[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = \"run -p wgpu-xtask --\"\n"
  },
  {
    "path": ".claude/skills/cts-triage/SKILL.md",
    "content": "---\nname: cts-triage\ndescription: Run CTS test suites and investigate failures\n---\n\n# Triage Process\n\nWhen working on a category of CTS tests, follow this systematic process to identify issues, prioritize fixes, and document findings.\n\n## Step 0: Divide Into Manageable Chunks\n\nList all the tests matching a selector:\n\n```bash\ncargo xtask cts -- --list 'webgpu:api,validation,*' 2>&1 | wc -l\n```\n\nIf there is a reasonable number of tests matching the selector (less than a\ncouple hundred or so, but this isn't a hard cutoff), then you can proceed with\ntriage. Otherwise, make a list of more detailed wildcards that match fewer\ntests, verify that each wildcard matches a reasonable number of tests, and\ntriage each wildcard separately.\n\n## Step 1: Get Overall Statistics\n\nRun the full test suite for the category to understand the scope:\n\n```bash\ncargo xtask cts 'webgpu:api,validation,category:*' 2>&1 | grep -E \"(Summary|Passed|Failed)\" | tail -5\n```\n\nThis gives you the pass rate and number of failures. Document this as your baseline.\n\n## Step 2: Identify Test Subcategories\n\nReview the output from the running the CTS (with or without `--list`) to\nidentify any subcategories that may exist within the suite being analyzed.\nSubcategories typically have an additional `:`- or `,`-delimited word in the\ntest name. Running tests by subcategory may be more manageable than running\nwith the entire suite at once or running individual tests.\n\n## Step 3: Run Each Subcategory\n\nTest each subcategory individually to identify which ones are passing vs failing:\n\n```bash\ncargo xtask cts 'webgpu:api,validation,category:subcategory:*' 2>&1 | grep -E \"(Passed|Failed|Skipped)\" | tail -3\n```\n\nTrack the pass rate for each subcategory. This helps you identify:\n- What's already working (don't break it!)\n- Where the failures are concentrated\n- Which issues affect multiple categories\n\n## Step 4: Analyze Failure Patterns\n\nFor failing subcategories, look at what tests are failing:\n\n```bash\ncargo xtask cts 'webgpu:api,validation,category:subcategory:*' 2>&1 | grep \"\\[fail\\]\" | head -20\n```\n\nLook for patterns:\n- **Format-specific failures**: May indicate missing format capability checks\n- **Parameter-specific failures**: Validation missing for specific parameter combinations\n\n## Step 5: Examine Specific Failures\n\nPick a representative failing test and run it individually to see the error:\n\n```bash\ncargo xtask cts 'webgpu:api,validation,category:subcategory:specific_test' 2>&1 | tail -30\n```\n\nLook for:\n- **\"EXPECTATION FAILED: DID NOT REJECT\"**: wgpu is accepting invalid input (validation gap)\n- **\"Validation succeeded unexpectedly\"**: Similar to above\n- **\"Unexpected validation error occurred\"**: wgpu is rejecting valid input\n- **Error message content**: Tells you what validation is triggering or missing\n\n## Step 6: Check CTS Test Source\n\nTo understand what the test expects, read the TypeScript source:\n\n```bash\ngrep -A 40 \"test_name\" cts/src/webgpu/api/validation/path/file.spec.ts\n```\n\nThe test source shows:\n- What configurations are being tested\n- What the expected behavior is (pass/fail)\n- The validation rules from the WebGPU spec\n- Comments explaining the rationale\n\n## Step 7: Categorize Issues\n\nGroup failures into categories:\n\n**High Priority - Validation Gaps:**\n- wgpu accepts invalid configurations that should fail\n- Security or correctness implications\n- Example: Accepting wrong texture formats, missing aspect checks\n\n**Medium Priority - Spec Compliance:**\n- Edge cases not handled correctly\n- Optional field validation issues\n- Example: depthCompare optional field handling\n\n**Low Priority - Minor Gaps:**\n- Less common scenarios\n- Limited real-world impact\n- Example: Depth bias with non-triangle topologies\n\n**Known Issues - Skip:**\n- Known failure patterns (documented in AGENTS.md)\n- Track count but don't try to fix\n\n## Step 8: Identify Root Causes\n\nFor validation gaps, find where validation should happen:\n\n1. **Search for existing validation:**\n   ```bash\n   grep -n \"relevant_keyword\" wgpu-core/src/device/resource.rs\n   ```\n\n2. **Look for render/compute pipeline creation:**\n   - Render pipeline: `wgpu-core/src/device/resource.rs` around `create_render_pipeline`\n   - Compute pipeline: Similar location\n   - Look for existing validation patterns you can follow\n\n3. **Check for helper functions:**\n   ```bash\n   grep \"fn is_\" wgpu-types/src/texture/format.rs\n   ```\n\n4. **Find error enums:**\n   ```bash\n   grep \"pub enum.*Error\" wgpu-core/src/pipeline.rs\n   ```\n\n## Step 9: Implement Fixes\n\nWhen implementing fixes:\n\n1. **Add error variants if needed** (in `wgpu-core/src/pipeline.rs`)\n2. **Add helper methods** (in `wgpu-types` if checking properties)\n3. **Add validation checks** (in `wgpu-core/src/device/resource.rs`)\n4. **Test the fix** with specific failing tests\n5. **Run full subcategory** to verify all related tests pass\n6. **Check you didn't break passing tests**\n\n## Step 10: Document Findings\n\nCreate or update a triage document (e.g., `category_triage.md`).\n\nDo not write information about changes you have made to the triage document. Only capture the state of the tests and any investigation into open issues.\n\n````markdown\n# Category CTS Tests - Triage Report\n\n**Overall Status:** XP/YF/ZS (%/%/%)\n\n## Passing Sub-suites ✅\n[List sub-suites that have no failures (all pass or skip)]\n\n## Remaining Issues ⚠️\n[List sub-suites that have failures and if it can be stated concisely, a summary of the issue]\n\n## Issue Detail\n[List detail of any investigation into failures. Do not go into detail about passed suites, just list the failures.]\n\n### 1. title, e.g. a distinguishing word from the test selector\n**Test selector:** `webgpu:api,validation,render_pipeline,depth_stencil_state:format:*`\n**What it tests:** [Description]\n**Example failure:**\n[a selector for a single failing test, e.g.:]\n```\nwebgpu:api,validation,render_pipeline,depth_stencil_state:depthCompare_optional:isAsync=false;format=\"stencil8\"\n```\n\n**Error:**\n[error message from the failing tests, e.g.:]\n```\nUnexpected validation error occurred: Depth/stencil state is invalid:\nFormat Stencil8 does not have a depth aspect, but depth test/write is enabled\n```\n\n**Root cause:**\n[Your analysis of the root cause. Do not speculate. Only include the results of specific investigation you have done.]\nThe validation is triggering incorrectly. When `depthCompare` is undefined/missing in JavaScript, it's getting translated to a default value that makes `is_depth_enabled()` return true, even for stencil-only formats.\n\n**Fix needed:**\n[Your proposed fix. Again, do not speculate. Only state the fix if it is obvious from the root cause analysis, or if you have done specific investigation into how to fix it.]\n\n### 2. title\n[repeat as needed for additional issues]\n````\n\n## Step 11: Update test.lst\n\nFor fixed tests that are now passing, add them to `cts_runner/test.lst`:\n\n1. **Use wildcards** to minimize lines:\n   ```\n   webgpu:api,validation,category:subcategory:isAsync=false;*\n   ```\n\n2. **Group related tests** when possible:\n   - If multiple subcategories all pass, use a higher-level wildcard\n   - Example: `webgpu:api,validation,category:*` if all subcategories pass\n\n3. **Maintain alphabetical order** roughly in the file\n\n## Step 12: Verify and Build\n\nBefore finishing:\n\n```bash\n# Format code\ncargo fmt\n\n# Check for errors\ncargo clippy --tests\n\n# Build to ensure no compilation errors\ncargo build\n\n# Run the tests you added to test.lst\ncargo xtask cts 'webgpu:api,validation,category:subcategory:isAsync=false;*'\n```\n\n# Common Patterns\n\nIf the user asked to investigate a failure, and the cause of the failure is\nnoted here with \"do not attempt to fix\", then stop and ask the user before\nattempting to fix.\n\n**Pattern: Format-specific failures**\n- Check if format validation is missing\n- Look for `is_depth_stencil_format()`, `is_color_format()` etc.\n- May need to add format capability checks\n\n**Pattern: Aspect-related failures**\n- Check if code validates format aspects (DEPTH, STENCIL, COLOR)\n- Use `hal::FormatAspects::from(format)` to check\n- Validate operations match available aspects\n\n**Pattern: Optional field failures**\n- May be WebGPU optional field semantics issue\n- Check if undefined in JS becomes a default value in Rust\n- May need to distinguish \"not set\" from \"set to default\"\n\n**Pattern: Atomics accepted incorrectly**\n- Naga allows referencing an atomic directly in an expression\n- Should only allow accessing via `atomicLoad`, `atomicStore`, etc.\n- Only investigate as necessary to confirm this is the issue. Do not attempt to fix. Refer user to https://github.com/gfx-rs/wgpu/issues/5474.\n\n**Pattern: Error reporting for destroyed resources**\n- Tests that check for validation errors when a destroyed resource is used. `wgpu` often reports these errors later than WebGPU requires, causing the tests to fail.\n- `wgpu` may report these errors earlier than it should, causing the test to fail with an unexpected validation error.\n- Look for:\n  - Tests with `state=\"destroyed\"` parameter\n  - Tests checking that operations on destroyed buffers or textures should fail\n- Example failing tests:\n  - `webgpu:api,validation,encoding,cmds,compute_pass:indirect_dispatch_buffer_state:` with `state=\"destroyed\"` subcases\n- Only investigate as necessary to confirm this is the issue. Do not attempt to fix. Refer user to https://github.com/gfx-rs/wgpu/issues/7881.\n\n# Tips\n\n- **Start with high-impact fixes**: Validation gaps with security implications\n- **Look for existing patterns**: Other validation code shows the style\n- **Test incrementally**: Fix one category at a time, verify it works\n- **Document as you go**: Don't wait until the end to write the triage\n- **Ask for clarification**: If test expectations are unclear, check the WebGPU spec or test source\n- **Track your progress**: Update pass rates as you fix issues\n"
  },
  {
    "path": ".claude/skills/webgpu-specs/SKILL.md",
    "content": "---\nname: webgpu-specs\ndescription: Download WebGPU and WGSL specifications for use as a reference\nallowed-tools: \"Bash(sh .claude/skills/webgpu-specs/download.sh)\"\n---\n\nRun `sh .claude/skills/webgpu-specs/download.sh` to download the\nWebGPU and WGSL specifications if they are not present or if they have\nbeen updated. You do not need to change directory before running the script.\n\nAfter the specs are downloaded, you can search in `target/claude/webgpu-spec.bs`\nand `target/claude/wgsl-spec.bs` for relevant sections of the specification.\n\nWhen referencing the specifications, prefer to use named anchors rather than\nline numbers. For example, to reference the \"Object Descriptors\" section, which has the\nfollowing header:\n\n```\n### Object Descriptors ### {#object-descriptors}\n```\n\nUse the URL <https://gpuweb.github.io/gpuweb/#object-descriptors> so the user\ncan click to navigate directly to that section.\n\nFor the WGSL specification, the base URL is <https://gpuweb.github.io/gpuweb/wgsl/>.\n\nIf necessary, read additional content from the file to find the header preceding\nthe text you want to reference. You may provide line numbers as additional\ncontext, but always make every effort to provide the user with a clickable link.\n"
  },
  {
    "path": ".claude/skills/webgpu-specs/download.sh",
    "content": "#!/bin/sh\n\nset -e\n\nTARGET_DIR=\"$(cargo metadata --format-version 1 | jq -r \".target_directory\")\"\n\nWEBGPU=\"$TARGET_DIR/claude/webgpu-spec\"\nWGSL=\"$TARGET_DIR/claude/wgsl-spec\"\nmkdir -p \"$TARGET_DIR/claude\"\n\nif [ -f \"$WEBGPU.etag\" ]; then\n    curl --etag-save \"$WEBGPU.etag.new\" --etag-compare \"$WEBGPU.etag\" -fsSL https://raw.githubusercontent.com/gpuweb/gpuweb/main/spec/index.bs -o \"$WEBGPU.bs\"\n    [ -s \"$WEBGPU.etag.new\" ] && mv \"$WEBGPU.etag.new\" \"$WEBGPU.etag\" || rm \"$WEBGPU.etag.new\"\nelse\n    curl --etag-save \"$WEBGPU.etag\" https://raw.githubusercontent.com/gpuweb/gpuweb/main/spec/index.bs -o \"$WEBGPU.bs\"\nfi\n\nif [ -f \"$WGSL.etag\" ]; then\n    curl --etag-save \"$WGSL.etag.new\" --etag-compare \"$WGSL.etag\" -fsSL https://raw.githubusercontent.com/gpuweb/gpuweb/main/wgsl/index.bs -o \"$WGSL.bs\"\n    [ -s \"$WGSL.etag.new\" ] && mv \"$WGSL.etag.new\" \"$WGSL.etag\" || rm \"$WGSL.etag.new\"\nelse\n    curl --etag-save \"$WGSL.etag\" https://raw.githubusercontent.com/gpuweb/gpuweb/main/wgsl/index.bs -o \"$WGSL.bs\"\nfi\n"
  },
  {
    "path": ".config/nextest.toml",
    "content": "# None of our tests should take longer than 45s, and if they've gone 2x that,\n# terminate them to prevent infinite run-on.\n[profile.default]\ndefault-filter = \"!test(~oom_test)\"\nslow-timeout = { period = \"45s\", terminate-after = 2 }\nfail-fast = false\nretries = 0\n\n[profile.default-miri]\n# Miri is very very slow, so give it a much longer timeout.\nslow-timeout = { period = \"120s\", terminate-after = 4 }\n\n# Use two threads for tests with \"2 threads\" in their name\n[[profile.default.overrides]]\nfilter = 'test(~2_threads) | test(~2 threads)'\nthreads-required = 2\n\n# Use four threads for tests with \"4 threads\" in their name\n[[profile.default.overrides]]\nfilter = 'test(~4_threads) | test(~4 threads)'\nthreads-required = 4\n\n# Use eight threads for tests with \"8 threads\" in their name\n[[profile.default.overrides]]\nfilter = 'test(~8_threads) | test(~8 threads)'\nthreads-required = 8\n\n#\n# Workarounds for flaky tests\n#\n\n# https://github.com/gfx-rs/wgpu/issues/7200\n[[profile.default.overrides]]\nfilter = 'test(wgpu_gpu::image_atomics::image_32_atomics)'\nplatform = 'cfg(target_vendor = \"apple\")'\nretries = 1\n\n[[profile.default.overrides]]\nfilter = 'test(compile_fail)'\nslow-timeout = { period = \"3m\", terminate-after = 2 }\n\n[[profile.default.overrides]]\nfilter = 'test(~oom_test)'\nthreads-required = \"num-test-threads\"\n\n#\n# Priorities for slow tests so that they run first, increasing overall test suite speed.\n# On software renderers, they can take 10-60 seconds. Compile fail can easily take 30+\n# seconds as it has to compile wgpu.\n#\n[[profile.default.overrides]]\npriority = 1\nfilter = 'test(clear_texture) | test(clear_buffer_range_respected) | test(compile_fail) | test(test_api)'\n"
  },
  {
    "path": ".deny.toml",
    "content": "[bans]\nmultiple-versions = \"deny\"\nskip-tree = [\n    { name = \"rustc-hash\", version = \"1.1.0\" },\n\n    # introduced by Deno, to be investigated\n    { name = \"petgraph\", version = \"0.6.5\" },\n\n    # Winit 0.30 uses an older objc2\n    { name = \"objc2-foundation\", version = \"0.2\" },\n\n    # glutin and tracy-client-sys use windows-sys 0.52, pulling older windows-targets\n    { name = \"windows-targets\", version = \"0.52\" },\n\n    # winit uses an old version via android-activity → jni\n    { name = \"windows-targets\", version = \"0.42\" },\n]\nskip = [\n    # flume -> fastrand uses an old version only on wasm32\n    { name = \"getrandom\", version = \"0.2.17\" },\n    # the ecosystem is migrating from getrandom 0.3 to 0.4, so this captures many stragglers\n    { name = \"getrandom\", version = \"0.3.4\" },\n\n    # Deno uses an old version\n    { name = \"bincode\", version = \"1.3.3\" },\n    { name = \"which\", version = \"6.0.3\" },\n\n    # Winit uses an old version via android-activity → jni\n    { name = \"windows-sys\", version = \"0.45\" },\n\n    # Winit uses an old version via calloop → rustix 0.38\n    { name = \"linux-raw-sys\", version = \"0.4\" },\n    { name = \"rustix\", version = \"0.38\" },\n    { name = \"windows-sys\", version = \"0.59\" },\n\n    # Winit uses an old version via android-activity\n    { name = \"thiserror\", version = \"1\" },\n    { name = \"thiserror-impl\", version = \"1\" },\n\n    # glutin uses an old version\n    { name = \"windows-sys\", version = \"0.52\" },\n\n    # getrandom 0.3 uses an old version\n    { name = \"r-efi\", version = \"5\" },\n\n    # parking-lot uses an old version\n    { name = \"redox_syscall\", version = \"0.5.18\" },\n\n    # deno uses an old version\n    { name = \"bit-vec\", version = \"0.8.0\" },\n    { name = \"bit-set\", version = \"0.8.0\" },\n]\nwildcards = \"deny\"\nallow-wildcard-paths = true\n\n[advisories]\nignore = [\n    # `paste` crate is no longer maintained https://rustsec.org/advisories/RUSTSEC-2024-0436\n    # It's a dependency of `metal` (which is to be replaced with `objc2-metal`), and a\n    # transitive dependency of `deno`. https://github.com/gfx-rs/wgpu/issues/7873\n    \"RUSTSEC-2024-0436\",\n    # `unic-*` crates are no longer maintained https://rustsec.org/advisories/RUSTSEC-2025-0100\n    # These are used via `deno`. https://github.com/gfx-rs/wgpu/issues/8393\n    \"RUSTSEC-2025-0075\",\n    \"RUSTSEC-2025-0080\",\n    \"RUSTSEC-2025-0081\",\n    \"RUSTSEC-2025-0098\",\n    \"RUSTSEC-2025-0100\",\n    # `bincode` is no longer maintained https://rustsec.org/advisories/RUSTSEC-2025-0141\n    # We only use it directly for tests and tools. It is also used indirectly via deno.\n    \"RUSTSEC-2025-0141\",\n]\n\n[licenses]\nallow = [\n    \"Apache-2.0\",\n    \"Apache-2.0 WITH LLVM-exception\",\n    \"BSD-2-Clause\",\n    \"BSD-3-Clause\",\n    \"CC0-1.0\",\n    \"ISC\",\n    \"MPL-2.0\",\n    \"MIT\",\n    \"MIT-0\",\n    \"Unicode-3.0\",\n    \"Zlib\",\n]\nprivate = { ignore = true }\n\n[sources]\nallow-git = [\n    # Waiting on releases; used in examples/tests only\n\n    # Pending a release for https://github.com/rust-cli/env_logger/commit/143fa647ab33ed3acc9f160dfa3cb075cc62b5a3\n    \"https://github.com/rust-cli/env_logger\",\n    # Pending merge/release for https://github.com/LukasKalbertodt/libtest-mimic/pull/58\n    \"https://github.com/cwfitzgerald/libtest-mimic\",\n]\nunknown-registry = \"deny\"\nunknown-git = \"deny\"\nrequired-git-spec = \"rev\"\n\n[sources.allow-org]\ngithub = []\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.mtl binary\n*.obj binary\nwgpu/src/backend/webgpu/webgpu_sys/** linguist-generated=true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n<!-- Thank you for filing this! Please read the [debugging tips](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications).\nThat may let you investigate on your own, or provide additional information that helps us to assist.-->\n\n**Description**\nA clear and concise description of what the bug is.\n\n**Repro steps**\nIdeally, a runnable example we can check out.\n\n**Expected vs observed behavior**\nClearly describe what you get, and how it goes across your expectations.\n\n**Extra materials**\nScreenshots to help explain your problem.\nValidation logs can be attached in case there are warnings and errors.\nZip-compressed API traces and GPU captures can also land here.\n\n**Platform**\nInformation about your OS, version of `wgpu`, your tech stack, etc.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Question about wgpu\n    url: https://github.com/gfx-rs/wgpu/discussions/new/choose\n    about: Any questions about how to use wgpu should go here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.md",
    "content": "---\nname: Other\nabout: Strange things you want to tell us\ntitle: ''\nlabels: question\nassignees: ''\n---\n"
  },
  {
    "path": ".github/actions/install-agility-sdk/action.yml",
    "content": "name: \"Install Agility SDK\"\ndescription: \"Install the D3D12 Agility SDK and export its environment variables\"\nruns:\n  using: \"composite\"\n  steps:\n    - shell: bash\n      run: |\n        set -e\n\n        cargo xtask install-agility-sdk >> \"$GITHUB_ENV\"\n        echo WGPU_DX12_AGILITY_SDK_REQUIRE=1 >> \"$GITHUB_ENV\"\n"
  },
  {
    "path": ".github/actions/install-dxc/action.yml",
    "content": "name: \"Install DXC\"\ndescription: \"Install DXC\"\nruns:\n  using: \"composite\"\n  steps:\n    - shell: bash\n      run: |\n        set -e\n\n        export DXC_RELEASE=\"v1.8.2505.1\"\n        export DXC_FILENAME=\"dxc_2025_07_14.zip\"\n\n        curl.exe -L --retry 5 https://github.com/microsoft/DirectXShaderCompiler/releases/download/$DXC_RELEASE/$DXC_FILENAME -o dxc.zip\n        7z.exe e dxc.zip -odxc bin/x64/{dxc.exe,dxcompiler.dll}\n\n        # We need to use cygpath to convert PWD to a windows path as we're using bash.\n        cygpath --windows \"$PWD/dxc\" >> \"$GITHUB_PATH\"\n"
  },
  {
    "path": ".github/actions/install-mesa/action.yml",
    "content": "name: \"Install Mesa\"\ndescription: \"Install Mesa\"\ninputs:\n  # Sourced from https://archive.mesa3d.org/. Bumping this requires\n  # updating the mesa build in https://github.com/gfx-rs/ci-build and creating a new release.\n  version:\n    default: \"25.2.7\"\n  # Corresponds to https://github.com/gfx-rs/ci-build/releases\n  ci-binary-build:\n    default: \"build26\"\n  target-dir:\n    description: \"The directory into which to install the Mesa libraries.\"\n    default: \"target/llvm-cov-target/debug\"\nruns:\n  using: \"composite\"\n  steps:\n    - name: (Linux) Install Mesa\n      if: runner.os == 'Linux'\n      shell: bash\n      env:\n        MESA_VERSION: ${{ inputs.version }}\n        CI_BINARY_BUILD: ${{ inputs.ci-binary-build }}\n      run: |\n        set -e\n\n        curl -L --retry 5 https://github.com/gfx-rs/ci-build/releases/download/$CI_BINARY_BUILD/mesa-$MESA_VERSION-linux-x86_64.tar.xz -o mesa.tar.xz\n        mkdir mesa\n        tar xpf mesa.tar.xz -C mesa\n\n        # The ICD provided by the mesa build is hardcoded to the build environment.\n        #\n        # We write out our own ICD file to point to the mesa vulkan\n        cat <<- EOF > icd.json\n        {\n          \"ICD\": {\n              \"api_version\": \"1.1.255\",\n              \"library_path\": \"$PWD/mesa/lib/x86_64-linux-gnu/libvulkan_lvp.so\"\n          },\n          \"file_format_version\": \"1.0.0\"\n        }\n        EOF\n\n        echo \"VK_DRIVER_FILES=$PWD/icd.json\" >> \"$GITHUB_ENV\"\n        echo \"LD_LIBRARY_PATH=$PWD/mesa/lib/x86_64-linux-gnu/:$LD_LIBRARY_PATH\" >> \"$GITHUB_ENV\"\n        echo \"LIBGL_DRIVERS_PATH=$PWD/mesa/lib/x86_64-linux-gnu/dri\" >> \"$GITHUB_ENV\"\n\n    - name: (Windows) Install Mesa\n      if: runner.os == 'Windows'\n      shell: bash\n      env:\n        MESA_VERSION: ${{ inputs.version }}\n        CI_BINARY_BUILD: ${{ inputs.ci-binary-build }}\n      run: |\n        set -e\n\n        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\n        7z.exe e mesa.7z -omesa x64/{opengl32.dll,libgallium_wgl.dll,libglapi.dll,vulkan_lvp.dll,lvp_icd.x86_64.json}\n\n        cp -v mesa/* ${{ inputs.target-dir }}/\n        cp -v mesa/* ${{ inputs.target-dir }}/deps\n\n        # We need to use cygpath to convert PWD to a windows path as we're using bash.\n        echo \"VK_DRIVER_FILES=`cygpath --windows $PWD/mesa/lvp_icd.x86_64.json`\" >> \"$GITHUB_ENV\"\n        echo \"GALLIUM_DRIVER=llvmpipe\" >> \"$GITHUB_ENV\"\n"
  },
  {
    "path": ".github/actions/install-vulkan-sdk/action.yml",
    "content": "name: \"Install Vulkan SDK\"\ndescription: \"Install Vulkan SDK\"\ninputs:\n  # Sourced from https://vulkan.lunarg.com/sdk/home#linux\n  version:\n    default: \"1.4.328\"\n  full-version:\n    default: \"1.4.328.1\"\nruns:\n  using: \"composite\"\n  steps:\n    - name: (Linux) Install Vulkan SDK\n      if: runner.os == 'Linux'\n      shell: bash\n      env:\n        VULKAN_SDK_VERSION: ${{ inputs.version }}\n        VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }}\n      run: |\n        set -e\n\n        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\n        mkdir vulkan-sdk\n        tar xpf vulkan-sdk.tar.xz -C vulkan-sdk\n\n        mv ./vulkan-sdk/${{ env.VULKAN_FULL_SDK_VERSION }} $HOME/VulkanSDK\n\n        echo \"$HOME/VulkanSDK/x86_64/bin\" >> \"$GITHUB_PATH\"\n        echo \"LD_LIBRARY_PATH=$HOME/VulkanSDK/x86_64/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}\" >> \"$GITHUB_ENV\"\n        echo \"VK_ADD_LAYER_PATH=$HOME/VulkanSDK/x86_64/share/vulkan/explicit_layer.d\" >> \"$GITHUB_ENV\"\n\n    - name: (Windows) Install Vulkan SDK\n      if: runner.os == 'Windows'\n      shell: bash\n      env:\n        VULKAN_SDK_VERSION: ${{ inputs.version }}\n        VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }}\n      run: |\n        set -e\n\n        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\n\n        ./vulkan-sdk-installer.exe --accept-licenses --default-answer --confirm-command install\n\n        echo \"C:/VulkanSDK/${{ env.VULKAN_FULL_SDK_VERSION }}/Bin\" >> \"$GITHUB_PATH\"\n\n    - name: (Mac) Install Vulkan SDK\n      if: runner.os == 'macOS'\n      shell: bash\n      env:\n        VULKAN_SDK_VERSION: ${{ inputs.version }}\n        VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }}\n      run: |\n        set -e\n\n        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\n        unzip vulkan-sdk.zip -d vulkan-sdk\n\n        ls -l vulkan-sdk\n        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\n\n        echo \"$HOME/VulkanSDK/macOS/bin\" >> \"$GITHUB_PATH\"\n"
  },
  {
    "path": ".github/actions/install-warp/action.yml",
    "content": "name: \"Install WARP\"\ndescription: \"Install WARP\"\ninputs:\n  target-dir:\n    description: \"The directory into which to install the WARP DLL.\"\n    required: true\nruns:\n  using: \"composite\"\n  steps:\n    - shell: bash\n      run: |\n        set -e\n\n        cargo xtask install-warp --target-dir ${{ inputs.target-dir }}\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "**Connections**\n_Link to the issues addressed by this PR, or dependent PRs in other repositories_\n\n_When one pull request builds on another, please put \"Depends on\n#NNNN\" towards the top of its description. This helps maintainers\nnotice that they shouldn't merge it until its ancestor has been\napproved. Don't use draft PR status to indicate this._\n\n**Description**\n_Describe what problem this is solving, and how it's solved._\n\n**Testing**\n_Explain how this change is tested._\n\n**Squash or Rebase?**\n\n_If your pull request contains multiple commits, please indicate whether\nthey need to be squashed into a single commit before they're merged,\nor if they're ready to rebase onto `trunk` as they stand. In the\nlatter case, please ensure that each commit passes all CI tests, so\nthat we can continue to bisect along `trunk` to isolate bugs._\n\n<!--\nThanks for filing! Reviewers are assigned for non-draft PRs in the weekly wgpu maintainers meetings.\n\nAfter you get a review and have addressed any comments, please explicitly re-request a review from the\nperson(s) who reviewed your changes. This will make sure it gets re-added to their review queue - you're not bothering us!\n-->\n\n**Checklist**\n\n- [ ] Run `cargo fmt`.\n- [ ] Run `taplo format`.\n- [ ] Run `cargo clippy --tests`. If applicable, add:\n  - [ ] `--target wasm32-unknown-unknown`\n- [ ] Run `cargo xtask test` to run tests.\n- [ ] If this contains user-facing changes, add a `CHANGELOG.md` entry. <!-- See instructions at the top of `CHANGELOG.md`. -->\n"
  },
  {
    "path": ".github/workflows/changelog.yml",
    "content": "name: changelog\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/changelog.yml\"\n      - \"CHANGELOG.md\"\n      - \"xtask/**/*\"\n    types:\n      - opened\n      - synchronize\n      - reopened\n      - labeled\n      - unlabeled\n\nenv:\n  #\n  # Dependency versioning\n  #\n\n  # This is the MSRV used by all repository infrastructure.\n  REPO_MSRV: \"1.93\"\n\n  #\n  # Environment variables\n  #\n\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  RUST_LOG: info\n  RUST_BACKTRACE: \"1\"\n  CACHE_SUFFIX: c # cache busting\n\njobs:\n  changelog:\n    timeout-minutes: 2\n\n    name: Check changelog for errors\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      # NOTE: Keep label name(s) in sync. with `xtask`'s implementation.\n      - name: Run `cargo xtask changelog …`\n        run: |\n          cargo xtask changelog \"origin/${{ github.event.pull_request.base.ref }}\" ${{ contains(github.event.pull_request.labels.*.name, 'changelog: released entry changed') && '--allow-released-changes' || ''  }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  #\n  # Dependency versioning\n  #\n\n  # This is the MSRV used by all repository infrastructure.\n  REPO_MSRV: \"1.93\"\n  # This is the MSRV used by `wgpu` itself.\n  WGPU_MSRV: \"1.87\"\n  # This is the MSRV used by the `wgpu-core`, `wgpu-hal`, and `wgpu-types` crates,\n  # to ensure that they can be used with firefox.\n  CORE_MSRV: \"1.87\"\n\n  #\n  # Environment variables\n  #\n\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  WGPU_DX12_COMPILER: dxc\n  RUST_LOG: debug,wasm_bindgen_wasm_interpreter=warn,wasm_bindgen_cli_support=warn,walrus=warn,naga=info\n  RUST_BACKTRACE: full\n  PKG_CONFIG_ALLOW_CROSS: 1 # allow android to work\n  RUSTFLAGS: -D warnings\n  RUSTDOCFLAGS: -D warnings\n  WASM_BINDGEN_TEST_TIMEOUT: 300 # 5 minutes\n  CACHE_SUFFIX: e # cache busting\n  WGPU_CI: true\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\n# We distinguish the following kinds of builds:\n# - native: build for the same target as we compile on\n# - web: build for the Web\n# - em: build for the Emscripten\n\n# For build time and size optimization we disable debug symbols\n# entirely on clippy jobs and reduce it to line-numbers\n# only for ones where we run tests.\n#\n# Additionally, we disable incremental builds entirely\n# as our caching system doesn't actually cache our crates.\n# It adds overhead to the build and another point of failure.\n\njobs:\n  check:\n    # runtime is normally 2-8 minutes\n    #\n    # currently high due to documentation time problems on mac.\n    # https://github.com/rust-lang/rust/issues/114891\n    timeout-minutes: 30\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # Windows\n          - name: Windows x86_64\n            os: windows-2022\n            target: x86_64-pc-windows-msvc\n            tier: 1\n            kind: native\n\n          # Windows\n          - name: Windows aarch64\n            os: windows-2022\n            target: aarch64-pc-windows-msvc\n            tier: 2\n            kind: native\n\n          # MacOS\n          - name: MacOS x86_64\n            os: macos-14\n            target: x86_64-apple-darwin\n            tier: 1\n            kind: native\n\n          - name: MacOS aarch64\n            os: macos-14\n            target: aarch64-apple-darwin\n            tier: 1\n            kind: native\n\n          # IOS\n          - name: IOS aarch64\n            os: macos-14\n            target: aarch64-apple-ios\n            tier: 2\n            kind: native\n\n          # VisionOS\n          - name: VisionOS aarch64\n            os: macos-14\n            target: aarch64-apple-visionos\n            kind: wgpu-only\n            tier: 3\n            extra-flags: -Zbuild-std\n\n          # Linux\n          - name: Linux x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-linux-gnu\n            tier: 1\n            kind: native\n\n          - name: Linux aarch64\n            os: ubuntu-24.04\n            target: aarch64-unknown-linux-gnu\n            tier: 1\n            kind: native\n\n          # FreeBSD\n          - name: FreeBSD x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-freebsd\n            tier: 2\n            kind: wgpu-only\n\n          # NetBSD\n          - name: NetBSD x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-netbsd\n            tier: 2\n            kind: wgpu-only\n\n          # Android\n          - name: Android aarch64\n            os: ubuntu-24.04\n            target: aarch64-linux-android\n            tier: 2\n            kind: native\n\n          # Android\n          - name: Android ARMv7\n            os: ubuntu-24.04\n            target: armv7-linux-androideabi\n            tier: 2\n            kind: wgpu-only\n\n          # Android\n          - name: Android x86_64\n            os: ubuntu-24.04\n            target: x86_64-linux-android\n            tier: 2\n            kind: wgpu-only\n\n          # OpenHarmony\n          - name: OpenHarmony aarch64\n            os: ubuntu-24.04\n            target: aarch64-unknown-linux-ohos\n            tier: 2\n            kind: native\n\n          # WebGPU/WebGL\n          - name: WebAssembly\n            os: ubuntu-24.04\n            target: wasm32-unknown-unknown\n            tier: 2\n            kind: web\n\n          - name: Emscripten\n            os: ubuntu-24.04\n            target: wasm32-unknown-emscripten\n            tier: 2\n            kind: wgpu-only\n\n          - name: WebAssembly Core 1.0\n            os: ubuntu-24.04\n            target: wasm32v1-none\n            tier: 2\n            kind: no_std\n\n          # 32-bit PowerPC Linux\n          # Included to test support for `portable-atomic`\n          - name: Linux ppc32\n            os: ubuntu-24.04\n            target: powerpc-unknown-linux-gnu\n            tier: 2\n            kind: wgpu-only\n\n    name: Clippy ${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install toolchain (repo MSRV - tier 1 or 2)\n        if: matrix.tier == 1 || matrix.tier == 2\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy\n          rustup target add ${{ matrix.target }} --toolchain ${{ env.REPO_MSRV }}\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      # In order to build on platforms that require a nightly toolchain, we install stable as expected,\n      # add the rust-src component, then tell stable to consider itself nightly by setting RUSTC_BOOTSTRAP=1.\n      #\n      # This is not formally \"correct\" thing to do, but it saves significant maintainer burden. If we were to\n      # use a proper nightly toolchain we would have to manually find a date that works. Even with a date that is\n      # carefully coordinated with the version of stable we are using, there are often mismatches of clippy lints\n      # between nightly and stable. This is a real mess. By using RUSTC_BOOTSTRAP=1, we get access to all the nice\n      # nightly features without needing to go through the hassle of maintaining a nightly toolchain.\n      #\n      # RUSTC_BOOTSTRAP=1 is how the rust project builds itself when bootstrapping the compiler, so while not \"stable\"\n      # it has been around for many years and don't anticipate it going away any time soon.\n      - name: Install toolchain (repo MSRV - tier 3)\n        if: matrix.tier == 3\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy,rust-src\n          echo \"RUSTC_BOOTSTRAP=1\" >> \"$GITHUB_ENV\"\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: clippy-${{ matrix.target }}-${{ matrix.kind }}-${{ env.CACHE_SUFFIX }}\n\n      - name: (Linux `aarch64`) Install `aarch64-linux-gnu` `g++`\n        if: matrix.target == 'aarch64-unknown-linux-gnu'\n        run: |\n          set -e\n\n          sudo apt-get update -y -qq\n\n          sudo apt-get install g++-aarch64-linux-gnu\n\n      - name: (Android) Add Android APK to `PATH`\n        if: matrix.target == 'aarch64-linux-android'\n        run: |\n          # clang++ will be detected correctly by CC from path\n          echo \"$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin\" >> $GITHUB_PATH\n\n          # the android sdk doesn't use the conventional name for ar, so explicitly set it.\n          echo \"AR_aarch64_linux_android=llvm-ar\" >> \"$GITHUB_ENV\"\n\n      # Building for wasm32 requires a series of specific tests for the WebGPU backend.\n      - name: Check web\n        if: matrix.kind == 'web'\n        shell: bash\n        run: |\n          set -e\n\n          # build for WebGPU\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --features glsl,spirv,fragile-send-sync-non-atomic-wasm\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --features glsl,spirv\n          cargo --locked doc --target ${{ matrix.target }} ${{ matrix.extra-flags }} --no-deps --features glsl,spirv\n\n          # check with only the web feature\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --no-default-features --features=web\n\n          # all features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --all-features\n          cargo --locked doc --target ${{ matrix.target }} ${{ matrix.extra-flags }} --no-deps --all-features\n\n      # Building for platforms where the tests do not compile.\n      - name: Check `wgpu` only\n        if: matrix.kind == 'wgpu-only'\n        shell: bash\n        run: |\n          set -e\n\n          # check with no features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu -p wgpu-hal --no-default-features\n\n          # Don't check samples since we use winit in our samples which has dropped support for Emscripten.\n\n          # Check with all features.\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-hal --all-features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu --all-features\n\n      # Building for no_std platforms.\n      - name: Check `no_std`\n        if: matrix.kind == 'no_std'\n        shell: bash\n        run: |\n          set -e\n\n          # check with no features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-types --no-default-features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p naga --no-default-features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-hal --no-default-features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu --no-default-features\n\n          # Check with all compatible features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-types --no-default-features --features strict_asserts,fragile-send-sync-non-atomic-wasm,serde,counters\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p naga --no-default-features --features dot-out,spv-in,spv-out\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-hal --no-default-features --features fragile-send-sync-non-atomic-wasm\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu --no-default-features --features serde\n\n      # Building for native platforms with standard tests.\n      - name: Check native\n        if: matrix.kind == 'native'\n        shell: bash\n        run: |\n          set -e\n\n          # check with no features\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --no-default-features\n\n          # Check with all features.\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --benches --all-features\n\n          # Check with all features and profiling macro code.\n          # If we don't check this then errors inside `profiling::scope!()` will not be caught.\n          cargo --locked clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --benches --all-features --features test-build-with-profiling\n\n          # build docs\n          cargo --locked doc --target ${{ matrix.target }} ${{ matrix.extra-flags }} --all-features --no-deps\n\n      - name: Check private item docs\n        if: matrix.kind == 'native'\n        shell: bash\n        run: |\n          set -e\n\n          cargo --locked doc --target ${{ matrix.target }} ${{ matrix.extra-flags }} \\\n                --package wgpu-core \\\n                --package wgpu-hal \\\n                --package naga \\\n                --package wgpu \\\n                --all-features --no-deps --document-private-items\n\n  # We run minimal checks on the MSRV of the `wgpu` crate, ensuring that\n  # its dependency tree does not cause issues for servo or other users.\n  #\n  # We don't test all platforms, just ones with different dependency stacks.\n  check-wgpu-msrv:\n    # runtime is normally 1-3 minutes\n    timeout-minutes: 10\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # Windows\n          - name: Windows x86_64\n            os: windows-2022\n            target: x86_64-pc-windows-msvc\n\n          # MacOS\n          - name: MacOS x86_64\n            os: macos-14\n            target: x86_64-apple-darwin\n\n          # Linux\n          - name: Linux x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-linux-gnu\n\n          # WebGPU\n          - name: WebAssembly\n            os: ubuntu-24.04\n            target: wasm32-unknown-unknown\n\n    name: wgpu MSRV Check ${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install core MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.WGPU_MSRV }} --no-self-update --profile=minimal --component clippy --target ${{ matrix.target }}\n          rustup override set ${{ env.WGPU_MSRV }}\n          cargo -V\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: wgpu-msrv-check-${{ matrix.target }}-${{ env.CACHE_SUFFIX }}\n\n      - name: Reduce MSRV on dependencies\n        shell: bash\n        run: |\n          set -e\n\n          # No needed operations currently.\n          # If we find a dependency that needs to be downgraded to achieve\n          # the MSRV, we can do it here with `cargo update -p <crate> --precise <version>`\n\n      - name: Check native\n        shell: bash\n        run: |\n          set -e\n\n          # check `wgpu` with all features.\n          cargo check --target ${{ matrix.target }} --all-features -p wgpu\n\n  # We run minimal checks on the MSRV of the core crates, ensuring that\n  # its dependency tree does not cause issues for firefox.\n  #\n  # We don't test all platforms, just ones with different dependency stacks.\n  check-core-msrv:\n    # runtime is normally 1-3 minutes\n    timeout-minutes: 10\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # Windows\n          - name: Windows x86_64\n            os: windows-2022\n            target: x86_64-pc-windows-msvc\n\n          # MacOS\n          - name: MacOS x86_64\n            os: macos-14\n            target: x86_64-apple-darwin\n\n          # Linux\n          - name: Linux x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-linux-gnu\n\n    name: Core MSRV Check ${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install core MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.CORE_MSRV }} --no-self-update --profile=minimal --component clippy --target ${{ matrix.target }}\n          rustup override set ${{ env.CORE_MSRV }}\n          cargo -V\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: core-msrv-check-${{ matrix.target }}-${{ env.CACHE_SUFFIX }}\n\n      - name: Reduce MSRV on dependencies\n        shell: bash\n        run: |\n          set -e\n\n          # No needed operations currently.\n          # If we find a dependency that needs to be downgraded to achieve\n          # the MSRV, we can do it here with `cargo update -p <crate> --precise <version>`\n\n      - name: Check native\n        shell: bash\n        run: |\n          set -e\n\n          # check `wgpu-core` with all features. This will also get `wgpu-hal` and `wgpu-types`.\n          cargo check --target ${{ matrix.target }} --all-features -p wgpu-core\n\n  # Check that the libraries build — but not that there are no warnings or that tests pass -\n  # with `-Zdirect-minimal-versions` which lowers all dependencies from the workspace packages\n  # to non-workspace packages to their minimum allowed version.\n  check-minimal-versions:\n    # runtime is normally 2 minutes\n    timeout-minutes: 10\n\n    name: MSRV Minimal Versions\n    runs-on: ubuntu-24.04\n    env:\n      # Override flags to NOT include `-D warnings`, because warnings may be due to harmless problems in deps.\n      # Also, allow unexpected_cfgs because it is very common and spammy when using old deps.\n      RUSTFLAGS: -A unexpected_cfgs\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n          echo \"\"\"\n          [profile.dev]\n          debug = false\" >> .cargo/config.toml\n\n      - name: Set minimal versions\n        shell: bash\n        run: |\n          set -e\n\n          cargo +${{ env.REPO_MSRV }} update -Zdirect-minimal-versions\n        env:\n          RUSTC_BOOTSTRAP: 1\n\n      - name: Run cargo check\n        shell: bash\n        run: |\n          set -e\n\n          cargo check --all-targets --all-features\n\n  wasm-test:\n    # runtime is normally 2 minutes\n    timeout-minutes: 10\n\n    name: Test WebAssembly\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy --target wasm32-unknown-unknown\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Install `wasm-pack`\n        uses: taiki-e/install-action@v2\n        with:\n          tool: wasm-pack\n\n      - name: Execute tests\n        run: |\n          cd wgpu\n\n          wasm-pack test --headless --chrome --no-default-features --features wgsl,webgl,web --workspace\n\n  gpu-test:\n    # runtime is normally 5-15 minutes\n    timeout-minutes: 30\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # Windows\n          - name: Windows x86_64\n            os: windows-2022\n\n          # Mac\n          - name: Mac aarch64\n            os: macos-14\n\n          # Linux\n          - name: Linux x86_64\n            os: ubuntu-24.04\n\n    name: Test ${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal -c llvm-tools\n          cargo -V\n\n      - name: Install `cargo-nextest` and `cargo-llvm-cov`\n        uses: taiki-e/install-action@v2\n        with:\n          tool: cargo-nextest,cargo-llvm-cov\n\n      - name: Debug symbols to line-tables-only\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = \"line-tables-only\"\n          EOF\n\n        # Cache step must go before warp and mesa install on windows as they write into the\n        # target directory, and rust-cache will overwrite the entirety of the target directory.\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: test-${{ matrix.os }}-${{ env.CACHE_SUFFIX }}\n          workspaces: |\n            . -> target\n\n      - name: (Windows) Install DXC\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-dxc\n\n      - name: (Windows) Install WARP\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-warp\n        with:\n          target-dir: \"target/llvm-cov-target/debug\"\n\n      - name: (Windows) Install Agility SDK\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-agility-sdk\n\n      - name: (Windows) Install Mesa\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-mesa\n\n      - name: (Windows) Install Vulkan SDK\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-vulkan-sdk\n\n      - name: (Mac) Install Vulkan SDK\n        if: matrix.os == 'macos-14'\n        uses: ./.github/actions/install-vulkan-sdk\n\n      - name: (Linux) Install Vulkan SDK\n        if: matrix.os == 'ubuntu-24.04'\n        uses: ./.github/actions/install-vulkan-sdk\n\n      - name: (Linux) Install Mesa\n        if: matrix.os == 'ubuntu-24.04'\n        uses: ./.github/actions/install-mesa\n\n      - name: Delete Naga snapshots\n        shell: bash\n        run: |\n          set -e\n\n          # Delete snapshots so we can ensure there aren't any excess output files.\n          rm -r naga/tests/out\n\n      - name: Run tests\n        shell: bash\n        run: |\n          set -e\n\n          cargo --locked xtask test --llvm-cov\n\n      - name: Check Naga snapshots\n        # git diff doesn't check untracked files, we need to stage those then compare with HEAD.\n        run: git add . && git diff --exit-code HEAD naga/tests/out\n\n      - uses: actions/upload-artifact@v7\n        name: Upload comparison images\n        if: always() # We want artifacts even if the tests fail.\n        with:\n          name: comparison-images-${{ matrix.os }}\n          path: |\n            **/*-actual.png\n            **/*-difference.png\n\n      # Print GPU configuration so we can see what features were available during the test.\n      - name: Print GPU configurations\n        if: always() # We want this information even if the tests fail.\n        shell: bash\n        run: |\n          set -e\n\n          cat .gpuconfig\n\n      - name: Generate coverage report\n        id: coverage\n        shell: bash\n        continue-on-error: true\n        run: |\n          set -e\n\n          cargo --locked llvm-cov report --lcov --output-path lcov.info\n\n      - name: Upload coverage report to Codecov\n        uses: codecov/codecov-action@v5\n        if: steps.coverage.outcome == 'success'\n        with:\n          files: lcov.info\n          token: ${{ secrets.CODECOV_TOKEN }}\n\n  doctest:\n    # runtime is normally 2 minutes\n    timeout-minutes: 10\n\n    name: Doctest\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component rustfmt\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: doctests-${{ env.CACHE_SUFFIX }}\n\n      - name: Run doctests\n        shell: bash\n        run: |\n          set -e\n\n          cargo --locked test --doc\n\n  miri:\n    # runtime is normally 2 minutes\n    timeout-minutes: 10\n\n    name: Miri (limited scope)\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      # Miri is *only* available on nightly.\n      - name: Install nightly toolchain\n      \n        run: |\n          rustup toolchain install nightly --no-self-update --profile=minimal --component miri\n          rustup override set nightly\n          cargo -V\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: miri-${{ env.CACHE_SUFFIX }}\n\n      - name: Run Miri on selected tests\n        shell: bash\n        run: |\n          set -e\n\n          # Note: this is a *smaller* set of tests than `cargo xtask miri` runs;\n          # it is ones that are both important and cheap.\n          # Consider expanding it to include some API tests with the noop backend.\n          cargo miri test --package=wgpu --no-default-features -- write_only\n\n\n  fmt:\n    # runtime is normally 15 seconds\n    timeout-minutes: 2\n\n    name: Format & Typos\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component rustfmt\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Run `cargo fmt`\n        run: |\n          cargo --locked fmt -- --check\n\n      - name: Install Taplo\n        uses: uncenter/setup-taplo@v1\n        with:\n          version: \"0.9.3\"\n\n      - name: Run `taplo fmt`\n        run: taplo format --check --diff\n\n      - name: Check for typos\n        uses: crate-ci/typos@v1.44.0\n\n  check-cts-runner:\n    # runtime is normally 2 minutes\n    timeout-minutes: 10\n\n    name: Clippy cts_runner\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: cts-runner-${{ env.CACHE_SUFFIX }}\n\n      - name: Build Deno\n        run: |\n          cargo --locked clippy --manifest-path cts_runner/Cargo.toml\n\n  # Separate job so that new advisories don't block CI.\n  #\n  # This job is not required to pass for PRs to be merged.\n  cargo-deny-check-advisories:\n    # runtime is normally 1 minute\n    timeout-minutes: 5\n\n    name: \"cargo-deny advisories\"\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Run `cargo deny check`\n        uses: EmbarkStudios/cargo-deny-action@v2\n        with:\n          command: check advisories\n          arguments: --all-features --workspace\n          command-arguments: -Dwarnings -Aunmatched-organization\n          rust-version: ${{ env.REPO_MSRV }}\n\n  cargo-deny-check-rest:\n    # runtime is normally 1 minute\n    timeout-minutes: 5\n\n    name: \"cargo-deny\"\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n\n      - name: Run `cargo deny check`\n        uses: EmbarkStudios/cargo-deny-action@v2\n        with:\n          command: check bans licenses sources\n          arguments: --all-features --workspace\n          command-arguments: -Dwarnings -Aunmatched-organization\n          rust-version: ${{ env.REPO_MSRV }}\n"
  },
  {
    "path": ".github/workflows/cts.yml",
    "content": "name: CTS\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  REPO_MSRV: \"1.93\"\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  DENO_WEBGPU_DX12_COMPILER: dynamicdxc\n  RUST_BACKTRACE: full\n  RUSTFLAGS: -D warnings\n  RUSTDOCFLAGS: -D warnings\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  cts:\n    strategy:\n      fail-fast: false\n      matrix:\n        suite: [\"API Operation\", \"API Validation\", \"Other\"]\n        platform: [\"Windows x86_64\", \"Mac aarch64\", \"Linux x86_64\"]\n        include:\n          # When an `include` item matches existing matrix entries, the additional items are\n          # added only to the matching entries. The two lines above define the matrix, and\n          # the items below attach the rest of the configuration values that apply to suites\n          # and platforms.\n          - suite: API Operation\n            filter: \"^webgpu:api,operation,\"\n          - suite: API Validation\n            filter: \"^webgpu:api,validation,\"\n          - suite: Other\n            filter: \"!^webgpu:api,operation,|^webgpu:api,validation,\"\n\n          # Windows\n          - platform: Windows x86_64\n            os: windows-2022\n            target: x86_64-pc-windows-msvc\n            backend: dx12\n            shell: bash\n\n          # Mac\n          # Need to use `zsh` for `source <(cmd)` because it doesn't work in\n          # ancient MacOS bash. `zsh` is a non-standard shell in GitHub\n          # actions, so must be specified as a command string.\n          - platform: Mac aarch64\n            os: macos-14\n            target: x86_64-apple-darwin\n            backend: metal\n            shell: zsh {0}\n\n          # Linux\n          - platform: Linux x86_64\n            os: ubuntu-24.04\n            target: x86_64-unknown-linux-gnu\n            backend: vulkan\n            shell: bash\n\n    name: ${{ matrix.suite }} ${{ matrix.platform }}\n    runs-on: ${{ matrix.os }}\n\n    defaults:\n      run:\n        # Substitution of ${{ matrix.shell }} appears not to work in per-step configuration\n        shell: ${{ matrix.shell }}\n\n    steps:\n      - name: checkout repo\n        uses: actions/checkout@v6\n\n      - name: Install Repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --target ${{ matrix.target }} --component llvm-tools\n          rustup override set ${{ env.REPO_MSRV }}\n          cargo -V\n\n      - name: Install `cargo-llvm-cov`\n        uses: taiki-e/install-action@v2\n        with:\n          tool: cargo-llvm-cov\n\n      - name: caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          # The version number can be incremented for cache busting.\n          prefix-key: v2-rust-${{ hashFiles('cts_runner/revision.txt') }}\n          cache-directories: cts\n\n      # We enable line numbers for panics, but that's it\n      - name: disable debug\n        shell: bash\n        run: |\n          mkdir -p .cargo\n          cat <<EOF >> .cargo/config.toml\n          [profile.dev]\n          debug = \"line-tables-only\"\n          EOF\n\n      - name: (Windows) Install DXC\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-dxc\n\n      # Note: `target-dir` is intentionally different from other jobs.\n      # See note in xtask about `llvm-cov show-env`.\n      - name: (Windows) Install WARP\n        if: matrix.os == 'windows-2022'\n        uses: ./.github/actions/install-warp\n        with:\n          target-dir: \"target/debug\"\n\n      - name: (Linux) Install Mesa\n        if: matrix.os == 'ubuntu-24.04'\n        uses: ./.github/actions/install-mesa\n        with:\n          target-dir: \"target/debug\"\n\n      # Some of these tests check stdout, so we explicitly set the backend\n      # to avoid getting EGL messages in the output.\n      - name: Test cts_runner\n        if: matrix.suite == 'other'\n        shell: bash\n        run: |\n          export LLVM_PROFILE_FILE=${{ github.workspace }}/target/wgpu-%p-%m.profraw\n          export DENO_WEBGPU_BACKEND=${{ matrix.backend }}\n          cargo --locked llvm-cov --no-cfg-coverage --no-report test -p cts_runner\n\n      - name: Run CTS\n        shell: bash\n        run: cargo --locked xtask cts --llvm-cov --backend ${{ matrix.backend }} --filter '${{matrix.filter }}'\n\n      - name: Generate coverage report\n        id: coverage\n        continue-on-error: true\n        run: |\n          set -e\n\n          source <(cargo llvm-cov --no-cfg-coverage show-env --sh)\n          cargo --locked llvm-cov report --lcov --output-path lcov.info\n\n      - name: Upload coverage report to Codecov\n        uses: codecov/codecov-action@v5\n        if: steps.coverage.outcome == 'success'\n        with:\n          files: lcov.info\n          token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Docs\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  # This is the MSRV used by all repository infrastructure.\n  REPO_MSRV: \"1.93\"\n\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  RUST_BACKTRACE: full\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install documentation toolchain\n        run: |\n          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal\n          rustup override set ${{ env.REPO_MSRV }}\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: doc-build\n\n      - name: Build the docs\n        run: |\n          cargo doc --no-deps --lib --document-private-items\n        env:\n          RUSTDOCFLAGS: --cfg docsrs\n          RUSTC_BOOTSTRAP: 1\n\n      - name: Deploy the docs\n        uses: JamesIves/github-pages-deploy-action@v4.8.0\n        if: github.ref == 'refs/heads/trunk'\n        with:\n          token: ${{ secrets.WEB_DEPLOY }}\n          folder: target/doc\n          repository-name: gfx-rs/wgpu-rs.github.io\n          branch: master\n          target-folder: doc\n"
  },
  {
    "path": ".github/workflows/generate.yml",
    "content": "name: cargo-generate\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  #\n  # Dependency versioning\n  #\n\n  # This is the MSRV used by `wgpu` itself.\n  WGPU_MSRV: \"1.87\"\n  RUSTFLAGS: -D warnings\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  cargo-generate:\n    timeout-minutes: 5\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - name: \"01-hello-compute\"\n            path: \"examples/standalone/01_hello_compute\"\n          - name: \"02-hello-window\"\n            path: \"examples/standalone/02_hello_window\"\n          - name: \"custom_backend\"\n            path: \"examples/standalone/custom_backend\"\n\n    name: \"${{ matrix.name }}\"\n\n    steps:\n      - uses: actions/checkout@v6\n\n      # We can't rely on an override here, as that would only set\n      # the toolchain for the current directory, not the newly generated project.\n      - name: Install repo MSRV toolchain\n        run: |\n          rustup toolchain install ${{ env.WGPU_MSRV }} --no-self-update --profile=minimal\n          cargo -V\n\n      - name: Disable debug symbols\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = false\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: cargo-generate-${{ matrix.name }}\n\n      - name: Install `cargo-generate`\n        uses: taiki-e/install-action@v2\n        with:\n          tool: cargo-generate\n\n      - name: Run `cargo-generate`\n        run: |\n          cd ..\n          cargo generate --path wgpu --name ${{ matrix.name }} ${{ matrix.path }}\n\n      - name: Check generated files\n        run: |\n          cd ../${{ matrix.name }}/\n          cat <<EOF >> Cargo.toml\n            [patch.crates-io]\n            wgpu = { path = \"../wgpu/wgpu\" }\n          EOF\n          cargo +${{ env.WGPU_MSRV }} check\n"
  },
  {
    "path": ".github/workflows/lazy.yml",
    "content": "# Non-critical jobs\nname: Lazy\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  RUST_BACKTRACE: full\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  parse-dota2:\n    name: \"Validate Shaders: Dota2\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - run: mkdir naga/data\n\n      - name: Download shaders\n        run: curl https://user.fm/files/v2-5573e18b9f03f42c6ae53c392083da35/dota2-shaders.zip -o naga/data/all.zip\n\n      - name: Unpack shaders\n        run: |\n          cd naga/data\n          unzip all.zip\n\n      - name: Build Naga\n        run: |\n          cd naga\n          cargo build --release -p naga-cli\n\n      - name: Convert shaders\n        run: |\n          cd naga\n          for file in data/*.spv ; do echo \"Translating\" ${file} && ../target/release/naga --validate 27 ${file} ${file}.metal; done\n\n  parse-vulkan-tutorial-shaders:\n    name: \"Validate Shaders: Sascha Willems Vulkan Tutorial\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Download shaders\n        run: cd naga && git clone https://github.com/SaschaWillems/Vulkan.git\n\n      - name: Build Naga\n        run: |\n          cd naga\n          cargo build --release -p naga-cli\n\n      - name: Convert Metal shaders\n        run: |\n          # No needed to stop workflow if we can't validate one file\n          set +e\n          cd naga\n          touch counter\n          SUCCESS_RESULT_COUNT=0\n          FILE_COUNT=0\n          mkdir -p out\n          find \"Vulkan/data/shaders/glsl/\" -name '*.spv' | while read fname;\n          do\n              echo \"Convert: $fname\"\n              FILE_COUNT=$((FILE_COUNT+1))\n              ../target/release/naga --validate 27 $(realpath ${fname}) out/$(basename ${fname}).metal\n              if [[ $? -eq 0 ]]; then\n                  SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1))\n              fi\n              echo \"Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT\" > counter\n          done\n          cat counter\n\n  dneto0_spirv-samples:\n    name: \"Validate Shaders: dneto0 spirv-samples\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Download shaders\n        run: |\n          cd naga\n          git clone https://github.com/dneto0/spirv-samples.git\n\n      - name: Build Naga\n        run: |\n          cargo build --release -p naga-cli\n\n      - name: Install Vulkan SDK\n        uses: ./.github/actions/install-vulkan-sdk\n\n      - name: Compile `spv` from `spvasm`\n        run: |\n          cd naga/spirv-samples\n          mkdir -p spv\n\n          find \"./spvasm\" -name '*.spvasm' | while read fname;\n          do\n              echo \"Convert to spv with spirv-as: $fname\"\n              spirv-as --target-env spv1.3 $(realpath ${fname}) -o ./spv/$(basename ${fname}).spv\n          done;\n\n      - name: Validate `spv` and generate `wgsl`\n        run: |\n          set +e\n          cd naga/spirv-samples\n          SUCCESS_RESULT_COUNT=0\n          FILE_COUNT=0\n          mkdir -p spv\n          mkdir -p wgsl\n\n          echo \"==== Validate spv and generate wgsl ====\"\n          rm -f counter\n          touch counter\n\n          find \"./spv\" -name '*.spv' | while read fname;\n          do\n              echo \"Convert: $fname\"\n              FILE_COUNT=$((FILE_COUNT+1))\n              ../../target/release/naga --validate 27 $(realpath ${fname}) ./wgsl/$(basename ${fname}).wgsl\n              if [[ $? -eq 0 ]]; then\n                  SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1))\n              fi\n              echo \"Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT\" > counter\n          done\n          cat counter\n\n      - name: Validate output `wgsl`\n        run: |\n          set +e\n          cd naga/spirv-samples\n          SUCCESS_RESULT_COUNT=0\n          FILE_COUNT=0\n\n          rm -f counter\n          touch counter\n\n          find \"./wgsl\" -name '*.wgsl' | while read fname;\n          do\n              echo \"Validate: $fname\"\n              FILE_COUNT=$((FILE_COUNT+1))\n              ../../target/release/naga --validate 27 $(realpath ${fname})\n              if [[ $? -eq 0 ]]; then\n                  SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1))\n              fi\n              echo \"Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT\" > counter\n          done\n          cat counter\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\nenv:\n  CARGO_INCREMENTAL: false\n  CARGO_TERM_COLOR: always\n  RUST_BACKTRACE: full\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust WASM target\n        run: rustup target add wasm32-unknown-unknown\n\n      - name: Get `wasm-bindgen` version\n        run: |\n          WASM_BINDGEN_VERSION=$(cargo metadata --format-version 1 --all-features | jq '.packages[] | select(.name == \"wasm-bindgen\") | .version' | tr -d '\"')\n\n          echo $WASM_BINDGEN_VERSION\n\n          echo \"WASM_BINDGEN_VERSION=$WASM_BINDGEN_VERSION\" >> \"$GITHUB_ENV\"\n\n      - name: Install `wasm-bindgen`\n        run: cargo +stable install wasm-bindgen-cli --version=$WASM_BINDGEN_VERSION\n\n      - name: Debug symbols to line-tables-only\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = \"line-tables-only\"\n          EOF\n\n      - name: Caching\n        uses: Swatinem/rust-cache@v2\n        with:\n          key: publish-build\n\n      - name: Build examples\n        run: cargo xtask run-wasm --no-serve\n\n      - name: Deploy WebGPU examples\n        uses: JamesIves/github-pages-deploy-action@v4.8.0\n        if: github.ref == 'refs/heads/trunk'\n        with:\n          token: ${{ secrets.WEB_DEPLOY }}\n          folder: target/generated\n          repository-name: gfx-rs/wgpu-rs.github.io\n          branch: master\n          target-folder: examples/\n"
  },
  {
    "path": ".github/workflows/shaders.yml",
    "content": "name: Shaders\n\non:\n  push:\n    branches-ignore: [\n        # Renovate branches are always PRs, so they will be covered\n        # by the pull_request event.\n        \"renovate/**\",\n        # Branches with the `gh-readonly-queue` prefix are used by the\n        # merge queue, so they are already covered by the `merge_group` event.\n        \"gh-readonly-queue/**\",\n      ]\n  pull_request:\n  merge_group:\n\n# Every time a PR is pushed to, cancel any previous jobs. This\n# makes us behave nicer to github and get faster turnaround times\n# on PRs that are pushed to multiple times in rapid succession.\nconcurrency:\n  group: ${{github.workflow}}-${{github.ref}}\n  cancel-in-progress: ${{github.event_name == 'pull_request'}}\n\njobs:\n  naga-validate-windows:\n    name: \"Validate: HLSL\"\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Debug symbols to `line-tables-only`\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = \"line-tables-only\"\n          EOF\n\n      - uses: Swatinem/rust-cache@v2\n\n      # We must have the FXC job before the DXC job, so the DXC PATH has priority\n      # over the FXC PATH. This is because the windows kits also include an older\n      # version of DXC, which we don't want to use.\n      - name: Setup FXC\n        run: |\n          Get-Childitem -Path \"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\**\\x64\\fxc.exe\" `\n          | Sort-Object -Property LastWriteTime -Descending `\n          | Select-Object -First 1 `\n          | Split-Path -Parent `\n          | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append\n        shell: powershell\n\n      - name: Setup DXC\n        uses: ./.github/actions/install-dxc\n\n      - name: Validate\n        shell: bash\n        run: |\n          set -e\n\n          dxc --version\n\n          cd naga\n          cargo xtask validate hlsl dxc\n          cargo xtask validate hlsl fxc\n\n  naga-validate-macos:\n    name: \"Validate: MSL\"\n    runs-on: macos-15\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Debug symbols to line-tables-only\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = \"line-tables-only\"\n          EOF\n\n      - uses: Swatinem/rust-cache@v2\n\n      - run: |\n          cd naga\n          cargo xtask validate msl\n\n  naga-validate-linux:\n    name: \"Validate: SPIR-V/GLSL/DOT/WGSL\"\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Install Vulkan SDK\n        uses: ./.github/actions/install-vulkan-sdk\n\n      - name: Install Graphviz\n        run: sudo apt-get install graphviz\n\n      - name: Debug symbols to `line-tables-only`\n        shell: bash\n        run: |\n          mkdir -p .cargo\n\n          cat <<EOF >> .cargo/config.toml\n            [profile.dev]\n            debug = \"line-tables-only\"\n          EOF\n\n      - uses: Swatinem/rust-cache@v2\n\n      - run: cd naga; cargo xtask validate spv\n\n      - run: cd naga; cargo xtask validate glsl\n\n      - run: cd naga; cargo xtask validate dot\n\n      - run: cd naga; cargo xtask validate wgsl\n"
  },
  {
    "path": ".gitignore",
    "content": "# Jujutsu\n#\n# When using the wgpu repository through [Jujutsu](https://github.com/martinvonz/jj), `.jj/` dirs.\n# are ignored because Jujutsu writes a `.jj/.gitignore` file containing `/*`. Some tools, like\n# `prettier`, don't handle nested `.gitgnore` properly, but they _do_ handle top-level `.gitignore`\n# patterns properly. So, include it here, even though we shouldn't need it. :cry:\n.jj/\n\n# Generated by Cargo\n# will have compiled files and executables\n/target\n# Include the root lockfile but not the others\n*/Cargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Other\n.fuse_hidden*\n.DS_Store\n\n# IDE/Editor configuration files\n.vscode\n.idea\n\n# Output from capture example\nwgpu/red.png\n\n# Output from render_to_texture example\n**/please_don't_git_push_me.png\n\n# Output from invalid comparison tests\n**/*-actual.png\n**/*-difference.png\n\n# Readme says to check out CTS here\n/cts\n\n# Readme says to put angle in working directory\n*.dll\n\n# Cached GPU config\n.gpuconfig\n\n# Temporary clone location for wasm-bindgen mirroring\nwgpu/src/backend/webgpu/webgpu_sys/wasm_bindgen_clone_tmp\n\n# LLM-related\n.claude/settings.local.json\n"
  },
  {
    "path": ".prettierignore",
    "content": "**/*.frag\n**/*.vert\nnaga/tests/in/\nnaga/tests/out/\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# Global instructions\n\n## Code Style\n- Limit the amount of comments you put in the code to a strict minimum. You should almost never add comments, except sometimes on non-trivial code, function definitions if the arguments aren't self-explanatory, and class definitions and their members.\n- Do not use emoji.\n\n## Workflow\n\n- You can test that the code builds by running `cargo build`. Don't pass `--release` unless there's a specific need to test a release build, it's much slower.\n- After making code changes, ensure the code is formatted by using `cargo fmt`, linted by using `cargo clippy --tests`. If there are no errors, you can use `cargo xtask test` and `cargo xtask cts --backend <backend>` to run tests (to fully validate a change, run both). On MacOS, the backend is `metal`. On Windows, the backend is `dx12`. On Linux, the backend is `vulkan`.\n- Do not perform commits yourself, ever.\n- Use the WebGPU and WGSL specifications as a reference to determine the correct behavior. Do not assume that a behavior is correct just because the CTS expects it.\n\n## Changelog\n\nWe maintain a changelog in CHANGELOG.md. Changes should be noted in the\nchangelog if they are user-visible (changes to documented public APIs,\nsignificant bug fixes, or new functionality). Changelog descriptions should be\nconcise. If you are not sure whether something should be in the CHANGELOG or\nyou are not sure how to describe the change, ask the user for guidance.\n\n## More information about tests\n\nThere is some more detailed information about tests in @docs/testing.md.\n\n# CTS\n\n## Listing and Running Individual Tests\n\nEach line is a selector that will run multiple subtests. You can run a test with:\n\n```bash\ncargo xtask cts 'webgpu:selector/path:test:*'\n```\n\nFor fixing problems, it may be useful to run specific cases. You can get a list of the subtests with:\n\n```bash\ncargo xtask cts -- --list 'webgpu:selector,path:test:*'\n```\n\nThen you can pass individual subtests on the command line as the selector.\n\n## Running Suites\n\nTo run a suite of CTS tests and keep just the summary at the end:\n\n```bash\ncargo xtask cts 'webgpu:selector,path:test:*' 2>&1 | grep -E \"(Summary|Passed|Failed)\" | tail -5\n```\n\nTo run a suite of tests and keep just the failing tests:\n\n```bash\ncargo xtask cts 'webgpu:selector,path:test:*' 2>&1 | grep \"\\[fail\\]\"\n```\n\n## Keeping Track of Results\n\nWe maintain three files listing CTS test selectors that are expected to pass,\nnot pass, or to be entirely skipped. These files are respectively\ncts_runner/test.lst, cts_runner/fail.lst, and cts_runner/skip.lst.\n\nIf you fix a CTS test, add the selector to test.lst. Be aware that the file is\nnot an exhaustive list of passing tests.\n\nCI expects every test in test.lst to pass, so only add suites that have zero\nfailures to test.lst. Do not add any suites that are not 100% pass/skip, even\nif they are ≥99% passing.\n\nList suites with failures in fail.lst. You can put a comment `// xx%`\nnoting the pass rate, especially in cases where the pass rate is high.\n\nList suites that are ≥90% skips in skip.lst\n\nWhen adding to a lst file, use wildcards to identify the tests with as few lines\nas possible. In some cases when adding tests it may be possible to combine with\nexisting lines to use a higher level wildcard. Only use a higher level wildcard\nwhen all the tests it will match go in the same file. Do not use a higher level\nwildcard if it would match tests that belong in a different file.\n\n## Source for the CTS Tests\n\nThe TypeScript sources for the CTS are under cts/src. There is also generated JavaScript in cts/out.\nDo not assume that a behavior is correct just because a CTS test expects it. Verify the correct\nbehavior in the WebGPU or WGSL specification.\n\n# Overview of the Code\n\nSee `docs/big-picture.png` for an overview of the system.\n\nThe major components are:\n\n* `wgpu-hal` implements a backend for each supported graphics API (Vulkan, DX12, Metal, GLES).\n\n* `wgpu-core` implements the WebGPU API, including resource management and validation.\n  It calls the platform graphics APIs via `wgpu-hal`, and uses `naga` for shader translation.\n\n* `wgpu` is the native Rust API. In addition to providing bindings to `wgpu-core`, the `wgpu`\n  crate can also be compiled to WASM and built against the \"WebGPU\" backend, where it uses\n  whatever WebGPU implementation is provided by the WASM environment. `wgpu` is not used\n  by Deno and Firefox.\n\n* `naga` is the shader translator. It reads shaders in WGSL, GLSL, or SPIR-V,\n  translates to Naga IR, and then writes shaders in GLSL, HLSL, MSL (Metal Shading Language), SPIR-V, or WGSL.\n  It is responsible for validating that WGSL shaders are valid according to the WGSL language specification.\n\n* `wgpu-types` contains some type definitions that are applicable both to `wgpu-core` and to\n  the `wgpu` \"WebGPU\" backend.\n\n* `deno_webgpu` contains WebGPU bindings for the Deno Javascript runtime.\n  We also use Deno as a test environment for running the WebGPU CTS.\n  Only make a change in the Deno bindings if you are sure that the issue\n  doesn't apply to other clients (Firefox or `wgpu` Rust API). If it does\n  apply to other clients, the issue should probably be fixed in `wgpu-core`.\n\n\nFor a more detailed discussion of the `wgpu` architecture, refer to\n<https://github.com/gfx-rs/wgpu/wiki/Architecture>.\n\n## Naga\n\n### Constant Evaluator (`naga/src/proc/constant_evaluator.rs`)\n\nThe constant evaluator is responsible for evaluating constant expressions at compile time in WGSL/GLSL shaders.\n\n**Key Components:**\n\n1. **Expression Handling** (~line 1228): The `try_eval_and_append_impl` method handles different expression types and evaluates them if possible.\n\n2. **Type Conversions**:\n   - `cast()` (~line 2256): Handles type conversions with `Expression::As` where `convert` is `Some(width)`\n   - `bitcast()` (~line 2449): Handles bit reinterpretation with `Expression::As` where `convert` is `None`\n   - Key insight: For bitcast, the target width equals the source width (bit-preserving operation)\n\n3. **Helper Macros**:\n   - `gen_component_wise_extractor!`: Generates functions for component-wise operations on scalars/vectors\n   - `component_wise_scalar!`, `component_wise_float!`: Convenience macros for applying operations to each component\n\n4. **Borrow Checker Patterns**:\n   - When implementing methods that need to call themselves recursively or make multiple mutable borrows, extract all needed data from `self` fields before the recursive calls\n   - Clone vectors before iterating if you need to mutably borrow `self` during iteration\n\n## Expression Types and Type Inference\n\n- `Expression::As` with `convert: None` represents bitcast operations\n- The validator (`naga/src/valid/expression.rs` ~line 1132) determines result types by:\n  - Getting source type's scalar\n  - Updating the `kind` to target kind\n  - Keeping the same `width` for bitcast (no conversion)\n\n# Other\n\nThis is stuff that Claude wrote for itself. It can probably be improved.\n\n## Naga Tests\n\n**Test Structure:**\n\n1. **Unit Tests**: In the same file as the code being tested\n   - Example: `naga/src/proc/constant_evaluator.rs` has tests in a `#[cfg(test)] mod tests` block\n   - Pattern: Create arenas, build expressions, evaluate, assert results\n\n2. **Snapshot Tests**:\n   - Input files: `naga/tests/in/wgsl/*.wgsl`\n   - Output files: `naga/tests/out/wgsl/*.wgsl`\n   - Run with: `cargo test -p naga --test naga snapshots::convert_snapshots_wgsl`\n   - The output shows how constant expressions are evaluated at compile time\n\n3. **Adding Integration Tests**:\n   - Add test WGSL code to `naga/tests/in/wgsl/const-exprs.wgsl` for const expression tests\n   - The test automatically parses, validates, and generates output in `naga/tests/out/wgsl/wgsl-const-exprs.wgsl`\n   - Verify constant evaluation by checking the output file\n\n**Running Tests:**\n```bash\n# Unit tests only\ncargo test -p naga --lib\n\n# Specific test\ncargo test -p naga --lib bitcast\n\n# All naga tests (including integration)\ncargo test -p naga\n\n# WGSL snapshot tests\ncargo test -p naga --test naga snapshots::convert_snapshots_wgsl\n```\n\n## WGSL Specification Implementation\n\nWhen implementing WGSL built-in functions:\n\n1. **Read the spec carefully**: WGSL spec section numbers are usually commented in code (e.g., \"17.2.1. bitcast\")\n2. **Check parameterization**: The spec lists exactly which type combinations are valid\n3. **Don't assume types**: For bitcast, only `i32`, `u32`, `f32` are specified - not 64-bit types\n4. **AbstractInt special cases**: Some operations have special handling for abstract integers\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n<!--\nPlease add your PR to the changelog! Choose from a top level and bottom\nlevel category, then write your changes like follows:\n\n- Describe your change in a user friendly format. By @yourslug in [#99999](https://github.com/gfx-rs/wgpu/pull/99999)\n\nYou can add additional user facing information if it's a major breaking change. You can use the following to help:\n\n```diff\n- Old code\n+ New code\n```\n\nTop level categories:\n\n- Major changes\n- Added/New Features\n- Changes\n- Bug Fixes (that don't change API)\n- Performance\n- Documentation\n- Dependency Updates\n- deno_webgpu\n- Examples\n- Testing/Internal\n\nBottom level categories:\n\n- General\n- naga\n- Validation\n- DX12\n- Vulkan\n- Metal\n- GLES / OpenGL\n- WebGPU\n- Emscripten\n- Hal\n-->\n\n## Unreleased\n\n### Changes\n\n#### General\n\n- `Features::CLIP_DISTANCE`, `naga::Capabilities::CLIP_DISTANCE`, and `naga::BuiltIn::ClipDistance` have been renamed to `CLIP_DISTANCES` and `ClipDistances` (viz., pluralized) as appropriate, to match the WebGPU spec. By @ErichDonGubler in [#9267](https://github.com/gfx-rs/wgpu/pull/9267).\n\n#### Validation\n\n- Add clip distances validation for `maxInterStageShaderVariables`. By @ErichDonGubler in [8762](https://github.com/gfx-rs/wgpu/pull/8762). This may break some existing programs, but it compiles with the WebGPU spec.\n\n### Bug Fixes\n\n#### General\n\n- Fix limit comparison logic for `max_inter_stage_shader_variables` By @ErichDonGubler in [9264](https://github.com/gfx-rs/wgpu/pull/9264).\n\n## v29.0.0 (2026-03-18)\n\n### Major Changes\n\n#### `Surface::get_current_texture` now returns `CurrentSurfaceTexture` enum\n\n`Surface::get_current_texture` no longer returns `Result<SurfaceTexture, SurfaceError>`.\nInstead, it returns a single `CurrentSurfaceTexture` enum that represents all possible outcomes as variants.\n`SurfaceError` has been removed, and the `suboptimal` field on `SurfaceTexture` has been replaced by a dedicated `Suboptimal` variant.\n\n```rust\nmatch surface.get_current_texture() {\n    wgpu::CurrentSurfaceTexture::Success(frame) => { /* render */ }\n    wgpu::CurrentSurfaceTexture::Timeout\n      | wgpu::CurrentSurfaceTexture::Occluded => { /* skip frame */ }\n    wgpu::CurrentSurfaceTexture::Outdated\n      | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => { /* reconfigure surface */ }\n    wgpu::CurrentSurfaceTexture::Lost => { /* reconfigure surface, or recreate device if device lost */ }\n    wgpu::CurrentSurfaceTexture::Validation => {\n        /* Only happens if there is a validation error and you\n           have registered a error scope or uncaptured error handler. */\n    }\n}\n```\n\nBy @cwfitzgerald, @Wumpf, and @emilk in [#9141](https://github.com/gfx-rs/wgpu/pull/9141) and [#9257](https://github.com/gfx-rs/wgpu/pull/9257).\n\n#### `InstanceDescriptor` initialization APIs and display handle changes\n\nA display handle represents a connection to the platform's display server (e.g. a Wayland or X11 connection on Linux). This is distinct from a window — a display handle is the system-level connection through which windows are created and managed.\n\n`InstanceDescriptor`'s convenience constructors (an implementation of `Default` and the static `from_env_or_default` method) have been removed. In their place are new static methods that force recognition of whether a display handle is used:\n\n- `new_with_display_handle`\n- `new_with_display_handle_from_env`\n- `new_without_display_handle`\n- `new_without_display_handle_from_env`\n\nIf you are using `winit`, this can be populated using `EventLoop::owned_display_handle`.\n\n```diff\n- InstanceDescriptor::default();\n- InstanceDescriptor::from_env_or_default();\n+ InstanceDescriptor::new_with_display_handle(Box::new(event_loop.owned_display_handle()));\n+ InstanceDescriptor::new_with_display_handle_from_env(Box::new(event_loop.owned_display_handle()));\n```\n\nAdditionally, `DisplayHandle` is now optional when creating a surface if a display handle was already passed to `InstanceDescriptor`. This means that once you've provided the display handle at instance creation time, you no longer need to pass it again for each surface you create.\n\nBy @MarijnS95 in [#8782](https://github.com/gfx-rs/wgpu/pull/8782)\n\n#### Bind group layouts now optional in `PipelineLayoutDescriptor`\n\nThis allows gaps in bind group layouts and adds full support for unbinding, bring us in compliance with the WebGPU spec. As a result of this `PipelineLayoutDescriptor`'s `bind_group_layouts` field now has type of `&[Option<&BindGroupLayout>]`. To migrate wrap bind group layout references in `Some`:\n\n```diff\n  let pl_desc = wgpu::PipelineLayoutDescriptor {\n      label: None,\n      bind_group_layouts: &[\n-         &bind_group_layout\n+         Some(&bind_group_layout)\n      ],\n      immediate_size: 0,\n  });\n```\n\nBy @teoxoy in [#9034](https://github.com/gfx-rs/wgpu/pull/9034).\n\n#### MSRV update\n\n`wgpu` now has a new MSRV policy. This release has an MSRV of **1.87**. This is lower than v27's 1.88 and v28's 1.92. Going forward, we will only bump wgpu's MSRV if it has tangible benefits for the code, and we will never bump to an MSRV higher than `stable - 3`. So if stable is at 1.97 and 1.94 brought benefit to our code, we could bump it no higher than 1.94. As before, MSRV bumps will always be breaking changes.\n\nBy @cwfitzgerald in [#8999](https://github.com/gfx-rs/wgpu/pull/8999).\n\n#### `WriteOnly`\n\nTo ensure memory safety when accessing mapped GPU memory, `MapMode::Write` buffer mappings (`BufferViewMut` and also `QueueWriteBufferView`) can no longer be dereferenced to Rust `&mut [u8]`. Instead, they must be used through the new pointer type `wgpu::WriteOnly<[u8]>`, which does not allow reading at all.\n\n`WriteOnly<[u8]>` is designed to offer similar functionality to `&mut [u8]` and have almost no performance overhead, but you will probably need to make some changes for anything more complicated than `get_mapped_range_mut().copy_from_slice(my_data)`; in particular, replacing `view[start..end]` with `view.slice(start..end)`.\n\nBy @kpreid in [#9042](https://github.com/gfx-rs/wgpu/pull/9042).\n\n#### Depth/stencil state changes\n\nThe `depth_write_enabled` and `depth_compare` members of `DepthStencilState` are now optional, and may be omitted when they do not apply, to match WebGPU.\n\n`depth_write_enabled` is applicable, and must be `Some`, if `format` has a depth aspect, i.e., is a depth or depth/stencil format. Otherwise, a value of `None` best reflects that it does not apply, although `Some(false)` is also accepted.\n\n`depth_compare` is applicable, and must be `Some`, if `depth_write_enabled` is `Some(true)`, or if `depth_fail_op` for either stencil face is not `Keep`. Otherwise, a value of `None` best reflects that it does not apply, although `Some(CompareFunction::Always)` is also accepted.\n\nThere is also a new constructor `DepthStencilState::stencil` which may be used instead of a struct literal for stencil operations.\n\nExample 1: A configuration that does a depth test and writes updated values:\n\n```diff\n depth_stencil: Some(wgpu::DepthStencilState {\n     format: wgpu::TextureFormat::Depth32Float,\n-    depth_write_enabled: true,\n-    depth_compare: wgpu::CompareFunction::Less,\n+    depth_write_enabled: Some(true),\n+    depth_compare: Some(wgpu::CompareFunction::Less),\n     stencil: wgpu::StencilState::default(),\n     bias: wgpu::DepthBiasState::default(),\n }),\n```\n\nExample 2: A configuration with only stencil:\n\n```diff\n depth_stencil: Some(wgpu::DepthStencilState {\n     format: wgpu::TextureFormat::Stencil8,\n-    depth_write_enabled: false,\n-    depth_compare: wgpu::CompareFunction::Always,\n+    depth_write_enabled: None,\n+    depth_compare: None,\n     stencil: wgpu::StencilState::default(),\n     bias: wgpu::DepthBiasState::default(),\n }),\n```\n\nExample 3: The previous example written using the new `stencil()` constructor:\n\n```rust\ndepth_stencil: Some(wgpu::DepthStencilState::stencil(\n    wgpu::TextureFormat::Stencil8,\n    wgpu::StencilState::default(),\n)),\n```\n\n#### D3D12 Agility SDK support\n\nAdded support for loading a specific [DirectX 12 Agility SDK](https://devblogs.microsoft.com/directx/directx12agility/) runtime via the [Independent Devices API](https://devblogs.microsoft.com/directx/d3d12-independent-devices/). The Agility SDK lets applications ship a newer D3D12 runtime alongside their binary, unlocking the latest D3D12 features without waiting for an OS update.\n\nConfigure it programmatically:\n\n```rust\nlet options = wgpu::Dx12BackendOptions {\n    agility_sdk: Some(wgpu::Dx12AgilitySDK {\n        sdk_version: 619,\n        sdk_path: \"path/to/sdk/bin/x64\".into(),\n    }),\n    ..Default::default()\n};\n```\n\nOr via environment variables:\n\n```\nWGPU_DX12_AGILITY_SDK_PATH=path/to/sdk/bin/x64\nWGPU_DX12_AGILITY_SDK_VERSION=619\n```\n\nThe `sdk_version` must match the version of the `D3D12Core.dll` in the provided path exactly, or loading will fail.\n\nIf the Agility SDK fails to load (e.g. version mismatch, missing DLL, or unsupported OS), wgpu logs a warning and falls back to the system D3D12 runtime.\n\nBy @cwfitzgerald in [#9130](https://github.com/gfx-rs/wgpu/pull/9130).\n\n#### `primitive_index` is now a WGSL `enable` extension\n\nWGSL shaders using `@builtin(primitive_index)` must now request it with `enable primitive_index;`. The `SHADER_PRIMITIVE_INDEX` feature has been renamed to `PRIMITIVE_INDEX` and moved from `FeaturesWGPU` to `FeaturesWebGPU`. By @inner-daemons in [#8879](https://github.com/gfx-rs/wgpu/pull/8879) and @andyleiserson in [#9101](https://github.com/gfx-rs/wgpu/pull/9101).\n\n```diff\n- device.features().contains(wgpu::FeaturesWGPU::SHADER_PRIMITIVE_INDEX)\n+ device.features().contains(wgpu::FeaturesWebGPU::PRIMITIVE_INDEX)\n```\n\n```wgsl\n// WGSL shaders must now include this directive:\nenable primitive_index;\n```\n\n#### `maxInterStageShaderComponents` replaced by `maxInterStageShaderVariables`\n\nMigrated from the `max_inter_stage_shader_components` limit to `max_inter_stage_shader_variables`, following the latest WebGPU spec. Components counted individual scalars (e.g. a `vec4` = 4 components), while variables counts locations (e.g. a `vec4` = 1 variable). This changes validation in a way that should not affect most programs. By @ErichDonGubler in [#8652](https://github.com/gfx-rs/wgpu/pull/8652), [#8792](https://github.com/gfx-rs/wgpu/pull/8792).\n\n```diff\n- limits.max_inter_stage_shader_components\n+ limits.max_inter_stage_shader_variables\n```\n\n#### Other Breaking Changes\n\n- Use clearer field names for `StageError::InvalidWorkgroupSize`. By @ErichDonGubler in [#9192](https://github.com/gfx-rs/wgpu/pull/9192).\n\n### New Features\n\n#### General\n\n- Added TLAS binding array support via `ACCELERATION_STRUCTURE_BINDING_ARRAY`. By @kvark in [#8923](https://github.com/gfx-rs/wgpu/pull/8923).\n- Added `wgpu-naga-bridge` crate with conversions between `naga` and `wgpu-types` (features to capabilities, storage format mapping, shader stage mapping). By @atlv24 in [#9201](https://github.com/gfx-rs/wgpu/pull/9201).\n- Added support for cooperative load/store operations in shaders. Currently only WGSL on the input and SPIR-V, METAL, and WGSL on the output are supported. By @kvark in [#8251](https://github.com/gfx-rs/wgpu/issues/8251).\n- Added support for per-vertex attributes in fragment shaders. Currently only WGSL input is supported, and only SPIR-V or WGSL output is supported. By @atlv24 in [#8821](https://github.com/gfx-rs/wgpu/issues/8821).\n- Added support for no-perspective barycentric coordinates. By @atlv24 in [#8852](https://github.com/gfx-rs/wgpu/issues/8852).\n- Added support for obtaining `AdapterInfo` from `Device`. By @sagudev in [#8807](https://github.com/gfx-rs/wgpu/pull/8807).\n- Added `Limits::or_worse_values_from`. By @atlv24 in [#8870](https://github.com/gfx-rs/wgpu/pull/8870).\n- Added `Features::FLOAT32_BLENDABLE` on Vulkan and Metal. By @timokoesters in [#8963](https://github.com/gfx-rs/wgpu/pull/8963) and @andyleiserson in [#9032](https://github.com/gfx-rs/wgpu/pull/9032).\n- Added `Dx12BackendOptions::force_shader_model` to allow using advanced features in passthrough shaders without bundling DXC. By @inner-daemons in [#8984](https://github.com/gfx-rs/wgpu/pull/8984).\n- Changed passthrough shaders to not require an entry point parameter, so that the same shader module may be used in multiple entry points. Also added support for metallib passthrough. By @inner-daemons in [#8886](https://github.com/gfx-rs/wgpu/pull/8886).\n- Added `Dx12Compiler::Auto` to automatically use static or dynamic DXC if available, before falling back to FXC. By @inner-daemons in [#8882](https://github.com/gfx-rs/wgpu/pull/8882).\n- Added support for `insert_debug_marker`, `push_debug_group` and `pop_debug_group` on WebGPU. By @evilpie in [#9017](https://github.com/gfx-rs/wgpu/pull/9017).\n- Added support for `@builtin(draw_index)` to the vulkan backend. By @inner-daemons in [#8883](https://github.com/gfx-rs/wgpu/pull/8883).\n- Added `TextureFormat::channels` method to get some information about which color channels are covered by the texture format. By @TornaxO7 in [#9167](https://github.com/gfx-rs/wgpu/pull/9167)\n- BREAKING: Add `V6_8` variant to `DxcShaderModel` and `naga::back::hlsl::ShaderModel`. By @inner-daemons in [#8882](https://github.com/gfx-rs/wgpu/pull/8882) and @ErichDonGubler in [#9083](https://github.com/gfx-rs/wgpu/pull/9083).\n- BREAKING: Add `V6_9` variant to `DxcShaderModel` and `naga::back::hlsl::ShaderModel`. By @ErichDonGubler in [#9083](https://github.com/gfx-rs/wgpu/pull/9083).\n\n#### naga\n\n- Initial wgsl-in ray tracing pipelines. By @Vecvec in [#8570](https://github.com/gfx-rs/wgpu/pull/8570).\n- wgsl-out ray tracing pipelines. By @Vecvec in [#8970](https://github.com/gfx-rs/wgpu/pull/8970).\n- Allow parsing shaders which make use of `SPV_KHR_non_semantic_info` for debug info. Also removes `naga::front::spv::SUPPORTED_EXT_SETS`. By @inner-daemons in [#8827](https://github.com/gfx-rs/wgpu/pull/8827).\n- Added memory decorations for storage buffers: `coherent`, supported on all native backends, and `volatile`, only on Vulkan and GL. By @atlv24 in [#9168](https://github.com/gfx-rs/wgpu/pull/9168).\n- Made the following available in `const` contexts; by @ErichDonGubler in [#8943](https://github.com/gfx-rs/wgpu/pull/8943):\n  - `naga`\n    - `Arena::len`\n    - `Arena::is_empty`\n    - `Range::first_and_last`\n    - `front::wgsl::Frontend::set_options`\n    - `ir::Block::is_empty`\n    - `ir::Block::len`\n\n#### GLES\n\n- Added `GlDebugFns` option in `GlBackendOptions` to control OpenGL debug functions (`glPushDebugGroup`, `glPopDebugGroup`, `glObjectLabel`, etc.). Automatically disables them on Mali GPUs to work around a driver crash. By @Xavientois in [#8931](https://github.com/gfx-rs/wgpu/pull/8931).\n\n#### WebGPU\n\n- Added support for `insert_debug_marker`, `push_debug_group` and `pop_debug_group`. By @evilpie in [#9017](https://github.com/gfx-rs/wgpu/pull/9017).\n- Added support for `begin_occlusion_query` and `end_occlusion_query`. By @evilpie in [#9039](https://github.com/gfx-rs/wgpu/pull/9039).\n\n### Changes\n\n#### General\n\n- Tracing now uses the `.metal` extension for metal source files, instead of `.msl`. By @inner-daemons in [#8880](https://github.com/gfx-rs/wgpu/pull/8880).\n- BREAKING: Several error APIs were changed by @ErichDonGubler in [#9073](https://github.com/gfx-rs/wgpu/pull/9073) and [#9205](https://github.com/gfx-rs/wgpu/pull/9205):\n  - `BufferAccessError`:\n    - Split the `OutOfBoundsOverrun` variant into new `OutOfBoundsStartOffsetOverrun` and `OutOfBoundsEndOffsetOverrun` variants.\n    - Removed the `NegativeRange` variant in favor of new `MapStartOffsetUnderrun` and `MapStartOffsetOverrun` variants.\n  - Split the `TransferError::BufferOverrun` variant into new `BufferStartOffsetOverrun` and `BufferEndOffsetOverrun` variants.\n  - `ImmediateUploadError`:\n    - Removed the `TooLarge` variant in favor of new `StartOffsetOverrun` and `EndOffsetOverrun` variants.\n    - Removed the `Unaligned` variant in favor of new `StartOffsetUnaligned` and `SizeUnaligned` variants.\n    - Added the `ValueStartIndexOverrun` and `ValueEndIndexOverrun` invariants\n- The various \"max resources per stage\" limits are now capped at 100, so that their total remains below `max_bindings_per_bind_group`, as required by WebGPU. By @andyleiserson in [#9118](https://github.com/gfx-rs/wgpu/pull/9118).\n- The `max_uniform_buffer_binding_size` and `max_storage_buffer_binding_size` limits are now `u64` instead of `u32`, to match WebGPU. By @wingertge in [#9146](https://github.com/gfx-rs/wgpu/pull/9146).\n- The main 3 native backends now report their limits properly. By @teoxoy in [#9196](https://github.com/gfx-rs/wgpu/pull/9196).\n\n#### naga\n\n- Naga and `wgpu` now reject shaders with an `enable` directive for functionality that is not available, even if that functionality is not used by the shader. By @andyleiserson in [#8913](https://github.com/gfx-rs/wgpu/pull/8913).\n- Prevent UB from incorrectly using ray queries on HLSL. By @Vecvec in [#8763](https://github.com/gfx-rs/wgpu/pull/8763).\n- Added support for dual-source blending in SPIR-V shaders. By @andyleiserson in [#8865](https://github.com/gfx-rs/wgpu/pull/8865).\n- Added `supported_capabilities` to all backends. By @inner-daemons in [#9068](https://github.com/gfx-rs/wgpu/pull/9068).\n- Updated codespan-reporting to 0.13. By @cwfitzgerald in [#9243](https://github.com/gfx-rs/wgpu/pull/9243).\n\n#### Metal\n\n- Use autogenerated `objc2` bindings internally, which should resolve a lot of leaks and unsoundness. By @madsmtm in [#5641](https://github.com/gfx-rs/wgpu/pull/5641).\n- Implements ray-tracing acceleration structures for metal backend. By @lichtso in [#8071](https://github.com/gfx-rs/wgpu/pull/8071).\n- Remove mutex for `MTLCommandQueue` because the Metal object is thread-safe. By @andyleiserson in [#9217](https://github.com/gfx-rs/wgpu/pull/9217).\n\n#### deno_webgpu\n\n- Expose the `GPU.wgslLanguageFeatures` property. By @andyleiserson in [#8884](https://github.com/gfx-rs/wgpu/pull/8884).\n- `GPUFeatureName` now includes all `wgpu` extensions. Feature names for extensions should be written with a `wgpu-` prefix, although unprefixed names that were accepted previously are still accepted. By @andyleiserson in [#9163](https://github.com/gfx-rs/wgpu/pull/9163).\n\n#### Hal\n\n- Make ordered texture and buffer uses hal specific. By @NiklasEi in [#8924](https://github.com/gfx-rs/wgpu/pull/8924).\n\n### Bug Fixes\n\n#### General\n\n- Tracing support has been restored. By @andyleiserson in [#8429](https://github.com/gfx-rs/wgpu/pull/8429).\n- Pipelines using passthrough shaders now correctly require explicit pipeline layout. By @inner-daemons in [#8881](https://github.com/gfx-rs/wgpu/pull/8881).\n- Allow using a shader that defines I/O for dual-source blending in a pipeline that does not make use of it. By @andyleiserson in [#8856](https://github.com/gfx-rs/wgpu/pull/8856).\n- Improve validation of dual-source blending, by @andyleiserson in [#9200](https://github.com/gfx-rs/wgpu/pull/9200):\n  - Validate structs with `@blend_src` members whether or not they are used by an entry point.\n  - Dual-source blending is not supported when there are multiple color attachments.\n  - `TypeFlags::IO_SHAREABLE` is not set for structs other than `@blend_src` structs.\n- Validate `strip_index_format` isn't None and equals index buffer format for indexed drawing with strip topology. By @beicause in [#8850](https://github.com/gfx-rs/wgpu/pull/8850).\n- BREAKING: Renamed `EXPERIMENTAL_PASSTHROUGH_SHADERS` to `PASSTHROUGH_SHADERS` and made this no longer an experimental feature. By @inner-daemons in [#9054](https://github.com/gfx-rs/wgpu/pull/9054).\n- BREAKING: End offsets in trace and `player` commands are now represented using `offset` + `size` instead. By @ErichDonGubler in [#9073](https://github.com/gfx-rs/wgpu/pull/9073).\n- Validate some uncaught cases where buffer transfer operations could overflow when computing an end offset. By @ErichDonGubler in [#9073](https://github.com/gfx-rs/wgpu/pull/9073).\n- Fix `local_invocation_id` and `local_invocation_index` being written multiple times in HLSL/MSL backends, and naming conflicts when users name variables `__local_invocation_id` or `__local_invocation_index`. By @inner-daemons in [#9099](https://github.com/gfx-rs/wgpu/pull/9099).\n- Added internal labels to validation GPU objects and timestamp normalization code to improve clarity in graphics debuggers. By @szostid in [#9094](https://github.com/gfx-rs/wgpu/pull/9094)\n- Fix multi-planar texture copying. By @noituri in [#9069](https://github.com/gfx-rs/wgpu/pull/9069)\n\n#### naga\n\n- The validator checks that override-sized arrays have a positive size, if overrides have been resolved. By @andyleiserson in [#8822](https://github.com/gfx-rs/wgpu/pull/8822).\n- Fix some cases where f16 constants were not working. By @andyleiserson in [#8816](https://github.com/gfx-rs/wgpu/pull/8816).\n- Use wrapping arithmetic when evaluating constant expressions involving `u32`. By @andyleiserson in [#8912](https://github.com/gfx-rs/wgpu/pull/8912).\n- Fix missing side effects from sequence expressions in GLSL. By @Vipitis in [#8787](https://github.com/gfx-rs/wgpu/pull/8787).\n- Naga now enforces the `@must_use` attribute on WGSL built-in functions, when applicable. You can waive the error with a phony assignment, e.g., `_ = subgroupElect()`. By @andyleiserson in [#8713](https://github.com/gfx-rs/wgpu/pull/8713).\n- Reject zero-value construction of a runtime-sized array with a validation error. Previously it would crash in the HLSL backend. By @mooori in [#8741](https://github.com/gfx-rs/wgpu/pull/8741).\n- Reject splat vector construction if the argument type does not match the type of the vector's scalar. Previously it would succeed. By @mooori in [#8829](https://github.com/gfx-rs/wgpu/pull/8829).\n- Fixed `workgroupUniformLoad` incorrectly returning an atomic when called on an atomic, it now returns the inner `T` as per the spec. By @cryvosh in [#8791](https://github.com/gfx-rs/wgpu/pull/8791).\n- Fixed constant evaluation for `sign()` builtin to return zero when the argument is zero. By @mandryskowski in [#8942](https://github.com/gfx-rs/wgpu/pull/8942).\n- Allow array generation to compile with the macOS 10.12 Metal compiler. By @madsmtm in [#8953](https://github.com/gfx-rs/wgpu/pull/8953)\n- Naga now detects bitwise shifts by a constant exceeding the operand bit width at compile time, and disallows scalar-by-vector and vector-by-scalar shifts in constant evaluation. By @andyleiserson in [#8907](https://github.com/gfx-rs/wgpu/pull/8907).\n- Naga uses wrapping arithmetic when evaluating dot products on concrete integer types (`u32` and `i32`). By @BKDaugherty in [#9142](https://github.com/gfx-rs/wgpu/pull/9142).\n- Disallow negation of a matrix in WGSL. By @andyleiserson in [#9157](https://github.com/gfx-rs/wgpu/pull/9157).\n- Fix evaluation order of compound assignment (e.g. `+=`) LHS and RHS. By @andyleiserson in [#9181](https://github.com/gfx-rs/wgpu/pull/9181).\n- Fixed invalid MSL when `float16`-format vertex input data was accessed via an `f16`-type variable in a vertex shader. By @andyleiserson in [#9166](https://github.com/gfx-rs/wgpu/pull/9166).\n\n#### Validation\n\n- Fixed validation of the texture format in GPUDepthStencilState when neither depth nor stencil is actually enabled. By @andyleiserson in [#8766](https://github.com/gfx-rs/wgpu/pull/8766).\n- Check that depth bias is not used with non-triangle topologies. By @andyleiserson in [#8856](https://github.com/gfx-rs/wgpu/pull/8856).\n- Check that if the shader outputs `frag_depth`, then the pipeline must have a depth attachment. By @andyleiserson in [#8856](https://github.com/gfx-rs/wgpu/pull/8856).\n- Fix incorrect acceptance of some swizzle selectors that are not valid for their operand, e.g. `const v = vec2<i32>(); let r = v.xyz`. By @andyleiserson in [#8949](https://github.com/gfx-rs/wgpu/pull/8949).\n- Fixed calculation of the total number of bindings in a pipeline layout when validating against device limits. By @andyleiserson in [#8997](https://github.com/gfx-rs/wgpu/pull/8997).\n- Reject non-constructible types (runtime- and override-sized arrays, and structs containing non-constructible types) in more places where they should not be allowed. By @andyleiserson in [#8873](https://github.com/gfx-rs/wgpu/pull/8873).\n- The query set type for an occlusion query is now validated when opening the render pass, in addition to within the call to `beginOcclusionQuery`. By @andyleiserson in [#9086](https://github.com/gfx-rs/wgpu/pull/9086).\n- Require that the blend factor is `One` when the blend operation is `Min` or `Max`. The `BlendFactorOnUnsupportedTarget` error is now reported within `ColorStateError` rather than directly in `CreateRenderPipelineError`. By @andyleiserson in [#9110](https://github.com/gfx-rs/wgpu/pull/9110).\n\n#### Vulkan\n\n- Fixed a variety of mesh shader SPIR-V writer issues from the original implementation. By @inner-daemons in [#8756](https://github.com/gfx-rs/wgpu/pull/8756)\n- Offset the vertex buffer device address when building a BLAS instead of using the `first_vertex` field. By @Vecvec in [#9220](https://github.com/gfx-rs/wgpu/pull/9220)\n- Remove incorrect ordered texture uses. By @NiklasEi in [#8924](https://github.com/gfx-rs/wgpu/pull/8924).\n\n#### Metal / macOS\n\n- Fix one-second delay when switching a wgpu app to the foreground. By [@emilk](https://github.com/emilk) in [#9141](https://github.com/gfx-rs/wgpu/pull/9141)\n- Work around Metal driver bug with atomic textures. By @atlv24 in [#9185](https://github.com/gfx-rs/wgpu/pull/9185)\n- Fix setting an immediate for a Mesh shader. By [@waywardmonkeys](https://github.com/waywardmonkeys) in [#9254](https://github.com/gfx-rs/wgpu/pull/9254)\n\n#### GLES\n\n- `DisplayHandle` should now be passed to `InstanceDescriptor` for correct EGL initialization on Wayland. By @MarijnS95 in [#8012](https://github.com/gfx-rs/wgpu/pull/8012)\n  Note that the existing workaround to create surfaces before the adapter is no longer valid.\n- Changing shader constants now correctly recompiles the shader. By @DerSchmale in [#8291](https://github.com/gfx-rs/wgpu/pull/8291).\n\n### Performance\n\n#### GLES\n\n- The GL backend would now try to take advantage of `GL_EXT_multisampled_render_to_texture` extension when applicable to skip the multi-sample resolve operation. By @opstic in [#8536](https://github.com/gfx-rs/wgpu/pull/8536).\n\n### Documentation\n\n#### General\n\n- Expanded documentation of `QuerySet`, `QueryType`, and `resolve_query_set()` describing how to use queries. By @kpreid in [#8776](https://github.com/gfx-rs/wgpu/pull/8776).\n\n## v28.0.1 (2025-03-01)\n\n### General\n\n- Fixed crash on nvidia cards when presenting from another thread. By @inner-daemons in [#9036](https://github.com/gfx-rs/wgpu/pull/9036).\n\n### Vulkan\n\n- Fixed crash on some Mali drivers on Android. By @beicause in [#8769](https://github.com/gfx-rs/wgpu/pull/8769).\n\n### Metal\n\n- Re-added support for TRANSIENT textures on Apple A7 chips. By @Opstic in [#8725](https://github.com/gfx-rs/wgpu/pull/8725).\n\n## v28.0.0 (2025-12-17)\n\n### Major Changes\n\n#### Mesh Shaders\n\nThis has been a long time coming. See [the tracking issue](https://github.com/gfx-rs/wgpu/issues/7197) for more information.\nThey are now fully supported on Vulkan, and supported on Metal and DX12 with passthrough shaders. WGSL parsing and rewriting\nis supported, meaning they can be used through WESL or naga_oil.\n\nMesh shader pipelines replace the standard vertex shader pipelines and allow new ways to render meshes.\nThey are ideal for meshlet rendering, a form of rendering where small groups of triangles are handled together,\nfor both culling and rendering.\n\nThey are compute-like shaders, and generate primitives which are passed directly to the rasterizer, rather\nthan having a list of vertices generated individually and then using a static index buffer. This means that certain computations\non nearby groups of triangles can be done together, the relationship between vertices and primitives is more programmable, and\nyou can even pass non-interpolated per-primitive data to the fragment shader, independent of vertices.\n\nMesh shaders are very versatile, and are powerful enough to replace vertex shaders, tesselation shaders, and geometry shaders\non their own or with task shaders.\n\nA full example of mesh shaders in use can be seen in the `mesh_shader` example. For the full specification of mesh shaders in wgpu, go to [docs/api-specs/mesh_shading.md](docs/api-specs/mesh_shading.md). Below is a small snippet of shader code demonstrating their usage:\n\n```wgsl\n@task\n@payload(taskPayload)\n@workgroup_size(1)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    // Task shaders can use workgroup variables like compute shaders\n    workgroupData = 1.0;\n    // Pass some data to all mesh shaders dispatched by this workgroup\n    taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n    taskPayload.visible = 1;\n    // Dispatch a mesh shader grid with one workgroup\n    return vec3(1, 1, 1);\n}\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(1)\nfn ms_main(@builtin(local_invocation_index) index: u32, @builtin(global_invocation_id) id: vec3<u32>) {\n    // Set how many outputs this workgroup will generate\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    // Can also use workgroup variables\n    workgroupData = 2.0;\n\n    // Set vertex outputs\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0] * taskPayload.colorMask;\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1] * taskPayload.colorMask;\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2] * taskPayload.colorMask;\n\n    // Set the vertex indices for the only primitive\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    // Cull it if the data passed by the task shader says to\n    mesh_output.primitives[0].cull = taskPayload.visible == 1;\n    // Give a noninterpolated per-primitive vec4 to the fragment shader\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n```\n\n##### Thanks\n\nThis was a monumental effort from many different people, but it was championed by @inner-daemons, without whom it would not have happened.\nThank you @cwfitzgerald for doing the bulk of the code review. Finally thank you @ColinTimBarndt for coordinating the testing effort.\n\nReviewers:\n\n- @cwfitzgerald\n- @jimblandy\n- @ErichDonGubler\n\n`wgpu` Contributions:\n\n- Metal implementation in wgpu-hal. By @inner-daemons in [#8139](https://github.com/gfx-rs/wgpu/pull/8139).\n- DX12 implementation in wgpu-hal. By @inner-daemons in [#8110](https://github.com/gfx-rs/wgpu/pull/8110).\n- Vulkan implementation in wgpu-hal. By @inner-daemons in [#7089](https://github.com/gfx-rs/wgpu/pull/7089).\n- wgpu/wgpu-core implementation. By @inner-daemons in [#7345](https://github.com/gfx-rs/wgpu/pull/7345).\n- New mesh shader limits and validation. By @inner-daemons in [#8507](https://github.com/gfx-rs/wgpu/pull/8507).\n\n`naga` Contributions:\n\n- Naga IR implementation. By @inner-daemons in [#8104](https://github.com/gfx-rs/wgpu/pull/8104).\n- `wgsl-in` implementation in naga. By @inner-daemons in [#8370](https://github.com/gfx-rs/wgpu/pull/8370).\n- `spv-out` implementation in naga. By @inner-daemons in [#8456](https://github.com/gfx-rs/wgpu/pull/8456).\n- `wgsl-out` implementation in naga. By @Slightlyclueless in [#8481](https://github.com/gfx-rs/wgpu/pull/8481).\n- Allow barriers in mesh/task shaders. By @inner-daemons in [#8749](https://github.com/gfx-rs/wgpu/pull/8749)\n\nTesting Assistance:\n\n- @ColinTimBarndt\n- @AdamK2003\n- @Mhowser\n- @9291Sam\n- 3 more testers who wished to remain anonymous.\n\nThank you to everyone to made this happen!\n\n#### Switch from `gpu-alloc` to `gpu-allocator` in the `vulkan` backend\n\n`gpu-allocator` is the allocator used in the `dx12` backend, allowing to configure\nthe allocator the same way in those two backends converging their behavior.\n\nThis also brings the `Device::generate_allocator_report` feature to\nthe vulkan backend.\n\nBy @DeltaEvo in [#8158](https://github.com/gfx-rs/wgpu/pull/8158).\n\n#### `wgpu::Instance::enumerate_adapters` is now `async` & available on WebGPU\n\nBREAKING CHANGE: `enumerate_adapters` is now `async`:\n\n```diff\n- pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {\n+ pub fn enumerate_adapters(&self, backends: Backends) -> impl Future<Output = Vec<Adapter>> {\n```\n\nThis yields two benefits:\n\n- This method is now implemented on non-native using the standard `Adapter::request_adapter(…)`, making `enumerate_adapters` a portable surface. This was previously a nontrivial pain point when an application wanted to do some of its own filtering of adapters.\n- This method can now be implemented in custom backends.\n\nBy @R-Cramer4 in [#8230](https://github.com/gfx-rs/wgpu/pull/8230)\n\n#### New `LoadOp::DontCare`\n\nIn the case where a renderpass unconditionally writes to all pixels in the rendertarget,\n`Load` can cause unnecessary memory traffic, and `Clear` can spend time unnecessarily\nclearing the rendertargets. `DontCare` is a new `LoadOp` which will leave the contents\nof the rendertarget undefined. Because this could lead to undefined behavior, this API\nrequires that the user gives an unsafe token to use the api.\n\nWhile you can use this unconditionally, on platforms where `DontCare` is not available,\nit will internally use a different load op.\n\n```rust\nload: LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() })\n```\n\nBy @cwfitzgerald in [#8549](https://github.com/gfx-rs/wgpu/pull/8549)\n\n#### `MipmapFilterMode` is split from `FilterMode`\n\nThis is a breaking change that aligns wgpu with spec.\n\n```diff\nSamplerDescriptor {\n...\n-     mipmap_filter: FilterMode::Nearest\n+     mipmap_filter: MipmapFilterMode::Nearest\n...\n}\n```\n\nBy @sagudev in [#8314](https://github.com/gfx-rs/wgpu/pull/8314).\n\n#### Multiview on all major platforms and support for multiview bitmasks\n\nMultiview is a feature that allows rendering the same content to multiple layers of a texture.\nThis is useful primarily in VR where you wish to display almost identical content to 2 views,\njust with a different perspective. Instead of using 2 draw calls or 2 instances for each object, you\ncan use this feature.\n\nMultiview is also called view instancing in DX12 or vertex amplification in Metal.\n\nMultiview has been reworked, adding support for Metal and DX12, and adding testing and validation to wgpu itself.\nThis change also introduces a view bitmask, a new field in `RenderPassDescriptor` that allows a render pass to render\nto multiple non-adjacent layers when using the `SELECTIVE_MULTIVIEW` feature. If you don't use multi-view,\nyou can set this field to none.\n\n```diff\n- wgpu::RenderPassDescriptor {\n-     label: None,\n-     color_attachments: &color_attachments,\n-     depth_stencil_attachment: None,\n-     timestamp_writes: None,\n-     occlusion_query_set: None,\n- }\n+ wgpu::RenderPassDescriptor {\n+     label: None,\n+     color_attachments: &color_attachments,\n+     depth_stencil_attachment: None,\n+     timestamp_writes: None,\n+     occlusion_query_set: None,\n+     multiview_mask: NonZero::new(3),\n+ }\n```\n\nOne other breaking change worth noting is that in WGSL `@builtin(view_index)` now requires a type of `u32`, where previously it required `i32`.\n\nBy @inner-daemons in [#8206](https://github.com/gfx-rs/wgpu/pull/8206).\n\n#### Error scopes now use guards and are thread-local.\n\n```diff\n- device.push_error_scope(wgpu::ErrorFilter::Validation);\n+ let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n  // ... perform operations on the device ...\n- let error: Option<Error> = device.pop_error_scope().await;\n+ let error: Option<Error> = scope.pop().await;\n```\n\nDevice error scopes now operate on a per-thread basis. This allows them to be used easily within multithreaded contexts,\nwithout having the error scope capture errors from other threads.\n\nWhen the `std` feature is **not** enabled, we have no way to differentiate between threads, so error scopes return to be\nglobal operations.\n\nBy @cwfitzgerald in [#8685](https://github.com/gfx-rs/wgpu/pull/8685)\n\n#### Log Levels\n\nWe have received complaints about wgpu being way too log spammy at log levels `info`/`warn`/`error`. We have\nadjusted our log policy and changed logging such that `info` and above should be silent unless some exceptional\nevent happens. Our new log policy is as follows:\n\n- Error: if we can’t (for some reason, usually a bug) communicate an error any other way.\n- Warning: similar, but there may be one-shot warnings about almost certainly sub-optimal.\n- Info: do not use\n- Debug: Used for interesting events happening inside wgpu.\n- Trace: Used for all events that might be useful to either `wgpu` or application developers.\n\nBy @cwfitzgerald in [#8579](https://github.com/gfx-rs/wgpu/pull/8579).\n\n#### Push constants renamed immediates, API brought in line with spec.\n\nAs the \"immediate data\" api is getting close to stabilization in the WebGPU specification,\nwe're bringing our implementation in line with what the spec dictates.\n\nFirst, in the `PipelineLayoutDescriptor`, you now pass a unified size for all stages:\n\n```diff\n- push_constant_ranges: &[wgpu::PushConstantRange {\n-     stages: wgpu::ShaderStages::VERTEX_FRAGMENT,\n-     range: 0..12,\n- }]\n+ immediate_size: 12,\n```\n\nSecond, on the command encoder you no longer specify a shader stage, uploads apply\nto all shader stages that use immediate data.\n\n```diff\n- rpass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytes);\n+ rpass.set_immediates(0, bytes);\n```\n\nThird, immediates are now declared with the `immediate` address space instead of\nthe `push_constant` address space. Due to a [known issue on DX12](https://github.com/gfx-rs/wgpu/issues/5683)\nit is advised to always use a structure for your immediates until that issue\nis fixed.\n\n```diff\n- var<push_constant> my_pc: MyPushConstant;\n+ var<immediate> my_imm: MyImmediate;\n```\n\nFinally, our implementation currently still zero-initializes the immediate data\nrange you declared in the pipeline layout. This is not spec compliant and failing\nto populate immediate \"slots\" that are used in the shader will be a validation error\nin a future version. See [the proposal][immediate-data-spec] for details for determining\nwhich slots are populated in a given shader.\n\nBy @cwfitzgerald in [#8724](https://github.com/gfx-rs/wgpu/pull/8724).\n\n[immediate-data-spec]: https://github.com/gpuweb/gpuweb/blob/main/proposals/immediate-data.md#immediate-slots\n\n#### `subgroup_{min,max}_size` renamed and moved from `Limits` -> `AdapterInfo`\n\nTo bring our code in line with the WebGPU spec, we have moved information about subgroup size\nfrom limits to adapter info. Limits was not the correct place for this anyway, and we had some\ncode special casing those limits.\n\nAdditionally we have renamed the fields to match the spec.\n\n```diff\n- let min = limits.min_subgroup_size;\n+ let min = info.subgroup_min_size;\n- let max = limits.max_subgroup_size;\n+ let max = info.subgroup_max_size;\n```\n\nBy @cwfitzgerald in [#8609](https://github.com/gfx-rs/wgpu/pull/8609).\n\n### New Features\n\n- Added support for transient textures on Vulkan and Metal. By @opstic in [#8247](https://github.com/gfx-rs/wgpu/pull/8247)\n- Implement shader triangle barycentric coordinate builtins. By @atlv24 in [#8320](https://github.com/gfx-rs/wgpu/pull/8320).\n- Added support for binding arrays of storage textures on Metal. By @msvbg in [#8464](https://github.com/gfx-rs/wgpu/pull/8464)\n- Added support for multisampled texture arrays on Vulkan through adapter feature `MULTISAMPLE_ARRAY`. By @LaylBongers in [#8571](https://github.com/gfx-rs/wgpu/pull/8571).\n- Added `get_configuration` to `wgpu::Surface`, that returns the current configuration of `wgpu::Surface`. By @sagudev in [#8664](https://github.com/gfx-rs/wgpu/pull/8664).\n- Add `wgpu_core::Global::create_bind_group_layout_error`. By @ErichDonGubler in [#8650](https://github.com/gfx-rs/wgpu/pull/8650).\n\n### Changes\n\n#### General\n\n- Require new enable extensions when using ray queries and position fetch (`wgpu_ray_query`, `wgpu_ray_query_vertex_return`). By @Vecvec in [#8545](https://github.com/gfx-rs/wgpu/pull/8545).\n- Texture now has `from_custom`. By @R-Cramer4 in [#8315](https://github.com/gfx-rs/wgpu/pull/8315).\n- Using both the wgpu command encoding APIs and `CommandEncoder::as_hal_mut` on the same encoder will now result in a panic.\n- Allow `include_spirv!` and `include_spirv_raw!` macros to be used in constants and statics. By @clarfonthey in [#8250](https://github.com/gfx-rs/wgpu/pull/8250).\n- Added support for rendering onto multi-planar textures. By @noituri in [#8307](https://github.com/gfx-rs/wgpu/pull/8307).\n- Validation errors from `CommandEncoder::finish()` will report the label of the invalid encoder. By @kpreid in [#8449](https://github.com/gfx-rs/wgpu/pull/8449).\n- Corrected documentation of the minimum alignment of the _end_ of a mapped range of a buffer (it is 4, not 8). By @kpreid in [#8450](https://github.com/gfx-rs/wgpu/pull/8450).\n- `util::StagingBelt` now takes a `Device` when it is created instead of when it is used. By @kpreid in [#8462](https://github.com/gfx-rs/wgpu/pull/8462).\n- `wgpu_hal::vulkan::Texture` API changes to handle externally-created textures and memory more flexibly. By @s-ol in [#8512](https://github.com/gfx-rs/wgpu/pull/8512), [#8521](https://github.com/gfx-rs/wgpu/pull/8521).\n- Render passes are now validated against the `maxColorAttachmentBytesPerSample` limit. By @andyleiserson in [#8697](https://github.com/gfx-rs/wgpu/pull/8697).\n\n#### Metal\n\n- Expose render layer. By @xiaopengli89 in [#8707](https://github.com/gfx-rs/wgpu/pull/8707)\n- `MTLDevice` is thread-safe. By @uael in [#8168](https://github.com/gfx-rs/wgpu/pull/8168)\n\n#### naga\n\n- Prevent UB with invalid ray query calls on spirv. By @Vecvec in [#8390](https://github.com/gfx-rs/wgpu/pull/8390).\n- Update the set of binding_array capabilities. In most cases, they are set automatically from `wgpu` features, and this change should not be user-visible. By @andyleiserson in [#8671](https://github.com/gfx-rs/wgpu/pull/8671).\n- Naga now accepts the `var<function>` syntax for declaring local variables. By @andyleiserson in [#8710](https://github.com/gfx-rs/wgpu/pull/8710).\n\n### Bug Fixes\n\n#### General\n\n- Fixed a bug where mapping sub-ranges of a buffer on web would fail with `OperationError: GPUBuffer.getMappedRange: GetMappedRange range extends beyond buffer's mapped range`. By @ryankaplan in [#8349](https://github.com/gfx-rs/wgpu/pull/8349)\n- Reject fragment shader output `location`s > `max_color_attachments` limit. By @ErichDonGubler in [#8316](https://github.com/gfx-rs/wgpu/pull/8316).\n- WebGPU device requests now support the required limits `maxColorAttachments` and `maxColorAttachmentBytesPerSample`. By @evilpie in [#8328](https://github.com/gfx-rs/wgpu/pull/8328)\n- Reject binding indices that exceed `wgpu_types::Limits::max_bindings_per_bind_group` when deriving a bind group layout for a pipeline. By @jimblandy in [#8325](https://github.com/gfx-rs/wgpu/pull/8325).\n- Removed three features from `wgpu-hal` which did nothing useful: `\"cargo-clippy\"`, `\"gpu-allocator\"`, and `\"rustc-hash\"`. By @kpreid in [#8357](https://github.com/gfx-rs/wgpu/pull/8357).\n- `wgpu_types::PollError` now always implements the `Error` trait. By @kpreid in [#8384](https://github.com/gfx-rs/wgpu/pull/8384).\n- The texture subresources used by the color attachments of a render pass are no longer allowed to overlap when accessed via different texture views. By @andyleiserson in [#8402](https://github.com/gfx-rs/wgpu/pull/8402).\n- The `STORAGE_READ_ONLY` texture usage is now permitted to coexist with other read-only usages. By @andyleiserson in [#8490](https://github.com/gfx-rs/wgpu/pull/8490).\n- Validate that buffers are unmapped in `write_buffer` calls. By @ErichDonGubler in [#8454](https://github.com/gfx-rs/wgpu/pull/8454).\n- Shorten critical section inside present such that the snatch write lock is no longer held during present, preventing other work happening on other threads. By @cwfitzgerald in [#8608](https://github.com/gfx-rs/wgpu/pull/8608).\n\n#### naga\n\n- The `||` and `&&` operators now \"short circuit\", i.e., do not evaluate the RHS if the result can be determined from just the LHS. By @andyleiserson in [#7339](https://github.com/gfx-rs/wgpu/pull/7339).\n- Fix a bug that resulted in the Metal error `program scope variable must reside in constant address space` in some cases. By @teoxoy in [#8311](https://github.com/gfx-rs/wgpu/pull/8311).\n- Handle `rayQueryTerminate` in spv-out instead of ignoring it. By @Vecvec in [#8581](https://github.com/gfx-rs/wgpu/pull/8581).\n\n#### DX12\n\n- 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).\n- Fix detection of Int64 Buffer/Texture atomic features. By @cwfitzgerald in [#8667](https://github.com/gfx-rs/wgpu/pull/8667).\n\n#### Vulkan\n\n- Fixed a validation error regarding atomic memory semantics. By @atlv24 in [#8391](https://github.com/gfx-rs/wgpu/pull/8391).\n\n#### Metal\n\n- Fixed a variety of feature detection related bugs. By @inner-daemons in [#8439](https://github.com/gfx-rs/wgpu/pull/8439).\n\n#### WebGPU\n\n- Fixed a bug where the texture aspect was not passed through when calling `copy_texture_to_buffer` in WebGPU, causing the copy to fail for depth/stencil textures. By @Tim-Evans-Seequent in [#8445](https://github.com/gfx-rs/wgpu/pull/8445).\n\n#### GLES\n\n- Fix race when downloading texture from compute shader pass. By @SpeedCrash100 in [#8527](https://github.com/gfx-rs/wgpu/pull/8527)\n- Fix double window class registration when dynamic libraries are used. By @Azorlogh in [#8548](https://github.com/gfx-rs/wgpu/pull/8548)\n- Fix context loss on device initialization on GL3.3-4.1 contexts. By @cwfitzgerald in [#8674](https://github.com/gfx-rs/wgpu/pull/8674).\n- `VertexFormat::Unorm10_10_10_2` can now be used on `gl` backends. By @mooori in [#8717](https://github.com/gfx-rs/wgpu/pull/8717).\n\n#### hal\n\n- `DropCallback`s are now called after dropping all other fields of their parent structs. By @jerzywilczek in [#8353](https://github.com/gfx-rs/wgpu/pull/8353)\n\n## v27.0.4 (2025-10-23)\n\nThis release includes `wgpu-hal` version `27.0.4`. All other crates remain at their previous versions.\n\n### Bug Fixes\n\n#### General\n\n- Remove fragile dependency constraint on `ordered-float` that prevented semver-compatible changes above `5.0.0`. By @kpreid in [#8371](https://github.com/gfx-rs/wgpu/pull/8371).\n\n#### Vulkan\n\n- 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).\n\n## v27.0.3 (2025-10-22)\n\nThis release includes `naga`, `wgpu-core` and `wgpu-hal` version `27.0.3`. All other crates remain at their previous versions.\n\n### Bug Fixes\n\n#### naga\n\n- 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.\n\n#### General\n\n- 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).\n\n#### DX12\n\n- 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).\n\n## v27.0.2 (2025-10-03)\n\n### Bug Fixes\n\n#### DX12\n\n- Fix device creation failures for devices that do not support mesh shaders. By @vorporeal in [#8297](https://github.com/gfx-rs/wgpu/pull/8297).\n\n## v27.0.1 (2025-10-02)\n\n### Bug Fixes\n\n- Fixed the build on docs.rs. By @cwfitzgerald in [#8292](https://github.com/gfx-rs/wgpu/pull/8292).\n\n## v27.0.0 (2025-10-01)\n\n### Major Changes\n\n#### Deferred command buffer actions: `map_buffer_on_submit` and `on_submitted_work_done`\n\nYou may schedule buffer mapping and a submission-complete callback to run automatically after you submit, directly from encoders, command buffers, and passes.\n\n```rust\n// Record some GPU work so the submission isn't empty and touches `buffer`.\nencoder.clear_buffer(&buffer, 0, None);\n\n// Defer mapping until this encoder is submitted.\nencoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..size, |result| { .. });\n\n// Fires after the command buffer's work is finished.\nencoder.on_submitted_work_done(|| { .. });\n\n// Automatically calls `map_async` and `on_submitted_work_done` after this submission finishes.\nqueue.submit([encoder.finish()]);\n```\n\nAvailable on `CommandEncoder`, `CommandBuffer`, `RenderPass`, and `ComputePass`.\n\nBy @cwfitzgerald in [#8125](https://github.com/gfx-rs/wgpu/pull/8125).\n\n#### Builtin Support for DXGI swapchains on top of of DirectComposition Visuals in DX12\n\nBy enabling DirectComposition support, the dx12 backend can now support transparent windows.\n\nThis 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`.\n\n```rust\nlet instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {\n    backend_options: wgpu::BackendOptions {\n        dx12: wgpu::Dx12BackendOptions {\n            presentation_system: wgpu::Dx12SwapchainKind::DxgiFromVisual,\n            ..\n        },\n        ..\n    },\n    ..\n});\n```\n\nBy @n1ght-hunter in [#7550](https://github.com/gfx-rs/wgpu/pull/7550).\n\n#### `EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE` has been merged into `EXPERIMENTAL_RAY_QUERY`\n\nWe 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.\n\n```diff\n- Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE\n+ Features::EXPERIMENTAL_RAY_QUERY\n```\n\nBy @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).\n\n#### New `EXPERIMENTAL_PRECOMPILED_SHADERS` API\n\nWe 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)\n\nDifference for SPIR-V passthrough:\n\n```diff\n- device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough::SpirV(\n-     wgpu::ShaderModuleDescriptorSpirV {\n-         label: None,\n-         source: spirv_code,\n-     },\n- ))\n+ device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n+     entry_point: \"main\".into(),\n+     label: None,\n+     spirv: Some(spirv_code),\n+     ..Default::default()\n})\n```\n\nThis 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.\n\n#### Buffer mapping apis no longer have lifetimes\n\n`Buffer::get_mapped_range()`, `Buffer::get_mapped_range_mut()`, and `Queue::write_buffer_with()` now return guard objects without any lifetimes. This\nmakes it significantly easier to store these types in structs, which is useful for building utilities that build the contents of a buffer over time.\n\n```diff\n- let buffer_mapping_ref: wgpu::BufferView<'_>           = buffer.get_mapped_range(..);\n- let buffer_mapping_mut: wgpu::BufferViewMut<'_>        = buffer.get_mapped_range_mut(..);\n- let queue_write_with:   wgpu::QueueWriteBufferView<'_> = queue.write_buffer_with(..);\n+ let buffer_mapping_ref: wgpu::BufferView               = buffer.get_mapped_range(..);\n+ let buffer_mapping_mut: wgpu::BufferViewMut            = buffer.get_mapped_range_mut(..);\n+ let queue_write_with:   wgpu::QueueWriteBufferView     = queue.write_buffer_with(..);\n```\n\nBy @sagudev in [#8046](https://github.com/gfx-rs/wgpu/pull/8046) and @cwfitzgerald in [#8070](https://github.com/gfx-rs/wgpu/pull/8161).\n\n#### `EXPERIMENTAL_*` features now require unsafe code to enable\n\nWe want to be able to expose potentially experimental features to our users before we have ensured that they are fully sound to use.\nAs such, we now require any feature that is prefixed with `EXPERIMENTAL` to have a special unsafe token enabled in the device descriptor\nacknowledging that the features may still have bugs in them and to report any they find.\n\n```rust\nadapter.request_device(&wgpu::DeviceDescriptor {\n    features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,\n    experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }\n    ..\n})\n```\n\nBy @cwfitzgerald in [#8163](https://github.com/gfx-rs/wgpu/pull/8163).\n\n#### Multi-draw indirect is now unconditionally supported when indirect draws are supported\n\nWe have removed `Features::MULTI_DRAW_INDIRECT` as it was unconditionally available on all platforms.\n`RenderPass::multi_draw_indirect` is now available if the device supports downlevel flag `DownlevelFlags::INDIRECT_EXECUTION`.\n\nIf you are using spirv-passthrough with multi-draw indirect and `gl_DrawID`, you can know if `MULTI_DRAW_INDIRECT` is being emulated\nby if the `Feature::MULTI_DRAW_INDIRECT_COUNT` feature is available on the device, this feature cannot be emulated efficicently.\n\nBy @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162).\n\n#### `wgpu::PollType::Wait` has now an optional timeout\n\nWe removed `wgpu::PollType::WaitForSubmissionIndex` and added fields to `wgpu::PollType::Wait` in order to express timeouts.\n\nBefore/after for `wgpu::PollType::Wait`:\n\n```diff\n-device.poll(wgpu::PollType::Wait).unwrap();\n-device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n+device.poll(wgpu::PollType::Wait {\n+      submission_index: None, // Wait for most recent submission\n+      timeout: Some(std::time::Duration::from_secs(60)), // Previous behavior, but more likely you want `None` instead.\n+  })\n+  .unwrap();\n```\n\nBefore/after for `wgpu::PollType::WaitForSubmissionIndex`:\n\n```diff\n-device.poll(wgpu::PollType::WaitForSubmissionIndex(index_to_wait_on))\n+device.poll(wgpu::PollType::Wait {\n+      submission_index: Some(index_to_wait_on),\n+      timeout: Some(std::time::Duration::from_secs(60)), // Previous behavior, but more likely you want `None` instead.\n+  })\n+  .unwrap();\n```\n\n⚠️ Previously, both `wgpu::PollType::WaitForSubmissionIndex` and `wgpu::PollType::Wait` had a hard-coded timeout of 60 seconds.\n\nTo wait indefinitely on the latest submission, you can also use the `wait_indefinitely` convenience function:\n\n```rust\ndevice.poll(wgpu::PollType::wait_indefinitely());\n```\n\nBy @wumpf in [#8282](https://github.com/gfx-rs/wgpu/pull/8282), [#8285](https://github.com/gfx-rs/wgpu/pull/8285)\n\n### New Features\n\n#### General\n\n- Added mesh shader support to `wgpu`, with examples. Requires passthrough. By @SupaMaggie70Incorporated in [#7345](https://github.com/gfx-rs/wgpu/pull/7345).\n- 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).\n- `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)\n\n#### naga\n\n- Expose `naga::front::wgsl::UnimplementedEnableExtension`. By @ErichDonGubler in [#8237](https://github.com/gfx-rs/wgpu/pull/8237).\n\n### Changes\n\n#### General\n\n- 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).\n- 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).\n- 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).\n- `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).\n- 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.\n  - Copies within the same texture must not overlap.\n  - Copies of multisampled or depth/stencil formats must span an entire subresource (layer).\n  - Copies of depth/stencil formats must be 4B aligned.\n  - For texture-buffer copies, `bytes_per_row` on the buffer side must be 256B-aligned, even if the transfer is a single row.\n- 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).\n- 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).\n- 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`.\n  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).\n  By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011).\n- 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).\n- 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).\n- Improve errors when buffer mapping is done incorrectly. Allow aliasing immutable [`BufferViews`]. By @cwfitzgerald in [#8150](https://github.com/gfx-rs/wgpu/pull/8150).\n- 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).\n- 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).\n- 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).\n\n#### naga\n\n- 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).\n- 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).\n- 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).\n- 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).\n- **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).\n- 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).\n- 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).\n- 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).\n- naga implement OpImageGather and OpImageDrefGather operations when ingesting SPIR-V. By @hasenbanck in [8280](https://github.com/gfx-rs/wgpu/pull/8280).\n\n#### DX12\n\n- Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400)\n- Add mesh shader support, including to the example. By @SupaMaggie70Incorporated in [#8110](https://github.com/gfx-rs/wgpu/issues/8110)\n\n### Bug Fixes\n\n#### General\n\n- 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).\n\n#### DX12\n\n- 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).\n- 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).\n\n##### EGL\n\n- Fixed unwrap failed in context creation for some Android devices. By @uael in [#8024](https://github.com/gfx-rs/wgpu/pull/8024).\n\n##### Vulkan\n\n- 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).\n\n#### naga\n\n- [wgsl-in] Allow a trailing comma in `@blend_src(…)` attributes. By @ErichDonGubler in [#8137](https://github.com/gfx-rs/wgpu/pull/8137).\n- [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).\n- Escape, rather than strip, identifiers with Unicode. By @ErichDonGubler in [7995](https://github.com/gfx-rs/wgpu/pull/7995).\n\n### Documentation\n\n#### General\n\n- 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).\n\n# v26.0.6 (2025-10-23)\n\nThis release includes `wgpu-hal` version `26.0.6`. All other crates remain at their previous versions.\n\n### Bug Fixes\n\n#### Vulkan\n\n- 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).\n\n## v26.0.5 (2025-10-21)\n\nThis release includes `wgpu-hal` version `26.0.5`. All other crates remain at their previous versions.\n\n### Bug Fixes\n\n#### DX12\n\n- 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 [#8375](https://github.com/gfx-rs/wgpu/pull/8375).\n\n## v26.0.4 (2025-08-07)\n\n### Bug Fixes\n\n#### Vulkan\n\n- 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).\n\n## v26.0.3 (2025-07-30)\n\n### Bug Fixes\n\n- Fixed memory leak in vulkan backend. By @cwfitzgerald in [#8031](https://github.com/gfx-rs/wgpu/pull/8031).\n\n### Bug Fixes\n\n#### naga\n\n- Fix empty `if` statements causing errors on spirv 1.6+. By @Vecvec in [#7883](https://github.com/gfx-rs/wgpu/pull/7883).\n\n## v26.0.2 (2025-07-23)\n\n### Bug Fixes\n\n- Fixed vulkan validation error regarding the swapchain in latest SDK. By @cwfitzgerald in [#7971](https://github.com/gfx-rs/wgpu/pull/7971).\n- 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).\n\n> [!WARNING]\n> There is formally a breaking change in `wgpu_hal::vulkan::Device::texture_from_raw` as there is now a `&self` receiver where\n> there previously wasn't one. This will not affect you unless you explicitly use this api. We have gone ahead with the release\n> as the bug was pervasive and made wgpu unusable for the affected people on v26.\n\n## v26.0.1 (2025-07-10)\n\n### Bug Fixes\n\n- 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).\n- 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).\n- 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).\n\n## v26.0.0 (2025-07-09)\n\n### Major Features\n\n#### New method `TextureView::texture`\n\nYou can now call `texture_view.texture()` to get access to the texture that\na given texture view points to.\n\nBy @cwfitzgerald and @Wumpf in [#7907](https://github.com/gfx-rs/wgpu/pull/7907).\n\n#### `as_hal` calls now return guards instead of using callbacks.\n\nPreviously, if you wanted to get access to the wgpu-hal or underlying api types, you would call `as_hal` and get the hal type as a callback. Now the function returns a guard which dereferences to the hal type.\n\n```diff\n- device.as_hal::<hal::api::Vulkan>(|hal_device| {...});\n+ let hal_device: impl Deref<Item = hal::vulkan::Device> = device.as_hal::<hal::api::Vulkan>();\n```\n\nBy @cwfitzgerald in [#7863](https://github.com/gfx-rs/wgpu/pull/7863).\n\n#### Enabling Vulkan Features/Extensions\n\nFor those who are doing vulkan/wgpu interop or passthrough and need to enable features/extensions that wgpu does not expose, there is a new `wgpu_hal::vulkan::Adapter::open_with_callback` that allows the user to modify the pnext chains and extension lists populated by wgpu before we create a vulkan device. This should vastly simplify the experience, as previously you needed to create a device yourself.\n\nUnderlying api interop is a quickly evolving space, so we welcome all feedback!\n\n```rust\ntype VkApi = wgpu::hal::api::Vulkan;\nlet adapter: wgpu::Adapter = ...;\n\nlet mut buffer_device_address_create_info = ash::vk::PhysicalDeviceBufferDeviceAddressFeatures { .. };\nlet hal_device: wgpu::hal::OpenDevice<VkApi> = adapter\n    .as_hal::<VkApi>()\n    .unwrap()\n    .open_with_callback(\n        wgpu::Features::empty(),\n        &wgpu::MemoryHints::Performance,\n        Some(Box::new(|args| {\n            // Add the buffer device address extension.\n            args.extensions.push(ash::khr::buffer_device_address::NAME);\n            // Extend the create info with the buffer device address create info.\n            *args.create_info = args\n                .create_info\n                .push_next(&mut buffer_device_address_create_info);\n            // We also have access to the queue create infos if we need them.\n            let _ = args.queue_create_infos;\n        })),\n    )\n    .unwrap();\n\nlet (device, queue) = adapter\n    .create_device_from_hal(hal_device, &wgpu::DeviceDescriptor { .. })\n    .unwrap();\n```\n\nBy @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829).\n\n### naga\n\n- Added `no_std` support with default features disabled. By @Bushrat011899 in [#7585](https://github.com/gfx-rs/wgpu/pull/7585).\n- [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).\n- When emitting GLSL, Uniform and Storage Buffer memory layouts are now emitted even if no explicit binding is given. By @cloone8 in [#7579](https://github.com/gfx-rs/wgpu/pull/7579).\n- Diagnostic rendering methods (i.e., `naga::{front::wgsl::ParseError,WithSpan}::emit_error_to_string_with_path`) now accept more types for their `path` argument via a new sealed `AsDiagnosticFilePath` trait. By @atlv24, @bushrat011899, and @ErichDonGubler in [#7643](https://github.com/gfx-rs/wgpu/pull/7643).\n- Add support for [quad operations](https://www.w3.org/TR/WGSL/#quad-builtin-functions) (requires `SUBGROUP` feature to be enabled). By @dzamkov and @valaphee in [#7683](https://github.com/gfx-rs/wgpu/pull/7683).\n- Add support for `atomicCompareExchangeWeak` in HLSL and GLSL backends. By @cryvosh in [#7658](https://github.com/gfx-rs/wgpu/pull/7658)\n\n### General\n\n- Add support for astc-sliced-3d feature. By @mehmetoguzderin in [#7577](https://github.com/gfx-rs/wgpu/issues/7577)\n- Added `wgpu_hal::dx12::Adapter::as_raw()`. By @tronical in [##7852](https://github.com/gfx-rs/wgpu/pull/7852)\n- Add support for rendering to slices of 3D texture views and single layered 2D-Array texture views (this requires `VK_KHR_maintenance1` which should be widely available on newer drivers). By @teoxoy in [#7596](https://github.com/gfx-rs/wgpu/pull/7596)\n- Add extra acceleration structure vertex formats. By @Vecvec in [#7580](https://github.com/gfx-rs/wgpu/pull/7580).\n- Add acceleration structure limits. By @Vecvec in [#7845](https://github.com/gfx-rs/wgpu/pull/7845).\n- Add support for clip-distances feature for Vulkan and GL backends. By @dzamkov in [#7730](https://github.com/gfx-rs/wgpu/pull/7730)\n- 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).\n\n[`GPUError`]: https://www.w3.org/TR/webgpu/#gpuerror\n\n### Bug Fixes\n\n#### General\n\n- Fix error message for sampler array limit. By @LPGhatguy in [#7704](https://github.com/gfx-rs/wgpu/pull/7704).\n- 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()`.\n\n#### naga\n\n- 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).\n- 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).\n- 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).\n- Allow textureLoad's sample index arg to be unsigned. By @jimblandy in [#7625](https://github.com/gfx-rs/wgpu/pull/7625).\n- Properly convert arguments to atomic operations. By @jimblandy in [#7573](https://github.com/gfx-rs/wgpu/pull/7573).\n- Apply necessary automatic conversions to the `value` argument of `textureStore`. By @jimblandy in [#7567](https://github.com/gfx-rs/wgpu/pull/7567).\n- Properly apply WGSL's automatic conversions to the arguments to texture sampling functions. By @jimblandy in [#7548](https://github.com/gfx-rs/wgpu/pull/7548).\n- Properly evaluate `abs(most negative abstract int)`. By @jimblandy in [#7507](https://github.com/gfx-rs/wgpu/pull/7507).\n- Generate vectorized code for `[un]pack4x{I,U}8[Clamp]` on SPIR-V and MSL 2.1+. By @robamler in [#7664](https://github.com/gfx-rs/wgpu/pull/7664).\n- Fix typing for `select`, which had issues particularly with a lack of automatic type conversion. By @ErichDonGubler in [#7572](https://github.com/gfx-rs/wgpu/pull/7572).\n- Allow scalars as the first argument of the `distance` built-in function. By @bernhl in [#7530](https://github.com/gfx-rs/wgpu/pull/7530).\n- Don't panic when handling `f16` for pipeline constants, i.e., `override`s in WGSL. By @ErichDonGubler in [#7801](https://github.com/gfx-rs/wgpu/pull/7801).\n- Prevent aliased ray queries crashing naga when writing SPIR-V out. By @Vecvec in [#7759](https://github.com/gfx-rs/wgpu/pull/7759).\n\n#### DX12\n\n- Get `vertex_index` & `instance_index` builtins working for indirect draws. By @teoxoy in [#7535](https://github.com/gfx-rs/wgpu/pull/7535)\n\n#### Vulkan\n\n- Fix OpenBSD compilation of `wgpu_hal::vulkan::drm`. By @ErichDonGubler in [#7810](https://github.com/gfx-rs/wgpu/pull/7810).\n- Fix warnings for unrecognized present mode. By @Wumpf in [#7850](https://github.com/gfx-rs/wgpu/pull/7850).\n\n#### Metal\n\n- Remove extraneous main thread warning in `fn surface_capabilities()`. By @jamesordner in [#7692](https://github.com/gfx-rs/wgpu/pull/7692)\n\n#### WebGPU\n\n- Fix setting unclipped_depth. By @atlv24 in [#7841](https://github.com/gfx-rs/wgpu/pull/7841)\n- Implement `on_submitted_work_done` for WebGPU backend. By @drewcrawford in [#7864](https://github.com/gfx-rs/wgpu/pull/7864)\n\n### Changes\n\n- Loosen Viewport validation requirements to match the [new specs](https://github.com/gpuweb/gpuweb/pull/5025). By @ebbdrop in [#7564](https://github.com/gfx-rs/wgpu/pull/7564)\n- `wgpu` and `deno_webgpu` now use `wgpu-types::error::WebGpuError` to classify errors. Any changes here are likely to be regressions; please report them if you find them! By @ErichDonGubler in [#6547](https://github.com/gfx-rs/wgpu/pull/6547).\n\n#### General\n\n- Support BLAS compaction in wgpu. By @Vecvec in [#7285](https://github.com/gfx-rs/wgpu/pull/7285).\n- Removed `MaintainBase` in favor of using `PollType`. By @waywardmonkeys in [#7508](https://github.com/gfx-rs/wgpu/pull/7508).\n- The `destroy` functions for buffers and textures in wgpu-core are now infallible. Previously, they returned an error if called multiple times for the same object. This only affects the wgpu-core API; the wgpu API already allowed multiple `destroy` calls. By @andyleiserson in [#7686](https://github.com/gfx-rs/wgpu/pull/7686) and [#7720](https://github.com/gfx-rs/wgpu/pull/7720).\n- Remove `CommandEncoder::build_acceleration_structures_unsafe_tlas` in favour of `as_hal` and apply\n  simplifications allowed by this. By @Vecvec in [#7513](https://github.com/gfx-rs/wgpu/pull/7513)\n- The type of the `size` parameter to `copy_buffer_to_buffer` has changed from `BufferAddress` to `impl Into<Option<BufferAddress>>`. This achieves the spec-defined behavior of the value being optional, while still accepting existing calls without changes. By @andyleiserson in [#7659](https://github.com/gfx-rs/wgpu/pull/7659).\n- To bring wgpu's error reporting into compliance with the WebGPU specification, the error type returned from some functions has changed, and some errors may be raised at a different time than they were previously.\n  - The error type returned by many methods on `CommandEncoder`, `RenderPassEncoder`, `ComputePassEncoder`, and `RenderBundleEncoder` has changed to `EncoderStateError` or `PassStateError`. These functions will return the `Ended` variant of these errors if called on an encoder that is no longer active. Reporting of all other errors is deferred until a call to `finish()`.\n  - Variants holding a `CommandEncoderError` in the error enums `ClearError`, `ComputePassErrorInner`, `QueryError`, and `RenderPassErrorInner` have been replaced with variants holding an `EncoderStateError`.\n  - 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.\n  - `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`).\n\n#### naga\n\n- 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)\n- `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).\n- Implemented `early_depth_test` for SPIR-V backend, enabling `SHADER_EARLY_DEPTH_TEST` for Vulkan. Additionally, fixed conservative depth optimizations when using `early_depth_test`. The syntax for forcing early depth tests is now `@early_depth_test(force)` instead of `@early_depth_test`. By @dzamkov in [#7676](https://github.com/gfx-rs/wgpu/pull/7676).\n- `ImplementedLanguageExtension::VARIANTS` is now implemented manually rather than derived using `strum` (allowing `strum` to become a dev-only dependency) so it is no longer a member of the `strum::VARIANTS` trait. Unless you are using this trait as a bound this should have no effect.\n- Compaction changes, by @andyleiserson in [#7703](https://github.com/gfx-rs/wgpu/pull/7703):\n  - [`process_overrides`](https://docs.rs/naga/latest/naga/back/pipeline_constants/fn.process_overrides.html) now compacts the module to remove unused items. It is no longer necessary to supply values for overrides that are not used by the active entry point.\n  - The `compact` Cargo feature has been removed. It is no longer possible to exclude compaction support from the build.\n  - [`compact`](https://docs.rs/naga/latest/naga/compact/fn.compact.html) now has an additional argument that specifies whether to remove unused functions, globals, and named types and overrides. For the previous behavior, pass `KeepUnused::Yes`.\n\n#### D3D12\n\n- Remove the need for dxil.dll. By @teoxoy in [#7566](https://github.com/gfx-rs/wgpu/pull/7566)\n- Ability to get the raw `IDXGIFactory4` from `Instance`. By @MendyBerger in [#7827](https://github.com/gfx-rs/wgpu/pull/7827)\n\n#### Vulkan\n\n- Use highest SPIR-V version supported by Vulkan API version. By @robamler in [#7595](https://github.com/gfx-rs/wgpu/pull/7595)\n\n#### HAL\n\n- Added initial `no_std` support to `wgpu-hal`. By @bushrat011899 in [#7599](https://github.com/gfx-rs/wgpu/pull/7599)\n\n### Documentation\n\n#### General\n\n- Remove outdated information about `Adapter::request_device`. By @tesselode in [#7768](https://github.com/gfx-rs/wgpu/pull/7768)\n\n## v25.0.2 (2025-05-24)\n\n### Bug Fixes\n\n#### General\n\n- Fix a possible deadlock within `Queue::write_buffer`. By @RedMindZ in [#7582](https://github.com/gfx-rs/wgpu/pull/7582)\n- Fix `raw-window-handle` dependency being too lenient. By @kpreid in [#7526](https://github.com/gfx-rs/wgpu/pull/7526)\n\n#### WebGPU\n\n- Insert fragment pipeline constants into fragment descriptor instead of vertex descriptor. By @DerSchmale in [#7621](https://github.com/gfx-rs/wgpu/pull/7621)\n\n## v25.0.1 (2025-04-11)\n\n### Bug Fixes\n\n- Fix typos in various documentation. By @waywardmonkeys in [#7510](https://github.com/gfx-rs/wgpu/pull/7510).\n- Fix compile error when building with `profiling/profile-with-*` feature enabled. By @waywardmonkeys in [#7509](https://github.com/gfx-rs/wgpu/pull/7509).\n- Use `once_cell::race::OnceBox` instead of `std::sync::LazyLock` to allow `naga::proc::Namer::default()` to be available without backend features being enabled. By @cwfitzgerald in [#7517](https://github.com/gfx-rs/wgpu/pull/7517).\n\n#### DX12\n\n- Fix validation error when creating a non-mappable buffer using the committed allocation scheme. By @cwfitzgerald and @ErichDonGubler in [#7519](https://github.com/gfx-rs/wgpu/pull/7519).\n\n## v25.0.0 (2025-04-10)\n\n### Major Features\n\n#### Hashmaps Removed from APIs\n\nBoth `PipelineCompilationOptions::constants` and `ShaderSource::Glsl::defines` now take\nslices of key-value pairs instead of `hashmap`s. This is to prepare for `no_std`\nsupport and allow us to keep which `hashmap` hasher and such as implementation details. It\nalso allows more easily creating these structures inline.\n\nBy @cwfitzgerald in [#7133](https://github.com/gfx-rs/wgpu/pull/7133)\n\n#### All Backends Now Have Features\n\nPreviously, the `vulkan` and `gles` backends were non-optional on windows, linux, and android and there was no way to disable them. We have now figured out how to properly make them disablable! Additionally, if you turn on the `webgl` feature, you will only get the GLES backend on WebAssembly, it won't leak into native builds, like previously it might have.\n\n> [!WARNING]\n> If you use wgpu with `default-features = false` and you want to retain the `vulkan` and `gles` backends, you will need to add them to your feature list.\n>\n> ```diff\n> -wgpu = { version = \"24\", default-features = false, features = [\"metal\", \"wgsl\", \"webgl\"] }\n> +wgpu = { version = \"25\", default-features = false, features = [\"metal\", \"wgsl\", \"webgl\", \"vulkan\", \"gles\"] }\n> ```\n\nBy @cwfitzgerald in [#7076](https://github.com/gfx-rs/wgpu/pull/7076).\n\n#### `device.poll` Api Reworked\n\nThis release reworked the poll api significantly to allow polling to return errors when polling hits internal timeout limits.\n\n`Maintain` was renamed `PollType`. Additionally, `poll` now returns a result containing information about what happened during the poll.\n\n```diff\n-pub fn wgpu::Device::poll(&self, maintain: wgpu::Maintain) -> wgpu::MaintainResult\n+pub fn wgpu::Device::poll(&self, poll_type: wgpu::PollType) -> Result<wgpu::PollStatus, wgpu::PollError>\n\n-device.poll(wgpu::Maintain::Poll);\n+device.poll(wgpu::PollType::Poll).unwrap();\n```\n\n```rust\npub enum PollType<T> {\n    /// On wgpu-core based backends, block until the given submission has\n    /// completed execution, and any callbacks have been invoked.\n    ///\n    /// On WebGPU, this has no effect. Callbacks are invoked from the\n    /// window event loop.\n    WaitForSubmissionIndex(T),\n    /// Same as WaitForSubmissionIndex but waits for the most recent submission.\n    Wait,\n    /// Check the device for a single time without blocking.\n    Poll,\n}\n\npub enum PollStatus {\n    /// There are no active submissions in flight as of the beginning of the poll call.\n    /// Other submissions may have been queued on other threads during the call.\n    ///\n    /// This implies that the given Wait was satisfied before the timeout.\n    QueueEmpty,\n\n    /// The requested Wait was satisfied before the timeout.\n    WaitSucceeded,\n\n    /// This was a poll.\n    Poll,\n}\n\npub enum PollError {\n    /// The requested Wait timed out before the submission was completed.\n    Timeout,\n}\n```\n\n> [!WARNING]\n> As part of this change, WebGL's default behavior has changed. Previously `device.poll(Wait)` appeared as though it functioned correctly. This was a quirk caused by the bug that these PRs fixed. Now it will always return `Timeout` if the submission has not already completed. As many people rely on this behavior on WebGL, there is a new options in `BackendOptions`. If you want the old behavior, set the following on instance creation:\n>\n> ```rust\n> instance_desc.backend_options.gl.fence_behavior = wgpu::GlFenceBehavior::AutoFinish;\n> ```\n>\n> You will lose the ability to know exactly when a submission has completed, but `device.poll(Wait)` will behave the same as it does on native.\n\nBy @cwfitzgerald in [#6942](https://github.com/gfx-rs/wgpu/pull/6942) and [#7030](https://github.com/gfx-rs/wgpu/pull/7030).\n\n#### `wgpu::Device::start_capture` renamed, documented, and made unsafe\n\n```diff\n- device.start_capture();\n+ unsafe { device.start_graphics_debugger_capture() }\n// Your code here\n- device.stop_capture();\n+ unsafe { device.stop_graphics_debugger_capture() }\n```\n\nThere is now documentation to describe how this maps to the various debuggers' apis.\n\nBy @cwfitzgerald in [#7470](https://github.com/gfx-rs/wgpu/pull/7470)\n\n##### Ensure loops generated by SPIR-V and HLSL naga backends are bounded\n\nMake sure that all loops in shaders generated by these naga backends are bounded\nto avoid undefined behaviour due to infinite loops. Note that this may have a\nperformance cost. As with the existing implementation for the MSL backend this\ncan be disabled by using `Device::create_shader_module_trusted()`.\n\nBy @jamienicol in [#6929](https://github.com/gfx-rs/wgpu/pull/6929) and [#7080](https://github.com/gfx-rs/wgpu/pull/7080).\n\n#### Split up `Features` internally\n\nInternally split up the `Features` struct and recombine them internally using a macro. There should be no breaking\nchanges from this. This means there are also namespaces (as well as the old `Features::*`) for all wgpu specific\nfeatures and webgpu feature (`FeaturesWGPU` and `FeaturesWebGPU` respectively) and `Features::from_internal_flags` which\nallow you to be explicit about whether features you need are available on the web too.\n\nBy @Vecvec in [#6905](https://github.com/gfx-rs/wgpu/pull/6905), [#7086](https://github.com/gfx-rs/wgpu/pull/7086)\n\n#### WebGPU compliant dual source blending feature\n\nPreviously, dual source blending was implemented with a `wgpu` native only feature flag and used a custom syntax in wgpu.\nBy now, dual source blending was added to the [WebGPU spec as an extension](https://www.w3.org/TR/webgpu/#dom-gpufeaturename-dual-source-blending).\nWe're now following suite and implement the official syntax.\n\nExisting shaders using dual source blending need to be updated:\n\n```diff\nstruct FragmentOutput{\n-    @location(0) source0: vec4<f32>,\n-    @location(0) @second_blend_source source1: vec4<f32>,\n+    @location(0) @blend_src(0) source0: vec4<f32>,\n+    @location(0) @blend_src(1) source1: vec4<f32>,\n}\n\n```\n\nWith that `wgpu::Features::DUAL_SOURCE_BLENDING` is now available on WebGPU.\n\nFurthermore, GLSL shaders now support dual source blending as well via the `index` layout qualifier:\n\n```c\nlayout(location = 0, index = 0) out vec4 output0;\nlayout(location = 0, index = 1) out vec4 output1;\n```\n\nBy @wumpf in [#7144](https://github.com/gfx-rs/wgpu/pull/7144)\n\n#### Unify interface for SpirV shader passthrough\n\nReplace device `create_shader_module_spirv` function with a generic `create_shader_module_passthrough` function\ntaking a `ShaderModuleDescriptorPassthrough` enum as parameter.\n\nUpdate your calls to `create_shader_module_spirv` and use `create_shader_module_passthrough` instead:\n\n```diff\n-    device.create_shader_module_spirv(\n-        wgpu::ShaderModuleDescriptorSpirV {\n-            label: Some(&name),\n-            source: Cow::Borrowed(&source),\n-        }\n-    )\n+    device.create_shader_module_passthrough(\n+        wgpu::ShaderModuleDescriptorPassthrough::SpirV(\n+            wgpu::ShaderModuleDescriptorSpirV {\n+                label: Some(&name),\n+                source: Cow::Borrowed(&source),\n+            },\n+        ),\n+    )\n```\n\nBy @syl20bnr in [#7326](https://github.com/gfx-rs/wgpu/pull/7326).\n\n#### Noop Backend\n\nIt is now possible to create a dummy `wgpu` device even when no GPU is available. This may be useful for testing of code which manages graphics resources. Currently, it supports reading and writing buffers, and other resource types can be created but do nothing.\n\nTo use it, enable the `noop` feature of `wgpu`, and either call `Device::noop()`, or add `NoopBackendOptions { enable: true }` to the backend options of your `Instance` (this is an additional safeguard beyond the `Backends` bits).\n\nBy @kpreid in [#7063](https://github.com/gfx-rs/wgpu/pull/7063) and [#7342](https://github.com/gfx-rs/wgpu/pull/7342).\n\n#### `SHADER_F16` feature is now available with naga shaders\n\nPreviously this feature only allowed you to use `f16` on SPIR-V passthrough shaders. Now you can use it on all shaders, including WGSL, SPIR-V, and GLSL!\n\n```wgsl\nenable f16;\n\nfn hello_world(a: f16) -> f16 {\n    return a + 1.0h;\n}\n```\n\nBy @FL33TW00D, @ErichDonGubler, and @cwfitzgerald in [#5701](https://github.com/gfx-rs/wgpu/pull/5701)\n\n#### Bindless support improved and validation rules changed.\n\nMetal support for bindless has significantly improved and the limits for binding arrays have been increased.\n\nPreviously, all resources inside binding arrays contributed towards the standard limit of their type (`texture_2d` arrays for example would contribute to `max_sampled_textures_per_shader_stage`). Now these resources will only contribute towards binding-array specific limits:\n\n- `max_binding_array_elements_per_shader_stage` for all non-sampler resources\n- `max_binding_array_sampler_elements_per_shader_stage` for sampler resources.\n\nThis change has allowed the metal binding array limits to go from between 32 and 128 resources, all the way 500,000 sampled textures. Additionally binding arrays are now bound more efficiently on Metal.\n\nThis change also enabled legacy Intel GPUs to support 1M bindless resources, instead of the previous 1800.\n\nTo facilitate this change, there was an additional validation rule put in place: if there is a binding array in a bind group, you may not use dynamic offset buffers or uniform buffers in that bind group. This requirement comes from vulkan rules on `UpdateAfterBind` descriptors.\nBy @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](https://github.com/gfx-rs/wgpu/pull/6815), and [#6952](https://github.com/gfx-rs/wgpu/pull/6952).\n\n### New Features\n\n#### General\n\n- Add `Buffer` methods corresponding to `BufferSlice` methods, so you can skip creating a `BufferSlice` when it offers no benefit, and `BufferSlice::slice()` for sub-slicing a slice. By @kpreid in [#7123](https://github.com/gfx-rs/wgpu/pull/7123).\n- Add `BufferSlice::buffer()`, `BufferSlice::offset()` and `BufferSlice::size()`. By @kpreid in [#7148](https://github.com/gfx-rs/wgpu/pull/7148).\n- Add `impl From<BufferSlice> for BufferBinding` and `impl From<BufferSlice> for BindingResource`, allowing `BufferSlice`s to be easily used in creating bind groups. By @kpreid in [#7148](https://github.com/gfx-rs/wgpu/pull/7148).\n- Add `util::StagingBelt::allocate()` so the staging belt can be used to write textures. By @kpreid in [#6900](https://github.com/gfx-rs/wgpu/pull/6900).\n- Added `CommandEncoder::transition_resources()` for native API interop, and allowing users to slightly optimize barriers. By @JMS55 in [#6678](https://github.com/gfx-rs/wgpu/pull/6678).\n- Add `wgpu_hal::vulkan::Adapter::texture_format_as_raw` for native API interop. By @JMS55 in [#7228](https://github.com/gfx-rs/wgpu/pull/7228).\n- Support getting vertices of the hit triangle when raytracing. By @Vecvec in [#7183](https://github.com/gfx-rs/wgpu/pull/7183).\n- Add `as_hal` for both acceleration structures. By @Vecvec in [#7303](https://github.com/gfx-rs/wgpu/pull/7303).\n- Add Metal compute shader passthrough. Use `create_shader_module_passthrough` on device. By @syl20bnr in [#7326](https://github.com/gfx-rs/wgpu/pull/7326).\n- 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).\n- Added mesh shader support to `wgpu_hal`. By @SupaMaggie70Incorporated in [#7089](https://github.com/gfx-rs/wgpu/pull/7089)\n\n#### naga\n\n- 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).\n- Support @must_use attribute on function declarations. By @turbocrime in [#6801](https://github.com/gfx-rs/wgpu/pull/6801).\n- Support for generating the candidate intersections from AABB geometry, and confirming the hits. By @kvark in [#7047](https://github.com/gfx-rs/wgpu/pull/7047).\n- Make naga::back::spv::Function::to_words write the OpFunctionEnd instruction in itself, instead of making another call after it. By @junjunjd in [#7156](https://github.com/gfx-rs/wgpu/pull/7156).\n- Add support for texture memory barriers. By @Devon7925 in [#7173](https://github.com/gfx-rs/wgpu/pull/7173).\n- Add polyfills for `unpackSnorm4x8`, `unpackUnorm4x8`, `unpackSnorm2x16`, `unpackUnorm2x16` for GLSL versions they aren't supported in. By @DJMcNab in [#7408](https://github.com/gfx-rs/wgpu/pull/7408).\n\n#### Examples\n\n- Added an example that shows how to handle datasets too large to fit in a single `GPUBuffer` by distributing it across many buffers, and then having the shader receive them as a `binding_array` of storage buffers. By @alphastrata in [#6138](https://github.com/gfx-rs/wgpu/pull/6138)\n\n### Changes\n\n#### General\n\n- `wgpu::Instance::request_adapter()` now returns `Result` instead of `Option`; the error provides information about why no suitable adapter was returned. By @kpreid in [#7330](https://github.com/gfx-rs/wgpu/pull/7330).\n- Support BLAS compaction in wgpu-hal. By @Vecvec in [#7101](https://github.com/gfx-rs/wgpu/pull/7101).\n- Avoid using default features in many dependencies, etc. By Brody in [#7031](https://github.com/gfx-rs/wgpu/pull/7031)\n- Use `hashbrown` to simplify no-std support. By Brody in [#6938](https://github.com/gfx-rs/wgpu/pull/6938) & [#6925](https://github.com/gfx-rs/wgpu/pull/6925).\n- If you use Binding Arrays in a bind group, you may not use Dynamic Offset Buffers or Uniform Buffers in that bind group. By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811)\n- Rename `instance_id` and `instance_custom_index` to `instance_index` and `instance_custom_data` by @Vecvec in\n  [#6780](https://github.com/gfx-rs/wgpu/pull/6780)\n\n#### naga\n\n- naga IR types are now available in the module `naga::ir` (e.g. `naga::ir::Module`).\n  The original names (e.g. `naga::Module`) remain present for compatibility.\n  By @kpreid in [#7365](https://github.com/gfx-rs/wgpu/pull/7365).\n- Refactored `use` statements to simplify future `no_std` support. By @bushrat011899 in [#7256](https://github.com/gfx-rs/wgpu/pull/7256)\n- naga's WGSL frontend no longer allows using the `&` operator to take the address of a component of a vector,\n  which is not permitted by the WGSL specification. By @andyleiserson in [#7284](https://github.com/gfx-rs/wgpu/pull/7284)\n- 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)\n\n#### Vulkan\n\n##### HAL queue callback support\n\n- Add a way to notify with `Queue::submit()` to Vulkan's `vk::Semaphore` allocated outside of wgpu. By @sotaroikeda in [#6813](https://github.com/gfx-rs/wgpu/pull/6813).\n\n### Bug Fixes\n\n#### naga\n\n- 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).\n- Allow abstract expressions to be used in WGSL function return statements. By @jamienicol in [#7035](https://github.com/gfx-rs/wgpu/pull/7035).\n- Error if structs have two fields with the same name. By @SparkyPotato in [#7088](https://github.com/gfx-rs/wgpu/pull/7088).\n- Forward '--keep-coordinate-space' flag to GLSL backend in naga-cli. By @cloone8 in [#7206](https://github.com/gfx-rs/wgpu/pull/7206).\n- Allow template lists to have a trailing comma. By @KentSlaney in [#7142](https://github.com/gfx-rs/wgpu/pull/7142).\n- Allow WGSL const declarations to have abstract types. By @jamienicol in [#7055](https://github.com/gfx-rs/wgpu/pull/7055) and [#7222](https://github.com/gfx-rs/wgpu/pull/7222).\n- Allows override-sized arrays to resolve to the same size without causing the type arena to panic. By @KentSlaney in [#7082](https://github.com/gfx-rs/wgpu/pull/7082).\n- Allow abstract types to be used for WGSL switch statement selector and case selector expressions. By @jamienicol in [#7250](https://github.com/gfx-rs/wgpu/pull/7250).\n- Apply automatic conversions to `let` declarations, and accept `vecN()` as a constructor for vectors (in any context). By @andyleiserson in [#7367](https://github.com/gfx-rs/wgpu/pull/7367).\n- The `&&` and `||` operators are no longer allowed on vectors. By @andyleiserson in [#7368](https://github.com/gfx-rs/wgpu/pull/7368).\n- Prevent ray intersection function overwriting each other. By @Vecvec in [#7497](https://github.com/gfx-rs/wgpu/pull/7497).\n- Require that the level operand of an ImageQuery::Size expression is i32 or u32, per spec. By @jimblandy in [#7426](https://github.com/gfx-rs/wgpu/pull/7426).\n- Implement constant evaluation for the cross builtin. By @jimblandy in [#7404](https://github.com/gfx-rs/wgpu/pull/7404).\n- Properly handle automatic type conversions in calls to `MathFunction` builtins. By @jimblandy in [#6833](https://github.com/gfx-rs/wgpu/pull/6833).\n\n#### General\n\n- Fix some validation errors when building acceleration\n  structures. By @Vecvec in [#7486](https://github.com/gfx-rs/wgpu/pull/7486).\n- Avoid overflow in query set bounds check validation. By @ErichDonGubler in [#6933](https://github.com/gfx-rs/wgpu/pull/6933).\n- Add Flush to GL Queue::submit. By @cwfitzgerald in [#6941](https://github.com/gfx-rs/wgpu/pull/6941).\n- Reduce downlevel `max_color_attachments` limit from 8 to 4 for better GLES compatibility. By @adrian17 in [#6994](https://github.com/gfx-rs/wgpu/pull/6994).\n- Fix building a BLAS with a transform buffer by adding a flag to indicate usage of the transform buffer. By @Vecvec in\n  [#7062](https://github.com/gfx-rs/wgpu/pull/7062).\n- Move incrementation of `Device::last_acceleration_structure_build_command_index` into queue submit. By @Vecvec in [#7462](https://github.com/gfx-rs/wgpu/pull/7462).\n- Implement indirect draw validation. By @teoxoy in [#7140](https://github.com/gfx-rs/wgpu/pull/7140)\n\n#### Vulkan\n\n- Stop naga causing undefined behavior when a ray query misses. By @Vecvec in [#6752](https://github.com/gfx-rs/wgpu/pull/6752).\n- In naga's SPIR-V backend, avoid duplicating SPIR-V OpTypePointer instructions. By @jimblandy in [#7246](https://github.com/gfx-rs/wgpu/pull/7246).\n\n#### Gles\n\n- Support OpenHarmony render with `gles`. By @richerfu in [#7085](https://github.com/gfx-rs/wgpu/pull/7085)\n\n#### Dx12\n\n- Fix HLSL storage format generation. By @Vecvec in [#6993](https://github.com/gfx-rs/wgpu/pull/6993) and [#7104](https://github.com/gfx-rs/wgpu/pull/7104)\n- Fix 3D storage texture bindings. By @SparkyPotato in [#7071](https://github.com/gfx-rs/wgpu/pull/7071)\n- Fix DX12 composite alpha modes. By @amrbashir in [#7117](https://github.com/gfx-rs/wgpu/pull/7117)\n- Bound check dynamic buffers. By @teoxoy in [#6931](https://github.com/gfx-rs/wgpu/pull/6931)\n- Fix size of buffer. By @teoxoy in [#7310](https://github.com/gfx-rs/wgpu/pull/7310)\n\n#### WebGPU\n\n- Improve efficiency of dropping read-only buffer mappings. By @kpreid in [#7007](https://github.com/gfx-rs/wgpu/pull/7007).\n\n### Performance\n\n#### naga\n\n- Replace `unicode-xid` with `unicode-ident`. By @CrazyboyQCD in [#7135](https://github.com/gfx-rs/wgpu/pull/7135)\n\n### Documentation\n\n- Improved documentation around pipeline caches and `TextureBlitter`. By @DJMcNab in [#6978](https://github.com/gfx-rs/wgpu/pull/6978) and [#7003](https://github.com/gfx-rs/wgpu/pull/7003).\n- Improved documentation of `PresentMode`, buffer mapping functions, memory alignment requirements, texture formats’ automatic conversions, and various types and constants. By @kpreid in [#7211](https://github.com/gfx-rs/wgpu/pull/7211) and [#7283](https://github.com/gfx-rs/wgpu/pull/7283).\n\n- Added a hello window example. By @laycookie in [#6992](https://github.com/gfx-rs/wgpu/pull/6992).\n\n### Examples\n\n- Call `pre_present_notify()` before presenting. By @kjarosh in [#7074](https://github.com/gfx-rs/wgpu/pull/7074).\n\n## v24.0.5 (2025-05-24)\n\n### Bug Fixes\n\n#### General\n\n- Fix a possible deadlock within `Queue::write_buffer`. By @RedMindZ in [#7582](https://github.com/gfx-rs/wgpu/pull/7582)\n\n#### WebGPU\n\n- Insert fragment pipeline constants into fragment descriptor instead of vertex descriptor. By @DerSchmale in [#7621](https://github.com/gfx-rs/wgpu/pull/7621)\n\n## v24.0.4 (2025-04-03)\n\n### Metal\n\n- Use resize observers for smoother resizing. By @madsmtm in [#7026](https://github.com/gfx-rs/wgpu/pull/7026).\n\n## v24.0.3 (2025-03-19)\n\n### Bug Fixes\n\n- Fix drop order in `Surface`, solving segfaults on exit on some systems. By @ed-2100 in [#6997](https://github.com/gfx-rs/wgpu/pull/6997)\n\n## v24.0.2 (2025-02-26)\n\n### Bug Fixes\n\n- Fix GLES renderpass clears causing violation of `max_color_attachments` limit. By @adrian17 in [#6994](https://github.com/gfx-rs/wgpu/pull/6994).\n- Fix a possible deadlock within `Queue::write_texture`. By @metamuffin in [#7004](https://github.com/gfx-rs/wgpu/pull/7004)\n- Decrement `max_storage_buffer_binding_size` by 1 to match `max_buffer_size`. By @minus1ms in [#7217](https://github.com/gfx-rs/wgpu/pull/7217)\n\n## v24.0.1 (2025-01-22)\n\n### Bug Fixes\n\n- Fix `wgpu` not building with `--no-default-features` on when targeting `wasm32-unknown-unknown`. By @wumpf in [#6946](https://github.com/gfx-rs/wgpu/pull/6946).\n- Implement `Clone` on `ShaderModule`. By @a1phyr in [#6937](https://github.com/gfx-rs/wgpu/pull/6937).\n- Fix `CopyExternalImageDestInfo` not exported on `wgpu`. By @wumpf in [#6962](https://github.com/gfx-rs/wgpu/pull/6962).\n\n## v24.0.0 (2025-01-15)\n\n### Major changes\n\n#### Refactored Dispatch Between `wgpu-core` and `webgpu`\n\nThe crate `wgpu` has two different \"backends\", one which targets webgpu in the browser, one which targets `wgpu_core` on native platforms and webgl. This was previously very difficult to traverse and add new features to. The entire system was refactored to make it simpler. Additionally the new system has zero overhead if there is only one \"backend\" in use. You can see the new system in action by using go-to-definition on any wgpu functions in your IDE.\n\nBy @cwfitzgerald in [#6619](https://github.com/gfx-rs/wgpu/pull/6619).\n\n#### Most objects in `wgpu` are now `Clone`\n\nAll types in the `wgpu` API are now `Clone`.\nThis is implemented with internal reference counting, so cloning for instance a `Buffer` does copies only the \"handle\" of the GPU buffer, not the underlying resource.\n\nPreviously, libraries using `wgpu` objects like `Device`, `Buffer` or `Texture` etc. often had to manually wrap them in a `Arc` to allow passing between libraries.\nThis caused a lot of friction since if one library wanted to use a `Buffer` by value, calling code had to give up ownership of the resource which may interfere with other subsystems.\nNote that this also mimics how the WebGPU javascript API works where objects can be cloned and moved around freely.\n\nBy @cwfitzgerald in [#6665](https://github.com/gfx-rs/wgpu/pull/6665).\n\n#### Render and Compute Passes Now Properly Enforce Their Lifetime\n\nA regression introduced in 23.0.0 caused lifetimes of render and compute passes to be incorrectly enforced. While this is not\na soundness issue, the intent is to move an error from runtime to compile time. This issue has been fixed and restored to the 22.0.0 behavior.\n\n#### Bindless (`binding_array`) Grew More Capabilities\n\n- DX12 now supports `PARTIALLY_BOUND_BINDING_ARRAY` on Resource Binding Tier 3 Hardware. This is most D3D12 hardware [D3D12 Feature Table] for more information on what hardware supports this feature. By @cwfitzgerald in [#6734](https://github.com/gfx-rs/wgpu/pull/6734).\n\n[D3D12 Feature Table]: https://d3d12infodb.boolka.dev/FeatureTable.html\n\n#### `Device::create_shader_module_unchecked` Renamed and Now Has Configuration Options\n\n`create_shader_module_unchecked` became `create_shader_module_trusted`.\n\nThis allows you to customize which exact checks are omitted so that you can get the correct balance of performance and safety for your use case. Calling the function is still unsafe, but now can be used to skip certain checks only on certain builds.\n\nThis also allows users to disable the workarounds in the `msl-out` backend to prevent the compiler from optimizing infinite loops. This can have a big impact on performance, but is not recommended for untrusted shaders.\n\n```diff\nlet desc: ShaderModuleDescriptor = include_wgsl!(...)\n- let module = unsafe { device.create_shader_module_unchecked(desc) };\n+ let module = unsafe { device.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked()) };\n```\n\nBy @cwfitzgerald and @rudderbucky in [#6662](https://github.com/gfx-rs/wgpu/pull/6662).\n\n#### `wgpu::Instance::new` now takes `InstanceDescriptor` by reference\n\nPreviously `wgpu::Instance::new` took `InstanceDescriptor` by value (which is overall fairly uncommon in wgpu).\nFurthermore, `InstanceDescriptor` is now cloneable.\n\n```diff\n- let instance = wgpu::Instance::new(instance_desc);\n+ let instance = wgpu::Instance::new(&instance_desc);\n```\n\nBy @wumpf in [#6849](https://github.com/gfx-rs/wgpu/pull/6849).\n\n#### Environment Variable Handling Overhaul\n\nPreviously how various bits of code handled reading settings from environment variables was inconsistent and unideomatic.\nWe have unified it to (`Type::from_env()` or `Type::from_env_or_default()`) and `Type::with_env` for all types.\n\n```diff\n- wgpu::util::backend_bits_from_env()\n+ wgpu::Backends::from_env()\n\n- wgpu::util::power_preference_from_env()\n+ wgpu::PowerPreference::from_env()\n\n- wgpu::util::dx12_shader_compiler_from_env()\n+ wgpu::Dx12Compiler::from_env()\n\n- wgpu::util::gles_minor_version_from_env()\n+ wgpu::Gles3MinorVersion::from_env()\n\n- wgpu::util::instance_descriptor_from_env()\n+ wgpu::InstanceDescriptor::from_env_or_default()\n\n- wgpu::util::parse_backends_from_comma_list(&str)\n+ wgpu::Backends::from_comma_list(&str)\n```\n\nBy @cwfitzgerald in [#6895](https://github.com/gfx-rs/wgpu/pull/6895)\n\n#### Backend-specific instance options are now in separate structs\n\nIn order to better facilitate growing more interesting backend options, we have put them into individual structs. This allows users to more easily understand what options can be defaulted and which they care about. All of these new structs implement `from_env()` and delegate to their respective `from_env()` methods.\n\n```diff\n- let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {\n-     backends: wgpu::Backends::all(),\n-     flags: wgpu::InstanceFlags::default(),\n-     dx12_shader_compiler: wgpu::Dx12Compiler::Dxc,\n-     gles_minor_version: wgpu::Gles3MinorVersion::Automatic,\n- });\n+ let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {\n+     backends: wgpu::Backends::all(),\n+     flags: wgpu::InstanceFlags::default(),\n+     backend_options: wgpu::BackendOptions {\n+         dx12: wgpu::Dx12BackendOptions {\n+             shader_compiler: wgpu::Dx12ShaderCompiler::Dxc,\n+         },\n+         gl: wgpu::GlBackendOptions {\n+             gles_minor_version: wgpu::Gles3MinorVersion::Automatic,\n+         },\n+     },\n+ });\n```\n\nIf you do not need any of these options, or only need one backend's info use the `default()` impl to fill out the remaining feelds.\n\nBy @cwfitzgerald in [#6895](https://github.com/gfx-rs/wgpu/pull/6895)\n\n#### The `diagnostic(…);` directive is now supported in WGSL\n\nnaga 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 <https://github.com/gfx-rs/wgpu/issues/4369>) this snippet would emit a uniformity error:\n\n```wgsl\n@group(0) @binding(0) var s : sampler;\n@group(0) @binding(2) var tex : texture_2d<f32>;\n@group(1) @binding(0) var<storage, read> ro_buffer : array<f32, 4>;\n\n@fragment\nfn main(@builtin(position) p : vec4f) -> @location(0) vec4f {\n  if ro_buffer[0] == 0 {\n    // Emits a derivative uniformity error during validation.\n    return textureSample(tex, s, vec2(0.,0.));\n  }\n\n  return vec4f(0.);\n}\n```\n\n…but we can now silence it with the `off` severity level, like so:\n\n```wgsl\n// Disable the diagnosic with this…\ndiagnostic(off, derivative_uniformity);\n\n@group(0) @binding(0) var s : sampler;\n@group(0) @binding(2) var tex : texture_2d<f32>;\n@group(1) @binding(0) var<storage, read> ro_buffer : array<f32, 4>;\n\n@fragment\nfn main(@builtin(position) p : vec4f) -> @location(0) vec4f {\n  if ro_buffer[0] == 0 {\n    // Look ma, no error!\n    return textureSample(tex, s, vec2(0.,0.));\n  }\n\n  return vec4f(0.);\n}\n```\n\nThere are some limitations to keep in mind with this new functionality:\n\n- 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 <https://github.com/gfx-rs/wgpu/issues/5320>, so we can determine how to prioritize it!\n- 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 <https://github.com/gfx-rs/wgpu/issues/6458>.\n- 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.\n- 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., <https://github.com/gfx-rs/wgpu/issues/3135>) before we took significant effort to fix this (tracked in <https://github.com/gfx-rs/wgpu/issues/6496>).\n\nBy @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).\n\n#### New Features\n\n##### naga\n\n- 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).\n- Clean up tests for atomic operations support in SPIR-V frontend. By @schell in [#6692](https://github.com/gfx-rs/wgpu/pull/6692)\n- Fix an issue where `naga` CLI would incorrectly skip the first positional argument when `--stdin-file-path` was specified. By @ErichDonGubler in [#6480](https://github.com/gfx-rs/wgpu/pull/6480).\n- Fix textureNumLevels in the GLSL backend. By @magcius in [#6483](https://github.com/gfx-rs/wgpu/pull/6483).\n- Support 64-bit hex literals and unary operations in constants [#6616](https://github.com/gfx-rs/wgpu/pull/6616).\n- Implement `quantizeToF16()` for WGSL frontend, and WGSL, SPIR-V, HLSL, MSL, and GLSL backends. By @jamienicol in [#6519](https://github.com/gfx-rs/wgpu/pull/6519).\n- Add support for GLSL `usampler*` and `isampler*`. By @DavidPeicho in [#6513](https://github.com/gfx-rs/wgpu/pull/6513).\n- Expose Ray Query flags as constants in WGSL. Implement candidate intersections. By @kvark in [#5429](https://github.com/gfx-rs/wgpu/pull/5429)\n- Add new vertex formats (`{U,S}{int,norm}{8,16}`, `Float16` and `Unorm8x4Bgra`). By @nolanderc in [#6632](https://github.com/gfx-rs/wgpu/pull/6632)\n- Allow for override-expressions in `workgroup_size`. By @KentSlaney in [#6635](https://github.com/gfx-rs/wgpu/pull/6635).\n- Add support for OpAtomicCompareExchange in SPIR-V frontend. By @schell in [#6590](https://github.com/gfx-rs/wgpu/pull/6590).\n- Implement type inference for abstract arguments to user-defined functions. By @jamienicol in [#6577](https://github.com/gfx-rs/wgpu/pull/6577).\n- Allow for override-expressions in array sizes. By @KentSlaney in [#6654](https://github.com/gfx-rs/wgpu/pull/6654).\n- [`pointer_composite_access` WGSL language extension](https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access) is implemented. By @sagudev in [#6913](https://github.com/gfx-rs/wgpu/pull/6913)\n\n##### General\n\n- Add unified documentation for ray-tracing. By @Vecvec in [#6747](https://github.com/gfx-rs/wgpu/pull/6747)\n- Return submission index in `map_async` and `on_submitted_work_done` to track down completion of async callbacks. By @eliemichel in [#6360](https://github.com/gfx-rs/wgpu/pull/6360).\n- Move raytracing alignments into HAL instead of in core. By @Vecvec in [#6563](https://github.com/gfx-rs/wgpu/pull/6563).\n- Allow for statically linking DXC rather than including separate `.dll` files. By @DouglasDwyer in [#6574](https://github.com/gfx-rs/wgpu/pull/6574).\n- `DeviceType` and `AdapterInfo` now impl `Hash` by @cwfitzgerald in [#6868](https://github.com/gfx-rs/wgpu/pull/6868)\n- Add build support for Apple Vision Pro. By @guusw in [#6611](https://github.com/gfx-rs/wgpu/pull/6611).\n- Add `wgsl_language_features` for obtaining available WGSL language feature by @sagudev in [#6814](https://github.com/gfx-rs/wgpu/pull/6814)\n- Image atomic support in shaders. By @atlv24 in [#6706](https://github.com/gfx-rs/wgpu/pull/6706)\n- 64 bit image atomic support in shaders. By @atlv24 in [#5537](https://github.com/gfx-rs/wgpu/pull/5537)\n- Add `no_std` support to `wgpu-types`. By @bushrat011899 in [#6892](https://github.com/gfx-rs/wgpu/pull/6892).\n\n##### Vulkan\n\n- Allow using some 32-bit floating-point atomic operations (load, store, add, sub, exchange) in shaders. It requires the extension `VK_EXT_shader_atomic_float`. By @AsherJingkongChen in [#6234](https://github.com/gfx-rs/wgpu/pull/6234).\n\n##### Metal\n\n- Allow using some 32-bit floating-point atomic operations (load, store, add, sub, exchange) in shaders. It requires Metal 3.0+ with Apple 7, 8, 9 or Mac 2. By @AsherJingkongChen in [#6234](https://github.com/gfx-rs/wgpu/pull/6234).\n- Add build support for Apple Vision Pro. By @guusw in [#6611](https://github.com/gfx-rs/wgpu/pull/6611).\n- Add `raw_handle` method to access raw Metal textures in [#6894](https://github.com/gfx-rs/wgpu/pull/6894).\n\n#### D3D12\n\n- Support DXR (DirectX Ray-tracing) in wgpu-hal. By @Vecvec in [#6777](https://github.com/gfx-rs/wgpu/pull/6777)\n\n#### Changes\n\n##### naga\n\n- Show types of LHS and RHS in binary operation type mismatch errors. By @ErichDonGubler in [#6450](https://github.com/gfx-rs/wgpu/pull/6450).\n- The GLSL parser now uses less expressions for function calls. By @magcius in [#6604](https://github.com/gfx-rs/wgpu/pull/6604).\n- Add a note to help with a common syntax error case for global diagnostic filter directives. By @e-hat in [#6718](https://github.com/gfx-rs/wgpu/pull/6718)\n- Change arithmetic operations between two i32 variables to wrap on overflow to match WGSL spec. By @matthew-wong1 in [#6835](https://github.com/gfx-rs/wgpu/pull/6835).\n- Add directives to suggestions in error message for parsing global items. By @e-hat in [#6723](https://github.com/gfx-rs/wgpu/pull/6723).\n- Automatic conversion for `override` initializers. By @sagudev in [6920](https://github.com/gfx-rs/wgpu/pull/6920)\n\n##### General\n\n- Align Storage Access enums to the webgpu spec. By @atlv24 in [#6642](https://github.com/gfx-rs/wgpu/pull/6642)\n- Make `Surface::as_hal` take an immutable reference to the surface. By @jerzywilczek in [#9999](https://github.com/gfx-rs/wgpu/pull/9999)\n- Add actual sample type to `CreateBindGroupError::InvalidTextureSampleType` error message. By @ErichDonGubler in [#6530](https://github.com/gfx-rs/wgpu/pull/6530).\n- Improve binding error to give a clearer message when there is a mismatch between resource binding as it is in the shader and as it is in the binding layout. By @eliemichel in [#6553](https://github.com/gfx-rs/wgpu/pull/6553).\n- `Surface::configure` and `Surface::get_current_texture` are no longer fatal. By @alokedesai in [#6253](https://github.com/gfx-rs/wgpu/pull/6253)\n- Rename `BlasTriangleGeometry::index_buffer_offset` to `BlasTriangleGeometry::first_index`. By @Vecvec in [#6873](https://github.com/gfx-rs/wgpu/pull/6873/files)\n\n##### D3D12\n\n- Avoid using FXC as fallback when the DXC container was passed at instance creation. Paths to `dxcompiler.dll` & `dxil.dll` are also now required. By @teoxoy in [#6643](https://github.com/gfx-rs/wgpu/pull/6643).\n\n##### Vulkan\n\n- Add a cache for samplers, deduplicating any samplers, allowing more programs to stay within the global sampler limit. By @cwfitzgerald in [#6847](https://github.com/gfx-rs/wgpu/pull/6847)\n\n##### HAL\n\n- Replace `usage: Range<T>`, for `BufferUses`, `TextureUses`, and `AccelerationStructureBarrier` with a new `StateTransition<T>`. By @atlv24 in [#6703](https://github.com/gfx-rs/wgpu/pull/6703)\n- Change the `DropCallback` API to use `FnOnce` instead of `FnMut`. By @jerzywilczek in [#6482](https://github.com/gfx-rs/wgpu/pull/6482)\n\n### Bug Fixes\n\n#### General\n\n- Handle query set creation failure as an internal error that loses the `Device`, rather than panicking. By @ErichDonGubler in [#6505](https://github.com/gfx-rs/wgpu/pull/6505).\n- Ensure that `Features::TIMESTAMP_QUERY` is set when using timestamp writes in render and compute passes. By @ErichDonGubler in [#6497](https://github.com/gfx-rs/wgpu/pull/6497).\n- Check for device mismatches when beginning render and compute passes. By @ErichDonGubler in [#6497](https://github.com/gfx-rs/wgpu/pull/6497).\n- Lower `QUERY_SET_MAX_QUERIES` (and enforced limits) from 8192 to 4096 to match WebGPU spec. By @ErichDonGubler in [#6525](https://github.com/gfx-rs/wgpu/pull/6525).\n- Allow non-filterable float on texture bindings never used with samplers when using a derived bind group layout. By @ErichDonGubler in [#6531](https://github.com/gfx-rs/wgpu/pull/6531/).\n- Replace potentially unsound usage of `PreHashedMap` with `FastHashMap`. By @jamienicol in [#6541](https://github.com/gfx-rs/wgpu/pull/6541).\n- Add missing validation for timestamp writes in compute and render passes. By @ErichDonGubler in [#6578](https://github.com/gfx-rs/wgpu/pull/6578), [#6583](https://github.com/gfx-rs/wgpu/pull/6583).\n  - Check the status of the `TIMESTAMP_QUERY` feature before other validation.\n  - Check that indices are in-bounds for the query set.\n  - Check that begin and end indices are not equal.\n  - Check that at least one index is specified.\n- Reject destroyed buffers in query set resolution. By @ErichDonGubler in [#6579](https://github.com/gfx-rs/wgpu/pull/6579).\n- Fix panic when dropping `Device` on some environments. By @Dinnerbone in [#6681](https://github.com/gfx-rs/wgpu/pull/6681).\n- Reduced the overhead of command buffer validation. By @nical in [#6721](https://github.com/gfx-rs/wgpu/pull/6721).\n- Set index type to NONE in `get_acceleration_structure_build_sizes`. By @Vecvec in [#6802](https://github.com/gfx-rs/wgpu/pull/6802).\n- Fix `wgpu-info` not showing dx12 adapters. By @wumpf in [#6844](https://github.com/gfx-rs/wgpu/pull/6844).\n- Use `transform_buffer_offset` when initialising `transform_buffer`. By @Vecvec in [#6864](https://github.com/gfx-rs/wgpu/pull/6864).\n\n#### naga\n\n- Fix crash when a texture argument is missing. By @aedm in [#6486](https://github.com/gfx-rs/wgpu/pull/6486)\n- 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).\n- Fix an error in template list matching `>=` in `a<b>=c`. By @KentSlaney in [#6898](https://github.com/gfx-rs/wgpu/pull/6898).\n- Correctly validate handles in override-sized array types. By @jimblandy in [#6882](https://github.com/gfx-rs/wgpu/pull/6882).\n- Clean up validation of `Statement::ImageStore`. By @jimblandy in [#6729](https://github.com/gfx-rs/wgpu-pull/6729).\n- In compaction, avoid cloning the type arena. By @jimblandy in [#6790](https://github.com/gfx-rs/wgpu-pull/6790)\n- In validation, forbid cycles between global expressions and types. By @jimblandy in [#6800](https://github.com/gfx-rs/wgpu-pull/6800)\n- Allow abstract scalars in modf and frexp results. By @jimblandy in [#6821](https://github.com/gfx-rs/wgpu-pull/6821)\n- In the WGSL front end, apply automatic conversions to values being assigned. By @jimblandy in [#6822](https://github.com/gfx-rs/wgpu-pull/6822)\n- Fix a leak by ensuring that types that depend on expressions are correctly compacted. By @KentSlaney in [#6934](https://github.com/gfx-rs/wgpu/pull/6934).\n\n#### Vulkan\n\n- Allocate descriptors for acceleration structures. By @Vecvec in [#6861](https://github.com/gfx-rs/wgpu/pull/6861).\n- `max_color_attachment_bytes_per_sample` is now correctly set to 128. By @cwfitzgerald in [#6866](https://github.com/gfx-rs/wgpu/pull/6866)\n\n#### D3D12\n\n- Fix no longer showing software rasterizer adapters. By @wumpf in [#6843](https://github.com/gfx-rs/wgpu/pull/6843).\n- `max_color_attachment_bytes_per_sample` is now correctly set to 128. By @cwfitzgerald in [#6866](https://github.com/gfx-rs/wgpu/pull/6866)\n\n### Examples\n\n- Add multiple render targets example. By @kaphula in [#5297](https://github.com/gfx-rs/wgpu/pull/5313)\n\n### Testing\n\n- Tests the early returns in the acceleration structure build calls with empty calls. By @Vecvec in [#6651](https://github.com/gfx-rs/wgpu/pull/6651).\n\n## 23.0.1 (2024-11-25)\n\nThis release includes patches for `wgpu`, `wgpu-core` and `wgpu-hal`. All other crates remain at [23.0.0](https://github.com/gfx-rs/wgpu/releases/tag/v23.0.0).\nBelow changes were cherry-picked from 24.0.0 development line.\n\n### Bug fixes\n\n#### General\n\n- Fix Texture view leaks regression. By @xiaopengli89 in [#6576](https://github.com/gfx-rs/wgpu/pull/6576)\n\n#### Metal\n\n- Fix surface creation crashing on iOS. By @mockersf in [#6535](https://github.com/gfx-rs/wgpu/pull/6535)\n\n#### Vulkan\n\n- Fix surface capabilities being advertised when its query failed. By @wumpf in [#6510](https://github.com/gfx-rs/wgpu/pull/6510)\n\n## 23.0.0 (2024-10-25)\n\n### Themes of this release\n\nThis 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.\n\nThis 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.\n\n[nodejs-webgpu-interest]: https://github.com/orgs/nodejs/discussions/41994\n[deno_webgpu-crate-manifest]: https://github.com/gfx-rs/wgpu/tree/64a61ee5c69569bbb3db03563997e88a229eba17/deno_webgpu#deno_webgpu\n[eat-the-world-meaning]: https://www.quora.com/What-did-Marc-Andreessen-mean-when-he-said-that-software-is-eating-the-world\n\nwgpu 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?\n\nThe 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 <https://github.com/gfx-rs/wgpu/issues/4400>:\n\n```wgsl\nfn gimme_a_float() -> f32 {\n  return 42; // fails in naga, but standard WGSL happily converts to `f32`\n}\n```\n\nWe 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.\n\nThis 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!\n\n### Contributor spotlight: @sagudev\n\nThis 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:\n\n```wgsl\nconst arr: array<u32, 4> = array(1, 2, 3, 4);\n\nfn what_number_should_i_use(idx: u32) -> u32 {\n  return arr[idx];\n}\n```\n\n…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.\n\nWe'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.\n\n### Major Changes\n\nIn addition to the above spotlight, we have the following particularly interesting items to call out for this release:\n\n#### `wgpu-core` is no longer generic over `wgpu-hal` backends\n\nDynamic dispatch between different backends has been moved from the user facing `wgpu` crate, to a new dynamic dispatch mechanism inside the backend abstraction layer `wgpu-hal`.\n\nWhenever targeting more than a single backend (default on Windows & Linux) this leads to faster compile times and smaller binaries! This also solves a long standing issue with `cargo doc` failing to run for `wgpu-core`.\n\nBenchmarking indicated that compute pass recording is slower as a consequence, whereas on render passes speed improvements have been observed. However, this effort simplifies many of the internals of the wgpu family of crates which we're hoping to build performance improvements upon in the future.\n\nBy @wumpf in [#6069](https://github.com/gfx-rs/wgpu/pull/6069), [#6099](https://github.com/gfx-rs/wgpu/pull/6099), [#6100](https://github.com/gfx-rs/wgpu/pull/6100).\n\n#### `wgpu`'s resources no longer have `.global_id()` getters\n\n`wgpu-core`'s internals no longer use nor need IDs and we are moving towards removing IDs completely. This is a step in that direction.\n\nCurrent users of `.global_id()` are encouraged to make use of the `PartialEq`, `Eq`, `Hash`, `PartialOrd` and `Ord` traits that have now been implemented for `wgpu` resources.\n\nBy @teoxoy in [#6134](https://github.com/gfx-rs/wgpu/pull/6134).\n\n#### `set_bind_group` now takes an `Option` for the bind group argument.\n\nhttps://gpuweb.github.io/gpuweb/#programmable-passes-bind-groups specifies that bindGroup is nullable. This change is the start of implementing this part of the spec. Callers that specify a `Some()` value should have unchanged behavior. Handling of `None` values still needs to be implemented by backends.\n\nFor convenience, the `set_bind_group` on compute/render passes & encoders takes `impl Into<Option<&BindGroup>>`, so most code should still work the same.\n\nBy @bradwerth in [#6216](https://github.com/gfx-rs/wgpu/pull/6216).\n\n#### `entry_point`s are now `Option`al\n\nOne of the changes in the WebGPU spec. (from [about this time last year][optional-entrypoint-in-spec] 😅) was to allow optional entry points in `GPUProgrammableStage`. In `wgpu`, this corresponds to a subset of fields in `FragmentState`, `VertexState`, and `ComputeState` as the `entry_point` member:\n\n```wgsl\nlet render_pipeline = device.createRenderPipeline(wgpu::RenderPipelineDescriptor {\n    module,\n    entry_point: Some(\"cs_main\"), // This is now `Option`al.\n    // …\n});\n\nlet compute_pipeline = device.createComputePipeline(wgpu::ComputePipelineDescriptor {\n    module,\n    entry_point: None, // This is now `Option`al.\n    // …\n});\n```\n\nWhen set to `None`, it's assumed that the shader only has a single entry point associated with the pipeline stage (i.e., `@compute`, `@fragment`, or `@vertex`). If there is not one and only one candidate entry point, then a validation error is returned. To continue the example, we might have written the above API usage with the following shader module:\n\n```wgsl\n// We can't use `entry_point: None` for compute pipelines with this module,\n// because there are two `@compute` entry points.\n\n@compute\nfn cs_main() { /* … */ }\n\n@compute\nfn other_cs_main() { /* … */ }\n\n// The following entry points _can_ be inferred from `entry_point: None` in a\n// render pipeline, because they're the only `@vertex` and `@fragment` entry\n// points:\n\n@vertex\nfn vs_main() { /* … */ }\n\n@fragment\nfn fs_main() { /* … */ }\n```\n\n[optional-entrypoint-in-spec]: https://github.com/gpuweb/gpuweb/issues/4342\n\n#### wgpu's DX12 backend is now based on the `windows` crate ecosystem, instead of the `d3d12` crate\n\nwgpu 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.\n\nBy @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006).\n\n### New Features\n\n#### Wgpu\n\n- 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)\n\n#### naga\n\n- 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).\n- Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181).\n- Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824).\n- Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156).\n- Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198).\n- Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385).\n- 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).\n- 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).\n- Unify naga CLI error output with the format of shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436).\n\n#### General\n\n- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170).\n- Add `wgpu::util::new_instance_with_webgpu_detection` & `wgpu::util::is_browser_webgpu_supported` to make it easier to support WebGPU & WebGL in the same binary. By @wumpf in [#6371](https://github.com/gfx-rs/wgpu/pull/6371).\n\n#### Vulkan\n\n- Allow using [VK_GOOGLE_display_timing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html) unsafely with the `VULKAN_GOOGLE_DISPLAY_TIMING` feature. By @DJMcNab in [#6149](https://github.com/gfx-rs/wgpu/pull/6149).\n\n#### Metal\n\n- Implement `atomicCompareExchangeWeak`. By @AsherJingkongChen in [#6265](https://github.com/gfx-rs/wgpu/pull/6265).\n- Unless an explicit `CAMetalLayer` is provided, surfaces now render to a sublayer. This improves resizing behavior, fixing glitches during on window resize. By @madsmtm in [#6107](https://github.com/gfx-rs/wgpu/pull/6107).\n\n### Bug Fixes\n\n- Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123).\n\n#### naga\n\n- 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).\n- Accept only `vec3` (not `vecN`) for the `cross` built-in. By @ErichDonGubler in [#6171](https://github.com/gfx-rs/wgpu/pull/6171).\n- Configure `SourceLanguage` when enabling debug info in SPV-out. By @kvark in [#6256](https://github.com/gfx-rs/wgpu/pull/6256).\n- Do not consider per-polygon and flat inputs subgroup uniform. By @magcius in [#6276](https://github.com/gfx-rs/wgpu/pull/6276).\n- Validate all swizzle components are either color (rgba) or dimension (xyzw) in WGSL. By @sagudev in [#6187](https://github.com/gfx-rs/wgpu/pull/6187).\n- Fix detection of shl overflows to detect arithmetic overflows. By @sagudev in [#6186](https://github.com/gfx-rs/wgpu/pull/6186).\n- Fix type parameters to vec/mat type constructors to also support aliases. By @sagudev in [#6189](https://github.com/gfx-rs/wgpu/pull/6189).\n- Accept global `var`s without explicit type. By @sagudev in [#6199](https://github.com/gfx-rs/wgpu/pull/6199).\n- Fix handling of phony statements, so they are actually emitted. By @sagudev in [#6328](https://github.com/gfx-rs/wgpu/pull/6328).\n- Added `gl_DrawID` to glsl and `DrawIndex` to spv. By @ChosenName in [#6325](https://github.com/gfx-rs/wgpu/pull/6325).\n- Matrices can now be indexed by value (#4337), and indexing arrays by value no longer causes excessive spilling (#6358). By @jimblandy in [#6390](https://github.com/gfx-rs/wgpu/pull/6390).\n- Add support for `textureQueryLevels` to the GLSL parser. By @magcius in [#6325](https://github.com/gfx-rs/wgpu/pull/6415).\n- Fix unescaped identifiers in the Metal backend shader I/O structures causing shader miscompilation. By @ErichDonGubler in [#6438](https://github.com/gfx-rs/wgpu/pull/6438).\n\n#### General\n\n- If GL context creation fails retry with GLES. By @Rapdorian in [#5996](https://github.com/gfx-rs/wgpu/pull/5996).\n- Bump MSRV for `d3d12`/`naga`/`wgpu-core`/`wgpu-hal`/`wgpu-types`' to 1.76. By @wumpf in [#6003](https://github.com/gfx-rs/wgpu/pull/6003).\n- Print requested and supported usages on `UnsupportedUsage` error. By @VladasZ in [#6007](https://github.com/gfx-rs/wgpu/pull/6007).\n- Deduplicate bind group layouts that are created from pipelines with \"auto\" layouts. By @teoxoy [#6049](https://github.com/gfx-rs/wgpu/pull/6049).\n- 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).\n- 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).\n- **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).\n- Call `flush_mapped_ranges` when unmapping write-mapped buffers. By @teoxoy in [#6089](https://github.com/gfx-rs/wgpu/pull/6089).\n- 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).\n- Add a separate pipeline constants error. By @teoxoy in [#6094](https://github.com/gfx-rs/wgpu/pull/6094).\n- Ensure safety of indirect dispatch by injecting a compute shader that validates the content of the indirect buffer. By @teoxoy in [#5714](https://github.com/gfx-rs/wgpu/pull/5714).\n- Add conversions between `TextureFormat` and `StorageFormat`. By @caelunshun in [#6185](https://github.com/gfx-rs/wgpu/pull/6185)\n\n#### GLES / OpenGL\n\n- Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114).\n- Fix calling `slice::from_raw_parts` with unaligned pointers in push constant handling. By @Imberflur in [#6341](https://github.com/gfx-rs/wgpu/pull/6341).\n- Optimise fence checking when `Queue::submit` is called many times per frame. By @dinnerbone in [#6427](https://github.com/gfx-rs/wgpu/pull/6427).\n\n#### WebGPU\n\n- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197).\n\n#### Vulkan\n\n- Avoid undefined behaviour with adversarial debug label. By @DJMcNab in [#6257](https://github.com/gfx-rs/wgpu/pull/6257).\n- Add `.index_type(vk::IndexType::NONE_KHR)` when creating `AccelerationStructureGeometryTrianglesDataKHR` in the raytraced triangle example to prevent a validation error. By @Vecvec in [#6282](https://github.com/gfx-rs/wgpu/pull/6282).\n\n### Changes\n\n- `wgpu_hal::gles::Adapter::new_external` now requires the context to be current when dropping the adapter and related objects. By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114).\n- Reduce the amount of debug and trace logs emitted by wgpu-core and wgpu-hal. By @nical in [#6065](https://github.com/gfx-rs/wgpu/issues/6065).\n- Rename `Rg11b10Float` to `Rg11b10Ufloat`. By @sagudev in [#6108](https://github.com/gfx-rs/wgpu/pull/6108).\n- Invalidate the device when we encounter driver-induced device loss or on unexpected errors. By @teoxoy in [#6229](https://github.com/gfx-rs/wgpu/pull/6229).\n- Make Vulkan error handling more robust. By @teoxoy in [#6119](https://github.com/gfx-rs/wgpu/pull/6119).\n- Add bounds checking to Buffer slice method. By @beholdnec in [#6432](https://github.com/gfx-rs/wgpu/pull/6432).\n- Replace `impl From<StorageFormat> for ScalarKind` with `impl From<StorageFormat> for Scalar` so that byte width is included. By @atlv24 in [#6451](https://github.com/gfx-rs/wgpu/pull/6451).\n\n#### Internal\n\n- Tracker simplifications. By @teoxoy in [#6073](https://github.com/gfx-rs/wgpu/pull/6073) & [#6088](https://github.com/gfx-rs/wgpu/pull/6088).\n- D3D12 cleanup. By @teoxoy in [#6200](https://github.com/gfx-rs/wgpu/pull/6200).\n- Use `ManuallyDrop` in remaining places. By @teoxoy in [#6092](https://github.com/gfx-rs/wgpu/pull/6092).\n- Move out invalidity from the `Registry`. By @teoxoy in [#6243](https://github.com/gfx-rs/wgpu/pull/6243).\n- Remove `backend` from ID. By @teoxoy in [#6263](https://github.com/gfx-rs/wgpu/pull/6263).\n\n#### HAL\n\n- Change the inconsistent `DropGuard` based API on Vulkan and GLES to a consistent, callback-based one. By @jerzywilczek in [#6164](https://github.com/gfx-rs/wgpu/pull/6164).\n\n### Documentation\n\n- Removed some OpenGL and Vulkan references from `wgpu-types` documentation. Fixed Storage texel types in examples. By @Nelarius in [#6271](https://github.com/gfx-rs/wgpu/pull/6271).\n- Used `wgpu::include_wgsl!(…)` more in examples and tests. By @ErichDonGubler in [#6326](https://github.com/gfx-rs/wgpu/pull/6326).\n\n### Dependency Updates\n\n#### GLES\n\n- Replace `winapi` code in WGL wrapper to use the `windows` crate. By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006).\n- Update `glutin` to `0.31` with `glutin-winit` crate. By @MarijnS95 in [#6150](https://github.com/gfx-rs/wgpu/pull/6150) and [#6176](https://github.com/gfx-rs/wgpu/pull/6176).\n- Implement `Adapter::new_external()` for WGL (just like EGL) to import an external OpenGL ES context. By @MarijnS95 in [#6152](https://github.com/gfx-rs/wgpu/pull/6152).\n\n#### DX12\n\n- Replace `winapi` code to use the `windows` crate. By @MarijnS95 in [#5956](https://github.com/gfx-rs/wgpu/pull/5956) and [#6173](https://github.com/gfx-rs/wgpu/pull/6173).\n- Get `num_workgroups` builtin working for indirect dispatches. By @teoxoy in [#5730](https://github.com/gfx-rs/wgpu/pull/5730).\n\n#### HAL\n\n- Update `parking_lot` to `0.12`. By @mahkoh in [#6287](https://github.com/gfx-rs/wgpu/pull/6287).\n\n## v22.1.0 (2024-07-17)\n\nThis release includes `wgpu`, `wgpu-core` and `naga`. All other crates remain at 22.0.0.\n\n### Added\n\n#### naga\n\n- Added back implementations of PartialEq for more IR types. By @teoxoy in [#6045](https://github.com/gfx-rs/wgpu/pull/6045)\n\n### Bug Fixes\n\n#### General\n\n- Fix profiling with `tracy`. By @waywardmonkeys in [#5988](https://github.com/gfx-rs/wgpu/pull/5988)\n- Fix function for checking bind compatibility to error instead of panic. By @sagudev [#6012](https://github.com/gfx-rs/wgpu/pull/6012)\n- Fix crash when dropping the surface after the device. By @wumpf in [#6052](https://github.com/gfx-rs/wgpu/pull/6052)\n- Fix length of copy in `queue_write_texture`. By @teoxoy in [#6009](https://github.com/gfx-rs/wgpu/pull/6009)\n- Fix error message that is thrown in create_render_pass to no longer say `compute_pass`. By @matthew-wong1 [#6041](https://github.com/gfx-rs/wgpu/pull/6041)\n- As a workaround for [issue #4905](https://github.com/gfx-rs/wgpu/issues/4905), `wgpu-core` is undocumented unless `--cfg wgpu_core_doc` feature is enabled. By @kpreid in [#5987](https://github.com/gfx-rs/wgpu/pull/5987)\n\n## 22.0.0 (2024-07-17)\n\n### Overview\n\n### Our first major version release!\n\nFor 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):\n\n> ### How do I know when to release 1.0.0?\n>\n> 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.\n\nIt 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.\n\nNote 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.\n\n### Overview\n\nA 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.\n\nWhile 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!\n\n### Major Changes\n\n#### Lifetime bounds on `wgpu::RenderPass` & `wgpu::ComputePass`\n\n`wgpu::RenderPass` & `wgpu::ComputePass` recording methods (e.g. `wgpu::RenderPass:set_render_pipeline`) no longer impose a lifetime constraint to objects passed to a pass (like pipelines/buffers/bindgroups/query-sets etc.).\n\nThis means the following pattern works now as expected:\n\n```rust\nlet mut pipelines: Vec<wgpu::RenderPipeline> = ...;\n// ...\nlet mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\ncpass.set_pipeline(&pipelines[123]);\n// Change pipeline container - this requires mutable access to `pipelines` while one of the pipelines is in use.\npipelines.push(/* ... */);\n// Continue pass recording.\ncpass.set_bindgroup(...);\n```\n\nPreviously, a set pipeline (or other resource) had to outlive pass recording which often affected wider systems,\nmeaning that users needed to prove to the borrow checker that `Vec<wgpu::RenderPipeline>` (or similar constructs)\naren't accessed mutably for the duration of pass recording.\n\nFurthermore, you can now opt out of `wgpu::RenderPass`/`wgpu::ComputePass`'s lifetime dependency on its parent `wgpu::CommandEncoder` using `wgpu::RenderPass::forget_lifetime`/`wgpu::ComputePass::forget_lifetime`:\n\n```rust\nfn independent_cpass<'enc>(encoder: &'enc mut wgpu::CommandEncoder) -> wgpu::ComputePass<'static> {\n    let cpass: wgpu::ComputePass<'enc> = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n    cpass.forget_lifetime()\n}\n```\n\n⚠️ As long as a `wgpu::RenderPass`/`wgpu::ComputePass` is pending for a given `wgpu::CommandEncoder`, creation of a compute or render pass is an error and invalidates the `wgpu::CommandEncoder`.\n`forget_lifetime` can be very useful for library authors, but opens up an easy way for incorrect use, so use with care.\nThis method doesn't add any additional overhead and has no side effects on pass recording.\n\nBy @wumpf in [#5569](https://github.com/gfx-rs/wgpu/pull/5569), [#5575](https://github.com/gfx-rs/wgpu/pull/5575), [#5620](https://github.com/gfx-rs/wgpu/pull/5620), [#5768](https://github.com/gfx-rs/wgpu/pull/5768) (together with @kpreid), [#5671](https://github.com/gfx-rs/wgpu/pull/5671), [#5794](https://github.com/gfx-rs/wgpu/pull/5794), [#5884](https://github.com/gfx-rs/wgpu/pull/5884).\n\n#### Querying shader compilation errors\n\nWgpu now supports querying [shader compilation info](https://www.w3.org/TR/webgpu/#dom-gpushadermodule-getcompilationinfo).\n\nThis allows you to get more structured information about compilation errors, warnings and info:\n\n```rust\n...\nlet lighting_shader = ctx.device.create_shader_module(include_wgsl!(\"lighting.wgsl\"));\nlet compilation_info = lighting_shader.get_compilation_info().await;\nfor message in compilation_info\n    .messages\n    .iter()\n    .filter(|m| m.message_type == wgpu::CompilationMessageType::Error)\n{\n    let line = message.location.map(|l| l.line_number).unwrap_or(1);\n    println!(\"Compile error at line {line}\");\n}\n```\n\nBy @stefnotch in [#5410](https://github.com/gfx-rs/wgpu/pull/5410)\n\n#### 64 bit integer atomic support in shaders.\n\nAdd support for 64 bit integer atomic operations in shaders.\n\nAdd the following flags to `wgpu_types::Features`:\n\n- `SHADER_INT64_ATOMIC_ALL_OPS` enables all atomic operations on `atomic<i64>` and\n  `atomic<u64>` values.\n\n- `SHADER_INT64_ATOMIC_MIN_MAX` is a subset of the above, enabling only\n  `AtomicFunction::Min` and `AtomicFunction::Max` operations on `atomic<i64>` and\n  `atomic<u64>` values in the `Storage` address space. These are the only 64-bit\n  atomic operations available on Metal as of 3.1.\n\nAdd corresponding flags to `naga::valid::Capabilities`. These are supported by the\nWGSL front end, and all naga backends.\n\nPlatform support:\n\n- On Direct3d 12, in `D3D12_FEATURE_DATA_D3D12_OPTIONS9`, if\n  `AtomicInt64OnTypedResourceSupported` and `AtomicInt64OnGroupSharedSupported` are\n  both available, then both wgpu features described above are available.\n\n- On Metal, `SHADER_INT64_ATOMIC_MIN_MAX` is available on Apple9 hardware, and on\n  hardware that advertises both Apple8 and Mac2 support. This also requires Metal\n  Shading Language 2.4 or later. Metal does not yet support the more general\n  `SHADER_INT64_ATOMIC_ALL_OPS`.\n\n- On Vulkan, if the `VK_KHR_shader_atomic_int64` extension is available with both the\n  `shader_buffer_int64_atomics` and `shader_shared_int64_atomics` features, then both\n  wgpu features described above are available.\n\nBy @atlv24 in [#5383](https://github.com/gfx-rs/wgpu/pull/5383)\n\n#### A compatible surface is now required for `request_adapter()` on WebGL2 + `enumerate_adapters()` is now native only.\n\nWhen targeting WebGL2, it has always been the case that a surface had to be created before calling `request_adapter()`.\nWe now make this requirement explicit.\n\nValidation was also added to prevent configuring the surface with a device that doesn't share the same underlying\nWebGL2 context since this has never worked.\n\nCalling `enumerate_adapters()` when targeting WebGPU used to return an empty `Vec` and since we now require users\nto pass a compatible surface when targeting WebGL2, having `enumerate_adapters()` doesn't make sense.\n\nBy @teoxoy in [#5901](https://github.com/gfx-rs/wgpu/pull/5901)\n\n### New features\n\n#### General\n\n- Added `as_hal` for `Buffer` to access wgpu created buffers form wgpu-hal. By @JasondeWolff in [#5724](https://github.com/gfx-rs/wgpu/pull/5724)\n- `include_wgsl!` is now callable in const contexts by @9SMTM6 in [#5872](https://github.com/gfx-rs/wgpu/pull/5872)\n- Added memory allocation hints to `DeviceDescriptor` by @nical in [#5875](https://github.com/gfx-rs/wgpu/pull/5875)\n  - `MemoryHints::Performance`, the default, favors performance over memory usage and will likely cause large amounts of VRAM to be allocated up-front. This hint is typically good for games.\n  - `MemoryHints::MemoryUsage` favors memory usage over performance. This hint is typically useful for smaller applications or UI libraries.\n  - `MemoryHints::Manual` allows the user to specify parameters for the underlying GPU memory allocator. These parameters are subject to change.\n  - These hints may be ignored by some backends. Currently only the Vulkan and D3D12 backends take them into account.\n- Add `HTMLImageElement` and `ImageData` as external source for copying images. By @Valaphee in [#5668](https://github.com/gfx-rs/wgpu/pull/5668)\n\n#### naga\n\n- Added -D, --defines option to naga CLI to define preprocessor macros by @theomonnom in [#5859](https://github.com/gfx-rs/wgpu/pull/5859)\n- 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).\n- Implement `WGSL`'s `unpack4xI8`,`unpack4xU8`,`pack4xI8` and `pack4xU8`. By @VlaDexa in [#5424](https://github.com/gfx-rs/wgpu/pull/5424)\n- Began work adding support for atomics to the SPIR-V frontend. Tracking issue is [here](https://github.com/gfx-rs/wgpu/issues/4489). By @schell in [#5702](https://github.com/gfx-rs/wgpu/pull/5702).\n- In hlsl-out, allow passing information about the fragment entry point to omit vertex outputs that are not in the fragment inputs. By @Imberflur in [#5531](https://github.com/gfx-rs/wgpu/pull/5531)\n- In spv-out, allow passing `acceleration_structure` as a function argument. By @kvark in [#5961](https://github.com/gfx-rs/wgpu/pull/5961)\n\n  ```diff\n  let writer: naga::back::hlsl::Writer = /* ... */;\n  -writer.write(&module, &module_info);\n  +writer.write(&module, &module_info, None);\n  ```\n\n- HLSL & MSL output can now be added conditionally on the target via the `msl-out-if-target-apple` and `hlsl-out-if-target-windows` features. This is used in wgpu-hal to no longer compile with MSL output when `metal` is enabled & MacOS isn't targeted and no longer compile with HLSL output when `dx12` is enabled & Windows isn't targeted. By @wumpf in [#5919](https://github.com/gfx-rs/wgpu/pull/5919)\n\n#### Vulkan\n\n- Added a `PipelineCache` resource to allow using Vulkan pipeline caches. By @DJMcNab in [#5319](https://github.com/gfx-rs/wgpu/pull/5319)\n\n#### WebGPU\n\n- Added support for pipeline-overridable constants to the WebGPU backend by @DouglasDwyer in [#5688](https://github.com/gfx-rs/wgpu/pull/5688)\n\n### Changes\n\n#### General\n\n- Unconsumed vertex outputs are now always allowed. Removed `StageError::InputNotConsumed`, `Features::SHADER_UNUSED_VERTEX_OUTPUT`, and associated validation. By @Imberflur in [#5531](https://github.com/gfx-rs/wgpu/pull/5531)\n- Avoid introducing spurious features for optional dependencies. By @bjorn3 in [#5691](https://github.com/gfx-rs/wgpu/pull/5691)\n- `wgpu::Error` is now `Sync`, making it possible to be wrapped in `anyhow::Error` or `eyre::Report`. By @nolanderc in [#5820](https://github.com/gfx-rs/wgpu/pull/5820)\n- Added benchmark suite. By @cwfitzgerald in [#5694](https://github.com/gfx-rs/wgpu/pull/5694), compute passes by @wumpf in [#5767](https://github.com/gfx-rs/wgpu/pull/5767)\n- Improve performance of `.submit()` by 39-64% (`.submit()` + `.poll()` by 22-32%). By @teoxoy in [#5910](https://github.com/gfx-rs/wgpu/pull/5910)\n- The `trace` wgpu feature has been temporarily removed. By @teoxoy in [#5975](https://github.com/gfx-rs/wgpu/pull/5975)\n\n#### Metal\n\n- Removed the `link` Cargo feature.\n\n  This was used to allow weakly linking frameworks. This can be achieved with putting something like the following in your `.cargo/config.toml` instead:\n\n  ```toml\n  [target.'cfg(target_vendor = \"apple\")']\n  rustflags = [\"-C\", \"link-args=-weak_framework Metal -weak_framework QuartzCore -weak_framework CoreGraphics\"]\n  ```\n\n  By @madsmtm in [#5752](https://github.com/gfx-rs/wgpu/pull/5752)\n\n### Bug Fixes\n\n#### General\n\n- Ensure render pipelines have at least 1 target. By @ErichDonGubler in [#5715](https://github.com/gfx-rs/wgpu/pull/5715)\n- `wgpu::ComputePass` now internally takes ownership of `QuerySet` for both `wgpu::ComputePassTimestampWrites` as well as timestamp writes and statistics query, fixing crashes when destroying `QuerySet` before ending the pass. By @wumpf in [#5671](https://github.com/gfx-rs/wgpu/pull/5671)\n- Validate resources passed during compute pass recording for mismatching device. By @wumpf in [#5779](https://github.com/gfx-rs/wgpu/pull/5779)\n- Fix staging buffers being destroyed too early. By @teoxoy in [#5910](https://github.com/gfx-rs/wgpu/pull/5910)\n- Fix attachment byte cost validation panicking with native only formats. By @teoxoy in [#5934](https://github.com/gfx-rs/wgpu/pull/5934)\n- [wgpu] Fix leaks from auto layout pipelines. By @teoxoy in [#5971](https://github.com/gfx-rs/wgpu/pull/5971)\n- [wgpu-core] Fix length of copy in `queue_write_texture` (causing UB). By @teoxoy in [#5973](https://github.com/gfx-rs/wgpu/pull/5973)\n- Add missing same device checks. By @teoxoy in [#5980](https://github.com/gfx-rs/wgpu/pull/5980)\n\n#### GLES / OpenGL\n\n- Fix `ClearColorF`, `ClearColorU` and `ClearColorI` commands being issued before `SetDrawColorBuffers` [#5666](https://github.com/gfx-rs/wgpu/pull/5666)\n- 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)\n- Return the unmodified version in driver_info. By @Valaphee in [#5753](https://github.com/gfx-rs/wgpu/pull/5753)\n\n#### naga\n\n- 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)\n- Add `packed` as a keyword for GLSL by @kjarosh in [#5855](https://github.com/gfx-rs/wgpu/pull/5855)\n\n## v0.20.2 (2024-06-12)\n\nThis release force-bumps transitive dependencies of `wgpu` on `wgpu-core` and `wgpu-hal` to 0.21.1, to resolve some undefined behavior observable in the DX12 backend after upgrading to Rust 1.79 or later.\n\n### Bug Fixes\n\n#### General\n\n- Fix a `CommandBuffer` leak. By @cwfitzgerald and @nical in [#5141](https://github.com/gfx-rs/wgpu/pull/5141)\n\n#### DX12\n\n- Do not feed `&\"\"` to `D3DCompile`, by @workingjubilee in [#5812](https://github.com/gfx-rs/wgpu/issues/5812).\n\n## v0.20.1 (2024-06-12)\n\nThis release included v0.21.0 of `wgpu-core` and `wgpu-hal`, due to breaking changes needed to solve vulkan validation issues.\n\n### Bug Fixes\n\nThis release fixes the validation errors whenever a surface is used with the vulkan backend. By @cwfitzgerald in [#5681](https://github.com/gfx-rs/wgpu/pull/5681).\n\n#### General\n\n- Clean up weak references to texture views and bind groups to prevent memory leaks. By @xiaopengli89 in [#5595](https://github.com/gfx-rs/wgpu/pull/5595).\n- Fix segfault on exit is queue & device are dropped before surface. By @sagudev in [#5640](https://github.com/gfx-rs/wgpu/pull/5640).\n\n#### Metal\n\n- Fix unrecognized selector crash on iOS 12. By @vladasz in [#5744](https://github.com/gfx-rs/wgpu/pull/5744).\n\n#### Vulkan\n\n- Fix enablement of subgroup ops extension on Vulkan devices that don't support Vulkan 1.3. By @cwfitzgerald in [#5624](https://github.com/gfx-rs/wgpu/pull/5624).\n\n#### GLES / OpenGL\n\n- Fix regression on OpenGL (EGL) where non-sRGB still used sRGB [#5642](https://github.com/gfx-rs/wgpu/pull/5642)\n\n#### naga\n\n- 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)\n- 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)\n\n## v0.20.0 (2024-04-28)\n\n### Major Changes\n\n#### Pipeline overridable constants\n\nWgpu supports now [pipeline-overridable constants](https://www.w3.org/TR/webgpu/#dom-gpuprogrammablestage-constants)\n\nThis allows you to define constants in wgsl like this:\n\n```rust\noverride some_factor: f32 = 42.1337; // Specifies a default of 42.1337 if it's not set.\n```\n\nAnd then set them at runtime like so on your pipeline consuming this shader:\n\n```rust\n// ...\nfragment: Some(wgpu::FragmentState {\n    compilation_options: wgpu::PipelineCompilationOptions {\n        constants: &[(\"some_factor\".to_owned(), 0.1234)].into(), // Sets `some_factor` to 0.1234.\n        ..Default::default()\n    },\n    // ...\n}),\n// ...\n```\n\nBy @teoxoy & @jimblandy in [#5500](https://github.com/gfx-rs/wgpu/pull/5500)\n\n#### Changed feature requirements for timestamps\n\nDue to a specification change `write_timestamp` is no longer supported on WebGPU.\n`wgpu::CommandEncoder::write_timestamp` requires now the new `wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS` feature which is available on all native backends but not on WebGPU.\n\nBy @wumpf in [#5188](https://github.com/gfx-rs/wgpu/pull/5188)\n\n#### Wgsl const evaluation for many more built-ins\n\nMany numeric built-ins have had a constant evaluation implementation added for them, which allows them to be used in a `const` context:\n\n`abs`, `acos`, `acosh`, `asin`, `asinh`, `atan`, `atanh`, `cos`, `cosh`, `round`, `saturate`, `sin`, `sinh`, `sqrt`, `step`, `tan`, `tanh`, `ceil`, `countLeadingZeros`, `countOneBits`, `countTrailingZeros`, `degrees`, `exp`, `exp2`, `floor`, `fract`, `fma`, `inverseSqrt`, `log`, `log2`, `max`, `min`, `radians`, `reverseBits`, `sign`, `trunc`\n\nBy @ErichDonGubler in [#4879](https://github.com/gfx-rs/wgpu/pull/4879), [#5098](https://github.com/gfx-rs/wgpu/pull/5098)\n\n#### New **native-only** wgsl features\n\n##### Subgroup operations\n\nThe following subgroup operations are available in wgsl now:\n\n`subgroupBallot`, `subgroupAll`, `subgroupAny`, `subgroupAdd`, `subgroupMul`, `subgroupMin`, `subgroupMax`, `subgroupAnd`, `subgroupOr`, `subgroupXor`, `subgroupExclusiveAdd`, `subgroupExclusiveMul`, `subgroupInclusiveAdd`, `subgroupInclusiveMul`, `subgroupBroadcastFirst`, `subgroupBroadcast`, `subgroupShuffle`, `subgroupShuffleDown`, `subgroupShuffleUp`, `subgroupShuffleXor`\n\nAvailability is governed by the following feature flags:\n\n- `wgpu::Features::SUBGROUP` for all operations except `subgroupBarrier` in fragment & compute, supported on Vulkan, DX12 and Metal.\n- `wgpu::Features::SUBGROUP_VERTEX`, for all operations except `subgroupBarrier` general operations in vertex shaders, supported on Vulkan\n- `wgpu::Features::SUBGROUP_BARRIER`, for support of the `subgroupBarrier` operation, supported on Vulkan & Metal\n\nNote that there currently [some differences](https://github.com/gfx-rs/wgpu/issues/5555) between wgpu's native-only implementation and the [open WebGPU proposal](https://github.com/gpuweb/gpuweb/blob/main/proposals/subgroups.md).\n\nBy @exrook and @lichtso in [#5301](https://github.com/gfx-rs/wgpu/pull/5301)\n\n##### Signed and unsigned 64 bit integer support in shaders.\n\n`wgpu::Features::SHADER_INT64` enables 64 bit integer signed and unsigned integer variables in wgsl (`i64` and `u64` respectively).\nSupported on Vulkan, DX12 (requires DXC) and Metal (with MSL 2.3+ support).\n\nBy @atlv24 and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154)\n\n### New features\n\n#### General\n\n- Implemented the `Unorm10_10_10_2` VertexFormat by @McMackety in [#5477](https://github.com/gfx-rs/wgpu/pull/5477)\n- `wgpu-types`'s `trace` and `replay` features have been replaced by the `serde` feature. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149)\n- `wgpu-core`'s `serial-pass` feature has been removed. Use `serde` instead. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149)\n- Added `InstanceFlags::GPU_BASED_VALIDATION`, which enables GPU-based validation for shaders. This is currently only supported on the DX12 and Vulkan backends; other platforms ignore this flag, for now. By @ErichDonGubler in [#5146](https://github.com/gfx-rs/wgpu/pull/5146), [#5046](https://github.com/gfx-rs/wgpu/pull/5046).\n  - When set, this flag implies `InstanceFlags::VALIDATION`.\n  - This has been added to the set of flags set by `InstanceFlags::advanced_debugging`. Since the overhead is potentially very large, the flag is not enabled by default in debug builds when using `InstanceFlags::from_build_config`.\n  - As with other instance flags, this flag can be changed in calls to `InstanceFlags::with_env` with the new `WGPU_GPU_BASED_VALIDATION` environment variable.\n- `wgpu::Instance` can now report which `wgpu::Backends` are available based on the build configuration. By @wumpf [#5167](https://github.com/gfx-rs/wgpu/pull/5167)\n  ```diff\n  -wgpu::Instance::any_backend_feature_enabled()\n  +!wgpu::Instance::enabled_backend_features().is_empty()\n  ```\n- Breaking change: [`wgpu_core::pipeline::ProgrammableStageDescriptor`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ProgrammableStageDescriptor.html#structfield.entry_point) is now optional. By @ErichDonGubler in [#5305](https://github.com/gfx-rs/wgpu/pull/5305).\n- `Features::downlevel{_webgl2,}_features` was made const by @MultisampledNight in [#5343](https://github.com/gfx-rs/wgpu/pull/5343)\n- Breaking change: [`wgpu_core::pipeline::ShaderError`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ShaderError.html) has been moved to `naga`. By @stefnotch in [#5410](https://github.com/gfx-rs/wgpu/pull/5410)\n- More as_hal methods and improvements by @JMS55 in [#5452](https://github.com/gfx-rs/wgpu/pull/5452)\n  - Added `wgpu::CommandEncoder::as_hal_mut`\n  - Added `wgpu::TextureView::as_hal`\n  - `wgpu::Texture::as_hal` now returns a user-defined type to match the other as_hal functions\n\n#### naga\n\n- 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)\n- 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)\n- 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)\n- 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)\n\n#### WebGPU\n\n- Implement the `device_set_device_lost_callback` method for `ContextWebGpu`. By @suti in [#5438](https://github.com/gfx-rs/wgpu/pull/5438)\n- Add support for storage texture access modes `ReadOnly` and `ReadWrite`. By @JolifantoBambla in [#5434](https://github.com/gfx-rs/wgpu/pull/5434)\n\n#### GLES / OpenGL\n\n- Log an error when GLES texture format heuristics fail. By @PolyMeilex in [#5266](https://github.com/gfx-rs/wgpu/issues/5266)\n- Cache the sample count to keep `get_texture_format_features` cheap. By @Dinnerbone in [#5346](https://github.com/gfx-rs/wgpu/pull/5346)\n- Mark `DEPTH32FLOAT_STENCIL8` as supported in GLES. By @Dinnerbone in [#5370](https://github.com/gfx-rs/wgpu/pull/5370)\n- Desktop GL now also supports `TEXTURE_COMPRESSION_ETC2`. By @Valaphee in [#5568](https://github.com/gfx-rs/wgpu/pull/5568)\n- Don't create a program for shader-clearing if that workaround isn't required. By @Dinnerbone in [#5348](https://github.com/gfx-rs/wgpu/pull/5348).\n- OpenGL will now be preferred over OpenGL ES on EGL, making it consistent with WGL. By @valaphee in [#5482](https://github.com/gfx-rs/wgpu/pull/5482)\n- Fill out `driver` and `driver_info`, with the OpenGL flavor and version, similar to Vulkan. By @valaphee in [#5482](https://github.com/gfx-rs/wgpu/pull/5482)\n\n#### Metal\n\n- Metal 3.0 and 3.1 detection. By @atlv24 in [#5497](https://github.com/gfx-rs/wgpu/pull/5497)\n\n#### DX12\n\n- Shader Model 6.1-6.7 detection. By @atlv24 in [#5498](https://github.com/gfx-rs/wgpu/pull/5498)\n\n### Other performance improvements\n\n- Simplify and speed up the allocation of internal IDs. By @nical in [#5229](https://github.com/gfx-rs/wgpu/pull/5229)\n- Use memory pooling for UsageScopes to avoid frequent large allocations. by @robtfm in [#5414](https://github.com/gfx-rs/wgpu/pull/5414)\n- Eager release of GPU resources comes from device.trackers. By @bradwerth in [#5075](https://github.com/gfx-rs/wgpu/pull/5075)\n- Support disabling zero-initialization of workgroup local memory in compute shaders. By @DJMcNab in [#5508](https://github.com/gfx-rs/wgpu/pull/5508)\n\n### Documentation\n\n- Improved `wgpu_hal` documentation. By @jimblandy in [#5516](https://github.com/gfx-rs/wgpu/pull/5516), [#5524](https://github.com/gfx-rs/wgpu/pull/5524), [#5562](https://github.com/gfx-rs/wgpu/pull/5562), [#5563](https://github.com/gfx-rs/wgpu/pull/5563), [#5566](https://github.com/gfx-rs/wgpu/pull/5566), [#5617](https://github.com/gfx-rs/wgpu/pull/5617), [#5618](https://github.com/gfx-rs/wgpu/pull/5618)\n- Add mention of primitive restart in the description of `PrimitiveState::strip_index_format`. By @cpsdqs in [#5350](https://github.com/gfx-rs/wgpu/pull/5350)\n- Document and tweak precise behaviour of `SourceLocation`. By @stefnotch in [#5386](https://github.com/gfx-rs/wgpu/pull/5386) and [#5410](https://github.com/gfx-rs/wgpu/pull/5410)\n- Give short example of WGSL `push_constant` syntax. By @waywardmonkeys in [#5393](https://github.com/gfx-rs/wgpu/pull/5393)\n- Fix incorrect documentation of `Limits::max_compute_workgroup_storage_size` default value. By @atlv24 in [#5601](https://github.com/gfx-rs/wgpu/pull/5601)\n\n### Bug Fixes\n\n#### General\n\n- Fix `serde` feature not compiling for `wgpu-types`. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149)\n- Fix the validation of vertex and index ranges. By @nical in [#5144](https://github.com/gfx-rs/wgpu/pull/5144) and [#5156](https://github.com/gfx-rs/wgpu/pull/5156)\n- Fix panic when creating a surface while no backend is available. By @wumpf [#5166](https://github.com/gfx-rs/wgpu/pull/5166)\n- Correctly compute minimum buffer size for array-typed `storage` and `uniform` vars. By @jimblandy [#5222](https://github.com/gfx-rs/wgpu/pull/5222)\n- Fix timeout when presenting a surface where no work has been done. By @waywardmonkeys in [#5200](https://github.com/gfx-rs/wgpu/pull/5200)\n- Fix registry leaks with de-duplicated resources. By @nical in [#5244](https://github.com/gfx-rs/wgpu/pull/5244)\n- Fix linking when targeting android. By @ashdnazg in [#5326](https://github.com/gfx-rs/wgpu/pull/5326).\n- Failing to set the device lost closure will call the closure before returning. By @bradwerth in [#5358](https://github.com/gfx-rs/wgpu/pull/5358).\n- Fix deadlocks caused by recursive read-write lock acquisitions [#5426](https://github.com/gfx-rs/wgpu/pull/5426).\n- Remove exposed C symbols (`extern \"C\"` + [no_mangle]) from RenderPass & ComputePass recording. By @wumpf in [#5409](https://github.com/gfx-rs/wgpu/pull/5409).\n- 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).\n\n#### naga\n\n- 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).\n- 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)\n- Add a limit for curly brace nesting in WGSL parsing, plus a note about stack size requirements. By @ErichDonGubler in [#5447](https://github.com/gfx-rs/wgpu/pull/5447).\n- In hlsl-out, fix accesses on zero value expressions by generating helper functions for `Expression::ZeroValue`. By @Imberflur in [#5587](https://github.com/gfx-rs/wgpu/pull/5587).\n- Fix behavior of `extractBits` and `insertBits` when `offset + count` overflows the bit width. By @cwfitzgerald in [#5305](https://github.com/gfx-rs/wgpu/pull/5305)\n- Fix behavior of integer `clamp` when `min` argument > `max` argument. By @cwfitzgerald in [#5300](https://github.com/gfx-rs/wgpu/pull/5300).\n- Fix `TypeInner::scalar_width` to be consistent with the rest of the codebase and return values in bytes not bits. By @atlv24 in [#5532](https://github.com/gfx-rs/wgpu/pull/5532).\n\n#### GLES / OpenGL\n\n- GLSL 410 does not support layout(binding = ...), enable only for GLSL 420. By @bes in [#5357](https://github.com/gfx-rs/wgpu/pull/5357)\n- Fixes for being able to use an OpenGL 4.1 core context provided by macOS with wgpu. By @bes in [#5331](https://github.com/gfx-rs/wgpu/pull/5331).\n- Fix crash when holding multiple devices on wayland/surfaceless. By @ashdnazg in [#5351](https://github.com/gfx-rs/wgpu/pull/5351).\n- Fix `first_instance` getting ignored in draw indexed when `ARB_shader_draw_parameters` feature is present and `base_vertex` is 0. By @valaphee in [#5482](https://github.com/gfx-rs/wgpu/pull/5482)\n\n#### Vulkan\n\n- Set object labels when the DEBUG flag is set, even if the VALIDATION flag is disabled. By @DJMcNab in [#5345](https://github.com/gfx-rs/wgpu/pull/5345).\n- Add safety check to `wgpu_hal::vulkan::CommandEncoder` to make sure `discard_encoding` is not called in the closed state. By @villuna in [#5557](https://github.com/gfx-rs/wgpu/pull/5557)\n- Fix SPIR-V type capability requests to not depend on `LocalType` caching. By @atlv24 in [#5590](https://github.com/gfx-rs/wgpu/pull/5590)\n- Upgrade `ash` to `0.38`. By @MarijnS95 in [#5504](https://github.com/gfx-rs/wgpu/pull/5504).\n\n#### Tests\n\n- Fix intermittent crashes on Linux in the `multithreaded_compute` test. By @jimblandy in [#5129](https://github.com/gfx-rs/wgpu/pull/5129).\n- Refactor tests to read feature flags by name instead of a hardcoded hexadecimal u64. By @atlv24 in [#5155](https://github.com/gfx-rs/wgpu/pull/5155).\n- Add test that verifies that we can drop the queue before using the device to create a command encoder. By @Davidster in [#5211](https://github.com/gfx-rs/wgpu/pull/5211)\n\n## 0.19.5 (2024-07-16)\n\nThis release only releases `wgpu-hal` 0.19.5, which contains an important fix\nfor DX12.\n\n### Bug Fixes\n\n#### DX12\n\n- Do not feed `&\"\"` to `D3DCompile`, by @workingjubilee in [#5812](https://github.com/gfx-rs/wgpu/issues/5812), backported by @Elabajaba in [#5833](https://github.com/gfx-rs/wgpu/pull/5833).\n\n## v0.19.4 (2024-04-17)\n\n### Bug Fixes\n\n#### General\n\n- Don't depend on bind group and bind group layout entry order in backends. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421).\n- Properly clean up all write_buffer/texture temporary resources. By @robtfm in [#5413](https://github.com/gfx-rs/wgpu/pull/5413).\n- Fix deadlock in certain situations when mapping buffers using `wgpu-profiler`. By @cwfitzgerald in [#5517](https://github.com/gfx-rs/wgpu/pull/5517)\n\n#### WebGPU\n\n- Correctly pass through timestamp queries to WebGPU. By @cwfitzgerald in [#5527](https://github.com/gfx-rs/wgpu/pull/5527).\n\n## v0.19.3 (2024-03-01)\n\nThis release includes `wgpu`, `wgpu-core`, and `wgpu-hal`. All other crates are unchanged.\n\n### Major Changes\n\n#### Vendored WebGPU Bindings from `web_sys`\n\n**`--cfg=web_sys_unstable_apis` is no longer needed in your `RUSTFLAGS` to compile for WebGPU!!!**\n\nWhile WebGPU's javascript api is stable in the browsers, the `web_sys` bindings for WebGPU are still improving. As such they are hidden behind the special cfg `--cfg=web_sys_unstable_apis` and are not available by default. Everyone who wanted to use our WebGPU backend needed to enable this cfg in their `RUSTFLAGS`. This was very inconvenient and made it hard to use WebGPU, especially when WebGPU is enabled by default. Additionally, the unstable APIs don't adhere to semver, so there were repeated breakages.\n\nTo combat this problem we have decided to vendor the `web_sys` bindings for WebGPU within the crate. Notably we are not forking the bindings, merely vendoring, so any improvements we make to the bindings will be contributed directly to upstream `web_sys`.\n\nBy @cwfitzgerald in [#5325](https://github.com/gfx-rs/wgpu/pull/5325).\n\n### Bug Fixes\n\n#### General\n\n- Fix an issue where command encoders weren't properly freed if an error occurred during command encoding. By @ErichDonGubler in [#5251](https://github.com/gfx-rs/wgpu/pull/5251).\n- Fix incorrect validation causing all indexed draws on render bundles to fail. By @wumpf in [#5430](https://github.com/gfx-rs/wgpu/pull/5340).\n\n#### Android\n\n- Fix linking error when targeting android without `winit`. By @ashdnazg in [#5326](https://github.com/gfx-rs/wgpu/pull/5326).\n\n## v0.19.2 (2024-02-29)\n\nThis release includes `wgpu`, `wgpu-core`, `wgpu-hal`, `wgpu-types`, and `naga`. All other crates are unchanged.\n\n### Added/New Features\n\n#### General\n\n- `wgpu::Id` now implements `PartialOrd`/`Ord` allowing it to be put in `BTreeMap`s. By @cwfitzgerald and @9291Sam in [#5176](https://github.com/gfx-rs/wgpu/pull/5176)\n\n#### OpenGL\n\n- Log an error when OpenGL texture format heuristics fail. By @PolyMeilex in [#5266](https://github.com/gfx-rs/wgpu/issues/5266)\n\n#### `wgsl-out`\n\n- Learned to generate acceleration structure types. By @JMS55 in [#5261](https://github.com/gfx-rs/wgpu/pull/5261)\n\n### Documentation\n\n- Fix link in `wgpu::Instance::create_surface` documentation. By @HexoKnight in [#5280](https://github.com/gfx-rs/wgpu/pull/5280).\n- Fix typo in `wgpu::CommandEncoder::clear_buffer` documentation. By @PWhiddy in [#5281](https://github.com/gfx-rs/wgpu/pull/5281).\n- `Surface` configuration incorrectly claimed that `wgpu::Instance::create_surface` was unsafe. By @hackaugusto in [#5265](https://github.com/gfx-rs/wgpu/pull/5265).\n\n### Bug Fixes\n\n#### General\n\n- Device lost callbacks are invoked when replaced and when global is dropped. By @bradwerth in [#5168](https://github.com/gfx-rs/wgpu/pull/5168)\n- Fix performance regression when allocating a large amount of resources of the same type. By @nical in [#5229](https://github.com/gfx-rs/wgpu/pull/5229)\n- Fix docs.rs wasm32 builds. By @cwfitzgerald in [#5310](https://github.com/gfx-rs/wgpu/pull/5310)\n- Improve error message when binding count limit hit. By @hackaugusto in [#5298](https://github.com/gfx-rs/wgpu/pull/5298)\n- Remove an unnecessary `clone` during GLSL shader ingestion. By @a1phyr in [#5118](https://github.com/gfx-rs/wgpu/pull/5118).\n- Fix missing validation for `Device::clear_buffer` where `offset + size > buffer.size` was not checked when `size` was omitted. By @ErichDonGubler in [#5282](https://github.com/gfx-rs/wgpu/pull/5282).\n\n#### DX12\n\n- Fix `panic!` when dropping `Instance` without `InstanceFlags::VALIDATION`. By @hakolao in [#5134](https://github.com/gfx-rs/wgpu/pull/5134)\n\n#### OpenGL\n\n- Fix internal format for the `Etc2Rgba8Unorm` format. By @andristarr in [#5178](https://github.com/gfx-rs/wgpu/pull/5178)\n- Try to load `libX11.so.6` in addition to `libX11.so` on linux. [#5307](https://github.com/gfx-rs/wgpu/pull/5307)\n- Make use of `GL_EXT_texture_shadow_lod` to support sampling a cube depth texture with an explicit LOD. By @cmrschwarz in #[5171](https://github.com/gfx-rs/wgpu/pull/5171).\n\n#### `glsl-in`\n\n- Fix code generation from nested loops. By @cwfitzgerald and @teoxoy in [#5311](https://github.com/gfx-rs/wgpu/pull/5311)\n\n## v0.19.1 (2024-01-22)\n\nThis release includes `wgpu` and `wgpu-hal`. The rest of the crates are unchanged since 0.19.0.\n\n### Bug Fixes\n\n#### DX12\n\n- Properly register all swapchain buffers to prevent error on surface present. By @dtzxporter in [#5091](https://github.com/gfx-rs/wgpu/pull/5091)\n- Check for extra null states when creating resources. By @nical in [#5096](https://github.com/gfx-rs/wgpu/pull/5096)\n- Fix depth-only and stencil-only views causing crashes. By @teoxoy in [#5100](https://github.com/gfx-rs/wgpu/pull/5100)\n\n#### OpenGL\n\n- In Surface::configure and Surface::present on Windows, fix the current GL context not being unset when releasing the lock that guards access to making the context current. This was causing other threads to panic when trying to make the context current. By @Imberflur in [#5087](https://github.com/gfx-rs/wgpu/pull/5087).\n\n#### WebGPU\n\n- Improve error message when compiling WebGPU backend on wasm without the `web_sys_unstable_apis` set. By @rukai in [#5104](https://github.com/gfx-rs/wgpu/pull/5104)\n\n### Documentation\n\n- Document Wayland specific behavior related to `SurfaceTexture::present`. By @i509VCB in [#5093](https://github.com/gfx-rs/wgpu/pull/5093).\n\n## v0.19.0 (2024-01-17)\n\nThis release includes:\n\n- `wgpu`\n- `wgpu-core`\n- `wgpu-hal`\n- `wgpu-types`\n- `wgpu-info`\n- `naga` (skipped from 0.14 to 0.19)\n- `naga-cli` (skipped from 0.14 to 0.19)\n- `d3d12` (skipped from 0.7 to 0.19)\n\n### Improved Multithreading through internal use of Reference Counting\n\nLarge refactoring of wgpu’s internals aiming at reducing lock contention, and providing better performance when using wgpu on multiple threads.\n\n[Check the blog post!](https://gfx-rs.github.io/2023/11/24/arcanization.html)\n\nBy @gents83 in [#3626](https://github.com/gfx-rs/wgpu/pull/3626) and thanks also to @jimblandy, @nical, @Wumpf, @Elabajaba & @cwfitzgerald\n\n### All Public Dependencies are Re-Exported\n\nAll of wgpu's public dependencies are now re-exported at the top level so that users don't need to take their own dependencies.\nThis includes:\n\n- wgpu-core\n- wgpu-hal\n- naga\n- raw_window_handle\n- web_sys\n\n### Feature Flag Changes\n\n#### WebGPU & WebGL in the same Binary\n\nEnabling `webgl` no longer removes the `webgpu` backend.\n\nInstead, there's a new (default enabled) `webgpu` feature that allows to explicitly opt-out of `webgpu` if so desired.\nIf both `webgl` & `webgpu` are enabled, `wgpu::Instance` decides upon creation whether to target wgpu-core/WebGL or WebGPU.\nThis means that adapter selection is not handled as with regular adapters, but still allows to decide at runtime whether\n`webgpu` or the `webgl` backend should be used using a single wasm binary.\nBy @wumpf in [#5044](https://github.com/gfx-rs/wgpu/pull/5044)\n\n#### `naga-ir` Dedicated Feature\n\nThe `naga-ir` feature has been added to allow you to add naga module shaders without guessing about what other features needed to be enabled to get access to it.\nBy @cwfitzgerald in [#5063](https://github.com/gfx-rs/wgpu/pull/5063).\n\n#### `expose-ids` Feature available unconditionally\n\nThis feature allowed you to call `global_id` on any wgpu opaque handle to get a unique hashable identity for the given resource. This is now available without the feature flag.\nBy @cwfitzgerald in [#4841](https://github.com/gfx-rs/wgpu/pull/4841).\n\n#### `dx12` and `metal` Backend Crate Features\n\nwgpu now exposes backend feature for the Direct3D 12 (`dx12`) and Metal (`metal`) backend. These are enabled by default, but don't do anything when not targeting the corresponding OS.\nBy @daxpedda in [#4815](https://github.com/gfx-rs/wgpu/pull/4815).\n\n### Direct3D 11 Backend Removal\n\nThis backend had no functionality, and with the recent support for GL on Desktop, which allows wgpu to run on older devices, there was no need to keep this backend.\nBy @valaphee in [#4828](https://github.com/gfx-rs/wgpu/pull/4828).\n\n### `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER` Environment Variable\n\nThis adds a way to allow a Vulkan driver which is non-compliant per `VK_KHR_driver_properties` to be enumerated. This is intended for testing new Vulkan drivers which are not Vulkan compliant yet.\nBy @i509VCB in [#4754](https://github.com/gfx-rs/wgpu/pull/4754).\n\n### `DeviceExt::create_texture_with_data` allows Mip-Major Data\n\nPreviously, `DeviceExt::create_texture_with_data` only allowed data to be provided in layer major order. There is now a `order` parameter which allows you to specify if the data is in layer major or mip major order.\n\n```diff\n    let tex = ctx.device.create_texture_with_data(\n        &queue,\n        &descriptor,\n+       wgpu::util::TextureDataOrder::LayerMajor,\n        src_data,\n    );\n```\n\nBy @cwfitzgerald in [#4780](https://github.com/gfx-rs/wgpu/pull/4780).\n\n### Safe & unified Surface Creation\n\nIt is now possible to safely create a `wgpu::Surface` with `wgpu::Instance::create_surface()` by letting `wgpu::Surface` hold a lifetime to `window`.\nPassing an owned value `window` to `Surface` will return a `wgpu::Surface<'static>`.\n\nAll possible safe variants (owned windows and web canvases) are grouped using `wgpu::SurfaceTarget`.\nConversion to `wgpu::SurfaceTarget` is automatic for any type implementing `raw-window-handle`'s `HasWindowHandle` & `HasDisplayHandle` traits, i.e. most window types.\nFor web canvas types this has to be done explicitly:\n\n```rust\nlet surface: wgpu::Surface<'static> = instance.create_surface(wgpu::SurfaceTarget::Canvas(my_canvas))?;\n```\n\nAll unsafe variants are now grouped under `wgpu::Instance::create_surface_unsafe` which takes the\n`wgpu::SurfaceTargetUnsafe` enum and always returns `wgpu::Surface<'static>`.\n\nIn order to create a `wgpu::Surface<'static>` without passing ownership of the window use\n`wgpu::SurfaceTargetUnsafe::from_window`:\n\n```rust\nlet surface = unsafe {\n  instance.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&my_window))?\n};\n```\n\nThe easiest way to make this code safe is to use shared ownership:\n\n```rust\nlet window: Arc<winit::Window>;\n// ...\nlet surface = instance.create_surface(window.clone())?;\n```\n\nAll platform specific surface creation using points have moved into `SurfaceTargetUnsafe` as well.\nFor example:\n\nSafety by @daxpedda in [#4597](https://github.com/gfx-rs/wgpu/pull/4597)\nUnification by @wumpf in [#4984](https://github.com/gfx-rs/wgpu/pull/4984)\n\n### Add partial Support for WGSL Abstract Types\n\nAbstract types make numeric literals easier to use, by\nautomatically converting literals and other constant expressions\nfrom abstract numeric types to concrete types when safe and\nnecessary. For example, to build a vector of floating-point\nnumbers, naga previously made you write:\n\n```rust\nvec3<f32>(1.0, 2.0, 3.0)\n```\n\nWith this change, you can now simply write:\n\n```rust\nvec3<f32>(1, 2, 3)\n```\n\nEven though the literals are abstract integers, naga recognizes\nthat it is safe and necessary to convert them to `f32` values in\norder to build the vector. You can also use abstract values as\ninitializers for global constants and global and local variables,\nlike this:\n\n```rust\nvar unit_x: vec2<f32> = vec2(1, 0);\n```\n\nThe literals `1` and `0` are abstract integers, and the expression\n`vec2(1, 0)` is an abstract vector. However, naga recognizes that\nit can convert that to the concrete type `vec2<f32>` to satisfy\nthe given type of `unit_x`.\nThe WGSL specification permits abstract integers and\nfloating-point values in almost all contexts, but naga's support\nfor this is still incomplete. Many WGSL operators and builtin\nfunctions are specified to produce abstract results when applied\nto abstract inputs, but for now naga simply concretizes them all\nbefore applying the operation. We will expand naga's abstract type\nsupport in subsequent pull requests.\nAs part of this work, the public types `naga::ScalarKind` and\n`naga::Literal` now have new variants, `AbstractInt` and `AbstractFloat`.\n\nBy @jimblandy in [#4743](https://github.com/gfx-rs/wgpu/pull/4743), [#4755](https://github.com/gfx-rs/wgpu/pull/4755).\n\n### `Instance::enumerate_adapters` now returns `Vec<Adapter>` instead of an `ExactSizeIterator`\n\nThis allows us to support WebGPU and WebGL in the same binary.\n\n```diff\n- let adapters: Vec<Adapter> = instance.enumerate_adapters(wgpu::Backends::all()).collect();\n+ let adapters: Vec<Adapter> = instance.enumerate_adapters(wgpu::Backends::all());\n```\n\nBy @wumpf in [#5044](https://github.com/gfx-rs/wgpu/pull/5044)\n\n### `device.poll()` now returns a `MaintainResult` instead of a `bool`\n\nThis is a forward looking change, as we plan to add more information to the `MaintainResult` in the future.\nThis enum has the same data as the boolean, but with some useful helper functions.\n\n```diff\n- let queue_finished: bool = device.poll(wgpu::Maintain::Wait);\n+ let queue_finished: bool = device.poll(wgpu::Maintain::Wait).is_queue_empty();\n```\n\nBy @cwfitzgerald in [#5053](https://github.com/gfx-rs/wgpu/pull/5053)\n\n### New Features\n\n#### General\n\n- Added `DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW` to know if `@builtin(vertex_index)` and `@builtin(instance_index)` will respect the `first_vertex` / `first_instance` in indirect calls. If this is not present, both will always start counting from 0. Currently enabled on all backends except DX12. By @cwfitzgerald in [#4722](https://github.com/gfx-rs/wgpu/pull/4722).\n- Added support for the `FLOAT32_FILTERABLE` feature (web and native, corresponds to WebGPU's `float32-filterable`). By @almarklein in [#4759](https://github.com/gfx-rs/wgpu/pull/4759).\n- GPU buffer memory is released during \"lose the device\". By @bradwerth in [#4851](https://github.com/gfx-rs/wgpu/pull/4851).\n- wgpu and wgpu-core cargo feature flags are now documented on docs.rs. By @wumpf in [#4886](https://github.com/gfx-rs/wgpu/pull/4886).\n- DeviceLostClosure is guaranteed to be invoked exactly once. By @bradwerth in [#4862](https://github.com/gfx-rs/wgpu/pull/4862).\n- Log vulkan validation layer messages during instance creation and destruction: By @exrook in [#4586](https://github.com/gfx-rs/wgpu/pull/4586).\n- `TextureFormat::block_size` is deprecated, use `TextureFormat::block_copy_size` instead: By @wumpf in [#4647](https://github.com/gfx-rs/wgpu/pull/4647).\n- Rename of `DispatchIndirect`, `DrawIndexedIndirect`, and `DrawIndirect` types in the `wgpu::util` module to `DispatchIndirectArgs`, `DrawIndexedIndirectArgs`, and `DrawIndirectArgs`. By @cwfitzgerald in [#4723](https://github.com/gfx-rs/wgpu/pull/4723).\n- Make the size parameter of `encoder.clear_buffer` an `Option<u64>` instead of `Option<NonZero<u64>>`. By @nical in [#4737](https://github.com/gfx-rs/wgpu/pull/4737).\n- Reduce the `info` log level noise. By @nical in [#4769](https://github.com/gfx-rs/wgpu/pull/4769), [#4711](https://github.com/gfx-rs/wgpu/pull/4711) and [#4772](https://github.com/gfx-rs/wgpu/pull/4772)\n- Rename `features` & `limits` fields of `DeviceDescriptor` to `required_features` & `required_limits`. By @teoxoy in [#4803](https://github.com/gfx-rs/wgpu/pull/4803).\n- `SurfaceConfiguration` now exposes `desired_maximum_frame_latency` which was previously hard-coded to 2. By setting it to 1 you can reduce latency under the risk of making GPU & CPU work sequential. Currently, on DX12 this affects the `MaximumFrameLatency`, on all other backends except OpenGL the size of the swapchain (on OpenGL this has no effect). By @emilk & @wumpf in [#4899](https://github.com/gfx-rs/wgpu/pull/4899)\n\n#### OpenGL\n\n- `@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).\n- Desktop GL now supports `POLYGON_MODE_LINE` and `POLYGON_MODE_POINT`. By @valaphee in [#4836](https://github.com/gfx-rs/wgpu/pull/4836).\n\n#### naga\n\n- 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).\n- 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).\n- 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).\n- Add `--bulk-validate` option to naga CLI. By @jimblandy in [#4871](https://github.com/gfx-rs/wgpu/pull/4871).\n- 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).\n- 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).\n- 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).\n- Add more metal keywords. By @fornwall in [#4707](https://github.com/gfx-rs/wgpu/pull/4707).\n- Add a new `naga::Literal` variant, `I64`, for signed 64-bit literals. [#4711](https://github.com/gfx-rs/wgpu/pull/4711).\n- Emit and init `struct` member padding always. By @ErichDonGubler in [#4701](https://github.com/gfx-rs/wgpu/pull/4701).\n- In WGSL output, always include the `i` suffix on `i32` literals. By @jimblandy in [#4863](https://github.com/gfx-rs/wgpu/pull/4863).\n- In WGSL output, always include the `f` suffix on `f32` literals. By @jimblandy in [#4869](https://github.com/gfx-rs/wgpu/pull/4869).\n\n### Bug Fixes\n\n#### General\n\n- `BufferMappedRange` trait is now `WasmNotSendSync`, i.e. it is `Send`/`Sync` if not on wasm or `fragile-send-sync-non-atomic-wasm` is enabled. By @wumpf in [#4818](https://github.com/gfx-rs/wgpu/pull/4818).\n- Align `wgpu_types::CompositeAlphaMode` serde serialization to spec. By @littledivy in [#4940](https://github.com/gfx-rs/wgpu/pull/4940).\n- Fix error message of `ConfigureSurfaceError::TooLarge`. By @Dinnerbone in [#4960](https://github.com/gfx-rs/wgpu/pull/4960).\n- Fix dropping of `DeviceLostCallbackC` params. By @bradwerth in [#5032](https://github.com/gfx-rs/wgpu/pull/5032).\n- Fixed a number of panics. By @nical in [#4999](https://github.com/gfx-rs/wgpu/pull/4999), [#5014](https://github.com/gfx-rs/wgpu/pull/5014), [#5024](https://github.com/gfx-rs/wgpu/pull/5024), [#5025](https://github.com/gfx-rs/wgpu/pull/5025), [#5026](https://github.com/gfx-rs/wgpu/pull/5026), [#5027](https://github.com/gfx-rs/wgpu/pull/5027), [#5028](https://github.com/gfx-rs/wgpu/pull/5028) and [#5042](https://github.com/gfx-rs/wgpu/pull/5042).\n- No longer validate surfaces against their allowed extent range on configure. This caused warnings that were almost impossible to avoid. As before, the resulting behavior depends on the compositor. By @wumpf in [#4796](https://github.com/gfx-rs/wgpu/pull/4796).\n\n#### DX12\n\n- Fixed D3D12_SUBRESOURCE_FOOTPRINT calculation for block compressed textures which caused a crash with `Queue::write_texture` on DX12. By @DTZxPorter in [#4990](https://github.com/gfx-rs/wgpu/pull/4990).\n\n#### Vulkan\n\n- Use `VK_EXT_robustness2` only when not using an outdated intel iGPU driver. By @TheoDulka in [#4602](https://github.com/gfx-rs/wgpu/pull/4602).\n\n#### WebGPU\n\n- Allow calling `BufferSlice::get_mapped_range` multiple times on the same buffer slice (instead of throwing a Javascript exception). By @DouglasDwyer in [#4726](https://github.com/gfx-rs/wgpu/pull/4726).\n\n#### WGL\n\n- 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)\n\n#### naga\n\n- 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).\n- Improve algorithm used by module compaction. By @jimblandy in [#4662](https://github.com/gfx-rs/wgpu/pull/4662).\n- 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).\n- 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).\n- Preserve the source spans for constants and expressions correctly across module compaction. By @jimblandy in [#4696](https://github.com/gfx-rs/wgpu/pull/4696).\n- Record the names of WGSL `alias` declarations in naga IR `Type`s. By @jimblandy in [#4733](https://github.com/gfx-rs/wgpu/pull/4733).\n\n#### Metal\n\n- Allow the `COPY_SRC` usage flag in surface configuration. By @Toqozz in [#4852](https://github.com/gfx-rs/wgpu/pull/4852).\n\n### Examples\n\n- remove winit dependency from hello-compute example. By @psvri in [#4699](https://github.com/gfx-rs/wgpu/pull/4699)\n- hello-compute example fix failure with `wgpu error: Validation Error` if arguments are missing. By @vilcans in [#4939](https://github.com/gfx-rs/wgpu/pull/4939).\n- Made the examples page not crash on Chrome on Android, and responsive to screen sizes. By @Dinnerbone in [#4958](https://github.com/gfx-rs/wgpu/pull/4958).\n\n## v0.18.2 (2023-12-06)\n\nThis release includes `naga` version 0.14.2. The crates `wgpu-core`, `wgpu-hal` are still at `0.18.1` and the crates `wgpu` and `wgpu-types` are still at `0.18.0`.\n\n### Bug Fixes\n\n#### naga\n\n- 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).\n\n## v0.18.1 (2023-11-15)\n\n(naga version 0.14.1)\n\n### Bug Fixes\n\n#### General\n\n- Fix panic in `Surface::configure` in debug builds. By @cwfitzgerald in [#4635](https://github.com/gfx-rs/wgpu/pull/4635)\n- Fix crash when all the following are true: By @teoxoy in #[#4642](https://github.com/gfx-rs/wgpu/pull/4642)\n  - Passing a naga module directly to `Device::create_shader_module`.\n  - `InstanceFlags::DEBUG` is enabled.\n\n#### DX12\n\n- Always use HLSL 2018 when using DXC to compile HLSL shaders. By @daxpedda in [#4629](https://github.com/gfx-rs/wgpu/pull/4629)\n\n#### Metal\n\n- In Metal Shading Language output, fix issue where local variables were sometimes using variable names from previous functions. By @DJMcNab in [#4594](https://github.com/gfx-rs/wgpu/pull/4594)\n\n## v0.18.0 (2023-10-25)\n\nFor naga changelogs at or before v0.14.0. See [naga's changelog](naga/CHANGELOG.md).\n\n### Desktop OpenGL 3.3+ Support on Windows\n\nWe now support OpenGL on Windows! This brings support for a vast majority of the hardware that used to be covered by our DX11 backend. As of this writing we support OpenGL 3.3+, though there are efforts to reduce that further.\n\nThis allows us to cover the last 12 years of Intel GPUs (starting with Ivy Bridge; aka 3xxx), and the last 16 years of AMD (starting with Terascale; aka HD 2000) / NVidia GPUs (starting with Tesla; aka GeForce 8xxx).\n\nBy @Zoxc in [#4248](https://github.com/gfx-rs/wgpu/pull/4248)\n\n### Timestamp Queries Supported on Metal and OpenGL\n\nTimestamp queries are now supported on both Metal and Desktop OpenGL. On Apple chips on Metal, they only support timestamp queries in command buffers or in the renderpass descriptor,\nthey do not support them inside a pass.\n\nMetal: By @Wumpf in [#4008](https://github.com/gfx-rs/wgpu/pull/4008)\nOpenGL: By @Zoxc in [#4267](https://github.com/gfx-rs/wgpu/pull/4267)\n\n### Render/Compute Pass Query Writes\n\nAddition of the `TimestampWrites` type to compute and render pass descriptors to allow profiling on tilers which do not support timestamps inside passes.\n\nAdded [an example](https://github.com/gfx-rs/wgpu/tree/trunk/examples/timestamp-queries) to demonstrate the various kinds of timestamps.\n\nAdditionally, metal now supports timestamp queries!\n\nBy @FL33TW00D & @wumpf in [#3636](https://github.com/gfx-rs/wgpu/pull/3636).\n\n### Occlusion Queries\n\nWe now support binary occlusion queries! This allows you to determine if any of the draw calls within the query drew any pixels.\n\nUse the new `occlusion_query_set` field on `RenderPassDescriptor` to give a query set that occlusion queries will write to.\n\n```diff\nlet mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n    // ...\n+   occlusion_query_set: Some(&my_occlusion_query_set),\n});\n```\n\nWithin the renderpass do the following to write the occlusion query results to the query set at the given index:\n\n```rust\nrpass.begin_occlusion_query(index);\nrpass.draw(...);\nrpass.draw(...);\nrpass.end_occlusion_query();\n```\n\nThese are binary occlusion queries, so the result will be either 0 or an unspecified non-zero value.\n\nBy @Valaphee in [#3402](https://github.com/gfx-rs/wgpu/pull/3402)\n\n### Shader Improvements\n\n```rust\n// WGSL constant expressions are now supported!\nconst BLAH: u32 = 1u + 1u;\n\n// `rgb10a2uint` and `bgra8unorm` can now be used as a storage image format.\nvar image: texture_storage_2d<rgb10a2uint, write>;\nvar image: texture_storage_2d<bgra8unorm, write>;\n\n// You can now use dual source blending!\nstruct FragmentOutput{\n    @location(0) source1: vec4<f32>,\n    @location(0) @second_blend_source source2: vec4<f32>,\n}\n\n// `modf`/`frexp` now return structures\nlet result = modf(1.5);\nresult.fract == 0.5;\nresult.whole == 1.0;\n\nlet result = frexp(1.5);\nresult.fract == 0.75;\nresult.exponent == 2i;\n\n// `modf`/`frexp` are currently disabled on GLSL and SPIR-V input.\n```\n\n### Shader Validation Improvements\n\n```rust\n// Cannot get pointer to a workgroup variable\nfn func(p: ptr<workgroup, u32>); // ERROR\n\n// Cannot create Inf/NaN through constant expressions\nconst INF: f32 = 3.40282347e+38 + 1.0; // ERROR\nconst NAN: f32 = 0.0 / 0.0; // ERROR\n\n// `outerProduct` function removed\n\n// Error on repeated or missing `@workgroup_size()`\n@workgroup_size(1) @workgroup_size(2) // ERROR\nfn compute_main() {}\n\n// Error on repeated attributes.\nfn fragment_main(@location(0) @location(0) location_0: f32) // ERROR\n```\n\n### RenderPass `StoreOp` is now Enumeration\n\n`wgpu::Operations::store` used to be an underdocumented boolean value,\ncausing misunderstandings of the effect of setting it to `false`.\n\nThe API now more closely resembles WebGPU which distinguishes between `store` and `discard`,\nsee [WebGPU spec on GPUStoreOp](https://gpuweb.github.io/gpuweb/#enumdef-gpustoreop).\n\n```diff\n// ...\ndepth_ops: Some(wgpu::Operations {\n    load: wgpu::LoadOp::Clear(1.0),\n-   store: false,\n+   store: wgpu::StoreOp::Discard,\n}),\n// ...\n```\n\nBy @wumpf in [#4147](https://github.com/gfx-rs/wgpu/pull/4147)\n\n### Instance Descriptor Settings\n\nThe instance descriptor grew two more fields: `flags` and `gles_minor_version`.\n\n`flags` allow you to toggle the underlying api validation layers, debug information about shaders and objects in capture programs, and the ability to discard labels\n\n`gles_minor_version` is a rather niche feature that allows you to force the GLES backend to use a specific minor version, this is useful to get ANGLE to enable more than GLES 3.0.\n\n```diff\nlet instance = wgpu::Instance::new(InstanceDescriptor {\n    ...\n+   flags: wgpu::InstanceFlags::default()\n+   gles_minor_version: wgpu::Gles3MinorVersion::Automatic,\n});\n```\n\n`gles_minor_version`: By @PJB3005 in [#3998](https://github.com/gfx-rs/wgpu/pull/3998)\n`flags`: By @nical in [#4230](https://github.com/gfx-rs/wgpu/pull/4230)\n\n### Many New Examples!\n\n- Added the following examples: By @JustAnotherCodemonkey in [#3885](https://github.com/gfx-rs/wgpu/pull/3885).\n  - [repeated-compute](https://github.com/gfx-rs/wgpu/tree/trunk/examples/repeated-compute)\n  - [storage-texture](https://github.com/gfx-rs/wgpu/tree/trunk/examples/storage-texture)\n  - [render-to-texture](https://github.com/gfx-rs/wgpu/tree/trunk/examples/render-to-texture)\n  - [uniform-values](https://github.com/gfx-rs/wgpu/tree/trunk/examples/uniform-values)\n  - [hello-workgroups](https://github.com/gfx-rs/wgpu/tree/trunk/examples/hello-workgroups)\n  - [hello-synchronization](https://github.com/gfx-rs/wgpu/tree/trunk/examples/hello-synchronization)\n\n### Revamped Testing Suite\n\nOur testing harness was completely revamped and now automatically runs against all gpus in the system, shows the expected status of every test, and is tolerant to flakes.\n\nAdditionally, we have filled out our CI to now run the latest versions of WARP and Mesa. This means we can test even more features on CI than before.\n\nBy @cwfitzgerald in [#3873](https://github.com/gfx-rs/wgpu/pull/3873)\n\n### The GLES backend is now optional on macOS\n\nThe `angle` feature flag has to be set for the GLES backend to be enabled on Windows & macOS.\n\nBy @teoxoy in [#4185](https://github.com/gfx-rs/wgpu/pull/4185)\n\n### Added/New Features\n\n- Re-export naga. By @exrook in [#4172](https://github.com/gfx-rs/wgpu/pull/4172)\n- Add WinUI 3 SwapChainPanel support. By @ddrboxman in [#4191](https://github.com/gfx-rs/wgpu/pull/4191)\n\n### Changes\n\n#### General\n\n- Omit texture store bound checks since they are no-ops if out of bounds on all APIs. By @teoxoy in [#3975](https://github.com/gfx-rs/wgpu/pull/3975)\n- Validate `DownlevelFlags::READ_ONLY_DEPTH_STENCIL`. By @teoxoy in [#4031](https://github.com/gfx-rs/wgpu/pull/4031)\n- Add validation in accordance with WebGPU `setViewport` valid usage for `x`, `y` and `this.[[attachment_size]]`. By @James2022-rgb in [#4058](https://github.com/gfx-rs/wgpu/pull/4058)\n- `wgpu::CreateSurfaceError` and `wgpu::RequestDeviceError` now give details of the failure, but no longer implement `PartialEq` and cannot be constructed. By @kpreid in [#4066](https://github.com/gfx-rs/wgpu/pull/4066) and [#4145](https://github.com/gfx-rs/wgpu/pull/4145)\n- Make `WGPU_POWER_PREF=none` a valid value. By @fornwall in [4076](https://github.com/gfx-rs/wgpu/pull/4076)\n- Support dual source blending in OpenGL ES, Metal, Vulkan & DX12. By @freqmod in [4022](https://github.com/gfx-rs/wgpu/pull/4022)\n- Add stub support for device destroy and device validity. By @bradwerth in [4163](https://github.com/gfx-rs/wgpu/pull/4163) and in [4212](https://github.com/gfx-rs/wgpu/pull/4212)\n- Add trace-level logging for most entry points in wgpu-core By @nical in [4183](https://github.com/gfx-rs/wgpu/pull/4183)\n- Add `Rgb10a2Uint` format. By @teoxoy in [4199](https://github.com/gfx-rs/wgpu/pull/4199)\n- Validate that resources are used on the right device. By @nical in [4207](https://github.com/gfx-rs/wgpu/pull/4207)\n- Expose instance flags.\n- Add support for the bgra8unorm-storage feature. By @jinleili and @nical in [#4228](https://github.com/gfx-rs/wgpu/pull/4228)\n- Calls to lost devices now return `DeviceError::Lost` instead of `DeviceError::Invalid`. By @bradwerth in [#4238]([https://github.com/gfx-rs/wgpu/pull/4238])\n- Let the `\"strict_asserts\"` feature enable check that wgpu-core's lock-ordering tokens are unique per thread. By @jimblandy in [#4258]([https://github.com/gfx-rs/wgpu/pull/4258])\n- Allow filtering labels out before they are passed to GPU drivers by @nical in [https://github.com/gfx-rs/wgpu/pull/4246](4246)\n- `DeviceLostClosure` callback mechanism provided so user agents can resolve `GPUDevice.lost` Promises at the appropriate time by @bradwerth in [#4645](https://github.com/gfx-rs/wgpu/pull/4645)\n\n#### Vulkan\n\n- Rename `wgpu_hal::vulkan::Instance::required_extensions` to `desired_extensions`. By @jimblandy in [#4115](https://github.com/gfx-rs/wgpu/pull/4115)\n- Don't bother calling `vkFreeCommandBuffers` when `vkDestroyCommandPool` will take care of that for us. By @jimblandy in [#4059](https://github.com/gfx-rs/wgpu/pull/4059)\n\n#### DX12\n\n- Bump `gpu-allocator` to 0.23. By @Elabajaba in [#4198](https://github.com/gfx-rs/wgpu/pull/4198)\n\n### Documentation\n\n- Use WGSL for VertexFormat example types. By @ScanMountGoat in [#4035](https://github.com/gfx-rs/wgpu/pull/4035)\n- Fix description of `Features::TEXTURE_COMPRESSION_ASTC_HDR` in [#4157](https://github.com/gfx-rs/wgpu/pull/4157)\n\n### Bug Fixes\n\n#### General\n\n- Derive storage bindings via `naga::StorageAccess` instead of `naga::GlobalUse`. By @teoxoy in [#3985](https://github.com/gfx-rs/wgpu/pull/3985).\n- `Queue::on_submitted_work_done` callbacks will now always be called after all previous `BufferSlice::map_async` callbacks, even when there are no active submissions. By @cwfitzgerald in [#4036](https://github.com/gfx-rs/wgpu/pull/4036).\n- Fix `clear` texture views being leaked when `wgpu::SurfaceTexture` is dropped before it is presented. By @rajveermalviya in [#4057](https://github.com/gfx-rs/wgpu/pull/4057).\n- Add `Feature::SHADER_UNUSED_VERTEX_OUTPUT` to allow unused vertex shader outputs. By @Aaron1011 in [#4116](https://github.com/gfx-rs/wgpu/pull/4116).\n- Fix a panic in `surface_configure`. By @nical in [#4220](https://github.com/gfx-rs/wgpu/pull/4220) and [#4227](https://github.com/gfx-rs/wgpu/pull/4227)\n- Pipelines register their implicit layouts in error cases. By @bradwerth in [#4624](https://github.com/gfx-rs/wgpu/pull/4624)\n- Better handle explicit destruction of textures and buffers. By @nical in [#4657](https://github.com/gfx-rs/wgpu/pull/4657)\n\n#### Vulkan\n\n- Fix enabling `wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY` not being actually enabled in vulkan backend. By @39ali in[#3772](https://github.com/gfx-rs/wgpu/pull/3772).\n- Don't pass `vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR` unless the `VK_KHR_portability_enumeration` extension is available. By @jimblandy in[#4038](https://github.com/gfx-rs/wgpu/pull/4038).\n- Enhancement of [#4038], using ash's definition instead of hard-coded c_str. By @hybcloud in[#4044](https://github.com/gfx-rs/wgpu/pull/4044).\n- Enable vulkan presentation on (Linux) Intel Mesa >= v21.2. By @flukejones in[#4110](https://github.com/gfx-rs/wgpu/pull/4110)\n\n#### DX12\n\n- DX12 doesn't support `Features::POLYGON_MODE_POINT``. By @teoxoy in [#4032](https://github.com/gfx-rs/wgpu/pull/4032).\n- Set `Features::VERTEX_WRITABLE_STORAGE` based on the right feature level. By @teoxoy in [#4033](https://github.com/gfx-rs/wgpu/pull/4033).\n\n#### Metal\n\n- Ensure that MTLCommandEncoder calls endEncoding before it is deallocated. By @bradwerth in [#4023](https://github.com/gfx-rs/wgpu/pull/4023)\n\n#### WebGPU\n\n- Ensure that limit requests and reporting is done correctly. By @OptimisticPeach in [#4107](https://github.com/gfx-rs/wgpu/pull/4107)\n- Validate usage of polygon mode. By @teoxoy in [#4196](https://github.com/gfx-rs/wgpu/pull/4196)\n\n#### GLES\n\n- enable/disable blending per attachment only when available (on ES 3.2 or higher). By @teoxoy in [#4234](https://github.com/gfx-rs/wgpu/pull/4234)\n\n### Documentation\n\n- Add an overview of `RenderPass` and how render state works. By @kpreid in [#4055](https://github.com/gfx-rs/wgpu/pull/4055)\n\n### Examples\n\n- Created `wgpu-example::utils` module to contain misc functions and such that are common code but aren't part of the example framework. Add to it the functions `output_image_wasm` and `output_image_native`, both for outputting `Vec<u8>` RGBA images either to the disc or the web page. By @JustAnotherCodemonkey in [#3885](https://github.com/gfx-rs/wgpu/pull/3885).\n- Removed `capture` example as it had issues (did not run on wasm) and has been replaced by `render-to-texture` (see above). By @JustAnotherCodemonkey in [#3885](https://github.com/gfx-rs/wgpu/pull/3885).\n\n## v0.17.2 (2023-10-03)\n\n### Bug Fixes\n\n#### Vulkan\n\n- Fix x11 hang while resizing on vulkan. @Azorlogh in [#4184](https://github.com/gfx-rs/wgpu/pull/4184).\n\n## v0.17.1 (2023-09-27)\n\n### Added/New Features\n\n- Add `get_mapped_range_as_array_buffer` for faster buffer read-backs in wasm builds. By @ryankaplan in [#4042] (https://github.com/gfx-rs/wgpu/pull/4042).\n\n### Bug Fixes\n\n#### DX12\n\n- Fix panic on resize when using DX12. By @cwfitzgerald in [#4106](https://github.com/gfx-rs/wgpu/pull/4106)\n\n#### Vulkan\n\n- Suppress validation error caused by OBS layer. This was also fixed upstream. By @cwfitzgerald in [#4002](https://github.com/gfx-rs/wgpu/pull/4002)\n- Work around bug in nvidia's vkCmdFillBuffer implementation. By @cwfitzgerald in [#4132](https://github.com/gfx-rs/wgpu/pull/4132).\n\n## v0.17.0 (2023-07-20)\n\nThis is the first release that featured `wgpu-info` as a binary crate for getting information about what devices wgpu sees in your system. It can dump the information in both human readable format and json.\n\n### Major Changes\n\nThis release was fairly minor as breaking changes go.\n\n#### `wgpu` types now `!Send` `!Sync` on wasm\n\nUp until this point, wgpu has made the assumption that threads do not exist on wasm. With the rise of libraries like [`wasm_thread`](https://crates.io/crates/wasm_thread) making it easier and easier to do wasm multithreading this assumption is no longer sound. As all wgpu objects contain references into the JS heap, they cannot leave the thread they started on.\n\nAs we understand that this change might be very inconvenient for users who don't care about wasm threading, there is a crate feature which re-enables the old behavior: `fragile-send-sync-non-atomic-wasm`. So long as you don't compile your code with `-Ctarget-feature=+atomics`, `Send` and `Sync` will be implemented again on wgpu types on wasm. As the name implies, especially for libraries, this is very fragile, as you don't know if a user will want to compile with atomics (and therefore threads) or not.\n\nBy @daxpedda in [#3691](https://github.com/gfx-rs/wgpu/pull/3691)\n\n#### Power Preference is now optional\n\nThe `power_preference` field of `RequestAdapterOptions` is now optional. If it is `PowerPreference::None`, we will choose the first available adapter, preferring GPU adapters over CPU adapters.\n\nBy @Aaron1011 in [#3903](https://github.com/gfx-rs/wgpu/pull/3903)\n\n#### `initialize_adapter_from_env` argument changes\n\nRemoved the backend_bits parameter from `initialize_adapter_from_env` and `initialize_adapter_from_env_or_default`. If you want to limit the backends used by this function, only enable the wanted backends in the instance.\n\nAdded a compatible surface parameter, to ensure the given device is able to be presented onto the given surface.\n\n```diff\n- wgpu::util::initialize_adapter_from_env(instance, backend_bits);\n+ wgpu::util::initialize_adapter_from_env(instance, Some(&compatible_surface));\n```\n\nBy @fornwall in [#3904](https://github.com/gfx-rs/wgpu/pull/3904) and [#3905](https://github.com/gfx-rs/wgpu/pull/3905)\n\n#### Misc Breaking Changes\n\n- Change `AdapterInfo::{device,vendor}` to be `u32` instead of `usize`. By @ameknite in [#3760](https://github.com/gfx-rs/wgpu/pull/3760)\n\n### Changes\n\n- Added support for importing external buffers using `buffer_from_raw` (Dx12, Metal, Vulkan) and `create_buffer_from_hal`. By @AdrianEddy in [#3355](https://github.com/gfx-rs/wgpu/pull/3355)\n\n#### Vulkan\n\n- Work around [Vulkan-ValidationLayers#5671](https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5671) by ignoring reports of violations of [VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01912](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01912). By @jimblandy in [#3809](https://github.com/gfx-rs/wgpu/pull/3809).\n\n### Added/New Features\n\n#### General\n\n- Empty scissor rects are allowed now, matching the specification. by @PJB3005 in [#3863](https://github.com/gfx-rs/wgpu/pull/3863).\n- Add back components info to `TextureFormat`s. By @teoxoy in [#3843](https://github.com/gfx-rs/wgpu/pull/3843).\n- Add `get_mapped_range_as_array_buffer` for faster buffer read-backs in wasm builds. By @ryankaplan in [#4042] (https://github.com/gfx-rs/wgpu/pull/4042).\n\n### Documentation\n\n- Better documentation for draw, draw_indexed, set_viewport and set_scissor_rect. By @genusistimelord in [#3860](https://github.com/gfx-rs/wgpu/pull/3860)\n- Fix link to `GPUVertexBufferLayout`. By @fornwall in [#3906](https://github.com/gfx-rs/wgpu/pull/3906)\n- Document feature requirements for `DEPTH32FLOAT_STENCIL8` by @ErichDonGubler in [#3734](https://github.com/gfx-rs/wgpu/pull/3734).\n- Flesh out docs. for `AdapterInfo::{device,vendor}` by @ErichDonGubler in [#3763](https://github.com/gfx-rs/wgpu/pull/3763).\n- Spell out which sizes are in bytes. By @jimblandy in [#3773](https://github.com/gfx-rs/wgpu/pull/3773).\n- Validate that `descriptor.usage` is not empty in `create_buffer` by @nical in [#3928](https://github.com/gfx-rs/wgpu/pull/3928)\n- Update `max_bindings_per_bind_group` limit to reflect spec changes by @ErichDonGubler and @nical in [#3943](https://github.com/gfx-rs/wgpu/pull/3943) [#3942](https://github.com/gfx-rs/wgpu/pull/3942)\n- Add better docs for `Limits`, listing the actual limits returned by `downlevel_defaults` and `downlevel_webgl2_defaults` by @JustAnotherCodemonkey in [#3988](https://github.com/gfx-rs/wgpu/pull/3988)\n\n### Bug Fixes\n\n#### General\n\n- Fix order of arguments to glPolygonOffset by @komadori in [#3783](https://github.com/gfx-rs/wgpu/pull/3783).\n- Fix OpenGL/EGL backend not respecting non-sRGB texture formats in `SurfaceConfiguration`. by @liquidev in [#3817](https://github.com/gfx-rs/wgpu/pull/3817)\n- Make write- and read-only marked buffers match non-readonly layouts. by @fornwall in [#3893](https://github.com/gfx-rs/wgpu/pull/3893)\n- Fix leaking X11 connections. by @wez in [#3924](https://github.com/gfx-rs/wgpu/pull/3924)\n- Fix ASTC feature selection in the webgl backend. by @expenses in [#3934](https://github.com/gfx-rs/wgpu/pull/3934)\n- Fix Multiview to disable validation of TextureViewDimension and ArrayLayerCount. By @MalekiRe in [#3779](https://github.com/gfx-rs/wgpu/pull/3779#issue-1713269437).\n\n#### Vulkan\n\n- Fix incorrect aspect in barriers when using emulated Stencil8 textures. By @cwfitzgerald in [#3833](https://github.com/gfx-rs/wgpu/pull/3833).\n- Implement depth-clip-control using depthClamp instead of VK_EXT_depth_clip_enable. By @AlbinBernhardssonARM [#3892](https://github.com/gfx-rs/wgpu/pull/3892).\n- Fix enabling `wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY` not being actually enabled in vulkan backend. By @39ali in[#3772](https://github.com/gfx-rs/wgpu/pull/3772).\n\n#### Metal\n\n- Fix renderpasses being used inside of renderpasses. By @cwfitzgerald in [#3828](https://github.com/gfx-rs/wgpu/pull/3828)\n- Support (simulated) visionOS. By @jinleili in [#3883](https://github.com/gfx-rs/wgpu/pull/3883)\n\n#### DX12\n\n- Disable suballocation on Intel Iris(R) Xe. By @xiaopengli89 in [#3668](https://github.com/gfx-rs/wgpu/pull/3668)\n- Change the `max_buffer_size` limit from `u64::MAX` to `i32::MAX`. By @nical in [#4020](https://github.com/gfx-rs/wgpu/pull/4020)\n\n#### WebGPU\n\n- Use `get_preferred_canvas_format()` to fill `formats` of `SurfaceCapabilities`. By @jinleili in [#3744](https://github.com/gfx-rs/wgpu/pull/3744)\n\n### Examples\n\n- Publish examples to wgpu.rs on updates to trunk branch instead of gecko. By @paul-hansen in [#3750](https://github.com/gfx-rs/wgpu/pull/3750)\n- Ignore the exception values generated by the winit resize event. By @jinleili in [#3916](https://github.com/gfx-rs/wgpu/pull/3916)\n\n## v0.16.3 (2023-07-19)\n\n### Changes\n\n#### General\n\n- Make the `Id` type that is exposed when using the `expose-ids` feature implement `Send` and `Sync` again. This was unintentionally changed by the v0.16.0 release and is now fixed.\n\n## v0.16.2 (2023-07-09)\n\n### Changes\n\n#### DX12\n\n- Increase the `max_storage_buffers_per_shader_stage` and `max_storage_textures_per_shader_stage` limits based on what the hardware supports. by @Elabajaba in [#3798]https://github.com/gfx-rs/wgpu/pull/3798\n\n## v0.16.1 (2023-05-24)\n\n### Bug Fixes\n\n- Fix missing 4X MSAA support on some OpenGL backends. By @emilk in [#3780](https://github.com/gfx-rs/wgpu/pull/3780)\n\n#### General\n\n- Fix crash on dropping `wgpu::CommandBuffer`. By @wumpf in [#3726](https://github.com/gfx-rs/wgpu/pull/3726).\n- Use `u32`s internally for bind group indices, rather than `u8`. By @ErichDonGubler in [#3743](https://github.com/gfx-rs/wgpu/pull/3743).\n\n#### WebGPU\n\n- Fix crash when calling `create_surface_from_canvas`. By @grovesNL in [#3718](https://github.com/gfx-rs/wgpu/pull/3718)\n\n## v0.16.0 (2023-04-19)\n\n### Major changes\n\n#### Shader Changes\n\n`type` has been replaced with `alias` to match with upstream WebGPU.\n\n```diff\n- type MyType = vec4<u32>;\n+ alias MyType = vec4<u32>;\n```\n\n#### TextureFormat info API\n\nThe `TextureFormat::describe` function was removed in favor of separate functions: `block_dimensions`, `is_compressed`, `is_srgb`, `required_features`, `guaranteed_format_features`, `sample_type` and `block_size`.\n\n```diff\n- let block_dimensions = format.describe().block_dimensions;\n+ let block_dimensions = format.block_dimensions();\n- let is_compressed = format.describe().is_compressed();\n+ let is_compressed = format.is_compressed();\n- let is_srgb = format.describe().srgb;\n+ let is_srgb = format.is_srgb();\n- let required_features = format.describe().required_features;\n+ let required_features = format.required_features();\n```\n\nAdditionally `guaranteed_format_features` now takes a set of features to assume are enabled.\n\n```diff\n- let guaranteed_format_features = format.describe().guaranteed_format_features;\n+ let guaranteed_format_features = format.guaranteed_format_features(device.features());\n```\n\nAdditionally `sample_type` and `block_size` now take an optional `TextureAspect` and return `Option`s.\n\n```diff\n- let sample_type = format.describe().sample_type;\n+ let sample_type = format.sample_type(None).expect(\"combined depth-stencil format requires specifying a TextureAspect\");\n- let block_size = format.describe().block_size;\n+ let block_size = format.block_size(None).expect(\"combined depth-stencil format requires specifying a TextureAspect\");\n```\n\nBy @teoxoy in [#3436](https://github.com/gfx-rs/wgpu/pull/3436)\n\n#### BufferUsages::QUERY_RESOLVE\n\nBuffers used as the `destination` argument of `CommandEncoder::resolve_query_set` now have to contain the `QUERY_RESOLVE` usage instead of the `COPY_DST` usage.\n\n```diff\n  let destination = device.create_buffer(&wgpu::BufferDescriptor {\n      // ...\n-     usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n+     usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::MAP_READ,\n      mapped_at_creation: false,\n  });\n  command_encoder.resolve_query_set(&query_set, query_range, &destination, destination_offset);\n```\n\nBy @JolifantoBambla in [#3489](https://github.com/gfx-rs/wgpu/pull/3489)\n\n#### Renamed features\n\nThe following `Features` have been renamed.\n\n- `SHADER_FLOAT16` -> `SHADER_F16`\n- `SHADER_FLOAT64` -> `SHADER_F64`\n- `SHADER_INT16` -> `SHADER_I16`\n- `TEXTURE_COMPRESSION_ASTC_LDR` -> `TEXTURE_COMPRESSION_ASTC`\n- `WRITE_TIMESTAMP_INSIDE_PASSES` -> `TIMESTAMP_QUERY_INSIDE_PASSES`\n\nBy @teoxoy in [#3534](https://github.com/gfx-rs/wgpu/pull/3534)\n\n#### Anisotropic Filtering\n\nAnisotropic filtering has been brought in line with the spec. The anisotropic clamp is now a `u16` (was a `Option<u8>`) which must be at least 1.\n\nIf the anisotropy clamp is not 1, all the filters in a sampler must be `Linear`.\n\n```diff\nSamplerDescriptor {\n-    anisotropic_clamp: None,\n+    anisotropic_clamp: 1,\n}\n```\n\nBy @cwfitzgerald in [#3610](https://github.com/gfx-rs/wgpu/pull/3610).\n\n#### TextureFormat Names\n\nSome texture format names have changed to get back in line with the spec.\n\n```diff\n- TextureFormat::Bc6hRgbSfloat\n+ TextureFormat::Bc6hRgbFloat\n```\n\nBy @cwfitzgerald in [#3671](https://github.com/gfx-rs/wgpu/pull/3671).\n\n#### Misc Breaking Changes\n\n- Change type of `mip_level_count` and `array_layer_count` (members of `TextureViewDescriptor` and `ImageSubresourceRange`) from `Option<NonZeroU32>` to `Option<u32>`. By @teoxoy in [#3445](https://github.com/gfx-rs/wgpu/pull/3445)\n- Change type of `bytes_per_row` and `rows_per_image` (members of `ImageDataLayout`) from `Option<NonZeroU32>` to `Option<u32>`. By @teoxoy in [#3529](https://github.com/gfx-rs/wgpu/pull/3529)\n- On Web, `Instance::create_surface_from_canvas()` and `create_surface_from_offscreen_canvas()` now take the canvas by value. By @daxpedda in [#3690](https://github.com/gfx-rs/wgpu/pull/3690)\n\n### Added/New Features\n\n#### General\n\n- Added feature flags for ray-tracing (currently only hal): `RAY_QUERY` and `RAY_TRACING` @daniel-keitel (started by @expenses) in [#3507](https://github.com/gfx-rs/wgpu/pull/3507)\n\n#### Vulkan\n\n- Implemented basic ray-tracing api for acceleration structures, and ray-queries @daniel-keitel (started by @expenses) in [#3507](https://github.com/gfx-rs/wgpu/pull/3507)\n\n#### Hal\n\n- Added basic ray-tracing api for acceleration structures, and ray-queries @daniel-keitel (started by @expenses) in [#3507](https://github.com/gfx-rs/wgpu/pull/3507)\n\n### Changes\n\n#### General\n\n- Added `TextureFormatFeatureFlags::MULTISAMPLE_X16`. By @Dinnerbone in [#3454](https://github.com/gfx-rs/wgpu/pull/3454)\n- Added `BufferUsages::QUERY_RESOLVE`. By @JolifantoBambla in [#3489](https://github.com/gfx-rs/wgpu/pull/3489)\n- Support stencil-only views and copying to/from combined depth-stencil textures. By @teoxoy in [#3436](https://github.com/gfx-rs/wgpu/pull/3436)\n- Added `Features::SHADER_EARLY_DEPTH_TEST`. By @teoxoy in [#3494](https://github.com/gfx-rs/wgpu/pull/3494)\n- All `fxhash` dependencies have been replaced with `rustc-hash`. By @james7132 in [#3502](https://github.com/gfx-rs/wgpu/pull/3502)\n- Allow copying of textures with copy-compatible formats. By @teoxoy in [#3528](https://github.com/gfx-rs/wgpu/pull/3528)\n- Improve attachment related errors. By @cwfitzgerald in [#3549](https://github.com/gfx-rs/wgpu/pull/3549)\n- Make error descriptions all upper case. By @cwfitzgerald in [#3549](https://github.com/gfx-rs/wgpu/pull/3549)\n- Don't include ANSI terminal color escape sequences in shader module validation error messages. By @jimblandy in [#3591](https://github.com/gfx-rs/wgpu/pull/3591)\n- Report error messages from DXC compile. By @Davidster in [#3632](https://github.com/gfx-rs/wgpu/pull/3632)\n- Error in native when using a filterable `TextureSampleType::Float` on a multisample `BindingType::Texture`. By @mockersf in [#3686](https://github.com/gfx-rs/wgpu/pull/3686)\n- On Web, the size of the canvas is adjusted when using `Surface::configure()`. If the canvas was given an explicit size (via CSS), this will not affect the visual size of the canvas. By @daxpedda in [#3690](https://github.com/gfx-rs/wgpu/pull/3690)\n- Added `Global::create_render_bundle_error`. By @jimblandy in [#3746](https://github.com/gfx-rs/wgpu/pull/3746)\n\n#### WebGPU\n\n- Implement the new checks for readonly stencils. By @JCapucho in [#3443](https://github.com/gfx-rs/wgpu/pull/3443)\n- Reimplement `adapter|device_features`. By @jinleili in [#3428](https://github.com/gfx-rs/wgpu/pull/3428)\n- Implement `command_encoder_resolve_query_set`. By @JolifantoBambla in [#3489](https://github.com/gfx-rs/wgpu/pull/3489)\n- Add support for `Features::RG11B10UFLOAT_RENDERABLE`. By @mockersf in [#3689](https://github.com/gfx-rs/wgpu/pull/3689)\n\n#### Vulkan\n\n- Set `max_memory_allocation_size` via `PhysicalDeviceMaintenance3Properties`. By @jinleili in [#3567](https://github.com/gfx-rs/wgpu/pull/3567)\n- Silence false-positive validation error about surface resizing. By @seabassjh in [#3627](https://github.com/gfx-rs/wgpu/pull/3627)\n\n### Bug Fixes\n\n#### General\n\n- `copyTextureToTexture` src/dst aspects must both refer to all aspects of src/dst format. By @teoxoy in [#3431](https://github.com/gfx-rs/wgpu/pull/3431)\n- Validate before extracting texture selectors. By @teoxoy in [#3487](https://github.com/gfx-rs/wgpu/pull/3487)\n- Fix fatal errors (those which panic even if an error handler is set) not including all of the details. By @kpreid in [#3563](https://github.com/gfx-rs/wgpu/pull/3563)\n- Validate shader location clashes. By @emilk in [#3613](https://github.com/gfx-rs/wgpu/pull/3613)\n- Fix surfaces not being dropped until exit. By @benjaminschaaf in [#3647](https://github.com/gfx-rs/wgpu/pull/3647)\n\n#### WebGPU\n\n- Fix handling of `None` values for `depth_ops` and `stencil_ops` in `RenderPassDescriptor::depth_stencil_attachment`. By @niklaskorz in [#3660](https://github.com/gfx-rs/wgpu/pull/3660)\n- Avoid using `WasmAbi` functions for WebGPU backend. By @grovesNL in [#3657](https://github.com/gfx-rs/wgpu/pull/3657)\n\n#### DX12\n\n- Use typeless formats for textures that might be viewed as srgb or non-srgb. By @teoxoy in [#3555](https://github.com/gfx-rs/wgpu/pull/3555)\n\n#### GLES\n\n- Set FORCE_POINT_SIZE if it is vertex shader with mesh consist of point list. By @REASY in [3440](https://github.com/gfx-rs/wgpu/pull/3440)\n- Remove unwraps inside `surface.configure`. By @cwfitzgerald in [#3585](https://github.com/gfx-rs/wgpu/pull/3585)\n- Fix `copy_external_image_to_texture`, `copy_texture_to_texture` and `copy_buffer_to_texture` not taking the specified index into account if the target texture is a cube map, 2D texture array or cube map array. By @daxpedda [#3641](https://github.com/gfx-rs/wgpu/pull/3641)\n- Fix disabling of vertex attributes with non-consecutive locations. By @Azorlogh in [#3706](https://github.com/gfx-rs/wgpu/pull/3706)\n\n#### Metal\n\n- Fix metal erroring on an `array_stride` of 0. By @teoxoy in [#3538](https://github.com/gfx-rs/wgpu/pull/3538)\n- `create_texture` returns an error if `new_texture` returns NULL. By @jinleili in [#3554](https://github.com/gfx-rs/wgpu/pull/3554)\n- Fix shader bounds checking being ignored. By @FL33TW00D in [#3603](https://github.com/gfx-rs/wgpu/pull/3603)\n\n#### Vulkan\n\n- Treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android due to rotation issues. By @James2022-rgb in [#3525](https://github.com/gfx-rs/wgpu/pull/3525)\n\n### Examples\n\n- Use `BufferUsages::QUERY_RESOLVE` instead of `BufferUsages::COPY_DST` for buffers used in `CommandEncoder::resolve_query_set` calls in `mipmap` example. By @JolifantoBambla in [#3489](https://github.com/gfx-rs/wgpu/pull/3489)\n\n## v0.15.3 (2023-03-22)\n\n### Bug Fixes\n\n#### Metal\n\n- Fix incorrect mipmap being sampled when using `MinLod <= 0.0` and `MaxLod >= 32.0` or when the fragment shader samples different Lods in the same quad. By @cwfitzgerald in [#3610](https://github.com/gfx-rs/wgpu/pull/3610).\n\n#### GLES\n\n- Fix `Vertex buffer is not big enough for the draw call.` for ANGLE/Web when rendering with instance attributes on a single instance. By @wumpf in [#3596](https://github.com/gfx-rs/wgpu/pull/3596)\n- Reset all queue state between command buffers in a submit. By @jleibs [#3589](https://github.com/gfx-rs/wgpu/pull/3589)\n- Reset the state of `SAMPLE_ALPHA_TO_COVERAGE` on queue reset. By @jleibs [#3589](https://github.com/gfx-rs/wgpu/pull/3589)\n\n## wgpu-0.15.2 (2023-03-08)\n\n### Bug Fixes\n\n#### Metal\n\n- Fix definition of `NSOperatingSystemVersion` to avoid potential crashes. By @grovesNL in [#3557](https://github.com/gfx-rs/wgpu/pull/3557)\n\n#### GLES\n\n- Enable `WEBGL_debug_renderer_info` before querying unmasked vendor/renderer to avoid crashing on emscripten in [#3519](https://github.com/gfx-rs/wgpu/pull/3519)\n\n## wgpu-0.15.1 (2023-02-09)\n\n### Changes\n\n#### General\n\n- Fix for some minor issues in comments on some features. By @Wumpf in [#3455](https://github.com/gfx-rs/wgpu/pull/3455)\n\n#### Vulkan\n\n- Improve format MSAA capabilities detection. By @jinleili in [#3429](https://github.com/gfx-rs/wgpu/pull/3429)\n\n#### DX12\n\n- Update gpu allocator to 0.22. By @Elabajaba in [#3447](https://github.com/gfx-rs/wgpu/pull/3447)\n\n#### WebGPU\n\n- Implement `CommandEncoder::clear_buffer`. By @raphlinus in [#3426](https://github.com/gfx-rs/wgpu/pull/3426)\n\n### Bug Fixes\n\n#### General\n\n- Re-sort supported surface formats based on srgb-ness. By @cwfitzgerald in [#3444](https://github.com/gfx-rs/wgpu/pull/3444)\n\n#### Vulkan\n\n- Fix surface view formats validation error. By @jinleili in [#3432](https://github.com/gfx-rs/wgpu/pull/3432)\n\n#### DX12\n\n- Fix DXC validation issues when using a custom `dxil_path`. By @Elabajaba in [#3434](https://github.com/gfx-rs/wgpu/pull/3434)\n\n#### GLES\n\n- Unbind vertex buffers at end of renderpass. By @cwfitzgerald in [#3459](https://github.com/gfx-rs/wgpu/pull/3459)\n\n#### WebGPU\n\n- Reimplement `{adapter|device}_features`. By @jinleili in [#3428](https://github.com/gfx-rs/wgpu/pull/3428)\n\n### Documentation\n\n#### General\n\n- Build for Wasm on docs.rs. By @daxpedda in [#3462](https://github.com/gfx-rs/wgpu/pull/3428)\n\n## wgpu-0.15.0 (2023-01-25)\n\n### Major Changes\n\n#### WGSL Top-Level `let` is now `const`\n\nAll top level constants are now declared with `const`, catching up with the wgsl spec.\n\n`let` is no longer allowed at the global scope, only within functions.\n\n```diff\n-let SOME_CONSTANT = 12.0;\n+const SOME_CONSTANT = 12.0;\n```\n\nSee https://github.com/gfx-rs/naga/blob/master/CHANGELOG.md#v011-2023-01-25 for smaller shader improvements.\n\n#### Surface Capabilities API\n\nThe various surface capability functions were combined into a single call that gives you all the capabilities.\n\n```diff\n- let formats = surface.get_supported_formats(&adapter);\n- let present_modes = surface.get_supported_present_modes(&adapter);\n- let alpha_modes = surface.get_supported_alpha_modes(&adapter);\n+ let caps = surface.get_capabilities(&adapter);\n+ let formats = caps.formats;\n+ let present_modes = caps.present_modes;\n+ let alpha_modes = caps.alpha_modes;\n```\n\nAdditionally `Surface::get_default_config` now returns an Option and returns None if the surface isn't supported by the adapter.\n\n```diff\n- let config = surface.get_default_config(&adapter);\n+ let config = surface.get_default_config(&adapter).expect(\"Surface unsupported by adapter\");\n```\n\n#### Fallible surface creation\n\n`Instance::create_surface()` now returns `Result<Surface, CreateSurfaceError>` instead of `Surface`. This allows an error to be returned if the given window is a HTML canvas and obtaining a WebGPU or WebGL 2 context fails. (No other platforms currently report any errors through this path.) By @kpreid in [#3052](https://github.com/gfx-rs/wgpu/pull/3052/)\n\n#### `Queue::copy_external_image_to_texture` on WebAssembly\n\nA new api, `Queue::copy_external_image_to_texture`, allows you to create wgpu textures from various web image primitives. Specifically from `HtmlVideoElement`, `HtmlCanvasElement`, `OffscreenCanvas`, and `ImageBitmap`. This provides multiple low-copy ways of interacting with the browser. WebGL is also supported, though WebGL has some additional restrictions, represented by the `UNRESTRICTED_EXTERNAL_IMAGE_COPIES` downlevel flag. By @cwfitzgerald in [#3288](https://github.com/gfx-rs/wgpu/pull/3288)\n\n#### Instance creation now takes `InstanceDescriptor` instead of `Backends`\n\n`Instance::new()` and `hub::Global::new()` now take an `InstanceDescriptor` struct which contains both the existing `Backends` selection as well as a new `Dx12Compiler` field for selecting which Dx12 shader compiler to use.\n\n```diff\n- let instance = Instance::new(wgpu::Backends::all());\n+ let instance = Instance::new(wgpu::InstanceDescriptor {\n+     backends: wgpu::Backends::all(),\n+     dx12_shader_compiler: wgpu::Dx12Compiler::Fxc,\n+ });\n```\n\n`Instance` now also also implements `Default`, which uses `wgpu::Backends::all()` and `wgpu::Dx12Compiler::Fxc` for `InstanceDescriptor`\n\n```diff\n- let instance = Instance::new(wgpu::InstanceDescriptor {\n-     backends: wgpu::Backends::all(),\n-     dx12_shader_compiler: wgpu::Dx12Compiler::Fxc,\n- });\n+ let instance = Instance::default();\n```\n\nBy @Elabajaba in [#3356](https://github.com/gfx-rs/wgpu/pull/3356)\n\n#### Texture Format Reinterpretation\n\nThe new `view_formats` field in the `TextureDescriptor` is used to specify a list of formats the texture can be re-interpreted to in a texture view. Currently only changing srgb-ness is allowed (ex. `Rgba8Unorm` <=> `Rgba8UnormSrgb`).\n\n```diff\nlet texture = device.create_texture(&wgpu::TextureDescriptor {\n  // ...\n  format: TextureFormat::Rgba8UnormSrgb,\n+ view_formats: &[TextureFormat::Rgba8Unorm],\n});\n```\n\n```diff\nlet config = wgpu::SurfaceConfiguration {\n  // ...\n  format: TextureFormat::Rgba8Unorm,\n+ view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb],\n};\nsurface.configure(&device, &config);\n```\n\n#### MSAA x2 and x8 Support\n\nVia the `TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` feature, MSAA x2 and x8 are now supported on textures. To query for x2 or x8 support, enable the feature and look at the texture format flags for the texture format of your choice.\n\nBy @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140)\n\n#### DXC Shader Compiler Support for DX12\n\nYou can now choose to use the DXC compiler for DX12 instead of FXC. The DXC compiler is faster, less buggy, and allows for new features compared to the old, unmaintained FXC compiler.\n\nYou can choose which compiler to use at `Instance` creation using the `dx12_shader_compiler` field in the `InstanceDescriptor` struct. Note that DXC requires both `dxcompiler.dll` and `dxil.dll`, which can be downloaded from https://github.com/microsoft/DirectXShaderCompiler/releases. Both .dlls need to be shipped with your application when targeting DX12 and using the `DXC` compiler. If the .dlls can't be loaded, then it will fall back to the FXC compiler. By @39ali and @Elabajaba in [#3356](https://github.com/gfx-rs/wgpu/pull/3356)\n\n#### Suballocate DX12 buffers and textures\n\nThe DX12 backend can now suballocate buffers and textures from larger chunks of memory, which can give a significant increase in performance (in testing a 100x improvement has been seen in a simple scene with 200 `write_buffer` calls per frame, and a 1.4x improvement in [Bistro using Bevy](https://github.com/vleue/bevy_bistro_playground)).\n\nPreviously `wgpu-hal`'s DX12 backend created a new heap on the GPU every time you called `write_buffer` (by calling `CreateCommittedResource`), whereas now it uses [`gpu_allocator`](https://crates.io/crates/gpu-allocator) to manage GPU memory (and calls `CreatePlacedResource` with a suballocated heap). By @Elabajaba in [#3163](https://github.com/gfx-rs/wgpu/pull/3163)\n\n#### Backend selection by features in wgpu-core\n\nWhereas `wgpu-core` used to automatically select backends to enable\nbased on the target OS and architecture, it now has separate features\nto enable each backend:\n\n- \"metal\", for the Metal API on macOS and iOS\n- \"vulkan\", for the Vulkan API (Linux, some Android, and occasionally Windows)\n- \"dx12\", for Microsoft's Direct3D 12 API\n- \"gles\", OpenGL ES, available on many systems\n- \"dx11\", for Microsoft's Direct3D 11 API\n\nNone are enabled by default, but the `wgpu` crate automatically\nselects these features based on the target operating system and\narchitecture, using the same rules that `wgpu-core` used to, so users\nof `wgpu` should be unaffected by this change. However, other crates\nusing `wgpu-core` directly will need to copy `wgpu`'s logic or write\ntheir own. See the `[target]` section of `wgpu/Cargo.toml` for\ndetails.\n\nSimilarly, `wgpu-core` now has `emscripten` and `renderdoc` features\nthat `wgpu` enables on appropriate platforms.\n\nIn previous releases, the `wgpu-core` crate decided which backends to\nsupport. However, this left `wgpu-core`'s users with no way to\noverride those choices. (Firefox doesn't want the GLES back end, for\nexample.) There doesn't seem to be any way to have a crate select\nbackends based on target OS and architecture that users of that crate\ncan still override. Default features can't be selected based on the\ntarget, for example. That implies that we should do the selection as\nlate in the dependency DAG as feasible. Having `wgpu` (and\n`wgpu-core`'s other dependents) choose backends seems like the best\noption.\n\nBy @jimblandy in [#3254](https://github.com/gfx-rs/wgpu/pull/3254).\n\n### Changes\n\n#### General\n\n- Convert all `Default` Implementations on Enums to `derive(Default)`\n- Implement `Default` for `CompositeAlphaMode`\n- New downlevel feature `UNRESTRICTED_INDEX_BUFFER` to indicate support for using `INDEX` together with other non-copy/map usages (unsupported on WebGL). By @Wumpf in [#3157](https://github.com/gfx-rs/wgpu/pull/3157)\n- Add missing `DEPTH_BIAS_CLAMP` and `FULL_DRAW_INDEX_UINT32` downlevel flags. By @teoxoy in [#3316](https://github.com/gfx-rs/wgpu/pull/3316)\n- Combine `Surface::get_supported_formats`, `Surface::get_supported_present_modes`, and `Surface::get_supported_alpha_modes` into `Surface::get_capabilities` and `SurfaceCapabilities`. By @cwfitzgerald in [#3157](https://github.com/gfx-rs/wgpu/pull/3157)\n- Make `Surface::get_default_config` return an Option to prevent panics. By @cwfitzgerald in [#3157](https://github.com/gfx-rs/wgpu/pull/3157)\n- Lower the `max_buffer_size` limit value for compatibility with Apple2 and WebGPU compliance. By @jinleili in [#3255](https://github.com/gfx-rs/wgpu/pull/3255)\n- Limits `min_uniform_buffer_offset_alignment` and `min_storage_buffer_offset_alignment` is now always at least 32. By @wumpf [#3262](https://github.com/gfx-rs/wgpu/pull/3262)\n- Dereferencing a buffer view is now marked inline. By @Wumpf in [#3307](https://github.com/gfx-rs/wgpu/pull/3307)\n- The `strict_assert` family of macros was moved to `wgpu-types`. By @i509VCB in [#3051](https://github.com/gfx-rs/wgpu/pull/3051)\n- Make `ObjectId` structure and invariants idiomatic. By @teoxoy in [#3347](https://github.com/gfx-rs/wgpu/pull/3347)\n- Add validation in accordance with WebGPU `GPUSamplerDescriptor` valid usage for `lodMinClamp` and `lodMaxClamp`. By @James2022-rgb in [#3353](https://github.com/gfx-rs/wgpu/pull/3353)\n- Remove panics in `Deref` implementations for `QueueWriteBufferView` and `BufferViewMut`. Instead, warnings are logged, since reading from these types is not recommended. By @botahamec in [#3336]\n- Implement `view_formats` in the TextureDescriptor to match the WebGPU spec. By @jinleili in [#3237](https://github.com/gfx-rs/wgpu/pull/3237)\n- Show more information in error message for non-aligned buffer bindings in WebGL [#3414](https://github.com/gfx-rs/wgpu/pull/3414)\n- Update `TextureView` validation according to the WebGPU spec. By @teoxoy in [#3410](https://github.com/gfx-rs/wgpu/pull/3410)\n- Implement `view_formats` in the SurfaceConfiguration to match the WebGPU spec. By @jinleili in [#3409](https://github.com/gfx-rs/wgpu/pull/3409)\n\n#### Vulkan\n\n- Set `WEBGPU_TEXTURE_FORMAT_SUPPORT` downlevel flag depending on the proper format support by @teoxoy in [#3367](https://github.com/gfx-rs/wgpu/pull/3367).\n- Set `COPY_SRC`/`COPY_DST` only based on Vulkan's `TRANSFER_SRC`/`TRANSFER_DST` by @teoxoy in [#3366](https://github.com/gfx-rs/wgpu/pull/3366).\n\n#### GLES\n\n- Browsers that support `OVR_multiview2` now report the `MULTIVIEW` feature by @expenses in [#3121](https://github.com/gfx-rs/wgpu/pull/3121).\n- `Limits::max_push_constant_size` on GLES is now 256 by @Dinnerbone in [#3374](https://github.com/gfx-rs/wgpu/pull/3374).\n- Creating multiple pipelines with the same shaders will now be faster, by @Dinnerbone in [#3380](https://github.com/gfx-rs/wgpu/pull/3380).\n\n#### WebGPU\n\n- Implement `queue_validate_write_buffer` by @jinleili in [#3098](https://github.com/gfx-rs/wgpu/pull/3098)\n- Sync depth/stencil copy restrictions with the spec by @teoxoy in [#3314](https://github.com/gfx-rs/wgpu/pull/3314)\n\n### Added/New Features\n\n#### General\n\n- Implement `Hash` for `DepthStencilState` and `DepthBiasState`\n- Add the `\"wgsl\"` feature, to enable WGSL shaders in `wgpu-core` and `wgpu`. Enabled by default in `wgpu`. By @daxpedda in [#2890](https://github.com/gfx-rs/wgpu/pull/2890).\n- Implement `Clone` for `ShaderSource` and `ShaderModuleDescriptor` in `wgpu`. By @daxpedda in [#3086](https://github.com/gfx-rs/wgpu/pull/3086).\n- Add `get_default_config` for `Surface` to simplify user creation of `SurfaceConfiguration`. By @jinleili in [#3034](https://github.com/gfx-rs/wgpu/pull/3034)\n- Improve compute shader validation error message. By @haraldreingruber in [#3139](https://github.com/gfx-rs/wgpu/pull/3139)\n- Native adapters can now use MSAA x2 and x8 if it's supported , previously only x1 and x4 were supported . By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140)\n- Implemented correleation between user timestamps and platform specific presentation timestamps via [`Adapter::get_presentation_timestamp`]. By @cwfitzgerald in [#3240](https://github.com/gfx-rs/wgpu/pull/3240)\n- Added support for `Features::SHADER_PRIMITIVE_INDEX` on all backends. By @cwfitzgerald in [#3272](https://github.com/gfx-rs/wgpu/pull/3272)\n- Implemented `TextureFormat::Stencil8`, allowing for stencil testing without depth components. By @Dinnerbone in [#3343](https://github.com/gfx-rs/wgpu/pull/3343)\n- Implemented `add_srgb_suffix()` for `TextureFormat` for converting linear formats to sRGB. By @Elabajaba in [#3419](https://github.com/gfx-rs/wgpu/pull/3419)\n- Zero-initialize workgroup memory. By @teoxoy in [#3174](https://github.com/gfx-rs/wgpu/pull/3174)\n\n#### GLES\n\n- Surfaces support now `TextureFormat::Rgba8Unorm` and (non-web only) `TextureFormat::Bgra8Unorm`. By @Wumpf in [#3070](https://github.com/gfx-rs/wgpu/pull/3070)\n- Support alpha to coverage. By @Wumpf in [#3156](https://github.com/gfx-rs/wgpu/pull/3156)\n- Support filtering f32 textures. By @expenses in [#3261](https://github.com/gfx-rs/wgpu/pull/3261)\n\n#### Vulkan\n\n- Add `SHADER_INT16` feature to enable the `shaderInt16` VkPhysicalDeviceFeature. By @Elabajaba in [#3401](https://github.com/gfx-rs/wgpu/pull/3401)\n\n#### WebGPU\n\n- Add `MULTISAMPLE_X2`, `MULTISAMPLE_X4` and `MULTISAMPLE_X8` to `TextureFormatFeatureFlags`. By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140)\n- Sync `TextureFormat.describe` with the spec. By @teoxoy in [3312](https://github.com/gfx-rs/wgpu/pull/3312)\n\n#### Metal\n\n- Add a way to create `Device` and `Queue` from raw Metal resources in wgpu-hal. By @AdrianEddy in [#3338](https://github.com/gfx-rs/wgpu/pull/3338)\n\n### Bug Fixes\n\n#### General\n\n- Update ndk-sys to v0.4.1+23.1.7779620, to fix checksum failures. By @jimblandy in [#3232](https://github.com/gfx-rs/wgpu/pull/3232).\n- Bother to free the `hal::Api::CommandBuffer` when a `wgpu_core::command::CommandEncoder` is dropped. By @jimblandy in [#3069](https://github.com/gfx-rs/wgpu/pull/3069).\n- Fixed the mipmap example by adding the missing WRITE_TIMESTAMP_INSIDE_PASSES feature. By @Olaroll in [#3081](https://github.com/gfx-rs/wgpu/pull/3081).\n- Avoid panicking in some interactions with invalid resources by @nical in (#3094)[https://github.com/gfx-rs/wgpu/pull/3094]\n- Fixed an integer overflow in `copy_texture_to_texture` by @nical [#3090](https://github.com/gfx-rs/wgpu/pull/3090)\n- Remove `wgpu_types::Features::DEPTH24PLUS_STENCIL8`, making `wgpu::TextureFormat::Depth24PlusStencil8` available on all backends. By @Healthire in (#3151)[https://github.com/gfx-rs/wgpu/pull/3151]\n- Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146]\n- Make `RenderPassCompatibilityError` and `CreateShaderModuleError` not so huge. By @jimblandy in (#3226)[https://github.com/gfx-rs/wgpu/pull/3226]\n- Check for invalid bitflag bits in wgpu-core and allow them to be captured/replayed by @nical in (#3229)[https://github.com/gfx-rs/wgpu/pull/3229]\n- Evaluate `gfx_select!`'s `#[cfg]` conditions at the right time. By @jimblandy in [#3253](https://github.com/gfx-rs/wgpu/pull/3253)\n- Improve error messages when binding bind group with dynamic offsets. By @cwfitzgerald in [#3294](https://github.com/gfx-rs/wgpu/pull/3294)\n- Allow non-filtering sampling of integer textures. By @JMS55 in [#3362](https://github.com/gfx-rs/wgpu/pull/3362).\n- Validate texture ids in `Global::queue_texture_write`. By @jimblandy in [#3378](https://github.com/gfx-rs/wgpu/pull/3378).\n- Don't panic on mapped buffer in queue_submit. By @crowlKats in [#3364](https://github.com/gfx-rs/wgpu/pull/3364).\n- Fix being able to sample a depth texture with a filtering sampler. By @teoxoy in [#3394](https://github.com/gfx-rs/wgpu/pull/3394).\n- Make `make_spirv_raw` and `make_spirv` handle big-endian binaries. By @1e1001 in [#3411](https://github.com/gfx-rs/wgpu/pull/3411).\n\n#### Vulkan\n\n- Update ash to 0.37.1+1.3.235 to fix CI breaking by changing a call to the deprecated `debug_utils_set_object_name()` function to `set_debug_utils_object_name()` by @elabajaba in [#3273](https://github.com/gfx-rs/wgpu/pull/3273)\n- Document and improve extension detection. By @teoxoy in [#3327](https://github.com/gfx-rs/wgpu/pull/3327)\n- Don't use a pointer to a local copy of a `PhysicalDeviceDriverProperties` struct after it has gone out of scope. In fact, don't make a local copy at all. Introduce a helper function for building `CStr`s from C character arrays, and remove some `unsafe` blocks. By @jimblandy in [#3076](https://github.com/gfx-rs/wgpu/pull/3076).\n\n#### DX12\n\n- Fix `depth16Unorm` formats by @teoxoy in [#3313](https://github.com/gfx-rs/wgpu/pull/3313)\n- Don't re-use `GraphicsCommandList` when `close` or `reset` fails. By @xiaopengli89 in [#3204](https://github.com/gfx-rs/wgpu/pull/3204)\n\n#### Metal\n\n- Fix texture view creation with full-resource views when using an explicit `mip_level_count` or `array_layer_count`. By @cwfitzgerald in [#3323](https://github.com/gfx-rs/wgpu/pull/3323)\n\n#### GLES\n\n- Fixed WebGL not displaying srgb targets correctly if a non-screen filling viewport was previously set. By @Wumpf in [#3093](https://github.com/gfx-rs/wgpu/pull/3093)\n- Fix disallowing multisampling for float textures if otherwise supported. By @Wumpf in [#3183](https://github.com/gfx-rs/wgpu/pull/3183)\n- Fix a panic when creating a pipeline with opaque types other than samplers (images and atomic counters). By @James2022-rgb in [#3361](https://github.com/gfx-rs/wgpu/pull/3361)\n- Fix uniform buffers being empty on some vendors. By @Dinnerbone in [#3391](https://github.com/gfx-rs/wgpu/pull/3391)\n- Fix a panic allocating a new buffer on webgl. By @Dinnerbone in [#3396](https://github.com/gfx-rs/wgpu/pull/3396)\n\n#### WebGPU\n\n- Use `log` instead of `println` in hello example by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858)\n\n#### deno-webgpu\n\n- Let `setVertexBuffer` and `setIndexBuffer` calls on\n  `GPURenderBundleEncoder` throw an error if the `size` argument is\n  zero, rather than treating that as \"until the end of the buffer\".\n  By @jimblandy in [#3171](https://github.com/gfx-rs/wgpu/pull/3171)\n\n#### Emscripten\n\n- Let the wgpu examples `framework.rs` compile again under Emscripten. By @jimblandy in [#3246](https://github.com/gfx-rs/wgpu/pull/3246)\n\n### Examples\n\n- Log adapter info in hello example on wasm target by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858)\n- Added new example `stencil-triangles` to show basic use of stencil testing. By @Dinnerbone in [#3343](https://github.com/gfx-rs/wgpu/pull/3343)\n\n### Testing/Internal\n\n- Update the `minimum supported rust version` to 1.64\n- Move `ResourceMetadata` into its own module. By @jimblandy in [#3213](https://github.com/gfx-rs/wgpu/pull/3213)\n- Add WebAssembly testing infrastructure. By @haraldreingruber in [#3238](https://github.com/gfx-rs/wgpu/pull/3238)\n- Error message when you forget to use cargo-nextest. By @cwfitzgerald in [#3293](https://github.com/gfx-rs/wgpu/pull/3293)\n- Fix all suggestions from `cargo clippy`\n\n## wgpu-0.14.2 (2022-11-28)\n\n### Bug Fixes\n\n- Fix incorrect offset in `get_mapped_range` by @nical in [#3233](https://github.com/gfx-rs/wgpu/pull/3233)\n\n## wgpu-0.14.1 (2022-11-02)\n\n### Bug Fixes\n\n- Make `wgpu::TextureFormat::Depth24PlusStencil8` available on all backends by making the feature unconditionally available and the feature unneeded to use the format. By @Healthire and @cwfitzgerald in [#3165](https://github.com/gfx-rs/wgpu/pull/3165)\n\n## wgpu-0.14.0 (2022-10-05)\n\n### Major Changes\n\n#### @invariant Warning\n\nWhen using CompareFunction::Equal or CompareFunction::NotEqual on a pipeline, there is now a warning logged if the vertex\nshader does not have a @invariant tag on it. On some machines, rendering the same triangles multiple times without an\n@invariant tag will result in slightly different depths for every pixel. Because the \\*Equal functions rely on depth being\nthe same every time it is rendered, we now warn if it is missing.\n\n```diff\n-@vertex\n-fn vert_main(v_in: VertexInput) -> @builtin(position) vec4<f32> {...}\n+@vertex\n+fn vert_main(v_in: VertexInput) -> @builtin(position) @invariant vec4<f32> {...}\n```\n\n#### Surface Alpha and PresentModes\n\nSurface supports `alpha_mode` now. When alpha_mode is equal to `PreMultiplied` or `PostMultiplied`,\nthe alpha channel of framebuffer is respected in the compositing process, but which mode is available depends on\nthe different API and `Device`. If don't care about alpha_mode, you can set it to `Auto`.\n\n```diff\nSurfaceConfiguration {\n// ...\n+ alpha_mode: surface.get_supported_alpha_modes(&adapter)[0],\n}\n```\n\nThe function to enumerate supported presentation modes changed:\n\n```diff\n- pub fn wgpu::Surface::get_supported_modes(&self, adapter: &wgpu::Adapter) -> Vec<PresentMode>\n+ pub fn wgpu::Surface::get_supported_present_modes(&self, adapter: &wgpu::Adapter) -> Vec<PresentMode>\n```\n\n#### Updated raw-window-handle to 0.5\n\nThis will allow use of the latest version of winit. As such the bound on create_surface is now RWH 0.5 and requires\nboth `raw_window_handle::HasRawWindowHandle` and `raw_window_handle::HasRawDisplayHandle`.\n\n### Added/New Features\n\n- Add `Buffer::size()` and `Buffer::usage()`; by @kpreid in [#2923](https://github.com/gfx-rs/wgpu/pull/2923)\n- Split Blendability and Filterability into Two Different TextureFormatFeatureFlags; by @stakka in [#3012](https://github.com/gfx-rs/wgpu/pull/3012)\n- Expose `alpha_mode` on SurfaceConfiguration, by @jinleili in [#2836](https://github.com/gfx-rs/wgpu/pull/2836)\n- Introduce fields for driver name and info in `AdapterInfo`, by @i509VCB in [#3037](https://github.com/gfx-rs/wgpu/pull/3037)\n- Add way to create gles hal textures from raw gl names to allow externally managed textures. By @i509VCB [#3046](https://github.com/gfx-rs/wgpu/pull/3046)\n- Implemented `copy_external_image_to_texture` on WebGPU, by @ybiletskyi in [#2781](https://github.com/gfx-rs/wgpu/pull/2781)\n\n### Bug Fixes\n\n#### General\n\n- Free `StagingBuffers` even when an error occurs in the operation that consumes them. By @jimblandy in [#2961](https://github.com/gfx-rs/wgpu/pull/2961)\n- Avoid overflow when checking that texture copies fall within bounds. By @jimblandy in [#2963](https://github.com/gfx-rs/wgpu/pull/2963)\n- Improve the validation and error reporting of buffer mappings by @nical in [#2848](https://github.com/gfx-rs/wgpu/pull/2848)\n- Fix compilation errors when using wgpu-core in isolation while targeting `wasm32-unknown-unknown` by @Seamooo in [#2922](https://github.com/gfx-rs/wgpu/pull/2922)\n- Fixed opening of RenderDoc library by @abuffseagull in [#2930](https://github.com/gfx-rs/wgpu/pull/2930)\n- Added missing validation for `BufferUsages` mismatches when `Features::MAPPABLE_PRIMARY_BUFFERS` is not\n  enabled. By @imberflur in [#3023](https://github.com/gfx-rs/wgpu/pull/3023)\n- Fixed `CommandEncoder` not being `Send` and `Sync` on web by @i509VCB in [#3025](https://github.com/gfx-rs/wgpu/pull/3025)\n- Document meaning of `vendor` in `AdapterInfo` if the vendor has no PCI id.\n- Fix missing resource labels from some Errors by @scoopr in [#3066](https://github.com/gfx-rs/wgpu/pull/3066)\n\n#### Metal\n\n- Add the missing `msg_send![view, retain]` call within `from_view` by @jinleili in [#2976](https://github.com/gfx-rs/wgpu/pull/2976)\n- Fix `max_buffer` `max_texture` and `max_vertex_buffers` limits by @jinleili in [#2978](https://github.com/gfx-rs/wgpu/pull/2978)\n- Remove PrivateCapabilities's `format_rgb10a2_unorm_surface` field by @jinleili in [#2981](https://github.com/gfx-rs/wgpu/pull/2981)\n- Fix validation error when copying into a subset of a single-layer texture by @nical in [#3063](https://github.com/gfx-rs/wgpu/pull/3063)\n- Fix `_buffer_sizes` encoding by @dtiselice in [#3047](https://github.com/gfx-rs/wgpu/pull/3047)\n\n#### Vulkan\n\n- Fix `astc_hdr` formats support by @jinleili in [#2971]](https://github.com/gfx-rs/wgpu/pull/2971)\n- Update to naga b209d911 (2022-9-1) to avoid generating SPIR-V that\n  violates Vulkan valid usage rules `VUID-StandaloneSpirv-Flat-06202`\n  and `VUID-StandaloneSpirv-Flat-04744`. By @jimblandy in\n  [#3008](https://github.com/gfx-rs/wgpu/pull/3008)\n- Fix bug where the Vulkan backend would panic when using a supported window and display handle but the\n  dependent extensions are not available by @i509VCB in [#3054](https://github.com/gfx-rs/wgpu/pull/3054).\n\n#### GLES\n\n- Report vendor id for Mesa and Apple GPUs. By @i509VCB [#3036](https://github.com/gfx-rs/wgpu/pull/3036)\n- Report Apple M2 gpu as integrated. By @i509VCB [#3036](https://github.com/gfx-rs/wgpu/pull/3036)\n\n#### WebGPU\n\n- When called in a web worker, `Context::init()` now uses `web_sys::WorkerGlobalContext` to create a `wgpu::Instance` instead of trying to access the unavailable `web_sys::Window` by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858)\n\n### Changes\n\n#### General\n\n- Changed wgpu-hal and wgpu-core implementation to pass RawDisplayHandle and RawWindowHandle as separate\n  parameters instead of passing an impl trait over both HasRawDisplayHandle and HasRawWindowHandle. By @i509VCB in [#3022](https://github.com/gfx-rs/wgpu/pull/3022)\n- Changed `Instance::as_hal<A>` to just return an `Option<&A::Instance>` rather than taking a callback. By @jimb in [#2991](https://github.com/gfx-rs/wgpu/pull/2991)\n- Added downlevel restriction error message for `InvalidFormatUsages` error by @Seamooo in [#2886](https://github.com/gfx-rs/wgpu/pull/2886)\n- Add warning when using CompareFunction::\\*Equal with vertex shader that is missing @invariant tag by @cwfitzgerald in [#2887](https://github.com/gfx-rs/wgpu/pull/2887)\n- 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)\n- Address Clippy 0.1.63 complaints. By @jimblandy in [#2977](https://github.com/gfx-rs/wgpu/pull/2977)\n- Don't use `PhantomData` for `IdentityManager`'s `Input` type. By @jimblandy in [#2972](https://github.com/gfx-rs/wgpu/pull/2972)\n- 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)\n- Updated the maximum binding index to match the WebGPU specification by @nical in [#2957](https://github.com/gfx-rs/wgpu/pull/2957)\n- 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).\n\n#### Metal\n\n- Extract the generic code into `get_metal_layer` by @jinleili in [#2826](https://github.com/gfx-rs/wgpu/pull/2826)\n\n#### Vulkan\n\n- Remove use of Vulkan12Features/Properties types. By @i509VCB in [#2936](https://github.com/gfx-rs/wgpu/pull/2936)\n- Provide a means for `wgpu` users to access `vk::Queue` and the queue index. By @anlumo in [#2950](https://github.com/gfx-rs/wgpu/pull/2950)\n- Use the use effective api version for determining device features instead of wrongly assuming `VkPhysicalDeviceProperties.apiVersion`\n  is the actual version of the device. By @i509VCB in [#3011](https://github.com/gfx-rs/wgpu/pull/3011)\n- `DropGuard` has been moved to the root of the wgpu-hal crate. By @i509VCB [#3046](https://github.com/gfx-rs/wgpu/pull/3046)\n\n#### GLES\n\n- Add `Rgba16Float` format support for color attachments. By @jinleili in [#3045](https://github.com/gfx-rs/wgpu/pull/3045)\n- `TEXTURE_COMPRESSION_ASTC_HDR` feature detection by @jinleili in [#3042](https://github.com/gfx-rs/wgpu/pull/3042)\n\n### Performance\n\n- Made `StagingBelt::write_buffer()` check more thoroughly for reusable memory; by @kpreid in [#2906](https://github.com/gfx-rs/wgpu/pull/2906)\n\n### Documentation\n\n- Add WGSL examples to complement existing examples written in GLSL by @norepimorphism in [#2888](https://github.com/gfx-rs/wgpu/pull/2888)\n- Document `wgpu_core` resource allocation. @jimblandy in [#2973](https://github.com/gfx-rs/wgpu/pull/2973)\n- Expanded `StagingBelt` documentation by @kpreid in [#2905](https://github.com/gfx-rs/wgpu/pull/2905)\n- Fixed documentation for `Instance::create_surface_from_canvas` and\n  `Instance::create_surface_from_offscreen_canvas` regarding their\n  safety contract. These functions are not unsafe. By @jimblandy [#2990](https://github.com/gfx-rs/wgpu/pull/2990)\n- Document that `write_buffer_with()` is sound but unwise to read from by @kpreid in [#3006](https://github.com/gfx-rs/wgpu/pull/3006)\n- Explain why `Adapter::as_hal` and `Device::as_hal` have to take callback functions. By @jimblandy in [#2992](https://github.com/gfx-rs/wgpu/pull/2992)\n\n### Dependency Updates\n\n#### WebGPU\n\n- Update wasm32 dependencies, set `alpha_mode` on web target by @jinleili in [#3040](https://github.com/gfx-rs/wgpu/pull/3040)\n\n### Build Configuration\n\n- Add the `\"strict_asserts\"` feature, to enable additional internal\n  run-time validation in `wgpu-core`. By @jimblandy in\n  [#2872](https://github.com/gfx-rs/wgpu/pull/2872).\n\n### Full API Diff\n\nManual concatenation of `cargo public-api --diff-git-checkouts v0.13.2 v0.14.0 -p wgpu` and `cargo public-api --diff-git-checkouts v0.13.2 v0.14.0 -p wgpu-types`\n\n```diff\nRemoved items from the public API\n=================================\n-pub fn wgpu::Surface::get_supported_modes(&self, adapter: &wgpu::Adapter) -> Vec<PresentMode>\n-pub const wgpu::Features::DEPTH24UNORM_STENCIL8: Self\n-pub enum variant wgpu::TextureFormat::Depth24UnormStencil8\n\nChanged items in the public API\n===============================\n-pub unsafe fn wgpu::Instance::as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&<A as >::Instance>) -> R, R>(&self, hal_instance_callback: F) -> R\n+pub unsafe fn wgpu::Instance::as_hal<A: wgc::hub::HalApi>(&self) -> Option<&<A as >::Instance>\n-pub unsafe fn wgpu::Instance::create_surface<W: raw_window_handle::HasRawWindowHandle>(&self, window: &W) -> wgpu::Surface\n+pub unsafe fn wgpu::Instance::create_surface<W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle>(&self, window: &W) -> wgpu::Surface\n\nAdded items to the public API\n=============================\n+pub fn wgpu::Buffer::size(&self) -> wgt::BufferAddress\n+pub fn wgpu::Buffer::usage(&self) -> BufferUsages\n+pub fn wgpu::Surface::get_supported_alpha_modes(&self, adapter: &wgpu::Adapter) -> Vec<CompositeAlphaMode>\n+pub fn wgpu::Surface::get_supported_present_modes(&self, adapter: &wgpu::Adapter) -> Vec<PresentMode>\n+#[repr(C)] pub enum wgpu::CompositeAlphaMode\n+impl RefUnwindSafe for wgpu::CompositeAlphaMode\n+impl Send for wgpu::CompositeAlphaMode\n+impl Sync for wgpu::CompositeAlphaMode\n+impl Unpin for wgpu::CompositeAlphaMode\n+impl UnwindSafe for wgpu::CompositeAlphaMode\n+pub const wgpu::Features::DEPTH24PLUS_STENCIL8: Self\n+pub const wgpu::TextureFormatFeatureFlags::BLENDABLE: Self\n+pub enum variant wgpu::CompositeAlphaMode::Auto = 0\n+pub enum variant wgpu::CompositeAlphaMode::Inherit = 4\n+pub enum variant wgpu::CompositeAlphaMode::Opaque = 1\n+pub enum variant wgpu::CompositeAlphaMode::PostMultiplied = 3\n+pub enum variant wgpu::CompositeAlphaMode::PreMultiplied = 2\n+pub enum variant wgpu::TextureFormat::Depth16Unorm\n+pub fn wgpu::CompositeAlphaMode::clone(&self) -> wgpu::CompositeAlphaMode\n+pub fn wgpu::CompositeAlphaMode::eq(&self, other: &wgpu::CompositeAlphaMode) -> bool\n+pub fn wgpu::CompositeAlphaMode::fmt(&self, f: &mut $crate::fmt::Formatter<'_>) -> $crate::fmt::Result\n+pub fn wgpu::CompositeAlphaMode::hash<__H: $crate::hash::Hasher>(&self, state: &mut __H) -> ()\n+pub struct field wgpu::AdapterInfo::driver: String\n+pub struct field wgpu::AdapterInfo::driver_info: String\n+pub struct field wgpu::SurfaceConfiguration::alpha_mode: wgpu_types::CompositeAlphaMode\n```\n\n## wgpu-0.13.2 (2022-07-13)\n\n### Bug Fixes\n\n#### General\n\n- Prefer `DeviceType::DiscreteGpu` over `DeviceType::Other` for `PowerPreference::LowPower` so Vulkan is preferred over OpenGL again by @Craig-Macomber in [#2853](https://github.com/gfx-rs/wgpu/pull/2853)\n- Allow running `get_texture_format_features` on unsupported texture formats (returning no flags) by @cwfitzgerald in [#2856](https://github.com/gfx-rs/wgpu/pull/2856)\n- Allow multi-sampled textures that are supported by the device but not WebGPU if `TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` is enabled by @cwfitzgerald in [#2856](https://github.com/gfx-rs/wgpu/pull/2856)\n- `get_texture_format_features` only lists the COPY\\_\\* usages if the adapter actually supports that usage by @cwfitzgerald in [#2856](https://github.com/gfx-rs/wgpu/pull/2856)\n- Fix bind group / pipeline deduplication not taking into account RenderBundle execution resetting these values by @shoebe [#2867](https://github.com/gfx-rs/wgpu/pull/2867)\n- Fix panics that occur when using `as_hal` functions when the hal generic type does not match the hub being looked up in by @i509VCB [#2871](https://github.com/gfx-rs/wgpu/pull/2871)\n- Add some validation in map_async by @nical in [#2876](https://github.com/gfx-rs/wgpu/pull/2876)\n- Fix bugs when mapping/unmapping zero-sized buffers and ranges by @nical in [#2877](https://github.com/gfx-rs/wgpu/pull/2877)\n- Fix out-of-bound write in `map_buffer` with non-zero offset by @nical in [#2916](https://github.com/gfx-rs/wgpu/pull/2916)\n- Validate the number of color attachments in `create_render_pipeline` by @nical in [#2913](https://github.com/gfx-rs/wgpu/pull/2913)\n- Validate against the maximum binding index in `create_bind_group_layout` by @nical in [#2892](https://github.com/gfx-rs/wgpu/pull/2892)\n- Validate that map_async's range is not negative by @nical in [#2938](https://github.com/gfx-rs/wgpu/pull/2938)\n- Fix calculation/validation of layer/mip ranges in create_texture_view by @nical in [#2955](https://github.com/gfx-rs/wgpu/pull/2955)\n- Validate the sample count and mip level in `copy_texture_to_buffer` by @nical in [#2958](https://github.com/gfx-rs/wgpu/pull/2958)\n- Expose the cause of the error in the `map_async` callback in [#2939](https://github.com/gfx-rs/wgpu/pull/2939)\n\n#### DX12\n\n- `DownlevelCapabilities::default()` now returns the `ANISOTROPIC_FILTERING` flag set to true so DX12 lists `ANISOTROPIC_FILTERING` as true again by @cwfitzgerald in [#2851](https://github.com/gfx-rs/wgpu/pull/2851)\n- Properly query format features for UAV/SRV usages of depth formats by @cwfitzgerald in [#2856](https://github.com/gfx-rs/wgpu/pull/2856)\n\n#### Vulkan\n\n- Vulkan 1.0 drivers that support `VK_KHR_multiview` now properly report the `MULTIVIEW` feature as supported by @i509VCB in [#2934](https://github.com/gfx-rs/wgpu/pull/2934).\n- Stop using `VkPhysicalDevice11Features` in Vulkan 1.1 which is confusingly provided in Vulkan 1.2 by @i509VCB in [#2934](https://github.com/gfx-rs/wgpu/pull/2934).\n\n#### GLES\n\n- Fix depth stencil texture format capability by @jinleili in [#2854](https://github.com/gfx-rs/wgpu/pull/2854)\n- `get_texture_format_features` now only returns usages for formats it actually supports by @cwfitzgerald in [#2856](https://github.com/gfx-rs/wgpu/pull/2856)\n\n#### Hal\n\n- Allow access to queue family index in Vulkan hal by @i509VCB in [#2859](https://github.com/gfx-rs/wgpu/pull/2859)\n- Allow access to the EGLDisplay and EGLContext pointer in Gles hal Adapter and Device by @i509VCB in [#2860](https://github.com/gfx-rs/wgpu/pull/2860)\n\n### Documentation\n\n- Update present_mode docs as most of them don't automatically fall back to Fifo anymore. by @Elabajaba in [#2855](https://github.com/gfx-rs/wgpu/pull/2855)\n\n#### Hal\n\n- Document safety requirements for `Adapter::from_external` in gles hal by @i509VCB in [#2863](https://github.com/gfx-rs/wgpu/pull/2863)\n- Make `AdapterContext` a publicly accessible type in the gles hal by @i509VCB in [#2870](https://github.com/gfx-rs/wgpu/pull/2870)\n\n## wgpu-0.13.1 (2022-07-02)\n\n### Bug Fixes\n\n#### General\n\n- Fix out of bounds access when surface texture is written to by multiple command buffers by @cwfitzgerald in [#2843](https://github.com/gfx-rs/wgpu/pull/2843)\n\n#### GLES\n\n- AutoNoVSync now correctly falls back to Fifo by @simbleau in [#2842](https://github.com/gfx-rs/wgpu/pull/2842)\n- Fix GL_EXT_color_buffer_float detection on native by @cwfitzgerald in [#2843](https://github.com/gfx-rs/wgpu/pull/2843)\n\n## wgpu-0.13 (2022-06-30)\n\n### Major Changes\n\n#### WGSL Syntax\n\nWGSL syntax has changed in a couple ways. The new syntax is easier to read and work with.\n\nAttribute declarations are written differently:\n\n```diff\n- [[group(1), binding(0)]]\n+ @group(1) @binding(0)\n```\n\nStage declarations are now separate attributes rather than part of the `stage` attribute:\n\n```diff\n- [[stage(vertex)]]\n+ @vertex\n```\n\nStructs now use `,` as field separator and no longer need semicolons after the declaration:\n\n```diff\n- struct MyStruct {\n-     my_member: u32;\n- };\n+ struct MyStruct {\n+     my_member: u32,\n+ }\n```\n\n#### Surface API\n\nThe method of getting the preferred swapchain format has changed to allow viewing all formats supported by the surface.\n\n```diff\n- let format = surface.get_preferred_format(&adapter).unwrap();\n+ let format = surface.get_supported_formats(&adapter)[0];\n```\n\nPresentation modes now need to match exactly what the surface supports. `FIFO` is _always_ supported,\nbut all other modes vary from API to API and `Device` to `Device`. To get a list of all supported modes,\ncall the following. The order does not indicate preference.\n\n```rust\nlet modes = surface.get_supported_present_modes(&adapter);\n```\n\n#### Timestamp Queries\n\nTimestamp queries are now restricted behind multiple features to allow implementation on TBDR (Tile-Based Deferred Rendering)\nbased GPUs, such as mobile devices and Apple's M chips.\n\n`Features::TIMESTAMP_QUERIES` now allows for calling `write_timestamp` only on `CommandEncoder`s.\n\n`Features::WRITE_TIMESTAMP_INSIDE_PASSES` is needed to call `write_timestamp` on `RenderPassEncoder`s or `ComputePassEncoder`s.\n\n#### map_async\n\nThe function for mapping buffers no longer returns a future, and instead calls a callback when the buffer is mapped.\n\nThis aligns with the use of the API more clearly - you aren't supposed to block and wait on the future to resolve,\nyou are supposed to keep rendering and wait until the buffer maps on its own. Mapping and the flow of mapping\nis an under-documented area that we hope to improve in the future.\n\n```diff\n- let future = buffer.slice(..).map_async(MapMode::Read);\n+ buffer.slice(..).map_async(MapMode::Read, || {\n+     // Called when buffer is mapped.\n+ })\n```\n\n#### Submission Indexes\n\nCalling `queue.submit` now returns an opaque submission index that can be used as an argument to\n`device.poll` to say which submission to wait to complete.\n\n### Other Breaking Changes\n\n`Device::create_shader_module` now takes the shader descriptor by value:\n\n```diff\n- device.create_shader_module(&shader_module_descriptor)\n+ device.create_shader_module(shader_module_descriptor)\n```\n\nColor attachments can be sparse, so they are now optional:\n\n```diff\nFragmentState {\n-  targets: &[color_target_state]\n+  targets: &[Some(color_target_state)]\n  // ..\n}\n```\n\n```diff\nRenderPassDescriptor {\n-  color_attachments: &[render_pass_color_attachment]\n+  color_attachments: &[Some(render_pass_color_attachment)]\n  // ..\n}\n```\n\n```diff\nRenderBundleEncoderDescriptor {\n-  color_formats: &[texture_format]\n+  color_formats: &[Some(texture_format)]\n  // ..\n}\n```\n\n`Extent3d::max_mips` now requires you to pass a TextureDimension to specify whether or not depth_or_array_layers should be ignored:\n\n```diff\nExtent3d {\n  width: 1920,\n  height: 1080,\n  depth_or_array_layers: 6,\n- }.max_mips()\n+ }.max_mips(wgpu::TextureDimension::D3)\n```\n\n`Limits` has a new field, [`max_buffer_size`](https://docs.rs/wgpu/0.13.0/wgpu/struct.Limits.html#structfield.max_buffer_size) (not an issue if you don't define limits manually):\n\n```diff\nLimits {\n  // ...\n+ max_buffer_size: 256 * 1024 * 1024, // adjust as you see fit\n}\n```\n\n`Features::CLEAR_COMMANDS` is now unnecessary and no longer exists. The feature to clear buffers and textures is now part of upstream WebGPU.\n\n```diff\nDeviceDescriptor {\n  // ...\n  features: wgpu::Features::VERTEX_WRITABLE_STORAGE\n    | wgpu::Features::MAPPABLE_PRIMARY_BUFFERS\n    | wgpu::Features::TEXTURE_BINDING_ARRAY\n    | wgpu::Features::BUFFER_BINDING_ARRAY\n    | wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY\n-    | wgpu::Features::CLEAR_COMMANDS\n  ,\n}\n```\n\n`ComputePass::dispatch` has been renamed to `ComputePass::dispatch_workgroups`\n\n```diff\n- cpass.dispatch(self.work_group_count, 1, 1)\n+ cpass.dispatch_workgroups(self.work_group_count, 1, 1)\n```\n\n### Added/New Features\n\n#### General\n\n- Add `util::indirect::*` helper structs by @IcanDivideBy0 in [#2365](https://github.com/gfx-rs/wgpu/pull/2365)\n- Add `AddressMode::ClampToZero` by @laptou in [#2364](https://github.com/gfx-rs/wgpu/pull/2364)\n- Add MULTISAMPLED_SHADING downlevel flag by @jinleili in [#2425](https://github.com/gfx-rs/wgpu/pull/2425)\n- Allow non struct buffers in wgsl by @IcanDivideBy0 in [#2451](https://github.com/gfx-rs/wgpu/pull/2451)\n- Prefix every wgpu-generated label with `(wgpu)`. by @kpreid in [#2590](https://github.com/gfx-rs/wgpu/pull/2590)\n- Permit non-struct, non-array types as buffers. by @jimblandy in [#2584](https://github.com/gfx-rs/wgpu/pull/2584)\n- Return `queue_empty` for Device::poll by @xiaopengli89 in [#2643](https://github.com/gfx-rs/wgpu/pull/2643)\n- Add `SHADER_FLOAT16` feature by @jinleili in [#2646](https://github.com/gfx-rs/wgpu/pull/2646)\n- Add DEPTH32FLOAT_STENCIL8 feature by @jinleili in [#2664](https://github.com/gfx-rs/wgpu/pull/2664)\n- Add DEPTH24UNORM_STENCIL8 feature by @jinleili in [#2689](https://github.com/gfx-rs/wgpu/pull/2689)\n- Implement submission indexes by @cwfitzgerald in [#2700](https://github.com/gfx-rs/wgpu/pull/2700)\n- [WebGL] Add a downlevel capability for rendering to floating point textures by @expenses in [#2729](https://github.com/gfx-rs/wgpu/pull/2729)\n- allow creating wgpu::Instance from wgpu_core::Instance by @i509VCB in [#2763](https://github.com/gfx-rs/wgpu/pull/2763)\n- Force binding sizes to be multiples of 16 on webgl by @cwfitzgerald in [#2808](https://github.com/gfx-rs/wgpu/pull/2808)\n- Add naga variant to ShaderSource by @rttad in [#2801](https://github.com/gfx-rs/wgpu/pull/2801)\n- Implement Queue::write_buffer_with by @teoxoy in [#2777](https://github.com/gfx-rs/wgpu/pull/2777)\n\n#### Vulkan\n\n- Re-allow vk backend on Apple platforms via `vulkan-portability` feature by @jinleili in [#2488](https://github.com/gfx-rs/wgpu/pull/2488)\n- vulkan: HDR ASTC formats support by @jinleili in [#2496](https://github.com/gfx-rs/wgpu/pull/2496)\n\n#### Metal\n\n- Implement push constants for metal backend by @TheOnlyMrCat in [#2314](https://github.com/gfx-rs/wgpu/pull/2314)\n- Metal backend ASTC HDR formats support by @jinleili in [#2477](https://github.com/gfx-rs/wgpu/pull/2477)\n- Add COPY_DST to Metal's surface usage bits by @vl4dimir in [#2491](https://github.com/gfx-rs/wgpu/pull/2491)\n- Add `Features::MULTI_DRAW_INDIRECT` to Metal by @expenses in [#2737](https://github.com/gfx-rs/wgpu/pull/2737)\n\n#### GLES\n\n- Support externally initialized contexts by @kvark in [#2350](https://github.com/gfx-rs/wgpu/pull/2350)\n- Angle support on macOS by @jinleili in [#2461](https://github.com/gfx-rs/wgpu/pull/2461)\n- Use EGL surfaceless platform when windowing system is not found by @sh7dm in [#2339](https://github.com/gfx-rs/wgpu/pull/2339)\n- Do a downlevel check for anisotrophy and enable it in the webgl backend by @expenses in [#2616](https://github.com/gfx-rs/wgpu/pull/2616)\n- OffscreenCanvas Support for WebGL Backend by @haraldreingruber-dedalus in [#2603](https://github.com/gfx-rs/wgpu/pull/2603)\n\n#### DX12\n\n- Support to create surface from visual on Windows by @xiaopengli89 in [#2434](https://github.com/gfx-rs/wgpu/pull/2434)\n- Add raw_queue for d3d12 device by @xiaopengli89 in [#2600](https://github.com/gfx-rs/wgpu/pull/2600)\n\n#### DX11\n\n- Skeleton of a DX11 backend - not working yet by @cwfitzgerald in [#2443](https://github.com/gfx-rs/wgpu/pull/2443)\n\n#### Hal\n\n- Adapter and Instance as_hal functions by @i509VCB in [#2663](https://github.com/gfx-rs/wgpu/pull/2663)\n- expose some underlying types in Vulkan hal by @i509VCB in [#2667](https://github.com/gfx-rs/wgpu/pull/2667)\n- Add raw_device method for dx12, vulkan hal by @xiaopengli89 in [#2360](https://github.com/gfx-rs/wgpu/pull/2360)\n- expose egl display in gles Instance hal by @i509VCB in [#2670](https://github.com/gfx-rs/wgpu/pull/2670)\n- Add raw_adapter method for dx12 hal adapter by @xiaopengli89 in [#2714](https://github.com/gfx-rs/wgpu/pull/2714)\n- Acquire texture: `Option<std::time::Duration>` timeouts by @rib in [#2724](https://github.com/gfx-rs/wgpu/pull/2724)\n- expose vulkan physical device capabilities, enabled device extensions by @i509VCB in [#2688](https://github.com/gfx-rs/wgpu/pull/2688)\n\n#### Emscripten\n\n- feature: emscripten by @caiiiycuk in [#2422](https://github.com/gfx-rs/wgpu/pull/2422)\n- feature = emscripten, compatibility fixes for wgpu-native by @caiiiycuk in [#2450](https://github.com/gfx-rs/wgpu/pull/2450)\n\n### Changes\n\n#### General\n\n- Make ShaderSource #[non_exhaustive] by @fintelia in [#2312](https://github.com/gfx-rs/wgpu/pull/2312)\n- Make `execute_bundles()` receive IntoIterator by @maku693 in [#2410](https://github.com/gfx-rs/wgpu/pull/2410)\n- Raise `wgpu_hal::MAX_COLOR_TARGETS` to 8. by @jimblandy in [#2640](https://github.com/gfx-rs/wgpu/pull/2640)\n- Rename dispatch -> dispatch_workgroups by @jinleili in [#2619](https://github.com/gfx-rs/wgpu/pull/2619)\n- Update texture_create_view logic to match spec by @jinleili in [#2621](https://github.com/gfx-rs/wgpu/pull/2621)\n- Move TEXTURE_COMPRESSION_ETC2 | ASTC_LDR to web section to match spec by @jinleili in [#2671](https://github.com/gfx-rs/wgpu/pull/2671)\n- Check that all vertex outputs are consumed by the fragment shader by @cwfitzgerald in [#2704](https://github.com/gfx-rs/wgpu/pull/2704)\n- Convert map_async from being async to being callback based by @cwfitzgerald in [#2698](https://github.com/gfx-rs/wgpu/pull/2698)\n- Align the validation of Device::create_texture with the WebGPU spec by @nical in [#2759](https://github.com/gfx-rs/wgpu/pull/2759)\n- Add InvalidGroupIndex validation at create_shader_module by @jinleili in [#2775](https://github.com/gfx-rs/wgpu/pull/2775)\n- Rename MAX_COLOR_TARGETS to MAX_COLOR_ATTACHMENTS to match spec by @jinleili in [#2780](https://github.com/gfx-rs/wgpu/pull/2780)\n- Change get_preferred_format to get_supported_formats by @stevenhuyn in [#2783](https://github.com/gfx-rs/wgpu/pull/2783)\n- Restrict WriteTimestamp Inside Passes by @cwfitzgerald in [#2802](https://github.com/gfx-rs/wgpu/pull/2802)\n- Flip span labels to work better with tools by @cwfitzgerald in [#2820](https://github.com/gfx-rs/wgpu/pull/2820)\n\n#### Gles\n\n- Make GLES DeviceType unknown by default by @PolyMeilex in [#2647](https://github.com/gfx-rs/wgpu/pull/2647)\n\n#### Metal\n\n- metal: check if in the main thread when calling `create_surface` by @jinleili in [#2736](https://github.com/gfx-rs/wgpu/pull/2736)\n\n#### Hal\n\n- limit binding sizes to i32 by @kvark in [#2363](https://github.com/gfx-rs/wgpu/pull/2363)\n\n### Bug Fixes\n\n#### General\n\n- Fix trac(y/ing) compile issue by @cwfitzgerald in [#2333](https://github.com/gfx-rs/wgpu/pull/2333)\n- Improve detection and validation of cubemap views by @kvark in [#2331](https://github.com/gfx-rs/wgpu/pull/2331)\n- Don't create array layer trackers for 3D textures. by @ElectronicRU in [#2348](https://github.com/gfx-rs/wgpu/pull/2348)\n- Limit 1D texture mips to 1 by @kvark in [#2374](https://github.com/gfx-rs/wgpu/pull/2374)\n- Texture format MSAA capabilities by @kvark in [#2377](https://github.com/gfx-rs/wgpu/pull/2377)\n- Fix write_buffer to surface texture @kvark in [#2385](https://github.com/gfx-rs/wgpu/pull/2385)\n- Improve some error messages by @cwfitzgerald in [#2446](https://github.com/gfx-rs/wgpu/pull/2446)\n- Don't recycle indices that reach EOL by @kvark in [#2462](https://github.com/gfx-rs/wgpu/pull/2462)\n- Validated render usages for 3D textures by @kvark in [#2482](https://github.com/gfx-rs/wgpu/pull/2482)\n- Wrap all validation logs with catch_unwinds by @cwfitzgerald in [#2511](https://github.com/gfx-rs/wgpu/pull/2511)\n- Fix clippy lints by @a1phyr in [#2560](https://github.com/gfx-rs/wgpu/pull/2560)\n- Free the raw device when `wgpu::Device` is dropped. by @jimblandy in [#2567](https://github.com/gfx-rs/wgpu/pull/2567)\n- wgpu-core: Register new pipelines with device's tracker. by @jimblandy in [#2565](https://github.com/gfx-rs/wgpu/pull/2565)\n- impl Debug for StagingBelt by @kpreid in [#2572](https://github.com/gfx-rs/wgpu/pull/2572)\n- Use fully qualified syntax for some calls. by @jimblandy in [#2655](https://github.com/gfx-rs/wgpu/pull/2655)\n- fix: panic in `Storage::get` by @SparkyPotato in [#2657](https://github.com/gfx-rs/wgpu/pull/2657)\n- Report invalid pipelines in render bundles as errors, not panics. by @jimblandy in [#2666](https://github.com/gfx-rs/wgpu/pull/2666)\n- Perform \"valid to use with\" checks when recording render bundles. by @jimblandy in [#2690](https://github.com/gfx-rs/wgpu/pull/2690)\n- Stop using storage usage for sampling by @cwfitzgerald in [#2703](https://github.com/gfx-rs/wgpu/pull/2703)\n- Track depth and stencil writability separately. by @jimblandy in [#2693](https://github.com/gfx-rs/wgpu/pull/2693)\n- Improve InvalidScissorRect error message by @jinleili in [#2713](https://github.com/gfx-rs/wgpu/pull/2713)\n- Improve InvalidViewport error message by @jinleili in [#2723](https://github.com/gfx-rs/wgpu/pull/2723)\n- Don't dirty the vertex buffer for stride/rate changes on bundles. by @jimblandy in [#2744](https://github.com/gfx-rs/wgpu/pull/2744)\n- Clean up render bundle index buffer tracking. by @jimblandy in [#2743](https://github.com/gfx-rs/wgpu/pull/2743)\n- Improve read-write and read-only texture storage error message by @jinleili in [#2745](https://github.com/gfx-rs/wgpu/pull/2745)\n- Change `WEBGPU_TEXTURE_FORMAT_SUPPORT` to `1 << 14` instead of `1 << 15` by @expenses in [#2772](https://github.com/gfx-rs/wgpu/pull/2772)\n- fix BufferMapCallbackC & SubmittedWorkDoneClosureC by @rajveermalviya in [#2787](https://github.com/gfx-rs/wgpu/pull/2787)\n- Fix formatting of `TextureDimensionError::LimitExceeded`. by @kpreid in [#2799](https://github.com/gfx-rs/wgpu/pull/2799)\n- Remove redundant `#[cfg]` conditions from `backend/direct.rs`. by @jimblandy in [#2811](https://github.com/gfx-rs/wgpu/pull/2811)\n- Replace android-properties with android_system_properties. by @nical in [#2815](https://github.com/gfx-rs/wgpu/pull/2815)\n- Relax render pass color_attachments validation by @jinleili in [#2778](https://github.com/gfx-rs/wgpu/pull/2778)\n- Properly Barrier Compute Indirect Buffers by @cwfitzgerald in [#2810](https://github.com/gfx-rs/wgpu/pull/2810)\n- Use numeric constants to define `wgpu_types::Features` values. by @jimblandy in [#2817](https://github.com/gfx-rs/wgpu/pull/2817)\n\n#### Metal\n\n- Fix surface texture clear view by @kvark in [#2341](https://github.com/gfx-rs/wgpu/pull/2341)\n- Set preserveInvariance for shader options by @scoopr in [#2372](https://github.com/gfx-rs/wgpu/pull/2372)\n- Properly set msl version to 2.3 if supported by @cwfitzgerald in [#2418](https://github.com/gfx-rs/wgpu/pull/2418)\n- Identify Apple M1 GPU as integrated by @superdump in [#2429](https://github.com/gfx-rs/wgpu/pull/2429)\n- Fix M1 in macOS incorrectly reports supported compressed texture formats by @superdump in [#2453](https://github.com/gfx-rs/wgpu/pull/2453)\n- Msl: support unsized array not in structures by @kvark in [#2459](https://github.com/gfx-rs/wgpu/pull/2459)\n- Fix `Surface::from_uiview` can not guarantee set correct `contentScaleFactor` by @jinleili in [#2470](https://github.com/gfx-rs/wgpu/pull/2470)\n- Set `max_buffer_size` by the correct physical device restriction by @jinleili in [#2502](https://github.com/gfx-rs/wgpu/pull/2502)\n- Refactor `PrivateCapabilities` creation by @jinleili in [#2509](https://github.com/gfx-rs/wgpu/pull/2509)\n- Refactor texture_format_capabilities function by @jinleili in [#2522](https://github.com/gfx-rs/wgpu/pull/2522)\n- Improve `push | pop_debug_marker` by @jinleili in [#2537](https://github.com/gfx-rs/wgpu/pull/2537)\n- Fix some supported limits by @jinleili in [#2608](https://github.com/gfx-rs/wgpu/pull/2608)\n- Don't skip incomplete binding resources. by @dragostis in [#2622](https://github.com/gfx-rs/wgpu/pull/2622)\n- Fix `Rgb9e5Ufloat` capabilities and `sampler_lod_average` support by @jinleili in [#2656](https://github.com/gfx-rs/wgpu/pull/2656)\n- Fix Depth24Plus | Depth24PlusStencil8 capabilities by @jinleili in [#2686](https://github.com/gfx-rs/wgpu/pull/2686)\n- Get_supported_formats: sort like the old get_preferred_format and simplify return type by @victorvde in [#2786](https://github.com/gfx-rs/wgpu/pull/2786)\n- Restrict hal::TextureUses::COLOR_TARGET condition within create_texture by @jinleili in [#2818](https://github.com/gfx-rs/wgpu/pull/2818)\n\n#### DX12\n\n- Fix UMA check by @kvark in [#2305](https://github.com/gfx-rs/wgpu/pull/2305)\n- Fix partial texture barrier not affecting stencil aspect by @Wumpf in [#2308](https://github.com/gfx-rs/wgpu/pull/2308)\n- Improve RowPitch computation by @kvark in [#2409](https://github.com/gfx-rs/wgpu/pull/2409)\n\n#### Vulkan\n\n- Explicitly set Vulkan debug message types instead of !empty() by @victorvde in [#2321](https://github.com/gfx-rs/wgpu/pull/2321)\n- Use stencil read/write masks by @kvark in [#2382](https://github.com/gfx-rs/wgpu/pull/2382)\n- Vulkan: correctly set INDEPENDENT_BLEND，make runnable on Android 8.x by @jinleili in [#2498](https://github.com/gfx-rs/wgpu/pull/2498)\n- Fix ASTC format mapping by @kvark in [#2476](https://github.com/gfx-rs/wgpu/pull/2476)\n- Support flipped Y on VK 1.1 devices by @cwfitzgerald in [#2512](https://github.com/gfx-rs/wgpu/pull/2512)\n- Fixed builtin(primitive_index) for vulkan backend by @kwillemsen in [#2716](https://github.com/gfx-rs/wgpu/pull/2716)\n- Fix PIPELINE_STATISTICS_QUERY feature support by @jinleili in [#2750](https://github.com/gfx-rs/wgpu/pull/2750)\n- Add a vulkan workaround for large buffers. by @nical in [#2796](https://github.com/gfx-rs/wgpu/pull/2796)\n\n#### GLES\n\n- Fix index buffer state not being reset in reset_state by @rparrett in [#2391](https://github.com/gfx-rs/wgpu/pull/2391)\n- Allow push constants trough emulation by @JCapucho in [#2400](https://github.com/gfx-rs/wgpu/pull/2400)\n- Hal/gles: fix dirty vertex buffers that are unused by @kvark in [#2427](https://github.com/gfx-rs/wgpu/pull/2427)\n- Fix texture description for bgra formats by @JCapucho in [#2520](https://github.com/gfx-rs/wgpu/pull/2520)\n- Remove a `log::error!` debugging statement from the gles queue by @expenses in [#2630](https://github.com/gfx-rs/wgpu/pull/2630)\n- Fix clearing depth and stencil at the same time by @expenses in [#2675](https://github.com/gfx-rs/wgpu/pull/2675)\n- Handle cubemap copies by @expenses in [#2725](https://github.com/gfx-rs/wgpu/pull/2725)\n- Allow clearing index buffers by @grovesNL in [#2740](https://github.com/gfx-rs/wgpu/pull/2740)\n- Fix buffer-texture copy for 2d arrays by @tuchs in [#2809](https://github.com/gfx-rs/wgpu/pull/2809)\n\n#### Wayland\n\n- Search for different versions of libwayland by @sh7dm in [#2336](https://github.com/gfx-rs/wgpu/pull/2336)\n\n#### WebGPU\n\n- Fix compilation on wasm32-unknown-unknown without `webgl` feature by @jakobhellermann in [#2355](https://github.com/gfx-rs/wgpu/pull/2355)\n- Solve crash on WebGPU by @cwfitzgerald in [#2807](https://github.com/gfx-rs/wgpu/pull/2807)\n\n#### Emscripten\n\n- Fix emscripten by @cwfitzgerald in [#2494](https://github.com/gfx-rs/wgpu/pull/2494)\n\n### Performance\n\n- Do texture init via clear passes when possible by @Wumpf in [#2307](https://github.com/gfx-rs/wgpu/pull/2307)\n- Bind group deduplication by @cwfitzgerald in [#2623](https://github.com/gfx-rs/wgpu/pull/2623)\n- Tracking Optimization and Rewrite by @cwfitzgerald in [#2662](https://github.com/gfx-rs/wgpu/pull/2662)\n\n### Documentation\n\n- Add defaults to new limits and correct older ones by @MultisampledNight in [#/2303](https://github.com/gfx-rs/wgpu/pull/2303)\n- Improve shader source documentation by @grovesNL in [#2315](https://github.com/gfx-rs/wgpu/pull/2315)\n- Fix typo by @rustui in [#2393](https://github.com/gfx-rs/wgpu/pull/2393)\n- Add a :star: to the feature matrix of examples README by @yutannihilation in [#2457](https://github.com/gfx-rs/wgpu/pull/2457)\n- Fix get_timestamp_period type in docs by @superdump in [#2478](https://github.com/gfx-rs/wgpu/pull/2478)\n- Fix mistake in Access doc comment by @nical in [#2479](https://github.com/gfx-rs/wgpu/pull/2479)\n- Improve shader support documentation by @cwfitzgerald in [#2501](https://github.com/gfx-rs/wgpu/pull/2501)\n- Document the gfx_select! macro. by @jimblandy in [#2555](https://github.com/gfx-rs/wgpu/pull/2555)\n- Add Windows 11 to section about DX12 by @HeavyRain266 in [#2552](https://github.com/gfx-rs/wgpu/pull/2552)\n- Document some aspects of resource tracking. by @jimblandy in [#2558](https://github.com/gfx-rs/wgpu/pull/2558)\n- Documentation for various things. by @jimblandy in [#2566](https://github.com/gfx-rs/wgpu/pull/2566)\n- Fix doc links. by @jimblandy in [#2579](https://github.com/gfx-rs/wgpu/pull/2579)\n- Fixed misspelling in documentation by @zenitopires in [#2634](https://github.com/gfx-rs/wgpu/pull/2634)\n- Update push constant docs to reflect the API by @Noxime in [#2637](https://github.com/gfx-rs/wgpu/pull/2637)\n- Exclude dependencies from documentation by @yutannihilation in [#2642](https://github.com/gfx-rs/wgpu/pull/2642)\n- Document `GpuFuture`. by @jimblandy in [#2644](https://github.com/gfx-rs/wgpu/pull/2644)\n- Document random bits and pieces. by @jimblandy in [#2651](https://github.com/gfx-rs/wgpu/pull/2651)\n- Add cross-references to each wgpu type's documentation. by @kpreid in [#2653](https://github.com/gfx-rs/wgpu/pull/2653)\n- RenderPassDescriptor: make label lifetime match doc, and make names descriptive. by @kpreid in [#2654](https://github.com/gfx-rs/wgpu/pull/2654)\n- Document `VertexStepMode`. by @jimblandy in [#2685](https://github.com/gfx-rs/wgpu/pull/2685)\n- Add links for SpirV documents. by @huandzh in [#2697](https://github.com/gfx-rs/wgpu/pull/2697)\n- Add symlink LICENSE files into crates. by @dskkato in [#2604](https://github.com/gfx-rs/wgpu/pull/2604)\n- Fix documentation links. by @jimblandy in [#2756](https://github.com/gfx-rs/wgpu/pull/2756)\n- Improve push constant documentation, including internal docs. by @jimblandy in [#2764](https://github.com/gfx-rs/wgpu/pull/2764)\n- Clarify docs for `wgpu_core`'s `Id` and `gfx_select!`. by @jimblandy in [#2766](https://github.com/gfx-rs/wgpu/pull/2766)\n- Update the Supported Platforms table in README by @jinleili in [#2770](https://github.com/gfx-rs/wgpu/pull/2770)\n- Remove depth image from readme - we don't dictate direction of depth by @cwfitzgerald in [#2812](https://github.com/gfx-rs/wgpu/pull/2812)\n\n### Dependency Updates\n\n- Update `ash` to `0.37` by @a1phyr in [#2557](https://github.com/gfx-rs/wgpu/pull/2557)\n- Update parking_lot to 0.12. by @emilio in [#2639](https://github.com/gfx-rs/wgpu/pull/2639)\n- Accept both parking-lot 0.11 and 0.12, to avoid windows-rs. by @jimblandy in [#2660](https://github.com/gfx-rs/wgpu/pull/2660)\n- Update web-sys to 0.3.58, sparse attachments support by @jinleili in [#2813](https://github.com/gfx-rs/wgpu/pull/2813)\n- Remove use of inplace_it by @mockersf in [#2889](https://github.com/gfx-rs/wgpu/pull/2889)\n\n### deno-webgpu\n\n- Clean up features in deno by @crowlKats in [#2445](https://github.com/gfx-rs/wgpu/pull/2445)\n- Dont panic when submitting same commandbuffer multiple times by @crowlKats in [#2449](https://github.com/gfx-rs/wgpu/pull/2449)\n- Handle error sources to display full errors by @crowlKats in [#2454](https://github.com/gfx-rs/wgpu/pull/2454)\n- Pull changes from deno repo by @crowlKats in [#2455](https://github.com/gfx-rs/wgpu/pull/2455)\n- Fix cts_runner by @crowlKats in [#2456](https://github.com/gfx-rs/wgpu/pull/2456)\n- Update deno_webgpu by @crowlKats in [#2539](https://github.com/gfx-rs/wgpu/pull/2539)\n- Custom op arity by @crowlKats in [#2542](https://github.com/gfx-rs/wgpu/pull/2542)\n\n### Examples\n\n- Fix conserative-raster low res target getting zero sized on resize by @Wumpf in [#2318](https://github.com/gfx-rs/wgpu/pull/2318)\n- Replace run-wasm-example.sh with aliased rust crate (xtask) by @rukai in [#2346](https://github.com/gfx-rs/wgpu/pull/2346)\n- Get cargo-run-wasm from crates.io by @rukai in [#2415](https://github.com/gfx-rs/wgpu/pull/2415)\n- Fix msaa-line example's unnecessary MSAA data store by @jinleili in [#2421](https://github.com/gfx-rs/wgpu/pull/2421)\n- Make shadow example runnable on iOS Android devices by @jinleili in [#2433](https://github.com/gfx-rs/wgpu/pull/2433)\n- Blit should only draw one triangle by @CurryPseudo in [#2474](https://github.com/gfx-rs/wgpu/pull/2474)\n- Fix wasm examples failing to compile by @Liamolucko in [#2524](https://github.com/gfx-rs/wgpu/pull/2524)\n- Fix incorrect filtering used in mipmap generation by @LaylBongers in [#2525](https://github.com/gfx-rs/wgpu/pull/2525)\n- Correct program output (\"Steps\", not \"Times\") by @skierpage in [#2535](https://github.com/gfx-rs/wgpu/pull/2535)\n- Fix resizing behaviour of hello-triangle example by @FrankenApps in [#2543](https://github.com/gfx-rs/wgpu/pull/2543)\n- Switch from `cgmath` to `glam` in examples by @a1phyr in [#2544](https://github.com/gfx-rs/wgpu/pull/2544)\n- Generate 1x1 mip level by @davidar in [#2551](https://github.com/gfx-rs/wgpu/pull/2551)\n- Wgpu/examples/shadow: Don't run on llvmpipe. by @jimblandy in [#2595](https://github.com/gfx-rs/wgpu/pull/2595)\n- Avoid new WGSL reserved words in wgpu examples. by @jimblandy in [#2606](https://github.com/gfx-rs/wgpu/pull/2606)\n- Move texture-array example over to wgsl by @cwfitzgerald in [#2618](https://github.com/gfx-rs/wgpu/pull/2618)\n- Remove the default features from wgpu-info by @jinleili in [#2753](https://github.com/gfx-rs/wgpu/pull/2753)\n- Fix bunnymark test screenshot and replace rand with nanorand by @stevenhuyn in [#2746](https://github.com/gfx-rs/wgpu/pull/2746)\n- Use FIFO swapchain in examples by @cwfitzgerald in [#2790](https://github.com/gfx-rs/wgpu/pull/2790)\n\n### Testing/Internal\n\n- Test WebGPU backend with extra features by @kvark in [#2362](https://github.com/gfx-rs/wgpu/pull/2362)\n- Lint deno_webgpu & wgpu-core by @AaronO in [#2403](https://github.com/gfx-rs/wgpu/pull/2403)\n- IdentityManager: `from_index` method is unneeded. by @jimblandy in [#2424](https://github.com/gfx-rs/wgpu/pull/2424)\n- Added id32 feature by @caiiiycuk in [#2464](https://github.com/gfx-rs/wgpu/pull/2464)\n- Update dev deps by @rukai in [#2493](https://github.com/gfx-rs/wgpu/pull/2493)\n- Use cargo nextest for running our tests by @cwfitzgerald in [#2495](https://github.com/gfx-rs/wgpu/pull/2495)\n- Many Steps Towards GL Testing Working by @cwfitzgerald in [#2504](https://github.com/gfx-rs/wgpu/pull/2504)\n- Rename ci.txt to ci.yml by @simon446 in [#2510](https://github.com/gfx-rs/wgpu/pull/2510)\n- Re-enable GL testing in CI by @cwfitzgerald in [#2508](https://github.com/gfx-rs/wgpu/pull/2508)\n- Expect shadow example to pass on GL by @kvark in [#2541](https://github.com/gfx-rs/wgpu/pull/2541)\n- Simplify implementation of RefCount and MultiRefCount. by @jimblandy in [#2548](https://github.com/gfx-rs/wgpu/pull/2548)\n- Provide a proper `new` method for `RefCount`. by @jimblandy in [#2570](https://github.com/gfx-rs/wgpu/pull/2570)\n- Add logging to LifetimeTracker::triage_suspected. by @jimblandy in [#2569](https://github.com/gfx-rs/wgpu/pull/2569)\n- wgpu-hal: Work around cbindgen bug: ignore `gles::egl` module. by @jimblandy in [#2576](https://github.com/gfx-rs/wgpu/pull/2576)\n- Specify an exact wasm-bindgen-cli version in publish.yml. by @jimblandy in [#2624](https://github.com/gfx-rs/wgpu/pull/2624)\n- Rename `timeout_us` to `timeout_ns`, to match actual units. by @jimblandy in [#2645](https://github.com/gfx-rs/wgpu/pull/2645)\n- Move set_index_buffer FFI functions back into wgpu. by @jimblandy in [#2661](https://github.com/gfx-rs/wgpu/pull/2661)\n- New function: `Global::create_buffer_error`. by @jimblandy in [#2673](https://github.com/gfx-rs/wgpu/pull/2673)\n- Actually use RenderBundleEncoder::set_bind_group in tests. by @jimblandy in [#2678](https://github.com/gfx-rs/wgpu/pull/2678)\n- Eliminate wgpu_core::commands::bundle::State::raw_dynamic_offsets. by @jimblandy in [#2684](https://github.com/gfx-rs/wgpu/pull/2684)\n- Move RenderBundleEncoder::finish's pipeline layout id into the state. by @jimblandy in [#2755](https://github.com/gfx-rs/wgpu/pull/2755)\n- Expect shader_primitive_index tests to fail on AMD RADV POLARIS12. by @jimblandy in [#2754](https://github.com/gfx-rs/wgpu/pull/2754)\n- Introduce `VertexStep`: a stride and a step mode. by @jimblandy in [#2768](https://github.com/gfx-rs/wgpu/pull/2768)\n- Increase max_outliers on wgpu water example reftest. by @jimblandy in [#2767](https://github.com/gfx-rs/wgpu/pull/2767)\n- wgpu_core::command::bundle: Consolidate pipeline and vertex state. by @jimblandy in [#2769](https://github.com/gfx-rs/wgpu/pull/2769)\n- Add type annotation to render pass code, for rust-analyzer. by @jimblandy in [#2773](https://github.com/gfx-rs/wgpu/pull/2773)\n- Expose naga span location helpers by @nical in [#2752](https://github.com/gfx-rs/wgpu/pull/2752)\n- Add create_texture_error by @nical in [#2800](https://github.com/gfx-rs/wgpu/pull/2800)\n\n## wgpu-hal 0.12.5 (2022-04-19)\n\n- fix crashes when logging in debug message callbacks\n- fix program termination when dx12 or gles error messages happen.\n- implement validation canary\n- DX12:\n  - Ignore erroneous validation error from DXGI debug layer.\n\n## wgpu-hal-0.12.4 (2022-01-24)\n\n- Metal:\n  - check for MSL-2.3\n\n## wgpu-hal-0.12.3, deno-webgpu-? (2022-01-20)\n\n- Metal:\n  - preserve vertex invariance\n- Vulkan\n  - fix stencil read/write masks\n- Gles:\n  - reset index binding properly\n- DX12:\n  - fix copies into 1D textures\n\n## wgpu-core-0.12.2, wgpu-hal-0.12.2 (2022-01-10)\n\n- fix tracy compile error\n- fix buffer binding limits beyond 2Gb\n- fix zero initialization of 3D textures\n- Metal:\n  - fix surface texture views\n- Gles:\n  - extend `libwayland` search paths\n\n## wgpu-core-0.12.1, wgpu-hal-0.12.1 (2021-12-29)\n\n- zero initialization uses now render target clears when possible (faster and doesn't enforce COPY_DST internally if not necessary)\n  - fix use of MSAA targets in WebGL\n  - fix not providing `COPY_DST` flag for textures causing assertions in some cases\n  - fix surface textures not getting zero initialized\n  - clear_texture supports now depth/stencil targets\n- error message on creating depth/stencil volume texture\n- Vulkan:\n  - fix validation error on debug message types\n- DX12:\n  - fix check for integrated GPUs\n  - fix stencil subresource transitions\n- Metal:\n  - implement push constants\n\n## wgpu-0.12 (2021-12-18)\n\n- API:\n  - `MULTIVIEW` feature\n  - `DEPTH_CLIP_CONTROL` feature to replace the old `DEPTH_CLAMP`\n  - `TEXTURE_FORMAT_16BIT_NORM` feature\n  - push/pop error scopes on the device\n  - more limits for compute shaders\n  - `SamplerBindingType` instead of booleans\n  - sampler arrays are supported by `TEXTURE_BINDING_ARRAY` feature\n  - \"glsl\" cargo feature for accepting GLSL shader code\n  - enforced MSRV-1.53\n- correctness:\n  - textures are zero-initialized\n  - lots and lots of fixes\n- validation:\n  - match texture-sampler pairs\n  - check `min_binding_size` late at draw\n  - check formats to match in `copy_texture_to_texture`\n  - allow `strip_index_format` to be none if unused\n  - check workgroup sizes and counts\n- shaders:\n  - please refer to [naga-0.8 changelog](https://github.com/gfx-rs/naga/pull/1610/files)\n  - nice error messages\n\n### wgpu-core-0.11.3, wgpu-hal-0.11.5, wgpu-0.11.1 (2021-12-01)\n\n- Core:\n  - validate device descriptor before actually creating it\n  - fix validation of texture-sampler pairs\n- Vulkan:\n  - fix running on Vulkan-1.1 instance\n  - improve detection of workaround for Intel+Nvidia on Linux\n  - fix resource limits on Vulkan-1.2\n  - fix the check for storage buffer requirement\n  - change internal semaphore logic to work around Linux+Intel bugs\n  - fix enabling extension-provided features\n- GLES:\n  - fix running on old and bogus drivers\n  - fix stale samplers on bindings change\n  - fix integer textures\n  - fix querying work group parameters\n  - fix stale PBO bindings caused by resource copies\n  - fix rendering to cubemap faces\n  - fix `Rgba16Float` format\n  - fix stale vertex attributes when changing the pipeline\n- Metal:\n  - fix window resizing for running in multiple processes\n- Web:\n  - fix `set_index_buffer` and `set_vertex_buffer` to have optional sizes\n\n### wgpu-core-0.11.2, wgpu-hal-0.11.4 (2021-10-22)\n\n- fix buffer transition barriers\n- Metal:\n  - disable RW buffers on macOS 10.11\n  - fix memory leaks in render pass descriptor\n- WebGL:\n  - fix surface reconfiguration\n- GLES:\n  - fix mapping when persistent mapping isn't supported\n  - allow presentation in Android emulator\n  - fix sRGB attributes on EGL-1.4 contexts\n\n### wgpu-hal-0.11.3 (2021-10-16)\n\n- GL:\n  - fix mapping flags and buffer initialization\n  - fix context creation when sRGB is available\n\n### wgpu-core-0.11.1 (2021-10-15)\n\n- fix bind group layout lifetime with regard to bind groups\n\n### wgpu-hal-0.11.2 (2021-10-12)\n\n- GL/WebGL: fix vertex buffer bindings with non-zero first instance\n- DX12: fix cube array view construction\n\n### wgpu-hal-0.11.1 (2021-10-09)\n\n- Vulkan: fix NV optimus detection on Linux\n- GL:\n  - fix indirect dispatch buffers\n- WebGL:\n  - fix querying storage-related limits\n  - work around a browser bug in the clear shader\n\n## wgpu-0.11 (2021-10-07)\n\n- Infrastructure:\n  - Deno WebGPU plugin is a part of the repository\n  - WebGPU CTS is ran on CI via Deno\n- API:\n  - initial WebGL support\n  - `SwapchainFrame` is removed. `SurfaceTexture::present()` needs to be called instead of dropping.\n  - better SPIR-V control flow processing\n  - ability to request a software (fallback) adapter\n  - new limits for `min_uniform_buffer_offset_alignment` and `min_storage_buffer_offset_alignment`\n  - features:\n    - new `PARTIALLY_BOUND_BINDING_ARRAY`\n    - `NON_FILL_POLYGON_MODE` is split into `POLYGON_MODE_LINE` and `POLYGON_MODE_POINT`\n- fixes:\n  - many shader-related fixes in naga-0.7\n  - fix a panic in resource cleanup happening when they are dropped on another thread\n  - Vulkan:\n    - create SPIR-V per entry point to work around driver bugs\n    - expose higher descriptor limits based on descriptor indexing capabilities\n  - GL and Vulkan:\n    - Fix renderdoc device pointers\n- optimization:\n  - on Vulkan, bounds checks are omitted if the platform can do them natively\n\n### wgpu-core-0.10.4, wgpu-0.10.2 (2021-09-23)\n\n- fix `write_texture` for array textures\n- fix closing an encoder on validation error\n- expose Metal surface creation\n- panic with an actual error message in the default handler\n\n### wgpu-hal-0.10.7 (2021-09-14)\n\n- Metal:\n  - fix stencil back-face state\n  - fix the limit on command buffer count\n\n### wgpu-hal-0.10.6 (2021-09-12)\n\n- Metal:\n  - fix stencil operations\n  - fix memory leak on M1 when out of focus\n  - fix depth clamping checks\n  - fix unsized storage buffers beyond the first\n\n### wgpu-core-0.10.3, wgpu-hal-0.10.4 (2021-09-08)\n\n- Vulkan:\n  - fix read access barriers for writable storage buffers\n  - fix shaders using cube array textures\n  - work around Linux Intel+Nvidia driver conflicts\n  - work around Adreno bug with `OpName`\n- DX12:\n  - fix storage binding offsets\n- Metal:\n  - fix compressed texture copies\n\n### wgpu-core-0.10.2, wgpu-hal-0.10.3 (2021-09-01)\n\n- All:\n  - fix querying the size of storage textures\n- Vulkan:\n  - use render pass labels\n- Metal:\n  - fix moving the surface between displays\n- DX12:\n  - enable BC compressed textures\n- GL:\n  - fix vertex-buffer and storage related limits\n\n### wgpu-core-0.10.1, wgpu-hal-0.10.2 (2021-08-24)\n\n- All:\n  - expose more formats via adapter-specific feature\n  - fix creation of depth+stencil views\n  - validate cube textures to not be used as storage\n  - fix mip level count check for storage textures\n- Metal:\n  - fix usage of work group memory\n- DX12:\n  - critical fix of pipeline layout\n\n## v0.10 (2021-08-18)\n\n- Infrastructure:\n  - `gfx-hal` is replaced by the in-house graphics abstraction `wgpu-hal`. Backends: Vulkan, Metal, D3D-12, and OpenGL ES-3.\n  - examples are tested automatically for image snapshots.\n- API:\n  - `cross` feature is removed entirely. Only Rust code from now on.\n  - processing SPIR-V inputs for later translation now requires `spirv` compile feature enabled\n  - new `Features::SPIRV_SHADER_PASSTHROUGH` run-time feature allows providing pass-through SPIR-V (orthogonal to the compile feature)\n  - several bitflag names are renamed to plural: `TextureUsage`, `BufferUsage`, `ColorWrite`.\n  - the `SwapChain` is merged into `Surface`. Returned frames are `Texture` instead of `TextureView`.\n  - renamed `TextureUsage` bits: `SAMPLED` -> `TEXTURE_BINDING`, `STORAGE` -> `STORAGE_BINDING`.\n  - renamed `InputStepMode` to `VertexStepMode`.\n  - readable storage textures are no longer a part of the base API. Only exposed via format-specific features, non-portably.\n  - implemented `Rgb9e5Ufloat` format.\n  - added limits for binding sizes, vertex data, per-stage bindings, and others.\n  - reworked downlevel flags, added downlevel limits.\n  - `resolver = \"2\"` is now required in top-level cargo manifests\n- Fixed:\n  - `Device::create_query_set` would return an error when creating exactly `QUERY_SET_MAX_QUERIES` (8192) queries. Now it only returns an error when trying to create _more_ than `QUERY_SET_MAX_QUERIES` queries.\n\n### wgpu-core-0.9.2\n\n- fix `Features::TEXTURE_SPECIFIC_FORMAT_FEATURES` not being supported for rendertargets\n\n### wgpu-core-0.9.1 (2021-07-13)\n\n- fix buffer inits delayed by a frame\n- fix query resolves to initialize buffers\n- fix pipeline statistics stride\n- fix the check for maximum query count\n\n## v0.9 (2021-06-18)\n\n- Updated:\n  - naga to `v0.5`.\n- Added:\n  - `Features::VERTEX_WRITABLE_STORAGE`.\n  - `Features::CLEAR_COMMANDS` which allows you to use `cmd_buf.clear_texture` and `cmd_buf.clear_buffer`.\n- Changed:\n  - Updated default storage buffer/image limit to `8` from `4`.\n- Fixed:\n  - `Buffer::get_mapped_range` can now have a range of zero.\n  - Fixed output spirv requiring the \"kernel\" capability.\n  - Fixed segfault due to improper drop order.\n  - Fixed incorrect dynamic stencil reference for Replace ops.\n  - Fixed tracking of temporary resources.\n  - Stopped unconditionally adding cubemap flags when the backend doesn't support cubemaps.\n- Validation:\n  - Ensure that if resources are viewed from the vertex stage, they are read only unless `Features::VERTEX_WRITABLE_STORAGE` is true.\n  - Ensure storage class (i.e. storage vs uniform) is consistent between the shader and the pipeline layout.\n  - Error when a color texture is used as a depth/stencil texture.\n  - Check that pipeline output formats are logical\n  - Added shader label to log messages if validation fails.\n- Tracing:\n  - Make renderpasses show up in the trace before they are run.\n- Docs:\n  - Fix typo in `PowerPreference::LowPower` description.\n- Player:\n  - Automatically start and stop RenderDoc captures.\n- Examples:\n  - Handle winit's unconditional exception.\n- Internal:\n  - Merged wgpu-rs and wgpu back into a single repository.\n  - The tracker was split into two different stateful/stateless trackers to reduce overhead.\n  - Added code coverage testing\n  - CI can now test on lavapipe\n  - Add missing extern \"C\" in wgpu-core on `wgpu_render_pass_execute_bundles`\n  - Fix incorrect function name `wgpu_render_pass_bundle_indexed_indirect` to `wgpu_render_bundle_draw_indexed_indirect`.\n\n### wgpu-types-0.8.1 (2021-06-08)\n\n- fix dynamic stencil reference for Replace ops\n\n### v0.8.1 (2021-05-06)\n\n- fix SPIR-V generation from WGSL, which was broken due to \"Kernel\" capability\n- validate buffer storage classes\n- Added support for storage texture arrays for Vulkan and Metal.\n\n## v0.8 (2021-04-29)\n\n- naga is used by default to translate shaders, SPIRV-Cross is optional behind `cross` feature\n- Features:\n  - buffers are zero-initialized\n  - downlevel limits for DX11/OpenGL support\n  - conservative rasterization (native-only)\n  - buffer resource indexing (native-only)\n- API adjustments to the spec:\n  - Renamed `RenderPassColorAttachmentDescriptor` to `RenderPassColorAttachment`:\n    - Renamed the `attachment` member to `view`\n  - Renamed `RenderPassDepthStencilAttachmentDescriptor` to `RenderPassDepthStencilAttachment`:\n    - Renamed the `attachment` member to `view`\n  - Renamed `VertexFormat` values\n    - Examples: `Float3` -> `Float32x3`, `Ushort2` -> `Uint16x2`\n  - Renamed the `depth` value of `Extent3d` to `depth_or_array_layers`\n  - Updated blending options in `ColorTargetState`:\n    - Renamed `BlendState` to `BlendComponent`\n    - Added `BlendState` struct to hold color and alpha blend state\n    - Moved `color_blend` and `alpha_blend` members into `blend` member\n  - Moved `clamp_depth` from `RastizerState` to `PrimitiveState`\n  - Updated `PrimitiveState`:\n    - Added `conservative` member for enabling conservative rasterization\n  - Updated copy view structs:\n    - Renamed `TextureCopyView` to `ImageCopyTexture`\n    - Renamed `TextureDataLayout` to `ImageDataLayout`\n    - Changed `bytes_per_row` and `rows_per_image` members of `ImageDataLayout` from `u32` to `Option<NonZeroU32>` <!-- wgpu-rs only -->\n  - Changed `BindingResource::Binding` from containing fields directly to containing a `BufferBinding`\n  - Added `BindingResource::BufferArray`\n- Infrastructure:\n  - switch from `tracing` to `profiling`\n  - more concrete and detailed errors\n  - API traces include the command that crashed/panicked\n  - Vulkan Portability support is removed from Apple platforms\n- Validation:\n  - texture bindings\n  - filtering of textures by samplers\n  - interpolation qualifiers\n  - allow vertex components to be underspecified\n\n### wgpu-core-0.7.1 (2021-02-25)\n\n- expose `wgc::device::queue` sub-module in public\n- fix the indexed buffer check\n- fix command allocator race condition\n\n## v0.7 (2021-01-31)\n\n- Major API changes:\n  - `RenderPipelineDescriptor`\n  - `BindingType`\n  - new `ShaderModuleDescriptor`\n  - new `RenderEncoder`\n- Features:\n  - (beta) WGSL support, including the ability to bypass SPIR-V entirely\n  - (beta) implicit bind group layout support\n  - better error messages\n  - timestamp and pipeline statistics queries\n  - ETC2 and ASTC compressed textures\n  - (beta) targeting Wasm with WebGL backend\n  - reduced dependencies\n  - Native-only:\n    - clamp-to-border addressing\n    - polygon fill modes\n    - query a format for extra capabilities\n    - `f64` support in shaders\n- Validation:\n  - shader interface\n  - render pipeline descriptor\n  - vertex buffers\n\n### wgpu-0.6.2 (2020-11-24)\n\n- don't panic in the staging belt if the channel is dropped\n\n## v0.6 (2020-08-17)\n\n- Crates:\n  - C API is moved to [another repository](https://github.com/gfx-rs/wgpu-native)\n  - `player`: standalone API replayer and tester\n- Features:\n  - Proper error handling with all functions returning `Result`\n  - Graceful handling of \"error\" objects\n  - API tracing [infrastructure](http://kvark.github.io/wgpu/debug/test/ron/2020/07/18/wgpu-api-tracing.html)\n  - uploading data with `write_buffer`/`write_texture` queue operations\n  - reusable render bundles\n  - read-only depth/stencil attachments\n  - bind group layout deduplication\n  - Cows, cows everywhere\n  - Web+Native features:\n    - Depth clamping (feature)\n    - BC texture compression\n  - Native-only features:\n    - mappable primary buffers\n    - texture array bindings\n    - push constants\n    - multi-draw indirect\n- Validation:\n  - all transfer operations\n  - all resource creation\n  - bind group matching to the layout\n  - experimental shader interface matching with naga\n\n### wgpu-core-0.5.6 (2020-07-09)\n\n- add debug markers support\n\n### wgpu-core-0.5.5 (2020-05-20)\n\n- fix destruction of adapters, swap chains, and bind group layouts\n- fix command pool leak with temporary threads\n- improve assertion messages\n- implement `From<TextureFormat>` for `TextureComponentType`\n\n### wgpu-core-0.5.4 (2020-04-24)\n\n- fix memory management of staging buffers\n\n### wgpu-core-0.5.3 (2020-04-18)\n\n- fix reading access to storage textures\n- another fix to layout transitions for swapchain images\n\n### wgpu-core-0.5.2 (2020-04-15)\n\n- fix read-only storage flags\n- fix pipeline layout life time\n- improve various assert messages\n\n### wgpu-core-0.5.1 (2020-04-10)\n\n- fix tracking of swapchain images that are used multiple times in a command buffer\n- fix tracking of initial usage of a resource across a command buffer\n\n## v0.5 (2020-04-06)\n\n- Crates:\n  - `wgpu-types`: common types between native and web targets\n  - `wgpu-core`: internal API for the native and remote wrappers\n- Features:\n  - based on gfx-hal-0.5\n  - moved from Rendy to the new `gfx-memory` and `gfx-descriptor` crates\n  - passes are now recorded on the client side. The user is also responsible to keep all resources referenced in the pass up until it ends recording.\n  - coordinate system is changed to have Y up in the rendering space\n  - revised GPU lifetime tracking of all resources\n  - revised usage tracking logic\n  - all IDs are now non-zero\n  - Mailbox present mode\n- Validation:\n  - active pipeline\n- Fixes:\n  - lots of small API changes to closely match upstream WebGPU\n  - true read-only storage bindings\n  - unmapping dropped buffers\n  - better error messages on misused swapchain frames\n\n### wgpu-core-0.4.3 (2020-01-20)\n\n- improved swap chain error handling\n\n### wgpu-core-0.4.2 (2019-12-15)\n\n- fixed render pass transitions\n\n### wgpu-core-0.4.1 (2019-11-28)\n\n- fixed depth/stencil transitions\n- fixed dynamic offset iteration\n\n## v0.4 (2019-11-03)\n\n- Platforms: removed OpenGL/WebGL support temporarily\n- Features:\n  - based on gfx-hal-0.4 with the new swapchain model\n  - exposing adapters from all available backends on a system\n  - tracking of samplers\n  - cube map support with an example\n- Validation:\n  - buffer and texture usage\n\n### wgpu-core-0.3.3 (2019-08-22)\n\n- fixed instance creation on Windows\n\n### wgpu-core-0.3.1 (2019-08-21)\n\n- fixed pipeline barriers that aren't transitions\n\n## v0.3 (2019-08-21)\n\n- Platforms: experimental OpenGL/WebGL\n- Crates:\n  - Rust API is moved out to [another repository](https://github.com/gfx-rs/wgpu-rs)\n- Features:\n  - based on gfx-hal-0.3 with help of `rendy-memory` and `rendy-descriptor`\n  - type-system-assisted deadlock prevention (for locking internal structures)\n  - texture sub-resource tracking\n  - `raw-window-handle` integration instead of `winit`\n  - multisampling with an example\n  - indirect draws and dispatches\n  - stencil masks and reference values\n  - native \"compute\" example\n  - everything implements `Debug`\n- Validation\n  - vertex/index/instance ranges at draw calls\n  - bing groups vs their expected layouts\n  - bind group buffer ranges\n  - required stencil reference, blend color\n\n### wgpu-core-0.2.6 (2019-04-04)\n\n- fixed frame acquisition GPU waits\n\n### wgpu-core-0.2.5 (2019-03-31)\n\n- fixed submission tracking\n- added support for blend colors\n- fixed bind group compatibility at the gfx-hal level\n- validating the bind groups and blend colors\n\n### wgpu-core-0.2.3 (2019-03-20)\n\n- fixed vertex format mapping\n- fixed building with \"empty\" backend on Windows\n- bumped the default descriptor pool size\n- fixed host mapping alignments\n- validating the uniform buffer offset\n\n## v0.2 (2019-03-06)\n\n- Platforms: iOS/Metal, D3D11\n- Crates:\n  - `wgpu-remote`: remoting layer for the cross-process boundary\n  - `gfx-examples`: selected gfx pre-ll examples ported over\n- Features:\n  - native example for compute\n  - \"gfx-cube\" and \"gfx-shadow\" examples\n  - copies between buffers and textures\n  - separate object identity for the remote client\n  - texture view tracking\n  - native swapchain resize support\n  - buffer mapping\n  - object index epochs\n  - comprehensive list of vertex and texture formats\n  - validation of pipeline compatibility with the pass\n- Fixes\n  - fixed resource destruction\n\n## v0.1 (2019-01-24)\n\n- Platforms: Linux/Vulkan, Windows/Vulkan, D3D12, macOS/Metal\n- Crates:\n  - `wgpu-native`: C API implementation of WebGPU, based on gfx-hal\n  - `wgpu-bindings`: auto-generated C headers\n  - `wgpu`: idiomatic Rust wrapper\n  - `examples`: native C examples\n- Features:\n- native examples for triangle rendering\n- basic native swapchain integration\n- concept of the storage hub\n- basic recording of passes and command buffers\n- submission-based lifetime tracking and command buffer recycling\n- automatic resource transitions\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n_This Code of Conduct is based on the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct), which is adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) and the [Contributor Covenant](https://www.contributor-covenant.org)._\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.\n\n## Our Standards\n\nIn this community we strive to go the extra step to look out for each other. Don’t just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they’re off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.\n\nAnd if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.\n\n* Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.\n* Please be kind and courteous. There’s no need to be mean or rude.\n* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.\n* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.\n* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups.\n* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact the maintainers immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back.\n* Do not make casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.\n* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.\n\n## Moderation\n\nThese are the policies for upholding [our community’s standards of conduct](#our-standards). If you feel that a thread needs moderation, please contact the maintainers.\n\n1. Remarks that violate the community standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner).\n2. Remarks that maintainers find inappropriate, whether listed in the code of conduct or not, are also not allowed.\n3. Maintainers will first respond to such remarks with a warning.\n4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.\n5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.\n6. Maintainers may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.\n7. If a maintainer bans someone and you think it was unjustified, please take it up with that maintainer, or with a different maintainer, in private. Complaints about bans in-channel are not allowed.\n8. Maintainers are held to a higher standard than other community members. If a maintainer creates an inappropriate situation, they should expect less leeway than others.\n\nThe enforcement policies in the code of conduct apply to all official venues, including Discord channels, GitHub repositories, the Twitter/Mastodon/Bluesky community and all other forums.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "This document is a guide for contributions to the wgpu project.\n\n## Welcome!\n\nFirst of all, welcome to the wgpu community! 👋 We're glad you want to\ncontribute. If you are unfamiliar with the wgpu project, we recommend you read\n[`GOVERNANCE.md`] for an overview of its goals, and how it's governed.\n\n## Table of Contents\n- [Documentation Overview](#documentation-overview)\n- [Talking to other humans in the wgpu project](#talking-to-other-humans-in-the-wgpu-project)\n- [\"What can I work on?\" as a new contributor](#what-can-i-work-on-as-a-new-contributor)\n- [Setting up a wgpu development environment](#setting-up-a-wgpu-development-environment)\n- [What to expect when you file an issue](#what-to-expect-when-you-file-an-issue)\n- [Pull requests](#pull-requests)\n  - [Change Ownership](#change-ownership)\n  - [LLMs (AI)](#llms-ai)\n  - [Designing new features](#designing-new-features)\n  - [Undue Burden](#undue-burden)\n  - [Large pull requests are risky](#large-pull-requests-are-risky)\n\n## Documentation Overview:\n\n- [`GOVERNANCE.md`]: An overview of the wgpu project's goals and governance.\n- [`CODE_OF_CONDUCT.md`]: The code of conduct for the wgpu project.\n- [`docs/release-checklist.md`]: Checklist for creating a new release of wgpu.\n- [`docs/review-checklist.md`]: Checklist for reviewing a pull request in wgpu.\n- [`docs/testing.md`]: Information on the test suites in wgpu and naga.\n\n[`GOVERNANCE.md`]: ./GOVERNANCE.md\n[`CODE_OF_CONDUCT.md`]: ./CODE_OF_CONDUCT.md\n[`docs/release-checklist.md`]: ./docs/release-checklist.md\n[`docs/review-checklist.md`]: ./docs/review-checklist.md\n[`docs/testing.md`]: ./docs/testing.md\n\n## Talking to other humans in the wgpu project\n\nThe wgpu project has multiple official platforms for community engagement:\n\n- The Matrix channel [`wgpu:matrix.org`](https://matrix.to/#/#wgpu:matrix.org)\n  is dedicated to informal chat about contributions the project. It is\n  particularly useful for:\n\n  - Saying hello, and introducing yourself.\n  - Validating contributions (i.e., determining if they'll be accepted,\n    ensuring your approach is correct, making sure you aren't wasting effort,\n    etc.).\n  - Setting expectations for contributions.\n\n  Notification in Matrix can sometimes be unreliable. Feel free to explicitly\n  tag people from whom you would like attention, esp. to follow-up after a day\n  or so if you do not get a response to your contributions.\n\n- The [#wgpu channel on the Rust Gamedev Discord](https://discord.gg/X3MYBNXUMJ)\n  is dedicated to information chat about both contributing and using the project. Not\n  all of the developers are on Discord, but this is monitored by the maintainers. Similar\n  in place to the Matrix channels.\n\n- [GitHub issues] are used to discuss open development questions and track work\n  the community intends to complete; this might include:\n\n  - Work that needs resolution via pull requests (see below)\n    - Bug reports\n    - Feature requests\n    - Creating new releases of crates\n  - Recording project decisions formally.\n    - Architectural discussion\n    - ???\n  - Compiling sets of other issues needed for a specific feature or use case\n    (AKA `[meta]` issues).\n\n- [GitHub pull requests]: Modifications to the contents of this repository are\n  done through pull requests.\n- `wgpu` Maintainership Meetings: Every week, the maintainership of the wgpu\n  project meets to discuss the project's direction and review ongoing work.\n  These meetings are open to the public, and you are welcome to attend. They\n  happen on Google Meet and happen on Wednesday at 11:00 US Eastern Standard\n  Time and last approximately an hour. Remember to obey the\n  [`CODE_OF_CONDUCT.md`] in the meeting.\n\n  - [Meeting Notes]\n  - [Meeting Link]\n- [GitHub discussions]: TODO: Experimentally used by some enthusiastic members\n  of our community. Not supported officially.\n  \n\n[GitHub discussions]: https://github.com/gfx-rs/wgpu/discussions\n[GitHub issues]: https://github.com/gfx-rs/wgpu/issues\n[GitHub pull requests]: https://github.com/gfx-rs/wgpu/pulls\n[Meeting Notes]: https://docs.google.com/document/d/1Z3qjy3m7eAYaTsh2n-iKxLV4Hjc6wZxgukzdQOgVH1c/edit?usp=sharing\n[Meeting Link]: https://meet.google.com/ubo-ztcw-gwf\n[`CODE_OF_CONDUCT.md`]: ./CODE_OF_CONDUCT.md\n\n### \"What can I work on?\" as a new contributor\n\nTODO\n\nWe discourage new contributors from submitting large changes or opinionated\nrefactors unless they have been specifically validated by wgpu maintainership.\nThese are likely to be rejected on basis of needing discussion before a formal\nreview.\n\n### Setting up a wgpu development environment\n\nWe use the following components in a wgpu development environment:\n\n- [A Rust toolchain][install-rust] matching the version specified in\n  [`rust-toolchain.toml`](./rust-toolchain.toml), to compile wgpu's code. If you\n  use `rustup`, this will be automatically installed when you first run a\n  `cargo` command in the repository.\n- [Taplo](https://taplo.tamasfe.dev/) to keep TOML files formatted.\n- [Vulkan SDK](https://vulkan.lunarg.com/) to provide Vulkan validation layers\n  and other Vulkan/SPIR-V tools for testing.\n\nOnce these are done, you should be ready to hack on wgpu! Drop into your\nfavorite editor, make some changes to the repository's code, and test that wgpu\nhas been changed the way you expect. Take a look at [`docs/testing.md`] for more\ninfo on testing.\n\nWhen testing your own code against your patch, we recommend\n[using a `path` dependency][path-deps] in Cargo for local testing of changes,\nand a [`git` dependency][git-deps] pointing to your own fork to share changes\nwith other contributors.\n\nOnce you are ready to request a review of your changes so they become part of\nwgpu public history, create a pull request with your changes committed to a\nbranch in your own fork of wgpu in GitHub. See documentation for that\n[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).\n\n[install-rust]: https://www.rust-lang.org/tools/install\n[path-deps]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies\n[git-deps]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories\n\n### What to expect when you file an issue\n\nTODO\n\n- Describe the filing process\n  - Link to new issue page\n  - Describe how to socialize the issue effectively\n  - Feel free to ping us if it's a blocker!\n  - Suggesting tags is helpful.\n  - Describe how the project will handle the issue\n    - Our ability to respond to an issue depends entirely on whether it is\n      _actionable_ (viz., that there is a course of action that is reasonable\n      for a volunteer to take the time to do). If it's not actionable, we\n      reserve the right to close it.\n      - Being responsive to requests for further information is important.\n      - Understanding what point in the repository's history an issue began is\n        also important. Maybe link to `git bisect` or something similar?\n      - In particular, expecting others to fix something hardware- or\n        driver-specific that current maintainership (1) can't mentor you\n        into fixing and (2) otherwise isn't being prioritized are likely to\n        be closed.\n\n### Pull requests\n\nYou can see some common things that PR reviewers are going to look for in\n[`docs/review-checklist.md`].\n\nA draft pull request is taken to be not yet ready for review and as such\nis not included in our weekly triage meetings. If you need a review before\nbeing taken out of draft, please let one of us know.\n\nThe `Assigned` field on a pull request indicates who has taken\nresponsibility for shepherding it through the review process, not who\nis responsible for authoring it. The assignee is usually the reviewer,\nbut they can also delegate the review to someone else. The intent of\nassignment is simply to ensure that pull requests don't get neglected.\n\n#### Change Ownership\n\nPR authors must be able to understand, justify, and explain all proposed\nchanges. After a PR is accepted, both the reviewer and author must\nunderstand it as a positive change to the codebase.\n\n#### LLMs (AI)\n\nUsing LLMs and AIs to generate code that is part of a contribution is allowed.\nHowever, the author submitting the PR must fully adhere to [Change Ownership](#change-ownership) rules.\nThe author is responsible for the code, regardless of how it was created. \nDo not use \"LLM generated\" as a justification for low quality code.\n\n#### Designing new features\n\nAs an open source project, wgpu wants to serve a broad audience. This\nhelps us cast a wide net for contributors, and widens the impact of\ntheir work. However, wgpu does not promise to incorporate every\nproposed feature.\n\nLarge efforts that are ultimately rejected tend to burn contributors\nout on both sides of a review. To avoid this, we strongly encourage\nyou to validate time-consuming contributions by engaging\nmaintainership before you invest yourself too heavily. Try to build a\nconsensus on the approach, including API changes, shader language\nextensions, implementation architecture, error handling, testing\nplans, benchmarking, and so on.\n\n#### Undue Burden\n\nWe reserve the right to close any PRs that cause an undue burden on\nthe maintainership. This could include, but is not limited to, [massive PRs](#large-pull-requests-are-risky),\n[LLM slop](#llms-ai), or contributions not in good faith.\n\n#### Large pull requests are risky\n\nContributors should anticipate that the larger and more complex a pull\nrequest is, the less likely it is that reviewers will accept it,\nregardless of its merits.\n\nThe wgpu project has had poor experiences with large, complex pull\nrequests:\n\n- Complex pull requests are difficult to review effectively. It is\n  common for us to debug a problem in wgpu and find that it was\n  introduced by some massive pull request that we had reviewed and\n  accepted, showing that we obviously hadn't understood it as well as\n  we'd thought.\n\n- A large, complex pull request obviously represents a significant\n  effort on the part of the author. At a personal level, it is quite\n  stressful to question its design decisions, knowing that changing\n  them will require the author to essentially reimplement the project\n  from scratch. Such pull requests make it hard for maintainers to\n  uphold their responsibility to keep wgpu maintainable. Incremental\n  changes are easier to discuss and revise without drama.\n\nThese problems are serious enough that maintainers may choose to\nreject large, complex pull requests, regardless of the value of the\nfeature or the technical merit of the code.\n\nThe problem isn't really the *size* of the pull request: a simple\nrename, with no changes to functionality, might touch hundreds of\nfiles, but be easy to review. Or, a change to naga might affect dozens\nof snapshot test output files, without being hard to understand.\n\nRather, the problem is the *complexity* of the pull request: how many\nmoving pieces does the reviewer need to assess at once? In our\nexperience, almost every large change can be pared down by separating\nout:\n\n- Preparatory refactors that are at least harmless in isolation, and\n  perhaps beneficial.\n\n- Helpers and utilities that can be used elsewhere in the code base,\n  even if they don't show their full value until the whole thing is\n  merged.\n  \n- Renames and code motion with no semantic effect, like changes to\n  types or behavior. When putting these in a separate pull request\n  would be awkward, they should at least be segregated into their own\n  commits within a pull request.\n\nBrevity for brevity's sake is not the goal. Rather, the goal is to\nhelp the reviewer anticipate the changes' consequences. When a pull\nrequest addresses only a single issue, even if it is textually large,\na trustworthy review becomes more achievable.\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"cts_runner\",\n    \"deno_webgpu\",\n\n    # default members\n    \"benches\",\n    \"examples/features\",\n    \"examples/standalone/*\",\n    \"examples/bug-repro/*\",\n    \"lock-analyzer\",\n    \"naga-cli\",\n    \"naga-test\",\n    \"naga\",\n    \"naga/fuzz\",\n    \"naga/hlsl-snapshots\",\n    \"naga/xtask\",\n    \"player\",\n    \"tests\",\n    \"wgpu-core\",\n    \"wgpu-core/platform-deps/*\",\n    \"wgpu-hal\",\n    \"wgpu-info\",\n    \"wgpu-macros\",\n    \"wgpu-naga-bridge\",\n    \"wgpu-types\",\n    \"wgpu\",\n    \"xtask\",\n]\nexclude = []\ndefault-members = [\n    \"benches\",\n    \"examples/features\",\n    \"examples/standalone/*\",\n    \"examples/bug-repro/*\",\n    \"lock-analyzer\",\n    \"naga-cli\",\n    \"naga-test\",\n    \"naga\",\n    \"naga/fuzz\",\n    \"naga/hlsl-snapshots\",\n    \"naga/xtask\",\n    \"player\",\n    \"tests\",\n    \"wgpu-core\",\n    \"wgpu-core/platform-deps/*\",\n    \"wgpu-hal\",\n    \"wgpu-info\",\n    \"wgpu-macros\",\n    \"wgpu-naga-bridge\",\n    \"wgpu-types\",\n    \"wgpu\",\n    \"xtask\",\n]\n\n[workspace.lints.clippy]\nref_as_ptr = \"warn\"\n# NOTE: clippy configuration values (disallowed-types and\n# large-error-threshold) are in other file: clippy.toml\n\n[workspace.package]\nedition = \"2021\"\nrust-version = \"1.93\"\nkeywords = [\"graphics\"]\nlicense = \"MIT OR Apache-2.0\"\nhomepage = \"https://wgpu.rs/\"\nrepository = \"https://github.com/gfx-rs/wgpu\"\nversion = \"29.0.0\"\nauthors = [\"gfx-rs developers\"]\n\n[workspace.dependencies]\nnaga = { version = \"29.0.0\", path = \"./naga\" }\nnaga-test = { path = \"./naga-test\" }\nwgpu = { version = \"29.0.0\", path = \"./wgpu\", default-features = false, features = [\n    \"std\",\n    \"serde\",\n    \"wgsl\",\n    \"vulkan\",\n    \"gles\",\n    \"dx12\",\n    \"metal\",\n    \"vulkan-portability\",\n    \"angle\",\n    \"static-dxc\",\n    \"noop\",               # This should be removed if we ever have non-test crates that depend on wgpu\n] }\nwgpu-core = { version = \"29.0.0\", path = \"./wgpu-core\" }\nwgpu-hal = { version = \"29.0.0\", path = \"./wgpu-hal\" }\nwgpu-macros = { version = \"29.0.0\", path = \"./wgpu-macros\" }\nwgpu-naga-bridge = { version = \"29.0.0\", path = \"./wgpu-naga-bridge\" }\nwgpu-test = { version = \"29.0.0\", path = \"./tests\" }\nwgpu-types = { version = \"29.0.0\", path = \"./wgpu-types\", default-features = false }\n\n# These _cannot_ have a version specified. If it does, crates.io will look\n# for a version of the package on crates when we publish naga. Path dependencies\n# are allowed through though.\nhlsl-snapshots = { path = \"naga/hlsl-snapshots\" }\n\nwgpu-core-deps-windows-linux-android = { version = \"29.0.0\", path = \"./wgpu-core/platform-deps/windows-linux-android\" }\nwgpu-core-deps-apple = { version = \"29.0.0\", path = \"./wgpu-core/platform-deps/apple\" }\nwgpu-core-deps-wasm = { version = \"29.0.0\", path = \"./wgpu-core/platform-deps/wasm\" }\nwgpu-core-deps-emscripten = { version = \"29.0.0\", path = \"./wgpu-core/platform-deps/emscripten\" }\n\nanyhow = { version = \"1.0.87\", default-features = false }\napprox = \"0.5\"\narbitrary = \"1.4.2\"\nargh = \"0.1.13\"\narrayvec = { version = \"0.7.1\", default-features = false }\nbincode = \"2\"\nbit-set = { version = \"0.9\", default-features = false }\nbit-vec = { version = \"0.9\", default-features = false }\nbitflags = \"2.9\"\nbytemuck = { version = \"1.22\", features = [\n    \"extern_crate_alloc\",\n    \"min_const_generics\",\n] }\ncargo_metadata = \"0.23\"\ncfg_aliases = \"0.2.1\"\ncfg-if = \"1\"\ncodespan-reporting = { version = \"0.13\", default-features = false }\nctor = \"0.6\"\ndiff = \"0.1\"\ndocument-features = \"0.2.10\"\nencase = \"0.12\"\nenv_logger = { version = \"0.11\", default-features = false }\nfern = \"0.7\"\nflume = \"0.12\"\nfutures-lite = \"2\"\nglam = \"0.32.0\"\nglob = \"0.3\"\nhalf = { version = \"2.5\", default-features = false } # We require 2.5 to have `Arbitrary` support.\nhashbrown = { version = \"0.16\", default-features = false, features = [\n    \"default-hasher\",\n    \"inline-more\",\n] }\nheck = \"0.5\"\nhexf-parse = \"0.2\"\nimage = { version = \"0.25\", default-features = false, features = [\"png\"] }\nindexmap = { version = \"2.11.4\", default-features = false }\nindicatif = \"0.18\"\nitertools = { version = \"0.14\" }\njobserver = \"0.1\"\nktx2 = \"0.4\"\nlibc = { version = \"0.2.172\", default-features = false }\n# See https://github.com/rust-fuzz/libfuzzer/issues/126\nlibfuzzer-sys = \">0.4.0,<=0.4.7\"\nlibloading = \"0.8\"\nlibm = { version = \"0.2.6\", default-features = false }\nlibtest-mimic = \"0.8\"\nlog = \"0.4.29\"\nmacro_rules_attribute = \"0.2\"\nnanoserde = \"0.2\"\nnanorand = { version = \"0.8\", default-features = false, features = [\"wyrand\"] }\nnoise = \"0.9\"\nnum_cpus = \"1\"\n# `half` requires 0.2.16 for `FromBytes` and `ToBytes`.\nnum-traits = { version = \"0.2.16\", default-features = false }\nnv-flip = \"0.1\"\nobj = \"0.10\"\n# NOTE: once_cell/std is *required* for some commonly-used features, selecting this per crate\nonce_cell = { version = \"1.21\", default-features = false }\n# Firefox has 3.4.0 vendored, so we allow that version in our dependencies\nordered-float = { version = \">=3, <6.0\", default-features = false }\nparking_lot = \"0.12.3\"\npetgraph = { version = \"0.8\", default-features = false }\npico-args = { version = \"0.5\", features = [\n    \"eq-separator\",\n    \"short-space-opt\",\n    \"combined-flags\",\n] }\npng = \"0.18\"\npollster = \"0.4\"\nportable-atomic = \"1.10\"\nportable-atomic-util = \"0.2.4\"\npp-rs = \"0.2.1\"\nprofiling = { version = \"1.0.1\", default-features = false }\nquote = \"1.0.38\"\nraw-window-handle = { version = \"0.6.2\", default-features = false }\nrayon = \"1.3\"\nregex-lite = \"0.1\"\nrenderdoc-sys = \"1\"\nrspirv = \"0.13\"\nron = \"0.12\"\n# NOTE: rustc-hash v2 is a completely different hasher with different performance characteristics\n# see discussion here (including with some other alternatives): https://github.com/gfx-rs/wgpu/issues/6999\n# (using default-features = false to support no-std build, avoiding any extra features that may require std::collections)\nrustc-hash = { version = \"1.1\", default-features = false }\nserde_json = \"1.0.143\"\nserde = { version = \"1.0.225\", default-features = false }\nshell-words = \"1\"\nsmallvec = \"1.14\"\nspirv = \"0.4\"\nstatic_assertions = \"1.1\"\nstrum = { version = \"0.27.1\", default-features = false, features = [\"derive\"] }\nsyn = \"2.0.98\"\ntempfile = \"3\"\ntoml = \"1.0.0\"\ntrybuild = \"1\"\ntracy-client = \"0.18\"\nthiserror = { version = \"2.0.12\", default-features = false }\nunicode-ident = \"1.0.5\"\nwalkdir = \"2.3\"\nwinit = { version = \"0.30.8\", features = [\"android-native-activity\"] }\nwhich = \"8\"\nxshell = \"0.2.2\"\n\n# Metal dependencies\nblock2 = \"0.6.2\"\nobjc2 = \"0.6.3\"\nobjc2-core-foundation = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"CFCGTypes\",\n] }\nobjc2-foundation = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"NSError\",\n    \"NSProcessInfo\",\n    \"NSRange\",\n    \"NSString\",\n] }\nobjc2-metal = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"block2\",\n    \"MTLAllocation\",\n    \"MTLBlitCommandEncoder\",\n    \"MTLBlitPass\",\n    \"MTLBuffer\",\n    \"MTLCaptureManager\",\n    \"MTLCaptureScope\",\n    \"MTLCommandBuffer\",\n    \"MTLCommandEncoder\",\n    \"MTLCommandQueue\",\n    \"MTLComputeCommandEncoder\",\n    \"MTLComputePass\",\n    \"MTLComputePipeline\",\n    \"MTLCounters\",\n    \"MTLDepthStencil\",\n    \"MTLDevice\",\n    \"MTLDrawable\",\n    \"MTLEvent\",\n    \"MTLLibrary\",\n    \"MTLPipeline\",\n    \"MTLPixelFormat\",\n    \"MTLRenderCommandEncoder\",\n    \"MTLRenderPass\",\n    \"MTLRenderPipeline\",\n    \"MTLResource\",\n    \"MTLSampler\",\n    \"MTLStageInputOutputDescriptor\",\n    \"MTLTexture\",\n    \"MTLTypes\",\n    \"MTLVertexDescriptor\",\n    \"MTLAccelerationStructure\",\n    \"MTLAccelerationStructureTypes\",\n    \"MTLAccelerationStructureCommandEncoder\",\n    \"MTLResidencySet\",\n] }\nobjc2-quartz-core = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"objc2-core-foundation\",\n    \"CALayer\",\n    \"CAMetalLayer\",\n    \"objc2-metal\",\n] }\nraw-window-metal = \"1.0\"\n\n# Vulkan dependencies\nandroid_system_properties = \"0.1.1\"\nash = \"0.38\"\ngpu-descriptor = \"0.3.2\"\n\n# DX12 dependencies\nrange-alloc = \"0.1\"\nmach-dxcompiler-rs = { version = \"0.1.4\", default-features = false } # remember to increase max_shader_model if applicable\nwindows-core = { version = \"0.62\", default-features = false }\n\n# DX12 and Vulkan dependencies\ngpu-allocator = { version = \"0.28\", default-features = false, features = [\n    \"hashbrown\",\n] }\n\n# Gles dependencies\nkhronos-egl = \"6\"\nglow = \"0.17\"\nglutin = { version = \"0.32\", default-features = false }\nglutin-winit = { version = \"0.5\", default-features = false }\nglutin_wgl_sys = \"0.6\"\n\n# DX12 and GLES dependencies\nwindows = { version = \"0.62\", default-features = false }\n\n# wasm32 dependencies\nconsole_error_panic_hook = \"0.1.5\"\nconsole_log = \"1\"\njs-sys = { version = \"0.3.85\", default-features = false }\nwasm-bindgen = { version = \"0.2.108\", default-features = false }\nwasm-bindgen-futures = { version = \"0.4.58\", default-features = false }\nwasm-bindgen-test = \"0.3\"\nweb-sys = { version = \"0.3.85\", default-features = false }\nweb-time = \"1.1.0\"\n\n# deno dependencies\ndeno_console = \"0.214.0\"\ndeno_core = \"0.355.0\"\ndeno_features = \"0.11.0\"\ndeno_url = \"0.214.0\"\ndeno_web = \"0.245.0\"\ndeno_webidl = \"0.214.0\"\ndeno_webgpu = { version = \"0.181.0\", path = \"./deno_webgpu\" }\ndeno_unsync = \"0.4.4\"\ndeno_error = \"0.7.0\"\ntokio = \"1.47\"\ntermcolor = \"1.4.1\"\n\n# android dependencies\nndk-sys = \"0.6\"\n\n# These overrides allow our examples to explicitly depend on release crates\n[patch.crates-io]\nwgpu = { path = \"./wgpu\" }\n\nenv_logger = { version = \"0.11\", git = \"https://github.com/rust-cli/env_logger.git\", rev = \"d550741\" }\nlibtest-mimic = { version = \"0.8\", git = \"https://github.com/cwfitzgerald/libtest-mimic.git\", rev = \"9979b3c\" }\n\n[profile.release]\nlto = \"thin\"\ndebug = true\n\n# Speed up image comparison even in debug builds\n[profile.dev.package.\"nv-flip-sys\"]\nopt-level = 3\n"
  },
  {
    "path": "GOVERNANCE.md",
    "content": "The **wgpu project** is a set of open-source libraries that _enables application\nauthors to write portable and performant graphics programs_. It was originally\nconceived to provide an implementation of WebGPU for Firefox as the standard\nevolved, and settled into something that could be shipped on all web browsers.\nwgpu has also enjoyed much contribution and use from other projects that require\ngraphics programming. We expect that these sorts of users will continue for the\nlifetime of project, and we embrace these contributors' needs and effort as the\nlifeblood of wgpu.\n\n## Mission\n\nThe wgpu community seeks to realize the following directives through the\nproject: it…\n\n1. …provides libraries for the WebGPU API that…\n    1. …are correct and fully conformant.\n    1. …are portable across all major platforms, that is, …\n        1. …`wgpu-core` enables JavaScript platforms to implement their own\n           proper WebGPU API.\n        1. …`wgpu` provides a WebGPU-style API library for native applications,\n           which allows shipping to all major platforms, including WebGPU's\n           JavaScript API.\n    1. …are performant enough to enable demanding applications.\n1. …serves as a platform of experimentation for:\n    1. …WebGPU standards development.\n    1. …native application authors that wish to experiment with features that\n       are not (yet?) standard.\n\n## Decision-making\n\nThe wgpu community's decision-making is influenced by the following\ngroups:\n\n* Community leadership:\n    * Connor Fitzgerald (@cwfitzgerald)\n    * Joshua Groves (@grovesNL)\n    * Andreas Reich (@wumpf)\n* Firefox's WebGPU team (@jimblandy, @nical, @teoxoy, @ErichDonGubler, and\n  others)\n* Deno's WebGPU contributors (@crowlKats)\n* Other users that ship applications based on wgpu\n\nIt is no coincidence that these groups correspond to the historically most\nactive and consistent contributors. In general, wgpu's community structure is\nmeritocratic: social influence is granted proportionate to groups' contribution\nto and stake in wgpu's mission.\n\nThese decision-making groups meet together regularly to discuss issues of\nimportance to the community, with a focus on wgpu's [mission](#Mission).\n\n---\n\nNOTE: The above is a snapshot of a perpetually changing state of affairs in the\nwgpu community. It is not a binding contract between users and decision-makers\nof the wgpu project.\n"
  },
  {
    "path": "LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# wgpu\n<img align=\"right\" width=\"20%\" src=\"logo.png\">\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/gfx-rs/wgpu/ci.yml?branch=trunk&logo=github&label=CI)](https://github.com/gfx-rs/wgpu/actions)\n[![codecov.io](https://img.shields.io/codecov/c/github/gfx-rs/wgpu?logo=codecov&logoColor=fff&label=codecov&token=84qJTesmeS)](https://codecov.io/gh/gfx-rs/wgpu)\n\n`wgpu` is a cross-platform, safe, pure-Rust graphics API. It runs natively on Vulkan, Metal, D3D12, and OpenGL; and on top of WebGL2 and WebGPU on wasm.\n\nThe API is based on the [WebGPU standard][webgpu], but is a fully native Rust library. It serves as the core of the WebGPU integration in Firefox, Servo, and Deno.\n\n## Getting Started\n\nSee our examples online at <https://wgpu.rs/examples/>. You can see the Rust sources at [examples](examples) and run them directly with `cargo run --bin wgpu-examples <example>`.\n\n### Learning `wgpu`\n\nIf you are new to `wgpu` and graphics programming, we recommend starting with [Learn Wgpu].\n<!-- Note, \"Learn Wgpu\" is using the capitalization style in their header, NOT our styling -->\n\nAdditionally, [WebGPU Fundamentals] is a tutorial for WebGPU which is very similar to our API, minus differences between Rust and Javascript.\n\n[Learn Wgpu]: https://sotrh.github.io/learn-wgpu/\n[WebGPU Fundamentals]: https://webgpufundamentals.org/\n\n### Wiki\n\nWe have a [wiki](https://github.com/gfx-rs/wgpu/wiki) which has information on useful architecture patterns, debugging tips, and more getting started information. \n\n### Need Help? Want to Contribute? \n\nThe wgpu community uses Matrix and Discord to discuss.\n\n- [![`#wgpu:matrix.org`](https://img.shields.io/static/v1?label=wgpu-devs&message=%23wgpu&color=blueviolet&logo=matrix)](https://matrix.to/#/#wgpu:matrix.org) - discussion of wgpu's development.\n- [![`#wgpu-users:matrix.org`](https://img.shields.io/static/v1?label=wgpu-users&message=%23wgpu-users&color=blueviolet&logo=matrix)](https://matrix.to/#/#wgpu-users:matrix.org) - discussion of using the library and the surrounding ecosystem.\n- [![#wgpu on the Rust Gamedev Discord](https://img.shields.io/discord/676678179678715904?logo=discord&logoColor=E0E3FF&label=%23wgpu&color=5865F2)\n](https://discord.gg/X3MYBNXUMJ) - Dedicated support channel on the Rust Gamedev Discord.\n\n\n### Other Languages\n\nTo use wgpu in C or dozens of other languages, look at [wgpu-native](https://github.com/gfx-rs/wgpu-native). These are C bindings to wgpu and has an up-to-date list of libraries bringing support to other languages. \n\n[Learn WebGPU (for C++)] is a good resource for learning how to use wgpu-native from C++.\n\n[Learn WebGPU (for C++)]: https://eliemichel.github.io/LearnWebGPU/\n[webgpu]: https://gpuweb.github.io/gpuweb/\n\n## Quick Links\n\n| Docs                  | Examples                  | Changelog               |\n|:---------------------:|:-------------------------:|:-----------------------:|\n| [v29][rel-docs]       | [v29][rel-examples]       | [v29][rel-change]       |\n| [`trunk`][trunk-docs] | [`trunk`][trunk-examples] | [`trunk`][trunk-change] |\n\nContributors are welcome! See [CONTRIBUTING.md][contrib] for more information.\n\n[rel-docs]: https://docs.rs/wgpu/\n[rel-examples]: https://github.com/gfx-rs/wgpu/tree/v29/examples#readme\n[rel-change]: https://github.com/gfx-rs/wgpu/releases\n[trunk-docs]: https://wgpu.rs/doc/wgpu/\n[trunk-examples]: https://github.com/gfx-rs/wgpu/tree/trunk/examples#readme\n[trunk-change]: https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#unreleased\n[contrib]: CONTRIBUTING.md\n\n## Supported Platforms\n\n| API    | Windows            | Linux/Android      | macOS/iOS          | Web (wasm)         |\n| ------ | ------------------ | ------------------ | ------------------ | ------------------ |\n| Vulkan |         ✅         |         ✅         |         🌋         |                    |\n| Metal  |                    |                    |         ✅         |                    |\n| DX12   |         ✅         |                    |                    |                    |\n| OpenGL |    🆗 (GL 3.3+)    |  🆗 (GL ES 3.0+)   |         📐         |    🆗 (WebGL2)     |\n| WebGPU |                    |                    |                    |         ✅         |\n\n✅ = First Class Support  \n🆗 = Downlevel/Best Effort Support  \n📐 = Requires the [ANGLE](https://github.com/gfx-rs/wgpu/wiki/Running-on-ANGLE) translation layer (GL ES 3.0 only)  \n🌋 = Requires the [MoltenVK](https://vulkan.lunarg.com/sdk/home#mac) translation layer  \n🛠️ = Unsupported, though open to contributions\n\n## Environment Variables\n\nTesting, examples, and `::from_env()` methods use a standardized set of environment variables to control wgpu's behavior.\n\n- `WGPU_BACKEND` with a comma-separated list of the backends you want to use (`vulkan`, `metal`, `dx12`, or `gl`).\n- `WGPU_ADAPTER_NAME` with a case-insensitive substring of the name of the adapter you want to use (ex. `1080` will match `NVIDIA GeForce 1080ti`).\n- `WGPU_DX12_COMPILER` with the DX12 shader compiler you wish to use (`dxc`, `static-dxc`, or `fxc`). Note that `dxc` requires `dxcompiler.dll` (min v1.8.2502) to be in the working directory, and `static-dxc` requires the `static-dxc` crate feature to be enabled. Otherwise, it will fall back to `fxc`.\n\nSee the [documentation](https://docs.rs/wgpu/latest/wgpu/index.html?search=env) for more environment variables.\n\nWhen running the CTS, use the variables `DENO_WEBGPU_ADAPTER_NAME`, `DENO_WEBGPU_BACKEND`, `DENO_WEBGPU_POWER_PREFERENCE`, and `DENO_WEBGPU_DX12_COMPILER`.\n\n## Repo Overview\n\nFor an overview of all the components in the gfx-rs ecosystem, see [the big picture](./docs/big-picture.png).\n\n## MSRV policy\n\nTL;DR: If you're using `wgpu`, our MSRV is **1.87**. If you're running our tests or examples, our MSRV is **1.93**.\n\nWe will avoid bumping the MSRV of `wgpu` without good reason, and such a change is considered breaking.\n\n<details>\n<summary> Specific Details </summary>\n\nDue to complex dependants, we have three MSRV policies:\n\n- `wgpu`'s MSRV is **1.87**\n- `wgpu-core` (and hence `wgpu-hal`, `naga`, and `wgpu-types`)'s MSRV is **1.87**.\n- The rest of the workspace has an MSRV of **1.93**.\n\nIt is enforced on CI (in \"/.github/workflows/ci.yml\") with the `WGPU_MSRV`, `CORE_MSRV`, and `REPO_MSRV` variables, respectively.\nThis version can only be upgraded in breaking releases, though we release a breaking version every three months.\n\nThe following rules apply:\n- The `wgpu-core` crate should never require an MSRV ahead of Firefox's MSRV for nightly builds, as\ndetermined by the value of `MINIMUM_RUST_VERSION` in [`python/mozboot/mozboot/util.py`][moz-msrv].\n- The `wgpu` crate should never require an MSRV ahead of Servo's MSRV, as determined by the value of\ntheir rust-version declaration in [`Cargo.toml`][servo-msrv]\n- The repository MSRV should never require an MSRV higher than `stable - 3`. For example, if stable is\nat 1.97, the repository MSRV should be no higher than 1.94. This is to allow people who are using a decently-updated\nOS-provided rust to be able to build our repository. Consider cross checking with [NixOS][nixos-msrv], though this\nis not required.\n\n[moz-msrv]: https://searchfox.org/mozilla-central/source/python/mozboot/mozboot/util.py\n[servo-msrv]: https://github.com/servo/servo/blob/main/Cargo.toml#L23\n[nixos-msrv]: https://search.nixos.org/packages?show=rustc\n\n</details>\n\n## Testing and Environment Variables\n\n[Information about testing](./docs/testing.md), including where tests of various kinds live, and how to run the tests.\n\n## Tracking the WebGPU and WGSL draft specifications\n\nThe `wgpu` crate is meant to be an idiomatic Rust translation of the [WebGPU API][webgpu spec].\nThat specification, along with its shading language, [WGSL][wgsl spec],\nare both still in the \"Working Draft\" phase,\nand while the general outlines are stable,\ndetails change frequently.\nUntil the specification is stabilized, the `wgpu` crate and the version of WGSL it implements\nwill likely differ from what is specified,\nas the implementation catches up.\n\nExactly which WGSL features `wgpu` supports depends on how you are using it:\n\n- When running as native code, `wgpu` uses [Naga][naga]\n  to translate WGSL code into the shading language of your platform's native GPU API.\n  Naga is working on catching up to the WGSL specification,\n  with [bugs][naga bugs] tracking various issues,\n  but there is no concise summary of differences from the specification.\n\n- When running in a web browser (by compilation to WebAssembly)\n  without the `\"webgl\"` feature enabled,\n  `wgpu` relies on the browser's own WebGPU implementation.\n  WGSL shaders are simply passed through to the browser,\n  so that determines which WGSL features you can use.\n\n- When running in a web browser with `wgpu`'s `\"webgl\"` feature enabled,\n  `wgpu` uses Naga to translate WGSL programs into GLSL.\n  This uses the same version of Naga as if you were running `wgpu` as native code.\n\n[webgpu spec]: https://www.w3.org/TR/webgpu/\n[wgsl spec]: https://gpuweb.github.io/gpuweb/wgsl/\n[naga]: https://github.com/gfx-rs/wgpu/tree/trunk/naga/\n[naga bugs]: https://github.com/gfx-rs/wgpu/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22naga%22\n\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# wgpu Security Policy\n\nThis document describes what is considered a security vulnerability in wgpu and\nhow vulnerabilities should be reported.\n\n\n## Vulnerability Definition\n\nWebGPU introduces a different threat model than is sometimes applied to\nGPU-related software. Unlike typical gaming or high-performance computing\napplications, where the software accessing GPU APIs is proprietary or\nobtained from a trusted developer, WebGPU makes GPU APIs available to\narbitrary web applications. In the threat model of the web, malicious\ncontent should not be able to use the GPU APIs to access data or interfaces\noutside the intended scope for interaction with web content. Therefore, `wgpu`\nseeks to prevent undefined behavior and data leaks even when its API is\nmisused, and failures to do so may be considered vulnerabilities. (This is\nalso in accordance with the Rust principle of safe vs. unsafe code, since the\n`wgpu` library exposes a safe API.)\n\nThe wgpu maintainers have discretion in assigning a severity to individual\nvulnerabilities. It is generally considered a high-severity vulnerability in\nwgpu if JavaScript or WebAssembly code, running with privileges of ordinary web\ncontent in a browser that is using wgpu to provide the WebGPU API to that\ncontent, is able to:\n\n- Access data associated with native applications other than the user agent,\n  or associated with other web origins.\n- Escape the applicable sandbox and run arbitrary code or call arbitrary system\n  APIs on the user agent host.\n- Consume system resources to the point that it is difficult to recover\n  (e.g. by closing the web page).\n\nThe wgpu Rust API offers some functionality, both supported and experimental,\nthat is not part of the WebGPU standard and is not made available in JavaScript\nenvironments using wgpu. Associated vulnerabilities may be assigned lower\nseverity than vulnerabilities that apply to a wgpu-based WebGPU implementation\nexposed to JavaScript.\n\n\n## Supported Versions\n\nThe wgpu project maintains security support for serious vulnerabilities in the\n[most recent major release](https://github.com/gfx-rs/wgpu/releases). Fixes for\nsecurity vulnerabilities found shortly after the initial release of a major\nversion may also be provided for the previous major release.\n\nMozilla provides security support for versions of wgpu used in [current\nversions of Firefox](https://whattrainisitnow.com/).\n\nThe version of wgpu that is active can be found in the Firefox repositories:\n\n- [release](https://github.com/mozilla-firefox/firefox/blob/release/gfx/wgpu_bindings/Cargo.toml),\n- [beta](https://github.com/mozilla-firefox/firefox/blob/beta/gfx/wgpu_bindings/Cargo.toml), and\n- [nightly](https://github.com/mozilla-firefox/firefox/blob/main/gfx/wgpu_bindings/Cargo.toml),\n\nWe welcome reports of security vulnerabilities in any of these released\nversions or in the latest code on the `trunk` branch.\n\n\n## Reporting a Vulnerability\n\nAlthough not all vulnerabilities in wgpu will affect Firefox, Mozilla accepts\nall vulnerability reports for wgpu and directs them appropriately. Additionally,\nMozilla serves as the CVE numbering authority for the wgpu project.\n\nTo report a security problem with wgpu, create a bug in Mozilla's Bugzilla\ninstance in the\n[Core :: Graphics :: WebGPU](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Graphics%3A+WebGPU&groups=core-security&groups=gfx-core-security)\ncomponent.\n\n**IMPORTANT: For security issues, please make sure that you check the box\nlabelled \"Many users could be harmed by this security problem\".** We advise\nthat you check this option for anything that is potentially\nsecurity-relevant, including memory safety, crashes, race conditions, and\nhandling of confidential information.\n\nReview Mozilla's [guides on bug\nreporting](https://bugzilla.mozilla.org/page.cgi?id=bug-writing.html) before\nyou open a bug.\n\nMozilla operates a [bug bounty\nprogram](https://www.mozilla.org/en-US/security/bug-bounty/). Some\nvulnerabilities in this project may be eligible.\n"
  },
  {
    "path": "benches/Cargo.toml",
    "content": "[package]\nname = \"wgpu-benchmark\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"wgpu benchmarking suite\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\npublish = false\n\n[[bench]]\nname = \"wgpu-benchmark\"\nharness = false\n\n[features]\ntracy = [\"dep:tracy-client\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n    'cfg(feature, values(\"tracy\"))',\n] }\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nanyhow.workspace = true\nbincode = { workspace = true, features = [\"serde\"] }\nbytemuck.workspace = true\nnaga = { workspace = true, features = [\n    \"deserialize\",\n    \"serialize\",\n    \"wgsl-in\",\n    \"spv-in\",\n    \"glsl-in\",\n    \"spv-out\",\n    \"msl-out\",\n    \"hlsl-out\",\n    \"glsl-out\",\n    \"wgsl-out\",\n] }\nnaga-test = { workspace = true, features = [] }\nnanorand.workspace = true\npico-args.workspace = true\npollster.workspace = true\nprofiling.workspace = true\nrayon.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\nserde_json.workspace = true\ntermcolor.workspace = true\ntracy-client = { workspace = true, optional = true }\nwgpu.workspace = true\n"
  },
  {
    "path": "benches/README.md",
    "content": "Collection of CPU benchmarks for `wgpu`.\n\nThese benchmarks are designed as a first line of defence against performance regressions and generally approximate the performance for users.\n\n## Usage\n\n```sh\n# Run all benchmarks\ncargo bench -p wgpu-benchmark\n# Run a specific benchmarks that contains \"filter\" in its name\ncargo bench -p wgpu-benchmark -- \"filter\"\n```\n\nUse `WGPU_BACKEND` and `WGPU_ADAPTER_NAME` to adjust which device the benchmarks use. [More info on env vars](../README.md#environment-variables).\n\n## Comparing Against a Baseline\n\nTo compare the current benchmarks against a baseline, you can use the `--save-baseline` and `--baseline` flags.\n\nFor example, to compare v28 against trunk, you could run the following:\n\n```sh\ngit checkout v28\n# Run the baseline benchmarks\ncargo bench -p wgpu-benchmark -- --save-baseline \"v28\"\n\ngit checkout trunk\n# Run the current benchmarks\ncargo bench -p wgpu-benchmark -- --baseline \"v28\"\n```\n\nThe current benchmarking framework was added before v28, so comparisons only work after it was added. Before that the same commands will work, but comparison will be done using `criterion`.\n\n## Integration with Profilers\n\nThe benchmarks can be run with a profiler to get more detailed information about where time is being spent.\nIntegrations are available for `tracy` and `superluminal`.\n\n#### Tracy\n\nTracy is available prebuilt for Windows on [github](https://github.com/wolfpld/tracy/releases/latest/).\n\n```sh\n# Once this is running, you can connect to it with the Tracy Profiler\ncargo bench -p wgpu-benchmark --features tracy,profiling/profile-with-tracy\n```\n\n#### Superluminal\n\nSuperluminal is a paid product for windows available [here](https://superluminal.eu/).\n\n```sh\n# This command will build the benchmarks, and display the path to the executable\ncargo bench -p wgpu-benchmark --features profiling/profile-with-superluminal -- -h\n\n# Have Superluminal run the following command (replacing with the path to the executable)\n<path_to_exe> --bench \"filter\"\n```\n\n#### `perf` and others\n\nYou can follow the same pattern as above to run the benchmarks with other profilers.\nFor example, the command line tool `perf` can be used to profile the benchmarks.\n\n```sh\n# This command will build the benchmarks, and display the path to the executable\ncargo bench -p wgpu-benchmark -- -h\n\n# Run the benchmarks with perf\nperf record <path_to_exe> --bench \"filter\"\n```\n\n## Benchmarks\n\n#### `Renderpass Encoding`\n\nThis benchmark measures the performance of recording and submitting a render pass with a large\nnumber of draw calls and resources, emulating an intense, more traditional graphics application. \nBy default it measures 10k draw calls, with 90k total resources.\n\nWithin this benchmark, both single threaded and multi-threaded recording are tested, as well as splitting\nthe render pass into multiple passes over multiple command buffers.\nIf available, it also tests a bindless approach, binding all textures at once instead of switching\nthe bind group for every draw call.\n\n#### `Computepass Encoding`\n\nThis benchmark measures the performance of recording and submitting a compute pass with a large\nnumber of dispatches and resources.\nBy default it measures 10k dispatch calls, with 60k total resources, emulating an unusually complex and sequential compute workload.\n\nWithin this benchmark, both single threaded and multi-threaded recording are tested, as well as splitting\nthe compute pass into multiple passes over multiple command buffers.\nIf available, it also tests a bindless approach, binding all resources at once instead of switching\nthe bind group for every draw call.\nTODO(https://github.com/gfx-rs/wgpu/issues/5766): The bindless version uses only 1k dispatches with 6k resources since it would be too slow for a reasonable benchmarking time otherwise.\n\n#### `Device::create_buffer`\n\nThis benchmark measures the performance of creating large buffers.\n\n#### `Device::create_bind_group`\n\nThis benchmark measures the performance of creating large bind groups of 5 to 50,000 resources.\n\n#### `naga::back`, `naga::compact`, `naga::front`, and `naga::valid`\n\nThese benchmark measures the performance of naga parsing, validating, and generating shaders. \n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/bind_groups.rs",
    "content": "use std::{num::NonZeroU32, time::Instant};\n\nuse nanorand::{Rng, WyRand};\nuse wgpu_benchmark::{iter, BenchmarkContext, SubBenchResult};\n\nuse crate::DeviceState;\n\nstruct Params {\n    max_texture_count: u32,\n    texture_counts: &'static [u32],\n}\n\n// Creating 50_000 textures takes a considerable amount of time with syncval enabled.\n//\n// We greatly reduce the number of textures for the test case to keep the runtime\n// reasonable for testing.\nconst BENCHMARK_PARAMS: Params = Params {\n    max_texture_count: 50_000,\n    texture_counts: &[5, 50, 500, 5_000, 50_000],\n};\n\nconst TEST_PARAMS: Params = Params {\n    max_texture_count: 5,\n    texture_counts: &[5],\n};\n\npub fn run_bench(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    let device_state = DeviceState::new();\n\n    if !device_state\n        .device\n        .features()\n        .contains(wgpu::Features::TEXTURE_BINDING_ARRAY)\n    {\n        anyhow::bail!(\"Device does not support required feature TEXTURE_BINDING_ARRAY\");\n    }\n\n    let params = if ctx.is_test() {\n        TEST_PARAMS\n    } else {\n        BENCHMARK_PARAMS\n    };\n\n    // Performance gets considerably worse if the resources are shuffled.\n    //\n    // This more closely matches the real-world use case where resources have no\n    // well defined usage order.\n    let mut random = WyRand::new_seed(0x8BADF00D);\n\n    let mut texture_views = Vec::with_capacity(params.max_texture_count as usize);\n    for i in 0..params.max_texture_count {\n        let texture = device_state\n            .device\n            .create_texture(&wgpu::TextureDescriptor {\n                label: Some(&format!(\"Texture {i}\")),\n                size: wgpu::Extent3d {\n                    width: 1,\n                    height: 1,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                usage: wgpu::TextureUsages::TEXTURE_BINDING,\n                view_formats: &[],\n            });\n        texture_views.push(texture.create_view(&wgpu::TextureViewDescriptor {\n            label: Some(&format!(\"Texture View {i}\")),\n            ..Default::default()\n        }));\n    }\n    random.shuffle(&mut texture_views);\n\n    let mut results = Vec::new();\n\n    for &count in params.texture_counts {\n        let bind_group_layout =\n            device_state\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                            multisampled: false,\n                        },\n                        count: Some(NonZeroU32::new(count).unwrap()),\n                    }],\n                });\n\n        let texture_view_refs: Vec<_> = texture_views.iter().take(count as usize).collect();\n\n        let name = format!(\"{count} Textures\");\n\n        let res = iter(&ctx, &name, \"bindings\", count, || {\n            let start = Instant::now();\n            let bind_group = device_state\n                .device\n                .create_bind_group(&wgpu::BindGroupDescriptor {\n                    layout: &bind_group_layout,\n                    entries: &[wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureViewArray(&texture_view_refs),\n                    }],\n                    label: None,\n                });\n\n            let time = start.elapsed();\n\n            drop(bind_group);\n            device_state\n                .device\n                .poll(wgpu::PollType::wait_indefinitely())\n                .unwrap();\n\n            time\n        });\n\n        results.push(res);\n    }\n\n    Ok(results)\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/computepass-bindless.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: binding_array<texture_2d<f32>>;\n\n@group(0) @binding(1)\n// TODO(https://github.com/gfx-rs/wgpu/issues/5765): The extra whitespace between the angle brackets is needed to workaround a parsing bug.\nvar images: binding_array<texture_storage_2d<r32float, read_write> >;\nstruct BufferElement {\n    element: vec4f,\n}\n\n@group(0) @binding(2)\nvar<storage, read_write> buffers: binding_array<BufferElement>;\n\n@compute\n@workgroup_size(16)\nfn cs_main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {\n    let offset = global_invocation_id.x; // Would be nice to offset this dynamically (it's just 0 always in the current setup)\n    \n    let idx0 = offset * 2 + 0;\n    let idx1 = offset * 2 + 1;\n    \n    let tex = textureLoad(tex[idx0], vec2u(0), 0) + textureLoad(tex[idx0], vec2u(0), 0);\n    let image = textureLoad(images[idx0], vec2u(0)) + textureLoad(images[idx1], vec2u(0));\n    buffers[idx0].element = tex.rrrr;\n    buffers[idx1].element = image.rrrr;\n}"
  },
  {
    "path": "benches/benches/wgpu-benchmark/computepass.rs",
    "content": "use std::{\n    num::{NonZeroU32, NonZeroU64},\n    time::{Duration, Instant},\n};\n\nuse nanorand::{Rng, WyRand};\nuse rayon::iter::{IntoParallelIterator, ParallelIterator};\nuse wgpu_benchmark::{iter_auto, iter_many, BenchmarkContext, LoopControl};\n\nuse crate::DeviceState;\n\nfn dispatch_count(ctx: &BenchmarkContext) -> usize {\n    // When testing we only want to run a very lightweight version of the benchmark\n    // to ensure that it does not break.\n    if ctx.is_test() {\n        8\n    } else {\n        10_000\n    }\n}\n\n// Currently bindless is _much_ slower than with regularly resources,\n// since wgpu needs to issues barriers for all resources between each dispatch for all read/write textures & buffers.\n// This is in fact so slow that it makes the benchmark unusable when we use the same amount of\n// resources as the regular benchmark.\n// For details see https://github.com/gfx-rs/wgpu/issues/5766\nfn dispatch_count_bindless(ctx: &BenchmarkContext) -> usize {\n    // On CI we only want to run a very lightweight version of the benchmark\n    // to ensure that it does not break.\n    if ctx.is_test() {\n        8\n    } else {\n        1_000\n    }\n}\n\nfn thread_count_list(ctx: &BenchmarkContext) -> &'static [usize] {\n    if ctx.is_test() {\n        &[2]\n    } else {\n        &[2, 4, 8]\n    }\n}\n\n// Must match the number of textures in the computepass.wgsl shader\nconst TEXTURES_PER_DISPATCH: usize = 2;\nconst STORAGE_TEXTURES_PER_DISPATCH: usize = 2;\nconst STORAGE_BUFFERS_PER_DISPATCH: usize = 2;\n\nconst BUFFER_SIZE: u64 = 16;\n\nstruct ComputepassState {\n    device_state: DeviceState,\n    pipeline: wgpu::ComputePipeline,\n    bind_groups: Vec<wgpu::BindGroup>,\n\n    // Bindless resources\n    bindless_bind_group: Option<wgpu::BindGroup>,\n    bindless_pipeline: Option<wgpu::ComputePipeline>,\n}\n\nimpl ComputepassState {\n    /// Create and prepare all the resources needed for the computepass benchmark.\n    fn new(ctx: &BenchmarkContext) -> Self {\n        let device_state = DeviceState::new();\n\n        let dispatch_count = dispatch_count(ctx);\n        let dispatch_count_bindless = dispatch_count_bindless(ctx);\n        let texture_count = dispatch_count * TEXTURES_PER_DISPATCH;\n        let storage_buffer_count = dispatch_count * STORAGE_BUFFERS_PER_DISPATCH;\n        let storage_texture_count = dispatch_count * STORAGE_TEXTURES_PER_DISPATCH;\n\n        let supports_bindless = device_state.device.features().contains(\n            wgpu::Features::BUFFER_BINDING_ARRAY\n                | wgpu::Features::TEXTURE_BINDING_ARRAY\n                | wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY\n                | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n        )\n        // TODO: as of writing llvmpipe segfaults the bindless benchmark on ci\n        && device_state.adapter_info.driver != \"llvmpipe\";\n\n        // Performance gets considerably worse if the resources are shuffled.\n        //\n        // This more closely matches the real-world use case where resources have no\n        // well defined usage order.\n        let mut random = WyRand::new_seed(0x8BADF00D);\n\n        let mut bind_group_layout_entries = Vec::with_capacity(TEXTURES_PER_DISPATCH);\n        for i in 0..TEXTURES_PER_DISPATCH {\n            bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry {\n                binding: i as u32,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Texture {\n                    sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                    view_dimension: wgpu::TextureViewDimension::D2,\n                    multisampled: false,\n                },\n                count: None,\n            });\n        }\n        for i in 0..STORAGE_TEXTURES_PER_DISPATCH {\n            bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry {\n                binding: (TEXTURES_PER_DISPATCH + i) as u32,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::StorageTexture {\n                    access: wgpu::StorageTextureAccess::ReadWrite,\n                    format: wgpu::TextureFormat::R32Float,\n                    view_dimension: wgpu::TextureViewDimension::D2,\n                },\n                count: None,\n            });\n        }\n        for i in 0..STORAGE_BUFFERS_PER_DISPATCH {\n            bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry {\n                binding: (TEXTURES_PER_DISPATCH + STORAGE_BUFFERS_PER_DISPATCH + i) as u32,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(BUFFER_SIZE),\n                },\n                count: None,\n            });\n        }\n\n        let bind_group_layout =\n            device_state\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &bind_group_layout_entries,\n                });\n\n        let mut texture_views = Vec::with_capacity(texture_count);\n        for i in 0..texture_count {\n            let texture = device_state\n                .device\n                .create_texture(&wgpu::TextureDescriptor {\n                    label: Some(&format!(\"Texture {i}\")),\n                    size: wgpu::Extent3d {\n                        width: 1,\n                        height: 1,\n                        depth_or_array_layers: 1,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                    usage: wgpu::TextureUsages::TEXTURE_BINDING,\n                    view_formats: &[],\n                });\n            texture_views.push(texture.create_view(&wgpu::TextureViewDescriptor {\n                label: Some(&format!(\"Texture View {i}\")),\n                ..Default::default()\n            }));\n        }\n        random.shuffle(&mut texture_views);\n        let texture_view_refs: Vec<_> = texture_views.iter().collect();\n\n        let mut storage_texture_views = Vec::with_capacity(storage_texture_count);\n        for i in 0..storage_texture_count {\n            let texture = device_state\n                .device\n                .create_texture(&wgpu::TextureDescriptor {\n                    label: Some(&format!(\"StorageTexture {i}\")),\n                    size: wgpu::Extent3d {\n                        width: 1,\n                        height: 1,\n                        depth_or_array_layers: 1,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::R32Float,\n                    usage: wgpu::TextureUsages::STORAGE_BINDING,\n                    view_formats: &[],\n                });\n            storage_texture_views.push(texture.create_view(&wgpu::TextureViewDescriptor {\n                label: Some(&format!(\"StorageTexture View {i}\")),\n                ..Default::default()\n            }));\n        }\n        random.shuffle(&mut storage_texture_views);\n        let storage_texture_view_refs: Vec<_> = storage_texture_views.iter().collect();\n\n        let mut storage_buffers = Vec::with_capacity(storage_buffer_count);\n        for i in 0..storage_buffer_count {\n            storage_buffers.push(device_state.device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(&format!(\"Buffer {i}\")),\n                size: BUFFER_SIZE,\n                usage: wgpu::BufferUsages::STORAGE,\n                mapped_at_creation: false,\n            }));\n        }\n        random.shuffle(&mut storage_buffers);\n        let storage_buffer_bindings: Vec<_> = storage_buffers\n            .iter()\n            .map(|b| b.as_entire_buffer_binding())\n            .collect();\n\n        let mut bind_groups = Vec::with_capacity(dispatch_count);\n        for dispatch_idx in 0..dispatch_count {\n            let mut entries = Vec::with_capacity(TEXTURES_PER_DISPATCH);\n            for tex_idx in 0..TEXTURES_PER_DISPATCH {\n                entries.push(wgpu::BindGroupEntry {\n                    binding: tex_idx as u32,\n                    resource: wgpu::BindingResource::TextureView(\n                        &texture_views[dispatch_idx * TEXTURES_PER_DISPATCH + tex_idx],\n                    ),\n                });\n            }\n            for tex_idx in 0..STORAGE_TEXTURES_PER_DISPATCH {\n                entries.push(wgpu::BindGroupEntry {\n                    binding: (TEXTURES_PER_DISPATCH + tex_idx) as u32,\n                    resource: wgpu::BindingResource::TextureView(\n                        &storage_texture_views\n                            [dispatch_idx * STORAGE_TEXTURES_PER_DISPATCH + tex_idx],\n                    ),\n                });\n            }\n            for buffer_idx in 0..STORAGE_BUFFERS_PER_DISPATCH {\n                entries.push(wgpu::BindGroupEntry {\n                    binding: (TEXTURES_PER_DISPATCH + STORAGE_BUFFERS_PER_DISPATCH + buffer_idx)\n                        as u32,\n                    resource: wgpu::BindingResource::Buffer(\n                        storage_buffers[dispatch_idx * STORAGE_BUFFERS_PER_DISPATCH + buffer_idx]\n                            .as_entire_buffer_binding(),\n                    ),\n                });\n            }\n\n            bind_groups.push(\n                device_state\n                    .device\n                    .create_bind_group(&wgpu::BindGroupDescriptor {\n                        label: None,\n                        layout: &bind_group_layout,\n                        entries: &entries,\n                    }),\n            );\n        }\n        random.shuffle(&mut bind_groups);\n\n        let sm = device_state\n            .device\n            .create_shader_module(wgpu::include_wgsl!(\"computepass.wgsl\"));\n\n        let pipeline_layout =\n            device_state\n                .device\n                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                    label: None,\n                    bind_group_layouts: &[Some(&bind_group_layout)],\n                    immediate_size: 0,\n                });\n\n        let pipeline =\n            device_state\n                .device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: Some(\"Compute Pipeline\"),\n                    layout: Some(&pipeline_layout),\n                    module: &sm,\n                    entry_point: Some(\"cs_main\"),\n                    compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    cache: None,\n                });\n\n        let (bindless_bind_group, bindless_pipeline) = if supports_bindless {\n            let bindless_bind_group_layout =\n                device_state\n                    .device\n                    .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                        label: None,\n                        entries: &[\n                            wgpu::BindGroupLayoutEntry {\n                                binding: 0,\n                                visibility: wgpu::ShaderStages::COMPUTE,\n                                ty: wgpu::BindingType::Texture {\n                                    sample_type: wgpu::TextureSampleType::Float {\n                                        filterable: true,\n                                    },\n                                    view_dimension: wgpu::TextureViewDimension::D2,\n                                    multisampled: false,\n                                },\n                                count: Some(NonZeroU32::new(texture_count as u32).unwrap()),\n                            },\n                            wgpu::BindGroupLayoutEntry {\n                                binding: 1,\n                                visibility: wgpu::ShaderStages::COMPUTE,\n                                ty: wgpu::BindingType::StorageTexture {\n                                    access: wgpu::StorageTextureAccess::ReadWrite,\n                                    format: wgpu::TextureFormat::R32Float,\n                                    view_dimension: wgpu::TextureViewDimension::D2,\n                                },\n                                count: Some(NonZeroU32::new(storage_texture_count as u32).unwrap()),\n                            },\n                            wgpu::BindGroupLayoutEntry {\n                                binding: 2,\n                                visibility: wgpu::ShaderStages::COMPUTE,\n                                ty: wgpu::BindingType::Buffer {\n                                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                                    has_dynamic_offset: false,\n                                    min_binding_size: std::num::NonZeroU64::new(BUFFER_SIZE),\n                                },\n                                count: Some(NonZeroU32::new(storage_buffer_count as u32).unwrap()),\n                            },\n                        ],\n                    });\n\n            let bindless_bind_group =\n                device_state\n                    .device\n                    .create_bind_group(&wgpu::BindGroupDescriptor {\n                        label: None,\n                        layout: &bindless_bind_group_layout,\n                        entries: &[\n                            wgpu::BindGroupEntry {\n                                binding: 0,\n                                resource: wgpu::BindingResource::TextureViewArray(\n                                    &texture_view_refs[..dispatch_count_bindless],\n                                ),\n                            },\n                            wgpu::BindGroupEntry {\n                                binding: 1,\n                                resource: wgpu::BindingResource::TextureViewArray(\n                                    &storage_texture_view_refs[..dispatch_count_bindless],\n                                ),\n                            },\n                            wgpu::BindGroupEntry {\n                                binding: 2,\n                                resource: wgpu::BindingResource::BufferArray(\n                                    &storage_buffer_bindings[..dispatch_count_bindless],\n                                ),\n                            },\n                        ],\n                    });\n\n            let bindless_sm = device_state\n                .device\n                .create_shader_module(wgpu::include_wgsl!(\"computepass-bindless.wgsl\"));\n\n            let bindless_pipeline_layout =\n                device_state\n                    .device\n                    .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                        label: None,\n                        bind_group_layouts: &[Some(&bindless_bind_group_layout)],\n                        immediate_size: 0,\n                    });\n\n            let bindless_pipeline =\n                device_state\n                    .device\n                    .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                        label: Some(\"Compute Pipeline bindless\"),\n                        layout: Some(&bindless_pipeline_layout),\n                        module: &bindless_sm,\n                        entry_point: Some(\"cs_main\"),\n                        compilation_options: wgpu::PipelineCompilationOptions::default(),\n                        cache: None,\n                    });\n\n            (Some(bindless_bind_group), Some(bindless_pipeline))\n        } else {\n            (None, None)\n        };\n\n        Self {\n            device_state,\n            pipeline,\n            bind_groups,\n\n            bindless_bind_group,\n            bindless_pipeline,\n        }\n    }\n\n    fn run_subpass(\n        &self,\n        ctx: &BenchmarkContext,\n        pass_number: usize,\n        total_passes: usize,\n    ) -> wgpu::CommandBuffer {\n        profiling::scope!(\"Computepass\", &format!(\"Pass {pass_number}/{total_passes}\"));\n\n        let dispatch_count = dispatch_count(ctx);\n        let dispatch_per_pass = dispatch_count / total_passes;\n\n        let mut encoder = self\n            .device_state\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n\n        let start_idx = pass_number * dispatch_per_pass;\n        let end_idx = start_idx + dispatch_per_pass;\n        for dispatch_idx in start_idx..end_idx {\n            compute_pass.set_pipeline(&self.pipeline);\n            compute_pass.set_bind_group(0, &self.bind_groups[dispatch_idx], &[]);\n            compute_pass.dispatch_workgroups(1, 1, 1);\n        }\n\n        drop(compute_pass);\n\n        encoder.finish()\n    }\n\n    fn run_bindless_pass(&self, dispatch_count_bindless: usize) -> wgpu::CommandBuffer {\n        profiling::scope!(\"Bindless Computepass\");\n\n        let mut encoder = self\n            .device_state\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n\n        compute_pass.set_pipeline(self.bindless_pipeline.as_ref().unwrap());\n        compute_pass.set_bind_group(0, Some(self.bindless_bind_group.as_ref().unwrap()), &[]);\n        for _ in 0..dispatch_count_bindless {\n            compute_pass.dispatch_workgroups(1, 1, 1);\n        }\n\n        drop(compute_pass);\n\n        encoder.finish()\n    }\n}\n\npub fn run_bench(mut ctx: BenchmarkContext) -> anyhow::Result<Vec<wgpu_benchmark::SubBenchResult>> {\n    let state = ComputepassState::new(&ctx);\n\n    ctx.default_iterations = LoopControl::Time(Duration::from_secs(3));\n\n    // This benchmark hangs on Apple Paravirtualized GPUs. No idea why.\n    if state.device_state.adapter_info.name.contains(\"Paravirtual\") {\n        anyhow::bail!(\"Benchmark unsupported on Paravirtualized GPUs\");\n    }\n\n    let dispatch_count = dispatch_count(&ctx);\n    let dispatch_count_bindless = dispatch_count_bindless(&ctx);\n\n    let mut results = Vec::new();\n\n    // Test 10k dispatch calls split up into 1, 2, 4, and 8 computepasses\n    for &cpasses in thread_count_list(&ctx) {\n        let labels = vec![\n            format!(\"Encoding ({cpasses} passes)\"),\n            format!(\"Submit ({cpasses} passes)\"),\n        ];\n\n        results.extend(iter_many(\n            &ctx,\n            labels,\n            \"dispatches\",\n            dispatch_count as _,\n            || {\n                let mut buffers: Vec<wgpu::CommandBuffer> = Vec::with_capacity(cpasses);\n                let encoding_start = Instant::now();\n                for i in 0..cpasses {\n                    buffers.push(state.run_subpass(&ctx, i, cpasses));\n                }\n                let encoding_duration = encoding_start.elapsed();\n\n                let submit_start = Instant::now();\n                state.device_state.queue.submit(buffers);\n                let submit_duration = submit_start.elapsed();\n\n                state\n                    .device_state\n                    .device\n                    .poll(wgpu::PollType::wait_indefinitely())\n                    .unwrap();\n\n                vec![encoding_duration, submit_duration]\n            },\n        ));\n    }\n\n    // Test 10k dispatch calls split up over 2, 4, and 8 threads.\n    for &threads in thread_count_list(&ctx) {\n        let labels = vec![\n            format!(\"Encoding ({threads} threads)\"),\n            format!(\"Submit ({threads} threads)\"),\n        ];\n\n        results.extend(iter_many(\n            &ctx,\n            labels,\n            \"dispatches\",\n            dispatch_count as _,\n            || {\n                let encoding_start = Instant::now();\n                let buffers = (0..threads)\n                    .into_par_iter()\n                    .map(|i| state.run_subpass(&ctx, i, threads))\n                    .collect::<Vec<_>>();\n                let encoding_duration = encoding_start.elapsed();\n\n                let submit_start = Instant::now();\n                state.device_state.queue.submit(buffers);\n                let submit_duration = submit_start.elapsed();\n\n                state\n                    .device_state\n                    .device\n                    .poll(wgpu::PollType::wait_indefinitely())\n                    .unwrap();\n\n                vec![encoding_duration, submit_duration]\n            },\n        ));\n    }\n\n    // Test 10k dispatch calls with bindless rendering.\n    if state.bindless_bind_group.is_some() {\n        let labels = vec![\n            \"Encoding (bindless)\".to_string(),\n            \"Submit (bindless)\".to_string(),\n        ];\n\n        results.extend(iter_many(\n            &ctx,\n            labels,\n            \"dispatches\",\n            dispatch_count_bindless as _,\n            || {\n                let encoding_start = Instant::now();\n                let buffer = state.run_bindless_pass(dispatch_count_bindless);\n                let encoding_duration = encoding_start.elapsed();\n\n                let submit_start = Instant::now();\n                state.device_state.queue.submit([buffer]);\n                let submit_duration = submit_start.elapsed();\n\n                state\n                    .device_state\n                    .device\n                    .poll(wgpu::PollType::wait_indefinitely())\n                    .unwrap();\n\n                vec![encoding_duration, submit_duration]\n            },\n        ));\n    }\n\n    // Test empty submit overhead with all resources\n    let texture_count = dispatch_count * TEXTURES_PER_DISPATCH;\n    let storage_buffer_count = dispatch_count * STORAGE_BUFFERS_PER_DISPATCH;\n    let storage_texture_count = dispatch_count * STORAGE_TEXTURES_PER_DISPATCH;\n\n    results.push(iter_auto(\n        &ctx,\n        &format!(\n            \"Empty Submit with {} Resources\",\n            texture_count + storage_texture_count + storage_buffer_count\n        ),\n        \"submits\",\n        1,\n        || {\n            state.device_state.queue.submit([]);\n        },\n    ));\n\n    Ok(results)\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/computepass.wgsl",
    "content": "@group(0) @binding(0)\nvar tex_0: texture_2d<f32>;\n\n@group(0) @binding(1)\nvar tex_1: texture_2d<f32>;\n\n@group(0) @binding(2)\nvar image_0: texture_storage_2d<r32float, read_write>;\n\n@group(0) @binding(3)\nvar image_1: texture_storage_2d<r32float, read_write>;\n\n@group(0) @binding(4)\nvar<storage, read_write> buffer0 : array<vec4f>;\n\n@group(0) @binding(5)\nvar<storage, read_write> buffer1 : array<vec4f>;\n\n@compute\n@workgroup_size(16)\nfn cs_main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {\n    let tex = textureLoad(tex_0, vec2u(0), 0) + textureLoad(tex_1, vec2u(0), 0);\n    let image = textureLoad(image_0, vec2u(0)) + textureLoad(image_1, vec2u(0));\n    buffer0[0] = tex.rrrr;\n    buffer1[0] = image.rrrr;\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/main.rs",
    "content": "#![cfg_attr(target_arch = \"wasm32\", no_main)]\n#![cfg(not(target_arch = \"wasm32\"))]\nuse pollster::block_on;\nuse wgpu_benchmark::Benchmark;\n\nmod bind_groups;\nmod computepass;\nmod renderpass;\nmod resource_creation;\nmod shader;\n\nstruct DeviceState {\n    adapter_info: wgpu::AdapterInfo,\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n}\n\nimpl DeviceState {\n    fn new() -> Self {\n        #[cfg(feature = \"tracy\")]\n        tracy_client::Client::start();\n\n        let base_backend = if cfg!(target_os = \"macos\") {\n            // We don't want to use Molten-VK on Mac.\n            wgpu::Backends::METAL\n        } else {\n            wgpu::Backends::all()\n        };\n\n        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {\n            backends: wgpu::Backends::from_env().unwrap_or(base_backend),\n            ..wgpu::InstanceDescriptor::new_without_display_handle_from_env()\n        });\n\n        let adapter = block_on(wgpu::util::initialize_adapter_from_env_or_default(\n            &instance, None,\n        ))\n        .unwrap();\n\n        let adapter_info = adapter.get_info();\n\n        println!(\n            \"  Using adapter: {} ({:?})\",\n            adapter_info.name, adapter_info.backend\n        );\n\n        let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n            required_features: adapter.features(),\n            required_limits: adapter.limits(),\n            memory_hints: wgpu::MemoryHints::Performance,\n            experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },\n            label: None,\n            trace: wgpu::Trace::Off,\n        }))\n        .unwrap();\n\n        Self {\n            adapter_info,\n            device,\n            queue,\n        }\n    }\n}\n\nfn main() {\n    let benchmarks = vec![\n        Benchmark {\n            name: \"Device::create_bind_group\",\n            func: bind_groups::run_bench,\n        },\n        Benchmark {\n            name: \"Device::create_buffer\",\n            func: resource_creation::run_bench,\n        },\n        Benchmark {\n            name: \"naga::front\",\n            func: shader::frontends,\n        },\n        Benchmark {\n            name: \"naga::valid\",\n            func: shader::validation,\n        },\n        Benchmark {\n            name: \"naga::compact\",\n            func: shader::compact,\n        },\n        Benchmark {\n            name: \"naga::back\",\n            func: shader::backends,\n        },\n        Benchmark {\n            name: \"Renderpass Encoding\",\n            func: renderpass::run_bench,\n        },\n        Benchmark {\n            name: \"Computepass Encoding\",\n            func: computepass::run_bench,\n        },\n    ];\n\n    wgpu_benchmark::main(benchmarks);\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/renderpass-bindless.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: binding_array<texture_2d<f32>>;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4f,\n    @location(0) @interpolate(flat) instance_index: u32,\n}\n\n@vertex\nfn vs_main(@builtin(instance_index) instance_index: u32) -> VertexOutput {\n    return VertexOutput(\n        vec4f(0.0, 0.0, 0.0, 1.0),\n        instance_index\n    );\n}\n\n@fragment\nfn fs_main(vs_in: VertexOutput) -> @location(0) vec4f {\n    return textureLoad(tex[7 * vs_in.instance_index + 0], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 1], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 2], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 3], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 4], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 5], vec2u(0), 0) +\n           textureLoad(tex[7 * vs_in.instance_index + 6], vec2u(0), 0); \n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/renderpass.rs",
    "content": "use std::{\n    num::NonZeroU32,\n    time::{Duration, Instant},\n};\n\nuse nanorand::{Rng, WyRand};\nuse rayon::iter::{IntoParallelIterator, ParallelIterator};\nuse wgpu_benchmark::{iter_many, BenchmarkContext, LoopControl};\n\nuse crate::DeviceState;\n\nfn draw_count(ctx: &BenchmarkContext) -> u32 {\n    // When testing we only want to run a very lightweight version of the benchmark\n    // to ensure that it does not break.\n    if ctx.is_test() {\n        8\n    } else {\n        10_000\n    }\n}\n\nfn thread_count_list(ctx: &BenchmarkContext) -> &'static [u32] {\n    if ctx.is_test() {\n        &[2]\n    } else {\n        &[1, 2, 4]\n    }\n}\n\n// Must match the number of textures in the renderpass.wgsl shader\nconst TEXTURES_PER_DRAW: u32 = 7;\nconst VERTEX_BUFFERS_PER_DRAW: u32 = 2;\n\nstruct RenderpassState {\n    device_state: DeviceState,\n    pipeline: wgpu::RenderPipeline,\n    bind_groups: Vec<wgpu::BindGroup>,\n    vertex_buffers: Vec<wgpu::Buffer>,\n    index_buffers: Vec<wgpu::Buffer>,\n    render_target: wgpu::TextureView,\n\n    // Bindless resources\n    bindless_bind_group: Option<wgpu::BindGroup>,\n    bindless_pipeline: Option<wgpu::RenderPipeline>,\n}\n\nimpl RenderpassState {\n    /// Create and prepare all the resources needed for the renderpass benchmark.\n    fn new(ctx: &BenchmarkContext) -> Self {\n        let device_state = DeviceState::new();\n\n        let draw_count = draw_count(ctx);\n        let vertex_buffer_count = draw_count * VERTEX_BUFFERS_PER_DRAW;\n        let texture_count = draw_count * TEXTURES_PER_DRAW;\n\n        let supports_bindless = device_state.device.features().contains(\n            wgpu::Features::TEXTURE_BINDING_ARRAY\n                | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n        ) && device_state\n            .device\n            .limits()\n            .max_sampled_textures_per_shader_stage\n            >= texture_count as _;\n\n        // Performance gets considerably worse if the resources are shuffled.\n        //\n        // This more closely matches the real-world use case where resources have no\n        // well defined usage order.\n        let mut random = WyRand::new_seed(0x8BADF00D);\n\n        let mut bind_group_layout_entries = Vec::with_capacity(TEXTURES_PER_DRAW as usize);\n        for i in 0..TEXTURES_PER_DRAW {\n            bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry {\n                binding: i,\n                visibility: wgpu::ShaderStages::FRAGMENT,\n                ty: wgpu::BindingType::Texture {\n                    sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                    view_dimension: wgpu::TextureViewDimension::D2,\n                    multisampled: false,\n                },\n                count: None,\n            });\n        }\n\n        let bind_group_layout =\n            device_state\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &bind_group_layout_entries,\n                });\n\n        let mut texture_views = Vec::with_capacity(texture_count as usize);\n        for i in 0..texture_count {\n            let texture = device_state\n                .device\n                .create_texture(&wgpu::TextureDescriptor {\n                    label: Some(&format!(\"Texture {i}\")),\n                    size: wgpu::Extent3d {\n                        width: 1,\n                        height: 1,\n                        depth_or_array_layers: 1,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                    usage: wgpu::TextureUsages::TEXTURE_BINDING,\n                    view_formats: &[],\n                });\n            texture_views.push(texture.create_view(&wgpu::TextureViewDescriptor {\n                label: Some(&format!(\"Texture View {i}\")),\n                ..Default::default()\n            }));\n        }\n        random.shuffle(&mut texture_views);\n\n        let texture_view_refs: Vec<_> = texture_views.iter().collect();\n\n        let mut bind_groups = Vec::with_capacity(draw_count as usize);\n        for draw_idx in 0..draw_count {\n            let mut entries = Vec::with_capacity(TEXTURES_PER_DRAW as usize);\n            for tex_idx in 0..TEXTURES_PER_DRAW {\n                entries.push(wgpu::BindGroupEntry {\n                    binding: tex_idx,\n                    resource: wgpu::BindingResource::TextureView(\n                        &texture_views[(draw_idx * TEXTURES_PER_DRAW + tex_idx) as usize],\n                    ),\n                });\n            }\n\n            bind_groups.push(\n                device_state\n                    .device\n                    .create_bind_group(&wgpu::BindGroupDescriptor {\n                        label: None,\n                        layout: &bind_group_layout,\n                        entries: &entries,\n                    }),\n            );\n        }\n        random.shuffle(&mut bind_groups);\n\n        let sm = device_state\n            .device\n            .create_shader_module(wgpu::include_wgsl!(\"renderpass.wgsl\"));\n\n        let pipeline_layout =\n            device_state\n                .device\n                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                    label: None,\n                    bind_group_layouts: &[Some(&bind_group_layout)],\n                    immediate_size: 0,\n                });\n\n        let mut vertex_buffers = Vec::with_capacity(vertex_buffer_count as usize);\n        for _ in 0..vertex_buffer_count {\n            vertex_buffers.push(device_state.device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size: 3 * 16,\n                usage: wgpu::BufferUsages::VERTEX,\n                mapped_at_creation: false,\n            }));\n        }\n        random.shuffle(&mut vertex_buffers);\n\n        let mut index_buffers = Vec::with_capacity(draw_count as usize);\n        for _ in 0..draw_count {\n            index_buffers.push(device_state.device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size: 3 * 4,\n                usage: wgpu::BufferUsages::INDEX,\n                mapped_at_creation: false,\n            }));\n        }\n        random.shuffle(&mut index_buffers);\n\n        let mut vertex_buffer_attributes = Vec::with_capacity(VERTEX_BUFFERS_PER_DRAW as usize);\n        for i in 0..VERTEX_BUFFERS_PER_DRAW {\n            vertex_buffer_attributes.push(wgpu::vertex_attr_array![i => Float32x4]);\n        }\n\n        let mut vertex_buffer_layouts = Vec::with_capacity(VERTEX_BUFFERS_PER_DRAW as usize);\n        for attributes in &vertex_buffer_attributes {\n            vertex_buffer_layouts.push(wgpu::VertexBufferLayout {\n                array_stride: 16,\n                step_mode: wgpu::VertexStepMode::Vertex,\n                attributes,\n            });\n        }\n\n        let pipeline =\n            device_state\n                .device\n                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                    label: None,\n                    layout: Some(&pipeline_layout),\n                    vertex: wgpu::VertexState {\n                        module: &sm,\n                        entry_point: Some(\"vs_main\"),\n                        buffers: &vertex_buffer_layouts,\n                        compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    },\n                    primitive: wgpu::PrimitiveState {\n                        topology: wgpu::PrimitiveTopology::TriangleList,\n                        strip_index_format: None,\n                        front_face: wgpu::FrontFace::Cw,\n                        cull_mode: Some(wgpu::Face::Back),\n                        polygon_mode: wgpu::PolygonMode::Fill,\n                        unclipped_depth: false,\n                        conservative: false,\n                    },\n                    depth_stencil: None,\n                    multisample: wgpu::MultisampleState::default(),\n                    fragment: Some(wgpu::FragmentState {\n                        module: &sm,\n                        entry_point: Some(\"fs_main\"),\n                        targets: &[Some(wgpu::ColorTargetState {\n                            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                            blend: None,\n                            write_mask: wgpu::ColorWrites::ALL,\n                        })],\n                        compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    }),\n                    multiview_mask: None,\n                    cache: None,\n                });\n\n        let render_target = device_state\n            .device\n            .create_texture(&wgpu::TextureDescriptor {\n                label: Some(\"Render Target\"),\n                size: wgpu::Extent3d {\n                    width: 1,\n                    height: 1,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            })\n            .create_view(&wgpu::TextureViewDescriptor::default());\n\n        let mut bindless_bind_group = None;\n        let mut bindless_pipeline = None;\n\n        if supports_bindless {\n            let bindless_bind_group_layout =\n                device_state\n                    .device\n                    .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                        label: None,\n                        entries: &[wgpu::BindGroupLayoutEntry {\n                            binding: 0,\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Texture {\n                                sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                                view_dimension: wgpu::TextureViewDimension::D2,\n                                multisampled: false,\n                            },\n                            count: Some(NonZeroU32::new(texture_count).unwrap()),\n                        }],\n                    });\n\n            bindless_bind_group = Some(device_state.device.create_bind_group(\n                &wgpu::BindGroupDescriptor {\n                    label: None,\n                    layout: &bindless_bind_group_layout,\n                    entries: &[wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureViewArray(&texture_view_refs),\n                    }],\n                },\n            ));\n\n            let bindless_shader_module = device_state\n                .device\n                .create_shader_module(wgpu::include_wgsl!(\"renderpass-bindless.wgsl\"));\n\n            let bindless_pipeline_layout =\n                device_state\n                    .device\n                    .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                        label: None,\n                        bind_group_layouts: &[Some(&bindless_bind_group_layout)],\n                        immediate_size: 0,\n                    });\n\n            bindless_pipeline = Some(device_state.device.create_render_pipeline(\n                &wgpu::RenderPipelineDescriptor {\n                    label: None,\n                    layout: Some(&bindless_pipeline_layout),\n                    vertex: wgpu::VertexState {\n                        module: &bindless_shader_module,\n                        entry_point: Some(\"vs_main\"),\n                        buffers: &vertex_buffer_layouts,\n                        compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    },\n                    primitive: wgpu::PrimitiveState {\n                        topology: wgpu::PrimitiveTopology::TriangleList,\n                        strip_index_format: None,\n                        front_face: wgpu::FrontFace::Cw,\n                        cull_mode: Some(wgpu::Face::Back),\n                        polygon_mode: wgpu::PolygonMode::Fill,\n                        unclipped_depth: false,\n                        conservative: false,\n                    },\n                    depth_stencil: None,\n                    multisample: wgpu::MultisampleState::default(),\n                    fragment: Some(wgpu::FragmentState {\n                        module: &bindless_shader_module,\n                        entry_point: Some(\"fs_main\"),\n                        targets: &[Some(wgpu::ColorTargetState {\n                            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                            blend: None,\n                            write_mask: wgpu::ColorWrites::ALL,\n                        })],\n                        compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    }),\n                    multiview_mask: None,\n                    cache: None,\n                },\n            ));\n        }\n\n        Self {\n            device_state,\n            pipeline,\n            bind_groups,\n            vertex_buffers,\n            index_buffers,\n            render_target,\n\n            bindless_bind_group,\n            bindless_pipeline,\n        }\n    }\n\n    fn run_subpass(\n        &self,\n        pass_number: u32,\n        total_passes: u32,\n        draw_count: u32,\n    ) -> wgpu::CommandBuffer {\n        profiling::scope!(\"Renderpass\", &format!(\"Pass {pass_number}/{total_passes}\"));\n\n        let draws_per_pass = draw_count / total_passes;\n\n        let mut encoder = self\n            .device_state\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &self.render_target,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            occlusion_query_set: None,\n            timestamp_writes: None,\n            depth_stencil_attachment: None,\n            multiview_mask: None,\n        });\n\n        let start_idx = pass_number * draws_per_pass;\n        let end_idx = start_idx + draws_per_pass;\n        for draw_idx in start_idx..end_idx {\n            render_pass.set_pipeline(&self.pipeline);\n            render_pass.set_bind_group(0, &self.bind_groups[draw_idx as usize], &[]);\n            for i in 0..VERTEX_BUFFERS_PER_DRAW {\n                render_pass.set_vertex_buffer(\n                    i,\n                    self.vertex_buffers[(draw_idx * VERTEX_BUFFERS_PER_DRAW + i) as usize]\n                        .slice(..),\n                );\n            }\n            render_pass.set_index_buffer(\n                self.index_buffers[draw_idx as usize].slice(..),\n                wgpu::IndexFormat::Uint32,\n            );\n            render_pass.draw_indexed(0..3, 0, 0..1);\n        }\n\n        drop(render_pass);\n\n        encoder.finish()\n    }\n\n    fn run_bindless_pass(&self, draw_count: u32) -> wgpu::CommandBuffer {\n        profiling::scope!(\"Bindless Renderpass\");\n\n        let mut encoder = self\n            .device_state\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &self.render_target,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            occlusion_query_set: None,\n            timestamp_writes: None,\n            depth_stencil_attachment: None,\n            multiview_mask: None,\n        });\n\n        render_pass.set_pipeline(self.bindless_pipeline.as_ref().unwrap());\n        render_pass.set_bind_group(0, Some(self.bindless_bind_group.as_ref().unwrap()), &[]);\n        for i in 0..VERTEX_BUFFERS_PER_DRAW {\n            render_pass.set_vertex_buffer(i, self.vertex_buffers[0].slice(..));\n        }\n        render_pass.set_index_buffer(self.index_buffers[0].slice(..), wgpu::IndexFormat::Uint32);\n\n        for draw_idx in 0..draw_count {\n            render_pass.draw_indexed(0..3, 0, draw_idx..draw_idx + 1);\n        }\n\n        drop(render_pass);\n\n        encoder.finish()\n    }\n}\n\npub fn run_bench(mut ctx: BenchmarkContext) -> anyhow::Result<Vec<wgpu_benchmark::SubBenchResult>> {\n    let state = RenderpassState::new(&ctx);\n\n    ctx.default_iterations = LoopControl::Time(Duration::from_secs(3));\n\n    // This benchmark hangs on Apple Paravirtualized GPUs. No idea why.\n    if state.device_state.adapter_info.name.contains(\"Paravirtual\") {\n        anyhow::bail!(\"Benchmark unsupported on Paravirtualized GPUs\");\n    }\n\n    let draw_count = draw_count(&ctx);\n\n    let mut results = Vec::new();\n\n    // Test 10k draw calls split up into 1, 2, 4, and 8 renderpasses\n    for &rpasses in thread_count_list(&ctx) {\n        let labels = vec![\n            format!(\"Encoding ({rpasses} passes)\"),\n            format!(\"Submit ({rpasses} passes)\"),\n        ];\n\n        results.extend(iter_many(&ctx, labels, \"draw calls\", draw_count, || {\n            let mut buffers: Vec<wgpu::CommandBuffer> = Vec::with_capacity(rpasses as usize);\n            let encoding_start = Instant::now();\n            for i in 0..rpasses {\n                buffers.push(state.run_subpass(i, rpasses, draw_count));\n            }\n            let encoding_duration = encoding_start.elapsed();\n\n            let submit_start = Instant::now();\n            state.device_state.queue.submit(buffers);\n            let submit_duration = submit_start.elapsed();\n\n            state\n                .device_state\n                .device\n                .poll(wgpu::PollType::wait_indefinitely())\n                .unwrap();\n\n            vec![encoding_duration, submit_duration]\n        }));\n    }\n\n    // Test 10k draw calls split up over 2, 4, and 8 threads.\n    for &threads in thread_count_list(&ctx) {\n        let labels = vec![\n            format!(\"Encoding ({threads} threads)\"),\n            format!(\"Submit ({threads} threads)\"),\n        ];\n\n        results.extend(iter_many(&ctx, labels, \"draw calls\", draw_count, || {\n            let encoding_start = Instant::now();\n            let buffers = (0..threads)\n                .into_par_iter()\n                .map(|i| state.run_subpass(i, threads, draw_count))\n                .collect::<Vec<_>>();\n            let encoding_duration = encoding_start.elapsed();\n\n            let submit_start = Instant::now();\n            state.device_state.queue.submit(buffers);\n            let submit_duration = submit_start.elapsed();\n\n            state\n                .device_state\n                .device\n                .poll(wgpu::PollType::wait_indefinitely())\n                .unwrap();\n\n            vec![encoding_duration, submit_duration]\n        }));\n    }\n\n    // Test 10k draw calls with bindless rendering.\n    if state.bindless_bind_group.is_some() {\n        let labels = vec![\n            \"Encoding (bindless)\".to_string(),\n            \"Submit (bindless)\".to_string(),\n        ];\n\n        results.extend(iter_many(&ctx, labels, \"draw calls\", draw_count, || {\n            let encoding_start = Instant::now();\n            let buffer = state.run_bindless_pass(draw_count);\n            let encoding_duration = encoding_start.elapsed();\n\n            let submit_start = Instant::now();\n            state.device_state.queue.submit([buffer]);\n            let submit_duration = submit_start.elapsed();\n\n            state\n                .device_state\n                .device\n                .poll(wgpu::PollType::wait_indefinitely())\n                .unwrap();\n\n            vec![encoding_duration, submit_duration]\n        }));\n    }\n\n    Ok(results)\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/renderpass.wgsl",
    "content": "@group(0) @binding(0)\nvar tex_1: texture_2d<f32>;\n\n@group(0) @binding(1)\nvar tex_2: texture_2d<f32>;\n\n@group(0) @binding(2)\nvar tex_3: texture_2d<f32>;\n\n@group(0) @binding(3)\nvar tex_4: texture_2d<f32>;\n\n@group(0) @binding(4)\nvar tex_5: texture_2d<f32>;\n\n@group(0) @binding(5)\nvar tex_6: texture_2d<f32>;\n\n@group(0) @binding(6)\nvar tex_7: texture_2d<f32>;\n\n@vertex\nfn vs_main() -> @builtin(position) vec4f {\n    return vec4f(0.0, 0.0, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4f {\n    return textureLoad(tex_1, vec2u(0), 0) +\n           textureLoad(tex_2, vec2u(0), 0) +\n           textureLoad(tex_3, vec2u(0), 0) +\n           textureLoad(tex_4, vec2u(0), 0) +\n           textureLoad(tex_5, vec2u(0), 0) +\n           textureLoad(tex_6, vec2u(0), 0) +\n           textureLoad(tex_7, vec2u(0), 0); \n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/resource_creation.rs",
    "content": "use std::time::Instant;\n\nuse rayon::iter::{IntoParallelIterator, ParallelIterator};\nuse wgpu_benchmark::{iter, BenchmarkContext, SubBenchResult};\n\nuse crate::DeviceState;\n\nfn thread_count_list(ctx: &BenchmarkContext) -> &'static [usize] {\n    if ctx.is_test() {\n        &[2]\n    } else {\n        &[1, 2, 4, 8]\n    }\n}\n\npub fn run_bench(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    let state = DeviceState::new();\n\n    const RESOURCES_TO_CREATE: usize = 8;\n\n    let mut results = Vec::new();\n    for &threads in thread_count_list(&ctx) {\n        let resources_per_thread = RESOURCES_TO_CREATE / threads;\n\n        results.push(iter(\n            &ctx,\n            &format!(\"{threads} threads\"),\n            \"buffers\",\n            RESOURCES_TO_CREATE as u32,\n            || {\n                let start = Instant::now();\n                let buffers = (0..threads)\n                    .into_par_iter()\n                    .map(|_| {\n                        (0..resources_per_thread)\n                            .map(|_| {\n                                state.device.create_buffer(&wgpu::BufferDescriptor {\n                                    label: None,\n                                    size: 256 * 1024 * 1024,\n                                    usage: wgpu::BufferUsages::COPY_DST,\n                                    mapped_at_creation: false,\n                                })\n                            })\n                            .collect::<Vec<_>>()\n                    })\n                    .collect::<Vec<_>>();\n                let duration = start.elapsed();\n\n                drop(buffers);\n\n                state.queue.submit([]);\n                state\n                    .device\n                    .poll(wgpu::PollType::wait_indefinitely())\n                    .unwrap();\n\n                duration\n            },\n        ));\n    }\n    Ok(results)\n}\n"
  },
  {
    "path": "benches/benches/wgpu-benchmark/shader.rs",
    "content": "use std::{fs, process::Command};\nuse wgpu_benchmark::{iter_auto, BenchmarkContext, SubBenchResult};\n\nconst DIR_IN: &str = concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/../naga/tests/in\");\n\nuse naga_test::*;\n\nstruct InputWithInfo {\n    inner: Input,\n    data: Vec<u8>,\n    string: Option<String>,\n    options: Parameters,\n    module: Option<naga::Module>,\n    module_info: Option<naga::valid::ModuleInfo>,\n}\nimpl From<Input> for InputWithInfo {\n    fn from(value: Input) -> Self {\n        let mut options = value.read_parameters(DIR_IN);\n        options.targets = Some(options.targets.unwrap_or(Targets::all()));\n        Self {\n            options,\n            inner: value,\n            data: Vec::new(),\n            string: None,\n            module: None,\n            module_info: None,\n        }\n    }\n}\nimpl InputWithInfo {\n    fn filename(&self) -> &str {\n        self.inner.file_name.file_name().unwrap().to_str().unwrap()\n    }\n}\n\nstruct Inputs {\n    inner: Vec<InputWithInfo>,\n}\n\nimpl Inputs {\n    #[track_caller]\n    fn from_dir(folder: &str, extension: &str) -> Self {\n        let inputs: Vec<InputWithInfo> = Input::files_in_dir(folder, &[extension], DIR_IN)\n            .map(|a| a.into())\n            .collect();\n\n        Self { inner: inputs }\n    }\n    fn bytes(&self) -> u64 {\n        self.inner\n            .iter()\n            .map(|input| input.inner.bytes(DIR_IN))\n            .sum()\n    }\n\n    fn load(&mut self) {\n        for input in &mut self.inner {\n            if !input.data.is_empty() {\n                continue;\n            }\n\n            input.data = fs::read(input.inner.input_path(DIR_IN)).unwrap_or_default();\n        }\n    }\n\n    fn load_utf8(&mut self) {\n        self.load();\n\n        for input in &mut self.inner {\n            if input.string.is_some() {\n                continue;\n            }\n\n            input.string = Some(std::str::from_utf8(&input.data).unwrap().to_string());\n        }\n    }\n\n    fn parse(&mut self) {\n        self.load_utf8();\n\n        let mut parser = naga::front::wgsl::Frontend::new();\n        for input in &mut self.inner {\n            if input.module.is_some() {\n                continue;\n            }\n\n            parser.set_options((&input.options.wgsl_in).into());\n\n            input.module = Some(parser.parse(input.string.as_ref().unwrap()).unwrap());\n        }\n    }\n\n    fn validate(&mut self) {\n        self.parse();\n\n        let mut validator = naga::valid::Validator::new(\n            naga::valid::ValidationFlags::all(),\n            // Note, this is empty, to let all backends work.\n            naga::valid::Capabilities::empty(),\n        );\n\n        for input in &mut self.inner {\n            if input.module_info.is_some() {\n                continue;\n            }\n\n            input.module_info = validator.validate(input.module.as_ref().unwrap()).ok();\n        }\n\n        self.inner.retain(|input| input.module_info.is_some());\n    }\n\n    fn is_empty(&self) -> bool {\n        self.inner.is_empty()\n    }\n}\n\nfn parse_glsl(stage: naga::ShaderStage, inputs: &Inputs) {\n    let mut parser = naga::front::glsl::Frontend::default();\n    let options = naga::front::glsl::Options {\n        stage,\n        defines: Default::default(),\n    };\n    for input in &inputs.inner {\n        parser\n            .parse(&options, &input.inner.read_source(DIR_IN, false))\n            .unwrap();\n    }\n}\n\nfn get_wgsl_inputs() -> Inputs {\n    let mut inputs: Vec<InputWithInfo> = Input::files_in_dir(\"wgsl\", &[\"wgsl\"], DIR_IN)\n        .map(|a| a.into())\n        .collect();\n\n    // remove \"large-source\" tests, they skew the results\n    inputs.retain(|input| !input.filename().contains(\"large-source\"));\n\n    assert!(!inputs.is_empty());\n\n    Inputs { inner: inputs }\n}\n\npub fn frontends(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    let mut results = Vec::new();\n\n    let mut inputs_wgsl = get_wgsl_inputs();\n\n    inputs_wgsl.parse();\n    inputs_wgsl.load_utf8();\n\n    let inputs_bin = inputs_wgsl\n        .inner\n        .iter()\n        .map(|input| {\n            bincode::serde::encode_to_vec(\n                input.module.as_ref().unwrap(),\n                bincode::config::standard(),\n            )\n            .unwrap()\n        })\n        .collect::<Vec<_>>();\n\n    results.push(iter_auto(\n        &ctx,\n        \"bincode decode\",\n        \"bytes\",\n        inputs_wgsl.bytes() as u32,\n        move || {\n            for input in inputs_bin.iter() {\n                bincode::serde::decode_from_slice::<naga::Module, _>(\n                    input,\n                    bincode::config::standard(),\n                )\n                .unwrap();\n            }\n        },\n    ));\n\n    let mut frontend = naga::front::wgsl::Frontend::new();\n\n    results.push(iter_auto(\n        &ctx,\n        \"wgsl\",\n        \"bytes\",\n        inputs_wgsl.bytes() as u32,\n        || {\n            for input in &inputs_wgsl.inner {\n                frontend.set_options((&input.options.wgsl_in).into());\n                frontend.parse(input.string.as_ref().unwrap()).unwrap();\n            }\n        },\n    ));\n\n    let inputs_spirv = Inputs::from_dir(\"spv\", \"spvasm\");\n    assert!(!inputs_spirv.is_empty());\n\n    // Assemble all the SPIR-V assembly.\n    let mut assembled_spirv = Vec::<Vec<u32>>::new();\n    'spirv: for input in &inputs_spirv.inner {\n        let output = match Command::new(\"spirv-as\")\n            .arg(input.inner.input_path(DIR_IN))\n            .arg(\"-o\")\n            .arg(\"-\")\n            .output()\n        {\n            Ok(output) => output,\n            Err(e) => {\n                eprintln!(\n                    \"Failed to execute spirv-as: {e}\\n\\\n                    spvasm benchmarks will be skipped.\\n\\\n                    spirv-as can be installed by installing the Vulkan SDK and adding \\\n                        it to your PATH.\",\n                );\n                break 'spirv;\n            }\n        };\n\n        if !output.status.success() {\n            panic!(\n                \"spirv-as failed: {}\\n{}\",\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n\n        assembled_spirv.push(bytemuck::pod_collect_to_vec(&output.stdout));\n    }\n\n    let total_bytes: u64 = assembled_spirv.iter().map(|spv| spv.len() as u64).sum();\n\n    assert!(assembled_spirv.len() == inputs_spirv.inner.len() || assembled_spirv.is_empty());\n\n    results.push(iter_auto(\n        &ctx,\n        \"spv parse\",\n        \"bytes\",\n        total_bytes as u32,\n        || {\n            for (i, input) in assembled_spirv.iter().enumerate() {\n                let params = &inputs_spirv.inner[i].options;\n                let SpirvInParameters {\n                    adjust_coordinate_space,\n                } = params.spv_in;\n\n                let parser = naga::front::spv::Frontend::new(\n                    input.iter().cloned(),\n                    &naga::front::spv::Options {\n                        adjust_coordinate_space,\n                        strict_capabilities: true,\n                        ..Default::default()\n                    },\n                );\n                parser.parse().unwrap();\n            }\n        },\n    ));\n\n    let mut inputs_vertex = Inputs::from_dir(\"glsl\", \"vert\");\n    let mut inputs_fragment = Inputs::from_dir(\"glsl\", \"frag\");\n    let mut inputs_compute = Inputs::from_dir(\"glsl\", \"comp\");\n    assert!(!inputs_vertex.is_empty());\n    assert!(!inputs_fragment.is_empty());\n    assert!(!inputs_compute.is_empty());\n\n    inputs_vertex.load_utf8();\n    inputs_fragment.load_utf8();\n    inputs_compute.load_utf8();\n\n    results.push(iter_auto(\n        &ctx,\n        \"glsl parse\",\n        \"bytes\",\n        (inputs_vertex.bytes() + inputs_fragment.bytes() + inputs_compute.bytes()) as u32,\n        || {\n            parse_glsl(naga::ShaderStage::Vertex, &inputs_vertex);\n            parse_glsl(naga::ShaderStage::Fragment, &inputs_fragment);\n            parse_glsl(naga::ShaderStage::Compute, &inputs_compute);\n        },\n    ));\n\n    Ok(results)\n}\n\npub fn validation(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    let mut results = Vec::new();\n\n    let mut inputs = get_wgsl_inputs();\n\n    inputs.parse();\n\n    let mut validator = naga::valid::Validator::new(\n        naga::valid::ValidationFlags::all(),\n        naga::valid::Capabilities::all(),\n    );\n    validator\n        .subgroup_stages(naga::valid::ShaderStages::all())\n        .subgroup_operations(naga::valid::SubgroupOperationSet::all());\n\n    results.push(iter_auto(\n        &ctx,\n        \"validation\",\n        \"bytes\",\n        inputs.bytes() as u32,\n        || {\n            for input in &inputs.inner {\n                validator.validate(input.module.as_ref().unwrap()).unwrap();\n            }\n        },\n    ));\n\n    Ok(results)\n}\n\npub fn compact(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    use naga::compact::{compact, KeepUnused};\n\n    let mut results = Vec::new();\n\n    let mut inputs = get_wgsl_inputs();\n\n    inputs.validate();\n    assert!(!inputs.is_empty());\n\n    results.push(iter_auto(\n        &ctx,\n        \"compact\",\n        \"bytes\",\n        inputs.bytes() as u32,\n        || {\n            for input in &mut inputs.inner {\n                compact(input.module.as_mut().unwrap(), KeepUnused::No);\n            }\n        },\n    ));\n\n    Ok(results)\n}\n\npub fn backends(ctx: BenchmarkContext) -> anyhow::Result<Vec<SubBenchResult>> {\n    let mut results = Vec::new();\n\n    let mut inputs = get_wgsl_inputs();\n\n    inputs.validate();\n    assert!(!inputs.is_empty());\n\n    let total_bytes = inputs.bytes() as u32;\n\n    results.push(iter_auto(&ctx, \"wgsl\", \"bytes\", total_bytes, || {\n        let mut string = String::new();\n        for input in &inputs.inner {\n            if input.options.targets.unwrap().contains(Targets::WGSL) {\n                let mut writer =\n                    naga::back::wgsl::Writer::new(&mut string, (&input.options.wgsl).into());\n                let _ = writer.write(\n                    input.module.as_ref().unwrap(),\n                    input.module_info.as_ref().unwrap(),\n                );\n                string.clear();\n            }\n        }\n    }));\n\n    results.push(iter_auto(&ctx, \"spv\", \"bytes\", total_bytes, || {\n        let mut data = Vec::new();\n        let mut writer = naga::back::spv::Writer::new(&Default::default()).unwrap();\n        for input in &inputs.inner {\n            let shared_info = WriterSharedOptions {\n                mesh_output_validation: input.options.mesh_output_validation,\n                task_limits: input.options.task_limits,\n                bounds_checks_policies: input.options.bounds_check_policies,\n            };\n            if input.options.targets.unwrap().contains(Targets::SPIRV) {\n                if input.filename().contains(\"pointer-function-arg\") {\n                    continue;\n                }\n                let opt = input.options.spv.to_options(&shared_info, None);\n                if writer.set_options(&opt).is_ok() {\n                    let _ = writer.write(\n                        input.module.as_ref().unwrap(),\n                        input.module_info.as_ref().unwrap(),\n                        None,\n                        &None,\n                        &mut data,\n                    );\n                    data.clear();\n                }\n            }\n        }\n    }));\n\n    results.push(iter_auto(\n        &ctx,\n        \"spv multiple entrypoints\",\n        \"bytes\",\n        total_bytes,\n        || {\n            let mut data = Vec::new();\n            let options = naga::back::spv::Options::default();\n            for input in &inputs.inner {\n                if input.options.targets.unwrap().contains(Targets::SPIRV) {\n                    if input.filename().contains(\"pointer-function-arg\") {\n                        continue;\n                    }\n                    let mut writer = naga::back::spv::Writer::new(&options).unwrap();\n                    let module = input.module.as_ref().unwrap();\n                    for ep in module.entry_points.iter() {\n                        let pipeline_options = naga::back::spv::PipelineOptions {\n                            shader_stage: ep.stage,\n                            entry_point: ep.name.clone(),\n                        };\n                        let _ = writer.write(\n                            input.module.as_ref().unwrap(),\n                            input.module_info.as_ref().unwrap(),\n                            Some(&pipeline_options),\n                            &None,\n                            &mut data,\n                        );\n                        data.clear();\n                    }\n                }\n            }\n        },\n    ));\n\n    results.push(iter_auto(&ctx, \"msl\", \"bytes\", total_bytes, || {\n        let mut string = String::new();\n        let options = naga::back::msl::Options::default();\n        for input in &inputs.inner {\n            if input.options.targets.unwrap().contains(Targets::METAL) {\n                let pipeline_options = naga::back::msl::PipelineOptions::default();\n                let mut writer = naga::back::msl::Writer::new(&mut string);\n                let _ = writer.write(\n                    input.module.as_ref().unwrap(),\n                    input.module_info.as_ref().unwrap(),\n                    &options,\n                    &pipeline_options,\n                );\n                string.clear();\n            }\n        }\n    }));\n\n    results.push(iter_auto(&ctx, \"hlsl\", \"bytes\", total_bytes, || {\n        let options = naga::back::hlsl::Options::default();\n        let mut string = String::new();\n        for input in &inputs.inner {\n            if input.options.targets.unwrap().contains(Targets::HLSL) {\n                let pipeline_options = Default::default();\n                let mut writer =\n                    naga::back::hlsl::Writer::new(&mut string, &options, &pipeline_options);\n                let _ = writer.write(\n                    input.module.as_ref().unwrap(),\n                    input.module_info.as_ref().unwrap(),\n                    None,\n                );\n                string.clear();\n            }\n        }\n    }));\n\n    results.push(iter_auto(\n        &ctx,\n        \"glsl multiple entrypoints\",\n        \"bytes\",\n        total_bytes,\n        || {\n            let mut string = String::new();\n            let options = naga::back::glsl::Options {\n                version: naga::back::glsl::Version::new_gles(320),\n                writer_flags: naga::back::glsl::WriterFlags::empty(),\n                binding_map: Default::default(),\n                zero_initialize_workgroup_memory: true,\n            };\n            for input in &inputs.inner {\n                if !input.options.targets.unwrap().contains(Targets::GLSL) {\n                    continue;\n                }\n                let module = input.module.as_ref().unwrap();\n                let info = input.module_info.as_ref().unwrap();\n                for ep in module.entry_points.iter() {\n                    let pipeline_options = naga::back::glsl::PipelineOptions {\n                        shader_stage: ep.stage,\n                        entry_point: ep.name.clone(),\n                        multiview: None,\n                    };\n\n                    if let Ok(mut writer) = naga::back::glsl::Writer::new(\n                        &mut string,\n                        module,\n                        info,\n                        &options,\n                        &pipeline_options,\n                        naga::proc::BoundsCheckPolicies::default(),\n                    ) {\n                        let _ = writer.write();\n                    }\n\n                    string.clear();\n                }\n            }\n        },\n    ));\n\n    Ok(results)\n}\n"
  },
  {
    "path": "benches/src/context.rs",
    "content": "use std::time::Duration;\n\n#[derive(Clone, Copy)]\npub enum LoopControl {\n    Iterations(u32),\n    Time(Duration),\n}\n\nimpl Default for LoopControl {\n    fn default() -> Self {\n        LoopControl::Time(Duration::from_secs(2))\n    }\n}\n\nimpl LoopControl {\n    pub(crate) fn finished(&self, iterations: u32, elapsed: Duration) -> bool {\n        match self {\n            LoopControl::Iterations(target) => iterations >= *target,\n            LoopControl::Time(target) => elapsed >= *target,\n        }\n    }\n}\n\npub struct BenchmarkContext {\n    pub(crate) override_iters: Option<LoopControl>,\n    pub default_iterations: LoopControl,\n    pub(crate) is_test: bool,\n}\n\nimpl BenchmarkContext {\n    pub fn is_test(&self) -> bool {\n        self.is_test\n    }\n}\n"
  },
  {
    "path": "benches/src/file.rs",
    "content": "use anyhow::Context as _;\n\nuse crate::BenchmarkFile;\n\nconst FILE_PREFIX: &str = concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/../target/bench/\");\npub const PREVIOUS: &str = \"previous\";\n\npub(crate) fn get_comparison_file(baseline: Option<&str>) -> Option<BenchmarkFile> {\n    let file_name = baseline.unwrap_or(PREVIOUS);\n    let path = format!(\"{FILE_PREFIX}{file_name}.json\");\n\n    let file = std::fs::read_to_string(path).ok()?;\n    let benchmark_file: BenchmarkFile = serde_json::from_str(&file).ok()?;\n    Some(benchmark_file)\n}\n\npub(crate) fn write_results_file(\n    file_name: &str,\n    output_file: &BenchmarkFile,\n) -> anyhow::Result<()> {\n    let path = format!(\"{FILE_PREFIX}{file_name}.json\");\n    let json = serde_json::to_string_pretty(output_file)?;\n    std::fs::create_dir_all(FILE_PREFIX)\n        .with_context(|| format!(\"Trying to create directory {FILE_PREFIX}\"))?;\n    std::fs::write(&path, json).with_context(|| format!(\"Trying to write file {path}\"))?;\n    Ok(())\n}\n"
  },
  {
    "path": "benches/src/iter.rs",
    "content": "use std::time::Duration;\n\nuse crate::{BenchmarkContext, LoopControl, SubBenchResult};\n\npub fn iter(\n    ctx: &BenchmarkContext,\n    name: &str,\n    throughput_unit: &str,\n    throughput_count_per_iteration: u32,\n    mut f: impl FnMut() -> Duration,\n) -> SubBenchResult {\n    profiling::scope!(\"iter\", name);\n\n    let mut iterations = 0_u32;\n    let mut duration = Duration::ZERO;\n\n    let control = if let Some(override_control) = ctx.override_iters {\n        override_control\n    } else {\n        ctx.default_iterations\n    };\n\n    while !control.finished(iterations, duration) {\n        duration += f();\n        iterations += 1;\n    }\n\n    SubBenchResult {\n        name: name.to_string(),\n        avg_duration_per_iteration: duration / iterations,\n        iterations,\n        throughput_unit: throughput_unit.to_string(),\n        throughput_count_per_iteration,\n    }\n}\n\npub fn iter_auto(\n    ctx: &BenchmarkContext,\n    name: &str,\n    throughput_unit: &str,\n    throughput_count_per_iteration: u32,\n    mut f: impl FnMut(),\n) -> SubBenchResult {\n    iter(\n        ctx,\n        name,\n        throughput_unit,\n        throughput_count_per_iteration,\n        || {\n            let start = std::time::Instant::now();\n            f();\n            start.elapsed()\n        },\n    )\n}\n\npub fn iter_many(\n    ctx: &BenchmarkContext,\n    names: Vec<String>,\n    throughput_unit: &str,\n    throughput_count_per_iteration: u32,\n    mut f: impl FnMut() -> Vec<Duration>,\n) -> Vec<SubBenchResult> {\n    profiling::scope!(\"iter\", &*names[0]);\n\n    let mut iterations = 0_u32;\n    let mut durations = vec![Duration::ZERO; names.len()];\n\n    let control = if let Some(override_control) = ctx.override_iters {\n        override_control\n    } else {\n        LoopControl::Time(Duration::from_secs(1))\n    };\n\n    // We use the first duration to determine whether to stop. This means the other sub-benchmarks\n    // could have run for longer or shorter than intended, but that's acceptable.\n    while !control.finished(iterations, *durations.first().unwrap_or(&Duration::ZERO)) {\n        let iteration_durations = f();\n        assert_eq!(iteration_durations.len(), names.len());\n        for (i, dur) in iteration_durations.into_iter().enumerate() {\n            durations[i] += dur;\n        }\n        iterations += 1;\n    }\n\n    durations\n        .into_iter()\n        .enumerate()\n        .map(|(i, d)| SubBenchResult {\n            name: names[i].to_string(),\n            avg_duration_per_iteration: d / iterations,\n            iterations,\n            throughput_unit: throughput_unit.to_string(),\n            throughput_count_per_iteration,\n        })\n        .collect()\n}\n"
  },
  {
    "path": "benches/src/lib.rs",
    "content": "#![cfg(not(target_arch = \"wasm32\"))]\n#![expect(clippy::disallowed_types)] // We're outside of the main wgpu codebase\n\n//! Benchmarking framework for `wgpu`.\n//!\n//! This crate is a basic framework for benchmarking. Its design is guided\n//! by a few goals:\n//!\n//! - Enumerating tests should be extremely cheap. `criterion` needs\n//!   to run all of your benchmark functions to enumerate them during\n//!   testing. This requires your code to contort itself to avoid doing\n//!   any work until you enter a benchmark callback. This framework\n//!   avoids that by having an explicit list of benchmark function.\n//! - It must be compatible with `cargo-nextest` and have a compatible\n//!   \"test\" mode that runs each benchmark exactly once.\n//! - It should be able to have intuitive test grouping, allowing for\n//!   allowing for quick execution of a reasonable baseline set of benchmarks\n//!   during development, while still allowing for a more exhaustive\n//!   benchmark suite to be run if desired.\n//!\n//! By default all tests run for 2 seconds, but this can be overridden\n//! by individual tests.\n\nuse std::{collections::HashMap, io::IsTerminal, time::Duration};\n\nuse anyhow::Result;\nuse pico_args::Arguments;\nuse serde::{Deserialize, Serialize};\nuse termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};\n\nmod context;\nmod file;\nmod iter;\nmod print;\n\npub use context::*;\npub use iter::*;\n\nuse crate::file::PREVIOUS;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct BenchmarkFile {\n    pub results: HashMap<String, Vec<SubBenchResult>>,\n}\n\nimpl BenchmarkFile {\n    pub fn get_result(\n        &self,\n        benchmark_name: &str,\n        sub_benchmark_name: &str,\n    ) -> Option<&SubBenchResult> {\n        self.results\n            .get(benchmark_name)?\n            .iter()\n            .find(|r| r.name == sub_benchmark_name)\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct SubBenchResult {\n    /// Name of the subbenchmark.\n    pub name: String,\n    /// Average duration per iteration of the subbenchmark.\n    pub avg_duration_per_iteration: Duration,\n    /// Total number of iterations executed.\n    pub iterations: u32,\n    /// Throughput unit description. e.g., \"bytes\", \"elements\", etc.\n    pub throughput_unit: String,\n    /// Number of throughput units processed per iteration.\n    pub throughput_count_per_iteration: u32,\n}\n\nimpl SubBenchResult {\n    pub fn throughput_per_second(&self) -> f64 {\n        let secs_f64 = self.avg_duration_per_iteration.as_secs_f64();\n        if secs_f64 == 0.0 {\n            return 0.0;\n        }\n        self.throughput_count_per_iteration as f64 / secs_f64\n    }\n}\n\npub struct Benchmark {\n    pub name: &'static str,\n    pub func: fn(BenchmarkContext) -> Result<Vec<SubBenchResult>>,\n}\n\nconst HELP: &str = \"\\\nUsage: wgpu-benchmark [OPTIONS] [BENCHMARK_NAME]\n\nModes:\n    --bench                     Run in benchmark mode, comparing against previous results.\n    --list                      List available benchmarks.\n    <no flag>                   Run in test mode, executing each benchmark exactly once.\n\nTest Matching:\n    --exact                     When specifying BENCHMARK_NAME, only run exact matches.\n    BENCHMARK_NAME              Only run benchmarks whose names contain this substring.\n\nComparison:\n    -b, --baseline NAME         Specify a baseline file for comparison.\n    -s, --save-baseline NAME    Save the results as a baseline file.\n\nTimings:\n    --iters N                   Override number of iterations per benchmark.\n    --time SECONDS              Override time per benchmark in seconds.\n\nOther:\n    --color                     Set colored output (always,always-ansi,auto,never).\n    --format terse              Specify --list output format (only 'terse' is supported).\n    --no-capture                (Ignored)\n\";\n\npub fn main(benchmarks: Vec<Benchmark>) {\n    let mut args = Arguments::from_env();\n\n    let help = args.contains([\"-h\", \"--help\"]);\n\n    if help {\n        println!(\"{HELP}\");\n        return;\n    }\n\n    let mut color: ColorChoice = args\n        .opt_value_from_str(\"--color\")\n        .unwrap_or(None)\n        .unwrap_or(ColorChoice::Auto);\n    if color == ColorChoice::Auto && !std::io::stdin().is_terminal() {\n        color = ColorChoice::Never;\n    }\n\n    let exact = args.contains(\"--exact\");\n    // We don't actually need this flag, but cargo-nextest passes it in\n    // test mode, so we need to accept it.\n    let _no_capture = args.contains(\"--no-capture\");\n\n    #[expect(clippy::manual_map)] // So much clearer this way\n    let mut override_iterations = if let Some(iters) = args.opt_value_from_str(\"--iters\").unwrap() {\n        Some(LoopControl::Iterations(iters))\n    } else if let Some(seconds) = args.opt_value_from_str(\"--time\").unwrap() {\n        Some(LoopControl::Time(Duration::from_secs_f64(seconds)))\n    } else {\n        None\n    };\n\n    let baseline_name: Option<String> = args.opt_value_from_str([\"-b\", \"--baseline\"]).unwrap();\n    let write_baseline: Option<String> =\n        args.opt_value_from_str([\"-s\", \"--save-baseline\"]).unwrap();\n\n    let is_bench = args.contains(\"--bench\");\n    let is_list = args.contains(\"--list\");\n    let is_test = !is_bench && !is_list;\n\n    let format: Option<String> = args.opt_value_from_str(\"--format\").unwrap();\n\n    if let Some(fmt) = format {\n        assert_eq!(fmt, \"terse\", \"Only 'terse' format is supported.\");\n    }\n    if let Some(ref baseline) = baseline_name {\n        if baseline == PREVIOUS {\n            eprintln!(\"Cannot use '{PREVIOUS}' as a baseline name.\");\n            return;\n        }\n    }\n    if let Some(ref write_baseline) = write_baseline {\n        if write_baseline == PREVIOUS {\n            eprintln!(\"Cannot use '{PREVIOUS}' as a baseline name.\");\n            return;\n        }\n    }\n\n    if override_iterations.is_none() && is_test {\n        override_iterations = Some(LoopControl::Iterations(1));\n    }\n\n    let name = args.free_from_str::<String>().ok();\n\n    let baseline = if is_bench {\n        let res = file::get_comparison_file(baseline_name.as_deref());\n\n        match (&res, baseline_name.as_deref()) {\n            (Some(_), Some(baseline)) => {\n                println!(\"Using baseline \\\"{baseline}\\\" for comparison.\\n\")\n            }\n            (None, Some(baseline)) => {\n                eprintln!(\"Could not find baseline named {baseline:?}.\\n\");\n                return;\n            }\n            (Some(_), None) => {\n                println!(\"Using previous benchmark results for comparison.\\n\");\n            }\n            (None, None) => {\n                println!(\"No previous benchmark results found for comparison.\\n\");\n            }\n        }\n\n        res\n    } else {\n        None\n    };\n\n    let mut output_file = BenchmarkFile::default();\n\n    let mut stdout = StandardStream::stdout(color);\n\n    for bench in benchmarks {\n        if let Some(ref bench_name) = name {\n            if exact {\n                if bench.name != bench_name {\n                    continue;\n                }\n            } else if !bench.name.contains(bench_name) {\n                continue;\n            }\n        }\n\n        if is_list {\n            println!(\"{}: benchmark\", bench.name);\n            continue;\n        }\n\n        let ctx = BenchmarkContext {\n            override_iters: override_iterations,\n            default_iterations: LoopControl::default(),\n            is_test,\n        };\n\n        stdout\n            .set_color(ColorSpec::new().set_fg(Some(Color::Blue)))\n            .unwrap();\n        println!(\"Running benchmark: {}\", bench.name);\n        stdout.reset().unwrap();\n\n        let results = {\n            profiling::scope!(\"bench\", bench.name);\n            let r = (bench.func)(ctx);\n            match r {\n                Ok(r) => r,\n                Err(e) => {\n                    eprintln!(\"  Error running benchmark '{}': {:?}\", bench.name, e);\n                    continue;\n                }\n            }\n        };\n\n        let previous_results = if let Some(ref baseline) = baseline {\n            baseline.results.get(bench.name).map(|r| r.as_slice())\n        } else {\n            None\n        };\n\n        print::print_results(&mut stdout, &results, previous_results);\n\n        output_file.results.insert(bench.name.to_string(), results);\n    }\n\n    file::write_results_file(PREVIOUS, &output_file).unwrap();\n    if let Some(output_baseline) = write_baseline {\n        file::write_results_file(&output_baseline, &output_file).unwrap();\n    }\n}\n"
  },
  {
    "path": "benches/src/print.rs",
    "content": "use std::collections::HashMap;\nuse std::io::Write;\n\nuse termcolor::{Color, ColorSpec, StandardStream, WriteColor};\n\nuse crate::SubBenchResult;\n\n#[derive(Default, Clone)]\nstruct Delta {\n    throughput_change_str: String,\n    throughput_change: f64,\n    time_change_str: String,\n    time_change: f64,\n}\n\nimpl Delta {\n    fn new(previous: &SubBenchResult, current: &SubBenchResult) -> Self {\n        let prev_throughput = previous.throughput_per_second();\n        let curr_throughput = current.throughput_per_second();\n        let delta_throughput = if prev_throughput != 0.0 {\n            (curr_throughput - prev_throughput) / prev_throughput * 100.0\n        } else {\n            0.0\n        };\n        let throughput_change = format!(\" ({delta_throughput:+.2}%)\");\n\n        let prev_time = previous.avg_duration_per_iteration;\n        let curr_time = current.avg_duration_per_iteration;\n        let delta_time = if prev_time.as_nanos() != 0 {\n            (curr_time.as_secs_f64() - prev_time.as_secs_f64()) / prev_time.as_secs_f64() * 100.0\n        } else {\n            0.0\n        };\n\n        let time_change = format!(\"{delta_time:+.2}%; \");\n\n        Delta {\n            throughput_change_str: throughput_change,\n            throughput_change: delta_throughput,\n            time_change_str: time_change,\n            time_change: delta_time,\n        }\n    }\n}\n\n/// Get a color spec for the given change percentage.\n///\n/// Positive changes are red (regression), negative changes are green (improvement).\n/// This represents changes for time durations. For throughput changes, the sign should be inverted\n/// before passing to this method.\nfn get_change_color(percent_change: f64) -> ColorSpec {\n    let mut color_spec = ColorSpec::new();\n    if percent_change > 3.0 {\n        color_spec.set_fg(Some(Color::Red));\n    } else if percent_change < -3.0 {\n        color_spec.set_fg(Some(Color::Green));\n    } else {\n        color_spec.set_fg(Some(Color::Yellow));\n    }\n    if percent_change.abs() > 15.0 {\n        color_spec.set_intense(true);\n    }\n    color_spec\n}\n\npub fn print_results(\n    stdout: &mut StandardStream,\n    results: &[SubBenchResult],\n    previous_results: Option<&[SubBenchResult]>,\n) {\n    let mut deltas = HashMap::new();\n    if let Some(previous_results) = previous_results {\n        for result in results {\n            if let Some(previous_result) = previous_results.iter().find(|r| r.name == result.name) {\n                deltas.insert(result.name.clone(), Delta::new(previous_result, result));\n            }\n        }\n    }\n\n    let longest_throughput_change_len = deltas\n        .values()\n        .map(|d| d.throughput_change_str.len())\n        .max()\n        .unwrap_or(0);\n    let longest_time_change_len = deltas\n        .values()\n        .map(|d| d.time_change_str.len())\n        .max()\n        .unwrap_or(0);\n\n    let longest_name_len = results.iter().map(|r| r.name.len()).max().unwrap_or(0);\n    let duration_strings: Vec<String> = results\n        .iter()\n        .map(|r| format!(\"{:.3?}\", r.avg_duration_per_iteration))\n        .collect();\n    let longest_duration_len = duration_strings.iter().map(|s| s.len()).max().unwrap_or(0);\n\n    let iterations_strings: Vec<String> = results\n        .iter()\n        .map(|r| format!(\"{}\", r.iterations))\n        .collect();\n    let longest_iterations_len = iterations_strings\n        .iter()\n        .map(|s| s.len())\n        .max()\n        .unwrap_or(0);\n\n    let throughput_strings: Vec<String> = results\n        .iter()\n        .map(|r| {\n            let throughput_per_second = r.throughput_count_per_iteration as f64\n                / r.avg_duration_per_iteration.as_secs_f64();\n            human_scale(throughput_per_second)\n        })\n        .collect();\n    let longest_throughput_len = throughput_strings\n        .iter()\n        .map(|s| s.len())\n        .max()\n        .unwrap_or(0);\n\n    let longest_throughput_unit_len = results\n        .iter()\n        .map(|r| r.throughput_unit.len())\n        .max()\n        .unwrap_or(0);\n\n    for (i, result) in results.iter().enumerate() {\n        let delta = deltas.get(&result.name).cloned().unwrap_or_default();\n        let time_color = get_change_color(delta.time_change);\n        let throughput_color = get_change_color(-delta.throughput_change);\n\n        stdout\n            .set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))\n            .unwrap();\n        write!(stdout, \"    {:>longest_name_len$}: \", result.name).unwrap();\n\n        stdout.set_color(&time_color).unwrap();\n        write!(stdout, \"{:>longest_duration_len$} \", duration_strings[i],).unwrap();\n        stdout.reset().unwrap();\n        write!(stdout, \"(\").unwrap();\n        stdout.set_color(&time_color).unwrap();\n        write!(\n            stdout,\n            \"{:>longest_time_change_len$}\",\n            delta.time_change_str\n        )\n        .unwrap();\n        stdout.reset().unwrap();\n\n        write!(\n            stdout,\n            \"over {:>longest_iterations_len$} iter) \",\n            result.iterations,\n        )\n        .unwrap();\n\n        stdout.set_color(&throughput_color).unwrap();\n        write!(stdout, \"{:>longest_throughput_len$}\", throughput_strings[i]).unwrap();\n        stdout.reset().unwrap();\n        write!(\n            stdout,\n            \" {:>longest_throughput_unit_len$}/s\",\n            result.throughput_unit,\n        )\n        .unwrap();\n        stdout.set_color(&throughput_color).unwrap();\n        writeln!(\n            stdout,\n            \"{:>longest_throughput_change_len$}\",\n            delta.throughput_change_str\n        )\n        .unwrap();\n    }\n    println!();\n}\n\nfn human_scale(value: f64) -> String {\n    const PREFIXES: &[&str] = &[\"\", \"K\", \"M\", \"G\", \"T\", \"P\"];\n\n    if value == 0.0 {\n        return \"0\".to_string();\n    }\n\n    let abs_value = value.abs();\n    let exponent = (abs_value.log10() / 3.0).floor() as usize;\n    let prefix_index = exponent.min(PREFIXES.len() - 1);\n\n    let scaled = value / 10_f64.powi((prefix_index * 3) as i32);\n\n    // Determine decimal places for 3 significant figures\n    let decimal_places = if scaled.abs() >= 100.0 {\n        0\n    } else if scaled.abs() >= 10.0 {\n        1\n    } else {\n        2\n    };\n\n    format!(\n        \"{:.prec$}{}\",\n        scaled,\n        PREFIXES[prefix_index],\n        prec = decimal_places\n    )\n}\n"
  },
  {
    "path": "clippy.toml",
    "content": "# NOTE: Other global Clippy config (severity overrides) is in top-level Cargo.toml.\n\ndisallowed-types = [\n    { path = \"std::collections::HashMap\", reason = \"use hashbrown::HashMap instead\" },\n    { path = \"std::collections::HashSet\", reason = \"use hashbrown::HashSet instead\" },\n]\n\n# The default large-error-threshold is 128. We have a bunch of complex error\n# types that are slightly larger than that.\nlarge-error-threshold = 192\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  status:\n    project:\n      default:\n        informational: true\n        if_ci_failed: success\n    patch:\n      default:\n        informational: true\n        if_ci_failed: success\ncomment: false\ngithub_checks:\n    annotations: false\n"
  },
  {
    "path": "cts_runner/Cargo.toml",
    "content": "[package]\nname = \"cts_runner\"\nversion.workspace = true\nauthors = [\"Luca Casonato <hello@lcas.dev>\"]\nedition.workspace = true\ndescription = \"CTS runner for wgpu\"\nlicense.workspace = true\npublish = false\n\n[dependencies]\nenv_logger.workspace = true\n\n# We make all dependencies conditional on not being wasm,\n# so the whole workspace can built as wasm.\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\ndeno_console.workspace = true\ndeno_core.workspace = true\ndeno_features.workspace = true\ndeno_url.workspace = true\ndeno_web.workspace = true\ndeno_webidl.workspace = true\ndeno_webgpu.workspace = true\nlog.workspace = true\npico-args.workspace = true\ntokio = { workspace = true, features = [\"full\"] }\ntermcolor.workspace = true\n\n[dev-dependencies]\ntempfile.workspace = true\n"
  },
  {
    "path": "cts_runner/README.md",
    "content": "# cts_runner\n\nThis crate contains infrastructure for running the WebGPU conformance tests on\nDeno's `wgpu`-based implementation of WebGPU.\n\nInstructions for running the tests via the CTS `xtask` are in\n[docs/testing.md](https://github.com/gfx-rs/wgpu/blob/trunk/docs/testing.md#webgpu-cts).\nThe file [revision.txt](./revision.txt) specifies the version of the CTS that\nwill be used by default.\n\n`cts_runner` is somewhat misnamed at this point, in that it is useful for\nthings other than just running the CTS:\n\n- The [tests](./tests) directory contains a few directed tests for\n  Deno's bindings to `wgpu`.\n- Standalone JavaScript snippets that use WebGPU can be run\n  with a command like: `cargo run -p cts_runner -- test.js`.\n"
  },
  {
    "path": "cts_runner/examples/hello-compute.js",
    "content": "const adapter = await navigator.gpu.requestAdapter();\n\nconst numbers = [1, 4, 3, 295];\n\nconst device = await adapter.requestDevice();\n\nconst shaderCode = `\n@group(0)\n@binding(0)\nvar<storage, read_write> v_indices: array<u32>; // this is used as both input and output for convenience\n// The Collatz Conjecture states that for any integer n:\n// If n is even, n = n/2\n// If n is odd, n = 3n+1\n// And repeat this process for each new n, you will always eventually reach 1.\n// Though the conjecture has not been proven, no counterexample has ever been found.\n// This function returns how many times this recurrence needs to be applied to reach 1.\nfn collatz_iterations(n_base: u32) -> u32{\n    var n: u32 = n_base;\n    var i: u32 = 0u;\n    loop {\n        if (n <= 1u) {\n            break;\n        }\n        if (n % 2u == 0u) {\n            n = n / 2u;\n        }\n        else {\n            // Overflow? (i.e. 3*n + 1 > 0xffffffffu?)\n            if (n >= 1431655765u) {   // 0x55555555u\n                return 4294967295u;   // 0xffffffffu\n            }\n            n = 3u * n + 1u;\n        }\n        i = i + 1u;\n    }\n    return i;\n}\n@compute\n@workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    v_indices[global_id.x] = collatz_iterations(v_indices[global_id.x]);\n}`;\n\nconst shaderModule = device.createShaderModule({\n  code: shaderCode,\n});\n\nconst size = new Uint32Array(numbers).byteLength;\n\nconst stagingBuffer = device.createBuffer({\n  size: size,\n  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,\n});\n\nconst storageBuffer = device.createBuffer({\n  label: \"Storage Buffer\",\n  size: size,\n  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST |\n    GPUBufferUsage.COPY_SRC,\n  mappedAtCreation: true,\n});\n\nconst buf = new Uint32Array(storageBuffer.getMappedRange());\n\nbuf.set(numbers);\n\nstorageBuffer.unmap();\n\nconst computePipeline = device.createComputePipeline({\n  layout: \"auto\",\n  compute: {\n    module: shaderModule,\n    entryPoint: \"main\",\n  },\n});\nconst bindGroupLayout = computePipeline.getBindGroupLayout(0);\n\nconst bindGroup = device.createBindGroup({\n  layout: bindGroupLayout,\n  entries: [\n    {\n      binding: 0,\n      resource: {\n        buffer: storageBuffer,\n      },\n    },\n  ],\n});\n\nconst encoder = device.createCommandEncoder();\n\nconst computePass = encoder.beginComputePass();\ncomputePass.setPipeline(computePipeline);\ncomputePass.setBindGroup(0, bindGroup);\ncomputePass.insertDebugMarker(\"compute collatz iterations\");\ncomputePass.dispatchWorkgroups(numbers.length);\ncomputePass.end();\n\nencoder.copyBufferToBuffer(storageBuffer, 0, stagingBuffer, 0, size);\n\ndevice.queue.submit([encoder.finish()]);\n\nawait stagingBuffer.mapAsync(1);\n\nconst data = stagingBuffer.getMappedRange();\n\nfunction isTypedArrayEqual(a, b) {\n  if (a.byteLength !== b.byteLength) return false;\n  return a.every((val, i) => val === b[i]);\n}\n\nconst actual = new Uint32Array(data);\nconst expected = new Uint32Array([0, 2, 7, 55]);\n\nconsole.log(\"actual\", actual);\nconsole.log(\"expected\", expected);\n\nif (!isTypedArrayEqual(actual, expected)) {\n  throw new TypeError(\"Actual does not equal expected!\");\n}\n\nstagingBuffer.unmap();\n\ndevice.destroy();\n"
  },
  {
    "path": "cts_runner/fail.lst",
    "content": "// CTS test selectors that are not expected to pass.\n//\n// At present this only includes tests in the `api,validation` and\n// `shader,validation` hierarchies.\n//\n// Percentages in comments are the portion of tests that are passing. Some\n// failures that are intermittent or platform-dependent are marked with `***`.\n// Many populated by Claude and may be wrong.\n//\n// Linked issues may not cover all failures in the suite.\n//\n// The `cts_runner` integration test `lst_files_are_sorted` verifies that test selectors\n// appear in this file in order -- including ones in comments.\nwebgpu:api,validation,buffer,mapping:* // crash\nwebgpu:api,validation,capability_checks,features,* // 21%, TypeError not thrown for missing features (needs deno_webgpu fix)\nwebgpu:api,validation,capability_checks,limits,* // 60%, workgroup storage size validation unimplemented; interstage vars counting; bind group timing on dx12\nwebgpu:api,validation,compute_pipeline:limits,workgroup_storage_size:* // 0%\nwebgpu:api,validation,compute_pipeline:overrides,workgroup_size,limits,workgroup_storage_size:* // 0%, missing workgroup storage size validation\nwebgpu:api,validation,compute_pipeline:overrides,workgroup_size,limits:* // 0%\nwebgpu:api,validation,createBindGroup:buffer,resource_state:* // 0%, https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,createBindGroup:external_texture,* // 0%, no external external texture in deno\nwebgpu:api,validation,createBindGroup:texture,resource_state:* // crash, https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,createBindGroupLayout:visibility,VERTEX_shader_stage_buffer_type:* // 25%, writable storage buffers not allowed in VERTEX\nwebgpu:api,validation,createBindGroupLayout:visibility,VERTEX_shader_stage_storage_texture_access:* // 25%, write-access storage textures not allowed in VERTEX\nwebgpu:api,validation,createBindGroupLayout:visibility:* // 25%, missing per-stage storage limits\nwebgpu:api,validation,createView:texture_state:* // 0%, https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges:* // ***, https://github.com/gfx-rs/wgpu/issues/8118\nwebgpu:api,validation,encoding,cmds,debug:* // 92%, https://github.com/gfx-rs/wgpu/issues/8039\nwebgpu:api,validation,encoding,cmds,render,* // https://github.com/gfx-rs/wgpu/issues/7912\nwebgpu:api,validation,encoding,cmds,setBindGroup:dynamic_offsets_match_expectations_in_pass_encoder:* // deno unwrap\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"compute%20pass\";state=\"destroyed\";* // https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20bundle\";state=\"destroyed\";* // https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20pass\";state=\"destroyed\";* // https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,encoding,cmds,setBindGroup:u32array_start_and_length:* // deno unwrap\nwebgpu:api,validation,encoding,cmds,setImmediates:* // 0%, feature not implemented\nwebgpu:api,validation,encoding,createRenderBundleEncoder:* // 26%, empty attachments, format compatibility\nwebgpu:api,validation,encoding,encoder_open_state:* // https://github.com/gfx-rs/wgpu/issues/7857\nwebgpu:api,validation,encoding,queries,resolveQuerySet:* // 93%, https://github.com/gfx-rs/wgpu/issues/7881\nwebgpu:api,validation,encoding,render_bundle:* // 81%, readonly flag normalization mismatch\nwebgpu:api,validation,image_copy,buffer_texture_copies:* // https://github.com/gfx-rs/wgpu/issues/7946\nwebgpu:api,validation,layout_shader_compat:pipeline_layout_shader_exact_match:* // dx12, https://bugzilla.mozilla.org/show_bug.cgi?id=2017725\nwebgpu:api,validation,non_filterable_texture:non_filterable_texture_with_filtering_sampler:* // 80%, depth textures with filtering samplers\nwebgpu:api,validation,query_set,create:count:* // 0%, wgpu incorrectly rejects zero-count query sets\nwebgpu:api,validation,queue,buffer_mapped:* // ***, vulkan\nwebgpu:api,validation,queue,destroyed,* // 71%, writeBuffer/writeTexture return value, destroyed query set\nwebgpu:api,validation,queue,writeBuffer:ranges:* // 0%, missing OperationError for invalid ranges\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,depth_stencil_read_only_write_state:* // fails on dx12\nwebgpu:api,validation,render_pass,render_pass_descriptor:depth_stencil_attachment,loadOp_storeOp_match_depthReadOnly_stencilReadOnly:* // 33%, missing validation: depth/stencil load/store ops provided for missing texture format aspects\nwebgpu:api,validation,render_pipeline,fragment_state:dual_source_blending,color_target_count:*\nwebgpu:api,validation,render_pipeline,fragment_state:pipeline_output_targets,blend:* // 95%, blend factors reading source alpha require vec4\nwebgpu:api,validation,render_pipeline,fragment_state:pipeline_output_targets:* // 3%, color target without shader output requires writeMask=0\nwebgpu:api,validation,render_pipeline,inter_stage:* // 15%, inter-stage type validation gaps and interpolation default handling\nwebgpu:api,validation,render_pipeline,misc:external_texture:* // 0%, no external texture in deno\nwebgpu:api,validation,render_pipeline,misc:storage_texture,format:* // 8%, similar to compute pipeline issue\nwebgpu:api,validation,render_pipeline,multisample_state:* // 60%, https://github.com/gfx-rs/wgpu/issues/8779\nwebgpu:api,validation,render_pipeline,overrides:* // 17%, missing validation: invalid pipeline constant identifiers silently ignored\nwebgpu:api,validation,render_pipeline,vertex_state:max_vertex_attribute_limit:* // 0%, empty vertex buffers not counted, GPUInternalError no message\nwebgpu:api,validation,render_pipeline,vertex_state:max_vertex_buffer_limit:* // 0%, empty vertex buffers not counted toward limit\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_contained_in_stride:* // fails on metal in CI https://github.com/gfx-rs/wgpu/issues/8312\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_offset_alignment:* // fails on metal in CI\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_color:* // 92%, https://github.com/gfx-rs/wgpu/issues/3126\nwebgpu:api,validation,resource_usages,texture,in_render_common:subresources,depth_stencil_attachment_and_bind_group:* // 69%, https://github.com/gfx-rs/wgpu/issues/8705\nwebgpu:api,validation,state,device_lost,destroy:* // crash\nwebgpu:api,validation,texture,destroy:submit_a_destroyed_texture_as_attachment:* // 44%, https://github.com/gfx-rs/wgpu/issues/8714\n\nwebgpu:shader,execution,expression,call,builtin,atan2:f16:* // dx12, fails with dxc, passes with fxc, https://github.com/gfx-rs/wgpu/issues/9179\n\nwebgpu:shader,validation,decl,context_dependent_resolution:* // 92%, f16 as reserved keyword when enabled\nwebgpu:shader,validation,decl,let:* // texture/sampler let\nwebgpu:shader,validation,decl,override:* // 93%, unrestricted_pointer_parameters not implemented, https://github.com/gfx-rs/wgpu/issues/5158\nwebgpu:shader,validation,decl,var:* // 99%, https://github.com/gfx-rs/wgpu/issues/8925 (trailing comma), atomics in read-only storage, shader stage restrictions\nwebgpu:shader,validation,expression,access,array:* // 97%, runtime indexing with compile-time-known values, https://github.com/gfx-rs/wgpu/issues/4390\nwebgpu:shader,validation,expression,access,matrix:* // 93%, runtime OOB matrix access with literal indices incorrectly rejected\nwebgpu:shader,validation,expression,access,vector:* // 52%, https://github.com/gfx-rs/wgpu/issues/4390, and missing swizzle validation\nwebgpu:shader,validation,expression,binary,add_sub_mul:* // 95%, u32 const-eval overflow incorrectly rejected, f16 const-eval overflow not rejected, atomics #5474\nwebgpu:shader,validation,expression,binary,and_or_xor:* // 96%, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,binary,bitwise_shift:invalid_types:* // 93%, atomics #5474\nwebgpu:shader,validation,expression,binary,comparison:* // 74%, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,binary,div_rem:* // 86%, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,binary,short_circuiting_and_or:* // 92%, https://github.com/gfx-rs/wgpu/issues/8440\nwebgpu:shader,validation,expression,call,builtin,abs:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,acosh:* // 56%, missing domain validation [1,∞) in const eval\nwebgpu:shader,validation,expression,call,builtin,atomics:* // 86%, atomics in vertex shaders, invalid address spaces/access modes\nwebgpu:shader,validation,expression,call,builtin,bitcast:* // 57%, bitcast const-eval unimplemented; size validation missing; f16 vector validation incorrect\nwebgpu:shader,validation,expression,call,builtin,clamp:* // 71%, clamp low<=high constraint not checked for const/override parameters\nwebgpu:shader,validation,expression,call,builtin,countLeadingZeros:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,countOneBits:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,countTrailingZeros:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,cross:* // 86%, abstract int/float overflow issues in const eval\nwebgpu:shader,validation,expression,call,builtin,derivatives:* // 83%, f16 support not properly validated\nwebgpu:shader,validation,expression,call,builtin,determinant:* // 71%, abstract int/float const eval issues\nwebgpu:shader,validation,expression,call,builtin,distance:* // 66%, scalar distance uses wrong formula (sqrt instead of abs)\nwebgpu:shader,validation,expression,call,builtin,dot4I8Packed:* // 91%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,dot4U8Packed:* // 91%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,extractBits:* // 55%, const eval issues\nwebgpu:shader,validation,expression,call,builtin,faceForward:* // 68%, const eval overflow for abstract/f16\nwebgpu:shader,validation,expression,call,builtin,firstLeadingBit:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,firstTrailingBit:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,fma:* // 85%, const eval issues\nwebgpu:shader,validation,expression,call,builtin,frexp:* // 39%, const/override eval not supported\nwebgpu:shader,validation,expression,call,builtin,insertBits:* // 73%, missing const eval support\nwebgpu:shader,validation,expression,call,builtin,inverseSqrt:* // 61%, const eval overflow for abstract/f16\nwebgpu:shader,validation,expression,call,builtin,ldexp:* // 43%, const eval not implemented\nwebgpu:shader,validation,expression,call,builtin,length:* // 74%, const eval overflow for abstract/f16\nwebgpu:shader,validation,expression,call,builtin,mix:* // 66%, const eval issues\nwebgpu:shader,validation,expression,call,builtin,modf:* // 52%, const eval not fully implemented\nwebgpu:shader,validation,expression,call,builtin,normalize:* // 63%, missing const/override eval overflow validation (intermediate values overflow to infinity)\nwebgpu:shader,validation,expression,call,builtin,pack2x16float:* // 80%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack2x16snorm:* // 88%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack2x16unorm:* // 88%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4x8snorm:* // 88%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4x8unorm:* // 88%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4xI8:* // 74%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4xI8Clamp:* // 74%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4xU8:* // 74%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pack4xU8Clamp:* // 74%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,pow:* // 63%, missing const/override eval validation (negative base, zero^non-positive, overflow), http://github.com/gpuweb/issues/4527\nwebgpu:shader,validation,expression,call,builtin,quantizeToF16:* // 77%, overflow validation issues\nwebgpu:shader,validation,expression,call,builtin,reflect:* // 39%, const eval not implemented\nwebgpu:shader,validation,expression,call,builtin,refract:* // 44%, const eval not implemented\nwebgpu:shader,validation,expression,call,builtin,reverseBits:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,select:* // >99%, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,expression,call,builtin,smoothstep:* // 69%, const eval issues\nwebgpu:shader,validation,expression,call,builtin,textureDimensions:* // >99%, no external texture in deno\nwebgpu:shader,validation,expression,call,builtin,textureGather:* // 99%, https://github.com/gfx-rs/wgpu/issues/8876\nwebgpu:shader,validation,expression,call,builtin,textureGatherCompare:* // 99%, edge case in offset or external texture validation\nwebgpu:shader,validation,expression,call,builtin,textureLoad:* // 99%, texture_external not implemented\nwebgpu:shader,validation,expression,call,builtin,textureSample:* // 99%, texture_external\nwebgpu:shader,validation,expression,call,builtin,textureSampleBaseClampToEdge:* // 96%, texture_external\nwebgpu:shader,validation,expression,call,builtin,textureSampleBias:* // 99%, missing offset validation (range & cube texture)\nwebgpu:shader,validation,expression,call,builtin,textureSampleCompare:* // 99%, missing offset range validation (-8 to +7)\nwebgpu:shader,validation,expression,call,builtin,textureSampleCompareLevel:* // 99%, missing offset range validation (-8 to +7)\nwebgpu:shader,validation,expression,call,builtin,textureSampleGrad:* // 99%, missing offset range validation + depth textures incorrectly accepted\nwebgpu:shader,validation,expression,call,builtin,textureSampleLevel:* // 99%, missing offset validation (cube texture & value range)\nwebgpu:shader,validation,expression,call,builtin,transpose:* // 86%, missing const eval\nwebgpu:shader,validation,expression,call,builtin,unpack2x16float:* // 81%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack2x16snorm:* // 81%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack2x16unorm:* // 81%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack4x8snorm:* // 81%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack4x8unorm:* // 81%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack4xI8:* // 75%, missing const eval (#4507)\nwebgpu:shader,validation,expression,call,builtin,unpack4xU8:* // 75%, missing const eval (#4507)\nwebgpu:shader,validation,expression,early_evaluation:* // 67%, mixed override/runtime composite evaluation\nwebgpu:shader,validation,expression,matrix,* // 99%, #5474, #8868, atomic\nwebgpu:shader,validation,expression,precedence:* // 76%, https://github.com/gfx-rs/wgpu/issues/4536\nwebgpu:shader,validation,expression,unary,* // 99%, atomics #5474\nwebgpu:shader,validation,extension,dual_source_blending:blend_src_usage:* // 61%, @blend_src validation gaps\nwebgpu:shader,validation,extension,readonly_and_readwrite_storage_textures:* // 0%, CTS bug https://github.com/gpuweb/cts/pull/4567\nwebgpu:shader,validation,functions,alias_analysis:* // 2%, https://github.com/gfx-rs/wgpu/issues/7650\nwebgpu:shader,validation,functions,restrictions:* // 81%, texture_external\nwebgpu:shader,validation,parse,attribute:* // 93%, group index validation at shader module creation\nwebgpu:shader,validation,parse,blankspace:* // 95%, null in comments, https://github.com/gfx-rs/wgpu/issues/8877\nwebgpu:shader,validation,parse,comments:* // 93%, unterminated block comments, https://github.com/gfx-rs/wgpu/issues/8877\nwebgpu:shader,validation,parse,diagnostic:* // 50%, https://github.com/gfx-rs/wgpu/issues/6458\nwebgpu:shader,validation,parse,literal:* // 96%, https://github.com/gfx-rs/wgpu/issues/7046\nwebgpu:shader,validation,parse,must_use:* // 97%, https://github.com/gfx-rs/wgpu/issues/8876\nwebgpu:shader,validation,parse,shadow_builtins:* // 83%, function param shadowing parser issue; determinant const-eval; texture_external capability missing\nwebgpu:shader,validation,shader_io,align:* // 98%, https://github.com/gfx-rs/wgpu/issues/8892\nwebgpu:shader,validation,shader_io,binding:* // 95%, https://github.com/gfx-rs/wgpu/issues/8892\nwebgpu:shader,validation,shader_io,builtins:* // 50%, trailing comma not accepted\nwebgpu:shader,validation,shader_io,group:* // 95%, https://github.com/gfx-rs/wgpu/issues/8892\nwebgpu:shader,validation,shader_io,id:* // 94%, https://github.com/gfx-rs/wgpu/issues/8892, @id on const\nwebgpu:shader,validation,shader_io,interpolate:* // 91%, https://github.com/gfx-rs/wgpu/issues/8892\nwebgpu:shader,validation,shader_io,layout_constraints:* // 99%, struct alignment not inferred from @align on members\nwebgpu:shader,validation,shader_io,locations:stage_inout:* // 66%, invalid usage @location in compute shaders\nwebgpu:shader,validation,shader_io,pipeline_stage:* // 92%, stage attributes incorrectly accepted on var<private> and var<storage>\nwebgpu:shader,validation,shader_io,size:* // 92%, https://github.com/gfx-rs/wgpu/issues/8892, large size validation, runtime-sized array\nwebgpu:shader,validation,shader_io,workgroup_size:* // 83%, https://github.com/gfx-rs/wgpu/issues/8892, type mixing, attribute placement\nwebgpu:shader,validation,statement,continue:* // 90%, continue bypassing declaration used in continuing block, https://github.com/gfx-rs/wgpu/issues/7650\nwebgpu:shader,validation,statement,for:* // 93%, phony/increment in for-loop init/cont, empty loop behavior\nwebgpu:shader,validation,statement,increment_decrement:* // 98%, atomic type validation gap, https://github.com/gfx-rs/wgpu/issues/5474\nwebgpu:shader,validation,statement,loop:* // 92%, https://github.com/gfx-rs/wgpu/issues/7650\nwebgpu:shader,validation,statement,phony:* // 90%, phony assignment in for-loops with semicolons\nwebgpu:shader,validation,statement,statement_behavior:* // https://github.com/gfx-rs/wgpu/issues/7650\nwebgpu:shader,validation,statement,switch:* // https://github.com/gfx-rs/wgpu/issues/7650\nwebgpu:shader,validation,types,* // 95%, texture_external not supported (2 tests), atomic validation gaps (8 tests), pointer validation gaps (5 tests), 16-bit normalized storage texture formats (36 tests)\nwebgpu:shader,validation,uniformity,* // 21%, https://github.com/gfx-rs/wgpu/issues/4369\n"
  },
  {
    "path": "cts_runner/revision.txt",
    "content": "e9adcf85f5d3698da4dbd7d0e2de0ef0350c3d90\n"
  },
  {
    "path": "cts_runner/skip.lst",
    "content": "// CTS test selectors that are entirely or nearly entirely skipped.\n//\n// The `cts_runner` integration test `lst_files_are_sorted` verifies that test selectors\n// appear in this file in order -- including ones in comments.\n\nwebgpu:api,validation,createPipelineLayout:immediate_data_size:* // immediates, https://github.com/gfx-rs/wgpu/issues/8556\nwebgpu:api,validation,encoding,programmable,pipeline_immediate:* // immediates, https://github.com/gfx-rs/wgpu/issues/8556\nwebgpu:api,validation,gpu_external_texture_expiration:* // external texture\nwebgpu:api,validation,pipeline,immediates:pipeline_creation_immediate_size_mismatch:* // immediates, https://github.com/gfx-rs/wgpu/issues/8556\nwebgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:* // external texture\n\nwebgpu:shader,validation,expression,call,builtin,quadBroadcast:* // subgroups, https://github.com/gfx-rs/wgpu/issues/8722\nwebgpu:shader,validation,expression,call,builtin,quadSwap:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupAdd:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupAnyAll:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupBallot:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupBitwise:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupBroadcast:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupBroadcastFirst:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupElect:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupMinMax:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupMul:* // ibid.\nwebgpu:shader,validation,expression,call,builtin,subgroupShuffle:* // ibid.\nwebgpu:shader,validation,statement,swizzle_assignment:* // swizzle assignment, https://github.com/gfx-rs/wgpu/issues/9159\n"
  },
  {
    "path": "cts_runner/src/bootstrap.js",
    "content": "// Adapted from https://github.com/denoland/deno/blob/6abf126c2a7a451cded8c6b5e6ddf1b69c84055d/runtime/js/99_main.js\n\n// Removes the `__proto__` for security reasons.  This intentionally makes\n// Deno non compliant with ECMA-262 Annex B.2.2.1\n//\ndelete Object.prototype.__proto__;\n\nimport { core, primordials } from \"ext:core/mod.js\";\nconst {\n  Error,\n  ObjectDefineProperty,\n  ObjectDefineProperties,\n  ObjectSetPrototypeOf,\n  Symbol,\n  DateNow,\n} = primordials;\n\nimport { pathFromURL } from \"ext:deno_web/00_infra.js\";\nimport * as webidl from \"ext:deno_webidl/00_webidl.js\";\nimport * as globalInterfaces from \"ext:deno_web/04_global_interfaces.js\";\nimport * as event from \"ext:deno_web/02_event.js\";\nimport * as timers from \"ext:deno_web/02_timers.js\";\nimport * as base64 from \"ext:deno_web/05_base64.js\";\nimport * as encoding from \"ext:deno_web/08_text_encoding.js\";\nimport { Console } from \"ext:deno_console/01_console.js\";\nimport * as url from \"ext:deno_url/00_url.js\";\nimport { DOMException } from \"ext:deno_web/01_dom_exception.js\";\nimport * as performance from \"ext:deno_web/15_performance.js\";\nimport { loadWebGPU } from \"ext:deno_webgpu/00_init.js\";\nimport * as imageData from \"ext:deno_web/16_image_data.js\";\nconst webgpu = loadWebGPU();\nwebgpu.initGPU();\n\n// imports needed to pass module evaluation\nimport \"ext:deno_url/01_urlpattern.js\";\nimport \"ext:deno_web/01_mimesniff.js\";\nimport \"ext:deno_web/03_abort_signal.js\";\nimport \"ext:deno_web/06_streams.js\";\nimport \"ext:deno_web/09_file.js\";\nimport \"ext:deno_web/10_filereader.js\";\nimport \"ext:deno_web/12_location.js\";\nimport \"ext:deno_web/13_message_port.js\";\nimport \"ext:deno_web/14_compression.js\";\nimport \"ext:deno_webgpu/02_surface.js\";\n\nlet globalThis_;\n\nclass NotFound extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"NotFound\";\n  }\n}\n\nclass BrokenPipe extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"BrokenPipe\";\n  }\n}\n\nclass AlreadyExists extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"AlreadyExists\";\n  }\n}\n\nclass InvalidData extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"InvalidData\";\n  }\n}\n\nclass TimedOut extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"TimedOut\";\n  }\n}\n\nclass WriteZero extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"WriteZero\";\n  }\n}\n\nclass UnexpectedEof extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"UnexpectedEof\";\n  }\n}\n\nclass NotSupported extends Error {\n  constructor(msg) {\n    super(msg);\n    this.name = \"NotSupported\";\n  }\n}\n\nconst util = {\n  writable(value) {\n    return {\n      value,\n      writable: true,\n      enumerable: true,\n      configurable: true,\n    };\n  },\n  nonEnumerable(value) {\n    return {\n      value,\n      writable: true,\n      configurable: true,\n    };\n  },\n  readOnly(value) {\n    return {\n      value,\n      enumerable: true,\n    };\n  },\n  getterOnly(getter) {\n    return {\n      get: getter,\n      set() {\n      },\n      enumerable: true,\n      configurable: true,\n    };\n  },\n};\n\nclass Navigator {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n}\nconst NavigatorPrototype = Navigator.prototype;\n\nconst navigator = webidl.createBranded(Navigator);\n\nObjectDefineProperties(NavigatorPrototype, {\n  gpu: {\n    configurable: true,\n    enumerable: true,\n    get() {\n      webidl.assertBranded(this, NavigatorPrototype);\n      return webgpu.gpu;\n    },\n  },\n});\n\nconst windowOrWorkerGlobalScope = {\n  CloseEvent: util.nonEnumerable(event.CloseEvent),\n  CustomEvent: util.nonEnumerable(event.CustomEvent),\n  DOMException: util.nonEnumerable(DOMException),\n  ErrorEvent: util.nonEnumerable(event.ErrorEvent),\n  Event: util.nonEnumerable(event.Event),\n  EventTarget: util.nonEnumerable(event.EventTarget),\n  Navigator: util.nonEnumerable(Navigator),\n  navigator: util.getterOnly(() => navigator),\n  MessageEvent: util.nonEnumerable(event.MessageEvent),\n  Performance: util.nonEnumerable(performance.Performance),\n  PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),\n  PerformanceMark: util.nonEnumerable(performance.PerformanceMark),\n  PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),\n  TextDecoder: util.nonEnumerable(encoding.TextDecoder),\n  TextEncoder: util.nonEnumerable(encoding.TextEncoder),\n  URL: util.nonEnumerable(url.URL),\n  URLSearchParams: util.nonEnumerable(url.URLSearchParams),\n  atob: util.writable(base64.atob),\n  btoa: util.writable(base64.btoa),\n  console: util.writable(new Console(core.print)),\n  setInterval: util.writable(timers.setInterval),\n  setTimeout: util.writable(timers.setTimeout),\n  clearInterval: util.writable(timers.clearInterval),\n  clearTimeout: util.writable(timers.clearTimeout),\n  performance: util.writable(performance.performance),\n  ImageData: core.propNonEnumerable(imageData.ImageData),\n\n  GPU: util.nonEnumerable(webgpu.GPU),\n  GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),\n  GPUAdapterInfo: util.nonEnumerable(webgpu.GPUAdapterInfo),\n  GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),\n  GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),\n  GPUDeviceLostInfo: util.nonEnumerable(webgpu.GPUDeviceLostInfo),\n  GPUDevice: util.nonEnumerable(webgpu.GPUDevice),\n  GPUQueue: util.nonEnumerable(webgpu.GPUQueue),\n  GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),\n  GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),\n  GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),\n  GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),\n  GPUTexture: util.nonEnumerable(webgpu.GPUTexture),\n  GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),\n  GPUExternalTexture: util.nonEnumerable(webgpu.GPUExternalTexture),\n  GPUSampler: util.nonEnumerable(webgpu.GPUSampler),\n  GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),\n  GPUPipelineError: util.nonEnumerable(webgpu.GPUPipelineError),\n  GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),\n  GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),\n  GPUCompilationInfo: util.nonEnumerable(webgpu.GPUCompilationInfo),\n  GPUCompilationMessage: util.nonEnumerable(webgpu.GPUCompilationMessage),\n  GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),\n  GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),\n  GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),\n  GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),\n  GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),\n  GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),\n  GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),\n  GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),\n  GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),\n  GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),\n  GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),\n  GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),\n  GPUError: util.nonEnumerable(webgpu.GPUError),\n  GPUInternalError: util.nonEnumerable(webgpu.GPUInternalError),\n  GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),\n  GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),\n  GPUUncapturedErrorEvent: util.nonEnumerable(webgpu.GPUUncapturedErrorEvent),\n};\n\nwindowOrWorkerGlobalScope.console.enumerable = false;\n\n// Print uncaptured WebGPU errors to stderr. This is useful when running\n// standalone JavaScript test snippets. It isn't needed for the CTS, because the\n// CTS uses error scopes. (The CTS also installs its own error handler with\n// `addEventListener`, so having this here may result in printing duplicate\n// errors from the CTS in some cases.) Printing uncaptured errors to stderr\n// isn't desired as built-in behavior in Deno, because the console is reserved\n// for the application.\n//\n// Note that catching an error here _does not_ result in a non-zero exit status.\nlet enableExternalTexture_ = false;\nconst requestDevice = webgpu.GPUAdapter.prototype.requestDevice;\nwebgpu.GPUAdapter.prototype.requestDevice = function(desc) {\n    if (enableExternalTexture_) {\n        // Deno doesn't meaningfully support external textures, but we provide\n        // an option to enable it anyways to allow running some CTS tests that\n        // do pass.\n        if (!desc) {\n            desc = { requiredFeatures: ['wgpu-external-texture'] };\n        } else if (!desc.requiredFeatures) {\n            desc.requiredFeatures = ['wgpu-external-texture'];\n        } else {\n            desc.requiredFeatures.push('wgpu-external-texture');\n        }\n    }\n\n    return requestDevice.call(this, desc).then((device) => {\n        device.onuncapturederror = (event) => {\n            core.print(\"cts_runner caught WebGPU error: \" + event.error.message + \"\\n\", true);\n        };\n        return device;\n    })\n};\n\nconst mainRuntimeGlobalProperties = {\n  Window: globalInterfaces.windowConstructorDescriptor,\n  window: util.readOnly(globalThis),\n  self: util.readOnly(globalThis),\n};\n\nconst denoNs = {\n  exit(code) {\n    core.ops.op_exit(code);\n  },\n  readFileSync(path) {\n    return core.ops.op_read_file_sync(pathFromURL(path));\n  },\n  readTextFileSync(path) {\n    const buf = core.ops.op_read_file_sync(pathFromURL(path));\n    const decoder = new TextDecoder();\n    return decoder.decode(buf);\n  },\n  writeFileSync(path, buf) {\n    return core.ops.op_write_file_sync(pathFromURL(path), buf);\n  },\n};\n\ncore.registerErrorClass(\"NotFound\", NotFound);\ncore.registerErrorClass(\"AlreadyExists\", AlreadyExists);\ncore.registerErrorClass(\"InvalidData\", InvalidData);\ncore.registerErrorClass(\"TimedOut\", TimedOut);\ncore.registerErrorClass(\"WriteZero\", WriteZero);\ncore.registerErrorClass(\"UnexpectedEof\", UnexpectedEof);\ncore.registerErrorClass(\"NotSupported\", NotSupported);\ncore.registerErrorBuilder(\n  \"DOMExceptionOperationError\",\n  function DOMExceptionOperationError(msg) {\n    return new DOMException(msg, \"OperationError\");\n  },\n);\ncore.registerErrorBuilder(\n    \"DOMExceptionAbortError\",\n    function DOMExceptionAbortError(msg) {\n      return new domException.DOMException(msg, \"AbortError\");\n    },\n);\ncore.registerErrorBuilder(\n    \"DOMExceptionInvalidCharacterError\",\n    function DOMExceptionInvalidCharacterError(msg) {\n      return new domException.DOMException(msg, \"InvalidCharacterError\");\n    },\n);\ncore.registerErrorBuilder(\n    \"DOMExceptionDataError\",\n    function DOMExceptionDataError(msg) {\n      return new domException.DOMException(msg, \"DataError\");\n    },\n);\n\nlet hasBootstrapped = false;\n\nfunction bootstrapRuntime({ args, cwd, enableExternalTexture = false }) {\n  if (hasBootstrapped) {\n    throw new Error(\"Runtime has already been bootstrapped.\");\n  }\n  performance.setTimeOrigin(DateNow());\n  globalThis_ = globalThis;\n  enableExternalTexture_ = enableExternalTexture;\n\n  // Remove bootstrapping data from the global scope\n  delete globalThis.__bootstrap;\n  delete globalThis.bootstrap;\n  hasBootstrapped = true;\n\n  event.setEventTargetData(globalThis);\n  event.saveGlobalThisReference(globalThis);\n\n  Error.prepareStackTrace = core.prepareStackTrace;\n\n  ObjectDefineProperties(globalThis, windowOrWorkerGlobalScope);\n  ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);\n  ObjectSetPrototypeOf(globalThis, Window.prototype);\n  event.setEventTargetData(globalThis);\n\n  denoNs.args = args;\n  denoNs.cwd = () => cwd;\n\n  ObjectDefineProperty(globalThis, \"Deno\", util.readOnly(denoNs));\n\n  Error.prepareStackTrace = core.prepareStackTrace;\n}\n\nglobalThis.bootstrap = bootstrapRuntime;\n"
  },
  {
    "path": "cts_runner/src/main.rs",
    "content": "#![cfg_attr(target_arch = \"wasm32\", no_main)]\n#![cfg(not(target_arch = \"wasm32\"))]\n\nuse std::sync::Arc;\nuse std::{\n    env, fmt,\n    io::{Read, Write},\n    rc::Rc,\n};\n\nuse deno_core::anyhow::anyhow;\nuse deno_core::error::AnyError;\nuse deno_core::op2;\nuse deno_core::resolve_url_or_path;\nuse deno_core::serde_json::json;\nuse deno_core::v8;\nuse deno_core::JsRuntime;\nuse deno_core::RuntimeOptions;\nuse deno_web::BlobStore;\nuse termcolor::Ansi;\nuse termcolor::Color::Red;\nuse termcolor::ColorSpec;\nuse termcolor::WriteColor;\n\npub async fn run() -> Result<(), AnyError> {\n    let mut args = pico_args::Arguments::from_env();\n    let enable_external_texture = args.contains(\"--enable-external-texture\");\n    let url = args\n        .subcommand()\n        .ok()\n        .flatten()\n        .ok_or_else(|| anyhow!(\"missing specifier in first command line argument\"))?;\n    let specifier = resolve_url_or_path(&url, &env::current_dir()?)?;\n\n    #[cfg(target_os = \"windows\")]\n    match env::var(deno_webgpu::DX12_COMPILER_ENV_VAR) {\n        Ok(val) => {\n            log::info!(\n                \"Environment variable `{}` is set to `{val}`.\",\n                deno_webgpu::DX12_COMPILER_ENV_VAR,\n            );\n        }\n        Err(_) => {\n            log::info!(\n                \"cts_runner uses DXC by default. Configure with `{}` environment variable.\",\n                deno_webgpu::DX12_COMPILER_ENV_VAR\n            );\n            unsafe {\n                // SAFETY: Both of the following conditions apply; either is sufficient.\n                // 1. Calling `env::set_var` is always safe on Windows.\n                // 2. We are single-threaded at this point.\n                env::set_var(deno_webgpu::DX12_COMPILER_ENV_VAR, \"dynamicdxc\");\n            }\n        }\n    }\n\n    let options = RuntimeOptions {\n        module_loader: Some(Rc::new(deno_core::FsModuleLoader)),\n        extensions: vec![\n            deno_webidl::deno_webidl::init(),\n            deno_console::deno_console::init(),\n            deno_url::deno_url::init(),\n            deno_web::deno_web::init::<Permissions>(Arc::new(BlobStore::default()), None),\n            deno_webgpu::deno_webgpu::init(),\n            cts_runner::init(),\n        ],\n        ..Default::default()\n    };\n    let mut js_runtime = JsRuntime::new(options);\n    let args = args\n        .finish()\n        .into_iter()\n        .map(|os| os.into_string().ok())\n        .collect::<Option<Vec<String>>>()\n        .ok_or_else(|| anyhow!(\"Invalid UTF-8 in arguments\"))?;\n    let cfg = json!({\n        \"args\": args,\n        \"cwd\": env::current_dir().unwrap().to_string_lossy(),\n        \"enableExternalTexture\": enable_external_texture,\n    });\n\n    {\n        let context = js_runtime.main_context();\n        let scope = &mut js_runtime.handle_scope();\n        let context_local = v8::Local::new(scope, context);\n        let global_obj = context_local.global(scope);\n        let bootstrap_str = v8::String::new(scope, \"bootstrap\").unwrap();\n        let bootstrap_fn = global_obj.get(scope, bootstrap_str.into()).unwrap();\n        let bootstrap_fn = v8::Local::<v8::Function>::try_from(bootstrap_fn).unwrap();\n\n        let options_v8 = deno_core::serde_v8::to_v8(scope, cfg).unwrap();\n        let undefined = v8::undefined(scope);\n        bootstrap_fn\n            .call(scope, undefined.into(), &[options_v8])\n            .unwrap();\n    }\n\n    let mod_id = js_runtime.load_main_es_module(&specifier).await?;\n    let result = js_runtime.mod_evaluate(mod_id);\n    js_runtime.run_event_loop(Default::default()).await?;\n    result.await?;\n\n    Ok(())\n}\n\ndeno_core::extension!(\n    cts_runner,\n    deps = [deno_webidl, deno_web],\n    ops = [op_exit, op_read_file_sync, op_write_file_sync],\n    esm_entry_point = \"ext:cts_runner/src/bootstrap.js\",\n    esm = [\"src/bootstrap.js\"],\n    state = |state| {\n        let mut feature_checker = deno_features::FeatureChecker::default();\n        feature_checker.enable_feature(deno_webgpu::UNSTABLE_FEATURE_NAME);\n        state.put(feature_checker);\n        state.put(Permissions {});\n    }\n);\n\n#[op2(fast)]\nfn op_exit(code: i32) {\n    std::process::exit(code)\n}\n\n#[op2]\n#[buffer]\nfn op_read_file_sync(#[string] path: &str) -> Result<Vec<u8>, std::io::Error> {\n    let path = std::path::Path::new(path);\n    let mut file = std::fs::File::open(path)?;\n    let mut buf = Vec::new();\n    file.read_to_end(&mut buf)?;\n    Ok(buf)\n}\n\n#[op2(fast)]\nfn op_write_file_sync(#[string] path: &str, #[buffer] buf: &[u8]) -> Result<(), std::io::Error> {\n    let path = std::path::Path::new(path);\n    let mut file = std::fs::File::create(path)?;\n    file.write_all(buf)?;\n    Ok(())\n}\n\npub fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {\n    match result {\n        Ok(value) => value,\n        Err(error) => {\n            eprintln!(\"{}: {:?}\", red_bold(\"error\"), error);\n            std::process::exit(1);\n        }\n    }\n}\n\nfn style<S: AsRef<str>>(s: S, colorspec: ColorSpec) -> impl fmt::Display {\n    let mut v = Vec::new();\n    let mut ansi_writer = Ansi::new(&mut v);\n    ansi_writer.set_color(&colorspec).unwrap();\n    ansi_writer.write_all(s.as_ref().as_bytes()).unwrap();\n    ansi_writer.reset().unwrap();\n    String::from_utf8_lossy(&v).into_owned()\n}\n\nfn red_bold<S: AsRef<str>>(s: S) -> impl fmt::Display {\n    let mut style_spec = ColorSpec::new();\n    style_spec.set_fg(Some(Red)).set_bold(true);\n    style(s, style_spec)\n}\n\n// NOP permissions\nstruct Permissions;\n\nimpl deno_web::TimersPermission for Permissions {\n    fn allow_hrtime(&mut self) -> bool {\n        false\n    }\n}\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() {\n    env_logger::init();\n    unwrap_or_exit(run().await)\n}\n"
  },
  {
    "path": "cts_runner/test.lst",
    "content": "// CTS test selectors that are expected to pass. These are run in CI.\n//\n// The following may be useful to generate lists of tests for this file:\n// ```\n// cargo xtask cts -- --list <selector>\n// ```\n//\n// The `cts_runner` integration test `lst_files_are_sorted` verifies that test selectors\n// appear in this file in order -- including ones in comments.\n\nunittests:*\n\nwebgpu:api,operation,adapter,requestAdapter:*\nwebgpu:api,operation,buffers,createBindGroup:buffer_binding_resource:*\nwebgpu:api,operation,command_buffer,basic:*\nwebgpu:api,operation,command_buffer,copyBufferToBuffer:*\nfails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"depth16unorm\"\nfails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"depth24plus\"\nfails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"depth24plus-stencil8\"\nfails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"depth32float\"\nfails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"depth32float-stencil8\"\nwebgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format=\"stencil8\"\nfails-if(dx12) webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes:*\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"CopyB2T\";checkMethod=\"FullCopyT2B\";dimension=\"1d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"CopyB2T\";checkMethod=\"FullCopyT2B\";dimension=\"2d\"\nfails-if(dx12,vulkan,metal) webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"CopyB2T\";checkMethod=\"FullCopyT2B\";dimension=\"3d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"FullCopyT2B\";dimension=\"1d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"FullCopyT2B\";dimension=\"2d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"FullCopyT2B\";dimension=\"3d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"PartialCopyT2B\";dimension=\"1d\"\nfails-if(dx12) webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"PartialCopyT2B\";dimension=\"2d\"\nwebgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod=\"WriteTexture\";checkMethod=\"PartialCopyT2B\";dimension=\"3d\"\nwebgpu:api,operation,compute_pipeline,overrides:*\n//FAIL: webgpu:api,operation,compute,basic:large_dispatch:*\nwebgpu:api,operation,compute,basic:memcpy:*\nwebgpu:api,operation,device,lost:*\nwebgpu:api,operation,render_pass,storeOp:*\nwebgpu:api,operation,render_pipeline,overrides:*\nwebgpu:api,operation,render_pipeline,pipeline_output_targets:color,component_count,blend:*\nwebgpu:api,operation,rendering,3d_texture_slices:*\nwebgpu:api,operation,rendering,basic:clear:*\nwebgpu:api,operation,rendering,basic:fullscreen_quad:*\n//FAIL: webgpu:api,operation,rendering,basic:large_draw:*\nwebgpu:api,operation,rendering,color_target_state:blend_constant,setting:*\nwebgpu:api,operation,rendering,color_target_state:blending,formats:*\nwebgpu:api,operation,rendering,color_target_state:blending,GPUBlendComponent:*\nwebgpu:api,operation,rendering,depth:*\nwebgpu:api,operation,rendering,draw:*\nwebgpu:api,operation,shader_module,compilation_info:*\nwebgpu:api,operation,uncapturederror:iff_uncaptured:*\n//FAIL: webgpu:api,operation,uncapturederror:onuncapturederror_order_wrt_addEventListener\n// There are also two unimplemented SKIPs in uncapturederror not enumerated here.\nfails-if(vulkan) webgpu:api,operation,vertex_state,correctness:array_stride_zero:*\nwebgpu:api,operation,vertex_state,correctness:non_zero_array_stride_and_attribute_offset:*\nwebgpu:api,operation,vertex_state,correctness:setVertexBuffer_offset_and_attribute_offset:*\n\nwebgpu:api,validation,buffer,create:*\nwebgpu:api,validation,buffer,destroy:*\nwebgpu:api,validation,capability_checks,features,clip_distances:*\nwebgpu:api,validation,capability_checks,limits,maxBindGroups:setBindGroup,*\nwebgpu:api,validation,capability_checks,limits,maxBindingsPerBindGroup:validate,*\nwebgpu:api,validation,capability_checks,limits,maxBufferSize:*\nwebgpu:api,validation,capability_checks,limits,maxComputeWorkgroupSizeX:validate,*\nwebgpu:api,validation,capability_checks,limits,maxComputeWorkgroupSizeY:validate,*\nwebgpu:api,validation,capability_checks,limits,maxComputeWorkgroupSizeZ:validate,*\nwebgpu:api,validation,capability_checks,limits,maxComputeWorkgroupsPerDimension:validate,*\n//FAIL: other `maxInterStageShaderVariables` cases w/ limitTest ∈ [underDefault, overMaximum]\n// https://github.com/gfx-rs/wgpu/issues/8945\nwebgpu:api,validation,capability_checks,limits,maxInterStageShaderVariables:createRenderPipeline,at_over:limitTest=\"atDefault\";*\nwebgpu:api,validation,capability_checks,limits,maxInterStageShaderVariables:createRenderPipeline,at_over:limitTest=\"atMaximum\";*\nwebgpu:api,validation,capability_checks,limits,maxInterStageShaderVariables:createRenderPipeline,at_over:limitTest=\"betweenDefaultAndMaximum\";*\nwebgpu:api,validation,capability_checks,limits,maxStorageBufferBindingSize:validate,*\nwebgpu:api,validation,capability_checks,limits,maxUniformBufferBindingSize:validate,*\nwebgpu:api,validation,capability_checks,limits,maxVertexBufferArrayStride:validate,*\nwebgpu:api,validation,capability_checks,limits,minStorageBufferOffsetAlignment:validate,*\nwebgpu:api,validation,capability_checks,limits,minUniformBufferOffsetAlignment:validate,*\nwebgpu:api,validation,compute_pipeline:basic:*\nwebgpu:api,validation,compute_pipeline:limits,invocations_per_workgroup,each_component:*\nwebgpu:api,validation,compute_pipeline:limits,invocations_per_workgroup:*\nwebgpu:api,validation,compute_pipeline:overrides,entry_point,validation_error:*\nwebgpu:api,validation,compute_pipeline:overrides,identifier:*\nwebgpu:api,validation,compute_pipeline:overrides,uninitialized:*\nwebgpu:api,validation,compute_pipeline:overrides,value,type_error:*\nwebgpu:api,validation,compute_pipeline:overrides,value,validation_error,f16:*\nwebgpu:api,validation,compute_pipeline:overrides,value,validation_error:*\nwebgpu:api,validation,compute_pipeline:overrides,workgroup_size:*\nwebgpu:api,validation,compute_pipeline:pipeline_layout,device_mismatch:*\nwebgpu:api,validation,compute_pipeline:resource_compatibility:*\nwebgpu:api,validation,compute_pipeline:shader_module,*\nwebgpu:api,validation,compute_pipeline:storage_texture,format:*\nwebgpu:api,validation,createBindGroup:bind_group_layout,device_mismatch:*\nwebgpu:api,validation,createBindGroup:binding_count_mismatch:*\nwebgpu:api,validation,createBindGroup:binding_must_be_present_in_layout:*\nwebgpu:api,validation,createBindGroup:binding_must_contain_resource_defined_in_layout:*\nwebgpu:api,validation,createBindGroup:binding_resources,device_mismatch:*\nwebgpu:api,validation,createBindGroup:buffer_offset_and_size_for_bind_groups_match:*\nwebgpu:api,validation,createBindGroup:buffer,effective_buffer_binding_size:*\nwebgpu:api,validation,createBindGroup:buffer,resource_binding_size:*\nwebgpu:api,validation,createBindGroup:buffer,resource_offset:*\nwebgpu:api,validation,createBindGroup:buffer,usage:*\nwebgpu:api,validation,createBindGroup:minBindingSize:*\nwebgpu:api,validation,createBindGroup:multisampled_validation:*\nwebgpu:api,validation,createBindGroup:sampler,*\nwebgpu:api,validation,createBindGroup:storage_texture,*\nwebgpu:api,validation,createBindGroup:texture_binding_must_have_correct_usage:*\nwebgpu:api,validation,createBindGroup:texture_must_have_correct_component_type:*\nwebgpu:api,validation,createBindGroup:texture_must_have_correct_dimension:*\nwebgpu:api,validation,createBindGroupLayout:duplicate_bindings:*\nwebgpu:api,validation,createBindGroupLayout:max_dynamic_buffers:*\n// Doesn't actually fail, but is very slow. https://github.com/gfx-rs/wgpu/issues/9229\nfails-if(dx12) webgpu:api,validation,createBindGroupLayout:max_resources_per_stage,*\nwebgpu:api,validation,createBindGroupLayout:maximum_binding_limit:*\nwebgpu:api,validation,createBindGroupLayout:multisampled_validation:*\nwebgpu:api,validation,createBindGroupLayout:storage_texture,formats:*\nwebgpu:api,validation,createBindGroupLayout:storage_texture,layout_dimension:*\nwebgpu:api,validation,createPipelineLayout:*\nwebgpu:api,validation,createSampler:*\nwebgpu:api,validation,createTexture:*\nwebgpu:api,validation,createView:array_layers:*\nwebgpu:api,validation,createView:aspect:*\nwebgpu:api,validation,createView:cube_faces_square:*\nwebgpu:api,validation,createView:dimension:*\nwebgpu:api,validation,createView:format:*\nwebgpu:api,validation,createView:mip_levels:*\nwebgpu:api,validation,createView:texture_view_usage_with_view_format:*\nwebgpu:api,validation,createView:texture_view_usage:*\nwebgpu:api,validation,debugMarker:*\nwebgpu:api,validation,encoding,beginComputePass:*\nwebgpu:api,validation,encoding,beginRenderPass:*\nwebgpu:api,validation,encoding,cmds,clearBuffer:*\nwebgpu:api,validation,encoding,cmds,compute_pass:*\nwebgpu:api,validation,encoding,cmds,copyBufferToBuffer:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_aspects:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges_with_compressed_texture_formats:*\n// Intermittently fails on dx12, https://github.com/gfx-rs/wgpu/issues/8118\nfails-if(dx12) webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_with_invalid_or_destroyed_texture:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_within_same_texture:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:depth_stencil_copy_restrictions:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:mipmap_level:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:multisampled_copy_restrictions:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:sample_count:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_format_compatibility:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_usage:*\nwebgpu:api,validation,encoding,cmds,copyTextureToTexture:texture,device_mismatch:*\nwebgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType=\"compute%20pass\"\nwebgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType=\"non-pass\"\n//FAIL: webgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType=\"render%20bundle\"\n// https://github.com/gfx-rs/wgpu/issues/8039\nwebgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType=\"render%20pass\"\nwebgpu:api,validation,encoding,cmds,debug:debug_group:*\nwebgpu:api,validation,encoding,cmds,debug:debug_marker:*\nwebgpu:api,validation,encoding,cmds,index_access:*\nwebgpu:api,validation,encoding,cmds,render,draw:index_buffer_OOB:*\nwebgpu:api,validation,encoding,cmds,render,draw:unused_buffer_bound:*\nwebgpu:api,validation,encoding,cmds,render,dynamic_state:*\nwebgpu:api,validation,encoding,cmds,render,indirect_draw:indirect_buffer_usage:*\nwebgpu:api,validation,encoding,cmds,render,indirect_draw:indirect_buffer,device_mismatch:*\nwebgpu:api,validation,encoding,cmds,render,setIndexBuffer:*\nwebgpu:api,validation,encoding,cmds,render,setPipeline:*\nwebgpu:api,validation,encoding,cmds,render,setVertexBuffer:*\nwebgpu:api,validation,encoding,cmds,render,state_tracking:vertex_buffers_do_not_inherit_between_render_passes:*\nwebgpu:api,validation,encoding,cmds,render,state_tracking:vertex_buffers_inherit_from_previous_pipeline:*\nwebgpu:api,validation,encoding,cmds,setBindGroup:bind_group,device_mismatch:*\nwebgpu:api,validation,encoding,cmds,setBindGroup:buffer_dynamic_offsets:*\nwebgpu:api,validation,encoding,cmds,setBindGroup:dynamic_offsets_passed_but_not_expected:*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"compute%20pass\";state=\"invalid\";*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"compute%20pass\";state=\"valid\";*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20bundle\";state=\"invalid\";*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20bundle\";state=\"valid\";*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20pass\";state=\"invalid\";*\nwebgpu:api,validation,encoding,cmds,setBindGroup:state_and_binding_index:encoderType=\"render%20pass\";state=\"valid\";*\nwebgpu:api,validation,encoding,encoder_open_state:compute_pass_commands:*\nwebgpu:api,validation,encoding,encoder_open_state:non_pass_commands:*\n//FAIL: webgpu:api,validation,encoding,encoder_open_state:render_bundle_commands:*\n// https://github.com/gfx-rs/wgpu/issues/7857\nwebgpu:api,validation,encoding,encoder_open_state:render_pass_commands:*\nwebgpu:api,validation,encoding,encoder_state:*\nwebgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:*\nwebgpu:api,validation,encoding,queries,begin_end:nesting:*\nwebgpu:api,validation,encoding,queries,begin_end:occlusion_query,*\nwebgpu:api,validation,encoding,queries,general:occlusion_query,*\nwebgpu:api,validation,error_scope:*\nwebgpu:api,validation,getBindGroupLayout:*\nwebgpu:api,validation,image_copy,buffer_related:*\n// image_copy depth/stencil failures on dx12: https://github.com/gfx-rs/wgpu/issues/8133\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format=\"depth24plus-stencil8\";aspect=\"stencil-only\";copyType=\"CopyB2T\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format=\"depth24plus-stencil8\";aspect=\"stencil-only\";copyType=\"CopyT2B\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format=\"depth32float\";aspect=\"depth-only\";copyType=\"CopyT2B\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format=\"stencil8\";aspect=\"stencil-only\";copyType=\"CopyB2T\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format=\"stencil8\";aspect=\"stencil-only\";copyType=\"CopyT2B\"\nwebgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_size:*\nwebgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_usage_and_aspect:*\nwebgpu:api,validation,image_copy,buffer_texture_copies:device_mismatch:*\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"astc-4x4-unorm\";copyType=\"CopyB2T\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"astc-4x4-unorm\";copyType=\"CopyB2T\";dimension=\"3d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"astc-4x4-unorm\";copyType=\"CopyT2B\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"astc-4x4-unorm\";copyType=\"CopyT2B\";dimension=\"3d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"bgra8unorm\";copyType=\"CopyB2T\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"bgra8unorm\";copyType=\"CopyT2B\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"r8uint\";copyType=\"CopyB2T\";dimension=\"1d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"r8uint\";copyType=\"CopyT2B\";dimension=\"1d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba32float\";copyType=\"CopyB2T\";dimension=\"1d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba32float\";copyType=\"CopyT2B\";dimension=\"1d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba8unorm\";copyType=\"CopyB2T\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba8unorm\";copyType=\"CopyB2T\";dimension=\"3d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba8unorm\";copyType=\"CopyT2B\";dimension=\"2d\"\nfails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format=\"rgba8unorm\";copyType=\"CopyT2B\";dimension=\"3d\"\nwebgpu:api,validation,image_copy,buffer_texture_copies:sample_count:*\nwebgpu:api,validation,image_copy,buffer_texture_copies:texture_buffer_usages:*\nwebgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:*\nwebgpu:api,validation,image_copy,layout_related:bound_on_offset:*\nwebgpu:api,validation,image_copy,layout_related:bound_on_rows_per_image:*\nwebgpu:api,validation,image_copy,layout_related:copy_end_overflows_u64:*\nwebgpu:api,validation,image_copy,layout_related:offset_alignment:*\nwebgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:*\nwebgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:*\nwebgpu:api,validation,image_copy,texture_related:*\nfails-if(dx12) webgpu:api,validation,layout_shader_compat:pipeline_layout_shader_exact_match:*\nwebgpu:api,validation,query_set,destroy:*\nwebgpu:api,validation,queue,buffer_mapped:copyBufferToBuffer:*\nwebgpu:api,validation,queue,buffer_mapped:copyBufferToTexture:*\nwebgpu:api,validation,queue,buffer_mapped:copyTextureToBuffer:*\nwebgpu:api,validation,queue,buffer_mapped:map_command_recording_order:*\n// `vulkan` failure: https://github.com/gfx-rs/wgpu/issues/????\nfails-if(vulkan) webgpu:api,validation,queue,buffer_mapped:writeBuffer:*\nwebgpu:api,validation,queue,submit:command_buffer,*\nwebgpu:api,validation,queue,writeBuffer:buffer_state:*\nwebgpu:api,validation,queue,writeBuffer:buffer,device_mismatch:*\nwebgpu:api,validation,queue,writeBuffer:usages:*\nwebgpu:api,validation,queue,writeTexture:*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_and_bundle,*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,color_count:*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,color_format:*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,color_sparse:*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,depth_format:*\nfails-if(dx12) webgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,depth_stencil_read_only_write_state:*\nwebgpu:api,validation,render_pass,attachment_compatibility:render_pass_or_bundle_and_pipeline,sample_count:*\nwebgpu:api,validation,render_pass,render_pass_descriptor:attachments,*\nwebgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,*\nwebgpu:api,validation,render_pass,render_pass_descriptor:depth_stencil_attachment,depth_clear_value:*\nwebgpu:api,validation,render_pass,render_pass_descriptor:depth_stencil_attachment,sample_counts_mismatch:*\nwebgpu:api,validation,render_pass,render_pass_descriptor:occlusionQuerySet,query_set_type:*\nwebgpu:api,validation,render_pass,render_pass_descriptor:resolveTarget,*\nwebgpu:api,validation,render_pass,render_pass_descriptor:timestampWrite,query_index:*\nwebgpu:api,validation,render_pass,render_pass_descriptor:timestampWrites,query_set_type:*\nwebgpu:api,validation,render_pass,resolve:resolve_attachment:*\nwebgpu:api,validation,render_pipeline,depth_stencil_state:*\nwebgpu:api,validation,render_pipeline,float32_blendable:*\nwebgpu:api,validation,render_pipeline,fragment_state:color_target_exists:*\nwebgpu:api,validation,render_pipeline,fragment_state:dual_source_blending,*\nwebgpu:api,validation,render_pipeline,fragment_state:limits,*\nwebgpu:api,validation,render_pipeline,fragment_state:targets_blend:*\nwebgpu:api,validation,render_pipeline,fragment_state:targets_format_filterable:*\nwebgpu:api,validation,render_pipeline,fragment_state:targets_format_is_color_format:*\nwebgpu:api,validation,render_pipeline,fragment_state:targets_format_renderable:*\nwebgpu:api,validation,render_pipeline,fragment_state:targets_write_mask:*\nwebgpu:api,validation,render_pipeline,inter_stage:location,*\nwebgpu:api,validation,render_pipeline,inter_stage:max_shader_variable_location:*\nfails-if(dx12) webgpu:api,validation,render_pipeline,inter_stage:max_variables_count,*\nwebgpu:api,validation,render_pipeline,misc:basic:*\nfails-if(vulkan) webgpu:api,validation,render_pipeline,misc:external_texture:*\nwebgpu:api,validation,render_pipeline,misc:no_attachment:*\nwebgpu:api,validation,render_pipeline,misc:pipeline_layout,device_mismatch:*\nwebgpu:api,validation,render_pipeline,misc:vertex_state_only:*\nwebgpu:api,validation,render_pipeline,primitive_state:*\nwebgpu:api,validation,render_pipeline,resource_compatibility:*\nwebgpu:api,validation,render_pipeline,shader_module:*\nwebgpu:api,validation,render_pipeline,vertex_state:many_attributes_overlapping:*\nwebgpu:api,validation,render_pipeline,vertex_state:max_vertex_buffer_array_stride_limit:*\nfails-if(metal) webgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_contained_in_stride:*\nfails-if(metal) webgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_offset_alignment:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_shaderLocation_limit:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_attribute_shaderLocation_unique:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_buffer_array_stride_limit_alignment:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_shader_input_location_in_vertex_state:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_shader_input_location_limit:*\nwebgpu:api,validation,render_pipeline,vertex_state:vertex_shader_type_matches_attribute_format:*\nwebgpu:api,validation,resource_usages,buffer,in_pass_encoder:*\nwebgpu:api,validation,resource_usages,buffer,in_pass_misc:*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:bindings_in_bundle:*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:replaced_binding:*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:scope,*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:shader_stages_and_visibility,*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:*\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_color:compute=false;type0=\"render-target\";type1=\"render-target\"\nwebgpu:api,validation,resource_usages,texture,in_pass_encoder:unused_bindings_in_pipeline:*\nwebgpu:api,validation,resource_usages,texture,in_render_common:subresources,color_attachment_and_bind_group:*\nwebgpu:api,validation,resource_usages,texture,in_render_common:subresources,color_attachments:*\n// FAIL: webgpu:api,validation,resource_usages,texture,in_render_common:subresources,depth_stencil_attachment_and_bind_group:*\n// https://github.com/gfx-rs/wgpu/issues/8705\nwebgpu:api,validation,resource_usages,texture,in_render_common:subresources,depth_stencil_texture_in_bind_groups:*\nwebgpu:api,validation,resource_usages,texture,in_render_common:subresources,multiple_bind_groups:*\nwebgpu:api,validation,resource_usages,texture,in_render_misc:*\nwebgpu:api,validation,shader_module,*\nwebgpu:api,validation,texture,bgra8unorm_storage:*\nwebgpu:api,validation,texture,destroy:base:*\nwebgpu:api,validation,texture,destroy:invalid_texture:*\nwebgpu:api,validation,texture,destroy:twice:*\nwebgpu:api,validation,texture,float32_filterable:create_bind_group:*\nwebgpu:api,validation,texture,rg11b10ufloat_renderable:*\n\nfails-if(dx12) webgpu:shader,execution,expression,call,builtin,atan2:f16:*\nwebgpu:shader,execution,expression,call,builtin,atan2:f32:*\nwebgpu:shader,execution,expression,call,builtin,dot:abstract_int_vec2:*\nwebgpu:shader,execution,expression,call,builtin,dot:abstract_int_vec3:*\nwebgpu:shader,execution,expression,call,builtin,dot:abstract_int_vec4:*\nwebgpu:shader,execution,expression,call,builtin,dot:i32_vec2:*\nwebgpu:shader,execution,expression,call,builtin,dot:i32_vec3:*\nwebgpu:shader,execution,expression,call,builtin,dot:i32_vec4:*\nwebgpu:shader,execution,expression,call,builtin,dot:u32_vec2:*\nwebgpu:shader,execution,expression,call,builtin,dot:u32_vec3:*\nwebgpu:shader,execution,expression,call,builtin,dot:u32_vec4:*\n//FAIL: webgpu:shader,execution,expression,call,builtin,select:*\n// - Fails with `const`/abstract int cases on all platforms because of <https://github.com/gfx-rs/wgpu/issues/4507>.\n// - Fails with `vec3` & `f16` cases on macOS because of <https://github.com/gfx-rs/wgpu/issues/5262>.\nwebgpu:shader,execution,expression,call,builtin,textureSample:sampled_1d_coords:*\nwebgpu:shader,execution,expression,call,builtin,textureSampleBaseClampToEdge:2d_coords:stage=\"c\";textureType=\"texture_2d<f32>\";*\n// NOTE: This is supposed to be an exhaustive listing underneath\n// `webgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:*`, so exceptions can be\n// worked around.\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"array%3Cu32,%204%3E\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"atomic%3Ci32%3E\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"atomic%3Cu32%3E\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"AtomicInStruct\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"bool\";*\n//FAIL: https://github.com/gfx-rs/wgpu/issues/8812\n// webgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"ComplexStruct\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"mat3x2f\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"SimpleStruct\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"u32\";*\nwebgpu:shader,execution,expression,call,builtin,workgroupUniformLoad:types:type=\"vec4u\";*\nwebgpu:shader,execution,expression,unary,bool_conversion:*\nwebgpu:shader,execution,flow_control,return:*\n// Many other vertex_buffer_access subtests also passing, but there are too many to enumerate.\n// Fails on Metal in CI only, not when running locally.\nfails-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\nwebgpu:shader,execution,shader_io,fragment_builtins:inputs,front_facing:*\nwebgpu:shader,execution,shader_io,fragment_builtins:inputs,interStage,centroid:*\nfails-if(vulkan) webgpu:shader,execution,shader_io,fragment_builtins:inputs,interStage:*\nfails-if(dx12,vulkan) webgpu:shader,execution,shader_io,fragment_builtins:inputs,position:*\nwebgpu:shader,execution,shader_io,fragment_builtins:inputs,sample_index:*\nfails-if(dx12,vulkan) webgpu:shader,execution,shader_io,fragment_builtins:inputs,sample_mask:*\nwebgpu:shader,execution,shader_io,fragment_builtins:primitive_index,*\nwebgpu:shader,execution,shader_io,fragment_builtins:subgroup_invocation_id:*\nwebgpu:shader,execution,shader_io,fragment_builtins:subgroup_size:*\nwebgpu:shader,execution,shader_io,vertex_builtins:outputs,clip_distances:*\nwebgpu:shader,execution,statement,compound:*\nwebgpu:shader,validation,const_assert,const_assert:*\nwebgpu:shader,validation,decl,assignment_statement:*\nwebgpu:shader,validation,decl,compound_statement:*\nwebgpu:shader,validation,decl,const:*\nwebgpu:shader,validation,expression,access,array:early_eval_errors:case=\"override_array_cnt_size_neg\"\nwebgpu:shader,validation,expression,access,array:early_eval_errors:case=\"override_array_cnt_size_one\"\nwebgpu:shader,validation,expression,access,array:early_eval_errors:case=\"override_array_cnt_size_zero_signed\"\nwebgpu:shader,validation,expression,access,array:early_eval_errors:case=\"override_array_cnt_size_zero_unsigned\"\nwebgpu:shader,validation,expression,access,array:early_eval_errors:case=\"override_in_bounds\"\nwebgpu:shader,validation,expression,access,structure:*\nwebgpu:shader,validation,expression,binary,add_sub_mul:scalar_vector_out_of_range:lhs=\"i32\";*\nwebgpu:shader,validation,expression,binary,add_sub_mul:scalar_vector_out_of_range:lhs=\"u32\";*\nwebgpu:shader,validation,expression,binary,bitwise_shift:partial_eval_errors:*\nwebgpu:shader,validation,expression,binary,bitwise_shift:scalar_vector:*\nwebgpu:shader,validation,expression,binary,bitwise_shift:shift_left_abstract:*\nwebgpu:shader,validation,expression,binary,bitwise_shift:shift_left_concrete:*\nwebgpu:shader,validation,expression,binary,bitwise_shift:shift_right_abstract:*\nwebgpu:shader,validation,expression,binary,bitwise_shift:shift_right_concrete:*\nwebgpu:shader,validation,expression,binary,parse:*\nwebgpu:shader,validation,expression,binary,short_circuiting_and_or:array_override:op=\"%26%26\";a_val=1;b_val=1\nwebgpu:shader,validation,expression,binary,short_circuiting_and_or:invalid_types:*\nwebgpu:shader,validation,expression,binary,short_circuiting_and_or:scalar_vector:op=\"%26%26\";lhs=\"bool\";rhs=\"bool\"\nwebgpu:shader,validation,expression,call,builtin,acos:*\nwebgpu:shader,validation,expression,call,builtin,all:*\nwebgpu:shader,validation,expression,call,builtin,any:*\nwebgpu:shader,validation,expression,call,builtin,arrayLength:*\nwebgpu:shader,validation,expression,call,builtin,asin:*\nwebgpu:shader,validation,expression,call,builtin,asinh:*\nwebgpu:shader,validation,expression,call,builtin,atan:*\nwebgpu:shader,validation,expression,call,builtin,atan2:*\nwebgpu:shader,validation,expression,call,builtin,atanh:*\nwebgpu:shader,validation,expression,call,builtin,barriers:*\nwebgpu:shader,validation,expression,call,builtin,ceil:*\nwebgpu:shader,validation,expression,call,builtin,cos:*\nwebgpu:shader,validation,expression,call,builtin,cosh:*\nwebgpu:shader,validation,expression,call,builtin,degrees:*\nwebgpu:shader,validation,expression,call,builtin,distance:values:stage=\"constant\";type=\"f32\"\nwebgpu:shader,validation,expression,call,builtin,dot:*\nwebgpu:shader,validation,expression,call,builtin,exp:*\nwebgpu:shader,validation,expression,call,builtin,exp2:*\nwebgpu:shader,validation,expression,call,builtin,floor:*\nwebgpu:shader,validation,expression,call,builtin,fract:*\nwebgpu:shader,validation,expression,call,builtin,length:scalar:stage=\"constant\";type=\"f32\"\nwebgpu:shader,validation,expression,call,builtin,log:*\nwebgpu:shader,validation,expression,call,builtin,log2:*\nwebgpu:shader,validation,expression,call,builtin,max:*\nwebgpu:shader,validation,expression,call,builtin,min:*\nwebgpu:shader,validation,expression,call,builtin,radians:*\nwebgpu:shader,validation,expression,call,builtin,round:*\nwebgpu:shader,validation,expression,call,builtin,saturate:*\nwebgpu:shader,validation,expression,call,builtin,sign:*\nwebgpu:shader,validation,expression,call,builtin,sin:*\nwebgpu:shader,validation,expression,call,builtin,sinh:*\nwebgpu:shader,validation,expression,call,builtin,sqrt:*\nwebgpu:shader,validation,expression,call,builtin,step:*\nwebgpu:shader,validation,expression,call,builtin,tan:*\nwebgpu:shader,validation,expression,call,builtin,tanh:*\n// Uses external textures\nfails-if(vulkan) webgpu:shader,validation,expression,call,builtin,textureLoad:*\nwebgpu:shader,validation,expression,call,builtin,textureNumLayers:*\nwebgpu:shader,validation,expression,call,builtin,textureNumLevels:*\nwebgpu:shader,validation,expression,call,builtin,textureNumSamples:*\n// Uses external textures\nfails-if(vulkan) webgpu:shader,validation,expression,call,builtin,textureSampleBaseClampToEdge:*\nwebgpu:shader,validation,expression,call,builtin,textureStore:*\nwebgpu:shader,validation,expression,call,builtin,trunc:*\nwebgpu:shader,validation,expression,call,builtin,value_constructor:*\nwebgpu:shader,validation,expression,call,builtin,workgroupUniformLoad:*\nwebgpu:shader,validation,expression,overload_resolution:*\nwebgpu:shader,validation,extension,clip_distances:*\nwebgpu:shader,validation,extension,dual_source_blending:*\nwebgpu:shader,validation,extension,pointer_composite_access:*\nwebgpu:shader,validation,functions,restrictions:param_type_can_be_alias:*\nwebgpu:shader,validation,parse,blankspace:blankspace:*\nwebgpu:shader,validation,parse,blankspace:bom:*\nwebgpu:shader,validation,parse,blankspace:null_characters:contains_null=false;*\nwebgpu:shader,validation,parse,blankspace:null_characters:contains_null=true;placement=\"delimiter\"\nwebgpu:shader,validation,parse,blankspace:null_characters:contains_null=true;placement=\"eol\"\nwebgpu:shader,validation,parse,enable:*\nwebgpu:shader,validation,parse,identifiers:*\nwebgpu:shader,validation,parse,requires:*\nwebgpu:shader,validation,parse,semicolon:*\nwebgpu:shader,validation,parse,shadow_builtins:function_param:*\nwebgpu:shader,validation,parse,source:*\nwebgpu:shader,validation,shader_io,entry_point:*\n// Uses external texture bindings\nfails-if(vulkan) webgpu:shader,validation,shader_io,group_and_binding:*\nwebgpu:shader,validation,shader_io,invariant:*\nwebgpu:shader,validation,shader_io,locations:duplicates:*\nwebgpu:shader,validation,shader_io,locations:location_fp16:*\nwebgpu:shader,validation,shader_io,locations:nesting:*\nwebgpu:shader,validation,shader_io,locations:out_of_order:*\nwebgpu:shader,validation,shader_io,locations:type:*\nwebgpu:shader,validation,shader_io,locations:validation:*\nwebgpu:shader,validation,statement,break_if:*\nwebgpu:shader,validation,statement,break:*\nwebgpu:shader,validation,statement,compound:*\nwebgpu:shader,validation,statement,const_assert:*\nwebgpu:shader,validation,statement,continuing:*\nwebgpu:shader,validation,statement,discard:*\nwebgpu:shader,validation,statement,if:*\nwebgpu:shader,validation,statement,return:*\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"break_if\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"break\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"continue\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"for3\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"for4\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"for5\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"loop4\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"loop5\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"loop6\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"loop8\"\nwebgpu:shader,validation,statement,statement_behavior:invalid_statements:body=\"switch1\"\nwebgpu:shader,validation,statement,while:*\nwebgpu:util,texture,texture_ok:*\n"
  },
  {
    "path": "cts_runner/tests/integration.rs",
    "content": "use std::{\n    ffi::OsStr,\n    fs,\n    io::Write,\n    path::PathBuf,\n    process::{Command, Output},\n    str,\n};\n\nuse tempfile::NamedTempFile;\n\npub fn target_dir() -> PathBuf {\n    let current_exe = std::env::current_exe().unwrap();\n    let target_dir = current_exe.parent().unwrap().parent().unwrap();\n    target_dir.into()\n}\n\npub fn cts_runner_exe_path() -> PathBuf {\n    // Something like /Users/lucacasonato/src/wgpu/target/debug/cts_runner\n    let mut p = target_dir().join(\"cts_runner\");\n    if cfg!(windows) {\n        p.set_extension(\"exe\");\n    }\n    p\n}\n\nfn exec_cts_runner(script_file: impl AsRef<OsStr>) -> Output {\n    Command::new(cts_runner_exe_path())\n        .arg(script_file)\n        .output()\n        .unwrap()\n}\n\n// The idea here is that if the test outputs something on stderr, we want to\n// print it verbatim, not as a quoted string with escape sequences.\nstruct Error(String);\n\nimpl std::fmt::Debug for Error {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(&self.0)\n    }\n}\n\nfn exec_js_file(script_file: impl AsRef<OsStr>) -> Result<(), Error> {\n    let output = exec_cts_runner(script_file);\n    println!(\"{}\", str::from_utf8(&output.stdout).unwrap());\n    eprintln!(\"{}\", str::from_utf8(&output.stderr).unwrap());\n    if !output.status.success() {\n        return Err(Error(format!(\n            \"process exited unsuccessfully: {}\",\n            output.status\n        )));\n    }\n    Ok(())\n}\n\nfn check_js_stderr(script: &str, expected: &str) -> Result<(), Error> {\n    let mut tempfile = NamedTempFile::new().unwrap();\n    tempfile.write_all(script.as_bytes()).unwrap();\n    tempfile.flush().unwrap();\n    let output = exec_cts_runner(tempfile.path());\n    if !output.stdout.is_empty() {\n        return Err(Error(format!(\n            \"unexpected output on stdout: {}\",\n            str::from_utf8(&output.stdout).unwrap(),\n        )));\n    }\n    let stderr_str = str::from_utf8(&output.stderr).unwrap();\n    if expected.is_empty() && !stderr_str.is_empty() {\n        return Err(Error(format!(\n            \"unexpected output on stderr: {}\",\n            stderr_str,\n        )));\n    } else if stderr_str != expected {\n        return Err(Error(format!(\n            \"expected the following output on stderr:\\n{}\\n\\nbut observed:\\n{}\",\n            expected, stderr_str,\n        )));\n    }\n    if !output.status.success() {\n        return Err(Error(format!(\n            \"process exited unsuccessfully: {}\",\n            output.status\n        )));\n    }\n    Ok(())\n}\n\nfn exec_js(script: &str) -> Result<(), Error> {\n    check_js_stderr(script, \"\")\n}\n\n#[test]\nfn hello_compute_example() -> Result<(), Error> {\n    exec_js_file(\"examples/hello-compute.js\")\n}\n\n#[test]\nfn features() -> Result<(), Error> {\n    // Check that we don't expose native-only features.\n    exec_js(\n        r#\"\n        const adapter = await navigator.gpu.requestAdapter();\n\n        if (adapter.features.has(\"mappable-primary-buffers\")) {\n            throw new TypeError(\"Adapter should not report support for wgpu native-only features\");\n        }\n    \"#,\n    )?;\n\n    // Check for features tested by the CTS. Because these are optional\n    // features, the applicable CTS tests will pass (silently, without\n    // exercising the functionality) when support is not reported. This test\n    // serves to bridge the gap between the coverage provided by the CTS\n    // (\"feature must work if available\") and our desired coverage (\"feature\n    // must be implemented and work\"), in case we inadvertently stop reporting\n    // support for a feature. (There ought to also be relevant wgpu tests of the\n    // feature that would catch this, but better to be safe.)\n    exec_js(\n        r#\"\n        const adapter = await navigator.gpu.requestAdapter();\n\n        if (!adapter.features.has(\"primitive-index\")) {\n            throw new TypeError(\"Adapter should report support for primitive-index feature\");\n        }\n    \"#,\n    )?;\n\n    Ok(())\n}\n\n#[test]\nfn uncaptured_error() -> Result<(), Error> {\n    check_js_stderr(\n        r#\"\n            const code = `const val: u32 = 1.1;`;\n\n            const adapter = await navigator.gpu.requestAdapter();\n            const device = await adapter.requestDevice();\n            device.createShaderModule({ code })\n        \"#,\n        \"cts_runner caught WebGPU error:\\x20\nShader '' parsing error: the type of `val` is expected to be `u32`, but got `{AbstractFloat}`\n  ┌─ wgsl:1:7\n  │\n1 │ const val: u32 = 1.1;\n  │       ^^^ definition of `val`\\n\\n\\n\",\n    )\n}\n\n#[test]\nfn lst_files_are_sorted() {\n    let workspace_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .to_path_buf();\n    let files = [\"test.lst\", \"fail.lst\", \"skip.lst\"];\n\n    for file in &files {\n        let file_path = workspace_dir.join(\"cts_runner\").join(file);\n        let contents = fs::read_to_string(&file_path).unwrap();\n        let selectors = contents\n            .lines()\n            .enumerate()\n            .filter_map(|(idx, line)| {\n                // Extract selectors (including in comments, removing fails-if annotations)\n                let trimmed = line.trim();\n                trimmed\n                    .find(\"webgpu:\")\n                    .or_else(|| trimmed.find(\"unittests:\"))\n                    .map(|pos| (idx, &trimmed[pos..]))\n            })\n            .map(|(idx, line)| {\n                // Crude en_US sort. '_' < ',' < ':' < digits < letters\n                let sort_key = line\n                    .chars()\n                    .map(|c| {\n                        if c.is_ascii_uppercase() {\n                            c.to_ascii_lowercase()\n                        } else if c == '_' {\n                            ' '\n                        } else if c == ':' {\n                            '-'\n                        } else {\n                            c\n                        }\n                    })\n                    .collect::<String>();\n                (idx, line, sort_key)\n            })\n            .collect::<Vec<_>>();\n\n        let mut sorted = selectors.clone();\n        sorted.sort_by_key(|(_, _, sort_key)| sort_key.clone());\n\n        if selectors != sorted {\n            let (found, expected) = selectors\n                .iter()\n                .zip(sorted.iter())\n                .find(|(a, b)| a != b)\n                .unwrap();\n            panic!(\n                \"{} is not sorted. First mismatch on line {}:\\nFound: {}\\nShould be: {}\",\n                file,\n                found.0 + 1,\n                found.1,\n                expected.1,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "deno_webgpu/00_init.js",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nimport { core } from \"ext:core/mod.js\";\n\nconst loadWebGPU = core.createLazyLoader(\"ext:deno_webgpu/01_webgpu.js\");\n\nexport { loadWebGPU };\n"
  },
  {
    "path": "deno_webgpu/01_webgpu.js",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\n// @ts-check\n/// <reference path=\"../../core/lib.deno_core.d.ts\" />\n/// <reference path=\"../web/internal.d.ts\" />\n/// <reference path=\"../web/lib.deno_web.d.ts\" />\n/// <reference path=\"./lib.deno_webgpu.d.ts\" />\n\nimport { core, primordials } from \"ext:core/mod.js\";\nimport {\n  GPU,\n  GPUAdapter,\n  GPUAdapterInfo,\n  GPUBindGroup,\n  GPUBindGroupLayout,\n  GPUBuffer,\n  GPUCommandBuffer,\n  GPUCommandEncoder,\n  GPUCompilationInfo,\n  GPUCompilationMessage,\n  GPUComputePassEncoder,\n  GPUComputePipeline,\n  GPUDevice,\n  GPUDeviceLostInfo,\n  GPUPipelineLayout,\n  GPUQuerySet,\n  GPUQueue,\n  GPURenderBundle,\n  GPURenderBundleEncoder,\n  GPURenderPassEncoder,\n  GPURenderPipeline,\n  GPUSampler,\n  GPUShaderModule,\n  GPUSupportedFeatures,\n  GPUSupportedLimits,\n  GPUTexture,\n  GPUTextureView,\n  GPUExternalTexture,\n  WGSLLanguageFeatures,\n  op_create_gpu,\n  op_webgpu_device_start_capture,\n  op_webgpu_device_stop_capture,\n} from \"ext:core/ops\";\nconst {\n  ObjectDefineProperty,\n  ObjectPrototypeIsPrototypeOf,\n  ObjectSetPrototypeOf,\n  ReflectGet,\n  Symbol,\n  SymbolFor,\n} = primordials;\n\nimport * as webidl from \"ext:deno_webidl/00_webidl.js\";\nimport {\n  defineEventHandler,\n  Event,\n  EventTargetPrototype,\n  setEventTargetData,\n} from \"ext:deno_web/02_event.js\";\nimport { DOMException } from \"ext:deno_web/01_dom_exception.js\";\nimport { createFilteredInspectProxy } from \"ext:deno_console/01_console.js\";\n\nconst privateCustomInspect = SymbolFor(\"Deno.privateCustomInspect\");\nconst _message = Symbol(\"[[message]]\");\nconst illegalConstructorKey = Symbol(\"illegalConstructorKey\");\n\nclass GPUError {\n  constructor(key = null) {\n    if (key !== illegalConstructorKey) {\n      webidl.illegalConstructor();\n    }\n  }\n\n  [_message];\n  get message() {\n    webidl.assertBranded(this, GPUErrorPrototype);\n    return this[_message];\n  }\n\n  [privateCustomInspect](inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUErrorPrototype, this),\n        keys: [\n          \"message\",\n        ],\n      }),\n      inspectOptions,\n    );\n  }\n}\nconst GPUErrorPrototype = GPUError.prototype;\n\nclass GPUValidationError extends GPUError {\n  /** @param {string} message */\n  constructor(message) {\n    const prefix = \"Failed to construct 'GPUValidationError'\";\n    webidl.requiredArguments(arguments.length, 1, prefix);\n    message = webidl.converters.DOMString(message, prefix, \"Argument 1\");\n    super(illegalConstructorKey);\n    this[webidl.brand] = webidl.brand;\n    this[_message] = message;\n  }\n}\ncore.registerErrorClass(\"GPUValidationError\", GPUValidationError);\n\nclass GPUOutOfMemoryError extends GPUError {\n  constructor(message) {\n    const prefix = \"Failed to construct 'GPUOutOfMemoryError'\";\n    webidl.requiredArguments(arguments.length, 1, prefix);\n    message = webidl.converters.DOMString(message, prefix, \"Argument 1\");\n    super(illegalConstructorKey);\n    this[webidl.brand] = webidl.brand;\n    this[_message] = message;\n  }\n}\ncore.registerErrorClass(\"GPUOutOfMemoryError\", GPUOutOfMemoryError);\n\nclass GPUInternalError extends GPUError {\n  constructor() {\n    super(illegalConstructorKey);\n    this[webidl.brand] = webidl.brand;\n  }\n}\ncore.registerErrorClass(\"GPUInternalError\", GPUInternalError);\n\nclass GPUPipelineError extends DOMException {\n  #reason;\n\n  constructor(message = \"\", options = { __proto__: null }) {\n    const prefix = \"Failed to construct 'GPUPipelineError'\";\n    message = webidl.converters.DOMString(message, prefix, \"Argument 1\");\n    options = webidl.converters.GPUPipelineErrorInit(\n      options,\n      prefix,\n      \"Argument 2\",\n    );\n    super(message, \"GPUPipelineError\");\n\n    this.#reason = options.reason;\n  }\n\n  get reason() {\n    webidl.assertBranded(this, GPUPipelineErrorPrototype);\n    return this.#reason;\n  }\n}\nconst GPUPipelineErrorPrototype = GPUPipelineError.prototype;\n\nclass GPUUncapturedErrorEvent extends Event {\n  #error;\n\n  constructor(type, gpuUncapturedErrorEventInitDict) {\n    super(type, gpuUncapturedErrorEventInitDict);\n    this[webidl.brand] = webidl.brand;\n\n    const prefix = \"Failed to construct 'GPUUncapturedErrorEvent'\";\n    webidl.requiredArguments(arguments.length, 2, prefix);\n    gpuUncapturedErrorEventInitDict = webidl.converters\n      .GPUUncapturedErrorEventInit(\n        gpuUncapturedErrorEventInitDict,\n        prefix,\n        \"Argument 2\",\n      );\n\n    this.#error = gpuUncapturedErrorEventInitDict.error;\n  }\n\n  get error() {\n    webidl.assertBranded(this, GPUUncapturedErrorEventPrototype);\n    return this.#error;\n  }\n}\nconst GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype;\n\nconst GPUPrototype = GPU.prototype;\nObjectDefineProperty(GPUPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return `${this.constructor.name} ${inspect({}, inspectOptions)}`;\n  },\n});\n\nconst GPUAdapterPrototype = GPUAdapter.prototype;\nObjectDefineProperty(GPUAdapterPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUAdapterPrototype, this),\n        keys: [\n          \"features\",\n          \"limits\",\n          \"info\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUAdapterInfoPrototype = GPUAdapterInfo.prototype;\nObjectDefineProperty(GPUAdapterInfoPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUAdapterInfoPrototype, this),\n        keys: [\n          \"vendor\",\n          \"architecture\",\n          \"device\",\n          \"description\",\n          \"subgroupMinSize\",\n          \"subgroupMaxSize\",\n          \"isFallbackAdapter\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype;\nwebidl.setlikeObjectWrap(GPUSupportedFeaturesPrototype, true);\nObjectDefineProperty(GPUSupportedFeaturesPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) {\n      return `${this.constructor.name} ${\n        // deno-lint-ignore prefer-primordials\n        inspect([...this], inspectOptions)}`;\n    } else {\n      return `${this.constructor.name} ${inspect({}, inspectOptions)}`;\n    }\n  },\n});\n\nconst WGSLLanguageFeaturesPrototype = WGSLLanguageFeatures.prototype;\nwebidl.setlikeObjectWrap(WGSLLanguageFeaturesPrototype, true);\nObjectDefineProperty(WGSLLanguageFeaturesPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    if (ObjectPrototypeIsPrototypeOf(WGSLLanguageFeaturesPrototype, this)) {\n      return `${this.constructor.name} ${\n        // deno-lint-ignore prefer-primordials\n        inspect([...this], inspectOptions)}`;\n    } else {\n      return `${this.constructor.name} ${inspect({}, inspectOptions)}`;\n    }\n  },\n});\n\nconst GPUSupportedLimitsPrototype = GPUSupportedLimits.prototype;\nObjectDefineProperty(GPUSupportedLimitsPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUSupportedLimitsPrototype,\n          this,\n        ),\n        keys: [\n          \"maxTextureDimension1D\",\n          \"maxTextureDimension2D\",\n          \"maxTextureDimension3D\",\n          \"maxTextureArrayLayers\",\n          \"maxBindGroups\",\n          // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers\n          // \"maxBindGroupsPlusVertexBuffers\",\n          \"maxBindingsPerBindGroup\",\n          \"maxDynamicUniformBuffersPerPipelineLayout\",\n          \"maxDynamicStorageBuffersPerPipelineLayout\",\n          \"maxSampledTexturesPerShaderStage\",\n          \"maxSamplersPerShaderStage\",\n          \"maxStorageBuffersPerShaderStage\",\n          \"maxStorageTexturesPerShaderStage\",\n          \"maxUniformBuffersPerShaderStage\",\n          \"maxUniformBufferBindingSize\",\n          \"maxStorageBufferBindingSize\",\n          \"minUniformBufferOffsetAlignment\",\n          \"minStorageBufferOffsetAlignment\",\n          \"maxVertexBuffers\",\n          \"maxBufferSize\",\n          \"maxVertexAttributes\",\n          \"maxVertexBufferArrayStride\",\n          \"maxInterStageShaderVariables\",\n          \"maxColorAttachments\",\n          \"maxColorAttachmentBytesPerSample\",\n          \"maxComputeWorkgroupStorageSize\",\n          \"maxComputeInvocationsPerWorkgroup\",\n          \"maxComputeWorkgroupSizeX\",\n          \"maxComputeWorkgroupSizeY\",\n          \"maxComputeWorkgroupSizeZ\",\n          \"maxComputeWorkgroupsPerDimension\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype;\nObjectDefineProperty(GPUDeviceLostInfoPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUDeviceLostInfoPrototype,\n          this,\n        ),\n        keys: [\n          \"reason\",\n          \"message\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUDevicePrototype = GPUDevice.prototype;\nObjectSetPrototypeOf(GPUDevicePrototype, EventTargetPrototype);\ndefineEventHandler(GPUDevicePrototype, \"uncapturederror\");\nObjectDefineProperty(GPUDevicePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUDevicePrototype, this),\n        keys: [\n          \"features\",\n          \"label\",\n          \"limits\",\n          \"lost\",\n          \"queue\",\n          // TODO(lucacasonato): emit an UncapturedErrorEvent\n          // \"onuncapturederror\"\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUQueuePrototype = GPUQueue.prototype;\nObjectDefineProperty(GPUQueuePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUQueuePrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUBufferPrototype = GPUBuffer.prototype;\nObjectDefineProperty(GPUBufferPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUBufferPrototype, this),\n        keys: [\n          \"label\",\n          \"mapState\",\n          \"size\",\n          \"usage\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nclass GPUBufferUsage {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n\n  static get MAP_READ() {\n    return 0x0001;\n  }\n  static get MAP_WRITE() {\n    return 0x0002;\n  }\n  static get COPY_SRC() {\n    return 0x0004;\n  }\n  static get COPY_DST() {\n    return 0x0008;\n  }\n  static get INDEX() {\n    return 0x0010;\n  }\n  static get VERTEX() {\n    return 0x0020;\n  }\n  static get UNIFORM() {\n    return 0x0040;\n  }\n  static get STORAGE() {\n    return 0x0080;\n  }\n  static get INDIRECT() {\n    return 0x0100;\n  }\n  static get QUERY_RESOLVE() {\n    return 0x0200;\n  }\n}\n\nclass GPUMapMode {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n\n  static get READ() {\n    return 0x0001;\n  }\n  static get WRITE() {\n    return 0x0002;\n  }\n}\n\nconst GPUTexturePrototype = GPUTexture.prototype;\nObjectDefineProperty(GPUTexturePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUTexturePrototype, this),\n        keys: [\n          \"label\",\n          \"width\",\n          \"height\",\n          \"depthOrArrayLayers\",\n          \"mipLevelCount\",\n          \"sampleCount\",\n          \"dimension\",\n          \"format\",\n          \"usage\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nclass GPUTextureUsage {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n\n  static get COPY_SRC() {\n    return 0x01;\n  }\n  static get COPY_DST() {\n    return 0x02;\n  }\n  static get TEXTURE_BINDING() {\n    return 0x04;\n  }\n  static get STORAGE_BINDING() {\n    return 0x08;\n  }\n  static get RENDER_ATTACHMENT() {\n    return 0x10;\n  }\n}\n\nconst GPUTextureViewPrototype = GPUTextureView.prototype;\nObjectDefineProperty(GPUTextureViewPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUTextureViewPrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUSamplerPrototype = GPUSampler.prototype;\nObjectDefineProperty(GPUSamplerPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUSamplerPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUBindGroupLayoutPrototype = GPUBindGroupLayout.prototype;\nObjectDefineProperty(GPUBindGroupLayout, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUBindGroupLayoutPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype;\nObjectDefineProperty(GPUPipelineLayoutPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUPipelineLayoutPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUBindGroupPrototype = GPUBindGroup.prototype;\nObjectDefineProperty(GPUBindGroupPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUBindGroupPrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUShaderModulePrototype = GPUShaderModule.prototype;\nObjectDefineProperty(GPUShaderModulePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUShaderModulePrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nObjectDefineProperty(GPUCompilationInfo, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUCompilationInfoPrototype,\n          this,\n        ),\n        keys: [\n          \"messages\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\nconst GPUCompilationInfoPrototype = GPUCompilationInfo.prototype;\n\nObjectDefineProperty(GPUCompilationMessage, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUCompilationMessagePrototype,\n          this,\n        ),\n        keys: [\n          \"message\",\n          \"type\",\n          \"line_num\",\n          \"line_pos\",\n          \"offset\",\n          \"length\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\nconst GPUCompilationMessagePrototype = GPUCompilationMessage.prototype;\n\nclass GPUShaderStage {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n\n  static get VERTEX() {\n    return 0x1;\n  }\n\n  static get FRAGMENT() {\n    return 0x2;\n  }\n\n  static get COMPUTE() {\n    return 0x4;\n  }\n}\n\nconst GPUComputePipelinePrototype = GPUComputePipeline.prototype;\nObjectDefineProperty(GPUComputePipelinePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUComputePipelinePrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPURenderPipelinePrototype = GPURenderPipeline.prototype;\nObjectDefineProperty(GPURenderPipelinePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPURenderPipelinePrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nclass GPUColorWrite {\n  constructor() {\n    webidl.illegalConstructor();\n  }\n\n  static get RED() {\n    return 0x1;\n  }\n  static get GREEN() {\n    return 0x2;\n  }\n  static get BLUE() {\n    return 0x4;\n  }\n  static get ALPHA() {\n    return 0x8;\n  }\n  static get ALL() {\n    return 0xF;\n  }\n}\n\nconst GPUCommandEncoderPrototype = GPUCommandEncoder.prototype;\nObjectDefineProperty(GPUCommandEncoderPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUCommandEncoderPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPURenderPassEncoderPrototype = GPURenderPassEncoder.prototype;\nObjectDefineProperty(GPURenderPassEncoderPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPURenderPassEncoderPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUComputePassEncoderPrototype = GPUComputePassEncoder.prototype;\nObjectDefineProperty(GPUComputePassEncoderPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPUComputePassEncoderPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUCommandBufferPrototype = GPUCommandBuffer.prototype;\nObjectDefineProperty(GPUCommandBufferPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUCommandBufferPrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype;\nObjectDefineProperty(GPURenderBundleEncoderPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(\n          GPURenderBundleEncoderPrototype,\n          this,\n        ),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPURenderBundlePrototype = GPURenderBundle.prototype;\nObjectDefineProperty(GPURenderBundlePrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPURenderBundlePrototype, this),\n        keys: [\n          \"label\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\nconst GPUQuerySetPrototype = GPUQuerySet.prototype;\nObjectDefineProperty(GPUQuerySetPrototype, privateCustomInspect, {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUQuerySetPrototype, this),\n        keys: [\n          \"label\",\n          \"type\",\n          \"count\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\n\n// Converters\n\nwebidl.converters[\"GPUPipelineErrorReason\"] = webidl.createEnumConverter(\n  \"GPUPipelineErrorReason\",\n  [\n    \"validation\",\n    \"internal\",\n  ],\n);\n\nwebidl.converters[\"GPUPipelineErrorInit\"] = webidl.createDictionaryConverter(\n  \"GPUPipelineErrorInit\",\n  [\n    {\n      key: \"reason\",\n      converter: webidl.converters.GPUPipelineErrorReason,\n      required: true,\n    },\n  ],\n);\n\nwebidl.converters[\"GPUError\"] = webidl.converters.any /* put union here! */;\n\nconst dictMembersGPUUncapturedErrorEventInit = [\n  { key: \"error\", converter: webidl.converters[\"GPUError\"], required: true },\n];\nwebidl.converters[\"GPUUncapturedErrorEventInit\"] = webidl\n  .createDictionaryConverter(\n    \"GPUUncapturedErrorEventInit\",\n    // dictMembersEventInit,\n    dictMembersGPUUncapturedErrorEventInit,\n  );\n\nfunction deviceStartCapture(device) {\n  op_webgpu_device_start_capture(device);\n}\n\nfunction deviceStopCapture(device) {\n  op_webgpu_device_stop_capture(device);\n}\n\nconst denoNsWebGPU = {\n  deviceStartCapture,\n  deviceStopCapture,\n};\n\nlet gpu;\nfunction initGPU() {\n  if (!gpu) {\n    gpu = op_create_gpu(\n      webidl.brand,\n      setEventTargetData,\n      GPUUncapturedErrorEvent,\n      GPUPipelineError,\n    );\n  }\n}\n\nexport {\n  denoNsWebGPU,\n  GPU,\n  gpu,\n  GPUAdapter,\n  GPUAdapterInfo,\n  GPUBindGroup,\n  GPUBindGroupLayout,\n  GPUBuffer,\n  GPUBufferUsage,\n  GPUColorWrite,\n  GPUCommandBuffer,\n  GPUCommandEncoder,\n  GPUCompilationInfo,\n  GPUCompilationMessage,\n  GPUComputePassEncoder,\n  GPUComputePipeline,\n  GPUDevice,\n  GPUDeviceLostInfo,\n  GPUError,\n  GPUInternalError,\n  GPUMapMode,\n  GPUOutOfMemoryError,\n  GPUPipelineError,\n  GPUPipelineLayout,\n  GPUQuerySet,\n  GPUQueue,\n  GPURenderBundle,\n  GPURenderBundleEncoder,\n  GPURenderPassEncoder,\n  GPURenderPipeline,\n  GPUSampler,\n  GPUShaderModule,\n  GPUShaderStage,\n  GPUSupportedFeatures,\n  GPUSupportedLimits,\n  GPUTexture,\n  GPUTextureUsage,\n  GPUTextureView,\n  GPUExternalTexture,\n  GPUUncapturedErrorEvent,\n  GPUValidationError,\n  WGSLLanguageFeatures,\n  initGPU,\n};\n"
  },
  {
    "path": "deno_webgpu/02_surface.js",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\n// @ts-check\n/// <reference path=\"../../core/lib.deno_core.d.ts\" />\n/// <reference path=\"../web/internal.d.ts\" />\n/// <reference path=\"../web/lib.deno_web.d.ts\" />\n/// <reference path=\"./lib.deno_webgpu.d.ts\" />\n\nimport { primordials } from \"ext:core/mod.js\";\nimport { GPUCanvasContext, UnsafeWindowSurface } from \"ext:core/ops\";\nconst {\n  ObjectDefineProperty,\n  ObjectPrototypeIsPrototypeOf,\n  SymbolFor,\n} = primordials;\nimport { createFilteredInspectProxy } from \"ext:deno_console/01_console.js\";\n\nObjectDefineProperty(GPUCanvasContext, SymbolFor(\"Deno.privateCustomInspect\"), {\n  __proto__: null,\n  value(inspect, inspectOptions) {\n    return inspect(\n      createFilteredInspectProxy({\n        object: this,\n        evaluate: ObjectPrototypeIsPrototypeOf(GPUCanvasContextPrototype, this),\n        keys: [\n          \"canvas\",\n        ],\n      }),\n      inspectOptions,\n    );\n  },\n});\nconst GPUCanvasContextPrototype = GPUCanvasContext.prototype;\n\nexport { GPUCanvasContext, UnsafeWindowSurface };\n"
  },
  {
    "path": "deno_webgpu/Cargo.toml",
    "content": "# Copyright 2018-2025 the Deno authors. MIT license.\n\n[package]\nname = \"deno_webgpu\"\nversion = \"0.181.0\"\nauthors = [\"the Deno authors\"]\nedition.workspace = true\nlicense = \"MIT\"\nreadme = \"README.md\"\nrepository = \"https://github.com/gfx-rs/wgpu\"\ndescription = \"WebGPU implementation for Deno\"\n\n[lib]\npath = \"lib.rs\"\n\n# We make all dependencies conditional on not being wasm,\n# so the whole workspace can built as wasm.\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nwgpu-core = { workspace = true, features = [\n    \"trace\",\n    \"replay\",\n    \"serde\",\n    \"strict_asserts\",\n    \"wgsl\",\n    \"gles\",\n] }\nwgpu-types = { workspace = true, features = [\"serde\", \"std\"] }\n\ndeno_core.workspace = true\ndeno_error.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\ntokio = { workspace = true, features = [\"full\"] }\nraw-window-handle.workspace = true\nthiserror.workspace = true\nindexmap.workspace = true\nserde_json.workspace = true\ndeno_unsync.workspace = true\n\n# Apple Platforms\n#\n# We want the Metal backend.\n[target.'cfg(target_vendor = \"apple\")'.dependencies]\nwgpu-core = { workspace = true, features = [\"metal\"] }\n\n# Windows\n#\n# We want the DX12 backend.\n[target.'cfg(windows)'.dependencies]\nwgpu-core = { workspace = true, features = [\"dx12\"] }\n\n# Windows and Unix (not Emscripten)\n#\n# We want the Vulkan backend.\n[target.'cfg(any(windows, all(unix, not(target_os = \"emscripten\"))))'.dependencies]\nwgpu-core = { workspace = true, features = [\"vulkan\"] }\n"
  },
  {
    "path": "deno_webgpu/LICENSE.md",
    "content": "MIT License\n\nCopyright 2018-2024 the Deno authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "deno_webgpu/README.md",
    "content": "# deno_webgpu\n\nThis op crate implements the WebGPU API as defined in\nhttps://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec\ndraft as of March 31, 2024. The spec is still very much in flux. This extension\ntries to stay up to date with the spec, but is constrained by the features\nimplemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu).\n\nThe spec is still very bare bones, and is still missing many details. As the\nspec becomes more concrete, we will implement to follow the spec more closely.\n\nIn addition, setting the `DENO_WEBGPU_TRACE` environmental variable will output\na\n[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)\nto the specified directory.\n\nThis op crate is tested primarily by running the\n[WebGPU conformance test suite](https://github.com/gpuweb/cts) using `wgpu`'s\n[`cts_runner`](https://github.com/gfx-rs/wgpu/blob/trunk/README.md#webgpu-conformance-test-suite).\n`cts_runner` also has a few\n[directed tests](https://github.com/gfx-rs/wgpu/tree/trunk/cts_runner/tests)\nto fill in missing coverage.\n\nGPU availability in GitHub CI is limited, so some configurations rely on\nsoftware like DX WARP & Vulkan lavapipe.\n\n## Links\n\nSpecification: https://gpuweb.github.io/gpuweb/\n\nDesign documents: https://github.com/gpuweb/gpuweb/tree/main/design\n\nConformance tests suite: https://github.com/gpuweb/cts\n\nWebGPU examples for Deno: https://github.com/crowlKats/webgpu-examples\n\nwgpu-users matrix channel: https://matrix.to/#/#wgpu-users:matrix.org\n"
  },
  {
    "path": "deno_webgpu/adapter.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::ops::BitOr;\nuse std::rc::Rc;\n\nuse deno_core::cppgc::SameObject;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::GarbageCollected;\nuse deno_core::OpState;\nuse deno_core::V8TaskSpawner;\nuse deno_core::WebIDL;\n\nuse super::device::GPUDevice;\nuse super::device::DEVICE_EXTERNAL_MEMORY_SIZE;\nuse super::queue::GPUQueue;\nuse crate::error::GPUGenericError;\nuse crate::webidl::GPUFeatureName;\nuse crate::Instance;\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURequestAdapterOptions {\n  #[webidl(default = \"core\".into())]\n  pub feature_level: String,\n  pub power_preference: Option<GPUPowerPreference>,\n  #[webidl(default = false)]\n  pub force_fallback_adapter: bool,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUPowerPreference {\n  LowPower,\n  HighPerformance,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\nstruct GPUDeviceDescriptor {\n  #[webidl(default = String::new())]\n  label: String,\n\n  #[webidl(default = vec![])]\n  required_features: Vec<GPUFeatureName>,\n  #[webidl(default = Default::default())]\n  #[options(enforce_range = true)]\n  required_limits: indexmap::IndexMap<String, Option<u64>>,\n}\n\npub struct GPUAdapter {\n  pub instance: Instance,\n  pub id: wgpu_core::id::AdapterId,\n\n  pub features: SameObject<GPUSupportedFeatures>,\n  pub limits: SameObject<GPUSupportedLimits>,\n  pub info: Rc<SameObject<GPUAdapterInfo>>,\n}\n\nimpl Drop for GPUAdapter {\n  fn drop(&mut self) {\n    self.instance.adapter_drop(self.id);\n  }\n}\n\nimpl GarbageCollected for GPUAdapter {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUAdapter\"\n  }\n}\n\n#[op2]\nimpl GPUAdapter {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUAdapter, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[global]\n  fn info(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {\n    self.info.get(scope, |_| {\n      let info = self.instance.adapter_get_info(self.id);\n\n      GPUAdapterInfo { info }\n    })\n  }\n\n  #[getter]\n  #[global]\n  fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {\n    self.features.get(scope, |scope| {\n      let features = self.instance.adapter_features(self.id);\n      // Only expose WebGPU features, not wgpu native-only features\n      let features = features & wgpu_types::Features::all_webgpu_mask();\n      GPUSupportedFeatures::new(scope, features)\n    })\n  }\n\n  #[getter]\n  #[global]\n  fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {\n    self.limits.get(scope, |_| {\n      let adapter_limits = self.instance.adapter_limits(self.id);\n      GPUSupportedLimits(adapter_limits)\n    })\n  }\n\n  #[async_method(fake)]\n  #[global]\n  fn request_device(\n    &self,\n    state: &mut OpState,\n    scope: &mut v8::HandleScope,\n    #[webidl] descriptor: GPUDeviceDescriptor,\n  ) -> Result<v8::Global<v8::Value>, CreateDeviceError> {\n    let supported_features = self.instance.adapter_features(self.id);\n    let required_features = descriptor\n      .required_features\n      .iter()\n      .map(|f| wgpu_types::Features::from(*f))\n      .fold(wgpu_types::Features::empty(), BitOr::bitor);\n\n    // External textures are a required part of WebGPU, and `external-texture`\n    // is not a WebGPU-defined feature. `wgpu` has it behind a feature for now,\n    // because support is not complete. Allow applications to request that\n    // feature even though it is not reported as an adapter-supported feature.\n    //\n    // There is probably not anything useful that Deno applications can do with\n    // external textures, but it is useful to be able to enable it in\n    // `cts_runner`.\n    if !required_features\n      .difference(supported_features | wgpu_types::Features::EXTERNAL_TEXTURE)\n      .is_empty()\n    {\n      return Err(CreateDeviceError::RequiredFeaturesNotASubset);\n    }\n\n    // When support for compatibility mode is added, this will need to look\n    // at whether the adapter is \"compatibility-defaulting\" or\n    // \"core-defaulting\", and choose the appropriate set of defaults.\n    //\n    // Support for compatibility mode is tracked in\n    // https://github.com/gfx-rs/wgpu/issues/8124.\n    let required_limits = serde_json::from_value::<wgpu_types::Limits>(\n      serde_json::to_value(descriptor.required_limits)?,\n    )?\n    .or_better_values_from(&wgpu_types::Limits::default());\n\n    let trace = std::env::var_os(\"DENO_WEBGPU_TRACE\")\n      .map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path)))\n      .unwrap_or_default();\n\n    let wgpu_descriptor = wgpu_types::DeviceDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      required_features,\n      required_limits,\n      experimental_features: wgpu_types::ExperimentalFeatures::disabled(),\n      memory_hints: Default::default(),\n      trace,\n    };\n\n    let (device, queue) = self.instance.adapter_request_device(\n      self.id,\n      &wgpu_descriptor,\n      None,\n      None,\n    )?;\n\n    // Associate external memory with the device to encourage V8 to garbage\n    // collect devices promptly.\n    scope\n      .adjust_amount_of_external_allocated_memory(DEVICE_EXTERNAL_MEMORY_SIZE);\n\n    let spawner = state.borrow::<V8TaskSpawner>().clone();\n    let lost_resolver = v8::PromiseResolver::new(scope).unwrap();\n    let lost_promise = lost_resolver.get_promise(scope);\n    let error_handler = Rc::new(super::error::DeviceErrorHandler::new(\n      v8::Global::new(scope, lost_resolver),\n      spawner,\n    ));\n\n    // Create the queue object eagerly so that the wgpu-core queue resource\n    // gets cleaned up when the device is garbage collected, even if JS code\n    // never accesses the queue property.\n    let queue_obj = deno_core::cppgc::make_cppgc_object(\n      scope,\n      GPUQueue {\n        instance: self.instance.clone(),\n        error_handler: error_handler.clone(),\n        label: descriptor.label.clone(),\n        id: queue,\n        device,\n      },\n    );\n    let queue_obj = v8::Global::new(scope, queue_obj);\n\n    let device = GPUDevice {\n      instance: self.instance.clone(),\n      id: device,\n      label: descriptor.label,\n      queue_obj,\n      adapter_info: self.info.clone(),\n      error_handler,\n      adapter: self.id,\n      lost_promise: v8::Global::new(scope, lost_promise),\n      limits: SameObject::new(),\n      features: SameObject::new(),\n      weak: std::sync::OnceLock::new(),\n    };\n    let device = deno_core::cppgc::make_cppgc_object(scope, device);\n    let weak_device = v8::Weak::new(scope, device);\n    let event_target_setup = state.borrow::<crate::EventTargetSetup>();\n    let webidl_brand = v8::Local::new(scope, event_target_setup.brand.clone());\n    device.set(scope, webidl_brand, webidl_brand);\n    let set_event_target_data =\n      v8::Local::new(scope, event_target_setup.set_event_target_data.clone())\n        .cast::<v8::Function>();\n    let null = v8::null(scope);\n    set_event_target_data.call(scope, null.into(), &[device.into()]);\n\n    let finalizer = v8::Weak::with_finalizer(\n      scope,\n      device,\n      Box::new(move |isolate| {\n        isolate.adjust_amount_of_external_allocated_memory(\n          -DEVICE_EXTERNAL_MEMORY_SIZE,\n        );\n      }),\n    );\n\n    // Now that the device is fully constructed, give the error handler a\n    // weak reference to it, and store the finalizer weak reference.\n    let device = device.cast::<v8::Value>();\n    let device_ref =\n      deno_core::cppgc::try_unwrap_cppgc_object::<GPUDevice>(scope, device)\n        .unwrap();\n    device_ref.error_handler.set_device(weak_device);\n    device_ref.weak.set(finalizer).unwrap();\n\n    Ok(v8::Global::new(scope, device))\n  }\n}\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\npub enum CreateDeviceError {\n  #[class(type)]\n  #[error(\"requiredFeatures must be a subset of the adapter features\")]\n  RequiredFeaturesNotASubset,\n  #[class(inherit)]\n  #[error(transparent)]\n  Serde(#[from] serde_json::Error),\n  #[class(\"DOMExceptionOperationError\")]\n  #[error(transparent)]\n  Device(#[from] wgpu_core::instance::RequestDeviceError),\n}\n\npub struct GPUSupportedLimits(pub wgpu_types::Limits);\n\nimpl GarbageCollected for GPUSupportedLimits {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUSupportedLimits\"\n  }\n}\n\n#[op2]\nimpl GPUSupportedLimits {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUSupportedLimits, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  fn maxTextureDimension1D(&self) -> u32 {\n    self.0.max_texture_dimension_1d\n  }\n\n  #[getter]\n  fn maxTextureDimension2D(&self) -> u32 {\n    self.0.max_texture_dimension_2d\n  }\n\n  #[getter]\n  fn maxTextureDimension3D(&self) -> u32 {\n    self.0.max_texture_dimension_3d\n  }\n\n  #[getter]\n  fn maxTextureArrayLayers(&self) -> u32 {\n    self.0.max_texture_array_layers\n  }\n\n  #[getter]\n  fn maxBindGroups(&self) -> u32 {\n    self.0.max_bind_groups\n  }\n\n  // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers\n\n  #[getter]\n  fn maxBindingsPerBindGroup(&self) -> u32 {\n    self.0.max_bindings_per_bind_group\n  }\n\n  #[getter]\n  fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 {\n    self.0.max_dynamic_uniform_buffers_per_pipeline_layout\n  }\n\n  #[getter]\n  fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 {\n    self.0.max_dynamic_storage_buffers_per_pipeline_layout\n  }\n\n  #[getter]\n  fn maxSampledTexturesPerShaderStage(&self) -> u32 {\n    self.0.max_sampled_textures_per_shader_stage\n  }\n\n  #[getter]\n  fn maxSamplersPerShaderStage(&self) -> u32 {\n    self.0.max_samplers_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageBuffersPerShaderStage(&self) -> u32 {\n    self.0.max_storage_buffers_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageBuffersInVertexStage(&self) -> u32 {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8748): InVertexStage limit\n    // not implemented; return the PerShaderStage limit.\n    self.0.max_storage_buffers_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageBuffersInFragmentStage(&self) -> u32 {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8748): InFragmentStage limit\n    // not implemented; return the PerShaderStage limit.\n    self.0.max_storage_buffers_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageTexturesPerShaderStage(&self) -> u32 {\n    self.0.max_storage_textures_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageTexturesInVertexStage(&self) -> u32 {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8748): InVertexStage limit\n    // not implemented; return the PerShaderStage limit.\n    self.0.max_storage_textures_per_shader_stage\n  }\n\n  #[getter]\n  fn maxStorageTexturesInFragmentStage(&self) -> u32 {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8748): InFragmentStage limit\n    // not implemented; return the PerShaderStage limit.\n    self.0.max_storage_textures_per_shader_stage\n  }\n\n  #[getter]\n  fn maxUniformBuffersPerShaderStage(&self) -> u32 {\n    self.0.max_uniform_buffers_per_shader_stage\n  }\n\n  #[getter]\n  #[number]\n  fn maxUniformBufferBindingSize(&self) -> u64 {\n    self.0.max_uniform_buffer_binding_size\n  }\n\n  #[getter]\n  #[number]\n  fn maxStorageBufferBindingSize(&self) -> u64 {\n    self.0.max_storage_buffer_binding_size\n  }\n\n  #[getter]\n  fn minUniformBufferOffsetAlignment(&self) -> u32 {\n    self.0.min_uniform_buffer_offset_alignment\n  }\n\n  #[getter]\n  fn minStorageBufferOffsetAlignment(&self) -> u32 {\n    self.0.min_storage_buffer_offset_alignment\n  }\n\n  #[getter]\n  fn maxVertexBuffers(&self) -> u32 {\n    self.0.max_vertex_buffers\n  }\n\n  #[getter]\n  #[number]\n  fn maxBufferSize(&self) -> u64 {\n    self.0.max_buffer_size\n  }\n\n  #[getter]\n  fn maxVertexAttributes(&self) -> u32 {\n    self.0.max_vertex_attributes\n  }\n\n  #[getter]\n  fn maxVertexBufferArrayStride(&self) -> u32 {\n    self.0.max_vertex_buffer_array_stride\n  }\n\n  #[getter]\n  fn maxInterStageShaderVariables(&self) -> u32 {\n    self.0.max_inter_stage_shader_variables\n  }\n\n  #[getter]\n  fn maxColorAttachments(&self) -> u32 {\n    self.0.max_color_attachments\n  }\n\n  #[getter]\n  fn maxColorAttachmentBytesPerSample(&self) -> u32 {\n    self.0.max_color_attachment_bytes_per_sample\n  }\n\n  #[getter]\n  fn maxComputeWorkgroupStorageSize(&self) -> u32 {\n    self.0.max_compute_workgroup_storage_size\n  }\n\n  #[getter]\n  fn maxComputeInvocationsPerWorkgroup(&self) -> u32 {\n    self.0.max_compute_invocations_per_workgroup\n  }\n\n  #[getter]\n  fn maxComputeWorkgroupSizeX(&self) -> u32 {\n    self.0.max_compute_workgroup_size_x\n  }\n\n  #[getter]\n  fn maxComputeWorkgroupSizeY(&self) -> u32 {\n    self.0.max_compute_workgroup_size_y\n  }\n\n  #[getter]\n  fn maxComputeWorkgroupSizeZ(&self) -> u32 {\n    self.0.max_compute_workgroup_size_z\n  }\n\n  #[getter]\n  fn maxComputeWorkgroupsPerDimension(&self) -> u32 {\n    self.0.max_compute_workgroups_per_dimension\n  }\n}\n\npub struct GPUSupportedFeatures(v8::Global<v8::Value>);\n\nimpl GarbageCollected for GPUSupportedFeatures {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUSupportedFeatures\"\n  }\n}\n\nimpl GPUSupportedFeatures {\n  pub fn new(\n    scope: &mut v8::HandleScope,\n    features: wgpu_types::Features,\n  ) -> Self {\n    let set = v8::Set::new(scope);\n\n    for feature in features.iter() {\n      let key = v8::String::new(scope, feature.as_str().unwrap()).unwrap();\n      set.add(scope, key.into());\n    }\n\n    Self(v8::Global::new(scope, <v8::Local<v8::Value>>::from(set)))\n  }\n}\n\n#[op2]\nimpl GPUSupportedFeatures {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUSupportedFeatures, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[global]\n  #[symbol(\"setlike_set\")]\n  fn set(&self) -> v8::Global<v8::Value> {\n    self.0.clone()\n  }\n}\n\npub struct GPUAdapterInfo {\n  pub info: wgpu_types::AdapterInfo,\n}\n\nimpl GarbageCollected for GPUAdapterInfo {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUAdapterInfo\"\n  }\n}\n\n#[op2]\nimpl GPUAdapterInfo {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUAdapterInfo, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn vendor(&self) -> String {\n    self.info.vendor.to_string()\n  }\n\n  #[getter]\n  #[string]\n  fn architecture(&self) -> &'static str {\n    \"\" // TODO(https://github.com/gfx-rs/wgpu/issues/8649): implement when wgpu has architecture detection\n  }\n\n  #[getter]\n  #[string]\n  fn device(&self) -> String {\n    self.info.device.to_string()\n  }\n\n  #[getter]\n  #[string]\n  fn description(&self) -> String {\n    self.info.name.clone()\n  }\n\n  #[getter]\n  fn subgroup_min_size(&self) -> u32 {\n    self.info.subgroup_min_size\n  }\n\n  #[getter]\n  fn subgroup_max_size(&self) -> u32 {\n    self.info.subgroup_max_size\n  }\n\n  #[getter]\n  fn is_fallback_adapter(&self) -> bool {\n    self.info.device_type == wgpu_types::DeviceType::Cpu\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/bind_group.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8::HandleScope;\nuse deno_core::v8::Local;\nuse deno_core::v8::Value;\nuse deno_core::webidl::ContextFn;\nuse deno_core::webidl::WebIdlConverter;\nuse deno_core::webidl::WebIdlError;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::buffer::GPUBuffer;\nuse crate::error::GPUGenericError;\nuse crate::sampler::GPUSampler;\nuse crate::texture::GPUExternalTexture;\nuse crate::texture::GPUTexture;\nuse crate::texture::GPUTextureView;\nuse crate::Instance;\n\npub struct GPUBindGroup {\n  pub instance: Instance,\n  pub id: wgpu_core::id::BindGroupId,\n  pub label: String,\n}\n\nimpl Drop for GPUBindGroup {\n  fn drop(&mut self) {\n    self.instance.bind_group_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUBindGroup {\n  const NAME: &'static str = \"GPUBindGroup\";\n}\n\nimpl GarbageCollected for GPUBindGroup {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUBindGroup\"\n  }\n}\n\n#[op2]\nimpl GPUBindGroup {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUBindGroup, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBindGroupDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub layout: Ptr<super::bind_group_layout::GPUBindGroupLayout>,\n  pub entries: Vec<GPUBindGroupEntry>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBindGroupEntry {\n  #[options(enforce_range = true)]\n  pub binding: u32,\n  pub resource: GPUBindingResource,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBufferBinding {\n  pub buffer: Ptr<GPUBuffer>,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  pub offset: u64,\n  #[options(enforce_range = true)]\n  pub size: Option<u64>,\n}\n\npub(crate) enum GPUBindingResource {\n  Sampler(Ptr<GPUSampler>),\n  Texture(Ptr<GPUTexture>),\n  TextureView(Ptr<GPUTextureView>),\n  Buffer(Ptr<GPUBuffer>),\n  BufferBinding(GPUBufferBinding),\n  ExternalTexture(Ptr<GPUExternalTexture>),\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUBindingResource {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut HandleScope<'a>,\n    value: Local<'a, Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    <Ptr<GPUSampler>>::convert(\n      scope,\n      value,\n      prefix.clone(),\n      context.borrowed(),\n      options,\n    )\n    .map(Self::Sampler)\n    .or_else(|_| {\n      <Ptr<GPUTexture>>::convert(\n        scope,\n        value,\n        prefix.clone(),\n        context.borrowed(),\n        options,\n      )\n      .map(Self::Texture)\n    })\n    .or_else(|_| {\n      <Ptr<GPUTextureView>>::convert(\n        scope,\n        value,\n        prefix.clone(),\n        context.borrowed(),\n        options,\n      )\n      .map(Self::TextureView)\n    })\n    .or_else(|_| {\n      <Ptr<GPUBuffer>>::convert(\n        scope,\n        value,\n        prefix.clone(),\n        context.borrowed(),\n        options,\n      )\n      .map(Self::Buffer)\n    })\n    .or_else(|_| {\n      <Ptr<GPUExternalTexture>>::convert(\n        scope,\n        value,\n        prefix.clone(),\n        context.borrowed(),\n        options,\n      )\n      .map(Self::ExternalTexture)\n    })\n    .or_else(|_| {\n      GPUBufferBinding::convert(scope, value, prefix, context, options)\n        .map(Self::BufferBinding)\n    })\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/bind_group_layout.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::op2;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::error::GPUGenericError;\nuse crate::texture::GPUTextureViewDimension;\nuse crate::webidl::GPUShaderStageFlags;\nuse crate::Instance;\n\npub struct GPUBindGroupLayout {\n  pub instance: Instance,\n  pub id: wgpu_core::id::BindGroupLayoutId,\n  pub label: String,\n}\n\nimpl Drop for GPUBindGroupLayout {\n  fn drop(&mut self) {\n    self.instance.bind_group_layout_drop(self.id);\n  }\n}\n\nimpl deno_core::webidl::WebIdlInterfaceConverter for GPUBindGroupLayout {\n  const NAME: &'static str = \"GPUBindGroupLayout\";\n}\n\nimpl GarbageCollected for GPUBindGroupLayout {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUBindGroupLayout\"\n  }\n}\n\n#[op2]\nimpl GPUBindGroupLayout {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUBindGroupLayout, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBindGroupLayoutDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n  pub entries: Vec<GPUBindGroupLayoutEntry>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBindGroupLayoutEntry {\n  #[options(enforce_range = true)]\n  pub binding: u32,\n  pub visibility: GPUShaderStageFlags,\n  pub buffer: Option<GPUBufferBindingLayout>,\n  pub sampler: Option<GPUSamplerBindingLayout>,\n  pub texture: Option<GPUTextureBindingLayout>,\n  pub storage_texture: Option<GPUStorageTextureBindingLayout>,\n  pub external_texture: Option<GPUExternalTextureBindingLayout>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBufferBindingLayout {\n  #[webidl(default = GPUBufferBindingType::Uniform)]\n  pub r#type: GPUBufferBindingType,\n  #[webidl(default = false)]\n  pub has_dynamic_offset: bool,\n  #[webidl(default = 0)]\n  pub min_binding_size: u64,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUBufferBindingType {\n  Uniform,\n  Storage,\n  ReadOnlyStorage,\n}\n\nimpl From<GPUBufferBindingType> for wgpu_types::BufferBindingType {\n  fn from(value: GPUBufferBindingType) -> Self {\n    match value {\n      GPUBufferBindingType::Uniform => Self::Uniform,\n      GPUBufferBindingType::Storage => Self::Storage { read_only: false },\n      GPUBufferBindingType::ReadOnlyStorage => {\n        Self::Storage { read_only: true }\n      }\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUSamplerBindingLayout {\n  #[webidl(default = GPUSamplerBindingType::Filtering)]\n  pub r#type: GPUSamplerBindingType,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUSamplerBindingType {\n  Filtering,\n  NonFiltering,\n  Comparison,\n}\n\nimpl From<GPUSamplerBindingType> for wgpu_types::SamplerBindingType {\n  fn from(value: GPUSamplerBindingType) -> Self {\n    match value {\n      GPUSamplerBindingType::Filtering => Self::Filtering,\n      GPUSamplerBindingType::NonFiltering => Self::NonFiltering,\n      GPUSamplerBindingType::Comparison => Self::Comparison,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUTextureBindingLayout {\n  #[webidl(default = GPUTextureSampleType::Float)]\n  pub sample_type: GPUTextureSampleType,\n  #[webidl(default = GPUTextureViewDimension::D2)]\n  pub view_dimension: GPUTextureViewDimension,\n  #[webidl(default = false)]\n  pub multisampled: bool,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUTextureSampleType {\n  Float,\n  UnfilterableFloat,\n  Depth,\n  Sint,\n  Uint,\n}\n\nimpl From<GPUTextureSampleType> for wgpu_types::TextureSampleType {\n  fn from(value: GPUTextureSampleType) -> Self {\n    match value {\n      GPUTextureSampleType::Float => Self::Float { filterable: true },\n      GPUTextureSampleType::UnfilterableFloat => {\n        Self::Float { filterable: false }\n      }\n      GPUTextureSampleType::Depth => Self::Depth,\n      GPUTextureSampleType::Sint => Self::Sint,\n      GPUTextureSampleType::Uint => Self::Uint,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUStorageTextureBindingLayout {\n  #[webidl(default = GPUStorageTextureAccess::WriteOnly)]\n  pub access: GPUStorageTextureAccess,\n  pub format: super::texture::GPUTextureFormat,\n  #[webidl(default = GPUTextureViewDimension::D2)]\n  pub view_dimension: GPUTextureViewDimension,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUStorageTextureAccess {\n  WriteOnly,\n  ReadOnly,\n  ReadWrite,\n}\n\nimpl From<GPUStorageTextureAccess> for wgpu_types::StorageTextureAccess {\n  fn from(value: GPUStorageTextureAccess) -> Self {\n    match value {\n      GPUStorageTextureAccess::WriteOnly => Self::WriteOnly,\n      GPUStorageTextureAccess::ReadOnly => Self::ReadOnly,\n      GPUStorageTextureAccess::ReadWrite => Self::ReadWrite,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUExternalTextureBindingLayout {}\n"
  },
  {
    "path": "deno_webgpu/buffer.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse deno_core::futures::channel::oneshot;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\nuse wgpu_core::device::HostMap as MapMode;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBufferDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub size: u64,\n  #[options(enforce_range = true)]\n  pub usage: u32,\n  #[webidl(default = false)]\n  pub mapped_at_creation: bool,\n}\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\npub enum BufferError {\n  #[class(generic)]\n  #[error(transparent)]\n  Canceled(#[from] oneshot::Canceled),\n  #[class(\"DOMExceptionOperationError\")]\n  #[error(transparent)]\n  Access(#[from] wgpu_core::resource::BufferAccessError),\n  #[class(\"DOMExceptionOperationError\")]\n  #[error(\"{0}\")]\n  Operation(&'static str),\n  #[class(inherit)]\n  #[error(transparent)]\n  Other(#[from] JsErrorBox),\n}\n\npub struct GPUBuffer {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub id: wgpu_core::id::BufferId,\n  pub device: wgpu_core::id::DeviceId,\n\n  pub label: String,\n\n  pub size: u64,\n  pub usage: u32,\n\n  pub map_state: RefCell<&'static str>,\n  pub map_mode: RefCell<Option<MapMode>>,\n\n  pub mapped_js_buffers: RefCell<Vec<v8::Global<v8::ArrayBuffer>>>,\n}\n\nimpl Drop for GPUBuffer {\n  fn drop(&mut self) {\n    self.instance.buffer_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUBuffer {\n  const NAME: &'static str = \"GPUBuffer\";\n}\n\nimpl GarbageCollected for GPUBuffer {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUBuffer\"\n  }\n}\n\n#[op2]\nimpl GPUBuffer {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUBuffer, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[getter]\n  #[number]\n  fn size(&self) -> u64 {\n    self.size\n  }\n  #[getter]\n  fn usage(&self) -> u32 {\n    self.usage\n  }\n\n  #[getter]\n  #[string]\n  fn map_state(&self) -> &'static str {\n    *self.map_state.borrow()\n  }\n\n  // In the successful case, the promise should resolve to undefined, but\n  // `#[undefined]` does not seem to work here.\n  // https://github.com/denoland/deno/issues/29603\n  #[async_method]\n  async fn map_async(\n    &self,\n    #[webidl(options(enforce_range = true))] mode: u32,\n    #[webidl(default = 0)] offset: u64,\n    #[webidl] size: Option<u64>,\n  ) -> Result<(), BufferError> {\n    let read_mode = (mode & 0x0001) == 0x0001;\n    let write_mode = (mode & 0x0002) == 0x0002;\n    if (read_mode && write_mode) || (!read_mode && !write_mode) {\n      return Err(BufferError::Operation(\n        \"exactly one of READ or WRITE map mode must be set\",\n      ));\n    }\n\n    let mode = if read_mode {\n      MapMode::Read\n    } else {\n      assert!(write_mode);\n      MapMode::Write\n    };\n\n    {\n      *self.map_state.borrow_mut() = \"pending\";\n    }\n\n    let (sender, receiver) =\n      oneshot::channel::<wgpu_core::resource::BufferAccessResult>();\n\n    {\n      let callback = Box::new(move |status| {\n        sender.send(status).unwrap();\n      });\n\n      let err = self\n        .instance\n        .buffer_map_async(\n          self.id,\n          offset,\n          size,\n          wgpu_core::resource::BufferMapOperation {\n            host: mode,\n            callback: Some(callback),\n          },\n        )\n        .err();\n\n      if err.is_some() {\n        self.error_handler.push_error(err);\n        return Err(BufferError::Operation(\"validation error occurred\"));\n      }\n    }\n\n    let done = Rc::new(RefCell::new(false));\n    let done_ = done.clone();\n    let device_poll_fut = async move {\n      while !*done.borrow() {\n        {\n          self\n            .instance\n            .device_poll(self.device, wgpu_types::PollType::wait_indefinitely())\n            .unwrap();\n        }\n        tokio::time::sleep(Duration::from_millis(10)).await;\n      }\n      Ok::<(), BufferError>(())\n    };\n\n    let receiver_fut = async move {\n      receiver.await??;\n      let mut done = done_.borrow_mut();\n      *done = true;\n      Ok::<(), BufferError>(())\n    };\n\n    tokio::try_join!(device_poll_fut, receiver_fut)?;\n\n    *self.map_state.borrow_mut() = \"mapped\";\n    *self.map_mode.borrow_mut() = Some(mode);\n\n    Ok(())\n  }\n\n  fn get_mapped_range<'s>(\n    &self,\n    scope: &mut v8::HandleScope<'s>,\n    #[webidl(default = 0)] offset: u64,\n    #[webidl] size: Option<u64>,\n  ) -> Result<v8::Local<'s, v8::ArrayBuffer>, BufferError> {\n    let (slice_pointer, range_size) = self\n      .instance\n      .buffer_get_mapped_range(self.id, offset, size)\n      .map_err(BufferError::Access)?;\n\n    let mode = self.map_mode.borrow();\n    let mode = mode.as_ref().unwrap();\n\n    let bs = if mode == &MapMode::Write {\n      unsafe extern \"C\" fn noop_deleter_callback(\n        _data: *mut std::ffi::c_void,\n        _byte_length: usize,\n        _deleter_data: *mut std::ffi::c_void,\n      ) {\n      }\n\n      // SAFETY: creating a backing store from the pointer and length provided by wgpu\n      unsafe {\n        v8::ArrayBuffer::new_backing_store_from_ptr(\n          slice_pointer.as_ptr() as _,\n          range_size as usize,\n          noop_deleter_callback,\n          std::ptr::null_mut(),\n        )\n      }\n    } else {\n      // SAFETY: creating a vector from the pointer and length provided by wgpu\n      let slice = unsafe {\n        std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize)\n      };\n      v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec())\n    };\n\n    let shared_bs = bs.make_shared();\n    let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs);\n\n    if mode == &MapMode::Write {\n      self\n        .mapped_js_buffers\n        .borrow_mut()\n        .push(v8::Global::new(scope, ab));\n    }\n\n    Ok(ab)\n  }\n\n  #[nofast]\n  #[undefined]\n  fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> {\n    for ab in self.mapped_js_buffers.replace(vec![]) {\n      let ab = ab.open(scope);\n      ab.detach(None);\n    }\n\n    self\n      .instance\n      .buffer_unmap(self.id)\n      .map_err(BufferError::Access)?;\n\n    *self.map_state.borrow_mut() = \"unmapped\";\n\n    Ok(())\n  }\n\n  #[fast]\n  #[undefined]\n  fn destroy(&self) {\n    self.instance.buffer_destroy(self.id);\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/byow.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::cell::RefCell;\nuse std::ffi::c_void;\n#[cfg(any(\n  target_os = \"linux\",\n  target_os = \"macos\",\n  target_os = \"freebsd\",\n  target_os = \"netbsd\",\n  target_os = \"openbsd\"\n))]\nuse std::ptr::NonNull;\n\nuse deno_core::cppgc::SameObject;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::v8::Local;\nuse deno_core::v8::Value;\nuse deno_core::FromV8;\nuse deno_core::GarbageCollected;\nuse deno_core::OpState;\nuse deno_error::JsErrorBox;\n\nuse crate::surface::GPUCanvasContext;\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\npub enum ByowError {\n  #[cfg(not(any(\n    target_os = \"macos\",\n    target_os = \"windows\",\n    target_os = \"linux\",\n    target_os = \"freebsd\",\n    target_os = \"netbsd\",\n    target_os = \"openbsd\",\n  )))]\n  #[class(type)]\n  #[error(\"Unsupported platform\")]\n  Unsupported,\n  #[class(type)]\n  #[error(\n    \"Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?\"\n  )]\n  WebGPUNotInitiated,\n  #[class(type)]\n  #[error(\"Invalid parameters\")]\n  InvalidParameters,\n  #[class(generic)]\n  #[error(transparent)]\n  CreateSurface(wgpu_core::instance::CreateSurfaceError),\n  #[cfg(target_os = \"windows\")]\n  #[class(type)]\n  #[error(\"Invalid system on Windows\")]\n  InvalidSystem,\n  #[cfg(target_os = \"macos\")]\n  #[class(type)]\n  #[error(\"Invalid system on macOS\")]\n  InvalidSystem,\n  #[cfg(any(\n    target_os = \"linux\",\n    target_os = \"freebsd\",\n    target_os = \"netbsd\",\n    target_os = \"openbsd\"\n  ))]\n  #[class(type)]\n  #[error(\"Invalid system on Linux/BSD\")]\n  InvalidSystem,\n  #[cfg(any(\n    target_os = \"windows\",\n    target_os = \"linux\",\n    target_os = \"freebsd\",\n    target_os = \"netbsd\",\n    target_os = \"openbsd\"\n  ))]\n  #[class(type)]\n  #[error(\"window is null\")]\n  NullWindow,\n  #[cfg(any(\n    target_os = \"linux\",\n    target_os = \"freebsd\",\n    target_os = \"netbsd\",\n    target_os = \"openbsd\"\n  ))]\n  #[class(type)]\n  #[error(\"display is null\")]\n  NullDisplay,\n  #[cfg(target_os = \"macos\")]\n  #[class(type)]\n  #[error(\"ns_view is null\")]\n  NSViewDisplay,\n}\n\n// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it.\npub struct UnsafeWindowSurface {\n  pub id: wgpu_core::id::SurfaceId,\n  pub width: RefCell<u32>,\n  pub height: RefCell<u32>,\n\n  pub context: SameObject<GPUCanvasContext>,\n}\n\nimpl GarbageCollected for UnsafeWindowSurface {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"UnsafeWindowSurface\"\n  }\n}\n\n#[op2]\nimpl UnsafeWindowSurface {\n  #[constructor]\n  #[cppgc]\n  fn new(\n    state: &mut OpState,\n    #[from_v8] options: UnsafeWindowSurfaceOptions,\n  ) -> Result<UnsafeWindowSurface, ByowError> {\n    let instance = state\n      .try_borrow::<super::Instance>()\n      .ok_or(ByowError::WebGPUNotInitiated)?;\n\n    // Security note:\n    //\n    // The `window_handle` and `display_handle` options are pointers to\n    // platform-specific window handles.\n    //\n    // The code below works under the assumption that:\n    //\n    // - handles can only be created by the FFI interface which\n    // enforces --allow-ffi.\n    //\n    // - `*const c_void` deserializes null and v8::External.\n    //\n    // - Only FFI can export v8::External to user code.\n    if options.window_handle.is_null() {\n      return Err(ByowError::InvalidParameters);\n    }\n\n    let (win_handle, display_handle) = raw_window(\n      options.system,\n      options.window_handle,\n      options.display_handle,\n    )?;\n\n    // SAFETY: see above comment\n    let id = unsafe {\n      instance\n        .instance_create_surface(display_handle, win_handle, None)\n        .map_err(ByowError::CreateSurface)?\n    };\n\n    Ok(UnsafeWindowSurface {\n      id,\n      width: RefCell::new(options.width),\n      height: RefCell::new(options.height),\n      context: SameObject::new(),\n    })\n  }\n\n  #[global]\n  fn get_context(\n    &self,\n    #[this] this: v8::Global<v8::Object>,\n    scope: &mut v8::HandleScope,\n  ) -> v8::Global<v8::Object> {\n    self.context.get(scope, |_| GPUCanvasContext {\n      surface_id: self.id,\n      width: self.width.clone(),\n      height: self.height.clone(),\n      config: RefCell::new(None),\n      texture: RefCell::new(None),\n      canvas: this,\n    })\n  }\n\n  #[nofast]\n  fn present(&self, scope: &mut v8::HandleScope) -> Result<(), JsErrorBox> {\n    let Some(context) = self.context.try_unwrap(scope) else {\n      return Err(JsErrorBox::type_error(\"getContext was never called\"));\n    };\n\n    context.present().map_err(JsErrorBox::from_err)\n  }\n\n  #[fast]\n  fn resize(&self, width: u32, height: u32, scope: &mut v8::HandleScope) {\n    self.width.replace(width);\n    self.height.replace(height);\n\n    let Some(context) = self.context.try_unwrap(scope) else {\n      return;\n    };\n\n    context.resize_configure(width, height);\n  }\n}\n\nstruct UnsafeWindowSurfaceOptions {\n  system: UnsafeWindowSurfaceSystem,\n  window_handle: *const c_void,\n  display_handle: *const c_void,\n  width: u32,\n  height: u32,\n}\n\n#[derive(Eq, PartialEq)]\nenum UnsafeWindowSurfaceSystem {\n  Cocoa,\n  Win32,\n  X11,\n  Wayland,\n}\n\nimpl<'a> FromV8<'a> for UnsafeWindowSurfaceOptions {\n  type Error = JsErrorBox;\n\n  fn from_v8(\n    scope: &mut v8::HandleScope<'a>,\n    value: Local<'a, Value>,\n  ) -> Result<Self, Self::Error> {\n    let obj = value\n      .try_cast::<v8::Object>()\n      .map_err(|_| JsErrorBox::type_error(\"is not an object\"))?;\n\n    let key = v8::String::new(scope, \"system\").unwrap();\n    let val = obj\n      .get(scope, key.into())\n      .ok_or_else(|| JsErrorBox::type_error(\"missing field 'system'\"))?;\n    let s = String::from_v8(scope, val).unwrap();\n    let system = match s.as_str() {\n      \"cocoa\" => UnsafeWindowSurfaceSystem::Cocoa,\n      \"win32\" => UnsafeWindowSurfaceSystem::Win32,\n      \"x11\" => UnsafeWindowSurfaceSystem::X11,\n      \"wayland\" => UnsafeWindowSurfaceSystem::Wayland,\n      _ => {\n        return Err(JsErrorBox::type_error(format!(\n          \"Invalid system kind '{s}'\"\n        )));\n      }\n    };\n\n    let key = v8::String::new(scope, \"windowHandle\").unwrap();\n    let val = obj\n      .get(scope, key.into())\n      .ok_or_else(|| JsErrorBox::type_error(\"missing field 'windowHandle'\"))?;\n    let Some(window_handle) = deno_core::_ops::to_external_option(&val) else {\n      return Err(JsErrorBox::type_error(\"expected external\"));\n    };\n\n    let key = v8::String::new(scope, \"displayHandle\").unwrap();\n    let val = obj\n      .get(scope, key.into())\n      .ok_or_else(|| JsErrorBox::type_error(\"missing field 'displayHandle'\"))?;\n    let Some(display_handle) = deno_core::_ops::to_external_option(&val) else {\n      return Err(JsErrorBox::type_error(\"expected external\"));\n    };\n\n    let key = v8::String::new(scope, \"width\").unwrap();\n    let val = obj\n      .get(scope, key.into())\n      .ok_or_else(|| JsErrorBox::type_error(\"missing field 'width'\"))?;\n    let width = deno_core::convert::Number::<u32>::from_v8(scope, val)?.0;\n\n    let key = v8::String::new(scope, \"height\").unwrap();\n    let val = obj\n      .get(scope, key.into())\n      .ok_or_else(|| JsErrorBox::type_error(\"missing field 'height'\"))?;\n    let height = deno_core::convert::Number::<u32>::from_v8(scope, val)?.0;\n\n    Ok(Self {\n      system,\n      window_handle,\n      display_handle,\n      width,\n      height,\n    })\n  }\n}\n\ntype RawHandles = (\n  raw_window_handle::RawWindowHandle,\n  Option<raw_window_handle::RawDisplayHandle>,\n);\n\n#[cfg(target_os = \"macos\")]\nfn raw_window(\n  system: UnsafeWindowSurfaceSystem,\n  _ns_window: *const c_void,\n  ns_view: *const c_void,\n) -> Result<RawHandles, ByowError> {\n  if system != UnsafeWindowSurfaceSystem::Cocoa {\n    return Err(ByowError::InvalidSystem);\n  }\n\n  let win_handle = raw_window_handle::RawWindowHandle::AppKit(\n    raw_window_handle::AppKitWindowHandle::new(\n      NonNull::new(ns_view as *mut c_void).ok_or(ByowError::NSViewDisplay)?,\n    ),\n  );\n\n  let display_handle = raw_window_handle::RawDisplayHandle::AppKit(\n    raw_window_handle::AppKitDisplayHandle::new(),\n  );\n  Ok((win_handle, Some(display_handle)))\n}\n\n#[cfg(target_os = \"windows\")]\nfn raw_window(\n  system: UnsafeWindowSurfaceSystem,\n  window: *const c_void,\n  hinstance: *const c_void,\n) -> Result<RawHandles, ByowError> {\n  use raw_window_handle::WindowsDisplayHandle;\n  if system != UnsafeWindowSurfaceSystem::Win32 {\n    return Err(ByowError::InvalidSystem);\n  }\n\n  let win_handle = {\n    let mut handle = raw_window_handle::Win32WindowHandle::new(\n      std::num::NonZeroIsize::new(window as isize)\n        .ok_or(ByowError::NullWindow)?,\n    );\n    handle.hinstance = std::num::NonZeroIsize::new(hinstance as isize);\n\n    raw_window_handle::RawWindowHandle::Win32(handle)\n  };\n\n  let display_handle =\n    raw_window_handle::RawDisplayHandle::Windows(WindowsDisplayHandle::new());\n  Ok((win_handle, Some(display_handle)))\n}\n\n#[cfg(any(\n  target_os = \"linux\",\n  target_os = \"freebsd\",\n  target_os = \"netbsd\",\n  target_os = \"openbsd\"\n))]\nfn raw_window(\n  system: UnsafeWindowSurfaceSystem,\n  window: *const c_void,\n  display: *const c_void,\n) -> Result<RawHandles, ByowError> {\n  let (win_handle, display_handle);\n  if system == UnsafeWindowSurfaceSystem::X11 {\n    win_handle = raw_window_handle::RawWindowHandle::Xlib(\n      raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _),\n    );\n\n    display_handle = raw_window_handle::RawDisplayHandle::Xlib(\n      raw_window_handle::XlibDisplayHandle::new(\n        NonNull::new(display as *mut c_void),\n        0,\n      ),\n    );\n  } else if system == UnsafeWindowSurfaceSystem::Wayland {\n    win_handle = raw_window_handle::RawWindowHandle::Wayland(\n      raw_window_handle::WaylandWindowHandle::new(\n        NonNull::new(window as *mut c_void).ok_or(ByowError::NullWindow)?,\n      ),\n    );\n\n    display_handle = raw_window_handle::RawDisplayHandle::Wayland(\n      raw_window_handle::WaylandDisplayHandle::new(\n        NonNull::new(display as *mut c_void).ok_or(ByowError::NullDisplay)?,\n      ),\n    );\n  } else {\n    return Err(ByowError::InvalidSystem);\n  }\n\n  Ok((win_handle, Some(display_handle)))\n}\n\n#[cfg(not(any(\n  target_os = \"macos\",\n  target_os = \"windows\",\n  target_os = \"linux\",\n  target_os = \"freebsd\",\n  target_os = \"netbsd\",\n  target_os = \"openbsd\",\n)))]\nfn raw_window(\n  _system: UnsafeWindowSurfaceSystem,\n  _window: *const c_void,\n  _display: *const c_void,\n) -> Result<RawHandles, ByowError> {\n  Err(ByowError::Unsupported)\n}\n"
  },
  {
    "path": "deno_webgpu/command_buffer.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::op2;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUCommandBuffer {\n  pub instance: Instance,\n  pub id: wgpu_core::id::CommandBufferId,\n  pub label: String,\n}\n\nimpl Drop for GPUCommandBuffer {\n  fn drop(&mut self) {\n    self.instance.command_buffer_drop(self.id);\n  }\n}\n\nimpl deno_core::webidl::WebIdlInterfaceConverter for GPUCommandBuffer {\n  const NAME: &'static str = \"GPUCommandBuffer\";\n}\n\nimpl GarbageCollected for GPUCommandBuffer {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUCommandBuffer\"\n  }\n}\n\n#[op2]\nimpl GPUCommandBuffer {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUCommandBuffer, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUCommandBufferDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n}\n"
  },
  {
    "path": "deno_webgpu/command_encoder.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\nuse std::cell::RefCell;\nuse std::num::NonZero;\n#[cfg(target_vendor = \"apple\")]\nuse std::sync::OnceLock;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::{IntOptions, WebIdlConverter, WebIdlError};\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\nuse wgpu_core::command::PassChannel;\nuse wgpu_types::{BufferAddress, TexelCopyBufferInfo};\n\nuse crate::buffer::GPUBuffer;\nuse crate::command_buffer::GPUCommandBuffer;\nuse crate::compute_pass::GPUComputePassEncoder;\nuse crate::error::GPUGenericError;\nuse crate::queue::GPUTexelCopyTextureInfo;\nuse crate::render_pass::GPURenderPassEncoder;\nuse crate::webidl::GPUExtent3D;\nuse crate::Instance;\n\npub struct GPUCommandEncoder {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub id: wgpu_core::id::CommandEncoderId,\n  pub label: String,\n\n  // Weak reference to the JS object so we can attach a finalizer.\n  // See `GPUDevice::create_command_encoder`.\n  #[cfg(target_vendor = \"apple\")]\n  pub(crate) weak: OnceLock<v8::Weak<v8::Object>>,\n}\n\nimpl Drop for GPUCommandEncoder {\n  fn drop(&mut self) {\n    self.instance.command_encoder_drop(self.id);\n  }\n}\n\nimpl GarbageCollected for GPUCommandEncoder {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUCommandEncoder\"\n  }\n}\n\n#[op2]\nimpl GPUCommandEncoder {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUCommandEncoder, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn begin_render_pass(\n    &self,\n    #[webidl] descriptor: crate::render_pass::GPURenderPassDescriptor,\n  ) -> Result<GPURenderPassEncoder, JsErrorBox> {\n    let color_attachments = Cow::Owned(\n      descriptor\n        .color_attachments\n        .into_iter()\n        .map(|attachment| {\n          attachment.into_option().map(|attachment| {\n            wgpu_core::command::RenderPassColorAttachment {\n              view: attachment.view.to_view_id(),\n              depth_slice: attachment.depth_slice,\n              resolve_target: attachment\n                .resolve_target\n                .map(|target| target.to_view_id()),\n              load_op: attachment\n                .load_op\n                .with_default_value(attachment.clear_value.map(Into::into)),\n              store_op: attachment.store_op.into(),\n            }\n          })\n        })\n        .collect::<Vec<_>>(),\n    );\n\n    let depth_stencil_attachment =\n      descriptor.depth_stencil_attachment.map(|attachment| {\n        wgpu_core::command::RenderPassDepthStencilAttachment {\n          view: attachment.view.to_view_id(),\n          depth: PassChannel {\n            load_op: attachment\n              .depth_load_op\n              .map(|load_op| load_op.with_value(attachment.depth_clear_value)),\n            store_op: attachment.depth_store_op.map(Into::into),\n            read_only: attachment.depth_read_only,\n          },\n          stencil: PassChannel {\n            load_op: attachment.stencil_load_op.map(|load_op| {\n              load_op.with_value(Some(attachment.stencil_clear_value))\n            }),\n            store_op: attachment.stencil_store_op.map(Into::into),\n            read_only: attachment.stencil_read_only,\n          },\n        }\n      });\n\n    let timestamp_writes =\n      descriptor.timestamp_writes.map(|timestamp_writes| {\n        wgpu_core::command::PassTimestampWrites {\n          query_set: timestamp_writes.query_set.id,\n          beginning_of_pass_write_index: timestamp_writes\n            .beginning_of_pass_write_index,\n          end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,\n        }\n      });\n\n    let wgpu_descriptor = wgpu_core::command::RenderPassDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      color_attachments,\n      depth_stencil_attachment: depth_stencil_attachment.as_ref(),\n      timestamp_writes: timestamp_writes.as_ref(),\n      occlusion_query_set: descriptor\n        .occlusion_query_set\n        .map(|query_set| query_set.id),\n      multiview_mask: NonZero::new(descriptor.multiview_mask),\n    };\n\n    let (render_pass, err) = self\n      .instance\n      .command_encoder_begin_render_pass(self.id, &wgpu_descriptor);\n\n    self.error_handler.push_error(err);\n\n    Ok(GPURenderPassEncoder {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      render_pass: RefCell::new(render_pass),\n      label: descriptor.label,\n    })\n  }\n\n  #[cppgc]\n  fn begin_compute_pass(\n    &self,\n    #[webidl] descriptor: crate::compute_pass::GPUComputePassDescriptor,\n  ) -> GPUComputePassEncoder {\n    let timestamp_writes =\n      descriptor.timestamp_writes.map(|timestamp_writes| {\n        wgpu_core::command::PassTimestampWrites {\n          query_set: timestamp_writes.query_set.id,\n          beginning_of_pass_write_index: timestamp_writes\n            .beginning_of_pass_write_index,\n          end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,\n        }\n      });\n\n    let wgpu_descriptor = wgpu_core::command::ComputePassDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      timestamp_writes,\n    };\n\n    let (compute_pass, err) = self\n      .instance\n      .command_encoder_begin_compute_pass(self.id, &wgpu_descriptor);\n\n    self.error_handler.push_error(err);\n\n    GPUComputePassEncoder {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      compute_pass: RefCell::new(compute_pass),\n      label: descriptor.label,\n    }\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn copy_buffer_to_buffer<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n    #[webidl] source: Ptr<GPUBuffer>,\n    arg2: v8::Local<'a, v8::Value>,\n    arg3: v8::Local<'a, v8::Value>,\n    arg4: v8::Local<'a, v8::Value>,\n    arg5: v8::Local<'a, v8::Value>,\n  ) -> Result<(), WebIdlError> {\n    let prefix = \"Failed to execute 'GPUCommandEncoder.copyBufferToBuffer'\";\n    let int_options = IntOptions {\n      clamp: false,\n      enforce_range: true,\n    };\n\n    let source_offset: BufferAddress;\n    let destination: Ptr<GPUBuffer>;\n    let destination_offset: BufferAddress;\n    let size: Option<BufferAddress>;\n    // Note that the last argument to either overload of `copy_buffer_to_buffer`\n    // is optional, so `arg5.is_undefined()` would not work here.\n    if arg4.is_undefined() {\n      // 3-argument overload\n      source_offset = 0;\n      destination = Ptr::<GPUBuffer>::convert(\n        scope,\n        arg2,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"destination\")).into(),\n        &(),\n      )?;\n      destination_offset = 0;\n      size = <Option<u64>>::convert(\n        scope,\n        arg3,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"size\")).into(),\n        &int_options,\n      )?;\n    } else {\n      // 5-argument overload\n      source_offset = u64::convert(\n        scope,\n        arg2,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"sourceOffset\")).into(),\n        &int_options,\n      )?;\n      destination = Ptr::<GPUBuffer>::convert(\n        scope,\n        arg3,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"destination\")).into(),\n        &(),\n      )?;\n      destination_offset = u64::convert(\n        scope,\n        arg4,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"destinationOffset\")).into(),\n        &int_options,\n      )?;\n      size = <Option<u64>>::convert(\n        scope,\n        arg5,\n        Cow::Borrowed(prefix),\n        (|| Cow::Borrowed(\"size\")).into(),\n        &int_options,\n      )?;\n    }\n\n    let err = self\n      .instance\n      .command_encoder_copy_buffer_to_buffer(\n        self.id,\n        source.id,\n        source_offset,\n        destination.id,\n        destination_offset,\n        size,\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n\n    Ok(())\n  }\n\n  #[required(3)]\n  #[undefined]\n  fn copy_buffer_to_texture(\n    &self,\n    #[webidl] source: GPUTexelCopyBufferInfo,\n    #[webidl] destination: GPUTexelCopyTextureInfo,\n    #[webidl] copy_size: GPUExtent3D,\n  ) {\n    let source = TexelCopyBufferInfo {\n      buffer: source.buffer.id,\n      layout: wgpu_types::TexelCopyBufferLayout {\n        offset: source.offset,\n        bytes_per_row: source.bytes_per_row,\n        rows_per_image: source.rows_per_image,\n      },\n    };\n    let destination = wgpu_types::TexelCopyTextureInfo {\n      texture: destination.texture.id,\n      mip_level: destination.mip_level,\n      origin: destination.origin.into(),\n      aspect: destination.aspect.into(),\n    };\n\n    let err = self\n      .instance\n      .command_encoder_copy_buffer_to_texture(\n        self.id,\n        &source,\n        &destination,\n        &copy_size.into(),\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n\n  #[required(3)]\n  #[undefined]\n  fn copy_texture_to_buffer(\n    &self,\n    #[webidl] source: GPUTexelCopyTextureInfo,\n    #[webidl] destination: GPUTexelCopyBufferInfo,\n    #[webidl] copy_size: GPUExtent3D,\n  ) {\n    let source = wgpu_types::TexelCopyTextureInfo {\n      texture: source.texture.id,\n      mip_level: source.mip_level,\n      origin: source.origin.into(),\n      aspect: source.aspect.into(),\n    };\n    let destination = TexelCopyBufferInfo {\n      buffer: destination.buffer.id,\n      layout: wgpu_types::TexelCopyBufferLayout {\n        offset: destination.offset,\n        bytes_per_row: destination.bytes_per_row,\n        rows_per_image: destination.rows_per_image,\n      },\n    };\n\n    let err = self\n      .instance\n      .command_encoder_copy_texture_to_buffer(\n        self.id,\n        &source,\n        &destination,\n        &copy_size.into(),\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n\n  #[required(3)]\n  #[undefined]\n  fn copy_texture_to_texture(\n    &self,\n    #[webidl] source: GPUTexelCopyTextureInfo,\n    #[webidl] destination: GPUTexelCopyTextureInfo,\n    #[webidl] copy_size: GPUExtent3D,\n  ) {\n    let source = wgpu_types::TexelCopyTextureInfo {\n      texture: source.texture.id,\n      mip_level: source.mip_level,\n      origin: source.origin.into(),\n      aspect: source.aspect.into(),\n    };\n    let destination = wgpu_types::TexelCopyTextureInfo {\n      texture: destination.texture.id,\n      mip_level: destination.mip_level,\n      origin: destination.origin.into(),\n      aspect: destination.aspect.into(),\n    };\n\n    let err = self\n      .instance\n      .command_encoder_copy_texture_to_texture(\n        self.id,\n        &source,\n        &destination,\n        &copy_size.into(),\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn clear_buffer(\n    &self,\n    #[webidl] buffer: Ptr<GPUBuffer>,\n    #[webidl(default = 0, options(enforce_range = true))] offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) {\n    let err = self\n      .instance\n      .command_encoder_clear_buffer(self.id, buffer.id, offset, size)\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(5)]\n  #[undefined]\n  fn resolve_query_set(\n    &self,\n    #[webidl] query_set: Ptr<super::query_set::GPUQuerySet>,\n    #[webidl(options(enforce_range = true))] first_query: u32,\n    #[webidl(options(enforce_range = true))] query_count: u32,\n    #[webidl] destination: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] destination_offset: u64,\n  ) {\n    let err = self\n      .instance\n      .command_encoder_resolve_query_set(\n        self.id,\n        query_set.id,\n        first_query,\n        query_count,\n        destination.id,\n        destination_offset,\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n\n  #[cppgc]\n  fn finish(\n    &self,\n    #[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor,\n  ) -> GPUCommandBuffer {\n    let wgpu_descriptor = wgpu_types::CommandBufferDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n    };\n\n    let (id, opt_label_and_err) =\n      self\n        .instance\n        .command_encoder_finish(self.id, &wgpu_descriptor, None);\n\n    self\n      .error_handler\n      .push_error(opt_label_and_err.map(|(_label, err)| err));\n\n    GPUCommandBuffer {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    }\n  }\n\n  fn push_debug_group(&self, #[webidl] group_label: String) {\n    let err = self\n      .instance\n      .command_encoder_push_debug_group(self.id, &group_label)\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  fn pop_debug_group(&self) {\n    let err = self.instance.command_encoder_pop_debug_group(self.id).err();\n    self.error_handler.push_error(err);\n  }\n\n  fn insert_debug_marker(&self, #[webidl] marker_label: String) {\n    let err = self\n      .instance\n      .command_encoder_insert_debug_marker(self.id, &marker_label)\n      .err();\n    self.error_handler.push_error(err);\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUCommandEncoderDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUTexelCopyBufferInfo {\n  pub buffer: Ptr<GPUBuffer>,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  offset: u64,\n  #[options(enforce_range = true)]\n  bytes_per_row: Option<u32>,\n  #[options(enforce_range = true)]\n  rows_per_image: Option<u32>,\n}\n"
  },
  {
    "path": "deno_webgpu/compute_pass.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\nuse std::cell::RefCell;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::IntOptions;\nuse deno_core::webidl::Nullable;\nuse deno_core::webidl::WebIdlConverter;\nuse deno_core::webidl::WebIdlError;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUComputePassEncoder {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub compute_pass: RefCell<wgpu_core::command::ComputePass>,\n  pub label: String,\n}\n\nimpl GarbageCollected for GPUComputePassEncoder {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUComputePassEncoder\"\n  }\n}\n\n#[op2]\nimpl GPUComputePassEncoder {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUComputePassEncoder, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[undefined]\n  fn set_pipeline(\n    &self,\n    #[webidl] pipeline: Ptr<crate::compute_pipeline::GPUComputePipeline>,\n  ) {\n    let err = self\n      .instance\n      .compute_pass_set_pipeline(\n        &mut self.compute_pass.borrow_mut(),\n        pipeline.id,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn dispatch_workgroups(\n    &self,\n    #[webidl(options(enforce_range = true))] work_group_count_x: u32,\n    #[webidl(default = 1, options(enforce_range = true))]\n    work_group_count_y: u32,\n    #[webidl(default = 1, options(enforce_range = true))]\n    work_group_count_z: u32,\n  ) {\n    let err = self\n      .instance\n      .compute_pass_dispatch_workgroups(\n        &mut self.compute_pass.borrow_mut(),\n        work_group_count_x,\n        work_group_count_y,\n        work_group_count_z,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn dispatch_workgroups_indirect(\n    &self,\n    #[webidl] indirect_buffer: Ptr<crate::buffer::GPUBuffer>,\n    #[webidl(options(enforce_range = true))] indirect_offset: u64,\n  ) {\n    let err = self\n      .instance\n      .compute_pass_dispatch_workgroups_indirect(\n        &mut self.compute_pass.borrow_mut(),\n        indirect_buffer.id,\n        indirect_offset,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  #[undefined]\n  fn end(&self) {\n    let err = self\n      .instance\n      .compute_pass_end(&mut self.compute_pass.borrow_mut())\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn push_debug_group(&self, #[webidl] group_label: String) {\n    let err = self\n      .instance\n      .compute_pass_push_debug_group(\n        &mut self.compute_pass.borrow_mut(),\n        &group_label,\n        0, // wgpu#975\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  #[undefined]\n  fn pop_debug_group(&self) {\n    let err = self\n      .instance\n      .compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut())\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn insert_debug_marker(&self, #[webidl] marker_label: String) {\n    let err = self\n      .instance\n      .compute_pass_insert_debug_marker(\n        &mut self.compute_pass.borrow_mut(),\n        &marker_label,\n        0, // wgpu#975\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn set_bind_group<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n    #[webidl(options(enforce_range = true))] index: u32,\n    #[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,\n    dynamic_offsets: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_start: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_length: v8::Local<'a, v8::Value>,\n  ) -> Result<(), WebIdlError> {\n    const PREFIX: &str =\n      \"Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'\";\n    let err = if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>()\n    {\n      let start = u64::convert(\n        scope,\n        dynamic_offsets_data_start,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 4\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n      let len = u32::convert(\n        scope,\n        dynamic_offsets_data_length,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 5\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n\n      let ab = uint_32.buffer(scope).unwrap();\n      let ptr = ab.data().unwrap();\n      let ab_len = ab.byte_length() / 4;\n\n      // SAFETY: compute_pass_set_bind_group internally calls extend_from_slice with this slice\n      let data =\n        unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };\n\n      let offsets = &data[start..(start + len)];\n\n      self\n        .instance\n        .compute_pass_set_bind_group(\n          &mut self.compute_pass.borrow_mut(),\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          offsets,\n        )\n        .err()\n    } else {\n      let offsets = <Option<Vec<u32>>>::convert(\n        scope,\n        dynamic_offsets,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 3\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )?\n      .unwrap_or_default();\n\n      self\n        .instance\n        .compute_pass_set_bind_group(\n          &mut self.compute_pass.borrow_mut(),\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          &offsets,\n        )\n        .err()\n    };\n\n    self.error_handler.push_error(err);\n\n    Ok(())\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUComputePassDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub timestamp_writes: Option<GPUComputePassTimestampWrites>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUComputePassTimestampWrites {\n  pub query_set: Ptr<crate::query_set::GPUQuerySet>,\n  #[options(enforce_range = true)]\n  pub beginning_of_pass_write_index: Option<u32>,\n  #[options(enforce_range = true)]\n  pub end_of_pass_write_index: Option<u32>,\n}\n"
  },
  {
    "path": "deno_webgpu/compute_pipeline.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse indexmap::IndexMap;\n\nuse crate::bind_group_layout::GPUBindGroupLayout;\nuse crate::error::GPUGenericError;\nuse crate::shader::GPUShaderModule;\nuse crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;\nuse crate::Instance;\n\npub struct GPUComputePipeline {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub id: wgpu_core::id::ComputePipelineId,\n  pub label: String,\n}\n\nimpl Drop for GPUComputePipeline {\n  fn drop(&mut self) {\n    self.instance.compute_pipeline_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUComputePipeline {\n  const NAME: &'static str = \"GPUComputePipeline\";\n}\n\nimpl GarbageCollected for GPUComputePipeline {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUComputePipeline\"\n  }\n}\n\n#[op2]\nimpl GPUComputePipeline {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUComputePipeline, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[cppgc]\n  fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {\n    let (id, err) = self\n      .instance\n      .compute_pipeline_get_bind_group_layout(self.id, index, None);\n\n    self.error_handler.push_error(err);\n\n    // TODO(wgpu): needs to support retrieving the label\n    GPUBindGroupLayout {\n      instance: self.instance.clone(),\n      id,\n      label: \"\".to_string(),\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUComputePipelineDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub compute: GPUProgrammableStage,\n  pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUProgrammableStage {\n  pub module: Ptr<GPUShaderModule>,\n  pub entry_point: Option<String>,\n  #[webidl(default = Default::default())]\n  pub constants: IndexMap<String, f64>,\n}\n"
  },
  {
    "path": "deno_webgpu/device.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\nuse std::cell::RefCell;\nuse std::num::NonZeroU64;\nuse std::rc::Rc;\n\nuse deno_core::cppgc::{make_cppgc_object, SameObject};\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_error::JsErrorBox;\nuse wgpu_core::binding_model::BindingResource;\nuse wgpu_core::pipeline::ProgrammableStageDescriptor;\nuse wgpu_types::BindingType;\n\nuse super::bind_group::GPUBindGroup;\nuse super::bind_group::GPUBindingResource;\nuse super::bind_group_layout::GPUBindGroupLayout;\nuse super::buffer::GPUBuffer;\nuse super::compute_pipeline::GPUComputePipeline;\nuse super::pipeline_layout::GPUPipelineLayout;\nuse super::sampler::GPUSampler;\nuse super::shader::GPUShaderModule;\nuse super::texture::GPUTexture;\nuse crate::adapter::GPUAdapterInfo;\nuse crate::adapter::GPUSupportedFeatures;\nuse crate::adapter::GPUSupportedLimits;\nuse crate::command_encoder::GPUCommandEncoder;\nuse crate::error::{fmt_err, make_pipeline_error};\nuse crate::error::{GPUError, GPUGenericError, GPUPipelineErrorReason};\nuse crate::query_set::GPUQuerySet;\nuse crate::render_bundle::GPURenderBundleEncoder;\nuse crate::render_pipeline::GPURenderPipeline;\nuse crate::shader::GPUCompilationInfo;\nuse crate::Instance;\n\n/// External memory associated with device and queue, to encourage V8 to garbage\n/// collect devices promptly. This seems to be particularly important when\n/// running CTS tests under `webgpu:api,validation,capability_checks,limits,*`\n/// on DX12 in wgpu CI, where any smaller power of two results in OOM errors.\npub(crate) const DEVICE_EXTERNAL_MEMORY_SIZE: i64 = 1 << 24; // 16 MB\n\npub struct GPUDevice {\n  pub instance: Instance,\n  pub id: wgpu_core::id::DeviceId,\n  pub adapter: wgpu_core::id::AdapterId,\n\n  pub label: String,\n\n  pub features: SameObject<GPUSupportedFeatures>,\n  pub limits: SameObject<GPUSupportedLimits>,\n  pub adapter_info: Rc<SameObject<GPUAdapterInfo>>,\n\n  pub queue_obj: v8::Global<v8::Object>,\n\n  pub error_handler: super::error::ErrorHandler,\n  pub lost_promise: v8::Global<v8::Promise>,\n\n  // Weak reference to the JS object so we can attach a finalizer.\n  pub(crate) weak: std::sync::OnceLock<v8::Weak<v8::Object>>,\n}\n\nimpl Drop for GPUDevice {\n  fn drop(&mut self) {\n    self.instance.device_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUDevice {\n  const NAME: &'static str = \"GPUDevice\";\n}\n\nimpl GarbageCollected for GPUDevice {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUDevice\"\n  }\n}\n\n// EventTarget is extended in JS\n#[op2]\nimpl GPUDevice {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUDevice, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[getter]\n  #[global]\n  fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {\n    self.features.get(scope, |scope| {\n      let features = self.instance.device_features(self.id);\n      GPUSupportedFeatures::new(scope, features)\n    })\n  }\n\n  #[getter]\n  #[global]\n  fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {\n    self.limits.get(scope, |_| {\n      let limits = self.instance.device_limits(self.id);\n      GPUSupportedLimits(limits)\n    })\n  }\n\n  #[getter]\n  #[global]\n  fn adapter_info(\n    &self,\n    scope: &mut v8::HandleScope,\n  ) -> v8::Global<v8::Object> {\n    self.adapter_info.get(scope, |_| {\n      let info = self.instance.adapter_get_info(self.adapter);\n\n      GPUAdapterInfo { info }\n    })\n  }\n\n  #[getter]\n  #[global]\n  fn queue(&self) -> v8::Global<v8::Object> {\n    self.queue_obj.clone()\n  }\n\n  #[fast]\n  #[undefined]\n  fn destroy(&self) {\n    self.instance.device_destroy(self.id);\n    self\n      .error_handler\n      .push_error(Some(GPUError::Lost(GPUDeviceLostReason::Destroyed)));\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_buffer(\n    &self,\n    #[webidl] descriptor: super::buffer::GPUBufferDescriptor,\n  ) -> Result<GPUBuffer, JsErrorBox> {\n    // wgpu-core would also check this, but it needs to be reported via a JS\n    // error, not a validation error. (WebGPU specifies this check on the\n    // content timeline.)\n    if descriptor.mapped_at_creation\n      && !descriptor\n        .size\n        .is_multiple_of(wgpu_types::COPY_BUFFER_ALIGNMENT)\n    {\n      return Err(JsErrorBox::range_error(\n        format!(\n          \"The size of a buffer that is mapped at creation must be a multiple of {}\",\n          wgpu_types::COPY_BUFFER_ALIGNMENT,\n        )\n      ));\n    }\n\n    // Validation of the usage needs to happen on the device timeline, so\n    // don't raise an error immediately if it isn't valid. wgpu will\n    // reject `BufferUsages::empty()`.\n    let usage = wgpu_types::BufferUsages::from_bits(descriptor.usage)\n      .unwrap_or(wgpu_types::BufferUsages::empty());\n\n    let wgpu_descriptor = wgpu_core::resource::BufferDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      size: descriptor.size,\n      usage,\n      mapped_at_creation: descriptor.mapped_at_creation,\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .device_create_buffer(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    Ok(GPUBuffer {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      id,\n      device: self.id,\n      label: descriptor.label,\n      size: descriptor.size,\n      usage: descriptor.usage,\n      map_state: RefCell::new(if descriptor.mapped_at_creation {\n        \"mapped\"\n      } else {\n        \"unmapped\"\n      }),\n      map_mode: RefCell::new(if descriptor.mapped_at_creation {\n        Some(wgpu_core::device::HostMap::Write)\n      } else {\n        None\n      }),\n      mapped_js_buffers: RefCell::new(vec![]),\n    })\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_texture(\n    &self,\n    #[webidl] descriptor: super::texture::GPUTextureDescriptor,\n  ) -> Result<GPUTexture, JsErrorBox> {\n    let wgpu_descriptor = wgpu_core::resource::TextureDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      size: descriptor.size.into(),\n      mip_level_count: descriptor.mip_level_count,\n      sample_count: descriptor.sample_count,\n      dimension: descriptor.dimension.clone().into(),\n      format: descriptor.format.clone().into(),\n      usage: descriptor.usage.into(),\n      view_formats: descriptor\n        .view_formats\n        .into_iter()\n        .map(Into::into)\n        .collect(),\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .device_create_texture(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    Ok(GPUTexture {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      id,\n      default_view_id: Default::default(),\n      label: descriptor.label,\n      size: wgpu_descriptor.size,\n      mip_level_count: wgpu_descriptor.mip_level_count,\n      sample_count: wgpu_descriptor.sample_count,\n      dimension: descriptor.dimension,\n      format: descriptor.format,\n      usage: descriptor.usage,\n    })\n  }\n\n  #[cppgc]\n  fn create_sampler(\n    &self,\n    #[webidl] descriptor: super::sampler::GPUSamplerDescriptor,\n  ) -> Result<GPUSampler, JsErrorBox> {\n    let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      address_modes: [\n        descriptor.address_mode_u.into(),\n        descriptor.address_mode_v.into(),\n        descriptor.address_mode_w.into(),\n      ],\n      mag_filter: descriptor.mag_filter.into(),\n      min_filter: descriptor.min_filter.into(),\n      mipmap_filter: descriptor.mipmap_filter.into(),\n      lod_min_clamp: descriptor.lod_min_clamp,\n      lod_max_clamp: descriptor.lod_max_clamp,\n      compare: descriptor.compare.map(Into::into),\n      anisotropy_clamp: descriptor.max_anisotropy,\n      border_color: None,\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .device_create_sampler(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    Ok(GPUSampler {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    })\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_bind_group_layout(\n    &self,\n    #[webidl]\n    descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor,\n  ) -> Result<GPUBindGroupLayout, JsErrorBox> {\n    let mut entries = Vec::with_capacity(descriptor.entries.len());\n\n    for entry in descriptor.entries {\n      let n_entries = [\n        entry.buffer.is_some(),\n        entry.sampler.is_some(),\n        entry.texture.is_some(),\n        entry.storage_texture.is_some(),\n        entry.external_texture.is_some(),\n      ]\n      .into_iter()\n      .filter(|t| *t)\n      .count();\n\n      if n_entries != 1 {\n        return Err(JsErrorBox::type_error(\n          \"Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified\",\n        ));\n      }\n\n      let ty = if let Some(buffer) = entry.buffer {\n        BindingType::Buffer {\n          ty: buffer.r#type.into(),\n          has_dynamic_offset: buffer.has_dynamic_offset,\n          min_binding_size: NonZeroU64::new(buffer.min_binding_size),\n        }\n      } else if let Some(sampler) = entry.sampler {\n        BindingType::Sampler(sampler.r#type.into())\n      } else if let Some(texture) = entry.texture {\n        BindingType::Texture {\n          sample_type: texture.sample_type.into(),\n          view_dimension: texture.view_dimension.into(),\n          multisampled: texture.multisampled,\n        }\n      } else if let Some(storage_texture) = entry.storage_texture {\n        BindingType::StorageTexture {\n          access: storage_texture.access.into(),\n          format: storage_texture.format.into(),\n          view_dimension: storage_texture.view_dimension.into(),\n        }\n      } else if entry.external_texture.is_some() {\n        BindingType::ExternalTexture\n      } else {\n        unreachable!()\n      };\n\n      entries.push(wgpu_types::BindGroupLayoutEntry {\n        binding: entry.binding,\n        visibility: entry.visibility.into(),\n        ty,\n        count: None, // native-only\n      });\n    }\n\n    let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      entries: Cow::Owned(entries),\n    };\n\n    let (id, err) = self.instance.device_create_bind_group_layout(\n      self.id,\n      &wgpu_descriptor,\n      None,\n    );\n\n    self.error_handler.push_error(err);\n\n    Ok(GPUBindGroupLayout {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    })\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_pipeline_layout(\n    &self,\n    #[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor,\n  ) -> GPUPipelineLayout {\n    let bind_group_layouts = descriptor\n      .bind_group_layouts\n      .into_iter()\n      .map(|bind_group_layout| {\n        bind_group_layout\n          .into_option()\n          .map(|bind_group_layout| bind_group_layout.id)\n      })\n      .collect();\n\n    let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      bind_group_layouts: Cow::Owned(bind_group_layouts),\n      immediate_size: 0,\n    };\n\n    let (id, err) = self.instance.device_create_pipeline_layout(\n      self.id,\n      &wgpu_descriptor,\n      None,\n    );\n\n    self.error_handler.push_error(err);\n\n    GPUPipelineLayout {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    }\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_bind_group(\n    &self,\n    #[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor,\n  ) -> GPUBindGroup {\n    let entries = descriptor\n      .entries\n      .into_iter()\n      .map(|entry| wgpu_core::binding_model::BindGroupEntry {\n        binding: entry.binding,\n        resource: match entry.resource {\n          GPUBindingResource::Sampler(sampler) => {\n            BindingResource::Sampler(sampler.id)\n          }\n          GPUBindingResource::Texture(texture) => {\n            BindingResource::TextureView(texture.default_view_id())\n          }\n          GPUBindingResource::TextureView(texture_view) => {\n            BindingResource::TextureView(texture_view.id)\n          }\n          GPUBindingResource::Buffer(buffer) => {\n            BindingResource::Buffer(wgpu_core::binding_model::BufferBinding {\n              buffer: buffer.id,\n              offset: 0,\n              size: Some(buffer.size),\n            })\n          }\n          GPUBindingResource::BufferBinding(buffer_binding) => {\n            BindingResource::Buffer(wgpu_core::binding_model::BufferBinding {\n              buffer: buffer_binding.buffer.id,\n              offset: buffer_binding.offset,\n              size: buffer_binding.size,\n            })\n          }\n          GPUBindingResource::ExternalTexture(external_texture) => {\n            BindingResource::ExternalTexture(external_texture.id)\n          }\n        },\n      })\n      .collect::<Vec<_>>();\n\n    let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      layout: descriptor.layout.id,\n      entries: Cow::Owned(entries),\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .device_create_bind_group(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    GPUBindGroup {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    }\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_shader_module(\n    &self,\n    scope: &mut v8::HandleScope<'_>,\n    #[webidl] descriptor: super::shader::GPUShaderModuleDescriptor,\n  ) -> GPUShaderModule {\n    let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      runtime_checks: wgpu_types::ShaderRuntimeChecks::default(),\n    };\n\n    let (id, err) = self.instance.device_create_shader_module(\n      self.id,\n      &wgpu_descriptor,\n      wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(\n        &descriptor.code,\n      )),\n      None,\n    );\n\n    let compilation_info =\n      GPUCompilationInfo::new(scope, err.iter(), &descriptor.code);\n    let compilation_info = make_cppgc_object(scope, compilation_info);\n    let compilation_info = v8::Global::new(scope, compilation_info);\n    self.error_handler.push_error(err);\n\n    GPUShaderModule {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n      compilation_info,\n    }\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_compute_pipeline(\n    &self,\n    #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,\n  ) -> GPUComputePipeline {\n    let (pipeline, err) = self.new_compute_pipeline(descriptor);\n    self.error_handler.push_error(err);\n    pipeline\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_render_pipeline(\n    &self,\n    #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,\n  ) -> GPURenderPipeline {\n    let (pipeline, err) = self.new_render_pipeline(descriptor);\n    self.error_handler.push_error(err);\n    pipeline\n  }\n\n  #[async_method(fake)]\n  #[required(1)]\n  #[cppgc]\n  #[global]\n  fn create_compute_pipeline_async(\n    &self,\n    scope: &mut v8::HandleScope,\n    #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,\n  ) -> v8::Global<v8::Promise> {\n    let resolver = v8::PromiseResolver::new(scope).unwrap();\n    let promise = resolver.get_promise(scope);\n\n    let (pipeline, err) = self.new_compute_pipeline(descriptor);\n    if let Some(err) = err {\n      let err = make_pipeline_error(\n        scope,\n        GPUPipelineErrorReason::Validation,\n        &fmt_err(&err),\n      );\n      resolver.reject(scope, err.into());\n    } else {\n      let val = make_cppgc_object(scope, pipeline).into();\n      resolver.resolve(scope, val);\n    }\n    v8::Global::new(scope, promise)\n  }\n\n  #[async_method(fake)]\n  #[required(1)]\n  #[cppgc]\n  #[global]\n  fn create_render_pipeline_async(\n    &self,\n    scope: &mut v8::HandleScope,\n    #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,\n  ) -> v8::Global<v8::Promise> {\n    let resolver = v8::PromiseResolver::new(scope).unwrap();\n    let promise = resolver.get_promise(scope);\n\n    let (pipeline, err) = self.new_render_pipeline(descriptor);\n    if let Some(err) = err {\n      let err = make_pipeline_error(\n        scope,\n        GPUPipelineErrorReason::Validation,\n        &fmt_err(&err),\n      );\n      resolver.reject(scope, err.into());\n    } else {\n      let val = make_cppgc_object(scope, pipeline).into();\n      resolver.resolve(scope, val);\n    }\n    v8::Global::new(scope, promise)\n  }\n\n  fn create_command_encoder<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n    #[webidl] descriptor: Option<\n      super::command_encoder::GPUCommandEncoderDescriptor,\n    >,\n  ) -> v8::Local<'a, v8::Object> {\n    // Metal imposes a limit on the number of outstanding command buffers.\n    // Attempting to create another command buffer after reaching that limit\n    // will block, which can result in a deadlock if GC is required to\n    // recover old command buffers. To encourage V8 to garbage collect\n    // command buffers before that happens, we associate some external\n    // memory with each command buffer.\n    #[cfg(target_vendor = \"apple\")]\n    const EXTERNAL_MEMORY_AMOUNT: i64 = 1 << 16;\n\n    let label = descriptor.map(|d| d.label).unwrap_or_default();\n    let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor {\n      label: Some(Cow::Owned(label.clone())),\n    };\n\n    #[cfg(target_vendor = \"apple\")]\n    scope.adjust_amount_of_external_allocated_memory(EXTERNAL_MEMORY_AMOUNT);\n\n    let (id, err) = self.instance.device_create_command_encoder(\n      self.id,\n      &wgpu_descriptor,\n      None,\n    );\n\n    self.error_handler.push_error(err);\n\n    let encoder = GPUCommandEncoder {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      id,\n      label,\n      #[cfg(target_vendor = \"apple\")]\n      weak: std::sync::OnceLock::new(),\n    };\n\n    let obj = make_cppgc_object(scope, encoder);\n\n    #[cfg(target_vendor = \"apple\")]\n    {\n      let finalizer = v8::Weak::with_finalizer(\n        scope,\n        obj,\n        Box::new(|isolate: &mut v8::Isolate| {\n          isolate.adjust_amount_of_external_allocated_memory(\n            -EXTERNAL_MEMORY_AMOUNT,\n          );\n        }),\n      );\n      deno_core::cppgc::try_unwrap_cppgc_object::<GPUCommandEncoder>(\n        scope,\n        obj.into(),\n      )\n      .unwrap()\n      .weak\n      .set(finalizer)\n      .unwrap();\n    }\n\n    obj\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_render_bundle_encoder(\n    &self,\n    #[webidl]\n    descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor,\n  ) -> GPURenderBundleEncoder {\n    let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      color_formats: Cow::Owned(\n        descriptor\n          .color_formats\n          .into_iter()\n          .map(|format| format.into_option().map(Into::into))\n          .collect::<Vec<_>>(),\n      ),\n      depth_stencil: descriptor.depth_stencil_format.map(|format| {\n        wgpu_types::RenderBundleDepthStencil {\n          format: format.into(),\n          depth_read_only: descriptor.depth_read_only,\n          stencil_read_only: descriptor.stencil_read_only,\n        }\n      }),\n      sample_count: descriptor.sample_count,\n      multiview: None,\n    };\n\n    let res =\n      wgpu_core::command::RenderBundleEncoder::new(&wgpu_descriptor, self.id);\n    let (encoder, err) = match res {\n      Ok(encoder) => (encoder, None),\n      Err(e) => (\n        wgpu_core::command::RenderBundleEncoder::dummy(self.id),\n        Some(e),\n      ),\n    };\n\n    self.error_handler.push_error(err);\n\n    GPURenderBundleEncoder {\n      instance: self.instance.clone(),\n      error_handler: self.error_handler.clone(),\n      encoder: RefCell::new(Some(encoder)),\n      label: descriptor.label,\n    }\n  }\n\n  #[required(1)]\n  #[cppgc]\n  fn create_query_set(\n    &self,\n    #[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor,\n  ) -> GPUQuerySet {\n    let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      ty: descriptor.r#type.clone().into(),\n      count: descriptor.count,\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .device_create_query_set(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    GPUQuerySet {\n      instance: self.instance.clone(),\n      id,\n      r#type: descriptor.r#type,\n      count: descriptor.count,\n      label: descriptor.label,\n    }\n  }\n\n  #[getter]\n  #[global]\n  fn lost(&self) -> v8::Global<v8::Promise> {\n    self.lost_promise.clone()\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) {\n    self\n      .error_handler\n      .scopes\n      .lock()\n      .unwrap()\n      .push((filter, None));\n  }\n\n  #[async_method(fake)]\n  #[global]\n  fn pop_error_scope(\n    &self,\n    scope: &mut v8::HandleScope,\n  ) -> Result<v8::Global<v8::Value>, JsErrorBox> {\n    if self.error_handler.is_lost.get().is_some() {\n      let val = v8::null(scope).cast::<v8::Value>();\n      return Ok(v8::Global::new(scope, val));\n    }\n\n    let Some((_, error)) = self.error_handler.scopes.lock().unwrap().pop()\n    else {\n      return Err(JsErrorBox::new(\n        \"DOMExceptionOperationError\",\n        \"There are no error scopes on the error scope stack\",\n      ));\n    };\n\n    let val = if let Some(err) = error {\n      deno_core::error::to_v8_error(scope, &err)\n    } else {\n      v8::null(scope).into()\n    };\n\n    Ok(v8::Global::new(scope, val))\n  }\n\n  #[fast]\n  fn start_capture(&self) {\n    unsafe {\n      self\n        .instance\n        .device_start_graphics_debugger_capture(self.id)\n    };\n  }\n  #[fast]\n  fn stop_capture(&self) {\n    self\n      .instance\n      .device_poll(self.id, wgpu_types::PollType::wait_indefinitely())\n      .unwrap();\n    unsafe { self.instance.device_stop_graphics_debugger_capture(self.id) };\n  }\n}\n\nimpl GPUDevice {\n  fn new_compute_pipeline(\n    &self,\n    descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,\n  ) -> (\n    GPUComputePipeline,\n    Option<wgpu_core::pipeline::CreateComputePipelineError>,\n  ) {\n    let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      layout: descriptor.layout.into(),\n      stage: ProgrammableStageDescriptor {\n        module: descriptor.compute.module.id,\n        entry_point: descriptor.compute.entry_point.map(Into::into),\n        constants: descriptor.compute.constants.into_iter().collect(),\n        zero_initialize_workgroup_memory: true,\n      },\n      cache: None,\n    };\n\n    let (id, err) = self.instance.device_create_compute_pipeline(\n      self.id,\n      &wgpu_descriptor,\n      None,\n    );\n\n    (\n      GPUComputePipeline {\n        instance: self.instance.clone(),\n        error_handler: self.error_handler.clone(),\n        id,\n        label: descriptor.label.clone(),\n      },\n      err,\n    )\n  }\n\n  fn new_render_pipeline(\n    &self,\n    descriptor: super::render_pipeline::GPURenderPipelineDescriptor,\n  ) -> (\n    GPURenderPipeline,\n    Option<wgpu_core::pipeline::CreateRenderPipelineError>,\n  ) {\n    let vertex = wgpu_core::pipeline::VertexState {\n      stage: ProgrammableStageDescriptor {\n        module: descriptor.vertex.module.id,\n        entry_point: descriptor.vertex.entry_point.map(Into::into),\n        constants: descriptor.vertex.constants.into_iter().collect(),\n        zero_initialize_workgroup_memory: true,\n      },\n      buffers: Cow::Owned(\n        descriptor\n          .vertex\n          .buffers\n          .into_iter()\n          .map(|b| {\n            b.into_option().map_or_else(\n              wgpu_core::pipeline::VertexBufferLayout::default,\n              |layout| wgpu_core::pipeline::VertexBufferLayout {\n                array_stride: layout.array_stride,\n                step_mode: layout.step_mode.into(),\n                attributes: Cow::Owned(\n                  layout\n                    .attributes\n                    .into_iter()\n                    .map(|attr| wgpu_types::VertexAttribute {\n                      format: attr.format.into(),\n                      offset: attr.offset,\n                      shader_location: attr.shader_location,\n                    })\n                    .collect(),\n                ),\n              },\n            )\n          })\n          .collect(),\n      ),\n    };\n\n    let primitive = wgpu_types::PrimitiveState {\n      topology: descriptor.primitive.topology.into(),\n      strip_index_format: descriptor\n        .primitive\n        .strip_index_format\n        .map(Into::into),\n      front_face: descriptor.primitive.front_face.into(),\n      cull_mode: descriptor.primitive.cull_mode.into(),\n      unclipped_depth: descriptor.primitive.unclipped_depth,\n      polygon_mode: Default::default(),\n      conservative: false,\n    };\n\n    let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| {\n      let front = wgpu_types::StencilFaceState {\n        compare: depth_stencil.stencil_front.compare.into(),\n        fail_op: depth_stencil.stencil_front.fail_op.into(),\n        depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(),\n        pass_op: depth_stencil.stencil_front.pass_op.into(),\n      };\n      let back = wgpu_types::StencilFaceState {\n        compare: depth_stencil.stencil_back.compare.into(),\n        fail_op: depth_stencil.stencil_back.fail_op.into(),\n        depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(),\n        pass_op: depth_stencil.stencil_back.pass_op.into(),\n      };\n\n      wgpu_types::DepthStencilState {\n        format: depth_stencil.format.into(),\n        depth_write_enabled: depth_stencil.depth_write_enabled,\n        depth_compare: depth_stencil.depth_compare.map(Into::into),\n        stencil: wgpu_types::StencilState {\n          front,\n          back,\n          read_mask: depth_stencil.stencil_read_mask,\n          write_mask: depth_stencil.stencil_write_mask,\n        },\n        bias: wgpu_types::DepthBiasState {\n          constant: depth_stencil.depth_bias,\n          slope_scale: depth_stencil.depth_bias_slope_scale,\n          clamp: depth_stencil.depth_bias_clamp,\n        },\n      }\n    });\n\n    let multisample = wgpu_types::MultisampleState {\n      count: descriptor.multisample.count,\n      mask: descriptor.multisample.mask as u64,\n      alpha_to_coverage_enabled: descriptor\n        .multisample\n        .alpha_to_coverage_enabled,\n    };\n\n    let fragment =\n      descriptor\n        .fragment\n        .map(|fragment| wgpu_core::pipeline::FragmentState {\n          stage: ProgrammableStageDescriptor {\n            module: fragment.module.id,\n            entry_point: fragment.entry_point.map(Into::into),\n            constants: fragment.constants.into_iter().collect(),\n            zero_initialize_workgroup_memory: true,\n          },\n          targets: Cow::Owned(\n            fragment\n              .targets\n              .into_iter()\n              .map(|target| {\n                target.into_option().map(|target| {\n                  wgpu_types::ColorTargetState {\n                    format: target.format.into(),\n                    blend: target.blend.map(|blend| wgpu_types::BlendState {\n                      color: wgpu_types::BlendComponent {\n                        src_factor: blend.color.src_factor.into(),\n                        dst_factor: blend.color.dst_factor.into(),\n                        operation: blend.color.operation.into(),\n                      },\n                      alpha: wgpu_types::BlendComponent {\n                        src_factor: blend.alpha.src_factor.into(),\n                        dst_factor: blend.alpha.dst_factor.into(),\n                        operation: blend.alpha.operation.into(),\n                      },\n                    }),\n                    write_mask: target.write_mask.into(),\n                  }\n                })\n              })\n              .collect(),\n          ),\n        });\n\n    let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      layout: descriptor.layout.into(),\n      vertex,\n      primitive,\n      depth_stencil,\n      multisample,\n      fragment,\n      cache: None,\n      multiview_mask: None,\n    };\n\n    let (id, err) = self.instance.device_create_render_pipeline(\n      self.id,\n      &wgpu_descriptor,\n      None,\n    );\n\n    (\n      GPURenderPipeline {\n        instance: self.instance.clone(),\n        error_handler: self.error_handler.clone(),\n        id,\n        label: descriptor.label,\n      },\n      err,\n    )\n  }\n}\n\n#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]\npub enum GPUDeviceLostReason {\n  #[default]\n  Unknown,\n  Destroyed,\n}\n\nimpl From<wgpu_types::DeviceLostReason> for GPUDeviceLostReason {\n  fn from(value: wgpu_types::DeviceLostReason) -> Self {\n    match value {\n      wgpu_types::DeviceLostReason::Unknown => Self::Unknown,\n      wgpu_types::DeviceLostReason::Destroyed => Self::Destroyed,\n    }\n  }\n}\n\n#[derive(Default)]\npub struct GPUDeviceLostInfo {\n  pub reason: GPUDeviceLostReason,\n}\n\nimpl GarbageCollected for GPUDeviceLostInfo {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUDeviceLostInfo\"\n  }\n}\n\n#[op2]\nimpl GPUDeviceLostInfo {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUDeviceLostInfo, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn reason(&self) -> &'static str {\n    use GPUDeviceLostReason::*;\n    match self.reason {\n      Unknown => \"unknown\",\n      Destroyed => \"destroyed\",\n    }\n  }\n\n  #[getter]\n  #[string]\n  fn message(&self) -> &'static str {\n    \"device was lost\"\n  }\n}\n\n#[op2(fast)]\npub fn op_webgpu_device_start_capture(#[cppgc] device: &GPUDevice) {\n  unsafe {\n    device\n      .instance\n      .device_start_graphics_debugger_capture(device.id);\n  }\n}\n\n#[op2(fast)]\npub fn op_webgpu_device_stop_capture(#[cppgc] device: &GPUDevice) {\n  unsafe {\n    device\n      .instance\n      .device_stop_graphics_debugger_capture(device.id);\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/error.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::fmt::Display;\nuse std::fmt::Formatter;\nuse std::sync::Mutex;\nuse std::sync::OnceLock;\n\nuse deno_core::cppgc::make_cppgc_object;\nuse deno_core::v8;\n\nuse deno_core::JsRuntime;\nuse deno_core::V8TaskSpawner;\nuse wgpu_core::binding_model::CreateBindGroupError;\nuse wgpu_core::binding_model::CreateBindGroupLayoutError;\nuse wgpu_core::binding_model::CreatePipelineLayoutError;\nuse wgpu_core::binding_model::GetBindGroupLayoutError;\nuse wgpu_core::command::ClearError;\nuse wgpu_core::command::CommandEncoderError;\nuse wgpu_core::command::ComputePassError;\nuse wgpu_core::command::CreateRenderBundleError;\nuse wgpu_core::command::EncoderStateError;\nuse wgpu_core::command::PassStateError;\nuse wgpu_core::command::QueryError;\nuse wgpu_core::command::RenderBundleError;\nuse wgpu_core::command::RenderPassError;\nuse wgpu_core::device::queue::QueueSubmitError;\nuse wgpu_core::device::queue::QueueWriteError;\nuse wgpu_core::device::DeviceError;\nuse wgpu_core::pipeline::CreateComputePipelineError;\nuse wgpu_core::pipeline::CreateRenderPipelineError;\nuse wgpu_core::pipeline::CreateShaderModuleError;\nuse wgpu_core::present::ConfigureSurfaceError;\nuse wgpu_core::resource::BufferAccessError;\nuse wgpu_core::resource::CreateBufferError;\nuse wgpu_core::resource::CreateQuerySetError;\nuse wgpu_core::resource::CreateSamplerError;\nuse wgpu_core::resource::CreateTextureError;\nuse wgpu_core::resource::CreateTextureViewError;\nuse wgpu_types::error::{ErrorType, WebGpuError};\n\nuse crate::device::GPUDeviceLostInfo;\nuse crate::device::GPUDeviceLostReason;\n\npub type ErrorHandler = std::rc::Rc<DeviceErrorHandler>;\n\npub struct DeviceErrorHandler {\n  pub is_lost: OnceLock<()>,\n  pub scopes: Mutex<Vec<(GPUErrorFilter, Option<GPUError>)>>,\n  lost_resolver: Mutex<Option<v8::Global<v8::PromiseResolver>>>,\n  spawner: V8TaskSpawner,\n\n  // The error handler is constructed before the device. A weak\n  // reference to the device is placed here with `set_device`\n  // after the device is constructed.\n  device: OnceLock<v8::Weak<v8::Object>>,\n}\n\nimpl DeviceErrorHandler {\n  pub fn new(\n    lost_resolver: v8::Global<v8::PromiseResolver>,\n    spawner: V8TaskSpawner,\n  ) -> Self {\n    Self {\n      is_lost: Default::default(),\n      scopes: Mutex::new(vec![]),\n      lost_resolver: Mutex::new(Some(lost_resolver)),\n      device: OnceLock::new(),\n      spawner,\n    }\n  }\n\n  pub fn set_device(&self, device: v8::Weak<v8::Object>) {\n    self.device.set(device).unwrap()\n  }\n\n  pub fn push_error<E: Into<GPUError>>(&self, err: Option<E>) {\n    let Some(err) = err else {\n      return;\n    };\n\n    if self.is_lost.get().is_some() {\n      return;\n    }\n\n    let err = err.into();\n\n    if let GPUError::Lost(reason) = err {\n      let _ = self.is_lost.set(());\n      if let Some(resolver) = self.lost_resolver.lock().unwrap().take() {\n        self.spawner.spawn(move |scope| {\n          let resolver = v8::Local::new(scope, resolver);\n          let info = make_cppgc_object(scope, GPUDeviceLostInfo { reason });\n          let info = v8::Local::new(scope, info);\n          resolver.resolve(scope, info.into());\n        });\n      }\n\n      return;\n    }\n\n    let error_filter = match err {\n      GPUError::Lost(_) => unreachable!(),\n      GPUError::Validation(_) => GPUErrorFilter::Validation,\n      GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory,\n      GPUError::Internal => GPUErrorFilter::Internal,\n    };\n\n    let mut scopes = self.scopes.lock().unwrap();\n    let scope = scopes\n      .iter_mut()\n      .rfind(|(filter, _)| filter == &error_filter);\n\n    if let Some(scope) = scope {\n      // Only saving the first error in the scope as it's likely the culprit.\n      if scope.1.is_none() {\n        scope.1 = Some(err);\n      }\n    } else {\n      let device = self\n        .device\n        .get()\n        .expect(\"set_device was not called\")\n        .clone();\n      self.spawner.spawn(move |scope| {\n        let state = JsRuntime::op_state_from(&*scope);\n        let Some(device) = device.to_local(scope) else {\n          // The device has already gone away, so we don't have\n          // anywhere to report the error.\n          return;\n        };\n        let key = v8::String::new(scope, \"dispatchEvent\").unwrap();\n        let val = device.get(scope, key.into()).unwrap();\n        let func =\n          v8::Global::new(scope, val.try_cast::<v8::Function>().unwrap());\n        let device = v8::Global::new(scope, device.cast::<v8::Value>());\n        let error_event_class =\n          state.borrow().borrow::<crate::ErrorEventClass>().0.clone();\n\n        let error = deno_core::error::to_v8_error(scope, &err);\n\n        let error_event_class =\n          v8::Local::new(scope, error_event_class.clone());\n        let constructor =\n          v8::Local::<v8::Function>::try_from(error_event_class).unwrap();\n        let kind = v8::String::new(scope, \"uncapturederror\").unwrap();\n\n        let obj = v8::Object::new(scope);\n        let key = v8::String::new(scope, \"error\").unwrap();\n        obj.set(scope, key.into(), error);\n\n        let event = constructor\n          .new_instance(scope, &[kind.into(), obj.into()])\n          .unwrap();\n\n        let recv = v8::Local::new(scope, device);\n        func.open(scope).call(scope, recv, &[event.into()]);\n      });\n    }\n  }\n}\n\n#[derive(deno_core::WebIDL, Eq, PartialEq)]\n#[webidl(enum)]\npub enum GPUErrorFilter {\n  Validation,\n  OutOfMemory,\n  Internal,\n}\n\n#[derive(Debug, deno_error::JsError)]\npub enum GPUError {\n  // TODO(@crowlKats): consider adding an unreachable value that uses unreachable!()\n  #[class(\"UNREACHABLE\")]\n  Lost(GPUDeviceLostReason),\n  #[class(\"GPUValidationError\")]\n  Validation(String),\n  #[class(\"GPUOutOfMemoryError\")]\n  OutOfMemory,\n  #[class(\"GPUInternalError\")]\n  Internal,\n}\n\nimpl Display for GPUError {\n  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n    match self {\n      GPUError::Lost(_) => Ok(()),\n      GPUError::Validation(s) => f.write_str(s),\n      GPUError::OutOfMemory => f.write_str(\"not enough memory left\"),\n      GPUError::Internal => Ok(()),\n    }\n  }\n}\n\nimpl std::error::Error for GPUError {}\n\nimpl GPUError {\n  fn from_webgpu(e: impl WebGpuError) -> Self {\n    match e.webgpu_error_type() {\n      ErrorType::Internal => GPUError::Internal,\n      ErrorType::DeviceLost => GPUError::Lost(GPUDeviceLostReason::Unknown), // TODO: this variant should be ignored, register the lost callback instead.\n      ErrorType::OutOfMemory => GPUError::OutOfMemory,\n      ErrorType::Validation => GPUError::Validation(fmt_err(&e)),\n    }\n  }\n}\n\npub(crate) fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String {\n  let mut output = err.to_string();\n\n  let mut e = err.source();\n  while let Some(source) = e {\n    output.push_str(&format!(\": {source}\"));\n    e = source.source();\n  }\n\n  if output.is_empty() {\n    output.push_str(\"validation error\");\n  }\n\n  output\n}\n\nimpl From<EncoderStateError> for GPUError {\n  fn from(err: EncoderStateError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<PassStateError> for GPUError {\n  fn from(err: PassStateError) -> Self {\n    GPUError::Validation(fmt_err(&err))\n  }\n}\n\nimpl From<CreateBufferError> for GPUError {\n  fn from(err: CreateBufferError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<DeviceError> for GPUError {\n  fn from(err: DeviceError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<BufferAccessError> for GPUError {\n  fn from(err: BufferAccessError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateBindGroupLayoutError> for GPUError {\n  fn from(err: CreateBindGroupLayoutError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreatePipelineLayoutError> for GPUError {\n  fn from(err: CreatePipelineLayoutError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateBindGroupError> for GPUError {\n  fn from(err: CreateBindGroupError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<RenderBundleError> for GPUError {\n  fn from(err: RenderBundleError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateRenderBundleError> for GPUError {\n  fn from(err: CreateRenderBundleError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CommandEncoderError> for GPUError {\n  fn from(err: CommandEncoderError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<QueryError> for GPUError {\n  fn from(err: QueryError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<ComputePassError> for GPUError {\n  fn from(err: ComputePassError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateComputePipelineError> for GPUError {\n  fn from(err: CreateComputePipelineError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<GetBindGroupLayoutError> for GPUError {\n  fn from(err: GetBindGroupLayoutError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateRenderPipelineError> for GPUError {\n  fn from(err: CreateRenderPipelineError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<RenderPassError> for GPUError {\n  fn from(err: RenderPassError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateSamplerError> for GPUError {\n  fn from(err: CreateSamplerError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateShaderModuleError> for GPUError {\n  fn from(err: CreateShaderModuleError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateTextureError> for GPUError {\n  fn from(err: CreateTextureError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateTextureViewError> for GPUError {\n  fn from(err: CreateTextureViewError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<CreateQuerySetError> for GPUError {\n  fn from(err: CreateQuerySetError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<QueueSubmitError> for GPUError {\n  fn from(err: QueueSubmitError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<QueueWriteError> for GPUError {\n  fn from(err: QueueWriteError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<ClearError> for GPUError {\n  fn from(err: ClearError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\nimpl From<ConfigureSurfaceError> for GPUError {\n  fn from(err: ConfigureSurfaceError) -> Self {\n    GPUError::from_webgpu(err)\n  }\n}\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\npub enum GPUGenericError {\n  #[class(type)]\n  #[error(\"Illegal constructor\")]\n  InvalidConstructor,\n}\n\npub enum GPUPipelineErrorReason {\n  Validation,\n  #[expect(dead_code)]\n  Internal,\n}\n\nimpl Display for GPUPipelineErrorReason {\n  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n    match self {\n      Self::Validation => f.write_str(\"validation\"),\n      Self::Internal => f.write_str(\"internal\"),\n    }\n  }\n}\n\npub(crate) fn make_pipeline_error<'a>(\n  scope: &mut v8::HandleScope<'a>,\n  reason: GPUPipelineErrorReason,\n  message: &str,\n) -> v8::Local<'a, v8::Object> {\n  let state = JsRuntime::op_state_from(scope);\n  let class = state\n    .borrow()\n    .borrow::<crate::PipelineErrorClass>()\n    .0\n    .clone();\n  let constructor =\n    v8::Local::<v8::Function>::try_from(v8::Local::new(scope, class)).unwrap();\n  let message_str = v8::String::new(scope, message).unwrap();\n  let reason_str = v8::String::new(scope, &reason.to_string()).unwrap();\n\n  let options = v8::Object::new(scope);\n  let key = v8::String::new(scope, \"reason\").unwrap();\n  options.set(scope, key.into(), reason_str.into());\n\n  constructor\n    .new_instance(scope, &[message_str.into(), options.into()])\n    .unwrap()\n}\n"
  },
  {
    "path": "deno_webgpu/lib.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n#![cfg(not(target_arch = \"wasm32\"))]\n#![warn(unsafe_op_in_unsafe_fn)]\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse std::sync::Arc;\n\nuse deno_core::cppgc::SameObject;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::GarbageCollected;\nuse deno_core::OpState;\nuse serde::de::IntoDeserializer;\nuse serde::Deserialize as _;\npub use wgpu_core;\npub use wgpu_types;\nuse wgpu_types::PowerPreference;\n\nuse crate::error::GPUGenericError;\n\nmod adapter;\nmod bind_group;\nmod bind_group_layout;\nmod buffer;\nmod byow;\nmod command_buffer;\nmod command_encoder;\nmod compute_pass;\nmod compute_pipeline;\nmod device;\nmod error;\nmod pipeline_layout;\nmod query_set;\nmod queue;\nmod render_bundle;\nmod render_pass;\nmod render_pipeline;\nmod sampler;\nmod shader;\nmod surface;\nmod texture;\nmod webidl;\n\npub const UNSTABLE_FEATURE_NAME: &str = \"webgpu\";\n\npub const DX12_COMPILER_ENV_VAR: &str = \"DENO_WEBGPU_DX12_COMPILER\";\n\n#[allow(clippy::print_stdout)]\npub fn print_linker_flags(name: &str) {\n  if cfg!(windows) {\n    // these dls load slowly, so delay loading them\n    let dlls = [\n      // webgpu\n      \"d3dcompiler_47\",\n      \"OPENGL32\",\n      // network related functions\n      \"iphlpapi\",\n    ];\n    for dll in dlls {\n      println!(\"cargo:rustc-link-arg-bin={name}=/delayload:{dll}.dll\");\n    }\n    // enable delay loading\n    println!(\"cargo:rustc-link-arg-bin={name}=delayimp.lib\");\n  }\n}\n\npub type Instance = Arc<wgpu_core::global::Global>;\n\ndeno_core::extension!(\n  deno_webgpu,\n  deps = [deno_webidl, deno_web],\n  ops = [\n    op_create_gpu,\n    device::op_webgpu_device_start_capture,\n    device::op_webgpu_device_stop_capture,\n  ],\n  objects = [\n    GPU,\n    WGSLLanguageFeatures,\n    adapter::GPUAdapter,\n    adapter::GPUAdapterInfo,\n    bind_group::GPUBindGroup,\n    bind_group_layout::GPUBindGroupLayout,\n    buffer::GPUBuffer,\n    command_buffer::GPUCommandBuffer,\n    command_encoder::GPUCommandEncoder,\n    compute_pass::GPUComputePassEncoder,\n    compute_pipeline::GPUComputePipeline,\n    device::GPUDevice,\n    device::GPUDeviceLostInfo,\n    pipeline_layout::GPUPipelineLayout,\n    query_set::GPUQuerySet,\n    queue::GPUQueue,\n    render_bundle::GPURenderBundle,\n    render_bundle::GPURenderBundleEncoder,\n    render_pass::GPURenderPassEncoder,\n    render_pipeline::GPURenderPipeline,\n    sampler::GPUSampler,\n    shader::GPUCompilationInfo,\n    shader::GPUCompilationMessage,\n    shader::GPUShaderModule,\n    adapter::GPUSupportedFeatures,\n    adapter::GPUSupportedLimits,\n    texture::GPUTexture,\n    texture::GPUTextureView,\n    texture::GPUExternalTexture,\n    byow::UnsafeWindowSurface,\n    surface::GPUCanvasContext,\n  ],\n  esm = [\"00_init.js\", \"02_surface.js\"],\n  lazy_loaded_esm = [\"01_webgpu.js\"],\n);\n\n#[op2]\n#[cppgc]\npub fn op_create_gpu(\n  state: &mut OpState,\n  scope: &mut v8::HandleScope,\n  webidl_brand: v8::Local<v8::Value>,\n  set_event_target_data: v8::Local<v8::Value>,\n  uncaptured_error_event_class: v8::Local<v8::Value>,\n  pipeline_error_class: v8::Local<v8::Value>,\n) -> GPU {\n  state.put(EventTargetSetup {\n    brand: v8::Global::new(scope, webidl_brand),\n    set_event_target_data: v8::Global::new(scope, set_event_target_data),\n  });\n  state.put(ErrorEventClass(v8::Global::new(\n    scope,\n    uncaptured_error_event_class,\n  )));\n  state.put(PipelineErrorClass(v8::Global::new(\n    scope,\n    pipeline_error_class,\n  )));\n  GPU {\n    wgsl_language_features: SameObject::new(),\n  }\n}\n\nstruct EventTargetSetup {\n  brand: v8::Global<v8::Value>,\n  set_event_target_data: v8::Global<v8::Value>,\n}\nstruct ErrorEventClass(v8::Global<v8::Value>);\nstruct PipelineErrorClass(v8::Global<v8::Value>);\n\npub struct GPU {\n  pub wgsl_language_features: SameObject<WGSLLanguageFeatures>,\n}\n\nimpl GarbageCollected for GPU {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPU\"\n  }\n}\n\n#[op2]\nimpl GPU {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPU, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[async_method]\n  #[cppgc]\n  async fn request_adapter(\n    &self,\n    state: Rc<RefCell<OpState>>,\n    #[webidl] options: adapter::GPURequestAdapterOptions,\n  ) -> Option<adapter::GPUAdapter> {\n    let mut state = state.borrow_mut();\n\n    let dx12_compiler = std::env::var(DX12_COMPILER_ENV_VAR)\n      .ok()\n      .and_then(|s| s.parse().ok());\n    let backends = std::env::var(\"DENO_WEBGPU_BACKEND\").map_or_else(\n      |_| wgpu_types::Backends::all(),\n      |s| wgpu_types::Backends::from_comma_list(&s),\n    );\n    let instance = if let Some(instance) = state.try_borrow::<Instance>() {\n      instance\n    } else {\n      state.put(Arc::new(wgpu_core::global::Global::new(\n        \"webgpu\",\n        wgpu_types::InstanceDescriptor {\n          backends,\n          flags: wgpu_types::InstanceFlags::from_build_config(),\n          memory_budget_thresholds: wgpu_types::MemoryBudgetThresholds {\n            for_resource_creation: Some(97),\n            for_device_loss: Some(99),\n          },\n          backend_options: wgpu_types::BackendOptions {\n            dx12: wgpu_types::Dx12BackendOptions {\n              shader_compiler: dx12_compiler\n                .unwrap_or(wgpu_types::Dx12Compiler::Fxc),\n              ..Default::default()\n            },\n            gl: wgpu_types::GlBackendOptions::default(),\n            noop: wgpu_types::NoopBackendOptions::default(),\n          },\n          display: None,\n        },\n        None,\n      )));\n      state.borrow::<Instance>()\n    };\n\n    // Check that the feature level string is valid.\n    // `wgpu` does not support compatibility-level adapters. As permitted\n    // by the spec, we always return a core-level adapter.\n    wgpu_types::FeatureLevel::deserialize(IntoDeserializer::<\n      serde::de::value::Error,\n    >::into_deserializer(\n      options.feature_level.as_str()\n    ))\n    .ok()?;\n\n    let descriptor = wgpu_core::instance::RequestAdapterOptions {\n      power_preference: options\n        .power_preference\n        .map(|pp| match pp {\n          adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower,\n          adapter::GPUPowerPreference::HighPerformance => {\n            PowerPreference::HighPerformance\n          }\n        })\n        .unwrap_or_default(),\n      force_fallback_adapter: options.force_fallback_adapter,\n      compatible_surface: None, // windowless\n    };\n    let id = instance.request_adapter(&descriptor, backends, None).ok()?;\n\n    Some(adapter::GPUAdapter {\n      instance: instance.clone(),\n      features: SameObject::new(),\n      limits: SameObject::new(),\n      info: Rc::new(SameObject::new()),\n      id,\n    })\n  }\n\n  #[string]\n  fn getPreferredCanvasFormat(&self) -> &'static str {\n    // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47\n    if cfg!(target_os = \"android\") {\n      texture::GPUTextureFormat::Rgba8unorm.as_str()\n    } else {\n      texture::GPUTextureFormat::Bgra8unorm.as_str()\n    }\n  }\n\n  #[getter]\n  #[global]\n  fn wgslLanguageFeatures(\n    &self,\n    scope: &mut v8::HandleScope,\n  ) -> v8::Global<v8::Object> {\n    self\n      .wgsl_language_features\n      .get(scope, WGSLLanguageFeatures::new)\n  }\n}\n\npub struct WGSLLanguageFeatures(v8::Global<v8::Value>);\n\nimpl GarbageCollected for WGSLLanguageFeatures {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"WGSLLanguageFeatures\"\n  }\n}\n\nimpl WGSLLanguageFeatures {\n  pub fn new(scope: &mut v8::HandleScope) -> Self {\n    use wgpu_core::naga::front::wgsl::ImplementedLanguageExtension;\n\n    let set = v8::Set::new(scope);\n    for ext in ImplementedLanguageExtension::all() {\n      let key = v8::String::new(scope, ext.to_ident()).unwrap();\n      set.add(scope, key.into());\n    }\n    Self(v8::Global::new(scope, <v8::Local<v8::Value>>::from(set)))\n  }\n}\n\n#[op2]\nimpl WGSLLanguageFeatures {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<WGSLLanguageFeatures, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[global]\n  #[symbol(\"setlike_set\")]\n  fn set(&self) -> v8::Global<v8::Value> {\n    self.0.clone()\n  }\n}\n\nfn transform_label<'a>(label: String) -> Option<std::borrow::Cow<'a, str>> {\n  if label.is_empty() {\n    None\n  } else {\n    Some(std::borrow::Cow::Owned(label))\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/pipeline_layout.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::webidl::Nullable;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUPipelineLayout {\n  pub instance: Instance,\n  pub id: wgpu_core::id::PipelineLayoutId,\n  pub label: String,\n}\n\nimpl Drop for GPUPipelineLayout {\n  fn drop(&mut self) {\n    self.instance.pipeline_layout_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUPipelineLayout {\n  const NAME: &'static str = \"GPUPipelineLayout\";\n}\n\nimpl GarbageCollected for GPUPipelineLayout {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUPipelineLayout\"\n  }\n}\n\n#[op2]\nimpl GPUPipelineLayout {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUPipelineLayout, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUPipelineLayoutDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub bind_group_layouts:\n    Vec<Nullable<Ptr<super::bind_group_layout::GPUBindGroupLayout>>>,\n}\n"
  },
  {
    "path": "deno_webgpu/query_set.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::op2;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUQuerySet {\n  pub instance: Instance,\n  pub id: wgpu_core::id::QuerySetId,\n  pub r#type: GPUQueryType,\n  pub count: u32,\n  pub label: String,\n}\n\nimpl Drop for GPUQuerySet {\n  fn drop(&mut self) {\n    self.instance.query_set_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUQuerySet {\n  const NAME: &'static str = \"GPUQuerySet\";\n}\n\nimpl GarbageCollected for GPUQuerySet {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUQuerySet\"\n  }\n}\n\n#[op2]\nimpl GPUQuerySet {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUQuerySet, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[fast]\n  #[undefined]\n  fn destroy(&self) -> Result<(), JsErrorBox> {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/6495): Destroy the query\n    // set. Until that is supported, it is okay to do nothing here, the\n    // query set will be garbage collected and dropped eventually.\n    Ok(())\n  }\n\n  #[getter]\n  #[string]\n  #[rename(\"type\")]\n  fn r#type(&self) -> &'static str {\n    self.r#type.as_str()\n  }\n\n  #[getter]\n  fn count(&self) -> u32 {\n    self.count\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUQuerySetDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub r#type: GPUQueryType,\n  #[options(enforce_range = true)]\n  pub count: u32,\n}\n\n#[derive(WebIDL, Clone)]\n#[webidl(enum)]\npub(crate) enum GPUQueryType {\n  Occlusion,\n  Timestamp,\n}\nimpl From<GPUQueryType> for wgpu_types::QueryType {\n  fn from(value: GPUQueryType) -> Self {\n    match value {\n      GPUQueryType::Occlusion => Self::Occlusion,\n      GPUQueryType::Timestamp => Self::Timestamp,\n    }\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/queue.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::futures::channel::oneshot;\nuse deno_core::op2;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\n\nuse crate::buffer::GPUBuffer;\nuse crate::command_buffer::GPUCommandBuffer;\nuse crate::error::GPUGenericError;\nuse crate::texture::GPUTexture;\nuse crate::texture::GPUTextureAspect;\nuse crate::webidl::GPUExtent3D;\nuse crate::webidl::GPUOrigin3D;\nuse crate::Instance;\n\npub struct GPUQueue {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub label: String,\n\n  pub id: wgpu_core::id::QueueId,\n  pub device: wgpu_core::id::DeviceId,\n}\n\nimpl Drop for GPUQueue {\n  fn drop(&mut self) {\n    self.instance.queue_drop(self.id);\n  }\n}\n\nimpl GarbageCollected for GPUQueue {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUQueue\"\n  }\n}\n\n#[op2]\nimpl GPUQueue {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUQueue, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn submit(\n    &self,\n    #[webidl] command_buffers: Vec<Ptr<GPUCommandBuffer>>,\n  ) -> Result<(), JsErrorBox> {\n    let ids = command_buffers\n      .into_iter()\n      .map(|cb| cb.id)\n      .collect::<Vec<_>>();\n\n    let err = self.instance.queue_submit(self.id, &ids).err();\n\n    if let Some((_, err)) = err {\n      self.error_handler.push_error(Some(err));\n    }\n\n    Ok(())\n  }\n\n  // In the successful case, the promise should resolve to undefined, but\n  // `#[undefined]` does not seem to work here.\n  // https://github.com/denoland/deno/issues/29603\n  #[async_method]\n  async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> {\n    let (sender, receiver) = oneshot::channel::<()>();\n\n    let callback = Box::new(move || {\n      sender.send(()).unwrap();\n    });\n\n    self\n      .instance\n      .queue_on_submitted_work_done(self.id, callback);\n\n    let done = Rc::new(RefCell::new(false));\n    let done_ = done.clone();\n    let device_poll_fut = async move {\n      while !*done.borrow() {\n        {\n          self\n            .instance\n            .device_poll(self.device, wgpu_types::PollType::wait_indefinitely())\n            .unwrap();\n        }\n        tokio::time::sleep(Duration::from_millis(10)).await;\n      }\n      Ok::<(), JsErrorBox>(())\n    };\n\n    let receiver_fut = async move {\n      receiver\n        .await\n        .map_err(|e| JsErrorBox::generic(e.to_string()))?;\n      let mut done = done_.borrow_mut();\n      *done = true;\n      Ok::<(), JsErrorBox>(())\n    };\n\n    tokio::try_join!(device_poll_fut, receiver_fut)?;\n\n    Ok(())\n  }\n\n  #[required(3)]\n  #[undefined]\n  fn write_buffer(\n    &self,\n    #[webidl] buffer: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] buffer_offset: u64,\n    #[anybuffer] buf: &[u8],\n    #[webidl(default = 0, options(enforce_range = true))] data_offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) {\n    let data = match size {\n      Some(size) => {\n        &buf[(data_offset as usize)..((data_offset + size) as usize)]\n      }\n      None => &buf[(data_offset as usize)..],\n    };\n\n    let err = self\n      .instance\n      .queue_write_buffer(self.id, buffer.id, buffer_offset, data)\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n\n  #[required(4)]\n  #[undefined]\n  fn write_texture(\n    &self,\n    #[webidl] destination: GPUTexelCopyTextureInfo,\n    #[anybuffer] buf: &[u8],\n    #[webidl] data_layout: GPUTexelCopyBufferLayout,\n    #[webidl] size: GPUExtent3D,\n  ) {\n    let destination = wgpu_types::TexelCopyTextureInfo {\n      texture: destination.texture.id,\n      mip_level: destination.mip_level,\n      origin: destination.origin.into(),\n      aspect: destination.aspect.into(),\n    };\n\n    let data_layout = wgpu_types::TexelCopyBufferLayout {\n      offset: data_layout.offset,\n      bytes_per_row: data_layout.bytes_per_row,\n      rows_per_image: data_layout.rows_per_image,\n    };\n\n    let err = self\n      .instance\n      .queue_write_texture(\n        self.id,\n        &destination,\n        buf,\n        &data_layout,\n        &size.into(),\n      )\n      .err();\n\n    self.error_handler.push_error(err);\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUTexelCopyTextureInfo {\n  pub texture: Ptr<GPUTexture>,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  pub mip_level: u32,\n  #[webidl(default = Default::default())]\n  pub origin: GPUOrigin3D,\n  #[webidl(default = GPUTextureAspect::All)]\n  pub aspect: GPUTextureAspect,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\nstruct GPUTexelCopyBufferLayout {\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  offset: u64,\n  #[options(enforce_range = true)]\n  bytes_per_row: Option<u32>,\n  #[options(enforce_range = true)]\n  rows_per_image: Option<u32>,\n}\n"
  },
  {
    "path": "deno_webgpu/render_bundle.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\nuse std::cell::RefCell;\nuse std::num::NonZeroU64;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::IntOptions;\nuse deno_core::webidl::Nullable;\nuse deno_core::webidl::WebIdlConverter;\nuse deno_core::webidl::WebIdlError;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\n\nuse crate::buffer::GPUBuffer;\nuse crate::error::GPUGenericError;\nuse crate::texture::GPUTextureFormat;\nuse crate::Instance;\n\nfn c_string_truncated_at_first_nul<T: Into<Vec<u8>>>(\n  src: T,\n) -> std::ffi::CString {\n  std::ffi::CString::new(src).unwrap_or_else(|err| {\n    let nul_pos = err.nul_position();\n    std::ffi::CString::new(err.into_vec().split_at(nul_pos).0).unwrap()\n  })\n}\n\npub struct GPURenderBundleEncoder {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub encoder: RefCell<Option<wgpu_core::command::RenderBundleEncoder>>,\n  pub label: String,\n}\n\nimpl GarbageCollected for GPURenderBundleEncoder {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPURenderBundleEncoder\"\n  }\n}\n\n#[op2]\nimpl GPURenderBundleEncoder {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPURenderBundleEncoder, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[cppgc]\n  fn finish(\n    &self,\n    #[webidl] descriptor: GPURenderBundleDescriptor,\n  ) -> GPURenderBundle {\n    let wgpu_descriptor = wgpu_core::command::RenderBundleDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n    };\n\n    let (id, err) = self.instance.render_bundle_encoder_finish(\n      self.encoder.borrow_mut().take().unwrap(),\n      &wgpu_descriptor,\n      None,\n    );\n\n    self.error_handler.push_error(err);\n\n    GPURenderBundle {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label.clone(),\n    }\n  }\n\n  #[undefined]\n  fn push_debug_group(\n    &self,\n    #[webidl] group_label: String,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    let label = c_string_truncated_at_first_nul(group_label);\n    // SAFETY: the string the raw pointer points to lives longer than the below\n    // function invocation.\n    unsafe {\n      wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(\n        encoder,\n        label.as_ptr(),\n      );\n    }\n\n    Ok(())\n  }\n\n  #[fast]\n  #[undefined]\n  fn pop_debug_group(&self) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder);\n    Ok(())\n  }\n\n  #[undefined]\n  fn insert_debug_marker(\n    &self,\n    #[webidl] marker_label: String,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    let label = c_string_truncated_at_first_nul(marker_label);\n    // SAFETY: the string the raw pointer points to lives longer than the below\n    // function invocation.\n    unsafe {\n      wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(\n        encoder,\n        label.as_ptr(),\n      );\n    }\n    Ok(())\n  }\n\n  #[undefined]\n  fn set_bind_group<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n    #[webidl(options(enforce_range = true))] index: u32,\n    #[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,\n    dynamic_offsets: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_start: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_length: v8::Local<'a, v8::Value>,\n  ) -> Result<(), SetBindGroupError> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    const PREFIX: &str =\n      \"Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'\";\n    if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>() {\n      let start = u64::convert(\n        scope,\n        dynamic_offsets_data_start,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 4\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n      let len = u32::convert(\n        scope,\n        dynamic_offsets_data_length,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 5\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n\n      let ab = uint_32.buffer(scope).unwrap();\n      let ptr = ab.data().unwrap();\n      let ab_len = ab.byte_length() / 4;\n\n      // SAFETY: created from an array buffer, slice is dropped at end of function call\n      let data =\n        unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };\n\n      let offsets = &data[start..(start + len)];\n\n      // SAFETY: wgpu FFI call\n      unsafe {\n        wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(\n          encoder,\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          offsets.as_ptr(),\n          offsets.len(),\n        );\n      }\n    } else {\n      let offsets = <Option<Vec<u32>>>::convert(\n        scope,\n        dynamic_offsets,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 3\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )?\n      .unwrap_or_default();\n\n      // SAFETY: wgpu FFI call\n      unsafe {\n        wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(\n          encoder,\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          offsets.as_ptr(),\n          offsets.len(),\n        );\n      }\n    }\n\n    Ok(())\n  }\n\n  #[undefined]\n  fn set_pipeline(\n    &self,\n    #[webidl] pipeline: Ptr<crate::render_pipeline::GPURenderPipeline>,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(\n      encoder,\n      pipeline.id,\n    );\n    Ok(())\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn set_index_buffer(\n    &self,\n    #[webidl] buffer: Ptr<GPUBuffer>,\n    #[webidl] index_format: crate::render_pipeline::GPUIndexFormat,\n    #[webidl(default = 0, options(enforce_range = true))] offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    encoder.set_index_buffer(\n      buffer.id,\n      index_format.into(),\n      offset,\n      size.and_then(NonZeroU64::new),\n    );\n    Ok(())\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn set_vertex_buffer(\n    &self,\n    #[webidl(options(enforce_range = true))] slot: u32,\n    #[webidl] buffer: Ptr<GPUBuffer>, // TODO(wgpu): support nullable buffer\n    #[webidl(default = 0, options(enforce_range = true))] offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(\n      encoder,\n      slot,\n      buffer.id,\n      offset,\n      size.and_then(NonZeroU64::new),\n    );\n    Ok(())\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn draw(\n    &self,\n    #[webidl(options(enforce_range = true))] vertex_count: u32,\n    #[webidl(default = 1, options(enforce_range = true))] instance_count: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_instance: u32,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(\n      encoder,\n      vertex_count,\n      instance_count,\n      first_vertex,\n      first_instance,\n    );\n    Ok(())\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn draw_indexed(\n    &self,\n    #[webidl(options(enforce_range = true))] index_count: u32,\n    #[webidl(default = 1, options(enforce_range = true))] instance_count: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_index: u32,\n    #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32,\n    #[webidl(default = 0, options(enforce_range = true))] first_instance: u32,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(\n      encoder,\n      index_count,\n      instance_count,\n      first_index,\n      base_vertex,\n      first_instance,\n    );\n    Ok(())\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn draw_indirect(\n    &self,\n    #[webidl] indirect_buffer: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] indirect_offset: u64,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(\n      encoder,\n      indirect_buffer.id,\n      indirect_offset,\n    );\n    Ok(())\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn draw_indexed_indirect(\n    &self,\n    #[webidl] indirect_buffer: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] indirect_offset: u64,\n  ) -> Result<(), JsErrorBox> {\n    let mut encoder = self.encoder.borrow_mut();\n    let encoder = encoder.as_mut().ok_or_else(|| {\n      JsErrorBox::generic(\"Encoder has already been finished\")\n    })?;\n\n    wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect(\n      encoder,\n      indirect_buffer.id,\n      indirect_offset,\n    );\n    Ok(())\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderBundleEncoderDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub color_formats: Vec<Nullable<GPUTextureFormat>>,\n  pub depth_stencil_format: Option<GPUTextureFormat>,\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  pub sample_count: u32,\n\n  #[webidl(default = false)]\n  pub depth_read_only: bool,\n  #[webidl(default = false)]\n  pub stencil_read_only: bool,\n}\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\nenum SetBindGroupError {\n  #[class(inherit)]\n  #[error(transparent)]\n  WebIDL(#[from] WebIdlError),\n  #[class(inherit)]\n  #[error(transparent)]\n  Other(#[from] JsErrorBox),\n}\n\npub struct GPURenderBundle {\n  pub instance: Instance,\n  pub id: wgpu_core::id::RenderBundleId,\n  pub label: String,\n}\n\nimpl Drop for GPURenderBundle {\n  fn drop(&mut self) {\n    self.instance.render_bundle_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPURenderBundle {\n  const NAME: &'static str = \"GPURenderBundle\";\n}\n\nimpl GarbageCollected for GPURenderBundle {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPURenderBundle\"\n  }\n}\n\n#[op2]\nimpl GPURenderBundle {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPURenderBundle, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderBundleDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n}\n"
  },
  {
    "path": "deno_webgpu/render_pass.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\nuse std::cell::RefCell;\nuse std::num::NonZeroU64;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::v8::HandleScope;\nuse deno_core::v8::Local;\nuse deno_core::v8::Value;\nuse deno_core::webidl::ContextFn;\nuse deno_core::webidl::IntOptions;\nuse deno_core::webidl::Nullable;\nuse deno_core::webidl::WebIdlConverter;\nuse deno_core::webidl::WebIdlError;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::buffer::GPUBuffer;\nuse crate::error::GPUGenericError;\nuse crate::render_bundle::GPURenderBundle;\nuse crate::texture::GPUTexture;\nuse crate::texture::GPUTextureView;\nuse crate::webidl::GPUColor;\nuse crate::Instance;\n\npub struct GPURenderPassEncoder {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub render_pass: RefCell<wgpu_core::command::RenderPass>,\n  pub label: String,\n}\n\nimpl GarbageCollected for GPURenderPassEncoder {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPURenderPassEncoder\"\n  }\n}\n\n#[op2]\nimpl GPURenderPassEncoder {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPURenderPassEncoder, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[required(6)]\n  #[undefined]\n  fn set_viewport(\n    &self,\n    #[webidl] x: f32,\n    #[webidl] y: f32,\n    #[webidl] width: f32,\n    #[webidl] height: f32,\n    #[webidl] min_depth: f32,\n    #[webidl] max_depth: f32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_viewport(\n        &mut self.render_pass.borrow_mut(),\n        x,\n        y,\n        width,\n        height,\n        min_depth,\n        max_depth,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(4)]\n  #[undefined]\n  fn set_scissor_rect(\n    &self,\n    #[webidl(options(enforce_range = true))] x: u32,\n    #[webidl(options(enforce_range = true))] y: u32,\n    #[webidl(options(enforce_range = true))] width: u32,\n    #[webidl(options(enforce_range = true))] height: u32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_scissor_rect(\n        &mut self.render_pass.borrow_mut(),\n        x,\n        y,\n        width,\n        height,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn set_blend_constant(&self, #[webidl] color: GPUColor) {\n    let err = self\n      .instance\n      .render_pass_set_blend_constant(\n        &mut self.render_pass.borrow_mut(),\n        color.into(),\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn set_stencil_reference(\n    &self,\n    #[webidl(options(enforce_range = true))] reference: u32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_stencil_reference(\n        &mut self.render_pass.borrow_mut(),\n        reference,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn begin_occlusion_query(\n    &self,\n    #[webidl(options(enforce_range = true))] query_index: u32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_begin_occlusion_query(\n        &mut self.render_pass.borrow_mut(),\n        query_index,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  #[undefined]\n  fn end_occlusion_query(&self) {\n    let err = self\n      .instance\n      .render_pass_end_occlusion_query(&mut self.render_pass.borrow_mut())\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn execute_bundles(&self, #[webidl] bundles: Vec<Ptr<GPURenderBundle>>) {\n    let err = self\n      .instance\n      .render_pass_execute_bundles(\n        &mut self.render_pass.borrow_mut(),\n        &bundles\n          .into_iter()\n          .map(|bundle| bundle.id)\n          .collect::<Vec<_>>(),\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  #[undefined]\n  fn end(&self) {\n    let err = self\n      .instance\n      .render_pass_end(&mut self.render_pass.borrow_mut())\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn push_debug_group(&self, #[webidl] group_label: String) {\n    let err = self\n      .instance\n      .render_pass_push_debug_group(\n        &mut self.render_pass.borrow_mut(),\n        &group_label,\n        0, // wgpu#975\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[fast]\n  #[undefined]\n  fn pop_debug_group(&self) {\n    let err = self\n      .instance\n      .render_pass_pop_debug_group(&mut self.render_pass.borrow_mut())\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn insert_debug_marker(&self, #[webidl] marker_label: String) {\n    let err = self\n      .instance\n      .render_pass_insert_debug_marker(\n        &mut self.render_pass.borrow_mut(),\n        &marker_label,\n        0, // wgpu#975\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[undefined]\n  fn set_bind_group<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n    #[webidl(options(enforce_range = true))] index: u32,\n    #[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,\n    dynamic_offsets: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_start: v8::Local<'a, v8::Value>,\n    dynamic_offsets_data_length: v8::Local<'a, v8::Value>,\n  ) -> Result<(), WebIdlError> {\n    const PREFIX: &str =\n      \"Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'\";\n\n    let err = if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>()\n    {\n      let start = u64::convert(\n        scope,\n        dynamic_offsets_data_start,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 4\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n      let len = u32::convert(\n        scope,\n        dynamic_offsets_data_length,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 5\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )? as usize;\n\n      let ab = uint_32.buffer(scope).unwrap();\n      let ptr = ab.data().unwrap();\n      let ab_len = ab.byte_length() / 4;\n\n      // SAFETY: created from an array buffer, slice is dropped at end of function call\n      let data =\n        unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };\n\n      let offsets = &data[start..(start + len)];\n\n      self\n        .instance\n        .render_pass_set_bind_group(\n          &mut self.render_pass.borrow_mut(),\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          offsets,\n        )\n        .err()\n    } else {\n      let offsets = <Option<Vec<u32>>>::convert(\n        scope,\n        dynamic_offsets,\n        Cow::Borrowed(PREFIX),\n        (|| Cow::Borrowed(\"Argument 3\")).into(),\n        &IntOptions {\n          clamp: false,\n          enforce_range: true,\n        },\n      )?\n      .unwrap_or_default();\n\n      self\n        .instance\n        .render_pass_set_bind_group(\n          &mut self.render_pass.borrow_mut(),\n          index,\n          bind_group.into_option().map(|bind_group| bind_group.id),\n          &offsets,\n        )\n        .err()\n    };\n\n    self.error_handler.push_error(err);\n\n    Ok(())\n  }\n\n  #[undefined]\n  fn set_pipeline(\n    &self,\n    #[webidl] pipeline: Ptr<crate::render_pipeline::GPURenderPipeline>,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_pipeline(&mut self.render_pass.borrow_mut(), pipeline.id)\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn set_index_buffer(\n    &self,\n    #[webidl] buffer: Ptr<GPUBuffer>,\n    #[webidl] index_format: crate::render_pipeline::GPUIndexFormat,\n    #[webidl(default = 0, options(enforce_range = true))] offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_index_buffer(\n        &mut self.render_pass.borrow_mut(),\n        buffer.id,\n        index_format.into(),\n        offset,\n        size.and_then(NonZeroU64::new),\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn set_vertex_buffer(\n    &self,\n    #[webidl(options(enforce_range = true))] slot: u32,\n    #[webidl] buffer: Ptr<GPUBuffer>, // TODO(wgpu): support nullable buffer\n    #[webidl(default = 0, options(enforce_range = true))] offset: u64,\n    #[webidl(options(enforce_range = true))] size: Option<u64>,\n  ) {\n    let err = self\n      .instance\n      .render_pass_set_vertex_buffer(\n        &mut self.render_pass.borrow_mut(),\n        slot,\n        buffer.id,\n        offset,\n        size.and_then(NonZeroU64::new),\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn draw(\n    &self,\n    #[webidl(options(enforce_range = true))] vertex_count: u32,\n    #[webidl(default = 1, options(enforce_range = true))] instance_count: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_instance: u32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_draw(\n        &mut self.render_pass.borrow_mut(),\n        vertex_count,\n        instance_count,\n        first_vertex,\n        first_instance,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(1)]\n  #[undefined]\n  fn draw_indexed(\n    &self,\n    #[webidl(options(enforce_range = true))] index_count: u32,\n    #[webidl(default = 1, options(enforce_range = true))] instance_count: u32,\n    #[webidl(default = 0, options(enforce_range = true))] first_index: u32,\n    #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32,\n    #[webidl(default = 0, options(enforce_range = true))] first_instance: u32,\n  ) {\n    let err = self\n      .instance\n      .render_pass_draw_indexed(\n        &mut self.render_pass.borrow_mut(),\n        index_count,\n        instance_count,\n        first_index,\n        base_vertex,\n        first_instance,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn draw_indirect(\n    &self,\n    #[webidl] indirect_buffer: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] indirect_offset: u64,\n  ) {\n    let err = self\n      .instance\n      .render_pass_draw_indirect(\n        &mut self.render_pass.borrow_mut(),\n        indirect_buffer.id,\n        indirect_offset,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n\n  #[required(2)]\n  #[undefined]\n  fn draw_indexed_indirect(\n    &self,\n    #[webidl] indirect_buffer: Ptr<GPUBuffer>,\n    #[webidl(options(enforce_range = true))] indirect_offset: u64,\n  ) {\n    let err = self\n      .instance\n      .render_pass_draw_indexed_indirect(\n        &mut self.render_pass.borrow_mut(),\n        indirect_buffer.id,\n        indirect_offset,\n      )\n      .err();\n    self.error_handler.push_error(err);\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderPassDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub color_attachments: Vec<Nullable<GPURenderPassColorAttachment>>,\n  pub depth_stencil_attachment: Option<GPURenderPassDepthStencilAttachment>,\n  pub occlusion_query_set: Option<Ptr<crate::query_set::GPUQuerySet>>,\n  pub timestamp_writes: Option<GPURenderPassTimestampWrites>,\n  /*#[webidl(default = 50000000)]\n  #[options(enforce_range = true)]\n  pub max_draw_count: u64,*/\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  pub multiview_mask: u32,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderPassColorAttachment {\n  pub view: GPUTextureOrView,\n  #[options(enforce_range = true)]\n  pub depth_slice: Option<u32>,\n  pub resolve_target: Option<GPUTextureOrView>,\n  pub clear_value: Option<GPUColor>,\n  pub load_op: GPULoadOp,\n  pub store_op: GPUStoreOp,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPULoadOp {\n  Load,\n  Clear,\n}\nimpl GPULoadOp {\n  pub fn with_default_value<V: Default>(\n    self,\n    val: Option<V>,\n  ) -> wgpu_core::command::LoadOp<V> {\n    match self {\n      GPULoadOp::Load => wgpu_core::command::LoadOp::Load,\n      GPULoadOp::Clear => {\n        wgpu_core::command::LoadOp::Clear(val.unwrap_or_default())\n      }\n    }\n  }\n\n  pub fn with_value<V>(self, val: V) -> wgpu_core::command::LoadOp<V> {\n    match self {\n      GPULoadOp::Load => wgpu_core::command::LoadOp::Load,\n      GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val),\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUStoreOp {\n  Store,\n  Discard,\n}\nimpl From<GPUStoreOp> for wgpu_core::command::StoreOp {\n  fn from(value: GPUStoreOp) -> Self {\n    match value {\n      GPUStoreOp::Store => Self::Store,\n      GPUStoreOp::Discard => Self::Discard,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderPassDepthStencilAttachment {\n  pub view: GPUTextureOrView,\n  pub depth_clear_value: Option<f32>,\n  pub depth_load_op: Option<GPULoadOp>,\n  pub depth_store_op: Option<GPUStoreOp>,\n  #[webidl(default = false)]\n  pub depth_read_only: bool,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  pub stencil_clear_value: u32,\n  pub stencil_load_op: Option<GPULoadOp>,\n  pub stencil_store_op: Option<GPUStoreOp>,\n  #[webidl(default = false)]\n  pub stencil_read_only: bool,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderPassTimestampWrites {\n  pub query_set: Ptr<crate::query_set::GPUQuerySet>,\n  #[options(enforce_range = true)]\n  pub beginning_of_pass_write_index: Option<u32>,\n  #[options(enforce_range = true)]\n  pub end_of_pass_write_index: Option<u32>,\n}\n\npub(crate) enum GPUTextureOrView {\n  Texture(Ptr<GPUTexture>),\n  TextureView(Ptr<GPUTextureView>),\n}\n\nimpl GPUTextureOrView {\n  pub(crate) fn to_view_id(&self) -> wgpu_core::id::TextureViewId {\n    match self {\n      Self::Texture(texture) => texture.default_view_id(),\n      Self::TextureView(texture_view) => texture_view.id,\n    }\n  }\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUTextureOrView {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut HandleScope<'a>,\n    value: Local<'a, Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    <Ptr<GPUTexture>>::convert(\n      scope,\n      value,\n      prefix.clone(),\n      context.borrowed(),\n      options,\n    )\n    .map(Self::Texture)\n    .or_else(|_| {\n      <Ptr<GPUTextureView>>::convert(\n        scope,\n        value,\n        prefix.clone(),\n        context.borrowed(),\n        options,\n      )\n      .map(Self::TextureView)\n    })\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/render_pipeline.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::webidl::Nullable;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse indexmap::IndexMap;\n\nuse crate::bind_group_layout::GPUBindGroupLayout;\nuse crate::error::GPUGenericError;\nuse crate::sampler::GPUCompareFunction;\nuse crate::shader::GPUShaderModule;\nuse crate::texture::GPUTextureFormat;\nuse crate::webidl::GPUColorWriteFlags;\nuse crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;\nuse crate::Instance;\n\npub struct GPURenderPipeline {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub id: wgpu_core::id::RenderPipelineId,\n  pub label: String,\n}\n\nimpl Drop for GPURenderPipeline {\n  fn drop(&mut self) {\n    self.instance.render_pipeline_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPURenderPipeline {\n  const NAME: &'static str = \"GPURenderPipeline\";\n}\n\nimpl GarbageCollected for GPURenderPipeline {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPURenderPipeline\"\n  }\n}\n\n#[op2]\nimpl GPURenderPipeline {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPURenderPipeline, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[cppgc]\n  fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {\n    let (id, err) = self\n      .instance\n      .render_pipeline_get_bind_group_layout(self.id, index, None);\n\n    self.error_handler.push_error(err);\n\n    // TODO(wgpu): needs to add a way to retrieve the label\n    GPUBindGroupLayout {\n      instance: self.instance.clone(),\n      id,\n      label: \"\".to_string(),\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPURenderPipelineDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,\n  pub vertex: GPUVertexState,\n  pub primitive: GPUPrimitiveState,\n  pub depth_stencil: Option<GPUDepthStencilState>,\n  pub multisample: GPUMultisampleState,\n  pub fragment: Option<GPUFragmentState>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUMultisampleState {\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  pub count: u32,\n  #[webidl(default = 0xFFFFFFFF)]\n  #[options(enforce_range = true)]\n  pub mask: u32,\n  #[webidl(default = false)]\n  pub alpha_to_coverage_enabled: bool,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUDepthStencilState {\n  pub format: GPUTextureFormat,\n  pub depth_write_enabled: Option<bool>,\n  pub depth_compare: Option<GPUCompareFunction>,\n  pub stencil_front: GPUStencilFaceState,\n  pub stencil_back: GPUStencilFaceState,\n  #[webidl(default = 0xFFFFFFFF)]\n  #[options(enforce_range = true)]\n  pub stencil_read_mask: u32,\n  #[webidl(default = 0xFFFFFFFF)]\n  #[options(enforce_range = true)]\n  pub stencil_write_mask: u32,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  pub depth_bias: i32,\n  #[webidl(default = 0.0)]\n  pub depth_bias_slope_scale: f32,\n  #[webidl(default = 0.0)]\n  pub depth_bias_clamp: f32,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUStencilFaceState {\n  #[webidl(default = GPUCompareFunction::Always)]\n  pub compare: GPUCompareFunction,\n  #[webidl(default = GPUStencilOperation::Keep)]\n  pub fail_op: GPUStencilOperation,\n  #[webidl(default = GPUStencilOperation::Keep)]\n  pub depth_fail_op: GPUStencilOperation,\n  #[webidl(default = GPUStencilOperation::Keep)]\n  pub pass_op: GPUStencilOperation,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUStencilOperation {\n  Keep,\n  Zero,\n  Replace,\n  Invert,\n  IncrementClamp,\n  DecrementClamp,\n  IncrementWrap,\n  DecrementWrap,\n}\n\nimpl From<GPUStencilOperation> for wgpu_types::StencilOperation {\n  fn from(value: GPUStencilOperation) -> Self {\n    match value {\n      GPUStencilOperation::Keep => Self::Keep,\n      GPUStencilOperation::Zero => Self::Zero,\n      GPUStencilOperation::Replace => Self::Replace,\n      GPUStencilOperation::Invert => Self::Invert,\n      GPUStencilOperation::IncrementClamp => Self::IncrementClamp,\n      GPUStencilOperation::DecrementClamp => Self::DecrementClamp,\n      GPUStencilOperation::IncrementWrap => Self::IncrementWrap,\n      GPUStencilOperation::DecrementWrap => Self::DecrementWrap,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUVertexState {\n  pub module: Ptr<GPUShaderModule>,\n  pub entry_point: Option<String>,\n  #[webidl(default = Default::default())]\n  pub constants: IndexMap<String, f64>,\n  #[webidl(default = vec![])]\n  pub buffers: Vec<Nullable<GPUVertexBufferLayout>>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUFragmentState {\n  pub module: Ptr<GPUShaderModule>,\n  pub entry_point: Option<String>,\n  #[webidl(default = Default::default())]\n  pub constants: IndexMap<String, f64>,\n  pub targets: Vec<Nullable<GPUColorTargetState>>,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUColorTargetState {\n  pub format: GPUTextureFormat,\n  pub blend: Option<GPUBlendState>,\n  #[webidl(default = GPUColorWriteFlags(wgpu_types::ColorWrites::ALL))]\n  pub write_mask: GPUColorWriteFlags,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBlendState {\n  pub color: GPUBlendComponent,\n  pub alpha: GPUBlendComponent,\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUBlendComponent {\n  #[webidl(default = GPUBlendOperation::Add)]\n  pub operation: GPUBlendOperation,\n  #[webidl(default = GPUBlendFactor::One)]\n  pub src_factor: GPUBlendFactor,\n  #[webidl(default = GPUBlendFactor::Zero)]\n  pub dst_factor: GPUBlendFactor,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUBlendOperation {\n  Add,\n  Subtract,\n  ReverseSubtract,\n  Min,\n  Max,\n}\n\nimpl From<GPUBlendOperation> for wgpu_types::BlendOperation {\n  fn from(value: GPUBlendOperation) -> Self {\n    match value {\n      GPUBlendOperation::Add => Self::Add,\n      GPUBlendOperation::Subtract => Self::Subtract,\n      GPUBlendOperation::ReverseSubtract => Self::ReverseSubtract,\n      GPUBlendOperation::Min => Self::Min,\n      GPUBlendOperation::Max => Self::Max,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUBlendFactor {\n  #[webidl(rename = \"zero\")]\n  Zero,\n  #[webidl(rename = \"one\")]\n  One,\n  #[webidl(rename = \"src\")]\n  Src,\n  #[webidl(rename = \"one-minus-src\")]\n  OneMinusSrc,\n  #[webidl(rename = \"src-alpha\")]\n  SrcAlpha,\n  #[webidl(rename = \"one-minus-src-alpha\")]\n  OneMinusSrcAlpha,\n  #[webidl(rename = \"dst\")]\n  Dst,\n  #[webidl(rename = \"one-minus-dst\")]\n  OneMinusDst,\n  #[webidl(rename = \"dst-alpha\")]\n  DstAlpha,\n  #[webidl(rename = \"one-minus-dst-alpha\")]\n  OneMinusDstAlpha,\n  #[webidl(rename = \"src-alpha-saturated\")]\n  SrcAlphaSaturated,\n  #[webidl(rename = \"constant\")]\n  Constant,\n  #[webidl(rename = \"one-minus-constant\")]\n  OneMinusConstant,\n  #[webidl(rename = \"src1\")]\n  Src1,\n  #[webidl(rename = \"one-minus-src1\")]\n  OneMinusSrc1,\n  #[webidl(rename = \"src1-alpha\")]\n  Src1Alpha,\n  #[webidl(rename = \"one-minus-src1-alpha\")]\n  OneMinusSrc1Alpha,\n}\n\nimpl From<GPUBlendFactor> for wgpu_types::BlendFactor {\n  fn from(value: GPUBlendFactor) -> Self {\n    match value {\n      GPUBlendFactor::Zero => Self::Zero,\n      GPUBlendFactor::One => Self::One,\n      GPUBlendFactor::Src => Self::Src,\n      GPUBlendFactor::OneMinusSrc => Self::OneMinusSrc,\n      GPUBlendFactor::SrcAlpha => Self::SrcAlpha,\n      GPUBlendFactor::OneMinusSrcAlpha => Self::OneMinusSrcAlpha,\n      GPUBlendFactor::Dst => Self::Dst,\n      GPUBlendFactor::OneMinusDst => Self::OneMinusDst,\n      GPUBlendFactor::DstAlpha => Self::DstAlpha,\n      GPUBlendFactor::OneMinusDstAlpha => Self::OneMinusDstAlpha,\n      GPUBlendFactor::SrcAlphaSaturated => Self::SrcAlphaSaturated,\n      GPUBlendFactor::Constant => Self::Constant,\n      GPUBlendFactor::OneMinusConstant => Self::OneMinusConstant,\n      GPUBlendFactor::Src1 => Self::Src1,\n      GPUBlendFactor::OneMinusSrc1 => Self::OneMinusSrc1,\n      GPUBlendFactor::Src1Alpha => Self::Src1Alpha,\n      GPUBlendFactor::OneMinusSrc1Alpha => Self::OneMinusSrc1Alpha,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUPrimitiveState {\n  #[webidl(default = GPUPrimitiveTopology::TriangleList)]\n  pub topology: GPUPrimitiveTopology,\n  pub strip_index_format: Option<GPUIndexFormat>,\n  #[webidl(default = GPUFrontFace::Ccw)]\n  pub front_face: GPUFrontFace,\n  #[webidl(default = GPUCullMode::None)]\n  pub cull_mode: GPUCullMode,\n  #[webidl(default = false)]\n  pub unclipped_depth: bool,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUPrimitiveTopology {\n  PointList,\n  LineList,\n  LineStrip,\n  TriangleList,\n  TriangleStrip,\n}\n\nimpl From<GPUPrimitiveTopology> for wgpu_types::PrimitiveTopology {\n  fn from(value: GPUPrimitiveTopology) -> Self {\n    match value {\n      GPUPrimitiveTopology::PointList => Self::PointList,\n      GPUPrimitiveTopology::LineList => Self::LineList,\n      GPUPrimitiveTopology::LineStrip => Self::LineStrip,\n      GPUPrimitiveTopology::TriangleList => Self::TriangleList,\n      GPUPrimitiveTopology::TriangleStrip => Self::TriangleStrip,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUIndexFormat {\n  #[webidl(rename = \"uint16\")]\n  Uint16,\n  #[webidl(rename = \"uint32\")]\n  Uint32,\n}\n\nimpl From<GPUIndexFormat> for wgpu_types::IndexFormat {\n  fn from(value: GPUIndexFormat) -> Self {\n    match value {\n      GPUIndexFormat::Uint16 => Self::Uint16,\n      GPUIndexFormat::Uint32 => Self::Uint32,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUFrontFace {\n  Ccw,\n  Cw,\n}\n\nimpl From<GPUFrontFace> for wgpu_types::FrontFace {\n  fn from(value: GPUFrontFace) -> Self {\n    match value {\n      GPUFrontFace::Ccw => Self::Ccw,\n      GPUFrontFace::Cw => Self::Cw,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUCullMode {\n  None,\n  Front,\n  Back,\n}\n\nimpl From<GPUCullMode> for Option<wgpu_types::Face> {\n  fn from(value: GPUCullMode) -> Self {\n    match value {\n      GPUCullMode::None => None,\n      GPUCullMode::Front => Some(wgpu_types::Face::Front),\n      GPUCullMode::Back => Some(wgpu_types::Face::Back),\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUVertexBufferLayout {\n  #[options(enforce_range = true)]\n  pub array_stride: u64,\n  #[webidl(default = GPUVertexStepMode::Vertex)]\n  pub step_mode: GPUVertexStepMode,\n  pub attributes: Vec<GPUVertexAttribute>,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUVertexStepMode {\n  Vertex,\n  Instance,\n}\n\nimpl From<GPUVertexStepMode> for wgpu_types::VertexStepMode {\n  fn from(value: GPUVertexStepMode) -> Self {\n    match value {\n      GPUVertexStepMode::Vertex => Self::Vertex,\n      GPUVertexStepMode::Instance => Self::Instance,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUVertexAttribute {\n  pub format: GPUVertexFormat,\n  #[options(enforce_range = true)]\n  pub offset: u64,\n  #[options(enforce_range = true)]\n  pub shader_location: u32,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUVertexFormat {\n  #[webidl(rename = \"uint8\")]\n  Uint8,\n  #[webidl(rename = \"uint8x2\")]\n  Uint8x2,\n  #[webidl(rename = \"uint8x4\")]\n  Uint8x4,\n  #[webidl(rename = \"sint8\")]\n  Sint8,\n  #[webidl(rename = \"sint8x2\")]\n  Sint8x2,\n  #[webidl(rename = \"sint8x4\")]\n  Sint8x4,\n  #[webidl(rename = \"unorm8\")]\n  Unorm8,\n  #[webidl(rename = \"unorm8x2\")]\n  Unorm8x2,\n  #[webidl(rename = \"unorm8x4\")]\n  Unorm8x4,\n  #[webidl(rename = \"snorm8\")]\n  Snorm8,\n  #[webidl(rename = \"snorm8x2\")]\n  Snorm8x2,\n  #[webidl(rename = \"snorm8x4\")]\n  Snorm8x4,\n  #[webidl(rename = \"uint16\")]\n  Uint16,\n  #[webidl(rename = \"uint16x2\")]\n  Uint16x2,\n  #[webidl(rename = \"uint16x4\")]\n  Uint16x4,\n  #[webidl(rename = \"sint16\")]\n  Sint16,\n  #[webidl(rename = \"sint16x2\")]\n  Sint16x2,\n  #[webidl(rename = \"sint16x4\")]\n  Sint16x4,\n  #[webidl(rename = \"unorm16\")]\n  Unorm16,\n  #[webidl(rename = \"unorm16x2\")]\n  Unorm16x2,\n  #[webidl(rename = \"unorm16x4\")]\n  Unorm16x4,\n  #[webidl(rename = \"snorm16\")]\n  Snorm16,\n  #[webidl(rename = \"snorm16x2\")]\n  Snorm16x2,\n  #[webidl(rename = \"snorm16x4\")]\n  Snorm16x4,\n  #[webidl(rename = \"float16\")]\n  Float16,\n  #[webidl(rename = \"float16x2\")]\n  Float16x2,\n  #[webidl(rename = \"float16x4\")]\n  Float16x4,\n  #[webidl(rename = \"float32\")]\n  Float32,\n  #[webidl(rename = \"float32x2\")]\n  Float32x2,\n  #[webidl(rename = \"float32x3\")]\n  Float32x3,\n  #[webidl(rename = \"float32x4\")]\n  Float32x4,\n  #[webidl(rename = \"uint32\")]\n  Uint32,\n  #[webidl(rename = \"uint32x2\")]\n  Uint32x2,\n  #[webidl(rename = \"uint32x3\")]\n  Uint32x3,\n  #[webidl(rename = \"uint32x4\")]\n  Uint32x4,\n  #[webidl(rename = \"sint32\")]\n  Sint32,\n  #[webidl(rename = \"sint32x2\")]\n  Sint32x2,\n  #[webidl(rename = \"sint32x3\")]\n  Sint32x3,\n  #[webidl(rename = \"sint32x4\")]\n  Sint32x4,\n  #[webidl(rename = \"unorm10-10-10-2\")]\n  Unorm1010102,\n  #[webidl(rename = \"unorm8x4-bgra\")]\n  Unorm8x4Bgra,\n}\n\nimpl From<GPUVertexFormat> for wgpu_types::VertexFormat {\n  fn from(value: GPUVertexFormat) -> Self {\n    match value {\n      GPUVertexFormat::Uint8 => Self::Uint8,\n      GPUVertexFormat::Uint8x2 => Self::Uint8x2,\n      GPUVertexFormat::Uint8x4 => Self::Uint8x4,\n      GPUVertexFormat::Sint8 => Self::Sint8,\n      GPUVertexFormat::Sint8x2 => Self::Sint8x2,\n      GPUVertexFormat::Sint8x4 => Self::Sint8x4,\n      GPUVertexFormat::Unorm8 => Self::Unorm8,\n      GPUVertexFormat::Unorm8x2 => Self::Unorm8x2,\n      GPUVertexFormat::Unorm8x4 => Self::Unorm8x4,\n      GPUVertexFormat::Snorm8 => Self::Snorm8,\n      GPUVertexFormat::Snorm8x2 => Self::Snorm8x2,\n      GPUVertexFormat::Snorm8x4 => Self::Snorm8x4,\n      GPUVertexFormat::Uint16 => Self::Uint16,\n      GPUVertexFormat::Uint16x2 => Self::Uint16x2,\n      GPUVertexFormat::Uint16x4 => Self::Uint16x4,\n      GPUVertexFormat::Sint16 => Self::Sint16,\n      GPUVertexFormat::Sint16x2 => Self::Sint16x2,\n      GPUVertexFormat::Sint16x4 => Self::Sint16x4,\n      GPUVertexFormat::Unorm16 => Self::Unorm16,\n      GPUVertexFormat::Unorm16x2 => Self::Unorm16x2,\n      GPUVertexFormat::Unorm16x4 => Self::Unorm16x4,\n      GPUVertexFormat::Snorm16 => Self::Snorm16,\n      GPUVertexFormat::Snorm16x2 => Self::Snorm16x2,\n      GPUVertexFormat::Snorm16x4 => Self::Snorm16x4,\n      GPUVertexFormat::Float16 => Self::Float16,\n      GPUVertexFormat::Float16x2 => Self::Float16x2,\n      GPUVertexFormat::Float16x4 => Self::Float16x4,\n      GPUVertexFormat::Float32 => Self::Float32,\n      GPUVertexFormat::Float32x2 => Self::Float32x2,\n      GPUVertexFormat::Float32x3 => Self::Float32x3,\n      GPUVertexFormat::Float32x4 => Self::Float32x4,\n      GPUVertexFormat::Uint32 => Self::Uint32,\n      GPUVertexFormat::Uint32x2 => Self::Uint32x2,\n      GPUVertexFormat::Uint32x3 => Self::Uint32x3,\n      GPUVertexFormat::Uint32x4 => Self::Uint32x4,\n      GPUVertexFormat::Sint32 => Self::Sint32,\n      GPUVertexFormat::Sint32x2 => Self::Sint32x2,\n      GPUVertexFormat::Sint32x3 => Self::Sint32x3,\n      GPUVertexFormat::Sint32x4 => Self::Sint32x4,\n      GPUVertexFormat::Unorm1010102 => Self::Unorm10_10_10_2,\n      GPUVertexFormat::Unorm8x4Bgra => Self::Unorm8x4Bgra,\n    }\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/rustfmt.toml",
    "content": "max_width = 80\ntab_spaces = 2\nedition = \"2021\"\n"
  },
  {
    "path": "deno_webgpu/sampler.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::op2;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUSampler {\n  pub instance: Instance,\n  pub id: wgpu_core::id::SamplerId,\n  pub label: String,\n}\n\nimpl Drop for GPUSampler {\n  fn drop(&mut self) {\n    self.instance.sampler_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUSampler {\n  const NAME: &'static str = \"GPUSampler\";\n}\n\nimpl GarbageCollected for GPUSampler {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUSampler\"\n  }\n}\n\n#[op2]\nimpl GPUSampler {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUSampler, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(super) struct GPUSamplerDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  #[webidl(default = GPUAddressMode::ClampToEdge)]\n  pub address_mode_u: GPUAddressMode,\n  #[webidl(default = GPUAddressMode::ClampToEdge)]\n  pub address_mode_v: GPUAddressMode,\n  #[webidl(default = GPUAddressMode::ClampToEdge)]\n  pub address_mode_w: GPUAddressMode,\n  #[webidl(default = GPUFilterMode::Nearest)]\n  pub mag_filter: GPUFilterMode,\n  #[webidl(default = GPUFilterMode::Nearest)]\n  pub min_filter: GPUFilterMode,\n  #[webidl(default = GPUMipmapFilterMode::Nearest)]\n  pub mipmap_filter: GPUMipmapFilterMode,\n\n  #[webidl(default = 0.0)]\n  pub lod_min_clamp: f32,\n  #[webidl(default = 32.0)]\n  pub lod_max_clamp: f32,\n\n  pub compare: Option<GPUCompareFunction>,\n\n  #[webidl(default = 1)]\n  #[options(clamp = true)]\n  pub max_anisotropy: u16,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUAddressMode {\n  ClampToEdge,\n  Repeat,\n  MirrorRepeat,\n}\n\nimpl From<GPUAddressMode> for wgpu_types::AddressMode {\n  fn from(value: GPUAddressMode) -> Self {\n    match value {\n      GPUAddressMode::ClampToEdge => Self::ClampToEdge,\n      GPUAddressMode::Repeat => Self::Repeat,\n      GPUAddressMode::MirrorRepeat => Self::MirrorRepeat,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUFilterMode {\n  Nearest,\n  Linear,\n}\n\nimpl From<GPUFilterMode> for wgpu_types::FilterMode {\n  fn from(value: GPUFilterMode) -> Self {\n    match value {\n      GPUFilterMode::Nearest => Self::Nearest,\n      GPUFilterMode::Linear => Self::Linear,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUMipmapFilterMode {\n  Nearest,\n  Linear,\n}\n\nimpl From<GPUMipmapFilterMode> for wgpu_types::MipmapFilterMode {\n  fn from(value: GPUMipmapFilterMode) -> Self {\n    match value {\n      GPUMipmapFilterMode::Nearest => Self::Nearest,\n      GPUMipmapFilterMode::Linear => Self::Linear,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUCompareFunction {\n  Never,\n  Less,\n  Equal,\n  LessEqual,\n  Greater,\n  NotEqual,\n  GreaterEqual,\n  Always,\n}\n\nimpl From<GPUCompareFunction> for wgpu_types::CompareFunction {\n  fn from(value: GPUCompareFunction) -> Self {\n    match value {\n      GPUCompareFunction::Never => Self::Never,\n      GPUCompareFunction::Less => Self::Less,\n      GPUCompareFunction::Equal => Self::Equal,\n      GPUCompareFunction::LessEqual => Self::LessEqual,\n      GPUCompareFunction::Greater => Self::Greater,\n      GPUCompareFunction::NotEqual => Self::NotEqual,\n      GPUCompareFunction::GreaterEqual => Self::GreaterEqual,\n      GPUCompareFunction::Always => Self::Always,\n    }\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/shader.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse deno_core::cppgc::make_cppgc_object;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse wgpu_core::pipeline;\n\nuse crate::error::GPUGenericError;\nuse crate::Instance;\n\npub struct GPUShaderModule {\n  pub instance: Instance,\n  pub id: wgpu_core::id::ShaderModuleId,\n  pub label: String,\n  pub compilation_info: v8::Global<v8::Object>,\n}\n\nimpl Drop for GPUShaderModule {\n  fn drop(&mut self) {\n    self.instance.shader_module_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUShaderModule {\n  const NAME: &'static str = \"GPUShaderModule\";\n}\n\nimpl GarbageCollected for GPUShaderModule {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUShaderModule\"\n  }\n}\n\n#[op2]\nimpl GPUShaderModule {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUShaderModule, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  fn get_compilation_info<'a>(\n    &self,\n    scope: &mut v8::HandleScope<'a>,\n  ) -> v8::Local<'a, v8::Promise> {\n    let resolver = v8::PromiseResolver::new(scope).unwrap();\n    let info = v8::Local::new(scope, self.compilation_info.clone());\n    resolver.resolve(scope, info.into()).unwrap();\n    resolver.get_promise(scope)\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUShaderModuleDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub code: String,\n}\n\npub struct GPUCompilationMessage {\n  message: String,\n  r#type: GPUCompilationMessageType,\n  line_num: u64,\n  line_pos: u64,\n  offset: u64,\n  length: u64,\n}\n\nimpl GarbageCollected for GPUCompilationMessage {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUCompilationMessage\"\n  }\n}\n\n#[op2]\nimpl GPUCompilationMessage {\n  #[getter]\n  #[string]\n  fn message(&self) -> String {\n    self.message.clone()\n  }\n\n  #[getter]\n  #[string]\n  #[rename(\"type\")]\n  fn r#type(&self) -> &'static str {\n    self.r#type.as_str()\n  }\n\n  #[getter]\n  #[number]\n  fn line_num(&self) -> u64 {\n    self.line_num\n  }\n\n  #[getter]\n  #[number]\n  fn line_pos(&self) -> u64 {\n    self.line_pos\n  }\n\n  #[getter]\n  #[number]\n  fn offset(&self) -> u64 {\n    self.offset\n  }\n\n  #[getter]\n  #[number]\n  fn length(&self) -> u64 {\n    self.length\n  }\n}\n\nimpl GPUCompilationMessage {\n  fn new(error: &pipeline::CreateShaderModuleError, source: &str) -> Self {\n    let message = error.to_string();\n\n    let loc = match error {\n      pipeline::CreateShaderModuleError::Parsing(e) => e.inner.location(source),\n      pipeline::CreateShaderModuleError::Validation(e) => {\n        e.inner.location(source)\n      }\n      _ => None,\n    };\n\n    match loc {\n      Some(loc) => {\n        let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum();\n\n        let start = loc.offset as usize;\n\n        // Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it.\n        let line_start =\n          source[0..start].rfind('\\n').map(|pos| pos + 1).unwrap_or(0);\n        let line_pos = len_utf16(&source[line_start..start]) + 1;\n\n        Self {\n          message,\n          r#type: GPUCompilationMessageType::Error,\n          line_num: loc.line_number.into(),\n          line_pos,\n          offset: len_utf16(&source[0..start]),\n          length: len_utf16(&source[start..start + loc.length as usize]),\n        }\n      }\n      _ => Self {\n        message,\n        r#type: GPUCompilationMessageType::Error,\n        line_num: 0,\n        line_pos: 0,\n        offset: 0,\n        length: 0,\n      },\n    }\n  }\n}\n\npub struct GPUCompilationInfo {\n  messages: v8::Global<v8::Object>,\n}\n\nimpl GarbageCollected for GPUCompilationInfo {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUCompilationInfo\"\n  }\n}\n\n#[op2]\nimpl GPUCompilationInfo {\n  #[getter]\n  #[global]\n  fn messages(&self) -> v8::Global<v8::Object> {\n    self.messages.clone()\n  }\n}\n\nimpl GPUCompilationInfo {\n  pub fn new<'args, 'scope>(\n    scope: &mut v8::HandleScope<'scope>,\n    messages: impl ExactSizeIterator<\n      Item = &'args pipeline::CreateShaderModuleError,\n    >,\n    source: &'args str,\n  ) -> Self {\n    let array = v8::Array::new(scope, messages.len().try_into().unwrap());\n    for (i, message) in messages.enumerate() {\n      let message_object =\n        make_cppgc_object(scope, GPUCompilationMessage::new(message, source));\n      array.set_index(scope, i.try_into().unwrap(), message_object.into());\n    }\n\n    let object: v8::Local<v8::Object> = array.into();\n    object\n      .set_integrity_level(scope, v8::IntegrityLevel::Frozen)\n      .unwrap();\n\n    Self {\n      messages: v8::Global::new(scope, object),\n    }\n  }\n}\n\n#[derive(WebIDL, Clone)]\n#[webidl(enum)]\npub(crate) enum GPUCompilationMessageType {\n  Error,\n  Warning,\n  Info,\n}\n"
  },
  {
    "path": "deno_webgpu/surface.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::cell::RefCell;\n\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_core::_ops::make_cppgc_object;\nuse deno_core::cppgc::Ptr;\nuse deno_core::op2;\nuse deno_core::v8;\nuse deno_error::JsErrorBox;\nuse wgpu_types::SurfaceStatus;\n\nuse crate::device::GPUDevice;\nuse crate::error::GPUGenericError;\nuse crate::texture::GPUTexture;\nuse crate::texture::GPUTextureFormat;\nuse crate::webidl::GPUTextureUsageFlags;\n\n#[derive(Debug, thiserror::Error, deno_error::JsError)]\npub enum SurfaceError {\n  #[class(\"DOMExceptionInvalidStateError\")]\n  #[error(\"Context is not configured\")]\n  UnconfiguredContext,\n  #[class(generic)]\n  #[error(\"Invalid Surface Status\")]\n  InvalidStatus,\n  #[class(generic)]\n  #[error(transparent)]\n  Surface(#[from] wgpu_core::present::SurfaceError),\n}\n\npub struct Configuration {\n  pub device: Ptr<GPUDevice>,\n  pub usage: GPUTextureUsageFlags,\n  pub format: GPUTextureFormat,\n  pub surface_config:\n    wgpu_types::SurfaceConfiguration<Vec<wgpu_types::TextureFormat>>,\n}\n\npub struct GPUCanvasContext {\n  pub surface_id: wgpu_core::id::SurfaceId,\n  pub width: RefCell<u32>,\n  pub height: RefCell<u32>,\n\n  pub config: RefCell<Option<Configuration>>,\n  pub texture: RefCell<Option<v8::Global<v8::Object>>>,\n\n  pub canvas: v8::Global<v8::Object>,\n}\n\nimpl GarbageCollected for GPUCanvasContext {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUCanvasContext\"\n  }\n}\n\n#[op2]\nimpl GPUCanvasContext {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUCanvasContext, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[global]\n  fn canvas(&self) -> v8::Global<v8::Object> {\n    self.canvas.clone()\n  }\n\n  #[undefined]\n  fn configure(\n    &self,\n    #[webidl] configuration: GPUCanvasConfiguration,\n  ) -> Result<(), JsErrorBox> {\n    let format = configuration.format.clone().into();\n    let conf = wgpu_types::SurfaceConfiguration {\n      usage: configuration.usage.into(),\n      format,\n      width: *self.width.borrow(),\n      height: *self.height.borrow(),\n      present_mode: configuration\n        .present_mode\n        .map(Into::into)\n        .unwrap_or_default(),\n      alpha_mode: configuration.alpha_mode.into(),\n      view_formats: configuration\n        .view_formats\n        .into_iter()\n        .map(Into::into)\n        .collect(),\n      desired_maximum_frame_latency: 2,\n    };\n\n    let device = configuration.device;\n\n    let err =\n      device\n        .instance\n        .surface_configure(self.surface_id, device.id, &conf);\n\n    device.error_handler.push_error(err);\n\n    self.config.borrow_mut().replace(Configuration {\n      device,\n      usage: configuration.usage,\n      format: configuration.format,\n      surface_config: conf,\n    });\n\n    Ok(())\n  }\n\n  #[fast]\n  #[undefined]\n  fn unconfigure(&self) {\n    *self.config.borrow_mut() = None;\n  }\n\n  #[global]\n  fn get_current_texture(\n    &self,\n    scope: &mut v8::HandleScope,\n  ) -> Result<v8::Global<v8::Object>, SurfaceError> {\n    let config = self.config.borrow();\n    let Some(config) = config.as_ref() else {\n      return Err(SurfaceError::UnconfiguredContext);\n    };\n\n    {\n      if let Some(obj) = self.texture.borrow().as_ref() {\n        return Ok(obj.clone());\n      }\n    }\n\n    let output = config\n      .device\n      .instance\n      .surface_get_current_texture(self.surface_id, None)?;\n\n    match output.status {\n      SurfaceStatus::Good | SurfaceStatus::Suboptimal => {\n        let id = output.texture.unwrap();\n\n        let texture = GPUTexture {\n          instance: config.device.instance.clone(),\n          error_handler: config.device.error_handler.clone(),\n          id,\n          default_view_id: Default::default(),\n          label: \"\".to_string(),\n          size: wgpu_types::Extent3d {\n            width: *self.width.borrow(),\n            height: *self.height.borrow(),\n            depth_or_array_layers: 1,\n          },\n          mip_level_count: 0,\n          sample_count: 0,\n          dimension: crate::texture::GPUTextureDimension::D2,\n          format: config.format.clone(),\n          usage: config.usage,\n        };\n        let obj = make_cppgc_object(scope, texture);\n        let obj = v8::Global::new(scope, obj);\n        *self.texture.borrow_mut() = Some(obj.clone());\n\n        Ok(obj)\n      }\n      _ => Err(SurfaceError::InvalidStatus),\n    }\n  }\n}\n\nimpl GPUCanvasContext {\n  pub fn present(&self) -> Result<(), SurfaceError> {\n    let config = self.config.borrow();\n    let Some(config) = config.as_ref() else {\n      return Err(SurfaceError::UnconfiguredContext);\n    };\n\n    config.device.instance.surface_present(self.surface_id)?;\n\n    // next `get_current_texture` call would get a new texture\n    *self.texture.borrow_mut() = None;\n\n    Ok(())\n  }\n\n  pub fn resize_configure(&self, width: u32, height: u32) {\n    self.width.replace(width);\n    self.height.replace(height);\n\n    let mut config = self.config.borrow_mut();\n    let Some(config) = &mut *config else {\n      return;\n    };\n\n    config.surface_config.width = width;\n    config.surface_config.height = height;\n\n    let err = config.device.instance.surface_configure(\n      self.surface_id,\n      config.device.id,\n      &config.surface_config,\n    );\n\n    config.device.error_handler.push_error(err);\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\nstruct GPUCanvasConfiguration {\n  device: Ptr<GPUDevice>,\n  format: GPUTextureFormat,\n  #[webidl(default = GPUTextureUsageFlags(wgpu_types::TextureUsages::RENDER_ATTACHMENT))]\n  usage: GPUTextureUsageFlags,\n  #[webidl(default = GPUCanvasAlphaMode::Opaque)]\n  alpha_mode: GPUCanvasAlphaMode,\n\n  // Extended from spec\n  present_mode: Option<GPUPresentMode>,\n  #[webidl(default = vec![])]\n  view_formats: Vec<GPUTextureFormat>,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\nenum GPUCanvasAlphaMode {\n  Opaque,\n  Premultiplied,\n}\n\nimpl From<GPUCanvasAlphaMode> for wgpu_types::CompositeAlphaMode {\n  fn from(value: GPUCanvasAlphaMode) -> Self {\n    match value {\n      GPUCanvasAlphaMode::Opaque => Self::Opaque,\n      GPUCanvasAlphaMode::Premultiplied => Self::PreMultiplied,\n    }\n  }\n}\n\n// Extended from spec\n#[derive(WebIDL)]\n#[webidl(enum)]\nenum GPUPresentMode {\n  #[webidl(rename = \"autoVsync\")]\n  AutoVsync,\n  #[webidl(rename = \"autoNoVsync\")]\n  AutoNoVsync,\n  #[webidl(rename = \"fifo\")]\n  Fifo,\n  #[webidl(rename = \"fifoRelaxed\")]\n  FifoRelaxed,\n  #[webidl(rename = \"immediate\")]\n  Immediate,\n  #[webidl(rename = \"mailbox\")]\n  Mailbox,\n}\n\nimpl From<GPUPresentMode> for wgpu_types::PresentMode {\n  fn from(value: GPUPresentMode) -> Self {\n    match value {\n      GPUPresentMode::AutoVsync => Self::AutoVsync,\n      GPUPresentMode::AutoNoVsync => Self::AutoNoVsync,\n      GPUPresentMode::Fifo => Self::Fifo,\n      GPUPresentMode::FifoRelaxed => Self::FifoRelaxed,\n      GPUPresentMode::Immediate => Self::Immediate,\n      GPUPresentMode::Mailbox => Self::Mailbox,\n    }\n  }\n}\n"
  },
  {
    "path": "deno_webgpu/texture.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::sync::OnceLock;\n\nuse deno_core::op2;\nuse deno_core::webidl::WebIdlInterfaceConverter;\nuse deno_core::GarbageCollected;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\nuse wgpu_types::AstcBlock;\nuse wgpu_types::AstcChannel;\nuse wgpu_types::Extent3d;\nuse wgpu_types::TextureAspect;\nuse wgpu_types::TextureDimension;\nuse wgpu_types::TextureFormat;\nuse wgpu_types::TextureViewDimension;\n\nuse crate::error::GPUGenericError;\nuse crate::webidl::GPUTextureUsageFlags;\nuse crate::Instance;\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUTextureDescriptor {\n  #[webidl(default = String::new())]\n  pub label: String,\n\n  pub size: super::webidl::GPUExtent3D,\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  pub mip_level_count: u32,\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  pub sample_count: u32,\n  #[webidl(default = GPUTextureDimension::D2)]\n  pub dimension: GPUTextureDimension,\n  pub format: GPUTextureFormat,\n  pub usage: GPUTextureUsageFlags,\n  #[webidl(default = vec![])]\n  pub view_formats: Vec<GPUTextureFormat>,\n}\n\npub struct GPUTexture {\n  pub instance: Instance,\n  pub error_handler: super::error::ErrorHandler,\n\n  pub id: wgpu_core::id::TextureId,\n  pub default_view_id: OnceLock<wgpu_core::id::TextureViewId>,\n\n  pub label: String,\n\n  pub size: Extent3d,\n  pub mip_level_count: u32,\n  pub sample_count: u32,\n  pub dimension: GPUTextureDimension,\n  pub format: GPUTextureFormat,\n  pub usage: GPUTextureUsageFlags,\n}\n\nimpl GPUTexture {\n  pub(crate) fn default_view_id(&self) -> wgpu_core::id::TextureViewId {\n    *self.default_view_id.get_or_init(|| {\n      let (id, err) =\n        self\n          .instance\n          .texture_create_view(self.id, &Default::default(), None);\n      if let Some(err) = err {\n        use wgpu_types::error::WebGpuError;\n        assert_ne!(\n          err.webgpu_error_type(),\n          wgpu_types::error::ErrorType::Validation,\n          concat!(\n            \"getting default view for a texture \",\n            \"caused a validation error (!?)\"\n          )\n        );\n        self.error_handler.push_error(Some(err));\n      }\n      id\n    })\n  }\n}\n\nimpl Drop for GPUTexture {\n  fn drop(&mut self) {\n    if let Some(id) = self.default_view_id.take() {\n      self.instance.texture_view_drop(id);\n    }\n    self.instance.texture_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUTexture {\n  const NAME: &'static str = \"GPUTexture\";\n}\n\nimpl GarbageCollected for GPUTexture {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUTexture\"\n  }\n}\n\n#[op2]\nimpl GPUTexture {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUTexture, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n\n  #[getter]\n  fn width(&self) -> u32 {\n    self.size.width\n  }\n  #[getter]\n  fn height(&self) -> u32 {\n    self.size.height\n  }\n  #[getter]\n  fn depth_or_array_layers(&self) -> u32 {\n    self.size.depth_or_array_layers\n  }\n  #[getter]\n  fn mip_level_count(&self) -> u32 {\n    self.mip_level_count\n  }\n  #[getter]\n  fn sample_count(&self) -> u32 {\n    self.sample_count\n  }\n  #[getter]\n  #[string]\n  fn dimension(&self) -> &'static str {\n    self.dimension.as_str()\n  }\n  #[getter]\n  #[string]\n  fn format(&self) -> &'static str {\n    self.format.as_str()\n  }\n  #[getter]\n  fn usage(&self) -> u32 {\n    self.usage.bits()\n  }\n  #[fast]\n  #[undefined]\n  fn destroy(&self) {\n    self.instance.texture_destroy(self.id);\n  }\n\n  #[cppgc]\n  fn create_view(\n    &self,\n    #[webidl] descriptor: GPUTextureViewDescriptor,\n  ) -> Result<GPUTextureView, JsErrorBox> {\n    let wgpu_descriptor = wgpu_core::resource::TextureViewDescriptor {\n      label: crate::transform_label(descriptor.label.clone()),\n      format: descriptor.format.map(Into::into),\n      dimension: descriptor.dimension.map(Into::into),\n      usage: Some(descriptor.usage.into()),\n      range: wgpu_types::ImageSubresourceRange {\n        aspect: descriptor.aspect.into(),\n        base_mip_level: descriptor.base_mip_level,\n        mip_level_count: descriptor.mip_level_count,\n        base_array_layer: descriptor.base_array_layer,\n        array_layer_count: descriptor.array_layer_count,\n      },\n    };\n\n    let (id, err) =\n      self\n        .instance\n        .texture_create_view(self.id, &wgpu_descriptor, None);\n\n    self.error_handler.push_error(err);\n\n    Ok(GPUTextureView {\n      instance: self.instance.clone(),\n      id,\n      label: descriptor.label,\n    })\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\nstruct GPUTextureViewDescriptor {\n  #[webidl(default = String::new())]\n  label: String,\n\n  format: Option<GPUTextureFormat>,\n  dimension: Option<GPUTextureViewDimension>,\n  #[webidl(default = GPUTextureUsageFlags(wgpu_types::TextureUsages::empty()))]\n  usage: GPUTextureUsageFlags,\n  #[webidl(default = GPUTextureAspect::All)]\n  aspect: GPUTextureAspect,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  base_mip_level: u32,\n  #[options(enforce_range = true)]\n  mip_level_count: Option<u32>,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  base_array_layer: u32,\n  #[options(enforce_range = true)]\n  array_layer_count: Option<u32>,\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUTextureViewDimension {\n  #[webidl(rename = \"1d\")]\n  D1,\n  #[webidl(rename = \"2d\")]\n  D2,\n  #[webidl(rename = \"2d-array\")]\n  D2Array,\n  #[webidl(rename = \"cube\")]\n  Cube,\n  #[webidl(rename = \"cube-array\")]\n  CubeArray,\n  #[webidl(rename = \"3d\")]\n  D3,\n}\n\nimpl From<GPUTextureViewDimension> for TextureViewDimension {\n  fn from(value: GPUTextureViewDimension) -> Self {\n    match value {\n      GPUTextureViewDimension::D1 => Self::D1,\n      GPUTextureViewDimension::D2 => Self::D2,\n      GPUTextureViewDimension::D3 => Self::D3,\n      GPUTextureViewDimension::D2Array => Self::D2Array,\n      GPUTextureViewDimension::Cube => Self::Cube,\n      GPUTextureViewDimension::CubeArray => Self::CubeArray,\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub enum GPUTextureAspect {\n  All,\n  StencilOnly,\n  DepthOnly,\n}\n\nimpl From<GPUTextureAspect> for TextureAspect {\n  fn from(value: GPUTextureAspect) -> Self {\n    match value {\n      GPUTextureAspect::All => Self::All,\n      GPUTextureAspect::StencilOnly => Self::StencilOnly,\n      GPUTextureAspect::DepthOnly => Self::DepthOnly,\n    }\n  }\n}\n\npub struct GPUTextureView {\n  pub instance: Instance,\n  pub id: wgpu_core::id::TextureViewId,\n  pub label: String,\n}\n\nimpl Drop for GPUTextureView {\n  fn drop(&mut self) {\n    self.instance.texture_view_drop(self.id);\n  }\n}\n\nimpl WebIdlInterfaceConverter for GPUTextureView {\n  const NAME: &'static str = \"GPUTextureView\";\n}\n\nimpl GarbageCollected for GPUTextureView {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUTextureView\"\n  }\n}\n// TODO(@crowlKats): weakref in texture for view\n\n#[op2]\nimpl GPUTextureView {\n  #[constructor]\n  #[cppgc]\n  fn constructor(_: bool) -> Result<GPUTextureView, GPUGenericError> {\n    Err(GPUGenericError::InvalidConstructor)\n  }\n\n  #[getter]\n  #[string]\n  fn label(&self) -> String {\n    self.label.clone()\n  }\n  #[setter]\n  #[string]\n  fn label(&self, #[webidl] _label: String) {\n    // TODO(@crowlKats): no-op, needs wpgu to implement changing the label\n  }\n}\n\n#[derive(WebIDL, Clone)]\n#[webidl(enum)]\npub enum GPUTextureDimension {\n  #[webidl(rename = \"1d\")]\n  D1,\n  #[webidl(rename = \"2d\")]\n  D2,\n  #[webidl(rename = \"3d\")]\n  D3,\n}\n\nimpl From<GPUTextureDimension> for TextureDimension {\n  fn from(value: GPUTextureDimension) -> Self {\n    match value {\n      GPUTextureDimension::D1 => Self::D1,\n      GPUTextureDimension::D2 => Self::D2,\n      GPUTextureDimension::D3 => Self::D3,\n    }\n  }\n}\n\n#[derive(WebIDL, Clone)]\n#[webidl(enum)]\npub(crate) enum GPUTextureFormat {\n  #[webidl(rename = \"r8unorm\")]\n  R8unorm,\n  #[webidl(rename = \"r8snorm\")]\n  R8snorm,\n  #[webidl(rename = \"r8uint\")]\n  R8uint,\n  #[webidl(rename = \"r8sint\")]\n  R8sint,\n  #[webidl(rename = \"r16uint\")]\n  R16uint,\n  #[webidl(rename = \"r16sint\")]\n  R16sint,\n  #[webidl(rename = \"r16float\")]\n  R16float,\n  #[webidl(rename = \"rg8unorm\")]\n  Rg8unorm,\n  #[webidl(rename = \"rg8snorm\")]\n  Rg8snorm,\n  #[webidl(rename = \"rg8uint\")]\n  Rg8uint,\n  #[webidl(rename = \"rg8sint\")]\n  Rg8sint,\n  #[webidl(rename = \"r32uint\")]\n  R32uint,\n  #[webidl(rename = \"r32sint\")]\n  R32sint,\n  #[webidl(rename = \"r32float\")]\n  R32float,\n  #[webidl(rename = \"rg16uint\")]\n  Rg16uint,\n  #[webidl(rename = \"rg16sint\")]\n  Rg16sint,\n  #[webidl(rename = \"rg16float\")]\n  Rg16float,\n  #[webidl(rename = \"rgba8unorm\")]\n  Rgba8unorm,\n  #[webidl(rename = \"rgba8unorm-srgb\")]\n  Rgba8unormSrgb,\n  #[webidl(rename = \"rgba8snorm\")]\n  Rgba8snorm,\n  #[webidl(rename = \"rgba8uint\")]\n  Rgba8uint,\n  #[webidl(rename = \"rgba8sint\")]\n  Rgba8sint,\n  #[webidl(rename = \"bgra8unorm\")]\n  Bgra8unorm,\n  #[webidl(rename = \"bgra8unorm-srgb\")]\n  Bgra8unormSrgb,\n  #[webidl(rename = \"rgb9e5ufloat\")]\n  Rgb9e5ufloat,\n  #[webidl(rename = \"rgb10a2uint\")]\n  Rgb10a2uint,\n  #[webidl(rename = \"rgb10a2unorm\")]\n  Rgb10a2unorm,\n  #[webidl(rename = \"rg11b10ufloat\")]\n  Rg11b10ufloat,\n  #[webidl(rename = \"rg32uint\")]\n  Rg32uint,\n  #[webidl(rename = \"rg32sint\")]\n  Rg32sint,\n  #[webidl(rename = \"rg32float\")]\n  Rg32float,\n  #[webidl(rename = \"rgba16uint\")]\n  Rgba16uint,\n  #[webidl(rename = \"rgba16sint\")]\n  Rgba16sint,\n  #[webidl(rename = \"rgba16float\")]\n  Rgba16float,\n  #[webidl(rename = \"rgba32uint\")]\n  Rgba32uint,\n  #[webidl(rename = \"rgba32sint\")]\n  Rgba32sint,\n  #[webidl(rename = \"rgba32float\")]\n  Rgba32float,\n  #[webidl(rename = \"stencil8\")]\n  Stencil8,\n  #[webidl(rename = \"depth16unorm\")]\n  Depth16unorm,\n  #[webidl(rename = \"depth24plus\")]\n  Depth24plus,\n  #[webidl(rename = \"depth24plus-stencil8\")]\n  Depth24plusStencil8,\n  #[webidl(rename = \"depth32float\")]\n  Depth32float,\n  #[webidl(rename = \"depth32float-stencil8\")]\n  Depth32floatStencil8,\n  #[webidl(rename = \"bc1-rgba-unorm\")]\n  Bc1RgbaUnorm,\n  #[webidl(rename = \"bc1-rgba-unorm-srgb\")]\n  Bc1RgbaUnormSrgb,\n  #[webidl(rename = \"bc2-rgba-unorm\")]\n  Bc2RgbaUnorm,\n  #[webidl(rename = \"bc2-rgba-unorm-srgb\")]\n  Bc2RgbaUnormSrgb,\n  #[webidl(rename = \"bc3-rgba-unorm\")]\n  Bc3RgbaUnorm,\n  #[webidl(rename = \"bc3-rgba-unorm-srgb\")]\n  Bc3RgbaUnormSrgb,\n  #[webidl(rename = \"bc4-r-unorm\")]\n  Bc4RUnorm,\n  #[webidl(rename = \"bc4-r-snorm\")]\n  Bc4RSnorm,\n  #[webidl(rename = \"bc5-rg-unorm\")]\n  Bc5RgUnorm,\n  #[webidl(rename = \"bc5-rg-snorm\")]\n  Bc5RgSnorm,\n  #[webidl(rename = \"bc6h-rgb-ufloat\")]\n  Bc6hRgbUfloat,\n  #[webidl(rename = \"bc6h-rgb-float\")]\n  Bc6hRgbFloat,\n  #[webidl(rename = \"bc7-rgba-unorm\")]\n  Bc7RgbaUnorm,\n  #[webidl(rename = \"bc7-rgba-unorm-srgb\")]\n  Bc7RgbaUnormSrgb,\n  #[webidl(rename = \"etc2-rgb8unorm\")]\n  Etc2Rgb8unorm,\n  #[webidl(rename = \"etc2-rgb8unorm-srgb\")]\n  Etc2Rgb8unormSrgb,\n  #[webidl(rename = \"etc2-rgb8a1unorm\")]\n  Etc2Rgb8a1unorm,\n  #[webidl(rename = \"etc2-rgb8a1unorm-srgb\")]\n  Etc2Rgb8a1unormSrgb,\n  #[webidl(rename = \"etc2-rgba8unorm\")]\n  Etc2Rgba8unorm,\n  #[webidl(rename = \"etc2-rgba8unorm-srgb\")]\n  Etc2Rgba8unormSrgb,\n  #[webidl(rename = \"eac-r11unorm\")]\n  EacR11unorm,\n  #[webidl(rename = \"eac-r11snorm\")]\n  EacR11snorm,\n  #[webidl(rename = \"eac-rg11unorm\")]\n  EacRg11unorm,\n  #[webidl(rename = \"eac-rg11snorm\")]\n  EacRg11snorm,\n  #[webidl(rename = \"astc-4x4-unorm\")]\n  Astc4x4Unorm,\n  #[webidl(rename = \"astc-4x4-unorm-srgb\")]\n  Astc4x4UnormSrgb,\n  #[webidl(rename = \"astc-5x4-unorm\")]\n  Astc5x4Unorm,\n  #[webidl(rename = \"astc-5x4-unorm-srgb\")]\n  Astc5x4UnormSrgb,\n  #[webidl(rename = \"astc-5x5-unorm\")]\n  Astc5x5Unorm,\n  #[webidl(rename = \"astc-5x5-unorm-srgb\")]\n  Astc5x5UnormSrgb,\n  #[webidl(rename = \"astc-6x5-unorm\")]\n  Astc6x5Unorm,\n  #[webidl(rename = \"astc-6x5-unorm-srgb\")]\n  Astc6x5UnormSrgb,\n  #[webidl(rename = \"astc-6x6-unorm\")]\n  Astc6x6Unorm,\n  #[webidl(rename = \"astc-6x6-unorm-srgb\")]\n  Astc6x6UnormSrgb,\n  #[webidl(rename = \"astc-8x5-unorm\")]\n  Astc8x5Unorm,\n  #[webidl(rename = \"astc-8x5-unorm-srgb\")]\n  Astc8x5UnormSrgb,\n  #[webidl(rename = \"astc-8x6-unorm\")]\n  Astc8x6Unorm,\n  #[webidl(rename = \"astc-8x6-unorm-srgb\")]\n  Astc8x6UnormSrgb,\n  #[webidl(rename = \"astc-8x8-unorm\")]\n  Astc8x8Unorm,\n  #[webidl(rename = \"astc-8x8-unorm-srgb\")]\n  Astc8x8UnormSrgb,\n  #[webidl(rename = \"astc-10x5-unorm\")]\n  Astc10x5Unorm,\n  #[webidl(rename = \"astc-10x5-unorm-srgb\")]\n  Astc10x5UnormSrgb,\n  #[webidl(rename = \"astc-10x6-unorm\")]\n  Astc10x6Unorm,\n  #[webidl(rename = \"astc-10x6-unorm-srgb\")]\n  Astc10x6UnormSrgb,\n  #[webidl(rename = \"astc-10x8-unorm\")]\n  Astc10x8Unorm,\n  #[webidl(rename = \"astc-10x8-unorm-srgb\")]\n  Astc10x8UnormSrgb,\n  #[webidl(rename = \"astc-10x10-unorm\")]\n  Astc10x10Unorm,\n  #[webidl(rename = \"astc-10x10-unorm-srgb\")]\n  Astc10x10UnormSrgb,\n  #[webidl(rename = \"astc-12x10-unorm\")]\n  Astc12x10Unorm,\n  #[webidl(rename = \"astc-12x10-unorm-srgb\")]\n  Astc12x10UnormSrgb,\n  #[webidl(rename = \"astc-12x12-unorm\")]\n  Astc12x12Unorm,\n  #[webidl(rename = \"astc-12x12-unorm-srgb\")]\n  Astc12x12UnormSrgb,\n}\n\nimpl From<GPUTextureFormat> for TextureFormat {\n  fn from(value: GPUTextureFormat) -> Self {\n    match value {\n      GPUTextureFormat::R8unorm => Self::R8Unorm,\n      GPUTextureFormat::R8snorm => Self::R8Snorm,\n      GPUTextureFormat::R8uint => Self::R8Uint,\n      GPUTextureFormat::R8sint => Self::R8Sint,\n      GPUTextureFormat::R16uint => Self::R16Uint,\n      GPUTextureFormat::R16sint => Self::R16Sint,\n      GPUTextureFormat::R16float => Self::R16Float,\n      GPUTextureFormat::Rg8unorm => Self::Rg8Unorm,\n      GPUTextureFormat::Rg8snorm => Self::Rg8Snorm,\n      GPUTextureFormat::Rg8uint => Self::Rg8Uint,\n      GPUTextureFormat::Rg8sint => Self::Rg8Sint,\n      GPUTextureFormat::R32uint => Self::R32Uint,\n      GPUTextureFormat::R32sint => Self::R32Sint,\n      GPUTextureFormat::R32float => Self::R32Float,\n      GPUTextureFormat::Rg16uint => Self::Rg16Uint,\n      GPUTextureFormat::Rg16sint => Self::Rg16Sint,\n      GPUTextureFormat::Rg16float => Self::Rg16Float,\n      GPUTextureFormat::Rgba8unorm => Self::Rgba8Unorm,\n      GPUTextureFormat::Rgba8unormSrgb => Self::Rgba8UnormSrgb,\n      GPUTextureFormat::Rgba8snorm => Self::Rgba8Snorm,\n      GPUTextureFormat::Rgba8uint => Self::Rgba8Uint,\n      GPUTextureFormat::Rgba8sint => Self::Rgba8Sint,\n      GPUTextureFormat::Bgra8unorm => Self::Bgra8Unorm,\n      GPUTextureFormat::Bgra8unormSrgb => Self::Bgra8UnormSrgb,\n      GPUTextureFormat::Rgb9e5ufloat => Self::Rgb9e5Ufloat,\n      GPUTextureFormat::Rgb10a2uint => Self::Rgb10a2Uint,\n      GPUTextureFormat::Rgb10a2unorm => Self::Rgb10a2Unorm,\n      GPUTextureFormat::Rg11b10ufloat => Self::Rg11b10Ufloat,\n      GPUTextureFormat::Rg32uint => Self::Rg32Uint,\n      GPUTextureFormat::Rg32sint => Self::Rg32Sint,\n      GPUTextureFormat::Rg32float => Self::Rg32Float,\n      GPUTextureFormat::Rgba16uint => Self::Rgba16Uint,\n      GPUTextureFormat::Rgba16sint => Self::Rgba16Sint,\n      GPUTextureFormat::Rgba16float => Self::Rgba16Float,\n      GPUTextureFormat::Rgba32uint => Self::Rgba32Uint,\n      GPUTextureFormat::Rgba32sint => Self::Rgba32Sint,\n      GPUTextureFormat::Rgba32float => Self::Rgba32Float,\n      GPUTextureFormat::Stencil8 => Self::Stencil8,\n      GPUTextureFormat::Depth16unorm => Self::Depth16Unorm,\n      GPUTextureFormat::Depth24plus => Self::Depth24Plus,\n      GPUTextureFormat::Depth24plusStencil8 => Self::Depth24PlusStencil8,\n      GPUTextureFormat::Depth32float => Self::Depth32Float,\n      GPUTextureFormat::Depth32floatStencil8 => Self::Depth32FloatStencil8,\n      GPUTextureFormat::Bc1RgbaUnorm => Self::Bc1RgbaUnorm,\n      GPUTextureFormat::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnormSrgb,\n      GPUTextureFormat::Bc2RgbaUnorm => Self::Bc2RgbaUnorm,\n      GPUTextureFormat::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnormSrgb,\n      GPUTextureFormat::Bc3RgbaUnorm => Self::Bc3RgbaUnorm,\n      GPUTextureFormat::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnormSrgb,\n      GPUTextureFormat::Bc4RUnorm => Self::Bc4RUnorm,\n      GPUTextureFormat::Bc4RSnorm => Self::Bc4RSnorm,\n      GPUTextureFormat::Bc5RgUnorm => Self::Bc5RgUnorm,\n      GPUTextureFormat::Bc5RgSnorm => Self::Bc5RgSnorm,\n      GPUTextureFormat::Bc6hRgbUfloat => Self::Bc6hRgbUfloat,\n      GPUTextureFormat::Bc6hRgbFloat => Self::Bc6hRgbFloat,\n      GPUTextureFormat::Bc7RgbaUnorm => Self::Bc7RgbaUnorm,\n      GPUTextureFormat::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnormSrgb,\n      GPUTextureFormat::Etc2Rgb8unorm => Self::Etc2Rgb8Unorm,\n      GPUTextureFormat::Etc2Rgb8unormSrgb => Self::Etc2Rgb8UnormSrgb,\n      GPUTextureFormat::Etc2Rgb8a1unorm => Self::Etc2Rgb8A1Unorm,\n      GPUTextureFormat::Etc2Rgb8a1unormSrgb => Self::Etc2Rgb8A1UnormSrgb,\n      GPUTextureFormat::Etc2Rgba8unorm => Self::Etc2Rgba8Unorm,\n      GPUTextureFormat::Etc2Rgba8unormSrgb => Self::Etc2Rgba8UnormSrgb,\n      GPUTextureFormat::EacR11unorm => Self::EacR11Unorm,\n      GPUTextureFormat::EacR11snorm => Self::EacR11Snorm,\n      GPUTextureFormat::EacRg11unorm => Self::EacRg11Unorm,\n      GPUTextureFormat::EacRg11snorm => Self::EacRg11Snorm,\n      GPUTextureFormat::Astc4x4Unorm => Self::Astc {\n        block: AstcBlock::B4x4,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc {\n        block: AstcBlock::B4x4,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc5x4Unorm => Self::Astc {\n        block: AstcBlock::B5x4,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc5x4UnormSrgb => Self::Astc {\n        block: AstcBlock::B5x4,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc5x5Unorm => Self::Astc {\n        block: AstcBlock::B5x5,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc5x5UnormSrgb => Self::Astc {\n        block: AstcBlock::B5x5,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc6x5Unorm => Self::Astc {\n        block: AstcBlock::B6x5,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc6x5UnormSrgb => Self::Astc {\n        block: AstcBlock::B6x5,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc6x6Unorm => Self::Astc {\n        block: AstcBlock::B6x6,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc6x6UnormSrgb => Self::Astc {\n        block: AstcBlock::B6x6,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc8x5Unorm => Self::Astc {\n        block: AstcBlock::B8x5,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc8x5UnormSrgb => Self::Astc {\n        block: AstcBlock::B8x5,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc8x6Unorm => Self::Astc {\n        block: AstcBlock::B8x6,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc8x6UnormSrgb => Self::Astc {\n        block: AstcBlock::B8x6,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc8x8Unorm => Self::Astc {\n        block: AstcBlock::B8x8,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc8x8UnormSrgb => Self::Astc {\n        block: AstcBlock::B8x8,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc10x5Unorm => Self::Astc {\n        block: AstcBlock::B10x5,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc10x5UnormSrgb => Self::Astc {\n        block: AstcBlock::B10x5,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc10x6Unorm => Self::Astc {\n        block: AstcBlock::B10x6,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc10x6UnormSrgb => Self::Astc {\n        block: AstcBlock::B10x6,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc10x8Unorm => Self::Astc {\n        block: AstcBlock::B10x8,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc10x8UnormSrgb => Self::Astc {\n        block: AstcBlock::B10x8,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc10x10Unorm => Self::Astc {\n        block: AstcBlock::B10x10,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc10x10UnormSrgb => Self::Astc {\n        block: AstcBlock::B10x10,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc12x10Unorm => Self::Astc {\n        block: AstcBlock::B12x10,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc12x10UnormSrgb => Self::Astc {\n        block: AstcBlock::B12x10,\n        channel: AstcChannel::UnormSrgb,\n      },\n      GPUTextureFormat::Astc12x12Unorm => Self::Astc {\n        block: AstcBlock::B12x12,\n        channel: AstcChannel::Unorm,\n      },\n      GPUTextureFormat::Astc12x12UnormSrgb => Self::Astc {\n        block: AstcBlock::B12x12,\n        channel: AstcChannel::UnormSrgb,\n      },\n    }\n  }\n}\n\npub struct GPUExternalTexture {\n  pub id: wgpu_core::id::ExternalTextureId,\n}\n\nimpl WebIdlInterfaceConverter for GPUExternalTexture {\n  const NAME: &'static str = \"GPUExternalTexture\";\n}\n\nimpl GarbageCollected for GPUExternalTexture {\n  fn get_name(&self) -> &'static std::ffi::CStr {\n    c\"GPUExternalTexture\"\n  }\n}\n\n#[op2]\nimpl GPUExternalTexture {}\n"
  },
  {
    "path": "deno_webgpu/webidl.rs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nuse std::borrow::Cow;\n\nuse deno_core::cppgc::Ptr;\nuse deno_core::v8;\nuse deno_core::webidl::ContextFn;\nuse deno_core::webidl::IntOptions;\nuse deno_core::webidl::WebIdlConverter;\nuse deno_core::webidl::WebIdlError;\nuse deno_core::webidl::WebIdlErrorKind;\nuse deno_core::WebIDL;\nuse deno_error::JsErrorBox;\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUExtent3DDict {\n  #[options(enforce_range = true)]\n  width: u32,\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  height: u32,\n  #[webidl(default = 1)]\n  #[options(enforce_range = true)]\n  depth_or_array_layers: u32,\n}\n\npub(crate) enum GPUExtent3D {\n  Dict(GPUExtent3DDict),\n  Sequence((u32, u32, u32)),\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUExtent3D {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    if value.is_null_or_undefined() {\n      return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(\n        scope,\n        value,\n        prefix,\n        context.borrowed(),\n        options,\n      )?));\n    }\n    if let Ok(obj) = value.try_cast::<v8::Object>() {\n      let iter = v8::Symbol::get_iterator(scope);\n      if let Some(iter) = obj.get(scope, iter.into()) {\n        if !iter.is_undefined() {\n          let conv = <Vec<u32>>::convert(\n            scope,\n            value,\n            prefix.clone(),\n            context.borrowed(),\n            &IntOptions {\n              clamp: false,\n              enforce_range: true,\n            },\n          )?;\n          if conv.is_empty() || conv.len() > 3 {\n            return Err(WebIdlError::other(\n              prefix,\n              context,\n              JsErrorBox::type_error(format!(\n                \"A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements\",\n                conv.len()\n              )),\n            ));\n          }\n\n          let mut iter = conv.into_iter();\n          return Ok(GPUExtent3D::Sequence((\n            iter.next().unwrap(),\n            iter.next().unwrap_or(1),\n            iter.next().unwrap_or(1),\n          )));\n        }\n      }\n\n      return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(\n        scope, value, prefix, context, options,\n      )?));\n    }\n\n    Err(WebIdlError::new(\n      prefix,\n      context,\n      WebIdlErrorKind::ConvertToConverterType(\n        \"sequence<GPUIntegerCoordinate> or GPUExtent3DDict\",\n      ),\n    ))\n  }\n}\n\nimpl From<GPUExtent3D> for wgpu_types::Extent3d {\n  fn from(value: GPUExtent3D) -> Self {\n    match value {\n      GPUExtent3D::Dict(dict) => Self {\n        width: dict.width,\n        height: dict.height,\n        depth_or_array_layers: dict.depth_or_array_layers,\n      },\n      GPUExtent3D::Sequence((width, height, depth)) => Self {\n        width,\n        height,\n        depth_or_array_layers: depth,\n      },\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUOrigin3DDict {\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  x: u32,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  y: u32,\n  #[webidl(default = 0)]\n  #[options(enforce_range = true)]\n  z: u32,\n}\n\npub(crate) enum GPUOrigin3D {\n  Dict(GPUOrigin3DDict),\n  Sequence((u32, u32, u32)),\n}\n\nimpl Default for GPUOrigin3D {\n  fn default() -> Self {\n    GPUOrigin3D::Sequence((0, 0, 0))\n  }\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUOrigin3D {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    if value.is_null_or_undefined() {\n      return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(\n        scope,\n        value,\n        prefix,\n        context.borrowed(),\n        options,\n      )?));\n    }\n    if let Ok(obj) = value.try_cast::<v8::Object>() {\n      let iter = v8::Symbol::get_iterator(scope);\n      if let Some(iter) = obj.get(scope, iter.into()) {\n        if !iter.is_undefined() {\n          let conv = <Vec<u32>>::convert(\n            scope,\n            value,\n            prefix.clone(),\n            context.borrowed(),\n            &IntOptions {\n              clamp: false,\n              enforce_range: true,\n            },\n          )?;\n          if conv.len() > 3 {\n            return Err(WebIdlError::other(\n              prefix,\n              context,\n              JsErrorBox::type_error(format!(\n                \"A sequence of number used as a GPUOrigin3D must have at most 3 elements, received {} elements\",\n                conv.len()\n              )),\n            ));\n          }\n\n          let mut iter = conv.into_iter();\n          return Ok(GPUOrigin3D::Sequence((\n            iter.next().unwrap_or(0),\n            iter.next().unwrap_or(0),\n            iter.next().unwrap_or(0),\n          )));\n        }\n      }\n\n      return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(\n        scope, value, prefix, context, options,\n      )?));\n    }\n\n    Err(WebIdlError::new(\n      prefix,\n      context,\n      WebIdlErrorKind::ConvertToConverterType(\n        \"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict\",\n      ),\n    ))\n  }\n}\n\nimpl From<GPUOrigin3D> for wgpu_types::Origin3d {\n  fn from(value: GPUOrigin3D) -> Self {\n    match value {\n      GPUOrigin3D::Dict(dict) => Self {\n        x: dict.x,\n        y: dict.y,\n        z: dict.z,\n      },\n      GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z },\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(dictionary)]\npub(crate) struct GPUColorDict {\n  r: f64,\n  g: f64,\n  b: f64,\n  a: f64,\n}\n\npub(crate) enum GPUColor {\n  Dict(GPUColorDict),\n  Sequence((f64, f64, f64, f64)),\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUColor {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    if value.is_null_or_undefined() {\n      return Ok(GPUColor::Dict(GPUColorDict::convert(\n        scope,\n        value,\n        prefix,\n        context.borrowed(),\n        options,\n      )?));\n    }\n    if let Ok(obj) = value.try_cast::<v8::Object>() {\n      let iter = v8::Symbol::get_iterator(scope);\n      if let Some(iter) = obj.get(scope, iter.into()) {\n        if !iter.is_undefined() {\n          let conv = <Vec<f64>>::convert(\n            scope,\n            value,\n            prefix.clone(),\n            context.borrowed(),\n            options,\n          )?;\n          if conv.len() != 4 {\n            return Err(WebIdlError::other(\n              prefix,\n              context,\n              JsErrorBox::type_error(format!(\n                \"A sequence of number used as a GPUColor must have exactly 4 elements, received {} elements\",\n                conv.len()\n              )),\n            ));\n          }\n\n          let mut iter = conv.into_iter();\n          return Ok(GPUColor::Sequence((\n            iter.next().unwrap(),\n            iter.next().unwrap(),\n            iter.next().unwrap(),\n            iter.next().unwrap(),\n          )));\n        }\n      }\n\n      return Ok(GPUColor::Dict(GPUColorDict::convert(\n        scope, value, prefix, context, options,\n      )?));\n    }\n\n    Err(WebIdlError::new(\n      prefix,\n      context,\n      WebIdlErrorKind::ConvertToConverterType(\n        \"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict\",\n      ),\n    ))\n  }\n}\n\nimpl From<GPUColor> for wgpu_types::Color {\n  fn from(value: GPUColor) -> Self {\n    match value {\n      GPUColor::Dict(dict) => Self {\n        r: dict.r,\n        g: dict.g,\n        b: dict.b,\n        a: dict.a,\n      },\n      GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a },\n    }\n  }\n}\n\n#[derive(WebIDL)]\n#[webidl(enum)]\npub(crate) enum GPUAutoLayoutMode {\n  Auto,\n}\n\npub(crate) enum GPUPipelineLayoutOrGPUAutoLayoutMode {\n  PipelineLayout(Ptr<crate::pipeline_layout::GPUPipelineLayout>),\n  AutoLayoutMode(GPUAutoLayoutMode),\n}\n\nimpl From<GPUPipelineLayoutOrGPUAutoLayoutMode>\n  for Option<wgpu_core::id::PipelineLayoutId>\n{\n  fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self {\n    match value {\n      GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => {\n        Some(layout.id)\n      }\n      GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode(\n        GPUAutoLayoutMode::Auto,\n      ) => None,\n    }\n  }\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUPipelineLayoutOrGPUAutoLayoutMode {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    if value.is_object() {\n      Ok(Self::PipelineLayout(WebIdlConverter::convert(\n        scope, value, prefix, context, options,\n      )?))\n    } else {\n      Ok(Self::AutoLayoutMode(WebIdlConverter::convert(\n        scope, value, prefix, context, options,\n      )?))\n    }\n  }\n}\n\nimpl<'a> WebIdlConverter<'a> for GPUFeatureName {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    _options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    let s = value.to_rust_string_lossy(scope);\n    s.parse().map(Self).map_err(|()| {\n      WebIdlError::new(\n        prefix,\n        context,\n        WebIdlErrorKind::InvalidEnumVariant {\n          converter: \"GPUFeatureName\",\n          variant: s,\n        },\n      )\n    })\n  }\n}\n\n/// A WebGPU optional feature.\n///\n/// Named after the WebIDL enum, which represents features as strings, but we store the\n/// feature as bitflag, which must always have exactly one bit set (across both the WebGPU\n/// and wgpu native features).\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\npub struct GPUFeatureName(wgpu_types::Features);\n\nimpl From<GPUFeatureName> for wgpu_types::Features {\n  fn from(value: GPUFeatureName) -> wgpu_types::Features {\n    value.0\n  }\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct GPUTextureUsageFlags(pub(crate) wgpu_types::TextureUsages);\n\nimpl<'a> WebIdlConverter<'a> for GPUTextureUsageFlags {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    _options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    let flags_value = u32::convert(\n      scope,\n      value,\n      prefix.clone(),\n      context.borrowed(),\n      &IntOptions {\n        clamp: false,\n        enforce_range: true,\n      },\n    )?;\n\n    let flags =\n      wgpu_types::TextureUsages::from_bits(flags_value).ok_or_else(|| {\n        WebIdlError::other(\n          prefix,\n          context,\n          JsErrorBox::type_error(\"usage is not valid\"),\n        )\n      })?;\n\n    Ok(GPUTextureUsageFlags(flags))\n  }\n}\n\nimpl From<GPUTextureUsageFlags> for wgpu_types::TextureUsages {\n  fn from(value: GPUTextureUsageFlags) -> Self {\n    value.0\n  }\n}\n\nimpl GPUTextureUsageFlags {\n  pub fn bits(&self) -> u32 {\n    self.0.bits()\n  }\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct GPUShaderStageFlags(pub(crate) wgpu_types::ShaderStages);\n\nimpl<'a> WebIdlConverter<'a> for GPUShaderStageFlags {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    _options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    let flags_value = u32::convert(\n      scope,\n      value,\n      prefix.clone(),\n      context.borrowed(),\n      &IntOptions {\n        clamp: false,\n        enforce_range: true,\n      },\n    )?;\n\n    let flags =\n      wgpu_types::ShaderStages::from_bits(flags_value).ok_or_else(|| {\n        WebIdlError::other(\n          prefix,\n          context,\n          JsErrorBox::type_error(\"shader stage is not valid\"),\n        )\n      })?;\n\n    Ok(GPUShaderStageFlags(flags))\n  }\n}\n\nimpl From<GPUShaderStageFlags> for wgpu_types::ShaderStages {\n  fn from(value: GPUShaderStageFlags) -> Self {\n    value.0\n  }\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct GPUColorWriteFlags(pub(crate) wgpu_types::ColorWrites);\n\nimpl<'a> WebIdlConverter<'a> for GPUColorWriteFlags {\n  type Options = ();\n\n  fn convert<'b>(\n    scope: &mut v8::HandleScope<'a>,\n    value: v8::Local<'a, v8::Value>,\n    prefix: Cow<'static, str>,\n    context: ContextFn<'b>,\n    _options: &Self::Options,\n  ) -> Result<Self, WebIdlError> {\n    let flags_value = u32::convert(\n      scope,\n      value,\n      prefix.clone(),\n      context.borrowed(),\n      &IntOptions {\n        clamp: false,\n        enforce_range: true,\n      },\n    )?;\n\n    // WebGPU specifies a validation error for invalid color write mask values.\n    // We propagate invalid bits here; wgpu_core will validate it.\n    Ok(GPUColorWriteFlags(\n      wgpu_types::ColorWrites::from_bits_retain(flags_value),\n    ))\n  }\n}\n\nimpl From<GPUColorWriteFlags> for wgpu_types::ColorWrites {\n  fn from(value: GPUColorWriteFlags) -> Self {\n    value.0\n  }\n}\n"
  },
  {
    "path": "docs/api-specs/cooperative_matrix.md",
    "content": "# Cooperative Matrix Extensions\n\n🧪Experimental🧪\n\n`wgpu` supports an experimental cooperative matrix feature when `Features::EXPERIMENTAL_COOPERATIVE_MATRIX` is enabled.\nThis exposes hardware-accelerated matrix multiply-accumulate (MMA) operations (for example, NVIDIA tensor cores,\nMetal SIMD-group matrices, and Vulkan `VK_KHR_cooperative_matrix`).\n\n**Note**: The features documented here may have bugs and are subject to breaking changes. The API and shader\nsemantics are expected to evolve. Please refer to the GitHub issue tracker for the latest status and discussions.\n\n---\n\n## Overview\n\nCooperative matrices allow a **workgroup** (or equivalent execution group) to collectively:\n\n- load small matrix tiles from memory,\n- perform matrix multiply-accumulate operations on those tiles, and\n- store the results back to memory.\n\nConceptually, this is specialized hardware that evaluates:\n\n> `C = A * B + C`\n\nfor relatively small tiles, but at very high throughput compared to composing the same operation from\nscalar/vector instructions.\n\nCooperative matrix operations are most useful in workloads such as:\n\n- machine learning and inference,\n- dense linear algebra and scientific computing,\n- image processing, filtering, and transforms.\n\nThe cooperative nature means that all lanes in the cooperating execution group must participate in\nthe operations; individual invocations cannot diverge.\n\nTypical example:\n\n- `A` is an M×K matrix.\n- `B` is a K×N matrix.\n- `C` is an M×N matrix, acting as the accumulator and result.\n\n---\n\n## Querying hardware support (host side)\n\nBefore using cooperative matrices in shaders, you must query what configurations your hardware and backend support.\n\nOn the `Adapter`, `wgpu` exposes:\n\n- `Adapter::cooperative_matrix_properties() -> Vec<CooperativeMatrixProperties>`\n\nEach `CooperativeMatrixProperties` describes a single supported configuration. Fields are:\n\n- `m_size`: height of matrices A and C (type: `naga::CooperativeSize`)\n- `n_size`: width of matrices B and C (type: `naga::CooperativeSize`)\n- `k_size`: shared inner dimension of A and B (type: `naga::CooperativeSize`)\n- `ab_type`: scalar element type for A and B (type: `naga::Scalar`)\n- `cr_type`: scalar element type for C and the result (type: `naga::Scalar`)\n- `saturating_accumulation`: `bool` indicating whether overflow clamping on accumulation\n  is supported for this configuration\n\nExample usage:\n\n```/dev/null/cooperative-matrix-host.rs#L1-40\nlet coop_props = adapter.cooperative_matrix_properties();\nfor prop in &coop_props {\n    println!(\n        \"{:?}x{:?}x{:?} - AB: {:?}, CR: {:?}, saturating: {}\",\n        prop.m_size, prop.n_size, prop.k_size,\n        prop.ab_type, prop.cr_type,\n        prop.saturating_accumulation,\n    );\n}\n```\n\nYou **must**:\n\n1. Enable `Features::EXPERIMENTAL_COOPERATIVE_MATRIX` on the `Device`.\n2. Query `adapter.cooperative_matrix_properties()` and ensure that the configuration(s) you intend\n   to use in WGSL are actually available on the running adapter/backend.\n3. Treat the sizes and types as a contract between your shaders and the underlying hardware implementation.\n   Using unsupported configurations is an error.\n\n---\n\n## Feature and backend requirements\n\n### `wgpu` feature\n\n- Using cooperative matrices requires enabling:\n\n  - `Features::EXPERIMENTAL_COOPERATIVE_MATRIX`\n\nThis feature may be restricted to certain backends and hardware.\n\n### Hardware / backend notes\n\nThese are general guidelines, not a complete compatibility matrix:\n\n- **Metal**:\n  - Requires Apple7+ (A14) or Mac2+ (M1) GPU with MSL 2.3+.\n  - Strong support for 8×8 `f32`, 8×8 `f16`, and mixed-precision modes (e.g. `f16` A/B and `f32` accumulator C).\n  - Implementation is based on SIMD-group matrix operations.\n\n- **Vulkan**:\n  - Requires the `VK_KHR_cooperative_matrix` extension.\n  - Many NVIDIA and AMD GPUs support `f16` at 16×16 tile sizes and similar.\n  - 8×8 `f32` support is hardware-dependent.\n  - Exact configurations are enumerated by `Adapter::cooperative_matrix_properties()`.\n\n- **Other backends**:\n  - May not support cooperative matrices at all. In that case the feature will not be exposed, and\n    `adapter.cooperative_matrix_properties()` will return an empty list.\n\n> Always treat the properties returned at runtime as the source of truth.\n\n---\n\n## `wgpu` API surface\n\nThis section summarizes the host-side API elements related to cooperative matrices.\n(For exact signatures and details, refer to the Rust documentation.)\n\n### Adapter\n\n- `Adapter::cooperative_matrix_properties() -> Vec<CooperativeMatrixProperties>`\n\nReturns all cooperative matrix configurations supported by the adapter/backend.\n\n### Structures\n\n- `CooperativeMatrixProperties`\n\n  - `m_size: naga::CooperativeSize`\n  - `n_size: naga::CooperativeSize`\n  - `k_size: naga::CooperativeSize`\n  - `ab_type: naga::Scalar`\n  - `cr_type: naga::Scalar`\n  - `saturating_accumulation: bool`\n\nThe `naga` types (`CooperativeSize`, `Scalar`) are part of the shader translation layer and\ndetermine the legal WGSL/cooperative matrix combinations.\n\nThere are currently no dedicated `wgpu` buffer or texture types for cooperative matrices; they are\nexpressed in WGSL as special value types accessed via pointers into ordinary `var<storage>` /\n`var<workgroup>` / `var<private>` / etc.\n\n---\n\n## WGSL extension specification\n\nCooperative matrices are enabled and accessed via WGSL extensions. The exact extension spelling\nmay change; the details below describe the intended semantics.\n\n### Enabling cooperative matrices in WGSL\n\nAny WGSL program using cooperative matrices must declare an extension at the top of the shader, for example:\n\n```/dev/null/example.wgsl#L1-3\nenable wgpu_cooperative_matrix;\n```\n\nThe shader is invalid if any cooperative matrix types or builtins are used without enabling this extension.\n\n### Cooperative matrix types\n\nA cooperative matrix is a value type parameterized by:\n\n- tile size (M×N),\n- scalar element type `T`, and\n- role `R` indicating how the matrix participates in the multiply-accumulate:\n\n  - `A`: left operand\n  - `B`: right operand\n  - `C`: accumulator / result\n\nConceptually:\n\n```/dev/null/example.wgsl#L1-8\n// A: MxK, B: KxN, C: MxN\ntype coop_matMxN<T, A>;\ntype coop_matMxN<T, B>;\ntype coop_matMxN<T, C>;\n```\n\nConcrete examples (sizes and types must match a supported configuration from\n`Adapter::cooperative_matrix_properties`):\n\n```/dev/null/example.wgsl#L10-20\n// 8x8 single-precision tiles\nalias CoopMatA = coop_mat8x8<f32, A>;\nalias CoopMatB = coop_mat8x8<f32, B>;\nalias CoopMatC = coop_mat8x8<f32, C>;\n\n// 16x16 half-precision inputs, 16x16 f32 accumulator (mixed precision)\nalias CoopMat16x16A = coop_mat16x16<f16, A>;\nalias CoopMat16x16B = coop_mat16x16<f16, B>;\nalias CoopMat16x16C = coop_mat16x16<f32, C>;\n```\n\nThe actual set of legal `(M, N, T, R)` combinations is defined by the cooperative matrix\nproperties returned at runtime; shaders must not use arbitrary combinations.\n\n### Roles and semantics\n\n- `A` role:\n\n  - Treated as the left operand in the multiplication. Has shape M×K.\n  - Participates as `A` in `A * B + C`.\n\n- `B` role:\n\n  - Treated as the right operand in the multiplication. Has shape K×N.\n  - Participates as `B` in `A * B + C`.\n\n- `C` role:\n\n  - Treated as accumulator and result. Has shape M×N.\n  - Participates as `C` in `A * B + C`.\n\nThese roles are part of the type; they are not interchangeable.\n\n### Cooperative matrix operations\n\nWGSL provides built-in functions for operating on cooperative matrices. The exact spelling may\nchange; the semantics are:\n\n#### `coopLoad`\n\nCollectively load a tile from memory into a cooperative matrix.\n\n```/dev/null/example.wgsl#L1-6\nfn coopLoad<T, R>(\n    ptr: ptr<STORAGE_CLASS, T>, // base pointer to scalar or vector elements\n    stride: u32                  // stride (in elements) between rows/columns\n) -> coop_matMxN<T, R>;\n```\n\n- Loads an M×N tile (or M×K / K×N, depending on role and operation) from memory pointed to by `ptr`.\n- `stride` describes the layout in memory; it is usually the number of elements between adjacent rows.\n- All invocations in the cooperative group must call `coopLoad` in a converged fashion.\n- Memory address range must be valid and properly aligned for the scalar type.\n\n> Implementation note: Each lane contributes to filling the tile based on an implementation-defined mapping from\n> invocation/lane ID to sub-fragment of the matrix.\n\n#### `coopStore`\n\nCollectively store a cooperative matrix tile back to memory.\n\n```/dev/null/example.wgsl#L8-13\nfn coopStore<T, R>(\n    value: coop_matMxN<T, R>,\n    ptr: ptr<STORAGE_CLASS, T>,\n    stride: u32\n);\n```\n\n- Stores `value` into the memory region addressed by `ptr` with given `stride`.\n- All invocations in the cooperative group must participate.\n- The store must not alias overlapping tiles in undefined ways.\n\n#### `coopMultiplyAdd`\n\nPerform a matrix multiply-accumulate operation on cooperative matrices:\n\n```/dev/null/example.wgsl#L15-23\nfn coopMultiplyAdd<Tab, Tcr, MA, KA, KB, NB>(\n    a: coop_matMAxKA<Tab, A>, // A: MAxKA tile\n    b: coop_matKBxNB<Tab, B>, // B: KBxNB tile (KB == KA)\n    c: coop_matMAxNB<Tcr, C>  // C: MAxNB accumulator/result\n) -> coop_matMAxNB<Tcr, C>;\n```\n\nSemantics:\n\n- Computes `C' = A * B + C`.\n- Returns the resulting accumulator tile `C'`.\n- Implies:\n  - `KA == KB` (inner dimension must match).\n  - Types `(Tab, Tcr)` must be one of the supported AB/CR combinations given by\n    `CooperativeMatrixProperties`.\n  - Sizes `(MA, NB, KA)` must match a supported `(m_size, n_size, k_size)` triple.\n\nFor example, with a supported configuration:\n\n```/dev/null/example.wgsl#L25-39\nenable wgpu_cooperative_matrix;\n\nalias MatA = coop_mat8x8<f32, A>;\nalias MatB = coop_mat8x8<f32, B>;\nalias MatC = coop_mat8x8<f32, C>;\n\nfn matmul_tile(\n    ptr_a: ptr<storage, f32>,\n    ptr_b: ptr<storage, f32>,\n    ptr_c: ptr<storage, f32>,\n    stride: u32,\n) {\n    let a: MatA = coopLoad<_, A>(ptr_a, stride);\n    let b: MatB = coopLoad<_, B>(ptr_b, stride);\n    let c: MatC = coopLoad<_, C>(ptr_c, stride);\n\n    let result: MatC = coopMultiplyAdd(a, b, c);\n    coopStore(result, ptr_c, stride);\n}\n```\n\nIf `saturating_accumulation` is true for the chosen configuration, then overflow during accumulation\nis clamped (e.g. saturating arithmetic). If false, overflow behavior for the accumulator follows the\nunderlying scalar type semantics (e.g. IEEE-754 for floats).\n\n### Workgroup cooperation and execution model\n\nCooperative matrix operations are **collective**:\n\n- All invocations in the relevant execution group must execute each cooperative operation in uniform control flow:\n\n  - Using `coopLoad`, `coopStore`, or `coopMultiplyAdd` in divergent control flow (e.g. some lanes taking\n    a branch, others not) is undefined behavior.\n  - The exact execution group may be a workgroup, a SIMD-group / subgroup, or another backend-specific\n    granularity; shaders must treat it abstractly.\n\n- The workgroup (or cooperating group) size is constrained by both:\n  - the cooperative matrix configuration, and\n  - backend-specific implementation details.\n\nFor portable code:\n\n- Choose a workgroup size that is known to be supported efficiently on your target backends, for example:\n\n  - `@workgroup_size(8, 8, 1)` to operate on an 8×8 tile, or\n  - a multiple of the tile size where each subgroup handles a tile.\n\n- Avoid control-flow divergence around cooperative operations.\n\nExample:\n\n```/dev/null/example.wgsl#L1-42\nenable wgpu_cooperative_matrix;\n\nstruct Matrices {\n    // Row-major tiles for A, B, C.\n    data: array<f32>,\n};\n\n@group(0) @binding(0)\nvar<storage, read>  buf_a: Matrices;\n@group(0) @binding(1)\nvar<storage, read>  buf_b: Matrices;\n@group(0) @binding(2)\nvar<storage, read_write> buf_c: Matrices;\n\nalias MatA = coop_mat8x8<f32, A>;\nalias MatB = coop_mat8x8<f32, B>;\nalias MatC = coop_mat8x8<f32, C>;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(\n    @builtin(workgroup_id) wg_id: vec3<u32>,\n    @builtin(local_invocation_id) lid: vec3<u32>,\n) {\n    // Compute tile offset; this is one of many possible mappings.\n    let tile_index = wg_id.x; // 1D tiling in this simple example\n    let tile_offset = tile_index * 64u; // 8x8 tile has 64 elements\n\n    // Base pointers for tiles of A, B, C.\n    let base_a = &buf_a.data[tile_offset];\n    let base_b = &buf_b.data[tile_offset];\n    let base_c = &buf_c.data[tile_offset];\n\n    let a: MatA = coopLoad<f32, A>(base_a, 8u);\n    let b: MatB = coopLoad<f32, B>(base_b, 8u);\n    let c: MatC = coopLoad<f32, C>(base_c, 8u);\n\n    let result: MatC = coopMultiplyAdd(a, b, c);\n    coopStore(result, base_c, 8u);\n}\n```\n\n---\n\n## Validation rules and undefined behavior\n\nImplementations must validate the following where possible:\n\n- The `wgpu_cooperative_matrix` WGSL extension is enabled if any cooperative matrix types\n  or builtins are used.\n- Tile sizes `(M, N, K)` and scalar types `(ab_type, cr_type)` match at least one\n  `CooperativeMatrixProperties` entry for the current adapter/backend.\n- Workgroup size, shader stage, and other pipeline configuration constraints required\n  by the backend are satisfied.\n\nThe following are examples of **undefined behavior** (non-exhaustive):\n\n- Using cooperative matrix operations without enabling the WGSL extension.\n- Using a cooperative matrix type `(M, N, T, R)` not supported by\n  `Adapter::cooperative_matrix_properties()`.\n- Mismatching sizes or roles in `coopMultiplyAdd` (e.g. incompatible M/N/K, or incorrect roles).\n- Executing `coopLoad`, `coopStore`, or `coopMultiplyAdd` in divergent control flow within the\n  cooperating execution group.\n- Providing invalid, misaligned, or out-of-bounds pointers to `coopLoad` / `coopStore`.\n- Overlapping `coopStore` targets in a way that creates data races or aliasing that the memory\n  model does not allow.\n\n---\n\n## Example: 64×64 matrix multiply using 8×8 tiles\n\nThe example in `examples/features/src/cooperative_matrix` demonstrates using cooperative matrices to\ncompute:\n\n- `C = A * B + C` where:\n  - `A` is 64×64,\n  - `B` is 64×64,\n  - `C` is 64×64.\n\nA high-level tiling strategy:\n\n1. Partition A, B, and C into 8×8 tiles.\n2. Launch one workgroup per output tile of C (i.e. 8×8 tiles for a 64×64 matrix = 8×8 = 64 tiles).\n3. Within each workgroup:\n   - Loop over K-dimension tiles.\n   - For each `k` tile:\n     - Load an 8×8 tile of A (`MatA`).\n     - Load an 8×8 tile of B (`MatB`).\n     - Maintain an 8×8 accumulator tile (`MatC`) and repeatedly apply `coopMultiplyAdd`.\n4. After the K loop, store the final accumulator tile back to C.\n\nKey points from the example:\n\n- Workgroup size is chosen so that all cooperative operations are well-defined and efficient for 8×8 tiles.\n- Host-side code:\n  - Enables `Features::EXPERIMENTAL_COOPERATIVE_MATRIX`.\n  - Queries `cooperative_matrix_properties` and verifies that 8×8 `f32` or chosen configuration is supported.\n  - Dispatches the compute pipeline with appropriate grid dimensions.\n\n---\n\n## Notes and best practices\n\n- Always query `adapter.cooperative_matrix_properties()` and check that the configuration your shaders use exists.\n  Do not hard-code assumptions about available tile sizes or element types.\n- Treat the cooperative execution group as an abstract concept; avoid making assumptions about how\n  tiles are mapped to lanes beyond what is guaranteed by the spec.\n- Avoid divergent control flow around cooperative operations.\n- Consider providing a fallback non-cooperative implementation for devices that do not support the feature.\n- This is an experimental extension; API and semantics may change across versions of `wgpu` and `naga`.\n"
  },
  {
    "path": "docs/api-specs/mesh_shading.md",
    "content": "# Mesh Shader Extensions\n\n🧪Experimental🧪\n\n`wgpu` supports an experimental version of mesh shading when `Features::EXPERIMENTAL_MESH_SHADER` is enabled.\nThe status of the implementation is documented in [the mesh-shading issue](https://github.com/gfx-rs/wgpu/issues/7197).\n\n**Note**: The features documented here may have major bugs in them and are expected to be subject\nto breaking changes. Suggestions for the API exposed by this should be posted on the issue above.\n\n## Mesh shaders overview\n\n### What are mesh shaders?\n\nMesh shaders are a new kind of rasterization pipeline intended to address some of the shortfalls with the vertex shader pipeline. The core idea of mesh shaders is that the GPU decides how to render the many small parts of a scene instead of the CPU issuing a draw call for every small part or issuing an inefficient monolithic draw call for a large part of the scene.\n\nMesh shaders are specifically designed to be used with **meshlet rendering**, a technique where every object is split into many subobjects called meshlets that are each rendered with their own parameters. With the standard vertex pipeline, each draw call specifies an exact number of primitives to render and the same parameters for all vertex shaders on an entire object (or even multiple objects). This doesn't leave room for different LODs for different parts of an object, for example a closer part having more detail, nor does it allow culling smaller sections (or primitives) of objects. With mesh shaders, each task workgroup might get assigned to a single object. It can then analyze the different meshlets(sections) of that object, determine which are visible and should actually be rendered, and for those meshlets determine what LOD to use based on the distance from the camera. It can then dispatch a mesh workgroup for each meshlet, with each mesh workgroup then reading the data for that specific LOD of its meshlet, determining which and how many vertices and primitives to output, determining which remaining primitives need to be culled, and passing the resulting primitives to the rasterizer.\n\nMesh shaders are most effective in scenes with many polygons. They can allow skipping processing of entire groups of primitives that are facing away from the camera or otherwise occluded, which reduces the number of primitives that need to be processed by more than half in most cases, and they can reduce the number of primitives that need to be processed for more distant objects. Scenes that are not bottlenecked by geometry (perhaps instead by fragment processing or post processing) will not see much benefit from using them.\n\nMesh shaders were first shown off in [NVIDIA's asteroids demo](https://www.youtube.com/watch?v=CRfZYJ_sk5E). Now, they form the basis for [Unreal Engine's Nanite](https://www.unrealengine.com/en-US/blog/unreal-engine-5-is-now-available-in-preview#Nanite).\n\n### Mesh shader pipeline\n\nWith the current pipeline set to a mesh pipeline, a draw command like\n`render_pass.draw_mesh_tasks(x, y, z)` takes the following steps:\n\n* If the pipeline has a task shader stage:\n\n    * Dispatch a grid of task shader workgroups, where `x`, `y`, and `z` give\n      the number of workgroups along each axis of the grid. Each task shader\n      workgroup produces a mesh shader workgroup grid size `(mx, my, mz)` and a\n      task payload value `mp`.\n\n    * For each task shader workgroup, dispatch a grid of mesh shader workgroups,\n      where `mx`, `my`, and `mz` give the number of workgroups along each axis\n      of the grid. Pass `mp` to each of these workgroup's mesh shader\n      invocations.\n\n* Alternatively, if the pipeline does not have a task shader stage:\n\n    * Dispatch a single grid of mesh shader workgroups, where `x`, `y`, and `z`\n      give the number of workgroups along each axis of the grid. These mesh\n      shaders receive no task payload value.\n\n* Each mesh shader workgroup produces a list of output vertices, and a list of\n  primitives built from those vertices. The workgroup can supply per-primitive\n  values as well, if needed. Each primitive selects its vertices by index, like\n  an indexed draw call, from among the vertices generated by this workgroup.\n\n  Unlike a grid of ordinary compute shader workgroups collaborating to build\n  vertex and index data in common storage buffers, the vertices and primitives\n  produced by a mesh shader workgroup are entirely private to that workgroup,\n  and are not accessible by other workgroups.\n\n* Primitives produced by a mesh shader workgroup can have a culling flag. If a\n  primitive's culling flag is false, it is skipped during rasterization.\n\n* The primitives produced by all mesh shader workgroups are then rasterized in\n  the usual way, with each fragment shader invocation handling one pixel.\n\n  Attributes from the vertices produced by the mesh shader workgroup are\n  provided to the fragment shader with interpolation applied as appropriate.\n\n  If the mesh shader workgroup supplied per-primitive values, these are\n  available to each primitive's fragment shader invocations. Per-primitive\n  values are never interpolated; fragment shaders simply receive the values\n  the mesh shader workgroup associated with their primitive.\n\n## `wgpu` API\n\n### New `wgpu` functions\n\n`Device::create_mesh_pipeline` - Creates a mesh shader pipeline. This is very similar to creating a standard render pipeline, except that it takes a mesh shader state and optional task shader state instead of a vertex state. If the task state is omitted, during rendering the number of workgroups is passed directly from the draw call to the mesh shader state, with an empty payload.\n\n`RenderPass::draw_mesh_tasks` - Dispatches the mesh shader pipeline. This ignores render pipeline specific information, such as vertex buffer bindings and index buffer bindings. The dispatch size must adhere to the limits described below.\n\n`RenderPass::draw_mesh_tasks_indirect`, `RenderPass::multi_draw_mesh_tasks_indirect` and `RenderPass::multi_draw_mesh_tasks_indirect_count` - Dispatches the mesh shader pipeline with dispatch size taken from a buffer. This ignores render pipeline specific information, such as vertex buffer bindings and index buffer bindings. The dispatch size must adhere to the limits described below. Analogous to `draw_indirect`, `multi_draw_indirect` and `multi_draw_indirect_count`. Requires the corresponding indirect feature to be enabled.\n\nAn example of using mesh shaders to render a single triangle can be seen [here](../../examples/features/src/mesh_shader).\n\n### Features\n* Using mesh shaders requires enabling `Features::EXPERIMENTAL_MESH_SHADER`.\n* Using mesh shaders with multiview requires enabling `Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW`.\n* Using mesh shaders with point primitives requires enabling `Features::EXPERIMENTAL_MESH_SHADER_POINTS`.\n* Queries are unsupported\n* Primitive index support will be added once support lands in for them in general.\n\n### Limits\n\n> **NOTE**: More limits will be added when support is added to `naga`.\n\n* `Limits::max_task_workgroup_total_count` - the maximum total number of workgroups from a `draw_mesh_tasks` command or similar. The dimensions passed must be less than or equal to this limit when multiplied together.\n* `Limits::max_task_workgroups_per_dimension` - the maximum for each of the 3 workgroup dimensions in a `draw_mesh_tasks` command. Each dimension passed must be less than or equal to this limit.\n* `max_mesh_multiview_count` - The maximum number of views used when multiview rendering with a mesh shader pipeline.\n* `max_mesh_output_layers` - the maximum number of output layers for a mesh shader pipeline.\n\n## Naga implementation\n\n### Supported frontends\n* 🛠️ WGSL\n* ❌ SPIR-V\n* 🚫 GLSL\n\n### Supported backends\n* 🛠️ SPIR-V\n* 🛠️ HLSL\n* ❌ MSL\n* 🚫 GLSL\n* 🚫 WGSL\n\n✔️ = Complete\n🛠️ = In progress\n❌ = Planned\n🚫 = Unplanned/impossible\n\n## `WGSL` extension specification\n\nThe majority of changes relating to mesh shaders will be in WGSL and `naga`.\n\nUsing any of these features in a `wgsl` program will require adding the `enable wgpu_mesh_shader;` directive to the top of a program.\n\nTwo new shader stages will be added to `WGSL`. Fragment shaders are also modified slightly. Both task shaders and mesh shaders are allowed to use any compute-available functionality, including subgroup operations.\n\n### Task shader\n\nA function with the `@task` attribute is a **task shader entry point**. A mesh shader pipeline may optionally specify a task shader entry point, and if it does, mesh draw commands using that pipeline dispatch a **task shader grid** of workgroups running the task shader entry point. Like compute shader dispatches, the three-component size passed to `draw_mesh_tasks`, or drawn from the indirect buffer for its indirect variants, specifies the size of the task shader grid as the number of workgroups along each of the grid's three axes.\n\nA task shader entry point must have a `@workgroup_size` attribute, meeting the same requirements as one appearing on a compute shader entry point.\n\nA task shader entry point must also have a `@payload(G)` property, where `G` is the name of a global variable in the `task_payload` address space. Each task shader workgroup has its own instance of this variable, visible to all invocations in the workgroup. Whatever value the workgroup collectively stores in that global variable becomes the **task payload**, and is provided to all invocations in the mesh shader grid dispatched for the workgroup. A task payload variable must be at least 4 bytes in size.\n\nA task shader entry point must return a `vec3<u32>` value decorated with `@builtin(mesh_task_size)`. The return value of each workgroup's first invocation (that is, the one whose `local_invocation_index` is `0`) is taken as the size of a **mesh shader grid** to dispatch, measured in workgroups. (If the task shader entry point returns `vec3(0, 0, 0)`, then no mesh shaders are dispatched.) Mesh shader grids are described in the next section.\n\nEach task shader workgroup dispatches an independent mesh shader grid: in mesh shader invocations, `@builtin` values like `workgroup_id` and `global_invocation_id` describe the position of the workgroup and invocation within that grid;\nand `@builtin(num_workgroups)` matches the task shader workgroup's return value. Mesh shaders dispatched for other task shader workgroups are not included in the count. If it is necessary for a mesh shader to know which task shader workgroup dispatched it, the task shader can include its own workgroup id in the task payload.\n\nTask shaders can use compute and subgroup builtin inputs, in addition to `view_index` and `draw_id`.\n\n### Mesh shader\n\nA function with the `@mesh` attribute is a **mesh shader entry point**. Mesh shaders must not return anything.\n\nLike compute shaders, mesh shaders are invoked in a grid of workgroups, called a **mesh shader grid**. If the mesh shader pipeline has a task shader, then each task shader workgroup determines the size of a mesh shader grid to be dispatched, as described above. Otherwise, the three-component size passed to `draw_mesh_tasks`, or drawn from the indirect buffer for its indirect variants, specifies the size of the mesh shader grid directly, as the number of workgroups along each of the grid's three axes.\n\nIf the mesh shader pipeline has a task shader entry point, then the pipeline's mesh shader entry point must also have a `@payload(G)` attribute, and the sizes of the variables must match. Mesh shader invocations can read from, but not write to, this variable, which is initialized to whatever value was written to it by the task shader workgroup that dispatched this mesh shader grid.\n\nIf the mesh shader pipeline does not have a task shader entry point, then the mesh shader entry point must not have any `@payload` attribute.\n\nA mesh shader entry point must have the following attributes:\n\n- `@workgroup_size`: this has the same meaning as when it appears on a compute shader entry point.\n\n- `@mesh(VAR)`: Here, `VAR` represents a workgroup variable storing the output information.\n\nAll mesh shader outputs are per-workgroup, and taken from the workgroup variable specified above. The type must have exactly 4 fields:\n- A field decorated with `@builtin(vertex_count)`, with type `u32`: this field represents the number of vertices that will be drawn\n- A field decorated with `@builtin(primitive_count)`, with type `u32`: this field represents the number of primitives that will be drawn\n- A field decorated with `@builtin(vertices)`, typed as an array of `V`, where `V` is the vertex output type as specified below\n- A field decorated with `@builtin(primitives)`, typed as an array of `P`, where `P` is the primitive output type as specified below\n\nFor a vertex count `NV`, the first `NV` elements of the vertex array above are outputted. Therefore, `NV` must be less than or equal to the size of the vertex array. The same is true for primitives with `NP`.\n\nThe vertex output type `V` must meet the same requirements as a struct type returned by a `@vertex` entry point: all members must have either `@builtin` or `@location` attributes, there must be a `@builtin(position)`, and so on.\n\nThe primitive output type `P` must be a struct type, every member of which either has a `@location` or `@builtin` attribute. All members decorated with `@location` must also be decorated with `@per_primitive`, as must the corresponding fragment input. The `@per_primitive` decoration may only be applied to members decorated with `@location`. The following `@builtin` attributes are allowed:\n\n- `triangle_indices`, `line_indices`, or `point_index`: The annotated member must be of type `vec3<u32>`, `vec2<u32>`, or `u32`.\n\n    The member's components are indices (or, its value is an index) into the list of vertices generated by this workgroup, identifying the vertices of the primitive to be drawn. These indices must be less than the value of `numVertices` passed to `setMeshOutputs`.\n\n    The type `P` must contain exactly one member with one of these attributes, determining what sort of primitives the mesh shader generates.\n\n- `cull_primitive`: The annotated member must be of type `bool`. If it is true, then the primitive is skipped during rendering.\n\nThe `@location` attributes of `P` and `V` must not overlap, since they are merged to produce the user-defined inputs to the fragment shader.\n\nMesh shaders may write to the `primitive_index` builtin. This is treated just like a field decorated with `@location`, so if the mesh shader outputs `primitive_index` the fragment shader must input it, and if the fragment shader inputs it, the mesh shader must write it (unlike vertex shader pipelines).\n\nMesh shaders can use compute and mesh shader builtin inputs, in addition to `view_index`, and if no task shader is present, `draw_id`.\n\n### Fragment shader\n\nFragment shaders can access vertex output data as if it is from a vertex shader. They can also access primitive output data, provided the input is decorated with `@per_primitive`. The `@per_primitive` decoration may only be applied to inputs or struct members decorated with `@location`.\n\nThe primitive state is part of the fragment input and must match the output of the mesh shader in the pipeline. Using `@per_primitive` also requires enabling the mesh shader extension. Additionally, the locations of vertex and primitive input cannot overlap.\n\n### Full example\n\nThe following is a full example of WGSL shaders that could be used to create a mesh shader pipeline, showing off many of the features.\n\n```wgsl\nenable wgpu_mesh_shader;\n\nconst positions = array(\n    vec4(0., 1., 0., 1.),\n    vec4(-1., -1., 0., 1.),\n    vec4(1., -1., 0., 1.)\n);\nconst colors = array(\n    vec4(0., 1., 0., 1.),\n    vec4(0., 0., 1., 1.),\n    vec4(1., 0., 0., 1.)\n);\n\nstruct TaskPayload {\n    colorMask: vec4<f32>,\n    visible: bool,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) color: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n    @builtin(cull_primitive) cull: bool,\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\nstruct PrimitiveInput {\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> workgroupData: f32;\n\n@task\n@payload(taskPayload)\n@workgroup_size(1)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    workgroupData = 1.0;\n    taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n    taskPayload.visible = true;\n    return vec3(1, 1, 1);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(1)\nfn ms_main() {\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    workgroupData = 2.0;\n\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0] * taskPayload.colorMask;\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1] * taskPayload.colorMask;\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2] * taskPayload.colorMask;\n\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    mesh_output.primitives[0].cull = !taskPayload.visible;\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput, primitive: PrimitiveInput) -> @location(0) vec4<f32> {\n    return vertex.color * primitive.colorMask;\n}\n```\n"
  },
  {
    "path": "docs/api-specs/ray_tracing.md",
    "content": "# Ray Tracing Extensions\n\n🧪Experimental🧪\n\n`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\n`Features::EXPERIMENTAL_RAY_QUERY` enabled) and interacted with in shaders. Currently `naga` only supports ray queries\n(accessible with `Features::EXPERIMENTAL_RAY_QUERY` enabled in wgpu).\n\n**Note**: The features documented here may have major bugs in them and are expected to be subject\nto breaking changes, suggestions for the API exposed by this should be posted on [the ray-tracing issue](https://github.com/gfx-rs/wgpu/issues/1040).\nLarge changes may mean that this documentation may be out of date.\n\n***This is not*** an introduction to raytracing, and assumes basic prior knowledge, to look at the fundamentals look at\nan [introduction](https://developer.nvidia.com/blog/introduction-nvidia-rtx-directx-ray-tracing/).\n\n## `wgpu`'s raytracing API:\n\nThe documentation and specific details of the functions and structures provided\ncan be found with their definitions.\n\nAcceleration structures do not have a separate feature, instead they are enabled by `Features::EXPERIMENTAL_RAY_QUERY`, unlike vulkan.\nWhen ray tracing pipelines are added, that feature will also enable acceleration structures.\n\nA [`Blas`] can be created with [`Device::create_blas`].\nA [`Tlas`] can be created with [`Device::create_tlas`].\n\nThe [`Tlas`] reference can be placed in a bind group to be used in a shader. A reference to a [`Blas`] can\nbe used to create [`TlasInstance`] alongside a transformation matrix, custom data\n(this can be any data that should be given to the shader on a hit) which only the first 24\nbits may be set, and a mask to filter hits in the shader.\n\nA [`Blas`] must be built in either the same build as any [`Tlas`] it is used to build or an earlier build call.\nBefore a [`Tlas`] is used in a shader it must\n- have been built\n- have all [`Blas`]es that it was last built with to have last been built in either the same build as\n  this [`Tlas`] or an earlier build call.\n\n### [`Blas`] compaction\n\nOnce a [`Blas`] has been built, it can be compacted. Acceleration structures are allocated conservatively, without\nknowing the exact data that is inside them. Once a [`Blas`] has been built, the driver can make data specific\noptimisations to make the [`BLAS`] smaller. To begin compaction call [`Blas::prepare_compaction_async`] on it. This\nmethod waits until all builds operating on the [`Blas`] are finished, prepares the [`Blas`] to be compacted, and runs\nthe given callback. To check whether the [`Blas`] is ready, you can also call [`Blas::ready_for_compaction`] instead of\nwaiting for the callback (useful if you are asynchronously compacting a large number of [`Blas`]es). Submitting a\nrebuild of a [`Blas`] terminates any [`Blas::prepare_compaction_async`], preventing the callback from being called, and\nmaking the [`Blas`] no longer ready to compact. Once a [`Blas`] is ready for compaction, it can be compacted using\n[`Queue::compact_blas`] this returns the new compacted [`Blas`], which is independent of the [`Blas`] passed in. The\nother [`Blas`] can be used for other things, including being rebuilt without affecting the new [`Blas`]. The returned\n[`Blas`] behaves largely like the [`Blas`] it was created from, except that it can be neither rebuilt, nor compacted\nagain.\n\nAn example of compaction being run when [`Blas`]es are ready, this would be in a situation when memory was not a major\nproblem, otherwise (e.g. if you get an out of memory error) you should compact immediately (and switching all\nnon-compacted [`Blas`]es to compacted ones).\n```rust\nuse std::iter;\nuse wgpu::Blas;\n\nstruct BlasToBeCompacted {\n    blas: Blas,\n    /// The index into the TlasInstance this BLAS is used in.\n    tlas_index: usize,\n}\n\nfn render(/*whatever args you need to render*/) {\n  /* additional code to prepare the renderer */ \n  //An iterator of whatever BLASes you have called `prepare_compaction_async` on.\n  let blas_s_pending_compaction: impl Iterator<Item = BlasToBeCompacted> = iter::empty();\n  for blas_to_be_compacted in blas_s_pending_compaction {\n    if blas_to_be_compacted.blas.ready_for_compaction() {\n        let compacted_blas = queue.compact_blas(&blas_to_be_compacted.blas);\n        tlas_instance[blas_to_be_compacted.tlas_index].set_blas(&compacted_blas);\n    }\n  }\n  let mut encoder =\n    device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n  /* do other preparations on the TlasInstance.*/\n  encoder.build_acceleration_structures(iter::empty(), iter::once(&tlas_package));\n  /* more render code */\n  queue.submit([encoder.finish()]);\n}\n```\n\n[`Device::create_blas`]: https://wgpu.rs/doc/wgpu/struct.Device.html#method.create_blas\n[`Device::create_tlas`]: https://wgpu.rs/doc/wgpu/struct.Device.html#method.create_tlas\n[`Tlas`]: https://wgpu.rs/doc/wgpu/struct.Tlas.html\n[`Blas`]: https://wgpu.rs/doc/wgpu/struct.Blas.html\n[`TlasInstance`]: https://wgpu.rs/doc/wgpu/struct.TlasInstance.html\n[`Blas::prepare_compaction_async`]: https://wgpu.rs/doc/wgpu/struct.Blas.html#method.prepare_compaction_async\n[`Blas::ready_for_compaction`]: https://wgpu.rs/doc/wgpu/struct.Blas.html#method.ready_for_compaction\n[`Queue::compact_blas`]: https://wgpu.rs/doc/wgpu/struct.Queue.html#method.compact_blas\n\n## `naga`'s raytracing API:\n\n`naga` supports ray queries (also known as inline raytracing). To enable basic ray query functions you must add\n`enable wgpu_ray_query` to the shader, ray queries and acceleration structures also support tags which require extra\n`enable` extensions (see Acceleration structure tags for more info). Ray tracing pipelines are currently in\ndevelopment. Naming is mostly taken from vulkan.\n\n### Ray Queries\n\n```wgsl\n// - Initializes the `ray_query` to check where (if anywhere) the ray defined by `ray_desc` hits in `acceleration_structure`\nrayQueryInitialize(rq: ptr<function, ray_query>, acceleration_structure: acceleration_structure, ray_desc: RayDesc)\n// Overload.\nrayQueryInitialize(rq: ptr<function, ray_query<vertex_return>>, acceleration_structure: acceleration_structure<vertex_return>, ray_desc: RayDesc)\n\n// - Traces the ray in the initialized ray_query (partially) through the scene.\n// - Returns true if a triangle that was hit by the ray was in a `Blas` that is not marked as opaque.\n// - Returns false if all triangles that were hit by the ray were in `Blas`es that were marked as opaque.\n// - The hit is considered `Candidate` if this function returns true, and the hit is considered `Committed` if\n//   this function returns false.\n// - A `Candidate` intersection interrupts the ray traversal.\n// - A `Candidate` intersection may happen anywhere along the ray, it should not be relied on to give the closest hit. A\n//   `Candidate` intersection is to allow the user themselves to decide if that intersection is valid*. If one wants to get\n//   the closest hit a `Committed` intersection should be used.\n// - Calling this function multiple times will cause the ray traversal to continue if it was interrupted by a `Candidate`\n//   intersection.\nrayQueryProceed(rq: ptr<function, ray_query>) -> bool\n// Overload.\nrayQueryProceed(rq: ptr<function, ray_query<vertex_return>>) -> bool\n\n// - Generates a hit from procedural geometry at a particular distance.\nrayQueryGenerateIntersection(hit_t: f32)\n\n// - Commits a hit from triangular non-opaque geometry.\nrayQueryConfirmIntersection()\n\n// Aborts the query which is in progress, that is, the next `rayQueryProceed` is guaranteed to return `false`\n// and any call to `rayQueryGetCommittedIntersection` will return the closest committed result so far.\nrayQueryTerminate(rq: ptr<function, ray_query>)\n\n// - Returns intersection details about a hit considered `Committed`.\nrayQueryGetCommittedIntersection(rq: ptr<function, ray_query>) -> RayIntersection\n// Overload.\nrayQueryGetCommittedIntersection(rq: ptr<function, ray_query<vertex_return>>) -> RayIntersection\n\n// - Returns intersection details about a hit considered `Candidate`.\nrayQueryGetCandidateIntersection(rq: ptr<function, ray_query>) -> RayIntersection\n// Overload.\nrayQueryGetCandidateIntersection(rq: ptr<function, ray_query<vertex_return>>) -> RayIntersection\n\n// - Returns the vertices of the hit triangle considered `Committed`.\ngetCommittedHitVertexPositions(rq: ptr<function, ray_query<vertex_return>>) -> array<vec3<f32>, 3>\n\n// - Returns the vertices of the hit triangle considered `Candidate`.\ngetCandidateHitVertexPositions(rq: ptr<function, ray_query<vertex_return>>) -> array<vec3<f32>, 3>\n```\n\n> [!CAUTION]\n>\n> #### ⚠️Undefined behavior ⚠️:\n> - Calling `rayQueryGetCommittedIntersection` or `rayQueryGetCandidateIntersection` when `rayQueryProceed` has not been\n> called on this ray query since it was initialized (or if the ray query has not been previously initialized).\n> - Calling `rayQueryGetCommittedIntersection` when `rayQueryProceed`'s latest return on this ray query is considered\n>   `Candidate`.\n> - Calling `rayQueryGetCandidateIntersection` when `rayQueryProceed`'s latest return on this ray query is considered\n>   `Committed`.\n> - Calling `getCommittedHitVertexPositions` when `rayQueryProceed`'s latest return on this ray query is considered\n>   `Candidate`.\n> - Calling `getCandidateHitVertexPositions` when `rayQueryProceed`'s latest return on this ray query is considered\n>   `Committed`.\n> - Calling `get*HitVertexPositions` when the last `rayQueryProceed` did not hit a triangle\n> - Calling `rayQueryProceed` when `rayQueryInitialize` has not previously been called on this ray query\n> - Calling `rayQueryGenerateIntersection` on a query with last intersection kind not being\n>   `RAY_QUERY_INTERSECTION_AABB`,\n> - Calling `rayQueryGenerateIntersection` with `hit_t` outside of `RayDesc::t_min .. RayDesc::t_max` range.\n>   or when `rayQueryProceed`'s latest return on this ray query is not considered `Candidate`.\n> - Calling `rayQueryConfirmIntersection` on a query with last intersection kind not being\n>   `RAY_QUERY_INTERSECTION_TRIANGLE`,\n>   or when `rayQueryProceed`'s latest return on this ray query is not considered `Candidate`.\n>\n> *this is only known undefined behaviour, and will be worked around in the future.\n\n```wgsl\nstruct RayDesc {\n    // Contains flags to use for this ray (e.g. consider all `Blas`es opaque)\n    flags: u32,\n    // If the bitwise and of this and any `TlasInstance`'s `mask` is not zero then the object inside\n    // the `Blas` contained within that `TlasInstance` may be hit.\n    cull_mask: u32,\n    // Only points on the ray whose t is greater than this may be hit.\n    t_min: f32,\n    // Only points on the ray whose t is less than this may be hit.\n    t_max: f32,\n    // The origin of the ray.\n    origin: vec3<f32>,\n    // The direction of the ray, t is calculated as the length down the ray divided by the length of `dir`.\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    // the kind of the hit, no other member of this structure is useful if this is equal\n    // to constant `RAY_QUERY_INTERSECTION_NONE`.\n    kind: u32,\n    // Distance from starting point, measured in units of `RayDesc::dir`.\n    t: f32,\n    // Corresponds to `instance.custom_data` where `instance` is the `TlasInstance`\n    // that the intersected object was contained in.\n    instance_custom_data: u32,\n    // The index into the `TlasPackage` to get the `TlasInstance` that the hit object is in\n    instance_index: u32,\n    // The offset into the shader binding table. Currently, this value is always 0.\n    sbt_record_offset: u32,\n    // The index into the `Blas`'s build descriptor (e.g. if `BlasBuildEntry::geometry` is\n    // `BlasGeometries::TriangleGeometries` then it is the index into that contained vector).\n    geometry_index: u32,\n    // The object hit's index into the provided buffer (e.g. if the object is a triangle\n    // then this is the triangle index)\n    primitive_index: u32,\n    // Two of the barycentric coordinates, the third can be calculated (only useful if this is a triangle).\n    barycentrics: vec2<f32>,\n    // Whether the hit face is the front (only useful if this is a triangle).\n    front_face: bool,\n    // Matrix for converting from object-space to world-space.\n    //\n    // This matrix needs to be on the left side of the multiplication. Using it the other way round will not work.\n    // Use it this way: `let transformed_vector = intersecion.object_to_world * vec4<f32>(x, y, z, transform_multiplier);\n    object_to_world: mat4x3<f32>,\n    // Matrix for converting from world-space to object-space\n    //\n    // This matrix needs to be on the left side of the multiplication. Using it the other way round will not work.\n    // Use it this way: `let transformed_vector = intersecion.world_to_object * vec4<f32>(x, y, z, transform_multiplier);\n    world_to_object: mat4x3<f32>,\n}\n\n/// -- Flags for `RayDesc::flags` --\n\n// All `Blas`es are marked as opaque.\nconst FORCE_OPAQUE = 0x1;\n\n// All `Blas`es are marked as non-opaque.\nconst FORCE_NO_OPAQUE = 0x2;\n\n// Instead of searching for the closest hit return the first hit.\nconst TERMINATE_ON_FIRST_HIT = 0x4;\n\n// Unused: implemented for raytracing pipelines.\nconst SKIP_CLOSEST_HIT_SHADER = 0x8;\n\n// If `RayIntersection::front_face` is false do not return a hit.\nconst CULL_BACK_FACING = 0x10;\n\n// If `RayIntersection::front_face` is true do not return a hit.\nconst CULL_FRONT_FACING = 0x20;\n\n// If the `Blas` a intersection is checking is marked as opaque do not return a hit.\nconst CULL_OPAQUE = 0x40;\n\n// If the `Blas` a intersection is checking is not marked as opaque do not return a hit.\nconst CULL_NO_OPAQUE = 0x80;\n\n// If the `Blas` a intersection is checking contains triangles do not return a hit.\nconst SKIP_TRIANGLES = 0x100;\n\n// If the `Blas` a intersection is checking contains AABBs do not return a hit.\nconst SKIP_AABBS = 0x200;\n\n/// -- Constants for `RayIntersection::kind` --\n\n// The ray hit nothing.\nconst RAY_QUERY_INTERSECTION_NONE = 0;\n\n// The ray hit a triangle.\nconst RAY_QUERY_INTERSECTION_TRIANGLE = 1;\n\n// The ray hit a custom object, this will only happen in a committed intersection\n// if a ray which intersected a bounding box for a custom object which was then committed.\nconst RAY_QUERY_INTERSECTION_GENERATED = 2;\n\n// The ray hit a AABB, this will only happen in a candidate intersection\n// if the ray intersects the bounding box for a custom object.\nconst RAY_QUERY_INTERSECTION_AABB = 3;\n```\n\n### Ray Tracing Pipelines\n\nFunctions\n```wgsl\n// Begins to check where (if anywhere) the ray defined by `ray_desc` hits in `acceleration_structure` calling through the `any_hit` shaders and `closest_hit` shader if something was hit or the `miss` shader if no hit was found\ntraceRay<T>(acceleration_structure: acceleration_structure, ray_desc: RayDesc, payload: ptr<ray_payload, T>)\n```\n\n> [!CAUTION]\n>\n> #### ⚠️Undefined behavior ⚠️:\n> Calling `traceRay` inside another `traceRay` more than `max_recursion_depth` times\n>\n> *this is only known undefined behaviour, and will be worked around in the future.\n\nNew shader stages\n```wgsl\n// First stage to be called, allowed to call `traceRay`\n@ray_generation\nfn rg() {}\n\n// Stage called on any hit that is not opaque, not allowed to call `traceRay`\n@any_hit\nfn ah() {}\n\n// Stage called on the closest hit, allowed to call `traceRay`\n@closest_hit\nfn ch() {}\n\n// Stage call if there was never a hit, allowed to call `traceRay`\n@miss\nfn miss() {}\n```\n### Acceleration structure tags\n\nThese are tags that can be added to a acceleration structure (`acceleration_structure` ->\n`acceleration_structure<... insert tags here! ...>`) and to a ray query (`ray_query` ->\n`ray_query<... insert tags here! ...>`). These require more features.\n\n\n| Tag | Requirements | Description |\n| --- | ------------ | -- |\n| `vertex_return`| `enable wgpu_ray_query_vertex_return` | Allows getting the vertices of the hit triangle when using ray queries |\n"
  },
  {
    "path": "docs/big-picture.xml",
    "content": "<mxfile host=\"app.diagrams.net\" modified=\"2023-12-04T19:38:59.956Z\" agent=\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0\" etag=\"ZvsPE6AT2WA2xAhdKlfq\" version=\"22.1.5\" type=\"device\"><diagram id=\"FLHi6BIjYAkfD6NTjxEH\" name=\"Page-1\">7V1be5s4E/41vrQfJHHyZU7tdrftl6dpm93c7IONbNMQSDFOnP76T9jIRgeDjMUh3XjbrREgsGbm1TujkTRAFw/r94n3uPgU+zgcQMNfD9DlAELgQJf8k5W8bEvGrrUtmCeBn1+0L7gJfuG80MhLV4GPl8yFaRyHafDIFk7jKMLTlCnzkiR+Zi+bxSH71EdvjoWCm6kXiqW3gZ8utqWuZezL/8DBfEGfDIz8zMSb3s+TeBXlzxtANNt8tqcfPFpXfv1y4fnxc+Gh6GqALpI4TrffHtYXOMzaljbb9r53B87u3jvBUapyQxgNJy9X7979+udrMPx4he9Wi7shgttqnrxwlTfIX/glf+H0hTbS5mfirCIwQOfPiyDFN4/eNDv7TNSClC3ShzA/HXoTHJ7vWuciDuOEnIriiFx/PoujNNcCYObH9BLShMbmQ8q9MJhHpGxKfh4mJ8+fcJIGRG5n+Yk0zp4rtkLeMNnleM03N1FjHD/gNCG/0cjPWmYuoVyDTSqx570+wLEzyvV6UdQGqiZeroXzXe17SZAvuTAOCObP2/n1nffzj6H/dPfpSwrMu4ehIwrmz5th5KXBEybFt3jy/vpb9uBHYie8uCoE5HvLxU6YsyAMC+1P9BdOpwebW5DKRpz5k7PqlmkS3+NChb49sS1bk6QotrxwzV+QFLANUU5OU2KyXUFMW9EMP1NR6ZYQBr6FHY0SGtsO8jRJyAQKErJalZAhk9Bw4i1JA/fYkHSYi80KA9oSYLMlsNaYMKyxIIwBtMM0a8vgiXydZ1+/rJbpTiLbs5OEnqQl5PmFW7RKzp3iEsk1ISi2A5L0P1KbAeOm5ERJXaFBsU9oU34YJ+kinseRF17tS8/3pR/jrKU2rfMDp+lL3t97qzRmxYDXQfo3+W7k3//JvpOOdnt0uS6cunyhBxH5hX/TCrKDwl3Z4f62zRG9b4t7lOOhMskt41UyxSXtQ4mql8xxWnadub0wa7xSRUhwuO0vGHqrW6zIaUmsVETGCFqMlI6SESxqyMixCkoCSlWkvmCRqmB1y3Vz61mSeC+FCx7jIEqXhZqvs4I9biBKP3LgQAbnAnDXm45Vdj35sn0D+d1wzMIUjz7bxs1v4jR11yRqyitzVURCnGz6CYlCf8w8EFYlK92Jh8D3N/qe4GXwy5ts6suUKRcCqdw6H1iXnAsDKx0eURlLbVPoNXaub/5Og7x/ONibDImtQGPM8rDt0XHqKGgAsEasAg2RMaIYR6uJZ7MlPlkDpE00bhm+uB7Gqepi9mCljlUi5NVHL6jcL/UCvhybBRTkOofhSJcSUXImMtDMrBntsn+uYnpiuNxoyxm5gMhmvT9JGejz/HE1nMYJLhDUbY0HGOpy4T1mX4mgvTDEYTxPvIcMb3ASkB+ZgRR77np/oioEMwvWmMbaJAyXOJCub8p8QRdOkK3LF+TiKsi0RF5rSnit3Zj7YbbNf46jqBQ/wECdDp+AFopggXoBFpDTJmBYpVxHuB6WcB1d4CJyFIl7e46fXhS92A4xYucFCxgxcS3TKlU9dYzgIhRjiecLJQiBNCCE9+3pc3p5++4q/gLCL//++1d082FIw1dtOb6spTuKnm8BSVS8KibYUR8vVH0j6pR07Ry5NgsAqMI54twb7vqTAeNq4fxIJpO7b5/MG/RjOZmMv34eIjE6eSHVwJZdmkOjMse6OqVGdrqrQwwBANbToRh8oqszdNAIuuPCh30KgGyNDXo9Kn2KPg57kL72vcNSILVFLddEcnex8pIeDLXKcTvtwZQ7MGBAlhuPx6iKHWdHBX05kf6ar4v+uojtnVyLU5XS0J3Fk+Ex+37bVtASuytp6wKGjUajPoGHAtvN7qBWlJuDfgIMHBkDlgzR6WDAcvxArwE/WOyAVY51fZSwXhdKcBE1u8JHtnmf2j0CVBDNUdmF71rFFEoi95jy2YuiePXKYEU/hshSLppyouVRkLaGGWuE2w/F9huEEFsRQvoRlLcRF7W1wTGYwDnRaMy9X8OgYAugcIOTp7hXmEATg5pNleP8klZDa3L1PhwcVfVagU281o0vyjmukTf3XsO4i4fdmVT09tTFk1kzLiqyWNfBlHFMU8IxG0tuNYEglVYHcpXHcY/gpvX7B6DqiQK7kR6i0ltENG2kItPjVLZKn6NKV7nrm4nQQjFC668plv0eQdpSG9WSjwJNmwvGb49ODdICwNTKOSDNxWTNtpzkUwEs57UUwzpHMKcXHBeO2R4RWscE02jW3A6GINdP6kuEkyISEEmuZETgaRXee5Fi2P63Ay86aKJhhMkwAJtlOdQEXpbDQiKnRc2hFxDDsbXGj2RMfJMGtfDC18DGjxowgpqCNQKJkWVFNTViJB2ObSursl1SbTXS00g6A1aagKPKB+IdRyczyB/TbKakGGElVXrhMBEnSvXbqdZgtnwuo9FiOEVqtT2eoXOC1bbk4fLmxDu4DUcpJQzORz6Ab5aVxZu7tixKxH4z02rG9aoyLepatWZadBrN3rS85aJPVjWzsv+kVrX5iL7R9tOIte2OOxsWMFvOVzJGABZzDgBx8JqZcJpVoi9dST3G0o/0W+iwET8TtRGsFa1fEhqZh3iJ/quREW25t0NjZFiIDY3oioxAplabraDBwIi4msc8jJ/71Ht0wslkK+K0nFIiTtt7j6f34sB+Zet2MBbvcAHj7sfibdksSB75Iv8sW19rDzGFdmRnrW7YDF3eytgebAwkDKL7sjas7KMKLSRbAYOWnTrgycrHUgzoV9WzM6O2kl+gSu/3v5tDXZ9wqZ+QNk2W/ekqtXSB9iEDrjE44LiIHUo6dfoJrYZ3m5rr9GyRM22C+HStsQN93wKvPcLAuV4vL931d/D4Dk9cGamTycucHwvcrjHbaSlphgVyBudPTrosrqGgMnPxoCAr3SWa49RR1mXl7ELED/dpGiiw+TW/zPJZj5ZZen0zIwtUOAXIucTR4SzNzrCmHX5oG9wkkDaxRupHiyM/ZfRwGnrLZTCVx1vY+R2wmRnO1UBYH0lUpz3T2RA94bQmv6hcXVJrcd4L4FcHbAi5gGFrRSKpostSaVpR9EqF7aDPVJ7gb/RK0xEXrVBcl0xcjoiPjfPwqknRHdtsX9GhGNp6xhOizvhhEmZvL+r96418lpq6lsAnMk7083YZrGM0Apax/3DuJCAPA4XT9Lla/UJpe4kKUxsaa/oKvaEA1EXW5kx0RAH4FLK6FEDIVdSEjLyTRBdXOPheoPT6ZpBUtoSxil0sSSOlhyOq/MSfgu2MYdF6Rlk+ap1Z0pXjlZUWtPkJ74KsvTod4ewX0+bnPJq8daiambBgHm+vupIJEZt5bI5bGJqljVKcmrnwfKI3whLf73GEEyKROBLPldKU7hZ5LcbPHr1pEM0HhxbzLAUVLeTEpq7oycSa4yLOyG4iLC3Xl+OGqKT8Q5Vf1s7wqM85XhvIWYDDDENtTmA1WjYEcsILW1Vcgr3eMtoARShRcmEYjsXJylG77wQGfQY+ux+4a9XTA4cisHXQ1DIdV4sFDbkRJXfkNpHYIm8RWV5UU2AqS2tVI6ztwa9GMqo60aQbMqo6ObGSjAqbwmjCaeiwuKt7aovcHmTBDQFIN7Nd/qsYqnM7AsPhlorSlCcIuezDJmZQyqOpIjnNNpL6KKjFb7/ODJ8p2PnQJZRNbm26q2shvnlCV+aKXVmpVve0KzONml0Z4MKXFp0erj8vT96ubU9xKDIvHROKTtMWvROKaLCqSnji+DJXj6nmwNbJeZFtX1RHB9htV1taZFNM+To6jMIFniv3zVHDNun+tSK0tbg8Z+UWWYCG0HSTZn5zC/PIzTDy99KW5VUim3KO/Z3wHiL2/i8v3tJSnlxqAGqRTkmFqBKGbVMq6ptLlyGVBklZlsNthmZKFl6VbUbMe+T6ZCVGE7/ItsgjvzAdSKZz8I1ZMIC8iLqzIZ6lZc6sTKj7zqzaSS3K0SiRY5yNtaRZ+2szQKgiVqdVsYoeTVYR+Xu++fMm3VOkK3FY25WuQq4t27gViNlnhJQ0drsIqRAHbzzpvMcC2iXUdCYgMTCb7wb+4eExuzuekf9drUnLR1ls1ji7/vCGgCchoEzmrSIgtcGOEJCTeSezzexx78gkFFPjt9tDvRmb+sRvu3dkUpIH3iNja2VxA4mxdc1L6HZMxYUnZmvZcmBv5naUuXXN7iX7O/bH3Fqa3SiYW+csE4lBrR/ek7ecJsHjW7jkNJPrnE4iWdyZE2l1gr50XLh8aMSs4631ZKxV4gryu3+ojrbaoLIqjeugSDVADJjdxsl9lpQNjWsvFZcDfDPxozxGyaKB7Zp43fnpTZq42i7bPbF3Sadc196dauho2t5FV/Vr7Mdvxq6lP+/c2EWP9esCkwLqIRnnwQbYg2m6SsQFi04Xd3XypkaBm2UC1yBh2eJzpgup1IsidRsTaZ0tB7QlwxyTrceAemnfUE9uyss2d7C1jqqILz5OZ+HPn9/v/MnqKU6uz5bW+bCtDZJqp7xx7SSRTuOZbMgc1U1ms6qrqp3PRg6TONvUZX85QbTFp9jPstuv/g8=</diagram></mxfile>"
  },
  {
    "path": "docs/broadcast_license.nu",
    "content": "# Quick maintenance script which broadcasts the license files to all crates\n# that are released on crates.io\n\n# Change to the root of the repository\ncd ($env.FILE_PWD | path dirname)\n\nlet crates = [\n    \"wgpu\",\n    \"wgpu-core\",\n    \"wgpu-core/platform-deps/apple\",\n    \"wgpu-core/platform-deps/emscripten\",\n    \"wgpu-core/platform-deps/wasm\",\n    \"wgpu-core/platform-deps/windows-linux-android\",\n    \"wgpu-hal\",\n    \"wgpu-info\",\n    \"wgpu-naga-bridge\",\n    \"wgpu-types\",\n    \"naga\",\n    \"naga-cli\",\n]\n\nfor crate in $crates {\n    cp LICENSE.APACHE LICENSE.MIT $\"./($crate)/\"\n}\n"
  },
  {
    "path": "docs/release-checklist.md",
    "content": "This is an overview of how to run wgpu releases.\n\n## Structure\n\nWe do a major breaking release every 12 weeks. This happens no matter the status of various in-flight projects.\n\nWe do a patch releases as needed in the weeks between major releases. Once a new major release is cut, we stop doing patch releases for the previous major release unless there is a critical bug or a compilation issue.\n\n## People\n\nAnyone in the @gfx-rs/wgpu team can perform these steps.\n\n## Major Release Process\n\nApprox 1 Week Before:\n\n- Determine if `glow` (@groves), `rspirv` (@gfx-rs/wgpu) or any other dependant crates will need a release. If so, coordinate with their maintainers.\n- Go through the changelog:\n  - Re-categorize miscategorized items.\n  - Edit major changes so a user can easily understand what they need to do.\n  - Add missing major changes that users need to know about.\n  - Copy-edit the changelog for clarity.\n\nDay of Release:\n\n- Update the version number in the root `Cargo.toml` to the new version, this will update all crates to the new version.\n- Bump the wgpu dependency numbers in the following places:\n  - `Cargo.toml`\n  - `examples/standalone/*`\n  - `examples/bug-repro/*`\n- Grep for the previous version to ensure various documentation links are updated.\n  - For example, if the previous version was v24.0.0, grep for `v24` and `24.0`\n- Ensure `glow` and `rspirv` are updated to the latest version if needed.\n- Add a new header for the changelog with the release version and date.\n- Create a PR with all of the version changes and changelog updates.\n- While waiting on the PR, do a dry run of publishing.\n  ```bash\n    cargo publish --dry-run --workspace --all-features --exclude deno_webgpu\n  ```\n- Once the PR is CI clean and publish worked, (force) merge it.\n- Checkout `trunk` with the merged PR.\n- Publish! These commands can be pasted directly into your terminal in a single command, and they will publish everything.\n  ```bash\n    cargo publish --workspace --all-features --exclude deno_webgpu\n  ```\n- If there were any newly published crates, ensure `github:gfx-rs/wgpu` is added as an owner of that crate.\n- Create a new tag called `vX.Y.Z` and push it to the repo.\n  - For each crate being released (viz., every `publish`-able crate that is not `deno*`), create a new tag of the form `{crate_name}-vX.Y.X`.\n- Create a new release on the `wgpu` repo with the changelog from this version, targeting that tag\n- Create a branch with the with the new version `vX` and push it to the repo.\n  - On this branch, remove the [!NOTE] at the top of [wgpu/examples/README.md].\n- Complete the release's milestone on GitHub.\n- Create a new milestone for the next release, in 12 weeks time.\n- Update the release checklist with any needed changes.\n- Publish the link to the github release in the following places.\n  - [r/rust](https://www.reddit.com/r/rust/).\n    - Add an AMA comment.\n  - Crosspost to [r/rust_gamedev](https://www.reddit.com/r/rust_gamedev/).\n    - Add an AMA comment.\n  - Include the r/rust post shortlink in the following posts as well:\n  - [wgpu matrix](https://matrix.to/#/#wgpu:matrix.org)\n  - [Rust Gamedev Discord](https://discord.gg/X3MYBNXUMJ) in the #crates and #wgpu channel\n  - [Bevy Discord](https://discord.com/invite/bevy) in the #rendering-dev channel\n  - [Graphics Programming Discord](https://discord.gg/6mgNGk7) in the #webgpu channel\n  - [Rust Community Discord](https://discord.gg/rust-lang-community) in the #games-and-graphics channel\n\n## Patch Release Process\n\n- 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)\n- 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.\n- Remove the `needs-backport` label from the PRs.\n- Fix the changelogs items and add a new header for the patch release with the release version and date.\n  - The release section should start with a header saying the following (for example)\n    ```markdown\n    This release includes `crate1`, `crate2` and `crate3` version `X.Y.Z`. All other crates remain at their previous versions.\n    ```\n- Once all the PRs are cherry-picked, look at the diff between HEAD and the previous patch release. See what crates changed.\n- Bump all the versions of the crates that changed.\n- Create a PR with all of the version changes and changelog updates into the release branch.\n- Once the PR is CI clean, (force) rebase merge it.\n- Checkout the release branch with the merged PR.\n- Publish all relevant crates (see list above).\n- Create a new release on the `wgpu` repo with the relevant changelog included, based on a new tag called `vX.Y.Z` in the release branch.\n  - For each crate released, also create a tag `{crate_name}-vX.Y.Z`.\n- Backport the changelog and version bumps to the `trunk` branch.\n  - Ensure that any items in the newly-released changelog don't appear in the \"unreleased\" section of the trunk changelog.\n- Update the release checklist with any needed changes.\n"
  },
  {
    "path": "docs/review-checklist.md",
    "content": "# Review Checklist\n\nThis is a collection of notes on things to watch out for when\nreviewing pull requests submitted to wgpu and Naga.\n\nIdeally, we want to keep items off this list entirely:\n\n- Using Rust effectively can turn some mistakes into compile-time\n  errors. For example, in Naga, using exhaustive matching ensures that\n  changes to the IR will cause compile-time errors in any code that\n  hasn't been updated.\n\n- Refactoring can gather together all the code responsible for\n  enforcing some invariant in one place, making it clear whether a\n  change preserves it or not. For example, Naga localizes all handle\n  validation to `naga::valid::Validator::validate_module_handles`,\n  allowing the rest of the validator to assume that all handles are\n  valid.\n\n- Offering custom abstractions can help contributors avoid\n  implementing a weaker abstraction by themselves. For example,\n  because `HandleSet` and `HandleVec` are used throughout Naga,\n  contributors are less likely to write code that uses a `BitSet` or\n  `Vec` on handle indices, which would invite bugs by erasing the\n  handle types.\n\nThis checklist gathers up the concerns that we haven't found a\nsatisfying way to address in a more robust way.\n\n## Naga\n\n### General\n\n- [ ] If your change iterates over a collection, did you ensure the\n      order of iteration was deterministic? Using `HashMap` and\n      `HashSet` is fine, as long as you don't iterate over it.\n- [ ] If you insert elements into a set or map that you expect are not\n      already present, did you make an assertion about `insert`'s\n      return value?\n\n### WGSL Extensions\n\n- [ ] If you added a new feature to WGSL that is not covered by the\n      WebGPU specification:\n  - [ ] Did you add a `Capability` flag for it?\n  - [ ] Did you document the feature fully in that flag's doc comment?\n  - [ ] Did you ensure the validator rejects programs that use the\n        feature unless its capability is enabled?\n\n### IR changes\n\nIf your change adds or removes `Handle`s from the IR:\n- [ ] Did you update handle validation in `valid::handles`?\n- [ ] Did you update the compactor in `compact`?\n- [ ] Did you update `back::pipeline_constants::adjust_expr`?\n\nIf your change adds a new operation:\n- [ ] Did you update the typifier in `proc::typifier`?\n- [ ] Did you update the validator in `valid::expression`?\n- [ ] If the operation can be used in constant expressions, did you\n      update the constant evaluator in `proc::constant_evaluator`?\n\n### Backend changes\n\n- [ ] If your change introduces any new identifiers to generated code,\n      how did you ensure they won't conflict with the users'\n      identifiers? (This is usually not relevant to the SPIR-V\n      backend.)\n  - [ ] Did you use the `Namer` to generate a fresh identifier?\n  - [ ] Did you register the identifier as a reserved word with the `Namer`?\n  - [ ] Did you use a reserved prefix registered with the `Namer`?\n"
  },
  {
    "path": "docs/testing.md",
    "content": "# Testing in `wgpu` and `naga`\n\nThere exist a large variety of tests within the `wgpu` repository\nto make sure we can easily test all the aspects of our libraries.\nThis document serves as a guide to each class of test, and what\nthey are used for.\n\n## Requirements\n\nThe tests require that the [Vulkan SDK](https://vulkan.lunarg.com/sdk/home)\nis installed on the system and the `bin` folder of the SDK is in your `PATH`.\nWithout this some tests may fail to run, or report false negatives.\n\nAdditionally you require you run the tests with `cargo-nextest`.\nThis is what our xtask calls. You can install it with `cargo install cargo-nextest`.\n\n## Run All Tests\n\nTo run all tests, run `cargo xtask test` from the root of the repository.\n\n## Test Breakdown\n\nThis is a table of contents, in the form of the repository's directory structure.\n\n- benches\n   - [benches](#benchmark-tests)\n- [cts_runner](#webgpu-cts)\n- examples\n   - [features](#example-tests)\n- naga\n   - tests\n      - [example_wgsl](#naga-example-tests)\n      - [snapshot](#naga-snapshot-tests)\n      - [spirv-capabilities](#naga-spirv-capabilities-test)\n      - [validation](#naga-validation)\n      - [wgsl_errors](#naga-wgsl-error-tests)\n- player\n   - [tests](#player-tests)\n- tests\n   - [compile](#wgpu-compile-tests)\n   - [dependency](#wgpu-dependency-tests)\n   - [gpu](#wgpu-gpu-tests)\n   - [trace](#wgpu-trace-tests)\n   - [validation](#wgpu-validation-tests)\n\nAnd where applicable [unit-tests](#unit-tests)\nare scatteredthroughout the codebase.\n\n## Benchmark Tests\n\n- Located in: `benches/benches`\n- Run with `cargo nextest run --bench wgpu-benchmark`\n- `wgpu` benchmarks for performance testing.\n\nThese are benchmarks that test the performance of `wgpu` in various\nscenarios. When run as part of the test suite, they run a single\niteration of each benchmark to ensure they continue to function.\n\nThese tests only run on your system's default GPU.\n\nThe benchmarks should be very careful to avoid doing any significant\nwork (including connecting to a GPU) outside of the various `benchmark`\n`criterion` functions. If this is done, the benchmarks will take a long\ntime to list available tests, slowing down the test suite.\n\nTo run the benchmarks for benchmarking purposes, use `cargo bench`.\n\n## Example Tests\n\n- Located in: `examples/features`\n- Run with `cargo xtask test --bin wgpu-examples`\n- Uses a custom `#[gpu_test]` harness.\n- `wgpu` integration tests, with access to `wgpu_test` helpers.\n\nThese tests validate that the examples are functioning correctly\nand do not have any regressions. They use the same harness as the\n[gpu tests](#wgpu-gpu-tests), see that section for more information\non the harness.\n\nThese tests use `nv-flip`'s image comparison through the wgpu\nexample framework to validate that the images outputted by the\nexamples are within tolerance of the expected output.\n\nExamples written in `examples/standalone` do not have tests, as\nthey should be easy to copy into a standalone project.\n\n## `naga` Example Tests\n\n- Located in: `naga/tests/naga/example_wgsl`\n- Run with `cargo nextest run --test naga example_wgsl`\n\nThis simple test ensures that all wgsl files in the `examples`\ndirectory can be parsed by `naga`'s `wgsl` parser and validate correctly.\n\n## `naga` Snapshot Tests\n\n- Located in: `naga/tests/naga/snapshot`, `naga/tests/in`, and `naga/tests/out`\n- Run with `cargo nextest run --test naga snapshots`\n- Data driven snapshot tests for `naga`'s input/output.\n\nThese tests are snapshot tests for `naga`s parsers and code generators.\nThere are inputs in `wgsl`, `spirv`, and `glsl`. There are outputs for\n`hlsl`, `spirv`, `wgsl`, `msl`, `glsl`, and naga's internal IR. The tests\ncan be configured by a sidecar toml file of the same name as the input file.\n\nThis is the goto tool for testing all kinds of codegen and parsing features.\n\nTo avoid clutter we generally use the following pattern:\n\n- `wgsl` tests generate output to all backends.\n- `spirv`, `glsl` tests generate `wgsl` output\n\nThis \"butterfly\" pattern ensures we don't need to test the\nfull matrix of possibilities to get full coverage.\n\nWhile we do not run the results of the code generators, we do\ntest that the generated code is valid. This is done by running\n`cargo xtask validate <backend>` in the `naga` directory and\nwill use the respective tool to validate the generated code.\n\n## `naga` SPIR-V Capabilities Tests\n\n- Located in: `naga/tests/naga/spirv_capabilities`\n- Run with `cargo nextest run --test naga spirv_capabilities`\n- Uses the standard `#[test]` harness.\n\nThese tests convert the given wgsl snippet to spirv and\nthen assert that the spirv has enabled the expected capabilities.\n\n## `naga` Validation Tests\n\n- Located in: `naga/tests/naga/validation`\n- Run with `cargo nextest run --test naga validation`\n\nThese are hand rolled tests against the naga's validator.\nIf you don't need to test the validator with a custom module,\nand can use the `wgsl` frontend, you should put the test in\nthe [wgsl errors](#naga-wgsl-error-tests) tests.\n\n## `naga` WGSL Error Tests\n\n- Located in: `naga/tests/naga/wgsl_errors`\n- Run with `cargo nextest run --test naga wgsl_errors`\n\nThese are tests for the error messages that the `wgsl` frontend\nproduces. Additionally you can check that a given validation error\nis produced by the validator from a given `wgsl` snippet.\n\n## `player` Tests\n\n- Located in: `player/tests`\n- Run with `cargo nextest run --test player`\n- Data driven tests using the `player`'s replay system.\n- `wgpu` integration tests.\n\nThese are soft-deprecated tests which are another way to write\nAPI tests. These use captures of the api calls and replay them\nto assert on the behavior. They are very difficult to write, and\nthe trace capturing system is currently broken, so these\ntests exist, but you should not write new ones.\n\nThese tests only run on your system's default GPU.\n\n## `wgpu` Compile Tests\n\n- Located in: `tests/tests/wgpu-compile`\n- Run with `cargo nextest run --test wgpu-compile`\n- `trybuild` tests of all rust files in `tests/tests/wgpu-compile/fail` directory.\n\nThese use the `trybuild` crate to test a few scenarios where\nthe `wgpu` crate is expected to fail to compile. This mainly\nrevolves around ensuring lifetimes are properly handled when\ndropping passes, etc.\n\n## `wgpu` Dependency Tests\n\n- Located in: `tests/tests/wgpu-dependency`\n- Run with `cargo nextest run --test wgpu-dependency`\n- Tests against `cargo tree`.\n\nThese tests ensure that the `wgpu` crate has the correct dependency\ntree on all platforms. It's super easy to subtly mess up the dependencies\nwhich can cause issues or extra dependencies to be pulled in.\n\nThis provides a way to ensure that our `toml` files are correct.\n\n## `wgpu` GPU Tests\n\n- Located in: `tests/tests/wgpu-gpu`\n- Run with `cargo xtask test --test wgpu-gpu`\n- Uses a custom `#[gpu_test]` harness.\n- `wgpu` integration tests, with access to `wgpu_test` helpers.\n\nThese tests use a custom harness to run each test on all GPUs\navailable on the system. They are general integration tests\nthat write code against the normal `wgpu` API and assert on the behavior.\n\nThese tests are useful to check the runtime behavior of a program,\nvalidate that there are no validation errors coming from the\n`vulkan`/`dx12`/`metal` validation layers, and ensure behavior\nis the same across GPUs. If the test does not need to run on a\nreal GPU, it should be in the [validation tests](#wgpu-validation-tests) instead.\n\nThere is a special parameter system that deals with if a GPU\ncan support the given test, and dealing with expectation\nmanagement for tests that are expected to fail due to driver or wgpu bugs.\n\nNormal `#[test]`s will not be found in this test crate, as we use a custom harness.\n\nSee also the [example tests](#example-tests) for additional GPU tests.\n\n## `wgpu` Trace Tests\n\n- Located in: `tests/tests/wgpu_trace.rs`\n- Run with `cargo nextest run --test wgpu_trace`\n- Use the standard `#[test]` harness.\n\nThese tests are focused on testing the tracing functionality in `wgpu`.  They\nuse the a special `noop` backend which does not connect to a real GPU.\n\n## `wgpu` Validation Tests\n\n- Located in: `tests/tests/wgpu-validation`\n- Run with `cargo nextest run --test wgpu-validation`\n- Use the standard `#[test]` harness.\n- `wgpu` integration tests, with access to `wgpu_test` helpers.\n\nThese tests are focused on testing the validation inside of `wgpu-core`.\nThey are written against the `wgpu` API, but are targeting a special `noop`\nbackend which does not connect to a real GPU.\n\nThis is significantly faster and simpler than running on real hardware,\nand allows any validation logic to be checked, even if real hardware\ndoes not support those features.\n\n## Unit Tests\n\n- Located throughout the codebase.\n- Run with `cargo nextest test -p <package>`\n- Standard `#[test]`s.\n\nThroughout the codebase we have standard `#[test]`s that test individual\nfunctions or small parts of the codebase. These don't run on the gpu.\n\n## WebGPU CTS\n\nWebGPU includes a Conformance Test Suite to validate that implementations are\nworking correctly. We run cases from the CTS against wgpu using\n[Deno](https://deno.com/). A [default list of enabled\ntests](../cts_runner/test.lst) is automatically run on pull requests in CI.\n\nTo run the default set of CTS tests locally, run:\n\n```\ncargo xtask cts\n```\n\nYou can also specify a test selector on the command line:\n\n```\ncargo xtask cts 'webgpu:api,operation,command_buffer,basic:*'\n```\n\nOr supply your own test list in a file:\n\n```\ncargo xtask cts -f your_tests.lst\n```\n\nTo find the full list of tests, go to the\n[web-based standalone CTS runner](https://gpuweb.github.io/cts/standalone/?runnow=0&worker=0&debug=0&q=webgpu:*).\n\nThe version of the CTS used by `cargo xtask cts` is specified in\n[`cts_runner/revision.txt`](../cts_runner/revision.txt).\n"
  },
  {
    "path": "examples/README.md",
    "content": "> [!NOTE]  \n> These are the examples for the development version of wgpu. If you want to see the examples for the latest crates.io release\n> of wgpu, go to the [latest release branch](https://github.com/gfx-rs/wgpu/tree/v29/examples#readme).\n\n# Examples\n\nIf you are just starting your graphics programming journey entirely, we recommend going through [Learn-wgpu](https://sotrh.github.io/learn-wgpu/)\nfor a mode guided tutorial, which will also teach you the basics of graphics programming.\n\n## Standalone Examples\n\nAll the standalone examples are separate crates and include all boilerplate inside the example itself. They can\nbe cloned out of the repository to serve as a starting point for your own projects and are fully commented.\n\n| Name   | Description | Platforms |\n|--------|-------------|-----------|\n| ---    | Introductory Examples | --- |\n| [1. hello compute](standalone/01_hello_compute/) | Simplest example and shows how to run a compute shader on a given set of input data and get the results back. | Native-Only |\n| [2. hello window](standalone/02_hello_window/) | Shows how to create a window and render into it. | Native-Only |\n| --- | Special Examples | --- |\n| [custom backend](standalone/custom_backend/) | Shows how to implement and use custom wgpu context | All |\n\nYou can also use [`cargo-generate`](https://github.com/cargo-generate/cargo-generate) to easily use these as a basis for your own projects.\n\n```sh\ncargo generate gfx-rs/wgpu --branch v29\n```\n\n## Framework Examples\n\nThese examples use a common framework to handle wgpu init, window creation, and event handling. This allows the example to focus on the unique code in the example itself. Refer to the standalone examples for a more detailed look at the boilerplate code.\n\n#### Graphics\n\n- `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.\n- `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.\n- `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.\n- `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.\n- `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`.\n- `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.\n- `multiple-render-targets` - Demonstrates how to render to two texture targets simultaneously from fragment shader.\n- `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.\n- `render_with_compute` - Renders an image using compute shaders.\n- `ray_cube_fragment` - Demonstrates using ray queries with a fragment shader.\n- `ray_scene` - Demonstrates using ray queries and model loading\n- `ray_shadows` - Demonstrates a simple use of ray queries - high quality shadows - uses a light set with immediates to raytrace through an untransformed scene and detect whether there is something obstructing the light.\n- `mesh_shader` - Renders a triangle to a window with mesh shaders, while showcasing most mesh shader related features(task shaders, payloads, per primitive data).\n\n#### Compute\n\n- `hello_compute` - Demonstrates the basic workflow for getting arrays of numbers to the GPU, executing a shader on them, and getting the results back. The operation it performs is finding the Collatz value (how many iterations of the [Collatz equation](https://en.wikipedia.org/wiki/Collatz_conjecture) it takes for the number to either reach 1 or overflow) of a set of numbers and prints the results.\n- `repeated_compute` - Mostly for going into detail on subjects `hello-compute` did not. It, too, computes the Collatz conjecture, but this time, it automatically loads large arrays of randomly generated numbers, prints them, runs them, and prints the result. It does this cycle 10 times.\n- `hello_workgroups` - Teaches the user about the basics of compute workgroups; what they are and what they can do.\n- `hello_synchronization` - Teaches the user about synchronization in WGSL, the ability to force all invocations in a workgroup to synchronize with each other before continuing via a sort of barrier.\n- `storage_texture` - Demonstrates the use of storage textures as outputs to compute shaders. The example on the outside seems very similar to `render_to_texture` in that it outputs an image either to the file system or the web page, except displaying a grayscale render of the Mandelbrot Set. However, inside, the example dispatches a grid of compute workgroups, one for each pixel, which calculates the pixel value and stores it to the corresponding pixel of the output storage texture. This example either outputs an image file of your naming (pass command line arguments after specifying a `--` like `cargo run --bin wgpu-examples -- storage_texture \"test.png\"`) or adds an `img` element containing the image to the page in WASM.\n- `big_compute_buffers` - Demonstrates how you can split _large_ datasets across multiple buffers, using `binding_array` in your `wgsl` [NOTE: native only, no WASM support].\n\n#### Combined\n\n- `boids` - Demonstrates how to combine compute and render workflows by performing a [boid](https://en.wikipedia.org/wiki/Boids) simulation and rendering the boids to the screen as little triangles.\n- `ray_cube_compute` - Demonstrates using ray queries with a compute shader.\n- `ray_traced_triangle` - A simpler example demonstrating using ray queries with a compute shader\n\n## Running on the Web\n\nTo run the examples in a browser, run `cargo xtask run-wasm`.\nThen open `http://localhost:8000` in your browser, and you can choose an example to run.\nNaturally, in order to display any of the WebGPU based examples, you need to make sure your browser supports it.\n"
  },
  {
    "path": "examples/bug-repro/01_texture_atomic_bug/Cargo.toml",
    "content": "[package]\nname = \"wgpu-bug-repro-01-texture-atomic-bug\"\nedition = \"2021\"\nrust-version = \"1.87\"\npublish = false\n\n[dependencies]\nenv_logger = \"0.11\"\npollster = \"0.4\"\nwgpu = \"29.0.0\"\nwinit = \"0.30.8\"\n"
  },
  {
    "path": "examples/bug-repro/01_texture_atomic_bug/src/main.rs",
    "content": "//! Repro for Metal driver bug where fragment shader texture atomic writes randomly drop unless\n//! a compute pass with atomic access to the texture is inserted between the write and the read.\n//! Both 32-bit and 64-bit atomic textures are affected.\n//! The bug does not reproduce with `MTL_SHADER_VALIDATION=1`.\n//! Known to reproduce on Apple M4 Max, macOS 26.3 (Tahoe).\n//! Dropped writes appear as various tile-shaped black holes that flicker around each frame.\n\nuse std::sync::Arc;\n\nuse winit::application::ApplicationHandler;\nuse winit::event::WindowEvent;\nuse winit::event_loop::{ActiveEventLoop, EventLoop};\nuse winit::window::{Window, WindowId};\n\nfn main() {\n    env_logger::init();\n    let event_loop = EventLoop::new().unwrap();\n    event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);\n    let mut app = App::default();\n    event_loop.run_app(&mut app).unwrap();\n}\n\n#[derive(Default)]\nstruct App {\n    state: Option<State>,\n}\n\nstruct State {\n    instance: wgpu::Instance,\n    window: Arc<Window>,\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n    surface: wgpu::Surface<'static>,\n    surface_config: wgpu::SurfaceConfiguration,\n    width: u32,\n    height: u32,\n    _storage_texture: wgpu::Texture,\n    _dummy_texture: wgpu::Texture,\n    clear_pipeline: wgpu::ComputePipeline,\n    clear_bg: wgpu::BindGroup,\n    raster_pipeline: wgpu::RenderPipeline,\n    raster_bg: wgpu::BindGroup,\n    dummy_view: wgpu::TextureView,\n    vis_pipeline: wgpu::RenderPipeline,\n    vis_bg: wgpu::BindGroup,\n}\n\nimpl ApplicationHandler for App {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        if self.state.is_some() {\n            return;\n        }\n        let window = Arc::new(\n            event_loop\n                .create_window(\n                    Window::default_attributes()\n                        .with_title(\"Metal Texture Atomic Bug\")\n                        .with_inner_size(winit::dpi::LogicalSize::new(2560, 1440)),\n                )\n                .unwrap(),\n        );\n        self.state = Some(State::new(window));\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        _window_id: WindowId,\n        event: WindowEvent,\n    ) {\n        let Some(state) = &mut self.state else { return };\n        match event {\n            WindowEvent::CloseRequested => event_loop.exit(),\n            WindowEvent::Resized(size) if size.width > 0 && size.height > 0 => {\n                state.surface_config.width = size.width;\n                state.surface_config.height = size.height;\n                state\n                    .surface\n                    .configure(&state.device, &state.surface_config);\n            }\n            WindowEvent::RedrawRequested => state.render_frame(),\n            _ => {}\n        }\n    }\n\n    fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {\n        if let Some(state) = &self.state {\n            state.window.request_redraw();\n        }\n    }\n}\n\nfn tex_bind_entry(binding: u32, resource: wgpu::BindingResource<'_>) -> wgpu::BindGroupEntry<'_> {\n    wgpu::BindGroupEntry { binding, resource }\n}\n\nimpl State {\n    fn new(window: Arc<Window>) -> Self {\n        let size = window.inner_size();\n        let width = size.width.max(1);\n        let height = size.height.max(1);\n\n        let instance =\n            wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());\n        let surface = instance.create_surface(window.clone()).unwrap();\n        let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {\n            compatible_surface: Some(&surface),\n            ..Default::default()\n        }))\n        .expect(\"No adapter\");\n\n        println!(\"Adapter: {:?}\", adapter.get_info().name);\n\n        let required = wgpu::Features::TEXTURE_ATOMIC;\n        assert!(\n            adapter.features().contains(required),\n            \"Texture atomics not supported\"\n        );\n\n        let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n            required_features: required,\n            ..Default::default()\n        }))\n        .unwrap();\n\n        let surface_format = surface.get_capabilities(&adapter).formats[0];\n        let surface_config = wgpu::SurfaceConfiguration {\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            format: surface_format,\n            width,\n            height,\n            present_mode: wgpu::PresentMode::AutoVsync,\n            alpha_mode: wgpu::CompositeAlphaMode::Auto,\n            view_formats: vec![],\n            desired_maximum_frame_latency: 2,\n        };\n        surface.configure(&device, &surface_config);\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let tex_size = wgpu::Extent3d {\n            width,\n            height,\n            depth_or_array_layers: 1,\n        };\n\n        let storage_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: tex_size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R32Uint,\n            usage: wgpu::TextureUsages::STORAGE_ATOMIC | wgpu::TextureUsages::STORAGE_BINDING,\n            view_formats: &[],\n        });\n        let storage_view = storage_texture.create_view(&Default::default());\n\n        let dummy_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: tex_size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R8Uint,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n        let dummy_view = dummy_texture.create_view(&Default::default());\n\n        // Pipelines use auto-layout; bind groups derived from pipeline layouts.\n        let clear_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"clear\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n        let clear_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &clear_pipeline.get_bind_group_layout(0),\n            entries: &[tex_bind_entry(\n                0,\n                wgpu::BindingResource::TextureView(&storage_view),\n            )],\n        });\n\n        let raster_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"fullscreen\"),\n                buffers: &[],\n                compilation_options: Default::default(),\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"write_atomic\"),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::R8Uint,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::empty(),\n                })],\n                compilation_options: Default::default(),\n            }),\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: Default::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n        let raster_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &raster_pipeline.get_bind_group_layout(0),\n            entries: &[tex_bind_entry(\n                0,\n                wgpu::BindingResource::TextureView(&storage_view),\n            )],\n        });\n\n        let vis_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"fullscreen\"),\n                buffers: &[],\n                compilation_options: Default::default(),\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"visualize\"),\n                targets: &[Some(surface_format.into())],\n                compilation_options: Default::default(),\n            }),\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: Default::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n        let vis_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &vis_pipeline.get_bind_group_layout(0),\n            entries: &[tex_bind_entry(\n                0,\n                wgpu::BindingResource::TextureView(&storage_view),\n            )],\n        });\n\n        State {\n            instance,\n            window,\n            device,\n            queue,\n            surface,\n            surface_config,\n            width,\n            height,\n            _storage_texture: storage_texture,\n            _dummy_texture: dummy_texture,\n            clear_pipeline,\n            clear_bg,\n            raster_pipeline,\n            raster_bg,\n            dummy_view,\n            vis_pipeline,\n            vis_bg,\n        }\n    }\n\n    fn render_frame(&mut self) {\n        let frame = match self.surface.get_current_texture() {\n            wgpu::CurrentSurfaceTexture::Success(f) => f,\n            wgpu::CurrentSurfaceTexture::Suboptimal(_) | wgpu::CurrentSurfaceTexture::Outdated => {\n                self.surface.configure(&self.device, &self.surface_config);\n                return;\n            }\n            wgpu::CurrentSurfaceTexture::Lost => {\n                self.surface = self.instance.create_surface(self.window.clone()).unwrap();\n                self.surface.configure(&self.device, &self.surface_config);\n                return;\n            }\n            _ => return,\n        };\n        let frame_view = frame.texture.create_view(&Default::default());\n        let mut enc = self.device.create_command_encoder(&Default::default());\n\n        // Clear texture to zero\n        {\n            let mut pass = enc.begin_compute_pass(&Default::default());\n            pass.set_pipeline(&self.clear_pipeline);\n            pass.set_bind_group(0, &self.clear_bg, &[]);\n            pass.dispatch_workgroups(self.width.div_ceil(8), self.height.div_ceil(8), 1);\n        }\n\n        // Write via textureAtomicMax in fragment shader\n        {\n            let mut pass = enc.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &self.dummy_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Discard,\n                    },\n                })],\n                ..Default::default()\n            });\n            pass.set_pipeline(&self.raster_pipeline);\n            pass.set_bind_group(0, &self.raster_bg, &[]);\n            pass.draw(0..3, 0..1);\n        }\n\n        // Read texture in fragment shader to visualize\n        {\n            let mut pass = enc.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &frame_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                ..Default::default()\n            });\n            pass.set_pipeline(&self.vis_pipeline);\n            pass.set_bind_group(0, &self.vis_bg, &[]);\n            pass.draw(0..3, 0..1);\n        }\n\n        self.queue.submit([enc.finish()]);\n        frame.present();\n    }\n}\n"
  },
  {
    "path": "examples/bug-repro/01_texture_atomic_bug/src/shader.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_storage_2d<r32uint, atomic>;\n\n@compute @workgroup_size(8, 8, 1)\nfn clear(@builtin(global_invocation_id) id: vec3<u32>) {\n    let dims = textureDimensions(tex);\n    if id.x >= dims.x || id.y >= dims.y { return; }\n    textureStore(tex, id.xy, vec4<u32>(0u));\n}\n\n@vertex\nfn fullscreen(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n    let x = f32(i32(vi) / 2) * 4.0 - 1.0;\n    let y = f32(i32(vi) % 2) * 4.0 - 1.0;\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n@fragment\nfn write_atomic(@builtin(position) pos: vec4<f32>) {\n    textureAtomicMax(tex, vec2<u32>(pos.xy), 1u);\n}\n\n@group(0) @binding(0)\nvar tex_read: texture_storage_2d<r32uint, read>;\n\n@fragment\nfn visualize(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {\n    let val = textureLoad(tex_read, vec2<u32>(pos.xy)).r;\n    if val == 1u { return vec4<f32>(0.0, 0.8, 0.0, 1.0); }\n    if val == 0u { return vec4<f32>(0.0, 0.0, 0.0, 1.0); }\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "examples/features/Cargo.toml",
    "content": "[package]\nname = \"wgpu-examples\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Common example code\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\npublish = false\n\n[package.metadata.cargo-machete]\n# Cargo machete struggles with this dev dependency:\nignored = [\"wasm_bindgen_test\"]\n\n[lib]\npath = \"src/lib.rs\"\nharness = false\nbench = false\n\n[[bin]]\nname = \"wgpu-examples\"\npath = \"src/main.rs\"\ntest = false\n\n[features]\ndefault = []\nwebgl = [\"wgpu/webgl\"]\nwebgpu = [\"wgpu/webgpu\"]\n\n[dependencies]\nbytemuck.workspace = true\ncfg-if.workspace = true\nencase = { workspace = true }\nflume.workspace = true\nglam = { workspace = true, features = [\"bytemuck\", \"encase\"] }\nhalf = { workspace = true, features = [\"bytemuck\"] }\nktx2.workspace = true\nlog.workspace = true\nnanorand = { workspace = true, features = [\"getrandom\"] }\nnoise.workspace = true\nobj.workspace = true\npng.workspace = true\npollster.workspace = true\nweb-time.workspace = true\nwinit.workspace = true\n\n[dev-dependencies]\nwgpu-test.workspace = true\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nenv_logger.workspace = true\nwgpu = { workspace = true, features = [\"trace\"] }\n\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\nconsole_error_panic_hook.workspace = true\nconsole_log.workspace = true\nfern.workspace = true\nwasm-bindgen.workspace = true\nwasm-bindgen-futures.workspace = true\nwgpu = { workspace = true, default-features = false, features = [\n    \"wgsl\",\n    \"std\",\n    \"trace\",\n] }\n# We need these features in the framework examples and tests\nweb-sys = { workspace = true, features = [\n    \"Location\",\n    \"Blob\",\n    \"RequestInit\",\n    \"RequestMode\",\n    \"Request\",\n    \"ImageData\",\n    \"Response\",\n    \"HtmlImageElement\",\n    \"WebGl2RenderingContext\",\n    \"CanvasRenderingContext2d\",\n] }\n\n[target.'cfg(target_arch = \"wasm32\")'.dev-dependencies]\nwasm-bindgen-test.workspace = true\n\n[lints.clippy]\ndisallowed_types = \"allow\"\n"
  },
  {
    "path": "examples/features/src/big_compute_buffers/README.md",
    "content": "# big-compute-buffers\n\n*NOTE: `binding_array` is Vulkan only.*\n\nThis example assumes you're familiar with the other GP-GPU compute examples in this repository, if you're not you should go look at those first.\n\nThis example also assumes you've specifically come here looking to do this, because you want at least the following:\n\n1. To be working on your 'data' in your shader treating it contiguously, not batching etc.\n2. The data you are wanting to work on does **not** fit within a single buffer on your device, see the [hello](https://github.com/gfx-rs/wgpu/tree/trunk/examples/src/hello) example for how to print information about your unique device to explore its maximum supported buffer size.\n\nDemonstrates how to split larger datasets (things too big to fit into a single buffer), across multiple buffers.\n\n- Creates a set of buffers totalling `1GB`, full of `0.0f32`.\n- Moves those buffers to the DEVICE.\n- Increments each element in each set of buffers by `1.0`, on the DEVICE.\n- Returns those modified buffers full of `1.0` values as a back to the HOST.\n\n## Caution\n\n- Large buffers can fail to allocate due to fragmentation issues, you will **always** need not only the appropriate amount of space required for your buffer(s) but, that space will also need to be contiguous within GPU/Device memory for this strategy to work.\n\nYou can read more about fragmentation [here](https://developer.nvidia.com/docs/drive/drive-os/archives/6.0.4/linux/sdk/common/topics/graphics_content/avoiding_memory_fragmentation.html).\n\n## To Run\n\n```sh\n# linux/mac\nRUST_LOG=wgpu_examples::big_compute_buffers=info cargo run -r --bin wgpu-examples -- big_compute_buffers\n\n# windows (Powershell)\n$env:WGPU_BACKEND=\"Vulkan\"; $env:RUST_LOG=\"wgpu_examples::big_compute_buffers=info\"; cargo run -r --bin wgpu-examples -- big_compute_buffers\n```\n\n## Example Output\n\n```txt\n[2024-09-29T11:47:55Z INFO  wgpu_examples::big_compute_buffers] All 0.0s\n[2024-09-29T11:47:58Z INFO  wgpu_examples::big_compute_buffers] GPU RUNTIME: 3228ms\n[2024-09-29T11:47:58Z INFO  wgpu_examples::big_compute_buffers] All 1.0s\n```\n"
  },
  {
    "path": "examples/features/src/big_compute_buffers/mod.rs",
    "content": "//! This example shows you a potential course for when your 'data' is too large\n//! for a single Buffer.\n//!\n//! A lot of things aren't explained here via comments. See hello-compute and\n//! repeated-compute for code that is more thoroughly commented.\n\nuse std::num::{NonZeroU32, NonZeroU64};\nuse wgpu::{util::DeviceExt, Features};\n\n// These are set by the minimum required defaults for webgpu.\nconst MAX_BUFFER_SIZE: u64 = 1 << 27; // 134_217_728 // 134MB\nconst MAX_DISPATCH_SIZE: u32 = (1 << 16) - 1;\n\npub async fn execute_gpu(numbers: &[f32]) -> Vec<f32> {\n    let instance = wgpu::Instance::default();\n\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .unwrap();\n\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            // These features are required to use `binding_array` in your wgsl.\n            // Without them your shader may fail to compile.\n            required_features: Features::BUFFER_BINDING_ARRAY\n                | Features::STORAGE_RESOURCE_BINDING_ARRAY\n                | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n            memory_hints: wgpu::MemoryHints::Performance,\n            required_limits: wgpu::Limits {\n                max_buffer_size: MAX_BUFFER_SIZE,\n                max_binding_array_elements_per_shader_stage: 8,\n                ..Default::default()\n            },\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n\n    execute_gpu_inner(&device, &queue, numbers).await\n}\n\npub async fn execute_gpu_inner(\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n    numbers: &[f32],\n) -> Vec<f32> {\n    let (staging_buffers, storage_buffers, bind_group, compute_pipeline) = setup(device, numbers);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"compute pass descriptor\"),\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&compute_pipeline);\n        cpass.set_bind_group(0, Some(&bind_group), &[]);\n\n        cpass.dispatch_workgroups(MAX_DISPATCH_SIZE.min(numbers.len() as u32), 1, 1);\n    }\n\n    for (storage_buffer, staging_buffer) in storage_buffers.iter().zip(staging_buffers.iter()) {\n        let stg_size = staging_buffer.size();\n\n        encoder.copy_buffer_to_buffer(\n            storage_buffer, // Source buffer\n            0,\n            staging_buffer, // Destination buffer\n            0,\n            stg_size,\n        );\n    }\n\n    queue.submit(Some(encoder.finish()));\n\n    for staging_buffer in &staging_buffers {\n        let slice = staging_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| {});\n    }\n\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n    let mut data = Vec::new();\n    for staging_buffer in &staging_buffers {\n        let slice = staging_buffer.slice(..);\n        let mapped = slice.get_mapped_range();\n        data.extend_from_slice(bytemuck::cast_slice(&mapped));\n        drop(mapped);\n        staging_buffer.unmap();\n    }\n\n    data\n}\n\nfn setup(\n    device: &wgpu::Device,\n    numbers: &[f32],\n) -> (\n    Vec<wgpu::Buffer>,\n    Vec<wgpu::Buffer>,\n    wgpu::BindGroup,\n    wgpu::ComputePipeline,\n) {\n    let cs_module = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    let staging_buffers = create_staging_buffers(device, numbers);\n    let storage_buffers = create_storage_buffers(device, numbers);\n\n    let (bind_group_layout, bind_group) = setup_binds(&storage_buffers, device);\n\n    let compute_pipeline = setup_pipeline(device, bind_group_layout, cs_module);\n    (\n        staging_buffers,\n        storage_buffers,\n        bind_group,\n        compute_pipeline,\n    )\n}\n\nfn setup_pipeline(\n    device: &wgpu::Device,\n    bind_group_layout: wgpu::BindGroupLayout,\n    cs_module: wgpu::ShaderModule,\n) -> wgpu::ComputePipeline {\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: Some(\"Compute Pipeline Layout\"),\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n\n    device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: Some(\"Compute Pipeline\"),\n        layout: Some(&pipeline_layout),\n        module: &cs_module,\n        entry_point: Some(\"main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    })\n}\n\nfn setup_binds(\n    storage_buffers: &[wgpu::Buffer],\n    device: &wgpu::Device,\n) -> (wgpu::BindGroupLayout, wgpu::BindGroup) {\n    let buffers: Vec<_> = storage_buffers\n        .iter()\n        .map(|b| b.as_entire_buffer_binding())\n        .collect();\n\n    let entry = wgpu::BindGroupEntry {\n        binding: 0,\n        resource: wgpu::BindingResource::BufferArray(&buffers),\n    };\n\n    let bgl_entry = wgpu::BindGroupLayoutEntry {\n        binding: 0,\n        visibility: wgpu::ShaderStages::COMPUTE,\n        ty: wgpu::BindingType::Buffer {\n            ty: wgpu::BufferBindingType::Storage { read_only: false },\n            has_dynamic_offset: false,\n            min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n        },\n        count: Some(NonZeroU32::new(buffers.len() as u32).unwrap()),\n    };\n\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: Some(\"Custom Storage Bind Group Layout\"),\n        entries: &[bgl_entry],\n    });\n\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"Combined Storage Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[entry],\n    });\n\n    (bind_group_layout, bind_group)\n}\n\nfn calculate_chunks(numbers: &[f32], max_buffer_size: u64) -> Vec<&[f32]> {\n    let max_elements_per_chunk = max_buffer_size as usize / std::mem::size_of::<f32>();\n    numbers.chunks(max_elements_per_chunk).collect()\n}\n\nfn create_storage_buffers(device: &wgpu::Device, numbers: &[f32]) -> Vec<wgpu::Buffer> {\n    let chunks = calculate_chunks(numbers, MAX_BUFFER_SIZE);\n\n    chunks\n        .iter()\n        .enumerate()\n        .map(|(e, seg)| {\n            device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                label: Some(&format!(\"Storage Buffer-{e}\")),\n                contents: bytemuck::cast_slice(seg),\n                usage: wgpu::BufferUsages::STORAGE\n                    | wgpu::BufferUsages::COPY_DST\n                    | wgpu::BufferUsages::COPY_SRC,\n            })\n        })\n        .collect()\n}\n\nfn create_staging_buffers(device: &wgpu::Device, numbers: &[f32]) -> Vec<wgpu::Buffer> {\n    let chunks = calculate_chunks(numbers, MAX_BUFFER_SIZE);\n\n    (0..chunks.len())\n        .map(|e| {\n            let size = std::mem::size_of_val(chunks[e]) as u64;\n\n            device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(&format!(\"staging buffer-{e}\")),\n                size,\n                usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n                mapped_at_creation: false,\n            })\n        })\n        .collect()\n}\n\n#[cfg_attr(target_arch = \"wasm32\", allow(clippy::allow_attributes, dead_code))]\nasync fn run() {\n    let numbers = {\n        const BYTES_PER_GB: usize = 1024 * 1024 * 1024;\n        // 4 bytes per f32\n        let elements = (BYTES_PER_GB as f32 / 4.0) as usize;\n        vec![0.0; elements]\n    };\n    assert!(numbers.iter().all(|n| *n == 0.0));\n    log::info!(\"All 0.0s\");\n    let t1 = std::time::Instant::now();\n    let results = execute_gpu(&numbers).await;\n    log::info!(\"GPU RUNTIME: {}ms\", t1.elapsed().as_millis());\n    assert_eq!(numbers.len(), results.len());\n    assert!(results.iter().all(|n| *n == 1.0));\n    log::info!(\"All 1.0s\");\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::init();\n        pollster::block_on(run());\n    }\n}\n\n#[cfg(test)]\n#[cfg(not(target_arch = \"wasm32\"))]\npub mod tests;\n"
  },
  {
    "path": "examples/features/src/big_compute_buffers/shader.wgsl",
    "content": "const OFFSET: u32 = 1u << 8u;\nconst BUFFER_MAX_ELEMENTS: u32 = 1u << 25u; // Think `buffer.len()`\nconst NUM_BUFFERS: u32 = 8u;\nconst TOTAL_SIZE: u32 = BUFFER_MAX_ELEMENTS * NUM_BUFFERS;\n\n\n// `binding_array` requires a custom struct\nstruct ContiguousArray {\n    inner: array<f32>\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> storage_array: binding_array<ContiguousArray, NUM_BUFFERS>;\n\n\n@compute @workgroup_size(256, 1, 1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    let base_index = global_id.x * OFFSET;\n\n    for (var i = 0u; i < OFFSET; i++) {\n        let index = base_index + i;\n\n        if index < TOTAL_SIZE {\n            let buffer_index = index / BUFFER_MAX_ELEMENTS;\n            let inner_index = index % BUFFER_MAX_ELEMENTS;\n\n            storage_array[buffer_index].inner[inner_index] = add_one(storage_array[buffer_index].inner[inner_index]);\n        }\n    }\n}\n\nfn add_one(n: f32) -> f32 {\n    return n + 1.0;\n}\n"
  },
  {
    "path": "examples/features/src/big_compute_buffers/tests.rs",
    "content": "use super::*;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};\n\n#[gpu_test]\npub static TWO_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(\n                Features::BUFFER_BINDING_ARRAY\n                    | Features::STORAGE_RESOURCE_BINDING_ARRAY\n                    | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n            )\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits {\n                max_buffer_size: MAX_BUFFER_SIZE,\n                max_binding_array_elements_per_shader_stage: 8,\n                ..Default::default()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n    )\n    .run_async(|ctx| {\n        // The test environment's GPU reports 134MB as the max storage buffer size.https://github.com/gfx-rs/wgpu/actions/runs/11001397782/job/30546188996#step:12:1096\n        const SIZE: usize = (1 << 27) / std::mem::size_of::<f32>() * 8;\n        // 2 Buffers worth, of 0.0s.\n        let input = &[0.0; SIZE];\n\n        async move { assert_execute_gpu(&ctx.device, &ctx.queue, input).await }\n    });\n\nasync fn assert_execute_gpu(device: &wgpu::Device, queue: &wgpu::Queue, input: &[f32]) {\n    let expected_len = input.len();\n    let produced = execute_gpu_inner(device, queue, input).await;\n\n    assert_eq!(produced.len(), expected_len);\n    assert!(produced.into_iter().all(|v| v == 1.0));\n}\n"
  },
  {
    "path": "examples/features/src/boids/README.md",
    "content": "# boids\n\nFlocking boids example with gpu compute update pass\n\n## To Run\n\n```\ncargo run --bin wgpu-examples boids\n```\n\n## Screenshots\n\n![Boids example](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/boids/compute.wgsl",
    "content": "struct Particle {\n  pos : vec2<f32>,\n  vel : vec2<f32>,\n};\n\nstruct SimParams {\n  deltaT : f32,\n  rule1Distance : f32,\n  rule2Distance : f32,\n  rule3Distance : f32,\n  rule1Scale : f32,\n  rule2Scale : f32,\n  rule3Scale : f32,\n};\n\n@group(0) @binding(0) var<uniform> params : SimParams;\n@group(0) @binding(1) var<storage, read> particlesSrc : array<Particle>;\n@group(0) @binding(2) var<storage, read_write> particlesDst : array<Particle>;\n\n// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp\n@compute\n@workgroup_size(64)\nfn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {\n  let total = arrayLength(&particlesSrc);\n  let index = global_invocation_id.x;\n  if (index >= total) {\n    return;\n  }\n\n  var vPos : vec2<f32> = particlesSrc[index].pos;\n  var vVel : vec2<f32> = particlesSrc[index].vel;\n\n  var cMass : vec2<f32> = vec2<f32>(0.0, 0.0);\n  var cVel : vec2<f32> = vec2<f32>(0.0, 0.0);\n  var colVel : vec2<f32> = vec2<f32>(0.0, 0.0);\n  var cMassCount : i32 = 0;\n  var cVelCount : i32 = 0;\n\n  var i : u32 = 0u;\n  loop {\n    if (i >= total) {\n      break;\n    }\n    if (i == index) {\n      continue;\n    }\n\n    let pos = particlesSrc[i].pos;\n    let vel = particlesSrc[i].vel;\n\n    if (distance(pos, vPos) < params.rule1Distance) {\n      cMass += pos;\n      cMassCount += 1;\n    }\n    if (distance(pos, vPos) < params.rule2Distance) {\n      colVel -= pos - vPos;\n    }\n    if (distance(pos, vPos) < params.rule3Distance) {\n      cVel += vel;\n      cVelCount += 1;\n    }\n\n    continuing {\n      i = i + 1u;\n    }\n  }\n  if (cMassCount > 0) {\n    cMass = cMass * (1.0 / f32(cMassCount)) - vPos;\n  }\n  if (cVelCount > 0) {\n    cVel *= 1.0 / f32(cVelCount);\n  }\n\n  vVel = vVel + (cMass * params.rule1Scale) +\n      (colVel * params.rule2Scale) +\n      (cVel * params.rule3Scale);\n\n  // clamp velocity for a more pleasing simulation\n  vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);\n\n  // kinematic update\n  vPos += vVel * params.deltaT;\n\n  // Wrap around boundary\n  if (vPos.x < -1.0) {\n    vPos.x = 1.0;\n  }\n  if (vPos.x > 1.0) {\n    vPos.x = -1.0;\n  }\n  if (vPos.y < -1.0) {\n    vPos.y = 1.0;\n  }\n  if (vPos.y > 1.0) {\n    vPos.y = -1.0;\n  }\n\n  // Write back\n  particlesDst[index] = Particle(vPos, vVel);\n}\n"
  },
  {
    "path": "examples/features/src/boids/draw.wgsl",
    "content": "@vertex\nfn main_vs(\n    @location(0) particle_pos: vec2<f32>,\n    @location(1) particle_vel: vec2<f32>,\n    @location(2) position: vec2<f32>,\n) -> @builtin(position) vec4<f32> {\n    let angle = -atan2(particle_vel.x, particle_vel.y);\n    let pos = vec2<f32>(\n        position.x * cos(angle) - position.y * sin(angle),\n        position.x * sin(angle) + position.y * cos(angle)\n    );\n    return vec4<f32>(pos + particle_pos, 0.0, 1.0);\n}\n\n@fragment\nfn main_fs() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/boids/mod.rs",
    "content": "// Flocking boids example with gpu compute update pass\n// adapted from https://github.com/austinEng/webgpu-samples/blob/master/src/examples/computeBoids.ts\n\nuse nanorand::{Rng, WyRand};\nuse wgpu::util::DeviceExt;\n\n// number of boid particles to simulate\n\nconst NUM_PARTICLES: u32 = 1500;\n\n// number of single-particle calculations (invocations) in each gpu work group\n\nconst PARTICLES_PER_GROUP: u32 = 64;\n\n/// Example struct holds references to wgpu resources and frame persistent data\nstruct Example {\n    particle_bind_groups: Vec<wgpu::BindGroup>,\n    particle_buffers: Vec<wgpu::Buffer>,\n    vertices_buffer: wgpu::Buffer,\n    compute_pipeline: wgpu::ComputePipeline,\n    render_pipeline: wgpu::RenderPipeline,\n    work_group_count: u32,\n    frame_num: usize,\n}\n\nimpl crate::framework::Example for Example {\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::downlevel_defaults()\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    /// constructs initial instance of Example struct\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let compute_shader = device.create_shader_module(wgpu::include_wgsl!(\"compute.wgsl\"));\n        let draw_shader = device.create_shader_module(wgpu::include_wgsl!(\"draw.wgsl\"));\n\n        // buffer for simulation parameters uniform\n\n        let sim_param_data = [\n            0.04f32, // deltaT\n            0.1,     // rule1Distance\n            0.025,   // rule2Distance\n            0.025,   // rule3Distance\n            0.02,    // rule1Scale\n            0.05,    // rule2Scale\n            0.005,   // rule3Scale\n        ]\n        .to_vec();\n        let sim_param_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Simulation Parameter Buffer\"),\n            contents: bytemuck::cast_slice(&sim_param_data),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        // create compute bind layout group and compute pipeline layout\n\n        let compute_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new(\n                                (sim_param_data.len() * size_of::<f32>()) as _,\n                            ),\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: true },\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new((NUM_PARTICLES * 16) as _),\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 2,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new((NUM_PARTICLES * 16) as _),\n                        },\n                        count: None,\n                    },\n                ],\n                label: None,\n            });\n        let compute_pipeline_layout =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"compute\"),\n                bind_group_layouts: &[Some(&compute_bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        // create render pipeline with empty bind group layout\n\n        let render_pipeline_layout =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"render\"),\n                bind_group_layouts: &[],\n                immediate_size: 0,\n            });\n\n        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&render_pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &draw_shader,\n                entry_point: Some(\"main_vs\"),\n                compilation_options: Default::default(),\n                buffers: &[\n                    wgpu::VertexBufferLayout {\n                        array_stride: 4 * 4,\n                        step_mode: wgpu::VertexStepMode::Instance,\n                        attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],\n                    },\n                    wgpu::VertexBufferLayout {\n                        array_stride: 2 * 4,\n                        step_mode: wgpu::VertexStepMode::Vertex,\n                        attributes: &wgpu::vertex_attr_array![2 => Float32x2],\n                    },\n                ],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &draw_shader,\n                entry_point: Some(\"main_fs\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        // create compute pipeline\n\n        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"Compute pipeline\"),\n            layout: Some(&compute_pipeline_layout),\n            module: &compute_shader,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        // buffer for the three 2d triangle vertices of each instance\n\n        let vertex_buffer_data = [-0.01f32, -0.02, 0.01, -0.02, 0.00, 0.02];\n        let vertices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::bytes_of(&vertex_buffer_data),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n        // buffer for all particles data of type [(posx,posy,velx,vely),...]\n\n        let mut initial_particle_data = vec![0.0f32; (4 * NUM_PARTICLES) as usize];\n        let mut rng = WyRand::new_seed(42);\n        let mut unif = || rng.generate::<f32>() * 2f32 - 1f32; // Generate a num (-1, 1)\n        for particle_instance_chunk in initial_particle_data.chunks_mut(4) {\n            particle_instance_chunk[0] = unif(); // posx\n            particle_instance_chunk[1] = unif(); // posy\n            particle_instance_chunk[2] = unif() * 0.1; // velx\n            particle_instance_chunk[3] = unif() * 0.1; // vely\n        }\n\n        // creates two buffers of particle data each of size NUM_PARTICLES\n        // the two buffers alternate as dst and src for each frame\n\n        let mut particle_buffers = Vec::<wgpu::Buffer>::new();\n        let mut particle_bind_groups = Vec::<wgpu::BindGroup>::new();\n        for i in 0..2 {\n            particle_buffers.push(\n                device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                    label: Some(&format!(\"Particle Buffer {i}\")),\n                    contents: bytemuck::cast_slice(&initial_particle_data),\n                    usage: wgpu::BufferUsages::VERTEX\n                        | wgpu::BufferUsages::STORAGE\n                        | wgpu::BufferUsages::COPY_DST,\n                }),\n            );\n        }\n\n        // create two bind groups, one for each buffer as the src\n        // where the alternate buffer is used as the dst\n\n        for i in 0..2 {\n            particle_bind_groups.push(device.create_bind_group(&wgpu::BindGroupDescriptor {\n                layout: &compute_bind_group_layout,\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: sim_param_buffer.as_entire_binding(),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: particle_buffers[i].as_entire_binding(),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 2,\n                        resource: particle_buffers[(i + 1) % 2].as_entire_binding(), // bind to opposite buffer\n                    },\n                ],\n                label: None,\n            }));\n        }\n\n        // calculates number of work groups from PARTICLES_PER_GROUP constant\n        let work_group_count = NUM_PARTICLES.div_ceil(PARTICLES_PER_GROUP);\n\n        // returns Example struct and No encoder commands\n\n        Example {\n            particle_bind_groups,\n            particle_buffers,\n            vertices_buffer,\n            compute_pipeline,\n            render_pipeline,\n            work_group_count,\n            frame_num: 0,\n        }\n    }\n\n    /// update is called for any WindowEvent not handled by the framework\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    /// resize is called on WindowEvent::Resized events\n    fn resize(\n        &mut self,\n        _sc_desc: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        //empty\n    }\n\n    /// render is called each frame, dispatching compute groups proportional\n    ///   a TriangleList draw call for all NUM_PARTICLES at 3 vertices each\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        // create render pass descriptor and its color attachments\n        let color_attachments = [Some(wgpu::RenderPassColorAttachment {\n            view,\n            depth_slice: None,\n            resolve_target: None,\n            ops: wgpu::Operations {\n                load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                store: wgpu::StoreOp::Store,\n            },\n        })];\n        let render_pass_descriptor = wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &color_attachments,\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        };\n\n        // get command encoder\n        let mut command_encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        command_encoder.push_debug_group(\"compute boid movement\");\n        {\n            // compute pass\n            let mut cpass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&self.compute_pipeline);\n            cpass.set_bind_group(0, &self.particle_bind_groups[self.frame_num % 2], &[]);\n            cpass.dispatch_workgroups(self.work_group_count, 1, 1);\n        }\n        command_encoder.pop_debug_group();\n\n        command_encoder.push_debug_group(\"render boids\");\n        {\n            // render pass\n            let mut rpass = command_encoder.begin_render_pass(&render_pass_descriptor);\n            rpass.set_pipeline(&self.render_pipeline);\n            // render dst particles\n            rpass.set_vertex_buffer(0, self.particle_buffers[(self.frame_num + 1) % 2].slice(..));\n            // the three instance-local vertices\n            rpass.set_vertex_buffer(1, self.vertices_buffer.slice(..));\n            rpass.draw(0..3, 0..NUM_PARTICLES);\n        }\n        command_encoder.pop_debug_group();\n\n        // update frame count\n        self.frame_num += 1;\n\n        // done\n        queue.submit(Some(command_encoder.finish()));\n    }\n}\n\n/// run example\npub fn main() {\n    crate::framework::run::<Example>(\"boids\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"boids\",\n    // Generated on 1080ti on Vk/Windows\n    image_path: \"/examples/features/src/boids/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n        .limits(wgpu::Limits::downlevel_defaults()),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.005)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/bunnymark/README.md",
    "content": "# bunnymark\n\n\n## To Run\n\n```\ncargo run --bin wgpu-examples bunnymark\n```\n\n## Example output\n\n![Example output](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/bunnymark/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse nanorand::{Rng, WyRand};\nuse std::borrow::Cow;\nuse wgpu::util::DeviceExt;\nuse winit::{\n    event::{ElementState, KeyEvent},\n    keyboard::{Key, NamedKey},\n};\n\nconst MAX_BUNNIES: usize = 1 << 20;\nconst BUNNY_SIZE: f32 = 0.15 * 256.0;\nconst GRAVITY: f32 = -9.8 * 100.0;\nconst MAX_VELOCITY: f32 = 750.0;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Globals {\n    mvp: [[f32; 4]; 4],\n    size: [f32; 2],\n    pad: [f32; 2],\n}\n\n#[repr(C, align(256))]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Bunny {\n    position: [f32; 2],\n    velocity: [f32; 2],\n    color: u32,\n    _pad: [u32; (256 - 20) / 4],\n}\n\nimpl Bunny {\n    fn update_data(&mut self, delta: f32, extent: &[u32; 2]) {\n        self.position[0] += self.velocity[0] * delta;\n        self.position[1] += self.velocity[1] * delta;\n        self.velocity[1] += GRAVITY * delta;\n\n        if (self.velocity[0] > 0.0 && self.position[0] + 0.5 * BUNNY_SIZE > extent[0] as f32)\n            || (self.velocity[0] < 0.0 && self.position[0] - 0.5 * BUNNY_SIZE < 0.0)\n        {\n            self.velocity[0] *= -1.0;\n        }\n\n        if self.velocity[1] < 0.0 && self.position[1] < 0.5 * BUNNY_SIZE {\n            self.velocity[1] *= -1.0;\n        }\n\n        // Top boundary check\n        if self.velocity[1] > 0.0 && self.position[1] + 0.5 * BUNNY_SIZE > extent[1] as f32 {\n            self.velocity[1] *= -1.0;\n        }\n    }\n}\n\n/// Example struct holds references to wgpu resources and frame persistent data\nstruct Example {\n    view: wgpu::TextureView,\n    sampler: wgpu::Sampler,\n    global_bind_group_layout: wgpu::BindGroupLayout,\n    global_group: wgpu::BindGroup,\n    local_group: wgpu::BindGroup,\n    pipeline: wgpu::RenderPipeline,\n    bunnies: Vec<Bunny>,\n    local_buffer: wgpu::Buffer,\n    extent: [u32; 2],\n    rng: WyRand,\n}\n\nimpl Example {\n    fn spawn_bunnies(&mut self) {\n        let spawn_count = 64;\n        let color = self.rng.generate::<u32>();\n        println!(\n            \"Spawning {} bunnies, total at {}\",\n            spawn_count,\n            self.bunnies.len() + spawn_count\n        );\n        for _ in 0..spawn_count {\n            let speed = self.rng.generate::<f32>() * MAX_VELOCITY - (MAX_VELOCITY * 0.5);\n            self.bunnies.push(Bunny {\n                position: [0.0, 0.5 * (self.extent[1] as f32)],\n                velocity: [speed, 0.0],\n                color,\n                _pad: Zeroable::zeroed(),\n            });\n        }\n    }\n\n    fn render_inner(\n        &mut self,\n        view: &wgpu::TextureView,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        let delta = 0.01;\n        for bunny in self.bunnies.iter_mut() {\n            bunny.update_data(delta, &self.extent);\n        }\n\n        let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;\n        queue.write_buffer(&self.local_buffer, 0, bytemuck::cast_slice(&self.bunnies));\n\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        {\n            let clear_color = wgpu::Color {\n                r: 0.1,\n                g: 0.2,\n                b: 0.3,\n                a: 1.0,\n            };\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(clear_color),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, &self.global_group, &[]);\n            for i in 0..self.bunnies.len() {\n                let offset =\n                    (i as wgpu::DynamicOffset) * (uniform_alignment as wgpu::DynamicOffset);\n                rpass.set_bind_group(1, &self.local_group, &[offset]);\n                rpass.draw(0..4, 0..1);\n            }\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\n                \"../../../../wgpu-hal/examples/halmark/shader.wgsl\"\n            ))),\n        });\n\n        let global_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::VERTEX,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new(size_of::<Globals>() as _),\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                            multisampled: false,\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 2,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    },\n                ],\n                label: None,\n            });\n        let local_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: true,\n                        min_binding_size: wgpu::BufferSize::new(size_of::<Bunny>() as _),\n                    },\n                    count: None,\n                }],\n                label: None,\n            });\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[\n                Some(&global_bind_group_layout),\n                Some(&local_bind_group_layout),\n            ],\n            immediate_size: 0,\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),\n                    write_mask: wgpu::ColorWrites::default(),\n                })],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint16),\n                ..wgpu::PrimitiveState::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let texture = {\n            let img_data = include_bytes!(\"../../../../logo.png\");\n            let decoder = png::Decoder::new(std::io::Cursor::new(img_data));\n            let mut reader = decoder.read_info().unwrap();\n            let buf_len = reader\n                .output_buffer_size()\n                .expect(\"output buffer would not fit in memory\");\n            let mut buf = vec![0; buf_len];\n            let info = reader.next_frame(&mut buf).unwrap();\n\n            let size = wgpu::Extent3d {\n                width: info.width,\n                height: info.height,\n                depth_or_array_layers: 1,\n            };\n            let texture = device.create_texture(&wgpu::TextureDescriptor {\n                label: None,\n                size,\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,\n                view_formats: &[],\n            });\n            queue.write_texture(\n                texture.as_image_copy(),\n                &buf,\n                wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(info.width * 4),\n                    rows_per_image: None,\n                },\n                size,\n            );\n            texture\n        };\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: None,\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let globals = Globals {\n            mvp: glam::Mat4::orthographic_rh(\n                0.0,\n                config.width as f32,\n                0.0,\n                config.height as f32,\n                -1.0,\n                1.0,\n            )\n            .to_cols_array_2d(),\n            size: [BUNNY_SIZE; 2],\n            pad: [0.0; 2],\n        };\n\n        let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"global\"),\n            contents: bytemuck::bytes_of(&globals),\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,\n        });\n        let uniform_alignment =\n            device.limits().min_uniform_buffer_offset_alignment as wgpu::BufferAddress;\n        let local_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"local\"),\n            size: (MAX_BUNNIES as wgpu::BufferAddress) * uniform_alignment,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,\n            mapped_at_creation: false,\n        });\n\n        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n        let global_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &global_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: global_buffer.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n            label: None,\n        });\n        let local_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &local_bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &local_buffer,\n                    offset: 0,\n                    size: wgpu::BufferSize::new(size_of::<Bunny>() as _),\n                }),\n            }],\n            label: None,\n        });\n\n        let rng = WyRand::new_seed(42);\n\n        let mut ex = Example {\n            view,\n            sampler,\n            global_bind_group_layout,\n            pipeline,\n            global_group,\n            local_group,\n            bunnies: Vec::new(),\n            local_buffer,\n            extent: [config.width, config.height],\n            rng,\n        };\n\n        ex.spawn_bunnies();\n\n        ex\n    }\n\n    fn update(&mut self, event: winit::event::WindowEvent) {\n        if let winit::event::WindowEvent::KeyboardInput {\n            event:\n                KeyEvent {\n                    logical_key: Key::Named(NamedKey::Space),\n                    state: ElementState::Pressed,\n                    ..\n                },\n            ..\n        } = event\n        {\n            self.spawn_bunnies();\n        }\n    }\n\n    fn resize(\n        &mut self,\n        sc_desc: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        self.extent = [sc_desc.width, sc_desc.height];\n\n        let globals = Globals {\n            mvp: glam::Mat4::orthographic_rh(\n                0.0,\n                sc_desc.width as f32,\n                0.0,\n                sc_desc.height as f32,\n                -1.0,\n                1.0,\n            )\n            .to_cols_array_2d(),\n            size: [BUNNY_SIZE; 2],\n            pad: [0.0; 2],\n        };\n\n        let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"global\"),\n            contents: bytemuck::bytes_of(&globals),\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,\n        });\n\n        let global_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &self.global_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: global_buffer.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&self.view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&self.sampler),\n                },\n            ],\n            label: None,\n        });\n        self.global_group = global_group;\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        self.render_inner(view, device, queue);\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"bunnymark\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"bunnymark\",\n    image_path: \"/examples/features/src/bunnymark/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    // We're looking for very small differences, so look in the high percentiles.\n    comparisons: &[\n        wgpu_test::ComparisonType::Mean(0.05),\n        wgpu_test::ComparisonType::Percentile {\n            percentile: 0.99,\n            threshold: 0.37,\n        },\n    ],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/conservative_raster/README.md",
    "content": "# conservative_raster\n\nThis example shows how to render with conservative rasterization (native extension with limited support).\n\nWhen enabled, any pixel touched by a triangle primitive is rasterized.\nThis is useful for various advanced techniques, most prominently for implementing realtime voxelization.\n\nThe demonstration here is implemented by rendering a triangle to a low-resolution target and then upscaling it with nearest-neighbor filtering.\nThe outlines of the triangle are then rendered in the original solution, using the same vertex shader as the triangle.\nPixels only drawn with conservative rasterization enabled are depicted red.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples conservative_raster\n```\n\n## Screenshots\n\n![Conservative-raster window](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/conservative_raster/mod.rs",
    "content": "const RENDER_TARGET_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;\n\nstruct Example {\n    low_res_target: wgpu::TextureView,\n    bind_group_upscale: wgpu::BindGroup,\n\n    pipeline_triangle_conservative: wgpu::RenderPipeline,\n    pipeline_triangle_regular: wgpu::RenderPipeline,\n    pipeline_upscale: wgpu::RenderPipeline,\n    pipeline_lines: Option<wgpu::RenderPipeline>,\n    bind_group_layout_upscale: wgpu::BindGroupLayout,\n}\n\nimpl Example {\n    fn create_low_res_target(\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        bind_group_layout_upscale: &wgpu::BindGroupLayout,\n    ) -> (wgpu::TextureView, wgpu::BindGroup) {\n        let texture_view = device\n            .create_texture(&wgpu::TextureDescriptor {\n                label: Some(\"Low Resolution Target\"),\n                size: wgpu::Extent3d {\n                    width: (config.width / 16).max(1),\n                    height: (config.height / 16).max(1),\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: RENDER_TARGET_FORMAT,\n                usage: wgpu::TextureUsages::TEXTURE_BINDING\n                    | wgpu::TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            })\n            .create_view(&Default::default());\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"Nearest Neighbor Sampler\"),\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(\"upscale bind group\"),\n            layout: bind_group_layout_upscale,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        (texture_view, bind_group)\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::CONSERVATIVE_RASTERIZATION\n    }\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::POLYGON_MODE_LINE\n    }\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let pipeline_layout_empty =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[],\n                immediate_size: 0,\n            });\n\n        let shader_triangle_and_lines =\n            device.create_shader_module(wgpu::include_wgsl!(\"triangle_and_lines.wgsl\"));\n\n        let pipeline_triangle_conservative =\n            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"Conservative Rasterization\"),\n                layout: Some(&pipeline_layout_empty),\n                vertex: wgpu::VertexState {\n                    module: &shader_triangle_and_lines,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module: &shader_triangle_and_lines,\n                    entry_point: Some(\"fs_main_red\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(RENDER_TARGET_FORMAT.into())],\n                }),\n                primitive: wgpu::PrimitiveState {\n                    conservative: true,\n                    ..Default::default()\n                },\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        let pipeline_triangle_regular =\n            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"Regular Rasterization\"),\n                layout: Some(&pipeline_layout_empty),\n                vertex: wgpu::VertexState {\n                    module: &shader_triangle_and_lines,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module: &shader_triangle_and_lines,\n                    entry_point: Some(\"fs_main_blue\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(RENDER_TARGET_FORMAT.into())],\n                }),\n                primitive: wgpu::PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        let pipeline_lines = if device\n            .features()\n            .contains(wgpu::Features::POLYGON_MODE_LINE)\n        {\n            Some(\n                device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                    label: Some(\"Lines\"),\n                    layout: Some(&pipeline_layout_empty),\n                    vertex: wgpu::VertexState {\n                        module: &shader_triangle_and_lines,\n                        entry_point: Some(\"vs_main\"),\n                        compilation_options: Default::default(),\n                        buffers: &[],\n                    },\n                    fragment: Some(wgpu::FragmentState {\n                        module: &shader_triangle_and_lines,\n                        entry_point: Some(\"fs_main_white\"),\n                        compilation_options: Default::default(),\n                        targets: &[Some(config.view_formats[0].into())],\n                    }),\n                    primitive: wgpu::PrimitiveState {\n                        polygon_mode: wgpu::PolygonMode::Line,\n                        topology: wgpu::PrimitiveTopology::LineStrip,\n                        ..Default::default()\n                    },\n                    depth_stencil: None,\n                    multisample: wgpu::MultisampleState::default(),\n                    multiview_mask: None,\n                    cache: None,\n                }),\n            )\n        } else {\n            None\n        };\n\n        let (pipeline_upscale, bind_group_layout_upscale) = {\n            let bind_group_layout =\n                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: Some(\"upscale bindgroup\"),\n                    entries: &[\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 0,\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Texture {\n                                sample_type: wgpu::TextureSampleType::Float { filterable: false },\n                                view_dimension: wgpu::TextureViewDimension::D2,\n                                multisampled: false,\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 1,\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),\n                            count: None,\n                        },\n                    ],\n                });\n\n            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bind_group_layout)],\n                immediate_size: 0,\n            });\n            let shader = device.create_shader_module(wgpu::include_wgsl!(\"upscale.wgsl\"));\n            (\n                device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                    label: Some(\"Upscale\"),\n                    layout: Some(&pipeline_layout),\n                    vertex: wgpu::VertexState {\n                        module: &shader,\n                        entry_point: Some(\"vs_main\"),\n                        compilation_options: Default::default(),\n                        buffers: &[],\n                    },\n                    fragment: Some(wgpu::FragmentState {\n                        module: &shader,\n                        entry_point: Some(\"fs_main\"),\n                        compilation_options: Default::default(),\n                        targets: &[Some(config.view_formats[0].into())],\n                    }),\n                    primitive: wgpu::PrimitiveState::default(),\n                    depth_stencil: None,\n                    multisample: wgpu::MultisampleState::default(),\n                    multiview_mask: None,\n                    cache: None,\n                }),\n                bind_group_layout,\n            )\n        };\n\n        let (low_res_target, bind_group_upscale) =\n            Self::create_low_res_target(config, device, &bind_group_layout_upscale);\n\n        Self {\n            low_res_target,\n            bind_group_upscale,\n\n            pipeline_triangle_conservative,\n            pipeline_triangle_regular,\n            pipeline_upscale,\n            pipeline_lines,\n            bind_group_layout_upscale,\n        }\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        let (low_res_target, bind_group_upscale) =\n            Self::create_low_res_target(config, device, &self.bind_group_layout_upscale);\n        self.low_res_target = low_res_target;\n        self.bind_group_upscale = bind_group_upscale;\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"primary\"),\n        });\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"low resolution\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &self.low_res_target,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.pipeline_triangle_conservative);\n            rpass.draw(0..3, 0..1);\n            rpass.set_pipeline(&self.pipeline_triangle_regular);\n            rpass.draw(0..3, 0..1);\n        }\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"full resolution\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.pipeline_upscale);\n            rpass.set_bind_group(0, &self.bind_group_upscale, &[]);\n            rpass.draw(0..3, 0..1);\n\n            if let Some(pipeline_lines) = &self.pipeline_lines {\n                rpass.set_pipeline(pipeline_lines);\n                rpass.draw(0..4, 0..1);\n            }\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"conservative-raster\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"conservative-raster\",\n    image_path: \"/examples/features/src/conservative_raster/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/conservative_raster/triangle_and_lines.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {\n    let i: i32 = i32(vertex_index % 3u);\n    let x: f32 = f32(i - 1) * 0.75;\n    let y: f32 = f32((i & 1) * 2 - 1) * 0.75 + x * 0.2 + 0.1;\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main_red() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main_blue() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.13, 0.31, 0.85, 1.0); // cornflower blue in linear space\n}\n\n@fragment\nfn fs_main_white() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/conservative_raster/upscale.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    let x: f32 = f32(i32(vertex_index & 1u) << 2u) - 1.0;\n    let y: f32 = f32(i32(vertex_index & 2u) << 1u) - 1.0;\n    var result: VertexOutput;\n    result.position = vec4<f32>(x, -y, 0.0, 1.0);\n    result.tex_coords = vec2<f32>(x + 1.0, y + 1.0) * 0.5;\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/cooperative_matrix/README.md",
    "content": "# Cooperative Matrix Multiplication\n\nThis example demonstrates how to use cooperative matrix operations (also known as tensor cores on NVIDIA GPUs) to perform efficient matrix multiplication on the GPU.\n\nFor the full description of the cooperative matrix feature (supported configurations, WGSL types and operations, validation rules, and backend support), see the central API spec:\n\n- `docs/api-specs/cooperative_matrix.md`\n\n## Example specifics\n\nThis example computes `C = A * B + C` where:\n- A is a 64×64 matrix\n- B is a 64×64 matrix\n- C is a 64×64 matrix (accumulator/result)\n\nThe example:\n- Tiles the 64×64 matrices into cooperative matrix tiles (e.g. 8×8) and performs a tiled matmul\n- Uses a compute shader and compares GPU results against a CPU reference implementation\n\n## Requirements\n\n- A GPU and backend that expose `Features::EXPERIMENTAL_COOPERATIVE_MATRIX`\n- A configuration returned from `adapter.cooperative_matrix_properties()` that matches the tile size and element types used by this example\n- See `docs/api-specs/cooperative_matrix.md` for details on hardware / backend support\n\n## Running\n\n```bash\ncargo run --bin wgpu-examples -- cooperative_matrix\n```\n\n## Notes\n\n- This is an experimental feature and may not work on all hardware\n- The shader uses the standard `create_shader_module` with full validation\n- Results are verified against a CPU reference implementation\n"
  },
  {
    "path": "examples/features/src/cooperative_matrix/mod.rs",
    "content": "//! Cooperative Matrix Multiplication Example\n//!\n//! This example demonstrates how to use cooperative matrix operations\n//! (also known as tensor cores on NVIDIA GPUs or simdgroup matrix\n//! operations on Apple GPUs) to perform efficient matrix multiplication.\n//!\n//! Cooperative matrices allow a workgroup to collectively load, store,\n//! and perform matrix operations on small tiles of data, enabling\n//! hardware-accelerated matrix math.\n//!\n//! Note: This feature requires hardware support and is currently\n//! experimental. Use `adapter.cooperative_matrix_properties()` to query\n//! supported configurations:\n//! - Metal (Apple): 8x8 f32, 8x8 f16, mixed precision (f16 inputs, f32 accumulator)\n//! - Vulkan (AMD): Typically 16x16 f16\n//! - Vulkan (NVIDIA): Varies by GPU generation\n\nuse bytemuck::{Pod, Zeroable};\nuse half::f16;\n\n/// Matrix dimensions for our example (must be divisible by tile size)\nconst M: u32 = 64; // Rows of A and C\nconst N: u32 = 64; // Cols of B and C\nconst K: u32 = 64; // Cols of A, Rows of B\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Dimensions {\n    m: u32,\n    n: u32,\n    k: u32,\n    stride: u32,\n}\n\nasync fn run() {\n    // Initialize wgpu\n    let instance = wgpu::Instance::default();\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions {\n            power_preference: wgpu::PowerPreference::HighPerformance,\n            ..Default::default()\n        })\n        .await\n        .expect(\"Failed to find an appropriate adapter\");\n\n    log::info!(\"Using adapter: {:?}\", adapter.get_info());\n\n    // Query supported cooperative matrix configurations\n    let coop_props = adapter.cooperative_matrix_properties();\n    if coop_props.is_empty() {\n        log::error!(\n            \"Cooperative matrix is not supported on this adapter.\\n\\\n            This feature requires:\\n\\\n            - Metal: Apple7+ (A14/M1) with MSL 2.3+\\n\\\n            - Vulkan: VK_KHR_cooperative_matrix extension\"\n        );\n        return;\n    }\n\n    // Display supported configurations\n    log::info!(\"Supported cooperative matrix configurations:\");\n    for (i, prop) in coop_props.iter().enumerate() {\n        log::info!(\n            \"  [{}] {:?}x{:?}x{:?} - AB: {:?}, CR: {:?}{}\",\n            i,\n            prop.m_size,\n            prop.n_size,\n            prop.k_size,\n            prop.ab_type,\n            prop.cr_type,\n            if prop.saturating_accumulation {\n                \" (saturating)\"\n            } else {\n                \"\"\n            }\n        );\n    }\n\n    // Find a suitable configuration - prefer f32, but accept f16\n    // Try 16x16 first (AMD), then 8x8 (Apple Metal)\n    let selected_config = coop_props\n        .iter()\n        .find(|prop| {\n            prop.m_size == 16\n                && prop.n_size == 16\n                && prop.k_size == 16\n                && prop.ab_type == wgpu::CooperativeScalarType::F16\n                && prop.cr_type == wgpu::CooperativeScalarType::F16\n        })\n        .or_else(|| {\n            coop_props.iter().find(|prop| {\n                prop.m_size == 8\n                    && prop.n_size == 8\n                    && prop.k_size == 8\n                    && prop.ab_type == wgpu::CooperativeScalarType::F32\n                    && prop.cr_type == wgpu::CooperativeScalarType::F32\n            })\n        });\n\n    let config = match selected_config {\n        Some(c) => {\n            log::info!(\n                \"Selected configuration: {:?}x{:?}x{:?} AB={:?} CR={:?}\",\n                c.m_size,\n                c.n_size,\n                c.k_size,\n                c.ab_type,\n                c.cr_type\n            );\n            c\n        }\n        None => {\n            log::error!(\n                \"No suitable cooperative matrix configuration found.\\n\\\n                This example supports 16x16 f16 (AMD) or 8x8 f32 (Apple Metal).\\n\\\n                Available configurations are listed above.\"\n            );\n            return;\n        }\n    };\n\n    let tile_size = config.m_size;\n    let use_f16 = config.ab_type == wgpu::CooperativeScalarType::F16;\n\n    log::info!(\n        \"Using {}x{} tiles with {} precision\",\n        tile_size,\n        tile_size,\n        if use_f16 { \"f16\" } else { \"f32\" }\n    );\n\n    // Check if cooperative matrix is supported\n    let adapter_features = adapter.features();\n    if !adapter_features.contains(wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {\n        log::error!(\"EXPERIMENTAL_COOPERATIVE_MATRIX feature not available\");\n        return;\n    }\n\n    // Check if f16 is needed and available\n    if use_f16 && !adapter_features.contains(wgpu::Features::SHADER_F16) {\n        log::error!(\"SHADER_F16 feature not available, but required for f16 cooperative matrices\");\n        return;\n    }\n\n    // Build required features\n    let mut required_features = wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX;\n    if use_f16 {\n        required_features |= wgpu::Features::SHADER_F16;\n    }\n\n    // Request device with experimental features enabled\n    let (device, queue) = unsafe {\n        adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                label: Some(\"Cooperative Matrix Device\"),\n                required_features,\n                required_limits: wgpu::Limits::downlevel_defaults(),\n                experimental_features: wgpu::ExperimentalFeatures::enabled(),\n                memory_hints: wgpu::MemoryHints::Performance,\n                trace: wgpu::Trace::Off,\n            })\n            .await\n            .expect(\"Failed to create device\")\n    };\n\n    let results = execute(&device, &queue, config).await;\n\n    log::info!(\n        \"Matrix multiplication {M}x{K}x{N} completed using {} precision!\",\n        if use_f16 { \"f16\" } else { \"f32\" }\n    );\n    log::info!(\"Max error vs CPU reference: {:.6}\", results.max_error);\n\n    if results.max_error < results.tolerance {\n        log::info!(\n            \"✓ Results match CPU reference within tolerance ({})\",\n            results.tolerance\n        );\n    } else {\n        log::warn!(\n            \"✗ Results differ from CPU reference (tolerance: {})\",\n            results.tolerance\n        );\n    }\n\n    // Print a small sample of the result\n    log::info!(\"Sample of result matrix C (top-left 4x4):\");\n    for i in 0..4 {\n        let row: Vec<String> = (0..4)\n            .map(|j| format!(\"{:6.2}\", results.matrix[i * N as usize + j]))\n            .collect();\n        log::info!(\"  [{}]\", row.join(\", \"));\n    }\n}\n\nstruct ExecuteResults {\n    max_error: f32,\n    tolerance: f32,\n    matrix: Vec<f32>,\n}\n\nasync fn execute(\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n    config: &wgpu::CooperativeMatrixProperties,\n) -> ExecuteResults {\n    let use_f16 = config.ab_type == wgpu::CooperativeScalarType::F16;\n\n    // Select the appropriate shader based on configuration\n    let shader_source = if use_f16 {\n        include_str!(\"shader_f16_16x16.wgsl\")\n    } else {\n        include_str!(\"shader.wgsl\")\n    };\n\n    // Create the shader module using the standard validated path\n    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n        label: Some(\"Cooperative Matrix Shader\"),\n        source: wgpu::ShaderSource::Wgsl(shader_source.into()),\n    });\n\n    // Initialize matrices\n    // A is MxK, B is KxN, C is MxN (result)\n    // Use f32 for computation, convert to f16 if needed for GPU\n    let matrix_a_f32: Vec<f32> = (0..M * K).map(|i| (i % 7) as f32 * 0.1).collect();\n    let matrix_b_f32: Vec<f32> = (0..K * N).map(|i| (i % 11) as f32 * 0.1).collect();\n    let matrix_c_f32: Vec<f32> = vec![0.0; (M * N) as usize];\n\n    // Element size depends on precision\n    let element_size = if use_f16 { 2usize } else { 4usize };\n    let num_elements_a = (M * K) as usize;\n    let num_elements_b = (K * N) as usize;\n    let num_elements_c = (M * N) as usize;\n\n    // Create buffers\n    let buffer_a = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Matrix A\"),\n        size: (num_elements_a * element_size) as u64,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let buffer_b = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Matrix B\"),\n        size: (num_elements_b * element_size) as u64,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let buffer_c = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Matrix C\"),\n        size: (num_elements_c * element_size) as u64,\n        usage: wgpu::BufferUsages::STORAGE\n            | wgpu::BufferUsages::COPY_DST\n            | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let dimensions = Dimensions {\n        m: M,\n        n: N,\n        k: K,\n        stride: N,\n    };\n    let buffer_dims = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Dimensions\"),\n        size: std::mem::size_of::<Dimensions>() as u64,\n        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Staging Buffer\"),\n        size: (num_elements_c * element_size) as u64,\n        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    // Upload data (convert to f16 if needed)\n    if use_f16 {\n        let matrix_a_f16: Vec<f16> = matrix_a_f32.iter().map(|&x| f16::from_f32(x)).collect();\n        let matrix_b_f16: Vec<f16> = matrix_b_f32.iter().map(|&x| f16::from_f32(x)).collect();\n        let matrix_c_f16: Vec<f16> = matrix_c_f32.iter().map(|&x| f16::from_f32(x)).collect();\n        queue.write_buffer(&buffer_a, 0, bytemuck::cast_slice(&matrix_a_f16));\n        queue.write_buffer(&buffer_b, 0, bytemuck::cast_slice(&matrix_b_f16));\n        queue.write_buffer(&buffer_c, 0, bytemuck::cast_slice(&matrix_c_f16));\n    } else {\n        queue.write_buffer(&buffer_a, 0, bytemuck::cast_slice(&matrix_a_f32));\n        queue.write_buffer(&buffer_b, 0, bytemuck::cast_slice(&matrix_b_f32));\n        queue.write_buffer(&buffer_c, 0, bytemuck::cast_slice(&matrix_c_f32));\n    }\n    queue.write_buffer(&buffer_dims, 0, bytemuck::bytes_of(&dimensions));\n\n    // Create bind group layout and bind group\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: Some(\"Cooperative Matrix Bind Group Layout\"),\n        entries: &[\n            wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: true },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n            wgpu::BindGroupLayoutEntry {\n                binding: 1,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: true },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n            wgpu::BindGroupLayoutEntry {\n                binding: 2,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n            wgpu::BindGroupLayoutEntry {\n                binding: 3,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n        ],\n    });\n\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"Cooperative Matrix Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: buffer_a.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: buffer_b.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 2,\n                resource: buffer_c.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 3,\n                resource: buffer_dims.as_entire_binding(),\n            },\n        ],\n    });\n\n    // Create compute pipeline\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: Some(\"Cooperative Matrix Pipeline Layout\"),\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n\n    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: Some(\"Cooperative Matrix Pipeline\"),\n        layout: Some(&pipeline_layout),\n        module: &shader,\n        entry_point: Some(\"main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n\n    // Dispatch compute\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n        label: Some(\"Cooperative Matrix Encoder\"),\n    });\n\n    {\n        let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"Cooperative Matrix Pass\"),\n            timestamp_writes: None,\n        });\n        compute_pass.set_pipeline(&pipeline);\n        compute_pass.set_bind_group(0, &bind_group, &[]);\n        // Dispatch one workgroup per tile of the output\n        compute_pass.dispatch_workgroups(M / config.m_size, N / config.m_size, 1);\n    }\n\n    // Copy result to staging buffer\n    encoder.copy_buffer_to_buffer(&buffer_c, 0, &staging_buffer, 0, staging_buffer.size());\n\n    queue.submit(Some(encoder.finish()));\n\n    // Read back results\n    let buffer_slice = staging_buffer.slice(..);\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .expect(\"Poll failed\");\n    receiver\n        .recv_async()\n        .await\n        .expect(\"Channel receive failed\")\n        .expect(\"Buffer mapping failed\");\n\n    let data = buffer_slice.get_mapped_range();\n\n    // Convert result back to f32 for comparison\n    let result: Vec<f32> = if use_f16 {\n        let result_f16: &[f16] = bytemuck::cast_slice(&data);\n        result_f16.iter().map(|x| x.to_f32()).collect()\n    } else {\n        bytemuck::cast_slice::<_, f32>(&data).to_vec()\n    };\n\n    // Compute reference result on CPU for verification\n    let mut reference = vec![0.0f32; (M * N) as usize];\n    for i in 0..M {\n        for j in 0..N {\n            let mut sum = 0.0f32;\n            for k in 0..K {\n                sum += matrix_a_f32[(i * K + k) as usize] * matrix_b_f32[(k * N + j) as usize];\n            }\n            reference[(i * N + j) as usize] = sum;\n        }\n    }\n\n    // Verify results (use larger tolerance for f16)\n    let tolerance = if use_f16 { 0.1 } else { 0.01 };\n    let mut max_error = 0.0f32;\n    for i in 0..(M * N) as usize {\n        let error = (result[i] - reference[i]).abs();\n        max_error = max_error.max(error);\n    }\n\n    ExecuteResults {\n        max_error,\n        tolerance,\n        matrix: result,\n    }\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n        pollster::block_on(run());\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n        crate::utils::add_web_nothing_to_see_msg();\n        wasm_bindgen_futures::spawn_local(run());\n    }\n}\n\n#[cfg(test)]\npub mod tests;\n"
  },
  {
    "path": "examples/features/src/cooperative_matrix/shader.wgsl",
    "content": "// Cooperative Matrix Multiplication Example\n//\n// This shader demonstrates using cooperative matrix operations to perform\n// matrix multiplication: C = A * B + C\n//\n// The matrices are stored in row-major order in storage buffers.\n// Each workgroup cooperatively loads tiles of A and B, multiplies them,\n// and accumulates the result into C.\n\nenable wgpu_cooperative_matrix;\n\n// Matrix dimensions (8x8 tiles)\nconst TILE_SIZE: u32 = 8u;\n\n@group(0) @binding(0)\nvar<storage, read> matrix_a: array<f32>;\n\n@group(0) @binding(1)\nvar<storage, read> matrix_b: array<f32>;\n\n@group(0) @binding(2)\nvar<storage, read_write> matrix_c: array<f32>;\n\n// Dimensions passed as uniforms: M, N, K for C[M,N] = A[M,K] * B[K,N]\n@group(0) @binding(3)\nvar<uniform> dimensions: vec4<u32>; // x=M, y=N, z=K, w=stride\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(workgroup_id) workgroup_id: vec3<u32>) {\n    let M = dimensions.x;\n    let N = dimensions.y;\n    let K = dimensions.z;\n    let stride = dimensions.w;\n\n    // Each workgroup handles one 8x8 tile of the output matrix C\n    let tile_row = workgroup_id.x * TILE_SIZE;\n    let tile_col = workgroup_id.y * TILE_SIZE;\n\n    // Load the C tile (accumulator)\n    let c_offset = tile_row * stride + tile_col;\n    var c_tile = coopLoad<coop_mat8x8<f32, C>>(&matrix_c[c_offset], stride);\n\n    // Iterate over K dimension in tiles\n    for (var k: u32 = 0u; k < K; k += TILE_SIZE) {\n        // Load A tile: rows [tile_row, tile_row+8), cols [k, k+8)\n        let a_offset = tile_row * K + k;\n        let a_tile = coopLoad<coop_mat8x8<f32, A>>(&matrix_a[a_offset], K);\n\n        // Load B tile: rows [k, k+8), cols [tile_col, tile_col+8)\n        let b_offset = k * stride + tile_col;\n        let b_tile = coopLoad<coop_mat8x8<f32, B>>(&matrix_b[b_offset], stride);\n\n        // Multiply and accumulate: C += A * B\n        c_tile = coopMultiplyAdd(a_tile, b_tile, c_tile);\n    }\n\n    // Store the result back to C\n    coopStore(c_tile, &matrix_c[c_offset], stride);\n}\n"
  },
  {
    "path": "examples/features/src/cooperative_matrix/shader_f16_16x16.wgsl",
    "content": "// Cooperative Matrix Multiplication Example (16x16 f16 variant)\n//\n// This shader demonstrates using cooperative matrix operations to perform\n// matrix multiplication: C = A * B + C\n//\n// This variant uses 16x16 tiles with f16 precision, which is supported\n// on AMD GPUs via VK_KHR_cooperative_matrix.\n//\n// The matrices are stored in row-major order in storage buffers.\n// Each workgroup cooperatively loads tiles of A and B, multiplies them,\n// and accumulates the result into C.\n\nenable f16;\nenable wgpu_cooperative_matrix;\n\n// Matrix dimensions (16x16 tiles)\nconst TILE_SIZE: u32 = 16u;\n\n@group(0) @binding(0)\nvar<storage, read> matrix_a: array<f16>;\n\n@group(0) @binding(1)\nvar<storage, read> matrix_b: array<f16>;\n\n@group(0) @binding(2)\nvar<storage, read_write> matrix_c: array<f16>;\n\n// Dimensions passed as uniforms: M, N, K for C[M,N] = A[M,K] * B[K,N]\n@group(0) @binding(3)\nvar<uniform> dimensions: vec4<u32>; // x=M, y=N, z=K, w=stride\n\n// Workgroup size must be a multiple of subgroup size (64 on AMD)\n// Using 64x1x1 ensures compatibility while still processing 16x16 tiles\n@compute @workgroup_size(64, 1, 1)\nfn main(@builtin(workgroup_id) workgroup_id: vec3<u32>) {\n    let M = dimensions.x;\n    let N = dimensions.y;\n    let K = dimensions.z;\n    let stride = dimensions.w;\n\n    // Each workgroup handles one 16x16 tile of the output matrix C\n    let tile_row = workgroup_id.x * TILE_SIZE;\n    let tile_col = workgroup_id.y * TILE_SIZE;\n\n    // Load the C tile (accumulator)\n    let c_offset = tile_row * stride + tile_col;\n    var c_tile = coopLoad<coop_mat16x16<f16, C>>(&matrix_c[c_offset], stride);\n\n    // Iterate over K dimension in tiles\n    for (var k: u32 = 0u; k < K; k += TILE_SIZE) {\n        // Load A tile: rows [tile_row, tile_row+16), cols [k, k+16)\n        let a_offset = tile_row * K + k;\n        let a_tile = coopLoad<coop_mat16x16<f16, A>>(&matrix_a[a_offset], K);\n\n        // Load B tile: rows [k, k+16), cols [tile_col, tile_col+16)\n        let b_offset = k * stride + tile_col;\n        let b_tile = coopLoad<coop_mat16x16<f16, B>>(&matrix_b[b_offset], stride);\n\n        // Multiply and accumulate: C += A * B\n        c_tile = coopMultiplyAdd(a_tile, b_tile, c_tile);\n    }\n\n    // Store the result back to C\n    coopStore(c_tile, &matrix_c[c_offset], stride);\n}\n"
  },
  {
    "path": "examples/features/src/cooperative_matrix/tests.rs",
    "content": "use super::*;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};\n\n#[gpu_test]\npub static COOPERATIVE_MATRIX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX)\n            .limits(wgpu::Limits::default()),\n    )\n    .run_async(|ctx| async move {\n        let coop_props = ctx.adapter.cooperative_matrix_properties();\n        let config = coop_props\n            .iter()\n            .find(|prop| {\n                prop.m_size == 16\n                    && prop.n_size == 16\n                    && prop.k_size == 16\n                    && prop.ab_type == wgpu::CooperativeScalarType::F16\n                    && prop.cr_type == wgpu::CooperativeScalarType::F16\n            })\n            .or_else(|| {\n                coop_props.iter().find(|prop| {\n                    prop.m_size == 8\n                        && prop.n_size == 8\n                        && prop.k_size == 8\n                        && prop.ab_type == wgpu::CooperativeScalarType::F32\n                        && prop.cr_type == wgpu::CooperativeScalarType::F32\n                })\n            })\n            .unwrap();\n        let ExecuteResults {\n            max_error,\n            tolerance,\n            matrix: _,\n        } = execute(&ctx.device, &ctx.queue, config).await;\n        assert!(max_error < tolerance);\n    });\n"
  },
  {
    "path": "examples/features/src/cube/README.md",
    "content": "# cube\n\nThis example renders a textured cube.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples cube\n```\n\n## Screenshots\n\n![Cube example](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/cube/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse std::f32::consts;\nuse wgpu::util::DeviceExt;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n    _tex_coord: [f32; 2],\n}\n\nfn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n    }\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0]),\n        vertex([1, -1, 1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([-1, 1, 1], [0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [1, 0]),\n        vertex([1, 1, -1], [0, 0]),\n        vertex([1, -1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [0, 0]),\n        vertex([1, 1, -1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([1, -1, 1], [0, 1]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, 1, 1], [0, 0]),\n        vertex([-1, 1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [1, 0]),\n        vertex([-1, 1, -1], [0, 0]),\n        vertex([-1, 1, 1], [0, 1]),\n        vertex([1, 1, 1], [1, 1]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, 0]),\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, -1, -1], [1, 1]),\n        vertex([1, -1, -1], [0, 1]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\nfn create_texels(size: usize) -> Vec<u8> {\n    (0..size * size)\n        .map(|id| {\n            // get high five for recognizing this ;)\n            let cx = 3.0 * (id % size) as f32 / (size - 1) as f32 - 2.0;\n            let cy = 2.0 * (id / size) as f32 / (size - 1) as f32 - 1.0;\n            let (mut x, mut y, mut count) = (cx, cy, 0);\n            while count < 0xFF && x * x + y * y < 4.0 {\n                let old_x = x;\n                x = x * x - y * y + cx;\n                y = 2.0 * old_x * y + cy;\n                count += 1;\n            }\n            count\n        })\n        .collect()\n}\n\nstruct Example {\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_count: usize,\n    bind_group: wgpu::BindGroup,\n    uniform_buf: wgpu::Buffer,\n    pipeline: wgpu::RenderPipeline,\n    pipeline_wire: Option<wgpu::RenderPipeline>,\n}\n\nimpl Example {\n    fn generate_matrix(aspect_ratio: f32) -> glam::Mat4 {\n        let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0);\n        let view = glam::Mat4::look_at_rh(\n            glam::Vec3::new(1.5f32, -5.0, 3.0),\n            glam::Vec3::ZERO,\n            glam::Vec3::Z,\n        );\n        projection * view\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::POLYGON_MODE_LINE\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        // Create the vertex and index buffers\n        let vertex_size = size_of::<Vertex>();\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX,\n        });\n\n        // Create pipeline layout\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: wgpu::BufferSize::new(64),\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Texture {\n                        multisampled: false,\n                        sample_type: wgpu::TextureSampleType::Uint,\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                    },\n                    count: None,\n                },\n            ],\n        });\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        // Create the texture\n        let size = 256u32;\n        let texels = create_texels(size as usize);\n        let texture_extent = wgpu::Extent3d {\n            width: size,\n            height: size,\n            depth_or_array_layers: 1,\n        };\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: texture_extent,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R8Uint,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n        queue.write_texture(\n            texture.as_image_copy(),\n            &texels,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size),\n                rows_per_image: None,\n            },\n            texture_extent,\n        );\n\n        // Create other resources\n        let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n        let mx_ref: &[f32; 16] = mx_total.as_ref();\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(mx_ref),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        // Create bind group\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n            ],\n            label: None,\n        });\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let vertex_buffers = [wgpu::VertexBufferLayout {\n            array_stride: vertex_size as wgpu::BufferAddress,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &[\n                wgpu::VertexAttribute {\n                    format: wgpu::VertexFormat::Float32x4,\n                    offset: 0,\n                    shader_location: 0,\n                },\n                wgpu::VertexAttribute {\n                    format: wgpu::VertexFormat::Float32x2,\n                    offset: 4 * 4,\n                    shader_location: 1,\n                },\n            ],\n        }];\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &vertex_buffers,\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                cull_mode: Some(wgpu::Face::Back),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let pipeline_wire = if device\n            .features()\n            .contains(wgpu::Features::POLYGON_MODE_LINE)\n        {\n            let pipeline_wire = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: None,\n                layout: Some(&pipeline_layout),\n                vertex: wgpu::VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &vertex_buffers,\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module: &shader,\n                    entry_point: Some(\"fs_wire\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(wgpu::ColorTargetState {\n                        format: config.view_formats[0],\n                        blend: Some(wgpu::BlendState {\n                            color: wgpu::BlendComponent {\n                                operation: wgpu::BlendOperation::Add,\n                                src_factor: wgpu::BlendFactor::SrcAlpha,\n                                dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                            },\n                            alpha: wgpu::BlendComponent::REPLACE,\n                        }),\n                        write_mask: wgpu::ColorWrites::ALL,\n                    })],\n                }),\n                primitive: wgpu::PrimitiveState {\n                    front_face: wgpu::FrontFace::Ccw,\n                    cull_mode: Some(wgpu::Face::Back),\n                    polygon_mode: wgpu::PolygonMode::Line,\n                    ..Default::default()\n                },\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n            Some(pipeline_wire)\n        } else {\n            None\n        };\n\n        // Done\n        Example {\n            vertex_buf,\n            index_buf,\n            index_count: index_data.len(),\n            bind_group,\n            uniform_buf,\n            pipeline,\n            pipeline_wire,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n        let mx_ref: &[f32; 16] = mx_total.as_ref();\n        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(mx_ref));\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.push_debug_group(\"Prepare data for draw.\");\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint16);\n            rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));\n            rpass.pop_debug_group();\n            rpass.insert_debug_marker(\"Draw!\");\n            rpass.draw_indexed(0..self.index_count as u32, 0, 0..1);\n            if let Some(ref pipe) = self.pipeline_wire {\n                rpass.set_pipeline(pipe);\n                rpass.draw_indexed(0..self.index_count as u32, 0, 0..1);\n            }\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"cube\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"cube\",\n    // Generated on 1080ti on Vk/Windows\n    image_path: \"/examples/features/src/cube/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[\n        wgpu_test::ComparisonType::Mean(0.041), // Bounded by Apple A9\n    ],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_LINES: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"cube-lines\",\n    // Generated on 1080ti on Vk/Windows\n    image_path: \"/examples/features/src/cube/screenshot-lines.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::POLYGON_MODE_LINE,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    // We're looking for tiny changes here, so we focus on a spike in the 95th percentile.\n    comparisons: &[\n        wgpu_test::ComparisonType::Mean(0.05), // Bounded by Intel 630 on Vk/Windows\n        wgpu_test::ComparisonType::Percentile {\n            percentile: 0.95,\n            threshold: 0.36,\n        }, // Bounded by 1080ti on DX12\n    ],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/cube/shader.wgsl",
    "content": "struct VertexOutput {\n    @location(0) tex_coord: vec2<f32>,\n    @builtin(position) position: vec4<f32>,\n};\n\n@group(0)\n@binding(0)\nvar<uniform> transform: mat4x4<f32>;\n\n@vertex\nfn vs_main(\n    @location(0) position: vec4<f32>,\n    @location(1) tex_coord: vec2<f32>,\n) -> VertexOutput {\n    var result: VertexOutput;\n    result.tex_coord = tex_coord;\n    result.position = transform * position;\n    return result;\n}\n\n@group(0)\n@binding(1)\nvar r_color: texture_2d<u32>;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    let tex = textureLoad(r_color, vec2<i32>(vertex.tex_coord * 256.0), 0);\n    let v = f32(tex.x) / 255.0;\n    return vec4<f32>(1.0 - (v * 5.0), 1.0 - (v * 15.0), 1.0 - (v * 50.0), 1.0);\n}\n\n@fragment\nfn fs_wire(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0, 0.5, 0.0, 0.5);\n}\n"
  },
  {
    "path": "examples/features/src/framework.rs",
    "content": "use std::future::Future;\nuse std::sync::Arc;\n\nuse wgpu::{Instance, Surface};\nuse winit::{\n    application::ApplicationHandler,\n    dpi::PhysicalSize,\n    event::{KeyEvent, WindowEvent},\n    event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},\n    keyboard::{Key, NamedKey},\n    window::Window,\n};\n\npub trait Example: 'static + Sized {\n    const SRGB: bool = true;\n\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::empty()\n    }\n\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::empty()\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::empty(),\n            shader_model: wgpu::ShaderModel::Sm5,\n            ..wgpu::DownlevelCapabilities::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::downlevel_webgl2_defaults() // These downlevel limits will allow the code to run on all possible hardware\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self;\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    );\n\n    fn update(&mut self, event: WindowEvent);\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue);\n}\n\n// Initialize logging in platform dependant ways.\nfn init_logger() {\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            // As we don't have an environment to pull logging level from, we use the query string.\n            let query_string = web_sys::window().unwrap().location().search().unwrap();\n            let query_level: Option<log::LevelFilter> = parse_url_query_string(&query_string, \"RUST_LOG\")\n                .and_then(|x| x.parse().ok());\n\n            let base_level = query_level.unwrap_or(log::LevelFilter::Info);\n\n            // On web, we use fern, as console_log doesn't have filtering on a per-module level.\n            fern::Dispatch::new()\n                .level(base_level)\n                .chain(fern::Output::call(console_log::log))\n                .apply()\n                .unwrap();\n            std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        } else {\n            // parse_default_env will read the RUST_LOG environment variable and apply it on top\n            // of these default filters.\n            env_logger::builder()\n                .filter_level(log::LevelFilter::Info)\n                .parse_default_env()\n                .init();\n        }\n    }\n}\n\n/// Runs a future to completion. On native this blocks via pollster, on wasm this spawns\n/// a local task. This allows the same async wgpu initialization code to work on both platforms.\n#[cfg(not(target_arch = \"wasm32\"))]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    pollster::block_on(f);\n}\n\n/// Runs a future to completion. On native this blocks via pollster, on wasm this spawns\n/// a local task. This allows the same async wgpu initialization code to work on both platforms.\n#[cfg(target_arch = \"wasm32\")]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    wasm_bindgen_futures::spawn_local(f);\n}\n\n/// Wrapper type which manages the surface and surface configuration.\n///\n/// As surface usage varies per platform, wrapping this up cleans up the event loop code.\nstruct SurfaceWrapper {\n    surface: Option<wgpu::Surface<'static>>,\n    config: Option<wgpu::SurfaceConfiguration>,\n}\n\nimpl SurfaceWrapper {\n    /// Create a new surface wrapper with no surface or configuration.\n    fn new() -> Self {\n        Self {\n            surface: None,\n            config: None,\n        }\n    }\n\n    /// Called after the instance is created, but before we request an adapter.\n    ///\n    /// On wasm, we need to create the surface here, as the WebGL backend needs\n    /// a surface (and hence a canvas) to be present to create the adapter.\n    ///\n    /// We cannot unconditionally create a surface here, as Android requires\n    /// us to wait until we receive the `Resumed` event to do so.\n    fn pre_adapter(&mut self, instance: &Instance, window: Arc<Window>) {\n        if cfg!(target_arch = \"wasm32\") {\n            self.surface = Some(instance.create_surface(window).unwrap());\n        }\n    }\n\n    /// Called on resume to create (on native) and configure the surface.\n    ///\n    /// On all native platforms, this is where we create the surface.\n    /// On wasm, the surface was already created in [`Self::pre_adapter`].\n    ///\n    /// Additionally, we configure the surface based on the (now valid) window size.\n    fn resume(&mut self, context: &ExampleContext, window: Arc<Window>, srgb: bool) {\n        // Window size is only actually valid after we enter the event loop.\n        let window_size = window.inner_size();\n        let width = window_size.width.max(1);\n        let height = window_size.height.max(1);\n\n        log::info!(\"Surface resume {window_size:?}\");\n\n        // We didn't create the surface in pre_adapter, so we need to do so now.\n        if !cfg!(target_arch = \"wasm32\") {\n            self.surface = Some(context.instance.create_surface(window).unwrap());\n        }\n\n        // From here on, self.surface should be Some.\n\n        let surface = self.surface.as_ref().unwrap();\n\n        // Get the default configuration,\n        let mut config = surface\n            .get_default_config(&context.adapter, width, height)\n            .expect(\"Surface isn't supported by the adapter.\");\n        if srgb {\n            // Not all platforms (WebGPU) support sRGB swapchains, so we need to use view formats\n            let view_format = config.format.add_srgb_suffix();\n            config.view_formats.push(view_format);\n        } else {\n            // All platforms support non-sRGB swapchains, so we can just use the format directly.\n            let format = config.format.remove_srgb_suffix();\n            config.format = format;\n            config.view_formats.push(format);\n        };\n        config.desired_maximum_frame_latency = 3;\n\n        surface.configure(&context.device, &config);\n        self.config = Some(config);\n    }\n\n    /// Resize the surface, making sure to not resize to zero.\n    fn resize(&mut self, context: &ExampleContext, size: PhysicalSize<u32>) {\n        log::info!(\"Surface resize {size:?}\");\n\n        let config = self.config.as_mut().unwrap();\n        config.width = size.width.max(1);\n        config.height = size.height.max(1);\n        let surface = self.surface.as_ref().unwrap();\n        surface.configure(&context.device, config);\n    }\n\n    /// Acquire the next surface texture.\n    ///\n    /// Returns `None` on failure.\n    fn acquire(\n        &mut self,\n        context: &ExampleContext,\n        window: Arc<Window>,\n    ) -> Option<wgpu::SurfaceTexture> {\n        use wgpu::CurrentSurfaceTexture;\n\n        let surface = self.surface.as_ref().unwrap();\n\n        match surface.get_current_texture() {\n            CurrentSurfaceTexture::Success(frame) => Some(frame),\n            // If we timed out or the window is occluded, skip this frame:\n            CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => None,\n            // If the surface is outdated or suboptimal, reconfigure and retry.\n            CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {\n                surface.configure(&context.device, self.config());\n                match surface.get_current_texture() {\n                    CurrentSurfaceTexture::Success(frame)\n                    | CurrentSurfaceTexture::Suboptimal(frame) => Some(frame),\n                    other => panic!(\"Failed to acquire next surface texture: {other:?}\"),\n                }\n            }\n            CurrentSurfaceTexture::Validation => {\n                unreachable!(\"No error scope registered, so validation errors will panic\")\n            }\n            // If the surface is lost, recreate and reconfigure it.\n            CurrentSurfaceTexture::Lost => {\n                self.surface = Some(context.instance.create_surface(window).unwrap());\n                self.surface\n                    .as_ref()\n                    .unwrap()\n                    .configure(&context.device, self.config());\n                match self.surface.as_ref().unwrap().get_current_texture() {\n                    CurrentSurfaceTexture::Success(frame)\n                    | CurrentSurfaceTexture::Suboptimal(frame) => Some(frame),\n                    other => panic!(\"Failed to acquire next surface texture: {other:?}\"),\n                }\n            }\n        }\n    }\n\n    /// On suspend on android, we drop the surface, as it's no longer valid.\n    ///\n    /// A suspend event is always followed by at least one resume event.\n    fn suspend(&mut self) {\n        if cfg!(target_os = \"android\") {\n            self.surface = None;\n        }\n    }\n\n    fn get(&self) -> Option<&'_ Surface<'static>> {\n        self.surface.as_ref()\n    }\n\n    fn config(&self) -> &wgpu::SurfaceConfiguration {\n        self.config.as_ref().unwrap()\n    }\n}\n\n/// Context containing global wgpu resources.\nstruct ExampleContext {\n    instance: wgpu::Instance,\n    adapter: wgpu::Adapter,\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n}\nimpl ExampleContext {\n    /// Initializes the example context.\n    async fn init_async<E: Example>(\n        surface: &mut SurfaceWrapper,\n        window: Arc<Window>,\n        display_handle: winit::event_loop::OwnedDisplayHandle,\n    ) -> Self {\n        log::info!(\"Initializing wgpu...\");\n\n        let instance_descriptor =\n            wgpu::InstanceDescriptor::new_with_display_handle_from_env(Box::new(display_handle));\n        let instance = wgpu::Instance::new(instance_descriptor);\n        surface.pre_adapter(&instance, window);\n        let adapter = get_adapter_with_capabilities_or_from_env(\n            &instance,\n            &E::required_features(),\n            &E::required_downlevel_capabilities(),\n            &surface.get(),\n        )\n        .await;\n        // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.\n        let needed_limits = E::required_limits().using_resolution(adapter.limits());\n\n        let info = adapter.get_info();\n        log::info!(\"Selected adapter: {} ({:?})\", info.name, info.backend);\n\n        let (device, queue) = adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                label: None,\n                required_features: (E::optional_features() & adapter.features())\n                    | E::required_features(),\n                required_limits: needed_limits,\n                experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },\n                memory_hints: wgpu::MemoryHints::MemoryUsage,\n                trace: match std::env::var_os(\"WGPU_TRACE\") {\n                    Some(path) => wgpu::Trace::Directory(path.into()),\n                    None => wgpu::Trace::Off,\n                },\n            })\n            .await\n            .expect(\"Unable to find a suitable GPU adapter!\");\n\n        Self {\n            instance,\n            adapter,\n            device,\n            queue,\n        }\n    }\n}\n\nstruct FrameCounter {\n    // Instant of the last time we printed the frame time.\n    last_printed_instant: web_time::Instant,\n    // Number of frames since the last time we printed the frame time.\n    frame_count: u32,\n}\n\nimpl FrameCounter {\n    fn new() -> Self {\n        Self {\n            last_printed_instant: web_time::Instant::now(),\n            frame_count: 0,\n        }\n    }\n\n    fn update(&mut self) {\n        self.frame_count += 1;\n        let new_instant = web_time::Instant::now();\n        let elapsed_secs = (new_instant - self.last_printed_instant).as_secs_f32();\n        if elapsed_secs > 1.0 {\n            let elapsed_ms = elapsed_secs * 1000.0;\n            let frame_time = elapsed_ms / self.frame_count as f32;\n            let fps = self.frame_count as f32 / elapsed_secs;\n            log::info!(\"Frame time {frame_time:.2}ms ({fps:.1} FPS)\");\n\n            self.last_printed_instant = new_instant;\n            self.frame_count = 0;\n        }\n    }\n}\n\n/// User event sent via [`EventLoopProxy`] to deliver async initialization results\n/// back to the main event loop.\nenum AppAction {\n    /// The async wgpu initialization has completed.\n    WgpuInitialized {\n        context: ExampleContext,\n        surface: SurfaceWrapper,\n    },\n}\n\n#[expect(clippy::large_enum_variant)]\nenum AppState<E> {\n    /// Waiting for the first `resumed()` call.\n    Uninitialized,\n    /// Window created, async wgpu initialization in progress.\n    Loading,\n    /// Fully initialized and rendering.\n    Running {\n        context: ExampleContext,\n        surface: SurfaceWrapper,\n        example: E,\n    },\n}\n\n/// The main application struct, implementing winit's [`ApplicationHandler`].\n///\n/// Winit 0.30 requires that windows are not created until the `resumed()` callback,\n/// and that all wgpu resources (instance, adapter, device) are initialized after the\n/// window exists. On native, this init happens synchronously via `pollster::block_on`.\n/// On wasm, it happens asynchronously via `wasm_bindgen_futures::spawn_local`, with\n/// the results delivered back through an [`EventLoopProxy`] user event.\nstruct App<E: Example> {\n    title: &'static str,\n    proxy: EventLoopProxy<AppAction>,\n    window: Option<Arc<Window>>,\n    frame_counter: FrameCounter,\n    occluded: bool,\n    state: AppState<E>,\n}\n\nimpl<E: Example> App<E> {\n    fn new(title: &'static str, event_loop: &EventLoop<AppAction>) -> Self {\n        Self {\n            title,\n            proxy: event_loop.create_proxy(),\n            window: None,\n            frame_counter: FrameCounter::new(),\n            occluded: false,\n            state: AppState::Uninitialized,\n        }\n    }\n}\n\nimpl<E: Example> ApplicationHandler<AppAction> for App<E> {\n    /// Called when the application is (re)started. On the first call, the window and wgpu\n    /// resources are created. On Android, this may be called again after each suspend —\n    /// in that case we only need to re-create the surface.\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        // On Android, re-create the surface after a suspend/resume cycle.\n        if let AppState::Running {\n            ref context,\n            ref mut surface,\n            ..\n        } = self.state\n        {\n            if let Some(window) = &self.window {\n                surface.resume(context, window.clone(), E::SRGB);\n                window.request_redraw();\n            }\n            return;\n        }\n\n        if !matches!(self.state, AppState::Uninitialized) {\n            return;\n        }\n        self.state = AppState::Loading;\n\n        #[cfg_attr(\n            not(target_arch = \"wasm32\"),\n            expect(unused_mut, reason = \"wasm32 re-assigns to specify canvas\")\n        )]\n        let mut attributes = Window::default_attributes().with_title(self.title);\n\n        #[cfg(target_arch = \"wasm32\")]\n        {\n            use wasm_bindgen::JsCast;\n            use winit::platform::web::WindowAttributesExtWebSys;\n            let canvas = web_sys::window()\n                .unwrap()\n                .document()\n                .unwrap()\n                .get_element_by_id(\"canvas\")\n                .unwrap()\n                .dyn_into::<web_sys::HtmlCanvasElement>()\n                .unwrap();\n            attributes = attributes.with_canvas(Some(canvas));\n        }\n\n        let window = Arc::new(\n            event_loop\n                .create_window(attributes)\n                .expect(\"Failed to create window\"),\n        );\n        self.window = Some(window.clone());\n\n        let display_handle = event_loop.owned_display_handle();\n        let proxy = self.proxy.clone();\n\n        // Spawn the async wgpu initialization. On native, `spawn` uses `pollster::block_on`\n        // so this completes synchronously before `resumed()` returns. On wasm, `spawn` uses\n        // `wasm_bindgen_futures::spawn_local` so the result arrives later via `user_event()`.\n        spawn(async move {\n            let mut surface = SurfaceWrapper::new();\n            let context =\n                ExampleContext::init_async::<E>(&mut surface, window.clone(), display_handle).await;\n            surface.resume(&context, window, E::SRGB);\n            let _ = proxy.send_event(AppAction::WgpuInitialized { context, surface });\n        });\n    }\n\n    /// Receives the result of the async wgpu initialization. Creates the [`Example`] and\n    /// transitions to the running state.\n    fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: AppAction) {\n        match event {\n            AppAction::WgpuInitialized { context, surface } => {\n                let example = E::init(\n                    surface.config(),\n                    &context.adapter,\n                    &context.device,\n                    &context.queue,\n                );\n\n                self.state = AppState::Running {\n                    context,\n                    surface,\n                    example,\n                };\n\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n        }\n    }\n\n    fn suspended(&mut self, _event_loop: &ActiveEventLoop) {\n        if let AppState::Running { surface, .. } = &mut self.state {\n            surface.suspend();\n        }\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        _window_id: winit::window::WindowId,\n        event: WindowEvent,\n    ) {\n        let AppState::Running {\n            ref mut context,\n            ref mut surface,\n            ref mut example,\n        } = self.state\n        else {\n            return;\n        };\n\n        match event {\n            WindowEvent::Resized(size) => {\n                surface.resize(context, size);\n                example.resize(surface.config(), &context.device, &context.queue);\n\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::KeyboardInput {\n                event:\n                    KeyEvent {\n                        logical_key: Key::Named(NamedKey::Escape),\n                        ..\n                    },\n                ..\n            }\n            | WindowEvent::CloseRequested => {\n                event_loop.exit();\n            }\n            #[cfg(not(target_arch = \"wasm32\"))]\n            WindowEvent::KeyboardInput {\n                event:\n                    KeyEvent {\n                        logical_key: Key::Character(s),\n                        ..\n                    },\n                ..\n            } if s == \"r\" => {\n                println!(\"{:#?}\", context.instance.generate_report());\n            }\n            WindowEvent::RedrawRequested => {\n                // Don't render while occluded, this may leak on apple platforms.\n                if self.occluded {\n                    return;\n                }\n\n                self.frame_counter.update();\n\n                let window_arc = self.window.clone().unwrap();\n                if let Some(frame) = surface.acquire(context, window_arc) {\n                    let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {\n                        format: Some(surface.config().view_formats[0]),\n                        ..wgpu::TextureViewDescriptor::default()\n                    });\n\n                    example.render(&view, &context.device, &context.queue);\n\n                    if let Some(window) = &self.window {\n                        window.pre_present_notify();\n                    }\n                    frame.present();\n                }\n\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::Occluded(is_occluded) => {\n                self.occluded = is_occluded;\n                // Resume rendering when un-occluded.\n                if !is_occluded {\n                    if let Some(window) = &self.window {\n                        window.request_redraw();\n                    }\n                }\n            }\n            _ => example.update(event),\n        }\n    }\n}\n\nfn start<E: Example>(title: &'static str) {\n    init_logger();\n\n    log::debug!(\n        \"Enabled backends: {:?}\",\n        wgpu::Instance::enabled_backend_features()\n    );\n\n    let event_loop = EventLoop::with_user_event().build().unwrap();\n\n    #[cfg_attr(target_arch = \"wasm32\", expect(unused_mut))]\n    let mut app = App::<E>::new(title, &event_loop);\n\n    log::info!(\"Entering event loop...\");\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            use winit::platform::web::EventLoopExtWebSys;\n            event_loop.spawn_app(app);\n        } else {\n            event_loop.run_app(&mut app).unwrap();\n        }\n    }\n}\n\npub fn run<E: Example>(title: &'static str) {\n    start::<E>(title);\n}\n\n#[cfg(target_arch = \"wasm32\")]\n/// Parse the query string as returned by `web_sys::window()?.location().search()?` and get a\n/// specific key out of it.\npub fn parse_url_query_string<'a>(query: &'a str, search_key: &str) -> Option<&'a str> {\n    let query_string = query.strip_prefix('?')?;\n\n    for pair in query_string.split('&') {\n        let mut pair = pair.split('=');\n        let key = pair.next()?;\n        let value = pair.next()?;\n\n        if key == search_key {\n            return Some(value);\n        }\n    }\n\n    None\n}\n\n#[cfg(test)]\npub use wgpu_test::image::ComparisonType;\n\nuse crate::utils::get_adapter_with_capabilities_or_from_env;\n\n#[cfg(test)]\n#[derive(Clone)]\npub struct ExampleTestParams<E> {\n    pub name: &'static str,\n    // Path to the reference image, relative to the root of the repo.\n    pub image_path: &'static str,\n    pub width: u32,\n    pub height: u32,\n    pub optional_features: wgpu::Features,\n    pub base_test_parameters: wgpu_test::TestParameters,\n    /// Comparisons against FLIP statistics that determine if the test passes or fails.\n    pub comparisons: &'static [ComparisonType],\n    pub _phantom: std::marker::PhantomData<E>,\n}\n\n#[cfg(test)]\nimpl<E: Example + wgpu::WasmNotSendSync> From<ExampleTestParams<E>>\n    for wgpu_test::GpuTestConfiguration\n{\n    fn from(params: ExampleTestParams<E>) -> Self {\n        wgpu_test::GpuTestConfiguration::new()\n            .name(params.name)\n            .parameters({\n                assert_eq!(params.width % 64, 0, \"width needs to be aligned 64\");\n\n                let features = E::required_features() | params.optional_features;\n\n                params\n                    .base_test_parameters\n                    .clone()\n                    .features(features)\n                    .limits(E::required_limits())\n            })\n            .run_async(move |ctx| async move {\n                let format = if E::SRGB {\n                    wgpu::TextureFormat::Rgba8UnormSrgb\n                } else {\n                    wgpu::TextureFormat::Rgba8Unorm\n                };\n                let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                    label: Some(\"destination\"),\n                    size: wgpu::Extent3d {\n                        width: params.width,\n                        height: params.height,\n                        depth_or_array_layers: 1,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format,\n                    usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n                    view_formats: &[],\n                });\n\n                let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n                let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                    label: Some(\"image map buffer\"),\n                    size: params.width as u64 * params.height as u64 * 4,\n                    usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n                    mapped_at_creation: false,\n                });\n\n                let mut example = E::init(\n                    &wgpu::SurfaceConfiguration {\n                        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                        format,\n                        width: params.width,\n                        height: params.height,\n                        desired_maximum_frame_latency: 2,\n                        present_mode: wgpu::PresentMode::Fifo,\n                        alpha_mode: wgpu::CompositeAlphaMode::Auto,\n                        view_formats: vec![format],\n                    },\n                    &ctx.adapter,\n                    &ctx.device,\n                    &ctx.queue,\n                );\n\n                example.render(&dst_view, &ctx.device, &ctx.queue);\n\n                let mut cmd_buf = ctx\n                    .device\n                    .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n                cmd_buf.copy_texture_to_buffer(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &dst_texture,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d::ZERO,\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::TexelCopyBufferInfo {\n                        buffer: &dst_buffer,\n                        layout: wgpu::TexelCopyBufferLayout {\n                            offset: 0,\n                            bytes_per_row: Some(params.width * 4),\n                            rows_per_image: None,\n                        },\n                    },\n                    wgpu::Extent3d {\n                        width: params.width,\n                        height: params.height,\n                        depth_or_array_layers: 1,\n                    },\n                );\n\n                ctx.queue.submit(Some(cmd_buf.finish()));\n\n                let dst_buffer_slice = dst_buffer.slice(..);\n                dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ());\n                ctx.async_poll(wgpu::PollType::wait_indefinitely())\n                    .await\n                    .unwrap();\n                let bytes = dst_buffer_slice.get_mapped_range().to_vec();\n\n                wgpu_test::image::compare_image_output(\n                    dbg!(env!(\"CARGO_MANIFEST_DIR\").to_string() + \"/../../\" + params.image_path),\n                    &ctx.adapter_info,\n                    params.width,\n                    params.height,\n                    &bytes,\n                    params.comparisons,\n                )\n                .await;\n            })\n    }\n}\n"
  },
  {
    "path": "examples/features/src/hello_synchronization/README.md",
    "content": "# hello_synchronization\n\nThis example is \n1. A small demonstration of the importance of synchronization.\n2. How basic synchronization you can understand from the CPU is performed on the GPU.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples hello_synchronization\n```\n\n## A Primer on WGSL Synchronization Functions\n\nThe official documentation is a little scattered and sparse. The meat of the subject is found [here](https://www.w3.org/TR/2023/WD-WGSL-20230629/#sync-builtin-functions) but there's also a bit on control barriers [here](https://www.w3.org/TR/2023/WD-WGSL-20230629/#control-barrier). The most important part comes from that first link though, where the spec says \"the affected memory and atomic operations program-ordered before the synchronization function must be visible to all other threads in the workgroup before any affected memory or atomic operation program-ordered after the synchronization function is executed by a member of the workgroup.\" And at the second, we also get \"a control barrier is executed by all invocations in the same workgroup as if it were executed concurrently.\"\n\nThat's rather vague (and it is by design) so let's break it down and make a comparison that should make that sentence come a bit more into focus. [Barriers in Rust](https://doc.rust-lang.org/std/sync/struct.Barrier.html#) fit both bills rather nicely. Firstly, Rust barriers are executed as if they were executed concurrently because they are - at least as long as you define the execution by when it finishes, when [`Barrier::wait`](https://doc.rust-lang.org/std/sync/struct.Barrier.html#method.wait) finally unblocks the thread and execution continues concurrently from there. Rust barriers also fit the first bill; because all affected threads must execute `Barrier::wait` in order for execution to continue, we can guarantee that _all (synchronous)_ operations ordered before the wait call are executed before any operations ordered after the wait call begin execution. Applying this to WGSL barriers, we can think of a barrier in WGSL as a checkpoint all invocations within each workgroup must reach before the entire workgroup continues with the program together.\n\nThere are two key differences though and one is that although Rust barriers don't enforce that atomic operations called before the barrier are visible after the barrier, WGSL barriers do. This is incredibly useful and important though and is demonstrated in this example.\n\nAnother is that WGSL's synchronous functions only affect memory and atomic operations in a certain address space. This applies to the whole 'all atomic operations called before the function are visible after the function' thing. There are currently three different synchronization functions:\n- `storageBarrier` which works in the storage address space and is a simple barrier.\n- `workgroupBarrier` which works in the workgroup address space and is a simple barrier.\n- `workgroupUniformLoad` which also works in the workgroup address space and is more than just a barrier.\nRead up on all three [here](https://www.w3.org/TR/2023/WD-WGSL-20230629/#sync-builtin-functions)."
  },
  {
    "path": "examples/features/src/hello_synchronization/mod.rs",
    "content": "const ARR_SIZE: usize = 128;\n\nstruct ExecuteResults {\n    patient_workgroup_results: Vec<u32>,\n    hasty_workgroup_results: Vec<u32>,\n}\n\nasync fn run() {\n    let instance = wgpu::Instance::default();\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .unwrap();\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: wgpu::Features::empty(),\n            required_limits: wgpu::Limits::downlevel_defaults(),\n            experimental_features: wgpu::ExperimentalFeatures::disabled(),\n            memory_hints: wgpu::MemoryHints::Performance,\n            trace: wgpu::Trace::Off,\n        })\n        .await\n        .unwrap();\n\n    let ExecuteResults {\n        patient_workgroup_results,\n        hasty_workgroup_results,\n    } = execute(&device, &queue, ARR_SIZE).await;\n\n    // Print data\n    log::info!(\"Patient results: {patient_workgroup_results:?}\");\n    if !patient_workgroup_results.iter().any(|e| *e != 16) {\n        log::info!(\"patient_main was patient.\");\n    } else {\n        log::error!(\"patient_main was not patient!\");\n    }\n    log::info!(\"Hasty results: {hasty_workgroup_results:?}\");\n    if hasty_workgroup_results.iter().any(|e| *e != 16) {\n        log::info!(\"hasty_main was not patient.\");\n    } else {\n        log::info!(\"hasty_main got lucky.\");\n    }\n}\n\nasync fn execute(\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n    result_vec_size: usize,\n) -> ExecuteResults {\n    let mut local_patient_workgroup_results = vec![0u32; result_vec_size];\n    let mut local_hasty_workgroup_results = local_patient_workgroup_results.clone();\n\n    let shaders_module = device.create_shader_module(wgpu::include_wgsl!(\"shaders.wgsl\"));\n\n    let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_of_val(local_patient_workgroup_results.as_slice()) as u64,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n    let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_of_val(local_patient_workgroup_results.as_slice()) as u64,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[wgpu::BindGroupLayoutEntry {\n            binding: 0,\n            visibility: wgpu::ShaderStages::COMPUTE,\n            ty: wgpu::BindingType::Buffer {\n                ty: wgpu::BufferBindingType::Storage { read_only: false },\n                has_dynamic_offset: false,\n                min_binding_size: None,\n            },\n            count: None,\n        }],\n    });\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: storage_buffer.as_entire_binding(),\n        }],\n    });\n\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n    let patient_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &shaders_module,\n        entry_point: Some(\"patient_main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n    let hasty_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &shaders_module,\n        entry_point: Some(\"hasty_main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n\n    //----------------------------------------------------------\n\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        compute_pass.set_pipeline(&patient_pipeline);\n        compute_pass.set_bind_group(0, &bind_group, &[]);\n        compute_pass.dispatch_workgroups(local_patient_workgroup_results.len() as u32, 1, 1);\n    }\n    queue.submit(Some(command_encoder.finish()));\n\n    get_data(\n        local_patient_workgroup_results.as_mut_slice(),\n        &storage_buffer,\n        &output_staging_buffer,\n        device,\n        queue,\n    )\n    .await;\n\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        compute_pass.set_pipeline(&hasty_pipeline);\n        compute_pass.set_bind_group(0, &bind_group, &[]);\n        compute_pass.dispatch_workgroups(local_patient_workgroup_results.len() as u32, 1, 1);\n    }\n    queue.submit(Some(command_encoder.finish()));\n\n    get_data(\n        local_hasty_workgroup_results.as_mut_slice(),\n        &storage_buffer,\n        &output_staging_buffer,\n        device,\n        queue,\n    )\n    .await;\n\n    ExecuteResults {\n        patient_workgroup_results: local_patient_workgroup_results,\n        hasty_workgroup_results: local_hasty_workgroup_results,\n    }\n}\n\nasync fn get_data<T: bytemuck::Pod>(\n    output: &mut [T],\n    storage_buffer: &wgpu::Buffer,\n    staging_buffer: &wgpu::Buffer,\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n) {\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    command_encoder.copy_buffer_to_buffer(\n        storage_buffer,\n        0,\n        staging_buffer,\n        0,\n        size_of_val(output) as u64,\n    );\n    queue.submit(Some(command_encoder.finish()));\n    let buffer_slice = staging_buffer.slice(..);\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    receiver.recv_async().await.unwrap().unwrap();\n    output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));\n    staging_buffer.unmap();\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n        pollster::block_on(run());\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n\n        crate::utils::add_web_nothing_to_see_msg();\n\n        wasm_bindgen_futures::spawn_local(run());\n    }\n}\n\n#[cfg(test)]\npub mod tests;\n"
  },
  {
    "path": "examples/features/src/hello_synchronization/shaders.wgsl",
    "content": "@group(0)\n@binding(0)\nvar<storage, read_write> output: array<u32>;\n\nvar<workgroup> count: atomic<u32>;\n\n@compute\n@workgroup_size(16)\nfn patient_main(\n    @builtin(local_invocation_id) local_id: vec3<u32>,\n    @builtin(workgroup_id) workgroup_id: vec3<u32>\n) {\n    atomicAdd(&count, 1u);\n    workgroupBarrier();\n    if (local_id.x == 0u) {\n        output[workgroup_id.x] = atomicLoad(&count);\n    }\n}\n\n@compute\n@workgroup_size(16)\nfn hasty_main(\n    @builtin(local_invocation_id) local_id: vec3<u32>,\n    @builtin(workgroup_id) workgroup_id: vec3<u32>\n) {\n    atomicAdd(&count, 1u);\n    if (local_id.x == 0u) {\n        output[workgroup_id.x] = atomicLoad(&count);\n    }\n}"
  },
  {
    "path": "examples/features/src/hello_synchronization/tests.rs",
    "content": "use super::*;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};\n\n#[gpu_test]\npub static SYNC: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        // Taken from hello-compute tests.\n        TestParameters::default()\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| async move {\n        let ExecuteResults {\n            patient_workgroup_results,\n            hasty_workgroup_results: _,\n        } = execute(&ctx.device, &ctx.queue, ARR_SIZE).await;\n        assert_eq!(patient_workgroup_results, [16_u32; ARR_SIZE]);\n    });\n"
  },
  {
    "path": "examples/features/src/hello_triangle/README.md",
    "content": "# hello_triangle\n\nThis example renders a triangle to a window.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples hello_triangle\n```\n\n## Screenshots\n\n![Triangle window](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/hello_triangle/mod.rs",
    "content": "use std::{borrow::Cow, future::Future, sync::Arc};\nuse wgpu::CurrentSurfaceTexture;\nuse winit::{\n    application::ApplicationHandler,\n    event::WindowEvent,\n    event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},\n    window::Window,\n};\n\n/// Runs a future to completion. On native this blocks synchronously via pollster.\n/// On wasm this spawns a local task so control returns to the browser immediately.\n#[cfg(not(target_arch = \"wasm32\"))]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    pollster::block_on(f);\n}\n\n/// Runs a future to completion. On native this blocks synchronously via pollster.\n/// On wasm this spawns a local task so control returns to the browser immediately.\n#[cfg(target_arch = \"wasm32\")]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    wasm_bindgen_futures::spawn_local(f);\n}\n\nstruct WgpuState {\n    instance: wgpu::Instance,\n    window: Arc<Window>,\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n    surface: wgpu::Surface<'static>,\n    config: wgpu::SurfaceConfiguration,\n    render_pipeline: wgpu::RenderPipeline,\n}\n\nenum TriangleAction {\n    Initialized(WgpuState),\n}\n\n#[expect(clippy::large_enum_variant)]\nenum AppState {\n    Uninitialized,\n    Loading,\n    Running(WgpuState),\n}\n\nstruct App {\n    proxy: EventLoopProxy<TriangleAction>,\n    window: Option<Arc<Window>>,\n    state: AppState,\n}\n\nimpl App {\n    fn new(event_loop: &EventLoop<TriangleAction>) -> Self {\n        Self {\n            proxy: event_loop.create_proxy(),\n            window: None,\n            state: AppState::Uninitialized,\n        }\n    }\n}\n\nimpl ApplicationHandler<TriangleAction> for App {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        if !matches!(self.state, AppState::Uninitialized) {\n            return;\n        }\n        self.state = AppState::Loading;\n\n        #[cfg_attr(\n            not(target_arch = \"wasm32\"),\n            expect(unused_mut, reason = \"wasm32 re-assigns to specify canvas\")\n        )]\n        let mut attributes = Window::default_attributes();\n\n        #[cfg(target_arch = \"wasm32\")]\n        {\n            use wasm_bindgen::JsCast;\n            use winit::platform::web::WindowAttributesExtWebSys;\n            let canvas = web_sys::window()\n                .unwrap()\n                .document()\n                .unwrap()\n                .get_element_by_id(\"canvas\")\n                .unwrap()\n                .dyn_into::<web_sys::HtmlCanvasElement>()\n                .unwrap();\n            attributes = attributes.with_canvas(Some(canvas));\n        }\n\n        let window = Arc::new(\n            event_loop\n                .create_window(attributes)\n                .expect(\"Failed to create window\"),\n        );\n        self.window = Some(window.clone());\n\n        let display_handle = event_loop.owned_display_handle();\n        let proxy = self.proxy.clone();\n\n        spawn(async move {\n            let mut size = window.inner_size();\n            size.width = size.width.max(1);\n            size.height = size.height.max(1);\n\n            let instance =\n                wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle_from_env(\n                    Box::new(display_handle),\n                ));\n\n            let surface = instance.create_surface(window.clone()).unwrap();\n            let adapter = instance\n                .request_adapter(&wgpu::RequestAdapterOptions {\n                    power_preference: wgpu::PowerPreference::default(),\n                    force_fallback_adapter: false,\n                    // Request an adapter which can render to our surface\n                    compatible_surface: Some(&surface),\n                })\n                .await\n                .expect(\"Failed to find an appropriate adapter\");\n\n            // Create the logical device and command queue\n            let (device, queue) = adapter\n                .request_device(&wgpu::DeviceDescriptor {\n                    label: None,\n                    required_features: wgpu::Features::empty(),\n                    // Make sure we use the texture resolution limits from the adapter,\n                    // so we can support images the size of the swapchain.\n                    required_limits: wgpu::Limits::downlevel_webgl2_defaults()\n                        .using_resolution(adapter.limits()),\n                    experimental_features: wgpu::ExperimentalFeatures::disabled(),\n                    memory_hints: wgpu::MemoryHints::MemoryUsage,\n                    trace: wgpu::Trace::Off,\n                })\n                .await\n                .expect(\"Failed to create device\");\n\n            // Load the shaders from disk\n            let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n            });\n\n            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[],\n                immediate_size: 0,\n            });\n\n            let swapchain_capabilities = surface.get_capabilities(&adapter);\n            let swapchain_format = swapchain_capabilities.formats[0];\n\n            let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: None,\n                layout: Some(&pipeline_layout),\n                vertex: wgpu::VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_main\"),\n                    buffers: &[],\n                    compilation_options: Default::default(),\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module: &shader,\n                    entry_point: Some(\"fs_main\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(swapchain_format.into())],\n                }),\n                primitive: wgpu::PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n            let config = surface\n                .get_default_config(&adapter, size.width, size.height)\n                .unwrap();\n            surface.configure(&device, &config);\n\n            let _ = proxy.send_event(TriangleAction::Initialized(WgpuState {\n                instance,\n                window,\n                device,\n                queue,\n                surface,\n                config,\n                render_pipeline,\n            }));\n        });\n    }\n\n    fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: TriangleAction) {\n        match event {\n            TriangleAction::Initialized(wgpu_state) => {\n                self.state = AppState::Running(wgpu_state);\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n        }\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        _window_id: winit::window::WindowId,\n        event: WindowEvent,\n    ) {\n        let AppState::Running(wgpu_state) = &mut self.state else {\n            return;\n        };\n\n        match event {\n            WindowEvent::Resized(new_size) => {\n                // Reconfigure the surface with the new size\n                wgpu_state.config.width = new_size.width.max(1);\n                wgpu_state.config.height = new_size.height.max(1);\n                wgpu_state\n                    .surface\n                    .configure(&wgpu_state.device, &wgpu_state.config);\n                // On macos the window needs to be redrawn manually after resizing\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::RedrawRequested => {\n                let frame = match wgpu_state.surface.get_current_texture() {\n                    CurrentSurfaceTexture::Success(frame) => frame,\n                    CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {\n                        // Try again later\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                    CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {\n                        wgpu_state\n                            .surface\n                            .configure(&wgpu_state.device, &wgpu_state.config);\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                    CurrentSurfaceTexture::Validation => {\n                        unreachable!(\"No error scope registered, so validation errors will panic\")\n                    }\n                    CurrentSurfaceTexture::Lost => {\n                        wgpu_state.surface = wgpu_state\n                            .instance\n                            .create_surface(wgpu_state.window.clone())\n                            .unwrap();\n                        wgpu_state\n                            .surface\n                            .configure(&wgpu_state.device, &wgpu_state.config);\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                };\n\n                let view = frame\n                    .texture\n                    .create_view(&wgpu::TextureViewDescriptor::default());\n                let mut encoder = wgpu_state\n                    .device\n                    .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n                {\n                    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                        label: None,\n                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                            view: &view,\n                            depth_slice: None,\n                            resolve_target: None,\n                            ops: wgpu::Operations {\n                                load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                                store: wgpu::StoreOp::Store,\n                            },\n                        })],\n                        depth_stencil_attachment: None,\n                        timestamp_writes: None,\n                        occlusion_query_set: None,\n                        multiview_mask: None,\n                    });\n                    rpass.set_pipeline(&wgpu_state.render_pipeline);\n                    rpass.draw(0..3, 0..1);\n                }\n\n                wgpu_state.queue.submit(Some(encoder.finish()));\n                if let Some(window) = &self.window {\n                    window.pre_present_notify();\n                }\n                frame.present();\n            }\n            WindowEvent::Occluded(is_occluded) => {\n                if !is_occluded {\n                    if let Some(window) = &self.window {\n                        window.request_redraw();\n                    }\n                }\n            }\n            WindowEvent::CloseRequested => event_loop.exit(),\n            _ => {}\n        }\n    }\n}\n\npub fn main() {\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n            console_log::init().expect(\"could not initialize logger\");\n        } else {\n            env_logger::init();\n        }\n    }\n\n    let event_loop = EventLoop::with_user_event().build().unwrap();\n\n    #[cfg_attr(target_arch = \"wasm32\", expect(unused_mut))]\n    let mut app = App::new(&event_loop);\n\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            use winit::platform::web::EventLoopExtWebSys;\n            event_loop.spawn_app(app);\n        } else {\n            event_loop.run_app(&mut app).unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/features/src/hello_triangle/shader.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {\n    let x = f32(i32(in_vertex_index) - 1);\n    let y = f32(i32(in_vertex_index & 1u) * 2 - 1);\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/hello_windows/README.md",
    "content": "# hello_windows\n\nThis example renders a set of 16 windows, with a differently colored background\n\n## To Run\n\n```\ncargo run --bin wgpu-examples hello_windows\n```\n\n## Screenshots\n\n![16 windows](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/hello_windows/mod.rs",
    "content": "#![cfg_attr(target_arch = \"wasm32\", allow(dead_code, unused_imports))]\n\nuse std::{collections::HashMap, sync::Arc};\nuse wgpu::CurrentSurfaceTexture;\nuse winit::{\n    application::ApplicationHandler,\n    event::WindowEvent,\n    event_loop::ActiveEventLoop,\n    window::{Window, WindowId},\n};\n\nstruct ViewportDesc {\n    window: Arc<Window>,\n    background: wgpu::Color,\n    surface: wgpu::Surface<'static>,\n}\n\nstruct Viewport {\n    desc: ViewportDesc,\n    config: wgpu::SurfaceConfiguration,\n}\n\nimpl ViewportDesc {\n    fn new(window: Arc<Window>, background: wgpu::Color, instance: &wgpu::Instance) -> Self {\n        let surface = instance.create_surface(window.clone()).unwrap();\n        Self {\n            window,\n            background,\n            surface,\n        }\n    }\n\n    fn build(self, adapter: &wgpu::Adapter, device: &wgpu::Device) -> Viewport {\n        let size = self.window.inner_size();\n        let config = self\n            .surface\n            .get_default_config(adapter, size.width, size.height)\n            .unwrap();\n        self.surface.configure(device, &config);\n        Viewport { desc: self, config }\n    }\n}\n\nimpl Viewport {\n    fn resize(&mut self, device: &wgpu::Device, size: winit::dpi::PhysicalSize<u32>) {\n        self.config.width = size.width;\n        self.config.height = size.height;\n        self.desc.surface.configure(device, &self.config);\n    }\n\n    fn get_current_texture(&mut self) -> CurrentSurfaceTexture {\n        self.desc.surface.get_current_texture()\n    }\n}\n\nconst WINDOW_SIZE: u32 = 128;\nconst WINDOW_PADDING: u32 = 16;\nconst WINDOW_TITLEBAR: u32 = 32;\nconst WINDOW_OFFSET: u32 = WINDOW_SIZE + WINDOW_PADDING;\nconst ROWS: u32 = 4;\nconst COLUMNS: u32 = 4;\n\nenum AppState {\n    Uninitialized,\n    Running {\n        instance: wgpu::Instance,\n        device: wgpu::Device,\n        queue: wgpu::Queue,\n        viewports: HashMap<WindowId, Viewport>,\n    },\n}\n\nstruct App {\n    state: AppState,\n}\n\nimpl App {\n    fn new() -> Self {\n        Self {\n            state: AppState::Uninitialized,\n        }\n    }\n}\n\nimpl ApplicationHandler for App {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        if !matches!(self.state, AppState::Uninitialized) {\n            return;\n        }\n\n        // Create all 16 windows.\n        let mut windows: Vec<(Arc<Window>, wgpu::Color)> =\n            Vec::with_capacity((ROWS * COLUMNS) as usize);\n        for row in 0..ROWS {\n            for column in 0..COLUMNS {\n                let window = Arc::new(\n                    event_loop\n                        .create_window(\n                            Window::default_attributes()\n                                .with_title(format!(\"x{column}y{row}\"))\n                                .with_inner_size(winit::dpi::PhysicalSize::new(\n                                    WINDOW_SIZE,\n                                    WINDOW_SIZE,\n                                )),\n                        )\n                        .unwrap(),\n                );\n                window.set_outer_position(winit::dpi::PhysicalPosition::new(\n                    WINDOW_PADDING + column * WINDOW_OFFSET,\n                    WINDOW_PADDING + row * (WINDOW_OFFSET + WINDOW_TITLEBAR),\n                ));\n                fn frac(index: u32, max: u32) -> f64 {\n                    index as f64 / max as f64\n                }\n                windows.push((\n                    window,\n                    wgpu::Color {\n                        r: frac(row, ROWS),\n                        g: 0.5 - frac(row * column, ROWS * COLUMNS) * 0.5,\n                        b: frac(column, COLUMNS),\n                        a: 1.0,\n                    },\n                ));\n            }\n        }\n\n        // Initialize wgpu synchronously (native-only).\n        let instance =\n            wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle_from_env(\n                Box::new(event_loop.owned_display_handle()),\n            ));\n        let viewport_descs: Vec<_> = windows\n            .into_iter()\n            .map(|(window, color)| ViewportDesc::new(window, color, &instance))\n            .collect();\n        let (adapter, device, queue) = pollster::block_on(async {\n            let adapter = instance\n                .request_adapter(&wgpu::RequestAdapterOptions {\n                    compatible_surface: viewport_descs.first().map(|desc| &desc.surface),\n                    ..Default::default()\n                })\n                .await\n                .expect(\"Failed to find an appropriate adapter\");\n\n            let (device, queue) = adapter\n                .request_device(&wgpu::DeviceDescriptor {\n                    label: None,\n                    required_features: wgpu::Features::empty(),\n                    required_limits: wgpu::Limits::downlevel_defaults(),\n                    experimental_features: wgpu::ExperimentalFeatures::disabled(),\n                    memory_hints: wgpu::MemoryHints::MemoryUsage,\n                    trace: wgpu::Trace::Off,\n                })\n                .await\n                .expect(\"Failed to create device\");\n\n            (adapter, device, queue)\n        });\n\n        let viewports: HashMap<WindowId, Viewport> = viewport_descs\n            .into_iter()\n            .map(|desc| (desc.window.id(), desc.build(&adapter, &device)))\n            .collect();\n\n        self.state = AppState::Running {\n            instance,\n            device,\n            queue,\n            viewports,\n        };\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n        event: WindowEvent,\n    ) {\n        let AppState::Running {\n            instance,\n            device,\n            queue,\n            viewports,\n        } = &mut self.state\n        else {\n            return;\n        };\n\n        match event {\n            WindowEvent::Resized(new_size) => {\n                // Recreate the swap chain with the new size\n                if let Some(viewport) = viewports.get_mut(&window_id) {\n                    viewport.resize(device, new_size);\n                    // On macos the window needs to be redrawn manually after resizing\n                    viewport.desc.window.request_redraw();\n                }\n            }\n            WindowEvent::RedrawRequested => {\n                if let Some(viewport) = viewports.get_mut(&window_id) {\n                    let frame = match viewport.get_current_texture() {\n                        CurrentSurfaceTexture::Success(frame) => frame,\n                        CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {\n                            viewport.desc.window.request_redraw();\n                            return;\n                        }\n                        CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {\n                            viewport.desc.surface.configure(device, &viewport.config);\n                            viewport.desc.window.request_redraw();\n                            return;\n                        }\n                        CurrentSurfaceTexture::Validation => {\n                            unreachable!(\n                                \"No error scope registered, so validation errors will panic\"\n                            )\n                        }\n                        CurrentSurfaceTexture::Lost => {\n                            viewport.desc.surface = instance\n                                .create_surface(viewport.desc.window.clone())\n                                .unwrap();\n                            viewport.desc.surface.configure(device, &viewport.config);\n                            viewport.desc.window.request_redraw();\n                            return;\n                        }\n                    };\n\n                    let view = frame\n                        .texture\n                        .create_view(&wgpu::TextureViewDescriptor::default());\n                    let mut encoder = device\n                        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n                    {\n                        let _rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                            label: None,\n                            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                                view: &view,\n                                depth_slice: None,\n                                resolve_target: None,\n                                ops: wgpu::Operations {\n                                    load: wgpu::LoadOp::Clear(viewport.desc.background),\n                                    store: wgpu::StoreOp::Store,\n                                },\n                            })],\n                            depth_stencil_attachment: None,\n                            timestamp_writes: None,\n                            occlusion_query_set: None,\n                            multiview_mask: None,\n                        });\n                    }\n\n                    queue.submit(Some(encoder.finish()));\n                    viewport.desc.window.pre_present_notify();\n                    frame.present();\n                }\n            }\n            WindowEvent::Occluded(is_occluded) => {\n                if !is_occluded {\n                    if let Some(viewport) = viewports.get(&window_id) {\n                        viewport.desc.window.request_redraw();\n                    }\n                }\n            }\n            WindowEvent::CloseRequested => {\n                viewports.remove(&window_id);\n                if viewports.is_empty() {\n                    event_loop.exit();\n                }\n            }\n            _ => {}\n        }\n    }\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::init();\n        let event_loop = winit::event_loop::EventLoop::new().unwrap();\n        let mut app = App::new();\n        event_loop.run_app(&mut app).unwrap();\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        panic!(\"wasm32 is not supported\")\n    }\n}\n"
  },
  {
    "path": "examples/features/src/hello_workgroups/README.md",
    "content": "# hello_workgroups\n\nNow you finally know what that silly little `@workgroup_size(1)` means!\n\nThis example is an extremely bare-bones and arguably somewhat unreasonable demonstration of what workgroup sizes mean in an attempt to explain workgroups in general.\n\nThe example starts with two arrays of numbers. One where `a[i] = i` and the other where `b[i] = 2i`. Both are bound to the shader. The program dispatches a workgroup for each index, each workgroup representing both elements at that index in both arrays. Each invocation in each workgroup works on its respective array and adds 1 to the element there.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples hello_workgroups\n```\n\n## What are Workgroups?\n\n### TLDR / Key Takeaways\n\n- Workgroups fit in a 3d grid of workgroups executed in a single dispatch.\n- All invocations in a workgroup are guaranteed to execute concurrently.\n- Workgroups carry no other guarantees for concurrency outside of those individual workgroups, meaning...\n  - No two workgroups can be guaranteed to be executed in parallel.\n  - No two workgroups can be guaranteed NOT to be executed in parallel.\n  - No set of workgroups can be guaranteed to execute in any predictable or reliable order in relation to each other.\n- The size of a workgroup is defined with the `@workgroup_size` attribute on a compute shader main function.\n- The location of an invocation within its workgroup grid can be got with `@builtin(local_invocation_id)`.\n- The location of an invocation within the entire compute shader grid can be gotten with `@builtin(global_invocation_id)`.\n- The location of an invocation's workgroup within the dispatch grid can be gotten with `@builtin(workgroup_id)`.\n- Workgroups share memory within the `workgroup` address space. Workgroup memory is similar to private memory but it is shared within a workgroup. Invocations within a workgroup will see the same memory but invocations across workgroups will be accessing different memory.\n\n### Introduction\n\nWhen you call `ComputePass::dispatch_workgroups`, the function dispatches multiple workgroups in a 3d grid defined by the `x`, `y`, and `z` parameters you pass to the function. For example, `dispatch_workgroups(5, 2, 1)` would create a dispatch grid like\n||||||\n|---|---|---|---|---|\n| W | W | W | W | W |\n| W | W | W | W | W |\n\nWhere each W is a workgroup. If you want your shader to consider what workgroup within the dispatch the current invocation is in, add a function argument with type `vec3<u32>` and with the attribute `@builtin(workgroup_id)`.\n\nNote here that in this example, the term \"dispatch grid\" is used throughout to mean the grid of workgroups within the dispatch but is not a proper term within WGSL. Other terms to know though that are proper are \"workgroup grid\" which refers to the invocations in a single _workgroup_ and \"compute shader grid\" which refers to the grid of _all_ the invocations in the _entire dispatch_.\n\n### Within the Workgroup\n\nAlthough with hello-compute and repeated-compute, we used a workgroup size of `(1)`, or rather, (1, 1, 1), and then each workgroup called from `dispatch_workgroups` made _an_ invocation, this isn't always the case. Each workgroup represents its own little grid of individual invocations tied together. This could be just one or practically any number in a 3d grid of invocations. The grid size of each workgroup and thus the number of invocations called per workgroup is determined by the `@workgroup_size` attribute you've seen in other compute shaders. To get the current invocation's location within a workgroup, add a `vec3<u32>` argument to the main function with the attribute `@builtin(local_invocation_id)`. We'll look at the compute shader grid of a dispatch of size (2, 2, 1) with workgroup sizes of (2, 2, 1) as well. Let `w` be the `workgroup_id` and `i` be the `local_invocation_id`.\n\n||||| \n|------------------------|------------------------|------------------------|------------------------|\n| w(0, 0, 0), i(0, 0, 0) | w(0, 0, 0), i(1, 0, 0) | w(1, 0, 0), i(0, 0, 0) | w(1, 0, 0), i(1, 0, 0) |\n| w(0, 0, 0), i(0, 1, 0) | w(0, 0, 0), i(1, 1, 0) | w(1, 0, 0), i(0, 1, 0) | w(1, 0, 0), i(1, 1, 0) |\n| w(0, 1, 0), i(0, 0, 0) | w(0, 1, 0), i(1, 0, 0) | w(1, 1, 0), i(0, 0, 0) | w(1, 1, 0), i(1, 0, 0) |\n| w(0, 1, 0), i(0, 1, 0) | w(0, 1, 0), i(1, 1, 0) | w(1, 1, 0), i(0, 1, 0) | w(1, 1, 0), i(1, 1, 0) |\n\n### Execution of Workgroups\n\nAs stated before, workgroups are groups of invocations. The invocations within a workgroup are always guaranteed to execute in parallel. That said, the guarantees basically stop there. You cannot get any guarantee as to when any given workgroup will execute, including in relation to other workgroups. You can't guarantee that any two workgroups will execute together nor can you guarantee that they will _not_ execute together. Of the workgroups that don't execute together, you additionally cannot guarantee that they will execute in any particular order. When your function runs in an invocation, you know that it will be working together with its workgroup buddies and that's basically it.\n\nSee [the WGSL spec on compute shader execution](https://www.w3.org/TR/2023/WD-WGSL-20230629/#compute-shader-workgroups) for more details.\n\n### Workgroups and their Invocations in a Global Scope\n\nAs mentioned above, invocations exist both within the context of a workgroup grid as well as a compute shader grid which is a grid, divided into workgroup sections, of invocations that represents the whole of the dispatch. Similar to how `@builtin(local_invocation_id)` gets you the place of the invocation within the workgroup grid, `@builtin(global_invocation_id)` gets you the place of the invocation within the entire compute shader grid. Slight trivia: you might imagine that this is computed from `local_invocation_id` and `workgroup_id` but it's actually the opposite. Everything operates on the compute shader grid, the workgroups are imagined sectors within the compute shader grid, and `local_invocation_id` and `workgroup_id` are calculated based on global id and known workgroup size. Yes, we live in a matrix... of compute shader invocations. This isn't super useful information but it can help fit things into a larger picture.\n\n## Barriers and Workgroups\n\nArguably, workgroups are at their most useful when being used alongside barriers. Since barriers are already explained more thoroughly in the hello-synchronization example, this section will be short. Despite affecting different memory address spaces, all synchronization functions affect invocations on a workgroup level, synchronizing the workgroup. See [hello-synchronization/README.md](../hello-synchronization/README.md) for more.\n\n## Links to Technical Resources\n\nFor a rather long explainer, this README may still leave the more technically minded person with questions. The specifications for both WebGPU and WGSL (\"WebGPU Shading Language\") are long and it's rather unintuitive that by far the vast majority of specification on how workgroups and compute shaders more generally work, is all in the WGSL spec. Below are some links into the specifications at a couple interesting points:\n\n- [Here](https://www.w3.org/TR/WGSL/#compute-shader-workgroups) is the main section on workgroups and outlines important terminology in technical terms. It is recommended that everyone looking for something in this section of this README start by reading this.\n- [Here](https://www.w3.org/TR/webgpu/#computing-operations) is a section on compute shaders from a WebGPU perspective (instead of WGSL). It's still a stub but hopefully it will grow in the future.\n- Don't forget your [`@builtin()`'s](https://www.w3.org/TR/WGSL/#builtin-inputs-outputs)!"
  },
  {
    "path": "examples/features/src/hello_workgroups/mod.rs",
    "content": "//! This example assumes that you've seen hello-compute and or repeated-compute\n//! and thus have a general understanding of what's going on here.\n//!\n//! There's an explainer on what this example does exactly and what workgroups\n//! are and the meaning of `@workgroup(size_x, size_y, size_z)` in the\n//! README. Also see commenting in shader.wgsl as well.\n//!\n//! Only parts specific to this example will be commented.\n\nuse wgpu::util::DeviceExt;\n\nasync fn run() {\n    let mut local_a = [0i32; 100];\n    for (i, e) in local_a.iter_mut().enumerate() {\n        *e = i as i32;\n    }\n    log::info!(\"Input a: {local_a:?}\");\n    let mut local_b = [0i32; 100];\n    for (i, e) in local_b.iter_mut().enumerate() {\n        *e = i as i32 * 2;\n    }\n    log::info!(\"Input b: {local_b:?}\");\n\n    let instance = wgpu::Instance::default();\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .unwrap();\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: wgpu::Features::empty(),\n            required_limits: wgpu::Limits::downlevel_defaults(),\n            experimental_features: wgpu::ExperimentalFeatures::disabled(),\n            memory_hints: wgpu::MemoryHints::MemoryUsage,\n            trace: wgpu::Trace::Off,\n        })\n        .await\n        .unwrap();\n\n    let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    let storage_buffer_a = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(&local_a[..]),\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n    });\n    let storage_buffer_b = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(&local_b[..]),\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n    });\n    let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_of_val(&local_a) as u64,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[\n            wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n            wgpu::BindGroupLayoutEntry {\n                binding: 1,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n        ],\n    });\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: storage_buffer_a.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: storage_buffer_b.as_entire_binding(),\n            },\n        ],\n    });\n\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &shader,\n        entry_point: Some(\"main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n\n    //----------------------------------------------------------\n\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        compute_pass.set_pipeline(&pipeline);\n        compute_pass.set_bind_group(0, &bind_group, &[]);\n        /* Note that since each workgroup will cover both arrays, we only need to\n        cover the length of one array. */\n        compute_pass.dispatch_workgroups(local_a.len() as u32, 1, 1);\n    }\n    queue.submit(Some(command_encoder.finish()));\n\n    //----------------------------------------------------------\n\n    get_data(\n        &mut local_a[..],\n        &storage_buffer_a,\n        &output_staging_buffer,\n        &device,\n        &queue,\n    )\n    .await;\n    get_data(\n        &mut local_b[..],\n        &storage_buffer_b,\n        &output_staging_buffer,\n        &device,\n        &queue,\n    )\n    .await;\n\n    log::info!(\"Output in A: {local_a:?}\");\n    log::info!(\"Output in B: {local_b:?}\");\n}\n\nasync fn get_data<T: bytemuck::Pod>(\n    output: &mut [T],\n    storage_buffer: &wgpu::Buffer,\n    staging_buffer: &wgpu::Buffer,\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n) {\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    command_encoder.copy_buffer_to_buffer(\n        storage_buffer,\n        0,\n        staging_buffer,\n        0,\n        size_of_val(output) as u64,\n    );\n    queue.submit(Some(command_encoder.finish()));\n    let buffer_slice = staging_buffer.slice(..);\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    receiver.recv_async().await.unwrap().unwrap();\n    output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));\n    staging_buffer.unmap();\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n        pollster::block_on(run());\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n\n        crate::utils::add_web_nothing_to_see_msg();\n\n        wasm_bindgen_futures::spawn_local(run());\n    }\n}\n"
  },
  {
    "path": "examples/features/src/hello_workgroups/shader.wgsl",
    "content": "// This is useful because we can't use, say, vec2<array<i32>> because\n// of array<T> being unsized. Normally we would interweave them or use\n// and array of structs but this is just for the sake of demonstration.\n\n@group(0)\n@binding(0)\nvar<storage, read_write> a: array<i32>;\n\n@group(0)\n@binding(1)\nvar<storage, read_write> b: array<i32>;\n\n@compute\n@workgroup_size(2, 1, 1)\nfn main(@builtin(local_invocation_id) lid: vec3<u32>, @builtin(workgroup_id) wid: vec3<u32>) {\n    if lid.x == 0u {\n        // Do computation (use your imagionation)\n        a[wid.x] += 1;\n    } else if lid.x == 1u {\n        // Do computation\n        b[wid.x] += 1;\n    }\n}"
  },
  {
    "path": "examples/features/src/lib.rs",
    "content": "#![allow(clippy::arc_with_non_send_sync)] // False positive on wasm\n#![warn(clippy::allow_attributes)]\n\npub mod framework;\npub mod utils;\n\npub mod big_compute_buffers;\npub mod boids;\npub mod bunnymark;\npub mod conservative_raster;\npub mod cooperative_matrix;\npub mod cube;\npub mod hello_synchronization;\npub mod hello_triangle;\npub mod hello_windows;\npub mod hello_workgroups;\npub mod mesh_shader;\npub mod mipmap;\npub mod msaa_line;\npub mod multiple_render_targets;\npub mod multiview;\npub mod ray_cube_compute;\npub mod ray_cube_fragment;\npub mod ray_cube_normals;\npub mod ray_scene;\npub mod ray_shadows;\npub mod ray_traced_triangle;\npub mod render_to_texture;\npub mod render_with_compute;\npub mod repeated_compute;\npub mod shadow;\npub mod skybox;\npub mod srgb_blend;\npub mod stencil_triangles;\npub mod storage_texture;\npub mod texture_arrays;\npub mod timestamp_queries;\npub mod uniform_values;\npub mod water;\n\n#[cfg(test)]\nfn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {\n    #[cfg_attr(\n        target_arch = \"wasm32\",\n        expect(unused_mut, reason = \"non-wasm32 needs this mutable\")\n    )]\n    let mut test_list = vec![\n        boids::TEST,\n        bunnymark::TEST,\n        conservative_raster::TEST,\n        cube::TEST,\n        cube::TEST_LINES,\n        hello_synchronization::tests::SYNC,\n        mesh_shader::TEST,\n        mipmap::TEST,\n        mipmap::TEST_QUERY,\n        msaa_line::TEST,\n        multiple_render_targets::TEST,\n        ray_cube_compute::TEST,\n        ray_cube_fragment::TEST,\n        ray_cube_normals::TEST,\n        ray_scene::TEST,\n        ray_shadows::TEST,\n        ray_traced_triangle::TEST,\n        shadow::TEST,\n        skybox::TEST,\n        skybox::TEST_ASTC,\n        skybox::TEST_BCN,\n        skybox::TEST_ETC2,\n        srgb_blend::TEST_LINEAR,\n        srgb_blend::TEST_SRGB,\n        stencil_triangles::TEST,\n        texture_arrays::TEST,\n        texture_arrays::TEST_NON_UNIFORM,\n        texture_arrays::TEST_UNIFORM,\n        timestamp_queries::tests::TIMESTAMPS_ENCODER,\n        timestamp_queries::tests::TIMESTAMPS_PASSES,\n        timestamp_queries::tests::TIMESTAMPS_PASS_BOUNDARIES,\n        water::TEST,\n    ];\n\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        test_list.push(big_compute_buffers::tests::TWO_BUFFERS);\n        test_list.push(cooperative_matrix::tests::COOPERATIVE_MATRIX);\n    }\n\n    test_list\n}\n\n#[cfg(test)]\nwgpu_test::gpu_test_main!(all_tests());\n"
  },
  {
    "path": "examples/features/src/main.rs",
    "content": "struct ExampleDesc {\n    name: &'static str,\n    function: fn(),\n    #[cfg_attr(not(target_arch = \"wasm32\"), expect(dead_code))]\n    webgl: bool,\n    #[cfg_attr(not(target_arch = \"wasm32\"), expect(dead_code))]\n    webgpu: bool,\n}\n\nconst EXAMPLES: &[ExampleDesc] = &[\n    ExampleDesc {\n        name: \"big_compute_buffers\",\n        function: wgpu_examples::big_compute_buffers::main,\n        webgl: false,  // Native only example\n        webgpu: false, // Native only example\n    },\n    ExampleDesc {\n        name: \"boids\",\n        function: wgpu_examples::boids::main,\n        webgl: false, // No compute\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"bunnymark\",\n        function: wgpu_examples::bunnymark::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"conservative_raster\",\n        function: wgpu_examples::conservative_raster::main,\n        webgl: false,  // No conservative raster\n        webgpu: false, // No conservative raster\n    },\n    ExampleDesc {\n        name: \"cooperative_matrix\",\n        function: wgpu_examples::cooperative_matrix::main,\n        webgl: false,  // No cooperative matrix support\n        webgpu: false, // No cooperative matrix support\n    },\n    ExampleDesc {\n        name: \"cube\",\n        function: wgpu_examples::cube::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"hello_synchronization\",\n        function: wgpu_examples::hello_synchronization::main,\n        webgl: false, // No canvas for WebGL\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"hello_triangle\",\n        function: wgpu_examples::hello_triangle::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"hello_windows\",\n        function: wgpu_examples::hello_windows::main,\n        webgl: false,  // Native only example\n        webgpu: false, // Native only example\n    },\n    ExampleDesc {\n        name: \"hello_workgroups\",\n        function: wgpu_examples::hello_workgroups::main,\n        webgl: false,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"mipmap\",\n        function: wgpu_examples::mipmap::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"msaa_line\",\n        function: wgpu_examples::msaa_line::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"multiple_render_targets\",\n        function: wgpu_examples::multiple_render_targets::main,\n        webgl: false,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"render_to_texture\",\n        function: wgpu_examples::render_to_texture::main,\n        webgl: false, // No canvas for WebGL\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"render_with_compute\",\n        function: wgpu_examples::render_with_compute::main,\n        webgl: false,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"repeated_compute\",\n        function: wgpu_examples::repeated_compute::main,\n        webgl: false, // No compute\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"shadow\",\n        function: wgpu_examples::shadow::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"skybox\",\n        function: wgpu_examples::skybox::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"srgb_blend\",\n        function: wgpu_examples::srgb_blend::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"stencil_triangles\",\n        function: wgpu_examples::stencil_triangles::main,\n        webgl: true,\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"storage_texture\",\n        function: wgpu_examples::storage_texture::main,\n        webgl: false, // No storage textures\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"texture_arrays\",\n        function: wgpu_examples::texture_arrays::main,\n        webgl: false,  // No texture arrays\n        webgpu: false, // No texture arrays\n    },\n    ExampleDesc {\n        name: \"timestamp_queries\",\n        function: wgpu_examples::timestamp_queries::main,\n        webgl: false,  // No canvas for WebGL\n        webgpu: false, // No timestamp queries\n    },\n    ExampleDesc {\n        name: \"uniform_values\",\n        function: wgpu_examples::uniform_values::main,\n        webgl: false, // No compute\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"water\",\n        function: wgpu_examples::water::main,\n        webgl: false, // No RODS\n        webgpu: true,\n    },\n    ExampleDesc {\n        name: \"ray_cube_compute\",\n        function: wgpu_examples::ray_cube_compute::main,\n        webgl: false,  // No Ray-tracing extensions\n        webgpu: false, // No Ray-tracing extensions (yet)\n    },\n    ExampleDesc {\n        name: \"ray_cube_fragment\",\n        function: wgpu_examples::ray_cube_fragment::main,\n        webgl: false,  // No Ray-tracing extensions\n        webgpu: false, // No Ray-tracing extensions (yet)\n    },\n    ExampleDesc {\n        name: \"ray_scene\",\n        function: wgpu_examples::ray_scene::main,\n        webgl: false,  // No Ray-tracing extensions\n        webgpu: false, // No Ray-tracing extensions (yet)\n    },\n    ExampleDesc {\n        name: \"ray_shadows\",\n        function: wgpu_examples::ray_shadows::main,\n        webgl: false,  // No Ray-tracing extensions\n        webgpu: false, // No Ray-tracing extensions (yet)\n    },\n    ExampleDesc {\n        name: \"ray_traced_triangle\",\n        function: wgpu_examples::ray_traced_triangle::main,\n        webgl: false,\n        webgpu: false,\n    },\n    ExampleDesc {\n        name: \"ray_cube_normals\",\n        function: wgpu_examples::ray_cube_normals::main,\n        webgl: false,  // No Ray-tracing extensions\n        webgpu: false, // No Ray-tracing extensions (yet)\n    },\n    ExampleDesc {\n        name: \"mesh_shader\",\n        function: wgpu_examples::mesh_shader::main,\n        webgl: false,\n        webgpu: false,\n    },\n    ExampleDesc {\n        name: \"multiview\",\n        function: wgpu_examples::multiview::main,\n        webgl: false,\n        webgpu: false,\n    },\n];\n\nfn get_example_name() -> Option<String> {\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            let query_string = web_sys::window()?.location().search().ok()?;\n\n            wgpu_examples::framework::parse_url_query_string(&query_string, \"example\").map(String::from)\n        } else {\n            std::env::args().nth(1)\n        }\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn print_examples() {\n    // Get the document, header, and body elements.\n    let document = web_sys::window().unwrap().document().unwrap();\n\n    for backend in [\"webgl2\", \"webgpu\"] {\n        let ul = document\n            .get_element_by_id(&format!(\"{backend}-list\"))\n            .unwrap();\n\n        for example in EXAMPLES {\n            if backend == \"webgl2\" && !example.webgl {\n                continue;\n            }\n            if backend == \"webgpu\" && !example.webgpu {\n                continue;\n            }\n\n            let link = document.create_element(\"a\").unwrap();\n            link.set_text_content(Some(example.name));\n            link.set_attribute(\n                \"href\",\n                &format!(\"?backend={backend}&example={}\", example.name),\n            )\n            .unwrap();\n            link.set_class_name(\"example-link\");\n\n            let item = document.create_element(\"div\").unwrap();\n            item.append_child(&link).unwrap();\n            item.set_class_name(\"example-item\");\n            ul.append_child(&item).unwrap();\n        }\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn print_unknown_example(_result: Option<String>) {}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn print_unknown_example(result: Option<String>) {\n    if let Some(example) = result {\n        println!(\"Unknown example: {example}\");\n    } else {\n        println!(\"Please specify an example as the first argument!\");\n    }\n\n    println!(\"\\nAvailable Examples:\");\n    for examples in EXAMPLES {\n        println!(\"\\t{}\", examples.name);\n    }\n}\n\nfn main() {\n    #[cfg(target_arch = \"wasm32\")]\n    print_examples();\n\n    let Some(example) = get_example_name() else {\n        print_unknown_example(None);\n        return;\n    };\n\n    let Some(found) = EXAMPLES.iter().find(|e| e.name == example) else {\n        print_unknown_example(Some(example));\n        return;\n    };\n\n    (found.function)();\n}\n"
  },
  {
    "path": "examples/features/src/mesh_shader/README.md",
    "content": "# mesh_shader\n\nThis example renders a triangle to a window with mesh shaders, while showcasing most mesh shader related features(task shaders, payloads, per primitive data).\n\n## To Run\n\n```\ncargo run --bin wgpu-examples mesh_shader\n```"
  },
  {
    "path": "examples/features/src/mesh_shader/mod.rs",
    "content": "// Same as in mesh shader tests\nfn compile_wgsl(device: &wgpu::Device) -> wgpu::ShaderModule {\n    // Workgroup memory zero initialization can be expensive for mesh shaders\n    unsafe {\n        device.create_shader_module_trusted(\n            wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(include_str!(\"shader.wgsl\").into()),\n            },\n            wgpu::ShaderRuntimeChecks::unchecked(),\n        )\n    }\n}\nfn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::ShaderModule {\n    let out_path = format!(\n        \"{}/src/mesh_shader/shader.{stage_str}.cso\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n    let cmd = std::process::Command::new(\"dxc\")\n        .args([\n            \"-T\",\n            &format!(\"{stage_str}_6_5\"),\n            \"-E\",\n            entry,\n            &format!(\"{}/src/mesh_shader/shader.hlsl\", env!(\"CARGO_MANIFEST_DIR\")),\n            \"-Fo\",\n            &out_path,\n        ])\n        .output()\n        .unwrap();\n    if !cmd.status.success() {\n        panic!(\"DXC failed:\\n{}\", String::from_utf8(cmd.stderr).unwrap());\n    }\n    let file = std::fs::read(&out_path).unwrap();\n    std::fs::remove_file(out_path).unwrap();\n    unsafe {\n        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n            label: None,\n            num_workgroups: (1, 1, 1),\n            dxil: Some(std::borrow::Cow::Owned(file)),\n            ..Default::default()\n        })\n    }\n}\n\nfn compile_msl(device: &wgpu::Device) -> wgpu::ShaderModule {\n    unsafe {\n        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n            label: None,\n            msl: Some(std::borrow::Cow::Borrowed(include_str!(\"shader.metal\"))),\n            num_workgroups: (1, 1, 1),\n            ..Default::default()\n        })\n    }\n}\n\nstruct Shaders {\n    ts: wgpu::ShaderModule,\n    ms: wgpu::ShaderModule,\n    fs: wgpu::ShaderModule,\n    ts_name: &'static str,\n    ms_name: &'static str,\n    fs_name: &'static str,\n}\n\nfn get_shaders(device: &wgpu::Device, backend: wgpu::Backend) -> Shaders {\n    // In the case that the platform does support mesh shaders, the dummy\n    // shader is used to avoid requiring PASSTHROUGH_SHADERS.\n    match backend {\n        wgpu::Backend::Vulkan => {\n            let compiled = compile_wgsl(device);\n            Shaders {\n                ts: compiled.clone(),\n                ms: compiled.clone(),\n                fs: compiled.clone(),\n                ts_name: \"ts_main\",\n                ms_name: \"ms_main\",\n                fs_name: \"fs_main\",\n            }\n        }\n        wgpu::Backend::Dx12 => Shaders {\n            ts: compile_hlsl(device, \"Task\", \"as\"),\n            ms: compile_hlsl(device, \"Mesh\", \"ms\"),\n            fs: compile_hlsl(device, \"Frag\", \"ps\"),\n            ts_name: \"main\",\n            ms_name: \"main\",\n            fs_name: \"main\",\n        },\n        wgpu::Backend::Metal => {\n            let compiled = compile_msl(device);\n            Shaders {\n                ts: compiled.clone(),\n                ms: compiled.clone(),\n                fs: compiled.clone(),\n                ts_name: \"taskShader\",\n                ms_name: \"meshShader\",\n                fs_name: \"fragShader\",\n            }\n        }\n        _ => unreachable!(),\n    }\n}\n\npub struct Example {\n    pipeline: wgpu::RenderPipeline,\n}\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let Shaders {\n            ts,\n            ms,\n            fs,\n            ts_name,\n            ms_name,\n            fs_name,\n        } = get_shaders(device, adapter.get_info().backend);\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[],\n            immediate_size: 0,\n        });\n        let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            task: Some(wgpu::TaskState {\n                module: &ts,\n                entry_point: Some(ts_name),\n                compilation_options: Default::default(),\n            }),\n            mesh: wgpu::MeshState {\n                module: &ms,\n                entry_point: Some(ms_name),\n                compilation_options: Default::default(),\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &fs,\n                entry_point: Some(fs_name),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                cull_mode: Some(wgpu::Face::Back),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: Default::default(),\n            multiview: None,\n            cache: None,\n        });\n        Self { pipeline }\n    }\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                    depth_slice: None,\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.push_debug_group(\"Prepare data for draw.\");\n            rpass.set_pipeline(&self.pipeline);\n            rpass.pop_debug_group();\n            rpass.insert_debug_marker(\"Draw!\");\n            rpass.draw_mesh_tasks(1, 1, 1);\n        }\n        queue.submit(Some(encoder.finish()));\n    }\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        Default::default()\n    }\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::PASSTHROUGH_SHADERS\n    }\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values()\n    }\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        // empty\n    }\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        // empty\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"mesh_shader\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"mesh_shader\",\n    image_path: \"/examples/features/src/mesh_shader/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        .features(wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::PASSTHROUGH_SHADERS)\n        .instance_flags(wgpu::InstanceFlags::advanced_debugging())\n        .limits(wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values())\n        .skip(wgpu_test::FailureCase {\n            backends: None,\n            // Skip Mesa because LLVMPIPE has what is believed to be a driver bug\n            vendor: Some(0x10005),\n            adapter: None,\n            driver: None,\n            reasons: vec![],\n            behavior: wgpu_test::FailureBehavior::Ignore,\n        }),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.005)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/mesh_shader/shader.hlsl",
    "content": "struct OutVertex {\n    float4 Position : SV_POSITION;\n    float4 Color: COLOR;\n};\nstruct OutPrimitive {\n    float4 ColorMask : COLOR_MASK : PRIMITIVE;\n    bool CullPrimitive: SV_CullPrimitive;\n};\nstruct InVertex {\n    float4 Color: COLOR;\n};\nstruct InPrimitive {\n    float4 ColorMask : COLOR_MASK : PRIMITIVE;\n};\nstruct PayloadData {\n    float4 ColorMask;\n    bool Visible;\n};\n\n\nstatic 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)};\nstatic const float4 colors[3] = {float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.)};\n\ngroupshared PayloadData outPayload;\n\n[numthreads(1, 1, 1)]\nvoid Task() {\n    outPayload.ColorMask = float4(1.0, 1.0, 0.0, 1.0);\n    outPayload.Visible = true;\n    DispatchMesh(3, 1, 1, outPayload);\n}\n\n[outputtopology(\"triangle\")]\n[numthreads(1, 1, 1)]\nvoid Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], out primitives OutPrimitive primitives[1], in payload PayloadData payload) {\n    SetMeshOutputCounts(3, 1);\n\n    vertices[0].Position = positions[0];\n    vertices[1].Position = positions[1];\n    vertices[2].Position = positions[2];\n    \n    vertices[0].Color = colors[0] * payload.ColorMask;\n    vertices[1].Color = colors[1] * payload.ColorMask;\n    vertices[2].Color = colors[2] * payload.ColorMask;\n\n    triangles[0] = uint3(0, 1, 2);\n    primitives[0].ColorMask = float4(1.0, 0.0, 0.0, 1.0);\n    primitives[0].CullPrimitive = !payload.Visible;\n}\n\nfloat4 Frag(InVertex vertex, InPrimitive primitive) : SV_Target  {\n    return vertex.Color * primitive.ColorMask;\n}\n"
  },
  {
    "path": "examples/features/src/mesh_shader/shader.metal",
    "content": "using namespace metal;\n\nstruct OutVertex {\n    float4 Position [[position]];\n    float4 Color [[user(locn0)]];\n};\n\nstruct OutPrimitive {\n    float4 ColorMask [[flat]] [[user(locn1)]];\n    bool CullPrimitive [[primitive_culled]];\n};\n\nstruct InVertex {\n};\n\nstruct InPrimitive {\n    float4 ColorMask [[flat]] [[user(locn1)]];\n};\n\nstruct FragmentIn {\n    float4 Color [[user(locn0)]];\n    float4 ColorMask [[flat]] [[user(locn1)]];\n};\n\nstruct PayloadData {\n    float4 ColorMask;\n    bool Visible;\n};\n\nusing Meshlet = metal::mesh<OutVertex, OutPrimitive, 3, 1, topology::triangle>;\n\n\nconstant float4 positions[3] = {\n    float4(0.0, 1.0, 0.0, 1.0),\n    float4(-1.0, -1.0, 0.0, 1.0),\n    float4(1.0, -1.0, 0.0, 1.0)\n};\n\nconstant float4 colors[3] = {\n    float4(0.0, 1.0, 0.0, 1.0),\n    float4(0.0, 0.0, 1.0, 1.0),\n    float4(1.0, 0.0, 0.0, 1.0)\n};\n\n\n[[object]]\nvoid taskShader(uint3 tid [[thread_position_in_grid]], object_data PayloadData &outPayload [[payload]], mesh_grid_properties grid) {\n    outPayload.ColorMask = float4(1.0, 1.0, 0.0, 1.0);\n    outPayload.Visible = true;\n    grid.set_threadgroups_per_grid(uint3(3, 1, 1));\n}\n\n[[mesh]]\nvoid meshShader(\n    object_data PayloadData const& payload [[payload]],\n    Meshlet out\n)\n{\n    out.set_primitive_count(1);\n\n    for(int i = 0;i < 3;i++) {\n        OutVertex vert;\n        vert.Position = positions[i];\n        vert.Color = colors[i] * payload.ColorMask;\n        out.set_vertex(i, vert);\n        out.set_index(i, i);\n    }\n\n    OutPrimitive prim;\n    prim.ColorMask = float4(1.0, 0.0, 0.0, 1.0);\n    prim.CullPrimitive = !payload.Visible;\n    out.set_primitive(0, prim);\n}\n\nfragment float4 fragShader(FragmentIn data [[stage_in]]) {\n    return data.Color * data.ColorMask;\n}\n"
  },
  {
    "path": "examples/features/src/mesh_shader/shader.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nconst positions = array(\n    vec4(0., 1., 0., 1.),\n    vec4(-1., -1., 0., 1.),\n    vec4(1., -1., 0., 1.)\n);\nconst colors = array(\n    vec4(0., 1., 0., 1.),\n    vec4(0., 0., 1., 1.),\n    vec4(1., 0., 0., 1.)\n);\n\nstruct TaskPayload {\n    colorMask: vec4<f32>,\n    visible: bool,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) color: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n    @builtin(cull_primitive) cull: bool,\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\nstruct PrimitiveInput {\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> workgroupData: f32;\n\n@task\n@payload(taskPayload)\n@workgroup_size(64)\nfn ts_main(@builtin(local_invocation_id) thread_id: vec3<u32>) -> @builtin(mesh_task_size) vec3<u32> {\n    if thread_id.x == 0 {\n        workgroupData = 1.0;\n        taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n        taskPayload.visible = true;\n        return vec3(1, 1, 1);\n    }\n    return vec3(0, 0, 0);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(64)\nfn ms_main(@builtin(local_invocation_id) thread_id: vec3<u32>) {\n    if thread_id.x == 0 {\n        mesh_output.vertex_count = 3;\n        mesh_output.primitive_count = 1;\n        workgroupData = 2.0;\n\n        mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n        mesh_output.primitives[0].cull = !taskPayload.visible;\n        mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n    }\n    if thread_id.x < 3 {\n        mesh_output.vertices[thread_id.x].position = positions[thread_id.x];\n        mesh_output.vertices[thread_id.x].color = colors[thread_id.x] * taskPayload.colorMask;\n    }\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput, primitive: PrimitiveInput) -> @location(0) vec4<f32> {\n    return vertex.color * primitive.colorMask;\n}\n"
  },
  {
    "path": "examples/features/src/mipmap/README.md",
    "content": "# mipmap\n\nThis example shows how to generate and make use of mipmaps.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples mipmap\n```\n\n## Screenshots\n\n![Mip maps](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/mipmap/blit.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    . \n// |              * .\n// |***************\n// |            . 1,-1 \n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/mipmap/draw.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\nstruct Locals {\n    transform: mat4x4<f32>\n};\n@group(0)\n@binding(0)\nvar<uniform> r_data: Locals;\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    let pos = vec2<f32>(\n        100.0 * (1.0 - f32(vertex_index & 2u)),\n        1000.0 * f32(vertex_index & 1u)\n    );\n    var result: VertexOutput;\n    result.tex_coords = 0.05 * pos + vec2<f32>(0.5, 0.5);\n    result.position = r_data.transform * vec4<f32>(pos, 0.0, 1.0);\n    return result;\n}\n\n@group(0)\n@binding(1)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(2)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/mipmap/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse std::f32::consts;\nuse wgpu::util::DeviceExt;\n\nconst TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;\nconst MIP_LEVEL_COUNT: u32 = 10;\nconst MIP_PASS_COUNT: u32 = MIP_LEVEL_COUNT - 1;\n\nconst QUERY_FEATURES: wgpu::Features = {\n    wgpu::Features::TIMESTAMP_QUERY\n        .union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES)\n        .union(wgpu::Features::PIPELINE_STATISTICS_QUERY)\n};\n\nfn create_texels(size: usize, cx: f32, cy: f32) -> Vec<u8> {\n    use std::iter;\n\n    (0..size * size)\n        .flat_map(|id| {\n            // get high five for recognizing this ;)\n            let mut x = 4.0 * (id % size) as f32 / (size - 1) as f32 - 2.0;\n            let mut y = 2.0 * (id / size) as f32 / (size - 1) as f32 - 1.0;\n            let mut count = 0;\n            while count < 0xFF && x * x + y * y < 4.0 {\n                let old_x = x;\n                x = x * x - y * y + cx;\n                y = 2.0 * old_x * y + cy;\n                count += 1;\n            }\n            iter::once(0xFF - (count * 2) as u8)\n                .chain(iter::once(0xFF - (count * 5) as u8))\n                .chain(iter::once(0xFF - (count * 13) as u8))\n                .chain(iter::once(u8::MAX))\n        })\n        .collect()\n}\n\nstruct QuerySets {\n    timestamp: wgpu::QuerySet,\n    timestamp_period: f32,\n    pipeline_statistics: wgpu::QuerySet,\n    data_buffer: wgpu::Buffer,\n    mapping_buffer: wgpu::Buffer,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct TimestampData {\n    start: u64,\n    end: u64,\n}\n\ntype TimestampQueries = [TimestampData; MIP_PASS_COUNT as usize];\ntype PipelineStatisticsQueries = [u64; MIP_PASS_COUNT as usize];\n\nfn pipeline_statistics_offset() -> wgpu::BufferAddress {\n    (size_of::<TimestampQueries>() as wgpu::BufferAddress).max(wgpu::QUERY_RESOLVE_BUFFER_ALIGNMENT)\n}\n\nstruct Example {\n    bind_group: wgpu::BindGroup,\n    uniform_buf: wgpu::Buffer,\n    draw_pipeline: wgpu::RenderPipeline,\n}\n\nimpl Example {\n    fn generate_matrix(aspect_ratio: f32) -> glam::Mat4 {\n        let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 1.0, 1000.0);\n        let view = glam::Mat4::look_at_rh(\n            glam::Vec3::new(0f32, 0.0, 10.0),\n            glam::Vec3::new(0f32, 50.0, 0.0),\n            glam::Vec3::Z,\n        );\n        projection * view\n    }\n\n    fn generate_mipmaps(\n        encoder: &mut wgpu::CommandEncoder,\n        device: &wgpu::Device,\n        texture: &wgpu::Texture,\n        query_sets: &Option<QuerySets>,\n        mip_count: u32,\n    ) {\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"blit.wgsl\"));\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"blit\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(TEXTURE_FORMAT.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let bind_group_layout = pipeline.get_bind_group_layout(0);\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"mip\"),\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let views = (0..mip_count)\n            .map(|mip| {\n                texture.create_view(&wgpu::TextureViewDescriptor {\n                    label: Some(\"mip\"),\n                    format: None,\n                    dimension: None,\n                    usage: None,\n                    aspect: wgpu::TextureAspect::All,\n                    base_mip_level: mip,\n                    mip_level_count: Some(1),\n                    base_array_layer: 0,\n                    array_layer_count: None,\n                })\n            })\n            .collect::<Vec<_>>();\n\n        for target_mip in 1..mip_count as usize {\n            let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n                layout: &bind_group_layout,\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: wgpu::BindingResource::Sampler(&sampler),\n                    },\n                ],\n                label: None,\n            });\n\n            let pipeline_query_index_base = target_mip as u32 - 1;\n            let timestamp_query_index_base = (target_mip as u32 - 1) * 2;\n\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &views[target_mip],\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            if let Some(ref query_sets) = query_sets {\n                rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base);\n                rpass.begin_pipeline_statistics_query(\n                    &query_sets.pipeline_statistics,\n                    pipeline_query_index_base,\n                );\n            }\n            rpass.set_pipeline(&pipeline);\n            rpass.set_bind_group(0, &bind_group, &[]);\n            rpass.draw(0..3, 0..1);\n            if let Some(ref query_sets) = query_sets {\n                rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base + 1);\n                rpass.end_pipeline_statistics_query();\n            }\n        }\n\n        if let Some(ref query_sets) = query_sets {\n            let timestamp_query_count = MIP_PASS_COUNT * 2;\n            encoder.resolve_query_set(\n                &query_sets.timestamp,\n                0..timestamp_query_count,\n                &query_sets.data_buffer,\n                0,\n            );\n            encoder.resolve_query_set(\n                &query_sets.pipeline_statistics,\n                0..MIP_PASS_COUNT,\n                &query_sets.data_buffer,\n                pipeline_statistics_offset(),\n            );\n        }\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        QUERY_FEATURES\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let mut init_encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        // Create the texture\n        let size = 1 << MIP_PASS_COUNT;\n        let texels = create_texels(size as usize, -0.8, 0.156);\n        let texture_extent = wgpu::Extent3d {\n            width: size,\n            height: size,\n            depth_or_array_layers: 1,\n        };\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: texture_extent,\n            mip_level_count: MIP_LEVEL_COUNT,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: TEXTURE_FORMAT,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::RENDER_ATTACHMENT\n                | wgpu::TextureUsages::COPY_DST,\n            label: None,\n            view_formats: &[],\n        });\n        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n        //Note: we could use queue.write_texture instead, and this is what other\n        // examples do, but here we want to show another way to do this.\n        let temp_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Temporary Buffer\"),\n            contents: texels.as_slice(),\n            usage: wgpu::BufferUsages::COPY_SRC,\n        });\n        init_encoder.copy_buffer_to_texture(\n            wgpu::TexelCopyBufferInfo {\n                buffer: &temp_buf,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(4 * size),\n                    rows_per_image: None,\n                },\n            },\n            texture.as_image_copy(),\n            texture_extent,\n        );\n\n        // Create other resources\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: None,\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Linear,\n            ..Default::default()\n        });\n        let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n        let mx_ref: &[f32; 16] = mx_total.as_ref();\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(mx_ref),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        // Create the render pipeline\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"draw.wgsl\"));\n\n        let draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"draw\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: Some(wgpu::Face::Back),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        // Create bind group\n        let bind_group_layout = draw_pipeline.get_bind_group_layout(0);\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n            label: None,\n        });\n\n        // If both kinds of query are supported, use queries\n        let query_sets = if device.features().contains(QUERY_FEATURES) {\n            // For N total mips, it takes N - 1 passes to generate them, and we're measuring those.\n            let mip_passes = MIP_LEVEL_COUNT - 1;\n\n            // Create the timestamp query set. We need twice as many queries as we have passes,\n            // as we need a query at the beginning and at the end of the operation.\n            let timestamp = device.create_query_set(&wgpu::QuerySetDescriptor {\n                label: None,\n                count: mip_passes * 2,\n                ty: wgpu::QueryType::Timestamp,\n            });\n            // Timestamp queries use an device-specific timestamp unit. We need to figure out how many\n            // nanoseconds go by for the timestamp to be incremented by one. The period is this value.\n            let timestamp_period = queue.get_timestamp_period();\n\n            // We only need one pipeline statistics query per pass.\n            let pipeline_statistics = device.create_query_set(&wgpu::QuerySetDescriptor {\n                label: None,\n                count: mip_passes,\n                ty: wgpu::QueryType::PipelineStatistics(\n                    wgpu::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS,\n                ),\n            });\n\n            // This databuffer has to store all of the query results, 2 * passes timestamp queries\n            // and 1 * passes statistics queries. Each query returns a u64 value.\n            let buffer_size = pipeline_statistics_offset()\n                + size_of::<PipelineStatisticsQueries>() as wgpu::BufferAddress;\n            let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(\"query buffer\"),\n                size: buffer_size,\n                usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,\n                mapped_at_creation: false,\n            });\n\n            // Mapping buffer\n            let mapping_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(\"query buffer\"),\n                size: buffer_size,\n                usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n                mapped_at_creation: false,\n            });\n\n            Some(QuerySets {\n                timestamp,\n                timestamp_period,\n                pipeline_statistics,\n                data_buffer,\n                mapping_buffer,\n            })\n        } else {\n            None\n        };\n\n        Self::generate_mipmaps(\n            &mut init_encoder,\n            device,\n            &texture,\n            &query_sets,\n            MIP_LEVEL_COUNT,\n        );\n\n        if let Some(ref query_sets) = query_sets {\n            init_encoder.copy_buffer_to_buffer(\n                &query_sets.data_buffer,\n                0,\n                &query_sets.mapping_buffer,\n                0,\n                query_sets.data_buffer.size(),\n            );\n        }\n\n        queue.submit(Some(init_encoder.finish()));\n        if let Some(ref query_sets) = query_sets {\n            // We can ignore the callback as we're about to wait for the device.\n            query_sets\n                .mapping_buffer\n                .slice(..)\n                .map_async(wgpu::MapMode::Read, |_| ());\n            // Wait for device to be done rendering mipmaps\n            device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n            // This is guaranteed to be ready.\n            let timestamp_view = query_sets\n                .mapping_buffer\n                .slice(..size_of::<TimestampQueries>() as wgpu::BufferAddress)\n                .get_mapped_range();\n            let pipeline_stats_view = query_sets\n                .mapping_buffer\n                .slice(pipeline_statistics_offset()..)\n                .get_mapped_range();\n            // Convert the raw data into a useful structure\n            let timestamp_data: &TimestampQueries = bytemuck::from_bytes(&timestamp_view);\n            let pipeline_stats_data: &PipelineStatisticsQueries =\n                bytemuck::from_bytes(&pipeline_stats_view);\n            // Iterate over the data\n            for (idx, (timestamp, pipeline)) in timestamp_data\n                .iter()\n                .zip(pipeline_stats_data.iter())\n                .enumerate()\n            {\n                // Figure out the timestamp differences and multiply by the period to get nanoseconds\n                let nanoseconds =\n                    (timestamp.end - timestamp.start) as f32 * query_sets.timestamp_period;\n                // Nanoseconds is a bit small, so lets use microseconds.\n                let microseconds = nanoseconds / 1000.0;\n                // Print the data!\n                println!(\n                    \"Generating mip level {} took {:.3} μs and called the fragment shader {} times\",\n                    idx + 1,\n                    microseconds,\n                    pipeline\n                );\n            }\n        }\n\n        Example {\n            bind_group,\n            uniform_buf,\n            draw_pipeline,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n        let mx_ref: &[f32; 16] = mx_total.as_ref();\n        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(mx_ref));\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let clear_color = wgpu::Color {\n                r: 0.1,\n                g: 0.2,\n                b: 0.3,\n                a: 1.0,\n            };\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(clear_color),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.set_pipeline(&self.draw_pipeline);\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.draw(0..4, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"mipmap\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"mipmap\",\n    image_path: \"/examples/features/src/mipmap/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_QUERY: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"mipmap-query\",\n    image_path: \"/examples/features/src/mipmap/screenshot_query.png\",\n    width: 1024,\n    height: 768,\n    optional_features: QUERY_FEATURES,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    // Somehow, this test on CI lavapipe reasonably often gets error of 0.025341, significantly higher\n    // than the comparison we usually do with mean 0.005. This only happens when the query is used.\n    comparisons: &[\n        wgpu_test::ComparisonType::Mean(0.03),\n        wgpu_test::ComparisonType::Percentile {\n            percentile: 0.99,\n            threshold: 0.1,\n        },\n    ],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/msaa_line/README.md",
    "content": "# msaa_line\n\nThis example shows how to render lines using MSAA.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples msaa_line\n```\n\n## Screenshots\n\n![MSAA line](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/msaa_line/mod.rs",
    "content": "//! The parts of this example enabling MSAA are:\n//! *    The render pipeline is created with a sample_count > 1.\n//! *    A new texture with a sample_count > 1 is created and set as the color_attachment instead of the swapchain.\n//! *    The swapchain is now specified as a resolve_target.\n//!\n//! The parts of this example enabling LineList are:\n//! *   Set the primitive_topology to PrimitiveTopology::LineList.\n//! *   Vertices and Indices describe the two points that make up a line.\n\nuse std::iter;\n\nuse bytemuck::{Pod, Zeroable};\nuse wgpu::util::DeviceExt;\n\nuse winit::{\n    event::{ElementState, KeyEvent, WindowEvent},\n    keyboard::{Key, NamedKey},\n};\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 2],\n    _color: [f32; 4],\n}\n\nstruct Example {\n    bundle: wgpu::RenderBundle,\n    shader: wgpu::ShaderModule,\n    pipeline_layout: wgpu::PipelineLayout,\n    multisampled_framebuffer: wgpu::TextureView,\n    vertex_buffer: wgpu::Buffer,\n    vertex_count: u32,\n    sample_count: u32,\n    rebuild_bundle: bool,\n    config: wgpu::SurfaceConfiguration,\n    max_sample_count: u32,\n}\n\nimpl Example {\n    fn create_bundle(\n        device: &wgpu::Device,\n        config: &wgpu::SurfaceConfiguration,\n        shader: &wgpu::ShaderModule,\n        pipeline_layout: &wgpu::PipelineLayout,\n        sample_count: u32,\n        vertex_buffer: &wgpu::Buffer,\n        vertex_count: u32,\n    ) -> wgpu::RenderBundle {\n        log::info!(\"sample_count: {sample_count}\");\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: size_of::<Vertex>() as wgpu::BufferAddress,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x4],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::LineList,\n                front_face: wgpu::FrontFace::Ccw,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState {\n                count: sample_count,\n                ..Default::default()\n            },\n            multiview_mask: None,\n            cache: None,\n        });\n        let mut encoder =\n            device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {\n                label: None,\n                color_formats: &[Some(config.view_formats[0])],\n                depth_stencil: None,\n                sample_count,\n                multiview: None,\n            });\n        encoder.set_pipeline(&pipeline);\n        encoder.set_vertex_buffer(0, vertex_buffer.slice(..));\n        encoder.draw(0..vertex_count, 0..1);\n        encoder.finish(&wgpu::RenderBundleDescriptor {\n            label: Some(\"main\"),\n        })\n    }\n\n    fn create_multisampled_framebuffer(\n        device: &wgpu::Device,\n        config: &wgpu::SurfaceConfiguration,\n        sample_count: u32,\n    ) -> wgpu::TextureView {\n        let multisampled_texture_extent = wgpu::Extent3d {\n            width: config.width,\n            height: config.height,\n            depth_or_array_layers: 1,\n        };\n        let multisampled_frame_descriptor = &wgpu::TextureDescriptor {\n            size: multisampled_texture_extent,\n            mip_level_count: 1,\n            sample_count,\n            dimension: wgpu::TextureDimension::D2,\n            format: config.view_formats[0],\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,\n            label: None,\n            view_formats: &[],\n        };\n\n        device\n            .create_texture(multisampled_frame_descriptor)\n            .create_view(&wgpu::TextureViewDescriptor::default())\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        log::info!(\"Press left/right arrow keys to change sample_count.\");\n\n        let sample_flags = _adapter\n            .get_texture_format_features(config.view_formats[0])\n            .flags;\n\n        let max_sample_count = {\n            if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X16) {\n                16\n            } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X8) {\n                8\n            } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X4) {\n                4\n            } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X2) {\n                2\n            } else {\n                1\n            }\n        };\n\n        let sample_count = max_sample_count;\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[],\n            immediate_size: 0,\n        });\n\n        let multisampled_framebuffer =\n            Example::create_multisampled_framebuffer(device, config, sample_count);\n\n        let mut vertex_data = vec![];\n\n        let max = 50;\n        for i in 0..max {\n            let percent = i as f32 / max as f32;\n            let (sin, cos) = (percent * 2.0 * std::f32::consts::PI).sin_cos();\n            vertex_data.push(Vertex {\n                _pos: [0.0, 0.0],\n                _color: [1.0, -sin, cos, 1.0],\n            });\n            vertex_data.push(Vertex {\n                _pos: [1.0 * cos, 1.0 * sin],\n                _color: [sin, -cos, 1.0, 1.0],\n            });\n        }\n\n        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n        let vertex_count = vertex_data.len() as u32;\n\n        let bundle = Example::create_bundle(\n            device,\n            config,\n            &shader,\n            &pipeline_layout,\n            sample_count,\n            &vertex_buffer,\n            vertex_count,\n        );\n\n        Example {\n            bundle,\n            shader,\n            pipeline_layout,\n            multisampled_framebuffer,\n            vertex_buffer,\n            vertex_count,\n            sample_count,\n            max_sample_count,\n            rebuild_bundle: false,\n            config: config.clone(),\n        }\n    }\n\n    #[expect(clippy::single_match)]\n    fn update(&mut self, event: winit::event::WindowEvent) {\n        match event {\n            WindowEvent::KeyboardInput {\n                event:\n                    KeyEvent {\n                        logical_key,\n                        state: ElementState::Pressed,\n                        ..\n                    },\n                ..\n            } => match logical_key {\n                // TODO: Switch back to full scans of possible options when we expose\n                //       supported sample counts to the user.\n                Key::Named(NamedKey::ArrowLeft) => {\n                    if self.sample_count == self.max_sample_count {\n                        self.sample_count = 1;\n                        self.rebuild_bundle = true;\n                    }\n                }\n                Key::Named(NamedKey::ArrowRight) => {\n                    if self.sample_count == 1 {\n                        self.sample_count = self.max_sample_count;\n                        self.rebuild_bundle = true;\n                    }\n                }\n                _ => {}\n            },\n            _ => {}\n        }\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        self.config = config.clone();\n        self.multisampled_framebuffer =\n            Example::create_multisampled_framebuffer(device, config, self.sample_count);\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        if self.rebuild_bundle {\n            self.bundle = Example::create_bundle(\n                device,\n                &self.config,\n                &self.shader,\n                &self.pipeline_layout,\n                self.sample_count,\n                &self.vertex_buffer,\n                self.vertex_count,\n            );\n            self.multisampled_framebuffer =\n                Example::create_multisampled_framebuffer(device, &self.config, self.sample_count);\n            self.rebuild_bundle = false;\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let rpass_color_attachment = if self.sample_count == 1 {\n                wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Store,\n                    },\n                }\n            } else {\n                wgpu::RenderPassColorAttachment {\n                    view: &self.multisampled_framebuffer,\n                    depth_slice: None,\n                    resolve_target: Some(view),\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        // Storing pre-resolve MSAA data is unnecessary if it isn't used later.\n                        // On tile-based GPU, avoid store can reduce your app's memory footprint.\n                        store: wgpu::StoreOp::Discard,\n                    },\n                }\n            };\n\n            encoder\n                .begin_render_pass(&wgpu::RenderPassDescriptor {\n                    label: None,\n                    color_attachments: &[Some(rpass_color_attachment)],\n                    depth_stencil_attachment: None,\n                    timestamp_writes: None,\n                    occlusion_query_set: None,\n                    multiview_mask: None,\n                })\n                .execute_bundles(iter::once(&self.bundle));\n        }\n\n        queue.submit(iter::once(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"msaa-line\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"msaa-line\",\n    image_path: \"/examples/features/src/msaa_line/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    // There's a lot of natural variance so we check the weighted median too to differentiate\n    // real failures from variance.\n    comparisons: &[\n        wgpu_test::ComparisonType::Mean(0.065),\n        wgpu_test::ComparisonType::Percentile {\n            percentile: 0.5,\n            threshold: 0.29,\n        },\n    ],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/msaa_line/shader.wgsl",
    "content": "struct VertexOutput {\n    @location(0) color: vec4<f32>,\n    @builtin(position) position: vec4<f32>,\n};\n\n@vertex\nfn vs_main(\n    @location(0) position: vec2<f32>,\n    @location(1) color: vec4<f32>,\n) -> VertexOutput {\n    var result: VertexOutput;\n    result.position = vec4<f32>(position, 0.0, 1.0);\n    result.color = color;\n    return result;\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return vertex.color;\n}\n"
  },
  {
    "path": "examples/features/src/multiple_render_targets/README.md",
    "content": "# multiple_render_targets\n\nThis example demonstrates how to use fragment shader to output to two color attachments of a renderpass simultaneously. \n\nThe program generates a black and white ball-shaped texture from scratch and uses the fragment shader to draw it to two\nseparate texture targets. The first texture target receives a red version of the texture and the second target receives\na green version. \n\nOnce the colored shapes have been drawn to two separate textures, they\nwill be displayed on the screen by rendering each one of them to their own viewports that split the screen in half.\n\n\n## To Run\n\n```\ncargo run --bin wgpu-examples multiple_render_targets\n```\n\n## Screenshots\n\n![Multi render target](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/multiple_render_targets/mod.rs",
    "content": "const EXAMPLE_NAME: &str = \"multiple_render_targets\";\n\n/// Renderer that draws its outputs to two output texture targets at the same time.\nstruct MultiTargetRenderer {\n    pipeline: wgpu::RenderPipeline,\n    bindgroup: wgpu::BindGroup,\n}\n\nfn create_ball_texture_data(width: usize, height: usize) -> Vec<u8> {\n    // Creates black and white pixel data for the texture to sample.\n    let mut img_data = Vec::with_capacity(width * height);\n    let center: glam::Vec2 = glam::Vec2::new(width as f32 * 0.5, height as f32 * 0.5);\n    let half_distance = width as f32 * 0.5;\n    for y in 0..width {\n        for x in 0..height {\n            let cur_pos = glam::Vec2::new(x as f32, y as f32);\n            let distance_to_center_normalized = 1.0 - (cur_pos - center).length() / half_distance;\n            let val: u8 = (u8::MAX as f32 * distance_to_center_normalized) as u8;\n            img_data.push(val)\n        }\n    }\n    img_data\n}\n\nimpl MultiTargetRenderer {\n    fn create_image_texture(\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> (wgpu::Texture, wgpu::TextureView) {\n        const WIDTH: usize = 256;\n        const HEIGHT: usize = 256;\n\n        let size = wgpu::Extent3d {\n            width: WIDTH as u32,\n            height: HEIGHT as u32,\n            depth_or_array_layers: 1,\n        };\n\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"data texture\"),\n            size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R8Unorm, // we need only the red channel for black/white image,\n            usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let ball_texture_data = &create_ball_texture_data(WIDTH, HEIGHT);\n\n        queue.write_texture(\n            wgpu::TexelCopyTextureInfo {\n                aspect: wgpu::TextureAspect::All,\n                texture: &texture,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n            },\n            ball_texture_data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(WIDTH as u32),\n                rows_per_image: Some(HEIGHT as u32),\n            },\n            size,\n        );\n\n        let view = texture.create_view(&wgpu::TextureViewDescriptor {\n            label: Some(\"view\"),\n            format: None,\n            dimension: Some(wgpu::TextureViewDimension::D2),\n            usage: None,\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: None,\n        });\n\n        (texture, view)\n    }\n\n    fn init(\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        shader: &wgpu::ShaderModule,\n        target_states: &[Option<wgpu::ColorTargetState>],\n    ) -> MultiTargetRenderer {\n        let texture_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            multisampled: false,\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    },\n                ],\n                label: None,\n            });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&texture_bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let (_, texture_view) = Self::create_image_texture(device, queue);\n\n        let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &texture_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n            label: None,\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: shader,\n                entry_point: Some(\"fs_multi_main\"),\n                // IMPORTANT: specify the color states for the outputs:\n                compilation_options: Default::default(),\n                targets: target_states,\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        Self {\n            pipeline,\n            bindgroup,\n        }\n    }\n\n    fn draw(\n        &self,\n        encoder: &mut wgpu::CommandEncoder,\n        targets: &[Option<wgpu::RenderPassColorAttachment>],\n    ) {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: targets,\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &self.bindgroup, &[]);\n        rpass.draw(0..3, 0..1);\n    }\n}\n\n/// Renderer that displays results on the screen.\nstruct TargetRenderer {\n    pipeline: wgpu::RenderPipeline,\n    bindgroup_layout: wgpu::BindGroupLayout,\n    bindgroup_left: wgpu::BindGroup,\n    bindgroup_right: wgpu::BindGroup,\n    sampler: wgpu::Sampler,\n}\n\nimpl TargetRenderer {\n    fn init(\n        device: &wgpu::Device,\n        shader: &wgpu::ShaderModule,\n        format: wgpu::TextureFormat,\n        targets: &TextureTargets,\n    ) -> TargetRenderer {\n        let texture_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            multisampled: false,\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    },\n                ],\n                label: None,\n            });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&texture_bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: shader,\n                entry_point: Some(\"fs_display_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format,\n                    blend: None,\n                    write_mask: Default::default(),\n                })],\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let (bg_left, bg_right) =\n            Self::create_bindgroups(device, &texture_bind_group_layout, targets, &sampler);\n        Self {\n            pipeline: render_pipeline,\n            bindgroup_layout: texture_bind_group_layout,\n            bindgroup_left: bg_left,\n            bindgroup_right: bg_right,\n            sampler,\n        }\n    }\n    fn create_bindgroups(\n        device: &wgpu::Device,\n        layout: &wgpu::BindGroupLayout,\n        texture_targets: &TextureTargets,\n        sampler: &wgpu::Sampler,\n    ) -> (wgpu::BindGroup, wgpu::BindGroup) {\n        let left = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&texture_targets.red_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(sampler),\n                },\n            ],\n            label: None,\n        });\n\n        let right = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&texture_targets.green_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(sampler),\n                },\n            ],\n            label: None,\n        });\n        (left, right)\n    }\n\n    fn draw(\n        &self,\n        encoder: &mut wgpu::CommandEncoder,\n        surface_view: &wgpu::TextureView,\n        width: u32,\n        height: u32,\n    ) {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: surface_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &self.bindgroup_left, &[]);\n\n        let height = height as f32;\n        let half_w = width as f32 * 0.5;\n\n        // draw results in two separate viewports that split the screen:\n\n        rpass.set_viewport(0.0, 0.0, half_w, height, 0.0, 1.0);\n        rpass.draw(0..3, 0..1);\n\n        rpass.set_viewport(half_w, 0.0, half_w, height, 0.0, 1.0);\n        rpass.set_bind_group(0, &self.bindgroup_right, &[]);\n        rpass.draw(0..3, 0..1);\n    }\n\n    fn rebuild_resources(&mut self, device: &wgpu::Device, texture_targets: &TextureTargets) {\n        (self.bindgroup_left, self.bindgroup_right) = Self::create_bindgroups(\n            device,\n            &self.bindgroup_layout,\n            texture_targets,\n            &self.sampler,\n        )\n    }\n}\n\nstruct TextureTargets {\n    red_view: wgpu::TextureView,\n    green_view: wgpu::TextureView,\n}\n\nimpl TextureTargets {\n    fn new(\n        device: &wgpu::Device,\n        format: wgpu::TextureFormat,\n        width: u32,\n        height: u32,\n    ) -> TextureTargets {\n        let size = wgpu::Extent3d {\n            width,\n            height,\n            depth_or_array_layers: 1,\n        };\n\n        let red_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format,\n            usage: wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[format],\n        });\n        let green_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format,\n            usage: wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[format],\n        });\n        let red_view = red_texture.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(format),\n            dimension: Some(wgpu::TextureViewDimension::D2),\n            ..wgpu::TextureViewDescriptor::default()\n        });\n        let green_view = green_texture.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(format),\n            dimension: Some(wgpu::TextureViewDimension::D2),\n            ..wgpu::TextureViewDescriptor::default()\n        });\n        TextureTargets {\n            red_view,\n            green_view,\n        }\n    }\n}\n\nstruct Example {\n    drawer: TargetRenderer,\n    multi_target_renderer: MultiTargetRenderer,\n    texture_targets: TextureTargets,\n    screen_width: u32,\n    screen_height: u32,\n}\n\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(\n                \"shader.wgsl\"\n            ))),\n        });\n        // Renderer that draws to 2 textures at the same time:\n        let multi_target_renderer = MultiTargetRenderer::init(\n            device,\n            queue,\n            &shader,\n            // ColorTargetStates specify how the data will be written to the\n            // output textures:\n            &[\n                Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: None,\n                    write_mask: Default::default(),\n                }),\n                Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: None,\n                    write_mask: Default::default(),\n                }),\n            ],\n        );\n\n        // create our target textures that will receive the simultaneous rendering:\n        let texture_targets =\n            TextureTargets::new(device, config.view_formats[0], config.width, config.height);\n\n        // helper renderer that displays the results in 2 separate viewports:\n        let drawer =\n            TargetRenderer::init(device, &shader, config.view_formats[0], &texture_targets);\n\n        Self {\n            texture_targets,\n            multi_target_renderer,\n            drawer,\n            screen_width: config.width,\n            screen_height: config.height,\n        }\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        self.screen_width = config.width;\n        self.screen_height = config.height;\n        self.texture_targets =\n            TextureTargets::new(device, config.view_formats[0], config.width, config.height);\n        self.drawer.rebuild_resources(device, &self.texture_targets);\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        // draw to 2 textures at the same time:\n        self.multi_target_renderer.draw(\n            &mut encoder,\n            &[\n                Some(wgpu::RenderPassColorAttachment {\n                    view: &self.texture_targets.red_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: Default::default(),\n                }),\n                Some(wgpu::RenderPassColorAttachment {\n                    view: &self.texture_targets.green_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: Default::default(),\n                }),\n            ],\n        );\n\n        // display results of the both drawn textures on screen:\n        self.drawer\n            .draw(&mut encoder, view, self.screen_width, self.screen_height);\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(EXAMPLE_NAME);\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: EXAMPLE_NAME,\n    image_path: \"/examples/features/src/multiple_render_targets/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    // Bounded by lavapipe\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.014)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/multiple_render_targets/shader.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) uv: vec2<f32>,\n};\n\n@vertex\nfn vs_main( @builtin(vertex_index) vi: u32) -> VertexOutput {\n    var out: VertexOutput;\n    out.uv = vec2<f32>(\n        f32((vi << 1u) & 2u),\n        f32(vi & 2u),\n    );\n    out.clip_position = vec4<f32>(out.uv * 2.0 - 1.0, 0.0, 1.0);\n    out.uv.y = 1.0 - out.uv.y;\n    return out;\n}\n\n@group(0)\n@binding(0)\nvar image_texture: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar image_sampler: sampler;\n\nstruct FragmentOutput {\n  @location(0) red_target : vec4<f32>,\n  @location(1) green_target : vec4<f32>,\n}\n\n@fragment\nfn fs_multi_main(vs: VertexOutput) -> FragmentOutput {\n    let smp = textureSample(image_texture, image_sampler, vs.uv).x;\n\n    var output: FragmentOutput;\n    output.red_target = vec4<f32>(smp, 0.0, 0.0, 1.0);\n    output.green_target = vec4<f32>(0.0, smp, 0.0, 1.0);\n    return output;\n}\n\n@fragment\nfn fs_display_main(vs: VertexOutput) -> @location(0) vec4<f32> {\n    let smp = textureSample(image_texture, image_sampler, vs.uv).xyz;\n    return vec4<f32>(smp, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/multiview/mod.rs",
    "content": "//! Renders different content to different layers of an array texture using multiview,\n//! a feature commonly used for VR rendering.\n\nuse std::{num::NonZero, time::Instant};\n\nuse wgpu::util::TextureBlitter;\n\nconst TEXTURE_SIZE: u32 = 512;\n\n// Change this to demonstrate non-contiguous multiview functionality\nconst LAYER_MASK: u32 = 0b11;\n\nconst NUM_LAYERS: u32 = 32 - LAYER_MASK.leading_zeros();\n\npub struct Example {\n    pipeline: wgpu::RenderPipeline,\n    entire_texture_view: wgpu::TextureView,\n    views: Vec<wgpu::TextureView>,\n    start_time: Instant,\n    blitter: TextureBlitter,\n}\n\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let shader_src = include_str!(\"shader.wgsl\");\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            vertex: wgpu::VertexState {\n                buffers: &[],\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: NonZero::new(LAYER_MASK),\n            multisample: Default::default(),\n            layout: None,\n            depth_stencil: None,\n            cache: None,\n        });\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: TEXTURE_SIZE,\n                height: TEXTURE_SIZE,\n                depth_or_array_layers: NUM_LAYERS,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT\n                | wgpu::TextureUsages::COPY_SRC\n                | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n        let entire_texture_view = texture.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            format: Some(wgpu::TextureFormat::Rgba8Unorm),\n            dimension: Some(wgpu::TextureViewDimension::D2Array),\n            usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: Some(NUM_LAYERS),\n        });\n        let mut views = Vec::new();\n        for i in 0..NUM_LAYERS {\n            views.push(texture.create_view(&wgpu::TextureViewDescriptor {\n                label: None,\n                format: Some(wgpu::TextureFormat::Rgba8Unorm),\n                dimension: Some(wgpu::TextureViewDimension::D2),\n                usage: Some(wgpu::TextureUsages::TEXTURE_BINDING),\n                aspect: wgpu::TextureAspect::All,\n                base_mip_level: 0,\n                mip_level_count: None,\n                base_array_layer: i,\n                array_layer_count: Some(1),\n            }));\n        }\n        let blitter = wgpu::util::TextureBlitter::new(device, config.format);\n        Self {\n            pipeline,\n            entire_texture_view,\n            views,\n            blitter,\n            start_time: Instant::now(),\n        }\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &self.entire_texture_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.02,\n                            g: 0.02,\n                            b: 0.02,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: NonZero::new(LAYER_MASK),\n            });\n            rpass.set_pipeline(&self.pipeline);\n            rpass.draw(0..6, 0..1);\n        }\n\n        let layer = (Instant::now() - self.start_time).as_secs() % NUM_LAYERS as u64;\n        self.blitter\n            .copy(device, &mut encoder, &self.views[layer as usize], view);\n        queue.submit(Some(encoder.finish()));\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        Default::default()\n    }\n\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::MULTIVIEW\n            | if !(LAYER_MASK + 1).is_power_of_two() {\n                wgpu::Features::SELECTIVE_MULTIVIEW\n            } else {\n                wgpu::Features::empty()\n            }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits {\n            max_multiview_view_count: NUM_LAYERS,\n            ..Default::default()\n        }\n    }\n\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        // empty\n    }\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        // empty\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"multiview\");\n}\n"
  },
  {
    "path": "examples/features/src/multiview/shader.wgsl",
    "content": "const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {\n    return vec4f(triangles[vertex_index], 0.0, 1.0);\n}\n\n@fragment\nfn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f {\n    return vec4f(f32(view_index) * 0.25 + 0.125, 1.0 - f32(view_index) * 0.25, 1.0 - 0.5 * f32(view_index), 1.0);\n}"
  },
  {
    "path": "examples/features/src/ray_cube_compute/README.md",
    "content": "# ray-cube\n\nThis example renders a ray traced cube with hardware acceleration.\nA separate compute shader is used to perform the ray queries. \n\n## To Run\n\n```\ncargo run --bin wgpu-examples ray_cube_compute\n```\n\n## Screenshots\n\n![Cube example](screenshot.png)\n"
  },
  {
    "path": "examples/features/src/ray_cube_compute/blit.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    . \n// |              * .\n// |***************\n// |            . 1,-1 \n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/ray_cube_compute/mod.rs",
    "content": "use std::{borrow::Cow, iter, mem};\n\nuse bytemuck::{Pod, Zeroable};\nuse glam::{Affine3A, Mat4, Quat, Vec3};\nuse wgpu::util::DeviceExt;\n\nuse wgpu::StoreOp;\n\nuse crate::utils;\n\n// from cube\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n    _tex_coord: [f32; 2],\n}\n\nfn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n    }\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0]),\n        vertex([1, -1, 1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([-1, 1, 1], [0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [1, 0]),\n        vertex([1, 1, -1], [0, 0]),\n        vertex([1, -1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [0, 0]),\n        vertex([1, 1, -1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([1, -1, 1], [0, 1]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, 1, 1], [0, 0]),\n        vertex([-1, 1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [1, 0]),\n        vertex([-1, 1, -1], [0, 0]),\n        vertex([-1, 1, 1], [0, 1]),\n        vertex([1, 1, 1], [1, 1]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, 0]),\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, -1, -1], [1, 1]),\n        vertex([1, -1, -1], [0, 1]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n}\n\n#[inline]\nfn affine_to_rows(mat: &Affine3A) -> [f32; 12] {\n    let row_0 = mat.matrix3.row(0);\n    let row_1 = mat.matrix3.row(1);\n    let row_2 = mat.matrix3.row(2);\n    let translation = mat.translation;\n    [\n        row_0.x,\n        row_0.y,\n        row_0.z,\n        translation.x,\n        row_1.x,\n        row_1.y,\n        row_1.z,\n        translation.y,\n        row_2.x,\n        row_2.y,\n        row_2.z,\n        translation.z,\n    ]\n}\n\nstruct Example {\n    rt_target: wgpu::Texture,\n    #[expect(dead_code)]\n    rt_view: wgpu::TextureView,\n    #[expect(dead_code)]\n    sampler: wgpu::Sampler,\n    #[expect(dead_code)]\n    uniform_buf: wgpu::Buffer,\n    #[expect(dead_code)]\n    vertex_buf: wgpu::Buffer,\n    #[expect(dead_code)]\n    index_buf: wgpu::Buffer,\n    tlas: wgpu::Tlas,\n    compute_pipeline: wgpu::ComputePipeline,\n    compute_bind_group: wgpu::BindGroup,\n    blit_pipeline: wgpu::RenderPipeline,\n    blit_bind_group: wgpu::BindGroup,\n    animation_timer: utils::AnimationTimer,\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::TEXTURE_BINDING_ARRAY\n            | wgpu::Features::VERTEX_WRITABLE_STORAGE\n            | wgpu::Features::EXPERIMENTAL_RAY_QUERY\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let side_count = 8;\n\n        let rt_target = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"rt_target\"),\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::STORAGE_BINDING,\n            view_formats: &[wgpu::TextureFormat::Rgba8Unorm],\n        });\n\n        let rt_view = rt_target.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            format: Some(wgpu::TextureFormat::Rgba8Unorm),\n            dimension: Some(wgpu::TextureViewDimension::D2),\n            usage: None,\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: None,\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"rt_sampler\"),\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let uniforms = {\n            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);\n            let proj = Mat4::perspective_rh(\n                59.0_f32.to_radians(),\n                config.width as f32 / config.height as f32,\n                0.001,\n                1000.0,\n            );\n\n            Uniforms {\n                view_inverse: view.inverse(),\n                proj_inverse: proj.inverse(),\n            }\n        };\n\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: wgpu::BufferUsages::UNIFORM,\n        });\n\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n            vertex_format: wgpu::VertexFormat::Float32x3,\n            vertex_count: vertex_data.len() as u32,\n            index_format: Some(wgpu::IndexFormat::Uint16),\n            index_count: Some(index_data.len() as u32),\n            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n        };\n\n        let blas = device.create_blas(\n            &wgpu::CreateBlasDescriptor {\n                label: None,\n                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            },\n            wgpu::BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_geo_size_desc.clone()],\n            },\n        );\n\n        let mut tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            max_instances: side_count * side_count,\n        });\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"rt_computer\"),\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n        });\n\n        let blit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"blit\"),\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"blit.wgsl\"))),\n        });\n\n        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"rt\"),\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        let compute_bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n\n        let compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &compute_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&rt_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::AccelerationStructure(&tlas),\n                },\n            ],\n        });\n\n        let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"blit\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &blit_shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &blit_shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let blit_bind_group_layout = blit_pipeline.get_bind_group_layout(0);\n\n        let blit_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &blit_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&rt_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        let dist = 3.0;\n\n        for x in 0..side_count {\n            for y in 0..side_count {\n                tlas[(x + y * side_count) as usize] = Some(wgpu::TlasInstance::new(\n                    &blas,\n                    affine_to_rows(&Affine3A::from_rotation_translation(\n                        Quat::from_rotation_y(45.9_f32.to_radians()),\n                        Vec3 {\n                            x: x as f32 * dist,\n                            y: y as f32 * dist,\n                            z: -30.0,\n                        },\n                    )),\n                    0,\n                    0xff,\n                ));\n            }\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(\n            iter::once(&wgpu::BlasBuildEntry {\n                blas: &blas,\n                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![\n                    wgpu::BlasTriangleGeometry {\n                        size: &blas_geo_size_desc,\n                        vertex_buffer: &vertex_buf,\n                        first_vertex: 0,\n                        vertex_stride: mem::size_of::<Vertex>() as u64,\n                        index_buffer: Some(&index_buf),\n                        first_index: Some(0),\n                        transform_buffer: None,\n                        transform_buffer_offset: None,\n                    },\n                ]),\n            }),\n            iter::once(&tlas),\n        );\n\n        queue.submit(Some(encoder.finish()));\n\n        Example {\n            rt_target,\n            rt_view,\n            sampler,\n            uniform_buf,\n            vertex_buf,\n            index_buf,\n            tlas,\n            compute_pipeline,\n            compute_bind_group,\n            blit_pipeline,\n            blit_bind_group,\n            animation_timer: utils::AnimationTimer::default(),\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let anim_time = self.animation_timer.time();\n\n        self.tlas[0].as_mut().unwrap().transform =\n            affine_to_rows(&Affine3A::from_rotation_translation(\n                Quat::from_euler(\n                    glam::EulerRot::XYZ,\n                    anim_time * 0.342,\n                    anim_time * 0.254,\n                    anim_time * 0.832,\n                ),\n                Vec3 {\n                    x: 0.0,\n                    y: 0.0,\n                    z: -6.0,\n                },\n            ));\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));\n\n        {\n            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&self.compute_pipeline);\n            cpass.set_bind_group(0, Some(&self.compute_bind_group), &[]);\n            cpass.dispatch_workgroups(self.rt_target.width() / 8, self.rt_target.height() / 8, 1);\n        }\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                        store: StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.blit_pipeline);\n            rpass.set_bind_group(0, Some(&self.blit_bind_group), &[]);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray-cube\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_cube_compute\",\n    image_path: \"/examples/features/src/ray_cube_compute/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        // https://github.com/gfx-rs/wgpu/issues/9100\n        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_cube_compute/shader.wgsl",
    "content": "/*\nThe contents of the RayQuery struct are roughly as follows\nlet RAY_FLAG_NONE = 0x00u;\nlet RAY_FLAG_OPAQUE = 0x01u;\nlet RAY_FLAG_NO_OPAQUE = 0x02u;\nlet RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u;\nlet RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u;\nlet RAY_FLAG_CULL_BACK_FACING = 0x10u;\nlet RAY_FLAG_CULL_FRONT_FACING = 0x20u;\nlet RAY_FLAG_CULL_OPAQUE = 0x40u;\nlet RAY_FLAG_CULL_NO_OPAQUE = 0x80u;\nlet RAY_FLAG_SKIP_TRIANGLES = 0x100u;\nlet RAY_FLAG_SKIP_AABBS = 0x200u;\n\nlet RAY_QUERY_INTERSECTION_NONE = 0u;\nlet RAY_QUERY_INTERSECTION_TRIANGLE = 1u;\nlet RAY_QUERY_INTERSECTION_GENERATED = 2u;\nlet RAY_QUERY_INTERSECTION_AABB = 3u;\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    t_min: f32,\n    t_max: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    kind: u32,\n    t: f32,\n    instance_custom_data: u32,\n    instance_index: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: bool,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n*/\nenable wgpu_ray_query;\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n};\n\n@group(0) @binding(0)\nvar output: texture_storage_2d<rgba8unorm, write>;\n\n@group(0) @binding(1)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(2)\nvar acc_struct: acceleration_structure;\n\n@compute @workgroup_size(8, 8)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    let target_size = textureDimensions(output);\n    var color =  vec4<f32>(vec2<f32>(global_id.xy) / vec2<f32>(target_size), 0.0, 1.0);\n\n\tlet pixel_center = vec2<f32>(global_id.xy) + vec2<f32>(0.5);\n\tlet in_uv = pixel_center/vec2<f32>(target_size.xy);\n\tlet d = in_uv * 2.0 - 1.0;\n\n\tlet origin = (uniforms.view_inv * vec4<f32>(0.0,0.0,0.0,1.0)).xyz;\n\tlet temp = uniforms.proj_inv * vec4<f32>(d.x, d.y, 1.0, 1.0);\n\tlet direction = (uniforms.view_inv * vec4<f32>(normalize(temp.xyz), 0.0)).xyz;\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) {\n        color = vec4<f32>(intersection.barycentrics, 1.0 - intersection.barycentrics.x - intersection.barycentrics.y, 1.0);\n    }\n\n    textureStore(output, global_id.xy, color);\n}\n"
  },
  {
    "path": "examples/features/src/ray_cube_fragment/README.md",
    "content": "# ray-cube\n\nThis example renders a ray traced cube with hardware acceleration.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples ray_cube_fragment\n```\n\n## Screenshots\n\n![Cube example](screenshot.png)\n"
  },
  {
    "path": "examples/features/src/ray_cube_fragment/mod.rs",
    "content": "use crate::utils;\nuse bytemuck::{Pod, Zeroable};\nuse glam::{Mat4, Quat, Vec3};\nuse std::ops::IndexMut;\nuse std::{borrow::Cow, iter, mem};\nuse wgpu::util::DeviceExt;\n\n// from cube\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n    _tex_coord: [f32; 2],\n}\n\nfn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n    }\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0]),\n        vertex([1, -1, 1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([-1, 1, 1], [0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [1, 0]),\n        vertex([1, 1, -1], [0, 0]),\n        vertex([1, -1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [0, 0]),\n        vertex([1, 1, -1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([1, -1, 1], [0, 1]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, 1, 1], [0, 0]),\n        vertex([-1, 1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [1, 0]),\n        vertex([-1, 1, -1], [0, 0]),\n        vertex([-1, 1, 1], [0, 1]),\n        vertex([1, 1, 1], [1, 1]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, 0]),\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, -1, -1], [1, 1]),\n        vertex([1, -1, -1], [0, 1]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n}\n\nstruct Example {\n    uniforms: Uniforms,\n    uniform_buf: wgpu::Buffer,\n    blas: wgpu::Blas,\n    tlas: wgpu::Tlas,\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    animation_timer: utils::AnimationTimer,\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_RAY_QUERY\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let side_count = 8;\n\n        let uniforms = {\n            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);\n            let proj = Mat4::perspective_rh(\n                59.0_f32.to_radians(),\n                config.width as f32 / config.height as f32,\n                0.001,\n                1000.0,\n            );\n\n            Uniforms {\n                view_inverse: view.inverse(),\n                proj_inverse: proj.inverse(),\n            }\n        };\n\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n            vertex_format: wgpu::VertexFormat::Float32x3,\n            vertex_count: vertex_data.len() as u32,\n            index_format: Some(wgpu::IndexFormat::Uint16),\n            index_count: Some(index_data.len() as u32),\n            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n        };\n\n        let blas = device.create_blas(\n            &wgpu::CreateBlasDescriptor {\n                label: None,\n                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            },\n            wgpu::BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_geo_size_desc.clone()],\n            },\n        );\n\n        let tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            max_instances: side_count * side_count,\n        });\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let bind_group_layout = pipeline.get_bind_group_layout(0);\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::AccelerationStructure(&tlas),\n                },\n            ],\n        });\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(\n            iter::once(&wgpu::BlasBuildEntry {\n                blas: &blas,\n                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![\n                    wgpu::BlasTriangleGeometry {\n                        size: &blas_geo_size_desc,\n                        vertex_buffer: &vertex_buf,\n                        first_vertex: 0,\n                        vertex_stride: mem::size_of::<Vertex>() as u64,\n                        index_buffer: Some(&index_buf),\n                        first_index: Some(0),\n                        transform_buffer: None,\n                        transform_buffer_offset: None,\n                    },\n                ]),\n            }),\n            // iter::empty(),\n            iter::once(&tlas),\n        );\n\n        queue.submit(Some(encoder.finish()));\n\n        Example {\n            uniforms,\n            uniform_buf,\n            blas,\n            tlas,\n            pipeline,\n            bind_group,\n            animation_timer: utils::AnimationTimer::default(),\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        let proj = Mat4::perspective_rh(\n            59.0_f32.to_radians(),\n            config.width as f32 / config.height as f32,\n            0.001,\n            1000.0,\n        );\n\n        self.uniforms.proj_inverse = proj.inverse();\n\n        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        // scene update\n        {\n            let dist = 12.0;\n\n            let side_count = 8;\n\n            let anim_time = self.animation_timer.time();\n\n            for x in 0..side_count {\n                for y in 0..side_count {\n                    let instance = self.tlas.index_mut((x + y * side_count) as usize);\n\n                    let x = x as f32 / (side_count - 1) as f32;\n                    let y = y as f32 / (side_count - 1) as f32;\n                    let x = x * 2.0 - 1.0;\n                    let y = y * 2.0 - 1.0;\n\n                    let transform = Mat4::from_rotation_translation(\n                        Quat::from_euler(\n                            glam::EulerRot::XYZ,\n                            anim_time * 0.5 * 0.342,\n                            anim_time * 0.5 * 0.254,\n                            anim_time * 0.5 * 0.832,\n                        ),\n                        Vec3 {\n                            x: x * dist,\n                            y: y * dist,\n                            z: -24.0,\n                        },\n                    );\n                    let transform = transform.transpose().to_cols_array()[..12]\n                        .try_into()\n                        .unwrap();\n\n                    *instance = Some(wgpu::TlasInstance::new(&self.blas, transform, 0, 0xff));\n                }\n            }\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, Some(&self.bind_group), &[]);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray-cube\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_cube_fragment\",\n    image_path: \"/examples/features/src/ray_cube_fragment/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        // https://github.com/gfx-rs/wgpu/issues/9100\n        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_cube_fragment/shader.wgsl",
    "content": "enable wgpu_ray_query;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    . \n// |              * .\n// |***************\n// |            . 1,-1 \n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(1)\nvar acc_struct: acceleration_structure;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n\n    var color =  vec4<f32>(vertex.tex_coords, 0.0, 1.0);\n\n\tlet d = vertex.tex_coords * 2.0 - 1.0;\n\n\tlet origin = (uniforms.view_inv * vec4<f32>(0.0,0.0,0.0,1.0)).xyz;\n\tlet temp = uniforms.proj_inv * vec4<f32>(d.x, d.y, 1.0, 1.0);\n\tlet direction = (uniforms.view_inv * vec4<f32>(normalize(temp.xyz), 0.0)).xyz;\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) {\n        color = vec4<f32>(intersection.barycentrics, 1.0 - intersection.barycentrics.x - intersection.barycentrics.y, 1.0);\n    }\n\n    return color;\n}\n"
  },
  {
    "path": "examples/features/src/ray_cube_normals/README.md",
    "content": "# ray-cube\n\nThis example renders a ray traced cube with hardware acceleration.\nA separate compute shader is used to perform the ray queries. \n\n## To Run\n\n```\ncargo run --bin wgpu-examples ray_cube_normals\n```\n\n## Screenshots\n\n![Cube example](screenshot.png)\n"
  },
  {
    "path": "examples/features/src/ray_cube_normals/blit.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    . \n// |              * .\n// |***************\n// |            . 1,-1 \n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/ray_cube_normals/mod.rs",
    "content": "use std::{borrow::Cow, iter, mem};\n\nuse bytemuck::{Pod, Zeroable};\nuse glam::{Affine3A, Mat4, Quat, Vec3};\nuse wgpu::util::DeviceExt;\n\nuse crate::utils;\nuse wgpu::StoreOp;\n\n// from cube\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n    _tex_coord: [f32; 2],\n}\n\nfn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n    }\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0]),\n        vertex([1, -1, 1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([-1, 1, 1], [0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [1, 0]),\n        vertex([1, 1, -1], [0, 0]),\n        vertex([1, -1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [0, 0]),\n        vertex([1, 1, -1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([1, -1, 1], [0, 1]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, 1, 1], [0, 0]),\n        vertex([-1, 1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [1, 0]),\n        vertex([-1, 1, -1], [0, 0]),\n        vertex([-1, 1, 1], [0, 1]),\n        vertex([1, 1, 1], [1, 1]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, 0]),\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, -1, -1], [1, 1]),\n        vertex([1, -1, -1], [0, 1]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n}\n\n#[inline]\nfn affine_to_rows(mat: &Affine3A) -> [f32; 12] {\n    let row_0 = mat.matrix3.row(0);\n    let row_1 = mat.matrix3.row(1);\n    let row_2 = mat.matrix3.row(2);\n    let translation = mat.translation;\n    [\n        row_0.x,\n        row_0.y,\n        row_0.z,\n        translation.x,\n        row_1.x,\n        row_1.y,\n        row_1.z,\n        translation.y,\n        row_2.x,\n        row_2.y,\n        row_2.z,\n        translation.z,\n    ]\n}\n\nstruct Example {\n    rt_target: wgpu::Texture,\n    tlas: wgpu::Tlas,\n    compute_pipeline: wgpu::ComputePipeline,\n    compute_bind_group: wgpu::BindGroup,\n    blit_pipeline: wgpu::RenderPipeline,\n    blit_bind_group: wgpu::BindGroup,\n    animation_timer: utils::AnimationTimer,\n}\n\nimpl crate::framework::Example for Example {\n    // Don't want srgb, so normals show up better.\n    const SRGB: bool = false;\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let side_count = 8;\n\n        let rt_target = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"rt_target\"),\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::STORAGE_BINDING,\n            view_formats: &[wgpu::TextureFormat::Rgba8Unorm],\n        });\n\n        let rt_view = rt_target.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            format: Some(wgpu::TextureFormat::Rgba8Unorm),\n            dimension: Some(wgpu::TextureViewDimension::D2),\n            usage: None,\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: None,\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"rt_sampler\"),\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let uniforms = {\n            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);\n            let proj = Mat4::perspective_rh(\n                59.0_f32.to_radians(),\n                config.width as f32 / config.height as f32,\n                0.001,\n                1000.0,\n            );\n\n            Uniforms {\n                view_inverse: view.inverse(),\n                proj_inverse: proj.inverse(),\n            }\n        };\n\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: wgpu::BufferUsages::UNIFORM,\n        });\n\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n            vertex_format: wgpu::VertexFormat::Float32x3,\n            vertex_count: vertex_data.len() as u32,\n            index_format: Some(wgpu::IndexFormat::Uint16),\n            index_count: Some(index_data.len() as u32),\n            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n        };\n\n        let blas = device.create_blas(\n            &wgpu::CreateBlasDescriptor {\n                label: None,\n                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE\n                    | wgpu::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,\n                update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            },\n            wgpu::BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_geo_size_desc.clone()],\n            },\n        );\n\n        let mut tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE\n                | wgpu::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            max_instances: side_count * side_count,\n        });\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"rt_computer\"),\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n        });\n\n        let blit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"blit\"),\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"blit.wgsl\"))),\n        });\n\n        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"rt\"),\n            layout: None,\n            module: &shader,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        let compute_bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n\n        let compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &compute_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&rt_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::AccelerationStructure(&tlas),\n                },\n            ],\n        });\n\n        let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"blit\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &blit_shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &blit_shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let blit_bind_group_layout = blit_pipeline.get_bind_group_layout(0);\n\n        let blit_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &blit_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&rt_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        let dist = 3.0;\n\n        for x in 0..side_count {\n            for y in 0..side_count {\n                tlas[(x + y * side_count) as usize] = Some(wgpu::TlasInstance::new(\n                    &blas,\n                    affine_to_rows(&Affine3A::from_rotation_translation(\n                        Quat::from_rotation_y(45.9_f32.to_radians()),\n                        Vec3 {\n                            x: x as f32 * dist,\n                            y: y as f32 * dist,\n                            z: -30.0,\n                        },\n                    )),\n                    0,\n                    0xff,\n                ));\n            }\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(\n            iter::once(&wgpu::BlasBuildEntry {\n                blas: &blas,\n                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![\n                    wgpu::BlasTriangleGeometry {\n                        size: &blas_geo_size_desc,\n                        vertex_buffer: &vertex_buf,\n                        first_vertex: 0,\n                        vertex_stride: mem::size_of::<Vertex>() as u64,\n                        index_buffer: Some(&index_buf),\n                        first_index: Some(0),\n                        transform_buffer: None,\n                        transform_buffer_offset: None,\n                    },\n                ]),\n            }),\n            iter::once(&tlas),\n        );\n\n        queue.submit(Some(encoder.finish()));\n\n        Example {\n            rt_target,\n            tlas,\n            compute_pipeline,\n            compute_bind_group,\n            blit_pipeline,\n            blit_bind_group,\n            animation_timer: utils::AnimationTimer::default(),\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let anim_time = self.animation_timer.time();\n\n        self.tlas[0].as_mut().unwrap().transform =\n            affine_to_rows(&Affine3A::from_rotation_translation(\n                Quat::from_euler(\n                    glam::EulerRot::XYZ,\n                    anim_time * 0.342,\n                    anim_time * 0.254,\n                    anim_time * 0.832,\n                ),\n                Vec3 {\n                    x: 0.0,\n                    y: 0.0,\n                    z: -6.0,\n                },\n            ));\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));\n\n        {\n            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&self.compute_pipeline);\n            cpass.set_bind_group(0, Some(&self.compute_bind_group), &[]);\n            cpass.dispatch_workgroups(self.rt_target.width() / 8, self.rt_target.height() / 8, 1);\n        }\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                        store: StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.blit_pipeline);\n            rpass.set_bind_group(0, Some(&self.blit_bind_group), &[]);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray-cube\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_cube_normals\",\n    image_path: \"/examples/features/src/ray_cube_normals/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default().expect_fail(\n        // RADV does this fine.\n        wgpu_test::FailureCase {\n            backends: Some(wgpu::Backends::VULKAN),\n            adapter: Some(\"AMD\"),\n            driver: Some(\"AMD proprietary driver\"),\n            ..wgpu_test::FailureCase::default()\n        }\n        .panic(\"Image data mismatch\"),\n    ),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_cube_normals/shader.wgsl",
    "content": "/*\nlet RAY_FLAG_NONE = 0x00u;\nlet RAY_FLAG_OPAQUE = 0x01u;\nlet RAY_FLAG_NO_OPAQUE = 0x02u;\nlet RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u;\nlet RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u;\nlet RAY_FLAG_CULL_BACK_FACING = 0x10u;\nlet RAY_FLAG_CULL_FRONT_FACING = 0x20u;\nlet RAY_FLAG_CULL_OPAQUE = 0x40u;\nlet RAY_FLAG_CULL_NO_OPAQUE = 0x80u;\nlet RAY_FLAG_SKIP_TRIANGLES = 0x100u;\nlet RAY_FLAG_SKIP_AABBS = 0x200u;\n\nlet RAY_QUERY_INTERSECTION_NONE = 0u;\nlet RAY_QUERY_INTERSECTION_TRIANGLE = 1u;\nlet RAY_QUERY_INTERSECTION_GENERATED = 2u;\nlet RAY_QUERY_INTERSECTION_AABB = 4u;\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    t_min: f32,\n    t_max: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    kind: u32,\n    t: f32,\n    instance_custom_index: u32,\n    instance_id: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: bool,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n*/\nenable wgpu_ray_query;\nenable wgpu_ray_query_vertex_return;\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n};\n\n@group(0) @binding(0)\nvar output: texture_storage_2d<rgba8unorm, write>;\n\n@group(0) @binding(1)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(2)\nvar acc_struct: acceleration_structure<vertex_return>;\n\n@compute @workgroup_size(8, 8)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    let target_size = textureDimensions(output);\n    var color =  vec4<f32>(vec2<f32>(global_id.xy) / vec2<f32>(target_size), 0.0, 1.0);\n\n\n\tlet pixel_center = vec2<f32>(global_id.xy) + vec2<f32>(0.5);\n\tlet in_uv = pixel_center/vec2<f32>(target_size.xy);\n\tlet d = in_uv * 2.0 - 1.0;\n\n\tlet origin = (uniforms.view_inv * vec4<f32>(0.0,0.0,0.0,1.0)).xyz;\n\tlet temp = uniforms.proj_inv * vec4<f32>(d.x, d.y, 1.0, 1.0);\n\tlet direction = (uniforms.view_inv * vec4<f32>(normalize(temp.xyz), 0.0)).xyz;\n\n    var rq: ray_query<vertex_return>;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) {\n        var positions : array<vec3f, 3> = getCommittedHitVertexPositions(&rq);\n        // The cube should change colour as it rotates because it's normals are changing\n        let normals = intersection.object_to_world * vec4f(normalize(cross(positions[0] - positions[1], positions[0] - positions[2])), 0.0);\n        // the y is negated because the texture coordinates are inverted\n        color = vec4f(normals.x, -normals.y, normals.z, 1.0);\n    }\n\n    textureStore(output, global_id.xy, color);\n}\n"
  },
  {
    "path": "examples/features/src/ray_scene/cube.mtl",
    "content": "# Blender MTL File: 'None'\n# Material Count: 2\n\nnewmtl Material\nNs 250.000000\nKa 1.000000 1.000000 1.000000\nKd 0.000000 0.009087 0.800000\nKs 0.500000 0.500000 0.500000\nKe 0.000000 0.000000 0.000000\nNi 1.450000\nd 1.000000\nillum 2\n\nnewmtl None\nNs 500\nKa 0.8 0.8 0.8\nKd 0.8 0.8 0.8\nKs 0.8 0.8 0.8\nd 1\nillum 2\n"
  },
  {
    "path": "examples/features/src/ray_scene/cube.obj",
    "content": "# Blender v3.3.1 OBJ File: ''\n# www.blender.org\nmtllib cube.mtl\no Cube\nv 1.000000 1.000000 -1.000000\nv 1.000000 -1.000000 -1.000000\nv 1.000000 1.000000 1.000000\nv 1.000000 -1.000000 1.000000\nv -1.000000 1.000000 -1.000000\nv -1.000000 -1.000000 -1.000000\nv -1.000000 1.000000 1.000000\nv -1.000000 -1.000000 1.000000\nvt 0.875000 0.500000\nvt 0.625000 0.750000\nvt 0.625000 0.500000\nvt 0.375000 1.000000\nvt 0.375000 0.750000\nvt 0.625000 0.000000\nvt 0.375000 0.250000\nvt 0.375000 0.000000\nvt 0.375000 0.500000\nvt 0.125000 0.750000\nvt 0.125000 0.500000\nvt 0.625000 0.250000\nvt 0.875000 0.750000\nvt 0.625000 1.000000\nvn 0.0000 1.0000 0.0000\nvn 0.0000 0.0000 1.0000\nvn -1.0000 0.0000 0.0000\nvn 0.0000 -1.0000 0.0000\nvn 1.0000 0.0000 0.0000\nvn 0.0000 0.0000 -1.0000\nusemtl Material\ns off\nf 5/1/1 3/2/1 1/3/1\nf 3/2/2 8/4/2 4/5/2\nf 7/6/3 6/7/3 8/8/3\nf 2/9/4 8/10/4 6/11/4\nf 1/3/5 4/5/5 2/9/5\nf 5/12/6 2/9/6 6/7/6\nf 5/1/1 7/13/1 3/2/1\nf 3/2/2 7/14/2 8/4/2\nf 7/6/3 5/12/3 6/7/3\nf 2/9/4 4/5/4 8/10/4\nf 1/3/5 3/2/5 4/5/5\nf 5/12/6 1/3/6 2/9/6\no Suzanne\nv 0.437500 2.679682 0.765625\nv -0.437500 2.679682 0.765625\nv 0.500000 2.609370 0.687500\nv -0.500000 2.609370 0.687500\nv 0.546875 2.570307 0.578125\nv -0.546875 2.570307 0.578125\nv 0.351562 2.492182 0.617188\nv -0.351562 2.492182 0.617188\nv 0.351562 2.546870 0.718750\nv -0.351562 2.546870 0.718750\nv 0.351562 2.648432 0.781250\nv -0.351562 2.648432 0.781250\nv 0.273438 2.679682 0.796875\nv -0.273438 2.679682 0.796875\nv 0.203125 2.609370 0.742188\nv -0.203125 2.609370 0.742188\nv 0.156250 2.570307 0.648438\nv -0.156250 2.570307 0.648438\nv 0.078125 2.757807 0.656250\nv -0.078125 2.757807 0.656250\nv 0.140625 2.757807 0.742188\nv -0.140625 2.757807 0.742188\nv 0.242188 2.757807 0.796875\nv -0.242188 2.757807 0.796875\nv 0.273438 2.843745 0.796875\nv -0.273438 2.843745 0.796875\nv 0.203125 2.906245 0.742188\nv -0.203125 2.906245 0.742188\nv 0.156250 2.953120 0.648438\nv -0.156250 2.953120 0.648438\nv 0.351562 3.031245 0.617188\nv -0.351562 3.031245 0.617188\nv 0.351562 2.968745 0.718750\nv -0.351562 2.968745 0.718750\nv 0.351562 2.874995 0.781250\nv -0.351562 2.874995 0.781250\nv 0.437500 2.843745 0.765625\nv -0.437500 2.843745 0.765625\nv 0.500000 2.906245 0.687500\nv -0.500000 2.906245 0.687500\nv 0.546875 2.953120 0.578125\nv -0.546875 2.953120 0.578125\nv 0.625000 2.757807 0.562500\nv -0.625000 2.757807 0.562500\nv 0.562500 2.757807 0.671875\nv -0.562500 2.757807 0.671875\nv 0.468750 2.757807 0.757812\nv -0.468750 2.757807 0.757812\nv 0.476562 2.757807 0.773438\nv -0.476562 2.757807 0.773438\nv 0.445312 2.851557 0.781250\nv -0.445312 2.851557 0.781250\nv 0.351562 2.890620 0.804688\nv -0.351562 2.890620 0.804688\nv 0.265625 2.851557 0.820312\nv -0.265625 2.851557 0.820312\nv 0.226562 2.757807 0.820312\nv -0.226562 2.757807 0.820312\nv 0.265625 2.671870 0.820312\nv -0.265625 2.671870 0.820312\nv 0.351562 2.757807 0.828125\nv -0.351562 2.757807 0.828125\nv 0.351562 2.632807 0.804688\nv -0.351562 2.632807 0.804688\nv 0.445312 2.671870 0.781250\nv -0.445312 2.671870 0.781250\nv 0.000000 2.945307 0.742188\nv 0.000000 2.867182 0.820312\nv 0.000000 1.835932 0.734375\nv 0.000000 2.195307 0.781250\nv 0.000000 2.328120 0.796875\nv 0.000000 1.742182 0.718750\nv 0.000000 2.921870 0.601562\nv 0.000000 3.085932 0.570312\nv 0.000000 3.414057 -0.546875\nv 0.000000 3.078120 -0.851562\nv 0.000000 2.585932 -0.828125\nv 0.000000 2.132807 -0.351562\nv 0.203125 2.328120 0.562500\nv -0.203125 2.328120 0.562500\nv 0.312500 2.078120 0.570312\nv -0.312500 2.078120 0.570312\nv 0.351562 1.820307 0.570312\nv -0.351562 1.820307 0.570312\nv 0.367188 1.624995 0.531250\nv -0.367188 1.624995 0.531250\nv 0.328125 1.570307 0.523438\nv -0.328125 1.570307 0.523438\nv 0.179688 1.546870 0.554688\nv -0.179688 1.546870 0.554688\nv 0.000000 1.531245 0.578125\nv 0.437500 2.374995 0.531250\nv -0.437500 2.374995 0.531250\nv 0.632812 2.476557 0.539062\nv -0.632812 2.476557 0.539062\nv 0.828125 2.664057 0.445312\nv -0.828125 2.664057 0.445312\nv 0.859375 2.945307 0.593750\nv -0.859375 2.945307 0.593750\nv 0.710938 2.999995 0.625000\nv -0.710938 2.999995 0.625000\nv 0.492188 3.117182 0.687500\nv -0.492188 3.117182 0.687500\nv 0.320312 3.273432 0.734375\nv -0.320312 3.273432 0.734375\nv 0.156250 3.234370 0.757812\nv -0.156250 3.234370 0.757812\nv 0.062500 3.007807 0.750000\nv -0.062500 3.007807 0.750000\nv 0.164062 2.929682 0.773438\nv -0.164062 2.929682 0.773438\nv 0.125000 2.820307 0.765625\nv -0.125000 2.820307 0.765625\nv 0.203125 2.609370 0.742188\nv -0.203125 2.609370 0.742188\nv 0.375000 2.531245 0.703125\nv -0.375000 2.531245 0.703125\nv 0.492188 2.578120 0.671875\nv -0.492188 2.578120 0.671875\nv 0.625000 2.703120 0.648438\nv -0.625000 2.703120 0.648438\nv 0.640625 2.812495 0.648438\nv -0.640625 2.812495 0.648438\nv 0.601562 2.890620 0.664062\nv -0.601562 2.890620 0.664062\nv 0.429688 2.953120 0.718750\nv -0.429688 2.953120 0.718750\nv 0.250000 2.984370 0.757812\nv -0.250000 2.984370 0.757812\nv 0.000000 1.749995 0.734375\nv 0.109375 1.796870 0.734375\nv -0.109375 1.796870 0.734375\nv 0.117188 1.679682 0.710938\nv -0.117188 1.679682 0.710938\nv 0.062500 1.632807 0.695312\nv -0.062500 1.632807 0.695312\nv 0.000000 1.624995 0.687500\nv 0.000000 2.320307 0.750000\nv 0.000000 2.374995 0.742188\nv 0.101562 2.367182 0.742188\nv -0.101562 2.367182 0.742188\nv 0.125000 2.289057 0.750000\nv -0.125000 2.289057 0.750000\nv 0.085938 2.226557 0.742188\nv -0.085938 2.226557 0.742188\nv 0.398438 2.468745 0.671875\nv -0.398438 2.468745 0.671875\nv 0.617188 2.570307 0.625000\nv -0.617188 2.570307 0.625000\nv 0.726562 2.718745 0.601562\nv -0.726562 2.718745 0.601562\nv 0.742188 2.890620 0.656250\nv -0.742188 2.890620 0.656250\nv 0.687500 2.929682 0.726562\nv -0.687500 2.929682 0.726562\nv 0.437500 3.062495 0.796875\nv -0.437500 3.062495 0.796875\nv 0.312500 3.156245 0.835938\nv -0.312500 3.156245 0.835938\nv 0.203125 3.132807 0.851562\nv -0.203125 3.132807 0.851562\nv 0.101562 2.945307 0.843750\nv -0.101562 2.945307 0.843750\nv 0.125000 2.414057 0.812500\nv -0.125000 2.414057 0.812500\nv 0.210938 2.070307 0.710938\nv -0.210938 2.070307 0.710938\nv 0.250000 1.812495 0.687500\nv -0.250000 1.812495 0.687500\nv 0.265625 1.695307 0.664062\nv -0.265625 1.695307 0.664062\nv 0.234375 1.601557 0.632812\nv -0.234375 1.601557 0.632812\nv 0.164062 1.585932 0.632812\nv -0.164062 1.585932 0.632812\nv 0.000000 1.570307 0.640625\nv 0.000000 2.562495 0.726562\nv 0.000000 2.726557 0.765625\nv 0.328125 2.992182 0.742188\nv -0.328125 2.992182 0.742188\nv 0.164062 2.656245 0.750000\nv -0.164062 2.656245 0.750000\nv 0.132812 2.726557 0.757812\nv -0.132812 2.726557 0.757812\nv 0.117188 1.828120 0.734375\nv -0.117188 1.828120 0.734375\nv 0.078125 2.070307 0.750000\nv -0.078125 2.070307 0.750000\nv 0.000000 2.070307 0.750000\nv 0.000000 2.187495 0.742188\nv 0.093750 2.242182 0.781250\nv -0.093750 2.242182 0.781250\nv 0.132812 2.289057 0.796875\nv -0.132812 2.289057 0.796875\nv 0.109375 2.382807 0.781250\nv -0.109375 2.382807 0.781250\nv 0.039062 2.390620 0.781250\nv -0.039062 2.390620 0.781250\nv 0.000000 2.312495 0.828125\nv 0.046875 2.367182 0.812500\nv -0.046875 2.367182 0.812500\nv 0.093750 2.359370 0.812500\nv -0.093750 2.359370 0.812500\nv 0.109375 2.289057 0.828125\nv -0.109375 2.289057 0.828125\nv 0.078125 2.265620 0.804688\nv -0.078125 2.265620 0.804688\nv 0.000000 2.226557 0.804688\nv 0.257812 2.203120 0.554688\nv -0.257812 2.203120 0.554688\nv 0.164062 2.273432 0.710938\nv -0.164062 2.273432 0.710938\nv 0.179688 2.203120 0.710938\nv -0.179688 2.203120 0.710938\nv 0.234375 2.265620 0.554688\nv -0.234375 2.265620 0.554688\nv 0.000000 1.640620 0.687500\nv 0.046875 1.648432 0.687500\nv -0.046875 1.648432 0.687500\nv 0.093750 1.695307 0.710938\nv -0.093750 1.695307 0.710938\nv 0.093750 1.773432 0.726562\nv -0.093750 1.773432 0.726562\nv 0.000000 1.734370 0.656250\nv 0.093750 1.765620 0.664062\nv -0.093750 1.765620 0.664062\nv 0.093750 1.703120 0.640625\nv -0.093750 1.703120 0.640625\nv 0.046875 1.664057 0.632812\nv -0.046875 1.664057 0.632812\nv 0.000000 1.656245 0.632812\nv 0.171875 2.734370 0.781250\nv -0.171875 2.734370 0.781250\nv 0.187500 2.671870 0.773438\nv -0.187500 2.671870 0.773438\nv 0.335938 2.945307 0.757812\nv -0.335938 2.945307 0.757812\nv 0.273438 2.937495 0.773438\nv -0.273438 2.937495 0.773438\nv 0.421875 2.914057 0.773438\nv -0.421875 2.914057 0.773438\nv 0.562500 2.867182 0.695312\nv -0.562500 2.867182 0.695312\nv 0.585938 2.804682 0.687500\nv -0.585938 2.804682 0.687500\nv 0.578125 2.710932 0.679688\nv -0.578125 2.710932 0.679688\nv 0.476562 2.617182 0.718750\nv -0.476562 2.617182 0.718750\nv 0.375000 2.578120 0.742188\nv -0.375000 2.578120 0.742188\nv 0.226562 2.624995 0.781250\nv -0.226562 2.624995 0.781250\nv 0.179688 2.812495 0.781250\nv -0.179688 2.812495 0.781250\nv 0.210938 2.890620 0.781250\nv -0.210938 2.890620 0.781250\nv 0.234375 2.874995 0.757812\nv -0.234375 2.874995 0.757812\nv 0.195312 2.812495 0.757812\nv -0.195312 2.812495 0.757812\nv 0.242188 2.640620 0.757812\nv -0.242188 2.640620 0.757812\nv 0.375000 2.601557 0.726562\nv -0.375000 2.601557 0.726562\nv 0.460938 2.632807 0.703125\nv -0.460938 2.632807 0.703125\nv 0.546875 2.726557 0.671875\nv -0.546875 2.726557 0.671875\nv 0.554688 2.796870 0.671875\nv -0.554688 2.796870 0.671875\nv 0.531250 2.851557 0.679688\nv -0.531250 2.851557 0.679688\nv 0.414062 2.906245 0.750000\nv -0.414062 2.906245 0.750000\nv 0.281250 2.914057 0.765625\nv -0.281250 2.914057 0.765625\nv 0.335938 2.921870 0.750000\nv -0.335938 2.921870 0.750000\nv 0.203125 2.687495 0.750000\nv -0.203125 2.687495 0.750000\nv 0.195312 2.742182 0.750000\nv -0.195312 2.742182 0.750000\nv 0.109375 2.976557 0.609375\nv -0.109375 2.976557 0.609375\nv 0.195312 3.179682 0.617188\nv -0.195312 3.179682 0.617188\nv 0.335938 3.203120 0.593750\nv -0.335938 3.203120 0.593750\nv 0.484375 3.070307 0.554688\nv -0.484375 3.070307 0.554688\nv 0.679688 2.968745 0.492188\nv -0.679688 2.968745 0.492188\nv 0.796875 2.921870 0.460938\nv -0.796875 2.921870 0.460938\nv 0.773438 2.679682 0.375000\nv -0.773438 2.679682 0.375000\nv 0.601562 2.515620 0.414062\nv -0.601562 2.515620 0.414062\nv 0.437500 2.421870 0.468750\nv -0.437500 2.421870 0.468750\nv 0.000000 3.414057 0.289062\nv 0.000000 3.499995 -0.078125\nv 0.000000 2.320307 -0.671875\nv 0.000000 2.054682 0.187500\nv 0.000000 1.539057 0.460938\nv 0.000000 1.710932 0.343750\nv 0.000000 1.945307 0.320312\nv 0.000000 2.031245 0.281250\nv 0.851562 2.749995 0.054688\nv -0.851562 2.749995 0.054688\nv 0.859375 2.835932 -0.046875\nv -0.859375 2.835932 -0.046875\nv 0.773438 2.781245 -0.437500\nv -0.773438 2.781245 -0.437500\nv 0.460938 2.953120 -0.703125\nv -0.460938 2.953120 -0.703125\nv 0.734375 2.468745 0.070312\nv -0.734375 2.468745 0.070312\nv 0.593750 2.390620 -0.164062\nv -0.593750 2.390620 -0.164062\nv 0.640625 2.507807 -0.429688\nv -0.640625 2.507807 -0.429688\nv 0.335938 2.570307 -0.664062\nv -0.335938 2.570307 -0.664062\nv 0.234375 2.164057 0.406250\nv -0.234375 2.164057 0.406250\nv 0.179688 2.101557 0.257812\nv -0.179688 2.101557 0.257812\nv 0.289062 1.804682 0.382812\nv -0.289062 1.804682 0.382812\nv 0.250000 2.015620 0.390625\nv -0.250000 2.015620 0.390625\nv 0.328125 1.601557 0.398438\nv -0.328125 1.601557 0.398438\nv 0.140625 1.757807 0.367188\nv -0.140625 1.757807 0.367188\nv 0.125000 1.976557 0.359375\nv -0.125000 1.976557 0.359375\nv 0.164062 1.570307 0.437500\nv -0.164062 1.570307 0.437500\nv 0.218750 2.234370 0.429688\nv -0.218750 2.234370 0.429688\nv 0.210938 2.289057 0.468750\nv -0.210938 2.289057 0.468750\nv 0.203125 2.343745 0.500000\nv -0.203125 2.343745 0.500000\nv 0.210938 2.124995 0.164062\nv -0.210938 2.124995 0.164062\nv 0.296875 2.203120 -0.265625\nv -0.296875 2.203120 -0.265625\nv 0.343750 2.367182 -0.539062\nv -0.343750 2.367182 -0.539062\nv 0.453125 3.382807 -0.382812\nv -0.453125 3.382807 -0.382812\nv 0.453125 3.445307 -0.070312\nv -0.453125 3.445307 -0.070312\nv 0.453125 3.367182 0.234375\nv -0.453125 3.367182 0.234375\nv 0.460938 3.039057 0.429688\nv -0.460938 3.039057 0.429688\nv 0.726562 2.921870 0.335938\nv -0.726562 2.921870 0.335938\nv 0.632812 2.968745 0.281250\nv -0.632812 2.968745 0.281250\nv 0.640625 3.218745 0.054688\nv -0.640625 3.218745 0.054688\nv 0.796875 3.078120 0.125000\nv -0.796875 3.078120 0.125000\nv 0.796875 3.132807 -0.117188\nv -0.796875 3.132807 -0.117188\nv 0.640625 3.265620 -0.195312\nv -0.640625 3.265620 -0.195312\nv 0.640625 3.195307 -0.445312\nv -0.640625 3.195307 -0.445312\nv 0.796875 3.054682 -0.359375\nv -0.796875 3.054682 -0.359375\nv 0.617188 2.843745 -0.585938\nv -0.617188 2.843745 -0.585938\nv 0.484375 2.539057 -0.546875\nv -0.484375 2.539057 -0.546875\nv 0.820312 2.843745 -0.203125\nv -0.820312 2.843745 -0.203125\nv 0.406250 2.343745 0.148438\nv -0.406250 2.343745 0.148438\nv 0.429688 2.320307 -0.210938\nv -0.429688 2.320307 -0.210938\nv 0.890625 2.921870 -0.234375\nv -0.890625 2.921870 -0.234375\nv 0.773438 2.374995 -0.125000\nv -0.773438 2.374995 -0.125000\nv 1.039062 2.414057 -0.328125\nv -1.039062 2.414057 -0.328125\nv 1.281250 2.570307 -0.429688\nv -1.281250 2.570307 -0.429688\nv 1.351562 2.835932 -0.421875\nv -1.351562 2.835932 -0.421875\nv 1.234375 3.023432 -0.421875\nv -1.234375 3.023432 -0.421875\nv 1.023438 2.992182 -0.312500\nv -1.023438 2.992182 -0.312500\nv 1.015625 2.929682 -0.289062\nv -1.015625 2.929682 -0.289062\nv 1.187500 2.953120 -0.390625\nv -1.187500 2.953120 -0.390625\nv 1.265625 2.804682 -0.406250\nv -1.265625 2.804682 -0.406250\nv 1.210938 2.593745 -0.406250\nv -1.210938 2.593745 -0.406250\nv 1.031250 2.476557 -0.304688\nv -1.031250 2.476557 -0.304688\nv 0.828125 2.445307 -0.132812\nv -0.828125 2.445307 -0.132812\nv 0.921875 2.874995 -0.218750\nv -0.921875 2.874995 -0.218750\nv 0.945312 2.820307 -0.289062\nv -0.945312 2.820307 -0.289062\nv 0.882812 2.492182 -0.210938\nv -0.882812 2.492182 -0.210938\nv 1.039062 2.515620 -0.367188\nv -1.039062 2.515620 -0.367188\nv 1.187500 2.609370 -0.445312\nv -1.187500 2.609370 -0.445312\nv 1.234375 2.765620 -0.445312\nv -1.234375 2.765620 -0.445312\nv 1.171875 2.874995 -0.437500\nv -1.171875 2.874995 -0.437500\nv 1.023438 2.859370 -0.359375\nv -1.023438 2.859370 -0.359375\nv 0.843750 2.804682 -0.210938\nv -0.843750 2.804682 -0.210938\nv 0.835938 2.687495 -0.273438\nv -0.835938 2.687495 -0.273438\nv 0.757812 2.609370 -0.273438\nv -0.757812 2.609370 -0.273438\nv 0.820312 2.601557 -0.273438\nv -0.820312 2.601557 -0.273438\nv 0.843750 2.531245 -0.273438\nv -0.843750 2.531245 -0.273438\nv 0.812500 2.499995 -0.273438\nv -0.812500 2.499995 -0.273438\nv 0.726562 2.515620 -0.070312\nv -0.726562 2.515620 -0.070312\nv 0.718750 2.492182 -0.171875\nv -0.718750 2.492182 -0.171875\nv 0.718750 2.554682 -0.187500\nv -0.718750 2.554682 -0.187500\nv 0.796875 2.718745 -0.210938\nv -0.796875 2.718745 -0.210938\nv 0.890625 2.757807 -0.265625\nv -0.890625 2.757807 -0.265625\nv 0.890625 2.749995 -0.320312\nv -0.890625 2.749995 -0.320312\nv 0.812500 2.499995 -0.320312\nv -0.812500 2.499995 -0.320312\nv 0.851562 2.531245 -0.320312\nv -0.851562 2.531245 -0.320312\nv 0.828125 2.593745 -0.320312\nv -0.828125 2.593745 -0.320312\nv 0.765625 2.609370 -0.320312\nv -0.765625 2.609370 -0.320312\nv 0.843750 2.687495 -0.320312\nv -0.843750 2.687495 -0.320312\nv 1.039062 2.843745 -0.414062\nv -1.039062 2.843745 -0.414062\nv 1.187500 2.859370 -0.484375\nv -1.187500 2.859370 -0.484375\nv 1.257812 2.757807 -0.492188\nv -1.257812 2.757807 -0.492188\nv 1.210938 2.601557 -0.484375\nv -1.210938 2.601557 -0.484375\nv 1.046875 2.515620 -0.421875\nv -1.046875 2.515620 -0.421875\nv 0.882812 2.499995 -0.265625\nv -0.882812 2.499995 -0.265625\nv 0.953125 2.804682 -0.343750\nv -0.953125 2.804682 -0.343750\nv 0.890625 2.624995 -0.328125\nv -0.890625 2.624995 -0.328125\nv 0.937500 2.578120 -0.335938\nv -0.937500 2.578120 -0.335938\nv 1.000000 2.640620 -0.367188\nv -1.000000 2.640620 -0.367188\nv 0.960938 2.687495 -0.351562\nv -0.960938 2.687495 -0.351562\nv 1.015625 2.749995 -0.375000\nv -1.015625 2.749995 -0.375000\nv 1.054688 2.703120 -0.382812\nv -1.054688 2.703120 -0.382812\nv 1.109375 2.726557 -0.390625\nv -1.109375 2.726557 -0.390625\nv 1.085938 2.789057 -0.390625\nv -1.085938 2.789057 -0.390625\nv 1.023438 2.953120 -0.484375\nv -1.023438 2.953120 -0.484375\nv 1.250000 2.984370 -0.546875\nv -1.250000 2.984370 -0.546875\nv 1.367188 2.812495 -0.500000\nv -1.367188 2.812495 -0.500000\nv 1.312500 2.570307 -0.531250\nv -1.312500 2.570307 -0.531250\nv 1.039062 2.429682 -0.492188\nv -1.039062 2.429682 -0.492188\nv 0.789062 2.390620 -0.328125\nv -0.789062 2.390620 -0.328125\nv 0.859375 2.898432 -0.382812\nv -0.859375 2.898432 -0.382812\nvt 0.890955 0.590063\nvt 0.860081 0.560115\nvt 0.904571 0.559404\nvt 0.856226 0.850547\nvt 0.888398 0.821999\nvt 0.900640 0.853232\nvt 0.853018 0.521562\nvt 0.920166 0.524546\nvt 0.847458 0.888748\nvt 0.914672 0.888748\nvt 0.798481 0.569535\nvt 0.795104 0.838402\nvt 0.870622 0.589649\nvt 0.828900 0.590771\nvt 0.826436 0.818537\nvt 0.868067 0.821510\nvt 0.854402 0.604754\nvt 0.828171 0.633354\nvt 0.827598 0.775964\nvt 0.852534 0.805700\nvt 0.791018 0.645443\nvt 0.791018 0.762238\nvt 0.855181 0.668527\nvt 0.856142 0.742025\nvt 0.844839 0.707525\nvt 0.854107 0.625459\nvt 0.853157 0.785002\nvt 0.867508 0.642291\nvt 0.900375 0.666964\nvt 0.901223 0.745592\nvt 0.867293 0.768782\nvt 0.842358 0.702491\nvt 0.921180 0.713713\nvt 0.931889 0.636832\nvt 0.918898 0.699697\nvt 0.931368 0.777093\nvt 0.968213 0.770220\nvt 0.905882 0.627902\nvt 0.890474 0.641909\nvt 0.904990 0.784860\nvt 0.906232 0.605742\nvt 0.904357 0.807013\nvt 0.931250 0.820926\nvt 0.933717 0.593037\nvt 0.968392 0.645333\nvt 0.965038 0.841671\nvt 0.968392 0.573812\nvt 0.889591 0.593275\nvt 0.887178 0.818729\nvt 0.900583 0.804677\nvt 0.902359 0.607909\nvt 0.898822 0.786233\nvt 0.899781 0.626257\nvt 0.890219 0.770183\nvt 0.887351 0.775442\nvt 0.887842 0.636527\nvt 0.870376 0.775972\nvt 0.859881 0.623942\nvt 0.870908 0.635245\nvt 0.858859 0.786774\nvt 0.859664 0.608186\nvt 0.857942 0.802505\nvt 0.871664 0.593961\nvt 0.869299 0.817249\nvt 0.879400 0.616512\nvt 0.878029 0.795063\nvt 0.536419 0.062072\nvt 0.518916 0.050294\nvt 0.540260 0.053805\nvt 0.501452 0.062043\nvt 0.518925 0.059681\nvt 0.542788 0.064089\nvt 0.551930 0.058338\nvt 0.495083 0.064047\nvt 0.497626 0.053770\nvt 0.555073 0.061900\nvt 0.482805 0.061829\nvt 0.485955 0.058273\nvt 0.563812 0.076586\nvt 0.546290 0.072669\nvt 0.491565 0.072625\nvt 0.474014 0.076511\nvt 0.583135 0.108495\nvt 0.548333 0.084893\nvt 0.489507 0.084858\nvt 0.454527 0.108481\nvt 0.605512 0.165134\nvt 0.621513 0.227818\nvt 0.553118 0.209599\nvt 0.416514 0.229490\nvt 0.432024 0.165644\nvt 0.485339 0.210053\nvt 0.676379 0.233241\nvt 0.647395 0.200502\nvt 0.360308 0.235899\nvt 0.372747 0.256357\nvt 0.683908 0.279995\nvt 0.664761 0.253225\nvt 0.353696 0.284606\nvt 0.707254 0.310054\nvt 0.715342 0.265392\nvt 0.330721 0.316853\nvt 0.351187 0.317440\nvt 0.697446 0.332673\nvt 0.687515 0.311539\nvt 0.341964 0.339667\nvt 0.362723 0.329722\nvt 0.662817 0.372521\nvt 0.676824 0.323937\nvt 0.379297 0.378686\nvt 0.402772 0.362131\nvt 0.618316 0.375151\nvt 0.639050 0.357330\nvt 0.424583 0.379267\nvt 0.604826 0.397804\nvt 0.626842 0.395792\nvt 0.439252 0.401540\nvt 0.442396 0.381222\nvt 0.553095 0.390512\nvt 0.600808 0.377857\nvt 0.490934 0.391862\nvt 0.482938 0.358497\nvt 0.521923 0.386009\nvt 0.559674 0.357011\nvt 0.521086 0.343868\nvt 0.599845 0.344815\nvt 0.577279 0.340156\nvt 0.441977 0.347815\nvt 0.615546 0.342005\nvt 0.634472 0.332311\nvt 0.425972 0.345582\nvt 0.662406 0.312804\nvt 0.406362 0.336480\nvt 0.668440 0.297958\nvt 0.377061 0.317685\nvt 0.664101 0.277872\nvt 0.370304 0.302644\nvt 0.639236 0.253047\nvt 0.374100 0.281778\nvt 0.613992 0.242662\nvt 0.398938 0.255633\nvt 0.572941 0.258564\nvt 0.424464 0.244473\nvt 0.519760 0.248864\nvt 0.466409 0.259709\nvt 0.558527 0.316594\nvt 0.482619 0.317843\nvt 0.520277 0.294764\nvt 0.556923 0.291214\nvt 0.483433 0.292249\nvt 0.563905 0.272007\nvt 0.475886 0.273078\nvt 0.525483 0.068967\nvt 0.512375 0.068956\nvt 0.531231 0.073829\nvt 0.506626 0.073811\nvt 0.531019 0.087431\nvt 0.555621 0.121749\nvt 0.532669 0.090920\nvt 0.505177 0.090908\nvt 0.482177 0.121781\nvt 0.506827 0.087416\nvt 0.518981 0.151749\nvt 0.532042 0.127713\nvt 0.538112 0.158382\nvt 0.505828 0.127728\nvt 0.518941 0.128358\nvt 0.518925 0.093952\nvt 0.518927 0.085180\nvt 0.548362 0.173560\nvt 0.535214 0.166808\nvt 0.502799 0.166857\nvt 0.489683 0.173693\nvt 0.499851 0.158434\nvt 0.544281 0.193366\nvt 0.537959 0.175966\nvt 0.500100 0.176033\nvt 0.493996 0.193428\nvt 0.528757 0.191785\nvt 0.519841 0.200843\nvt 0.509219 0.191626\nvt 0.500890 0.187571\nvt 0.519132 0.185382\nvt 0.517577 0.190607\nvt 0.518998 0.159028\nvt 0.519016 0.165599\nvt 0.506910 0.171667\nvt 0.528222 0.186316\nvt 0.509787 0.186260\nvt 0.533528 0.184215\nvt 0.537248 0.187577\nvt 0.504547 0.184206\nvt 0.504604 0.176791\nvt 0.531131 0.171631\nvt 0.533449 0.176739\nvt 0.519099 0.179457\nvt 0.561572 0.167779\nvt 0.476363 0.167996\nvt 0.478371 0.149447\nvt 0.559475 0.149319\nvt 0.596138 0.133426\nvt 0.441395 0.133592\nvt 0.601169 0.147885\nvt 0.436337 0.148194\nvt 0.528933 0.084957\nvt 0.508915 0.084945\nvt 0.518925 0.083865\nvt 0.529036 0.075429\nvt 0.508820 0.075415\nvt 0.523751 0.070508\nvt 0.514106 0.070501\nvt 0.518928 0.067899\nvt 0.518929 0.069468\nvt 0.518928 0.074259\nvt 0.516297 0.074966\nvt 0.524236 0.076691\nvt 0.521560 0.074970\nvt 0.513619 0.076684\nvt 0.524601 0.079886\nvt 0.513252 0.079879\nvt 0.518926 0.079331\nvt 0.571787 0.277295\nvt 0.568351 0.292904\nvt 0.468070 0.278617\nvt 0.471978 0.294282\nvt 0.573085 0.311386\nvt 0.467790 0.313081\nvt 0.584855 0.327708\nvt 0.456477 0.329961\nvt 0.458737 0.268049\nvt 0.611720 0.255725\nvt 0.580734 0.266620\nvt 0.427062 0.257728\nvt 0.632494 0.262853\nvt 0.406068 0.265508\nvt 0.653658 0.279971\nvt 0.384904 0.283634\nvt 0.656064 0.297636\nvt 0.383015 0.301864\nvt 0.386858 0.314615\nvt 0.652752 0.310186\nvt 0.411556 0.327673\nvt 0.614408 0.331972\nvt 0.629040 0.323864\nvt 0.426727 0.335361\nvt 0.601033 0.333624\nvt 0.440344 0.336537\nvt 0.601799 0.328453\nvt 0.439372 0.331331\nvt 0.450408 0.323919\nvt 0.613335 0.327083\nvt 0.427623 0.330358\nvt 0.626851 0.320513\nvt 0.413648 0.324175\nvt 0.646248 0.306421\nvt 0.393381 0.310510\nvt 0.649541 0.296225\nvt 0.389662 0.300183\nvt 0.647785 0.283486\nvt 0.391040 0.287071\nvt 0.629829 0.267263\nvt 0.408893 0.269959\nvt 0.612641 0.261560\nvt 0.426254 0.263693\nvt 0.585166 0.270991\nvt 0.454369 0.272583\nvt 0.578124 0.281900\nvt 0.461798 0.283441\nvt 0.579548 0.309340\nvt 0.590644 0.321516\nvt 0.461204 0.311233\nvt 0.577524 0.293776\nvt 0.462754 0.295432\nvt 0.553209 0.433063\nvt 0.523031 0.433628\nvt 0.492809 0.434538\nvt 0.609819 0.431516\nvt 0.435860 0.435740\nvt 0.416915 0.400552\nvt 0.396518 0.425416\nvt 0.648174 0.419316\nvt 0.350292 0.396229\nvt 0.692106 0.388274\nvt 0.312756 0.350588\nvt 0.735879 0.312112\nvt 0.726332 0.341754\nvt 0.301067 0.320593\nvt 0.320452 0.270303\nvt 0.304876 0.261087\nvt 0.698172 0.216906\nvt 0.729900 0.256393\nvt 0.337414 0.219179\nvt 0.663103 0.190671\nvt 0.373474 0.191872\nvt 0.649444 0.022378\nvt 0.621440 0.048089\nvt 0.626908 0.015608\nvt 0.388827 0.021586\nvt 0.416419 0.047631\nvt 0.376796 0.075296\nvt 0.577206 0.032801\nvt 0.567460 0.000144\nvt 0.411318 0.015131\nvt 0.460782 0.032656\nvt 0.547413 0.041724\nvt 0.518922 0.024886\nvt 0.470636 0.000144\nvt 0.490511 0.041669\nvt 0.558059 0.053871\nvt 0.479842 0.053785\nvt 0.576951 0.057998\nvt 0.460920 0.057845\nvt 0.611687 0.078268\nvt 0.425932 0.077985\nvt 0.660451 0.076084\nvt 0.626663 0.111357\nvt 0.410618 0.111244\nvt 0.629482 0.130456\nvt 0.407648 0.130594\nvt 0.413741 0.147158\nvt 0.619303 0.159841\nvt 0.418035 0.160361\nvt 0.389677 0.201890\nvt 0.886245 0.121777\nvt 0.891780 0.036916\nvt 0.945900 0.079569\nvt 0.141314 0.112482\nvt 0.142277 0.021467\nvt 0.183115 0.092127\nvt 0.849114 0.099732\nvt 0.805584 0.010786\nvt 0.232648 0.003484\nvt 0.246353 0.076510\nvt 0.687018 0.077204\nvt 0.672384 0.022201\nvt 0.349875 0.075955\nvt 0.365979 0.020991\nvt 0.760215 0.193244\nvt 0.789046 0.233323\nvt 0.271553 0.193871\nvt 0.241255 0.236977\nvt 0.909112 0.183261\nvt 0.994525 0.167705\nvt 0.107928 0.179083\nvt 0.078961 0.060719\nvt 0.862868 0.338556\nvt 0.962901 0.344752\nvt 0.911671 0.402429\nvt 0.160557 0.356821\nvt 0.043968 0.367038\nvt 0.123776 0.315519\nvt 0.915360 0.259804\nvt 0.999856 0.254640\nvt 0.098965 0.266968\nvt 0.000144 0.259113\nvt 0.011829 0.155367\nvt 0.749542 0.334683\nvt 0.766337 0.300809\nvt 0.789162 0.313727\nvt 0.267408 0.310142\nvt 0.288183 0.346496\nvt 0.242992 0.325552\nvt 0.815314 0.276388\nvt 0.846174 0.293397\nvt 0.213065 0.285164\nvt 0.178537 0.304983\nvt 0.845007 0.256352\nvt 0.873517 0.265922\nvt 0.179662 0.263312\nvt 0.147089 0.274284\nvt 0.859075 0.228168\nvt 0.886999 0.233769\nvt 0.162803 0.231720\nvt 0.131514 0.237587\nvt 0.875030 0.184705\nvt 0.842355 0.195160\nvt 0.145224 0.182749\nvt 0.894128 0.301884\nvt 0.794286 0.364062\nvt 0.770185 0.379538\nvt 0.239776 0.382592\nvt 0.845499 0.449967\nvt 0.106400 0.432652\nvt 0.815858 0.445381\nvt 0.755700 0.418603\nvt 0.287033 0.442912\nvt 0.219260 0.477186\nvt 0.268122 0.398737\nvt 0.185281 0.484099\nvt 0.819845 0.468071\nvt 0.215894 0.503605\nvt 0.809631 0.233887\nvt 0.219168 0.237388\nvt 0.829287 0.219562\nvt 0.199067 0.222464\nvt 0.788458 0.080826\nvt 0.715482 0.139727\nvt 0.319538 0.139409\nvt 0.246666 0.114850\nvt 0.785486 0.152330\nvt 0.245969 0.151002\nvt 0.623495 0.146796\nvt 0.837382 0.156361\nvt 0.196622 0.155241\nvt 0.171653 0.132294\nvt 0.786480 0.117591\nvt 0.858171 0.137775\nvt 0.432388 0.894943\nvt 0.491058 0.881714\nvt 0.506166 0.904851\nvt 0.321637 0.893225\nvt 0.263032 0.878321\nvt 0.315867 0.868209\nvt 0.572792 0.860484\nvt 0.604825 0.879946\nvt 0.181486 0.854693\nvt 0.247207 0.901159\nvt 0.148729 0.873349\nvt 0.619962 0.791615\nvt 0.136063 0.784093\nvt 0.169745 0.787474\nvt 0.586396 0.793977\nvt 0.563786 0.739211\nvt 0.194086 0.733241\nvt 0.208656 0.740879\nvt 0.549027 0.746412\nvt 0.508270 0.697693\nvt 0.250811 0.693249\nvt 0.258399 0.707497\nvt 0.438641 0.680683\nvt 0.434803 0.658882\nvt 0.320962 0.677959\nvt 0.325318 0.656224\nvt 0.500314 0.711729\nvt 0.452955 0.700023\nvt 0.306136 0.696976\nvt 0.505666 0.730944\nvt 0.252524 0.726592\nvt 0.568148 0.787367\nvt 0.188269 0.781375\nvt 0.214575 0.750414\nvt 0.555495 0.826352\nvt 0.199850 0.820889\nvt 0.501231 0.844356\nvt 0.253846 0.840502\nvt 0.457832 0.840040\nvt 0.297562 0.837358\nvt 0.783193 0.187449\nvt 0.246955 0.187075\nvt 0.233625 0.175620\nvt 0.394766 0.686125\nvt 0.391039 0.611891\nvt 0.364838 0.684445\nvt 0.391747 0.862097\nvt 0.438797 0.870229\nvt 0.363377 0.861308\nvt 0.435018 0.718280\nvt 0.323658 0.715731\nvt 0.384658 0.710299\nvt 0.433669 0.729661\nvt 0.374400 0.708969\nvt 0.410995 0.747662\nvt 0.427812 0.742828\nvt 0.324726 0.727177\nvt 0.347028 0.745816\nvt 0.330270 0.740536\nvt 0.384657 0.795423\nvt 0.418086 0.784946\nvt 0.372270 0.794472\nvt 0.431333 0.817535\nvt 0.401605 0.841460\nvt 0.324790 0.815460\nvt 0.338952 0.783073\nvt 0.354026 0.840297\nvt 0.825107 0.209762\nvt 0.199767 0.214827\nvt 0.816266 0.203086\nvt 0.209828 0.206161\nvt 0.226485 0.183086\nvt 0.796021 0.176969\nvt 0.802192 0.184609\nvt 0.448505 0.804621\nvt 0.473386 0.824700\nvt 0.307886 0.802031\nvt 0.282357 0.821525\nvt 0.321237 0.777208\nvt 0.423718 0.754191\nvt 0.435868 0.779569\nvt 0.334089 0.752045\nvt 0.319919 0.747250\nvt 0.437950 0.749777\nvt 0.312907 0.729222\nvt 0.440995 0.724383\nvt 0.445392 0.731997\nvt 0.317510 0.721697\nvt 0.455277 0.713731\nvt 0.303460 0.710657\nvt 0.512485 0.828811\nvt 0.242975 0.824574\nvt 0.550942 0.811814\nvt 0.204839 0.806417\nvt 0.552139 0.787682\nvt 0.204331 0.782156\nvt 0.539407 0.764539\nvt 0.542850 0.755753\nvt 0.217774 0.759319\nvt 0.508439 0.743135\nvt 0.249419 0.738732\nvt 0.454776 0.761665\nvt 0.302729 0.758742\nvt 0.286960 0.745020\nvt 0.470841 0.748408\nvt 0.475403 0.783904\nvt 0.281439 0.780511\nvt 0.268291 0.766661\nvt 0.503673 0.787562\nvt 0.494476 0.802470\nvt 0.252972 0.783410\nvt 0.261790 0.798626\nvt 0.516802 0.807339\nvt 0.239243 0.802891\nvt 0.237920 0.787045\nvt 0.518562 0.791602\nvt 0.484068 0.628776\nvt 0.543385 0.683538\nvt 0.276936 0.625067\nvt 0.216123 0.678120\nvt 0.581052 0.726933\nvt 0.177176 0.720426\nvt 0.616701 0.759965\nvt 0.140379 0.752377\nvt 0.660647 0.741167\nvt 0.707492 0.759884\nvt 0.097038 0.732052\nvt 0.677256 0.670436\nvt 0.745511 0.652100\nvt 0.049526 0.748824\nvt 0.083564 0.662038\nvt 0.671403 0.592656\nvt 0.740843 0.572428\nvt 0.019409 0.639749\nvt 0.092820 0.589862\nvt 0.834705 0.206959\nvt 0.051216 0.522659\nvt 0.033664 0.564403\nvt 0.620420 0.565675\nvt 0.498072 0.552315\nvt 0.145041 0.562595\nvt 0.264218 0.550140\nvt 0.369913 0.610196\nvt 0.464579 0.342230\nvt 0.176788 0.196179\nvt 0.770572 0.444261\nvt 0.271364 0.473316\nvt 0.488870 0.770464\nvt 0.834578 0.206879\nvn 0.9693 -0.0118 0.2456\nvn 0.6076 -0.5104 0.6085\nvn 0.8001 -0.0028 0.5999\nvn -0.6076 -0.5104 0.6085\nvn -0.9693 -0.0118 0.2456\nvn -0.8001 -0.0028 0.5999\nvn 0.6802 -0.5463 0.4888\nvn 0.8682 -0.0048 0.4961\nvn -0.6802 -0.5463 0.4888\nvn -0.8682 -0.0048 0.4961\nvn 0.1193 -0.8712 0.4763\nvn -0.1193 -0.8712 0.4763\nvn 0.7290 -0.6566 0.1934\nvn 0.0995 -0.7515 0.6522\nvn -0.0995 -0.7515 0.6522\nvn -0.7290 -0.6566 0.1934\nvn 0.0314 -0.9670 0.2529\nvn -0.4563 -0.5362 0.7101\nvn 0.4563 -0.5362 0.7101\nvn -0.0314 -0.9670 0.2529\nvn -0.5539 -0.6332 0.5406\nvn 0.5539 -0.6332 0.5406\nvn -0.6899 -0.0041 0.7239\nvn 0.6899 -0.0041 0.7239\nvn 0.8097 -0.0070 0.5868\nvn -0.6506 -0.6883 0.3210\nvn 0.6506 -0.6883 0.3210\nvn -0.9521 -0.0102 0.3057\nvn -0.4560 0.5222 0.7207\nvn 0.4560 0.5222 0.7207\nvn 0.9521 -0.0102 0.3057\nvn -0.8097 -0.0070 0.5868\nvn 0.5306 0.6258 0.5717\nvn 0.1031 0.7402 0.6644\nvn -0.5306 0.6258 0.5717\nvn -0.1031 0.7402 0.6644\nvn -0.1257 0.8416 0.5253\nvn 0.0258 0.9726 0.2312\nvn -0.6644 0.6821 0.3056\nvn -0.0258 0.9726 0.2312\nvn 0.7364 0.6521 0.1803\nvn -0.7364 0.6521 0.1803\nvn -0.6102 0.4956 0.6181\nvn 0.6102 0.4956 0.6181\nvn 0.1257 0.8416 0.5253\nvn -0.6682 0.5371 0.5148\nvn 0.6682 0.5371 0.5148\nvn 0.9645 -0.0127 0.2639\nvn -0.9645 -0.0127 0.2639\nvn -0.7216 0.6556 0.2224\nvn 0.7216 0.6556 0.2224\nvn -0.0432 0.9389 0.3415\nvn 0.0432 0.9389 0.3415\nvn 0.6644 0.6821 0.3056\nvn 0.6237 0.6285 0.4647\nvn -0.6237 0.6285 0.4647\nvn 0.9270 -0.0130 0.3749\nvn -0.6159 -0.6366 0.4641\nvn -0.9270 -0.0130 0.3749\nvn 0.6159 -0.6366 0.4641\nvn 0.0426 -0.9404 0.3375\nvn -0.0426 -0.9404 0.3375\nvn 0.7152 -0.6625 0.2227\nvn -0.7152 -0.6625 0.2227\nvn 0.1836 -0.0053 0.9830\nvn -0.1836 -0.0053 0.9830\nvn 0.1554 -0.7590 0.6323\nvn 0.0000 -0.9677 0.2523\nvn 0.1596 -0.9753 0.1529\nvn -0.1554 -0.7590 0.6323\nvn 0.0000 -0.7753 0.6316\nvn 0.3502 -0.6392 0.6847\nvn 0.5267 -0.8347 0.1611\nvn -0.3502 -0.6392 0.6847\nvn -0.1596 -0.9753 0.1529\nvn 0.9457 -0.2579 0.1977\nvn -0.9457 -0.2579 0.1977\nvn -0.5267 -0.8347 0.1611\nvn 0.9728 0.1003 0.2087\nvn 0.5557 -0.2264 0.8000\nvn -0.5557 -0.2264 0.8000\nvn -0.9728 0.1003 0.2087\nvn 0.9557 0.2492 0.1565\nvn 0.5652 -0.0297 0.8244\nvn -0.5652 -0.0297 0.8244\nvn -0.9557 0.2492 0.1565\nvn 0.8916 -0.3307 0.3095\nvn 0.3842 -0.5671 0.7286\nvn 0.0402 -0.2722 0.9614\nvn -0.3842 -0.5671 0.7286\nvn -0.8916 -0.3307 0.3095\nvn -0.0402 -0.2722 0.9614\nvn 0.5875 -0.7849 0.1970\nvn 0.3489 -0.9371 -0.0082\nvn -0.5875 -0.7849 0.1970\nvn -0.4991 -0.3761 0.7807\nvn 0.5666 -0.3188 0.7598\nvn 0.4991 -0.3761 0.7807\nvn -0.5666 -0.3188 0.7598\nvn 0.8451 0.4434 0.2985\nvn 0.9070 -0.4009 -0.1290\nvn -0.8451 0.4434 0.2985\nvn -0.4607 -0.1448 0.8757\nvn 0.5171 0.8291 0.2125\nvn 0.4607 -0.1448 0.8757\nvn -0.5171 0.8291 0.2125\nvn -0.4801 -0.1833 0.8578\nvn 0.5976 0.7847 0.1646\nvn 0.4801 -0.1833 0.8578\nvn -0.5976 0.7847 0.1646\nvn -0.3085 0.0039 0.9512\nvn 0.2666 0.2166 0.9392\nvn 0.3085 0.0039 0.9512\nvn -0.2666 0.2166 0.9392\nvn -0.6051 0.7680 0.2098\nvn 0.2313 0.9570 0.1751\nvn 0.6051 0.7680 0.2098\nvn 0.1574 0.1660 0.9735\nvn -0.8242 0.5468 0.1473\nvn -0.1574 0.1660 0.9735\nvn 0.8242 0.5468 0.1473\nvn 0.0611 -0.0253 0.9978\nvn 0.0000 0.9636 0.2673\nvn -0.0611 -0.0253 0.9978\nvn 0.0000 -0.0827 0.9966\nvn 0.2582 -0.1265 0.9578\nvn 0.3679 -0.2836 0.8856\nvn -0.2582 -0.1265 0.9578\nvn 0.1490 -0.1542 0.9767\nvn 0.2190 0.0372 0.9750\nvn -0.1490 -0.1542 0.9767\nvn 0.2254 -0.3608 0.9050\nvn -0.2190 0.0372 0.9750\nvn 0.3588 -0.1192 0.9258\nvn -0.2254 -0.3608 0.9050\nvn 0.4602 -0.1651 0.8723\nvn -0.3588 -0.1192 0.9258\nvn 0.4279 -0.3895 0.8156\nvn -0.4602 -0.1651 0.8723\nvn 0.3322 -0.3667 0.8690\nvn -0.4279 -0.3895 0.8156\nvn -0.1522 -0.2549 0.9549\nvn -0.3322 -0.3667 0.8690\nvn -0.0000 0.0643 0.9979\nvn 0.1522 -0.2549 0.9549\nvn 0.0316 -0.1782 0.9835\nvn -0.0316 -0.1782 0.9835\nvn -0.0000 -0.2220 0.9750\nvn -0.2006 -0.1350 0.9703\nvn 0.2006 -0.1350 0.9703\nvn -0.2393 -0.3012 0.9230\nvn 0.2393 -0.3012 0.9230\nvn -0.0589 -0.3784 0.9238\nvn 0.0589 -0.3784 0.9238\nvn 0.1307 -0.3187 0.9388\nvn -0.1307 -0.3187 0.9388\nvn 0.1460 -0.1202 0.9820\nvn 0.5937 0.1082 0.7974\nvn 0.1815 -0.0452 0.9823\nvn -0.1815 -0.0452 0.9823\nvn -0.5937 0.1082 0.7974\nvn -0.1460 -0.1202 0.9820\nvn 0.0000 -0.4760 0.8795\nvn 0.1341 0.0063 0.9909\nvn 0.5003 -0.4293 0.7520\nvn -0.1341 0.0063 0.9909\nvn 0.0000 0.0000 1.0000\nvn 0.0000 -0.0341 0.9994\nvn -0.0000 -0.5870 0.8096\nvn 0.9304 -0.1242 0.3448\nvn 0.5836 -0.6929 0.4235\nvn -0.5836 -0.6929 0.4235\nvn -0.9304 -0.1242 0.3448\nvn -0.5003 -0.4293 0.7520\nvn 0.4931 -0.3412 0.8002\nvn 0.9306 -0.2353 0.2804\nvn -0.9306 -0.2353 0.2804\nvn -0.4931 -0.3412 0.8002\nvn -0.2405 0.9491 0.2036\nvn 0.0000 0.5166 0.8562\nvn 0.2405 0.9491 0.2036\nvn -0.6286 0.7688 0.1177\nvn 0.0000 0.8287 0.5597\nvn 0.0000 0.9515 0.3076\nvn 0.0000 -0.8654 0.5011\nvn 0.0000 -0.4815 0.8764\nvn -0.1833 -0.5864 0.7890\nvn -0.1858 0.5956 0.7815\nvn 0.1858 0.5956 0.7815\nvn 0.3611 0.4713 0.8047\nvn 0.6286 0.7688 0.1177\nvn -0.3611 0.4713 0.8047\nvn -0.4488 -0.3147 0.8364\nvn 0.1833 -0.5864 0.7890\nvn 0.4488 -0.3147 0.8364\nvn 0.0000 0.1578 0.9875\nvn 0.7752 0.0387 0.6306\nvn -0.7752 0.0387 0.6306\nvn -0.6507 0.1488 0.7447\nvn 0.6507 0.1488 0.7447\nvn 0.9278 0.3530 0.1209\nvn -0.9278 0.3530 0.1209\nvn 0.9306 0.3435 0.1263\nvn -0.9306 0.3435 0.1263\nvn -0.1369 -0.5273 0.8386\nvn 0.1369 -0.5273 0.8386\nvn 0.0000 -0.9619 0.2732\nvn -0.6351 0.0428 0.7712\nvn 0.6351 0.0428 0.7712\nvn -0.4141 0.5798 0.7016\nvn 0.4141 0.5798 0.7016\nvn 0.0000 -0.3465 0.9380\nvn 0.0000 0.5588 0.8293\nvn 0.0000 0.5334 0.8459\nvn 0.2959 0.4750 0.8288\nvn -0.6738 0.1155 0.7299\nvn -0.2959 0.4750 0.8288\nvn 0.6738 0.1155 0.7299\nvn -0.5177 -0.7041 0.4860\nvn 0.5177 -0.7041 0.4860\nvn 0.0000 -0.6989 0.7152\nvn -0.0101 -0.0700 0.9975\nvn 0.1581 -0.0843 0.9838\nvn 0.0101 -0.0700 0.9975\nvn -0.1581 -0.0843 0.9838\nvn 0.2934 -0.0602 0.9541\nvn -0.2934 -0.0602 0.9541\nvn 0.1588 -0.1065 0.9816\nvn -0.1588 -0.1065 0.9816\nvn 0.0317 -0.2198 0.9750\nvn 0.1845 -0.1863 0.9650\nvn -0.0317 -0.2198 0.9750\nvn -0.1845 -0.1863 0.9650\nvn 0.2990 -0.0356 0.9536\nvn -0.2990 -0.0356 0.9536\nvn 0.2943 -0.1021 0.9502\nvn -0.2943 -0.1020 0.9502\nvn 0.1776 -0.0608 0.9822\nvn -0.1776 -0.0608 0.9822\nvn -0.2944 0.0046 0.9557\nvn 0.2944 0.0046 0.9557\nvn -0.0887 -0.1272 0.9879\nvn 0.2036 0.1032 0.9736\nvn 0.0887 -0.1272 0.9879\nvn -0.2036 0.1032 0.9736\nvn 0.1435 0.0966 0.9849\nvn -0.1435 0.0966 0.9849\nvn 0.2886 -0.2786 0.9160\nvn -0.2886 -0.2786 0.9160\nvn -0.4508 -0.4658 0.7614\nvn 0.1133 -0.3142 0.9426\nvn -0.1133 -0.3142 0.9426\nvn -0.2741 -0.8556 0.4391\nvn 0.2741 -0.8556 0.4391\nvn -0.1423 -0.5826 0.8002\nvn 0.1423 -0.5826 0.8002\nvn -0.4229 -0.1078 0.8997\nvn 0.4229 -0.1078 0.8997\nvn -0.1921 0.1914 0.9625\nvn 0.1921 0.1914 0.9625\nvn -0.1653 0.6098 0.7751\nvn 0.1653 0.6098 0.7751\nvn 0.1431 0.5587 0.8169\nvn -0.1431 0.5587 0.8169\nvn 0.4323 0.5833 0.6877\nvn -0.4323 0.5833 0.6877\nvn 0.6881 0.2985 0.6614\nvn -0.6881 0.2985 0.6614\nvn 0.7894 -0.2032 0.5793\nvn 0.4508 -0.4658 0.7614\nvn -0.7894 -0.2032 0.5793\nvn 0.8016 0.0110 0.5977\nvn -0.8016 0.0110 0.5977\nvn -0.4603 0.8619 0.2127\nvn 0.0000 0.8592 0.5116\nvn 0.4603 0.8619 0.2127\nvn -0.4792 0.5120 -0.7129\nvn 0.4792 0.5120 -0.7129\nvn -0.2313 0.9570 0.1751\nvn -0.1217 0.6503 -0.7499\nvn 0.1217 0.6503 -0.7499\nvn -0.2275 0.8745 -0.4283\nvn 0.2275 0.8745 -0.4283\nvn -0.3456 0.9125 -0.2192\nvn 0.6957 0.5814 -0.4218\nvn 0.3456 0.9125 -0.2192\nvn -0.6957 0.5814 -0.4218\nvn -0.9070 -0.4009 -0.1290\nvn -0.9302 -0.3062 -0.2024\nvn 0.5444 -0.8372 -0.0533\nvn 0.9302 -0.3062 -0.2024\nvn -0.5444 -0.8372 -0.0533\nvn 0.4720 -0.8637 -0.1768\nvn -0.4720 -0.8637 -0.1768\nvn 0.0000 -0.7711 -0.6367\nvn 0.2771 -0.3147 -0.9078\nvn -0.0000 -0.2133 -0.9770\nvn -0.2771 -0.3147 -0.9078\nvn -0.6894 -0.6687 -0.2786\nvn 0.1514 -0.1510 -0.9769\nvn 0.0000 -0.2974 -0.9548\nvn -0.1514 -0.1510 -0.9769\nvn 0.0675 -0.7832 -0.6181\nvn 0.0000 -0.8818 -0.4716\nvn -0.0675 -0.7832 -0.6181\nvn 0.5551 -0.4762 -0.6820\nvn -0.5551 -0.4762 -0.6820\nvn 0.6204 0.0835 -0.7798\nvn -0.6204 0.0835 -0.7798\nvn 0.7799 -0.0105 -0.6259\nvn -0.7799 -0.0105 -0.6259\nvn 0.6894 -0.6687 -0.2786\nvn 0.8957 0.2578 -0.3624\nvn -0.8957 0.2578 -0.3624\nvn 0.9787 -0.1959 0.0615\nvn -0.9787 -0.1959 0.0615\nvn -0.8872 -0.1577 0.4336\nvn 0.7857 -0.5715 0.2368\nvn -0.7857 -0.5715 0.2368\nvn -0.3489 -0.9371 -0.0082\nvn 0.4455 -0.3584 -0.8204\nvn 0.0000 -0.6913 -0.7226\nvn -0.0000 -0.3049 -0.9524\nvn -0.4455 -0.3584 -0.8204\nvn -0.5223 -0.6536 -0.5477\nvn 0.5223 -0.6536 -0.5477\nvn 0.0000 -0.9417 -0.3365\nvn -0.5071 -0.8376 -0.2033\nvn 0.5727 -0.8197 0.0120\nvn 0.0000 -0.9831 -0.1833\nvn -0.5727 -0.8197 0.0120\nvn 0.7211 -0.6898 0.0651\nvn 0.9850 -0.1605 0.0631\nvn -0.7211 -0.6898 0.0651\nvn -0.9850 -0.1605 0.0631\nvn 0.4730 0.1763 -0.8632\nvn 0.0000 0.3650 -0.9310\nvn -0.4730 0.1763 -0.8632\nvn 0.4442 0.7244 0.5271\nvn 0.0000 0.9997 0.0226\nvn 0.0000 0.8306 0.5568\nvn -0.4442 0.7244 0.5271\nvn -0.4135 0.9096 0.0395\nvn 0.3913 0.8153 -0.4268\nvn 0.0000 0.8343 -0.5514\nvn -0.3913 0.8153 -0.4268\nvn 0.7717 0.6311 0.0785\nvn 0.4444 0.7886 0.4250\nvn -0.7717 0.6311 0.0785\nvn -0.4444 0.7886 0.4250\nvn 0.7418 0.5164 0.4279\nvn 0.6682 0.6719 0.3195\nvn -0.7418 0.5164 0.4279\nvn -0.6682 0.6719 0.3195\nvn 0.8486 0.5288 -0.0140\nvn 0.6784 0.7314 -0.0695\nvn -0.8486 0.5288 -0.0140\nvn -0.6784 0.7314 -0.0695\nvn 0.8722 0.3146 -0.3747\nvn 0.6075 0.5696 -0.5536\nvn -0.8722 0.3146 -0.3747\nvn -0.6075 0.5696 -0.5536\nvn 0.6197 -0.0605 -0.7825\nvn 0.6708 -0.0453 -0.7403\nvn -0.6197 -0.0605 -0.7825\nvn 0.4135 0.9096 0.0395\nvn 0.3406 0.8832 0.3223\nvn -0.3406 0.8832 0.3223\nvn 0.0000 0.5293 0.8485\nvn 0.9983 -0.0283 -0.0502\nvn -0.9983 -0.0283 -0.0502\nvn 0.8403 0.4934 0.2246\nvn -0.8403 0.4934 0.2246\nvn 0.5071 -0.8376 -0.2033\nvn 0.5790 -0.8027 0.1427\nvn -0.5790 -0.8027 0.1427\nvn -0.5633 -0.8173 -0.1213\nvn 0.3123 -0.9500 0.0012\nvn -0.3123 -0.9500 0.0012\nvn 0.8872 -0.1577 0.4336\nvn 0.3255 -0.6029 -0.7284\nvn -0.3255 -0.6029 -0.7284\nvn -0.5292 -0.5051 -0.6817\nvn 0.5633 -0.8173 -0.1213\nvn 0.5292 -0.5051 -0.6817\nvn -0.2793 0.7683 0.5759\nvn 0.5512 -0.0788 0.8307\nvn 0.0188 0.8723 0.4887\nvn 0.2793 0.7683 0.5759\nvn -0.5512 -0.0788 0.8307\nvn -0.4493 -0.0383 0.8926\nvn 0.3215 -0.0923 0.9424\nvn 0.3836 0.8630 0.3288\nvn -0.3215 -0.0923 0.9424\nvn -0.0188 0.8723 0.4887\nvn -0.3836 0.8630 0.3288\nvn 0.7788 0.1678 0.6044\nvn -0.7788 0.1678 0.6044\nvn 0.1545 -0.1239 0.9802\nvn -0.1545 -0.1239 0.9802\nvn 0.6526 -0.4768 0.5888\nvn -0.6526 -0.4768 0.5888\nvn 0.0411 0.3108 0.9496\nvn -0.0411 0.3108 0.9496\nvn 0.5029 -0.7810 0.3703\nvn -0.5029 -0.7810 0.3703\nvn -0.5384 0.2953 0.7893\nvn 0.3300 0.3157 0.8896\nvn 0.0295 -0.6350 0.7719\nvn -0.3300 0.3157 0.8896\nvn -0.0295 -0.6350 0.7719\nvn 0.5384 0.2953 0.7893\nvn 0.1629 0.8581 0.4870\nvn -0.1629 0.8581 0.4870\nvn -0.1868 0.9538 0.2351\nvn 0.1868 0.9538 0.2351\nvn -0.9848 -0.0996 0.1426\nvn 0.9848 -0.0996 0.1426\nvn 0.7622 0.6471 -0.0193\nvn -0.1496 -0.7455 0.6495\nvn 0.1496 -0.7455 0.6495\nvn 0.5605 -0.6609 0.4991\nvn -0.5605 -0.6609 0.4991\nvn 0.6842 -0.5558 0.4722\nvn -0.6842 -0.5558 0.4722\nvn 0.8572 -0.4931 -0.1483\nvn -0.8572 -0.4931 -0.1483\nvn -0.7312 0.1144 0.6725\nvn 0.7312 0.1144 0.6725\nvn 0.4493 -0.0383 0.8926\nvn 0.5998 0.5131 0.6139\nvn -0.5998 0.5131 0.6139\nvn 0.9610 -0.1188 0.2499\nvn 0.8420 -0.1763 0.5098\nvn -0.9610 -0.1188 0.2499\nvn 0.8515 0.0414 0.5228\nvn 0.4814 0.6344 0.6048\nvn -0.8420 -0.1763 0.5098\nvn -0.8515 0.0414 0.5228\nvn -0.4814 0.6344 0.6048\nvn 0.8303 -0.4790 0.2850\nvn 0.6864 -0.6234 0.3746\nvn -0.8303 -0.4790 0.2850\nvn 0.7261 -0.4989 0.4732\nvn 0.7949 -0.2332 0.5601\nvn -0.7261 -0.4989 0.4732\nvn -0.6864 -0.6234 0.3746\nvn -0.7949 -0.2332 0.5601\nvn 0.6593 -0.4685 0.5881\nvn 0.6482 -0.4206 0.6347\nvn -0.6593 -0.4685 0.5881\nvn -0.6482 -0.4206 0.6347\nvn -0.5725 -0.4189 0.7048\nvn 0.7584 0.2665 0.5948\nvn 0.5725 -0.4189 0.7048\nvn -0.7584 0.2665 0.5948\nvn -0.4492 0.3799 0.8086\nvn 0.4492 0.3799 0.8086\nvn -0.2929 0.3709 0.8813\nvn 0.6450 0.3102 0.6984\nvn 0.2929 0.3709 0.8813\nvn -0.6450 0.3102 0.6984\nvn -0.0331 0.9449 0.3256\nvn 0.0331 0.9449 0.3256\nvn 0.4618 -0.3291 0.8237\nvn -0.4618 -0.3291 0.8237\nvn -0.2624 -0.5331 0.8043\nvn 0.2624 -0.5331 0.8043\nvn -0.7529 -0.0338 0.6573\nvn 0.7529 -0.0338 0.6573\nvn -0.5831 0.4999 0.6403\nvn -0.7622 0.6471 -0.0193\nvn 0.5831 0.4999 0.6403\nvn 0.0650 0.7039 0.7074\nvn -0.0650 0.7039 0.7074\nvn 0.1951 0.0390 0.9800\nvn -0.1951 0.0390 0.9800\nvn -0.4085 0.1273 0.9039\nvn 0.4085 0.1273 0.9039\nvn 0.3347 -0.0046 0.9423\nvn -0.3347 -0.0046 0.9423\nvn -0.4448 -0.0937 0.8907\nvn 0.3144 -0.1038 0.9436\nvn 0.3343 0.1068 0.9364\nvn -0.3144 -0.1038 0.9436\nvn -0.3343 0.1068 0.9364\nvn 0.2897 0.3158 0.9035\nvn -0.2897 0.3158 0.9035\nvn -0.3831 -0.0685 0.9211\nvn 0.3831 -0.0685 0.9211\nvn -0.0989 -0.8408 -0.5322\nvn -0.0253 -0.6796 -0.7331\nvn 0.0989 -0.8408 -0.5322\nvn 0.0253 -0.6796 -0.7331\nvn 0.6366 -0.5043 -0.5834\nvn -0.6366 -0.5043 -0.5834\nvn 0.9253 0.0918 -0.3680\nvn -0.9253 0.0918 -0.3680\nvn 0.2870 0.5978 -0.7485\nvn -0.2870 0.5978 -0.7485\nvn -0.4142 0.5509 -0.7245\nvn 0.4142 0.5509 -0.7245\nvn -0.6501 0.5847 -0.4854\nvn 0.6501 0.5847 -0.4854\nvn -0.6708 -0.0453 -0.7403\nvn -0.3679 -0.2836 0.8856\nvn 0.4448 -0.0937 0.8907\nusemtl None\ns 1\nf 55/15/7 11/16/8 53/17/9\nf 12/18/10 56/19/11 54/20/12\nf 53/17/9 13/21/13 51/22/14\nf 14/23/15 54/20/12 52/24/16\nf 11/16/8 15/25/17 13/21/13\nf 16/26/18 12/18/10 14/23/15\nf 9/27/19 17/28/20 11/16/8\nf 18/29/21 10/30/22 12/18/10\nf 19/31/23 23/32/24 17/28/20\nf 24/33/25 20/34/26 18/29/21\nf 17/28/20 25/35/27 15/25/17\nf 26/36/28 18/29/21 16/26/18\nf 29/37/29 25/35/27 23/32/24\nf 30/38/30 26/36/28 28/39/31\nf 21/40/32 29/37/29 23/32/24\nf 30/38/30 22/41/33 24/33/25\nf 31/42/34 35/43/35 29/37/29\nf 36/44/36 32/45/37 30/38/30\nf 35/43/35 27/46/38 29/37/29\nf 36/44/36 28/39/31 38/47/39\nf 41/48/40 37/49/41 35/43/35\nf 42/50/42 38/47/39 40/51/43\nf 43/52/44 35/43/35 33/53/45\nf 44/54/46 36/44/36 42/50/42\nf 45/55/47 41/48/40 43/52/44\nf 46/56/48 42/50/42 48/57/49\nf 47/58/50 39/59/51 41/48/40\nf 48/57/49 40/51/43 50/60/52\nf 53/17/9 49/61/53 47/58/50\nf 54/20/12 50/60/52 52/24/16\nf 55/15/7 47/58/50 45/55/47\nf 56/19/11 48/57/49 54/20/12\nf 45/55/47 57/62/54 55/15/7\nf 46/56/48 58/63/55 60/64/56\nf 43/52/44 59/65/57 45/55/47\nf 44/54/46 60/64/56 62/66/58\nf 33/53/45 61/67/59 43/52/44\nf 34/68/60 62/66/58 64/69/61\nf 31/42/34 63/70/62 33/53/45\nf 32/45/37 64/69/61 66/71/63\nf 31/42/34 67/72/64 65/73/65\nf 68/74/66 32/45/37 66/71/63\nf 21/40/32 71/75/67 67/72/64\nf 72/76/68 22/41/33 68/74/66\nf 19/31/23 73/77/69 71/75/67\nf 74/78/70 20/34/26 72/76/68\nf 9/27/19 57/62/54 73/77/69\nf 58/63/55 10/30/22 74/78/70\nf 69/79/71 73/77/69 57/62/54\nf 58/63/55 74/78/70 70/80/72\nf 71/75/67 73/77/69 69/79/71\nf 70/80/72 74/78/70 72/76/68\nf 69/79/71 67/72/64 71/75/67\nf 72/76/68 68/74/66 70/80/72\nf 69/79/71 65/73/65 67/72/64\nf 68/74/66 66/71/63 70/80/72\nf 69/79/71 63/70/62 65/73/65\nf 66/71/63 64/69/61 70/80/72\nf 69/79/71 61/67/59 63/70/62\nf 64/69/61 62/66/58 70/80/72\nf 69/79/71 59/65/57 61/67/59\nf 62/66/58 60/64/56 70/80/72\nf 69/79/71 57/62/54 59/65/57\nf 60/64/56 58/63/55 70/80/72\nf 182/81/73 99/82/74 97/83/75\nf 183/84/76 99/82/74 184/85/77\nf 180/86/78 97/83/75 95/87/79\nf 181/88/80 98/89/81 183/84/76\nf 93/90/82 180/86/78 95/87/79\nf 181/88/80 94/91/83 96/92/84\nf 91/93/85 178/94/86 93/90/82\nf 179/95/87 92/96/88 94/91/83\nf 89/97/89 176/98/90 91/93/85\nf 177/99/91 90/100/92 92/96/88\nf 87/101/93 154/102/94 172/103/95\nf 155/104/96 88/105/97 173/106/98\nf 102/107/99 154/102/94 100/108/100\nf 103/109/101 155/104/96 157/110/102\nf 102/107/99 158/111/103 156/112/104\nf 159/113/105 103/109/101 157/110/102\nf 106/114/106 158/111/103 104/115/107\nf 107/116/108 159/113/105 161/117/109\nf 108/118/110 160/119/111 106/114/106\nf 109/120/112 161/117/109 163/121/113\nf 110/122/114 162/123/115 108/118/110\nf 111/124/116 163/121/113 165/125/117\nf 110/122/114 166/126/118 164/127/119\nf 167/128/120 111/124/116 165/125/117\nf 114/129/121 166/126/118 112/130/122\nf 115/131/123 167/128/120 169/132/124\nf 116/133/125 168/134/126 114/129/121\nf 117/135/127 169/132/124 171/136/128\nf 75/137/129 170/138/130 116/133/125\nf 75/137/129 171/136/128 76/139/131\nf 136/140/132 170/138/130 118/141/133\nf 137/142/134 171/136/128 169/132/124\nf 136/140/132 166/126/118 168/134/126\nf 167/128/120 137/142/134 169/132/124\nf 164/127/119 187/143/135 134/144/136\nf 165/125/117 188/145/137 167/128/120\nf 162/123/115 134/144/136 132/146/138\nf 163/121/113 135/147/139 165/125/117\nf 160/119/111 132/146/138 130/148/140\nf 161/117/109 133/149/141 163/121/113\nf 158/111/103 130/148/140 128/150/142\nf 159/113/105 131/151/143 161/117/109\nf 156/112/104 128/150/142 126/152/144\nf 157/110/102 129/153/145 159/113/105\nf 154/102/94 126/152/144 124/154/146\nf 155/104/96 127/155/147 157/110/102\nf 172/103/95 124/154/146 122/156/148\nf 173/106/98 125/157/149 155/104/96\nf 122/156/148 185/158/150 172/103/95\nf 185/158/150 123/159/151 173/106/98\nf 170/138/130 120/160/152 118/141/133\nf 171/136/128 121/161/153 76/139/131\nf 120/160/152 186/162/154 191/163/155\nf 186/162/154 121/161/153 192/164/156\nf 189/165/157 186/162/154 185/158/150\nf 190/166/158 186/162/154 192/164/156\nf 143/167/159 184/85/77 182/81/73\nf 184/85/77 144/168/160 183/84/76\nf 141/169/161 182/81/73 180/86/78\nf 183/84/76 142/170/162 181/88/80\nf 141/169/161 178/94/86 139/171/163\nf 142/170/162 179/95/87 181/88/80\nf 174/172/164 193/173/165 176/98/90\nf 194/174/166 175/175/167 177/99/91\nf 139/171/163 176/98/90 193/173/165\nf 177/99/91 140/176/168 194/174/166\nf 198/177/169 195/178/170 152/179/171\nf 198/177/169 196/180/172 197/181/173\nf 195/178/170 77/182/174 193/173/165\nf 196/180/172 77/182/174 197/181/173\nf 139/171/163 77/182/174 138/183/175\nf 140/176/168 77/182/174 194/174/166\nf 150/184/176 199/185/177 152/179/171\nf 200/186/178 151/187/179 153/188/180\nf 148/189/181 201/190/182 150/184/176\nf 202/191/183 149/192/184 151/187/179\nf 205/193/185 148/189/181 147/194/186\nf 206/195/187 149/192/184 204/196/188\nf 79/197/189 147/194/186 146/198/190\nf 79/197/189 147/194/186 206/195/187\nf 152/179/171 78/199/191 198/177/169\nf 153/188/180 78/199/191 200/186/178\nf 199/185/177 216/200/192 78/199/191\nf 200/186/178 216/200/192 215/201/193\nf 79/197/189 208/202/194 205/193/185\nf 209/203/195 79/197/189 206/195/187\nf 205/193/185 210/204/196 203/205/197\nf 211/206/198 206/195/187 204/196/188\nf 210/204/196 201/190/182 203/205/197\nf 211/206/198 202/191/183 213/207/199\nf 201/190/182 214/208/200 199/185/177\nf 215/201/193 202/191/183 200/186/178\nf 212/209/201 208/202/194 207/210/202\nf 213/207/199 209/203/195 211/206/198\nf 207/210/202 214/208/200 212/209/201\nf 215/201/193 207/210/202 213/207/199\nf 147/194/186 172/103/95 185/158/150\nf 173/106/98 147/194/186 185/158/150\nf 148/189/181 219/211/203 172/103/95\nf 220/212/204 149/192/184 173/106/98\nf 152/179/171 219/211/203 150/184/176\nf 153/188/180 220/212/204 222/213/205\nf 195/178/170 221/214/206 152/179/171\nf 196/180/172 222/213/205 175/175/167\nf 217/215/207 174/172/164 89/97/89\nf 218/216/208 175/175/167 222/213/205\nf 223/217/209 221/214/206 217/215/207\nf 224/218/210 222/213/205 220/212/204\nf 87/101/93 219/211/203 223/217/209\nf 220/212/204 88/105/97 224/218/210\nf 138/183/175 230/219/211 139/171/163\nf 138/183/175 231/220/212 80/221/213\nf 141/169/161 230/219/211 228/222/214\nf 231/220/212 142/170/162 229/223/215\nf 143/167/159 228/222/214 226/224/216\nf 229/223/215 144/168/160 227/225/217\nf 145/226/218 226/224/216 225/227/219\nf 227/225/217 145/226/218 225/227/219\nf 226/224/216 239/228/220 225/227/219\nf 227/225/217 239/228/220 238/229/221\nf 226/224/216 235/230/222 237/231/223\nf 236/232/224 227/225/217 238/229/221\nf 228/222/214 233/233/225 235/230/222\nf 234/234/226 229/223/215 236/232/224\nf 80/221/213 233/233/225 230/219/211\nf 80/221/213 234/234/226 232/235/227\nf 232/235/227 237/231/223 233/233/225\nf 238/229/221 232/235/227 234/234/226\nf 233/233/225 237/231/223 235/230/222\nf 236/232/224 238/229/221 234/234/226\nf 191/163/155 242/236/228 240/237/229\nf 243/238/230 192/164/156 241/239/231\nf 120/160/152 240/237/229 262/240/232\nf 241/239/231 121/161/153 263/241/233\nf 120/160/152 264/242/234 118/141/133\nf 121/161/153 265/243/235 263/241/233\nf 122/156/148 242/236/228 189/165/157\nf 123/159/151 243/238/230 261/244/236\nf 122/156/148 258/245/237 260/246/238\nf 259/247/239 123/159/151 261/244/236\nf 124/154/146 256/248/240 258/245/237\nf 257/249/241 125/157/149 259/247/239\nf 126/152/144 254/250/242 256/248/240\nf 255/251/243 127/155/147 257/249/241\nf 128/150/142 252/252/244 254/250/242\nf 253/253/245 129/153/145 255/251/243\nf 132/146/138 252/252/244 130/148/140\nf 133/149/141 253/253/245 251/254/246\nf 134/144/136 250/255/247 132/146/138\nf 135/147/139 251/254/246 249/256/248\nf 134/144/136 244/257/249 248/258/250\nf 245/259/251 135/147/139 249/256/248\nf 187/143/135 246/260/252 244/257/249\nf 247/261/253 188/145/137 245/259/251\nf 136/140/132 264/242/234 246/260/252\nf 265/243/235 137/142/134 247/261/253\nf 264/242/234 284/262/254 246/260/252\nf 265/243/235 285/263/255 267/264/256\nf 244/257/249 284/262/254 286/265/257\nf 285/263/255 245/259/251 287/266/258\nf 244/257/249 282/267/259 248/258/250\nf 245/259/251 283/268/260 287/266/258\nf 248/258/250 280/269/261 250/255/247\nf 249/256/248 281/270/262 283/268/260\nf 252/252/244 280/269/261 278/271/263\nf 281/270/262 253/253/245 279/272/264\nf 252/252/244 276/273/265 254/250/242\nf 253/253/245 277/274/266 279/272/264\nf 256/248/240 276/273/265 274/275/267\nf 277/274/266 257/249/241 275/276/268\nf 256/248/240 272/277/269 258/245/237\nf 257/249/241 273/278/270 275/276/268\nf 258/245/237 270/279/271 260/246/238\nf 259/247/239 271/280/272 273/278/270\nf 242/236/228 270/279/271 288/281/273\nf 271/280/272 243/238/230 289/282/274\nf 264/242/234 268/283/275 266/284/276\nf 269/285/277 265/243/235 267/264/256\nf 262/240/232 290/286/278 268/283/275\nf 291/287/279 263/241/233 269/285/277\nf 240/237/229 288/281/273 290/286/278\nf 289/282/274 241/239/231 291/287/279\nf 75/137/129 292/288/280 81/289/281\nf 293/290/282 75/137/129 81/289/281\nf 116/133/125 294/291/283 292/288/280\nf 295/292/284 117/135/127 293/290/282\nf 112/130/122 294/291/283 114/129/121\nf 113/293/285 295/292/284 297/294/286\nf 110/122/114 296/295/287 112/130/122\nf 111/124/116 297/294/286 299/296/288\nf 108/118/110 298/297/289 110/122/114\nf 109/120/112 299/296/288 301/298/290\nf 108/118/110 302/299/291 300/300/292\nf 303/301/293 109/120/112 301/298/290\nf 104/115/107 302/299/291 106/114/106\nf 105/302/294 303/301/293 305/303/295\nf 104/115/107 306/304/296 304/305/297\nf 307/306/298 105/302/294 305/303/295\nf 102/107/99 308/307/299 306/304/296\nf 309/308/300 103/109/101 307/306/298\nf 317/309/301 346/310/302 316/311/303\nf 317/312/301 347/313/304 337/314/305\nf 316/311/303 344/315/306 315/316/307\nf 316/317/303 345/318/308 347/313/304\nf 315/316/307 348/319/309 314/320/310\nf 315/321/307 349/322/311 345/318/308\nf 97/83/75 314/320/310 348/319/309\nf 314/320/310 98/89/81 349/322/311\nf 95/87/79 348/319/309 342/323/312\nf 349/322/311 96/92/84 343/324/313\nf 93/90/82 342/323/312 338/325/314\nf 343/324/313 94/91/83 339/326/315\nf 91/93/85 338/325/314 340/327/316\nf 339/326/315 92/96/88 341/328/317\nf 338/325/314 346/310/302 340/327/316\nf 347/313/304 339/326/315 341/328/317\nf 342/323/312 344/315/306 338/325/314\nf 343/324/313 345/318/308 349/322/311\nf 340/327/316 336/329/318 334/330/319\nf 341/328/317 337/314/305 347/313/304\nf 89/97/89 340/327/316 334/330/319\nf 341/328/317 90/100/92 335/331/320\nf 350/332/321 223/217/209 217/215/207\nf 351/333/322 224/218/210 353/334/323\nf 334/330/319 217/215/207 89/97/89\nf 335/331/320 218/216/208 351/333/322\nf 223/217/209 354/335/324 87/101/93\nf 224/218/210 355/336/325 353/334/323\nf 354/335/324 100/108/100 87/101/93\nf 355/336/325 101/337/326 309/308/300\nf 332/338/327 312/339/328 85/340/329\nf 333/341/330 312/342/328 361/343/331\nf 360/344/332 86/345/333 312/339/328\nf 361/343/331 86/346/333 359/347/334\nf 86/345/333 356/348/335 313/349/336\nf 357/350/337 86/346/333 313/351/336\nf 313/349/336 336/329/318 317/309/301\nf 337/314/305 313/351/336 317/312/301\nf 336/329/318 350/332/321 334/330/319\nf 337/314/305 351/333/322 357/350/337\nf 304/305/297 326/352/338 318/353/339\nf 327/354/340 305/303/295 319/355/341\nf 324/356/342 85/340/329 84/357/343\nf 325/358/344 85/359/329 333/341/330\nf 366/360/345 311/361/346 310/362/347\nf 367/363/348 311/364/346 365/365/349\nf 311/361/346 362/366/350 83/367/351\nf 363/368/352 311/364/346 83/369/351\nf 83/367/351 324/356/342 84/357/343\nf 325/358/344 83/369/351 84/370/343\nf 300/371/292 370/372/353 372/373/354\nf 371/374/355 301/375/290 373/376/356\nf 372/373/354 376/377/357 374/378/358\nf 377/379/359 373/376/356 375/380/360\nf 374/378/358 378/381/361 380/382/362\nf 379/383/363 375/380/360 381/384/364\nf 380/382/362 384/385/365 382/386/366\nf 385/387/367 381/384/364 383/388/368\nf 386/389/369 384/385/365 322/390/370\nf 387/391/371 385/387/367 383/388/368\nf 324/356/342 382/386/366 386/389/369\nf 383/388/368 325/358/344 387/391/371\nf 362/366/350 380/382/362 382/386/366\nf 381/384/364 363/368/352 383/388/368\nf 364/392/372 374/378/358 380/382/362\nf 375/380/360 365/365/349 381/384/364\nf 366/360/345 372/373/354 374/378/358\nf 373/376/356 367/363/348 375/380/360\nf 300/371/292 368/393/373 298/394/289\nf 301/375/290 369/395/374 373/376/356\nf 368/393/373 310/362/347 82/396/375\nf 369/395/374 310/397/347 367/363/348\nf 292/398/280 296/399/287 298/394/289\nf 297/400/286 293/401/282 299/402/288\nf 292/398/280 368/393/373 82/396/375\nf 369/395/374 293/401/282 82/403/375\nf 81/404/281 292/398/280 82/396/375\nf 82/403/375 293/401/282 81/405/281\nf 304/305/297 370/372/353 302/299/291\nf 305/303/295 371/374/355 319/355/341\nf 318/353/339 376/377/357 370/372/353\nf 377/379/359 319/355/341 371/374/355\nf 320/406/376 378/381/361 376/377/357\nf 379/383/363 321/407/377 377/379/359\nf 384/385/365 390/408/378 322/390/370\nf 385/387/367 391/409/379 379/383/363\nf 358/410/380 392/411/381 356/348/335\nf 359/347/334 393/412/382 395/413/383\nf 392/411/381 328/414/384 326/352/338\nf 393/412/382 329/415/385 395/413/383\nf 306/304/296 392/411/381 326/352/338\nf 393/412/382 307/306/298 327/354/340\nf 308/307/299 350/332/321 392/411/381\nf 351/333/322 309/308/300 393/412/382\nf 350/332/321 356/348/335 392/411/381\nf 393/412/382 357/350/337 351/333/322\nf 308/307/299 354/335/324 352/416/386\nf 353/334/323 355/336/325 309/308/300\nf 330/417/387 386/389/369 322/390/370\nf 331/418/388 387/391/371 389/419/389\nf 386/389/369 332/338/327 324/356/342\nf 387/391/371 333/341/330 389/419/389\nf 394/420/390 330/417/387 328/414/384\nf 395/413/383 331/418/388 389/419/389\nf 360/344/332 394/420/390 358/410/380\nf 361/343/331 395/413/383 389/419/389\nf 332/338/327 388/421/391 360/344/332\nf 361/343/331 389/419/389 333/341/330\nf 396/422/392 410/423/393 408/424/394\nf 397/425/395 411/426/396 423/427/397\nf 408/424/394 412/428/398 406/429/399\nf 413/430/400 409/431/401 407/432/402\nf 412/428/398 404/433/403 406/429/399\nf 413/430/400 405/434/404 415/435/405\nf 414/436/406 402/437/407 404/433/403\nf 415/435/405 403/438/408 417/439/409\nf 416/440/410 400/441/411 402/437/407\nf 417/439/409 401/442/412 419/443/413\nf 400/441/411 420/444/414 398/445/415\nf 421/446/416 401/442/412 399/447/417\nf 418/448/418 426/449/419 420/444/414\nf 427/450/420 419/443/413 421/446/416\nf 416/440/410 428/451/421 418/448/418\nf 429/452/422 417/439/409 419/443/413\nf 432/453/423 416/440/410 414/436/406\nf 433/454/424 417/439/409 431/455/425\nf 434/456/426 414/436/406 412/428/398\nf 435/457/427 415/435/405 433/454/424\nf 436/458/428 412/428/398 410/423/393\nf 437/459/429 413/430/400 435/457/427\nf 410/423/393 424/460/430 436/458/428\nf 425/461/431 411/426/396 437/459/429\nf 328/414/384 450/462/432 326/352/338\nf 329/415/385 451/463/433 453/464/434\nf 398/445/415 452/465/435 328/466/384\nf 399/447/417 453/467/434 421/446/416\nf 318/353/339 450/462/432 320/406/376\nf 451/463/433 319/355/341 321/407/377\nf 390/468/378 422/469/436 396/422/392\nf 423/427/397 391/470/379 397/425/395\nf 420/444/414 448/471/437 452/465/435\nf 449/472/438 421/446/416 453/467/434\nf 454/473/439 448/471/437 446/474/440\nf 455/475/441 449/472/438 453/467/434\nf 442/476/442 446/474/440 444/477/443\nf 447/478/444 443/479/445 445/480/446\nf 456/481/447 442/476/442 440/482/448\nf 457/483/449 443/479/445 455/475/441\nf 456/481/447 458/484/450 438/485/451\nf 457/483/449 459/486/452 441/487/453\nf 438/485/451 424/460/430 422/469/436\nf 439/488/454 425/461/431 459/486/452\nf 320/406/376 438/489/451 390/408/378\nf 439/490/454 321/407/377 391/409/379\nf 450/462/432 456/491/447 320/406/376\nf 451/463/433 457/492/449 455/493/441\nf 450/462/432 452/494/435 454/495/439\nf 455/493/441 453/464/434 451/463/433\nf 424/460/430 460/496/455 484/497/456\nf 461/498/457 425/461/431 485/499/458\nf 440/482/448 460/496/455 458/484/450\nf 441/487/453 461/498/457 471/500/459\nf 440/482/448 468/501/460 470/502/461\nf 469/503/462 441/487/453 471/500/459\nf 444/477/443 468/501/460 442/476/442\nf 445/480/446 469/503/462 467/504/463\nf 446/474/440 466/505/464 444/477/443\nf 447/478/444 467/504/463 465/506/465\nf 446/474/440 462/507/466 464/508/467\nf 463/509/468 447/478/444 465/506/465\nf 448/471/437 482/510/469 462/507/466\nf 483/511/470 449/472/438 463/509/468\nf 436/458/428 484/497/456 472/512/471\nf 485/499/458 437/459/429 473/513/472\nf 434/456/426 472/512/471 474/514/473\nf 473/513/472 435/457/427 475/515/474\nf 432/453/423 474/514/473 476/516/475\nf 475/515/474 433/454/424 477/517/476\nf 432/453/423 478/518/477 430/519/478\nf 433/454/424 479/520/479 477/517/476\nf 430/519/478 480/521/480 428/451/421\nf 431/455/425 481/522/481 479/520/479\nf 428/451/421 482/510/469 426/449/419\nf 429/452/422 483/511/470 481/522/481\nf 464/508/467 486/523/482 466/505/464\nf 465/506/465 487/524/483 489/525/484\nf 488/526/485 492/527/486 486/523/482\nf 489/525/484 493/528/487 491/529/488\nf 492/527/486 496/530/489 494/531/490\nf 497/532/491 493/528/487 495/533/492\nf 496/530/489 500/534/493 494/531/490\nf 497/532/491 501/535/494 499/536/495\nf 472/512/471 494/531/490 500/534/493\nf 495/533/492 473/513/472 501/535/494\nf 492/527/486 484/497/456 460/496/455\nf 493/528/487 485/499/458 495/533/492\nf 470/502/461 492/527/486 460/496/455\nf 471/500/459 493/528/487 487/524/483\nf 466/505/464 470/502/461 468/501/460\nf 471/500/459 467/504/463 469/503/462\nf 482/510/469 464/508/467 462/507/466\nf 483/511/470 465/506/465 489/525/484\nf 480/521/480 488/526/485 482/510/469\nf 489/525/484 481/522/481 483/511/470\nf 496/530/489 480/521/480 478/518/477\nf 497/532/491 481/522/481 491/529/488\nf 498/537/496 478/518/477 476/516/475\nf 499/536/495 479/520/479 497/532/491\nf 474/514/473 498/537/496 476/516/475\nf 499/536/495 475/515/474 477/517/476\nf 472/512/471 500/534/493 474/514/473\nf 475/515/474 501/535/494 473/513/472\nf 400/441/411 512/538/497 510/539/498\nf 513/540/499 401/442/412 511/541/500\nf 402/437/407 510/539/498 508/542/501\nf 511/541/500 403/438/408 509/543/502\nf 402/437/407 506/544/503 404/433/403\nf 403/438/408 507/545/504 509/543/502\nf 404/433/403 504/546/505 406/547/399\nf 405/434/404 505/548/506 507/545/504\nf 406/547/399 502/549/507 408/550/394\nf 407/551/402 503/552/508 505/548/506\nf 408/550/394 514/553/509 396/554/392\nf 409/555/401 515/556/510 503/552/508\nf 510/539/498 514/553/509 502/549/507\nf 511/541/500 515/556/510 513/540/499\nf 502/549/507 508/542/501 510/539/498\nf 509/543/502 503/552/508 511/541/500\nf 504/546/505 506/544/503 508/542/501\nf 509/543/502 507/545/504 505/548/506\nf 390/408/378 514/557/509 322/390/370\nf 391/558/379 515/556/510 397/559/395\nf 322/560/370 512/538/497 330/561/387\nf 513/540/499 323/562/511 331/563/388\nf 328/466/384 512/538/497 398/445/415\nf 513/540/499 329/564/385 399/447/417\nf 55/15/7 9/27/19 11/16/8\nf 12/18/10 10/30/22 56/19/11\nf 53/17/9 11/16/8 13/21/13\nf 14/23/15 12/18/10 54/20/12\nf 11/16/8 17/28/20 15/25/17\nf 16/26/18 18/29/21 12/18/10\nf 9/27/19 19/31/23 17/28/20\nf 18/29/21 20/34/26 10/30/22\nf 19/31/23 21/40/32 23/32/24\nf 24/33/25 22/41/33 20/34/26\nf 17/28/20 23/32/24 25/35/27\nf 26/36/28 24/33/25 18/29/21\nf 29/37/29 27/46/38 25/35/27\nf 30/38/30 24/33/25 26/36/28\nf 21/40/32 31/42/34 29/37/29\nf 30/38/30 32/45/37 22/41/33\nf 31/42/34 33/53/45 35/43/35\nf 36/44/36 34/68/60 32/45/37\nf 35/43/35 37/49/41 27/46/38\nf 36/44/36 30/38/30 28/39/31\nf 41/48/40 39/59/51 37/49/41\nf 42/50/42 36/44/36 38/47/39\nf 43/52/44 41/48/40 35/43/35\nf 44/54/46 34/68/60 36/44/36\nf 45/55/47 47/58/50 41/48/40\nf 46/56/48 44/54/46 42/50/42\nf 47/58/50 49/61/53 39/59/51\nf 48/57/49 42/50/42 40/51/43\nf 53/17/9 51/22/14 49/61/53\nf 54/20/12 48/57/49 50/60/52\nf 55/15/7 53/17/9 47/58/50\nf 56/19/11 46/56/48 48/57/49\nf 45/55/47 59/65/57 57/62/54\nf 46/56/48 56/19/11 58/63/55\nf 43/52/44 61/67/59 59/65/57\nf 44/54/46 46/56/48 60/64/56\nf 33/53/45 63/70/62 61/67/59\nf 34/68/60 44/54/46 62/66/58\nf 31/42/34 65/73/65 63/70/62\nf 32/45/37 34/68/60 64/69/61\nf 31/42/34 21/40/32 67/72/64\nf 68/74/66 22/41/33 32/45/37\nf 21/40/32 19/31/23 71/75/67\nf 72/76/68 20/34/26 22/41/33\nf 19/31/23 9/27/19 73/77/69\nf 74/78/70 10/30/22 20/34/26\nf 9/27/19 55/15/7 57/62/54\nf 58/63/55 56/19/11 10/30/22\nf 182/81/73 184/85/77 99/82/74\nf 183/84/76 98/89/81 99/82/74\nf 180/86/78 182/81/73 97/83/75\nf 181/88/80 96/92/84 98/89/81\nf 93/90/82 178/94/86 180/86/78\nf 181/88/80 179/95/87 94/91/83\nf 91/93/85 176/98/90 178/94/86\nf 179/95/87 177/99/91 92/96/88\nf 89/97/89 174/172/164 176/98/90\nf 177/99/91 175/175/167 90/100/92\nf 87/101/93 100/108/100 154/102/94\nf 155/104/96 101/337/326 88/105/97\nf 102/107/99 156/112/104 154/102/94\nf 103/109/101 101/337/326 155/104/96\nf 102/107/99 104/115/107 158/111/103\nf 159/113/105 105/302/294 103/109/101\nf 106/114/106 160/119/111 158/111/103\nf 107/116/108 105/302/294 159/113/105\nf 108/118/110 162/123/115 160/119/111\nf 109/120/112 107/116/108 161/117/109\nf 110/122/114 164/127/119 162/123/115\nf 111/124/116 109/120/112 163/121/113\nf 110/122/114 112/130/122 166/126/118\nf 167/128/120 113/293/285 111/124/116\nf 114/129/121 168/134/126 166/126/118\nf 115/131/123 113/293/285 167/128/120\nf 116/133/125 170/138/130 168/134/126\nf 117/135/127 115/131/123 169/132/124\nf 75/137/129 76/139/131 170/138/130\nf 75/137/129 117/135/127 171/136/128\nf 136/140/132 168/134/126 170/138/130\nf 137/142/134 119/565/512 171/136/128\nf 136/140/132 187/143/135 166/126/118\nf 167/128/120 188/145/137 137/142/134\nf 164/127/119 166/126/118 187/143/135\nf 165/125/117 135/147/139 188/145/137\nf 162/123/115 164/127/119 134/144/136\nf 163/121/113 133/149/141 135/147/139\nf 160/119/111 162/123/115 132/146/138\nf 161/117/109 131/151/143 133/149/141\nf 158/111/103 160/119/111 130/148/140\nf 159/113/105 129/153/145 131/151/143\nf 156/112/104 158/111/103 128/150/142\nf 157/110/102 127/155/147 129/153/145\nf 154/102/94 156/112/104 126/152/144\nf 155/104/96 125/157/149 127/155/147\nf 172/103/95 154/102/94 124/154/146\nf 173/106/98 123/159/151 125/157/149\nf 122/156/148 189/165/157 185/158/150\nf 185/158/150 190/166/158 123/159/151\nf 170/138/130 76/139/131 120/160/152\nf 171/136/128 119/565/512 121/161/153\nf 120/160/152 76/139/131 186/162/154\nf 186/162/154 76/139/131 121/161/153\nf 189/165/157 191/163/155 186/162/154\nf 190/166/158 185/158/150 186/162/154\nf 143/167/159 145/226/218 184/85/77\nf 184/85/77 145/226/218 144/168/160\nf 141/169/161 143/167/159 182/81/73\nf 183/84/76 144/168/160 142/170/162\nf 141/169/161 180/86/78 178/94/86\nf 142/170/162 140/176/168 179/95/87\nf 174/172/164 195/178/170 193/173/165\nf 194/174/166 196/180/172 175/175/167\nf 139/171/163 178/94/86 176/98/90\nf 177/99/91 179/95/87 140/176/168\nf 198/177/169 197/181/173 195/178/170\nf 198/177/169 153/188/180 196/180/172\nf 195/178/170 197/181/173 77/182/174\nf 196/180/172 194/174/166 77/182/174\nf 139/171/163 193/173/165 77/182/174\nf 140/176/168 138/183/175 77/182/174\nf 150/184/176 201/190/182 199/185/177\nf 200/186/178 202/191/183 151/187/179\nf 148/189/181 203/205/197 201/190/182\nf 202/191/183 204/196/188 149/192/184\nf 205/193/185 203/205/197 148/189/181\nf 206/195/187 147/194/186 149/192/184\nf 79/197/189 205/193/185 147/194/186\nf 79/197/189 146/198/190 147/194/186\nf 152/179/171 199/185/177 78/199/191\nf 153/188/180 198/177/169 78/199/191\nf 199/185/177 214/208/200 216/200/192\nf 200/186/178 78/199/191 216/200/192\nf 79/197/189 207/210/202 208/202/194\nf 209/203/195 207/210/202 79/197/189\nf 205/193/185 208/202/194 210/204/196\nf 211/206/198 209/203/195 206/195/187\nf 210/204/196 212/209/201 201/190/182\nf 211/206/198 204/196/188 202/191/183\nf 201/190/182 212/209/201 214/208/200\nf 215/201/193 213/207/199 202/191/183\nf 212/209/201 210/204/196 208/202/194\nf 213/207/199 207/210/202 209/203/195\nf 207/210/202 216/200/192 214/208/200\nf 215/201/193 216/200/192 207/210/202\nf 147/194/186 148/189/181 172/103/95\nf 173/106/98 149/192/184 147/194/186\nf 148/189/181 150/184/176 219/211/203\nf 220/212/204 151/187/179 149/192/184\nf 152/179/171 221/214/206 219/211/203\nf 153/188/180 151/187/179 220/212/204\nf 195/178/170 174/172/164 221/214/206\nf 196/180/172 153/188/180 222/213/205\nf 217/215/207 221/214/206 174/172/164\nf 218/216/208 90/100/92 175/175/167\nf 223/217/209 219/211/203 221/214/206\nf 224/218/210 218/216/208 222/213/205\nf 87/101/93 172/103/95 219/211/203\nf 220/212/204 173/106/98 88/105/97\nf 138/183/175 80/221/213 230/219/211\nf 138/183/175 140/176/168 231/220/212\nf 141/169/161 139/171/163 230/219/211\nf 231/220/212 140/176/168 142/170/162\nf 143/167/159 141/169/161 228/222/214\nf 229/223/215 142/170/162 144/168/160\nf 145/226/218 143/167/159 226/224/216\nf 227/225/217 144/168/160 145/226/218\nf 226/224/216 237/231/223 239/228/220\nf 227/225/217 225/227/219 239/228/220\nf 226/224/216 228/222/214 235/230/222\nf 236/232/224 229/223/215 227/225/217\nf 228/222/214 230/219/211 233/233/225\nf 234/234/226 231/220/212 229/223/215\nf 80/221/213 232/235/227 233/233/225\nf 80/221/213 231/220/212 234/234/226\nf 232/235/227 239/228/220 237/231/223\nf 238/229/221 239/228/220 232/235/227\nf 191/163/155 189/165/157 242/236/228\nf 243/238/230 190/166/158 192/164/156\nf 120/160/152 191/163/155 240/237/229\nf 241/239/231 192/164/156 121/161/153\nf 120/160/152 262/240/232 264/242/234\nf 121/161/153 119/565/512 265/243/235\nf 122/156/148 260/246/238 242/236/228\nf 123/159/151 190/166/158 243/238/230\nf 122/156/148 124/154/146 258/245/237\nf 259/247/239 125/157/149 123/159/151\nf 124/154/146 126/152/144 256/248/240\nf 257/249/241 127/155/147 125/157/149\nf 126/152/144 128/150/142 254/250/242\nf 255/251/243 129/153/145 127/155/147\nf 128/150/142 130/148/140 252/252/244\nf 253/253/245 131/151/143 129/153/145\nf 132/146/138 250/255/247 252/252/244\nf 133/149/141 131/151/143 253/253/245\nf 134/144/136 248/258/250 250/255/247\nf 135/147/139 133/149/141 251/254/246\nf 134/144/136 187/143/135 244/257/249\nf 245/259/251 188/145/137 135/147/139\nf 187/143/135 136/140/132 246/260/252\nf 247/261/253 137/142/134 188/145/137\nf 136/140/132 118/141/133 264/242/234\nf 265/243/235 119/565/512 137/142/134\nf 264/242/234 266/284/276 284/262/254\nf 265/243/235 247/261/253 285/263/255\nf 244/257/249 246/260/252 284/262/254\nf 285/263/255 247/261/253 245/259/251\nf 244/257/249 286/265/257 282/267/259\nf 245/259/251 249/256/248 283/268/260\nf 248/258/250 282/267/259 280/269/261\nf 249/256/248 251/254/246 281/270/262\nf 252/252/244 250/255/247 280/269/261\nf 281/270/262 251/254/246 253/253/245\nf 252/252/244 278/271/263 276/273/265\nf 253/253/245 255/251/243 277/274/266\nf 256/248/240 254/250/242 276/273/265\nf 277/274/266 255/251/243 257/249/241\nf 256/248/240 274/275/267 272/277/269\nf 257/249/241 259/247/239 273/278/270\nf 258/245/237 272/277/269 270/279/271\nf 259/247/239 261/244/236 271/280/272\nf 242/236/228 260/246/238 270/279/271\nf 271/280/272 261/244/236 243/238/230\nf 264/242/234 262/240/232 268/283/275\nf 269/285/277 263/241/233 265/243/235\nf 262/240/232 240/237/229 290/286/278\nf 291/287/279 241/239/231 263/241/233\nf 240/237/229 242/236/228 288/281/273\nf 289/282/274 243/238/230 241/239/231\nf 75/137/129 116/133/125 292/288/280\nf 293/290/282 117/135/127 75/137/129\nf 116/133/125 114/129/121 294/291/283\nf 295/292/284 115/131/123 117/135/127\nf 112/130/122 296/295/287 294/291/283\nf 113/293/285 115/131/123 295/292/284\nf 110/122/114 298/297/289 296/295/287\nf 111/124/116 113/293/285 297/294/286\nf 108/118/110 300/300/292 298/297/289\nf 109/120/112 111/124/116 299/296/288\nf 108/118/110 106/114/106 302/299/291\nf 303/301/293 107/116/108 109/120/112\nf 104/115/107 304/305/297 302/299/291\nf 105/302/294 107/116/108 303/301/293\nf 104/115/107 102/107/99 306/304/296\nf 307/306/298 103/109/101 105/302/294\nf 102/107/99 100/108/100 308/307/299\nf 309/308/300 101/337/326 103/109/101\nf 317/309/301 336/329/318 346/310/302\nf 317/312/301 316/317/303 347/313/304\nf 316/311/303 346/310/302 344/315/306\nf 316/317/303 315/321/307 345/318/308\nf 315/316/307 344/315/306 348/319/309\nf 315/321/307 314/320/310 349/322/311\nf 97/83/75 99/82/74 314/320/310\nf 314/320/310 99/82/74 98/89/81\nf 95/87/79 97/83/75 348/319/309\nf 349/322/311 98/89/81 96/92/84\nf 93/90/82 95/87/79 342/323/312\nf 343/324/313 96/92/84 94/91/83\nf 91/93/85 93/90/82 338/325/314\nf 339/326/315 94/91/83 92/96/88\nf 338/325/314 344/315/306 346/310/302\nf 347/313/304 345/318/308 339/326/315\nf 342/323/312 348/319/309 344/315/306\nf 343/324/313 339/326/315 345/318/308\nf 340/327/316 346/310/302 336/329/318\nf 341/328/317 335/331/320 337/314/305\nf 89/97/89 91/93/85 340/327/316\nf 341/328/317 92/96/88 90/100/92\nf 350/332/321 352/416/386 223/217/209\nf 351/333/322 218/216/208 224/218/210\nf 334/330/319 350/332/321 217/215/207\nf 335/331/320 90/100/92 218/216/208\nf 223/217/209 352/416/386 354/335/324\nf 224/218/210 88/105/97 355/336/325\nf 354/335/324 308/307/299 100/108/100\nf 355/336/325 88/105/97 101/337/326\nf 332/338/327 360/344/332 312/339/328\nf 333/341/330 85/359/329 312/342/328\nf 360/344/332 358/410/380 86/345/333\nf 361/343/331 312/342/328 86/346/333\nf 86/345/333 358/410/380 356/348/335\nf 357/350/337 359/347/334 86/346/333\nf 313/349/336 356/348/335 336/329/318\nf 337/314/305 357/350/337 313/351/336\nf 336/329/318 356/348/335 350/332/321\nf 337/314/305 335/331/320 351/333/322\nf 304/305/297 306/304/296 326/352/338\nf 327/354/340 307/306/298 305/303/295\nf 324/356/342 332/338/327 85/340/329\nf 325/358/344 84/370/343 85/359/329\nf 366/360/345 364/392/372 311/361/346\nf 367/363/348 310/397/347 311/364/346\nf 311/361/346 364/392/372 362/366/350\nf 363/368/352 365/365/349 311/364/346\nf 83/367/351 362/366/350 324/356/342\nf 325/358/344 363/368/352 83/369/351\nf 300/371/292 302/299/291 370/372/353\nf 371/374/355 303/301/293 301/375/290\nf 372/373/354 370/372/353 376/377/357\nf 377/379/359 371/374/355 373/376/356\nf 374/378/358 376/377/357 378/381/361\nf 379/383/363 377/379/359 375/380/360\nf 380/382/362 378/381/361 384/385/365\nf 385/387/367 379/383/363 381/384/364\nf 386/389/369 382/386/366 384/385/365\nf 387/391/371 323/566/511 385/387/367\nf 324/356/342 362/366/350 382/386/366\nf 383/388/368 363/368/352 325/358/344\nf 362/366/350 364/392/372 380/382/362\nf 381/384/364 365/365/349 363/368/352\nf 364/392/372 366/360/345 374/378/358\nf 375/380/360 367/363/348 365/365/349\nf 366/360/345 368/393/373 372/373/354\nf 373/376/356 369/395/374 367/363/348\nf 300/371/292 372/373/354 368/393/373\nf 301/375/290 299/402/288 369/395/374\nf 368/393/373 366/360/345 310/362/347\nf 369/395/374 82/403/375 310/397/347\nf 292/398/280 294/567/283 296/399/287\nf 297/400/286 295/568/284 293/401/282\nf 292/398/280 298/394/289 368/393/373\nf 369/395/374 299/402/288 293/401/282\nf 304/305/297 318/353/339 370/372/353\nf 305/303/295 303/301/293 371/374/355\nf 318/353/339 320/406/376 376/377/357\nf 377/379/359 321/407/377 319/355/341\nf 320/406/376 390/408/378 378/381/361\nf 379/383/363 391/409/379 321/407/377\nf 384/385/365 378/381/361 390/408/378\nf 385/387/367 323/566/511 391/409/379\nf 358/410/380 394/420/390 392/411/381\nf 359/347/334 357/350/337 393/412/382\nf 392/411/381 394/420/390 328/414/384\nf 393/412/382 327/354/340 329/415/385\nf 306/304/296 308/307/299 392/411/381\nf 393/412/382 309/308/300 307/306/298\nf 308/307/299 352/416/386 350/332/321\nf 351/333/322 353/334/323 309/308/300\nf 330/417/387 388/421/391 386/389/369\nf 331/418/388 323/566/511 387/391/371\nf 386/389/369 388/421/391 332/338/327\nf 387/391/371 325/358/344 333/341/330\nf 394/420/390 388/421/391 330/417/387\nf 395/413/383 329/415/385 331/418/388\nf 360/344/332 388/421/391 394/420/390\nf 361/343/331 359/347/334 395/413/383\nf 396/422/392 422/469/436 410/423/393\nf 397/425/395 409/431/401 411/426/396\nf 408/424/394 410/423/393 412/428/398\nf 413/430/400 411/426/396 409/431/401\nf 412/428/398 414/436/406 404/433/403\nf 413/430/400 407/432/402 405/434/404\nf 414/436/406 416/440/410 402/437/407\nf 415/435/405 405/434/404 403/438/408\nf 416/440/410 418/448/418 400/441/411\nf 417/439/409 403/438/408 401/442/412\nf 400/441/411 418/448/418 420/444/414\nf 421/446/416 419/443/413 401/442/412\nf 418/448/418 428/451/421 426/449/419\nf 427/450/420 429/452/422 419/443/413\nf 416/440/410 430/519/478 428/451/421\nf 429/452/422 431/455/425 417/439/409\nf 432/453/423 430/519/478 416/440/410\nf 433/454/424 415/435/405 417/439/409\nf 434/456/426 432/453/423 414/436/406\nf 435/457/427 413/430/400 415/435/405\nf 436/458/428 434/456/426 412/428/398\nf 437/459/429 411/426/396 413/430/400\nf 410/423/393 422/469/436 424/460/430\nf 425/461/431 423/427/397 411/426/396\nf 328/414/384 452/494/435 450/462/432\nf 329/415/385 327/354/340 451/463/433\nf 398/445/415 420/444/414 452/465/435\nf 399/447/417 329/564/385 453/467/434\nf 318/353/339 326/352/338 450/462/432\nf 451/463/433 327/354/340 319/355/341\nf 390/468/378 438/485/451 422/469/436\nf 423/427/397 439/488/454 391/470/379\nf 420/444/414 426/449/419 448/471/437\nf 449/472/438 427/450/420 421/446/416\nf 454/473/439 452/465/435 448/471/437\nf 455/475/441 447/478/444 449/472/438\nf 442/476/442 454/473/439 446/474/440\nf 447/478/444 455/475/441 443/479/445\nf 456/481/447 454/473/439 442/476/442\nf 457/483/449 441/487/453 443/479/445\nf 456/481/447 440/482/448 458/484/450\nf 457/483/449 439/488/454 459/486/452\nf 438/485/451 458/484/450 424/460/430\nf 439/488/454 423/427/397 425/461/431\nf 320/406/376 456/491/447 438/489/451\nf 439/490/454 457/492/449 321/407/377\nf 450/462/432 454/495/439 456/491/447\nf 451/463/433 321/407/377 457/492/449\nf 424/460/430 458/484/450 460/496/455\nf 461/498/457 459/486/452 425/461/431\nf 440/482/448 470/502/461 460/496/455\nf 441/487/453 459/486/452 461/498/457\nf 440/482/448 442/476/442 468/501/460\nf 469/503/462 443/479/445 441/487/453\nf 444/477/443 466/505/464 468/501/460\nf 445/480/446 443/479/445 469/503/462\nf 446/474/440 464/508/467 466/505/464\nf 447/478/444 445/480/446 467/504/463\nf 446/474/440 448/471/437 462/507/466\nf 463/509/468 449/472/438 447/478/444\nf 448/471/437 426/449/419 482/510/469\nf 483/511/470 427/450/420 449/472/438\nf 436/458/428 424/460/430 484/497/456\nf 485/499/458 425/461/431 437/459/429\nf 434/456/426 436/458/428 472/512/471\nf 473/513/472 437/459/429 435/457/427\nf 432/453/423 434/456/426 474/514/473\nf 475/515/474 435/457/427 433/454/424\nf 432/453/423 476/516/475 478/518/477\nf 433/454/424 431/455/425 479/520/479\nf 430/519/478 478/518/477 480/521/480\nf 431/455/425 429/452/422 481/522/481\nf 428/451/421 480/521/480 482/510/469\nf 429/452/422 427/450/420 483/511/470\nf 464/508/467 488/526/485 486/523/482\nf 465/506/465 467/504/463 487/524/483\nf 488/526/485 490/569/513 492/527/486\nf 489/525/484 487/524/483 493/528/487\nf 492/527/486 490/569/513 496/530/489\nf 497/532/491 491/529/488 493/528/487\nf 496/530/489 498/537/496 500/534/493\nf 497/532/491 495/533/492 501/535/494\nf 472/512/471 484/497/456 494/531/490\nf 495/533/492 485/499/458 473/513/472\nf 492/527/486 494/531/490 484/497/456\nf 493/528/487 461/498/457 485/499/458\nf 470/502/461 486/523/482 492/527/486\nf 471/500/459 461/498/457 493/528/487\nf 466/505/464 486/523/482 470/502/461\nf 471/500/459 487/524/483 467/504/463\nf 482/510/469 488/526/485 464/508/467\nf 483/511/470 463/509/468 465/506/465\nf 480/521/480 490/569/513 488/526/485\nf 489/525/484 491/529/488 481/522/481\nf 496/530/489 490/569/513 480/521/480\nf 497/532/491 479/520/479 481/522/481\nf 498/537/496 496/530/489 478/518/477\nf 499/536/495 477/517/476 479/520/479\nf 474/514/473 500/534/493 498/537/496\nf 499/536/495 501/535/494 475/515/474\nf 400/441/411 398/445/415 512/538/497\nf 513/540/499 399/447/417 401/442/412\nf 402/437/407 400/441/411 510/539/498\nf 511/541/500 401/442/412 403/438/408\nf 402/437/407 508/542/501 506/544/503\nf 403/438/408 405/434/404 507/545/504\nf 404/433/403 506/544/503 504/546/505\nf 405/434/404 407/551/402 505/548/506\nf 406/547/399 504/546/505 502/549/507\nf 407/551/402 409/555/401 503/552/508\nf 408/550/394 502/549/507 514/553/509\nf 409/555/401 397/559/395 515/556/510\nf 510/539/498 512/538/497 514/553/509\nf 511/541/500 503/552/508 515/556/510\nf 502/549/507 504/546/505 508/542/501\nf 509/543/502 505/548/506 503/552/508\nf 390/408/378 396/570/392 514/557/509\nf 391/558/379 323/562/511 515/556/510\nf 322/560/370 514/553/509 512/538/497\nf 513/540/499 515/556/510 323/562/511\nf 328/466/384 330/561/387 512/538/497\nf 513/540/499 331/563/388 329/564/385\n"
  },
  {
    "path": "examples/features/src/ray_scene/mod.rs",
    "content": "use crate::utils;\nuse bytemuck::{Pod, Zeroable};\nuse glam::{Mat4, Quat, Vec3};\nuse std::f32::consts::PI;\nuse std::ops::IndexMut;\nuse std::{borrow::Cow, iter, mem, ops::Range};\nuse wgpu::util::DeviceExt;\n\n// from cube\n#[repr(C)]\n#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]\nstruct Vertex {\n    pos: [f32; 3],\n    _p0: [u32; 1],\n    normal: [f32; 3],\n    _p1: [u32; 1],\n    uv: [f32; 2],\n    _p2: [u32; 2],\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n}\n\n#[derive(Debug, Clone, Default)]\nstruct RawSceneComponents {\n    vertices: Vec<Vertex>,\n    indices: Vec<u32>,\n    geometries: Vec<(Range<usize>, Material)>, // index range, material\n    instances: Vec<(Range<usize>, Range<usize>)>, // vertex range, geometry range\n}\n\nstruct SceneComponents {\n    vertices: wgpu::Buffer,\n    indices: wgpu::Buffer,\n    geometries: wgpu::Buffer,\n    instances: wgpu::Buffer,\n    bottom_level_acceleration_structures: Vec<wgpu::Blas>,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct InstanceEntry {\n    first_vertex: u32,\n    first_geometry: u32,\n    last_geometry: u32,\n    _pad: u32,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable, Default)]\nstruct GeometryEntry {\n    first_index: u32,\n    _p0: [u32; 3],\n    material: Material,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable, Default, Debug)]\nstruct Material {\n    roughness_exponent: f32,\n    metalness: f32,\n    specularity: f32,\n    _p0: [u32; 1],\n    albedo: [f32; 3],\n    _p1: [u32; 1],\n}\n\nfn load_model(scene: &mut RawSceneComponents, path: &str) {\n    let path = env!(\"CARGO_MANIFEST_DIR\").to_string() + \"/src\" + path;\n    println!(\"{path}\");\n    let mut object = obj::Obj::load(path).unwrap();\n    object.load_mtls().unwrap();\n\n    let data = object.data;\n\n    let start_vertex_index = scene.vertices.len();\n    let start_geometry_index = scene.geometries.len();\n\n    let mut mapping = std::collections::HashMap::<(usize, Option<usize>, usize), usize>::new();\n\n    let mut next_index = 0;\n\n    for object in data.objects {\n        for group in object.groups {\n            let start_index_index = scene.indices.len();\n            for poly in group.polys {\n                for end_index in 2..poly.0.len() {\n                    for &index in &[0, end_index - 1, end_index] {\n                        let obj::IndexTuple(position_id, texture_id, normal_id) = poly.0[index];\n                        let uv = texture_id\n                            .map(|texture_id| data.texture[texture_id])\n                            .unwrap_or_default();\n                        let normal_id = normal_id.expect(\"normals required\");\n\n                        let index = *mapping\n                            .entry((position_id, texture_id, normal_id))\n                            .or_insert(next_index);\n                        if index == next_index {\n                            next_index += 1;\n\n                            scene.vertices.push(Vertex {\n                                pos: data.position[position_id],\n                                uv,\n                                normal: data.normal[normal_id],\n                                ..Default::default()\n                            })\n                        }\n\n                        scene.indices.push(index as u32);\n                    }\n                }\n            }\n\n            let mut material: Material = Default::default();\n\n            if let Some(obj::ObjMaterial::Mtl(mat)) = group.material {\n                if let Some(kd) = mat.kd {\n                    material.albedo = kd;\n                }\n                if let Some(ns) = mat.ns {\n                    material.roughness_exponent = ns;\n                }\n                if let Some(ka) = mat.ka {\n                    material.metalness = ka[0];\n                }\n                if let Some(ks) = mat.ks {\n                    material.specularity = ks[0];\n                }\n            }\n\n            scene\n                .geometries\n                .push((start_index_index..scene.indices.len(), material));\n        }\n    }\n    scene.instances.push((\n        start_vertex_index..scene.vertices.len(),\n        start_geometry_index..scene.geometries.len(),\n    ));\n\n    // dbg!(scene.vertices.len());\n    // dbg!(scene.indices.len());\n    // dbg!(&scene.geometries);\n    // dbg!(&scene.instances);\n}\n\nfn upload_scene_components(\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n    scene: &RawSceneComponents,\n) -> SceneComponents {\n    let geometry_buffer_content = scene\n        .geometries\n        .iter()\n        .map(|(index_range, material)| GeometryEntry {\n            first_index: index_range.start as u32,\n            material: *material,\n            ..Default::default()\n        })\n        .collect::<Vec<_>>();\n\n    let instance_buffer_content = scene\n        .instances\n        .iter()\n        .map(|geometry| InstanceEntry {\n            first_vertex: geometry.0.start as u32,\n            first_geometry: geometry.1.start as u32,\n            last_geometry: geometry.1.end as u32,\n            _pad: 1,\n        })\n        .collect::<Vec<_>>();\n\n    let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Vertices\"),\n        contents: bytemuck::cast_slice(&scene.vertices),\n        usage: wgpu::BufferUsages::VERTEX\n            | wgpu::BufferUsages::STORAGE\n            | wgpu::BufferUsages::BLAS_INPUT,\n    });\n    let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Indices\"),\n        contents: bytemuck::cast_slice(&scene.indices),\n        usage: wgpu::BufferUsages::INDEX\n            | wgpu::BufferUsages::STORAGE\n            | wgpu::BufferUsages::BLAS_INPUT,\n    });\n    let geometries = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Geometries\"),\n        contents: bytemuck::cast_slice(&geometry_buffer_content),\n        usage: wgpu::BufferUsages::STORAGE,\n    });\n    let instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Instances\"),\n        contents: bytemuck::cast_slice(&instance_buffer_content),\n        usage: wgpu::BufferUsages::STORAGE,\n    });\n\n    let (size_descriptors, bottom_level_acceleration_structures): (Vec<_>, Vec<_>) = scene\n        .instances\n        .iter()\n        .map(|(vertex_range, geometry_range)| {\n            let size_desc: Vec<wgpu::BlasTriangleGeometrySizeDescriptor> = (*geometry_range)\n                .clone()\n                .map(|i| wgpu::BlasTriangleGeometrySizeDescriptor {\n                    vertex_format: wgpu::VertexFormat::Float32x3,\n                    vertex_count: vertex_range.end as u32 - vertex_range.start as u32,\n                    index_format: Some(wgpu::IndexFormat::Uint32),\n                    index_count: Some(\n                        scene.geometries[i].0.end as u32 - scene.geometries[i].0.start as u32,\n                    ),\n                    flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n                })\n                .collect();\n\n            let blas = device.create_blas(\n                &wgpu::CreateBlasDescriptor {\n                    label: None,\n                    flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n                },\n                wgpu::BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: size_desc.clone(),\n                },\n            );\n            (size_desc, blas)\n        })\n        .unzip();\n\n    let build_entries: Vec<_> = scene\n        .instances\n        .iter()\n        .zip(size_descriptors.iter())\n        .zip(bottom_level_acceleration_structures.iter())\n        .map(|(((vertex_range, geometry_range), size_desc), blas)| {\n            let triangle_geometries: Vec<_> = size_desc\n                .iter()\n                .zip(geometry_range.clone())\n                .map(|(size, i)| wgpu::BlasTriangleGeometry {\n                    size,\n                    vertex_buffer: &vertices,\n                    first_vertex: vertex_range.start as u32,\n                    vertex_stride: mem::size_of::<Vertex>() as u64,\n                    index_buffer: Some(&indices),\n                    first_index: Some(scene.geometries[i].0.start as u32),\n                    transform_buffer: None,\n                    transform_buffer_offset: None,\n                })\n                .collect();\n\n            wgpu::BlasBuildEntry {\n                blas,\n                geometry: wgpu::BlasGeometries::TriangleGeometries(triangle_geometries),\n            }\n        })\n        .collect();\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    encoder.build_acceleration_structures(build_entries.iter(), iter::empty());\n\n    queue.submit(Some(encoder.finish()));\n\n    SceneComponents {\n        vertices,\n        indices,\n        geometries,\n        instances,\n        bottom_level_acceleration_structures,\n    }\n}\n\nfn load_scene(device: &wgpu::Device, queue: &wgpu::Queue) -> SceneComponents {\n    let mut scene = RawSceneComponents::default();\n\n    load_model(&mut scene, \"/skybox/models/rustacean-3d.obj\");\n    load_model(&mut scene, \"/ray_scene/cube.obj\");\n\n    upload_scene_components(device, queue, &scene)\n}\n\nstruct Example {\n    uniforms: Uniforms,\n    uniform_buf: wgpu::Buffer,\n    tlas: wgpu::Tlas,\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    scene_components: SceneComponents,\n    animation_timer: utils::AnimationTimer,\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_RAY_QUERY\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let side_count = 8;\n\n        let scene_components = load_scene(device, queue);\n\n        let uniforms = {\n            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);\n            let proj = Mat4::perspective_rh(\n                59.0_f32.to_radians(),\n                config.width as f32 / config.height as f32,\n                0.001,\n                1000.0,\n            );\n\n            Uniforms {\n                view_inverse: view.inverse(),\n                proj_inverse: proj.inverse(),\n            }\n        };\n\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        let tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            max_instances: side_count * side_count,\n        });\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let bind_group_layout = pipeline.get_bind_group_layout(0);\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 5,\n                    resource: tlas.as_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: scene_components.vertices.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: scene_components.indices.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 3,\n                    resource: scene_components.geometries.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 4,\n                    resource: scene_components.instances.as_entire_binding(),\n                },\n            ],\n        });\n\n        Example {\n            uniforms,\n            uniform_buf,\n            tlas,\n            pipeline,\n            bind_group,\n            scene_components,\n            animation_timer: utils::AnimationTimer::default(),\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        let proj = Mat4::perspective_rh(\n            59.0_f32.to_radians(),\n            config.width as f32 / config.height as f32,\n            0.001,\n            1000.0,\n        );\n\n        self.uniforms.proj_inverse = proj.inverse();\n\n        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        // scene update\n        {\n            let dist = 3.5;\n\n            let side_count = 2;\n\n            let anim_time = self.animation_timer.time();\n\n            for x in 0..side_count {\n                for y in 0..side_count {\n                    let instance = self.tlas.index_mut(x + y * side_count);\n\n                    let blas_index = (x + y)\n                        % self\n                            .scene_components\n                            .bottom_level_acceleration_structures\n                            .len();\n\n                    let x = x as f32 / (side_count - 1) as f32;\n                    let y = y as f32 / (side_count - 1) as f32;\n                    let x = x * 2.0 - 1.0;\n                    let y = y * 2.0 - 1.0;\n\n                    let transform = Mat4::from_rotation_translation(\n                        Quat::from_euler(\n                            glam::EulerRot::XYZ,\n                            anim_time * 0.5 * 0.342,\n                            anim_time * 0.5 * 0.254,\n                            anim_time * 0.5 * 0.832 + PI,\n                        ),\n                        Vec3 {\n                            x: x * dist,\n                            y: y * dist,\n                            z: -14.0,\n                        },\n                    );\n                    let transform = transform.transpose().to_cols_array()[..12]\n                        .try_into()\n                        .unwrap();\n                    *instance = Some(wgpu::TlasInstance::new(\n                        &self.scene_components.bottom_level_acceleration_structures[blas_index],\n                        transform,\n                        blas_index as u32,\n                        0xff,\n                    ));\n                }\n            }\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, Some(&self.bind_group), &[]);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray_scene\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_scene\",\n    image_path: \"/examples/features/src/ray_scene/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        // https://github.com/gfx-rs/wgpu/issues/9100\n        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_scene/shader.wgsl",
    "content": "enable wgpu_ray_query;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\n/*\nThe contents of the RayQuery struct are roughly as follows\nlet RAY_FLAG_NONE = 0x00u;\nlet RAY_FLAG_OPAQUE = 0x01u;\nlet RAY_FLAG_NO_OPAQUE = 0x02u;\nlet RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u;\nlet RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u;\nlet RAY_FLAG_CULL_BACK_FACING = 0x10u;\nlet RAY_FLAG_CULL_FRONT_FACING = 0x20u;\nlet RAY_FLAG_CULL_OPAQUE = 0x40u;\nlet RAY_FLAG_CULL_NO_OPAQUE = 0x80u;\nlet RAY_FLAG_SKIP_TRIANGLES = 0x100u;\nlet RAY_FLAG_SKIP_AABBS = 0x200u;\n\nlet RAY_QUERY_INTERSECTION_NONE = 0u;\nlet RAY_QUERY_INTERSECTION_TRIANGLE = 1u;\nlet RAY_QUERY_INTERSECTION_GENERATED = 2u;\nlet RAY_QUERY_INTERSECTION_AABB = 3u;\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    t_min: f32,\n    t_max: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    kind: u32,\n    t: f32,\n    instance_custom_data: u32,\n    instance_index: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: bool,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n*/\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n};\n\nstruct Vertex {\n    pos: vec3<f32>,\n    normal: vec3<f32>,\n    uv: vec2<f32>,\n};\n\n\nstruct Instance {\n    first_vertex: u32,\n    first_geometry: u32,\n    last_geometry: u32,\n    _pad: u32\n};\n\nstruct Material{\n    roughness_exponent: f32,\n    metalness: f32,\n    specularity: f32,\n    albedo: vec3<f32>\n}\n\nstruct Geometry {\n    first_index: u32,\n    material: Material,\n};\n\n\n@group(0) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(1)\nvar<storage, read> vertices: array<Vertex>;\n\n@group(0) @binding(2)\nvar<storage, read> indices: array<u32>;\n\n@group(0) @binding(3)\nvar<storage, read> geometries: array<Geometry>;\n\n@group(0) @binding(4)\nvar<storage, read> instances: array<Instance>;\n\n@group(0) @binding(5)\nvar acc_struct: acceleration_structure;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n\n    var color =  vec4<f32>(vertex.tex_coords, 0.0, 1.0);\n\n\tlet d = vertex.tex_coords * 2.0 - 1.0;\n\n\tlet origin = (uniforms.view_inv * vec4<f32>(0.0,0.0,0.0,1.0)).xyz;\n\tlet temp = uniforms.proj_inv * vec4<f32>(d.x, d.y, 1.0, 1.0);\n\tlet direction = (uniforms.view_inv * vec4<f32>(normalize(temp.xyz), 0.0)).xyz;\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) {\n        let instance = instances[intersection.instance_custom_data];\n        let geometry = geometries[intersection.geometry_index + instance.first_geometry];\n\n        let index_offset = geometry.first_index;\n        let vertex_offset = instance.first_vertex;\n\n        let first_index_index = intersection.primitive_index * 3u + index_offset;\n\n        let v_0 = vertices[vertex_offset+indices[first_index_index+0u]];\n        let v_1 = vertices[vertex_offset+indices[first_index_index+1u]];\n        let v_2 = vertices[vertex_offset+indices[first_index_index+2u]];\n\n        let bary = vec3<f32>(1.0 - intersection.barycentrics.x - intersection.barycentrics.y, intersection.barycentrics);\n\n        let pos = v_0.pos * bary.x + v_1.pos * bary.y + v_2.pos * bary.z;\n        let normal_raw = v_0.normal * bary.x + v_1.normal * bary.y + v_2.normal * bary.z;\n        let uv = v_0.uv * bary.x + v_1.uv * bary.y + v_2.uv * bary.z;\n\n        let normal = normalize(normal_raw);\n\n        let material = geometry.material;\n\n        color = vec4<f32>(material.albedo, 1.0);\n\n        if(intersection.instance_custom_data == 1u){\n            color = vec4<f32>(normal, 1.0);\n        }\n    }\n\n    return color;\n}\n"
  },
  {
    "path": "examples/features/src/ray_shadows/README.md",
    "content": "# ray-shadows\n\nThis example renders a ray traced shadow with hardware acceleration.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples ray_shadows\n```\n\n## Screenshots\n\n![Shadow example](screenshot.png)\n"
  },
  {
    "path": "examples/features/src/ray_shadows/mod.rs",
    "content": "use std::{borrow::Cow, iter, mem};\n\nuse bytemuck::{Pod, Zeroable};\nuse glam::{Mat4, Vec3};\nuse wgpu::util::DeviceExt;\nuse wgpu::{vertex_attr_array, IndexFormat, VertexBufferLayout};\n\nuse crate::utils;\n\n// from cube\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 3],\n    _normal: [f32; 3],\n}\n\nfn vertex(pos: [f32; 3], normal: [f32; 3]) -> Vertex {\n    Vertex {\n        _pos: pos,\n        _normal: normal,\n    }\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // base\n        vertex([-1.0, 0.0, -1.0], [0.0, 1.0, 0.0]),\n        vertex([-1.0, 0.0, 1.0], [0.0, 1.0, 0.0]),\n        vertex([1.0, 0.0, -1.0], [0.0, 1.0, 0.0]),\n        vertex([1.0, 0.0, 1.0], [0.0, 1.0, 0.0]),\n        //shadow caster\n        vertex([-(1.0 / 3.0), 0.0, 1.0], [0.0, 0.0, 1.0]),\n        vertex([-(1.0 / 3.0), 2.0 / 3.0, 1.0], [0.0, 0.0, 1.0]),\n        vertex([1.0 / 3.0, 0.0, 1.0], [0.0, 0.0, 1.0]),\n        vertex([1.0 / 3.0, 2.0 / 3.0, 1.0], [0.0, 0.0, 1.0]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 1, //base\n        4, 5, 6, 6, 7, 5,\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n    vertex: Mat4,\n}\n\nstruct Example {\n    uniforms: Uniforms,\n    uniform_buf: wgpu::Buffer,\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    animation_timer: utils::AnimationTimer,\n}\n\nconst CAM_LOOK_AT: Vec3 = Vec3::new(0.0, 1.0, -1.5);\n\nfn create_matrix(config: &wgpu::SurfaceConfiguration) -> Uniforms {\n    let view = Mat4::look_at_rh(CAM_LOOK_AT, Vec3::ZERO, Vec3::Y);\n    let proj = Mat4::perspective_rh(\n        59.0_f32.to_radians(),\n        config.width as f32 / config.height as f32,\n        0.1,\n        1000.0,\n    );\n\n    Uniforms {\n        view_inverse: view.inverse(),\n        proj_inverse: proj.inverse(),\n        vertex: (proj * view),\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::IMMEDIATES\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits {\n            max_immediate_size: 16,\n            ..wgpu::Limits::default()\n        }\n        .using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let uniforms = create_matrix(config);\n\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Uniform Buffer\"),\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n            vertex_format: wgpu::VertexFormat::Float32x3,\n            vertex_count: vertex_data.len() as u32,\n            index_format: Some(wgpu::IndexFormat::Uint16),\n            index_count: Some(index_data.len() as u32),\n            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n        };\n\n        let blas = device.create_blas(\n            &wgpu::CreateBlasDescriptor {\n                label: None,\n                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            },\n            wgpu::BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_geo_size_desc.clone()],\n            },\n        );\n\n        let mut tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n            max_instances: 1,\n        });\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"shader.wgsl\"))),\n        });\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::AccelerationStructure {\n                        vertex_return: false,\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 16,\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[VertexBufferLayout {\n                    array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,\n                    step_mode: Default::default(),\n                    attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x3],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        tlas[0] = Some(wgpu::TlasInstance::new(\n            &blas,\n            [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],\n            0,\n            0xFF,\n        ));\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(\n            iter::once(&wgpu::BlasBuildEntry {\n                blas: &blas,\n                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![\n                    wgpu::BlasTriangleGeometry {\n                        size: &blas_geo_size_desc,\n                        vertex_buffer: &vertex_buf,\n                        first_vertex: 0,\n                        vertex_stride: mem::size_of::<Vertex>() as u64,\n                        index_buffer: Some(&index_buf),\n                        first_index: Some(0),\n                        transform_buffer: None,\n                        transform_buffer_offset: None,\n                    },\n                ]),\n            }),\n            iter::once(&tlas),\n        );\n\n        queue.submit(Some(encoder.finish()));\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: tlas.as_binding(),\n                },\n            ],\n        });\n\n        let animation_timer = utils::AnimationTimer::default();\n\n        Example {\n            uniforms,\n            uniform_buf,\n            vertex_buf,\n            index_buf,\n            pipeline,\n            bind_group,\n            animation_timer,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        self.uniforms = create_matrix(config);\n\n        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));\n        queue.submit(None);\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        //device.push_error_scope(wgpu::ErrorFilter::Validation);\n        const LIGHT_DISTANCE: f32 = 5.0;\n        const TIME_SCALE: f32 = -0.2;\n        const INITIAL_TIME: f32 = 1.0;\n        let time = self.animation_timer.time();\n        let cos = (time * TIME_SCALE + INITIAL_TIME).cos() * LIGHT_DISTANCE;\n        let sin = (time * TIME_SCALE + INITIAL_TIME).sin() * LIGHT_DISTANCE;\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.1,\n                            b: 0.1,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, Some(&self.bind_group), &[]);\n            rpass.set_immediates(0, &0.0_f32.to_ne_bytes());\n            rpass.set_immediates(4, &cos.to_ne_bytes());\n            rpass.set_immediates(8, &sin.to_ne_bytes());\n            rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));\n            rpass.set_index_buffer(self.index_buf.slice(..), IndexFormat::Uint16);\n            rpass.draw_indexed(0..12, 0, 0..1);\n        }\n        queue.submit(Some(encoder.finish()));\n        device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray-shadows\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_shadows\",\n    image_path: \"/examples/features/src/ray_shadows/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        // https://github.com/gfx-rs/wgpu/issues/9100\n        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_shadows/shader.wgsl",
    "content": "enable wgpu_ray_query;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n    @location(1) normal: vec3<f32>,\n    @location(2) world_position: vec3<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32, @location(0) position: vec3<f32>, @location(1) normal: vec3<f32>,) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.tex_coords = tc;\n    result.position = uniforms.vertex * vec4<f32>(position, 1.0);\n    result.normal = normal;\n    result.world_position = position;\n    return result;\n}\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n    vertex: mat4x4<f32>,\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(1)\nvar acc_struct: acceleration_structure;\n\nstruct ImmediateData {\n    light: vec3<f32>,\n    padding: f32,\n}\nvar<immediate> pc: ImmediateData;\n\nconst SURFACE_BRIGHTNESS = 0.5;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    let camera = (uniforms.view_inv * vec4<f32>(0.0,0.0,0.0,1.0)).xyz;\n    var color = vec4<f32>(vertex.tex_coords, 0.0, 1.0);\n\n\tlet d = vertex.tex_coords * 2.0 - 1.0;\n\n\tlet origin = vertex.world_position;\n\tlet direction = normalize(pc.light - vertex.world_position);\n\n\tvar normal: vec3<f32>;\n\tlet dir_cam = normalize(camera - vertex.world_position);\n\tif (dot(dir_cam, vertex.normal) < 0.0) {\n\t    normal = -vertex.normal;\n\t} else {\n\t    normal = vertex.normal;\n\t}\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.0001, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) {\n        color = vec4<f32>(vec3<f32>(0.1) * SURFACE_BRIGHTNESS, 1.0);\n    } else {\n        color = vec4<f32>(vec3<f32>(max(dot(direction, normal), 0.1)) * SURFACE_BRIGHTNESS, 1.0);\n    }\n\n    return color;\n}\n"
  },
  {
    "path": "examples/features/src/ray_traced_triangle/README.md",
    "content": "# ray-traced-triangle\n\nThis example renders three triangles with hardware acceleration.\nThis is the same scene set-up as hal ray-traced triangle\n\n## To Run\n\n```\ncargo run --bin wgpu-examples ray_traced_triangle\n```\n\n## Screenshots\n\n![Triangle example](screenshot.png)\n"
  },
  {
    "path": "examples/features/src/ray_traced_triangle/blit.wgsl",
    "content": "// same as ray_cube_compute/blit.wgsl\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n};\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    . \n// |              * .\n// |***************\n// |            . 1,-1 \n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    let tc = vec2<f32>(\n        f32(x) * 2.0,\n        f32(y) * 2.0\n    );\n    result.position = vec4<f32>(\n        tc.x * 2.0 - 1.0,\n        1.0 - tc.y * 2.0,\n        0.0, 1.0\n    );\n    result.tex_coords = tc;\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n@group(0)\n@binding(1)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_color, r_sampler, vertex.tex_coords);\n}\n"
  },
  {
    "path": "examples/features/src/ray_traced_triangle/mod.rs",
    "content": "use glam::{Mat4, Vec3};\nuse std::mem;\nuse wgpu::util::{BufferInitDescriptor, DeviceExt};\nuse wgpu::{include_wgsl, BufferUsages, IndexFormat, SamplerDescriptor};\nuse wgpu::{\n    AccelerationStructureFlags, AccelerationStructureUpdateMode, BlasBuildEntry, BlasGeometries,\n    BlasGeometrySizeDescriptors, BlasTriangleGeometry, BlasTriangleGeometrySizeDescriptor,\n    CreateBlasDescriptor, CreateTlasDescriptor, Tlas, TlasInstance,\n};\n\nuse crate::utils;\n\nstruct Example {\n    tlas: Tlas,\n    compute_pipeline: wgpu::ComputePipeline,\n    blit_pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    blit_bind_group: wgpu::BindGroup,\n    storage_texture: wgpu::Texture,\n    animation_timer: utils::AnimationTimer,\n}\n\n#[repr(C)]\n#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]\nstruct Uniforms {\n    view_inverse: Mat4,\n    proj_inverse: Mat4,\n}\n\nimpl crate::framework::Example for Example {\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::EXPERIMENTAL_RAY_QUERY\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n    }\n\n    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {\n        wgpu::DownlevelCapabilities {\n            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            ..Default::default()\n        }\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let shader = device.create_shader_module(include_wgsl!(\"shader.wgsl\"));\n\n        let blit_shader = device.create_shader_module(include_wgsl!(\"blit.wgsl\"));\n\n        let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bgl for shader.wgsl\"),\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::StorageTexture {\n                        access: wgpu::StorageTextureAccess::WriteOnly,\n                        format: wgpu::TextureFormat::Rgba8Unorm,\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 2,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::AccelerationStructure {\n                        vertex_return: false,\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n        let blit_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bgl for blit.wgsl\"),\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,\n                    ty: wgpu::BindingType::Texture {\n                        sample_type: wgpu::TextureSampleType::Float { filterable: false },\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,\n                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),\n                    count: None,\n                },\n            ],\n        });\n\n        let vertices: [f32; 9] = [1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 0.0, -1.0, 0.0];\n\n        let indices: [u32; 3] = [0, 1, 2];\n\n        let vertex_buffer = device.create_buffer_init(&BufferInitDescriptor {\n            label: Some(\"vertex buffer\"),\n            contents: bytemuck::cast_slice(&vertices),\n            usage: BufferUsages::BLAS_INPUT,\n        });\n\n        let index_buffer = device.create_buffer_init(&BufferInitDescriptor {\n            label: Some(\"index buffer\"),\n            contents: bytemuck::cast_slice(&indices),\n            usage: BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_size_desc = BlasTriangleGeometrySizeDescriptor {\n            vertex_format: wgpu::VertexFormat::Float32x3,\n            // 3 coordinates per vertex\n            vertex_count: (vertices.len() / 3) as u32,\n            index_format: Some(IndexFormat::Uint32),\n            index_count: Some(indices.len() as u32),\n            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n        };\n\n        let blas = device.create_blas(\n            &CreateBlasDescriptor {\n                label: None,\n                flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: AccelerationStructureUpdateMode::Build,\n            },\n            BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_size_desc.clone()],\n            },\n        );\n\n        let mut tlas = device.create_tlas(&CreateTlasDescriptor {\n            label: None,\n            max_instances: 3,\n            flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        });\n\n        tlas[0] = Some(TlasInstance::new(\n            &blas,\n            Mat4::from_translation(Vec3 {\n                x: 0.0,\n                y: 0.0,\n                z: 0.0,\n            })\n            .transpose()\n            .to_cols_array()[..12]\n                .try_into()\n                .unwrap(),\n            0,\n            0xff,\n        ));\n\n        tlas[1] = Some(TlasInstance::new(\n            &blas,\n            Mat4::from_translation(Vec3 {\n                x: -1.0,\n                y: -1.0,\n                z: -2.0,\n            })\n            .transpose()\n            .to_cols_array()[..12]\n                .try_into()\n                .unwrap(),\n            0,\n            0xff,\n        ));\n\n        tlas[2] = Some(TlasInstance::new(\n            &blas,\n            Mat4::from_translation(Vec3 {\n                x: 1.0,\n                y: -1.0,\n                z: -2.0,\n            })\n            .transpose()\n            .to_cols_array()[..12]\n                .try_into()\n                .unwrap(),\n            0,\n            0xff,\n        ));\n\n        let uniforms = {\n            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);\n            let proj = Mat4::perspective_rh(59.0_f32.to_radians(), 1.0, 0.001, 1000.0);\n\n            Uniforms {\n                view_inverse: view.inverse(),\n                proj_inverse: proj.inverse(),\n            }\n        };\n\n        let uniform_buffer = device.create_buffer_init(&BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&[uniforms]),\n            usage: BufferUsages::UNIFORM,\n        });\n\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        encoder.build_acceleration_structures(\n            Some(&BlasBuildEntry {\n                blas: &blas,\n                geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n                    size: &blas_size_desc,\n                    vertex_buffer: &vertex_buffer,\n                    first_vertex: 0,\n                    vertex_stride: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,\n                    // in this case since one triangle gets no compression from an index buffer `index_buffer` and `first_index` could be `None`.\n                    index_buffer: Some(&index_buffer),\n                    first_index: Some(0),\n                    transform_buffer: None,\n                    transform_buffer_offset: None,\n                }]),\n            }),\n            Some(&tlas),\n        );\n\n        queue.submit(Some(encoder.finish()));\n\n        let storage_tex = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let sampler = device.create_sampler(&SamplerDescriptor {\n            label: None,\n            address_mode_u: Default::default(),\n            address_mode_v: Default::default(),\n            address_mode_w: Default::default(),\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            lod_min_clamp: 1.0,\n            lod_max_clamp: 1.0,\n            compare: None,\n            anisotropy_clamp: 1,\n            border_color: None,\n        });\n\n        let compute_pipeline_layout =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"pipeline layout for shader.wgsl\"),\n                bind_group_layouts: &[Some(&bgl)],\n                immediate_size: 0,\n            });\n\n        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"pipeline for shader.wgsl\"),\n            layout: Some(&compute_pipeline_layout),\n            module: &shader,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        let blit_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline layout for blit.wgsl\"),\n            bind_group_layouts: &[Some(&blit_bgl)],\n            immediate_size: 0,\n        });\n\n        let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"pipeline for blit.wgsl\"),\n            layout: Some(&blit_pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &blit_shader,\n                entry_point: None,\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: Default::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &blit_shader,\n                entry_point: None,\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: config.format,\n                    blend: None,\n                    write_mask: Default::default(),\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(\"bind group for shader.wgsl\"),\n            layout: &bgl,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buffer.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(\n                        &storage_tex.create_view(&wgpu::TextureViewDescriptor::default()),\n                    ),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::AccelerationStructure(&tlas),\n                },\n            ],\n        });\n\n        let blit_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(\"bind group for blit.wgsl\"),\n            layout: &blit_bgl,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(\n                        &storage_tex.create_view(&wgpu::TextureViewDescriptor::default()),\n                    ),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        Self {\n            tlas,\n            compute_pipeline,\n            blit_pipeline,\n            bind_group,\n            blit_bind_group,\n            storage_texture: storage_tex,\n            animation_timer: utils::AnimationTimer::default(),\n        }\n    }\n\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        self.tlas[0].as_mut().unwrap().transform =\n            Mat4::from_rotation_y(self.animation_timer.time())\n                .transpose()\n                .to_cols_array()[..12]\n                .try_into()\n                .unwrap();\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.build_acceleration_structures(None, Some(&self.tlas));\n\n        {\n            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&self.compute_pipeline);\n            cpass.set_bind_group(0, Some(&self.bind_group), &[]);\n            cpass.dispatch_workgroups(\n                self.storage_texture.width() / 8,\n                self.storage_texture.height() / 8,\n                1,\n            );\n        }\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.blit_pipeline);\n            rpass.set_bind_group(0, Some(&self.blit_bind_group), &[]);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"ray-traced-triangle\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"ray_traced_triangle\",\n    image_path: \"/examples/features/src/ray_traced_triangle/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        // https://github.com/gfx-rs/wgpu/issues/9100\n        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/ray_traced_triangle/shader.wgsl",
    "content": "// duplicate of hal's ray-traced triangle shader\n\nenable wgpu_ray_query;\n\nstruct Uniforms {\n    view_inv: mat4x4<f32>,\n    proj_inv: mat4x4<f32>,\n};\n@group(0) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\n@group(0) @binding(1)\nvar output: texture_storage_2d<rgba8unorm, write>;\n\n@group(0) @binding(2)\nvar acc_struct: acceleration_structure;\n\n@compute @workgroup_size(8, 8)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    let target_size = textureDimensions(output);\n\n    let pixel_center = vec2<f32>(global_id.xy) + vec2<f32>(0.5);\n    let in_uv = pixel_center / vec2<f32>(target_size.xy);\n    let d = in_uv * 2.0 - 1.0;\n\n    let origin = (uniforms.view_inv * vec4<f32>(0.0, 0.0, 0.0, 1.0)).xyz;\n    let temp = uniforms.proj_inv * vec4<f32>(d.x, d.y, 1.0, 1.0);\n    let direction = (uniforms.view_inv * vec4<f32>(normalize(temp.xyz), 0.0)).xyz;\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction));\n    rayQueryProceed(&rq);\n\n    var color = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    if intersection.kind != RAY_QUERY_INTERSECTION_NONE {\n        color = vec4<f32>(intersection.barycentrics, 1.0 - intersection.barycentrics.x - intersection.barycentrics.y, 1.0);\n    }\n\n    textureStore(output, global_id.xy, color);\n}"
  },
  {
    "path": "examples/features/src/render_to_texture/README.md",
    "content": "# render_to_texture\n\nSimilar to hello-triangle but instead of rendering to a window or canvas, renders to a texture that is then output as an image like the storage-texture example.\n\nIf all goes well, the end result should look familiarly like hello-triangle with its red triangle on a green background.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples render_to_texture\n```\n"
  },
  {
    "path": "examples/features/src/render_to_texture/mod.rs",
    "content": "#[cfg(not(target_arch = \"wasm32\"))]\nuse crate::utils::output_image_native;\n#[cfg(target_arch = \"wasm32\")]\nuse crate::utils::output_image_wasm;\n\nconst TEXTURE_DIMS: (usize, usize) = (512, 512);\n\nasync fn run(_path: Option<String>) {\n    // This will later store the raw pixel value data locally. We'll create it now as\n    // a convenient size reference.\n    let mut texture_data = Vec::<u8>::with_capacity(TEXTURE_DIMS.0 * TEXTURE_DIMS.1 * 4);\n\n    let instance = wgpu::Instance::default();\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .unwrap();\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: wgpu::Features::empty(),\n            required_limits: wgpu::Limits::downlevel_defaults(),\n            experimental_features: wgpu::ExperimentalFeatures::disabled(),\n            memory_hints: wgpu::MemoryHints::MemoryUsage,\n            trace: wgpu::Trace::Off,\n        })\n        .await\n        .unwrap();\n\n    let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    let render_target = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: TEXTURE_DIMS.0 as u32,\n            height: TEXTURE_DIMS.1 as u32,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8UnormSrgb,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],\n    });\n    let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: texture_data.capacity() as u64,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n            buffers: &[],\n        },\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::TextureFormat::Rgba8UnormSrgb.into())],\n        }),\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        multiview_mask: None,\n        cache: None,\n    });\n\n    log::info!(\"Wgpu context set up.\");\n\n    //-----------------------------------------------\n\n    let texture_view = render_target.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &texture_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            occlusion_query_set: None,\n            timestamp_writes: None,\n            multiview_mask: None,\n        });\n        render_pass.set_pipeline(&pipeline);\n        render_pass.draw(0..3, 0..1);\n    }\n    // The texture now contains our rendered image\n    command_encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &render_target,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &output_staging_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                // This needs to be a multiple of 256. Normally we would need to pad\n                // it but we here know it will work out anyways.\n                bytes_per_row: Some((TEXTURE_DIMS.0 * 4) as u32),\n                rows_per_image: Some(TEXTURE_DIMS.1 as u32),\n            },\n        },\n        wgpu::Extent3d {\n            width: TEXTURE_DIMS.0 as u32,\n            height: TEXTURE_DIMS.1 as u32,\n            depth_or_array_layers: 1,\n        },\n    );\n    queue.submit(Some(command_encoder.finish()));\n    log::info!(\"Commands submitted.\");\n\n    //-----------------------------------------------\n\n    // Time to get our image.\n    let buffer_slice = output_staging_buffer.slice(..);\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    receiver.recv_async().await.unwrap().unwrap();\n    log::info!(\"Output buffer mapped.\");\n    {\n        let view = buffer_slice.get_mapped_range();\n        texture_data.extend_from_slice(&view[..]);\n    }\n    log::info!(\"Image data copied to local.\");\n    output_staging_buffer.unmap();\n\n    #[cfg(not(target_arch = \"wasm32\"))]\n    output_image_native(texture_data.to_vec(), TEXTURE_DIMS, _path.unwrap());\n    #[cfg(target_arch = \"wasm32\")]\n    output_image_wasm(texture_data.to_vec(), TEXTURE_DIMS);\n    log::info!(\"Done.\");\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n\n        let path = std::env::args()\n            .nth(2)\n            .unwrap_or_else(|| \"please_don't_git_push_me.png\".to_string());\n        pollster::block_on(run(Some(path)));\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n        wasm_bindgen_futures::spawn_local(run(None));\n    }\n}\n"
  },
  {
    "path": "examples/features/src/render_to_texture/shader.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {\n    var vertices = array<vec4<f32>, 3>(\n        vec4<f32>(0.0, 1.0, 0.0, 1.0),\n        vec4<f32>(-1.0, -1.0, 0.0, 1.0),\n        vec4<f32>(1.0, -1.0, 0.0, 1.0)\n    );\n    return vertices[in_vertex_index];\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/render_with_compute/mod.rs",
    "content": "//! This renders to the screen with compute shaders. Note that due to limitations in Firefox,\n//! the wait will cause FPS to be capped at 10 when running on webgpu on Firefox. It is\n//! therefore not recommended to use this code, at least until\n//! <https://bugzilla.mozilla.org/show_bug.cgi?id=1870699> (and possibly further work) is resolved.\n\nuse std::time::Instant;\n\n#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]\n#[repr(C)]\n#[repr(align(16))]\nstruct GlobalParams {\n    time: f32,\n    frame: f32,\n    _padding: [u8; 8],\n}\n\npub struct Example {\n    pipeline: wgpu::ComputePipeline,\n    texture_view: wgpu::TextureView,\n    global_params: wgpu::Buffer,\n    bg: wgpu::BindGroup,\n    bgl: wgpu::BindGroupLayout,\n    blitter: wgpu::util::TextureBlitter,\n    frame_count: u32,\n    start_time: Option<Instant>,\n}\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let sm = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n        let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::StorageTexture {\n                        access: wgpu::StorageTextureAccess::WriteOnly,\n                        format: wgpu::TextureFormat::Rgba8Unorm,\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                    },\n                    count: None,\n                },\n            ],\n        });\n        let ppl = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&ppl),\n            module: &sm,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n        let blitter = wgpu::util::TextureBlitter::new(device, config.format);\n        let global_params = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: size_of::<GlobalParams>() as u64,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let (texture_view, bg) =\n            create_tv_and_bg(device, &bgl, &global_params, config.width, config.height);\n        Self {\n            pipeline,\n            texture_view,\n            global_params,\n            bg,\n            bgl,\n            blitter,\n            frame_count: 0,\n            start_time: None,\n        }\n    }\n\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits {\n            max_storage_textures_per_shader_stage: 1,\n            ..Default::default()\n        }\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        let (texture_view, bg) = create_tv_and_bg(\n            device,\n            &self.bgl,\n            &self.global_params,\n            config.width,\n            config.height,\n        );\n        self.bg = bg;\n        self.texture_view = texture_view;\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let now = Instant::now();\n        let start = *self.start_time.get_or_insert(now);\n        let time_since_start = (now - start).as_secs_f32();\n        queue.write_buffer(\n            &self.global_params,\n            0,\n            bytemuck::bytes_of(&GlobalParams {\n                time: time_since_start,\n                frame: self.frame_count as f32,\n                _padding: [0; 8],\n            }),\n        );\n        let mut encoder = device.create_command_encoder(&Default::default());\n\n        {\n            let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            pass.set_pipeline(&self.pipeline);\n            pass.set_bind_group(0, &self.bg, &[]);\n            const SHADER_WORKGROUP_DIM: u32 = 16;\n            pass.dispatch_workgroups(\n                self.texture_view\n                    .texture()\n                    .width()\n                    .div_ceil(SHADER_WORKGROUP_DIM),\n                self.texture_view\n                    .texture()\n                    .height()\n                    .div_ceil(SHADER_WORKGROUP_DIM),\n                1,\n            );\n        }\n        self.blitter\n            .copy(device, &mut encoder, &self.texture_view, view);\n\n        queue.submit([encoder.finish()]);\n        device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n        self.frame_count += 1;\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {}\n}\n\nfn create_tv_and_bg(\n    device: &wgpu::Device,\n    bgl: &wgpu::BindGroupLayout,\n    global_params: &wgpu::Buffer,\n    width: u32,\n    height: u32,\n) -> (wgpu::TextureView, wgpu::BindGroup) {\n    let texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width,\n            height,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    });\n    let view = texture.create_view(&Default::default());\n    let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: bgl,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(global_params.as_entire_buffer_binding()),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: wgpu::BindingResource::TextureView(&view),\n            },\n        ],\n    });\n    (view, bg)\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"render-with-compute\");\n}\n"
  },
  {
    "path": "examples/features/src/render_with_compute/shader.wgsl",
    "content": "// This shader has gone through several stages of derived work:\n//\n// 1. Originally authored by a user named `dynamite`, distributed as a [Shadertoy page] under the\n//    [CC BY-NC-SA 3.0 license].\n//\n//    [Shadertoy page]: https://www.shadertoy.com/view/XdlSDs\n//    [CC BY-NC-SA 3.0 license]: https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en\n//\n// 2. Ported to Slang for the Slang Playground project as [the `circle.slang` demo].\n//\n//    [the `circle.slang` demo]: https://github.com/shader-slang/slang-playground/blob/60f0ca29d9952d3cb598936511288f00451ced34/public/demos/circle.slang\n//\n// 3. Compiled to WGSL via Slang 2026.4 on the Slang playground.\n//\n//    For convenience, both the ported shader and the WGSL output can be found at:\n//    <https://shader-slang.org/slang-playground/?target=WGSL&code=eJx9VNtu2kAQfcZfMcrTmsDa4JBKpqlEIJWQkjYqUW9RFC14jVeyvdZ6zaVR_r2za-5Ji7AFc86cOTsztufBAEqRFSmHMmERV1BIpXkEsZIZJFoXZeh5y-WS1rCWazqTmbcQfOn9jNLJqKSOhyqVTqQKIVrnLBOam9itmPG85CEMh3D9q_1l2J4MIKD-TnamONNiwVEwk3lJpZp7aZ1UetN1O5-1S-ZhhhdxHlGeOw5aRX9QpGw9V7LKo_42pHiO_kQ-7zvO4x4Pw4fx3c2TU-UiliqDOJVMgxYZP-V9_ja4u3kej065sWKWrNcFZ6lgJaDjLlzVcLd_DARbIDgBLrbAhSlcd5Oc4dGLSvMz98l5zKtMJ9iSqCSdyxbYy8QPTQ4Ht7dhOBn_vnn--pmcyUpj-gNf6UpZkYUUEYiMzfkdEzmpRK67EImyYHqWPFj18SiEyffn0UnQdV4cwE-k2PJerHhKTtNaQKxciXPj-UT84S5cfYIXp9GwHSnwhG-S6GoNTehSP4b2QSaGXfCA2I64B_F1H-U2Q2LVtmd0zvW9IG4ttacwJDDN8i4p6KpV0LW7xxRiKc_nOiGF2_Tph15_67RaIGZ-EYYmsE4L1EGmNmXN1BG89GmMA2s0PA89gE44zGQq1Y68GsrUHLxa0BUeUXuBcRnULjdgnMmIsGlJzH-3ZdDY3bgJAB8cDMtK1aYC4tNurwX7u6XiV8RgFeAjdKjvYsT0vrHLpwrOrwyEPgyvf4zODbqNv-LF05LDgWj3QNSG2lbtPZl_Fpm-V-RUtPtGdPpfUXUsaqdhF7WeBUw5yzBox0pQHAdQLVzU6hwui2H9EJFODAv3Ac7BlPFpD_kzWdYjbELHtwpm_ZrY_46FU5YVZlulIj2Ez7csk6dd1wzKb9mg65omNppgBm6OhGse-BtTuKFHg79GT9ux7_xZhuL4TOf2zUEI2VDNau3agkXNGiD5FW-vfwGjSbCy>\n\n@binding(1) @group(0) var outputTexture_0 : texture_storage_2d<rgba8unorm, write>;\n\nstruct GlobalParams_std140_0\n{\n    @align(16) time_0 : f32,\n    @align(4) frame_0 : f32,\n};\n\n@binding(0) @group(0) var<uniform> globalParams_0 : GlobalParams_std140_0;\nstruct imageMain_slang_Lambda_imageMain_1_0\n{\n     dispatchThreadID_0 : vec2<u32>,\n};\n\nfn imageMain_slang_Lambda_imageMain_1_x24init_0( dispatchThreadID_1 : vec2<u32>) -> imageMain_slang_Lambda_imageMain_1_0\n{\n    var _S1 : imageMain_slang_Lambda_imageMain_1_0;\n    _S1.dispatchThreadID_0 = dispatchThreadID_1;\n    return _S1;\n}\n\nfn float_getPi_0() -> f32\n{\n    return 3.14159274101257324f;\n}\n\nfn imageMain_slang_Lambda_imageMain_1_x28x29_0( this_0 : imageMain_slang_Lambda_imageMain_1_0,  screenSize_0 : vec2<i32>) -> vec4<f32>\n{\n    var _S2 : vec2<f32> = vec2<f32>(2.0f);\n    var p_0 : vec2<f32> = (vec2<f32>(this_0.dispatchThreadID_0.xy) * _S2 - vec2<f32>(screenSize_0.xy)) / vec2<f32>(f32(screenSize_0.y));\n    var tau_0 : f32 = float_getPi_0() * 2.0f;\n    var _S3 : f32 = atan2(p_0.x, p_0.y) / tau_0;\n    var uv_0 : vec2<f32> = vec2<f32>(_S3, length(p_0) * 0.75f);\n    var t_0 : f32 = globalParams_0.frame_0 / 60.0f;\n    var xCol_0 : f32 = ((((abs((_S3 - t_0 / 3.0f) * 3.0f))) % ((3.0f))));\n    var horColour_0 : vec3<f32> = vec3<f32>(0.25f, 0.25f, 0.25f);\n    if(xCol_0 < 1.0f)\n    {\n        horColour_0[i32(0)] = horColour_0[i32(0)] + (1.0f - xCol_0);\n        horColour_0[i32(1)] = horColour_0[i32(1)] + xCol_0;\n    }\n    else\n    {\n        if(xCol_0 < 2.0f)\n        {\n            var xCol_1 : f32 = xCol_0 - 1.0f;\n            horColour_0[i32(1)] = horColour_0[i32(1)] + (1.0f - xCol_1);\n            horColour_0[i32(2)] = horColour_0[i32(2)] + xCol_1;\n        }\n        else\n        {\n            var xCol_2 : f32 = xCol_0 - 2.0f;\n            horColour_0[i32(2)] = horColour_0[i32(2)] + (1.0f - xCol_2);\n            horColour_0[i32(0)] = horColour_0[i32(0)] + xCol_2;\n        }\n    }\n    var uv_1 : vec2<f32> = _S2 * uv_0 - vec2<f32>(1.0f);\n    return vec4<f32>(vec3<f32>(((0.69999998807907104f + 0.5f * cos(uv_1.x * 10.0f * tau_0 * 0.15000000596046448f * clamp(floor(5.0f + 10.0f * cos(t_0)), 0.0f, 10.0f))) * abs(1.0f / (30.0f * uv_1.y)))) * horColour_0, 1.0f);\n}\n\nfn imageMain_slang_Lambda_imageMain_1_x24_syn_x28x29_0( this_1 : imageMain_slang_Lambda_imageMain_1_0,  _S4 : vec2<i32>) -> vec4<f32>\n{\n    return imageMain_slang_Lambda_imageMain_1_x28x29_0(this_1, _S4);\n}\n\nfn drawPixel_0( location_0 : vec2<u32>,  _S5 : imageMain_slang_Lambda_imageMain_1_0)\n{\n    var width_0 : u32 = u32(0);\n    var height_0 : u32 = u32(0);\n    {var dim = textureDimensions((outputTexture_0));((width_0)) = dim.x;((height_0)) = dim.y;};\n    var color_0 : vec4<f32> = imageMain_slang_Lambda_imageMain_1_x24_syn_x28x29_0(_S5, vec2<i32>(i32(width_0), i32(height_0)));\n    var _S6 : bool;\n    if((location_0.x) >= width_0)\n    {\n        _S6 = true;\n    }\n    else\n    {\n        _S6 = (location_0.y) >= height_0;\n    }\n    if(_S6)\n    {\n        return;\n    }\n    textureStore((outputTexture_0), (location_0), (color_0));\n    return;\n}\n\n@compute\n@workgroup_size(16, 16, 1)\nfn imageMain(@builtin(global_invocation_id) dispatchThreadID_2 : vec3<u32>)\n{\n    var dispatchThreadID_3 : vec2<u32> = dispatchThreadID_2.xy;\n    drawPixel_0(dispatchThreadID_3, imageMain_slang_Lambda_imageMain_1_x24init_0(dispatchThreadID_3));\n    return;\n}\n"
  },
  {
    "path": "examples/features/src/repeated_compute/README.md",
    "content": "# repeated_compute\n\nRepeatedly performs the Collatz calculation used in `hello-compute` on sets of random numbers.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples repeated_compute\n```\n\n## Sample\n\nRandomly generated input:\n```\n[61917, 53957, 5717, 40520, 41020, 5120, 44281, 19584, 2975, 5310, 4162, 38159, 25343, 16551, 40532, 31464, 64505, 55815, 34793, 24285, 62190, 10530, 49321, 57494, 18473, 18291, 9067, 2665, 53877, 6754, 37616, 51136, 54990, 31159, 38648, 24127, 49640, 12095, 4529, 56275, 18200, 24423, 14065, 17512, 31421, 19612, 63709, 47666, 21805, 13608, 63529, 17809, 6737, 55362, 24647, 30348, 44906, 46325, 503, 52776, 63112, 20785, 63338, 28904, 55772, 56851, 53870, 65503, 30290, 57374, 61244, 39866, 625, 2353, 54901, 25511, 64046, 47882, 22723, 54917, 19563, 24130, 54374, 41964, 3999, 2805, 918, 32932, 6717, 46311, 4818, 28843, 37972, 50981, 31555, 39064, 42814, 37957, 17963, 22678, 3048, 18823, 7293, 63312, 29086, 45580, 5347, 1761, 19090, 41520, 35919, 38705, 51378, 29090, 31100, 55324, 26807, 26017, 24295, 62389, 51934, 27026, 1795, 14965, 51274, 10875, 21396, 22828, 37077, 49922, 46486, 55817, 58928, 64455, 47269, 53484, 6602, 52270, 24417, 6525, 60485, 6389, 10336, 62651, 15721, 8793, 37174, 11962, 768, 21426, 9919, 14295, 55401, 33099, 2221, 9021, 793, 27731, 58923, 28847, 56634, 20447, 33108, 11355, 32437, 15594, 26951, 62607, 28151, 46173, 53140, 48397, 64164, 12279, 54591, 36440, 42712, 3495, 28316, 4674, 35028, 50809, 17289, 3355, 6840, 38134, 29806, 53215, 12076, 55685, 31314, 33548, 51846, 29484, 36845, 12242, 11836, 5449, 11549, 12626, 23699, 52777, 350, 19344, 6380, 63964, 49649, 42487, 26543, 60198, 43868, 38280, 12917, 33574, 44104, 24176, 1348, 47752, 34890, 1471, 34329, 59348, 25115, 148, 62147, 12340, 23654, 26821, 3695, 41075, 15125, 56593, 44273, 34180, 35209, 26294, 48642, 19007, 40617, 46831, 9988, 522, 36478, 64700, 31220, 41376, 43870, 6053, 56665, 56475, 475, 60238, 38170, 53613, 23654, 26273]\n```\n\nResulting output:\n```\n[\"148\", \"78\", \"36\", \"75\", \"150\", \"15\", \"163\", \"43\", \"48\", \"54\", \"64\", \"80\", \"201\", \"120\", \"36\", \"147\", \"192\", \"65\", \"129\", \"157\", \"60\", \"42\", \"189\", \"73\", \"92\", \"66\", \"47\", \"53\", \"91\", \"36\", \"62\", \"78\", \"215\", \"54\", \"124\", \"144\", \"158\", \"94\", \"64\", \"83\", \"22\", \"100\", \"58\", \"35\", \"85\", \"105\", \"254\", \"101\", \"56\", \"63\", \"78\", \"97\", \"181\", \"228\", \"219\", \"72\", \"132\", \"57\", \"66\", \"34\", \"104\", \"149\", \"148\", \"121\", \"60\", \"104\", \"91\", \"130\", \"165\", \"78\", \"86\", \"106\", \"25\", \"32\", \"122\", \"113\", \"47\", \"96\", \"82\", \"60\", \"79\", \"51\", \"184\", \"88\", \"188\", \"84\", \"129\", \"147\", \"88\", \"114\", \"121\", \"165\", \"80\", \"83\", \"103\", \"75\", \"194\", \"155\", \"48\", \"131\", \"110\", \"61\", \"163\", \"55\", \"165\", \"70\", \"116\", \"104\", \"79\", \"106\", \"93\", \"75\", \"52\", \"134\", \"54\", \"91\", \"108\", \"126\", \"188\", \"148\", \"109\", \"38\", \"68\", \"133\", \"127\", \"117\", \"48\", \"30\", \"36\", \"52\", \"114\", \"184\", \"135\", \"161\", \"83\", \"52\", \"137\", \"109\", \"69\", \"137\", \"86\", \"124\", \"104\", \"179\", \"84\", \"127\", \"62\", \"50\", \"15\", \"30\", \"148\", \"102\", \"78\", \"160\", \"32\", \"140\", \"77\", \"90\", \"135\", \"165\", \"104\", \"180\", \"129\", \"161\", \"160\", \"146\", \"183\", \"148\", \"108\", \"145\", \"109\", \"70\", \"104\", \"125\", \"78\", \"62\", \"49\", \"56\", \"103\", \"59\", \"36\", \"202\", \"110\", \"92\", \"57\", \"54\", \"165\", \"171\", \"68\", \"109\", \"85\", \"67\", \"171\", \"46\", \"124\", \"174\", \"99\", \"160\", \"130\", \"156\", \"100\", \"83\", \"81\", \"61\", \"75\", \"55\", \"158\", \"101\", \"77\", \"91\", \"119\", \"75\", \"76\", \"129\", \"101\", \"95\", \"114\", \"96\", \"142\", \"171\", \"111\", \"122\", \"64\", \"23\", \"179\", \"37\", \"82\", \"46\", \"206\", \"150\", \"40\", \"104\", \"101\", \"129\", \"155\", \"64\", \"65\", \"154\", \"212\", \"132\", \"91\", \"30\", \"67\", \"148\", \"178\", \"106\", \"163\", \"67\", \"60\", \"135\", \"27\", \"117\", \"106\", \"109\", \"82\", \"201\"]\n```"
  },
  {
    "path": "examples/features/src/repeated_compute/mod.rs",
    "content": "//! See hello-compute example main.rs for more details\n//! as similar items here are not explained.\n//!\n//! This example does elaborate on some things though that the\n//! hello-compute example does not such as mapping buffers\n//! and why use the async channels.\n\nuse nanorand::Rng;\n\nconst OVERFLOW: u32 = 0xffffffff;\n\nasync fn run() {\n    let mut numbers = [0u32; 256];\n    let context = WgpuContext::new(size_of_val(&numbers)).await;\n\n    let mut rand = nanorand::WyRand::new();\n\n    for _ in 0..10 {\n        for p in numbers.iter_mut() {\n            *p = rand.generate::<u16>() as u32;\n        }\n\n        compute(&mut numbers, &context).await;\n\n        let printed_numbers = numbers\n            .iter()\n            .map(|n| match n {\n                &OVERFLOW => \"(overflow)\".to_string(),\n                n => n.to_string(),\n            })\n            .collect::<Vec<String>>();\n        log::info!(\"Results: {printed_numbers:?}\");\n    }\n}\n\nasync fn compute(local_buffer: &mut [u32], context: &WgpuContext) {\n    log::info!(\"Beginning GPU compute on data {local_buffer:?}.\");\n    // Local buffer contents -> GPU storage buffer\n    // Adds a write buffer command to the queue. This command is more complicated\n    // than it appears.\n    context.queue.write_buffer(\n        &context.storage_buffer,\n        0,\n        bytemuck::cast_slice(local_buffer),\n    );\n    log::info!(\"Wrote to buffer.\");\n\n    let mut command_encoder = context\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    {\n        let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        compute_pass.set_pipeline(&context.pipeline);\n        compute_pass.set_bind_group(0, &context.bind_group, &[]);\n        compute_pass.dispatch_workgroups(local_buffer.len() as u32, 1, 1);\n    }\n    // We finish the compute pass by dropping it.\n\n    // Entire storage buffer -> staging buffer.\n    command_encoder.copy_buffer_to_buffer(\n        &context.storage_buffer,\n        0,\n        &context.output_staging_buffer,\n        0,\n        context.storage_buffer.size(),\n    );\n\n    // Finalize the command encoder, add the contained commands to the queue and flush.\n    context.queue.submit(Some(command_encoder.finish()));\n    log::info!(\"Submitted commands.\");\n\n    // Finally time to get our results.\n    // First we get a buffer slice which represents a chunk of the buffer (which we\n    // can't access yet).\n    // We want the whole thing so use unbounded range.\n    let buffer_slice = context.output_staging_buffer.slice(..);\n    // Now things get complicated. WebGPU, for safety reasons, only allows either the GPU\n    // or CPU to access a buffer's contents at a time. We need to \"map\" the buffer which means\n    // flipping ownership of the buffer over to the CPU and making access legal. We do this\n    // with `BufferSlice::map_async`.\n    //\n    // The problem is that map_async is not an async function so we can't await it. What\n    // we need to do instead is pass in a closure that will be executed when the slice is\n    // either mapped or the mapping has failed.\n    //\n    // The problem with this is that we don't have a reliable way to wait in the main\n    // code for the buffer to be mapped and even worse, calling get_mapped_range or\n    // get_mapped_range_mut prematurely will cause a panic, not return an error.\n    //\n    // Using channels solves this as awaiting the receiving of a message from\n    // the passed closure will force the outside code to wait. It also doesn't hurt\n    // if the closure finishes before the outside code catches up as the message is\n    // buffered and receiving will just pick that up.\n    //\n    // It may also be worth noting that although on native, the usage of asynchronous\n    // channels is wholly unnecessary, for the sake of portability to WASM (std channels\n    // don't work on WASM,) we'll use async channels that work on both native and WASM.\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    // In order for the mapping to be completed, one of three things must happen.\n    // One of those can be calling `Device::poll`. This isn't necessary on the web as devices\n    // are polled automatically but natively, we need to make sure this happens manually.\n    // `PollType::Wait` will cause the thread to wait on native but not on WebGpu.\n    context\n        .device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n    log::info!(\"Device polled.\");\n    // Now we await the receiving and panic if anything went wrong because we're lazy.\n    receiver.recv_async().await.unwrap().unwrap();\n    log::info!(\"Result received.\");\n    // NOW we can call get_mapped_range.\n    {\n        let view = buffer_slice.get_mapped_range();\n        local_buffer.copy_from_slice(bytemuck::cast_slice(&view));\n    }\n    log::info!(\"Results written to local buffer.\");\n    // We need to make sure all `BufferView`'s are dropped before we do what we're about\n    // to do.\n    // Unmap so that we can copy to the staging buffer in the next iteration.\n    context.output_staging_buffer.unmap();\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n        pollster::block_on(run());\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n\n        crate::utils::add_web_nothing_to_see_msg();\n\n        wasm_bindgen_futures::spawn_local(run());\n    }\n}\n\n/// A convenient way to hold together all the useful wgpu stuff together.\nstruct WgpuContext {\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n    pipeline: wgpu::ComputePipeline,\n    bind_group: wgpu::BindGroup,\n    storage_buffer: wgpu::Buffer,\n    output_staging_buffer: wgpu::Buffer,\n}\n\nimpl WgpuContext {\n    async fn new(buffer_size: usize) -> WgpuContext {\n        let instance = wgpu::Instance::default();\n        let adapter = instance\n            .request_adapter(&wgpu::RequestAdapterOptions::default())\n            .await\n            .unwrap();\n        let (device, queue) = adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                label: None,\n                required_features: wgpu::Features::empty(),\n                required_limits: wgpu::Limits::downlevel_defaults(),\n                experimental_features: wgpu::ExperimentalFeatures::disabled(),\n                memory_hints: wgpu::MemoryHints::Performance,\n                trace: wgpu::Trace::Off,\n            })\n            .await\n            .unwrap();\n\n        // Our shader, kindly compiled with Naga.\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        // This is where the GPU will read from and write to.\n        let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size as wgpu::BufferAddress,\n            usage: wgpu::BufferUsages::STORAGE\n                | wgpu::BufferUsages::COPY_DST\n                | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        // For portability reasons, WebGPU draws a distinction between memory that is\n        // accessible by the CPU and memory that is accessible by the GPU. Only\n        // buffers accessible by the CPU can be mapped and accessed by the CPU and\n        // only buffers visible to the GPU can be used in shaders. In order to get\n        // data from the GPU, we need to use CommandEncoder::copy_buffer_to_buffer\n        // (which we will later) to copy the buffer modified by the GPU into a\n        // mappable, CPU-accessible buffer which we'll create here.\n        let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size as wgpu::BufferAddress,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        // This can be though of as the function signature for our CPU-GPU function.\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    // Going to have this be None just to be safe.\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n        // This ties actual resources stored in the GPU to our metaphorical function\n        // through the binding slots we defined above.\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: storage_buffer.as_entire_binding(),\n            }],\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            module: &shader,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        WgpuContext {\n            device,\n            queue,\n            pipeline,\n            bind_group,\n            storage_buffer,\n            output_staging_buffer,\n        }\n    }\n}\n"
  },
  {
    "path": "examples/features/src/repeated_compute/shader.wgsl",
    "content": "@group(0)\n@binding(0)\nvar<storage, read_write> v_indices: array<u32>; // this is used as both input and output for convenience\n\n// The Collatz Conjecture states that for any integer n:\n// If n is even, n = n/2\n// If n is odd, n = 3n+1\n// And repeat this process for each new n, you will always eventually reach 1.\n// Though the conjecture has not been proven, no counterexample has ever been found.\n// This function returns how many times this recurrence needs to be applied to reach 1.\nfn collatz_iterations(n_base: u32) -> u32{\n    var n: u32 = n_base;\n    var i: u32 = 0u;\n    loop {\n        if (n <= 1u) {\n            break;\n        }\n        if (n % 2u == 0u) {\n            n = n / 2u;\n        }\n        else {\n            // Overflow? (i.e. 3*n + 1 > 0xffffffffu?)\n            if (n >= 1431655765u) {   // 0x55555555u\n                return 4294967295u;   // 0xffffffffu\n            }\n\n            n = 3u * n + 1u;\n        }\n        i = i + 1u;\n    }\n    return i;\n}\n\n@compute\n@workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    v_indices[global_id.x] = collatz_iterations(v_indices[global_id.x]);\n}\n"
  },
  {
    "path": "examples/features/src/shadow/README.md",
    "content": "# shadow\n\nThis animated example demonstrates shadow mapping.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples shadow\n```\n\n## Screenshots\n\n![Shadow mapping](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/shadow/mod.rs",
    "content": "use std::{f32::consts, iter, ops::Range};\n\nuse bytemuck::{Pod, Zeroable};\nuse wgpu::util::{align_to, DeviceExt};\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [i8; 4],\n    _normal: [i8; 4],\n}\n\nfn vertex(pos: [i8; 3], nor: [i8; 3]) -> Vertex {\n    Vertex {\n        _pos: [pos[0], pos[1], pos[2], 1],\n        _normal: [nor[0], nor[1], nor[2], 0],\n    }\n}\n\nfn create_cube() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0, 1]),\n        vertex([1, -1, 1], [0, 0, 1]),\n        vertex([1, 1, 1], [0, 0, 1]),\n        vertex([-1, 1, 1], [0, 0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [0, 0, -1]),\n        vertex([1, 1, -1], [0, 0, -1]),\n        vertex([1, -1, -1], [0, 0, -1]),\n        vertex([-1, -1, -1], [0, 0, -1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [1, 0, 0]),\n        vertex([1, 1, -1], [1, 0, 0]),\n        vertex([1, 1, 1], [1, 0, 0]),\n        vertex([1, -1, 1], [1, 0, 0]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [-1, 0, 0]),\n        vertex([-1, 1, 1], [-1, 0, 0]),\n        vertex([-1, 1, -1], [-1, 0, 0]),\n        vertex([-1, -1, -1], [-1, 0, 0]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [0, 1, 0]),\n        vertex([-1, 1, -1], [0, 1, 0]),\n        vertex([-1, 1, 1], [0, 1, 0]),\n        vertex([1, 1, 1], [0, 1, 0]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, -1, 0]),\n        vertex([-1, -1, 1], [0, -1, 0]),\n        vertex([-1, -1, -1], [0, -1, 0]),\n        vertex([1, -1, -1], [0, -1, 0]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\nfn create_plane(size: i8) -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        vertex([size, -size, 0], [0, 0, 1]),\n        vertex([size, size, 0], [0, 0, 1]),\n        vertex([-size, -size, 0], [0, 0, 1]),\n        vertex([-size, size, 0], [0, 0, 1]),\n    ];\n\n    let index_data: &[u16] = &[0, 1, 2, 2, 1, 3];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\nstruct Entity {\n    mx_world: glam::Mat4,\n    rotation_speed: f32,\n    color: wgpu::Color,\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_format: wgpu::IndexFormat,\n    index_count: usize,\n    uniform_offset: wgpu::DynamicOffset,\n}\n\nstruct Light {\n    pos: glam::Vec3,\n    color: wgpu::Color,\n    fov: f32,\n    depth: Range<f32>,\n    target_view: wgpu::TextureView,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct LightRaw {\n    proj: [[f32; 4]; 4],\n    pos: [f32; 4],\n    color: [f32; 4],\n}\n\nimpl Light {\n    fn to_raw(&self) -> LightRaw {\n        let view = glam::Mat4::look_at_rh(self.pos, glam::Vec3::ZERO, glam::Vec3::Z);\n        let projection = glam::Mat4::perspective_rh(\n            self.fov * consts::PI / 180.,\n            1.0,\n            self.depth.start,\n            self.depth.end,\n        );\n        let view_proj = projection * view;\n        LightRaw {\n            proj: view_proj.to_cols_array_2d(),\n            pos: [self.pos.x, self.pos.y, self.pos.z, 1.0],\n            color: [\n                self.color.r as f32,\n                self.color.g as f32,\n                self.color.b as f32,\n                1.0,\n            ],\n        }\n    }\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct GlobalUniforms {\n    proj: [[f32; 4]; 4],\n    num_lights: [u32; 4],\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct EntityUniforms {\n    model: [[f32; 4]; 4],\n    color: [f32; 4],\n}\n\nstruct Pass {\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    uniform_buf: wgpu::Buffer,\n}\n\nstruct Example {\n    entities: Vec<Entity>,\n    lights: Vec<Light>,\n    lights_are_dirty: bool,\n    shadow_pass: Pass,\n    forward_pass: Pass,\n    forward_depth: wgpu::TextureView,\n    entity_bind_group: wgpu::BindGroup,\n    light_storage_buf: wgpu::Buffer,\n    entity_uniform_buf: wgpu::Buffer,\n}\n\nimpl Example {\n    const MAX_LIGHTS: usize = 10;\n    const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;\n    const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d {\n        width: 512,\n        height: 512,\n        depth_or_array_layers: Self::MAX_LIGHTS as u32,\n    };\n    const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;\n\n    fn generate_matrix(aspect_ratio: f32) -> glam::Mat4 {\n        let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 1.0, 20.0);\n        let view = glam::Mat4::look_at_rh(\n            glam::Vec3::new(3.0f32, -10.0, 6.0),\n            glam::Vec3::new(0f32, 0.0, 0.0),\n            glam::Vec3::Z,\n        );\n        projection * view\n    }\n\n    fn create_depth_texture(\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n    ) -> wgpu::TextureView {\n        let depth_texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: Self::DEPTH_FORMAT,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,\n            label: None,\n            view_formats: &[],\n        });\n\n        depth_texture.create_view(&wgpu::TextureViewDescriptor::default())\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::DEPTH_CLIP_CONTROL\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        let supports_storage_resources = adapter\n            .get_downlevel_capabilities()\n            .flags\n            .contains(wgpu::DownlevelFlags::VERTEX_STORAGE)\n            && device.limits().max_storage_buffers_per_shader_stage > 0;\n\n        // Create the vertex and index buffers\n        let vertex_size = size_of::<Vertex>();\n        let (cube_vertex_data, cube_index_data) = create_cube();\n        let cube_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Cubes Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&cube_vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let cube_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Cubes Index Buffer\"),\n            contents: bytemuck::cast_slice(&cube_index_data),\n            usage: wgpu::BufferUsages::INDEX,\n        });\n\n        let (plane_vertex_data, plane_index_data) = create_plane(7);\n        let plane_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Plane Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&plane_vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let plane_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Plane Index Buffer\"),\n            contents: bytemuck::cast_slice(&plane_index_data),\n            usage: wgpu::BufferUsages::INDEX,\n        });\n\n        struct CubeDesc {\n            offset: glam::Vec3,\n            angle: f32,\n            scale: f32,\n            rotation: f32,\n        }\n        let cube_descs = [\n            CubeDesc {\n                offset: glam::Vec3::new(-2.0, -2.0, 2.0),\n                angle: 10.0,\n                scale: 0.7,\n                rotation: 0.1,\n            },\n            CubeDesc {\n                offset: glam::Vec3::new(2.0, -2.0, 2.0),\n                angle: 50.0,\n                scale: 1.3,\n                rotation: 0.2,\n            },\n            CubeDesc {\n                offset: glam::Vec3::new(-2.0, 2.0, 2.0),\n                angle: 140.0,\n                scale: 1.1,\n                rotation: 0.3,\n            },\n            CubeDesc {\n                offset: glam::Vec3::new(2.0, 2.0, 2.0),\n                angle: 210.0,\n                scale: 0.9,\n                rotation: 0.4,\n            },\n        ];\n\n        let entity_uniform_size = size_of::<EntityUniforms>() as wgpu::BufferAddress;\n        let num_entities = 1 + cube_descs.len() as wgpu::BufferAddress;\n        // Make the `uniform_alignment` >= `entity_uniform_size` and aligned to `min_uniform_buffer_offset_alignment`.\n        let uniform_alignment = {\n            let alignment =\n                device.limits().min_uniform_buffer_offset_alignment as wgpu::BufferAddress;\n            align_to(entity_uniform_size, alignment)\n        };\n        // Note: dynamic uniform offsets also have to be aligned to `Limits::min_uniform_buffer_offset_alignment`.\n        let entity_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: num_entities * uniform_alignment,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let index_format = wgpu::IndexFormat::Uint16;\n\n        let mut entities = vec![{\n            Entity {\n                mx_world: glam::Mat4::IDENTITY,\n                rotation_speed: 0.0,\n                color: wgpu::Color::WHITE,\n                vertex_buf: plane_vertex_buf,\n                index_buf: plane_index_buf,\n                index_format,\n                index_count: plane_index_data.len(),\n                uniform_offset: 0,\n            }\n        }];\n\n        for (i, cube) in cube_descs.iter().enumerate() {\n            let mx_world = glam::Mat4::from_scale_rotation_translation(\n                glam::Vec3::splat(cube.scale),\n                glam::Quat::from_axis_angle(\n                    cube.offset.normalize(),\n                    cube.angle * consts::PI / 180.,\n                ),\n                cube.offset,\n            );\n            entities.push(Entity {\n                mx_world,\n                rotation_speed: cube.rotation,\n                color: wgpu::Color::GREEN,\n                vertex_buf: cube_vertex_buf.clone(),\n                index_buf: cube_index_buf.clone(),\n                index_format,\n                index_count: cube_index_data.len(),\n                uniform_offset: ((i + 1) * uniform_alignment as usize) as _,\n            });\n        }\n\n        let local_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                entries: &[wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: true,\n                        min_binding_size: wgpu::BufferSize::new(entity_uniform_size),\n                    },\n                    count: None,\n                }],\n                label: None,\n            });\n        let entity_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &local_bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &entity_uniform_buf,\n                    offset: 0,\n                    size: wgpu::BufferSize::new(entity_uniform_size),\n                }),\n            }],\n            label: None,\n        });\n\n        // Create other resources\n        let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"shadow\"),\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            compare: Some(wgpu::CompareFunction::LessEqual),\n            ..Default::default()\n        });\n\n        let shadow_texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: Self::SHADOW_SIZE,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: Self::SHADOW_FORMAT,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,\n            label: None,\n            view_formats: &[],\n        });\n        let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n        let mut shadow_target_views = (0..2)\n            .map(|i| {\n                Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor {\n                    label: Some(\"shadow\"),\n                    format: None,\n                    dimension: Some(wgpu::TextureViewDimension::D2),\n                    usage: None,\n                    aspect: wgpu::TextureAspect::All,\n                    base_mip_level: 0,\n                    mip_level_count: None,\n                    base_array_layer: i as u32,\n                    array_layer_count: Some(1),\n                }))\n            })\n            .collect::<Vec<_>>();\n        let lights = vec![\n            Light {\n                pos: glam::Vec3::new(7.0, -5.0, 10.0),\n                color: wgpu::Color {\n                    r: 0.5,\n                    g: 1.0,\n                    b: 0.5,\n                    a: 1.0,\n                },\n                fov: 60.0,\n                depth: 1.0..20.0,\n                target_view: shadow_target_views[0].take().unwrap(),\n            },\n            Light {\n                pos: glam::Vec3::new(-5.0, 7.0, 10.0),\n                color: wgpu::Color {\n                    r: 1.0,\n                    g: 0.5,\n                    b: 0.5,\n                    a: 1.0,\n                },\n                fov: 45.0,\n                depth: 1.0..20.0,\n                target_view: shadow_target_views[1].take().unwrap(),\n            },\n        ];\n        let light_uniform_size = (Self::MAX_LIGHTS * size_of::<LightRaw>()) as wgpu::BufferAddress;\n        let light_storage_buf = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: light_uniform_size,\n            usage: if supports_storage_resources {\n                wgpu::BufferUsages::STORAGE\n            } else {\n                wgpu::BufferUsages::UNIFORM\n            } | wgpu::BufferUsages::COPY_SRC\n                | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let vertex_attr = wgpu::vertex_attr_array![0 => Sint8x4, 1 => Sint8x4];\n        let vb_desc = wgpu::VertexBufferLayout {\n            array_stride: vertex_size as wgpu::BufferAddress,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &vertex_attr,\n        };\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let shadow_pass = {\n            let uniform_size = size_of::<GlobalUniforms>() as wgpu::BufferAddress;\n            // Create pipeline layout\n            let bind_group_layout =\n                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0, // global\n                        visibility: wgpu::ShaderStages::VERTEX,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new(uniform_size),\n                        },\n                        count: None,\n                    }],\n                });\n            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"shadow\"),\n                bind_group_layouts: &[Some(&bind_group_layout), Some(&local_bind_group_layout)],\n                immediate_size: 0,\n            });\n\n            let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size: uniform_size,\n                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n                mapped_at_creation: false,\n            });\n\n            // Create bind group\n            let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n                layout: &bind_group_layout,\n                entries: &[wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                }],\n                label: None,\n            });\n\n            // Create the render pipeline\n            let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"shadow\"),\n                layout: Some(&pipeline_layout),\n                vertex: wgpu::VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_bake\"),\n                    compilation_options: Default::default(),\n                    buffers: std::slice::from_ref(&vb_desc),\n                },\n                fragment: None,\n                primitive: wgpu::PrimitiveState {\n                    topology: wgpu::PrimitiveTopology::TriangleList,\n                    front_face: wgpu::FrontFace::Ccw,\n                    cull_mode: Some(wgpu::Face::Back),\n                    unclipped_depth: device\n                        .features()\n                        .contains(wgpu::Features::DEPTH_CLIP_CONTROL),\n                    ..Default::default()\n                },\n                depth_stencil: Some(wgpu::DepthStencilState {\n                    format: Self::SHADOW_FORMAT,\n                    depth_write_enabled: Some(true),\n                    depth_compare: Some(wgpu::CompareFunction::LessEqual),\n                    stencil: wgpu::StencilState::default(),\n                    bias: wgpu::DepthBiasState {\n                        constant: 2, // corresponds to bilinear filtering\n                        slope_scale: 2.0,\n                        clamp: 0.0,\n                    },\n                }),\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n            Pass {\n                pipeline,\n                bind_group,\n                uniform_buf,\n            }\n        };\n\n        let forward_pass = {\n            // Create pipeline layout\n            let bind_group_layout =\n                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    entries: &[\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 0, // global\n                            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Buffer {\n                                ty: wgpu::BufferBindingType::Uniform,\n                                has_dynamic_offset: false,\n                                min_binding_size: wgpu::BufferSize::new(\n                                    size_of::<GlobalUniforms>() as _,\n                                ),\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 1, // lights\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Buffer {\n                                ty: if supports_storage_resources {\n                                    wgpu::BufferBindingType::Storage { read_only: true }\n                                } else {\n                                    wgpu::BufferBindingType::Uniform\n                                },\n                                has_dynamic_offset: false,\n                                min_binding_size: wgpu::BufferSize::new(light_uniform_size),\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 2,\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Texture {\n                                multisampled: false,\n                                sample_type: wgpu::TextureSampleType::Depth,\n                                view_dimension: wgpu::TextureViewDimension::D2Array,\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 3,\n                            visibility: wgpu::ShaderStages::FRAGMENT,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),\n                            count: None,\n                        },\n                    ],\n                    label: None,\n                });\n            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"main\"),\n                bind_group_layouts: &[Some(&bind_group_layout), Some(&local_bind_group_layout)],\n                immediate_size: 0,\n            });\n\n            let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n            let forward_uniforms = GlobalUniforms {\n                proj: mx_total.to_cols_array_2d(),\n                num_lights: [lights.len() as u32, 0, 0, 0],\n            };\n            let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                label: Some(\"Uniform Buffer\"),\n                contents: bytemuck::bytes_of(&forward_uniforms),\n                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            });\n\n            // Create bind group\n            let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n                layout: &bind_group_layout,\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: uniform_buf.as_entire_binding(),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: light_storage_buf.as_entire_binding(),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 2,\n                        resource: wgpu::BindingResource::TextureView(&shadow_view),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 3,\n                        resource: wgpu::BindingResource::Sampler(&shadow_sampler),\n                    },\n                ],\n                label: None,\n            });\n\n            // Create the render pipeline\n            let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"main\"),\n                layout: Some(&pipeline_layout),\n                vertex: wgpu::VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[vb_desc],\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module: &shader,\n                    entry_point: Some(if supports_storage_resources {\n                        \"fs_main\"\n                    } else {\n                        \"fs_main_without_storage\"\n                    }),\n                    compilation_options: Default::default(),\n                    targets: &[Some(config.view_formats[0].into())],\n                }),\n                primitive: wgpu::PrimitiveState {\n                    front_face: wgpu::FrontFace::Ccw,\n                    cull_mode: Some(wgpu::Face::Back),\n                    ..Default::default()\n                },\n                depth_stencil: Some(wgpu::DepthStencilState {\n                    format: Self::DEPTH_FORMAT,\n                    depth_write_enabled: Some(true),\n                    depth_compare: Some(wgpu::CompareFunction::Less),\n                    stencil: wgpu::StencilState::default(),\n                    bias: wgpu::DepthBiasState::default(),\n                }),\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n            Pass {\n                pipeline,\n                bind_group,\n                uniform_buf,\n            }\n        };\n\n        let forward_depth = Self::create_depth_texture(config, device);\n\n        Example {\n            entities,\n            lights,\n            lights_are_dirty: true,\n            shadow_pass,\n            forward_pass,\n            forward_depth,\n            light_storage_buf,\n            entity_uniform_buf,\n            entity_bind_group,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        // update view-projection matrix\n        let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);\n        let mx_ref: &[f32; 16] = mx_total.as_ref();\n        queue.write_buffer(\n            &self.forward_pass.uniform_buf,\n            0,\n            bytemuck::cast_slice(mx_ref),\n        );\n\n        self.forward_depth = Self::create_depth_texture(config, device);\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        // update uniforms\n        for entity in self.entities.iter_mut() {\n            if entity.rotation_speed != 0.0 {\n                let rotation =\n                    glam::Mat4::from_rotation_x(entity.rotation_speed * consts::PI / 180.);\n                entity.mx_world *= rotation;\n            }\n            let data = EntityUniforms {\n                model: entity.mx_world.to_cols_array_2d(),\n                color: [\n                    entity.color.r as f32,\n                    entity.color.g as f32,\n                    entity.color.b as f32,\n                    entity.color.a as f32,\n                ],\n            };\n            queue.write_buffer(\n                &self.entity_uniform_buf,\n                entity.uniform_offset as wgpu::BufferAddress,\n                bytemuck::bytes_of(&data),\n            );\n        }\n\n        if self.lights_are_dirty {\n            self.lights_are_dirty = false;\n            for (i, light) in self.lights.iter().enumerate() {\n                queue.write_buffer(\n                    &self.light_storage_buf,\n                    (i * size_of::<LightRaw>()) as wgpu::BufferAddress,\n                    bytemuck::bytes_of(&light.to_raw()),\n                );\n            }\n        }\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.push_debug_group(\"shadow passes\");\n        for (i, light) in self.lights.iter().enumerate() {\n            encoder.push_debug_group(&format!(\n                \"shadow pass {} (light at position {:?})\",\n                i, light.pos\n            ));\n\n            // The light uniform buffer already has the projection,\n            // let's just copy it over to the shadow uniform buffer.\n            encoder.copy_buffer_to_buffer(\n                &self.light_storage_buf,\n                (i * size_of::<LightRaw>()) as wgpu::BufferAddress,\n                &self.shadow_pass.uniform_buf,\n                0,\n                64,\n            );\n\n            encoder.insert_debug_marker(\"render entities\");\n            {\n                let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                    label: None,\n                    color_attachments: &[],\n                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                        view: &light.target_view,\n                        depth_ops: Some(wgpu::Operations {\n                            load: wgpu::LoadOp::Clear(1.0),\n                            store: wgpu::StoreOp::Store,\n                        }),\n                        stencil_ops: None,\n                    }),\n                    timestamp_writes: None,\n                    occlusion_query_set: None,\n                    multiview_mask: None,\n                });\n                pass.set_pipeline(&self.shadow_pass.pipeline);\n                pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]);\n\n                for entity in &self.entities {\n                    pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);\n                    pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);\n                    pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));\n                    pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);\n                }\n            }\n\n            encoder.pop_debug_group();\n        }\n        encoder.pop_debug_group();\n\n        // forward pass\n        encoder.push_debug_group(\"forward rendering pass\");\n        {\n            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &self.forward_depth,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Discard,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            pass.set_pipeline(&self.forward_pass.pipeline);\n            pass.set_bind_group(0, &self.forward_pass.bind_group, &[]);\n\n            for entity in &self.entities {\n                pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);\n                pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);\n                pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));\n                pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);\n            }\n        }\n        encoder.pop_debug_group();\n\n        queue.submit(iter::once(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"shadow\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"shadow\",\n    image_path: \"/examples/features/src/shadow/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS)\n        // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916\n        .expect_fail(wgpu_test::FailureCase::backend_adapter(\n            wgpu::Backends::VULKAN,\n            \"V3D\",\n        )),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.026)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/shadow/shader.wgsl",
    "content": "struct Globals {\n    view_proj: mat4x4<f32>,\n    num_lights: vec4<u32>,\n};\n\n@group(0)\n@binding(0)\nvar<uniform> u_globals: Globals;\n\nstruct Entity {\n    world: mat4x4<f32>,\n    color: vec4<f32>,\n};\n\n@group(1)\n@binding(0)\nvar<uniform> u_entity: Entity;\n\n@vertex\nfn vs_bake(@location(0) position: vec4<i32>) -> @builtin(position) vec4<f32> {\n    return u_globals.view_proj * u_entity.world * vec4<f32>(position);\n}\n\nstruct VertexOutput {\n    @builtin(position) proj_position: vec4<f32>,\n    @location(0) world_normal: vec3<f32>,\n    @location(1) world_position: vec4<f32>\n};\n\n@vertex\nfn vs_main(\n    @location(0) position: vec4<i32>,\n    @location(1) normal: vec4<i32>,\n) -> VertexOutput {\n    let w = u_entity.world;\n    let world_pos = u_entity.world * vec4<f32>(position);\n    var result: VertexOutput;\n    result.world_normal = mat3x3<f32>(w[0].xyz, w[1].xyz, w[2].xyz) * vec3<f32>(normal.xyz);\n    result.world_position = world_pos;\n    result.proj_position = u_globals.view_proj * world_pos;\n    return result;\n}\n\n// fragment shader\n\nstruct Light {\n    proj: mat4x4<f32>,\n    pos: vec4<f32>,\n    color: vec4<f32>,\n};\n\n@group(0)\n@binding(1)\nvar<storage, read> s_lights: array<Light>;\n@group(0)\n@binding(1)\nvar<uniform> u_lights: array<Light, 10>; // Used when storage types are not supported\n@group(0)\n@binding(2)\nvar t_shadow: texture_depth_2d_array;\n@group(0)\n@binding(3)\nvar sampler_shadow: sampler_comparison;\n\nfn fetch_shadow(light_id: u32, homogeneous_coords: vec4<f32>) -> f32 {\n    if (homogeneous_coords.w <= 0.0) {\n        return 1.0;\n    }\n    // compensate for the Y-flip difference between the NDC and texture coordinates\n    let flip_correction = vec2<f32>(0.5, -0.5);\n    // compute texture coordinates for shadow lookup\n    let proj_correction = 1.0 / homogeneous_coords.w;\n    let light_local = homogeneous_coords.xy * flip_correction * proj_correction + vec2<f32>(0.5, 0.5);\n    // do the lookup, using HW PCF and comparison\n    return textureSampleCompareLevel(t_shadow, sampler_shadow, light_local, i32(light_id), homogeneous_coords.z * proj_correction);\n}\n\nconst c_ambient: vec3<f32> = vec3<f32>(0.05, 0.05, 0.05);\nconst c_max_lights: u32 = 10u;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(vertex.world_normal);\n    // accumulate color\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i += 1u) {\n        let light = s_lights[i];\n        // project into the light space\n        let shadow = fetch_shadow(i, light.proj * vertex.world_position);\n        // compute Lambertian diffuse term\n        let light_dir = normalize(light.pos.xyz - vertex.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        // add light contribution\n        color += shadow * diffuse * light.color.xyz;\n    }\n    // multiply the light by material color\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n\n// The fragment entrypoint used when storage buffers are not available for the lights\n@fragment\nfn fs_main_without_storage(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(vertex.world_normal);\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i += 1u) {\n        // This line is the only difference from the entrypoint above. It uses the lights\n        // uniform instead of the lights storage buffer\n        let light = u_lights[i];\n        let shadow = fetch_shadow(i, light.proj * vertex.world_position);\n        let light_dir = normalize(light.pos.xyz - vertex.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        color += shadow * diffuse * light.color.xyz;\n    }\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n"
  },
  {
    "path": "examples/features/src/skybox/README.md",
    "content": "# skybox\n\nThis animated example demonstrates loading a Wavefront OBJ model, and rendering it with skybox and simple reflections.\nIt hooks up `winit` mouse controls for camera rotation around the model at the center.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples skybox\n```\n\n## Screenshots\n\n![Skybox](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/skybox/images/generation.bash",
    "content": "# Needs montage from ImageMagick in PATH\n# Needs compressonatorcli.exe from https://github.com/GPUOpen-Tools/compressonator in PATH\n# Needs PVRTexToolCLI.exe from https://developer.imaginationtech.com/pvrtextool/ in PATH\n\n# Generate a skybox image from 6 jpeg in the folder in first argument.\n# The images must be named right.jpg, left.jpg, top.jpg, bottom.jpg, back.jpg, front.jpg\n#\n# Must be called from the root of the project.\n#\n# bash examples/src/skybox/images/generation.bash ./path/to/images/folder\n\nSCRIPT_DIRECTORY=examples/src/skybox/images\nCHUNK_SIZE=\"256x256\"\n\nset -e\n\n# ensure the script is called from the root of the project\nif [ ! -f \"$SCRIPT_DIRECTORY/generation.bash\" ]; then\n    echo \"The script must be called from the root of the project!\"\n    exit 1\nfi\n\n# ensure an argument is passed\nif [ $# -eq 0 ]; then\n    echo \"No arguments supplied!\"\n    echo\n    echo \"Usage: bash examples/src/skybox/images/generation.bash ./path/to/images/folder\"\n    exit 1\nfi\n\nTEMP=examples/src/skybox/images/tmp\n\nmkdir -p $TEMP\n# resize images to 256x256\nmagick mogrify -path $TEMP -resize 256x256 -format png $1/*.jpg\n# create an uncompressed ktx2 cubemap file\nPVRTexToolCLI.exe -i $TEMP/right.png,$TEMP/left.png,$TEMP/top.png,$TEMP/bottom.png,$TEMP/front.png,$TEMP/back.png -ics SRGB -cube -m -f r8g8b8a8,UBN,SRGB -o $SCRIPT_DIRECTORY/rgba8.ktx2\n# create the bc7 compressed ktx2 cubemap files using compressonator\ncompressonatorcli.exe -fd BC7 $SCRIPT_DIRECTORY/rgba8.ktx2 $SCRIPT_DIRECTORY/bc7.ktx2\n# create the etc2 and astc compressed ktx2 cubemap file using PVRTexTool\n#\n# compressonator has support for etc2, but the result looks terrible.\nPVRTexToolCLI.exe -i $SCRIPT_DIRECTORY/rgba8.ktx2 -ics srgb -m -f ETC2_RGB_A1,UBN,SRGB -q etcslow -o $SCRIPT_DIRECTORY/etc2.ktx2\nPVRTexToolCLI.exe -i $SCRIPT_DIRECTORY/rgba8.ktx2 -ics srgb -m -f ASTC_4X4,UBN,SRGB -q astcexhaustive -o $SCRIPT_DIRECTORY/astc.ktx2\nrm -r $TEMP\n"
  },
  {
    "path": "examples/features/src/skybox/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse std::f32::consts;\nuse wgpu::{util::DeviceExt, AstcBlock, AstcChannel};\n\nconst IMAGE_SIZE: u32 = 256;\n\n#[derive(Clone, Copy, Pod, Zeroable)]\n#[repr(C)]\nstruct Vertex {\n    pos: [f32; 3],\n    normal: [f32; 3],\n}\n\nstruct Entity {\n    vertex_count: u32,\n    vertex_buf: wgpu::Buffer,\n}\n\n// Note: we use the Y=up coordinate space in this example.\nstruct Camera {\n    screen_size: (u32, u32),\n    angle_y: f32,\n    angle_xz: f32,\n    dist: f32,\n}\n\nconst MODEL_CENTER_Y: f32 = 2.0;\n\nimpl Camera {\n    fn to_uniform_data(&self) -> [f32; 16 * 3 + 4] {\n        let aspect = self.screen_size.0 as f32 / self.screen_size.1 as f32;\n        let proj = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect, 1.0, 50.0);\n        let cam_pos = glam::Vec3::new(\n            self.angle_xz.cos() * self.angle_y.sin() * self.dist,\n            self.angle_xz.sin() * self.dist + MODEL_CENTER_Y,\n            self.angle_xz.cos() * self.angle_y.cos() * self.dist,\n        );\n        let view = glam::Mat4::look_at_rh(\n            cam_pos,\n            glam::Vec3::new(0f32, MODEL_CENTER_Y, 0.0),\n            glam::Vec3::Y,\n        );\n        let proj_inv = proj.inverse();\n\n        let mut raw = [0f32; 16 * 3 + 4];\n        raw[..16].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj)[..]);\n        raw[16..32].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj_inv)[..]);\n        raw[32..48].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view)[..]);\n        raw[48..51].copy_from_slice(AsRef::<[f32; 3]>::as_ref(&cam_pos));\n        raw[51] = 1.0;\n        raw\n    }\n}\n\npub struct Example {\n    camera: Camera,\n    sky_pipeline: wgpu::RenderPipeline,\n    entity_pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    uniform_buf: wgpu::Buffer,\n    entities: Vec<Entity>,\n    depth_view: wgpu::TextureView,\n    staging_belt: wgpu::util::StagingBelt,\n}\n\nimpl Example {\n    const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus;\n\n    fn create_depth_texture(\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n    ) -> wgpu::TextureView {\n        let depth_texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: Self::DEPTH_FORMAT,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,\n            label: None,\n            view_formats: &[],\n        });\n\n        depth_texture.create_view(&wgpu::TextureViewDescriptor::default())\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::TEXTURE_COMPRESSION_ASTC\n            | wgpu::Features::TEXTURE_COMPRESSION_ETC2\n            | wgpu::Features::TEXTURE_COMPRESSION_BC\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let mut entities = Vec::new();\n        {\n            let source = include_bytes!(\"models/rustacean-3d.obj\");\n            let data = obj::ObjData::load_buf(&source[..]).unwrap();\n            let mut vertices = Vec::new();\n            for object in data.objects {\n                for group in object.groups {\n                    vertices.clear();\n                    for poly in group.polys {\n                        for end_index in 2..poly.0.len() {\n                            for &index in &[0, end_index - 1, end_index] {\n                                let obj::IndexTuple(position_id, _texture_id, normal_id) =\n                                    poly.0[index];\n                                let [x, y, z] = data.position[position_id];\n                                vertices.push(Vertex {\n                                    pos: [y, z, x], // model is rotated to face down, so need to rotate it\n                                    normal: data.normal[normal_id.unwrap()],\n                                })\n                            }\n                        }\n                    }\n                    let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                        label: Some(\"Vertex\"),\n                        contents: bytemuck::cast_slice(&vertices),\n                        usage: wgpu::BufferUsages::VERTEX,\n                    });\n                    entities.push(Entity {\n                        vertex_count: vertices.len() as u32,\n                        vertex_buf,\n                    });\n                }\n            }\n        }\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Texture {\n                        sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                        multisampled: false,\n                        view_dimension: wgpu::TextureViewDimension::Cube,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 2,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                    count: None,\n                },\n            ],\n        });\n\n        // Create the render pipeline\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let camera = Camera {\n            screen_size: (config.width, config.height),\n            angle_xz: 0.2,\n            angle_y: 0.2,\n            dist: 20.0,\n        };\n        let raw_uniforms = camera.to_uniform_data();\n        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Buffer\"),\n            contents: bytemuck::cast_slice(&raw_uniforms),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        // Create the render pipelines\n        let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"Sky\"),\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_sky\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_sky\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                front_face: wgpu::FrontFace::Cw,\n                ..Default::default()\n            },\n            depth_stencil: Some(wgpu::DepthStencilState {\n                format: Self::DEPTH_FORMAT,\n                depth_write_enabled: Some(false),\n                depth_compare: Some(wgpu::CompareFunction::LessEqual),\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            }),\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n        let entity_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"Entity\"),\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_entity\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: size_of::<Vertex>() as wgpu::BufferAddress,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_entity\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                front_face: wgpu::FrontFace::Cw,\n                ..Default::default()\n            },\n            depth_stencil: Some(wgpu::DepthStencilState {\n                format: Self::DEPTH_FORMAT,\n                depth_write_enabled: Some(true),\n                depth_compare: Some(wgpu::CompareFunction::LessEqual),\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            }),\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: None,\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Linear,\n            ..Default::default()\n        });\n\n        let device_features = device.features();\n\n        let skybox_format = if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ASTC) {\n            log::info!(\"Using astc\");\n            wgpu::TextureFormat::Astc {\n                block: AstcBlock::B4x4,\n                channel: AstcChannel::UnormSrgb,\n            }\n        } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ETC2) {\n            log::info!(\"Using etc2\");\n            wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb\n        } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) {\n            log::info!(\"Using bc7\");\n            wgpu::TextureFormat::Bc7RgbaUnormSrgb\n        } else {\n            log::info!(\"Using rgba8\");\n            wgpu::TextureFormat::Rgba8UnormSrgb\n        };\n\n        let size = wgpu::Extent3d {\n            width: IMAGE_SIZE,\n            height: IMAGE_SIZE,\n            depth_or_array_layers: 6,\n        };\n\n        let layer_size = wgpu::Extent3d {\n            depth_or_array_layers: 1,\n            ..size\n        };\n        let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2);\n\n        log::debug!(\n            \"Copying {skybox_format:?} skybox images of size {IMAGE_SIZE}, {IMAGE_SIZE}, 6 with {max_mips} mips to gpu\",\n        );\n\n        let bytes = match skybox_format {\n            wgpu::TextureFormat::Astc {\n                block: AstcBlock::B4x4,\n                channel: AstcChannel::UnormSrgb,\n            } => &include_bytes!(\"images/astc.ktx2\")[..],\n            wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb => &include_bytes!(\"images/etc2.ktx2\")[..],\n            wgpu::TextureFormat::Bc7RgbaUnormSrgb => &include_bytes!(\"images/bc7.ktx2\")[..],\n            wgpu::TextureFormat::Rgba8UnormSrgb => &include_bytes!(\"images/rgba8.ktx2\")[..],\n            _ => unreachable!(),\n        };\n\n        let reader = ktx2::Reader::new(bytes).unwrap();\n        let header = reader.header();\n\n        let mut image = Vec::with_capacity(reader.data().len());\n        for level in reader.levels() {\n            image.extend_from_slice(level.data);\n        }\n\n        let texture = device.create_texture_with_data(\n            queue,\n            &wgpu::TextureDescriptor {\n                size,\n                mip_level_count: header.level_count,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: skybox_format,\n                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n                label: None,\n                view_formats: &[],\n            },\n            // KTX2 stores mip levels in mip major order.\n            wgpu::util::TextureDataOrder::MipMajor,\n            &image,\n        );\n\n        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            dimension: Some(wgpu::TextureViewDimension::Cube),\n            ..wgpu::TextureViewDescriptor::default()\n        });\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: uniform_buf.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n            label: None,\n        });\n\n        let depth_view = Self::create_depth_texture(config, device);\n\n        Example {\n            camera,\n            sky_pipeline,\n            entity_pipeline,\n            bind_group,\n            uniform_buf,\n            entities,\n            depth_view,\n            staging_belt: wgpu::util::StagingBelt::new(device.clone(), 0x100),\n        }\n    }\n\n    #[expect(clippy::single_match)]\n    fn update(&mut self, event: winit::event::WindowEvent) {\n        match event {\n            winit::event::WindowEvent::CursorMoved { position, .. } => {\n                let norm_x = position.x as f32 / self.camera.screen_size.0 as f32 - 0.5;\n                let norm_y = position.y as f32 / self.camera.screen_size.1 as f32 - 0.5;\n                self.camera.angle_y = norm_x * 5.0;\n                self.camera.angle_xz = norm_y;\n            }\n            _ => {}\n        }\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        self.depth_view = Self::create_depth_texture(config, device);\n        self.camera.screen_size = (config.width, config.height);\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        // update rotation\n        let raw_uniforms = self.camera.to_uniform_data();\n        self.staging_belt\n            .write_buffer(\n                &mut encoder,\n                &self.uniform_buf,\n                0,\n                wgpu::BufferSize::new((raw_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(),\n            )\n            .copy_from_slice(bytemuck::cast_slice(&raw_uniforms));\n\n        self.staging_belt.finish();\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &self.depth_view,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Discard,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.set_pipeline(&self.entity_pipeline);\n\n            for entity in self.entities.iter() {\n                rpass.set_vertex_buffer(0, entity.vertex_buf.slice(..));\n                rpass.draw(0..entity.vertex_count, 0..1);\n            }\n\n            rpass.set_pipeline(&self.sky_pipeline);\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(std::iter::once(encoder.finish()));\n\n        self.staging_belt.recall();\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"skybox\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"skybox\",\n    image_path: \"/examples/features/src/skybox/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default().expect_fail(\n        wgpu_test::FailureCase::backend_adapter(wgpu::Backends::GL, \"ANGLE\"),\n    ),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_BCN: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"skybox-bc7\",\n    image_path: \"/examples/features/src/skybox/screenshot_bc7.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_ETC2: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"skybox-etc2\",\n    image_path: \"/examples/features/src/skybox/screenshot_etc2.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.016)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_ASTC: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"skybox-astc\",\n    image_path: \"/examples/features/src/skybox/screenshot_astc.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC,\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.017)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/skybox/models/rustacean-3d.mtl",
    "content": "# Blender MTL File: 'ferris.blend'\n# Material Count: 4\n\nnewmtl ferris_belly\nNs 96.078431\nKa 1.000000 1.000000 1.000000\nKd 0.800000 0.347323 0.172688\nKs 0.500000 0.500000 0.500000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 2\n\nnewmtl ferris_black\nNs 96.078431\nKa 1.000000 1.000000 1.000000\nKd 0.000000 0.000000 0.000000\nKs 0.500000 0.500000 0.500000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 2\n\nnewmtl ferris_orange\nNs 96.078431\nKa 1.000000 1.000000 1.000000\nKd 0.639282 0.129624 0.012797\nKs 0.500000 0.500000 0.500000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 2\n\nnewmtl ferris_white\nNs 96.078431\nKa 1.000000 1.000000 1.000000\nKd 0.640000 0.640000 0.640000\nKs 0.500000 0.500000 0.500000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 2\n"
  },
  {
    "path": "examples/features/src/skybox/models/rustacean-3d.obj",
    "content": "# Blender v2.79 (sub 0) OBJ File: 'ferris.blend'\n# www.blender.org\nmtllib rustacean-3d.mtl\no ferris_Circle.020\nv 0.343562 -0.115370 1.395143\nv 0.655653 0.451674 1.201330\nv 0.343562 0.271006 1.361339\nv 0.655654 -0.321079 1.268937\nv 0.966308 -0.140679 1.105856\nv 0.966308 0.245697 1.072053\nv 0.819567 0.095934 1.585309\nv 1.085411 0.410798 0.734117\nv 1.085411 -0.361954 0.801724\nv 1.200292 -0.174713 0.435707\nv 1.200292 0.190101 0.436587\nv 1.449873 0.035984 0.900069\nv 1.025759 -0.028440 0.163706\nv 0.479206 0.105580 0.016986\nv -0.001648 0.144262 -0.001157\nv 0.213908 -0.947237 0.554960\nv 0.421748 -0.853499 1.003199\nv 0.213908 -0.920817 0.856942\nv 0.421748 -0.906339 0.399235\nv 0.628899 -0.836463 0.545269\nv 0.628899 -0.810043 0.847251\nv 0.449768 -0.990436 0.637856\nv 0.449768 -0.977754 0.782807\nv 0.174526 -0.936381 0.466145\nv 0.193919 -0.922133 0.406498\nv 0.562379 -0.681502 1.154380\nv 0.872660 -0.634649 0.799880\nv 0.872660 -0.660002 0.510098\nv 0.482496 -1.188729 0.133281\nv 0.800427 -1.097982 0.162852\nv 1.039682 -1.340016 0.310857\nv 1.172861 -0.943232 0.440154\nv 1.040352 -0.542321 0.477912\nv 0.705973 -1.040242 0.326957\nv 0.907219 -0.612008 0.321213\nv 0.963359 -0.808547 0.257168\nv 0.483649 -0.982584 0.200456\nv 0.809329 -1.676895 -0.025794\nv 1.278937 -1.448384 0.048669\nv 1.382362 -0.944251 0.212947\nv 1.173484 -0.455032 0.372365\nv 1.092953 -1.047636 -0.047617\nv 1.172861 -0.809566 0.029961\nv 1.040352 -0.490720 0.215446\nv 0.705973 -0.954643 0.064271\nv 0.519263 -0.507354 0.204743\nv 1.013693 -0.227480 0.227631\nv 1.287415 -0.313834 0.237237\nv 1.364960 -0.076581 0.181727\nv 1.176592 -0.219118 0.004002\nv 1.130867 -0.336723 0.081494\nv 1.417569 -0.652758 -0.005180\nv 1.102248 0.869963 0.434285\nv 0.937434 0.564140 0.085652\nv 1.311133 0.279575 0.271811\nv 1.358940 0.590368 0.201054\nv 1.159818 0.370578 -0.024071\nv 1.183834 0.161861 0.052418\nv 1.453527 -0.084113 -0.018462\nv 0.617534 1.140735 0.567823\nv 0.876999 0.810416 0.904753\nv 0.399680 0.911546 1.095147\nv 0.461937 0.837762 0.242420\nv -0.000000 0.464342 1.346132\nv 0.000000 -0.308410 1.413738\nv -0.343562 -0.115371 1.395143\nv 0.000000 0.111769 1.766311\nv -0.655654 0.451673 1.201330\nv -0.343562 0.271005 1.361339\nv -0.655654 -0.321079 1.268937\nv -0.966308 -0.140680 1.105857\nv -0.966308 0.245696 1.072053\nv -0.819567 0.095933 1.585309\nv -1.085411 0.410798 0.734117\nv -1.085411 -0.361955 0.801724\nv -1.200292 -0.174713 0.435707\nv -1.200292 0.190100 0.436587\nv -1.449873 0.035983 0.900069\nv -1.025759 -0.028441 0.163706\nv -0.479206 0.105580 0.016986\nv 0.001648 0.144262 -0.001157\nv -0.213907 -0.947237 0.554960\nv -0.421747 -0.853500 1.003199\nv -0.213907 -0.920817 0.856942\nv -0.421747 -0.906340 0.399235\nv -0.628899 -0.836464 0.545269\nv -0.628899 -0.810044 0.847251\nv -0.449767 -0.990436 0.637856\nv -0.449767 -0.977755 0.782807\nv 0.000000 -0.977554 0.440547\nv 0.000000 -0.952702 0.318504\nv -0.174526 -0.936381 0.466145\nv -0.193918 -0.922133 0.406498\nv 0.000000 -1.007741 0.540521\nv 0.000000 -0.979008 0.868941\nv 0.000000 -0.867202 1.034359\nv 0.000000 -0.963683 0.250442\nv 0.000000 -0.667980 1.308930\nv -0.562378 -0.681502 1.154380\nv -0.872660 -0.634650 0.799880\nv -0.872660 -0.660003 0.510098\nv -0.482495 -1.188729 0.133281\nv -0.800426 -1.097982 0.162852\nv -1.039681 -1.340016 0.310857\nv -1.172860 -0.943233 0.440154\nv -1.040351 -0.542322 0.477912\nv -0.705972 -1.040243 0.326957\nv -0.907219 -0.612009 0.321213\nv -0.963359 -0.808547 0.257168\nv -0.483649 -0.982585 0.200456\nv -0.809328 -1.676896 -0.025794\nv -1.278936 -1.448385 0.048670\nv -1.382361 -0.944252 0.212947\nv -1.173484 -0.455033 0.372365\nv -1.092953 -1.047637 -0.047617\nv -1.172860 -0.809567 0.029961\nv -1.040351 -0.490721 0.215446\nv -0.705972 -0.954643 0.064271\nv 0.000000 -0.730753 0.146421\nv -0.519262 -0.507354 0.204743\nv -1.013693 -0.227481 0.227631\nv -1.287415 -0.313835 0.237238\nv -1.364960 -0.076582 0.181727\nv -1.176592 -0.219119 0.004002\nv -1.130866 -0.336723 0.081494\nv -1.417568 -0.652759 -0.005180\nv -1.102249 0.869962 0.434285\nv -0.937435 0.564139 0.085652\nv -1.311133 0.279574 0.271811\nv -1.358940 0.590367 0.201055\nv -1.159819 0.370577 -0.024071\nv -1.183834 0.161860 0.052418\nv -1.453527 -0.084114 -0.018462\nv -0.617535 1.140735 0.567823\nv -0.000000 1.293851 0.592225\nv -0.876999 0.810416 0.904753\nv -0.399681 0.911546 1.095147\nv -0.000000 0.948039 1.089680\nv -0.461938 0.837762 0.242420\nv -0.000000 0.935008 0.273219\nv 0.421748 -0.906339 0.399235\nv 0.213908 -0.947237 0.554960\nv 0.213908 -0.920817 0.856942\nv 0.421748 -0.853499 1.003199\nv 0.628899 -0.836463 0.545269\nv 0.628899 -0.810043 0.847251\nv 0.193919 -0.922133 0.406498\nv 0.000000 -0.952702 0.318504\nv 1.102248 0.869963 0.434285\nv 0.617534 1.140735 0.567823\nv -0.000000 1.293851 0.592225\nv -0.421747 -0.906340 0.399235\nv -0.213907 -0.947237 0.554960\nv -0.213907 -0.920817 0.856942\nv -0.421747 -0.853500 1.003199\nv -0.628899 -0.836464 0.545269\nv -0.628899 -0.810044 0.847251\nv -0.193918 -0.922133 0.406498\nv -1.102249 0.869962 0.434285\nv -0.617535 1.140735 0.567823\nvn 0.0000 -0.4368 0.8995\nvn 0.2051 -0.3077 0.9291\nvn 0.0000 0.0871 0.9962\nvn 0.1833 0.4441 0.8770\nvn 0.0000 0.5455 0.8381\nvn 0.4226 0.0790 0.9028\nvn 0.4060 -0.4949 0.7682\nvn 0.6783 -0.3555 0.6430\nvn 0.6960 0.3841 0.6066\nvn 0.3899 0.5340 0.7501\nvn 0.9401 0.0277 0.3397\nvn 0.7461 -0.5687 0.3462\nvn 0.9235 -0.3715 0.0959\nvn 0.9946 0.1040 0.0008\nvn 0.8265 0.4248 0.3693\nvn 0.0429 -0.0050 -0.9991\nvn 0.8653 0.4533 -0.2139\nvn -0.1990 0.2234 -0.9542\nvn 0.2076 -0.9735 -0.0956\nvn 0.1864 -0.9608 -0.2051\nvn 0.0753 -0.9969 0.0243\nvn 0.3425 -0.6712 -0.6573\nvn 0.0000 -0.9443 0.3291\nvn 0.1771 -0.9405 0.2899\nvn 0.0000 -0.8293 0.5588\nvn 0.0000 -0.9954 -0.0960\nvn 0.0000 -0.9745 -0.2243\nvn 0.0000 -0.9858 0.1677\nvn 0.0000 -0.8603 -0.5097\nvn 0.2563 -0.8081 0.5303\nvn 0.4194 -0.5813 0.6972\nvn 0.0000 -0.5390 0.8423\nvn 0.6633 -0.6696 0.3340\nvn 0.5694 -0.7664 0.2973\nvn 0.5665 -0.7135 -0.4121\nvn 0.4860 -0.8572 -0.1704\nvn 0.1453 -0.1681 0.9750\nvn -0.9087 0.3069 0.2830\nvn -0.4718 -0.8813 0.0279\nvn -0.0512 -0.4465 0.8933\nvn -0.4985 -0.8124 -0.3026\nvn 0.6489 -0.5432 0.5328\nvn 0.9988 0.0452 0.0185\nvn 0.9877 0.1307 0.0858\nvn -0.4748 -0.7197 -0.5065\nvn 0.7671 -0.5939 -0.2425\nvn 0.0303 -0.2539 0.9667\nvn -0.6417 -0.7291 -0.2376\nvn -0.8813 0.4492 0.1464\nvn 0.1052 0.1086 -0.9885\nvn 0.2404 0.1329 -0.9615\nvn 0.2497 0.4772 -0.8425\nvn -0.3275 0.3544 -0.8758\nvn -0.0395 -0.4896 -0.8710\nvn 0.0000 -0.3718 -0.9283\nvn 0.2081 -0.3972 -0.8938\nvn 0.3307 -0.5788 0.7454\nvn -0.7608 -0.6104 -0.2203\nvn 0.3931 -0.8722 -0.2910\nvn 0.0359 0.4048 -0.9137\nvn 0.1688 0.6839 -0.7098\nvn 0.8393 0.5311 -0.1162\nvn -0.0417 0.1765 -0.9834\nvn -0.7999 0.3768 0.4670\nvn -0.8393 0.5311 -0.1162\nvn -0.8000 -0.2840 0.5286\nvn -0.3594 -0.7498 -0.5555\nvn 0.8000 -0.2840 0.5286\nvn 0.4446 -0.8453 -0.2963\nvn 0.6152 0.5343 0.5796\nvn 0.7999 0.3768 0.4670\nvn 0.2460 0.6330 0.7340\nvn 0.0000 0.8563 0.5164\nvn 0.0000 0.6708 0.7416\nvn 0.3408 0.8184 0.4627\nvn 0.0000 0.6829 -0.7305\nvn 0.1480 0.6828 -0.7154\nvn 0.1015 0.5061 -0.8565\nvn 0.0000 0.5337 -0.8456\nvn -0.2051 -0.3077 0.9291\nvn -0.1833 0.4441 0.8770\nvn -0.4226 0.0790 0.9028\nvn -0.4060 -0.4949 0.7682\nvn -0.6783 -0.3555 0.6430\nvn -0.6960 0.3841 0.6066\nvn -0.3899 0.5340 0.7501\nvn -0.9401 0.0277 0.3397\nvn -0.7461 -0.5687 0.3462\nvn -0.9235 -0.3715 0.0959\nvn -0.9946 0.1040 0.0008\nvn -0.8265 0.4248 0.3693\nvn -0.0429 -0.0050 -0.9991\nvn 0.1990 0.2234 -0.9542\nvn -0.8653 0.4533 -0.2139\nvn -0.2076 -0.9735 -0.0956\nvn -0.3425 -0.6712 -0.6573\nvn -0.0753 -0.9969 0.0243\nvn -0.1864 -0.9608 -0.2051\nvn -0.1771 -0.9405 0.2899\nvn -0.2563 -0.8081 0.5303\nvn -0.4194 -0.5813 0.6972\nvn -0.6633 -0.6696 0.3340\nvn -0.5694 -0.7664 0.2973\nvn -0.5665 -0.7135 -0.4121\nvn -0.4860 -0.8572 -0.1704\nvn -0.1453 -0.1681 0.9750\nvn 0.0512 -0.4465 0.8933\nvn 0.4718 -0.8813 0.0279\nvn 0.9087 0.3069 0.2830\nvn 0.4985 -0.8124 -0.3026\nvn -0.6489 -0.5432 0.5328\nvn -0.9877 0.1307 0.0858\nvn -0.9988 0.0452 0.0185\nvn 0.4748 -0.7197 -0.5065\nvn -0.7671 -0.5939 -0.2425\nvn -0.0303 -0.2539 0.9667\nvn 0.6417 -0.7291 -0.2376\nvn 0.8813 0.4492 0.1464\nvn 0.3275 0.3544 -0.8758\nvn -0.2497 0.4772 -0.8425\nvn -0.1052 0.1086 -0.9885\nvn -0.2404 0.1329 -0.9615\nvn 0.0395 -0.4896 -0.8710\nvn -0.2081 -0.3972 -0.8938\nvn -0.3307 -0.5788 0.7454\nvn 0.7608 -0.6104 -0.2203\nvn -0.3931 -0.8722 -0.2910\nvn -0.0359 0.4048 -0.9137\nvn 0.0417 0.1765 -0.9834\nvn -0.1688 0.6839 -0.7098\nvn 0.3594 -0.7498 -0.5555\nvn -0.4446 -0.8453 -0.2963\nvn -0.6152 0.5343 0.5796\nvn -0.2460 0.6330 0.7340\nvn -0.3408 0.8184 0.4627\nvn -0.1015 0.5061 -0.8565\nvn -0.1480 0.6828 -0.7154\nvn -0.1442 -0.9514 0.2718\nvn -0.1442 -0.9842 -0.1025\nvn 0.2552 -0.9633 -0.0827\nvn 0.2552 -0.9343 0.2487\nvn 0.6107 -0.7499 0.2542\nvn 0.6107 -0.7827 -0.1201\nvn 0.2342 -0.9079 -0.3475\nvn 0.2342 -0.8338 0.4999\nvn 0.0000 -0.9827 -0.1850\nvn 0.2410 -0.9537 -0.1795\nvn 0.1442 -0.9514 0.2718\nvn -0.2552 -0.9343 0.2487\nvn -0.2552 -0.9633 -0.0827\nvn 0.1442 -0.9842 -0.1025\nvn -0.6107 -0.7499 0.2542\nvn -0.6107 -0.7827 -0.1201\nvn -0.2342 -0.8338 0.4999\nvn -0.2342 -0.9079 -0.3475\nvn -0.2410 -0.9537 -0.1795\nvn 0.0887 -0.0077 -0.9960\nvn 0.0738 0.0631 -0.9953\nvn -0.0887 -0.0077 -0.9960\nvn -0.0738 0.0631 -0.9953\nusemtl ferris_orange\ns 1\nf 65//1 1//2 67//3\nf 1//2 3//4 67//3\nf 3//4 64//5 67//3\nf 3//4 1//2 7//6\nf 1//2 4//7 7//6\nf 4//7 5//8 7//6\nf 5//8 6//9 7//6\nf 6//9 2//10 7//6\nf 2//10 3//4 7//6\nf 6//9 5//8 12//11\nf 5//8 9//12 12//11\nf 9//12 10//13 12//11\nf 10//13 11//14 12//11\nf 11//14 8//15 12//11\nf 8//15 6//9 12//11\nf 13//16 11//14 49//17 50//18\nf 16//19 24//20 25//21 19//22\nf 95//23 18//24 96//25\nf 18//24 95//23 94//26 16//19\nf 90//27 24//20 16//19 94//26\nf 91//28 97//29 19//22 25//21\nf 18//24 17//30 96//25\nf 26//31 98//32 96//25 17//30\nf 4//7 1//2 65//1 98//32 26//31\nf 5//8 4//7 26//31 27//33 9//12\nf 21//34 27//33 26//31 17//30\nf 20//35 28//36 27//33 21//34\nf 32//37 36//38 30//39 31//40\nf 31//40 30//39 38//41\nf 33//42 32//37 40//43 41//44\nf 35//45 36//38 32//37 33//42\nf 40//43 32//37 31//40 39//46\nf 36//38 34//47 30//39\nf 30//39 34//47 29//48\nf 29//48 34//47 37//49\nf 37//49 34//47 36//38\nf 31//40 38//41 39//46\nf 33//42 28//36 35//45\nf 10//13 9//12 33//42\nf 9//12 27//33 33//42\nf 27//33 28//36 33//42\nf 41//44 10//13 33//42\nf 42//50 38//41 30//39\nf 44//51 41//44 40//43 43//52\nf 35//45 44//51 43//52 36//38\nf 40//43 39//46 42//50 43//52\nf 30//39 29//48 45//53\nf 29//48 37//49 45//53\nf 37//49 36//38 45//53\nf 42//50 39//46 38//41\nf 10//13 41//44 44//51 47//54\nf 119//55 46//56 19//22 97//29\nf 19//22 46//56 28//36 20//35\nf 28//36 46//56 35//45\nf 46//56 13//16 47//54\nf 35//45 46//56 47//54 44//51\nf 11//14 10//13 48//57 49//17\nf 10//13 47//54 51//58 48//57\nf 47//54 13//16 50//18 51//58\nf 51//58 50//18 52//59\nf 49//17 48//57 52//59\nf 48//57 51//58 52//59\nf 50//18 49//17 52//59\nf 54//60 149//61 56//62 57//63\nf 127//64 130//65 129//66\nf 11//14 13//16 58//67 55//68\nf 58//67 57//63 59//69\nf 56//62 55//68 59//69\nf 55//68 58//67 59//69\nf 57//63 56//62 59//69\nf 61//70 8//15 53//71\nf 8//15 11//14 53//71\nf 2//10 6//9 8//15 61//70\nf 62//72 2//10 61//70\nf 151//73 138//74 62//72 150//75\nf 150//75 61//70 53//71\nf 150//75 62//72 61//70\nf 64//5 3//4 2//10 62//72 138//74\nf 135//76 60//77 63//78 140//79\nf 54//60 63//78 60//77 149//61\nf 65//1 67//3 66//80\nf 66//80 67//3 69//81\nf 69//81 67//3 64//5\nf 69//81 73//82 66//80\nf 66//80 73//82 70//83\nf 70//83 73//82 71//84\nf 71//84 73//82 72//85\nf 72//85 73//82 68//86\nf 68//86 73//82 69//81\nf 72//85 78//87 71//84\nf 71//84 78//87 75//88\nf 75//88 78//87 76//89\nf 76//89 78//87 77//90\nf 77//90 78//87 74//91\nf 74//91 78//87 72//85\nf 79//92 124//93 123//94 77//90\nf 82//95 85//96 93//97 92//98\nf 95//23 96//25 84//99\nf 84//99 82//95 94//26 95//23\nf 90//27 94//26 82//95 92//98\nf 91//28 93//97 85//96 97//29\nf 84//99 96//25 83//100\nf 99//101 83//100 96//25 98//32\nf 70//83 99//101 98//32 65//1 66//80\nf 71//84 75//88 100//102 99//101 70//83\nf 87//103 83//100 99//101 100//102\nf 86//104 87//103 100//102 101//105\nf 105//106 104//107 103//108 109//109\nf 104//107 111//110 103//108\nf 106//111 114//112 113//113 105//106\nf 108//114 106//111 105//106 109//109\nf 113//113 112//115 104//107 105//106\nf 109//109 103//108 107//116\nf 103//108 102//117 107//116\nf 102//117 110//118 107//116\nf 110//118 109//109 107//116\nf 104//107 112//115 111//110\nf 106//111 108//114 101//105\nf 76//89 106//111 75//88\nf 75//88 106//111 100//102\nf 100//102 106//111 101//105\nf 118//119 116//120 109//109\nf 114//112 106//111 76//89\nf 115//121 103//108 111//110\nf 117//122 116//120 113//113 114//112\nf 108//114 109//109 116//120 117//122\nf 113//113 116//120 115//121 112//115\nf 103//108 115//121 118//119\nf 103//108 118//119 102//117\nf 102//117 118//119 110//118\nf 110//118 118//119 109//109\nf 115//121 111//110 112//115\nf 76//89 121//123 117//122 114//112\nf 119//55 97//29 85//96 120//124\nf 85//96 86//104 101//105 120//124\nf 101//105 108//114 120//124\nf 120//124 121//123 79//92\nf 108//114 117//122 121//123 120//124\nf 77//90 123//94 122//125 76//89\nf 76//89 122//125 125//126 121//123\nf 121//123 125//126 124//93 79//92\nf 125//126 126//127 124//93\nf 123//94 126//127 122//125\nf 122//125 126//127 125//126\nf 124//93 126//127 123//94\nf 128//128 131//129 130//65 159//130\nf 77//90 129//66 132//131 79//92\nf 132//131 133//132 131//129\nf 130//65 133//132 129//66\nf 129//66 133//132 132//131\nf 131//129 133//132 130//65\nf 136//133 127//64 74//91\nf 74//91 127//64 77//90\nf 68//86 136//133 74//91 72//85\nf 137//134 136//133 68//86\nf 151//73 160//135 137//134 138//74\nf 160//135 127//64 136//133\nf 160//135 136//133 137//134\nf 64//5 138//74 137//134 68//86 69//81\nf 135//76 140//79 139//136 134//137\nf 128//128 159//130 134//137 139//136\nf 56//62 53//71 55//68\nf 77//90 127//64 129//66\nf 53//71 11//14 55//68\nf 118//119 115//121 116//120\nf 36//38 43//52 45//53\nf 43//52 42//50 45//53\nf 30//39 45//53 42//50\nusemtl ferris_black\nf 143//138 142//139 22//140 23//141\nf 146//142 23//141 22//140 145//143\nf 141//144 22//140 142//139\nf 144//145 23//141 146//142\nf 145//143 22//140 141//144\nf 148//146 147//147 24//20 90//27\nf 154//148 89//149 88//150 153//151\nf 157//152 156//153 88//150 89//149\nf 154//148 155//154 89//149\nf 152//155 153//151 88//150\nf 156//153 152//155 88//150\nf 148//146 90//27 92//98 158//156\nusemtl ferris_white\nf 143//138 23//141 144//145\nf 155//154 157//152 89//149\nusemtl ferris_belly\nf 14//157 13//16 46//56\nf 15//158 14//157 46//56 119//55\nf 13//16 54//60 57//63 58//67\nf 14//157 63//78 54//60 13//16\nf 15//158 140//79 63//78 14//157\nf 80//159 120//124 79//92\nf 81//160 119//55 120//124 80//159\nf 79//92 132//131 131//129 128//128\nf 80//159 79//92 128//128 139//136\nf 81//160 80//159 139//136 140//79\n"
  },
  {
    "path": "examples/features/src/skybox/shader.wgsl",
    "content": "struct SkyOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) uv: vec3<f32>,\n};\n\nstruct Data {\n    // from camera to screen\n    proj: mat4x4<f32>,\n    // from screen to camera\n    proj_inv: mat4x4<f32>,\n    // from world to camera\n    view: mat4x4<f32>,\n    // camera position\n    cam_pos: vec4<f32>,\n};\n@group(0)\n@binding(0)\nvar<uniform> r_data: Data;\n\n@vertex\nfn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {\n    // hacky way to draw a large triangle\n    let tmp1 = i32(vertex_index) / 2;\n    let tmp2 = i32(vertex_index) & 1;\n    let pos = vec4<f32>(\n        f32(tmp1) * 4.0 - 1.0,\n        f32(tmp2) * 4.0 - 1.0,\n        1.0,\n        1.0\n    );\n\n    // transposition = inversion for this orthonormal matrix\n    let inv_model_view = transpose(mat3x3<f32>(r_data.view[0].xyz, r_data.view[1].xyz, r_data.view[2].xyz));\n    let unprojected = r_data.proj_inv * pos;\n\n    var result: SkyOutput;\n    result.uv = inv_model_view * unprojected.xyz;\n    result.position = pos;\n    return result;\n}\n\nstruct EntityOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(1) normal: vec3<f32>,\n    @location(3) view: vec3<f32>,\n};\n\n@vertex\nfn vs_entity(\n    @location(0) pos: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n) -> EntityOutput {\n    var result: EntityOutput;\n    result.normal = normal;\n    result.view = pos - r_data.cam_pos.xyz;\n    result.position = r_data.proj * r_data.view * vec4<f32>(pos, 1.0);\n    return result;\n}\n\n@group(0)\n@binding(1)\nvar r_texture: texture_cube<f32>;\n@group(0)\n@binding(2)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_sky(vertex: SkyOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_texture, r_sampler, vertex.uv);\n}\n\n@fragment\nfn fs_entity(vertex: EntityOutput) -> @location(0) vec4<f32> {\n    let incident = normalize(vertex.view);\n    let normal = normalize(vertex.normal);\n    let reflected = incident - 2.0 * dot(normal, incident) * normal;\n\n    let reflected_color = textureSample(r_texture, r_sampler, reflected).rgb;\n    return vec4<f32>(vec3<f32>(0.1) + 0.5 * reflected_color, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/srgb_blend/README.md",
    "content": "# srgb_blend\n\nThis example shows blending in sRGB or linear space.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples srgb_blend linear\n```\n\n```\ncargo run --bin wgpu-examples srgb_blend\n```\n\n## Screenshots\n\nBlending in linear space:\n\n![sRGB blend example](./screenshot-linear.png)\n\nBlending in sRGB space:\n\n![sRGB blend example](./screenshot-srgb.png)\n"
  },
  {
    "path": "examples/features/src/srgb_blend/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse wgpu::util::DeviceExt;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n    _color: [f32; 4],\n}\n\nfn vertex(pos: [i8; 2], _color: [f32; 4], offset: f32) -> Vertex {\n    let scale = 0.5;\n    Vertex {\n        _pos: [\n            (pos[0] as f32 + offset) * scale,\n            (pos[1] as f32 + offset) * scale,\n            0.0,\n            1.0,\n        ],\n        _color,\n    }\n}\n\nfn quad(vertices: &mut Vec<Vertex>, indices: &mut Vec<u16>, color: [f32; 4], offset: f32) {\n    let base = vertices.len() as u16;\n\n    vertices.extend_from_slice(&[\n        vertex([-1, -1], color, offset),\n        vertex([1, -1], color, offset),\n        vertex([1, 1], color, offset),\n        vertex([-1, 1], color, offset),\n    ]);\n\n    indices.extend([0, 1, 2, 2, 3, 0].iter().map(|i| base + *i));\n}\n\nfn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let mut vertices = Vec::new();\n    let mut indices = Vec::new();\n\n    let red = [1.0, 0.0, 0.0, 0.5];\n    let blue = [0.0, 0.0, 1.0, 0.5];\n\n    quad(&mut vertices, &mut indices, red, 0.5);\n    quad(&mut vertices, &mut indices, blue, -0.5);\n\n    (vertices, indices)\n}\n\nstruct Example<const SRGB: bool> {\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_count: usize,\n    bind_group: wgpu::BindGroup,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl<const SRGB: bool> crate::framework::Example for Example<SRGB> {\n    const SRGB: bool = SRGB;\n\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::POLYGON_MODE_LINE\n    }\n\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        // Create the vertex and index buffers\n        let vertex_size = size_of::<Vertex>();\n        let (vertex_data, index_data) = create_vertices();\n\n        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX,\n        });\n\n        // Create pipeline layout\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[],\n        });\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        // Create bind group\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            entries: &[],\n            label: None,\n        });\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let vertex_buffers = [wgpu::VertexBufferLayout {\n            array_stride: vertex_size as wgpu::BufferAddress,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &[\n                wgpu::VertexAttribute {\n                    format: wgpu::VertexFormat::Float32x4,\n                    offset: 0,\n                    shader_location: 0,\n                },\n                wgpu::VertexAttribute {\n                    format: wgpu::VertexFormat::Float32x4,\n                    offset: 4 * 4,\n                    shader_location: 1,\n                },\n            ],\n        }];\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &vertex_buffers,\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            primitive: wgpu::PrimitiveState {\n                cull_mode: Some(wgpu::Face::Back),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        // Done\n        Example {\n            vertex_buf,\n            index_buf,\n            index_count: index_data.len(),\n            bind_group,\n            pipeline,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        _config: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.0,\n                            g: 0.0,\n                            b: 0.0,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.push_debug_group(\"Prepare data for draw.\");\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint16);\n            rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));\n            rpass.pop_debug_group();\n            rpass.insert_debug_marker(\"Draw!\");\n            rpass.draw_indexed(0..self.index_count as u32, 0, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    let mut args = std::env::args();\n    args.next();\n    if Some(\"linear\") == args.nth(1).as_deref() {\n        crate::framework::run::<Example<false>>(\"srgb-blend-linear\");\n    } else {\n        crate::framework::run::<Example<true>>(\"srgb-blend-srg\");\n    }\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_SRGB: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"srgb-blend-srg\",\n    // Generated on WARP/Windows\n    image_path: \"/examples/features/src/srgb_blend/screenshot-srgb.png\",\n    width: 192,\n    height: 192,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.04)],\n    _phantom: std::marker::PhantomData::<Example<true>>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_LINEAR: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"srgb-blend-linear\",\n    // Generated on WARP/Windows\n    image_path: \"/examples/features/src/srgb_blend/screenshot-linear.png\",\n    width: 192,\n    height: 192,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.04)],\n    _phantom: std::marker::PhantomData::<Example<false>>,\n};\n"
  },
  {
    "path": "examples/features/src/srgb_blend/shader.wgsl",
    "content": "struct VertexOutput {\n    @location(0) color: vec4<f32>,\n    @builtin(position) position: vec4<f32>,\n};\n\n@vertex\nfn vs_main(\n    @location(0) position: vec4<f32>,\n    @location(1) color: vec4<f32>,\n) -> VertexOutput {\n    var result: VertexOutput;\n    result.color = color;\n    result.position = position;\n    return result;\n}\n\n@group(0)\n@binding(1)\nvar<uniform> color: vec4<f32>;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return vertex.color;\n}\n"
  },
  {
    "path": "examples/features/src/stencil_triangles/README.md",
    "content": "# stencil_triangles\n\nThis example renders two different sized triangles to display three same sized triangles,\nby demonstrating the use of stencil buffers.\n\nFirst it draws a small \"mask\" triangle, which sets the stencil buffer at every pixel to 1.\n\nThen, it draws a larger \"outer\" triangle which only touches pixels where the stencil buffer is less than 1.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples stencil_triangles\n```\n\n## Screenshots\n\n![Stencil Triangles window](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/stencil_triangles/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse wgpu::util::DeviceExt;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 4],\n}\n\nfn vertex(x: f32, y: f32) -> Vertex {\n    Vertex {\n        _pos: [x, y, 0.0, 1.0],\n    }\n}\n\nstruct Example {\n    outer_vertex_buffer: wgpu::Buffer,\n    mask_vertex_buffer: wgpu::Buffer,\n    outer_pipeline: wgpu::RenderPipeline,\n    mask_pipeline: wgpu::RenderPipeline,\n    stencil_buffer: wgpu::Texture,\n}\n\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) -> Self {\n        // Create the vertex and index buffers\n        let vertex_size = size_of::<Vertex>();\n        let outer_vertices = [vertex(-1.0, -1.0), vertex(1.0, -1.0), vertex(0.0, 1.0)];\n        let mask_vertices = [vertex(-0.5, 0.0), vertex(0.0, -1.0), vertex(0.5, 0.0)];\n\n        let outer_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Outer Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&outer_vertices),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let mask_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Mask Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&mask_vertices),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[],\n            immediate_size: 0,\n        });\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let vertex_buffers = [wgpu::VertexBufferLayout {\n            array_stride: vertex_size as wgpu::BufferAddress,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &[wgpu::VertexAttribute {\n                format: wgpu::VertexFormat::Float32x4,\n                offset: 0,\n                shader_location: 0,\n            }],\n        }];\n\n        let mask_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &vertex_buffers,\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::empty(),\n                })],\n            }),\n            primitive: Default::default(),\n            depth_stencil: Some(wgpu::DepthStencilState::stencil(\n                wgpu::TextureFormat::Stencil8,\n                wgpu::StencilState {\n                    front: wgpu::StencilFaceState {\n                        compare: wgpu::CompareFunction::Always,\n                        pass_op: wgpu::StencilOperation::Replace,\n                        ..Default::default()\n                    },\n                    back: wgpu::StencilFaceState::IGNORE,\n                    read_mask: !0,\n                    write_mask: !0,\n                },\n            )),\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let outer_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &vertex_buffers,\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: Default::default(),\n            depth_stencil: Some(wgpu::DepthStencilState::stencil(\n                wgpu::TextureFormat::Stencil8,\n                wgpu::StencilState {\n                    front: wgpu::StencilFaceState {\n                        compare: wgpu::CompareFunction::Greater,\n                        ..Default::default()\n                    },\n                    back: wgpu::StencilFaceState::IGNORE,\n                    read_mask: !0,\n                    write_mask: !0,\n                },\n            )),\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"Stencil buffer\"),\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Stencil8,\n            view_formats: &[],\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        });\n\n        // Done\n        Example {\n            outer_vertex_buffer,\n            mask_vertex_buffer,\n            outer_pipeline,\n            mask_pipeline,\n            stencil_buffer,\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        // empty\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        self.stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"Stencil buffer\"),\n            size: wgpu::Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Stencil8,\n            view_formats: &[],\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        });\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let depth_view = self.stencil_buffer.create_view(&Default::default());\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &depth_view,\n                    depth_ops: None,\n                    stencil_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(0),\n                        store: wgpu::StoreOp::Store,\n                    }),\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_stencil_reference(1);\n\n            rpass.set_pipeline(&self.mask_pipeline);\n            rpass.set_vertex_buffer(0, self.mask_vertex_buffer.slice(..));\n            rpass.draw(0..3, 0..1);\n\n            rpass.set_pipeline(&self.outer_pipeline);\n            rpass.set_vertex_buffer(0, self.outer_vertex_buffer.slice(..));\n            rpass.draw(0..3, 0..1);\n        }\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"stencil-triangles\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"stencil-triangles\",\n    image_path: \"/examples/features/src/stencil_triangles/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default(),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.04)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/stencil_triangles/shader.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n};\n\n@vertex\nfn vs_main(@location(0) position: vec4<f32>) -> VertexOutput {\n    var result: VertexOutput;\n    result.position = position;\n    return result;\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return vec4<f32>(0.97, 0.88, 0.21, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/storage_texture/README.md",
    "content": "# storage_texture\n\nA simple example that uses a storage texture to compute an image of the Mandelbrot set (https://en.wikipedia.org/wiki/Mandelbrot_set) and either saves it as an image or presents it to the browser screen in such a way that it can be saved as an image.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples storage_texture\n```\n\n\n## Example Output\n\n![Example output](./example.png)"
  },
  {
    "path": "examples/features/src/storage_texture/mod.rs",
    "content": "//! This example demonstrates the basic usage of storage textures for the purpose of\n//! creating a digital image of the Mandelbrot set\n//! (<https://en.wikipedia.org/wiki/Mandelbrot_set>).\n//!\n//! Storage textures work like normal textures but they operate similar to storage buffers\n//! in that they can be written to. The issue is that as it stands, write-only is the\n//! only valid access mode for storage textures in WGSL and although there is a wgpu feature\n//! to allow for read-write access, this is unfortunately a native-only feature and thus\n//! we won't be using it here. If we needed a reference texture, we would need to add a\n//! second texture to act as a reference and attach that as well. Luckily, we don't need\n//! to read anything in our shader except the dimensions of our texture, which we can\n//! easily get via `textureDimensions`.\n//!\n//! A lot of things aren't explained here via comments. See hello-compute and\n//! repeated-compute for code that is more thoroughly commented.\n\n#[cfg(not(target_arch = \"wasm32\"))]\nuse crate::utils::output_image_native;\n#[cfg(target_arch = \"wasm32\")]\nuse crate::utils::output_image_wasm;\n\nconst TEXTURE_DIMS: (usize, usize) = (512, 512);\n\nasync fn run(_path: Option<String>) {\n    let mut texture_data = vec![0u8; TEXTURE_DIMS.0 * TEXTURE_DIMS.1 * 4];\n\n    let instance = wgpu::Instance::default();\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .unwrap();\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: wgpu::Features::empty(),\n            required_limits: wgpu::Limits::downlevel_defaults(),\n            experimental_features: wgpu::ExperimentalFeatures::disabled(),\n            memory_hints: wgpu::MemoryHints::MemoryUsage,\n            trace: wgpu::Trace::Off,\n        })\n        .await\n        .unwrap();\n\n    let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    let storage_texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: TEXTURE_DIMS.0 as u32,\n            height: TEXTURE_DIMS.1 as u32,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let storage_texture_view = storage_texture.create_view(&wgpu::TextureViewDescriptor::default());\n    let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_of_val(&texture_data[..]) as u64,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[wgpu::BindGroupLayoutEntry {\n            binding: 0,\n            visibility: wgpu::ShaderStages::COMPUTE,\n            ty: wgpu::BindingType::StorageTexture {\n                access: wgpu::StorageTextureAccess::WriteOnly,\n                format: wgpu::TextureFormat::Rgba8Unorm,\n                view_dimension: wgpu::TextureViewDimension::D2,\n            },\n            count: None,\n        }],\n    });\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::TextureView(&storage_texture_view),\n        }],\n    });\n\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &shader,\n        entry_point: Some(\"main\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n\n    log::info!(\"Wgpu context set up.\");\n    //----------------------------------------\n\n    let mut command_encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        compute_pass.set_bind_group(0, &bind_group, &[]);\n        compute_pass.set_pipeline(&pipeline);\n        compute_pass.dispatch_workgroups(TEXTURE_DIMS.0 as u32, TEXTURE_DIMS.1 as u32, 1);\n    }\n    command_encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &storage_texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &output_staging_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                // This needs to be padded to 256.\n                bytes_per_row: Some((TEXTURE_DIMS.0 * 4) as u32),\n                rows_per_image: Some(TEXTURE_DIMS.1 as u32),\n            },\n        },\n        wgpu::Extent3d {\n            width: TEXTURE_DIMS.0 as u32,\n            height: TEXTURE_DIMS.1 as u32,\n            depth_or_array_layers: 1,\n        },\n    );\n    queue.submit(Some(command_encoder.finish()));\n\n    let buffer_slice = output_staging_buffer.slice(..);\n    let (sender, receiver) = flume::bounded(1);\n    buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    receiver.recv_async().await.unwrap().unwrap();\n    log::info!(\"Output buffer mapped\");\n    {\n        let view = buffer_slice.get_mapped_range();\n        texture_data.copy_from_slice(&view[..]);\n    }\n    log::info!(\"GPU data copied to local.\");\n    output_staging_buffer.unmap();\n\n    #[cfg(not(target_arch = \"wasm32\"))]\n    output_image_native(texture_data.to_vec(), TEXTURE_DIMS, _path.unwrap());\n    #[cfg(target_arch = \"wasm32\")]\n    output_image_wasm(texture_data.to_vec(), TEXTURE_DIMS);\n    log::info!(\"Done.\")\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::builder()\n            .filter_level(log::LevelFilter::Info)\n            .format_timestamp_nanos()\n            .init();\n\n        let path = std::env::args()\n            .nth(2)\n            .unwrap_or_else(|| \"please_don't_git_push_me.png\".to_string());\n        pollster::block_on(run(Some(path)));\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init_with_level(log::Level::Info).expect(\"could not initialize logger\");\n        wasm_bindgen_futures::spawn_local(run(None));\n    }\n}\n"
  },
  {
    "path": "examples/features/src/storage_texture/shader.wgsl",
    "content": "const MAX_ITERATIONS: u32 = 50u;\n\n@group(0)\n@binding(0)\nvar texture: texture_storage_2d<rgba8unorm, write>;\n\n@compute\n@workgroup_size(1)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n    var final_iteration = MAX_ITERATIONS;\n    var c = vec2(\n        // Translated to put everything nicely in frame.\n        (f32(id.x) / f32(textureDimensions(texture).x)) * 3.0 - 2.25,\n        (f32(id.y) / f32(textureDimensions(texture).y)) * 3.0 - 1.5\n    );\n    var current_z = c;\n    var next_z: vec2<f32>;\n    for (var i = 0u; i < MAX_ITERATIONS; i++) {\n        next_z.x = (current_z.x * current_z.x - current_z.y * current_z.y) + c.x;\n        next_z.y = (2.0 * current_z.x * current_z.y) + c.y;\n        current_z = next_z;\n        if length(current_z) > 4.0 {\n            final_iteration = i;\n            break;\n        }\n    }\n    let value = f32(final_iteration) / f32(MAX_ITERATIONS);\n    textureStore(texture, vec2(i32(id.x), i32(id.y)), vec4(value, value, value, 1.0));\n}"
  },
  {
    "path": "examples/features/src/texture_arrays/README.md",
    "content": "# texture_arrays\n\n## To Run\n\n```\ncargo run --bin wgpu-examples texture_arrays\n```\n\n## Example Output\n\n![Example output](./screenshot.png)"
  },
  {
    "path": "examples/features/src/texture_arrays/indexing.wgsl",
    "content": "struct VertexInput {\n    @location(0) position: vec2<f32>,\n    @location(1) tex_coord: vec2<f32>,\n    @location(2) index: i32,\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coord: vec2<f32>,\n    @location(1) index: i32,\n}\n\n@vertex\nfn vert_main(vertex: VertexInput) -> VertexOutput {\n    var outval: VertexOutput;\n    outval.position = vec4<f32>(vertex.position.x, vertex.position.y, 0.0, 1.0);\n    outval.tex_coord = vertex.tex_coord;\n    outval.index = vertex.index;\n    return outval;\n}\n\nstruct FragmentInput {\n    @location(0) tex_coord: vec2<f32>,\n    @location(1) index: i32,\n}\n\n@group(0) @binding(0)\nvar texture_array_top: binding_array<texture_2d<f32>>;\n@group(0) @binding(1)\nvar texture_array_bottom: binding_array<texture_2d<f32>>;\n@group(0) @binding(2)\nvar sampler_array: binding_array<sampler>;\n\nstruct Uniforms {\n    index: u32,\n}\n\n@group(1) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\n@fragment\nfn uniform_main(fragment: FragmentInput) -> @location(0) vec4<f32> {\n    var outval: vec3<f32>;\n    if fragment.tex_coord.y <= 0.5 {\n        outval = textureSampleLevel(\n            texture_array_top[uniforms.index],\n            sampler_array[uniforms.index],\n            fragment.tex_coord,\n            0.0\n        ).rgb;\n    } else {\n        outval = textureSampleLevel(\n            texture_array_bottom[uniforms.index],\n            sampler_array[uniforms.index],\n            fragment.tex_coord,\n            0.0\n        ).rgb;\n    }\n\n    return vec4<f32>(outval.x, outval.y, outval.z, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/texture_arrays/mod.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse std::num::{NonZeroU32, NonZeroU64};\nuse wgpu::util::DeviceExt;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\nstruct Vertex {\n    _pos: [f32; 2],\n    _tex_coord: [f32; 2],\n    _index: u32,\n}\n\nfn vertex(pos: [i8; 2], tc: [i8; 2], index: i8) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n        _index: index as u32,\n    }\n}\n\nfn create_vertices() -> Vec<Vertex> {\n    vec![\n        // left rectangle\n        vertex([-1, -1], [0, 1], 0),\n        vertex([-1, 1], [0, 0], 0),\n        vertex([0, 1], [1, 0], 0),\n        vertex([0, -1], [1, 1], 0),\n        // right rectangle\n        vertex([0, -1], [0, 1], 1),\n        vertex([0, 1], [0, 0], 1),\n        vertex([1, 1], [1, 0], 1),\n        vertex([1, -1], [1, 1], 1),\n    ]\n}\n\nfn create_indices() -> Vec<u16> {\n    vec![\n        // Left rectangle\n        0, 1, 2, // 1st\n        2, 0, 3, // 2nd\n        // Right rectangle\n        4, 5, 6, // 1st\n        6, 4, 7, // 2nd\n    ]\n}\n\n#[derive(Copy, Clone)]\nenum Color {\n    Red,\n    Green,\n    Blue,\n    White,\n}\n\nfn create_texture_data(color: Color) -> [u8; 4] {\n    match color {\n        Color::Red => [255, 0, 0, 255],\n        Color::Green => [0, 255, 0, 255],\n        Color::Blue => [0, 0, 255, 255],\n        Color::White => [255, 255, 255, 255],\n    }\n}\n\nstruct Example {\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    uniform_bind_group: wgpu::BindGroup,\n    vertex_buffer: wgpu::Buffer,\n    index_buffer: wgpu::Buffer,\n    index_format: wgpu::IndexFormat,\n    uniform_workaround: bool,\n}\n\nimpl crate::framework::Example for Example {\n    fn optional_features() -> wgpu::Features {\n        wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING\n    }\n    fn required_features() -> wgpu::Features {\n        wgpu::Features::TEXTURE_BINDING_ARRAY\n    }\n    fn required_limits() -> wgpu::Limits {\n        wgpu::Limits {\n            max_binding_array_elements_per_shader_stage: 6,\n            max_binding_array_sampler_elements_per_shader_stage: 2,\n            ..wgpu::Limits::downlevel_defaults()\n        }\n    }\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        let mut uniform_workaround = false;\n        let base_shader_module = device.create_shader_module(wgpu::include_wgsl!(\"indexing.wgsl\"));\n        let env_override = match std::env::var(\"WGPU_TEXTURE_ARRAY_STYLE\") {\n            Ok(value) => match &*value.to_lowercase() {\n                \"nonuniform\" | \"non_uniform\" => Some(true),\n                \"uniform\" => Some(false),\n                _ => None,\n            },\n            Err(_) => None,\n        };\n        let fragment_entry_point = match (device.features(), env_override) {\n            (_, Some(false)) => {\n                uniform_workaround = true;\n                \"uniform_main\"\n            }\n            (_, Some(true)) => \"non_uniform_main\",\n            (f, _)\n                if f.contains(\n                    wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n                ) =>\n            {\n                \"non_uniform_main\"\n            }\n            _ => {\n                uniform_workaround = true;\n                \"uniform_main\"\n            }\n        };\n        let non_uniform_shader_module;\n        // TODO: Because naga's capabilities are evaluated on validate, not on write, we cannot make a shader module with unsupported\n        // capabilities even if we don't use it. So for now put it in a separate module.\n        let fragment_shader_module = if !uniform_workaround {\n            non_uniform_shader_module =\n                device.create_shader_module(wgpu::include_wgsl!(\"non_uniform_indexing.wgsl\"));\n            &non_uniform_shader_module\n        } else {\n            &base_shader_module\n        };\n\n        println!(\"Using fragment entry point '{fragment_entry_point}'\");\n\n        let vertex_size = size_of::<Vertex>();\n        let vertex_data = create_vertices();\n        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let index_data = create_indices();\n        let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: wgpu::BufferUsages::INDEX,\n        });\n\n        let mut texture_index_buffer_contents = vec![0u32; 128];\n        texture_index_buffer_contents[0] = 0;\n        texture_index_buffer_contents[64] = 1;\n        let texture_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Index Buffer\"),\n            contents: bytemuck::cast_slice(&texture_index_buffer_contents),\n            usage: wgpu::BufferUsages::UNIFORM,\n        });\n\n        let red_texture_data = create_texture_data(Color::Red);\n        let green_texture_data = create_texture_data(Color::Green);\n        let blue_texture_data = create_texture_data(Color::Blue);\n        let white_texture_data = create_texture_data(Color::White);\n\n        let texture_descriptor = wgpu::TextureDescriptor {\n            size: wgpu::Extent3d::default(),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n            label: None,\n            view_formats: &[],\n        };\n        let red_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"red\"),\n            view_formats: &[],\n            ..texture_descriptor\n        });\n        let green_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"green\"),\n            view_formats: &[],\n            ..texture_descriptor\n        });\n        let blue_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"blue\"),\n            view_formats: &[],\n            ..texture_descriptor\n        });\n        let white_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"white\"),\n            view_formats: &[],\n            ..texture_descriptor\n        });\n\n        let red_texture_view = red_texture.create_view(&wgpu::TextureViewDescriptor::default());\n        let green_texture_view = green_texture.create_view(&wgpu::TextureViewDescriptor::default());\n        let blue_texture_view = blue_texture.create_view(&wgpu::TextureViewDescriptor::default());\n        let white_texture_view = white_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n        queue.write_texture(\n            red_texture.as_image_copy(),\n            &red_texture_data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: None,\n            },\n            wgpu::Extent3d::default(),\n        );\n        queue.write_texture(\n            green_texture.as_image_copy(),\n            &green_texture_data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: None,\n            },\n            wgpu::Extent3d::default(),\n        );\n        queue.write_texture(\n            blue_texture.as_image_copy(),\n            &blue_texture_data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: None,\n            },\n            wgpu::Extent3d::default(),\n        );\n        queue.write_texture(\n            white_texture.as_image_copy(),\n            &white_texture_data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: None,\n            },\n            wgpu::Extent3d::default(),\n        );\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind group layout\"),\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Texture {\n                        sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: NonZeroU32::new(2),\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Texture {\n                        sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: NonZeroU32::new(2),\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 2,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                    count: NonZeroU32::new(2),\n                },\n            ],\n        });\n\n        let uniform_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: Some(\"uniform bind group layout\"),\n                entries: &[wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: true,\n                        min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n                    },\n                    count: None,\n                }],\n            });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureViewArray(&[\n                        &red_texture_view,\n                        &green_texture_view,\n                    ]),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureViewArray(&[\n                        &blue_texture_view,\n                        &white_texture_view,\n                    ]),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::SamplerArray(&[&sampler, &sampler]),\n                },\n            ],\n            layout: &bind_group_layout,\n            label: Some(\"bind group\"),\n        });\n\n        let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &texture_index_buffer,\n                    offset: 0,\n                    size: Some(NonZeroU64::new(4).unwrap()),\n                }),\n            }],\n            layout: &uniform_bind_group_layout,\n            label: Some(\"uniform bind group\"),\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"main\"),\n            bind_group_layouts: &[Some(&bind_group_layout), Some(&uniform_bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        let index_format = wgpu::IndexFormat::Uint16;\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &base_shader_module,\n                entry_point: Some(\"vert_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: vertex_size as wgpu::BufferAddress,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Sint32],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: fragment_shader_module,\n                entry_point: Some(fragment_entry_point),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                front_face: wgpu::FrontFace::Ccw,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None\n        });\n\n        Self {\n            pipeline,\n            bind_group,\n            uniform_bind_group,\n            vertex_buffer,\n            index_buffer,\n            index_format,\n            uniform_workaround,\n        }\n    }\n    fn resize(\n        &mut self,\n        _sc_desc: &wgpu::SurfaceConfiguration,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n    ) {\n        // noop\n    }\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        // noop\n    }\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"primary\"),\n        });\n\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));\n        rpass.set_index_buffer(self.index_buffer.slice(..), self.index_format);\n        if self.uniform_workaround {\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.set_bind_group(1, &self.uniform_bind_group, &[0]);\n            rpass.draw_indexed(0..6, 0, 0..1);\n            rpass.set_bind_group(1, &self.uniform_bind_group, &[256]);\n            rpass.draw_indexed(6..12, 0, 0..1);\n        } else {\n            rpass.set_bind_group(0, &self.bind_group, &[]);\n            rpass.set_bind_group(1, &self.uniform_bind_group, &[0]);\n            rpass.draw_indexed(0..12, 0, 0..1);\n        }\n\n        drop(rpass);\n\n        queue.submit(Some(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"texture-arrays\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"texture-arrays\",\n    image_path: \"/examples/features/src/texture_arrays/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::empty(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n        // https://github.com/gfx-rs/wgpu/issues/9184\n        .expect_fail(\n            wgpu_test::FailureCase::molten_vk()\n                .validation_error(\"Shader library compile failed\")\n                .validation_error(\"could not be compiled into pipeline\"),\n        ),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.0001)],\n    _phantom: std::marker::PhantomData::<Example>,\n};\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_UNIFORM: crate::framework::ExampleTestParams =\n    crate::framework::ExampleTestParams {\n        name: \"texture-arrays-uniform\",\n        image_path: \"/examples/features/src/texture_arrays/screenshot.png\",\n        width: 1024,\n        height: 768,\n        optional_features: wgpu::Features::empty(),\n        base_test_parameters: wgpu_test::TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n        comparisons: &[wgpu_test::ComparisonType::Mean(0.0001)],\n        _phantom: std::marker::PhantomData::<Example>,\n    };\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST_NON_UNIFORM: crate::framework::ExampleTestParams =\n    crate::framework::ExampleTestParams {\n        name: \"texture-arrays-non-uniform\",\n        image_path: \"/examples/features/src/texture_arrays/screenshot.png\",\n        width: 1024,\n        height: 768,\n        optional_features:\n            wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n        base_test_parameters: wgpu_test::TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n        comparisons: &[wgpu_test::ComparisonType::Mean(0.0001)],\n        _phantom: std::marker::PhantomData::<Example>,\n    };\n"
  },
  {
    "path": "examples/features/src/texture_arrays/non_uniform_indexing.wgsl",
    "content": "struct FragmentInput {\n    @location(0) tex_coord: vec2<f32>,\n    @location(1) index: i32,\n}\n\n@group(0) @binding(0)\nvar texture_array_top: binding_array<texture_2d<f32>>;\n@group(0) @binding(1)\nvar texture_array_bottom: binding_array<texture_2d<f32>>;\n@group(0) @binding(2)\nvar sampler_array: binding_array<sampler>;\n\n@fragment\nfn non_uniform_main(fragment: FragmentInput) -> @location(0) vec4<f32> {\n    var outval: vec3<f32>;\n    if fragment.tex_coord.y <= 0.5 {\n        outval = textureSampleLevel(\n            texture_array_top[fragment.index],\n            sampler_array[fragment.index],\n            fragment.tex_coord,\n            0.0\n        ).rgb;\n    } else {\n        outval = textureSampleLevel(\n            texture_array_bottom[fragment.index],\n            sampler_array[fragment.index],\n            fragment.tex_coord,\n            0.0\n        ).rgb;\n    }\n\n    return vec4<f32>(outval.x, outval.y, outval.z, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/timestamp_queries/README.md",
    "content": "# timestamp_queries\n\nThis example shows various ways of querying time when supported.\n\n## To Run\n\n```\ncargo run --bin wgpu-examples timestamp_queries\n```\n"
  },
  {
    "path": "examples/features/src/timestamp_queries/mod.rs",
    "content": "//! Sample demonstrating different kinds of gpu timestamp queries.\n//!\n//! Timestamp queries are typically used to profile how long certain operations take on the GPU.\n//! wgpu has several ways of performing gpu timestamp queries:\n//! * passing `wgpu::RenderPassTimestampWrites`/`wgpu::ComputePassTimestampWrites` during render/compute pass creation.\n//!   This writes timestamps for the beginning and end of a given pass.\n//!   (enabled with wgpu::Features::TIMESTAMP_QUERY)\n//! * `wgpu::CommandEncoder::write_timestamp` writes a timestamp between any commands recorded on an encoder.\n//!   (enabled with wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)\n//! * `wgpu::RenderPass/ComputePass::write_timestamp` writes a timestamp within commands of a render pass.\n//!   Note that some GPU architectures do not support this.\n//!   (native only, enabled with wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES)\n//!\n//! Any timestamp is written to a `wgpu::QuerySet` which needs to be resolved to a buffer with `wgpu::BufferUsages::QUERY_RESOLVE`.\n//! Since this usage is incompatible with `wgpu::BufferUsages::MAP_READ` we need to copy the resolved timestamps to a separate buffer afterwards.\n//!\n//! The period, i.e. the unit of time, of the timestamps in wgpu is undetermined and needs to be queried with `wgpu::Queue::get_timestamp_period`\n//! in order to get comparable results.\n\nuse wgpu::util::DeviceExt;\n\nstruct Queries {\n    set: wgpu::QuerySet,\n    resolve_buffer: wgpu::Buffer,\n    destination_buffer: wgpu::Buffer,\n    num_queries: u64,\n    next_unused_query: u32,\n}\n\nstruct QueryResults {\n    encoder_timestamps: [u64; 2],\n    render_start_end_timestamps: [u64; 2],\n    render_inside_timestamp: Option<u64>,\n    compute_start_end_timestamps: [u64; 2],\n    compute_inside_timestamp: Option<u64>,\n}\n\nimpl QueryResults {\n    // Queries:\n    // * encoder timestamp start\n    // * encoder timestamp end\n    // * render start\n    // * render in-between (optional)\n    // * render end\n    // * compute start\n    // * compute in-between (optional)\n    // * compute end\n    const NUM_QUERIES: u64 = 8;\n\n    #[expect(\n        clippy::redundant_closure,\n        reason = \"false positive for `get_next_slot`, which needs to be used by reference\"\n    )]\n    fn from_raw_results(timestamps: Vec<u64>, timestamps_inside_passes: bool) -> Self {\n        assert_eq!(timestamps.len(), Self::NUM_QUERIES as usize);\n\n        let mut next_slot = 0;\n        let mut get_next_slot = || {\n            let slot = timestamps[next_slot];\n            next_slot += 1;\n            slot\n        };\n\n        let mut encoder_timestamps = [0, 0];\n        encoder_timestamps[0] = get_next_slot();\n        let render_start_end_timestamps = [get_next_slot(), get_next_slot()];\n        let render_inside_timestamp = timestamps_inside_passes.then(|| get_next_slot());\n        let compute_start_end_timestamps = [get_next_slot(), get_next_slot()];\n        let compute_inside_timestamp = timestamps_inside_passes.then(|| get_next_slot());\n        encoder_timestamps[1] = get_next_slot();\n\n        QueryResults {\n            encoder_timestamps,\n            render_start_end_timestamps,\n            render_inside_timestamp,\n            compute_start_end_timestamps,\n            compute_inside_timestamp,\n        }\n    }\n\n    fn print(&self, queue: &wgpu::Queue) {\n        let period = queue.get_timestamp_period();\n        let elapsed_us = |start, end: u64| end.wrapping_sub(start) as f64 * period as f64 / 1000.0;\n\n        println!(\n            \"Elapsed time before render until after compute: {:.2} μs\",\n            elapsed_us(self.encoder_timestamps[0], self.encoder_timestamps[1]),\n        );\n        println!(\n            \"Elapsed time render pass: {:.2} μs\",\n            elapsed_us(\n                self.render_start_end_timestamps[0],\n                self.render_start_end_timestamps[1]\n            )\n        );\n        if let Some(timestamp) = self.render_inside_timestamp {\n            println!(\n                \"Elapsed time first triangle: {:.2} μs\",\n                elapsed_us(self.render_start_end_timestamps[0], timestamp)\n            );\n        }\n        println!(\n            \"Elapsed time compute pass: {:.2} μs\",\n            elapsed_us(\n                self.compute_start_end_timestamps[0],\n                self.compute_start_end_timestamps[1]\n            )\n        );\n        if let Some(timestamp) = self.compute_inside_timestamp {\n            println!(\n                \"Elapsed time after first dispatch: {:.2} μs\",\n                elapsed_us(self.compute_start_end_timestamps[0], timestamp)\n            );\n        }\n    }\n}\n\nimpl Queries {\n    fn new(device: &wgpu::Device, num_queries: u64) -> Self {\n        Queries {\n            set: device.create_query_set(&wgpu::QuerySetDescriptor {\n                label: Some(\"Timestamp query set\"),\n                count: num_queries as _,\n                ty: wgpu::QueryType::Timestamp,\n            }),\n            resolve_buffer: device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(\"query resolve buffer\"),\n                size: size_of::<u64>() as u64 * num_queries,\n                usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::QUERY_RESOLVE,\n                mapped_at_creation: false,\n            }),\n            destination_buffer: device.create_buffer(&wgpu::BufferDescriptor {\n                label: Some(\"query dest buffer\"),\n                size: size_of::<u64>() as u64 * num_queries,\n                usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n                mapped_at_creation: false,\n            }),\n            num_queries,\n            next_unused_query: 0,\n        }\n    }\n\n    fn resolve(&self, encoder: &mut wgpu::CommandEncoder) {\n        encoder.resolve_query_set(\n            &self.set,\n            // TODO(https://github.com/gfx-rs/wgpu/issues/3993): Musn't be larger than the number valid queries in the set.\n            0..self.next_unused_query,\n            &self.resolve_buffer,\n            0,\n        );\n        encoder.copy_buffer_to_buffer(\n            &self.resolve_buffer,\n            0,\n            &self.destination_buffer,\n            0,\n            self.resolve_buffer.size(),\n        );\n    }\n\n    fn wait_for_results(&self, device: &wgpu::Device, is_test_on_metal: bool) -> Vec<u64> {\n        self.destination_buffer\n            .slice(..)\n            .map_async(wgpu::MapMode::Read, |_| ());\n        let poll_type = if is_test_on_metal {\n            // Use a short timeout because the `timestamps_encoder` test (which\n            // is also marked as flaky) has been observed to hang on Metal.\n            //\n            // Note that a timeout here is *not* considered an error. In this\n            // particular case that is what we want, but in general, waits in\n            // tests should probably treat a timeout as an error.\n            wgpu::PollType::Wait {\n                submission_index: None,\n                timeout: Some(std::time::Duration::from_secs(5)),\n            }\n        } else {\n            wgpu::PollType::wait_indefinitely()\n        };\n        device.poll(poll_type).unwrap();\n\n        let timestamps = {\n            let timestamp_view = self\n                .destination_buffer\n                .slice(..(size_of::<u64>() as wgpu::BufferAddress * self.num_queries))\n                .get_mapped_range();\n            bytemuck::cast_slice(&timestamp_view).to_vec()\n        };\n\n        self.destination_buffer.unmap();\n\n        timestamps\n    }\n}\n\nasync fn run() {\n    // Instantiates instance of wgpu\n    let instance =\n        wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());\n\n    // `request_adapter` instantiates the general connection to the GPU\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions::default())\n        .await\n        .expect(\"Failed to request adapter.\");\n\n    // Check timestamp features.\n    let features = adapter.features()\n        & (wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES);\n    if features.contains(wgpu::Features::TIMESTAMP_QUERY) {\n        println!(\"Adapter supports timestamp queries.\");\n    } else {\n        println!(\"Adapter does not support timestamp queries, aborting.\");\n        return;\n    }\n    let timestamps_inside_passes = features.contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES);\n    if timestamps_inside_passes {\n        println!(\"Adapter supports timestamp queries within passes.\");\n    } else {\n        println!(\"Adapter does not support timestamp queries within passes.\");\n    }\n\n    // `request_device` instantiates the feature specific connection to the GPU, defining some parameters,\n    //  `features` being the available features.\n    let (device, queue) = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: features,\n            required_limits: wgpu::Limits::downlevel_defaults(),\n            experimental_features: wgpu::ExperimentalFeatures::disabled(),\n            memory_hints: wgpu::MemoryHints::MemoryUsage,\n            trace: wgpu::Trace::Off,\n        })\n        .await\n        .unwrap();\n\n    let queries = submit_render_and_compute_pass_with_queries(&device, &queue);\n    let raw_results = queries.wait_for_results(&device, false);\n    println!(\"Raw timestamp buffer contents: {raw_results:?}\");\n    QueryResults::from_raw_results(raw_results, timestamps_inside_passes).print(&queue);\n}\n\nfn submit_render_and_compute_pass_with_queries(\n    device: &wgpu::Device,\n    queue: &wgpu::Queue,\n) -> Queries {\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    let mut queries = Queries::new(device, QueryResults::NUM_QUERIES);\n    let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    if device\n        .features()\n        .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)\n    {\n        encoder.write_timestamp(&queries.set, queries.next_unused_query);\n        queries.next_unused_query += 1;\n    }\n\n    // Render two triangles and profile it.\n    render_pass(\n        device,\n        &shader,\n        &mut encoder,\n        &queries.set,\n        &mut queries.next_unused_query,\n    );\n\n    // Compute a hash function on a single thread a bunch of time and profile it.\n    compute_pass(\n        device,\n        &shader,\n        &mut encoder,\n        &queries.set,\n        &mut queries.next_unused_query,\n    );\n\n    if device\n        .features()\n        .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)\n    {\n        encoder.write_timestamp(&queries.set, queries.next_unused_query);\n        queries.next_unused_query += 1;\n    }\n\n    queries.resolve(&mut encoder);\n    queue.submit(Some(encoder.finish()));\n\n    queries\n}\n\nfn compute_pass(\n    device: &wgpu::Device,\n    module: &wgpu::ShaderModule,\n    encoder: &mut wgpu::CommandEncoder,\n    query_set: &wgpu::QuerySet,\n    next_unused_query: &mut u32,\n) {\n    let storage_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Storage Buffer\"),\n        contents: bytemuck::cast_slice(&[42]),\n        usage: wgpu::BufferUsages::STORAGE,\n    });\n    let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: None,\n        module,\n        entry_point: Some(\"main_cs\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n    let bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: storage_buffer.as_entire_binding(),\n        }],\n    });\n\n    let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: Some(wgpu::ComputePassTimestampWrites {\n            query_set,\n            beginning_of_pass_write_index: Some(*next_unused_query),\n            end_of_pass_write_index: Some(*next_unused_query + 1),\n        }),\n    });\n    *next_unused_query += 2;\n    cpass.set_pipeline(&compute_pipeline);\n    cpass.set_bind_group(0, &bind_group, &[]);\n    cpass.dispatch_workgroups(1, 1, 1);\n    if device\n        .features()\n        .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES)\n    {\n        cpass.write_timestamp(query_set, *next_unused_query);\n        *next_unused_query += 1;\n    }\n    cpass.dispatch_workgroups(1, 1, 1);\n}\n\nfn render_pass(\n    device: &wgpu::Device,\n    module: &wgpu::ShaderModule,\n    encoder: &mut wgpu::CommandEncoder,\n    query_set: &wgpu::QuerySet,\n    next_unused_query: &mut u32,\n) {\n    let format = wgpu::TextureFormat::Rgba8Unorm;\n\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[],\n        immediate_size: 0,\n    });\n\n    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        vertex: wgpu::VertexState {\n            module,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n            buffers: &[],\n        },\n        fragment: Some(wgpu::FragmentState {\n            module,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(format.into())],\n        }),\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        multiview_mask: None,\n        cache: None,\n    });\n    let render_target = device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"rendertarget\"),\n        size: wgpu::Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[format],\n    });\n    let render_target_view = render_target.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            view: &render_target_view,\n            depth_slice: None,\n            resolve_target: None,\n            ops: wgpu::Operations {\n                load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                store: wgpu::StoreOp::Store,\n            },\n        })],\n        depth_stencil_attachment: None,\n        timestamp_writes: Some(wgpu::RenderPassTimestampWrites {\n            query_set,\n            beginning_of_pass_write_index: Some(*next_unused_query),\n            end_of_pass_write_index: Some(*next_unused_query + 1),\n        }),\n        occlusion_query_set: None,\n        multiview_mask: None,\n    });\n    *next_unused_query += 2;\n\n    rpass.set_pipeline(&render_pipeline);\n\n    rpass.draw(0..3, 0..1);\n    if device\n        .features()\n        .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES)\n    {\n        rpass.write_timestamp(query_set, *next_unused_query);\n        *next_unused_query += 1;\n    }\n\n    rpass.draw(0..3, 0..1);\n}\n\npub fn main() {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        env_logger::init();\n        pollster::block_on(run());\n    }\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n        console_log::init().expect(\"could not initialize logger\");\n        wasm_bindgen_futures::spawn_local(run());\n    }\n}\n\n#[cfg(test)]\npub mod tests {\n    use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration};\n\n    use super::{submit_render_and_compute_pass_with_queries, QueryResults};\n\n    #[gpu_test]\n    pub static TIMESTAMPS_PASS_BOUNDARIES: GpuTestConfiguration = GpuTestConfiguration::new()\n        .parameters(\n            wgpu_test::TestParameters::default()\n                .limits(wgpu::Limits::downlevel_defaults())\n                .features(wgpu::Features::TIMESTAMP_QUERY),\n        )\n        .run_sync(|ctx| test_timestamps(ctx, false, false));\n\n    #[gpu_test]\n    pub static TIMESTAMPS_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new()\n        .parameters(\n            wgpu_test::TestParameters::default()\n                .limits(wgpu::Limits::downlevel_defaults())\n                .features(\n                    wgpu::Features::TIMESTAMP_QUERY\n                        | wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS,\n                )\n                // see https://github.com/gfx-rs/wgpu/issues/2521\n                // If marking this test non-flaky, also consider removing the silent\n                // timeout in `wait_for_results`.\n                .expect_fail(FailureCase::always().panic(\"unexpected timestamp\").flaky()),\n        )\n        .run_sync(|ctx| test_timestamps(ctx, true, false));\n\n    #[gpu_test]\n    pub static TIMESTAMPS_PASSES: GpuTestConfiguration = GpuTestConfiguration::new()\n        .parameters(\n            wgpu_test::TestParameters::default()\n                .limits(wgpu::Limits::downlevel_defaults())\n                .features(\n                    wgpu::Features::TIMESTAMP_QUERY\n                        | wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS\n                        | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,\n                )\n                // see https://github.com/gfx-rs/wgpu/issues/2521\n                // If marking this test non-flaky, also consider removing the silent\n                // timeout in `wait_for_results`.\n                .expect_fail(FailureCase::always().panic(\"unexpected timestamp\").flaky()),\n        )\n        .run_sync(|ctx| test_timestamps(ctx, true, true));\n\n    fn test_timestamps(\n        ctx: wgpu_test::TestingContext,\n        timestamps_on_encoder: bool,\n        timestamps_inside_passes: bool,\n    ) {\n        let is_metal = ctx.adapter.get_info().backend == wgpu::Backend::Metal;\n        let queries = submit_render_and_compute_pass_with_queries(&ctx.device, &ctx.queue);\n        let raw_results = queries.wait_for_results(&ctx.device, is_metal);\n        let QueryResults {\n            encoder_timestamps,\n            render_start_end_timestamps,\n            render_inside_timestamp,\n            compute_start_end_timestamps,\n            compute_inside_timestamp,\n        } = QueryResults::from_raw_results(raw_results, timestamps_inside_passes);\n\n        // Timestamps may wrap around, so can't really only reason about deltas!\n        // Making things worse, deltas are allowed to be zero.\n        let render_delta =\n            render_start_end_timestamps[1].wrapping_sub(render_start_end_timestamps[0]);\n        let compute_delta =\n            compute_start_end_timestamps[1].wrapping_sub(compute_start_end_timestamps[0]);\n        let encoder_delta = encoder_timestamps[1].wrapping_sub(encoder_timestamps[0]);\n\n        if timestamps_on_encoder {\n            assert!(encoder_delta > 0, \"unexpected timestamp\");\n            assert!(\n                encoder_delta >= render_delta + compute_delta,\n                \"unexpected timestamp\"\n            );\n        }\n        if let Some(render_inside_timestamp) = render_inside_timestamp {\n            assert!(\n                render_inside_timestamp >= render_start_end_timestamps[0],\n                \"unexpected timestamp\"\n            );\n            assert!(\n                render_inside_timestamp <= render_start_end_timestamps[1],\n                \"unexpected timestamp\"\n            );\n        }\n        if let Some(compute_inside_timestamp) = compute_inside_timestamp {\n            assert!(\n                compute_inside_timestamp >= compute_start_end_timestamps[0],\n                \"unexpected timestamp\"\n            );\n            assert!(\n                compute_inside_timestamp <= compute_start_end_timestamps[1],\n                \"unexpected timestamp\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "examples/features/src/timestamp_queries/shader.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {\n    let x = f32(i32(in_vertex_index) - 1);\n    let y = f32(i32(in_vertex_index & 1u) * 2 - 1);\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n\n\n@group(0)\n@binding(0)\nvar<storage, read_write> buffer: array<u32>; // Used as both input and output for convenience.\n\nfn pcg_hash(input: u32) -> u32 {\n    let state = input * 747796405u + 2891336453u;\n    let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\n    return (word >> 22u) ^ word;\n}\n\n@compute\n@workgroup_size(1)\nfn main_cs(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    var value = buffer[0];\n\n    for (var i = 0u; i < 128u; i += 1u) {\n        value = pcg_hash(value);\n    }\n\n    buffer[0] = value;\n}\n"
  },
  {
    "path": "examples/features/src/uniform_values/README.md",
    "content": "# uniform_values\n\nCreates a window which displays a grayscale render of the [Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set). Pressing the arrow keys will translate the set and scrolling the mouse wheel will zoom in and out. If the image appears too 'bright', it may be because you are using too few iterations or 'samples'. Use U and D to increase or decrease respectively the max number of iterations used. Make sure to play around with this too to get an optimally photogenic screen cap. The window can be resized and pressing ESC will close the window. Explore the Mandelbrot set using the power of uniform variables to transfer state from the main program to the shader!\n\n## To Run\n\n```\ncargo run --bin wgpu-examples uniform_values\n```\n\n## Usage of Uniform Buffers / Variables\n\nSince the codebase of this example is so large (because why not demonstrate with a sort-of game) and the points of interest in terms of the actual point of the example so small, there is a module doc comment at the top of main.rs that points out the important points of the usage of uniform values.\n\n## Limitations\nAt some point in exploring the fractal, you may discover there is actually a resolution; if you zoom to deep, things become weirdly pixilated. Unfortunately, the relatively basic shader is currently limited by the faults of 32-bit floating point precision. As much as I'd like to upgrade to 64-bit floats, the support in WGSL for f64's is limited and you can't even cast to one as of time of writing. Still pretty cool though.\n\n## Screenshots\n\n![On load](screenshot1.png)\n![Zoomed in](screenshot2.png)\n![A different part zoomed in](screenshot3.png)"
  },
  {
    "path": "examples/features/src/uniform_values/mod.rs",
    "content": "//! Points of interest for seeing uniforms in action:\n//!\n//! 1. the struct for the data stored in the uniform buffer is defined.\n//! 2. the uniform buffer itself is created.\n//! 3. the bind group that will bind the uniform buffer and it's layout are created.\n//! 4. the bind group layout is attached to the pipeline layout.\n//! 5. the uniform buffer and the bind group are stored alongside the pipeline.\n//! 6. an instance of `AppState` is created. This variable will be modified\n//!    to change parameters in the shader and modified by app events to preform and save\n//!    those changes.\n//! 7. (7a and 7b) the `state` variable created at (6) is modified by commands such\n//!    as pressing the arrow keys or zooming in or out.\n//! 8. the contents of the `AppState` are loaded into the uniform buffer in preparation.\n//! 9. the bind group with the uniform buffer is attached to the render pass.\n//!\n//! The usage of the uniform buffer within the shader itself is pretty self-explanatory given\n//! some understanding of WGSL.\n\nuse std::{future::Future, sync::Arc};\n// We won't bring StorageBuffer into scope as that might be too easy to confuse\n// with actual GPU-allocated wgpu storage buffers.\nuse encase::ShaderType;\nuse wgpu::CurrentSurfaceTexture;\nuse winit::{\n    application::ApplicationHandler,\n    event::{KeyEvent, WindowEvent},\n    event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},\n    keyboard::{Key, NamedKey},\n    window::Window,\n};\n\nconst ZOOM_INCREMENT_FACTOR: f32 = 1.1;\nconst CAMERA_POS_INCREMENT_FACTOR: f32 = 0.1;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    pollster::block_on(f);\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn spawn(f: impl Future<Output = ()> + 'static) {\n    wasm_bindgen_futures::spawn_local(f);\n}\n\n// (1)\n#[derive(Debug, ShaderType)]\nstruct ShaderState {\n    pub cursor_pos: glam::Vec2,\n    pub zoom: f32,\n    pub max_iterations: u32,\n}\n\nimpl ShaderState {\n    // Translating Rust structures to WGSL is always tricky and can prove\n    // incredibly difficult to remember all the rules by which WGSL\n    // lays out and formats structs in memory. It is also often extremely\n    // frustrating to debug when things don't go right.\n    //\n    // You may sometimes see structs translated to bytes through\n    // using `#[repr(C)]` on the struct so that the struct has a defined,\n    // guaranteed internal layout and then implementing bytemuck's POD\n    // trait so that one can preform a bitwise cast. There are issues with\n    // this approach though as C's struct layouts aren't always compatible\n    // with WGSL, such as when special WGSL types like vec's and mat's\n    // get involved that have special alignment rules and especially\n    // when the target buffer is going to be used in the uniform memory\n    // space.\n    //\n    // Here though, we use the encase crate which makes translating potentially\n    // complex Rust structs easy through combined use of the [`ShaderType`] trait\n    // / derive macro and the buffer structs which hold data formatted for WGSL\n    // in either the storage or uniform spaces.\n    fn as_wgsl_bytes(&self) -> encase::internal::Result<Vec<u8>> {\n        let mut buffer = encase::UniformBuffer::new(Vec::new());\n        buffer.write(self)?;\n        Ok(buffer.into_inner())\n    }\n\n    fn translate_view(&mut self, increments: i32, axis: usize) {\n        self.cursor_pos[axis] += CAMERA_POS_INCREMENT_FACTOR * increments as f32 / self.zoom;\n    }\n\n    fn zoom(&mut self, amount: f32) {\n        self.zoom += ZOOM_INCREMENT_FACTOR * amount * self.zoom.powf(1.02);\n        self.zoom = self.zoom.max(1.1);\n    }\n}\n\nimpl Default for ShaderState {\n    fn default() -> Self {\n        ShaderState {\n            cursor_pos: glam::Vec2::ZERO,\n            zoom: 1.0,\n            max_iterations: 50,\n        }\n    }\n}\n\nstruct WgpuContext {\n    pub instance: wgpu::Instance,\n    pub window: Arc<Window>,\n    pub surface: wgpu::Surface<'static>,\n    pub surface_config: wgpu::SurfaceConfiguration,\n    pub device: wgpu::Device,\n    pub queue: wgpu::Queue,\n    pub pipeline: wgpu::RenderPipeline,\n    pub bind_group: wgpu::BindGroup,\n    pub uniform_buffer: wgpu::Buffer,\n}\n\nimpl WgpuContext {\n    async fn new(\n        window: Arc<Window>,\n        display_handle: winit::event_loop::OwnedDisplayHandle,\n    ) -> WgpuContext {\n        let size = window.inner_size();\n\n        let instance = wgpu::Instance::new(\n            wgpu::InstanceDescriptor::new_with_display_handle_from_env(Box::new(display_handle)),\n        );\n        let surface = instance.create_surface(window.clone()).unwrap();\n        let adapter = instance\n            .request_adapter(&wgpu::RequestAdapterOptions {\n                power_preference: wgpu::PowerPreference::HighPerformance,\n                compatible_surface: Some(&surface),\n                force_fallback_adapter: false,\n            })\n            .await\n            .unwrap();\n        let (device, queue) = adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                label: None,\n                required_features: wgpu::Features::empty(),\n                required_limits: wgpu::Limits::downlevel_defaults(),\n                experimental_features: wgpu::ExperimentalFeatures::disabled(),\n                memory_hints: wgpu::MemoryHints::MemoryUsage,\n                trace: wgpu::Trace::Off,\n            })\n            .await\n            .unwrap();\n\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        // (2)\n        let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: size_of::<ShaderState>() as u64,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        // (3)\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &uniform_buffer,\n                    offset: 0,\n                    size: None,\n                }),\n            }],\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            // (4)\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        let swapchain_capabilities = surface.get_capabilities(&adapter);\n        let swapchain_format = swapchain_capabilities.formats[0];\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(swapchain_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n        let surface_config = surface\n            .get_default_config(&adapter, size.width, size.height)\n            .unwrap();\n        surface.configure(&device, &surface_config);\n\n        // (5)\n        WgpuContext {\n            instance,\n            window,\n            surface,\n            surface_config,\n            device,\n            queue,\n            pipeline,\n            bind_group,\n            uniform_buffer,\n        }\n    }\n\n    fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {\n        self.surface_config.width = new_size.width;\n        self.surface_config.height = new_size.height;\n        self.surface.configure(&self.device, &self.surface_config);\n    }\n}\n\nenum UniformAction {\n    Initialized(WgpuContext),\n}\n\n#[expect(clippy::large_enum_variant)]\nenum RunState {\n    Uninitialized,\n    Loading,\n    Running {\n        wgpu_ctx: WgpuContext,\n        // (6)\n        shader_state: ShaderState,\n    },\n}\n\nstruct App {\n    proxy: EventLoopProxy<UniformAction>,\n    window: Option<Arc<Window>>,\n    state: RunState,\n}\n\nimpl App {\n    fn new(event_loop: &EventLoop<UniformAction>) -> Self {\n        Self {\n            proxy: event_loop.create_proxy(),\n            window: None,\n            state: RunState::Uninitialized,\n        }\n    }\n}\n\nimpl ApplicationHandler<UniformAction> for App {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        if !matches!(self.state, RunState::Uninitialized) {\n            return;\n        }\n        self.state = RunState::Loading;\n\n        #[cfg_attr(\n            not(target_arch = \"wasm32\"),\n            expect(unused_mut, reason = \"wasm32 re-assigns to specify canvas\")\n        )]\n        let mut attributes = Window::default_attributes()\n            .with_title(\"Remember: Use U/D to change sample count!\")\n            .with_inner_size(winit::dpi::LogicalSize::new(900, 900));\n\n        #[cfg(target_arch = \"wasm32\")]\n        {\n            use wasm_bindgen::JsCast;\n            use winit::platform::web::WindowAttributesExtWebSys;\n            let canvas = web_sys::window()\n                .unwrap()\n                .document()\n                .unwrap()\n                .get_element_by_id(\"canvas\")\n                .unwrap()\n                .dyn_into::<web_sys::HtmlCanvasElement>()\n                .unwrap();\n            attributes = attributes.with_canvas(Some(canvas));\n        }\n\n        let window = Arc::new(\n            event_loop\n                .create_window(attributes)\n                .expect(\"Failed to create window\"),\n        );\n        self.window = Some(window.clone());\n\n        let display_handle = event_loop.owned_display_handle();\n        let proxy = self.proxy.clone();\n\n        spawn(async move {\n            let wgpu_ctx = WgpuContext::new(window, display_handle).await;\n            let _ = proxy.send_event(UniformAction::Initialized(wgpu_ctx));\n        });\n    }\n\n    fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UniformAction) {\n        match event {\n            UniformAction::Initialized(wgpu_ctx) => {\n                self.state = RunState::Running {\n                    wgpu_ctx,\n                    shader_state: ShaderState::default(),\n                };\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n        }\n    }\n\n    fn exiting(&mut self, _event_loop: &ActiveEventLoop) {\n        self.state = RunState::Uninitialized;\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        _window_id: winit::window::WindowId,\n        event: WindowEvent,\n    ) {\n        let RunState::Running {\n            wgpu_ctx,\n            shader_state,\n        } = &mut self.state\n        else {\n            return;\n        };\n\n        match event {\n            WindowEvent::CloseRequested => {\n                event_loop.exit();\n            }\n            WindowEvent::KeyboardInput {\n                event: KeyEvent {\n                    logical_key, text, ..\n                },\n                ..\n            } => {\n                if let Key::Named(key) = logical_key {\n                    match key {\n                        NamedKey::Escape => event_loop.exit(),\n                        NamedKey::ArrowUp => shader_state.translate_view(1, 1),\n                        NamedKey::ArrowDown => shader_state.translate_view(-1, 1),\n                        NamedKey::ArrowLeft => shader_state.translate_view(-1, 0),\n                        NamedKey::ArrowRight => shader_state.translate_view(1, 0),\n                        _ => {}\n                    }\n                }\n\n                if let Some(text) = text {\n                    if text == \"u\" {\n                        shader_state.max_iterations += 3;\n                    } else if text == \"d\" {\n                        shader_state.max_iterations = shader_state.max_iterations.saturating_sub(3);\n                    }\n                };\n\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::MouseWheel { delta, .. } => {\n                let change = match delta {\n                    winit::event::MouseScrollDelta::LineDelta(_, vertical) => vertical,\n                    winit::event::MouseScrollDelta::PixelDelta(pos) => pos.y as f32 / 20.0,\n                };\n                // (7b)\n                shader_state.zoom(change);\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::Resized(new_size) => {\n                wgpu_ctx.resize(new_size);\n                if let Some(window) = &self.window {\n                    window.request_redraw();\n                }\n            }\n            WindowEvent::RedrawRequested => {\n                let frame = match wgpu_ctx.surface.get_current_texture() {\n                    CurrentSurfaceTexture::Success(frame) => frame,\n                    CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                    CurrentSurfaceTexture::Suboptimal(_) | CurrentSurfaceTexture::Outdated => {\n                        wgpu_ctx\n                            .surface\n                            .configure(&wgpu_ctx.device, &wgpu_ctx.surface_config);\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                    CurrentSurfaceTexture::Validation => {\n                        unreachable!(\"No error scope registered, so validation errors will panic\")\n                    }\n                    CurrentSurfaceTexture::Lost => {\n                        wgpu_ctx.surface = wgpu_ctx\n                            .instance\n                            .create_surface(wgpu_ctx.window.clone())\n                            .unwrap();\n                        wgpu_ctx\n                            .surface\n                            .configure(&wgpu_ctx.device, &wgpu_ctx.surface_config);\n                        if let Some(window) = &self.window {\n                            window.request_redraw();\n                        }\n                        return;\n                    }\n                };\n\n                let view = frame\n                    .texture\n                    .create_view(&wgpu::TextureViewDescriptor::default());\n\n                // (8)\n                wgpu_ctx.queue.write_buffer(\n                    &wgpu_ctx.uniform_buffer,\n                    0,\n                    &shader_state.as_wgsl_bytes().expect(\n                        \"Error in encase translating ShaderState \\\n                    struct to WGSL bytes.\",\n                    ),\n                );\n                let mut encoder = wgpu_ctx\n                    .device\n                    .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n                {\n                    let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                        label: None,\n                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                            view: &view,\n                            depth_slice: None,\n                            resolve_target: None,\n                            ops: wgpu::Operations {\n                                load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                                store: wgpu::StoreOp::Store,\n                            },\n                        })],\n                        depth_stencil_attachment: None,\n                        occlusion_query_set: None,\n                        timestamp_writes: None,\n                        multiview_mask: None,\n                    });\n                    render_pass.set_pipeline(&wgpu_ctx.pipeline);\n                    // (9)\n                    render_pass.set_bind_group(0, Some(&wgpu_ctx.bind_group), &[]);\n                    render_pass.draw(0..3, 0..1);\n                }\n                wgpu_ctx.queue.submit(Some(encoder.finish()));\n                if let Some(window) = &self.window {\n                    window.pre_present_notify();\n                }\n                frame.present();\n            }\n            WindowEvent::Occluded(is_occluded) => {\n                if !is_occluded {\n                    if let Some(window) = &self.window {\n                        window.request_redraw();\n                    }\n                }\n            }\n            _ => {}\n        }\n    }\n}\n\npub fn main() {\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n            console_log::init().expect(\"could not initialize logger\");\n        } else {\n            env_logger::builder().format_timestamp_nanos().init();\n        }\n    }\n\n    let event_loop = EventLoop::with_user_event().build().unwrap();\n\n    #[cfg_attr(target_arch = \"wasm32\", expect(unused_mut))]\n    let mut app = App::new(&event_loop);\n\n    cfg_if::cfg_if! {\n        if #[cfg(target_arch = \"wasm32\")] {\n            use winit::platform::web::EventLoopExtWebSys;\n\n            let document = web_sys::window()\n                .and_then(|win| win.document())\n                .expect(\"Failed to get document.\");\n            let body = document.body().unwrap();\n            let controls_text = document\n                .create_element(\"p\")\n                .expect(\"Failed to create controls text as element.\");\n            controls_text.set_inner_html(\n                \"Controls: <br/>\nUp, Down, Left, Right: Move view, <br/>\nScroll: Zoom, <br/>\nU, D: Increase / decrease sample count.\",\n            );\n            body.append_child(&controls_text)\n                .expect(\"Failed to append controls text to body.\");\n\n            event_loop.spawn_app(app);\n        } else {\n            event_loop.run_app(&mut app).unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/features/src/uniform_values/shader.wgsl",
    "content": "// Some credit to https://github.com/paulgb/wgsl-playground/tree/main.\n\n// We use separate the x and y instead of using a vec2 to avoid wgsl padding.\nstruct AppState {\n    pos_x: f32,\n    pos_y: f32,\n    zoom: f32,\n    max_iterations: u32,\n}\n\nstruct VertexInput {\n    @builtin(vertex_index) vertex_index: u32,\n};\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) coord: vec2<f32>,\n};\n\n@group(0)\n@binding(0)\nvar<uniform> app_state: AppState;\n\n@vertex\nfn vs_main(in: VertexInput) -> VertexOutput {\n    var vertices = array<vec2<f32>, 3>(\n        vec2<f32>(-1., 1.),\n        vec2<f32>(3.0, 1.),\n        vec2<f32>(-1., -3.0),\n    );\n    var out: VertexOutput;\n    out.coord = vertices[in.vertex_index];\n    out.position = vec4<f32>(out.coord, 0.0, 1.0);\n\n    return out;\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    let max_iterations = app_state.max_iterations;\n    var final_iteration = max_iterations;\n    let c = vec2(\n        // Translated to put everything nicely in frame.\n        (in.coord.x) * 3.0 / app_state.zoom + app_state.pos_x,\n        (in.coord.y) * 3.0 / app_state.zoom + app_state.pos_y\n    );\n    var current_z = c;\n    var next_z: vec2<f32>;\n    for (var i = 0u; i < max_iterations; i++) {\n        next_z.x = (current_z.x * current_z.x - current_z.y * current_z.y) + c.x;\n        next_z.y = (2.0 * current_z.x * current_z.y) + c.y;\n        current_z = next_z;\n        if length(current_z) > 4.0 {\n            final_iteration = i;\n            break;\n        }\n    }\n    let value = f32(final_iteration) / f32(max_iterations);\n\n    return vec4(value, value, value, 1.0);\n}"
  },
  {
    "path": "examples/features/src/utils.rs",
    "content": "#[cfg(not(target_arch = \"wasm32\"))]\nuse std::io::Write;\nuse std::time::Instant;\n#[cfg(target_arch = \"wasm32\")]\nuse wasm_bindgen::prelude::*;\n\n#[cfg(target_arch = \"wasm32\")]\nfn get_content_div() -> web_sys::Element {\n    web_sys::window()\n        .and_then(|window| window.document())\n        .and_then(|document| document.get_element_by_id(\"content\"))\n        .expect(\"Could not get document / content.\")\n}\n\n/// Replaces the site body with a message telling the user to open the console and use that.\n#[cfg(target_arch = \"wasm32\")]\npub fn add_web_nothing_to_see_msg() {\n    get_content_div().set_inner_html(\n        \"<h1>This is a compute example, so there's nothing to see here. Open the console!</h1>\",\n    );\n}\n\n/// Outputs a vector of RGBA bytes as a png image with the given dimensions on the given path.\n#[cfg(not(target_arch = \"wasm32\"))]\npub fn output_image_native(image_data: Vec<u8>, texture_dims: (usize, usize), path: String) {\n    let mut png_data = Vec::<u8>::with_capacity(image_data.len());\n    let mut encoder = png::Encoder::new(\n        std::io::Cursor::new(&mut png_data),\n        texture_dims.0 as u32,\n        texture_dims.1 as u32,\n    );\n    encoder.set_color(png::ColorType::Rgba);\n    let mut png_writer = encoder.write_header().unwrap();\n    png_writer.write_image_data(&image_data[..]).unwrap();\n    png_writer.finish().unwrap();\n    log::info!(\"PNG file encoded in memory.\");\n\n    let mut file = std::fs::File::create(&path).unwrap();\n    file.write_all(&png_data[..]).unwrap();\n    log::info!(\"PNG file written to disc as \\\"{path}\\\".\");\n}\n\n/// Effectively a version of `output_image_native` but meant for web browser contexts.\n///\n/// This is achieved via in `img` element on the page. If the target image element does\n/// not exist, this function creates one. If it does, the image data is overridden.\n///\n/// This function makes use of a hidden staging canvas which the data is copied to in\n/// order to create a data URL.\n#[cfg(target_arch = \"wasm32\")]\npub fn output_image_wasm(image_data: Vec<u8>, texture_dims: (usize, usize)) {\n    let document = web_sys::window().unwrap().document().unwrap();\n    let content_div = get_content_div();\n\n    let canvas = if let Some(found_canvas) = document.get_element_by_id(\"staging-canvas\") {\n        match found_canvas.dyn_into::<web_sys::HtmlCanvasElement>() {\n            Ok(canvas_as_canvas) => canvas_as_canvas,\n            Err(e) => {\n                log::error!(\n                    \"In searching for a staging canvas for outputting an image \\\n                    (element with id \\\"staging-canvas\\\"), found non-canvas element: {e:?}.\n                    Replacing with standard staging canvas.\"\n                );\n                e.remove();\n                create_staging_canvas(&document)\n            }\n        }\n    } else {\n        log::info!(\"Output image staging canvas element not found; creating.\");\n        create_staging_canvas(&document)\n    };\n    // Having the size attributes the right size is so important, we should always do it\n    // just to be safe. Also, what if we might want the image size to be able to change?\n    let image_dimension_strings = (texture_dims.0.to_string(), texture_dims.1.to_string());\n    canvas\n        .set_attribute(\"width\", image_dimension_strings.0.as_str())\n        .unwrap();\n    canvas\n        .set_attribute(\"height\", image_dimension_strings.1.as_str())\n        .unwrap();\n\n    let context = canvas\n        .get_context(\"2d\")\n        .unwrap()\n        .unwrap()\n        .dyn_into::<web_sys::CanvasRenderingContext2d>()\n        .unwrap();\n    let image_data = web_sys::ImageData::new_with_u8_clamped_array(\n        wasm_bindgen::Clamped(&image_data),\n        texture_dims.0 as u32,\n    )\n    .unwrap();\n    context.put_image_data(&image_data, 0.0, 0.0).unwrap();\n\n    // Get the img element that will act as our target for rendering from the canvas.\n    let image_element = if let Some(found_image_element) =\n        document.get_element_by_id(\"output-image-target\")\n    {\n        match found_image_element.dyn_into::<web_sys::HtmlImageElement>() {\n            Ok(e) => e,\n            Err(e) => {\n                log::error!(\n                    \"Found an element with the id \\\"output-image-target\\\" but it was not an image: {e:?}.\n                    Replacing with default image output element.\",\n                );\n                e.remove();\n                create_output_image_element(&document)\n            }\n        }\n    } else {\n        log::info!(\"Output image element not found; creating.\");\n        create_output_image_element(&document)\n    };\n    // The canvas is currently the image we ultimately want. We can create a data url from it now.\n    let data_url = canvas.to_data_url().unwrap();\n    image_element.set_src(&data_url);\n    log::info!(\"Copied image from staging canvas to image element.\");\n\n    if document.get_element_by_id(\"image-for-you-text\").is_none() {\n        log::info!(\"\\\"Image for you\\\" text not found; creating.\");\n        let p = document\n            .create_element(\"p\")\n            .expect(\"Failed to create p element for \\\"image for you text\\\".\");\n        p.set_text_content(Some(\n            \"The above image is for you!\n        You can drag it to your desktop to download.\",\n        ));\n        p.set_id(\"image-for-you-text\");\n        content_div\n            .append_child(&p)\n            .expect(\"Failed to append \\\"image for you text\\\" to document.\");\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn create_staging_canvas(document: &web_sys::Document) -> web_sys::HtmlCanvasElement {\n    let content_div = get_content_div();\n    let new_canvas = document\n        .create_element(\"canvas\")\n        .expect(\"Failed to create staging canvas.\")\n        .dyn_into::<web_sys::HtmlCanvasElement>()\n        .unwrap();\n    // We don't want to show the canvas, we just want it to exist in the background.\n    new_canvas.set_attribute(\"hidden\", \"true\").unwrap();\n    new_canvas.set_attribute(\"background-color\", \"red\").unwrap();\n    content_div.append_child(&new_canvas).unwrap();\n    log::info!(\"Created new staging canvas: {:?}\", &new_canvas);\n    new_canvas\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn create_output_image_element(document: &web_sys::Document) -> web_sys::HtmlImageElement {\n    let content_div = get_content_div();\n    let new_image = document\n        .create_element(\"img\")\n        .expect(\"Failed to create output image element.\")\n        .dyn_into::<web_sys::HtmlImageElement>()\n        .unwrap();\n    new_image.set_id(\"output-image-target\");\n    content_div.replace_children_with_node_1(&new_image);\n    log::info!(\"Created new output target image: {:?}\", &new_image);\n    new_image\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\n/// If the environment variable `WGPU_ADAPTER_NAME` is set, this function will attempt to\n/// initialize the adapter with that name. If it is not set, it will attempt to initialize\n/// the adapter which supports the required features.\npub(crate) async fn get_adapter_with_capabilities_or_from_env(\n    instance: &wgpu::Instance,\n    required_features: &wgpu::Features,\n    required_downlevel_capabilities: &wgpu::DownlevelCapabilities,\n    surface: &Option<&wgpu::Surface<'_>>,\n) -> wgpu::Adapter {\n    use wgpu::Backends;\n    if std::env::var(\"WGPU_ADAPTER_NAME\").is_ok() {\n        let adapter = wgpu::util::initialize_adapter_from_env_or_default(instance, *surface)\n            .await\n            .expect(\"No suitable GPU adapters found on the system!\");\n\n        let adapter_info = adapter.get_info();\n        log::info!(\"Using {} ({:?})\", adapter_info.name, adapter_info.backend);\n\n        let adapter_features = adapter.features();\n        assert!(\n            adapter_features.contains(*required_features),\n            \"Adapter does not support required features for this example: {:?}\",\n            *required_features - adapter_features\n        );\n\n        let downlevel_capabilities = adapter.get_downlevel_capabilities();\n        assert!(\n            downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,\n            \"Adapter does not support the minimum shader model required to run this example: {:?}\",\n            required_downlevel_capabilities.shader_model\n        );\n        assert!(\n                downlevel_capabilities\n                    .flags\n                    .contains(required_downlevel_capabilities.flags),\n                \"Adapter does not support the downlevel capabilities required to run this example: {:?}\",\n                required_downlevel_capabilities.flags - downlevel_capabilities.flags\n            );\n        adapter\n    } else {\n        let adapters = instance.enumerate_adapters(Backends::all()).await;\n\n        let mut chosen_adapter = None;\n        for adapter in adapters {\n            if let Some(surface) = surface {\n                if !adapter.is_surface_supported(surface) {\n                    continue;\n                }\n            }\n\n            let required_features = *required_features;\n            let adapter_features = adapter.features();\n            if !adapter_features.contains(required_features) {\n                continue;\n            } else {\n                chosen_adapter = Some(adapter);\n                break;\n            }\n        }\n\n        chosen_adapter.expect(\"No suitable GPU adapters found on the system!\")\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\npub(crate) async fn get_adapter_with_capabilities_or_from_env(\n    instance: &wgpu::Instance,\n    required_features: &wgpu::Features,\n    required_downlevel_capabilities: &wgpu::DownlevelCapabilities,\n    surface: &Option<&wgpu::Surface<'_>>,\n) -> wgpu::Adapter {\n    let adapter = wgpu::util::initialize_adapter_from_env_or_default(instance, *surface)\n        .await\n        .expect(\"No suitable GPU adapters found on the system!\");\n\n    let adapter_info = adapter.get_info();\n    log::info!(\"Using {} ({:?})\", adapter_info.name, adapter_info.backend);\n\n    let adapter_features = adapter.features();\n    assert!(\n        adapter_features.contains(*required_features),\n        \"Adapter does not support required features for this example: {:?}\",\n        *required_features - adapter_features\n    );\n\n    let downlevel_capabilities = adapter.get_downlevel_capabilities();\n    assert!(\n        downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,\n        \"Adapter does not support the minimum shader model required to run this example: {:?}\",\n        required_downlevel_capabilities.shader_model\n    );\n    assert!(\n        downlevel_capabilities\n            .flags\n            .contains(required_downlevel_capabilities.flags),\n        \"Adapter does not support the downlevel capabilities required to run this example: {:?}\",\n        required_downlevel_capabilities.flags - downlevel_capabilities.flags\n    );\n    adapter\n}\n\n/// A custom timer that only starts counting after the first call to get its time value.\n/// Useful because some examples have animations that would otherwise get started at initialization\n/// leading to random CI fails.\n#[derive(Default)]\npub struct AnimationTimer {\n    start_time: Option<Instant>,\n}\n\nimpl AnimationTimer {\n    pub fn time(&mut self) -> f32 {\n        match self.start_time {\n            None => {\n                self.start_time = Some(Instant::now());\n                0.0\n            }\n            Some(ref instant) => instant.elapsed().as_secs_f32(),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/features/src/water/README.md",
    "content": "# Water example\n\nThis example renders animated water.\n\nIt demonstrates Read only Depth/Stencil (abbreviated RODS), where a depth/stencil buffer is used as an attachment which is read-only. In this case it's used in the shaders to calculate reflections and depth.\n\n## Files:\n\n```\nwater\n├── main.rs ------------------ Main program\n├── point_gen.rs ------------- Hexagon point generation\n├── README.md ---------------- This readme\n├── screenshot.png ----------- Screenshot\n├── terrain.wgsl ------------- WGSL Shader for terrain\n└── water.wgsl --------------- WGSL Shader for water\n```\n\n## To run\n\n```\ncargo run --bin wgpu-examples water\n```\n\n## Screenshot\n\n![Water example](./screenshot.png)\n"
  },
  {
    "path": "examples/features/src/water/mod.rs",
    "content": "mod point_gen;\n\nuse bytemuck::{Pod, Zeroable};\nuse glam::Vec3;\nuse nanorand::{Rng, WyRand};\nuse std::{f32::consts, iter};\nuse wgpu::util::DeviceExt;\n\n///\n/// Radius of the terrain.\n///\n/// Changing this value will change the size of the\n/// water and terrain. Note however, that changes to\n/// this value will require modification of the time\n/// scale in the `render` method below.\n///\nconst SIZE: f32 = 29.0;\n\n///\n/// Location of the camera.\n/// Location of light is in terrain/water shaders.\n///\nconst CAMERA: Vec3 = glam::Vec3::new(-200.0, 70.0, 200.0);\n\nstruct Matrices {\n    view: glam::Mat4,\n    flipped_view: glam::Mat4,\n    projection: glam::Mat4,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]\nstruct TerrainUniforms {\n    view_projection: [f32; 16],\n    clipping_plane: [f32; 4],\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]\nstruct WaterUniforms {\n    view: [f32; 16],\n    projection: [f32; 16],\n    time_size_width: [f32; 4],\n    height: [f32; 4],\n}\n\nstruct Uniforms {\n    terrain_normal: TerrainUniforms,\n    terrain_flipped: TerrainUniforms,\n    water: WaterUniforms,\n}\n\nstruct Example {\n    water_vertex_buf: wgpu::Buffer,\n    water_vertex_count: usize,\n    water_bind_group_layout: wgpu::BindGroupLayout,\n    water_bind_group: wgpu::BindGroup,\n    water_uniform_buf: wgpu::Buffer,\n    water_pipeline: wgpu::RenderPipeline,\n\n    terrain_vertex_buf: wgpu::Buffer,\n    terrain_vertex_count: usize,\n    terrain_normal_bind_group: wgpu::BindGroup,\n    terrain_normal_uniform_buf: wgpu::Buffer,\n    ///\n    /// Contains uniform variables where the camera\n    /// has been placed underwater.\n    ///\n    terrain_flipped_uniform_buf: wgpu::Buffer,\n    terrain_pipeline: wgpu::RenderPipeline,\n\n    /// A render bundle for drawing the terrain.\n    ///\n    /// This isn't really necessary, but it does make sure we have at\n    /// least one use of `RenderBundleEncoder::set_bind_group` among\n    /// the examples.\n    terrain_bundle: wgpu::RenderBundle,\n\n    reflect_view: wgpu::TextureView,\n\n    depth_buffer: wgpu::TextureView,\n\n    current_frame: usize,\n\n    ///\n    /// Used to prevent issues when rendering after\n    /// minimizing the window.\n    ///\n    active: Option<usize>,\n}\n\nimpl Example {\n    ///\n    /// Creates the view matrices, and the corrected projection matrix.\n    ///\n    fn generate_matrices(aspect_ratio: f32) -> Matrices {\n        let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 10.0, 400.0);\n        let reg_view = glam::Mat4::look_at_rh(\n            CAMERA,\n            glam::Vec3::new(0f32, 0.0, 0.0),\n            glam::Vec3::Y, //Note that y is up. Differs from other examples.\n        );\n\n        let scale = glam::Mat4::from_scale(glam::Vec3::new(8.0, 1.5, 8.0));\n\n        let reg_view = reg_view * scale;\n\n        let flipped_view = glam::Mat4::look_at_rh(\n            glam::Vec3::new(CAMERA.x, -CAMERA.y, CAMERA.z),\n            glam::Vec3::ZERO,\n            glam::Vec3::Y,\n        );\n\n        let flipped_view = flipped_view * scale;\n\n        Matrices {\n            view: reg_view,\n            flipped_view,\n            projection,\n        }\n    }\n\n    fn generate_uniforms(width: u32, height: u32) -> Uniforms {\n        let Matrices {\n            view,\n            flipped_view,\n            projection,\n        } = Self::generate_matrices(width as f32 / height as f32);\n\n        Uniforms {\n            terrain_normal: TerrainUniforms {\n                view_projection: *(projection * view).as_ref(),\n                clipping_plane: [0.0; 4],\n            },\n            terrain_flipped: TerrainUniforms {\n                view_projection: *(projection * flipped_view).as_ref(),\n                clipping_plane: [0., 1., 0., 0.],\n            },\n            water: WaterUniforms {\n                view: *view.as_ref(),\n                projection: *projection.as_ref(),\n                time_size_width: [0.0, 1.0, SIZE * 2.0, width as f32],\n                height: [height as f32, 0.0, 0.0, 0.0],\n            },\n        }\n    }\n\n    ///\n    /// Initializes Uniforms and textures.\n    ///\n    fn initialize_resources(\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        water_uniforms: &wgpu::Buffer,\n        terrain_normal_uniforms: &wgpu::Buffer,\n        terrain_flipped_uniforms: &wgpu::Buffer,\n        water_bind_group_layout: &wgpu::BindGroupLayout,\n    ) -> (wgpu::TextureView, wgpu::TextureView, wgpu::BindGroup) {\n        // Matrices for our projection and view.\n        // flipped_view is the view from under the water.\n        let Uniforms {\n            terrain_normal,\n            terrain_flipped,\n            water,\n        } = Self::generate_uniforms(config.width, config.height);\n\n        // Put the uniforms into buffers on the GPU\n        queue.write_buffer(\n            terrain_normal_uniforms,\n            0,\n            bytemuck::cast_slice(&[terrain_normal]),\n        );\n        queue.write_buffer(\n            terrain_flipped_uniforms,\n            0,\n            bytemuck::cast_slice(&[terrain_flipped]),\n        );\n        queue.write_buffer(water_uniforms, 0, bytemuck::cast_slice(&[water]));\n\n        let texture_extent = wgpu::Extent3d {\n            width: config.width,\n            height: config.height,\n            depth_or_array_layers: 1,\n        };\n\n        let reflection_texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"Reflection Render Texture\"),\n            size: texture_extent,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: config.view_formats[0],\n            usage: wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n\n        let draw_depth_buffer = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"Depth Buffer\"),\n            size: texture_extent,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Depth32Float,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n\n        let color_sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"Color Sampler\"),\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            ..Default::default()\n        });\n\n        let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            label: Some(\"Depth Sampler\"),\n            ..Default::default()\n        });\n\n        let depth_view = draw_depth_buffer.create_view(&wgpu::TextureViewDescriptor::default());\n\n        let water_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: water_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: water_uniforms.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(\n                        &reflection_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                    ),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::TextureView(&depth_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 3,\n                    resource: wgpu::BindingResource::Sampler(&color_sampler),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 4,\n                    resource: wgpu::BindingResource::Sampler(&depth_sampler),\n                },\n            ],\n            label: Some(\"Water Bind Group\"),\n        });\n\n        (\n            reflection_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n            depth_view,\n            water_bind_group,\n        )\n    }\n}\n\nimpl crate::framework::Example for Example {\n    fn init(\n        config: &wgpu::SurfaceConfiguration,\n        _adapter: &wgpu::Adapter,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) -> Self {\n        // Size of one water vertex\n        let water_vertex_size = size_of::<point_gen::WaterVertexAttributes>();\n\n        let water_vertices = point_gen::HexWaterMesh::generate(SIZE).generate_points();\n\n        // Size of one terrain vertex\n        let terrain_vertex_size = size_of::<point_gen::TerrainVertexAttributes>();\n\n        // Noise generation\n        let terrain_noise = noise::OpenSimplex::default();\n\n        // Random colouration\n        let mut terrain_random = WyRand::new_seed(42);\n\n        // Generate terrain. The closure determines what each hexagon will look like.\n        let terrain =\n            point_gen::HexTerrainMesh::generate(SIZE, |point| -> point_gen::TerrainVertex {\n                use noise::NoiseFn;\n                let noise = terrain_noise.get([point[0] as f64 / 5.0, point[1] as f64 / 5.0]) + 0.1;\n\n                let y = noise as f32 * 22.0;\n\n                // Multiplies a colour by some random amount.\n                fn mul_arr(mut arr: [u8; 4], by: f32) -> [u8; 4] {\n                    arr[0] = (arr[0] as f32 * by).min(255.0) as u8;\n                    arr[1] = (arr[1] as f32 * by).min(255.0) as u8;\n                    arr[2] = (arr[2] as f32 * by).min(255.0) as u8;\n                    arr\n                }\n\n                // Under water\n                const DARK_SAND: [u8; 4] = [235, 175, 71, 255];\n                // Coast\n                const SAND: [u8; 4] = [217, 191, 76, 255];\n                // Normal\n                const GRASS: [u8; 4] = [122, 170, 19, 255];\n                // Mountain\n                const SNOW: [u8; 4] = [175, 224, 237, 255];\n\n                // Random colouration.\n                let random = terrain_random.generate::<f32>() * 0.2 + 0.9;\n\n                // Choose colour.\n                let colour = if y <= 0.0 {\n                    DARK_SAND\n                } else if y <= 0.8 {\n                    SAND\n                } else if y <= 10.0 {\n                    GRASS\n                } else {\n                    SNOW\n                };\n                point_gen::TerrainVertex {\n                    position: Vec3::new(point[0], y, point[1]),\n                    colour: mul_arr(colour, random),\n                }\n            });\n\n        // Generate the buffer data.\n        let terrain_vertices = terrain.make_buffer_data();\n\n        // Create the buffers on the GPU to hold the data.\n        let water_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Water vertices\"),\n            contents: bytemuck::cast_slice(&water_vertices),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let terrain_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Terrain vertices\"),\n            contents: bytemuck::cast_slice(&terrain_vertices),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        // Create the bind group layout. This is what our uniforms will look like.\n        let water_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: Some(\"Water Bind Group Layout\"),\n                entries: &[\n                    // Uniform variables such as projection/view.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new(\n                                size_of::<WaterUniforms>() as _,\n                            ),\n                        },\n                        count: None,\n                    },\n                    // Reflection texture.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            multisampled: false,\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                        },\n                        count: None,\n                    },\n                    // Depth texture for terrain.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 2,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            multisampled: false,\n                            sample_type: wgpu::TextureSampleType::Float { filterable: false },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                        },\n                        count: None,\n                    },\n                    // Sampler to be able to sample the textures.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 3,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    },\n                    // Sampler to be able to sample the textures.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 4,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),\n                        count: None,\n                    },\n                ],\n            });\n\n        let terrain_bind_group_layout =\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: Some(\"Terrain Bind Group Layout\"),\n                entries: &[\n                    // Regular uniform variables like view/projection.\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::VERTEX,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: wgpu::BufferSize::new(\n                                size_of::<TerrainUniforms>() as _\n                            ),\n                        },\n                        count: None,\n                    },\n                ],\n            });\n\n        // Create our pipeline layouts.\n        let water_pipeline_layout =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"water\"),\n                bind_group_layouts: &[Some(&water_bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        let terrain_pipeline_layout =\n            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: Some(\"terrain\"),\n                bind_group_layouts: &[Some(&terrain_bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        let water_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"Water Uniforms\"),\n            size: size_of::<WaterUniforms>() as _,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let terrain_normal_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"Normal Terrain Uniforms\"),\n            size: size_of::<TerrainUniforms>() as _,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let terrain_flipped_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"Flipped Terrain Uniforms\"),\n            size: size_of::<TerrainUniforms>() as _,\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        // Create bind group.\n        // This puts values behind what was laid out in the bind group layout.\n\n        let (reflect_view, depth_buffer, water_bind_group) = Self::initialize_resources(\n            config,\n            device,\n            queue,\n            &water_uniform_buf,\n            &terrain_normal_uniform_buf,\n            &terrain_flipped_uniform_buf,\n            &water_bind_group_layout,\n        );\n\n        let terrain_normal_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &terrain_bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: terrain_normal_uniform_buf.as_entire_binding(),\n            }],\n            label: Some(\"Terrain Normal Bind Group\"),\n        });\n\n        // Binds to the uniform buffer where the\n        // camera has been placed underwater.\n        let terrain_flipped_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &terrain_bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: terrain_flipped_uniform_buf.as_entire_binding(),\n            }],\n            label: Some(\"Terrain Flipped Bind Group\"),\n        });\n\n        // Upload/compile them to GPU code.\n        let terrain_module = device.create_shader_module(wgpu::include_wgsl!(\"terrain.wgsl\"));\n        let water_module = device.create_shader_module(wgpu::include_wgsl!(\"water.wgsl\"));\n\n        // Create the render pipelines. These describe how the data will flow through the GPU, and what\n        // constraints and modifiers it will have.\n        let water_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"water\"),\n            // The \"layout\" is what uniforms will be needed.\n            layout: Some(&water_pipeline_layout),\n            // Vertex shader and input buffers\n            vertex: wgpu::VertexState {\n                module: &water_module,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                // Layout of our vertices. This should match the structs\n                // which are uploaded to the GPU. This should also be\n                // ensured by tagging on either a `#[repr(C)]` onto a\n                // struct, or a `#[repr(transparent)]` if it only contains\n                // one item, which is itself `repr(C)`.\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: water_vertex_size as wgpu::BufferAddress,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Sint16x2, 1 => Sint8x4],\n                }],\n            },\n            // Fragment shader and output targets\n            fragment: Some(wgpu::FragmentState {\n                module: &water_module,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                // Describes how the colour will be interpolated\n                // and assigned to the output attachment.\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: config.view_formats[0],\n                    blend: Some(wgpu::BlendState {\n                        color: wgpu::BlendComponent {\n                            src_factor: wgpu::BlendFactor::SrcAlpha,\n                            dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                            operation: wgpu::BlendOperation::Add,\n                        },\n                        alpha: wgpu::BlendComponent {\n                            src_factor: wgpu::BlendFactor::One,\n                            dst_factor: wgpu::BlendFactor::One,\n                            operation: wgpu::BlendOperation::Max,\n                        },\n                    }),\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            // How the triangles will be rasterized. This is more important\n            // for the terrain because of the beneath-the water shot.\n            // This is also dependent on how the triangles are being generated.\n            primitive: wgpu::PrimitiveState {\n                // What kind of data are we passing in?\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                front_face: wgpu::FrontFace::Cw,\n                ..Default::default()\n            },\n            // Describes how us writing to the depth/stencil buffer\n            // will work. Since this is water, we need to read from the\n            // depth buffer both as a texture in the shader, and as an\n            // input attachment to do depth-testing. We don't write, so\n            // depth_write_enabled is set to false. This is called RODS,\n            // or read-only depth stencil. Here, we don't use stencil.\n            depth_stencil: Some(wgpu::DepthStencilState {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: Some(false),\n                depth_compare: Some(wgpu::CompareFunction::Less),\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            }),\n            // No multisampling is used.\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            // No pipeline caching is used\n            cache: None,\n        });\n\n        // Same idea as the water pipeline.\n        let terrain_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"terrain\"),\n            layout: Some(&terrain_pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &terrain_module,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: terrain_vertex_size as wgpu::BufferAddress,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Unorm8x4],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &terrain_module,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(config.view_formats[0].into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: Some(wgpu::Face::Front),\n                ..Default::default()\n            },\n            depth_stencil: Some(wgpu::DepthStencilState {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: Some(true),\n                depth_compare: Some(wgpu::CompareFunction::Less),\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            }),\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None\n        });\n\n        // A render bundle to draw the terrain.\n        let terrain_bundle = {\n            let mut encoder =\n                device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {\n                    label: None,\n                    color_formats: &[Some(config.view_formats[0])],\n                    depth_stencil: Some(wgpu::RenderBundleDepthStencil {\n                        format: wgpu::TextureFormat::Depth32Float,\n                        depth_read_only: false,\n                        stencil_read_only: true,\n                    }),\n                    sample_count: 1,\n                    multiview: None,\n                });\n            encoder.set_pipeline(&terrain_pipeline);\n            encoder.set_bind_group(0, &terrain_flipped_bind_group, &[]);\n            encoder.set_vertex_buffer(0, terrain_vertex_buf.slice(..));\n            encoder.draw(0..terrain_vertices.len() as u32, 0..1);\n            encoder.finish(&wgpu::RenderBundleDescriptor::default())\n        };\n\n        // Done\n        Example {\n            water_vertex_buf,\n            water_vertex_count: water_vertices.len(),\n            water_bind_group_layout,\n            water_bind_group,\n            water_uniform_buf,\n            water_pipeline,\n\n            terrain_vertex_buf,\n            terrain_vertex_count: terrain_vertices.len(),\n            terrain_normal_bind_group,\n            terrain_normal_uniform_buf,\n            terrain_flipped_uniform_buf,\n            terrain_pipeline,\n            terrain_bundle,\n\n            reflect_view,\n\n            depth_buffer,\n\n            current_frame: 0,\n\n            active: Some(0),\n        }\n    }\n\n    fn update(&mut self, _event: winit::event::WindowEvent) {\n        //empty\n    }\n\n    fn resize(\n        &mut self,\n        config: &wgpu::SurfaceConfiguration,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n    ) {\n        if config.width == 0 && config.height == 0 {\n            // Stop rendering altogether.\n            self.active = None;\n            return;\n        }\n        self.active = Some(self.current_frame);\n\n        // Regenerate all of the buffers and textures.\n\n        let (reflect_view, depth_buffer, water_bind_group) = Self::initialize_resources(\n            config,\n            device,\n            queue,\n            &self.water_uniform_buf,\n            &self.terrain_normal_uniform_buf,\n            &self.terrain_flipped_uniform_buf,\n            &self.water_bind_group_layout,\n        );\n        self.water_bind_group = water_bind_group;\n\n        self.depth_buffer = depth_buffer;\n        self.reflect_view = reflect_view;\n    }\n\n    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {\n        // Increment frame count regardless of if we draw.\n        self.current_frame += 1;\n        #[expect(clippy::eq_op, reason = \"keeping common divisor on all elements\")]\n        let back_color = wgpu::Color {\n            r: 161.0 / 255.0,\n            g: 246.0 / 255.0,\n            b: 255.0 / 255.0,\n            a: 1.0,\n        };\n\n        // Write the sin/cos values to the uniform buffer for the water.\n        let (water_sin, water_cos) = ((self.current_frame as f32) / 600.0).sin_cos();\n        queue.write_buffer(\n            &self.water_uniform_buf,\n            size_of::<[f32; 16]>() as wgpu::BufferAddress * 2,\n            bytemuck::cast_slice(&[water_sin, water_cos]),\n        );\n\n        // Only render valid frames. See resize method.\n        if let Some(active) = self.active {\n            if active >= self.current_frame {\n                return;\n            }\n        } else {\n            return;\n        }\n\n        // The encoder provides a way to turn our instructions here, into\n        // a command buffer the GPU can understand.\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"Main Command Encoder\"),\n        });\n\n        // First pass: render the reflection.\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &self.reflect_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(back_color),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                // We still need to use the depth buffer here\n                // since the pipeline requires it.\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &self.depth_buffer,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Store,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.execute_bundles([&self.terrain_bundle]);\n        }\n        // Terrain right side up. This time we need to use the\n        // depth values, so we must use StoreOp::Store.\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(back_color),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &self.depth_buffer,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Store,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.set_pipeline(&self.terrain_pipeline);\n            rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);\n            rpass.set_vertex_buffer(0, self.terrain_vertex_buf.slice(..));\n            rpass.draw(0..self.terrain_vertex_count as u32, 0..1);\n        }\n        // Render the water. This reads from the depth buffer, but does not write\n        // to it, so it cannot be in the same render pass.\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Load,\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &self.depth_buffer,\n                    depth_ops: None,\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&self.water_pipeline);\n            rpass.set_bind_group(0, &self.water_bind_group, &[]);\n            rpass.set_vertex_buffer(0, self.water_vertex_buf.slice(..));\n            rpass.draw(0..self.water_vertex_count as u32, 0..1);\n        }\n\n        queue.submit(iter::once(encoder.finish()));\n    }\n}\n\npub fn main() {\n    crate::framework::run::<Example>(\"water\");\n}\n\n#[cfg(test)]\n#[wgpu_test::gpu_test]\npub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {\n    name: \"water\",\n    image_path: \"/examples/features/src/water/screenshot.png\",\n    width: 1024,\n    height: 768,\n    optional_features: wgpu::Features::default(),\n    base_test_parameters: wgpu_test::TestParameters::default()\n        .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)\n        // To be fixed in <https://github.com/gfx-rs/wgpu/issues/5231>.\n        .expect_fail(wgpu_test::FailureCase {\n            backends: Some(wgpu::Backends::VULKAN),\n            reasons: vec![wgpu_test::FailureReason::validation_error()\n                .with_message(\"WRITE_AFTER_WRITE hazard detected.\")],\n            behavior: wgpu_test::FailureBehavior::AssertFailure,\n            ..Default::default()\n        }),\n    comparisons: &[wgpu_test::ComparisonType::Mean(0.018)], // Bounded by Apple A9\n    _phantom: std::marker::PhantomData::<Example>,\n};\n"
  },
  {
    "path": "examples/features/src/water/point_gen.rs",
    "content": "//!\n//! This module covers generating points in a hexagonal fashion.\n//!\n\nuse bytemuck::{Pod, Zeroable};\nuse std::collections::HashMap;\n\n// The following constants are used in calculations.\n// A and B are multiplication factors for x and y.\n\n///\n/// X multiplication factor.\n/// 1.0 / sqrt(2)\n///\nconst A: f32 = std::f32::consts::FRAC_1_SQRT_2;\n\n///\n/// Y multiplication factor.\n/// sqrt(3) / sqrt(2) == sqrt(1.5)\n///\nconst B: f32 = SQRT_3 * A;\n\n///\n/// `sin(45deg)` is used to rotate the points.\n///\nconst S45: f32 = std::f32::consts::FRAC_1_SQRT_2;\n///\n/// `cos(45deg)` is used to rotate the points.\n///\nconst C45: f32 = S45;\n\nconst SQRT_3: f32 = 1.7320508;\n\n#[repr(C)]\n#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]\npub struct TerrainVertexAttributes {\n    position: [f32; 3],\n    normal: [f32; 3],\n    colour: [u8; 4],\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Pod, Zeroable)]\npub struct WaterVertexAttributes {\n    position: [i16; 2],\n    offsets: [i8; 4],\n}\n\n///\n/// Represents the center of a single hexagon.\n///\n#[derive(Copy, Clone, Debug)]\npub struct TerrainVertex {\n    pub position: glam::Vec3,\n    pub colour: [u8; 4],\n}\n\n///\n/// Gets the surrounding hexagonal points from a point.\n///\n/// +---0---1\n/// | / |   |\n/// 5---p---2\n/// |   | / |\n/// 4---3---+\n///\nfn surrounding_hexagonal_points(x: isize, y: isize) -> [(isize, isize); 6] {\n    [\n        (x, y - 1),\n        (x + 1, y - 1),\n        (x + 1, y),\n        (x, y + 1),\n        (x - 1, y + 1),\n        (x - 1, y),\n    ]\n}\n\nfn surrounding_point_values_iter<T>(\n    hashmap: &HashMap<(isize, isize), T>,\n    x: isize,\n    y: isize,\n    for_each: impl FnMut((&T, &T)),\n) {\n    let points = surrounding_hexagonal_points(x, y);\n    let points = [\n        points[0], points[1], points[2], points[3], points[4], points[5], points[0],\n    ];\n    points\n        .windows(2)\n        .map(|x| (hashmap.get(&x[0]), hashmap.get(&x[1])))\n        .flat_map(|(a, b)| a.and_then(|x| b.map(|y| (x, y))))\n        .for_each(for_each);\n}\n\n///\n/// Used in calculating terrain normals.\n///\npub fn calculate_normal(a: glam::Vec3, b: glam::Vec3, c: glam::Vec3) -> glam::Vec3 {\n    (b - a).normalize().cross((c - a).normalize()).normalize()\n}\n\n///\n/// Given the radius, how large of a square do we need to make a unit hexagon grid?\n///\nfn q_given_r(radius: f32) -> usize {\n    ((((((4.0 * radius) / SQRT_3) + 1.0).floor() / 2.0).floor() * 2.0) + 1.0) as usize\n}\n\n///\n/// Represents terrain, however it contains the vertices only once.\n///\n#[derive(Clone)]\npub struct HexTerrainMesh {\n    pub vertices: HashMap<(isize, isize), TerrainVertex>,\n    half_size: isize,\n}\n\nimpl HexTerrainMesh {\n    ///\n    /// Generates the vertices (or the centers of the hexagons). The colour and height is determined by\n    /// a function passed in by the user.\n    ///\n    pub fn generate(radius: f32, mut gen_vertex: impl FnMut([f32; 2]) -> TerrainVertex) -> Self {\n        let width = q_given_r(radius);\n        let half_width = (width / 2) as isize;\n        let mut map = HashMap::new();\n        let mut max = f32::NEG_INFINITY;\n        for i in -half_width..=half_width {\n            let x_o = i as f32;\n            for j in -half_width..=half_width {\n                let y_o = j as f32;\n                let x = A * (x_o * C45 - y_o * S45);\n                let z = B * (x_o * S45 + y_o * C45);\n                if x.hypot(z) < radius {\n                    let vertex = gen_vertex([x, z]);\n                    if vertex.position.y > max {\n                        max = vertex.position.y;\n                    }\n                    map.insert((i, j), vertex);\n                }\n            }\n        }\n        Self {\n            vertices: map,\n            half_size: width as isize / 2,\n        }\n    }\n\n    ///\n    /// Creates the points required to render the mesh.\n    ///\n    pub fn make_buffer_data(&self) -> Vec<TerrainVertexAttributes> {\n        let mut vertices = Vec::new();\n        fn middle(p1: &TerrainVertex, p2: &TerrainVertex, p: &TerrainVertex) -> glam::Vec3 {\n            (p1.position + p2.position + p.position) / 3.0\n        }\n        fn half(p1: &TerrainVertex, p2: &TerrainVertex) -> glam::Vec3 {\n            (p1.position + p2.position) / 2.0\n        }\n        let mut push_triangle = |p1: &TerrainVertex,\n                                 p2: &TerrainVertex,\n                                 p: &TerrainVertex,\n                                 c: [u8; 4]| {\n            let m = middle(p1, p2, p);\n            let ap = half(p1, p);\n            let bp = half(p2, p);\n            let p = p.position;\n            let n1 = calculate_normal(ap, m, p);\n            let n2 = calculate_normal(m, bp, p);\n\n            vertices.extend(\n                [ap, m, p, m, bp, p]\n                    .iter()\n                    .zip(\n                        std::iter::repeat::<[f32; 3]>(n1.into())\n                            .chain(std::iter::repeat::<[f32; 3]>(n2.into())),\n                    )\n                    .zip(std::iter::repeat(c))\n                    .map(|((pos, normal), colour)| TerrainVertexAttributes {\n                        position: *pos.as_ref(),\n                        normal,\n                        colour,\n                    }),\n            );\n        };\n        for i in -self.half_size..=self.half_size {\n            for j in -self.half_size..=self.half_size {\n                if let Some(p) = self.vertices.get(&(i, j)) {\n                    surrounding_point_values_iter(&self.vertices, i, j, |(a, b)| {\n                        push_triangle(a, b, p, p.colour)\n                    });\n                }\n            }\n        }\n        vertices\n    }\n}\n\n///\n/// Water mesh which contains vertex data for the water mesh.\n///\n/// It stores the values multiplied and rounded to the\n/// nearest whole number to be more efficient with space when\n/// sending large meshes to the GPU.\n///\npub struct HexWaterMesh {\n    pub vertices: HashMap<(isize, isize), [i16; 2]>,\n    half_size: isize,\n}\n\nimpl HexWaterMesh {\n    pub fn generate(radius: f32) -> Self {\n        let width = q_given_r(radius);\n        let half_width = (width / 2) as isize;\n        let mut map = HashMap::new();\n\n        for i in -half_width..=half_width {\n            let x_o = i as f32;\n            for j in -half_width..=half_width {\n                let y_o = j as f32;\n                let x = A * (x_o * C45 - y_o * S45);\n                let z = B * (x_o * S45 + y_o * C45);\n                if x.hypot(z) < radius {\n                    let x = (x * 2.0).round() as i16;\n                    let z = ((z / B) * std::f32::consts::SQRT_2).round() as i16;\n                    map.insert((i, j), [x, z]);\n                }\n            }\n        }\n        Self {\n            vertices: map,\n            half_size: half_width,\n        }\n    }\n    ///\n    /// Generates the points required to render the mesh.\n    ///\n    pub fn generate_points(&self) -> Vec<WaterVertexAttributes> {\n        let mut vertices = Vec::new();\n\n        fn calculate_differences(a: [i16; 2], b: [i16; 2], c: [i16; 2]) -> [i8; 4] {\n            [\n                (b[0] - a[0]) as i8,\n                (b[1] - a[1]) as i8,\n                (c[0] - a[0]) as i8,\n                (c[1] - a[1]) as i8,\n            ]\n        }\n\n        let mut push_triangle = |a: [i16; 2], b: [i16; 2], c: [i16; 2]| {\n            let bc = calculate_differences(a, b, c);\n            let ca = calculate_differences(b, c, a);\n            let ab = calculate_differences(c, a, b);\n\n            vertices.extend(\n                [a, b, c]\n                    .iter()\n                    .zip([bc, ca, ab].iter())\n                    .map(|(&position, &offsets)| WaterVertexAttributes { position, offsets }),\n            );\n        };\n\n        for i in -self.half_size..=self.half_size {\n            for j in -self.half_size..=self.half_size {\n                if (i - j) % 3 == 0 {\n                    if let Some(&p) = self.vertices.get(&(i, j)) {\n                        surrounding_point_values_iter(&self.vertices, i, j, |(a, b)| {\n                            push_triangle(*a, *b, p)\n                        });\n                    }\n                }\n            }\n        }\n\n        vertices\n    }\n}\n"
  },
  {
    "path": "examples/features/src/water/terrain.wgsl",
    "content": "struct Uniforms {\n    projection_view: mat4x4<f32>,\n    clipping_plane: vec4<f32>,\n};\n\n@group(0)\n@binding(0)\nvar<uniform> uniforms: Uniforms;\n\nconst light = vec3<f32>(150.0, 70.0, 0.0);\nconst light_colour = vec3<f32>(1.0, 0.98, 0.82);\nconst ambient = 0.2;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) colour: vec4<f32>,\n    // Comment this out if using user-clipping planes:\n    @location(1) clip_dist: f32,\n};\n\n@vertex\nfn vs_main(\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n    @location(2) colour: vec4<f32>,\n) -> VertexOutput {\n    var result: VertexOutput;\n    result.position = uniforms.projection_view * vec4<f32>(position, 1.0);\n\n    // https://www.desmos.com/calculator/nqgyaf8uvo\n    let normalized_light_direction = normalize(position - light);\n    let brightness_diffuse = clamp(dot(normalized_light_direction, normal), 0.2, 1.0);\n\n    result.colour = vec4<f32>(max((brightness_diffuse + ambient) * light_colour * colour.rgb, vec3<f32>(0.0, 0.0, 0.0)), colour.a);\n    result.clip_dist = dot(vec4<f32>(position, 1.0), uniforms.clipping_plane);\n    return result;\n}\n\n@fragment\nfn fs_main(\n    vertex: VertexOutput,\n) -> @location(0) vec4<f32> {\n    // Comment this out if using user-clipping planes:\n    if(vertex.clip_dist < 0.0) {\n        discard;\n    }\n\n    return vec4<f32>(vertex.colour.xyz, 1.0);\n}\n"
  },
  {
    "path": "examples/features/src/water/water.wgsl",
    "content": "struct Uniforms {\n    view: mat4x4<f32>,\n    projection: mat4x4<f32>,\n    time_size_width: vec4<f32>,\n    viewport_height: f32,\n};\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\nconst light_point = vec3<f32>(150.0, 70.0, 0.0);\nconst light_colour = vec3<f32>(1.0, 0.98, 0.82);\nconst one = vec4<f32>(1.0, 1.0, 1.0, 1.0);\n\nconst Y_SCL: f32 = 0.86602540378443864676372317075294;\nconst CURVE_BIAS: f32 = -0.1;\nconst INV_1_CURVE_BIAS: f32 = 1.11111111111; //1.0 / (1.0 + CURVE_BIAS);\n\n// Polyfill for modf to deal with differences between chrome's WebGPU and\n// current naga.\nfn modf_polyfill_vec3(value: vec3<f32>, int_part: ptr<function, vec3<f32>>) -> vec3<f32> {\n    *int_part = trunc(value);\n    return value - *int_part;\n}\nfn modf_polyfill_vec4(value: vec4<f32>, int_part: ptr<function, vec4<f32>>) -> vec4<f32> {\n    *int_part = trunc(value);\n    return value - *int_part;\n}\n\n//\n// The following code to calculate simplex 3D\n// is from https://github.com/ashima/webgl-noise\n//\n//  Simplex 3D Noise\n//  by Ian McEwan, Ashima Arts.\n//\nfn permute(x: vec4<f32>) -> vec4<f32> {\n    var temp: vec4<f32> = 289.0 * one;\n    return modf_polyfill_vec4(((x*34.0) + one) * x, &temp);\n}\n\nfn taylorInvSqrt(r: vec4<f32>) -> vec4<f32> {\n    return 1.79284291400159 * one - 0.85373472095314 * r;\n}\n\nfn snoise(v: vec3<f32>) -> f32 {\n    let C = vec2<f32>(1.0/6.0, 1.0/3.0);\n    let D = vec4<f32>(0.0, 0.5, 1.0, 2.0);\n\n    // First corner\n    //TODO: use the splat operations when available\n    let vCy = dot(v, C.yyy);\n    var i: vec3<f32> = floor(v + vec3<f32>(vCy, vCy, vCy));\n    let iCx = dot(i, C.xxx);\n    let x0 = v - i + vec3<f32>(iCx, iCx, iCx);\n\n    // Other corners\n    let g = step(x0.yzx, x0.xyz);\n    let l = (vec3<f32>(1.0, 1.0, 1.0) - g).zxy;\n    let i1 = min(g, l);\n    let i2 = max(g, l);\n\n    //   x0 = x0 - 0.0 + 0.0 * C.xxx;\n    //   x1 = x0 - i1  + 1.0 * C.xxx;\n    //   x2 = x0 - i2  + 2.0 * C.xxx;\n    //   x3 = x0 - 1.0 + 3.0 * C.xxx;\n    let x1 = x0 - i1 + C.xxx;\n    let x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n    let x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n    // Permutations\n    var temp: vec3<f32> = 289.0 * one.xyz;\n    i = modf_polyfill_vec3(i, &temp);\n    let p = permute(\n        permute(\n            permute(i.zzzz + vec4<f32>(0.0, i1.z, i2.z, 1.0))\n            + i.yyyy + vec4<f32>(0.0, i1.y, i2.y, 1.0))\n        + i.xxxx + vec4<f32>(0.0, i1.x, i2.x, 1.0));\n\n    // Gradients: 7x7 points over a square, mapped onto an octahedron.\n    // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n    let n_ = 0.142857142857;// 1.0/7.0\n    let ns = n_ * D.wyz - D.xzx;\n\n    let j = p - 49.0 * floor(p * ns.z * ns.z);//  mod(p,7*7)\n\n    let x_ = floor(j * ns.z);\n    let y_ = floor(j - 7.0 * x_);// mod(j,N)\n\n    var x: vec4<f32> = x_ *ns.x + ns.yyyy;\n    var y: vec4<f32> = y_ *ns.x + ns.yyyy;\n    let h = one - abs(x) - abs(y);\n\n    let b0 = vec4<f32>(x.xy, y.xy);\n    let b1 = vec4<f32>(x.zw, y.zw);\n\n    //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - one;\n    //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - one;\n    let s0 = floor(b0)*2.0 + one;\n    let s1 = floor(b1)*2.0 + one;\n    let sh = -step(h, 0.0 * one);\n\n    let a0 = b0.xzyw + s0.xzyw*sh.xxyy;\n    let a1 = b1.xzyw + s1.xzyw*sh.zzww;\n\n    var p0 = vec3<f32>(a0.xy, h.x);\n    var p1 = vec3<f32>(a0.zw, h.y);\n    var p2 = vec3<f32>(a1.xy, h.z);\n    var p3 = vec3<f32>(a1.zw, h.w);\n\n    //Normalise gradients\n    let norm = taylorInvSqrt(vec4<f32>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));\n    p0 *= norm.x;\n    p1 *= norm.y;\n    p2 *= norm.z;\n    p3 *= norm.w;\n\n    // Mix final noise value\n    var m: vec4<f32> = max(0.6 * one - vec4<f32>(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0 * one);\n    m *= m;\n    return 9.0 * dot(m*m, vec4<f32>(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));\n}\n\n// End of 3D simplex code.\n\nfn apply_distortion(pos: vec3<f32>) -> vec3<f32> {\n    var perlin_pos: vec3<f32> = pos;\n\n    //Do noise transformation to permit for smooth,\n    //continuous movement.\n\n    //TODO: we should be able to name them `sin` and `cos`.\n    let sn = uniforms.time_size_width.x;\n    let cs = uniforms.time_size_width.y;\n    let size = uniforms.time_size_width.z;\n\n    // Rotate 90 Z, Move Left Size / 2\n    perlin_pos = vec3<f32>(perlin_pos.y - perlin_pos.x - size, perlin_pos.x, perlin_pos.z);\n\n    let xcos = perlin_pos.x * cs;\n    let xsin = perlin_pos.x * sn;\n    let ycos = perlin_pos.y * cs;\n    let ysin = perlin_pos.y * sn;\n    let zcos = perlin_pos.z * cs;\n    let zsin = perlin_pos.z * sn;\n\n    // Rotate Time Y\n    let perlin_pos_y = vec3<f32>(xcos + zsin, perlin_pos.y, -xsin + xcos);\n\n    // Rotate Time Z\n    let perlin_pos_z = vec3<f32>(xcos - ysin, xsin + ycos, perlin_pos.x);\n\n    // Rotate 90 Y\n    perlin_pos = vec3<f32>(perlin_pos.z - perlin_pos.x, perlin_pos.y, perlin_pos.x);\n\n    // Rotate Time X\n    let perlin_pos_x = vec3<f32>(perlin_pos.x, ycos - zsin, ysin + zcos);\n\n    // Sample at different places for x/y/z to get random-looking water.\n    return vec3<f32>(\n        //TODO: use splats\n        pos.x + snoise(perlin_pos_x + 2.0*one.xxx) * 0.4,\n        pos.y + snoise(perlin_pos_y - 2.0*one.xxx) * 1.8,\n        pos.z + snoise(perlin_pos_z) * 0.4\n    );\n}\n\n// Multiply the input by the scale values.\nfn make_position(original: vec2<f32>) -> vec4<f32> {\n    let interpreted = vec3<f32>(original.x * 0.5, 0.0, original.y * Y_SCL);\n    return vec4<f32>(apply_distortion(interpreted), 1.0);\n}\n\n// Create the normal, and apply the curve. Change the Curve Bias above.\nfn make_normal(a: vec3<f32>, b: vec3<f32>, c: vec3<f32>) -> vec3<f32> {\n    let norm = normalize(cross(b - c, a - c));\n    let center = (a + b + c) * (1.0 / 3.0); //TODO: use splat\n    return (normalize(a - center) * CURVE_BIAS + norm) * INV_1_CURVE_BIAS;\n}\n\n// Calculate the fresnel effect.\nfn calc_fresnel(view: vec3<f32>, normal: vec3<f32>) -> f32 {\n    var refractive: f32 = abs(dot(view, normal));\n    refractive = pow(refractive, 1.33333333333);\n    return refractive;\n}\n\n// Calculate the specular lighting.\nfn calc_specular(eye: vec3<f32>, normal: vec3<f32>, light: vec3<f32>) -> f32 {\n    let light_reflected = reflect(light, normal);\n    var specular: f32 = max(dot(eye, light_reflected), 0.0);\n    specular = pow(specular, 10.0);\n    return specular;\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) f_WaterScreenPos: vec2<f32>,\n    @location(1) f_Fresnel: f32,\n    @location(2) f_Light: vec3<f32>,\n};\n\n@vertex\nfn vs_main(\n    @location(0) position: vec2<i32>,\n    @location(1) offsets: vec4<i32>,\n) -> VertexOutput {\n    let p_pos = vec2<f32>(position);\n    let b_pos = make_position(p_pos + vec2<f32>(offsets.xy));\n    let c_pos = make_position(p_pos + vec2<f32>(offsets.zw));\n    let a_pos = make_position(p_pos);\n    let original_pos = vec4<f32>(p_pos.x * 0.5, 0.0, p_pos.y * Y_SCL, 1.0);\n\n    let vm = uniforms.view;\n    let transformed_pos = vm * a_pos;\n    //TODO: use vector splats for division\n    let water_pos = transformed_pos.xyz * (1.0 / transformed_pos.w);\n    let normal = make_normal((vm * a_pos).xyz, (vm * b_pos).xyz, (vm * c_pos).xyz);\n    let eye = normalize(-water_pos);\n    let transformed_light = vm * vec4<f32>(light_point, 1.0);\n\n    var result: VertexOutput;\n    result.f_Light = light_colour * calc_specular(eye, normal, normalize(water_pos.xyz - (transformed_light.xyz * (1.0 / transformed_light.w))));\n    result.f_Fresnel = calc_fresnel(eye, normal);\n\n    let gridpos = uniforms.projection * vm * original_pos;\n    result.f_WaterScreenPos = (0.5 * gridpos.xy * (1.0 / gridpos.w)) + vec2<f32>(0.5, 0.5);\n\n    result.position = uniforms.projection * transformed_pos;\n    return result;\n}\n\n\nconst water_colour = vec3<f32>(0.0, 0.46, 0.95);\nconst zNear = 10.0;\nconst zFar = 400.0;\n\n@group(0) @binding(1) var reflection: texture_2d<f32>;\n@group(0) @binding(2) var terrain_depth_tex: texture_2d<f32>;\n@group(0) @binding(3) var colour_sampler: sampler;\n@group(0) @binding(4) var depth_sampler: sampler;\n\nfn to_linear_depth(depth: f32) -> f32 {\n    let z_n = 2.0 * depth - 1.0;\n    let z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));\n    return z_e;\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    let reflection_colour = textureSample(reflection, colour_sampler, vertex.f_WaterScreenPos.xy).xyz;\n\n    let pixel_depth = to_linear_depth(vertex.position.z);\n    let normalized_coords = vertex.position.xy / vec2<f32>(uniforms.time_size_width.w, uniforms.viewport_height);\n    let terrain_depth = to_linear_depth(textureSample(terrain_depth_tex, depth_sampler, normalized_coords).r);\n\n    let dist = terrain_depth - pixel_depth;\n    let clamped = pow(smoothstep(0.0, 1.5, dist), 4.8);\n\n    let final_colour = vertex.f_Light + reflection_colour;\n    let t = smoothstep(1.0, 5.0, dist) * 0.2; //TODO: splat for mix()?\n    let depth_colour = mix(final_colour, water_colour, vec3<f32>(t, t, t));\n\n    return vec4<f32>(depth_colour, clamped * (1.0 - vertex.f_Fresnel));\n}\n"
  },
  {
    "path": "examples/features/web-static/index.html",
    "content": "<html>\n\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta content=\"text/html;charset=utf-8\" http-equiv=\"Content-Type\" />\n    <style type=\"text/css\">\n        :focus {\n            outline: none;\n        }\n\n        body {\n            margin: 0px;\n            background: #fff;\n            font-family: \"Calibri\", \"Arial\", sans-serif;\n            width: 100%;\n            height: 100%;\n        }\n\n        .root {\n            width: 100%;\n            height: 100%;\n            display: flex;\n            flex-direction: column;\n        }\n\n        #banner {\n            background: #dee;\n            padding: 0.5em 0;\n            border-bottom: 1px solid #abb;\n\n            @media only screen and (max-width: 1000px) {\n                max-height: 0;\n                transition: max-height 0.15s ease;\n                overflow: hidden;\n                padding: 0;\n            }\n        }\n\n        #banner.visible {\n            max-height: 100%;\n        }\n\n        .banner-prefix {\n            text-align: center;\n        }\n\n        p {\n            font-size: 0.8em;\n            padding: 0;\n            margin: 0.5em 0;\n        }\n\n        h1 {\n            margin-left: 0.75em;\n            margin-right: 0.75em;\n        }\n\n        .backend-list-container {\n            display: flex;\n            flex-direction: row;\n            justify-content: space-evenly;\n\n            @media only screen and (max-width: 1000px) {\n                flex-direction: column;\n            }\n        }\n\n        .backend-list {\n            display: flex;\n            flex-direction: column;\n            flex-wrap: wrap;\n            justify-content: center;\n        }\n\n        .item-list {\n            display: flex;\n            flex-direction: column;\n            flex-wrap: wrap;\n            align-content: center;\n            height: 100px;\n\n            @media only screen and (max-width: 1000px) {\n                flex-direction: row;\n                height: initial;\n            }\n        }\n\n        .example-item {\n            @media only screen and (max-width: 1000px) {\n                width: 33vw;\n            }\n\n            @media only screen and (max-width: 500px) {\n                width: 50vw;\n            }\n        }\n\n        .example-link {\n            margin-left: 7px;\n            margin-right: 7px;\n        }\n        \n        .backend-name {\n            text-align: center;\n        }\n\n        #content {\n            /* This allows the flexbox to grow to max size, this is needed for WebGPU */\n            flex: 1 1 100%;\n            /* This forces CSS to ignore the width/height of the canvas, this is needed for WebGL */\n            contain: size;\n        }\n\n        .main-canvas {\n            margin: 0;\n            width: 100%;\n            height: 100%;\n        }\n\n        #menu-button {\n            display: none;\n\n            @media only screen and (max-width: 1000px) {\n                display: block;\n                width: 30px;\n                height: 33px;\n                margin: 0 auto;\n            }\n        }\n\n        #menu-button span {\n            display: block;\n            width: 100%;\n            height: 3px;\n            margin: 6px auto;\n            background-color: #333;\n            transition: all 0.3s ease-in-out;\n        }\n\n    </style>\n</head>\n\n<body>\n    <div class=\"root\">\n        <div id=\"menu-button\"><span></span><span></span><span></span></div>\n\n        <div id=\"banner\">\n            <div class=\"banner-prefix\">\n                <p>\n                    <a href=\"../index.html\">\n                        Back to home page\n                    </a>\n                </p>\n            </div>\n            <div class=\"backend-list-container\">\n                <div class=\"backend-list\">\n                    <p class=\"backend-name\">WebGL2</p>\n                    <div class=\"item-list\" id=\"webgl2-list\">\n                        \n                    </div>\n                </div>\n                <div class=\"backend-list\">\n                    <p class=\"backend-name\">WebGPU</p>\n                    <div class=\"item-list\" id=\"webgpu-list\">\n                \n                    </div>\n                </div>\n            </div>\n        </div>\n        <div id=\"content\">\n            <canvas class=\"main-canvas\" id=\"canvas\"></canvas>\n        </div>\n    </div>\n    <script type=\"module\">\n        const params = new URLSearchParams(window.location.search);\n        const backend = params.get(\"backend\");\n        const example = params.get(\"example\");\n        if (backend !== null && example !== null) {\n            import(`./${backend}.js`).then((module) => module.default());\n        } else {\n            window.location.assign(\n                `${window.location.href}?backend=webgl2&example=hello_triangle`\n            );\n        }\n\n        const menuButton = document.getElementById(\"menu-button\");\n        const banner = document.getElementById(\"banner\");\n        menuButton.onclick = () => {\n            if (menuButton.classList.contains(\"active\")) {\n                menuButton.classList.remove(\"active\");\n                banner.classList.remove(\"visible\");\n            } else {\n                menuButton.classList.add(\"active\");\n                banner.classList.add(\"visible\");\n            }\n        };\n    </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "examples/standalone/01_hello_compute/Cargo.toml",
    "content": "[package]\nname = \"wgpu-example-01-hello-compute\"\nedition = \"2021\"\nrust-version = \"1.87\"\npublish = false\n\n[dependencies]\nbytemuck = \"1.22.0\"\nenv_logger = \"0.11\"\npollster = \"0.4\"\nwgpu = \"29.0.0\"\n"
  },
  {
    "path": "examples/standalone/01_hello_compute/cargo-generate.toml",
    "content": "\n"
  },
  {
    "path": "examples/standalone/01_hello_compute/src/main.rs",
    "content": "/// To serve as an introduction to the wgpu api, we will implement a simple\n/// compute shader which takes a list of numbers on the CPU and doubles them on the GPU.\n///\n/// While this isn't a very practical example, you will see all the major components\n/// of using wgpu headlessly, including getting a device, running a shader, and transferring\n/// data between the CPU and GPU.\n///\n/// If you time the recording and execution of this example you will certainly see that\n/// running on the gpu is slower than doing the same calculation on the cpu. This is because\n/// floating point multiplication is a very simple operation so the transfer/submission overhead\n/// is quite a lot higher than the actual computation. This is normal and shows that the GPU\n/// needs a lot higher work/transfer ratio to come out ahead.\nuse std::{num::NonZeroU64, str::FromStr};\nuse wgpu::util::DeviceExt;\n\nfn main() {\n    // Parse all arguments as floats. We need to skip argument 0, which is the name of the program.\n    let arguments: Vec<f32> = std::env::args()\n        .skip(1)\n        .map(|s| {\n            f32::from_str(&s).unwrap_or_else(|_| panic!(\"Cannot parse argument {s:?} as a float.\"))\n        })\n        .collect();\n\n    if arguments.is_empty() {\n        println!(\"No arguments provided. Please provide a list of numbers to double.\");\n        return;\n    }\n\n    println!(\"Parsed {} arguments\", arguments.len());\n\n    // wgpu uses `log` for all of our logging, so we initialize a logger with the `env_logger` crate.\n    //\n    // To change the log level, set the `RUST_LOG` environment variable. See the `env_logger`\n    // documentation for more information.\n    env_logger::init();\n\n    // We first initialize an wgpu `Instance`, which contains any \"global\" state wgpu needs.\n    //\n    // This is what loads the vulkan/dx12/metal/opengl libraries.\n    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());\n\n    // We then create an `Adapter` which represents a physical gpu in the system. It allows\n    // us to query information about it and create a `Device` from it.\n    //\n    // This function is asynchronous in WebGPU, so request_adapter returns a future. On native/webgl\n    // the future resolves immediately, so we can block on it without harm.\n    let adapter =\n        pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))\n            .expect(\"Failed to create adapter\");\n\n    // Print out some basic information about the adapter.\n    println!(\"Running on Adapter: {:#?}\", adapter.get_info());\n\n    // Check to see if the adapter supports compute shaders. While WebGPU guarantees support for\n    // compute shaders, wgpu supports a wider range of devices through the use of \"downlevel\" devices.\n    let downlevel_capabilities = adapter.get_downlevel_capabilities();\n    if !downlevel_capabilities\n        .flags\n        .contains(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n    {\n        panic!(\"Adapter does not support compute shaders\");\n    }\n\n    // We then create a `Device` and a `Queue` from the `Adapter`.\n    //\n    // The `Device` is used to create and manage GPU resources.\n    // The `Queue` is a queue used to submit work for the GPU to process.\n    let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n        label: None,\n        required_features: wgpu::Features::empty(),\n        required_limits: wgpu::Limits::downlevel_defaults(),\n        experimental_features: wgpu::ExperimentalFeatures::disabled(),\n        memory_hints: wgpu::MemoryHints::MemoryUsage,\n        trace: wgpu::Trace::Off,\n    }))\n    .expect(\"Failed to create device\");\n\n    // Create a shader module from our shader code. This will parse and validate the shader.\n    //\n    // `include_wgsl` is a macro provided by wgpu like `include_str` which constructs a ShaderModuleDescriptor.\n    // If you want to load shaders differently, you can construct the ShaderModuleDescriptor manually.\n    let module = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n    // Create a buffer with the data we want to process on the GPU.\n    //\n    // `create_buffer_init` is a utility provided by `wgpu::util::DeviceExt` which simplifies creating\n    // a buffer with some initial data.\n    //\n    // We use the `bytemuck` crate to cast the slice of f32 to a &[u8] to be uploaded to the GPU.\n    let input_data_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(&arguments),\n        usage: wgpu::BufferUsages::STORAGE,\n    });\n\n    // Now we create a buffer to store the output data.\n    let output_data_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: input_data_buffer.size(),\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    // Finally we create a buffer which can be read by the CPU. This buffer is how we will read\n    // the data. We need to use a separate buffer because we need to have a usage of `MAP_READ`,\n    // and that usage can only be used with `COPY_DST`.\n    let download_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: input_data_buffer.size(),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    // A bind group layout describes the types of resources that a bind group can contain. Think\n    // of this like a C-style header declaration, ensuring both the pipeline and bind group agree\n    // on the types of resources.\n    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[\n            // Input buffer\n            wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: true },\n                    // This is the size of a single element in the buffer.\n                    min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n                    has_dynamic_offset: false,\n                },\n                count: None,\n            },\n            // Output buffer\n            wgpu::BindGroupLayoutEntry {\n                binding: 1,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    // This is the size of a single element in the buffer.\n                    min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n                    has_dynamic_offset: false,\n                },\n                count: None,\n            },\n        ],\n    });\n\n    // The bind group contains the actual resources to bind to the pipeline.\n    //\n    // Even when the buffers are individually dropped, wgpu will keep the bind group and buffers\n    // alive until the bind group itself is dropped.\n    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: input_data_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: output_data_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    // The pipeline layout describes the bind groups that a pipeline expects\n    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[Some(&bind_group_layout)],\n        immediate_size: 0,\n    });\n\n    // The pipeline is the ready-to-go program state for the GPU. It contains the shader modules,\n    // the interfaces (bind group layouts) and the shader entry point.\n    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &module,\n        entry_point: Some(\"doubleMe\"),\n        compilation_options: wgpu::PipelineCompilationOptions::default(),\n        cache: None,\n    });\n\n    // The command encoder allows us to record commands that we will later submit to the GPU.\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    // A compute pass is a single series of compute operations. While we are recording a compute\n    // pass, we cannot record to the encoder.\n    let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    // Set the pipeline that we want to use\n    compute_pass.set_pipeline(&pipeline);\n    // Set the bind group that we want to use\n    compute_pass.set_bind_group(0, &bind_group, &[]);\n\n    // Now we dispatch a series of workgroups. Each workgroup is a 3D grid of individual programs.\n    //\n    // We defined the workgroup size in the shader as 64x1x1. So in order to process all of our\n    // inputs, we ceiling divide the number of inputs by 64. If the user passes 32 inputs, we will\n    // dispatch 1 workgroups. If the user passes 65 inputs, we will dispatch 2 workgroups, etc.\n    let workgroup_count = arguments.len().div_ceil(64);\n    compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);\n\n    // Now we drop the compute pass, giving us access to the encoder again.\n    drop(compute_pass);\n\n    // We add a copy operation to the encoder. This will copy the data from the output buffer on the\n    // GPU to the download buffer on the CPU.\n    encoder.copy_buffer_to_buffer(\n        &output_data_buffer,\n        0,\n        &download_buffer,\n        0,\n        output_data_buffer.size(),\n    );\n\n    // We finish the encoder, giving us a fully recorded command buffer.\n    let command_buffer = encoder.finish();\n\n    // At this point nothing has actually been executed on the gpu. We have recorded a series of\n    // commands that we want to execute, but they haven't been sent to the gpu yet.\n    //\n    // Submitting to the queue sends the command buffer to the gpu. The gpu will then execute the\n    // commands in the command buffer in order.\n    queue.submit([command_buffer]);\n\n    // We now map the download buffer so we can read it. Mapping tells wgpu that we want to read/write\n    // to the buffer directly by the CPU and it should not permit any more GPU operations on the buffer.\n    //\n    // Mapping requires that the GPU be finished using the buffer before it resolves, so mapping has a callback\n    // to tell you when the mapping is complete.\n    let buffer_slice = download_buffer.slice(..);\n    buffer_slice.map_async(wgpu::MapMode::Read, |_| {\n        // In this case we know exactly when the mapping will be finished,\n        // so we don't need to do anything in the callback.\n    });\n\n    // Wait for the GPU to finish working on the submitted work. This doesn't work on WebGPU, so we would need\n    // to rely on the callback to know when the buffer is mapped.\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n    // We can now read the data from the buffer.\n    let data = buffer_slice.get_mapped_range();\n    // Convert the data back to a slice of f32.\n    let result: &[f32] = bytemuck::cast_slice(&data);\n\n    // Print out the result.\n    println!(\"Result: {result:?}\");\n}\n"
  },
  {
    "path": "examples/standalone/01_hello_compute/src/shader.wgsl",
    "content": "// Input to the shader. The length of the array is determined by what buffer is bound.\n//\n// Out of bounds accesses \n@group(0) @binding(0)\nvar<storage, read> input: array<f32>;\n// Output of the shader.  \n@group(0) @binding(1)\nvar<storage, read_write> output: array<f32>;\n\n// Ideal workgroup size depends on the hardware, the workload, and other factors. However, it should\n// _generally_ be a multiple of 64. Common sizes are 64x1x1, 256x1x1; or 8x8x1, 16x16x1 for 2D workloads.\n@compute @workgroup_size(64)\nfn doubleMe(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    // While compute invocations are 3d, we're only using one dimension.\n    let index = global_id.x;\n\n    // Because we're using a workgroup size of 64, if the input size isn't a multiple of 64,\n    // we will have some \"extra\" invocations. This is fine, but we should tell them to stop\n    // to avoid out-of-bounds accesses.\n    let array_length = arrayLength(&input);\n    if (global_id.x >= array_length) {\n        return;\n    }\n\n    // Do the multiply by two and write to the output.\n    output[global_id.x] = input[global_id.x] * 2.0;\n}\n"
  },
  {
    "path": "examples/standalone/02_hello_window/Cargo.toml",
    "content": "[package]\nname = \"wgpu-example-02-hello-window\"\nedition = \"2021\"\nrust-version = \"1.87\"\npublish = false\n\n[dependencies]\nenv_logger = \"0.11\"\npollster = \"0.4\"\nwgpu = \"29.0.0\"\nwinit = { version = \"0.30.8\", features = [\"android-native-activity\"] }\n"
  },
  {
    "path": "examples/standalone/02_hello_window/src/main.rs",
    "content": "use std::sync::Arc;\n\nuse winit::{\n    application::ApplicationHandler,\n    event::WindowEvent,\n    event_loop::{ActiveEventLoop, ControlFlow, EventLoop, OwnedDisplayHandle},\n    window::{Window, WindowId},\n};\n\nstruct State {\n    instance: wgpu::Instance,\n    window: Arc<Window>,\n    device: wgpu::Device,\n    queue: wgpu::Queue,\n    size: winit::dpi::PhysicalSize<u32>,\n    surface: wgpu::Surface<'static>,\n    surface_format: wgpu::TextureFormat,\n}\n\nimpl State {\n    async fn new(display: OwnedDisplayHandle, window: Arc<Window>) -> State {\n        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle(\n            Box::new(display),\n        ));\n        let adapter = instance\n            .request_adapter(&wgpu::RequestAdapterOptions::default())\n            .await\n            .unwrap();\n        let (device, queue) = adapter\n            .request_device(&wgpu::DeviceDescriptor::default())\n            .await\n            .unwrap();\n\n        let size = window.inner_size();\n\n        let surface = instance.create_surface(window.clone()).unwrap();\n        let cap = surface.get_capabilities(&adapter);\n        let surface_format = cap.formats[0];\n\n        let state = State {\n            instance,\n            window,\n            device,\n            queue,\n            size,\n            surface,\n            surface_format,\n        };\n\n        // Configure surface for the first time\n        state.configure_surface();\n\n        state\n    }\n\n    fn get_window(&self) -> &Window {\n        &self.window\n    }\n\n    fn configure_surface(&self) {\n        let surface_config = wgpu::SurfaceConfiguration {\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            format: self.surface_format,\n            // Request compatibility with the sRGB-format texture view we‘re going to create later.\n            view_formats: vec![self.surface_format.add_srgb_suffix()],\n            alpha_mode: wgpu::CompositeAlphaMode::Auto,\n            width: self.size.width,\n            height: self.size.height,\n            desired_maximum_frame_latency: 2,\n            present_mode: wgpu::PresentMode::AutoVsync,\n        };\n        self.surface.configure(&self.device, &surface_config);\n    }\n\n    fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {\n        self.size = new_size;\n\n        // reconfigure the surface\n        self.configure_surface();\n    }\n\n    fn render(&mut self) {\n        // Create texture view.\n        // NOTE: We must handle Timeout because the surface may be unavailable\n        // (e.g., when the window is occluded on macOS).\n        let surface_texture = match self.surface.get_current_texture() {\n            wgpu::CurrentSurfaceTexture::Success(texture) => texture,\n            wgpu::CurrentSurfaceTexture::Occluded | wgpu::CurrentSurfaceTexture::Timeout => return,\n            wgpu::CurrentSurfaceTexture::Suboptimal(_) | wgpu::CurrentSurfaceTexture::Outdated => {\n                self.configure_surface();\n                return;\n            }\n            wgpu::CurrentSurfaceTexture::Validation => {\n                unreachable!(\"No error scope registered, so validation errors will panic\")\n            }\n            wgpu::CurrentSurfaceTexture::Lost => {\n                self.surface = self.instance.create_surface(self.window.clone()).unwrap();\n                self.configure_surface();\n                return;\n            }\n        };\n        let texture_view = surface_texture\n            .texture\n            .create_view(&wgpu::TextureViewDescriptor {\n                // Without add_srgb_suffix() the image we will be working with\n                // might not be \"gamma correct\".\n                format: Some(self.surface_format.add_srgb_suffix()),\n                ..Default::default()\n            });\n\n        // Renders a GREEN screen\n        let mut encoder = self.device.create_command_encoder(&Default::default());\n        // Create the renderpass which will clear the screen.\n        let renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &texture_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        // If you wanted to call any drawing commands, they would go here.\n\n        // End the renderpass.\n        drop(renderpass);\n\n        // Submit the command in the queue to execute\n        self.queue.submit([encoder.finish()]);\n        self.window.pre_present_notify();\n        surface_texture.present();\n    }\n}\n\n#[derive(Default)]\nstruct App {\n    state: Option<State>,\n}\n\nimpl ApplicationHandler for App {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        // Create window object\n        let window = Arc::new(\n            event_loop\n                .create_window(Window::default_attributes())\n                .unwrap(),\n        );\n\n        let state = pollster::block_on(State::new(\n            event_loop.owned_display_handle(),\n            window.clone(),\n        ));\n        self.state = Some(state);\n\n        window.request_redraw();\n    }\n\n    fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {\n        let state = self.state.as_mut().unwrap();\n        match event {\n            WindowEvent::CloseRequested => {\n                println!(\"The close button was pressed; stopping\");\n                event_loop.exit();\n            }\n            WindowEvent::RedrawRequested => {\n                state.render();\n                // Emits a new redraw requested event.\n                state.get_window().request_redraw();\n            }\n            WindowEvent::Resized(size) => {\n                // Reconfigures the size of the surface. We do not re-render\n                // here as this event is always followed up by redraw request.\n                state.resize(size);\n            }\n            _ => (),\n        }\n    }\n}\n\nfn main() {\n    // wgpu uses `log` for all of our logging, so we initialize a logger with the `env_logger` crate.\n    //\n    // To change the log level, set the `RUST_LOG` environment variable. See the `env_logger`\n    // documentation for more information.\n    env_logger::init();\n\n    let event_loop = EventLoop::new().unwrap();\n\n    // When the current loop iteration finishes, immediately begin a new\n    // iteration regardless of whether or not new events are available to\n    // process. Preferred for applications that want to render as fast as\n    // possible, like games.\n    event_loop.set_control_flow(ControlFlow::Poll);\n\n    // When the current loop iteration finishes, suspend the thread until\n    // another event arrives. Helps keeping CPU utilization low if nothing\n    // is happening, which is preferred if the application might be idling in\n    // the background.\n    // event_loop.set_control_flow(ControlFlow::Wait);\n\n    let mut app = App::default();\n    event_loop.run_app(&mut app).unwrap();\n}\n"
  },
  {
    "path": "examples/standalone/custom_backend/Cargo.toml",
    "content": "[package]\nname = \"wgpu-example-custom-backend\"\nedition = \"2021\"\nrust-version = \"1.87\"\npublish = false\n\n[features]\ndefault = [\"web\"]\nweb = [\"wgpu/web\"]\n\n[dependencies]\nwgpu = { version = \"29.0.0\", features = [\n    \"custom\",\n    \"wgsl\",\n], default-features = false }\npollster = { version = \"0.4\", features = [\"macro\"] }\n"
  },
  {
    "path": "examples/standalone/custom_backend/src/custom.rs",
    "content": "#![allow(dead_code)]\nuse std::pin::Pin;\nuse std::sync::Arc;\n\nuse wgpu::custom::{\n    AdapterInterface, ComputePipelineInterface, DeviceInterface, DispatchAdapter, DispatchBlas,\n    DispatchDevice, DispatchQueue, DispatchShaderModule, DispatchSurface, InstanceInterface,\n    QueueInterface, RequestAdapterFuture, ShaderModuleInterface,\n};\n\n#[derive(Debug, Clone)]\npub struct Counter(Arc<()>);\n\nimpl Counter {\n    pub fn new() -> Self {\n        Self(Arc::new(()))\n    }\n\n    pub fn count(&self) -> usize {\n        Arc::strong_count(&self.0)\n    }\n}\n\n#[derive(Debug)]\npub struct CustomInstance(pub Counter);\n\nimpl InstanceInterface for CustomInstance {\n    fn new(__desc: wgpu::InstanceDescriptor) -> Self\n    where\n        Self: Sized,\n    {\n        Self(Counter::new())\n    }\n\n    unsafe fn create_surface(\n        &self,\n        _target: wgpu::SurfaceTargetUnsafe,\n    ) -> Result<DispatchSurface, wgpu::CreateSurfaceError> {\n        unimplemented!()\n    }\n\n    fn request_adapter(\n        &self,\n        _options: &wgpu::RequestAdapterOptions<'_, '_>,\n    ) -> std::pin::Pin<Box<dyn RequestAdapterFuture>> {\n        Box::pin(std::future::ready(Ok(DispatchAdapter::custom(\n            CustomAdapter(self.0.clone()),\n        ))))\n    }\n\n    fn poll_all_devices(&self, _force_wait: bool) -> bool {\n        unimplemented!()\n    }\n\n    fn wgsl_language_features(&self) -> wgpu::WgslLanguageFeatures {\n        unimplemented!()\n    }\n\n    fn enumerate_adapters(\n        &self,\n        _backends: wgpu::Backends,\n    ) -> Pin<Box<dyn wgpu::custom::EnumerateAdapterFuture>> {\n        unimplemented!()\n    }\n}\n\n#[derive(Debug)]\nstruct CustomAdapter(Counter);\n\nimpl AdapterInterface for CustomAdapter {\n    fn request_device(\n        &self,\n        desc: &wgpu::DeviceDescriptor<'_>,\n    ) -> Pin<Box<dyn wgpu::custom::RequestDeviceFuture>> {\n        assert_eq!(desc.label, Some(\"device\"));\n        let res: Result<_, wgpu::RequestDeviceError> = Ok((\n            DispatchDevice::custom(CustomDevice(self.0.clone())),\n            DispatchQueue::custom(CustomQueue(self.0.clone())),\n        ));\n        Box::pin(std::future::ready(res))\n    }\n\n    fn cooperative_matrix_properties(&self) -> Vec<wgpu::CooperativeMatrixProperties> {\n        Vec::new()\n    }\n\n    fn is_surface_supported(&self, _surface: &DispatchSurface) -> bool {\n        unimplemented!()\n    }\n\n    fn features(&self) -> wgpu::Features {\n        unimplemented!()\n    }\n\n    fn limits(&self) -> wgpu::Limits {\n        unimplemented!()\n    }\n\n    fn downlevel_capabilities(&self) -> wgpu::DownlevelCapabilities {\n        unimplemented!()\n    }\n\n    fn get_info(&self) -> wgpu::AdapterInfo {\n        unimplemented!()\n    }\n\n    fn get_texture_format_features(\n        &self,\n        _format: wgpu::TextureFormat,\n    ) -> wgpu::TextureFormatFeatures {\n        unimplemented!()\n    }\n\n    fn get_presentation_timestamp(&self) -> wgpu::PresentationTimestamp {\n        unimplemented!()\n    }\n}\n\n#[derive(Debug)]\nstruct CustomDevice(Counter);\n\nimpl DeviceInterface for CustomDevice {\n    fn features(&self) -> wgpu::Features {\n        unimplemented!()\n    }\n\n    fn limits(&self) -> wgpu::Limits {\n        unimplemented!()\n    }\n\n    fn adapter_info(&self) -> wgpu::AdapterInfo {\n        unimplemented!()\n    }\n\n    fn create_shader_module(\n        &self,\n        desc: wgpu::ShaderModuleDescriptor<'_>,\n        _shader_bound_checks: wgpu::ShaderRuntimeChecks,\n    ) -> DispatchShaderModule {\n        assert_eq!(desc.label, Some(\"shader\"));\n        DispatchShaderModule::custom(CustomShaderModule(self.0.clone()))\n    }\n\n    unsafe fn create_shader_module_passthrough(\n        &self,\n        _desc: &wgpu::ShaderModuleDescriptorPassthrough<'_>,\n    ) -> DispatchShaderModule {\n        unimplemented!()\n    }\n\n    fn create_bind_group_layout(\n        &self,\n        _desc: &wgpu::BindGroupLayoutDescriptor<'_>,\n    ) -> wgpu::custom::DispatchBindGroupLayout {\n        unimplemented!()\n    }\n\n    fn create_bind_group(\n        &self,\n        _desc: &wgpu::BindGroupDescriptor<'_>,\n    ) -> wgpu::custom::DispatchBindGroup {\n        unimplemented!()\n    }\n\n    fn create_pipeline_layout(\n        &self,\n        _desc: &wgpu::PipelineLayoutDescriptor<'_>,\n    ) -> wgpu::custom::DispatchPipelineLayout {\n        unimplemented!()\n    }\n\n    fn create_render_pipeline(\n        &self,\n        _desc: &wgpu::RenderPipelineDescriptor<'_>,\n    ) -> wgpu::custom::DispatchRenderPipeline {\n        unimplemented!()\n    }\n\n    fn create_mesh_pipeline(\n        &self,\n        _desc: &wgpu::MeshPipelineDescriptor<'_>,\n    ) -> wgpu::custom::DispatchRenderPipeline {\n        unimplemented!()\n    }\n\n    fn create_compute_pipeline(\n        &self,\n        desc: &wgpu::ComputePipelineDescriptor<'_>,\n    ) -> wgpu::custom::DispatchComputePipeline {\n        let module = desc.module.as_custom::<CustomShaderModule>().unwrap();\n        wgpu::custom::DispatchComputePipeline::custom(CustomComputePipeline(module.0.clone()))\n    }\n\n    unsafe fn create_pipeline_cache(\n        &self,\n        _desc: &wgpu::PipelineCacheDescriptor<'_>,\n    ) -> wgpu::custom::DispatchPipelineCache {\n        unimplemented!()\n    }\n\n    fn create_buffer(&self, _desc: &wgpu::BufferDescriptor<'_>) -> wgpu::custom::DispatchBuffer {\n        unimplemented!()\n    }\n\n    fn create_texture(&self, _desc: &wgpu::TextureDescriptor<'_>) -> wgpu::custom::DispatchTexture {\n        unimplemented!()\n    }\n\n    fn create_external_texture(\n        &self,\n        _desc: &wgpu::ExternalTextureDescriptor<'_>,\n        _planes: &[&wgpu::TextureView],\n    ) -> wgpu::custom::DispatchExternalTexture {\n        unimplemented!()\n    }\n\n    fn create_blas(\n        &self,\n        _desc: &wgpu::CreateBlasDescriptor<'_>,\n        _sizes: wgpu::BlasGeometrySizeDescriptors,\n    ) -> (Option<u64>, wgpu::custom::DispatchBlas) {\n        unimplemented!()\n    }\n\n    fn create_tlas(&self, _desc: &wgpu::CreateTlasDescriptor<'_>) -> wgpu::custom::DispatchTlas {\n        unimplemented!()\n    }\n\n    fn create_sampler(&self, _desc: &wgpu::SamplerDescriptor<'_>) -> wgpu::custom::DispatchSampler {\n        unimplemented!()\n    }\n\n    fn create_query_set(\n        &self,\n        _desc: &wgpu::QuerySetDescriptor<'_>,\n    ) -> wgpu::custom::DispatchQuerySet {\n        unimplemented!()\n    }\n\n    fn create_command_encoder(\n        &self,\n        _desc: &wgpu::CommandEncoderDescriptor<'_>,\n    ) -> wgpu::custom::DispatchCommandEncoder {\n        unimplemented!()\n    }\n\n    fn create_render_bundle_encoder(\n        &self,\n        _desc: &wgpu::RenderBundleEncoderDescriptor<'_>,\n    ) -> wgpu::custom::DispatchRenderBundleEncoder {\n        unimplemented!()\n    }\n\n    fn set_device_lost_callback(&self, _device_lost_callback: wgpu::custom::BoxDeviceLostCallback) {\n        unimplemented!()\n    }\n\n    fn on_uncaptured_error(&self, _handler: Arc<dyn wgpu::UncapturedErrorHandler>) {\n        unimplemented!()\n    }\n\n    fn push_error_scope(&self, _filter: wgpu::ErrorFilter) -> u32 {\n        unimplemented!()\n    }\n\n    fn pop_error_scope(&self, _index: u32) -> Pin<Box<dyn wgpu::custom::PopErrorScopeFuture>> {\n        unimplemented!()\n    }\n\n    unsafe fn start_graphics_debugger_capture(&self) {\n        unimplemented!()\n    }\n\n    unsafe fn stop_graphics_debugger_capture(&self) {\n        unimplemented!()\n    }\n\n    fn poll(\n        &self,\n        _maintain: wgpu::wgt::PollType<u64>,\n    ) -> Result<wgpu::PollStatus, wgpu::PollError> {\n        unimplemented!()\n    }\n\n    fn get_internal_counters(&self) -> wgpu::InternalCounters {\n        unimplemented!()\n    }\n\n    fn generate_allocator_report(&self) -> Option<wgpu::AllocatorReport> {\n        unimplemented!()\n    }\n\n    fn destroy(&self) {\n        unimplemented!()\n    }\n}\n\n#[derive(Debug)]\npub struct CustomShaderModule(pub Counter);\n\nimpl ShaderModuleInterface for CustomShaderModule {\n    fn get_compilation_info(&self) -> Pin<Box<dyn wgpu::custom::ShaderCompilationInfoFuture>> {\n        unimplemented!()\n    }\n}\n\n#[derive(Debug)]\nstruct CustomQueue(Counter);\n\nimpl QueueInterface for CustomQueue {\n    fn write_buffer(\n        &self,\n        _buffer: &wgpu::custom::DispatchBuffer,\n        _offset: wgpu::BufferAddress,\n        _data: &[u8],\n    ) {\n        unimplemented!()\n    }\n\n    fn create_staging_buffer(\n        &self,\n        _size: wgpu::BufferSize,\n    ) -> Option<wgpu::custom::DispatchQueueWriteBuffer> {\n        unimplemented!()\n    }\n\n    fn validate_write_buffer(\n        &self,\n        _buffer: &wgpu::custom::DispatchBuffer,\n        _offset: wgpu::BufferAddress,\n        _size: wgpu::BufferSize,\n    ) -> Option<()> {\n        unimplemented!()\n    }\n\n    fn write_staging_buffer(\n        &self,\n        _buffer: &wgpu::custom::DispatchBuffer,\n        _offset: wgpu::BufferAddress,\n        _staging_buffer: &wgpu::custom::DispatchQueueWriteBuffer,\n    ) {\n        unimplemented!()\n    }\n\n    fn write_texture(\n        &self,\n        _texture: wgpu::TexelCopyTextureInfo<'_>,\n        _data: &[u8],\n        _data_layout: wgpu::TexelCopyBufferLayout,\n        _size: wgpu::Extent3d,\n    ) {\n        unimplemented!()\n    }\n\n    fn submit(\n        &self,\n        _command_buffers: &mut dyn Iterator<Item = wgpu::custom::DispatchCommandBuffer>,\n    ) -> u64 {\n        unimplemented!()\n    }\n\n    fn get_timestamp_period(&self) -> f32 {\n        unimplemented!()\n    }\n\n    fn on_submitted_work_done(&self, _callback: wgpu::custom::BoxSubmittedWorkDoneCallback) {\n        unimplemented!()\n    }\n\n    #[cfg(all(target_arch = \"wasm32\", feature = \"web\"))]\n    fn copy_external_image_to_texture(\n        &self,\n        _source: &wgpu::CopyExternalImageSourceInfo,\n        _dest: wgpu::CopyExternalImageDestInfo<&wgpu::Texture>,\n        _size: wgpu::Extent3d,\n    ) {\n        unimplemented!()\n    }\n\n    fn compact_blas(&self, _blas: &DispatchBlas) -> (Option<u64>, DispatchBlas) {\n        unimplemented!()\n    }\n}\n\n#[derive(Debug)]\npub struct CustomComputePipeline(pub Counter);\n\nimpl ComputePipelineInterface for CustomComputePipeline {\n    fn get_bind_group_layout(&self, _index: u32) -> wgpu::custom::DispatchBindGroupLayout {\n        unimplemented!()\n    }\n}\n"
  },
  {
    "path": "examples/standalone/custom_backend/src/main.rs",
    "content": "use std::marker::PhantomData;\n\nuse custom::{Counter, CustomShaderModule};\nuse wgpu::{DeviceDescriptor, RequestAdapterOptions};\n\nmod custom;\n\n#[pollster::main]\nasync fn main() {\n    let counter = Counter::new();\n    {\n        let custom_instance = custom::CustomInstance(counter.clone());\n        // wrap custom instance into wgpu abstraction\n        let instance = wgpu::Instance::from_custom(custom_instance);\n        assert_eq!(counter.count(), 2);\n        // do work on instance (usually by passing it to other libs)\n\n        // here we will simulate a library and ensure that counter is incremented\n        let adapter = instance\n            .request_adapter(&RequestAdapterOptions::default())\n            .await\n            .unwrap();\n        assert_eq!(counter.count(), 3);\n\n        let (device, _queue) = adapter\n            .request_device(&DeviceDescriptor {\n                label: Some(\"device\"),\n                ..Default::default()\n            })\n            .await\n            .unwrap();\n        assert_eq!(counter.count(), 5);\n\n        let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"shader\"),\n            source: wgpu::ShaderSource::Dummy(PhantomData),\n        });\n\n        let custom_module = module.as_custom::<CustomShaderModule>().unwrap();\n        assert_eq!(custom_module.0.count(), 6);\n        let _module_clone = module.clone();\n        assert_eq!(counter.count(), 6);\n\n        let _pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        assert_eq!(counter.count(), 7);\n    }\n    assert_eq!(counter.count(), 1);\n}\n"
  },
  {
    "path": "lock-analyzer/Cargo.toml",
    "content": "[package]\nname = \"lock-analyzer\"\nedition.workspace = true\nrust-version.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nhomepage.workspace = true\nrepository.workspace = true\nversion.workspace = true\nauthors.workspace = true\npublish = false\n\n[dependencies]\nron.workspace = true\nanyhow.workspace = true\n\n[dependencies.serde]\nworkspace = true\nfeatures = [\"default\", \"serde_derive\"]\n\n[lints.clippy]\ndisallowed_types = \"allow\"\n"
  },
  {
    "path": "lock-analyzer/src/main.rs",
    "content": "//! Analyzer for data produced by `wgpu-core`'s `observe_locks` feature.\n//!\n//! When `wgpu-core`'s `observe_locks` feature is enabled, if the\n//! `WGPU_CORE_LOCK_OBSERVE_DIR` environment variable is set to the\n//! path of an existing directory, then every thread that acquires a\n//! lock in `wgpu-core` will write its own log file to that directory.\n//! You can then run this program to read those files and summarize\n//! the results.\n//!\n//! This program also consults the `WGPU_CORE_LOCK_OBSERVE_DIR`\n//! environment variable to find the log files written by `wgpu-core`.\n//!\n//! See `wgpu_core/src/lock/observing.rs` for a general explanation of\n//! this analysis.\n\nuse std::sync::Arc;\nuse std::{\n    collections::{btree_map::Entry, BTreeMap, BTreeSet, HashMap},\n    fmt,\n    path::PathBuf,\n};\n\nuse anyhow::{Context, Result};\n\nfn main() -> Result<()> {\n    let mut ranks: BTreeMap<u32, Rank> = BTreeMap::default();\n\n    let Ok(dir) = std::env::var(\"WGPU_CORE_LOCK_OBSERVE_DIR\") else {\n        eprintln!(concat!(\n            \"Please set the `WGPU_CORE_LOCK_OBSERVE_DIR` environment variable\\n\",\n            \"to the path of the directory containing the files written by\\n\",\n            \"`wgpu-core`'s `observe_locks` feature.\"\n        ));\n        anyhow::bail!(\"`WGPU_CORE_LOCK_OBSERVE_DIR` environment variable is not set\");\n    };\n    let entries =\n        std::fs::read_dir(&dir).with_context(|| format!(\"failed to read directory {dir}\"))?;\n    for entry in entries {\n        let entry = entry.with_context(|| format!(\"failed to read directory entry from {dir}\"))?;\n        let name = PathBuf::from(&entry.file_name());\n        let Some(extension) = name.extension() else {\n            eprintln!(\"Ignoring {}\", name.display());\n            continue;\n        };\n        if extension != \"ron\" {\n            eprintln!(\"Ignoring {}\", name.display());\n            continue;\n        }\n\n        let contents = std::fs::read(entry.path())\n            .with_context(|| format!(\"failed to read lock observations from {}\", name.display()))?;\n        // The addresses of `&'static Location<'static>` values could\n        // vary from run to run.\n        let mut locations: HashMap<u64, Arc<Location>> = HashMap::default();\n        for line in contents.split(|&b| b == b'\\n') {\n            if line.is_empty() {\n                continue;\n            }\n            let action = ron::de::from_bytes::<Action>(line)\n                .with_context(|| format!(\"Error parsing action from {}\", name.display()))?;\n            match action {\n                Action::Location {\n                    address,\n                    file,\n                    line,\n                    column,\n                } => {\n                    let file = match file.split_once(\"src/\") {\n                        Some((_, after)) => after.to_string(),\n                        None => file,\n                    };\n                    assert!(locations\n                        .insert(address, Arc::new(Location { file, line, column }))\n                        .is_none());\n                }\n                Action::Rank {\n                    bit,\n                    member_name,\n                    const_name,\n                } => match ranks.entry(bit) {\n                    Entry::Occupied(occupied) => {\n                        let rank = occupied.get();\n                        assert_eq!(rank.member_name, member_name);\n                        assert_eq!(rank.const_name, const_name);\n                    }\n                    Entry::Vacant(vacant) => {\n                        vacant.insert(Rank {\n                            member_name,\n                            const_name,\n                            acquisitions: BTreeMap::default(),\n                        });\n                    }\n                },\n                Action::Acquisition {\n                    older_rank,\n                    older_location,\n                    newer_rank,\n                    newer_location,\n                } => {\n                    let older_location = locations[&older_location].clone();\n                    let newer_location = locations[&newer_location].clone();\n                    ranks\n                        .get_mut(&older_rank)\n                        .unwrap()\n                        .acquisitions\n                        .entry(newer_rank)\n                        .or_default()\n                        .entry(older_location)\n                        .or_default()\n                        .insert(newer_location);\n                }\n            }\n        }\n    }\n\n    for older_rank in ranks.values() {\n        if older_rank.is_leaf() {\n            // We'll print leaf locks separately, below.\n            continue;\n        }\n        println!(\n            \"    rank {} {:?} followed by {{\",\n            older_rank.const_name, older_rank.member_name\n        );\n        let mut acquired_any_leaf_locks = false;\n        let mut first_newer = true;\n        for (newer_rank, locations) in &older_rank.acquisitions {\n            // List acquisitions of leaf locks at the end.\n            if ranks[newer_rank].is_leaf() {\n                acquired_any_leaf_locks = true;\n                continue;\n            }\n            if !first_newer {\n                println!();\n            }\n            for (older_location, newer_locations) in locations {\n                if newer_locations.len() == 1 {\n                    for newer_loc in newer_locations {\n                        println!(\"        // holding {older_location} while locking {newer_loc}\");\n                    }\n                } else {\n                    println!(\"        // holding {older_location} while locking:\");\n                    for newer_loc in newer_locations {\n                        println!(\"        //     {newer_loc}\");\n                    }\n                }\n            }\n            println!(\"        {},\", ranks[newer_rank].const_name);\n            first_newer = false;\n        }\n\n        if acquired_any_leaf_locks {\n            // We checked that older_rank isn't a leaf lock, so we\n            // must have printed something above.\n            if !first_newer {\n                println!();\n            }\n            println!(\"        // leaf lock acquisitions:\");\n            for newer_rank in older_rank.acquisitions.keys() {\n                if !ranks[newer_rank].is_leaf() {\n                    continue;\n                }\n                println!(\"        {},\", ranks[newer_rank].const_name);\n            }\n        }\n        println!(\"    }};\");\n        println!();\n    }\n\n    for older_rank in ranks.values() {\n        if !older_rank.is_leaf() {\n            continue;\n        }\n\n        println!(\n            \"    rank {} {:?} followed by {{ }};\",\n            older_rank.const_name, older_rank.member_name\n        );\n    }\n\n    Ok(())\n}\n\n#[derive(Debug, serde::Deserialize)]\n#[serde(deny_unknown_fields)]\nenum Action {\n    /// A location that we will refer to in later actions.\n    Location {\n        address: LocationAddress,\n        file: String,\n        line: u32,\n        column: u32,\n    },\n\n    /// A lock rank that we will refer to in later actions.\n    Rank {\n        bit: u32,\n        member_name: String,\n        const_name: String,\n    },\n\n    /// An attempt to acquire a lock while holding another lock.\n    Acquisition {\n        /// The number of the already acquired lock's rank.\n        older_rank: u32,\n\n        /// The source position at which we acquired it. Specifically,\n        /// its `Location`'s address, as an integer.\n        older_location: LocationAddress,\n\n        /// The number of the rank of the lock we are acquiring.\n        newer_rank: u32,\n\n        /// The source position at which we are acquiring it.\n        /// Specifically, its `Location`'s address, as an integer.\n        newer_location: LocationAddress,\n    },\n}\n\n/// The memory address at which the `Location` was stored in the\n/// observed process.\n///\n/// This is not `usize` because it does not represent an address in\n/// this `lock-analyzer` process. We might generate logs on a 64-bit\n/// machine and analyze them on a 32-bit machine. The `u64` type is a\n/// reasonable universal type for addresses on any machine.\ntype LocationAddress = u64;\n\nstruct Rank {\n    member_name: String,\n    const_name: String,\n    acquisitions: BTreeMap<u32, LocationSet>,\n}\n\nimpl Rank {\n    fn is_leaf(&self) -> bool {\n        self.acquisitions.is_empty()\n    }\n}\n\ntype LocationSet = BTreeMap<Arc<Location>, BTreeSet<Arc<Location>>>;\n\n#[derive(Eq, Ord, PartialEq, PartialOrd)]\nstruct Location {\n    file: String,\n    line: u32,\n    column: u32,\n}\n\nimpl fmt::Display for Location {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}:{}\", self.file, self.line)\n    }\n}\n"
  },
  {
    "path": "naga/.cargo/config.toml",
    "content": "[alias]\nxtask = \"run -p naga-xtask --\"\n"
  },
  {
    "path": "naga/.gitattributes",
    "content": "tests/naga/out/**/* text eol=lf\n"
  },
  {
    "path": "naga/.gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\n.DS_Store\n.fuse_hidden*\n.idea\n.vscode\n*.swp\n/*.dot\n/*.metal\n/*.metallib\n/*.ron\n/*.spv\n/*.vert\n/*.frag\n/*.comp\n/*.wgsl\n/*.hlsl\n/*.txt\n"
  },
  {
    "path": "naga/CHANGELOG.md",
    "content": "# Change Log\n\nFor changelogs after v0.14, see [the wgpu changelog](../CHANGELOG.md).\n\n## v0.14 (2023-10-25)\n\n#### GENERAL\n\n- Add support for const-expressions. ([#2309](https://github.com/gfx-rs/naga/pull/2309)) **@teoxoy**, **@jimblandy**\n- Add support for the `rgb10a2uint` storage format. ([#2525](https://github.com/gfx-rs/naga/pull/2525)) **@teoxoy**\n- Implement module compaction for snapshot testing and the CLI. ([#2472](https://github.com/gfx-rs/naga/pull/2472)) **@jimblandy**\n- Fix validation and GLSL parsing of `ldexp`. ([#2449](https://github.com/gfx-rs/naga/pull/2449)) **@fornwall**\n- Add support for dual source blending. ([#2427](https://github.com/gfx-rs/naga/pull/2427)) **@freqmod**\n- Bump `indexmap` to v2. ([#2426](https://github.com/gfx-rs/naga/pull/2426)) **@daxpedda**\n- Bump MSRV to 1.65. ([#2420](https://github.com/gfx-rs/naga/pull/2420)) **@jimblandy**\n\n#### API\n\n- Split `UnaryOperator::Not` into `UnaryOperator::LogicalNot` & `UnaryOperator::BitwiseNot`. ([#2554](https://github.com/gfx-rs/naga/pull/2554)) **@teoxoy**\n- Remove `IsFinite` & `IsNormal` relational functions. ([#2532](https://github.com/gfx-rs/naga/pull/2532)) **@teoxoy**\n- Derive `PartialEq` on `Expression`. ([#2417](https://github.com/gfx-rs/naga/pull/2417)) **@robtfm**\n- Use `FastIndexMap` for `SpecialTypes::predeclared_types`. ([#2495](https://github.com/gfx-rs/naga/pull/2495)) **@jimblandy**\n\n#### CLI\n\n- Change `--generate-debug-symbols` from an `option` to a `switch`. ([#2472](https://github.com/gfx-rs/naga/pull/2472)) **@jimblandy**\n- Add support for `.{vert,frag,comp}.glsl` files. ([#2462](https://github.com/gfx-rs/naga/pull/2462)) **@eliemichel**\n\n#### VALIDATOR\n\n- Require `Capabilities::FLOAT64` for 64-bit floating-point literals. ([#2567](https://github.com/gfx-rs/naga/pull/2567)) **@jimblandy**\n- Add `Capabilities::CUBE_ARRAY_TEXTURES`. ([#2530](https://github.com/gfx-rs/naga/pull/2530)) **@teoxoy**\n- Disallow passing pointers to variables in the workgroup address space to functions. ([#2507](https://github.com/gfx-rs/naga/pull/2507)) **@teoxoy**\n- Avoid OOM with large sparse resource bindings. ([#2561](https://github.com/gfx-rs/naga/pull/2561)) **@teoxoy**\n- Require that `Function` and `Private` variables be `CONSTRUCTIBLE`. ([#2545](https://github.com/gfx-rs/naga/pull/2545)) **@jimblandy**\n- Disallow floating-point NaNs and infinities. ([#2508](https://github.com/gfx-rs/naga/pull/2508)) **@teoxoy**\n- Temporarily disable uniformity analysis for the fragment stage. ([#2515](https://github.com/gfx-rs/naga/pull/2515)) **@teoxoy**\n- Validate that `textureSampleBias` is only used in the fragment stage. ([#2515](https://github.com/gfx-rs/naga/pull/2515)) **@teoxoy**\n- Validate variable initializer for address spaces. ([#2513](https://github.com/gfx-rs/naga/pull/2513)) **@teoxoy**\n- Prevent using multiple push constant variables in one entry point. ([#2484](https://github.com/gfx-rs/naga/pull/2484)) **@andriyDev**\n- Validate `binding_array` variable address space. ([#2422](https://github.com/gfx-rs/naga/pull/2422)) **@teoxoy**\n- Validate storage buffer access. ([#2415](https://github.com/gfx-rs/naga/pull/2415)) **@teoxoy**\n\n#### WGSL-IN\n\n- Fix expected min arg count of `textureLoad`. ([#2584](https://github.com/gfx-rs/naga/pull/2584)) **@teoxoy**\n- Turn `Error::Other` into `Error::Internal`, to help devs. ([#2574](https://github.com/gfx-rs/naga/pull/2574)) **@jimblandy**\n- Fix OOB typifier indexing. ([#2570](https://github.com/gfx-rs/naga/pull/2570)) **@teoxoy**\n- Add support for the `bgra8unorm` storage format. ([#2542](https://github.com/gfx-rs/naga/pull/2542) & [#2550](https://github.com/gfx-rs/naga/pull/2550)) **@nical**\n- Remove the `outerProduct` built-in function. ([#2535](https://github.com/gfx-rs/naga/pull/2535)) **@teoxoy**\n- Add support for `i32` overload of the `sign` built-in function. ([#2463](https://github.com/gfx-rs/naga/pull/2463)) **@fornwall**\n- Properly implement `modf` and `frexp`. ([#2454](https://github.com/gfx-rs/naga/pull/2454)) **@fornwall**\n- Add support for scalar overloads of `all` & `any` built-in functions. ([#2445](https://github.com/gfx-rs/naga/pull/2445)) **@fornwall**\n- Don't splat the left hand operand of a binary operation if it's not a scalar. ([#2444](https://github.com/gfx-rs/naga/pull/2444)) **@fornwall**\n- Avoid splatting all binary operator expressions. ([#2440](https://github.com/gfx-rs/naga/pull/2440)) **@fornwall**\n- Error on repeated or missing `@workgroup_size()`. ([#2435](https://github.com/gfx-rs/naga/pull/2435)) **@fornwall**\n- Error on repeated attributes. ([#2428](https://github.com/gfx-rs/naga/pull/2428)) **@fornwall**\n- Fix error message for invalid `texture{Load,Store}()` on arrayed textures. ([#2432](https://github.com/gfx-rs/naga/pull/2432)) **@fornwall**\n\n#### SPV-IN\n\n- Disable `Modf` & `Frexp` and translate `ModfStruct` & `FrexpStruct` to their IR equivalents. ([#2527](https://github.com/gfx-rs/naga/pull/2527)) **@teoxoy**\n- Don't advertise support for `Capability::ImageMSArray` & `Capability::InterpolationFunction`. ([#2529](https://github.com/gfx-rs/naga/pull/2529)) **@teoxoy**\n- Fix `OpImageQueries` to allow Uints. ([#2404](https://github.com/gfx-rs/naga/pull/2404)) **@evahop**\n\n#### GLSL-IN\n\n- Disable `modf` & `frexp`. ([#2527](https://github.com/gfx-rs/naga/pull/2527)) **@teoxoy**\n\n#### SPV-OUT\n\n- Require `ClipDistance` & `CullDistance` capabilities if necessary. ([#2528](https://github.com/gfx-rs/naga/pull/2528)) **@teoxoy**\n- Change `naga::back::spv::DebugInfo::file_name` to a `&Path`. ([#2501](https://github.com/gfx-rs/naga/pull/2501)) **@jimblandy**\n- Always give structs with runtime arrays a `Block` decoration. ([#2455](https://github.com/gfx-rs/naga/pull/2455)) **@TheoDulka**\n- Decorate the result of the `OpLoad` with `NonUniform` (not the access chain) when loading images/samplers (resources in the Handle address space). ([#2422](https://github.com/gfx-rs/naga/pull/2422)) **@teoxoy**\n- Cache `OpConstantNull`. ([#2414](https://github.com/gfx-rs/naga/pull/2414)) **@evahop**\n\n#### MSL-OUT\n\n- Add and fix minimum Metal version checks for optional functionality. ([#2486](https://github.com/gfx-rs/naga/pull/2486)) **@teoxoy**\n- Make varyings' struct members unique. ([#2521](https://github.com/gfx-rs/naga/pull/2521)) **@evahop**\n- Add experimental vertex pulling transform flag. ([#5254](https://github.com/gfx-rs/wgpu/pull/5254)) **@bradwerth**\n- Fixup some generated MSL for vertex buffer unpack functions. ([#5829](https://github.com/gfx-rs/wgpu/pull/5829)) **@bradwerth**\n- Make vertex pulling transform on by default. ([#5773](https://github.com/gfx-rs/wgpu/pull/5773)) **@bradwerth**\n\n#### GLSL-OUT\n\n- Cull functions that should not be available for a given stage. ([#2531](https://github.com/gfx-rs/naga/pull/2531)) **@teoxoy**\n- Rename identifiers containing double underscores. ([#2510](https://github.com/gfx-rs/naga/pull/2510)) **@evahop**\n- Polyfill `frexp`. ([#2504](https://github.com/gfx-rs/naga/pull/2504)) **@evahop**\n- Add built-in functions to keywords. ([#2410](https://github.com/gfx-rs/naga/pull/2410)) **@fornwall**\n\n#### WGSL-OUT\n\n- Generate correct code for bit complement on integers. ([#2548](https://github.com/gfx-rs/naga/pull/2548)) **@jimblandy**\n- Don't include type parameter in splat expressions. ([#2469](https://github.com/gfx-rs/naga/pull/2469)) **@jimblandy**\n\n## v0.13 (2023-07-21)\n\n#### GENERAL\n\n- Move from `make` to `cargo xtask` workflows. ([#2297](https://github.com/gfx-rs/naga/pull/2297)) **@ErichDonGubler**\n- Omit non referenced expressions from output. ([#2378](https://github.com/gfx-rs/naga/pull/2378)) **@teoxoy**\n- Bump `bitflags` to v2. ([#2358](https://github.com/gfx-rs/naga/pull/2358)) **@daxpedda**\n- Implement `workgroupUniformLoad`. ([#2201](https://github.com/gfx-rs/naga/pull/2201)) **@DJMcNab**\n\n#### API\n\n- Expose early depth test field. ([#2393](https://github.com/gfx-rs/naga/pull/2393)) **@Joeoc2001**\n- Split image bounds check policy. ([#2265](https://github.com/gfx-rs/naga/pull/2265)) **@teoxoy**\n- Change type of constant sized arrays to `NonZeroU32`. ([#2337](https://github.com/gfx-rs/naga/pull/2337)) **@teoxoy**\n- Introduce `GlobalCtx`. ([#2335](https://github.com/gfx-rs/naga/pull/2335)) **@teoxoy**\n- Introduce `Expression::Literal`. ([#2333](https://github.com/gfx-rs/naga/pull/2333)) **@teoxoy**\n- Introduce `Expression::ZeroValue`. ([#2332](https://github.com/gfx-rs/naga/pull/2332)) **@teoxoy**\n- Add support for const-expressions (only at the API level, functionality is still WIP). ([#2266](https://github.com/gfx-rs/naga/pull/2266)) **@teoxoy**, **@jimblandy**\n\n#### DOCS\n\n- Document which expressions are in scope for a `break_if` expression. ([#2326](https://github.com/gfx-rs/naga/pull/2326)) **@jimblandy**\n\n#### VALIDATOR\n\n- Don't `use std::opsIndex`, used only when `\"validate\"` is on. ([#2383](https://github.com/gfx-rs/naga/pull/2383)) **@jimblandy**\n- Remove unneeded `ConstantError::Unresolved{Component,Size}`. ([#2330](https://github.com/gfx-rs/naga/pull/2330)) **@ErichDonGubler**\n- Remove `TypeError::UnresolvedBase`. ([#2308](https://github.com/gfx-rs/naga/pull/2308)) **@ErichDonGubler**\n\n#### WGSL-IN\n\n- Error on param redefinition. ([#2342](https://github.com/gfx-rs/naga/pull/2342)) **@SparkyPotato**\n\n#### SPV-IN\n\n- Improve documentation for SPIR-V control flow parsing. ([#2324](https://github.com/gfx-rs/naga/pull/2324)) **@jimblandy**\n- Obey the `is_depth` field of `OpTypeImage`. ([#2341](https://github.com/gfx-rs/naga/pull/2341)) **@expenses**\n- Convert conditional backedges to `break if`. ([#2290](https://github.com/gfx-rs/naga/pull/2290)) **@eddyb**\n\n#### GLSL-IN\n\n- Support commas in structure definitions. ([#2400](https://github.com/gfx-rs/naga/pull/2400)) **@fornwall**\n\n#### SPV-OUT\n\n- Add debug info. ([#2379](https://github.com/gfx-rs/naga/pull/2379)) **@wicast**\n- Use `IndexSet` instead of `HashSet` for iterated sets (capabilities/extensions). ([#2389](https://github.com/gfx-rs/naga/pull/2389)) **@eddyb**\n- Support array bindings of buffers. ([#2282](https://github.com/gfx-rs/naga/pull/2282)) **@kvark**\n\n#### MSL-OUT\n\n- Rename `allow_point_size` to `allow_and_force_point_size`. ([#2280](https://github.com/gfx-rs/naga/pull/2280)) **@teoxoy**\n- Initialize arrays inline. ([#2331](https://github.com/gfx-rs/naga/pull/2331)) **@teoxoy**\n\n#### HLSL-OUT\n\n- Implement Pack/Unpack for HLSL. ([#2353](https://github.com/gfx-rs/naga/pull/2353)) **@Elabajaba**\n- Complete HLSL reserved symbols. ([#2367](https://github.com/gfx-rs/naga/pull/2367)) **@teoxoy**\n- Handle case insensitive FXC keywords. ([#2347](https://github.com/gfx-rs/naga/pull/2347)) **@PJB3005**\n- Fix return type for firstbitlow/high. ([#2315](https://github.com/gfx-rs/naga/pull/2315)) **@evahop**\n\n#### GLSL-OUT\n\n- `textureSize` level must be a signed integer. ([#2397](https://github.com/gfx-rs/naga/pull/2397)) **@nical**\n- Fix functions with array return type. ([#2382](https://github.com/gfx-rs/naga/pull/2382)) **@Gordon-F**\n\n#### WGSL-OUT\n\n- Output `@interpolate(flat)` attribute for integer locations. ([#2318](https://github.com/gfx-rs/naga/pull/2318)) **@expenses**\n\n## v0.12.3 (2023-07-09)\n\n#### WGSL-OUT\n\n- (Backport) Output `@interpolate(flat)` attribute for integer locations. ([#2318](https://github.com/gfx-rs/naga/pull/2318)) **@expenses**\n\n## v0.12.2 (2023-05-30)\n\n#### SPV-OUT\n\n- (Backport) Support array bindings of buffers. ([#2282](https://github.com/gfx-rs/naga/pull/2282)) **@kvark**\n\n## v0.12.1 (2023-05-18)\n\n#### SPV-IN\n\n- (Backport) Convert conditional backedges to `break if`. ([#2290](https://github.com/gfx-rs/naga/pull/2290)) **@eddyb**\n\n## v0.12 (2023-04-19)\n\n#### GENERAL\n\n- Allow `array_index` to be unsigned. ([#2298](https://github.com/gfx-rs/naga/pull/2298)) **@daxpedda**\n- Add ray query support. ([#2256](https://github.com/gfx-rs/naga/pull/2256)) **@kvark**\n- Add partial derivative builtins. ([#2277](https://github.com/gfx-rs/naga/pull/2277)) **@evahop**\n- Skip `gl_PerVertex` unused builtins in the SPIR-V frontend. ([#2272](https://github.com/gfx-rs/naga/pull/2272)) **@teoxoy**\n- Differentiate between `i32` and `u32` in switch statement cases. ([#2269](https://github.com/gfx-rs/naga/pull/2269)) **@evahop**\n- Fix zero initialization of workgroup memory. ([#2259](https://github.com/gfx-rs/naga/pull/2259)) **@teoxoy**\n- Add `countTrailingZeros`. ([#2243](https://github.com/gfx-rs/naga/pull/2243)) **@gents83**\n- Fix texture built-ins where u32 was expected. ([#2245](https://github.com/gfx-rs/naga/pull/2245)) **@evahop**\n- Add `countLeadingZeros`. ([#2226](https://github.com/gfx-rs/naga/pull/2226)) **@evahop**\n- [glsl/hlsl-out] Write sizes of arrays behind pointers in function arguments. ([#2250](https://github.com/gfx-rs/naga/pull/2250)) **@pluiedev**\n\n#### VALIDATOR\n\n- Validate vertex stage returns the position built-in. ([#2264](https://github.com/gfx-rs/naga/pull/2264)) **@teoxoy**\n- Enforce discard is only used in the fragment stage. ([#2262](https://github.com/gfx-rs/naga/pull/2262)) **@Uriopass**\n- Add `Capabilities::MULTISAMPLED_SHADING`. ([#2255](https://github.com/gfx-rs/naga/pull/2255)) **@teoxoy**\n- Add `Capabilities::EARLY_DEPTH_TEST`. ([#2255](https://github.com/gfx-rs/naga/pull/2255)) **@teoxoy**\n- Add `Capabilities::MULTIVIEW`. ([#2255](https://github.com/gfx-rs/naga/pull/2255)) **@teoxoy**\n- Improve forward declaration validation. ([#2232](https://github.com/gfx-rs/naga/pull/2232)) **@JCapucho**\n\n#### WGSL-IN\n\n- Use `alias` instead of `type` for type aliases. ([#2299](https://github.com/gfx-rs/naga/pull/2299)) **@FL33TW00D**\n- Add predeclared vector and matrix type aliases. ([#2251](https://github.com/gfx-rs/naga/pull/2251)) **@evahop**\n- Improve invalid assignment diagnostic. ([#2233](https://github.com/gfx-rs/naga/pull/2233)) **@SparkyPotato**\n- Expect semicolons wherever required. ([#2233](https://github.com/gfx-rs/naga/pull/2233)) **@SparkyPotato**\n- Fix panic on invalid zero array size. ([#2233](https://github.com/gfx-rs/naga/pull/2233)) **@SparkyPotato**\n- Check for leading `{` while parsing a block. ([#2233](https://github.com/gfx-rs/naga/pull/2233)) **@SparkyPotato**\n\n#### SPV-IN\n\n- Don't apply interpolation to fragment shaders outputs. ([#2239](https://github.com/gfx-rs/naga/pull/2239)) **@JCapucho**\n\n#### GLSL-IN\n\n- Add switch implicit type conversion. ([#2273](https://github.com/gfx-rs/naga/pull/2273)) **@evahop**\n- Document some fields of `naga::front::glsl::context::Context`. ([#2244](https://github.com/gfx-rs/naga/pull/2244)) **@jimblandy**\n- Perform output parameters implicit casts. ([#2063](https://github.com/gfx-rs/naga/pull/2063)) **@JCapucho**\n- Add `not` vector relational builtin. ([#2227](https://github.com/gfx-rs/naga/pull/2227)) **@JCapucho**\n- Add double overloads for relational vector builtins. ([#2227](https://github.com/gfx-rs/naga/pull/2227)) **@JCapucho**\n- Add bool overloads for relational vector builtins. ([#2227](https://github.com/gfx-rs/naga/pull/2227)) **@JCapucho**\n\n#### SPV-OUT\n\n- Fix invalid spirv being generated from integer dot products. ([#2291](https://github.com/gfx-rs/naga/pull/2291)) **@PyryM**\n- Fix adding illegal decorators on fragment outputs. ([#2286](https://github.com/gfx-rs/naga/pull/2286)) **@Wumpf**\n- Fix `countLeadingZeros` impl. ([#2258](https://github.com/gfx-rs/naga/pull/2258)) **@teoxoy**\n- Cache constant composites. ([#2257](https://github.com/gfx-rs/naga/pull/2257)) **@evahop**\n- Support SPIR-V version 1.4. ([#2230](https://github.com/gfx-rs/naga/pull/2230)) **@kvark**\n\n#### MSL-OUT\n\n- Replace `per_stage_map` with `per_entry_point_map` ([#2237](https://github.com/gfx-rs/naga/pull/2237)) **@armansito**\n- Update `firstLeadingBit` for signed integers ([#2235](https://github.com/gfx-rs/naga/pull/2235)) **@evahop**\n\n#### HLSL-OUT\n\n- Use `Interlocked<op>` intrinsic for atomic integers (#2294) ([#2294](https://github.com/gfx-rs/naga/pull/2294)) **@ErichDonGubler**\n- Document storage access generation. ([#2295](https://github.com/gfx-rs/naga/pull/2295)) **@jimblandy**\n- Emit constructor functions for arrays. ([#2281](https://github.com/gfx-rs/naga/pull/2281)) **@ErichDonGubler**\n- Clear `named_expressions` inserted by duplicated blocks. ([#2116](https://github.com/gfx-rs/naga/pull/2116)) **@teoxoy**\n\n#### GLSL-OUT\n\n- Skip `invariant` for `gl_FragCoord` on WebGL2. ([#2254](https://github.com/gfx-rs/naga/pull/2254)) **@grovesNL**\n- Inject default `gl_PointSize = 1.0` in vertex shaders if `FORCE_POINT_SIZE` option was set. ([#2223](https://github.com/gfx-rs/naga/pull/2223)) **@REASY**\n\n## v0.11.1 (2023-05-18)\n\n#### SPV-IN\n\n- (Backport) Convert conditional backedges to `break if`. ([#2290](https://github.com/gfx-rs/naga/pull/2290)) **@eddyb**\n\n## v0.11 (2023-01-25)\n\n- Move to the Rust 2021 edition ([#2085](https://github.com/gfx-rs/naga/pull/2085)) **@ErichDonGubler**\n- Bump MSRV to 1.63 ([#2129](https://github.com/gfx-rs/naga/pull/2129)) **@teoxoy**\n\n#### API\n\n- Add handle validation pass to `Validator` ([#2090](https://github.com/gfx-rs/naga/pull/2090)) **@ErichDonGubler**\n- Add `Range::new_from_bounds` ([#2148](https://github.com/gfx-rs/naga/pull/2148)) **@robtfm**\n\n#### DOCS\n\n- Fix docs for `Emit` statements ([#2208](https://github.com/gfx-rs/naga/pull/2208)) **@jimblandy**\n- Fix invalid `<...>` URLs with code spans ([#2176](https://github.com/gfx-rs/naga/pull/2176)) **@ErichDonGubler**\n- Explain how case clauses with multiple selectors are supported ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n- Document `EarlyDepthTest` and `ConservativeDepth` syntax ([#2132](https://github.com/gfx-rs/naga/pull/2132)) **@coreh**\n\n#### VALIDATOR\n\n- Allow `u32` coordinates for `textureStore`/`textureLoad` ([#2172](https://github.com/gfx-rs/naga/pull/2172)) **@PENGUINLIONG**\n- Fix array being flagged as constructible when its base isn't ([#2111](https://github.com/gfx-rs/naga/pull/2111)) **@teoxoy**\n- Add `type_flags` to `ModuleInfo` ([#2111](https://github.com/gfx-rs/naga/pull/2111)) **@teoxoy**\n- Remove overly restrictive array stride check ([#2215](https://github.com/gfx-rs/naga/pull/2215)) **@fintelia**\n- Let the uniformity analysis trust the handle validation pass ([#2200](https://github.com/gfx-rs/naga/pull/2200)) **@jimblandy**\n- Fix warnings when building tests without validation ([#2177](https://github.com/gfx-rs/naga/pull/2177)) **@jimblandy**\n- Add `ValidationFlags::BINDINGS` ([#2156](https://github.com/gfx-rs/naga/pull/2156)) **@kvark**\n- Fix `textureGather` on `texture_2d<u32/i32>` ([#2138](https://github.com/gfx-rs/naga/pull/2138)) **@JMS55**\n\n#### ALL (FRONTENDS/BACKENDS)\n\n- Support 16-bit unorm/snorm formats ([#2210](https://github.com/gfx-rs/naga/pull/2210)) **@fintelia**\n- Support `gl_PointCoord` ([#2180](https://github.com/gfx-rs/naga/pull/2180)) **@Neo-Zhixing**\n\n#### ALL BACKENDS\n\n- Add support for zero-initializing workgroup memory ([#2111](https://github.com/gfx-rs/naga/pull/2111)) **@teoxoy**\n\n#### WGSL-IN\n\n- Implement module-level scoping ([#2075](https://github.com/gfx-rs/naga/pull/2075)) **@SparkyPotato**\n- Remove `isFinite` and `isNormal` ([#2218](https://github.com/gfx-rs/naga/pull/2218)) **@evahop**\n- Update inverse hyperbolic built-ins ([#2218](https://github.com/gfx-rs/naga/pull/2218)) **@evahop**\n- Add `refract` built-in ([#2218](https://github.com/gfx-rs/naga/pull/2218)) **@evahop**\n- Update reserved keywords ([#2130](https://github.com/gfx-rs/naga/pull/2130)) **@teoxoy**\n- Remove non-32bit integers ([#2146](https://github.com/gfx-rs/naga/pull/2146)) **@teoxoy**\n- Remove `workgroup_size` builtin ([#2147](https://github.com/gfx-rs/naga/pull/2147)) **@teoxoy**\n- Remove fallthrough statement ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n\n#### SPV-IN\n\n- Support binding arrays ([#2199](https://github.com/gfx-rs/naga/pull/2199)) **@Patryk27**\n\n#### GLSL-IN\n\n- Fix position propagation in lowering ([#2079](https://github.com/gfx-rs/naga/pull/2079)) **@JCapucho**\n- Update initializer list type when parsing ([#2066](https://github.com/gfx-rs/naga/pull/2066)) **@JCapucho**\n- Parenthesize unary negations to avoid `--` ([#2087](https://github.com/gfx-rs/naga/pull/2087)) **@ErichDonGubler**\n\n#### SPV-OUT\n\n- Add support for `atomicCompareExchangeWeak` ([#2165](https://github.com/gfx-rs/naga/pull/2165)) **@aweinstock314**\n- Omit extra switch case blocks where possible ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n- Fix switch cases after default not being output ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n\n#### MSL-OUT\n\n- Don't panic on missing bindings ([#2175](https://github.com/gfx-rs/naga/pull/2175)) **@kvark**\n- Omit extra switch case blocks where possible ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n- Fix `textureGather` compatibility on macOS 10.13 ([#2104](https://github.com/gfx-rs/naga/pull/2104)) **@xiaopengli89**\n- Fix incorrect atomic bounds check on metal back-end ([#2099](https://github.com/gfx-rs/naga/pull/2099)) **@raphlinus**\n- Parenthesize unary negations to avoid `--` ([#2087](https://github.com/gfx-rs/naga/pull/2087)) **@ErichDonGubler**\n\n#### HLSL-OUT\n\n- Simplify `write_default_init` ([#2111](https://github.com/gfx-rs/naga/pull/2111)) **@teoxoy**\n- Omit extra switch case blocks where possible ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n- Properly implement bitcast ([#2097](https://github.com/gfx-rs/naga/pull/2097)) **@cwfitzgerald**\n- Fix storage access chain through a matrix ([#2097](https://github.com/gfx-rs/naga/pull/2097)) **@cwfitzgerald**\n- Workaround FXC Bug in Matrix Indexing ([#2096](https://github.com/gfx-rs/naga/pull/2096)) **@cwfitzgerald**\n- Parenthesize unary negations to avoid `--` ([#2087](https://github.com/gfx-rs/naga/pull/2087)) **@ErichDonGubler**\n\n#### GLSL-OUT\n\n- Introduce a flag to include unused items ([#2205](https://github.com/gfx-rs/naga/pull/2205)) **@robtfm**\n- Use `fma` polyfill for versions below gles 320 ([#2197](https://github.com/gfx-rs/naga/pull/2197)) **@teoxoy**\n- Emit reflection info for non-struct uniforms ([#2189](https://github.com/gfx-rs/naga/pull/2189)) **@Rainb0wCodes**\n- Introduce a new block for switch cases ([#2126](https://github.com/gfx-rs/naga/pull/2126)) **@teoxoy**\n\n#### WGSL-OUT\n\n- Write correct scalar kind when `width != 4` ([#1514](https://github.com/gfx-rs/naga/pull/1514)) **@fintelia**\n\n## v0.10.1 (2023-06-21)\n\nSPV-OUT\n- Backport #2389 (Use `IndexSet` instead of `HashSet` for iterated sets (capabilities/extensions)) by @eddyb, @jimblandy in https://github.com/gfx-rs/naga/pull/2391\n\nSPV-IN\n- Backport #2290 (Convert conditional backedges to `break if`) by @eddyb in https://github.com/gfx-rs/naga/pull/2387\n\n## v0.10 (2022-10-05)\n\n- Make termcolor dependency optional by @AldaronLau in https://github.com/gfx-rs/naga/pull/2014\n- Fix clippy lints for 1.63 by @JCapucho in https://github.com/gfx-rs/naga/pull/2026\n- Saturate by @evahop in https://github.com/gfx-rs/naga/pull/2025\n- Use `Option::as_deref` as appropriate. by @jimblandy in https://github.com/gfx-rs/naga/pull/2040\n- Explicitly enable std for indexmap by @maxammann in https://github.com/gfx-rs/naga/pull/2062\n- Fix compiler warning by @Gordon-F in https://github.com/gfx-rs/naga/pull/2074\n\nAPI\n- Implement `Clone` for `Module` by @daxpedda in https://github.com/gfx-rs/naga/pull/2013\n- Remove the glsl-validate feature by @JCapucho in https://github.com/gfx-rs/naga/pull/2045\n\nDOCS\n- Document arithmetic binary operation type rules. by @jimblandy in https://github.com/gfx-rs/naga/pull/2051\n\nVALIDATOR\n- Add `emit_to_{stderr,string}` helpers to validation error by @nolanderc in https://github.com/gfx-rs/naga/pull/2012\n- Check regular functions don't have bindings by @JCapucho in https://github.com/gfx-rs/naga/pull/2050\n\nWGSL-IN\n- Update reserved WGSL keywords by @norepimorphism in https://github.com/gfx-rs/naga/pull/2009\n- Implement lexical scopes by @JCapucho in https://github.com/gfx-rs/naga/pull/2024\n- Rename `Scope` to `Rule`, since we now have lexical scope. by @jimblandy in https://github.com/gfx-rs/naga/pull/2042\n- Splat on compound assignments by @JCapucho in https://github.com/gfx-rs/naga/pull/2049\n- Fix bad span in assignment lhs error by @JCapucho in https://github.com/gfx-rs/naga/pull/2054\n- Fix inclusion of trivia in spans by @SparkyPotato in https://github.com/gfx-rs/naga/pull/2055\n- Improve assignment diagnostics by @SparkyPotato in https://github.com/gfx-rs/naga/pull/2056\n- Break up long string, reformat rest of file. by @jimblandy in https://github.com/gfx-rs/naga/pull/2057\n- Fix line endings on wgsl reserved words list. by @jimblandy in https://github.com/gfx-rs/naga/pull/2059\n\nGLSL-IN\n- Add support for .length() by @SpaceCat-Chan in https://github.com/gfx-rs/naga/pull/2017\n- Fix missing stores for local declarations by @adeline-sparks in https://github.com/gfx-rs/naga/pull/2029\n- Migrate to `SymbolTable` by @JCapucho in https://github.com/gfx-rs/naga/pull/2044\n- Update initializer list type when parsing by @JCapucho in https://github.com/gfx-rs/naga/pull/2066\n\nSPV-OUT\n- Don't decorate varyings with interpolation modes at pipeline start/end by @nical in https://github.com/gfx-rs/naga/pull/2038\n- Decorate integer builtins as Flat in the spirv writer by @nical in https://github.com/gfx-rs/naga/pull/2035\n- Properly combine the fixes for #2035 and #2038. by @jimblandy in https://github.com/gfx-rs/naga/pull/2041\n- Don't emit no-op `OpBitCast` instructions. by @jimblandy in https://github.com/gfx-rs/naga/pull/2043\n\nHLSL-OUT\n- Use the namer to sanitise entrypoint input/output struct names by @expenses in https://github.com/gfx-rs/naga/pull/2001\n- Handle Unpack2x16float in hlsl by @expenses in https://github.com/gfx-rs/naga/pull/2002\n- Add support for push constants by @JCapucho in https://github.com/gfx-rs/naga/pull/2005\n\nDOT-OUT\n- Improvements by @JCapucho in https://github.com/gfx-rs/naga/pull/1987\n\n## v0.9 (2022-06-30)\n\n- Fix minimal-versions of dependencies ([#1840](https://github.com/gfx-rs/naga/pull/1840)) **@teoxoy**\n- Update MSRV to 1.56 ([#1838](https://github.com/gfx-rs/naga/pull/1838)) **@teoxoy**\n\nAPI\n\n- Rename `TypeFlags` `INTERFACE`/`HOST_SHARED` to `IO_SHARED`/`HOST_SHAREABLE` ([#1872](https://github.com/gfx-rs/naga/pull/1872)) **@jimblandy**\n- Expose more error information ([#1827](https://github.com/gfx-rs/naga/pull/1827), [#1937](https://github.com/gfx-rs/naga/pull/1937)) **@jakobhellermann** **@nical** **@jimblandy**\n- Do not unconditionally make error output colorful ([#1707](https://github.com/gfx-rs/naga/pull/1707)) **@rhysd**\n- Rename `StorageClass` to `AddressSpace` ([#1699](https://github.com/gfx-rs/naga/pull/1699)) **@kvark**\n- Add a way to emit errors to a path ([#1640](https://github.com/gfx-rs/naga/pull/1640)) **@laptou**\n\nCLI\n\n- Add `bincode` representation ([#1729](https://github.com/gfx-rs/naga/pull/1729)) **@kvark**\n- Include file path in WGSL parse error ([#1708](https://github.com/gfx-rs/naga/pull/1708)) **@rhysd**\n- Add `--version` flag ([#1706](https://github.com/gfx-rs/naga/pull/1706)) **@rhysd**\n- Support reading input from stdin via `--stdin-file-path` ([#1701](https://github.com/gfx-rs/naga/pull/1701)) **@rhysd**\n- Use `panic = \"abort\"` ([#1597](https://github.com/gfx-rs/naga/pull/1597)) **@jrmuizel**\n\nDOCS\n\n- Standardize some docs ([#1660](https://github.com/gfx-rs/naga/pull/1660)) **@NoelTautges**\n- Document `TypeInner::BindingArray` ([#1859](https://github.com/gfx-rs/naga/pull/1859)) **@jimblandy**\n- Clarify accepted types for `Expression::AccessIndex` ([#1862](https://github.com/gfx-rs/naga/pull/1862)) **@NoelTautges**\n- Document `proc::layouter` ([#1693](https://github.com/gfx-rs/naga/pull/1693)) **@jimblandy**\n- Document Naga's promises around validation and panics ([#1828](https://github.com/gfx-rs/naga/pull/1828)) **@jimblandy**\n- `FunctionInfo` doc fixes ([#1726](https://github.com/gfx-rs/naga/pull/1726)) **@jimblandy**\n\nVALIDATOR\n\n- Forbid returning pointers and atomics from functions ([#911](https://github.com/gfx-rs/naga/pull/911)) **@jimblandy**\n- Let validation check for more unsupported builtins ([#1962](https://github.com/gfx-rs/naga/pull/1962)) **@jimblandy**\n- Fix `Capabilities::SAMPLER_NON_UNIFORM_INDEXING` bitflag ([#1915](https://github.com/gfx-rs/naga/pull/1915)) **@cwfitzgerald**\n- Properly check that user-defined IO uses IO-shareable types ([#912](https://github.com/gfx-rs/naga/pull/912)) **@jimblandy**\n- Validate `ValuePointer` exactly like a `Pointer` to a `Scalar` ([#1875](https://github.com/gfx-rs/naga/pull/1875)) **@jimblandy**\n- Reject empty structs ([#1826](https://github.com/gfx-rs/naga/pull/1826)) **@jimblandy**\n- Validate uniform address space layout constraints ([#1812](https://github.com/gfx-rs/naga/pull/1812)) **@teoxoy**\n- Improve `AddressSpace` related error messages ([#1710](https://github.com/gfx-rs/naga/pull/1710)) **@kvark**\n\nWGSL-IN\n\nMain breaking changes\n\n- Commas to separate struct members (comma after last member is optional)\n  - `struct S { a: f32; b: i32; }` -> `struct S { a: f32, b: i32 }`\n- Attribute syntax\n  - `[[binding(0), group(0)]]` -> `@binding(0) @group(0)`\n- Entry point stage attributes\n  - `@stage(vertex)` -> `@vertex`\n  - `@stage(fragment)` -> `@fragment`\n  - `@stage(compute)` -> `@compute`\n- Function renames\n  - `smoothStep` -> `smoothstep`\n  - `findLsb` -> `firstTrailingBit`\n  - `findMsb` -> `firstLeadingBit`\n\nSpecification Changes (relevant changes have also been applied to the WGSL backend)\n\n- Add support for `break if` ([#1993](https://github.com/gfx-rs/naga/pull/1993)) **@JCapucho**\n- Update number literal format ([#1863](https://github.com/gfx-rs/naga/pull/1863)) **@teoxoy**\n- Allow non-ascii characters in identifiers ([#1849](https://github.com/gfx-rs/naga/pull/1849)) **@teoxoy**\n- Update reserved keywords ([#1847](https://github.com/gfx-rs/naga/pull/1847), [#1870](https://github.com/gfx-rs/naga/pull/1870), [#1905](https://github.com/gfx-rs/naga/pull/1905)) **@teoxoy** **@Gordon-F**\n- Update entry point stage attributes ([#1833](https://github.com/gfx-rs/naga/pull/1833)) **@Gordon-F**\n- Make colon in case optional ([#1801](https://github.com/gfx-rs/naga/pull/1801)) **@Gordon-F**\n- Rename `smoothStep` to `smoothstep` ([#1800](https://github.com/gfx-rs/naga/pull/1800)) **@Gordon-F**\n- Make semicolon after struct declaration optional ([#1791](https://github.com/gfx-rs/naga/pull/1791)) **@stshine**\n- Use commas to separate struct members instead of semicolons ([#1773](https://github.com/gfx-rs/naga/pull/1773)) **@Gordon-F**\n- Rename `findLsb`/`findMsb` to `firstTrailingBit`/`firstLeadingBit` ([#1735](https://github.com/gfx-rs/naga/pull/1735)) **@kvark**\n- Make parenthesis optional for `if` and `switch` statements ([#1725](https://github.com/gfx-rs/naga/pull/1725)) **@Gordon-F**\n- Declare attributes with `@attrib` instead of `[[attrib]]` ([#1676](https://github.com/gfx-rs/naga/pull/1676)) **@kvark**\n- Allow non-structure buffer types ([#1682](https://github.com/gfx-rs/naga/pull/1682)) **@kvark**\n- Remove `stride` attribute ([#1681](https://github.com/gfx-rs/naga/pull/1681)) **@kvark**\n\nImprovements\n\n- Implement complete validation for size and align attributes ([#1979](https://github.com/gfx-rs/naga/pull/1979)) **@teoxoy**\n- Implement `firstTrailingBit`/`firstLeadingBit` u32 overloads ([#1865](https://github.com/gfx-rs/naga/pull/1865)) **@teoxoy**\n- Add error for non-floating-point matrix ([#1917](https://github.com/gfx-rs/naga/pull/1917)) **@grovesNL**\n- Implement partial vector & matrix identity constructors ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**\n- Implement phony assignment ([#1866](https://github.com/gfx-rs/naga/pull/1866), [#1869](https://github.com/gfx-rs/naga/pull/1869)) **@teoxoy**\n- Fix being able to match `~=` as LogicalOperation ([#1849](https://github.com/gfx-rs/naga/pull/1849)) **@teoxoy**\n- Implement Binding Arrays ([#1845](https://github.com/gfx-rs/naga/pull/1845)) **@cwfitzgerald**\n- Implement unary vector operators ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**\n- Implement zero value constructors and constructors that infer their type from their parameters ([#1790](https://github.com/gfx-rs/naga/pull/1790)) **@teoxoy**\n- Implement invariant attribute ([#1789](https://github.com/gfx-rs/naga/pull/1789), [#1822](https://github.com/gfx-rs/naga/pull/1822)) **@teoxoy** **@jimblandy**\n- Implement increment and decrement statements ([#1788](https://github.com/gfx-rs/naga/pull/1788), [#1912](https://github.com/gfx-rs/naga/pull/1912)) **@teoxoy**\n- Implement `while` loop ([#1787](https://github.com/gfx-rs/naga/pull/1787)) **@teoxoy**\n- Fix array size on globals ([#1717](https://github.com/gfx-rs/naga/pull/1717)) **@jimblandy**\n- Implement integer vector overloads for `dot` function ([#1689](https://github.com/gfx-rs/naga/pull/1689)) **@francesco-cattoglio**\n- Implement block comments ([#1675](https://github.com/gfx-rs/naga/pull/1675)) **@kocsis1david**\n- Implement assignment binary operators ([#1662](https://github.com/gfx-rs/naga/pull/1662)) **@kvark**\n- Implement `radians`/`degrees` builtin functions ([#1627](https://github.com/gfx-rs/naga/pull/1627)) **@encounter**\n- Implement `findLsb`/`findMsb` builtin functions ([#1473](https://github.com/gfx-rs/naga/pull/1473)) **@fintelia**\n- Implement `textureGather`/`textureGatherCompare` builtin functions ([#1596](https://github.com/gfx-rs/naga/pull/1596)) **@kvark**\n\nSPV-IN\n\n- Implement `OpBitReverse` and `OpBitCount` ([#1954](https://github.com/gfx-rs/naga/pull/1954)) **@JCapucho**\n- Add `MultiView` to `SUPPORTED_CAPABILITIES` ([#1934](https://github.com/gfx-rs/naga/pull/1934)) **@expenses**\n- Translate `OpSMod` and `OpFMod` correctly ([#1867](https://github.com/gfx-rs/naga/pull/1867), [#1995](https://github.com/gfx-rs/naga/pull/1995)) **@teoxoy** **@JCapucho**\n- Error on unsupported `MatrixStride` ([#1805](https://github.com/gfx-rs/naga/pull/1805)) **@teoxoy**\n- Align array stride for undecorated arrays ([#1724](https://github.com/gfx-rs/naga/pull/1724)) **@JCapucho**\n\nGLSL-IN\n\n- Don't allow empty last case in switch ([#1981](https://github.com/gfx-rs/naga/pull/1981)) **@JCapucho**\n- Fix last case fallthrough and empty switch ([#1981](https://github.com/gfx-rs/naga/pull/1981)) **@JCapucho**\n- Splat inputs for smoothstep if needed ([#1976](https://github.com/gfx-rs/naga/pull/1976)) **@JCapucho**\n- Fix parameter not changing to depth ([#1967](https://github.com/gfx-rs/naga/pull/1967)) **@JCapucho**\n- Fix matrix multiplication check ([#1953](https://github.com/gfx-rs/naga/pull/1953)) **@JCapucho**\n- Fix panic (stop emitter in conditional) ([#1952](https://github.com/gfx-rs/naga/pull/1952)) **@JCapucho**\n- Translate `mod` fn correctly ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**\n- Make the ternary operator behave as an if ([#1877](https://github.com/gfx-rs/naga/pull/1877)) **@JCapucho**\n- Add support for `clamp` function ([#1502](https://github.com/gfx-rs/naga/pull/1502)) **@sjinno**\n- Better errors for bad constant expression ([#1501](https://github.com/gfx-rs/naga/pull/1501)) **@sjinno**\n- Error on a `matCx2` used with the `std140` layout ([#1806](https://github.com/gfx-rs/naga/pull/1806)) **@teoxoy**\n- Allow nested accesses in lhs positions ([#1794](https://github.com/gfx-rs/naga/pull/1794)) **@JCapucho**\n- Use forced conversions for vector/matrix constructors ([#1796](https://github.com/gfx-rs/naga/pull/1796)) **@JCapucho**\n- Add support for `barrier` function ([#1793](https://github.com/gfx-rs/naga/pull/1793)) **@fintelia**\n- Fix panic (resume expression emit after `imageStore`) ([#1795](https://github.com/gfx-rs/naga/pull/1795)) **@JCapucho**\n- Allow multiple array specifiers ([#1780](https://github.com/gfx-rs/naga/pull/1780)) **@JCapucho**\n- Fix memory qualifiers being inverted ([#1779](https://github.com/gfx-rs/naga/pull/1779)) **@JCapucho**\n- Support arrays as input/output types ([#1759](https://github.com/gfx-rs/naga/pull/1759)) **@JCapucho**\n- Fix freestanding constructor parsing ([#1758](https://github.com/gfx-rs/naga/pull/1758)) **@JCapucho**\n- Fix matrix - scalar operations ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**\n- Fix matrix - matrix division ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**\n- Fix matrix comparisons ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**\n- Add support for `texelFetchOffset` ([#1746](https://github.com/gfx-rs/naga/pull/1746)) **@JCapucho**\n- Inject `sampler2DMSArray` builtins on use ([#1737](https://github.com/gfx-rs/naga/pull/1737)) **@JCapucho**\n- Inject `samplerCubeArray` builtins on use ([#1736](https://github.com/gfx-rs/naga/pull/1736)) **@JCapucho**\n- Add support for image builtin functions ([#1723](https://github.com/gfx-rs/naga/pull/1723)) **@JCapucho**\n- Add support for image declarations ([#1723](https://github.com/gfx-rs/naga/pull/1723)) **@JCapucho**\n- Texture builtins fixes ([#1719](https://github.com/gfx-rs/naga/pull/1719)) **@JCapucho**\n- Type qualifiers rework ([#1713](https://github.com/gfx-rs/naga/pull/1713)) **@JCapucho**\n- `texelFetch` accept multisampled textures ([#1715](https://github.com/gfx-rs/naga/pull/1715)) **@JCapucho**\n- Fix panic when culling nested block ([#1714](https://github.com/gfx-rs/naga/pull/1714)) **@JCapucho**\n- Fix composite constructors ([#1631](https://github.com/gfx-rs/naga/pull/1631)) **@JCapucho**\n- Fix using swizzle as out arguments ([#1632](https://github.com/gfx-rs/naga/pull/1632)) **@JCapucho**\n\nSPV-OUT\n\n- Implement `reverseBits` and `countOneBits` ([#1897](https://github.com/gfx-rs/naga/pull/1897)) **@hasali19**\n- Use `OpCopyObject` for matrix identity casts ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**\n- Use `OpCopyObject` for bool - bool conversion due to `OpBitcast` not being feasible for booleans ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**\n- Zero init variables in function and private address spaces ([#1871](https://github.com/gfx-rs/naga/pull/1871)) **@teoxoy**\n- Use `SRem` instead of `SMod` ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**\n- Add support for integer vector - scalar multiplication ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**\n- Add support for matrix addition and subtraction ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**\n- Emit required decorations on wrapper struct types ([#1815](https://github.com/gfx-rs/naga/pull/1815)) **@jimblandy**\n- Decorate array and struct type layouts unconditionally ([#1815](https://github.com/gfx-rs/naga/pull/1815)) **@jimblandy**\n- Fix wrong `MatrixStride` for `matCx2` and `mat2xR` ([#1781](https://github.com/gfx-rs/naga/pull/1781)) **@teoxoy**\n- Use `OpImageQuerySize` for MS images ([#1742](https://github.com/gfx-rs/naga/pull/1742)) **@JCapucho**\n\nMSL-OUT\n\n- Insert padding initialization for global constants ([#1988](https://github.com/gfx-rs/naga/pull/1988)) **@teoxoy**\n- Don't rely on cached expressions ([#1975](https://github.com/gfx-rs/naga/pull/1975)) **@JCapucho**\n- Fix pointers to private or workgroup address spaces possibly being read only ([#1901](https://github.com/gfx-rs/naga/pull/1901)) **@teoxoy**\n- Zero init variables in function address space ([#1871](https://github.com/gfx-rs/naga/pull/1871)) **@teoxoy**\n- Make binding arrays play nice with bounds checks ([#1855](https://github.com/gfx-rs/naga/pull/1855)) **@cwfitzgerald**\n- Permit `invariant` qualifier on vertex shader outputs ([#1821](https://github.com/gfx-rs/naga/pull/1821)) **@jimblandy**\n- Fix packed `vec3` stores ([#1816](https://github.com/gfx-rs/naga/pull/1816)) **@teoxoy**\n- Actually test push constants to be used ([#1767](https://github.com/gfx-rs/naga/pull/1767)) **@kvark**\n- Properly rename entry point arguments for struct members ([#1766](https://github.com/gfx-rs/naga/pull/1766)) **@jimblandy**\n- Qualify read-only storage with const ([#1763](https://github.com/gfx-rs/naga/pull/1763)) **@kvark**\n- Fix not unary operator for integer scalars ([#1760](https://github.com/gfx-rs/naga/pull/1760)) **@vincentisambart**\n- Add bounds checks for `ImageLoad` and `ImageStore` ([#1730](https://github.com/gfx-rs/naga/pull/1730)) **@jimblandy**\n- Fix resource bindings for non-structures ([#1718](https://github.com/gfx-rs/naga/pull/1718)) **@kvark**\n- Always check whether _buffer_sizes arg is needed ([#1717](https://github.com/gfx-rs/naga/pull/1717)) **@jimblandy**\n- WGSL storage address space should always correspond to MSL device address space ([#1711](https://github.com/gfx-rs/naga/pull/1711)) **@wtholliday**\n- Mitigation for MSL atomic bounds check ([#1703](https://github.com/gfx-rs/naga/pull/1703)) **@glalonde**\n\nHLSL-OUT\n\n- More `matCx2` fixes (#1989) ([#1989](https://github.com/gfx-rs/naga/pull/1989)) **@teoxoy**\n- Fix fallthrough in switch statements ([#1920](https://github.com/gfx-rs/naga/pull/1920)) **@teoxoy**\n- Fix missing break statements ([#1919](https://github.com/gfx-rs/naga/pull/1919)) **@teoxoy**\n- Fix `countOneBits` and `reverseBits` for signed integers ([#1928](https://github.com/gfx-rs/naga/pull/1928)) **@hasali19**\n- Fix array constructor return type ([#1914](https://github.com/gfx-rs/naga/pull/1914)) **@teoxoy**\n- Fix hlsl output for writes to scalar/vector storage buffer ([#1903](https://github.com/gfx-rs/naga/pull/1903)) **@hasali19**\n- Use `fmod` instead of `%` ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**\n- Use wrapped constructors when loading from storage address space ([#1893](https://github.com/gfx-rs/naga/pull/1893)) **@teoxoy**\n- Zero init struct constructor ([#1890](https://github.com/gfx-rs/naga/pull/1890)) **@teoxoy**\n- Flesh out matrix handling documentation ([#1850](https://github.com/gfx-rs/naga/pull/1850)) **@jimblandy**\n- Emit `row_major` qualifier on matrix uniform globals ([#1846](https://github.com/gfx-rs/naga/pull/1846)) **@jimblandy**\n- Fix bool splat ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**\n- Add more padding when necessary ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**\n- Support multidimensional arrays ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**\n- Don't output interpolation modifier if it's the default ([#1809](https://github.com/gfx-rs/naga/pull/1809)) **@NoelTautges**\n- Fix `matCx2` translation for uniform buffers ([#1802](https://github.com/gfx-rs/naga/pull/1802)) **@teoxoy**\n- Fix modifiers not being written in the vertex output and fragment input structs ([#1789](https://github.com/gfx-rs/naga/pull/1789)) **@teoxoy**\n- Fix matrix not being declared as transposed ([#1784](https://github.com/gfx-rs/naga/pull/1784)) **@teoxoy**\n- Insert padding between struct members ([#1786](https://github.com/gfx-rs/naga/pull/1786)) **@teoxoy**\n- Fix not unary operator for integer scalars ([#1760](https://github.com/gfx-rs/naga/pull/1760)) **@vincentisambart**\n\nGLSL-OUT\n\n- Fix vector bitcasts (#1966) ([#1966](https://github.com/gfx-rs/naga/pull/1966)) **@expenses**\n- Perform casts in int only math functions ([#1978](https://github.com/gfx-rs/naga/pull/1978)) **@JCapucho**\n- Don't rely on cached expressions ([#1975](https://github.com/gfx-rs/naga/pull/1975)) **@JCapucho**\n- Fix type error for `countOneBits` implementation ([#1897](https://github.com/gfx-rs/naga/pull/1897)) **@hasali19**\n- Fix storage format for `Rgba8Unorm` ([#1955](https://github.com/gfx-rs/naga/pull/1955)) **@JCapucho**\n- Implement bounds checks for `ImageLoad` ([#1889](https://github.com/gfx-rs/naga/pull/1889)) **@JCapucho**\n- Fix feature search in expressions ([#1887](https://github.com/gfx-rs/naga/pull/1887)) **@JCapucho**\n- Emit globals of any type ([#1823](https://github.com/gfx-rs/naga/pull/1823)) **@jimblandy**\n- Add support for boolean vector `~`, `|` and `&` ops ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**\n- Fix array function arguments ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**\n- Write constant sized array type for uniform ([#1768](https://github.com/gfx-rs/naga/pull/1768)) **@hatoo**\n- Texture function fixes ([#1742](https://github.com/gfx-rs/naga/pull/1742)) **@JCapucho**\n- Push constants use anonymous uniforms ([#1683](https://github.com/gfx-rs/naga/pull/1683)) **@JCapucho**\n- Add support for push constant emulation ([#1672](https://github.com/gfx-rs/naga/pull/1672)) **@JCapucho**\n- Skip unsized types if unused ([#1649](https://github.com/gfx-rs/naga/pull/1649)) **@kvark**\n- Write struct and array initializers ([#1644](https://github.com/gfx-rs/naga/pull/1644)) **@JCapucho**\n\n\n## v0.8.5 (2022-01-25)\n\nMSL-OUT\n\n- Make VS-output positions invariant on even more systems ([#1697](https://github.com/gfx-rs/naga/pull/1697)) **@cwfitzgerald**\n- Improve support for point primitives ([#1696](https://github.com/gfx-rs/naga/pull/1696)) **@kvark**\n\n\n## v0.8.4 (2022-01-24)\n\nMSL-OUT\n\n- Make VS-output positions invariant if possible ([#1687](https://github.com/gfx-rs/naga/pull/1687)) **@kvark**\n\nGLSL-OUT\n\n- Fix `floatBitsToUint` spelling ([#1688](https://github.com/gfx-rs/naga/pull/1688)) **@cwfitzgerald**\n- Call proper memory barrier functions ([#1680](https://github.com/gfx-rs/naga/pull/1680)) **@francesco-cattoglio**\n\n\n## v0.8.3 (2022-01-20)\n\n- Don't pin `indexmap` version ([#1666](https://github.com/gfx-rs/naga/pull/1666)) **@a1phyr**\n\nMSL-OUT\n\n- Fix support for point primitives ([#1674](https://github.com/gfx-rs/naga/pull/1674)) **@kvark**\n\nGLSL-OUT\n\n- Fix sampler association ([#1671](https://github.com/gfx-rs/naga/pull/1671)) **@JCapucho**\n\n\n## v0.8.2 (2022-01-11)\n\nVALIDATOR\n\n- Check structure resource types ([#1639](https://github.com/gfx-rs/naga/pull/1639)) **@kvark**\n\nWGSL-IN\n\n- Improve type mismatch errors ([#1658](https://github.com/gfx-rs/naga/pull/1658)) **@Gordon-F**\n\nSPV-IN\n\n- Implement more sign agnostic operations ([#1651](https://github.com/gfx-rs/naga/pull/1651), [#1650](https://github.com/gfx-rs/naga/pull/1650)) **@JCapucho**\n\nSPV-OUT\n\n- Fix modulo operator (use `OpFRem` instead of `OpFMod`) ([#1653](https://github.com/gfx-rs/naga/pull/1653)) **@JCapucho**\n\nMSL-OUT\n\n- Fix `texture1d` accesses ([#1647](https://github.com/gfx-rs/naga/pull/1647)) **@jimblandy**\n- Fix data packing functions ([#1637](https://github.com/gfx-rs/naga/pull/1637)) **@phoekz**\n\n\n## v0.8.1 (2021-12-29)\n\nAPI\n\n- Make `WithSpan` cloneable ([#1620](https://github.com/gfx-rs/naga/pull/1620)) **@jakobhellermann**\n\nMSL-OUT\n\n- Fix packed vec access ([#1634](https://github.com/gfx-rs/naga/pull/1634)) **@kvark**\n- Fix packed float support ([#1630](https://github.com/gfx-rs/naga/pull/1630)) **@kvark**\n\nHLSL-OUT\n\n- Support arrays of matrices ([#1629](https://github.com/gfx-rs/naga/pull/1629)) **@kvark**\n- Use `mad` instead of `fma` function ([#1580](https://github.com/gfx-rs/naga/pull/1580)) **@parasyte**\n\nGLSL-OUT\n\n- Fix conflicting names for globals ([#1616](https://github.com/gfx-rs/naga/pull/1616)) **@Gordon-F**\n- Fix `fma` function ([#1580](https://github.com/gfx-rs/naga/pull/1580)) **@parasyte**\n\n\n## v0.8 (2021-12-18)\n  - development release for wgpu-0.12\n  - lots of fixes in all parts\n  - validator:\n    - now gated by `validate` feature\n    - nicely detailed error messages with spans\n  - API:\n    - image gather operations\n  - WGSL-in:\n    - remove `[[block]]` attribute\n    - `elseif` is removed in favor of `else if`\n  - MSL-out:\n    - full out-of-bounds checking\n\n## v0.7.3 (2021-12-14)\n  - API:\n    - `view_index` builtin\n  - GLSL-out:\n    - reflect textures without samplers\n  - SPV-out:\n    - fix incorrect pack/unpack\n\n## v0.7.2 (2021-12-01)\n  - validator:\n    - check stores for proper pointer class\n  - HLSL-out:\n    - fix stores into `mat3`\n    - respect array strides\n  - SPV-out:\n    - fix multi-word constants\n  - WGSL-in:\n    - permit names starting with underscores\n  - SPV-in:\n    - cull unused builtins\n    - support empty debug labels\n  - GLSL-in:\n    - don't panic on invalid integer operations\n\n## v0.7.1 (2021-10-12)\n  - implement casts from and to booleans in the backends\n\n## v0.7 (2021-10-07)\n  - development release for wgpu-0.11\n  - API:\n    - bit extraction and packing functions\n    - hyperbolic trigonometry functions\n    - validation is gated by a cargo feature\n    - `view_index` builtin\n    - separate bounds checking policies for locals/buffers/textures\n  - IR:\n    - types and constants are guaranteed to be unique\n  - WGSL-in:\n    - new hex literal parser\n    - updated list of reserved words\n    - rewritten logic for resolving references and pointers\n    - `switch` can use unsigned selectors\n  - GLSL-in:\n    - better support for texture sampling\n    - better logic for auto-splatting scalars\n  - GLSL-out:\n    - fixed storage buffer layout\n    - fix module operator\n  - HLSL-out:\n    - fixed texture queries\n  - SPV-in:\n    - control flow handling is rewritten from scratch\n  - SPV-out:\n    - fully covered out-of-bounds checking\n    - option to emit point size\n    - option to clamp output depth\n\n## v0.6.3 (2021-09-08)\n  - Reduced heap allocations when generating WGSL, HLSL, and GLSL\n  - WGSL-in:\n    - support module-scope `let` type inference\n  - SPV-in:\n    - fix depth sampling with projection\n  - HLSL-out:\n    - fix local struct construction\n  - GLSL-out:\n    - fix `select()` order\n  - SPV-out:\n    - allow working around Adreno issue with `OpName`\n\n## v0.6.2 (2021-09-01)\n  - SPV-out fixes:\n    - requested capabilities for 1D and cube images, storage formats\n    - handling `break` and `continue` in a `switch` statement\n    - avoid generating duplicate `OpTypeImage` types\n  - HLSL-out fixes:\n    - fix output struct member names\n  - MSL-out fixes:\n    - fix packing of fields in interface structs\n  - GLSL-out fixes:\n    - fix non-fallthrough `switch` cases\n  - GLSL-in fixes:\n    - avoid infinite loop on invalid statements\n\n## v0.6.1 (2021-08-24)\n  - HLSL-out fixes:\n    - array arguments\n    - pointers to array arguments\n    - switch statement\n    - rewritten interface matching\n  - SPV-in fixes:\n    - array storage texture stores\n    - tracking sampling across function parameters\n    - updated petgraph dependencies\n  - MSL-out:\n    - gradient sampling\n  - GLSL-out:\n    - modulo operator on floats\n\n## v0.6 (2021-08-18)\n  - development release for wgpu-0.10\n  - API:\n    - atomic types and functions\n    - storage access is moved from global variables to the storage class and storage texture type\n    - new built-ins: `primitive_index` and `num_workgroups`\n    - support for multi-sampled depth images\n  - WGSL:\n    - `select()` order of true/false is swapped\n  - HLSL backend is vastly improved and now usable\n  - GLSL frontend is heavily reworked\n\n## v0.5 (2021-06-18)\n  - development release for wgpu-0.9\n  - API:\n    - barriers\n    - dynamic indexing of matrices and arrays is only allowed on variables\n    - validator now accepts a list of IR capabilities to allow\n    - improved documentation\n  - Infrastructure:\n    - much richer test suite, focused around consuming or emitting WGSL\n    - lazy testing on large shader corpuses\n    - the binary is moved to a sub-crate \"naga-cli\"\n  - Frontends:\n    - GLSL frontend:\n      - rewritten from scratch and effectively revived, no longer depends on `pomelo`\n      - only supports 440/450/460 versions for now\n      - has optional support for codespan messages\n    - SPIRV frontend has improved CFG resolution (still with issues unresolved)\n    - WGSL got better error messages, workgroup memory support\n  - Backends:\n    - general: better expression naming and emitting\n    - new HLSL backend (in progress)\n    - MSL:\n      - support `ArraySize` expression\n      - better texture sampling instructions\n    - GLSL:\n      - multisampling on GLES\n    - WGSL is vastly improved and now usable\n\n## v0.4.2 (2021-05-28)\n  - SPIR-V frontend:\n    - fix image stores\n    - fix matrix stride check\n  - SPIR-V backend:\n    - fix auto-deriving the capabilities\n  - GLSL backend:\n    - support sample interpolation\n    - write out swizzled vector accesses\n\n## v0.4.1 (2021-05-14)\n  - numerous additions and improvements to SPIR-V frontend:\n    - int8, in16, int64\n    - null constant initializers for structs and matrices\n    - `OpArrayLength`, `OpCopyMemory`, `OpInBoundsAccessChain`, `OpLogicalXxxEqual`\n    - outer product\n    - fix struct size alignment\n    - initialize built-ins with default values\n    - fix read-only decorations on struct members\n  - fix struct size alignment in WGSL\n  - fix `fwidth` in WGSL\n  - fix scalars arrays in GLSL backend\n\n## v0.4 (2021-04-29)\n  - development release for wgpu-0.8\n  - API:\n    - expressions are explicitly emitted with `Statement::Emit`\n    - entry points have inputs in arguments and outputs in the result type\n    - `input`/`output` storage classes are gone, but `push_constant` is added\n    - `Interpolation` is moved into `Binding::Location` variant\n    - real pointer semantics with required `Expression::Load`\n    - `TypeInner::ValuePointer` is added\n    - image query expressions are added\n    - new `Statement::ImageStore`\n    - all function calls are `Statement::Call`\n    - `GlobalUse` is moved out into processing\n    - `Header` is removed\n    - entry points are an array instead of a map\n    - new `Swizzle` and `Splat` expressions\n    - interpolation qualifiers are extended and required\n    - struct member layout is based on the byte offsets\n  - Infrastructure:\n    - control flow uniformity analysis\n    - texture-sampler combination gathering\n    - `CallGraph` processor is moved out into `glsl` backend\n    - `Interface` is removed, instead the analysis produces `ModuleInfo` with all the derived info\n    - validation of statement tree, expressions, and constants\n    - code linting is more strict for matches\n  - new GraphViz `dot` backend for pretty visualization of the IR\n  - Metal support for inlined samplers\n  - `convert` example is transformed into the default binary target named `naga`\n  - lots of frontend and backend fixes\n\n## v0.3.2 (2021-02-15)\n  - fix logical expression types\n  - fix _FragDepth_ semantics\n  - spv-in:\n    - derive block status of structures\n  - spv-out:\n    - add lots of missing math functions\n    - implement discard\n\n## v0.3.1 (2021-01-31)\n  - wgsl:\n    - support constant array sizes\n  - spv-out:\n    - fix block decorations on nested structures\n    - fix fixed-size arrays\n    - fix matrix decorations inside structures\n    - implement read-only decorations\n\n## v0.3 (2021-01-30)\n  - development release for wgpu-0.7\n  - API:\n    - math functions\n    - type casts\n    - updated storage classes\n    - updated image sub-types\n    - image sampling/loading options\n    - storage images\n    - interpolation qualifiers\n    - early and conservative depth\n  - Processors:\n    - name manager\n    - automatic layout\n    - termination analysis\n    - validation of types, constants, variables, and entry points\n\n## v0.2 (2020-08-17)\n  - development release for wgpu-0.6\n\n## v0.1 (2020-02-26)\n  - initial release\n"
  },
  {
    "path": "naga/Cargo.toml",
    "content": "[package]\nname = \"naga\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Shader translator and validator. Part of the wgpu project\"\nrepository.workspace = true\nkeywords = [\"shader\", \"SPIR-V\", \"GLSL\", \"MSL\"]\nlicense.workspace = true\nexclude = [\"bin/**/*\", \"tests/**/*\", \"Cargo.lock\", \"target/**/*\"]\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.87\"\n\n[package.metadata.docs.rs]\nall-features = true\n\n[features]\ndefault = []\ndot-out = []\nglsl-in = [\"dep:pp-rs\"]\nglsl-out = []\n\n## Enables outputting to the Metal Shading Language (MSL).\n##\n## This enables MSL output regardless of the target platform.\n## If you want to enable it only when targeting iOS/tvOS/watchOS/macOS, use `naga/msl-out-if-target-apple`.\nmsl-out = []\n\n## Enables outputting to the Metal Shading Language (MSL) only if the target platform is iOS/tvOS/watchOS/macOS.\n##\n## If you want to enable MSL output it regardless of the target platform, use `naga/msl-out`.\nmsl-out-if-target-apple = []\n\nserialize = [\n    \"dep:serde\",\n    \"bitflags/serde\",\n    \"half/serde\",\n    \"hashbrown/serde\",\n    \"indexmap/serde\",\n]\ndeserialize = [\n    \"dep:serde\",\n    \"bitflags/serde\",\n    \"half/serde\",\n    \"hashbrown/serde\",\n    \"indexmap/serde\",\n]\narbitrary = [\n    \"dep:arbitrary\",\n    \"bitflags/arbitrary\",\n    \"indexmap/arbitrary\",\n    \"half/arbitrary\",\n    \"half/std\",\n]\nspv-in = [\"dep:petgraph\", \"petgraph/graphmap\", \"dep:spirv\"]\nspv-out = [\"dep:spirv\"]\nwgsl-in = [\"dep:hexf-parse\", \"dep:unicode-ident\"]\nwgsl-out = []\n\n## Enables outputting to HLSL (Microsoft's High-Level Shader Language).\n##\n## This enables HLSL output regardless of the target platform.\n## If you want to enable it only when targeting Windows, use `hlsl-out-if-target-windows`.\nhlsl-out = []\n\n## Enables outputting to HLSL (Microsoft's High-Level Shader Language) only if the target platform is Windows.\n##\n## If you want to enable HLSL output it regardless of the target platform, use `naga/hlsl-out`.\nhlsl-out-if-target-windows = []\n\n## Enables colored output through codespan-reporting and termcolor.\ntermcolor = [\"codespan-reporting/termcolor\"]\n\n## Enables writing output to stderr.\nstderr = [\"codespan-reporting/std\"]\n\n## Enables integration with the underlying filesystem.\nfs = []\n\n[dependencies]\narbitrary = { workspace = true, features = [\"derive\"], optional = true }\narrayvec.workspace = true\nbitflags.workspace = true\nbit-set.workspace = true\ncfg-if.workspace = true\ncodespan-reporting = { workspace = true }\nhashbrown.workspace = true\nhalf = { workspace = true, features = [\"num-traits\"] }\nrustc-hash.workspace = true\nindexmap.workspace = true\nlibm = { workspace = true, default-features = false }\nlog.workspace = true\nnum-traits.workspace = true\nonce_cell = { workspace = true, features = [\"alloc\", \"race\"] }\nspirv = { workspace = true, optional = true }\nthiserror.workspace = true\nserde = { workspace = true, features = [\"alloc\", \"derive\"], optional = true }\npetgraph = { workspace = true, optional = true }\npp-rs = { workspace = true, optional = true }\nhexf-parse = { workspace = true, optional = true }\nunicode-ident = { workspace = true, optional = true }\n\n[build-dependencies]\ncfg_aliases.workspace = true\n\n[dev-dependencies]\ndiff.workspace = true\nenv_logger.workspace = true\nhashbrown = { workspace = true, features = [\"serde\"] }\nhlsl-snapshots.workspace = true\nitertools.workspace = true\nnaga-test.workspace = true\nron.workspace = true\nrspirv.workspace = true\n# So we don't actually need this, however if we remove this, it\n# brakes calling `--features spirv` at the workspace level. I think\n# this is because there is a `dep:spirv` in the regular feature set,\n# so cargo tries to match the feature against that, fails as it's a optional dep,\n# and then refuses to build instead of ignoring it.\nspirv.workspace = true\nserde = { workspace = true, features = [\"default\", \"derive\"] }\nstrum = { workspace = true }\nwalkdir.workspace = true\n\n[lints.clippy]\nstd_instead_of_alloc = \"warn\"\nstd_instead_of_core = \"warn\"\nalloc_instead_of_core = \"warn\"\n"
  },
  {
    "path": "naga/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "naga/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "naga/README.md",
    "content": "# Naga\n\n[![Matrix](https://img.shields.io/badge/Matrix-%23naga%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#naga:matrix.org)\n[![Crates.io](https://img.shields.io/crates/v/naga.svg?label=naga)](https://crates.io/crates/naga)\n[![Docs.rs](https://docs.rs/naga/badge.svg)](https://docs.rs/naga)\n[![Build Status](https://github.com/gfx-rs/naga/workflows/pipeline/badge.svg)](https://github.com/gfx-rs/naga/actions)\n![MSRV](https://img.shields.io/badge/rustc-1.90-blue.svg)\n[![codecov.io](https://codecov.io/gh/gfx-rs/naga/branch/master/graph/badge.svg?token=9VOKYO8BM2)](https://codecov.io/gh/gfx-rs/naga)\n\nThe shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu).\n\n## Supported end-points\n\nFront-end       |       Status       | Feature | Notes |\n--------------- | ------------------ | ------- | ----- |\nSPIR-V (binary) | :white_check_mark: | spv-in  |       |\nWGSL            | :white_check_mark: | wgsl-in | Fully validated |\nGLSL            | :ok:               | glsl-in | GLSL 440+ and Vulkan semantics only |\n\nBack-end        |       Status       | Feature  | Notes |\n--------------- | ------------------ | -------- | ----- |\nSPIR-V          | :white_check_mark: | spv-out  |       |\nWGSL            | :ok:               | wgsl-out |       |\nMetal           | :white_check_mark: | msl-out  |       |\nHLSL            | :white_check_mark: | hlsl-out | Shader Model 5.0+ (DirectX 11+) |\nGLSL            | :ok:               | glsl-out | GLSL 330+ and GLSL ES 300+ |\nAIR             |                    |          |       |\nDXIL/DXIR       |                    |          |       |\nDXBC            |                    |          |       |\nDOT (GraphViz)  | :ok:               | dot-out  | Not a shading language |\n\n:white_check_mark: = Primary support — :ok: = Secondary support — :construction: = Unsupported, but support in progress\n\n## Conversion tool\n\nNaga can be used as a CLI, which allows testing the conversion of different code paths.\n\nFirst, install `naga-cli` from crates.io or directly from GitHub.\n\n```bash\n# release version\ncargo install naga-cli\n\n# development version\ncargo install naga-cli --git https://github.com/gfx-rs/wgpu.git\n```\n\nThen, you can run `naga` command.\n\n```bash\nnaga my_shader.wgsl # validate only\nnaga my_shader.spv my_shader.txt # dump the IR module into a file\nnaga my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Metal, also dump the SPIR-V flow graph to `flow-dir`\nnaga my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile\n```\n\nAs naga includes a default binary target, you can also use `cargo run` without installation. This is useful when you develop naga itself or investigate the behavior of naga at a specific commit (e.g. [wgpu](https://github.com/gfx-rs/wgpu) might pin a different version of naga than the `HEAD` of this repository).\n\n```bash\ncargo run my_shader.wgsl\n```\n\n## Development workflow\n\nThe main instrument aiding the development is the good old `cargo test --all-features --workspace`,\nwhich will run the unit tests and also update all the snapshots. You'll see these\nchanges in git before committing the code.\n\nIf working on a particular front-end or back-end, it may be convenient to\nenable the relevant features in `Cargo.toml`, e.g.\n```toml\ndefault = [\"spv-out\"] #TEMP!\n```\nThis allows IDE basic checks to report errors there unless your IDE is sufficiently configurable already.\n\nFinally, when changes to the snapshots are made, we should verify that the produced shaders\nare indeed valid for the target platforms they are compiled for:\n```bash\ncargo xtask validate spv # for Vulkan shaders, requires SPIRV-Tools installed\ncargo xtask validate msl # for Metal shaders, requires XCode command-line tools installed\ncargo xtask validate glsl # for OpenGL shaders, requires GLSLang installed\ncargo xtask validate dot # for dot files, requires GraphViz installed\ncargo xtask validate wgsl # for WGSL shaders\ncargo xtask validate hlsl dxc # for HLSL shaders via DXC\ncargo xtask validate hlsl fxc # for HLSL shaders via FXC\n```\n"
  },
  {
    "path": "naga/build.rs",
    "content": "fn main() {\n    cfg_aliases::cfg_aliases! {\n        dot_out: { feature = \"dot-out\" },\n        glsl_out: { feature = \"glsl-out\" },\n        hlsl_out: { any(feature = \"hlsl-out\", all(target_os = \"windows\", feature = \"hlsl-out-if-target-windows\")) },\n        msl_out: { any(feature = \"msl-out\", all(target_vendor = \"apple\", feature = \"msl-out-if-target-apple\")) },\n        spv_out: { feature = \"spv-out\" },\n        wgsl_out: { feature = \"wgsl-out\" },\n        std: { any(test, feature = \"wgsl-in\", feature = \"stderr\", feature = \"fs\") },\n        no_std: { not(std) },\n    }\n}\n"
  },
  {
    "path": "naga/fuzz/.gitignore",
    "content": "target\ncorpus\nartifacts\n"
  },
  {
    "path": "naga/fuzz/Cargo.toml",
    "content": "[package]\nname = \"naga-fuzz\"\nversion.workspace = true\nauthors.workspace = true\npublish = false\nedition.workspace = true\nlicense.workspace = true\nbuild = \"build.rs\"\n\n[package.metadata]\ncargo-fuzz = true\n\n[target.'cfg(not(any(target_arch = \"wasm32\", target_os = \"ios\")))'.dependencies]\narbitrary = { workspace = true, features = [\"derive\"] }\nlibfuzzer-sys.workspace = true\n\n[target.'cfg(not(any(target_arch = \"wasm32\", target_os = \"ios\", target_os = \"visionos\")))'.dependencies.naga]\nworkspace = true\nfeatures = [\"arbitrary\", \"spv-in\", \"wgsl-in\", \"glsl-in\"]\n\n[build-dependencies]\ncfg_aliases.workspace = true\n\n[[bin]]\nname = \"spv_parser\"\npath = \"fuzz_targets/spv_parser.rs\"\nbench = false\ntest = false\ndoc = false\n\n[[bin]]\nname = \"wgsl_parser\"\npath = \"fuzz_targets/wgsl_parser.rs\"\nbench = false\ntest = false\ndoc = false\n\n[[bin]]\nname = \"glsl_parser\"\npath = \"fuzz_targets/glsl_parser.rs\"\nbench = false\ntest = false\ndoc = false\n\n[[bin]]\nname = \"ir\"\npath = \"fuzz_targets/ir.rs\"\nbench = false\ntest = false\ndoc = false\n\n[lints.clippy]\ndisallowed_types = \"allow\"\n"
  },
  {
    "path": "naga/fuzz/build.rs",
    "content": "fn main() {\n    cfg_aliases::cfg_aliases! {\n        fuzzable_platform: { not(any(target_arch = \"wasm32\", target_os = \"ios\", all(windows, target_arch = \"aarch64\"))) },\n    }\n    // This cfg provided by cargo-fuzz\n    println!(\"cargo::rustc-check-cfg=cfg(fuzzing)\");\n}\n"
  },
  {
    "path": "naga/fuzz/fuzz_targets/glsl_parser.rs",
    "content": "#![cfg_attr(all(fuzzable_platform, fuzzing), no_main)]\n\n#[cfg(all(fuzzable_platform, fuzzing))]\nmod fuzz {\n    use std::iter::FromIterator;\n\n    use arbitrary::Arbitrary;\n    use libfuzzer_sys::fuzz_target;\n    use naga::{\n        front::glsl::{Frontend, Options},\n        FastHashMap, ShaderStage,\n    };\n\n    #[derive(Debug, Arbitrary)]\n    struct OptionsProxy {\n        pub stage: ShaderStage,\n        pub defines: std::collections::HashMap<String, String>,\n    }\n\n    impl From<OptionsProxy> for Options {\n        fn from(proxy: OptionsProxy) -> Self {\n            Options {\n                stage: proxy.stage,\n                // NOTE: This is a workaround needed due to lack of rust-fuzz/arbitrary support for hashbrown.\n                defines: FastHashMap::from_iter(\n                    proxy\n                        .defines\n                        .keys()\n                        .map(|k| (k.clone(), proxy.defines.get(&k.clone()).unwrap().clone())),\n                ),\n            }\n        }\n    }\n\n    fuzz_target!(|data: (OptionsProxy, String)| {\n        let (options, source) = data;\n        // Ensure the parser can handle potentially malformed strings without crashing.\n        let mut parser = Frontend::default();\n        let _result = parser.parse(&options.into(), &source);\n    });\n}\n\n#[cfg(not(all(fuzzable_platform, fuzzing)))]\nfn main() {}\n"
  },
  {
    "path": "naga/fuzz/fuzz_targets/ir.rs",
    "content": "#![cfg_attr(all(fuzzable_platform, fuzzing), no_main)]\n\n#[cfg(all(fuzzable_platform, fuzzing))]\nmod fuzz {\n    use libfuzzer_sys::fuzz_target;\n\n    fuzz_target!(|module: naga::Module| {\n        use naga::valid as v;\n        // Check if the module validates without errors.\n        //TODO: may also fuzz the flags and capabilities\n        let mut validator =\n            v::Validator::new(v::ValidationFlags::all(), v::Capabilities::default());\n        let _result = validator.validate(&module);\n    });\n}\n\n#[cfg(not(all(fuzzable_platform, fuzzing)))]\nfn main() {}\n"
  },
  {
    "path": "naga/fuzz/fuzz_targets/spv_parser.rs",
    "content": "#![cfg_attr(all(fuzzable_platform, fuzzing), no_main)]\n\n#[cfg(all(fuzzable_platform, fuzzing))]\nmod fuzz {\n    use libfuzzer_sys::fuzz_target;\n    use naga::front::spv::{Frontend, Options};\n\n    fuzz_target!(|data: Vec<u32>| {\n        // Ensure the parser can handle potentially malformed data without crashing.\n        let options = Options::default();\n        let _result = Frontend::new(data.into_iter(), &options).parse();\n    });\n}\n\n#[cfg(not(all(fuzzable_platform, fuzzing)))]\nfn main() {}\n"
  },
  {
    "path": "naga/fuzz/fuzz_targets/wgsl_parser.rs",
    "content": "#![cfg_attr(all(fuzzable_platform, fuzzing), no_main)]\n\n#[cfg(all(fuzzable_platform, fuzzing))]\nmod fuzz {\n    use libfuzzer_sys::fuzz_target;\n    use naga::front::wgsl::Frontend;\n\n    fuzz_target!(|data: String| {\n        // Ensure the parser can handle potentially malformed strings without crashing.\n        let _result = Frontend::new().parse(&data);\n    });\n}\n\n#[cfg(not(all(fuzzable_platform, fuzzing)))]\nfn main() {}\n"
  },
  {
    "path": "naga/hlsl-snapshots/Cargo.toml",
    "content": "[package]\nname = \"hlsl-snapshots\"\nversion.workspace = true\nedition.workspace = true\npublish = false\nlicense.workspace = true\n\n[lib]\nname = \"hlsl_snapshots\"\npath = \"src/lib.rs\"\ntest = false\n\n[dependencies]\nanyhow.workspace = true\nnanoserde.workspace = true\n"
  },
  {
    "path": "naga/hlsl-snapshots/src/lib.rs",
    "content": "use std::{error::Error, fmt::Display, fs, io, path::Path};\n\nuse anyhow::{anyhow, ensure};\nuse nanoserde::{self, DeRon, DeRonErr, SerRon};\n\n#[derive(Debug)]\nstruct BadRonParse(BadRonParseKind);\n\nimpl Display for BadRonParse {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"failed to read RON configuration of HLSL snapshot test\")\n    }\n}\n\nimpl Error for BadRonParse {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        Some(&self.0)\n    }\n}\n\n#[derive(Debug)]\nenum BadRonParseKind {\n    Read { source: io::Error },\n    Parse { source: DeRonErr },\n    Empty,\n}\n\nimpl Display for BadRonParseKind {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            BadRonParseKind::Read { source } => Display::fmt(source, f),\n            BadRonParseKind::Parse { source } => Display::fmt(source, f),\n            BadRonParseKind::Empty => write!(f, \"no configuration was specified\"),\n        }\n    }\n}\n\nimpl Error for BadRonParseKind {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        match self {\n            BadRonParseKind::Read { source } => source.source(),\n            BadRonParseKind::Parse { source } => source.source(),\n            BadRonParseKind::Empty => None,\n        }\n    }\n}\n\n#[derive(Debug, DeRon, SerRon)]\npub struct Config {\n    pub vertex: Vec<ConfigItem>,\n    pub fragment: Vec<ConfigItem>,\n    pub compute: Vec<ConfigItem>,\n}\n\nimpl Config {\n    #[must_use]\n    pub fn empty() -> Self {\n        Self {\n            vertex: Default::default(),\n            fragment: Default::default(),\n            compute: Default::default(),\n        }\n    }\n\n    pub fn from_path(path: impl AsRef<Path>) -> anyhow::Result<Config> {\n        let path = path.as_ref();\n        let raw_config = fs::read_to_string(path)\n            .map_err(|source| BadRonParse(BadRonParseKind::Read { source }))?;\n        let config = Config::deserialize_ron(&raw_config)\n            .map_err(|source| BadRonParse(BadRonParseKind::Parse { source }))?;\n        ensure!(!config.is_empty(), BadRonParse(BadRonParseKind::Empty));\n        Ok(config)\n    }\n\n    pub fn to_file(&self, path: impl AsRef<Path>) -> anyhow::Result<()> {\n        let path = path.as_ref();\n        let mut s = self.serialize_ron();\n        s.push('\\n');\n        fs::write(path, &s).map_err(|e| anyhow!(\"failed to write to {}: {e}\", path.display()))\n    }\n\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        let Self {\n            vertex,\n            fragment,\n            compute,\n        } = self;\n        vertex.is_empty() && fragment.is_empty() && compute.is_empty()\n    }\n}\n\n#[derive(Debug, DeRon, SerRon)]\npub struct ConfigItem {\n    pub entry_point: String,\n    /// See also\n    /// <https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using>.\n    pub target_profile: String,\n}\n"
  },
  {
    "path": "naga/src/arena/handle.rs",
    "content": "//! Well-typed indices into [`Arena`]s and [`UniqueArena`]s.\n//!\n//! This module defines [`Handle`] and related types.\n//!\n//! [`Arena`]: super::Arena\n//! [`UniqueArena`]: super::UniqueArena\n\nuse core::{cmp::Ordering, fmt, hash, marker::PhantomData};\n\n/// An unique index in the arena array that a handle points to.\n/// The \"non-max\" part ensures that an `Option<Handle<T>>` has\n/// the same size and representation as `Handle<T>`.\npub type Index = crate::non_max_u32::NonMaxU32;\n\n#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq)]\n#[error(\"Handle {index} of {kind} is either not present, or inaccessible yet\")]\npub struct BadHandle {\n    pub kind: &'static str,\n    pub index: usize,\n}\n\nimpl BadHandle {\n    pub fn new<T>(handle: Handle<T>) -> Self {\n        Self {\n            kind: core::any::type_name::<T>(),\n            index: handle.index(),\n        }\n    }\n}\n\n/// A strongly typed reference to an arena item.\n///\n/// A `Handle` value can be used as an index into an [`Arena`] or [`UniqueArena`].\n///\n/// [`Arena`]: super::Arena\n/// [`UniqueArena`]: super::UniqueArena\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(\n    any(feature = \"serialize\", feature = \"deserialize\"),\n    serde(transparent)\n)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub struct Handle<T> {\n    index: Index,\n    #[cfg_attr(any(feature = \"serialize\", feature = \"deserialize\"), serde(skip))]\n    marker: PhantomData<T>,\n}\n\nimpl<T> Clone for Handle<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> Copy for Handle<T> {}\n\nimpl<T> PartialEq for Handle<T> {\n    fn eq(&self, other: &Self) -> bool {\n        self.index == other.index\n    }\n}\n\nimpl<T> Eq for Handle<T> {}\n\nimpl<T> PartialOrd for Handle<T> {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl<T> Ord for Handle<T> {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.index.cmp(&other.index)\n    }\n}\n\nimpl<T> fmt::Debug for Handle<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(formatter, \"[{}]\", self.index)\n    }\n}\n\nimpl<T> hash::Hash for Handle<T> {\n    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {\n        self.index.hash(hasher)\n    }\n}\n\nimpl<T> Handle<T> {\n    pub(crate) const fn new(index: Index) -> Self {\n        Handle {\n            index,\n            marker: PhantomData,\n        }\n    }\n\n    /// Returns the index of this handle.\n    pub const fn index(self) -> usize {\n        self.index.get() as usize\n    }\n\n    /// Convert a `usize` index into a `Handle<T>`.\n    pub(super) fn from_usize(index: usize) -> Self {\n        let handle_index = u32::try_from(index)\n            .ok()\n            .and_then(Index::new)\n            .expect(\"Failed to insert into arena. Handle overflows\");\n        Handle::new(handle_index)\n    }\n\n    /// Write this handle's index to `formatter`, preceded by `prefix`.\n    pub fn write_prefixed(\n        &self,\n        formatter: &mut fmt::Formatter,\n        prefix: &'static str,\n    ) -> fmt::Result {\n        formatter.write_str(prefix)?;\n        <usize as fmt::Display>::fmt(&self.index(), formatter)\n    }\n}\n"
  },
  {
    "path": "naga/src/arena/handle_set.rs",
    "content": "//! The [`HandleSet`] type and associated definitions.\n\nuse crate::arena::{Arena, Handle, UniqueArena};\n\n/// A set of `Handle<T>` values.\n#[derive(Debug)]\npub struct HandleSet<T> {\n    /// Bound on indexes of handles stored in this set.\n    len: usize,\n\n    /// `members[i]` is true if the handle with index `i` is a member.\n    members: bit_set::BitSet,\n\n    /// This type is indexed by values of type `T`.\n    as_keys: core::marker::PhantomData<T>,\n}\n\nimpl<T> HandleSet<T> {\n    /// Return a new, empty `HandleSet`.\n    pub fn new() -> Self {\n        Self {\n            len: 0,\n            members: bit_set::BitSet::new(),\n            as_keys: core::marker::PhantomData,\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.members.is_empty()\n    }\n\n    /// Return a new, empty `HandleSet`, sized to hold handles from `arena`.\n    pub fn for_arena(arena: &impl ArenaType<T>) -> Self {\n        let len = arena.len();\n        Self {\n            len,\n            members: bit_set::BitSet::with_capacity(len),\n            as_keys: core::marker::PhantomData,\n        }\n    }\n\n    /// Remove all members from `self`.\n    pub fn clear(&mut self) {\n        self.members.make_empty();\n    }\n\n    /// Remove all members from `self`, and reserve space to hold handles from `arena`.\n    pub fn clear_for_arena(&mut self, arena: &impl ArenaType<T>) {\n        self.members.make_empty();\n        self.members.reserve_len(arena.len());\n    }\n\n    /// Return an iterator over all handles that could be made members\n    /// of this set.\n    pub fn all_possible(&self) -> impl Iterator<Item = Handle<T>> {\n        super::Range::full_range_from_size(self.len)\n    }\n\n    /// Add `handle` to the set.\n    ///\n    /// Return `true` if `handle` was not already present in the set.\n    pub fn insert(&mut self, handle: Handle<T>) -> bool {\n        self.members.insert(handle.index())\n    }\n\n    /// Remove `handle` from the set.\n    ///\n    /// Returns `true` if `handle` was present in the set.\n    pub fn remove(&mut self, handle: Handle<T>) -> bool {\n        self.members.remove(handle.index())\n    }\n\n    /// Add handles from `iter` to the set.\n    pub fn insert_iter(&mut self, iter: impl IntoIterator<Item = Handle<T>>) {\n        for handle in iter {\n            self.insert(handle);\n        }\n    }\n\n    /// Add all of the handles that can be included in this set.\n    pub fn add_all(&mut self) {\n        self.members.get_mut().fill(true);\n    }\n\n    pub fn contains(&self, handle: Handle<T>) -> bool {\n        self.members.contains(handle.index())\n    }\n\n    /// Return an iterator over all handles in `self`.\n    pub fn iter(&self) -> impl '_ + Iterator<Item = Handle<T>> {\n        self.members.iter().map(Handle::from_usize)\n    }\n\n    /// Removes and returns the numerically largest handle in the set, or `None`\n    /// if the set is empty.\n    pub fn pop(&mut self) -> Option<Handle<T>> {\n        let members = core::mem::take(&mut self.members);\n        let mut vec = members.into_bit_vec();\n        let result = vec.iter_mut().enumerate().rev().find_map(|(i, mut bit)| {\n            if *bit {\n                *bit = false;\n                Some(i)\n            } else {\n                None\n            }\n        });\n        self.members = bit_set::BitSet::from_bit_vec(vec);\n        result.map(Handle::from_usize)\n    }\n}\n\nimpl<T> Default for HandleSet<T> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub trait ArenaType<T> {\n    fn len(&self) -> usize;\n}\n\nimpl<T> ArenaType<T> for Arena<T> {\n    fn len(&self) -> usize {\n        self.len()\n    }\n}\n\nimpl<T: core::hash::Hash + Eq> ArenaType<T> for UniqueArena<T> {\n    fn len(&self) -> usize {\n        self.len()\n    }\n}\n"
  },
  {
    "path": "naga/src/arena/handlevec.rs",
    "content": "//! The [`HandleVec`] type and associated definitions.\n\nuse super::handle::Handle;\nuse alloc::{vec, vec::Vec};\n\nuse core::marker::PhantomData;\nuse core::ops;\n\n/// A [`Vec`] indexed by [`Handle`]s.\n///\n/// A `HandleVec<T, U>` is a [`Vec<U>`] indexed by values of type `Handle<T>`,\n/// rather than `usize`.\n///\n/// Rather than a `push` method, `HandleVec` has an [`insert`] method, analogous\n/// to [`HashMap::insert`], that requires you to provide the handle at which the\n/// new value should appear. However, since `HandleVec` only supports insertion\n/// at the end, the given handle's index must be equal to the `HandleVec`'s\n/// current length; otherwise, the insertion will panic.\n///\n/// [`insert`]: HandleVec::insert\n/// [`HashMap::insert`]: hashbrown::HashMap::insert\n#[derive(Debug)]\npub(crate) struct HandleVec<T, U> {\n    inner: Vec<U>,\n    as_keys: PhantomData<T>,\n}\n\nimpl<T, U> Default for HandleVec<T, U> {\n    fn default() -> Self {\n        Self {\n            inner: vec![],\n            as_keys: PhantomData,\n        }\n    }\n}\n\n#[allow(dead_code)]\nimpl<T, U> HandleVec<T, U> {\n    pub(crate) const fn new() -> Self {\n        Self {\n            inner: vec![],\n            as_keys: PhantomData,\n        }\n    }\n\n    pub(crate) fn with_capacity(capacity: usize) -> Self {\n        Self {\n            inner: Vec::with_capacity(capacity),\n            as_keys: PhantomData,\n        }\n    }\n\n    pub(crate) const fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Insert a mapping from `handle` to `value`.\n    ///\n    /// Unlike a [`HashMap`], a `HandleVec` can only have new entries inserted at\n    /// the end, like [`Vec::push`]. So the index of `handle` must equal\n    /// [`self.len()`].\n    ///\n    /// [`HashMap`]: hashbrown::HashMap\n    /// [`self.len()`]: HandleVec::len\n    pub(crate) fn insert(&mut self, handle: Handle<T>, value: U) {\n        assert_eq!(handle.index(), self.inner.len());\n        self.inner.push(value);\n    }\n\n    pub(crate) fn get(&self, handle: Handle<T>) -> Option<&U> {\n        self.inner.get(handle.index())\n    }\n\n    pub(crate) fn clear(&mut self) {\n        self.inner.clear()\n    }\n\n    pub(crate) fn resize(&mut self, len: usize, fill: U)\n    where\n        U: Clone,\n    {\n        self.inner.resize(len, fill);\n    }\n\n    pub(crate) fn iter(&self) -> impl Iterator<Item = &U> {\n        self.inner.iter()\n    }\n\n    pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut U> {\n        self.inner.iter_mut()\n    }\n}\n\nimpl<T, U> ops::Index<Handle<T>> for HandleVec<T, U> {\n    type Output = U;\n\n    fn index(&self, handle: Handle<T>) -> &Self::Output {\n        &self.inner[handle.index()]\n    }\n}\n\nimpl<T, U> ops::IndexMut<Handle<T>> for HandleVec<T, U> {\n    fn index_mut(&mut self, handle: Handle<T>) -> &mut Self::Output {\n        &mut self.inner[handle.index()]\n    }\n}\n"
  },
  {
    "path": "naga/src/arena/mod.rs",
    "content": "/*! The [`Arena`], [`UniqueArena`], and [`Handle`] types.\n\nTo improve translator performance and reduce memory usage, most structures are\nstored in an [`Arena`]. An `Arena<T>` stores a series of `T` values, indexed by\n[`Handle<T>`] values, which are just wrappers around integer indexes.\nFor example, a `Function`'s expressions are stored in an `Arena<Expression>`,\nand compound expressions refer to their sub-expressions via `Handle<Expression>`\nvalues.\n\nA [`UniqueArena`] is just like an `Arena`, except that it stores only a single\ninstance of each value. The value type must implement `Eq` and `Hash`. Like an\n`Arena`, inserting a value into a `UniqueArena` returns a `Handle` which can be\nused to efficiently access the value, without a hash lookup. Inserting a value\nmultiple times returns the same `Handle`.\n\nIf the `span` feature is enabled, both `Arena` and `UniqueArena` can associate a\nsource code span with each element.\n\n[`Handle<T>`]: Handle\n*/\n\nmod handle;\nmod handle_set;\nmod handlevec;\nmod range;\nmod unique_arena;\n\npub use handle::{BadHandle, Handle};\npub(crate) use handle_set::HandleSet;\npub(crate) use handlevec::HandleVec;\npub use range::{BadRangeError, Range};\npub use unique_arena::UniqueArena;\n\nuse alloc::vec::Vec;\nuse core::{fmt, ops};\n\nuse crate::Span;\n\nuse handle::Index;\n\n/// An arena holding some kind of component (e.g., type, constant,\n/// instruction, etc.) that can be referenced.\n///\n/// Adding new items to the arena produces a strongly-typed [`Handle`].\n/// The arena can be indexed using the given handle to obtain\n/// a reference to the stored item.\n#[derive(Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"serialize\", serde(transparent))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Arena<T> {\n    /// Values of this arena.\n    data: Vec<T>,\n    #[cfg_attr(feature = \"serialize\", serde(skip))]\n    span_info: Vec<Span>,\n}\n\nimpl<T> Default for Arena<T> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for Arena<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_map().entries(self.iter()).finish()\n    }\n}\n\nimpl<T> Arena<T> {\n    /// Create a new arena with no initial capacity allocated.\n    pub const fn new() -> Self {\n        Arena {\n            data: Vec::new(),\n            span_info: Vec::new(),\n        }\n    }\n\n    /// Extracts the inner vector.\n    pub fn into_inner(self) -> Vec<T> {\n        self.data\n    }\n\n    /// Returns the current number of items stored in this arena.\n    pub const fn len(&self) -> usize {\n        self.data.len()\n    }\n\n    /// Returns `true` if the arena contains no elements.\n    pub const fn is_empty(&self) -> bool {\n        self.data.is_empty()\n    }\n\n    /// Returns an iterator over the items stored in this arena, returning both\n    /// the item's handle and a reference to it.\n    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (Handle<T>, &T)> + ExactSizeIterator {\n        self.data\n            .iter()\n            .enumerate()\n            .map(|(i, v)| (Handle::from_usize(i), v))\n    }\n\n    /// Returns an iterator over the items stored in this arena, returning both\n    /// the item's handle and a reference to it.\n    pub fn iter_mut_span(\n        &mut self,\n    ) -> impl DoubleEndedIterator<Item = (Handle<T>, &mut T, &Span)> + ExactSizeIterator {\n        self.data\n            .iter_mut()\n            .zip(self.span_info.iter())\n            .enumerate()\n            .map(|(i, (v, span))| (Handle::from_usize(i), v, span))\n    }\n\n    /// Drains the arena, returning an iterator over the items stored.\n    pub fn drain(&mut self) -> impl DoubleEndedIterator<Item = (Handle<T>, T, Span)> {\n        let arena = core::mem::take(self);\n        arena\n            .data\n            .into_iter()\n            .zip(arena.span_info)\n            .enumerate()\n            .map(|(i, (v, span))| (Handle::from_usize(i), v, span))\n    }\n\n    /// Returns a iterator over the items stored in this arena,\n    /// returning both the item's handle and a mutable reference to it.\n    pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = (Handle<T>, &mut T)> {\n        self.data\n            .iter_mut()\n            .enumerate()\n            .map(|(i, v)| (Handle::from_usize(i), v))\n    }\n\n    /// Adds a new value to the arena, returning a typed handle.\n    pub fn append(&mut self, value: T, span: Span) -> Handle<T> {\n        let index = self.data.len();\n        self.data.push(value);\n        self.span_info.push(span);\n        Handle::from_usize(index)\n    }\n\n    /// Fetch a handle to an existing type.\n    pub fn fetch_if<F: Fn(&T) -> bool>(&self, fun: F) -> Option<Handle<T>> {\n        self.data\n            .iter()\n            .position(fun)\n            .map(|index| Handle::from_usize(index))\n    }\n\n    /// Adds a value with a custom check for uniqueness:\n    /// returns a handle pointing to\n    /// an existing element if the check succeeds, or adds a new\n    /// element otherwise.\n    pub fn fetch_if_or_append<F: Fn(&T, &T) -> bool>(\n        &mut self,\n        value: T,\n        span: Span,\n        fun: F,\n    ) -> Handle<T> {\n        if let Some(index) = self.data.iter().position(|d| fun(d, &value)) {\n            Handle::from_usize(index)\n        } else {\n            self.append(value, span)\n        }\n    }\n\n    /// Adds a value with a check for uniqueness, where the check is plain comparison.\n    pub fn fetch_or_append(&mut self, value: T, span: Span) -> Handle<T>\n    where\n        T: PartialEq,\n    {\n        self.fetch_if_or_append(value, span, T::eq)\n    }\n\n    pub fn try_get(&self, handle: Handle<T>) -> Result<&T, BadHandle> {\n        self.data\n            .get(handle.index())\n            .ok_or_else(|| BadHandle::new(handle))\n    }\n\n    /// Get a mutable reference to an element in the arena.\n    pub fn get_mut(&mut self, handle: Handle<T>) -> &mut T {\n        self.data.get_mut(handle.index()).unwrap()\n    }\n\n    /// Get the range of handles from a particular number of elements to the end.\n    pub fn range_from(&self, old_length: usize) -> Range<T> {\n        let range = old_length as u32..self.data.len() as u32;\n        Range::from_index_range(range, self)\n    }\n\n    /// Clears the arena keeping all allocations\n    pub fn clear(&mut self) {\n        self.data.clear()\n    }\n\n    pub fn get_span(&self, handle: Handle<T>) -> Span {\n        *self\n            .span_info\n            .get(handle.index())\n            .unwrap_or(&Span::default())\n    }\n\n    /// Assert that `handle` is valid for this arena.\n    pub fn check_contains_handle(&self, handle: Handle<T>) -> Result<(), BadHandle> {\n        if handle.index() < self.data.len() {\n            Ok(())\n        } else {\n            Err(BadHandle::new(handle))\n        }\n    }\n\n    /// Assert that `range` is valid for this arena.\n    pub fn check_contains_range(&self, range: &Range<T>) -> Result<(), BadRangeError> {\n        // Since `range.inner` is a `Range<u32>`, we only need to check that the\n        // start precedes the end, and that the end is in range.\n        if range.inner.start > range.inner.end {\n            return Err(BadRangeError::new(range.clone()));\n        }\n\n        // Empty ranges are tolerated: they can be produced by compaction.\n        if range.inner.start == range.inner.end {\n            return Ok(());\n        }\n\n        let last_handle = Handle::new(Index::new(range.inner.end - 1).unwrap());\n        if self.check_contains_handle(last_handle).is_err() {\n            return Err(BadRangeError::new(range.clone()));\n        }\n\n        Ok(())\n    }\n\n    pub(crate) fn retain_mut<P>(&mut self, mut predicate: P)\n    where\n        P: FnMut(Handle<T>, &mut T) -> bool,\n    {\n        let mut index = 0;\n        let mut retained = 0;\n        self.data.retain_mut(|elt| {\n            let handle = Handle::from_usize(index);\n            let keep = predicate(handle, elt);\n\n            // Since `predicate` needs mutable access to each element,\n            // we can't feasibly call it twice, so we have to compact\n            // spans by hand in parallel as part of this iteration.\n            if keep {\n                self.span_info[retained] = self.span_info[index];\n                retained += 1;\n            }\n\n            index += 1;\n            keep\n        });\n\n        self.span_info.truncate(retained);\n    }\n}\n\n#[cfg(feature = \"deserialize\")]\nimpl<'de, T> serde::Deserialize<'de> for Arena<T>\nwhere\n    T: serde::Deserialize<'de>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let data = Vec::deserialize(deserializer)?;\n        let span_info = core::iter::repeat_n(Span::default(), data.len()).collect();\n\n        Ok(Self { data, span_info })\n    }\n}\n\nimpl<T> ops::Index<Handle<T>> for Arena<T> {\n    type Output = T;\n    fn index(&self, handle: Handle<T>) -> &T {\n        &self.data[handle.index()]\n    }\n}\n\nimpl<T> ops::IndexMut<Handle<T>> for Arena<T> {\n    fn index_mut(&mut self, handle: Handle<T>) -> &mut T {\n        &mut self.data[handle.index()]\n    }\n}\n\nimpl<T> ops::Index<Range<T>> for Arena<T> {\n    type Output = [T];\n    fn index(&self, range: Range<T>) -> &[T] {\n        &self.data[range.inner.start as usize..range.inner.end as usize]\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn append_non_unique() {\n        let mut arena: Arena<u8> = Arena::new();\n        let t1 = arena.append(0, Default::default());\n        let t2 = arena.append(0, Default::default());\n        assert!(t1 != t2);\n        assert!(arena[t1] == arena[t2]);\n    }\n\n    #[test]\n    fn append_unique() {\n        let mut arena: Arena<u8> = Arena::new();\n        let t1 = arena.append(0, Default::default());\n        let t2 = arena.append(1, Default::default());\n        assert!(t1 != t2);\n        assert!(arena[t1] != arena[t2]);\n    }\n\n    #[test]\n    fn fetch_or_append_non_unique() {\n        let mut arena: Arena<u8> = Arena::new();\n        let t1 = arena.fetch_or_append(0, Default::default());\n        let t2 = arena.fetch_or_append(0, Default::default());\n        assert!(t1 == t2);\n        assert!(arena[t1] == arena[t2])\n    }\n\n    #[test]\n    fn fetch_or_append_unique() {\n        let mut arena: Arena<u8> = Arena::new();\n        let t1 = arena.fetch_or_append(0, Default::default());\n        let t2 = arena.fetch_or_append(1, Default::default());\n        assert!(t1 != t2);\n        assert!(arena[t1] != arena[t2]);\n    }\n}\n"
  },
  {
    "path": "naga/src/arena/range.rs",
    "content": "//! Well-typed ranges of [`Arena`]s.\n//!\n//! This module defines the [`Range`] type, representing a contiguous range of\n//! entries in an [`Arena`].\n//!\n//! [`Arena`]: super::Arena\n\nuse core::{fmt, marker::PhantomData, ops};\n\nuse super::{\n    handle::{Handle, Index},\n    Arena,\n};\n\n/// A strongly typed range of handles.\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(\n    any(feature = \"serialize\", feature = \"deserialize\"),\n    serde(transparent)\n)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Range<T> {\n    pub(super) inner: ops::Range<u32>,\n    #[cfg_attr(any(feature = \"serialize\", feature = \"deserialize\"), serde(skip))]\n    marker: PhantomData<T>,\n}\n\nimpl<T> Range<T> {\n    pub(crate) const fn erase_type(self) -> Range<()> {\n        let Self { inner, marker: _ } = self;\n        Range {\n            inner,\n            marker: PhantomData,\n        }\n    }\n}\n\n// NOTE: Keep this diagnostic in sync with that of [`BadHandle`].\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\n#[error(\"Handle range {range:?} of {kind} is either not present, or inaccessible yet\")]\npub struct BadRangeError {\n    // This error is used for many `Handle` types, but there's no point in making this generic, so\n    // we just flatten them all to `Handle<()>` here.\n    kind: &'static str,\n    range: Range<()>,\n}\n\nimpl BadRangeError {\n    pub fn new<T>(range: Range<T>) -> Self {\n        Self {\n            kind: core::any::type_name::<T>(),\n            range: range.erase_type(),\n        }\n    }\n}\n\nimpl<T> Clone for Range<T> {\n    fn clone(&self) -> Self {\n        Range {\n            inner: self.inner.clone(),\n            marker: self.marker,\n        }\n    }\n}\n\nimpl<T> fmt::Debug for Range<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(formatter, \"[{}..{}]\", self.inner.start, self.inner.end)\n    }\n}\n\nimpl<T> Iterator for Range<T> {\n    type Item = Handle<T>;\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.inner.start < self.inner.end {\n            let next = self.inner.start;\n            self.inner.start += 1;\n            Some(Handle::new(Index::new(next).unwrap()))\n        } else {\n            None\n        }\n    }\n}\n\nimpl<T> Range<T> {\n    /// Return a range enclosing handles `first` through `last`, inclusive.\n    pub fn new_from_bounds(first: Handle<T>, last: Handle<T>) -> Self {\n        Self {\n            inner: (first.index() as u32)..(last.index() as u32 + 1),\n            marker: Default::default(),\n        }\n    }\n\n    /// Return a range covering all handles with indices from `0` to `size`.\n    pub(super) fn full_range_from_size(size: usize) -> Self {\n        Self {\n            inner: 0..size as u32,\n            marker: Default::default(),\n        }\n    }\n\n    /// return the first and last handles included in `self`.\n    ///\n    /// If `self` is an empty range, there are no handles included, so\n    /// return `None`.\n    pub const fn first_and_last(&self) -> Option<(Handle<T>, Handle<T>)> {\n        if self.inner.start < self.inner.end {\n            Some((\n                // `Range::new_from_bounds` expects a start- and end-inclusive\n                // range, but `self.inner` is an end-exclusive range.\n                Handle::new(Index::new(self.inner.start).unwrap()),\n                Handle::new(Index::new(self.inner.end - 1).unwrap()),\n            ))\n        } else {\n            None\n        }\n    }\n\n    /// Return the index range covered by `self`.\n    pub fn index_range(&self) -> ops::Range<u32> {\n        self.inner.clone()\n    }\n\n    /// Construct a `Range` that covers the indices in `inner`.\n    pub fn from_index_range(inner: ops::Range<u32>, arena: &Arena<T>) -> Self {\n        // Since `inner` is a `Range<u32>`, we only need to check that\n        // the start and end are well-ordered, and that the end fits\n        // within `arena`.\n        assert!(inner.start <= inner.end);\n        assert!(inner.end as usize <= arena.len());\n        Self {\n            inner,\n            marker: Default::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/arena/unique_arena.rs",
    "content": "//! The [`UniqueArena`] type and supporting definitions.\n\nuse alloc::vec::Vec;\nuse core::{fmt, hash, ops};\n\nuse super::handle::{BadHandle, Handle, Index};\nuse crate::{FastIndexSet, Span};\n\n/// An arena whose elements are guaranteed to be unique.\n///\n/// A `UniqueArena` holds a set of unique values of type `T`, each with an\n/// associated [`Span`]. Inserting a value returns a `Handle<T>`, which can be\n/// used to index the `UniqueArena` and obtain shared access to the `T` element.\n/// Access via a `Handle` is an array lookup - no hash lookup is necessary.\n///\n/// The element type must implement `Eq` and `Hash`. Insertions of equivalent\n/// elements, according to `Eq`, all return the same `Handle`.\n///\n/// Once inserted, elements generally may not be mutated, although a `replace`\n/// method exists to support rare cases.\n///\n/// `UniqueArena` is similar to [`Arena`]: If `Arena` is vector-like,\n/// `UniqueArena` is `HashSet`-like.\n///\n/// [`Arena`]: super::Arena\n#[derive(Clone)]\npub struct UniqueArena<T> {\n    set: FastIndexSet<T>,\n\n    /// Spans for the elements, indexed by handle.\n    ///\n    /// The length of this vector is always equal to `set.len()`. `FastIndexSet`\n    /// promises that its elements \"are indexed in a compact range, without\n    /// holes in the range 0..set.len()\", so we can always use the indices\n    /// returned by insertion as indices into this vector.\n    span_info: Vec<Span>,\n}\n\nimpl<T> UniqueArena<T> {\n    /// Create a new arena with no initial capacity allocated.\n    pub fn new() -> Self {\n        UniqueArena {\n            set: FastIndexSet::default(),\n            span_info: Vec::new(),\n        }\n    }\n\n    /// Return the current number of items stored in this arena.\n    pub fn len(&self) -> usize {\n        self.set.len()\n    }\n\n    /// Return `true` if the arena contains no elements.\n    pub fn is_empty(&self) -> bool {\n        self.set.is_empty()\n    }\n\n    /// Clears the arena, keeping all allocations.\n    pub fn clear(&mut self) {\n        self.set.clear();\n        self.span_info.clear();\n    }\n\n    /// Return the span associated with `handle`.\n    ///\n    /// If a value has been inserted multiple times, the span returned is the\n    /// one provided with the first insertion.\n    pub fn get_span(&self, handle: Handle<T>) -> Span {\n        *self\n            .span_info\n            .get(handle.index())\n            .unwrap_or(&Span::default())\n    }\n\n    pub(crate) fn drain_all(&mut self) -> UniqueArenaDrain<'_, T> {\n        UniqueArenaDrain {\n            inner_elts: self.set.drain(..),\n            inner_spans: self.span_info.drain(..),\n            index: Index::new(0).unwrap(),\n        }\n    }\n}\n\npub struct UniqueArenaDrain<'a, T> {\n    inner_elts: indexmap::set::Drain<'a, T>,\n    inner_spans: alloc::vec::Drain<'a, Span>,\n    index: Index,\n}\n\nimpl<T> Iterator for UniqueArenaDrain<'_, T> {\n    type Item = (Handle<T>, T, Span);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.inner_elts.next() {\n            Some(elt) => {\n                let handle = Handle::new(self.index);\n                self.index = self.index.checked_add(1).unwrap();\n                let span = self.inner_spans.next().unwrap();\n                Some((handle, elt, span))\n            }\n            None => None,\n        }\n    }\n}\n\nimpl<T: Eq + hash::Hash> UniqueArena<T> {\n    /// Returns an iterator over the items stored in this arena, returning both\n    /// the item's handle and a reference to it.\n    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (Handle<T>, &T)> + ExactSizeIterator {\n        self.set.iter().enumerate().map(|(i, v)| {\n            let index = Index::new(i as u32).unwrap();\n            (Handle::new(index), v)\n        })\n    }\n\n    /// Insert a new value into the arena.\n    ///\n    /// Return a [`Handle<T>`], which can be used to index this arena to get a\n    /// shared reference to the element.\n    ///\n    /// If this arena already contains an element that is `Eq` to `value`,\n    /// return a `Handle` to the existing element, and drop `value`.\n    ///\n    /// If `value` is inserted into the arena, associate `span` with\n    /// it. An element's span can be retrieved with the [`get_span`]\n    /// method.\n    ///\n    /// [`Handle<T>`]: Handle\n    /// [`get_span`]: UniqueArena::get_span\n    pub fn insert(&mut self, value: T, span: Span) -> Handle<T> {\n        let (index, added) = self.set.insert_full(value);\n\n        if added {\n            debug_assert!(index == self.span_info.len());\n            self.span_info.push(span);\n        }\n\n        debug_assert!(self.set.len() == self.span_info.len());\n\n        Handle::from_usize(index)\n    }\n\n    /// Replace an old value with a new value.\n    ///\n    /// # Panics\n    ///\n    /// - if the old value is not in the arena\n    /// - if the new value already exists in the arena\n    pub fn replace(&mut self, old: Handle<T>, new: T) {\n        let (index, added) = self.set.insert_full(new);\n        assert!(added && index == self.set.len() - 1);\n\n        self.set.swap_remove_index(old.index()).unwrap();\n    }\n\n    /// Return this arena's handle for `value`, if present.\n    ///\n    /// If this arena already contains an element equal to `value`,\n    /// return its handle. Otherwise, return `None`.\n    pub fn get(&self, value: &T) -> Option<Handle<T>> {\n        self.set\n            .get_index_of(value)\n            .map(|index| Handle::from_usize(index))\n    }\n\n    /// Return this arena's value at `handle`, if that is a valid handle.\n    pub fn get_handle(&self, handle: Handle<T>) -> Result<&T, BadHandle> {\n        self.set\n            .get_index(handle.index())\n            .ok_or_else(|| BadHandle::new(handle))\n    }\n\n    /// Assert that `handle` is valid for this arena.\n    pub fn check_contains_handle(&self, handle: Handle<T>) -> Result<(), BadHandle> {\n        if handle.index() < self.set.len() {\n            Ok(())\n        } else {\n            Err(BadHandle::new(handle))\n        }\n    }\n}\n\nimpl<T> Default for UniqueArena<T> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<T: fmt::Debug + Eq + hash::Hash> fmt::Debug for UniqueArena<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_map().entries(self.iter()).finish()\n    }\n}\n\nimpl<T> ops::Index<Handle<T>> for UniqueArena<T> {\n    type Output = T;\n    fn index(&self, handle: Handle<T>) -> &T {\n        &self.set[handle.index()]\n    }\n}\n\n#[cfg(feature = \"serialize\")]\nimpl<T> serde::Serialize for UniqueArena<T>\nwhere\n    T: Eq + hash::Hash + serde::Serialize,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.set.serialize(serializer)\n    }\n}\n\n#[cfg(feature = \"deserialize\")]\nimpl<'de, T> serde::Deserialize<'de> for UniqueArena<T>\nwhere\n    T: Eq + hash::Hash + serde::Deserialize<'de>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let set = FastIndexSet::deserialize(deserializer)?;\n        let span_info = core::iter::repeat_n(Span::default(), set.len()).collect();\n\n        Ok(Self { set, span_info })\n    }\n}\n\n//Note: largely borrowed from `HashSet` implementation\n#[cfg(feature = \"arbitrary\")]\nimpl<'a, T> arbitrary::Arbitrary<'a> for UniqueArena<T>\nwhere\n    T: Eq + hash::Hash + arbitrary::Arbitrary<'a>,\n{\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let mut arena = Self::default();\n        for elem in u.arbitrary_iter()? {\n            arena.set.insert(elem?);\n            arena.span_info.push(Span::UNDEFINED);\n        }\n        Ok(arena)\n    }\n\n    fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let mut arena = Self::default();\n        for elem in u.arbitrary_take_rest_iter()? {\n            arena.set.insert(elem?);\n            arena.span_info.push(Span::UNDEFINED);\n        }\n        Ok(arena)\n    }\n\n    #[inline]\n    fn size_hint(depth: usize) -> (usize, Option<usize>) {\n        let depth_hint = <usize as arbitrary::Arbitrary>::size_hint(depth);\n        arbitrary::size_hint::and(depth_hint, (0, None))\n    }\n}\n"
  },
  {
    "path": "naga/src/back/continue_forward.rs",
    "content": "//! Workarounds for platform bugs and limitations in switches and loops.\n//!\n//! In these docs, we use CamelCase links for Naga IR concepts, and ordinary\n//! `code` formatting for HLSL or GLSL concepts.\n//!\n//! ## Avoiding `continue` within `switch`\n//!\n//! As described in <https://github.com/gfx-rs/wgpu/issues/4485>, the FXC HLSL\n//! compiler doesn't allow `continue` statements within `switch` statements, but\n//! Naga IR does. We work around this by introducing synthetic boolean local\n//! variables and branches.\n//!\n//! Specifically:\n//!\n//! - We generate code for [`Continue`] statements within [`SwitchCase`]s that\n//!   sets an introduced `bool` local to `true` and does a `break`, jumping to\n//!   immediately after the generated `switch`.\n//!\n//! - When generating code for a [`Switch`] statement, we conservatively assume\n//!   it might contain such a [`Continue`] statement, so:\n//!\n//!   - If it's the outermost such [`Switch`] within a [`Loop`], we declare the\n//!     `bool` local ahead of the switch, initialized to `false`. Immediately\n//!     after the `switch`, we check the local and do a `continue` if it's set.\n//!\n//!   - If the [`Switch`] is nested within other [`Switch`]es, then after the\n//!     generated `switch`, we check the local (which we know was declared\n//!     before the surrounding `switch`) and do a `break` if it's set.\n//!\n//!   - As an optimization, we only generate the check of the local if a\n//!     [`Continue`] statement is encountered within the [`Switch`]. This may\n//!     help drivers more easily identify that the `bool` is unused.\n//!\n//! So while we \"weaken\" the [`Continue`] statement by rendering it as a `break`\n//! statement, we also place checks immediately at the locations to which those\n//! `break` statements will jump, until we can be sure we've reached the\n//! intended target of the original [`Continue`].\n//!\n//! In the case of nested [`Loop`] and [`Switch`] statements, there may be\n//! multiple introduced `bool` locals in scope, but there's no problem knowing\n//! which one to operate on. At any point, there is at most one [`Loop`]\n//! statement that could be targeted by a [`Continue`] statement, so the correct\n//! `bool` local to set and test is always the one introduced for the innermost\n//! enclosing [`Loop`]'s outermost [`Switch`].\n//!\n//! # Avoiding single body `switch` statements\n//!\n//! As described in <https://github.com/gfx-rs/wgpu/issues/4514>, some language\n//! front ends miscompile `switch` statements where all cases branch to the same\n//! body. Our HLSL and GLSL backends render [`Switch`] statements with a single\n//! [`SwitchCase`] as `do {} while(false);` loops.\n//!\n//! However, this rewriting introduces a new loop that could \"capture\"\n//! `continue` statements in its body. To avoid doing so, we apply the\n//! [`Continue`]-to-`break` transformation described above.\n//!\n//! [`Continue`]: crate::Statement::Continue\n//! [`Loop`]: crate::Statement::Loop\n//! [`Switch`]: crate::Statement::Switch\n//! [`SwitchCase`]: crate::SwitchCase\n\nuse alloc::{rc::Rc, string::String, vec::Vec};\n\nuse crate::proc::Namer;\n\n/// A summary of the code surrounding a statement.\nenum Nesting {\n    /// Currently nested in at least one [`Loop`] statement.\n    ///\n    /// [`Continue`] should apply to the innermost loop.\n    ///\n    /// When this entry is on the top of the stack:\n    ///\n    /// * When entering an inner [`Loop`] statement, push a [`Loop`][nl] state\n    ///   onto the stack.\n    ///\n    /// * When entering a nested [`Switch`] statement, push a [`Switch`][ns]\n    ///   state onto the stack with a new variable name. Before the generated\n    ///   `switch`, introduce a `bool` local with that name, initialized to\n    ///   `false`.\n    ///\n    /// When exiting the [`Loop`] for which this entry was pushed, pop it from\n    /// the stack.\n    ///\n    /// [`Continue`]: crate::Statement::Continue\n    /// [`Loop`]: crate::Statement::Loop\n    /// [`Switch`]: crate::Statement::Switch\n    /// [ns]: Nesting::Switch\n    /// [nl]: Nesting::Loop\n    Loop,\n\n    /// Currently nested in at least one [`Switch`] that may need to forward\n    /// [`Continue`]s.\n    ///\n    /// This includes [`Switch`]es rendered as `do {} while(false)` loops, but\n    /// doesn't need to include regular [`Switch`]es in backends that can\n    /// support `continue` within switches.\n    ///\n    /// [`Continue`] should be forwarded to the innermost surrounding [`Loop`].\n    ///\n    /// When this entry is on the top of the stack:\n    ///\n    /// * When entering a nested [`Loop`], push a [`Loop`][nl] state onto the\n    ///   stack.\n    ///\n    /// * When entering a nested [`Switch`], push a [`Switch`][ns] state onto\n    ///   the stack with a clone of the introduced `bool` variable's name.\n    ///\n    /// * When encountering a [`Continue`] statement, render it as code to set\n    ///   the introduced `bool` local (whose name is held in [`variable`]) to\n    ///   `true`, and then `break`. Set [`continue_encountered`] to `true` to\n    ///   record that the [`Switch`] contains a [`Continue`].\n    ///\n    /// * When exiting this [`Switch`], pop its entry from the stack. If\n    ///   [`continue_encountered`] is set, then we have rendered [`Continue`]\n    ///   statements as `break` statements that jump to this point. Generate\n    ///   code to check `variable`, and if it is `true`:\n    ///\n    ///     * If there is another [`Switch`][ns] left on top of the stack, set\n    ///       its `continue_encountered` flag, and generate a `break`. (Both\n    ///       [`Switch`][ns]es are within the same [`Loop`] and share the same\n    ///       introduced variable, so there's no need to set another flag to\n    ///       continue to exit the `switch`es.)\n    ///\n    ///     * Otherwise, `continue`.\n    ///\n    /// When we exit the [`Switch`] for which this entry was pushed, pop it.\n    ///\n    /// [`Continue`]: crate::Statement::Continue\n    /// [`Loop`]: crate::Statement::Loop\n    /// [`Switch`]: crate::Statement::Switch\n    /// [`variable`]: Nesting::Switch::variable\n    /// [`continue_encountered`]: Nesting::Switch::continue_encountered\n    /// [ns]: Nesting::Switch\n    /// [nl]: Nesting::Loop\n    Switch {\n        variable: Rc<String>,\n\n        /// Set if we've generated code for a [`Continue`] statement with this\n        /// entry on the top of the stack.\n        ///\n        /// If this is still clear when we finish rendering the [`Switch`], then\n        /// we know we don't need to generate branch forwarding code. Omitting\n        /// that may make it easier for drivers to tell that the `bool` we\n        /// introduced ahead of the [`Switch`] is actually unused.\n        ///\n        /// [`Continue`]: crate::Statement::Continue\n        /// [`Switch`]: crate::Statement::Switch\n        continue_encountered: bool,\n    },\n}\n\n/// A micro-IR for code a backend should generate after a [`Switch`].\n///\n/// [`Switch`]: crate::Statement::Switch\npub(super) enum ExitControlFlow {\n    None,\n    /// Emit `if (continue_variable) { continue; }`\n    Continue {\n        variable: Rc<String>,\n    },\n    /// Emit `if (continue_variable) { break; }`\n    ///\n    /// Used after a [`Switch`] to exit from an enclosing [`Switch`].\n    ///\n    /// After the enclosing switch, its associated check will consult this same\n    /// variable, see that it is set, and exit early.\n    ///\n    /// [`Switch`]: crate::Statement::Switch\n    Break {\n        variable: Rc<String>,\n    },\n}\n\n/// Utility for tracking nesting of loops and switches to orchestrate forwarding\n/// of continue statements inside of a switch to the enclosing loop.\n///\n/// See [module docs](self) for why we need this.\n#[derive(Default)]\npub(super) struct ContinueCtx {\n    stack: Vec<Nesting>,\n}\n\nimpl ContinueCtx {\n    /// Resets internal state.\n    ///\n    /// Use this to reuse memory between writing sessions.\n    #[allow(dead_code, reason = \"only used by some backends\")]\n    pub fn clear(&mut self) {\n        self.stack.clear();\n    }\n\n    /// Updates internal state to record entering a [`Loop`] statement.\n    ///\n    /// [`Loop`]: crate::Statement::Loop\n    pub fn enter_loop(&mut self) {\n        self.stack.push(Nesting::Loop);\n    }\n\n    /// Updates internal state to record exiting a [`Loop`] statement.\n    ///\n    /// [`Loop`]: crate::Statement::Loop\n    pub fn exit_loop(&mut self) {\n        if !matches!(self.stack.pop(), Some(Nesting::Loop)) {\n            unreachable!(\"ContinueCtx stack out of sync\");\n        }\n    }\n\n    /// Updates internal state to record entering a [`Switch`] statement.\n    ///\n    /// Return `Some(variable)` if this [`Switch`] is nested within a [`Loop`],\n    /// and the caller should introcue a new `bool` local variable named\n    /// `variable` above the `switch`, for forwarding [`Continue`] statements.\n    ///\n    /// `variable` is guaranteed not to conflict with any names used by the\n    /// program itself.\n    ///\n    /// [`Continue`]: crate::Statement::Continue\n    /// [`Loop`]: crate::Statement::Loop\n    /// [`Switch`]: crate::Statement::Switch\n    pub fn enter_switch(&mut self, namer: &mut Namer) -> Option<Rc<String>> {\n        match self.stack.last() {\n            // If the stack is empty, we are not in loop, so we don't need to\n            // forward continue statements within this `Switch`. We can leave\n            // the stack empty.\n            None => None,\n            Some(&Nesting::Loop) => {\n                let variable = Rc::new(namer.call(\"should_continue\"));\n                self.stack.push(Nesting::Switch {\n                    variable: Rc::clone(&variable),\n                    continue_encountered: false,\n                });\n                Some(variable)\n            }\n            Some(&Nesting::Switch { ref variable, .. }) => {\n                self.stack.push(Nesting::Switch {\n                    variable: Rc::clone(variable),\n                    continue_encountered: false,\n                });\n                // We have already declared the variable before some enclosing\n                // `Switch`.\n                None\n            }\n        }\n    }\n\n    /// Update internal state to record leaving a [`Switch`] statement.\n    ///\n    /// Return an [`ExitControlFlow`] value indicating what code should be\n    /// introduced after the generated `switch` to forward continues.\n    ///\n    /// [`Switch`]: crate::Statement::Switch\n    pub fn exit_switch(&mut self) -> ExitControlFlow {\n        match self.stack.pop() {\n            // This doesn't indicate a problem: we don't start pushing entries\n            // for `Switch` statements unless we have an enclosing `Loop`.\n            None => ExitControlFlow::None,\n            Some(Nesting::Loop) => {\n                unreachable!(\"Unexpected loop state when exiting switch\");\n            }\n            Some(Nesting::Switch {\n                variable,\n                continue_encountered: inner_continue,\n            }) => {\n                if !inner_continue {\n                    // No `Continue` statement was encountered, so we didn't\n                    // introduce any `break`s jumping to this point.\n                    ExitControlFlow::None\n                } else if let Some(&mut Nesting::Switch {\n                    continue_encountered: ref mut outer_continue,\n                    ..\n                }) = self.stack.last_mut()\n                {\n                    // This is nested in another `Switch`. Propagate upwards\n                    // that there is a continue statement present.\n                    *outer_continue = true;\n                    ExitControlFlow::Break { variable }\n                } else {\n                    ExitControlFlow::Continue { variable }\n                }\n            }\n        }\n    }\n\n    /// Determine what to generate for a [`Continue`] statement.\n    ///\n    /// If we can generate an ordinary `continue` statement, return `None`.\n    ///\n    /// Otherwise, we're enclosed by a [`Switch`] that is itself enclosed by a\n    /// [`Loop`]. Return `Some(variable)` to indicate that the [`Continue`]\n    /// should be rendered as setting `variable` to `true`, and then doing a\n    /// `break`.\n    ///\n    /// This also notes that we've encountered a [`Continue`] statement, so that\n    /// we can generate the right code to forward the branch following the\n    /// enclosing `switch`.\n    ///\n    /// [`Continue`]: crate::Statement::Continue\n    /// [`Loop`]: crate::Statement::Loop\n    /// [`Switch`]: crate::Statement::Switch\n    pub fn continue_encountered(&mut self) -> Option<&str> {\n        if let Some(&mut Nesting::Switch {\n            ref variable,\n            ref mut continue_encountered,\n        }) = self.stack.last_mut()\n        {\n            *continue_encountered = true;\n            Some(variable)\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/dot/mod.rs",
    "content": "/*!\nBackend for [DOT][dot] (Graphviz).\n\nThis backend writes a graph in the DOT language, for the ease\nof IR inspection and debugging.\n\n[dot]: https://graphviz.org/doc/info/lang.html\n*/\n\nuse alloc::{\n    borrow::Cow,\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::fmt::{Error as FmtError, Write as _};\n\nuse crate::{\n    arena::Handle,\n    valid::{FunctionInfo, ModuleInfo},\n};\n\n/// Configuration options for the dot backend\n#[derive(Clone, Default)]\npub struct Options {\n    /// Only emit function bodies\n    pub cfg_only: bool,\n}\n\n/// Identifier used to address a graph node\ntype NodeId = usize;\n\n/// Stores the target nodes for control flow statements\n#[derive(Default, Clone, Copy)]\nstruct Targets {\n    /// The node, if some, where continue operations will land\n    continue_target: Option<usize>,\n    /// The node, if some, where break operations will land\n    break_target: Option<usize>,\n}\n\n/// Stores information about the graph of statements\n#[derive(Default)]\nstruct StatementGraph {\n    /// List of node names\n    nodes: Vec<&'static str>,\n    /// List of edges of the control flow, the items are defined as\n    /// (from, to, label)\n    flow: Vec<(NodeId, NodeId, &'static str)>,\n    /// List of implicit edges of the control flow, used for jump\n    /// operations such as continue or break, the items are defined as\n    /// (from, to, label, color_id)\n    jumps: Vec<(NodeId, NodeId, &'static str, usize)>,\n    /// List of dependency relationships between a statement node and\n    /// expressions\n    dependencies: Vec<(NodeId, Handle<crate::Expression>, &'static str)>,\n    /// List of expression emitted by statement node\n    emits: Vec<(NodeId, Handle<crate::Expression>)>,\n    /// List of function call by statement node\n    calls: Vec<(NodeId, Handle<crate::Function>)>,\n}\n\nimpl StatementGraph {\n    /// Adds a new block to the statement graph, returning the first and last node, respectively\n    fn add(&mut self, block: &[crate::Statement], targets: Targets) -> (NodeId, NodeId) {\n        use crate::Statement as S;\n\n        // The first node of the block isn't a statement but a virtual node\n        let root = self.nodes.len();\n        self.nodes.push(if root == 0 { \"Root\" } else { \"Node\" });\n        // Track the last placed node, this will be returned to the caller and\n        // will also be used to generate the control flow edges\n        let mut last_node = root;\n        for statement in block {\n            // Reserve a new node for the current statement and link it to the\n            // node of the previous statement\n            let id = self.nodes.len();\n            self.flow.push((last_node, id, \"\"));\n            self.nodes.push(\"\"); // reserve space\n\n            // Track the node identifier for the merge node, the merge node is\n            // the last node of a statement, normally this is the node itself,\n            // but for control flow statements such as `if`s and `switch`s this\n            // is a virtual node where all branches merge back.\n            let mut merge_id = id;\n\n            self.nodes[id] = match *statement {\n                S::Emit(ref range) => {\n                    for handle in range.clone() {\n                        self.emits.push((id, handle));\n                    }\n                    \"Emit\"\n                }\n                S::Kill => \"Kill\", //TODO: link to the beginning\n                S::Break => {\n                    // Try to link to the break target, otherwise produce\n                    // a broken connection\n                    if let Some(target) = targets.break_target {\n                        self.jumps.push((id, target, \"Break\", 5))\n                    } else {\n                        self.jumps.push((id, root, \"Broken\", 7))\n                    }\n                    \"Break\"\n                }\n                S::Continue => {\n                    // Try to link to the continue target, otherwise produce\n                    // a broken connection\n                    if let Some(target) = targets.continue_target {\n                        self.jumps.push((id, target, \"Continue\", 5))\n                    } else {\n                        self.jumps.push((id, root, \"Broken\", 7))\n                    }\n                    \"Continue\"\n                }\n                S::ControlBarrier(_flags) => \"ControlBarrier\",\n                S::MemoryBarrier(_flags) => \"MemoryBarrier\",\n                S::Block(ref b) => {\n                    let (other, last) = self.add(b, targets);\n                    self.flow.push((id, other, \"\"));\n                    // All following nodes should connect to the end of the block\n                    // statement so change the merge id to it.\n                    merge_id = last;\n                    \"Block\"\n                }\n                S::If {\n                    condition,\n                    ref accept,\n                    ref reject,\n                } => {\n                    self.dependencies.push((id, condition, \"condition\"));\n                    let (accept_id, accept_last) = self.add(accept, targets);\n                    self.flow.push((id, accept_id, \"accept\"));\n                    let (reject_id, reject_last) = self.add(reject, targets);\n                    self.flow.push((id, reject_id, \"reject\"));\n\n                    // Create a merge node, link the branches to it and set it\n                    // as the merge node to make the next statement node link to it\n                    merge_id = self.nodes.len();\n                    self.nodes.push(\"Merge\");\n                    self.flow.push((accept_last, merge_id, \"\"));\n                    self.flow.push((reject_last, merge_id, \"\"));\n\n                    \"If\"\n                }\n                S::Switch {\n                    selector,\n                    ref cases,\n                } => {\n                    self.dependencies.push((id, selector, \"selector\"));\n\n                    // Create a merge node and set it as the merge node to make\n                    // the next statement node link to it\n                    merge_id = self.nodes.len();\n                    self.nodes.push(\"Merge\");\n\n                    // Create a new targets structure and set the break target\n                    // to the merge node\n                    let mut targets = targets;\n                    targets.break_target = Some(merge_id);\n\n                    for case in cases {\n                        let (case_id, case_last) = self.add(&case.body, targets);\n                        let label = match case.value {\n                            crate::SwitchValue::Default => \"default\",\n                            _ => \"case\",\n                        };\n                        self.flow.push((id, case_id, label));\n                        // Link the last node of the branch to the merge node\n                        self.flow.push((case_last, merge_id, \"\"));\n                    }\n                    \"Switch\"\n                }\n                S::Loop {\n                    ref body,\n                    ref continuing,\n                    break_if,\n                } => {\n                    // Create a new targets structure and set the break target\n                    // to the merge node, this must happen before generating the\n                    // continuing block since it can break.\n                    let mut targets = targets;\n                    targets.break_target = Some(id);\n\n                    let (continuing_id, continuing_last) = self.add(continuing, targets);\n\n                    // Set the the continue target to the beginning\n                    // of the newly generated continuing block\n                    targets.continue_target = Some(continuing_id);\n\n                    let (body_id, body_last) = self.add(body, targets);\n\n                    self.flow.push((id, body_id, \"body\"));\n\n                    // Link the last node of the body to the continuing block\n                    self.flow.push((body_last, continuing_id, \"continuing\"));\n                    // Link the last node of the continuing block back to the\n                    // beginning of the loop body\n                    self.flow.push((continuing_last, body_id, \"continuing\"));\n\n                    if let Some(expr) = break_if {\n                        self.dependencies.push((continuing_id, expr, \"break if\"));\n                    }\n\n                    \"Loop\"\n                }\n                S::Return { value } => {\n                    if let Some(expr) = value {\n                        self.dependencies.push((id, expr, \"value\"));\n                    }\n                    \"Return\"\n                }\n                S::Store { pointer, value } => {\n                    self.dependencies.push((id, value, \"value\"));\n                    self.emits.push((id, pointer));\n                    \"Store\"\n                }\n                S::ImageStore {\n                    image,\n                    coordinate,\n                    array_index,\n                    value,\n                } => {\n                    self.dependencies.push((id, image, \"image\"));\n                    self.dependencies.push((id, coordinate, \"coordinate\"));\n                    if let Some(expr) = array_index {\n                        self.dependencies.push((id, expr, \"array_index\"));\n                    }\n                    self.dependencies.push((id, value, \"value\"));\n                    \"ImageStore\"\n                }\n                S::Call {\n                    function,\n                    ref arguments,\n                    result,\n                } => {\n                    for &arg in arguments {\n                        self.dependencies.push((id, arg, \"arg\"));\n                    }\n                    if let Some(expr) = result {\n                        self.emits.push((id, expr));\n                    }\n                    self.calls.push((id, function));\n                    \"Call\"\n                }\n                S::Atomic {\n                    pointer,\n                    ref fun,\n                    value,\n                    result,\n                } => {\n                    if let Some(result) = result {\n                        self.emits.push((id, result));\n                    }\n                    self.dependencies.push((id, pointer, \"pointer\"));\n                    self.dependencies.push((id, value, \"value\"));\n                    if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {\n                        self.dependencies.push((id, cmp, \"cmp\"));\n                    }\n                    \"Atomic\"\n                }\n                S::ImageAtomic {\n                    image,\n                    coordinate,\n                    array_index,\n                    fun: _,\n                    value,\n                } => {\n                    self.dependencies.push((id, image, \"image\"));\n                    self.dependencies.push((id, coordinate, \"coordinate\"));\n                    if let Some(expr) = array_index {\n                        self.dependencies.push((id, expr, \"array_index\"));\n                    }\n                    self.dependencies.push((id, value, \"value\"));\n                    \"ImageAtomic\"\n                }\n                S::WorkGroupUniformLoad { pointer, result } => {\n                    self.emits.push((id, result));\n                    self.dependencies.push((id, pointer, \"pointer\"));\n                    \"WorkGroupUniformLoad\"\n                }\n                S::RayQuery { query, ref fun } => {\n                    self.dependencies.push((id, query, \"query\"));\n                    match *fun {\n                        crate::RayQueryFunction::Initialize {\n                            acceleration_structure,\n                            descriptor,\n                        } => {\n                            self.dependencies.push((\n                                id,\n                                acceleration_structure,\n                                \"acceleration_structure\",\n                            ));\n                            self.dependencies.push((id, descriptor, \"descriptor\"));\n                            \"RayQueryInitialize\"\n                        }\n                        crate::RayQueryFunction::Proceed { result } => {\n                            self.emits.push((id, result));\n                            \"RayQueryProceed\"\n                        }\n                        crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                            self.dependencies.push((id, hit_t, \"hit_t\"));\n                            \"RayQueryGenerateIntersection\"\n                        }\n                        crate::RayQueryFunction::ConfirmIntersection => {\n                            \"RayQueryConfirmIntersection\"\n                        }\n                        crate::RayQueryFunction::Terminate => \"RayQueryTerminate\",\n                    }\n                }\n                S::SubgroupBallot { result, predicate } => {\n                    if let Some(predicate) = predicate {\n                        self.dependencies.push((id, predicate, \"predicate\"));\n                    }\n                    self.emits.push((id, result));\n                    \"SubgroupBallot\"\n                }\n                S::SubgroupCollectiveOperation {\n                    op,\n                    collective_op,\n                    argument,\n                    result,\n                } => {\n                    self.dependencies.push((id, argument, \"arg\"));\n                    self.emits.push((id, result));\n                    match (collective_op, op) {\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {\n                            \"SubgroupAll\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {\n                            \"SubgroupAny\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {\n                            \"SubgroupAdd\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {\n                            \"SubgroupMul\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {\n                            \"SubgroupMax\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {\n                            \"SubgroupMin\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {\n                            \"SubgroupAnd\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {\n                            \"SubgroupOr\"\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {\n                            \"SubgroupXor\"\n                        }\n                        (\n                            crate::CollectiveOperation::ExclusiveScan,\n                            crate::SubgroupOperation::Add,\n                        ) => \"SubgroupExclusiveAdd\",\n                        (\n                            crate::CollectiveOperation::ExclusiveScan,\n                            crate::SubgroupOperation::Mul,\n                        ) => \"SubgroupExclusiveMul\",\n                        (\n                            crate::CollectiveOperation::InclusiveScan,\n                            crate::SubgroupOperation::Add,\n                        ) => \"SubgroupInclusiveAdd\",\n                        (\n                            crate::CollectiveOperation::InclusiveScan,\n                            crate::SubgroupOperation::Mul,\n                        ) => \"SubgroupInclusiveMul\",\n                        _ => unimplemented!(),\n                    }\n                }\n                S::SubgroupGather {\n                    mode,\n                    argument,\n                    result,\n                } => {\n                    match mode {\n                        crate::GatherMode::BroadcastFirst => {}\n                        crate::GatherMode::Broadcast(index)\n                        | crate::GatherMode::Shuffle(index)\n                        | crate::GatherMode::ShuffleDown(index)\n                        | crate::GatherMode::ShuffleUp(index)\n                        | crate::GatherMode::ShuffleXor(index)\n                        | crate::GatherMode::QuadBroadcast(index) => {\n                            self.dependencies.push((id, index, \"index\"))\n                        }\n                        crate::GatherMode::QuadSwap(_) => {}\n                    }\n                    self.dependencies.push((id, argument, \"arg\"));\n                    self.emits.push((id, result));\n                    match mode {\n                        crate::GatherMode::BroadcastFirst => \"SubgroupBroadcastFirst\",\n                        crate::GatherMode::Broadcast(_) => \"SubgroupBroadcast\",\n                        crate::GatherMode::Shuffle(_) => \"SubgroupShuffle\",\n                        crate::GatherMode::ShuffleDown(_) => \"SubgroupShuffleDown\",\n                        crate::GatherMode::ShuffleUp(_) => \"SubgroupShuffleUp\",\n                        crate::GatherMode::ShuffleXor(_) => \"SubgroupShuffleXor\",\n                        crate::GatherMode::QuadBroadcast(_) => \"SubgroupQuadBroadcast\",\n                        crate::GatherMode::QuadSwap(direction) => match direction {\n                            crate::Direction::X => \"SubgroupQuadSwapX\",\n                            crate::Direction::Y => \"SubgroupQuadSwapY\",\n                            crate::Direction::Diagonal => \"SubgroupQuadSwapDiagonal\",\n                        },\n                    }\n                }\n                S::CooperativeStore { target, data } => {\n                    self.dependencies.push((id, target, \"target\"));\n                    self.dependencies.push((id, data.pointer, \"pointer\"));\n                    self.dependencies.push((id, data.stride, \"stride\"));\n                    if data.row_major {\n                        \"CoopStoreT\"\n                    } else {\n                        \"CoopStore\"\n                    }\n                }\n                S::RayPipelineFunction(func) => match func {\n                    crate::RayPipelineFunction::TraceRay {\n                        acceleration_structure,\n                        descriptor,\n                        payload,\n                    } => {\n                        self.dependencies.push((\n                            id,\n                            acceleration_structure,\n                            \"acceleration_structure\",\n                        ));\n                        self.dependencies.push((id, descriptor, \"descriptor\"));\n                        self.dependencies.push((id, payload, \"payload\"));\n                        \"TraceRay\"\n                    }\n                },\n            };\n            // Set the last node to the merge node\n            last_node = merge_id;\n        }\n        (root, last_node)\n    }\n}\n\nfn name(option: &Option<String>) -> &str {\n    option.as_deref().unwrap_or_default()\n}\n\n/// set39 color scheme from <https://graphviz.org/doc/info/colors.html>\nconst COLORS: &[&str] = &[\n    \"white\", // pattern starts at 1\n    \"#8dd3c7\", \"#ffffb3\", \"#bebada\", \"#fb8072\", \"#80b1d3\", \"#fdb462\", \"#b3de69\", \"#fccde5\",\n    \"#d9d9d9\",\n];\n\nstruct Prefixed<T>(Handle<T>);\n\nimpl core::fmt::Display for Prefixed<crate::Expression> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"e\")\n    }\n}\n\nimpl core::fmt::Display for Prefixed<crate::LocalVariable> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"l\")\n    }\n}\n\nimpl core::fmt::Display for Prefixed<crate::GlobalVariable> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"g\")\n    }\n}\n\nimpl core::fmt::Display for Prefixed<crate::Function> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"f\")\n    }\n}\n\nfn write_fun(\n    output: &mut String,\n    prefix: String,\n    fun: &crate::Function,\n    info: Option<&FunctionInfo>,\n    options: &Options,\n) -> Result<(), FmtError> {\n    writeln!(output, \"\\t\\tnode [ style=filled ]\")?;\n\n    if !options.cfg_only {\n        for (handle, var) in fun.local_variables.iter() {\n            writeln!(\n                output,\n                \"\\t\\t{}_{} [ shape=hexagon label=\\\"{:?} '{}'\\\" ]\",\n                prefix,\n                Prefixed(handle),\n                handle,\n                name(&var.name),\n            )?;\n        }\n\n        write_function_expressions(output, &prefix, fun, info)?;\n    }\n\n    let mut sg = StatementGraph::default();\n    sg.add(&fun.body, Targets::default());\n    for (index, label) in sg.nodes.into_iter().enumerate() {\n        writeln!(\n            output,\n            \"\\t\\t{prefix}_s{index} [ shape=square label=\\\"{label}\\\" ]\",\n        )?;\n    }\n    for (from, to, label) in sg.flow {\n        writeln!(\n            output,\n            \"\\t\\t{prefix}_s{from} -> {prefix}_s{to} [ arrowhead=tee label=\\\"{label}\\\" ]\",\n        )?;\n    }\n    for (from, to, label, color_id) in sg.jumps {\n        writeln!(\n            output,\n            \"\\t\\t{}_s{} -> {}_s{} [ arrowhead=tee style=dashed color=\\\"{}\\\" label=\\\"{}\\\" ]\",\n            prefix, from, prefix, to, COLORS[color_id], label,\n        )?;\n    }\n\n    if !options.cfg_only {\n        for (to, expr, label) in sg.dependencies {\n            writeln!(\n                output,\n                \"\\t\\t{}_{} -> {}_s{} [ label=\\\"{}\\\" ]\",\n                prefix,\n                Prefixed(expr),\n                prefix,\n                to,\n                label,\n            )?;\n        }\n        for (from, to) in sg.emits {\n            writeln!(\n                output,\n                \"\\t\\t{}_s{} -> {}_{} [ style=dotted ]\",\n                prefix,\n                from,\n                prefix,\n                Prefixed(to),\n            )?;\n        }\n    }\n\n    assert!(sg.calls.is_empty());\n    for (from, function) in sg.calls {\n        writeln!(\n            output,\n            \"\\t\\t{}_s{} -> {}_s0\",\n            prefix,\n            from,\n            Prefixed(function),\n        )?;\n    }\n\n    Ok(())\n}\n\nfn write_function_expressions(\n    output: &mut String,\n    prefix: &str,\n    fun: &crate::Function,\n    info: Option<&FunctionInfo>,\n) -> Result<(), FmtError> {\n    enum Payload<'a> {\n        Arguments(&'a [Handle<crate::Expression>]),\n        Local(Handle<crate::LocalVariable>),\n        Global(Handle<crate::GlobalVariable>),\n    }\n\n    let mut edges = crate::FastHashMap::<&str, _>::default();\n    let mut payload = None;\n    for (handle, expression) in fun.expressions.iter() {\n        use crate::Expression as E;\n        let (label, color_id) = match *expression {\n            E::Literal(_) => (\"Literal\".into(), 2),\n            E::Constant(_) => (\"Constant\".into(), 2),\n            E::Override(_) => (\"Override\".into(), 2),\n            E::ZeroValue(_) => (\"ZeroValue\".into(), 2),\n            E::Compose { ref components, .. } => {\n                payload = Some(Payload::Arguments(components));\n                (\"Compose\".into(), 3)\n            }\n            E::Access { base, index } => {\n                edges.insert(\"base\", base);\n                edges.insert(\"index\", index);\n                (\"Access\".into(), 1)\n            }\n            E::AccessIndex { base, index } => {\n                edges.insert(\"base\", base);\n                (format!(\"AccessIndex[{index}]\").into(), 1)\n            }\n            E::Splat { size, value } => {\n                edges.insert(\"value\", value);\n                (format!(\"Splat{size:?}\").into(), 3)\n            }\n            E::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                edges.insert(\"vector\", vector);\n                (format!(\"Swizzle{:?}\", &pattern[..size as usize]).into(), 3)\n            }\n            E::FunctionArgument(index) => (format!(\"Argument[{index}]\").into(), 1),\n            E::GlobalVariable(h) => {\n                payload = Some(Payload::Global(h));\n                (\"Global\".into(), 2)\n            }\n            E::LocalVariable(h) => {\n                payload = Some(Payload::Local(h));\n                (\"Local\".into(), 1)\n            }\n            E::Load { pointer } => {\n                edges.insert(\"pointer\", pointer);\n                (\"Load\".into(), 4)\n            }\n            E::ImageSample {\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset: _,\n                level,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                edges.insert(\"image\", image);\n                edges.insert(\"sampler\", sampler);\n                edges.insert(\"coordinate\", coordinate);\n                if let Some(expr) = array_index {\n                    edges.insert(\"array_index\", expr);\n                }\n                match level {\n                    crate::SampleLevel::Auto => {}\n                    crate::SampleLevel::Zero => {}\n                    crate::SampleLevel::Exact(expr) => {\n                        edges.insert(\"level\", expr);\n                    }\n                    crate::SampleLevel::Bias(expr) => {\n                        edges.insert(\"bias\", expr);\n                    }\n                    crate::SampleLevel::Gradient { x, y } => {\n                        edges.insert(\"grad_x\", x);\n                        edges.insert(\"grad_y\", y);\n                    }\n                }\n                if let Some(expr) = depth_ref {\n                    edges.insert(\"depth_ref\", expr);\n                }\n                let string = match gather {\n                    Some(component) => Cow::Owned(format!(\"ImageGather{component:?}\")),\n                    _ => Cow::Borrowed(\"ImageSample\"),\n                };\n                (string, 5)\n            }\n            E::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                edges.insert(\"image\", image);\n                edges.insert(\"coordinate\", coordinate);\n                if let Some(expr) = array_index {\n                    edges.insert(\"array_index\", expr);\n                }\n                if let Some(sample) = sample {\n                    edges.insert(\"sample\", sample);\n                }\n                if let Some(level) = level {\n                    edges.insert(\"level\", level);\n                }\n                (\"ImageLoad\".into(), 5)\n            }\n            E::ImageQuery { image, query } => {\n                edges.insert(\"image\", image);\n                let args = match query {\n                    crate::ImageQuery::Size { level } => {\n                        if let Some(expr) = level {\n                            edges.insert(\"level\", expr);\n                        }\n                        Cow::from(\"ImageSize\")\n                    }\n                    _ => Cow::Owned(format!(\"{query:?}\")),\n                };\n                (args, 7)\n            }\n            E::Unary { op, expr } => {\n                edges.insert(\"expr\", expr);\n                (format!(\"{op:?}\").into(), 6)\n            }\n            E::Binary { op, left, right } => {\n                edges.insert(\"left\", left);\n                edges.insert(\"right\", right);\n                (format!(\"{op:?}\").into(), 6)\n            }\n            E::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                edges.insert(\"condition\", condition);\n                edges.insert(\"accept\", accept);\n                edges.insert(\"reject\", reject);\n                (\"Select\".into(), 3)\n            }\n            E::Derivative { axis, ctrl, expr } => {\n                edges.insert(\"\", expr);\n                (format!(\"d{axis:?}{ctrl:?}\").into(), 8)\n            }\n            E::Relational { fun, argument } => {\n                edges.insert(\"arg\", argument);\n                (format!(\"{fun:?}\").into(), 6)\n            }\n            E::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                edges.insert(\"arg\", arg);\n                if let Some(expr) = arg1 {\n                    edges.insert(\"arg1\", expr);\n                }\n                if let Some(expr) = arg2 {\n                    edges.insert(\"arg2\", expr);\n                }\n                if let Some(expr) = arg3 {\n                    edges.insert(\"arg3\", expr);\n                }\n                (format!(\"{fun:?}\").into(), 7)\n            }\n            E::As {\n                kind,\n                expr,\n                convert,\n            } => {\n                edges.insert(\"\", expr);\n                let string = match convert {\n                    Some(width) => format!(\"Convert<{kind:?},{width}>\"),\n                    None => format!(\"Bitcast<{kind:?}>\"),\n                };\n                (string.into(), 3)\n            }\n            E::CallResult(_function) => (\"CallResult\".into(), 4),\n            E::AtomicResult { .. } => (\"AtomicResult\".into(), 4),\n            E::WorkGroupUniformLoadResult { .. } => (\"WorkGroupUniformLoadResult\".into(), 4),\n            E::ArrayLength(expr) => {\n                edges.insert(\"\", expr);\n                (\"ArrayLength\".into(), 7)\n            }\n            E::RayQueryProceedResult => (\"rayQueryProceedResult\".into(), 4),\n            E::RayQueryGetIntersection { query, committed } => {\n                edges.insert(\"\", query);\n                let ty = if committed { \"Committed\" } else { \"Candidate\" };\n                (format!(\"rayQueryGet{ty}Intersection\").into(), 4)\n            }\n            E::SubgroupBallotResult => (\"SubgroupBallotResult\".into(), 4),\n            E::SubgroupOperationResult { .. } => (\"SubgroupOperationResult\".into(), 4),\n            E::RayQueryVertexPositions { query, committed } => {\n                edges.insert(\"\", query);\n                let ty = if committed { \"Committed\" } else { \"Candidate\" };\n                (format!(\"get{ty}HitVertexPositions\").into(), 4)\n            }\n            E::CooperativeLoad { ref data, .. } => {\n                edges.insert(\"pointer\", data.pointer);\n                edges.insert(\"stride\", data.stride);\n                let suffix = if data.row_major { \"T \" } else { \"\" };\n                (format!(\"coopLoad{suffix}\").into(), 4)\n            }\n            E::CooperativeMultiplyAdd { a, b, c } => {\n                edges.insert(\"a\", a);\n                edges.insert(\"b\", b);\n                edges.insert(\"c\", c);\n                (\"cooperativeMultiplyAdd\".into(), 4)\n            }\n        };\n\n        // give uniform expressions an outline\n        let color_attr = match info {\n            Some(info) if info[handle].uniformity.non_uniform_result.is_none() => \"fillcolor\",\n            _ => \"color\",\n        };\n        writeln!(\n            output,\n            \"\\t\\t{}_{} [ {}=\\\"{}\\\" label=\\\"{:?} {}\\\" ]\",\n            prefix,\n            Prefixed(handle),\n            color_attr,\n            COLORS[color_id],\n            handle,\n            label,\n        )?;\n\n        for (key, edge) in edges.drain() {\n            writeln!(\n                output,\n                \"\\t\\t{}_{} -> {}_{} [ label=\\\"{}\\\" ]\",\n                prefix,\n                Prefixed(edge),\n                prefix,\n                Prefixed(handle),\n                key,\n            )?;\n        }\n        match payload.take() {\n            Some(Payload::Arguments(list)) => {\n                write!(output, \"\\t\\t{{\")?;\n                for &comp in list {\n                    write!(output, \" {}_{}\", prefix, Prefixed(comp))?;\n                }\n                writeln!(output, \" }} -> {}_{}\", prefix, Prefixed(handle))?;\n            }\n            Some(Payload::Local(h)) => {\n                writeln!(\n                    output,\n                    \"\\t\\t{}_{} -> {}_{}\",\n                    prefix,\n                    Prefixed(h),\n                    prefix,\n                    Prefixed(handle),\n                )?;\n            }\n            Some(Payload::Global(h)) => {\n                writeln!(\n                    output,\n                    \"\\t\\t{} -> {}_{} [fillcolor=gray]\",\n                    Prefixed(h),\n                    prefix,\n                    Prefixed(handle),\n                )?;\n            }\n            None => {}\n        }\n    }\n\n    Ok(())\n}\n\n/// Write shader module to a [`String`].\npub fn write(\n    module: &crate::Module,\n    mod_info: Option<&ModuleInfo>,\n    options: Options,\n) -> Result<String, FmtError> {\n    use core::fmt::Write as _;\n\n    let mut output = String::new();\n    output += \"digraph Module {\\n\";\n\n    if !options.cfg_only {\n        writeln!(output, \"\\tsubgraph cluster_globals {{\")?;\n        writeln!(output, \"\\t\\tlabel=\\\"Globals\\\"\")?;\n        for (handle, var) in module.global_variables.iter() {\n            writeln!(\n                output,\n                \"\\t\\t{} [ shape=hexagon label=\\\"{:?} {:?}/'{}'\\\" ]\",\n                Prefixed(handle),\n                handle,\n                var.space,\n                name(&var.name),\n            )?;\n        }\n        writeln!(output, \"\\t}}\")?;\n    }\n\n    for (handle, fun) in module.functions.iter() {\n        let prefix = Prefixed(handle).to_string();\n        writeln!(output, \"\\tsubgraph cluster_{prefix} {{\")?;\n        writeln!(\n            output,\n            \"\\t\\tlabel=\\\"Function{:?}/'{}'\\\"\",\n            handle,\n            name(&fun.name)\n        )?;\n        let info = mod_info.map(|a| &a[handle]);\n        write_fun(&mut output, prefix, fun, info, &options)?;\n        writeln!(output, \"\\t}}\")?;\n    }\n    for (ep_index, ep) in module.entry_points.iter().enumerate() {\n        let prefix = format!(\"ep{ep_index}\");\n        writeln!(output, \"\\tsubgraph cluster_{prefix} {{\")?;\n        writeln!(output, \"\\t\\tlabel=\\\"{:?}/'{}'\\\"\", ep.stage, ep.name)?;\n        let info = mod_info.map(|a| a.get_entry_point(ep_index));\n        write_fun(&mut output, prefix, &ep.function, info, &options)?;\n        writeln!(output, \"\\t}}\")?;\n    }\n\n    output += \"}\\n\";\n    Ok(output)\n}\n"
  },
  {
    "path": "naga/src/back/glsl/conv.rs",
    "content": "use crate::back::glsl::{BackendResult, Error, VaryingOptions};\n\n/// Structure returned by [`glsl_scalar`]\n///\n/// It contains both a prefix used in other types and the full type name\npub(in crate::back::glsl) struct ScalarString<'a> {\n    /// The prefix used to compose other types\n    pub prefix: &'a str,\n    /// The name of the scalar type\n    pub full: &'a str,\n}\n\n/// Helper function that returns scalar related strings\n///\n/// Check [`ScalarString`] for the information provided\n///\n/// # Errors\n/// If a [`Float`](crate::ScalarKind::Float) with an width that isn't 4 or 8\npub(in crate::back::glsl) const fn glsl_scalar(\n    scalar: crate::Scalar,\n) -> Result<ScalarString<'static>, Error> {\n    use crate::ScalarKind as Sk;\n\n    Ok(match scalar.kind {\n        Sk::Sint => ScalarString {\n            prefix: \"i\",\n            full: \"int\",\n        },\n        Sk::Uint => ScalarString {\n            prefix: \"u\",\n            full: \"uint\",\n        },\n        Sk::Float => match scalar.width {\n            4 => ScalarString {\n                prefix: \"\",\n                full: \"float\",\n            },\n            8 => ScalarString {\n                prefix: \"d\",\n                full: \"double\",\n            },\n            _ => return Err(Error::UnsupportedScalar(scalar)),\n        },\n        Sk::Bool => ScalarString {\n            prefix: \"b\",\n            full: \"bool\",\n        },\n        Sk::AbstractInt | Sk::AbstractFloat => {\n            return Err(Error::UnsupportedScalar(scalar));\n        }\n    })\n}\n\n/// Helper function that returns the glsl variable name for a builtin\npub(in crate::back::glsl) const fn glsl_built_in(\n    built_in: crate::BuiltIn,\n    options: VaryingOptions,\n) -> &'static str {\n    use crate::BuiltIn as Bi;\n\n    match built_in {\n        Bi::Position { .. } => {\n            if options.output {\n                \"gl_Position\"\n            } else {\n                \"gl_FragCoord\"\n            }\n        }\n        Bi::ViewIndex => {\n            if options.targeting_webgl {\n                \"gl_ViewID_OVR\"\n            } else {\n                \"uint(gl_ViewIndex)\"\n            }\n        }\n        // vertex\n        Bi::BaseInstance => \"uint(gl_BaseInstance)\",\n        Bi::BaseVertex => \"uint(gl_BaseVertex)\",\n        Bi::ClipDistances => \"gl_ClipDistance\",\n        Bi::CullDistance => \"gl_CullDistance\",\n        Bi::InstanceIndex => {\n            if options.draw_parameters {\n                \"(uint(gl_InstanceID) + uint(gl_BaseInstanceARB))\"\n            } else {\n                // Must match FIRST_INSTANCE_BINDING\n                \"(uint(gl_InstanceID) + naga_vs_first_instance)\"\n            }\n        }\n        Bi::PointSize => \"gl_PointSize\",\n        Bi::VertexIndex => \"uint(gl_VertexID)\",\n        Bi::DrawIndex => \"gl_DrawID\",\n        // fragment\n        Bi::FragDepth => \"gl_FragDepth\",\n        Bi::PointCoord => \"gl_PointCoord\",\n        Bi::FrontFacing => \"gl_FrontFacing\",\n        Bi::PrimitiveIndex => \"uint(gl_PrimitiveID)\",\n        Bi::Barycentric { perspective: true } => \"gl_BaryCoordEXT\",\n        Bi::Barycentric { perspective: false } => \"gl_BaryCoordNoPerspEXT\",\n        Bi::SampleIndex => \"gl_SampleID\",\n        Bi::SampleMask => {\n            if options.output {\n                \"gl_SampleMask\"\n            } else {\n                \"gl_SampleMaskIn\"\n            }\n        }\n        // compute\n        Bi::GlobalInvocationId => \"gl_GlobalInvocationID\",\n        Bi::LocalInvocationId => \"gl_LocalInvocationID\",\n        Bi::LocalInvocationIndex => \"gl_LocalInvocationIndex\",\n        Bi::WorkGroupId => \"gl_WorkGroupID\",\n        Bi::WorkGroupSize => \"gl_WorkGroupSize\",\n        Bi::NumWorkGroups => \"gl_NumWorkGroups\",\n        // subgroup\n        Bi::NumSubgroups => \"gl_NumSubgroups\",\n        Bi::SubgroupId => \"gl_SubgroupID\",\n        Bi::SubgroupSize => \"gl_SubgroupSize\",\n        Bi::SubgroupInvocationId => \"gl_SubgroupInvocationID\",\n        // mesh\n        // TODO: figure out how to map these to glsl things as glsl treats them as arrays\n        Bi::CullPrimitive\n        | Bi::PointIndex\n        | Bi::LineIndices\n        | Bi::TriangleIndices\n        | Bi::MeshTaskSize\n        | Bi::VertexCount\n        | Bi::PrimitiveCount\n        | Bi::Vertices\n        | Bi::Primitives\n        | Bi::RayInvocationId\n        | Bi::NumRayInvocations\n        | Bi::InstanceCustomData\n        | Bi::GeometryIndex\n        | Bi::WorldRayOrigin\n        | Bi::WorldRayDirection\n        | Bi::ObjectRayOrigin\n        | Bi::ObjectRayDirection\n        | Bi::RayTmin\n        | Bi::RayTCurrentMax\n        | Bi::ObjectToWorld\n        | Bi::WorldToObject\n        | Bi::HitKind => {\n            unimplemented!()\n        }\n    }\n}\n\n/// Helper function that returns the string corresponding to the address space\npub(in crate::back::glsl) const fn glsl_storage_qualifier(\n    space: crate::AddressSpace,\n) -> Option<&'static str> {\n    use crate::AddressSpace as As;\n\n    match space {\n        As::Function => None,\n        As::Private => None,\n        As::Storage { .. } => Some(\"buffer\"),\n        As::Uniform => Some(\"uniform\"),\n        As::Handle => Some(\"uniform\"),\n        As::WorkGroup => Some(\"shared\"),\n        As::Immediate => Some(\"uniform\"),\n        As::TaskPayload | As::RayPayload | As::IncomingRayPayload => unreachable!(),\n    }\n}\n\n/// Helper function that returns the string corresponding to the glsl interpolation qualifier\npub(in crate::back::glsl) const fn glsl_interpolation(\n    interpolation: crate::Interpolation,\n) -> &'static str {\n    use crate::Interpolation as I;\n\n    match interpolation {\n        I::Perspective => \"smooth\",\n        I::Linear => \"noperspective\",\n        I::Flat => \"flat\",\n        I::PerVertex => unreachable!(),\n    }\n}\n\n/// Return the GLSL auxiliary qualifier for the given sampling value.\npub(in crate::back::glsl) const fn glsl_sampling(\n    sampling: crate::Sampling,\n) -> BackendResult<Option<&'static str>> {\n    use crate::Sampling as S;\n\n    Ok(match sampling {\n        S::First => return Err(Error::FirstSamplingNotSupported),\n        S::Center | S::Either => None,\n        S::Centroid => Some(\"centroid\"),\n        S::Sample => Some(\"sample\"),\n    })\n}\n\n/// Helper function that returns the glsl dimension string of [`ImageDimension`](crate::ImageDimension)\npub(in crate::back::glsl) const fn glsl_dimension(dim: crate::ImageDimension) -> &'static str {\n    use crate::ImageDimension as IDim;\n\n    match dim {\n        IDim::D1 => \"1D\",\n        IDim::D2 => \"2D\",\n        IDim::D3 => \"3D\",\n        IDim::Cube => \"Cube\",\n    }\n}\n\n/// Helper function that returns the glsl storage format string of [`StorageFormat`](crate::StorageFormat)\npub(in crate::back::glsl) fn glsl_storage_format(\n    format: crate::StorageFormat,\n) -> Result<&'static str, Error> {\n    use crate::StorageFormat as Sf;\n\n    Ok(match format {\n        Sf::R8Unorm => \"r8\",\n        Sf::R8Snorm => \"r8_snorm\",\n        Sf::R8Uint => \"r8ui\",\n        Sf::R8Sint => \"r8i\",\n        Sf::R16Uint => \"r16ui\",\n        Sf::R16Sint => \"r16i\",\n        Sf::R16Float => \"r16f\",\n        Sf::Rg8Unorm => \"rg8\",\n        Sf::Rg8Snorm => \"rg8_snorm\",\n        Sf::Rg8Uint => \"rg8ui\",\n        Sf::Rg8Sint => \"rg8i\",\n        Sf::R32Uint => \"r32ui\",\n        Sf::R32Sint => \"r32i\",\n        Sf::R32Float => \"r32f\",\n        Sf::Rg16Uint => \"rg16ui\",\n        Sf::Rg16Sint => \"rg16i\",\n        Sf::Rg16Float => \"rg16f\",\n        Sf::Rgba8Unorm => \"rgba8\",\n        Sf::Rgba8Snorm => \"rgba8_snorm\",\n        Sf::Rgba8Uint => \"rgba8ui\",\n        Sf::Rgba8Sint => \"rgba8i\",\n        Sf::Rgb10a2Uint => \"rgb10_a2ui\",\n        Sf::Rgb10a2Unorm => \"rgb10_a2\",\n        Sf::Rg11b10Ufloat => \"r11f_g11f_b10f\",\n        Sf::R64Uint => \"r64ui\",\n        Sf::Rg32Uint => \"rg32ui\",\n        Sf::Rg32Sint => \"rg32i\",\n        Sf::Rg32Float => \"rg32f\",\n        Sf::Rgba16Uint => \"rgba16ui\",\n        Sf::Rgba16Sint => \"rgba16i\",\n        Sf::Rgba16Float => \"rgba16f\",\n        Sf::Rgba32Uint => \"rgba32ui\",\n        Sf::Rgba32Sint => \"rgba32i\",\n        Sf::Rgba32Float => \"rgba32f\",\n        Sf::R16Unorm => \"r16\",\n        Sf::R16Snorm => \"r16_snorm\",\n        Sf::Rg16Unorm => \"rg16\",\n        Sf::Rg16Snorm => \"rg16_snorm\",\n        Sf::Rgba16Unorm => \"rgba16\",\n        Sf::Rgba16Snorm => \"rgba16_snorm\",\n\n        Sf::Bgra8Unorm => {\n            return Err(Error::Custom(\n                \"Support format BGRA8 is not implemented\".into(),\n            ))\n        }\n    })\n}\n"
  },
  {
    "path": "naga/src/back/glsl/features.rs",
    "content": "use core::fmt::Write;\n\nuse super::{BackendResult, Error, Version, Writer};\nuse crate::{\n    back::glsl::{Options, WriterFlags},\n    AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation,\n    SampleLevel, Sampling, Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner,\n};\n\nbitflags::bitflags! {\n    /// Structure used to encode additions to GLSL that aren't supported by all versions.\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct Features: u32 {\n        /// Buffer address space support.\n        const BUFFER_STORAGE = 1;\n        const ARRAY_OF_ARRAYS = 1 << 1;\n        /// 8 byte floats.\n        const DOUBLE_TYPE = 1 << 2;\n        /// More image formats.\n        const FULL_IMAGE_FORMATS = 1 << 3;\n        const MULTISAMPLED_TEXTURES = 1 << 4;\n        const MULTISAMPLED_TEXTURE_ARRAYS = 1 << 5;\n        const CUBE_TEXTURES_ARRAY = 1 << 6;\n        const COMPUTE_SHADER = 1 << 7;\n        /// Image load and early depth tests.\n        const IMAGE_LOAD_STORE = 1 << 8;\n        const CONSERVATIVE_DEPTH = 1 << 9;\n        /// Interpolation and auxiliary qualifiers.\n        ///\n        /// Perspective, Flat, and Centroid are available in all GLSL versions we support.\n        const NOPERSPECTIVE_QUALIFIER = 1 << 11;\n        const SAMPLE_QUALIFIER = 1 << 12;\n        const CLIP_DISTANCE = 1 << 13;\n        const CULL_DISTANCE = 1 << 14;\n        /// Sample ID.\n        const SAMPLE_VARIABLES = 1 << 15;\n        /// Arrays with a dynamic length.\n        const DYNAMIC_ARRAY_SIZE = 1 << 16;\n        const MULTI_VIEW = 1 << 17;\n        /// Texture samples query\n        const TEXTURE_SAMPLES = 1 << 18;\n        /// Texture levels query\n        const TEXTURE_LEVELS = 1 << 19;\n        /// Image size query\n        const IMAGE_SIZE = 1 << 20;\n        /// Dual source blending\n        const DUAL_SOURCE_BLENDING = 1 << 21;\n        /// Instance index\n        ///\n        /// We can always support this, either through the language or a polyfill\n        const INSTANCE_INDEX = 1 << 22;\n        /// Sample specific LODs of cube / array shadow textures\n        const TEXTURE_SHADOW_LOD = 1 << 23;\n        /// Subgroup operations\n        const SUBGROUP_OPERATIONS = 1 << 24;\n        /// Image atomics\n        const TEXTURE_ATOMICS = 1 << 25;\n        /// Image atomics\n        const SHADER_BARYCENTRICS = 1 << 26;\n        /// Primitive index builtin\n        const PRIMITIVE_INDEX = 1 << 27;\n    }\n}\n\n/// Helper structure used to store the required [`Features`] needed to output a\n/// [`Module`](crate::Module)\n///\n/// Provides helper methods to check for availability and writing required extensions\npub(crate) struct FeaturesManager(Features);\n\nimpl FeaturesManager {\n    /// Creates a new [`FeaturesManager`] instance\n    pub const fn new() -> Self {\n        Self(Features::empty())\n    }\n\n    /// Adds to the list of required [`Features`]\n    pub fn request(&mut self, features: Features) {\n        self.0 |= features\n    }\n\n    /// Checks if the list of features [`Features`] contains the specified [`Features`]\n    pub const fn contains(&mut self, features: Features) -> bool {\n        self.0.contains(features)\n    }\n\n    /// Checks that all required [`Features`] are available for the specified\n    /// [`Version`] otherwise returns an [`Error::MissingFeatures`].\n    pub fn check_availability(&self, version: Version) -> BackendResult {\n        // Will store all the features that are unavailable\n        let mut missing = Features::empty();\n\n        // Helper macro to check for feature availability\n        macro_rules! check_feature {\n            // Used when only core glsl supports the feature\n            ($feature:ident, $core:literal) => {\n                if self.0.contains(Features::$feature)\n                    && (version < Version::Desktop($core) || version.is_es())\n                {\n                    missing |= Features::$feature;\n                }\n            };\n            // Used when both core and es support the feature\n            ($feature:ident, $core:literal, $es:literal) => {\n                if self.0.contains(Features::$feature)\n                    && (version < Version::Desktop($core) || version < Version::new_gles($es))\n                {\n                    missing |= Features::$feature;\n                }\n            };\n        }\n\n        check_feature!(COMPUTE_SHADER, 420, 310);\n        check_feature!(BUFFER_STORAGE, 400, 310);\n        check_feature!(DOUBLE_TYPE, 150);\n        check_feature!(CUBE_TEXTURES_ARRAY, 130, 310);\n        check_feature!(MULTISAMPLED_TEXTURES, 150, 300);\n        check_feature!(MULTISAMPLED_TEXTURE_ARRAYS, 150, 310);\n        check_feature!(ARRAY_OF_ARRAYS, 120, 310);\n        check_feature!(IMAGE_LOAD_STORE, 130, 310);\n        check_feature!(CONSERVATIVE_DEPTH, 130, 300);\n        check_feature!(NOPERSPECTIVE_QUALIFIER, 130);\n        check_feature!(SAMPLE_QUALIFIER, 400, 320);\n        check_feature!(CLIP_DISTANCE, 130, 300 /* with extension */);\n        check_feature!(CULL_DISTANCE, 450, 300 /* with extension */);\n        check_feature!(SAMPLE_VARIABLES, 400, 300);\n        check_feature!(DYNAMIC_ARRAY_SIZE, 400 /* with extension */, 310);\n        check_feature!(DUAL_SOURCE_BLENDING, 330, 300 /* with extension */);\n        check_feature!(SUBGROUP_OPERATIONS, 430, 310);\n        check_feature!(TEXTURE_ATOMICS, 420, 310);\n        match version {\n            Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),\n            _ => check_feature!(MULTI_VIEW, 140, 310),\n        };\n        // Only available on glsl core, this means that opengl es can't query the number\n        // of samples nor levels in a image and neither do bound checks on the sample nor\n        // the level argument of texelFecth\n        check_feature!(TEXTURE_SAMPLES, 150);\n        check_feature!(TEXTURE_LEVELS, 130);\n        check_feature!(IMAGE_SIZE, 430, 310);\n        check_feature!(TEXTURE_SHADOW_LOD, 200, 300);\n\n        // Return an error if there are missing features\n        if missing.is_empty() {\n            Ok(())\n        } else {\n            Err(Error::MissingFeatures(missing))\n        }\n    }\n\n    /// Helper method used to write all needed extensions\n    ///\n    /// # Notes\n    /// This won't check for feature availability so it might output extensions that aren't even\n    /// supported.[`check_availability`](Self::check_availability) will check feature availability\n    pub fn write(&self, options: &Options, mut out: impl Write) -> BackendResult {\n        if self.0.contains(Features::COMPUTE_SHADER) && !options.version.is_es() {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_compute_shader.txt\n            writeln!(out, \"#extension GL_ARB_compute_shader : require\")?;\n        }\n\n        if self.0.contains(Features::BUFFER_STORAGE) && !options.version.is_es() {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_storage_buffer_object.txt\n            writeln!(\n                out,\n                \"#extension GL_ARB_shader_storage_buffer_object : require\"\n            )?;\n        }\n\n        if self.0.contains(Features::DOUBLE_TYPE) && options.version < Version::Desktop(400) {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gpu_shader_fp64.txt\n            writeln!(out, \"#extension GL_ARB_gpu_shader_fp64 : require\")?;\n        }\n\n        if self.0.contains(Features::CUBE_TEXTURES_ARRAY) {\n            if options.version.is_es() {\n                // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_cube_map_array.txt\n                writeln!(out, \"#extension GL_EXT_texture_cube_map_array : require\")?;\n            } else if options.version < Version::Desktop(400) {\n                // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_cube_map_array.txt\n                writeln!(out, \"#extension GL_ARB_texture_cube_map_array : require\")?;\n            }\n        }\n\n        if self.0.contains(Features::MULTISAMPLED_TEXTURE_ARRAYS) && options.version.is_es() {\n            // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_texture_storage_multisample_2d_array.txt\n            writeln!(\n                out,\n                \"#extension GL_OES_texture_storage_multisample_2d_array : require\"\n            )?;\n        }\n\n        if self.0.contains(Features::ARRAY_OF_ARRAYS) && options.version < Version::Desktop(430) {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_arrays_of_arrays.txt\n            writeln!(out, \"#extension ARB_arrays_of_arrays : require\")?;\n        }\n\n        if self.0.contains(Features::IMAGE_LOAD_STORE) {\n            if self.0.contains(Features::FULL_IMAGE_FORMATS) && options.version.is_es() {\n                // https://www.khronos.org/registry/OpenGL/extensions/NV/NV_image_formats.txt\n                writeln!(out, \"#extension GL_NV_image_formats : require\")?;\n            }\n\n            if options.version < Version::Desktop(420) {\n                // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_image_load_store.txt\n                writeln!(out, \"#extension GL_ARB_shader_image_load_store : require\")?;\n            }\n        }\n\n        if self.0.contains(Features::CONSERVATIVE_DEPTH) {\n            if options.version.is_es() {\n                // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_conservative_depth.txt\n                writeln!(out, \"#extension GL_EXT_conservative_depth : require\")?;\n            }\n\n            if options.version < Version::Desktop(420) {\n                // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt\n                writeln!(out, \"#extension GL_ARB_conservative_depth : require\")?;\n            }\n        }\n\n        if (self.0.contains(Features::CLIP_DISTANCE) || self.0.contains(Features::CULL_DISTANCE))\n            && options.version.is_es()\n        {\n            // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_clip_cull_distance.txt\n            writeln!(out, \"#extension GL_EXT_clip_cull_distance : require\")?;\n        }\n\n        if self.0.contains(Features::SAMPLE_VARIABLES) && options.version.is_es() {\n            // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_sample_variables.txt\n            writeln!(out, \"#extension GL_OES_sample_variables : require\")?;\n        }\n\n        if self.0.contains(Features::MULTI_VIEW) {\n            if let Version::Embedded { is_webgl: true, .. } = options.version {\n                // https://www.khronos.org/registry/OpenGL/extensions/OVR/OVR_multiview2.txt\n                writeln!(out, \"#extension GL_OVR_multiview2 : require\")?;\n            } else {\n                // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_multiview.txt\n                writeln!(out, \"#extension GL_EXT_multiview : require\")?;\n            }\n        }\n\n        if self.0.contains(Features::TEXTURE_SAMPLES) {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_texture_image_samples.txt\n            writeln!(\n                out,\n                \"#extension GL_ARB_shader_texture_image_samples : require\"\n            )?;\n        }\n\n        if self.0.contains(Features::TEXTURE_LEVELS) && options.version < Version::Desktop(430) {\n            // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt\n            writeln!(out, \"#extension GL_ARB_texture_query_levels : require\")?;\n        }\n        if self.0.contains(Features::DUAL_SOURCE_BLENDING) && options.version.is_es() {\n            // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_blend_func_extended.txt\n            writeln!(out, \"#extension GL_EXT_blend_func_extended : require\")?;\n        }\n\n        if self.0.contains(Features::INSTANCE_INDEX) {\n            if options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS) {\n                // https://registry.khronos.org/OpenGL/extensions/ARB/ARB_shader_draw_parameters.txt\n                writeln!(out, \"#extension GL_ARB_shader_draw_parameters : require\")?;\n            }\n        }\n\n        if self.0.contains(Features::TEXTURE_SHADOW_LOD) {\n            // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shadow_lod.txt\n            writeln!(out, \"#extension GL_EXT_texture_shadow_lod : require\")?;\n        }\n\n        if self.0.contains(Features::SUBGROUP_OPERATIONS) {\n            // https://registry.khronos.org/OpenGL/extensions/KHR/KHR_shader_subgroup.txt\n            writeln!(out, \"#extension GL_KHR_shader_subgroup_basic : require\")?;\n            writeln!(out, \"#extension GL_KHR_shader_subgroup_vote : require\")?;\n            writeln!(\n                out,\n                \"#extension GL_KHR_shader_subgroup_arithmetic : require\"\n            )?;\n            writeln!(out, \"#extension GL_KHR_shader_subgroup_ballot : require\")?;\n            writeln!(out, \"#extension GL_KHR_shader_subgroup_shuffle : require\")?;\n            writeln!(\n                out,\n                \"#extension GL_KHR_shader_subgroup_shuffle_relative : require\"\n            )?;\n            writeln!(out, \"#extension GL_KHR_shader_subgroup_quad : require\")?;\n        }\n\n        if self.0.contains(Features::TEXTURE_ATOMICS) {\n            // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_shader_image_atomic.txt\n            writeln!(out, \"#extension GL_OES_shader_image_atomic : require\")?;\n        }\n\n        if self.0.contains(Features::SHADER_BARYCENTRICS) {\n            // https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_fragment_shader_barycentric.txt\n            writeln!(\n                out,\n                \"#extension GL_EXT_fragment_shader_barycentric : require\"\n            )?;\n        }\n\n        if self.0.contains(Features::PRIMITIVE_INDEX) {\n            match options.version {\n                Version::Embedded { version, .. } if version < 320 => {\n                    writeln!(out, \"#extension GL_OES_geometry_shader : require\")?;\n                }\n                Version::Desktop(version) if version < 150 => {\n                    writeln!(out, \"#extension GL_ARB_geometry_shader4 : require\")?;\n                }\n                _ => (),\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl<W> Writer<'_, W> {\n    /// Helper method that searches the module for all the needed [`Features`]\n    ///\n    /// # Errors\n    /// If the version doesn't support any of the needed [`Features`] a\n    /// [`Error::MissingFeatures`] will be returned\n    pub(super) fn collect_required_features(&mut self) -> BackendResult {\n        let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);\n\n        if let Some(early_depth_test) = self.entry_point.early_depth_test {\n            match early_depth_test {\n                crate::EarlyDepthTest::Force => {\n                    if self.options.version.supports_early_depth_test() {\n                        self.features.request(Features::IMAGE_LOAD_STORE);\n                    }\n                }\n                crate::EarlyDepthTest::Allow { .. } => {\n                    self.features.request(Features::CONSERVATIVE_DEPTH);\n                }\n            }\n        }\n\n        for arg in self.entry_point.function.arguments.iter() {\n            self.varying_required_features(arg.binding.as_ref(), arg.ty);\n        }\n        if let Some(ref result) = self.entry_point.function.result {\n            self.varying_required_features(result.binding.as_ref(), result.ty);\n        }\n\n        if let ShaderStage::Compute = self.entry_point.stage {\n            self.features.request(Features::COMPUTE_SHADER)\n        }\n\n        if self.multiview.is_some() {\n            self.features.request(Features::MULTI_VIEW);\n        }\n\n        for (ty_handle, ty) in self.module.types.iter() {\n            match ty.inner {\n                TypeInner::Scalar(scalar)\n                | TypeInner::Vector { scalar, .. }\n                | TypeInner::Matrix { scalar, .. } => self.scalar_required_features(scalar),\n                TypeInner::Array { base, size, .. } => {\n                    if let TypeInner::Array { .. } = self.module.types[base].inner {\n                        self.features.request(Features::ARRAY_OF_ARRAYS)\n                    }\n\n                    // If the array is dynamically sized\n                    if size == crate::ArraySize::Dynamic {\n                        let mut is_used = false;\n\n                        // Check if this type is used in a global that is needed by the current entrypoint\n                        for (global_handle, global) in self.module.global_variables.iter() {\n                            // Skip unused globals\n                            if ep_info[global_handle].is_empty() {\n                                continue;\n                            }\n\n                            // If this array is the type of a global, then this array is used\n                            if global.ty == ty_handle {\n                                is_used = true;\n                                break;\n                            }\n\n                            // If the type of this global is a struct\n                            if let TypeInner::Struct { ref members, .. } =\n                                self.module.types[global.ty].inner\n                            {\n                                // Check the last element of the struct to see if it's type uses\n                                // this array\n                                if let Some(last) = members.last() {\n                                    if last.ty == ty_handle {\n                                        is_used = true;\n                                        break;\n                                    }\n                                }\n                            }\n                        }\n\n                        // If this dynamically size array is used, we need dynamic array size support\n                        if is_used {\n                            self.features.request(Features::DYNAMIC_ARRAY_SIZE);\n                        }\n                    }\n                }\n                TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                } => {\n                    if arrayed && dim == ImageDimension::Cube {\n                        self.features.request(Features::CUBE_TEXTURES_ARRAY)\n                    }\n\n                    match class {\n                        ImageClass::Sampled { multi: true, .. }\n                        | ImageClass::Depth { multi: true } => {\n                            self.features.request(Features::MULTISAMPLED_TEXTURES);\n                            if arrayed {\n                                self.features.request(Features::MULTISAMPLED_TEXTURE_ARRAYS);\n                            }\n                        }\n                        ImageClass::Storage { format, .. } => match format {\n                            StorageFormat::R8Unorm\n                            | StorageFormat::R8Snorm\n                            | StorageFormat::R8Uint\n                            | StorageFormat::R8Sint\n                            | StorageFormat::R16Uint\n                            | StorageFormat::R16Sint\n                            | StorageFormat::R16Float\n                            | StorageFormat::Rg8Unorm\n                            | StorageFormat::Rg8Snorm\n                            | StorageFormat::Rg8Uint\n                            | StorageFormat::Rg8Sint\n                            | StorageFormat::Rg16Uint\n                            | StorageFormat::Rg16Sint\n                            | StorageFormat::Rg16Float\n                            | StorageFormat::Rgb10a2Uint\n                            | StorageFormat::Rgb10a2Unorm\n                            | StorageFormat::Rg11b10Ufloat\n                            | StorageFormat::R64Uint\n                            | StorageFormat::Rg32Uint\n                            | StorageFormat::Rg32Sint\n                            | StorageFormat::Rg32Float => {\n                                self.features.request(Features::FULL_IMAGE_FORMATS)\n                            }\n                            _ => {}\n                        },\n                        ImageClass::Sampled { multi: false, .. }\n                        | ImageClass::Depth { multi: false }\n                        | ImageClass::External => {}\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        let mut immediates_used = false;\n\n        for (handle, global) in self.module.global_variables.iter() {\n            if ep_info[handle].is_empty() {\n                continue;\n            }\n            match global.space {\n                AddressSpace::WorkGroup => self.features.request(Features::COMPUTE_SHADER),\n                AddressSpace::Storage { .. } => self.features.request(Features::BUFFER_STORAGE),\n                AddressSpace::Immediate => {\n                    if immediates_used {\n                        return Err(Error::MultipleImmediateData);\n                    }\n                    immediates_used = true;\n                }\n                _ => {}\n            }\n        }\n\n        // We will need to pass some of the members to a closure, so we need\n        // to separate them otherwise the borrow checker will complain, this\n        // shouldn't be needed in rust 2021\n        let &mut Self {\n            module,\n            info,\n            ref mut features,\n            entry_point,\n            entry_point_idx,\n            ref policies,\n            ..\n        } = self;\n\n        // Loop through all expressions in both functions and the entry point\n        // to check for needed features\n        for (expressions, info) in module\n            .functions\n            .iter()\n            .map(|(h, f)| (&f.expressions, &info[h]))\n            .chain(core::iter::once((\n                &entry_point.function.expressions,\n                info.get_entry_point(entry_point_idx as usize),\n            )))\n        {\n            for (_, expr) in expressions.iter() {\n                match *expr {\n                // Check for queries that need aditonal features\n                Expression::ImageQuery {\n                    image,\n                    query,\n                    ..\n                } => match query {\n                    // Storage images use `imageSize` which is only available\n                    // in glsl > 420\n                    //\n                    // layers queries are also implemented as size queries\n                    crate::ImageQuery::Size { .. } | crate::ImageQuery::NumLayers => {\n                        if let TypeInner::Image {\n                            class: ImageClass::Storage { .. }, ..\n                        } = *info[image].ty.inner_with(&module.types) {\n                            features.request(Features::IMAGE_SIZE)\n                        }\n                    },\n                    crate::ImageQuery::NumLevels => features.request(Features::TEXTURE_LEVELS),\n                    crate::ImageQuery::NumSamples => features.request(Features::TEXTURE_SAMPLES),\n                }\n                ,\n                // Check for image loads that needs bound checking on the sample\n                // or level argument since this requires a feature\n                Expression::ImageLoad {\n                    sample, level, ..\n                } => {\n                    if policies.image_load != crate::proc::BoundsCheckPolicy::Unchecked {\n                        if sample.is_some() {\n                            features.request(Features::TEXTURE_SAMPLES)\n                        }\n\n                        if level.is_some() {\n                            features.request(Features::TEXTURE_LEVELS)\n                        }\n                    }\n                }\n                Expression::ImageSample { image, level, offset, .. } => {\n                    if let TypeInner::Image {\n                        dim,\n                        arrayed,\n                        class: ImageClass::Depth { .. },\n                    } = *info[image].ty.inner_with(&module.types) {\n                        let lod = matches!(level, SampleLevel::Zero | SampleLevel::Exact(_));\n                        let bias = matches!(level, SampleLevel::Bias(_));\n                        let auto = matches!(level, SampleLevel::Auto);\n                        let cube = dim == ImageDimension::Cube;\n                        let array2d = dim == ImageDimension::D2 && arrayed;\n                        let gles = self.options.version.is_es();\n\n                        // We have a workaround of using `textureGrad` instead of `textureLod` if the LOD is zero,\n                        // so we don't *need* this extension for those cases.\n                        // But if we're explicitly allowed to use the extension (`WriterFlags::TEXTURE_SHADOW_LOD`),\n                        // we always use it instead of the workaround.\n                        let grad_workaround_applicable = (array2d || (cube && !arrayed)) && level == SampleLevel::Zero;\n                        let prefer_grad_workaround = grad_workaround_applicable && !self.options.writer_flags.contains(WriterFlags::TEXTURE_SHADOW_LOD);\n\n                        let mut ext_used = false;\n\n                        // float texture(sampler2DArrayShadow sampler, vec4 P [, float bias])\n                        // float texture(samplerCubeArrayShadow sampler, vec4 P, float compare [, float bias])\n                        ext_used |= (array2d || cube && arrayed) && bias;\n\n                        // The non `bias` version of this was standardized in GL 4.3, but never in GLES.\n                        // float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset [, float bias])\n                        ext_used |= array2d && (bias || (gles && auto)) && offset.is_some();\n\n                        // float textureLod(sampler2DArrayShadow sampler, vec4 P, float lod)\n                        // float textureLodOffset(sampler2DArrayShadow sampler, vec4 P, float lod, ivec2 offset)\n                        // float textureLod(samplerCubeShadow sampler, vec4 P, float lod)\n                        // float textureLod(samplerCubeArrayShadow sampler, vec4 P, float compare, float lod)\n                        ext_used |= (cube || array2d) && lod && !prefer_grad_workaround;\n\n                        if ext_used {\n                            features.request(Features::TEXTURE_SHADOW_LOD);\n                        }\n                    }\n                }\n                Expression::SubgroupBallotResult |\n                Expression::SubgroupOperationResult { .. } => {\n                    features.request(Features::SUBGROUP_OPERATIONS)\n                }\n                _ => {}\n            }\n            }\n        }\n\n        for blocks in module\n            .functions\n            .iter()\n            .map(|(_, f)| &f.body)\n            .chain(core::iter::once(&entry_point.function.body))\n        {\n            for (stmt, _) in blocks.span_iter() {\n                match *stmt {\n                    crate::Statement::ImageAtomic { .. } => {\n                        features.request(Features::TEXTURE_ATOMICS)\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        self.features.check_availability(self.options.version)\n    }\n\n    /// Helper method that checks the [`Features`] needed by a scalar\n    fn scalar_required_features(&mut self, scalar: Scalar) {\n        if scalar.kind == ScalarKind::Float && scalar.width == 8 {\n            self.features.request(Features::DOUBLE_TYPE);\n        }\n    }\n\n    fn varying_required_features(&mut self, binding: Option<&Binding>, ty: Handle<Type>) {\n        if let TypeInner::Struct { ref members, .. } = self.module.types[ty].inner {\n            for member in members {\n                self.varying_required_features(member.binding.as_ref(), member.ty);\n            }\n        } else if let Some(binding) = binding {\n            match *binding {\n                Binding::BuiltIn(built_in) => match built_in {\n                    crate::BuiltIn::ClipDistances => self.features.request(Features::CLIP_DISTANCE),\n                    crate::BuiltIn::CullDistance => self.features.request(Features::CULL_DISTANCE),\n                    crate::BuiltIn::SampleIndex => {\n                        self.features.request(Features::SAMPLE_VARIABLES)\n                    }\n                    crate::BuiltIn::ViewIndex => self.features.request(Features::MULTI_VIEW),\n                    crate::BuiltIn::InstanceIndex | crate::BuiltIn::DrawIndex => {\n                        self.features.request(Features::INSTANCE_INDEX)\n                    }\n                    crate::BuiltIn::Barycentric { .. } => {\n                        self.features.request(Features::SHADER_BARYCENTRICS)\n                    }\n                    crate::BuiltIn::PrimitiveIndex => {\n                        self.features.request(Features::PRIMITIVE_INDEX)\n                    }\n                    _ => {}\n                },\n                Binding::Location {\n                    location: _,\n                    interpolation,\n                    sampling,\n                    blend_src,\n                    per_primitive: _,\n                } => {\n                    if interpolation == Some(Interpolation::Linear) {\n                        self.features.request(Features::NOPERSPECTIVE_QUALIFIER);\n                    }\n                    if sampling == Some(Sampling::Sample) {\n                        self.features.request(Features::SAMPLE_QUALIFIER);\n                    }\n                    if blend_src.is_some() {\n                        self.features.request(Features::DUAL_SOURCE_BLENDING);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/glsl/keywords.rs",
    "content": "use crate::proc::KeywordSet;\nuse crate::racy_lock::RacyLock;\n\npub const RESERVED_KEYWORDS: &[&str] = &[\n    //\n    // GLSL 4.6 keywords, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L2004-L2322\n    // GLSL ES 3.2 keywords, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/es/3.2/GLSL_ES_Specification_3.20.html#L2166-L2478\n    //\n    // Note: The GLSL ES 3.2 keywords are the same as GLSL 4.6 keywords with some residing in the reserved section.\n    // The only exception are the missing Vulkan keywords which I think is an oversight (see https://github.com/KhronosGroup/OpenGL-Registry/issues/585).\n    //\n    \"const\",\n    \"uniform\",\n    \"buffer\",\n    \"shared\",\n    \"attribute\",\n    \"varying\",\n    \"coherent\",\n    \"volatile\",\n    \"restrict\",\n    \"readonly\",\n    \"writeonly\",\n    \"atomic_uint\",\n    \"layout\",\n    \"centroid\",\n    \"flat\",\n    \"smooth\",\n    \"noperspective\",\n    \"patch\",\n    \"sample\",\n    \"invariant\",\n    \"precise\",\n    \"break\",\n    \"continue\",\n    \"do\",\n    \"for\",\n    \"while\",\n    \"switch\",\n    \"case\",\n    \"default\",\n    \"if\",\n    \"else\",\n    \"subroutine\",\n    \"in\",\n    \"out\",\n    \"inout\",\n    \"int\",\n    \"void\",\n    \"bool\",\n    \"true\",\n    \"false\",\n    \"float\",\n    \"double\",\n    \"discard\",\n    \"return\",\n    \"vec2\",\n    \"vec3\",\n    \"vec4\",\n    \"ivec2\",\n    \"ivec3\",\n    \"ivec4\",\n    \"bvec2\",\n    \"bvec3\",\n    \"bvec4\",\n    \"uint\",\n    \"uvec2\",\n    \"uvec3\",\n    \"uvec4\",\n    \"dvec2\",\n    \"dvec3\",\n    \"dvec4\",\n    \"mat2\",\n    \"mat3\",\n    \"mat4\",\n    \"mat2x2\",\n    \"mat2x3\",\n    \"mat2x4\",\n    \"mat3x2\",\n    \"mat3x3\",\n    \"mat3x4\",\n    \"mat4x2\",\n    \"mat4x3\",\n    \"mat4x4\",\n    \"dmat2\",\n    \"dmat3\",\n    \"dmat4\",\n    \"dmat2x2\",\n    \"dmat2x3\",\n    \"dmat2x4\",\n    \"dmat3x2\",\n    \"dmat3x3\",\n    \"dmat3x4\",\n    \"dmat4x2\",\n    \"dmat4x3\",\n    \"dmat4x4\",\n    \"lowp\",\n    \"mediump\",\n    \"highp\",\n    \"precision\",\n    \"sampler1D\",\n    \"sampler1DShadow\",\n    \"sampler1DArray\",\n    \"sampler1DArrayShadow\",\n    \"isampler1D\",\n    \"isampler1DArray\",\n    \"usampler1D\",\n    \"usampler1DArray\",\n    \"sampler2D\",\n    \"sampler2DShadow\",\n    \"sampler2DArray\",\n    \"sampler2DArrayShadow\",\n    \"isampler2D\",\n    \"isampler2DArray\",\n    \"usampler2D\",\n    \"usampler2DArray\",\n    \"sampler2DRect\",\n    \"sampler2DRectShadow\",\n    \"isampler2DRect\",\n    \"usampler2DRect\",\n    \"sampler2DMS\",\n    \"isampler2DMS\",\n    \"usampler2DMS\",\n    \"sampler2DMSArray\",\n    \"isampler2DMSArray\",\n    \"usampler2DMSArray\",\n    \"sampler3D\",\n    \"isampler3D\",\n    \"usampler3D\",\n    \"samplerCube\",\n    \"samplerCubeShadow\",\n    \"isamplerCube\",\n    \"usamplerCube\",\n    \"samplerCubeArray\",\n    \"samplerCubeArrayShadow\",\n    \"isamplerCubeArray\",\n    \"usamplerCubeArray\",\n    \"samplerBuffer\",\n    \"isamplerBuffer\",\n    \"usamplerBuffer\",\n    \"image1D\",\n    \"iimage1D\",\n    \"uimage1D\",\n    \"image1DArray\",\n    \"iimage1DArray\",\n    \"uimage1DArray\",\n    \"image2D\",\n    \"iimage2D\",\n    \"uimage2D\",\n    \"image2DArray\",\n    \"iimage2DArray\",\n    \"uimage2DArray\",\n    \"image2DRect\",\n    \"iimage2DRect\",\n    \"uimage2DRect\",\n    \"image2DMS\",\n    \"iimage2DMS\",\n    \"uimage2DMS\",\n    \"image2DMSArray\",\n    \"iimage2DMSArray\",\n    \"uimage2DMSArray\",\n    \"image3D\",\n    \"iimage3D\",\n    \"uimage3D\",\n    \"imageCube\",\n    \"iimageCube\",\n    \"uimageCube\",\n    \"imageCubeArray\",\n    \"iimageCubeArray\",\n    \"uimageCubeArray\",\n    \"imageBuffer\",\n    \"iimageBuffer\",\n    \"uimageBuffer\",\n    \"struct\",\n    // Vulkan keywords\n    \"texture1D\",\n    \"texture1DArray\",\n    \"itexture1D\",\n    \"itexture1DArray\",\n    \"utexture1D\",\n    \"utexture1DArray\",\n    \"texture2D\",\n    \"texture2DArray\",\n    \"itexture2D\",\n    \"itexture2DArray\",\n    \"utexture2D\",\n    \"utexture2DArray\",\n    \"texture2DRect\",\n    \"itexture2DRect\",\n    \"utexture2DRect\",\n    \"texture2DMS\",\n    \"itexture2DMS\",\n    \"utexture2DMS\",\n    \"texture2DMSArray\",\n    \"itexture2DMSArray\",\n    \"utexture2DMSArray\",\n    \"texture3D\",\n    \"itexture3D\",\n    \"utexture3D\",\n    \"textureCube\",\n    \"itextureCube\",\n    \"utextureCube\",\n    \"textureCubeArray\",\n    \"itextureCubeArray\",\n    \"utextureCubeArray\",\n    \"textureBuffer\",\n    \"itextureBuffer\",\n    \"utextureBuffer\",\n    \"sampler\",\n    \"samplerShadow\",\n    \"subpassInput\",\n    \"isubpassInput\",\n    \"usubpassInput\",\n    \"subpassInputMS\",\n    \"isubpassInputMS\",\n    \"usubpassInputMS\",\n    // Reserved keywords\n    \"common\",\n    \"partition\",\n    \"active\",\n    \"asm\",\n    \"class\",\n    \"union\",\n    \"enum\",\n    \"typedef\",\n    \"template\",\n    \"this\",\n    \"resource\",\n    \"goto\",\n    \"inline\",\n    \"noinline\",\n    \"public\",\n    \"static\",\n    \"extern\",\n    \"external\",\n    \"interface\",\n    \"long\",\n    \"short\",\n    \"half\",\n    \"fixed\",\n    \"unsigned\",\n    \"superp\",\n    \"input\",\n    \"output\",\n    \"hvec2\",\n    \"hvec3\",\n    \"hvec4\",\n    \"fvec2\",\n    \"fvec3\",\n    \"fvec4\",\n    \"filter\",\n    \"sizeof\",\n    \"cast\",\n    \"namespace\",\n    \"using\",\n    \"sampler3DRect\",\n    // Reserved keywords that were unreserved in GLSL 4.2\n    \"image1DArrayShadow\",\n    \"image1DShadow\",\n    \"image2DArrayShadow\",\n    \"image2DShadow\",\n    // Reserved keywords that were unreserved in GLSL 4.4\n    \"packed\",\n    \"row_major\",\n    //\n    // GLSL 4.6 Built-In Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L13314\n    //\n    // Angle and Trigonometry Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L13469-L13561C5\n    \"radians\",\n    \"degrees\",\n    \"sin\",\n    \"cos\",\n    \"tan\",\n    \"asin\",\n    \"acos\",\n    \"atan\",\n    \"sinh\",\n    \"cosh\",\n    \"tanh\",\n    \"asinh\",\n    \"acosh\",\n    \"atanh\",\n    // Exponential Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L13569-L13620\n    \"pow\",\n    \"exp\",\n    \"log\",\n    \"exp2\",\n    \"log2\",\n    \"sqrt\",\n    \"inversesqrt\",\n    // Common Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L13628-L13908\n    \"abs\",\n    \"sign\",\n    \"floor\",\n    \"trunc\",\n    \"round\",\n    \"roundEven\",\n    \"ceil\",\n    \"fract\",\n    \"mod\",\n    \"modf\",\n    \"min\",\n    \"max\",\n    \"clamp\",\n    \"mix\",\n    \"step\",\n    \"smoothstep\",\n    \"isnan\",\n    \"isinf\",\n    \"floatBitsToInt\",\n    \"floatBitsToUint\",\n    \"intBitsToFloat\",\n    \"uintBitsToFloat\",\n    \"fma\",\n    \"frexp\",\n    \"ldexp\",\n    // Floating-Point Pack and Unpack Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L13916-L14007\n    \"packUnorm2x16\",\n    \"packSnorm2x16\",\n    \"packUnorm4x8\",\n    \"packSnorm4x8\",\n    \"unpackUnorm2x16\",\n    \"unpackSnorm2x16\",\n    \"unpackUnorm4x8\",\n    \"unpackSnorm4x8\",\n    \"packHalf2x16\",\n    \"unpackHalf2x16\",\n    \"packDouble2x32\",\n    \"unpackDouble2x32\",\n    // Geometric Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14014-L14121\n    \"length\",\n    \"distance\",\n    \"dot\",\n    \"cross\",\n    \"normalize\",\n    \"ftransform\",\n    \"faceforward\",\n    \"reflect\",\n    \"refract\",\n    // Matrix Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14151-L14215\n    \"matrixCompMult\",\n    \"outerProduct\",\n    \"transpose\",\n    \"determinant\",\n    \"inverse\",\n    // Vector Relational Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14259-L14322\n    \"lessThan\",\n    \"lessThanEqual\",\n    \"greaterThan\",\n    \"greaterThanEqual\",\n    \"equal\",\n    \"notEqual\",\n    \"any\",\n    \"all\",\n    \"not\",\n    // Integer Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14335-L14432\n    \"uaddCarry\",\n    \"usubBorrow\",\n    \"umulExtended\",\n    \"imulExtended\",\n    \"bitfieldExtract\",\n    \"bitfieldInsert\",\n    \"bitfieldReverse\",\n    \"bitCount\",\n    \"findLSB\",\n    \"findMSB\",\n    // Texture Query Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14645-L14732\n    \"textureSize\",\n    \"textureQueryLod\",\n    \"textureQueryLevels\",\n    \"textureSamples\",\n    // Texel Lookup Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L14736-L14997\n    \"texture\",\n    \"textureProj\",\n    \"textureLod\",\n    \"textureOffset\",\n    \"texelFetch\",\n    \"texelFetchOffset\",\n    \"textureProjOffset\",\n    \"textureLodOffset\",\n    \"textureProjLod\",\n    \"textureProjLodOffset\",\n    \"textureGrad\",\n    \"textureGradOffset\",\n    \"textureProjGrad\",\n    \"textureProjGradOffset\",\n    // Texture Gather Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15077-L15154\n    \"textureGather\",\n    \"textureGatherOffset\",\n    \"textureGatherOffsets\",\n    // Compatibility Profile Texture Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15161-L15220\n    \"texture1D\",\n    \"texture1DProj\",\n    \"texture1DLod\",\n    \"texture1DProjLod\",\n    \"texture2D\",\n    \"texture2DProj\",\n    \"texture2DLod\",\n    \"texture2DProjLod\",\n    \"texture3D\",\n    \"texture3DProj\",\n    \"texture3DLod\",\n    \"texture3DProjLod\",\n    \"textureCube\",\n    \"textureCubeLod\",\n    \"shadow1D\",\n    \"shadow2D\",\n    \"shadow1DProj\",\n    \"shadow2DProj\",\n    \"shadow1DLod\",\n    \"shadow2DLod\",\n    \"shadow1DProjLod\",\n    \"shadow2DProjLod\",\n    // Atomic Counter Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15241-L15531\n    \"atomicCounterIncrement\",\n    \"atomicCounterDecrement\",\n    \"atomicCounter\",\n    \"atomicCounterAdd\",\n    \"atomicCounterSubtract\",\n    \"atomicCounterMin\",\n    \"atomicCounterMax\",\n    \"atomicCounterAnd\",\n    \"atomicCounterOr\",\n    \"atomicCounterXor\",\n    \"atomicCounterExchange\",\n    \"atomicCounterCompSwap\",\n    // Atomic Memory Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15563-L15624\n    \"atomicAdd\",\n    \"atomicMin\",\n    \"atomicMax\",\n    \"atomicAnd\",\n    \"atomicOr\",\n    \"atomicXor\",\n    \"atomicExchange\",\n    \"atomicCompSwap\",\n    // Image Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15763-L15878\n    \"imageSize\",\n    \"imageSamples\",\n    \"imageLoad\",\n    \"imageStore\",\n    \"imageAtomicAdd\",\n    \"imageAtomicMin\",\n    \"imageAtomicMax\",\n    \"imageAtomicAnd\",\n    \"imageAtomicOr\",\n    \"imageAtomicXor\",\n    \"imageAtomicExchange\",\n    \"imageAtomicCompSwap\",\n    // Geometry Shader Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L15886-L15932\n    \"EmitStreamVertex\",\n    \"EndStreamPrimitive\",\n    \"EmitVertex\",\n    \"EndPrimitive\",\n    // Fragment Processing Functions, Derivative Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16041-L16114\n    \"dFdx\",\n    \"dFdy\",\n    \"dFdxFine\",\n    \"dFdyFine\",\n    \"dFdxCoarse\",\n    \"dFdyCoarse\",\n    \"fwidth\",\n    \"fwidthFine\",\n    \"fwidthCoarse\",\n    // Fragment Processing Functions, Interpolation Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16150-L16198\n    \"interpolateAtCentroid\",\n    \"interpolateAtSample\",\n    \"interpolateAtOffset\",\n    // Noise Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16214-L16243\n    \"noise1\",\n    \"noise2\",\n    \"noise3\",\n    \"noise4\",\n    // Shader Invocation Control Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16255-L16276\n    \"barrier\",\n    // Shader Memory Control Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16336-L16382\n    \"memoryBarrier\",\n    \"memoryBarrierAtomicCounter\",\n    \"memoryBarrierBuffer\",\n    \"memoryBarrierShared\",\n    \"memoryBarrierImage\",\n    \"groupMemoryBarrier\",\n    // Subpass-Input Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16451-L16470\n    \"subpassLoad\",\n    // Shader Invocation Group Functions, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L16483-L16511\n    \"anyInvocation\",\n    \"allInvocations\",\n    \"allInvocationsEqual\",\n    //\n    // entry point name (should not be shadowed)\n    //\n    \"main\",\n    // Naga utilities:\n    super::MODF_FUNCTION,\n    super::FREXP_FUNCTION,\n    super::FIRST_INSTANCE_BINDING,\n];\n\n/// The above set of reserved keywords, turned into a cached HashSet. This saves\n/// significant time during [`Namer::reset`](crate::proc::Namer::reset).\n///\n/// See <https://github.com/gfx-rs/wgpu/pull/7338> for benchmarks.\npub static RESERVED_KEYWORD_SET: RacyLock<KeywordSet> =\n    RacyLock::new(|| KeywordSet::from_iter(RESERVED_KEYWORDS));\n"
  },
  {
    "path": "naga/src/back/glsl/mod.rs",
    "content": "/*!\nBackend for [GLSL][glsl] (OpenGL Shading Language).\n\nThe main structure is [`Writer`], it maintains internal state that is used\nto output a [`Module`](crate::Module) into glsl\n\n# Supported versions\n### Core\n- 330\n- 400\n- 410\n- 420\n- 430\n- 450\n\n### ES\n- 300\n- 310\n\n[glsl]: https://www.khronos.org/registry/OpenGL/index_gl.php\n*/\n\n// GLSL is mostly a superset of C but it also removes some parts of it this is a list of relevant\n// aspects for this backend.\n//\n// The most notable change is the introduction of the version preprocessor directive that must\n// always be the first line of a glsl file and is written as\n// `#version number profile`\n// `number` is the version itself (i.e. 300) and `profile` is the\n// shader profile we only support \"core\" and \"es\", the former is used in desktop applications and\n// the later is used in embedded contexts, mobile devices and browsers. Each one as it's own\n// versions (at the time of writing this the latest version for \"core\" is 460 and for \"es\" is 320)\n//\n// Other important preprocessor addition is the extension directive which is written as\n// `#extension name: behaviour`\n// Extensions provide increased features in a plugin fashion but they aren't required to be\n// supported hence why they are called extensions, that's why `behaviour` is used it specifies\n// whether the extension is strictly required or if it should only be enabled if needed. In our case\n// when we use extensions we set behaviour to `require` always.\n//\n// The only thing that glsl removes that makes a difference are pointers.\n//\n// Additions that are relevant for the backend are the discard keyword, the introduction of\n// vector, matrices, samplers, image types and functions that provide common shader operations\n\npub use features::Features;\npub use writer::Writer;\n\nuse alloc::{\n    borrow::ToOwned,\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::{\n    cmp::Ordering,\n    fmt::{self, Error as FmtError, Write},\n    mem,\n};\n\nuse hashbrown::hash_map;\nuse thiserror::Error;\n\nuse crate::{\n    back::{self, Baked},\n    common,\n    proc::{self, NameKey},\n    valid, Handle, ShaderStage, TypeInner,\n};\nuse conv::*;\nuse features::FeaturesManager;\n\n/// Contains simple 1:1 conversion functions.\nmod conv;\n/// Contains the features related code and the features querying method\nmod features;\n/// Contains a constant with a slice of all the reserved keywords RESERVED_KEYWORDS\nmod keywords;\n/// Contains the [`Writer`] type.\nmod writer;\n\n/// List of supported `core` GLSL versions.\npub const SUPPORTED_CORE_VERSIONS: &[u16] = &[140, 150, 330, 400, 410, 420, 430, 440, 450, 460];\n/// List of supported `es` GLSL versions.\npub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];\n\n/// The suffix of the variable that will hold the calculated clamped level\n/// of detail for bounds checking in `ImageLoad`\nconst CLAMPED_LOD_SUFFIX: &str = \"_clamped_lod\";\n\npub(crate) const MODF_FUNCTION: &str = \"naga_modf\";\npub(crate) const FREXP_FUNCTION: &str = \"naga_frexp\";\n\n// Must match code in glsl_built_in\npub const FIRST_INSTANCE_BINDING: &str = \"naga_vs_first_instance\";\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct BindingMapSerialization {\n    resource_binding: crate::ResourceBinding,\n    bind_target: u8,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;\n    let mut map = BindingMap::default();\n    for item in vec {\n        map.insert(item.resource_binding, item.bind_target);\n    }\n    Ok(map)\n}\n\n/// Mapping between resources and bindings.\npub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, u8>;\n\nimpl crate::AtomicFunction {\n    const fn to_glsl(self) -> &'static str {\n        match self {\n            Self::Add | Self::Subtract => \"Add\",\n            Self::And => \"And\",\n            Self::InclusiveOr => \"Or\",\n            Self::ExclusiveOr => \"Xor\",\n            Self::Min => \"Min\",\n            Self::Max => \"Max\",\n            Self::Exchange { compare: None } => \"Exchange\",\n            Self::Exchange { compare: Some(_) } => \"\", //TODO\n        }\n    }\n}\n\nimpl crate::AddressSpace {\n    /// Whether a variable with this address space can be initialized\n    const fn initializable(&self) -> bool {\n        match *self {\n            crate::AddressSpace::Function | crate::AddressSpace::Private => true,\n            crate::AddressSpace::WorkGroup\n            | crate::AddressSpace::Uniform\n            | crate::AddressSpace::Storage { .. }\n            | crate::AddressSpace::Handle\n            | crate::AddressSpace::Immediate\n            | crate::AddressSpace::TaskPayload => false,\n\n            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {\n                unreachable!()\n            }\n        }\n    }\n}\n\n/// A GLSL version.\n#[derive(Debug, Copy, Clone, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum Version {\n    /// `core` GLSL.\n    Desktop(u16),\n    /// `es` GLSL.\n    Embedded { version: u16, is_webgl: bool },\n}\n\nimpl Version {\n    /// Create a new gles version\n    pub const fn new_gles(version: u16) -> Self {\n        Self::Embedded {\n            version,\n            is_webgl: false,\n        }\n    }\n\n    /// Returns true if self is `Version::Embedded` (i.e. is a es version)\n    const fn is_es(&self) -> bool {\n        match *self {\n            Version::Desktop(_) => false,\n            Version::Embedded { .. } => true,\n        }\n    }\n\n    /// Returns true if targeting WebGL\n    const fn is_webgl(&self) -> bool {\n        match *self {\n            Version::Desktop(_) => false,\n            Version::Embedded { is_webgl, .. } => is_webgl,\n        }\n    }\n\n    /// Checks the list of currently supported versions and returns true if it contains the\n    /// specified version\n    ///\n    /// # Notes\n    /// As an invalid version number will never be added to the supported version list\n    /// so this also checks for version validity\n    fn is_supported(&self) -> bool {\n        match *self {\n            Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v),\n            Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v),\n        }\n    }\n\n    fn supports_io_locations(&self) -> bool {\n        *self >= Version::Desktop(330) || *self >= Version::new_gles(300)\n    }\n\n    /// Checks if the version supports all of the explicit layouts:\n    /// - `location=` qualifiers for bindings\n    /// - `binding=` qualifiers for resources\n    ///\n    /// Note: `location=` for vertex inputs and fragment outputs is supported\n    /// unconditionally for GLES 300.\n    fn supports_explicit_locations(&self) -> bool {\n        *self >= Version::Desktop(420) || *self >= Version::new_gles(310)\n    }\n\n    fn supports_early_depth_test(&self) -> bool {\n        *self >= Version::Desktop(130) || *self >= Version::new_gles(310)\n    }\n\n    fn supports_std140_layout(&self) -> bool {\n        *self >= Version::Desktop(140) || *self >= Version::new_gles(300)\n    }\n\n    fn supports_std430_layout(&self) -> bool {\n        // std430 is available from 400 via GL_ARB_shader_storage_buffer_object.\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)\n    }\n\n    fn supports_fma_function(&self) -> bool {\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(320)\n    }\n\n    fn supports_integer_functions(&self) -> bool {\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)\n    }\n\n    fn supports_frexp_function(&self) -> bool {\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)\n    }\n\n    fn supports_derivative_control(&self) -> bool {\n        *self >= Version::Desktop(450)\n    }\n\n    // For supports_pack_unpack_4x8, supports_pack_unpack_snorm_2x16, supports_pack_unpack_unorm_2x16\n    // see:\n    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackUnorm.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackUnorm.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packUnorm.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/es3/html/packUnorm.xhtml\n    fn supports_pack_unpack_4x8(&self) -> bool {\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)\n    }\n    fn supports_pack_unpack_snorm_2x16(&self) -> bool {\n        *self >= Version::Desktop(420) || *self >= Version::new_gles(300)\n    }\n    fn supports_pack_unpack_unorm_2x16(&self) -> bool {\n        *self >= Version::Desktop(400) || *self >= Version::new_gles(300)\n    }\n\n    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackHalf2x16.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packHalf2x16.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackHalf2x16.xhtml\n    // https://registry.khronos.org/OpenGL-Refpages/es3/html/packHalf2x16.xhtml\n    fn supports_pack_unpack_half_2x16(&self) -> bool {\n        *self >= Version::Desktop(420) || *self >= Version::new_gles(300)\n    }\n}\n\nimpl PartialOrd for Version {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        match (*self, *other) {\n            (Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)),\n            (Version::Embedded { version: x, .. }, Version::Embedded { version: y, .. }) => {\n                Some(x.cmp(&y))\n            }\n            _ => None,\n        }\n    }\n}\n\nimpl fmt::Display for Version {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            Version::Desktop(v) => write!(f, \"{v} core\"),\n            Version::Embedded { version: v, .. } => write!(f, \"{v} es\"),\n        }\n    }\n}\n\nbitflags::bitflags! {\n    /// Configuration flags for the [`Writer`].\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct WriterFlags: u32 {\n        /// Flip output Y and extend Z from (0, 1) to (-1, 1).\n        const ADJUST_COORDINATE_SPACE = 0x1;\n        /// Supports GL_EXT_texture_shadow_lod on the host, which provides\n        /// additional functions on shadows and arrays of shadows.\n        const TEXTURE_SHADOW_LOD = 0x2;\n        /// Supports ARB_shader_draw_parameters on the host, which provides\n        /// support for `gl_BaseInstanceARB`, `gl_BaseVertexARB`, `gl_DrawIDARB`, and `gl_DrawID`.\n        const DRAW_PARAMETERS = 0x4;\n        /// Include unused global variables, constants and functions. By default the output will exclude\n        /// global variables that are not used in the specified entrypoint (including indirect use),\n        /// all constant declarations, and functions that use excluded global variables.\n        const INCLUDE_UNUSED_ITEMS = 0x10;\n        /// Emit `PointSize` output builtin to vertex shaders, which is\n        /// required for drawing with `PointList` topology.\n        ///\n        /// https://registry.khronos.org/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.html#built-in-language-variables\n        /// The variable gl_PointSize is intended for a shader to write the size of the point to be rasterized. It is measured in pixels.\n        /// If gl_PointSize is not written to, its value is undefined in subsequent pipe stages.\n        const FORCE_POINT_SIZE = 0x20;\n    }\n}\n\n/// Configuration used in the [`Writer`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct Options {\n    /// The GLSL version to be used.\n    pub version: Version,\n    /// Configuration flags for the [`Writer`].\n    pub writer_flags: WriterFlags,\n    /// Map of resources association to binding locations.\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_binding_map\")\n    )]\n    pub binding_map: BindingMap,\n    /// Should workgroup variables be zero initialized (by polyfilling)?\n    pub zero_initialize_workgroup_memory: bool,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Options {\n            version: Version::new_gles(310),\n            writer_flags: WriterFlags::ADJUST_COORDINATE_SPACE,\n            binding_map: BindingMap::default(),\n            zero_initialize_workgroup_memory: true,\n        }\n    }\n}\n\n/// A subset of options meant to be changed per pipeline.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct PipelineOptions {\n    /// The stage of the entry point.\n    pub shader_stage: ShaderStage,\n    /// The name of the entry point.\n    ///\n    /// If no entry point that matches is found while creating a [`Writer`], an\n    /// error will be thrown.\n    pub entry_point: String,\n    /// How many views to render to, if doing multiview rendering.\n    pub multiview: Option<core::num::NonZeroU32>,\n}\n\n#[derive(Debug)]\npub struct VaryingLocation {\n    /// The location of the global.\n    /// This corresponds to `layout(location = ..)` in GLSL.\n    pub location: u32,\n    /// The index which can be used for dual source blending.\n    /// This corresponds to `layout(index = ..)` in GLSL.\n    pub index: u32,\n}\n\n/// Reflection info for texture mappings and uniforms.\n#[derive(Debug)]\npub struct ReflectionInfo {\n    /// Mapping between texture names and variables/samplers.\n    pub texture_mapping: crate::FastHashMap<String, TextureMapping>,\n    /// Mapping between uniform variables and names.\n    pub uniforms: crate::FastHashMap<Handle<crate::GlobalVariable>, String>,\n    /// Mapping between names and attribute locations.\n    pub varying: crate::FastHashMap<String, VaryingLocation>,\n    /// List of immediate data items in the shader.\n    pub immediates_items: Vec<ImmediateItem>,\n    /// Number of user-defined clip planes. Only applicable to vertex shaders.\n    pub clip_distance_count: u32,\n}\n\n/// Mapping between a texture and its sampler, if it exists.\n///\n/// GLSL pre-Vulkan has no concept of separate textures and samplers. Instead, everything is a\n/// `gsamplerN` where `g` is the scalar type and `N` is the dimension. But naga uses separate textures\n/// and samplers in the IR, so the backend produces a [`FastHashMap`](crate::FastHashMap) with the texture name\n/// as a key and a [`TextureMapping`] as a value. This way, the user knows where to bind.\n///\n/// [`Storage`](crate::ImageClass::Storage) images produce `gimageN` and don't have an associated sampler,\n/// so the [`sampler`](Self::sampler) field will be [`None`].\n#[derive(Debug, Clone)]\npub struct TextureMapping {\n    /// Handle to the image global variable.\n    pub texture: Handle<crate::GlobalVariable>,\n    /// Handle to the associated sampler global variable, if it exists.\n    pub sampler: Option<Handle<crate::GlobalVariable>>,\n}\n\n/// All information to bind a single uniform value to the shader.\n///\n/// Immediates are emulated using traditional uniforms in OpenGL.\n///\n/// These are composed of a set of primitives (scalar, vector, matrix) that\n/// are given names. Because they are not backed by the concept of a buffer,\n/// we must do the work of calculating the offset of each primitive in the\n/// immediate data block.\n#[derive(Debug, Clone)]\npub struct ImmediateItem {\n    /// GL uniform name for the item. This name is the same as if you were\n    /// to access it directly from a GLSL shader.\n    ///\n    /// The with the following example, the following names will be generated,\n    /// one name per GLSL uniform.\n    ///\n    /// ```glsl\n    /// struct InnerStruct {\n    ///     value: f32,\n    /// }\n    ///\n    /// struct ImmediateData {\n    ///     InnerStruct inner;\n    ///     vec4 array[2];\n    /// }\n    ///\n    /// uniform ImmediateData _immediates_binding_cs;\n    /// ```\n    ///\n    /// ```text\n    /// - _immediates_binding_cs.inner.value\n    /// - _immediates_binding_cs.array[0]\n    /// - _immediates_binding_cs.array[1]\n    /// ```\n    ///\n    pub access_path: String,\n    /// Type of the uniform. This will only ever be a scalar, vector, or matrix.\n    pub ty: Handle<crate::Type>,\n    /// The offset in the immediate data memory block this uniform maps to.\n    ///\n    /// The size of the uniform can be derived from the type.\n    pub offset: u32,\n}\n\n/// Helper structure that generates a number\n#[derive(Default)]\nstruct IdGenerator(u32);\n\nimpl IdGenerator {\n    /// Generates a number that's guaranteed to be unique for this `IdGenerator`\n    const fn generate(&mut self) -> u32 {\n        // It's just an increasing number but it does the job\n        let ret = self.0;\n        self.0 += 1;\n        ret\n    }\n}\n\n/// Assorted options needed for generating varyings.\n#[derive(Clone, Copy)]\nstruct VaryingOptions {\n    output: bool,\n    targeting_webgl: bool,\n    draw_parameters: bool,\n}\n\nimpl VaryingOptions {\n    const fn from_writer_options(options: &Options, output: bool) -> Self {\n        Self {\n            output,\n            targeting_webgl: options.version.is_webgl(),\n            draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS),\n        }\n    }\n}\n\n/// Helper wrapper used to get a name for a varying\n///\n/// Varying have different naming schemes depending on their binding:\n/// - Varyings with builtin bindings get their name from [`glsl_built_in`].\n/// - Varyings with location bindings are named `_S_location_X` where `S` is a\n///   prefix identifying which pipeline stage the varying connects, and `X` is\n///   the location.\nstruct VaryingName<'a> {\n    binding: &'a crate::Binding,\n    stage: ShaderStage,\n    options: VaryingOptions,\n}\nimpl fmt::Display for VaryingName<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match *self.binding {\n            crate::Binding::Location {\n                blend_src: Some(1), ..\n            } => {\n                write!(f, \"_fs2p_location1\",)\n            }\n            crate::Binding::Location { location, .. } => {\n                let prefix = match (self.stage, self.options.output) {\n                    (ShaderStage::Compute, _) => unreachable!(),\n                    // pipeline to vertex\n                    (ShaderStage::Vertex, false) => \"p2vs\",\n                    // vertex to fragment\n                    (ShaderStage::Vertex, true) | (ShaderStage::Fragment, false) => \"vs2fs\",\n                    // fragment to pipeline\n                    (ShaderStage::Fragment, true) => \"fs2p\",\n                    (\n                        ShaderStage::Task\n                        | ShaderStage::Mesh\n                        | ShaderStage::RayGeneration\n                        | ShaderStage::AnyHit\n                        | ShaderStage::ClosestHit\n                        | ShaderStage::Miss,\n                        _,\n                    ) => unreachable!(),\n                };\n                write!(f, \"_{prefix}_location{location}\",)\n            }\n            crate::Binding::BuiltIn(built_in) => {\n                write!(f, \"{}\", glsl_built_in(built_in, self.options))\n            }\n        }\n    }\n}\n\nimpl ShaderStage {\n    const fn to_str(self) -> &'static str {\n        match self {\n            ShaderStage::Compute => \"cs\",\n            ShaderStage::Fragment => \"fs\",\n            ShaderStage::Vertex => \"vs\",\n            ShaderStage::Task\n            | ShaderStage::Mesh\n            | ShaderStage::RayGeneration\n            | ShaderStage::AnyHit\n            | ShaderStage::ClosestHit\n            | ShaderStage::Miss => unreachable!(),\n        }\n    }\n}\n\n/// Shorthand result used internally by the backend\ntype BackendResult<T = ()> = Result<T, Error>;\n\n/// A GLSL compilation error.\n#[derive(Debug, Error)]\npub enum Error {\n    /// A error occurred while writing to the output.\n    #[error(\"Format error\")]\n    FmtError(#[from] FmtError),\n    /// The specified [`Version`] doesn't have all required [`Features`].\n    ///\n    /// Contains the missing [`Features`].\n    #[error(\"The selected version doesn't support {0:?}\")]\n    MissingFeatures(Features),\n    /// [`AddressSpace::Immediate`](crate::AddressSpace::Immediate) was used more than\n    /// once in the entry point, which isn't supported.\n    #[error(\"Multiple immediates aren't supported\")]\n    MultipleImmediateData,\n    /// The specified [`Version`] isn't supported.\n    #[error(\"The specified version isn't supported\")]\n    VersionNotSupported,\n    /// The entry point couldn't be found.\n    #[error(\"The requested entry point couldn't be found\")]\n    EntryPointNotFound,\n    /// A call was made to an unsupported external.\n    #[error(\"A call was made to an unsupported external: {0}\")]\n    UnsupportedExternal(String),\n    /// A scalar with an unsupported width was requested.\n    #[error(\"A scalar with an unsupported width was requested: {0:?}\")]\n    UnsupportedScalar(crate::Scalar),\n    /// A image was used with multiple samplers, which isn't supported.\n    #[error(\"A image was used with multiple samplers\")]\n    ImageMultipleSamplers,\n    #[error(\"{0}\")]\n    Custom(String),\n    #[error(\"overrides should not be present at this stage\")]\n    Override,\n    /// [`crate::Sampling::First`] is unsupported.\n    #[error(\"`{:?}` sampling is unsupported\", crate::Sampling::First)]\n    FirstSamplingNotSupported,\n    #[error(transparent)]\n    ResolveArraySizeError(#[from] proc::ResolveArraySizeError),\n}\n\n/// Binary operation with a different logic on the GLSL side.\nenum BinaryOperation {\n    /// Vector comparison should use the function like `greaterThan()`, etc.\n    VectorCompare,\n    /// Vector component wise operation; used to polyfill unsupported ops like `|` and `&` for `bvecN`'s\n    VectorComponentWise,\n    /// GLSL `%` is SPIR-V `OpUMod/OpSMod` and `mod()` is `OpFMod`, but [`BinaryOperator::Modulo`](crate::BinaryOperator::Modulo) is `OpFRem`.\n    Modulo,\n    /// Any plain operation. No additional logic required.\n    Other,\n}\n\nfn is_value_init_supported(module: &crate::Module, ty: Handle<crate::Type>) -> bool {\n    match module.types[ty].inner {\n        TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Matrix { .. } => true,\n        TypeInner::Array { base, size, .. } => {\n            size != crate::ArraySize::Dynamic && is_value_init_supported(module, base)\n        }\n        TypeInner::Struct { ref members, .. } => members\n            .iter()\n            .all(|member| is_value_init_supported(module, member.ty)),\n        _ => false,\n    }\n}\n\npub fn supported_capabilities() -> valid::Capabilities {\n    use valid::Capabilities as Caps;\n\n    // Lots of these aren't supported on GLES in general, but naga is able to write them without panicking.\n\n    Caps::IMMEDIATES\n        | Caps::FLOAT64\n        | Caps::PRIMITIVE_INDEX\n        | Caps::CLIP_DISTANCES\n        | Caps::MULTIVIEW\n        | Caps::EARLY_DEPTH_TEST\n        | Caps::MULTISAMPLED_SHADING\n        | Caps::DUAL_SOURCE_BLENDING\n        | Caps::CUBE_ARRAY_TEXTURES\n        | Caps::SHADER_INT64\n        | Caps::SHADER_INT64_ATOMIC_ALL_OPS\n        | Caps::TEXTURE_ATOMIC\n        | Caps::TEXTURE_INT64_ATOMIC\n        | Caps::SUBGROUP\n        | Caps::SUBGROUP_BARRIER\n        | Caps::SHADER_FLOAT16\n        | Caps::SHADER_FLOAT16_IN_FLOAT32\n        | Caps::SHADER_BARYCENTRICS\n        | Caps::DRAW_INDEX\n        | Caps::MEMORY_DECORATION_COHERENT\n        | Caps::MEMORY_DECORATION_VOLATILE\n}\n"
  },
  {
    "path": "naga/src/back/glsl/writer.rs",
    "content": "use super::*;\n\n/// Writer responsible for all code generation.\npub struct Writer<'a, W> {\n    // Inputs\n    /// The module being written.\n    pub(in crate::back::glsl) module: &'a crate::Module,\n    /// The module analysis.\n    pub(in crate::back::glsl) info: &'a valid::ModuleInfo,\n    /// The output writer.\n    out: W,\n    /// User defined configuration to be used.\n    pub(in crate::back::glsl) options: &'a Options,\n    /// The bound checking policies to be used\n    pub(in crate::back::glsl) policies: proc::BoundsCheckPolicies,\n\n    // Internal State\n    /// Features manager used to store all the needed features and write them.\n    pub(in crate::back::glsl) features: FeaturesManager,\n    namer: proc::Namer,\n    /// A map with all the names needed for writing the module\n    /// (generated by a [`Namer`](crate::proc::Namer)).\n    names: crate::FastHashMap<NameKey, String>,\n    /// A map with the names of global variables needed for reflections.\n    reflection_names_globals: crate::FastHashMap<Handle<crate::GlobalVariable>, String>,\n    /// The selected entry point.\n    pub(in crate::back::glsl) entry_point: &'a crate::EntryPoint,\n    /// The index of the selected entry point.\n    pub(in crate::back::glsl) entry_point_idx: proc::EntryPointIndex,\n    /// A generator for unique block numbers.\n    block_id: IdGenerator,\n    /// Set of expressions that have associated temporary variables.\n    named_expressions: crate::NamedExpressions,\n    /// Set of expressions that need to be baked to avoid unnecessary repetition in output\n    need_bake_expressions: back::NeedBakeExpressions,\n    /// Information about nesting of loops and switches.\n    ///\n    /// Used for forwarding continue statements in switches that have been\n    /// transformed to `do {} while(false);` loops.\n    continue_ctx: back::continue_forward::ContinueCtx,\n    /// How many views to render to, if doing multiview rendering.\n    pub(in crate::back::glsl) multiview: Option<core::num::NonZeroU32>,\n    /// Mapping of varying variables to their location. Needed for reflections.\n    varying: crate::FastHashMap<String, VaryingLocation>,\n    /// Number of user-defined clip planes. Only non-zero for vertex shaders.\n    clip_distance_count: u32,\n}\n\nimpl<'a, W: Write> Writer<'a, W> {\n    /// Creates a new [`Writer`] instance.\n    ///\n    /// # Errors\n    /// - If the version specified is invalid or supported.\n    /// - If the entry point couldn't be found in the module.\n    /// - If the version specified doesn't support some used features.\n    pub fn new(\n        out: W,\n        module: &'a crate::Module,\n        info: &'a valid::ModuleInfo,\n        options: &'a Options,\n        pipeline_options: &'a PipelineOptions,\n        policies: proc::BoundsCheckPolicies,\n    ) -> Result<Self, Error> {\n        // Check if the requested version is supported\n        if !options.version.is_supported() {\n            log::error!(\"Version {}\", options.version);\n            return Err(Error::VersionNotSupported);\n        }\n\n        // Try to find the entry point and corresponding index\n        let ep_idx = module\n            .entry_points\n            .iter()\n            .position(|ep| {\n                pipeline_options.shader_stage == ep.stage && pipeline_options.entry_point == ep.name\n            })\n            .ok_or(Error::EntryPointNotFound)?;\n\n        // Generate a map with names required to write the module\n        let mut names = crate::FastHashMap::default();\n        let mut namer = proc::Namer::default();\n        namer.reset(\n            module,\n            &keywords::RESERVED_KEYWORD_SET,\n            proc::KeywordSet::empty(),\n            proc::CaseInsensitiveKeywordSet::empty(),\n            &[\n                \"gl_\",                  // all GL built-in variables\n                \"_group\",               // all normal bindings\n                \"_immediates_binding_\", // all immediate data bindings\n            ],\n            &mut names,\n        );\n\n        // Build the instance\n        let mut this = Self {\n            module,\n            info,\n            out,\n            options,\n            policies,\n\n            namer,\n            features: FeaturesManager::new(),\n            names,\n            reflection_names_globals: crate::FastHashMap::default(),\n            entry_point: &module.entry_points[ep_idx],\n            entry_point_idx: ep_idx as u16,\n            multiview: pipeline_options.multiview,\n            block_id: IdGenerator::default(),\n            named_expressions: Default::default(),\n            need_bake_expressions: Default::default(),\n            continue_ctx: back::continue_forward::ContinueCtx::default(),\n            varying: Default::default(),\n            clip_distance_count: 0,\n        };\n\n        // Find all features required to print this module\n        this.collect_required_features()?;\n\n        Ok(this)\n    }\n\n    /// Writes the [`Module`](crate::Module) as glsl to the output\n    ///\n    /// # Notes\n    /// If an error occurs while writing, the output might have been written partially\n    ///\n    /// # Panics\n    /// Might panic if the module is invalid\n    pub fn write(&mut self) -> Result<ReflectionInfo, Error> {\n        // We use `writeln!(self.out)` throughout the write to add newlines\n        // to make the output more readable\n\n        let es = self.options.version.is_es();\n\n        // Write the version (It must be the first thing or it isn't a valid glsl output)\n        writeln!(self.out, \"#version {}\", self.options.version)?;\n        // Write all the needed extensions\n        //\n        // This used to be the last thing being written as it allowed to search for features while\n        // writing the module saving some loops but some older versions (420 or less) required the\n        // extensions to appear before being used, even though extensions are part of the\n        // preprocessor not the processor ¯\\_(ツ)_/¯\n        self.features.write(self.options, &mut self.out)?;\n\n        // glsl es requires a precision to be specified for floats and ints\n        // TODO: Should this be user configurable?\n        if es {\n            writeln!(self.out)?;\n            writeln!(self.out, \"precision highp float;\")?;\n            writeln!(self.out, \"precision highp int;\")?;\n            writeln!(self.out)?;\n        }\n\n        if self.entry_point.stage == ShaderStage::Compute {\n            let workgroup_size = self.entry_point.workgroup_size;\n            writeln!(\n                self.out,\n                \"layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\",\n                workgroup_size[0], workgroup_size[1], workgroup_size[2]\n            )?;\n            writeln!(self.out)?;\n        }\n\n        if self.entry_point.stage == ShaderStage::Vertex\n            && !self\n                .options\n                .writer_flags\n                .contains(WriterFlags::DRAW_PARAMETERS)\n            && self.features.contains(Features::INSTANCE_INDEX)\n        {\n            writeln!(self.out, \"uniform uint {FIRST_INSTANCE_BINDING};\")?;\n            writeln!(self.out)?;\n        }\n\n        // Enable early depth tests if needed\n        if let Some(early_depth_test) = self.entry_point.early_depth_test {\n            // If early depth test is supported for this version of GLSL\n            if self.options.version.supports_early_depth_test() {\n                match early_depth_test {\n                    crate::EarlyDepthTest::Force => {\n                        writeln!(self.out, \"layout(early_fragment_tests) in;\")?;\n                    }\n                    crate::EarlyDepthTest::Allow { conservative, .. } => {\n                        use crate::ConservativeDepth as Cd;\n                        let depth = match conservative {\n                            Cd::GreaterEqual => \"greater\",\n                            Cd::LessEqual => \"less\",\n                            Cd::Unchanged => \"unchanged\",\n                        };\n                        writeln!(self.out, \"layout (depth_{depth}) out float gl_FragDepth;\")?;\n                    }\n                }\n            } else {\n                log::warn!(\n                    \"Early depth testing is not supported for this version of GLSL: {}\",\n                    self.options.version\n                );\n            }\n        }\n\n        if self.entry_point.stage == ShaderStage::Vertex && self.options.version.is_webgl() {\n            if let Some(multiview) = self.multiview.as_ref() {\n                writeln!(self.out, \"layout(num_views = {multiview}) in;\")?;\n                writeln!(self.out)?;\n            }\n        }\n\n        // Write struct types.\n        //\n        // This are always ordered because the IR is structured in a way that\n        // you can't make a struct without adding all of its members first.\n        for (handle, ty) in self.module.types.iter() {\n            if let TypeInner::Struct { ref members, .. } = ty.inner {\n                let struct_name = &self.names[&NameKey::Type(handle)];\n\n                // Structures ending with runtime-sized arrays can only be\n                // rendered as shader storage blocks in GLSL, not stand-alone\n                // struct types.\n                if !self.module.types[members.last().unwrap().ty]\n                    .inner\n                    .is_dynamically_sized(&self.module.types)\n                {\n                    write!(self.out, \"struct {struct_name} \")?;\n                    self.write_struct_body(handle, members)?;\n                    writeln!(self.out, \";\")?;\n                }\n            }\n        }\n\n        // Write functions for special types.\n        for (type_key, struct_ty) in self.module.special_types.predeclared_types.iter() {\n            match type_key {\n                &crate::PredeclaredType::ModfResult { size, scalar }\n                | &crate::PredeclaredType::FrexpResult { size, scalar } => {\n                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];\n                    let arg_type_name_owner;\n                    let arg_type_name = if let Some(size) = size {\n                        arg_type_name_owner = format!(\n                            \"{}vec{}\",\n                            if scalar.width == 8 { \"d\" } else { \"\" },\n                            size as u8\n                        );\n                        &arg_type_name_owner\n                    } else if scalar.width == 8 {\n                        \"double\"\n                    } else {\n                        \"float\"\n                    };\n\n                    let other_type_name_owner;\n                    let (defined_func_name, called_func_name, other_type_name) =\n                        if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {\n                            (MODF_FUNCTION, \"modf\", arg_type_name)\n                        } else {\n                            let other_type_name = if let Some(size) = size {\n                                other_type_name_owner = format!(\"ivec{}\", size as u8);\n                                &other_type_name_owner\n                            } else {\n                                \"int\"\n                            };\n                            (FREXP_FUNCTION, \"frexp\", other_type_name)\n                        };\n\n                    writeln!(self.out)?;\n                    if !self.options.version.supports_frexp_function()\n                        && matches!(type_key, &crate::PredeclaredType::FrexpResult { .. })\n                    {\n                        writeln!(\n                            self.out,\n                            \"{struct_name} {defined_func_name}({arg_type_name} arg) {{\n    {other_type_name} other = arg == {arg_type_name}(0) ? {other_type_name}(0) : {other_type_name}({arg_type_name}(1) + log2(arg));\n    {arg_type_name} fract = arg * exp2({arg_type_name}(-other));\n    return {struct_name}(fract, other);\n}}\",\n                        )?;\n                    } else {\n                        writeln!(\n                            self.out,\n                            \"{struct_name} {defined_func_name}({arg_type_name} arg) {{\n    {other_type_name} other;\n    {arg_type_name} fract = {called_func_name}(arg, other);\n    return {struct_name}(fract, other);\n}}\",\n                        )?;\n                    }\n                }\n                &crate::PredeclaredType::AtomicCompareExchangeWeakResult(_) => {\n                    // Handled by the general struct writing loop earlier.\n                }\n            }\n        }\n\n        // Write all named constants\n        let mut constants = self\n            .module\n            .constants\n            .iter()\n            .filter(|&(_, c)| c.name.is_some())\n            .peekable();\n        while let Some((handle, _)) = constants.next() {\n            self.write_global_constant(handle)?;\n            // Add extra newline for readability on last iteration\n            if constants.peek().is_none() {\n                writeln!(self.out)?;\n            }\n        }\n\n        let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);\n\n        // Write the globals\n        //\n        // Unless explicitly disabled with WriterFlags::INCLUDE_UNUSED_ITEMS,\n        // we filter all globals that aren't used by the selected entry point as they might be\n        // interfere with each other (i.e. two globals with the same location but different with\n        // different classes)\n        let include_unused = self\n            .options\n            .writer_flags\n            .contains(WriterFlags::INCLUDE_UNUSED_ITEMS);\n        for (handle, global) in self.module.global_variables.iter() {\n            let is_unused = ep_info[handle].is_empty();\n            if !include_unused && is_unused {\n                continue;\n            }\n\n            match self.module.types[global.ty].inner {\n                // We treat images separately because they might require\n                // writing the storage format\n                TypeInner::Image {\n                    mut dim,\n                    arrayed,\n                    class,\n                } => {\n                    // Gather the storage format if needed\n                    let storage_format_access = match self.module.types[global.ty].inner {\n                        TypeInner::Image {\n                            class: crate::ImageClass::Storage { format, access },\n                            ..\n                        } => Some((format, access)),\n                        _ => None,\n                    };\n\n                    if dim == crate::ImageDimension::D1 && es {\n                        dim = crate::ImageDimension::D2\n                    }\n\n                    // Gether the location if needed\n                    let layout_binding = if self.options.version.supports_explicit_locations() {\n                        let br = global.binding.as_ref().unwrap();\n                        self.options.binding_map.get(br).cloned()\n                    } else {\n                        None\n                    };\n\n                    // Write all the layout qualifiers\n                    if layout_binding.is_some() || storage_format_access.is_some() {\n                        write!(self.out, \"layout(\")?;\n                        if let Some(binding) = layout_binding {\n                            write!(self.out, \"binding = {binding}\")?;\n                        }\n                        if let Some((format, _)) = storage_format_access {\n                            let format_str = glsl_storage_format(format)?;\n                            let separator = match layout_binding {\n                                Some(_) => \",\",\n                                None => \"\",\n                            };\n                            write!(self.out, \"{separator}{format_str}\")?;\n                        }\n                        write!(self.out, \") \")?;\n                    }\n\n                    if let Some((_, access)) = storage_format_access {\n                        self.write_storage_access(access)?;\n                    }\n\n                    // All images in glsl are `uniform`\n                    // The trailing space is important\n                    write!(self.out, \"uniform \")?;\n\n                    // write the type\n                    //\n                    // This is way we need the leading space because `write_image_type` doesn't add\n                    // any spaces at the beginning or end\n                    self.write_image_type(dim, arrayed, class)?;\n\n                    // Finally write the name and end the global with a `;`\n                    // The leading space is important\n                    let global_name = self.get_global_name(handle, global);\n                    writeln!(self.out, \" {global_name};\")?;\n                    writeln!(self.out)?;\n\n                    self.reflection_names_globals.insert(handle, global_name);\n                }\n                // glsl has no concept of samplers so we just ignore it\n                TypeInner::Sampler { .. } => continue,\n                // All other globals are written by `write_global`\n                _ => {\n                    self.write_global(handle, global)?;\n                    // Add a newline (only for readability)\n                    writeln!(self.out)?;\n                }\n            }\n        }\n\n        for arg in self.entry_point.function.arguments.iter() {\n            self.write_varying(arg.binding.as_ref(), arg.ty, false)?;\n        }\n        if let Some(ref result) = self.entry_point.function.result {\n            self.write_varying(result.binding.as_ref(), result.ty, true)?;\n        }\n        writeln!(self.out)?;\n\n        // Write all regular functions\n        for (handle, function) in self.module.functions.iter() {\n            // Check that the function doesn't use globals that aren't supported\n            // by the current entry point\n            if !include_unused && !ep_info.dominates_global_use(&self.info[handle]) {\n                continue;\n            }\n\n            let fun_info = &self.info[handle];\n\n            // Skip functions that that are not compatible with this entry point's stage.\n            //\n            // When validation is enabled, it rejects modules whose entry points try to call\n            // incompatible functions, so if we got this far, then any functions incompatible\n            // with our selected entry point must not be used.\n            //\n            // When validation is disabled, `fun_info.available_stages` is always just\n            // `ShaderStages::all()`, so this will write all functions in the module, and\n            // the downstream GLSL compiler will catch any problems.\n            if !fun_info.available_stages.contains(ep_info.available_stages) {\n                continue;\n            }\n\n            // Write the function\n            self.write_function(back::FunctionType::Function(handle), function, fun_info)?;\n\n            writeln!(self.out)?;\n        }\n\n        self.write_function(\n            back::FunctionType::EntryPoint(self.entry_point_idx),\n            &self.entry_point.function,\n            ep_info,\n        )?;\n\n        // Add newline at the end of file\n        writeln!(self.out)?;\n\n        // Collect all reflection info and return it to the user\n        self.collect_reflection_info()\n    }\n\n    fn write_array_size(\n        &mut self,\n        base: Handle<crate::Type>,\n        size: crate::ArraySize,\n    ) -> BackendResult {\n        write!(self.out, \"[\")?;\n\n        // Write the array size\n        // Writes nothing if `IndexableLength::Dynamic`\n        match size.resolve(self.module.to_ctx())? {\n            proc::IndexableLength::Known(size) => {\n                write!(self.out, \"{size}\")?;\n            }\n            proc::IndexableLength::Dynamic => (),\n        }\n\n        write!(self.out, \"]\")?;\n\n        if let TypeInner::Array {\n            base: next_base,\n            size: next_size,\n            ..\n        } = self.module.types[base].inner\n        {\n            self.write_array_size(next_base, next_size)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write value types\n    ///\n    /// # Notes\n    /// Adds no trailing or leading whitespace\n    fn write_value_type(&mut self, inner: &TypeInner) -> BackendResult {\n        match *inner {\n            // Scalars are simple we just get the full name from `glsl_scalar`\n            TypeInner::Scalar(scalar)\n            | TypeInner::Atomic(scalar)\n            | TypeInner::ValuePointer {\n                size: None,\n                scalar,\n                space: _,\n            } => write!(self.out, \"{}\", glsl_scalar(scalar)?.full)?,\n            // Vectors are just `gvecN` where `g` is the scalar prefix and `N` is the vector size\n            TypeInner::Vector { size, scalar }\n            | TypeInner::ValuePointer {\n                size: Some(size),\n                scalar,\n                space: _,\n            } => write!(self.out, \"{}vec{}\", glsl_scalar(scalar)?.prefix, size as u8)?,\n            // Matrices are written with `gmatMxN` where `g` is the scalar prefix (only floats and\n            // doubles are allowed), `M` is the columns count and `N` is the rows count\n            //\n            // glsl supports a matrix shorthand `gmatN` where `N` = `M` but it doesn't justify the\n            // extra branch to write matrices this way\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => write!(\n                self.out,\n                \"{}mat{}x{}\",\n                glsl_scalar(scalar)?.prefix,\n                columns as u8,\n                rows as u8\n            )?,\n            // GLSL arrays are written as `type name[size]`\n            // Here we only write the size of the array i.e. `[size]`\n            // Base `type` and `name` should be written outside\n            TypeInner::Array { base, size, .. } => self.write_array_size(base, size)?,\n            // Write all variants instead of `_` so that if new variants are added a\n            // no exhaustiveness error is thrown\n            TypeInner::Pointer { .. }\n            | TypeInner::Struct { .. }\n            | TypeInner::Image { .. }\n            | TypeInner::Sampler { .. }\n            | TypeInner::AccelerationStructure { .. }\n            | TypeInner::RayQuery { .. }\n            | TypeInner::BindingArray { .. }\n            | TypeInner::CooperativeMatrix { .. } => {\n                return Err(Error::Custom(format!(\"Unable to write type {inner:?}\")))\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write non image/sampler types\n    ///\n    /// # Notes\n    /// Adds no trailing or leading whitespace\n    fn write_type(&mut self, ty: Handle<crate::Type>) -> BackendResult {\n        match self.module.types[ty].inner {\n            // glsl has no pointer types so just write types as normal and loads are skipped\n            TypeInner::Pointer { base, .. } => self.write_type(base),\n            // glsl structs are written as just the struct name\n            TypeInner::Struct { .. } => {\n                // Get the struct name\n                let name = &self.names[&NameKey::Type(ty)];\n                write!(self.out, \"{name}\")?;\n                Ok(())\n            }\n            // glsl array has the size separated from the base type\n            TypeInner::Array { base, .. } => self.write_type(base),\n            ref other => self.write_value_type(other),\n        }\n    }\n\n    /// Helper method to write a image type\n    ///\n    /// # Notes\n    /// Adds no leading or trailing whitespace\n    fn write_image_type(\n        &mut self,\n        dim: crate::ImageDimension,\n        arrayed: bool,\n        class: crate::ImageClass,\n    ) -> BackendResult {\n        // glsl images consist of four parts the scalar prefix, the image \"type\", the dimensions\n        // and modifiers\n        //\n        // There exists two image types\n        // - sampler - for sampled images\n        // - image - for storage images\n        //\n        // There are three possible modifiers that can be used together and must be written in\n        // this order to be valid\n        // - MS - used if it's a multisampled image\n        // - Array - used if it's an image array\n        // - Shadow - used if it's a depth image\n        use crate::ImageClass as Ic;\n        use crate::Scalar as S;\n        let float = S {\n            kind: crate::ScalarKind::Float,\n            width: 4,\n        };\n        let (base, scalar, ms, comparison) = match class {\n            Ic::Sampled { kind, multi: true } => (\"sampler\", S { kind, width: 4 }, \"MS\", \"\"),\n            Ic::Sampled { kind, multi: false } => (\"sampler\", S { kind, width: 4 }, \"\", \"\"),\n            Ic::Depth { multi: true } => (\"sampler\", float, \"MS\", \"\"),\n            Ic::Depth { multi: false } => (\"sampler\", float, \"\", \"Shadow\"),\n            Ic::Storage { format, .. } => (\"image\", format.into(), \"\", \"\"),\n            Ic::External => unimplemented!(),\n        };\n\n        let precision = if self.options.version.is_es() {\n            \"highp \"\n        } else {\n            \"\"\n        };\n\n        write!(\n            self.out,\n            \"{}{}{}{}{}{}{}\",\n            precision,\n            glsl_scalar(scalar)?.prefix,\n            base,\n            glsl_dimension(dim),\n            ms,\n            if arrayed { \"Array\" } else { \"\" },\n            comparison\n        )?;\n\n        Ok(())\n    }\n\n    /// Helper method used by [Self::write_global] to write just the layout part of\n    /// a non image/sampler global variable, if applicable.\n    ///\n    /// # Notes\n    ///\n    /// Adds trailing whitespace if any layout qualifier is written\n    fn write_global_layout(&mut self, global: &crate::GlobalVariable) -> BackendResult {\n        // Determine which (if any) explicit memory layout to use, and whether we support it\n        let layout = match global.space {\n            crate::AddressSpace::Uniform => {\n                if !self.options.version.supports_std140_layout() {\n                    return Err(Error::Custom(\n                        \"Uniform address space requires std140 layout support\".to_string(),\n                    ));\n                }\n\n                Some(\"std140\")\n            }\n            crate::AddressSpace::Storage { .. } => {\n                if !self.options.version.supports_std430_layout() {\n                    return Err(Error::Custom(\n                        \"Storage address space requires std430 layout support\".to_string(),\n                    ));\n                }\n\n                Some(\"std430\")\n            }\n            _ => None,\n        };\n\n        // If our version supports explicit layouts, we can also output the explicit binding\n        // if we have it\n        if self.options.version.supports_explicit_locations() {\n            if let Some(ref br) = global.binding {\n                match self.options.binding_map.get(br) {\n                    Some(binding) => {\n                        write!(self.out, \"layout(\")?;\n\n                        if let Some(layout) = layout {\n                            write!(self.out, \"{layout}, \")?;\n                        }\n\n                        write!(self.out, \"binding = {binding}) \")?;\n\n                        return Ok(());\n                    }\n                    None => {\n                        log::debug!(\"unassigned binding for {:?}\", global.name);\n                    }\n                }\n            }\n        }\n\n        // Either no explicit bindings are supported or we didn't have any.\n        // Write just the memory layout.\n        if let Some(layout) = layout {\n            write!(self.out, \"layout({layout}) \")?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write non images/sampler globals\n    ///\n    /// # Notes\n    /// Adds a newline\n    ///\n    /// # Panics\n    /// If the global has type sampler\n    fn write_global(\n        &mut self,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        self.write_global_layout(global)?;\n\n        if let crate::AddressSpace::Storage { access } = global.space {\n            self.write_storage_access(access)?;\n            if global\n                .memory_decorations\n                .contains(crate::MemoryDecorations::COHERENT)\n            {\n                write!(self.out, \"coherent \")?;\n            }\n            if global\n                .memory_decorations\n                .contains(crate::MemoryDecorations::VOLATILE)\n            {\n                write!(self.out, \"volatile \")?;\n            }\n        }\n\n        if let Some(storage_qualifier) = glsl_storage_qualifier(global.space) {\n            write!(self.out, \"{storage_qualifier} \")?;\n        }\n\n        match global.space {\n            crate::AddressSpace::Private => {\n                self.write_simple_global(handle, global)?;\n            }\n            crate::AddressSpace::WorkGroup => {\n                self.write_simple_global(handle, global)?;\n            }\n            crate::AddressSpace::Immediate => {\n                self.write_simple_global(handle, global)?;\n            }\n            crate::AddressSpace::Uniform => {\n                self.write_interface_block(handle, global)?;\n            }\n            crate::AddressSpace::Storage { .. } => {\n                self.write_interface_block(handle, global)?;\n            }\n            crate::AddressSpace::TaskPayload => {\n                self.write_interface_block(handle, global)?;\n            }\n            // A global variable in the `Function` address space is a\n            // contradiction in terms.\n            crate::AddressSpace::Function => unreachable!(),\n            // Textures and samplers are handled directly in `Writer::write`.\n            crate::AddressSpace::Handle => unreachable!(),\n            // ray tracing pipelines unsupported\n            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {\n                unreachable!()\n            }\n        }\n\n        Ok(())\n    }\n\n    fn write_simple_global(\n        &mut self,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        self.write_type(global.ty)?;\n        write!(self.out, \" \")?;\n        self.write_global_name(handle, global)?;\n\n        if let TypeInner::Array { base, size, .. } = self.module.types[global.ty].inner {\n            self.write_array_size(base, size)?;\n        }\n\n        if global.space.initializable() && is_value_init_supported(self.module, global.ty) {\n            write!(self.out, \" = \")?;\n            if let Some(init) = global.init {\n                self.write_const_expr(init, &self.module.global_expressions)?;\n            } else {\n                self.write_zero_init_value(global.ty)?;\n            }\n        }\n\n        writeln!(self.out, \";\")?;\n\n        if let crate::AddressSpace::Immediate = global.space {\n            let global_name = self.get_global_name(handle, global);\n            self.reflection_names_globals.insert(handle, global_name);\n        }\n\n        Ok(())\n    }\n\n    /// Write an interface block for a single Naga global.\n    ///\n    /// Write `block_name { members }`. Since `block_name` must be unique\n    /// between blocks and structs, we add `_block_ID` where `ID` is a\n    /// `IdGenerator` generated number. Write `members` in the same way we write\n    /// a struct's members.\n    fn write_interface_block(\n        &mut self,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        // Write the block name, it's just the struct name appended with `_block_ID`\n        let ty_name = &self.names[&NameKey::Type(global.ty)];\n        let block_name = format!(\n            \"{}_block_{}{:?}\",\n            // avoid double underscores as they are reserved in GLSL\n            ty_name.trim_end_matches('_'),\n            self.block_id.generate(),\n            self.entry_point.stage,\n        );\n        write!(self.out, \"{block_name} \")?;\n        self.reflection_names_globals.insert(handle, block_name);\n\n        match self.module.types[global.ty].inner {\n            TypeInner::Struct { ref members, .. }\n                if self.module.types[members.last().unwrap().ty]\n                    .inner\n                    .is_dynamically_sized(&self.module.types) =>\n            {\n                // Structs with dynamically sized arrays must have their\n                // members lifted up as members of the interface block. GLSL\n                // can't write such struct types anyway.\n                self.write_struct_body(global.ty, members)?;\n                write!(self.out, \" \")?;\n                self.write_global_name(handle, global)?;\n            }\n            _ => {\n                // A global of any other type is written as the sole member\n                // of the interface block. Since the interface block is\n                // anonymous, this becomes visible in the global scope.\n                write!(self.out, \"{{ \")?;\n                self.write_type(global.ty)?;\n                write!(self.out, \" \")?;\n                self.write_global_name(handle, global)?;\n                if let TypeInner::Array { base, size, .. } = self.module.types[global.ty].inner {\n                    self.write_array_size(base, size)?;\n                }\n                write!(self.out, \"; }}\")?;\n            }\n        }\n\n        writeln!(self.out, \";\")?;\n\n        Ok(())\n    }\n\n    /// Helper method used to find which expressions of a given function require baking\n    ///\n    /// # Notes\n    /// Clears `need_bake_expressions` set before adding to it\n    fn update_expressions_to_bake(&mut self, func: &crate::Function, info: &valid::FunctionInfo) {\n        use crate::Expression;\n        self.need_bake_expressions.clear();\n        for (fun_handle, expr) in func.expressions.iter() {\n            let expr_info = &info[fun_handle];\n            let min_ref_count = func.expressions[fun_handle].bake_ref_count();\n            if min_ref_count <= expr_info.ref_count {\n                self.need_bake_expressions.insert(fun_handle);\n            }\n\n            let inner = expr_info.ty.inner_with(&self.module.types);\n\n            if let Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                ..\n            } = *expr\n            {\n                match fun {\n                    crate::MathFunction::Dot => {\n                        // if the expression is a Dot product with integer arguments,\n                        // then the args needs baking as well\n                        if let TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                            ..\n                        }) = *inner\n                        {\n                            self.need_bake_expressions.insert(arg);\n                            self.need_bake_expressions.insert(arg1.unwrap());\n                        }\n                    }\n                    crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {\n                        self.need_bake_expressions.insert(arg);\n                        self.need_bake_expressions.insert(arg1.unwrap());\n                    }\n                    crate::MathFunction::Pack4xI8\n                    | crate::MathFunction::Pack4xU8\n                    | crate::MathFunction::Pack4xI8Clamp\n                    | crate::MathFunction::Pack4xU8Clamp\n                    | crate::MathFunction::Unpack4xI8\n                    | crate::MathFunction::Unpack4xU8\n                    | crate::MathFunction::QuantizeToF16 => {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    /* crate::MathFunction::Pack4x8unorm | */\n                    crate::MathFunction::Unpack4x8snorm\n                        if !self.options.version.supports_pack_unpack_4x8() =>\n                    {\n                        // We have a fallback if the platform doesn't natively support these\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    /* crate::MathFunction::Pack4x8unorm | */\n                    crate::MathFunction::Unpack4x8unorm\n                        if !self.options.version.supports_pack_unpack_4x8() =>\n                    {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    /* crate::MathFunction::Pack2x16snorm |  */\n                    crate::MathFunction::Unpack2x16snorm\n                        if !self.options.version.supports_pack_unpack_snorm_2x16() =>\n                    {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    /* crate::MathFunction::Pack2x16unorm | */\n                    crate::MathFunction::Unpack2x16unorm\n                        if !self.options.version.supports_pack_unpack_unorm_2x16() =>\n                    {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    crate::MathFunction::ExtractBits => {\n                        // Only argument 1 is re-used.\n                        self.need_bake_expressions.insert(arg1.unwrap());\n                    }\n                    crate::MathFunction::InsertBits => {\n                        // Only argument 2 is re-used.\n                        self.need_bake_expressions.insert(arg2.unwrap());\n                    }\n                    crate::MathFunction::CountLeadingZeros => {\n                        if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() {\n                            self.need_bake_expressions.insert(arg);\n                        }\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        for statement in func.body.iter() {\n            match *statement {\n                crate::Statement::Atomic {\n                    fun: crate::AtomicFunction::Exchange { compare: Some(cmp) },\n                    ..\n                } => {\n                    self.need_bake_expressions.insert(cmp);\n                }\n                _ => {}\n            }\n        }\n    }\n\n    /// Helper method used to get a name for a global\n    ///\n    /// Globals have different naming schemes depending on their binding:\n    /// - Globals without bindings use the name from the [`Namer`](crate::proc::Namer)\n    /// - Globals with resource binding are named `_group_X_binding_Y` where `X`\n    ///   is the group and `Y` is the binding\n    fn get_global_name(\n        &self,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> String {\n        match (&global.binding, global.space) {\n            (&Some(ref br), _) => {\n                format!(\n                    \"_group_{}_binding_{}_{}\",\n                    br.group,\n                    br.binding,\n                    self.entry_point.stage.to_str()\n                )\n            }\n            (&None, crate::AddressSpace::Immediate) => {\n                format!(\"_immediates_binding_{}\", self.entry_point.stage.to_str())\n            }\n            (&None, _) => self.names[&NameKey::GlobalVariable(handle)].clone(),\n        }\n    }\n\n    /// Helper method used to write a name for a global without additional heap allocation\n    fn write_global_name(\n        &mut self,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        match (&global.binding, global.space) {\n            (&Some(ref br), _) => write!(\n                self.out,\n                \"_group_{}_binding_{}_{}\",\n                br.group,\n                br.binding,\n                self.entry_point.stage.to_str()\n            )?,\n            (&None, crate::AddressSpace::Immediate) => write!(\n                self.out,\n                \"_immediates_binding_{}\",\n                self.entry_point.stage.to_str()\n            )?,\n            (&None, _) => write!(\n                self.out,\n                \"{}\",\n                &self.names[&NameKey::GlobalVariable(handle)]\n            )?,\n        }\n\n        Ok(())\n    }\n\n    /// Write a GLSL global that will carry a Naga entry point's argument or return value.\n    ///\n    /// A Naga entry point's arguments and return value are rendered in GLSL as\n    /// variables at global scope with the `in` and `out` storage qualifiers.\n    /// The code we generate for `main` loads from all the `in` globals into\n    /// appropriately named locals. Before it returns, `main` assigns the\n    /// components of its return value into all the `out` globals.\n    ///\n    /// This function writes a declaration for one such GLSL global,\n    /// representing a value passed into or returned from [`self.entry_point`]\n    /// that has a [`Location`] binding. The global's name is generated based on\n    /// the location index and the shader stages being connected; see\n    /// [`VaryingName`]. This means we don't need to know the names of\n    /// arguments, just their types and bindings.\n    ///\n    /// Emit nothing for entry point arguments or return values with [`BuiltIn`]\n    /// bindings; `main` will read from or assign to the appropriate GLSL\n    /// special variable; these are pre-declared. As an exception, we do declare\n    /// `gl_Position` or `gl_FragCoord` with the `invariant` qualifier if\n    /// needed.\n    ///\n    /// Use `output` together with [`self.entry_point.stage`] to determine which\n    /// shader stages are being connected, and choose the `in` or `out` storage\n    /// qualifier.\n    ///\n    /// [`self.entry_point`]: Writer::entry_point\n    /// [`self.entry_point.stage`]: crate::EntryPoint::stage\n    /// [`Location`]: crate::Binding::Location\n    /// [`BuiltIn`]: crate::Binding::BuiltIn\n    fn write_varying(\n        &mut self,\n        binding: Option<&crate::Binding>,\n        ty: Handle<crate::Type>,\n        output: bool,\n    ) -> Result<(), Error> {\n        // For a struct, emit a separate global for each member with a binding.\n        if let TypeInner::Struct { ref members, .. } = self.module.types[ty].inner {\n            for member in members {\n                self.write_varying(member.binding.as_ref(), member.ty, output)?;\n            }\n            return Ok(());\n        }\n\n        let binding = match binding {\n            None => return Ok(()),\n            Some(binding) => binding,\n        };\n\n        let (location, interpolation, sampling, blend_src) = match *binding {\n            crate::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src,\n                per_primitive: _,\n            } => (location, interpolation, sampling, blend_src),\n            crate::Binding::BuiltIn(built_in) => {\n                match built_in {\n                    crate::BuiltIn::Position { invariant: true } => {\n                        match (self.options.version, self.entry_point.stage) {\n                            (\n                                Version::Embedded {\n                                    version: 300,\n                                    is_webgl: true,\n                                },\n                                ShaderStage::Fragment,\n                            ) => {\n                                // `invariant gl_FragCoord` is not allowed in WebGL2 and possibly\n                                // OpenGL ES in general (waiting on confirmation).\n                                //\n                                // See https://github.com/KhronosGroup/WebGL/issues/3518\n                            }\n                            _ => {\n                                writeln!(\n                                    self.out,\n                                    \"invariant {};\",\n                                    glsl_built_in(\n                                        built_in,\n                                        VaryingOptions::from_writer_options(self.options, output)\n                                    )\n                                )?;\n                            }\n                        }\n                    }\n                    crate::BuiltIn::ClipDistances => {\n                        // Re-declare `gl_ClipDistance` with number of clip planes.\n                        let TypeInner::Array { size, .. } = self.module.types[ty].inner else {\n                            unreachable!();\n                        };\n                        let proc::IndexableLength::Known(size) =\n                            size.resolve(self.module.to_ctx())?\n                        else {\n                            unreachable!();\n                        };\n                        self.clip_distance_count = size;\n                        writeln!(self.out, \"out float gl_ClipDistance[{size}];\")?;\n                    }\n                    _ => {}\n                }\n                return Ok(());\n            }\n        };\n\n        // Write the interpolation modifier if needed\n        //\n        // We ignore all interpolation and auxiliary modifiers that aren't used in fragment\n        // shaders' input globals or vertex shaders' output globals.\n        let emit_interpolation_and_auxiliary = match self.entry_point.stage {\n            ShaderStage::Vertex => output,\n            ShaderStage::Fragment => !output,\n            ShaderStage::Compute => false,\n            ShaderStage::Task\n            | ShaderStage::Mesh\n            | ShaderStage::RayGeneration\n            | ShaderStage::AnyHit\n            | ShaderStage::ClosestHit\n            | ShaderStage::Miss => unreachable!(),\n        };\n\n        // Write the I/O locations, if allowed\n        let io_location = if self.options.version.supports_explicit_locations()\n            || !emit_interpolation_and_auxiliary\n        {\n            if self.options.version.supports_io_locations() {\n                if let Some(blend_src) = blend_src {\n                    write!(\n                        self.out,\n                        \"layout(location = {location}, index = {blend_src}) \"\n                    )?;\n                } else {\n                    write!(self.out, \"layout(location = {location}) \")?;\n                }\n                None\n            } else {\n                Some(VaryingLocation {\n                    location,\n                    index: blend_src.unwrap_or(0),\n                })\n            }\n        } else {\n            None\n        };\n\n        // Write the interpolation qualifier.\n        if let Some(interp) = interpolation {\n            if emit_interpolation_and_auxiliary {\n                write!(self.out, \"{} \", glsl_interpolation(interp))?;\n            }\n        }\n\n        // Write the sampling auxiliary qualifier.\n        //\n        // Before GLSL 4.2, the `centroid` and `sample` qualifiers were required to appear\n        // immediately before the `in` / `out` qualifier, so we'll just follow that rule\n        // here, regardless of the version.\n        if let Some(sampling) = sampling {\n            if emit_interpolation_and_auxiliary {\n                if let Some(qualifier) = glsl_sampling(sampling)? {\n                    write!(self.out, \"{qualifier} \")?;\n                }\n            }\n        }\n\n        // Write the input/output qualifier.\n        write!(self.out, \"{} \", if output { \"out\" } else { \"in\" })?;\n\n        // Write the type\n        // `write_type` adds no leading or trailing spaces\n        self.write_type(ty)?;\n\n        // Finally write the global name and end the global with a `;` and a newline\n        // Leading space is important\n        let vname = VaryingName {\n            binding: &crate::Binding::Location {\n                location,\n                interpolation: None,\n                sampling: None,\n                blend_src,\n                per_primitive: false,\n            },\n            stage: self.entry_point.stage,\n            options: VaryingOptions::from_writer_options(self.options, output),\n        };\n        writeln!(self.out, \" {vname};\")?;\n\n        if let Some(location) = io_location {\n            self.varying.insert(vname.to_string(), location);\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write functions (both entry points and regular functions)\n    ///\n    /// # Notes\n    /// Adds a newline\n    fn write_function(\n        &mut self,\n        ty: back::FunctionType,\n        func: &crate::Function,\n        info: &valid::FunctionInfo,\n    ) -> BackendResult {\n        // Create a function context for the function being written\n        let ctx = back::FunctionCtx {\n            ty,\n            info,\n            expressions: &func.expressions,\n            named_expressions: &func.named_expressions,\n        };\n\n        self.named_expressions.clear();\n        self.update_expressions_to_bake(func, info);\n\n        // Write the function header\n        //\n        // glsl headers are the same as in c:\n        // `ret_type name(args)`\n        // `ret_type` is the return type\n        // `name` is the function name\n        // `args` is a comma separated list of `type name`\n        //  | - `type` is the argument type\n        //  | - `name` is the argument name\n\n        // Start by writing the return type if any otherwise write void\n        // This is the only place where `void` is a valid type\n        // (though it's more a keyword than a type)\n        if let back::FunctionType::EntryPoint(_) = ctx.ty {\n            write!(self.out, \"void\")?;\n        } else if let Some(ref result) = func.result {\n            self.write_type(result.ty)?;\n            if let TypeInner::Array { base, size, .. } = self.module.types[result.ty].inner {\n                self.write_array_size(base, size)?\n            }\n        } else {\n            write!(self.out, \"void\")?;\n        }\n\n        // Write the function name and open parentheses for the argument list\n        let function_name = match ctx.ty {\n            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],\n            back::FunctionType::EntryPoint(_) => \"main\",\n        };\n        write!(self.out, \" {function_name}(\")?;\n\n        // Write the comma separated argument list\n        //\n        // We need access to `Self` here so we use the reference passed to the closure as an\n        // argument instead of capturing as that would cause a borrow checker error\n        let arguments = match ctx.ty {\n            back::FunctionType::EntryPoint(_) => &[][..],\n            back::FunctionType::Function(_) => &func.arguments,\n        };\n        let arguments: Vec<_> = arguments\n            .iter()\n            .enumerate()\n            .filter(|&(_, arg)| match self.module.types[arg.ty].inner {\n                TypeInner::Sampler { .. } => false,\n                _ => true,\n            })\n            .collect();\n        self.write_slice(&arguments, |this, _, &(i, arg)| {\n            // Write the argument type\n            match this.module.types[arg.ty].inner {\n                // We treat images separately because they might require\n                // writing the storage format\n                TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                } => {\n                    // Write the storage format if needed\n                    if let TypeInner::Image {\n                        class: crate::ImageClass::Storage { format, .. },\n                        ..\n                    } = this.module.types[arg.ty].inner\n                    {\n                        write!(this.out, \"layout({}) \", glsl_storage_format(format)?)?;\n                    }\n\n                    // write the type\n                    //\n                    // This is way we need the leading space because `write_image_type` doesn't add\n                    // any spaces at the beginning or end\n                    this.write_image_type(dim, arrayed, class)?;\n                }\n                TypeInner::Pointer { base, .. } => {\n                    // write parameter qualifiers\n                    write!(this.out, \"inout \")?;\n                    this.write_type(base)?;\n                }\n                // All other types are written by `write_type`\n                _ => {\n                    this.write_type(arg.ty)?;\n                }\n            }\n\n            // Write the argument name\n            // The leading space is important\n            write!(this.out, \" {}\", &this.names[&ctx.argument_key(i as u32)])?;\n\n            // Write array size\n            match this.module.types[arg.ty].inner {\n                TypeInner::Array { base, size, .. } => {\n                    this.write_array_size(base, size)?;\n                }\n                TypeInner::Pointer { base, .. } => {\n                    if let TypeInner::Array { base, size, .. } = this.module.types[base].inner {\n                        this.write_array_size(base, size)?;\n                    }\n                }\n                _ => {}\n            }\n\n            Ok(())\n        })?;\n\n        // Close the parentheses and open braces to start the function body\n        writeln!(self.out, \") {{\")?;\n\n        if self.options.zero_initialize_workgroup_memory\n            && ctx.ty.is_compute_like_entry_point(self.module)\n        {\n            self.write_workgroup_variables_initialization(&ctx)?;\n        }\n\n        // Compose the function arguments from globals, in case of an entry point.\n        if let back::FunctionType::EntryPoint(ep_index) = ctx.ty {\n            let stage = self.module.entry_points[ep_index as usize].stage;\n            for (index, arg) in func.arguments.iter().enumerate() {\n                write!(self.out, \"{}\", back::INDENT)?;\n                self.write_type(arg.ty)?;\n                let name = &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];\n                write!(self.out, \" {name}\")?;\n                write!(self.out, \" = \")?;\n                match self.module.types[arg.ty].inner {\n                    TypeInner::Struct { ref members, .. } => {\n                        self.write_type(arg.ty)?;\n                        write!(self.out, \"(\")?;\n                        for (index, member) in members.iter().enumerate() {\n                            let varying_name = VaryingName {\n                                binding: member.binding.as_ref().unwrap(),\n                                stage,\n                                options: VaryingOptions::from_writer_options(self.options, false),\n                            };\n                            if index != 0 {\n                                write!(self.out, \", \")?;\n                            }\n                            write!(self.out, \"{varying_name}\")?;\n                        }\n                        writeln!(self.out, \");\")?;\n                    }\n                    _ => {\n                        let varying_name = VaryingName {\n                            binding: arg.binding.as_ref().unwrap(),\n                            stage,\n                            options: VaryingOptions::from_writer_options(self.options, false),\n                        };\n                        writeln!(self.out, \"{varying_name};\")?;\n                    }\n                }\n            }\n        }\n\n        // Write all function locals\n        // Locals are `type name (= init)?;` where the init part (including the =) are optional\n        //\n        // Always adds a newline\n        for (handle, local) in func.local_variables.iter() {\n            // Write indentation (only for readability) and the type\n            // `write_type` adds no trailing space\n            write!(self.out, \"{}\", back::INDENT)?;\n            self.write_type(local.ty)?;\n\n            // Write the local name\n            // The leading space is important\n            write!(self.out, \" {}\", self.names[&ctx.name_key(handle)])?;\n            // Write size for array type\n            if let TypeInner::Array { base, size, .. } = self.module.types[local.ty].inner {\n                self.write_array_size(base, size)?;\n            }\n            // Write the local initializer if needed\n            if let Some(init) = local.init {\n                // Put the equal signal only if there's a initializer\n                // The leading and trailing spaces aren't needed but help with readability\n                write!(self.out, \" = \")?;\n\n                // Write the constant\n                // `write_constant` adds no trailing or leading space/newline\n                self.write_expr(init, &ctx)?;\n            } else if is_value_init_supported(self.module, local.ty) {\n                write!(self.out, \" = \")?;\n                self.write_zero_init_value(local.ty)?;\n            }\n\n            // Finish the local with `;` and add a newline (only for readability)\n            writeln!(self.out, \";\")?\n        }\n\n        // Write the function body (statement list)\n        for sta in func.body.iter() {\n            // Write a statement, the indentation should always be 1 when writing the function body\n            // `write_stmt` adds a newline\n            self.write_stmt(sta, &ctx, back::Level(1))?;\n        }\n\n        // Close braces and add a newline\n        writeln!(self.out, \"}}\")?;\n\n        Ok(())\n    }\n\n    fn write_workgroup_variables_initialization(\n        &mut self,\n        ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        let mut vars = self\n            .module\n            .global_variables\n            .iter()\n            .filter(|&(handle, var)| {\n                !ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup\n            })\n            .peekable();\n\n        if vars.peek().is_some() {\n            let level = back::Level(1);\n\n            writeln!(self.out, \"{level}if (gl_LocalInvocationID == uvec3(0u)) {{\")?;\n\n            for (handle, var) in vars {\n                let name = &self.names[&NameKey::GlobalVariable(handle)];\n                write!(self.out, \"{}{} = \", level.next(), name)?;\n                self.write_zero_init_value(var.ty)?;\n                writeln!(self.out, \";\")?;\n            }\n\n            writeln!(self.out, \"{level}}}\")?;\n            self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;\n        }\n\n        Ok(())\n    }\n\n    /// Write a list of comma separated `T` values using a writer function `F`.\n    ///\n    /// The writer function `F` receives a mutable reference to `self` that if needed won't cause\n    /// borrow checker issues (using for example a closure with `self` will cause issues), the\n    /// second argument is the 0 based index of the element on the list, and the last element is\n    /// a reference to the element `T` being written\n    ///\n    /// # Notes\n    /// - Adds no newlines or leading/trailing whitespace\n    /// - The last element won't have a trailing `,`\n    fn write_slice<T, F: FnMut(&mut Self, u32, &T) -> BackendResult>(\n        &mut self,\n        data: &[T],\n        mut f: F,\n    ) -> BackendResult {\n        // Loop through `data` invoking `f` for each element\n        for (index, item) in data.iter().enumerate() {\n            if index != 0 {\n                write!(self.out, \", \")?;\n            }\n            f(self, index as u32, item)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write global constants\n    fn write_global_constant(&mut self, handle: Handle<crate::Constant>) -> BackendResult {\n        write!(self.out, \"const \")?;\n        let constant = &self.module.constants[handle];\n        self.write_type(constant.ty)?;\n        let name = &self.names[&NameKey::Constant(handle)];\n        write!(self.out, \" {name}\")?;\n        if let TypeInner::Array { base, size, .. } = self.module.types[constant.ty].inner {\n            self.write_array_size(base, size)?;\n        }\n        write!(self.out, \" = \")?;\n        self.write_const_expr(constant.init, &self.module.global_expressions)?;\n        writeln!(self.out, \";\")?;\n        Ok(())\n    }\n\n    /// Helper method used to output a dot product as an arithmetic expression\n    ///\n    fn write_dot_product(\n        &mut self,\n        arg: Handle<crate::Expression>,\n        arg1: Handle<crate::Expression>,\n        size: usize,\n        ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        // Write parentheses around the dot product expression to prevent operators\n        // with different precedences from applying earlier.\n        write!(self.out, \"(\")?;\n\n        // Cycle through all the components of the vector\n        for index in 0..size {\n            let component = back::COMPONENTS[index];\n            // Write the addition to the previous product\n            // This will print an extra '+' at the beginning but that is fine in glsl\n            write!(self.out, \" + \")?;\n            // Write the first vector expression, this expression is marked to be\n            // cached so unless it can't be cached (for example, it's a Constant)\n            // it shouldn't produce large expressions.\n            self.write_expr(arg, ctx)?;\n            // Access the current component on the first vector\n            write!(self.out, \".{component} * \")?;\n            // Write the second vector expression, this expression is marked to be\n            // cached so unless it can't be cached (for example, it's a Constant)\n            // it shouldn't produce large expressions.\n            self.write_expr(arg1, ctx)?;\n            // Access the current component on the second vector\n            write!(self.out, \".{component}\")?;\n        }\n\n        write!(self.out, \")\")?;\n        Ok(())\n    }\n\n    /// Helper method used to write structs\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_struct_body(\n        &mut self,\n        handle: Handle<crate::Type>,\n        members: &[crate::StructMember],\n    ) -> BackendResult {\n        // glsl structs are written as in C\n        // `struct name() { members };`\n        //  | `struct` is a keyword\n        //  | `name` is the struct name\n        //  | `members` is a semicolon separated list of `type name`\n        //      | `type` is the member type\n        //      | `name` is the member name\n        writeln!(self.out, \"{{\")?;\n\n        for (idx, member) in members.iter().enumerate() {\n            // The indentation is only for readability\n            write!(self.out, \"{}\", back::INDENT)?;\n\n            match self.module.types[member.ty].inner {\n                TypeInner::Array {\n                    base,\n                    size,\n                    stride: _,\n                } => {\n                    self.write_type(base)?;\n                    write!(\n                        self.out,\n                        \" {}\",\n                        &self.names[&NameKey::StructMember(handle, idx as u32)]\n                    )?;\n                    // Write [size]\n                    self.write_array_size(base, size)?;\n                    // Newline is important\n                    writeln!(self.out, \";\")?;\n                }\n                _ => {\n                    // Write the member type\n                    // Adds no trailing space\n                    self.write_type(member.ty)?;\n\n                    // Write the member name and put a semicolon\n                    // The leading space is important\n                    // All members must have a semicolon even the last one\n                    writeln!(\n                        self.out,\n                        \" {};\",\n                        &self.names[&NameKey::StructMember(handle, idx as u32)]\n                    )?;\n                }\n            }\n        }\n\n        write!(self.out, \"}}\")?;\n        Ok(())\n    }\n\n    /// Helper method used to write statements\n    ///\n    /// # Notes\n    /// Always adds a newline\n    fn write_stmt(\n        &mut self,\n        sta: &crate::Statement,\n        ctx: &back::FunctionCtx,\n        level: back::Level,\n    ) -> BackendResult {\n        use crate::Statement;\n\n        match *sta {\n            // This is where we can generate intermediate constants for some expression types.\n            Statement::Emit(ref range) => {\n                for handle in range.clone() {\n                    let ptr_class = ctx.resolve_type(handle, &self.module.types).pointer_space();\n                    let expr_name = if ptr_class.is_some() {\n                        // GLSL can't save a pointer-valued expression in a variable,\n                        // but we shouldn't ever need to: they should never be named expressions,\n                        // and none of the expression types flagged by bake_ref_count can be pointer-valued.\n                        None\n                    } else if let Some(name) = ctx.named_expressions.get(&handle) {\n                        // Front end provides names for all variables at the start of writing.\n                        // But we write them to step by step. We need to recache them\n                        // Otherwise, we could accidentally write variable name instead of full expression.\n                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.\n                        Some(self.namer.call(name))\n                    } else if self.need_bake_expressions.contains(&handle) {\n                        Some(Baked(handle).to_string())\n                    } else {\n                        None\n                    };\n\n                    // If we are going to write an `ImageLoad` next and the target image\n                    // is sampled and we are using the `Restrict` policy for bounds\n                    // checking images we need to write a local holding the clamped lod.\n                    if let crate::Expression::ImageLoad {\n                        image,\n                        level: Some(level_expr),\n                        ..\n                    } = ctx.expressions[handle]\n                    {\n                        if let TypeInner::Image {\n                            class: crate::ImageClass::Sampled { .. },\n                            ..\n                        } = *ctx.resolve_type(image, &self.module.types)\n                        {\n                            if let proc::BoundsCheckPolicy::Restrict = self.policies.image_load {\n                                write!(self.out, \"{level}\")?;\n                                self.write_clamped_lod(ctx, handle, image, level_expr)?\n                            }\n                        }\n                    }\n\n                    if let Some(name) = expr_name {\n                        write!(self.out, \"{level}\")?;\n                        self.write_named_expr(handle, name, handle, ctx)?;\n                    }\n                }\n            }\n            // Blocks are simple we just need to write the block statements between braces\n            // We could also just print the statements but this is more readable and maps more\n            // closely to the IR\n            Statement::Block(ref block) => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"{{\")?;\n                for sta in block.iter() {\n                    // Increase the indentation to help with readability\n                    self.write_stmt(sta, ctx, level.next())?\n                }\n                writeln!(self.out, \"{level}}}\")?\n            }\n            // Ifs are written as in C:\n            // ```\n            // if(condition) {\n            //  accept\n            // } else {\n            //  reject\n            // }\n            // ```\n            Statement::If {\n                condition,\n                ref accept,\n                ref reject,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"if (\")?;\n                self.write_expr(condition, ctx)?;\n                writeln!(self.out, \") {{\")?;\n\n                for sta in accept {\n                    // Increase indentation to help with readability\n                    self.write_stmt(sta, ctx, level.next())?;\n                }\n\n                // If there are no statements in the reject block we skip writing it\n                // This is only for readability\n                if !reject.is_empty() {\n                    writeln!(self.out, \"{level}}} else {{\")?;\n\n                    for sta in reject {\n                        // Increase indentation to help with readability\n                        self.write_stmt(sta, ctx, level.next())?;\n                    }\n                }\n\n                writeln!(self.out, \"{level}}}\")?\n            }\n            // Switch are written as in C:\n            // ```\n            // switch (selector) {\n            //      // Fallthrough\n            //      case label:\n            //          block\n            //      // Non fallthrough\n            //      case label:\n            //          block\n            //          break;\n            //      default:\n            //          block\n            //  }\n            //  ```\n            //  Where the `default` case happens isn't important but we put it last\n            //  so that we don't need to print a `break` for it\n            Statement::Switch {\n                selector,\n                ref cases,\n            } => {\n                let l2 = level.next();\n                // Some GLSL consumers may not handle switches with a single\n                // body correctly: See wgpu#4514. Write such switch statements\n                // as a `do {} while(false);` loop instead.\n                //\n                // Since doing so may inadvertently capture `continue`\n                // statements in the switch body, we must apply continue\n                // forwarding. See the `naga::back::continue_forward` module\n                // docs for details.\n                let one_body = cases\n                    .iter()\n                    .rev()\n                    .skip(1)\n                    .all(|case| case.fall_through && case.body.is_empty());\n                if one_body {\n                    // Unlike HLSL, in GLSL `continue_ctx` only needs to know\n                    // about [`Switch`] statements that are being rendered as\n                    // `do-while` loops.\n                    if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {\n                        writeln!(self.out, \"{level}bool {variable} = false;\",)?;\n                    };\n                    writeln!(self.out, \"{level}do {{\")?;\n                    // Note: Expressions have no side-effects so we don't need to emit selector expression.\n\n                    // Body\n                    if let Some(case) = cases.last() {\n                        for sta in case.body.iter() {\n                            self.write_stmt(sta, ctx, l2)?;\n                        }\n                    }\n                    // End do-while\n                    writeln!(self.out, \"{level}}} while(false);\")?;\n\n                    // Handle any forwarded continue statements.\n                    use back::continue_forward::ExitControlFlow;\n                    let op = match self.continue_ctx.exit_switch() {\n                        ExitControlFlow::None => None,\n                        ExitControlFlow::Continue { variable } => Some((\"continue\", variable)),\n                        ExitControlFlow::Break { variable } => Some((\"break\", variable)),\n                    };\n                    if let Some((control_flow, variable)) = op {\n                        writeln!(self.out, \"{level}if ({variable}) {{\")?;\n                        writeln!(self.out, \"{l2}{control_flow};\")?;\n                        writeln!(self.out, \"{level}}}\")?;\n                    }\n                } else {\n                    // Start the switch\n                    write!(self.out, \"{level}\")?;\n                    write!(self.out, \"switch(\")?;\n                    self.write_expr(selector, ctx)?;\n                    writeln!(self.out, \") {{\")?;\n\n                    // Write all cases\n                    for case in cases {\n                        match case.value {\n                            crate::SwitchValue::I32(value) => {\n                                write!(self.out, \"{l2}case {value}:\")?\n                            }\n                            crate::SwitchValue::U32(value) => {\n                                write!(self.out, \"{l2}case {value}u:\")?\n                            }\n                            crate::SwitchValue::Default => write!(self.out, \"{l2}default:\")?,\n                        }\n\n                        let write_block_braces = !(case.fall_through && case.body.is_empty());\n                        if write_block_braces {\n                            writeln!(self.out, \" {{\")?;\n                        } else {\n                            writeln!(self.out)?;\n                        }\n\n                        for sta in case.body.iter() {\n                            self.write_stmt(sta, ctx, l2.next())?;\n                        }\n\n                        if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator())\n                        {\n                            writeln!(self.out, \"{}break;\", l2.next())?;\n                        }\n\n                        if write_block_braces {\n                            writeln!(self.out, \"{l2}}}\")?;\n                        }\n                    }\n\n                    writeln!(self.out, \"{level}}}\")?\n                }\n            }\n            // Loops in naga IR are based on wgsl loops, glsl can emulate the behaviour by using a\n            // while true loop and appending the continuing block to the body resulting on:\n            // ```\n            // bool loop_init = true;\n            // while(true) {\n            //  if (!loop_init) { <continuing> }\n            //  loop_init = false;\n            //  <body>\n            // }\n            // ```\n            Statement::Loop {\n                ref body,\n                ref continuing,\n                break_if,\n            } => {\n                self.continue_ctx.enter_loop();\n                if !continuing.is_empty() || break_if.is_some() {\n                    let gate_name = self.namer.call(\"loop_init\");\n                    writeln!(self.out, \"{level}bool {gate_name} = true;\")?;\n                    writeln!(self.out, \"{level}while(true) {{\")?;\n                    let l2 = level.next();\n                    let l3 = l2.next();\n                    writeln!(self.out, \"{l2}if (!{gate_name}) {{\")?;\n                    for sta in continuing {\n                        self.write_stmt(sta, ctx, l3)?;\n                    }\n                    if let Some(condition) = break_if {\n                        write!(self.out, \"{l3}if (\")?;\n                        self.write_expr(condition, ctx)?;\n                        writeln!(self.out, \") {{\")?;\n                        writeln!(self.out, \"{}break;\", l3.next())?;\n                        writeln!(self.out, \"{l3}}}\")?;\n                    }\n                    writeln!(self.out, \"{l2}}}\")?;\n                    writeln!(self.out, \"{}{} = false;\", level.next(), gate_name)?;\n                } else {\n                    writeln!(self.out, \"{level}while(true) {{\")?;\n                }\n                for sta in body {\n                    self.write_stmt(sta, ctx, level.next())?;\n                }\n                writeln!(self.out, \"{level}}}\")?;\n                self.continue_ctx.exit_loop();\n            }\n            // Break, continue and return as written as in C\n            // `break;`\n            Statement::Break => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"break;\")?\n            }\n            // `continue;`\n            Statement::Continue => {\n                // Sometimes we must render a `Continue` statement as a `break`.\n                // See the docs for the `back::continue_forward` module.\n                if let Some(variable) = self.continue_ctx.continue_encountered() {\n                    writeln!(self.out, \"{level}{variable} = true;\",)?;\n                    writeln!(self.out, \"{level}break;\")?\n                } else {\n                    writeln!(self.out, \"{level}continue;\")?\n                }\n            }\n            // `return expr;`, `expr` is optional\n            Statement::Return { value } => {\n                write!(self.out, \"{level}\")?;\n                match ctx.ty {\n                    back::FunctionType::Function(_) => {\n                        write!(self.out, \"return\")?;\n                        // Write the expression to be returned if needed\n                        if let Some(expr) = value {\n                            write!(self.out, \" \")?;\n                            self.write_expr(expr, ctx)?;\n                        }\n                        writeln!(self.out, \";\")?;\n                    }\n                    back::FunctionType::EntryPoint(ep_index) => {\n                        let mut has_point_size = false;\n                        let ep = &self.module.entry_points[ep_index as usize];\n                        if let Some(ref result) = ep.function.result {\n                            let value = value.unwrap();\n                            match self.module.types[result.ty].inner {\n                                TypeInner::Struct { ref members, .. } => {\n                                    let temp_struct_name = match ctx.expressions[value] {\n                                        crate::Expression::Compose { .. } => {\n                                            let return_struct = \"_tmp_return\";\n                                            write!(\n                                                self.out,\n                                                \"{} {} = \",\n                                                &self.names[&NameKey::Type(result.ty)],\n                                                return_struct\n                                            )?;\n                                            self.write_expr(value, ctx)?;\n                                            writeln!(self.out, \";\")?;\n                                            write!(self.out, \"{level}\")?;\n                                            Some(return_struct)\n                                        }\n                                        _ => None,\n                                    };\n\n                                    for (index, member) in members.iter().enumerate() {\n                                        if let Some(crate::Binding::BuiltIn(\n                                            crate::BuiltIn::PointSize,\n                                        )) = member.binding\n                                        {\n                                            has_point_size = true;\n                                        }\n\n                                        let varying_name = VaryingName {\n                                            binding: member.binding.as_ref().unwrap(),\n                                            stage: ep.stage,\n                                            options: VaryingOptions::from_writer_options(\n                                                self.options,\n                                                true,\n                                            ),\n                                        };\n                                        write!(self.out, \"{varying_name} = \")?;\n\n                                        if let Some(struct_name) = temp_struct_name {\n                                            write!(self.out, \"{struct_name}\")?;\n                                        } else {\n                                            self.write_expr(value, ctx)?;\n                                        }\n\n                                        // Write field name\n                                        writeln!(\n                                            self.out,\n                                            \".{};\",\n                                            &self.names\n                                                [&NameKey::StructMember(result.ty, index as u32)]\n                                        )?;\n                                        write!(self.out, \"{level}\")?;\n                                    }\n                                }\n                                _ => {\n                                    let name = VaryingName {\n                                        binding: result.binding.as_ref().unwrap(),\n                                        stage: ep.stage,\n                                        options: VaryingOptions::from_writer_options(\n                                            self.options,\n                                            true,\n                                        ),\n                                    };\n                                    write!(self.out, \"{name} = \")?;\n                                    self.write_expr(value, ctx)?;\n                                    writeln!(self.out, \";\")?;\n                                    write!(self.out, \"{level}\")?;\n                                }\n                            }\n                        }\n\n                        let is_vertex_stage = self.module.entry_points[ep_index as usize].stage\n                            == ShaderStage::Vertex;\n                        if is_vertex_stage\n                            && self\n                                .options\n                                .writer_flags\n                                .contains(WriterFlags::ADJUST_COORDINATE_SPACE)\n                        {\n                            writeln!(\n                                self.out,\n                                \"gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\",\n                            )?;\n                            write!(self.out, \"{level}\")?;\n                        }\n\n                        if is_vertex_stage\n                            && self\n                                .options\n                                .writer_flags\n                                .contains(WriterFlags::FORCE_POINT_SIZE)\n                            && !has_point_size\n                        {\n                            writeln!(self.out, \"gl_PointSize = 1.0;\")?;\n                            write!(self.out, \"{level}\")?;\n                        }\n                        writeln!(self.out, \"return;\")?;\n                    }\n                }\n            }\n            // This is one of the places were glsl adds to the syntax of C in this case the discard\n            // keyword which ceases all further processing in a fragment shader, it's called OpKill\n            // in spir-v that's why it's called `Statement::Kill`\n            Statement::Kill => writeln!(self.out, \"{level}discard;\")?,\n            Statement::ControlBarrier(flags) => {\n                self.write_control_barrier(flags, level)?;\n            }\n            Statement::MemoryBarrier(flags) => {\n                self.write_memory_barrier(flags, level)?;\n            }\n            // Stores in glsl are just variable assignments written as `pointer = value;`\n            Statement::Store { pointer, value } => {\n                write!(self.out, \"{level}\")?;\n                self.write_expr(pointer, ctx)?;\n                write!(self.out, \" = \")?;\n                self.write_expr(value, ctx)?;\n                writeln!(self.out, \";\")?\n            }\n            Statement::WorkGroupUniformLoad { pointer, result } => {\n                // GLSL doesn't have pointers, which means that this backend needs to ensure that\n                // the actual \"loading\" is happening between the two barriers.\n                // This is done in `Emit` by never emitting a variable name for pointer variables\n                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;\n\n                let result_name = Baked(result).to_string();\n                write!(self.out, \"{level}\")?;\n                // Expressions cannot have side effects, so just writing the expression here is fine.\n                self.write_named_expr(pointer, result_name, result, ctx)?;\n\n                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;\n            }\n            // Stores a value into an image.\n            Statement::ImageStore {\n                image,\n                coordinate,\n                array_index,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n                self.write_image_store(ctx, image, coordinate, array_index, value)?\n            }\n            // A `Call` is written `name(arguments)` where `arguments` is a comma separated expressions list\n            Statement::Call {\n                function,\n                ref arguments,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                if let Some(expr) = result {\n                    let name = Baked(expr).to_string();\n                    let result = self.module.functions[function].result.as_ref().unwrap();\n                    self.write_type(result.ty)?;\n                    write!(self.out, \" {name}\")?;\n                    if let TypeInner::Array { base, size, .. } = self.module.types[result.ty].inner\n                    {\n                        self.write_array_size(base, size)?\n                    }\n                    write!(self.out, \" = \")?;\n                    self.named_expressions.insert(expr, name);\n                }\n                write!(self.out, \"{}(\", &self.names[&NameKey::Function(function)])?;\n                let arguments: Vec<_> = arguments\n                    .iter()\n                    .enumerate()\n                    .filter_map(|(i, arg)| {\n                        let arg_ty = self.module.functions[function].arguments[i].ty;\n                        match self.module.types[arg_ty].inner {\n                            TypeInner::Sampler { .. } => None,\n                            _ => Some(*arg),\n                        }\n                    })\n                    .collect();\n                self.write_slice(&arguments, |this, _, arg| this.write_expr(*arg, ctx))?;\n                writeln!(self.out, \");\")?\n            }\n            Statement::Atomic {\n                pointer,\n                ref fun,\n                value,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n\n                match *fun {\n                    crate::AtomicFunction::Exchange {\n                        compare: Some(compare_expr),\n                    } => {\n                        let result_handle = result.expect(\"CompareExchange must have a result\");\n                        let res_name = Baked(result_handle).to_string();\n                        self.write_type(ctx.info[result_handle].ty.handle().unwrap())?;\n                        write!(self.out, \" {res_name};\")?;\n                        write!(self.out, \" {res_name}.old_value = atomicCompSwap(\")?;\n                        self.write_expr(pointer, ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(compare_expr, ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(value, ctx)?;\n                        writeln!(self.out, \");\")?;\n\n                        write!(\n                            self.out,\n                            \"{level}{res_name}.exchanged = ({res_name}.old_value == \"\n                        )?;\n                        self.write_expr(compare_expr, ctx)?;\n                        writeln!(self.out, \");\")?;\n                        self.named_expressions.insert(result_handle, res_name);\n                    }\n                    _ => {\n                        if let Some(result) = result {\n                            let res_name = Baked(result).to_string();\n                            self.write_type(ctx.info[result].ty.handle().unwrap())?;\n                            write!(self.out, \" {res_name} = \")?;\n                            self.named_expressions.insert(result, res_name);\n                        }\n                        let fun_str = fun.to_glsl();\n                        write!(self.out, \"atomic{fun_str}(\")?;\n                        self.write_expr(pointer, ctx)?;\n                        write!(self.out, \", \")?;\n                        if let crate::AtomicFunction::Subtract = *fun {\n                            // Emulate `atomicSub` with `atomicAdd` by negating the value.\n                            write!(self.out, \"-\")?;\n                        }\n                        self.write_expr(value, ctx)?;\n                        writeln!(self.out, \");\")?;\n                    }\n                }\n            }\n            // Stores a value into an image.\n            Statement::ImageAtomic {\n                image,\n                coordinate,\n                array_index,\n                fun,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n                self.write_image_atomic(ctx, image, coordinate, array_index, fun, value)?\n            }\n            Statement::RayQuery { .. } => unreachable!(),\n            Statement::SubgroupBallot { result, predicate } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);\n                self.write_value_type(res_ty)?;\n                write!(self.out, \" {res_name} = \")?;\n                self.named_expressions.insert(result, res_name);\n\n                write!(self.out, \"subgroupBallot(\")?;\n                match predicate {\n                    Some(predicate) => self.write_expr(predicate, ctx)?,\n                    None => write!(self.out, \"true\")?,\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupCollectiveOperation {\n                op,\n                collective_op,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);\n                self.write_value_type(res_ty)?;\n                write!(self.out, \" {res_name} = \")?;\n                self.named_expressions.insert(result, res_name);\n\n                match (collective_op, op) {\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {\n                        write!(self.out, \"subgroupAll(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {\n                        write!(self.out, \"subgroupAny(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupAdd(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupMul(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {\n                        write!(self.out, \"subgroupMax(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {\n                        write!(self.out, \"subgroupMin(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {\n                        write!(self.out, \"subgroupAnd(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {\n                        write!(self.out, \"subgroupOr(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {\n                        write!(self.out, \"subgroupXor(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupExclusiveAdd(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupExclusiveMul(\")?\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupInclusiveAdd(\")?\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupInclusiveMul(\")?\n                    }\n                    _ => unimplemented!(),\n                }\n                self.write_expr(argument, ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupGather {\n                mode,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);\n                self.write_value_type(res_ty)?;\n                write!(self.out, \" {res_name} = \")?;\n                self.named_expressions.insert(result, res_name);\n\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {\n                        write!(self.out, \"subgroupBroadcastFirst(\")?;\n                    }\n                    crate::GatherMode::Broadcast(_) => {\n                        write!(self.out, \"subgroupBroadcast(\")?;\n                    }\n                    crate::GatherMode::Shuffle(_) => {\n                        write!(self.out, \"subgroupShuffle(\")?;\n                    }\n                    crate::GatherMode::ShuffleDown(_) => {\n                        write!(self.out, \"subgroupShuffleDown(\")?;\n                    }\n                    crate::GatherMode::ShuffleUp(_) => {\n                        write!(self.out, \"subgroupShuffleUp(\")?;\n                    }\n                    crate::GatherMode::ShuffleXor(_) => {\n                        write!(self.out, \"subgroupShuffleXor(\")?;\n                    }\n                    crate::GatherMode::QuadBroadcast(_) => {\n                        write!(self.out, \"subgroupQuadBroadcast(\")?;\n                    }\n                    crate::GatherMode::QuadSwap(direction) => match direction {\n                        crate::Direction::X => {\n                            write!(self.out, \"subgroupQuadSwapHorizontal(\")?;\n                        }\n                        crate::Direction::Y => {\n                            write!(self.out, \"subgroupQuadSwapVertical(\")?;\n                        }\n                        crate::Direction::Diagonal => {\n                            write!(self.out, \"subgroupQuadSwapDiagonal(\")?;\n                        }\n                    },\n                }\n                self.write_expr(argument, ctx)?;\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {}\n                    crate::GatherMode::Broadcast(index)\n                    | crate::GatherMode::Shuffle(index)\n                    | crate::GatherMode::ShuffleDown(index)\n                    | crate::GatherMode::ShuffleUp(index)\n                    | crate::GatherMode::ShuffleXor(index)\n                    | crate::GatherMode::QuadBroadcast(index) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(index, ctx)?;\n                    }\n                    crate::GatherMode::QuadSwap(_) => {}\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::CooperativeStore { .. } => unimplemented!(),\n            Statement::RayPipelineFunction(_) => unimplemented!(),\n        }\n\n        Ok(())\n    }\n\n    /// Write a const expression.\n    ///\n    /// Write `expr`, a handle to an [`Expression`] in the current [`Module`]'s\n    /// constant expression arena, as GLSL expression.\n    ///\n    /// # Notes\n    /// Adds no newlines or leading/trailing whitespace\n    ///\n    /// [`Expression`]: crate::Expression\n    /// [`Module`]: crate::Module\n    fn write_const_expr(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        arena: &crate::Arena<crate::Expression>,\n    ) -> BackendResult {\n        self.write_possibly_const_expr(\n            expr,\n            arena,\n            |expr| &self.info[expr],\n            |writer, expr| writer.write_const_expr(expr, arena),\n        )\n    }\n\n    /// Write [`Expression`] variants that can occur in both runtime and const expressions.\n    ///\n    /// Write `expr`, a handle to an [`Expression`] in the arena `expressions`,\n    /// as as GLSL expression. This must be one of the [`Expression`] variants\n    /// that is allowed to occur in constant expressions.\n    ///\n    /// Use `write_expression` to write subexpressions.\n    ///\n    /// This is the common code for `write_expr`, which handles arbitrary\n    /// runtime expressions, and `write_const_expr`, which only handles\n    /// const-expressions. Each of those callers passes itself (essentially) as\n    /// the `write_expression` callback, so that subexpressions are restricted\n    /// to the appropriate variants.\n    ///\n    /// # Notes\n    /// Adds no newlines or leading/trailing whitespace\n    ///\n    /// [`Expression`]: crate::Expression\n    fn write_possibly_const_expr<'w, I, E>(\n        &'w mut self,\n        expr: Handle<crate::Expression>,\n        expressions: &crate::Arena<crate::Expression>,\n        info: I,\n        write_expression: E,\n    ) -> BackendResult\n    where\n        I: Fn(Handle<crate::Expression>) -> &'w proc::TypeResolution,\n        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,\n    {\n        use crate::Expression;\n\n        match expressions[expr] {\n            Expression::Literal(literal) => {\n                match literal {\n                    // Floats are written using `Debug` instead of `Display` because it always appends the\n                    // decimal part even it's zero which is needed for a valid glsl float constant\n                    crate::Literal::F64(value) => write!(self.out, \"{value:?}LF\")?,\n                    crate::Literal::F32(value) => write!(self.out, \"{value:?}\")?,\n                    crate::Literal::F16(_) => {\n                        return Err(Error::Custom(\"GLSL has no 16-bit float type\".into()));\n                    }\n                    // Unsigned integers need a `u` at the end\n                    //\n                    // While `core` doesn't necessarily need it, it's allowed and since `es` needs it we\n                    // always write it as the extra branch wouldn't have any benefit in readability\n                    crate::Literal::U32(value) => write!(self.out, \"{value}u\")?,\n                    crate::Literal::I32(value) => write!(self.out, \"{value}\")?,\n                    crate::Literal::Bool(value) => write!(self.out, \"{value}\")?,\n                    crate::Literal::I64(_) => {\n                        return Err(Error::Custom(\"GLSL has no 64-bit integer type\".into()));\n                    }\n                    crate::Literal::U64(_) => {\n                        return Err(Error::Custom(\"GLSL has no 64-bit integer type\".into()));\n                    }\n                    crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {\n                        return Err(Error::Custom(\n                            \"Abstract types should not appear in IR presented to backends\".into(),\n                        ));\n                    }\n                }\n            }\n            Expression::Constant(handle) => {\n                let constant = &self.module.constants[handle];\n                if constant.name.is_some() {\n                    write!(self.out, \"{}\", self.names[&NameKey::Constant(handle)])?;\n                } else {\n                    self.write_const_expr(constant.init, &self.module.global_expressions)?;\n                }\n            }\n            Expression::ZeroValue(ty) => {\n                self.write_zero_init_value(ty)?;\n            }\n            Expression::Compose { ty, ref components } => {\n                self.write_type(ty)?;\n\n                if let TypeInner::Array { base, size, .. } = self.module.types[ty].inner {\n                    self.write_array_size(base, size)?;\n                }\n\n                write!(self.out, \"(\")?;\n                for (index, component) in components.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    write_expression(self, *component)?;\n                }\n                write!(self.out, \")\")?\n            }\n            // `Splat` needs to actually write down a vector, it's not always inferred in GLSL.\n            Expression::Splat { size: _, value } => {\n                let resolved = info(expr).inner_with(&self.module.types);\n                self.write_value_type(resolved)?;\n                write!(self.out, \"(\")?;\n                write_expression(self, value)?;\n                write!(self.out, \")\")?\n            }\n            _ => {\n                return Err(Error::Override);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Helper method to write expressions\n    ///\n    /// # Notes\n    /// Doesn't add any newlines or leading/trailing spaces\n    fn write_expr(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        use crate::Expression;\n\n        if let Some(name) = self.named_expressions.get(&expr) {\n            write!(self.out, \"{name}\")?;\n            return Ok(());\n        }\n\n        match ctx.expressions[expr] {\n            Expression::Literal(_)\n            | Expression::Constant(_)\n            | Expression::ZeroValue(_)\n            | Expression::Compose { .. }\n            | Expression::Splat { .. } => {\n                self.write_possibly_const_expr(\n                    expr,\n                    ctx.expressions,\n                    |expr| &ctx.info[expr].ty,\n                    |writer, expr| writer.write_expr(expr, ctx),\n                )?;\n            }\n            Expression::Override(_) => return Err(Error::Override),\n            // `Access` is applied to arrays, vectors and matrices and is written as indexing\n            Expression::Access { base, index } => {\n                self.write_expr(base, ctx)?;\n                write!(self.out, \"[\")?;\n                self.write_expr(index, ctx)?;\n                write!(self.out, \"]\")?\n            }\n            // `AccessIndex` is the same as `Access` except that the index is a constant and it can\n            // be applied to structs, in this case we need to find the name of the field at that\n            // index and write `base.field_name`\n            Expression::AccessIndex { base, index } => {\n                self.write_expr(base, ctx)?;\n\n                let base_ty_res = &ctx.info[base].ty;\n                let mut resolved = base_ty_res.inner_with(&self.module.types);\n                let base_ty_handle = match *resolved {\n                    TypeInner::Pointer { base, space: _ } => {\n                        resolved = &self.module.types[base].inner;\n                        Some(base)\n                    }\n                    _ => base_ty_res.handle(),\n                };\n\n                match *resolved {\n                    TypeInner::Vector { .. } => {\n                        // Write vector access as a swizzle\n                        write!(self.out, \".{}\", back::COMPONENTS[index as usize])?\n                    }\n                    TypeInner::Matrix { .. }\n                    | TypeInner::Array { .. }\n                    | TypeInner::ValuePointer { .. } => write!(self.out, \"[{index}]\")?,\n                    TypeInner::Struct { .. } => {\n                        // This will never panic in case the type is a `Struct`, this is not true\n                        // for other types so we can only check while inside this match arm\n                        let ty = base_ty_handle.unwrap();\n\n                        write!(\n                            self.out,\n                            \".{}\",\n                            &self.names[&NameKey::StructMember(ty, index)]\n                        )?\n                    }\n                    ref other => return Err(Error::Custom(format!(\"Cannot index {other:?}\"))),\n                }\n            }\n            // `Swizzle` adds a few letters behind the dot.\n            Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                self.write_expr(vector, ctx)?;\n                write!(self.out, \".\")?;\n                for &sc in pattern[..size as usize].iter() {\n                    self.out.write_char(back::COMPONENTS[sc as usize])?;\n                }\n            }\n            // Function arguments are written as the argument name\n            Expression::FunctionArgument(pos) => {\n                write!(self.out, \"{}\", &self.names[&ctx.argument_key(pos)])?\n            }\n            // Global variables need some special work for their name but\n            // `get_global_name` does the work for us\n            Expression::GlobalVariable(handle) => {\n                let global = &self.module.global_variables[handle];\n                self.write_global_name(handle, global)?\n            }\n            // A local is written as it's name\n            Expression::LocalVariable(handle) => {\n                write!(self.out, \"{}\", self.names[&ctx.name_key(handle)])?\n            }\n            // glsl has no pointers so there's no load operation, just write the pointer expression\n            Expression::Load { pointer } => self.write_expr(pointer, ctx)?,\n            // `ImageSample` is a bit complicated compared to the rest of the IR.\n            //\n            // First there are three variations depending whether the sample level is explicitly set,\n            // if it's automatic or it it's bias:\n            // `texture(image, coordinate)` - Automatic sample level\n            // `texture(image, coordinate, bias)` - Bias sample level\n            // `textureLod(image, coordinate, level)` - Zero or Exact sample level\n            //\n            // Furthermore if `depth_ref` is some we need to append it to the coordinate vector\n            Expression::ImageSample {\n                image,\n                sampler: _, //TODO?\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                let (dim, class, arrayed) = match *ctx.resolve_type(image, &self.module.types) {\n                    TypeInner::Image {\n                        dim,\n                        class,\n                        arrayed,\n                        ..\n                    } => (dim, class, arrayed),\n                    _ => unreachable!(),\n                };\n                let mut err = None;\n                if dim == crate::ImageDimension::Cube {\n                    if offset.is_some() {\n                        err = Some(\"gsamplerCube[Array][Shadow] doesn't support texture sampling with offsets\");\n                    }\n                    if arrayed\n                        && matches!(class, crate::ImageClass::Depth { .. })\n                        && matches!(level, crate::SampleLevel::Gradient { .. })\n                    {\n                        err = Some(\"samplerCubeArrayShadow don't support textureGrad\");\n                    }\n                }\n                if gather.is_some() && level != crate::SampleLevel::Zero {\n                    err = Some(\"textureGather doesn't support LOD parameters\");\n                }\n                if let Some(err) = err {\n                    return Err(Error::Custom(String::from(err)));\n                }\n\n                // `textureLod[Offset]` on `sampler2DArrayShadow` and `samplerCubeShadow` does not exist in GLSL,\n                // unless `GL_EXT_texture_shadow_lod` is present.\n                // But if the target LOD is zero, we can emulate that by using `textureGrad[Offset]` with a constant gradient of 0.\n                let workaround_lod_with_grad = ((dim == crate::ImageDimension::Cube && !arrayed)\n                    || (dim == crate::ImageDimension::D2 && arrayed))\n                    && level == crate::SampleLevel::Zero\n                    && matches!(class, crate::ImageClass::Depth { .. })\n                    && !self.features.contains(Features::TEXTURE_SHADOW_LOD);\n\n                // Write the function to be used depending on the sample level\n                let fun_name = match level {\n                    crate::SampleLevel::Zero if gather.is_some() => \"textureGather\",\n                    crate::SampleLevel::Zero if workaround_lod_with_grad => \"textureGrad\",\n                    crate::SampleLevel::Auto | crate::SampleLevel::Bias(_) => \"texture\",\n                    crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => \"textureLod\",\n                    crate::SampleLevel::Gradient { .. } => \"textureGrad\",\n                };\n                let offset_name = match offset {\n                    Some(_) => \"Offset\",\n                    None => \"\",\n                };\n\n                write!(self.out, \"{fun_name}{offset_name}(\")?;\n\n                // Write the image that will be used\n                self.write_expr(image, ctx)?;\n                // The space here isn't required but it helps with readability\n                write!(self.out, \", \")?;\n\n                // TODO: handle clamp_to_edge\n                // https://github.com/gfx-rs/wgpu/issues/7791\n\n                // We need to get the coordinates vector size to later build a vector that's `size + 1`\n                // if `depth_ref` is some, if it isn't a vector we panic as that's not a valid expression\n                let mut coord_dim = match *ctx.resolve_type(coordinate, &self.module.types) {\n                    TypeInner::Vector { size, .. } => size as u8,\n                    TypeInner::Scalar { .. } => 1,\n                    _ => unreachable!(),\n                };\n\n                if array_index.is_some() {\n                    coord_dim += 1;\n                }\n                let merge_depth_ref = depth_ref.is_some() && gather.is_none() && coord_dim < 4;\n                if merge_depth_ref {\n                    coord_dim += 1;\n                }\n\n                let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();\n                let is_vec = tex_1d_hack || coord_dim != 1;\n                // Compose a new texture coordinates vector\n                if is_vec {\n                    write!(self.out, \"vec{}(\", coord_dim + tex_1d_hack as u8)?;\n                }\n                self.write_expr(coordinate, ctx)?;\n                if tex_1d_hack {\n                    write!(self.out, \", 0.0\")?;\n                }\n                if let Some(expr) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(expr, ctx)?;\n                }\n                if merge_depth_ref {\n                    write!(self.out, \", \")?;\n                    self.write_expr(depth_ref.unwrap(), ctx)?;\n                }\n                if is_vec {\n                    write!(self.out, \")\")?;\n                }\n\n                if let (Some(expr), false) = (depth_ref, merge_depth_ref) {\n                    write!(self.out, \", \")?;\n                    self.write_expr(expr, ctx)?;\n                }\n\n                match level {\n                    // Auto needs no more arguments\n                    crate::SampleLevel::Auto => (),\n                    // Zero needs level set to 0\n                    crate::SampleLevel::Zero => {\n                        if workaround_lod_with_grad {\n                            let vec_dim = match dim {\n                                crate::ImageDimension::Cube => 3,\n                                _ => 2,\n                            };\n                            write!(self.out, \", vec{vec_dim}(0.0), vec{vec_dim}(0.0)\")?;\n                        } else if gather.is_none() {\n                            write!(self.out, \", 0.0\")?;\n                        }\n                    }\n                    // Exact and bias require another argument\n                    crate::SampleLevel::Exact(expr) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(expr, ctx)?;\n                    }\n                    crate::SampleLevel::Bias(_) => {\n                        // This needs to be done after the offset writing\n                    }\n                    crate::SampleLevel::Gradient { x, y } => {\n                        // If we are using sampler2D to replace sampler1D, we also\n                        // need to make sure to use vec2 gradients\n                        if tex_1d_hack {\n                            write!(self.out, \", vec2(\")?;\n                            self.write_expr(x, ctx)?;\n                            write!(self.out, \", 0.0)\")?;\n                            write!(self.out, \", vec2(\")?;\n                            self.write_expr(y, ctx)?;\n                            write!(self.out, \", 0.0)\")?;\n                        } else {\n                            write!(self.out, \", \")?;\n                            self.write_expr(x, ctx)?;\n                            write!(self.out, \", \")?;\n                            self.write_expr(y, ctx)?;\n                        }\n                    }\n                }\n\n                if let Some(constant) = offset {\n                    write!(self.out, \", \")?;\n                    if tex_1d_hack {\n                        write!(self.out, \"ivec2(\")?;\n                    }\n                    self.write_const_expr(constant, ctx.expressions)?;\n                    if tex_1d_hack {\n                        write!(self.out, \", 0)\")?;\n                    }\n                }\n\n                // Bias is always the last argument\n                if let crate::SampleLevel::Bias(expr) = level {\n                    write!(self.out, \", \")?;\n                    self.write_expr(expr, ctx)?;\n                }\n\n                if let (Some(component), None) = (gather, depth_ref) {\n                    write!(self.out, \", {}\", component as usize)?;\n                }\n\n                // End the function\n                write!(self.out, \")\")?\n            }\n            Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => self.write_image_load(expr, ctx, image, coordinate, array_index, sample, level)?,\n            // Query translates into one of the:\n            // - textureSize/imageSize\n            // - textureQueryLevels\n            // - textureSamples/imageSamples\n            Expression::ImageQuery { image, query } => {\n                use crate::ImageClass;\n\n                // This will only panic if the module is invalid\n                let (dim, class) = match *ctx.resolve_type(image, &self.module.types) {\n                    TypeInner::Image {\n                        dim,\n                        arrayed: _,\n                        class,\n                    } => (dim, class),\n                    _ => unreachable!(),\n                };\n                let components = match dim {\n                    crate::ImageDimension::D1 => 1,\n                    crate::ImageDimension::D2 => 2,\n                    crate::ImageDimension::D3 => 3,\n                    crate::ImageDimension::Cube => 2,\n                };\n\n                if let crate::ImageQuery::Size { .. } = query {\n                    match components {\n                        1 => write!(self.out, \"uint(\")?,\n                        _ => write!(self.out, \"uvec{components}(\")?,\n                    }\n                } else {\n                    write!(self.out, \"uint(\")?;\n                }\n\n                match query {\n                    crate::ImageQuery::Size { level } => {\n                        match class {\n                            ImageClass::Sampled { multi, .. } | ImageClass::Depth { multi } => {\n                                write!(self.out, \"textureSize(\")?;\n                                self.write_expr(image, ctx)?;\n                                if let Some(expr) = level {\n                                    let cast_to_int = matches!(\n                                        *ctx.resolve_type(expr, &self.module.types),\n                                        TypeInner::Scalar(crate::Scalar {\n                                            kind: crate::ScalarKind::Uint,\n                                            ..\n                                        })\n                                    );\n\n                                    write!(self.out, \", \")?;\n\n                                    if cast_to_int {\n                                        write!(self.out, \"int(\")?;\n                                    }\n\n                                    self.write_expr(expr, ctx)?;\n\n                                    if cast_to_int {\n                                        write!(self.out, \")\")?;\n                                    }\n                                } else if !multi {\n                                    // All textureSize calls requires an lod argument\n                                    // except for multisampled samplers\n                                    write!(self.out, \", 0\")?;\n                                }\n                            }\n                            ImageClass::Storage { .. } => {\n                                write!(self.out, \"imageSize(\")?;\n                                self.write_expr(image, ctx)?;\n                            }\n                            ImageClass::External => unimplemented!(),\n                        }\n                        write!(self.out, \")\")?;\n                        if components != 1 || self.options.version.is_es() {\n                            write!(self.out, \".{}\", &\"xyz\"[..components])?;\n                        }\n                    }\n                    crate::ImageQuery::NumLevels => {\n                        write!(self.out, \"textureQueryLevels(\",)?;\n                        self.write_expr(image, ctx)?;\n                        write!(self.out, \")\",)?;\n                    }\n                    crate::ImageQuery::NumLayers => {\n                        let fun_name = match class {\n                            ImageClass::Sampled { .. } | ImageClass::Depth { .. } => \"textureSize\",\n                            ImageClass::Storage { .. } => \"imageSize\",\n                            ImageClass::External => unimplemented!(),\n                        };\n                        write!(self.out, \"{fun_name}(\")?;\n                        self.write_expr(image, ctx)?;\n                        // All textureSize calls requires an lod argument\n                        // except for multisampled samplers\n                        if !class.is_multisampled() {\n                            write!(self.out, \", 0\")?;\n                        }\n                        write!(self.out, \")\")?;\n                        if components != 1 || self.options.version.is_es() {\n                            write!(self.out, \".{}\", back::COMPONENTS[components])?;\n                        }\n                    }\n                    crate::ImageQuery::NumSamples => {\n                        let fun_name = match class {\n                            ImageClass::Sampled { .. } | ImageClass::Depth { .. } => {\n                                \"textureSamples\"\n                            }\n                            ImageClass::Storage { .. } => \"imageSamples\",\n                            ImageClass::External => unimplemented!(),\n                        };\n                        write!(self.out, \"{fun_name}(\")?;\n                        self.write_expr(image, ctx)?;\n                        write!(self.out, \")\",)?;\n                    }\n                }\n\n                write!(self.out, \")\")?;\n            }\n            Expression::Unary { op, expr } => {\n                let operator_or_fn = match op {\n                    crate::UnaryOperator::Negate => \"-\",\n                    crate::UnaryOperator::LogicalNot => {\n                        match *ctx.resolve_type(expr, &self.module.types) {\n                            TypeInner::Vector { .. } => \"not\",\n                            _ => \"!\",\n                        }\n                    }\n                    crate::UnaryOperator::BitwiseNot => \"~\",\n                };\n                write!(self.out, \"{operator_or_fn}(\")?;\n\n                self.write_expr(expr, ctx)?;\n\n                write!(self.out, \")\")?\n            }\n            // `Binary` we just write `left op right`, except when dealing with\n            // comparison operations on vectors as they are implemented with\n            // builtin functions.\n            // Once again we wrap everything in parentheses to avoid precedence issues\n            Expression::Binary {\n                mut op,\n                left,\n                right,\n            } => {\n                // Holds `Some(function_name)` if the binary operation is\n                // implemented as a function call\n                use crate::{BinaryOperator as Bo, ScalarKind as Sk, TypeInner as Ti};\n\n                let left_inner = ctx.resolve_type(left, &self.module.types);\n                let right_inner = ctx.resolve_type(right, &self.module.types);\n\n                let function = match (left_inner, right_inner) {\n                    (&Ti::Vector { scalar, .. }, &Ti::Vector { .. }) => match op {\n                        Bo::Less\n                        | Bo::LessEqual\n                        | Bo::Greater\n                        | Bo::GreaterEqual\n                        | Bo::Equal\n                        | Bo::NotEqual => BinaryOperation::VectorCompare,\n                        Bo::Modulo if scalar.kind == Sk::Float => BinaryOperation::Modulo,\n                        Bo::And if scalar.kind == Sk::Bool => {\n                            op = crate::BinaryOperator::LogicalAnd;\n                            BinaryOperation::VectorComponentWise\n                        }\n                        Bo::InclusiveOr if scalar.kind == Sk::Bool => {\n                            op = crate::BinaryOperator::LogicalOr;\n                            BinaryOperation::VectorComponentWise\n                        }\n                        _ => BinaryOperation::Other,\n                    },\n                    _ => match (left_inner.scalar_kind(), right_inner.scalar_kind()) {\n                        (Some(Sk::Float), _) | (_, Some(Sk::Float)) => match op {\n                            Bo::Modulo => BinaryOperation::Modulo,\n                            _ => BinaryOperation::Other,\n                        },\n                        (Some(Sk::Bool), Some(Sk::Bool)) => match op {\n                            Bo::InclusiveOr => {\n                                op = crate::BinaryOperator::LogicalOr;\n                                BinaryOperation::Other\n                            }\n                            Bo::And => {\n                                op = crate::BinaryOperator::LogicalAnd;\n                                BinaryOperation::Other\n                            }\n                            _ => BinaryOperation::Other,\n                        },\n                        _ => BinaryOperation::Other,\n                    },\n                };\n\n                match function {\n                    BinaryOperation::VectorCompare => {\n                        let op_str = match op {\n                            Bo::Less => \"lessThan(\",\n                            Bo::LessEqual => \"lessThanEqual(\",\n                            Bo::Greater => \"greaterThan(\",\n                            Bo::GreaterEqual => \"greaterThanEqual(\",\n                            Bo::Equal => \"equal(\",\n                            Bo::NotEqual => \"notEqual(\",\n                            _ => unreachable!(),\n                        };\n                        write!(self.out, \"{op_str}\")?;\n                        self.write_expr(left, ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(right, ctx)?;\n                        write!(self.out, \")\")?;\n                    }\n                    BinaryOperation::VectorComponentWise => {\n                        self.write_value_type(left_inner)?;\n                        write!(self.out, \"(\")?;\n\n                        let size = match *left_inner {\n                            Ti::Vector { size, .. } => size,\n                            _ => unreachable!(),\n                        };\n\n                        for i in 0..size as usize {\n                            if i != 0 {\n                                write!(self.out, \", \")?;\n                            }\n\n                            self.write_expr(left, ctx)?;\n                            write!(self.out, \".{}\", back::COMPONENTS[i])?;\n\n                            write!(self.out, \" {} \", back::binary_operation_str(op))?;\n\n                            self.write_expr(right, ctx)?;\n                            write!(self.out, \".{}\", back::COMPONENTS[i])?;\n                        }\n\n                        write!(self.out, \")\")?;\n                    }\n                    // TODO: handle undefined behavior of BinaryOperator::Modulo\n                    //\n                    // sint:\n                    // if right == 0 return 0\n                    // if left == min(type_of(left)) && right == -1 return 0\n                    // if sign(left) == -1 || sign(right) == -1 return result as defined by WGSL\n                    //\n                    // uint:\n                    // if right == 0 return 0\n                    //\n                    // float:\n                    // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798\n                    BinaryOperation::Modulo => {\n                        write!(self.out, \"(\")?;\n\n                        // write `e1 - e2 * trunc(e1 / e2)`\n                        self.write_expr(left, ctx)?;\n                        write!(self.out, \" - \")?;\n                        self.write_expr(right, ctx)?;\n                        write!(self.out, \" * \")?;\n                        write!(self.out, \"trunc(\")?;\n                        self.write_expr(left, ctx)?;\n                        write!(self.out, \" / \")?;\n                        self.write_expr(right, ctx)?;\n                        write!(self.out, \")\")?;\n\n                        write!(self.out, \")\")?;\n                    }\n                    BinaryOperation::Other => {\n                        write!(self.out, \"(\")?;\n\n                        self.write_expr(left, ctx)?;\n                        write!(self.out, \" {} \", back::binary_operation_str(op))?;\n                        self.write_expr(right, ctx)?;\n\n                        write!(self.out, \")\")?;\n                    }\n                }\n            }\n            // `Select` is written as `condition ? accept : reject`\n            // We wrap everything in parentheses to avoid precedence issues\n            Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                let cond_ty = ctx.resolve_type(condition, &self.module.types);\n                let vec_select = if let TypeInner::Vector { .. } = *cond_ty {\n                    true\n                } else {\n                    false\n                };\n\n                // TODO: Boolean mix on desktop required GL_EXT_shader_integer_mix\n                if vec_select {\n                    // Glsl defines that for mix when the condition is a boolean the first element\n                    // is picked if condition is false and the second if condition is true\n                    write!(self.out, \"mix(\")?;\n                    self.write_expr(reject, ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(accept, ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(condition, ctx)?;\n                } else {\n                    write!(self.out, \"(\")?;\n                    self.write_expr(condition, ctx)?;\n                    write!(self.out, \" ? \")?;\n                    self.write_expr(accept, ctx)?;\n                    write!(self.out, \" : \")?;\n                    self.write_expr(reject, ctx)?;\n                }\n\n                write!(self.out, \")\")?\n            }\n            // `Derivative` is a function call to a glsl provided function\n            Expression::Derivative { axis, ctrl, expr } => {\n                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n                let fun_name = if self.options.version.supports_derivative_control() {\n                    match (axis, ctrl) {\n                        (Axis::X, Ctrl::Coarse) => \"dFdxCoarse\",\n                        (Axis::X, Ctrl::Fine) => \"dFdxFine\",\n                        (Axis::X, Ctrl::None) => \"dFdx\",\n                        (Axis::Y, Ctrl::Coarse) => \"dFdyCoarse\",\n                        (Axis::Y, Ctrl::Fine) => \"dFdyFine\",\n                        (Axis::Y, Ctrl::None) => \"dFdy\",\n                        (Axis::Width, Ctrl::Coarse) => \"fwidthCoarse\",\n                        (Axis::Width, Ctrl::Fine) => \"fwidthFine\",\n                        (Axis::Width, Ctrl::None) => \"fwidth\",\n                    }\n                } else {\n                    match axis {\n                        Axis::X => \"dFdx\",\n                        Axis::Y => \"dFdy\",\n                        Axis::Width => \"fwidth\",\n                    }\n                };\n                write!(self.out, \"{fun_name}(\")?;\n                self.write_expr(expr, ctx)?;\n                write!(self.out, \")\")?\n            }\n            // `Relational` is a normal function call to some glsl provided functions\n            Expression::Relational { fun, argument } => {\n                use crate::RelationalFunction as Rf;\n\n                let fun_name = match fun {\n                    Rf::IsInf => \"isinf\",\n                    Rf::IsNan => \"isnan\",\n                    Rf::All => \"all\",\n                    Rf::Any => \"any\",\n                };\n                write!(self.out, \"{fun_name}(\")?;\n\n                self.write_expr(argument, ctx)?;\n\n                write!(self.out, \")\")?\n            }\n            Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                use crate::MathFunction as Mf;\n\n                let fun_name = match fun {\n                    // comparison\n                    Mf::Abs => \"abs\",\n                    Mf::Min => \"min\",\n                    Mf::Max => \"max\",\n                    Mf::Clamp => {\n                        let scalar_kind = ctx\n                            .resolve_type(arg, &self.module.types)\n                            .scalar_kind()\n                            .unwrap();\n                        match scalar_kind {\n                            crate::ScalarKind::Float => \"clamp\",\n                            // Clamp is undefined if min > max. In practice this means it can use a median-of-three\n                            // instruction to determine the value. This is fine according to the WGSL spec for float\n                            // clamp, but integer clamp _must_ use min-max. As such we write out min/max.\n                            _ => {\n                                write!(self.out, \"min(max(\")?;\n                                self.write_expr(arg, ctx)?;\n                                write!(self.out, \", \")?;\n                                self.write_expr(arg1.unwrap(), ctx)?;\n                                write!(self.out, \"), \")?;\n                                self.write_expr(arg2.unwrap(), ctx)?;\n                                write!(self.out, \")\")?;\n\n                                return Ok(());\n                            }\n                        }\n                    }\n                    Mf::Saturate => {\n                        write!(self.out, \"clamp(\")?;\n\n                        self.write_expr(arg, ctx)?;\n\n                        match *ctx.resolve_type(arg, &self.module.types) {\n                            TypeInner::Vector { size, .. } => write!(\n                                self.out,\n                                \", vec{}(0.0), vec{0}(1.0)\",\n                                common::vector_size_str(size)\n                            )?,\n                            _ => write!(self.out, \", 0.0, 1.0\")?,\n                        }\n\n                        write!(self.out, \")\")?;\n\n                        return Ok(());\n                    }\n                    // trigonometry\n                    Mf::Cos => \"cos\",\n                    Mf::Cosh => \"cosh\",\n                    Mf::Sin => \"sin\",\n                    Mf::Sinh => \"sinh\",\n                    Mf::Tan => \"tan\",\n                    Mf::Tanh => \"tanh\",\n                    Mf::Acos => \"acos\",\n                    Mf::Asin => \"asin\",\n                    Mf::Atan => \"atan\",\n                    Mf::Asinh => \"asinh\",\n                    Mf::Acosh => \"acosh\",\n                    Mf::Atanh => \"atanh\",\n                    Mf::Radians => \"radians\",\n                    Mf::Degrees => \"degrees\",\n                    // glsl doesn't have atan2 function\n                    // use two-argument variation of the atan function\n                    Mf::Atan2 => \"atan\",\n                    // decomposition\n                    Mf::Ceil => \"ceil\",\n                    Mf::Floor => \"floor\",\n                    Mf::Round => \"roundEven\",\n                    Mf::Fract => \"fract\",\n                    Mf::Trunc => \"trunc\",\n                    Mf::Modf => MODF_FUNCTION,\n                    Mf::Frexp => FREXP_FUNCTION,\n                    Mf::Ldexp => \"ldexp\",\n                    // exponent\n                    Mf::Exp => \"exp\",\n                    Mf::Exp2 => \"exp2\",\n                    Mf::Log => \"log\",\n                    Mf::Log2 => \"log2\",\n                    Mf::Pow => \"pow\",\n                    // geometry\n                    Mf::Dot => match *ctx.resolve_type(arg, &self.module.types) {\n                        TypeInner::Vector {\n                            scalar:\n                                crate::Scalar {\n                                    kind: crate::ScalarKind::Float,\n                                    ..\n                                },\n                            ..\n                        } => \"dot\",\n                        TypeInner::Vector { size, .. } => {\n                            return self.write_dot_product(arg, arg1.unwrap(), size as usize, ctx)\n                        }\n                        _ => unreachable!(\n                            \"Correct TypeInner for dot product should be already validated\"\n                        ),\n                    },\n                    fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed) => {\n                        let conversion = match fun {\n                            Mf::Dot4I8Packed => \"int\",\n                            Mf::Dot4U8Packed => \"\",\n                            _ => unreachable!(),\n                        };\n\n                        let arg1 = arg1.unwrap();\n\n                        // Write parentheses around the dot product expression to prevent operators\n                        // with different precedences from applying earlier.\n                        write!(self.out, \"(\")?;\n                        for i in 0..4 {\n                            // Since `bitfieldExtract` only sign extends if the value is signed, we\n                            // need to convert the inputs to `int` in case of `Dot4I8Packed`. For\n                            // `Dot4U8Packed`, the code below only introduces parenthesis around\n                            // each factor, which aren't strictly needed because both operands are\n                            // baked, but which don't hurt either.\n                            write!(self.out, \"bitfieldExtract({conversion}(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \"), {}, 8)\", i * 8)?;\n\n                            write!(self.out, \" * bitfieldExtract({conversion}(\")?;\n                            self.write_expr(arg1, ctx)?;\n                            write!(self.out, \"), {}, 8)\", i * 8)?;\n\n                            if i != 3 {\n                                write!(self.out, \" + \")?;\n                            }\n                        }\n                        write!(self.out, \")\")?;\n\n                        return Ok(());\n                    }\n                    Mf::Outer => \"outerProduct\",\n                    Mf::Cross => \"cross\",\n                    Mf::Distance => \"distance\",\n                    Mf::Length => \"length\",\n                    Mf::Normalize => \"normalize\",\n                    Mf::FaceForward => \"faceforward\",\n                    Mf::Reflect => \"reflect\",\n                    Mf::Refract => \"refract\",\n                    // computational\n                    Mf::Sign => \"sign\",\n                    Mf::Fma => {\n                        if self.options.version.supports_fma_function() {\n                            // Use the fma function when available\n                            \"fma\"\n                        } else {\n                            // No fma support. Transform the function call into an arithmetic expression\n                            write!(self.out, \"(\")?;\n\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" * \")?;\n\n                            let arg1 =\n                                arg1.ok_or_else(|| Error::Custom(\"Missing fma arg1\".to_owned()))?;\n                            self.write_expr(arg1, ctx)?;\n                            write!(self.out, \" + \")?;\n\n                            let arg2 =\n                                arg2.ok_or_else(|| Error::Custom(\"Missing fma arg2\".to_owned()))?;\n                            self.write_expr(arg2, ctx)?;\n                            write!(self.out, \")\")?;\n\n                            return Ok(());\n                        }\n                    }\n                    Mf::Mix => \"mix\",\n                    Mf::Step => \"step\",\n                    Mf::SmoothStep => \"smoothstep\",\n                    Mf::Sqrt => \"sqrt\",\n                    Mf::InverseSqrt => \"inversesqrt\",\n                    Mf::Inverse => \"inverse\",\n                    Mf::Transpose => \"transpose\",\n                    Mf::Determinant => \"determinant\",\n                    Mf::QuantizeToF16 => match *ctx.resolve_type(arg, &self.module.types) {\n                        TypeInner::Scalar { .. } => {\n                            write!(self.out, \"unpackHalf2x16(packHalf2x16(vec2(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \"))).x\")?;\n                            return Ok(());\n                        }\n                        TypeInner::Vector {\n                            size: crate::VectorSize::Bi,\n                            ..\n                        } => {\n                            write!(self.out, \"unpackHalf2x16(packHalf2x16(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \"))\")?;\n                            return Ok(());\n                        }\n                        TypeInner::Vector {\n                            size: crate::VectorSize::Tri,\n                            ..\n                        } => {\n                            write!(self.out, \"vec3(unpackHalf2x16(packHalf2x16(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \".xy)), unpackHalf2x16(packHalf2x16(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \".zz)).x)\")?;\n                            return Ok(());\n                        }\n                        TypeInner::Vector {\n                            size: crate::VectorSize::Quad,\n                            ..\n                        } => {\n                            write!(self.out, \"vec4(unpackHalf2x16(packHalf2x16(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \".xy)), unpackHalf2x16(packHalf2x16(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \".zw)))\")?;\n                            return Ok(());\n                        }\n                        _ => unreachable!(\n                            \"Correct TypeInner for QuantizeToF16 should be already validated\"\n                        ),\n                    },\n                    // bits\n                    Mf::CountTrailingZeros => {\n                        match *ctx.resolve_type(arg, &self.module.types) {\n                            TypeInner::Vector { size, scalar, .. } => {\n                                let s = common::vector_size_str(size);\n                                if let crate::ScalarKind::Uint = scalar.kind {\n                                    write!(self.out, \"min(uvec{s}(findLSB(\")?;\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \")), uvec{s}(32u))\")?;\n                                } else {\n                                    write!(self.out, \"ivec{s}(min(uvec{s}(findLSB(\")?;\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \")), uvec{s}(32u)))\")?;\n                                }\n                            }\n                            TypeInner::Scalar(scalar) => {\n                                if let crate::ScalarKind::Uint = scalar.kind {\n                                    write!(self.out, \"min(uint(findLSB(\")?;\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \")), 32u)\")?;\n                                } else {\n                                    write!(self.out, \"int(min(uint(findLSB(\")?;\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \")), 32u))\")?;\n                                }\n                            }\n                            _ => unreachable!(),\n                        };\n                        return Ok(());\n                    }\n                    Mf::CountLeadingZeros => {\n                        if self.options.version.supports_integer_functions() {\n                            match *ctx.resolve_type(arg, &self.module.types) {\n                                TypeInner::Vector { size, scalar } => {\n                                    let s = common::vector_size_str(size);\n\n                                    if let crate::ScalarKind::Uint = scalar.kind {\n                                        write!(self.out, \"uvec{s}(ivec{s}(31) - findMSB(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \"))\")?;\n                                    } else {\n                                        write!(self.out, \"mix(ivec{s}(31) - findMSB(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \"), ivec{s}(0), lessThan(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \", ivec{s}(0)))\")?;\n                                    }\n                                }\n                                TypeInner::Scalar(scalar) => {\n                                    if let crate::ScalarKind::Uint = scalar.kind {\n                                        write!(self.out, \"uint(31 - findMSB(\")?;\n                                    } else {\n                                        write!(self.out, \"(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \" < 0 ? 0 : 31 - findMSB(\")?;\n                                    }\n\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \"))\")?;\n                                }\n                                _ => unreachable!(),\n                            };\n                        } else {\n                            match *ctx.resolve_type(arg, &self.module.types) {\n                                TypeInner::Vector { size, scalar } => {\n                                    let s = common::vector_size_str(size);\n\n                                    if let crate::ScalarKind::Uint = scalar.kind {\n                                        write!(self.out, \"uvec{s}(\")?;\n                                        write!(self.out, \"vec{s}(31.0) - floor(log2(vec{s}(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \") + 0.5)))\")?;\n                                    } else {\n                                        write!(self.out, \"ivec{s}(\")?;\n                                        write!(self.out, \"mix(vec{s}(31.0) - floor(log2(vec{s}(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \") + 0.5)), \")?;\n                                        write!(self.out, \"vec{s}(0.0), lessThan(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \", ivec{s}(0u))))\")?;\n                                    }\n                                }\n                                TypeInner::Scalar(scalar) => {\n                                    if let crate::ScalarKind::Uint = scalar.kind {\n                                        write!(self.out, \"uint(31.0 - floor(log2(float(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \") + 0.5)))\")?;\n                                    } else {\n                                        write!(self.out, \"(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \" < 0 ? 0 : int(\")?;\n                                        write!(self.out, \"31.0 - floor(log2(float(\")?;\n                                        self.write_expr(arg, ctx)?;\n                                        write!(self.out, \") + 0.5))))\")?;\n                                    }\n                                }\n                                _ => unreachable!(),\n                            };\n                        }\n\n                        return Ok(());\n                    }\n                    Mf::CountOneBits => \"bitCount\",\n                    Mf::ReverseBits => \"bitfieldReverse\",\n                    Mf::ExtractBits => {\n                        // The behavior of ExtractBits is undefined when offset + count > bit_width. We need\n                        // to first sanitize the offset and count first. If we don't do this, AMD and Intel chips\n                        // will return out-of-spec values if the extracted range is not within the bit width.\n                        //\n                        // This encodes the exact formula specified by the wgsl spec, without temporary values:\n                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin\n                        //\n                        // w = sizeof(x) * 8\n                        // o = min(offset, w)\n                        // c = min(count, w - o)\n                        //\n                        // bitfieldExtract(x, o, c)\n                        //\n                        // extract_bits(e, min(offset, w), min(count, w - min(offset, w))))\n                        let scalar_bits = ctx\n                            .resolve_type(arg, &self.module.types)\n                            .scalar_width()\n                            .unwrap()\n                            * 8;\n\n                        write!(self.out, \"bitfieldExtract(\")?;\n                        self.write_expr(arg, ctx)?;\n                        write!(self.out, \", int(min(\")?;\n                        self.write_expr(arg1.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u)), int(min(\",)?;\n                        self.write_expr(arg2.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u - min(\")?;\n                        self.write_expr(arg1.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u))))\")?;\n\n                        return Ok(());\n                    }\n                    Mf::InsertBits => {\n                        // InsertBits has the same considerations as ExtractBits above\n                        let scalar_bits = ctx\n                            .resolve_type(arg, &self.module.types)\n                            .scalar_width()\n                            .unwrap()\n                            * 8;\n\n                        write!(self.out, \"bitfieldInsert(\")?;\n                        self.write_expr(arg, ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(arg1.unwrap(), ctx)?;\n                        write!(self.out, \", int(min(\")?;\n                        self.write_expr(arg2.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u)), int(min(\",)?;\n                        self.write_expr(arg3.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u - min(\")?;\n                        self.write_expr(arg2.unwrap(), ctx)?;\n                        write!(self.out, \", {scalar_bits}u))))\")?;\n\n                        return Ok(());\n                    }\n                    Mf::FirstTrailingBit => \"findLSB\",\n                    Mf::FirstLeadingBit => \"findMSB\",\n                    // data packing\n                    Mf::Pack4x8snorm => {\n                        if self.options.version.supports_pack_unpack_4x8() {\n                            \"packSnorm4x8\"\n                        } else {\n                            // polyfill should go here. Needs a corresponding entry in `need_bake_expression`\n                            return Err(Error::UnsupportedExternal(\"packSnorm4x8\".into()));\n                        }\n                    }\n                    Mf::Pack4x8unorm => {\n                        if self.options.version.supports_pack_unpack_4x8() {\n                            \"packUnorm4x8\"\n                        } else {\n                            return Err(Error::UnsupportedExternal(\"packUnorm4x8\".to_owned()));\n                        }\n                    }\n                    Mf::Pack2x16snorm => {\n                        if self.options.version.supports_pack_unpack_snorm_2x16() {\n                            \"packSnorm2x16\"\n                        } else {\n                            return Err(Error::UnsupportedExternal(\"packSnorm2x16\".to_owned()));\n                        }\n                    }\n                    Mf::Pack2x16unorm => {\n                        if self.options.version.supports_pack_unpack_unorm_2x16() {\n                            \"packUnorm2x16\"\n                        } else {\n                            return Err(Error::UnsupportedExternal(\"packUnorm2x16\".to_owned()));\n                        }\n                    }\n                    Mf::Pack2x16float => {\n                        if self.options.version.supports_pack_unpack_half_2x16() {\n                            \"packHalf2x16\"\n                        } else {\n                            return Err(Error::UnsupportedExternal(\"packHalf2x16\".to_owned()));\n                        }\n                    }\n\n                    fun @ (Mf::Pack4xI8 | Mf::Pack4xU8 | Mf::Pack4xI8Clamp | Mf::Pack4xU8Clamp) => {\n                        let was_signed = matches!(fun, Mf::Pack4xI8 | Mf::Pack4xI8Clamp);\n                        let clamp_bounds = match fun {\n                            Mf::Pack4xI8Clamp => Some((\"-128\", \"127\")),\n                            Mf::Pack4xU8Clamp => Some((\"0\", \"255\")),\n                            _ => None,\n                        };\n                        let const_suffix = if was_signed { \"\" } else { \"u\" };\n                        if was_signed {\n                            write!(self.out, \"uint(\")?;\n                        }\n                        let write_arg = |this: &mut Self| -> BackendResult {\n                            if let Some((min, max)) = clamp_bounds {\n                                write!(this.out, \"clamp(\")?;\n                                this.write_expr(arg, ctx)?;\n                                write!(this.out, \", {min}{const_suffix}, {max}{const_suffix})\")?;\n                            } else {\n                                this.write_expr(arg, ctx)?;\n                            }\n                            Ok(())\n                        };\n                        write!(self.out, \"(\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[0] & 0xFF{const_suffix}) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[1] & 0xFF{const_suffix}) << 8) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[2] & 0xFF{const_suffix}) << 16) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[3] & 0xFF{const_suffix}) << 24)\")?;\n                        if was_signed {\n                            write!(self.out, \")\")?;\n                        }\n\n                        return Ok(());\n                    }\n                    // data unpacking\n                    Mf::Unpack2x16float => {\n                        if self.options.version.supports_pack_unpack_half_2x16() {\n                            \"unpackHalf2x16\"\n                        } else {\n                            return Err(Error::UnsupportedExternal(\"unpackHalf2x16\".into()));\n                        }\n                    }\n                    Mf::Unpack2x16snorm => {\n                        if self.options.version.supports_pack_unpack_snorm_2x16() {\n                            \"unpackSnorm2x16\"\n                        } else {\n                            let scale = 32767;\n\n                            write!(self.out, \"(vec2(ivec2(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" << 16, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \") >> 16) / {scale}.0)\")?;\n                            return Ok(());\n                        }\n                    }\n                    Mf::Unpack2x16unorm => {\n                        if self.options.version.supports_pack_unpack_unorm_2x16() {\n                            \"unpackUnorm2x16\"\n                        } else {\n                            let scale = 65535;\n\n                            write!(self.out, \"(vec2(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" & 0xFFFFu, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" >> 16) / {scale}.0)\")?;\n                            return Ok(());\n                        }\n                    }\n                    Mf::Unpack4x8snorm => {\n                        if self.options.version.supports_pack_unpack_4x8() {\n                            \"unpackSnorm4x8\"\n                        } else {\n                            let scale = 127;\n\n                            write!(self.out, \"(vec4(ivec4(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" << 24, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" << 16, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" << 8, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \") >> 24) / {scale}.0)\")?;\n                            return Ok(());\n                        }\n                    }\n                    Mf::Unpack4x8unorm => {\n                        if self.options.version.supports_pack_unpack_4x8() {\n                            \"unpackUnorm4x8\"\n                        } else {\n                            let scale = 255;\n\n                            write!(self.out, \"(vec4(\")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" & 0xFFu, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" >> 8 & 0xFFu, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" >> 16 & 0xFFu, \")?;\n                            self.write_expr(arg, ctx)?;\n                            write!(self.out, \" >> 24) / {scale}.0)\")?;\n                            return Ok(());\n                        }\n                    }\n                    fun @ (Mf::Unpack4xI8 | Mf::Unpack4xU8) => {\n                        let sign_prefix = match fun {\n                            Mf::Unpack4xI8 => 'i',\n                            Mf::Unpack4xU8 => 'u',\n                            _ => unreachable!(),\n                        };\n                        write!(self.out, \"{sign_prefix}vec4(\")?;\n                        for i in 0..4 {\n                            write!(self.out, \"bitfieldExtract(\")?;\n                            // Since bitfieldExtract only sign extends if the value is signed, this\n                            // cast is needed\n                            match fun {\n                                Mf::Unpack4xI8 => {\n                                    write!(self.out, \"int(\")?;\n                                    self.write_expr(arg, ctx)?;\n                                    write!(self.out, \")\")?;\n                                }\n                                Mf::Unpack4xU8 => self.write_expr(arg, ctx)?,\n                                _ => unreachable!(),\n                            };\n                            write!(self.out, \", {}, 8)\", i * 8)?;\n                            if i != 3 {\n                                write!(self.out, \", \")?;\n                            }\n                        }\n                        write!(self.out, \")\")?;\n\n                        return Ok(());\n                    }\n                };\n\n                let extract_bits = fun == Mf::ExtractBits;\n                let insert_bits = fun == Mf::InsertBits;\n\n                // Some GLSL functions always return signed integers (like findMSB),\n                // so they need to be cast to uint if the argument is also an uint.\n                let ret_might_need_int_to_uint = matches!(\n                    fun,\n                    Mf::FirstTrailingBit | Mf::FirstLeadingBit | Mf::CountOneBits | Mf::Abs\n                );\n\n                // Some GLSL functions only accept signed integers (like abs),\n                // so they need their argument cast from uint to int.\n                let arg_might_need_uint_to_int = matches!(fun, Mf::Abs);\n\n                // Check if the argument is an unsigned integer and return the vector size\n                // in case it's a vector\n                let maybe_uint_size = match *ctx.resolve_type(arg, &self.module.types) {\n                    TypeInner::Scalar(crate::Scalar {\n                        kind: crate::ScalarKind::Uint,\n                        ..\n                    }) => Some(None),\n                    TypeInner::Vector {\n                        scalar:\n                            crate::Scalar {\n                                kind: crate::ScalarKind::Uint,\n                                ..\n                            },\n                        size,\n                    } => Some(Some(size)),\n                    _ => None,\n                };\n\n                // Cast to uint if the function needs it\n                if ret_might_need_int_to_uint {\n                    if let Some(maybe_size) = maybe_uint_size {\n                        match maybe_size {\n                            Some(size) => write!(self.out, \"uvec{}(\", size as u8)?,\n                            None => write!(self.out, \"uint(\")?,\n                        }\n                    }\n                }\n\n                write!(self.out, \"{fun_name}(\")?;\n\n                // Cast to int if the function needs it\n                if arg_might_need_uint_to_int {\n                    if let Some(maybe_size) = maybe_uint_size {\n                        match maybe_size {\n                            Some(size) => write!(self.out, \"ivec{}(\", size as u8)?,\n                            None => write!(self.out, \"int(\")?,\n                        }\n                    }\n                }\n\n                self.write_expr(arg, ctx)?;\n\n                // Close the cast from uint to int\n                if arg_might_need_uint_to_int && maybe_uint_size.is_some() {\n                    write!(self.out, \")\")?\n                }\n\n                if let Some(arg) = arg1 {\n                    write!(self.out, \", \")?;\n                    if extract_bits {\n                        write!(self.out, \"int(\")?;\n                        self.write_expr(arg, ctx)?;\n                        write!(self.out, \")\")?;\n                    } else {\n                        self.write_expr(arg, ctx)?;\n                    }\n                }\n                if let Some(arg) = arg2 {\n                    write!(self.out, \", \")?;\n                    if extract_bits || insert_bits {\n                        write!(self.out, \"int(\")?;\n                        self.write_expr(arg, ctx)?;\n                        write!(self.out, \")\")?;\n                    } else {\n                        self.write_expr(arg, ctx)?;\n                    }\n                }\n                if let Some(arg) = arg3 {\n                    write!(self.out, \", \")?;\n                    if insert_bits {\n                        write!(self.out, \"int(\")?;\n                        self.write_expr(arg, ctx)?;\n                        write!(self.out, \")\")?;\n                    } else {\n                        self.write_expr(arg, ctx)?;\n                    }\n                }\n                write!(self.out, \")\")?;\n\n                // Close the cast from int to uint\n                if ret_might_need_int_to_uint && maybe_uint_size.is_some() {\n                    write!(self.out, \")\")?\n                }\n            }\n            // `As` is always a call.\n            // If `convert` is true the function name is the type\n            // Else the function name is one of the glsl provided bitcast functions\n            Expression::As {\n                expr,\n                kind: target_kind,\n                convert,\n            } => {\n                let inner = ctx.resolve_type(expr, &self.module.types);\n                match convert {\n                    Some(width) => {\n                        // this is similar to `write_type`, but with the target kind\n                        let scalar = glsl_scalar(crate::Scalar {\n                            kind: target_kind,\n                            width,\n                        })?;\n                        match *inner {\n                            TypeInner::Matrix { columns, rows, .. } => write!(\n                                self.out,\n                                \"{}mat{}x{}\",\n                                scalar.prefix, columns as u8, rows as u8\n                            )?,\n                            TypeInner::Vector { size, .. } => {\n                                write!(self.out, \"{}vec{}\", scalar.prefix, size as u8)?\n                            }\n                            _ => write!(self.out, \"{}\", scalar.full)?,\n                        }\n\n                        write!(self.out, \"(\")?;\n                        self.write_expr(expr, ctx)?;\n                        write!(self.out, \")\")?\n                    }\n                    None => {\n                        use crate::ScalarKind as Sk;\n\n                        let target_vector_type = match *inner {\n                            TypeInner::Vector { size, scalar } => Some(TypeInner::Vector {\n                                size,\n                                scalar: crate::Scalar {\n                                    kind: target_kind,\n                                    width: scalar.width,\n                                },\n                            }),\n                            _ => None,\n                        };\n\n                        let source_kind = inner.scalar_kind().unwrap();\n\n                        match (source_kind, target_kind, target_vector_type) {\n                            // No conversion needed\n                            (Sk::Sint, Sk::Sint, _)\n                            | (Sk::Uint, Sk::Uint, _)\n                            | (Sk::Float, Sk::Float, _)\n                            | (Sk::Bool, Sk::Bool, _) => {\n                                self.write_expr(expr, ctx)?;\n                                return Ok(());\n                            }\n\n                            // Cast to/from floats\n                            (Sk::Float, Sk::Sint, _) => write!(self.out, \"floatBitsToInt\")?,\n                            (Sk::Float, Sk::Uint, _) => write!(self.out, \"floatBitsToUint\")?,\n                            (Sk::Sint, Sk::Float, _) => write!(self.out, \"intBitsToFloat\")?,\n                            (Sk::Uint, Sk::Float, _) => write!(self.out, \"uintBitsToFloat\")?,\n\n                            // Cast between vector types\n                            (_, _, Some(vector)) => {\n                                self.write_value_type(&vector)?;\n                            }\n\n                            // There is no way to bitcast between Uint/Sint in glsl. Use constructor conversion\n                            (Sk::Uint | Sk::Bool, Sk::Sint, None) => write!(self.out, \"int\")?,\n                            (Sk::Sint | Sk::Bool, Sk::Uint, None) => write!(self.out, \"uint\")?,\n                            (Sk::Bool, Sk::Float, None) => write!(self.out, \"float\")?,\n                            (Sk::Sint | Sk::Uint | Sk::Float, Sk::Bool, None) => {\n                                write!(self.out, \"bool\")?\n                            }\n\n                            (Sk::AbstractInt | Sk::AbstractFloat, _, _)\n                            | (_, Sk::AbstractInt | Sk::AbstractFloat, _) => unreachable!(),\n                        };\n\n                        write!(self.out, \"(\")?;\n                        self.write_expr(expr, ctx)?;\n                        write!(self.out, \")\")?;\n                    }\n                }\n            }\n            // These expressions never show up in `Emit`.\n            Expression::CallResult(_)\n            | Expression::AtomicResult { .. }\n            | Expression::RayQueryProceedResult\n            | Expression::WorkGroupUniformLoadResult { .. }\n            | Expression::SubgroupOperationResult { .. }\n            | Expression::SubgroupBallotResult => unreachable!(),\n            // `ArrayLength` is written as `expr.length()` and we convert it to a uint\n            Expression::ArrayLength(expr) => {\n                write!(self.out, \"uint(\")?;\n                self.write_expr(expr, ctx)?;\n                write!(self.out, \".length())\")?\n            }\n            // not supported yet\n            Expression::RayQueryGetIntersection { .. }\n            | Expression::RayQueryVertexPositions { .. }\n            | Expression::CooperativeLoad { .. }\n            | Expression::CooperativeMultiplyAdd { .. } => unreachable!(),\n        }\n\n        Ok(())\n    }\n\n    /// Helper function to write the local holding the clamped lod\n    fn write_clamped_lod(\n        &mut self,\n        ctx: &back::FunctionCtx,\n        expr: Handle<crate::Expression>,\n        image: Handle<crate::Expression>,\n        level_expr: Handle<crate::Expression>,\n    ) -> Result<(), Error> {\n        // Define our local and start a call to `clamp`\n        write!(\n            self.out,\n            \"int {}{} = clamp(\",\n            Baked(expr),\n            CLAMPED_LOD_SUFFIX\n        )?;\n        // Write the lod that will be clamped\n        self.write_expr(level_expr, ctx)?;\n        // Set the min value to 0 and start a call to `textureQueryLevels` to get\n        // the maximum value\n        write!(self.out, \", 0, textureQueryLevels(\")?;\n        // Write the target image as an argument to `textureQueryLevels`\n        self.write_expr(image, ctx)?;\n        // Close the call to `textureQueryLevels` subtract 1 from it since\n        // the lod argument is 0 based, close the `clamp` call and end the\n        // local declaration statement.\n        writeln!(self.out, \") - 1);\")?;\n\n        Ok(())\n    }\n\n    // Helper method used to retrieve how many elements a coordinate vector\n    // for the images operations need.\n    fn get_coordinate_vector_size(&self, dim: crate::ImageDimension, arrayed: bool) -> u8 {\n        // openGL es doesn't have 1D images so we need workaround it\n        let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();\n        // Get how many components the coordinate vector needs for the dimensions only\n        let tex_coord_size = match dim {\n            crate::ImageDimension::D1 => 1,\n            crate::ImageDimension::D2 => 2,\n            crate::ImageDimension::D3 => 3,\n            crate::ImageDimension::Cube => 2,\n        };\n        // Calculate the true size of the coordinate vector by adding 1 for arrayed images\n        // and another 1 if we need to workaround 1D images by making them 2D\n        tex_coord_size + tex_1d_hack as u8 + arrayed as u8\n    }\n\n    /// Helper method to write the coordinate vector for image operations\n    fn write_texture_coord(\n        &mut self,\n        ctx: &back::FunctionCtx,\n        vector_size: u8,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        // Emulate 1D images as 2D for profiles that don't support it (glsl es)\n        tex_1d_hack: bool,\n    ) -> Result<(), Error> {\n        match array_index {\n            // If the image needs an array indice we need to add it to the end of our\n            // coordinate vector, to do so we will use the `ivec(ivec, scalar)`\n            // constructor notation (NOTE: the inner `ivec` can also be a scalar, this\n            // is important for 1D arrayed images).\n            Some(layer_expr) => {\n                write!(self.out, \"ivec{vector_size}(\")?;\n                self.write_expr(coordinate, ctx)?;\n                write!(self.out, \", \")?;\n                // If we are replacing sampler1D with sampler2D we also need\n                // to add another zero to the coordinates vector for the y component\n                if tex_1d_hack {\n                    write!(self.out, \"0, \")?;\n                }\n                self.write_expr(layer_expr, ctx)?;\n                write!(self.out, \")\")?;\n            }\n            // Otherwise write just the expression (and the 1D hack if needed)\n            None => {\n                let uvec_size = match *ctx.resolve_type(coordinate, &self.module.types) {\n                    TypeInner::Scalar(crate::Scalar {\n                        kind: crate::ScalarKind::Uint,\n                        ..\n                    }) => Some(None),\n                    TypeInner::Vector {\n                        size,\n                        scalar:\n                            crate::Scalar {\n                                kind: crate::ScalarKind::Uint,\n                                ..\n                            },\n                    } => Some(Some(size as u32)),\n                    _ => None,\n                };\n                if tex_1d_hack {\n                    write!(self.out, \"ivec2(\")?;\n                } else if uvec_size.is_some() {\n                    match uvec_size {\n                        Some(None) => write!(self.out, \"int(\")?,\n                        Some(Some(size)) => write!(self.out, \"ivec{size}(\")?,\n                        _ => {}\n                    }\n                }\n                self.write_expr(coordinate, ctx)?;\n                if tex_1d_hack {\n                    write!(self.out, \", 0)\")?;\n                } else if uvec_size.is_some() {\n                    write!(self.out, \")\")?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Helper method to write the `ImageStore` statement\n    fn write_image_store(\n        &mut self,\n        ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        value: Handle<crate::Expression>,\n    ) -> Result<(), Error> {\n        use crate::ImageDimension as IDim;\n\n        // NOTE: openGL requires that `imageStore`s have no effects when the texel is invalid\n        // so we don't need to generate bounds checks (OpenGL 4.2 Core §3.9.20)\n\n        // This will only panic if the module is invalid\n        let dim = match *ctx.resolve_type(image, &self.module.types) {\n            TypeInner::Image { dim, .. } => dim,\n            _ => unreachable!(),\n        };\n\n        // Begin our call to `imageStore`\n        write!(self.out, \"imageStore(\")?;\n        self.write_expr(image, ctx)?;\n        // Separate the image argument from the coordinates\n        write!(self.out, \", \")?;\n\n        // openGL es doesn't have 1D images so we need workaround it\n        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();\n        // Write the coordinate vector\n        self.write_texture_coord(\n            ctx,\n            // Get the size of the coordinate vector\n            self.get_coordinate_vector_size(dim, array_index.is_some()),\n            coordinate,\n            array_index,\n            tex_1d_hack,\n        )?;\n\n        // Separate the coordinate from the value to write and write the expression\n        // of the value to write.\n        write!(self.out, \", \")?;\n        self.write_expr(value, ctx)?;\n        // End the call to `imageStore` and the statement.\n        writeln!(self.out, \");\")?;\n\n        Ok(())\n    }\n\n    /// Helper method to write the `ImageAtomic` statement\n    fn write_image_atomic(\n        &mut self,\n        ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        fun: crate::AtomicFunction,\n        value: Handle<crate::Expression>,\n    ) -> Result<(), Error> {\n        use crate::ImageDimension as IDim;\n\n        // NOTE: openGL requires that `imageAtomic`s have no effects when the texel is invalid\n        // so we don't need to generate bounds checks (OpenGL 4.2 Core §3.9.20)\n\n        // This will only panic if the module is invalid\n        let dim = match *ctx.resolve_type(image, &self.module.types) {\n            TypeInner::Image { dim, .. } => dim,\n            _ => unreachable!(),\n        };\n\n        // Begin our call to `imageAtomic`\n        let fun_str = fun.to_glsl();\n        write!(self.out, \"imageAtomic{fun_str}(\")?;\n        self.write_expr(image, ctx)?;\n        // Separate the image argument from the coordinates\n        write!(self.out, \", \")?;\n\n        // openGL es doesn't have 1D images so we need workaround it\n        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();\n        // Write the coordinate vector\n        self.write_texture_coord(\n            ctx,\n            // Get the size of the coordinate vector\n            self.get_coordinate_vector_size(dim, false),\n            coordinate,\n            array_index,\n            tex_1d_hack,\n        )?;\n\n        // Separate the coordinate from the value to write and write the expression\n        // of the value to write.\n        write!(self.out, \", \")?;\n        self.write_expr(value, ctx)?;\n        // End the call to `imageAtomic` and the statement.\n        writeln!(self.out, \");\")?;\n\n        Ok(())\n    }\n\n    /// Helper method for writing an `ImageLoad` expression.\n    #[allow(clippy::too_many_arguments)]\n    fn write_image_load(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        sample: Option<Handle<crate::Expression>>,\n        level: Option<Handle<crate::Expression>>,\n    ) -> Result<(), Error> {\n        use crate::ImageDimension as IDim;\n\n        // `ImageLoad` is a bit complicated.\n        // There are two functions one for sampled\n        // images another for storage images, the former uses `texelFetch` and the\n        // latter uses `imageLoad`.\n        //\n        // Furthermore we have `level` which is always `Some` for sampled images\n        // and `None` for storage images, so we end up with two functions:\n        // - `texelFetch(image, coordinate, level)` for sampled images\n        // - `imageLoad(image, coordinate)` for storage images\n        //\n        // Finally we also have to consider bounds checking, for storage images\n        // this is easy since openGL requires that invalid texels always return\n        // 0, for sampled images we need to either verify that all arguments are\n        // in bounds (`ReadZeroSkipWrite`) or make them a valid texel (`Restrict`).\n\n        // This will only panic if the module is invalid\n        let (dim, class) = match *ctx.resolve_type(image, &self.module.types) {\n            TypeInner::Image {\n                dim,\n                arrayed: _,\n                class,\n            } => (dim, class),\n            _ => unreachable!(),\n        };\n\n        // Get the name of the function to be used for the load operation\n        // and the policy to be used with it.\n        let (fun_name, policy) = match class {\n            // Sampled images inherit the policy from the user passed policies\n            crate::ImageClass::Sampled { .. } => (\"texelFetch\", self.policies.image_load),\n            crate::ImageClass::Storage { .. } => {\n                // OpenGL ES 3.1 mentions in Chapter \"8.22 Texture Image Loads and Stores\" that:\n                // \"Invalid image loads will return a vector where the value of R, G, and B components\n                // is 0 and the value of the A component is undefined.\"\n                //\n                // OpenGL 4.2 Core mentions in Chapter \"3.9.20 Texture Image Loads and Stores\" that:\n                // \"Invalid image loads will return zero.\"\n                //\n                // So, we only inject bounds checks for ES\n                let policy = if self.options.version.is_es() {\n                    self.policies.image_load\n                } else {\n                    proc::BoundsCheckPolicy::Unchecked\n                };\n                (\"imageLoad\", policy)\n            }\n            // TODO: Is there even a function for this?\n            crate::ImageClass::Depth { multi: _ } => {\n                return Err(Error::Custom(\n                    \"WGSL `textureLoad` from depth textures is not supported in GLSL\".to_string(),\n                ))\n            }\n            crate::ImageClass::External => unimplemented!(),\n        };\n\n        // openGL es doesn't have 1D images so we need workaround it\n        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();\n        // Get the size of the coordinate vector\n        let vector_size = self.get_coordinate_vector_size(dim, array_index.is_some());\n\n        if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {\n            // To write the bounds checks for `ReadZeroSkipWrite` we will use a\n            // ternary operator since we are in the middle of an expression and\n            // need to return a value.\n            //\n            // NOTE: glsl does short circuit when evaluating logical\n            // expressions so we can be sure that after we test a\n            // condition it will be true for the next ones\n\n            // Write parentheses around the ternary operator to prevent problems with\n            // expressions emitted before or after it having more precedence\n            write!(self.out, \"(\",)?;\n\n            // The lod check needs to precede the size check since we need\n            // to use the lod to get the size of the image at that level.\n            if let Some(level_expr) = level {\n                self.write_expr(level_expr, ctx)?;\n                write!(self.out, \" < textureQueryLevels(\",)?;\n                self.write_expr(image, ctx)?;\n                // Chain the next check\n                write!(self.out, \") && \")?;\n            }\n\n            // Check that the sample arguments doesn't exceed the number of samples\n            if let Some(sample_expr) = sample {\n                self.write_expr(sample_expr, ctx)?;\n                write!(self.out, \" < textureSamples(\",)?;\n                self.write_expr(image, ctx)?;\n                // Chain the next check\n                write!(self.out, \") && \")?;\n            }\n\n            // We now need to write the size checks for the coordinates and array index\n            // first we write the comparison function in case the image is 1D non arrayed\n            // (and no 1D to 2D hack was needed) we are comparing scalars so the less than\n            // operator will suffice, but otherwise we'll be comparing two vectors so we'll\n            // need to use the `lessThan` function but it returns a vector of booleans (one\n            // for each comparison) so we need to fold it all in one scalar boolean, since\n            // we want all comparisons to pass we use the `all` function which will only\n            // return `true` if all the elements of the boolean vector are also `true`.\n            //\n            // So we'll end with one of the following forms\n            // - `coord < textureSize(image, lod)` for 1D images\n            // - `all(lessThan(coord, textureSize(image, lod)))` for normal images\n            // - `all(lessThan(ivec(coord, array_index), textureSize(image, lod)))`\n            //    for arrayed images\n            // - `all(lessThan(coord, textureSize(image)))` for multi sampled images\n\n            if vector_size != 1 {\n                write!(self.out, \"all(lessThan(\")?;\n            }\n\n            // Write the coordinate vector\n            self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;\n\n            if vector_size != 1 {\n                // If we used the `lessThan` function we need to separate the\n                // coordinates from the image size.\n                write!(self.out, \", \")?;\n            } else {\n                // If we didn't use it (ie. 1D images) we perform the comparison\n                // using the less than operator.\n                write!(self.out, \" < \")?;\n            }\n\n            // Call `textureSize` to get our image size\n            write!(self.out, \"textureSize(\")?;\n            self.write_expr(image, ctx)?;\n            // `textureSize` uses the lod as a second argument for mipmapped images\n            if let Some(level_expr) = level {\n                // Separate the image from the lod\n                write!(self.out, \", \")?;\n                self.write_expr(level_expr, ctx)?;\n            }\n            // Close the `textureSize` call\n            write!(self.out, \")\")?;\n\n            if vector_size != 1 {\n                // Close the `all` and `lessThan` calls\n                write!(self.out, \"))\")?;\n            }\n\n            // Finally end the condition part of the ternary operator\n            write!(self.out, \" ? \")?;\n        }\n\n        // Begin the call to the function used to load the texel\n        write!(self.out, \"{fun_name}(\")?;\n        self.write_expr(image, ctx)?;\n        write!(self.out, \", \")?;\n\n        // If we are using `Restrict` bounds checking we need to pass valid texel\n        // coordinates, to do so we use the `clamp` function to get a value between\n        // 0 and the image size - 1 (indexing begins at 0)\n        if let proc::BoundsCheckPolicy::Restrict = policy {\n            write!(self.out, \"clamp(\")?;\n        }\n\n        // Write the coordinate vector\n        self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;\n\n        // If we are using `Restrict` bounds checking we need to write the rest of the\n        // clamp we initiated before writing the coordinates.\n        if let proc::BoundsCheckPolicy::Restrict = policy {\n            // Write the min value 0\n            if vector_size == 1 {\n                write!(self.out, \", 0\")?;\n            } else {\n                write!(self.out, \", ivec{vector_size}(0)\")?;\n            }\n            // Start the `textureSize` call to use as the max value.\n            write!(self.out, \", textureSize(\")?;\n            self.write_expr(image, ctx)?;\n            // If the image is mipmapped we need to add the lod argument to the\n            // `textureSize` call, but this needs to be the clamped lod, this should\n            // have been generated earlier and put in a local.\n            if class.is_mipmapped() {\n                write!(self.out, \", {}{}\", Baked(handle), CLAMPED_LOD_SUFFIX)?;\n            }\n            // Close the `textureSize` call\n            write!(self.out, \")\")?;\n\n            // Subtract 1 from the `textureSize` call since the coordinates are zero based.\n            if vector_size == 1 {\n                write!(self.out, \" - 1\")?;\n            } else {\n                write!(self.out, \" - ivec{vector_size}(1)\")?;\n            }\n\n            // Close the `clamp` call\n            write!(self.out, \")\")?;\n\n            // Add the clamped lod (if present) as the second argument to the\n            // image load function.\n            if level.is_some() {\n                write!(self.out, \", {}{}\", Baked(handle), CLAMPED_LOD_SUFFIX)?;\n            }\n\n            // If a sample argument is needed we need to clamp it between 0 and\n            // the number of samples the image has.\n            if let Some(sample_expr) = sample {\n                write!(self.out, \", clamp(\")?;\n                self.write_expr(sample_expr, ctx)?;\n                // Set the min value to 0 and start the call to `textureSamples`\n                write!(self.out, \", 0, textureSamples(\")?;\n                self.write_expr(image, ctx)?;\n                // Close the `textureSamples` call, subtract 1 from it since the sample\n                // argument is zero based, and close the `clamp` call\n                writeln!(self.out, \") - 1)\")?;\n            }\n        } else if let Some(sample_or_level) = sample.or(level) {\n            // GLSL only support SInt on this field while WGSL support also UInt\n            let cast_to_int = matches!(\n                *ctx.resolve_type(sample_or_level, &self.module.types),\n                TypeInner::Scalar(crate::Scalar {\n                    kind: crate::ScalarKind::Uint,\n                    ..\n                })\n            );\n\n            // If no bounds checking is need just add the sample or level argument\n            // after the coordinates\n            write!(self.out, \", \")?;\n\n            if cast_to_int {\n                write!(self.out, \"int(\")?;\n            }\n\n            self.write_expr(sample_or_level, ctx)?;\n\n            if cast_to_int {\n                write!(self.out, \")\")?;\n            }\n        }\n\n        // Close the image load function.\n        write!(self.out, \")\")?;\n\n        // If we were using the `ReadZeroSkipWrite` policy we need to end the first branch\n        // (which is taken if the condition is `true`) with a colon (`:`) and write the\n        // second branch which is just a 0 value.\n        if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {\n            // Get the kind of the output value.\n            let kind = match class {\n                // Only sampled images can reach here since storage images\n                // don't need bounds checks and depth images aren't implemented\n                crate::ImageClass::Sampled { kind, .. } => kind,\n                _ => unreachable!(),\n            };\n\n            // End the first branch\n            write!(self.out, \" : \")?;\n            // Write the 0 value\n            write!(\n                self.out,\n                \"{}vec4(\",\n                glsl_scalar(crate::Scalar { kind, width: 4 })?.prefix,\n            )?;\n            self.write_zero_init_scalar(kind)?;\n            // Close the zero value constructor\n            write!(self.out, \")\")?;\n            // Close the parentheses surrounding our ternary\n            write!(self.out, \")\")?;\n        }\n\n        Ok(())\n    }\n\n    fn write_named_expr(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        name: String,\n        // The expression which is being named.\n        // Generally, this is the same as handle, except in WorkGroupUniformLoad\n        named: Handle<crate::Expression>,\n        ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        match ctx.info[named].ty {\n            proc::TypeResolution::Handle(ty_handle) => match self.module.types[ty_handle].inner {\n                TypeInner::Struct { .. } => {\n                    let ty_name = &self.names[&NameKey::Type(ty_handle)];\n                    write!(self.out, \"{ty_name}\")?;\n                }\n                _ => {\n                    self.write_type(ty_handle)?;\n                }\n            },\n            proc::TypeResolution::Value(ref inner) => {\n                self.write_value_type(inner)?;\n            }\n        }\n\n        let resolved = ctx.resolve_type(named, &self.module.types);\n\n        write!(self.out, \" {name}\")?;\n        if let TypeInner::Array { base, size, .. } = *resolved {\n            self.write_array_size(base, size)?;\n        }\n        write!(self.out, \" = \")?;\n        self.write_expr(handle, ctx)?;\n        writeln!(self.out, \";\")?;\n        self.named_expressions.insert(named, name);\n\n        Ok(())\n    }\n\n    /// Helper function that write string with default zero initialization for supported types\n    fn write_zero_init_value(&mut self, ty: Handle<crate::Type>) -> BackendResult {\n        let inner = &self.module.types[ty].inner;\n        match *inner {\n            TypeInner::Scalar(scalar) | TypeInner::Atomic(scalar) => {\n                self.write_zero_init_scalar(scalar.kind)?;\n            }\n            TypeInner::Vector { scalar, .. } => {\n                self.write_value_type(inner)?;\n                write!(self.out, \"(\")?;\n                self.write_zero_init_scalar(scalar.kind)?;\n                write!(self.out, \")\")?;\n            }\n            TypeInner::Matrix { .. } => {\n                self.write_value_type(inner)?;\n                write!(self.out, \"(\")?;\n                self.write_zero_init_scalar(crate::ScalarKind::Float)?;\n                write!(self.out, \")\")?;\n            }\n            TypeInner::Array { base, size, .. } => {\n                let count = match size.resolve(self.module.to_ctx())? {\n                    proc::IndexableLength::Known(count) => count,\n                    proc::IndexableLength::Dynamic => return Ok(()),\n                };\n                self.write_type(base)?;\n                self.write_array_size(base, size)?;\n                write!(self.out, \"(\")?;\n                for _ in 1..count {\n                    self.write_zero_init_value(base)?;\n                    write!(self.out, \", \")?;\n                }\n                // write last parameter without comma and space\n                self.write_zero_init_value(base)?;\n                write!(self.out, \")\")?;\n            }\n            TypeInner::Struct { ref members, .. } => {\n                let name = &self.names[&NameKey::Type(ty)];\n                write!(self.out, \"{name}(\")?;\n                for (index, member) in members.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    self.write_zero_init_value(member.ty)?;\n                }\n                write!(self.out, \")\")?;\n            }\n            _ => unreachable!(),\n        }\n\n        Ok(())\n    }\n\n    /// Helper function that write string with zero initialization for scalar\n    fn write_zero_init_scalar(&mut self, kind: crate::ScalarKind) -> BackendResult {\n        match kind {\n            crate::ScalarKind::Bool => write!(self.out, \"false\")?,\n            crate::ScalarKind::Uint => write!(self.out, \"0u\")?,\n            crate::ScalarKind::Float => write!(self.out, \"0.0\")?,\n            crate::ScalarKind::Sint => write!(self.out, \"0\")?,\n            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {\n                return Err(Error::Custom(\n                    \"Abstract types should not appear in IR presented to backends\".to_string(),\n                ))\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Issue a control barrier.\n    fn write_control_barrier(\n        &mut self,\n        flags: crate::Barrier,\n        level: back::Level,\n    ) -> BackendResult {\n        self.write_memory_barrier(flags, level)?;\n        writeln!(self.out, \"{level}barrier();\")?;\n        Ok(())\n    }\n\n    /// Issue a memory barrier.\n    fn write_memory_barrier(&mut self, flags: crate::Barrier, level: back::Level) -> BackendResult {\n        if flags.contains(crate::Barrier::STORAGE) {\n            writeln!(self.out, \"{level}memoryBarrierBuffer();\")?;\n        }\n        if flags.contains(crate::Barrier::WORK_GROUP) {\n            writeln!(self.out, \"{level}memoryBarrierShared();\")?;\n        }\n        if flags.contains(crate::Barrier::SUB_GROUP) {\n            writeln!(self.out, \"{level}subgroupMemoryBarrier();\")?;\n        }\n        if flags.contains(crate::Barrier::TEXTURE) {\n            writeln!(self.out, \"{level}memoryBarrierImage();\")?;\n        }\n        Ok(())\n    }\n\n    /// Helper function that return the glsl storage access string of [`StorageAccess`](crate::StorageAccess)\n    ///\n    /// glsl allows adding both `readonly` and `writeonly` but this means that\n    /// they can only be used to query information about the resource which isn't what\n    /// we want here so when storage access is both `LOAD` and `STORE` add no modifiers\n    fn write_storage_access(&mut self, storage_access: crate::StorageAccess) -> BackendResult {\n        if storage_access.contains(crate::StorageAccess::ATOMIC) {\n            return Ok(());\n        }\n        if !storage_access.contains(crate::StorageAccess::STORE) {\n            write!(self.out, \"readonly \")?;\n        }\n        if !storage_access.contains(crate::StorageAccess::LOAD) {\n            write!(self.out, \"writeonly \")?;\n        }\n        Ok(())\n    }\n\n    /// Helper method used to produce the reflection info that's returned to the user\n    fn collect_reflection_info(&mut self) -> Result<ReflectionInfo, Error> {\n        let info = self.info.get_entry_point(self.entry_point_idx as usize);\n        let mut texture_mapping = crate::FastHashMap::default();\n        let mut uniforms = crate::FastHashMap::default();\n\n        for sampling in info.sampling_set.iter() {\n            let tex_name = self.reflection_names_globals[&sampling.image].clone();\n\n            match texture_mapping.entry(tex_name) {\n                hash_map::Entry::Vacant(v) => {\n                    v.insert(TextureMapping {\n                        texture: sampling.image,\n                        sampler: Some(sampling.sampler),\n                    });\n                }\n                hash_map::Entry::Occupied(e) => {\n                    if e.get().sampler != Some(sampling.sampler) {\n                        log::error!(\"Conflicting samplers for {}\", e.key());\n                        return Err(Error::ImageMultipleSamplers);\n                    }\n                }\n            }\n        }\n\n        let mut immediates_info = None;\n        for (handle, var) in self.module.global_variables.iter() {\n            if info[handle].is_empty() {\n                continue;\n            }\n            match self.module.types[var.ty].inner {\n                TypeInner::Image { .. } => {\n                    let tex_name = self.reflection_names_globals[&handle].clone();\n                    match texture_mapping.entry(tex_name) {\n                        hash_map::Entry::Vacant(v) => {\n                            v.insert(TextureMapping {\n                                texture: handle,\n                                sampler: None,\n                            });\n                        }\n                        hash_map::Entry::Occupied(_) => {\n                            // already used with a sampler, do nothing\n                        }\n                    }\n                }\n                _ => match var.space {\n                    crate::AddressSpace::Uniform | crate::AddressSpace::Storage { .. } => {\n                        let name = self.reflection_names_globals[&handle].clone();\n                        uniforms.insert(handle, name);\n                    }\n                    crate::AddressSpace::Immediate => {\n                        let name = self.reflection_names_globals[&handle].clone();\n                        immediates_info = Some((name, var.ty));\n                    }\n                    _ => (),\n                },\n            }\n        }\n\n        let mut immediates_segments = Vec::new();\n        let mut immediates_items = vec![];\n\n        if let Some((name, ty)) = immediates_info {\n            // We don't have a layouter available to us, so we need to create one.\n            //\n            // This is potentially a bit wasteful, but the set of types in the program\n            // shouldn't be too large.\n            let mut layouter = proc::Layouter::default();\n            layouter.update(self.module.to_ctx()).unwrap();\n\n            // We start with the name of the binding itself.\n            immediates_segments.push(name);\n\n            // We then recursively collect all the uniform fields of the immediate data.\n            self.collect_immediates_items(\n                ty,\n                &mut immediates_segments,\n                &layouter,\n                &mut 0,\n                &mut immediates_items,\n            );\n        }\n\n        Ok(ReflectionInfo {\n            texture_mapping,\n            uniforms,\n            varying: mem::take(&mut self.varying),\n            immediates_items,\n            clip_distance_count: self.clip_distance_count,\n        })\n    }\n\n    fn collect_immediates_items(\n        &mut self,\n        ty: Handle<crate::Type>,\n        segments: &mut Vec<String>,\n        layouter: &proc::Layouter,\n        offset: &mut u32,\n        items: &mut Vec<ImmediateItem>,\n    ) {\n        // At this point in the recursion, `segments` contains the path\n        // needed to access `ty` from the root.\n\n        let layout = &layouter[ty];\n        *offset = layout.alignment.round_up(*offset);\n        match self.module.types[ty].inner {\n            // All these types map directly to GL uniforms.\n            TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Matrix { .. } => {\n                // Build the full name, by combining all current segments.\n                let name: String = segments.iter().map(String::as_str).collect();\n                items.push(ImmediateItem {\n                    access_path: name,\n                    offset: *offset,\n                    ty,\n                });\n                *offset += layout.size;\n            }\n            // Arrays are recursed into.\n            TypeInner::Array { base, size, .. } => {\n                let crate::ArraySize::Constant(count) = size else {\n                    unreachable!(\"Cannot have dynamic arrays in immediates\");\n                };\n\n                for i in 0..count.get() {\n                    // Add the array accessor and recurse.\n                    segments.push(format!(\"[{i}]\"));\n                    self.collect_immediates_items(base, segments, layouter, offset, items);\n                    segments.pop();\n                }\n\n                // Ensure the stride is kept by rounding up to the alignment.\n                *offset = layout.alignment.round_up(*offset)\n            }\n            TypeInner::Struct { ref members, .. } => {\n                for (index, member) in members.iter().enumerate() {\n                    // Add struct accessor and recurse.\n                    segments.push(format!(\n                        \".{}\",\n                        self.names[&NameKey::StructMember(ty, index as u32)]\n                    ));\n                    self.collect_immediates_items(member.ty, segments, layouter, offset, items);\n                    segments.pop();\n                }\n\n                // Ensure ending padding is kept by rounding up to the alignment.\n                *offset = layout.alignment.round_up(*offset)\n            }\n            _ => unreachable!(),\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/conv.rs",
    "content": "use crate::common;\n\nuse alloc::{borrow::Cow, format, string::String};\n\nuse super::Error;\nuse crate::proc::Alignment;\n\nimpl crate::ScalarKind {\n    pub(super) fn to_hlsl_cast(self) -> &'static str {\n        match self {\n            Self::Float => \"asfloat\",\n            Self::Sint => \"asint\",\n            Self::Uint => \"asuint\",\n            Self::Bool | Self::AbstractInt | Self::AbstractFloat => unreachable!(),\n        }\n    }\n}\n\nimpl crate::Scalar {\n    /// Helper function that returns scalar related strings\n    ///\n    /// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-scalar>\n    pub(super) const fn to_hlsl_str(self) -> Result<&'static str, Error> {\n        match self.kind {\n            crate::ScalarKind::Sint => match self.width {\n                4 => Ok(\"int\"),\n                8 => Ok(\"int64_t\"),\n                _ => Err(Error::UnsupportedScalar(self)),\n            },\n            crate::ScalarKind::Uint => match self.width {\n                4 => Ok(\"uint\"),\n                8 => Ok(\"uint64_t\"),\n                _ => Err(Error::UnsupportedScalar(self)),\n            },\n            crate::ScalarKind::Float => match self.width {\n                2 => Ok(\"half\"),\n                4 => Ok(\"float\"),\n                8 => Ok(\"double\"),\n                _ => Err(Error::UnsupportedScalar(self)),\n            },\n            crate::ScalarKind::Bool => Ok(\"bool\"),\n            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {\n                Err(Error::UnsupportedScalar(self))\n            }\n        }\n    }\n}\n\nimpl crate::TypeInner {\n    pub(super) const fn is_matrix(&self) -> bool {\n        match *self {\n            Self::Matrix { .. } => true,\n            _ => false,\n        }\n    }\n\n    pub(super) fn size_hlsl(&self, gctx: crate::proc::GlobalCtx) -> Result<u32, Error> {\n        match *self {\n            Self::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                let stride = Alignment::from(rows) * scalar.width as u32;\n                let last_row_size = rows as u32 * scalar.width as u32;\n                Ok(((columns as u32 - 1) * stride) + last_row_size)\n            }\n            Self::Array { base, size, stride } => {\n                let count = match size.resolve(gctx)? {\n                    crate::proc::IndexableLength::Known(size) => size,\n                    // A dynamically-sized array has to have at least one element\n                    crate::proc::IndexableLength::Dynamic => 1,\n                };\n                let last_el_size = gctx.types[base].inner.size_hlsl(gctx)?;\n                Ok(((count - 1) * stride) + last_el_size)\n            }\n            _ => Ok(self.size(gctx)),\n        }\n    }\n\n    /// Used to generate the name of the wrapped type constructor\n    pub(super) fn hlsl_type_id<'a>(\n        base: crate::Handle<crate::Type>,\n        gctx: crate::proc::GlobalCtx,\n        names: &'a crate::FastHashMap<crate::proc::NameKey, String>,\n    ) -> Result<Cow<'a, str>, Error> {\n        Ok(match gctx.types[base].inner {\n            crate::TypeInner::Scalar(scalar) => Cow::Borrowed(scalar.to_hlsl_str()?),\n            crate::TypeInner::Vector { size, scalar } => Cow::Owned(format!(\n                \"{}{}\",\n                scalar.to_hlsl_str()?,\n                common::vector_size_str(size)\n            )),\n            crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => Cow::Owned(format!(\n                \"{}{}x{}\",\n                scalar.to_hlsl_str()?,\n                common::vector_size_str(columns),\n                common::vector_size_str(rows),\n            )),\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                ..\n            } => Cow::Owned(format!(\n                \"array{size}_{}_\",\n                Self::hlsl_type_id(base, gctx, names)?\n            )),\n            crate::TypeInner::Struct { .. } => {\n                Cow::Borrowed(&names[&crate::proc::NameKey::Type(base)])\n            }\n            _ => unreachable!(),\n        })\n    }\n}\n\nimpl crate::StorageFormat {\n    pub(super) const fn to_hlsl_str(self) -> &'static str {\n        match self {\n            Self::R16Float | Self::R32Float => \"float\",\n            Self::R8Unorm | Self::R16Unorm => \"unorm float\",\n            Self::R8Snorm | Self::R16Snorm => \"snorm float\",\n            Self::R8Uint | Self::R16Uint | Self::R32Uint => \"uint\",\n            Self::R8Sint | Self::R16Sint | Self::R32Sint => \"int\",\n            Self::R64Uint => \"uint64_t\",\n\n            Self::Rg16Float | Self::Rg32Float => \"float4\",\n            Self::Rg8Unorm | Self::Rg16Unorm => \"unorm float4\",\n            Self::Rg8Snorm | Self::Rg16Snorm => \"snorm float4\",\n\n            Self::Rg8Sint | Self::Rg16Sint | Self::Rg32Uint => \"int4\",\n            Self::Rg8Uint | Self::Rg16Uint | Self::Rg32Sint => \"uint4\",\n\n            Self::Rg11b10Ufloat => \"float4\",\n\n            Self::Rgba16Float | Self::Rgba32Float => \"float4\",\n            Self::Rgba8Unorm | Self::Bgra8Unorm | Self::Rgba16Unorm | Self::Rgb10a2Unorm => {\n                \"unorm float4\"\n            }\n            Self::Rgba8Snorm | Self::Rgba16Snorm => \"snorm float4\",\n\n            Self::Rgba8Uint | Self::Rgba16Uint | Self::Rgba32Uint | Self::Rgb10a2Uint => \"uint4\",\n            Self::Rgba8Sint | Self::Rgba16Sint | Self::Rgba32Sint => \"int4\",\n        }\n    }\n}\n\nimpl crate::BuiltIn {\n    pub(super) fn to_hlsl_str(self) -> Result<&'static str, Error> {\n        Ok(match self {\n            Self::Position { .. } => \"SV_Position\",\n            // vertex\n            Self::ClipDistances => \"SV_ClipDistance\",\n            Self::CullDistance => \"SV_CullDistance\",\n            Self::InstanceIndex => \"SV_InstanceID\",\n            Self::VertexIndex => \"SV_VertexID\",\n            // fragment\n            Self::FragDepth => \"SV_Depth\",\n            Self::FrontFacing => \"SV_IsFrontFace\",\n            Self::PrimitiveIndex => \"SV_PrimitiveID\",\n            Self::Barycentric { .. } => \"SV_Barycentrics\",\n            Self::SampleIndex => \"SV_SampleIndex\",\n            Self::SampleMask => \"SV_Coverage\",\n            // compute\n            Self::GlobalInvocationId => \"SV_DispatchThreadID\",\n            Self::LocalInvocationId => \"SV_GroupThreadID\",\n            Self::LocalInvocationIndex => \"SV_GroupIndex\",\n            Self::WorkGroupId => \"SV_GroupID\",\n            // The specific semantic we use here doesn't matter, because references\n            // to this field will get replaced with references to `SPECIAL_CBUF_VAR`\n            // in `Writer::write_expr`.\n            Self::NumWorkGroups => \"SV_GroupID\",\n            Self::ViewIndex => \"SV_ViewID\",\n            // These builtins map to functions\n            Self::SubgroupSize\n            | Self::SubgroupInvocationId\n            | Self::NumSubgroups\n            | Self::SubgroupId => unreachable!(),\n            Self::BaseInstance | Self::BaseVertex | Self::WorkGroupSize => {\n                return Err(Error::Unimplemented(format!(\"builtin {self:?}\")))\n            }\n            Self::PointSize | Self::PointCoord | Self::DrawIndex => {\n                return Err(Error::Custom(format!(\"Unsupported builtin {self:?}\")))\n            }\n            Self::CullPrimitive => \"SV_CullPrimitive\",\n            Self::PointIndex | Self::LineIndices | Self::TriangleIndices => unimplemented!(),\n            Self::MeshTaskSize\n            | Self::VertexCount\n            | Self::PrimitiveCount\n            | Self::Vertices\n            | Self::Primitives => unreachable!(),\n            Self::RayInvocationId\n            | Self::NumRayInvocations\n            | Self::InstanceCustomData\n            | Self::GeometryIndex\n            | Self::WorldRayOrigin\n            | Self::WorldRayDirection\n            | Self::ObjectRayOrigin\n            | Self::ObjectRayDirection\n            | Self::RayTmin\n            | Self::RayTCurrentMax\n            | Self::ObjectToWorld\n            | Self::WorldToObject\n            | Self::HitKind => unreachable!(),\n        })\n    }\n}\n\nimpl crate::Interpolation {\n    /// Return the string corresponding to the HLSL interpolation qualifier.\n    pub(super) const fn to_hlsl_str(self) -> Option<&'static str> {\n        match self {\n            // Would be \"linear\", but it's the default interpolation in SM4 and up\n            // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-struct#interpolation-modifiers-introduced-in-shader-model-4\n            Self::Perspective => None,\n            Self::Linear => Some(\"noperspective\"),\n            Self::Flat => Some(\"nointerpolation\"),\n            Self::PerVertex => unreachable!(),\n        }\n    }\n}\n\nimpl crate::Sampling {\n    /// Return the HLSL auxiliary qualifier for the given sampling value.\n    pub(super) const fn to_hlsl_str(self) -> Option<&'static str> {\n        match self {\n            Self::Center | Self::First | Self::Either => None,\n            Self::Centroid => Some(\"centroid\"),\n            Self::Sample => Some(\"sample\"),\n        }\n    }\n}\n\nimpl crate::AtomicFunction {\n    /// Return the HLSL suffix for the `InterlockedXxx` method.\n    pub(super) const fn to_hlsl_suffix(self) -> &'static str {\n        match self {\n            Self::Add | Self::Subtract => \"Add\",\n            Self::And => \"And\",\n            Self::InclusiveOr => \"Or\",\n            Self::ExclusiveOr => \"Xor\",\n            Self::Min => \"Min\",\n            Self::Max => \"Max\",\n            Self::Exchange { compare: None } => \"Exchange\",\n            Self::Exchange { .. } => \"CompareExchange\",\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/help.rs",
    "content": "/*!\nHelpers for the hlsl backend\n\nImportant note about `Expression::ImageQuery`/`Expression::ArrayLength` and hlsl backend:\n\nDue to implementation of `GetDimensions` function in hlsl (<https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions>)\nbackend can't work with it as an expression.\nInstead, it generates a unique wrapped function per `Expression::ImageQuery`, based on texture info and query function.\nSee `WrappedImageQuery` struct that represents a unique function and will be generated before writing all statements and expressions.\nThis allowed to works with `Expression::ImageQuery` as expression and write wrapped function.\n\nFor example:\n```wgsl\nlet dim_1d = textureDimensions(image_1d);\n```\n\n```hlsl\nint NagaDimensions1D(Texture1D<float4>)\n{\n   uint4 ret;\n   image_1d.GetDimensions(ret.x);\n   return ret.x;\n}\n\nint dim_1d = NagaDimensions1D(image_1d);\n```\n*/\n\nuse alloc::format;\nuse core::fmt::Write;\n\nuse super::{\n    super::FunctionCtx,\n    writer::{\n        ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, F2I32_FUNCTION, F2I64_FUNCTION,\n        F2U32_FUNCTION, F2U64_FUNCTION, IMAGE_LOAD_EXTERNAL_FUNCTION,\n        IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION, NEG_FUNCTION,\n    },\n    BackendResult, WrappedType,\n};\nuse crate::{arena::Handle, proc::NameKey, ScalarKind};\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedArrayLength {\n    pub(super) writable: bool,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedImageLoad {\n    pub(super) class: crate::ImageClass,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedImageSample {\n    pub(super) class: crate::ImageClass,\n    pub(super) clamp_to_edge: bool,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedImageQuery {\n    pub(super) dim: crate::ImageDimension,\n    pub(super) arrayed: bool,\n    pub(super) class: crate::ImageClass,\n    pub(super) query: ImageQuery,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedConstructor {\n    pub(super) ty: Handle<crate::Type>,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedStructMatrixAccess {\n    pub(super) ty: Handle<crate::Type>,\n    pub(super) index: u32,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedMatCx2 {\n    pub(super) columns: crate::VectorSize,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedMath {\n    pub(super) fun: crate::MathFunction,\n    pub(super) scalar: crate::Scalar,\n    pub(super) components: Option<u32>,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedZeroValue {\n    pub(super) ty: Handle<crate::Type>,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedUnaryOp {\n    pub(super) op: crate::UnaryOperator,\n    // This can only represent scalar or vector types. If we ever need to wrap\n    // unary ops with other types, we'll need a better representation.\n    pub(super) ty: (Option<crate::VectorSize>, crate::Scalar),\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedBinaryOp {\n    pub(super) op: crate::BinaryOperator,\n    // This can only represent scalar or vector types. If we ever need to wrap\n    // binary ops with other types, we'll need a better representation.\n    pub(super) left_ty: (Option<crate::VectorSize>, crate::Scalar),\n    pub(super) right_ty: (Option<crate::VectorSize>, crate::Scalar),\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) struct WrappedCast {\n    // This can only represent scalar or vector types. If we ever need to wrap\n    // casts with other types, we'll need a better representation.\n    pub(super) vector_size: Option<crate::VectorSize>,\n    pub(super) src_scalar: crate::Scalar,\n    pub(super) dst_scalar: crate::Scalar,\n}\n\n/// HLSL backend requires its own `ImageQuery` enum.\n///\n/// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function.\n/// IR version can't be unique per function, because it's store mipmap level as an expression.\n///\n/// For example:\n/// ```wgsl\n/// let dim_cube_array_lod = textureDimensions(image_cube_array, 1);\n/// let dim_cube_array_lod2 = textureDimensions(image_cube_array, 1);\n/// ```\n///\n/// ```ir\n/// ImageQuery {\n///  image: [1],\n///  query: Size {\n///      level: Some(\n///          [1],\n///      ),\n///  },\n/// },\n/// ImageQuery {\n///  image: [1],\n///  query: Size {\n///      level: Some(\n///          [2],\n///      ),\n///  },\n/// },\n/// ```\n///\n/// HLSL should generate only 1 function for this case.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\npub(super) enum ImageQuery {\n    Size,\n    SizeLevel,\n    NumLevels,\n    NumLayers,\n    NumSamples,\n}\n\nimpl From<crate::ImageQuery> for ImageQuery {\n    fn from(q: crate::ImageQuery) -> Self {\n        use crate::ImageQuery as Iq;\n        match q {\n            Iq::Size { level: Some(_) } => ImageQuery::SizeLevel,\n            Iq::Size { level: None } => ImageQuery::Size,\n            Iq::NumLevels => ImageQuery::NumLevels,\n            Iq::NumLayers => ImageQuery::NumLayers,\n            Iq::NumSamples => ImageQuery::NumSamples,\n        }\n    }\n}\n\npub(super) const IMAGE_STORAGE_LOAD_SCALAR_WRAPPER: &str = \"LoadedStorageValueFrom\";\n\nimpl<W: Write> super::Writer<'_, W> {\n    pub(super) fn write_image_type(\n        &mut self,\n        dim: crate::ImageDimension,\n        arrayed: bool,\n        class: crate::ImageClass,\n    ) -> BackendResult {\n        let access_str = match class {\n            crate::ImageClass::Storage { .. } => \"RW\",\n            _ => \"\",\n        };\n        let dim_str = dim.to_hlsl_str();\n        let arrayed_str = if arrayed { \"Array\" } else { \"\" };\n        write!(self.out, \"{access_str}Texture{dim_str}{arrayed_str}\")?;\n        match class {\n            crate::ImageClass::Depth { multi } => {\n                let multi_str = if multi { \"MS\" } else { \"\" };\n                write!(self.out, \"{multi_str}<float>\")?\n            }\n            crate::ImageClass::Sampled { kind, multi } => {\n                let multi_str = if multi { \"MS\" } else { \"\" };\n                let scalar_kind_str = crate::Scalar { kind, width: 4 }.to_hlsl_str()?;\n                write!(self.out, \"{multi_str}<{scalar_kind_str}4>\")?\n            }\n            crate::ImageClass::Storage { format, .. } => {\n                let storage_format_str = format.to_hlsl_str();\n                write!(self.out, \"<{storage_format_str}>\")?\n            }\n            crate::ImageClass::External => {\n                unreachable!(\n                    \"external images should be handled by `write_global_external_texture`\"\n                );\n            }\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_array_length_function_name(\n        &mut self,\n        query: WrappedArrayLength,\n    ) -> BackendResult {\n        let access_str = if query.writable { \"RW\" } else { \"\" };\n        write!(self.out, \"NagaBufferLength{access_str}\",)?;\n\n        Ok(())\n    }\n\n    /// Helper function that write wrapped function for `Expression::ArrayLength`\n    ///\n    /// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer-getdimensions>\n    pub(super) fn write_wrapped_array_length_function(\n        &mut self,\n        wal: WrappedArrayLength,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const ARGUMENT_VARIABLE_NAME: &str = \"buffer\";\n        const RETURN_VARIABLE_NAME: &str = \"ret\";\n\n        // Write function return type and name\n        write!(self.out, \"uint \")?;\n        self.write_wrapped_array_length_function_name(wal)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n        let access_str = if wal.writable { \"RW\" } else { \"\" };\n        writeln!(\n            self.out,\n            \"{access_str}ByteAddressBuffer {ARGUMENT_VARIABLE_NAME})\"\n        )?;\n        // Write function body\n        writeln!(self.out, \"{{\")?;\n\n        // Write `GetDimensions` function.\n        writeln!(self.out, \"{INDENT}uint {RETURN_VARIABLE_NAME};\")?;\n        writeln!(\n            self.out,\n            \"{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions({RETURN_VARIABLE_NAME});\"\n        )?;\n\n        // Write return value\n        writeln!(self.out, \"{INDENT}return {RETURN_VARIABLE_NAME};\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    /// Helper function used by [`Self::write_wrapped_image_load_function`] and\n    /// [`Self::write_wrapped_image_sample_function`] to write the shared YUV\n    /// to RGB conversion code for external textures. Expects the preceding\n    /// code to declare the Y component as a `float` variable of name `y`, the\n    /// UV components as a `float2` variable of name `uv`, and the external\n    /// texture params as a variable of name `params`. The emitted code will\n    /// return the result.\n    fn write_convert_yuv_to_rgb_and_return(\n        &mut self,\n        level: crate::back::Level,\n        y: &str,\n        uv: &str,\n        params: &str,\n    ) -> BackendResult {\n        let l1 = level;\n        let l2 = l1.next();\n\n        // Convert from YUV to non-linear RGB in the source color space. We\n        // declare our matrices as row_major in HLSL, therefore we must reverse\n        // the order of this multiplication\n        writeln!(\n            self.out,\n            \"{l1}float3 srcGammaRgb = mul(float4({y}, {uv}, 1.0), {params}.yuv_conversion_matrix).rgb;\"\n        )?;\n\n        // Apply the inverse of the source transfer function to convert to\n        // linear RGB in the source color space.\n        writeln!(\n            self.out,\n            \"{l1}float3 srcLinearRgb = srcGammaRgb < {params}.src_tf.k * {params}.src_tf.b ?\"\n        )?;\n        writeln!(self.out, \"{l2}srcGammaRgb / {params}.src_tf.k :\")?;\n        writeln!(self.out, \"{l2}pow((srcGammaRgb + {params}.src_tf.a - 1.0) / {params}.src_tf.a, {params}.src_tf.g);\")?;\n\n        // Multiply by the gamut conversion matrix to convert to linear RGB in\n        // the destination color space. We declare our matrices as row_major in\n        // HLSL, therefore we must reverse the order of this multiplication.\n        writeln!(\n            self.out,\n            \"{l1}float3 dstLinearRgb = mul(srcLinearRgb, {params}.gamut_conversion_matrix);\"\n        )?;\n\n        // Finally, apply the dest transfer function to convert to non-linear\n        // RGB in the destination color space, and return the result.\n        writeln!(\n            self.out,\n            \"{l1}float3 dstGammaRgb = dstLinearRgb < {params}.dst_tf.b ?\"\n        )?;\n        writeln!(self.out, \"{l2}{params}.dst_tf.k * dstLinearRgb :\")?;\n        writeln!(self.out, \"{l2}{params}.dst_tf.a * pow(dstLinearRgb, 1.0 / {params}.dst_tf.g) - ({params}.dst_tf.a - 1);\")?;\n\n        writeln!(self.out, \"{l1}return float4(dstGammaRgb, 1.0);\")?;\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_image_load_function(\n        &mut self,\n        module: &crate::Module,\n        load: WrappedImageLoad,\n    ) -> BackendResult {\n        match load {\n            WrappedImageLoad {\n                class: crate::ImageClass::External,\n            } => {\n                let l1 = crate::back::Level(1);\n                let l2 = l1.next();\n                let l3 = l2.next();\n                let params_ty_name = &self.names\n                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n                writeln!(self.out, \"float4 {IMAGE_LOAD_EXTERNAL_FUNCTION}(\")?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane0,\")?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane1,\")?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane2,\")?;\n                writeln!(self.out, \"{l1}{params_ty_name} params,\")?;\n                writeln!(self.out, \"{l1}uint2 coords)\")?;\n                writeln!(self.out, \"{{\")?;\n                writeln!(self.out, \"{l1}uint2 plane0_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);\"\n                )?;\n                // Clamp coords to provided size of external texture to prevent OOB read.\n                // If params.size is zero then clamp to the actual size of the texture.\n                writeln!(\n                    self.out,\n                    \"{l1}uint2 cropped_size = any(params.size) ? params.size : plane0_size;\"\n                )?;\n                writeln!(self.out, \"{l1}coords = min(coords, cropped_size - 1);\")?;\n\n                // Apply load transformation. We declare our matrices as row_major in\n                // HLSL, therefore we must reverse the order of this multiplication\n                writeln!(self.out, \"{l1}float3x2 load_transform = float3x2(\")?;\n                writeln!(self.out, \"{l2}params.load_transform_0,\")?;\n                writeln!(self.out, \"{l2}params.load_transform_1,\")?;\n                writeln!(self.out, \"{l2}params.load_transform_2\")?;\n                writeln!(self.out, \"{l1});\")?;\n                writeln!(self.out, \"{l1}uint2 plane0_coords = uint2(round(mul(float3(coords, 1.0), load_transform)));\")?;\n                writeln!(self.out, \"{l1}if (params.num_planes == 1u) {{\")?;\n                // For single plane, simply read from plane0\n                writeln!(\n                    self.out,\n                    \"{l2}return plane0.Load(uint3(plane0_coords, 0u));\"\n                )?;\n                writeln!(self.out, \"{l1}}} else {{\")?;\n\n                // Chroma planes may be subsampled so we must scale the coords accordingly.\n                writeln!(self.out, \"{l2}uint2 plane1_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);\"\n                )?;\n                writeln!(self.out, \"{l2}uint2 plane1_coords = uint2(floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));\")?;\n\n                // For multi-plane, read the Y value from plane 0\n                writeln!(\n                    self.out,\n                    \"{l2}float y = plane0.Load(uint3(plane0_coords, 0u)).x;\"\n                )?;\n\n                writeln!(self.out, \"{l2}float2 uv;\")?;\n                writeln!(self.out, \"{l2}if (params.num_planes == 2u) {{\")?;\n                // Read UV from interleaved plane 1\n                writeln!(\n                    self.out,\n                    \"{l3}uv = plane1.Load(uint3(plane1_coords, 0u)).xy;\"\n                )?;\n                writeln!(self.out, \"{l2}}} else {{\")?;\n                // Read U and V from planes 1 and 2 respectively\n                writeln!(self.out, \"{l3}uint2 plane2_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);\"\n                )?;\n                writeln!(self.out, \"{l3}uint2 plane2_coords = uint2(floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));\")?;\n                writeln!(self.out, \"{l3}uv = float2(plane1.Load(uint3(plane1_coords, 0u)).x, plane2.Load(uint3(plane2_coords, 0u)).x);\")?;\n                writeln!(self.out, \"{l2}}}\")?;\n\n                self.write_convert_yuv_to_rgb_and_return(l2, \"y\", \"uv\", \"params\")?;\n\n                writeln!(self.out, \"{l1}}}\")?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_image_sample_function(\n        &mut self,\n        module: &crate::Module,\n        sample: WrappedImageSample,\n    ) -> BackendResult {\n        match sample {\n            WrappedImageSample {\n                class: crate::ImageClass::External,\n                clamp_to_edge: true,\n            } => {\n                let l1 = crate::back::Level(1);\n                let l2 = l1.next();\n                let l3 = l2.next();\n                let params_ty_name = &self.names\n                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n                writeln!(\n                    self.out,\n                    \"float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(\"\n                )?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane0,\")?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane1,\")?;\n                writeln!(self.out, \"{l1}Texture2D<float4> plane2,\")?;\n                writeln!(self.out, \"{l1}{params_ty_name} params,\")?;\n                writeln!(self.out, \"{l1}SamplerState samp,\")?;\n                writeln!(self.out, \"{l1}float2 coords)\")?;\n                writeln!(self.out, \"{{\")?;\n                writeln!(self.out, \"{l1}float2 plane0_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);\"\n                )?;\n                writeln!(self.out, \"{l1}float3x2 sample_transform = float3x2(\")?;\n                writeln!(self.out, \"{l2}params.sample_transform_0,\")?;\n                writeln!(self.out, \"{l2}params.sample_transform_1,\")?;\n                writeln!(self.out, \"{l2}params.sample_transform_2\")?;\n                writeln!(self.out, \"{l1});\")?;\n                // Apply sample transformation. We declare our matrices as row_major in\n                // HLSL, therefore we must reverse the order of this multiplication\n                writeln!(\n                    self.out,\n                    \"{l1}coords = mul(float3(coords, 1.0), sample_transform);\"\n                )?;\n                // Calculate the sample bounds. The purported size of the texture\n                // (params.size) is irrelevant here as we are dealing with normalized\n                // coordinates. Usually we would clamp to (0,0)..(1,1). However, we must\n                // apply the sample transformation to that, also bearing in mind that it\n                // may contain a flip on either axis. We calculate and adjust for the\n                // half-texel separately for each plane as it depends on the actual\n                // texture size which may vary between planes.\n                writeln!(\n                    self.out,\n                    \"{l1}float2 bounds_min = mul(float3(0.0, 0.0, 1.0), sample_transform);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 bounds_max = mul(float3(1.0, 1.0, 1.0), sample_transform);\"\n                )?;\n                writeln!(self.out, \"{l1}float4 bounds = float4(min(bounds_min, bounds_max), max(bounds_min, bounds_max));\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 plane0_half_texel = float2(0.5, 0.5) / plane0_size;\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 plane0_coords = clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);\"\n                )?;\n                writeln!(self.out, \"{l1}if (params.num_planes == 1u) {{\")?;\n                // For single plane, simply sample from plane0\n                writeln!(\n                    self.out,\n                    \"{l2}return plane0.SampleLevel(samp, plane0_coords, 0.0f);\"\n                )?;\n                writeln!(self.out, \"{l1}}} else {{\")?;\n\n                writeln!(self.out, \"{l2}float2 plane1_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l2}float2 plane1_half_texel = float2(0.5, 0.5) / plane1_size;\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l2}float2 plane1_coords = clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);\"\n                )?;\n\n                // For multi-plane, sample the Y value from plane 0\n                writeln!(\n                    self.out,\n                    \"{l2}float y = plane0.SampleLevel(samp, plane0_coords, 0.0f).x;\"\n                )?;\n                writeln!(self.out, \"{l2}float2 uv;\")?;\n                writeln!(self.out, \"{l2}if (params.num_planes == 2u) {{\")?;\n                // Sample UV from interleaved plane 1\n                writeln!(\n                    self.out,\n                    \"{l3}uv = plane1.SampleLevel(samp, plane1_coords, 0.0f).xy;\"\n                )?;\n                writeln!(self.out, \"{l2}}} else {{\")?;\n                // Sample U and V from planes 1 and 2 respectively\n                writeln!(self.out, \"{l3}float2 plane2_size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l3}float2 plane2_half_texel = float2(0.5, 0.5) / plane2_size;\"\n                )?;\n                writeln!(self.out, \"{l3}float2 plane2_coords = clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane2_half_texel);\")?;\n                writeln!(self.out, \"{l3}uv = float2(plane1.SampleLevel(samp, plane1_coords, 0.0f).x, plane2.SampleLevel(samp, plane2_coords, 0.0f).x);\")?;\n                writeln!(self.out, \"{l2}}}\")?;\n\n                self.write_convert_yuv_to_rgb_and_return(l2, \"y\", \"uv\", \"params\")?;\n\n                writeln!(self.out, \"{l1}}}\")?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            WrappedImageSample {\n                class:\n                    crate::ImageClass::Sampled {\n                        kind: ScalarKind::Float,\n                        multi: false,\n                    },\n                clamp_to_edge: true,\n            } => {\n                writeln!(self.out, \"float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(Texture2D<float4> tex, SamplerState samp, float2 coords) {{\")?;\n                let l1 = crate::back::Level(1);\n                writeln!(self.out, \"{l1}float2 size;\")?;\n                writeln!(self.out, \"{l1}tex.GetDimensions(size.x, size.y);\")?;\n                writeln!(self.out, \"{l1}float2 half_texel = float2(0.5, 0.5) / size;\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}return tex.SampleLevel(samp, clamp(coords, half_texel, 1.0 - half_texel), 0.0);\"\n                )?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_image_query_function_name(\n        &mut self,\n        query: WrappedImageQuery,\n    ) -> BackendResult {\n        let dim_str = query.dim.to_hlsl_str();\n        let class_str = match query.class {\n            crate::ImageClass::Sampled { multi: true, .. } => \"MS\",\n            crate::ImageClass::Depth { multi: true } => \"DepthMS\",\n            crate::ImageClass::Depth { multi: false } => \"Depth\",\n            crate::ImageClass::Sampled { multi: false, .. } => \"\",\n            crate::ImageClass::Storage { .. } => \"RW\",\n            crate::ImageClass::External => \"External\",\n        };\n        let arrayed_str = if query.arrayed { \"Array\" } else { \"\" };\n        let query_str = match query.query {\n            ImageQuery::Size => \"Dimensions\",\n            ImageQuery::SizeLevel => \"MipDimensions\",\n            ImageQuery::NumLevels => \"NumLevels\",\n            ImageQuery::NumLayers => \"NumLayers\",\n            ImageQuery::NumSamples => \"NumSamples\",\n        };\n\n        write!(self.out, \"Naga{class_str}{query_str}{dim_str}{arrayed_str}\")?;\n\n        Ok(())\n    }\n\n    /// Helper function that write wrapped function for `Expression::ImageQuery`\n    ///\n    /// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions>\n    pub(super) fn write_wrapped_image_query_function(\n        &mut self,\n        module: &crate::Module,\n        wiq: WrappedImageQuery,\n        expr_handle: Handle<crate::Expression>,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        use crate::{\n            back::{COMPONENTS, INDENT},\n            ImageDimension as IDim,\n        };\n\n        match wiq.class {\n            crate::ImageClass::External => {\n                if wiq.query != ImageQuery::Size {\n                    return Err(super::Error::Custom(\n                        \"External images only support `Size` queries\".into(),\n                    ));\n                }\n\n                write!(self.out, \"uint2 \")?;\n                self.write_wrapped_image_query_function_name(wiq)?;\n                let params_name = &self.names\n                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n                // Only plane0 and params are used by this implementation, but it's easier to\n                // always take all of them as arguments so that we can unconditionally expand an\n                // external texture expression each of its parts.\n                writeln!(self.out, \"(Texture2D<float4> plane0, Texture2D<float4> plane1, Texture2D<float4> plane2, {params_name} params) {{\")?;\n                let l1 = crate::back::Level(1);\n                let l2 = l1.next();\n                writeln!(self.out, \"{l1}if (any(params.size)) {{\")?;\n                writeln!(self.out, \"{l2}return params.size;\")?;\n                writeln!(self.out, \"{l1}}} else {{\")?;\n                // params.size == (0, 0) indicates to query and return plane 0's actual size\n                writeln!(self.out, \"{l2}uint2 ret;\")?;\n                writeln!(self.out, \"{l2}plane0.GetDimensions(ret.x, ret.y);\")?;\n                writeln!(self.out, \"{l2}return ret;\")?;\n                writeln!(self.out, \"{l1}}}\")?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {\n                const ARGUMENT_VARIABLE_NAME: &str = \"tex\";\n                const RETURN_VARIABLE_NAME: &str = \"ret\";\n                const MIP_LEVEL_PARAM: &str = \"mip_level\";\n\n                // Write function return type and name\n                let ret_ty = func_ctx.resolve_type(expr_handle, &module.types);\n                self.write_value_type(module, ret_ty)?;\n                write!(self.out, \" \")?;\n                self.write_wrapped_image_query_function_name(wiq)?;\n\n                // Write function parameters\n                write!(self.out, \"(\")?;\n                // Texture always first parameter\n                self.write_image_type(wiq.dim, wiq.arrayed, wiq.class)?;\n                write!(self.out, \" {ARGUMENT_VARIABLE_NAME}\")?;\n                // Mipmap is a second parameter if exists\n                if let ImageQuery::SizeLevel = wiq.query {\n                    write!(self.out, \", uint {MIP_LEVEL_PARAM}\")?;\n                }\n                writeln!(self.out, \")\")?;\n\n                // Write function body\n                writeln!(self.out, \"{{\")?;\n\n                let array_coords = usize::from(wiq.arrayed);\n                // extra parameter is the mip level count or the sample count\n                let extra_coords = match wiq.class {\n                    crate::ImageClass::Storage { .. } => 0,\n                    crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => 1,\n                    crate::ImageClass::External => unreachable!(),\n                };\n\n                // GetDimensions Overloaded Methods\n                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions#overloaded-methods\n                let (ret_swizzle, number_of_params) = match wiq.query {\n                    ImageQuery::Size | ImageQuery::SizeLevel => {\n                        let ret = match wiq.dim {\n                            IDim::D1 => \"x\",\n                            IDim::D2 => \"xy\",\n                            IDim::D3 => \"xyz\",\n                            IDim::Cube => \"xy\",\n                        };\n                        (ret, ret.len() + array_coords + extra_coords)\n                    }\n                    ImageQuery::NumLevels | ImageQuery::NumSamples | ImageQuery::NumLayers => {\n                        if wiq.arrayed || wiq.dim == IDim::D3 {\n                            (\"w\", 4)\n                        } else {\n                            (\"z\", 3)\n                        }\n                    }\n                };\n\n                // Write `GetDimensions` function.\n                writeln!(self.out, \"{INDENT}uint4 {RETURN_VARIABLE_NAME};\")?;\n                write!(self.out, \"{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions(\")?;\n                match wiq.query {\n                    ImageQuery::SizeLevel => {\n                        write!(self.out, \"{MIP_LEVEL_PARAM}, \")?;\n                    }\n                    _ => match wiq.class {\n                        crate::ImageClass::Sampled { multi: true, .. }\n                        | crate::ImageClass::Depth { multi: true }\n                        | crate::ImageClass::Storage { .. } => {}\n                        _ => {\n                            // Write zero mipmap level for supported types\n                            write!(self.out, \"0, \")?;\n                        }\n                    },\n                }\n\n                for component in COMPONENTS[..number_of_params - 1].iter() {\n                    write!(self.out, \"{RETURN_VARIABLE_NAME}.{component}, \")?;\n                }\n\n                // write last parameter without comma and space for last parameter\n                write!(\n                    self.out,\n                    \"{}.{}\",\n                    RETURN_VARIABLE_NAME,\n                    COMPONENTS[number_of_params - 1]\n                )?;\n\n                writeln!(self.out, \");\")?;\n\n                // Write return value\n                writeln!(\n                    self.out,\n                    \"{INDENT}return {RETURN_VARIABLE_NAME}.{ret_swizzle};\"\n                )?;\n\n                // End of function body\n                writeln!(self.out, \"}}\")?;\n                // Write extra new line\n                writeln!(self.out)?;\n            }\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_constructor_function_name(\n        &mut self,\n        module: &crate::Module,\n        constructor: WrappedConstructor,\n    ) -> BackendResult {\n        let name = crate::TypeInner::hlsl_type_id(constructor.ty, module.to_ctx(), &self.names)?;\n        write!(self.out, \"Construct{name}\")?;\n        Ok(())\n    }\n\n    /// Helper function that write wrapped function for `Expression::Compose` for structures.\n    fn write_wrapped_constructor_function(\n        &mut self,\n        module: &crate::Module,\n        constructor: WrappedConstructor,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const ARGUMENT_VARIABLE_NAME: &str = \"arg\";\n        const RETURN_VARIABLE_NAME: &str = \"ret\";\n\n        // Write function return type and name\n        if let crate::TypeInner::Array { base, size, .. } = module.types[constructor.ty].inner {\n            write!(self.out, \"typedef \")?;\n            self.write_type(module, constructor.ty)?;\n            write!(self.out, \" ret_\")?;\n            self.write_wrapped_constructor_function_name(module, constructor)?;\n            self.write_array_size(module, base, size)?;\n            writeln!(self.out, \";\")?;\n\n            write!(self.out, \"ret_\")?;\n            self.write_wrapped_constructor_function_name(module, constructor)?;\n        } else {\n            self.write_type(module, constructor.ty)?;\n        }\n        write!(self.out, \" \")?;\n        self.write_wrapped_constructor_function_name(module, constructor)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n\n        let mut write_arg = |i, ty| -> BackendResult {\n            if i != 0 {\n                write!(self.out, \", \")?;\n            }\n            self.write_type(module, ty)?;\n            write!(self.out, \" {ARGUMENT_VARIABLE_NAME}{i}\")?;\n            if let crate::TypeInner::Array { base, size, .. } = module.types[ty].inner {\n                self.write_array_size(module, base, size)?;\n            }\n            Ok(())\n        };\n\n        match module.types[constructor.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => {\n                for (i, member) in members.iter().enumerate() {\n                    write_arg(i, member.ty)?;\n                }\n            }\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                ..\n            } => {\n                for i in 0..size.get() as usize {\n                    write_arg(i, base)?;\n                }\n            }\n            _ => unreachable!(),\n        };\n\n        write!(self.out, \")\")?;\n\n        // Write function body\n        writeln!(self.out, \" {{\")?;\n\n        match module.types[constructor.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => {\n                let struct_name = &self.names[&NameKey::Type(constructor.ty)];\n                writeln!(\n                    self.out,\n                    \"{INDENT}{struct_name} {RETURN_VARIABLE_NAME} = ({struct_name})0;\"\n                )?;\n                for (i, member) in members.iter().enumerate() {\n                    let field_name = &self.names[&NameKey::StructMember(constructor.ty, i as u32)];\n\n                    match module.types[member.ty].inner {\n                        crate::TypeInner::Matrix {\n                            columns,\n                            rows: crate::VectorSize::Bi,\n                            ..\n                        } if member.binding.is_none() => {\n                            for j in 0..columns as u8 {\n                                writeln!(\n                                    self.out,\n                                    \"{INDENT}{RETURN_VARIABLE_NAME}.{field_name}_{j} = {ARGUMENT_VARIABLE_NAME}{i}[{j}];\"\n                                )?;\n                            }\n                        }\n                        ref other => {\n                            // We cast arrays of native HLSL `floatCx2`s to arrays of `matCx2`s\n                            // (where the inner matrix is represented by a struct with C `float2` members).\n                            // See the module-level block comment in mod.rs for details.\n                            if let Some(super::writer::MatrixType {\n                                columns,\n                                rows: crate::VectorSize::Bi,\n                                width: 4,\n                            }) = super::writer::get_inner_matrix_data(module, member.ty)\n                            {\n                                write!(\n                                    self.out,\n                                    \"{}{}.{} = (__mat{}x2\",\n                                    INDENT, RETURN_VARIABLE_NAME, field_name, columns as u8\n                                )?;\n                                if let crate::TypeInner::Array { base, size, .. } = *other {\n                                    self.write_array_size(module, base, size)?;\n                                }\n                                writeln!(self.out, \"){ARGUMENT_VARIABLE_NAME}{i};\",)?;\n                            } else {\n                                writeln!(\n                                    self.out,\n                                    \"{INDENT}{RETURN_VARIABLE_NAME}.{field_name} = {ARGUMENT_VARIABLE_NAME}{i};\",\n                                )?;\n                            }\n                        }\n                    }\n                }\n            }\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                ..\n            } => {\n                write!(self.out, \"{INDENT}\")?;\n                self.write_type(module, base)?;\n                write!(self.out, \" {RETURN_VARIABLE_NAME}\")?;\n                self.write_array_size(module, base, crate::ArraySize::Constant(size))?;\n                write!(self.out, \" = {{ \")?;\n                for i in 0..size.get() {\n                    if i != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    write!(self.out, \"{ARGUMENT_VARIABLE_NAME}{i}\")?;\n                }\n                writeln!(self.out, \" }};\",)?;\n            }\n            _ => unreachable!(),\n        }\n\n        // Write return value\n        writeln!(self.out, \"{INDENT}return {RETURN_VARIABLE_NAME};\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    /// Writes the conversion from a single length storage texture load to a vec4 with the loaded\n    /// scalar in its `x` component, 1 in its `a` component and 0 everywhere else.\n    fn write_loaded_scalar_to_storage_loaded_value(\n        &mut self,\n        scalar_type: crate::Scalar,\n    ) -> BackendResult {\n        const ARGUMENT_VARIABLE_NAME: &str = \"arg\";\n        const RETURN_VARIABLE_NAME: &str = \"ret\";\n\n        let zero;\n        let one;\n        match scalar_type.kind {\n            ScalarKind::Sint => {\n                assert_eq!(\n                    scalar_type.width, 4,\n                    \"Scalar {scalar_type:?} is not a result from any storage format\"\n                );\n                zero = \"0\";\n                one = \"1\";\n            }\n            ScalarKind::Uint => match scalar_type.width {\n                4 => {\n                    zero = \"0u\";\n                    one = \"1u\";\n                }\n                8 => {\n                    zero = \"0uL\";\n                    one = \"1uL\"\n                }\n                _ => unreachable!(\"Scalar {scalar_type:?} is not a result from any storage format\"),\n            },\n            ScalarKind::Float => {\n                assert_eq!(\n                    scalar_type.width, 4,\n                    \"Scalar {scalar_type:?} is not a result from any storage format\"\n                );\n                zero = \"0.0\";\n                one = \"1.0\";\n            }\n            _ => unreachable!(\"Scalar {scalar_type:?} is not a result from any storage format\"),\n        }\n\n        let ty = scalar_type.to_hlsl_str()?;\n        writeln!(\n            self.out,\n            \"{ty}4 {IMAGE_STORAGE_LOAD_SCALAR_WRAPPER}{ty}({ty} {ARGUMENT_VARIABLE_NAME}) {{\\\n    {ty}4 {RETURN_VARIABLE_NAME} = {ty}4({ARGUMENT_VARIABLE_NAME}, {zero}, {zero}, {one});\\\n    return {RETURN_VARIABLE_NAME};\\\n}}\"\n        )?;\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_struct_matrix_get_function_name(\n        &mut self,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        let name = &self.names[&NameKey::Type(access.ty)];\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n        write!(self.out, \"GetMat{field_name}On{name}\")?;\n        Ok(())\n    }\n\n    /// Writes a function used to get a matCx2 from within a structure.\n    pub(super) fn write_wrapped_struct_matrix_get_function(\n        &mut self,\n        module: &crate::Module,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = \"obj\";\n\n        // Write function return type and name\n        let member = match module.types[access.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],\n            _ => unreachable!(),\n        };\n        let ret_ty = &module.types[member.ty].inner;\n        self.write_value_type(module, ret_ty)?;\n        write!(self.out, \" \")?;\n        self.write_wrapped_struct_matrix_get_function_name(access)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n        let struct_name = &self.names[&NameKey::Type(access.ty)];\n        write!(self.out, \"{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}\")?;\n\n        // Write function body\n        writeln!(self.out, \") {{\")?;\n\n        // Write return value\n        write!(self.out, \"{INDENT}return \")?;\n        self.write_value_type(module, ret_ty)?;\n        write!(self.out, \"(\")?;\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n        match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { columns, .. } => {\n                for i in 0..columns as u8 {\n                    if i != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    write!(self.out, \"{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i}\")?;\n                }\n            }\n            _ => unreachable!(),\n        }\n        writeln!(self.out, \");\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_struct_matrix_set_function_name(\n        &mut self,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        let name = &self.names[&NameKey::Type(access.ty)];\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n        write!(self.out, \"SetMat{field_name}On{name}\")?;\n        Ok(())\n    }\n\n    /// Writes a function used to set a matCx2 from within a structure.\n    pub(super) fn write_wrapped_struct_matrix_set_function(\n        &mut self,\n        module: &crate::Module,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = \"obj\";\n        const MATRIX_ARGUMENT_VARIABLE_NAME: &str = \"mat\";\n\n        // Write function return type and name\n        write!(self.out, \"void \")?;\n        self.write_wrapped_struct_matrix_set_function_name(access)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n        let struct_name = &self.names[&NameKey::Type(access.ty)];\n        write!(self.out, \"{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, \")?;\n        let member = match module.types[access.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],\n            _ => unreachable!(),\n        };\n        self.write_type(module, member.ty)?;\n        write!(self.out, \" {MATRIX_ARGUMENT_VARIABLE_NAME}\")?;\n        // Write function body\n        writeln!(self.out, \") {{\")?;\n\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n\n        match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { columns, .. } => {\n                for i in 0..columns as u8 {\n                    writeln!(\n                        self.out,\n                        \"{INDENT}{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {MATRIX_ARGUMENT_VARIABLE_NAME}[{i}];\"\n                    )?;\n                }\n            }\n            _ => unreachable!(),\n        }\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_struct_matrix_set_vec_function_name(\n        &mut self,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        let name = &self.names[&NameKey::Type(access.ty)];\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n        write!(self.out, \"SetMatVec{field_name}On{name}\")?;\n        Ok(())\n    }\n\n    /// Writes a function used to set a vec2 on a matCx2 from within a structure.\n    pub(super) fn write_wrapped_struct_matrix_set_vec_function(\n        &mut self,\n        module: &crate::Module,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = \"obj\";\n        const VECTOR_ARGUMENT_VARIABLE_NAME: &str = \"vec\";\n        const MATRIX_INDEX_ARGUMENT_VARIABLE_NAME: &str = \"mat_idx\";\n\n        // Write function return type and name\n        write!(self.out, \"void \")?;\n        self.write_wrapped_struct_matrix_set_vec_function_name(access)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n        let struct_name = &self.names[&NameKey::Type(access.ty)];\n        write!(self.out, \"{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, \")?;\n        let member = match module.types[access.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],\n            _ => unreachable!(),\n        };\n        let vec_ty = match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { rows, scalar, .. } => {\n                crate::TypeInner::Vector { size: rows, scalar }\n            }\n            _ => unreachable!(),\n        };\n        self.write_value_type(module, &vec_ty)?;\n        write!(\n            self.out,\n            \" {VECTOR_ARGUMENT_VARIABLE_NAME}, uint {MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}\"\n        )?;\n\n        // Write function body\n        writeln!(self.out, \") {{\")?;\n\n        writeln!(\n            self.out,\n            \"{INDENT}switch({MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}) {{\"\n        )?;\n\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n\n        match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { columns, .. } => {\n                for i in 0..columns as u8 {\n                    writeln!(\n                        self.out,\n                        \"{INDENT}case {i}: {{ {STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {VECTOR_ARGUMENT_VARIABLE_NAME}; break; }}\"\n                    )?;\n                }\n            }\n            _ => unreachable!(),\n        }\n\n        writeln!(self.out, \"{INDENT}}}\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_struct_matrix_set_scalar_function_name(\n        &mut self,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        let name = &self.names[&NameKey::Type(access.ty)];\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n        write!(self.out, \"SetMatScalar{field_name}On{name}\")?;\n        Ok(())\n    }\n\n    /// Writes a function used to set a float on a matCx2 from within a structure.\n    pub(super) fn write_wrapped_struct_matrix_set_scalar_function(\n        &mut self,\n        module: &crate::Module,\n        access: WrappedStructMatrixAccess,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = \"obj\";\n        const SCALAR_ARGUMENT_VARIABLE_NAME: &str = \"scalar\";\n        const MATRIX_INDEX_ARGUMENT_VARIABLE_NAME: &str = \"mat_idx\";\n        const VECTOR_INDEX_ARGUMENT_VARIABLE_NAME: &str = \"vec_idx\";\n\n        // Write function return type and name\n        write!(self.out, \"void \")?;\n        self.write_wrapped_struct_matrix_set_scalar_function_name(access)?;\n\n        // Write function parameters\n        write!(self.out, \"(\")?;\n        let struct_name = &self.names[&NameKey::Type(access.ty)];\n        write!(self.out, \"{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, \")?;\n        let member = match module.types[access.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],\n            _ => unreachable!(),\n        };\n        let scalar_ty = match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { scalar, .. } => crate::TypeInner::Scalar(scalar),\n            _ => unreachable!(),\n        };\n        self.write_value_type(module, &scalar_ty)?;\n        write!(\n            self.out,\n            \" {SCALAR_ARGUMENT_VARIABLE_NAME}, uint {MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}, uint {VECTOR_INDEX_ARGUMENT_VARIABLE_NAME}\"\n        )?;\n\n        // Write function body\n        writeln!(self.out, \") {{\")?;\n\n        writeln!(\n            self.out,\n            \"{INDENT}switch({MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}) {{\"\n        )?;\n\n        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];\n\n        match module.types[member.ty].inner {\n            crate::TypeInner::Matrix { columns, .. } => {\n                for i in 0..columns as u8 {\n                    writeln!(\n                        self.out,\n                        \"{INDENT}case {i}: {{ {STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i}[{VECTOR_INDEX_ARGUMENT_VARIABLE_NAME}] = {SCALAR_ARGUMENT_VARIABLE_NAME}; break; }}\"\n                    )?;\n                }\n            }\n            _ => unreachable!(),\n        }\n\n        writeln!(self.out, \"{INDENT}}}\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    /// Write functions to create special types.\n    pub(super) fn write_special_functions(&mut self, module: &crate::Module) -> BackendResult {\n        for (type_key, struct_ty) in module.special_types.predeclared_types.iter() {\n            match type_key {\n                &crate::PredeclaredType::ModfResult { size, scalar }\n                | &crate::PredeclaredType::FrexpResult { size, scalar } => {\n                    let arg_type_name_owner;\n                    let arg_type_name = if let Some(size) = size {\n                        arg_type_name_owner = format!(\n                            \"{}{}\",\n                            if scalar.width == 8 { \"double\" } else { \"float\" },\n                            size as u8\n                        );\n                        &arg_type_name_owner\n                    } else if scalar.width == 8 {\n                        \"double\"\n                    } else {\n                        \"float\"\n                    };\n\n                    let (defined_func_name, called_func_name, second_field_name, sign_multiplier) =\n                        if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {\n                            (super::writer::MODF_FUNCTION, \"modf\", \"whole\", \"\")\n                        } else {\n                            (\n                                super::writer::FREXP_FUNCTION,\n                                \"frexp\",\n                                \"exp_\",\n                                \"sign(arg) * \",\n                            )\n                        };\n\n                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];\n\n                    writeln!(\n                        self.out,\n                        \"{struct_name} {defined_func_name}({arg_type_name} arg) {{\n    {arg_type_name} other;\n    {struct_name} result;\n    result.fract = {sign_multiplier}{called_func_name}(arg, other);\n    result.{second_field_name} = other;\n    return result;\n}}\"\n                    )?;\n                    writeln!(self.out)?;\n                }\n                &crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {}\n            }\n        }\n        if module.special_types.ray_desc.is_some() {\n            self.write_ray_desc_from_ray_desc_constructor_function(module)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper function that writes wrapped functions for expressions in a function\n    pub(super) fn write_wrapped_expression_functions(\n        &mut self,\n        module: &crate::Module,\n        expressions: &crate::Arena<crate::Expression>,\n        context: Option<&FunctionCtx>,\n    ) -> BackendResult {\n        for (handle, _) in expressions.iter() {\n            match expressions[handle] {\n                crate::Expression::Compose { ty, .. } => {\n                    match module.types[ty].inner {\n                        crate::TypeInner::Struct { .. } | crate::TypeInner::Array { .. } => {\n                            let constructor = WrappedConstructor { ty };\n                            if self.wrapped.insert(WrappedType::Constructor(constructor)) {\n                                self.write_wrapped_constructor_function(module, constructor)?;\n                            }\n                        }\n                        _ => {}\n                    };\n                }\n                crate::Expression::ImageLoad { image, .. } => {\n                    // This can only happen in a function as this is not a valid const expression\n                    match *context.as_ref().unwrap().resolve_type(image, &module.types) {\n                        crate::TypeInner::Image {\n                            class: crate::ImageClass::Storage { format, .. },\n                            ..\n                        } => {\n                            if format.single_component() {\n                                let scalar: crate::Scalar = format.into();\n                                if self.wrapped.insert(WrappedType::ImageLoadScalar(scalar)) {\n                                    self.write_loaded_scalar_to_storage_loaded_value(scalar)?;\n                                }\n                            }\n                        }\n                        _ => {}\n                    }\n                }\n                crate::Expression::RayQueryGetIntersection { committed, .. } => {\n                    if committed {\n                        if !self.written_committed_intersection {\n                            self.write_committed_intersection_function(module)?;\n                            self.written_committed_intersection = true;\n                        }\n                    } else if !self.written_candidate_intersection {\n                        self.write_candidate_intersection_function(module)?;\n                        self.written_candidate_intersection = true;\n                    }\n                }\n                _ => {}\n            }\n        }\n        Ok(())\n    }\n\n    // TODO: we could merge this with iteration in write_wrapped_expression_functions...\n    //\n    /// Helper function that writes zero value wrapped functions\n    pub(super) fn write_wrapped_zero_value_functions(\n        &mut self,\n        module: &crate::Module,\n        expressions: &crate::Arena<crate::Expression>,\n    ) -> BackendResult {\n        for (handle, _) in expressions.iter() {\n            if let crate::Expression::ZeroValue(ty) = expressions[handle] {\n                let zero_value = WrappedZeroValue { ty };\n                if self.wrapped.insert(WrappedType::ZeroValue(zero_value)) {\n                    self.write_wrapped_zero_value_function(module, zero_value)?;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_math_functions(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        for (_, expression) in func_ctx.expressions.iter() {\n            if let crate::Expression::Math {\n                fun,\n                arg,\n                arg1: _arg1,\n                arg2: _arg2,\n                arg3: _arg3,\n            } = *expression\n            {\n                let arg_ty = func_ctx.resolve_type(arg, &module.types);\n\n                match fun {\n                    crate::MathFunction::ExtractBits => {\n                        // The behavior of our extractBits polyfill is undefined if offset + count > bit_width. We need\n                        // to first sanitize the offset and count first. If we don't do this, we will get out-of-spec\n                        // values if the extracted range is not within the bit width.\n                        //\n                        // This encodes the exact formula specified by the wgsl spec:\n                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin\n                        //\n                        // w = sizeof(x) * 8\n                        // o = min(offset, w)\n                        // c = min(count, w - o)\n                        //\n                        // bitfieldExtract(x, o, c)\n                        let scalar = arg_ty.scalar().unwrap();\n                        let components = arg_ty.components();\n\n                        let wrapped = WrappedMath {\n                            fun,\n                            scalar,\n                            components,\n                        };\n\n                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {\n                            continue;\n                        }\n\n                        // Write return type\n                        self.write_value_type(module, arg_ty)?;\n\n                        let scalar_width: u8 = scalar.width * 8;\n\n                        // Write function name and parameters\n                        writeln!(self.out, \" {EXTRACT_BITS_FUNCTION}(\")?;\n                        write!(self.out, \"    \")?;\n                        self.write_value_type(module, arg_ty)?;\n                        writeln!(self.out, \" e,\")?;\n                        writeln!(self.out, \"    uint offset,\")?;\n                        writeln!(self.out, \"    uint count\")?;\n                        writeln!(self.out, \") {{\")?;\n\n                        // Write function body\n                        writeln!(self.out, \"    uint w = {scalar_width};\")?;\n                        writeln!(self.out, \"    uint o = min(offset, w);\")?;\n                        writeln!(self.out, \"    uint c = min(count, w - o);\")?;\n                        writeln!(\n                            self.out,\n                            \"    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\"\n                        )?;\n\n                        // End of function body\n                        writeln!(self.out, \"}}\")?;\n                    }\n                    crate::MathFunction::InsertBits => {\n                        // The behavior of our insertBits polyfill has the same constraints as the extractBits polyfill.\n\n                        let scalar = arg_ty.scalar().unwrap();\n                        let components = arg_ty.components();\n\n                        let wrapped = WrappedMath {\n                            fun,\n                            scalar,\n                            components,\n                        };\n\n                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {\n                            continue;\n                        }\n\n                        // Write return type\n                        self.write_value_type(module, arg_ty)?;\n\n                        let scalar_width: u8 = scalar.width * 8;\n                        let scalar_max: u64 = match scalar.width {\n                            1 => 0xFF,\n                            2 => 0xFFFF,\n                            4 => 0xFFFFFFFF,\n                            8 => 0xFFFFFFFFFFFFFFFF,\n                            _ => unreachable!(),\n                        };\n\n                        // Write function name and parameters\n                        writeln!(self.out, \" {INSERT_BITS_FUNCTION}(\")?;\n                        write!(self.out, \"    \")?;\n                        self.write_value_type(module, arg_ty)?;\n                        writeln!(self.out, \" e,\")?;\n                        write!(self.out, \"    \")?;\n                        self.write_value_type(module, arg_ty)?;\n                        writeln!(self.out, \" newbits,\")?;\n                        writeln!(self.out, \"    uint offset,\")?;\n                        writeln!(self.out, \"    uint count\")?;\n                        writeln!(self.out, \") {{\")?;\n\n                        // Write function body\n                        writeln!(self.out, \"    uint w = {scalar_width}u;\")?;\n                        writeln!(self.out, \"    uint o = min(offset, w);\")?;\n                        writeln!(self.out, \"    uint c = min(count, w - o);\")?;\n\n                        // The `u` suffix on the literals is _extremely_ important. Otherwise it will use\n                        // i32 shifting instead of the intended u32 shifting.\n                        writeln!(\n                            self.out,\n                            \"    uint mask = (({scalar_max}u >> ({scalar_width}u - c)) << o);\"\n                        )?;\n                        writeln!(\n                            self.out,\n                            \"    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\"\n                        )?;\n\n                        // End of function body\n                        writeln!(self.out, \"}}\")?;\n                    }\n                    // Taking the absolute value of the minimum value of a two's\n                    // complement signed integer type causes overflow, which is\n                    // undefined behaviour in HLSL. To avoid this, when the value is\n                    // negative we bitcast the value to unsigned and negate it, then\n                    // bitcast back to signed.\n                    // This adheres to the WGSL spec in that the absolute of the type's\n                    // minimum value should equal to the minimum value.\n                    //\n                    // TODO(#7109): asint()/asuint() only support 32-bit integers, so we\n                    // must find another solution for different bit-widths.\n                    crate::MathFunction::Abs\n                        if matches!(arg_ty.scalar(), Some(crate::Scalar::I32)) =>\n                    {\n                        let scalar = arg_ty.scalar().unwrap();\n                        let components = arg_ty.components();\n\n                        let wrapped = WrappedMath {\n                            fun,\n                            scalar,\n                            components,\n                        };\n\n                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {\n                            continue;\n                        }\n\n                        self.write_value_type(module, arg_ty)?;\n                        write!(self.out, \" {ABS_FUNCTION}(\")?;\n                        self.write_value_type(module, arg_ty)?;\n                        writeln!(self.out, \" val) {{\")?;\n\n                        let level = crate::back::Level(1);\n                        writeln!(\n                            self.out,\n                            \"{level}return val >= 0 ? val : asint(-asuint(val));\"\n                        )?;\n                        writeln!(self.out, \"}}\")?;\n                        writeln!(self.out)?;\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_unary_ops(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        for (_, expression) in func_ctx.expressions.iter() {\n            if let crate::Expression::Unary { op, expr } = *expression {\n                let expr_ty = func_ctx.resolve_type(expr, &module.types);\n                let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else {\n                    continue;\n                };\n                let wrapped = WrappedUnaryOp {\n                    op,\n                    ty: (vector_size, scalar),\n                };\n\n                // Negating the minimum value of a two's complement signed integer type\n                // causes overflow, which is undefined behaviour in HLSL. To avoid this\n                // we bitcast the value to unsigned and negate it, then bitcast back to\n                // signed. This adheres to the WGSL spec in that the negative of the\n                // type's minimum value should equal to the minimum value.\n                //\n                // TODO(#7109): asint()/asuint() only support 32-bit integers, so we must\n                // find another solution for different bit-widths.\n                match (op, scalar) {\n                    (crate::UnaryOperator::Negate, crate::Scalar::I32) => {\n                        if !self.wrapped.insert(WrappedType::UnaryOp(wrapped)) {\n                            continue;\n                        }\n\n                        self.write_value_type(module, expr_ty)?;\n                        write!(self.out, \" {NEG_FUNCTION}(\")?;\n                        self.write_value_type(module, expr_ty)?;\n                        writeln!(self.out, \" val) {{\")?;\n\n                        let level = crate::back::Level(1);\n                        writeln!(self.out, \"{level}return asint(-asuint(val));\",)?;\n                        writeln!(self.out, \"}}\")?;\n                        writeln!(self.out)?;\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_binary_ops(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        for (expr_handle, expression) in func_ctx.expressions.iter() {\n            if let crate::Expression::Binary { op, left, right } = *expression {\n                let expr_ty = func_ctx.resolve_type(expr_handle, &module.types);\n                let left_ty = func_ctx.resolve_type(left, &module.types);\n                let right_ty = func_ctx.resolve_type(right, &module.types);\n\n                match (op, expr_ty.scalar()) {\n                    // Signed integer division of the type's minimum representable value\n                    // divided by -1, or signed or unsigned division by zero, is\n                    // undefined behaviour in HLSL. We override the divisor to 1 in these\n                    // cases.\n                    // This adheres to the WGSL spec in that:\n                    // * TYPE_MIN / -1 == TYPE_MIN\n                    // * x / 0 == x\n                    (\n                        crate::BinaryOperator::Divide,\n                        Some(\n                            scalar @ crate::Scalar {\n                                kind: ScalarKind::Sint | ScalarKind::Uint,\n                                ..\n                            },\n                        ),\n                    ) => {\n                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {\n                            continue;\n                        };\n                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {\n                            continue;\n                        };\n                        let wrapped = WrappedBinaryOp {\n                            op,\n                            left_ty: left_wrapped_ty,\n                            right_ty: right_wrapped_ty,\n                        };\n                        if !self.wrapped.insert(WrappedType::BinaryOp(wrapped)) {\n                            continue;\n                        }\n\n                        self.write_value_type(module, expr_ty)?;\n                        write!(self.out, \" {DIV_FUNCTION}(\")?;\n                        self.write_value_type(module, left_ty)?;\n                        write!(self.out, \" lhs, \")?;\n                        self.write_value_type(module, right_ty)?;\n                        writeln!(self.out, \" rhs) {{\")?;\n                        let level = crate::back::Level(1);\n                        match scalar.kind {\n                            ScalarKind::Sint => {\n                                let min_val = match scalar.width {\n                                    4 => crate::Literal::I32(i32::MIN),\n                                    8 => crate::Literal::I64(i64::MIN),\n                                    _ => {\n                                        return Err(super::Error::UnsupportedScalar(scalar));\n                                    }\n                                };\n                                write!(self.out, \"{level}return lhs / (((lhs == \")?;\n                                self.write_literal(min_val)?;\n                                writeln!(self.out, \" & rhs == -1) | (rhs == 0)) ? 1 : rhs);\")?\n                            }\n                            ScalarKind::Uint => {\n                                writeln!(self.out, \"{level}return lhs / (rhs == 0u ? 1u : rhs);\")?\n                            }\n                            _ => unreachable!(),\n                        }\n                        writeln!(self.out, \"}}\")?;\n                        writeln!(self.out)?;\n                    }\n                    // The modulus operator is only defined for integers in HLSL when\n                    // either both sides are positive or both sides are negative. To\n                    // avoid this undefined behaviour we use the following equation:\n                    //\n                    // dividend - (dividend / divisor) * divisor\n                    //\n                    // overriding the divisor to 1 if either it is 0, or it is -1\n                    // and the dividend is the minimum representable value.\n                    //\n                    // This adheres to the WGSL spec in that:\n                    // * min_value % -1 == 0\n                    // * x % 0 == 0\n                    (\n                        crate::BinaryOperator::Modulo,\n                        Some(\n                            scalar @ crate::Scalar {\n                                kind: ScalarKind::Sint | ScalarKind::Uint | ScalarKind::Float,\n                                ..\n                            },\n                        ),\n                    ) => {\n                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {\n                            continue;\n                        };\n                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {\n                            continue;\n                        };\n                        let wrapped = WrappedBinaryOp {\n                            op,\n                            left_ty: left_wrapped_ty,\n                            right_ty: right_wrapped_ty,\n                        };\n                        if !self.wrapped.insert(WrappedType::BinaryOp(wrapped)) {\n                            continue;\n                        }\n\n                        self.write_value_type(module, expr_ty)?;\n                        write!(self.out, \" {MOD_FUNCTION}(\")?;\n                        self.write_value_type(module, left_ty)?;\n                        write!(self.out, \" lhs, \")?;\n                        self.write_value_type(module, right_ty)?;\n                        writeln!(self.out, \" rhs) {{\")?;\n                        let level = crate::back::Level(1);\n                        match scalar.kind {\n                            ScalarKind::Sint => {\n                                let min_val = match scalar.width {\n                                    4 => crate::Literal::I32(i32::MIN),\n                                    8 => crate::Literal::I64(i64::MIN),\n                                    _ => {\n                                        return Err(super::Error::UnsupportedScalar(scalar));\n                                    }\n                                };\n                                write!(self.out, \"{level}\")?;\n                                self.write_value_type(module, right_ty)?;\n                                write!(self.out, \" divisor = ((lhs == \")?;\n                                self.write_literal(min_val)?;\n                                writeln!(self.out, \" & rhs == -1) | (rhs == 0)) ? 1 : rhs;\")?;\n                                writeln!(\n                                    self.out,\n                                    \"{level}return lhs - (lhs / divisor) * divisor;\"\n                                )?\n                            }\n                            ScalarKind::Uint => {\n                                writeln!(self.out, \"{level}return lhs % (rhs == 0u ? 1u : rhs);\")?\n                            }\n                            // HLSL's fmod has the same definition as WGSL's % operator but due\n                            // to its implementation in DXC it is not as accurate as the WGSL spec\n                            // requires it to be. See:\n                            // - https://shader-playground.timjones.io/0c8572816dbb6fc4435cc5d016a978a7\n                            // - https://github.com/llvm/llvm-project/blob/50f9b8acafdca48e87e6b8e393c1f116a2d193ee/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h#L78-L81\n                            ScalarKind::Float => {\n                                writeln!(self.out, \"{level}return lhs - rhs * trunc(lhs / rhs);\")?\n                            }\n                            _ => unreachable!(),\n                        }\n                        writeln!(self.out, \"}}\")?;\n                        writeln!(self.out)?;\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn write_wrapped_cast_functions(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        for (_, expression) in func_ctx.expressions.iter() {\n            if let crate::Expression::As {\n                expr,\n                kind,\n                convert: Some(width),\n            } = *expression\n            {\n                // Avoid undefined behaviour when casting from a float to integer\n                // when the value is out of range for the target type. Additionally\n                // ensure we clamp to the correct value as per the WGSL spec.\n                //\n                // https://www.w3.org/TR/WGSL/#floating-point-conversion:\n                // * If X is exactly representable in the target type T, then the\n                //   result is that value.\n                // * Otherwise, the result is the value in T closest to\n                //   truncate(X) and also exactly representable in the original\n                //   floating point type.\n                let src_ty = func_ctx.resolve_type(expr, &module.types);\n                let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else {\n                    continue;\n                };\n                let dst_scalar = crate::Scalar { kind, width };\n                if src_scalar.kind != ScalarKind::Float\n                    || (dst_scalar.kind != ScalarKind::Sint && dst_scalar.kind != ScalarKind::Uint)\n                {\n                    continue;\n                }\n\n                let wrapped = WrappedCast {\n                    src_scalar,\n                    vector_size,\n                    dst_scalar,\n                };\n                if !self.wrapped.insert(WrappedType::Cast(wrapped)) {\n                    continue;\n                }\n\n                let (src_ty, dst_ty) = match vector_size {\n                    None => (\n                        crate::TypeInner::Scalar(src_scalar),\n                        crate::TypeInner::Scalar(dst_scalar),\n                    ),\n                    Some(vector_size) => (\n                        crate::TypeInner::Vector {\n                            scalar: src_scalar,\n                            size: vector_size,\n                        },\n                        crate::TypeInner::Vector {\n                            scalar: dst_scalar,\n                            size: vector_size,\n                        },\n                    ),\n                };\n                let (min, max) =\n                    crate::proc::min_max_float_representable_by(src_scalar, dst_scalar);\n                let cast_str = format!(\n                    \"{}{}\",\n                    dst_scalar.to_hlsl_str()?,\n                    vector_size\n                        .map(crate::common::vector_size_str)\n                        .unwrap_or(\"\"),\n                );\n                let fun_name = match dst_scalar {\n                    crate::Scalar::I32 => F2I32_FUNCTION,\n                    crate::Scalar::U32 => F2U32_FUNCTION,\n                    crate::Scalar::I64 => F2I64_FUNCTION,\n                    crate::Scalar::U64 => F2U64_FUNCTION,\n                    _ => unreachable!(),\n                };\n                self.write_value_type(module, &dst_ty)?;\n                write!(self.out, \" {fun_name}(\")?;\n                self.write_value_type(module, &src_ty)?;\n                writeln!(self.out, \" value) {{\")?;\n                let level = crate::back::Level(1);\n                write!(self.out, \"{level}return {cast_str}(clamp(value, \")?;\n                self.write_literal(min)?;\n                write!(self.out, \", \")?;\n                self.write_literal(max)?;\n                writeln!(self.out, \"));\",)?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Helper function that writes various wrapped functions\n    pub(super) fn write_wrapped_functions(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        self.write_wrapped_math_functions(module, func_ctx)?;\n        self.write_wrapped_unary_ops(module, func_ctx)?;\n        self.write_wrapped_binary_ops(module, func_ctx)?;\n        self.write_wrapped_expression_functions(module, func_ctx.expressions, Some(func_ctx))?;\n        self.write_wrapped_zero_value_functions(module, func_ctx.expressions)?;\n        self.write_wrapped_cast_functions(module, func_ctx)?;\n\n        for (handle, _) in func_ctx.expressions.iter() {\n            match func_ctx.expressions[handle] {\n                crate::Expression::ArrayLength(expr) => {\n                    let global_expr = match func_ctx.expressions[expr] {\n                        crate::Expression::GlobalVariable(_) => expr,\n                        crate::Expression::AccessIndex { base, index: _ } => base,\n                        ref other => unreachable!(\"Array length of {:?}\", other),\n                    };\n                    let global_var = match func_ctx.expressions[global_expr] {\n                        crate::Expression::GlobalVariable(var_handle) => {\n                            &module.global_variables[var_handle]\n                        }\n                        ref other => {\n                            return Err(super::Error::Unimplemented(format!(\n                                \"Array length of base {other:?}\"\n                            )))\n                        }\n                    };\n                    let storage_access = match global_var.space {\n                        crate::AddressSpace::Storage { access } => access,\n                        _ => crate::StorageAccess::default(),\n                    };\n                    let wal = WrappedArrayLength {\n                        writable: storage_access.contains(crate::StorageAccess::STORE),\n                    };\n\n                    if self.wrapped.insert(WrappedType::ArrayLength(wal)) {\n                        self.write_wrapped_array_length_function(wal)?;\n                    }\n                }\n                crate::Expression::ImageLoad { image, .. } => {\n                    let class = match *func_ctx.resolve_type(image, &module.types) {\n                        crate::TypeInner::Image { class, .. } => class,\n                        _ => unreachable!(),\n                    };\n                    let wrapped = WrappedImageLoad { class };\n                    if self.wrapped.insert(WrappedType::ImageLoad(wrapped)) {\n                        self.write_wrapped_image_load_function(module, wrapped)?;\n                    }\n                }\n                crate::Expression::ImageSample {\n                    image,\n                    clamp_to_edge,\n                    ..\n                } => {\n                    let class = match *func_ctx.resolve_type(image, &module.types) {\n                        crate::TypeInner::Image { class, .. } => class,\n                        _ => unreachable!(),\n                    };\n                    let wrapped = WrappedImageSample {\n                        class,\n                        clamp_to_edge,\n                    };\n                    if self.wrapped.insert(WrappedType::ImageSample(wrapped)) {\n                        self.write_wrapped_image_sample_function(module, wrapped)?;\n                    }\n                }\n                crate::Expression::ImageQuery { image, query } => {\n                    let wiq = match *func_ctx.resolve_type(image, &module.types) {\n                        crate::TypeInner::Image {\n                            dim,\n                            arrayed,\n                            class,\n                        } => WrappedImageQuery {\n                            dim,\n                            arrayed,\n                            class,\n                            query: query.into(),\n                        },\n                        _ => unreachable!(\"we only query images\"),\n                    };\n\n                    if self.wrapped.insert(WrappedType::ImageQuery(wiq)) {\n                        self.write_wrapped_image_query_function(module, wiq, handle, func_ctx)?;\n                    }\n                }\n                // Write `WrappedConstructor` for structs that are loaded from `AddressSpace::Storage`\n                // since they will later be used by the fn `write_storage_load`\n                crate::Expression::Load { pointer } => {\n                    let pointer_space = func_ctx\n                        .resolve_type(pointer, &module.types)\n                        .pointer_space();\n\n                    if let Some(crate::AddressSpace::Storage { .. }) = pointer_space {\n                        if let Some(ty) = func_ctx.info[handle].ty.handle() {\n                            write_wrapped_constructor(self, ty, module)?;\n                        }\n                    }\n\n                    fn write_wrapped_constructor<W: Write>(\n                        writer: &mut super::Writer<'_, W>,\n                        ty: Handle<crate::Type>,\n                        module: &crate::Module,\n                    ) -> BackendResult {\n                        match module.types[ty].inner {\n                            crate::TypeInner::Struct { ref members, .. } => {\n                                for member in members {\n                                    write_wrapped_constructor(writer, member.ty, module)?;\n                                }\n\n                                let constructor = WrappedConstructor { ty };\n                                if writer.wrapped.insert(WrappedType::Constructor(constructor)) {\n                                    writer\n                                        .write_wrapped_constructor_function(module, constructor)?;\n                                }\n                            }\n                            crate::TypeInner::Array { base, .. } => {\n                                write_wrapped_constructor(writer, base, module)?;\n\n                                let constructor = WrappedConstructor { ty };\n                                if writer.wrapped.insert(WrappedType::Constructor(constructor)) {\n                                    writer\n                                        .write_wrapped_constructor_function(module, constructor)?;\n                                }\n                            }\n                            _ => {}\n                        };\n\n                        Ok(())\n                    }\n                }\n                // We treat matrices of the form `matCx2` as a sequence of C `vec2`s\n                // (see top level module docs for details).\n                //\n                // The functions injected here are required to get the matrix accesses working.\n                crate::Expression::AccessIndex { base, index } => {\n                    let base_ty_res = &func_ctx.info[base].ty;\n                    let mut resolved = base_ty_res.inner_with(&module.types);\n                    let base_ty_handle = match *resolved {\n                        crate::TypeInner::Pointer { base, .. } => {\n                            resolved = &module.types[base].inner;\n                            Some(base)\n                        }\n                        _ => base_ty_res.handle(),\n                    };\n                    if let crate::TypeInner::Struct { ref members, .. } = *resolved {\n                        let member = &members[index as usize];\n\n                        match module.types[member.ty].inner {\n                            crate::TypeInner::Matrix {\n                                rows: crate::VectorSize::Bi,\n                                ..\n                            } if member.binding.is_none() => {\n                                let ty = base_ty_handle.unwrap();\n                                let access = WrappedStructMatrixAccess { ty, index };\n\n                                if self.wrapped.insert(WrappedType::StructMatrixAccess(access)) {\n                                    self.write_wrapped_struct_matrix_get_function(module, access)?;\n                                    self.write_wrapped_struct_matrix_set_function(module, access)?;\n                                    self.write_wrapped_struct_matrix_set_vec_function(\n                                        module, access,\n                                    )?;\n                                    self.write_wrapped_struct_matrix_set_scalar_function(\n                                        module, access,\n                                    )?;\n                                }\n                            }\n                            _ => {}\n                        }\n                    }\n                }\n                _ => {}\n            };\n        }\n\n        Ok(())\n    }\n\n    /// Writes out the sampler heap declarations if they haven't been written yet.\n    pub(super) fn write_sampler_heaps(&mut self) -> BackendResult {\n        if self.wrapped.sampler_heaps {\n            return Ok(());\n        }\n\n        writeln!(\n            self.out,\n            \"SamplerState {}[2048]: register(s{}, space{});\",\n            super::writer::SAMPLER_HEAP_VAR,\n            self.options.sampler_heap_target.standard_samplers.register,\n            self.options.sampler_heap_target.standard_samplers.space\n        )?;\n        writeln!(\n            self.out,\n            \"SamplerComparisonState {}[2048]: register(s{}, space{});\",\n            super::writer::COMPARISON_SAMPLER_HEAP_VAR,\n            self.options\n                .sampler_heap_target\n                .comparison_samplers\n                .register,\n            self.options.sampler_heap_target.comparison_samplers.space\n        )?;\n\n        self.wrapped.sampler_heaps = true;\n\n        Ok(())\n    }\n\n    /// Writes out the sampler index buffer declaration if it hasn't been written yet.\n    pub(super) fn write_wrapped_sampler_buffer(\n        &mut self,\n        key: super::SamplerIndexBufferKey,\n    ) -> BackendResult {\n        // The astute will notice that we do a double hash lookup, but we do this to avoid\n        // holding a mutable reference to `self` while trying to call `write_sampler_heaps`.\n        //\n        // We only pay this double lookup cost when we actually need to write out the sampler\n        // buffer, which should be not be common.\n\n        if self.wrapped.sampler_index_buffers.contains_key(&key) {\n            return Ok(());\n        };\n\n        self.write_sampler_heaps()?;\n\n        // Because the group number can be arbitrary, we use the namer to generate a unique name\n        // instead of adding it to the reserved name list.\n        let sampler_array_name = self\n            .namer\n            .call(&format!(\"nagaGroup{}SamplerIndexArray\", key.group));\n\n        let bind_target = match self.options.sampler_buffer_binding_map.get(&key) {\n            Some(&bind_target) => bind_target,\n            None if self.options.fake_missing_bindings => super::BindTarget {\n                space: u8::MAX,\n                register: key.group,\n                binding_array_size: None,\n                dynamic_storage_buffer_offsets_index: None,\n                restrict_indexing: false,\n            },\n            None => {\n                unreachable!(\"Sampler buffer of group {key:?} not bound to a register\");\n            }\n        };\n\n        writeln!(\n            self.out,\n            \"StructuredBuffer<uint> {sampler_array_name} : register(t{}, space{});\",\n            bind_target.register, bind_target.space\n        )?;\n\n        self.wrapped\n            .sampler_index_buffers\n            .insert(key, sampler_array_name);\n\n        Ok(())\n    }\n\n    pub(super) fn write_texture_coordinates(\n        &mut self,\n        kind: &str,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        mip_level: Option<Handle<crate::Expression>>,\n        module: &crate::Module,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        // HLSL expects the array index to be merged with the coordinate\n        let extra = array_index.is_some() as usize + (mip_level.is_some()) as usize;\n        if extra == 0 {\n            self.write_expr(module, coordinate, func_ctx)?;\n        } else {\n            let num_coords = match *func_ctx.resolve_type(coordinate, &module.types) {\n                crate::TypeInner::Scalar { .. } => 1,\n                crate::TypeInner::Vector { size, .. } => size as usize,\n                _ => unreachable!(),\n            };\n            write!(self.out, \"{}{}(\", kind, num_coords + extra)?;\n            self.write_expr(module, coordinate, func_ctx)?;\n            if let Some(expr) = array_index {\n                write!(self.out, \", \")?;\n                self.write_expr(module, expr, func_ctx)?;\n            }\n            if let Some(expr) = mip_level {\n                // Explicit cast if needed\n                let cast_to_int = matches!(\n                    *func_ctx.resolve_type(expr, &module.types),\n                    crate::TypeInner::Scalar(crate::Scalar {\n                        kind: ScalarKind::Uint,\n                        ..\n                    })\n                );\n\n                write!(self.out, \", \")?;\n\n                if cast_to_int {\n                    write!(self.out, \"int(\")?;\n                }\n\n                self.write_expr(module, expr, func_ctx)?;\n\n                if cast_to_int {\n                    write!(self.out, \")\")?;\n                }\n            }\n            write!(self.out, \")\")?;\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_mat_cx2_typedef_and_functions(\n        &mut self,\n        WrappedMatCx2 { columns }: WrappedMatCx2,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        // typedef\n        write!(self.out, \"typedef struct {{ \")?;\n        for i in 0..columns as u8 {\n            write!(self.out, \"float2 _{i}; \")?;\n        }\n        writeln!(self.out, \"}} __mat{}x2;\", columns as u8)?;\n\n        // __get_col_of_mat\n        writeln!(\n            self.out,\n            \"float2 __get_col_of_mat{}x2(__mat{}x2 mat, uint idx) {{\",\n            columns as u8, columns as u8\n        )?;\n        writeln!(self.out, \"{INDENT}switch(idx) {{\")?;\n        for i in 0..columns as u8 {\n            writeln!(self.out, \"{INDENT}case {i}: {{ return mat._{i}; }}\")?;\n        }\n        writeln!(self.out, \"{INDENT}default: {{ return (float2)0; }}\")?;\n        writeln!(self.out, \"{INDENT}}}\")?;\n        writeln!(self.out, \"}}\")?;\n\n        // __set_col_of_mat\n        writeln!(\n            self.out,\n            \"void __set_col_of_mat{}x2(__mat{}x2 mat, uint idx, float2 value) {{\",\n            columns as u8, columns as u8\n        )?;\n        writeln!(self.out, \"{INDENT}switch(idx) {{\")?;\n        for i in 0..columns as u8 {\n            writeln!(self.out, \"{INDENT}case {i}: {{ mat._{i} = value; break; }}\")?;\n        }\n        writeln!(self.out, \"{INDENT}}}\")?;\n        writeln!(self.out, \"}}\")?;\n\n        // __set_el_of_mat\n        writeln!(\n            self.out,\n            \"void __set_el_of_mat{}x2(__mat{}x2 mat, uint idx, uint vec_idx, float value) {{\",\n            columns as u8, columns as u8\n        )?;\n        writeln!(self.out, \"{INDENT}switch(idx) {{\")?;\n        for i in 0..columns as u8 {\n            writeln!(\n                self.out,\n                \"{INDENT}case {i}: {{ mat._{i}[vec_idx] = value; break; }}\"\n            )?;\n        }\n        writeln!(self.out, \"{INDENT}}}\")?;\n        writeln!(self.out, \"}}\")?;\n\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n\n    pub(super) fn write_all_mat_cx2_typedefs_and_functions(\n        &mut self,\n        module: &crate::Module,\n    ) -> BackendResult {\n        for (handle, _) in module.global_variables.iter() {\n            let global = &module.global_variables[handle];\n\n            if global.space == crate::AddressSpace::Uniform {\n                if let Some(super::writer::MatrixType {\n                    columns,\n                    rows: crate::VectorSize::Bi,\n                    width: 4,\n                }) = super::writer::get_inner_matrix_data(module, global.ty)\n                {\n                    let entry = WrappedMatCx2 { columns };\n                    if self.wrapped.insert(WrappedType::MatCx2(entry)) {\n                        self.write_mat_cx2_typedef_and_functions(entry)?;\n                    }\n                }\n            }\n        }\n\n        for (_, ty) in module.types.iter() {\n            if let crate::TypeInner::Struct { ref members, .. } = ty.inner {\n                for member in members.iter() {\n                    if let crate::TypeInner::Array { .. } = module.types[member.ty].inner {\n                        if let Some(super::writer::MatrixType {\n                            columns,\n                            rows: crate::VectorSize::Bi,\n                            width: 4,\n                        }) = super::writer::get_inner_matrix_data(module, member.ty)\n                        {\n                            let entry = WrappedMatCx2 { columns };\n                            if self.wrapped.insert(WrappedType::MatCx2(entry)) {\n                                self.write_mat_cx2_typedef_and_functions(entry)?;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_zero_value_function_name(\n        &mut self,\n        module: &crate::Module,\n        zero_value: WrappedZeroValue,\n    ) -> BackendResult {\n        let name = crate::TypeInner::hlsl_type_id(zero_value.ty, module.to_ctx(), &self.names)?;\n        write!(self.out, \"ZeroValue{name}\")?;\n        Ok(())\n    }\n\n    /// Helper function that write wrapped function for `Expression::ZeroValue`\n    ///\n    /// This is necessary since we might have a member access after the zero value expression, e.g.\n    /// `.y` (in practice this can come up when consuming SPIRV that's been produced by glslc).\n    ///\n    /// So we can't just write `(float4)0` since `(float4)0.y` won't parse correctly.\n    ///\n    /// Parenthesizing the expression like `((float4)0).y` would work... except DXC can't handle\n    /// cases like:\n    ///\n    /// ```text\n    /// tests\\out\\hlsl\\access.hlsl:183:41: error: cannot compile this l-value expression yet\n    ///     t_1.am = (__mat4x2[2])((float4x2[2])0);\n    ///                                         ^\n    /// ```\n    fn write_wrapped_zero_value_function(\n        &mut self,\n        module: &crate::Module,\n        zero_value: WrappedZeroValue,\n    ) -> BackendResult {\n        use crate::back::INDENT;\n\n        // Write function return type and name\n        if let crate::TypeInner::Array { base, size, .. } = module.types[zero_value.ty].inner {\n            write!(self.out, \"typedef \")?;\n            self.write_type(module, zero_value.ty)?;\n            write!(self.out, \" ret_\")?;\n            self.write_wrapped_zero_value_function_name(module, zero_value)?;\n            self.write_array_size(module, base, size)?;\n            writeln!(self.out, \";\")?;\n\n            write!(self.out, \"ret_\")?;\n            self.write_wrapped_zero_value_function_name(module, zero_value)?;\n        } else {\n            self.write_type(module, zero_value.ty)?;\n        }\n        write!(self.out, \" \")?;\n        self.write_wrapped_zero_value_function_name(module, zero_value)?;\n\n        // Write function parameters (none) and start function body\n        writeln!(self.out, \"() {{\")?;\n\n        // Write `ZeroValue` function.\n        write!(self.out, \"{INDENT}return \")?;\n        self.write_default_init(module, zero_value.ty)?;\n        writeln!(self.out, \";\")?;\n\n        // End of function body\n        writeln!(self.out, \"}}\")?;\n        // Write extra new line\n        writeln!(self.out)?;\n\n        Ok(())\n    }\n}\n\nimpl crate::StorageFormat {\n    /// Returns `true` if there is just one component, otherwise `false`\n    pub(super) const fn single_component(&self) -> bool {\n        match *self {\n            crate::StorageFormat::R16Float\n            | crate::StorageFormat::R32Float\n            | crate::StorageFormat::R8Unorm\n            | crate::StorageFormat::R16Unorm\n            | crate::StorageFormat::R8Snorm\n            | crate::StorageFormat::R16Snorm\n            | crate::StorageFormat::R8Uint\n            | crate::StorageFormat::R16Uint\n            | crate::StorageFormat::R32Uint\n            | crate::StorageFormat::R8Sint\n            | crate::StorageFormat::R16Sint\n            | crate::StorageFormat::R32Sint\n            | crate::StorageFormat::R64Uint => true,\n            _ => false,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/keywords.rs",
    "content": "use crate::proc::{CaseInsensitiveKeywordSet, KeywordSet};\nuse crate::racy_lock::RacyLock;\n\n// When compiling with FXC without strict mode, these keywords are actually case insensitive.\n// If you compile with strict mode and specify a different casing like \"Pass\" instead in an identifier, FXC will give this error:\n// \"error X3086: alternate cases for 'pass' are deprecated in strict mode\"\n// This behavior is not documented anywhere, but as far as I can tell this is the full list.\npub const RESERVED_CASE_INSENSITIVE: &[&str] = &[\n    \"asm\",\n    \"decl\",\n    \"pass\",\n    \"technique\",\n    \"Texture1D\",\n    \"Texture2D\",\n    \"Texture3D\",\n    \"TextureCube\",\n];\n\npub const RESERVED: &[&str] = &[\n    // FXC keywords, from https://github.com/MicrosoftDocs/win32/blob/c885cb0c63b0e9be80c6a0e6512473ac6f4e771e/desktop-src/direct3dhlsl/dx-graphics-hlsl-appendix-keywords.md?plain=1#L99-L118\n    \"AppendStructuredBuffer\",\n    \"asm\",\n    \"asm_fragment\",\n    \"BlendState\",\n    \"bool\",\n    \"break\",\n    \"Buffer\",\n    \"ByteAddressBuffer\",\n    \"case\",\n    \"cbuffer\",\n    \"centroid\",\n    \"class\",\n    \"column_major\",\n    \"compile\",\n    \"compile_fragment\",\n    \"CompileShader\",\n    \"const\",\n    \"continue\",\n    \"ComputeShader\",\n    \"ConsumeStructuredBuffer\",\n    \"default\",\n    \"DepthStencilState\",\n    \"DepthStencilView\",\n    \"discard\",\n    \"do\",\n    \"double\",\n    \"DomainShader\",\n    \"dword\",\n    \"else\",\n    \"export\",\n    \"extern\",\n    \"false\",\n    \"float\",\n    \"for\",\n    \"fxgroup\",\n    \"GeometryShader\",\n    \"groupshared\",\n    \"half\",\n    \"Hullshader\",\n    \"if\",\n    \"in\",\n    \"inline\",\n    \"inout\",\n    \"InputPatch\",\n    \"int\",\n    \"interface\",\n    \"line\",\n    \"lineadj\",\n    \"linear\",\n    \"LineStream\",\n    \"matrix\",\n    \"min16float\",\n    \"min10float\",\n    \"min16int\",\n    \"min12int\",\n    \"min16uint\",\n    \"namespace\",\n    \"nointerpolation\",\n    \"noperspective\",\n    \"NULL\",\n    \"out\",\n    \"OutputPatch\",\n    \"packoffset\",\n    \"pass\",\n    \"pixelfragment\",\n    \"PixelShader\",\n    \"point\",\n    \"PointStream\",\n    \"precise\",\n    \"RasterizerState\",\n    \"RenderTargetView\",\n    \"return\",\n    \"register\",\n    \"row_major\",\n    \"RWBuffer\",\n    \"RWByteAddressBuffer\",\n    \"RWStructuredBuffer\",\n    \"RWTexture1D\",\n    \"RWTexture1DArray\",\n    \"RWTexture2D\",\n    \"RWTexture2DArray\",\n    \"RWTexture3D\",\n    \"sample\",\n    \"sampler\",\n    \"SamplerState\",\n    \"SamplerComparisonState\",\n    \"shared\",\n    \"snorm\",\n    \"stateblock\",\n    \"stateblock_state\",\n    \"static\",\n    \"string\",\n    \"struct\",\n    \"switch\",\n    \"StructuredBuffer\",\n    \"tbuffer\",\n    \"technique\",\n    \"technique10\",\n    \"technique11\",\n    \"texture\",\n    \"Texture1D\",\n    \"Texture1DArray\",\n    \"Texture2D\",\n    \"Texture2DArray\",\n    \"Texture2DMS\",\n    \"Texture2DMSArray\",\n    \"Texture3D\",\n    \"TextureCube\",\n    \"TextureCubeArray\",\n    \"true\",\n    \"typedef\",\n    \"triangle\",\n    \"triangleadj\",\n    \"TriangleStream\",\n    \"uint\",\n    \"uniform\",\n    \"unorm\",\n    \"unsigned\",\n    \"vector\",\n    \"vertexfragment\",\n    \"VertexShader\",\n    \"void\",\n    \"volatile\",\n    \"while\",\n    // FXC reserved keywords, from https://github.com/MicrosoftDocs/win32/blob/c885cb0c63b0e9be80c6a0e6512473ac6f4e771e/desktop-src/direct3dhlsl/dx-graphics-hlsl-appendix-reserved-words.md?plain=1#L19-L38\n    \"auto\",\n    \"case\",\n    \"catch\",\n    \"char\",\n    \"class\",\n    \"const_cast\",\n    \"default\",\n    \"delete\",\n    \"dynamic_cast\",\n    \"enum\",\n    \"explicit\",\n    \"friend\",\n    \"goto\",\n    \"long\",\n    \"mutable\",\n    \"new\",\n    \"operator\",\n    \"private\",\n    \"protected\",\n    \"public\",\n    \"reinterpret_cast\",\n    \"short\",\n    \"signed\",\n    \"sizeof\",\n    \"static_cast\",\n    \"template\",\n    \"this\",\n    \"throw\",\n    \"try\",\n    \"typename\",\n    \"union\",\n    \"unsigned\",\n    \"using\",\n    \"virtual\",\n    // FXC intrinsics, from https://github.com/MicrosoftDocs/win32/blob/1682b99e203708f6f5eda972d966e30f3c1588de/desktop-src/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions.md?plain=1#L26-L165\n    \"abort\",\n    \"abs\",\n    \"acos\",\n    \"all\",\n    \"AllMemoryBarrier\",\n    \"AllMemoryBarrierWithGroupSync\",\n    \"any\",\n    \"asdouble\",\n    \"asfloat\",\n    \"asin\",\n    \"asint\",\n    \"asuint\",\n    \"atan\",\n    \"atan2\",\n    \"ceil\",\n    \"CheckAccessFullyMapped\",\n    \"clamp\",\n    \"clip\",\n    \"cos\",\n    \"cosh\",\n    \"countbits\",\n    \"cross\",\n    \"D3DCOLORtoUBYTE4\",\n    \"ddx\",\n    \"ddx_coarse\",\n    \"ddx_fine\",\n    \"ddy\",\n    \"ddy_coarse\",\n    \"ddy_fine\",\n    \"degrees\",\n    \"determinant\",\n    \"DeviceMemoryBarrier\",\n    \"DeviceMemoryBarrierWithGroupSync\",\n    \"distance\",\n    \"dot\",\n    \"dst\",\n    \"errorf\",\n    \"EvaluateAttributeCentroid\",\n    \"EvaluateAttributeAtSample\",\n    \"EvaluateAttributeSnapped\",\n    \"exp\",\n    \"exp2\",\n    \"f16tof32\",\n    \"f32tof16\",\n    \"faceforward\",\n    \"firstbithigh\",\n    \"firstbitlow\",\n    \"floor\",\n    \"fma\",\n    \"fmod\",\n    \"frac\",\n    \"frexp\",\n    \"fwidth\",\n    \"GetRenderTargetSampleCount\",\n    \"GetRenderTargetSamplePosition\",\n    \"GroupMemoryBarrier\",\n    \"GroupMemoryBarrierWithGroupSync\",\n    \"InterlockedAdd\",\n    \"InterlockedAnd\",\n    \"InterlockedCompareExchange\",\n    \"InterlockedCompareStore\",\n    \"InterlockedExchange\",\n    \"InterlockedMax\",\n    \"InterlockedMin\",\n    \"InterlockedOr\",\n    \"InterlockedXor\",\n    \"isfinite\",\n    \"isinf\",\n    \"isnan\",\n    \"ldexp\",\n    \"length\",\n    \"lerp\",\n    \"lit\",\n    \"log\",\n    \"log10\",\n    \"log2\",\n    \"mad\",\n    \"max\",\n    \"min\",\n    \"modf\",\n    \"msad4\",\n    \"mul\",\n    \"noise\",\n    \"normalize\",\n    \"pow\",\n    \"printf\",\n    \"Process2DQuadTessFactorsAvg\",\n    \"Process2DQuadTessFactorsMax\",\n    \"Process2DQuadTessFactorsMin\",\n    \"ProcessIsolineTessFactors\",\n    \"ProcessQuadTessFactorsAvg\",\n    \"ProcessQuadTessFactorsMax\",\n    \"ProcessQuadTessFactorsMin\",\n    \"ProcessTriTessFactorsAvg\",\n    \"ProcessTriTessFactorsMax\",\n    \"ProcessTriTessFactorsMin\",\n    \"radians\",\n    \"rcp\",\n    \"reflect\",\n    \"refract\",\n    \"reversebits\",\n    \"round\",\n    \"rsqrt\",\n    \"saturate\",\n    \"sign\",\n    \"sin\",\n    \"sincos\",\n    \"sinh\",\n    \"smoothstep\",\n    \"sqrt\",\n    \"step\",\n    \"tan\",\n    \"tanh\",\n    \"tex1D\",\n    \"tex1Dbias\",\n    \"tex1Dgrad\",\n    \"tex1Dlod\",\n    \"tex1Dproj\",\n    \"tex2D\",\n    \"tex2Dbias\",\n    \"tex2Dgrad\",\n    \"tex2Dlod\",\n    \"tex2Dproj\",\n    \"tex3D\",\n    \"tex3Dbias\",\n    \"tex3Dgrad\",\n    \"tex3Dlod\",\n    \"tex3Dproj\",\n    \"texCUBE\",\n    \"texCUBEbias\",\n    \"texCUBEgrad\",\n    \"texCUBElod\",\n    \"texCUBEproj\",\n    \"transpose\",\n    \"trunc\",\n    // DXC (reserved) keywords, from https://github.com/microsoft/DirectXShaderCompiler/blob/d5d478470d3020a438d3cb810b8d3fe0992e6709/tools/clang/include/clang/Basic/TokenKinds.def#L222-L648\n    // with the KEYALL, KEYCXX, BOOLSUPPORT, WCHARSUPPORT, KEYHLSL options enabled (see https://github.com/microsoft/DirectXShaderCompiler/blob/d5d478470d3020a438d3cb810b8d3fe0992e6709/tools/clang/lib/Frontend/CompilerInvocation.cpp#L1199)\n    \"auto\",\n    \"break\",\n    \"case\",\n    \"char\",\n    \"const\",\n    \"continue\",\n    \"default\",\n    \"do\",\n    \"double\",\n    \"else\",\n    \"enum\",\n    \"extern\",\n    \"float\",\n    \"for\",\n    \"goto\",\n    \"if\",\n    \"inline\",\n    \"int\",\n    \"long\",\n    \"register\",\n    \"return\",\n    \"short\",\n    \"signed\",\n    \"sizeof\",\n    \"static\",\n    \"struct\",\n    \"switch\",\n    \"typedef\",\n    \"union\",\n    \"unsigned\",\n    \"void\",\n    \"volatile\",\n    \"while\",\n    \"_Alignas\",\n    \"_Alignof\",\n    \"_Atomic\",\n    \"_Complex\",\n    \"_Generic\",\n    \"_Imaginary\",\n    \"_Noreturn\",\n    \"_Static_assert\",\n    \"_Thread_local\",\n    \"__func__\",\n    \"__objc_yes\",\n    \"__objc_no\",\n    \"asm\",\n    \"bool\",\n    \"catch\",\n    \"class\",\n    \"const_cast\",\n    \"delete\",\n    \"dynamic_cast\",\n    \"explicit\",\n    \"export\",\n    \"false\",\n    \"friend\",\n    \"mutable\",\n    \"namespace\",\n    \"new\",\n    \"operator\",\n    \"private\",\n    \"protected\",\n    \"public\",\n    \"reinterpret_cast\",\n    \"static_cast\",\n    \"template\",\n    \"this\",\n    \"throw\",\n    \"true\",\n    \"try\",\n    \"typename\",\n    \"typeid\",\n    \"using\",\n    \"virtual\",\n    \"wchar_t\",\n    \"_Decimal32\",\n    \"_Decimal64\",\n    \"_Decimal128\",\n    \"__null\",\n    \"__alignof\",\n    \"__attribute\",\n    \"__builtin_choose_expr\",\n    \"__builtin_offsetof\",\n    \"__builtin_va_arg\",\n    \"__extension__\",\n    \"__imag\",\n    \"__int128\",\n    \"__label__\",\n    \"__real\",\n    \"__thread\",\n    \"__FUNCTION__\",\n    \"__PRETTY_FUNCTION__\",\n    \"__is_nothrow_assignable\",\n    \"__is_constructible\",\n    \"__is_nothrow_constructible\",\n    \"__has_nothrow_assign\",\n    \"__has_nothrow_move_assign\",\n    \"__has_nothrow_copy\",\n    \"__has_nothrow_constructor\",\n    \"__has_trivial_assign\",\n    \"__has_trivial_move_assign\",\n    \"__has_trivial_copy\",\n    \"__has_trivial_constructor\",\n    \"__has_trivial_move_constructor\",\n    \"__has_trivial_destructor\",\n    \"__has_virtual_destructor\",\n    \"__is_abstract\",\n    \"__is_base_of\",\n    \"__is_class\",\n    \"__is_convertible_to\",\n    \"__is_empty\",\n    \"__is_enum\",\n    \"__is_final\",\n    \"__is_literal\",\n    \"__is_literal_type\",\n    \"__is_pod\",\n    \"__is_polymorphic\",\n    \"__is_trivial\",\n    \"__is_union\",\n    \"__is_trivially_constructible\",\n    \"__is_trivially_copyable\",\n    \"__is_trivially_assignable\",\n    \"__underlying_type\",\n    \"__is_lvalue_expr\",\n    \"__is_rvalue_expr\",\n    \"__is_arithmetic\",\n    \"__is_floating_point\",\n    \"__is_integral\",\n    \"__is_complete_type\",\n    \"__is_void\",\n    \"__is_array\",\n    \"__is_function\",\n    \"__is_reference\",\n    \"__is_lvalue_reference\",\n    \"__is_rvalue_reference\",\n    \"__is_fundamental\",\n    \"__is_object\",\n    \"__is_scalar\",\n    \"__is_compound\",\n    \"__is_pointer\",\n    \"__is_member_object_pointer\",\n    \"__is_member_function_pointer\",\n    \"__is_member_pointer\",\n    \"__is_const\",\n    \"__is_volatile\",\n    \"__is_standard_layout\",\n    \"__is_signed\",\n    \"__is_unsigned\",\n    \"__is_same\",\n    \"__is_convertible\",\n    \"__array_rank\",\n    \"__array_extent\",\n    \"__private_extern__\",\n    \"__module_private__\",\n    \"__declspec\",\n    \"__cdecl\",\n    \"__stdcall\",\n    \"__fastcall\",\n    \"__thiscall\",\n    \"__vectorcall\",\n    \"cbuffer\",\n    \"tbuffer\",\n    \"packoffset\",\n    \"linear\",\n    \"centroid\",\n    \"nointerpolation\",\n    \"noperspective\",\n    \"sample\",\n    \"column_major\",\n    \"row_major\",\n    \"in\",\n    \"out\",\n    \"inout\",\n    \"uniform\",\n    \"precise\",\n    \"center\",\n    \"shared\",\n    \"groupshared\",\n    \"discard\",\n    \"snorm\",\n    \"unorm\",\n    \"point\",\n    \"line\",\n    \"lineadj\",\n    \"triangle\",\n    \"triangleadj\",\n    \"globallycoherent\",\n    \"interface\",\n    \"sampler_state\",\n    \"technique\",\n    \"indices\",\n    \"vertices\",\n    \"primitives\",\n    \"payload\",\n    \"Technique\",\n    \"technique10\",\n    \"technique11\",\n    \"__builtin_omp_required_simd_align\",\n    \"__pascal\",\n    \"__fp16\",\n    \"__alignof__\",\n    \"__asm\",\n    \"__asm__\",\n    \"__attribute__\",\n    \"__complex\",\n    \"__complex__\",\n    \"__const\",\n    \"__const__\",\n    \"__decltype\",\n    \"__imag__\",\n    \"__inline\",\n    \"__inline__\",\n    \"__nullptr\",\n    \"__real__\",\n    \"__restrict\",\n    \"__restrict__\",\n    \"__signed\",\n    \"__signed__\",\n    \"__typeof\",\n    \"__typeof__\",\n    \"__volatile\",\n    \"__volatile__\",\n    \"_Nonnull\",\n    \"_Nullable\",\n    \"_Null_unspecified\",\n    \"__builtin_convertvector\",\n    \"__char16_t\",\n    \"__char32_t\",\n    // DXC intrinsics, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/utils/hct/gen_intrin_main.txt#L86-L376\n    \"D3DCOLORtoUBYTE4\",\n    \"GetRenderTargetSampleCount\",\n    \"GetRenderTargetSamplePosition\",\n    \"abort\",\n    \"abs\",\n    \"acos\",\n    \"all\",\n    \"AllMemoryBarrier\",\n    \"AllMemoryBarrierWithGroupSync\",\n    \"any\",\n    \"asdouble\",\n    \"asfloat\",\n    \"asfloat16\",\n    \"asint16\",\n    \"asin\",\n    \"asint\",\n    \"asuint\",\n    \"asuint16\",\n    \"atan\",\n    \"atan2\",\n    \"ceil\",\n    \"clamp\",\n    \"clip\",\n    \"cos\",\n    \"cosh\",\n    \"countbits\",\n    \"cross\",\n    \"ddx\",\n    \"ddx_coarse\",\n    \"ddx_fine\",\n    \"ddy\",\n    \"ddy_coarse\",\n    \"ddy_fine\",\n    \"degrees\",\n    \"determinant\",\n    \"DeviceMemoryBarrier\",\n    \"DeviceMemoryBarrierWithGroupSync\",\n    \"distance\",\n    \"dot\",\n    \"dst\",\n    \"EvaluateAttributeAtSample\",\n    \"EvaluateAttributeCentroid\",\n    \"EvaluateAttributeSnapped\",\n    \"GetAttributeAtVertex\",\n    \"exp\",\n    \"exp2\",\n    \"f16tof32\",\n    \"f32tof16\",\n    \"faceforward\",\n    \"firstbithigh\",\n    \"firstbitlow\",\n    \"floor\",\n    \"fma\",\n    \"fmod\",\n    \"frac\",\n    \"frexp\",\n    \"fwidth\",\n    \"GroupMemoryBarrier\",\n    \"GroupMemoryBarrierWithGroupSync\",\n    \"InterlockedAdd\",\n    \"InterlockedMin\",\n    \"InterlockedMax\",\n    \"InterlockedAnd\",\n    \"InterlockedOr\",\n    \"InterlockedXor\",\n    \"InterlockedCompareStore\",\n    \"InterlockedExchange\",\n    \"InterlockedCompareExchange\",\n    \"InterlockedCompareStoreFloatBitwise\",\n    \"InterlockedCompareExchangeFloatBitwise\",\n    \"isfinite\",\n    \"isinf\",\n    \"isnan\",\n    \"ldexp\",\n    \"length\",\n    \"lerp\",\n    \"lit\",\n    \"log\",\n    \"log10\",\n    \"log2\",\n    \"mad\",\n    \"max\",\n    \"min\",\n    \"modf\",\n    \"msad4\",\n    \"mul\",\n    \"normalize\",\n    \"pow\",\n    \"printf\",\n    \"Process2DQuadTessFactorsAvg\",\n    \"Process2DQuadTessFactorsMax\",\n    \"Process2DQuadTessFactorsMin\",\n    \"ProcessIsolineTessFactors\",\n    \"ProcessQuadTessFactorsAvg\",\n    \"ProcessQuadTessFactorsMax\",\n    \"ProcessQuadTessFactorsMin\",\n    \"ProcessTriTessFactorsAvg\",\n    \"ProcessTriTessFactorsMax\",\n    \"ProcessTriTessFactorsMin\",\n    \"radians\",\n    \"rcp\",\n    \"reflect\",\n    \"refract\",\n    \"reversebits\",\n    \"round\",\n    \"rsqrt\",\n    \"saturate\",\n    \"sign\",\n    \"sin\",\n    \"sincos\",\n    \"sinh\",\n    \"smoothstep\",\n    \"source_mark\",\n    \"sqrt\",\n    \"step\",\n    \"tan\",\n    \"tanh\",\n    \"tex1D\",\n    \"tex1Dbias\",\n    \"tex1Dgrad\",\n    \"tex1Dlod\",\n    \"tex1Dproj\",\n    \"tex2D\",\n    \"tex2Dbias\",\n    \"tex2Dgrad\",\n    \"tex2Dlod\",\n    \"tex2Dproj\",\n    \"tex3D\",\n    \"tex3Dbias\",\n    \"tex3Dgrad\",\n    \"tex3Dlod\",\n    \"tex3Dproj\",\n    \"texCUBE\",\n    \"texCUBEbias\",\n    \"texCUBEgrad\",\n    \"texCUBElod\",\n    \"texCUBEproj\",\n    \"transpose\",\n    \"trunc\",\n    \"CheckAccessFullyMapped\",\n    \"AddUint64\",\n    \"NonUniformResourceIndex\",\n    \"WaveIsFirstLane\",\n    \"WaveGetLaneIndex\",\n    \"WaveGetLaneCount\",\n    \"WaveActiveAnyTrue\",\n    \"WaveActiveAllTrue\",\n    \"WaveActiveAllEqual\",\n    \"WaveActiveBallot\",\n    \"WaveReadLaneAt\",\n    \"WaveReadLaneFirst\",\n    \"WaveActiveCountBits\",\n    \"WaveActiveSum\",\n    \"WaveActiveProduct\",\n    \"WaveActiveBitAnd\",\n    \"WaveActiveBitOr\",\n    \"WaveActiveBitXor\",\n    \"WaveActiveMin\",\n    \"WaveActiveMax\",\n    \"WavePrefixCountBits\",\n    \"WavePrefixSum\",\n    \"WavePrefixProduct\",\n    \"WaveMatch\",\n    \"WaveMultiPrefixBitAnd\",\n    \"WaveMultiPrefixBitOr\",\n    \"WaveMultiPrefixBitXor\",\n    \"WaveMultiPrefixCountBits\",\n    \"WaveMultiPrefixProduct\",\n    \"WaveMultiPrefixSum\",\n    \"QuadReadLaneAt\",\n    \"QuadReadAcrossX\",\n    \"QuadReadAcrossY\",\n    \"QuadReadAcrossDiagonal\",\n    \"QuadAny\",\n    \"QuadAll\",\n    \"TraceRay\",\n    \"ReportHit\",\n    \"CallShader\",\n    \"IgnoreHit\",\n    \"AcceptHitAndEndSearch\",\n    \"DispatchRaysIndex\",\n    \"DispatchRaysDimensions\",\n    \"WorldRayOrigin\",\n    \"WorldRayDirection\",\n    \"ObjectRayOrigin\",\n    \"ObjectRayDirection\",\n    \"RayTMin\",\n    \"RayTCurrent\",\n    \"PrimitiveIndex\",\n    \"InstanceID\",\n    \"InstanceIndex\",\n    \"GeometryIndex\",\n    \"HitKind\",\n    \"RayFlags\",\n    \"ObjectToWorld\",\n    \"WorldToObject\",\n    \"ObjectToWorld3x4\",\n    \"WorldToObject3x4\",\n    \"ObjectToWorld4x3\",\n    \"WorldToObject4x3\",\n    \"dot4add_u8packed\",\n    \"dot4add_i8packed\",\n    \"dot2add\",\n    \"unpack_s8s16\",\n    \"unpack_u8u16\",\n    \"unpack_s8s32\",\n    \"unpack_u8u32\",\n    \"pack_s8\",\n    \"pack_u8\",\n    \"pack_clamp_s8\",\n    \"pack_clamp_u8\",\n    \"SetMeshOutputCounts\",\n    \"DispatchMesh\",\n    \"IsHelperLane\",\n    \"AllocateRayQuery\",\n    \"CreateResourceFromHeap\",\n    \"and\",\n    \"or\",\n    \"select\",\n    // DXC resource and other types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/HlslTypes.cpp#L441-#L572\n    \"InputPatch\",\n    \"OutputPatch\",\n    \"PointStream\",\n    \"LineStream\",\n    \"TriangleStream\",\n    \"Texture1D\",\n    \"RWTexture1D\",\n    \"Texture2D\",\n    \"RWTexture2D\",\n    \"Texture2DMS\",\n    \"RWTexture2DMS\",\n    \"Texture3D\",\n    \"RWTexture3D\",\n    \"TextureCube\",\n    \"RWTextureCube\",\n    \"Texture1DArray\",\n    \"RWTexture1DArray\",\n    \"Texture2DArray\",\n    \"RWTexture2DArray\",\n    \"Texture2DMSArray\",\n    \"RWTexture2DMSArray\",\n    \"TextureCubeArray\",\n    \"RWTextureCubeArray\",\n    \"FeedbackTexture2D\",\n    \"FeedbackTexture2DArray\",\n    \"RasterizerOrderedTexture1D\",\n    \"RasterizerOrderedTexture2D\",\n    \"RasterizerOrderedTexture3D\",\n    \"RasterizerOrderedTexture1DArray\",\n    \"RasterizerOrderedTexture2DArray\",\n    \"RasterizerOrderedBuffer\",\n    \"RasterizerOrderedByteAddressBuffer\",\n    \"RasterizerOrderedStructuredBuffer\",\n    \"ByteAddressBuffer\",\n    \"RWByteAddressBuffer\",\n    \"StructuredBuffer\",\n    \"RWStructuredBuffer\",\n    \"AppendStructuredBuffer\",\n    \"ConsumeStructuredBuffer\",\n    \"Buffer\",\n    \"RWBuffer\",\n    \"SamplerState\",\n    \"SamplerComparisonState\",\n    \"ConstantBuffer\",\n    \"TextureBuffer\",\n    \"RaytracingAccelerationStructure\",\n    // DXC templated types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp\n    // look for `BuiltinTypeDeclBuilder`\n    \"matrix\",\n    \"vector\",\n    \"TextureBuffer\",\n    \"ConstantBuffer\",\n    \"RayQuery\",\n    \"RayDesc\",\n    // Naga utilities\n    super::writer::MODF_FUNCTION,\n    super::writer::FREXP_FUNCTION,\n    super::writer::EXTRACT_BITS_FUNCTION,\n    super::writer::INSERT_BITS_FUNCTION,\n    super::writer::SAMPLER_HEAP_VAR,\n    super::writer::COMPARISON_SAMPLER_HEAP_VAR,\n    super::writer::SAMPLE_EXTERNAL_TEXTURE_FUNCTION,\n    super::writer::ABS_FUNCTION,\n    super::writer::DIV_FUNCTION,\n    super::writer::MOD_FUNCTION,\n    super::writer::NEG_FUNCTION,\n    super::writer::F2I32_FUNCTION,\n    super::writer::F2U32_FUNCTION,\n    super::writer::F2I64_FUNCTION,\n    super::writer::F2U64_FUNCTION,\n    super::writer::IMAGE_LOAD_EXTERNAL_FUNCTION,\n    super::writer::IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION,\n];\n\n// DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254\n// + vector and matrix shorthands\npub const TYPES: &[&str] = &{\n    const L: usize = 23 * (1 + 4 + 4 * 4);\n    let mut res = [\"\"; L];\n    let mut c = 0;\n\n    /// For each scalar type, it will additionally generate vector and matrix shorthands\n    macro_rules! generate {\n        ([$($roots:literal),*], $x:tt) => {\n            $(\n                generate!(@inner push $roots);\n                generate!(@inner $roots, $x);\n            )*\n        };\n\n        (@inner $root:literal, [$($x:literal),*]) => {\n            generate!(@inner vector $root, $($x)*);\n            generate!(@inner matrix $root, $($x)*);\n        };\n\n        (@inner vector $root:literal, $($x:literal)*) => {\n            $(\n                generate!(@inner push concat!($root, $x));\n            )*\n        };\n\n        (@inner matrix $root:literal, $($x:literal)*) => {\n            // Duplicate the list\n            generate!(@inner matrix $root, $($x)*; $($x)*);\n        };\n\n        // The head/tail recursion: pick the first element of the first list and recursively do it for the tail.\n        (@inner matrix $root:literal, $head:literal $($tail:literal)*; $($x:literal)*) => {\n            $(\n                generate!(@inner push concat!($root, $head, \"x\", $x));\n            )*\n            generate!(@inner matrix $root, $($tail)*; $($x)*);\n\n        };\n\n        // The end of iteration: we exhausted the list\n        (@inner matrix $root:literal, ; $($x:literal)*) => {};\n\n        (@inner push $v:expr) => {\n            res[c] = $v;\n            c += 1;\n        };\n    }\n\n    generate!(\n        [\n            \"bool\",\n            \"int\",\n            \"uint\",\n            \"dword\",\n            \"half\",\n            \"float\",\n            \"double\",\n            \"min10float\",\n            \"min16float\",\n            \"min12int\",\n            \"min16int\",\n            \"min16uint\",\n            \"int16_t\",\n            \"int32_t\",\n            \"int64_t\",\n            \"uint16_t\",\n            \"uint32_t\",\n            \"uint64_t\",\n            \"float16_t\",\n            \"float32_t\",\n            \"float64_t\",\n            \"int8_t4_packed\",\n            \"uint8_t4_packed\"\n        ],\n        [\"1\", \"2\", \"3\", \"4\"]\n    );\n\n    debug_assert!(c == L);\n\n    res\n};\n\n/// The above set of reserved keywords, turned into a cached HashSet. This saves\n/// significant time during [`Namer::reset`](crate::proc::Namer::reset).\n///\n/// See <https://github.com/gfx-rs/wgpu/pull/7338> for benchmarks.\npub static RESERVED_SET: RacyLock<KeywordSet> =\n    RacyLock::new(|| KeywordSet::from_iter(RESERVED.iter().chain(TYPES)));\n\npub static RESERVED_CASE_INSENSITIVE_SET: RacyLock<CaseInsensitiveKeywordSet> =\n    RacyLock::new(|| CaseInsensitiveKeywordSet::from_iter(RESERVED_CASE_INSENSITIVE));\n\npub const RESERVED_PREFIXES: &[&str] = &[\n    \"__dynamic_buffer_offsets\",\n    super::help::IMAGE_STORAGE_LOAD_SCALAR_WRAPPER,\n    super::writer::RAY_QUERY_TRACKER_VARIABLE_PREFIX,\n    super::writer::INTERNAL_PREFIX,\n];\n"
  },
  {
    "path": "naga/src/back/hlsl/mod.rs",
    "content": "/*!\nBackend for [HLSL][hlsl] (High-Level Shading Language).\n\n# Supported shader model versions:\n- 5.0\n- 5.1\n- 6.0\n\n# Layout of values in `uniform` buffers\n\nWGSL's [\"Internal Layout of Values\"][ilov] rules specify how each WGSL\ntype should be stored in `uniform` and `storage` buffers. The HLSL we\ngenerate must access values in that form, even when it is not what\nHLSL would use normally.\n\nMatching the WGSL memory layout is a concern only for `uniform`\nvariables. WGSL `storage` buffers are translated as HLSL\n`ByteAddressBuffers`, for which we generate `Load` and `Store` method\ncalls with explicit byte offsets. WGSL pipeline inputs must be scalars\nor vectors; they cannot be matrices, which is where the interesting\nproblems arise. However, when an affected type appears in a struct\ndefinition, the transformations described here are applied without\nconsideration of where the struct is used.\n\nAccess to storage buffers is implemented in `storage.rs`. Access to\nuniform buffers is implemented where applicable in `writer.rs`.\n\n## Row- and column-major ordering for matrices\n\nWGSL specifies that matrices in uniform buffers are stored in\ncolumn-major order. This matches HLSL's default, so one might expect\nthings to be straightforward. Unfortunately, WGSL and HLSL disagree on\nwhat indexing a matrix means: in WGSL, `m[i]` retrieves the `i`'th\n*column* of `m`, whereas in HLSL it retrieves the `i`'th *row*. We\nwant to avoid translating `m[i]` into some complicated reassembly of a\nvector from individually fetched components, so this is a problem.\n\nHowever, with a bit of trickery, it is possible to use HLSL's `m[i]`\nas the translation of WGSL's `m[i]`:\n\n- We declare all matrices in uniform buffers in HLSL with the\n  `row_major` qualifier, and transpose the row and column counts: a\n  WGSL `mat3x4<f32>`, say, becomes an HLSL `row_major float3x4`. (Note\n  that WGSL and HLSL type names put the row and column in reverse\n  order.) Since the HLSL type is the transpose of how WebGPU directs\n  the user to store the data, HLSL will load all matrices transposed.\n\n- Since matrices are transposed, an HLSL indexing expression retrieves\n  the \"columns\" of the intended WGSL value, as desired.\n\n- For vector-matrix multiplication, since `mul(transpose(m), v)` is\n  equivalent to `mul(v, m)` (note the reversal of the arguments), and\n  `mul(v, transpose(m))` is equivalent to `mul(m, v)`, we can\n  translate WGSL `m * v` and `v * m` to HLSL by simply reversing the\n  arguments to `mul`.\n\n## Padding in two-row matrices\n\nAn HLSL `row_major floatKx2` matrix has padding between its rows that\nthe WGSL `matKx2<f32>` matrix it represents does not. HLSL stores all\nmatrix rows [aligned on 16-byte boundaries][16bb], whereas WGSL says\nthat the columns of a `matKx2<f32>` need only be [aligned as required\nfor `vec2<f32>`][ilov], which is [eight-byte alignment][8bb].\n\nTo compensate for this, any time a `matKx2<f32>` appears in a WGSL\n`uniform` value or as part of a struct/array, we actually emit `K`\nseparate `float2` members, and assemble/disassemble the matrix from its\ncolumns (in WGSL; rows in HLSL) upon load and store.\n\nFor example, the following WGSL struct type:\n\n```ignore\nstruct Baz {\n        m: mat3x2<f32>,\n}\n```\n\nis rendered as the HLSL struct type:\n\n```ignore\nstruct Baz {\n    float2 m_0; float2 m_1; float2 m_2;\n};\n```\n\nThe `wrapped_struct_matrix` functions in `help.rs` generate HLSL\nhelper functions to access such members, converting between the stored\nform and the HLSL matrix types appropriately. For example, for reading\nthe member `m` of the `Baz` struct above, we emit:\n\n```ignore\nfloat3x2 GetMatmOnBaz(Baz obj) {\n    return float3x2(obj.m_0, obj.m_1, obj.m_2);\n}\n```\n\nWe also emit an analogous `Set` function, as well as functions for\naccessing individual columns by dynamic index.\n\n## Sampler Handling\n\nDue to limitations in how sampler heaps work in D3D12, we need to access samplers\nthrough a layer of indirection. Instead of directly binding samplers, we bind the entire\nsampler heap as both a standard and a comparison sampler heap. We then use a sampler\nindex buffer for each bind group. This buffer is accessed in the shader to get the actual\nsampler index within the heap. See the wgpu_hal dx12 backend documentation for more\ninformation.\n\n# External textures\n\nSupport for [`crate::ImageClass::External`] textures is implemented by lowering\neach external texture global variable to 3 `Texture2D<float4>`s, and a `cbuffer`\nof type `NagaExternalTextureParams`. This provides up to 3 planes of texture\ndata (for example single planar RGBA, or separate Y, Cb, and Cr planes), and the\nparameters buffer containing information describing how to handle these\ncorrectly. The bind target to use for each of these globals is specified via\n[`Options::external_texture_binding_map`].\n\nExternal textures are supported by WGSL's `textureDimensions()`,\n`textureLoad()`, and `textureSampleBaseClampToEdge()` built-in functions. These\nare implemented using helper functions. See the following functions for how\nthese are generated:\n * `Writer::write_wrapped_image_query_function`\n * `Writer::write_wrapped_image_load_function`\n * `Writer::write_wrapped_image_sample_function`\n\nIdeally the set of global variables could be wrapped in a single struct that\ncould conveniently be passed around. But, alas, HLSL does not allow structs to\nhave `Texture2D` members. Fortunately, however, external textures can only be\nused as arguments to either built-in or user-defined functions. We therefore\nexpand any external texture function argument to four consecutive arguments (3\ntextures and the params struct) when declaring user-defined functions, and\nensure our built-in function implementations take the same arguments. Then,\nwhenever we need to emit an external texture in `Writer::write_expr`, which\nfortunately can only ever be for a global variable or function argument, we\nsimply emit the variable name of each of the three textures and the parameters\nstruct in a comma-separated list. This won't win any awards for elegance, but\nit works for our purposes.\n\n[hlsl]: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl\n[ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout\n[16bb]: https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#constant-buffer-packing\n[8bb]: https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size\n*/\n\nmod conv;\nmod help;\nmod keywords;\nmod ray;\nmod storage;\nmod writer;\n\nuse alloc::{string::String, vec::Vec};\nuse core::fmt::Error as FmtError;\n\nuse thiserror::Error;\n\nuse crate::{back, ir, proc};\n\n/// Direct3D 12 binding information for a global variable.\n///\n/// This type provides the HLSL-specific information Naga needs to declare and\n/// access an HLSL global variable that cannot be derived from the `Module`\n/// itself.\n///\n/// An HLSL global variable declaration includes details that the Direct3D API\n/// will use to refer to it. For example:\n///\n///    RWByteAddressBuffer s_sasm : register(u0, space2);\n///\n/// This defines a global `s_sasm` that a Direct3D root signature would refer to\n/// as register `0` in register space `2` in a `UAV` descriptor range. Naga can\n/// infer the register's descriptor range type from the variable's address class\n/// (writable [`Storage`] variables are implemented by Direct3D Unordered Access\n/// Views, the `u` register type), but the register number and register space\n/// must be supplied by the user.\n///\n/// The [`back::hlsl::Options`] structure provides `BindTarget`s for various\n/// situations in which Naga may need to generate an HLSL global variable, like\n/// [`binding_map`] for Naga global variables, or [`immediates_target`] for\n/// a module's sole [`Immediate`] variable. See those fields' documentation\n/// for details.\n///\n/// [`Storage`]: crate::ir::AddressSpace::Storage\n/// [`back::hlsl::Options`]: Options\n/// [`binding_map`]: Options::binding_map\n/// [`immediates_target`]: Options::immediates_target\n/// [`Immediate`]: crate::ir::AddressSpace::Immediate\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct BindTarget {\n    pub space: u8,\n    /// For regular bindings this is the register number.\n    ///\n    /// For sampler bindings, this is the index to use into the bind group's sampler index buffer.\n    pub register: u32,\n    /// If the binding is an unsized binding array, this overrides the size.\n    pub binding_array_size: Option<u32>,\n    /// This is the index in the buffer at [`Options::dynamic_storage_buffer_offsets_targets`].\n    pub dynamic_storage_buffer_offsets_index: Option<u32>,\n    /// This is a hint that we need to restrict indexing of vectors, matrices and arrays.\n    ///\n    /// If [`Options::restrict_indexing`] is also `true`, we will restrict indexing.\n    #[cfg_attr(any(feature = \"serialize\", feature = \"deserialize\"), serde(default))]\n    pub restrict_indexing: bool,\n}\n\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n/// BindTarget for dynamic storage buffer offsets\npub struct OffsetsBindTarget {\n    pub space: u8,\n    pub register: u32,\n    pub size: u32,\n}\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct BindingMapSerialization {\n    resource_binding: crate::ResourceBinding,\n    bind_target: BindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;\n    let mut map = BindingMap::default();\n    for item in vec {\n        map.insert(item.resource_binding, item.bind_target);\n    }\n    Ok(map)\n}\n\n// Using `BTreeMap` instead of `HashMap` so that we can hash itself.\npub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindTarget>;\n\n/// A HLSL shader model version.\n#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum ShaderModel {\n    V5_0,\n    V5_1,\n    V6_0,\n    V6_1,\n    V6_2,\n    V6_3,\n    V6_4,\n    V6_5,\n    V6_6,\n    V6_7,\n    V6_8,\n    V6_9,\n}\n\nimpl ShaderModel {\n    pub const fn to_str(self) -> &'static str {\n        match self {\n            Self::V5_0 => \"5_0\",\n            Self::V5_1 => \"5_1\",\n            Self::V6_0 => \"6_0\",\n            Self::V6_1 => \"6_1\",\n            Self::V6_2 => \"6_2\",\n            Self::V6_3 => \"6_3\",\n            Self::V6_4 => \"6_4\",\n            Self::V6_5 => \"6_5\",\n            Self::V6_6 => \"6_6\",\n            Self::V6_7 => \"6_7\",\n            Self::V6_8 => \"6_8\",\n            Self::V6_9 => \"6_9\",\n        }\n    }\n}\n\nimpl crate::ShaderStage {\n    pub const fn to_hlsl_str(self) -> &'static str {\n        match self {\n            Self::Vertex => \"vs\",\n            Self::Fragment => \"ps\",\n            Self::Compute => \"cs\",\n            Self::Task => \"as\",\n            Self::Mesh => \"ms\",\n            Self::RayGeneration | Self::AnyHit | Self::ClosestHit | Self::Miss => \"lib\",\n        }\n    }\n}\n\nimpl crate::ImageDimension {\n    const fn to_hlsl_str(self) -> &'static str {\n        match self {\n            Self::D1 => \"1D\",\n            Self::D2 => \"2D\",\n            Self::D3 => \"3D\",\n            Self::Cube => \"Cube\",\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct SamplerIndexBufferKey {\n    pub group: u32,\n}\n\n#[derive(Clone, Debug, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct SamplerHeapBindTargets {\n    pub standard_samplers: BindTarget,\n    pub comparison_samplers: BindTarget,\n}\n\nimpl Default for SamplerHeapBindTargets {\n    fn default() -> Self {\n        Self {\n            standard_samplers: BindTarget {\n                space: 0,\n                register: 0,\n                binding_array_size: None,\n                dynamic_storage_buffer_offsets_index: None,\n                restrict_indexing: false,\n            },\n            comparison_samplers: BindTarget {\n                space: 1,\n                register: 0,\n                binding_array_size: None,\n                dynamic_storage_buffer_offsets_index: None,\n                restrict_indexing: false,\n            },\n        }\n    }\n}\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct SamplerIndexBufferBindingSerialization {\n    group: u32,\n    bind_target: BindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_sampler_index_buffer_bindings<'de, D>(\n    deserializer: D,\n) -> Result<SamplerIndexBufferBindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<SamplerIndexBufferBindingSerialization>::deserialize(deserializer)?;\n    let mut map = SamplerIndexBufferBindingMap::default();\n    for item in vec {\n        map.insert(\n            SamplerIndexBufferKey { group: item.group },\n            item.bind_target,\n        );\n    }\n    Ok(map)\n}\n\n// We use a BTreeMap here so that we can hash it.\npub type SamplerIndexBufferBindingMap =\n    alloc::collections::BTreeMap<SamplerIndexBufferKey, BindTarget>;\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct DynamicStorageBufferOffsetTargetSerialization {\n    index: u32,\n    bind_target: OffsetsBindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_storage_buffer_offsets<'de, D>(\n    deserializer: D,\n) -> Result<DynamicStorageBufferOffsetsTargets, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<DynamicStorageBufferOffsetTargetSerialization>::deserialize(deserializer)?;\n    let mut map = DynamicStorageBufferOffsetsTargets::default();\n    for item in vec {\n        map.insert(item.index, item.bind_target);\n    }\n    Ok(map)\n}\n\npub type DynamicStorageBufferOffsetsTargets = alloc::collections::BTreeMap<u32, OffsetsBindTarget>;\n\n/// HLSL binding information for a Naga [`External`] image global variable.\n///\n/// See the module documentation's section on [External textures][mod] for details.\n///\n/// [`External`]: crate::ir::ImageClass::External\n/// [mod]: #external-textures\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct ExternalTextureBindTarget {\n    /// HLSL binding information for the individual plane textures.\n    ///\n    /// Each of these should refer to an HLSL `Texture2D<float4>` holding one\n    /// plane of data for the external texture. The exact meaning of each plane\n    /// varies at runtime depending on where the external texture's data\n    /// originated.\n    pub planes: [BindTarget; 3],\n\n    /// HLSL binding information for a buffer holding the sampling parameters.\n    ///\n    /// This should refer to a cbuffer of type `NagaExternalTextureParams`, that\n    /// the code Naga generates for `textureSampleBaseClampToEdge` consults to\n    /// decide how to combine the data in [`planes`] to get the result required\n    /// by the spec.\n    ///\n    /// [`planes`]: Self::planes\n    pub params: BindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct ExternalTextureBindingMapSerialization {\n    resource_binding: crate::ResourceBinding,\n    bind_target: ExternalTextureBindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_external_texture_binding_map<'de, D>(\n    deserializer: D,\n) -> Result<ExternalTextureBindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<ExternalTextureBindingMapSerialization>::deserialize(deserializer)?;\n    let mut map = ExternalTextureBindingMap::default();\n    for item in vec {\n        map.insert(item.resource_binding, item.bind_target);\n    }\n    Ok(map)\n}\npub type ExternalTextureBindingMap =\n    alloc::collections::BTreeMap<crate::ResourceBinding, ExternalTextureBindTarget>;\n\n/// Shorthand result used internally by the backend\ntype BackendResult = Result<(), Error>;\n\n#[derive(Clone, Debug, PartialEq, thiserror::Error)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum EntryPointError {\n    #[error(\"mapping of {0:?} is missing\")]\n    MissingBinding(crate::ResourceBinding),\n}\n\n/// Configuration used in the [`Writer`].\n#[derive(Clone, Debug, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct Options {\n    /// The hlsl shader model to be used\n    pub shader_model: ShaderModel,\n\n    /// HLSL binding information for each Naga global variable.\n    ///\n    /// This maps Naga [`GlobalVariable`]'s [`ResourceBinding`]s to a\n    /// [`BindTarget`] specifying its register number and space, along with\n    /// other details necessary to generate a full HLSL declaration for it,\n    /// or to access its value.\n    ///\n    /// This must provide a [`BindTarget`] for every [`GlobalVariable`] in the\n    /// [`Module`] that has a [`binding`].\n    ///\n    /// [`GlobalVariable`]: crate::ir::GlobalVariable\n    /// [`ResourceBinding`]: crate::ir::ResourceBinding\n    /// [`Module`]: crate::ir::Module\n    /// [`binding`]: crate::ir::GlobalVariable::binding\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_binding_map\")\n    )]\n    pub binding_map: BindingMap,\n\n    /// Don't panic on missing bindings, instead generate any HLSL.\n    pub fake_missing_bindings: bool,\n    /// Add special constants to `SV_VertexIndex` and `SV_InstanceIndex`,\n    /// to make them work like in Vulkan/Metal, with help of the host.\n    pub special_constants_binding: Option<BindTarget>,\n\n    /// HLSL binding information for the [`Immediate`] global, if present.\n    ///\n    /// If a module contains a global in the [`Immediate`] address space, the\n    /// `dx12` backend stores its value directly in the root signature as a\n    /// series of [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`], whose binding\n    /// information is given here.\n    ///\n    /// [`Immediate`]: crate::ir::AddressSpace::Immediate\n    /// [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_root_parameter_type\n    pub immediates_target: Option<BindTarget>,\n\n    /// HLSL binding information for the sampler heap and comparison sampler heap.\n    pub sampler_heap_target: SamplerHeapBindTargets,\n\n    /// Mapping of each bind group's sampler index buffer to a bind target.\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_sampler_index_buffer_bindings\")\n    )]\n    pub sampler_buffer_binding_map: SamplerIndexBufferBindingMap,\n    /// Bind target for dynamic storage buffer offsets\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_storage_buffer_offsets\")\n    )]\n    pub dynamic_storage_buffer_offsets_targets: DynamicStorageBufferOffsetsTargets,\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_external_texture_binding_map\")\n    )]\n\n    /// HLSL binding information for [`External`] image global variables.\n    ///\n    /// See [`ExternalTextureBindTarget`] for details.\n    ///\n    /// [`External`]: crate::ir::ImageClass::External\n    pub external_texture_binding_map: ExternalTextureBindingMap,\n\n    /// Should workgroup variables be zero initialized (by polyfilling)?\n    pub zero_initialize_workgroup_memory: bool,\n    /// Should we restrict indexing of vectors, matrices and arrays?\n    pub restrict_indexing: bool,\n    /// If set, loops will have code injected into them, forcing the compiler\n    /// to think the number of iterations is bounded.\n    pub force_loop_bounding: bool,\n    /// if set, ray queries will get a variable to track their state to prevent\n    /// misuse.\n    pub ray_query_initialization_tracking: bool,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Options {\n            shader_model: ShaderModel::V5_1,\n            binding_map: BindingMap::default(),\n            fake_missing_bindings: true,\n            special_constants_binding: None,\n            sampler_heap_target: SamplerHeapBindTargets::default(),\n            sampler_buffer_binding_map: alloc::collections::BTreeMap::default(),\n            immediates_target: None,\n            dynamic_storage_buffer_offsets_targets: alloc::collections::BTreeMap::new(),\n            external_texture_binding_map: ExternalTextureBindingMap::default(),\n            zero_initialize_workgroup_memory: true,\n            restrict_indexing: true,\n            force_loop_bounding: true,\n            ray_query_initialization_tracking: true,\n        }\n    }\n}\n\nimpl Options {\n    fn resolve_resource_binding(\n        &self,\n        res_binding: &crate::ResourceBinding,\n    ) -> Result<BindTarget, EntryPointError> {\n        match self.binding_map.get(res_binding) {\n            Some(target) => Ok(*target),\n            None if self.fake_missing_bindings => Ok(BindTarget {\n                space: res_binding.group as u8,\n                register: res_binding.binding,\n                binding_array_size: None,\n                dynamic_storage_buffer_offsets_index: None,\n                restrict_indexing: false,\n            }),\n            None => Err(EntryPointError::MissingBinding(*res_binding)),\n        }\n    }\n\n    fn resolve_external_texture_resource_binding(\n        &self,\n        res_binding: &crate::ResourceBinding,\n    ) -> Result<ExternalTextureBindTarget, EntryPointError> {\n        match self.external_texture_binding_map.get(res_binding) {\n            Some(target) => Ok(*target),\n            None if self.fake_missing_bindings => {\n                let fake = BindTarget {\n                    space: res_binding.group as u8,\n                    register: res_binding.binding,\n                    binding_array_size: None,\n                    dynamic_storage_buffer_offsets_index: None,\n                    restrict_indexing: false,\n                };\n                Ok(ExternalTextureBindTarget {\n                    planes: [fake, fake, fake],\n                    params: fake,\n                })\n            }\n            None => Err(EntryPointError::MissingBinding(*res_binding)),\n        }\n    }\n}\n\n/// Reflection info for entry point names.\n#[derive(Default)]\npub struct ReflectionInfo {\n    /// Mapping of the entry point names.\n    ///\n    /// Each item in the array corresponds to an entry point index. The real entry point name may be different if one of the\n    /// reserved words are used.\n    ///\n    /// Note: Some entry points may fail translation because of missing bindings.\n    pub entry_point_names: Vec<Result<String, EntryPointError>>,\n}\n\n/// A subset of options that are meant to be changed per pipeline.\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct PipelineOptions {\n    /// The entry point to write.\n    ///\n    /// Entry points are identified by a shader stage specification,\n    /// and a name.\n    ///\n    /// If `None`, all entry points will be written. If `Some` and the entry\n    /// point is not found, an error will be thrown while writing.\n    pub entry_point: Option<(ir::ShaderStage, String)>,\n}\n\n#[derive(Error, Debug)]\npub enum Error {\n    #[error(transparent)]\n    IoError(#[from] FmtError),\n    #[error(\"A scalar with an unsupported width was requested: {0:?}\")]\n    UnsupportedScalar(crate::Scalar),\n    #[error(\"{0}\")]\n    Unimplemented(String), // TODO: Error used only during development\n    #[error(\"{0}\")]\n    Custom(String),\n    #[error(\"overrides should not be present at this stage\")]\n    Override,\n    #[error(transparent)]\n    ResolveArraySizeError(#[from] proc::ResolveArraySizeError),\n    #[error(\"entry point with stage {0:?} and name '{1}' not found\")]\n    EntryPointNotFound(ir::ShaderStage, String),\n    #[error(\"requires shader model {1:?} for reason: {0}\")]\n    ShaderModelTooLow(String, ShaderModel),\n}\n\n#[derive(PartialEq, Eq, Hash)]\nenum WrappedType {\n    ZeroValue(help::WrappedZeroValue),\n    ArrayLength(help::WrappedArrayLength),\n    ImageSample(help::WrappedImageSample),\n    ImageQuery(help::WrappedImageQuery),\n    ImageLoad(help::WrappedImageLoad),\n    ImageLoadScalar(crate::Scalar),\n    Constructor(help::WrappedConstructor),\n    StructMatrixAccess(help::WrappedStructMatrixAccess),\n    MatCx2(help::WrappedMatCx2),\n    Math(help::WrappedMath),\n    UnaryOp(help::WrappedUnaryOp),\n    BinaryOp(help::WrappedBinaryOp),\n    Cast(help::WrappedCast),\n}\n\n#[derive(Default)]\nstruct Wrapped {\n    types: crate::FastHashSet<WrappedType>,\n    /// If true, the sampler heaps have been written out.\n    sampler_heaps: bool,\n    // Mapping from SamplerIndexBufferKey to the name the namer returned.\n    sampler_index_buffers: crate::FastHashMap<SamplerIndexBufferKey, String>,\n}\n\nimpl Wrapped {\n    fn insert(&mut self, r#type: WrappedType) -> bool {\n        self.types.insert(r#type)\n    }\n\n    fn clear(&mut self) {\n        self.types.clear();\n    }\n}\n\n/// A fragment entry point to be considered when generating HLSL for the output interface of vertex\n/// entry points.\n///\n/// This is provided as an optional parameter to [`Writer::write`].\n///\n/// If this is provided, vertex outputs will be removed if they are not inputs of this fragment\n/// entry point. This is necessary for generating correct HLSL when some of the vertex shader\n/// outputs are not consumed by the fragment shader.\npub struct FragmentEntryPoint<'a> {\n    module: &'a crate::Module,\n    func: &'a crate::Function,\n}\n\nimpl<'a> FragmentEntryPoint<'a> {\n    /// Returns `None` if the entry point with the provided name can't be found or isn't a fragment\n    /// entry point.\n    pub fn new(module: &'a crate::Module, ep_name: &'a str) -> Option<Self> {\n        module\n            .entry_points\n            .iter()\n            .find(|ep| ep.name == ep_name)\n            .filter(|ep| ep.stage == crate::ShaderStage::Fragment)\n            .map(|ep| Self {\n                module,\n                func: &ep.function,\n            })\n    }\n}\n\npub struct Writer<'a, W> {\n    out: W,\n    names: crate::FastHashMap<proc::NameKey, String>,\n    namer: proc::Namer,\n    /// HLSL backend options\n    options: &'a Options,\n    /// Per-stage backend options\n    pipeline_options: &'a PipelineOptions,\n    /// Information about entry point arguments and result types.\n    entry_point_io: crate::FastHashMap<usize, writer::EntryPointInterface>,\n    /// Set of expressions that have associated temporary variables\n    named_expressions: crate::NamedExpressions,\n    wrapped: Wrapped,\n    written_committed_intersection: bool,\n    written_candidate_intersection: bool,\n    continue_ctx: back::continue_forward::ContinueCtx,\n\n    /// A reference to some part of a global variable, lowered to a series of\n    /// byte offset calculations.\n    ///\n    /// See the [`storage`] module for background on why we need this.\n    ///\n    /// Each [`SubAccess`] in the vector is a lowering of some [`Access`] or\n    /// [`AccessIndex`] expression to the level of byte strides and offsets. See\n    /// [`SubAccess`] for details.\n    ///\n    /// This field is a member of [`Writer`] solely to allow re-use of\n    /// the `Vec`'s dynamic allocation. The value is no longer needed\n    /// once HLSL for the access has been generated.\n    ///\n    /// [`Storage`]: crate::AddressSpace::Storage\n    /// [`SubAccess`]: storage::SubAccess\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    temp_access_chain: Vec<storage::SubAccess>,\n    need_bake_expressions: back::NeedBakeExpressions,\n}\n\npub fn supported_capabilities() -> crate::valid::Capabilities {\n    use crate::valid::Capabilities as Caps;\n    Caps::IMMEDIATES\n        | Caps::FLOAT64 // Unsupported by wgpu but supported by naga\n        | Caps::PRIMITIVE_INDEX\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY\n        // No BUFFER_BINDING_ARRAY\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY\n        // No STORAGE_BUFFER_BINDING_ARRAY\n        | Caps::ACCELERATION_STRUCTURE_BINDING_ARRAY\n        // No CLIP_DISTANCES\n        // No CULL_DISTANCE\n        | Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS\n        | Caps::MULTIVIEW\n        // No EARLY_DEPTH_TEST\n        | Caps::MULTISAMPLED_SHADING\n        | Caps::RAY_QUERY\n        | Caps::DUAL_SOURCE_BLENDING\n        | Caps::CUBE_ARRAY_TEXTURES\n        | Caps::SHADER_INT64\n        | Caps::SUBGROUP\n        // No SUBGROUP_BARRIER\n        // No SUBGROUP_VERTEX_STAGE\n        | Caps::SHADER_INT64_ATOMIC_MIN_MAX\n        | Caps::SHADER_INT64_ATOMIC_ALL_OPS\n        // No SHADER_FLOAT32_ATOMIC\n        | Caps::TEXTURE_ATOMIC\n        | Caps::TEXTURE_INT64_ATOMIC\n        // No RAY_HIT_VERTEX_POSITION\n        | Caps::SHADER_FLOAT16\n        | Caps::TEXTURE_EXTERNAL\n        | Caps::SHADER_FLOAT16_IN_FLOAT32\n        | Caps::SHADER_BARYCENTRICS\n        // No MESH_SHADER\n        // No MESH_SHADER_POINT_TOPOLOGY\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        // No BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        // No COOPERATIVE_MATRIX\n        // No PER_VERTEX\n        // No RAY_TRACING_PIPELINE\n        // No DRAW_INDEX\n        // No MEMORY_DECORATION_VOLATILE\n        | Caps::MEMORY_DECORATION_COHERENT\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/ray.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::fmt::Write;\n\nuse crate::{\n    back::{hlsl::BackendResult, Baked, Level},\n    Handle,\n};\nuse crate::{RayQueryIntersection, TypeInner};\n\nimpl<W: Write> super::Writer<'_, W> {\n    // https://sakibsaikia.github.io/graphics/2022/01/04/Nan-Checks-In-HLSL.html suggests that isnan may not work, unsure if this has changed.\n    fn write_not_finite(&mut self, expr: &str) -> BackendResult {\n        self.write_contains_flags(&format!(\"asuint({expr})\"), 0x7f800000)\n    }\n\n    fn write_nan(&mut self, expr: &str) -> BackendResult {\n        write!(self.out, \"(\")?;\n        self.write_not_finite(expr)?;\n        write!(self.out, \" && ((asuint({expr}) & 0x7fffff) != 0))\")?;\n        Ok(())\n    }\n\n    fn write_contains_flags(&mut self, expr: &str, flags: u32) -> BackendResult {\n        write!(self.out, \"(({expr} & {flags}) == {flags})\")?;\n        Ok(())\n    }\n\n    // constructs hlsl RayDesc from wgsl RayDesc\n    pub(super) fn write_ray_desc_from_ray_desc_constructor_function(\n        &mut self,\n        module: &crate::Module,\n    ) -> BackendResult {\n        write!(self.out, \"RayDesc RayDescFromRayDesc_(\")?;\n        self.write_type(module, module.special_types.ray_desc.unwrap())?;\n        writeln!(self.out, \" arg0) {{\")?;\n        writeln!(self.out, \"    RayDesc ret = (RayDesc)0;\")?;\n        writeln!(self.out, \"    ret.Origin = arg0.origin;\")?;\n        writeln!(self.out, \"    ret.TMin = arg0.tmin;\")?;\n        writeln!(self.out, \"    ret.Direction = arg0.dir;\")?;\n        writeln!(self.out, \"    ret.TMax = arg0.tmax;\")?;\n        writeln!(self.out, \"    return ret;\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n    pub(super) fn write_committed_intersection_function(\n        &mut self,\n        module: &crate::Module,\n    ) -> BackendResult {\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        write!(self.out, \" GetCommittedIntersection(\")?;\n        self.write_value_type(\n            module,\n            &TypeInner::RayQuery {\n                vertex_return: false,\n            },\n        )?;\n        write!(self.out, \" rq, \")?;\n        self.write_value_type(module, &TypeInner::Scalar(crate::Scalar::U32))?;\n        writeln!(self.out, \" rq_tracker) {{\")?;\n        write!(self.out, \"    \")?;\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        write!(self.out, \" ret = (\")?;\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        writeln!(self.out, \")0;\")?;\n        let mut extra_level = Level(0);\n        if self.options.ray_query_initialization_tracking {\n            // *Technically*, `CommittedStatus` is valid as long as the ray query is initialized, but the metal backend\n            // doesn't support this function unless it has finished traversal, so to encourage portable behaviour we\n            // disallow it here too.\n            write!(self.out, \"    if (\")?;\n            self.write_contains_flags(\n                \"rq_tracker\",\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            )?;\n            writeln!(self.out, \") {{\")?;\n            extra_level = extra_level.next();\n        }\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.kind = rq.CommittedStatus();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}if( rq.CommittedStatus() == COMMITTED_NOTHING) {{}} else {{\"\n        )?;\n        writeln!(self.out, \"        {extra_level}ret.t = rq.CommittedRayT();\")?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.instance_custom_data = rq.CommittedInstanceID();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.instance_index = rq.CommittedInstanceIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.sbt_record_offset = rq.CommittedInstanceContributionToHitGroupIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.geometry_index = rq.CommittedGeometryIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.primitive_index = rq.CommittedPrimitiveIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}if( rq.CommittedStatus() == COMMITTED_TRIANGLE_HIT ) {{\"\n        )?;\n        writeln!(\n            self.out,\n            \"            {extra_level}ret.barycentrics = rq.CommittedTriangleBarycentrics();\"\n        )?;\n        writeln!(\n            self.out,\n            \"            {extra_level}ret.front_face = rq.CommittedTriangleFrontFace();\"\n        )?;\n        writeln!(self.out, \"        {extra_level}}}\")?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.object_to_world = rq.CommittedObjectToWorld4x3();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.world_to_object = rq.CommittedWorldToObject4x3();\"\n        )?;\n        writeln!(self.out, \"    {extra_level}}}\")?;\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"    }}\")?;\n        }\n        writeln!(self.out, \"    return ret;\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n    pub(super) fn write_candidate_intersection_function(\n        &mut self,\n        module: &crate::Module,\n    ) -> BackendResult {\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        write!(self.out, \" GetCandidateIntersection(\")?;\n        self.write_value_type(\n            module,\n            &TypeInner::RayQuery {\n                vertex_return: false,\n            },\n        )?;\n        write!(self.out, \" rq, \")?;\n        self.write_value_type(module, &TypeInner::Scalar(crate::Scalar::U32))?;\n        writeln!(self.out, \" rq_tracker) {{\")?;\n        write!(self.out, \"    \")?;\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        write!(self.out, \" ret = (\")?;\n        self.write_type(module, module.special_types.ray_intersection.unwrap())?;\n        writeln!(self.out, \")0;\")?;\n        let mut extra_level = Level(0);\n        if self.options.ray_query_initialization_tracking {\n            write!(self.out, \"    if (\")?;\n            self.write_contains_flags(\"rq_tracker\", crate::back::RayQueryPoint::PROCEED.bits())?;\n            write!(self.out, \" && !\")?;\n            self.write_contains_flags(\n                \"rq_tracker\",\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            )?;\n            writeln!(self.out, \") {{\")?;\n            extra_level = extra_level.next();\n        }\n        writeln!(\n            self.out,\n            \"    {extra_level}CANDIDATE_TYPE kind = rq.CandidateType();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}if (kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {{\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.kind = {};\",\n            RayQueryIntersection::Triangle as u32\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.t = rq.CandidateTriangleRayT();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.barycentrics = rq.CandidateTriangleBarycentrics();\"\n        )?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.front_face = rq.CandidateTriangleFrontFace();\"\n        )?;\n        writeln!(self.out, \"    {extra_level}}} else {{\")?;\n        writeln!(\n            self.out,\n            \"        {extra_level}ret.kind = {};\",\n            RayQueryIntersection::Aabb as u32\n        )?;\n        writeln!(self.out, \"    {extra_level}}}\")?;\n\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.instance_custom_data = rq.CandidateInstanceID();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.instance_index = rq.CandidateInstanceIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.sbt_record_offset = rq.CandidateInstanceContributionToHitGroupIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.geometry_index = rq.CandidateGeometryIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.primitive_index = rq.CandidatePrimitiveIndex();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.object_to_world = rq.CandidateObjectToWorld4x3();\"\n        )?;\n        writeln!(\n            self.out,\n            \"    {extra_level}ret.world_to_object = rq.CandidateWorldToObject4x3();\"\n        )?;\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"    }}\")?;\n        }\n        writeln!(self.out, \"    return ret;\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    #[expect(clippy::too_many_arguments)]\n    pub(super) fn write_initialize_function(\n        &mut self,\n        module: &crate::Module,\n        mut level: Level,\n        query: Handle<crate::Expression>,\n        acceleration_structure: Handle<crate::Expression>,\n        descriptor: Handle<crate::Expression>,\n        rq_tracker: &str,\n        func_ctx: &crate::back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let base_level = level;\n\n        // This prevents variables flowing down a level and causing compile errors.\n        writeln!(self.out, \"{level}{{\")?;\n        level = level.next();\n        write!(self.out, \"{level}\")?;\n        self.write_type(\n            module,\n            module\n                .special_types\n                .ray_desc\n                .expect(\"should have been generated\"),\n        )?;\n        write!(self.out, \" naga_desc = \")?;\n        self.write_expr(module, descriptor, func_ctx)?;\n        writeln!(self.out, \";\")?;\n\n        if self.options.ray_query_initialization_tracking {\n            // Validate ray extents https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#ray-extents\n\n            // just for convenience\n            writeln!(self.out, \"{level}float naga_tmin = naga_desc.tmin;\")?;\n            writeln!(self.out, \"{level}float naga_tmax = naga_desc.tmax;\")?;\n            writeln!(self.out, \"{level}float3 naga_origin = naga_desc.origin;\")?;\n            writeln!(self.out, \"{level}float3 naga_dir = naga_desc.dir;\")?;\n            writeln!(self.out, \"{level}uint naga_flags = naga_desc.flags;\")?;\n            write!(\n                self.out,\n                \"{level}bool naga_tmin_valid = (naga_tmin >= 0.0) && (naga_tmin <= naga_tmax) && !\"\n            )?;\n            self.write_nan(\"naga_tmin\")?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_tmax_valid = !\")?;\n            self.write_nan(\"naga_tmax\")?;\n            writeln!(self.out, \";\")?;\n            // Unlike Vulkan it seems that for DX12, it seems only NaN components of the origin and direction are invalid\n            write!(self.out, \"{level}bool naga_origin_valid = !any(\")?;\n            self.write_nan(\"naga_origin\")?;\n            writeln!(self.out, \");\")?;\n            write!(self.out, \"{level}bool naga_dir_valid = !any(\")?;\n            self.write_nan(\"naga_dir\")?;\n            writeln!(self.out, \");\")?;\n            write!(self.out, \"{level}bool naga_contains_opaque = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::FORCE_OPAQUE.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_no_opaque = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::FORCE_NO_OPAQUE.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_cull_opaque = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::CULL_OPAQUE.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_cull_no_opaque = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::CULL_NO_OPAQUE.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_cull_front = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::CULL_FRONT_FACING.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_cull_back = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::CULL_BACK_FACING.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_skip_triangles = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::SKIP_TRIANGLES.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_contains_skip_aabbs = \")?;\n            self.write_contains_flags(\"naga_flags\", crate::RayFlag::SKIP_AABBS.bits())?;\n            writeln!(self.out, \";\")?;\n            // A textified version of the same in the spirv writer\n            fn less_than_two_true(mut bools: Vec<&str>) -> Result<String, super::Error> {\n                assert!(bools.len() > 1, \"Must have multiple booleans!\");\n                let mut final_expr = String::new();\n                while let Some(last_bool) = bools.pop() {\n                    for &bool in &bools {\n                        if !final_expr.is_empty() {\n                            final_expr.push_str(\"||\");\n                        }\n                        write!(final_expr, \" ({last_bool} && {bool}) \")?;\n                    }\n                }\n                Ok(final_expr)\n            }\n            writeln!(\n                self.out,\n                \"{level}bool naga_contains_skip_triangles_aabbs = {};\",\n                less_than_two_true(vec![\n                    \"naga_contains_skip_triangles\",\n                    \"naga_contains_skip_aabbs\"\n                ])?\n            )?;\n            writeln!(\n                self.out,\n                \"{level}bool naga_contains_skip_triangles_cull = {};\",\n                less_than_two_true(vec![\n                    \"naga_contains_skip_triangles\",\n                    \"naga_contains_cull_back\",\n                    \"naga_contains_cull_front\"\n                ])?\n            )?;\n            writeln!(\n                self.out,\n                \"{level}bool naga_contains_multiple_opaque = {};\",\n                less_than_two_true(vec![\n                    \"naga_contains_opaque\",\n                    \"naga_contains_no_opaque\",\n                    \"naga_contains_cull_opaque\",\n                    \"naga_contains_cull_no_opaque\"\n                ])?\n            )?;\n            writeln!(\n                self.out,\n                \"{level}if (naga_tmin_valid && naga_tmax_valid && naga_origin_valid && naga_dir_valid && !(naga_contains_skip_triangles_aabbs || naga_contains_skip_triangles_cull || naga_contains_multiple_opaque)) {{\"\n            )?;\n            level = level.next();\n            writeln!(\n                self.out,\n                \"{level}{rq_tracker} = {rq_tracker} | {};\",\n                crate::back::RayQueryPoint::INITIALIZED.bits()\n            )?;\n        }\n        write!(self.out, \"{level}\")?;\n        self.write_expr(module, query, func_ctx)?;\n        write!(self.out, \".TraceRayInline(\")?;\n        self.write_expr(module, acceleration_structure, func_ctx)?;\n        writeln!(\n            self.out,\n            \", naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\"\n        )?;\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"{base_level}    }}\")?;\n        }\n        writeln!(self.out, \"{base_level}}}\")?;\n        Ok(())\n    }\n\n    pub(super) fn write_proceed(\n        &mut self,\n        module: &crate::Module,\n        mut level: Level,\n        query: Handle<crate::Expression>,\n        result: Handle<crate::Expression>,\n        rq_tracker: &str,\n        func_ctx: &crate::back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let base_level = level;\n        write!(self.out, \"{level}\")?;\n        let name = Baked(result).to_string();\n        writeln!(self.out, \"bool {name} = false;\")?;\n        // This prevents variables flowing down a level and causing compile errors.\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"{level}{{\")?;\n            level = level.next();\n            write!(self.out, \"{level}bool naga_has_initialized = \")?;\n            self.write_contains_flags(rq_tracker, crate::back::RayQueryPoint::INITIALIZED.bits())?;\n            writeln!(self.out, \";\")?;\n            write!(self.out, \"{level}bool naga_has_finished = \")?;\n            self.write_contains_flags(\n                rq_tracker,\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            )?;\n            writeln!(self.out, \";\")?;\n            writeln!(\n                self.out,\n                \"{level}if (naga_has_initialized && !naga_has_finished) {{\"\n            )?;\n            level = level.next();\n        }\n\n        write!(self.out, \"{level}{name} = \")?;\n        self.write_expr(module, query, func_ctx)?;\n        writeln!(self.out, \".Proceed();\")?;\n\n        if self.options.ray_query_initialization_tracking {\n            writeln!(\n                self.out,\n                \"{level}{rq_tracker} = {rq_tracker} | {};\",\n                crate::back::RayQueryPoint::PROCEED.bits()\n            )?;\n            writeln!(\n                self.out,\n                \"{level}if (!{name}) {{ {rq_tracker} = {rq_tracker} | {}; }}\",\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits()\n            )?;\n            writeln!(self.out, \"{base_level}}}}}\")?;\n        }\n\n        self.named_expressions.insert(result, name);\n\n        Ok(())\n    }\n\n    pub(super) fn write_generate_intersection(\n        &mut self,\n        module: &crate::Module,\n        mut level: Level,\n        query: Handle<crate::Expression>,\n        hit_t: Handle<crate::Expression>,\n        rq_tracker: &str,\n        func_ctx: &crate::back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let base_level = level;\n        if self.options.ray_query_initialization_tracking {\n            write!(self.out, \"{level}if (\")?;\n            self.write_contains_flags(rq_tracker, crate::back::RayQueryPoint::PROCEED.bits())?;\n            write!(self.out, \" && !\")?;\n            self.write_contains_flags(\n                rq_tracker,\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            )?;\n            writeln!(self.out, \") {{\")?;\n            level = level.next();\n            write!(self.out, \"{level}CANDIDATE_TYPE naga_kind = \")?;\n            self.write_expr(module, query, func_ctx)?;\n            writeln!(self.out, \".CandidateType();\")?;\n            write!(self.out, \"{level}float naga_tmin = \")?;\n            self.write_expr(module, query, func_ctx)?;\n            writeln!(self.out, \".RayTMin();\")?;\n            write!(self.out, \"{level}float naga_tcurrentmax = \")?;\n            self.write_expr(module, query, func_ctx)?;\n            // This gets initialized to tmax and is updated after each intersection is committed so is valid to call.\n            // Note: there is a bug in DXC's spirv backend that makes this technically UB in spirv, but HLSL backend\n            // is intended for DXIL, so it should be fine (hopefully).\n            writeln!(self.out, \".CommittedRayT();\")?;\n            write!(\n                self.out,\n                \"{level}if ((naga_kind == CANDIDATE_PROCEDURAL_PRIMITIVE) && (naga_tmin <=\"\n            )?;\n            self.write_expr(module, hit_t, func_ctx)?;\n            write!(self.out, \") && (\")?;\n            self.write_expr(module, hit_t, func_ctx)?;\n            writeln!(self.out, \" <= naga_tcurrentmax)) {{\")?;\n            level = level.next();\n        }\n\n        write!(self.out, \"{level}\")?;\n        self.write_expr(module, query, func_ctx)?;\n        write!(self.out, \".CommitProceduralPrimitiveHit(\")?;\n        self.write_expr(module, hit_t, func_ctx)?;\n        writeln!(self.out, \");\")?;\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"{base_level}}}}}\")?;\n        }\n        Ok(())\n    }\n    pub(super) fn write_confirm_intersection(\n        &mut self,\n        module: &crate::Module,\n        mut level: Level,\n        query: Handle<crate::Expression>,\n        rq_tracker: &str,\n        func_ctx: &crate::back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let base_level = level;\n        if self.options.ray_query_initialization_tracking {\n            write!(self.out, \"{level}if (\")?;\n            self.write_contains_flags(rq_tracker, crate::back::RayQueryPoint::PROCEED.bits())?;\n            write!(self.out, \" && !\")?;\n            self.write_contains_flags(\n                rq_tracker,\n                crate::back::RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            )?;\n            writeln!(self.out, \") {{\")?;\n            level = level.next();\n            write!(self.out, \"{level}CANDIDATE_TYPE naga_kind = \")?;\n            self.write_expr(module, query, func_ctx)?;\n            writeln!(self.out, \".CandidateType();\")?;\n            writeln!(\n                self.out,\n                \"{level}if (naga_kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {{\"\n            )?;\n            level = level.next();\n        }\n\n        write!(self.out, \"{level}\")?;\n        self.write_expr(module, query, func_ctx)?;\n        writeln!(self.out, \".CommitNonOpaqueTriangleHit();\")?;\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"{base_level}}}}}\")?;\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_terminate(\n        &mut self,\n        module: &crate::Module,\n        mut level: Level,\n        query: Handle<crate::Expression>,\n        rq_tracker: &str,\n        func_ctx: &crate::back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let base_level = level;\n        if self.options.ray_query_initialization_tracking {\n            write!(self.out, \"{level}if (\")?;\n            // RayQuery::Abort() can be called any time after RayQuery::TraceRayInline() has been called.\n            // from https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#rayquery-abort\n            self.write_contains_flags(rq_tracker, crate::back::RayQueryPoint::INITIALIZED.bits())?;\n            writeln!(self.out, \") {{\")?;\n            level = level.next();\n        }\n\n        write!(self.out, \"{level}\")?;\n        self.write_expr(module, query, func_ctx)?;\n        writeln!(self.out, \".Abort();\")?;\n\n        if self.options.ray_query_initialization_tracking {\n            writeln!(self.out, \"{base_level}}}\")?;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/storage.rs",
    "content": "/*!\nGenerating accesses to [`ByteAddressBuffer`] contents.\n\nNaga IR globals in the [`Storage`] address space are rendered as\n[`ByteAddressBuffer`]s or [`RWByteAddressBuffer`]s in HLSL. These\nbuffers don't have HLSL types (structs, arrays, etc.); instead, they\nare just raw blocks of bytes, with methods to load and store values of\nspecific types at particular byte offsets. This means that Naga must\ntranslate chains of [`Access`] and [`AccessIndex`] expressions into\nHLSL expressions that compute byte offsets into the buffer.\n\nTo generate code for a [`Storage`] access:\n\n- Call [`Writer::fill_access_chain`] on the expression referring to\n  the value. This populates [`Writer::temp_access_chain`] with the\n  appropriate byte offset calculations, as a vector of [`SubAccess`]\n  values.\n\n- Call [`Writer::write_storage_address`] to emit an HLSL expression\n  for a given slice of [`SubAccess`] values.\n\nNaga IR expressions can operate on composite values of any type, but\n[`ByteAddressBuffer`] and [`RWByteAddressBuffer`] have only a fixed\nset of `Load` and `Store` methods, to access one through four\nconsecutive 32-bit values. To synthesize a Naga access, you can\ninitialize [`temp_access_chain`] to refer to the composite, and then\ntemporarily push and pop additional steps on\n[`Writer::temp_access_chain`] to generate accesses to the individual\nelements/members.\n\nThe [`temp_access_chain`] field is a member of [`Writer`] solely to\nallow re-use of the `Vec`'s dynamic allocation. Its value is no longer\nneeded once HLSL for the access has been generated.\n\nNote about DXC and Load/Store functions:\n\nDXC's HLSL has a generic [`Load` and `Store`] function for [`ByteAddressBuffer`] and\n[`RWByteAddressBuffer`]. This is not available in FXC's HLSL, so we use\nit only for types that are only available in DXC. Notably 64 and 16 bit types.\n\nFXC's HLSL has functions Load, Load2, Load3, and Load4 and Store, Store2, Store3, Store4.\nThis loads/stores a vector of length 1, 2, 3, or 4. We use that for 32bit types, bitcasting to the\ncorrect type if necessary.\n\n[`Storage`]: crate::AddressSpace::Storage\n[`ByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer\n[`RWByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer\n[`Access`]: crate::Expression::Access\n[`AccessIndex`]: crate::Expression::AccessIndex\n[`Writer::fill_access_chain`]: super::Writer::fill_access_chain\n[`Writer::write_storage_address`]: super::Writer::write_storage_address\n[`Writer::temp_access_chain`]: super::Writer::temp_access_chain\n[`temp_access_chain`]: super::Writer::temp_access_chain\n[`Writer`]: super::Writer\n[`Load` and `Store`]: https://github.com/microsoft/DirectXShaderCompiler/wiki/ByteAddressBuffer-Load-Store-Additions\n*/\n\nuse alloc::format;\nuse core::{fmt, mem};\n\nuse super::{super::FunctionCtx, BackendResult, Error};\nuse crate::{\n    proc::{Alignment, NameKey, TypeResolution},\n    Handle,\n};\n\nconst STORE_TEMP_NAME: &str = \"_value\";\n\n/// One step in accessing a [`Storage`] global's component or element.\n///\n/// [`Writer::temp_access_chain`] holds a series of these structures,\n/// describing how to compute the byte offset of a particular element\n/// or member of some global variable in the [`Storage`] address\n/// space.\n///\n/// [`Writer::temp_access_chain`]: super::Writer::temp_access_chain\n/// [`Storage`]: crate::AddressSpace::Storage\n#[derive(Debug)]\npub(super) enum SubAccess {\n    BufferOffset {\n        group: u32,\n        offset: u32,\n    },\n\n    /// Add the given byte offset. This is used for struct members, or\n    /// known components of a vector or matrix. In all those cases,\n    /// the byte offset is a compile-time constant.\n    Offset(u32),\n\n    /// Scale `value` by `stride`, and add that to the current byte\n    /// offset. This is used to compute the offset of an array element\n    /// whose index is computed at runtime.\n    Index {\n        value: Handle<crate::Expression>,\n        stride: u32,\n    },\n}\n\npub(super) enum StoreValue {\n    Expression(Handle<crate::Expression>),\n    TempIndex {\n        depth: usize,\n        index: u32,\n        ty: TypeResolution,\n    },\n    TempAccess {\n        depth: usize,\n        base: Handle<crate::Type>,\n        member_index: u32,\n    },\n    // Access to a single column of a Cx2 matrix within a struct\n    TempColumnAccess {\n        depth: usize,\n        base: Handle<crate::Type>,\n        member_index: u32,\n        column: u32,\n    },\n}\n\nimpl<W: fmt::Write> super::Writer<'_, W> {\n    pub(super) fn write_storage_address(\n        &mut self,\n        module: &crate::Module,\n        chain: &[SubAccess],\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        if chain.is_empty() {\n            write!(self.out, \"0\")?;\n        }\n        for (i, access) in chain.iter().enumerate() {\n            if i != 0 {\n                write!(self.out, \"+\")?;\n            }\n            match *access {\n                SubAccess::BufferOffset { group, offset } => {\n                    write!(self.out, \"__dynamic_buffer_offsets{group}._{offset}\")?;\n                }\n                SubAccess::Offset(offset) => {\n                    write!(self.out, \"{offset}\")?;\n                }\n                SubAccess::Index { value, stride } => {\n                    self.write_expr(module, value, func_ctx)?;\n                    write!(self.out, \"*{stride}\")?;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn write_storage_load_sequence<I: Iterator<Item = (TypeResolution, u32)>>(\n        &mut self,\n        module: &crate::Module,\n        var_handle: Handle<crate::GlobalVariable>,\n        sequence: I,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        for (i, (ty_resolution, offset)) in sequence.enumerate() {\n            // add the index temporarily\n            self.temp_access_chain.push(SubAccess::Offset(offset));\n            if i != 0 {\n                write!(self.out, \", \")?;\n            };\n            self.write_storage_load(module, var_handle, ty_resolution, func_ctx)?;\n            self.temp_access_chain.pop();\n        }\n        Ok(())\n    }\n\n    /// Emit code to access a [`Storage`] global's component.\n    ///\n    /// Emit HLSL to access the component of `var_handle`, a global\n    /// variable in the [`Storage`] address space, whose type is\n    /// `result_ty` and whose location within the global is given by\n    /// [`self.temp_access_chain`]. See the [`storage`] module's\n    /// documentation for background.\n    ///\n    /// [`Storage`]: crate::AddressSpace::Storage\n    /// [`self.temp_access_chain`]: super::Writer::temp_access_chain\n    pub(super) fn write_storage_load(\n        &mut self,\n        module: &crate::Module,\n        var_handle: Handle<crate::GlobalVariable>,\n        result_ty: TypeResolution,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        match *result_ty.inner_with(&module.types) {\n            crate::TypeInner::Scalar(scalar) => {\n                // working around the borrow checker in `self.write_expr`\n                let chain = mem::take(&mut self.temp_access_chain);\n                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                // See note about DXC and Load/Store in the module's documentation.\n                if scalar.width == 4 {\n                    let cast = scalar.kind.to_hlsl_cast();\n                    write!(self.out, \"{cast}({var_name}.Load(\")?;\n                } else {\n                    let ty = scalar.to_hlsl_str()?;\n                    write!(self.out, \"{var_name}.Load<{ty}>(\")?;\n                };\n                self.write_storage_address(module, &chain, func_ctx)?;\n                write!(self.out, \")\")?;\n                if scalar.width == 4 {\n                    write!(self.out, \")\")?;\n                }\n                self.temp_access_chain = chain;\n            }\n            crate::TypeInner::Vector { size, scalar } => {\n                // working around the borrow checker in `self.write_expr`\n                let chain = mem::take(&mut self.temp_access_chain);\n                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                let size = size as u8;\n                // See note about DXC and Load/Store in the module's documentation.\n                if scalar.width == 4 {\n                    let cast = scalar.kind.to_hlsl_cast();\n                    write!(self.out, \"{cast}({var_name}.Load{size}(\")?;\n                } else {\n                    let ty = scalar.to_hlsl_str()?;\n                    write!(self.out, \"{var_name}.Load<{ty}{size}>(\")?;\n                };\n                self.write_storage_address(module, &chain, func_ctx)?;\n                write!(self.out, \")\")?;\n                if scalar.width == 4 {\n                    write!(self.out, \")\")?;\n                }\n                self.temp_access_chain = chain;\n            }\n            crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                write!(\n                    self.out,\n                    \"{}{}x{}(\",\n                    scalar.to_hlsl_str()?,\n                    columns as u8,\n                    rows as u8,\n                )?;\n\n                // Note: Matrices containing vec3s, due to padding, act like they contain vec4s.\n                let row_stride = Alignment::from(rows) * scalar.width as u32;\n                let iter = (0..columns as u32).map(|i| {\n                    let ty_inner = crate::TypeInner::Vector { size: rows, scalar };\n                    (TypeResolution::Value(ty_inner), i * row_stride)\n                });\n                self.write_storage_load_sequence(module, var_handle, iter, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                stride,\n            } => {\n                let constructor = super::help::WrappedConstructor {\n                    ty: result_ty.handle().unwrap(),\n                };\n                self.write_wrapped_constructor_function_name(module, constructor)?;\n                write!(self.out, \"(\")?;\n                let iter = (0..size.get()).map(|i| (TypeResolution::Handle(base), stride * i));\n                self.write_storage_load_sequence(module, var_handle, iter, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            crate::TypeInner::Struct { ref members, .. } => {\n                let constructor = super::help::WrappedConstructor {\n                    ty: result_ty.handle().unwrap(),\n                };\n                self.write_wrapped_constructor_function_name(module, constructor)?;\n                write!(self.out, \"(\")?;\n                let iter = members\n                    .iter()\n                    .map(|m| (TypeResolution::Handle(m.ty), m.offset));\n                self.write_storage_load_sequence(module, var_handle, iter, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            _ => unreachable!(),\n        }\n        Ok(())\n    }\n\n    fn write_store_value(\n        &mut self,\n        module: &crate::Module,\n        value: &StoreValue,\n        func_ctx: &FunctionCtx,\n    ) -> BackendResult {\n        match *value {\n            StoreValue::Expression(expr) => self.write_expr(module, expr, func_ctx)?,\n            StoreValue::TempIndex {\n                depth,\n                index,\n                ty: _,\n            } => write!(self.out, \"{STORE_TEMP_NAME}{depth}[{index}]\")?,\n            StoreValue::TempAccess {\n                depth,\n                base,\n                member_index,\n            } => {\n                let name = &self.names[&NameKey::StructMember(base, member_index)];\n                write!(self.out, \"{STORE_TEMP_NAME}{depth}.{name}\")?\n            }\n            StoreValue::TempColumnAccess {\n                depth,\n                base,\n                member_index,\n                column,\n            } => {\n                let name = &self.names[&NameKey::StructMember(base, member_index)];\n                write!(self.out, \"{STORE_TEMP_NAME}{depth}.{name}_{column}\")?\n            }\n        }\n        Ok(())\n    }\n\n    /// Helper function to write down the Store operation on a `ByteAddressBuffer`.\n    pub(super) fn write_storage_store(\n        &mut self,\n        module: &crate::Module,\n        var_handle: Handle<crate::GlobalVariable>,\n        value: StoreValue,\n        func_ctx: &FunctionCtx,\n        level: crate::back::Level,\n        within_struct: Option<Handle<crate::Type>>,\n    ) -> BackendResult {\n        let temp_resolution;\n        let ty_resolution = match value {\n            StoreValue::Expression(expr) => &func_ctx.info[expr].ty,\n            StoreValue::TempIndex {\n                depth: _,\n                index: _,\n                ref ty,\n            } => ty,\n            StoreValue::TempAccess {\n                depth: _,\n                base,\n                member_index,\n            } => {\n                let ty_handle = match module.types[base].inner {\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        members[member_index as usize].ty\n                    }\n                    _ => unreachable!(),\n                };\n                temp_resolution = TypeResolution::Handle(ty_handle);\n                &temp_resolution\n            }\n            StoreValue::TempColumnAccess { .. } => {\n                unreachable!(\"attempting write_storage_store for TempColumnAccess\");\n            }\n        };\n        match *ty_resolution.inner_with(&module.types) {\n            crate::TypeInner::Scalar(scalar) => {\n                // working around the borrow checker in `self.write_expr`\n                let chain = mem::take(&mut self.temp_access_chain);\n                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                // See note about DXC and Load/Store in the module's documentation.\n                if scalar.width == 4 {\n                    write!(self.out, \"{level}{var_name}.Store(\")?;\n                    self.write_storage_address(module, &chain, func_ctx)?;\n                    write!(self.out, \", asuint(\")?;\n                    self.write_store_value(module, &value, func_ctx)?;\n                    writeln!(self.out, \"));\")?;\n                } else {\n                    write!(self.out, \"{level}{var_name}.Store(\")?;\n                    self.write_storage_address(module, &chain, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_store_value(module, &value, func_ctx)?;\n                    writeln!(self.out, \");\")?;\n                }\n                self.temp_access_chain = chain;\n            }\n            crate::TypeInner::Vector { size, scalar } => {\n                // working around the borrow checker in `self.write_expr`\n                let chain = mem::take(&mut self.temp_access_chain);\n                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                // See note about DXC and Load/Store in the module's documentation.\n                if scalar.width == 4 {\n                    write!(self.out, \"{}{}.Store{}(\", level, var_name, size as u8)?;\n                    self.write_storage_address(module, &chain, func_ctx)?;\n                    write!(self.out, \", asuint(\")?;\n                    self.write_store_value(module, &value, func_ctx)?;\n                    writeln!(self.out, \"));\")?;\n                } else {\n                    write!(self.out, \"{level}{var_name}.Store(\")?;\n                    self.write_storage_address(module, &chain, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_store_value(module, &value, func_ctx)?;\n                    writeln!(self.out, \");\")?;\n                }\n                self.temp_access_chain = chain;\n            }\n            crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                // Note: Matrices containing vec3s, due to padding, act like they contain vec4s.\n                let row_stride = Alignment::from(rows) * scalar.width as u32;\n\n                writeln!(self.out, \"{level}{{\")?;\n\n                match within_struct {\n                    Some(containing_struct) if rows == crate::VectorSize::Bi => {\n                        // If we are within a struct, then the struct was already assigned to\n                        // a temporary, we don't need to make another.\n                        let mut chain = mem::take(&mut self.temp_access_chain);\n                        for i in 0..columns as u32 {\n                            chain.push(SubAccess::Offset(i * row_stride));\n                            // working around the borrow checker in `self.write_expr`\n                            let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                            let StoreValue::TempAccess { member_index, .. } = value else {\n                                unreachable!(\n                                    \"write_storage_store within_struct but not TempAccess\"\n                                );\n                            };\n                            let column_value = StoreValue::TempColumnAccess {\n                                depth: level.0, // note not incrementing, b/c no temp\n                                base: containing_struct,\n                                member_index,\n                                column: i,\n                            };\n                            // See note about DXC and Load/Store in the module's documentation.\n                            if scalar.width == 4 {\n                                write!(\n                                    self.out,\n                                    \"{}{}.Store{}(\",\n                                    level.next(),\n                                    var_name,\n                                    rows as u8\n                                )?;\n                                self.write_storage_address(module, &chain, func_ctx)?;\n                                write!(self.out, \", asuint(\")?;\n                                self.write_store_value(module, &column_value, func_ctx)?;\n                                writeln!(self.out, \"));\")?;\n                            } else {\n                                write!(self.out, \"{}{var_name}.Store(\", level.next())?;\n                                self.write_storage_address(module, &chain, func_ctx)?;\n                                write!(self.out, \", \")?;\n                                self.write_store_value(module, &column_value, func_ctx)?;\n                                writeln!(self.out, \");\")?;\n                            }\n                            chain.pop();\n                        }\n                        self.temp_access_chain = chain;\n                    }\n                    _ => {\n                        // first, assign the value to a temporary\n                        let depth = level.0 + 1;\n                        write!(\n                            self.out,\n                            \"{}{}{}x{} {}{} = \",\n                            level.next(),\n                            scalar.to_hlsl_str()?,\n                            columns as u8,\n                            rows as u8,\n                            STORE_TEMP_NAME,\n                            depth,\n                        )?;\n                        self.write_store_value(module, &value, func_ctx)?;\n                        writeln!(self.out, \";\")?;\n\n                        // then iterate the stores\n                        for i in 0..columns as u32 {\n                            self.temp_access_chain\n                                .push(SubAccess::Offset(i * row_stride));\n                            let ty_inner = crate::TypeInner::Vector { size: rows, scalar };\n                            let sv = StoreValue::TempIndex {\n                                depth,\n                                index: i,\n                                ty: TypeResolution::Value(ty_inner),\n                            };\n                            self.write_storage_store(\n                                module,\n                                var_handle,\n                                sv,\n                                func_ctx,\n                                level.next(),\n                                None,\n                            )?;\n                            self.temp_access_chain.pop();\n                        }\n                    }\n                }\n                // done\n                writeln!(self.out, \"{level}}}\")?;\n            }\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                stride,\n            } => {\n                // first, assign the value to a temporary\n                writeln!(self.out, \"{level}{{\")?;\n                write!(self.out, \"{}\", level.next())?;\n                self.write_type(module, base)?;\n                let depth = level.next().0;\n                write!(self.out, \" {STORE_TEMP_NAME}{depth}\")?;\n                self.write_array_size(module, base, crate::ArraySize::Constant(size))?;\n                write!(self.out, \" = \")?;\n                self.write_store_value(module, &value, func_ctx)?;\n                writeln!(self.out, \";\")?;\n                // then iterate the stores\n                for i in 0..size.get() {\n                    self.temp_access_chain.push(SubAccess::Offset(i * stride));\n                    let sv = StoreValue::TempIndex {\n                        depth,\n                        index: i,\n                        ty: TypeResolution::Handle(base),\n                    };\n                    self.write_storage_store(module, var_handle, sv, func_ctx, level.next(), None)?;\n                    self.temp_access_chain.pop();\n                }\n                // done\n                writeln!(self.out, \"{level}}}\")?;\n            }\n            crate::TypeInner::Struct { ref members, .. } => {\n                // first, assign the value to a temporary\n                writeln!(self.out, \"{level}{{\")?;\n                let depth = level.next().0;\n                let struct_ty = ty_resolution.handle().unwrap();\n                let struct_name = &self.names[&NameKey::Type(struct_ty)];\n                write!(\n                    self.out,\n                    \"{}{} {}{} = \",\n                    level.next(),\n                    struct_name,\n                    STORE_TEMP_NAME,\n                    depth\n                )?;\n                self.write_store_value(module, &value, func_ctx)?;\n                writeln!(self.out, \";\")?;\n                // then iterate the stores\n                for (i, member) in members.iter().enumerate() {\n                    self.temp_access_chain\n                        .push(SubAccess::Offset(member.offset));\n                    let sv = StoreValue::TempAccess {\n                        depth,\n                        base: struct_ty,\n                        member_index: i as u32,\n                    };\n                    self.write_storage_store(\n                        module,\n                        var_handle,\n                        sv,\n                        func_ctx,\n                        level.next(),\n                        Some(struct_ty),\n                    )?;\n                    self.temp_access_chain.pop();\n                }\n                // done\n                writeln!(self.out, \"{level}}}\")?;\n            }\n            _ => unreachable!(),\n        }\n        Ok(())\n    }\n\n    /// Set [`temp_access_chain`] to compute the byte offset of `cur_expr`.\n    ///\n    /// The `cur_expr` expression must be a reference to a global\n    /// variable in the [`Storage`] address space, or a chain of\n    /// [`Access`] and [`AccessIndex`] expressions referring to some\n    /// component of such a global.\n    ///\n    /// [`temp_access_chain`]: super::Writer::temp_access_chain\n    /// [`Storage`]: crate::AddressSpace::Storage\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    pub(super) fn fill_access_chain(\n        &mut self,\n        module: &crate::Module,\n        mut cur_expr: Handle<crate::Expression>,\n        func_ctx: &FunctionCtx,\n    ) -> Result<Handle<crate::GlobalVariable>, Error> {\n        enum AccessIndex {\n            Expression(Handle<crate::Expression>),\n            Constant(u32),\n        }\n        enum Parent<'a> {\n            Array { stride: u32 },\n            Struct(&'a [crate::StructMember]),\n        }\n        self.temp_access_chain.clear();\n\n        loop {\n            let (next_expr, access_index) = match func_ctx.expressions[cur_expr] {\n                crate::Expression::GlobalVariable(handle) => {\n                    if let Some(ref binding) = module.global_variables[handle].binding {\n                        // this was already resolved earlier when we started evaluating an entry point.\n                        let bt = self.options.resolve_resource_binding(binding).unwrap();\n                        if let Some(dynamic_storage_buffer_offsets_index) =\n                            bt.dynamic_storage_buffer_offsets_index\n                        {\n                            self.temp_access_chain.push(SubAccess::BufferOffset {\n                                group: binding.group,\n                                offset: dynamic_storage_buffer_offsets_index,\n                            });\n                        }\n                    }\n                    return Ok(handle);\n                }\n                crate::Expression::Access { base, index } => (base, AccessIndex::Expression(index)),\n                crate::Expression::AccessIndex { base, index } => {\n                    (base, AccessIndex::Constant(index))\n                }\n                ref other => {\n                    return Err(Error::Unimplemented(format!(\"Pointer access of {other:?}\")))\n                }\n            };\n\n            let parent = match *func_ctx.resolve_type(next_expr, &module.types) {\n                crate::TypeInner::Pointer { base, .. } => match module.types[base].inner {\n                    crate::TypeInner::Struct { ref members, .. } => Parent::Struct(members),\n                    crate::TypeInner::Array { stride, .. } => Parent::Array { stride },\n                    crate::TypeInner::Vector { scalar, .. } => Parent::Array {\n                        stride: scalar.width as u32,\n                    },\n                    crate::TypeInner::Matrix { rows, scalar, .. } => Parent::Array {\n                        // The stride between matrices is the count of rows as this is how\n                        // long each column is.\n                        stride: Alignment::from(rows) * scalar.width as u32,\n                    },\n                    _ => unreachable!(),\n                },\n                crate::TypeInner::ValuePointer { scalar, .. } => Parent::Array {\n                    stride: scalar.width as u32,\n                },\n                _ => unreachable!(),\n            };\n\n            let sub = match (parent, access_index) {\n                (Parent::Array { stride }, AccessIndex::Expression(value)) => {\n                    SubAccess::Index { value, stride }\n                }\n                (Parent::Array { stride }, AccessIndex::Constant(index)) => {\n                    SubAccess::Offset(stride * index)\n                }\n                (Parent::Struct(members), AccessIndex::Constant(index)) => {\n                    SubAccess::Offset(members[index as usize].offset)\n                }\n                (Parent::Struct(_), AccessIndex::Expression(_)) => unreachable!(),\n            };\n\n            self.temp_access_chain.push(sub);\n            cur_expr = next_expr;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/hlsl/writer.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::{fmt, mem};\n\nuse super::{\n    help,\n    help::{\n        WrappedArrayLength, WrappedConstructor, WrappedImageQuery, WrappedStructMatrixAccess,\n        WrappedZeroValue,\n    },\n    storage::StoreValue,\n    BackendResult, Error, FragmentEntryPoint, Options, PipelineOptions, ShaderModel,\n};\nuse crate::{\n    back::{self, get_entry_points, Baked},\n    common,\n    proc::{self, index, ExternalTextureNameKey, NameKey},\n    valid, Handle, Module, RayQueryFunction, Scalar, ScalarKind, ShaderStage, TypeInner,\n};\n\nconst LOCATION_SEMANTIC: &str = \"LOC\";\nconst SPECIAL_CBUF_TYPE: &str = \"NagaConstants\";\nconst SPECIAL_CBUF_VAR: &str = \"_NagaConstants\";\nconst SPECIAL_FIRST_VERTEX: &str = \"first_vertex\";\nconst SPECIAL_FIRST_INSTANCE: &str = \"first_instance\";\nconst SPECIAL_OTHER: &str = \"other\";\n\npub(crate) const MODF_FUNCTION: &str = \"naga_modf\";\npub(crate) const FREXP_FUNCTION: &str = \"naga_frexp\";\npub(crate) const EXTRACT_BITS_FUNCTION: &str = \"naga_extractBits\";\npub(crate) const INSERT_BITS_FUNCTION: &str = \"naga_insertBits\";\npub(crate) const SAMPLER_HEAP_VAR: &str = \"nagaSamplerHeap\";\npub(crate) const COMPARISON_SAMPLER_HEAP_VAR: &str = \"nagaComparisonSamplerHeap\";\npub(crate) const SAMPLE_EXTERNAL_TEXTURE_FUNCTION: &str = \"nagaSampleExternalTexture\";\npub(crate) const ABS_FUNCTION: &str = \"naga_abs\";\npub(crate) const DIV_FUNCTION: &str = \"naga_div\";\npub(crate) const MOD_FUNCTION: &str = \"naga_mod\";\npub(crate) const NEG_FUNCTION: &str = \"naga_neg\";\npub(crate) const F2I32_FUNCTION: &str = \"naga_f2i32\";\npub(crate) const F2U32_FUNCTION: &str = \"naga_f2u32\";\npub(crate) const F2I64_FUNCTION: &str = \"naga_f2i64\";\npub(crate) const F2U64_FUNCTION: &str = \"naga_f2u64\";\npub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str =\n    \"nagaTextureSampleBaseClampToEdge\";\npub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = \"nagaTextureLoadExternal\";\npub(crate) const RAY_QUERY_TRACKER_VARIABLE_PREFIX: &str = \"naga_query_init_tracker_for_\";\n/// Prefix for variables in a naga statement\npub(crate) const INTERNAL_PREFIX: &str = \"naga_\";\n\nenum Index {\n    Expression(Handle<crate::Expression>),\n    Static(u32),\n}\n\nstruct EpStructMember {\n    name: String,\n    ty: Handle<crate::Type>,\n    // technically, this should always be `Some`\n    // (we `debug_assert!` this in `write_interface_struct`)\n    binding: Option<crate::Binding>,\n    index: u32,\n}\n\n/// Structure contains information required for generating\n/// wrapped structure of all entry points arguments\nstruct EntryPointBinding {\n    /// Name of the fake EP argument that contains the struct\n    /// with all the flattened input data.\n    arg_name: String,\n    /// Generated structure name\n    ty_name: String,\n    /// Members of generated structure\n    members: Vec<EpStructMember>,\n    local_invocation_index_name: Option<String>,\n}\n\npub(super) struct EntryPointInterface {\n    /// If `Some`, the input of an entry point is gathered in a special\n    /// struct with members sorted by binding.\n    /// The `EntryPointBinding::members` array is sorted by index,\n    /// so that we can walk it in `write_ep_arguments_initialization`.\n    input: Option<EntryPointBinding>,\n    /// If `Some`, the output of an entry point is flattened.\n    /// The `EntryPointBinding::members` array is sorted by binding,\n    /// So that we can walk it in `Statement::Return` handler.\n    output: Option<EntryPointBinding>,\n}\n\n#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]\nenum InterfaceKey {\n    Location(u32),\n    BuiltIn(crate::BuiltIn),\n    Other,\n}\n\nimpl InterfaceKey {\n    const fn new(binding: Option<&crate::Binding>) -> Self {\n        match binding {\n            Some(&crate::Binding::Location { location, .. }) => Self::Location(location),\n            Some(&crate::Binding::BuiltIn(built_in)) => Self::BuiltIn(built_in),\n            None => Self::Other,\n        }\n    }\n}\n\n#[derive(Copy, Clone, PartialEq)]\nenum Io {\n    Input,\n    Output,\n}\n\nconst fn is_subgroup_builtin_binding(binding: &Option<crate::Binding>) -> bool {\n    let &Some(crate::Binding::BuiltIn(builtin)) = binding else {\n        return false;\n    };\n    matches!(\n        builtin,\n        crate::BuiltIn::SubgroupSize\n            | crate::BuiltIn::SubgroupInvocationId\n            | crate::BuiltIn::NumSubgroups\n            | crate::BuiltIn::SubgroupId\n    )\n}\n\n/// Information for how to generate a `binding_array<sampler>` access.\nstruct BindingArraySamplerInfo {\n    /// Variable name of the sampler heap\n    sampler_heap_name: &'static str,\n    /// Variable name of the sampler index buffer\n    sampler_index_buffer_name: String,\n    /// Variable name of the base index _into_ the sampler index buffer\n    binding_array_base_index_name: String,\n}\n\nimpl<'a, W: fmt::Write> super::Writer<'a, W> {\n    pub fn new(out: W, options: &'a Options, pipeline_options: &'a PipelineOptions) -> Self {\n        Self {\n            out,\n            names: crate::FastHashMap::default(),\n            namer: proc::Namer::default(),\n            options,\n            pipeline_options,\n            entry_point_io: crate::FastHashMap::default(),\n            named_expressions: crate::NamedExpressions::default(),\n            wrapped: super::Wrapped::default(),\n            written_committed_intersection: false,\n            written_candidate_intersection: false,\n            continue_ctx: back::continue_forward::ContinueCtx::default(),\n            temp_access_chain: Vec::new(),\n            need_bake_expressions: Default::default(),\n        }\n    }\n\n    fn reset(&mut self, module: &Module) {\n        self.names.clear();\n        self.namer.reset(\n            module,\n            &super::keywords::RESERVED_SET,\n            proc::KeywordSet::empty(),\n            &super::keywords::RESERVED_CASE_INSENSITIVE_SET,\n            super::keywords::RESERVED_PREFIXES,\n            &mut self.names,\n        );\n        self.entry_point_io.clear();\n        self.named_expressions.clear();\n        self.wrapped.clear();\n        self.written_committed_intersection = false;\n        self.written_candidate_intersection = false;\n        self.continue_ctx.clear();\n        self.need_bake_expressions.clear();\n    }\n\n    /// Generates statements to be inserted immediately before and at the very\n    /// start of the body of each loop, to defeat infinite loop reasoning.\n    /// The 0th item of the returned tuple should be inserted immediately prior\n    /// to the loop and the 1st item should be inserted at the very start of\n    /// the loop body.\n    ///\n    /// See [`back::msl::Writer::gen_force_bounded_loop_statements`] for details.\n    fn gen_force_bounded_loop_statements(\n        &mut self,\n        level: back::Level,\n    ) -> Option<(String, String)> {\n        if !self.options.force_loop_bounding {\n            return None;\n        }\n\n        let loop_bound_name = self.namer.call(\"loop_bound\");\n        let max = u32::MAX;\n        // Count down from u32::MAX rather than up from 0 to avoid hang on\n        // certain Intel drivers. See <https://github.com/gfx-rs/wgpu/issues/7319>.\n        let decl = format!(\"{level}uint2 {loop_bound_name} = uint2({max}u, {max}u);\");\n        let level = level.next();\n        let break_and_inc = format!(\n            \"{level}if (all({loop_bound_name} == uint2(0u, 0u))) {{ break; }}\n{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);\"\n        );\n\n        Some((decl, break_and_inc))\n    }\n\n    /// Helper method used to find which expressions of a given function require baking\n    ///\n    /// # Notes\n    /// Clears `need_bake_expressions` set before adding to it\n    fn update_expressions_to_bake(\n        &mut self,\n        module: &Module,\n        func: &crate::Function,\n        info: &valid::FunctionInfo,\n    ) {\n        use crate::Expression;\n        self.need_bake_expressions.clear();\n        for (exp_handle, expr) in func.expressions.iter() {\n            let expr_info = &info[exp_handle];\n            let min_ref_count = func.expressions[exp_handle].bake_ref_count();\n            if min_ref_count <= expr_info.ref_count {\n                self.need_bake_expressions.insert(exp_handle);\n            }\n\n            if let Expression::Math { fun, arg, arg1, .. } = *expr {\n                match fun {\n                    crate::MathFunction::Asinh\n                    | crate::MathFunction::Acosh\n                    | crate::MathFunction::Atanh\n                    | crate::MathFunction::Unpack2x16float\n                    | crate::MathFunction::Unpack2x16snorm\n                    | crate::MathFunction::Unpack2x16unorm\n                    | crate::MathFunction::Unpack4x8snorm\n                    | crate::MathFunction::Unpack4x8unorm\n                    | crate::MathFunction::Unpack4xI8\n                    | crate::MathFunction::Unpack4xU8\n                    | crate::MathFunction::Pack2x16float\n                    | crate::MathFunction::Pack2x16snorm\n                    | crate::MathFunction::Pack2x16unorm\n                    | crate::MathFunction::Pack4x8snorm\n                    | crate::MathFunction::Pack4x8unorm\n                    | crate::MathFunction::Pack4xI8\n                    | crate::MathFunction::Pack4xU8\n                    | crate::MathFunction::Pack4xI8Clamp\n                    | crate::MathFunction::Pack4xU8Clamp => {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    crate::MathFunction::CountLeadingZeros => {\n                        let inner = info[exp_handle].ty.inner_with(&module.types);\n                        if let Some(ScalarKind::Sint) = inner.scalar_kind() {\n                            self.need_bake_expressions.insert(arg);\n                        }\n                    }\n                    crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {\n                        self.need_bake_expressions.insert(arg);\n                        self.need_bake_expressions.insert(arg1.unwrap());\n                    }\n                    _ => {}\n                }\n            }\n\n            if let Expression::Derivative { axis, ctrl, expr } = *expr {\n                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n                if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {\n                    self.need_bake_expressions.insert(expr);\n                }\n            }\n\n            if let Expression::GlobalVariable(_) = *expr {\n                let inner = info[exp_handle].ty.inner_with(&module.types);\n\n                if let TypeInner::Sampler { .. } = *inner {\n                    self.need_bake_expressions.insert(exp_handle);\n                }\n            }\n        }\n        for statement in func.body.iter() {\n            match *statement {\n                crate::Statement::SubgroupCollectiveOperation {\n                    op: _,\n                    collective_op: crate::CollectiveOperation::InclusiveScan,\n                    argument,\n                    result: _,\n                } => {\n                    self.need_bake_expressions.insert(argument);\n                }\n                crate::Statement::Atomic {\n                    fun: crate::AtomicFunction::Exchange { compare: Some(cmp) },\n                    ..\n                } => {\n                    self.need_bake_expressions.insert(cmp);\n                }\n                _ => {}\n            }\n        }\n    }\n\n    pub fn write(\n        &mut self,\n        module: &Module,\n        module_info: &valid::ModuleInfo,\n        fragment_entry_point: Option<&FragmentEntryPoint<'_>>,\n    ) -> Result<super::ReflectionInfo, Error> {\n        self.reset(module);\n\n        // Write special constants, if needed\n        if let Some(ref bt) = self.options.special_constants_binding {\n            writeln!(self.out, \"struct {SPECIAL_CBUF_TYPE} {{\")?;\n            writeln!(self.out, \"{}int {};\", back::INDENT, SPECIAL_FIRST_VERTEX)?;\n            writeln!(self.out, \"{}int {};\", back::INDENT, SPECIAL_FIRST_INSTANCE)?;\n            writeln!(self.out, \"{}uint {};\", back::INDENT, SPECIAL_OTHER)?;\n            writeln!(self.out, \"}};\")?;\n            write!(\n                self.out,\n                \"ConstantBuffer<{}> {}: register(b{}\",\n                SPECIAL_CBUF_TYPE, SPECIAL_CBUF_VAR, bt.register\n            )?;\n            if bt.space != 0 {\n                write!(self.out, \", space{}\", bt.space)?;\n            }\n            writeln!(self.out, \");\")?;\n\n            // Extra newline for readability\n            writeln!(self.out)?;\n        }\n\n        for (group, bt) in self.options.dynamic_storage_buffer_offsets_targets.iter() {\n            writeln!(self.out, \"struct __dynamic_buffer_offsetsTy{group} {{\")?;\n            for i in 0..bt.size {\n                writeln!(self.out, \"{}uint _{};\", back::INDENT, i)?;\n            }\n            writeln!(self.out, \"}};\")?;\n            writeln!(\n                self.out,\n                \"ConstantBuffer<__dynamic_buffer_offsetsTy{}> __dynamic_buffer_offsets{}: register(b{}, space{});\",\n                group, group, bt.register, bt.space\n            )?;\n\n            // Extra newline for readability\n            writeln!(self.out)?;\n        }\n\n        // Save all entry point output types\n        let ep_results = module\n            .entry_points\n            .iter()\n            .map(|ep| (ep.stage, ep.function.result.clone()))\n            .collect::<Vec<(ShaderStage, Option<crate::FunctionResult>)>>();\n\n        self.write_all_mat_cx2_typedefs_and_functions(module)?;\n\n        // Write all structs\n        for (handle, ty) in module.types.iter() {\n            if let TypeInner::Struct { ref members, span } = ty.inner {\n                if module.types[members.last().unwrap().ty]\n                    .inner\n                    .is_dynamically_sized(&module.types)\n                {\n                    // unsized arrays can only be in storage buffers,\n                    // for which we use `ByteAddressBuffer` anyway.\n                    continue;\n                }\n\n                let ep_result = ep_results.iter().find(|e| {\n                    if let Some(ref result) = e.1 {\n                        result.ty == handle\n                    } else {\n                        false\n                    }\n                });\n\n                self.write_struct(\n                    module,\n                    handle,\n                    members,\n                    span,\n                    ep_result.map(|r| (r.0, Io::Output)),\n                )?;\n                writeln!(self.out)?;\n            }\n        }\n\n        self.write_special_functions(module)?;\n\n        self.write_wrapped_expression_functions(module, &module.global_expressions, None)?;\n        self.write_wrapped_zero_value_functions(module, &module.global_expressions)?;\n\n        // Write all named constants\n        let mut constants = module\n            .constants\n            .iter()\n            .filter(|&(_, c)| c.name.is_some())\n            .peekable();\n        while let Some((handle, _)) = constants.next() {\n            self.write_global_constant(module, handle)?;\n            // Add extra newline for readability on last iteration\n            if constants.peek().is_none() {\n                writeln!(self.out)?;\n            }\n        }\n\n        // Write all globals\n        for (global, _) in module.global_variables.iter() {\n            self.write_global(module, global)?;\n        }\n\n        if !module.global_variables.is_empty() {\n            // Add extra newline for readability\n            writeln!(self.out)?;\n        }\n\n        let ep_range = get_entry_points(module, self.pipeline_options.entry_point.as_ref())\n            .map_err(|(stage, name)| Error::EntryPointNotFound(stage, name))?;\n\n        // Write all entry points wrapped structs\n        for index in ep_range.clone() {\n            let ep = &module.entry_points[index];\n            let ep_name = self.names[&NameKey::EntryPoint(index as u16)].clone();\n            let ep_io = self.write_ep_interface(\n                module,\n                &ep.function,\n                ep.stage,\n                &ep_name,\n                fragment_entry_point,\n            )?;\n            self.entry_point_io.insert(index, ep_io);\n        }\n\n        // Write all regular functions\n        for (handle, function) in module.functions.iter() {\n            let info = &module_info[handle];\n\n            // Check if all of the globals are accessible\n            if !self.options.fake_missing_bindings {\n                if let Some((var_handle, _)) =\n                    module\n                        .global_variables\n                        .iter()\n                        .find(|&(var_handle, var)| match var.binding {\n                            Some(ref binding) if !info[var_handle].is_empty() => {\n                                self.options.resolve_resource_binding(binding).is_err()\n                                    && self\n                                        .options\n                                        .resolve_external_texture_resource_binding(binding)\n                                        .is_err()\n                            }\n                            _ => false,\n                        })\n                {\n                    log::debug!(\n                        \"Skipping function {:?} (name {:?}) because global {:?} is inaccessible\",\n                        handle,\n                        function.name,\n                        var_handle\n                    );\n                    continue;\n                }\n            }\n\n            let ctx = back::FunctionCtx {\n                ty: back::FunctionType::Function(handle),\n                info,\n                expressions: &function.expressions,\n                named_expressions: &function.named_expressions,\n            };\n            let name = self.names[&NameKey::Function(handle)].clone();\n\n            self.write_wrapped_functions(module, &ctx)?;\n\n            self.write_function(module, name.as_str(), function, &ctx, info)?;\n\n            writeln!(self.out)?;\n        }\n\n        let mut translated_ep_names = Vec::with_capacity(ep_range.len());\n\n        // Write all entry points\n        for index in ep_range {\n            let ep = &module.entry_points[index];\n            let info = module_info.get_entry_point(index);\n\n            if !self.options.fake_missing_bindings {\n                let mut ep_error = None;\n                for (var_handle, var) in module.global_variables.iter() {\n                    match var.binding {\n                        Some(ref binding) if !info[var_handle].is_empty() => {\n                            if let Err(err) = self.options.resolve_resource_binding(binding) {\n                                if self\n                                    .options\n                                    .resolve_external_texture_resource_binding(binding)\n                                    .is_err()\n                                {\n                                    ep_error = Some(err);\n                                    break;\n                                }\n                            }\n                        }\n                        _ => {}\n                    }\n                }\n                if let Some(err) = ep_error {\n                    translated_ep_names.push(Err(err));\n                    continue;\n                }\n            }\n\n            let ctx = back::FunctionCtx {\n                ty: back::FunctionType::EntryPoint(index as u16),\n                info,\n                expressions: &ep.function.expressions,\n                named_expressions: &ep.function.named_expressions,\n            };\n\n            self.write_wrapped_functions(module, &ctx)?;\n\n            if ep.stage.compute_like() {\n                // HLSL is calling workgroup size \"num threads\"\n                let num_threads = ep.workgroup_size;\n                writeln!(\n                    self.out,\n                    \"[numthreads({}, {}, {})]\",\n                    num_threads[0], num_threads[1], num_threads[2]\n                )?;\n            }\n\n            let name = self.names[&NameKey::EntryPoint(index as u16)].clone();\n            self.write_function(module, &name, &ep.function, &ctx, info)?;\n\n            if index < module.entry_points.len() - 1 {\n                writeln!(self.out)?;\n            }\n\n            translated_ep_names.push(Ok(name));\n        }\n\n        Ok(super::ReflectionInfo {\n            entry_point_names: translated_ep_names,\n        })\n    }\n\n    fn write_modifier(&mut self, binding: &crate::Binding) -> BackendResult {\n        match *binding {\n            crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }) => {\n                write!(self.out, \"precise \")?;\n            }\n            crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { perspective: false }) => {\n                write!(self.out, \"noperspective \")?;\n            }\n            crate::Binding::Location {\n                interpolation,\n                sampling,\n                ..\n            } => {\n                if let Some(interpolation) = interpolation {\n                    if let Some(string) = interpolation.to_hlsl_str() {\n                        write!(self.out, \"{string} \")?\n                    }\n                }\n\n                if let Some(sampling) = sampling {\n                    if let Some(string) = sampling.to_hlsl_str() {\n                        write!(self.out, \"{string} \")?\n                    }\n                }\n            }\n            crate::Binding::BuiltIn(_) => {}\n        }\n\n        Ok(())\n    }\n\n    //TODO: we could force fragment outputs to always go through `entry_point_io.output` path\n    // if they are struct, so that the `stage` argument here could be omitted.\n    fn write_semantic(\n        &mut self,\n        binding: &Option<crate::Binding>,\n        stage: Option<(ShaderStage, Io)>,\n    ) -> BackendResult {\n        match *binding {\n            Some(crate::Binding::BuiltIn(builtin)) if !is_subgroup_builtin_binding(binding) => {\n                if builtin == crate::BuiltIn::ViewIndex\n                    && self.options.shader_model < ShaderModel::V6_1\n                {\n                    return Err(Error::ShaderModelTooLow(\n                        \"used @builtin(view_index) or SV_ViewID\".to_string(),\n                        ShaderModel::V6_1,\n                    ));\n                }\n                let builtin_str = builtin.to_hlsl_str()?;\n                write!(self.out, \" : {builtin_str}\")?;\n            }\n            Some(crate::Binding::Location {\n                blend_src: Some(1), ..\n            }) => {\n                write!(self.out, \" : SV_Target1\")?;\n            }\n            Some(crate::Binding::Location { location, .. }) => {\n                if stage == Some((ShaderStage::Fragment, Io::Output)) {\n                    write!(self.out, \" : SV_Target{location}\")?;\n                } else {\n                    write!(self.out, \" : {LOCATION_SEMANTIC}{location}\")?;\n                }\n            }\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    fn write_interface_struct(\n        &mut self,\n        module: &Module,\n        shader_stage: (ShaderStage, Io),\n        struct_name: String,\n        mut members: Vec<EpStructMember>,\n    ) -> Result<EntryPointBinding, Error> {\n        // Sort the members so that first come the user-defined varyings\n        // in ascending locations, and then built-ins. This allows VS and FS\n        // interfaces to match with regards to order.\n        members.sort_by_key(|m| InterfaceKey::new(m.binding.as_ref()));\n\n        write!(self.out, \"struct {struct_name}\")?;\n        writeln!(self.out, \" {{\")?;\n        let mut local_invocation_index_name = None;\n        let mut subgroup_id_used = false;\n        for m in members.iter() {\n            // Sanity check that each IO member is a built-in or is assigned a\n            // location. Also see note about nesting in `write_ep_input_struct`.\n            debug_assert!(m.binding.is_some());\n\n            match m.binding {\n                Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId)) => {\n                    subgroup_id_used = true;\n                }\n                Some(crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex)) => {\n                    local_invocation_index_name = Some(m.name.clone());\n                }\n                _ => (),\n            }\n\n            if is_subgroup_builtin_binding(&m.binding) {\n                continue;\n            }\n            write!(self.out, \"{}\", back::INDENT)?;\n            if let Some(ref binding) = m.binding {\n                self.write_modifier(binding)?;\n            }\n            self.write_type(module, m.ty)?;\n            write!(self.out, \" {}\", &m.name)?;\n            self.write_semantic(&m.binding, Some(shader_stage))?;\n            writeln!(self.out, \";\")?;\n        }\n        if subgroup_id_used && local_invocation_index_name.is_none() {\n            let name = self.namer.call(\"local_invocation_index\");\n            writeln!(self.out, \"{}uint {name} : SV_GroupIndex;\", back::INDENT)?;\n            local_invocation_index_name = Some(name);\n        }\n        writeln!(self.out, \"}};\")?;\n        writeln!(self.out)?;\n\n        // See ordering notes on EntryPointInterface fields\n        match shader_stage.1 {\n            Io::Input => {\n                // bring back the original order\n                members.sort_by_key(|m| m.index);\n            }\n            Io::Output => {\n                // keep it sorted by binding\n            }\n        }\n\n        Ok(EntryPointBinding {\n            arg_name: self.namer.call(struct_name.to_lowercase().as_str()),\n            ty_name: struct_name,\n            members,\n            local_invocation_index_name,\n        })\n    }\n\n    /// Flatten all entry point arguments into a single struct.\n    /// This is needed since we need to re-order them: first placing user locations,\n    /// then built-ins.\n    fn write_ep_input_struct(\n        &mut self,\n        module: &Module,\n        func: &crate::Function,\n        stage: ShaderStage,\n        entry_point_name: &str,\n    ) -> Result<EntryPointBinding, Error> {\n        let struct_name = format!(\"{stage:?}Input_{entry_point_name}\");\n\n        let mut fake_members = Vec::new();\n        for arg in func.arguments.iter() {\n            // NOTE: We don't need to handle nesting structs. All members must\n            // be either built-ins or assigned a location. I.E. `binding` is\n            // `Some`. This is checked in `VaryingContext::validate`. See:\n            // https://gpuweb.github.io/gpuweb/wgsl/#input-output-locations\n            match module.types[arg.ty].inner {\n                TypeInner::Struct { ref members, .. } => {\n                    for member in members.iter() {\n                        let name = self.namer.call_or(&member.name, \"member\");\n                        let index = fake_members.len() as u32;\n                        fake_members.push(EpStructMember {\n                            name,\n                            ty: member.ty,\n                            binding: member.binding.clone(),\n                            index,\n                        });\n                    }\n                }\n                _ => {\n                    let member_name = self.namer.call_or(&arg.name, \"member\");\n                    let index = fake_members.len() as u32;\n                    fake_members.push(EpStructMember {\n                        name: member_name,\n                        ty: arg.ty,\n                        binding: arg.binding.clone(),\n                        index,\n                    });\n                }\n            }\n        }\n\n        self.write_interface_struct(module, (stage, Io::Input), struct_name, fake_members)\n    }\n\n    /// Flatten all entry point results into a single struct.\n    /// This is needed since we need to re-order them: first placing user locations,\n    /// then built-ins.\n    fn write_ep_output_struct(\n        &mut self,\n        module: &Module,\n        result: &crate::FunctionResult,\n        stage: ShaderStage,\n        entry_point_name: &str,\n        frag_ep: Option<&FragmentEntryPoint<'_>>,\n    ) -> Result<EntryPointBinding, Error> {\n        let struct_name = format!(\"{stage:?}Output_{entry_point_name}\");\n\n        let empty = [];\n        let members = match module.types[result.ty].inner {\n            TypeInner::Struct { ref members, .. } => members,\n            ref other => {\n                log::error!(\"Unexpected {other:?} output type without a binding\");\n                &empty[..]\n            }\n        };\n\n        // Gather list of fragment input locations. We use this below to remove user-defined\n        // varyings from VS outputs that aren't in the FS inputs. This makes the VS interface match\n        // as long as the FS inputs are a subset of the VS outputs. This is only applied if the\n        // writer is supplied with information about the fragment entry point.\n        let fs_input_locs = if let (Some(frag_ep), ShaderStage::Vertex) = (frag_ep, stage) {\n            let mut fs_input_locs = Vec::new();\n            for arg in frag_ep.func.arguments.iter() {\n                let mut push_if_location = |binding: &Option<crate::Binding>| match *binding {\n                    Some(crate::Binding::Location { location, .. }) => fs_input_locs.push(location),\n                    Some(crate::Binding::BuiltIn(_)) | None => {}\n                };\n\n                // NOTE: We don't need to handle struct nesting. See note in\n                // `write_ep_input_struct`.\n                match frag_ep.module.types[arg.ty].inner {\n                    TypeInner::Struct { ref members, .. } => {\n                        for member in members.iter() {\n                            push_if_location(&member.binding);\n                        }\n                    }\n                    _ => push_if_location(&arg.binding),\n                }\n            }\n            fs_input_locs.sort();\n            Some(fs_input_locs)\n        } else {\n            None\n        };\n\n        let mut fake_members = Vec::new();\n        for (index, member) in members.iter().enumerate() {\n            if let Some(ref fs_input_locs) = fs_input_locs {\n                match member.binding {\n                    Some(crate::Binding::Location { location, .. }) => {\n                        if fs_input_locs.binary_search(&location).is_err() {\n                            continue;\n                        }\n                    }\n                    Some(crate::Binding::BuiltIn(_)) | None => {}\n                }\n            }\n\n            let member_name = self.namer.call_or(&member.name, \"member\");\n            fake_members.push(EpStructMember {\n                name: member_name,\n                ty: member.ty,\n                binding: member.binding.clone(),\n                index: index as u32,\n            });\n        }\n\n        self.write_interface_struct(module, (stage, Io::Output), struct_name, fake_members)\n    }\n\n    /// Writes special interface structures for an entry point. The special structures have\n    /// all the fields flattened into them and sorted by binding. They are needed to emulate\n    /// subgroup built-ins and to make the interfaces between VS outputs and FS inputs match.\n    fn write_ep_interface(\n        &mut self,\n        module: &Module,\n        func: &crate::Function,\n        stage: ShaderStage,\n        ep_name: &str,\n        frag_ep: Option<&FragmentEntryPoint<'_>>,\n    ) -> Result<EntryPointInterface, Error> {\n        Ok(EntryPointInterface {\n            input: if !func.arguments.is_empty()\n                && (stage == ShaderStage::Fragment\n                    || func\n                        .arguments\n                        .iter()\n                        .any(|arg| is_subgroup_builtin_binding(&arg.binding)))\n            {\n                Some(self.write_ep_input_struct(module, func, stage, ep_name)?)\n            } else {\n                None\n            },\n            output: match func.result {\n                Some(ref fr) if fr.binding.is_none() && stage == ShaderStage::Vertex => {\n                    Some(self.write_ep_output_struct(module, fr, stage, ep_name, frag_ep)?)\n                }\n                _ => None,\n            },\n        })\n    }\n\n    fn write_ep_argument_initialization(\n        &mut self,\n        ep: &crate::EntryPoint,\n        ep_input: &EntryPointBinding,\n        fake_member: &EpStructMember,\n    ) -> BackendResult {\n        match fake_member.binding {\n            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupSize)) => {\n                write!(self.out, \"WaveGetLaneCount()\")?\n            }\n            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupInvocationId)) => {\n                write!(self.out, \"WaveGetLaneIndex()\")?\n            }\n            Some(crate::Binding::BuiltIn(crate::BuiltIn::NumSubgroups)) => write!(\n                self.out,\n                \"({}u + WaveGetLaneCount() - 1u) / WaveGetLaneCount()\",\n                ep.workgroup_size[0] * ep.workgroup_size[1] * ep.workgroup_size[2]\n            )?,\n            Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId)) => {\n                write!(\n                    self.out,\n                    \"{}.{} / WaveGetLaneCount()\",\n                    ep_input.arg_name,\n                    // When writing SubgroupId, we always guarantee that local_invocation_index_name is written\n                    ep_input.local_invocation_index_name.as_ref().unwrap()\n                )?;\n            }\n            _ => {\n                write!(self.out, \"{}.{}\", ep_input.arg_name, fake_member.name)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Write an entry point preface that initializes the arguments as specified in IR.\n    fn write_ep_arguments_initialization(\n        &mut self,\n        module: &Module,\n        func: &crate::Function,\n        ep_index: u16,\n    ) -> BackendResult {\n        let ep = &module.entry_points[ep_index as usize];\n        let ep_input = match self\n            .entry_point_io\n            .get_mut(&(ep_index as usize))\n            .unwrap()\n            .input\n            .take()\n        {\n            Some(ep_input) => ep_input,\n            None => return Ok(()),\n        };\n        let mut fake_iter = ep_input.members.iter();\n        for (arg_index, arg) in func.arguments.iter().enumerate() {\n            write!(self.out, \"{}\", back::INDENT)?;\n            self.write_type(module, arg.ty)?;\n            let arg_name = &self.names[&NameKey::EntryPointArgument(ep_index, arg_index as u32)];\n            write!(self.out, \" {arg_name}\")?;\n            match module.types[arg.ty].inner {\n                TypeInner::Array { base, size, .. } => {\n                    self.write_array_size(module, base, size)?;\n                    write!(self.out, \" = \")?;\n                    self.write_ep_argument_initialization(\n                        ep,\n                        &ep_input,\n                        fake_iter.next().unwrap(),\n                    )?;\n                    writeln!(self.out, \";\")?;\n                }\n                TypeInner::Struct { ref members, .. } => {\n                    write!(self.out, \" = {{ \")?;\n                    for index in 0..members.len() {\n                        if index != 0 {\n                            write!(self.out, \", \")?;\n                        }\n                        self.write_ep_argument_initialization(\n                            ep,\n                            &ep_input,\n                            fake_iter.next().unwrap(),\n                        )?;\n                    }\n                    writeln!(self.out, \" }};\")?;\n                }\n                _ => {\n                    write!(self.out, \" = \")?;\n                    self.write_ep_argument_initialization(\n                        ep,\n                        &ep_input,\n                        fake_iter.next().unwrap(),\n                    )?;\n                    writeln!(self.out, \";\")?;\n                }\n            }\n        }\n        assert!(fake_iter.next().is_none());\n        Ok(())\n    }\n\n    /// Helper method used to write global variables\n    /// # Notes\n    /// Always adds a newline\n    fn write_global(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::GlobalVariable>,\n    ) -> BackendResult {\n        let global = &module.global_variables[handle];\n        let inner = &module.types[global.ty].inner;\n\n        let handle_ty = match *inner {\n            TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner,\n            _ => inner,\n        };\n\n        // External textures are handled entirely differently, so defer entirely to that method.\n        // We do so prior to calling resolve_resource_binding() below, as we even need to resolve\n        // their bindings separately.\n        let is_external_texture = matches!(\n            *handle_ty,\n            TypeInner::Image {\n                class: crate::ImageClass::External,\n                ..\n            }\n        );\n        if is_external_texture {\n            return self.write_global_external_texture(module, handle, global);\n        }\n\n        if let Some(ref binding) = global.binding {\n            if let Err(err) = self.options.resolve_resource_binding(binding) {\n                log::debug!(\n                    \"Skipping global {:?} (name {:?}) for being inaccessible: {}\",\n                    handle,\n                    global.name,\n                    err,\n                );\n                return Ok(());\n            }\n        }\n\n        // Samplers are handled entirely differently, so defer entirely to that method.\n        let is_sampler = matches!(*handle_ty, TypeInner::Sampler { .. });\n\n        if is_sampler {\n            return self.write_global_sampler(module, handle, global);\n        }\n\n        // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-register\n        let register_ty = match global.space {\n            crate::AddressSpace::Function => unreachable!(\"Function address space\"),\n            crate::AddressSpace::Private => {\n                write!(self.out, \"static \")?;\n                self.write_type(module, global.ty)?;\n                \"\"\n            }\n            crate::AddressSpace::WorkGroup => {\n                write!(self.out, \"groupshared \")?;\n                self.write_type(module, global.ty)?;\n                \"\"\n            }\n            crate::AddressSpace::TaskPayload => unimplemented!(),\n            crate::AddressSpace::Uniform => {\n                // constant buffer declarations are expected to be inlined, e.g.\n                // `cbuffer foo: register(b0) { field1: type1; }`\n                write!(self.out, \"cbuffer\")?;\n                \"b\"\n            }\n            crate::AddressSpace::Storage { access } => {\n                if global\n                    .memory_decorations\n                    .contains(crate::MemoryDecorations::COHERENT)\n                {\n                    write!(self.out, \"globallycoherent \")?;\n                }\n                let (prefix, register) = if access.contains(crate::StorageAccess::STORE) {\n                    (\"RW\", \"u\")\n                } else {\n                    (\"\", \"t\")\n                };\n                write!(self.out, \"{prefix}ByteAddressBuffer\")?;\n                register\n            }\n            crate::AddressSpace::Handle => {\n                let register = match *handle_ty {\n                    // all storage textures are UAV, unconditionally\n                    TypeInner::Image {\n                        class: crate::ImageClass::Storage { .. },\n                        ..\n                    } => \"u\",\n                    _ => \"t\",\n                };\n                self.write_type(module, global.ty)?;\n                register\n            }\n            crate::AddressSpace::Immediate => {\n                // The type of the immediates will be wrapped in `ConstantBuffer`\n                write!(self.out, \"ConstantBuffer<\")?;\n                \"b\"\n            }\n            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {\n                unimplemented!()\n            }\n        };\n\n        // If the global is a immediate data write the type now because it will be a\n        // generic argument to `ConstantBuffer`\n        if global.space == crate::AddressSpace::Immediate {\n            self.write_global_type(module, global.ty)?;\n\n            // need to write the array size if the type was emitted with `write_type`\n            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {\n                self.write_array_size(module, base, size)?;\n            }\n\n            // Close the angled brackets for the generic argument\n            write!(self.out, \">\")?;\n        }\n\n        let name = &self.names[&NameKey::GlobalVariable(handle)];\n        write!(self.out, \" {name}\")?;\n\n        // Immediates need to be assigned a binding explicitly by the consumer\n        // since naga has no way to know the binding from the shader alone\n        if global.space == crate::AddressSpace::Immediate {\n            match module.types[global.ty].inner {\n                TypeInner::Struct { .. } => {}\n                _ => {\n                    return Err(Error::Unimplemented(format!(\n                        \"push-constant '{name}' has non-struct type; tracked by: https://github.com/gfx-rs/wgpu/issues/5683\"\n                    )));\n                }\n            }\n\n            let target = self\n                .options\n                .immediates_target\n                .as_ref()\n                .expect(\"No bind target was defined for the immediates block\");\n            write!(self.out, \": register(b{}\", target.register)?;\n            if target.space != 0 {\n                write!(self.out, \", space{}\", target.space)?;\n            }\n            write!(self.out, \")\")?;\n        }\n\n        if let Some(ref binding) = global.binding {\n            // this was already resolved earlier when we started evaluating an entry point.\n            let bt = self.options.resolve_resource_binding(binding).unwrap();\n\n            // need to write the binding array size if the type was emitted with `write_type`\n            if let TypeInner::BindingArray { base, size, .. } = module.types[global.ty].inner {\n                if let Some(overridden_size) = bt.binding_array_size {\n                    write!(self.out, \"[{overridden_size}]\")?;\n                } else {\n                    self.write_array_size(module, base, size)?;\n                }\n            }\n\n            write!(self.out, \" : register({}{}\", register_ty, bt.register)?;\n            if bt.space != 0 {\n                write!(self.out, \", space{}\", bt.space)?;\n            }\n            write!(self.out, \")\")?;\n        } else {\n            // need to write the array size if the type was emitted with `write_type`\n            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {\n                self.write_array_size(module, base, size)?;\n            }\n            if global.space == crate::AddressSpace::Private {\n                write!(self.out, \" = \")?;\n                if let Some(init) = global.init {\n                    self.write_const_expression(module, init, &module.global_expressions)?;\n                } else {\n                    self.write_default_init(module, global.ty)?;\n                }\n            }\n        }\n\n        if global.space == crate::AddressSpace::Uniform {\n            write!(self.out, \" {{ \")?;\n\n            self.write_global_type(module, global.ty)?;\n\n            write!(\n                self.out,\n                \" {}\",\n                &self.names[&NameKey::GlobalVariable(handle)]\n            )?;\n\n            // need to write the array size if the type was emitted with `write_type`\n            if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {\n                self.write_array_size(module, base, size)?;\n            }\n\n            writeln!(self.out, \"; }}\")?;\n        } else {\n            writeln!(self.out, \";\")?;\n        }\n\n        Ok(())\n    }\n\n    fn write_global_sampler(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        let binding = *global.binding.as_ref().unwrap();\n\n        let key = super::SamplerIndexBufferKey {\n            group: binding.group,\n        };\n        self.write_wrapped_sampler_buffer(key)?;\n\n        // This was already validated, so we can confidently unwrap it.\n        let bt = self.options.resolve_resource_binding(&binding).unwrap();\n\n        match module.types[global.ty].inner {\n            TypeInner::Sampler { comparison } => {\n                // If we are generating a static access, we create a variable for the sampler.\n                //\n                // This prevents the DXIL from containing multiple lookups for the sampler, which\n                // the backend compiler will then have to eliminate. AMD does seem to be able to\n                // eliminate these, but better safe than sorry.\n\n                write!(self.out, \"static const \")?;\n                self.write_type(module, global.ty)?;\n\n                let heap_var = if comparison {\n                    COMPARISON_SAMPLER_HEAP_VAR\n                } else {\n                    SAMPLER_HEAP_VAR\n                };\n\n                let index_buffer_name = &self.wrapped.sampler_index_buffers[&key];\n                let name = &self.names[&NameKey::GlobalVariable(handle)];\n                writeln!(\n                    self.out,\n                    \" {name} = {heap_var}[{index_buffer_name}[{register}]];\",\n                    register = bt.register\n                )?;\n            }\n            TypeInner::BindingArray { .. } => {\n                // If we are generating a binding array, we cannot directly access the sampler as the index\n                // into the sampler index buffer is unknown at compile time. Instead we generate a constant\n                // that represents the \"base\" index into the sampler index buffer. This constant is added\n                // to the user provided index to get the final index into the sampler index buffer.\n\n                let name = &self.names[&NameKey::GlobalVariable(handle)];\n                writeln!(\n                    self.out,\n                    \"static const uint {name} = {register};\",\n                    register = bt.register\n                )?;\n            }\n            _ => unreachable!(),\n        };\n\n        Ok(())\n    }\n\n    /// Write the declarations for an external texture global variable.\n    /// These are emitted as multiple global variables: Three `Texture2D`s\n    /// (one for each plane) and a parameters cbuffer.\n    fn write_global_external_texture(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::GlobalVariable>,\n        global: &crate::GlobalVariable,\n    ) -> BackendResult {\n        let res_binding = global\n            .binding\n            .as_ref()\n            .expect(\"External texture global variables must have a resource binding\");\n        let ext_tex_bindings = match self\n            .options\n            .resolve_external_texture_resource_binding(res_binding)\n        {\n            Ok(bindings) => bindings,\n            Err(err) => {\n                log::debug!(\n                    \"Skipping global {:?} (name {:?}) for being inaccessible: {}\",\n                    handle,\n                    global.name,\n                    err,\n                );\n                return Ok(());\n            }\n        };\n\n        let mut write_plane = |bt: &super::BindTarget, name| -> BackendResult {\n            write!(\n                self.out,\n                \"Texture2D<float4> {}: register(t{}\",\n                name, bt.register\n            )?;\n            if bt.space != 0 {\n                write!(self.out, \", space{}\", bt.space)?;\n            }\n            writeln!(self.out, \");\")?;\n            Ok(())\n        };\n        for (i, bt) in ext_tex_bindings.planes.iter().enumerate() {\n            let plane_name = &self.names\n                [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Plane(i))];\n            write_plane(bt, plane_name)?;\n        }\n\n        let params_name = &self.names\n            [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Params)];\n        let params_ty_name =\n            &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n        write!(\n            self.out,\n            \"cbuffer {}: register(b{}\",\n            params_name, ext_tex_bindings.params.register\n        )?;\n        if ext_tex_bindings.params.space != 0 {\n            write!(self.out, \", space{}\", ext_tex_bindings.params.space)?;\n        }\n        writeln!(self.out, \") {{ {params_ty_name} {params_name}; }};\")?;\n\n        Ok(())\n    }\n\n    /// Helper method used to write global constants\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_global_constant(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Constant>,\n    ) -> BackendResult {\n        write!(self.out, \"static const \")?;\n        let constant = &module.constants[handle];\n        self.write_type(module, constant.ty)?;\n        let name = &self.names[&NameKey::Constant(handle)];\n        write!(self.out, \" {name}\")?;\n        // Write size for array type\n        if let TypeInner::Array { base, size, .. } = module.types[constant.ty].inner {\n            self.write_array_size(module, base, size)?;\n        }\n        write!(self.out, \" = \")?;\n        self.write_const_expression(module, constant.init, &module.global_expressions)?;\n        writeln!(self.out, \";\")?;\n        Ok(())\n    }\n\n    pub(super) fn write_array_size(\n        &mut self,\n        module: &Module,\n        base: Handle<crate::Type>,\n        size: crate::ArraySize,\n    ) -> BackendResult {\n        write!(self.out, \"[\")?;\n\n        match size.resolve(module.to_ctx())? {\n            proc::IndexableLength::Known(size) => {\n                write!(self.out, \"{size}\")?;\n            }\n            proc::IndexableLength::Dynamic => unreachable!(),\n        }\n\n        write!(self.out, \"]\")?;\n\n        if let TypeInner::Array {\n            base: next_base,\n            size: next_size,\n            ..\n        } = module.types[base].inner\n        {\n            self.write_array_size(module, next_base, next_size)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write structs\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_struct(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Type>,\n        members: &[crate::StructMember],\n        span: u32,\n        shader_stage: Option<(ShaderStage, Io)>,\n    ) -> BackendResult {\n        // Write struct name\n        let struct_name = &self.names[&NameKey::Type(handle)];\n        writeln!(self.out, \"struct {struct_name} {{\")?;\n\n        let mut last_offset = 0;\n        for (index, member) in members.iter().enumerate() {\n            if member.binding.is_none() && member.offset > last_offset {\n                // using int as padding should work as long as the backend\n                // doesn't support a type that's less than 4 bytes in size\n                // (Error::UnsupportedScalar catches this)\n                let padding = (member.offset - last_offset) / 4;\n                for i in 0..padding {\n                    writeln!(self.out, \"{}int _pad{}_{};\", back::INDENT, index, i)?;\n                }\n            }\n            let ty_inner = &module.types[member.ty].inner;\n            last_offset = member.offset + ty_inner.size_hlsl(module.to_ctx())?;\n\n            // The indentation is only for readability\n            write!(self.out, \"{}\", back::INDENT)?;\n\n            match module.types[member.ty].inner {\n                TypeInner::Array { base, size, .. } => {\n                    // HLSL arrays are written as `type name[size]`\n\n                    self.write_global_type(module, member.ty)?;\n\n                    // Write `name`\n                    write!(\n                        self.out,\n                        \" {}\",\n                        &self.names[&NameKey::StructMember(handle, index as u32)]\n                    )?;\n                    // Write [size]\n                    self.write_array_size(module, base, size)?;\n                }\n                // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.\n                // See the module-level block comment in mod.rs for details.\n                TypeInner::Matrix {\n                    rows,\n                    columns,\n                    scalar,\n                } if member.binding.is_none() && rows == crate::VectorSize::Bi => {\n                    let vec_ty = TypeInner::Vector { size: rows, scalar };\n                    let field_name_key = NameKey::StructMember(handle, index as u32);\n\n                    for i in 0..columns as u8 {\n                        if i != 0 {\n                            write!(self.out, \"; \")?;\n                        }\n                        self.write_value_type(module, &vec_ty)?;\n                        write!(self.out, \" {}_{}\", &self.names[&field_name_key], i)?;\n                    }\n                }\n                _ => {\n                    // Write modifier before type\n                    if let Some(ref binding) = member.binding {\n                        self.write_modifier(binding)?;\n                    }\n\n                    // Even though Naga IR matrices are column-major, we must describe\n                    // matrices passed from the CPU as being in row-major order.\n                    // See the module-level block comment in mod.rs for details.\n                    if let TypeInner::Matrix { .. } = module.types[member.ty].inner {\n                        write!(self.out, \"row_major \")?;\n                    }\n\n                    // Write the member type and name\n                    self.write_type(module, member.ty)?;\n                    write!(\n                        self.out,\n                        \" {}\",\n                        &self.names[&NameKey::StructMember(handle, index as u32)]\n                    )?;\n                }\n            }\n\n            self.write_semantic(&member.binding, shader_stage)?;\n            writeln!(self.out, \";\")?;\n        }\n\n        // add padding at the end since sizes of types don't get rounded up to their alignment in HLSL\n        if members.last().unwrap().binding.is_none() && span > last_offset {\n            let padding = (span - last_offset) / 4;\n            for i in 0..padding {\n                writeln!(self.out, \"{}int _end_pad_{};\", back::INDENT, i)?;\n            }\n        }\n\n        writeln!(self.out, \"}};\")?;\n        Ok(())\n    }\n\n    /// Helper method used to write global/structs non image/sampler types\n    ///\n    /// # Notes\n    /// Adds no trailing or leading whitespace\n    pub(super) fn write_global_type(\n        &mut self,\n        module: &Module,\n        ty: Handle<crate::Type>,\n    ) -> BackendResult {\n        let matrix_data = get_inner_matrix_data(module, ty);\n\n        // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.\n        // See the module-level block comment in mod.rs for details.\n        if let Some(MatrixType {\n            columns,\n            rows: crate::VectorSize::Bi,\n            width: 4,\n        }) = matrix_data\n        {\n            write!(self.out, \"__mat{}x2\", columns as u8)?;\n        } else {\n            // Even though Naga IR matrices are column-major, we must describe\n            // matrices passed from the CPU as being in row-major order.\n            // See the module-level block comment in mod.rs for details.\n            if matrix_data.is_some() {\n                write!(self.out, \"row_major \")?;\n            }\n\n            self.write_type(module, ty)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write non image/sampler types\n    ///\n    /// # Notes\n    /// Adds no trailing or leading whitespace\n    pub(super) fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {\n        let inner = &module.types[ty].inner;\n        match *inner {\n            TypeInner::Struct { .. } => write!(self.out, \"{}\", self.names[&NameKey::Type(ty)])?,\n            // hlsl array has the size separated from the base type\n            TypeInner::Array { base, .. } | TypeInner::BindingArray { base, .. } => {\n                self.write_type(module, base)?\n            }\n            ref other => self.write_value_type(module, other)?,\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write value types\n    ///\n    /// # Notes\n    /// Adds no trailing or leading whitespace\n    pub(super) fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {\n        match *inner {\n            TypeInner::Scalar(scalar) | TypeInner::Atomic(scalar) => {\n                write!(self.out, \"{}\", scalar.to_hlsl_str()?)?;\n            }\n            TypeInner::Vector { size, scalar } => {\n                write!(\n                    self.out,\n                    \"{}{}\",\n                    scalar.to_hlsl_str()?,\n                    common::vector_size_str(size)\n                )?;\n            }\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                // The IR supports only float matrix\n                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-matrix\n\n                // Because of the implicit transpose all matrices have in HLSL, we need to transpose the size as well.\n                write!(\n                    self.out,\n                    \"{}{}x{}\",\n                    scalar.to_hlsl_str()?,\n                    common::vector_size_str(columns),\n                    common::vector_size_str(rows),\n                )?;\n            }\n            TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                self.write_image_type(dim, arrayed, class)?;\n            }\n            TypeInner::Sampler { comparison } => {\n                let sampler = if comparison {\n                    \"SamplerComparisonState\"\n                } else {\n                    \"SamplerState\"\n                };\n                write!(self.out, \"{sampler}\")?;\n            }\n            // HLSL arrays are written as `type name[size]`\n            // Current code is written arrays only as `[size]`\n            // Base `type` and `name` should be written outside\n            TypeInner::Array { base, size, .. } | TypeInner::BindingArray { base, size } => {\n                self.write_array_size(module, base, size)?;\n            }\n            TypeInner::AccelerationStructure { .. } => {\n                write!(self.out, \"RaytracingAccelerationStructure\")?;\n            }\n            TypeInner::RayQuery { .. } => {\n                // these are constant flags, there are dynamic flags also but constant flags are not supported by naga\n                write!(self.out, \"RayQuery<RAY_FLAG_NONE>\")?;\n            }\n            _ => return Err(Error::Unimplemented(format!(\"write_value_type {inner:?}\"))),\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write functions\n    /// # Notes\n    /// Ends in a newline\n    fn write_function(\n        &mut self,\n        module: &Module,\n        name: &str,\n        func: &crate::Function,\n        func_ctx: &back::FunctionCtx<'_>,\n        info: &valid::FunctionInfo,\n    ) -> BackendResult {\n        // Function Declaration Syntax - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-function-syntax\n\n        self.update_expressions_to_bake(module, func, info);\n\n        if let Some(ref result) = func.result {\n            // Write typedef if return type is an array\n            let array_return_type = match module.types[result.ty].inner {\n                TypeInner::Array { base, size, .. } => {\n                    let array_return_type = self.namer.call(&format!(\"ret_{name}\"));\n                    write!(self.out, \"typedef \")?;\n                    self.write_type(module, result.ty)?;\n                    write!(self.out, \" {array_return_type}\")?;\n                    self.write_array_size(module, base, size)?;\n                    writeln!(self.out, \";\")?;\n                    Some(array_return_type)\n                }\n                _ => None,\n            };\n\n            // Write modifier\n            if let Some(\n                ref binding @ crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }),\n            ) = result.binding\n            {\n                self.write_modifier(binding)?;\n            }\n\n            // Write return type\n            match func_ctx.ty {\n                back::FunctionType::Function(_) => {\n                    if let Some(array_return_type) = array_return_type {\n                        write!(self.out, \"{array_return_type}\")?;\n                    } else {\n                        self.write_type(module, result.ty)?;\n                    }\n                }\n                back::FunctionType::EntryPoint(index) => {\n                    if let Some(ref ep_output) =\n                        self.entry_point_io.get(&(index as usize)).unwrap().output\n                    {\n                        write!(self.out, \"{}\", ep_output.ty_name)?;\n                    } else {\n                        self.write_type(module, result.ty)?;\n                    }\n                }\n            }\n        } else {\n            write!(self.out, \"void\")?;\n        }\n\n        // Write function name\n        write!(self.out, \" {name}(\")?;\n\n        let need_workgroup_variables_initialization =\n            self.need_workgroup_variables_initialization(func_ctx, module);\n\n        let needs_local_invocation_id_name = need_workgroup_variables_initialization;\n        let mut local_invocation_id_name = None;\n        // Write function arguments for non entry point functions\n        match func_ctx.ty {\n            back::FunctionType::Function(handle) => {\n                for (index, arg) in func.arguments.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n\n                    self.write_function_argument(module, handle, arg, index)?;\n                }\n            }\n            back::FunctionType::EntryPoint(ep_index) => {\n                if let Some(ref ep_input) =\n                    self.entry_point_io.get(&(ep_index as usize)).unwrap().input\n                {\n                    write!(self.out, \"{} {}\", ep_input.ty_name, ep_input.arg_name)?;\n                } else {\n                    let stage = module.entry_points[ep_index as usize].stage;\n                    for (index, arg) in func.arguments.iter().enumerate() {\n                        if index != 0 {\n                            write!(self.out, \", \")?;\n                        }\n                        self.write_type(module, arg.ty)?;\n\n                        let argument_name =\n                            &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];\n\n                        if arg.binding\n                            == Some(crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId))\n                        {\n                            local_invocation_id_name = Some(argument_name.clone());\n                        }\n\n                        write!(self.out, \" {argument_name}\")?;\n                        if let TypeInner::Array { base, size, .. } = module.types[arg.ty].inner {\n                            self.write_array_size(module, base, size)?;\n                        }\n\n                        self.write_semantic(&arg.binding, Some((stage, Io::Input)))?;\n                    }\n                }\n                if needs_local_invocation_id_name && local_invocation_id_name.is_none() {\n                    if self\n                        .entry_point_io\n                        .get(&(ep_index as usize))\n                        .unwrap()\n                        .input\n                        .is_some()\n                        || !func.arguments.is_empty()\n                    {\n                        write!(self.out, \", \")?;\n                    }\n                    let var_name = self.namer.call(\"local_invocation_id\");\n                    write!(self.out, \"uint3 {var_name} : SV_GroupThreadID\")?;\n                    local_invocation_id_name = Some(var_name);\n                }\n            }\n        }\n        // Ends of arguments\n        write!(self.out, \")\")?;\n\n        // Write semantic if it present\n        if let back::FunctionType::EntryPoint(index) = func_ctx.ty {\n            let stage = module.entry_points[index as usize].stage;\n            if let Some(crate::FunctionResult { ref binding, .. }) = func.result {\n                self.write_semantic(binding, Some((stage, Io::Output)))?;\n            }\n        }\n\n        // Function body start\n        writeln!(self.out)?;\n        writeln!(self.out, \"{{\")?;\n\n        if need_workgroup_variables_initialization {\n            self.write_workgroup_variables_initialization(\n                func_ctx,\n                module,\n                // need_workgroup_variables_initialization forces this to be written\n                // if the user doesn't specify it (so this must be Some())\n                local_invocation_id_name.unwrap(),\n            )?;\n        }\n\n        if let back::FunctionType::EntryPoint(index) = func_ctx.ty {\n            self.write_ep_arguments_initialization(module, func, index)?;\n        }\n\n        // Write function local variables\n        for (handle, local) in func.local_variables.iter() {\n            // Write indentation (only for readability)\n            write!(self.out, \"{}\", back::INDENT)?;\n\n            // Write the local name\n            // The leading space is important\n            self.write_type(module, local.ty)?;\n            write!(self.out, \" {}\", self.names[&func_ctx.name_key(handle)])?;\n            // Write size for array type\n            if let TypeInner::Array { base, size, .. } = module.types[local.ty].inner {\n                self.write_array_size(module, base, size)?;\n            }\n\n            let is_ray_query = match module.types[local.ty].inner {\n                // from https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#tracerayinline-example-1 it seems that ray queries shouldn't be zeroed\n                TypeInner::RayQuery { .. } => true,\n                _ => {\n                    write!(self.out, \" = \")?;\n                    // Write the local initializer if needed\n                    if let Some(init) = local.init {\n                        self.write_expr(module, init, func_ctx)?;\n                    } else {\n                        // Zero initialize local variables\n                        self.write_default_init(module, local.ty)?;\n                    }\n                    false\n                }\n            };\n            // Finish the local with `;` and add a newline (only for readability)\n            writeln!(self.out, \";\")?;\n            // If it's a ray query, we also want a tracker variable\n            if is_ray_query {\n                write!(self.out, \"{}\", back::INDENT)?;\n                self.write_value_type(module, &TypeInner::Scalar(Scalar::U32))?;\n                writeln!(\n                    self.out,\n                    \" {RAY_QUERY_TRACKER_VARIABLE_PREFIX}{} = 0;\",\n                    self.names[&func_ctx.name_key(handle)]\n                )?;\n            }\n        }\n\n        if !func.local_variables.is_empty() {\n            writeln!(self.out)?;\n        }\n\n        // Write the function body (statement list)\n        for sta in func.body.iter() {\n            // The indentation should always be 1 when writing the function body\n            self.write_stmt(module, sta, func_ctx, back::Level(1))?;\n        }\n\n        writeln!(self.out, \"}}\")?;\n\n        self.named_expressions.clear();\n\n        Ok(())\n    }\n\n    fn write_function_argument(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Function>,\n        arg: &crate::FunctionArgument,\n        index: usize,\n    ) -> BackendResult {\n        // External texture arguments must be expanded into separate\n        // arguments for each plane and the params buffer.\n        if let TypeInner::Image {\n            class: crate::ImageClass::External,\n            ..\n        } = module.types[arg.ty].inner\n        {\n            return self.write_function_external_texture_argument(module, handle, index);\n        }\n\n        // Write argument type\n        let arg_ty = match module.types[arg.ty].inner {\n            // pointers in function arguments are expected and resolve to `inout`\n            TypeInner::Pointer { base, .. } => {\n                //TODO: can we narrow this down to just `in` when possible?\n                write!(self.out, \"inout \")?;\n                base\n            }\n            _ => arg.ty,\n        };\n        self.write_type(module, arg_ty)?;\n\n        let argument_name = &self.names[&NameKey::FunctionArgument(handle, index as u32)];\n\n        // Write argument name. Space is important.\n        write!(self.out, \" {argument_name}\")?;\n        if let TypeInner::Array { base, size, .. } = module.types[arg_ty].inner {\n            self.write_array_size(module, base, size)?;\n        }\n\n        Ok(())\n    }\n\n    fn write_function_external_texture_argument(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Function>,\n        index: usize,\n    ) -> BackendResult {\n        let plane_names = [0, 1, 2].map(|i| {\n            &self.names[&NameKey::ExternalTextureFunctionArgument(\n                handle,\n                index as u32,\n                ExternalTextureNameKey::Plane(i),\n            )]\n        });\n        let params_name = &self.names[&NameKey::ExternalTextureFunctionArgument(\n            handle,\n            index as u32,\n            ExternalTextureNameKey::Params,\n        )];\n        let params_ty_name =\n            &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n        write!(\n            self.out,\n            \"Texture2D<float4> {}, Texture2D<float4> {}, Texture2D<float4> {}, {params_ty_name} {params_name}\",\n            plane_names[0], plane_names[1], plane_names[2],\n        )?;\n        Ok(())\n    }\n\n    fn need_workgroup_variables_initialization(\n        &mut self,\n        func_ctx: &back::FunctionCtx,\n        module: &Module,\n    ) -> bool {\n        self.options.zero_initialize_workgroup_memory\n            && func_ctx.ty.is_compute_like_entry_point(module)\n            && module.global_variables.iter().any(|(handle, var)| {\n                !func_ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup\n            })\n    }\n\n    fn write_workgroup_variables_initialization(\n        &mut self,\n        func_ctx: &back::FunctionCtx,\n        module: &Module,\n        local_invocation_id_name: String,\n    ) -> BackendResult {\n        let level = back::Level(1);\n\n        writeln!(\n            self.out,\n            \"{level}if (all({local_invocation_id_name} == uint3(0u, 0u, 0u))) {{\"\n        )?;\n\n        let vars = module.global_variables.iter().filter(|&(handle, var)| {\n            !func_ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup\n        });\n\n        for (handle, var) in vars {\n            let name = &self.names[&NameKey::GlobalVariable(handle)];\n            write!(self.out, \"{}{} = \", level.next(), name)?;\n            self.write_default_init(module, var.ty)?;\n            writeln!(self.out, \";\")?;\n        }\n\n        writeln!(self.out, \"{level}}}\")?;\n        self.write_control_barrier(crate::Barrier::WORK_GROUP, level)\n    }\n\n    /// Helper method used to write switches\n    fn write_switch(\n        &mut self,\n        module: &Module,\n        func_ctx: &back::FunctionCtx<'_>,\n        level: back::Level,\n        selector: Handle<crate::Expression>,\n        cases: &[crate::SwitchCase],\n    ) -> BackendResult {\n        // Write all cases\n        let indent_level_1 = level.next();\n        let indent_level_2 = indent_level_1.next();\n\n        // See docs of `back::continue_forward` module.\n        if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {\n            writeln!(self.out, \"{level}bool {variable} = false;\",)?;\n        };\n\n        // Check if there is only one body, by seeing if all except the last case are fall through\n        // with empty bodies. FXC doesn't handle these switches correctly, so\n        // we generate a `do {} while(false);` loop instead. There must be a default case, so there\n        // is no need to check if one of the cases would have matched.\n        let one_body = cases\n            .iter()\n            .rev()\n            .skip(1)\n            .all(|case| case.fall_through && case.body.is_empty());\n        if one_body {\n            // Start the do-while\n            writeln!(self.out, \"{level}do {{\")?;\n            // Note: Expressions have no side-effects so we don't need to emit selector expression.\n\n            // Body\n            if let Some(case) = cases.last() {\n                for sta in case.body.iter() {\n                    self.write_stmt(module, sta, func_ctx, indent_level_1)?;\n                }\n            }\n            // End do-while\n            writeln!(self.out, \"{level}}} while(false);\")?;\n        } else {\n            // Start the switch\n            write!(self.out, \"{level}\")?;\n            write!(self.out, \"switch(\")?;\n            self.write_expr(module, selector, func_ctx)?;\n            writeln!(self.out, \") {{\")?;\n\n            for (i, case) in cases.iter().enumerate() {\n                match case.value {\n                    crate::SwitchValue::I32(value) => {\n                        write!(self.out, \"{indent_level_1}case {value}:\")?\n                    }\n                    crate::SwitchValue::U32(value) => {\n                        write!(self.out, \"{indent_level_1}case {value}u:\")?\n                    }\n                    crate::SwitchValue::Default => write!(self.out, \"{indent_level_1}default:\")?,\n                }\n\n                // The new block is not only stylistic, it plays a role here:\n                // We might end up having to write the same case body\n                // multiple times due to FXC not supporting fallthrough.\n                // Therefore, some `Expression`s written by `Statement::Emit`\n                // will end up having the same name (`_expr<handle_index>`).\n                // So we need to put each case in its own scope.\n                let write_block_braces = !(case.fall_through && case.body.is_empty());\n                if write_block_braces {\n                    writeln!(self.out, \" {{\")?;\n                } else {\n                    writeln!(self.out)?;\n                }\n\n                // Although FXC does support a series of case clauses before\n                // a block[^yes], it does not support fallthrough from a\n                // non-empty case block to the next[^no]. If this case has a\n                // non-empty body with a fallthrough, emulate that by\n                // duplicating the bodies of all the cases it would fall\n                // into as extensions of this case's own body. This makes\n                // the HLSL output potentially quadratic in the size of the\n                // Naga IR.\n                //\n                // [^yes]: ```hlsl\n                // case 1:\n                // case 2: do_stuff()\n                // ```\n                // [^no]: ```hlsl\n                // case 1: do_this();\n                // case 2: do_that();\n                // ```\n                if case.fall_through && !case.body.is_empty() {\n                    let curr_len = i + 1;\n                    let end_case_idx = curr_len\n                        + cases\n                            .iter()\n                            .skip(curr_len)\n                            .position(|case| !case.fall_through)\n                            .unwrap();\n                    let indent_level_3 = indent_level_2.next();\n                    for case in &cases[i..=end_case_idx] {\n                        writeln!(self.out, \"{indent_level_2}{{\")?;\n                        let prev_len = self.named_expressions.len();\n                        for sta in case.body.iter() {\n                            self.write_stmt(module, sta, func_ctx, indent_level_3)?;\n                        }\n                        // Clear all named expressions that were previously inserted by the statements in the block\n                        self.named_expressions.truncate(prev_len);\n                        writeln!(self.out, \"{indent_level_2}}}\")?;\n                    }\n\n                    let last_case = &cases[end_case_idx];\n                    if last_case.body.last().is_none_or(|s| !s.is_terminator()) {\n                        writeln!(self.out, \"{indent_level_2}break;\")?;\n                    }\n                } else {\n                    for sta in case.body.iter() {\n                        self.write_stmt(module, sta, func_ctx, indent_level_2)?;\n                    }\n                    if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator()) {\n                        writeln!(self.out, \"{indent_level_2}break;\")?;\n                    }\n                }\n\n                if write_block_braces {\n                    writeln!(self.out, \"{indent_level_1}}}\")?;\n                }\n            }\n\n            writeln!(self.out, \"{level}}}\")?;\n        }\n\n        // Handle any forwarded continue statements.\n        use back::continue_forward::ExitControlFlow;\n        let op = match self.continue_ctx.exit_switch() {\n            ExitControlFlow::None => None,\n            ExitControlFlow::Continue { variable } => Some((\"continue\", variable)),\n            ExitControlFlow::Break { variable } => Some((\"break\", variable)),\n        };\n        if let Some((control_flow, variable)) = op {\n            writeln!(self.out, \"{level}if ({variable}) {{\")?;\n            writeln!(self.out, \"{indent_level_1}{control_flow};\")?;\n            writeln!(self.out, \"{level}}}\")?;\n        }\n\n        Ok(())\n    }\n\n    fn write_index(\n        &mut self,\n        module: &Module,\n        index: Index,\n        func_ctx: &back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        match index {\n            Index::Static(index) => {\n                write!(self.out, \"{index}\")?;\n            }\n            Index::Expression(index) => {\n                self.write_expr(module, index, func_ctx)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Helper method used to write statements\n    ///\n    /// # Notes\n    /// Always adds a newline\n    fn write_stmt(\n        &mut self,\n        module: &Module,\n        stmt: &crate::Statement,\n        func_ctx: &back::FunctionCtx<'_>,\n        level: back::Level,\n    ) -> BackendResult {\n        use crate::Statement;\n\n        match *stmt {\n            Statement::Emit(ref range) => {\n                for handle in range.clone() {\n                    let ptr_class = func_ctx.resolve_type(handle, &module.types).pointer_space();\n                    let expr_name = if ptr_class.is_some() {\n                        // HLSL can't save a pointer-valued expression in a variable,\n                        // but we shouldn't ever need to: they should never be named expressions,\n                        // and none of the expression types flagged by bake_ref_count can be pointer-valued.\n                        None\n                    } else if let Some(name) = func_ctx.named_expressions.get(&handle) {\n                        // Front end provides names for all variables at the start of writing.\n                        // But we write them to step by step. We need to recache them\n                        // Otherwise, we could accidentally write variable name instead of full expression.\n                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.\n                        Some(self.namer.call(name))\n                    } else if self.need_bake_expressions.contains(&handle) {\n                        Some(Baked(handle).to_string())\n                    } else {\n                        None\n                    };\n\n                    if let Some(name) = expr_name {\n                        write!(self.out, \"{level}\")?;\n                        self.write_named_expr(module, handle, name, handle, func_ctx)?;\n                    }\n                }\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::Block(ref block) => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"{{\")?;\n                for sta in block.iter() {\n                    // Increase the indentation to help with readability\n                    self.write_stmt(module, sta, func_ctx, level.next())?\n                }\n                writeln!(self.out, \"{level}}}\")?\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::If {\n                condition,\n                ref accept,\n                ref reject,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"if (\")?;\n                self.write_expr(module, condition, func_ctx)?;\n                writeln!(self.out, \") {{\")?;\n\n                let l2 = level.next();\n                for sta in accept {\n                    // Increase indentation to help with readability\n                    self.write_stmt(module, sta, func_ctx, l2)?;\n                }\n\n                // If there are no statements in the reject block we skip writing it\n                // This is only for readability\n                if !reject.is_empty() {\n                    writeln!(self.out, \"{level}}} else {{\")?;\n\n                    for sta in reject {\n                        // Increase indentation to help with readability\n                        self.write_stmt(module, sta, func_ctx, l2)?;\n                    }\n                }\n\n                writeln!(self.out, \"{level}}}\")?\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::Kill => writeln!(self.out, \"{level}discard;\")?,\n            Statement::Return { value: None } => {\n                writeln!(self.out, \"{level}return;\")?;\n            }\n            Statement::Return { value: Some(expr) } => {\n                let base_ty_res = &func_ctx.info[expr].ty;\n                let mut resolved = base_ty_res.inner_with(&module.types);\n                if let TypeInner::Pointer { base, space: _ } = *resolved {\n                    resolved = &module.types[base].inner;\n                }\n\n                if let TypeInner::Struct { .. } = *resolved {\n                    // We can safely unwrap here, since we now we working with struct\n                    let ty = base_ty_res.handle().unwrap();\n                    let struct_name = &self.names[&NameKey::Type(ty)];\n                    let variable_name = self.namer.call(&struct_name.to_lowercase());\n                    write!(self.out, \"{level}const {struct_name} {variable_name} = \",)?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    writeln!(self.out, \";\")?;\n\n                    // for entry point returns, we may need to reshuffle the outputs into a different struct\n                    let ep_output = match func_ctx.ty {\n                        back::FunctionType::Function(_) => None,\n                        back::FunctionType::EntryPoint(index) => self\n                            .entry_point_io\n                            .get(&(index as usize))\n                            .unwrap()\n                            .output\n                            .as_ref(),\n                    };\n                    let final_name = match ep_output {\n                        Some(ep_output) => {\n                            let final_name = self.namer.call(&variable_name);\n                            write!(\n                                self.out,\n                                \"{}const {} {} = {{ \",\n                                level, ep_output.ty_name, final_name,\n                            )?;\n                            for (index, m) in ep_output.members.iter().enumerate() {\n                                if index != 0 {\n                                    write!(self.out, \", \")?;\n                                }\n                                let member_name = &self.names[&NameKey::StructMember(ty, m.index)];\n                                write!(self.out, \"{variable_name}.{member_name}\")?;\n                            }\n                            writeln!(self.out, \" }};\")?;\n                            final_name\n                        }\n                        None => variable_name,\n                    };\n                    writeln!(self.out, \"{level}return {final_name};\")?;\n                } else {\n                    write!(self.out, \"{level}return \")?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    writeln!(self.out, \";\")?\n                }\n            }\n            Statement::Store { pointer, value } => {\n                let ty_inner = func_ctx.resolve_type(pointer, &module.types);\n                if let Some(crate::AddressSpace::Storage { .. }) = ty_inner.pointer_space() {\n                    let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;\n                    self.write_storage_store(\n                        module,\n                        var_handle,\n                        StoreValue::Expression(value),\n                        func_ctx,\n                        level,\n                        None,\n                    )?;\n                } else {\n                    // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.\n                    // See the module-level block comment in mod.rs for details.\n                    //\n                    // We handle matrix Stores here directly (including sub accesses for Vectors and Scalars).\n                    // Loads are handled by `Expression::AccessIndex` (since sub accesses work fine for Loads).\n                    enum MatrixAccess {\n                        Direct {\n                            base: Handle<crate::Expression>,\n                            index: u32,\n                        },\n                        Struct {\n                            columns: crate::VectorSize,\n                            base: Handle<crate::Expression>,\n                        },\n                    }\n\n                    let get_members = |expr: Handle<crate::Expression>| {\n                        let resolved = func_ctx.resolve_type(expr, &module.types);\n                        match *resolved {\n                            TypeInner::Pointer { base, .. } => match module.types[base].inner {\n                                TypeInner::Struct { ref members, .. } => Some(members),\n                                _ => None,\n                            },\n                            _ => None,\n                        }\n                    };\n\n                    write!(self.out, \"{level}\")?;\n\n                    let matrix_access_on_lhs =\n                        find_matrix_in_access_chain(module, pointer, func_ctx).and_then(\n                            |(matrix_expr, vector, scalar)| match (\n                                func_ctx.resolve_type(matrix_expr, &module.types),\n                                &func_ctx.expressions[matrix_expr],\n                            ) {\n                                (\n                                    &TypeInner::Pointer { base: ty, .. },\n                                    &crate::Expression::AccessIndex { base, index },\n                                ) if matches!(\n                                    module.types[ty].inner,\n                                    TypeInner::Matrix {\n                                        rows: crate::VectorSize::Bi,\n                                        ..\n                                    }\n                                ) && get_members(base)\n                                    .map(|members| members[index as usize].binding.is_none())\n                                    == Some(true) =>\n                                {\n                                    Some((MatrixAccess::Direct { base, index }, vector, scalar))\n                                }\n                                _ => {\n                                    if let Some(MatrixType {\n                                        columns,\n                                        rows: crate::VectorSize::Bi,\n                                        width: 4,\n                                    }) = get_inner_matrix_of_struct_array_member(\n                                        module,\n                                        matrix_expr,\n                                        func_ctx,\n                                        true,\n                                    ) {\n                                        Some((\n                                            MatrixAccess::Struct {\n                                                columns,\n                                                base: matrix_expr,\n                                            },\n                                            vector,\n                                            scalar,\n                                        ))\n                                    } else {\n                                        None\n                                    }\n                                }\n                            },\n                        );\n\n                    match matrix_access_on_lhs {\n                        Some((MatrixAccess::Direct { index, base }, vector, scalar)) => {\n                            let base_ty_res = &func_ctx.info[base].ty;\n                            let resolved = base_ty_res.inner_with(&module.types);\n                            let ty = match *resolved {\n                                TypeInner::Pointer { base, .. } => base,\n                                _ => base_ty_res.handle().unwrap(),\n                            };\n\n                            if let Some(Index::Static(vec_index)) = vector {\n                                self.write_expr(module, base, func_ctx)?;\n                                write!(\n                                    self.out,\n                                    \".{}_{}\",\n                                    &self.names[&NameKey::StructMember(ty, index)],\n                                    vec_index\n                                )?;\n\n                                if let Some(scalar_index) = scalar {\n                                    write!(self.out, \"[\")?;\n                                    self.write_index(module, scalar_index, func_ctx)?;\n                                    write!(self.out, \"]\")?;\n                                }\n\n                                write!(self.out, \" = \")?;\n                                self.write_expr(module, value, func_ctx)?;\n                                writeln!(self.out, \";\")?;\n                            } else {\n                                let access = WrappedStructMatrixAccess { ty, index };\n                                match (&vector, &scalar) {\n                                    (&Some(_), &Some(_)) => {\n                                        self.write_wrapped_struct_matrix_set_scalar_function_name(\n                                            access,\n                                        )?;\n                                    }\n                                    (&Some(_), &None) => {\n                                        self.write_wrapped_struct_matrix_set_vec_function_name(\n                                            access,\n                                        )?;\n                                    }\n                                    (&None, _) => {\n                                        self.write_wrapped_struct_matrix_set_function_name(access)?;\n                                    }\n                                }\n\n                                write!(self.out, \"(\")?;\n                                self.write_expr(module, base, func_ctx)?;\n                                write!(self.out, \", \")?;\n                                self.write_expr(module, value, func_ctx)?;\n\n                                if let Some(Index::Expression(vec_index)) = vector {\n                                    write!(self.out, \", \")?;\n                                    self.write_expr(module, vec_index, func_ctx)?;\n\n                                    if let Some(scalar_index) = scalar {\n                                        write!(self.out, \", \")?;\n                                        self.write_index(module, scalar_index, func_ctx)?;\n                                    }\n                                }\n                                writeln!(self.out, \");\")?;\n                            }\n                        }\n                        Some((\n                            MatrixAccess::Struct { columns, base },\n                            Some(Index::Expression(vec_index)),\n                            scalar,\n                        )) => {\n                            // We handle `Store`s to __matCx2 column vectors and scalar elements via\n                            // the previously injected functions __set_col_of_matCx2 / __set_el_of_matCx2.\n\n                            if scalar.is_some() {\n                                write!(self.out, \"__set_el_of_mat{}x2\", columns as u8)?;\n                            } else {\n                                write!(self.out, \"__set_col_of_mat{}x2\", columns as u8)?;\n                            }\n                            write!(self.out, \"(\")?;\n                            self.write_expr(module, base, func_ctx)?;\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, vec_index, func_ctx)?;\n\n                            if let Some(scalar_index) = scalar {\n                                write!(self.out, \", \")?;\n                                self.write_index(module, scalar_index, func_ctx)?;\n                            }\n\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, value, func_ctx)?;\n\n                            writeln!(self.out, \");\")?;\n                        }\n                        Some((MatrixAccess::Struct { .. }, Some(Index::Static(_)), _))\n                        | Some((MatrixAccess::Struct { .. }, None, _))\n                        | None => {\n                            self.write_expr(module, pointer, func_ctx)?;\n                            write!(self.out, \" = \")?;\n\n                            // We cast the RHS of this store in cases where the LHS\n                            // is a struct member with type:\n                            //  - matCx2 or\n                            //  - a (possibly nested) array of matCx2's\n                            if let Some(MatrixType {\n                                columns,\n                                rows: crate::VectorSize::Bi,\n                                width: 4,\n                            }) = get_inner_matrix_of_struct_array_member(\n                                module, pointer, func_ctx, false,\n                            ) {\n                                let mut resolved = func_ctx.resolve_type(pointer, &module.types);\n                                if let TypeInner::Pointer { base, .. } = *resolved {\n                                    resolved = &module.types[base].inner;\n                                }\n\n                                write!(self.out, \"(__mat{}x2\", columns as u8)?;\n                                if let TypeInner::Array { base, size, .. } = *resolved {\n                                    self.write_array_size(module, base, size)?;\n                                }\n                                write!(self.out, \")\")?;\n                            }\n\n                            self.write_expr(module, value, func_ctx)?;\n                            writeln!(self.out, \";\")?\n                        }\n                    }\n                }\n            }\n            Statement::Loop {\n                ref body,\n                ref continuing,\n                break_if,\n            } => {\n                let force_loop_bound_statements = self.gen_force_bounded_loop_statements(level);\n                let gate_name = (!continuing.is_empty() || break_if.is_some())\n                    .then(|| self.namer.call(\"loop_init\"));\n\n                if let Some((ref decl, _)) = force_loop_bound_statements {\n                    writeln!(self.out, \"{decl}\")?;\n                }\n                if let Some(ref gate_name) = gate_name {\n                    writeln!(self.out, \"{level}bool {gate_name} = true;\")?;\n                }\n\n                self.continue_ctx.enter_loop();\n                writeln!(self.out, \"{level}while(true) {{\")?;\n                if let Some((_, ref break_and_inc)) = force_loop_bound_statements {\n                    writeln!(self.out, \"{break_and_inc}\")?;\n                }\n                let l2 = level.next();\n                if let Some(gate_name) = gate_name {\n                    writeln!(self.out, \"{l2}if (!{gate_name}) {{\")?;\n                    let l3 = l2.next();\n                    for sta in continuing.iter() {\n                        self.write_stmt(module, sta, func_ctx, l3)?;\n                    }\n                    if let Some(condition) = break_if {\n                        write!(self.out, \"{l3}if (\")?;\n                        self.write_expr(module, condition, func_ctx)?;\n                        writeln!(self.out, \") {{\")?;\n                        writeln!(self.out, \"{}break;\", l3.next())?;\n                        writeln!(self.out, \"{l3}}}\")?;\n                    }\n                    writeln!(self.out, \"{l2}}}\")?;\n                    writeln!(self.out, \"{l2}{gate_name} = false;\")?;\n                }\n\n                for sta in body.iter() {\n                    self.write_stmt(module, sta, func_ctx, l2)?;\n                }\n\n                writeln!(self.out, \"{level}}}\")?;\n                self.continue_ctx.exit_loop();\n            }\n            Statement::Break => writeln!(self.out, \"{level}break;\")?,\n            Statement::Continue => {\n                if let Some(variable) = self.continue_ctx.continue_encountered() {\n                    writeln!(self.out, \"{level}{variable} = true;\")?;\n                    writeln!(self.out, \"{level}break;\")?\n                } else {\n                    writeln!(self.out, \"{level}continue;\")?\n                }\n            }\n            Statement::ControlBarrier(barrier) => {\n                self.write_control_barrier(barrier, level)?;\n            }\n            Statement::MemoryBarrier(barrier) => {\n                self.write_memory_barrier(barrier, level)?;\n            }\n            Statement::ImageStore {\n                image,\n                coordinate,\n                array_index,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n                self.write_expr(module, image, func_ctx)?;\n\n                write!(self.out, \"[\")?;\n                if let Some(index) = array_index {\n                    // Array index accepted only for texture_storage_2d_array, so we can safety use int3(coordinate, array_index) here\n                    write!(self.out, \"int3(\")?;\n                    self.write_expr(module, coordinate, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, index, func_ctx)?;\n                    write!(self.out, \")\")?;\n                } else {\n                    self.write_expr(module, coordinate, func_ctx)?;\n                }\n                write!(self.out, \"]\")?;\n\n                write!(self.out, \" = \")?;\n                self.write_expr(module, value, func_ctx)?;\n                writeln!(self.out, \";\")?;\n            }\n            Statement::Call {\n                function,\n                ref arguments,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                if let Some(expr) = result {\n                    write!(self.out, \"const \")?;\n                    let name = Baked(expr).to_string();\n                    let expr_ty = &func_ctx.info[expr].ty;\n                    let ty_inner = match *expr_ty {\n                        proc::TypeResolution::Handle(handle) => {\n                            self.write_type(module, handle)?;\n                            &module.types[handle].inner\n                        }\n                        proc::TypeResolution::Value(ref value) => {\n                            self.write_value_type(module, value)?;\n                            value\n                        }\n                    };\n                    write!(self.out, \" {name}\")?;\n                    if let TypeInner::Array { base, size, .. } = *ty_inner {\n                        self.write_array_size(module, base, size)?;\n                    }\n                    write!(self.out, \" = \")?;\n                    self.named_expressions.insert(expr, name);\n                }\n                let func_name = &self.names[&NameKey::Function(function)];\n                write!(self.out, \"{func_name}(\")?;\n                for (index, argument) in arguments.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    self.write_expr(module, *argument, func_ctx)?;\n                }\n                writeln!(self.out, \");\")?\n            }\n            Statement::Atomic {\n                pointer,\n                ref fun,\n                value,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let res_var_info = if let Some(res_handle) = result {\n                    let name = Baked(res_handle).to_string();\n                    match func_ctx.info[res_handle].ty {\n                        proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,\n                        proc::TypeResolution::Value(ref value) => {\n                            self.write_value_type(module, value)?\n                        }\n                    };\n                    write!(self.out, \" {name}; \")?;\n                    self.named_expressions.insert(res_handle, name.clone());\n                    Some((res_handle, name))\n                } else {\n                    None\n                };\n                let pointer_space = func_ctx\n                    .resolve_type(pointer, &module.types)\n                    .pointer_space()\n                    .unwrap();\n                let fun_str = fun.to_hlsl_suffix();\n                let compare_expr = match *fun {\n                    crate::AtomicFunction::Exchange { compare: Some(cmp) } => Some(cmp),\n                    _ => None,\n                };\n                match pointer_space {\n                    crate::AddressSpace::WorkGroup => {\n                        write!(self.out, \"Interlocked{fun_str}(\")?;\n                        self.write_expr(module, pointer, func_ctx)?;\n                        self.emit_hlsl_atomic_tail(\n                            module,\n                            func_ctx,\n                            fun,\n                            compare_expr,\n                            value,\n                            &res_var_info,\n                        )?;\n                    }\n                    crate::AddressSpace::Storage { .. } => {\n                        let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;\n                        let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                        let width = match func_ctx.resolve_type(value, &module.types) {\n                            &TypeInner::Scalar(Scalar { width: 8, .. }) => \"64\",\n                            _ => \"\",\n                        };\n                        write!(self.out, \"{var_name}.Interlocked{fun_str}{width}(\")?;\n                        let chain = mem::take(&mut self.temp_access_chain);\n                        self.write_storage_address(module, &chain, func_ctx)?;\n                        self.temp_access_chain = chain;\n                        self.emit_hlsl_atomic_tail(\n                            module,\n                            func_ctx,\n                            fun,\n                            compare_expr,\n                            value,\n                            &res_var_info,\n                        )?;\n                    }\n                    ref other => {\n                        return Err(Error::Custom(format!(\n                            \"invalid address space {other:?} for atomic statement\"\n                        )))\n                    }\n                }\n                if let Some(cmp) = compare_expr {\n                    if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {\n                        write!(\n                            self.out,\n                            \"{level}{res_name}.exchanged = ({res_name}.old_value == \"\n                        )?;\n                        self.write_expr(module, cmp, func_ctx)?;\n                        writeln!(self.out, \");\")?;\n                    }\n                }\n            }\n            Statement::ImageAtomic {\n                image,\n                coordinate,\n                array_index,\n                fun,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n\n                let fun_str = fun.to_hlsl_suffix();\n                write!(self.out, \"Interlocked{fun_str}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \"[\")?;\n                self.write_texture_coordinates(\n                    \"int\",\n                    coordinate,\n                    array_index,\n                    None,\n                    module,\n                    func_ctx,\n                )?;\n                write!(self.out, \"],\")?;\n\n                self.write_expr(module, value, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::WorkGroupUniformLoad { pointer, result } => {\n                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;\n                write!(self.out, \"{level}\")?;\n                let name = Baked(result).to_string();\n                self.write_named_expr(module, pointer, name, result, func_ctx)?;\n\n                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;\n            }\n            Statement::Switch {\n                selector,\n                ref cases,\n            } => {\n                self.write_switch(module, func_ctx, level, selector, cases)?;\n            }\n            Statement::RayQuery { query, ref fun } => {\n                // There are three possibilities for a ptr to be:\n                // 1. A variable\n                // 2. A function argument\n                // 3. part of a struct\n                //\n                // 2 and 3 are not possible, a ray query (in naga IR)\n                // is not allowed to be passed into a function, and\n                // all languages disallow it in a struct (you get fun results if\n                // you try it :) ).\n                //\n                // Therefore, the ray query expression must be a variable.\n                let crate::Expression::LocalVariable(query_var) = func_ctx.expressions[query]\n                else {\n                    unreachable!()\n                };\n\n                let tracker_expr_name = format!(\n                    \"{RAY_QUERY_TRACKER_VARIABLE_PREFIX}{}\",\n                    self.names[&func_ctx.name_key(query_var)]\n                );\n\n                match *fun {\n                    RayQueryFunction::Initialize {\n                        acceleration_structure,\n                        descriptor,\n                    } => {\n                        self.write_initialize_function(\n                            module,\n                            level,\n                            query,\n                            acceleration_structure,\n                            descriptor,\n                            &tracker_expr_name,\n                            func_ctx,\n                        )?;\n                    }\n                    RayQueryFunction::Proceed { result } => {\n                        self.write_proceed(\n                            module,\n                            level,\n                            query,\n                            result,\n                            &tracker_expr_name,\n                            func_ctx,\n                        )?;\n                    }\n                    RayQueryFunction::GenerateIntersection { hit_t } => {\n                        self.write_generate_intersection(\n                            module,\n                            level,\n                            query,\n                            hit_t,\n                            &tracker_expr_name,\n                            func_ctx,\n                        )?;\n                    }\n                    RayQueryFunction::ConfirmIntersection => {\n                        self.write_confirm_intersection(\n                            module,\n                            level,\n                            query,\n                            &tracker_expr_name,\n                            func_ctx,\n                        )?;\n                    }\n                    RayQueryFunction::Terminate => {\n                        self.write_terminate(module, level, query, &tracker_expr_name, func_ctx)?;\n                    }\n                }\n            }\n            Statement::SubgroupBallot { result, predicate } => {\n                write!(self.out, \"{level}\")?;\n                let name = Baked(result).to_string();\n                write!(self.out, \"const uint4 {name} = \")?;\n                self.named_expressions.insert(result, name);\n\n                write!(self.out, \"WaveActiveBallot(\")?;\n                match predicate {\n                    Some(predicate) => self.write_expr(module, predicate, func_ctx)?,\n                    None => write!(self.out, \"true\")?,\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupCollectiveOperation {\n                op,\n                collective_op,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"const \")?;\n                let name = Baked(result).to_string();\n                match func_ctx.info[result].ty {\n                    proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,\n                    proc::TypeResolution::Value(ref value) => {\n                        self.write_value_type(module, value)?\n                    }\n                };\n                write!(self.out, \" {name} = \")?;\n                self.named_expressions.insert(result, name);\n\n                match (collective_op, op) {\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {\n                        write!(self.out, \"WaveActiveAllTrue(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {\n                        write!(self.out, \"WaveActiveAnyTrue(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"WaveActiveSum(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"WaveActiveProduct(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {\n                        write!(self.out, \"WaveActiveMax(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {\n                        write!(self.out, \"WaveActiveMin(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {\n                        write!(self.out, \"WaveActiveBitAnd(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {\n                        write!(self.out, \"WaveActiveBitOr(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {\n                        write!(self.out, \"WaveActiveBitXor(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"WavePrefixSum(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"WavePrefixProduct(\")?\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {\n                        self.write_expr(module, argument, func_ctx)?;\n                        write!(self.out, \" + WavePrefixSum(\")?;\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {\n                        self.write_expr(module, argument, func_ctx)?;\n                        write!(self.out, \" * WavePrefixProduct(\")?;\n                    }\n                    _ => unimplemented!(),\n                }\n                self.write_expr(module, argument, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupGather {\n                mode,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"const \")?;\n                let name = Baked(result).to_string();\n                match func_ctx.info[result].ty {\n                    proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,\n                    proc::TypeResolution::Value(ref value) => {\n                        self.write_value_type(module, value)?\n                    }\n                };\n                write!(self.out, \" {name} = \")?;\n                self.named_expressions.insert(result, name);\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {\n                        write!(self.out, \"WaveReadLaneFirst(\")?;\n                        self.write_expr(module, argument, func_ctx)?;\n                    }\n                    crate::GatherMode::QuadBroadcast(index) => {\n                        write!(self.out, \"QuadReadLaneAt(\")?;\n                        self.write_expr(module, argument, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, index, func_ctx)?;\n                    }\n                    crate::GatherMode::QuadSwap(direction) => {\n                        match direction {\n                            crate::Direction::X => {\n                                write!(self.out, \"QuadReadAcrossX(\")?;\n                            }\n                            crate::Direction::Y => {\n                                write!(self.out, \"QuadReadAcrossY(\")?;\n                            }\n                            crate::Direction::Diagonal => {\n                                write!(self.out, \"QuadReadAcrossDiagonal(\")?;\n                            }\n                        }\n                        self.write_expr(module, argument, func_ctx)?;\n                    }\n                    _ => {\n                        write!(self.out, \"WaveReadLaneAt(\")?;\n                        self.write_expr(module, argument, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        match mode {\n                            crate::GatherMode::BroadcastFirst => unreachable!(),\n                            crate::GatherMode::Broadcast(index)\n                            | crate::GatherMode::Shuffle(index) => {\n                                self.write_expr(module, index, func_ctx)?;\n                            }\n                            crate::GatherMode::ShuffleDown(index) => {\n                                write!(self.out, \"WaveGetLaneIndex() + \")?;\n                                self.write_expr(module, index, func_ctx)?;\n                            }\n                            crate::GatherMode::ShuffleUp(index) => {\n                                write!(self.out, \"WaveGetLaneIndex() - \")?;\n                                self.write_expr(module, index, func_ctx)?;\n                            }\n                            crate::GatherMode::ShuffleXor(index) => {\n                                write!(self.out, \"WaveGetLaneIndex() ^ \")?;\n                                self.write_expr(module, index, func_ctx)?;\n                            }\n                            crate::GatherMode::QuadBroadcast(_) => unreachable!(),\n                            crate::GatherMode::QuadSwap(_) => unreachable!(),\n                        }\n                    }\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::CooperativeStore { .. } => unimplemented!(),\n            Statement::RayPipelineFunction(_) => unreachable!(),\n        }\n\n        Ok(())\n    }\n\n    fn write_const_expression(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        arena: &crate::Arena<crate::Expression>,\n    ) -> BackendResult {\n        self.write_possibly_const_expression(module, expr, arena, |writer, expr| {\n            writer.write_const_expression(module, expr, arena)\n        })\n    }\n\n    pub(super) fn write_literal(&mut self, literal: crate::Literal) -> BackendResult {\n        match literal {\n            crate::Literal::F64(value) => write!(self.out, \"{value:?}L\")?,\n            crate::Literal::F32(value) => write!(self.out, \"{value:?}\")?,\n            crate::Literal::F16(value) => write!(self.out, \"{value:?}h\")?,\n            crate::Literal::U32(value) => write!(self.out, \"{value}u\")?,\n            // `-2147483648` is parsed by some compilers as unary negation of\n            // positive 2147483648, which is too large for an int, causing\n            // issues for some compilers. Neither DXC nor FXC appear to have\n            // this problem, but this is not specified and could change. We\n            // therefore use `-2147483647 - 1` as a precaution.\n            crate::Literal::I32(value) if value == i32::MIN => {\n                write!(self.out, \"int({} - 1)\", value + 1)?\n            }\n            // HLSL has no suffix for explicit i32 literals, but not using any suffix\n            // makes the type ambiguous which prevents overload resolution from\n            // working. So we explicitly use the int() constructor syntax.\n            crate::Literal::I32(value) => write!(self.out, \"int({value})\")?,\n            crate::Literal::U64(value) => write!(self.out, \"{value}uL\")?,\n            // I64 version of the minimum I32 value issue described above.\n            crate::Literal::I64(value) if value == i64::MIN => {\n                write!(self.out, \"({}L - 1L)\", value + 1)?;\n            }\n            crate::Literal::I64(value) => write!(self.out, \"{value}L\")?,\n            crate::Literal::Bool(value) => write!(self.out, \"{value}\")?,\n            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {\n                return Err(Error::Custom(\n                    \"Abstract types should not appear in IR presented to backends\".into(),\n                ));\n            }\n        }\n        Ok(())\n    }\n\n    fn write_possibly_const_expression<E>(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        expressions: &crate::Arena<crate::Expression>,\n        write_expression: E,\n    ) -> BackendResult\n    where\n        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,\n    {\n        use crate::Expression;\n\n        match expressions[expr] {\n            Expression::Literal(literal) => {\n                self.write_literal(literal)?;\n            }\n            Expression::Constant(handle) => {\n                let constant = &module.constants[handle];\n                if constant.name.is_some() {\n                    write!(self.out, \"{}\", self.names[&NameKey::Constant(handle)])?;\n                } else {\n                    self.write_const_expression(module, constant.init, &module.global_expressions)?;\n                }\n            }\n            Expression::ZeroValue(ty) => {\n                self.write_wrapped_zero_value_function_name(module, WrappedZeroValue { ty })?;\n                write!(self.out, \"()\")?;\n            }\n            Expression::Compose { ty, ref components } => {\n                match module.types[ty].inner {\n                    TypeInner::Struct { .. } | TypeInner::Array { .. } => {\n                        self.write_wrapped_constructor_function_name(\n                            module,\n                            WrappedConstructor { ty },\n                        )?;\n                    }\n                    _ => {\n                        self.write_type(module, ty)?;\n                    }\n                };\n                write!(self.out, \"(\")?;\n                for (index, component) in components.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    write_expression(self, *component)?;\n                }\n                write!(self.out, \")\")?;\n            }\n            Expression::Splat { size, value } => {\n                // hlsl is not supported one value constructor\n                // if we write, for example, int4(0), dxc returns error:\n                // error: too few elements in vector initialization (expected 4 elements, have 1)\n                let number_of_components = match size {\n                    crate::VectorSize::Bi => \"xx\",\n                    crate::VectorSize::Tri => \"xxx\",\n                    crate::VectorSize::Quad => \"xxxx\",\n                };\n                write!(self.out, \"(\")?;\n                write_expression(self, value)?;\n                write!(self.out, \").{number_of_components}\")?\n            }\n            _ => {\n                return Err(Error::Override);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Helper method to write expressions\n    ///\n    /// # Notes\n    /// Doesn't add any newlines or leading/trailing spaces\n    pub(super) fn write_expr(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        use crate::Expression;\n\n        // Handle the special semantics of vertex_index/instance_index\n        let ff_input = if self.options.special_constants_binding.is_some() {\n            func_ctx.is_fixed_function_input(expr, module)\n        } else {\n            None\n        };\n        let closing_bracket = match ff_input {\n            Some(crate::BuiltIn::VertexIndex) => {\n                write!(self.out, \"({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX} + \")?;\n                \")\"\n            }\n            Some(crate::BuiltIn::InstanceIndex) => {\n                write!(self.out, \"({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE} + \",)?;\n                \")\"\n            }\n            Some(crate::BuiltIn::NumWorkGroups) => {\n                // Note: despite their names (`FIRST_VERTEX` and `FIRST_INSTANCE`),\n                // in compute shaders the special constants contain the number\n                // of workgroups, which we are using here.\n                write!(\n                    self.out,\n                    \"uint3({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX}, {SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE}, {SPECIAL_CBUF_VAR}.{SPECIAL_OTHER})\",\n                )?;\n                return Ok(());\n            }\n            _ => \"\",\n        };\n\n        if let Some(name) = self.named_expressions.get(&expr) {\n            write!(self.out, \"{name}{closing_bracket}\")?;\n            return Ok(());\n        }\n\n        let expression = &func_ctx.expressions[expr];\n\n        match *expression {\n            Expression::Literal(_)\n            | Expression::Constant(_)\n            | Expression::ZeroValue(_)\n            | Expression::Compose { .. }\n            | Expression::Splat { .. } => {\n                self.write_possibly_const_expression(\n                    module,\n                    expr,\n                    func_ctx.expressions,\n                    |writer, expr| writer.write_expr(module, expr, func_ctx),\n                )?;\n            }\n            Expression::Override(_) => return Err(Error::Override),\n            // Avoid undefined behaviour for addition, subtraction, and\n            // multiplication of signed integers by casting operands to\n            // unsigned, performing the operation, then casting the result back\n            // to signed.\n            // TODO(#7109): This relies on the asint()/asuint() functions which only work\n            // for 32-bit types, so we must find another solution for different bit widths.\n            Expression::Binary {\n                op:\n                    op @ crate::BinaryOperator::Add\n                    | op @ crate::BinaryOperator::Subtract\n                    | op @ crate::BinaryOperator::Multiply,\n                left,\n                right,\n            } if matches!(\n                func_ctx.resolve_type(expr, &module.types).scalar(),\n                Some(Scalar::I32)\n            ) =>\n            {\n                write!(self.out, \"asint(asuint(\",)?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \") {} asuint(\", back::binary_operation_str(op))?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \"))\")?;\n            }\n            // All of the multiplication can be expressed as `mul`,\n            // except vector * vector, which needs to use the \"*\" operator.\n            Expression::Binary {\n                op: crate::BinaryOperator::Multiply,\n                left,\n                right,\n            } if func_ctx.resolve_type(left, &module.types).is_matrix()\n                || func_ctx.resolve_type(right, &module.types).is_matrix() =>\n            {\n                // We intentionally flip the order of multiplication as our matrices are implicitly transposed.\n                write!(self.out, \"mul(\")?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n\n            // WGSL says that floating-point division by zero should return\n            // infinity. Microsoft's Direct3D 11 functional specification\n            // (https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm)\n            // says:\n            //\n            //     Divide by 0 produces +/- INF, except 0/0 which results in NaN.\n            //\n            // which is what we want. The DXIL specification for the FDiv\n            // instruction corroborates this:\n            //\n            // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#fdiv\n            Expression::Binary {\n                op: crate::BinaryOperator::Divide,\n                left,\n                right,\n            } if matches!(\n                func_ctx.resolve_type(expr, &module.types).scalar_kind(),\n                Some(ScalarKind::Sint | ScalarKind::Uint)\n            ) =>\n            {\n                write!(self.out, \"{DIV_FUNCTION}(\")?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n\n            Expression::Binary {\n                op: crate::BinaryOperator::Modulo,\n                left,\n                right,\n            } if matches!(\n                func_ctx.resolve_type(expr, &module.types).scalar_kind(),\n                Some(ScalarKind::Sint | ScalarKind::Uint | ScalarKind::Float)\n            ) =>\n            {\n                write!(self.out, \"{MOD_FUNCTION}(\")?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n\n            Expression::Binary { op, left, right } => {\n                write!(self.out, \"(\")?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \" {} \", back::binary_operation_str(op))?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::Access { base, index } => {\n                if let Some(crate::AddressSpace::Storage { .. }) =\n                    func_ctx.resolve_type(expr, &module.types).pointer_space()\n                {\n                    // do nothing, the chain is written on `Load`/`Store`\n                } else {\n                    // We use the function __get_col_of_matCx2 here in cases\n                    // where `base`s type resolves to a matCx2 and is part of a\n                    // struct member with type of (possibly nested) array of matCx2's.\n                    //\n                    // Note that this only works for `Load`s and we handle\n                    // `Store`s differently in `Statement::Store`.\n                    if let Some(MatrixType {\n                        columns,\n                        rows: crate::VectorSize::Bi,\n                        width: 4,\n                    }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)\n                        .or_else(|| get_global_uniform_matrix(module, base, func_ctx))\n                    {\n                        write!(self.out, \"__get_col_of_mat{}x2(\", columns as u8)?;\n                        self.write_expr(module, base, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, index, func_ctx)?;\n                        write!(self.out, \")\")?;\n                        return Ok(());\n                    }\n\n                    let resolved = func_ctx.resolve_type(base, &module.types);\n\n                    let (indexing_binding_array, non_uniform_qualifier) = match *resolved {\n                        TypeInner::BindingArray { .. } => {\n                            let uniformity = &func_ctx.info[index].uniformity;\n\n                            (true, uniformity.non_uniform_result.is_some())\n                        }\n                        _ => (false, false),\n                    };\n\n                    self.write_expr(module, base, func_ctx)?;\n\n                    let array_sampler_info = self.sampler_binding_array_info_from_expression(\n                        module, func_ctx, base, resolved,\n                    );\n\n                    if let Some(ref info) = array_sampler_info {\n                        write!(self.out, \"{}[\", info.sampler_heap_name)?;\n                    } else {\n                        write!(self.out, \"[\")?;\n                    }\n\n                    let needs_bound_check = self.options.restrict_indexing\n                        && !indexing_binding_array\n                        && match resolved.pointer_space() {\n                            Some(\n                                crate::AddressSpace::Function\n                                | crate::AddressSpace::Private\n                                | crate::AddressSpace::WorkGroup\n                                | crate::AddressSpace::Immediate\n                                | crate::AddressSpace::TaskPayload\n                                | crate::AddressSpace::RayPayload\n                                | crate::AddressSpace::IncomingRayPayload,\n                            )\n                            | None => true,\n                            Some(crate::AddressSpace::Uniform) => {\n                                // check if BindTarget.restrict_indexing is set, this is used for dynamic buffers\n                                let var_handle = self.fill_access_chain(module, base, func_ctx)?;\n                                let bind_target = self\n                                    .options\n                                    .resolve_resource_binding(\n                                        module.global_variables[var_handle]\n                                            .binding\n                                            .as_ref()\n                                            .unwrap(),\n                                    )\n                                    .unwrap();\n                                bind_target.restrict_indexing\n                            }\n                            Some(\n                                crate::AddressSpace::Handle | crate::AddressSpace::Storage { .. },\n                            ) => unreachable!(),\n                        };\n                    // Decide whether this index needs to be clamped to fall within range.\n                    let restriction_needed = if needs_bound_check {\n                        index::access_needs_check(\n                            base,\n                            index::GuardedIndex::Expression(index),\n                            module,\n                            func_ctx.expressions,\n                            func_ctx.info,\n                        )\n                    } else {\n                        None\n                    };\n                    if let Some(limit) = restriction_needed {\n                        write!(self.out, \"min(uint(\")?;\n                        self.write_expr(module, index, func_ctx)?;\n                        write!(self.out, \"), \")?;\n                        match limit {\n                            index::IndexableLength::Known(limit) => {\n                                write!(self.out, \"{}u\", limit - 1)?;\n                            }\n                            index::IndexableLength::Dynamic => unreachable!(),\n                        }\n                        write!(self.out, \")\")?;\n                    } else {\n                        if non_uniform_qualifier {\n                            write!(self.out, \"NonUniformResourceIndex(\")?;\n                        }\n                        if let Some(ref info) = array_sampler_info {\n                            write!(\n                                self.out,\n                                \"{}[{} + \",\n                                info.sampler_index_buffer_name, info.binding_array_base_index_name,\n                            )?;\n                        }\n                        self.write_expr(module, index, func_ctx)?;\n                        if array_sampler_info.is_some() {\n                            write!(self.out, \"]\")?;\n                        }\n                        if non_uniform_qualifier {\n                            write!(self.out, \")\")?;\n                        }\n                    }\n\n                    write!(self.out, \"]\")?;\n                }\n            }\n            Expression::AccessIndex { base, index } => {\n                if let Some(crate::AddressSpace::Storage { .. }) =\n                    func_ctx.resolve_type(expr, &module.types).pointer_space()\n                {\n                    // do nothing, the chain is written on `Load`/`Store`\n                } else {\n                    // See if we need to write the matrix column access in a\n                    // special way since the type of `base` is our special\n                    // __matCx2 struct.\n                    if let Some(MatrixType {\n                        rows: crate::VectorSize::Bi,\n                        width: 4,\n                        ..\n                    }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)\n                        .or_else(|| get_global_uniform_matrix(module, base, func_ctx))\n                    {\n                        self.write_expr(module, base, func_ctx)?;\n                        write!(self.out, \"._{index}\")?;\n                        return Ok(());\n                    }\n\n                    let base_ty_res = &func_ctx.info[base].ty;\n                    let mut resolved = base_ty_res.inner_with(&module.types);\n                    let base_ty_handle = match *resolved {\n                        TypeInner::Pointer { base, .. } => {\n                            resolved = &module.types[base].inner;\n                            Some(base)\n                        }\n                        _ => base_ty_res.handle(),\n                    };\n\n                    // We treat matrices of the form `matCx2` as a sequence of C `vec2`s.\n                    // See the module-level block comment in mod.rs for details.\n                    //\n                    // We handle matrix reconstruction here for Loads.\n                    // Stores are handled directly by `Statement::Store`.\n                    if let TypeInner::Struct { ref members, .. } = *resolved {\n                        let member = &members[index as usize];\n\n                        match module.types[member.ty].inner {\n                            TypeInner::Matrix {\n                                rows: crate::VectorSize::Bi,\n                                ..\n                            } if member.binding.is_none() => {\n                                let ty = base_ty_handle.unwrap();\n                                self.write_wrapped_struct_matrix_get_function_name(\n                                    WrappedStructMatrixAccess { ty, index },\n                                )?;\n                                write!(self.out, \"(\")?;\n                                self.write_expr(module, base, func_ctx)?;\n                                write!(self.out, \")\")?;\n                                return Ok(());\n                            }\n                            _ => {}\n                        }\n                    }\n\n                    let array_sampler_info = self.sampler_binding_array_info_from_expression(\n                        module, func_ctx, base, resolved,\n                    );\n\n                    if let Some(ref info) = array_sampler_info {\n                        write!(\n                            self.out,\n                            \"{}[{}\",\n                            info.sampler_heap_name, info.sampler_index_buffer_name\n                        )?;\n                    }\n\n                    self.write_expr(module, base, func_ctx)?;\n\n                    match *resolved {\n                        // We specifically lift the ValuePointer to this case. While `[0]` is valid\n                        // HLSL for any vector behind a value pointer, FXC completely miscompiles\n                        // it and generates completely nonsensical DXBC.\n                        //\n                        // See https://github.com/gfx-rs/naga/issues/2095 for more details.\n                        TypeInner::Vector { .. } | TypeInner::ValuePointer { .. } => {\n                            // Write vector access as a swizzle\n                            write!(self.out, \".{}\", back::COMPONENTS[index as usize])?\n                        }\n                        TypeInner::Matrix { .. }\n                        | TypeInner::Array { .. }\n                        | TypeInner::BindingArray { .. } => {\n                            if let Some(ref info) = array_sampler_info {\n                                write!(\n                                    self.out,\n                                    \"[{} + {index}]\",\n                                    info.binding_array_base_index_name\n                                )?;\n                            } else {\n                                write!(self.out, \"[{index}]\")?;\n                            }\n                        }\n                        TypeInner::Struct { .. } => {\n                            // This will never panic in case the type is a `Struct`, this is not true\n                            // for other types so we can only check while inside this match arm\n                            let ty = base_ty_handle.unwrap();\n\n                            write!(\n                                self.out,\n                                \".{}\",\n                                &self.names[&NameKey::StructMember(ty, index)]\n                            )?\n                        }\n                        ref other => return Err(Error::Custom(format!(\"Cannot index {other:?}\"))),\n                    }\n\n                    if array_sampler_info.is_some() {\n                        write!(self.out, \"]\")?;\n                    }\n                }\n            }\n            Expression::FunctionArgument(pos) => {\n                let ty = func_ctx.resolve_type(expr, &module.types);\n\n                // We know that any external texture function argument has been expanded into\n                // separate consecutive arguments for each plane and the parameters buffer. And we\n                // also know that external textures can only ever be used as an argument to another\n                // function. Therefore we can simply emit each of the expanded arguments in a\n                // consecutive comma-separated list.\n                if let TypeInner::Image {\n                    class: crate::ImageClass::External,\n                    ..\n                } = *ty\n                {\n                    let plane_names = [0, 1, 2].map(|i| {\n                        &self.names[&func_ctx\n                            .external_texture_argument_key(pos, ExternalTextureNameKey::Plane(i))]\n                    });\n                    let params_name = &self.names[&func_ctx\n                        .external_texture_argument_key(pos, ExternalTextureNameKey::Params)];\n                    write!(\n                        self.out,\n                        \"{}, {}, {}, {}\",\n                        plane_names[0], plane_names[1], plane_names[2], params_name\n                    )?;\n                } else {\n                    let key = func_ctx.argument_key(pos);\n                    let name = &self.names[&key];\n                    write!(self.out, \"{name}\")?;\n                }\n            }\n            Expression::ImageSample {\n                coordinate,\n                image,\n                sampler,\n                clamp_to_edge: true,\n                gather: None,\n                array_index: None,\n                offset: None,\n                level: crate::SampleLevel::Zero,\n                depth_ref: None,\n            } => {\n                write!(self.out, \"{IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, sampler, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::ImageSample {\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n            } => {\n                if clamp_to_edge {\n                    return Err(Error::Custom(\n                        \"ImageSample::clamp_to_edge should have been validated out\".to_string(),\n                    ));\n                }\n\n                use crate::SampleLevel as Sl;\n                const COMPONENTS: [&str; 4] = [\"\", \"Green\", \"Blue\", \"Alpha\"];\n\n                let (base_str, component_str) = match gather {\n                    Some(component) => (\"Gather\", COMPONENTS[component as usize]),\n                    None => (\"Sample\", \"\"),\n                };\n                let cmp_str = match depth_ref {\n                    Some(_) => \"Cmp\",\n                    None => \"\",\n                };\n                let level_str = match level {\n                    Sl::Zero if gather.is_none() => \"LevelZero\",\n                    Sl::Auto | Sl::Zero => \"\",\n                    Sl::Exact(_) => \"Level\",\n                    Sl::Bias(_) => \"Bias\",\n                    Sl::Gradient { .. } => \"Grad\",\n                };\n\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \".{base_str}{cmp_str}{component_str}{level_str}(\")?;\n                self.write_expr(module, sampler, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_texture_coordinates(\n                    \"float\",\n                    coordinate,\n                    array_index,\n                    None,\n                    module,\n                    func_ctx,\n                )?;\n\n                if let Some(depth_ref) = depth_ref {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, depth_ref, func_ctx)?;\n                }\n\n                match level {\n                    Sl::Auto | Sl::Zero => {}\n                    Sl::Exact(expr) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, expr, func_ctx)?;\n                    }\n                    Sl::Bias(expr) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, expr, func_ctx)?;\n                    }\n                    Sl::Gradient { x, y } => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, x, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, y, func_ctx)?;\n                    }\n                }\n\n                if let Some(offset) = offset {\n                    write!(self.out, \", \")?;\n                    write!(self.out, \"int2(\")?; // work around https://github.com/microsoft/DirectXShaderCompiler/issues/5082#issuecomment-1540147807\n                    self.write_const_expression(module, offset, func_ctx.expressions)?;\n                    write!(self.out, \")\")?;\n                }\n\n                write!(self.out, \")\")?;\n            }\n            Expression::ImageQuery { image, query } => {\n                // use wrapped image query function\n                if let TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                } = *func_ctx.resolve_type(image, &module.types)\n                {\n                    let wrapped_image_query = WrappedImageQuery {\n                        dim,\n                        arrayed,\n                        class,\n                        query: query.into(),\n                    };\n\n                    self.write_wrapped_image_query_function_name(wrapped_image_query)?;\n                    write!(self.out, \"(\")?;\n                    // Image always first param\n                    self.write_expr(module, image, func_ctx)?;\n                    if let crate::ImageQuery::Size { level: Some(level) } = query {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, level, func_ctx)?;\n                    }\n                    write!(self.out, \")\")?;\n                }\n            }\n            Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => self.write_image_load(\n                &module,\n                expr,\n                func_ctx,\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            )?,\n            Expression::GlobalVariable(handle) => {\n                let global_variable = &module.global_variables[handle];\n                let ty = &module.types[global_variable.ty].inner;\n\n                // In the case of binding arrays of samplers, we need to not write anything\n                // as the we are in the wrong position to fully write the expression.\n                //\n                // The entire writing is done by AccessIndex.\n                let is_binding_array_of_samplers = match *ty {\n                    TypeInner::BindingArray { base, .. } => {\n                        let base_ty = &module.types[base].inner;\n                        matches!(*base_ty, TypeInner::Sampler { .. })\n                    }\n                    _ => false,\n                };\n\n                let is_storage_space =\n                    matches!(global_variable.space, crate::AddressSpace::Storage { .. });\n\n                // Our external texture global variable has been expanded into multiple\n                // global variables, one for each plane and the parameters buffer.\n                // External textures can only ever be used as arguments to a function\n                // call, and we know that an external texture argument to any function\n                // will have been expanded to separate consecutive arguments for each\n                // plane and the parameters buffer. Therefore we can simply emit each of\n                // the expanded global variables in a consecutive comma-separated list.\n                if let TypeInner::Image {\n                    class: crate::ImageClass::External,\n                    ..\n                } = *ty\n                {\n                    let plane_names = [0, 1, 2].map(|i| {\n                        &self.names[&NameKey::ExternalTextureGlobalVariable(\n                            handle,\n                            ExternalTextureNameKey::Plane(i),\n                        )]\n                    });\n                    let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable(\n                        handle,\n                        ExternalTextureNameKey::Params,\n                    )];\n                    write!(\n                        self.out,\n                        \"{}, {}, {}, {}\",\n                        plane_names[0], plane_names[1], plane_names[2], params_name\n                    )?;\n                } else if !is_binding_array_of_samplers && !is_storage_space {\n                    let name = &self.names[&NameKey::GlobalVariable(handle)];\n                    write!(self.out, \"{name}\")?;\n                }\n            }\n            Expression::LocalVariable(handle) => {\n                write!(self.out, \"{}\", self.names[&func_ctx.name_key(handle)])?\n            }\n            Expression::Load { pointer } => {\n                match func_ctx\n                    .resolve_type(pointer, &module.types)\n                    .pointer_space()\n                {\n                    Some(crate::AddressSpace::Storage { .. }) => {\n                        let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;\n                        let result_ty = func_ctx.info[expr].ty.clone();\n                        self.write_storage_load(module, var_handle, result_ty, func_ctx)?;\n                    }\n                    _ => {\n                        let mut close_paren = false;\n\n                        // We cast the value loaded to a native HLSL floatCx2\n                        // in cases where it is of type:\n                        //  - __matCx2 or\n                        //  - a (possibly nested) array of __matCx2's\n                        if let Some(MatrixType {\n                            rows: crate::VectorSize::Bi,\n                            width: 4,\n                            ..\n                        }) = get_inner_matrix_of_struct_array_member(\n                            module, pointer, func_ctx, false,\n                        )\n                        .or_else(|| get_inner_matrix_of_global_uniform(module, pointer, func_ctx))\n                        {\n                            let mut resolved = func_ctx.resolve_type(pointer, &module.types);\n                            let ptr_tr = resolved.pointer_base_type();\n                            if let Some(ptr_ty) =\n                                ptr_tr.as_ref().map(|tr| tr.inner_with(&module.types))\n                            {\n                                resolved = ptr_ty;\n                            }\n\n                            write!(self.out, \"((\")?;\n                            if let TypeInner::Array { base, size, .. } = *resolved {\n                                self.write_type(module, base)?;\n                                self.write_array_size(module, base, size)?;\n                            } else {\n                                self.write_value_type(module, resolved)?;\n                            }\n                            write!(self.out, \")\")?;\n                            close_paren = true;\n                        }\n\n                        self.write_expr(module, pointer, func_ctx)?;\n\n                        if close_paren {\n                            write!(self.out, \")\")?;\n                        }\n                    }\n                }\n            }\n            Expression::Unary { op, expr } => {\n                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-operators#unary-operators\n                let op_str = match op {\n                    crate::UnaryOperator::Negate => {\n                        match func_ctx.resolve_type(expr, &module.types).scalar() {\n                            Some(Scalar::I32) => NEG_FUNCTION,\n                            _ => \"-\",\n                        }\n                    }\n                    crate::UnaryOperator::LogicalNot => \"!\",\n                    crate::UnaryOperator::BitwiseNot => \"~\",\n                };\n                write!(self.out, \"{op_str}(\")?;\n                self.write_expr(module, expr, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::As {\n                expr,\n                kind,\n                convert,\n            } => {\n                let inner = func_ctx.resolve_type(expr, &module.types);\n                if inner.scalar_kind() == Some(ScalarKind::Float)\n                    && (kind == ScalarKind::Sint || kind == ScalarKind::Uint)\n                    && convert.is_some()\n                {\n                    // Use helper functions for float to int casts in order to\n                    // avoid undefined behaviour when value is out of range for\n                    // the target type.\n                    let fun_name = match (kind, convert) {\n                        (ScalarKind::Sint, Some(4)) => F2I32_FUNCTION,\n                        (ScalarKind::Uint, Some(4)) => F2U32_FUNCTION,\n                        (ScalarKind::Sint, Some(8)) => F2I64_FUNCTION,\n                        (ScalarKind::Uint, Some(8)) => F2U64_FUNCTION,\n                        _ => unreachable!(),\n                    };\n                    write!(self.out, \"{fun_name}(\")?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    write!(self.out, \")\")?;\n                } else {\n                    let close_paren = match convert {\n                        Some(dst_width) => {\n                            let scalar = Scalar {\n                                kind,\n                                width: dst_width,\n                            };\n                            match *inner {\n                                TypeInner::Vector { size, .. } => {\n                                    write!(\n                                        self.out,\n                                        \"{}{}(\",\n                                        scalar.to_hlsl_str()?,\n                                        common::vector_size_str(size)\n                                    )?;\n                                }\n                                TypeInner::Scalar(_) => {\n                                    write!(self.out, \"{}(\", scalar.to_hlsl_str()?,)?;\n                                }\n                                TypeInner::Matrix { columns, rows, .. } => {\n                                    write!(\n                                        self.out,\n                                        \"{}{}x{}(\",\n                                        scalar.to_hlsl_str()?,\n                                        common::vector_size_str(columns),\n                                        common::vector_size_str(rows)\n                                    )?;\n                                }\n                                _ => {\n                                    return Err(Error::Unimplemented(format!(\n                                        \"write_expr expression::as {inner:?}\"\n                                    )));\n                                }\n                            };\n                            true\n                        }\n                        None => {\n                            if inner.scalar_width() == Some(8) {\n                                false\n                            } else {\n                                write!(self.out, \"{}(\", kind.to_hlsl_cast(),)?;\n                                true\n                            }\n                        }\n                    };\n                    self.write_expr(module, expr, func_ctx)?;\n                    if close_paren {\n                        write!(self.out, \")\")?;\n                    }\n                }\n            }\n            Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                use crate::MathFunction as Mf;\n\n                enum Function {\n                    Asincosh { is_sin: bool },\n                    Atanh,\n                    Pack2x16float,\n                    Pack2x16snorm,\n                    Pack2x16unorm,\n                    Pack4x8snorm,\n                    Pack4x8unorm,\n                    Pack4xI8,\n                    Pack4xU8,\n                    Pack4xI8Clamp,\n                    Pack4xU8Clamp,\n                    Unpack2x16float,\n                    Unpack2x16snorm,\n                    Unpack2x16unorm,\n                    Unpack4x8snorm,\n                    Unpack4x8unorm,\n                    Unpack4xI8,\n                    Unpack4xU8,\n                    Dot4I8Packed,\n                    Dot4U8Packed,\n                    QuantizeToF16,\n                    Regular(&'static str),\n                    MissingIntOverload(&'static str),\n                    MissingIntReturnType(&'static str),\n                    CountTrailingZeros,\n                    CountLeadingZeros,\n                }\n\n                let fun = match fun {\n                    // comparison\n                    Mf::Abs => match func_ctx.resolve_type(arg, &module.types).scalar() {\n                        Some(Scalar::I32) => Function::Regular(ABS_FUNCTION),\n                        _ => Function::Regular(\"abs\"),\n                    },\n                    Mf::Min => Function::Regular(\"min\"),\n                    Mf::Max => Function::Regular(\"max\"),\n                    Mf::Clamp => Function::Regular(\"clamp\"),\n                    Mf::Saturate => Function::Regular(\"saturate\"),\n                    // trigonometry\n                    Mf::Cos => Function::Regular(\"cos\"),\n                    Mf::Cosh => Function::Regular(\"cosh\"),\n                    Mf::Sin => Function::Regular(\"sin\"),\n                    Mf::Sinh => Function::Regular(\"sinh\"),\n                    Mf::Tan => Function::Regular(\"tan\"),\n                    Mf::Tanh => Function::Regular(\"tanh\"),\n                    Mf::Acos => Function::Regular(\"acos\"),\n                    Mf::Asin => Function::Regular(\"asin\"),\n                    Mf::Atan => Function::Regular(\"atan\"),\n                    Mf::Atan2 => Function::Regular(\"atan2\"),\n                    Mf::Asinh => Function::Asincosh { is_sin: true },\n                    Mf::Acosh => Function::Asincosh { is_sin: false },\n                    Mf::Atanh => Function::Atanh,\n                    Mf::Radians => Function::Regular(\"radians\"),\n                    Mf::Degrees => Function::Regular(\"degrees\"),\n                    // decomposition\n                    Mf::Ceil => Function::Regular(\"ceil\"),\n                    Mf::Floor => Function::Regular(\"floor\"),\n                    Mf::Round => Function::Regular(\"round\"),\n                    Mf::Fract => Function::Regular(\"frac\"),\n                    Mf::Trunc => Function::Regular(\"trunc\"),\n                    Mf::Modf => Function::Regular(MODF_FUNCTION),\n                    Mf::Frexp => Function::Regular(FREXP_FUNCTION),\n                    Mf::Ldexp => Function::Regular(\"ldexp\"),\n                    // exponent\n                    Mf::Exp => Function::Regular(\"exp\"),\n                    Mf::Exp2 => Function::Regular(\"exp2\"),\n                    Mf::Log => Function::Regular(\"log\"),\n                    Mf::Log2 => Function::Regular(\"log2\"),\n                    Mf::Pow => Function::Regular(\"pow\"),\n                    // geometry\n                    Mf::Dot => Function::Regular(\"dot\"),\n                    Mf::Dot4I8Packed => Function::Dot4I8Packed,\n                    Mf::Dot4U8Packed => Function::Dot4U8Packed,\n                    //Mf::Outer => ,\n                    Mf::Cross => Function::Regular(\"cross\"),\n                    Mf::Distance => Function::Regular(\"distance\"),\n                    Mf::Length => Function::Regular(\"length\"),\n                    Mf::Normalize => Function::Regular(\"normalize\"),\n                    Mf::FaceForward => Function::Regular(\"faceforward\"),\n                    Mf::Reflect => Function::Regular(\"reflect\"),\n                    Mf::Refract => Function::Regular(\"refract\"),\n                    // computational\n                    Mf::Sign => Function::Regular(\"sign\"),\n                    Mf::Fma => Function::Regular(\"mad\"),\n                    Mf::Mix => Function::Regular(\"lerp\"),\n                    Mf::Step => Function::Regular(\"step\"),\n                    Mf::SmoothStep => Function::Regular(\"smoothstep\"),\n                    Mf::Sqrt => Function::Regular(\"sqrt\"),\n                    Mf::InverseSqrt => Function::Regular(\"rsqrt\"),\n                    //Mf::Inverse =>,\n                    Mf::Transpose => Function::Regular(\"transpose\"),\n                    Mf::Determinant => Function::Regular(\"determinant\"),\n                    Mf::QuantizeToF16 => Function::QuantizeToF16,\n                    // bits\n                    Mf::CountTrailingZeros => Function::CountTrailingZeros,\n                    Mf::CountLeadingZeros => Function::CountLeadingZeros,\n                    Mf::CountOneBits => Function::MissingIntOverload(\"countbits\"),\n                    Mf::ReverseBits => Function::MissingIntOverload(\"reversebits\"),\n                    Mf::FirstTrailingBit => Function::MissingIntReturnType(\"firstbitlow\"),\n                    Mf::FirstLeadingBit => Function::MissingIntReturnType(\"firstbithigh\"),\n                    Mf::ExtractBits => Function::Regular(EXTRACT_BITS_FUNCTION),\n                    Mf::InsertBits => Function::Regular(INSERT_BITS_FUNCTION),\n                    // Data Packing\n                    Mf::Pack2x16float => Function::Pack2x16float,\n                    Mf::Pack2x16snorm => Function::Pack2x16snorm,\n                    Mf::Pack2x16unorm => Function::Pack2x16unorm,\n                    Mf::Pack4x8snorm => Function::Pack4x8snorm,\n                    Mf::Pack4x8unorm => Function::Pack4x8unorm,\n                    Mf::Pack4xI8 => Function::Pack4xI8,\n                    Mf::Pack4xU8 => Function::Pack4xU8,\n                    Mf::Pack4xI8Clamp => Function::Pack4xI8Clamp,\n                    Mf::Pack4xU8Clamp => Function::Pack4xU8Clamp,\n                    // Data Unpacking\n                    Mf::Unpack2x16float => Function::Unpack2x16float,\n                    Mf::Unpack2x16snorm => Function::Unpack2x16snorm,\n                    Mf::Unpack2x16unorm => Function::Unpack2x16unorm,\n                    Mf::Unpack4x8snorm => Function::Unpack4x8snorm,\n                    Mf::Unpack4x8unorm => Function::Unpack4x8unorm,\n                    Mf::Unpack4xI8 => Function::Unpack4xI8,\n                    Mf::Unpack4xU8 => Function::Unpack4xU8,\n                    _ => return Err(Error::Unimplemented(format!(\"write_expr_math {fun:?}\"))),\n                };\n\n                match fun {\n                    Function::Asincosh { is_sin } => {\n                        write!(self.out, \"log(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" + sqrt(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" * \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        match is_sin {\n                            true => write!(self.out, \" + 1.0))\")?,\n                            false => write!(self.out, \" - 1.0))\")?,\n                        }\n                    }\n                    Function::Atanh => {\n                        write!(self.out, \"0.5 * log((1.0 + \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \") / (1.0 - \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"))\")?;\n                    }\n                    Function::Pack2x16float => {\n                        write!(self.out, \"(f32tof16(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[0]) | f32tof16(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[1]) << 16)\")?;\n                    }\n                    Function::Pack2x16snorm => {\n                        let scale = 32767;\n\n                        write!(self.out, \"uint((int(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[0], -1.0, 1.0) * {scale}.0)) & 0xFFFF) | ((int(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[1], -1.0, 1.0) * {scale}.0)) & 0xFFFF) << 16))\",)?;\n                    }\n                    Function::Pack2x16unorm => {\n                        let scale = 65535;\n\n                        write!(self.out, \"(uint(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[1], 0.0, 1.0) * {scale}.0)) << 16)\")?;\n                    }\n                    Function::Pack4x8snorm => {\n                        let scale = 127;\n\n                        write!(self.out, \"uint((int(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[0], -1.0, 1.0) * {scale}.0)) & 0xFF) | ((int(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[1], -1.0, 1.0) * {scale}.0)) & 0xFF) << 8) | ((int(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[2], -1.0, 1.0) * {scale}.0)) & 0xFF) << 16) | ((int(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[3], -1.0, 1.0) * {scale}.0)) & 0xFF) << 24))\",)?;\n                    }\n                    Function::Pack4x8unorm => {\n                        let scale = 255;\n\n                        write!(self.out, \"(uint(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[1], 0.0, 1.0) * {scale}.0)) << 8 | uint(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(\n                            self.out,\n                            \"[2], 0.0, 1.0) * {scale}.0)) << 16 | uint(round(clamp(\"\n                        )?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"[3], 0.0, 1.0) * {scale}.0)) << 24)\")?;\n                    }\n                    fun @ (Function::Pack4xI8\n                    | Function::Pack4xU8\n                    | Function::Pack4xI8Clamp\n                    | Function::Pack4xU8Clamp) => {\n                        let was_signed =\n                            matches!(fun, Function::Pack4xI8 | Function::Pack4xI8Clamp);\n                        let clamp_bounds = match fun {\n                            Function::Pack4xI8Clamp => Some((\"-128\", \"127\")),\n                            Function::Pack4xU8Clamp => Some((\"0\", \"255\")),\n                            _ => None,\n                        };\n                        if was_signed {\n                            write!(self.out, \"uint(\")?;\n                        }\n                        let write_arg = |this: &mut Self| -> BackendResult {\n                            if let Some((min, max)) = clamp_bounds {\n                                write!(this.out, \"clamp(\")?;\n                                this.write_expr(module, arg, func_ctx)?;\n                                write!(this.out, \", {min}, {max})\")?;\n                            } else {\n                                this.write_expr(module, arg, func_ctx)?;\n                            }\n                            Ok(())\n                        };\n                        write!(self.out, \"(\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[0] & 0xFF) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[1] & 0xFF) << 8) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[2] & 0xFF) << 16) | ((\")?;\n                        write_arg(self)?;\n                        write!(self.out, \"[3] & 0xFF) << 24)\")?;\n                        if was_signed {\n                            write!(self.out, \")\")?;\n                        }\n                    }\n\n                    Function::Unpack2x16float => {\n                        write!(self.out, \"float2(f16tof32(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"), f16tof32((\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \") >> 16))\")?;\n                    }\n                    Function::Unpack2x16snorm => {\n                        let scale = 32767;\n\n                        write!(self.out, \"(float2(int2(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" << 16, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \") >> 16) / {scale}.0)\")?;\n                    }\n                    Function::Unpack2x16unorm => {\n                        let scale = 65535;\n\n                        write!(self.out, \"(float2(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" & 0xFFFF, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 16) / {scale}.0)\")?;\n                    }\n                    Function::Unpack4x8snorm => {\n                        let scale = 127;\n\n                        write!(self.out, \"(float4(int4(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" << 24, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" << 16, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" << 8, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \") >> 24) / {scale}.0)\")?;\n                    }\n                    Function::Unpack4x8unorm => {\n                        let scale = 255;\n\n                        write!(self.out, \"(float4(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" & 0xFF, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 8 & 0xFF, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 16 & 0xFF, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 24) / {scale}.0)\")?;\n                    }\n                    fun @ (Function::Unpack4xI8 | Function::Unpack4xU8) => {\n                        write!(self.out, \"(\")?;\n                        if matches!(fun, Function::Unpack4xU8) {\n                            write!(self.out, \"u\")?;\n                        }\n                        write!(self.out, \"int4(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 8, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 16, \")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \" >> 24) << 24 >> 24)\")?;\n                    }\n                    fun @ (Function::Dot4I8Packed | Function::Dot4U8Packed) => {\n                        let arg1 = arg1.unwrap();\n\n                        if self.options.shader_model >= ShaderModel::V6_4 {\n                            // Intrinsics `dot4add_{i, u}8packed` are available in SM 6.4 and later.\n                            let function_name = match fun {\n                                Function::Dot4I8Packed => \"dot4add_i8packed\",\n                                Function::Dot4U8Packed => \"dot4add_u8packed\",\n                                _ => unreachable!(),\n                            };\n                            write!(self.out, \"{function_name}(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg1, func_ctx)?;\n                            write!(self.out, \", 0)\")?;\n                        } else {\n                            // Fall back to a polyfill as `dot4add_u8packed` is not available.\n                            write!(self.out, \"dot(\")?;\n\n                            if matches!(fun, Function::Dot4U8Packed) {\n                                write!(self.out, \"u\")?;\n                            }\n                            write!(self.out, \"int4(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \" >> 8, \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \" >> 16, \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \" >> 24) << 24 >> 24, \")?;\n\n                            if matches!(fun, Function::Dot4U8Packed) {\n                                write!(self.out, \"u\")?;\n                            }\n                            write!(self.out, \"int4(\")?;\n                            self.write_expr(module, arg1, func_ctx)?;\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg1, func_ctx)?;\n                            write!(self.out, \" >> 8, \")?;\n                            self.write_expr(module, arg1, func_ctx)?;\n                            write!(self.out, \" >> 16, \")?;\n                            self.write_expr(module, arg1, func_ctx)?;\n                            write!(self.out, \" >> 24) << 24 >> 24)\")?;\n                        }\n                    }\n                    Function::QuantizeToF16 => {\n                        write!(self.out, \"f16tof32(f32tof16(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \"))\")?;\n                    }\n                    Function::Regular(fun_name) => {\n                        write!(self.out, \"{fun_name}(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        if let Some(arg) = arg1 {\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                        }\n                        if let Some(arg) = arg2 {\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                        }\n                        if let Some(arg) = arg3 {\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                        }\n                        write!(self.out, \")\")?\n                    }\n                    // These overloads are only missing on FXC, so this is only needed for 32bit types,\n                    // as non-32bit types are DXC only.\n                    Function::MissingIntOverload(fun_name) => {\n                        let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();\n                        if let Some(Scalar::I32) = scalar_kind {\n                            write!(self.out, \"asint({fun_name}(asuint(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \")))\")?;\n                        } else {\n                            write!(self.out, \"{fun_name}(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \")\")?;\n                        }\n                    }\n                    // These overloads are only missing on FXC, so this is only needed for 32bit types,\n                    // as non-32bit types are DXC only.\n                    Function::MissingIntReturnType(fun_name) => {\n                        let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();\n                        if let Some(Scalar::I32) = scalar_kind {\n                            write!(self.out, \"asint({fun_name}(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \"))\")?;\n                        } else {\n                            write!(self.out, \"{fun_name}(\")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                            write!(self.out, \")\")?;\n                        }\n                    }\n                    Function::CountTrailingZeros => {\n                        match *func_ctx.resolve_type(arg, &module.types) {\n                            TypeInner::Vector { size, scalar } => {\n                                let s = match size {\n                                    crate::VectorSize::Bi => \".xx\",\n                                    crate::VectorSize::Tri => \".xxx\",\n                                    crate::VectorSize::Quad => \".xxxx\",\n                                };\n\n                                let scalar_width_bits = scalar.width * 8;\n\n                                if scalar.kind == ScalarKind::Uint || scalar.width != 4 {\n                                    write!(\n                                        self.out,\n                                        \"min(({scalar_width_bits}u){s}, firstbitlow(\"\n                                    )?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \"))\")?;\n                                } else {\n                                    // This is only needed for the FXC path, on 32bit signed integers.\n                                    write!(\n                                        self.out,\n                                        \"asint(min(({scalar_width_bits}u){s}, firstbitlow(\"\n                                    )?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \")))\")?;\n                                }\n                            }\n                            TypeInner::Scalar(scalar) => {\n                                let scalar_width_bits = scalar.width * 8;\n\n                                if scalar.kind == ScalarKind::Uint || scalar.width != 4 {\n                                    write!(self.out, \"min({scalar_width_bits}u, firstbitlow(\")?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \"))\")?;\n                                } else {\n                                    // This is only needed for the FXC path, on 32bit signed integers.\n                                    write!(\n                                        self.out,\n                                        \"asint(min({scalar_width_bits}u, firstbitlow(\"\n                                    )?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \")))\")?;\n                                }\n                            }\n                            _ => unreachable!(),\n                        }\n\n                        return Ok(());\n                    }\n                    Function::CountLeadingZeros => {\n                        match *func_ctx.resolve_type(arg, &module.types) {\n                            TypeInner::Vector { size, scalar } => {\n                                let s = match size {\n                                    crate::VectorSize::Bi => \".xx\",\n                                    crate::VectorSize::Tri => \".xxx\",\n                                    crate::VectorSize::Quad => \".xxxx\",\n                                };\n\n                                // scalar width - 1\n                                let constant = scalar.width * 8 - 1;\n\n                                if scalar.kind == ScalarKind::Uint {\n                                    write!(self.out, \"(({constant}u){s} - firstbithigh(\")?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \"))\")?;\n                                } else {\n                                    let conversion_func = match scalar.width {\n                                        4 => \"asint\",\n                                        _ => \"\",\n                                    };\n                                    write!(self.out, \"(\")?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(\n                                        self.out,\n                                        \" < (0){s} ? (0){s} : ({constant}){s} - {conversion_func}(firstbithigh(\"\n                                    )?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \")))\")?;\n                                }\n                            }\n                            TypeInner::Scalar(scalar) => {\n                                // scalar width - 1\n                                let constant = scalar.width * 8 - 1;\n\n                                if let ScalarKind::Uint = scalar.kind {\n                                    write!(self.out, \"({constant}u - firstbithigh(\")?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \"))\")?;\n                                } else {\n                                    let conversion_func = match scalar.width {\n                                        4 => \"asint\",\n                                        _ => \"\",\n                                    };\n                                    write!(self.out, \"(\")?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(\n                                        self.out,\n                                        \" < 0 ? 0 : {constant} - {conversion_func}(firstbithigh(\"\n                                    )?;\n                                    self.write_expr(module, arg, func_ctx)?;\n                                    write!(self.out, \")))\")?;\n                                }\n                            }\n                            _ => unreachable!(),\n                        }\n\n                        return Ok(());\n                    }\n                }\n            }\n            Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                self.write_expr(module, vector, func_ctx)?;\n                write!(self.out, \".\")?;\n                for &sc in pattern[..size as usize].iter() {\n                    self.out.write_char(back::COMPONENTS[sc as usize])?;\n                }\n            }\n            Expression::ArrayLength(expr) => {\n                let var_handle = match func_ctx.expressions[expr] {\n                    Expression::AccessIndex { base, index: _ } => {\n                        match func_ctx.expressions[base] {\n                            Expression::GlobalVariable(handle) => handle,\n                            _ => unreachable!(),\n                        }\n                    }\n                    Expression::GlobalVariable(handle) => handle,\n                    _ => unreachable!(),\n                };\n\n                let var = &module.global_variables[var_handle];\n                let (offset, stride) = match module.types[var.ty].inner {\n                    TypeInner::Array { stride, .. } => (0, stride),\n                    TypeInner::Struct { ref members, .. } => {\n                        let last = members.last().unwrap();\n                        let stride = match module.types[last.ty].inner {\n                            TypeInner::Array { stride, .. } => stride,\n                            _ => unreachable!(),\n                        };\n                        (last.offset, stride)\n                    }\n                    _ => unreachable!(),\n                };\n\n                let storage_access = match var.space {\n                    crate::AddressSpace::Storage { access } => access,\n                    _ => crate::StorageAccess::default(),\n                };\n                let wrapped_array_length = WrappedArrayLength {\n                    writable: storage_access.contains(crate::StorageAccess::STORE),\n                };\n\n                write!(self.out, \"((\")?;\n                self.write_wrapped_array_length_function_name(wrapped_array_length)?;\n                let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];\n                write!(self.out, \"({var_name}) - {offset}) / {stride})\")?\n            }\n            Expression::Derivative { axis, ctrl, expr } => {\n                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n                if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {\n                    let tail = match ctrl {\n                        Ctrl::Coarse => \"coarse\",\n                        Ctrl::Fine => \"fine\",\n                        Ctrl::None => unreachable!(),\n                    };\n                    write!(self.out, \"abs(ddx_{tail}(\")?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    write!(self.out, \")) + abs(ddy_{tail}(\")?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    write!(self.out, \"))\")?\n                } else {\n                    let fun_str = match (axis, ctrl) {\n                        (Axis::X, Ctrl::Coarse) => \"ddx_coarse\",\n                        (Axis::X, Ctrl::Fine) => \"ddx_fine\",\n                        (Axis::X, Ctrl::None) => \"ddx\",\n                        (Axis::Y, Ctrl::Coarse) => \"ddy_coarse\",\n                        (Axis::Y, Ctrl::Fine) => \"ddy_fine\",\n                        (Axis::Y, Ctrl::None) => \"ddy\",\n                        (Axis::Width, Ctrl::Coarse | Ctrl::Fine) => unreachable!(),\n                        (Axis::Width, Ctrl::None) => \"fwidth\",\n                    };\n                    write!(self.out, \"{fun_str}(\")?;\n                    self.write_expr(module, expr, func_ctx)?;\n                    write!(self.out, \")\")?\n                }\n            }\n            Expression::Relational { fun, argument } => {\n                use crate::RelationalFunction as Rf;\n\n                let fun_str = match fun {\n                    Rf::All => \"all\",\n                    Rf::Any => \"any\",\n                    Rf::IsNan => \"isnan\",\n                    Rf::IsInf => \"isinf\",\n                };\n                write!(self.out, \"{fun_str}(\")?;\n                self.write_expr(module, argument, func_ctx)?;\n                write!(self.out, \")\")?\n            }\n            Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                write!(self.out, \"(\")?;\n                self.write_expr(module, condition, func_ctx)?;\n                write!(self.out, \" ? \")?;\n                self.write_expr(module, accept, func_ctx)?;\n                write!(self.out, \" : \")?;\n                self.write_expr(module, reject, func_ctx)?;\n                write!(self.out, \")\")?\n            }\n            Expression::RayQueryGetIntersection { query, committed } => {\n                // For reasoning, see write_stmt\n                let Expression::LocalVariable(query_var) = func_ctx.expressions[query] else {\n                    unreachable!()\n                };\n\n                let tracker_expr_name = format!(\n                    \"{RAY_QUERY_TRACKER_VARIABLE_PREFIX}{}\",\n                    self.names[&func_ctx.name_key(query_var)]\n                );\n\n                if committed {\n                    write!(self.out, \"GetCommittedIntersection(\")?;\n                    self.write_expr(module, query, func_ctx)?;\n                    write!(self.out, \", {tracker_expr_name})\")?;\n                } else {\n                    write!(self.out, \"GetCandidateIntersection(\")?;\n                    self.write_expr(module, query, func_ctx)?;\n                    write!(self.out, \", {tracker_expr_name})\")?;\n                }\n            }\n            // Not supported yet\n            Expression::RayQueryVertexPositions { .. }\n            | Expression::CooperativeLoad { .. }\n            | Expression::CooperativeMultiplyAdd { .. } => {\n                unreachable!()\n            }\n            // Nothing to do here, since call expression already cached\n            Expression::CallResult(_)\n            | Expression::AtomicResult { .. }\n            | Expression::WorkGroupUniformLoadResult { .. }\n            | Expression::RayQueryProceedResult\n            | Expression::SubgroupBallotResult\n            | Expression::SubgroupOperationResult { .. } => {}\n        }\n\n        if !closing_bracket.is_empty() {\n            write!(self.out, \"{closing_bracket}\")?;\n        }\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn write_image_load(\n        &mut self,\n        module: &&Module,\n        expr: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        sample: Option<Handle<crate::Expression>>,\n        level: Option<Handle<crate::Expression>>,\n    ) -> Result<(), Error> {\n        let mut wrapping_type = None;\n        match *func_ctx.resolve_type(image, &module.types) {\n            TypeInner::Image {\n                class: crate::ImageClass::External,\n                ..\n            } => {\n                write!(self.out, \"{IMAGE_LOAD_EXTERNAL_FUNCTION}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n                write!(self.out, \")\")?;\n                return Ok(());\n            }\n            TypeInner::Image {\n                class: crate::ImageClass::Storage { format, .. },\n                ..\n            } => {\n                if format.single_component() {\n                    wrapping_type = Some(Scalar::from(format));\n                }\n            }\n            _ => {}\n        }\n        if let Some(scalar) = wrapping_type {\n            write!(\n                self.out,\n                \"{}{}(\",\n                help::IMAGE_STORAGE_LOAD_SCALAR_WRAPPER,\n                scalar.to_hlsl_str()?\n            )?;\n        }\n        // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-load\n        self.write_expr(module, image, func_ctx)?;\n        write!(self.out, \".Load(\")?;\n\n        self.write_texture_coordinates(\"int\", coordinate, array_index, level, module, func_ctx)?;\n\n        if let Some(sample) = sample {\n            write!(self.out, \", \")?;\n            self.write_expr(module, sample, func_ctx)?;\n        }\n\n        // close bracket for Load function\n        write!(self.out, \")\")?;\n\n        if wrapping_type.is_some() {\n            write!(self.out, \")\")?;\n        }\n\n        // return x component if return type is scalar\n        if let TypeInner::Scalar(_) = *func_ctx.resolve_type(expr, &module.types) {\n            write!(self.out, \".x\")?;\n        }\n        Ok(())\n    }\n\n    /// Find the [`BindingArraySamplerInfo`] from an expression so that such an access\n    /// can be generated later.\n    fn sampler_binding_array_info_from_expression(\n        &mut self,\n        module: &Module,\n        func_ctx: &back::FunctionCtx<'_>,\n        base: Handle<crate::Expression>,\n        resolved: &TypeInner,\n    ) -> Option<BindingArraySamplerInfo> {\n        if let TypeInner::BindingArray {\n            base: base_ty_handle,\n            ..\n        } = *resolved\n        {\n            let base_ty = &module.types[base_ty_handle].inner;\n            if let TypeInner::Sampler { comparison, .. } = *base_ty {\n                let base = &func_ctx.expressions[base];\n\n                if let crate::Expression::GlobalVariable(handle) = *base {\n                    let variable = &module.global_variables[handle];\n\n                    let sampler_heap_name = match comparison {\n                        true => COMPARISON_SAMPLER_HEAP_VAR,\n                        false => SAMPLER_HEAP_VAR,\n                    };\n\n                    return Some(BindingArraySamplerInfo {\n                        sampler_heap_name,\n                        sampler_index_buffer_name: self\n                            .wrapped\n                            .sampler_index_buffers\n                            .get(&super::SamplerIndexBufferKey {\n                                group: variable.binding.unwrap().group,\n                            })\n                            .unwrap()\n                            .clone(),\n                        binding_array_base_index_name: self.names[&NameKey::GlobalVariable(handle)]\n                            .clone(),\n                    });\n                }\n            }\n        }\n\n        None\n    }\n\n    fn write_named_expr(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Expression>,\n        name: String,\n        // The expression which is being named.\n        // Generally, this is the same as handle, except in WorkGroupUniformLoad\n        named: Handle<crate::Expression>,\n        ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        match ctx.info[named].ty {\n            proc::TypeResolution::Handle(ty_handle) => match module.types[ty_handle].inner {\n                TypeInner::Struct { .. } => {\n                    let ty_name = &self.names[&NameKey::Type(ty_handle)];\n                    write!(self.out, \"{ty_name}\")?;\n                }\n                _ => {\n                    self.write_type(module, ty_handle)?;\n                }\n            },\n            proc::TypeResolution::Value(ref inner) => {\n                self.write_value_type(module, inner)?;\n            }\n        }\n\n        let resolved = ctx.resolve_type(named, &module.types);\n\n        write!(self.out, \" {name}\")?;\n        // If rhs is a array type, we should write array size\n        if let TypeInner::Array { base, size, .. } = *resolved {\n            self.write_array_size(module, base, size)?;\n        }\n        write!(self.out, \" = \")?;\n        self.write_expr(module, handle, ctx)?;\n        writeln!(self.out, \";\")?;\n        self.named_expressions.insert(named, name);\n\n        Ok(())\n    }\n\n    /// Helper function that write default zero initialization\n    pub(super) fn write_default_init(\n        &mut self,\n        module: &Module,\n        ty: Handle<crate::Type>,\n    ) -> BackendResult {\n        write!(self.out, \"(\")?;\n        self.write_type(module, ty)?;\n        if let TypeInner::Array { base, size, .. } = module.types[ty].inner {\n            self.write_array_size(module, base, size)?;\n        }\n        write!(self.out, \")0\")?;\n        Ok(())\n    }\n\n    fn write_control_barrier(\n        &mut self,\n        barrier: crate::Barrier,\n        level: back::Level,\n    ) -> BackendResult {\n        if barrier.contains(crate::Barrier::STORAGE) {\n            writeln!(self.out, \"{level}DeviceMemoryBarrierWithGroupSync();\")?;\n        }\n        if barrier.contains(crate::Barrier::WORK_GROUP) {\n            writeln!(self.out, \"{level}GroupMemoryBarrierWithGroupSync();\")?;\n        }\n        if barrier.contains(crate::Barrier::SUB_GROUP) {\n            // Does not exist in DirectX\n        }\n        if barrier.contains(crate::Barrier::TEXTURE) {\n            writeln!(self.out, \"{level}DeviceMemoryBarrierWithGroupSync();\")?;\n        }\n        Ok(())\n    }\n\n    fn write_memory_barrier(\n        &mut self,\n        barrier: crate::Barrier,\n        level: back::Level,\n    ) -> BackendResult {\n        if barrier.contains(crate::Barrier::STORAGE) {\n            writeln!(self.out, \"{level}DeviceMemoryBarrier();\")?;\n        }\n        if barrier.contains(crate::Barrier::WORK_GROUP) {\n            writeln!(self.out, \"{level}GroupMemoryBarrier();\")?;\n        }\n        if barrier.contains(crate::Barrier::SUB_GROUP) {\n            // Does not exist in DirectX\n        }\n        if barrier.contains(crate::Barrier::TEXTURE) {\n            writeln!(self.out, \"{level}DeviceMemoryBarrier();\")?;\n        }\n        Ok(())\n    }\n\n    /// Helper to emit the shared tail of an HLSL atomic call (arguments, value, result)\n    fn emit_hlsl_atomic_tail(\n        &mut self,\n        module: &Module,\n        func_ctx: &back::FunctionCtx<'_>,\n        fun: &crate::AtomicFunction,\n        compare_expr: Option<Handle<crate::Expression>>,\n        value: Handle<crate::Expression>,\n        res_var_info: &Option<(Handle<crate::Expression>, String)>,\n    ) -> BackendResult {\n        if let Some(cmp) = compare_expr {\n            write!(self.out, \", \")?;\n            self.write_expr(module, cmp, func_ctx)?;\n        }\n        write!(self.out, \", \")?;\n        if let crate::AtomicFunction::Subtract = *fun {\n            // we just wrote `InterlockedAdd`, so negate the argument\n            write!(self.out, \"-\")?;\n        }\n        self.write_expr(module, value, func_ctx)?;\n        if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {\n            write!(self.out, \", \")?;\n            if compare_expr.is_some() {\n                write!(self.out, \"{res_name}.old_value\")?;\n            } else {\n                write!(self.out, \"{res_name}\")?;\n            }\n        }\n        writeln!(self.out, \");\")?;\n        Ok(())\n    }\n}\n\npub(super) struct MatrixType {\n    pub(super) columns: crate::VectorSize,\n    pub(super) rows: crate::VectorSize,\n    pub(super) width: crate::Bytes,\n}\n\npub(super) fn get_inner_matrix_data(\n    module: &Module,\n    handle: Handle<crate::Type>,\n) -> Option<MatrixType> {\n    match module.types[handle].inner {\n        TypeInner::Matrix {\n            columns,\n            rows,\n            scalar,\n        } => Some(MatrixType {\n            columns,\n            rows,\n            width: scalar.width,\n        }),\n        TypeInner::Array { base, .. } => get_inner_matrix_data(module, base),\n        _ => None,\n    }\n}\n\n/// If `base` is an access chain of the form `mat`, `mat[col]`, or `mat[col][row]`,\n/// returns a tuple of the matrix, the column (vector) index (if present), and\n/// the row (scalar) index (if present).\nfn find_matrix_in_access_chain(\n    module: &Module,\n    base: Handle<crate::Expression>,\n    func_ctx: &back::FunctionCtx<'_>,\n) -> Option<(Handle<crate::Expression>, Option<Index>, Option<Index>)> {\n    let mut current_base = base;\n    let mut vector = None;\n    let mut scalar = None;\n    loop {\n        let resolved_tr = func_ctx\n            .resolve_type(current_base, &module.types)\n            .pointer_base_type();\n        let resolved = resolved_tr.as_ref()?.inner_with(&module.types);\n\n        match *resolved {\n            TypeInner::Matrix { .. } => return Some((current_base, vector, scalar)),\n            TypeInner::Scalar(_) | TypeInner::Vector { .. } => {}\n            _ => return None,\n        }\n\n        let index;\n        (current_base, index) = match func_ctx.expressions[current_base] {\n            crate::Expression::Access { base, index } => (base, Index::Expression(index)),\n            crate::Expression::AccessIndex { base, index } => (base, Index::Static(index)),\n            _ => return None,\n        };\n\n        match *resolved {\n            TypeInner::Scalar(_) => scalar = Some(index),\n            TypeInner::Vector { .. } => vector = Some(index),\n            _ => unreachable!(),\n        }\n    }\n}\n\n/// Returns the matrix data if the access chain starting at `base`:\n/// - starts with an expression with resolved type of [`TypeInner::Matrix`] if `direct = true`\n/// - contains one or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]\n/// - ends at an expression with resolved type of [`TypeInner::Struct`]\npub(super) fn get_inner_matrix_of_struct_array_member(\n    module: &Module,\n    base: Handle<crate::Expression>,\n    func_ctx: &back::FunctionCtx<'_>,\n    direct: bool,\n) -> Option<MatrixType> {\n    let mut mat_data = None;\n    let mut array_base = None;\n\n    let mut current_base = base;\n    loop {\n        let mut resolved = func_ctx.resolve_type(current_base, &module.types);\n        if let TypeInner::Pointer { base, .. } = *resolved {\n            resolved = &module.types[base].inner;\n        };\n\n        match *resolved {\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                mat_data = Some(MatrixType {\n                    columns,\n                    rows,\n                    width: scalar.width,\n                })\n            }\n            TypeInner::Array { base, .. } => {\n                array_base = Some(base);\n            }\n            TypeInner::Struct { .. } => {\n                if let Some(array_base) = array_base {\n                    if direct {\n                        return mat_data;\n                    } else {\n                        return get_inner_matrix_data(module, array_base);\n                    }\n                }\n\n                break;\n            }\n            _ => break,\n        }\n\n        current_base = match func_ctx.expressions[current_base] {\n            crate::Expression::Access { base, .. } => base,\n            crate::Expression::AccessIndex { base, .. } => base,\n            _ => break,\n        };\n    }\n    None\n}\n\n/// Simpler version of get_inner_matrix_of_global_uniform that only looks at the\n/// immediate expression, rather than traversing an access chain.\nfn get_global_uniform_matrix(\n    module: &Module,\n    base: Handle<crate::Expression>,\n    func_ctx: &back::FunctionCtx<'_>,\n) -> Option<MatrixType> {\n    let base_tr = func_ctx\n        .resolve_type(base, &module.types)\n        .pointer_base_type();\n    let base_ty = base_tr.as_ref().map(|tr| tr.inner_with(&module.types));\n    match (&func_ctx.expressions[base], base_ty) {\n        (\n            &crate::Expression::GlobalVariable(handle),\n            Some(&TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            }),\n        ) if module.global_variables[handle].space == crate::AddressSpace::Uniform => {\n            Some(MatrixType {\n                columns,\n                rows,\n                width: scalar.width,\n            })\n        }\n        _ => None,\n    }\n}\n\n/// Returns the matrix data if the access chain starting at `base`:\n/// - starts with an expression with resolved type of [`TypeInner::Matrix`]\n/// - contains zero or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]\n/// - ends with an [`Expression::GlobalVariable`](crate::Expression::GlobalVariable) in [`AddressSpace::Uniform`](crate::AddressSpace::Uniform)\nfn get_inner_matrix_of_global_uniform(\n    module: &Module,\n    base: Handle<crate::Expression>,\n    func_ctx: &back::FunctionCtx<'_>,\n) -> Option<MatrixType> {\n    let mut mat_data = None;\n    let mut array_base = None;\n\n    let mut current_base = base;\n    loop {\n        let mut resolved = func_ctx.resolve_type(current_base, &module.types);\n        if let TypeInner::Pointer { base, .. } = *resolved {\n            resolved = &module.types[base].inner;\n        };\n\n        match *resolved {\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                mat_data = Some(MatrixType {\n                    columns,\n                    rows,\n                    width: scalar.width,\n                })\n            }\n            TypeInner::Array { base, .. } => {\n                array_base = Some(base);\n            }\n            _ => break,\n        }\n\n        current_base = match func_ctx.expressions[current_base] {\n            crate::Expression::Access { base, .. } => base,\n            crate::Expression::AccessIndex { base, .. } => base,\n            crate::Expression::GlobalVariable(handle)\n                if module.global_variables[handle].space == crate::AddressSpace::Uniform =>\n            {\n                return mat_data.or_else(|| {\n                    array_base.and_then(|array_base| get_inner_matrix_data(module, array_base))\n                })\n            }\n            _ => break,\n        };\n    }\n    None\n}\n"
  },
  {
    "path": "naga/src/back/mod.rs",
    "content": "/*!\nBackend functions that export shader [`Module`](super::Module)s into binary and text formats.\n*/\n#![cfg_attr(\n    not(any(dot_out, glsl_out, hlsl_out, msl_out, spv_out, wgsl_out)),\n    allow(\n        dead_code,\n        reason = \"shared helpers can be dead if none of the enabled backends need it\"\n    )\n)]\n\nuse alloc::string::String;\n\n#[cfg(dot_out)]\npub mod dot;\n#[cfg(glsl_out)]\npub mod glsl;\n#[cfg(hlsl_out)]\npub mod hlsl;\n#[cfg(msl_out)]\npub mod msl;\n#[cfg(spv_out)]\npub mod spv;\n#[cfg(wgsl_out)]\npub mod wgsl;\n\n#[cfg(any(hlsl_out, msl_out, spv_out, glsl_out))]\npub mod pipeline_constants;\n\n#[cfg(any(hlsl_out, glsl_out))]\nmod continue_forward;\n\n/// Names of vector components.\npub const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];\n/// Indent for backends.\npub const INDENT: &str = \"    \";\n\n/// Expressions that need baking.\npub type NeedBakeExpressions = crate::FastHashSet<crate::Handle<crate::Expression>>;\n\n/// A type for displaying expression handles as baking identifiers.\n///\n/// Given an [`Expression`] [`Handle`] `h`, `Baked(h)` implements\n/// [`core::fmt::Display`], showing the handle's index prefixed by\n/// `_e`.\n///\n/// [`Expression`]: crate::Expression\n/// [`Handle`]: crate::Handle\n#[cfg_attr(\n    not(any(glsl_out, hlsl_out, msl_out, wgsl_out)),\n    allow(\n        dead_code,\n        reason = \"shared helpers can be dead if none of the enabled backends need it\"\n    )\n)]\nstruct Baked(crate::Handle<crate::Expression>);\n\nimpl core::fmt::Display for Baked {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"_e\")\n    }\n}\n\nbitflags::bitflags! {\n    /// How far through a ray query are we\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    #[cfg_attr(\n        not(any(hlsl_out, spv_out)),\n        allow(\n            dead_code,\n            reason = \"shared helpers can be dead if none of the enabled backends need it\"\n        )\n    )]\n    pub(super) struct RayQueryPoint: u32 {\n        /// Ray query has been successfully initialized.\n        const INITIALIZED = 1 << 0;\n        /// Proceed has been called on ray query.\n        const PROCEED = 1 << 1;\n        /// Proceed has returned false (have finished traversal).\n        const FINISHED_TRAVERSAL = 1 << 2;\n    }\n}\n\n/// Specifies the values of pipeline-overridable constants in the shader module.\n///\n/// If an `@id` attribute was specified on the declaration,\n/// the key must be the pipeline constant ID as a decimal ASCII number; if not,\n/// the key must be the constant's identifier name.\n///\n/// The value may represent any of WGSL's concrete scalar types.\npub type PipelineConstants = hashbrown::HashMap<String, f64>;\n\n/// Indentation level.\n#[derive(Clone, Copy)]\npub struct Level(pub usize);\n\nimpl Level {\n    pub const fn next(&self) -> Self {\n        Level(self.0 + 1)\n    }\n}\n\nimpl core::fmt::Display for Level {\n    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {\n        (0..self.0).try_for_each(|_| formatter.write_str(INDENT))\n    }\n}\n\n/// Locate the entry point(s) to write.\n///\n/// If `entry_point` is given, and the specified entry point exists, returns a\n/// length-1 `Range` containing the index of that entry point.  If no\n/// `entry_point` is given, returns the complete range of entry point indices.\n/// If `entry_point` is given but does not exist, returns an error.\n#[cfg(any(hlsl_out, msl_out))]\nfn get_entry_points(\n    module: &crate::ir::Module,\n    entry_point: Option<&(crate::ir::ShaderStage, String)>,\n) -> Result<core::ops::Range<usize>, (crate::ir::ShaderStage, String)> {\n    use alloc::borrow::ToOwned;\n\n    if let Some(&(stage, ref name)) = entry_point {\n        let Some(ep_index) = module\n            .entry_points\n            .iter()\n            .position(|ep| ep.stage == stage && ep.name == *name)\n        else {\n            return Err((stage, name.to_owned()));\n        };\n        Ok(ep_index..ep_index + 1)\n    } else {\n        Ok(0..module.entry_points.len())\n    }\n}\n\n/// Whether we're generating an entry point or a regular function.\n///\n/// Backend languages often require different code for a [`Function`]\n/// depending on whether it represents an [`EntryPoint`] or not.\n/// Backends can pass common code one of these values to select the\n/// right behavior.\n///\n/// These values also carry enough information to find the `Function`\n/// in the [`Module`]: the `Handle` for a regular function, or the\n/// index into [`Module::entry_points`] for an entry point.\n///\n/// [`Function`]: crate::Function\n/// [`EntryPoint`]: crate::EntryPoint\n/// [`Module`]: crate::Module\n/// [`Module::entry_points`]: crate::Module::entry_points\n#[derive(Clone, Copy, Debug)]\npub enum FunctionType {\n    /// A regular function.\n    Function(crate::Handle<crate::Function>),\n    /// An [`EntryPoint`], and its index in [`Module::entry_points`].\n    ///\n    /// [`EntryPoint`]: crate::EntryPoint\n    /// [`Module::entry_points`]: crate::Module::entry_points\n    EntryPoint(crate::proc::EntryPointIndex),\n}\n\nimpl FunctionType {\n    /// Returns true if the function is an entry point for a compute-like shader.\n    pub fn is_compute_like_entry_point(&self, module: &crate::Module) -> bool {\n        match *self {\n            FunctionType::EntryPoint(index) => {\n                module.entry_points[index as usize].stage.compute_like()\n            }\n            FunctionType::Function(_) => false,\n        }\n    }\n}\n\n/// Helper structure that stores data needed when writing the function\npub struct FunctionCtx<'a> {\n    /// The current function being written\n    pub ty: FunctionType,\n    /// Analysis about the function\n    pub info: &'a crate::valid::FunctionInfo,\n    /// The expression arena of the current function being written\n    pub expressions: &'a crate::Arena<crate::Expression>,\n    /// Map of expressions that have associated variable names\n    pub named_expressions: &'a crate::NamedExpressions,\n}\n\nimpl FunctionCtx<'_> {\n    /// Helper method that resolves a type of a given expression.\n    pub fn resolve_type<'a>(\n        &'a self,\n        handle: crate::Handle<crate::Expression>,\n        types: &'a crate::UniqueArena<crate::Type>,\n    ) -> &'a crate::TypeInner {\n        self.info[handle].ty.inner_with(types)\n    }\n\n    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function\n    pub const fn name_key(\n        &self,\n        local: crate::Handle<crate::LocalVariable>,\n    ) -> crate::proc::NameKey {\n        match self.ty {\n            FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),\n            FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),\n        }\n    }\n\n    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a function argument.\n    ///\n    /// # Panics\n    /// - If the function arguments are less or equal to `arg`\n    pub const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {\n        match self.ty {\n            FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),\n            FunctionType::EntryPoint(ep_index) => {\n                crate::proc::NameKey::EntryPointArgument(ep_index, arg)\n            }\n        }\n    }\n\n    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for an external texture\n    /// function argument.\n    ///\n    /// # Panics\n    /// - If the function arguments are less or equal to `arg`\n    /// - If `self.ty` is not `FunctionType::Function`.\n    pub const fn external_texture_argument_key(\n        &self,\n        arg: u32,\n        external_texture_key: crate::proc::ExternalTextureNameKey,\n    ) -> crate::proc::NameKey {\n        match self.ty {\n            FunctionType::Function(handle) => {\n                crate::proc::NameKey::ExternalTextureFunctionArgument(\n                    handle,\n                    arg,\n                    external_texture_key,\n                )\n            }\n            // This is a const function, which _sometimes_ gets called,\n            // so this lint is _sometimes_ triggered, depending on feature set.\n            #[expect(clippy::allow_attributes)]\n            #[allow(clippy::panic)]\n            FunctionType::EntryPoint(_) => {\n                panic!(\"External textures cannot be used as arguments to entry points\")\n            }\n        }\n    }\n\n    /// Returns true if the given expression points to a fixed-function pipeline input.\n    pub fn is_fixed_function_input(\n        &self,\n        mut expression: crate::Handle<crate::Expression>,\n        module: &crate::Module,\n    ) -> Option<crate::BuiltIn> {\n        let ep_function = match self.ty {\n            FunctionType::Function(_) => return None,\n            FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,\n        };\n        let mut built_in = None;\n        loop {\n            match self.expressions[expression] {\n                crate::Expression::FunctionArgument(arg_index) => {\n                    return match ep_function.arguments[arg_index as usize].binding {\n                        Some(crate::Binding::BuiltIn(bi)) => Some(bi),\n                        _ => built_in,\n                    };\n                }\n                crate::Expression::AccessIndex { base, index } => {\n                    match *self.resolve_type(base, &module.types) {\n                        crate::TypeInner::Struct { ref members, .. } => {\n                            if let Some(crate::Binding::BuiltIn(bi)) =\n                                members[index as usize].binding\n                            {\n                                built_in = Some(bi);\n                            }\n                        }\n                        _ => return None,\n                    }\n                    expression = base;\n                }\n                _ => return None,\n            }\n        }\n    }\n}\n\nimpl crate::Expression {\n    /// Returns the ref count, upon reaching which this expression\n    /// should be considered for baking.\n    ///\n    /// Note: we have to cache any expressions that depend on the control flow,\n    /// or otherwise they may be moved into a non-uniform control flow, accidentally.\n    /// See the [module-level documentation][emit] for details.\n    ///\n    /// [emit]: index.html#expression-evaluation-time\n    pub const fn bake_ref_count(&self) -> usize {\n        match *self {\n            // accesses are never cached, only loads are\n            crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,\n            // sampling may use the control flow, and image ops look better by themselves\n            crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,\n            // derivatives use the control flow\n            crate::Expression::Derivative { .. } => 1,\n            // TODO: We need a better fix for named `Load` expressions\n            // More info - https://github.com/gfx-rs/naga/pull/914\n            // And https://github.com/gfx-rs/naga/issues/910\n            crate::Expression::Load { .. } => 1,\n            // cache expressions that are referenced multiple times\n            _ => 2,\n        }\n    }\n}\n\n/// Helper function that returns the string corresponding to the [`BinaryOperator`](crate::BinaryOperator)\npub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {\n    use crate::BinaryOperator as Bo;\n    match op {\n        Bo::Add => \"+\",\n        Bo::Subtract => \"-\",\n        Bo::Multiply => \"*\",\n        Bo::Divide => \"/\",\n        Bo::Modulo => \"%\",\n        Bo::Equal => \"==\",\n        Bo::NotEqual => \"!=\",\n        Bo::Less => \"<\",\n        Bo::LessEqual => \"<=\",\n        Bo::Greater => \">\",\n        Bo::GreaterEqual => \">=\",\n        Bo::And => \"&\",\n        Bo::ExclusiveOr => \"^\",\n        Bo::InclusiveOr => \"|\",\n        Bo::LogicalAnd => \"&&\",\n        Bo::LogicalOr => \"||\",\n        Bo::ShiftLeft => \"<<\",\n        Bo::ShiftRight => \">>\",\n    }\n}\n\nimpl crate::TypeInner {\n    /// Returns true if a variable of this type is a handle.\n    pub const fn is_handle(&self) -> bool {\n        match *self {\n            Self::Image { .. } | Self::Sampler { .. } | Self::AccelerationStructure { .. } => true,\n            _ => false,\n        }\n    }\n}\n\nimpl crate::Statement {\n    /// Returns true if the statement directly terminates the current block.\n    ///\n    /// Used to decide whether case blocks require a explicit `break`.\n    pub const fn is_terminator(&self) -> bool {\n        match *self {\n            crate::Statement::Break\n            | crate::Statement::Continue\n            | crate::Statement::Return { .. }\n            | crate::Statement::Kill => true,\n            _ => false,\n        }\n    }\n}\n\nbitflags::bitflags! {\n    /// Ray flags, for a [`RayDesc`]'s `flags` field.\n    ///\n    /// Note that these exactly correspond to the SPIR-V \"Ray Flags\" mask, and\n    /// the SPIR-V backend passes them directly through to the\n    /// [`OpRayQueryInitializeKHR`][op] instruction. (We have to choose something, so\n    /// we might as well make one back end's life easier.)\n    ///\n    /// [`RayDesc`]: crate::Module::generate_ray_desc_type\n    /// [op]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpRayQueryInitializeKHR\n    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\n    pub struct RayFlag: u32 {\n        const OPAQUE = 0x01;\n        const NO_OPAQUE = 0x02;\n        const TERMINATE_ON_FIRST_HIT = 0x04;\n        const SKIP_CLOSEST_HIT_SHADER = 0x08;\n        const CULL_BACK_FACING = 0x10;\n        const CULL_FRONT_FACING = 0x20;\n        const CULL_OPAQUE = 0x40;\n        const CULL_NO_OPAQUE = 0x80;\n        const SKIP_TRIANGLES = 0x100;\n        const SKIP_AABBS = 0x200;\n    }\n}\n\n/// The intersection test to use for ray queries.\n#[repr(u32)]\npub enum RayIntersectionType {\n    Triangle = 1,\n    BoundingBox = 4,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct TaskDispatchLimits {\n    pub max_mesh_workgroups_per_dim: u32,\n    pub max_mesh_workgroups_total: u32,\n}\n"
  },
  {
    "path": "naga/src/back/msl/keywords.rs",
    "content": "use crate::proc::{concrete_int_scalars, vector_size_str, vector_sizes, KeywordSet};\nuse crate::racy_lock::RacyLock;\nuse alloc::{format, string::String, vec::Vec};\n\n// MSLS - Metal Shading Language Specification:\n// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf\n//\n// C++ - Standard for Programming Language C++ (N4431)\n// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4431.pdf\nconst RESERVED: &[&str] = &[\n    // Undocumented\n    \"assert\", // found in https://github.com/gfx-rs/wgpu/issues/5347\n    // Standard for Programming Language C++ (N4431): 2.5 Alternative tokens\n    \"and\",\n    \"bitor\",\n    \"or\",\n    \"xor\",\n    \"compl\",\n    \"bitand\",\n    \"and_eq\",\n    \"or_eq\",\n    \"xor_eq\",\n    \"not\",\n    \"not_eq\",\n    // Standard for Programming Language C++ (N4431): 2.11 Keywords\n    \"alignas\",\n    \"alignof\",\n    \"asm\",\n    \"auto\",\n    \"bool\",\n    \"break\",\n    \"case\",\n    \"catch\",\n    \"char\",\n    \"char16_t\",\n    \"char32_t\",\n    \"class\",\n    \"const\",\n    \"constexpr\",\n    \"const_cast\",\n    \"continue\",\n    \"decltype\",\n    \"default\",\n    \"delete\",\n    \"do\",\n    \"double\",\n    \"dynamic_cast\",\n    \"else\",\n    \"enum\",\n    \"explicit\",\n    \"export\",\n    \"extern\",\n    \"false\",\n    \"float\",\n    \"for\",\n    \"friend\",\n    \"goto\",\n    \"if\",\n    \"inline\",\n    \"int\",\n    \"long\",\n    \"mutable\",\n    \"namespace\",\n    \"new\",\n    \"noexcept\",\n    \"nullptr\",\n    \"operator\",\n    \"private\",\n    \"protected\",\n    \"public\",\n    \"register\",\n    \"reinterpret_cast\",\n    \"return\",\n    \"short\",\n    \"signed\",\n    \"sizeof\",\n    \"static\",\n    \"static_assert\",\n    \"static_cast\",\n    \"struct\",\n    \"switch\",\n    \"template\",\n    \"this\",\n    \"thread_local\",\n    \"throw\",\n    \"true\",\n    \"try\",\n    \"typedef\",\n    \"typeid\",\n    \"typename\",\n    \"union\",\n    \"unsigned\",\n    \"using\",\n    \"virtual\",\n    \"void\",\n    \"volatile\",\n    \"wchar_t\",\n    \"while\",\n    // Metal Shading Language Specification: 1.4.4 Restrictions\n    \"main\",\n    // Metal Shading Language Specification: 2.1 Scalar Data Types\n    \"int8_t\",\n    \"uchar\",\n    \"uint8_t\",\n    \"int16_t\",\n    \"ushort\",\n    \"uint16_t\",\n    \"int32_t\",\n    \"uint\",\n    \"uint32_t\",\n    \"int64_t\",\n    \"uint64_t\",\n    \"half\",\n    \"bfloat\",\n    \"size_t\",\n    \"ptrdiff_t\",\n    // Metal Shading Language Specification: 2.2 Vector Data Types\n    \"bool2\",\n    \"bool3\",\n    \"bool4\",\n    \"char2\",\n    \"char3\",\n    \"char4\",\n    \"short2\",\n    \"short3\",\n    \"short4\",\n    \"int2\",\n    \"int3\",\n    \"int4\",\n    \"long2\",\n    \"long3\",\n    \"long4\",\n    \"uchar2\",\n    \"uchar3\",\n    \"uchar4\",\n    \"ushort2\",\n    \"ushort3\",\n    \"ushort4\",\n    \"uint2\",\n    \"uint3\",\n    \"uint4\",\n    \"ulong2\",\n    \"ulong3\",\n    \"ulong4\",\n    \"half2\",\n    \"half3\",\n    \"half4\",\n    \"bfloat2\",\n    \"bfloat3\",\n    \"bfloat4\",\n    \"float2\",\n    \"float3\",\n    \"float4\",\n    \"vec\",\n    // Metal Shading Language Specification: 2.2.3 Packed Vector Types\n    \"packed_bool2\",\n    \"packed_bool3\",\n    \"packed_bool4\",\n    \"packed_char2\",\n    \"packed_char3\",\n    \"packed_char4\",\n    \"packed_short2\",\n    \"packed_short3\",\n    \"packed_short4\",\n    \"packed_int2\",\n    \"packed_int3\",\n    \"packed_int4\",\n    \"packed_uchar2\",\n    \"packed_uchar3\",\n    \"packed_uchar4\",\n    \"packed_ushort2\",\n    \"packed_ushort3\",\n    \"packed_ushort4\",\n    \"packed_uint2\",\n    \"packed_uint3\",\n    \"packed_uint4\",\n    \"packed_half2\",\n    \"packed_half3\",\n    \"packed_half4\",\n    \"packed_bfloat2\",\n    \"packed_bfloat3\",\n    \"packed_bfloat4\",\n    \"packed_float2\",\n    \"packed_float3\",\n    \"packed_float4\",\n    \"packed_long2\",\n    \"packed_long3\",\n    \"packed_long4\",\n    \"packed_vec\",\n    // Metal Shading Language Specification: 2.3 Matrix Data Types\n    \"half2x2\",\n    \"half2x3\",\n    \"half2x4\",\n    \"half3x2\",\n    \"half3x3\",\n    \"half3x4\",\n    \"half4x2\",\n    \"half4x3\",\n    \"half4x4\",\n    \"float2x2\",\n    \"float2x3\",\n    \"float2x4\",\n    \"float3x2\",\n    \"float3x3\",\n    \"float3x4\",\n    \"float4x2\",\n    \"float4x3\",\n    \"float4x4\",\n    \"matrix\",\n    // Metal Shading Language Specification: 2.6 Atomic Data Types\n    \"atomic\",\n    \"atomic_int\",\n    \"atomic_uint\",\n    \"atomic_bool\",\n    \"atomic_ulong\",\n    \"atomic_float\",\n    // Metal Shading Language Specification: 2.20 Type Conversions and Re-interpreting Data\n    \"as_type\",\n    // Metal Shading Language Specification: 4 Address Spaces\n    \"device\",\n    \"constant\",\n    \"thread\",\n    \"threadgroup\",\n    \"threadgroup_imageblock\",\n    \"ray_data\",\n    \"object_data\",\n    // Metal Shading Language Specification: 5.1 Functions\n    \"vertex\",\n    \"fragment\",\n    \"kernel\",\n    // Metal Shading Language Specification: 6.1 Namespace and Header Files\n    \"metal\",\n    // C99 / C++ extension:\n    \"restrict\",\n    // Metal reserved types in <metal_types>:\n    \"llong\",\n    \"ullong\",\n    \"quad\",\n    \"complex\",\n    \"imaginary\",\n    // Constants in <metal_types>:\n    \"CHAR_BIT\",\n    \"SCHAR_MAX\",\n    \"SCHAR_MIN\",\n    \"UCHAR_MAX\",\n    \"CHAR_MAX\",\n    \"CHAR_MIN\",\n    \"USHRT_MAX\",\n    \"SHRT_MAX\",\n    \"SHRT_MIN\",\n    \"UINT_MAX\",\n    \"INT_MAX\",\n    \"INT_MIN\",\n    \"ULONG_MAX\",\n    \"LONG_MAX\",\n    \"LONG_MIN\",\n    \"ULLONG_MAX\",\n    \"LLONG_MAX\",\n    \"LLONG_MIN\",\n    \"FLT_DIG\",\n    \"FLT_MANT_DIG\",\n    \"FLT_MAX_10_EXP\",\n    \"FLT_MAX_EXP\",\n    \"FLT_MIN_10_EXP\",\n    \"FLT_MIN_EXP\",\n    \"FLT_RADIX\",\n    \"FLT_MAX\",\n    \"FLT_MIN\",\n    \"FLT_EPSILON\",\n    \"FLT_DECIMAL_DIG\",\n    \"FP_ILOGB0\",\n    \"FP_ILOGB0\",\n    \"FP_ILOGBNAN\",\n    \"FP_ILOGBNAN\",\n    \"MAXFLOAT\",\n    \"HUGE_VALF\",\n    \"INFINITY\",\n    \"NAN\",\n    \"M_E_F\",\n    \"M_LOG2E_F\",\n    \"M_LOG10E_F\",\n    \"M_LN2_F\",\n    \"M_LN10_F\",\n    \"M_PI_F\",\n    \"M_PI_2_F\",\n    \"M_PI_4_F\",\n    \"M_1_PI_F\",\n    \"M_2_PI_F\",\n    \"M_2_SQRTPI_F\",\n    \"M_SQRT2_F\",\n    \"M_SQRT1_2_F\",\n    \"HALF_DIG\",\n    \"HALF_MANT_DIG\",\n    \"HALF_MAX_10_EXP\",\n    \"HALF_MAX_EXP\",\n    \"HALF_MIN_10_EXP\",\n    \"HALF_MIN_EXP\",\n    \"HALF_RADIX\",\n    \"HALF_MAX\",\n    \"HALF_MIN\",\n    \"HALF_EPSILON\",\n    \"HALF_DECIMAL_DIG\",\n    \"MAXHALF\",\n    \"HUGE_VALH\",\n    \"M_E_H\",\n    \"M_LOG2E_H\",\n    \"M_LOG10E_H\",\n    \"M_LN2_H\",\n    \"M_LN10_H\",\n    \"M_PI_H\",\n    \"M_PI_2_H\",\n    \"M_PI_4_H\",\n    \"M_1_PI_H\",\n    \"M_2_PI_H\",\n    \"M_2_SQRTPI_H\",\n    \"M_SQRT2_H\",\n    \"M_SQRT1_2_H\",\n    \"DBL_DIG\",\n    \"DBL_MANT_DIG\",\n    \"DBL_MAX_10_EXP\",\n    \"DBL_MAX_EXP\",\n    \"DBL_MIN_10_EXP\",\n    \"DBL_MIN_EXP\",\n    \"DBL_RADIX\",\n    \"DBL_MAX\",\n    \"DBL_MIN\",\n    \"DBL_EPSILON\",\n    \"DBL_DECIMAL_DIG\",\n    \"MAXDOUBLE\",\n    \"HUGE_VAL\",\n    \"M_E\",\n    \"M_LOG2E\",\n    \"M_LOG10E\",\n    \"M_LN2\",\n    \"M_LN10\",\n    \"M_PI\",\n    \"M_PI_2\",\n    \"M_PI_4\",\n    \"M_1_PI\",\n    \"M_2_PI\",\n    \"M_2_SQRTPI\",\n    \"M_SQRT2\",\n    \"M_SQRT1_2\",\n    // Naga utilities\n    \"DefaultConstructible\",\n    // Naga builtin names\n    \"__local_invocation_id\",\n    super::writer::FREXP_FUNCTION,\n    super::writer::MODF_FUNCTION,\n    super::writer::ABS_FUNCTION,\n    super::writer::DIV_FUNCTION,\n    // DOT_FUNCTION_PREFIX variants are added dynamically below\n    super::writer::MOD_FUNCTION,\n    super::writer::NEG_FUNCTION,\n    super::writer::F2I32_FUNCTION,\n    super::writer::F2U32_FUNCTION,\n    super::writer::F2I64_FUNCTION,\n    super::writer::F2U64_FUNCTION,\n    super::writer::IMAGE_LOAD_EXTERNAL_FUNCTION,\n    super::writer::IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION,\n    super::writer::IMAGE_SIZE_EXTERNAL_FUNCTION,\n    super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT,\n    super::writer::EXTERNAL_TEXTURE_WRAPPER_STRUCT,\n    super::writer::COOPERATIVE_LOAD_FUNCTION,\n    super::writer::COOPERATIVE_MULTIPLY_ADD_FUNCTION,\n];\n\n// The set of concrete integer dot product function variants.\n// This must match the set of names that could be produced by\n// `Writer::get_dot_wrapper_function_helper_name`.\nstatic DOT_FUNCTION_NAMES: RacyLock<Vec<String>> = RacyLock::new(|| {\n    let mut names = Vec::new();\n    for scalar in concrete_int_scalars().map(crate::Scalar::to_msl_name) {\n        for size_suffix in vector_sizes().map(vector_size_str) {\n            let fun_name = format!(\n                \"{}_{}{}\",\n                super::writer::DOT_FUNCTION_PREFIX,\n                scalar,\n                size_suffix\n            );\n            names.push(fun_name);\n        }\n    }\n    names\n});\n\n/// The above set of reserved keywords, turned into a cached HashSet. This saves\n/// significant time during [`Namer::reset`](crate::proc::Namer::reset).\n///\n/// See <https://github.com/gfx-rs/wgpu/pull/7338> for benchmarks.\npub static RESERVED_SET: RacyLock<KeywordSet> = RacyLock::new(|| {\n    let mut set = KeywordSet::from_iter(RESERVED);\n    set.extend(DOT_FUNCTION_NAMES.iter().map(String::as_str));\n    set\n});\n"
  },
  {
    "path": "naga/src/back/msl/mod.rs",
    "content": "/*!\nBackend for [MSL][msl] (Metal Shading Language).\n\nThis backend does not support the [`SHADER_INT64_ATOMIC_ALL_OPS`][all-atom]\ncapability.\n\n## Binding model\n\nMetal's bindings are flat per resource. Since there isn't an obvious mapping\nfrom SPIR-V's descriptor sets, we require a separate mapping provided in the options.\nThis mapping may have one or more resource end points for each descriptor set + index\npair.\n\n## Entry points\n\nEven though MSL and our IR appear to be similar in that the entry points in both can\naccept arguments and return values, the restrictions are different.\nMSL allows the varyings to be either in separate arguments, or inside a single\n`[[stage_in]]` struct. We gather input varyings and form this artificial structure.\nWe also add all the (non-Private) globals into the arguments.\n\nAt the beginning of the entry point, we assign the local constants and re-compose\nthe arguments as they are declared on IR side, so that the rest of the logic can\npretend that MSL doesn't have all the restrictions it has.\n\nFor the result type, if it's a structure, we re-compose it with a temporary value\nholding the result.\n\n[msl]: https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf\n[all-atom]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS\n\n## Pointer-typed bounds-checked expressions and OOB locals\n\nMSL (unlike HLSL and GLSL) has native support for pointer-typed function\narguments. When the [`BoundsCheckPolicy`] is `ReadZeroSkipWrite` and an\nout-of-bounds index expression is used for such an argument, our strategy is to\npass a pointer to a dummy variable. These dummy variables are called \"OOB\nlocals\". We emit at most one OOB local per function for each type, since all\nexpressions producing a result of that type can share the same OOB local. (Note\nthat the OOB local mechanism is not actually implementing \"skip write\", nor even\n\"read zero\" in some cases of read-after-write, but doing so would require\nadditional effort and the difference is unlikely to matter.)\n\n[`BoundsCheckPolicy`]: crate::proc::BoundsCheckPolicy\n\n## External textures\n\nSupport for [`crate::ImageClass::External`] textures is implemented by lowering\neach external texture global variable to 3 `texture2d<float, sample>`s, and a\nconstant buffer of type `NagaExternalTextureParams`. This provides up to 3\nplanes of texture data (for example single planar RGBA, or separate Y, Cb, and\nCr planes), and the parameters buffer containing information describing how to\nhandle these correctly. The bind target to use for each of these globals is\nspecified via the [`BindTarget::external_texture`] field of the relevant\nentries in [`EntryPointResources::resources`].\n\nExternal textures are supported by WGSL's `textureDimensions()`,\n`textureLoad()`, and `textureSampleBaseClampToEdge()` built-in functions. These\nare implemented using helper functions. See the following functions for how\nthese are generated:\n * `Writer::write_wrapped_image_query`\n * `Writer::write_wrapped_image_load`\n * `Writer::write_wrapped_image_sample`\n\nThe lowered global variables for each external texture global are passed to the\nentry point as separate arguments (see \"Entry points\" above). However, they are\nthen wrapped in a struct to allow them to be conveniently passed to user\ndefined and helper functions. See `writer::EXTERNAL_TEXTURE_WRAPPER_STRUCT`.\n*/\n\nuse alloc::{\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::fmt::{Error as FmtError, Write};\n\nuse crate::{arena::Handle, ir, proc::index, valid::ModuleInfo};\n\nmod keywords;\npub mod sampler;\nmod writer;\n\npub use writer::Writer;\n\npub type Slot = u8;\npub type InlineSamplerIndex = u8;\n\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum BindSamplerTarget {\n    Resource(Slot),\n    Inline(InlineSamplerIndex),\n}\n\n/// Binding information for a Naga [`External`] image global variable.\n///\n/// See the module documentation's section on external textures for details.\n///\n/// [`External`]: crate::ir::ImageClass::External\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct BindExternalTextureTarget {\n    pub planes: [Slot; 3],\n    pub params: Slot,\n}\n\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(any(feature = \"serialize\", feature = \"deserialize\"), serde(default))]\npub struct BindTarget {\n    pub buffer: Option<Slot>,\n    pub texture: Option<Slot>,\n    pub sampler: Option<BindSamplerTarget>,\n    pub external_texture: Option<BindExternalTextureTarget>,\n    pub mutable: bool,\n}\n\n#[cfg(feature = \"deserialize\")]\n#[derive(serde::Deserialize)]\nstruct BindingMapSerialization {\n    resource_binding: crate::ResourceBinding,\n    bind_target: BindTarget,\n}\n\n#[cfg(feature = \"deserialize\")]\nfn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;\n    let mut map = BindingMap::default();\n    for item in vec {\n        map.insert(item.resource_binding, item.bind_target);\n    }\n    Ok(map)\n}\n\n// Using `BTreeMap` instead of `HashMap` so that we can hash itself.\npub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindTarget>;\n\n#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(any(feature = \"serialize\", feature = \"deserialize\"), serde(default))]\npub struct EntryPointResources {\n    #[cfg_attr(\n        feature = \"deserialize\",\n        serde(deserialize_with = \"deserialize_binding_map\")\n    )]\n    pub resources: BindingMap,\n\n    pub immediates_buffer: Option<Slot>,\n\n    /// The slot of a buffer that contains an array of `u32`,\n    /// one for the size of each bound buffer that contains a runtime array,\n    /// in order of [`crate::GlobalVariable`] declarations.\n    pub sizes_buffer: Option<Slot>,\n}\n\npub type EntryPointResourceMap = alloc::collections::BTreeMap<String, EntryPointResources>;\n\nenum ResolvedBinding {\n    BuiltIn(crate::BuiltIn),\n    Attribute(u32),\n    Color {\n        location: u32,\n        blend_src: Option<u32>,\n    },\n    User {\n        prefix: &'static str,\n        index: u32,\n        interpolation: Option<ResolvedInterpolation>,\n    },\n    Resource(BindTarget),\n}\n\n#[derive(Copy, Clone)]\nenum ResolvedInterpolation {\n    CenterPerspective,\n    CenterNoPerspective,\n    CentroidPerspective,\n    CentroidNoPerspective,\n    SamplePerspective,\n    SampleNoPerspective,\n    Flat,\n}\n\n// Note: some of these should be removed in favor of proper IR validation.\n\n#[derive(Debug, thiserror::Error)]\npub enum Error {\n    #[error(transparent)]\n    Format(#[from] FmtError),\n    #[error(\"bind target {0:?} is empty\")]\n    UnimplementedBindTarget(BindTarget),\n    #[error(\"composing of {0:?} is not implemented yet\")]\n    UnsupportedCompose(Handle<crate::Type>),\n    #[error(\"operation {0:?} is not implemented yet\")]\n    UnsupportedBinaryOp(crate::BinaryOperator),\n    #[error(\"standard function '{0}' is not implemented yet\")]\n    UnsupportedCall(String),\n    #[error(\"feature '{0}' is not implemented yet\")]\n    FeatureNotImplemented(String),\n    #[error(\"internal naga error: module should not have validated: {0}\")]\n    GenericValidation(String),\n    #[error(\"BuiltIn {0:?} is not supported\")]\n    UnsupportedBuiltIn(crate::BuiltIn),\n    #[error(\"capability {0:?} is not supported\")]\n    CapabilityNotSupported(crate::valid::Capabilities),\n    #[error(\"attribute '{0}' is not supported for target MSL version\")]\n    UnsupportedAttribute(String),\n    #[error(\"function '{0}' is not supported for target MSL version\")]\n    UnsupportedFunction(String),\n    #[error(\"can not use writeable storage buffers in fragment stage prior to MSL 1.2\")]\n    UnsupportedWriteableStorageBuffer,\n    #[error(\"can not use writeable storage textures in {0:?} stage prior to MSL 1.2\")]\n    UnsupportedWriteableStorageTexture(ir::ShaderStage),\n    #[error(\"can not use read-write storage textures prior to MSL 1.2\")]\n    UnsupportedRWStorageTexture,\n    #[error(\"array of '{0}' is not supported for target MSL version\")]\n    UnsupportedArrayOf(String),\n    #[error(\"array of type '{0:?}' is not supported\")]\n    UnsupportedArrayOfType(Handle<crate::Type>),\n    #[error(\"ray tracing is not supported prior to MSL 2.4\")]\n    UnsupportedRayTracing,\n    #[error(\"cooperative matrix is not supported prior to MSL 2.3\")]\n    UnsupportedCooperativeMatrix,\n    #[error(\"overrides should not be present at this stage\")]\n    Override,\n    #[error(\"bitcasting to {0:?} is not supported\")]\n    UnsupportedBitCast(crate::TypeInner),\n    #[error(transparent)]\n    ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError),\n    #[error(\"entry point with stage {0:?} and name '{1}' not found\")]\n    EntryPointNotFound(ir::ShaderStage, String),\n}\n\n#[derive(Clone, Debug, PartialEq, thiserror::Error)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum EntryPointError {\n    #[error(\"global '{0}' doesn't have a binding\")]\n    MissingBinding(String),\n    #[error(\"mapping of {0:?} is missing\")]\n    MissingBindTarget(crate::ResourceBinding),\n    #[error(\"mapping for immediates is missing\")]\n    MissingImmediateData,\n    #[error(\"mapping for sizes buffer is missing\")]\n    MissingSizesBuffer,\n}\n\n/// Points in the MSL code where we might emit a pipeline input or output.\n///\n/// Note that, even though vertex shaders' outputs are always fragment\n/// shaders' inputs, we still need to distinguish `VertexOutput` and\n/// `FragmentInput`, since there are certain differences in the way\n/// [`ResolvedBinding`s] are represented on either side.\n///\n/// [`ResolvedBinding`s]: ResolvedBinding\n#[derive(Clone, Copy, Debug)]\nenum LocationMode {\n    /// Input to the vertex shader.\n    VertexInput,\n\n    /// Output from the vertex shader.\n    VertexOutput,\n\n    /// Input to the fragment shader.\n    FragmentInput,\n\n    /// Output from the fragment shader.\n    FragmentOutput,\n\n    /// Compute shader input or output.\n    Uniform,\n}\n\n#[derive(Clone, Debug, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct Options {\n    /// (Major, Minor) target version of the Metal Shading Language.\n    pub lang_version: (u8, u8),\n    /// Map of entry-point resources, indexed by entry point function name, to slots.\n    pub per_entry_point_map: EntryPointResourceMap,\n    /// Samplers to be inlined into the code.\n    pub inline_samplers: Vec<sampler::InlineSampler>,\n    /// Make it possible to link different stages via SPIRV-Cross.\n    pub spirv_cross_compatibility: bool,\n    /// Don't panic on missing bindings, instead generate invalid MSL.\n    pub fake_missing_bindings: bool,\n    /// Bounds checking policies.\n    pub bounds_check_policies: index::BoundsCheckPolicies,\n    /// Should workgroup variables be zero initialized (by polyfilling)?\n    pub zero_initialize_workgroup_memory: bool,\n    /// If set, loops will have code injected into them, forcing the compiler\n    /// to think the number of iterations is bounded.\n    pub force_loop_bounding: bool,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Options {\n            lang_version: (1, 0),\n            per_entry_point_map: EntryPointResourceMap::default(),\n            inline_samplers: Vec::new(),\n            spirv_cross_compatibility: false,\n            fake_missing_bindings: true,\n            bounds_check_policies: index::BoundsCheckPolicies::default(),\n            zero_initialize_workgroup_memory: true,\n            force_loop_bounding: true,\n        }\n    }\n}\n\n/// Corresponds to [WebGPU `GPUVertexFormat`](\n/// https://gpuweb.github.io/gpuweb/#enumdef-gpuvertexformat).\n#[repr(u32)]\n#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum VertexFormat {\n    /// One unsigned byte (u8). `u32` in shaders.\n    Uint8 = 0,\n    /// Two unsigned bytes (u8). `vec2<u32>` in shaders.\n    Uint8x2 = 1,\n    /// Four unsigned bytes (u8). `vec4<u32>` in shaders.\n    Uint8x4 = 2,\n    /// One signed byte (i8). `i32` in shaders.\n    Sint8 = 3,\n    /// Two signed bytes (i8). `vec2<i32>` in shaders.\n    Sint8x2 = 4,\n    /// Four signed bytes (i8). `vec4<i32>` in shaders.\n    Sint8x4 = 5,\n    /// One unsigned byte (u8). [0, 255] converted to float [0, 1] `f32` in shaders.\n    Unorm8 = 6,\n    /// Two unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec2<f32>` in shaders.\n    Unorm8x2 = 7,\n    /// Four unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec4<f32>` in shaders.\n    Unorm8x4 = 8,\n    /// One signed byte (i8). [-127, 127] converted to float [-1, 1] `f32` in shaders.\n    Snorm8 = 9,\n    /// Two signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec2<f32>` in shaders.\n    Snorm8x2 = 10,\n    /// Four signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec4<f32>` in shaders.\n    Snorm8x4 = 11,\n    /// One unsigned short (u16). `u32` in shaders.\n    Uint16 = 12,\n    /// Two unsigned shorts (u16). `vec2<u32>` in shaders.\n    Uint16x2 = 13,\n    /// Four unsigned shorts (u16). `vec4<u32>` in shaders.\n    Uint16x4 = 14,\n    /// One signed short (u16). `i32` in shaders.\n    Sint16 = 15,\n    /// Two signed shorts (i16). `vec2<i32>` in shaders.\n    Sint16x2 = 16,\n    /// Four signed shorts (i16). `vec4<i32>` in shaders.\n    Sint16x4 = 17,\n    /// One unsigned short (u16). [0, 65535] converted to float [0, 1] `f32` in shaders.\n    Unorm16 = 18,\n    /// Two unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec2<f32>` in shaders.\n    Unorm16x2 = 19,\n    /// Four unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec4<f32>` in shaders.\n    Unorm16x4 = 20,\n    /// One signed short (i16). [-32767, 32767] converted to float [-1, 1] `f32` in shaders.\n    Snorm16 = 21,\n    /// Two signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec2<f32>` in shaders.\n    Snorm16x2 = 22,\n    /// Four signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec4<f32>` in shaders.\n    Snorm16x4 = 23,\n    /// One half-precision float (no Rust equiv). `f32` in shaders.\n    Float16 = 24,\n    /// Two half-precision floats (no Rust equiv). `vec2<f32>` in shaders.\n    Float16x2 = 25,\n    /// Four half-precision floats (no Rust equiv). `vec4<f32>` in shaders.\n    Float16x4 = 26,\n    /// One single-precision float (f32). `f32` in shaders.\n    Float32 = 27,\n    /// Two single-precision floats (f32). `vec2<f32>` in shaders.\n    Float32x2 = 28,\n    /// Three single-precision floats (f32). `vec3<f32>` in shaders.\n    Float32x3 = 29,\n    /// Four single-precision floats (f32). `vec4<f32>` in shaders.\n    Float32x4 = 30,\n    /// One unsigned int (u32). `u32` in shaders.\n    Uint32 = 31,\n    /// Two unsigned ints (u32). `vec2<u32>` in shaders.\n    Uint32x2 = 32,\n    /// Three unsigned ints (u32). `vec3<u32>` in shaders.\n    Uint32x3 = 33,\n    /// Four unsigned ints (u32). `vec4<u32>` in shaders.\n    Uint32x4 = 34,\n    /// One signed int (i32). `i32` in shaders.\n    Sint32 = 35,\n    /// Two signed ints (i32). `vec2<i32>` in shaders.\n    Sint32x2 = 36,\n    /// Three signed ints (i32). `vec3<i32>` in shaders.\n    Sint32x3 = 37,\n    /// Four signed ints (i32). `vec4<i32>` in shaders.\n    Sint32x4 = 38,\n    /// Three unsigned 10-bit integers and one 2-bit integer, packed into a 32-bit integer (u32). [0, 1024] converted to float [0, 1] `vec4<f32>` in shaders.\n    #[cfg_attr(\n        any(feature = \"serialize\", feature = \"deserialize\"),\n        serde(rename = \"unorm10-10-10-2\")\n    )]\n    Unorm10_10_10_2 = 43,\n    /// Four unsigned 8-bit integers, packed into a 32-bit integer (u32). [0, 255] converted to float [0, 1] `vec4<f32>` in shaders.\n    #[cfg_attr(\n        any(feature = \"serialize\", feature = \"deserialize\"),\n        serde(rename = \"unorm8x4-bgra\")\n    )]\n    Unorm8x4Bgra = 44,\n}\n\n/// Defines how to advance the data in vertex buffers.\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum VertexBufferStepMode {\n    Constant,\n    #[default]\n    ByVertex,\n    ByInstance,\n}\n\n/// A mapping of vertex buffers and their attributes to shader\n/// locations.\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct AttributeMapping {\n    /// Shader location associated with this attribute\n    pub shader_location: u32,\n    /// Offset in bytes from start of vertex buffer structure\n    pub offset: u32,\n    /// Format code to help us unpack the attribute into the type\n    /// used by the shader. Codes correspond to a 0-based index of\n    /// <https://gpuweb.github.io/gpuweb/#enumdef-gpuvertexformat>.\n    /// The conversion process is described by\n    /// <https://gpuweb.github.io/gpuweb/#vertex-processing>.\n    pub format: VertexFormat,\n}\n\n/// A description of a vertex buffer with all the information we\n/// need to address the attributes within it.\n#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct VertexBufferMapping {\n    /// Shader location associated with this buffer\n    pub id: u32,\n    /// Size of the structure in bytes\n    pub stride: u32,\n    /// Vertex buffer step mode\n    pub step_mode: VertexBufferStepMode,\n    /// Vec of the attributes within the structure\n    pub attributes: Vec<AttributeMapping>,\n}\n\n/// A subset of options that are meant to be changed per pipeline.\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct PipelineOptions {\n    /// The entry point to write.\n    ///\n    /// Entry points are identified by a shader stage specification,\n    /// and a name.\n    ///\n    /// If `None`, all entry points will be written. If `Some` and the entry\n    /// point is not found, an error will be thrown while writing.\n    pub entry_point: Option<(ir::ShaderStage, String)>,\n\n    /// Allow `BuiltIn::PointSize` and inject it if doesn't exist.\n    ///\n    /// Metal doesn't like this for non-point primitive topologies and requires it for\n    /// point primitive topologies.\n    ///\n    /// Enable this for vertex shaders with point primitive topologies.\n    pub allow_and_force_point_size: bool,\n\n    /// If set, when generating the Metal vertex shader, transform it\n    /// to receive the vertex buffers, lengths, and vertex id as args,\n    /// and bounds-check the vertex id and use the index into the\n    /// vertex buffers to access attributes, rather than using Metal's\n    /// [[stage-in]] assembled attribute data. This is true by default,\n    /// but remains configurable for use by tests via deserialization\n    /// of this struct. There is no user-facing way to set this value.\n    pub vertex_pulling_transform: bool,\n\n    /// vertex_buffer_mappings are used during shader translation to\n    /// support vertex pulling.\n    pub vertex_buffer_mappings: Vec<VertexBufferMapping>,\n}\n\nimpl Options {\n    fn resolve_local_binding(\n        &self,\n        binding: &crate::Binding,\n        mode: LocationMode,\n    ) -> Result<ResolvedBinding, Error> {\n        match *binding {\n            crate::Binding::BuiltIn(mut built_in) => {\n                match built_in {\n                    crate::BuiltIn::Position { ref mut invariant } => {\n                        if *invariant && self.lang_version < (2, 1) {\n                            return Err(Error::UnsupportedAttribute(\"invariant\".to_string()));\n                        }\n\n                        // The 'invariant' attribute may only appear on vertex\n                        // shader outputs, not fragment shader inputs.\n                        if !matches!(mode, LocationMode::VertexOutput) {\n                            *invariant = false;\n                        }\n                    }\n                    crate::BuiltIn::BaseInstance if self.lang_version < (1, 2) => {\n                        return Err(Error::UnsupportedAttribute(\"base_instance\".to_string()));\n                    }\n                    crate::BuiltIn::InstanceIndex if self.lang_version < (1, 2) => {\n                        return Err(Error::UnsupportedAttribute(\"instance_id\".to_string()));\n                    }\n                    // macOS: Since Metal 2.2\n                    // iOS: Since Metal 2.3 (check depends on https://github.com/gfx-rs/wgpu/issues/4414)\n                    crate::BuiltIn::PrimitiveIndex if self.lang_version < (2, 3) => {\n                        return Err(Error::UnsupportedAttribute(\"primitive_id\".to_string()));\n                    }\n                    // macOS: since Metal 2.3\n                    // iOS: Since Metal 2.2\n                    // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf#page=114\n                    crate::BuiltIn::ViewIndex if self.lang_version < (2, 2) => {\n                        return Err(Error::UnsupportedAttribute(\"amplification_id\".to_string()));\n                    }\n                    // macOS: Since Metal 2.2\n                    // iOS: Since Metal 2.3 (check depends on https://github.com/gfx-rs/wgpu/issues/4414)\n                    crate::BuiltIn::Barycentric { .. } if self.lang_version < (2, 3) => {\n                        return Err(Error::UnsupportedAttribute(\"barycentric_coord\".to_string()));\n                    }\n                    _ => {}\n                }\n\n                Ok(ResolvedBinding::BuiltIn(built_in))\n            }\n            crate::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src,\n                per_primitive: _,\n            } => match mode {\n                LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)),\n                LocationMode::FragmentOutput => {\n                    if blend_src.is_some() && self.lang_version < (1, 2) {\n                        return Err(Error::UnsupportedAttribute(\"blend_src\".to_string()));\n                    }\n                    Ok(ResolvedBinding::Color {\n                        location,\n                        blend_src,\n                    })\n                }\n                LocationMode::VertexOutput | LocationMode::FragmentInput => {\n                    Ok(ResolvedBinding::User {\n                        prefix: if self.spirv_cross_compatibility {\n                            \"locn\"\n                        } else {\n                            \"loc\"\n                        },\n                        index: location,\n                        interpolation: {\n                            // unwrap: The verifier ensures that vertex shader outputs and fragment\n                            // shader inputs always have fully specified interpolation, and that\n                            // sampling is `None` only for Flat interpolation.\n                            let interpolation = interpolation.unwrap();\n                            let sampling = sampling.unwrap_or(crate::Sampling::Center);\n                            Some(ResolvedInterpolation::from_binding(interpolation, sampling))\n                        },\n                    })\n                }\n                LocationMode::Uniform => Err(Error::GenericValidation(format!(\n                    \"Unexpected Binding::Location({location}) for the Uniform mode\"\n                ))),\n            },\n        }\n    }\n\n    fn get_entry_point_resources(&self, ep: &crate::EntryPoint) -> Option<&EntryPointResources> {\n        self.per_entry_point_map.get(&ep.name)\n    }\n\n    fn get_resource_binding_target(\n        &self,\n        ep: &crate::EntryPoint,\n        res_binding: &crate::ResourceBinding,\n    ) -> Option<&BindTarget> {\n        self.get_entry_point_resources(ep)\n            .and_then(|res| res.resources.get(res_binding))\n    }\n\n    fn resolve_resource_binding(\n        &self,\n        ep: &crate::EntryPoint,\n        res_binding: &crate::ResourceBinding,\n    ) -> Result<ResolvedBinding, EntryPointError> {\n        let target = self.get_resource_binding_target(ep, res_binding);\n        match target {\n            Some(target) => Ok(ResolvedBinding::Resource(target.clone())),\n            None if self.fake_missing_bindings => Ok(ResolvedBinding::User {\n                prefix: \"fake\",\n                index: 0,\n                interpolation: None,\n            }),\n            None => Err(EntryPointError::MissingBindTarget(*res_binding)),\n        }\n    }\n\n    fn resolve_immediates(\n        &self,\n        ep: &crate::EntryPoint,\n    ) -> Result<ResolvedBinding, EntryPointError> {\n        let slot = self\n            .get_entry_point_resources(ep)\n            .and_then(|res| res.immediates_buffer);\n        match slot {\n            Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {\n                buffer: Some(slot),\n                ..Default::default()\n            })),\n            None if self.fake_missing_bindings => Ok(ResolvedBinding::User {\n                prefix: \"fake\",\n                index: 0,\n                interpolation: None,\n            }),\n            None => Err(EntryPointError::MissingImmediateData),\n        }\n    }\n\n    fn resolve_sizes_buffer(\n        &self,\n        ep: &crate::EntryPoint,\n    ) -> Result<ResolvedBinding, EntryPointError> {\n        let slot = self\n            .get_entry_point_resources(ep)\n            .and_then(|res| res.sizes_buffer);\n        match slot {\n            Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {\n                buffer: Some(slot),\n                ..Default::default()\n            })),\n            None if self.fake_missing_bindings => Ok(ResolvedBinding::User {\n                prefix: \"fake\",\n                index: 0,\n                interpolation: None,\n            }),\n            None => Err(EntryPointError::MissingSizesBuffer),\n        }\n    }\n}\n\nimpl ResolvedBinding {\n    fn as_inline_sampler<'a>(&self, options: &'a Options) -> Option<&'a sampler::InlineSampler> {\n        match *self {\n            Self::Resource(BindTarget {\n                sampler: Some(BindSamplerTarget::Inline(index)),\n                ..\n            }) => Some(&options.inline_samplers[index as usize]),\n            _ => None,\n        }\n    }\n\n    fn try_fmt<W: Write>(&self, out: &mut W) -> Result<(), Error> {\n        write!(out, \" [[\")?;\n        match *self {\n            Self::BuiltIn(built_in) => {\n                use crate::BuiltIn as Bi;\n                let name = match built_in {\n                    Bi::Position { invariant: false } => \"position\",\n                    Bi::Position { invariant: true } => \"position, invariant\",\n                    Bi::ViewIndex => \"amplification_id\",\n                    // vertex\n                    Bi::BaseInstance => \"base_instance\",\n                    Bi::BaseVertex => \"base_vertex\",\n                    Bi::ClipDistances => \"clip_distance\",\n                    Bi::InstanceIndex => \"instance_id\",\n                    Bi::PointSize => \"point_size\",\n                    Bi::VertexIndex => \"vertex_id\",\n                    // fragment\n                    Bi::FragDepth => \"depth(any)\",\n                    Bi::PointCoord => \"point_coord\",\n                    Bi::FrontFacing => \"front_facing\",\n                    Bi::PrimitiveIndex => \"primitive_id\",\n                    Bi::Barycentric { perspective: true } => \"barycentric_coord\",\n                    Bi::Barycentric { perspective: false } => {\n                        \"barycentric_coord, center_no_perspective\"\n                    }\n                    Bi::SampleIndex => \"sample_id\",\n                    Bi::SampleMask => \"sample_mask\",\n                    // compute\n                    Bi::GlobalInvocationId => \"thread_position_in_grid\",\n                    Bi::LocalInvocationId => \"thread_position_in_threadgroup\",\n                    Bi::LocalInvocationIndex => \"thread_index_in_threadgroup\",\n                    Bi::WorkGroupId => \"threadgroup_position_in_grid\",\n                    Bi::WorkGroupSize => \"dispatch_threads_per_threadgroup\",\n                    Bi::NumWorkGroups => \"threadgroups_per_grid\",\n                    // subgroup\n                    Bi::NumSubgroups => \"simdgroups_per_threadgroup\",\n                    Bi::SubgroupId => \"simdgroup_index_in_threadgroup\",\n                    Bi::SubgroupSize => \"threads_per_simdgroup\",\n                    Bi::SubgroupInvocationId => \"thread_index_in_simdgroup\",\n                    Bi::CullDistance | Bi::DrawIndex => {\n                        return Err(Error::UnsupportedBuiltIn(built_in))\n                    }\n                    Bi::CullPrimitive => \"primitive_culled\",\n                    // TODO: figure out how to make this written as a function call\n                    Bi::PointIndex | Bi::LineIndices | Bi::TriangleIndices => unimplemented!(),\n                    Bi::MeshTaskSize\n                    | Bi::VertexCount\n                    | Bi::PrimitiveCount\n                    | Bi::Vertices\n                    | Bi::Primitives\n                    | Bi::RayInvocationId\n                    | Bi::NumRayInvocations\n                    | Bi::InstanceCustomData\n                    | Bi::GeometryIndex\n                    | Bi::WorldRayOrigin\n                    | Bi::WorldRayDirection\n                    | Bi::ObjectRayOrigin\n                    | Bi::ObjectRayDirection\n                    | Bi::RayTmin\n                    | Bi::RayTCurrentMax\n                    | Bi::ObjectToWorld\n                    | Bi::WorldToObject\n                    | Bi::HitKind => unreachable!(),\n                };\n                write!(out, \"{name}\")?;\n            }\n            Self::Attribute(index) => write!(out, \"attribute({index})\")?,\n            Self::Color {\n                location,\n                blend_src,\n            } => {\n                if let Some(blend_src) = blend_src {\n                    write!(out, \"color({location}) index({blend_src})\")?\n                } else {\n                    write!(out, \"color({location})\")?\n                }\n            }\n            Self::User {\n                prefix,\n                index,\n                interpolation,\n            } => {\n                write!(out, \"user({prefix}{index})\")?;\n                if let Some(interpolation) = interpolation {\n                    write!(out, \", \")?;\n                    interpolation.try_fmt(out)?;\n                }\n            }\n            Self::Resource(ref target) => {\n                if let Some(id) = target.buffer {\n                    write!(out, \"buffer({id})\")?;\n                } else if let Some(id) = target.texture {\n                    write!(out, \"texture({id})\")?;\n                } else if let Some(BindSamplerTarget::Resource(id)) = target.sampler {\n                    write!(out, \"sampler({id})\")?;\n                } else {\n                    return Err(Error::UnimplementedBindTarget(target.clone()));\n                }\n            }\n        }\n        write!(out, \"]]\")?;\n        Ok(())\n    }\n}\n\nimpl ResolvedInterpolation {\n    const fn from_binding(interpolation: crate::Interpolation, sampling: crate::Sampling) -> Self {\n        use crate::Interpolation as I;\n        use crate::Sampling as S;\n\n        match (interpolation, sampling) {\n            (I::Perspective, S::Center) => Self::CenterPerspective,\n            (I::Perspective, S::Centroid) => Self::CentroidPerspective,\n            (I::Perspective, S::Sample) => Self::SamplePerspective,\n            (I::Linear, S::Center) => Self::CenterNoPerspective,\n            (I::Linear, S::Centroid) => Self::CentroidNoPerspective,\n            (I::Linear, S::Sample) => Self::SampleNoPerspective,\n            (I::Flat, _) => Self::Flat,\n            _ => unreachable!(),\n        }\n    }\n\n    fn try_fmt<W: Write>(self, out: &mut W) -> Result<(), Error> {\n        let identifier = match self {\n            Self::CenterPerspective => \"center_perspective\",\n            Self::CenterNoPerspective => \"center_no_perspective\",\n            Self::CentroidPerspective => \"centroid_perspective\",\n            Self::CentroidNoPerspective => \"centroid_no_perspective\",\n            Self::SamplePerspective => \"sample_perspective\",\n            Self::SampleNoPerspective => \"sample_no_perspective\",\n            Self::Flat => \"flat\",\n        };\n        out.write_str(identifier)?;\n        Ok(())\n    }\n}\n\n/// Information about a translated module that is required\n/// for the use of the result.\npub struct TranslationInfo {\n    /// Mapping of the entry point names. Each item in the array\n    /// corresponds to an entry point index.\n    ///\n    ///Note: Some entry points may fail translation because of missing bindings.\n    pub entry_point_names: Vec<Result<String, EntryPointError>>,\n}\n\npub fn write_string(\n    module: &crate::Module,\n    info: &ModuleInfo,\n    options: &Options,\n    pipeline_options: &PipelineOptions,\n) -> Result<(String, TranslationInfo), Error> {\n    let mut w = Writer::new(String::new());\n    let info = w.write(module, info, options, pipeline_options)?;\n    Ok((w.finish(), info))\n}\n\npub fn supported_capabilities() -> crate::valid::Capabilities {\n    use crate::valid::Capabilities as Caps;\n    Caps::IMMEDIATES\n        // No FLOAT64\n        | Caps::PRIMITIVE_INDEX\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY\n        // No BUFFER_BINDING_ARRAY\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY\n        | Caps::STORAGE_BUFFER_BINDING_ARRAY\n        | Caps::CLIP_DISTANCES // CLIP_DISTANCES isn't supported by metal backend? But is supported by MSL writer\n        // No CULL_DISTANCE\n        | Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS\n        | Caps::MULTIVIEW\n        // No EARLY_DEPTH_TEST\n        | Caps::MULTISAMPLED_SHADING\n        | Caps::RAY_QUERY\n        | Caps::DUAL_SOURCE_BLENDING\n        | Caps::CUBE_ARRAY_TEXTURES\n        | Caps::SHADER_INT64\n        | Caps::SUBGROUP\n        | Caps::SUBGROUP_BARRIER\n        // No SUBGROUP_VERTEX_STAGE\n        | Caps::SHADER_INT64_ATOMIC_MIN_MAX\n        // No SHADER_INT64_ATOMIC_ALL_OPS\n        | Caps::SHADER_FLOAT32_ATOMIC\n        | Caps::TEXTURE_ATOMIC\n        | Caps::TEXTURE_INT64_ATOMIC\n        // No RAY_HIT_VERTEX_POSITION\n        | Caps::SHADER_FLOAT16\n        | Caps::TEXTURE_EXTERNAL\n        | Caps::SHADER_FLOAT16_IN_FLOAT32\n        | Caps::SHADER_BARYCENTRICS\n        // No MESH_SHADER\n        // No MESH_SHADER_POINT_TOPOLOGY\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        // No BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::COOPERATIVE_MATRIX\n        // No PER_VERTEX\n        // No RAY_TRACING_PIPELINE\n        // No DRAW_INDEX\n        // No MEMORY_DECORATION_VOLATILE\n        | Caps::MEMORY_DECORATION_COHERENT\n}\n\n#[test]\nfn test_error_size() {\n    assert_eq!(size_of::<Error>(), 40);\n}\n"
  },
  {
    "path": "naga/src/back/msl/sampler.rs",
    "content": "use core::{num::NonZeroU32, ops::Range};\n\n#[cfg(feature = \"deserialize\")]\nuse serde::Deserialize;\n#[cfg(feature = \"serialize\")]\nuse serde::Serialize;\n\n#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub enum Coord {\n    #[default]\n    Normalized,\n    Pixel,\n}\n\nimpl Coord {\n    pub const fn as_str(&self) -> &'static str {\n        match *self {\n            Self::Normalized => \"normalized\",\n            Self::Pixel => \"pixel\",\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub enum Address {\n    Repeat,\n    MirroredRepeat,\n    #[default]\n    ClampToEdge,\n    ClampToZero,\n    ClampToBorder,\n}\n\nimpl Address {\n    pub const fn as_str(&self) -> &'static str {\n        match *self {\n            Self::Repeat => \"repeat\",\n            Self::MirroredRepeat => \"mirrored_repeat\",\n            Self::ClampToEdge => \"clamp_to_edge\",\n            Self::ClampToZero => \"clamp_to_zero\",\n            Self::ClampToBorder => \"clamp_to_border\",\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub enum BorderColor {\n    #[default]\n    TransparentBlack,\n    OpaqueBlack,\n    OpaqueWhite,\n}\n\nimpl BorderColor {\n    pub const fn as_str(&self) -> &'static str {\n        match *self {\n            Self::TransparentBlack => \"transparent_black\",\n            Self::OpaqueBlack => \"opaque_black\",\n            Self::OpaqueWhite => \"opaque_white\",\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub enum Filter {\n    #[default]\n    Nearest,\n    Linear,\n}\n\nimpl Filter {\n    pub const fn as_str(&self) -> &'static str {\n        match *self {\n            Self::Nearest => \"nearest\",\n            Self::Linear => \"linear\",\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub enum CompareFunc {\n    #[default]\n    Never,\n    Less,\n    LessEqual,\n    Greater,\n    GreaterEqual,\n    Equal,\n    NotEqual,\n    Always,\n}\n\nimpl CompareFunc {\n    pub const fn as_str(&self) -> &'static str {\n        match *self {\n            Self::Never => \"never\",\n            Self::Less => \"less\",\n            Self::LessEqual => \"less_equal\",\n            Self::Greater => \"greater\",\n            Self::GreaterEqual => \"greater_equal\",\n            Self::Equal => \"equal\",\n            Self::NotEqual => \"not_equal\",\n            Self::Always => \"always\",\n        }\n    }\n}\n\n#[derive(Clone, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\npub struct InlineSampler {\n    pub coord: Coord,\n    pub address: [Address; 3],\n    pub border_color: BorderColor,\n    pub mag_filter: Filter,\n    pub min_filter: Filter,\n    pub mip_filter: Option<Filter>,\n    pub lod_clamp: Option<Range<f32>>,\n    pub max_anisotropy: Option<NonZeroU32>,\n    pub compare_func: CompareFunc,\n}\n\nimpl Eq for InlineSampler {}\n\nimpl core::hash::Hash for InlineSampler {\n    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {\n        self.coord.hash(hasher);\n        self.address.hash(hasher);\n        self.border_color.hash(hasher);\n        self.mag_filter.hash(hasher);\n        self.min_filter.hash(hasher);\n        self.mip_filter.hash(hasher);\n        self.lod_clamp\n            .as_ref()\n            .map(|range| (range.start.to_bits(), range.end.to_bits()))\n            .hash(hasher);\n        self.max_anisotropy.hash(hasher);\n        self.compare_func.hash(hasher);\n    }\n}\n"
  },
  {
    "path": "naga/src/back/msl/writer.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::{\n    cmp::Ordering,\n    fmt::{Display, Error as FmtError, Formatter, Write},\n    iter,\n};\nuse num_traits::real::Real as _;\n\nuse half::f16;\n\nuse super::{sampler as sm, Error, LocationMode, Options, PipelineOptions, TranslationInfo};\nuse crate::{\n    arena::{Handle, HandleSet},\n    back::{self, get_entry_points, Baked},\n    common,\n    proc::{\n        self, concrete_int_scalars,\n        index::{self, BoundsCheck},\n        ExternalTextureNameKey, NameKey, TypeResolution,\n    },\n    valid, FastHashMap, FastHashSet,\n};\n\n#[cfg(test)]\nuse core::ptr;\n\n/// Shorthand result used internally by the backend\ntype BackendResult = Result<(), Error>;\n\nconst NAMESPACE: &str = \"metal\";\n// The name of the array member of the Metal struct types we generate to\n// represent Naga `Array` types. See the comments in `Writer::write_type_defs`\n// for details.\nconst WRAPPED_ARRAY_FIELD: &str = \"inner\";\n// This is a hack: we need to pass a pointer to an atomic,\n// but generally the backend isn't putting \"&\" in front of every pointer.\n// Some more general handling of pointers is needed to be implemented here.\nconst ATOMIC_REFERENCE: &str = \"&\";\n\nconst RT_NAMESPACE: &str = \"metal::raytracing\";\nconst RAY_QUERY_TYPE: &str = \"_RayQuery\";\nconst RAY_QUERY_FIELD_INTERSECTOR: &str = \"intersector\";\nconst RAY_QUERY_FIELD_INTERSECTION: &str = \"intersection\";\nconst RAY_QUERY_MODERN_SUPPORT: bool = false; //TODO\nconst RAY_QUERY_FIELD_READY: &str = \"ready\";\nconst RAY_QUERY_FUN_MAP_INTERSECTION: &str = \"_map_intersection_type\";\n\npub(crate) const ATOMIC_COMP_EXCH_FUNCTION: &str = \"naga_atomic_compare_exchange_weak_explicit\";\npub(crate) const MODF_FUNCTION: &str = \"naga_modf\";\npub(crate) const FREXP_FUNCTION: &str = \"naga_frexp\";\npub(crate) const ABS_FUNCTION: &str = \"naga_abs\";\npub(crate) const DIV_FUNCTION: &str = \"naga_div\";\npub(crate) const DOT_FUNCTION_PREFIX: &str = \"naga_dot\";\npub(crate) const MOD_FUNCTION: &str = \"naga_mod\";\npub(crate) const NEG_FUNCTION: &str = \"naga_neg\";\npub(crate) const F2I32_FUNCTION: &str = \"naga_f2i32\";\npub(crate) const F2U32_FUNCTION: &str = \"naga_f2u32\";\npub(crate) const F2I64_FUNCTION: &str = \"naga_f2i64\";\npub(crate) const F2U64_FUNCTION: &str = \"naga_f2u64\";\npub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = \"nagaTextureLoadExternal\";\npub(crate) const IMAGE_SIZE_EXTERNAL_FUNCTION: &str = \"nagaTextureDimensionsExternal\";\npub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str =\n    \"nagaTextureSampleBaseClampToEdge\";\n/// For some reason, Metal does not let you have `metal::texture<..>*` as a buffer argument.\n/// However, if you put that texture inside a struct, everything is totally fine. This\n/// baffles me to no end.\n///\n/// As such, we wrap all argument buffers in a struct that has a single generic `<T>` field.\n/// This allows `NagaArgumentBufferWrapper<metal::texture<..>>*` to work. The astute among\n/// you have noticed that this should be exactly the same to the compiler, and you're correct.\npub(crate) const ARGUMENT_BUFFER_WRAPPER_STRUCT: &str = \"NagaArgumentBufferWrapper\";\n/// Name of the struct that is declared to wrap the 3 textures and parameters\n/// buffer that [`crate::ImageClass::External`] variables are lowered to,\n/// allowing them to be conveniently passed to user-defined or wrapper\n/// functions. The struct is declared in [`Writer::write_type_defs`].\npub(crate) const EXTERNAL_TEXTURE_WRAPPER_STRUCT: &str = \"NagaExternalTextureWrapper\";\npub(crate) const COOPERATIVE_LOAD_FUNCTION: &str = \"NagaCooperativeLoad\";\npub(crate) const COOPERATIVE_MULTIPLY_ADD_FUNCTION: &str = \"NagaCooperativeMultiplyAdd\";\n\n/// Write the Metal name for a Naga numeric type: scalar, vector, or matrix.\n///\n/// The `sizes` slice determines whether this function writes a\n/// scalar, vector, or matrix type:\n///\n/// - An empty slice produces a scalar type.\n/// - A one-element slice produces a vector type.\n/// - A two element slice `[ROWS COLUMNS]` produces a matrix of the given size.\nfn put_numeric_type(\n    out: &mut impl Write,\n    scalar: crate::Scalar,\n    sizes: &[crate::VectorSize],\n) -> Result<(), FmtError> {\n    match (scalar, sizes) {\n        (scalar, &[]) => {\n            write!(out, \"{}\", scalar.to_msl_name())\n        }\n        (scalar, &[rows]) => {\n            write!(\n                out,\n                \"{}::{}{}\",\n                NAMESPACE,\n                scalar.to_msl_name(),\n                common::vector_size_str(rows)\n            )\n        }\n        (scalar, &[rows, columns]) => {\n            write!(\n                out,\n                \"{}::{}{}x{}\",\n                NAMESPACE,\n                scalar.to_msl_name(),\n                common::vector_size_str(columns),\n                common::vector_size_str(rows)\n            )\n        }\n        (_, _) => Ok(()), // not meaningful\n    }\n}\n\nconst fn scalar_is_int(scalar: crate::Scalar) -> bool {\n    use crate::ScalarKind::*;\n    match scalar.kind {\n        Sint | Uint | AbstractInt | Bool => true,\n        Float | AbstractFloat => false,\n    }\n}\n\n/// Prefix for cached clamped level-of-detail values for `ImageLoad` expressions.\nconst CLAMPED_LOD_LOAD_PREFIX: &str = \"clamped_lod_e\";\n\n/// Prefix for reinterpreted expressions using `as_type<T>(...)`.\nconst REINTERPRET_PREFIX: &str = \"reinterpreted_\";\n\n/// Wrapper for identifier names for clamped level-of-detail values\n///\n/// Values of this type implement [`core::fmt::Display`], formatting as\n/// the name of the variable used to hold the cached clamped\n/// level-of-detail value for an `ImageLoad` expression.\nstruct ClampedLod(Handle<crate::Expression>);\n\nimpl Display for ClampedLod {\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, CLAMPED_LOD_LOAD_PREFIX)\n    }\n}\n\n/// Wrapper for generating `struct _mslBufferSizes` member names for\n/// runtime-sized array lengths.\n///\n/// On Metal, `wgpu_hal` passes the element counts for all runtime-sized arrays\n/// as an argument to the entry point. This argument's type in the MSL is\n/// `struct _mslBufferSizes`, a Naga-synthesized struct with a `uint` member for\n/// each global variable containing a runtime-sized array.\n///\n/// If `global` is a [`Handle`] for a [`GlobalVariable`] that contains a\n/// runtime-sized array, then the value `ArraySize(global)` implements\n/// [`core::fmt::Display`], formatting as the name of the struct member carrying\n/// the number of elements in that runtime-sized array.\n///\n/// [`GlobalVariable`]: crate::GlobalVariable\nstruct ArraySizeMember(Handle<crate::GlobalVariable>);\n\nimpl Display for ArraySizeMember {\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        self.0.write_prefixed(f, \"size\")\n    }\n}\n\n/// Wrapper for reinterpreted variables using `as_type<target_type>(orig)`.\n///\n/// Implements [`core::fmt::Display`], formatting as a name derived from\n/// `target_type` and the variable name of `orig`.\n#[derive(Clone, Copy)]\nstruct Reinterpreted<'a> {\n    target_type: &'a str,\n    orig: Handle<crate::Expression>,\n}\n\nimpl<'a> Reinterpreted<'a> {\n    const fn new(target_type: &'a str, orig: Handle<crate::Expression>) -> Self {\n        Self { target_type, orig }\n    }\n}\n\nimpl Display for Reinterpreted<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        f.write_str(REINTERPRET_PREFIX)?;\n        f.write_str(self.target_type)?;\n        self.orig.write_prefixed(f, \"_e\")\n    }\n}\n\nstruct TypeContext<'a> {\n    handle: Handle<crate::Type>,\n    gctx: proc::GlobalCtx<'a>,\n    names: &'a FastHashMap<NameKey, String>,\n    access: crate::StorageAccess,\n    first_time: bool,\n}\n\nimpl TypeContext<'_> {\n    fn scalar(&self) -> Option<crate::Scalar> {\n        let ty = &self.gctx.types[self.handle];\n        ty.inner.scalar()\n    }\n\n    fn vector_size(&self) -> Option<crate::VectorSize> {\n        let ty = &self.gctx.types[self.handle];\n        match ty.inner {\n            crate::TypeInner::Vector { size, .. } => Some(size),\n            _ => None,\n        }\n    }\n}\n\nimpl Display for TypeContext<'_> {\n    fn fmt(&self, out: &mut Formatter<'_>) -> Result<(), FmtError> {\n        let ty = &self.gctx.types[self.handle];\n        if ty.needs_alias() && !self.first_time {\n            let name = &self.names[&NameKey::Type(self.handle)];\n            return write!(out, \"{name}\");\n        }\n\n        match ty.inner {\n            crate::TypeInner::Scalar(scalar) => put_numeric_type(out, scalar, &[]),\n            crate::TypeInner::Atomic(scalar) => {\n                write!(out, \"{}::atomic_{}\", NAMESPACE, scalar.to_msl_name())\n            }\n            crate::TypeInner::Vector { size, scalar } => put_numeric_type(out, scalar, &[size]),\n            crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => put_numeric_type(out, scalar, &[rows, columns]),\n            // Requires Metal-2.3\n            crate::TypeInner::CooperativeMatrix {\n                columns,\n                rows,\n                scalar,\n                role: _,\n            } => {\n                write!(\n                    out,\n                    \"{NAMESPACE}::simdgroup_{}{}x{}\",\n                    scalar.to_msl_name(),\n                    columns as u32,\n                    rows as u32,\n                )\n            }\n            crate::TypeInner::Pointer { base, space } => {\n                let sub = Self {\n                    handle: base,\n                    first_time: false,\n                    ..*self\n                };\n                let space_name = match space.to_msl_name() {\n                    Some(name) => name,\n                    None => return Ok(()),\n                };\n                write!(out, \"{space_name} {sub}&\")\n            }\n            crate::TypeInner::ValuePointer {\n                size,\n                scalar,\n                space,\n            } => {\n                match space.to_msl_name() {\n                    Some(name) => write!(out, \"{name} \")?,\n                    None => return Ok(()),\n                };\n                match size {\n                    Some(rows) => put_numeric_type(out, scalar, &[rows])?,\n                    None => put_numeric_type(out, scalar, &[])?,\n                };\n\n                write!(out, \"&\")\n            }\n            crate::TypeInner::Array { base, .. } => {\n                let sub = Self {\n                    handle: base,\n                    first_time: false,\n                    ..*self\n                };\n                // Array lengths go at the end of the type definition,\n                // so just print the element type here.\n                write!(out, \"{sub}\")\n            }\n            crate::TypeInner::Struct { .. } => unreachable!(),\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                let dim_str = match dim {\n                    crate::ImageDimension::D1 => \"1d\",\n                    crate::ImageDimension::D2 => \"2d\",\n                    crate::ImageDimension::D3 => \"3d\",\n                    crate::ImageDimension::Cube => \"cube\",\n                };\n                let (texture_str, msaa_str, scalar, access) = match class {\n                    crate::ImageClass::Sampled { kind, multi } => {\n                        let (msaa_str, access) = if multi {\n                            (\"_ms\", \"read\")\n                        } else {\n                            (\"\", \"sample\")\n                        };\n                        let scalar = crate::Scalar { kind, width: 4 };\n                        (\"texture\", msaa_str, scalar, access)\n                    }\n                    crate::ImageClass::Depth { multi } => {\n                        let (msaa_str, access) = if multi {\n                            (\"_ms\", \"read\")\n                        } else {\n                            (\"\", \"sample\")\n                        };\n                        let scalar = crate::Scalar {\n                            kind: crate::ScalarKind::Float,\n                            width: 4,\n                        };\n                        (\"depth\", msaa_str, scalar, access)\n                    }\n                    crate::ImageClass::Storage { format, .. } => {\n                        let access = if self\n                            .access\n                            .contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)\n                        {\n                            \"read_write\"\n                        } else if self.access.contains(crate::StorageAccess::STORE) {\n                            \"write\"\n                        } else if self.access.contains(crate::StorageAccess::LOAD) {\n                            \"read\"\n                        } else {\n                            log::warn!(\n                                \"Storage access for {:?} (name '{}'): {:?}\",\n                                self.handle,\n                                ty.name.as_deref().unwrap_or_default(),\n                                self.access\n                            );\n                            unreachable!(\"module is not valid\");\n                        };\n                        (\"texture\", \"\", format.into(), access)\n                    }\n                    crate::ImageClass::External => {\n                        return write!(out, \"{EXTERNAL_TEXTURE_WRAPPER_STRUCT}\");\n                    }\n                };\n                let base_name = scalar.to_msl_name();\n                let array_str = if arrayed { \"_array\" } else { \"\" };\n                write!(\n                    out,\n                    \"{NAMESPACE}::{texture_str}{dim_str}{msaa_str}{array_str}<{base_name}, {NAMESPACE}::access::{access}>\",\n                )\n            }\n            crate::TypeInner::Sampler { comparison: _ } => {\n                write!(out, \"{NAMESPACE}::sampler\")\n            }\n            crate::TypeInner::AccelerationStructure { vertex_return } => {\n                if vertex_return {\n                    unimplemented!(\"metal does not support vertex ray hit return\")\n                }\n                write!(out, \"{RT_NAMESPACE}::instance_acceleration_structure\")\n            }\n            crate::TypeInner::RayQuery { vertex_return } => {\n                if vertex_return {\n                    unimplemented!(\"metal does not support vertex ray hit return\")\n                }\n                write!(out, \"{RAY_QUERY_TYPE}\")\n            }\n            crate::TypeInner::BindingArray { base, .. } => {\n                let base_tyname = Self {\n                    handle: base,\n                    first_time: false,\n                    ..*self\n                };\n\n                write!(\n                    out,\n                    \"constant {ARGUMENT_BUFFER_WRAPPER_STRUCT}<{base_tyname}>*\"\n                )\n            }\n        }\n    }\n}\n\nstruct TypedGlobalVariable<'a> {\n    module: &'a crate::Module,\n    names: &'a FastHashMap<NameKey, String>,\n    handle: Handle<crate::GlobalVariable>,\n    usage: valid::GlobalUse,\n    reference: bool,\n}\n\nimpl TypedGlobalVariable<'_> {\n    fn try_fmt<W: Write>(&self, out: &mut W) -> BackendResult {\n        let var = &self.module.global_variables[self.handle];\n        let name = &self.names[&NameKey::GlobalVariable(self.handle)];\n\n        let storage_access = match var.space {\n            crate::AddressSpace::Storage { access } => access,\n            _ => match self.module.types[var.ty].inner {\n                crate::TypeInner::Image {\n                    class: crate::ImageClass::Storage { access, .. },\n                    ..\n                } => access,\n                crate::TypeInner::BindingArray { base, .. } => {\n                    match self.module.types[base].inner {\n                        crate::TypeInner::Image {\n                            class: crate::ImageClass::Storage { access, .. },\n                            ..\n                        } => access,\n                        _ => crate::StorageAccess::default(),\n                    }\n                }\n                _ => crate::StorageAccess::default(),\n            },\n        };\n        let ty_name = TypeContext {\n            handle: var.ty,\n            gctx: self.module.to_ctx(),\n            names: self.names,\n            access: storage_access,\n            first_time: false,\n        };\n\n        let (coherent, space, access, reference) = match var.space.to_msl_name() {\n            Some(space) if self.reference => {\n                let coherent = if var\n                    .memory_decorations\n                    .contains(crate::MemoryDecorations::COHERENT)\n                {\n                    \"coherent \"\n                } else {\n                    \"\"\n                };\n                let access = if var.space.needs_access_qualifier()\n                    && !self.usage.intersects(valid::GlobalUse::WRITE)\n                {\n                    \"const\"\n                } else {\n                    \"\"\n                };\n                (coherent, space, access, \"&\")\n            }\n            _ => (\"\", \"\", \"\", \"\"),\n        };\n\n        Ok(write!(\n            out,\n            \"{}{}{}{}{}{}{} {}\",\n            coherent,\n            space,\n            if space.is_empty() { \"\" } else { \" \" },\n            ty_name,\n            if access.is_empty() { \"\" } else { \" \" },\n            access,\n            reference,\n            name,\n        )?)\n    }\n}\n\n#[derive(Eq, PartialEq, Hash)]\nenum WrappedFunction {\n    UnaryOp {\n        op: crate::UnaryOperator,\n        ty: (Option<crate::VectorSize>, crate::Scalar),\n    },\n    BinaryOp {\n        op: crate::BinaryOperator,\n        left_ty: (Option<crate::VectorSize>, crate::Scalar),\n        right_ty: (Option<crate::VectorSize>, crate::Scalar),\n    },\n    Math {\n        fun: crate::MathFunction,\n        arg_ty: (Option<crate::VectorSize>, crate::Scalar),\n    },\n    Cast {\n        src_scalar: crate::Scalar,\n        vector_size: Option<crate::VectorSize>,\n        dst_scalar: crate::Scalar,\n    },\n    ImageLoad {\n        class: crate::ImageClass,\n    },\n    ImageSample {\n        class: crate::ImageClass,\n        clamp_to_edge: bool,\n    },\n    ImageQuerySize {\n        class: crate::ImageClass,\n    },\n    CooperativeLoad {\n        space_name: &'static str,\n        columns: crate::CooperativeSize,\n        rows: crate::CooperativeSize,\n        scalar: crate::Scalar,\n    },\n    CooperativeMultiplyAdd {\n        space_name: &'static str,\n        columns: crate::CooperativeSize,\n        rows: crate::CooperativeSize,\n        intermediate: crate::CooperativeSize,\n        scalar: crate::Scalar,\n    },\n}\n\npub struct Writer<W> {\n    out: W,\n    names: FastHashMap<NameKey, String>,\n    named_expressions: crate::NamedExpressions,\n    /// Set of expressions that need to be baked to avoid unnecessary repetition in output\n    need_bake_expressions: back::NeedBakeExpressions,\n    namer: proc::Namer,\n    wrapped_functions: FastHashSet<WrappedFunction>,\n    #[cfg(test)]\n    put_expression_stack_pointers: FastHashSet<*const ()>,\n    #[cfg(test)]\n    put_block_stack_pointers: FastHashSet<*const ()>,\n    /// Set of (struct type, struct field index) denoting which fields require\n    /// padding inserted **before** them (i.e. between fields at index - 1 and index)\n    struct_member_pads: FastHashSet<(Handle<crate::Type>, u32)>,\n}\n\nimpl crate::Scalar {\n    pub(super) fn to_msl_name(self) -> &'static str {\n        use crate::ScalarKind as Sk;\n        match self {\n            Self {\n                kind: Sk::Float,\n                width: 4,\n            } => \"float\",\n            Self {\n                kind: Sk::Float,\n                width: 2,\n            } => \"half\",\n            Self {\n                kind: Sk::Sint,\n                width: 4,\n            } => \"int\",\n            Self {\n                kind: Sk::Uint,\n                width: 4,\n            } => \"uint\",\n            Self {\n                kind: Sk::Sint,\n                width: 8,\n            } => \"long\",\n            Self {\n                kind: Sk::Uint,\n                width: 8,\n            } => \"ulong\",\n            Self {\n                kind: Sk::Bool,\n                width: _,\n            } => \"bool\",\n            Self {\n                kind: Sk::AbstractInt | Sk::AbstractFloat,\n                width: _,\n            } => unreachable!(\"Found Abstract scalar kind\"),\n            _ => unreachable!(\"Unsupported scalar kind: {:?}\", self),\n        }\n    }\n}\n\nconst fn separate(need_separator: bool) -> &'static str {\n    if need_separator {\n        \",\"\n    } else {\n        \"\"\n    }\n}\n\nfn should_pack_struct_member(\n    members: &[crate::StructMember],\n    span: u32,\n    index: usize,\n    module: &crate::Module,\n) -> Option<crate::Scalar> {\n    let member = &members[index];\n\n    let ty_inner = &module.types[member.ty].inner;\n    let last_offset = member.offset + ty_inner.size(module.to_ctx());\n    let next_offset = match members.get(index + 1) {\n        Some(next) => next.offset,\n        None => span,\n    };\n    let is_tight = next_offset == last_offset;\n\n    match *ty_inner {\n        crate::TypeInner::Vector {\n            size: crate::VectorSize::Tri,\n            scalar: scalar @ crate::Scalar { width: 4 | 2, .. },\n        } if is_tight => Some(scalar),\n        _ => None,\n    }\n}\n\nfn needs_array_length(ty: Handle<crate::Type>, arena: &crate::UniqueArena<crate::Type>) -> bool {\n    match arena[ty].inner {\n        crate::TypeInner::Struct { ref members, .. } => {\n            if let Some(member) = members.last() {\n                if let crate::TypeInner::Array {\n                    size: crate::ArraySize::Dynamic,\n                    ..\n                } = arena[member.ty].inner\n                {\n                    return true;\n                }\n            }\n            false\n        }\n        crate::TypeInner::Array {\n            size: crate::ArraySize::Dynamic,\n            ..\n        } => true,\n        _ => false,\n    }\n}\n\nimpl crate::AddressSpace {\n    /// Returns true if global variables in this address space are\n    /// passed in function arguments. These arguments need to be\n    /// passed through any functions called from the entry point.\n    const fn needs_pass_through(&self) -> bool {\n        match *self {\n            Self::Uniform\n            | Self::Storage { .. }\n            | Self::Private\n            | Self::WorkGroup\n            | Self::Immediate\n            | Self::Handle\n            | Self::TaskPayload => true,\n            Self::Function => false,\n            Self::RayPayload | Self::IncomingRayPayload => unreachable!(),\n        }\n    }\n\n    /// Returns true if the address space may need a \"const\" qualifier.\n    const fn needs_access_qualifier(&self) -> bool {\n        match *self {\n            //Note: we are ignoring the storage access here, and instead\n            // rely on the actual use of a global by functions. This means we\n            // may end up with \"const\" even if the binding is read-write,\n            // and that should be OK.\n            Self::Storage { .. } => true,\n            Self::TaskPayload | Self::RayPayload | Self::IncomingRayPayload => unimplemented!(),\n            // These should always be read-write.\n            Self::Private | Self::WorkGroup => false,\n            // These translate to `constant` address space, no need for qualifiers.\n            Self::Uniform | Self::Immediate => false,\n            // Not applicable.\n            Self::Handle | Self::Function => false,\n        }\n    }\n\n    const fn to_msl_name(self) -> Option<&'static str> {\n        match self {\n            Self::Handle => None,\n            Self::Uniform | Self::Immediate => Some(\"constant\"),\n            Self::Storage { .. } => Some(\"device\"),\n            // note for `RayPayload`, this probably needs to be emulated as a\n            // private variable, as metal has essentially an inout input\n            // for where it is passed.\n            Self::Private | Self::Function | Self::RayPayload => Some(\"thread\"),\n            Self::WorkGroup => Some(\"threadgroup\"),\n            Self::TaskPayload => Some(\"object_data\"),\n            Self::IncomingRayPayload => Some(\"ray_data\"),\n        }\n    }\n}\n\nimpl crate::Type {\n    // Returns `true` if we need to emit an alias for this type.\n    const fn needs_alias(&self) -> bool {\n        use crate::TypeInner as Ti;\n\n        match self.inner {\n            // value types are concise enough, we only alias them if they are named\n            Ti::Scalar(_)\n            | Ti::Vector { .. }\n            | Ti::Matrix { .. }\n            | Ti::CooperativeMatrix { .. }\n            | Ti::Atomic(_)\n            | Ti::Pointer { .. }\n            | Ti::ValuePointer { .. } => self.name.is_some(),\n            // composite types are better to be aliased, regardless of the name\n            Ti::Struct { .. } | Ti::Array { .. } => true,\n            // handle types may be different, depending on the global var access, so we always inline them\n            Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::RayQuery { .. }\n            | Ti::BindingArray { .. } => false,\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\nenum FunctionOrigin {\n    Handle(Handle<crate::Function>),\n    EntryPoint(proc::EntryPointIndex),\n}\n\ntrait NameKeyExt {\n    fn local(origin: FunctionOrigin, local_handle: Handle<crate::LocalVariable>) -> NameKey {\n        match origin {\n            FunctionOrigin::Handle(handle) => NameKey::FunctionLocal(handle, local_handle),\n            FunctionOrigin::EntryPoint(idx) => NameKey::EntryPointLocal(idx, local_handle),\n        }\n    }\n\n    /// Return the name key for a local variable used by ReadZeroSkipWrite bounds-check\n    /// policy when it needs to produce a pointer-typed result for an OOB access. These\n    /// are unique per accessed type, so the second argument is a type handle. See docs\n    /// for [`crate::back::msl`].\n    fn oob_local_for_type(origin: FunctionOrigin, ty: Handle<crate::Type>) -> NameKey {\n        match origin {\n            FunctionOrigin::Handle(handle) => NameKey::FunctionOobLocal(handle, ty),\n            FunctionOrigin::EntryPoint(idx) => NameKey::EntryPointOobLocal(idx, ty),\n        }\n    }\n}\n\nimpl NameKeyExt for NameKey {}\n\n/// A level of detail argument.\n///\n/// When [`BoundsCheckPolicy::Restrict`] applies to an [`ImageLoad`] access, we\n/// save the clamped level of detail in a temporary variable whose name is based\n/// on the handle of the `ImageLoad` expression. But for other policies, we just\n/// use the expression directly.\n///\n/// [`BoundsCheckPolicy::Restrict`]: index::BoundsCheckPolicy::Restrict\n/// [`ImageLoad`]: crate::Expression::ImageLoad\n#[derive(Clone, Copy)]\nenum LevelOfDetail {\n    Direct(Handle<crate::Expression>),\n    Restricted(Handle<crate::Expression>),\n}\n\n/// Values needed to select a particular texel for [`ImageLoad`] and [`ImageStore`].\n///\n/// When this is used in code paths unconcerned with the `Restrict` bounds check\n/// policy, the `LevelOfDetail` enum introduces an unneeded match, since `level`\n/// will always be either `None` or `Some(Direct(_))`. But this turns out not to\n/// be too awkward. If that changes, we can revisit.\n///\n/// [`ImageLoad`]: crate::Expression::ImageLoad\n/// [`ImageStore`]: crate::Statement::ImageStore\nstruct TexelAddress {\n    coordinate: Handle<crate::Expression>,\n    array_index: Option<Handle<crate::Expression>>,\n    sample: Option<Handle<crate::Expression>>,\n    level: Option<LevelOfDetail>,\n}\n\nstruct ExpressionContext<'a> {\n    function: &'a crate::Function,\n    origin: FunctionOrigin,\n    info: &'a valid::FunctionInfo,\n    module: &'a crate::Module,\n    mod_info: &'a valid::ModuleInfo,\n    pipeline_options: &'a PipelineOptions,\n    lang_version: (u8, u8),\n    policies: index::BoundsCheckPolicies,\n\n    /// The set of expressions used as indices in `ReadZeroSkipWrite`-policy\n    /// accesses. These may need to be cached in temporary variables. See\n    /// `index::find_checked_indexes` for details.\n    guarded_indices: HandleSet<crate::Expression>,\n    /// See [`Writer::gen_force_bounded_loop_statements`] for details.\n    force_loop_bounding: bool,\n}\n\nimpl<'a> ExpressionContext<'a> {\n    fn resolve_type(&self, handle: Handle<crate::Expression>) -> &'a crate::TypeInner {\n        self.info[handle].ty.inner_with(&self.module.types)\n    }\n\n    /// Return true if calls to `image`'s `read` and `write` methods should supply a level of detail.\n    ///\n    /// Only mipmapped images need to specify a level of detail. Since 1D\n    /// textures cannot have mipmaps, MSL requires that the level argument to\n    /// texture1d queries and accesses must be a constexpr 0. It's easiest\n    /// just to omit the level entirely for 1D textures.\n    fn image_needs_lod(&self, image: Handle<crate::Expression>) -> bool {\n        let image_ty = self.resolve_type(image);\n        if let crate::TypeInner::Image { dim, class, .. } = *image_ty {\n            class.is_mipmapped() && dim != crate::ImageDimension::D1\n        } else {\n            false\n        }\n    }\n\n    fn choose_bounds_check_policy(\n        &self,\n        pointer: Handle<crate::Expression>,\n    ) -> index::BoundsCheckPolicy {\n        self.policies\n            .choose_policy(pointer, &self.module.types, self.info)\n    }\n\n    /// See docs for [`proc::index::access_needs_check`].\n    fn access_needs_check(\n        &self,\n        base: Handle<crate::Expression>,\n        index: index::GuardedIndex,\n    ) -> Option<index::IndexableLength> {\n        index::access_needs_check(\n            base,\n            index,\n            self.module,\n            &self.function.expressions,\n            self.info,\n        )\n    }\n\n    /// See docs for [`proc::index::bounds_check_iter`].\n    fn bounds_check_iter(\n        &self,\n        chain: Handle<crate::Expression>,\n    ) -> impl Iterator<Item = BoundsCheck> + '_ {\n        index::bounds_check_iter(chain, self.module, self.function, self.info)\n    }\n\n    /// See docs for [`proc::index::oob_local_types`].\n    fn oob_local_types(&self) -> FastHashSet<Handle<crate::Type>> {\n        index::oob_local_types(self.module, self.function, self.info, self.policies)\n    }\n\n    fn get_packed_vec_kind(&self, expr_handle: Handle<crate::Expression>) -> Option<crate::Scalar> {\n        match self.function.expressions[expr_handle] {\n            crate::Expression::AccessIndex { base, index } => {\n                let ty = match *self.resolve_type(base) {\n                    crate::TypeInner::Pointer { base, .. } => &self.module.types[base].inner,\n                    ref ty => ty,\n                };\n                match *ty {\n                    crate::TypeInner::Struct {\n                        ref members, span, ..\n                    } => should_pack_struct_member(members, span, index as usize, self.module),\n                    _ => None,\n                }\n            }\n            _ => None,\n        }\n    }\n}\n\nstruct StatementContext<'a> {\n    expression: ExpressionContext<'a>,\n    result_struct: Option<&'a str>,\n}\n\nimpl<W: Write> Writer<W> {\n    /// Creates a new `Writer` instance.\n    pub fn new(out: W) -> Self {\n        Writer {\n            out,\n            names: FastHashMap::default(),\n            named_expressions: Default::default(),\n            need_bake_expressions: Default::default(),\n            namer: proc::Namer::default(),\n            wrapped_functions: FastHashSet::default(),\n            #[cfg(test)]\n            put_expression_stack_pointers: Default::default(),\n            #[cfg(test)]\n            put_block_stack_pointers: Default::default(),\n            struct_member_pads: FastHashSet::default(),\n        }\n    }\n\n    /// Finishes writing and returns the output.\n    // See https://github.com/rust-lang/rust-clippy/issues/4979.\n    pub fn finish(self) -> W {\n        self.out\n    }\n\n    /// Generates statements to be inserted immediately before and at the very\n    /// start of the body of each loop, to defeat MSL infinite loop reasoning.\n    /// The 0th item of the returned tuple should be inserted immediately prior\n    /// to the loop and the 1st item should be inserted at the very start of\n    /// the loop body.\n    ///\n    /// # What is this trying to solve?\n    ///\n    /// In Metal Shading Language, an infinite loop has undefined behavior.\n    /// (This rule is inherited from C++14.) This means that, if the MSL\n    /// compiler determines that a given loop will never exit, it may assume\n    /// that it is never reached. It may thus assume that any conditions\n    /// sufficient to cause the loop to be reached must be false. Like many\n    /// optimizing compilers, MSL uses this kind of analysis to establish limits\n    /// on the range of values variables involved in those conditions might\n    /// hold.\n    ///\n    /// For example, suppose the MSL compiler sees the code:\n    ///\n    /// ```ignore\n    /// if (i >= 10) {\n    ///     while (true) { }\n    /// }\n    /// ```\n    ///\n    /// It will recognize that the `while` loop will never terminate, conclude\n    /// that it must be unreachable, and thus infer that, if this code is\n    /// reached, then `i < 10` at that point.\n    ///\n    /// Now suppose that, at some point where `i` has the same value as above,\n    /// the compiler sees the code:\n    ///\n    /// ```ignore\n    /// if (i < 10) {\n    ///     a[i] = 1;\n    /// }\n    /// ```\n    ///\n    /// Because the compiler is confident that `i < 10`, it will make the\n    /// assignment to `a[i]` unconditional, rewriting this code as, simply:\n    ///\n    /// ```ignore\n    /// a[i] = 1;\n    /// ```\n    ///\n    /// If that `if` condition was injected by Naga to implement a bounds check,\n    /// the MSL compiler's optimizations could allow out-of-bounds array\n    /// accesses to occur.\n    ///\n    /// Naga cannot feasibly anticipate whether the MSL compiler will determine\n    /// that a loop is infinite, so an attacker could craft a Naga module\n    /// containing an infinite loop protected by conditions that cause the Metal\n    /// compiler to remove bounds checks that Naga injected elsewhere in the\n    /// function.\n    ///\n    /// This rewrite could occur even if the conditional assignment appears\n    /// *before* the `while` loop, as long as `i < 10` by the time the loop is\n    /// reached. This would allow the attacker to save the results of\n    /// unauthorized reads somewhere accessible before entering the infinite\n    /// loop. But even worse, the MSL compiler has been observed to simply\n    /// delete the infinite loop entirely, so that even code dominated by the\n    /// loop becomes reachable. This would make the attack even more flexible,\n    /// since shaders that would appear to never terminate would actually exit\n    /// nicely, after having stolen data from elsewhere in the GPU address\n    /// space.\n    ///\n    /// To avoid UB, Naga must persuade the MSL compiler that no loop Naga\n    /// generates is infinite. One approach would be to add inline assembly to\n    /// each loop that is annotated as potentially branching out of the loop,\n    /// but which in fact generates no instructions. Unfortunately, inline\n    /// assembly is not handled correctly by some Metal device drivers.\n    ///\n    /// A previously used approach was to add the following code to the bottom\n    /// of every loop:\n    ///\n    /// ```ignore\n    /// if (volatile bool unpredictable = false; unpredictable)\n    ///     break;\n    /// ```\n    ///\n    /// Although the `if` condition will always be false in any real execution,\n    /// the `volatile` qualifier prevents the compiler from assuming this. Thus,\n    /// it must assume that the `break` might be reached, and hence that the\n    /// loop is not unbounded. This prevents the range analysis impact described\n    /// above. Unfortunately this prevented the compiler from making important,\n    /// and safe, optimizations such as loop unrolling and was observed to\n    /// significantly hurt performance.\n    ///\n    /// Our current approach declares a counter before every loop and\n    /// increments it every iteration, breaking after 2^64 iterations:\n    ///\n    /// ```ignore\n    /// uint2 loop_bound = uint2(0);\n    /// while (true) {\n    ///   if (metal::all(loop_bound == uint2(4294967295))) { break; }\n    ///   loop_bound += uint2(loop_bound.y == 4294967295, 1);\n    /// }\n    /// ```\n    ///\n    /// This convinces the compiler that the loop is finite and therefore may\n    /// execute, whilst at the same time allowing optimizations such as loop\n    /// unrolling. Furthermore the 64-bit counter is large enough it seems\n    /// implausible that it would affect the execution of any shader.\n    ///\n    /// This approach is also used by Chromium WebGPU's Dawn shader compiler:\n    /// <https://dawn.googlesource.com/dawn/+/d9e2d1f718678ebee0728b999830576c410cce0a/src/tint/lang/core/ir/transform/prevent_infinite_loops.cc>\n    fn gen_force_bounded_loop_statements(\n        &mut self,\n        level: back::Level,\n        context: &StatementContext,\n    ) -> Option<(String, String)> {\n        if !context.expression.force_loop_bounding {\n            return None;\n        }\n\n        let loop_bound_name = self.namer.call(\"loop_bound\");\n        // Count down from u32::MAX rather than up from 0 to avoid hang on\n        // certain Intel drivers. See <https://github.com/gfx-rs/wgpu/issues/7319>.\n        let decl = format!(\"{level}uint2 {loop_bound_name} = uint2({}u);\", u32::MAX);\n        let level = level.next();\n        let break_and_inc = format!(\n            \"{level}if ({NAMESPACE}::all({loop_bound_name} == uint2(0u))) {{ break; }}\n{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);\"\n        );\n\n        Some((decl, break_and_inc))\n    }\n\n    fn put_call_parameters(\n        &mut self,\n        parameters: impl Iterator<Item = Handle<crate::Expression>>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        self.put_call_parameters_impl(parameters, context, |writer, context, expr| {\n            writer.put_expression(expr, context, true)\n        })\n    }\n\n    fn put_call_parameters_impl<C, E>(\n        &mut self,\n        parameters: impl Iterator<Item = Handle<crate::Expression>>,\n        ctx: &C,\n        put_expression: E,\n    ) -> BackendResult\n    where\n        E: Fn(&mut Self, &C, Handle<crate::Expression>) -> BackendResult,\n    {\n        write!(self.out, \"(\")?;\n        for (i, handle) in parameters.enumerate() {\n            if i != 0 {\n                write!(self.out, \", \")?;\n            }\n            put_expression(self, ctx, handle)?;\n        }\n        write!(self.out, \")\")?;\n        Ok(())\n    }\n\n    /// Writes the local variables of the given function, as well as any extra\n    /// out-of-bounds locals that are needed.\n    ///\n    /// The names of the OOB locals are also added to `self.names` at the same\n    /// time.\n    fn put_locals(&mut self, context: &ExpressionContext) -> BackendResult {\n        let oob_local_types = context.oob_local_types();\n        for &ty in oob_local_types.iter() {\n            let name_key = NameKey::oob_local_for_type(context.origin, ty);\n            self.names.insert(name_key, self.namer.call(\"oob\"));\n        }\n\n        for (name_key, ty, init) in context\n            .function\n            .local_variables\n            .iter()\n            .map(|(local_handle, local)| {\n                let name_key = NameKey::local(context.origin, local_handle);\n                (name_key, local.ty, local.init)\n            })\n            .chain(oob_local_types.iter().map(|&ty| {\n                let name_key = NameKey::oob_local_for_type(context.origin, ty);\n                (name_key, ty, None)\n            }))\n        {\n            let ty_name = TypeContext {\n                handle: ty,\n                gctx: context.module.to_ctx(),\n                names: &self.names,\n                access: crate::StorageAccess::empty(),\n                first_time: false,\n            };\n            write!(\n                self.out,\n                \"{}{} {}\",\n                back::INDENT,\n                ty_name,\n                self.names[&name_key]\n            )?;\n            match init {\n                Some(value) => {\n                    write!(self.out, \" = \")?;\n                    self.put_expression(value, context, true)?;\n                }\n                None => {\n                    write!(self.out, \" = {{}}\")?;\n                }\n            };\n            writeln!(self.out, \";\")?;\n        }\n        Ok(())\n    }\n\n    fn put_level_of_detail(\n        &mut self,\n        level: LevelOfDetail,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        match level {\n            LevelOfDetail::Direct(expr) => self.put_expression(expr, context, true)?,\n            LevelOfDetail::Restricted(load) => write!(self.out, \"{}\", ClampedLod(load))?,\n        }\n        Ok(())\n    }\n\n    fn put_image_query(\n        &mut self,\n        image: Handle<crate::Expression>,\n        query: &str,\n        level: Option<LevelOfDetail>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        self.put_expression(image, context, false)?;\n        write!(self.out, \".get_{query}(\")?;\n        if let Some(level) = level {\n            self.put_level_of_detail(level, context)?;\n        }\n        write!(self.out, \")\")?;\n        Ok(())\n    }\n\n    fn put_image_size_query(\n        &mut self,\n        image: Handle<crate::Expression>,\n        level: Option<LevelOfDetail>,\n        kind: crate::ScalarKind,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        if let crate::TypeInner::Image {\n            class: crate::ImageClass::External,\n            ..\n        } = *context.resolve_type(image)\n        {\n            write!(self.out, \"{IMAGE_SIZE_EXTERNAL_FUNCTION}(\")?;\n            self.put_expression(image, context, true)?;\n            write!(self.out, \")\")?;\n            return Ok(());\n        }\n\n        //Note: MSL only has separate width/height/depth queries,\n        // so compose the result of them.\n        let dim = match *context.resolve_type(image) {\n            crate::TypeInner::Image { dim, .. } => dim,\n            ref other => unreachable!(\"Unexpected type {:?}\", other),\n        };\n        let scalar = crate::Scalar { kind, width: 4 };\n        let coordinate_type = scalar.to_msl_name();\n        match dim {\n            crate::ImageDimension::D1 => {\n                // Since 1D textures never have mipmaps, MSL requires that the\n                // `level` argument be a constexpr 0. It's simplest for us just\n                // to pass `None` and omit the level entirely.\n                if kind == crate::ScalarKind::Uint {\n                    // No need to construct a vector. No cast needed.\n                    self.put_image_query(image, \"width\", None, context)?;\n                } else {\n                    // There's no definition for `int` in the `metal` namespace.\n                    write!(self.out, \"int(\")?;\n                    self.put_image_query(image, \"width\", None, context)?;\n                    write!(self.out, \")\")?;\n                }\n            }\n            crate::ImageDimension::D2 => {\n                write!(self.out, \"{NAMESPACE}::{coordinate_type}2(\")?;\n                self.put_image_query(image, \"width\", level, context)?;\n                write!(self.out, \", \")?;\n                self.put_image_query(image, \"height\", level, context)?;\n                write!(self.out, \")\")?;\n            }\n            crate::ImageDimension::D3 => {\n                write!(self.out, \"{NAMESPACE}::{coordinate_type}3(\")?;\n                self.put_image_query(image, \"width\", level, context)?;\n                write!(self.out, \", \")?;\n                self.put_image_query(image, \"height\", level, context)?;\n                write!(self.out, \", \")?;\n                self.put_image_query(image, \"depth\", level, context)?;\n                write!(self.out, \")\")?;\n            }\n            crate::ImageDimension::Cube => {\n                write!(self.out, \"{NAMESPACE}::{coordinate_type}2(\")?;\n                self.put_image_query(image, \"width\", level, context)?;\n                write!(self.out, \")\")?;\n            }\n        }\n        Ok(())\n    }\n\n    fn put_cast_to_uint_scalar_or_vector(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        // coordinates in IR are int, but Metal expects uint\n        match *context.resolve_type(expr) {\n            crate::TypeInner::Scalar(_) => {\n                put_numeric_type(&mut self.out, crate::Scalar::U32, &[])?\n            }\n            crate::TypeInner::Vector { size, .. } => {\n                put_numeric_type(&mut self.out, crate::Scalar::U32, &[size])?\n            }\n            _ => {\n                return Err(Error::GenericValidation(\n                    \"Invalid type for image coordinate\".into(),\n                ))\n            }\n        };\n\n        write!(self.out, \"(\")?;\n        self.put_expression(expr, context, true)?;\n        write!(self.out, \")\")?;\n        Ok(())\n    }\n\n    fn put_image_sample_level(\n        &mut self,\n        image: Handle<crate::Expression>,\n        level: crate::SampleLevel,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        let has_levels = context.image_needs_lod(image);\n        match level {\n            crate::SampleLevel::Auto => {}\n            crate::SampleLevel::Zero => {\n                //TODO: do we support Zero on `Sampled` image classes?\n            }\n            _ if !has_levels => {\n                log::warn!(\"1D image can't be sampled with level {level:?}\");\n            }\n            crate::SampleLevel::Exact(h) => {\n                write!(self.out, \", {NAMESPACE}::level(\")?;\n                self.put_expression(h, context, true)?;\n                write!(self.out, \")\")?;\n            }\n            crate::SampleLevel::Bias(h) => {\n                write!(self.out, \", {NAMESPACE}::bias(\")?;\n                self.put_expression(h, context, true)?;\n                write!(self.out, \")\")?;\n            }\n            crate::SampleLevel::Gradient { x, y } => {\n                write!(self.out, \", {NAMESPACE}::gradient2d(\")?;\n                self.put_expression(x, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(y, context, true)?;\n                write!(self.out, \")\")?;\n            }\n        }\n        Ok(())\n    }\n\n    fn put_image_coordinate_limits(\n        &mut self,\n        image: Handle<crate::Expression>,\n        level: Option<LevelOfDetail>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        self.put_image_size_query(image, level, crate::ScalarKind::Uint, context)?;\n        write!(self.out, \" - 1\")?;\n        Ok(())\n    }\n\n    /// General function for writing restricted image indexes.\n    ///\n    /// This is used to produce restricted mip levels, array indices, and sample\n    /// indices for [`ImageLoad`] and [`ImageStore`] accesses under the\n    /// [`Restrict`] bounds check policy.\n    ///\n    /// This function writes an expression of the form:\n    ///\n    /// ```ignore\n    ///\n    ///     metal::min(uint(INDEX), IMAGE.LIMIT_METHOD() - 1)\n    ///\n    /// ```\n    ///\n    /// [`ImageLoad`]: crate::Expression::ImageLoad\n    /// [`ImageStore`]: crate::Statement::ImageStore\n    /// [`Restrict`]: index::BoundsCheckPolicy::Restrict\n    fn put_restricted_scalar_image_index(\n        &mut self,\n        image: Handle<crate::Expression>,\n        index: Handle<crate::Expression>,\n        limit_method: &str,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        write!(self.out, \"{NAMESPACE}::min(uint(\")?;\n        self.put_expression(index, context, true)?;\n        write!(self.out, \"), \")?;\n        self.put_expression(image, context, false)?;\n        write!(self.out, \".{limit_method}() - 1)\")?;\n        Ok(())\n    }\n\n    fn put_restricted_texel_address(\n        &mut self,\n        image: Handle<crate::Expression>,\n        address: &TexelAddress,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        // Write the coordinate.\n        write!(self.out, \"{NAMESPACE}::min(\")?;\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, context)?;\n        write!(self.out, \", \")?;\n        self.put_image_coordinate_limits(image, address.level, context)?;\n        write!(self.out, \")\")?;\n\n        // Write the array index, if present.\n        if let Some(array_index) = address.array_index {\n            write!(self.out, \", \")?;\n            self.put_restricted_scalar_image_index(image, array_index, \"get_array_size\", context)?;\n        }\n\n        // Write the sample index, if present.\n        if let Some(sample) = address.sample {\n            write!(self.out, \", \")?;\n            self.put_restricted_scalar_image_index(image, sample, \"get_num_samples\", context)?;\n        }\n\n        // The level of detail should be clamped and cached by\n        // `put_cache_restricted_level`, so we don't need to clamp it here.\n        if let Some(level) = address.level {\n            write!(self.out, \", \")?;\n            self.put_level_of_detail(level, context)?;\n        }\n\n        Ok(())\n    }\n\n    /// Write an expression that is true if the given image access is in bounds.\n    fn put_image_access_bounds_check(\n        &mut self,\n        image: Handle<crate::Expression>,\n        address: &TexelAddress,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        let mut conjunction = \"\";\n\n        // First, check the level of detail. Only if that is in bounds can we\n        // use it to find the appropriate bounds for the coordinates.\n        let level = if let Some(level) = address.level {\n            write!(self.out, \"uint(\")?;\n            self.put_level_of_detail(level, context)?;\n            write!(self.out, \") < \")?;\n            self.put_expression(image, context, true)?;\n            write!(self.out, \".get_num_mip_levels()\")?;\n            conjunction = \" && \";\n            Some(level)\n        } else {\n            None\n        };\n\n        // Check sample index, if present.\n        if let Some(sample) = address.sample {\n            write!(self.out, \"uint(\")?;\n            self.put_expression(sample, context, true)?;\n            write!(self.out, \") < \")?;\n            self.put_expression(image, context, true)?;\n            write!(self.out, \".get_num_samples()\")?;\n            conjunction = \" && \";\n        }\n\n        // Check array index, if present.\n        if let Some(array_index) = address.array_index {\n            write!(self.out, \"{conjunction}uint(\")?;\n            self.put_expression(array_index, context, true)?;\n            write!(self.out, \") < \")?;\n            self.put_expression(image, context, true)?;\n            write!(self.out, \".get_array_size()\")?;\n            conjunction = \" && \";\n        }\n\n        // Finally, check if the coordinates are within bounds.\n        let coord_is_vector = match *context.resolve_type(address.coordinate) {\n            crate::TypeInner::Vector { .. } => true,\n            _ => false,\n        };\n        write!(self.out, \"{conjunction}\")?;\n        if coord_is_vector {\n            write!(self.out, \"{NAMESPACE}::all(\")?;\n        }\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, context)?;\n        write!(self.out, \" < \")?;\n        self.put_image_size_query(image, level, crate::ScalarKind::Uint, context)?;\n        if coord_is_vector {\n            write!(self.out, \")\")?;\n        }\n\n        Ok(())\n    }\n\n    fn put_image_load(\n        &mut self,\n        load: Handle<crate::Expression>,\n        image: Handle<crate::Expression>,\n        mut address: TexelAddress,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        if let crate::TypeInner::Image {\n            class: crate::ImageClass::External,\n            ..\n        } = *context.resolve_type(image)\n        {\n            write!(self.out, \"{IMAGE_LOAD_EXTERNAL_FUNCTION}(\")?;\n            self.put_expression(image, context, true)?;\n            write!(self.out, \", \")?;\n            self.put_cast_to_uint_scalar_or_vector(address.coordinate, context)?;\n            write!(self.out, \")\")?;\n            return Ok(());\n        }\n\n        match context.policies.image_load {\n            proc::BoundsCheckPolicy::Restrict => {\n                // Use the cached restricted level of detail, if any. Omit the\n                // level altogether for 1D textures.\n                if address.level.is_some() {\n                    address.level = if context.image_needs_lod(image) {\n                        Some(LevelOfDetail::Restricted(load))\n                    } else {\n                        None\n                    }\n                }\n\n                self.put_expression(image, context, false)?;\n                write!(self.out, \".read(\")?;\n                self.put_restricted_texel_address(image, &address, context)?;\n                write!(self.out, \")\")?;\n            }\n            proc::BoundsCheckPolicy::ReadZeroSkipWrite => {\n                write!(self.out, \"(\")?;\n                self.put_image_access_bounds_check(image, &address, context)?;\n                write!(self.out, \" ? \")?;\n                self.put_unchecked_image_load(image, &address, context)?;\n                write!(self.out, \": DefaultConstructible())\")?;\n            }\n            proc::BoundsCheckPolicy::Unchecked => {\n                self.put_unchecked_image_load(image, &address, context)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    fn put_unchecked_image_load(\n        &mut self,\n        image: Handle<crate::Expression>,\n        address: &TexelAddress,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        self.put_expression(image, context, false)?;\n        write!(self.out, \".read(\")?;\n        // coordinates in IR are int, but Metal expects uint\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, context)?;\n        if let Some(expr) = address.array_index {\n            write!(self.out, \", \")?;\n            self.put_expression(expr, context, true)?;\n        }\n        if let Some(sample) = address.sample {\n            write!(self.out, \", \")?;\n            self.put_expression(sample, context, true)?;\n        }\n        if let Some(level) = address.level {\n            if context.image_needs_lod(image) {\n                write!(self.out, \", \")?;\n                self.put_level_of_detail(level, context)?;\n            }\n        }\n        write!(self.out, \")\")?;\n\n        Ok(())\n    }\n\n    fn put_image_atomic(\n        &mut self,\n        level: back::Level,\n        image: Handle<crate::Expression>,\n        address: &TexelAddress,\n        fun: crate::AtomicFunction,\n        value: Handle<crate::Expression>,\n        context: &StatementContext,\n    ) -> BackendResult {\n        write!(self.out, \"{level}\")?;\n        self.put_expression(image, &context.expression, false)?;\n        let op = if context.expression.resolve_type(value).scalar_width() == Some(8) {\n            fun.to_msl_64_bit()?\n        } else {\n            fun.to_msl()\n        };\n        write!(self.out, \".atomic_{op}(\")?;\n        // coordinates in IR are int, but Metal expects uint\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, &context.expression)?;\n        write!(self.out, \", \")?;\n        self.put_expression(value, &context.expression, true)?;\n        writeln!(self.out, \");\")?;\n\n        // Workaround for Apple Metal TBDR driver bug: fragment shader atomic\n        // texture writes randomly drop unless followed by a standard texture\n        // write. Insert a dead-code write behind an unprovable condition so\n        // the compiler emits proper memory safety barriers.\n        // See: https://projects.blender.org/blender/blender/commit/aa95220576706122d79c91c7f5c522e6c7416425\n        let value_ty = context.expression.resolve_type(value);\n        let zero_value = match (value_ty.scalar_kind(), value_ty.scalar_width()) {\n            (Some(crate::ScalarKind::Sint), _) => \"int4(0)\",\n            (_, Some(8)) => \"ulong4(0uL)\",\n            _ => \"uint4(0u)\",\n        };\n        let coord_ty = context.expression.resolve_type(address.coordinate);\n        let x = if matches!(coord_ty, crate::TypeInner::Scalar(_)) {\n            \"\"\n        } else {\n            \".x\"\n        };\n        write!(self.out, \"{level}if (\")?;\n        self.put_expression(address.coordinate, &context.expression, true)?;\n        write!(self.out, \"{x} == -99999) {{ \")?;\n        self.put_expression(image, &context.expression, false)?;\n        write!(self.out, \".write({zero_value}, \")?;\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, &context.expression)?;\n        if let Some(array_index) = address.array_index {\n            write!(self.out, \", \")?;\n            self.put_expression(array_index, &context.expression, true)?;\n        }\n        writeln!(self.out, \"); }}\")?;\n\n        Ok(())\n    }\n\n    fn put_image_store(\n        &mut self,\n        level: back::Level,\n        image: Handle<crate::Expression>,\n        address: &TexelAddress,\n        value: Handle<crate::Expression>,\n        context: &StatementContext,\n    ) -> BackendResult {\n        write!(self.out, \"{level}\")?;\n        self.put_expression(image, &context.expression, false)?;\n        write!(self.out, \".write(\")?;\n        self.put_expression(value, &context.expression, true)?;\n        write!(self.out, \", \")?;\n        // coordinates in IR are int, but Metal expects uint\n        self.put_cast_to_uint_scalar_or_vector(address.coordinate, &context.expression)?;\n        if let Some(expr) = address.array_index {\n            write!(self.out, \", \")?;\n            self.put_expression(expr, &context.expression, true)?;\n        }\n        writeln!(self.out, \");\")?;\n\n        Ok(())\n    }\n\n    /// Write the maximum valid index of the dynamically sized array at the end of `handle`.\n    ///\n    /// The 'maximum valid index' is simply one less than the array's length.\n    ///\n    /// This emits an expression of the form `a / b`, so the caller must\n    /// parenthesize its output if it will be applying operators of higher\n    /// precedence.\n    ///\n    /// `handle` must be the handle of a global variable whose final member is a\n    /// dynamically sized array.\n    fn put_dynamic_array_max_index(\n        &mut self,\n        handle: Handle<crate::GlobalVariable>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        let global = &context.module.global_variables[handle];\n        let (offset, array_ty) = match context.module.types[global.ty].inner {\n            crate::TypeInner::Struct { ref members, .. } => match members.last() {\n                Some(&crate::StructMember { offset, ty, .. }) => (offset, ty),\n                None => return Err(Error::GenericValidation(\"Struct has no members\".into())),\n            },\n            crate::TypeInner::Array {\n                size: crate::ArraySize::Dynamic,\n                ..\n            } => (0, global.ty),\n            ref ty => {\n                return Err(Error::GenericValidation(format!(\n                    \"Expected type with dynamic array, got {ty:?}\"\n                )))\n            }\n        };\n\n        let (size, stride) = match context.module.types[array_ty].inner {\n            crate::TypeInner::Array { base, stride, .. } => (\n                context.module.types[base]\n                    .inner\n                    .size(context.module.to_ctx()),\n                stride,\n            ),\n            ref ty => {\n                return Err(Error::GenericValidation(format!(\n                    \"Expected array type, got {ty:?}\"\n                )))\n            }\n        };\n\n        // When the stride length is larger than the size, the final element's stride of\n        // bytes would have padding following the value. But the buffer size in\n        // `buffer_sizes.sizeN` may not include this padding - it only needs to be large\n        // enough to hold the actual values' bytes.\n        //\n        // So subtract off the size to get a byte size that falls at the start or within\n        // the final element. Then divide by the stride size, to get one less than the\n        // length, and then add one. This works even if the buffer size does include the\n        // stride padding, since division rounds towards zero (MSL 2.4 §6.1). It will fail\n        // if there are zero elements in the array, but the WebGPU `validating shader binding`\n        // rules, together with draw-time validation when `minBindingSize` is zero,\n        // prevent that.\n        write!(\n            self.out,\n            \"(_buffer_sizes.{member} - {offset} - {size}) / {stride}\",\n            member = ArraySizeMember(handle),\n            offset = offset,\n            size = size,\n            stride = stride,\n        )?;\n        Ok(())\n    }\n\n    /// Emit code for the arithmetic expression of the dot product.\n    ///\n    /// The argument `extractor` is a function that accepts a `Writer`, a vector, and\n    /// an index. It writes out the expression for the vector component at that index.\n    fn put_dot_product<T: Copy>(\n        &mut self,\n        arg: T,\n        arg1: T,\n        size: usize,\n        extractor: impl Fn(&mut Self, T, usize) -> BackendResult,\n    ) -> BackendResult {\n        // Write parentheses around the dot product expression to prevent operators\n        // with different precedences from applying earlier.\n        write!(self.out, \"(\")?;\n\n        // Cycle through all the components of the vector\n        for index in 0..size {\n            // Write the addition to the previous product\n            // This will print an extra '+' at the beginning but that is fine in msl\n            write!(self.out, \" + \")?;\n            extractor(self, arg, index)?;\n            write!(self.out, \" * \")?;\n            extractor(self, arg1, index)?;\n        }\n\n        write!(self.out, \")\")?;\n        Ok(())\n    }\n\n    /// Emit code for the WGSL functions `pack4x{I, U}8[Clamp]`.\n    fn put_pack4x8(\n        &mut self,\n        arg: Handle<crate::Expression>,\n        context: &ExpressionContext<'_>,\n        was_signed: bool,\n        clamp_bounds: Option<(&str, &str)>,\n    ) -> Result<(), Error> {\n        let write_arg = |this: &mut Self| -> BackendResult {\n            if let Some((min, max)) = clamp_bounds {\n                // Clamping with scalar bounds works (component-wise) even for packed_[u]char4.\n                write!(this.out, \"{NAMESPACE}::clamp(\")?;\n                this.put_expression(arg, context, true)?;\n                write!(this.out, \", {min}, {max})\")?;\n            } else {\n                this.put_expression(arg, context, true)?;\n            }\n            Ok(())\n        };\n\n        if context.lang_version >= (2, 1) {\n            let packed_type = if was_signed {\n                \"packed_char4\"\n            } else {\n                \"packed_uchar4\"\n            };\n            // Metal uses little endian byte order, which matches what WGSL expects here.\n            write!(self.out, \"as_type<uint>({packed_type}(\")?;\n            write_arg(self)?;\n            write!(self.out, \"))\")?;\n        } else {\n            // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.\n            if was_signed {\n                write!(self.out, \"uint(\")?;\n            }\n            write!(self.out, \"(\")?;\n            write_arg(self)?;\n            write!(self.out, \"[0] & 0xFF) | ((\")?;\n            write_arg(self)?;\n            write!(self.out, \"[1] & 0xFF) << 8) | ((\")?;\n            write_arg(self)?;\n            write!(self.out, \"[2] & 0xFF) << 16) | ((\")?;\n            write_arg(self)?;\n            write!(self.out, \"[3] & 0xFF) << 24)\")?;\n            if was_signed {\n                write!(self.out, \")\")?;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Emit code for the isign expression.\n    ///\n    fn put_isign(\n        &mut self,\n        arg: Handle<crate::Expression>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        write!(self.out, \"{NAMESPACE}::select({NAMESPACE}::select(\")?;\n        let scalar = context\n            .resolve_type(arg)\n            .scalar()\n            .expect(\"put_isign should only be called for args which have an integer scalar type\")\n            .to_msl_name();\n        match context.resolve_type(arg) {\n            &crate::TypeInner::Vector { size, .. } => {\n                let size = common::vector_size_str(size);\n                write!(self.out, \"{scalar}{size}(-1), {scalar}{size}(1)\")?;\n            }\n            _ => {\n                write!(self.out, \"{scalar}(-1), {scalar}(1)\")?;\n            }\n        }\n        write!(self.out, \", (\")?;\n        self.put_expression(arg, context, true)?;\n        write!(self.out, \" > 0)), {scalar}(0), (\")?;\n        self.put_expression(arg, context, true)?;\n        write!(self.out, \" == 0))\")?;\n        Ok(())\n    }\n\n    fn put_const_expression(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        module: &crate::Module,\n        mod_info: &valid::ModuleInfo,\n        arena: &crate::Arena<crate::Expression>,\n    ) -> BackendResult {\n        self.put_possibly_const_expression(\n            expr_handle,\n            arena,\n            module,\n            mod_info,\n            &(module, mod_info),\n            |&(_, mod_info), expr| &mod_info[expr],\n            |writer, &(module, _), expr| writer.put_const_expression(expr, module, mod_info, arena),\n        )\n    }\n\n    fn put_literal(&mut self, literal: crate::Literal) -> BackendResult {\n        match literal {\n            crate::Literal::F64(_) => {\n                return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64))\n            }\n            crate::Literal::F16(value) => {\n                if value.is_infinite() {\n                    let sign = if value.is_sign_negative() { \"-\" } else { \"\" };\n                    write!(self.out, \"{sign}INFINITY\")?;\n                } else if value.is_nan() {\n                    write!(self.out, \"NAN\")?;\n                } else {\n                    let suffix = if value.fract() == f16::from_f32(0.0) {\n                        \".0h\"\n                    } else {\n                        \"h\"\n                    };\n                    write!(self.out, \"{value}{suffix}\")?;\n                }\n            }\n            crate::Literal::F32(value) => {\n                if value.is_infinite() {\n                    let sign = if value.is_sign_negative() { \"-\" } else { \"\" };\n                    write!(self.out, \"{sign}INFINITY\")?;\n                } else if value.is_nan() {\n                    write!(self.out, \"NAN\")?;\n                } else {\n                    let suffix = if value.fract() == 0.0 { \".0\" } else { \"\" };\n                    write!(self.out, \"{value}{suffix}\")?;\n                }\n            }\n            crate::Literal::U32(value) => {\n                write!(self.out, \"{value}u\")?;\n            }\n            crate::Literal::I32(value) => {\n                // `-2147483648` is parsed as unary negation of positive 2147483648.\n                // 2147483648 is too large for int32_t meaning the expression gets\n                // promoted to a int64_t which is not our intention. Avoid this by instead\n                // using `-2147483647 - 1`.\n                if value == i32::MIN {\n                    write!(self.out, \"({} - 1)\", value + 1)?;\n                } else {\n                    write!(self.out, \"{value}\")?;\n                }\n            }\n            crate::Literal::U64(value) => {\n                write!(self.out, \"{value}uL\")?;\n            }\n            crate::Literal::I64(value) => {\n                // `-9223372036854775808` is parsed as unary negation of positive\n                // 9223372036854775808. 9223372036854775808 is too large for int64_t\n                // causing Metal to emit a `-Wconstant-conversion` warning, and change the\n                // value to `-9223372036854775808`. Which would then be negated, possibly\n                // causing undefined behaviour. Avoid this by instead using\n                // `-9223372036854775808L - 1L`.\n                if value == i64::MIN {\n                    write!(self.out, \"({}L - 1L)\", value + 1)?;\n                } else {\n                    write!(self.out, \"{value}L\")?;\n                }\n            }\n            crate::Literal::Bool(value) => {\n                write!(self.out, \"{value}\")?;\n            }\n            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {\n                return Err(Error::GenericValidation(\n                    \"Unsupported abstract literal\".into(),\n                ));\n            }\n        }\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn put_possibly_const_expression<C, I, E>(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        expressions: &crate::Arena<crate::Expression>,\n        module: &crate::Module,\n        mod_info: &valid::ModuleInfo,\n        ctx: &C,\n        get_expr_ty: I,\n        put_expression: E,\n    ) -> BackendResult\n    where\n        I: Fn(&C, Handle<crate::Expression>) -> &TypeResolution,\n        E: Fn(&mut Self, &C, Handle<crate::Expression>) -> BackendResult,\n    {\n        match expressions[expr_handle] {\n            crate::Expression::Literal(literal) => {\n                self.put_literal(literal)?;\n            }\n            crate::Expression::Constant(handle) => {\n                let constant = &module.constants[handle];\n                if constant.name.is_some() {\n                    write!(self.out, \"{}\", self.names[&NameKey::Constant(handle)])?;\n                } else {\n                    self.put_const_expression(\n                        constant.init,\n                        module,\n                        mod_info,\n                        &module.global_expressions,\n                    )?;\n                }\n            }\n            crate::Expression::ZeroValue(ty) => {\n                let ty_name = TypeContext {\n                    handle: ty,\n                    gctx: module.to_ctx(),\n                    names: &self.names,\n                    access: crate::StorageAccess::empty(),\n                    first_time: false,\n                };\n                write!(self.out, \"{ty_name} {{}}\")?;\n            }\n            crate::Expression::Compose { ty, ref components } => {\n                let ty_name = TypeContext {\n                    handle: ty,\n                    gctx: module.to_ctx(),\n                    names: &self.names,\n                    access: crate::StorageAccess::empty(),\n                    first_time: false,\n                };\n                write!(self.out, \"{ty_name}\")?;\n                match module.types[ty].inner {\n                    crate::TypeInner::Scalar(_)\n                    | crate::TypeInner::Vector { .. }\n                    | crate::TypeInner::Matrix { .. } => {\n                        self.put_call_parameters_impl(\n                            components.iter().copied(),\n                            ctx,\n                            put_expression,\n                        )?;\n                    }\n                    crate::TypeInner::Array { .. } => {\n                        // Naga Arrays are Metal arrays wrapped in structs, so\n                        // we need two levels of braces.\n                        write!(self.out, \" {{{{\")?;\n                        for (index, &component) in components.iter().enumerate() {\n                            if index != 0 {\n                                write!(self.out, \", \")?;\n                            }\n                            put_expression(self, ctx, component)?;\n                        }\n                        write!(self.out, \"}}}}\")?;\n                    }\n                    crate::TypeInner::Struct { .. } => {\n                        write!(self.out, \" {{\")?;\n                        for (index, &component) in components.iter().enumerate() {\n                            if index != 0 {\n                                write!(self.out, \", \")?;\n                            }\n                            // insert padding initialization, if needed\n                            if self.struct_member_pads.contains(&(ty, index as u32)) {\n                                write!(self.out, \"{{}}, \")?;\n                            }\n                            put_expression(self, ctx, component)?;\n                        }\n                        write!(self.out, \"}}\")?;\n                    }\n                    _ => return Err(Error::UnsupportedCompose(ty)),\n                }\n            }\n            crate::Expression::Splat { size, value } => {\n                let scalar = match *get_expr_ty(ctx, value).inner_with(&module.types) {\n                    crate::TypeInner::Scalar(scalar) => scalar,\n                    ref ty => {\n                        return Err(Error::GenericValidation(format!(\n                            \"Expected splat value type must be a scalar, got {ty:?}\",\n                        )))\n                    }\n                };\n                put_numeric_type(&mut self.out, scalar, &[size])?;\n                write!(self.out, \"(\")?;\n                put_expression(self, ctx, value)?;\n                write!(self.out, \")\")?;\n            }\n            _ => {\n                return Err(Error::Override);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Emit code for the expression `expr_handle`.\n    ///\n    /// The `is_scoped` argument is true if the surrounding operators have the\n    /// precedence of the comma operator, or lower. So, for example:\n    ///\n    /// - Pass `true` for `is_scoped` when writing function arguments, an\n    ///   expression statement, an initializer expression, or anything already\n    ///   wrapped in parenthesis.\n    ///\n    /// - Pass `false` if it is an operand of a `?:` operator, a `[]`, or really\n    ///   almost anything else.\n    fn put_expression(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        is_scoped: bool,\n    ) -> BackendResult {\n        // Add to the set in order to track the stack size.\n        #[cfg(test)]\n        self.put_expression_stack_pointers\n            .insert(ptr::from_ref(&expr_handle).cast());\n\n        if let Some(name) = self.named_expressions.get(&expr_handle) {\n            write!(self.out, \"{name}\")?;\n            return Ok(());\n        }\n\n        let expression = &context.function.expressions[expr_handle];\n        match *expression {\n            crate::Expression::Literal(_)\n            | crate::Expression::Constant(_)\n            | crate::Expression::ZeroValue(_)\n            | crate::Expression::Compose { .. }\n            | crate::Expression::Splat { .. } => {\n                self.put_possibly_const_expression(\n                    expr_handle,\n                    &context.function.expressions,\n                    context.module,\n                    context.mod_info,\n                    context,\n                    |context, expr: Handle<crate::Expression>| &context.info[expr].ty,\n                    |writer, context, expr| writer.put_expression(expr, context, true),\n                )?;\n            }\n            crate::Expression::Override(_) => return Err(Error::Override),\n            crate::Expression::Access { base, .. }\n            | crate::Expression::AccessIndex { base, .. } => {\n                // This is an acceptable place to generate a `ReadZeroSkipWrite` check.\n                // Since `put_bounds_checks` and `put_access_chain` handle an entire\n                // access chain at a time, recursing back through `put_expression` only\n                // for index expressions and the base object, we will never see intermediate\n                // `Access` or `AccessIndex` expressions here.\n                let policy = context.choose_bounds_check_policy(base);\n                if policy == index::BoundsCheckPolicy::ReadZeroSkipWrite\n                    && self.put_bounds_checks(\n                        expr_handle,\n                        context,\n                        back::Level(0),\n                        if is_scoped { \"\" } else { \"(\" },\n                    )?\n                {\n                    write!(self.out, \" ? \")?;\n                    self.put_access_chain(expr_handle, policy, context)?;\n                    write!(self.out, \" : \")?;\n\n                    if context.resolve_type(base).pointer_space().is_some() {\n                        // We can't just use `DefaultConstructible` if this is a pointer.\n                        // Instead, we create a dummy local variable to serve as pointer\n                        // target if the access is out of bounds.\n                        let result_ty = context.info[expr_handle]\n                            .ty\n                            .inner_with(&context.module.types)\n                            .pointer_base_type();\n                        let result_ty_handle = match result_ty {\n                            Some(TypeResolution::Handle(handle)) => handle,\n                            Some(TypeResolution::Value(_)) => {\n                                // As long as the result of a pointer access expression is\n                                // passed to a function or stored in a let binding, the\n                                // type will be in the arena. If additional uses of\n                                // pointers become valid, this assumption might no longer\n                                // hold. Note that the LHS of a load or store doesn't\n                                // take this path -- there is dedicated code in `put_load`\n                                // and `put_store`.\n                                unreachable!(\n                                    \"Expected type {result_ty:?} of access through pointer type {base:?} to be in the arena\",\n                                );\n                            }\n                            None => {\n                                unreachable!(\n                                    \"Expected access through pointer type {base:?} to return a pointer, but got {result_ty:?}\",\n                                )\n                            }\n                        };\n                        let name_key =\n                            NameKey::oob_local_for_type(context.origin, result_ty_handle);\n                        self.out.write_str(&self.names[&name_key])?;\n                    } else {\n                        write!(self.out, \"DefaultConstructible()\")?;\n                    }\n\n                    if !is_scoped {\n                        write!(self.out, \")\")?;\n                    }\n                } else {\n                    self.put_access_chain(expr_handle, policy, context)?;\n                }\n            }\n            crate::Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                self.put_wrapped_expression_for_packed_vec3_access(\n                    vector,\n                    context,\n                    false,\n                    &Self::put_expression,\n                )?;\n                write!(self.out, \".\")?;\n                for &sc in pattern[..size as usize].iter() {\n                    write!(self.out, \"{}\", back::COMPONENTS[sc as usize])?;\n                }\n            }\n            crate::Expression::FunctionArgument(index) => {\n                let name_key = match context.origin {\n                    FunctionOrigin::Handle(handle) => NameKey::FunctionArgument(handle, index),\n                    FunctionOrigin::EntryPoint(ep_index) => {\n                        NameKey::EntryPointArgument(ep_index, index)\n                    }\n                };\n                let name = &self.names[&name_key];\n                write!(self.out, \"{name}\")?;\n            }\n            crate::Expression::GlobalVariable(handle) => {\n                let name = &self.names[&NameKey::GlobalVariable(handle)];\n                write!(self.out, \"{name}\")?;\n            }\n            crate::Expression::LocalVariable(handle) => {\n                let name_key = NameKey::local(context.origin, handle);\n                let name = &self.names[&name_key];\n                write!(self.out, \"{name}\")?;\n            }\n            crate::Expression::Load { pointer } => self.put_load(pointer, context, is_scoped)?,\n            crate::Expression::ImageSample {\n                coordinate,\n                image,\n                sampler,\n                clamp_to_edge: true,\n                gather: None,\n                array_index: None,\n                offset: None,\n                level: crate::SampleLevel::Zero,\n                depth_ref: None,\n            } => {\n                write!(self.out, \"{IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(\")?;\n                self.put_expression(image, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(sampler, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(coordinate, context, true)?;\n                write!(self.out, \")\")?;\n            }\n            crate::Expression::ImageSample {\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n            } => {\n                if clamp_to_edge {\n                    return Err(Error::GenericValidation(\n                        \"ImageSample::clamp_to_edge should have been validated out\".to_string(),\n                    ));\n                }\n\n                let main_op = match gather {\n                    Some(_) => \"gather\",\n                    None => \"sample\",\n                };\n                let comparison_op = match depth_ref {\n                    Some(_) => \"_compare\",\n                    None => \"\",\n                };\n                self.put_expression(image, context, false)?;\n                write!(self.out, \".{main_op}{comparison_op}(\")?;\n                self.put_expression(sampler, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(coordinate, context, true)?;\n                if let Some(expr) = array_index {\n                    write!(self.out, \", \")?;\n                    self.put_expression(expr, context, true)?;\n                }\n                if let Some(dref) = depth_ref {\n                    write!(self.out, \", \")?;\n                    self.put_expression(dref, context, true)?;\n                }\n\n                self.put_image_sample_level(image, level, context)?;\n\n                if let Some(offset) = offset {\n                    write!(self.out, \", \")?;\n                    self.put_expression(offset, context, true)?;\n                }\n\n                match gather {\n                    None | Some(crate::SwizzleComponent::X) => {}\n                    Some(component) => {\n                        let is_cube_map = match *context.resolve_type(image) {\n                            crate::TypeInner::Image {\n                                dim: crate::ImageDimension::Cube,\n                                ..\n                            } => true,\n                            _ => false,\n                        };\n                        // Offset always comes before the gather, except\n                        // in cube maps where it's not applicable\n                        if offset.is_none() && !is_cube_map {\n                            write!(self.out, \", {NAMESPACE}::int2(0)\")?;\n                        }\n                        let letter = back::COMPONENTS[component as usize];\n                        write!(self.out, \", {NAMESPACE}::component::{letter}\")?;\n                    }\n                }\n                write!(self.out, \")\")?;\n            }\n            crate::Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                let address = TexelAddress {\n                    coordinate,\n                    array_index,\n                    sample,\n                    level: level.map(LevelOfDetail::Direct),\n                };\n                self.put_image_load(expr_handle, image, address, context)?;\n            }\n            //Note: for all the queries, the signed integers are expected,\n            // so a conversion is needed.\n            crate::Expression::ImageQuery { image, query } => match query {\n                crate::ImageQuery::Size { level } => {\n                    self.put_image_size_query(\n                        image,\n                        level.map(LevelOfDetail::Direct),\n                        crate::ScalarKind::Uint,\n                        context,\n                    )?;\n                }\n                crate::ImageQuery::NumLevels => {\n                    self.put_expression(image, context, false)?;\n                    write!(self.out, \".get_num_mip_levels()\")?;\n                }\n                crate::ImageQuery::NumLayers => {\n                    self.put_expression(image, context, false)?;\n                    write!(self.out, \".get_array_size()\")?;\n                }\n                crate::ImageQuery::NumSamples => {\n                    self.put_expression(image, context, false)?;\n                    write!(self.out, \".get_num_samples()\")?;\n                }\n            },\n            crate::Expression::Unary { op, expr } => {\n                let op_str = match op {\n                    crate::UnaryOperator::Negate => {\n                        match context.resolve_type(expr).scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => NEG_FUNCTION,\n                            _ => \"-\",\n                        }\n                    }\n                    crate::UnaryOperator::LogicalNot => \"!\",\n                    crate::UnaryOperator::BitwiseNot => \"~\",\n                };\n                write!(self.out, \"{op_str}(\")?;\n                self.put_expression(expr, context, false)?;\n                write!(self.out, \")\")?;\n            }\n            crate::Expression::Binary { op, left, right } => {\n                let kind = context\n                    .resolve_type(left)\n                    .scalar_kind()\n                    .ok_or(Error::UnsupportedBinaryOp(op))?;\n\n                if op == crate::BinaryOperator::Divide\n                    && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint)\n                {\n                    write!(self.out, \"{DIV_FUNCTION}(\")?;\n                    self.put_expression(left, context, true)?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(right, context, true)?;\n                    write!(self.out, \")\")?;\n                } else if op == crate::BinaryOperator::Modulo\n                    && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint)\n                {\n                    write!(self.out, \"{MOD_FUNCTION}(\")?;\n                    self.put_expression(left, context, true)?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(right, context, true)?;\n                    write!(self.out, \")\")?;\n                } else if op == crate::BinaryOperator::Modulo && kind == crate::ScalarKind::Float {\n                    // TODO: handle undefined behavior of BinaryOperator::Modulo\n                    //\n                    // float:\n                    // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798\n                    write!(self.out, \"{NAMESPACE}::fmod(\")?;\n                    self.put_expression(left, context, true)?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(right, context, true)?;\n                    write!(self.out, \")\")?;\n                } else if (op == crate::BinaryOperator::Add\n                    || op == crate::BinaryOperator::Subtract\n                    || op == crate::BinaryOperator::Multiply)\n                    && kind == crate::ScalarKind::Sint\n                {\n                    let to_unsigned = |ty: &crate::TypeInner| match *ty {\n                        crate::TypeInner::Scalar(scalar) => {\n                            Ok(crate::TypeInner::Scalar(crate::Scalar {\n                                kind: crate::ScalarKind::Uint,\n                                ..scalar\n                            }))\n                        }\n                        crate::TypeInner::Vector { size, scalar } => Ok(crate::TypeInner::Vector {\n                            size,\n                            scalar: crate::Scalar {\n                                kind: crate::ScalarKind::Uint,\n                                ..scalar\n                            },\n                        }),\n                        _ => Err(Error::UnsupportedBitCast(ty.clone())),\n                    };\n\n                    // Avoid undefined behaviour due to overflowing signed\n                    // integer arithmetic. Cast the operands to unsigned prior\n                    // to performing the operation, then cast the result back\n                    // to signed.\n                    self.put_bitcasted_expression(\n                        context.resolve_type(expr_handle),\n                        context,\n                        &|writer, context, is_scoped| {\n                            writer.put_binop(\n                                op,\n                                left,\n                                right,\n                                context,\n                                is_scoped,\n                                &|writer, expr, context, _is_scoped| {\n                                    writer.put_bitcasted_expression(\n                                        &to_unsigned(context.resolve_type(expr))?,\n                                        context,\n                                        &|writer, context, is_scoped| {\n                                            writer.put_expression(expr, context, is_scoped)\n                                        },\n                                    )\n                                },\n                            )\n                        },\n                    )?;\n                } else {\n                    self.put_binop(op, left, right, context, is_scoped, &Self::put_expression)?;\n                }\n            }\n            crate::Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => match *context.resolve_type(condition) {\n                crate::TypeInner::Scalar(crate::Scalar {\n                    kind: crate::ScalarKind::Bool,\n                    ..\n                }) => {\n                    if !is_scoped {\n                        write!(self.out, \"(\")?;\n                    }\n                    self.put_expression(condition, context, false)?;\n                    write!(self.out, \" ? \")?;\n                    self.put_expression(accept, context, false)?;\n                    write!(self.out, \" : \")?;\n                    self.put_expression(reject, context, false)?;\n                    if !is_scoped {\n                        write!(self.out, \")\")?;\n                    }\n                }\n                crate::TypeInner::Vector {\n                    scalar:\n                        crate::Scalar {\n                            kind: crate::ScalarKind::Bool,\n                            ..\n                        },\n                    ..\n                } => {\n                    write!(self.out, \"{NAMESPACE}::select(\")?;\n                    self.put_expression(reject, context, true)?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(accept, context, true)?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(condition, context, true)?;\n                    write!(self.out, \")\")?;\n                }\n                ref ty => {\n                    return Err(Error::GenericValidation(format!(\n                        \"Expected select condition to be a non-bool type, got {ty:?}\",\n                    )))\n                }\n            },\n            crate::Expression::Derivative { axis, expr, .. } => {\n                use crate::DerivativeAxis as Axis;\n                let op = match axis {\n                    Axis::X => \"dfdx\",\n                    Axis::Y => \"dfdy\",\n                    Axis::Width => \"fwidth\",\n                };\n                write!(self.out, \"{NAMESPACE}::{op}\")?;\n                self.put_call_parameters(iter::once(expr), context)?;\n            }\n            crate::Expression::Relational { fun, argument } => {\n                let op = match fun {\n                    crate::RelationalFunction::Any => \"any\",\n                    crate::RelationalFunction::All => \"all\",\n                    crate::RelationalFunction::IsNan => \"isnan\",\n                    crate::RelationalFunction::IsInf => \"isinf\",\n                };\n                write!(self.out, \"{NAMESPACE}::{op}\")?;\n                self.put_call_parameters(iter::once(argument), context)?;\n            }\n            crate::Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                use crate::MathFunction as Mf;\n\n                let arg_type = context.resolve_type(arg);\n                let scalar_argument = match arg_type {\n                    &crate::TypeInner::Scalar(_) => true,\n                    _ => false,\n                };\n\n                let fun_name = match fun {\n                    // comparison\n                    Mf::Abs => \"abs\",\n                    Mf::Min => \"min\",\n                    Mf::Max => \"max\",\n                    Mf::Clamp => \"clamp\",\n                    Mf::Saturate => \"saturate\",\n                    // trigonometry\n                    Mf::Cos => \"cos\",\n                    Mf::Cosh => \"cosh\",\n                    Mf::Sin => \"sin\",\n                    Mf::Sinh => \"sinh\",\n                    Mf::Tan => \"tan\",\n                    Mf::Tanh => \"tanh\",\n                    Mf::Acos => \"acos\",\n                    Mf::Asin => \"asin\",\n                    Mf::Atan => \"atan\",\n                    Mf::Atan2 => \"atan2\",\n                    Mf::Asinh => \"asinh\",\n                    Mf::Acosh => \"acosh\",\n                    Mf::Atanh => \"atanh\",\n                    Mf::Radians => \"\",\n                    Mf::Degrees => \"\",\n                    // decomposition\n                    Mf::Ceil => \"ceil\",\n                    Mf::Floor => \"floor\",\n                    Mf::Round => \"rint\",\n                    Mf::Fract => \"fract\",\n                    Mf::Trunc => \"trunc\",\n                    Mf::Modf => MODF_FUNCTION,\n                    Mf::Frexp => FREXP_FUNCTION,\n                    Mf::Ldexp => \"ldexp\",\n                    // exponent\n                    Mf::Exp => \"exp\",\n                    Mf::Exp2 => \"exp2\",\n                    Mf::Log => \"log\",\n                    Mf::Log2 => \"log2\",\n                    Mf::Pow => \"pow\",\n                    // geometry\n                    Mf::Dot => match *context.resolve_type(arg) {\n                        crate::TypeInner::Vector {\n                            scalar:\n                                crate::Scalar {\n                                    // Resolve float values to MSL's builtin dot function.\n                                    kind: crate::ScalarKind::Float,\n                                    ..\n                                },\n                            ..\n                        } => \"dot\",\n                        crate::TypeInner::Vector {\n                            size,\n                            scalar:\n                                scalar @ crate::Scalar {\n                                    kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                                    ..\n                                },\n                        } => {\n                            // Integer vector dot: call our mangled helper `dot_{type}{N}(a, b)`.\n                            let fun_name = self.get_dot_wrapper_function_helper_name(scalar, size);\n                            write!(self.out, \"{fun_name}(\")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(arg1.unwrap(), context, true)?;\n                            write!(self.out, \")\")?;\n                            return Ok(());\n                        }\n                        _ => unreachable!(\n                            \"Correct TypeInner for dot product should be already validated\"\n                        ),\n                    },\n                    fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed) => {\n                        if context.lang_version >= (2, 1) {\n                            // Write potentially optimizable code using `packed_(u?)char4`.\n                            // The two function arguments were already reinterpreted as packed (signed\n                            // or unsigned) chars in `Self::put_block`.\n                            let packed_type = match fun {\n                                Mf::Dot4I8Packed => \"packed_char4\",\n                                Mf::Dot4U8Packed => \"packed_uchar4\",\n                                _ => unreachable!(),\n                            };\n\n                            return self.put_dot_product(\n                                Reinterpreted::new(packed_type, arg),\n                                Reinterpreted::new(packed_type, arg1.unwrap()),\n                                4,\n                                |writer, arg, index| {\n                                    // MSL implicitly promotes these (signed or unsigned) chars to\n                                    // `int` or `uint` in the multiplication, so no overflow can occur.\n                                    write!(writer.out, \"{arg}[{index}]\")?;\n                                    Ok(())\n                                },\n                            );\n                        } else {\n                            // Fall back to a polyfill since MSL < 2.1 doesn't seem to support\n                            // bitcasting from uint to `packed_char4` or `packed_uchar4`.\n                            // See <https://github.com/gfx-rs/wgpu/pull/7574#issuecomment-2835464472>.\n                            let conversion = match fun {\n                                Mf::Dot4I8Packed => \"int\",\n                                Mf::Dot4U8Packed => \"\",\n                                _ => unreachable!(),\n                            };\n\n                            return self.put_dot_product(\n                                arg,\n                                arg1.unwrap(),\n                                4,\n                                |writer, arg, index| {\n                                    write!(writer.out, \"({conversion}(\")?;\n                                    writer.put_expression(arg, context, true)?;\n                                    if index == 3 {\n                                        write!(writer.out, \") >> 24)\")?;\n                                    } else {\n                                        write!(writer.out, \") << {} >> 24)\", (3 - index) * 8)?;\n                                    }\n                                    Ok(())\n                                },\n                            );\n                        }\n                    }\n                    Mf::Outer => return Err(Error::UnsupportedCall(format!(\"{fun:?}\"))),\n                    Mf::Cross => \"cross\",\n                    Mf::Distance => \"distance\",\n                    Mf::Length if scalar_argument => \"abs\",\n                    Mf::Length => \"length\",\n                    Mf::Normalize => \"normalize\",\n                    Mf::FaceForward => \"faceforward\",\n                    Mf::Reflect => \"reflect\",\n                    Mf::Refract => \"refract\",\n                    // computational\n                    Mf::Sign => match arg_type.scalar_kind() {\n                        Some(crate::ScalarKind::Sint) => {\n                            return self.put_isign(arg, context);\n                        }\n                        _ => \"sign\",\n                    },\n                    Mf::Fma => \"fma\",\n                    Mf::Mix => \"mix\",\n                    Mf::Step => \"step\",\n                    Mf::SmoothStep => \"smoothstep\",\n                    Mf::Sqrt => \"sqrt\",\n                    Mf::InverseSqrt => \"rsqrt\",\n                    Mf::Inverse => return Err(Error::UnsupportedCall(format!(\"{fun:?}\"))),\n                    Mf::Transpose => \"transpose\",\n                    Mf::Determinant => \"determinant\",\n                    Mf::QuantizeToF16 => \"\",\n                    // bits\n                    Mf::CountTrailingZeros => \"ctz\",\n                    Mf::CountLeadingZeros => \"clz\",\n                    Mf::CountOneBits => \"popcount\",\n                    Mf::ReverseBits => \"reverse_bits\",\n                    Mf::ExtractBits => \"\",\n                    Mf::InsertBits => \"\",\n                    Mf::FirstTrailingBit => \"\",\n                    Mf::FirstLeadingBit => \"\",\n                    // data packing\n                    Mf::Pack4x8snorm => \"pack_float_to_snorm4x8\",\n                    Mf::Pack4x8unorm => \"pack_float_to_unorm4x8\",\n                    Mf::Pack2x16snorm => \"pack_float_to_snorm2x16\",\n                    Mf::Pack2x16unorm => \"pack_float_to_unorm2x16\",\n                    Mf::Pack2x16float => \"\",\n                    Mf::Pack4xI8 => \"\",\n                    Mf::Pack4xU8 => \"\",\n                    Mf::Pack4xI8Clamp => \"\",\n                    Mf::Pack4xU8Clamp => \"\",\n                    // data unpacking\n                    Mf::Unpack4x8snorm => \"unpack_snorm4x8_to_float\",\n                    Mf::Unpack4x8unorm => \"unpack_unorm4x8_to_float\",\n                    Mf::Unpack2x16snorm => \"unpack_snorm2x16_to_float\",\n                    Mf::Unpack2x16unorm => \"unpack_unorm2x16_to_float\",\n                    Mf::Unpack2x16float => \"\",\n                    Mf::Unpack4xI8 => \"\",\n                    Mf::Unpack4xU8 => \"\",\n                };\n\n                match fun {\n                    Mf::ReverseBits | Mf::ExtractBits | Mf::InsertBits => {\n                        // reverse_bits is listed as requiring MSL 2.1 but that\n                        // is a copy/paste error. Looking at previous snapshots\n                        // on web.archive.org it's present in MSL 1.2.\n                        //\n                        // https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/WhatsNewiniOS10tvOS10andOSX1012/WhatsNewiniOS10tvOS10andOSX1012.html\n                        // also talks about MSL 1.2 adding \"New integer\n                        // functions to extract, insert, and reverse bits, as\n                        // described in Integer Functions.\"\n                        if context.lang_version < (1, 2) {\n                            return Err(Error::UnsupportedFunction(fun_name.to_string()));\n                        }\n                    }\n                    _ => {}\n                }\n\n                match fun {\n                    Mf::Abs if arg_type.scalar_kind() == Some(crate::ScalarKind::Sint) => {\n                        write!(self.out, \"{ABS_FUNCTION}(\")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \")\")?;\n                    }\n                    Mf::Distance if scalar_argument => {\n                        write!(self.out, \"{NAMESPACE}::abs(\")?;\n                        self.put_expression(arg, context, false)?;\n                        write!(self.out, \" - \")?;\n                        self.put_expression(arg1.unwrap(), context, false)?;\n                        write!(self.out, \")\")?;\n                    }\n                    Mf::FirstTrailingBit => {\n                        let scalar = context.resolve_type(arg).scalar().unwrap();\n                        let constant = scalar.width * 8 + 1;\n\n                        write!(self.out, \"((({NAMESPACE}::ctz(\")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \") + 1) % {constant}) - 1)\")?;\n                    }\n                    Mf::FirstLeadingBit => {\n                        let inner = context.resolve_type(arg);\n                        let scalar = inner.scalar().unwrap();\n                        let constant = scalar.width * 8 - 1;\n\n                        write!(\n                            self.out,\n                            \"{NAMESPACE}::select({constant} - {NAMESPACE}::clz(\"\n                        )?;\n\n                        if scalar.kind == crate::ScalarKind::Sint {\n                            write!(self.out, \"{NAMESPACE}::select(\")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \", ~\")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \" < 0)\")?;\n                        } else {\n                            self.put_expression(arg, context, true)?;\n                        }\n\n                        write!(self.out, \"), \")?;\n\n                        // or metal will complain that select is ambiguous\n                        match *inner {\n                            crate::TypeInner::Vector { size, scalar } => {\n                                let size = common::vector_size_str(size);\n                                let name = scalar.to_msl_name();\n                                write!(self.out, \"{name}{size}\")?;\n                            }\n                            crate::TypeInner::Scalar(scalar) => {\n                                let name = scalar.to_msl_name();\n                                write!(self.out, \"{name}\")?;\n                            }\n                            _ => (),\n                        }\n\n                        write!(self.out, \"(-1), \")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \" == 0 || \")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \" == -1)\")?;\n                    }\n                    Mf::Unpack2x16float => {\n                        write!(self.out, \"float2(as_type<half2>(\")?;\n                        self.put_expression(arg, context, false)?;\n                        write!(self.out, \"))\")?;\n                    }\n                    Mf::Pack2x16float => {\n                        write!(self.out, \"as_type<uint>(half2(\")?;\n                        self.put_expression(arg, context, false)?;\n                        write!(self.out, \"))\")?;\n                    }\n                    Mf::ExtractBits => {\n                        // The behavior of ExtractBits is undefined when offset + count > bit_width. We need\n                        // to first sanitize the offset and count first. If we don't do this, Apple chips\n                        // will return out-of-spec values if the extracted range is not within the bit width.\n                        //\n                        // This encodes the exact formula specified by the wgsl spec, without temporary values:\n                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin\n                        //\n                        // w = sizeof(x) * 8\n                        // o = min(offset, w)\n                        // tmp = w - o\n                        // c = min(count, tmp)\n                        //\n                        // bitfieldExtract(x, o, c)\n                        //\n                        // extract_bits(e, min(offset, w), min(count, w - min(offset, w))))\n\n                        let scalar_bits = context.resolve_type(arg).scalar_width().unwrap() * 8;\n\n                        write!(self.out, \"{NAMESPACE}::extract_bits(\")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \", {NAMESPACE}::min(\")?;\n                        self.put_expression(arg1.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u), {NAMESPACE}::min(\")?;\n                        self.put_expression(arg2.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u - {NAMESPACE}::min(\")?;\n                        self.put_expression(arg1.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u)))\")?;\n                    }\n                    Mf::InsertBits => {\n                        // The behavior of InsertBits has the same issue as ExtractBits.\n                        //\n                        // insertBits(e, newBits, min(offset, w), min(count, w - min(offset, w))))\n\n                        let scalar_bits = context.resolve_type(arg).scalar_width().unwrap() * 8;\n\n                        write!(self.out, \"{NAMESPACE}::insert_bits(\")?;\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \", \")?;\n                        self.put_expression(arg1.unwrap(), context, true)?;\n                        write!(self.out, \", {NAMESPACE}::min(\")?;\n                        self.put_expression(arg2.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u), {NAMESPACE}::min(\")?;\n                        self.put_expression(arg3.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u - {NAMESPACE}::min(\")?;\n                        self.put_expression(arg2.unwrap(), context, true)?;\n                        write!(self.out, \", {scalar_bits}u)))\")?;\n                    }\n                    Mf::Radians => {\n                        write!(self.out, \"((\")?;\n                        self.put_expression(arg, context, false)?;\n                        write!(self.out, \") * 0.017453292519943295474)\")?;\n                    }\n                    Mf::Degrees => {\n                        write!(self.out, \"((\")?;\n                        self.put_expression(arg, context, false)?;\n                        write!(self.out, \") * 57.295779513082322865)\")?;\n                    }\n                    Mf::Modf | Mf::Frexp => {\n                        write!(self.out, \"{fun_name}\")?;\n                        self.put_call_parameters(iter::once(arg), context)?;\n                    }\n                    Mf::Pack4xI8 => self.put_pack4x8(arg, context, true, None)?,\n                    Mf::Pack4xU8 => self.put_pack4x8(arg, context, false, None)?,\n                    Mf::Pack4xI8Clamp => {\n                        self.put_pack4x8(arg, context, true, Some((\"-128\", \"127\")))?\n                    }\n                    Mf::Pack4xU8Clamp => {\n                        self.put_pack4x8(arg, context, false, Some((\"0\", \"255\")))?\n                    }\n                    fun @ (Mf::Unpack4xI8 | Mf::Unpack4xU8) => {\n                        let sign_prefix = if matches!(fun, Mf::Unpack4xU8) {\n                            \"u\"\n                        } else {\n                            \"\"\n                        };\n\n                        if context.lang_version >= (2, 1) {\n                            // Metal uses little endian byte order, which matches what WGSL expects here.\n                            write!(\n                                self.out,\n                                \"{sign_prefix}int4(as_type<packed_{sign_prefix}char4>(\"\n                            )?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \"))\")?;\n                        } else {\n                            // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.\n                            write!(self.out, \"({sign_prefix}int4(\")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \" >> 8, \")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \" >> 16, \")?;\n                            self.put_expression(arg, context, true)?;\n                            write!(self.out, \" >> 24) << 24 >> 24)\")?;\n                        }\n                    }\n                    Mf::QuantizeToF16 => {\n                        match *context.resolve_type(arg) {\n                            crate::TypeInner::Scalar { .. } => write!(self.out, \"float(half(\")?,\n                            crate::TypeInner::Vector { size, .. } => write!(\n                                self.out,\n                                \"{NAMESPACE}::float{size}({NAMESPACE}::half{size}(\",\n                                size = common::vector_size_str(size),\n                            )?,\n                            _ => unreachable!(\n                                \"Correct TypeInner for QuantizeToF16 should be already validated\"\n                            ),\n                        };\n\n                        self.put_expression(arg, context, true)?;\n                        write!(self.out, \"))\")?;\n                    }\n                    _ => {\n                        write!(self.out, \"{NAMESPACE}::{fun_name}\")?;\n                        self.put_call_parameters(\n                            iter::once(arg).chain(arg1).chain(arg2).chain(arg3),\n                            context,\n                        )?;\n                    }\n                }\n            }\n            crate::Expression::As {\n                expr,\n                kind,\n                convert,\n            } => match *context.resolve_type(expr) {\n                crate::TypeInner::Scalar(src) | crate::TypeInner::Vector { scalar: src, .. } => {\n                    if src.kind == crate::ScalarKind::Float\n                        && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint)\n                        && convert.is_some()\n                    {\n                        // Use helper functions for float to int casts in order to avoid\n                        // undefined behaviour when value is out of range for the target\n                        // type.\n                        let fun_name = match (kind, convert) {\n                            (crate::ScalarKind::Sint, Some(4)) => F2I32_FUNCTION,\n                            (crate::ScalarKind::Uint, Some(4)) => F2U32_FUNCTION,\n                            (crate::ScalarKind::Sint, Some(8)) => F2I64_FUNCTION,\n                            (crate::ScalarKind::Uint, Some(8)) => F2U64_FUNCTION,\n                            _ => unreachable!(),\n                        };\n                        write!(self.out, \"{fun_name}(\")?;\n                        self.put_expression(expr, context, true)?;\n                        write!(self.out, \")\")?;\n                    } else {\n                        let target_scalar = crate::Scalar {\n                            kind,\n                            width: convert.unwrap_or(src.width),\n                        };\n                        let op = match convert {\n                            Some(_) => \"static_cast\",\n                            None => \"as_type\",\n                        };\n                        write!(self.out, \"{op}<\")?;\n                        match *context.resolve_type(expr) {\n                            crate::TypeInner::Vector { size, .. } => {\n                                put_numeric_type(&mut self.out, target_scalar, &[size])?\n                            }\n                            _ => put_numeric_type(&mut self.out, target_scalar, &[])?,\n                        };\n                        write!(self.out, \">(\")?;\n                        self.put_expression(expr, context, true)?;\n                        write!(self.out, \")\")?;\n                    }\n                }\n                crate::TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar,\n                } => {\n                    let target_scalar = crate::Scalar {\n                        kind,\n                        width: convert.unwrap_or(scalar.width),\n                    };\n                    put_numeric_type(&mut self.out, target_scalar, &[rows, columns])?;\n                    write!(self.out, \"(\")?;\n                    self.put_expression(expr, context, true)?;\n                    write!(self.out, \")\")?;\n                }\n                ref ty => {\n                    return Err(Error::GenericValidation(format!(\n                        \"Unsupported type for As: {ty:?}\"\n                    )))\n                }\n            },\n            // has to be a named expression\n            crate::Expression::CallResult(_)\n            | crate::Expression::AtomicResult { .. }\n            | crate::Expression::WorkGroupUniformLoadResult { .. }\n            | crate::Expression::SubgroupBallotResult\n            | crate::Expression::SubgroupOperationResult { .. }\n            | crate::Expression::RayQueryProceedResult => {\n                unreachable!()\n            }\n            crate::Expression::ArrayLength(expr) => {\n                // Find the global to which the array belongs.\n                let global = match context.function.expressions[expr] {\n                    crate::Expression::AccessIndex { base, .. } => {\n                        match context.function.expressions[base] {\n                            crate::Expression::GlobalVariable(handle) => handle,\n                            ref ex => {\n                                return Err(Error::GenericValidation(format!(\n                                    \"Expected global variable in AccessIndex, got {ex:?}\"\n                                )))\n                            }\n                        }\n                    }\n                    crate::Expression::GlobalVariable(handle) => handle,\n                    ref ex => {\n                        return Err(Error::GenericValidation(format!(\n                            \"Unexpected expression in ArrayLength, got {ex:?}\"\n                        )))\n                    }\n                };\n\n                if !is_scoped {\n                    write!(self.out, \"(\")?;\n                }\n                write!(self.out, \"1 + \")?;\n                self.put_dynamic_array_max_index(global, context)?;\n                if !is_scoped {\n                    write!(self.out, \")\")?;\n                }\n            }\n            crate::Expression::RayQueryVertexPositions { .. } => {\n                unimplemented!()\n            }\n            crate::Expression::RayQueryGetIntersection {\n                query,\n                committed: _,\n            } => {\n                if context.lang_version < (2, 4) {\n                    return Err(Error::UnsupportedRayTracing);\n                }\n\n                let ty = context.module.special_types.ray_intersection.unwrap();\n                let type_name = &self.names[&NameKey::Type(ty)];\n                write!(self.out, \"{type_name} {{{RAY_QUERY_FUN_MAP_INTERSECTION}(\")?;\n                self.put_expression(query, context, true)?;\n                write!(self.out, \".{RAY_QUERY_FIELD_INTERSECTION}.type)\")?;\n                let fields = [\n                    \"distance\",\n                    \"user_instance_id\", // req Metal 2.4\n                    \"instance_id\",\n                    \"\", // SBT offset\n                    \"geometry_id\",\n                    \"primitive_id\",\n                    \"triangle_barycentric_coord\",\n                    \"triangle_front_facing\",\n                    \"\",                          // padding\n                    \"object_to_world_transform\", // req Metal 2.4\n                    \"world_to_object_transform\", // req Metal 2.4\n                ];\n                for field in fields {\n                    write!(self.out, \", \")?;\n                    if field.is_empty() {\n                        write!(self.out, \"{{}}\")?;\n                    } else {\n                        self.put_expression(query, context, true)?;\n                        write!(self.out, \".{RAY_QUERY_FIELD_INTERSECTION}.{field}\")?;\n                    }\n                }\n                write!(self.out, \"}}\")?;\n            }\n            crate::Expression::CooperativeLoad { ref data, .. } => {\n                if context.lang_version < (2, 3) {\n                    return Err(Error::UnsupportedCooperativeMatrix);\n                }\n                write!(self.out, \"{COOPERATIVE_LOAD_FUNCTION}(\")?;\n                write!(self.out, \"&\")?;\n                self.put_access_chain(data.pointer, context.policies.index, context)?;\n                write!(self.out, \", \")?;\n                self.put_expression(data.stride, context, true)?;\n                write!(self.out, \", {})\", data.row_major)?;\n            }\n            crate::Expression::CooperativeMultiplyAdd { a, b, c } => {\n                if context.lang_version < (2, 3) {\n                    return Err(Error::UnsupportedCooperativeMatrix);\n                }\n                write!(self.out, \"{COOPERATIVE_MULTIPLY_ADD_FUNCTION}(\")?;\n                self.put_expression(a, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(b, context, true)?;\n                write!(self.out, \", \")?;\n                self.put_expression(c, context, true)?;\n                write!(self.out, \")\")?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Emits code for a binary operation, using the provided callback to emit\n    /// the left and right operands.\n    fn put_binop<F>(\n        &mut self,\n        op: crate::BinaryOperator,\n        left: Handle<crate::Expression>,\n        right: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        is_scoped: bool,\n        put_expression: &F,\n    ) -> BackendResult\n    where\n        F: Fn(&mut Self, Handle<crate::Expression>, &ExpressionContext, bool) -> BackendResult,\n    {\n        let op_str = back::binary_operation_str(op);\n\n        if !is_scoped {\n            write!(self.out, \"(\")?;\n        }\n\n        // Cast packed vector if necessary\n        // Packed vector - matrix multiplications are not supported in MSL\n        if op == crate::BinaryOperator::Multiply\n            && matches!(\n                context.resolve_type(right),\n                &crate::TypeInner::Matrix { .. }\n            )\n        {\n            self.put_wrapped_expression_for_packed_vec3_access(\n                left,\n                context,\n                false,\n                put_expression,\n            )?;\n        } else {\n            put_expression(self, left, context, false)?;\n        }\n\n        write!(self.out, \" {op_str} \")?;\n\n        // See comment above\n        if op == crate::BinaryOperator::Multiply\n            && matches!(context.resolve_type(left), &crate::TypeInner::Matrix { .. })\n        {\n            self.put_wrapped_expression_for_packed_vec3_access(\n                right,\n                context,\n                false,\n                put_expression,\n            )?;\n        } else {\n            put_expression(self, right, context, false)?;\n        }\n\n        if !is_scoped {\n            write!(self.out, \")\")?;\n        }\n\n        Ok(())\n    }\n\n    /// Used by expressions like Swizzle and Binary since they need packed_vec3's to be casted to a vec3\n    fn put_wrapped_expression_for_packed_vec3_access<F>(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        is_scoped: bool,\n        put_expression: &F,\n    ) -> BackendResult\n    where\n        F: Fn(&mut Self, Handle<crate::Expression>, &ExpressionContext, bool) -> BackendResult,\n    {\n        if let Some(scalar) = context.get_packed_vec_kind(expr_handle) {\n            write!(self.out, \"{}::{}3(\", NAMESPACE, scalar.to_msl_name())?;\n            put_expression(self, expr_handle, context, is_scoped)?;\n            write!(self.out, \")\")?;\n        } else {\n            put_expression(self, expr_handle, context, is_scoped)?;\n        }\n        Ok(())\n    }\n\n    /// Emits code for an expression using the provided callback, wrapping the\n    /// result in a bitcast to the type `cast_to`.\n    fn put_bitcasted_expression<F>(\n        &mut self,\n        cast_to: &crate::TypeInner,\n        context: &ExpressionContext,\n        put_expression: &F,\n    ) -> BackendResult\n    where\n        F: Fn(&mut Self, &ExpressionContext, bool) -> BackendResult,\n    {\n        write!(self.out, \"as_type<\")?;\n        match *cast_to {\n            crate::TypeInner::Scalar(scalar) => put_numeric_type(&mut self.out, scalar, &[])?,\n            crate::TypeInner::Vector { size, scalar } => {\n                put_numeric_type(&mut self.out, scalar, &[size])?\n            }\n            _ => return Err(Error::UnsupportedBitCast(cast_to.clone())),\n        };\n        write!(self.out, \">(\")?;\n        put_expression(self, context, true)?;\n        write!(self.out, \")\")?;\n\n        Ok(())\n    }\n\n    /// Write a `GuardedIndex` as a Metal expression.\n    fn put_index(\n        &mut self,\n        index: index::GuardedIndex,\n        context: &ExpressionContext,\n        is_scoped: bool,\n    ) -> BackendResult {\n        match index {\n            index::GuardedIndex::Expression(expr) => {\n                self.put_expression(expr, context, is_scoped)?\n            }\n            index::GuardedIndex::Known(value) => write!(self.out, \"{value}\")?,\n        }\n        Ok(())\n    }\n\n    /// Emit an index bounds check condition for `chain`, if required.\n    ///\n    /// `chain` is a subtree of `Access` and `AccessIndex` expressions,\n    /// operating either on a pointer to a value, or on a value directly. If we cannot\n    /// statically determine that all indexing operations in `chain` are within\n    /// bounds, then write a conditional expression to check them dynamically,\n    /// and return true. All accesses in the chain are checked by the generated\n    /// expression.\n    ///\n    /// This assumes that the [`BoundsCheckPolicy`] for `chain` is [`ReadZeroSkipWrite`].\n    ///\n    /// The text written is of the form:\n    ///\n    /// ```ignore\n    /// {level}{prefix}uint(i) < 4 && uint(j) < 10\n    /// ```\n    ///\n    /// where `{level}` and `{prefix}` are the arguments to this function. For [`Store`]\n    /// statements, presumably these arguments start an indented `if` statement; for\n    /// [`Load`] expressions, the caller is probably building up a ternary `?:`\n    /// expression. In either case, what is written is not a complete syntactic structure\n    /// in its own right, and the caller will have to finish it off if we return `true`.\n    ///\n    /// If no expression is written, return false.\n    ///\n    /// [`BoundsCheckPolicy`]: index::BoundsCheckPolicy\n    /// [`ReadZeroSkipWrite`]: index::BoundsCheckPolicy::ReadZeroSkipWrite\n    /// [`Store`]: crate::Statement::Store\n    /// [`Load`]: crate::Expression::Load\n    fn put_bounds_checks(\n        &mut self,\n        chain: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        level: back::Level,\n        prefix: &'static str,\n    ) -> Result<bool, Error> {\n        let mut check_written = false;\n\n        // Iterate over the access chain, handling each required bounds check.\n        for item in context.bounds_check_iter(chain) {\n            let BoundsCheck {\n                base,\n                index,\n                length,\n            } = item;\n\n            if check_written {\n                write!(self.out, \" && \")?;\n            } else {\n                write!(self.out, \"{level}{prefix}\")?;\n                check_written = true;\n            }\n\n            // Check that the index falls within bounds. Do this with a single\n            // comparison, by casting the index to `uint` first, so that negative\n            // indices become large positive values.\n            write!(self.out, \"uint(\")?;\n            self.put_index(index, context, true)?;\n            self.out.write_str(\") < \")?;\n            match length {\n                index::IndexableLength::Known(value) => write!(self.out, \"{value}\")?,\n                index::IndexableLength::Dynamic => {\n                    let global = context.function.originating_global(base).ok_or_else(|| {\n                        Error::GenericValidation(\"Could not find originating global\".into())\n                    })?;\n                    write!(self.out, \"1 + \")?;\n                    self.put_dynamic_array_max_index(global, context)?\n                }\n            }\n        }\n\n        Ok(check_written)\n    }\n\n    /// Write the access chain `chain`.\n    ///\n    /// `chain` is a subtree of [`Access`] and [`AccessIndex`] expressions,\n    /// operating either on a pointer to a value, or on a value directly.\n    ///\n    /// Generate bounds checks code only if `policy` is [`Restrict`]. The\n    /// [`ReadZeroSkipWrite`] policy requires checks before any accesses take place, so\n    /// that must be handled in the caller.\n    ///\n    /// Handle the entire chain, recursing back into `put_expression` only for index\n    /// expressions and the base expression that originates the pointer or composite value\n    /// being accessed. This allows `put_expression` to assume that any `Access` or\n    /// `AccessIndex` expressions it sees are the top of a chain, so it can emit\n    /// `ReadZeroSkipWrite` checks.\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    /// [`Restrict`]: crate::proc::index::BoundsCheckPolicy::Restrict\n    /// [`ReadZeroSkipWrite`]: crate::proc::index::BoundsCheckPolicy::ReadZeroSkipWrite\n    fn put_access_chain(\n        &mut self,\n        chain: Handle<crate::Expression>,\n        policy: index::BoundsCheckPolicy,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        match context.function.expressions[chain] {\n            crate::Expression::Access { base, index } => {\n                let mut base_ty = context.resolve_type(base);\n\n                // Look through any pointers to see what we're really indexing.\n                if let crate::TypeInner::Pointer { base, space: _ } = *base_ty {\n                    base_ty = &context.module.types[base].inner;\n                }\n\n                self.put_subscripted_access_chain(\n                    base,\n                    base_ty,\n                    index::GuardedIndex::Expression(index),\n                    policy,\n                    context,\n                )?;\n            }\n            crate::Expression::AccessIndex { base, index } => {\n                let base_resolution = &context.info[base].ty;\n                let mut base_ty = base_resolution.inner_with(&context.module.types);\n                let mut base_ty_handle = base_resolution.handle();\n\n                // Look through any pointers to see what we're really indexing.\n                if let crate::TypeInner::Pointer { base, space: _ } = *base_ty {\n                    base_ty = &context.module.types[base].inner;\n                    base_ty_handle = Some(base);\n                }\n\n                // Handle structs and anything else that can use `.x` syntax here, so\n                // `put_subscripted_access_chain` won't have to handle the absurd case of\n                // indexing a struct with an expression.\n                match *base_ty {\n                    crate::TypeInner::Struct { .. } => {\n                        let base_ty = base_ty_handle.unwrap();\n                        self.put_access_chain(base, policy, context)?;\n                        let name = &self.names[&NameKey::StructMember(base_ty, index)];\n                        write!(self.out, \".{name}\")?;\n                    }\n                    crate::TypeInner::ValuePointer { .. } | crate::TypeInner::Vector { .. } => {\n                        self.put_access_chain(base, policy, context)?;\n                        // Prior to Metal v2.1 component access for packed vectors wasn't available\n                        // however array indexing is\n                        if context.get_packed_vec_kind(base).is_some() {\n                            write!(self.out, \"[{index}]\")?;\n                        } else {\n                            write!(self.out, \".{}\", back::COMPONENTS[index as usize])?;\n                        }\n                    }\n                    _ => {\n                        self.put_subscripted_access_chain(\n                            base,\n                            base_ty,\n                            index::GuardedIndex::Known(index),\n                            policy,\n                            context,\n                        )?;\n                    }\n                }\n            }\n            _ => self.put_expression(chain, context, false)?,\n        }\n\n        Ok(())\n    }\n\n    /// Write a `[]`-style access of `base` by `index`.\n    ///\n    /// If `policy` is [`Restrict`], then generate code as needed to force all index\n    /// values within bounds.\n    ///\n    /// The `base_ty` argument must be the type we are actually indexing, like [`Array`] or\n    /// [`Vector`]. In other words, it's `base`'s type with any surrounding [`Pointer`]\n    /// removed. Our callers often already have this handy.\n    ///\n    /// This only emits `[]` expressions; it doesn't handle struct member accesses or\n    /// referencing vector components by name.\n    ///\n    /// [`Restrict`]: crate::proc::index::BoundsCheckPolicy::Restrict\n    /// [`Array`]: crate::TypeInner::Array\n    /// [`Vector`]: crate::TypeInner::Vector\n    /// [`Pointer`]: crate::TypeInner::Pointer\n    fn put_subscripted_access_chain(\n        &mut self,\n        base: Handle<crate::Expression>,\n        base_ty: &crate::TypeInner,\n        index: index::GuardedIndex,\n        policy: index::BoundsCheckPolicy,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        let accessing_wrapped_array = match *base_ty {\n            crate::TypeInner::Array {\n                size: crate::ArraySize::Constant(_) | crate::ArraySize::Pending(_),\n                ..\n            } => true,\n            _ => false,\n        };\n        let accessing_wrapped_binding_array =\n            matches!(*base_ty, crate::TypeInner::BindingArray { .. });\n\n        self.put_access_chain(base, policy, context)?;\n        if accessing_wrapped_array {\n            write!(self.out, \".{WRAPPED_ARRAY_FIELD}\")?;\n        }\n        write!(self.out, \"[\")?;\n\n        // Decide whether this index needs to be clamped to fall within range.\n        let restriction_needed = if policy == index::BoundsCheckPolicy::Restrict {\n            context.access_needs_check(base, index)\n        } else {\n            None\n        };\n        if let Some(limit) = restriction_needed {\n            write!(self.out, \"{NAMESPACE}::min(unsigned(\")?;\n            self.put_index(index, context, true)?;\n            write!(self.out, \"), \")?;\n            match limit {\n                index::IndexableLength::Known(limit) => {\n                    write!(self.out, \"{}u\", limit - 1)?;\n                }\n                index::IndexableLength::Dynamic => {\n                    let global = context.function.originating_global(base).ok_or_else(|| {\n                        Error::GenericValidation(\"Could not find originating global\".into())\n                    })?;\n                    self.put_dynamic_array_max_index(global, context)?;\n                }\n            }\n            write!(self.out, \")\")?;\n        } else {\n            self.put_index(index, context, true)?;\n        }\n\n        write!(self.out, \"]\")?;\n\n        if accessing_wrapped_binding_array {\n            write!(self.out, \".{WRAPPED_ARRAY_FIELD}\")?;\n        }\n\n        Ok(())\n    }\n\n    fn put_load(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        is_scoped: bool,\n    ) -> BackendResult {\n        // Since access chains never cross between address spaces, we can just\n        // check the index bounds check policy once at the top.\n        let policy = context.choose_bounds_check_policy(pointer);\n        if policy == index::BoundsCheckPolicy::ReadZeroSkipWrite\n            && self.put_bounds_checks(\n                pointer,\n                context,\n                back::Level(0),\n                if is_scoped { \"\" } else { \"(\" },\n            )?\n        {\n            write!(self.out, \" ? \")?;\n            self.put_unchecked_load(pointer, policy, context)?;\n            write!(self.out, \" : DefaultConstructible()\")?;\n\n            if !is_scoped {\n                write!(self.out, \")\")?;\n            }\n        } else {\n            self.put_unchecked_load(pointer, policy, context)?;\n        }\n\n        Ok(())\n    }\n\n    fn put_unchecked_load(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        policy: index::BoundsCheckPolicy,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        let is_atomic_pointer = context\n            .resolve_type(pointer)\n            .is_atomic_pointer(&context.module.types);\n\n        if is_atomic_pointer {\n            write!(\n                self.out,\n                \"{NAMESPACE}::atomic_load_explicit({ATOMIC_REFERENCE}\"\n            )?;\n            self.put_access_chain(pointer, policy, context)?;\n            write!(self.out, \", {NAMESPACE}::memory_order_relaxed)\")?;\n        } else {\n            // We don't do any dereferencing with `*` here as pointer arguments to functions\n            // are done by `&` references and not `*` pointers. These do not need to be\n            // dereferenced.\n            self.put_access_chain(pointer, policy, context)?;\n        }\n\n        Ok(())\n    }\n\n    fn put_return_value(\n        &mut self,\n        level: back::Level,\n        expr_handle: Handle<crate::Expression>,\n        result_struct: Option<&str>,\n        context: &ExpressionContext,\n    ) -> BackendResult {\n        match result_struct {\n            Some(struct_name) => {\n                let mut has_point_size = false;\n                let result_ty = context.function.result.as_ref().unwrap().ty;\n                match context.module.types[result_ty].inner {\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        let tmp = \"_tmp\";\n                        write!(self.out, \"{level}const auto {tmp} = \")?;\n                        self.put_expression(expr_handle, context, true)?;\n                        writeln!(self.out, \";\")?;\n                        write!(self.out, \"{level}return {struct_name} {{\")?;\n\n                        let mut is_first = true;\n\n                        for (index, member) in members.iter().enumerate() {\n                            if let Some(crate::Binding::BuiltIn(crate::BuiltIn::PointSize)) =\n                                member.binding\n                            {\n                                has_point_size = true;\n                                if !context.pipeline_options.allow_and_force_point_size {\n                                    continue;\n                                }\n                            }\n\n                            let comma = if is_first { \"\" } else { \",\" };\n                            is_first = false;\n                            let name = &self.names[&NameKey::StructMember(result_ty, index as u32)];\n                            // HACK: we are forcefully deduplicating the expression here\n                            // to convert from a wrapped struct to a raw array, e.g.\n                            // `float gl_ClipDistance1 [[clip_distance]] [1];`.\n                            if let crate::TypeInner::Array {\n                                size: crate::ArraySize::Constant(size),\n                                ..\n                            } = context.module.types[member.ty].inner\n                            {\n                                write!(self.out, \"{comma} {{\")?;\n                                for j in 0..size.get() {\n                                    if j != 0 {\n                                        write!(self.out, \",\")?;\n                                    }\n                                    write!(self.out, \"{tmp}.{name}.{WRAPPED_ARRAY_FIELD}[{j}]\")?;\n                                }\n                                write!(self.out, \"}}\")?;\n                            } else {\n                                write!(self.out, \"{comma} {tmp}.{name}\")?;\n                            }\n                        }\n                    }\n                    _ => {\n                        write!(self.out, \"{level}return {struct_name} {{ \")?;\n                        self.put_expression(expr_handle, context, true)?;\n                    }\n                }\n\n                if let FunctionOrigin::EntryPoint(ep_index) = context.origin {\n                    let stage = context.module.entry_points[ep_index as usize].stage;\n                    if context.pipeline_options.allow_and_force_point_size\n                        && stage == crate::ShaderStage::Vertex\n                        && !has_point_size\n                    {\n                        // point size was injected and comes last\n                        write!(self.out, \", 1.0\")?;\n                    }\n                }\n                write!(self.out, \" }}\")?;\n            }\n            None => {\n                write!(self.out, \"{level}return \")?;\n                self.put_expression(expr_handle, context, true)?;\n            }\n        }\n        writeln!(self.out, \";\")?;\n        Ok(())\n    }\n\n    /// Helper method used to find which expressions of a given function require baking\n    ///\n    /// # Notes\n    /// This function overwrites the contents of `self.need_bake_expressions`\n    fn update_expressions_to_bake(\n        &mut self,\n        func: &crate::Function,\n        info: &valid::FunctionInfo,\n        context: &ExpressionContext,\n    ) {\n        use crate::Expression;\n        self.need_bake_expressions.clear();\n\n        for (expr_handle, expr) in func.expressions.iter() {\n            // Expressions whose reference count is above the\n            // threshold should always be stored in temporaries.\n            let expr_info = &info[expr_handle];\n            let min_ref_count = func.expressions[expr_handle].bake_ref_count();\n            if min_ref_count <= expr_info.ref_count {\n                self.need_bake_expressions.insert(expr_handle);\n            } else {\n                match expr_info.ty {\n                    // force ray desc to be baked: it's used multiple times internally\n                    TypeResolution::Handle(h)\n                        if Some(h) == context.module.special_types.ray_desc =>\n                    {\n                        self.need_bake_expressions.insert(expr_handle);\n                    }\n                    _ => {}\n                }\n            }\n\n            if let Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                ..\n            } = *expr\n            {\n                match fun {\n                    // WGSL's `dot` function works on any `vecN` type, but Metal's only\n                    // works on floating-point vectors, so we emit inline code for\n                    // integer vector `dot` calls. But that code uses each argument `N`\n                    // times, once for each component (see `put_dot_product`), so to\n                    // avoid duplicated evaluation, we must bake integer operands.\n                    // This applies both when using the polyfill (because of the duplicate\n                    // evaluation issue) and when we don't use the polyfill (because we\n                    // need them to be emitted before casting to packed chars -- see the\n                    // comment at the call to `put_casting_to_packed_chars`).\n                    crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {\n                        self.need_bake_expressions.insert(arg);\n                        self.need_bake_expressions.insert(arg1.unwrap());\n                    }\n                    crate::MathFunction::FirstLeadingBit => {\n                        self.need_bake_expressions.insert(arg);\n                    }\n                    crate::MathFunction::Pack4xI8\n                    | crate::MathFunction::Pack4xU8\n                    | crate::MathFunction::Pack4xI8Clamp\n                    | crate::MathFunction::Pack4xU8Clamp\n                    | crate::MathFunction::Unpack4xI8\n                    | crate::MathFunction::Unpack4xU8 => {\n                        // On MSL < 2.1, we emit a polyfill for these functions that uses the\n                        // argument multiple times. This is no longer necessary on MSL >= 2.1.\n                        if context.lang_version < (2, 1) {\n                            self.need_bake_expressions.insert(arg);\n                        }\n                    }\n                    crate::MathFunction::ExtractBits => {\n                        // Only argument 1 is re-used.\n                        self.need_bake_expressions.insert(arg1.unwrap());\n                    }\n                    crate::MathFunction::InsertBits => {\n                        // Only argument 2 is re-used.\n                        self.need_bake_expressions.insert(arg2.unwrap());\n                    }\n                    crate::MathFunction::Sign => {\n                        // WGSL's `sign` function works also on signed ints, but Metal's only\n                        // works on floating points, so we emit inline code for integer `sign`\n                        // calls. But that code uses each argument 2 times (see `put_isign`),\n                        // so to avoid duplicated evaluation, we must bake the argument.\n                        let inner = context.resolve_type(expr_handle);\n                        if inner.scalar_kind() == Some(crate::ScalarKind::Sint) {\n                            self.need_bake_expressions.insert(arg);\n                        }\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n\n    fn start_baking_expression(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        context: &ExpressionContext,\n        name: &str,\n    ) -> BackendResult {\n        match context.info[handle].ty {\n            TypeResolution::Handle(ty_handle) => {\n                let ty_name = TypeContext {\n                    handle: ty_handle,\n                    gctx: context.module.to_ctx(),\n                    names: &self.names,\n                    access: crate::StorageAccess::empty(),\n                    first_time: false,\n                };\n                write!(self.out, \"{ty_name}\")?;\n            }\n            TypeResolution::Value(crate::TypeInner::Scalar(scalar)) => {\n                put_numeric_type(&mut self.out, scalar, &[])?;\n            }\n            TypeResolution::Value(crate::TypeInner::Vector { size, scalar }) => {\n                put_numeric_type(&mut self.out, scalar, &[size])?;\n            }\n            TypeResolution::Value(crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            }) => {\n                put_numeric_type(&mut self.out, scalar, &[rows, columns])?;\n            }\n            TypeResolution::Value(crate::TypeInner::CooperativeMatrix {\n                columns,\n                rows,\n                scalar,\n                role: _,\n            }) => {\n                write!(\n                    self.out,\n                    \"{}::simdgroup_{}{}x{}\",\n                    NAMESPACE,\n                    scalar.to_msl_name(),\n                    columns as u32,\n                    rows as u32,\n                )?;\n            }\n            TypeResolution::Value(ref other) => {\n                log::warn!(\"Type {other:?} isn't a known local\");\n                return Err(Error::FeatureNotImplemented(\"weird local type\".to_string()));\n            }\n        }\n\n        //TODO: figure out the naming scheme that wouldn't collide with user names.\n        write!(self.out, \" {name} = \")?;\n\n        Ok(())\n    }\n\n    /// Cache a clamped level of detail value, if necessary.\n    ///\n    /// [`ImageLoad`] accesses covered by [`BoundsCheckPolicy::Restrict`] use a\n    /// properly clamped level of detail value both in the access itself, and\n    /// for fetching the size of the requested MIP level, needed to clamp the\n    /// coordinates. To avoid recomputing this clamped level of detail, we cache\n    /// it in a temporary variable, as part of the [`Emit`] statement covering\n    /// the [`ImageLoad`] expression.\n    ///\n    /// [`ImageLoad`]: crate::Expression::ImageLoad\n    /// [`BoundsCheckPolicy::Restrict`]: index::BoundsCheckPolicy::Restrict\n    /// [`Emit`]: crate::Statement::Emit\n    fn put_cache_restricted_level(\n        &mut self,\n        load: Handle<crate::Expression>,\n        image: Handle<crate::Expression>,\n        mip_level: Option<Handle<crate::Expression>>,\n        indent: back::Level,\n        context: &StatementContext,\n    ) -> BackendResult {\n        // Does this image access actually require (or even permit) a\n        // level-of-detail, and does the policy require us to restrict it?\n        let level_of_detail = match mip_level {\n            Some(level) => level,\n            None => return Ok(()),\n        };\n\n        if context.expression.policies.image_load != index::BoundsCheckPolicy::Restrict\n            || !context.expression.image_needs_lod(image)\n        {\n            return Ok(());\n        }\n\n        write!(self.out, \"{}uint {} = \", indent, ClampedLod(load),)?;\n        self.put_restricted_scalar_image_index(\n            image,\n            level_of_detail,\n            \"get_num_mip_levels\",\n            &context.expression,\n        )?;\n        writeln!(self.out, \";\")?;\n\n        Ok(())\n    }\n\n    /// Convert the arguments of `Dot4{I, U}Packed` to `packed_(u?)char4`.\n    ///\n    /// Caches the results in temporary variables (whose names are derived from\n    /// the original variable names). This caching avoids the need to redo the\n    /// casting for each vector component when emitting the dot product.\n    fn put_casting_to_packed_chars(\n        &mut self,\n        fun: crate::MathFunction,\n        arg0: Handle<crate::Expression>,\n        arg1: Handle<crate::Expression>,\n        indent: back::Level,\n        context: &StatementContext<'_>,\n    ) -> Result<(), Error> {\n        let packed_type = match fun {\n            crate::MathFunction::Dot4I8Packed => \"packed_char4\",\n            crate::MathFunction::Dot4U8Packed => \"packed_uchar4\",\n            _ => unreachable!(),\n        };\n\n        for arg in [arg0, arg1] {\n            write!(\n                self.out,\n                \"{indent}{packed_type} {0} = as_type<{packed_type}>(\",\n                Reinterpreted::new(packed_type, arg)\n            )?;\n            self.put_expression(arg, &context.expression, true)?;\n            writeln!(self.out, \");\")?;\n        }\n\n        Ok(())\n    }\n\n    fn put_block(\n        &mut self,\n        level: back::Level,\n        statements: &[crate::Statement],\n        context: &StatementContext,\n    ) -> BackendResult {\n        // Add to the set in order to track the stack size.\n        #[cfg(test)]\n        self.put_block_stack_pointers\n            .insert(ptr::from_ref(&level).cast());\n\n        for statement in statements {\n            log::trace!(\"statement[{}] {:?}\", level.0, statement);\n            match *statement {\n                crate::Statement::Emit(ref range) => {\n                    for handle in range.clone() {\n                        use crate::MathFunction as Mf;\n\n                        match context.expression.function.expressions[handle] {\n                            // `ImageLoad` expressions covered by the `Restrict` bounds check policy\n                            // may need to cache a clamped version of their level-of-detail argument.\n                            crate::Expression::ImageLoad {\n                                image,\n                                level: mip_level,\n                                ..\n                            } => {\n                                self.put_cache_restricted_level(\n                                    handle, image, mip_level, level, context,\n                                )?;\n                            }\n\n                            // If we are going to write a `Dot4I8Packed` or `Dot4U8Packed` on Metal\n                            // 2.1+ then we introduce two intermediate variables that recast the two\n                            // arguments as packed (signed or unsigned) chars. The actual dot product\n                            // is implemented in `Self::put_expression`, and it uses both of these\n                            // intermediate variables multiple times. There's no danger that the\n                            // original arguments get modified between the definition of these\n                            // intermediate variables and the implementation of the actual dot\n                            // product since we require the inputs of `Dot4{I, U}Packed` to be baked.\n                            crate::Expression::Math {\n                                fun: fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed),\n                                arg,\n                                arg1,\n                                ..\n                            } if context.expression.lang_version >= (2, 1) => {\n                                self.put_casting_to_packed_chars(\n                                    fun,\n                                    arg,\n                                    arg1.unwrap(),\n                                    level,\n                                    context,\n                                )?;\n                            }\n\n                            _ => (),\n                        }\n\n                        let ptr_class = context.expression.resolve_type(handle).pointer_space();\n                        let expr_name = if ptr_class.is_some() {\n                            None // don't bake pointer expressions (just yet)\n                        } else if let Some(name) =\n                            context.expression.function.named_expressions.get(&handle)\n                        {\n                            // The `crate::Function::named_expressions` table holds\n                            // expressions that should be saved in temporaries once they\n                            // are `Emit`ted. We only add them to `self.named_expressions`\n                            // when we reach the `Emit` that covers them, so that we don't\n                            // try to use their names before we've actually initialized\n                            // the temporary that holds them.\n                            //\n                            // Don't assume the names in `named_expressions` are unique,\n                            // or even valid. Use the `Namer`.\n                            Some(self.namer.call(name))\n                        } else {\n                            // If this expression is an index that we're going to first compare\n                            // against a limit, and then actually use as an index, then we may\n                            // want to cache it in a temporary, to avoid evaluating it twice.\n                            let bake = if context.expression.guarded_indices.contains(handle) {\n                                true\n                            } else {\n                                self.need_bake_expressions.contains(&handle)\n                            };\n\n                            if bake {\n                                Some(Baked(handle).to_string())\n                            } else {\n                                None\n                            }\n                        };\n\n                        if let Some(name) = expr_name {\n                            write!(self.out, \"{level}\")?;\n                            self.start_baking_expression(handle, &context.expression, &name)?;\n                            self.put_expression(handle, &context.expression, true)?;\n                            self.named_expressions.insert(handle, name);\n                            writeln!(self.out, \";\")?;\n                        }\n                    }\n                }\n                crate::Statement::Block(ref block) => {\n                    if !block.is_empty() {\n                        writeln!(self.out, \"{level}{{\")?;\n                        self.put_block(level.next(), block, context)?;\n                        writeln!(self.out, \"{level}}}\")?;\n                    }\n                }\n                crate::Statement::If {\n                    condition,\n                    ref accept,\n                    ref reject,\n                } => {\n                    write!(self.out, \"{level}if (\")?;\n                    self.put_expression(condition, &context.expression, true)?;\n                    writeln!(self.out, \") {{\")?;\n                    self.put_block(level.next(), accept, context)?;\n                    if !reject.is_empty() {\n                        writeln!(self.out, \"{level}}} else {{\")?;\n                        self.put_block(level.next(), reject, context)?;\n                    }\n                    writeln!(self.out, \"{level}}}\")?;\n                }\n                crate::Statement::Switch {\n                    selector,\n                    ref cases,\n                } => {\n                    write!(self.out, \"{level}switch(\")?;\n                    self.put_expression(selector, &context.expression, true)?;\n                    writeln!(self.out, \") {{\")?;\n                    let lcase = level.next();\n                    for case in cases.iter() {\n                        match case.value {\n                            crate::SwitchValue::I32(value) => {\n                                write!(self.out, \"{lcase}case {value}:\")?;\n                            }\n                            crate::SwitchValue::U32(value) => {\n                                write!(self.out, \"{lcase}case {value}u:\")?;\n                            }\n                            crate::SwitchValue::Default => {\n                                write!(self.out, \"{lcase}default:\")?;\n                            }\n                        }\n\n                        let write_block_braces = !(case.fall_through && case.body.is_empty());\n                        if write_block_braces {\n                            writeln!(self.out, \" {{\")?;\n                        } else {\n                            writeln!(self.out)?;\n                        }\n\n                        self.put_block(lcase.next(), &case.body, context)?;\n                        if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator())\n                        {\n                            writeln!(self.out, \"{}break;\", lcase.next())?;\n                        }\n\n                        if write_block_braces {\n                            writeln!(self.out, \"{lcase}}}\")?;\n                        }\n                    }\n                    writeln!(self.out, \"{level}}}\")?;\n                }\n                crate::Statement::Loop {\n                    ref body,\n                    ref continuing,\n                    break_if,\n                } => {\n                    let force_loop_bound_statements =\n                        self.gen_force_bounded_loop_statements(level, context);\n                    let gate_name = (!continuing.is_empty() || break_if.is_some())\n                        .then(|| self.namer.call(\"loop_init\"));\n\n                    if let Some((ref decl, _)) = force_loop_bound_statements {\n                        writeln!(self.out, \"{decl}\")?;\n                    }\n                    if let Some(ref gate_name) = gate_name {\n                        writeln!(self.out, \"{level}bool {gate_name} = true;\")?;\n                    }\n\n                    writeln!(self.out, \"{level}while(true) {{\",)?;\n                    if let Some((_, ref break_and_inc)) = force_loop_bound_statements {\n                        writeln!(self.out, \"{break_and_inc}\")?;\n                    }\n                    if let Some(ref gate_name) = gate_name {\n                        let lif = level.next();\n                        let lcontinuing = lif.next();\n                        writeln!(self.out, \"{lif}if (!{gate_name}) {{\")?;\n                        self.put_block(lcontinuing, continuing, context)?;\n                        if let Some(condition) = break_if {\n                            write!(self.out, \"{lcontinuing}if (\")?;\n                            self.put_expression(condition, &context.expression, true)?;\n                            writeln!(self.out, \") {{\")?;\n                            writeln!(self.out, \"{}break;\", lcontinuing.next())?;\n                            writeln!(self.out, \"{lcontinuing}}}\")?;\n                        }\n                        writeln!(self.out, \"{lif}}}\")?;\n                        writeln!(self.out, \"{lif}{gate_name} = false;\")?;\n                    }\n                    self.put_block(level.next(), body, context)?;\n\n                    writeln!(self.out, \"{level}}}\")?;\n                }\n                crate::Statement::Break => {\n                    writeln!(self.out, \"{level}break;\")?;\n                }\n                crate::Statement::Continue => {\n                    writeln!(self.out, \"{level}continue;\")?;\n                }\n                crate::Statement::Return {\n                    value: Some(expr_handle),\n                } => {\n                    self.put_return_value(\n                        level,\n                        expr_handle,\n                        context.result_struct,\n                        &context.expression,\n                    )?;\n                }\n                crate::Statement::Return { value: None } => {\n                    writeln!(self.out, \"{level}return;\")?;\n                }\n                crate::Statement::Kill => {\n                    writeln!(self.out, \"{level}{NAMESPACE}::discard_fragment();\")?;\n                }\n                crate::Statement::ControlBarrier(flags)\n                | crate::Statement::MemoryBarrier(flags) => {\n                    self.write_barrier(flags, level)?;\n                }\n                crate::Statement::Store { pointer, value } => {\n                    self.put_store(pointer, value, level, context)?\n                }\n                crate::Statement::ImageStore {\n                    image,\n                    coordinate,\n                    array_index,\n                    value,\n                } => {\n                    let address = TexelAddress {\n                        coordinate,\n                        array_index,\n                        sample: None,\n                        level: None,\n                    };\n                    self.put_image_store(level, image, &address, value, context)?\n                }\n                crate::Statement::Call {\n                    function,\n                    ref arguments,\n                    result,\n                } => {\n                    write!(self.out, \"{level}\")?;\n                    if let Some(expr) = result {\n                        let name = Baked(expr).to_string();\n                        self.start_baking_expression(expr, &context.expression, &name)?;\n                        self.named_expressions.insert(expr, name);\n                    }\n                    let fun_name = &self.names[&NameKey::Function(function)];\n                    write!(self.out, \"{fun_name}(\")?;\n                    // first, write down the actual arguments\n                    for (i, &handle) in arguments.iter().enumerate() {\n                        if i != 0 {\n                            write!(self.out, \", \")?;\n                        }\n                        self.put_expression(handle, &context.expression, true)?;\n                    }\n                    // follow-up with any global resources used\n                    let mut separate = !arguments.is_empty();\n                    let fun_info = &context.expression.mod_info[function];\n                    let mut needs_buffer_sizes = false;\n                    for (handle, var) in context.expression.module.global_variables.iter() {\n                        if fun_info[handle].is_empty() {\n                            continue;\n                        }\n                        if var.space.needs_pass_through() {\n                            let name = &self.names[&NameKey::GlobalVariable(handle)];\n                            if separate {\n                                write!(self.out, \", \")?;\n                            } else {\n                                separate = true;\n                            }\n                            write!(self.out, \"{name}\")?;\n                        }\n                        needs_buffer_sizes |=\n                            needs_array_length(var.ty, &context.expression.module.types);\n                    }\n                    if needs_buffer_sizes {\n                        if separate {\n                            write!(self.out, \", \")?;\n                        }\n                        write!(self.out, \"_buffer_sizes\")?;\n                    }\n\n                    // done\n                    writeln!(self.out, \");\")?;\n                }\n                crate::Statement::Atomic {\n                    pointer,\n                    ref fun,\n                    value,\n                    result,\n                } => {\n                    let context = &context.expression;\n\n                    // This backend supports `SHADER_INT64_ATOMIC_MIN_MAX` but not\n                    // `SHADER_INT64_ATOMIC_ALL_OPS`, so we can assume that if `result` is\n                    // `Some`, we are not operating on a 64-bit value, and that if we are\n                    // operating on a 64-bit value, `result` is `None`.\n                    write!(self.out, \"{level}\")?;\n                    let fun_key = if let Some(result) = result {\n                        let res_name = Baked(result).to_string();\n                        self.start_baking_expression(result, context, &res_name)?;\n                        self.named_expressions.insert(result, res_name);\n                        fun.to_msl()\n                    } else if context.resolve_type(value).scalar_width() == Some(8) {\n                        fun.to_msl_64_bit()?\n                    } else {\n                        fun.to_msl()\n                    };\n\n                    // If the pointer we're passing to the atomic operation needs to be conditional\n                    // for `ReadZeroSkipWrite`, the condition needs to *surround* the atomic op, and\n                    // the pointer operand should be unchecked.\n                    let policy = context.choose_bounds_check_policy(pointer);\n                    let checked = policy == index::BoundsCheckPolicy::ReadZeroSkipWrite\n                        && self.put_bounds_checks(pointer, context, back::Level(0), \"\")?;\n\n                    // If requested and successfully put bounds checks, continue the ternary expression.\n                    if checked {\n                        write!(self.out, \" ? \")?;\n                    }\n\n                    // Put the atomic function invocation.\n                    match *fun {\n                        crate::AtomicFunction::Exchange { compare: Some(cmp) } => {\n                            write!(self.out, \"{ATOMIC_COMP_EXCH_FUNCTION}({ATOMIC_REFERENCE}\")?;\n                            self.put_access_chain(pointer, policy, context)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(cmp, context, true)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(value, context, true)?;\n                            write!(self.out, \")\")?;\n                        }\n                        _ => {\n                            write!(\n                                self.out,\n                                \"{NAMESPACE}::atomic_{fun_key}_explicit({ATOMIC_REFERENCE}\"\n                            )?;\n                            self.put_access_chain(pointer, policy, context)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(value, context, true)?;\n                            write!(self.out, \", {NAMESPACE}::memory_order_relaxed)\")?;\n                        }\n                    }\n\n                    // Finish the ternary expression.\n                    if checked {\n                        write!(self.out, \" : DefaultConstructible()\")?;\n                    }\n\n                    // Done\n                    writeln!(self.out, \";\")?;\n                }\n                crate::Statement::ImageAtomic {\n                    image,\n                    coordinate,\n                    array_index,\n                    fun,\n                    value,\n                } => {\n                    let address = TexelAddress {\n                        coordinate,\n                        array_index,\n                        sample: None,\n                        level: None,\n                    };\n                    self.put_image_atomic(level, image, &address, fun, value, context)?\n                }\n                crate::Statement::WorkGroupUniformLoad { pointer, result } => {\n                    self.write_barrier(crate::Barrier::WORK_GROUP, level)?;\n\n                    write!(self.out, \"{level}\")?;\n                    let name = self.namer.call(\"\");\n                    self.start_baking_expression(result, &context.expression, &name)?;\n                    self.put_load(pointer, &context.expression, true)?;\n                    self.named_expressions.insert(result, name);\n\n                    writeln!(self.out, \";\")?;\n                    self.write_barrier(crate::Barrier::WORK_GROUP, level)?;\n                }\n                crate::Statement::RayQuery { query, ref fun } => {\n                    if context.expression.lang_version < (2, 4) {\n                        return Err(Error::UnsupportedRayTracing);\n                    }\n\n                    match *fun {\n                        crate::RayQueryFunction::Initialize {\n                            acceleration_structure,\n                            descriptor,\n                        } => {\n                            //TODO: how to deal with winding?\n                            write!(self.out, \"{level}\")?;\n                            self.put_expression(query, &context.expression, true)?;\n                            writeln!(self.out, \".{RAY_QUERY_FIELD_INTERSECTOR}.assume_geometry_type({RT_NAMESPACE}::geometry_type::triangle);\")?;\n                            {\n                                let f_opaque = back::RayFlag::CULL_OPAQUE.bits();\n                                let f_no_opaque = back::RayFlag::CULL_NO_OPAQUE.bits();\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                write!(\n                                    self.out,\n                                    \".{RAY_QUERY_FIELD_INTERSECTOR}.set_opacity_cull_mode((\"\n                                )?;\n                                self.put_expression(descriptor, &context.expression, true)?;\n                                write!(self.out, \".flags & {f_opaque}) != 0 ? {RT_NAMESPACE}::opacity_cull_mode::opaque : (\")?;\n                                self.put_expression(descriptor, &context.expression, true)?;\n                                write!(self.out, \".flags & {f_no_opaque}) != 0 ? {RT_NAMESPACE}::opacity_cull_mode::non_opaque : \")?;\n                                writeln!(self.out, \"{RT_NAMESPACE}::opacity_cull_mode::none);\")?;\n                            }\n                            {\n                                let f_opaque = back::RayFlag::OPAQUE.bits();\n                                let f_no_opaque = back::RayFlag::NO_OPAQUE.bits();\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                write!(self.out, \".{RAY_QUERY_FIELD_INTERSECTOR}.force_opacity((\")?;\n                                self.put_expression(descriptor, &context.expression, true)?;\n                                write!(self.out, \".flags & {f_opaque}) != 0 ? {RT_NAMESPACE}::forced_opacity::opaque : (\")?;\n                                self.put_expression(descriptor, &context.expression, true)?;\n                                write!(self.out, \".flags & {f_no_opaque}) != 0 ? {RT_NAMESPACE}::forced_opacity::non_opaque : \")?;\n                                writeln!(self.out, \"{RT_NAMESPACE}::forced_opacity::none);\")?;\n                            }\n                            {\n                                let flag = back::RayFlag::TERMINATE_ON_FIRST_HIT.bits();\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                write!(\n                                    self.out,\n                                    \".{RAY_QUERY_FIELD_INTERSECTOR}.accept_any_intersection((\"\n                                )?;\n                                self.put_expression(descriptor, &context.expression, true)?;\n                                writeln!(self.out, \".flags & {flag}) != 0);\")?;\n                            }\n\n                            write!(self.out, \"{level}\")?;\n                            self.put_expression(query, &context.expression, true)?;\n                            write!(self.out, \".{RAY_QUERY_FIELD_INTERSECTION} = \")?;\n                            self.put_expression(query, &context.expression, true)?;\n                            write!(\n                                self.out,\n                                \".{RAY_QUERY_FIELD_INTERSECTOR}.intersect({RT_NAMESPACE}::ray(\"\n                            )?;\n                            self.put_expression(descriptor, &context.expression, true)?;\n                            write!(self.out, \".origin, \")?;\n                            self.put_expression(descriptor, &context.expression, true)?;\n                            write!(self.out, \".dir, \")?;\n                            self.put_expression(descriptor, &context.expression, true)?;\n                            write!(self.out, \".tmin, \")?;\n                            self.put_expression(descriptor, &context.expression, true)?;\n                            write!(self.out, \".tmax), \")?;\n                            self.put_expression(acceleration_structure, &context.expression, true)?;\n                            write!(self.out, \", \")?;\n                            self.put_expression(descriptor, &context.expression, true)?;\n                            write!(self.out, \".cull_mask);\")?;\n\n                            write!(self.out, \"{level}\")?;\n                            self.put_expression(query, &context.expression, true)?;\n                            writeln!(self.out, \".{RAY_QUERY_FIELD_READY} = true;\")?;\n                        }\n                        crate::RayQueryFunction::Proceed { result } => {\n                            write!(self.out, \"{level}\")?;\n                            let name = Baked(result).to_string();\n                            self.start_baking_expression(result, &context.expression, &name)?;\n                            self.named_expressions.insert(result, name);\n                            self.put_expression(query, &context.expression, true)?;\n                            writeln!(self.out, \".{RAY_QUERY_FIELD_READY};\")?;\n                            if RAY_QUERY_MODERN_SUPPORT {\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                writeln!(self.out, \".?.next();\")?;\n                            }\n                        }\n                        crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                            if RAY_QUERY_MODERN_SUPPORT {\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                write!(self.out, \".?.commit_bounding_box_intersection(\")?;\n                                self.put_expression(hit_t, &context.expression, true)?;\n                                writeln!(self.out, \");\")?;\n                            } else {\n                                log::warn!(\"Ray Query GenerateIntersection is not yet supported\");\n                            }\n                        }\n                        crate::RayQueryFunction::ConfirmIntersection => {\n                            if RAY_QUERY_MODERN_SUPPORT {\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                writeln!(self.out, \".?.commit_triangle_intersection();\")?;\n                            } else {\n                                log::warn!(\"Ray Query ConfirmIntersection is not yet supported\");\n                            }\n                        }\n                        crate::RayQueryFunction::Terminate => {\n                            if RAY_QUERY_MODERN_SUPPORT {\n                                write!(self.out, \"{level}\")?;\n                                self.put_expression(query, &context.expression, true)?;\n                                writeln!(self.out, \".?.abort();\")?;\n                            }\n                            write!(self.out, \"{level}\")?;\n                            self.put_expression(query, &context.expression, true)?;\n                            writeln!(self.out, \".{RAY_QUERY_FIELD_READY} = false;\")?;\n                        }\n                    }\n                }\n                crate::Statement::SubgroupBallot { result, predicate } => {\n                    write!(self.out, \"{level}\")?;\n                    let name = self.namer.call(\"\");\n                    self.start_baking_expression(result, &context.expression, &name)?;\n                    self.named_expressions.insert(result, name);\n                    write!(\n                        self.out,\n                        \"{NAMESPACE}::uint4((uint64_t){NAMESPACE}::simd_ballot(\"\n                    )?;\n                    if let Some(predicate) = predicate {\n                        self.put_expression(predicate, &context.expression, true)?;\n                    } else {\n                        write!(self.out, \"true\")?;\n                    }\n                    writeln!(self.out, \"), 0, 0, 0);\")?;\n                }\n                crate::Statement::SubgroupCollectiveOperation {\n                    op,\n                    collective_op,\n                    argument,\n                    result,\n                } => {\n                    write!(self.out, \"{level}\")?;\n                    let name = self.namer.call(\"\");\n                    self.start_baking_expression(result, &context.expression, &name)?;\n                    self.named_expressions.insert(result, name);\n                    match (collective_op, op) {\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {\n                            write!(self.out, \"{NAMESPACE}::simd_all(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {\n                            write!(self.out, \"{NAMESPACE}::simd_any(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {\n                            write!(self.out, \"{NAMESPACE}::simd_sum(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {\n                            write!(self.out, \"{NAMESPACE}::simd_product(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {\n                            write!(self.out, \"{NAMESPACE}::simd_max(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {\n                            write!(self.out, \"{NAMESPACE}::simd_min(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {\n                            write!(self.out, \"{NAMESPACE}::simd_and(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {\n                            write!(self.out, \"{NAMESPACE}::simd_or(\")?\n                        }\n                        (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {\n                            write!(self.out, \"{NAMESPACE}::simd_xor(\")?\n                        }\n                        (\n                            crate::CollectiveOperation::ExclusiveScan,\n                            crate::SubgroupOperation::Add,\n                        ) => write!(self.out, \"{NAMESPACE}::simd_prefix_exclusive_sum(\")?,\n                        (\n                            crate::CollectiveOperation::ExclusiveScan,\n                            crate::SubgroupOperation::Mul,\n                        ) => write!(self.out, \"{NAMESPACE}::simd_prefix_exclusive_product(\")?,\n                        (\n                            crate::CollectiveOperation::InclusiveScan,\n                            crate::SubgroupOperation::Add,\n                        ) => write!(self.out, \"{NAMESPACE}::simd_prefix_inclusive_sum(\")?,\n                        (\n                            crate::CollectiveOperation::InclusiveScan,\n                            crate::SubgroupOperation::Mul,\n                        ) => write!(self.out, \"{NAMESPACE}::simd_prefix_inclusive_product(\")?,\n                        _ => unimplemented!(),\n                    }\n                    self.put_expression(argument, &context.expression, true)?;\n                    writeln!(self.out, \");\")?;\n                }\n                crate::Statement::SubgroupGather {\n                    mode,\n                    argument,\n                    result,\n                } => {\n                    write!(self.out, \"{level}\")?;\n                    let name = self.namer.call(\"\");\n                    self.start_baking_expression(result, &context.expression, &name)?;\n                    self.named_expressions.insert(result, name);\n                    match mode {\n                        crate::GatherMode::BroadcastFirst => {\n                            write!(self.out, \"{NAMESPACE}::simd_broadcast_first(\")?;\n                        }\n                        crate::GatherMode::Broadcast(_) => {\n                            write!(self.out, \"{NAMESPACE}::simd_broadcast(\")?;\n                        }\n                        crate::GatherMode::Shuffle(_) => {\n                            write!(self.out, \"{NAMESPACE}::simd_shuffle(\")?;\n                        }\n                        crate::GatherMode::ShuffleDown(_) => {\n                            write!(self.out, \"{NAMESPACE}::simd_shuffle_down(\")?;\n                        }\n                        crate::GatherMode::ShuffleUp(_) => {\n                            write!(self.out, \"{NAMESPACE}::simd_shuffle_up(\")?;\n                        }\n                        crate::GatherMode::ShuffleXor(_) => {\n                            write!(self.out, \"{NAMESPACE}::simd_shuffle_xor(\")?;\n                        }\n                        crate::GatherMode::QuadBroadcast(_) => {\n                            write!(self.out, \"{NAMESPACE}::quad_broadcast(\")?;\n                        }\n                        crate::GatherMode::QuadSwap(_) => {\n                            write!(self.out, \"{NAMESPACE}::quad_shuffle_xor(\")?;\n                        }\n                    }\n                    self.put_expression(argument, &context.expression, true)?;\n                    match mode {\n                        crate::GatherMode::BroadcastFirst => {}\n                        crate::GatherMode::Broadcast(index)\n                        | crate::GatherMode::Shuffle(index)\n                        | crate::GatherMode::ShuffleDown(index)\n                        | crate::GatherMode::ShuffleUp(index)\n                        | crate::GatherMode::ShuffleXor(index)\n                        | crate::GatherMode::QuadBroadcast(index) => {\n                            write!(self.out, \", \")?;\n                            self.put_expression(index, &context.expression, true)?;\n                        }\n                        crate::GatherMode::QuadSwap(direction) => {\n                            write!(self.out, \", \")?;\n                            match direction {\n                                crate::Direction::X => {\n                                    write!(self.out, \"1u\")?;\n                                }\n                                crate::Direction::Y => {\n                                    write!(self.out, \"2u\")?;\n                                }\n                                crate::Direction::Diagonal => {\n                                    write!(self.out, \"3u\")?;\n                                }\n                            }\n                        }\n                    }\n                    writeln!(self.out, \");\")?;\n                }\n                crate::Statement::CooperativeStore { target, ref data } => {\n                    write!(self.out, \"{level}simdgroup_store(\")?;\n                    self.put_expression(target, &context.expression, true)?;\n                    write!(self.out, \", &\")?;\n                    self.put_access_chain(\n                        data.pointer,\n                        context.expression.policies.index,\n                        &context.expression,\n                    )?;\n                    write!(self.out, \", \")?;\n                    self.put_expression(data.stride, &context.expression, true)?;\n                    if data.row_major {\n                        let matrix_origin = \"0\";\n                        let transpose = true;\n                        write!(self.out, \", {matrix_origin}, {transpose}\")?;\n                    }\n                    writeln!(self.out, \");\")?;\n                }\n                crate::Statement::RayPipelineFunction(_) => unreachable!(),\n            }\n        }\n\n        // un-emit expressions\n        //TODO: take care of loop/continuing?\n        for statement in statements {\n            if let crate::Statement::Emit(ref range) = *statement {\n                for handle in range.clone() {\n                    self.named_expressions.shift_remove(&handle);\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn put_store(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        value: Handle<crate::Expression>,\n        level: back::Level,\n        context: &StatementContext,\n    ) -> BackendResult {\n        let policy = context.expression.choose_bounds_check_policy(pointer);\n        if policy == index::BoundsCheckPolicy::ReadZeroSkipWrite\n            && self.put_bounds_checks(pointer, &context.expression, level, \"if (\")?\n        {\n            writeln!(self.out, \") {{\")?;\n            self.put_unchecked_store(pointer, value, policy, level.next(), context)?;\n            writeln!(self.out, \"{level}}}\")?;\n        } else {\n            self.put_unchecked_store(pointer, value, policy, level, context)?;\n        }\n\n        Ok(())\n    }\n\n    fn put_unchecked_store(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        value: Handle<crate::Expression>,\n        policy: index::BoundsCheckPolicy,\n        level: back::Level,\n        context: &StatementContext,\n    ) -> BackendResult {\n        let is_atomic_pointer = context\n            .expression\n            .resolve_type(pointer)\n            .is_atomic_pointer(&context.expression.module.types);\n\n        if is_atomic_pointer {\n            write!(\n                self.out,\n                \"{level}{NAMESPACE}::atomic_store_explicit({ATOMIC_REFERENCE}\"\n            )?;\n            self.put_access_chain(pointer, policy, &context.expression)?;\n            write!(self.out, \", \")?;\n            self.put_expression(value, &context.expression, true)?;\n            writeln!(self.out, \", {NAMESPACE}::memory_order_relaxed);\")?;\n        } else {\n            write!(self.out, \"{level}\")?;\n            self.put_access_chain(pointer, policy, &context.expression)?;\n            write!(self.out, \" = \")?;\n            self.put_expression(value, &context.expression, true)?;\n            writeln!(self.out, \";\")?;\n        }\n\n        Ok(())\n    }\n\n    pub fn write(\n        &mut self,\n        module: &crate::Module,\n        info: &valid::ModuleInfo,\n        options: &Options,\n        pipeline_options: &PipelineOptions,\n    ) -> Result<TranslationInfo, Error> {\n        self.names.clear();\n        self.namer.reset(\n            module,\n            &super::keywords::RESERVED_SET,\n            proc::KeywordSet::empty(),\n            proc::CaseInsensitiveKeywordSet::empty(),\n            &[CLAMPED_LOD_LOAD_PREFIX],\n            &mut self.names,\n        );\n        self.wrapped_functions.clear();\n        self.struct_member_pads.clear();\n\n        writeln!(\n            self.out,\n            \"// language: metal{}.{}\",\n            options.lang_version.0, options.lang_version.1\n        )?;\n        writeln!(self.out, \"#include <metal_stdlib>\")?;\n        writeln!(self.out, \"#include <simd/simd.h>\")?;\n        writeln!(self.out)?;\n        // Work around Metal bug where `uint` is not available by default\n        writeln!(self.out, \"using {NAMESPACE}::uint;\")?;\n\n        let mut uses_ray_query = false;\n        for (_, ty) in module.types.iter() {\n            match ty.inner {\n                crate::TypeInner::AccelerationStructure { .. } => {\n                    if options.lang_version < (2, 4) {\n                        return Err(Error::UnsupportedRayTracing);\n                    }\n                }\n                crate::TypeInner::RayQuery { .. } => {\n                    if options.lang_version < (2, 4) {\n                        return Err(Error::UnsupportedRayTracing);\n                    }\n                    uses_ray_query = true;\n                }\n                _ => (),\n            }\n        }\n\n        if module.special_types.ray_desc.is_some()\n            || module.special_types.ray_intersection.is_some()\n        {\n            if options.lang_version < (2, 4) {\n                return Err(Error::UnsupportedRayTracing);\n            }\n        }\n\n        if uses_ray_query {\n            self.put_ray_query_type()?;\n        }\n\n        if options\n            .bounds_check_policies\n            .contains(index::BoundsCheckPolicy::ReadZeroSkipWrite)\n        {\n            self.put_default_constructible()?;\n        }\n        writeln!(self.out)?;\n\n        {\n            // Make a `Vec` of all the `GlobalVariable`s that contain\n            // runtime-sized arrays.\n            let globals: Vec<Handle<crate::GlobalVariable>> = module\n                .global_variables\n                .iter()\n                .filter(|&(_, var)| needs_array_length(var.ty, &module.types))\n                .map(|(handle, _)| handle)\n                .collect();\n\n            let mut buffer_indices = vec![];\n            for vbm in &pipeline_options.vertex_buffer_mappings {\n                buffer_indices.push(vbm.id);\n            }\n\n            if !globals.is_empty() || !buffer_indices.is_empty() {\n                writeln!(self.out, \"struct _mslBufferSizes {{\")?;\n\n                for global in globals {\n                    writeln!(\n                        self.out,\n                        \"{}uint {};\",\n                        back::INDENT,\n                        ArraySizeMember(global)\n                    )?;\n                }\n\n                for idx in buffer_indices {\n                    writeln!(self.out, \"{}uint buffer_size{};\", back::INDENT, idx)?;\n                }\n\n                writeln!(self.out, \"}};\")?;\n                writeln!(self.out)?;\n            }\n        };\n\n        self.write_type_defs(module)?;\n        self.write_global_constants(module, info)?;\n        self.write_functions(module, info, options, pipeline_options)\n    }\n\n    /// Write the definition for the `DefaultConstructible` class.\n    ///\n    /// The [`ReadZeroSkipWrite`] bounds check policy requires us to be able to\n    /// produce 'zero' values for any type, including structs, arrays, and so\n    /// on. We could do this by emitting default constructor applications, but\n    /// that would entail printing the name of the type, which is more trouble\n    /// than you'd think. Instead, we just construct this magic C++14 class that\n    /// can be converted to any type that can be default constructed, using\n    /// template parameter inference to detect which type is needed, so we don't\n    /// have to figure out the name.\n    ///\n    /// [`ReadZeroSkipWrite`]: index::BoundsCheckPolicy::ReadZeroSkipWrite\n    fn put_default_constructible(&mut self) -> BackendResult {\n        let tab = back::INDENT;\n        writeln!(self.out, \"struct DefaultConstructible {{\")?;\n        writeln!(self.out, \"{tab}template<typename T>\")?;\n        writeln!(self.out, \"{tab}operator T() && {{\")?;\n        writeln!(self.out, \"{tab}{tab}return T {{}};\")?;\n        writeln!(self.out, \"{tab}}}\")?;\n        writeln!(self.out, \"}};\")?;\n        Ok(())\n    }\n\n    fn put_ray_query_type(&mut self) -> BackendResult {\n        let tab = back::INDENT;\n        writeln!(self.out, \"struct {RAY_QUERY_TYPE} {{\")?;\n        let full_type = format!(\"{RT_NAMESPACE}::intersector<{RT_NAMESPACE}::instancing, {RT_NAMESPACE}::triangle_data, {RT_NAMESPACE}::world_space_data>\");\n        writeln!(self.out, \"{tab}{full_type} {RAY_QUERY_FIELD_INTERSECTOR};\")?;\n        writeln!(\n            self.out,\n            \"{tab}{full_type}::result_type {RAY_QUERY_FIELD_INTERSECTION};\"\n        )?;\n        writeln!(self.out, \"{tab}bool {RAY_QUERY_FIELD_READY} = false;\")?;\n        writeln!(self.out, \"}};\")?;\n        writeln!(self.out, \"constexpr {NAMESPACE}::uint {RAY_QUERY_FUN_MAP_INTERSECTION}(const {RT_NAMESPACE}::intersection_type ty) {{\")?;\n        let v_triangle = back::RayIntersectionType::Triangle as u32;\n        let v_bbox = back::RayIntersectionType::BoundingBox as u32;\n        writeln!(\n            self.out,\n            \"{tab}return ty=={RT_NAMESPACE}::intersection_type::triangle ? {v_triangle} : \"\n        )?;\n        writeln!(\n            self.out,\n            \"{tab}{tab}ty=={RT_NAMESPACE}::intersection_type::bounding_box ? {v_bbox} : 0;\"\n        )?;\n        writeln!(self.out, \"}}\")?;\n        Ok(())\n    }\n\n    fn write_type_defs(&mut self, module: &crate::Module) -> BackendResult {\n        let mut generated_argument_buffer_wrapper = false;\n        let mut generated_external_texture_wrapper = false;\n        for (handle, ty) in module.types.iter() {\n            match ty.inner {\n                crate::TypeInner::BindingArray { .. } if !generated_argument_buffer_wrapper => {\n                    writeln!(self.out, \"template <typename T>\")?;\n                    writeln!(self.out, \"struct {ARGUMENT_BUFFER_WRAPPER_STRUCT} {{\")?;\n                    writeln!(self.out, \"{}T {WRAPPED_ARRAY_FIELD};\", back::INDENT)?;\n                    writeln!(self.out, \"}};\")?;\n                    generated_argument_buffer_wrapper = true;\n                }\n                crate::TypeInner::Image {\n                    class: crate::ImageClass::External,\n                    ..\n                } if !generated_external_texture_wrapper => {\n                    let params_ty_name = &self.names\n                        [&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n                    writeln!(self.out, \"struct {EXTERNAL_TEXTURE_WRAPPER_STRUCT} {{\")?;\n                    writeln!(\n                        self.out,\n                        \"{}{NAMESPACE}::texture2d<float, {NAMESPACE}::access::sample> plane0;\",\n                        back::INDENT\n                    )?;\n                    writeln!(\n                        self.out,\n                        \"{}{NAMESPACE}::texture2d<float, {NAMESPACE}::access::sample> plane1;\",\n                        back::INDENT\n                    )?;\n                    writeln!(\n                        self.out,\n                        \"{}{NAMESPACE}::texture2d<float, {NAMESPACE}::access::sample> plane2;\",\n                        back::INDENT\n                    )?;\n                    writeln!(self.out, \"{}{params_ty_name} params;\", back::INDENT)?;\n                    writeln!(self.out, \"}};\")?;\n                    generated_external_texture_wrapper = true;\n                }\n                _ => {}\n            }\n\n            if !ty.needs_alias() {\n                continue;\n            }\n            let name = &self.names[&NameKey::Type(handle)];\n            match ty.inner {\n                // Naga IR can pass around arrays by value, but Metal, following\n                // C++, performs an array-to-pointer conversion (C++ [conv.array])\n                // on expressions of array type, so assigning the array by value\n                // isn't possible. However, Metal *does* assign structs by\n                // value. So in our Metal output, we wrap all array types in\n                // synthetic struct types:\n                //\n                //     struct type1 {\n                //         float inner[10]\n                //     };\n                //\n                // Then we carefully include `.inner` (`WRAPPED_ARRAY_FIELD`) in\n                // any expression that actually wants access to the array.\n                crate::TypeInner::Array {\n                    base,\n                    size,\n                    stride: _,\n                } => {\n                    let base_name = TypeContext {\n                        handle: base,\n                        gctx: module.to_ctx(),\n                        names: &self.names,\n                        access: crate::StorageAccess::empty(),\n                        first_time: false,\n                    };\n\n                    match size.resolve(module.to_ctx())? {\n                        proc::IndexableLength::Known(size) => {\n                            writeln!(self.out, \"struct {name} {{\")?;\n                            writeln!(\n                                self.out,\n                                \"{}{} {}[{}];\",\n                                back::INDENT,\n                                base_name,\n                                WRAPPED_ARRAY_FIELD,\n                                size\n                            )?;\n                            writeln!(self.out, \"}};\")?;\n                        }\n                        proc::IndexableLength::Dynamic => {\n                            writeln!(self.out, \"typedef {base_name} {name}[1];\")?;\n                        }\n                    }\n                }\n                crate::TypeInner::Struct {\n                    ref members, span, ..\n                } => {\n                    writeln!(self.out, \"struct {name} {{\")?;\n                    let mut last_offset = 0;\n                    for (index, member) in members.iter().enumerate() {\n                        if member.offset > last_offset {\n                            self.struct_member_pads.insert((handle, index as u32));\n                            let pad = member.offset - last_offset;\n                            writeln!(self.out, \"{}char _pad{}[{}];\", back::INDENT, index, pad)?;\n                        }\n                        let ty_inner = &module.types[member.ty].inner;\n                        last_offset = member.offset + ty_inner.size(module.to_ctx());\n\n                        let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];\n\n                        // If the member should be packed (as is the case for a misaligned vec3) issue a packed vector\n                        match should_pack_struct_member(members, span, index, module) {\n                            Some(scalar) => {\n                                writeln!(\n                                    self.out,\n                                    \"{}{}::packed_{}3 {};\",\n                                    back::INDENT,\n                                    NAMESPACE,\n                                    scalar.to_msl_name(),\n                                    member_name\n                                )?;\n                            }\n                            None => {\n                                let base_name = TypeContext {\n                                    handle: member.ty,\n                                    gctx: module.to_ctx(),\n                                    names: &self.names,\n                                    access: crate::StorageAccess::empty(),\n                                    first_time: false,\n                                };\n                                writeln!(\n                                    self.out,\n                                    \"{}{} {};\",\n                                    back::INDENT,\n                                    base_name,\n                                    member_name\n                                )?;\n\n                                // for 3-component vectors, add one component\n                                if let crate::TypeInner::Vector {\n                                    size: crate::VectorSize::Tri,\n                                    scalar,\n                                } = *ty_inner\n                                {\n                                    last_offset += scalar.width as u32;\n                                }\n                            }\n                        }\n                    }\n                    if last_offset < span {\n                        let pad = span - last_offset;\n                        writeln!(\n                            self.out,\n                            \"{}char _pad{}[{}];\",\n                            back::INDENT,\n                            members.len(),\n                            pad\n                        )?;\n                    }\n                    writeln!(self.out, \"}};\")?;\n                }\n                _ => {\n                    let ty_name = TypeContext {\n                        handle,\n                        gctx: module.to_ctx(),\n                        names: &self.names,\n                        access: crate::StorageAccess::empty(),\n                        first_time: true,\n                    };\n                    writeln!(self.out, \"typedef {ty_name} {name};\")?;\n                }\n            }\n        }\n\n        // Write functions to create special types.\n        for (type_key, struct_ty) in module.special_types.predeclared_types.iter() {\n            match type_key {\n                &crate::PredeclaredType::ModfResult { size, scalar }\n                | &crate::PredeclaredType::FrexpResult { size, scalar } => {\n                    let arg_type_name_owner;\n                    let arg_type_name = if let Some(size) = size {\n                        arg_type_name_owner = format!(\n                            \"{NAMESPACE}::{}{}\",\n                            if scalar.width == 8 { \"double\" } else { \"float\" },\n                            size as u8\n                        );\n                        &arg_type_name_owner\n                    } else if scalar.width == 8 {\n                        \"double\"\n                    } else {\n                        \"float\"\n                    };\n\n                    let other_type_name_owner;\n                    let (defined_func_name, called_func_name, other_type_name) =\n                        if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {\n                            (MODF_FUNCTION, \"modf\", arg_type_name)\n                        } else {\n                            let other_type_name = if let Some(size) = size {\n                                other_type_name_owner = format!(\"int{}\", size as u8);\n                                &other_type_name_owner\n                            } else {\n                                \"int\"\n                            };\n                            (FREXP_FUNCTION, \"frexp\", other_type_name)\n                        };\n\n                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];\n\n                    writeln!(self.out)?;\n                    writeln!(\n                        self.out,\n                        \"{struct_name} {defined_func_name}({arg_type_name} arg) {{\n    {other_type_name} other;\n    {arg_type_name} fract = {NAMESPACE}::{called_func_name}(arg, other);\n    return {struct_name}{{ fract, other }};\n}}\"\n                    )?;\n                }\n                &crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar) => {\n                    let arg_type_name = scalar.to_msl_name();\n                    let called_func_name = \"atomic_compare_exchange_weak_explicit\";\n                    let defined_func_name = ATOMIC_COMP_EXCH_FUNCTION;\n                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];\n\n                    writeln!(self.out)?;\n\n                    for address_space_name in [\"device\", \"threadgroup\"] {\n                        writeln!(\n                            self.out,\n                            \"\\\ntemplate <typename A>\n{struct_name} {defined_func_name}(\n    {address_space_name} A *atomic_ptr,\n    {arg_type_name} cmp,\n    {arg_type_name} v\n) {{\n    bool swapped = {NAMESPACE}::{called_func_name}(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return {struct_name}{{cmp, swapped}};\n}}\"\n                        )?;\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Writes all named constants\n    fn write_global_constants(\n        &mut self,\n        module: &crate::Module,\n        mod_info: &valid::ModuleInfo,\n    ) -> BackendResult {\n        let constants = module.constants.iter().filter(|&(_, c)| c.name.is_some());\n\n        for (handle, constant) in constants {\n            let ty_name = TypeContext {\n                handle: constant.ty,\n                gctx: module.to_ctx(),\n                names: &self.names,\n                access: crate::StorageAccess::empty(),\n                first_time: false,\n            };\n            let name = &self.names[&NameKey::Constant(handle)];\n            write!(self.out, \"constant {ty_name} {name} = \")?;\n            self.put_const_expression(constant.init, module, mod_info, &module.global_expressions)?;\n            writeln!(self.out, \";\")?;\n        }\n\n        Ok(())\n    }\n\n    fn put_inline_sampler_properties(\n        &mut self,\n        level: back::Level,\n        sampler: &sm::InlineSampler,\n    ) -> BackendResult {\n        for (&letter, address) in ['s', 't', 'r'].iter().zip(sampler.address.iter()) {\n            writeln!(\n                self.out,\n                \"{}{}::{}_address::{},\",\n                level,\n                NAMESPACE,\n                letter,\n                address.as_str(),\n            )?;\n        }\n        writeln!(\n            self.out,\n            \"{}{}::mag_filter::{},\",\n            level,\n            NAMESPACE,\n            sampler.mag_filter.as_str(),\n        )?;\n        writeln!(\n            self.out,\n            \"{}{}::min_filter::{},\",\n            level,\n            NAMESPACE,\n            sampler.min_filter.as_str(),\n        )?;\n        if let Some(filter) = sampler.mip_filter {\n            writeln!(\n                self.out,\n                \"{}{}::mip_filter::{},\",\n                level,\n                NAMESPACE,\n                filter.as_str(),\n            )?;\n        }\n        // avoid setting it on platforms that don't support it\n        if sampler.border_color != sm::BorderColor::TransparentBlack {\n            writeln!(\n                self.out,\n                \"{}{}::border_color::{},\",\n                level,\n                NAMESPACE,\n                sampler.border_color.as_str(),\n            )?;\n        }\n        //TODO: I'm not able to feed this in a way that MSL likes:\n        //>error: use of undeclared identifier 'lod_clamp'\n        //>error: no member named 'max_anisotropy' in namespace 'metal'\n        if false {\n            if let Some(ref lod) = sampler.lod_clamp {\n                writeln!(self.out, \"{}lod_clamp({},{}),\", level, lod.start, lod.end,)?;\n            }\n            if let Some(aniso) = sampler.max_anisotropy {\n                writeln!(self.out, \"{}max_anisotropy({}),\", level, aniso.get(),)?;\n            }\n        }\n        if sampler.compare_func != sm::CompareFunc::Never {\n            writeln!(\n                self.out,\n                \"{}{}::compare_func::{},\",\n                level,\n                NAMESPACE,\n                sampler.compare_func.as_str(),\n            )?;\n        }\n        writeln!(\n            self.out,\n            \"{}{}::coord::{}\",\n            level,\n            NAMESPACE,\n            sampler.coord.as_str()\n        )?;\n        Ok(())\n    }\n\n    fn write_unpacking_function(\n        &mut self,\n        format: back::msl::VertexFormat,\n    ) -> Result<(String, u32, Option<crate::VectorSize>, crate::Scalar), Error> {\n        use crate::{Scalar, VectorSize};\n        use back::msl::VertexFormat::*;\n        match format {\n            Uint8 => {\n                let name = self.namer.call(\"unpackUint8\");\n                writeln!(self.out, \"uint {name}(metal::uchar b0) {{\")?;\n                writeln!(self.out, \"{}return uint(b0);\", back::INDENT)?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 1, None, Scalar::U32))\n            }\n            Uint8x2 => {\n                let name = self.namer.call(\"unpackUint8x2\");\n                writeln!(\n                    self.out,\n                    \"metal::uint2 {name}(metal::uchar b0, \\\n                                         metal::uchar b1) {{\"\n                )?;\n                writeln!(self.out, \"{}return metal::uint2(b0, b1);\", back::INDENT)?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, Some(VectorSize::Bi), Scalar::U32))\n            }\n            Uint8x4 => {\n                let name = self.namer.call(\"unpackUint8x4\");\n                writeln!(\n                    self.out,\n                    \"metal::uint4 {name}(metal::uchar b0, \\\n                                         metal::uchar b1, \\\n                                         metal::uchar b2, \\\n                                         metal::uchar b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::uint4(b0, b1, b2, b3);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::U32))\n            }\n            Sint8 => {\n                let name = self.namer.call(\"unpackSint8\");\n                writeln!(self.out, \"int {name}(metal::uchar b0) {{\")?;\n                writeln!(self.out, \"{}return int(as_type<char>(b0));\", back::INDENT)?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 1, None, Scalar::I32))\n            }\n            Sint8x2 => {\n                let name = self.namer.call(\"unpackSint8x2\");\n                writeln!(\n                    self.out,\n                    \"metal::int2 {name}(metal::uchar b0, \\\n                                        metal::uchar b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int2(as_type<char>(b0), \\\n                                          as_type<char>(b1));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, Some(VectorSize::Bi), Scalar::I32))\n            }\n            Sint8x4 => {\n                let name = self.namer.call(\"unpackSint8x4\");\n                writeln!(\n                    self.out,\n                    \"metal::int4 {name}(metal::uchar b0, \\\n                                        metal::uchar b1, \\\n                                        metal::uchar b2, \\\n                                        metal::uchar b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int4(as_type<char>(b0), \\\n                                          as_type<char>(b1), \\\n                                          as_type<char>(b2), \\\n                                          as_type<char>(b3));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::I32))\n            }\n            Unorm8 => {\n                let name = self.namer.call(\"unpackUnorm8\");\n                writeln!(self.out, \"float {name}(metal::uchar b0) {{\")?;\n                writeln!(\n                    self.out,\n                    \"{}return float(float(b0) / 255.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 1, None, Scalar::F32))\n            }\n            Unorm8x2 => {\n                let name = self.namer.call(\"unpackUnorm8x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(metal::uchar b0, \\\n                                          metal::uchar b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float2(float(b0) / 255.0f, \\\n                                            float(b1) / 255.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Unorm8x4 => {\n                let name = self.namer.call(\"unpackUnorm8x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(metal::uchar b0, \\\n                                          metal::uchar b1, \\\n                                          metal::uchar b2, \\\n                                          metal::uchar b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(float(b0) / 255.0f, \\\n                                            float(b1) / 255.0f, \\\n                                            float(b2) / 255.0f, \\\n                                            float(b3) / 255.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Snorm8 => {\n                let name = self.namer.call(\"unpackSnorm8\");\n                writeln!(self.out, \"float {name}(metal::uchar b0) {{\")?;\n                writeln!(\n                    self.out,\n                    \"{}return float(metal::max(-1.0f, as_type<char>(b0) / 127.0f));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 1, None, Scalar::F32))\n            }\n            Snorm8x2 => {\n                let name = self.namer.call(\"unpackSnorm8x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(metal::uchar b0, \\\n                                          metal::uchar b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), \\\n                                            metal::max(-1.0f, as_type<char>(b1) / 127.0f));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Snorm8x4 => {\n                let name = self.namer.call(\"unpackSnorm8x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(metal::uchar b0, \\\n                                          metal::uchar b1, \\\n                                          metal::uchar b2, \\\n                                          metal::uchar b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), \\\n                                            metal::max(-1.0f, as_type<char>(b1) / 127.0f), \\\n                                            metal::max(-1.0f, as_type<char>(b2) / 127.0f), \\\n                                            metal::max(-1.0f, as_type<char>(b3) / 127.0f));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Uint16 => {\n                let name = self.namer.call(\"unpackUint16\");\n                writeln!(\n                    self.out,\n                    \"metal::uint {name}(metal::uint b0, \\\n                                        metal::uint b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::uint(b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, None, Scalar::U32))\n            }\n            Uint16x2 => {\n                let name = self.namer.call(\"unpackUint16x2\");\n                writeln!(\n                    self.out,\n                    \"metal::uint2 {name}(metal::uint b0, \\\n                                         metal::uint b1, \\\n                                         metal::uint b2, \\\n                                         metal::uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::uint2(b1 << 8 | b0, \\\n                                           b3 << 8 | b2);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Bi), Scalar::U32))\n            }\n            Uint16x4 => {\n                let name = self.namer.call(\"unpackUint16x4\");\n                writeln!(\n                    self.out,\n                    \"metal::uint4 {name}(metal::uint b0, \\\n                                         metal::uint b1, \\\n                                         metal::uint b2, \\\n                                         metal::uint b3, \\\n                                         metal::uint b4, \\\n                                         metal::uint b5, \\\n                                         metal::uint b6, \\\n                                         metal::uint b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::uint4(b1 << 8 | b0, \\\n                                           b3 << 8 | b2, \\\n                                           b5 << 8 | b4, \\\n                                           b7 << 8 | b6);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Quad), Scalar::U32))\n            }\n            Sint16 => {\n                let name = self.namer.call(\"unpackSint16\");\n                writeln!(\n                    self.out,\n                    \"int {name}(metal::ushort b0, \\\n                                metal::ushort b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return int(as_type<short>(metal::ushort(b1 << 8 | b0)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, None, Scalar::I32))\n            }\n            Sint16x2 => {\n                let name = self.namer.call(\"unpackSint16x2\");\n                writeln!(\n                    self.out,\n                    \"metal::int2 {name}(metal::ushort b0, \\\n                                        metal::ushort b1, \\\n                                        metal::ushort b2, \\\n                                        metal::ushort b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), \\\n                                          as_type<short>(metal::ushort(b3 << 8 | b2)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Bi), Scalar::I32))\n            }\n            Sint16x4 => {\n                let name = self.namer.call(\"unpackSint16x4\");\n                writeln!(\n                    self.out,\n                    \"metal::int4 {name}(metal::ushort b0, \\\n                                        metal::ushort b1, \\\n                                        metal::ushort b2, \\\n                                        metal::ushort b3, \\\n                                        metal::ushort b4, \\\n                                        metal::ushort b5, \\\n                                        metal::ushort b6, \\\n                                        metal::ushort b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), \\\n                                          as_type<short>(metal::ushort(b3 << 8 | b2)), \\\n                                          as_type<short>(metal::ushort(b5 << 8 | b4)), \\\n                                          as_type<short>(metal::ushort(b7 << 8 | b6)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Quad), Scalar::I32))\n            }\n            Unorm16 => {\n                let name = self.namer.call(\"unpackUnorm16\");\n                writeln!(\n                    self.out,\n                    \"float {name}(metal::ushort b0, \\\n                                  metal::ushort b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return float(float(b1 << 8 | b0) / 65535.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, None, Scalar::F32))\n            }\n            Unorm16x2 => {\n                let name = self.namer.call(\"unpackUnorm16x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(metal::ushort b0, \\\n                                          metal::ushort b1, \\\n                                          metal::ushort b2, \\\n                                          metal::ushort b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float2(float(b1 << 8 | b0) / 65535.0f, \\\n                                            float(b3 << 8 | b2) / 65535.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Unorm16x4 => {\n                let name = self.namer.call(\"unpackUnorm16x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(metal::ushort b0, \\\n                                          metal::ushort b1, \\\n                                          metal::ushort b2, \\\n                                          metal::ushort b3, \\\n                                          metal::ushort b4, \\\n                                          metal::ushort b5, \\\n                                          metal::ushort b6, \\\n                                          metal::ushort b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(float(b1 << 8 | b0) / 65535.0f, \\\n                                            float(b3 << 8 | b2) / 65535.0f, \\\n                                            float(b5 << 8 | b4) / 65535.0f, \\\n                                            float(b7 << 8 | b6) / 65535.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Snorm16 => {\n                let name = self.namer.call(\"unpackSnorm16\");\n                writeln!(\n                    self.out,\n                    \"float {name}(metal::ushort b0, \\\n                                  metal::ushort b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x;\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, None, Scalar::F32))\n            }\n            Snorm16x2 => {\n                let name = self.namer.call(\"unpackSnorm16x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Snorm16x4 => {\n                let name = self.namer.call(\"unpackSnorm16x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3, \\\n                                          uint b4, \\\n                                          uint b5, \\\n                                          uint b6, \\\n                                          uint b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                            metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Float16 => {\n                let name = self.namer.call(\"unpackFloat16\");\n                writeln!(\n                    self.out,\n                    \"float {name}(metal::ushort b0, \\\n                                  metal::ushort b1) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return float(as_type<half>(metal::ushort(b1 << 8 | b0)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 2, None, Scalar::F32))\n            }\n            Float16x2 => {\n                let name = self.namer.call(\"unpackFloat16x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(metal::ushort b0, \\\n                                          metal::ushort b1, \\\n                                          metal::ushort b2, \\\n                                          metal::ushort b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), \\\n                                            as_type<half>(metal::ushort(b3 << 8 | b2)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Float16x4 => {\n                let name = self.namer.call(\"unpackFloat16x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(metal::ushort b0, \\\n                                        metal::ushort b1, \\\n                                        metal::ushort b2, \\\n                                        metal::ushort b3, \\\n                                        metal::ushort b4, \\\n                                        metal::ushort b5, \\\n                                        metal::ushort b6, \\\n                                        metal::ushort b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), \\\n                                          as_type<half>(metal::ushort(b3 << 8 | b2)), \\\n                                          as_type<half>(metal::ushort(b5 << 8 | b4)), \\\n                                          as_type<half>(metal::ushort(b7 << 8 | b6)));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Float32 => {\n                let name = self.namer.call(\"unpackFloat32\");\n                writeln!(\n                    self.out,\n                    \"float {name}(uint b0, \\\n                                  uint b1, \\\n                                  uint b2, \\\n                                  uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, None, Scalar::F32))\n            }\n            Float32x2 => {\n                let name = self.namer.call(\"unpackFloat32x2\");\n                writeln!(\n                    self.out,\n                    \"metal::float2 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3, \\\n                                          uint b4, \\\n                                          uint b5, \\\n                                          uint b6, \\\n                                          uint b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                            as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Bi), Scalar::F32))\n            }\n            Float32x3 => {\n                let name = self.namer.call(\"unpackFloat32x3\");\n                writeln!(\n                    self.out,\n                    \"metal::float3 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3, \\\n                                          uint b4, \\\n                                          uint b5, \\\n                                          uint b6, \\\n                                          uint b7, \\\n                                          uint b8, \\\n                                          uint b9, \\\n                                          uint b10, \\\n                                          uint b11) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float3(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                            as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                            as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 12, Some(VectorSize::Tri), Scalar::F32))\n            }\n            Float32x4 => {\n                let name = self.namer.call(\"unpackFloat32x4\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3, \\\n                                          uint b4, \\\n                                          uint b5, \\\n                                          uint b6, \\\n                                          uint b7, \\\n                                          uint b8, \\\n                                          uint b9, \\\n                                          uint b10, \\\n                                          uint b11, \\\n                                          uint b12, \\\n                                          uint b13, \\\n                                          uint b14, \\\n                                          uint b15) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                            as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                            as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), \\\n                                            as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 16, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Uint32 => {\n                let name = self.namer.call(\"unpackUint32\");\n                writeln!(\n                    self.out,\n                    \"uint {name}(uint b0, \\\n                                 uint b1, \\\n                                 uint b2, \\\n                                 uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return (b3 << 24 | b2 << 16 | b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, None, Scalar::U32))\n            }\n            Uint32x2 => {\n                let name = self.namer.call(\"unpackUint32x2\");\n                writeln!(\n                    self.out,\n                    \"uint2 {name}(uint b0, \\\n                                  uint b1, \\\n                                  uint b2, \\\n                                  uint b3, \\\n                                  uint b4, \\\n                                  uint b5, \\\n                                  uint b6, \\\n                                  uint b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return uint2((b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                    (b7 << 24 | b6 << 16 | b5 << 8 | b4));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Bi), Scalar::U32))\n            }\n            Uint32x3 => {\n                let name = self.namer.call(\"unpackUint32x3\");\n                writeln!(\n                    self.out,\n                    \"uint3 {name}(uint b0, \\\n                                  uint b1, \\\n                                  uint b2, \\\n                                  uint b3, \\\n                                  uint b4, \\\n                                  uint b5, \\\n                                  uint b6, \\\n                                  uint b7, \\\n                                  uint b8, \\\n                                  uint b9, \\\n                                  uint b10, \\\n                                  uint b11) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return uint3((b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                    (b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                    (b11 << 24 | b10 << 16 | b9 << 8 | b8));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 12, Some(VectorSize::Tri), Scalar::U32))\n            }\n            Uint32x4 => {\n                let name = self.namer.call(\"unpackUint32x4\");\n                writeln!(\n                    self.out,\n                    \"{NAMESPACE}::uint4 {name}(uint b0, \\\n                                  uint b1, \\\n                                  uint b2, \\\n                                  uint b3, \\\n                                  uint b4, \\\n                                  uint b5, \\\n                                  uint b6, \\\n                                  uint b7, \\\n                                  uint b8, \\\n                                  uint b9, \\\n                                  uint b10, \\\n                                  uint b11, \\\n                                  uint b12, \\\n                                  uint b13, \\\n                                  uint b14, \\\n                                  uint b15) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return {NAMESPACE}::uint4((b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                    (b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                    (b11 << 24 | b10 << 16 | b9 << 8 | b8), \\\n                                    (b15 << 24 | b14 << 16 | b13 << 8 | b12));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 16, Some(VectorSize::Quad), Scalar::U32))\n            }\n            Sint32 => {\n                let name = self.namer.call(\"unpackSint32\");\n                writeln!(\n                    self.out,\n                    \"int {name}(uint b0, \\\n                                uint b1, \\\n                                uint b2, \\\n                                uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, None, Scalar::I32))\n            }\n            Sint32x2 => {\n                let name = self.namer.call(\"unpackSint32x2\");\n                writeln!(\n                    self.out,\n                    \"metal::int2 {name}(uint b0, \\\n                                        uint b1, \\\n                                        uint b2, \\\n                                        uint b3, \\\n                                        uint b4, \\\n                                        uint b5, \\\n                                        uint b6, \\\n                                        uint b7) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int2(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                          as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 8, Some(VectorSize::Bi), Scalar::I32))\n            }\n            Sint32x3 => {\n                let name = self.namer.call(\"unpackSint32x3\");\n                writeln!(\n                    self.out,\n                    \"metal::int3 {name}(uint b0, \\\n                                        uint b1, \\\n                                        uint b2, \\\n                                        uint b3, \\\n                                        uint b4, \\\n                                        uint b5, \\\n                                        uint b6, \\\n                                        uint b7, \\\n                                        uint b8, \\\n                                        uint b9, \\\n                                        uint b10, \\\n                                        uint b11) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int3(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                          as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                          as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 12, Some(VectorSize::Tri), Scalar::I32))\n            }\n            Sint32x4 => {\n                let name = self.namer.call(\"unpackSint32x4\");\n                writeln!(\n                    self.out,\n                    \"metal::int4 {name}(uint b0, \\\n                                        uint b1, \\\n                                        uint b2, \\\n                                        uint b3, \\\n                                        uint b4, \\\n                                        uint b5, \\\n                                        uint b6, \\\n                                        uint b7, \\\n                                        uint b8, \\\n                                        uint b9, \\\n                                        uint b10, \\\n                                        uint b11, \\\n                                        uint b12, \\\n                                        uint b13, \\\n                                        uint b14, \\\n                                        uint b15) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::int4(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), \\\n                                          as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), \\\n                                          as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8), \\\n                                          as_type<int>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 16, Some(VectorSize::Quad), Scalar::I32))\n            }\n            Unorm10_10_10_2 => {\n                let name = self.namer.call(\"unpackUnorm10_10_10_2\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(uint b0, \\\n                                          uint b1, \\\n                                          uint b2, \\\n                                          uint b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    // The following is correct for RGBA packing, but our format seems to\n                    // match ABGR, which can be fed into the Metal builtin function\n                    // unpack_unorm10a2_to_float.\n                    /*\n                    \"{}uint v = (b3 << 24 | b2 << 16 | b1 << 8 | b0); \\\n                       uint r = (v & 0xFFC00000) >> 22; \\\n                       uint g = (v & 0x003FF000) >> 12; \\\n                       uint b = (v & 0x00000FFC) >> 2; \\\n                       uint a = (v & 0x00000003); \\\n                       return metal::float4(float(r) / 1023.0f, float(g) / 1023.0f, float(b) / 1023.0f, float(a) / 3.0f);\",\n                    */\n                    \"{}return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::F32))\n            }\n            Unorm8x4Bgra => {\n                let name = self.namer.call(\"unpackUnorm8x4Bgra\");\n                writeln!(\n                    self.out,\n                    \"metal::float4 {name}(metal::uchar b0, \\\n                                          metal::uchar b1, \\\n                                          metal::uchar b2, \\\n                                          metal::uchar b3) {{\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{}return metal::float4(float(b2) / 255.0f, \\\n                                            float(b1) / 255.0f, \\\n                                            float(b0) / 255.0f, \\\n                                            float(b3) / 255.0f);\",\n                    back::INDENT\n                )?;\n                writeln!(self.out, \"}}\")?;\n                Ok((name, 4, Some(VectorSize::Quad), Scalar::F32))\n            }\n        }\n    }\n\n    fn write_wrapped_unary_op(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        op: crate::UnaryOperator,\n        operand: Handle<crate::Expression>,\n    ) -> BackendResult {\n        let operand_ty = func_ctx.resolve_type(operand, &module.types);\n        match op {\n            // Negating the TYPE_MIN of a two's complement signed integer\n            // type causes overflow, which is undefined behaviour in MSL. To\n            // avoid this we bitcast the value to unsigned and negate it,\n            // then bitcast back to signed.\n            // This adheres to the WGSL spec in that the negative of the\n            // type's minimum value should equal to the minimum value.\n            crate::UnaryOperator::Negate\n                if operand_ty.scalar_kind() == Some(crate::ScalarKind::Sint) =>\n            {\n                let Some((vector_size, scalar)) = operand_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let wrapped = WrappedFunction::UnaryOp {\n                    op,\n                    ty: (vector_size, scalar),\n                };\n                if !self.wrapped_functions.insert(wrapped) {\n                    return Ok(());\n                }\n\n                let unsigned_scalar = crate::Scalar {\n                    kind: crate::ScalarKind::Uint,\n                    ..scalar\n                };\n                let mut type_name = String::new();\n                let mut unsigned_type_name = String::new();\n                match vector_size {\n                    None => {\n                        put_numeric_type(&mut type_name, scalar, &[])?;\n                        put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])?\n                    }\n                    Some(size) => {\n                        put_numeric_type(&mut type_name, scalar, &[size])?;\n                        put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[size])?;\n                    }\n                };\n\n                writeln!(self.out, \"{type_name} {NEG_FUNCTION}({type_name} val) {{\")?;\n                let level = back::Level(1);\n                writeln!(\n                    self.out,\n                    \"{level}return as_type<{type_name}>(-as_type<{unsigned_type_name}>(val));\"\n                )?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    fn write_wrapped_binary_op(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        expr: Handle<crate::Expression>,\n        op: crate::BinaryOperator,\n        left: Handle<crate::Expression>,\n        right: Handle<crate::Expression>,\n    ) -> BackendResult {\n        let expr_ty = func_ctx.resolve_type(expr, &module.types);\n        let left_ty = func_ctx.resolve_type(left, &module.types);\n        let right_ty = func_ctx.resolve_type(right, &module.types);\n        match (op, expr_ty.scalar_kind()) {\n            // Signed integer division of TYPE_MIN / -1, or signed or\n            // unsigned division by zero, gives an unspecified value in MSL.\n            // We override the divisor to 1 in these cases.\n            // This adheres to the WGSL spec in that:\n            // * TYPE_MIN / -1 == TYPE_MIN\n            // * x / 0 == x\n            (\n                crate::BinaryOperator::Divide,\n                Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint),\n            ) => {\n                let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let wrapped = WrappedFunction::BinaryOp {\n                    op,\n                    left_ty: left_wrapped_ty,\n                    right_ty: right_wrapped_ty,\n                };\n                if !self.wrapped_functions.insert(wrapped) {\n                    return Ok(());\n                }\n\n                let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let mut type_name = String::new();\n                match vector_size {\n                    None => put_numeric_type(&mut type_name, scalar, &[])?,\n                    Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?,\n                };\n                writeln!(\n                    self.out,\n                    \"{type_name} {DIV_FUNCTION}({type_name} lhs, {type_name} rhs) {{\"\n                )?;\n                let level = back::Level(1);\n                match scalar.kind {\n                    crate::ScalarKind::Sint => {\n                        let min_val = match scalar.width {\n                            4 => crate::Literal::I32(i32::MIN),\n                            8 => crate::Literal::I64(i64::MIN),\n                            _ => {\n                                return Err(Error::GenericValidation(format!(\n                                    \"Unexpected width for scalar {scalar:?}\"\n                                )));\n                            }\n                        };\n                        write!(\n                            self.out,\n                            \"{level}return lhs / metal::select(rhs, 1, (lhs == \"\n                        )?;\n                        self.put_literal(min_val)?;\n                        writeln!(self.out, \" & rhs == -1) | (rhs == 0));\")?\n                    }\n                    crate::ScalarKind::Uint => writeln!(\n                        self.out,\n                        \"{level}return lhs / metal::select(rhs, 1u, rhs == 0u);\"\n                    )?,\n                    _ => unreachable!(),\n                }\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            // Integer modulo where one or both operands are negative, or the\n            // divisor is zero, is undefined behaviour in MSL. To avoid this\n            // we use the following equation:\n            //\n            // dividend - (dividend / divisor) * divisor\n            //\n            // overriding the divisor to 1 if either it is 0, or it is -1\n            // and the dividend is TYPE_MIN.\n            //\n            // This adheres to the WGSL spec in that:\n            // * TYPE_MIN % -1 == 0\n            // * x % 0 == 0\n            (\n                crate::BinaryOperator::Modulo,\n                Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint),\n            ) => {\n                let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let Some((right_vector_size, right_scalar)) = right_ty.vector_size_and_scalar()\n                else {\n                    return Ok(());\n                };\n                let wrapped = WrappedFunction::BinaryOp {\n                    op,\n                    left_ty: left_wrapped_ty,\n                    right_ty: (right_vector_size, right_scalar),\n                };\n                if !self.wrapped_functions.insert(wrapped) {\n                    return Ok(());\n                }\n\n                let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let mut type_name = String::new();\n                match vector_size {\n                    None => put_numeric_type(&mut type_name, scalar, &[])?,\n                    Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?,\n                };\n                let mut rhs_type_name = String::new();\n                match right_vector_size {\n                    None => put_numeric_type(&mut rhs_type_name, right_scalar, &[])?,\n                    Some(size) => put_numeric_type(&mut rhs_type_name, right_scalar, &[size])?,\n                };\n\n                writeln!(\n                    self.out,\n                    \"{type_name} {MOD_FUNCTION}({type_name} lhs, {type_name} rhs) {{\"\n                )?;\n                let level = back::Level(1);\n                match scalar.kind {\n                    crate::ScalarKind::Sint => {\n                        let min_val = match scalar.width {\n                            4 => crate::Literal::I32(i32::MIN),\n                            8 => crate::Literal::I64(i64::MIN),\n                            _ => {\n                                return Err(Error::GenericValidation(format!(\n                                    \"Unexpected width for scalar {scalar:?}\"\n                                )));\n                            }\n                        };\n                        write!(\n                            self.out,\n                            \"{level}{rhs_type_name} divisor = metal::select(rhs, 1, (lhs == \"\n                        )?;\n                        self.put_literal(min_val)?;\n                        writeln!(self.out, \" & rhs == -1) | (rhs == 0));\")?;\n                        writeln!(self.out, \"{level}return lhs - (lhs / divisor) * divisor;\")?\n                    }\n                    crate::ScalarKind::Uint => writeln!(\n                        self.out,\n                        \"{level}return lhs % metal::select(rhs, 1u, rhs == 0u);\"\n                    )?,\n                    _ => unreachable!(),\n                }\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    /// Build the mangled helper name for integer vector dot products.\n    ///\n    /// `scalar` must be a concrete integer scalar type.\n    ///\n    /// Result format: `{DOT_FUNCTION_PREFIX}_{type}{N}` (e.g., `naga_dot_int3`).\n    fn get_dot_wrapper_function_helper_name(\n        &self,\n        scalar: crate::Scalar,\n        size: crate::VectorSize,\n    ) -> String {\n        // Check for consistency with [`super::keywords::RESERVED_SET`]\n        debug_assert!(concrete_int_scalars().any(|s| s == scalar));\n\n        let type_name = scalar.to_msl_name();\n        let size_suffix = common::vector_size_str(size);\n        format!(\"{DOT_FUNCTION_PREFIX}_{type_name}{size_suffix}\")\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn write_wrapped_math_function(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        fun: crate::MathFunction,\n        arg: Handle<crate::Expression>,\n        _arg1: Option<Handle<crate::Expression>>,\n        _arg2: Option<Handle<crate::Expression>>,\n        _arg3: Option<Handle<crate::Expression>>,\n    ) -> BackendResult {\n        let arg_ty = func_ctx.resolve_type(arg, &module.types);\n        match fun {\n            // Taking the absolute value of the TYPE_MIN of a two's\n            // complement signed integer type causes overflow, which is\n            // undefined behaviour in MSL. To avoid this, when the value is\n            // negative we bitcast the value to unsigned and negate it, then\n            // bitcast back to signed.\n            // This adheres to the WGSL spec in that the absolute of the\n            // type's minimum value should equal to the minimum value.\n            crate::MathFunction::Abs if arg_ty.scalar_kind() == Some(crate::ScalarKind::Sint) => {\n                let Some((vector_size, scalar)) = arg_ty.vector_size_and_scalar() else {\n                    return Ok(());\n                };\n                let wrapped = WrappedFunction::Math {\n                    fun,\n                    arg_ty: (vector_size, scalar),\n                };\n                if !self.wrapped_functions.insert(wrapped) {\n                    return Ok(());\n                }\n\n                let unsigned_scalar = crate::Scalar {\n                    kind: crate::ScalarKind::Uint,\n                    ..scalar\n                };\n                let mut type_name = String::new();\n                let mut unsigned_type_name = String::new();\n                match vector_size {\n                    None => {\n                        put_numeric_type(&mut type_name, scalar, &[])?;\n                        put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])?\n                    }\n                    Some(size) => {\n                        put_numeric_type(&mut type_name, scalar, &[size])?;\n                        put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[size])?;\n                    }\n                };\n\n                writeln!(self.out, \"{type_name} {ABS_FUNCTION}({type_name} val) {{\")?;\n                let level = back::Level(1);\n                writeln!(self.out, \"{level}return metal::select(as_type<{type_name}>(-as_type<{unsigned_type_name}>(val)), val, val >= 0);\")?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n\n            crate::MathFunction::Dot => match *arg_ty {\n                crate::TypeInner::Vector { size, scalar }\n                    if matches!(\n                        scalar.kind,\n                        crate::ScalarKind::Sint | crate::ScalarKind::Uint\n                    ) =>\n                {\n                    // De-duplicate per (fun, arg type) like other wrapped math functions\n                    let wrapped = WrappedFunction::Math {\n                        fun,\n                        arg_ty: (Some(size), scalar),\n                    };\n                    if !self.wrapped_functions.insert(wrapped) {\n                        return Ok(());\n                    }\n\n                    let mut vec_ty = String::new();\n                    put_numeric_type(&mut vec_ty, scalar, &[size])?;\n                    let mut ret_ty = String::new();\n                    put_numeric_type(&mut ret_ty, scalar, &[])?;\n\n                    let fun_name = self.get_dot_wrapper_function_helper_name(scalar, size);\n\n                    // Emit function signature and body using put_dot_product for the expression\n                    writeln!(self.out, \"{ret_ty} {fun_name}({vec_ty} a, {vec_ty} b) {{\")?;\n                    let level = back::Level(1);\n                    write!(self.out, \"{level}return \")?;\n                    self.put_dot_product(\"a\", \"b\", size as usize, |writer, name, index| {\n                        write!(writer.out, \"{name}.{}\", back::COMPONENTS[index])?;\n                        Ok(())\n                    })?;\n                    writeln!(self.out, \";\")?;\n                    writeln!(self.out, \"}}\")?;\n                    writeln!(self.out)?;\n                }\n                _ => {}\n            },\n\n            _ => {}\n        }\n        Ok(())\n    }\n\n    fn write_wrapped_cast(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        expr: Handle<crate::Expression>,\n        kind: crate::ScalarKind,\n        convert: Option<crate::Bytes>,\n    ) -> BackendResult {\n        // Avoid undefined behaviour when casting from a float to integer\n        // when the value is out of range for the target type. Additionally\n        // ensure we clamp to the correct value as per the WGSL spec.\n        //\n        // https://www.w3.org/TR/WGSL/#floating-point-conversion:\n        // * If X is exactly representable in the target type T, then the\n        //   result is that value.\n        // * Otherwise, the result is the value in T closest to\n        //   truncate(X) and also exactly representable in the original\n        //   floating point type.\n        let src_ty = func_ctx.resolve_type(expr, &module.types);\n        let Some(width) = convert else {\n            return Ok(());\n        };\n        let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else {\n            return Ok(());\n        };\n        let dst_scalar = crate::Scalar { kind, width };\n        if src_scalar.kind != crate::ScalarKind::Float\n            || (dst_scalar.kind != crate::ScalarKind::Sint\n                && dst_scalar.kind != crate::ScalarKind::Uint)\n        {\n            return Ok(());\n        }\n        let wrapped = WrappedFunction::Cast {\n            src_scalar,\n            vector_size,\n            dst_scalar,\n        };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n        let (min, max) = proc::min_max_float_representable_by(src_scalar, dst_scalar);\n\n        let mut src_type_name = String::new();\n        match vector_size {\n            None => put_numeric_type(&mut src_type_name, src_scalar, &[])?,\n            Some(size) => put_numeric_type(&mut src_type_name, src_scalar, &[size])?,\n        };\n        let mut dst_type_name = String::new();\n        match vector_size {\n            None => put_numeric_type(&mut dst_type_name, dst_scalar, &[])?,\n            Some(size) => put_numeric_type(&mut dst_type_name, dst_scalar, &[size])?,\n        };\n        let fun_name = match dst_scalar {\n            crate::Scalar::I32 => F2I32_FUNCTION,\n            crate::Scalar::U32 => F2U32_FUNCTION,\n            crate::Scalar::I64 => F2I64_FUNCTION,\n            crate::Scalar::U64 => F2U64_FUNCTION,\n            _ => unreachable!(),\n        };\n\n        writeln!(\n            self.out,\n            \"{dst_type_name} {fun_name}({src_type_name} value) {{\"\n        )?;\n        let level = back::Level(1);\n        write!(\n            self.out,\n            \"{level}return static_cast<{dst_type_name}>({NAMESPACE}::clamp(value, \"\n        )?;\n        self.put_literal(min)?;\n        write!(self.out, \", \")?;\n        self.put_literal(max)?;\n        writeln!(self.out, \"));\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    /// Helper function used by [`Self::write_wrapped_image_load`] and\n    /// [`Self::write_wrapped_image_sample`] to write the shared YUV to RGB\n    /// conversion code for external textures. Expects the preceding code to\n    /// declare the Y component as a `float` variable of name `y`, the UV\n    /// components as a `float2` variable of name `uv`, and the external\n    /// texture params as a variable of name `params`. The emitted code will\n    /// return the result.\n    fn write_convert_yuv_to_rgb_and_return(\n        &mut self,\n        level: back::Level,\n        y: &str,\n        uv: &str,\n        params: &str,\n    ) -> BackendResult {\n        let l1 = level;\n        let l2 = l1.next();\n\n        // Convert from YUV to non-linear RGB in the source color space.\n        writeln!(\n            self.out,\n            \"{l1}float3 srcGammaRgb = ({params}.yuv_conversion_matrix * float4({y}, {uv}, 1.0)).rgb;\"\n        )?;\n\n        // Apply the inverse of the source transfer function to convert to\n        // linear RGB in the source color space.\n        writeln!(self.out, \"{l1}float3 srcLinearRgb = {NAMESPACE}::select(\")?;\n        writeln!(self.out, \"{l2}{NAMESPACE}::pow((srcGammaRgb + {params}.src_tf.a - 1.0) / {params}.src_tf.a, {params}.src_tf.g),\")?;\n        writeln!(self.out, \"{l2}srcGammaRgb / {params}.src_tf.k,\")?;\n        writeln!(\n            self.out,\n            \"{l2}srcGammaRgb < {params}.src_tf.k * {params}.src_tf.b);\"\n        )?;\n\n        // Multiply by the gamut conversion matrix to convert to linear RGB in\n        // the destination color space.\n        writeln!(\n            self.out,\n            \"{l1}float3 dstLinearRgb = {params}.gamut_conversion_matrix * srcLinearRgb;\"\n        )?;\n\n        // Finally, apply the dest transfer function to convert to non-linear\n        // RGB in the destination color space, and return the result.\n        writeln!(self.out, \"{l1}float3 dstGammaRgb = {NAMESPACE}::select(\")?;\n        writeln!(self.out, \"{l2}{params}.dst_tf.a * {NAMESPACE}::pow(dstLinearRgb, 1.0 / {params}.dst_tf.g) - ({params}.dst_tf.a - 1),\")?;\n        writeln!(self.out, \"{l2}{params}.dst_tf.k * dstLinearRgb,\")?;\n        writeln!(self.out, \"{l2}dstLinearRgb < {params}.dst_tf.b);\")?;\n\n        writeln!(self.out, \"{l1}return float4(dstGammaRgb, 1.0);\")?;\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn write_wrapped_image_load(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        _coordinate: Handle<crate::Expression>,\n        _array_index: Option<Handle<crate::Expression>>,\n        _sample: Option<Handle<crate::Expression>>,\n        _level: Option<Handle<crate::Expression>>,\n    ) -> BackendResult {\n        // We currently only need to wrap image loads for external textures\n        let class = match *func_ctx.resolve_type(image, &module.types) {\n            crate::TypeInner::Image { class, .. } => class,\n            _ => unreachable!(),\n        };\n        if class != crate::ImageClass::External {\n            return Ok(());\n        }\n        let wrapped = WrappedFunction::ImageLoad { class };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n\n        writeln!(self.out, \"float4 {IMAGE_LOAD_EXTERNAL_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex, uint2 coords) {{\")?;\n        let l1 = back::Level(1);\n        let l2 = l1.next();\n        let l3 = l2.next();\n        writeln!(\n            self.out,\n            \"{l1}uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());\"\n        )?;\n        // Clamp coords to provided size of external texture to prevent OOB\n        // read. If params.size is zero then clamp to the actual size of the\n        // texture.\n        writeln!(\n            self.out,\n            \"{l1}uint2 cropped_size = {NAMESPACE}::any(tex.params.size != 0) ? tex.params.size : plane0_size;\"\n        )?;\n        writeln!(\n            self.out,\n            \"{l1}coords = {NAMESPACE}::min(coords, cropped_size - 1);\"\n        )?;\n\n        // Apply load transformation\n        writeln!(self.out, \"{l1}uint2 plane0_coords = uint2({NAMESPACE}::round(tex.params.load_transform * float3(float2(coords), 1.0)));\")?;\n        writeln!(self.out, \"{l1}if (tex.params.num_planes == 1u) {{\")?;\n        // For single plane, simply read from plane0\n        writeln!(self.out, \"{l2}return tex.plane0.read(plane0_coords);\")?;\n        writeln!(self.out, \"{l1}}} else {{\")?;\n\n        // Chroma planes may be subsampled so we must scale the coords accordingly.\n        writeln!(\n            self.out,\n            \"{l2}uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());\"\n        )?;\n        writeln!(self.out, \"{l2}uint2 plane1_coords = uint2({NAMESPACE}::floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));\")?;\n\n        // For multi-plane, read the Y value from plane 0\n        writeln!(self.out, \"{l2}float y = tex.plane0.read(plane0_coords).x;\")?;\n\n        writeln!(self.out, \"{l2}float2 uv;\")?;\n        writeln!(self.out, \"{l2}if (tex.params.num_planes == 2u) {{\")?;\n        // For 2 planes, read UV from interleaved plane 1\n        writeln!(self.out, \"{l3}uv = tex.plane1.read(plane1_coords).xy;\")?;\n        writeln!(self.out, \"{l2}}} else {{\")?;\n        // For 3 planes, read U and V from planes 1 and 2 respectively\n        writeln!(\n            self.out,\n            \"{l2}uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());\"\n        )?;\n        writeln!(self.out, \"{l2}uint2 plane2_coords = uint2({NAMESPACE}::floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));\")?;\n        writeln!(\n            self.out,\n            \"{l3}uv = float2(tex.plane1.read(plane1_coords).x, tex.plane2.read(plane2_coords).x);\"\n        )?;\n        writeln!(self.out, \"{l2}}}\")?;\n\n        self.write_convert_yuv_to_rgb_and_return(l2, \"y\", \"uv\", \"tex.params\")?;\n\n        writeln!(self.out, \"{l1}}}\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn write_wrapped_image_sample(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        _sampler: Handle<crate::Expression>,\n        _gather: Option<crate::SwizzleComponent>,\n        _coordinate: Handle<crate::Expression>,\n        _array_index: Option<Handle<crate::Expression>>,\n        _offset: Option<Handle<crate::Expression>>,\n        _level: crate::SampleLevel,\n        _depth_ref: Option<Handle<crate::Expression>>,\n        clamp_to_edge: bool,\n    ) -> BackendResult {\n        // We currently only need to wrap textureSampleBaseClampToEdge, for\n        // both sampled and external textures.\n        if !clamp_to_edge {\n            return Ok(());\n        }\n        let class = match *func_ctx.resolve_type(image, &module.types) {\n            crate::TypeInner::Image { class, .. } => class,\n            _ => unreachable!(),\n        };\n        let wrapped = WrappedFunction::ImageSample {\n            class,\n            clamp_to_edge: true,\n        };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n        match class {\n            crate::ImageClass::External => {\n                writeln!(self.out, \"float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex, {NAMESPACE}::sampler samp, float2 coords) {{\")?;\n                let l1 = back::Level(1);\n                let l2 = l1.next();\n                let l3 = l2.next();\n                writeln!(self.out, \"{l1}uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}coords = tex.params.sample_transform * float3(coords, 1.0);\"\n                )?;\n\n                // Calculate the sample bounds. The purported size of the texture\n                // (params.size) is irrelevant here as we are dealing with normalized\n                // coordinates. Usually we would clamp to (0,0)..(1,1). However, we must\n                // apply the sample transformation to that, also bearing in mind that it\n                // may contain a flip on either axis. We calculate and adjust for the\n                // half-texel separately for each plane as it depends on the actual\n                // texture size which may vary between planes.\n                writeln!(\n                    self.out,\n                    \"{l1}float2 bounds_min = tex.params.sample_transform * float3(0.0, 0.0, 1.0);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 bounds_max = tex.params.sample_transform * float3(1.0, 1.0, 1.0);\"\n                )?;\n                writeln!(self.out, \"{l1}float4 bounds = float4({NAMESPACE}::min(bounds_min, bounds_max), {NAMESPACE}::max(bounds_min, bounds_max));\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 plane0_half_texel = float2(0.5, 0.5) / float2(plane0_size);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l1}float2 plane0_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);\"\n                )?;\n                writeln!(self.out, \"{l1}if (tex.params.num_planes == 1u) {{\")?;\n                // For single plane, simply sample from plane0\n                writeln!(\n                    self.out,\n                    \"{l2}return tex.plane0.sample(samp, plane0_coords, {NAMESPACE}::level(0.0f));\"\n                )?;\n                writeln!(self.out, \"{l1}}} else {{\")?;\n                writeln!(self.out, \"{l2}uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());\")?;\n                writeln!(\n                    self.out,\n                    \"{l2}float2 plane1_half_texel = float2(0.5, 0.5) / float2(plane1_size);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l2}float2 plane1_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);\"\n                )?;\n\n                // For multi-plane, sample the Y value from plane 0\n                writeln!(\n                    self.out,\n                    \"{l2}float y = tex.plane0.sample(samp, plane0_coords, {NAMESPACE}::level(0.0f)).r;\"\n                )?;\n                writeln!(self.out, \"{l2}float2 uv = float2(0.0, 0.0);\")?;\n                writeln!(self.out, \"{l2}if (tex.params.num_planes == 2u) {{\")?;\n                // For 2 planes, sample UV from interleaved plane 1\n                writeln!(\n                    self.out,\n                    \"{l3}uv = tex.plane1.sample(samp, plane1_coords, {NAMESPACE}::level(0.0f)).xy;\"\n                )?;\n                writeln!(self.out, \"{l2}}} else {{\")?;\n                // For 3 planes, sample U and V from planes 1 and 2 respectively\n                writeln!(self.out, \"{l3}uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());\")?;\n                writeln!(\n                    self.out,\n                    \"{l3}float2 plane2_half_texel = float2(0.5, 0.5) / float2(plane2_size);\"\n                )?;\n                writeln!(\n                    self.out,\n                    \"{l3}float2 plane2_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane1_half_texel);\"\n                )?;\n                writeln!(self.out, \"{l3}uv.x = tex.plane1.sample(samp, plane1_coords, {NAMESPACE}::level(0.0f)).x;\")?;\n                writeln!(self.out, \"{l3}uv.y = tex.plane2.sample(samp, plane2_coords, {NAMESPACE}::level(0.0f)).x;\")?;\n                writeln!(self.out, \"{l2}}}\")?;\n\n                self.write_convert_yuv_to_rgb_and_return(l2, \"y\", \"uv\", \"tex.params\")?;\n\n                writeln!(self.out, \"{l1}}}\")?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n            _ => {\n                writeln!(self.out, \"{NAMESPACE}::float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}({NAMESPACE}::texture2d<float, {NAMESPACE}::access::sample> tex, {NAMESPACE}::sampler samp, {NAMESPACE}::float2 coords) {{\")?;\n                let l1 = back::Level(1);\n                writeln!(self.out, \"{l1}{NAMESPACE}::float2 half_texel = 0.5 / {NAMESPACE}::float2(tex.get_width(0u), tex.get_height(0u));\")?;\n                writeln!(\n                    self.out,\n                    \"{l1}return tex.sample(samp, {NAMESPACE}::clamp(coords, half_texel, 1.0 - half_texel), {NAMESPACE}::level(0.0));\"\n                )?;\n                writeln!(self.out, \"}}\")?;\n                writeln!(self.out)?;\n            }\n        }\n        Ok(())\n    }\n\n    fn write_wrapped_image_query(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        image: Handle<crate::Expression>,\n        query: crate::ImageQuery,\n    ) -> BackendResult {\n        // We currently only need to wrap size image queries for external textures\n        if !matches!(query, crate::ImageQuery::Size { .. }) {\n            return Ok(());\n        }\n        let class = match *func_ctx.resolve_type(image, &module.types) {\n            crate::TypeInner::Image { class, .. } => class,\n            _ => unreachable!(),\n        };\n        if class != crate::ImageClass::External {\n            return Ok(());\n        }\n        let wrapped = WrappedFunction::ImageQuerySize { class };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n        writeln!(\n            self.out,\n            \"uint2 {IMAGE_SIZE_EXTERNAL_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex) {{\"\n        )?;\n        let l1 = back::Level(1);\n        let l2 = l1.next();\n        writeln!(\n            self.out,\n            \"{l1}if ({NAMESPACE}::any(tex.params.size != uint2(0u))) {{\"\n        )?;\n        writeln!(self.out, \"{l2}return tex.params.size;\")?;\n        writeln!(self.out, \"{l1}}} else {{\")?;\n        // params.size == (0, 0) indicates to query and return plane 0's actual size\n        writeln!(\n            self.out,\n            \"{l2}return uint2(tex.plane0.get_width(), tex.plane0.get_height());\"\n        )?;\n        writeln!(self.out, \"{l1}}}\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    fn write_wrapped_cooperative_load(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        columns: crate::CooperativeSize,\n        rows: crate::CooperativeSize,\n        pointer: Handle<crate::Expression>,\n    ) -> BackendResult {\n        let ptr_ty = func_ctx.resolve_type(pointer, &module.types);\n        let space = ptr_ty.pointer_space().unwrap();\n        let space_name = space.to_msl_name().unwrap_or_default();\n        let scalar = ptr_ty\n            .pointer_base_type()\n            .unwrap()\n            .inner_with(&module.types)\n            .scalar()\n            .unwrap();\n        let wrapped = WrappedFunction::CooperativeLoad {\n            space_name,\n            columns,\n            rows,\n            scalar,\n        };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n        let scalar_name = scalar.to_msl_name();\n        writeln!(\n            self.out,\n            \"{NAMESPACE}::simdgroup_{scalar_name}{}x{} {COOPERATIVE_LOAD_FUNCTION}(const {space_name} {scalar_name}* ptr, int stride, bool is_row_major) {{\",\n            columns as u32, rows as u32,\n        )?;\n        let l1 = back::Level(1);\n        writeln!(\n            self.out,\n            \"{l1}{NAMESPACE}::simdgroup_{scalar_name}{}x{} m;\",\n            columns as u32, rows as u32\n        )?;\n        let matrix_origin = \"0\";\n        writeln!(\n            self.out,\n            \"{l1}simdgroup_load(m, ptr, stride, {matrix_origin}, is_row_major);\"\n        )?;\n        writeln!(self.out, \"{l1}return m;\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    fn write_wrapped_cooperative_multiply_add(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n        space: crate::AddressSpace,\n        a: Handle<crate::Expression>,\n        b: Handle<crate::Expression>,\n    ) -> BackendResult {\n        let space_name = space.to_msl_name().unwrap_or_default();\n        let (a_c, a_r, scalar) = match *func_ctx.resolve_type(a, &module.types) {\n            crate::TypeInner::CooperativeMatrix {\n                columns,\n                rows,\n                scalar,\n                ..\n            } => (columns, rows, scalar),\n            _ => unreachable!(),\n        };\n        let (b_c, b_r) = match *func_ctx.resolve_type(b, &module.types) {\n            crate::TypeInner::CooperativeMatrix { columns, rows, .. } => (columns, rows),\n            _ => unreachable!(),\n        };\n        let wrapped = WrappedFunction::CooperativeMultiplyAdd {\n            space_name,\n            columns: b_c,\n            rows: a_r,\n            intermediate: a_c,\n            scalar,\n        };\n        if !self.wrapped_functions.insert(wrapped) {\n            return Ok(());\n        }\n        let scalar_name = scalar.to_msl_name();\n        writeln!(\n            self.out,\n            \"{NAMESPACE}::simdgroup_{scalar_name}{}x{} {COOPERATIVE_MULTIPLY_ADD_FUNCTION}(const {space_name} {NAMESPACE}::simdgroup_{scalar_name}{}x{}& a, const {space_name} {NAMESPACE}::simdgroup_{scalar_name}{}x{}& b, const {space_name} {NAMESPACE}::simdgroup_{scalar_name}{}x{}& c) {{\",\n            b_c as u32, a_r as u32, a_c as u32, a_r as u32, b_c as u32, b_r as u32, b_c as u32, a_r as u32,\n        )?;\n        let l1 = back::Level(1);\n        writeln!(\n            self.out,\n            \"{l1}{NAMESPACE}::simdgroup_{scalar_name}{}x{} d;\",\n            b_c as u32, a_r as u32\n        )?;\n        writeln!(self.out, \"{l1}simdgroup_multiply_accumulate(d,a,b,c);\")?;\n        writeln!(self.out, \"{l1}return d;\")?;\n        writeln!(self.out, \"}}\")?;\n        writeln!(self.out)?;\n        Ok(())\n    }\n\n    pub(super) fn write_wrapped_functions(\n        &mut self,\n        module: &crate::Module,\n        func_ctx: &back::FunctionCtx,\n    ) -> BackendResult {\n        for (expr_handle, expr) in func_ctx.expressions.iter() {\n            match *expr {\n                crate::Expression::Unary { op, expr: operand } => {\n                    self.write_wrapped_unary_op(module, func_ctx, op, operand)?;\n                }\n                crate::Expression::Binary { op, left, right } => {\n                    self.write_wrapped_binary_op(module, func_ctx, expr_handle, op, left, right)?;\n                }\n                crate::Expression::Math {\n                    fun,\n                    arg,\n                    arg1,\n                    arg2,\n                    arg3,\n                } => {\n                    self.write_wrapped_math_function(module, func_ctx, fun, arg, arg1, arg2, arg3)?;\n                }\n                crate::Expression::As {\n                    expr,\n                    kind,\n                    convert,\n                } => {\n                    self.write_wrapped_cast(module, func_ctx, expr, kind, convert)?;\n                }\n                crate::Expression::ImageLoad {\n                    image,\n                    coordinate,\n                    array_index,\n                    sample,\n                    level,\n                } => {\n                    self.write_wrapped_image_load(\n                        module,\n                        func_ctx,\n                        image,\n                        coordinate,\n                        array_index,\n                        sample,\n                        level,\n                    )?;\n                }\n                crate::Expression::ImageSample {\n                    image,\n                    sampler,\n                    gather,\n                    coordinate,\n                    array_index,\n                    offset,\n                    level,\n                    depth_ref,\n                    clamp_to_edge,\n                } => {\n                    self.write_wrapped_image_sample(\n                        module,\n                        func_ctx,\n                        image,\n                        sampler,\n                        gather,\n                        coordinate,\n                        array_index,\n                        offset,\n                        level,\n                        depth_ref,\n                        clamp_to_edge,\n                    )?;\n                }\n                crate::Expression::ImageQuery { image, query } => {\n                    self.write_wrapped_image_query(module, func_ctx, image, query)?;\n                }\n                crate::Expression::CooperativeLoad {\n                    columns,\n                    rows,\n                    role: _,\n                    ref data,\n                } => {\n                    self.write_wrapped_cooperative_load(\n                        module,\n                        func_ctx,\n                        columns,\n                        rows,\n                        data.pointer,\n                    )?;\n                }\n                crate::Expression::CooperativeMultiplyAdd { a, b, c: _ } => {\n                    let space = crate::AddressSpace::Private;\n                    self.write_wrapped_cooperative_multiply_add(module, func_ctx, space, a, b)?;\n                }\n                _ => {}\n            }\n        }\n\n        Ok(())\n    }\n\n    // Returns the array of mapped entry point names.\n    fn write_functions(\n        &mut self,\n        module: &crate::Module,\n        mod_info: &valid::ModuleInfo,\n        options: &Options,\n        pipeline_options: &PipelineOptions,\n    ) -> Result<TranslationInfo, Error> {\n        use back::msl::VertexFormat;\n\n        // Define structs to hold resolved/generated data for vertex buffers and\n        // their attributes.\n        struct AttributeMappingResolved {\n            ty_name: String,\n            dimension: Option<crate::VectorSize>,\n            scalar: crate::Scalar,\n            name: String,\n        }\n        let mut am_resolved = FastHashMap::<u32, AttributeMappingResolved>::default();\n\n        struct VertexBufferMappingResolved<'a> {\n            id: u32,\n            stride: u32,\n            step_mode: back::msl::VertexBufferStepMode,\n            ty_name: String,\n            param_name: String,\n            elem_name: String,\n            attributes: &'a Vec<back::msl::AttributeMapping>,\n        }\n        let mut vbm_resolved = Vec::<VertexBufferMappingResolved>::new();\n\n        // Define a struct to hold a named reference to a byte-unpacking function.\n        struct UnpackingFunction {\n            name: String,\n            byte_count: u32,\n            dimension: Option<crate::VectorSize>,\n            scalar: crate::Scalar,\n        }\n        let mut unpacking_functions = FastHashMap::<VertexFormat, UnpackingFunction>::default();\n\n        // Check if we are attempting vertex pulling. If we are, generate some\n        // names we'll need, and iterate the vertex buffer mappings to output\n        // all the conversion functions we'll need to unpack the attribute data.\n        // We can re-use these names for all entry points that need them, since\n        // those entry points also use self.namer.\n        let mut needs_vertex_id = false;\n        let v_id = self.namer.call(\"v_id\");\n\n        let mut needs_instance_id = false;\n        let i_id = self.namer.call(\"i_id\");\n        if pipeline_options.vertex_pulling_transform {\n            for vbm in &pipeline_options.vertex_buffer_mappings {\n                let buffer_id = vbm.id;\n                let buffer_stride = vbm.stride;\n\n                assert!(\n                    buffer_stride > 0,\n                    \"Vertex pulling requires a non-zero buffer stride.\"\n                );\n\n                match vbm.step_mode {\n                    back::msl::VertexBufferStepMode::Constant => {}\n                    back::msl::VertexBufferStepMode::ByVertex => {\n                        needs_vertex_id = true;\n                    }\n                    back::msl::VertexBufferStepMode::ByInstance => {\n                        needs_instance_id = true;\n                    }\n                }\n\n                let buffer_ty = self.namer.call(format!(\"vb_{buffer_id}_type\").as_str());\n                let buffer_param = self.namer.call(format!(\"vb_{buffer_id}_in\").as_str());\n                let buffer_elem = self.namer.call(format!(\"vb_{buffer_id}_elem\").as_str());\n\n                vbm_resolved.push(VertexBufferMappingResolved {\n                    id: buffer_id,\n                    stride: buffer_stride,\n                    step_mode: vbm.step_mode,\n                    ty_name: buffer_ty,\n                    param_name: buffer_param,\n                    elem_name: buffer_elem,\n                    attributes: &vbm.attributes,\n                });\n\n                // Iterate the attributes and generate needed unpacking functions.\n                for attribute in &vbm.attributes {\n                    if unpacking_functions.contains_key(&attribute.format) {\n                        continue;\n                    }\n                    let (name, byte_count, dimension, scalar) =\n                        match self.write_unpacking_function(attribute.format) {\n                            Ok((name, byte_count, dimension, scalar)) => {\n                                (name, byte_count, dimension, scalar)\n                            }\n                            _ => {\n                                continue;\n                            }\n                        };\n                    unpacking_functions.insert(\n                        attribute.format,\n                        UnpackingFunction {\n                            name,\n                            byte_count,\n                            dimension,\n                            scalar,\n                        },\n                    );\n                }\n            }\n        }\n\n        let mut pass_through_globals = Vec::new();\n        for (fun_handle, fun) in module.functions.iter() {\n            log::trace!(\n                \"function {:?}, handle {:?}\",\n                fun.name.as_deref().unwrap_or(\"(anonymous)\"),\n                fun_handle\n            );\n\n            let ctx = back::FunctionCtx {\n                ty: back::FunctionType::Function(fun_handle),\n                info: &mod_info[fun_handle],\n                expressions: &fun.expressions,\n                named_expressions: &fun.named_expressions,\n            };\n\n            writeln!(self.out)?;\n            self.write_wrapped_functions(module, &ctx)?;\n\n            let fun_info = &mod_info[fun_handle];\n            pass_through_globals.clear();\n            let mut needs_buffer_sizes = false;\n            for (handle, var) in module.global_variables.iter() {\n                if !fun_info[handle].is_empty() {\n                    if var.space.needs_pass_through() {\n                        pass_through_globals.push(handle);\n                    }\n                    needs_buffer_sizes |= needs_array_length(var.ty, &module.types);\n                }\n            }\n\n            let fun_name = &self.names[&NameKey::Function(fun_handle)];\n            match fun.result {\n                Some(ref result) => {\n                    let ty_name = TypeContext {\n                        handle: result.ty,\n                        gctx: module.to_ctx(),\n                        names: &self.names,\n                        access: crate::StorageAccess::empty(),\n                        first_time: false,\n                    };\n                    write!(self.out, \"{ty_name}\")?;\n                }\n                None => {\n                    write!(self.out, \"void\")?;\n                }\n            }\n            writeln!(self.out, \" {fun_name}(\")?;\n\n            for (index, arg) in fun.arguments.iter().enumerate() {\n                let name = &self.names[&NameKey::FunctionArgument(fun_handle, index as u32)];\n                let param_type_name = TypeContext {\n                    handle: arg.ty,\n                    gctx: module.to_ctx(),\n                    names: &self.names,\n                    access: crate::StorageAccess::empty(),\n                    first_time: false,\n                };\n                let separator = separate(\n                    !pass_through_globals.is_empty()\n                        || index + 1 != fun.arguments.len()\n                        || needs_buffer_sizes,\n                );\n                writeln!(\n                    self.out,\n                    \"{}{} {}{}\",\n                    back::INDENT,\n                    param_type_name,\n                    name,\n                    separator\n                )?;\n            }\n            for (index, &handle) in pass_through_globals.iter().enumerate() {\n                let tyvar = TypedGlobalVariable {\n                    module,\n                    names: &self.names,\n                    handle,\n                    usage: fun_info[handle],\n                    reference: true,\n                };\n                let separator =\n                    separate(index + 1 != pass_through_globals.len() || needs_buffer_sizes);\n                write!(self.out, \"{}\", back::INDENT)?;\n                tyvar.try_fmt(&mut self.out)?;\n                writeln!(self.out, \"{separator}\")?;\n            }\n\n            if needs_buffer_sizes {\n                writeln!(\n                    self.out,\n                    \"{}constant _mslBufferSizes& _buffer_sizes\",\n                    back::INDENT\n                )?;\n            }\n\n            writeln!(self.out, \") {{\")?;\n\n            let guarded_indices =\n                index::find_checked_indexes(module, fun, fun_info, options.bounds_check_policies);\n\n            let context = StatementContext {\n                expression: ExpressionContext {\n                    function: fun,\n                    origin: FunctionOrigin::Handle(fun_handle),\n                    info: fun_info,\n                    lang_version: options.lang_version,\n                    policies: options.bounds_check_policies,\n                    guarded_indices,\n                    module,\n                    mod_info,\n                    pipeline_options,\n                    force_loop_bounding: options.force_loop_bounding,\n                },\n                result_struct: None,\n            };\n\n            self.put_locals(&context.expression)?;\n            self.update_expressions_to_bake(fun, fun_info, &context.expression);\n            self.put_block(back::Level(1), &fun.body, &context)?;\n            writeln!(self.out, \"}}\")?;\n            self.named_expressions.clear();\n        }\n\n        let ep_range = get_entry_points(module, pipeline_options.entry_point.as_ref())\n            .map_err(|(stage, name)| Error::EntryPointNotFound(stage, name))?;\n\n        let mut info = TranslationInfo {\n            entry_point_names: Vec::with_capacity(ep_range.len()),\n        };\n\n        for ep_index in ep_range {\n            let ep = &module.entry_points[ep_index];\n            let fun = &ep.function;\n            let fun_info = mod_info.get_entry_point(ep_index);\n            let mut ep_error = None;\n\n            // For vertex_id and instance_id arguments, presume that we'll\n            // use our generated names, but switch to the name of an\n            // existing @builtin param, if we find one.\n            let mut v_existing_id = None;\n            let mut i_existing_id = None;\n\n            log::trace!(\n                \"entry point {:?}, index {:?}\",\n                fun.name.as_deref().unwrap_or(\"(anonymous)\"),\n                ep_index\n            );\n\n            let ctx = back::FunctionCtx {\n                ty: back::FunctionType::EntryPoint(ep_index as u16),\n                info: fun_info,\n                expressions: &fun.expressions,\n                named_expressions: &fun.named_expressions,\n            };\n\n            self.write_wrapped_functions(module, &ctx)?;\n\n            let (em_str, in_mode, out_mode, can_vertex_pull) = match ep.stage {\n                crate::ShaderStage::Vertex => (\n                    \"vertex\",\n                    LocationMode::VertexInput,\n                    LocationMode::VertexOutput,\n                    true,\n                ),\n                crate::ShaderStage::Fragment => (\n                    \"fragment\",\n                    LocationMode::FragmentInput,\n                    LocationMode::FragmentOutput,\n                    false,\n                ),\n                crate::ShaderStage::Compute => (\n                    \"kernel\",\n                    LocationMode::Uniform,\n                    LocationMode::Uniform,\n                    false,\n                ),\n                crate::ShaderStage::Task | crate::ShaderStage::Mesh => unimplemented!(),\n                crate::ShaderStage::RayGeneration\n                | crate::ShaderStage::AnyHit\n                | crate::ShaderStage::ClosestHit\n                | crate::ShaderStage::Miss => unimplemented!(),\n            };\n\n            // Should this entry point be modified to do vertex pulling?\n            let do_vertex_pulling = can_vertex_pull\n                && pipeline_options.vertex_pulling_transform\n                && !pipeline_options.vertex_buffer_mappings.is_empty();\n\n            // Is any global variable used by this entry point dynamically sized?\n            let needs_buffer_sizes = do_vertex_pulling\n                || module\n                    .global_variables\n                    .iter()\n                    .filter(|&(handle, _)| !fun_info[handle].is_empty())\n                    .any(|(_, var)| needs_array_length(var.ty, &module.types));\n\n            // skip this entry point if any global bindings are missing,\n            // or their types are incompatible.\n            if !options.fake_missing_bindings {\n                for (var_handle, var) in module.global_variables.iter() {\n                    if fun_info[var_handle].is_empty() {\n                        continue;\n                    }\n                    match var.space {\n                        crate::AddressSpace::Uniform\n                        | crate::AddressSpace::Storage { .. }\n                        | crate::AddressSpace::Handle => {\n                            let br = match var.binding {\n                                Some(ref br) => br,\n                                None => {\n                                    let var_name = var.name.clone().unwrap_or_default();\n                                    ep_error =\n                                        Some(super::EntryPointError::MissingBinding(var_name));\n                                    break;\n                                }\n                            };\n                            let target = options.get_resource_binding_target(ep, br);\n                            let good = match target {\n                                Some(target) => {\n                                    // We intentionally don't dereference binding_arrays here,\n                                    // so that binding arrays fall to the buffer location.\n\n                                    match module.types[var.ty].inner {\n                                        crate::TypeInner::Image {\n                                            class: crate::ImageClass::External,\n                                            ..\n                                        } => target.external_texture.is_some(),\n                                        crate::TypeInner::Image { .. } => target.texture.is_some(),\n                                        crate::TypeInner::Sampler { .. } => {\n                                            target.sampler.is_some()\n                                        }\n                                        _ => target.buffer.is_some(),\n                                    }\n                                }\n                                None => false,\n                            };\n                            if !good {\n                                ep_error = Some(super::EntryPointError::MissingBindTarget(*br));\n                                break;\n                            }\n                        }\n                        crate::AddressSpace::Immediate => {\n                            if let Err(e) = options.resolve_immediates(ep) {\n                                ep_error = Some(e);\n                                break;\n                            }\n                        }\n                        crate::AddressSpace::TaskPayload => {\n                            unimplemented!()\n                        }\n                        crate::AddressSpace::Function\n                        | crate::AddressSpace::Private\n                        | crate::AddressSpace::WorkGroup => {}\n                        crate::AddressSpace::RayPayload\n                        | crate::AddressSpace::IncomingRayPayload => unimplemented!(),\n                    }\n                }\n                if needs_buffer_sizes {\n                    if let Err(err) = options.resolve_sizes_buffer(ep) {\n                        ep_error = Some(err);\n                    }\n                }\n            }\n\n            if let Some(err) = ep_error {\n                info.entry_point_names.push(Err(err));\n                continue;\n            }\n            let fun_name = &self.names[&NameKey::EntryPoint(ep_index as _)];\n            info.entry_point_names.push(Ok(fun_name.clone()));\n\n            writeln!(self.out)?;\n\n            // Since `Namer.reset` wasn't expecting struct members to be\n            // suddenly injected into another namespace like this,\n            // `self.names` doesn't keep them distinct from other variables.\n            // Generate fresh names for these arguments, and remember the\n            // mapping.\n            let mut flattened_member_names = FastHashMap::default();\n            // Varyings' members get their own namespace\n            let mut varyings_namer = proc::Namer::default();\n\n            // List all the Naga `EntryPoint`'s `Function`'s arguments,\n            // flattening structs into their members. In Metal, we will pass\n            // each of these values to the entry point as a separate argument—\n            // except for the varyings, handled next.\n            let mut flattened_arguments = Vec::new();\n            for (arg_index, arg) in fun.arguments.iter().enumerate() {\n                match module.types[arg.ty].inner {\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        for (member_index, member) in members.iter().enumerate() {\n                            let member_index = member_index as u32;\n                            flattened_arguments.push((\n                                NameKey::StructMember(arg.ty, member_index),\n                                member.ty,\n                                member.binding.as_ref(),\n                            ));\n                            let name_key = NameKey::StructMember(arg.ty, member_index);\n                            let name = match member.binding {\n                                Some(crate::Binding::Location { .. }) => {\n                                    if do_vertex_pulling {\n                                        self.namer.call(&self.names[&name_key])\n                                    } else {\n                                        varyings_namer.call(&self.names[&name_key])\n                                    }\n                                }\n                                _ => self.namer.call(&self.names[&name_key]),\n                            };\n                            flattened_member_names.insert(name_key, name);\n                        }\n                    }\n                    _ => flattened_arguments.push((\n                        NameKey::EntryPointArgument(ep_index as _, arg_index as u32),\n                        arg.ty,\n                        arg.binding.as_ref(),\n                    )),\n                }\n            }\n\n            // Identify the varyings among the argument values, and maybe emit\n            // a struct type named `<fun>Input` to hold them. If we are doing\n            // vertex pulling, we instead update our attribute mapping to\n            // note the types, names, and zero values of the attributes.\n            let stage_in_name = self.namer.call(&format!(\"{fun_name}Input\"));\n            let varyings_member_name = self.namer.call(\"varyings\");\n            let mut has_varyings = false;\n            if !flattened_arguments.is_empty() {\n                if !do_vertex_pulling {\n                    writeln!(self.out, \"struct {stage_in_name} {{\")?;\n                }\n                for &(ref name_key, ty, binding) in flattened_arguments.iter() {\n                    let Some(binding) = binding else {\n                        continue;\n                    };\n                    let name = match *name_key {\n                        NameKey::StructMember(..) => &flattened_member_names[name_key],\n                        _ => &self.names[name_key],\n                    };\n                    let ty_name = TypeContext {\n                        handle: ty,\n                        gctx: module.to_ctx(),\n                        names: &self.names,\n                        access: crate::StorageAccess::empty(),\n                        first_time: false,\n                    };\n                    let resolved = options.resolve_local_binding(binding, in_mode)?;\n                    let location = match *binding {\n                        crate::Binding::Location { location, .. } => Some(location),\n                        crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { .. }) => None,\n                        crate::Binding::BuiltIn(_) => continue,\n                    };\n                    if do_vertex_pulling {\n                        let Some(location) = location else {\n                            continue;\n                        };\n                        // Update our attribute mapping.\n                        am_resolved.insert(\n                            location,\n                            AttributeMappingResolved {\n                                ty_name: ty_name.to_string(),\n                                dimension: ty_name.vector_size(),\n                                scalar: ty_name.scalar().unwrap(),\n                                name: name.to_string(),\n                            },\n                        );\n                    } else {\n                        has_varyings = true;\n                        write!(self.out, \"{}{} {}\", back::INDENT, ty_name, name)?;\n                        resolved.try_fmt(&mut self.out)?;\n                        writeln!(self.out, \";\")?;\n                    }\n                }\n                if !do_vertex_pulling {\n                    writeln!(self.out, \"}};\")?;\n                }\n            }\n\n            // Define a struct type named for the return value, if any, named\n            // `<fun>Output`.\n            let stage_out_name = self.namer.call(&format!(\"{fun_name}Output\"));\n            let result_member_name = self.namer.call(\"member\");\n            let result_type_name = match fun.result {\n                Some(ref result) => {\n                    let mut result_members = Vec::new();\n                    if let crate::TypeInner::Struct { ref members, .. } =\n                        module.types[result.ty].inner\n                    {\n                        for (member_index, member) in members.iter().enumerate() {\n                            result_members.push((\n                                &self.names[&NameKey::StructMember(result.ty, member_index as u32)],\n                                member.ty,\n                                member.binding.as_ref(),\n                            ));\n                        }\n                    } else {\n                        result_members.push((\n                            &result_member_name,\n                            result.ty,\n                            result.binding.as_ref(),\n                        ));\n                    }\n\n                    writeln!(self.out, \"struct {stage_out_name} {{\")?;\n                    let mut has_point_size = false;\n                    for (name, ty, binding) in result_members {\n                        let ty_name = TypeContext {\n                            handle: ty,\n                            gctx: module.to_ctx(),\n                            names: &self.names,\n                            access: crate::StorageAccess::empty(),\n                            first_time: true,\n                        };\n                        let binding = binding.ok_or_else(|| {\n                            Error::GenericValidation(\"Expected binding, got None\".into())\n                        })?;\n\n                        if let crate::Binding::BuiltIn(crate::BuiltIn::PointSize) = *binding {\n                            has_point_size = true;\n                            if !pipeline_options.allow_and_force_point_size {\n                                continue;\n                            }\n                        }\n\n                        let array_len = match module.types[ty].inner {\n                            crate::TypeInner::Array {\n                                size: crate::ArraySize::Constant(size),\n                                ..\n                            } => Some(size),\n                            _ => None,\n                        };\n                        let resolved = options.resolve_local_binding(binding, out_mode)?;\n                        write!(self.out, \"{}{} {}\", back::INDENT, ty_name, name)?;\n                        if let Some(array_len) = array_len {\n                            write!(self.out, \" [{array_len}]\")?;\n                        }\n                        resolved.try_fmt(&mut self.out)?;\n                        writeln!(self.out, \";\")?;\n                    }\n\n                    if pipeline_options.allow_and_force_point_size\n                        && ep.stage == crate::ShaderStage::Vertex\n                        && !has_point_size\n                    {\n                        // inject the point size output last\n                        writeln!(\n                            self.out,\n                            \"{}float _point_size [[point_size]];\",\n                            back::INDENT\n                        )?;\n                    }\n                    writeln!(self.out, \"}};\")?;\n                    &stage_out_name\n                }\n                None => \"void\",\n            };\n\n            // If we're doing a vertex pulling transform, define the buffer\n            // structure types.\n            if do_vertex_pulling {\n                for vbm in &vbm_resolved {\n                    let buffer_stride = vbm.stride;\n                    let buffer_ty = &vbm.ty_name;\n\n                    // Define a structure of bytes of the appropriate size.\n                    // When we access the attributes, we'll be unpacking these\n                    // bytes at some offset.\n                    writeln!(\n                        self.out,\n                        \"struct {buffer_ty} {{ metal::uchar data[{buffer_stride}]; }};\"\n                    )?;\n                }\n            }\n\n            // Write the entry point function's name, and begin its argument list.\n            writeln!(self.out, \"{em_str} {result_type_name} {fun_name}(\")?;\n\n            let mut is_first_argument = true;\n            let mut separator = || {\n                if is_first_argument {\n                    is_first_argument = false;\n                    ' '\n                } else {\n                    ','\n                }\n            };\n\n            // If we have produced a struct holding the `EntryPoint`'s\n            // `Function`'s arguments' varyings, pass that struct first.\n            if has_varyings {\n                writeln!(\n                    self.out,\n                    \"{} {stage_in_name} {varyings_member_name} [[stage_in]]\",\n                    separator()\n                )?;\n            }\n\n            let mut local_invocation_id = None;\n\n            // Then pass the remaining arguments not included in the varyings\n            // struct.\n            for &(ref name_key, ty, binding) in flattened_arguments.iter() {\n                let binding = match binding {\n                    Some(&crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { .. })) => continue,\n                    Some(binding @ &crate::Binding::BuiltIn { .. }) => binding,\n                    _ => continue,\n                };\n                let name = match *name_key {\n                    NameKey::StructMember(..) => &flattened_member_names[name_key],\n                    _ => &self.names[name_key],\n                };\n\n                if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {\n                    local_invocation_id = Some(name_key);\n                }\n\n                let ty_name = TypeContext {\n                    handle: ty,\n                    gctx: module.to_ctx(),\n                    names: &self.names,\n                    access: crate::StorageAccess::empty(),\n                    first_time: false,\n                };\n\n                match *binding {\n                    crate::Binding::BuiltIn(crate::BuiltIn::VertexIndex) => {\n                        v_existing_id = Some(name.clone());\n                    }\n                    crate::Binding::BuiltIn(crate::BuiltIn::InstanceIndex) => {\n                        i_existing_id = Some(name.clone());\n                    }\n                    _ => {}\n                };\n\n                let resolved = options.resolve_local_binding(binding, in_mode)?;\n                write!(self.out, \"{} {ty_name} {name}\", separator())?;\n                resolved.try_fmt(&mut self.out)?;\n                writeln!(self.out)?;\n            }\n\n            let need_workgroup_variables_initialization =\n                self.need_workgroup_variables_initialization(options, ep, module, fun_info);\n\n            if need_workgroup_variables_initialization && local_invocation_id.is_none() {\n                writeln!(\n                    self.out,\n                    \"{} {NAMESPACE}::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\", separator()\n                )?;\n            }\n\n            // Those global variables used by this entry point and its callees\n            // get passed as arguments. `Private` globals are an exception, they\n            // don't outlive this invocation, so we declare them below as locals\n            // within the entry point.\n            for (handle, var) in module.global_variables.iter() {\n                let usage = fun_info[handle];\n                if usage.is_empty() || var.space == crate::AddressSpace::Private {\n                    continue;\n                }\n\n                if options.lang_version < (1, 2) {\n                    match var.space {\n                        // This restriction is not documented in the MSL spec\n                        // but validation will fail if it is not upheld.\n                        //\n                        // We infer the required version from the \"Function\n                        // Buffer Read-Writes\" section of [what's new], where\n                        // the feature sets listed correspond with the ones\n                        // supporting MSL 1.2.\n                        //\n                        // [what's new]: https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/WhatsNewiniOS10tvOS10andOSX1012/WhatsNewiniOS10tvOS10andOSX1012.html\n                        crate::AddressSpace::Storage { access }\n                            if access.contains(crate::StorageAccess::STORE)\n                                && ep.stage == crate::ShaderStage::Fragment =>\n                        {\n                            return Err(Error::UnsupportedWriteableStorageBuffer)\n                        }\n                        crate::AddressSpace::Handle => {\n                            match module.types[var.ty].inner {\n                                crate::TypeInner::Image {\n                                    class: crate::ImageClass::Storage { access, .. },\n                                    ..\n                                } => {\n                                    // This restriction is not documented in the MSL spec\n                                    // but validation will fail if it is not upheld.\n                                    //\n                                    // We infer the required version from the \"Function\n                                    // Texture Read-Writes\" section of [what's new], where\n                                    // the feature sets listed correspond with the ones\n                                    // supporting MSL 1.2.\n                                    //\n                                    // [what's new]: https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/WhatsNewiniOS10tvOS10andOSX1012/WhatsNewiniOS10tvOS10andOSX1012.html\n                                    if access.contains(crate::StorageAccess::STORE)\n                                        && (ep.stage == crate::ShaderStage::Vertex\n                                            || ep.stage == crate::ShaderStage::Fragment)\n                                    {\n                                        return Err(Error::UnsupportedWriteableStorageTexture(\n                                            ep.stage,\n                                        ));\n                                    }\n\n                                    if access.contains(\n                                        crate::StorageAccess::LOAD | crate::StorageAccess::STORE,\n                                    ) {\n                                        return Err(Error::UnsupportedRWStorageTexture);\n                                    }\n                                }\n                                _ => {}\n                            }\n                        }\n                        _ => {}\n                    }\n                }\n\n                // Check min MSL version for binding arrays\n                match var.space {\n                    crate::AddressSpace::Handle => match module.types[var.ty].inner {\n                        crate::TypeInner::BindingArray { base, .. } => {\n                            match module.types[base].inner {\n                                crate::TypeInner::Sampler { .. } => {\n                                    if options.lang_version < (2, 0) {\n                                        return Err(Error::UnsupportedArrayOf(\n                                            \"samplers\".to_string(),\n                                        ));\n                                    }\n                                }\n                                crate::TypeInner::Image { class, .. } => match class {\n                                    crate::ImageClass::Sampled { .. }\n                                    | crate::ImageClass::Depth { .. }\n                                    | crate::ImageClass::Storage {\n                                        access: crate::StorageAccess::LOAD,\n                                        ..\n                                    } => {\n                                        // Array of textures since:\n                                        // - iOS: Metal 1.2 (check depends on https://github.com/gfx-rs/naga/issues/2164)\n                                        // - macOS: Metal 2\n\n                                        if options.lang_version < (2, 0) {\n                                            return Err(Error::UnsupportedArrayOf(\n                                                \"textures\".to_string(),\n                                            ));\n                                        }\n                                    }\n                                    crate::ImageClass::Storage {\n                                        access: crate::StorageAccess::STORE,\n                                        ..\n                                    } => {\n                                        // Array of write-only textures since:\n                                        // - iOS: Metal 2.2 (check depends on https://github.com/gfx-rs/naga/issues/2164)\n                                        // - macOS: Metal 2\n\n                                        if options.lang_version < (2, 0) {\n                                            return Err(Error::UnsupportedArrayOf(\n                                                \"write-only textures\".to_string(),\n                                            ));\n                                        }\n                                    }\n                                    crate::ImageClass::Storage { .. } => {\n                                        if options.lang_version < (3, 0) {\n                                            return Err(Error::UnsupportedArrayOf(\n                                                \"read-write textures\".to_string(),\n                                            ));\n                                        }\n                                    }\n                                    crate::ImageClass::External => {\n                                        return Err(Error::UnsupportedArrayOf(\n                                            \"external textures\".to_string(),\n                                        ));\n                                    }\n                                },\n                                _ => {\n                                    return Err(Error::UnsupportedArrayOfType(base));\n                                }\n                            }\n                        }\n                        _ => {}\n                    },\n                    _ => {}\n                }\n\n                // the resolves have already been checked for `!fake_missing_bindings` case\n                let resolved = match var.space {\n                    crate::AddressSpace::Immediate => options.resolve_immediates(ep).ok(),\n                    crate::AddressSpace::WorkGroup => None,\n                    _ => options\n                        .resolve_resource_binding(ep, var.binding.as_ref().unwrap())\n                        .ok(),\n                };\n                if let Some(ref resolved) = resolved {\n                    // Inline samplers are be defined in the EP body\n                    if resolved.as_inline_sampler(options).is_some() {\n                        continue;\n                    }\n                }\n\n                match module.types[var.ty].inner {\n                    crate::TypeInner::Image {\n                        class: crate::ImageClass::External,\n                        ..\n                    } => {\n                        // External texture global variables get lowered to 3 textures\n                        // and a constant buffer. We must emit a separate argument for\n                        // each of these.\n                        let target = match resolved {\n                            Some(back::msl::ResolvedBinding::Resource(target)) => {\n                                target.external_texture\n                            }\n                            _ => None,\n                        };\n\n                        for i in 0..3 {\n                            write!(self.out, \"{} \", separator())?;\n\n                            let plane_name = &self.names[&NameKey::ExternalTextureGlobalVariable(\n                                handle,\n                                ExternalTextureNameKey::Plane(i),\n                            )];\n                            write!(\n                              self.out,\n                              \"{NAMESPACE}::texture2d<float, {NAMESPACE}::access::sample> {plane_name}\"\n                            )?;\n                            if let Some(ref target) = target {\n                                write!(self.out, \" [[texture({})]]\", target.planes[i])?;\n                            }\n                            writeln!(self.out)?;\n                        }\n                        let params_ty_name = &self.names\n                            [&NameKey::Type(module.special_types.external_texture_params.unwrap())];\n                        let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable(\n                            handle,\n                            ExternalTextureNameKey::Params,\n                        )];\n                        write!(self.out, \"{} \", separator())?;\n                        write!(self.out, \"constant {params_ty_name}& {params_name}\")?;\n                        if let Some(ref target) = target {\n                            write!(self.out, \" [[buffer({})]]\", target.params)?;\n                        }\n                    }\n                    _ => {\n                        let tyvar = TypedGlobalVariable {\n                            module,\n                            names: &self.names,\n                            handle,\n                            usage,\n                            reference: true,\n                        };\n                        write!(self.out, \"{} \", separator())?;\n                        tyvar.try_fmt(&mut self.out)?;\n                        if let Some(resolved) = resolved {\n                            resolved.try_fmt(&mut self.out)?;\n                        }\n                        if let Some(value) = var.init {\n                            write!(self.out, \" = \")?;\n                            self.put_const_expression(\n                                value,\n                                module,\n                                mod_info,\n                                &module.global_expressions,\n                            )?;\n                        }\n                    }\n                }\n                writeln!(self.out)?;\n            }\n\n            if do_vertex_pulling {\n                if needs_vertex_id && v_existing_id.is_none() {\n                    // Write the [[vertex_id]] argument.\n                    writeln!(self.out, \"{} uint {v_id} [[vertex_id]]\", separator())?;\n                }\n\n                if needs_instance_id && i_existing_id.is_none() {\n                    writeln!(self.out, \"{} uint {i_id} [[instance_id]]\", separator())?;\n                }\n\n                // Iterate vbm_resolved, output one argument for every vertex buffer,\n                // using the names we generated earlier.\n                for vbm in &vbm_resolved {\n                    let id = &vbm.id;\n                    let ty_name = &vbm.ty_name;\n                    let param_name = &vbm.param_name;\n                    writeln!(\n                        self.out,\n                        \"{} const device {ty_name}* {param_name} [[buffer({id})]]\",\n                        separator()\n                    )?;\n                }\n            }\n\n            // If this entry uses any variable-length arrays, their sizes are\n            // passed as a final struct-typed argument.\n            if needs_buffer_sizes {\n                // this is checked earlier\n                let resolved = options.resolve_sizes_buffer(ep).unwrap();\n                write!(\n                    self.out,\n                    \"{} constant _mslBufferSizes& _buffer_sizes\",\n                    separator()\n                )?;\n                resolved.try_fmt(&mut self.out)?;\n                writeln!(self.out)?;\n            }\n\n            // end of the entry point argument list\n            writeln!(self.out, \") {{\")?;\n\n            // Starting the function body.\n            if do_vertex_pulling {\n                // Provide zero values for all the attributes, which we will overwrite with\n                // real data from the vertex attribute buffers, if the indices are in-bounds.\n                for vbm in &vbm_resolved {\n                    for attribute in vbm.attributes {\n                        let location = attribute.shader_location;\n                        let am_option = am_resolved.get(&location);\n                        if am_option.is_none() {\n                            // This bound attribute isn't used in this entry point, so\n                            // don't bother zero-initializing it.\n                            continue;\n                        }\n                        let am = am_option.unwrap();\n                        let attribute_ty_name = &am.ty_name;\n                        let attribute_name = &am.name;\n\n                        writeln!(\n                            self.out,\n                            \"{}{attribute_ty_name} {attribute_name} = {{}};\",\n                            back::Level(1)\n                        )?;\n                    }\n\n                    // Output a bounds check block that will set real values for the\n                    // attributes, if the bounds are satisfied.\n                    write!(self.out, \"{}if (\", back::Level(1))?;\n\n                    let idx = &vbm.id;\n                    let stride = &vbm.stride;\n                    let index_name = match vbm.step_mode {\n                        back::msl::VertexBufferStepMode::Constant => \"0\",\n                        back::msl::VertexBufferStepMode::ByVertex => {\n                            if let Some(ref name) = v_existing_id {\n                                name\n                            } else {\n                                &v_id\n                            }\n                        }\n                        back::msl::VertexBufferStepMode::ByInstance => {\n                            if let Some(ref name) = i_existing_id {\n                                name\n                            } else {\n                                &i_id\n                            }\n                        }\n                    };\n                    write!(\n                        self.out,\n                        \"{index_name} < (_buffer_sizes.buffer_size{idx} / {stride})\"\n                    )?;\n\n                    writeln!(self.out, \") {{\")?;\n\n                    // Pull the bytes out of the vertex buffer.\n                    let ty_name = &vbm.ty_name;\n                    let elem_name = &vbm.elem_name;\n                    let param_name = &vbm.param_name;\n\n                    writeln!(\n                        self.out,\n                        \"{}const {ty_name} {elem_name} = {param_name}[{index_name}];\",\n                        back::Level(2),\n                    )?;\n\n                    // Now set real values for each of the attributes, by unpacking the data\n                    // from the buffer elements.\n                    for attribute in vbm.attributes {\n                        let location = attribute.shader_location;\n                        let Some(am) = am_resolved.get(&location) else {\n                            // This bound attribute isn't used in this entry point, so\n                            // don't bother extracting the data. Too bad we emitted the\n                            // unpacking function earlier -- it might not get used.\n                            continue;\n                        };\n                        let attribute_name = &am.name;\n                        let attribute_ty_name = &am.ty_name;\n\n                        let offset = attribute.offset;\n                        let func = unpacking_functions\n                            .get(&attribute.format)\n                            .expect(\"Should have generated this unpacking function earlier.\");\n                        let func_name = &func.name;\n\n                        // Check dimensionality of the attribute compared to the unpacking\n                        // function. If attribute dimension > unpack dimension, we have to\n                        // pad out the unpack value from a vec4(0, 0, 0, 1) of matching\n                        // scalar type. Otherwise, if attribute dimension is < unpack\n                        // dimension, then we need to explicitly truncate the result.\n                        let needs_padding_or_truncation = am.dimension.cmp(&func.dimension);\n\n                        // We need an extra type conversion if the shader type does not\n                        // match the type returned from the unpacking function.\n                        let needs_conversion = am.scalar != func.scalar;\n\n                        if needs_padding_or_truncation != Ordering::Equal {\n                            // Emit a comment flagging that a conversion is happening,\n                            // since the actual logic can be at the end of a long line.\n                            writeln!(\n                                self.out,\n                                \"{}// {attribute_ty_name} <- {:?}\",\n                                back::Level(2),\n                                attribute.format\n                            )?;\n                        }\n\n                        write!(self.out, \"{}{attribute_name} = \", back::Level(2),)?;\n\n                        if needs_padding_or_truncation == Ordering::Greater {\n                            // Needs padding: emit constructor call for wider type\n                            write!(self.out, \"{attribute_ty_name}(\")?;\n                        }\n\n                        // Emit call to unpacking function\n                        if needs_conversion {\n                            put_numeric_type(&mut self.out, am.scalar, func.dimension.as_slice())?;\n                            write!(self.out, \"(\")?;\n                        }\n                        write!(self.out, \"{func_name}({elem_name}.data[{offset}]\")?;\n                        for i in (offset + 1)..(offset + func.byte_count) {\n                            write!(self.out, \", {elem_name}.data[{i}]\")?;\n                        }\n                        write!(self.out, \")\")?;\n                        if needs_conversion {\n                            write!(self.out, \")\")?;\n                        }\n\n                        match needs_padding_or_truncation {\n                            Ordering::Greater => {\n                                // Padding\n                                let ty_is_int = scalar_is_int(am.scalar);\n                                let zero_value = if ty_is_int { \"0\" } else { \"0.0\" };\n                                let one_value = if ty_is_int { \"1\" } else { \"1.0\" };\n                                for i in func.dimension.map_or(1, u8::from)\n                                    ..am.dimension.map_or(1, u8::from)\n                                {\n                                    write!(\n                                        self.out,\n                                        \", {}\",\n                                        if i == 3 { one_value } else { zero_value }\n                                    )?;\n                                }\n                            }\n                            Ordering::Less => {\n                                // Truncate to the first `am.dimension` components\n                                write!(\n                                    self.out,\n                                    \".{}\",\n                                    &\"xyzw\"[0..usize::from(am.dimension.map_or(1, u8::from))]\n                                )?;\n                            }\n                            Ordering::Equal => {}\n                        }\n\n                        if needs_padding_or_truncation == Ordering::Greater {\n                            write!(self.out, \")\")?;\n                        }\n\n                        writeln!(self.out, \";\")?;\n                    }\n\n                    // End the bounds check / attribute setting block.\n                    writeln!(self.out, \"{}}}\", back::Level(1))?;\n                }\n            }\n\n            if need_workgroup_variables_initialization {\n                self.write_workgroup_variables_initialization(\n                    module,\n                    mod_info,\n                    fun_info,\n                    local_invocation_id,\n                )?;\n            }\n\n            // Metal doesn't support private mutable variables outside of functions,\n            // so we put them here, just like the locals.\n            for (handle, var) in module.global_variables.iter() {\n                let usage = fun_info[handle];\n                if usage.is_empty() {\n                    continue;\n                }\n                if var.space == crate::AddressSpace::Private {\n                    let tyvar = TypedGlobalVariable {\n                        module,\n                        names: &self.names,\n                        handle,\n                        usage,\n\n                        reference: false,\n                    };\n                    write!(self.out, \"{}\", back::INDENT)?;\n                    tyvar.try_fmt(&mut self.out)?;\n                    match var.init {\n                        Some(value) => {\n                            write!(self.out, \" = \")?;\n                            self.put_const_expression(\n                                value,\n                                module,\n                                mod_info,\n                                &module.global_expressions,\n                            )?;\n                            writeln!(self.out, \";\")?;\n                        }\n                        None => {\n                            writeln!(self.out, \" = {{}};\")?;\n                        }\n                    };\n                } else if let Some(ref binding) = var.binding {\n                    let resolved = options.resolve_resource_binding(ep, binding).unwrap();\n                    if let Some(sampler) = resolved.as_inline_sampler(options) {\n                        // write an inline sampler\n                        let name = &self.names[&NameKey::GlobalVariable(handle)];\n                        writeln!(\n                            self.out,\n                            \"{}constexpr {}::sampler {}(\",\n                            back::INDENT,\n                            NAMESPACE,\n                            name\n                        )?;\n                        self.put_inline_sampler_properties(back::Level(2), sampler)?;\n                        writeln!(self.out, \"{});\", back::INDENT)?;\n                    } else if let crate::TypeInner::Image {\n                        class: crate::ImageClass::External,\n                        ..\n                    } = module.types[var.ty].inner\n                    {\n                        // Wrap the individual arguments for each external texture global\n                        // in a struct which can be easily passed around.\n                        let wrapper_name = &self.names[&NameKey::GlobalVariable(handle)];\n                        let l1 = back::Level(1);\n                        let l2 = l1.next();\n                        writeln!(\n                            self.out,\n                            \"{l1}const {EXTERNAL_TEXTURE_WRAPPER_STRUCT} {wrapper_name} {{\"\n                        )?;\n                        for i in 0..3 {\n                            let plane_name = &self.names[&NameKey::ExternalTextureGlobalVariable(\n                                handle,\n                                ExternalTextureNameKey::Plane(i),\n                            )];\n                            writeln!(self.out, \"{l2}.plane{i} = {plane_name},\")?;\n                        }\n                        let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable(\n                            handle,\n                            ExternalTextureNameKey::Params,\n                        )];\n                        writeln!(self.out, \"{l2}.params = {params_name},\")?;\n                        writeln!(self.out, \"{l1}}};\")?;\n                    }\n                }\n            }\n\n            // Now take the arguments that we gathered into structs, and the\n            // structs that we flattened into arguments, and emit local\n            // variables with initializers that put everything back the way the\n            // body code expects.\n            //\n            // If we had to generate fresh names for struct members passed as\n            // arguments, be sure to use those names when rebuilding the struct.\n            //\n            // \"Each day, I change some zeros to ones, and some ones to zeros.\n            // The rest, I leave alone.\"\n            for (arg_index, arg) in fun.arguments.iter().enumerate() {\n                let arg_name =\n                    &self.names[&NameKey::EntryPointArgument(ep_index as _, arg_index as u32)];\n                match module.types[arg.ty].inner {\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        let struct_name = &self.names[&NameKey::Type(arg.ty)];\n                        write!(\n                            self.out,\n                            \"{}const {} {} = {{ \",\n                            back::INDENT,\n                            struct_name,\n                            arg_name\n                        )?;\n                        for (member_index, member) in members.iter().enumerate() {\n                            let key = NameKey::StructMember(arg.ty, member_index as u32);\n                            let name = &flattened_member_names[&key];\n                            if member_index != 0 {\n                                write!(self.out, \", \")?;\n                            }\n                            // insert padding initialization, if needed\n                            if self\n                                .struct_member_pads\n                                .contains(&(arg.ty, member_index as u32))\n                            {\n                                write!(self.out, \"{{}}, \")?;\n                            }\n                            if let Some(crate::Binding::Location { .. }) = member.binding {\n                                if has_varyings {\n                                    write!(self.out, \"{varyings_member_name}.\")?;\n                                }\n                            }\n                            write!(self.out, \"{name}\")?;\n                        }\n                        writeln!(self.out, \" }};\")?;\n                    }\n                    _ => match arg.binding {\n                        Some(crate::Binding::Location { .. })\n                        | Some(crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { .. })) => {\n                            if has_varyings {\n                                writeln!(\n                                    self.out,\n                                    \"{}const auto {} = {}.{};\",\n                                    back::INDENT,\n                                    arg_name,\n                                    varyings_member_name,\n                                    arg_name\n                                )?;\n                            }\n                        }\n                        _ => {}\n                    },\n                }\n            }\n\n            let guarded_indices =\n                index::find_checked_indexes(module, fun, fun_info, options.bounds_check_policies);\n\n            let context = StatementContext {\n                expression: ExpressionContext {\n                    function: fun,\n                    origin: FunctionOrigin::EntryPoint(ep_index as _),\n                    info: fun_info,\n                    lang_version: options.lang_version,\n                    policies: options.bounds_check_policies,\n                    guarded_indices,\n                    module,\n                    mod_info,\n                    pipeline_options,\n                    force_loop_bounding: options.force_loop_bounding,\n                },\n                result_struct: Some(&stage_out_name),\n            };\n\n            // Finally, declare all the local variables that we need\n            //TODO: we can postpone this till the relevant expressions are emitted\n            self.put_locals(&context.expression)?;\n            self.update_expressions_to_bake(fun, fun_info, &context.expression);\n            self.put_block(back::Level(1), &fun.body, &context)?;\n            writeln!(self.out, \"}}\")?;\n            if ep_index + 1 != module.entry_points.len() {\n                writeln!(self.out)?;\n            }\n            self.named_expressions.clear();\n        }\n\n        Ok(info)\n    }\n\n    fn write_barrier(&mut self, flags: crate::Barrier, level: back::Level) -> BackendResult {\n        // Note: OR-ring bitflags requires `__HAVE_MEMFLAG_OPERATORS__`,\n        // so we try to avoid it here.\n        if flags.is_empty() {\n            writeln!(\n                self.out,\n                \"{level}{NAMESPACE}::threadgroup_barrier({NAMESPACE}::mem_flags::mem_none);\",\n            )?;\n        }\n        if flags.contains(crate::Barrier::STORAGE) {\n            writeln!(\n                self.out,\n                \"{level}{NAMESPACE}::threadgroup_barrier({NAMESPACE}::mem_flags::mem_device);\",\n            )?;\n        }\n        if flags.contains(crate::Barrier::WORK_GROUP) {\n            writeln!(\n                self.out,\n                \"{level}{NAMESPACE}::threadgroup_barrier({NAMESPACE}::mem_flags::mem_threadgroup);\",\n            )?;\n        }\n        if flags.contains(crate::Barrier::SUB_GROUP) {\n            writeln!(\n                self.out,\n                \"{level}{NAMESPACE}::simdgroup_barrier({NAMESPACE}::mem_flags::mem_threadgroup);\",\n            )?;\n        }\n        if flags.contains(crate::Barrier::TEXTURE) {\n            writeln!(\n                self.out,\n                \"{level}{NAMESPACE}::threadgroup_barrier({NAMESPACE}::mem_flags::mem_texture);\",\n            )?;\n        }\n        Ok(())\n    }\n}\n\n/// Initializing workgroup variables is more tricky for Metal because we have to deal\n/// with atomics at the type-level (which don't have a copy constructor).\nmod workgroup_mem_init {\n    use crate::EntryPoint;\n\n    use super::*;\n\n    enum Access {\n        GlobalVariable(Handle<crate::GlobalVariable>),\n        StructMember(Handle<crate::Type>, u32),\n        Array(usize),\n    }\n\n    impl Access {\n        fn write<W: Write>(\n            &self,\n            writer: &mut W,\n            names: &FastHashMap<NameKey, String>,\n        ) -> Result<(), core::fmt::Error> {\n            match *self {\n                Access::GlobalVariable(handle) => {\n                    write!(writer, \"{}\", &names[&NameKey::GlobalVariable(handle)])\n                }\n                Access::StructMember(handle, index) => {\n                    write!(writer, \".{}\", &names[&NameKey::StructMember(handle, index)])\n                }\n                Access::Array(depth) => write!(writer, \".{WRAPPED_ARRAY_FIELD}[__i{depth}]\"),\n            }\n        }\n    }\n\n    struct AccessStack {\n        stack: Vec<Access>,\n        array_depth: usize,\n    }\n\n    impl AccessStack {\n        const fn new() -> Self {\n            Self {\n                stack: Vec::new(),\n                array_depth: 0,\n            }\n        }\n\n        fn enter_array<R>(&mut self, cb: impl FnOnce(&mut Self, usize) -> R) -> R {\n            let array_depth = self.array_depth;\n            self.stack.push(Access::Array(array_depth));\n            self.array_depth += 1;\n            let res = cb(self, array_depth);\n            self.stack.pop();\n            self.array_depth -= 1;\n            res\n        }\n\n        fn enter<R>(&mut self, new: Access, cb: impl FnOnce(&mut Self) -> R) -> R {\n            self.stack.push(new);\n            let res = cb(self);\n            self.stack.pop();\n            res\n        }\n\n        fn write<W: Write>(\n            &self,\n            writer: &mut W,\n            names: &FastHashMap<NameKey, String>,\n        ) -> Result<(), core::fmt::Error> {\n            for next in self.stack.iter() {\n                next.write(writer, names)?;\n            }\n            Ok(())\n        }\n    }\n\n    impl<W: Write> Writer<W> {\n        pub(super) fn need_workgroup_variables_initialization(\n            &mut self,\n            options: &Options,\n            ep: &EntryPoint,\n            module: &crate::Module,\n            fun_info: &valid::FunctionInfo,\n        ) -> bool {\n            options.zero_initialize_workgroup_memory\n                && ep.stage.compute_like()\n                && module.global_variables.iter().any(|(handle, var)| {\n                    !fun_info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup\n                })\n        }\n\n        pub(super) fn write_workgroup_variables_initialization(\n            &mut self,\n            module: &crate::Module,\n            module_info: &valid::ModuleInfo,\n            fun_info: &valid::FunctionInfo,\n            local_invocation_id: Option<&NameKey>,\n        ) -> BackendResult {\n            let level = back::Level(1);\n\n            writeln!(\n                self.out,\n                \"{}if ({}::all({} == {}::uint3(0u))) {{\",\n                level,\n                NAMESPACE,\n                local_invocation_id\n                    .map(|name_key| self.names[name_key].as_str())\n                    .unwrap_or(\"__local_invocation_id\"),\n                NAMESPACE,\n            )?;\n\n            let mut access_stack = AccessStack::new();\n\n            let vars = module.global_variables.iter().filter(|&(handle, var)| {\n                !fun_info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup\n            });\n\n            for (handle, var) in vars {\n                access_stack.enter(Access::GlobalVariable(handle), |access_stack| {\n                    self.write_workgroup_variable_initialization(\n                        module,\n                        module_info,\n                        var.ty,\n                        access_stack,\n                        level.next(),\n                    )\n                })?;\n            }\n\n            writeln!(self.out, \"{level}}}\")?;\n            self.write_barrier(crate::Barrier::WORK_GROUP, level)\n        }\n\n        fn write_workgroup_variable_initialization(\n            &mut self,\n            module: &crate::Module,\n            module_info: &valid::ModuleInfo,\n            ty: Handle<crate::Type>,\n            access_stack: &mut AccessStack,\n            level: back::Level,\n        ) -> BackendResult {\n            if module_info[ty].contains(valid::TypeFlags::CONSTRUCTIBLE) {\n                write!(self.out, \"{level}\")?;\n                access_stack.write(&mut self.out, &self.names)?;\n                writeln!(self.out, \" = {{}};\")?;\n            } else {\n                match module.types[ty].inner {\n                    crate::TypeInner::Atomic { .. } => {\n                        write!(\n                            self.out,\n                            \"{level}{NAMESPACE}::atomic_store_explicit({ATOMIC_REFERENCE}\"\n                        )?;\n                        access_stack.write(&mut self.out, &self.names)?;\n                        writeln!(self.out, \", 0, {NAMESPACE}::memory_order_relaxed);\")?;\n                    }\n                    crate::TypeInner::Array { base, size, .. } => {\n                        let count = match size.resolve(module.to_ctx())? {\n                            proc::IndexableLength::Known(count) => count,\n                            proc::IndexableLength::Dynamic => unreachable!(),\n                        };\n\n                        access_stack.enter_array(|access_stack, array_depth| {\n                            writeln!(\n                                self.out,\n                                \"{level}for (int __i{array_depth} = 0; __i{array_depth} < {count}; __i{array_depth}++) {{\"\n                            )?;\n                            self.write_workgroup_variable_initialization(\n                                module,\n                                module_info,\n                                base,\n                                access_stack,\n                                level.next(),\n                            )?;\n                            writeln!(self.out, \"{level}}}\")?;\n                            BackendResult::Ok(())\n                        })?;\n                    }\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        for (index, member) in members.iter().enumerate() {\n                            access_stack.enter(\n                                Access::StructMember(ty, index as u32),\n                                |access_stack| {\n                                    self.write_workgroup_variable_initialization(\n                                        module,\n                                        module_info,\n                                        member.ty,\n                                        access_stack,\n                                        level,\n                                    )\n                                },\n                            )?;\n                        }\n                    }\n                    _ => unreachable!(),\n                }\n            }\n\n            Ok(())\n        }\n    }\n}\n\nimpl crate::AtomicFunction {\n    const fn to_msl(self) -> &'static str {\n        match self {\n            Self::Add => \"fetch_add\",\n            Self::Subtract => \"fetch_sub\",\n            Self::And => \"fetch_and\",\n            Self::InclusiveOr => \"fetch_or\",\n            Self::ExclusiveOr => \"fetch_xor\",\n            Self::Min => \"fetch_min\",\n            Self::Max => \"fetch_max\",\n            Self::Exchange { compare: None } => \"exchange\",\n            Self::Exchange { compare: Some(_) } => ATOMIC_COMP_EXCH_FUNCTION,\n        }\n    }\n\n    fn to_msl_64_bit(self) -> Result<&'static str, Error> {\n        Ok(match self {\n            Self::Min => \"min\",\n            Self::Max => \"max\",\n            _ => Err(Error::FeatureNotImplemented(\n                \"64-bit atomic operation other than min/max\".to_string(),\n            ))?,\n        })\n    }\n}\n"
  },
  {
    "path": "naga/src/back/pipeline_constants.rs",
    "content": "use alloc::{\n    borrow::Cow,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::mem;\n\nuse hashbrown::HashSet;\nuse thiserror::Error;\n\nuse super::PipelineConstants;\nuse crate::{\n    arena::HandleVec,\n    compact::{compact, KeepUnused},\n    ir,\n    proc::{ConstantEvaluator, ConstantEvaluatorError, Emitter},\n    valid::{Capabilities, ModuleInfo, ValidationError, ValidationFlags, Validator},\n    Arena, Block, Constant, Expression, Function, Handle, Literal, Module, Override, Range, Scalar,\n    Span, Statement, TypeInner, WithSpan,\n};\n\n// Possibly unused if not compiled with no_std\n#[allow(unused_imports)]\nuse num_traits::float::FloatCore as _;\n\n#[derive(Error, Debug, Clone)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum PipelineConstantError {\n    #[error(\"Missing value for pipeline-overridable constant with identifier string: '{0}'\")]\n    MissingValue(String),\n    #[error(\"pipeline-overridable constant '{0}' not found in the shader\")]\n    NotFound(String),\n    #[error(\n        \"Source f64 value needs to be finite ({}) for number destinations\",\n        \"NaNs and Inifinites are not allowed\"\n    )]\n    SrcNeedsToBeFinite,\n    #[error(\"Source f64 value doesn't fit in destination\")]\n    DstRangeTooSmall,\n    #[error(transparent)]\n    ConstantEvaluatorError(#[from] ConstantEvaluatorError),\n    #[error(transparent)]\n    ValidationError(#[from] WithSpan<ValidationError>),\n    #[error(\"workgroup_size override isn't strictly positive\")]\n    NegativeWorkgroupSize,\n    #[error(\"max vertices or max primitives is negative\")]\n    NegativeMeshOutputMax,\n}\n\n/// Compact `module` and replace all overrides with constants.\n///\n/// `module` must be valid. Both compaction and constant evaluation may produce\n/// invalid results (e.g. replace an invalid expression with a constant) for\n/// invalid modules.\n///\n/// If no changes are needed, this just returns `Cow::Borrowed` references to\n/// `module` and `module_info`. Otherwise, it clones `module`, retains only the\n/// selected entry point, compacts the module, edits its [`global_expressions`]\n/// arena to contain only fully-evaluated expressions, and returns the\n/// simplified module and its validation results.\n///\n/// The module returned has an empty `overrides` arena, and the\n/// `global_expressions` arena contains only fully-evaluated expressions.\n///\n/// [`global_expressions`]: Module::global_expressions\npub fn process_overrides<'a>(\n    module: &'a Module,\n    module_info: &'a ModuleInfo,\n    entry_point: Option<(ir::ShaderStage, &str)>,\n    pipeline_constants: &PipelineConstants,\n) -> Result<(Cow<'a, Module>, Cow<'a, ModuleInfo>), PipelineConstantError> {\n    let mut handles = module\n        .overrides\n        .iter()\n        .map(|(handle, _)| handle)\n        .collect::<Vec<_>>();\n    for c in pipeline_constants.keys() {\n        let c_id = c.parse().ok();\n        if let Some((i, _)) = handles.iter().enumerate().find(|&(_, handle)| {\n            let o = &module.overrides[*handle];\n            if o.id.is_some() {\n                o.id == c_id\n            } else {\n                o.name.as_deref() == Some(c.as_str())\n            }\n        }) {\n            handles.swap_remove(i);\n        } else {\n            return Err(PipelineConstantError::NotFound(c.clone()));\n        }\n    }\n\n    if (entry_point.is_none() || module.entry_points.len() <= 1) && module.overrides.is_empty() {\n        // We skip compacting the module here mostly to reduce the risk of\n        // hitting corner cases like https://github.com/gfx-rs/wgpu/issues/7793.\n        // Compaction doesn't cost very much [1], so it would also be reasonable\n        // to do it unconditionally. Even when there is a single entry point or\n        // when no entry point is specified, it is still possible that there\n        // are unreferenced items in the module that would be removed by this\n        // compaction.\n        //\n        // [1]: https://github.com/gfx-rs/wgpu/pull/7703#issuecomment-2902153760\n        return Ok((Cow::Borrowed(module), Cow::Borrowed(module_info)));\n    }\n\n    let mut module = module.clone();\n    if let Some((ep_stage, ep_name)) = entry_point {\n        module\n            .entry_points\n            .retain(|ep| ep.stage == ep_stage && ep.name == ep_name);\n    }\n\n    // Compact the module to remove anything not reachable from an entry point.\n    // This is necessary because we may not have values for overrides that are\n    // not reachable from the/an entry point.\n    compact(&mut module, KeepUnused::No);\n\n    // If there are no overrides in the module, then we can skip the rest.\n    if module.overrides.is_empty() {\n        return revalidate(module);\n    }\n\n    // A map from override handles to the handles of the constants\n    // we've replaced them with.\n    let mut override_map = HandleVec::with_capacity(module.overrides.len());\n\n    // A map from `module`'s original global expression handles to\n    // handles in the new, simplified global expression arena.\n    let mut adjusted_global_expressions = HandleVec::with_capacity(module.global_expressions.len());\n\n    // The set of constants whose initializer handles we've already\n    // updated to refer to the newly built global expression arena.\n    //\n    // All constants in `module` must have their `init` handles\n    // updated to point into the new, simplified global expression\n    // arena. Some of these we can most easily handle as a side effect\n    // during the simplification process, but we must handle the rest\n    // in a final fixup pass, guided by `adjusted_global_expressions`. We\n    // add their handles to this set, so that the final fixup step can\n    // leave them alone.\n    let mut adjusted_constant_initializers = HashSet::with_capacity(module.constants.len());\n\n    let mut global_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();\n    let mut layouter = crate::proc::Layouter::default();\n\n    // An iterator through the original overrides table, consumed in\n    // approximate tandem with the global expressions.\n    let mut overrides = mem::take(&mut module.overrides);\n    let mut override_iter = overrides.iter_mut_span();\n\n    // Do two things in tandem:\n    //\n    // - Rebuild the global expression arena from scratch, fully\n    //   evaluating all expressions, and replacing each `Override`\n    //   expression in `module.global_expressions` with a `Constant`\n    //   expression.\n    //\n    // - Build a new `Constant` in `module.constants` to take the\n    //   place of each `Override`.\n    //\n    // Build a map from old global expression handles to their\n    // fully-evaluated counterparts in `adjusted_global_expressions` as we\n    // go.\n    //\n    // Why in tandem? Overrides refer to expressions, and expressions\n    // refer to overrides, so we can't disentangle the two into\n    // separate phases. However, we can take advantage of the fact\n    // that the overrides and expressions must form a DAG, and work\n    // our way from the leaves to the roots, replacing and evaluating\n    // as we go.\n    //\n    // Although the two loops are nested, this is really two\n    // alternating phases: we adjust and evaluate constant expressions\n    // until we hit an `Override` expression, at which point we switch\n    // to building `Constant`s for `Overrides` until we've handled the\n    // one used by the expression. Then we switch back to processing\n    // expressions. Because we know they form a DAG, we know the\n    // `Override` expressions we encounter can only have initializers\n    // referring to global expressions we've already simplified.\n    for (old_h, expr, span) in module.global_expressions.drain() {\n        let mut expr = match expr {\n            Expression::Override(h) => {\n                let c_h = if let Some(new_h) = override_map.get(h) {\n                    *new_h\n                } else {\n                    let mut new_h = None;\n                    for entry in override_iter.by_ref() {\n                        let stop = entry.0 == h;\n                        new_h = Some(process_override(\n                            entry,\n                            pipeline_constants,\n                            &mut module,\n                            &mut override_map,\n                            &adjusted_global_expressions,\n                            &mut adjusted_constant_initializers,\n                            &mut global_expression_kind_tracker,\n                        )?);\n                        if stop {\n                            break;\n                        }\n                    }\n                    new_h.unwrap()\n                };\n                Expression::Constant(c_h)\n            }\n            Expression::Constant(c_h) => {\n                if adjusted_constant_initializers.insert(c_h) {\n                    let init = &mut module.constants[c_h].init;\n                    *init = adjusted_global_expressions[*init];\n                }\n                expr\n            }\n            expr => expr,\n        };\n        let mut evaluator = ConstantEvaluator::for_wgsl_module(\n            &mut module,\n            &mut global_expression_kind_tracker,\n            &mut layouter,\n            false,\n        );\n        adjust_expr(&adjusted_global_expressions, &mut expr);\n        let h = evaluator.try_eval_and_append(expr, span)?;\n        adjusted_global_expressions.insert(old_h, h);\n    }\n\n    // Finish processing any overrides we didn't visit in the loop above.\n    for entry in override_iter {\n        match *entry.1 {\n            Override { name: Some(_), .. } | Override { id: Some(_), .. } => {\n                process_override(\n                    entry,\n                    pipeline_constants,\n                    &mut module,\n                    &mut override_map,\n                    &adjusted_global_expressions,\n                    &mut adjusted_constant_initializers,\n                    &mut global_expression_kind_tracker,\n                )?;\n            }\n            Override {\n                init: Some(ref mut init),\n                ..\n            } => {\n                *init = adjusted_global_expressions[*init];\n            }\n            _ => {}\n        }\n    }\n\n    // Update the initialization expression handles of all `Constant`s\n    // and `GlobalVariable`s. Skip `Constant`s we'd already updated en\n    // passant.\n    for (_, c) in module\n        .constants\n        .iter_mut()\n        .filter(|&(c_h, _)| !adjusted_constant_initializers.contains(&c_h))\n    {\n        c.init = adjusted_global_expressions[c.init];\n    }\n\n    for (_, v) in module.global_variables.iter_mut() {\n        if let Some(ref mut init) = v.init {\n            *init = adjusted_global_expressions[*init];\n        }\n    }\n\n    let mut functions = mem::take(&mut module.functions);\n    for (_, function) in functions.iter_mut() {\n        process_function(&mut module, &override_map, &mut layouter, function)?;\n    }\n    module.functions = functions;\n\n    let mut entry_points = mem::take(&mut module.entry_points);\n    for ep in entry_points.iter_mut() {\n        process_function(&mut module, &override_map, &mut layouter, &mut ep.function)?;\n        process_workgroup_size_override(&mut module, &adjusted_global_expressions, ep)?;\n        process_mesh_shader_overrides(&mut module, &adjusted_global_expressions, ep)?;\n    }\n    module.entry_points = entry_points;\n    module.overrides = overrides;\n\n    // Now that we've rewritten all the expressions, we need to\n    // recompute their types and other metadata. For the time being,\n    // do a full re-validation.\n    revalidate(module)\n}\n\nfn revalidate(\n    module: Module,\n) -> Result<(Cow<'static, Module>, Cow<'static, ModuleInfo>), PipelineConstantError> {\n    let mut validator = Validator::new(ValidationFlags::all(), Capabilities::all());\n    let module_info = validator.validate_resolved_overrides(&module)?;\n    Ok((Cow::Owned(module), Cow::Owned(module_info)))\n}\n\nfn process_workgroup_size_override(\n    module: &mut Module,\n    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,\n    ep: &mut crate::EntryPoint,\n) -> Result<(), PipelineConstantError> {\n    match ep.workgroup_size_overrides {\n        None => {}\n        Some(overrides) => {\n            overrides.iter().enumerate().try_for_each(\n                |(i, overridden)| -> Result<(), PipelineConstantError> {\n                    match *overridden {\n                        None => Ok(()),\n                        Some(h) => {\n                            ep.workgroup_size[i] = module\n                                .to_ctx()\n                                .get_const_val(adjusted_global_expressions[h])\n                                .map(|n| {\n                                    if n == 0 {\n                                        Err(PipelineConstantError::NegativeWorkgroupSize)\n                                    } else {\n                                        Ok(n)\n                                    }\n                                })\n                                .map_err(|_| PipelineConstantError::NegativeWorkgroupSize)??;\n                            Ok(())\n                        }\n                    }\n                },\n            )?;\n            ep.workgroup_size_overrides = None;\n        }\n    }\n    Ok(())\n}\n\nfn process_mesh_shader_overrides(\n    module: &mut Module,\n    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,\n    ep: &mut crate::EntryPoint,\n) -> Result<(), PipelineConstantError> {\n    if let Some(ref mut mesh_info) = ep.mesh_info {\n        if let Some(r#override) = mesh_info.max_vertices_override {\n            mesh_info.max_vertices = module\n                .to_ctx()\n                .get_const_val(adjusted_global_expressions[r#override])\n                .map_err(|_| PipelineConstantError::NegativeMeshOutputMax)?;\n        }\n        if let Some(r#override) = mesh_info.max_primitives_override {\n            mesh_info.max_primitives = module\n                .to_ctx()\n                .get_const_val(adjusted_global_expressions[r#override])\n                .map_err(|_| PipelineConstantError::NegativeMeshOutputMax)?;\n        }\n    }\n    Ok(())\n}\n\n/// Add a [`Constant`] to `module` for the override `old_h`.\n///\n/// Add the new `Constant` to `override_map` and `adjusted_constant_initializers`.\nfn process_override(\n    (old_h, r#override, span): (Handle<Override>, &mut Override, &Span),\n    pipeline_constants: &PipelineConstants,\n    module: &mut Module,\n    override_map: &mut HandleVec<Override, Handle<Constant>>,\n    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,\n    adjusted_constant_initializers: &mut HashSet<Handle<Constant>>,\n    global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker,\n) -> Result<Handle<Constant>, PipelineConstantError> {\n    // Determine which key to use for `r#override` in `pipeline_constants`.\n    let key = if let Some(id) = r#override.id {\n        Cow::Owned(id.to_string())\n    } else if let Some(ref name) = r#override.name {\n        Cow::Borrowed(name)\n    } else {\n        unreachable!();\n    };\n\n    // Generate a global expression for `r#override`'s value, either\n    // from the provided `pipeline_constants` table or its initializer\n    // in the module.\n    let init = if let Some(value) = pipeline_constants.get::<str>(&key) {\n        let literal = match module.types[r#override.ty].inner {\n            TypeInner::Scalar(scalar) => map_value_to_literal(*value, scalar)?,\n            _ => unreachable!(),\n        };\n        let expr = module\n            .global_expressions\n            .append(Expression::Literal(literal), Span::UNDEFINED);\n        global_expression_kind_tracker.insert(expr, crate::proc::ExpressionKind::Const);\n        expr\n    } else if let Some(init) = r#override.init {\n        adjusted_global_expressions[init]\n    } else {\n        return Err(PipelineConstantError::MissingValue(key.to_string()));\n    };\n\n    // Generate a new `Constant` to represent the override's value.\n    let constant = Constant {\n        name: r#override.name.clone(),\n        ty: r#override.ty,\n        init,\n    };\n    let h = module.constants.append(constant, *span);\n    override_map.insert(old_h, h);\n    adjusted_constant_initializers.insert(h);\n    r#override.init = Some(init);\n    Ok(h)\n}\n\n/// Replace all override expressions in `function` with fully-evaluated constants.\n///\n/// Replace all `Expression::Override`s in `function`'s expression arena with\n/// the corresponding `Expression::Constant`s, as given in `override_map`.\n/// Replace any expressions whose values are now known with their fully\n/// evaluated form.\n///\n/// If `h` is a `Handle<Override>`, then `override_map[h]` is the\n/// `Handle<Constant>` for the override's final value.\nfn process_function(\n    module: &mut Module,\n    override_map: &HandleVec<Override, Handle<Constant>>,\n    layouter: &mut crate::proc::Layouter,\n    function: &mut Function,\n) -> Result<(), ConstantEvaluatorError> {\n    // A map from original local expression handles to\n    // handles in the new, local expression arena.\n    let mut adjusted_local_expressions = HandleVec::with_capacity(function.expressions.len());\n\n    let mut local_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();\n\n    let mut expressions = mem::take(&mut function.expressions);\n\n    // Dummy `emitter` and `block` for the constant evaluator.\n    // We can ignore the concept of emitting expressions here since\n    // expressions have already been covered by a `Statement::Emit`\n    // in the frontend.\n    // The only thing we might have to do is remove some expressions\n    // that have been covered by a `Statement::Emit`. See the docs of\n    // `filter_emits_in_block` for the reasoning.\n    let mut emitter = Emitter::default();\n    let mut block = Block::new();\n\n    let mut evaluator = ConstantEvaluator::for_wgsl_function(\n        module,\n        &mut function.expressions,\n        &mut local_expression_kind_tracker,\n        layouter,\n        &mut emitter,\n        &mut block,\n        false,\n    );\n\n    for (old_h, mut expr, span) in expressions.drain() {\n        if let Expression::Override(h) = expr {\n            expr = Expression::Constant(override_map[h]);\n        }\n        adjust_expr(&adjusted_local_expressions, &mut expr);\n        let h = evaluator.try_eval_and_append(expr, span)?;\n        adjusted_local_expressions.insert(old_h, h);\n    }\n\n    adjust_block(&adjusted_local_expressions, &mut function.body);\n\n    filter_emits_in_block(&mut function.body, &function.expressions);\n\n    // Update local expression initializers.\n    for (_, local) in function.local_variables.iter_mut() {\n        if let &mut Some(ref mut init) = &mut local.init {\n            *init = adjusted_local_expressions[*init];\n        }\n    }\n\n    // We've changed the keys of `function.named_expression`, so we have to\n    // rebuild it from scratch.\n    let named_expressions = mem::take(&mut function.named_expressions);\n    for (expr_h, name) in named_expressions {\n        function\n            .named_expressions\n            .insert(adjusted_local_expressions[expr_h], name);\n    }\n\n    Ok(())\n}\n\n/// Replace every expression handle in `expr` with its counterpart\n/// given by `new_pos`.\nfn adjust_expr(new_pos: &HandleVec<Expression, Handle<Expression>>, expr: &mut Expression) {\n    let adjust = |expr: &mut Handle<Expression>| {\n        *expr = new_pos[*expr];\n    };\n    match *expr {\n        Expression::Compose {\n            ref mut components,\n            ty: _,\n        } => {\n            for c in components.iter_mut() {\n                adjust(c);\n            }\n        }\n        Expression::Access {\n            ref mut base,\n            ref mut index,\n        } => {\n            adjust(base);\n            adjust(index);\n        }\n        Expression::AccessIndex {\n            ref mut base,\n            index: _,\n        } => {\n            adjust(base);\n        }\n        Expression::Splat {\n            ref mut value,\n            size: _,\n        } => {\n            adjust(value);\n        }\n        Expression::Swizzle {\n            ref mut vector,\n            size: _,\n            pattern: _,\n        } => {\n            adjust(vector);\n        }\n        Expression::Load { ref mut pointer } => {\n            adjust(pointer);\n        }\n        Expression::ImageSample {\n            ref mut image,\n            ref mut sampler,\n            ref mut coordinate,\n            ref mut array_index,\n            ref mut offset,\n            ref mut level,\n            ref mut depth_ref,\n            gather: _,\n            clamp_to_edge: _,\n        } => {\n            adjust(image);\n            adjust(sampler);\n            adjust(coordinate);\n            if let Some(e) = array_index.as_mut() {\n                adjust(e);\n            }\n            if let Some(e) = offset.as_mut() {\n                adjust(e);\n            }\n            match *level {\n                crate::SampleLevel::Exact(ref mut expr)\n                | crate::SampleLevel::Bias(ref mut expr) => {\n                    adjust(expr);\n                }\n                crate::SampleLevel::Gradient {\n                    ref mut x,\n                    ref mut y,\n                } => {\n                    adjust(x);\n                    adjust(y);\n                }\n                _ => {}\n            }\n            if let Some(e) = depth_ref.as_mut() {\n                adjust(e);\n            }\n        }\n        Expression::ImageLoad {\n            ref mut image,\n            ref mut coordinate,\n            ref mut array_index,\n            ref mut sample,\n            ref mut level,\n        } => {\n            adjust(image);\n            adjust(coordinate);\n            if let Some(e) = array_index.as_mut() {\n                adjust(e);\n            }\n            if let Some(e) = sample.as_mut() {\n                adjust(e);\n            }\n            if let Some(e) = level.as_mut() {\n                adjust(e);\n            }\n        }\n        Expression::ImageQuery {\n            ref mut image,\n            ref mut query,\n        } => {\n            adjust(image);\n            match *query {\n                crate::ImageQuery::Size { ref mut level } => {\n                    if let Some(e) = level.as_mut() {\n                        adjust(e);\n                    }\n                }\n                crate::ImageQuery::NumLevels\n                | crate::ImageQuery::NumLayers\n                | crate::ImageQuery::NumSamples => {}\n            }\n        }\n        Expression::Unary {\n            ref mut expr,\n            op: _,\n        } => {\n            adjust(expr);\n        }\n        Expression::Binary {\n            ref mut left,\n            ref mut right,\n            op: _,\n        } => {\n            adjust(left);\n            adjust(right);\n        }\n        Expression::Select {\n            ref mut condition,\n            ref mut accept,\n            ref mut reject,\n        } => {\n            adjust(condition);\n            adjust(accept);\n            adjust(reject);\n        }\n        Expression::Derivative {\n            ref mut expr,\n            axis: _,\n            ctrl: _,\n        } => {\n            adjust(expr);\n        }\n        Expression::Relational {\n            ref mut argument,\n            fun: _,\n        } => {\n            adjust(argument);\n        }\n        Expression::Math {\n            ref mut arg,\n            ref mut arg1,\n            ref mut arg2,\n            ref mut arg3,\n            fun: _,\n        } => {\n            adjust(arg);\n            if let Some(e) = arg1.as_mut() {\n                adjust(e);\n            }\n            if let Some(e) = arg2.as_mut() {\n                adjust(e);\n            }\n            if let Some(e) = arg3.as_mut() {\n                adjust(e);\n            }\n        }\n        Expression::As {\n            ref mut expr,\n            kind: _,\n            convert: _,\n        } => {\n            adjust(expr);\n        }\n        Expression::ArrayLength(ref mut expr) => {\n            adjust(expr);\n        }\n        Expression::RayQueryGetIntersection {\n            ref mut query,\n            committed: _,\n        } => {\n            adjust(query);\n        }\n        Expression::Literal(_)\n        | Expression::FunctionArgument(_)\n        | Expression::GlobalVariable(_)\n        | Expression::LocalVariable(_)\n        | Expression::CallResult(_)\n        | Expression::RayQueryProceedResult\n        | Expression::Constant(_)\n        | Expression::Override(_)\n        | Expression::ZeroValue(_)\n        | Expression::AtomicResult {\n            ty: _,\n            comparison: _,\n        }\n        | Expression::WorkGroupUniformLoadResult { ty: _ }\n        | Expression::SubgroupBallotResult\n        | Expression::SubgroupOperationResult { .. } => {}\n        Expression::RayQueryVertexPositions {\n            ref mut query,\n            committed: _,\n        } => {\n            adjust(query);\n        }\n        Expression::CooperativeLoad { ref mut data, .. } => {\n            adjust(&mut data.pointer);\n            adjust(&mut data.stride);\n        }\n        Expression::CooperativeMultiplyAdd {\n            ref mut a,\n            ref mut b,\n            ref mut c,\n        } => {\n            adjust(a);\n            adjust(b);\n            adjust(c);\n        }\n    }\n}\n\n/// Replace every expression handle in `block` with its counterpart\n/// given by `new_pos`.\nfn adjust_block(new_pos: &HandleVec<Expression, Handle<Expression>>, block: &mut Block) {\n    for stmt in block.iter_mut() {\n        adjust_stmt(new_pos, stmt);\n    }\n}\n\n/// Replace every expression handle in `stmt` with its counterpart\n/// given by `new_pos`.\nfn adjust_stmt(new_pos: &HandleVec<Expression, Handle<Expression>>, stmt: &mut Statement) {\n    let adjust = |expr: &mut Handle<Expression>| {\n        *expr = new_pos[*expr];\n    };\n    match *stmt {\n        Statement::Emit(ref mut range) => {\n            if let Some((mut first, mut last)) = range.first_and_last() {\n                adjust(&mut first);\n                adjust(&mut last);\n                *range = Range::new_from_bounds(first, last);\n            }\n        }\n        Statement::Block(ref mut block) => {\n            adjust_block(new_pos, block);\n        }\n        Statement::If {\n            ref mut condition,\n            ref mut accept,\n            ref mut reject,\n        } => {\n            adjust(condition);\n            adjust_block(new_pos, accept);\n            adjust_block(new_pos, reject);\n        }\n        Statement::Switch {\n            ref mut selector,\n            ref mut cases,\n        } => {\n            adjust(selector);\n            for case in cases.iter_mut() {\n                adjust_block(new_pos, &mut case.body);\n            }\n        }\n        Statement::Loop {\n            ref mut body,\n            ref mut continuing,\n            ref mut break_if,\n        } => {\n            adjust_block(new_pos, body);\n            adjust_block(new_pos, continuing);\n            if let Some(e) = break_if.as_mut() {\n                adjust(e);\n            }\n        }\n        Statement::Return { ref mut value } => {\n            if let Some(e) = value.as_mut() {\n                adjust(e);\n            }\n        }\n        Statement::Store {\n            ref mut pointer,\n            ref mut value,\n        } => {\n            adjust(pointer);\n            adjust(value);\n        }\n        Statement::ImageStore {\n            ref mut image,\n            ref mut coordinate,\n            ref mut array_index,\n            ref mut value,\n        } => {\n            adjust(image);\n            adjust(coordinate);\n            if let Some(e) = array_index.as_mut() {\n                adjust(e);\n            }\n            adjust(value);\n        }\n        Statement::Atomic {\n            ref mut pointer,\n            ref mut value,\n            ref mut result,\n            ref mut fun,\n        } => {\n            adjust(pointer);\n            adjust(value);\n            if let Some(ref mut result) = *result {\n                adjust(result);\n            }\n            match *fun {\n                crate::AtomicFunction::Exchange {\n                    compare: Some(ref mut compare),\n                } => {\n                    adjust(compare);\n                }\n                crate::AtomicFunction::Add\n                | crate::AtomicFunction::Subtract\n                | crate::AtomicFunction::And\n                | crate::AtomicFunction::ExclusiveOr\n                | crate::AtomicFunction::InclusiveOr\n                | crate::AtomicFunction::Min\n                | crate::AtomicFunction::Max\n                | crate::AtomicFunction::Exchange { compare: None } => {}\n            }\n        }\n        Statement::ImageAtomic {\n            ref mut image,\n            ref mut coordinate,\n            ref mut array_index,\n            fun: _,\n            ref mut value,\n        } => {\n            adjust(image);\n            adjust(coordinate);\n            if let Some(ref mut array_index) = *array_index {\n                adjust(array_index);\n            }\n            adjust(value);\n        }\n        Statement::WorkGroupUniformLoad {\n            ref mut pointer,\n            ref mut result,\n        } => {\n            adjust(pointer);\n            adjust(result);\n        }\n        Statement::SubgroupBallot {\n            ref mut result,\n            ref mut predicate,\n        } => {\n            if let Some(ref mut predicate) = *predicate {\n                adjust(predicate);\n            }\n            adjust(result);\n        }\n        Statement::SubgroupCollectiveOperation {\n            ref mut argument,\n            ref mut result,\n            ..\n        } => {\n            adjust(argument);\n            adjust(result);\n        }\n        Statement::SubgroupGather {\n            ref mut mode,\n            ref mut argument,\n            ref mut result,\n        } => {\n            match *mode {\n                crate::GatherMode::BroadcastFirst => {}\n                crate::GatherMode::Broadcast(ref mut index)\n                | crate::GatherMode::Shuffle(ref mut index)\n                | crate::GatherMode::ShuffleDown(ref mut index)\n                | crate::GatherMode::ShuffleUp(ref mut index)\n                | crate::GatherMode::ShuffleXor(ref mut index)\n                | crate::GatherMode::QuadBroadcast(ref mut index) => {\n                    adjust(index);\n                }\n                crate::GatherMode::QuadSwap(_) => {}\n            }\n            adjust(argument);\n            adjust(result)\n        }\n        Statement::Call {\n            ref mut arguments,\n            ref mut result,\n            function: _,\n        } => {\n            for argument in arguments.iter_mut() {\n                adjust(argument);\n            }\n            if let Some(e) = result.as_mut() {\n                adjust(e);\n            }\n        }\n        Statement::RayQuery {\n            ref mut query,\n            ref mut fun,\n        } => {\n            adjust(query);\n            match *fun {\n                crate::RayQueryFunction::Initialize {\n                    ref mut acceleration_structure,\n                    ref mut descriptor,\n                } => {\n                    adjust(acceleration_structure);\n                    adjust(descriptor);\n                }\n                crate::RayQueryFunction::Proceed { ref mut result } => {\n                    adjust(result);\n                }\n                crate::RayQueryFunction::GenerateIntersection { ref mut hit_t } => {\n                    adjust(hit_t);\n                }\n                crate::RayQueryFunction::ConfirmIntersection => {}\n                crate::RayQueryFunction::Terminate => {}\n            }\n        }\n        Statement::CooperativeStore {\n            ref mut target,\n            ref mut data,\n        } => {\n            adjust(target);\n            adjust(&mut data.pointer);\n            adjust(&mut data.stride);\n        }\n        Statement::RayPipelineFunction(ref mut func) => match *func {\n            crate::RayPipelineFunction::TraceRay {\n                ref mut acceleration_structure,\n                ref mut descriptor,\n                ref mut payload,\n            } => {\n                adjust(acceleration_structure);\n                adjust(descriptor);\n                adjust(payload);\n            }\n        },\n        Statement::Break\n        | Statement::Continue\n        | Statement::Kill\n        | Statement::ControlBarrier(_)\n        | Statement::MemoryBarrier(_) => {}\n    }\n}\n\n/// Adjust [`Emit`] statements in `block` to skip [`needs_pre_emit`] expressions we have introduced.\n///\n/// According to validation, [`Emit`] statements must not cover any expressions\n/// for which [`Expression::needs_pre_emit`] returns true. All expressions built\n/// by successful constant evaluation fall into that category, meaning that\n/// `process_function` will usually rewrite [`Override`] expressions and those\n/// that use their values into pre-emitted expressions, leaving any [`Emit`]\n/// statements that cover them invalid.\n///\n/// This function rewrites all [`Emit`] statements into zero or more new\n/// [`Emit`] statements covering only those expressions in the original range\n/// that are not pre-emitted.\n///\n/// [`Emit`]: Statement::Emit\n/// [`needs_pre_emit`]: Expression::needs_pre_emit\n/// [`Override`]: Expression::Override\nfn filter_emits_in_block(block: &mut Block, expressions: &Arena<Expression>) {\n    let original = mem::replace(block, Block::with_capacity(block.len()));\n    for (stmt, span) in original.span_into_iter() {\n        match stmt {\n            Statement::Emit(range) => {\n                let mut current = None;\n                for expr_h in range {\n                    if expressions[expr_h].needs_pre_emit() {\n                        if let Some((first, last)) = current {\n                            block.push(Statement::Emit(Range::new_from_bounds(first, last)), span);\n                        }\n\n                        current = None;\n                    } else if let Some((_, ref mut last)) = current {\n                        *last = expr_h;\n                    } else {\n                        current = Some((expr_h, expr_h));\n                    }\n                }\n                if let Some((first, last)) = current {\n                    block.push(Statement::Emit(Range::new_from_bounds(first, last)), span);\n                }\n            }\n            Statement::Block(mut child) => {\n                filter_emits_in_block(&mut child, expressions);\n                block.push(Statement::Block(child), span);\n            }\n            Statement::If {\n                condition,\n                mut accept,\n                mut reject,\n            } => {\n                filter_emits_in_block(&mut accept, expressions);\n                filter_emits_in_block(&mut reject, expressions);\n                block.push(\n                    Statement::If {\n                        condition,\n                        accept,\n                        reject,\n                    },\n                    span,\n                );\n            }\n            Statement::Switch {\n                selector,\n                mut cases,\n            } => {\n                for case in &mut cases {\n                    filter_emits_in_block(&mut case.body, expressions);\n                }\n                block.push(Statement::Switch { selector, cases }, span);\n            }\n            Statement::Loop {\n                mut body,\n                mut continuing,\n                break_if,\n            } => {\n                filter_emits_in_block(&mut body, expressions);\n                filter_emits_in_block(&mut continuing, expressions);\n                block.push(\n                    Statement::Loop {\n                        body,\n                        continuing,\n                        break_if,\n                    },\n                    span,\n                );\n            }\n            stmt => block.push(stmt.clone(), span),\n        }\n    }\n}\n\nfn map_value_to_literal(value: f64, scalar: Scalar) -> Result<Literal, PipelineConstantError> {\n    // note that in rust 0.0 == -0.0\n    match scalar {\n        Scalar::BOOL => {\n            // https://webidl.spec.whatwg.org/#js-boolean\n            let value = value != 0.0 && !value.is_nan();\n            Ok(Literal::Bool(value))\n        }\n        Scalar::I32 => {\n            // https://webidl.spec.whatwg.org/#js-long\n            if !value.is_finite() {\n                return Err(PipelineConstantError::SrcNeedsToBeFinite);\n            }\n\n            let value = value.trunc();\n            if value < f64::from(i32::MIN) || value > f64::from(i32::MAX) {\n                return Err(PipelineConstantError::DstRangeTooSmall);\n            }\n\n            let value = value as i32;\n            Ok(Literal::I32(value))\n        }\n        Scalar::U32 => {\n            // https://webidl.spec.whatwg.org/#js-unsigned-long\n            if !value.is_finite() {\n                return Err(PipelineConstantError::SrcNeedsToBeFinite);\n            }\n\n            let value = value.trunc();\n            if value < f64::from(u32::MIN) || value > f64::from(u32::MAX) {\n                return Err(PipelineConstantError::DstRangeTooSmall);\n            }\n\n            let value = value as u32;\n            Ok(Literal::U32(value))\n        }\n        Scalar::F16 => {\n            // https://webidl.spec.whatwg.org/#js-float\n            if !value.is_finite() {\n                return Err(PipelineConstantError::SrcNeedsToBeFinite);\n            }\n\n            let value = half::f16::from_f64(value);\n            if !value.is_finite() {\n                return Err(PipelineConstantError::DstRangeTooSmall);\n            }\n\n            Ok(Literal::F16(value))\n        }\n        Scalar::F32 => {\n            // https://webidl.spec.whatwg.org/#js-float\n            if !value.is_finite() {\n                return Err(PipelineConstantError::SrcNeedsToBeFinite);\n            }\n\n            let value = value as f32;\n            if !value.is_finite() {\n                return Err(PipelineConstantError::DstRangeTooSmall);\n            }\n\n            Ok(Literal::F32(value))\n        }\n        Scalar::F64 => {\n            // https://webidl.spec.whatwg.org/#js-double\n            if !value.is_finite() {\n                return Err(PipelineConstantError::SrcNeedsToBeFinite);\n            }\n\n            Ok(Literal::F64(value))\n        }\n        Scalar::ABSTRACT_FLOAT | Scalar::ABSTRACT_INT => {\n            unreachable!(\"abstract values should not be validated out of override processing\")\n        }\n        _ => unreachable!(\"unrecognized scalar type for override\"),\n    }\n}\n\n#[test]\nfn test_map_value_to_literal() {\n    let bool_test_cases = [\n        (0.0, false),\n        (-0.0, false),\n        (f64::NAN, false),\n        (1.0, true),\n        (f64::INFINITY, true),\n        (f64::NEG_INFINITY, true),\n    ];\n    for (value, out) in bool_test_cases {\n        let res = Ok(Literal::Bool(out));\n        assert_eq!(map_value_to_literal(value, Scalar::BOOL), res);\n    }\n\n    for scalar in [Scalar::I32, Scalar::U32, Scalar::F32, Scalar::F64] {\n        for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {\n            let res = Err(PipelineConstantError::SrcNeedsToBeFinite);\n            assert_eq!(map_value_to_literal(value, scalar), res);\n        }\n    }\n\n    // i32\n    assert_eq!(\n        map_value_to_literal(f64::from(i32::MIN), Scalar::I32),\n        Ok(Literal::I32(i32::MIN))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(i32::MAX), Scalar::I32),\n        Ok(Literal::I32(i32::MAX))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(i32::MIN) - 1.0, Scalar::I32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(i32::MAX) + 1.0, Scalar::I32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n\n    // u32\n    assert_eq!(\n        map_value_to_literal(f64::from(u32::MIN), Scalar::U32),\n        Ok(Literal::U32(u32::MIN))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(u32::MAX), Scalar::U32),\n        Ok(Literal::U32(u32::MAX))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(u32::MIN) - 1.0, Scalar::U32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(u32::MAX) + 1.0, Scalar::U32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n\n    // f32\n    assert_eq!(\n        map_value_to_literal(f64::from(f32::MIN), Scalar::F32),\n        Ok(Literal::F32(f32::MIN))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from(f32::MAX), Scalar::F32),\n        Ok(Literal::F32(f32::MAX))\n    );\n    assert_eq!(\n        map_value_to_literal(-f64::from_bits(0x47efffffefffffff), Scalar::F32),\n        Ok(Literal::F32(f32::MIN))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from_bits(0x47efffffefffffff), Scalar::F32),\n        Ok(Literal::F32(f32::MAX))\n    );\n    assert_eq!(\n        map_value_to_literal(-f64::from_bits(0x47effffff0000000), Scalar::F32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n    assert_eq!(\n        map_value_to_literal(f64::from_bits(0x47effffff0000000), Scalar::F32),\n        Err(PipelineConstantError::DstRangeTooSmall)\n    );\n\n    // f64\n    assert_eq!(\n        map_value_to_literal(f64::MIN, Scalar::F64),\n        Ok(Literal::F64(f64::MIN))\n    );\n    assert_eq!(\n        map_value_to_literal(f64::MAX, Scalar::F64),\n        Ok(Literal::F64(f64::MAX))\n    );\n}\n"
  },
  {
    "path": "naga/src/back/spv/block.rs",
    "content": "/*!\nImplementations for `BlockContext` methods.\n*/\n\nuse alloc::vec::Vec;\n\nuse arrayvec::ArrayVec;\nuse spirv::Word;\n\nuse super::{\n    helpers::map_storage_class, index::BoundsCheckResult, selection::Selection, Block,\n    BlockContext, Dimension, Error, IdGenerator, Instruction, LocalType, LookupType, NumericType,\n    ResultMember, WrappedFunction, Writer, WriterFlags,\n};\nuse crate::{\n    arena::Handle, back::spv::helpers::is_uniform_matcx2_struct_member_access,\n    proc::index::GuardedIndex, Statement,\n};\n\nfn get_dimension(type_inner: &crate::TypeInner) -> Dimension {\n    match *type_inner {\n        crate::TypeInner::Scalar(_) => Dimension::Scalar,\n        crate::TypeInner::Vector { .. } => Dimension::Vector,\n        crate::TypeInner::Matrix { .. } => Dimension::Matrix,\n        crate::TypeInner::CooperativeMatrix { .. } => Dimension::CooperativeMatrix,\n        _ => unreachable!(),\n    }\n}\n\n/// How to derive the type of `OpAccessChain` instructions from Naga IR.\n///\n/// Most of the time, we compile Naga IR to SPIR-V instructions whose result\n/// types are simply the direct SPIR-V analog of the Naga IR's. But in some\n/// cases, the Naga IR and SPIR-V types need to diverge.\n///\n/// This enum specifies how [`BlockContext::write_access_chain`] should\n/// choose a SPIR-V result type for the `OpAccessChain` it generates, based on\n/// the type of the given Naga IR [`Expression`] it's generating code for.\n///\n/// [`Expression`]: crate::Expression\n#[derive(Copy, Clone)]\nenum AccessTypeAdjustment {\n    /// No adjustment needed: the SPIR-V type should be the direct\n    /// analog of the Naga IR expression type.\n    ///\n    /// For most access chains, this is the right thing: the Naga IR access\n    /// expression produces a [`Pointer`] to the element / component, and the\n    /// SPIR-V `OpAccessChain` instruction does the same.\n    ///\n    /// [`Pointer`]: crate::TypeInner::Pointer\n    None,\n\n    /// The SPIR-V type should be an `OpPointer` to the direct analog of the\n    /// Naga IR expression's type.\n    ///\n    /// This is necessary for indexing binding arrays in the [`Handle`] address\n    /// space:\n    ///\n    /// - In Naga IR, referencing a binding array [`GlobalVariable`] in the\n    ///   [`Handle`] address space produces a value of type [`BindingArray`],\n    ///   not a pointer to such. And [`Access`] and [`AccessIndex`] expressions\n    ///   operate on handle binding arrays by value, and produce handle values,\n    ///   not pointers.\n    ///\n    /// - In SPIR-V, a binding array `OpVariable` produces a pointer to an\n    ///   array, and `OpAccessChain` instructions operate on pointers,\n    ///   regardless of whether the elements are opaque types or not.\n    ///\n    /// See also the documentation for [`BindingArray`].\n    ///\n    /// [`Handle`]: crate::AddressSpace::Handle\n    /// [`GlobalVariable`]: crate::GlobalVariable\n    /// [`BindingArray`]: crate::TypeInner::BindingArray\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    IntroducePointer(spirv::StorageClass),\n\n    /// The SPIR-V type should be an `OpPointer` to the std140 layout\n    /// compatible variant of the Naga IR expression's base type.\n    ///\n    /// This is used when accessing a type through an [`AddressSpace::Uniform`]\n    /// pointer in cases where the original type is incompatible with std140\n    /// layout requirements and we have therefore declared the uniform to be of\n    /// an alternative std140 compliant type.\n    ///\n    /// [`AddressSpace::Uniform`]: crate::AddressSpace::Uniform\n    UseStd140CompatType,\n}\n\n/// The results of emitting code for a left-hand-side expression.\n///\n/// On success, `write_access_chain` returns one of these.\nenum ExpressionPointer {\n    /// The pointer to the expression's value is available, as the value of the\n    /// expression with the given id.\n    Ready { pointer_id: Word },\n\n    /// The access expression must be conditional on the value of `condition`, a boolean\n    /// expression that is true if all indices are in bounds. If `condition` is true, then\n    /// `access` is an `OpAccessChain` instruction that will compute a pointer to the\n    /// expression's value. If `condition` is false, then executing `access` would be\n    /// undefined behavior.\n    Conditional {\n        condition: Word,\n        access: Instruction,\n    },\n}\n\n/// The termination statement to be added to the end of the block\nenum BlockExit {\n    /// Generates an OpReturn (void return)\n    Return,\n    /// Generates an OpBranch to the specified block\n    Branch {\n        /// The branch target block\n        target: Word,\n    },\n    /// Translates a loop `break if` into an `OpBranchConditional` to the\n    /// merge block if true (the merge block is passed through [`LoopContext::break_id`]\n    /// or else to the loop header (passed through [`preamble_id`])\n    ///\n    /// [`preamble_id`]: Self::BreakIf::preamble_id\n    BreakIf {\n        /// The condition of the `break if`\n        condition: Handle<crate::Expression>,\n        /// The loop header block id\n        preamble_id: Word,\n    },\n}\n\n/// What code generation did with a provided [`BlockExit`] value.\n///\n/// A function that accepts a [`BlockExit`] argument should return a value of\n/// this type, to indicate whether the code it generated ended up using the\n/// provided exit, or ignored it and did a non-local exit of some other kind\n/// (say, [`Break`] or [`Continue`]). Some callers must use this information to\n/// decide whether to generate the target block at all.\n///\n/// [`Break`]: Statement::Break\n/// [`Continue`]: Statement::Continue\n#[must_use]\nenum BlockExitDisposition {\n    /// The generated code used the provided `BlockExit` value. If it included a\n    /// block label, the caller should be sure to actually emit the block it\n    /// refers to.\n    Used,\n\n    /// The generated code did not use the provided `BlockExit` value. If it\n    /// included a block label, the caller should not bother to actually emit\n    /// the block it refers to, unless it knows the block is needed for\n    /// something else.\n    Discarded,\n}\n\n#[derive(Clone, Copy, Default)]\nstruct LoopContext {\n    continuing_id: Option<Word>,\n    break_id: Option<Word>,\n}\n\n#[derive(Debug)]\npub(crate) struct DebugInfoInner<'a> {\n    pub source_code: &'a str,\n    pub source_file_id: Word,\n}\n\nimpl Writer {\n    // Flip Y coordinate to adjust for coordinate space difference\n    // between SPIR-V and our IR.\n    // The `position_id` argument is a pointer to a `vecN<f32>`,\n    // whose `y` component we will negate.\n    fn write_epilogue_position_y_flip(\n        &mut self,\n        position_id: Word,\n        body: &mut Vec<Instruction>,\n    ) -> Result<(), Error> {\n        let float_ptr_type_id = self.get_f32_pointer_type_id(spirv::StorageClass::Output);\n        let index_y_id = self.get_index_constant(1);\n        let access_id = self.id_gen.next();\n        body.push(Instruction::access_chain(\n            float_ptr_type_id,\n            access_id,\n            position_id,\n            &[index_y_id],\n        ));\n\n        let float_type_id = self.get_f32_type_id();\n        let load_id = self.id_gen.next();\n        body.push(Instruction::load(float_type_id, load_id, access_id, None));\n\n        let neg_id = self.id_gen.next();\n        body.push(Instruction::unary(\n            spirv::Op::FNegate,\n            float_type_id,\n            neg_id,\n            load_id,\n        ));\n\n        body.push(Instruction::store(access_id, neg_id, None));\n        Ok(())\n    }\n\n    // Clamp fragment depth between 0 and 1.\n    fn write_epilogue_frag_depth_clamp(\n        &mut self,\n        frag_depth_id: Word,\n        body: &mut Vec<Instruction>,\n    ) -> Result<(), Error> {\n        let float_type_id = self.get_f32_type_id();\n        let zero_scalar_id = self.get_constant_scalar(crate::Literal::F32(0.0));\n        let one_scalar_id = self.get_constant_scalar(crate::Literal::F32(1.0));\n\n        let original_id = self.id_gen.next();\n        body.push(Instruction::load(\n            float_type_id,\n            original_id,\n            frag_depth_id,\n            None,\n        ));\n\n        let clamp_id = self.id_gen.next();\n        body.push(Instruction::ext_inst_gl_op(\n            self.gl450_ext_inst_id,\n            spirv::GlslStd450Op::FClamp,\n            float_type_id,\n            clamp_id,\n            &[original_id, zero_scalar_id, one_scalar_id],\n        ));\n\n        body.push(Instruction::store(frag_depth_id, clamp_id, None));\n        Ok(())\n    }\n\n    fn write_entry_point_return(\n        &mut self,\n        value_id: Word,\n        ir_result: &crate::FunctionResult,\n        result_members: &[ResultMember],\n        body: &mut Vec<Instruction>,\n    ) -> Result<Instruction, Error> {\n        for (index, res_member) in result_members.iter().enumerate() {\n            // This isn't a real builtin, and is handled elsewhere\n            if res_member.built_in == Some(crate::BuiltIn::MeshTaskSize) {\n                return Ok(Instruction::return_value(value_id));\n            }\n            let member_value_id = match ir_result.binding {\n                Some(_) => value_id,\n                None => {\n                    let member_value_id = self.id_gen.next();\n                    body.push(Instruction::composite_extract(\n                        res_member.type_id,\n                        member_value_id,\n                        value_id,\n                        &[index as u32],\n                    ));\n                    member_value_id\n                }\n            };\n\n            self.store_io_with_f16_polyfill(body, res_member.id, member_value_id);\n\n            match res_member.built_in {\n                Some(crate::BuiltIn::Position { .. })\n                    if self.flags.contains(WriterFlags::ADJUST_COORDINATE_SPACE) =>\n                {\n                    self.write_epilogue_position_y_flip(res_member.id, body)?;\n                }\n                Some(crate::BuiltIn::FragDepth)\n                    if self.flags.contains(WriterFlags::CLAMP_FRAG_DEPTH) =>\n                {\n                    self.write_epilogue_frag_depth_clamp(res_member.id, body)?;\n                }\n                _ => {}\n            }\n        }\n        Ok(Instruction::return_void())\n    }\n}\n\nimpl BlockContext<'_> {\n    /// Generates code to ensure that a loop is bounded. Should be called immediately\n    /// after adding the OpLoopMerge instruction to `block`. This function will\n    /// [`consume()`](crate::back::spv::Function::consume) `block` and append its\n    /// instructions to a new [`Block`], which will be returned to the caller for it to\n    /// consumed prior to writing the loop body.\n    ///\n    /// Additionally this function will populate [`force_loop_bounding_vars`](crate::back::spv::Function::force_loop_bounding_vars),\n    /// ensuring that [`Function::to_words()`](crate::back::spv::Function::to_words) will\n    /// declare the required variables.\n    ///\n    /// See [`crate::back::msl::Writer::gen_force_bounded_loop_statements`] for details\n    /// of why this is required.\n    fn write_force_bounded_loop_instructions(&mut self, mut block: Block, merge_id: Word) -> Block {\n        let uint_type_id = self.writer.get_u32_type_id();\n        let uint2_type_id = self.writer.get_vec2u_type_id();\n        let uint2_ptr_type_id = self\n            .writer\n            .get_vec2u_pointer_type_id(spirv::StorageClass::Function);\n        let bool_type_id = self.writer.get_bool_type_id();\n        let bool2_type_id = self.writer.get_vec2_bool_type_id();\n        let zero_uint_const_id = self.writer.get_constant_scalar(crate::Literal::U32(0));\n        let zero_uint2_const_id = self.writer.get_constant_composite(\n            LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                size: crate::VectorSize::Bi,\n                scalar: crate::Scalar::U32,\n            })),\n            &[zero_uint_const_id, zero_uint_const_id],\n        );\n        let one_uint_const_id = self.writer.get_constant_scalar(crate::Literal::U32(1));\n        let max_uint_const_id = self\n            .writer\n            .get_constant_scalar(crate::Literal::U32(u32::MAX));\n        let max_uint2_const_id = self.writer.get_constant_composite(\n            LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                size: crate::VectorSize::Bi,\n                scalar: crate::Scalar::U32,\n            })),\n            &[max_uint_const_id, max_uint_const_id],\n        );\n\n        let loop_counter_var_id = self.gen_id();\n        if self.writer.flags.contains(WriterFlags::DEBUG) {\n            self.writer\n                .debugs\n                .push(Instruction::name(loop_counter_var_id, \"loop_bound\"));\n        }\n        let var = super::LocalVariable {\n            id: loop_counter_var_id,\n            instruction: Instruction::variable(\n                uint2_ptr_type_id,\n                loop_counter_var_id,\n                spirv::StorageClass::Function,\n                Some(max_uint2_const_id),\n            ),\n        };\n        self.function.force_loop_bounding_vars.push(var);\n\n        let break_if_block = self.gen_id();\n\n        self.function\n            .consume(block, Instruction::branch(break_if_block));\n        block = Block::new(break_if_block);\n\n        // Load the current loop counter value from its variable. We use a vec2<u32> to\n        // simulate a 64-bit counter.\n        let load_id = self.gen_id();\n        block.body.push(Instruction::load(\n            uint2_type_id,\n            load_id,\n            loop_counter_var_id,\n            None,\n        ));\n\n        // If both the high and low u32s have reached 0 then break. ie\n        // if (all(eq(loop_counter, vec2(0)))) { break; }\n        let eq_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool2_type_id,\n            eq_id,\n            zero_uint2_const_id,\n            load_id,\n        ));\n        let all_eq_id = self.gen_id();\n        block.body.push(Instruction::relational(\n            spirv::Op::All,\n            bool_type_id,\n            all_eq_id,\n            eq_id,\n        ));\n\n        let inc_counter_block_id = self.gen_id();\n        block.body.push(Instruction::selection_merge(\n            inc_counter_block_id,\n            spirv::SelectionControl::empty(),\n        ));\n        self.function.consume(\n            block,\n            Instruction::branch_conditional(all_eq_id, merge_id, inc_counter_block_id),\n        );\n        block = Block::new(inc_counter_block_id);\n\n        // To simulate a 64-bit counter we always decrement the low u32, and decrement\n        // the high u32 when the low u32 overflows. ie\n        // counter -= vec2(select(0u, 1u, counter.y == 0), 1u);\n        // Count down from u32::MAX rather than up from 0 to avoid hang on\n        // certain Intel drivers. See <https://github.com/gfx-rs/wgpu/issues/7319>.\n        let low_id = self.gen_id();\n        block.body.push(Instruction::composite_extract(\n            uint_type_id,\n            low_id,\n            load_id,\n            &[1],\n        ));\n        let low_overflow_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            low_overflow_id,\n            low_id,\n            zero_uint_const_id,\n        ));\n        let carry_bit_id = self.gen_id();\n        block.body.push(Instruction::select(\n            uint_type_id,\n            carry_bit_id,\n            low_overflow_id,\n            one_uint_const_id,\n            zero_uint_const_id,\n        ));\n        let decrement_id = self.gen_id();\n        block.body.push(Instruction::composite_construct(\n            uint2_type_id,\n            decrement_id,\n            &[carry_bit_id, one_uint_const_id],\n        ));\n        let result_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::ISub,\n            uint2_type_id,\n            result_id,\n            load_id,\n            decrement_id,\n        ));\n        block\n            .body\n            .push(Instruction::store(loop_counter_var_id, result_id, None));\n\n        block\n    }\n\n    /// If `pointer` refers to an access chain that contains a dynamic indexing\n    /// of a two-row matrix in the [`Uniform`] address space, write code to\n    /// access the value returning the ID of the result. Else return None.\n    ///\n    /// Two-row matrices in the uniform address space will have been declared\n    /// using a alternative std140 layout compatible type, where each column is\n    /// a member of a containing struct. As a result, SPIR-V is unable to access\n    /// its columns with a non-constant index. To work around this limitation\n    /// this function will call [`Self::write_checked_load()`] to load the\n    /// matrix itself, which handles conversion from the std140 compatible type\n    /// to the real matrix type. It then calls a [`wrapper function`] to obtain\n    /// the correct column from the matrix, and possibly extracts a component\n    /// from the vector too.\n    ///\n    /// [`Uniform`]: crate::AddressSpace::Uniform\n    /// [`wrapper function`]: super::Writer::write_wrapped_matcx2_get_column\n    fn maybe_write_uniform_matcx2_dynamic_access(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<Option<Word>, Error> {\n        // If this access chain contains a dynamic matrix access, `pointer` is\n        // either a pointer to a vector (the column) or a scalar (a component\n        // within the column). In either case grab the pointer to the column,\n        // and remember the component index if there is one. If `pointer`\n        // points to any other type we're not interested.\n        let (column_pointer, component_index) = match self.fun_info[pointer]\n            .ty\n            .inner_with(&self.ir_module.types)\n            .pointer_base_type()\n        {\n            Some(resolution) => match *resolution.inner_with(&self.ir_module.types) {\n                crate::TypeInner::Scalar(_) => match self.ir_function.expressions[pointer] {\n                    crate::Expression::Access { base, index } => {\n                        (base, Some(GuardedIndex::Expression(index)))\n                    }\n                    crate::Expression::AccessIndex { base, index } => {\n                        (base, Some(GuardedIndex::Known(index)))\n                    }\n                    _ => return Ok(None),\n                },\n                crate::TypeInner::Vector { .. } => (pointer, None),\n                _ => return Ok(None),\n            },\n            None => return Ok(None),\n        };\n\n        // Ensure the column is accessed with a dynamic index (i.e.\n        // `Expression::Access`), and grab the pointer to the matrix.\n        let crate::Expression::Access {\n            base: matrix_pointer,\n            index: column_index,\n        } = self.ir_function.expressions[column_pointer]\n        else {\n            return Ok(None);\n        };\n\n        // Ensure the matrix pointer is in the uniform address space.\n        let crate::TypeInner::Pointer {\n            base: matrix_pointer_base_type,\n            space: crate::AddressSpace::Uniform,\n        } = *self.fun_info[matrix_pointer]\n            .ty\n            .inner_with(&self.ir_module.types)\n        else {\n            return Ok(None);\n        };\n\n        // Ensure the matrix pointer actually points to a Cx2 matrix.\n        let crate::TypeInner::Matrix {\n            columns,\n            rows: rows @ crate::VectorSize::Bi,\n            scalar,\n        } = self.ir_module.types[matrix_pointer_base_type].inner\n        else {\n            return Ok(None);\n        };\n\n        let matrix_type_id = self.get_numeric_type_id(NumericType::Matrix {\n            columns,\n            rows,\n            scalar,\n        });\n        let column_type_id = self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n        let component_type_id = self.get_numeric_type_id(NumericType::Scalar(scalar));\n        let get_column_function_id = self.writer.wrapped_functions\n            [&WrappedFunction::MatCx2GetColumn {\n                r#type: matrix_pointer_base_type,\n            }];\n\n        let matrix_load_id = self.write_checked_load(\n            matrix_pointer,\n            block,\n            AccessTypeAdjustment::None,\n            matrix_type_id,\n        )?;\n\n        // Naga IR allows the index to be either an I32 or U32 but our wrapper\n        // function expects a U32 argument, so convert it if required.\n        let column_index_id = match *self.fun_info[column_index]\n            .ty\n            .inner_with(&self.ir_module.types)\n        {\n            crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Uint,\n                ..\n            }) => self.cached[column_index],\n            crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Sint,\n                ..\n            }) => {\n                let cast_id = self.gen_id();\n                let u32_type_id = self.writer.get_u32_type_id();\n                block.body.push(Instruction::unary(\n                    spirv::Op::Bitcast,\n                    u32_type_id,\n                    cast_id,\n                    self.cached[column_index],\n                ));\n                cast_id\n            }\n            _ => return Err(Error::Validation(\"Matrix access index must be u32 or i32\")),\n        };\n        let column_id = self.gen_id();\n        block.body.push(Instruction::function_call(\n            column_type_id,\n            column_id,\n            get_column_function_id,\n            &[matrix_load_id, column_index_id],\n        ));\n        let result_id = match component_index {\n            Some(index) => self.write_vector_access(\n                component_type_id,\n                column_pointer,\n                Some(column_id),\n                index,\n                block,\n            )?,\n            None => column_id,\n        };\n\n        Ok(Some(result_id))\n    }\n\n    /// If `pointer` refers to two-row matrix that is a member of a struct in\n    /// the [`Uniform`] address space, write code to load the matrix returning\n    /// the ID of the result. Else return None.\n    ///\n    /// Two-row matrices that are struct members in the uniform address space\n    /// will have been decomposed such that the struct contains a separate\n    /// vector member for each column of the matrix. This function will load\n    /// each column separately from the containing struct, then composite them\n    /// into the real matrix type.\n    ///\n    /// [`Uniform`]: crate::AddressSpace::Uniform\n    fn maybe_write_load_uniform_matcx2_struct_member(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<Option<Word>, Error> {\n        // Check this is a uniform address space pointer to a two-row matrix.\n        let crate::TypeInner::Pointer {\n            base: matrix_type,\n            space: space @ crate::AddressSpace::Uniform,\n        } = *self.fun_info[pointer].ty.inner_with(&self.ir_module.types)\n        else {\n            return Ok(None);\n        };\n\n        let crate::TypeInner::Matrix {\n            columns,\n            rows: rows @ crate::VectorSize::Bi,\n            scalar,\n        } = self.ir_module.types[matrix_type].inner\n        else {\n            return Ok(None);\n        };\n\n        // Check this is a struct member. Note struct members can only be\n        // accessed with `AccessIndex`.\n        let crate::Expression::AccessIndex {\n            base: struct_pointer,\n            index: member_index,\n        } = self.ir_function.expressions[pointer]\n        else {\n            return Ok(None);\n        };\n\n        let crate::TypeInner::Pointer {\n            base: struct_type, ..\n        } = *self.fun_info[struct_pointer]\n            .ty\n            .inner_with(&self.ir_module.types)\n        else {\n            return Ok(None);\n        };\n\n        let crate::TypeInner::Struct { .. } = self.ir_module.types[struct_type].inner else {\n            return Ok(None);\n        };\n\n        let matrix_type_id = self.get_numeric_type_id(NumericType::Matrix {\n            columns,\n            rows,\n            scalar,\n        });\n        let column_type_id = self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n        let column_pointer_type_id =\n            self.get_pointer_type_id(column_type_id, map_storage_class(space));\n        let column0_index = self.writer.std140_compat_uniform_types[&struct_type].member_indices\n            [member_index as usize];\n        let column_indices = (0..columns as u32)\n            .map(|c| self.get_index_constant(column0_index + c))\n            .collect::<ArrayVec<_, 4>>();\n\n        // Load each column from the struct, then composite into the real\n        // matrix type.\n        let load_mat_from_struct =\n            |struct_pointer_id: Word, id_gen: &mut IdGenerator, block: &mut Block| -> Word {\n                let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();\n                for index in &column_indices {\n                    let column_pointer_id = id_gen.next();\n                    block.body.push(Instruction::access_chain(\n                        column_pointer_type_id,\n                        column_pointer_id,\n                        struct_pointer_id,\n                        &[*index],\n                    ));\n                    let column_id = id_gen.next();\n                    block.body.push(Instruction::load(\n                        column_type_id,\n                        column_id,\n                        column_pointer_id,\n                        None,\n                    ));\n                    column_ids.push(column_id);\n                }\n                let result_id = id_gen.next();\n                block.body.push(Instruction::composite_construct(\n                    matrix_type_id,\n                    result_id,\n                    &column_ids,\n                ));\n                result_id\n            };\n\n        let result_id = match self.write_access_chain(\n            struct_pointer,\n            block,\n            AccessTypeAdjustment::UseStd140CompatType,\n        )? {\n            ExpressionPointer::Ready { pointer_id } => {\n                load_mat_from_struct(pointer_id, &mut self.writer.id_gen, block)\n            }\n            ExpressionPointer::Conditional { condition, access } => self\n                .write_conditional_indexed_load(\n                    matrix_type_id,\n                    condition,\n                    block,\n                    |id_gen, block| {\n                        let pointer_id = access.result_id.unwrap();\n                        block.body.push(access);\n                        load_mat_from_struct(pointer_id, id_gen, block)\n                    },\n                ),\n        };\n\n        Ok(Some(result_id))\n    }\n\n    /// Cache an expression for a value.\n    pub(super) fn cache_expression_value(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        let is_named_expression = self\n            .ir_function\n            .named_expressions\n            .contains_key(&expr_handle);\n\n        if self.fun_info[expr_handle].ref_count == 0 && !is_named_expression {\n            return Ok(());\n        }\n\n        let result_type_id = self.get_expression_type_id(&self.fun_info[expr_handle].ty);\n        let id = match self.ir_function.expressions[expr_handle] {\n            crate::Expression::Literal(literal) => self.writer.get_constant_scalar(literal),\n            crate::Expression::Constant(handle) => {\n                let init = self.ir_module.constants[handle].init;\n                self.writer.constant_ids[init]\n            }\n            crate::Expression::Override(_) => return Err(Error::Override),\n            crate::Expression::ZeroValue(_) => self.writer.get_constant_null(result_type_id),\n            crate::Expression::Compose { ty, ref components } => {\n                self.temp_list.clear();\n                if self.expression_constness.is_const(expr_handle) {\n                    self.temp_list.extend(\n                        crate::proc::flatten_compose(\n                            ty,\n                            components,\n                            &self.ir_function.expressions,\n                            &self.ir_module.types,\n                        )\n                        .map(|component| self.cached[component]),\n                    );\n                    self.writer\n                        .get_constant_composite(LookupType::Handle(ty), &self.temp_list)\n                } else {\n                    self.temp_list\n                        .extend(components.iter().map(|&component| self.cached[component]));\n\n                    let id = self.gen_id();\n                    block.body.push(Instruction::composite_construct(\n                        result_type_id,\n                        id,\n                        &self.temp_list,\n                    ));\n                    id\n                }\n            }\n            crate::Expression::Splat { size, value } => {\n                let value_id = self.cached[value];\n                let components = &[value_id; 4][..size as usize];\n\n                if self.expression_constness.is_const(expr_handle) {\n                    let ty = self\n                        .writer\n                        .get_expression_lookup_type(&self.fun_info[expr_handle].ty);\n                    self.writer.get_constant_composite(ty, components)\n                } else {\n                    let id = self.gen_id();\n                    block.body.push(Instruction::composite_construct(\n                        result_type_id,\n                        id,\n                        components,\n                    ));\n                    id\n                }\n            }\n            crate::Expression::Access { base, index } => {\n                let base_ty_inner = self.fun_info[base].ty.inner_with(&self.ir_module.types);\n                match *base_ty_inner {\n                    crate::TypeInner::Pointer { .. } | crate::TypeInner::ValuePointer { .. } => {\n                        // When we have a chain of `Access` and `AccessIndex` expressions\n                        // operating on pointers, we want to generate a single\n                        // `OpAccessChain` instruction for the whole chain. Put off\n                        // generating any code for this until we find the `Expression`\n                        // that actually dereferences the pointer.\n                        0\n                    }\n                    _ if self.function.spilled_accesses.contains(base) => {\n                        // As far as Naga IR is concerned, this expression does not yield\n                        // a pointer (we just checked, above), but this backend spilled it\n                        // to a temporary variable, so SPIR-V thinks we're accessing it\n                        // via a pointer.\n\n                        // Since the base expression was spilled, mark this access to it\n                        // as spilled, too.\n                        self.function.spilled_accesses.insert(expr_handle);\n                        self.maybe_access_spilled_composite(expr_handle, block, result_type_id)?\n                    }\n                    crate::TypeInner::Vector { .. } => self.write_vector_access(\n                        result_type_id,\n                        base,\n                        None,\n                        GuardedIndex::Expression(index),\n                        block,\n                    )?,\n                    crate::TypeInner::Array { .. } | crate::TypeInner::Matrix { .. } => {\n                        // See if `index` is known at compile time.\n                        match GuardedIndex::from_expression(\n                            index,\n                            &self.ir_function.expressions,\n                            self.ir_module,\n                        ) {\n                            GuardedIndex::Known(value) => {\n                                // If `index` is known and in bounds, we can just use\n                                // `OpCompositeExtract`.\n                                //\n                                // At the moment, validation rejects programs if this\n                                // index is out of bounds, so we don't need bounds checks.\n                                // However, that rejection is incorrect, since WGSL says\n                                // that `let` bindings are not constant expressions\n                                // (#6396). So eventually we will need to emulate bounds\n                                // checks here.\n                                let id = self.gen_id();\n                                let base_id = self.cached[base];\n                                block.body.push(Instruction::composite_extract(\n                                    result_type_id,\n                                    id,\n                                    base_id,\n                                    &[value],\n                                ));\n                                id\n                            }\n                            GuardedIndex::Expression(_) => {\n                                // We are subscripting an array or matrix that is not\n                                // behind a pointer, using an index computed at runtime.\n                                // SPIR-V has no instructions that do this, so the best we\n                                // can do is spill the value to a new temporary variable,\n                                // at which point we can get a pointer to that and just\n                                // use `OpAccessChain` in the usual way.\n                                self.spill_to_internal_variable(base, block);\n\n                                // Since the base was spilled, mark this access to it as\n                                // spilled, too.\n                                self.function.spilled_accesses.insert(expr_handle);\n                                self.maybe_access_spilled_composite(\n                                    expr_handle,\n                                    block,\n                                    result_type_id,\n                                )?\n                            }\n                        }\n                    }\n                    crate::TypeInner::BindingArray {\n                        base: binding_type, ..\n                    } => {\n                        // Only binding arrays in the `Handle` address space will take\n                        // this path, since we handled the `Pointer` case above.\n                        let result_id = match self.write_access_chain(\n                            expr_handle,\n                            block,\n                            AccessTypeAdjustment::IntroducePointer(\n                                spirv::StorageClass::UniformConstant,\n                            ),\n                        )? {\n                            ExpressionPointer::Ready { pointer_id } => pointer_id,\n                            ExpressionPointer::Conditional { .. } => {\n                                return Err(Error::FeatureNotImplemented(\n                                    \"Texture array out-of-bounds handling\",\n                                ));\n                            }\n                        };\n\n                        let binding_type_id = self.get_handle_type_id(binding_type);\n\n                        let load_id = self.gen_id();\n                        block.body.push(Instruction::load(\n                            binding_type_id,\n                            load_id,\n                            result_id,\n                            None,\n                        ));\n\n                        // Subsequent image operations require the image/sampler to be decorated as NonUniform\n                        // if the image/sampler binding array was accessed with a non-uniform index\n                        // see VUID-RuntimeSpirv-NonUniform-06274\n                        if self.fun_info[index].uniformity.non_uniform_result.is_some() {\n                            self.writer\n                                .decorate_non_uniform_binding_array_access(load_id)?;\n                        }\n\n                        load_id\n                    }\n                    ref other => {\n                        log::error!(\n                            \"Unable to access base {:?} of type {:?}\",\n                            self.ir_function.expressions[base],\n                            other\n                        );\n                        return Err(Error::Validation(\n                            \"only vectors and arrays may be dynamically indexed by value\",\n                        ));\n                    }\n                }\n            }\n            crate::Expression::AccessIndex { base, index } => {\n                match *self.fun_info[base].ty.inner_with(&self.ir_module.types) {\n                    crate::TypeInner::Pointer { .. } | crate::TypeInner::ValuePointer { .. } => {\n                        // When we have a chain of `Access` and `AccessIndex` expressions\n                        // operating on pointers, we want to generate a single\n                        // `OpAccessChain` instruction for the whole chain. Put off\n                        // generating any code for this until we find the `Expression`\n                        // that actually dereferences the pointer.\n                        0\n                    }\n                    _ if self.function.spilled_accesses.contains(base) => {\n                        // As far as Naga IR is concerned, this expression does not yield\n                        // a pointer (we just checked, above), but this backend spilled it\n                        // to a temporary variable, so SPIR-V thinks we're accessing it\n                        // via a pointer.\n\n                        // Since the base expression was spilled, mark this access to it\n                        // as spilled, too.\n                        self.function.spilled_accesses.insert(expr_handle);\n                        self.maybe_access_spilled_composite(expr_handle, block, result_type_id)?\n                    }\n                    crate::TypeInner::Vector { .. }\n                    | crate::TypeInner::Matrix { .. }\n                    | crate::TypeInner::Array { .. }\n                    | crate::TypeInner::Struct { .. } => {\n                        // We never need bounds checks here: dynamically sized arrays can\n                        // only appear behind pointers, and are thus handled by the\n                        // `is_intermediate` case above. Everything else's size is\n                        // statically known and checked in validation.\n                        let id = self.gen_id();\n                        let base_id = self.cached[base];\n                        block.body.push(Instruction::composite_extract(\n                            result_type_id,\n                            id,\n                            base_id,\n                            &[index],\n                        ));\n                        id\n                    }\n                    crate::TypeInner::BindingArray {\n                        base: binding_type, ..\n                    } => {\n                        // Only binding arrays in the `Handle` address space will take\n                        // this path, since we handled the `Pointer` case above.\n                        let result_id = match self.write_access_chain(\n                            expr_handle,\n                            block,\n                            AccessTypeAdjustment::IntroducePointer(\n                                spirv::StorageClass::UniformConstant,\n                            ),\n                        )? {\n                            ExpressionPointer::Ready { pointer_id } => pointer_id,\n                            ExpressionPointer::Conditional { .. } => {\n                                return Err(Error::FeatureNotImplemented(\n                                    \"Texture array out-of-bounds handling\",\n                                ));\n                            }\n                        };\n\n                        let binding_type_id = self.get_handle_type_id(binding_type);\n\n                        let load_id = self.gen_id();\n                        block.body.push(Instruction::load(\n                            binding_type_id,\n                            load_id,\n                            result_id,\n                            None,\n                        ));\n\n                        load_id\n                    }\n                    ref other => {\n                        log::error!(\"Unable to access index of {other:?}\");\n                        return Err(Error::FeatureNotImplemented(\"access index for type\"));\n                    }\n                }\n            }\n            crate::Expression::GlobalVariable(handle) => {\n                self.writer.global_variables[handle].access_id\n            }\n            crate::Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                let vector_id = self.cached[vector];\n                self.temp_list.clear();\n                for &sc in pattern[..size as usize].iter() {\n                    self.temp_list.push(sc as Word);\n                }\n                let id = self.gen_id();\n                block.body.push(Instruction::vector_shuffle(\n                    result_type_id,\n                    id,\n                    vector_id,\n                    vector_id,\n                    &self.temp_list,\n                ));\n                id\n            }\n            crate::Expression::Unary { op, expr } => {\n                let id = self.gen_id();\n                let expr_id = self.cached[expr];\n                let expr_ty_inner = self.fun_info[expr].ty.inner_with(&self.ir_module.types);\n\n                let spirv_op = match op {\n                    crate::UnaryOperator::Negate => match expr_ty_inner.scalar_kind() {\n                        Some(crate::ScalarKind::Float) => spirv::Op::FNegate,\n                        Some(crate::ScalarKind::Sint) => spirv::Op::SNegate,\n                        _ => return Err(Error::Validation(\"Unexpected kind for negation\")),\n                    },\n                    crate::UnaryOperator::LogicalNot => spirv::Op::LogicalNot,\n                    crate::UnaryOperator::BitwiseNot => spirv::Op::Not,\n                };\n\n                block\n                    .body\n                    .push(Instruction::unary(spirv_op, result_type_id, id, expr_id));\n                id\n            }\n            crate::Expression::Binary { op, left, right } => {\n                let id = self.gen_id();\n                let left_id = self.cached[left];\n                let right_id = self.cached[right];\n                let left_type_id = self.get_expression_type_id(&self.fun_info[left].ty);\n                let right_type_id = self.get_expression_type_id(&self.fun_info[right].ty);\n\n                if let Some(function_id) =\n                    self.writer\n                        .wrapped_functions\n                        .get(&WrappedFunction::BinaryOp {\n                            op,\n                            left_type_id,\n                            right_type_id,\n                        })\n                {\n                    block.body.push(Instruction::function_call(\n                        result_type_id,\n                        id,\n                        *function_id,\n                        &[left_id, right_id],\n                    ));\n                } else {\n                    let left_ty_inner = self.fun_info[left].ty.inner_with(&self.ir_module.types);\n                    let right_ty_inner = self.fun_info[right].ty.inner_with(&self.ir_module.types);\n\n                    let left_dimension = get_dimension(left_ty_inner);\n                    let right_dimension = get_dimension(right_ty_inner);\n\n                    let mut reverse_operands = false;\n\n                    let spirv_op = match op {\n                        crate::BinaryOperator::Add => match *left_ty_inner {\n                            crate::TypeInner::Scalar(scalar)\n                            | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {\n                                crate::ScalarKind::Float => spirv::Op::FAdd,\n                                _ => spirv::Op::IAdd,\n                            },\n                            crate::TypeInner::Matrix {\n                                columns,\n                                rows,\n                                scalar,\n                            } => {\n                                //TODO: why not just rely on `Fadd` for matrices?\n                                self.write_matrix_matrix_column_op(\n                                    block,\n                                    id,\n                                    result_type_id,\n                                    left_id,\n                                    right_id,\n                                    columns,\n                                    rows,\n                                    scalar.width,\n                                    spirv::Op::FAdd,\n                                );\n\n                                self.cached[expr_handle] = id;\n                                return Ok(());\n                            }\n                            crate::TypeInner::CooperativeMatrix { .. } => spirv::Op::FAdd,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Subtract => match *left_ty_inner {\n                            crate::TypeInner::Scalar(scalar)\n                            | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {\n                                crate::ScalarKind::Float => spirv::Op::FSub,\n                                _ => spirv::Op::ISub,\n                            },\n                            crate::TypeInner::Matrix {\n                                columns,\n                                rows,\n                                scalar,\n                            } => {\n                                self.write_matrix_matrix_column_op(\n                                    block,\n                                    id,\n                                    result_type_id,\n                                    left_id,\n                                    right_id,\n                                    columns,\n                                    rows,\n                                    scalar.width,\n                                    spirv::Op::FSub,\n                                );\n\n                                self.cached[expr_handle] = id;\n                                return Ok(());\n                            }\n                            crate::TypeInner::CooperativeMatrix { .. } => spirv::Op::FSub,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Multiply => {\n                            match (left_dimension, right_dimension) {\n                                (Dimension::Scalar, Dimension::Vector) => {\n                                    self.write_vector_scalar_mult(\n                                        block,\n                                        id,\n                                        result_type_id,\n                                        right_id,\n                                        left_id,\n                                        right_ty_inner,\n                                    );\n\n                                    self.cached[expr_handle] = id;\n                                    return Ok(());\n                                }\n                                (Dimension::Vector, Dimension::Scalar) => {\n                                    self.write_vector_scalar_mult(\n                                        block,\n                                        id,\n                                        result_type_id,\n                                        left_id,\n                                        right_id,\n                                        left_ty_inner,\n                                    );\n\n                                    self.cached[expr_handle] = id;\n                                    return Ok(());\n                                }\n                                (Dimension::Vector, Dimension::Matrix) => {\n                                    spirv::Op::VectorTimesMatrix\n                                }\n                                (Dimension::Matrix, Dimension::Scalar)\n                                | (Dimension::CooperativeMatrix, Dimension::Scalar) => {\n                                    spirv::Op::MatrixTimesScalar\n                                }\n                                (Dimension::Scalar, Dimension::Matrix)\n                                | (Dimension::Scalar, Dimension::CooperativeMatrix) => {\n                                    reverse_operands = true;\n                                    spirv::Op::MatrixTimesScalar\n                                }\n                                (Dimension::Matrix, Dimension::Vector) => {\n                                    spirv::Op::MatrixTimesVector\n                                }\n                                (Dimension::Matrix, Dimension::Matrix) => {\n                                    spirv::Op::MatrixTimesMatrix\n                                }\n                                (Dimension::Vector, Dimension::Vector)\n                                | (Dimension::Scalar, Dimension::Scalar)\n                                    if left_ty_inner.scalar_kind()\n                                        == Some(crate::ScalarKind::Float) =>\n                                {\n                                    spirv::Op::FMul\n                                }\n                                (Dimension::Vector, Dimension::Vector)\n                                | (Dimension::Scalar, Dimension::Scalar) => spirv::Op::IMul,\n                                (Dimension::CooperativeMatrix, Dimension::CooperativeMatrix)\n                                //Note: technically can do `FMul` but IR doesn't have matrix per-component multiplication\n                                | (Dimension::CooperativeMatrix, _)\n                                | (_, Dimension::CooperativeMatrix) => {\n                                    unimplemented!()\n                                }\n                            }\n                        }\n                        crate::BinaryOperator::Divide => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::SDiv,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::UDiv,\n                            Some(crate::ScalarKind::Float) => spirv::Op::FDiv,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Modulo => match left_ty_inner.scalar_kind() {\n                            // TODO: handle undefined behavior\n                            // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798\n                            Some(crate::ScalarKind::Float) => spirv::Op::FRem,\n                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {\n                                unreachable!(\"Should have been handled by wrapped function\")\n                            }\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Equal => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {\n                                spirv::Op::IEqual\n                            }\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdEqual,\n                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalEqual,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::NotEqual => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {\n                                spirv::Op::INotEqual\n                            }\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdNotEqual,\n                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalNotEqual,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Less => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::SLessThan,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::ULessThan,\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThan,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::LessEqual => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::SLessThanEqual,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::ULessThanEqual,\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThanEqual,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::Greater => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThan,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThan,\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThan,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::GreaterEqual => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThanEqual,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThanEqual,\n                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThanEqual,\n                            _ => unimplemented!(),\n                        },\n                        crate::BinaryOperator::And => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalAnd,\n                            _ => spirv::Op::BitwiseAnd,\n                        },\n                        crate::BinaryOperator::ExclusiveOr => spirv::Op::BitwiseXor,\n                        crate::BinaryOperator::InclusiveOr => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalOr,\n                            _ => spirv::Op::BitwiseOr,\n                        },\n                        crate::BinaryOperator::LogicalAnd => spirv::Op::LogicalAnd,\n                        crate::BinaryOperator::LogicalOr => spirv::Op::LogicalOr,\n                        crate::BinaryOperator::ShiftLeft => spirv::Op::ShiftLeftLogical,\n                        crate::BinaryOperator::ShiftRight => match left_ty_inner.scalar_kind() {\n                            Some(crate::ScalarKind::Sint) => spirv::Op::ShiftRightArithmetic,\n                            Some(crate::ScalarKind::Uint) => spirv::Op::ShiftRightLogical,\n                            _ => unimplemented!(),\n                        },\n                    };\n\n                    block.body.push(Instruction::binary(\n                        spirv_op,\n                        result_type_id,\n                        id,\n                        if reverse_operands { right_id } else { left_id },\n                        if reverse_operands { left_id } else { right_id },\n                    ));\n                }\n                id\n            }\n            crate::Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                use crate::MathFunction as Mf;\n                enum MathOp {\n                    Ext(spirv::GlslStd450Op),\n                    Custom(Instruction),\n                }\n\n                let arg0_id = self.cached[arg];\n                let arg_ty = self.fun_info[arg].ty.inner_with(&self.ir_module.types);\n                let arg_scalar_kind = arg_ty.scalar_kind();\n                let arg1_id = match arg1 {\n                    Some(handle) => self.cached[handle],\n                    None => 0,\n                };\n                let arg2_id = match arg2 {\n                    Some(handle) => self.cached[handle],\n                    None => 0,\n                };\n                let arg3_id = match arg3 {\n                    Some(handle) => self.cached[handle],\n                    None => 0,\n                };\n\n                let id = self.gen_id();\n                let math_op = match fun {\n                    // comparison\n                    Mf::Abs => {\n                        match arg_scalar_kind {\n                            Some(crate::ScalarKind::Float) => {\n                                MathOp::Ext(spirv::GlslStd450Op::FAbs)\n                            }\n                            Some(crate::ScalarKind::Sint) => MathOp::Ext(spirv::GlslStd450Op::SAbs),\n                            Some(crate::ScalarKind::Uint) => {\n                                MathOp::Custom(Instruction::unary(\n                                    spirv::Op::CopyObject, // do nothing\n                                    result_type_id,\n                                    id,\n                                    arg0_id,\n                                ))\n                            }\n                            other => unimplemented!(\"Unexpected abs({:?})\", other),\n                        }\n                    }\n                    Mf::Min => MathOp::Ext(match arg_scalar_kind {\n                        Some(crate::ScalarKind::Float) => spirv::GlslStd450Op::FMin,\n                        Some(crate::ScalarKind::Sint) => spirv::GlslStd450Op::SMin,\n                        Some(crate::ScalarKind::Uint) => spirv::GlslStd450Op::UMin,\n                        other => unimplemented!(\"Unexpected min({:?})\", other),\n                    }),\n                    Mf::Max => MathOp::Ext(match arg_scalar_kind {\n                        Some(crate::ScalarKind::Float) => spirv::GlslStd450Op::FMax,\n                        Some(crate::ScalarKind::Sint) => spirv::GlslStd450Op::SMax,\n                        Some(crate::ScalarKind::Uint) => spirv::GlslStd450Op::UMax,\n                        other => unimplemented!(\"Unexpected max({:?})\", other),\n                    }),\n                    Mf::Clamp => match arg_scalar_kind {\n                        // Clamp is undefined if min > max. In practice this means it can use a median-of-three\n                        // instruction to determine the value. This is fine according to the WGSL spec for float\n                        // clamp, but integer clamp _must_ use min-max. As such we write out min/max.\n                        Some(crate::ScalarKind::Float) => MathOp::Ext(spirv::GlslStd450Op::FClamp),\n                        Some(_) => {\n                            let (min_op, max_op) = match arg_scalar_kind {\n                                Some(crate::ScalarKind::Sint) => {\n                                    (spirv::GlslStd450Op::SMin, spirv::GlslStd450Op::SMax)\n                                }\n                                Some(crate::ScalarKind::Uint) => {\n                                    (spirv::GlslStd450Op::UMin, spirv::GlslStd450Op::UMax)\n                                }\n                                _ => unreachable!(),\n                            };\n\n                            let max_id = self.gen_id();\n                            block.body.push(Instruction::ext_inst_gl_op(\n                                self.writer.gl450_ext_inst_id,\n                                max_op,\n                                result_type_id,\n                                max_id,\n                                &[arg0_id, arg1_id],\n                            ));\n\n                            MathOp::Custom(Instruction::ext_inst_gl_op(\n                                self.writer.gl450_ext_inst_id,\n                                min_op,\n                                result_type_id,\n                                id,\n                                &[max_id, arg2_id],\n                            ))\n                        }\n                        other => unimplemented!(\"Unexpected max({:?})\", other),\n                    },\n                    Mf::Saturate => {\n                        let (maybe_size, scalar) = match *arg_ty {\n                            crate::TypeInner::Vector { size, scalar } => (Some(size), scalar),\n                            crate::TypeInner::Scalar(scalar) => (None, scalar),\n                            ref other => unimplemented!(\"Unexpected saturate({:?})\", other),\n                        };\n                        let scalar = crate::Scalar::float(scalar.width);\n                        let mut arg1_id = self.writer.get_constant_scalar_with(0, scalar)?;\n                        let mut arg2_id = self.writer.get_constant_scalar_with(1, scalar)?;\n\n                        if let Some(size) = maybe_size {\n                            let ty =\n                                LocalType::Numeric(NumericType::Vector { size, scalar }).into();\n\n                            self.temp_list.clear();\n                            self.temp_list.resize(size as _, arg1_id);\n\n                            arg1_id = self.writer.get_constant_composite(ty, &self.temp_list);\n\n                            self.temp_list.fill(arg2_id);\n\n                            arg2_id = self.writer.get_constant_composite(ty, &self.temp_list);\n                        }\n\n                        MathOp::Custom(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::FClamp,\n                            result_type_id,\n                            id,\n                            &[arg0_id, arg1_id, arg2_id],\n                        ))\n                    }\n                    // trigonometry\n                    Mf::Sin => MathOp::Ext(spirv::GlslStd450Op::Sin),\n                    Mf::Sinh => MathOp::Ext(spirv::GlslStd450Op::Sinh),\n                    Mf::Asin => MathOp::Ext(spirv::GlslStd450Op::Asin),\n                    Mf::Cos => MathOp::Ext(spirv::GlslStd450Op::Cos),\n                    Mf::Cosh => MathOp::Ext(spirv::GlslStd450Op::Cosh),\n                    Mf::Acos => MathOp::Ext(spirv::GlslStd450Op::Acos),\n                    Mf::Tan => MathOp::Ext(spirv::GlslStd450Op::Tan),\n                    Mf::Tanh => MathOp::Ext(spirv::GlslStd450Op::Tanh),\n                    Mf::Atan => MathOp::Ext(spirv::GlslStd450Op::Atan),\n                    Mf::Atan2 => MathOp::Ext(spirv::GlslStd450Op::Atan2),\n                    Mf::Asinh => MathOp::Ext(spirv::GlslStd450Op::Asinh),\n                    Mf::Acosh => MathOp::Ext(spirv::GlslStd450Op::Acosh),\n                    Mf::Atanh => MathOp::Ext(spirv::GlslStd450Op::Atanh),\n                    Mf::Radians => MathOp::Ext(spirv::GlslStd450Op::Radians),\n                    Mf::Degrees => MathOp::Ext(spirv::GlslStd450Op::Degrees),\n                    // decomposition\n                    Mf::Ceil => MathOp::Ext(spirv::GlslStd450Op::Ceil),\n                    Mf::Round => MathOp::Ext(spirv::GlslStd450Op::RoundEven),\n                    Mf::Floor => MathOp::Ext(spirv::GlslStd450Op::Floor),\n                    Mf::Fract => MathOp::Ext(spirv::GlslStd450Op::Fract),\n                    Mf::Trunc => MathOp::Ext(spirv::GlslStd450Op::Trunc),\n                    Mf::Modf => MathOp::Ext(spirv::GlslStd450Op::ModfStruct),\n                    Mf::Frexp => MathOp::Ext(spirv::GlslStd450Op::FrexpStruct),\n                    Mf::Ldexp => MathOp::Ext(spirv::GlslStd450Op::Ldexp),\n                    // geometry\n                    Mf::Dot => match *self.fun_info[arg].ty.inner_with(&self.ir_module.types) {\n                        crate::TypeInner::Vector {\n                            scalar:\n                                crate::Scalar {\n                                    kind: crate::ScalarKind::Float,\n                                    ..\n                                },\n                            ..\n                        } => MathOp::Custom(Instruction::binary(\n                            spirv::Op::Dot,\n                            result_type_id,\n                            id,\n                            arg0_id,\n                            arg1_id,\n                        )),\n                        // TODO: consider using integer dot product if VK_KHR_shader_integer_dot_product is available\n                        crate::TypeInner::Vector { size, .. } => {\n                            self.write_dot_product(\n                                id,\n                                result_type_id,\n                                arg0_id,\n                                arg1_id,\n                                size as u32,\n                                block,\n                                |result_id, composite_id, index| {\n                                    Instruction::composite_extract(\n                                        result_type_id,\n                                        result_id,\n                                        composite_id,\n                                        &[index],\n                                    )\n                                },\n                            );\n                            self.cached[expr_handle] = id;\n                            return Ok(());\n                        }\n                        _ => unreachable!(\n                            \"Correct TypeInner for dot product should be already validated\"\n                        ),\n                    },\n                    fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed) => {\n                        if self\n                            .writer\n                            .require_all(&[\n                                spirv::Capability::DotProduct,\n                                spirv::Capability::DotProductInput4x8BitPacked,\n                            ])\n                            .is_ok()\n                        {\n                            // Write optimized code using `PackedVectorFormat4x8Bit`.\n                            if self.writer.lang_version() < (1, 6) {\n                                // SPIR-V 1.6 supports the required capabilities natively, so the extension\n                                // is only required for earlier versions. See right column of\n                                // <https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpSDot>.\n                                self.writer.use_extension(\"SPV_KHR_integer_dot_product\");\n                            }\n\n                            let op = match fun {\n                                Mf::Dot4I8Packed => spirv::Op::SDot,\n                                Mf::Dot4U8Packed => spirv::Op::UDot,\n                                _ => unreachable!(),\n                            };\n\n                            block.body.push(Instruction::ternary(\n                                op,\n                                result_type_id,\n                                id,\n                                arg0_id,\n                                arg1_id,\n                                spirv::PackedVectorFormat::PackedVectorFormat4x8Bit as Word,\n                            ));\n                        } else {\n                            // Fall back to a polyfill since `PackedVectorFormat4x8Bit` is not available.\n                            let (extract_op, arg0_id, arg1_id) = match fun {\n                                Mf::Dot4U8Packed => (spirv::Op::BitFieldUExtract, arg0_id, arg1_id),\n                                Mf::Dot4I8Packed => {\n                                    // Convert both packed arguments to signed integers so that we can apply the\n                                    // `BitFieldSExtract` operation on them in `write_dot_product` below.\n                                    let new_arg0_id = self.gen_id();\n                                    block.body.push(Instruction::unary(\n                                        spirv::Op::Bitcast,\n                                        result_type_id,\n                                        new_arg0_id,\n                                        arg0_id,\n                                    ));\n\n                                    let new_arg1_id = self.gen_id();\n                                    block.body.push(Instruction::unary(\n                                        spirv::Op::Bitcast,\n                                        result_type_id,\n                                        new_arg1_id,\n                                        arg1_id,\n                                    ));\n\n                                    (spirv::Op::BitFieldSExtract, new_arg0_id, new_arg1_id)\n                                }\n                                _ => unreachable!(),\n                            };\n\n                            let eight = self.writer.get_constant_scalar(crate::Literal::U32(8));\n\n                            const VEC_LENGTH: u8 = 4;\n                            let bit_shifts: [_; VEC_LENGTH as usize] =\n                                core::array::from_fn(|index| {\n                                    self.writer\n                                        .get_constant_scalar(crate::Literal::U32(index as u32 * 8))\n                                });\n\n                            self.write_dot_product(\n                                id,\n                                result_type_id,\n                                arg0_id,\n                                arg1_id,\n                                VEC_LENGTH as Word,\n                                block,\n                                |result_id, composite_id, index| {\n                                    Instruction::ternary(\n                                        extract_op,\n                                        result_type_id,\n                                        result_id,\n                                        composite_id,\n                                        bit_shifts[index as usize],\n                                        eight,\n                                    )\n                                },\n                            );\n                        }\n\n                        self.cached[expr_handle] = id;\n                        return Ok(());\n                    }\n                    Mf::Outer => MathOp::Custom(Instruction::binary(\n                        spirv::Op::OuterProduct,\n                        result_type_id,\n                        id,\n                        arg0_id,\n                        arg1_id,\n                    )),\n                    Mf::Cross => MathOp::Ext(spirv::GlslStd450Op::Cross),\n                    Mf::Distance => MathOp::Ext(spirv::GlslStd450Op::Distance),\n                    Mf::Length => MathOp::Ext(spirv::GlslStd450Op::Length),\n                    Mf::Normalize => MathOp::Ext(spirv::GlslStd450Op::Normalize),\n                    Mf::FaceForward => MathOp::Ext(spirv::GlslStd450Op::FaceForward),\n                    Mf::Reflect => MathOp::Ext(spirv::GlslStd450Op::Reflect),\n                    Mf::Refract => MathOp::Ext(spirv::GlslStd450Op::Refract),\n                    // exponent\n                    Mf::Exp => MathOp::Ext(spirv::GlslStd450Op::Exp),\n                    Mf::Exp2 => MathOp::Ext(spirv::GlslStd450Op::Exp2),\n                    Mf::Log => MathOp::Ext(spirv::GlslStd450Op::Log),\n                    Mf::Log2 => MathOp::Ext(spirv::GlslStd450Op::Log2),\n                    Mf::Pow => MathOp::Ext(spirv::GlslStd450Op::Pow),\n                    // computational\n                    Mf::Sign => MathOp::Ext(match arg_scalar_kind {\n                        Some(crate::ScalarKind::Float) => spirv::GlslStd450Op::FSign,\n                        Some(crate::ScalarKind::Sint) => spirv::GlslStd450Op::SSign,\n                        other => unimplemented!(\"Unexpected sign({:?})\", other),\n                    }),\n                    Mf::Fma => MathOp::Ext(spirv::GlslStd450Op::Fma),\n                    Mf::Mix => {\n                        let selector = arg2.unwrap();\n                        let selector_ty =\n                            self.fun_info[selector].ty.inner_with(&self.ir_module.types);\n                        match (arg_ty, selector_ty) {\n                            // if the selector is a scalar, we need to splat it\n                            (\n                                &crate::TypeInner::Vector { size, .. },\n                                &crate::TypeInner::Scalar(scalar),\n                            ) => {\n                                let selector_type_id =\n                                    self.get_numeric_type_id(NumericType::Vector { size, scalar });\n                                self.temp_list.clear();\n                                self.temp_list.resize(size as usize, arg2_id);\n\n                                let selector_id = self.gen_id();\n                                block.body.push(Instruction::composite_construct(\n                                    selector_type_id,\n                                    selector_id,\n                                    &self.temp_list,\n                                ));\n\n                                MathOp::Custom(Instruction::ext_inst_gl_op(\n                                    self.writer.gl450_ext_inst_id,\n                                    spirv::GlslStd450Op::FMix,\n                                    result_type_id,\n                                    id,\n                                    &[arg0_id, arg1_id, selector_id],\n                                ))\n                            }\n                            _ => MathOp::Ext(spirv::GlslStd450Op::FMix),\n                        }\n                    }\n                    Mf::Step => MathOp::Ext(spirv::GlslStd450Op::Step),\n                    Mf::SmoothStep => MathOp::Ext(spirv::GlslStd450Op::SmoothStep),\n                    Mf::Sqrt => MathOp::Ext(spirv::GlslStd450Op::Sqrt),\n                    Mf::InverseSqrt => MathOp::Ext(spirv::GlslStd450Op::InverseSqrt),\n                    Mf::Inverse => MathOp::Ext(spirv::GlslStd450Op::MatrixInverse),\n                    Mf::Transpose => MathOp::Custom(Instruction::unary(\n                        spirv::Op::Transpose,\n                        result_type_id,\n                        id,\n                        arg0_id,\n                    )),\n                    Mf::Determinant => MathOp::Ext(spirv::GlslStd450Op::Determinant),\n                    Mf::QuantizeToF16 => MathOp::Custom(Instruction::unary(\n                        spirv::Op::QuantizeToF16,\n                        result_type_id,\n                        id,\n                        arg0_id,\n                    )),\n                    Mf::ReverseBits => MathOp::Custom(Instruction::unary(\n                        spirv::Op::BitReverse,\n                        result_type_id,\n                        id,\n                        arg0_id,\n                    )),\n                    Mf::CountTrailingZeros => {\n                        let uint_id = match *arg_ty {\n                            crate::TypeInner::Vector { size, scalar } => {\n                                let ty =\n                                    LocalType::Numeric(NumericType::Vector { size, scalar }).into();\n\n                                self.temp_list.clear();\n                                self.temp_list.resize(\n                                    size as _,\n                                    self.writer\n                                        .get_constant_scalar_with(scalar.width * 8, scalar)?,\n                                );\n\n                                self.writer.get_constant_composite(ty, &self.temp_list)\n                            }\n                            crate::TypeInner::Scalar(scalar) => self\n                                .writer\n                                .get_constant_scalar_with(scalar.width * 8, scalar)?,\n                            _ => unreachable!(),\n                        };\n\n                        let lsb_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::FindILsb,\n                            result_type_id,\n                            lsb_id,\n                            &[arg0_id],\n                        ));\n\n                        MathOp::Custom(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::UMin,\n                            result_type_id,\n                            id,\n                            &[uint_id, lsb_id],\n                        ))\n                    }\n                    Mf::CountLeadingZeros => {\n                        let (int_type_id, int_id, width) = match *arg_ty {\n                            crate::TypeInner::Vector { size, scalar } => {\n                                let ty =\n                                    LocalType::Numeric(NumericType::Vector { size, scalar }).into();\n\n                                self.temp_list.clear();\n                                self.temp_list.resize(\n                                    size as _,\n                                    self.writer\n                                        .get_constant_scalar_with(scalar.width * 8 - 1, scalar)?,\n                                );\n\n                                (\n                                    self.get_type_id(ty),\n                                    self.writer.get_constant_composite(ty, &self.temp_list),\n                                    scalar.width,\n                                )\n                            }\n                            crate::TypeInner::Scalar(scalar) => (\n                                self.get_numeric_type_id(NumericType::Scalar(scalar)),\n                                self.writer\n                                    .get_constant_scalar_with(scalar.width * 8 - 1, scalar)?,\n                                scalar.width,\n                            ),\n                            _ => unreachable!(),\n                        };\n\n                        if width != 4 {\n                            unreachable!(\"This is validated out until a polyfill is implemented. https://github.com/gfx-rs/wgpu/issues/5276\");\n                        };\n\n                        let msb_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            if width != 4 {\n                                spirv::GlslStd450Op::FindILsb\n                            } else {\n                                spirv::GlslStd450Op::FindUMsb\n                            },\n                            int_type_id,\n                            msb_id,\n                            &[arg0_id],\n                        ));\n\n                        MathOp::Custom(Instruction::binary(\n                            spirv::Op::ISub,\n                            result_type_id,\n                            id,\n                            int_id,\n                            msb_id,\n                        ))\n                    }\n                    Mf::CountOneBits => MathOp::Custom(Instruction::unary(\n                        spirv::Op::BitCount,\n                        result_type_id,\n                        id,\n                        arg0_id,\n                    )),\n                    Mf::ExtractBits => {\n                        let op = match arg_scalar_kind {\n                            Some(crate::ScalarKind::Uint) => spirv::Op::BitFieldUExtract,\n                            Some(crate::ScalarKind::Sint) => spirv::Op::BitFieldSExtract,\n                            other => unimplemented!(\"Unexpected sign({:?})\", other),\n                        };\n\n                        // The behavior of ExtractBits is undefined when offset + count > bit_width. We need\n                        // to first sanitize the offset and count first. If we don't do this, AMD and Intel\n                        // will return out-of-spec values if the extracted range is not within the bit width.\n                        //\n                        // This encodes the exact formula specified by the wgsl spec:\n                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin\n                        //\n                        // w = sizeof(x) * 8\n                        // o = min(offset, w)\n                        // tmp = w - o\n                        // c = min(count, tmp)\n                        //\n                        // bitfieldExtract(x, o, c)\n\n                        let bit_width = arg_ty.scalar_width().unwrap() * 8;\n                        let width_constant = self\n                            .writer\n                            .get_constant_scalar(crate::Literal::U32(bit_width as u32));\n\n                        let u32_type =\n                            self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32));\n\n                        // o = min(offset, w)\n                        let offset_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::UMin,\n                            u32_type,\n                            offset_id,\n                            &[arg1_id, width_constant],\n                        ));\n\n                        // tmp = w - o\n                        let max_count_id = self.gen_id();\n                        block.body.push(Instruction::binary(\n                            spirv::Op::ISub,\n                            u32_type,\n                            max_count_id,\n                            width_constant,\n                            offset_id,\n                        ));\n\n                        // c = min(count, tmp)\n                        let count_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::UMin,\n                            u32_type,\n                            count_id,\n                            &[arg2_id, max_count_id],\n                        ));\n\n                        MathOp::Custom(Instruction::ternary(\n                            op,\n                            result_type_id,\n                            id,\n                            arg0_id,\n                            offset_id,\n                            count_id,\n                        ))\n                    }\n                    Mf::InsertBits => {\n                        // The behavior of InsertBits has the same undefined behavior as ExtractBits.\n\n                        let bit_width = arg_ty.scalar_width().unwrap() * 8;\n                        let width_constant = self\n                            .writer\n                            .get_constant_scalar(crate::Literal::U32(bit_width as u32));\n\n                        let u32_type =\n                            self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32));\n\n                        // o = min(offset, w)\n                        let offset_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::UMin,\n                            u32_type,\n                            offset_id,\n                            &[arg2_id, width_constant],\n                        ));\n\n                        // tmp = w - o\n                        let max_count_id = self.gen_id();\n                        block.body.push(Instruction::binary(\n                            spirv::Op::ISub,\n                            u32_type,\n                            max_count_id,\n                            width_constant,\n                            offset_id,\n                        ));\n\n                        // c = min(count, tmp)\n                        let count_id = self.gen_id();\n                        block.body.push(Instruction::ext_inst_gl_op(\n                            self.writer.gl450_ext_inst_id,\n                            spirv::GlslStd450Op::UMin,\n                            u32_type,\n                            count_id,\n                            &[arg3_id, max_count_id],\n                        ));\n\n                        MathOp::Custom(Instruction::quaternary(\n                            spirv::Op::BitFieldInsert,\n                            result_type_id,\n                            id,\n                            arg0_id,\n                            arg1_id,\n                            offset_id,\n                            count_id,\n                        ))\n                    }\n                    Mf::FirstTrailingBit => MathOp::Ext(spirv::GlslStd450Op::FindILsb),\n                    Mf::FirstLeadingBit => {\n                        if arg_ty.scalar_width() == Some(4) {\n                            let thing = match arg_scalar_kind {\n                                Some(crate::ScalarKind::Uint) => spirv::GlslStd450Op::FindUMsb,\n                                Some(crate::ScalarKind::Sint) => spirv::GlslStd450Op::FindSMsb,\n                                other => unimplemented!(\"Unexpected firstLeadingBit({:?})\", other),\n                            };\n                            MathOp::Ext(thing)\n                        } else {\n                            unreachable!(\"This is validated out until a polyfill is implemented. https://github.com/gfx-rs/wgpu/issues/5276\");\n                        }\n                    }\n                    Mf::Pack4x8unorm => MathOp::Ext(spirv::GlslStd450Op::PackUnorm4x8),\n                    Mf::Pack4x8snorm => MathOp::Ext(spirv::GlslStd450Op::PackSnorm4x8),\n                    Mf::Pack2x16float => MathOp::Ext(spirv::GlslStd450Op::PackHalf2x16),\n                    Mf::Pack2x16unorm => MathOp::Ext(spirv::GlslStd450Op::PackUnorm2x16),\n                    Mf::Pack2x16snorm => MathOp::Ext(spirv::GlslStd450Op::PackSnorm2x16),\n                    fun @ (Mf::Pack4xI8 | Mf::Pack4xU8 | Mf::Pack4xI8Clamp | Mf::Pack4xU8Clamp) => {\n                        let is_signed = matches!(fun, Mf::Pack4xI8 | Mf::Pack4xI8Clamp);\n                        let should_clamp = matches!(fun, Mf::Pack4xI8Clamp | Mf::Pack4xU8Clamp);\n\n                        let last_instruction =\n                            if self.writer.require_all(&[spirv::Capability::Int8]).is_ok() {\n                                self.write_pack4x8_optimized(\n                                    block,\n                                    result_type_id,\n                                    arg0_id,\n                                    id,\n                                    is_signed,\n                                    should_clamp,\n                                )\n                            } else {\n                                self.write_pack4x8_polyfill(\n                                    block,\n                                    result_type_id,\n                                    arg0_id,\n                                    id,\n                                    is_signed,\n                                    should_clamp,\n                                )\n                            };\n\n                        MathOp::Custom(last_instruction)\n                    }\n                    Mf::Unpack4x8unorm => MathOp::Ext(spirv::GlslStd450Op::UnpackUnorm4x8),\n                    Mf::Unpack4x8snorm => MathOp::Ext(spirv::GlslStd450Op::UnpackSnorm4x8),\n                    Mf::Unpack2x16float => MathOp::Ext(spirv::GlslStd450Op::UnpackHalf2x16),\n                    Mf::Unpack2x16unorm => MathOp::Ext(spirv::GlslStd450Op::UnpackUnorm2x16),\n                    Mf::Unpack2x16snorm => MathOp::Ext(spirv::GlslStd450Op::UnpackSnorm2x16),\n                    fun @ (Mf::Unpack4xI8 | Mf::Unpack4xU8) => {\n                        let is_signed = matches!(fun, Mf::Unpack4xI8);\n\n                        let last_instruction =\n                            if self.writer.require_all(&[spirv::Capability::Int8]).is_ok() {\n                                self.write_unpack4x8_optimized(\n                                    block,\n                                    result_type_id,\n                                    arg0_id,\n                                    id,\n                                    is_signed,\n                                )\n                            } else {\n                                self.write_unpack4x8_polyfill(\n                                    block,\n                                    result_type_id,\n                                    arg0_id,\n                                    id,\n                                    is_signed,\n                                )\n                            };\n\n                        MathOp::Custom(last_instruction)\n                    }\n                };\n\n                block.body.push(match math_op {\n                    MathOp::Ext(op) => Instruction::ext_inst_gl_op(\n                        self.writer.gl450_ext_inst_id,\n                        op,\n                        result_type_id,\n                        id,\n                        &[arg0_id, arg1_id, arg2_id, arg3_id][..fun.argument_count()],\n                    ),\n                    MathOp::Custom(inst) => inst,\n                });\n                id\n            }\n            crate::Expression::LocalVariable(variable) => {\n                if let Some(rq_tracker) = self\n                    .function\n                    .ray_query_initialization_tracker_variables\n                    .get(&variable)\n                {\n                    self.ray_query_tracker_expr.insert(\n                        expr_handle,\n                        super::RayQueryTrackers {\n                            initialized_tracker: rq_tracker.id,\n                            t_max_tracker: self\n                                .function\n                                .ray_query_t_max_tracker_variables\n                                .get(&variable)\n                                .expect(\"Both trackers are set at the same time.\")\n                                .id,\n                        },\n                    );\n                }\n                self.function.variables[&variable].id\n            }\n            crate::Expression::Load { pointer } => {\n                self.write_checked_load(pointer, block, AccessTypeAdjustment::None, result_type_id)?\n            }\n            crate::Expression::FunctionArgument(index) => self.function.parameter_id(index),\n            crate::Expression::CallResult(_)\n            | crate::Expression::AtomicResult { .. }\n            | crate::Expression::WorkGroupUniformLoadResult { .. }\n            | crate::Expression::RayQueryProceedResult\n            | crate::Expression::SubgroupBallotResult\n            | crate::Expression::SubgroupOperationResult { .. } => self.cached[expr_handle],\n            crate::Expression::As {\n                expr,\n                kind,\n                convert,\n            } => self.write_as_expression(expr, convert, kind, block, result_type_id)?,\n            crate::Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => self.write_image_load(\n                result_type_id,\n                image,\n                coordinate,\n                array_index,\n                level,\n                sample,\n                block,\n            )?,\n            crate::Expression::ImageSample {\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n            } => self.write_image_sample(\n                result_type_id,\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n                block,\n            )?,\n            crate::Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                let id = self.gen_id();\n                let mut condition_id = self.cached[condition];\n                let accept_id = self.cached[accept];\n                let reject_id = self.cached[reject];\n\n                let condition_ty = self.fun_info[condition]\n                    .ty\n                    .inner_with(&self.ir_module.types);\n                let object_ty = self.fun_info[accept].ty.inner_with(&self.ir_module.types);\n\n                if let (\n                    &crate::TypeInner::Scalar(\n                        condition_scalar @ crate::Scalar {\n                            kind: crate::ScalarKind::Bool,\n                            ..\n                        },\n                    ),\n                    &crate::TypeInner::Vector { size, .. },\n                ) = (condition_ty, object_ty)\n                {\n                    self.temp_list.clear();\n                    self.temp_list.resize(size as usize, condition_id);\n\n                    let bool_vector_type_id = self.get_numeric_type_id(NumericType::Vector {\n                        size,\n                        scalar: condition_scalar,\n                    });\n\n                    let id = self.gen_id();\n                    block.body.push(Instruction::composite_construct(\n                        bool_vector_type_id,\n                        id,\n                        &self.temp_list,\n                    ));\n                    condition_id = id\n                }\n\n                let instruction =\n                    Instruction::select(result_type_id, id, condition_id, accept_id, reject_id);\n                block.body.push(instruction);\n                id\n            }\n            crate::Expression::Derivative { axis, ctrl, expr } => {\n                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n                match ctrl {\n                    Ctrl::Coarse | Ctrl::Fine => {\n                        self.writer.require_any(\n                            \"DerivativeControl\",\n                            &[spirv::Capability::DerivativeControl],\n                        )?;\n                    }\n                    Ctrl::None => {}\n                }\n                let id = self.gen_id();\n                let expr_id = self.cached[expr];\n                let op = match (axis, ctrl) {\n                    (Axis::X, Ctrl::Coarse) => spirv::Op::DPdxCoarse,\n                    (Axis::X, Ctrl::Fine) => spirv::Op::DPdxFine,\n                    (Axis::X, Ctrl::None) => spirv::Op::DPdx,\n                    (Axis::Y, Ctrl::Coarse) => spirv::Op::DPdyCoarse,\n                    (Axis::Y, Ctrl::Fine) => spirv::Op::DPdyFine,\n                    (Axis::Y, Ctrl::None) => spirv::Op::DPdy,\n                    (Axis::Width, Ctrl::Coarse) => spirv::Op::FwidthCoarse,\n                    (Axis::Width, Ctrl::Fine) => spirv::Op::FwidthFine,\n                    (Axis::Width, Ctrl::None) => spirv::Op::Fwidth,\n                };\n                block\n                    .body\n                    .push(Instruction::derivative(op, result_type_id, id, expr_id));\n                id\n            }\n            crate::Expression::ImageQuery { image, query } => {\n                self.write_image_query(result_type_id, image, query, block)?\n            }\n            crate::Expression::Relational { fun, argument } => {\n                use crate::RelationalFunction as Rf;\n                let arg_id = self.cached[argument];\n                let op = match fun {\n                    Rf::All => spirv::Op::All,\n                    Rf::Any => spirv::Op::Any,\n                    Rf::IsNan => spirv::Op::IsNan,\n                    Rf::IsInf => spirv::Op::IsInf,\n                };\n                let id = self.gen_id();\n                block\n                    .body\n                    .push(Instruction::relational(op, result_type_id, id, arg_id));\n                id\n            }\n            crate::Expression::ArrayLength(expr) => self.write_runtime_array_length(expr, block)?,\n            crate::Expression::RayQueryGetIntersection { query, committed } => {\n                let query_id = self.cached[query];\n                let init_tracker_id = *self\n                    .ray_query_tracker_expr\n                    .get(&query)\n                    .expect(\"not a cached ray query\");\n                let func_id = self\n                    .writer\n                    .write_ray_query_get_intersection_function(committed, self.ir_module);\n                let ray_intersection = self.ir_module.special_types.ray_intersection.unwrap();\n                let intersection_type_id = self.get_handle_type_id(ray_intersection);\n                let id = self.gen_id();\n                block.body.push(Instruction::function_call(\n                    intersection_type_id,\n                    id,\n                    func_id,\n                    &[query_id, init_tracker_id.initialized_tracker],\n                ));\n                id\n            }\n            crate::Expression::RayQueryVertexPositions { query, committed } => {\n                self.writer.require_any(\n                    \"RayQueryVertexPositions\",\n                    &[spirv::Capability::RayQueryPositionFetchKHR],\n                )?;\n                self.write_ray_query_return_vertex_position(query, block, committed)\n            }\n            crate::Expression::CooperativeLoad { ref data, .. } => {\n                self.writer.require_any(\n                    \"CooperativeMatrix\",\n                    &[spirv::Capability::CooperativeMatrixKHR],\n                )?;\n                let layout = if data.row_major {\n                    spirv::CooperativeMatrixLayout::RowMajorKHR\n                } else {\n                    spirv::CooperativeMatrixLayout::ColumnMajorKHR\n                };\n                let layout_id = self.get_index_constant(layout as u32);\n                let stride_id = self.cached[data.stride];\n                match self.write_access_chain(data.pointer, block, AccessTypeAdjustment::None)? {\n                    ExpressionPointer::Ready { pointer_id } => {\n                        let id = self.gen_id();\n                        block.body.push(Instruction::coop_load(\n                            result_type_id,\n                            id,\n                            pointer_id,\n                            layout_id,\n                            stride_id,\n                        ));\n                        id\n                    }\n                    ExpressionPointer::Conditional { condition, access } => self\n                        .write_conditional_indexed_load(\n                            result_type_id,\n                            condition,\n                            block,\n                            |id_gen, block| {\n                                let pointer_id = access.result_id.unwrap();\n                                block.body.push(access);\n                                let id = id_gen.next();\n                                block.body.push(Instruction::coop_load(\n                                    result_type_id,\n                                    id,\n                                    pointer_id,\n                                    layout_id,\n                                    stride_id,\n                                ));\n                                id\n                            },\n                        ),\n                }\n            }\n            crate::Expression::CooperativeMultiplyAdd { a, b, c } => {\n                self.writer.require_any(\n                    \"CooperativeMatrix\",\n                    &[spirv::Capability::CooperativeMatrixKHR],\n                )?;\n                let a_id = self.cached[a];\n                let b_id = self.cached[b];\n                let c_id = self.cached[c];\n                let id = self.gen_id();\n                block.body.push(Instruction::coop_mul_add(\n                    result_type_id,\n                    id,\n                    a_id,\n                    b_id,\n                    c_id,\n                ));\n                id\n            }\n        };\n\n        self.cached[expr_handle] = id;\n        Ok(())\n    }\n\n    /// Helper which focuses on generating the `As` expressions and the various conversions\n    /// that need to happen because of that.\n    fn write_as_expression(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        convert: Option<u8>,\n        kind: crate::ScalarKind,\n\n        block: &mut Block,\n        result_type_id: u32,\n    ) -> Result<u32, Error> {\n        use crate::ScalarKind as Sk;\n        let expr_id = self.cached[expr];\n        let ty = self.fun_info[expr].ty.inner_with(&self.ir_module.types);\n\n        // Matrix casts needs special treatment in SPIR-V, as the cast functions\n        // can take vectors or scalars, but not matrices. In order to cast a matrix\n        // we need to cast each column of the matrix individually and construct a new\n        // matrix from the converted columns.\n        if let crate::TypeInner::Matrix {\n            columns,\n            rows,\n            scalar,\n        } = *ty\n        {\n            let Some(convert) = convert else {\n                // No conversion needs to be done, passes through.\n                return Ok(expr_id);\n            };\n\n            if convert == scalar.width {\n                // No conversion needs to be done, passes through.\n                return Ok(expr_id);\n            }\n\n            if kind != Sk::Float {\n                // Only float conversions are supported for matrices.\n                return Err(Error::Validation(\"Matrices must be floats\"));\n            }\n\n            // Type of each extracted column\n            let column_src_ty =\n                self.get_type_id(LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                    size: rows,\n                    scalar,\n                })));\n\n            // Type of the column after conversion\n            let column_dst_ty =\n                self.get_type_id(LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                    size: rows,\n                    scalar: crate::Scalar {\n                        kind,\n                        width: convert,\n                    },\n                })));\n\n            let mut components = ArrayVec::<Word, 4>::new();\n\n            for column in 0..columns as usize {\n                let column_id = self.gen_id();\n                block.body.push(Instruction::composite_extract(\n                    column_src_ty,\n                    column_id,\n                    expr_id,\n                    &[column as u32],\n                ));\n\n                let column_conv_id = self.gen_id();\n                block.body.push(Instruction::unary(\n                    spirv::Op::FConvert,\n                    column_dst_ty,\n                    column_conv_id,\n                    column_id,\n                ));\n\n                components.push(column_conv_id);\n            }\n\n            let construct_id = self.gen_id();\n\n            block.body.push(Instruction::composite_construct(\n                result_type_id,\n                construct_id,\n                &components,\n            ));\n\n            return Ok(construct_id);\n        }\n\n        let (src_scalar, src_size) = match *ty {\n            crate::TypeInner::Scalar(scalar) => (scalar, None),\n            crate::TypeInner::Vector { scalar, size } => (scalar, Some(size)),\n            ref other => {\n                log::error!(\"As source {other:?}\");\n                return Err(Error::Validation(\"Unexpected Expression::As source\"));\n            }\n        };\n\n        enum Cast {\n            Identity(Word),\n            Unary(spirv::Op, Word),\n            Binary(spirv::Op, Word, Word),\n            Ternary(spirv::Op, Word, Word, Word),\n        }\n        let cast = match (src_scalar.kind, kind, convert) {\n            // Filter out identity casts. Some Adreno drivers are\n            // confused by no-op OpBitCast instructions.\n            (src_kind, kind, convert)\n                if src_kind == kind\n                    && convert.filter(|&width| width != src_scalar.width).is_none() =>\n            {\n                Cast::Identity(expr_id)\n            }\n            (Sk::Bool, Sk::Bool, _) => Cast::Unary(spirv::Op::CopyObject, expr_id),\n            (_, _, None) => Cast::Unary(spirv::Op::Bitcast, expr_id),\n            // casting to a bool - generate `OpXxxNotEqual`\n            (_, Sk::Bool, Some(_)) => {\n                let op = match src_scalar.kind {\n                    Sk::Sint | Sk::Uint => spirv::Op::INotEqual,\n                    Sk::Float => spirv::Op::FUnordNotEqual,\n                    Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => unreachable!(),\n                };\n                let zero_scalar_id = self.writer.get_constant_scalar_with(0, src_scalar)?;\n                let zero_id = match src_size {\n                    Some(size) => {\n                        let ty = LocalType::Numeric(NumericType::Vector {\n                            size,\n                            scalar: src_scalar,\n                        })\n                        .into();\n\n                        self.temp_list.clear();\n                        self.temp_list.resize(size as _, zero_scalar_id);\n\n                        self.writer.get_constant_composite(ty, &self.temp_list)\n                    }\n                    None => zero_scalar_id,\n                };\n\n                Cast::Binary(op, expr_id, zero_id)\n            }\n            // casting from a bool - generate `OpSelect`\n            (Sk::Bool, _, Some(dst_width)) => {\n                let dst_scalar = crate::Scalar {\n                    kind,\n                    width: dst_width,\n                };\n                let zero_scalar_id = self.writer.get_constant_scalar_with(0, dst_scalar)?;\n                let one_scalar_id = self.writer.get_constant_scalar_with(1, dst_scalar)?;\n                let (accept_id, reject_id) = match src_size {\n                    Some(size) => {\n                        let ty = LocalType::Numeric(NumericType::Vector {\n                            size,\n                            scalar: dst_scalar,\n                        })\n                        .into();\n\n                        self.temp_list.clear();\n                        self.temp_list.resize(size as _, zero_scalar_id);\n\n                        let vec0_id = self.writer.get_constant_composite(ty, &self.temp_list);\n\n                        self.temp_list.fill(one_scalar_id);\n\n                        let vec1_id = self.writer.get_constant_composite(ty, &self.temp_list);\n\n                        (vec1_id, vec0_id)\n                    }\n                    None => (one_scalar_id, zero_scalar_id),\n                };\n\n                Cast::Ternary(spirv::Op::Select, expr_id, accept_id, reject_id)\n            }\n            // Avoid undefined behaviour when casting from a float to integer\n            // when the value is out of range for the target type. Additionally\n            // ensure we clamp to the correct value as per the WGSL spec.\n            //\n            // https://www.w3.org/TR/WGSL/#floating-point-conversion:\n            // * If X is exactly representable in the target type T, then the\n            //   result is that value.\n            // * Otherwise, the result is the value in T closest to\n            //   truncate(X) and also exactly representable in the original\n            //   floating point type.\n            (Sk::Float, Sk::Sint | Sk::Uint, Some(width)) => {\n                let dst_scalar = crate::Scalar { kind, width };\n                let (min, max) =\n                    crate::proc::min_max_float_representable_by(src_scalar, dst_scalar);\n                let expr_type_id = self.get_expression_type_id(&self.fun_info[expr].ty);\n\n                let maybe_splat_const = |writer: &mut Writer, const_id| match src_size {\n                    None => const_id,\n                    Some(size) => {\n                        let constituent_ids = [const_id; crate::VectorSize::MAX];\n                        writer.get_constant_composite(\n                            LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                                size,\n                                scalar: src_scalar,\n                            })),\n                            &constituent_ids[..size as usize],\n                        )\n                    }\n                };\n                let min_const_id = self.writer.get_constant_scalar(min);\n                let min_const_id = maybe_splat_const(self.writer, min_const_id);\n                let max_const_id = self.writer.get_constant_scalar(max);\n                let max_const_id = maybe_splat_const(self.writer, max_const_id);\n\n                let clamp_id = self.gen_id();\n                block.body.push(Instruction::ext_inst_gl_op(\n                    self.writer.gl450_ext_inst_id,\n                    spirv::GlslStd450Op::FClamp,\n                    expr_type_id,\n                    clamp_id,\n                    &[expr_id, min_const_id, max_const_id],\n                ));\n\n                let op = match dst_scalar.kind {\n                    crate::ScalarKind::Sint => spirv::Op::ConvertFToS,\n                    crate::ScalarKind::Uint => spirv::Op::ConvertFToU,\n                    _ => unreachable!(),\n                };\n                Cast::Unary(op, clamp_id)\n            }\n            (Sk::Float, Sk::Float, Some(dst_width)) if src_scalar.width != dst_width => {\n                Cast::Unary(spirv::Op::FConvert, expr_id)\n            }\n            (Sk::Sint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertSToF, expr_id),\n            (Sk::Sint, Sk::Sint, Some(dst_width)) if src_scalar.width != dst_width => {\n                Cast::Unary(spirv::Op::SConvert, expr_id)\n            }\n            (Sk::Uint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertUToF, expr_id),\n            (Sk::Uint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => {\n                Cast::Unary(spirv::Op::UConvert, expr_id)\n            }\n            (Sk::Uint, Sk::Sint, Some(dst_width)) if src_scalar.width != dst_width => {\n                Cast::Unary(spirv::Op::SConvert, expr_id)\n            }\n            (Sk::Sint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => {\n                Cast::Unary(spirv::Op::UConvert, expr_id)\n            }\n            // We assume it's either an identity cast, or int-uint.\n            _ => Cast::Unary(spirv::Op::Bitcast, expr_id),\n        };\n        Ok(match cast {\n            Cast::Identity(expr) => expr,\n            Cast::Unary(op, op1) => {\n                let id = self.gen_id();\n                block\n                    .body\n                    .push(Instruction::unary(op, result_type_id, id, op1));\n                id\n            }\n            Cast::Binary(op, op1, op2) => {\n                let id = self.gen_id();\n                block\n                    .body\n                    .push(Instruction::binary(op, result_type_id, id, op1, op2));\n                id\n            }\n            Cast::Ternary(op, op1, op2, op3) => {\n                let id = self.gen_id();\n                block\n                    .body\n                    .push(Instruction::ternary(op, result_type_id, id, op1, op2, op3));\n                id\n            }\n        })\n    }\n\n    /// Build an `OpAccessChain` instruction.\n    ///\n    /// Emit any needed bounds-checking expressions to `block`.\n    ///\n    /// Give the `OpAccessChain` a result type based on `expr_handle`, adjusted\n    /// according to `type_adjustment`; see the documentation for\n    /// [`AccessTypeAdjustment`] for details.\n    ///\n    /// On success, the return value is an [`ExpressionPointer`] value; see the\n    /// documentation for that type.\n    fn write_access_chain(\n        &mut self,\n        mut expr_handle: Handle<crate::Expression>,\n        block: &mut Block,\n        type_adjustment: AccessTypeAdjustment,\n    ) -> Result<ExpressionPointer, Error> {\n        let result_type_id = {\n            let resolution = &self.fun_info[expr_handle].ty;\n            match type_adjustment {\n                AccessTypeAdjustment::None => self.writer.get_expression_type_id(resolution),\n                AccessTypeAdjustment::IntroducePointer(class) => {\n                    self.writer.get_resolution_pointer_id(resolution, class)\n                }\n                AccessTypeAdjustment::UseStd140CompatType => {\n                    match *resolution.inner_with(&self.ir_module.types) {\n                        crate::TypeInner::Pointer {\n                            base,\n                            space: space @ crate::AddressSpace::Uniform,\n                        } => self.writer.get_pointer_type_id(\n                            self.writer.std140_compat_uniform_types[&base].type_id,\n                            map_storage_class(space),\n                        ),\n                        _ => unreachable!(\n                            \"`UseStd140CompatType` must only be used with uniform pointer types\"\n                        ),\n                    }\n                }\n            }\n        };\n\n        // The id of the boolean `and` of all dynamic bounds checks up to this point.\n        //\n        // See `extend_bounds_check_condition_chain` for a full explanation.\n        let mut accumulated_checks = None;\n\n        // Is true if we are accessing into a binding array with a non-uniform index.\n        let mut is_non_uniform_binding_array = false;\n\n        // The index value if the previously encountered expression was an\n        // `AccessIndex` of a matrix which has been decomposed into individual\n        // column vectors directly in the containing struct. The subsequent\n        // iteration will append the correct index to the list for accessing\n        // said column from the containing struct.\n        let mut prev_decomposed_matrix_index = None;\n\n        self.temp_list.clear();\n        let root_id = loop {\n            // If `expr_handle` was spilled, then the temporary variable has exactly\n            // the value we want to start from.\n            if let Some(spilled) = self.function.spilled_composites.get(&expr_handle) {\n                // The root id of the `OpAccessChain` instruction is the temporary\n                // variable we spilled the composite to.\n                break spilled.id;\n            }\n\n            expr_handle = match self.ir_function.expressions[expr_handle] {\n                crate::Expression::Access { base, index } => {\n                    is_non_uniform_binding_array |=\n                        self.is_nonuniform_binding_array_access(base, index);\n\n                    let index = GuardedIndex::Expression(index);\n                    let index_id =\n                        self.write_access_chain_index(base, index, &mut accumulated_checks, block)?;\n                    self.temp_list.push(index_id);\n\n                    base\n                }\n                crate::Expression::AccessIndex { base, index } => {\n                    // Decide whether we're indexing a struct (bounds checks\n                    // forbidden) or anything else (bounds checks required).\n                    let mut base_ty = self.fun_info[base].ty.inner_with(&self.ir_module.types);\n                    let mut base_ty_handle = self.fun_info[base].ty.handle();\n                    let mut pointer_space = None;\n                    if let crate::TypeInner::Pointer { base, space } = *base_ty {\n                        base_ty = &self.ir_module.types[base].inner;\n                        base_ty_handle = Some(base);\n                        pointer_space = Some(space);\n                    }\n                    match *base_ty {\n                        // When indexing a struct bounds checks are forbidden. If accessing the\n                        // struct through a uniform address space pointer, where the struct has\n                        // been declared with an alternative std140 compatible layout, we must use\n                        // the remapped member index. Additionally if the previous iteration was\n                        // accessing a column of a matrix member which has been decomposed directly\n                        // into the struct, we must ensure we access the correct column.\n                        crate::TypeInner::Struct { .. } => {\n                            let index = match base_ty_handle.and_then(|handle| {\n                                self.writer.std140_compat_uniform_types.get(&handle)\n                            }) {\n                                Some(std140_type_info)\n                                    if pointer_space == Some(crate::AddressSpace::Uniform) =>\n                                {\n                                    std140_type_info.member_indices[index as usize]\n                                        + prev_decomposed_matrix_index.take().unwrap_or(0)\n                                }\n                                _ => index,\n                            };\n                            let index_id = self.get_index_constant(index);\n                            self.temp_list.push(index_id);\n                        }\n                        // Bounds checks are not required when indexing a matrix. If indexing a\n                        // two-row matrix contained within a struct through a uniform address space\n                        // pointer then the matrix' columns will have been decomposed directly into\n                        // the containing struct. We skip adding an index to the list on this\n                        // iteration and instead adjust the index on the next iteration when\n                        // accessing the struct member.\n                        _ if is_uniform_matcx2_struct_member_access(\n                            self.ir_function,\n                            self.fun_info,\n                            self.ir_module,\n                            base,\n                        ) =>\n                        {\n                            assert!(prev_decomposed_matrix_index.is_none());\n                            prev_decomposed_matrix_index = Some(index);\n                        }\n                        _ => {\n                            // `index` is constant, so this can't possibly require\n                            // setting `is_nonuniform_binding_array_access`.\n\n                            // Even though the index value is statically known, `base`\n                            // may be a runtime-sized array, so we still need to go\n                            // through the bounds check process.\n                            let index_id = self.write_access_chain_index(\n                                base,\n                                GuardedIndex::Known(index),\n                                &mut accumulated_checks,\n                                block,\n                            )?;\n                            self.temp_list.push(index_id);\n                        }\n                    }\n                    base\n                }\n                crate::Expression::GlobalVariable(handle) => {\n                    let gv = &self.writer.global_variables[handle];\n                    break gv.access_id;\n                }\n                crate::Expression::LocalVariable(variable) => {\n                    let local_var = &self.function.variables[&variable];\n                    break local_var.id;\n                }\n                crate::Expression::FunctionArgument(index) => {\n                    break self.function.parameter_id(index);\n                }\n                ref other => unimplemented!(\"Unexpected pointer expression {:?}\", other),\n            }\n        };\n\n        let (pointer_id, expr_pointer) = if self.temp_list.is_empty() {\n            (\n                root_id,\n                ExpressionPointer::Ready {\n                    pointer_id: root_id,\n                },\n            )\n        } else {\n            self.temp_list.reverse();\n            let pointer_id = self.gen_id();\n            let access =\n                Instruction::access_chain(result_type_id, pointer_id, root_id, &self.temp_list);\n\n            // If we generated some bounds checks, we need to leave it to our\n            // caller to generate the branch, the access, the load or store, and\n            // the zero value (for loads). Otherwise, we can emit the access\n            // ourselves, and just hand them the id of the pointer.\n            let expr_pointer = match accumulated_checks {\n                Some(condition) => ExpressionPointer::Conditional { condition, access },\n                None => {\n                    block.body.push(access);\n                    ExpressionPointer::Ready { pointer_id }\n                }\n            };\n            (pointer_id, expr_pointer)\n        };\n        // Subsequent load, store and atomic operations require the pointer to be decorated as NonUniform\n        // if the binding array was accessed with a non-uniform index\n        // see VUID-RuntimeSpirv-NonUniform-06274\n        if is_non_uniform_binding_array {\n            self.writer\n                .decorate_non_uniform_binding_array_access(pointer_id)?;\n        }\n\n        Ok(expr_pointer)\n    }\n\n    fn is_nonuniform_binding_array_access(\n        &mut self,\n        base: Handle<crate::Expression>,\n        index: Handle<crate::Expression>,\n    ) -> bool {\n        let crate::Expression::GlobalVariable(var_handle) = self.ir_function.expressions[base]\n        else {\n            return false;\n        };\n\n        // The access chain needs to be decorated as NonUniform\n        // see VUID-RuntimeSpirv-NonUniform-06274\n        let gvar = &self.ir_module.global_variables[var_handle];\n        let crate::TypeInner::BindingArray { .. } = self.ir_module.types[gvar.ty].inner else {\n            return false;\n        };\n\n        self.fun_info[index].uniformity.non_uniform_result.is_some()\n    }\n\n    /// Compute a single index operand to an `OpAccessChain` instruction.\n    ///\n    /// Given that we are indexing `base` with `index`, apply the appropriate\n    /// bounds check policies, emitting code to `block` to clamp `index` or\n    /// determine whether it's in bounds. Return the SPIR-V instruction id of\n    /// the index value we should actually use.\n    ///\n    /// Extend `accumulated_checks` to include the results of any needed bounds\n    /// checks. See [`BlockContext::extend_bounds_check_condition_chain`].\n    fn write_access_chain_index(\n        &mut self,\n        base: Handle<crate::Expression>,\n        index: GuardedIndex,\n        accumulated_checks: &mut Option<Word>,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        match self.write_bounds_check(base, index, block)? {\n            BoundsCheckResult::KnownInBounds(known_index) => {\n                // Even if the index is known, `OpAccessChain`\n                // requires expression operands, not literals.\n                let scalar = crate::Literal::U32(known_index);\n                Ok(self.writer.get_constant_scalar(scalar))\n            }\n            BoundsCheckResult::Computed(computed_index_id) => Ok(computed_index_id),\n            BoundsCheckResult::Conditional {\n                condition_id: condition,\n                index_id: index,\n            } => {\n                self.extend_bounds_check_condition_chain(accumulated_checks, condition, block);\n\n                // Use the index from the `Access` expression unchanged.\n                Ok(index)\n            }\n        }\n    }\n\n    /// Add a condition to a chain of bounds checks.\n    ///\n    /// As we build an `OpAccessChain` instruction govered by\n    /// [`BoundsCheckPolicy::ReadZeroSkipWrite`], we accumulate a chain of\n    /// dynamic bounds checks, one for each index in the chain, which must all\n    /// be true for that `OpAccessChain`'s execution to be well-defined. This\n    /// function adds the boolean instruction id `comparison_id` to `chain`.\n    ///\n    /// If `chain` is `None`, that means there are no bounds checks in the chain\n    /// yet. If chain is `Some(id)`, then `id` is the conjunction of all the\n    /// bounds checks in the chain.\n    ///\n    /// When we have multiple bounds checks, we combine them with\n    /// `OpLogicalAnd`, not a short-circuit branch. This means we might do\n    /// comparisons we don't need to, but we expect these checks to almost\n    /// always succeed, and keeping branches to a minimum is essential.\n    ///\n    /// [`BoundsCheckPolicy::ReadZeroSkipWrite`]: crate::proc::BoundsCheckPolicy\n    fn extend_bounds_check_condition_chain(\n        &mut self,\n        chain: &mut Option<Word>,\n        comparison_id: Word,\n        block: &mut Block,\n    ) {\n        match *chain {\n            Some(ref mut prior_checks) => {\n                let combined = self.gen_id();\n                block.body.push(Instruction::binary(\n                    spirv::Op::LogicalAnd,\n                    self.writer.get_bool_type_id(),\n                    combined,\n                    *prior_checks,\n                    comparison_id,\n                ));\n                *prior_checks = combined;\n            }\n            None => {\n                // Start a fresh chain of checks.\n                *chain = Some(comparison_id);\n            }\n        }\n    }\n\n    fn write_checked_load(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        block: &mut Block,\n        access_type_adjustment: AccessTypeAdjustment,\n        result_type_id: Word,\n    ) -> Result<Word, Error> {\n        if let Some(result_id) = self.maybe_write_uniform_matcx2_dynamic_access(pointer, block)? {\n            Ok(result_id)\n        } else if let Some(result_id) =\n            self.maybe_write_load_uniform_matcx2_struct_member(pointer, block)?\n        {\n            Ok(result_id)\n        } else {\n            // If `pointer` refers to a uniform address space pointer to a type\n            // which was declared using a std140 compatible type variant (i.e.\n            // is a two-row matrix, or a struct or array containing such a\n            // matrix) we must ensure the access chain and the type of the load\n            // instruction use the std140 compatible type variant.\n            struct WrappedLoad {\n                access_type_adjustment: AccessTypeAdjustment,\n                r#type: Handle<crate::Type>,\n            }\n            let mut wrapped_load = None;\n            if let crate::TypeInner::Pointer {\n                base: pointer_base_type,\n                space: crate::AddressSpace::Uniform,\n            } = *self.fun_info[pointer].ty.inner_with(&self.ir_module.types)\n            {\n                if self\n                    .writer\n                    .std140_compat_uniform_types\n                    .contains_key(&pointer_base_type)\n                {\n                    wrapped_load = Some(WrappedLoad {\n                        access_type_adjustment: AccessTypeAdjustment::UseStd140CompatType,\n                        r#type: pointer_base_type,\n                    });\n                };\n            };\n\n            let (load_type_id, access_type_adjustment) = match wrapped_load {\n                Some(ref wrapped_load) => (\n                    self.writer.std140_compat_uniform_types[&wrapped_load.r#type].type_id,\n                    wrapped_load.access_type_adjustment,\n                ),\n                None => (result_type_id, access_type_adjustment),\n            };\n\n            let load_id = match self.write_access_chain(pointer, block, access_type_adjustment)? {\n                ExpressionPointer::Ready { pointer_id } => {\n                    let id = self.gen_id();\n                    let atomic_space =\n                        match *self.fun_info[pointer].ty.inner_with(&self.ir_module.types) {\n                            crate::TypeInner::Pointer { base, space } => {\n                                match self.ir_module.types[base].inner {\n                                    crate::TypeInner::Atomic { .. } => Some(space),\n                                    _ => None,\n                                }\n                            }\n                            _ => None,\n                        };\n                    let instruction = if let Some(space) = atomic_space {\n                        let (semantics, scope) = space.to_spirv_semantics_and_scope();\n                        let scope_constant_id = self.get_scope_constant(scope as u32);\n                        let semantics_id = self.get_index_constant(semantics.bits());\n                        Instruction::atomic_load(\n                            result_type_id,\n                            id,\n                            pointer_id,\n                            scope_constant_id,\n                            semantics_id,\n                        )\n                    } else {\n                        Instruction::load(load_type_id, id, pointer_id, None)\n                    };\n                    block.body.push(instruction);\n                    id\n                }\n                ExpressionPointer::Conditional { condition, access } => {\n                    //TODO: support atomics?\n                    self.write_conditional_indexed_load(\n                        load_type_id,\n                        condition,\n                        block,\n                        move |id_gen, block| {\n                            // The in-bounds path. Perform the access and the load.\n                            let pointer_id = access.result_id.unwrap();\n                            let value_id = id_gen.next();\n                            block.body.push(access);\n                            block.body.push(Instruction::load(\n                                load_type_id,\n                                value_id,\n                                pointer_id,\n                                None,\n                            ));\n                            value_id\n                        },\n                    )\n                }\n            };\n\n            match wrapped_load {\n                Some(ref wrapped_load) => {\n                    // If we loaded a std140 compat type then we must call the\n                    // function to convert the loaded value to the regular type.\n                    let result_id = self.gen_id();\n                    let function_id = self.writer.wrapped_functions\n                        [&WrappedFunction::ConvertFromStd140CompatType {\n                            r#type: wrapped_load.r#type,\n                        }];\n                    block.body.push(Instruction::function_call(\n                        result_type_id,\n                        result_id,\n                        function_id,\n                        &[load_id],\n                    ));\n                    Ok(result_id)\n                }\n                None => Ok(load_id),\n            }\n        }\n    }\n\n    fn spill_to_internal_variable(&mut self, base: Handle<crate::Expression>, block: &mut Block) {\n        use indexmap::map::Entry;\n\n        // Make sure we have an internal variable to spill `base` to.\n        let spill_variable_id = match self.function.spilled_composites.entry(base) {\n            Entry::Occupied(preexisting) => preexisting.get().id,\n            Entry::Vacant(vacant) => {\n                // Generate a new internal variable of the appropriate\n                // type for `base`.\n                let pointer_type_id = self.writer.get_resolution_pointer_id(\n                    &self.fun_info[base].ty,\n                    spirv::StorageClass::Function,\n                );\n                let id = self.writer.id_gen.next();\n                vacant.insert(super::LocalVariable {\n                    id,\n                    instruction: Instruction::variable(\n                        pointer_type_id,\n                        id,\n                        spirv::StorageClass::Function,\n                        None,\n                    ),\n                });\n                id\n            }\n        };\n\n        // Perform the store even if we already had a spill variable for `base`.\n        // Consider this code:\n        //\n        // var x = ...;\n        // var y = ...;\n        // var z = ...;\n        // for (i = 0; i<2; i++) {\n        //     let a = array(i, i, i);\n        //     if (i == 0) {\n        //         x += a[y];\n        //     } else [\n        //         x += a[z];\n        //     }\n        // }\n        //\n        // The value of `a` needs to be spilled so we can subscript it with `y` and `z`.\n        //\n        // When we generate SPIR-V for `a[y]`, we will create the spill\n        // variable, and store `a`'s value in it.\n        //\n        // When we generate SPIR-V for `a[z]`, we will notice that the spill\n        // variable for `a` has already been declared, but it is still essential\n        // that we store `a` into it, so that `a[z]` sees this iteration's value\n        // of `a`.\n        let base_id = self.cached[base];\n        block\n            .body\n            .push(Instruction::store(spill_variable_id, base_id, None));\n    }\n\n    /// Generate an access to a spilled temporary, if necessary.\n    ///\n    /// Given `access`, an [`Access`] or [`AccessIndex`] expression that refers\n    /// to a component of a composite value that has been spilled to a temporary\n    /// variable, determine whether other expressions are going to use\n    /// `access`'s value:\n    ///\n    /// - If so, perform the access and cache that as the value of `access`.\n    ///\n    /// - Otherwise, generate no code and cache no value for `access`.\n    ///\n    /// Return `Ok(0)` if no value was fetched, or `Ok(id)` if we loaded it into\n    /// the instruction given by `id`.\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    fn maybe_access_spilled_composite(\n        &mut self,\n        access: Handle<crate::Expression>,\n        block: &mut Block,\n        result_type_id: Word,\n    ) -> Result<Word, Error> {\n        let access_uses = self.function.access_uses.get(&access).map_or(0, |r| *r);\n        if access_uses == self.fun_info[access].ref_count {\n            // This expression is only used by other `Access` and\n            // `AccessIndex` expressions, so we don't need to cache a\n            // value for it yet.\n            Ok(0)\n        } else {\n            // There are other expressions that are going to expect this\n            // expression's value to be cached, not just other `Access` or\n            // `AccessIndex` expressions. We must actually perform the\n            // access on the spill variable now.\n            self.write_checked_load(\n                access,\n                block,\n                AccessTypeAdjustment::IntroducePointer(spirv::StorageClass::Function),\n                result_type_id,\n            )\n        }\n    }\n\n    /// Build the instructions for matrix - matrix column operations\n    #[allow(clippy::too_many_arguments)]\n    fn write_matrix_matrix_column_op(\n        &mut self,\n        block: &mut Block,\n        result_id: Word,\n        result_type_id: Word,\n        left_id: Word,\n        right_id: Word,\n        columns: crate::VectorSize,\n        rows: crate::VectorSize,\n        width: u8,\n        op: spirv::Op,\n    ) {\n        self.temp_list.clear();\n\n        let vector_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: rows,\n            scalar: crate::Scalar::float(width),\n        });\n\n        for index in 0..columns as u32 {\n            let column_id_left = self.gen_id();\n            let column_id_right = self.gen_id();\n            let column_id_res = self.gen_id();\n\n            block.body.push(Instruction::composite_extract(\n                vector_type_id,\n                column_id_left,\n                left_id,\n                &[index],\n            ));\n            block.body.push(Instruction::composite_extract(\n                vector_type_id,\n                column_id_right,\n                right_id,\n                &[index],\n            ));\n            block.body.push(Instruction::binary(\n                op,\n                vector_type_id,\n                column_id_res,\n                column_id_left,\n                column_id_right,\n            ));\n\n            self.temp_list.push(column_id_res);\n        }\n\n        block.body.push(Instruction::composite_construct(\n            result_type_id,\n            result_id,\n            &self.temp_list,\n        ));\n    }\n\n    /// Build the instructions for vector - scalar multiplication\n    fn write_vector_scalar_mult(\n        &mut self,\n        block: &mut Block,\n        result_id: Word,\n        result_type_id: Word,\n        vector_id: Word,\n        scalar_id: Word,\n        vector: &crate::TypeInner,\n    ) {\n        let (size, kind) = match *vector {\n            crate::TypeInner::Vector {\n                size,\n                scalar: crate::Scalar { kind, .. },\n            } => (size, kind),\n            _ => unreachable!(),\n        };\n\n        let (op, operand_id) = match kind {\n            crate::ScalarKind::Float => (spirv::Op::VectorTimesScalar, scalar_id),\n            _ => {\n                let operand_id = self.gen_id();\n                self.temp_list.clear();\n                self.temp_list.resize(size as usize, scalar_id);\n                block.body.push(Instruction::composite_construct(\n                    result_type_id,\n                    operand_id,\n                    &self.temp_list,\n                ));\n                (spirv::Op::IMul, operand_id)\n            }\n        };\n\n        block.body.push(Instruction::binary(\n            op,\n            result_type_id,\n            result_id,\n            vector_id,\n            operand_id,\n        ));\n    }\n\n    /// Build the instructions for the arithmetic expression of a dot product\n    ///\n    /// The argument `extractor` is a function that maps `(result_id,\n    /// composite_id, index)` to an instruction that extracts the `index`th\n    /// entry of the value with ID `composite_id` and assigns it to the slot\n    /// with id `result_id` (which must have type `result_type_id`).\n    #[expect(clippy::too_many_arguments)]\n    fn write_dot_product(\n        &mut self,\n        result_id: Word,\n        result_type_id: Word,\n        arg0_id: Word,\n        arg1_id: Word,\n        size: u32,\n        block: &mut Block,\n        extractor: impl Fn(Word, Word, Word) -> Instruction,\n    ) {\n        let mut partial_sum = self.writer.get_constant_null(result_type_id);\n        let last_component = size - 1;\n        for index in 0..=last_component {\n            // compute the product of the current components\n            let a_id = self.gen_id();\n            block.body.push(extractor(a_id, arg0_id, index));\n            let b_id = self.gen_id();\n            block.body.push(extractor(b_id, arg1_id, index));\n            let prod_id = self.gen_id();\n            block.body.push(Instruction::binary(\n                spirv::Op::IMul,\n                result_type_id,\n                prod_id,\n                a_id,\n                b_id,\n            ));\n\n            // choose the id for the next sum, depending on current index\n            let id = if index == last_component {\n                result_id\n            } else {\n                self.gen_id()\n            };\n\n            // sum the computed product with the partial sum\n            block.body.push(Instruction::binary(\n                spirv::Op::IAdd,\n                result_type_id,\n                id,\n                partial_sum,\n                prod_id,\n            ));\n            // set the id of the result as the previous partial sum\n            partial_sum = id;\n        }\n    }\n\n    /// Emit code for `pack4x{I,U}8[Clamp]` if capability \"Int8\" is available.\n    fn write_pack4x8_optimized(\n        &mut self,\n        block: &mut Block,\n        result_type_id: u32,\n        arg0_id: u32,\n        id: u32,\n        is_signed: bool,\n        should_clamp: bool,\n    ) -> Instruction {\n        let int_type = if is_signed {\n            crate::ScalarKind::Sint\n        } else {\n            crate::ScalarKind::Uint\n        };\n        let wide_vector_type = NumericType::Vector {\n            size: crate::VectorSize::Quad,\n            scalar: crate::Scalar {\n                kind: int_type,\n                width: 4,\n            },\n        };\n        let wide_vector_type_id = self.get_numeric_type_id(wide_vector_type);\n        let packed_vector_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Quad,\n            scalar: crate::Scalar {\n                kind: crate::ScalarKind::Uint,\n                width: 1,\n            },\n        });\n\n        let mut wide_vector = arg0_id;\n        if should_clamp {\n            let (min, max, clamp_op) = if is_signed {\n                (\n                    crate::Literal::I32(-128),\n                    crate::Literal::I32(127),\n                    spirv::GlslStd450Op::SClamp,\n                )\n            } else {\n                (\n                    crate::Literal::U32(0),\n                    crate::Literal::U32(255),\n                    spirv::GlslStd450Op::UClamp,\n                )\n            };\n            let [min, max] = [min, max].map(|lit| {\n                let scalar = self.writer.get_constant_scalar(lit);\n                self.writer.get_constant_composite(\n                    LookupType::Local(LocalType::Numeric(wide_vector_type)),\n                    &[scalar; 4],\n                )\n            });\n\n            let clamp_id = self.gen_id();\n            block.body.push(Instruction::ext_inst_gl_op(\n                self.writer.gl450_ext_inst_id,\n                clamp_op,\n                wide_vector_type_id,\n                clamp_id,\n                &[wide_vector, min, max],\n            ));\n\n            wide_vector = clamp_id;\n        }\n\n        let packed_vector = self.gen_id();\n        block.body.push(Instruction::unary(\n            spirv::Op::UConvert, // We truncate, so `UConvert` and `SConvert` behave identically.\n            packed_vector_type_id,\n            packed_vector,\n            wide_vector,\n        ));\n\n        // The SPIR-V spec [1] defines the bit order for bit casting between a vector\n        // and a scalar precisely as required by the WGSL spec [2].\n        // [1]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpBitcast\n        // [2]: https://www.w3.org/TR/WGSL/#pack4xI8-builtin\n        Instruction::unary(spirv::Op::Bitcast, result_type_id, id, packed_vector)\n    }\n\n    /// Emit code for `pack4x{I,U}8[Clamp]` if capability \"Int8\" is not available.\n    fn write_pack4x8_polyfill(\n        &mut self,\n        block: &mut Block,\n        result_type_id: u32,\n        arg0_id: u32,\n        id: u32,\n        is_signed: bool,\n        should_clamp: bool,\n    ) -> Instruction {\n        let int_type = if is_signed {\n            crate::ScalarKind::Sint\n        } else {\n            crate::ScalarKind::Uint\n        };\n        let uint_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32));\n        let int_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar {\n            kind: int_type,\n            width: 4,\n        }));\n\n        let mut last_instruction = Instruction::new(spirv::Op::Nop);\n\n        let zero = self.writer.get_constant_scalar(crate::Literal::U32(0));\n        let mut preresult = zero;\n        block\n            .body\n            .reserve(usize::from(VEC_LENGTH) * (2 + usize::from(is_signed)));\n\n        let eight = self.writer.get_constant_scalar(crate::Literal::U32(8));\n        const VEC_LENGTH: u8 = 4;\n        for i in 0..u32::from(VEC_LENGTH) {\n            let offset = self.writer.get_constant_scalar(crate::Literal::U32(i * 8));\n            let mut extracted = self.gen_id();\n            block.body.push(Instruction::binary(\n                spirv::Op::CompositeExtract,\n                int_type_id,\n                extracted,\n                arg0_id,\n                i,\n            ));\n            if is_signed {\n                let casted = self.gen_id();\n                block.body.push(Instruction::unary(\n                    spirv::Op::Bitcast,\n                    uint_type_id,\n                    casted,\n                    extracted,\n                ));\n                extracted = casted;\n            }\n            if should_clamp {\n                let (min, max, clamp_op) = if is_signed {\n                    (\n                        crate::Literal::I32(-128),\n                        crate::Literal::I32(127),\n                        spirv::GlslStd450Op::SClamp,\n                    )\n                } else {\n                    (\n                        crate::Literal::U32(0),\n                        crate::Literal::U32(255),\n                        spirv::GlslStd450Op::UClamp,\n                    )\n                };\n                let [min, max] = [min, max].map(|lit| self.writer.get_constant_scalar(lit));\n\n                let clamp_id = self.gen_id();\n                block.body.push(Instruction::ext_inst_gl_op(\n                    self.writer.gl450_ext_inst_id,\n                    clamp_op,\n                    result_type_id,\n                    clamp_id,\n                    &[extracted, min, max],\n                ));\n\n                extracted = clamp_id;\n            }\n            let is_last = i == u32::from(VEC_LENGTH - 1);\n            if is_last {\n                last_instruction = Instruction::quaternary(\n                    spirv::Op::BitFieldInsert,\n                    result_type_id,\n                    id,\n                    preresult,\n                    extracted,\n                    offset,\n                    eight,\n                )\n            } else {\n                let new_preresult = self.gen_id();\n                block.body.push(Instruction::quaternary(\n                    spirv::Op::BitFieldInsert,\n                    result_type_id,\n                    new_preresult,\n                    preresult,\n                    extracted,\n                    offset,\n                    eight,\n                ));\n                preresult = new_preresult;\n            }\n        }\n        last_instruction\n    }\n\n    /// Emit code for `unpack4x{I,U}8` if capability \"Int8\" is available.\n    fn write_unpack4x8_optimized(\n        &mut self,\n        block: &mut Block,\n        result_type_id: u32,\n        arg0_id: u32,\n        id: u32,\n        is_signed: bool,\n    ) -> Instruction {\n        let (int_type, convert_op) = if is_signed {\n            (crate::ScalarKind::Sint, spirv::Op::SConvert)\n        } else {\n            (crate::ScalarKind::Uint, spirv::Op::UConvert)\n        };\n\n        let packed_vector_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Quad,\n            scalar: crate::Scalar {\n                kind: int_type,\n                width: 1,\n            },\n        });\n\n        // The SPIR-V spec [1] defines the bit order for bit casting between a vector\n        // and a scalar precisely as required by the WGSL spec [2].\n        // [1]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpBitcast\n        // [2]: https://www.w3.org/TR/WGSL/#pack4xI8-builtin\n        let packed_vector = self.gen_id();\n        block.body.push(Instruction::unary(\n            spirv::Op::Bitcast,\n            packed_vector_type_id,\n            packed_vector,\n            arg0_id,\n        ));\n\n        Instruction::unary(convert_op, result_type_id, id, packed_vector)\n    }\n\n    /// Emit code for `unpack4x{I,U}8` if capability \"Int8\" is not available.\n    fn write_unpack4x8_polyfill(\n        &mut self,\n        block: &mut Block,\n        result_type_id: u32,\n        arg0_id: u32,\n        id: u32,\n        is_signed: bool,\n    ) -> Instruction {\n        let (int_type, extract_op) = if is_signed {\n            (crate::ScalarKind::Sint, spirv::Op::BitFieldSExtract)\n        } else {\n            (crate::ScalarKind::Uint, spirv::Op::BitFieldUExtract)\n        };\n\n        let sint_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::I32));\n\n        let eight = self.writer.get_constant_scalar(crate::Literal::U32(8));\n        let int_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar {\n            kind: int_type,\n            width: 4,\n        }));\n        block\n            .body\n            .reserve(usize::from(VEC_LENGTH) * 2 + usize::from(is_signed));\n        let arg_id = if is_signed {\n            let new_arg_id = self.gen_id();\n            block.body.push(Instruction::unary(\n                spirv::Op::Bitcast,\n                sint_type_id,\n                new_arg_id,\n                arg0_id,\n            ));\n            new_arg_id\n        } else {\n            arg0_id\n        };\n\n        const VEC_LENGTH: u8 = 4;\n        let parts: [_; VEC_LENGTH as usize] = core::array::from_fn(|_| self.gen_id());\n        for (i, part_id) in parts.into_iter().enumerate() {\n            let index = self\n                .writer\n                .get_constant_scalar(crate::Literal::U32(i as u32 * 8));\n            block.body.push(Instruction::ternary(\n                extract_op,\n                int_type_id,\n                part_id,\n                arg_id,\n                index,\n                eight,\n            ));\n        }\n\n        Instruction::composite_construct(result_type_id, id, &parts)\n    }\n\n    /// Generate one or more SPIR-V blocks for `naga_block`.\n    ///\n    /// Use `label_id` as the label for the SPIR-V entry point block.\n    ///\n    /// If control reaches the end of the SPIR-V block, terminate it according\n    /// to `exit`. This function's return value indicates whether it acted on\n    /// this parameter or not; see [`BlockExitDisposition`].\n    ///\n    /// If the block contains [`Break`] or [`Continue`] statements,\n    /// `loop_context` supplies the labels of the SPIR-V blocks to jump to. If\n    /// either of these labels are `None`, then it should have been a Naga\n    /// validation error for the corresponding statement to occur in this\n    /// context.\n    ///\n    /// [`Break`]: Statement::Break\n    /// [`Continue`]: Statement::Continue\n    fn write_block(\n        &mut self,\n        label_id: Word,\n        naga_block: &crate::Block,\n        exit: BlockExit,\n        loop_context: LoopContext,\n        debug_info: Option<&DebugInfoInner>,\n    ) -> Result<BlockExitDisposition, Error> {\n        let mut block = Block::new(label_id);\n        for (statement, span) in naga_block.span_iter() {\n            if let (Some(debug_info), false) = (\n                debug_info,\n                matches!(\n                    statement,\n                    &(Statement::Block(..)\n                        | Statement::Break\n                        | Statement::Continue\n                        | Statement::Kill\n                        | Statement::Return { .. }\n                        | Statement::Loop { .. })\n                ),\n            ) {\n                let loc: crate::SourceLocation = span.location(debug_info.source_code);\n                block.body.push(Instruction::line(\n                    debug_info.source_file_id,\n                    loc.line_number,\n                    loc.line_position,\n                ));\n            };\n            match *statement {\n                Statement::Emit(ref range) => {\n                    for handle in range.clone() {\n                        // omit const expressions as we've already cached those\n                        if !self.expression_constness.is_const(handle) {\n                            self.cache_expression_value(handle, &mut block)?;\n                        }\n                    }\n                }\n                Statement::Block(ref block_statements) => {\n                    let scope_id = self.gen_id();\n                    self.function.consume(block, Instruction::branch(scope_id));\n\n                    let merge_id = self.gen_id();\n                    let merge_used = self.write_block(\n                        scope_id,\n                        block_statements,\n                        BlockExit::Branch { target: merge_id },\n                        loop_context,\n                        debug_info,\n                    )?;\n\n                    match merge_used {\n                        BlockExitDisposition::Used => {\n                            block = Block::new(merge_id);\n                        }\n                        BlockExitDisposition::Discarded => {\n                            return Ok(BlockExitDisposition::Discarded);\n                        }\n                    }\n                }\n                Statement::If {\n                    condition,\n                    ref accept,\n                    ref reject,\n                } => {\n                    // In spirv 1.6, in a conditional branch the two block ids\n                    // of the branches can't have the same label. If `accept`\n                    // and `reject` are both empty (e.g. in `if (condition) {}`)\n                    // merge id will be both labels. Because both branches are\n                    // empty, we can skip the if statement.\n                    if !(accept.is_empty() && reject.is_empty()) {\n                        let condition_id = self.cached[condition];\n\n                        let merge_id = self.gen_id();\n                        block.body.push(Instruction::selection_merge(\n                            merge_id,\n                            spirv::SelectionControl::NONE,\n                        ));\n\n                        let accept_id = if accept.is_empty() {\n                            None\n                        } else {\n                            Some(self.gen_id())\n                        };\n                        let reject_id = if reject.is_empty() {\n                            None\n                        } else {\n                            Some(self.gen_id())\n                        };\n\n                        self.function.consume(\n                            block,\n                            Instruction::branch_conditional(\n                                condition_id,\n                                accept_id.unwrap_or(merge_id),\n                                reject_id.unwrap_or(merge_id),\n                            ),\n                        );\n\n                        if let Some(block_id) = accept_id {\n                            // We can ignore the `BlockExitDisposition` returned here because,\n                            // even if `merge_id` is not actually reachable, it is always\n                            // referred to by the `OpSelectionMerge` instruction we emitted\n                            // earlier.\n                            let _ = self.write_block(\n                                block_id,\n                                accept,\n                                BlockExit::Branch { target: merge_id },\n                                loop_context,\n                                debug_info,\n                            )?;\n                        }\n                        if let Some(block_id) = reject_id {\n                            // We can ignore the `BlockExitDisposition` returned here because,\n                            // even if `merge_id` is not actually reachable, it is always\n                            // referred to by the `OpSelectionMerge` instruction we emitted\n                            // earlier.\n                            let _ = self.write_block(\n                                block_id,\n                                reject,\n                                BlockExit::Branch { target: merge_id },\n                                loop_context,\n                                debug_info,\n                            )?;\n                        }\n\n                        block = Block::new(merge_id);\n                    }\n                }\n                Statement::Switch {\n                    selector,\n                    ref cases,\n                } => {\n                    let selector_id = self.cached[selector];\n\n                    let merge_id = self.gen_id();\n                    block.body.push(Instruction::selection_merge(\n                        merge_id,\n                        spirv::SelectionControl::NONE,\n                    ));\n\n                    let mut default_id = None;\n                    // id of previous empty fall-through case\n                    let mut last_id = None;\n\n                    let mut raw_cases = Vec::with_capacity(cases.len());\n                    let mut case_ids = Vec::with_capacity(cases.len());\n                    for case in cases.iter() {\n                        // take id of previous empty fall-through case or generate a new one\n                        let label_id = last_id.take().unwrap_or_else(|| self.gen_id());\n\n                        if case.fall_through && case.body.is_empty() {\n                            last_id = Some(label_id);\n                        }\n\n                        case_ids.push(label_id);\n\n                        match case.value {\n                            crate::SwitchValue::I32(value) => {\n                                raw_cases.push(super::instructions::Case {\n                                    value: value as Word,\n                                    label_id,\n                                });\n                            }\n                            crate::SwitchValue::U32(value) => {\n                                raw_cases.push(super::instructions::Case { value, label_id });\n                            }\n                            crate::SwitchValue::Default => {\n                                default_id = Some(label_id);\n                            }\n                        }\n                    }\n\n                    let default_id = default_id.unwrap();\n\n                    self.function.consume(\n                        block,\n                        Instruction::switch(selector_id, default_id, &raw_cases),\n                    );\n\n                    let inner_context = LoopContext {\n                        break_id: Some(merge_id),\n                        ..loop_context\n                    };\n\n                    for (i, (case, label_id)) in cases\n                        .iter()\n                        .zip(case_ids.iter())\n                        .filter(|&(case, _)| !(case.fall_through && case.body.is_empty()))\n                        .enumerate()\n                    {\n                        let case_finish_id = if case.fall_through {\n                            case_ids[i + 1]\n                        } else {\n                            merge_id\n                        };\n                        // We can ignore the `BlockExitDisposition` returned here because\n                        // `case_finish_id` is always referred to by either:\n                        //\n                        // - the `OpSwitch`, if it's the next case's label for a\n                        //   fall-through, or\n                        //\n                        // - the `OpSelectionMerge`, if it's the switch's overall merge\n                        //   block because there's no fall-through.\n                        let _ = self.write_block(\n                            *label_id,\n                            &case.body,\n                            BlockExit::Branch {\n                                target: case_finish_id,\n                            },\n                            inner_context,\n                            debug_info,\n                        )?;\n                    }\n\n                    block = Block::new(merge_id);\n                }\n                Statement::Loop {\n                    ref body,\n                    ref continuing,\n                    break_if,\n                } => {\n                    let preamble_id = self.gen_id();\n                    self.function\n                        .consume(block, Instruction::branch(preamble_id));\n\n                    let merge_id = self.gen_id();\n                    let body_id = self.gen_id();\n                    let continuing_id = self.gen_id();\n\n                    // SPIR-V requires the continuing to the `OpLoopMerge`,\n                    // so we have to start a new block with it.\n                    block = Block::new(preamble_id);\n                    // HACK the loop statement is begin with branch instruction,\n                    // so we need to put `OpLine` debug info before merge instruction\n                    if let Some(debug_info) = debug_info {\n                        let loc: crate::SourceLocation = span.location(debug_info.source_code);\n                        block.body.push(Instruction::line(\n                            debug_info.source_file_id,\n                            loc.line_number,\n                            loc.line_position,\n                        ))\n                    }\n                    block.body.push(Instruction::loop_merge(\n                        merge_id,\n                        continuing_id,\n                        spirv::SelectionControl::NONE,\n                    ));\n\n                    if self.force_loop_bounding {\n                        block = self.write_force_bounded_loop_instructions(block, merge_id);\n                    }\n                    self.function.consume(block, Instruction::branch(body_id));\n\n                    // We can ignore the `BlockExitDisposition` returned here because,\n                    // even if `continuing_id` is not actually reachable, it is always\n                    // referred to by the `OpLoopMerge` instruction we emitted earlier.\n                    let _ = self.write_block(\n                        body_id,\n                        body,\n                        BlockExit::Branch {\n                            target: continuing_id,\n                        },\n                        LoopContext {\n                            continuing_id: Some(continuing_id),\n                            break_id: Some(merge_id),\n                        },\n                        debug_info,\n                    )?;\n\n                    let exit = match break_if {\n                        Some(condition) => BlockExit::BreakIf {\n                            condition,\n                            preamble_id,\n                        },\n                        None => BlockExit::Branch {\n                            target: preamble_id,\n                        },\n                    };\n\n                    // We can ignore the `BlockExitDisposition` returned here because,\n                    // even if `merge_id` is not actually reachable, it is always referred\n                    // to by the `OpLoopMerge` instruction we emitted earlier.\n                    let _ = self.write_block(\n                        continuing_id,\n                        continuing,\n                        exit,\n                        LoopContext {\n                            continuing_id: None,\n                            break_id: Some(merge_id),\n                        },\n                        debug_info,\n                    )?;\n\n                    block = Block::new(merge_id);\n                }\n                Statement::Break => {\n                    self.function\n                        .consume(block, Instruction::branch(loop_context.break_id.unwrap()));\n                    return Ok(BlockExitDisposition::Discarded);\n                }\n                Statement::Continue => {\n                    self.function.consume(\n                        block,\n                        Instruction::branch(loop_context.continuing_id.unwrap()),\n                    );\n                    return Ok(BlockExitDisposition::Discarded);\n                }\n                Statement::Return { value: Some(value) } => {\n                    let value_id = self.cached[value];\n                    let instruction = match self.function.entry_point_context {\n                        // If this is an entry point, and we need to return anything,\n                        // let's instead store the output variables and return `void`.\n                        Some(ref context) => self.writer.write_entry_point_return(\n                            value_id,\n                            self.ir_function.result.as_ref().unwrap(),\n                            &context.results,\n                            &mut block.body,\n                        )?,\n                        None => Instruction::return_value(value_id),\n                    };\n                    self.function.consume(block, instruction);\n                    return Ok(BlockExitDisposition::Discarded);\n                }\n                Statement::Return { value: None } => {\n                    self.function.consume(block, Instruction::return_void());\n                    return Ok(BlockExitDisposition::Discarded);\n                }\n                Statement::Kill => {\n                    self.function.consume(block, Instruction::kill());\n                    return Ok(BlockExitDisposition::Discarded);\n                }\n                Statement::ControlBarrier(flags) => {\n                    self.writer.write_control_barrier(flags, &mut block.body);\n                }\n                Statement::MemoryBarrier(flags) => {\n                    self.writer.write_memory_barrier(flags, &mut block);\n                }\n                Statement::Store { pointer, value } => {\n                    let value_id = self.cached[value];\n                    match self.write_access_chain(\n                        pointer,\n                        &mut block,\n                        AccessTypeAdjustment::None,\n                    )? {\n                        ExpressionPointer::Ready { pointer_id } => {\n                            let atomic_space = match *self.fun_info[pointer]\n                                .ty\n                                .inner_with(&self.ir_module.types)\n                            {\n                                crate::TypeInner::Pointer { base, space } => {\n                                    match self.ir_module.types[base].inner {\n                                        crate::TypeInner::Atomic { .. } => Some(space),\n                                        _ => None,\n                                    }\n                                }\n                                _ => None,\n                            };\n                            let instruction = if let Some(space) = atomic_space {\n                                let (semantics, scope) = space.to_spirv_semantics_and_scope();\n                                let scope_constant_id = self.get_scope_constant(scope as u32);\n                                let semantics_id = self.get_index_constant(semantics.bits());\n                                Instruction::atomic_store(\n                                    pointer_id,\n                                    scope_constant_id,\n                                    semantics_id,\n                                    value_id,\n                                )\n                            } else {\n                                Instruction::store(pointer_id, value_id, None)\n                            };\n                            block.body.push(instruction);\n                        }\n                        ExpressionPointer::Conditional { condition, access } => {\n                            let mut selection = Selection::start(&mut block, ());\n                            selection.if_true(self, condition, ());\n\n                            // The in-bounds path. Perform the access and the store.\n                            let pointer_id = access.result_id.unwrap();\n                            selection.block().body.push(access);\n                            selection\n                                .block()\n                                .body\n                                .push(Instruction::store(pointer_id, value_id, None));\n\n                            // Finish the in-bounds block and start the merge block. This\n                            // is the block we'll leave current on return.\n                            selection.finish(self, ());\n                        }\n                    };\n                }\n                Statement::ImageStore {\n                    image,\n                    coordinate,\n                    array_index,\n                    value,\n                } => self.write_image_store(image, coordinate, array_index, value, &mut block)?,\n                Statement::Call {\n                    function: local_function,\n                    ref arguments,\n                    result,\n                } => {\n                    let id = self.gen_id();\n                    self.temp_list.clear();\n                    for &argument in arguments {\n                        self.temp_list.push(self.cached[argument]);\n                    }\n\n                    let type_id = match result {\n                        Some(expr) => {\n                            self.cached[expr] = id;\n                            self.get_expression_type_id(&self.fun_info[expr].ty)\n                        }\n                        None => self.writer.void_type,\n                    };\n\n                    block.body.push(Instruction::function_call(\n                        type_id,\n                        id,\n                        self.writer.lookup_function[&local_function],\n                        &self.temp_list,\n                    ));\n                }\n                Statement::Atomic {\n                    pointer,\n                    ref fun,\n                    value,\n                    result,\n                } => {\n                    let id = self.gen_id();\n                    // Compare-and-exchange operations produce a struct result,\n                    // so use `result`'s type if it is available. For no-result\n                    // operations, fall back to `value`'s type.\n                    let result_type_id =\n                        self.get_expression_type_id(&self.fun_info[result.unwrap_or(value)].ty);\n\n                    if let Some(result) = result {\n                        self.cached[result] = id;\n                    }\n\n                    let pointer_id = match self.write_access_chain(\n                        pointer,\n                        &mut block,\n                        AccessTypeAdjustment::None,\n                    )? {\n                        ExpressionPointer::Ready { pointer_id } => pointer_id,\n                        ExpressionPointer::Conditional { .. } => {\n                            return Err(Error::FeatureNotImplemented(\n                                \"Atomics out-of-bounds handling\",\n                            ));\n                        }\n                    };\n\n                    let space = self.fun_info[pointer]\n                        .ty\n                        .inner_with(&self.ir_module.types)\n                        .pointer_space()\n                        .unwrap();\n                    let (semantics, scope) = space.to_spirv_semantics_and_scope();\n                    let scope_constant_id = self.get_scope_constant(scope as u32);\n                    let semantics_id = self.get_index_constant(semantics.bits());\n                    let value_id = self.cached[value];\n                    let value_inner = self.fun_info[value].ty.inner_with(&self.ir_module.types);\n\n                    let crate::TypeInner::Scalar(scalar) = *value_inner else {\n                        return Err(Error::FeatureNotImplemented(\n                            \"Atomics with non-scalar values\",\n                        ));\n                    };\n\n                    let instruction = match *fun {\n                        crate::AtomicFunction::Add => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    spirv::Op::AtomicIAdd\n                                }\n                                crate::ScalarKind::Float => spirv::Op::AtomicFAddEXT,\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::Subtract => {\n                            let (spirv_op, value_id) = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    (spirv::Op::AtomicISub, value_id)\n                                }\n                                crate::ScalarKind::Float => {\n                                    // HACK: SPIR-V doesn't have a atomic subtraction,\n                                    // so we add the negated value instead.\n                                    let neg_result_id = self.gen_id();\n                                    block.body.push(Instruction::unary(\n                                        spirv::Op::FNegate,\n                                        result_type_id,\n                                        neg_result_id,\n                                        value_id,\n                                    ));\n                                    (spirv::Op::AtomicFAddEXT, neg_result_id)\n                                }\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::And => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    spirv::Op::AtomicAnd\n                                }\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::InclusiveOr => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    spirv::Op::AtomicOr\n                                }\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::ExclusiveOr => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    spirv::Op::AtomicXor\n                                }\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::Min => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint => spirv::Op::AtomicSMin,\n                                crate::ScalarKind::Uint => spirv::Op::AtomicUMin,\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::Max => {\n                            let spirv_op = match scalar.kind {\n                                crate::ScalarKind::Sint => spirv::Op::AtomicSMax,\n                                crate::ScalarKind::Uint => spirv::Op::AtomicUMax,\n                                _ => unimplemented!(),\n                            };\n                            Instruction::atomic_binary(\n                                spirv_op,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::Exchange { compare: None } => {\n                            Instruction::atomic_binary(\n                                spirv::Op::AtomicExchange,\n                                result_type_id,\n                                id,\n                                pointer_id,\n                                scope_constant_id,\n                                semantics_id,\n                                value_id,\n                            )\n                        }\n                        crate::AtomicFunction::Exchange { compare: Some(cmp) } => {\n                            let scalar_type_id =\n                                self.get_numeric_type_id(NumericType::Scalar(scalar));\n                            let bool_type_id =\n                                self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL));\n\n                            let cas_result_id = self.gen_id();\n                            let equality_result_id = self.gen_id();\n                            let equality_operator = match scalar.kind {\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint => {\n                                    spirv::Op::IEqual\n                                }\n                                _ => unimplemented!(),\n                            };\n\n                            let mut cas_instr = Instruction::new(spirv::Op::AtomicCompareExchange);\n                            cas_instr.set_type(scalar_type_id);\n                            cas_instr.set_result(cas_result_id);\n                            cas_instr.add_operand(pointer_id);\n                            cas_instr.add_operand(scope_constant_id);\n                            cas_instr.add_operand(semantics_id); // semantics if equal\n                            cas_instr.add_operand(semantics_id); // semantics if not equal\n                            cas_instr.add_operand(value_id);\n                            cas_instr.add_operand(self.cached[cmp]);\n                            block.body.push(cas_instr);\n                            block.body.push(Instruction::binary(\n                                equality_operator,\n                                bool_type_id,\n                                equality_result_id,\n                                cas_result_id,\n                                self.cached[cmp],\n                            ));\n                            Instruction::composite_construct(\n                                result_type_id,\n                                id,\n                                &[cas_result_id, equality_result_id],\n                            )\n                        }\n                    };\n\n                    block.body.push(instruction);\n                }\n                Statement::ImageAtomic {\n                    image,\n                    coordinate,\n                    array_index,\n                    fun,\n                    value,\n                } => {\n                    self.write_image_atomic(\n                        image,\n                        coordinate,\n                        array_index,\n                        fun,\n                        value,\n                        &mut block,\n                    )?;\n                }\n                Statement::WorkGroupUniformLoad { pointer, result } => {\n                    self.writer\n                        .write_control_barrier(crate::Barrier::WORK_GROUP, &mut block.body);\n                    let result_type_id = self.get_expression_type_id(&self.fun_info[result].ty);\n                    // Match `Expression::Load` behavior, including `OpAtomicLoad` when\n                    // loading from a pointer to `atomic<T>`.\n                    let id = self.write_checked_load(\n                        pointer,\n                        &mut block,\n                        AccessTypeAdjustment::None,\n                        result_type_id,\n                    )?;\n                    self.cached[result] = id;\n                    self.writer\n                        .write_control_barrier(crate::Barrier::WORK_GROUP, &mut block.body);\n                }\n                Statement::RayQuery { query, ref fun } => {\n                    self.write_ray_query_function(query, fun, &mut block);\n                }\n                Statement::SubgroupBallot {\n                    result,\n                    ref predicate,\n                } => {\n                    self.write_subgroup_ballot(predicate, result, &mut block)?;\n                }\n                Statement::SubgroupCollectiveOperation {\n                    ref op,\n                    ref collective_op,\n                    argument,\n                    result,\n                } => {\n                    self.write_subgroup_operation(op, collective_op, argument, result, &mut block)?;\n                }\n                Statement::SubgroupGather {\n                    ref mode,\n                    argument,\n                    result,\n                } => {\n                    self.write_subgroup_gather(mode, argument, result, &mut block)?;\n                }\n                Statement::CooperativeStore { target, ref data } => {\n                    let target_id = self.cached[target];\n                    let layout = if data.row_major {\n                        spirv::CooperativeMatrixLayout::RowMajorKHR\n                    } else {\n                        spirv::CooperativeMatrixLayout::ColumnMajorKHR\n                    };\n                    let layout_id = self.get_index_constant(layout as u32);\n                    let stride_id = self.cached[data.stride];\n                    match self.write_access_chain(\n                        data.pointer,\n                        &mut block,\n                        AccessTypeAdjustment::None,\n                    )? {\n                        ExpressionPointer::Ready { pointer_id } => {\n                            block.body.push(Instruction::coop_store(\n                                target_id, pointer_id, layout_id, stride_id,\n                            ));\n                        }\n                        ExpressionPointer::Conditional { condition, access } => {\n                            let mut selection = Selection::start(&mut block, ());\n                            selection.if_true(self, condition, ());\n\n                            // The in-bounds path. Perform the access and the store.\n                            let pointer_id = access.result_id.unwrap();\n                            selection.block().body.push(access);\n                            selection.block().body.push(Instruction::coop_store(\n                                target_id, pointer_id, layout_id, stride_id,\n                            ));\n\n                            // Finish the in-bounds block and start the merge block. This\n                            // is the block we'll leave current on return.\n                            selection.finish(self, ());\n                        }\n                    };\n                }\n                Statement::RayPipelineFunction(_) => unreachable!(),\n            }\n        }\n\n        let termination = match exit {\n            // We're generating code for the top-level Block of the function, so we\n            // need to end it with some kind of return instruction.\n            BlockExit::Return => match self.ir_function.result {\n                Some(ref result) if self.function.entry_point_context.is_none() => {\n                    let type_id = self.get_handle_type_id(result.ty);\n                    let null_id = self.writer.get_constant_null(type_id);\n                    Instruction::return_value(null_id)\n                }\n                _ => Instruction::return_void(),\n            },\n            BlockExit::Branch { target } => Instruction::branch(target),\n            BlockExit::BreakIf {\n                condition,\n                preamble_id,\n            } => {\n                let condition_id = self.cached[condition];\n\n                Instruction::branch_conditional(\n                    condition_id,\n                    loop_context.break_id.unwrap(),\n                    preamble_id,\n                )\n            }\n        };\n\n        self.function.consume(block, termination);\n        Ok(BlockExitDisposition::Used)\n    }\n\n    pub(super) fn write_function_body(\n        &mut self,\n        entry_id: Word,\n        debug_info: Option<&DebugInfoInner>,\n    ) -> Result<(), Error> {\n        // We can ignore the `BlockExitDisposition` returned here because\n        // `BlockExit::Return` doesn't refer to a block.\n        let _ = self.write_block(\n            entry_id,\n            &self.ir_function.body,\n            BlockExit::Return,\n            LoopContext::default(),\n            debug_info,\n        )?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/f16_polyfill.rs",
    "content": "/*!\nThis module provides functionality for polyfilling `f16` input/output variables\nwhen the `StorageInputOutput16` capability is not available or disabled.\n\nIt works by:\n\n1. Declaring `f16` I/O variables as `f32` in SPIR-V\n2. Converting between `f16` and `f32` at runtime using `OpFConvert`\n3. Maintaining mappings to track which variables need conversion\n*/\n\nuse crate::back::spv::{Instruction, LocalType, NumericType, Word};\nuse alloc::vec::Vec;\n\n/// Manages `f16` I/O polyfill state and operations.\n#[derive(Default)]\npub(in crate::back::spv) struct F16IoPolyfill {\n    use_native: bool,\n    io_var_to_f32_type: crate::FastHashMap<Word, Word>,\n}\n\nimpl F16IoPolyfill {\n    pub fn new(use_storage_input_output_16: bool) -> Self {\n        Self {\n            use_native: use_storage_input_output_16,\n            io_var_to_f32_type: crate::FastHashMap::default(),\n        }\n    }\n\n    pub fn needs_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {\n        use crate::{ScalarKind as Sk, TypeInner};\n\n        !self.use_native\n            && match *ty_inner {\n                TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => true,\n                TypeInner::Vector { scalar, .. }\n                    if scalar.kind == Sk::Float && scalar.width == 2 =>\n                {\n                    true\n                }\n                _ => false,\n            }\n    }\n\n    pub fn register_io_var(&mut self, variable_id: Word, f32_type_id: Word) {\n        self.io_var_to_f32_type.insert(variable_id, f32_type_id);\n    }\n\n    pub fn get_f32_io_type(&self, variable_id: Word) -> Option<Word> {\n        self.io_var_to_f32_type.get(&variable_id).copied()\n    }\n\n    pub fn emit_f16_to_f32_conversion(\n        f16_value_id: Word,\n        f32_type_id: Word,\n        converted_id: Word,\n        body: &mut Vec<Instruction>,\n    ) {\n        body.push(Instruction::unary(\n            spirv::Op::FConvert,\n            f32_type_id,\n            converted_id,\n            f16_value_id,\n        ));\n    }\n\n    pub fn emit_f32_to_f16_conversion(\n        f32_value_id: Word,\n        f16_type_id: Word,\n        converted_id: Word,\n        body: &mut Vec<Instruction>,\n    ) {\n        body.push(Instruction::unary(\n            spirv::Op::FConvert,\n            f16_type_id,\n            converted_id,\n            f32_value_id,\n        ));\n    }\n\n    pub fn create_polyfill_type(ty_inner: &crate::TypeInner) -> Option<LocalType> {\n        use crate::{ScalarKind as Sk, TypeInner};\n\n        match *ty_inner {\n            TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => {\n                Some(LocalType::Numeric(NumericType::Scalar(crate::Scalar::F32)))\n            }\n            TypeInner::Vector { size, scalar } if scalar.kind == Sk::Float && scalar.width == 2 => {\n                Some(LocalType::Numeric(NumericType::Vector {\n                    size,\n                    scalar: crate::Scalar::F32,\n                }))\n            }\n            _ => None,\n        }\n    }\n}\n\nimpl crate::back::spv::reclaimable::Reclaimable for F16IoPolyfill {\n    fn reclaim(mut self) -> Self {\n        self.io_var_to_f32_type = self.io_var_to_f32_type.reclaim();\n        self\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/helpers.rs",
    "content": "use alloc::{vec, vec::Vec};\n\nuse arrayvec::ArrayVec;\nuse spirv::Word;\n\nuse crate::{Handle, UniqueArena};\n\npub(super) fn bytes_to_words(bytes: &[u8]) -> Vec<Word> {\n    bytes\n        .chunks(4)\n        .map(|chars| chars.iter().rev().fold(0u32, |u, c| (u << 8) | *c as u32))\n        .collect()\n}\n\npub(super) fn string_to_words(input: &str) -> Vec<Word> {\n    let bytes = input.as_bytes();\n\n    str_bytes_to_words(bytes)\n}\n\npub(super) fn str_bytes_to_words(bytes: &[u8]) -> Vec<Word> {\n    let mut words = bytes_to_words(bytes);\n    if bytes.len().is_multiple_of(4) {\n        // nul-termination\n        words.push(0x0u32);\n    }\n\n    words\n}\n\n/// split a string into chunks and keep utf8 valid\n#[allow(unstable_name_collisions)]\npub(super) fn string_to_byte_chunks(input: &str, limit: usize) -> Vec<&[u8]> {\n    let mut offset: usize = 0;\n    let mut start: usize = 0;\n    let mut words = vec![];\n    while offset < input.len() {\n        offset = input.floor_char_boundary_polyfill(offset + limit);\n        // Clippy wants us to call as_bytes() first to avoid the UTF-8 check,\n        // but we want to assert the output is valid UTF-8.\n        #[allow(clippy::sliced_string_as_bytes)]\n        words.push(input[start..offset].as_bytes());\n        start = offset;\n    }\n\n    words\n}\n\npub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::StorageClass {\n    match space {\n        crate::AddressSpace::Handle => spirv::StorageClass::UniformConstant,\n        crate::AddressSpace::Function => spirv::StorageClass::Function,\n        crate::AddressSpace::Private => spirv::StorageClass::Private,\n        crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer,\n        crate::AddressSpace::Uniform => spirv::StorageClass::Uniform,\n        crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup,\n        crate::AddressSpace::Immediate => spirv::StorageClass::PushConstant,\n        crate::AddressSpace::TaskPayload => spirv::StorageClass::TaskPayloadWorkgroupEXT,\n        crate::AddressSpace::IncomingRayPayload | crate::AddressSpace::RayPayload => unreachable!(),\n    }\n}\n\npub(super) fn contains_builtin(\n    binding: Option<&crate::Binding>,\n    ty: Handle<crate::Type>,\n    arena: &UniqueArena<crate::Type>,\n    built_in: crate::BuiltIn,\n) -> bool {\n    if let Some(&crate::Binding::BuiltIn(bi)) = binding {\n        bi == built_in\n    } else if let crate::TypeInner::Struct { ref members, .. } = arena[ty].inner {\n        members\n            .iter()\n            .any(|member| contains_builtin(member.binding.as_ref(), member.ty, arena, built_in))\n    } else {\n        false // unreachable\n    }\n}\n\nimpl crate::AddressSpace {\n    pub(super) const fn to_spirv_semantics_and_scope(\n        self,\n    ) -> (spirv::MemorySemantics, spirv::Scope) {\n        match self {\n            Self::Storage { .. } => (spirv::MemorySemantics::empty(), spirv::Scope::Device),\n            Self::WorkGroup => (spirv::MemorySemantics::empty(), spirv::Scope::Workgroup),\n            Self::Uniform => (spirv::MemorySemantics::empty(), spirv::Scope::Device),\n            Self::Handle => (spirv::MemorySemantics::empty(), spirv::Scope::Device),\n            _ => (spirv::MemorySemantics::empty(), spirv::Scope::Invocation),\n        }\n    }\n}\n\n/// Return true if the global requires a type decorated with `Block`.\n///\n/// See [`back::spv::GlobalVariable`] for details.\n///\n/// [`back::spv::GlobalVariable`]: super::GlobalVariable\npub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool {\n    match var.space {\n        crate::AddressSpace::Uniform\n        | crate::AddressSpace::Storage { .. }\n        | crate::AddressSpace::Immediate => {}\n        _ => return false,\n    };\n    match ir_module.types[var.ty].inner {\n        crate::TypeInner::Struct {\n            ref members,\n            span: _,\n        } => match members.last() {\n            Some(member) => match ir_module.types[member.ty].inner {\n                // Structs with dynamically sized arrays can't be copied and can't be wrapped.\n                crate::TypeInner::Array {\n                    size: crate::ArraySize::Dynamic,\n                    ..\n                } => false,\n                _ => true,\n            },\n            None => false,\n        },\n        crate::TypeInner::BindingArray { .. } => false,\n        // if it's not a structure or a binding array, let's wrap it to be able to put \"Block\"\n        _ => true,\n    }\n}\n\n/// Returns true if `pointer` refers to two-row matrix which is a member of a\n/// struct in the [`crate::AddressSpace::Uniform`] address space.\npub fn is_uniform_matcx2_struct_member_access(\n    ir_function: &crate::Function,\n    fun_info: &crate::valid::FunctionInfo,\n    ir_module: &crate::Module,\n    pointer: Handle<crate::Expression>,\n) -> bool {\n    if let crate::TypeInner::Pointer {\n        base: pointer_base_type,\n        space: crate::AddressSpace::Uniform,\n    } = *fun_info[pointer].ty.inner_with(&ir_module.types)\n    {\n        if let crate::TypeInner::Matrix {\n            rows: crate::VectorSize::Bi,\n            ..\n        } = ir_module.types[pointer_base_type].inner\n        {\n            if let crate::Expression::AccessIndex {\n                base: parent_pointer,\n                ..\n            } = ir_function.expressions[pointer]\n            {\n                if let crate::TypeInner::Pointer {\n                    base: parent_type, ..\n                } = *fun_info[parent_pointer].ty.inner_with(&ir_module.types)\n                {\n                    if let crate::TypeInner::Struct { .. } = ir_module.types[parent_type].inner {\n                        return true;\n                    }\n                }\n            }\n        }\n    }\n\n    false\n}\n\n///HACK: this is taken from std unstable, remove it when std's floor_char_boundary is stable\n/// and available in our msrv.\ntrait U8Internal {\n    fn is_utf8_char_boundary_polyfill(&self) -> bool;\n}\n\nimpl U8Internal for u8 {\n    fn is_utf8_char_boundary_polyfill(&self) -> bool {\n        // This is bit magic equivalent to: b < 128 || b >= 192\n        (*self as i8) >= -0x40\n    }\n}\n\ntrait StrUnstable {\n    fn floor_char_boundary_polyfill(&self, index: usize) -> usize;\n}\n\nimpl StrUnstable for str {\n    fn floor_char_boundary_polyfill(&self, index: usize) -> usize {\n        if index >= self.len() {\n            self.len()\n        } else {\n            let lower_bound = index.saturating_sub(3);\n            let new_index = self.as_bytes()[lower_bound..=index]\n                .iter()\n                .rposition(|b| b.is_utf8_char_boundary_polyfill());\n\n            // We know that the character boundary will be within four bytes.\n            lower_bound + new_index.unwrap()\n        }\n    }\n}\n\npub enum BindingDecorations {\n    BuiltIn(spirv::BuiltIn, ArrayVec<spirv::Decoration, 2>),\n    Location {\n        location: u32,\n        others: ArrayVec<spirv::Decoration, 5>,\n        /// If this is `Some`, use Decoration::Index with blend_src as an operand\n        blend_src: Option<Word>,\n    },\n    None,\n}\n"
  },
  {
    "path": "naga/src/back/spv/image.rs",
    "content": "/*!\nGenerating SPIR-V for image operations.\n*/\n\nuse spirv::Word;\n\nuse super::{\n    selection::{MergeTuple, Selection},\n    Block, BlockContext, Error, IdGenerator, Instruction, LocalType, LookupType, NumericType,\n};\nuse crate::arena::Handle;\n\n/// Information about a vector of coordinates.\n///\n/// The coordinate vectors expected by SPIR-V `OpImageRead` and `OpImageFetch`\n/// supply the array index for arrayed images as an additional component at\n/// the end, whereas Naga's `ImageLoad`, `ImageStore`, and `ImageSample` carry\n/// the array index as a separate field.\n///\n/// In the process of generating code to compute the combined vector, we also\n/// produce SPIR-V types and vector lengths that are useful elsewhere. This\n/// struct gathers that information into one place, with standard names.\nstruct ImageCoordinates {\n    /// The SPIR-V id of the combined coordinate/index vector value.\n    ///\n    /// Note: when indexing a non-arrayed 1D image, this will be a scalar.\n    value_id: Word,\n\n    /// The SPIR-V id of the type of `value`.\n    type_id: Word,\n\n    /// The number of components in `value`, if it is a vector, or `None` if it\n    /// is a scalar.\n    size: Option<crate::VectorSize>,\n}\n\n/// A trait for image access (load or store) code generators.\n///\n/// Types implementing this trait hold information about an `ImageStore` or\n/// `ImageLoad` operation that is not affected by the bounds check policy. The\n/// `generate` method emits code for the access, given the results of bounds\n/// checking.\n///\n/// The [`image`] bounds checks policy affects access coordinates, level of\n/// detail, and sample index, but never the image id, result type (if any), or\n/// the specific SPIR-V instruction used. Types that implement this trait gather\n/// together the latter category, so we don't have to plumb them through the\n/// bounds-checking code.\n///\n/// [`image`]: crate::proc::BoundsCheckPolicies::index\ntrait Access {\n    /// The Rust type that represents SPIR-V values and types for this access.\n    ///\n    /// For operations like loads, this is `Word`. For operations like stores,\n    /// this is `()`.\n    ///\n    /// For `ReadZeroSkipWrite`, this will be the type of the selection\n    /// construct that performs the bounds checks, so it must implement\n    /// `MergeTuple`.\n    type Output: MergeTuple + Copy + Clone;\n\n    /// Write an image access to `block`.\n    ///\n    /// Access the texel at `coordinates_id`. The optional `level_id` indicates\n    /// the level of detail, and `sample_id` is the index of the sample to\n    /// access in a multisampled texel.\n    ///\n    /// This method assumes that `coordinates_id` has already had the image array\n    /// index, if any, folded in, as done by `write_image_coordinates`.\n    ///\n    /// Return the value id produced by the instruction, if any.\n    ///\n    /// Use `id_gen` to generate SPIR-V ids as necessary.\n    fn generate(\n        &self,\n        id_gen: &mut IdGenerator,\n        coordinates_id: Word,\n        level_id: Option<Word>,\n        sample_id: Option<Word>,\n        block: &mut Block,\n    ) -> Self::Output;\n\n    /// Return the SPIR-V type of the value produced by the code written by\n    /// `generate`. If the access does not produce a value, `Self::Output`\n    /// should be `()`.\n    fn result_type(&self) -> Self::Output;\n\n    /// Construct the SPIR-V 'zero' value to be returned for an out-of-bounds\n    /// access under the `ReadZeroSkipWrite` policy. If the access does not\n    /// produce a value, `Self::Output` should be `()`.\n    fn out_of_bounds_value(&self, ctx: &mut BlockContext<'_>) -> Self::Output;\n}\n\n/// Texel access information for an [`ImageLoad`] expression.\n///\n/// [`ImageLoad`]: crate::Expression::ImageLoad\nstruct Load {\n    /// The specific opcode we'll use to perform the fetch. Storage images\n    /// require `OpImageRead`, while sampled images require `OpImageFetch`.\n    opcode: spirv::Op,\n\n    /// The type id produced by the actual image access instruction.\n    type_id: Word,\n\n    /// The id of the image being accessed.\n    image_id: Word,\n}\n\nimpl Load {\n    fn from_image_expr(\n        ctx: &mut BlockContext<'_>,\n        image_id: Word,\n        image_class: crate::ImageClass,\n        result_type_id: Word,\n    ) -> Result<Load, Error> {\n        let opcode = match image_class {\n            crate::ImageClass::Storage { .. } => spirv::Op::ImageRead,\n            crate::ImageClass::Depth { .. } | crate::ImageClass::Sampled { .. } => {\n                spirv::Op::ImageFetch\n            }\n            crate::ImageClass::External => unimplemented!(),\n        };\n\n        // `OpImageRead` and `OpImageFetch` instructions produce vec4<f32>\n        // values. Most of the time, we can just use `result_type_id` for\n        // this. The exception is that `Expression::ImageLoad` from a depth\n        // image produces a scalar `f32`, so in that case we need to find\n        // the right SPIR-V type for the access instruction here.\n        let type_id = match image_class {\n            crate::ImageClass::Depth { .. } => ctx.get_numeric_type_id(NumericType::Vector {\n                size: crate::VectorSize::Quad,\n                scalar: crate::Scalar::F32,\n            }),\n            _ => result_type_id,\n        };\n\n        Ok(Load {\n            opcode,\n            type_id,\n            image_id,\n        })\n    }\n}\n\nimpl Access for Load {\n    type Output = Word;\n\n    /// Write an instruction to access a given texel of this image.\n    fn generate(\n        &self,\n        id_gen: &mut IdGenerator,\n        coordinates_id: Word,\n        level_id: Option<Word>,\n        sample_id: Option<Word>,\n        block: &mut Block,\n    ) -> Word {\n        let texel_id = id_gen.next();\n        let mut instruction = Instruction::image_fetch_or_read(\n            self.opcode,\n            self.type_id,\n            texel_id,\n            self.image_id,\n            coordinates_id,\n        );\n\n        match (level_id, sample_id) {\n            (None, None) => {}\n            (Some(level_id), None) => {\n                instruction.add_operand(spirv::ImageOperands::LOD.bits());\n                instruction.add_operand(level_id);\n            }\n            (None, Some(sample_id)) => {\n                instruction.add_operand(spirv::ImageOperands::SAMPLE.bits());\n                instruction.add_operand(sample_id);\n            }\n            // There's no such thing as a multi-sampled mipmap.\n            (Some(_), Some(_)) => unreachable!(),\n        }\n\n        block.body.push(instruction);\n\n        texel_id\n    }\n\n    fn result_type(&self) -> Word {\n        self.type_id\n    }\n\n    fn out_of_bounds_value(&self, ctx: &mut BlockContext<'_>) -> Word {\n        ctx.writer.get_constant_null(self.type_id)\n    }\n}\n\n/// Texel access information for a [`Store`] statement.\n///\n/// [`Store`]: crate::Statement::Store\nstruct Store {\n    /// The id of the image being written to.\n    image_id: Word,\n\n    /// The value we're going to write to the texel.\n    value_id: Word,\n}\n\nimpl Access for Store {\n    /// Stores don't generate any value.\n    type Output = ();\n\n    fn generate(\n        &self,\n        _id_gen: &mut IdGenerator,\n        coordinates_id: Word,\n        _level_id: Option<Word>,\n        _sample_id: Option<Word>,\n        block: &mut Block,\n    ) {\n        block.body.push(Instruction::image_write(\n            self.image_id,\n            coordinates_id,\n            self.value_id,\n        ));\n    }\n\n    /// Stores don't generate any value, so this just returns `()`.\n    fn result_type(&self) {}\n\n    /// Stores don't generate any value, so this just returns `()`.\n    fn out_of_bounds_value(&self, _ctx: &mut BlockContext<'_>) {}\n}\n\nimpl BlockContext<'_> {\n    /// Extend image coordinates with an array index, if necessary.\n    ///\n    /// Whereas [`Expression::ImageLoad`] and [`ImageSample`] treat the array\n    /// index as a separate operand from the coordinates, SPIR-V image access\n    /// instructions include the array index in the `coordinates` operand. This\n    /// function builds a SPIR-V coordinate vector from a Naga coordinate vector\n    /// and array index, if one is supplied, and returns a `ImageCoordinates`\n    /// struct describing what it built.\n    ///\n    /// If `array_index` is `Some(expr)`, then this function constructs a new\n    /// vector that is `coordinates` with `array_index` concatenated onto the\n    /// end: a `vec2` becomes a `vec3`, a scalar becomes a `vec2`, and so on.\n    ///\n    /// If `array_index` is `None`, then the return value uses `coordinates`\n    /// unchanged. Note that, when indexing a non-arrayed 1D image, this will be\n    /// a scalar value.\n    ///\n    /// If needed, this function generates code to convert the array index,\n    /// always an integer scalar, to match the component type of `coordinates`.\n    /// Naga's `ImageLoad` and SPIR-V's `OpImageRead`, `OpImageFetch`, and\n    /// `OpImageWrite` all use integer coordinates, while Naga's `ImageSample`\n    /// and SPIR-V's `OpImageSample...` instructions all take floating-point\n    /// coordinate vectors.\n    ///\n    /// [`Expression::ImageLoad`]: crate::Expression::ImageLoad\n    /// [`ImageSample`]: crate::Expression::ImageSample\n    fn write_image_coordinates(\n        &mut self,\n        coordinates: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        block: &mut Block,\n    ) -> Result<ImageCoordinates, Error> {\n        use crate::TypeInner as Ti;\n        use crate::VectorSize as Vs;\n\n        let coordinates_id = self.cached[coordinates];\n        let ty = &self.fun_info[coordinates].ty;\n        let inner_ty = ty.inner_with(&self.ir_module.types);\n\n        // If there's no array index, the image coordinates are exactly the\n        // `coordinate` field of the `Expression::ImageLoad`. No work is needed.\n        let array_index = match array_index {\n            None => {\n                let value_id = coordinates_id;\n                let type_id = self.get_expression_type_id(ty);\n                let size = match *inner_ty {\n                    Ti::Scalar { .. } => None,\n                    Ti::Vector { size, .. } => Some(size),\n                    _ => return Err(Error::Validation(\"coordinate type\")),\n                };\n                return Ok(ImageCoordinates {\n                    value_id,\n                    type_id,\n                    size,\n                });\n            }\n            Some(ix) => ix,\n        };\n\n        // Find the component type of `coordinates`, and figure out the size the\n        // combined coordinate vector will have.\n        let (component_scalar, size) = match *inner_ty {\n            Ti::Scalar(scalar @ crate::Scalar { width: 4, .. }) => (scalar, Vs::Bi),\n            Ti::Vector {\n                scalar: scalar @ crate::Scalar { width: 4, .. },\n                size: Vs::Bi,\n            } => (scalar, Vs::Tri),\n            Ti::Vector {\n                scalar: scalar @ crate::Scalar { width: 4, .. },\n                size: Vs::Tri,\n            } => (scalar, Vs::Quad),\n            Ti::Vector { size: Vs::Quad, .. } => {\n                return Err(Error::Validation(\"extending vec4 coordinate\"));\n            }\n            ref other => {\n                log::error!(\"wrong coordinate type {other:?}\");\n                return Err(Error::Validation(\"coordinate type\"));\n            }\n        };\n\n        // Convert the index to the coordinate component type, if necessary.\n        let array_index_id = self.cached[array_index];\n        let ty = &self.fun_info[array_index].ty;\n        let inner_ty = ty.inner_with(&self.ir_module.types);\n        let array_index_scalar = match *inner_ty {\n            Ti::Scalar(\n                scalar @ crate::Scalar {\n                    kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                    width: 4,\n                },\n            ) => scalar,\n            _ => unreachable!(\"we only allow i32 and u32\"),\n        };\n        let cast = match (component_scalar.kind, array_index_scalar.kind) {\n            (crate::ScalarKind::Sint, crate::ScalarKind::Sint)\n            | (crate::ScalarKind::Uint, crate::ScalarKind::Uint) => None,\n            (crate::ScalarKind::Sint, crate::ScalarKind::Uint)\n            | (crate::ScalarKind::Uint, crate::ScalarKind::Sint) => Some(spirv::Op::Bitcast),\n            (crate::ScalarKind::Float, crate::ScalarKind::Sint) => Some(spirv::Op::ConvertSToF),\n            (crate::ScalarKind::Float, crate::ScalarKind::Uint) => Some(spirv::Op::ConvertUToF),\n            (crate::ScalarKind::Bool, _) => unreachable!(\"we don't allow bool for component\"),\n            (_, crate::ScalarKind::Bool | crate::ScalarKind::Float) => {\n                unreachable!(\"we don't allow bool or float for array index\")\n            }\n            (crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat, _)\n            | (_, crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat) => {\n                unreachable!(\"abstract types should never reach backends\")\n            }\n        };\n        let reconciled_array_index_id = if let Some(cast) = cast {\n            let component_ty_id = self.get_numeric_type_id(NumericType::Scalar(component_scalar));\n            let reconciled_id = self.gen_id();\n            block.body.push(Instruction::unary(\n                cast,\n                component_ty_id,\n                reconciled_id,\n                array_index_id,\n            ));\n            reconciled_id\n        } else {\n            array_index_id\n        };\n\n        // Find the SPIR-V type for the combined coordinates/index vector.\n        let type_id = self.get_numeric_type_id(NumericType::Vector {\n            size,\n            scalar: component_scalar,\n        });\n\n        // Schmear the coordinates and index together.\n        let value_id = self.gen_id();\n        block.body.push(Instruction::composite_construct(\n            type_id,\n            value_id,\n            &[coordinates_id, reconciled_array_index_id],\n        ));\n        Ok(ImageCoordinates {\n            value_id,\n            type_id,\n            size: Some(size),\n        })\n    }\n\n    pub(super) fn get_handle_id(&mut self, expr_handle: Handle<crate::Expression>) -> Word {\n        let id = match self.ir_function.expressions[expr_handle] {\n            crate::Expression::GlobalVariable(handle) => {\n                self.writer.global_variables[handle].handle_id\n            }\n            crate::Expression::FunctionArgument(i) => {\n                self.function.parameters[i as usize].handle_id\n            }\n            crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => {\n                self.cached[expr_handle]\n            }\n            ref other => unreachable!(\"Unexpected image expression {:?}\", other),\n        };\n\n        if id == 0 {\n            unreachable!(\n                \"Image expression {:?} doesn't have a handle ID\",\n                expr_handle\n            );\n        }\n\n        id\n    }\n\n    /// Generate a vector or scalar 'one' for arithmetic on `coordinates`.\n    ///\n    /// If `coordinates` is a scalar, return a scalar one. Otherwise, return\n    /// a vector of ones.\n    fn write_coordinate_one(&mut self, coordinates: &ImageCoordinates) -> Result<Word, Error> {\n        let one = self.get_scope_constant(1);\n        match coordinates.size {\n            None => Ok(one),\n            Some(vector_size) => {\n                let ones = [one; 4];\n                let id = self.gen_id();\n                Instruction::constant_composite(\n                    coordinates.type_id,\n                    id,\n                    &ones[..vector_size as usize],\n                )\n                .to_words(&mut self.writer.logical_layout.declarations);\n                Ok(id)\n            }\n        }\n    }\n\n    /// Generate code to restrict `input` to fall between zero and one less than\n    /// `size_id`.\n    ///\n    /// Both must be 32-bit scalar integer values, whose type is given by\n    /// `type_id`. The computed value is also of type `type_id`.\n    fn restrict_scalar(\n        &mut self,\n        type_id: Word,\n        input_id: Word,\n        size_id: Word,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        let i32_one_id = self.get_scope_constant(1);\n\n        // Subtract one from `size` to get the largest valid value.\n        let limit_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::ISub,\n            type_id,\n            limit_id,\n            size_id,\n            i32_one_id,\n        ));\n\n        // Use an unsigned minimum, to handle both positive out-of-range values\n        // and negative values in a single instruction: negative values of\n        // `input_id` get treated as very large positive values.\n        let restricted_id = self.gen_id();\n        block.body.push(Instruction::ext_inst_gl_op(\n            self.writer.gl450_ext_inst_id,\n            spirv::GlslStd450Op::UMin,\n            type_id,\n            restricted_id,\n            &[input_id, limit_id],\n        ));\n\n        Ok(restricted_id)\n    }\n\n    /// Write instructions to query the size of an image.\n    ///\n    /// This takes care of selecting the right instruction depending on whether\n    /// a level of detail parameter is present.\n    fn write_coordinate_bounds(\n        &mut self,\n        type_id: Word,\n        image_id: Word,\n        level_id: Option<Word>,\n        block: &mut Block,\n    ) -> Word {\n        let coordinate_bounds_id = self.gen_id();\n        match level_id {\n            Some(level_id) => {\n                // A level of detail was provided, so fetch the image size for\n                // that level.\n                let mut inst = Instruction::image_query(\n                    spirv::Op::ImageQuerySizeLod,\n                    type_id,\n                    coordinate_bounds_id,\n                    image_id,\n                );\n                inst.add_operand(level_id);\n                block.body.push(inst);\n            }\n            _ => {\n                // No level of detail was given.\n                block.body.push(Instruction::image_query(\n                    spirv::Op::ImageQuerySize,\n                    type_id,\n                    coordinate_bounds_id,\n                    image_id,\n                ));\n            }\n        }\n\n        coordinate_bounds_id\n    }\n\n    /// Write code to restrict coordinates for an image reference.\n    ///\n    /// First, clamp the level of detail or sample index to fall within bounds.\n    /// Then, obtain the image size, possibly using the clamped level of detail.\n    /// Finally, use an unsigned minimum instruction to force all coordinates\n    /// into range.\n    ///\n    /// Return a triple `(COORDS, LEVEL, SAMPLE)`, where `COORDS` is a coordinate\n    /// vector (including the array index, if any), `LEVEL` is an optional level\n    /// of detail, and `SAMPLE` is an optional sample index, all guaranteed to\n    /// be in-bounds for `image_id`.\n    ///\n    /// The result is usually a vector, but it is a scalar when indexing\n    /// non-arrayed 1D images.\n    fn write_restricted_coordinates(\n        &mut self,\n        image_id: Word,\n        coordinates: ImageCoordinates,\n        level_id: Option<Word>,\n        sample_id: Option<Word>,\n        block: &mut Block,\n    ) -> Result<(Word, Option<Word>, Option<Word>), Error> {\n        self.writer.require_any(\n            \"the `Restrict` image bounds check policy\",\n            &[spirv::Capability::ImageQuery],\n        )?;\n\n        let i32_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::I32));\n\n        // If `level` is `Some`, clamp it to fall within bounds. This must\n        // happen first, because we'll use it to query the image size for\n        // clamping the actual coordinates.\n        let level_id = level_id\n            .map(|level_id| {\n                // Find the number of mipmap levels in this image.\n                let num_levels_id = self.gen_id();\n                block.body.push(Instruction::image_query(\n                    spirv::Op::ImageQueryLevels,\n                    i32_type_id,\n                    num_levels_id,\n                    image_id,\n                ));\n\n                self.restrict_scalar(i32_type_id, level_id, num_levels_id, block)\n            })\n            .transpose()?;\n\n        // If `sample_id` is `Some`, clamp it to fall within bounds.\n        let sample_id = sample_id\n            .map(|sample_id| {\n                // Find the number of samples per texel.\n                let num_samples_id = self.gen_id();\n                block.body.push(Instruction::image_query(\n                    spirv::Op::ImageQuerySamples,\n                    i32_type_id,\n                    num_samples_id,\n                    image_id,\n                ));\n\n                self.restrict_scalar(i32_type_id, sample_id, num_samples_id, block)\n            })\n            .transpose()?;\n\n        // Obtain the image bounds, including the array element count.\n        let coordinate_bounds_id =\n            self.write_coordinate_bounds(coordinates.type_id, image_id, level_id, block);\n\n        // Compute maximum valid values from the bounds.\n        let ones = self.write_coordinate_one(&coordinates)?;\n        let coordinate_limit_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::ISub,\n            coordinates.type_id,\n            coordinate_limit_id,\n            coordinate_bounds_id,\n            ones,\n        ));\n\n        // Restrict the coordinates to fall within those bounds.\n        //\n        // Use an unsigned minimum, to handle both positive out-of-range values\n        // and negative values in a single instruction: negative values of\n        // `coordinates` get treated as very large positive values.\n        let restricted_coordinates_id = self.gen_id();\n        block.body.push(Instruction::ext_inst_gl_op(\n            self.writer.gl450_ext_inst_id,\n            spirv::GlslStd450Op::UMin,\n            coordinates.type_id,\n            restricted_coordinates_id,\n            &[coordinates.value_id, coordinate_limit_id],\n        ));\n\n        Ok((restricted_coordinates_id, level_id, sample_id))\n    }\n\n    fn write_conditional_image_access<A: Access>(\n        &mut self,\n        image_id: Word,\n        coordinates: ImageCoordinates,\n        level_id: Option<Word>,\n        sample_id: Option<Word>,\n        block: &mut Block,\n        access: &A,\n    ) -> Result<A::Output, Error> {\n        self.writer.require_any(\n            \"the `ReadZeroSkipWrite` image bounds check policy\",\n            &[spirv::Capability::ImageQuery],\n        )?;\n\n        let bool_type_id = self.writer.get_bool_type_id();\n        let i32_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::I32));\n\n        let null_id = access.out_of_bounds_value(self);\n\n        let mut selection = Selection::start(block, access.result_type());\n\n        // If `level_id` is `Some`, check whether it is within bounds. This must\n        // happen first, because we'll be supplying this as an argument when we\n        // query the image size.\n        if let Some(level_id) = level_id {\n            // Find the number of mipmap levels in this image.\n            let num_levels_id = self.gen_id();\n            selection.block().body.push(Instruction::image_query(\n                spirv::Op::ImageQueryLevels,\n                i32_type_id,\n                num_levels_id,\n                image_id,\n            ));\n\n            let lod_cond_id = self.gen_id();\n            selection.block().body.push(Instruction::binary(\n                spirv::Op::ULessThan,\n                bool_type_id,\n                lod_cond_id,\n                level_id,\n                num_levels_id,\n            ));\n\n            selection.if_true(self, lod_cond_id, null_id);\n        }\n\n        // If `sample_id` is `Some`, check whether it is in bounds.\n        if let Some(sample_id) = sample_id {\n            // Find the number of samples per texel.\n            let num_samples_id = self.gen_id();\n            selection.block().body.push(Instruction::image_query(\n                spirv::Op::ImageQuerySamples,\n                i32_type_id,\n                num_samples_id,\n                image_id,\n            ));\n\n            let samples_cond_id = self.gen_id();\n            selection.block().body.push(Instruction::binary(\n                spirv::Op::ULessThan,\n                bool_type_id,\n                samples_cond_id,\n                sample_id,\n                num_samples_id,\n            ));\n\n            selection.if_true(self, samples_cond_id, null_id);\n        }\n\n        // Obtain the image bounds, including any array element count.\n        let coordinate_bounds_id = self.write_coordinate_bounds(\n            coordinates.type_id,\n            image_id,\n            level_id,\n            selection.block(),\n        );\n\n        // Compare the coordinates against the bounds.\n        let coords_numeric_type = match coordinates.size {\n            Some(size) => NumericType::Vector {\n                size,\n                scalar: crate::Scalar::BOOL,\n            },\n            None => NumericType::Scalar(crate::Scalar::BOOL),\n        };\n        let coords_bool_type_id = self.get_numeric_type_id(coords_numeric_type);\n        let coords_conds_id = self.gen_id();\n        selection.block().body.push(Instruction::binary(\n            spirv::Op::ULessThan,\n            coords_bool_type_id,\n            coords_conds_id,\n            coordinates.value_id,\n            coordinate_bounds_id,\n        ));\n\n        // If the comparison above was a vector comparison, then we need to\n        // check that all components of the comparison are true.\n        let coords_cond_id = if coords_bool_type_id != bool_type_id {\n            let id = self.gen_id();\n            selection.block().body.push(Instruction::relational(\n                spirv::Op::All,\n                bool_type_id,\n                id,\n                coords_conds_id,\n            ));\n            id\n        } else {\n            coords_conds_id\n        };\n\n        selection.if_true(self, coords_cond_id, null_id);\n\n        // All conditions are met. We can carry out the access.\n        let texel_id = access.generate(\n            &mut self.writer.id_gen,\n            coordinates.value_id,\n            level_id,\n            sample_id,\n            selection.block(),\n        );\n\n        // This, then, is the value of the 'true' branch.\n        Ok(selection.finish(self, texel_id))\n    }\n\n    /// Generate code for an `ImageLoad` expression.\n    ///\n    /// The arguments are the components of an `Expression::ImageLoad` variant.\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn write_image_load(\n        &mut self,\n        result_type_id: Word,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        level: Option<Handle<crate::Expression>>,\n        sample: Option<Handle<crate::Expression>>,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        let image_id = self.get_handle_id(image);\n        let image_type = self.fun_info[image].ty.inner_with(&self.ir_module.types);\n        let image_class = match *image_type {\n            crate::TypeInner::Image { class, .. } => class,\n            _ => return Err(Error::Validation(\"image type\")),\n        };\n\n        let access = Load::from_image_expr(self, image_id, image_class, result_type_id)?;\n        let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;\n\n        let level_id = level.map(|expr| self.cached[expr]);\n        let sample_id = sample.map(|expr| self.cached[expr]);\n\n        // Perform the access, according to the bounds check policy.\n        let access_id = match self.writer.bounds_check_policies.image_load {\n            crate::proc::BoundsCheckPolicy::Restrict => {\n                let (coords, level_id, sample_id) = self.write_restricted_coordinates(\n                    image_id,\n                    coordinates,\n                    level_id,\n                    sample_id,\n                    block,\n                )?;\n                access.generate(&mut self.writer.id_gen, coords, level_id, sample_id, block)\n            }\n            crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => self\n                .write_conditional_image_access(\n                    image_id,\n                    coordinates,\n                    level_id,\n                    sample_id,\n                    block,\n                    &access,\n                )?,\n            crate::proc::BoundsCheckPolicy::Unchecked => access.generate(\n                &mut self.writer.id_gen,\n                coordinates.value_id,\n                level_id,\n                sample_id,\n                block,\n            ),\n        };\n\n        // For depth images, `ImageLoad` expressions produce a single f32,\n        // whereas the SPIR-V instructions always produce a vec4. So we may have\n        // to pull out the component we need.\n        let result_id = if result_type_id == access.result_type() {\n            // The instruction produced the type we expected. We can use\n            // its result as-is.\n            access_id\n        } else {\n            // For `ImageClass::Depth` images, SPIR-V gave us four components,\n            // but we only want the first one.\n            let component_id = self.gen_id();\n            block.body.push(Instruction::composite_extract(\n                result_type_id,\n                component_id,\n                access_id,\n                &[0],\n            ));\n            component_id\n        };\n\n        Ok(result_id)\n    }\n\n    /// Generate code for an `ImageSample` expression.\n    ///\n    /// The arguments are the components of an `Expression::ImageSample` variant.\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn write_image_sample(\n        &mut self,\n        result_type_id: Word,\n        image: Handle<crate::Expression>,\n        sampler: Handle<crate::Expression>,\n        gather: Option<crate::SwizzleComponent>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        offset: Option<Handle<crate::Expression>>,\n        level: crate::SampleLevel,\n        depth_ref: Option<Handle<crate::Expression>>,\n        clamp_to_edge: bool,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        use super::instructions::SampleLod;\n        // image\n        let image_id = self.get_handle_id(image);\n        let image_type = self.fun_info[image].ty.handle().unwrap();\n        // SPIR-V doesn't know about our `Depth` class, and it returns\n        // `vec4<f32>`, so we need to grab the first component out of it.\n        let needs_sub_access = match self.ir_module.types[image_type].inner {\n            crate::TypeInner::Image {\n                class: crate::ImageClass::Depth { .. },\n                ..\n            } => depth_ref.is_none() && gather.is_none(),\n            _ => false,\n        };\n        let sample_result_type_id = if needs_sub_access {\n            self.get_numeric_type_id(NumericType::Vector {\n                size: crate::VectorSize::Quad,\n                scalar: crate::Scalar::F32,\n            })\n        } else {\n            result_type_id\n        };\n\n        // OpTypeSampledImage\n        let image_type_id = self.get_handle_type_id(image_type);\n        let sampled_image_type_id =\n            self.get_type_id(LookupType::Local(LocalType::SampledImage { image_type_id }));\n\n        let sampler_id = self.get_handle_id(sampler);\n\n        let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;\n        let coordinates_id = if clamp_to_edge {\n            self.writer.require_any(\n                \"clamp sample coordinates to edge\",\n                &[spirv::Capability::ImageQuery],\n            )?;\n\n            // clamp_to_edge can only be used with Level 0, and no array offset, offset,\n            // depth_ref or gather. This should have been caught by validation. Rather\n            // than entirely duplicate validation code here just ensure the level is\n            // zero, as we rely on that to query the texture size in order to calculate\n            // the clamped coordinates.\n            if level != crate::SampleLevel::Zero {\n                return Err(Error::Validation(\n                    \"ImageSample::clamp_to_edge requires SampleLevel::Zero\",\n                ));\n            }\n\n            // Query the size of level 0 of the texture.\n            let image_size_id = self.gen_id();\n            let vec2u_type_id = self.writer.get_vec2u_type_id();\n            let const_zero_uint_id = self.writer.get_constant_scalar(crate::Literal::U32(0));\n            let mut query_inst = Instruction::image_query(\n                spirv::Op::ImageQuerySizeLod,\n                vec2u_type_id,\n                image_size_id,\n                image_id,\n            );\n            query_inst.add_operand(const_zero_uint_id);\n            block.body.push(query_inst);\n\n            let image_size_f_id = self.gen_id();\n            let vec2f_type_id = self.writer.get_vec2f_type_id();\n            block.body.push(Instruction::unary(\n                spirv::Op::ConvertUToF,\n                vec2f_type_id,\n                image_size_f_id,\n                image_size_id,\n            ));\n\n            // Calculate the top-left and bottom-right margin for clamping to. I.e. a\n            // half-texel from each side.\n            let const_0_5_f32_id = self.writer.get_constant_scalar(crate::Literal::F32(0.5));\n            let const_0_5_vec2f_id = self.writer.get_constant_composite(\n                LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                    size: crate::VectorSize::Bi,\n                    scalar: crate::Scalar::F32,\n                })),\n                &[const_0_5_f32_id, const_0_5_f32_id],\n            );\n\n            let margin_left_id = self.gen_id();\n            block.body.push(Instruction::binary(\n                spirv::Op::FDiv,\n                vec2f_type_id,\n                margin_left_id,\n                const_0_5_vec2f_id,\n                image_size_f_id,\n            ));\n\n            let const_1_f32_id = self.writer.get_constant_scalar(crate::Literal::F32(1.0));\n            let const_1_vec2f_id = self.writer.get_constant_composite(\n                LookupType::Local(LocalType::Numeric(NumericType::Vector {\n                    size: crate::VectorSize::Bi,\n                    scalar: crate::Scalar::F32,\n                })),\n                &[const_1_f32_id, const_1_f32_id],\n            );\n\n            let margin_right_id = self.gen_id();\n            block.body.push(Instruction::binary(\n                spirv::Op::FSub,\n                vec2f_type_id,\n                margin_right_id,\n                const_1_vec2f_id,\n                margin_left_id,\n            ));\n\n            // Clamp the coords to the calculated margins\n            let clamped_coords_id = self.gen_id();\n            block.body.push(Instruction::ext_inst_gl_op(\n                self.writer.gl450_ext_inst_id,\n                spirv::GlslStd450Op::NClamp,\n                vec2f_type_id,\n                clamped_coords_id,\n                &[coordinates.value_id, margin_left_id, margin_right_id],\n            ));\n\n            clamped_coords_id\n        } else {\n            coordinates.value_id\n        };\n\n        let sampled_image_id = self.gen_id();\n        block.body.push(Instruction::sampled_image(\n            sampled_image_type_id,\n            sampled_image_id,\n            image_id,\n            sampler_id,\n        ));\n        let id = self.gen_id();\n\n        let depth_id = depth_ref.map(|handle| self.cached[handle]);\n        let mut mask = spirv::ImageOperands::empty();\n        mask.set(spirv::ImageOperands::CONST_OFFSET, offset.is_some());\n\n        let mut main_instruction = match (level, gather) {\n            (_, Some(component)) => {\n                let component_id = self.get_index_constant(component as u32);\n                let mut inst = Instruction::image_gather(\n                    sample_result_type_id,\n                    id,\n                    sampled_image_id,\n                    coordinates_id,\n                    component_id,\n                    depth_id,\n                );\n                if !mask.is_empty() {\n                    inst.add_operand(mask.bits());\n                }\n                inst\n            }\n            (crate::SampleLevel::Zero, None) => {\n                let mut inst = Instruction::image_sample(\n                    sample_result_type_id,\n                    id,\n                    SampleLod::Explicit,\n                    sampled_image_id,\n                    coordinates_id,\n                    depth_id,\n                );\n\n                let zero_id = self.writer.get_constant_scalar(crate::Literal::F32(0.0));\n\n                mask |= spirv::ImageOperands::LOD;\n                inst.add_operand(mask.bits());\n                inst.add_operand(zero_id);\n\n                inst\n            }\n            (crate::SampleLevel::Auto, None) => {\n                let mut inst = Instruction::image_sample(\n                    sample_result_type_id,\n                    id,\n                    SampleLod::Implicit,\n                    sampled_image_id,\n                    coordinates_id,\n                    depth_id,\n                );\n                if !mask.is_empty() {\n                    inst.add_operand(mask.bits());\n                }\n                inst\n            }\n            (crate::SampleLevel::Exact(lod_handle), None) => {\n                let mut inst = Instruction::image_sample(\n                    sample_result_type_id,\n                    id,\n                    SampleLod::Explicit,\n                    sampled_image_id,\n                    coordinates_id,\n                    depth_id,\n                );\n\n                let mut lod_id = self.cached[lod_handle];\n                // SPIR-V expects the LOD to be a float for all image classes.\n                // lod_id, however, will be an integer for depth images,\n                // therefore we must do a conversion.\n                if matches!(\n                    self.ir_module.types[image_type].inner,\n                    crate::TypeInner::Image {\n                        class: crate::ImageClass::Depth { .. },\n                        ..\n                    }\n                ) {\n                    let lod_f32_id = self.gen_id();\n                    let f32_type_id =\n                        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32));\n                    let convert_op = match *self.fun_info[lod_handle]\n                        .ty\n                        .inner_with(&self.ir_module.types)\n                    {\n                        crate::TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Uint,\n                            width: 4,\n                        }) => spirv::Op::ConvertUToF,\n                        crate::TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Sint,\n                            width: 4,\n                        }) => spirv::Op::ConvertSToF,\n                        _ => unreachable!(),\n                    };\n                    block.body.push(Instruction::unary(\n                        convert_op,\n                        f32_type_id,\n                        lod_f32_id,\n                        lod_id,\n                    ));\n                    lod_id = lod_f32_id;\n                }\n                mask |= spirv::ImageOperands::LOD;\n                inst.add_operand(mask.bits());\n                inst.add_operand(lod_id);\n\n                inst\n            }\n            (crate::SampleLevel::Bias(bias_handle), None) => {\n                let mut inst = Instruction::image_sample(\n                    sample_result_type_id,\n                    id,\n                    SampleLod::Implicit,\n                    sampled_image_id,\n                    coordinates_id,\n                    depth_id,\n                );\n\n                let bias_id = self.cached[bias_handle];\n                mask |= spirv::ImageOperands::BIAS;\n                inst.add_operand(mask.bits());\n                inst.add_operand(bias_id);\n\n                inst\n            }\n            (crate::SampleLevel::Gradient { x, y }, None) => {\n                let mut inst = Instruction::image_sample(\n                    sample_result_type_id,\n                    id,\n                    SampleLod::Explicit,\n                    sampled_image_id,\n                    coordinates_id,\n                    depth_id,\n                );\n\n                let x_id = self.cached[x];\n                let y_id = self.cached[y];\n                mask |= spirv::ImageOperands::GRAD;\n                inst.add_operand(mask.bits());\n                inst.add_operand(x_id);\n                inst.add_operand(y_id);\n\n                inst\n            }\n        };\n\n        if let Some(offset_const) = offset {\n            let offset_id = self.cached[offset_const];\n            main_instruction.add_operand(offset_id);\n        }\n\n        block.body.push(main_instruction);\n\n        let id = if needs_sub_access {\n            let sub_id = self.gen_id();\n            block.body.push(Instruction::composite_extract(\n                result_type_id,\n                sub_id,\n                id,\n                &[0],\n            ));\n            sub_id\n        } else {\n            id\n        };\n\n        Ok(id)\n    }\n\n    /// Generate code for an `ImageQuery` expression.\n    ///\n    /// The arguments are the components of an `Expression::ImageQuery` variant.\n    pub(super) fn write_image_query(\n        &mut self,\n        result_type_id: Word,\n        image: Handle<crate::Expression>,\n        query: crate::ImageQuery,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        use crate::{ImageClass as Ic, ImageDimension as Id, ImageQuery as Iq};\n\n        let image_id = self.get_handle_id(image);\n        let image_type = self.fun_info[image].ty.handle().unwrap();\n        let (dim, arrayed, class) = match self.ir_module.types[image_type].inner {\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => (dim, arrayed, class),\n            _ => {\n                return Err(Error::Validation(\"image type\"));\n            }\n        };\n\n        self.writer\n            .require_any(\"image queries\", &[spirv::Capability::ImageQuery])?;\n\n        let id = match query {\n            Iq::Size { level } => {\n                let dim_coords = match dim {\n                    Id::D1 => 1,\n                    Id::D2 | Id::Cube => 2,\n                    Id::D3 => 3,\n                };\n                let array_coords = usize::from(arrayed);\n                let vector_size = match dim_coords + array_coords {\n                    2 => Some(crate::VectorSize::Bi),\n                    3 => Some(crate::VectorSize::Tri),\n                    4 => Some(crate::VectorSize::Quad),\n                    _ => None,\n                };\n                let vector_numeric_type = match vector_size {\n                    Some(size) => NumericType::Vector {\n                        size,\n                        scalar: crate::Scalar::U32,\n                    },\n                    None => NumericType::Scalar(crate::Scalar::U32),\n                };\n\n                let extended_size_type_id = self.get_numeric_type_id(vector_numeric_type);\n\n                let (query_op, level_id) = match class {\n                    Ic::Sampled { multi: true, .. }\n                    | Ic::Depth { multi: true }\n                    | Ic::Storage { .. } => (spirv::Op::ImageQuerySize, None),\n                    _ => {\n                        let level_id = match level {\n                            Some(expr) => self.cached[expr],\n                            None => self.get_index_constant(0),\n                        };\n                        (spirv::Op::ImageQuerySizeLod, Some(level_id))\n                    }\n                };\n\n                // The ID of the vector returned by SPIR-V, which contains the dimensions\n                // as well as the layer count.\n                let id_extended = self.gen_id();\n                let mut inst = Instruction::image_query(\n                    query_op,\n                    extended_size_type_id,\n                    id_extended,\n                    image_id,\n                );\n                if let Some(expr_id) = level_id {\n                    inst.add_operand(expr_id);\n                }\n                block.body.push(inst);\n\n                if result_type_id != extended_size_type_id {\n                    let id = self.gen_id();\n                    let components = match dim {\n                        // always pick the first component, and duplicate it for all 3 dimensions\n                        Id::Cube => &[0u32, 0][..],\n                        _ => &[0u32, 1, 2, 3][..dim_coords],\n                    };\n                    block.body.push(Instruction::vector_shuffle(\n                        result_type_id,\n                        id,\n                        id_extended,\n                        id_extended,\n                        components,\n                    ));\n\n                    id\n                } else {\n                    id_extended\n                }\n            }\n            Iq::NumLevels => {\n                let query_id = self.gen_id();\n                block.body.push(Instruction::image_query(\n                    spirv::Op::ImageQueryLevels,\n                    result_type_id,\n                    query_id,\n                    image_id,\n                ));\n\n                query_id\n            }\n            Iq::NumLayers => {\n                let vec_size = match dim {\n                    Id::D1 => crate::VectorSize::Bi,\n                    Id::D2 | Id::Cube => crate::VectorSize::Tri,\n                    Id::D3 => crate::VectorSize::Quad,\n                };\n                let extended_size_type_id = self.get_numeric_type_id(NumericType::Vector {\n                    size: vec_size,\n                    scalar: crate::Scalar::U32,\n                });\n                let id_extended = self.gen_id();\n                let mut inst = Instruction::image_query(\n                    spirv::Op::ImageQuerySizeLod,\n                    extended_size_type_id,\n                    id_extended,\n                    image_id,\n                );\n                inst.add_operand(self.get_index_constant(0));\n                block.body.push(inst);\n\n                let extract_id = self.gen_id();\n                block.body.push(Instruction::composite_extract(\n                    result_type_id,\n                    extract_id,\n                    id_extended,\n                    &[vec_size as u32 - 1],\n                ));\n\n                extract_id\n            }\n            Iq::NumSamples => {\n                let query_id = self.gen_id();\n                block.body.push(Instruction::image_query(\n                    spirv::Op::ImageQuerySamples,\n                    result_type_id,\n                    query_id,\n                    image_id,\n                ));\n\n                query_id\n            }\n        };\n\n        Ok(id)\n    }\n\n    pub(super) fn write_image_store(\n        &mut self,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        value: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        let image_id = self.get_handle_id(image);\n        let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;\n        let value_id = self.cached[value];\n\n        let write = Store { image_id, value_id };\n\n        match *self.fun_info[image].ty.inner_with(&self.ir_module.types) {\n            crate::TypeInner::Image {\n                class:\n                    crate::ImageClass::Storage {\n                        format: crate::StorageFormat::Bgra8Unorm,\n                        ..\n                    },\n                ..\n            } => self.writer.require_any(\n                \"Bgra8Unorm storage write\",\n                &[spirv::Capability::StorageImageWriteWithoutFormat],\n            )?,\n            _ => {}\n        }\n\n        write.generate(\n            &mut self.writer.id_gen,\n            coordinates.value_id,\n            None,\n            None,\n            block,\n        );\n\n        Ok(())\n    }\n\n    pub(super) fn write_image_atomic(\n        &mut self,\n        image: Handle<crate::Expression>,\n        coordinate: Handle<crate::Expression>,\n        array_index: Option<Handle<crate::Expression>>,\n        fun: crate::AtomicFunction,\n        value: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        let image_id = match self.ir_function.originating_global(image) {\n            Some(handle) => self.writer.global_variables[handle].var_id,\n            _ => return Err(Error::Validation(\"Unexpected image type\")),\n        };\n        let crate::TypeInner::Image { class, .. } =\n            *self.fun_info[image].ty.inner_with(&self.ir_module.types)\n        else {\n            return Err(Error::Validation(\"Invalid image type\"));\n        };\n        let crate::ImageClass::Storage { format, .. } = class else {\n            return Err(Error::Validation(\"Invalid image class\"));\n        };\n        let scalar = format.into();\n        let scalar_type_id = self.get_numeric_type_id(NumericType::Scalar(scalar));\n        let pointer_type_id = self.get_pointer_type_id(scalar_type_id, spirv::StorageClass::Image);\n        let signed = scalar.kind == crate::ScalarKind::Sint;\n        if scalar.width == 8 {\n            self.writer\n                .require_any(\"64 bit image atomics\", &[spirv::Capability::Int64Atomics])?;\n        }\n        let pointer_id = self.gen_id();\n        let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;\n        let sample_id = self.writer.get_constant_scalar(crate::Literal::U32(0));\n        block.body.push(Instruction::image_texel_pointer(\n            pointer_type_id,\n            pointer_id,\n            image_id,\n            coordinates.value_id,\n            sample_id,\n        ));\n\n        let op = match fun {\n            crate::AtomicFunction::Add => spirv::Op::AtomicIAdd,\n            crate::AtomicFunction::Subtract => spirv::Op::AtomicISub,\n            crate::AtomicFunction::And => spirv::Op::AtomicAnd,\n            crate::AtomicFunction::ExclusiveOr => spirv::Op::AtomicXor,\n            crate::AtomicFunction::InclusiveOr => spirv::Op::AtomicOr,\n            crate::AtomicFunction::Min if signed => spirv::Op::AtomicSMin,\n            crate::AtomicFunction::Min => spirv::Op::AtomicUMin,\n            crate::AtomicFunction::Max if signed => spirv::Op::AtomicSMax,\n            crate::AtomicFunction::Max => spirv::Op::AtomicUMax,\n            crate::AtomicFunction::Exchange { .. } => {\n                return Err(Error::Validation(\"Exchange atomics are not supported yet\"))\n            }\n        };\n        let result_type_id = self.get_expression_type_id(&self.fun_info[value].ty);\n        let id = self.gen_id();\n        let space = crate::AddressSpace::Handle;\n        let (semantics, scope) = space.to_spirv_semantics_and_scope();\n        let scope_constant_id = self.get_scope_constant(scope as u32);\n        let semantics_id = self.get_index_constant(semantics.bits());\n        let value_id = self.cached[value];\n\n        block.body.push(Instruction::image_atomic(\n            op,\n            result_type_id,\n            id,\n            pointer_id,\n            scope_constant_id,\n            semantics_id,\n            value_id,\n        ));\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/index.rs",
    "content": "/*!\nBounds-checking for SPIR-V output.\n*/\n\nuse super::{\n    helpers::{global_needs_wrapper, map_storage_class},\n    selection::Selection,\n    Block, BlockContext, Error, IdGenerator, Instruction, Word,\n};\nuse crate::{\n    arena::Handle,\n    proc::{index::GuardedIndex, BoundsCheckPolicy},\n};\n\n/// The results of performing a bounds check.\n///\n/// On success, [`write_bounds_check`](BlockContext::write_bounds_check)\n/// returns a value of this type. The caller can assume that the right\n/// policy has been applied, and simply do what the variant says.\n#[derive(Debug)]\npub(super) enum BoundsCheckResult {\n    /// The index is statically known and in bounds, with the given value.\n    KnownInBounds(u32),\n\n    /// The given instruction computes the index to be used.\n    ///\n    /// When [`BoundsCheckPolicy::Restrict`] is in force, this is a\n    /// clamped version of the index the user supplied.\n    ///\n    /// When [`BoundsCheckPolicy::Unchecked`] is in force, this is\n    /// simply the index the user supplied. This variant indicates\n    /// that we couldn't prove statically that the index was in\n    /// bounds; otherwise we would have returned [`KnownInBounds`].\n    ///\n    /// [`KnownInBounds`]: BoundsCheckResult::KnownInBounds\n    Computed(Word),\n\n    /// The given instruction computes a boolean condition which is true\n    /// if the index is in bounds.\n    ///\n    /// This is returned when [`BoundsCheckPolicy::ReadZeroSkipWrite`]\n    /// is in force.\n    Conditional {\n        /// The access should only be permitted if this value is true.\n        condition_id: Word,\n\n        /// The access should use this index value.\n        index_id: Word,\n    },\n}\n\n/// A value that we either know at translation time, or need to compute at runtime.\n#[derive(Copy, Clone)]\npub(super) enum MaybeKnown<T> {\n    /// The value is known at shader translation time.\n    Known(T),\n\n    /// The value is computed by the instruction with the given id.\n    Computed(Word),\n}\n\nimpl BlockContext<'_> {\n    /// Emit code to compute the length of a run-time array.\n    ///\n    /// Given `array`, an expression referring a runtime-sized array, return the\n    /// instruction id for the array's length.\n    ///\n    /// Runtime-sized arrays may only appear in the values of global\n    /// variables, which must have one of the following Naga types:\n    ///\n    /// 1. A runtime-sized array.\n    /// 2. A struct whose last member is a runtime-sized array.\n    /// 3. A binding array of 2.\n    ///\n    /// Thus, the expression `array` has the form of:\n    ///\n    /// - An optional [`AccessIndex`], for case 2, applied to...\n    /// - An optional [`Access`] or [`AccessIndex`], for case 3, applied to...\n    /// - A [`GlobalVariable`].\n    ///\n    /// The generated SPIR-V takes into account wrapped globals; see\n    /// [`back::spv::GlobalVariable`] for details.\n    ///\n    /// [`GlobalVariable`]: crate::Expression::GlobalVariable\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    /// [`Access`]: crate::Expression::Access\n    /// [`base`]: crate::Expression::Access::base\n    /// [`back::spv::GlobalVariable`]: super::GlobalVariable\n    pub(super) fn write_runtime_array_length(\n        &mut self,\n        array: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        // The index into the binding array, if any.\n        let binding_array_index_id: Option<Word>;\n\n        // The handle to the Naga IR global we're referring to.\n        let global_handle: Handle<crate::GlobalVariable>;\n\n        // At the Naga type level, if the runtime-sized array is the final member of a\n        // struct, this is that member's index.\n        //\n        // This does not cover wrappers: if this backend wrapped the Naga global's\n        // type in a synthetic SPIR-V struct (see `global_needs_wrapper`), this is\n        // `None`.\n        let opt_last_member_index: Option<u32>;\n\n        // Inspect `array` and decide whether we have a binding array and/or an\n        // enclosing struct.\n        match self.ir_function.expressions[array] {\n            crate::Expression::AccessIndex { base, index } => {\n                match self.ir_function.expressions[base] {\n                    crate::Expression::AccessIndex {\n                        base: base_outer,\n                        index: index_outer,\n                    } => match self.ir_function.expressions[base_outer] {\n                        // An `AccessIndex` of an `AccessIndex` must be a\n                        // binding array holding structs whose last members are\n                        // runtime-sized arrays.\n                        crate::Expression::GlobalVariable(handle) => {\n                            let index_id = self.get_index_constant(index_outer);\n                            binding_array_index_id = Some(index_id);\n                            global_handle = handle;\n                            opt_last_member_index = Some(index);\n                        }\n                        _ => {\n                            return Err(Error::Validation(\n                                \"array length expression: AccessIndex(AccessIndex(Global))\",\n                            ))\n                        }\n                    },\n                    crate::Expression::Access {\n                        base: base_outer,\n                        index: index_outer,\n                    } => match self.ir_function.expressions[base_outer] {\n                        // Similarly, an `AccessIndex` of an `Access` must be a\n                        // binding array holding structs whose last members are\n                        // runtime-sized arrays.\n                        crate::Expression::GlobalVariable(handle) => {\n                            let index_id = self.cached[index_outer];\n                            binding_array_index_id = Some(index_id);\n                            global_handle = handle;\n                            opt_last_member_index = Some(index);\n                        }\n                        _ => {\n                            return Err(Error::Validation(\n                                \"array length expression: AccessIndex(Access(Global))\",\n                            ))\n                        }\n                    },\n                    crate::Expression::GlobalVariable(handle) => {\n                        // An outer `AccessIndex` applied directly to a\n                        // `GlobalVariable`. Since binding arrays can only contain\n                        // structs, this must be referring to the last member of a\n                        // struct that is a runtime-sized array.\n                        binding_array_index_id = None;\n                        global_handle = handle;\n                        opt_last_member_index = Some(index);\n                    }\n                    _ => {\n                        return Err(Error::Validation(\n                            \"array length expression: AccessIndex(<unexpected>)\",\n                        ))\n                    }\n                }\n            }\n            crate::Expression::GlobalVariable(handle) => {\n                // A direct reference to a global variable. This must hold the\n                // runtime-sized array directly.\n                binding_array_index_id = None;\n                global_handle = handle;\n                opt_last_member_index = None;\n            }\n            _ => return Err(Error::Validation(\"array length expression case-4\")),\n        };\n\n        // The verifier should have checked this, but make sure the inspection above\n        // agrees with the type about whether a binding array is involved.\n        //\n        // Eventually we do want to support `binding_array<array<T>>`. This check\n        // ensures that whoever relaxes the validator will get an error message from\n        // us, not just bogus SPIR-V.\n        let global = &self.ir_module.global_variables[global_handle];\n        match (\n            &self.ir_module.types[global.ty].inner,\n            binding_array_index_id,\n        ) {\n            (&crate::TypeInner::BindingArray { .. }, Some(_)) => {}\n            (_, None) => {}\n            _ => {\n                return Err(Error::Validation(\n                    \"array length expression: bad binding array inference\",\n                ))\n            }\n        }\n\n        // SPIR-V allows runtime-sized arrays to appear only as the last member of a\n        // struct. Determine this member's index.\n        let gvar = self.writer.global_variables[global_handle].clone();\n        let global = &self.ir_module.global_variables[global_handle];\n        let needs_wrapper = global_needs_wrapper(self.ir_module, global);\n        let (last_member_index, gvar_id) = match (opt_last_member_index, needs_wrapper) {\n            (Some(index), false) => {\n                // At the Naga type level, the runtime-sized array appears as the\n                // final member of a struct, whose index is `index`. We didn't need to\n                // wrap this, since the Naga type meets SPIR-V's requirements already.\n                (index, gvar.access_id)\n            }\n            (None, true) => {\n                // At the Naga type level, the runtime-sized array does not appear\n                // within a struct. We wrapped this in an OpTypeStruct with nothing\n                // else in it, so the index is zero. OpArrayLength wants the pointer\n                // to the wrapper struct, so use `gvar.var_id`.\n                (0, gvar.var_id)\n            }\n            _ => {\n                return Err(Error::Validation(\n                    \"array length expression: bad SPIR-V wrapper struct inference\",\n                ));\n            }\n        };\n\n        let structure_id = match binding_array_index_id {\n            // We are indexing inside a binding array, generate the access op.\n            Some(index_id) => {\n                let element_type_id = match self.ir_module.types[global.ty].inner {\n                    crate::TypeInner::BindingArray { base, size: _ } => {\n                        let base_id = self.get_handle_type_id(base);\n                        let class = map_storage_class(global.space);\n                        self.get_pointer_type_id(base_id, class)\n                    }\n                    _ => return Err(Error::Validation(\"array length expression case-5\")),\n                };\n                let structure_id = self.gen_id();\n                block.body.push(Instruction::access_chain(\n                    element_type_id,\n                    structure_id,\n                    gvar_id,\n                    &[index_id],\n                ));\n                structure_id\n            }\n            None => gvar_id,\n        };\n        let length_id = self.gen_id();\n        block.body.push(Instruction::array_length(\n            self.writer.get_u32_type_id(),\n            length_id,\n            structure_id,\n            last_member_index,\n        ));\n\n        Ok(length_id)\n    }\n\n    /// Compute the length of a subscriptable value.\n    ///\n    /// Given `sequence`, an expression referring to some indexable type, return\n    /// its length. The result may either be computed by SPIR-V instructions, or\n    /// known at shader translation time.\n    ///\n    /// `sequence` may be a `Vector`, `Matrix`, or `Array`, a `Pointer` to any\n    /// of those, or a `ValuePointer`. An array may be fixed-size, dynamically\n    /// sized, or use a specializable constant as its length.\n    fn write_sequence_length(\n        &mut self,\n        sequence: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<MaybeKnown<u32>, Error> {\n        let sequence_ty = self.fun_info[sequence].ty.inner_with(&self.ir_module.types);\n        match sequence_ty.indexable_length_resolved(self.ir_module) {\n            Ok(crate::proc::IndexableLength::Known(known_length)) => {\n                Ok(MaybeKnown::Known(known_length))\n            }\n            Ok(crate::proc::IndexableLength::Dynamic) => {\n                let length_id = self.write_runtime_array_length(sequence, block)?;\n                Ok(MaybeKnown::Computed(length_id))\n            }\n            Err(err) => {\n                log::error!(\"Sequence length for {sequence:?} failed: {err}\");\n                Err(Error::Validation(\"indexable length\"))\n            }\n        }\n    }\n\n    /// Compute the maximum valid index of a subscriptable value.\n    ///\n    /// Given `sequence`, an expression referring to some indexable type, return\n    /// its maximum valid index - one less than its length. The result may\n    /// either be computed, or known at shader translation time.\n    ///\n    /// `sequence` may be a `Vector`, `Matrix`, or `Array`, a `Pointer` to any\n    /// of those, or a `ValuePointer`. An array may be fixed-size, dynamically\n    /// sized, or use a specializable constant as its length.\n    fn write_sequence_max_index(\n        &mut self,\n        sequence: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<MaybeKnown<u32>, Error> {\n        match self.write_sequence_length(sequence, block)? {\n            MaybeKnown::Known(known_length) => {\n                // We should have thrown out all attempts to subscript zero-length\n                // sequences during validation, so the following subtraction should never\n                // underflow.\n                assert!(known_length > 0);\n                // Compute the max index from the length now.\n                Ok(MaybeKnown::Known(known_length - 1))\n            }\n            MaybeKnown::Computed(length_id) => {\n                // Emit code to compute the max index from the length.\n                let const_one_id = self.get_index_constant(1);\n                let max_index_id = self.gen_id();\n                block.body.push(Instruction::binary(\n                    spirv::Op::ISub,\n                    self.writer.get_u32_type_id(),\n                    max_index_id,\n                    length_id,\n                    const_one_id,\n                ));\n                Ok(MaybeKnown::Computed(max_index_id))\n            }\n        }\n    }\n\n    /// Restrict an index to be in range for a vector, matrix, or array.\n    ///\n    /// This is used to implement `BoundsCheckPolicy::Restrict`. An in-bounds\n    /// index is left unchanged. An out-of-bounds index is replaced with some\n    /// arbitrary in-bounds index. Note,this is not necessarily clamping; for\n    /// example, negative indices might be changed to refer to the last element\n    /// of the sequence, not the first, as clamping would do.\n    ///\n    /// Either return the restricted index value, if known, or add instructions\n    /// to `block` to compute it, and return the id of the result. See the\n    /// documentation for `BoundsCheckResult` for details.\n    ///\n    /// The `sequence` expression may be a `Vector`, `Matrix`, or `Array`, a\n    /// `Pointer` to any of those, or a `ValuePointer`. An array may be\n    /// fixed-size, dynamically sized, or use a specializable constant as its\n    /// length.\n    pub(super) fn write_restricted_index(\n        &mut self,\n        sequence: Handle<crate::Expression>,\n        index: GuardedIndex,\n        block: &mut Block,\n    ) -> Result<BoundsCheckResult, Error> {\n        let max_index = self.write_sequence_max_index(sequence, block)?;\n\n        // If both are known, we can compute the index to be used\n        // right now.\n        if let (GuardedIndex::Known(index), MaybeKnown::Known(max_index)) = (index, max_index) {\n            let restricted = core::cmp::min(index, max_index);\n            return Ok(BoundsCheckResult::KnownInBounds(restricted));\n        }\n\n        let index_id = match index {\n            GuardedIndex::Known(value) => self.get_index_constant(value),\n            GuardedIndex::Expression(expr) => self.cached[expr],\n        };\n\n        let max_index_id = match max_index {\n            MaybeKnown::Known(value) => self.get_index_constant(value),\n            MaybeKnown::Computed(id) => id,\n        };\n\n        // One or the other of the index or length is dynamic, so emit code for\n        // BoundsCheckPolicy::Restrict.\n        let restricted_index_id = self.gen_id();\n        block.body.push(Instruction::ext_inst_gl_op(\n            self.writer.gl450_ext_inst_id,\n            spirv::GlslStd450Op::UMin,\n            self.writer.get_u32_type_id(),\n            restricted_index_id,\n            &[index_id, max_index_id],\n        ));\n        Ok(BoundsCheckResult::Computed(restricted_index_id))\n    }\n\n    /// Write an index bounds comparison to `block`, if needed.\n    ///\n    /// This is used to implement [`BoundsCheckPolicy::ReadZeroSkipWrite`].\n    ///\n    /// If we're able to determine statically that `index` is in bounds for\n    /// `sequence`, return `KnownInBounds(value)`, where `value` is the actual\n    /// value of the index. (In principle, one could know that the index is in\n    /// bounds without knowing its specific value, but in our simple-minded\n    /// situation, we always know it.)\n    ///\n    /// If instead we must generate code to perform the comparison at run time,\n    /// return `Conditional(comparison_id)`, where `comparison_id` is an\n    /// instruction producing a boolean value that is true if `index` is in\n    /// bounds for `sequence`.\n    ///\n    /// The `sequence` expression may be a `Vector`, `Matrix`, or `Array`, a\n    /// `Pointer` to any of those, or a `ValuePointer`. An array may be\n    /// fixed-size, dynamically sized, or use a specializable constant as its\n    /// length.\n    fn write_index_comparison(\n        &mut self,\n        sequence: Handle<crate::Expression>,\n        index: GuardedIndex,\n        block: &mut Block,\n    ) -> Result<BoundsCheckResult, Error> {\n        let length = self.write_sequence_length(sequence, block)?;\n\n        // If both are known, we can decide whether the index is in\n        // bounds right now.\n        if let (GuardedIndex::Known(index), MaybeKnown::Known(length)) = (index, length) {\n            if index < length {\n                return Ok(BoundsCheckResult::KnownInBounds(index));\n            }\n\n            // In theory, when `index` is bad, we could return a new\n            // `KnownOutOfBounds` variant here. But it's simpler just to fall\n            // through and let the bounds check take place. The shader is broken\n            // anyway, so it doesn't make sense to invest in emitting the ideal\n            // code for it.\n        }\n\n        let index_id = match index {\n            GuardedIndex::Known(value) => self.get_index_constant(value),\n            GuardedIndex::Expression(expr) => self.cached[expr],\n        };\n\n        let length_id = match length {\n            MaybeKnown::Known(value) => self.get_index_constant(value),\n            MaybeKnown::Computed(id) => id,\n        };\n\n        // Compare the index against the length.\n        let condition_id = self.gen_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::ULessThan,\n            self.writer.get_bool_type_id(),\n            condition_id,\n            index_id,\n            length_id,\n        ));\n\n        // Indicate that we did generate the check.\n        Ok(BoundsCheckResult::Conditional {\n            condition_id,\n            index_id,\n        })\n    }\n\n    /// Emit a conditional load for `BoundsCheckPolicy::ReadZeroSkipWrite`.\n    ///\n    /// Generate code to load a value of `result_type` if `condition` is true,\n    /// and generate a null value of that type if it is false. Call `emit_load`\n    /// to emit the instructions to perform the load. Return the id of the\n    /// merged value of the two branches.\n    pub(super) fn write_conditional_indexed_load<F>(\n        &mut self,\n        result_type: Word,\n        condition: Word,\n        block: &mut Block,\n        emit_load: F,\n    ) -> Word\n    where\n        F: FnOnce(&mut IdGenerator, &mut Block) -> Word,\n    {\n        // For the out-of-bounds case, we produce a zero value.\n        let null_id = self.writer.get_constant_null(result_type);\n\n        let mut selection = Selection::start(block, result_type);\n\n        // As it turns out, we don't actually need a full 'if-then-else'\n        // structure for this: SPIR-V constants are declared up front, so the\n        // 'else' block would have no instructions. Instead we emit something\n        // like this:\n        //\n        //     result = zero;\n        //     if in_bounds {\n        //         result = do the load;\n        //     }\n        //     use result;\n\n        // Continue only if the index was in bounds. Otherwise, branch to the\n        // merge block.\n        selection.if_true(self, condition, null_id);\n\n        // The in-bounds path. Perform the access and the load.\n        let loaded_value = emit_load(&mut self.writer.id_gen, selection.block());\n\n        selection.finish(self, loaded_value)\n    }\n\n    /// Emit code for bounds checks for an array, vector, or matrix access.\n    ///\n    /// This tries to handle all the critical steps for bounds checks:\n    ///\n    /// - First, select the appropriate bounds check policy for `base`,\n    ///   depending on its address space.\n    ///\n    /// - Next, analyze `index` to see if its value is known at\n    ///   compile time, in which case we can decide statically whether\n    ///   the index is in bounds.\n    ///\n    /// - If the index's value is not known at compile time, emit code to:\n    ///\n    ///     - restrict its value (for [`BoundsCheckPolicy::Restrict`]), or\n    ///\n    ///     - check whether it's in bounds (for\n    ///       [`BoundsCheckPolicy::ReadZeroSkipWrite`]).\n    ///\n    /// Return a [`BoundsCheckResult`] indicating how the index should be\n    /// consumed. See that type's documentation for details.\n    pub(super) fn write_bounds_check(\n        &mut self,\n        base: Handle<crate::Expression>,\n        mut index: GuardedIndex,\n        block: &mut Block,\n    ) -> Result<BoundsCheckResult, Error> {\n        // If the value of `index` is known at compile time, find it now.\n        index.try_resolve_to_constant(&self.ir_function.expressions, self.ir_module);\n\n        let policy = self.writer.bounds_check_policies.choose_policy(\n            base,\n            &self.ir_module.types,\n            self.fun_info,\n        );\n\n        Ok(match policy {\n            BoundsCheckPolicy::Restrict => self.write_restricted_index(base, index, block)?,\n            BoundsCheckPolicy::ReadZeroSkipWrite => {\n                self.write_index_comparison(base, index, block)?\n            }\n            BoundsCheckPolicy::Unchecked => match index {\n                GuardedIndex::Known(value) => BoundsCheckResult::KnownInBounds(value),\n                GuardedIndex::Expression(expr) => BoundsCheckResult::Computed(self.cached[expr]),\n            },\n        })\n    }\n\n    /// Emit code to subscript a vector by value with a computed index.\n    ///\n    /// Return the id of the element value.\n    ///\n    /// If `base_id_override` is provided, it is used as the vector expression\n    /// to be subscripted into, rather than the cached value of `base`.\n    pub(super) fn write_vector_access(\n        &mut self,\n        result_type_id: Word,\n        base: Handle<crate::Expression>,\n        base_id_override: Option<Word>,\n        index: GuardedIndex,\n        block: &mut Block,\n    ) -> Result<Word, Error> {\n        let base_id = base_id_override.unwrap_or_else(|| self.cached[base]);\n\n        let result_id = match self.write_bounds_check(base, index, block)? {\n            BoundsCheckResult::KnownInBounds(known_index) => {\n                let result_id = self.gen_id();\n                block.body.push(Instruction::composite_extract(\n                    result_type_id,\n                    result_id,\n                    base_id,\n                    &[known_index],\n                ));\n                result_id\n            }\n            BoundsCheckResult::Computed(computed_index_id) => {\n                let result_id = self.gen_id();\n                block.body.push(Instruction::vector_extract_dynamic(\n                    result_type_id,\n                    result_id,\n                    base_id,\n                    computed_index_id,\n                ));\n                result_id\n            }\n            BoundsCheckResult::Conditional {\n                condition_id,\n                index_id,\n            } => {\n                // Run-time bounds checks were required. Emit\n                // conditional load.\n                self.write_conditional_indexed_load(\n                    result_type_id,\n                    condition_id,\n                    block,\n                    |id_gen, block| {\n                        // The in-bounds path. Generate the access.\n                        let element_id = id_gen.next();\n                        block.body.push(Instruction::vector_extract_dynamic(\n                            result_type_id,\n                            element_id,\n                            base_id,\n                            index_id,\n                        ));\n                        element_id\n                    },\n                )\n            }\n        };\n\n        Ok(result_id)\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/instructions.rs",
    "content": "use alloc::{vec, vec::Vec};\n\nuse spirv::{Op, Word};\n\nuse super::{block::DebugInfoInner, helpers};\n\npub(super) enum Signedness {\n    Unsigned = 0,\n    Signed = 1,\n}\n\npub(super) enum SampleLod {\n    Explicit,\n    Implicit,\n}\n\npub(super) struct Case {\n    pub value: Word,\n    pub label_id: Word,\n}\n\nimpl super::Instruction {\n    //\n    //  Debug Instructions\n    //\n\n    pub(super) fn string(name: &str, id: Word) -> Self {\n        let mut instruction = Self::new(Op::String);\n        instruction.set_result(id);\n        instruction.add_operands(helpers::string_to_words(name));\n        instruction\n    }\n\n    pub(super) fn source(\n        source_language: spirv::SourceLanguage,\n        version: u32,\n        source: &Option<DebugInfoInner>,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Source);\n        instruction.add_operand(source_language as u32);\n        instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes()));\n        if let Some(source) = source.as_ref() {\n            instruction.add_operand(source.source_file_id);\n            instruction.add_operands(helpers::string_to_words(source.source_code));\n        }\n        instruction\n    }\n\n    pub(super) fn source_continued(source: &[u8]) -> Self {\n        let mut instruction = Self::new(Op::SourceContinued);\n        instruction.add_operands(helpers::str_bytes_to_words(source));\n        instruction\n    }\n\n    pub(super) fn source_auto_continued(\n        source_language: spirv::SourceLanguage,\n        version: u32,\n        source: &Option<DebugInfoInner>,\n    ) -> Vec<Self> {\n        let mut instructions = vec![];\n\n        let with_continue = source.as_ref().and_then(|debug_info| {\n            (debug_info.source_code.len() > u16::MAX as usize).then_some(debug_info)\n        });\n        if let Some(debug_info) = with_continue {\n            let mut instruction = Self::new(Op::Source);\n            instruction.add_operand(source_language as u32);\n            instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes()));\n\n            let words = helpers::string_to_byte_chunks(debug_info.source_code, u16::MAX as usize);\n            instruction.add_operand(debug_info.source_file_id);\n            instruction.add_operands(helpers::str_bytes_to_words(words[0]));\n            instructions.push(instruction);\n            for word_bytes in words[1..].iter() {\n                let instruction_continue = Self::source_continued(word_bytes);\n                instructions.push(instruction_continue);\n            }\n        } else {\n            let instruction = Self::source(source_language, version, source);\n            instructions.push(instruction);\n        }\n        instructions\n    }\n\n    pub(super) fn name(target_id: Word, name: &str) -> Self {\n        let mut instruction = Self::new(Op::Name);\n        instruction.add_operand(target_id);\n        instruction.add_operands(helpers::string_to_words(name));\n        instruction\n    }\n\n    pub(super) fn member_name(target_id: Word, member: Word, name: &str) -> Self {\n        let mut instruction = Self::new(Op::MemberName);\n        instruction.add_operand(target_id);\n        instruction.add_operand(member);\n        instruction.add_operands(helpers::string_to_words(name));\n        instruction\n    }\n\n    pub(super) fn line(file: Word, line: Word, column: Word) -> Self {\n        let mut instruction = Self::new(Op::Line);\n        instruction.add_operand(file);\n        instruction.add_operand(line);\n        instruction.add_operand(column);\n        instruction\n    }\n\n    //\n    //  Annotation Instructions\n    //\n\n    pub(super) fn decorate(\n        target_id: Word,\n        decoration: spirv::Decoration,\n        operands: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::Decorate);\n        instruction.add_operand(target_id);\n        instruction.add_operand(decoration as u32);\n        for operand in operands {\n            instruction.add_operand(*operand)\n        }\n        instruction\n    }\n\n    pub(super) fn member_decorate(\n        target_id: Word,\n        member_index: Word,\n        decoration: spirv::Decoration,\n        operands: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::MemberDecorate);\n        instruction.add_operand(target_id);\n        instruction.add_operand(member_index);\n        instruction.add_operand(decoration as u32);\n        for operand in operands {\n            instruction.add_operand(*operand)\n        }\n        instruction\n    }\n\n    //\n    //  Extension Instructions\n    //\n\n    pub(super) fn extension(name: &str) -> Self {\n        let mut instruction = Self::new(Op::Extension);\n        instruction.add_operands(helpers::string_to_words(name));\n        instruction\n    }\n\n    pub(super) fn ext_inst_import(id: Word, name: &str) -> Self {\n        let mut instruction = Self::new(Op::ExtInstImport);\n        instruction.set_result(id);\n        instruction.add_operands(helpers::string_to_words(name));\n        instruction\n    }\n\n    pub(super) fn ext_inst_gl_op(\n        set_id: Word,\n        op: spirv::GlslStd450Op,\n        result_type_id: Word,\n        id: Word,\n        operands: &[Word],\n    ) -> Self {\n        Self::ext_inst(set_id, op as u32, result_type_id, id, operands)\n    }\n\n    pub(super) fn ext_inst(\n        set_id: Word,\n        op: u32,\n        result_type_id: Word,\n        id: Word,\n        operands: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::ExtInst);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(set_id);\n        instruction.add_operand(op);\n        for operand in operands {\n            instruction.add_operand(*operand)\n        }\n        instruction\n    }\n\n    //\n    //  Mode-Setting Instructions\n    //\n\n    pub(super) fn memory_model(\n        addressing_model: spirv::AddressingModel,\n        memory_model: spirv::MemoryModel,\n    ) -> Self {\n        let mut instruction = Self::new(Op::MemoryModel);\n        instruction.add_operand(addressing_model as u32);\n        instruction.add_operand(memory_model as u32);\n        instruction\n    }\n\n    pub(super) fn entry_point(\n        execution_model: spirv::ExecutionModel,\n        entry_point_id: Word,\n        name: &str,\n        interface_ids: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::EntryPoint);\n        instruction.add_operand(execution_model as u32);\n        instruction.add_operand(entry_point_id);\n        instruction.add_operands(helpers::string_to_words(name));\n\n        for interface_id in interface_ids {\n            instruction.add_operand(*interface_id);\n        }\n\n        instruction\n    }\n\n    pub(super) fn execution_mode(\n        entry_point_id: Word,\n        execution_mode: spirv::ExecutionMode,\n        args: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::ExecutionMode);\n        instruction.add_operand(entry_point_id);\n        instruction.add_operand(execution_mode as u32);\n        for arg in args {\n            instruction.add_operand(*arg);\n        }\n        instruction\n    }\n\n    pub(super) fn capability(capability: spirv::Capability) -> Self {\n        let mut instruction = Self::new(Op::Capability);\n        instruction.add_operand(capability as u32);\n        instruction\n    }\n\n    //\n    //  Type-Declaration Instructions\n    //\n\n    pub(super) fn type_void(id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeVoid);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn type_bool(id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeBool);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn type_int(id: Word, width: Word, signedness: Signedness) -> Self {\n        let mut instruction = Self::new(Op::TypeInt);\n        instruction.set_result(id);\n        instruction.add_operand(width);\n        instruction.add_operand(signedness as u32);\n        instruction\n    }\n\n    pub(super) fn type_float(id: Word, width: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeFloat);\n        instruction.set_result(id);\n        instruction.add_operand(width);\n        instruction\n    }\n\n    pub(super) fn type_vector(\n        id: Word,\n        component_type_id: Word,\n        component_count: crate::VectorSize,\n    ) -> Self {\n        let mut instruction = Self::new(Op::TypeVector);\n        instruction.set_result(id);\n        instruction.add_operand(component_type_id);\n        instruction.add_operand(component_count as u32);\n        instruction\n    }\n\n    pub(super) fn type_matrix(\n        id: Word,\n        column_type_id: Word,\n        column_count: crate::VectorSize,\n    ) -> Self {\n        let mut instruction = Self::new(Op::TypeMatrix);\n        instruction.set_result(id);\n        instruction.add_operand(column_type_id);\n        instruction.add_operand(column_count as u32);\n        instruction\n    }\n\n    pub(super) fn type_coop_matrix(\n        id: Word,\n        scalar_type_id: Word,\n        scope_id: Word,\n        row_count_id: Word,\n        column_count_id: Word,\n        matrix_use_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::TypeCooperativeMatrixKHR);\n        instruction.set_result(id);\n        instruction.add_operand(scalar_type_id);\n        instruction.add_operand(scope_id);\n        instruction.add_operand(row_count_id);\n        instruction.add_operand(column_count_id);\n        instruction.add_operand(matrix_use_id);\n        instruction\n    }\n\n    pub(super) fn type_image(\n        id: Word,\n        sampled_type_id: Word,\n        dim: spirv::Dim,\n        flags: super::ImageTypeFlags,\n        image_format: spirv::ImageFormat,\n    ) -> Self {\n        let mut instruction = Self::new(Op::TypeImage);\n        instruction.set_result(id);\n        instruction.add_operand(sampled_type_id);\n        instruction.add_operand(dim as u32);\n        instruction.add_operand(flags.contains(super::ImageTypeFlags::DEPTH) as u32);\n        instruction.add_operand(flags.contains(super::ImageTypeFlags::ARRAYED) as u32);\n        instruction.add_operand(flags.contains(super::ImageTypeFlags::MULTISAMPLED) as u32);\n        instruction.add_operand(if flags.contains(super::ImageTypeFlags::SAMPLED) {\n            1\n        } else {\n            2\n        });\n        instruction.add_operand(image_format as u32);\n        instruction\n    }\n\n    pub(super) fn type_sampler(id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeSampler);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn type_acceleration_structure(id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeAccelerationStructureKHR);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn type_ray_query(id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeRayQueryKHR);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn type_sampled_image(id: Word, image_type_id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeSampledImage);\n        instruction.set_result(id);\n        instruction.add_operand(image_type_id);\n        instruction\n    }\n\n    pub(super) fn type_array(id: Word, element_type_id: Word, length_id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeArray);\n        instruction.set_result(id);\n        instruction.add_operand(element_type_id);\n        instruction.add_operand(length_id);\n        instruction\n    }\n\n    pub(super) fn type_runtime_array(id: Word, element_type_id: Word) -> Self {\n        let mut instruction = Self::new(Op::TypeRuntimeArray);\n        instruction.set_result(id);\n        instruction.add_operand(element_type_id);\n        instruction\n    }\n\n    pub(super) fn type_struct(id: Word, member_ids: &[Word]) -> Self {\n        let mut instruction = Self::new(Op::TypeStruct);\n        instruction.set_result(id);\n\n        for member_id in member_ids {\n            instruction.add_operand(*member_id)\n        }\n\n        instruction\n    }\n\n    pub(super) fn type_pointer(\n        id: Word,\n        storage_class: spirv::StorageClass,\n        type_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::TypePointer);\n        instruction.set_result(id);\n        instruction.add_operand(storage_class as u32);\n        instruction.add_operand(type_id);\n        instruction\n    }\n\n    pub(super) fn type_function(id: Word, return_type_id: Word, parameter_ids: &[Word]) -> Self {\n        let mut instruction = Self::new(Op::TypeFunction);\n        instruction.set_result(id);\n        instruction.add_operand(return_type_id);\n\n        for parameter_id in parameter_ids {\n            instruction.add_operand(*parameter_id);\n        }\n\n        instruction\n    }\n\n    //\n    //  Constant-Creation Instructions\n    //\n\n    pub(super) fn constant_null(result_type_id: Word, id: Word) -> Self {\n        let mut instruction = Self::new(Op::ConstantNull);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn constant_true(result_type_id: Word, id: Word) -> Self {\n        let mut instruction = Self::new(Op::ConstantTrue);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn constant_false(result_type_id: Word, id: Word) -> Self {\n        let mut instruction = Self::new(Op::ConstantFalse);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn constant_16bit(result_type_id: Word, id: Word, low: Word) -> Self {\n        Self::constant(result_type_id, id, &[low])\n    }\n\n    pub(super) fn constant_32bit(result_type_id: Word, id: Word, value: Word) -> Self {\n        Self::constant(result_type_id, id, &[value])\n    }\n\n    pub(super) fn constant_64bit(result_type_id: Word, id: Word, low: Word, high: Word) -> Self {\n        Self::constant(result_type_id, id, &[low, high])\n    }\n\n    pub(super) fn constant(result_type_id: Word, id: Word, values: &[Word]) -> Self {\n        let mut instruction = Self::new(Op::Constant);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n\n        for value in values {\n            instruction.add_operand(*value);\n        }\n\n        instruction\n    }\n\n    pub(super) fn constant_composite(\n        result_type_id: Word,\n        id: Word,\n        constituent_ids: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::ConstantComposite);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n\n        for constituent_id in constituent_ids {\n            instruction.add_operand(*constituent_id);\n        }\n\n        instruction\n    }\n\n    //\n    //  Memory Instructions\n    //\n\n    pub(super) fn variable(\n        result_type_id: Word,\n        id: Word,\n        storage_class: spirv::StorageClass,\n        initializer_id: Option<Word>,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Variable);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(storage_class as u32);\n\n        if let Some(initializer_id) = initializer_id {\n            instruction.add_operand(initializer_id);\n        }\n\n        instruction\n    }\n\n    pub(super) fn load(\n        result_type_id: Word,\n        id: Word,\n        pointer_id: Word,\n        memory_access: Option<spirv::MemoryAccess>,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Load);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(pointer_id);\n\n        if let Some(memory_access) = memory_access {\n            instruction.add_operand(memory_access.bits());\n        }\n\n        instruction\n    }\n\n    pub(super) fn atomic_load(\n        result_type_id: Word,\n        id: Word,\n        pointer_id: Word,\n        scope_id: Word,\n        semantics_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::AtomicLoad);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(pointer_id);\n        instruction.add_operand(scope_id);\n        instruction.add_operand(semantics_id);\n        instruction\n    }\n\n    pub(super) fn store(\n        pointer_id: Word,\n        value_id: Word,\n        memory_access: Option<spirv::MemoryAccess>,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Store);\n        instruction.add_operand(pointer_id);\n        instruction.add_operand(value_id);\n\n        if let Some(memory_access) = memory_access {\n            instruction.add_operand(memory_access.bits());\n        }\n\n        instruction\n    }\n\n    pub(super) fn atomic_store(\n        pointer_id: Word,\n        scope_id: Word,\n        semantics_id: Word,\n        value_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::AtomicStore);\n        instruction.add_operand(pointer_id);\n        instruction.add_operand(scope_id);\n        instruction.add_operand(semantics_id);\n        instruction.add_operand(value_id);\n        instruction\n    }\n\n    pub(super) fn access_chain(\n        result_type_id: Word,\n        id: Word,\n        base_id: Word,\n        index_ids: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::AccessChain);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(base_id);\n\n        for index_id in index_ids {\n            instruction.add_operand(*index_id);\n        }\n\n        instruction\n    }\n\n    pub(super) fn array_length(\n        result_type_id: Word,\n        id: Word,\n        structure_id: Word,\n        array_member: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::ArrayLength);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(structure_id);\n        instruction.add_operand(array_member);\n        instruction\n    }\n\n    //\n    //  Function Instructions\n    //\n\n    pub(super) fn function(\n        return_type_id: Word,\n        id: Word,\n        function_control: spirv::FunctionControl,\n        function_type_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Function);\n        instruction.set_type(return_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(function_control.bits());\n        instruction.add_operand(function_type_id);\n        instruction\n    }\n\n    pub(super) fn function_parameter(result_type_id: Word, id: Word) -> Self {\n        let mut instruction = Self::new(Op::FunctionParameter);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) const fn function_end() -> Self {\n        Self::new(Op::FunctionEnd)\n    }\n\n    pub(super) fn function_call(\n        result_type_id: Word,\n        id: Word,\n        function_id: Word,\n        argument_ids: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::FunctionCall);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(function_id);\n\n        for argument_id in argument_ids {\n            instruction.add_operand(*argument_id);\n        }\n\n        instruction\n    }\n\n    //\n    //  Image Instructions\n    //\n\n    pub(super) fn sampled_image(\n        result_type_id: Word,\n        id: Word,\n        image: Word,\n        sampler: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::SampledImage);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(image);\n        instruction.add_operand(sampler);\n        instruction\n    }\n\n    pub(super) fn image_sample(\n        result_type_id: Word,\n        id: Word,\n        lod: SampleLod,\n        sampled_image: Word,\n        coordinates: Word,\n        depth_ref: Option<Word>,\n    ) -> Self {\n        let op = match (lod, depth_ref) {\n            (SampleLod::Explicit, None) => Op::ImageSampleExplicitLod,\n            (SampleLod::Implicit, None) => Op::ImageSampleImplicitLod,\n            (SampleLod::Explicit, Some(_)) => Op::ImageSampleDrefExplicitLod,\n            (SampleLod::Implicit, Some(_)) => Op::ImageSampleDrefImplicitLod,\n        };\n\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(sampled_image);\n        instruction.add_operand(coordinates);\n        if let Some(dref) = depth_ref {\n            instruction.add_operand(dref);\n        }\n\n        instruction\n    }\n\n    pub(super) fn image_gather(\n        result_type_id: Word,\n        id: Word,\n        sampled_image: Word,\n        coordinates: Word,\n        component_id: Word,\n        depth_ref: Option<Word>,\n    ) -> Self {\n        let op = match depth_ref {\n            None => Op::ImageGather,\n            Some(_) => Op::ImageDrefGather,\n        };\n\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(sampled_image);\n        instruction.add_operand(coordinates);\n        if let Some(dref) = depth_ref {\n            instruction.add_operand(dref);\n        } else {\n            instruction.add_operand(component_id);\n        }\n\n        instruction\n    }\n\n    pub(super) fn image_fetch_or_read(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        image: Word,\n        coordinates: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(image);\n        instruction.add_operand(coordinates);\n        instruction\n    }\n\n    pub(super) fn image_write(image: Word, coordinates: Word, value: Word) -> Self {\n        let mut instruction = Self::new(Op::ImageWrite);\n        instruction.add_operand(image);\n        instruction.add_operand(coordinates);\n        instruction.add_operand(value);\n        instruction\n    }\n\n    pub(super) fn image_texel_pointer(\n        result_type_id: Word,\n        id: Word,\n        image: Word,\n        coordinates: Word,\n        sample: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::ImageTexelPointer);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(image);\n        instruction.add_operand(coordinates);\n        instruction.add_operand(sample);\n        instruction\n    }\n\n    pub(super) fn image_atomic(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        pointer: Word,\n        scope_id: Word,\n        semantics_id: Word,\n        value: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(pointer);\n        instruction.add_operand(scope_id);\n        instruction.add_operand(semantics_id);\n        instruction.add_operand(value);\n        instruction\n    }\n\n    pub(super) fn image_query(op: Op, result_type_id: Word, id: Word, image: Word) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(image);\n        instruction\n    }\n\n    //\n    //  Ray Query Instructions\n    //\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn ray_query_initialize(\n        query: Word,\n        acceleration_structure: Word,\n        ray_flags: Word,\n        cull_mask: Word,\n        ray_origin: Word,\n        ray_tmin: Word,\n        ray_dir: Word,\n        ray_tmax: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::RayQueryInitializeKHR);\n        instruction.add_operand(query);\n        instruction.add_operand(acceleration_structure);\n        instruction.add_operand(ray_flags);\n        instruction.add_operand(cull_mask);\n        instruction.add_operand(ray_origin);\n        instruction.add_operand(ray_tmin);\n        instruction.add_operand(ray_dir);\n        instruction.add_operand(ray_tmax);\n        instruction\n    }\n\n    pub(super) fn ray_query_proceed(result_type_id: Word, id: Word, query: Word) -> Self {\n        let mut instruction = Self::new(Op::RayQueryProceedKHR);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(query);\n        instruction\n    }\n\n    pub(super) fn ray_query_generate_intersection(query: Word, hit: Word) -> Self {\n        let mut instruction = Self::new(Op::RayQueryGenerateIntersectionKHR);\n        instruction.add_operand(query);\n        instruction.add_operand(hit);\n        instruction\n    }\n\n    pub(super) fn ray_query_confirm_intersection(query: Word) -> Self {\n        let mut instruction = Self::new(Op::RayQueryConfirmIntersectionKHR);\n        instruction.add_operand(query);\n        instruction\n    }\n\n    pub(super) fn ray_query_return_vertex_position(\n        result_type_id: Word,\n        id: Word,\n        query: Word,\n        intersection: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::RayQueryGetIntersectionTriangleVertexPositionsKHR);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(query);\n        instruction.add_operand(intersection);\n        instruction\n    }\n\n    pub(super) fn ray_query_get_intersection(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        query: Word,\n        intersection: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(query);\n        instruction.add_operand(intersection);\n        instruction\n    }\n\n    pub(super) fn ray_query_get_t_min(result_type_id: Word, id: Word, query: Word) -> Self {\n        let mut instruction = Self::new(Op::RayQueryGetRayTMinKHR);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(query);\n        instruction\n    }\n\n    pub(super) fn ray_query_terminate(query: Word) -> Self {\n        let mut instruction = Self::new(Op::RayQueryTerminateKHR);\n        instruction.add_operand(query);\n        instruction\n    }\n\n    //\n    //  Conversion Instructions\n    //\n    pub(super) fn unary(op: Op, result_type_id: Word, id: Word, value: Word) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(value);\n        instruction\n    }\n\n    //\n    //  Composite Instructions\n    //\n\n    pub(super) fn composite_construct(\n        result_type_id: Word,\n        id: Word,\n        constituent_ids: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::CompositeConstruct);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n\n        for constituent_id in constituent_ids {\n            instruction.add_operand(*constituent_id);\n        }\n\n        instruction\n    }\n\n    pub(super) fn composite_extract(\n        result_type_id: Word,\n        id: Word,\n        composite_id: Word,\n        indices: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::CompositeExtract);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n\n        instruction.add_operand(composite_id);\n        for index in indices {\n            instruction.add_operand(*index);\n        }\n\n        instruction\n    }\n\n    pub(super) fn vector_extract_dynamic(\n        result_type_id: Word,\n        id: Word,\n        vector_id: Word,\n        index_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::VectorExtractDynamic);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n\n        instruction.add_operand(vector_id);\n        instruction.add_operand(index_id);\n\n        instruction\n    }\n\n    pub(super) fn vector_shuffle(\n        result_type_id: Word,\n        id: Word,\n        v1_id: Word,\n        v2_id: Word,\n        components: &[Word],\n    ) -> Self {\n        let mut instruction = Self::new(Op::VectorShuffle);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(v1_id);\n        instruction.add_operand(v2_id);\n\n        for &component in components {\n            instruction.add_operand(component);\n        }\n\n        instruction\n    }\n\n    //\n    // Arithmetic Instructions\n    //\n    pub(super) fn binary(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        operand_1: Word,\n        operand_2: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(operand_1);\n        instruction.add_operand(operand_2);\n        instruction\n    }\n\n    pub(super) fn ternary(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        operand_1: Word,\n        operand_2: Word,\n        operand_3: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(operand_1);\n        instruction.add_operand(operand_2);\n        instruction.add_operand(operand_3);\n        instruction\n    }\n\n    pub(super) fn quaternary(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        operand_1: Word,\n        operand_2: Word,\n        operand_3: Word,\n        operand_4: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(operand_1);\n        instruction.add_operand(operand_2);\n        instruction.add_operand(operand_3);\n        instruction.add_operand(operand_4);\n        instruction\n    }\n\n    pub(super) fn relational(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(expr_id);\n        instruction\n    }\n\n    pub(super) fn atomic_binary(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        pointer: Word,\n        scope_id: Word,\n        semantics_id: Word,\n        value: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(pointer);\n        instruction.add_operand(scope_id);\n        instruction.add_operand(semantics_id);\n        instruction.add_operand(value);\n        instruction\n    }\n\n    //\n    // Bit Instructions\n    //\n\n    //\n    // Relational and Logical Instructions\n    //\n\n    //\n    // Derivative Instructions\n    //\n\n    pub(super) fn derivative(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(expr_id);\n        instruction\n    }\n\n    //\n    // Control-Flow Instructions\n    //\n\n    pub(super) fn phi(\n        result_type_id: Word,\n        result_id: Word,\n        var_parent_pairs: &[(Word, Word)],\n    ) -> Self {\n        let mut instruction = Self::new(Op::Phi);\n        instruction.add_operand(result_type_id);\n        instruction.add_operand(result_id);\n        for &(variable, parent) in var_parent_pairs {\n            instruction.add_operand(variable);\n            instruction.add_operand(parent);\n        }\n        instruction\n    }\n\n    pub(super) fn selection_merge(\n        merge_id: Word,\n        selection_control: spirv::SelectionControl,\n    ) -> Self {\n        let mut instruction = Self::new(Op::SelectionMerge);\n        instruction.add_operand(merge_id);\n        instruction.add_operand(selection_control.bits());\n        instruction\n    }\n\n    pub(super) fn loop_merge(\n        merge_id: Word,\n        continuing_id: Word,\n        selection_control: spirv::SelectionControl,\n    ) -> Self {\n        let mut instruction = Self::new(Op::LoopMerge);\n        instruction.add_operand(merge_id);\n        instruction.add_operand(continuing_id);\n        instruction.add_operand(selection_control.bits());\n        instruction\n    }\n\n    pub(super) fn label(id: Word) -> Self {\n        let mut instruction = Self::new(Op::Label);\n        instruction.set_result(id);\n        instruction\n    }\n\n    pub(super) fn branch(id: Word) -> Self {\n        let mut instruction = Self::new(Op::Branch);\n        instruction.add_operand(id);\n        instruction\n    }\n\n    // TODO Branch Weights not implemented.\n    pub(super) fn branch_conditional(\n        condition_id: Word,\n        true_label: Word,\n        false_label: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::BranchConditional);\n        instruction.add_operand(condition_id);\n        instruction.add_operand(true_label);\n        instruction.add_operand(false_label);\n        instruction\n    }\n\n    pub(super) fn switch(selector_id: Word, default_id: Word, cases: &[Case]) -> Self {\n        let mut instruction = Self::new(Op::Switch);\n        instruction.add_operand(selector_id);\n        instruction.add_operand(default_id);\n        for case in cases {\n            instruction.add_operand(case.value);\n            instruction.add_operand(case.label_id);\n        }\n        instruction\n    }\n\n    pub(super) fn select(\n        result_type_id: Word,\n        id: Word,\n        condition_id: Word,\n        accept_id: Word,\n        reject_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::Select);\n        instruction.add_operand(result_type_id);\n        instruction.add_operand(id);\n        instruction.add_operand(condition_id);\n        instruction.add_operand(accept_id);\n        instruction.add_operand(reject_id);\n        instruction\n    }\n\n    pub(super) const fn kill() -> Self {\n        Self::new(Op::Kill)\n    }\n\n    pub(super) const fn return_void() -> Self {\n        Self::new(Op::Return)\n    }\n\n    pub(super) fn return_value(value_id: Word) -> Self {\n        let mut instruction = Self::new(Op::ReturnValue);\n        instruction.add_operand(value_id);\n        instruction\n    }\n\n    //\n    //  Atomic Instructions\n    //\n\n    //\n    //  Primitive Instructions\n    //\n\n    // Barriers\n\n    pub(super) fn control_barrier(\n        exec_scope_id: Word,\n        mem_scope_id: Word,\n        semantics_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::ControlBarrier);\n        instruction.add_operand(exec_scope_id);\n        instruction.add_operand(mem_scope_id);\n        instruction.add_operand(semantics_id);\n        instruction\n    }\n    pub(super) fn memory_barrier(mem_scope_id: Word, semantics_id: Word) -> Self {\n        let mut instruction = Self::new(Op::MemoryBarrier);\n        instruction.add_operand(mem_scope_id);\n        instruction.add_operand(semantics_id);\n        instruction\n    }\n\n    // Group Instructions\n\n    pub(super) fn group_non_uniform_ballot(\n        result_type_id: Word,\n        id: Word,\n        exec_scope_id: Word,\n        predicate: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::GroupNonUniformBallot);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(exec_scope_id);\n        instruction.add_operand(predicate);\n\n        instruction\n    }\n    pub(super) fn group_non_uniform_broadcast_first(\n        result_type_id: Word,\n        id: Word,\n        exec_scope_id: Word,\n        value: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::GroupNonUniformBroadcastFirst);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(exec_scope_id);\n        instruction.add_operand(value);\n\n        instruction\n    }\n    pub(super) fn group_non_uniform_gather(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        exec_scope_id: Word,\n        value: Word,\n        index: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(exec_scope_id);\n        instruction.add_operand(value);\n        instruction.add_operand(index);\n\n        instruction\n    }\n    pub(super) fn group_non_uniform_arithmetic(\n        op: Op,\n        result_type_id: Word,\n        id: Word,\n        exec_scope_id: Word,\n        group_op: Option<spirv::GroupOperation>,\n        value: Word,\n    ) -> Self {\n        let mut instruction = Self::new(op);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(exec_scope_id);\n        if let Some(group_op) = group_op {\n            instruction.add_operand(group_op as u32);\n        }\n        instruction.add_operand(value);\n\n        instruction\n    }\n    pub(super) fn group_non_uniform_quad_swap(\n        result_type_id: Word,\n        id: Word,\n        exec_scope_id: Word,\n        value: Word,\n        direction: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::GroupNonUniformQuadSwap);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(exec_scope_id);\n        instruction.add_operand(value);\n        instruction.add_operand(direction);\n\n        instruction\n    }\n\n    // Cooperative operations\n    pub(super) fn coop_load(\n        result_type_id: Word,\n        id: Word,\n        pointer_id: Word,\n        layout_id: Word,\n        stride_id: Word,\n    ) -> Self {\n        let mut instruction = Self::new(Op::CooperativeMatrixLoadKHR);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(pointer_id);\n        instruction.add_operand(layout_id);\n        instruction.add_operand(stride_id);\n        instruction\n    }\n    pub(super) fn coop_store(id: Word, pointer_id: Word, layout_id: Word, stride_id: Word) -> Self {\n        let mut instruction = Self::new(Op::CooperativeMatrixStoreKHR);\n        instruction.add_operand(pointer_id);\n        instruction.add_operand(id);\n        instruction.add_operand(layout_id);\n        instruction.add_operand(stride_id);\n        instruction\n    }\n    pub(super) fn coop_mul_add(result_type_id: Word, id: Word, a: Word, b: Word, c: Word) -> Self {\n        let mut instruction = Self::new(Op::CooperativeMatrixMulAddKHR);\n        instruction.set_type(result_type_id);\n        instruction.set_result(id);\n        instruction.add_operand(a);\n        instruction.add_operand(b);\n        instruction.add_operand(c);\n\n        instruction\n    }\n}\n\nimpl From<crate::StorageFormat> for spirv::ImageFormat {\n    fn from(format: crate::StorageFormat) -> Self {\n        use crate::StorageFormat as Sf;\n        match format {\n            Sf::R8Unorm => Self::R8,\n            Sf::R8Snorm => Self::R8Snorm,\n            Sf::R8Uint => Self::R8ui,\n            Sf::R8Sint => Self::R8i,\n            Sf::R16Uint => Self::R16ui,\n            Sf::R16Sint => Self::R16i,\n            Sf::R16Float => Self::R16f,\n            Sf::Rg8Unorm => Self::Rg8,\n            Sf::Rg8Snorm => Self::Rg8Snorm,\n            Sf::Rg8Uint => Self::Rg8ui,\n            Sf::Rg8Sint => Self::Rg8i,\n            Sf::R32Uint => Self::R32ui,\n            Sf::R32Sint => Self::R32i,\n            Sf::R32Float => Self::R32f,\n            Sf::Rg16Uint => Self::Rg16ui,\n            Sf::Rg16Sint => Self::Rg16i,\n            Sf::Rg16Float => Self::Rg16f,\n            Sf::Rgba8Unorm => Self::Rgba8,\n            Sf::Rgba8Snorm => Self::Rgba8Snorm,\n            Sf::Rgba8Uint => Self::Rgba8ui,\n            Sf::Rgba8Sint => Self::Rgba8i,\n            Sf::Bgra8Unorm => Self::Unknown,\n            Sf::Rgb10a2Uint => Self::Rgb10a2ui,\n            Sf::Rgb10a2Unorm => Self::Rgb10A2,\n            Sf::Rg11b10Ufloat => Self::R11fG11fB10f,\n            Sf::R64Uint => Self::R64ui,\n            Sf::Rg32Uint => Self::Rg32ui,\n            Sf::Rg32Sint => Self::Rg32i,\n            Sf::Rg32Float => Self::Rg32f,\n            Sf::Rgba16Uint => Self::Rgba16ui,\n            Sf::Rgba16Sint => Self::Rgba16i,\n            Sf::Rgba16Float => Self::Rgba16f,\n            Sf::Rgba32Uint => Self::Rgba32ui,\n            Sf::Rgba32Sint => Self::Rgba32i,\n            Sf::Rgba32Float => Self::Rgba32f,\n            Sf::R16Unorm => Self::R16,\n            Sf::R16Snorm => Self::R16Snorm,\n            Sf::Rg16Unorm => Self::Rg16,\n            Sf::Rg16Snorm => Self::Rg16Snorm,\n            Sf::Rgba16Unorm => Self::Rgba16,\n            Sf::Rgba16Snorm => Self::Rgba16Snorm,\n        }\n    }\n}\n\nimpl From<crate::ImageDimension> for spirv::Dim {\n    fn from(dim: crate::ImageDimension) -> Self {\n        use crate::ImageDimension as Id;\n        match dim {\n            Id::D1 => Self::Dim1D,\n            Id::D2 => Self::Dim2D,\n            Id::D3 => Self::Dim3D,\n            Id::Cube => Self::DimCube,\n        }\n    }\n}\n\nimpl From<crate::CooperativeRole> for spirv::CooperativeMatrixUse {\n    fn from(role: crate::CooperativeRole) -> Self {\n        match role {\n            crate::CooperativeRole::A => Self::MatrixAKHR,\n            crate::CooperativeRole::B => Self::MatrixBKHR,\n            crate::CooperativeRole::C => Self::MatrixAccumulatorKHR,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/layout.rs",
    "content": "use alloc::{vec, vec::Vec};\nuse core::iter;\n\nuse spirv::{Op, Word, MAGIC_NUMBER};\n\nuse super::{Instruction, LogicalLayout, PhysicalLayout};\n\n#[cfg(test)]\nuse alloc::format;\n\n// https://github.com/KhronosGroup/SPIRV-Headers/pull/195\nconst GENERATOR: Word = 28;\n\nimpl PhysicalLayout {\n    pub(super) const fn new(major_version: u8, minor_version: u8) -> Self {\n        let version = ((major_version as u32) << 16) | ((minor_version as u32) << 8);\n        PhysicalLayout {\n            magic_number: MAGIC_NUMBER,\n            version,\n            generator: GENERATOR,\n            bound: 0,\n            instruction_schema: 0x0u32,\n        }\n    }\n\n    pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {\n        sink.extend(iter::once(self.magic_number));\n        sink.extend(iter::once(self.version));\n        sink.extend(iter::once(self.generator));\n        sink.extend(iter::once(self.bound));\n        sink.extend(iter::once(self.instruction_schema));\n    }\n\n    /// Returns `(major, minor)`.\n    pub(super) const fn lang_version(&self) -> (u8, u8) {\n        let major = (self.version >> 16) as u8;\n        let minor = (self.version >> 8) as u8;\n        (major, minor)\n    }\n}\n\nimpl super::reclaimable::Reclaimable for PhysicalLayout {\n    fn reclaim(self) -> Self {\n        PhysicalLayout {\n            magic_number: self.magic_number,\n            version: self.version,\n            generator: self.generator,\n            instruction_schema: self.instruction_schema,\n            bound: 0,\n        }\n    }\n}\n\nimpl LogicalLayout {\n    pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {\n        sink.extend(self.capabilities.iter().cloned());\n        sink.extend(self.extensions.iter().cloned());\n        sink.extend(self.ext_inst_imports.iter().cloned());\n        sink.extend(self.memory_model.iter().cloned());\n        sink.extend(self.entry_points.iter().cloned());\n        sink.extend(self.execution_modes.iter().cloned());\n        sink.extend(self.debugs.iter().cloned());\n        sink.extend(self.annotations.iter().cloned());\n        sink.extend(self.declarations.iter().cloned());\n        sink.extend(self.function_declarations.iter().cloned());\n        sink.extend(self.function_definitions.iter().cloned());\n    }\n}\n\nimpl super::reclaimable::Reclaimable for LogicalLayout {\n    fn reclaim(self) -> Self {\n        Self {\n            capabilities: self.capabilities.reclaim(),\n            extensions: self.extensions.reclaim(),\n            ext_inst_imports: self.ext_inst_imports.reclaim(),\n            memory_model: self.memory_model.reclaim(),\n            entry_points: self.entry_points.reclaim(),\n            execution_modes: self.execution_modes.reclaim(),\n            debugs: self.debugs.reclaim(),\n            annotations: self.annotations.reclaim(),\n            declarations: self.declarations.reclaim(),\n            function_declarations: self.function_declarations.reclaim(),\n            function_definitions: self.function_definitions.reclaim(),\n        }\n    }\n}\n\nimpl Instruction {\n    pub(super) const fn new(op: Op) -> Self {\n        Instruction {\n            op,\n            wc: 1, // Always start at 1 for the first word (OP + WC),\n            type_id: None,\n            result_id: None,\n            operands: vec![],\n        }\n    }\n\n    pub(super) fn set_type(&mut self, id: Word) {\n        assert!(self.type_id.is_none(), \"Type can only be set once\");\n        self.type_id = Some(id);\n        self.wc += 1;\n    }\n\n    pub(super) fn set_result(&mut self, id: Word) {\n        assert!(self.result_id.is_none(), \"Result can only be set once\");\n        self.result_id = Some(id);\n        self.wc += 1;\n    }\n\n    pub(super) fn add_operand(&mut self, operand: Word) {\n        self.operands.push(operand);\n        self.wc += 1;\n    }\n\n    pub(super) fn add_operands(&mut self, operands: Vec<Word>) {\n        for operand in operands.into_iter() {\n            self.add_operand(operand)\n        }\n    }\n\n    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {\n        sink.extend(Some((self.wc << 16) | self.op as u32));\n        sink.extend(self.type_id);\n        sink.extend(self.result_id);\n        sink.extend(self.operands.iter().cloned());\n    }\n}\n\nimpl Instruction {\n    #[cfg(test)]\n    fn validate(&self, words: &[Word]) {\n        let mut inst_index = 0;\n        let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);\n        inst_index += 1;\n\n        assert_eq!(wc, words.len() as u16);\n        assert_eq!(op, self.op as u16);\n\n        if let Some(type_id) = self.type_id {\n            assert_eq!(words[inst_index], type_id);\n            inst_index += 1;\n        }\n\n        if let Some(result_id) = self.result_id {\n            assert_eq!(words[inst_index], result_id);\n            inst_index += 1;\n        }\n\n        for (op_index, i) in (inst_index..wc as usize).enumerate() {\n            assert_eq!(words[i], self.operands[op_index]);\n        }\n    }\n}\n\n#[test]\nfn test_physical_layout_in_words() {\n    let bound = 5;\n\n    // The least and most significant bytes of `version` must both be zero\n    // according to the SPIR-V spec.\n    let version = 0x0001_0200;\n\n    let mut output = vec![];\n    let mut layout = PhysicalLayout::new(1, 2);\n    layout.bound = bound;\n\n    layout.in_words(&mut output);\n\n    assert_eq!(&output, &[MAGIC_NUMBER, version, GENERATOR, bound, 0,]);\n}\n\n#[test]\nfn test_logical_layout_in_words() {\n    let mut output = vec![];\n    let mut layout = LogicalLayout::default();\n    let layout_vectors = 11;\n    let mut instructions = Vec::with_capacity(layout_vectors);\n\n    let vector_names = &[\n        \"Capabilities\",\n        \"Extensions\",\n        \"External Instruction Imports\",\n        \"Memory Model\",\n        \"Entry Points\",\n        \"Execution Modes\",\n        \"Debugs\",\n        \"Annotations\",\n        \"Declarations\",\n        \"Function Declarations\",\n        \"Function Definitions\",\n    ];\n\n    for (i, _) in vector_names.iter().enumerate().take(layout_vectors) {\n        let mut dummy_instruction = Instruction::new(Op::Constant);\n        dummy_instruction.set_type((i + 1) as u32);\n        dummy_instruction.set_result((i + 2) as u32);\n        dummy_instruction.add_operand((i + 3) as u32);\n        dummy_instruction.add_operands(super::helpers::string_to_words(\n            format!(\"This is the vector: {}\", vector_names[i]).as_str(),\n        ));\n        instructions.push(dummy_instruction);\n    }\n\n    instructions[0].to_words(&mut layout.capabilities);\n    instructions[1].to_words(&mut layout.extensions);\n    instructions[2].to_words(&mut layout.ext_inst_imports);\n    instructions[3].to_words(&mut layout.memory_model);\n    instructions[4].to_words(&mut layout.entry_points);\n    instructions[5].to_words(&mut layout.execution_modes);\n    instructions[6].to_words(&mut layout.debugs);\n    instructions[7].to_words(&mut layout.annotations);\n    instructions[8].to_words(&mut layout.declarations);\n    instructions[9].to_words(&mut layout.function_declarations);\n    instructions[10].to_words(&mut layout.function_definitions);\n\n    layout.in_words(&mut output);\n\n    let mut index: usize = 0;\n    for instruction in instructions {\n        let wc = instruction.wc as usize;\n        instruction.validate(&output[index..index + wc]);\n        index += wc;\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/mesh_shader.rs",
    "content": "use alloc::vec::Vec;\nuse spirv::Word;\n\nuse crate::{\n    back::spv::{\n        helpers::BindingDecorations, writer::FunctionInterface, Block, EntryPointContext, Error,\n        Instruction, WriterFlags,\n    },\n    non_max_u32::NonMaxU32,\n    Handle,\n};\n\n#[derive(Clone)]\npub struct MeshReturnMember {\n    pub ty_id: u32,\n    pub binding: crate::Binding,\n}\n\nstruct PerOutputTypeMeshReturnInfo {\n    max_length_constant: Word,\n    array_type_id: Word,\n    struct_members: Vec<MeshReturnMember>,\n\n    // * Most builtins must be in the same block.\n    // * All bindings must be in their own unique block.\n    // * The primitive indices builtin family needs its own block.\n    // * Cull primitive doesn't care about having its own block, but\n    //   some older validation layers didn't respect this.\n    builtin_block: Option<Word>,\n    bindings: Vec<Word>,\n}\n\npub struct MeshReturnInfo {\n    /// Id of the workgroup variable containing the data to be output\n    out_variable_id: Word,\n    /// All members of the output variable struct type\n    out_members: Vec<MeshReturnMember>,\n    /// Id of the input variable for local invocation id\n    local_invocation_index_var_id: Word,\n    /// Total workgroup size (product)\n    workgroup_size: u32,\n\n    /// Vertex-specific info\n    vertex_info: PerOutputTypeMeshReturnInfo,\n    /// Primitive-specific info\n    primitive_info: PerOutputTypeMeshReturnInfo,\n    /// Array variable for the primitive indices builtin\n    primitive_indices: Option<Word>,\n}\n\nimpl super::Writer {\n    /// Sets up an output variable that will handle part of the mesh shader output\n    pub(super) fn write_mesh_return_global_variable(\n        &mut self,\n        ty: u32,\n        array_size_id: u32,\n    ) -> Result<Word, Error> {\n        let array_ty = self.id_gen.next();\n        Instruction::type_array(array_ty, ty, array_size_id)\n            .to_words(&mut self.logical_layout.declarations);\n        let ptr_ty = self.get_pointer_type_id(array_ty, spirv::StorageClass::Output);\n        let var_id = self.id_gen.next();\n        Instruction::variable(ptr_ty, var_id, spirv::StorageClass::Output, None)\n            .to_words(&mut self.logical_layout.declarations);\n        Ok(var_id)\n    }\n\n    /// This does various setup things to allow mesh shader entry points\n    /// to be properly written, such as creating the output variables\n    pub(super) fn write_entry_point_mesh_shader_info(\n        &mut self,\n        iface: &mut FunctionInterface,\n        local_invocation_index_id: Option<Word>,\n        ir_module: &crate::Module,\n        ep_context: &mut EntryPointContext,\n    ) -> Result<(), Error> {\n        let Some(ref mesh_info) = iface.mesh_info else {\n            return Ok(());\n        };\n        // Collect the members in the output structs\n        let out_members: Vec<MeshReturnMember> =\n            match &ir_module.types[ir_module.global_variables[mesh_info.output_variable].ty] {\n                &crate::Type {\n                    inner: crate::TypeInner::Struct { ref members, .. },\n                    ..\n                } => members\n                    .iter()\n                    .map(|a| MeshReturnMember {\n                        ty_id: self.get_handle_type_id(a.ty),\n                        binding: a.binding.clone().unwrap(),\n                    })\n                    .collect(),\n                _ => unreachable!(),\n            };\n        let vertex_array_type_id = out_members\n            .iter()\n            .find(|a| a.binding == crate::Binding::BuiltIn(crate::BuiltIn::Vertices))\n            .unwrap()\n            .ty_id;\n        let primitive_array_type_id = out_members\n            .iter()\n            .find(|a| a.binding == crate::Binding::BuiltIn(crate::BuiltIn::Primitives))\n            .unwrap()\n            .ty_id;\n        let vertex_members = match &ir_module.types[mesh_info.vertex_output_type] {\n            &crate::Type {\n                inner: crate::TypeInner::Struct { ref members, .. },\n                ..\n            } => members\n                .iter()\n                .map(|a| MeshReturnMember {\n                    ty_id: self.get_handle_type_id(a.ty),\n                    binding: a.binding.clone().unwrap(),\n                })\n                .collect(),\n            _ => unreachable!(),\n        };\n        let primitive_members = match &ir_module.types[mesh_info.primitive_output_type] {\n            &crate::Type {\n                inner: crate::TypeInner::Struct { ref members, .. },\n                ..\n            } => members\n                .iter()\n                .map(|a| MeshReturnMember {\n                    ty_id: self.get_handle_type_id(a.ty),\n                    binding: a.binding.clone().unwrap(),\n                })\n                .collect(),\n            _ => unreachable!(),\n        };\n        // In the final return, we do a giant memcpy, for which this is helpful\n        let local_invocation_index_var_id = match local_invocation_index_id {\n            Some(a) => a,\n            None => {\n                let u32_id = self.get_u32_type_id();\n                let var = self.id_gen.next();\n                Instruction::variable(\n                    self.get_pointer_type_id(u32_id, spirv::StorageClass::Input),\n                    var,\n                    spirv::StorageClass::Input,\n                    None,\n                )\n                .to_words(&mut self.logical_layout.declarations);\n                Instruction::decorate(\n                    var,\n                    spirv::Decoration::BuiltIn,\n                    &[spirv::BuiltIn::LocalInvocationIndex as u32],\n                )\n                .to_words(&mut self.logical_layout.annotations);\n                iface.varying_ids.push(var);\n\n                var\n            }\n        };\n        // This is the information that is passed to the function writer\n        // so that it can write the final return logic\n        let mut mesh_return_info = MeshReturnInfo {\n            out_variable_id: self.global_variables[mesh_info.output_variable].var_id,\n            out_members,\n            local_invocation_index_var_id,\n            workgroup_size: self\n                .get_constant_scalar(crate::Literal::U32(iface.workgroup_size.iter().product())),\n\n            vertex_info: PerOutputTypeMeshReturnInfo {\n                array_type_id: vertex_array_type_id,\n                struct_members: vertex_members,\n                max_length_constant: self\n                    .get_constant_scalar(crate::Literal::U32(mesh_info.max_vertices)),\n                bindings: Vec::new(),\n                builtin_block: None,\n            },\n            primitive_info: PerOutputTypeMeshReturnInfo {\n                array_type_id: primitive_array_type_id,\n                struct_members: primitive_members,\n                max_length_constant: self\n                    .get_constant_scalar(crate::Literal::U32(mesh_info.max_primitives)),\n                bindings: Vec::new(),\n                builtin_block: None,\n            },\n            primitive_indices: None,\n        };\n        let vert_array_size_id =\n            self.get_constant_scalar(crate::Literal::U32(mesh_info.max_vertices));\n        let prim_array_size_id =\n            self.get_constant_scalar(crate::Literal::U32(mesh_info.max_primitives));\n\n        // Create the actual output variables and types.\n        // According to SPIR-V,\n        // * All builtins must be in the same output `Block` (except builtins for different output types like vertex/primitive)\n        // * Each member with `location` must be in its own `Block` decorated `struct`\n        // * Some builtins like CullPrimitiveEXT don't care as much (older validation layers don't know this! Wonderful!)\n        // * Some builtins like the indices ones need to be in their own output variable without a struct wrapper\n\n        // Write vertex builtin block\n        if mesh_return_info\n            .vertex_info\n            .struct_members\n            .iter()\n            .any(|a| matches!(a.binding, crate::Binding::BuiltIn(..)))\n        {\n            let builtin_block_ty_id = self.id_gen.next();\n            let mut ins = Instruction::type_struct(builtin_block_ty_id, &[]);\n            let mut bi_index = 0;\n            let mut decorations = Vec::new();\n            for member in &mesh_return_info.vertex_info.struct_members {\n                if let crate::Binding::BuiltIn(_) = member.binding {\n                    ins.add_operand(member.ty_id);\n                    let binding = self.map_binding(\n                        ir_module,\n                        iface.stage,\n                        spirv::StorageClass::Output,\n                        // Unused except in fragment shaders with other conditions, so we can pass null\n                        Handle::new(NonMaxU32::new(0).unwrap()),\n                        &member.binding,\n                    )?;\n                    match binding {\n                        BindingDecorations::BuiltIn(bi, others) => {\n                            decorations.push(Instruction::member_decorate(\n                                builtin_block_ty_id,\n                                bi_index,\n                                spirv::Decoration::BuiltIn,\n                                &[bi as Word],\n                            ));\n                            for other in others {\n                                decorations.push(Instruction::member_decorate(\n                                    builtin_block_ty_id,\n                                    bi_index,\n                                    other,\n                                    &[],\n                                ));\n                            }\n                        }\n                        _ => unreachable!(),\n                    }\n                    bi_index += 1;\n                }\n            }\n            ins.to_words(&mut self.logical_layout.declarations);\n            decorations.push(Instruction::decorate(\n                builtin_block_ty_id,\n                spirv::Decoration::Block,\n                &[],\n            ));\n            for dec in decorations {\n                dec.to_words(&mut self.logical_layout.annotations);\n            }\n            let v =\n                self.write_mesh_return_global_variable(builtin_block_ty_id, vert_array_size_id)?;\n            iface.varying_ids.push(v);\n            if self.flags.contains(WriterFlags::DEBUG) {\n                self.debugs\n                    .push(Instruction::name(v, \"naga_vertex_builtin_outputs\"));\n            }\n            mesh_return_info.vertex_info.builtin_block = Some(v);\n        }\n        // Write primitive builtin block\n        if mesh_return_info\n            .primitive_info\n            .struct_members\n            .iter()\n            .any(|a| {\n                !matches!(\n                    a.binding,\n                    crate::Binding::BuiltIn(\n                        crate::BuiltIn::PointIndex\n                            | crate::BuiltIn::LineIndices\n                            | crate::BuiltIn::TriangleIndices\n                    ) | crate::Binding::Location { .. }\n                )\n            })\n        {\n            let builtin_block_ty_id = self.id_gen.next();\n            let mut ins = Instruction::type_struct(builtin_block_ty_id, &[]);\n            let mut bi_index = 0;\n            let mut decorations = Vec::new();\n            for member in &mesh_return_info.primitive_info.struct_members {\n                if let crate::Binding::BuiltIn(bi) = member.binding {\n                    // These need to be in their own block, unlike other builtins\n                    if matches!(\n                        bi,\n                        crate::BuiltIn::PointIndex\n                            | crate::BuiltIn::LineIndices\n                            | crate::BuiltIn::TriangleIndices,\n                    ) {\n                        continue;\n                    }\n                    ins.add_operand(member.ty_id);\n                    let binding = self.map_binding(\n                        ir_module,\n                        iface.stage,\n                        spirv::StorageClass::Output,\n                        // Unused except in fragment shaders with other conditions, so we can pass null\n                        Handle::new(NonMaxU32::new(0).unwrap()),\n                        &member.binding,\n                    )?;\n                    match binding {\n                        BindingDecorations::BuiltIn(bi, others) => {\n                            decorations.push(Instruction::member_decorate(\n                                builtin_block_ty_id,\n                                bi_index,\n                                spirv::Decoration::BuiltIn,\n                                &[bi as Word],\n                            ));\n                            for other in others {\n                                decorations.push(Instruction::member_decorate(\n                                    builtin_block_ty_id,\n                                    bi_index,\n                                    other,\n                                    &[],\n                                ));\n                            }\n                        }\n                        _ => unreachable!(),\n                    }\n                    bi_index += 1;\n                }\n            }\n            ins.to_words(&mut self.logical_layout.declarations);\n            decorations.push(Instruction::decorate(\n                builtin_block_ty_id,\n                spirv::Decoration::Block,\n                &[],\n            ));\n            for dec in decorations {\n                dec.to_words(&mut self.logical_layout.annotations);\n            }\n            let v =\n                self.write_mesh_return_global_variable(builtin_block_ty_id, prim_array_size_id)?;\n            Instruction::decorate(v, spirv::Decoration::PerPrimitiveEXT, &[])\n                .to_words(&mut self.logical_layout.annotations);\n            iface.varying_ids.push(v);\n            if self.flags.contains(WriterFlags::DEBUG) {\n                self.debugs\n                    .push(Instruction::name(v, \"naga_primitive_builtin_outputs\"));\n            }\n            mesh_return_info.primitive_info.builtin_block = Some(v);\n        }\n\n        // Write vertex binding output blocks (1 array per output struct member)\n        for member in &mesh_return_info.vertex_info.struct_members {\n            match member.binding {\n                crate::Binding::Location { location, .. } => {\n                    // Create variable\n                    let v =\n                        self.write_mesh_return_global_variable(member.ty_id, vert_array_size_id)?;\n                    // Decorate the variable with Location\n                    Instruction::decorate(v, spirv::Decoration::Location, &[location])\n                        .to_words(&mut self.logical_layout.annotations);\n                    iface.varying_ids.push(v);\n                    mesh_return_info.vertex_info.bindings.push(v);\n                }\n                crate::Binding::BuiltIn(_) => (),\n            }\n        }\n        // Write primitive binding output blocks (1 array per output struct member)\n        // Also write indices output block\n        for member in &mesh_return_info.primitive_info.struct_members {\n            match member.binding {\n                crate::Binding::BuiltIn(\n                    crate::BuiltIn::PointIndex\n                    | crate::BuiltIn::LineIndices\n                    | crate::BuiltIn::TriangleIndices,\n                ) => {\n                    // This is written here instead of as part of the builtin block\n                    let v =\n                        self.write_mesh_return_global_variable(member.ty_id, prim_array_size_id)?;\n                    // This shouldn't be marked as PerPrimitiveEXT\n                    Instruction::decorate(\n                        v,\n                        spirv::Decoration::BuiltIn,\n                        &[match member.binding.to_built_in().unwrap() {\n                            crate::BuiltIn::PointIndex => spirv::BuiltIn::PrimitivePointIndicesEXT,\n                            crate::BuiltIn::LineIndices => spirv::BuiltIn::PrimitiveLineIndicesEXT,\n                            crate::BuiltIn::TriangleIndices => {\n                                spirv::BuiltIn::PrimitiveTriangleIndicesEXT\n                            }\n                            _ => unreachable!(),\n                        } as Word],\n                    )\n                    .to_words(&mut self.logical_layout.annotations);\n                    iface.varying_ids.push(v);\n                    if self.flags.contains(WriterFlags::DEBUG) {\n                        self.debugs\n                            .push(Instruction::name(v, \"naga_primitive_indices_outputs\"));\n                    }\n                    mesh_return_info.primitive_indices = Some(v);\n                }\n                crate::Binding::Location { location, .. } => {\n                    // Create variable\n                    let v =\n                        self.write_mesh_return_global_variable(member.ty_id, prim_array_size_id)?;\n                    // Decorate the variable with Location\n                    Instruction::decorate(v, spirv::Decoration::Location, &[location])\n                        .to_words(&mut self.logical_layout.annotations);\n                    // Decorate it with PerPrimitiveEXT\n                    Instruction::decorate(v, spirv::Decoration::PerPrimitiveEXT, &[])\n                        .to_words(&mut self.logical_layout.annotations);\n                    iface.varying_ids.push(v);\n\n                    mesh_return_info.primitive_info.bindings.push(v);\n                }\n                crate::Binding::BuiltIn(_) => (),\n            }\n        }\n\n        // Store this where it can be read later during function write\n        ep_context.mesh_state = Some(mesh_return_info);\n\n        Ok(())\n    }\n\n    pub(super) fn write_entry_point_task_return(\n        &mut self,\n        value_id: Word,\n        body: &mut Vec<Instruction>,\n        task_payload: Word,\n    ) -> Result<Instruction, Error> {\n        // OpEmitMeshTasksEXT must be called right before exiting (after setting other\n        // output variables if there are any)\n\n        // Extract the vec3<u32> into 3 u32's\n        let values = [self.id_gen.next(), self.id_gen.next(), self.id_gen.next()];\n        for (i, &value) in values.iter().enumerate() {\n            let instruction = Instruction::composite_extract(\n                self.get_u32_type_id(),\n                value,\n                value_id,\n                &[i as Word],\n            );\n            body.push(instruction);\n        }\n        let mut instruction = Instruction::new(spirv::Op::EmitMeshTasksEXT);\n        for id in values {\n            instruction.add_operand(id);\n        }\n        // We have to include the task payload in our call\n        instruction.add_operand(task_payload);\n        Ok(instruction)\n    }\n\n    /// This writes the actual loop\n    #[allow(clippy::too_many_arguments)]\n    fn write_mesh_copy_loop(\n        &mut self,\n        body: &mut Vec<Instruction>,\n        mut loop_body_block: Vec<Instruction>,\n        loop_header: u32,\n        loop_merge: u32,\n        count_id: u32,\n        index_var: u32,\n        return_info: &MeshReturnInfo,\n    ) {\n        let u32_id = self.get_u32_type_id();\n        let condition_check = self.id_gen.next();\n        let loop_continue = self.id_gen.next();\n        let loop_body = self.id_gen.next();\n\n        // Loop header\n        {\n            body.push(Instruction::label(loop_header));\n            body.push(Instruction::loop_merge(\n                loop_merge,\n                loop_continue,\n                spirv::SelectionControl::empty(),\n            ));\n            body.push(Instruction::branch(condition_check));\n        }\n        // Condition check - check if i is less than num vertices to copy\n        {\n            body.push(Instruction::label(condition_check));\n\n            let val_i = self.id_gen.next();\n            body.push(Instruction::load(u32_id, val_i, index_var, None));\n\n            let cond = self.id_gen.next();\n            body.push(Instruction::binary(\n                spirv::Op::ULessThan,\n                self.get_bool_type_id(),\n                cond,\n                val_i,\n                count_id,\n            ));\n            body.push(Instruction::branch_conditional(cond, loop_body, loop_merge));\n        }\n        // Loop body\n        {\n            body.push(Instruction::label(loop_body));\n            body.append(&mut loop_body_block);\n            body.push(Instruction::branch(loop_continue));\n        }\n        // Loop continue - increment i\n        {\n            body.push(Instruction::label(loop_continue));\n\n            let prev_val_i = self.id_gen.next();\n            body.push(Instruction::load(u32_id, prev_val_i, index_var, None));\n            let new_val_i = self.id_gen.next();\n            body.push(Instruction::binary(\n                spirv::Op::IAdd,\n                u32_id,\n                new_val_i,\n                prev_val_i,\n                return_info.workgroup_size,\n            ));\n            body.push(Instruction::store(index_var, new_val_i, None));\n\n            body.push(Instruction::branch(loop_header));\n        }\n    }\n\n    /// This generates the instructions used to copy all parts of a single output vertex/primitive\n    /// to their individual output locations\n    fn write_mesh_copy_body(\n        &mut self,\n        is_primitive: bool,\n        return_info: &MeshReturnInfo,\n        index_var: u32,\n        vert_array_ptr: u32,\n        prim_array_ptr: u32,\n    ) -> Vec<Instruction> {\n        let u32_type_id = self.get_u32_type_id();\n        let mut body = Vec::new();\n        // Current index to copy\n        let val_i = self.id_gen.next();\n        body.push(Instruction::load(u32_type_id, val_i, index_var, None));\n\n        let info = if is_primitive {\n            &return_info.primitive_info\n        } else {\n            &return_info.vertex_info\n        };\n        let array_ptr = if is_primitive {\n            prim_array_ptr\n        } else {\n            vert_array_ptr\n        };\n\n        let mut builtin_index = 0;\n        let mut binding_index = 0;\n        // Write individual members of the vertex\n        for (member_id, member) in info.struct_members.iter().enumerate() {\n            let val_to_copy_ptr = self.id_gen.next();\n            body.push(Instruction::access_chain(\n                self.get_pointer_type_id(member.ty_id, spirv::StorageClass::Workgroup),\n                val_to_copy_ptr,\n                array_ptr,\n                &[\n                    val_i,\n                    self.get_constant_scalar(crate::Literal::U32(member_id as u32)),\n                ],\n            ));\n            let val_to_copy = self.id_gen.next();\n            body.push(Instruction::load(\n                member.ty_id,\n                val_to_copy,\n                val_to_copy_ptr,\n                None,\n            ));\n            let mut needs_y_flip = false;\n            let ptr_to_copy_to = self.id_gen.next();\n            // Get a pointer to the struct member to copy\n            match member.binding {\n                crate::Binding::BuiltIn(\n                    crate::BuiltIn::PointIndex\n                    | crate::BuiltIn::LineIndices\n                    | crate::BuiltIn::TriangleIndices,\n                ) => {\n                    body.push(Instruction::access_chain(\n                        self.get_pointer_type_id(member.ty_id, spirv::StorageClass::Output),\n                        ptr_to_copy_to,\n                        return_info.primitive_indices.unwrap(),\n                        &[val_i],\n                    ));\n                }\n                crate::Binding::BuiltIn(bi) => {\n                    body.push(Instruction::access_chain(\n                        self.get_pointer_type_id(member.ty_id, spirv::StorageClass::Output),\n                        ptr_to_copy_to,\n                        info.builtin_block.unwrap(),\n                        &[\n                            val_i,\n                            self.get_constant_scalar(crate::Literal::U32(builtin_index)),\n                        ],\n                    ));\n                    needs_y_flip = matches!(bi, crate::BuiltIn::Position { .. })\n                        && self.flags.contains(WriterFlags::ADJUST_COORDINATE_SPACE);\n                    builtin_index += 1;\n                }\n                crate::Binding::Location { .. } => {\n                    body.push(Instruction::access_chain(\n                        self.get_pointer_type_id(member.ty_id, spirv::StorageClass::Output),\n                        ptr_to_copy_to,\n                        info.bindings[binding_index],\n                        &[val_i],\n                    ));\n                    binding_index += 1;\n                }\n            }\n            body.push(Instruction::store(ptr_to_copy_to, val_to_copy, None));\n            // Flip the vertex position y coordinate in some cases\n            // Can't use epilogue flip because can't read from this storage class\n            if needs_y_flip {\n                let prev_y = self.id_gen.next();\n                body.push(Instruction::composite_extract(\n                    self.get_f32_type_id(),\n                    prev_y,\n                    val_to_copy,\n                    &[1],\n                ));\n                let new_y = self.id_gen.next();\n                body.push(Instruction::unary(\n                    spirv::Op::FNegate,\n                    self.get_f32_type_id(),\n                    new_y,\n                    prev_y,\n                ));\n                let new_ptr_to_copy_to = self.id_gen.next();\n                body.push(Instruction::access_chain(\n                    self.get_f32_pointer_type_id(spirv::StorageClass::Output),\n                    new_ptr_to_copy_to,\n                    ptr_to_copy_to,\n                    &[self.get_constant_scalar(crate::Literal::U32(1))],\n                ));\n                body.push(Instruction::store(new_ptr_to_copy_to, new_y, None));\n            }\n        }\n        body\n    }\n\n    /// Writes the return call for a mesh shader, which involves copying previously\n    /// written vertices/primitives into the actual output location.\n    pub(super) fn write_mesh_shader_return(\n        &mut self,\n        return_info: &MeshReturnInfo,\n        block: &mut Block,\n        loop_counter_vertices: u32,\n        loop_counter_primitives: u32,\n        local_invocation_index_id: Word,\n    ) -> Result<(), Error> {\n        let u32_id = self.get_u32_type_id();\n\n        // Load the actual vertex and primitive counts\n        let mut load_u32_by_member_index =\n            |members: &[MeshReturnMember], bi: crate::BuiltIn, max: u32| {\n                let member_index = members\n                    .iter()\n                    .position(|a| a.binding == crate::Binding::BuiltIn(bi))\n                    .unwrap() as u32;\n                let ptr_id = self.id_gen.next();\n                block.body.push(Instruction::access_chain(\n                    self.get_pointer_type_id(u32_id, spirv::StorageClass::Workgroup),\n                    ptr_id,\n                    return_info.out_variable_id,\n                    &[self.get_constant_scalar(crate::Literal::U32(member_index))],\n                ));\n                let before_min_id = self.id_gen.next();\n                block\n                    .body\n                    .push(Instruction::load(u32_id, before_min_id, ptr_id, None));\n\n                // Clamp the values\n                let id = self.id_gen.next();\n                block.body.push(Instruction::ext_inst_gl_op(\n                    self.gl450_ext_inst_id,\n                    spirv::GlslStd450Op::UMin,\n                    u32_id,\n                    id,\n                    &[before_min_id, max],\n                ));\n                id\n            };\n        let vert_count_id = load_u32_by_member_index(\n            &return_info.out_members,\n            crate::BuiltIn::VertexCount,\n            return_info.vertex_info.max_length_constant,\n        );\n        let prim_count_id = load_u32_by_member_index(\n            &return_info.out_members,\n            crate::BuiltIn::PrimitiveCount,\n            return_info.primitive_info.max_length_constant,\n        );\n\n        // Get pointers to the arrays of data to extract\n        let mut get_array_ptr = |bi: crate::BuiltIn, array_type_id: u32| {\n            let id = self.id_gen.next();\n            block.body.push(Instruction::access_chain(\n                self.get_pointer_type_id(array_type_id, spirv::StorageClass::Workgroup),\n                id,\n                return_info.out_variable_id,\n                &[self.get_constant_scalar(crate::Literal::U32(\n                    return_info\n                        .out_members\n                        .iter()\n                        .position(|a| a.binding == crate::Binding::BuiltIn(bi))\n                        .unwrap() as u32,\n                ))],\n            ));\n            id\n        };\n        let vert_array_ptr = get_array_ptr(\n            crate::BuiltIn::Vertices,\n            return_info.vertex_info.array_type_id,\n        );\n        let prim_array_ptr = get_array_ptr(\n            crate::BuiltIn::Primitives,\n            return_info.primitive_info.array_type_id,\n        );\n\n        // This must be called exactly once before any other mesh outputs are written\n        {\n            let mut ins = Instruction::new(spirv::Op::SetMeshOutputsEXT);\n            ins.add_operand(vert_count_id);\n            ins.add_operand(prim_count_id);\n            block.body.push(ins);\n        }\n\n        // This is iterating over every returned vertex and splitting\n        // it out into the multiple per-output arrays.\n        let vertex_loop_header = self.id_gen.next();\n        let prim_loop_header = self.id_gen.next();\n        let in_between_loops = self.id_gen.next();\n        let func_end = self.id_gen.next();\n\n        block.body.push(Instruction::store(\n            loop_counter_vertices,\n            local_invocation_index_id,\n            None,\n        ));\n        block.body.push(Instruction::branch(vertex_loop_header));\n\n        let vertex_copy_body = self.write_mesh_copy_body(\n            false,\n            return_info,\n            loop_counter_vertices,\n            vert_array_ptr,\n            prim_array_ptr,\n        );\n        // Write vertex copy loop\n        self.write_mesh_copy_loop(\n            &mut block.body,\n            vertex_copy_body,\n            vertex_loop_header,\n            in_between_loops,\n            vert_count_id,\n            loop_counter_vertices,\n            return_info,\n        );\n\n        // In between loops, reset the initial index\n        {\n            block.body.push(Instruction::label(in_between_loops));\n\n            block.body.push(Instruction::store(\n                loop_counter_primitives,\n                local_invocation_index_id,\n                None,\n            ));\n\n            block.body.push(Instruction::branch(prim_loop_header));\n        }\n        let primitive_copy_body = self.write_mesh_copy_body(\n            true,\n            return_info,\n            loop_counter_primitives,\n            vert_array_ptr,\n            prim_array_ptr,\n        );\n        // Write primitive copy loop\n        self.write_mesh_copy_loop(\n            &mut block.body,\n            primitive_copy_body,\n            prim_loop_header,\n            func_end,\n            prim_count_id,\n            loop_counter_primitives,\n            return_info,\n        );\n\n        block.body.push(Instruction::label(func_end));\n        Ok(())\n    }\n\n    pub(super) fn write_mesh_shader_wrapper(\n        &mut self,\n        return_info: &MeshReturnInfo,\n        inner_id: u32,\n    ) -> Result<u32, Error> {\n        let out_id = self.id_gen.next();\n        let mut function = super::Function::default();\n        let lookup_function_type = super::LookupFunctionType {\n            parameter_type_ids: alloc::vec![],\n            return_type_id: self.void_type,\n        };\n        let function_type = self.get_function_type(lookup_function_type);\n        function.signature = Some(Instruction::function(\n            self.void_type,\n            out_id,\n            spirv::FunctionControl::empty(),\n            function_type,\n        ));\n        let u32_id = self.get_u32_type_id();\n        {\n            let mut block = Block::new(self.id_gen.next());\n            // A general function variable that we guarantee to allow in the final return. It must be\n            // declared at the top of the function. Currently it is used in the memcpy part to keep\n            // track of the current index to copy.\n            let loop_counter_vertices = self.id_gen.next();\n            let loop_counter_primitives = self.id_gen.next();\n            block.body.insert(\n                0,\n                Instruction::variable(\n                    self.get_pointer_type_id(u32_id, spirv::StorageClass::Function),\n                    loop_counter_vertices,\n                    spirv::StorageClass::Function,\n                    None,\n                ),\n            );\n            block.body.insert(\n                1,\n                Instruction::variable(\n                    self.get_pointer_type_id(u32_id, spirv::StorageClass::Function),\n                    loop_counter_primitives,\n                    spirv::StorageClass::Function,\n                    None,\n                ),\n            );\n            let local_invocation_index_id = self.id_gen.next();\n            block.body.push(Instruction::load(\n                u32_id,\n                local_invocation_index_id,\n                return_info.local_invocation_index_var_id,\n                None,\n            ));\n            block.body.push(Instruction::function_call(\n                self.void_type,\n                self.id_gen.next(),\n                inner_id,\n                &[],\n            ));\n            self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut block.body);\n            self.write_mesh_shader_return(\n                return_info,\n                &mut block,\n                loop_counter_vertices,\n                loop_counter_primitives,\n                local_invocation_index_id,\n            )?;\n            function.consume(block, Instruction::return_void());\n        }\n        function.to_words(&mut self.logical_layout.function_definitions);\n        Ok(out_id)\n    }\n\n    pub(super) fn write_task_shader_wrapper(\n        &mut self,\n        task_payload: Word,\n        inner_id: u32,\n    ) -> Result<u32, Error> {\n        let out_id = self.id_gen.next();\n        let mut function = super::Function::default();\n        let lookup_function_type = super::LookupFunctionType {\n            parameter_type_ids: alloc::vec![],\n            return_type_id: self.void_type,\n        };\n        let function_type = self.get_function_type(lookup_function_type);\n        function.signature = Some(Instruction::function(\n            self.void_type,\n            out_id,\n            spirv::FunctionControl::empty(),\n            function_type,\n        ));\n\n        {\n            let mut block = Block::new(self.id_gen.next());\n            let result = self.id_gen.next();\n            block.body.push(Instruction::function_call(\n                self.get_vec3u_type_id(),\n                result,\n                inner_id,\n                &[],\n            ));\n            self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut block.body);\n            let final_value = if let Some(task_limits) = self.task_dispatch_limits {\n                let zero_u32 = self.get_constant_scalar(crate::Literal::U32(0));\n                let max_per_dim = self.get_constant_scalar(crate::Literal::U32(\n                    task_limits.max_mesh_workgroups_per_dim,\n                ));\n                let max_total = self.get_constant_scalar(crate::Literal::U32(\n                    task_limits.max_mesh_workgroups_total,\n                ));\n                let combined_struct_type = self.get_tuple_of_u32s_ty_id();\n                let values = [self.id_gen.next(), self.id_gen.next(), self.id_gen.next()];\n                for (i, value) in values.into_iter().enumerate() {\n                    block.body.push(Instruction::composite_extract(\n                        self.get_u32_type_id(),\n                        value,\n                        result,\n                        &[i as u32],\n                    ));\n                }\n                let prod_1 = self.id_gen.next();\n                let overflows = [self.id_gen.next(), self.id_gen.next()];\n                {\n                    let struct_out = self.id_gen.next();\n                    block.body.push(Instruction::binary(\n                        spirv::Op::UMulExtended,\n                        combined_struct_type,\n                        struct_out,\n                        values[0],\n                        values[1],\n                    ));\n                    block.body.push(Instruction::composite_extract(\n                        self.get_u32_type_id(),\n                        prod_1,\n                        struct_out,\n                        &[0],\n                    ));\n                    block.body.push(Instruction::composite_extract(\n                        self.get_u32_type_id(),\n                        overflows[0],\n                        struct_out,\n                        &[1],\n                    ));\n                }\n                let prod_final = self.id_gen.next();\n                {\n                    let struct_out = self.id_gen.next();\n                    block.body.push(Instruction::binary(\n                        spirv::Op::UMulExtended,\n                        combined_struct_type,\n                        struct_out,\n                        prod_1,\n                        values[2],\n                    ));\n                    block.body.push(Instruction::composite_extract(\n                        self.get_u32_type_id(),\n                        prod_final,\n                        struct_out,\n                        &[0],\n                    ));\n                    block.body.push(Instruction::composite_extract(\n                        self.get_u32_type_id(),\n                        overflows[1],\n                        struct_out,\n                        &[1],\n                    ));\n                }\n                let total_too_large = self.id_gen.next();\n                block.body.push(Instruction::binary(\n                    spirv::Op::UGreaterThan,\n                    self.get_bool_type_id(),\n                    total_too_large,\n                    prod_final,\n                    max_total,\n                ));\n\n                let too_large = [self.id_gen.next(), self.id_gen.next(), self.id_gen.next()];\n                for (i, value) in values.into_iter().enumerate() {\n                    block.body.push(Instruction::binary(\n                        spirv::Op::UGreaterThan,\n                        self.get_bool_type_id(),\n                        too_large[i],\n                        value,\n                        max_per_dim,\n                    ));\n                }\n                let overflow_happens = [self.id_gen.next(), self.id_gen.next()];\n                for (i, value) in overflows.into_iter().enumerate() {\n                    block.body.push(Instruction::binary(\n                        spirv::Op::INotEqual,\n                        self.get_bool_type_id(),\n                        overflow_happens[i],\n                        value,\n                        zero_u32,\n                    ));\n                }\n                let mut current_violates_limits = total_too_large;\n                for is_too_large in too_large {\n                    let new = self.id_gen.next();\n                    block.body.push(Instruction::binary(\n                        spirv::Op::LogicalOr,\n                        self.get_bool_type_id(),\n                        new,\n                        current_violates_limits,\n                        is_too_large,\n                    ));\n                    current_violates_limits = new;\n                }\n                for overflow_happens in overflow_happens {\n                    let new = self.id_gen.next();\n                    block.body.push(Instruction::binary(\n                        spirv::Op::LogicalOr,\n                        self.get_bool_type_id(),\n                        new,\n                        current_violates_limits,\n                        overflow_happens,\n                    ));\n                    current_violates_limits = new;\n                }\n                let zero_vec3 = self.id_gen.next();\n                block.body.push(Instruction::composite_construct(\n                    self.get_vec3u_type_id(),\n                    zero_vec3,\n                    &[zero_u32, zero_u32, zero_u32],\n                ));\n                let final_result = self.id_gen.next();\n                block.body.push(Instruction::select(\n                    self.get_vec3u_type_id(),\n                    final_result,\n                    current_violates_limits,\n                    zero_vec3,\n                    result,\n                ));\n                final_result\n            } else {\n                result\n            };\n            let ins =\n                self.write_entry_point_task_return(final_value, &mut block.body, task_payload)?;\n            function.consume(block, ins);\n        }\n        function.to_words(&mut self.logical_layout.function_definitions);\n        Ok(out_id)\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/mod.rs",
    "content": "/*!\nBackend for [SPIR-V][spv] (Standard Portable Intermediate Representation).\n\n# Layout of values in `uniform` buffers\n\nWGSL's [\"Internal Layout of Values\"][ilov] rules specify the memory layout of\neach WGSL type. The memory layout is important for data stored in `uniform` and\n`storage` buffers, especially when exchanging data with CPU code.\n\nBoth WGSL and Vulkan specify some conditions that a type's memory layout\nmust satisfy in order to use that type in a `uniform` or `storage` buffer.\nFor `storage` buffers, the WGSL and Vulkan restrictions are compatible, but\nfor `uniform` buffers, WGSL allows some types that Vulkan does not, requiring\nadjustments when emitting SPIR-V for `uniform` buffers.\n\n## Padding in two-row matrices\n\nSPIR-V provides detailed control over the layout of matrix types, and is\ncapable of describing the WGSL memory layout. However, Vulkan imposes\nadditional restrictions.\n\nVulkan's [\"extended layout\"][extended-layout] (also known as std140) rules\napply to types used in `uniform` buffers. Under these rules, matrices are\ndefined in terms of arrays of their vector type, and arrays are defined to have\nan alignment equal to the alignment of their element type rounded up to a\nmultiple of 16. This means that each column of the matrix has a minimum\nalignment of 16. WGSL, and consequently Naga IR, on the other hand specifies\ncolumn alignment equal to the alignment of the vector type, without being\nrounded up to 16.\n\nTo compensate for this, for any `struct` used as a `uniform` buffer which\ncontains a two-row matrix, we declare an additional \"std140 compatible\" type\nin which each column of the matrix has been decomposed into the containing\nstruct. For example, the following WGSL struct type:\n\n```ignore\nstruct Baz {\n    m: mat3x2<f32>,\n}\n```\n\nis rendered as the SPIR-V struct type:\n\n```ignore\nOpTypeStruct %v2float %v2float %v2float\n```\n\nThis has the effect that struct indices in Naga IR for such types do not\ncorrespond to the struct indices used in SPIR-V. A mapping of struct indices\nfor these types is maintained in [`Std140CompatTypeInfo`].\n\nAdditionally, any two-row matrices that are declared directly as uniform\nbuffers without being wrapped in a struct are declared as a struct containing a\nvector member for each column. Any array of a two-row matrix in a uniform\nbuffer is declared as an array of a struct containing a vector member for each\ncolumn. Any struct or array within a uniform buffer which contains a member or\nwhose base type requires a std140 compatible type declaration, itself requires a\nstd140 compatible type declaration.\n\nWhenever a value of such a type is [`loaded`] we insert code to convert the\nloaded value from the std140 compatible type to the regular type. This occurs\nin `BlockContext::write_checked_load`, making use of the wrapper function\ndefined by `Writer::write_wrapped_convert_from_std140_compat_type`. For matrices\nthat have been decomposed as separate columns in the containing struct, we load\neach column separately then composite the matrix type in\n`BlockContext::maybe_write_load_uniform_matcx2_struct_member`.\n\nWhenever a column of a matrix that has been decomposed into its containing\nstruct is [`accessed`] with a constant index we adjust the emitted access chain\nto access from the containing struct instead, in `BlockContext::write_access_chain`.\n\nWhenever a column of a uniform buffer two-row matrix is [`dynamically accessed`]\nwe must first load the matrix type, converting it from its std140 compatible\ntype as described above, then access the column using the wrapper function\ndefined by `Writer::write_wrapped_matcx2_get_column`. This is handled by\n`BlockContext::maybe_write_uniform_matcx2_dynamic_access`.\n\nNote that this approach differs somewhat from the equivalent code in the HLSL\nbackend. For HLSL all structs containing two-row matrices (or arrays of such)\nhave their declarations modified, not just those used as uniform buffers.\nTwo-row matrices and arrays of such only use modified type declarations when\nused as uniform buffers, or additionally when used as struct member in any\ncontext. This avoids the need to convert struct values when loading from uniform\nbuffers, but when loading arrays and matrices from uniform buffers or from any\nstruct the conversion is still required. In contrast, the approach used here\nalways requires converting *any* affected type when loading from a uniform\nbuffer, but consistently *only* when loading from a uniform buffer. As a result\nthis also means we only have to handle loads and not stores, as uniform buffers\nare read-only.\n\n[spv]: https://www.khronos.org/registry/SPIR-V/\n[ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout\n[extended-layout]: https://docs.vulkan.org/spec/latest/chapters/interfaces.html#interfaces-resources-layout\n[`loaded`]: crate::Expression::Load\n[`accessed`]: crate::Expression::AccessIndex\n[`dynamically accessed`]: crate::Expression::Access\n*/\n\nmod block;\nmod f16_polyfill;\nmod helpers;\nmod image;\nmod index;\nmod instructions;\nmod layout;\nmod mesh_shader;\nmod ray;\nmod reclaimable;\nmod selection;\nmod subgroup;\nmod writer;\n\npub use mesh_shader::{MeshReturnInfo, MeshReturnMember};\npub use spirv::{Capability, SourceLanguage};\n\nuse alloc::{string::String, vec::Vec};\nuse core::ops;\n\nuse spirv::Word;\nuse thiserror::Error;\n\nuse crate::arena::{Handle, HandleVec};\nuse crate::back::TaskDispatchLimits;\nuse crate::proc::{BoundsCheckPolicies, TypeResolution};\n\n#[derive(Clone)]\nstruct PhysicalLayout {\n    magic_number: Word,\n    version: Word,\n    generator: Word,\n    bound: Word,\n    instruction_schema: Word,\n}\n\n#[derive(Default)]\nstruct LogicalLayout {\n    capabilities: Vec<Word>,\n    extensions: Vec<Word>,\n    ext_inst_imports: Vec<Word>,\n    memory_model: Vec<Word>,\n    entry_points: Vec<Word>,\n    execution_modes: Vec<Word>,\n    debugs: Vec<Word>,\n    annotations: Vec<Word>,\n    declarations: Vec<Word>,\n    function_declarations: Vec<Word>,\n    function_definitions: Vec<Word>,\n}\n\n#[derive(Clone)]\nstruct Instruction {\n    op: spirv::Op,\n    wc: u32,\n    type_id: Option<Word>,\n    result_id: Option<Word>,\n    operands: Vec<Word>,\n}\n\nconst BITS_PER_BYTE: crate::Bytes = 8;\n\n#[derive(Clone, Debug, Error)]\npub enum Error {\n    #[error(\"The requested entry point couldn't be found\")]\n    EntryPointNotFound,\n    #[error(\"target SPIRV-{0}.{1} is not supported\")]\n    UnsupportedVersion(u8, u8),\n    #[error(\"using {0} requires at least one of the capabilities {1:?}, but none are available\")]\n    MissingCapabilities(&'static str, Vec<Capability>),\n    #[error(\"unimplemented {0}\")]\n    FeatureNotImplemented(&'static str),\n    #[error(\"module is not validated properly: {0}\")]\n    Validation(&'static str),\n    #[error(\"overrides should not be present at this stage\")]\n    Override,\n    #[error(transparent)]\n    ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError),\n    #[error(\"module requires SPIRV-{0}.{1}, which isn't supported\")]\n    SpirvVersionTooLow(u8, u8),\n    #[error(\"mapping of {0:?} is missing\")]\n    MissingBinding(crate::ResourceBinding),\n}\n\n#[derive(Default)]\nstruct IdGenerator(Word);\n\nimpl IdGenerator {\n    const fn next(&mut self) -> Word {\n        self.0 += 1;\n        self.0\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct DebugInfo<'a> {\n    pub source_code: &'a str,\n    pub file_name: &'a str,\n    pub language: SourceLanguage,\n}\n\n/// A SPIR-V block to which we are still adding instructions.\n///\n/// A `Block` represents a SPIR-V block that does not yet have a termination\n/// instruction like `OpBranch` or `OpReturn`.\n///\n/// The `OpLabel` that starts the block is implicit. It will be emitted based on\n/// `label_id` when we write the block to a `LogicalLayout`.\n///\n/// To terminate a `Block`, pass the block and the termination instruction to\n/// `Function::consume`. This takes ownership of the `Block` and transforms it\n/// into a `TerminatedBlock`.\nstruct Block {\n    label_id: Word,\n    body: Vec<Instruction>,\n}\n\n/// A SPIR-V block that ends with a termination instruction.\nstruct TerminatedBlock {\n    label_id: Word,\n    body: Vec<Instruction>,\n}\n\nimpl Block {\n    const fn new(label_id: Word) -> Self {\n        Block {\n            label_id,\n            body: Vec::new(),\n        }\n    }\n}\n\nstruct LocalVariable {\n    id: Word,\n    instruction: Instruction,\n}\n\nstruct ResultMember {\n    id: Word,\n    type_id: Word,\n    built_in: Option<crate::BuiltIn>,\n}\n\nstruct EntryPointContext {\n    argument_ids: Vec<Word>,\n    results: Vec<ResultMember>,\n    task_payload_variable_id: Option<Word>,\n    mesh_state: Option<MeshReturnInfo>,\n}\n\n#[derive(Default)]\nstruct Function {\n    signature: Option<Instruction>,\n    parameters: Vec<FunctionArgument>,\n    variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,\n    /// Map from a local variable that is a ray query to its u32 tracker.\n    ray_query_initialization_tracker_variables:\n        crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,\n    /// Map from a local variable that is a ray query to its tracker for the t max.\n    ray_query_t_max_tracker_variables:\n        crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,\n    /// List of local variables used as a counters to ensure that all loops are bounded.\n    force_loop_bounding_vars: Vec<LocalVariable>,\n\n    /// A map from a Naga expression to the temporary SPIR-V variable we have\n    /// spilled its value to, if any.\n    ///\n    /// Naga IR lets us apply [`Access`] expressions to expressions whose value\n    /// is an array or matrix---not a pointer to such---but SPIR-V doesn't have\n    /// instructions that can do the same. So when we encounter such code, we\n    /// spill the expression's value to a generated temporary variable. That, we\n    /// can obtain a pointer to, and then use an `OpAccessChain` instruction to\n    /// do whatever series of [`Access`] and [`AccessIndex`] operations we need\n    /// (with bounds checks). Finally, we generate an `OpLoad` to get the final\n    /// value.\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    spilled_composites: crate::FastIndexMap<Handle<crate::Expression>, LocalVariable>,\n\n    /// A set of expressions that are either in [`spilled_composites`] or refer\n    /// to some component/element of such.\n    ///\n    /// [`spilled_composites`]: Function::spilled_composites\n    spilled_accesses: crate::arena::HandleSet<crate::Expression>,\n\n    /// A map taking each expression to the number of [`Access`] and\n    /// [`AccessIndex`] expressions that uses it as a base value. If an\n    /// expression has no entry, its count is zero: it is never used as a\n    /// [`Access`] or [`AccessIndex`] base.\n    ///\n    /// We use this, together with [`ExpressionInfo::ref_count`], to recognize\n    /// the tips of chains of [`Access`] and [`AccessIndex`] expressions that\n    /// access spilled values --- expressions in [`spilled_composites`]. We\n    /// defer generating code for the chain until we reach its tip, so we can\n    /// handle it with a single instruction.\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    /// [`ExpressionInfo::ref_count`]: crate::valid::ExpressionInfo\n    /// [`spilled_composites`]: Function::spilled_composites\n    access_uses: crate::FastHashMap<Handle<crate::Expression>, usize>,\n\n    blocks: Vec<TerminatedBlock>,\n    entry_point_context: Option<EntryPointContext>,\n}\n\nimpl Function {\n    fn consume(&mut self, mut block: Block, termination: Instruction) {\n        block.body.push(termination);\n        self.blocks.push(TerminatedBlock {\n            label_id: block.label_id,\n            body: block.body,\n        })\n    }\n\n    fn parameter_id(&self, index: u32) -> Word {\n        match self.entry_point_context {\n            Some(ref context) => context.argument_ids[index as usize],\n            None => self.parameters[index as usize]\n                .instruction\n                .result_id\n                .unwrap(),\n        }\n    }\n}\n\n/// Characteristics of a SPIR-V `OpTypeImage` type.\n///\n/// SPIR-V requires non-composite types to be unique, including images. Since we\n/// use `LocalType` for this deduplication, it's essential that `LocalImageType`\n/// be equal whenever the corresponding `OpTypeImage`s would be. To reduce the\n/// likelihood of mistakes, we use fields that correspond exactly to the\n/// operands of an `OpTypeImage` instruction, using the actual SPIR-V types\n/// where practical.\n#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]\nstruct LocalImageType {\n    sampled_type: crate::Scalar,\n    dim: spirv::Dim,\n    flags: ImageTypeFlags,\n    image_format: spirv::ImageFormat,\n}\n\nbitflags::bitflags! {\n    /// Flags corresponding to the boolean(-ish) parameters to OpTypeImage.\n    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n    pub struct ImageTypeFlags: u8 {\n        const DEPTH = 0x1;\n        const ARRAYED = 0x2;\n        const MULTISAMPLED = 0x4;\n        const SAMPLED = 0x8;\n    }\n}\n\nimpl LocalImageType {\n    /// Construct a `LocalImageType` from the fields of a `TypeInner::Image`.\n    fn from_inner(dim: crate::ImageDimension, arrayed: bool, class: crate::ImageClass) -> Self {\n        let make_flags = |multi: bool, other: ImageTypeFlags| -> ImageTypeFlags {\n            let mut flags = other;\n            flags.set(ImageTypeFlags::ARRAYED, arrayed);\n            flags.set(ImageTypeFlags::MULTISAMPLED, multi);\n            flags\n        };\n\n        let dim = spirv::Dim::from(dim);\n\n        match class {\n            crate::ImageClass::Sampled { kind, multi } => LocalImageType {\n                sampled_type: crate::Scalar { kind, width: 4 },\n                dim,\n                flags: make_flags(multi, ImageTypeFlags::SAMPLED),\n                image_format: spirv::ImageFormat::Unknown,\n            },\n            crate::ImageClass::Depth { multi } => LocalImageType {\n                sampled_type: crate::Scalar {\n                    kind: crate::ScalarKind::Float,\n                    width: 4,\n                },\n                dim,\n                flags: make_flags(multi, ImageTypeFlags::DEPTH | ImageTypeFlags::SAMPLED),\n                image_format: spirv::ImageFormat::Unknown,\n            },\n            crate::ImageClass::Storage { format, access: _ } => LocalImageType {\n                sampled_type: format.into(),\n                dim,\n                flags: make_flags(false, ImageTypeFlags::empty()),\n                image_format: format.into(),\n            },\n            crate::ImageClass::External => unimplemented!(),\n        }\n    }\n}\n\n/// A numeric type, for use in [`LocalType`].\n#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]\nenum NumericType {\n    Scalar(crate::Scalar),\n    Vector {\n        size: crate::VectorSize,\n        scalar: crate::Scalar,\n    },\n    Matrix {\n        columns: crate::VectorSize,\n        rows: crate::VectorSize,\n        scalar: crate::Scalar,\n    },\n}\n\nimpl NumericType {\n    const fn from_inner(inner: &crate::TypeInner) -> Option<Self> {\n        match *inner {\n            crate::TypeInner::Scalar(scalar) | crate::TypeInner::Atomic(scalar) => {\n                Some(NumericType::Scalar(scalar))\n            }\n            crate::TypeInner::Vector { size, scalar } => Some(NumericType::Vector { size, scalar }),\n            crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => Some(NumericType::Matrix {\n                columns,\n                rows,\n                scalar,\n            }),\n            _ => None,\n        }\n    }\n\n    const fn scalar(self) -> crate::Scalar {\n        match self {\n            NumericType::Scalar(scalar)\n            | NumericType::Vector { scalar, .. }\n            | NumericType::Matrix { scalar, .. } => scalar,\n        }\n    }\n\n    const fn with_scalar(self, scalar: crate::Scalar) -> Self {\n        match self {\n            NumericType::Scalar(_) => NumericType::Scalar(scalar),\n            NumericType::Vector { size, .. } => NumericType::Vector { size, scalar },\n            NumericType::Matrix { columns, rows, .. } => NumericType::Matrix {\n                columns,\n                rows,\n                scalar,\n            },\n        }\n    }\n}\n\n/// A cooperative type, for use in [`LocalType`].\n#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]\nenum CooperativeType {\n    Matrix {\n        columns: crate::CooperativeSize,\n        rows: crate::CooperativeSize,\n        scalar: crate::Scalar,\n        role: crate::CooperativeRole,\n    },\n}\n\nimpl CooperativeType {\n    const fn from_inner(inner: &crate::TypeInner) -> Option<Self> {\n        match *inner {\n            crate::TypeInner::CooperativeMatrix {\n                columns,\n                rows,\n                scalar,\n                role,\n            } => Some(Self::Matrix {\n                columns,\n                rows,\n                scalar,\n                role,\n            }),\n            _ => None,\n        }\n    }\n}\n\n/// A SPIR-V type constructed during code generation.\n///\n/// This is the variant of [`LookupType`] used to represent types that might not\n/// be available in the arena. Variants are present here for one of two reasons:\n///\n/// -   They represent types synthesized during code generation, as explained\n///     in the documentation for [`LookupType`].\n///\n/// -   They represent types for which SPIR-V forbids duplicate `OpType...`\n///     instructions, requiring deduplication.\n///\n/// This is not a complete copy of [`TypeInner`]: for example, SPIR-V generation\n/// never synthesizes new struct types, so `LocalType` has nothing for that.\n///\n/// Each `LocalType` variant should be handled identically to its analogous\n/// `TypeInner` variant. You can use the [`Writer::localtype_from_inner`]\n/// function to help with this, by converting everything possible to a\n/// `LocalType` before inspecting it.\n///\n/// ## `LocalType` equality and SPIR-V `OpType` uniqueness\n///\n/// The definition of `Eq` on `LocalType` is carefully chosen to help us follow\n/// certain SPIR-V rules. SPIR-V §2.8 requires some classes of `OpType...`\n/// instructions to be unique; for example, you can't have two `OpTypeInt 32 1`\n/// instructions in the same module. All 32-bit signed integers must use the\n/// same type id.\n///\n/// All SPIR-V types that must be unique can be represented as a `LocalType`,\n/// and two `LocalType`s are always `Eq` if SPIR-V would require them to use the\n/// same `OpType...` instruction. This lets us avoid duplicates by recording the\n/// ids of the type instructions we've already generated in a hash table,\n/// [`Writer::lookup_type`], keyed by `LocalType`.\n///\n/// As another example, [`LocalImageType`], stored in the `LocalType::Image`\n/// variant, is designed to help us deduplicate `OpTypeImage` instructions. See\n/// its documentation for details.\n///\n/// SPIR-V does not require pointer types to be unique - but different\n/// SPIR-V ids are considered to be distinct pointer types. Since Naga\n/// uses structural type equality, we need to represent each Naga\n/// equivalence class with a single SPIR-V `OpTypePointer`.\n///\n/// As it always must, the `Hash` implementation respects the `Eq` relation.\n///\n/// [`TypeInner`]: crate::TypeInner\n#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]\nenum LocalType {\n    /// A numeric type.\n    Numeric(NumericType),\n    Cooperative(CooperativeType),\n    Pointer {\n        base: Word,\n        class: spirv::StorageClass,\n    },\n    Image(LocalImageType),\n    SampledImage {\n        image_type_id: Word,\n    },\n    Sampler,\n    BindingArray {\n        base: Handle<crate::Type>,\n        size: u32,\n    },\n    AccelerationStructure,\n    RayQuery,\n}\n\n/// A type encountered during SPIR-V generation.\n///\n/// In the process of writing SPIR-V, we need to synthesize various types for\n/// intermediate results and such: pointer types, vector/matrix component types,\n/// or even booleans, which usually appear in SPIR-V code even when they're not\n/// used by the module source.\n///\n/// However, we can't use `crate::Type` or `crate::TypeInner` for these, as the\n/// type arena may not contain what we need (it only contains types used\n/// directly by other parts of the IR), and the IR module is immutable, so we\n/// can't add anything to it.\n///\n/// So for local use in the SPIR-V writer, we use this type, which holds either\n/// a handle into the arena, or a [`LocalType`] containing something synthesized\n/// locally.\n///\n/// This is very similar to the [`proc::TypeResolution`] enum, with `LocalType`\n/// playing the role of `TypeInner`. However, `LocalType` also has other\n/// properties needed for SPIR-V generation; see the description of\n/// [`LocalType`] for details.\n///\n/// [`proc::TypeResolution`]: crate::proc::TypeResolution\n#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]\nenum LookupType {\n    Handle(Handle<crate::Type>),\n    Local(LocalType),\n}\n\nimpl From<LocalType> for LookupType {\n    fn from(local: LocalType) -> Self {\n        Self::Local(local)\n    }\n}\n\n#[derive(Debug, PartialEq, Clone, Hash, Eq)]\nstruct LookupFunctionType {\n    parameter_type_ids: Vec<Word>,\n    return_type_id: Word,\n}\n\n#[derive(Debug, PartialEq, Clone, Hash, Eq)]\nenum LookupRayQueryFunction {\n    Initialize,\n    Proceed,\n    GenerateIntersection,\n    ConfirmIntersection,\n    GetVertexPositions { committed: bool },\n    GetIntersection { committed: bool },\n    Terminate,\n}\n\n#[derive(Debug)]\nenum Dimension {\n    Scalar,\n    Vector,\n    Matrix,\n    CooperativeMatrix,\n}\n\n/// Key used to look up an operation which we have wrapped in a helper\n/// function, which should be called instead of directly emitting code\n/// for the expression. See [`Writer::wrapped_functions`].\n#[derive(Debug, Eq, PartialEq, Hash)]\nenum WrappedFunction {\n    BinaryOp {\n        op: crate::BinaryOperator,\n        left_type_id: Word,\n        right_type_id: Word,\n    },\n    ConvertFromStd140CompatType {\n        r#type: Handle<crate::Type>,\n    },\n    MatCx2GetColumn {\n        r#type: Handle<crate::Type>,\n    },\n}\n\n/// A map from evaluated [`Expression`](crate::Expression)s to their SPIR-V ids.\n///\n/// When we emit code to evaluate a given `Expression`, we record the\n/// SPIR-V id of its value here, under its `Handle<Expression>` index.\n///\n/// A `CachedExpressions` value can be indexed by a `Handle<Expression>` value.\n///\n/// [emit]: index.html#expression-evaluation-time-and-scope\n#[derive(Default)]\nstruct CachedExpressions {\n    ids: HandleVec<crate::Expression, Word>,\n}\nimpl CachedExpressions {\n    fn reset(&mut self, length: usize) {\n        self.ids.clear();\n        self.ids.resize(length, 0);\n    }\n}\nimpl ops::Index<Handle<crate::Expression>> for CachedExpressions {\n    type Output = Word;\n    fn index(&self, h: Handle<crate::Expression>) -> &Word {\n        let id = &self.ids[h];\n        if *id == 0 {\n            unreachable!(\"Expression {:?} is not cached!\", h);\n        }\n        id\n    }\n}\nimpl ops::IndexMut<Handle<crate::Expression>> for CachedExpressions {\n    fn index_mut(&mut self, h: Handle<crate::Expression>) -> &mut Word {\n        let id = &mut self.ids[h];\n        if *id != 0 {\n            unreachable!(\"Expression {:?} is already cached!\", h);\n        }\n        id\n    }\n}\nimpl reclaimable::Reclaimable for CachedExpressions {\n    fn reclaim(self) -> Self {\n        CachedExpressions {\n            ids: self.ids.reclaim(),\n        }\n    }\n}\n\n#[derive(Eq, Hash, PartialEq)]\nenum CachedConstant {\n    Literal(crate::proc::HashableLiteral),\n    Composite {\n        ty: LookupType,\n        constituent_ids: Vec<Word>,\n    },\n    ZeroValue(Word),\n}\n\n/// The SPIR-V representation of a [`crate::GlobalVariable`].\n///\n/// In the Vulkan spec 1.3.296, the section [Descriptor Set Interface][dsi] says:\n///\n/// > Variables identified with the `Uniform` storage class are used to access\n/// > transparent buffer backed resources. Such variables *must* be:\n/// >\n/// > -   typed as `OpTypeStruct`, or an array of this type,\n/// >\n/// > -   identified with a `Block` or `BufferBlock` decoration, and\n/// >\n/// > -   laid out explicitly using the `Offset`, `ArrayStride`, and `MatrixStride`\n/// >     decorations as specified in \"Offset and Stride Assignment\".\n///\n/// This is followed by identical language for the `StorageBuffer`,\n/// except that a `BufferBlock` decoration is not allowed.\n///\n/// When we encounter a global variable in the [`Storage`] or [`Uniform`]\n/// address spaces whose type is not already [`Struct`], this backend implicitly\n/// wraps the global variable in a struct: we generate a SPIR-V global variable\n/// holding an `OpTypeStruct` with a single member, whose type is what the Naga\n/// global's type would suggest, decorated as required above.\n///\n/// The [`helpers::global_needs_wrapper`] function determines whether a given\n/// [`crate::GlobalVariable`] needs to be wrapped.\n///\n/// [dsi]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#interfaces-resources-descset\n/// [`Storage`]: crate::AddressSpace::Storage\n/// [`Uniform`]: crate::AddressSpace::Uniform\n/// [`Struct`]: crate::TypeInner::Struct\n#[derive(Clone)]\nstruct GlobalVariable {\n    /// The SPIR-V id of the `OpVariable` that declares the global.\n    ///\n    /// If this global has been implicitly wrapped in an `OpTypeStruct`, this id\n    /// refers to the wrapper, not the original Naga value it contains. If you\n    /// need the Naga value, use [`access_id`] instead of this field.\n    ///\n    /// If this global is not implicitly wrapped, this is the same as\n    /// [`access_id`].\n    ///\n    /// This is used to compute the `access_id` pointer in function prologues,\n    /// and used for `ArrayLength` expressions, which need to pass the wrapper\n    /// struct.\n    ///\n    /// [`access_id`]: GlobalVariable::access_id\n    var_id: Word,\n\n    /// The loaded value of a `AddressSpace::Handle` global variable.\n    ///\n    /// If the current function uses this global variable, this is the id of an\n    /// `OpLoad` instruction in the function's prologue that loads its value.\n    /// (This value is assigned as we write the prologue code of each function.)\n    /// It is then used for all operations on the global, such as `OpImageSample`.\n    handle_id: Word,\n\n    /// The SPIR-V id of a pointer to this variable's Naga IR value.\n    ///\n    /// If the current function uses this global variable, and it has been\n    /// implicitly wrapped in an `OpTypeStruct`, this is the id of an\n    /// `OpAccessChain` instruction in the function's prologue that refers to\n    /// the wrapped value inside the struct. (This value is assigned as we write\n    /// the prologue code of each function.) If you need the wrapper struct\n    /// itself, use [`var_id`] instead of this field.\n    ///\n    /// If this global is not implicitly wrapped, this is the same as\n    /// [`var_id`].\n    ///\n    /// [`var_id`]: GlobalVariable::var_id\n    access_id: Word,\n}\n\nimpl GlobalVariable {\n    const fn dummy() -> Self {\n        Self {\n            var_id: 0,\n            handle_id: 0,\n            access_id: 0,\n        }\n    }\n\n    const fn new(id: Word) -> Self {\n        Self {\n            var_id: id,\n            handle_id: 0,\n            access_id: 0,\n        }\n    }\n\n    /// Prepare `self` for use within a single function.\n    const fn reset_for_function(&mut self) {\n        self.handle_id = 0;\n        self.access_id = 0;\n    }\n}\n\nstruct FunctionArgument {\n    /// Actual instruction of the argument.\n    instruction: Instruction,\n    handle_id: Word,\n}\n\n/// Tracks the expressions for which the backend emits the following instructions:\n/// - OpConstantTrue\n/// - OpConstantFalse\n/// - OpConstant\n/// - OpConstantComposite\n/// - OpConstantNull\nstruct ExpressionConstnessTracker {\n    inner: crate::arena::HandleSet<crate::Expression>,\n}\n\nimpl ExpressionConstnessTracker {\n    fn from_arena(arena: &crate::Arena<crate::Expression>) -> Self {\n        let mut inner = crate::arena::HandleSet::for_arena(arena);\n        for (handle, expr) in arena.iter() {\n            let insert = match *expr {\n                crate::Expression::Literal(_)\n                | crate::Expression::ZeroValue(_)\n                | crate::Expression::Constant(_) => true,\n                crate::Expression::Compose { ref components, .. } => {\n                    components.iter().all(|&h| inner.contains(h))\n                }\n                crate::Expression::Splat { value, .. } => inner.contains(value),\n                _ => false,\n            };\n            if insert {\n                inner.insert(handle);\n            }\n        }\n        Self { inner }\n    }\n\n    fn is_const(&self, value: Handle<crate::Expression>) -> bool {\n        self.inner.contains(value)\n    }\n}\n\n/// General information needed to emit SPIR-V for Naga statements.\nstruct BlockContext<'w> {\n    /// The writer handling the module to which this code belongs.\n    writer: &'w mut Writer,\n\n    /// The [`Module`](crate::Module) for which we're generating code.\n    ir_module: &'w crate::Module,\n\n    /// The [`Function`](crate::Function) for which we're generating code.\n    ir_function: &'w crate::Function,\n\n    /// Information module validation produced about\n    /// [`ir_function`](BlockContext::ir_function).\n    fun_info: &'w crate::valid::FunctionInfo,\n\n    /// The [`spv::Function`](Function) to which we are contributing SPIR-V instructions.\n    function: &'w mut Function,\n\n    /// SPIR-V ids for expressions we've evaluated.\n    cached: CachedExpressions,\n\n    /// The `Writer`'s temporary vector, for convenience.\n    temp_list: Vec<Word>,\n\n    /// Tracks the constness of `Expression`s residing in `self.ir_function.expressions`\n    expression_constness: ExpressionConstnessTracker,\n\n    force_loop_bounding: bool,\n\n    /// Hash from an expression whose type is a ray query / pointer to a ray query to its tracker.\n    /// Note: this is sparse, so can't be a handle vec\n    ray_query_tracker_expr: crate::FastHashMap<Handle<crate::Expression>, RayQueryTrackers>,\n}\n\n#[derive(Clone, Copy)]\nstruct RayQueryTrackers {\n    // Initialization tracker\n    initialized_tracker: Word,\n    // Tracks the t max from ray query initialize.\n    // Unlike HLSL, spir-v's equivalent getter for the current committed t has UB (instead of just\n    // returning t_max) if there was no previous hit (though in some places it treats the behaviour as\n    // defined), therefore we must track the tmax inputted into ray query initialize.\n    t_max_tracker: Word,\n}\n\nimpl BlockContext<'_> {\n    const fn gen_id(&mut self) -> Word {\n        self.writer.id_gen.next()\n    }\n\n    fn get_type_id(&mut self, lookup_type: LookupType) -> Word {\n        self.writer.get_type_id(lookup_type)\n    }\n\n    fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {\n        self.writer.get_handle_type_id(handle)\n    }\n\n    fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {\n        self.writer.get_expression_type_id(tr)\n    }\n\n    fn get_index_constant(&mut self, index: Word) -> Word {\n        self.writer.get_constant_scalar(crate::Literal::U32(index))\n    }\n\n    fn get_scope_constant(&mut self, scope: Word) -> Word {\n        self.writer\n            .get_constant_scalar(crate::Literal::I32(scope as _))\n    }\n\n    fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {\n        self.writer.get_pointer_type_id(base, class)\n    }\n\n    fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {\n        self.writer.get_numeric_type_id(numeric)\n    }\n}\n\n/// Information about a type for which we have declared a std140 layout\n/// compatible variant, because the type is used in a uniform but does not\n/// adhere to std140 requirements. The uniform will be declared using the\n/// type `type_id`, and the result of any `Load` will be immediately converted\n/// to the base type. This is used for matrices with 2 rows, as well as any\n/// arrays or structs containing such matrices.\npub struct Std140CompatTypeInfo {\n    /// ID of the std140 compatible type declaration.\n    type_id: Word,\n    /// For structs, a mapping of Naga IR struct member indices to the indices\n    /// used in the generated SPIR-V. For non-struct types this will be empty.\n    member_indices: Vec<u32>,\n}\n\npub struct Writer {\n    physical_layout: PhysicalLayout,\n    logical_layout: LogicalLayout,\n    id_gen: IdGenerator,\n\n    /// The set of capabilities modules are permitted to use.\n    ///\n    /// This is initialized from `Options::capabilities`.\n    capabilities_available: Option<crate::FastHashSet<Capability>>,\n\n    /// The set of capabilities used by this module.\n    ///\n    /// If `capabilities_available` is `Some`, then this is always a subset of\n    /// that.\n    capabilities_used: crate::FastIndexSet<Capability>,\n\n    /// The set of spirv extensions used.\n    extensions_used: crate::FastIndexSet<&'static str>,\n\n    debug_strings: Vec<Instruction>,\n    debugs: Vec<Instruction>,\n    annotations: Vec<Instruction>,\n    flags: WriterFlags,\n    bounds_check_policies: BoundsCheckPolicies,\n    zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,\n    force_loop_bounding: bool,\n    use_storage_input_output_16: bool,\n    void_type: Word,\n    tuple_of_u32s_ty_id: Option<Word>,\n    //TODO: convert most of these into vectors, addressable by handle indices\n    lookup_type: crate::FastHashMap<LookupType, Word>,\n    lookup_function: crate::FastHashMap<Handle<crate::Function>, Word>,\n    lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>,\n    /// Operations which have been wrapped in a helper function. The value is\n    /// the ID of the function, which should be called instead of emitting code\n    /// for the operation directly.\n    wrapped_functions: crate::FastHashMap<WrappedFunction, Word>,\n    /// Indexed by const-expression handle indexes\n    constant_ids: HandleVec<crate::Expression, Word>,\n    cached_constants: crate::FastHashMap<CachedConstant, Word>,\n    global_variables: HandleVec<crate::GlobalVariable, GlobalVariable>,\n    std140_compat_uniform_types: crate::FastHashMap<Handle<crate::Type>, Std140CompatTypeInfo>,\n    fake_missing_bindings: bool,\n    binding_map: BindingMap,\n\n    // Cached expressions are only meaningful within a BlockContext, but we\n    // retain the table here between functions to save heap allocations.\n    saved_cached: CachedExpressions,\n\n    gl450_ext_inst_id: Word,\n\n    // Just a temporary list of SPIR-V ids\n    temp_list: Vec<Word>,\n\n    ray_query_functions: crate::FastHashMap<LookupRayQueryFunction, Word>,\n\n    /// F16 I/O polyfill manager for handling `f16` input/output variables\n    /// when `StorageInputOutput16` capability is not available.\n    io_f16_polyfills: f16_polyfill::F16IoPolyfill,\n\n    /// Non semantic debug printf extension `OpExtInstImport`\n    debug_printf: Option<Word>,\n    pub(crate) ray_query_initialization_tracking: bool,\n\n    /// Limits to the mesh shader dispatch group a task workgroup can dispatch.\n    ///\n    /// Metal for example limits to 1024 workgroups per task shader dispatch. Dispatching more is\n    /// undefined behavior, so this would validate that to dispatch zero workgroups.\n    task_dispatch_limits: Option<TaskDispatchLimits>,\n    /// If true, naga may generate checks that the primitive indices are valid in the output.\n    ///\n    /// Currently this validation is unimplemented.\n    mesh_shader_primitive_indices_clamp: bool,\n}\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct WriterFlags: u32 {\n        /// Include debug labels for everything.\n        const DEBUG = 0x1;\n\n        /// Flip Y coordinate of [`BuiltIn::Position`] output.\n        ///\n        /// [`BuiltIn::Position`]: crate::BuiltIn::Position\n        const ADJUST_COORDINATE_SPACE = 0x2;\n\n        /// Emit [`OpName`][op] for input/output locations.\n        ///\n        /// Contrary to spec, some drivers treat it as semantic, not allowing\n        /// any conflicts.\n        ///\n        /// [op]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpName\n        const LABEL_VARYINGS = 0x4;\n\n        /// Emit [`PointSize`] output builtin to vertex shaders, which is\n        /// required for drawing with `PointList` topology.\n        ///\n        /// [`PointSize`]: crate::BuiltIn::PointSize\n        const FORCE_POINT_SIZE = 0x8;\n\n        /// Clamp [`BuiltIn::FragDepth`] output between 0 and 1.\n        ///\n        /// [`BuiltIn::FragDepth`]: crate::BuiltIn::FragDepth\n        const CLAMP_FRAG_DEPTH = 0x10;\n\n        /// Instead of silently failing if the arguments to generate a ray query are\n        /// invalid, uses debug printf extension to print to the command line\n        ///\n        /// Note: VK_KHR_shader_non_semantic_info must be enabled. This will have no\n        /// effect if `options.ray_query_initialization_tracking` is set to false.\n        const PRINT_ON_RAY_QUERY_INITIALIZATION_FAIL = 0x20;\n    }\n}\n\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct BindingInfo {\n    pub descriptor_set: u32,\n    pub binding: u32,\n    /// If the binding is an unsized binding array, this overrides the size.\n    pub binding_array_size: Option<u32>,\n}\n\n// Using `BTreeMap` instead of `HashMap` so that we can hash itself.\npub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindingInfo>;\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum ZeroInitializeWorkgroupMemoryMode {\n    /// Via `VK_KHR_zero_initialize_workgroup_memory` or Vulkan 1.3\n    Native,\n    /// Via assignments + barrier\n    Polyfill,\n    None,\n}\n\n#[derive(Debug, Clone)]\npub struct Options<'a> {\n    /// (Major, Minor) target version of the SPIR-V.\n    pub lang_version: (u8, u8),\n\n    /// Configuration flags for the writer.\n    pub flags: WriterFlags,\n\n    /// Don't panic on missing bindings. Instead use fake values for `Binding`\n    /// and `DescriptorSet` decorations. This may result in invalid SPIR-V.\n    pub fake_missing_bindings: bool,\n\n    /// Map of resources to information about the binding.\n    pub binding_map: BindingMap,\n\n    /// If given, the set of capabilities modules are allowed to use. Code that\n    /// requires capabilities beyond these is rejected with an error.\n    ///\n    /// If this is `None`, all capabilities are permitted.\n    pub capabilities: Option<crate::FastHashSet<Capability>>,\n\n    /// How should generate code handle array, vector, matrix, or image texel\n    /// indices that are out of range?\n    pub bounds_check_policies: BoundsCheckPolicies,\n\n    /// Dictates the way workgroup variables should be zero initialized\n    pub zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,\n\n    /// If set, loops will have code injected into them, forcing the compiler\n    /// to think the number of iterations is bounded.\n    pub force_loop_bounding: bool,\n\n    /// if set, ray queries will get a variable to track their state to prevent\n    /// misuse.\n    pub ray_query_initialization_tracking: bool,\n\n    /// Whether to use the `StorageInputOutput16` capability for `f16` shader I/O.\n    /// When false, `f16` I/O is polyfilled using `f32` types with conversions.\n    pub use_storage_input_output_16: bool,\n\n    pub debug_info: Option<DebugInfo<'a>>,\n\n    pub task_dispatch_limits: Option<TaskDispatchLimits>,\n\n    pub mesh_shader_primitive_indices_clamp: bool,\n}\n\nimpl Default for Options<'_> {\n    fn default() -> Self {\n        let mut flags = WriterFlags::ADJUST_COORDINATE_SPACE\n            | WriterFlags::LABEL_VARYINGS\n            | WriterFlags::CLAMP_FRAG_DEPTH;\n        if cfg!(debug_assertions) {\n            flags |= WriterFlags::DEBUG;\n        }\n        Options {\n            lang_version: (1, 0),\n            flags,\n            fake_missing_bindings: true,\n            binding_map: BindingMap::default(),\n            capabilities: None,\n            bounds_check_policies: BoundsCheckPolicies::default(),\n            zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode::Polyfill,\n            force_loop_bounding: true,\n            ray_query_initialization_tracking: true,\n            use_storage_input_output_16: true,\n            debug_info: None,\n            task_dispatch_limits: None,\n            mesh_shader_primitive_indices_clamp: true,\n        }\n    }\n}\n\n// A subset of options meant to be changed per pipeline.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct PipelineOptions {\n    /// The stage of the entry point.\n    pub shader_stage: crate::ShaderStage,\n    /// The name of the entry point.\n    ///\n    /// If no entry point that matches is found while creating a [`Writer`], a error will be thrown.\n    pub entry_point: String,\n}\n\npub fn write_vec(\n    module: &crate::Module,\n    info: &crate::valid::ModuleInfo,\n    options: &Options,\n    pipeline_options: Option<&PipelineOptions>,\n) -> Result<Vec<u32>, Error> {\n    let mut words: Vec<u32> = Vec::new();\n    let mut w = Writer::new(options)?;\n\n    w.write(\n        module,\n        info,\n        pipeline_options,\n        &options.debug_info,\n        &mut words,\n    )?;\n    Ok(words)\n}\n\npub fn supported_capabilities() -> crate::valid::Capabilities {\n    use crate::valid::Capabilities as Caps;\n\n    Caps::IMMEDIATES\n        | Caps::FLOAT64\n        | Caps::PRIMITIVE_INDEX\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY\n        | Caps::BUFFER_BINDING_ARRAY\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY\n        | Caps::STORAGE_BUFFER_BINDING_ARRAY\n        | Caps::ACCELERATION_STRUCTURE_BINDING_ARRAY\n        | Caps::CLIP_DISTANCES\n        // No cull distance\n        | Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS\n        | Caps::MULTIVIEW\n        | Caps::EARLY_DEPTH_TEST\n        | Caps::MULTISAMPLED_SHADING\n        | Caps::RAY_QUERY\n        | Caps::DUAL_SOURCE_BLENDING\n        | Caps::CUBE_ARRAY_TEXTURES\n        | Caps::SHADER_INT64\n        | Caps::SUBGROUP\n        | Caps::SUBGROUP_BARRIER\n        | Caps::SUBGROUP_VERTEX_STAGE\n        | Caps::SHADER_INT64_ATOMIC_MIN_MAX\n        | Caps::SHADER_INT64_ATOMIC_ALL_OPS\n        | Caps::SHADER_FLOAT32_ATOMIC\n        | Caps::TEXTURE_ATOMIC\n        | Caps::TEXTURE_INT64_ATOMIC\n        | Caps::RAY_HIT_VERTEX_POSITION\n        | Caps::SHADER_FLOAT16\n        // No TEXTURE_EXTERNAL\n        | Caps::SHADER_FLOAT16_IN_FLOAT32\n        | Caps::SHADER_BARYCENTRICS\n        | Caps::MESH_SHADER\n        | Caps::MESH_SHADER_POINT_TOPOLOGY\n        | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        // No BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n        | Caps::COOPERATIVE_MATRIX\n        | Caps::PER_VERTEX\n        // No RAY_TRACING_PIPELINE\n        | Caps::DRAW_INDEX\n        | Caps::MEMORY_DECORATION_COHERENT\n        | Caps::MEMORY_DECORATION_VOLATILE\n}\n"
  },
  {
    "path": "naga/src/back/spv/ray/mod.rs",
    "content": "/*!\nModule for code shared between ray queries and ray tracing pipeline code.\nRay tracing pipelines are not yet implemented, so this is empty.\n*/\n\npub mod query;\n"
  },
  {
    "path": "naga/src/back/spv/ray/query.rs",
    "content": "/*!\nGenerating SPIR-V for ray query operations.\n*/\n\nuse alloc::{vec, vec::Vec};\n\nuse super::super::{\n    Block, BlockContext, Function, FunctionArgument, Instruction, LocalType, LookupFunctionType,\n    LookupRayQueryFunction, NumericType, Writer, WriterFlags,\n};\nuse crate::{arena::Handle, back::RayQueryPoint};\n\n/// helper function to check if a particular flag is set in a u32.\nfn write_ray_flags_contains_flags(\n    writer: &mut Writer,\n    block: &mut Block,\n    id: spirv::Word,\n    flag: u32,\n) -> spirv::Word {\n    let bit_id = writer.get_constant_scalar(crate::Literal::U32(flag));\n    let zero_id = writer.get_constant_scalar(crate::Literal::U32(0));\n    let u32_type_id = writer.get_u32_type_id();\n    let bool_ty = writer.get_bool_type_id();\n\n    let and_id = writer.id_gen.next();\n    block.body.push(Instruction::binary(\n        spirv::Op::BitwiseAnd,\n        u32_type_id,\n        and_id,\n        id,\n        bit_id,\n    ));\n\n    let eq_id = writer.id_gen.next();\n    block.body.push(Instruction::binary(\n        spirv::Op::INotEqual,\n        bool_ty,\n        eq_id,\n        and_id,\n        zero_id,\n    ));\n\n    eq_id\n}\n\nimpl Writer {\n    /// writes a logical and of two scalar booleans\n    fn write_logical_and(\n        &mut self,\n        block: &mut Block,\n        one: spirv::Word,\n        two: spirv::Word,\n    ) -> spirv::Word {\n        let id = self.id_gen.next();\n        let bool_id = self.get_bool_type_id();\n        block.body.push(Instruction::binary(\n            spirv::Op::LogicalAnd,\n            bool_id,\n            id,\n            one,\n            two,\n        ));\n        id\n    }\n\n    fn write_reduce_and(&mut self, block: &mut Block, mut bools: Vec<spirv::Word>) -> spirv::Word {\n        // The combined `and`ed together of all of the bools up to this point.\n        let mut current_combined = bools.pop().unwrap();\n        for boolean in bools {\n            current_combined = self.write_logical_and(block, current_combined, boolean)\n        }\n        current_combined\n    }\n\n    // returns the id of the function, the function, and ids for its arguments.\n    fn write_function_signature(\n        &mut self,\n        arg_types: &[spirv::Word],\n        return_ty: spirv::Word,\n    ) -> (spirv::Word, Function, Vec<spirv::Word>) {\n        let func_ty = self.get_function_type(LookupFunctionType {\n            parameter_type_ids: Vec::from(arg_types),\n            return_type_id: return_ty,\n        });\n\n        let mut function = Function::default();\n        let func_id = self.id_gen.next();\n        function.signature = Some(Instruction::function(\n            return_ty,\n            func_id,\n            spirv::FunctionControl::empty(),\n            func_ty,\n        ));\n\n        let mut arg_ids = Vec::with_capacity(arg_types.len());\n\n        for (idx, &arg_ty) in arg_types.iter().enumerate() {\n            let id = self.id_gen.next();\n            let instruction = Instruction::function_parameter(arg_ty, id);\n            function.parameters.push(FunctionArgument {\n                instruction,\n                handle_id: idx as u32,\n            });\n            arg_ids.push(id);\n        }\n        (func_id, function, arg_ids)\n    }\n\n    pub(in super::super) fn write_ray_query_get_intersection_function(\n        &mut self,\n        is_committed: bool,\n        ir_module: &crate::Module,\n    ) -> spirv::Word {\n        if let Some(&word) =\n            self.ray_query_functions\n                .get(&LookupRayQueryFunction::GetIntersection {\n                    committed: is_committed,\n                })\n        {\n            return word;\n        }\n        let ray_intersection = ir_module.special_types.ray_intersection.unwrap();\n        let intersection_type_id = self.get_handle_type_id(ray_intersection);\n        let intersection_pointer_type_id =\n            self.get_pointer_type_id(intersection_type_id, spirv::StorageClass::Function);\n\n        let flag_type_id = self.get_u32_type_id();\n        let flag_pointer_type_id =\n            self.get_pointer_type_id(flag_type_id, spirv::StorageClass::Function);\n\n        let transform_type_id = self.get_numeric_type_id(NumericType::Matrix {\n            columns: crate::VectorSize::Quad,\n            rows: crate::VectorSize::Tri,\n            scalar: crate::Scalar::F32,\n        });\n        let transform_pointer_type_id =\n            self.get_pointer_type_id(transform_type_id, spirv::StorageClass::Function);\n\n        let barycentrics_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Bi,\n            scalar: crate::Scalar::F32,\n        });\n        let barycentrics_pointer_type_id =\n            self.get_pointer_type_id(barycentrics_type_id, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n        let bool_pointer_type_id =\n            self.get_pointer_type_id(bool_type_id, spirv::StorageClass::Function);\n\n        let scalar_type_id = self.get_f32_type_id();\n        let float_pointer_type_id = self.get_f32_pointer_type_id(spirv::StorageClass::Function);\n\n        let argument_type_id = self.get_ray_query_pointer_id();\n\n        let (func_id, mut function, arg_ids) = self.write_function_signature(\n            &[argument_type_id, flag_pointer_type_id],\n            intersection_type_id,\n        );\n\n        let query_id = arg_ids[0];\n        let intersection_tracker_id = arg_ids[1];\n\n        let label_id = self.id_gen.next();\n        let mut block = Block::new(label_id);\n\n        let blank_intersection = self.get_constant_null(intersection_type_id);\n        let blank_intersection_id = self.id_gen.next();\n        // This must be before everything else in the function.\n        block.body.push(Instruction::variable(\n            intersection_pointer_type_id,\n            blank_intersection_id,\n            spirv::StorageClass::Function,\n            Some(blank_intersection),\n        ));\n\n        let intersection_id = self.get_constant_scalar(crate::Literal::U32(if is_committed {\n            spirv::RayQueryIntersection::RayQueryCommittedIntersectionKHR\n        } else {\n            spirv::RayQueryIntersection::RayQueryCandidateIntersectionKHR\n        } as _));\n\n        let loaded_ray_query_tracker_id = self.id_gen.next();\n        block.body.push(Instruction::load(\n            flag_type_id,\n            loaded_ray_query_tracker_id,\n            intersection_tracker_id,\n            None,\n        ));\n        let proceeded_id = write_ray_flags_contains_flags(\n            self,\n            &mut block,\n            loaded_ray_query_tracker_id,\n            RayQueryPoint::PROCEED.bits(),\n        );\n        let finished_proceed_id = write_ray_flags_contains_flags(\n            self,\n            &mut block,\n            loaded_ray_query_tracker_id,\n            RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n        );\n        let proceed_finished_correct_id = if is_committed {\n            finished_proceed_id\n        } else {\n            let not_finished_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                not_finished_id,\n                finished_proceed_id,\n            ));\n            not_finished_id\n        };\n\n        let is_valid_id =\n            self.write_logical_and(&mut block, proceed_finished_correct_id, proceeded_id);\n\n        let valid_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_id);\n\n        let final_label_id = self.id_gen.next();\n        let mut final_block = Block::new(final_label_id);\n\n        block.body.push(Instruction::selection_merge(\n            final_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            block,\n            Instruction::branch_conditional(is_valid_id, valid_id, final_label_id),\n        );\n\n        let raw_kind_id = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTypeKHR,\n                flag_type_id,\n                raw_kind_id,\n                query_id,\n                intersection_id,\n            ));\n        let kind_id = if is_committed {\n            // Nothing to do: the IR value matches `spirv::RayQueryCommittedIntersectionType`\n            raw_kind_id\n        } else {\n            // Remap from the candidate kind to IR\n            let condition_id = self.id_gen.next();\n            let committed_triangle_kind_id = self.get_constant_scalar(crate::Literal::U32(\n                spirv::RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionTriangleKHR\n                    as _,\n            ));\n            valid_block.body.push(Instruction::binary(\n                spirv::Op::IEqual,\n                self.get_bool_type_id(),\n                condition_id,\n                raw_kind_id,\n                committed_triangle_kind_id,\n            ));\n            let kind_id = self.id_gen.next();\n            valid_block.body.push(Instruction::select(\n                flag_type_id,\n                kind_id,\n                condition_id,\n                self.get_constant_scalar(crate::Literal::U32(\n                    crate::RayQueryIntersection::Triangle as _,\n                )),\n                self.get_constant_scalar(crate::Literal::U32(\n                    crate::RayQueryIntersection::Aabb as _,\n                )),\n            ));\n            kind_id\n        };\n        let idx_id = self.get_index_constant(0);\n        let access_idx = self.id_gen.next();\n        valid_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        valid_block\n            .body\n            .push(Instruction::store(access_idx, kind_id, None));\n\n        let not_none_comp_id = self.id_gen.next();\n        let none_id =\n            self.get_constant_scalar(crate::Literal::U32(crate::RayQueryIntersection::None as _));\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::INotEqual,\n            self.get_bool_type_id(),\n            not_none_comp_id,\n            kind_id,\n            none_id,\n        ));\n\n        let not_none_label_id = self.id_gen.next();\n        let mut not_none_block = Block::new(not_none_label_id);\n\n        let outer_merge_label_id = self.id_gen.next();\n        let outer_merge_block = Block::new(outer_merge_label_id);\n\n        valid_block.body.push(Instruction::selection_merge(\n            outer_merge_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            valid_block,\n            Instruction::branch_conditional(\n                not_none_comp_id,\n                not_none_label_id,\n                outer_merge_label_id,\n            ),\n        );\n\n        let instance_custom_index_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionInstanceCustomIndexKHR,\n                flag_type_id,\n                instance_custom_index_id,\n                query_id,\n                intersection_id,\n            ));\n        let instance_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionInstanceIdKHR,\n                flag_type_id,\n                instance_id,\n                query_id,\n                intersection_id,\n            ));\n        let sbt_record_offset_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR,\n                flag_type_id,\n                sbt_record_offset_id,\n                query_id,\n                intersection_id,\n            ));\n        let geometry_index_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionGeometryIndexKHR,\n                flag_type_id,\n                geometry_index_id,\n                query_id,\n                intersection_id,\n            ));\n        let primitive_index_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionPrimitiveIndexKHR,\n                flag_type_id,\n                primitive_index_id,\n                query_id,\n                intersection_id,\n            ));\n\n        //Note: there is also `OpRayQueryGetIntersectionCandidateAABBOpaqueKHR`,\n        // but it's not a property of an intersection.\n\n        let object_to_world_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionObjectToWorldKHR,\n                transform_type_id,\n                object_to_world_id,\n                query_id,\n                intersection_id,\n            ));\n        let world_to_object_id = self.id_gen.next();\n        not_none_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionWorldToObjectKHR,\n                transform_type_id,\n                world_to_object_id,\n                query_id,\n                intersection_id,\n            ));\n\n        // instance custom index\n        let idx_id = self.get_index_constant(2);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block.body.push(Instruction::store(\n            access_idx,\n            instance_custom_index_id,\n            None,\n        ));\n\n        // instance\n        let idx_id = self.get_index_constant(3);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, instance_id, None));\n\n        let idx_id = self.get_index_constant(4);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, sbt_record_offset_id, None));\n\n        let idx_id = self.get_index_constant(5);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, geometry_index_id, None));\n\n        let idx_id = self.get_index_constant(6);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            flag_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, primitive_index_id, None));\n\n        let idx_id = self.get_index_constant(9);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            transform_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, object_to_world_id, None));\n\n        let idx_id = self.get_index_constant(10);\n        let access_idx = self.id_gen.next();\n        not_none_block.body.push(Instruction::access_chain(\n            transform_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        not_none_block\n            .body\n            .push(Instruction::store(access_idx, world_to_object_id, None));\n\n        let tri_comp_id = self.id_gen.next();\n        let tri_id = self.get_constant_scalar(crate::Literal::U32(\n            crate::RayQueryIntersection::Triangle as _,\n        ));\n        not_none_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            self.get_bool_type_id(),\n            tri_comp_id,\n            kind_id,\n            tri_id,\n        ));\n\n        let tri_label_id = self.id_gen.next();\n        let mut tri_block = Block::new(tri_label_id);\n\n        let merge_label_id = self.id_gen.next();\n        let merge_block = Block::new(merge_label_id);\n        // t\n        {\n            let block = if is_committed {\n                &mut not_none_block\n            } else {\n                &mut tri_block\n            };\n            let t_id = self.id_gen.next();\n            block.body.push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTKHR,\n                scalar_type_id,\n                t_id,\n                query_id,\n                intersection_id,\n            ));\n            let idx_id = self.get_index_constant(1);\n            let access_idx = self.id_gen.next();\n            block.body.push(Instruction::access_chain(\n                float_pointer_type_id,\n                access_idx,\n                blank_intersection_id,\n                &[idx_id],\n            ));\n            block.body.push(Instruction::store(access_idx, t_id, None));\n        }\n        not_none_block.body.push(Instruction::selection_merge(\n            merge_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            not_none_block,\n            Instruction::branch_conditional(not_none_comp_id, tri_label_id, merge_label_id),\n        );\n\n        let barycentrics_id = self.id_gen.next();\n        tri_block.body.push(Instruction::ray_query_get_intersection(\n            spirv::Op::RayQueryGetIntersectionBarycentricsKHR,\n            barycentrics_type_id,\n            barycentrics_id,\n            query_id,\n            intersection_id,\n        ));\n\n        let front_face_id = self.id_gen.next();\n        tri_block.body.push(Instruction::ray_query_get_intersection(\n            spirv::Op::RayQueryGetIntersectionFrontFaceKHR,\n            bool_type_id,\n            front_face_id,\n            query_id,\n            intersection_id,\n        ));\n\n        let idx_id = self.get_index_constant(7);\n        let access_idx = self.id_gen.next();\n        tri_block.body.push(Instruction::access_chain(\n            barycentrics_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        tri_block\n            .body\n            .push(Instruction::store(access_idx, barycentrics_id, None));\n\n        let idx_id = self.get_index_constant(8);\n        let access_idx = self.id_gen.next();\n        tri_block.body.push(Instruction::access_chain(\n            bool_pointer_type_id,\n            access_idx,\n            blank_intersection_id,\n            &[idx_id],\n        ));\n        tri_block\n            .body\n            .push(Instruction::store(access_idx, front_face_id, None));\n        function.consume(tri_block, Instruction::branch(merge_label_id));\n        function.consume(merge_block, Instruction::branch(outer_merge_label_id));\n        function.consume(outer_merge_block, Instruction::branch(final_label_id));\n\n        let loaded_blank_intersection_id = self.id_gen.next();\n        final_block.body.push(Instruction::load(\n            intersection_type_id,\n            loaded_blank_intersection_id,\n            blank_intersection_id,\n            None,\n        ));\n        function.consume(\n            final_block,\n            Instruction::return_value(loaded_blank_intersection_id),\n        );\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n        self.ray_query_functions.insert(\n            LookupRayQueryFunction::GetIntersection {\n                committed: is_committed,\n            },\n            func_id,\n        );\n        func_id\n    }\n\n    fn write_ray_query_initialize(&mut self, ir_module: &crate::Module) -> spirv::Word {\n        if let Some(&word) = self\n            .ray_query_functions\n            .get(&LookupRayQueryFunction::Initialize)\n        {\n            return word;\n        }\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n        let acceleration_structure_type_id =\n            self.get_localtype_id(LocalType::AccelerationStructure);\n        let ray_desc_type_id = self.get_handle_type_id(\n            ir_module\n                .special_types\n                .ray_desc\n                .expect(\"ray desc should be set if ray queries are being initialized\"),\n        );\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let f32_type_id = self.get_f32_type_id();\n        let f32_ptr_ty = self.get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n        let bool_vec3_type_id = self.get_vec3_bool_type_id();\n\n        let (func_id, mut function, arg_ids) = self.write_function_signature(\n            &[\n                ray_query_type_id,\n                acceleration_structure_type_id,\n                ray_desc_type_id,\n                u32_ptr_ty,\n                f32_ptr_ty,\n            ],\n            self.void_type,\n        );\n\n        let query_id = arg_ids[0];\n        let acceleration_structure_id = arg_ids[1];\n        let desc_id = arg_ids[2];\n        let init_tracker_id = arg_ids[3];\n        let t_max_tracker_id = arg_ids[4];\n\n        let label_id = self.id_gen.next();\n        let mut block = Block::new(label_id);\n\n        let flag_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32));\n\n        //Note: composite extract indices and types must match `generate_ray_desc_type`\n        let ray_flags_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            flag_type_id,\n            ray_flags_id,\n            desc_id,\n            &[0],\n        ));\n        let cull_mask_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            flag_type_id,\n            cull_mask_id,\n            desc_id,\n            &[1],\n        ));\n\n        let tmin_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            f32_type_id,\n            tmin_id,\n            desc_id,\n            &[2],\n        ));\n        let tmax_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            f32_type_id,\n            tmax_id,\n            desc_id,\n            &[3],\n        ));\n        block\n            .body\n            .push(Instruction::store(t_max_tracker_id, tmax_id, None));\n\n        let vector_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Tri,\n            scalar: crate::Scalar::F32,\n        });\n        let ray_origin_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            vector_type_id,\n            ray_origin_id,\n            desc_id,\n            &[4],\n        ));\n        let ray_dir_id = self.id_gen.next();\n        block.body.push(Instruction::composite_extract(\n            vector_type_id,\n            ray_dir_id,\n            desc_id,\n            &[5],\n        ));\n\n        let valid_id = self.ray_query_initialization_tracking.then(||{\n            let tmin_le_tmax_id = self.id_gen.next();\n            // Check both that tmin is less than or equal to tmax (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06350)\n            // and implicitly that neither tmin or tmax are NaN (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06351)\n            // because this checks if tmin and tmax are ordered too (i.e: not NaN).\n            block.body.push(Instruction::binary(\n                spirv::Op::FOrdLessThanEqual,\n                bool_type_id,\n                tmin_le_tmax_id,\n                tmin_id,\n                tmax_id,\n            ));\n\n            // Check that tmin is greater than or equal to 0 (and\n            // therefore also tmax is too because it is greater than\n            // or equal to tmin) (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06349).\n            let tmin_ge_zero_id = self.id_gen.next();\n            let zero_id = self.get_constant_scalar(crate::Literal::F32(0.0));\n            block.body.push(Instruction::binary(\n                spirv::Op::FOrdGreaterThanEqual,\n                bool_type_id,\n                tmin_ge_zero_id,\n                tmin_id,\n                zero_id,\n            ));\n\n            // Check that ray origin is finite (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06348)\n            let ray_origin_infinite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::IsInf,\n                bool_vec3_type_id,\n                ray_origin_infinite_id,\n                ray_origin_id,\n            ));\n            let any_ray_origin_infinite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::Any,\n                bool_type_id,\n                any_ray_origin_infinite_id,\n                ray_origin_infinite_id,\n            ));\n\n            let ray_origin_nan_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::IsNan,\n                bool_vec3_type_id,\n                ray_origin_nan_id,\n                ray_origin_id,\n            ));\n            let any_ray_origin_nan_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::Any,\n                bool_type_id,\n                any_ray_origin_nan_id,\n                ray_origin_nan_id,\n            ));\n\n            let ray_origin_not_finite_id = self.id_gen.next();\n            block.body.push(Instruction::binary(\n                spirv::Op::LogicalOr,\n                bool_type_id,\n                ray_origin_not_finite_id,\n                any_ray_origin_nan_id,\n                any_ray_origin_infinite_id,\n            ));\n\n            let all_ray_origin_finite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                all_ray_origin_finite_id,\n                ray_origin_not_finite_id,\n            ));\n\n            // Check that ray direction is finite (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06348)\n            let ray_dir_infinite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::IsInf,\n                bool_vec3_type_id,\n                ray_dir_infinite_id,\n                ray_dir_id,\n            ));\n            let any_ray_dir_infinite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::Any,\n                bool_type_id,\n                any_ray_dir_infinite_id,\n                ray_dir_infinite_id,\n            ));\n\n            let ray_dir_nan_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::IsNan,\n                bool_vec3_type_id,\n                ray_dir_nan_id,\n                ray_dir_id,\n            ));\n            let any_ray_dir_nan_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::Any,\n                bool_type_id,\n                any_ray_dir_nan_id,\n                ray_dir_nan_id,\n            ));\n\n            let ray_dir_not_finite_id = self.id_gen.next();\n            block.body.push(Instruction::binary(\n                spirv::Op::LogicalOr,\n                bool_type_id,\n                ray_dir_not_finite_id,\n                any_ray_dir_nan_id,\n                any_ray_dir_infinite_id,\n            ));\n\n            let all_ray_dir_finite_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                all_ray_dir_finite_id,\n                ray_dir_not_finite_id,\n            ));\n\n            /// Writes spirv to check that less than two booleans are true\n            ///\n            /// For each boolean: removes it, `and`s it with all others (i.e for all possible combinations of two booleans in the list checks to see if both are true).\n            /// Then `or`s all of these checks together. This produces whether two or more booleans are true.\n            fn write_less_than_2_true(\n                writer: &mut Writer,\n                block: &mut Block,\n                mut bools: Vec<spirv::Word>,\n            ) -> spirv::Word {\n                assert!(bools.len() > 1, \"Must have multiple booleans!\");\n                let bool_ty = writer.get_bool_type_id();\n                let mut each_two_true = Vec::new();\n                while let Some(last_bool) = bools.pop() {\n                    for &bool in &bools {\n                        let both_true_id = writer.write_logical_and(\n                            block,\n                            last_bool,\n                            bool,\n                        );\n                        each_two_true.push(both_true_id);\n                    }\n                }\n                let mut all_or_id = each_two_true.pop().expect(\"since this must have multiple booleans, there must be at least one thing in `each_two_true`\");\n                for two_true in each_two_true {\n                    let new_all_or_id = writer.id_gen.next();\n                    block.body.push(Instruction::binary(\n                        spirv::Op::LogicalOr,\n                        bool_ty,\n                        new_all_or_id,\n                        all_or_id,\n                        two_true,\n                    ));\n                    all_or_id = new_all_or_id;\n                }\n\n                let less_than_two_id = writer.id_gen.next();\n                block.body.push(Instruction::unary(\n                    spirv::Op::LogicalNot,\n                    bool_ty,\n                    less_than_two_id,\n                    all_or_id,\n                ));\n                less_than_two_id\n            }\n\n            // Check that at most one of skip triangles and skip AABBs is\n            // present (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06889)\n            let contains_skip_triangles = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::SKIP_TRIANGLES.bits(),\n            );\n            let contains_skip_aabbs = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::SKIP_AABBS.bits(),\n            );\n\n            let not_contain_skip_triangles_aabbs = write_less_than_2_true(\n                self,\n                &mut block,\n                vec![contains_skip_triangles, contains_skip_aabbs],\n            );\n\n            // Check that at most one of skip triangles (taken from above check),\n            // cull back facing, and cull front face is present (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06890)\n            let contains_cull_back = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::CULL_BACK_FACING.bits(),\n            );\n            let contains_cull_front = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::CULL_FRONT_FACING.bits(),\n            );\n\n            let not_contain_skip_triangles_cull = write_less_than_2_true(\n                self,\n                &mut block,\n                vec![\n                    contains_skip_triangles,\n                    contains_cull_back,\n                    contains_cull_front,\n                ],\n            );\n\n            // Check that at most one of force opaque, force not opaque, cull opaque,\n            // and cull not opaque are present (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06891)\n            let contains_opaque = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::FORCE_OPAQUE.bits(),\n            );\n            let contains_no_opaque = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::FORCE_NO_OPAQUE.bits(),\n            );\n            let contains_cull_opaque = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::CULL_OPAQUE.bits(),\n            );\n            let contains_cull_no_opaque = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                ray_flags_id,\n                crate::RayFlag::CULL_NO_OPAQUE.bits(),\n            );\n\n            let not_contain_multiple_opaque = write_less_than_2_true(\n                self,\n                &mut block,\n                vec![\n                    contains_opaque,\n                    contains_no_opaque,\n                    contains_cull_opaque,\n                    contains_cull_no_opaque,\n                ],\n            );\n\n            // Combine all checks into a single flag saying whether the call is valid or not.\n            self.write_reduce_and(\n                &mut block,\n                vec![\n                    tmin_le_tmax_id,\n                    tmin_ge_zero_id,\n                    all_ray_origin_finite_id,\n                    all_ray_dir_finite_id,\n                    not_contain_skip_triangles_aabbs,\n                    not_contain_skip_triangles_cull,\n                    not_contain_multiple_opaque,\n                ],\n            )\n        });\n\n        let merge_label_id = self.id_gen.next();\n        let merge_block = Block::new(merge_label_id);\n\n        // NOTE: this block will be unreachable if initialization tracking is disabled.\n        let invalid_label_id = self.id_gen.next();\n        let mut invalid_block = Block::new(invalid_label_id);\n\n        let valid_label_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_label_id);\n\n        match valid_id {\n            Some(all_valid_id) => {\n                block.body.push(Instruction::selection_merge(\n                    merge_label_id,\n                    spirv::SelectionControl::NONE,\n                ));\n                function.consume(\n                    block,\n                    Instruction::branch_conditional(all_valid_id, valid_label_id, invalid_label_id),\n                );\n            }\n            None => {\n                function.consume(block, Instruction::branch(valid_label_id));\n            }\n        }\n\n        valid_block.body.push(Instruction::ray_query_initialize(\n            query_id,\n            acceleration_structure_id,\n            ray_flags_id,\n            cull_mask_id,\n            ray_origin_id,\n            tmin_id,\n            ray_dir_id,\n            tmax_id,\n        ));\n\n        let const_initialized =\n            self.get_constant_scalar(crate::Literal::U32(RayQueryPoint::INITIALIZED.bits()));\n        valid_block\n            .body\n            .push(Instruction::store(init_tracker_id, const_initialized, None));\n\n        function.consume(valid_block, Instruction::branch(merge_label_id));\n\n        if self\n            .flags\n            .contains(WriterFlags::PRINT_ON_RAY_QUERY_INITIALIZATION_FAIL)\n        {\n            self.write_debug_printf(\n                &mut invalid_block,\n                \"Naga ignored invalid arguments to rayQueryInitialize with flags: %u t_min: %f t_max: %f origin: %v4f dir: %v4f\",\n                &[\n                    ray_flags_id,\n                    tmin_id,\n                    tmax_id,\n                    ray_origin_id,\n                    ray_dir_id,\n                ],\n            );\n        }\n\n        function.consume(invalid_block, Instruction::branch(merge_label_id));\n\n        function.consume(merge_block, Instruction::return_void());\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        self.ray_query_functions\n            .insert(LookupRayQueryFunction::Initialize, func_id);\n        func_id\n    }\n\n    fn write_ray_query_proceed(&mut self) -> spirv::Word {\n        if let Some(&word) = self\n            .ray_query_functions\n            .get(&LookupRayQueryFunction::Proceed)\n        {\n            return word;\n        }\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n        let bool_ptr_ty = self.get_pointer_type_id(bool_type_id, spirv::StorageClass::Function);\n\n        let (func_id, mut function, arg_ids) =\n            self.write_function_signature(&[ray_query_type_id, u32_ptr_ty], bool_type_id);\n\n        let query_id = arg_ids[0];\n        let init_tracker_id = arg_ids[1];\n\n        let block_id = self.id_gen.next();\n        let mut block = Block::new(block_id);\n\n        // TODO: perhaps this could be replaced with an OpPhi?\n        let proceeded_id = self.id_gen.next();\n        let const_false = self.get_constant_scalar(crate::Literal::Bool(false));\n        block.body.push(Instruction::variable(\n            bool_ptr_ty,\n            proceeded_id,\n            spirv::StorageClass::Function,\n            Some(const_false),\n        ));\n\n        let initialized_tracker_id = self.id_gen.next();\n        block.body.push(Instruction::load(\n            u32_ty,\n            initialized_tracker_id,\n            init_tracker_id,\n            None,\n        ));\n\n        let merge_id = self.id_gen.next();\n        let mut merge_block = Block::new(merge_id);\n\n        let valid_block_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_block_id);\n\n        let instruction = if self.ray_query_initialization_tracking {\n            let is_initialized = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::INITIALIZED.bits(),\n            );\n\n            block.body.push(Instruction::selection_merge(\n                merge_id,\n                spirv::SelectionControl::NONE,\n            ));\n\n            Instruction::branch_conditional(is_initialized, valid_block_id, merge_id)\n        } else {\n            Instruction::branch(valid_block_id)\n        };\n\n        function.consume(block, instruction);\n\n        let has_proceeded = self.id_gen.next();\n        valid_block.body.push(Instruction::ray_query_proceed(\n            bool_type_id,\n            has_proceeded,\n            query_id,\n        ));\n\n        valid_block\n            .body\n            .push(Instruction::store(proceeded_id, has_proceeded, None));\n\n        let add_flag_finished = self.get_constant_scalar(crate::Literal::U32(\n            (RayQueryPoint::PROCEED | RayQueryPoint::FINISHED_TRAVERSAL).bits(),\n        ));\n        let add_flag_continuing =\n            self.get_constant_scalar(crate::Literal::U32(RayQueryPoint::PROCEED.bits()));\n\n        let add_flags_id = self.id_gen.next();\n        valid_block.body.push(Instruction::select(\n            u32_ty,\n            add_flags_id,\n            has_proceeded,\n            add_flag_continuing,\n            add_flag_finished,\n        ));\n        let final_flags = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::BitwiseOr,\n            u32_ty,\n            final_flags,\n            initialized_tracker_id,\n            add_flags_id,\n        ));\n        valid_block\n            .body\n            .push(Instruction::store(init_tracker_id, final_flags, None));\n\n        function.consume(valid_block, Instruction::branch(merge_id));\n\n        let loaded_proceeded_id = self.id_gen.next();\n        merge_block.body.push(Instruction::load(\n            bool_type_id,\n            loaded_proceeded_id,\n            proceeded_id,\n            None,\n        ));\n\n        function.consume(merge_block, Instruction::return_value(loaded_proceeded_id));\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        self.ray_query_functions\n            .insert(LookupRayQueryFunction::Proceed, func_id);\n        func_id\n    }\n\n    fn write_ray_query_generate_intersection(&mut self) -> spirv::Word {\n        if let Some(&word) = self\n            .ray_query_functions\n            .get(&LookupRayQueryFunction::GenerateIntersection)\n        {\n            return word;\n        }\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let f32_type_id = self.get_f32_type_id();\n        let f32_ptr_type_id = self.get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n\n        let (func_id, mut function, arg_ids) = self.write_function_signature(\n            &[ray_query_type_id, u32_ptr_ty, f32_type_id, f32_ptr_type_id],\n            self.void_type,\n        );\n\n        let query_id = arg_ids[0];\n        let init_tracker_id = arg_ids[1];\n        let depth_id = arg_ids[2];\n        let t_max_tracker_id = arg_ids[3];\n\n        let block_id = self.id_gen.next();\n        let mut block = Block::new(block_id);\n\n        let current_t = self.id_gen.next();\n        block.body.push(Instruction::variable(\n            f32_ptr_type_id,\n            current_t,\n            spirv::StorageClass::Function,\n            None,\n        ));\n\n        let current_t = self.id_gen.next();\n        block.body.push(Instruction::variable(\n            f32_ptr_type_id,\n            current_t,\n            spirv::StorageClass::Function,\n            None,\n        ));\n\n        let valid_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_id);\n\n        let final_label_id = self.id_gen.next();\n        let final_block = Block::new(final_label_id);\n\n        let instruction = if self.ray_query_initialization_tracking {\n            let initialized_tracker_id = self.id_gen.next();\n            block.body.push(Instruction::load(\n                u32_ty,\n                initialized_tracker_id,\n                init_tracker_id,\n                None,\n            ));\n\n            let proceeded_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::PROCEED.bits(),\n            );\n            let finished_proceed_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            );\n\n            // Can't find anything to suggest double calling this function is invalid.\n\n            let not_finished_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                not_finished_id,\n                finished_proceed_id,\n            ));\n\n            let is_valid_id = self.write_logical_and(&mut block, not_finished_id, proceeded_id);\n\n            block.body.push(Instruction::selection_merge(\n                final_label_id,\n                spirv::SelectionControl::NONE,\n            ));\n\n            Instruction::branch_conditional(is_valid_id, valid_id, final_label_id)\n        } else {\n            Instruction::branch(valid_id)\n        };\n\n        function.consume(block, instruction);\n\n        let intersection_id = self.get_constant_scalar(crate::Literal::U32(\n            spirv::RayQueryIntersection::RayQueryCandidateIntersectionKHR as _,\n        ));\n        let committed_intersection_id = self.get_constant_scalar(crate::Literal::U32(\n            spirv::RayQueryIntersection::RayQueryCommittedIntersectionKHR as _,\n        ));\n        let raw_kind_id = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTypeKHR,\n                u32_ty,\n                raw_kind_id,\n                query_id,\n                intersection_id,\n            ));\n\n        let candidate_aabb_id = self.get_constant_scalar(crate::Literal::U32(\n            spirv::RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionAABBKHR as _,\n        ));\n        let intersection_aabb_id = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            intersection_aabb_id,\n            raw_kind_id,\n            candidate_aabb_id,\n        ));\n\n        // Check that the provided t value is between t min and the current committed\n        // t value, (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-RuntimeSpirv-OpRayQueryGenerateIntersectionKHR-06353)\n\n        // Get the tmin\n        let t_min_id = self.id_gen.next();\n        valid_block.body.push(Instruction::ray_query_get_t_min(\n            f32_type_id,\n            t_min_id,\n            query_id,\n        ));\n\n        // Get the current committed t, or tmax if no hit.\n        // Basically emulate HLSL's (easier) version\n        // Pseudo-code:\n        // ````wgsl\n        // // start of function\n        // var current_t:f32;\n        // ...\n        // let committed_type_id = RayQueryGetIntersectionTypeKHR<Committed>(query_id);\n        // if committed_type_id == Committed_None {\n        //     current_t = load(t_max_tracker);\n        // } else {\n        //     current_t = RayQueryGetIntersectionTKHR<Committed>(query_id);\n        // }\n        // ...\n        // ````\n\n        let committed_type_id = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTypeKHR,\n                u32_ty,\n                committed_type_id,\n                query_id,\n                committed_intersection_id,\n            ));\n\n        let no_committed = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            no_committed,\n            committed_type_id,\n            self.get_constant_scalar(crate::Literal::U32(\n                spirv::RayQueryCommittedIntersectionType::RayQueryCommittedIntersectionNoneKHR as _,\n            )),\n        ));\n\n        let next_valid_block_id = self.id_gen.next();\n        let no_committed_block_id = self.id_gen.next();\n        let mut no_committed_block = Block::new(no_committed_block_id);\n        let committed_block_id = self.id_gen.next();\n        let mut committed_block = Block::new(committed_block_id);\n        valid_block.body.push(Instruction::selection_merge(\n            next_valid_block_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            valid_block,\n            Instruction::branch_conditional(\n                no_committed,\n                no_committed_block_id,\n                committed_block_id,\n            ),\n        );\n\n        // Assign t_max to current_t\n        let t_max_id = self.id_gen.next();\n        no_committed_block.body.push(Instruction::load(\n            f32_type_id,\n            t_max_id,\n            t_max_tracker_id,\n            None,\n        ));\n        no_committed_block\n            .body\n            .push(Instruction::store(current_t, t_max_id, None));\n        function.consume(no_committed_block, Instruction::branch(next_valid_block_id));\n\n        // Assign t_current to current_t\n        let latest_t_id = self.id_gen.next();\n        committed_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTKHR,\n                f32_type_id,\n                latest_t_id,\n                query_id,\n                intersection_id,\n            ));\n        committed_block\n            .body\n            .push(Instruction::store(current_t, latest_t_id, None));\n        function.consume(committed_block, Instruction::branch(next_valid_block_id));\n\n        let mut valid_block = Block::new(next_valid_block_id);\n\n        let t_ge_t_min = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::FOrdGreaterThanEqual,\n            bool_type_id,\n            t_ge_t_min,\n            depth_id,\n            t_min_id,\n        ));\n        let t_current = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::load(f32_type_id, t_current, current_t, None));\n        let t_le_t_current = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::FOrdLessThanEqual,\n            bool_type_id,\n            t_le_t_current,\n            depth_id,\n            t_current,\n        ));\n\n        let t_in_range = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::LogicalAnd,\n            bool_type_id,\n            t_in_range,\n            t_ge_t_min,\n            t_le_t_current,\n        ));\n\n        let call_valid_id = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::LogicalAnd,\n            bool_type_id,\n            call_valid_id,\n            t_in_range,\n            intersection_aabb_id,\n        ));\n\n        let generate_label_id = self.id_gen.next();\n        let mut generate_block = Block::new(generate_label_id);\n\n        let merge_label_id = self.id_gen.next();\n        let merge_block = Block::new(merge_label_id);\n\n        valid_block.body.push(Instruction::selection_merge(\n            merge_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            valid_block,\n            Instruction::branch_conditional(call_valid_id, generate_label_id, merge_label_id),\n        );\n\n        generate_block\n            .body\n            .push(Instruction::ray_query_generate_intersection(\n                query_id, depth_id,\n            ));\n\n        function.consume(generate_block, Instruction::branch(merge_label_id));\n        function.consume(merge_block, Instruction::branch(final_label_id));\n\n        function.consume(final_block, Instruction::return_void());\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        self.ray_query_functions\n            .insert(LookupRayQueryFunction::GenerateIntersection, func_id);\n        func_id\n    }\n\n    fn write_ray_query_confirm_intersection(&mut self) -> spirv::Word {\n        if let Some(&word) = self\n            .ray_query_functions\n            .get(&LookupRayQueryFunction::ConfirmIntersection)\n        {\n            return word;\n        }\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n\n        let (func_id, mut function, arg_ids) =\n            self.write_function_signature(&[ray_query_type_id, u32_ptr_ty], self.void_type);\n\n        let query_id = arg_ids[0];\n        let init_tracker_id = arg_ids[1];\n\n        let block_id = self.id_gen.next();\n        let mut block = Block::new(block_id);\n\n        let valid_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_id);\n\n        let final_label_id = self.id_gen.next();\n        let final_block = Block::new(final_label_id);\n\n        let instruction = if self.ray_query_initialization_tracking {\n            let initialized_tracker_id = self.id_gen.next();\n            block.body.push(Instruction::load(\n                u32_ty,\n                initialized_tracker_id,\n                init_tracker_id,\n                None,\n            ));\n\n            let proceeded_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::PROCEED.bits(),\n            );\n            let finished_proceed_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            );\n            // Although it seems strange to call this twice, I (Vecvec) can't find anything to suggest double calling this function is invalid.\n            let not_finished_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                not_finished_id,\n                finished_proceed_id,\n            ));\n\n            let is_valid_id = self.write_logical_and(&mut block, not_finished_id, proceeded_id);\n\n            block.body.push(Instruction::selection_merge(\n                final_label_id,\n                spirv::SelectionControl::NONE,\n            ));\n\n            Instruction::branch_conditional(is_valid_id, valid_id, final_label_id)\n        } else {\n            Instruction::branch(valid_id)\n        };\n\n        function.consume(block, instruction);\n\n        let intersection_id = self.get_constant_scalar(crate::Literal::U32(\n            spirv::RayQueryIntersection::RayQueryCandidateIntersectionKHR as _,\n        ));\n        let raw_kind_id = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTypeKHR,\n                u32_ty,\n                raw_kind_id,\n                query_id,\n                intersection_id,\n            ));\n\n        let candidate_tri_id = self.get_constant_scalar(crate::Literal::U32(\n            spirv::RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionTriangleKHR as _,\n        ));\n        let intersection_tri_id = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            intersection_tri_id,\n            raw_kind_id,\n            candidate_tri_id,\n        ));\n\n        let generate_label_id = self.id_gen.next();\n        let mut generate_block = Block::new(generate_label_id);\n\n        let merge_label_id = self.id_gen.next();\n        let merge_block = Block::new(merge_label_id);\n\n        valid_block.body.push(Instruction::selection_merge(\n            merge_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            valid_block,\n            Instruction::branch_conditional(intersection_tri_id, generate_label_id, merge_label_id),\n        );\n\n        generate_block\n            .body\n            .push(Instruction::ray_query_confirm_intersection(query_id));\n\n        function.consume(generate_block, Instruction::branch(merge_label_id));\n        function.consume(merge_block, Instruction::branch(final_label_id));\n\n        function.consume(final_block, Instruction::return_void());\n\n        self.ray_query_functions\n            .insert(LookupRayQueryFunction::ConfirmIntersection, func_id);\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        func_id\n    }\n\n    fn write_ray_query_get_vertex_positions(\n        &mut self,\n        is_committed: bool,\n        ir_module: &crate::Module,\n    ) -> spirv::Word {\n        if let Some(&word) =\n            self.ray_query_functions\n                .get(&LookupRayQueryFunction::GetVertexPositions {\n                    committed: is_committed,\n                })\n        {\n            return word;\n        }\n\n        let (committed_ty, committed_tri_ty) = if is_committed {\n            (\n                spirv::RayQueryIntersection::RayQueryCommittedIntersectionKHR as u32,\n                spirv::RayQueryCommittedIntersectionType::RayQueryCommittedIntersectionTriangleKHR\n                    as u32,\n            )\n        } else {\n            (\n                spirv::RayQueryIntersection::RayQueryCandidateIntersectionKHR as u32,\n                spirv::RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionTriangleKHR\n                    as u32,\n            )\n        };\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let rq_get_vertex_positions_ty_id = self.get_handle_type_id(\n            *ir_module\n                .special_types\n                .ray_vertex_return\n                .as_ref()\n                .expect(\"must be generated when reading in get vertex position\"),\n        );\n        let ptr_return_ty =\n            self.get_pointer_type_id(rq_get_vertex_positions_ty_id, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n\n        let (func_id, mut function, arg_ids) = self.write_function_signature(\n            &[ray_query_type_id, u32_ptr_ty],\n            rq_get_vertex_positions_ty_id,\n        );\n\n        let query_id = arg_ids[0];\n        let init_tracker_id = arg_ids[1];\n\n        let block_id = self.id_gen.next();\n        let mut block = Block::new(block_id);\n\n        let return_id = self.id_gen.next();\n        block.body.push(Instruction::variable(\n            ptr_return_ty,\n            return_id,\n            spirv::StorageClass::Function,\n            Some(self.get_constant_null(rq_get_vertex_positions_ty_id)),\n        ));\n\n        let valid_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_id);\n\n        let final_label_id = self.id_gen.next();\n        let mut final_block = Block::new(final_label_id);\n\n        let instruction = if self.ray_query_initialization_tracking {\n            let initialized_tracker_id = self.id_gen.next();\n            block.body.push(Instruction::load(\n                u32_ty,\n                initialized_tracker_id,\n                init_tracker_id,\n                None,\n            ));\n\n            let proceeded_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::PROCEED.bits(),\n            );\n            let finished_proceed_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            );\n\n            let correct_finish_id = if is_committed {\n                finished_proceed_id\n            } else {\n                let not_finished_id = self.id_gen.next();\n                block.body.push(Instruction::unary(\n                    spirv::Op::LogicalNot,\n                    bool_type_id,\n                    not_finished_id,\n                    finished_proceed_id,\n                ));\n                not_finished_id\n            };\n\n            let is_valid_id = self.write_logical_and(&mut block, correct_finish_id, proceeded_id);\n            block.body.push(Instruction::selection_merge(\n                final_label_id,\n                spirv::SelectionControl::NONE,\n            ));\n            Instruction::branch_conditional(is_valid_id, valid_id, final_label_id)\n        } else {\n            Instruction::branch(valid_id)\n        };\n\n        function.consume(block, instruction);\n\n        let intersection_id = self.get_constant_scalar(crate::Literal::U32(committed_ty));\n        let raw_kind_id = self.id_gen.next();\n        valid_block\n            .body\n            .push(Instruction::ray_query_get_intersection(\n                spirv::Op::RayQueryGetIntersectionTypeKHR,\n                u32_ty,\n                raw_kind_id,\n                query_id,\n                intersection_id,\n            ));\n\n        let candidate_tri_id = self.get_constant_scalar(crate::Literal::U32(committed_tri_ty));\n        let intersection_tri_id = self.id_gen.next();\n        valid_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            intersection_tri_id,\n            raw_kind_id,\n            candidate_tri_id,\n        ));\n\n        let generate_label_id = self.id_gen.next();\n        let mut vertex_return_block = Block::new(generate_label_id);\n\n        let merge_label_id = self.id_gen.next();\n        let merge_block = Block::new(merge_label_id);\n\n        valid_block.body.push(Instruction::selection_merge(\n            merge_label_id,\n            spirv::SelectionControl::NONE,\n        ));\n        function.consume(\n            valid_block,\n            Instruction::branch_conditional(intersection_tri_id, generate_label_id, merge_label_id),\n        );\n\n        let vertices_id = self.id_gen.next();\n        vertex_return_block\n            .body\n            .push(Instruction::ray_query_return_vertex_position(\n                rq_get_vertex_positions_ty_id,\n                vertices_id,\n                query_id,\n                intersection_id,\n            ));\n        vertex_return_block\n            .body\n            .push(Instruction::store(return_id, vertices_id, None));\n\n        function.consume(vertex_return_block, Instruction::branch(merge_label_id));\n        function.consume(merge_block, Instruction::branch(final_label_id));\n\n        let loaded_pos_id = self.id_gen.next();\n        final_block.body.push(Instruction::load(\n            rq_get_vertex_positions_ty_id,\n            loaded_pos_id,\n            return_id,\n            None,\n        ));\n\n        function.consume(final_block, Instruction::return_value(loaded_pos_id));\n\n        self.ray_query_functions.insert(\n            LookupRayQueryFunction::GetVertexPositions {\n                committed: is_committed,\n            },\n            func_id,\n        );\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        func_id\n    }\n\n    fn write_ray_query_terminate(&mut self) -> spirv::Word {\n        if let Some(&word) = self\n            .ray_query_functions\n            .get(&LookupRayQueryFunction::Terminate)\n        {\n            return word;\n        }\n\n        let ray_query_type_id = self.get_ray_query_pointer_id();\n\n        let u32_ty = self.get_u32_type_id();\n        let u32_ptr_ty = self.get_pointer_type_id(u32_ty, spirv::StorageClass::Function);\n\n        let bool_type_id = self.get_bool_type_id();\n\n        let (func_id, mut function, arg_ids) =\n            self.write_function_signature(&[ray_query_type_id, u32_ptr_ty], self.void_type);\n\n        let query_id = arg_ids[0];\n        let init_tracker_id = arg_ids[1];\n\n        let block_id = self.id_gen.next();\n        let mut block = Block::new(block_id);\n\n        let initialized_tracker_id = self.id_gen.next();\n        block.body.push(Instruction::load(\n            u32_ty,\n            initialized_tracker_id,\n            init_tracker_id,\n            None,\n        ));\n\n        let merge_id = self.id_gen.next();\n        let merge_block = Block::new(merge_id);\n\n        let valid_block_id = self.id_gen.next();\n        let mut valid_block = Block::new(valid_block_id);\n\n        let instruction = if self.ray_query_initialization_tracking {\n            let has_proceeded = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::PROCEED.bits(),\n            );\n\n            let finished_proceed_id = write_ray_flags_contains_flags(\n                self,\n                &mut block,\n                initialized_tracker_id,\n                RayQueryPoint::FINISHED_TRAVERSAL.bits(),\n            );\n\n            let not_finished_id = self.id_gen.next();\n            block.body.push(Instruction::unary(\n                spirv::Op::LogicalNot,\n                bool_type_id,\n                not_finished_id,\n                finished_proceed_id,\n            ));\n\n            let valid_call = self.write_logical_and(&mut block, not_finished_id, has_proceeded);\n\n            block.body.push(Instruction::selection_merge(\n                merge_id,\n                spirv::SelectionControl::NONE,\n            ));\n\n            Instruction::branch_conditional(valid_call, valid_block_id, merge_id)\n        } else {\n            Instruction::branch(valid_block_id)\n        };\n\n        function.consume(block, instruction);\n\n        valid_block\n            .body\n            .push(Instruction::ray_query_terminate(query_id));\n\n        function.consume(valid_block, Instruction::branch(merge_id));\n\n        function.consume(merge_block, Instruction::return_void());\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        self.ray_query_functions\n            .insert(LookupRayQueryFunction::Proceed, func_id);\n        func_id\n    }\n}\n\nimpl BlockContext<'_> {\n    pub(in super::super) fn write_ray_query_function(\n        &mut self,\n        query: Handle<crate::Expression>,\n        function: &crate::RayQueryFunction,\n        block: &mut Block,\n    ) {\n        let query_id = self.cached[query];\n        let tracker_ids = *self\n            .ray_query_tracker_expr\n            .get(&query)\n            .expect(\"not a cached ray query\");\n\n        match *function {\n            crate::RayQueryFunction::Initialize {\n                acceleration_structure,\n                descriptor,\n            } => {\n                let desc_id = self.cached[descriptor];\n                let acc_struct_id = self.get_handle_id(acceleration_structure);\n\n                let func = self.writer.write_ray_query_initialize(self.ir_module);\n\n                let func_id = self.gen_id();\n                block.body.push(Instruction::function_call(\n                    self.writer.void_type,\n                    func_id,\n                    func,\n                    &[\n                        query_id,\n                        acc_struct_id,\n                        desc_id,\n                        tracker_ids.initialized_tracker,\n                        tracker_ids.t_max_tracker,\n                    ],\n                ));\n            }\n            crate::RayQueryFunction::Proceed { result } => {\n                let id = self.gen_id();\n                self.cached[result] = id;\n\n                let bool_ty = self.writer.get_bool_type_id();\n\n                let func_id = self.writer.write_ray_query_proceed();\n                block.body.push(Instruction::function_call(\n                    bool_ty,\n                    id,\n                    func_id,\n                    &[query_id, tracker_ids.initialized_tracker],\n                ));\n            }\n            crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                let hit_id = self.cached[hit_t];\n\n                let func_id = self.writer.write_ray_query_generate_intersection();\n\n                let func_call_id = self.gen_id();\n                block.body.push(Instruction::function_call(\n                    self.writer.void_type,\n                    func_call_id,\n                    func_id,\n                    &[\n                        query_id,\n                        tracker_ids.initialized_tracker,\n                        hit_id,\n                        tracker_ids.t_max_tracker,\n                    ],\n                ));\n            }\n            crate::RayQueryFunction::ConfirmIntersection => {\n                let func_id = self.writer.write_ray_query_confirm_intersection();\n\n                let func_call_id = self.gen_id();\n                block.body.push(Instruction::function_call(\n                    self.writer.void_type,\n                    func_call_id,\n                    func_id,\n                    &[query_id, tracker_ids.initialized_tracker],\n                ));\n            }\n            crate::RayQueryFunction::Terminate => {\n                let id = self.gen_id();\n\n                let func_id = self.writer.write_ray_query_terminate();\n                block.body.push(Instruction::function_call(\n                    self.writer.void_type,\n                    id,\n                    func_id,\n                    &[query_id, tracker_ids.initialized_tracker],\n                ));\n            }\n        }\n    }\n\n    pub(in super::super) fn write_ray_query_return_vertex_position(\n        &mut self,\n        query: Handle<crate::Expression>,\n        block: &mut Block,\n        is_committed: bool,\n    ) -> spirv::Word {\n        let fn_id = self\n            .writer\n            .write_ray_query_get_vertex_positions(is_committed, self.ir_module);\n\n        let query_id = self.cached[query];\n        let tracker_id = *self\n            .ray_query_tracker_expr\n            .get(&query)\n            .expect(\"not a cached ray query\");\n\n        let rq_get_vertex_positions_ty_id = self.get_handle_type_id(\n            *self\n                .ir_module\n                .special_types\n                .ray_vertex_return\n                .as_ref()\n                .expect(\"must be generated when reading in get vertex position\"),\n        );\n\n        let func_call_id = self.gen_id();\n        block.body.push(Instruction::function_call(\n            rq_get_vertex_positions_ty_id,\n            func_call_id,\n            fn_id,\n            &[query_id, tracker_id.initialized_tracker],\n        ));\n        func_call_id\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/reclaimable.rs",
    "content": "/*!\nReusing collections' previous allocations.\n*/\n\nuse alloc::vec::Vec;\n\n/// A value that can be reset to its initial state, retaining its current allocations.\n///\n/// Naga attempts to lower the cost of SPIR-V generation by allowing clients to\n/// reuse the same `Writer` for multiple Module translations. Reusing a `Writer`\n/// means that the `Vec`s, `HashMap`s, and other heap-allocated structures the\n/// `Writer` uses internally begin the translation with heap-allocated buffers\n/// ready to use.\n///\n/// But this approach introduces the risk of `Writer` state leaking from one\n/// module to the next. When a developer adds fields to `Writer` or its internal\n/// types, they must remember to reset their contents between modules.\n///\n/// One trick to ensure that every field has been accounted for is to use Rust's\n/// struct literal syntax to construct a new, reset value. If a developer adds a\n/// field, but neglects to update the reset code, the compiler will complain\n/// that a field is missing from the literal. This trait's `recycle` method\n/// takes `self` by value, and returns `Self` by value, encouraging the use of\n/// struct literal expressions in its implementation.\npub trait Reclaimable {\n    /// Clear `self`, retaining its current memory allocations.\n    ///\n    /// Shrink the buffer if it's currently much larger than was actually used.\n    /// This prevents a module with exceptionally large allocations from causing\n    /// the `Writer` to retain more memory than it needs indefinitely.\n    fn reclaim(self) -> Self;\n}\n\n// Stock values for various collections.\n\nimpl<T> Reclaimable for Vec<T> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n\nimpl<K, V, S: Clone> Reclaimable for hashbrown::HashMap<K, V, S> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n\nimpl<K, S: Clone> Reclaimable for hashbrown::HashSet<K, S> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n\nimpl<K, S: Clone> Reclaimable for indexmap::IndexSet<K, S> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n\nimpl<K: Ord, V> Reclaimable for alloc::collections::BTreeMap<K, V> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n\nimpl<K, V> Reclaimable for crate::arena::HandleVec<K, V> {\n    fn reclaim(mut self) -> Self {\n        self.clear();\n        self\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/selection.rs",
    "content": "/*!\nGenerate SPIR-V conditional structures.\n\nBuilders for `if` structures with `and`s.\n\nThe types in this module track the information needed to emit SPIR-V code\nfor complex conditional structures, like those whose conditions involve\nshort-circuiting 'and' and 'or' structures. These track labels and can emit\n`OpPhi` instructions to merge values produced along different paths.\n\nThis currently only supports exactly the forms Naga uses, so it doesn't\nsupport `or` or `else`, and only supports zero or one merged values.\n\nNaga needs to emit code roughly like this:\n\n```ignore\n\n    value = DEFAULT;\n    if COND1 && COND2 {\n        value = THEN_VALUE;\n    }\n    // use value\n\n```\n\nAssuming `ctx` and `block` are a mutable references to a [`BlockContext`]\nand the current [`Block`], and `merge_type` is the SPIR-V type for the\nmerged value `value`, we can build SPIR-V for the code above like so:\n\n```ignore\n\n    let cond = Selection::start(block, merge_type);\n        // ... compute `cond1` ...\n    cond.if_true(ctx, cond1, DEFAULT);\n        // ... compute `cond2` ...\n    cond.if_true(ctx, cond2, DEFAULT);\n        // ... compute THEN_VALUE\n    let merged_value = cond.finish(ctx, THEN_VALUE);\n\n```\n\nAfter this, `merged_value` is either `DEFAULT` or `THEN_VALUE`, depending on\nthe path by which the merged block was reached.\n\nThis takes care of writing all branch instructions, including an\n`OpSelectionMerge` annotation in the header block; starting new blocks and\nassigning them labels; and emitting the `OpPhi` that gathers together the\nright sources for the merged values, for every path through the selection\nconstruct.\n\nWhen there is no merged value to produce, you can pass `()` for `merge_type`\nand the merge values. In this case no `OpPhi` instructions are produced, and\nthe `finish` method returns `()`.\n\nTo enforce proper nesting, a `Selection` takes ownership of the `&mut Block`\npointer for the duration of its lifetime. To obtain the block for generating\ncode in the selection's body, call the `Selection::block` method.\n*/\n\nuse alloc::{vec, vec::Vec};\n\nuse spirv::Word;\n\nuse super::{Block, BlockContext, Instruction};\n\n/// A private struct recording what we know about the selection construct so far.\npub(super) struct Selection<'b, M: MergeTuple> {\n    /// The block pointer we're emitting code into.\n    block: &'b mut Block,\n\n    /// The label of the selection construct's merge block, or `None` if we\n    /// haven't yet written the `OpSelectionMerge` merge instruction.\n    merge_label: Option<Word>,\n\n    /// A set of `(VALUES, PARENT)` pairs, used to build `OpPhi` instructions in\n    /// the merge block. Each `PARENT` is the label of a predecessor block of\n    /// the merge block. The corresponding `VALUES` holds the ids of the values\n    /// that `PARENT` contributes to the merged values.\n    ///\n    /// We emit all branches to the merge block, so we know all its\n    /// predecessors. And we refuse to emit a branch unless we're given the\n    /// values the branching block contributes to the merge, so we always have\n    /// everything we need to emit the correct phis, by construction.\n    values: Vec<(M, Word)>,\n\n    /// The types of the values in each element of `values`.\n    merge_types: M,\n}\n\nimpl<'b, M: MergeTuple> Selection<'b, M> {\n    /// Start a new selection construct.\n    ///\n    /// The `block` argument indicates the selection's header block.\n    ///\n    /// The `merge_types` argument should be a `Word` or tuple of `Word`s, each\n    /// value being the SPIR-V result type id of an `OpPhi` instruction that\n    /// will be written to the selection's merge block when this selection's\n    /// [`finish`] method is called. This argument may also be `()`, for\n    /// selections that produce no values.\n    ///\n    /// (This function writes no code to `block` itself; it simply constructs a\n    /// fresh `Selection`.)\n    ///\n    /// [`finish`]: Selection::finish\n    pub(super) const fn start(block: &'b mut Block, merge_types: M) -> Self {\n        Selection {\n            block,\n            merge_label: None,\n            values: vec![],\n            merge_types,\n        }\n    }\n\n    pub(super) const fn block(&mut self) -> &mut Block {\n        self.block\n    }\n\n    /// Branch to a successor block if `cond` is true, otherwise merge.\n    ///\n    /// If `cond` is false, branch to the merge block, using `values` as the\n    /// merged values. Otherwise, proceed to a new block.\n    ///\n    /// The `values` argument must be the same shape as the `merge_types`\n    /// argument passed to `Selection::start`.\n    pub(super) fn if_true(&mut self, ctx: &mut BlockContext, cond: Word, values: M) {\n        self.values.push((values, self.block.label_id));\n\n        let merge_label = self.make_merge_label(ctx);\n        let next_label = ctx.gen_id();\n        ctx.function.consume(\n            core::mem::replace(self.block, Block::new(next_label)),\n            Instruction::branch_conditional(cond, next_label, merge_label),\n        );\n    }\n\n    /// Emit an unconditional branch to the merge block, and compute merged\n    /// values.\n    ///\n    /// Use `final_values` as the merged values contributed by the current\n    /// block, and transition to the merge block, emitting `OpPhi` instructions\n    /// to produce the merged values. This must be the same shape as the\n    /// `merge_types` argument passed to [`Selection::start`].\n    ///\n    /// Return the SPIR-V ids of the merged values. This value has the same\n    /// shape as the `merge_types` argument passed to `Selection::start`.\n    pub(super) fn finish(self, ctx: &mut BlockContext, final_values: M) -> M {\n        match self {\n            Selection {\n                merge_label: None, ..\n            } => {\n                // We didn't actually emit any branches, so `self.values` must\n                // be empty, and `final_values` are the only sources we have for\n                // the merged values. Easy peasy.\n                final_values\n            }\n\n            Selection {\n                block,\n                merge_label: Some(merge_label),\n                mut values,\n                merge_types,\n            } => {\n                // Emit the final branch and transition to the merge block.\n                values.push((final_values, block.label_id));\n                ctx.function.consume(\n                    core::mem::replace(block, Block::new(merge_label)),\n                    Instruction::branch(merge_label),\n                );\n\n                // Now that we're in the merge block, build the phi instructions.\n                merge_types.write_phis(ctx, block, &values)\n            }\n        }\n    }\n\n    /// Return the id of the merge block, writing a merge instruction if needed.\n    fn make_merge_label(&mut self, ctx: &mut BlockContext) -> Word {\n        match self.merge_label {\n            None => {\n                let merge_label = ctx.gen_id();\n                self.block.body.push(Instruction::selection_merge(\n                    merge_label,\n                    spirv::SelectionControl::NONE,\n                ));\n                self.merge_label = Some(merge_label);\n                merge_label\n            }\n            Some(merge_label) => merge_label,\n        }\n    }\n}\n\n/// A trait to help `Selection` manage any number of merged values.\n///\n/// Some selection constructs, like a `ReadZeroSkipWrite` bounds check on a\n/// [`Load`] expression, produce a single merged value. Others produce no merged\n/// value, like a bounds check on a [`Store`] statement.\n///\n/// To let `Selection` work nicely with both cases, we let the merge type\n/// argument passed to [`Selection::start`] be any type that implements this\n/// `MergeTuple` trait. `MergeTuple` is then implemented for `()`, `Word`,\n/// `(Word, Word)`, and so on.\n///\n/// A `MergeTuple` type can represent either a bunch of SPIR-V types or values;\n/// the `merge_types` argument to `Selection::start` are type ids, whereas the\n/// `values` arguments to the [`if_true`] and [`finish`] methods are value ids.\n/// The set of merged value returned by `finish` is a tuple of value ids.\n///\n/// In fact, since Naga only uses zero- and single-valued selection constructs\n/// at present, we only implement `MergeTuple` for `()` and `Word`. But if you\n/// add more cases, feel free to add more implementations. Once const generics\n/// are available, we could have a single implementation of `MergeTuple` for all\n/// lengths of arrays, and be done with it.\n///\n/// [`Load`]: crate::Expression::Load\n/// [`Store`]: crate::Statement::Store\n/// [`if_true`]: Selection::if_true\n/// [`finish`]: Selection::finish\npub(super) trait MergeTuple: Sized {\n    /// Write OpPhi instructions for the given set of predecessors.\n    ///\n    /// The `predecessors` vector should be a vector of `(LABEL, VALUES)` pairs,\n    /// where each `VALUES` holds the values contributed by the branch from\n    /// `LABEL`, which should be one of the current block's predecessors.\n    fn write_phis(\n        self,\n        ctx: &mut BlockContext,\n        block: &mut Block,\n        predecessors: &[(Self, Word)],\n    ) -> Self;\n}\n\n/// Selections that produce a single merged value.\n///\n/// For example, `ImageLoad` with `BoundsCheckPolicy::ReadZeroSkipWrite` either\n/// returns a texel value or zeros.\nimpl MergeTuple for Word {\n    fn write_phis(\n        self,\n        ctx: &mut BlockContext,\n        block: &mut Block,\n        predecessors: &[(Word, Word)],\n    ) -> Word {\n        let merged_value = ctx.gen_id();\n        block\n            .body\n            .push(Instruction::phi(self, merged_value, predecessors));\n        merged_value\n    }\n}\n\n/// Selections that produce no merged values.\n///\n/// For example, `ImageStore` under `BoundsCheckPolicy::ReadZeroSkipWrite`\n/// either does the store or skips it, but in neither case does it produce a\n/// value.\nimpl MergeTuple for () {\n    /// No phis need to be generated.\n    fn write_phis(self, _: &mut BlockContext, _: &mut Block, _: &[((), Word)]) {}\n}\n"
  },
  {
    "path": "naga/src/back/spv/subgroup.rs",
    "content": "use super::{Block, BlockContext, Error, Instruction, NumericType};\nuse crate::{arena::Handle, TypeInner};\n\nimpl BlockContext<'_> {\n    pub(super) fn write_subgroup_ballot(\n        &mut self,\n        predicate: &Option<Handle<crate::Expression>>,\n        result: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        self.writer.require_any(\n            \"GroupNonUniformBallot\",\n            &[spirv::Capability::GroupNonUniformBallot],\n        )?;\n        let vec4_u32_type_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Quad,\n            scalar: crate::Scalar::U32,\n        });\n        let exec_scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);\n        let predicate = if let Some(predicate) = *predicate {\n            self.cached[predicate]\n        } else {\n            self.writer.get_constant_scalar(crate::Literal::Bool(true))\n        };\n        let id = self.gen_id();\n        block.body.push(Instruction::group_non_uniform_ballot(\n            vec4_u32_type_id,\n            id,\n            exec_scope_id,\n            predicate,\n        ));\n        self.cached[result] = id;\n        Ok(())\n    }\n    pub(super) fn write_subgroup_operation(\n        &mut self,\n        op: &crate::SubgroupOperation,\n        collective_op: &crate::CollectiveOperation,\n        argument: Handle<crate::Expression>,\n        result: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        use crate::SubgroupOperation as sg;\n        match *op {\n            sg::All | sg::Any => {\n                self.writer.require_any(\n                    \"GroupNonUniformVote\",\n                    &[spirv::Capability::GroupNonUniformVote],\n                )?;\n            }\n            _ => {\n                self.writer.require_any(\n                    \"GroupNonUniformArithmetic\",\n                    &[spirv::Capability::GroupNonUniformArithmetic],\n                )?;\n            }\n        }\n\n        let id = self.gen_id();\n        let result_ty = &self.fun_info[result].ty;\n        let result_type_id = self.get_expression_type_id(result_ty);\n        let result_ty_inner = result_ty.inner_with(&self.ir_module.types);\n\n        let (is_scalar, scalar) = match *result_ty_inner {\n            TypeInner::Scalar(kind) => (true, kind),\n            TypeInner::Vector { scalar: kind, .. } => (false, kind),\n            _ => unimplemented!(),\n        };\n\n        use crate::ScalarKind as sk;\n        let spirv_op = match (scalar.kind, *op) {\n            (sk::Bool, sg::All) if is_scalar => spirv::Op::GroupNonUniformAll,\n            (sk::Bool, sg::Any) if is_scalar => spirv::Op::GroupNonUniformAny,\n            (_, sg::All | sg::Any) => unimplemented!(),\n\n            (sk::Sint | sk::Uint, sg::Add) => spirv::Op::GroupNonUniformIAdd,\n            (sk::Float, sg::Add) => spirv::Op::GroupNonUniformFAdd,\n            (sk::Sint | sk::Uint, sg::Mul) => spirv::Op::GroupNonUniformIMul,\n            (sk::Float, sg::Mul) => spirv::Op::GroupNonUniformFMul,\n            (sk::Sint, sg::Max) => spirv::Op::GroupNonUniformSMax,\n            (sk::Uint, sg::Max) => spirv::Op::GroupNonUniformUMax,\n            (sk::Float, sg::Max) => spirv::Op::GroupNonUniformFMax,\n            (sk::Sint, sg::Min) => spirv::Op::GroupNonUniformSMin,\n            (sk::Uint, sg::Min) => spirv::Op::GroupNonUniformUMin,\n            (sk::Float, sg::Min) => spirv::Op::GroupNonUniformFMin,\n            (_, sg::Add | sg::Mul | sg::Min | sg::Max) => unimplemented!(),\n\n            (sk::Sint | sk::Uint, sg::And) => spirv::Op::GroupNonUniformBitwiseAnd,\n            (sk::Sint | sk::Uint, sg::Or) => spirv::Op::GroupNonUniformBitwiseOr,\n            (sk::Sint | sk::Uint, sg::Xor) => spirv::Op::GroupNonUniformBitwiseXor,\n            (sk::Bool, sg::And) => spirv::Op::GroupNonUniformLogicalAnd,\n            (sk::Bool, sg::Or) => spirv::Op::GroupNonUniformLogicalOr,\n            (sk::Bool, sg::Xor) => spirv::Op::GroupNonUniformLogicalXor,\n            (_, sg::And | sg::Or | sg::Xor) => unimplemented!(),\n        };\n\n        let exec_scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);\n\n        use crate::CollectiveOperation as c;\n        let group_op = match *op {\n            sg::All | sg::Any => None,\n            _ => Some(match *collective_op {\n                c::Reduce => spirv::GroupOperation::Reduce,\n                c::InclusiveScan => spirv::GroupOperation::InclusiveScan,\n                c::ExclusiveScan => spirv::GroupOperation::ExclusiveScan,\n            }),\n        };\n\n        let arg_id = self.cached[argument];\n        block.body.push(Instruction::group_non_uniform_arithmetic(\n            spirv_op,\n            result_type_id,\n            id,\n            exec_scope_id,\n            group_op,\n            arg_id,\n        ));\n        self.cached[result] = id;\n        Ok(())\n    }\n    pub(super) fn write_subgroup_gather(\n        &mut self,\n        mode: &crate::GatherMode,\n        argument: Handle<crate::Expression>,\n        result: Handle<crate::Expression>,\n        block: &mut Block,\n    ) -> Result<(), Error> {\n        match *mode {\n            crate::GatherMode::BroadcastFirst => {\n                self.writer.require_any(\n                    \"GroupNonUniformBallot\",\n                    &[spirv::Capability::GroupNonUniformBallot],\n                )?;\n            }\n            crate::GatherMode::Shuffle(_)\n            | crate::GatherMode::ShuffleXor(_)\n            | crate::GatherMode::Broadcast(_) => {\n                self.writer.require_any(\n                    \"GroupNonUniformShuffle\",\n                    &[spirv::Capability::GroupNonUniformShuffle],\n                )?;\n            }\n            crate::GatherMode::ShuffleDown(_) | crate::GatherMode::ShuffleUp(_) => {\n                self.writer.require_any(\n                    \"GroupNonUniformShuffleRelative\",\n                    &[spirv::Capability::GroupNonUniformShuffleRelative],\n                )?;\n            }\n            crate::GatherMode::QuadBroadcast(_) | crate::GatherMode::QuadSwap(_) => {\n                self.writer.require_any(\n                    \"GroupNonUniformQuad\",\n                    &[spirv::Capability::GroupNonUniformQuad],\n                )?;\n            }\n        }\n\n        let id = self.gen_id();\n        let result_ty = &self.fun_info[result].ty;\n        let result_type_id = self.get_expression_type_id(result_ty);\n\n        let exec_scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);\n\n        let arg_id = self.cached[argument];\n        match *mode {\n            crate::GatherMode::BroadcastFirst => {\n                block\n                    .body\n                    .push(Instruction::group_non_uniform_broadcast_first(\n                        result_type_id,\n                        id,\n                        exec_scope_id,\n                        arg_id,\n                    ));\n            }\n            crate::GatherMode::Broadcast(index)\n            | crate::GatherMode::Shuffle(index)\n            | crate::GatherMode::ShuffleDown(index)\n            | crate::GatherMode::ShuffleUp(index)\n            | crate::GatherMode::ShuffleXor(index)\n            | crate::GatherMode::QuadBroadcast(index) => {\n                let index_id = self.cached[index];\n                let op = match *mode {\n                    crate::GatherMode::BroadcastFirst => unreachable!(),\n                    // Use shuffle to emit broadcast to allow the index to\n                    // be dynamically uniform on Vulkan 1.1. The argument to\n                    // OpGroupNonUniformBroadcast must be a constant pre SPIR-V\n                    // 1.5 (vulkan 1.2)\n                    crate::GatherMode::Broadcast(_) => spirv::Op::GroupNonUniformShuffle,\n                    crate::GatherMode::Shuffle(_) => spirv::Op::GroupNonUniformShuffle,\n                    crate::GatherMode::ShuffleDown(_) => spirv::Op::GroupNonUniformShuffleDown,\n                    crate::GatherMode::ShuffleUp(_) => spirv::Op::GroupNonUniformShuffleUp,\n                    crate::GatherMode::ShuffleXor(_) => spirv::Op::GroupNonUniformShuffleXor,\n                    crate::GatherMode::QuadBroadcast(_) => spirv::Op::GroupNonUniformQuadBroadcast,\n                    crate::GatherMode::QuadSwap(_) => unreachable!(),\n                };\n                block.body.push(Instruction::group_non_uniform_gather(\n                    op,\n                    result_type_id,\n                    id,\n                    exec_scope_id,\n                    arg_id,\n                    index_id,\n                ));\n            }\n            crate::GatherMode::QuadSwap(direction) => {\n                let direction = self.get_index_constant(match direction {\n                    crate::Direction::X => 0,\n                    crate::Direction::Y => 1,\n                    crate::Direction::Diagonal => 2,\n                });\n                block.body.push(Instruction::group_non_uniform_quad_swap(\n                    result_type_id,\n                    id,\n                    exec_scope_id,\n                    arg_id,\n                    direction,\n                ));\n            }\n        }\n        self.cached[result] = id;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/back/spv/writer.rs",
    "content": "use alloc::{format, string::String, vec, vec::Vec};\n\nuse arrayvec::ArrayVec;\nuse hashbrown::hash_map::Entry;\nuse spirv::Word;\n\nuse super::{\n    block::DebugInfoInner,\n    helpers::{contains_builtin, global_needs_wrapper, map_storage_class},\n    Block, BlockContext, CachedConstant, CachedExpressions, CooperativeType, DebugInfo,\n    EntryPointContext, Error, Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction,\n    LocalImageType, LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType,\n    NumericType, Options, PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags,\n    BITS_PER_BYTE,\n};\nuse crate::{\n    arena::{Handle, HandleVec, UniqueArena},\n    back::spv::{\n        helpers::{is_uniform_matcx2_struct_member_access, BindingDecorations},\n        BindingInfo, Std140CompatTypeInfo, WrappedFunction,\n    },\n    common::ForDebugWithTypes as _,\n    proc::{Alignment, TypeResolution},\n    valid::{FunctionInfo, ModuleInfo},\n};\n\npub struct FunctionInterface<'a> {\n    pub varying_ids: &'a mut Vec<Word>,\n    pub stage: crate::ShaderStage,\n    pub task_payload: Option<Handle<crate::GlobalVariable>>,\n    pub mesh_info: Option<crate::MeshStageInfo>,\n    pub workgroup_size: [u32; 3],\n}\n\nimpl Function {\n    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {\n        self.signature.as_ref().unwrap().to_words(sink);\n        for argument in self.parameters.iter() {\n            argument.instruction.to_words(sink);\n        }\n        for (index, block) in self.blocks.iter().enumerate() {\n            Instruction::label(block.label_id).to_words(sink);\n            if index == 0 {\n                for local_var in self.variables.values() {\n                    local_var.instruction.to_words(sink);\n                }\n                for local_var in self.ray_query_initialization_tracker_variables.values() {\n                    local_var.instruction.to_words(sink);\n                }\n                for local_var in self.ray_query_t_max_tracker_variables.values() {\n                    local_var.instruction.to_words(sink);\n                }\n                for local_var in self.force_loop_bounding_vars.iter() {\n                    local_var.instruction.to_words(sink);\n                }\n                for internal_var in self.spilled_composites.values() {\n                    internal_var.instruction.to_words(sink);\n                }\n            }\n            for instruction in block.body.iter() {\n                instruction.to_words(sink);\n            }\n        }\n        Instruction::function_end().to_words(sink);\n    }\n}\n\nimpl Writer {\n    pub fn new(options: &Options) -> Result<Self, Error> {\n        let (major, minor) = options.lang_version;\n        if major != 1 {\n            return Err(Error::UnsupportedVersion(major, minor));\n        }\n\n        let mut capabilities_used = crate::FastIndexSet::default();\n        capabilities_used.insert(spirv::Capability::Shader);\n\n        let mut id_gen = IdGenerator::default();\n        let gl450_ext_inst_id = id_gen.next();\n        let void_type = id_gen.next();\n\n        Ok(Writer {\n            physical_layout: PhysicalLayout::new(major, minor),\n            logical_layout: LogicalLayout::default(),\n            id_gen,\n            capabilities_available: options.capabilities.clone(),\n            capabilities_used,\n            extensions_used: crate::FastIndexSet::default(),\n            debug_strings: vec![],\n            debugs: vec![],\n            annotations: vec![],\n            flags: options.flags,\n            bounds_check_policies: options.bounds_check_policies,\n            zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,\n            force_loop_bounding: options.force_loop_bounding,\n            ray_query_initialization_tracking: options.ray_query_initialization_tracking,\n            use_storage_input_output_16: options.use_storage_input_output_16,\n            void_type,\n            tuple_of_u32s_ty_id: None,\n            lookup_type: crate::FastHashMap::default(),\n            lookup_function: crate::FastHashMap::default(),\n            lookup_function_type: crate::FastHashMap::default(),\n            wrapped_functions: crate::FastHashMap::default(),\n            constant_ids: HandleVec::new(),\n            cached_constants: crate::FastHashMap::default(),\n            global_variables: HandleVec::new(),\n            std140_compat_uniform_types: crate::FastHashMap::default(),\n            fake_missing_bindings: options.fake_missing_bindings,\n            binding_map: options.binding_map.clone(),\n            saved_cached: CachedExpressions::default(),\n            gl450_ext_inst_id,\n            temp_list: Vec::new(),\n            ray_query_functions: crate::FastHashMap::default(),\n            io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new(\n                options.use_storage_input_output_16,\n            ),\n            debug_printf: None,\n            task_dispatch_limits: options.task_dispatch_limits,\n            mesh_shader_primitive_indices_clamp: options.mesh_shader_primitive_indices_clamp,\n        })\n    }\n\n    pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {\n        let (major, minor) = options.lang_version;\n        if major != 1 {\n            return Err(Error::UnsupportedVersion(major, minor));\n        }\n        self.physical_layout = PhysicalLayout::new(major, minor);\n        self.capabilities_available = options.capabilities.clone();\n        self.flags = options.flags;\n        self.bounds_check_policies = options.bounds_check_policies;\n        self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;\n        self.force_loop_bounding = options.force_loop_bounding;\n        self.use_storage_input_output_16 = options.use_storage_input_output_16;\n        self.binding_map = options.binding_map.clone();\n        self.io_f16_polyfills =\n            super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);\n        self.task_dispatch_limits = options.task_dispatch_limits;\n        self.mesh_shader_primitive_indices_clamp = options.mesh_shader_primitive_indices_clamp;\n        Ok(())\n    }\n\n    /// Returns `(major, minor)` of the SPIR-V language version.\n    pub const fn lang_version(&self) -> (u8, u8) {\n        self.physical_layout.lang_version()\n    }\n\n    /// Reset `Writer` to its initial state, retaining any allocations.\n    ///\n    /// Why not just implement `Reclaimable` for `Writer`? By design,\n    /// `Reclaimable::reclaim` requires ownership of the value, not just\n    /// `&mut`; see the trait documentation. But we need to use this method\n    /// from functions like `Writer::write`, which only have `&mut Writer`.\n    /// Workarounds include unsafe code (`core::ptr::read`, then `write`, ugh)\n    /// or something like a `Default` impl that returns an oddly-initialized\n    /// `Writer`, which is worse.\n    fn reset(&mut self) {\n        use super::reclaimable::Reclaimable;\n        use core::mem::take;\n\n        let mut id_gen = IdGenerator::default();\n        let gl450_ext_inst_id = id_gen.next();\n        let void_type = id_gen.next();\n\n        // Every field of the old writer that is not determined by the `Options`\n        // passed to `Writer::new` should be reset somehow.\n        let fresh = Writer {\n            // Copied from the old Writer:\n            flags: self.flags,\n            bounds_check_policies: self.bounds_check_policies,\n            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,\n            force_loop_bounding: self.force_loop_bounding,\n            ray_query_initialization_tracking: self.ray_query_initialization_tracking,\n            use_storage_input_output_16: self.use_storage_input_output_16,\n            capabilities_available: take(&mut self.capabilities_available),\n            fake_missing_bindings: self.fake_missing_bindings,\n            binding_map: take(&mut self.binding_map),\n            task_dispatch_limits: self.task_dispatch_limits,\n            mesh_shader_primitive_indices_clamp: self.mesh_shader_primitive_indices_clamp,\n\n            // Initialized afresh:\n            id_gen,\n            void_type,\n            tuple_of_u32s_ty_id: None,\n            gl450_ext_inst_id,\n\n            // Reclaimed:\n            capabilities_used: take(&mut self.capabilities_used).reclaim(),\n            extensions_used: take(&mut self.extensions_used).reclaim(),\n            physical_layout: self.physical_layout.clone().reclaim(),\n            logical_layout: take(&mut self.logical_layout).reclaim(),\n            debug_strings: take(&mut self.debug_strings).reclaim(),\n            debugs: take(&mut self.debugs).reclaim(),\n            annotations: take(&mut self.annotations).reclaim(),\n            lookup_type: take(&mut self.lookup_type).reclaim(),\n            lookup_function: take(&mut self.lookup_function).reclaim(),\n            lookup_function_type: take(&mut self.lookup_function_type).reclaim(),\n            wrapped_functions: take(&mut self.wrapped_functions).reclaim(),\n            constant_ids: take(&mut self.constant_ids).reclaim(),\n            cached_constants: take(&mut self.cached_constants).reclaim(),\n            global_variables: take(&mut self.global_variables).reclaim(),\n            std140_compat_uniform_types: take(&mut self.std140_compat_uniform_types).reclaim(),\n            saved_cached: take(&mut self.saved_cached).reclaim(),\n            temp_list: take(&mut self.temp_list).reclaim(),\n            ray_query_functions: take(&mut self.ray_query_functions).reclaim(),\n            io_f16_polyfills: take(&mut self.io_f16_polyfills).reclaim(),\n            debug_printf: None,\n        };\n\n        *self = fresh;\n\n        self.capabilities_used.insert(spirv::Capability::Shader);\n    }\n\n    /// Indicate that the code requires any one of the listed capabilities.\n    ///\n    /// If nothing in `capabilities` appears in the available capabilities\n    /// specified in the [`Options`] from which this `Writer` was created,\n    /// return an error. The `what` string is used in the error message to\n    /// explain what provoked the requirement. (If no available capabilities were\n    /// given, assume everything is available.)\n    ///\n    /// The first acceptable capability will be added to this `Writer`'s\n    /// [`capabilities_used`] table, and an `OpCapability` emitted for it in the\n    /// result. For this reason, more specific capabilities should be listed\n    /// before more general.\n    ///\n    /// [`capabilities_used`]: Writer::capabilities_used\n    pub(super) fn require_any(\n        &mut self,\n        what: &'static str,\n        capabilities: &[spirv::Capability],\n    ) -> Result<(), Error> {\n        match *capabilities {\n            [] => Ok(()),\n            [first, ..] => {\n                // Find the first acceptable capability, or return an error if\n                // there is none.\n                let selected = match self.capabilities_available {\n                    None => first,\n                    Some(ref available) => {\n                        match capabilities\n                            .iter()\n                            // need explicit type for hashbrown::HashSet::contains fn call to keep rustc happy\n                            .find(|cap| available.contains::<spirv::Capability>(cap))\n                        {\n                            Some(&cap) => cap,\n                            None => {\n                                return Err(Error::MissingCapabilities(what, capabilities.to_vec()))\n                            }\n                        }\n                    }\n                };\n                self.capabilities_used.insert(selected);\n                Ok(())\n            }\n        }\n    }\n\n    /// Indicate that the code requires all of the listed capabilities.\n    ///\n    /// If all entries of `capabilities` appear in the available capabilities\n    /// specified in the [`Options`] from which this `Writer` was created\n    /// (including the case where [`Options::capabilities`] is `None`), add\n    /// them all to this `Writer`'s [`capabilities_used`] table, and return\n    /// `Ok(())`. If at least one of the listed capabilities is not available,\n    /// do not add anything to the `capabilities_used` table, and return the\n    /// first unavailable requested capability, wrapped in `Err()`.\n    ///\n    /// This method is does not return an [`enum@Error`] in case of failure\n    /// because it may be used in cases where the caller can recover (e.g.,\n    /// with a polyfill) if the requested capabilities are not available. In\n    /// this case, it would be unnecessary work to find *all* the unavailable\n    /// requested capabilities, and to allocate a `Vec` for them, just so we\n    /// could return an [`Error::MissingCapabilities`]).\n    ///\n    /// [`capabilities_used`]: Writer::capabilities_used\n    pub(super) fn require_all(\n        &mut self,\n        capabilities: &[spirv::Capability],\n    ) -> Result<(), spirv::Capability> {\n        if let Some(ref available) = self.capabilities_available {\n            for requested in capabilities {\n                if !available.contains(requested) {\n                    return Err(*requested);\n                }\n            }\n        }\n\n        for requested in capabilities {\n            self.capabilities_used.insert(*requested);\n        }\n\n        Ok(())\n    }\n\n    /// Indicate that the code uses the given extension.\n    pub(super) fn use_extension(&mut self, extension: &'static str) {\n        self.extensions_used.insert(extension);\n    }\n\n    pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {\n        match self.lookup_type.entry(lookup_ty) {\n            Entry::Occupied(e) => *e.get(),\n            Entry::Vacant(e) => {\n                let local = match lookup_ty {\n                    LookupType::Handle(_handle) => unreachable!(\"Handles are populated at start\"),\n                    LookupType::Local(local) => local,\n                };\n\n                let id = self.id_gen.next();\n                e.insert(id);\n                self.write_type_declaration_local(id, local);\n                id\n            }\n        }\n    }\n\n    pub(super) fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {\n        self.get_type_id(LookupType::Handle(handle))\n    }\n\n    pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {\n        match *tr {\n            TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),\n            TypeResolution::Value(ref inner) => {\n                let inner_local_type = self.localtype_from_inner(inner).unwrap();\n                LookupType::Local(inner_local_type)\n            }\n        }\n    }\n\n    pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {\n        let lookup_ty = self.get_expression_lookup_type(tr);\n        self.get_type_id(lookup_ty)\n    }\n\n    pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {\n        self.get_type_id(LookupType::Local(local))\n    }\n\n    pub(super) fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {\n        self.get_type_id(LookupType::Local(LocalType::Pointer { base, class }))\n    }\n\n    pub(super) fn get_handle_pointer_type_id(\n        &mut self,\n        base: Handle<crate::Type>,\n        class: spirv::StorageClass,\n    ) -> Word {\n        let base_id = self.get_handle_type_id(base);\n        self.get_pointer_type_id(base_id, class)\n    }\n\n    pub(super) fn get_ray_query_pointer_id(&mut self) -> Word {\n        let rq_id = self.get_type_id(LookupType::Local(LocalType::RayQuery));\n        self.get_pointer_type_id(rq_id, spirv::StorageClass::Function)\n    }\n\n    /// Return a SPIR-V type for a pointer to `resolution`.\n    ///\n    /// The given `resolution` must be one that we can represent\n    /// either as a `LocalType::Pointer` or `LocalType::LocalPointer`.\n    pub(super) fn get_resolution_pointer_id(\n        &mut self,\n        resolution: &TypeResolution,\n        class: spirv::StorageClass,\n    ) -> Word {\n        let resolution_type_id = self.get_expression_type_id(resolution);\n        self.get_pointer_type_id(resolution_type_id, class)\n    }\n\n    pub(super) fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {\n        self.get_type_id(LocalType::Numeric(numeric).into())\n    }\n\n    pub(super) fn get_u32_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32))\n    }\n\n    pub(super) fn get_f32_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32))\n    }\n\n    pub(super) fn get_vec2u_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Bi,\n            scalar: crate::Scalar::U32,\n        })\n    }\n\n    pub(super) fn get_vec2f_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Bi,\n            scalar: crate::Scalar::F32,\n        })\n    }\n\n    pub(super) fn get_vec3u_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Tri,\n            scalar: crate::Scalar::U32,\n        })\n    }\n\n    pub(super) fn get_f32_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {\n        let f32_id = self.get_f32_type_id();\n        self.get_pointer_type_id(f32_id, class)\n    }\n\n    pub(super) fn get_vec2u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {\n        let vec2u_id = self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Bi,\n            scalar: crate::Scalar::U32,\n        });\n        self.get_pointer_type_id(vec2u_id, class)\n    }\n\n    pub(super) fn get_bool_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL))\n    }\n\n    pub(super) fn get_vec2_bool_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Bi,\n            scalar: crate::Scalar::BOOL,\n        })\n    }\n\n    pub(super) fn get_vec3_bool_type_id(&mut self) -> Word {\n        self.get_numeric_type_id(NumericType::Vector {\n            size: crate::VectorSize::Tri,\n            scalar: crate::Scalar::BOOL,\n        })\n    }\n\n    /// Used for \"mulhi\" to get the upper bits of multiplication.\n    ///\n    /// More specifically, `OpUMulExtended` multiplies 2 numbers and returns the lower and upper bits of the result\n    /// as a user-defined struct type with 2 u32s. This defines that struct.\n    pub(super) fn get_tuple_of_u32s_ty_id(&mut self) -> Word {\n        if let Some(val) = self.tuple_of_u32s_ty_id {\n            val\n        } else {\n            let id = self.id_gen.next();\n            let u32_id = self.get_u32_type_id();\n            let ins = Instruction::type_struct(id, &[u32_id, u32_id]);\n            ins.to_words(&mut self.logical_layout.declarations);\n            self.tuple_of_u32s_ty_id = Some(id);\n            id\n        }\n    }\n\n    pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {\n        self.annotations\n            .push(Instruction::decorate(id, decoration, operands));\n    }\n\n    /// Return `inner` as a `LocalType`, if that's possible.\n    ///\n    /// If `inner` can be represented as a `LocalType`, return\n    /// `Some(local_type)`.\n    ///\n    /// Otherwise, return `None`. In this case, the type must always be looked\n    /// up using a `LookupType::Handle`.\n    fn localtype_from_inner(&mut self, inner: &crate::TypeInner) -> Option<LocalType> {\n        Some(match *inner {\n            crate::TypeInner::Scalar(_)\n            | crate::TypeInner::Atomic(_)\n            | crate::TypeInner::Vector { .. }\n            | crate::TypeInner::Matrix { .. } => {\n                // We expect `NumericType::from_inner` to handle all\n                // these cases, so unwrap.\n                LocalType::Numeric(NumericType::from_inner(inner).unwrap())\n            }\n            crate::TypeInner::CooperativeMatrix { .. } => {\n                LocalType::Cooperative(CooperativeType::from_inner(inner).unwrap())\n            }\n            crate::TypeInner::Pointer { base, space } => {\n                let base_type_id = self.get_handle_type_id(base);\n                LocalType::Pointer {\n                    base: base_type_id,\n                    class: map_storage_class(space),\n                }\n            }\n            crate::TypeInner::ValuePointer {\n                size,\n                scalar,\n                space,\n            } => {\n                let base_numeric_type = match size {\n                    Some(size) => NumericType::Vector { size, scalar },\n                    None => NumericType::Scalar(scalar),\n                };\n                LocalType::Pointer {\n                    base: self.get_numeric_type_id(base_numeric_type),\n                    class: map_storage_class(space),\n                }\n            }\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),\n            crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,\n            crate::TypeInner::AccelerationStructure { .. } => LocalType::AccelerationStructure,\n            crate::TypeInner::RayQuery { .. } => LocalType::RayQuery,\n            crate::TypeInner::Array { .. }\n            | crate::TypeInner::Struct { .. }\n            | crate::TypeInner::BindingArray { .. } => return None,\n        })\n    }\n\n    /// Resolve the [`BindingInfo`] for a [`crate::ResourceBinding`] from the\n    /// provided [`Writer::binding_map`].\n    ///\n    /// If the specified resource is not present in the binding map this will\n    /// return an error, unless [`Writer::fake_missing_bindings`] is set.\n    fn resolve_resource_binding(\n        &self,\n        res_binding: &crate::ResourceBinding,\n    ) -> Result<BindingInfo, Error> {\n        match self.binding_map.get(res_binding) {\n            Some(target) => Ok(*target),\n            None if self.fake_missing_bindings => Ok(BindingInfo {\n                descriptor_set: res_binding.group,\n                binding: res_binding.binding,\n                binding_array_size: None,\n            }),\n            None => Err(Error::MissingBinding(*res_binding)),\n        }\n    }\n\n    /// Emits code for any wrapper functions required by the expressions in ir_function.\n    /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`].\n    fn write_wrapped_functions(\n        &mut self,\n        ir_function: &crate::Function,\n        info: &FunctionInfo,\n        ir_module: &crate::Module,\n    ) -> Result<(), Error> {\n        log::trace!(\"Generating wrapped functions for {:?}\", ir_function.name);\n\n        for (expr_handle, expr) in ir_function.expressions.iter() {\n            match *expr {\n                crate::Expression::Binary { op, left, right } => {\n                    let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);\n                    if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {\n                        match (op, expr_ty.scalar().kind) {\n                            // Division and modulo are undefined behaviour when the\n                            // dividend is the minimum representable value and the divisor\n                            // is negative one, or when the divisor is zero. These wrapped\n                            // functions override the divisor to one in these cases,\n                            // matching the WGSL spec.\n                            (\n                                crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,\n                                crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                            ) => {\n                                self.write_wrapped_binary_op(\n                                    op,\n                                    expr_ty,\n                                    &info[left].ty,\n                                    &info[right].ty,\n                                )?;\n                            }\n                            _ => {}\n                        }\n                    }\n                }\n                crate::Expression::Load { pointer } => {\n                    if let crate::TypeInner::Pointer {\n                        base: pointer_type,\n                        space: crate::AddressSpace::Uniform,\n                    } = *info[pointer].ty.inner_with(&ir_module.types)\n                    {\n                        if self.std140_compat_uniform_types.contains_key(&pointer_type) {\n                            // Loading a std140 compat type requires the wrapper function\n                            // to convert to the regular type.\n                            self.write_wrapped_convert_from_std140_compat_type(\n                                ir_module,\n                                pointer_type,\n                            )?;\n                        }\n                    }\n                }\n                crate::Expression::Access { base, .. } => {\n                    if let crate::TypeInner::Pointer {\n                        base: base_type,\n                        space: crate::AddressSpace::Uniform,\n                    } = *info[base].ty.inner_with(&ir_module.types)\n                    {\n                        // Dynamic accesses of a two-row matrix's columns require a\n                        // wrapper function.\n                        if let crate::TypeInner::Matrix {\n                            rows: crate::VectorSize::Bi,\n                            ..\n                        } = ir_module.types[base_type].inner\n                        {\n                            self.write_wrapped_matcx2_get_column(ir_module, base_type)?;\n                            // If the matrix is *not* directly a member of a struct, then\n                            // we additionally require a wrapper function to convert from\n                            // the std140 compat type to the regular type.\n                            if !is_uniform_matcx2_struct_member_access(\n                                ir_function,\n                                info,\n                                ir_module,\n                                base,\n                            ) {\n                                self.write_wrapped_convert_from_std140_compat_type(\n                                    ir_module, base_type,\n                                )?;\n                            }\n                        }\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Write a SPIR-V function that performs the operator `op` with Naga IR semantics.\n    ///\n    /// Define a function that performs an integer division or modulo operation,\n    /// except that using a divisor of zero or causing signed overflow with a\n    /// divisor of -1 returns the numerator unchanged, rather than exhibiting\n    /// undefined behavior.\n    ///\n    /// Store the generated function's id in the [`wrapped_functions`] table.\n    ///\n    /// The operator `op` must be either [`Divide`] or [`Modulo`].\n    ///\n    /// # Panics\n    ///\n    /// The `return_type`, `left_type` or `right_type` arguments must all be\n    /// integer scalars or vectors. If not, this function panics.\n    ///\n    /// [`wrapped_functions`]: Writer::wrapped_functions\n    /// [`Divide`]: crate::BinaryOperator::Divide\n    /// [`Modulo`]: crate::BinaryOperator::Modulo\n    fn write_wrapped_binary_op(\n        &mut self,\n        op: crate::BinaryOperator,\n        return_type: NumericType,\n        left_type: &TypeResolution,\n        right_type: &TypeResolution,\n    ) -> Result<(), Error> {\n        let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));\n        let left_type_id = self.get_expression_type_id(left_type);\n        let right_type_id = self.get_expression_type_id(right_type);\n\n        // Check if we've already emitted this function.\n        let wrapped = WrappedFunction::BinaryOp {\n            op,\n            left_type_id,\n            right_type_id,\n        };\n        let function_id = match self.wrapped_functions.entry(wrapped) {\n            Entry::Occupied(_) => return Ok(()),\n            Entry::Vacant(e) => *e.insert(self.id_gen.next()),\n        };\n\n        let scalar = return_type.scalar();\n\n        if self.flags.contains(WriterFlags::DEBUG) {\n            let function_name = match op {\n                crate::BinaryOperator::Divide => \"naga_div\",\n                crate::BinaryOperator::Modulo => \"naga_mod\",\n                _ => unreachable!(),\n            };\n            self.debugs\n                .push(Instruction::name(function_id, function_name));\n        }\n        let mut function = Function::default();\n\n        let function_type_id = self.get_function_type(LookupFunctionType {\n            parameter_type_ids: vec![left_type_id, right_type_id],\n            return_type_id,\n        });\n        function.signature = Some(Instruction::function(\n            return_type_id,\n            function_id,\n            spirv::FunctionControl::empty(),\n            function_type_id,\n        ));\n\n        let lhs_id = self.id_gen.next();\n        let rhs_id = self.id_gen.next();\n        if self.flags.contains(WriterFlags::DEBUG) {\n            self.debugs.push(Instruction::name(lhs_id, \"lhs\"));\n            self.debugs.push(Instruction::name(rhs_id, \"rhs\"));\n        }\n        let left_par = Instruction::function_parameter(left_type_id, lhs_id);\n        let right_par = Instruction::function_parameter(right_type_id, rhs_id);\n        for instruction in [left_par, right_par] {\n            function.parameters.push(FunctionArgument {\n                instruction,\n                handle_id: 0,\n            });\n        }\n\n        let label_id = self.id_gen.next();\n        let mut block = Block::new(label_id);\n\n        let bool_type = return_type.with_scalar(crate::Scalar::BOOL);\n        let bool_type_id = self.get_numeric_type_id(bool_type);\n\n        let maybe_splat_const = |writer: &mut Self, const_id| match return_type {\n            NumericType::Scalar(_) => const_id,\n            NumericType::Vector { size, .. } => {\n                let constituent_ids = [const_id; crate::VectorSize::MAX];\n                writer.get_constant_composite(\n                    LookupType::Local(LocalType::Numeric(return_type)),\n                    &constituent_ids[..size as usize],\n                )\n            }\n            NumericType::Matrix { .. } => unreachable!(),\n        };\n\n        let const_zero_id = self.get_constant_scalar_with(0, scalar)?;\n        let composite_zero_id = maybe_splat_const(self, const_zero_id);\n        let rhs_eq_zero_id = self.id_gen.next();\n        block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            bool_type_id,\n            rhs_eq_zero_id,\n            rhs_id,\n            composite_zero_id,\n        ));\n        let divisor_selector_id = match scalar.kind {\n            crate::ScalarKind::Sint => {\n                let (const_min_id, const_neg_one_id) = match scalar.width {\n                    4 => Ok((\n                        self.get_constant_scalar(crate::Literal::I32(i32::MIN)),\n                        self.get_constant_scalar(crate::Literal::I32(-1i32)),\n                    )),\n                    8 => Ok((\n                        self.get_constant_scalar(crate::Literal::I64(i64::MIN)),\n                        self.get_constant_scalar(crate::Literal::I64(-1i64)),\n                    )),\n                    _ => Err(Error::Validation(\"Unexpected scalar width\")),\n                }?;\n                let composite_min_id = maybe_splat_const(self, const_min_id);\n                let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);\n\n                let lhs_eq_int_min_id = self.id_gen.next();\n                block.body.push(Instruction::binary(\n                    spirv::Op::IEqual,\n                    bool_type_id,\n                    lhs_eq_int_min_id,\n                    lhs_id,\n                    composite_min_id,\n                ));\n                let rhs_eq_neg_one_id = self.id_gen.next();\n                block.body.push(Instruction::binary(\n                    spirv::Op::IEqual,\n                    bool_type_id,\n                    rhs_eq_neg_one_id,\n                    rhs_id,\n                    composite_neg_one_id,\n                ));\n                let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();\n                block.body.push(Instruction::binary(\n                    spirv::Op::LogicalAnd,\n                    bool_type_id,\n                    lhs_eq_int_min_and_rhs_eq_neg_one_id,\n                    lhs_eq_int_min_id,\n                    rhs_eq_neg_one_id,\n                ));\n                let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();\n                block.body.push(Instruction::binary(\n                    spirv::Op::LogicalOr,\n                    bool_type_id,\n                    rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,\n                    rhs_eq_zero_id,\n                    lhs_eq_int_min_and_rhs_eq_neg_one_id,\n                ));\n                rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id\n            }\n            crate::ScalarKind::Uint => rhs_eq_zero_id,\n            _ => unreachable!(),\n        };\n\n        let const_one_id = self.get_constant_scalar_with(1, scalar)?;\n        let composite_one_id = maybe_splat_const(self, const_one_id);\n        let divisor_id = self.id_gen.next();\n        block.body.push(Instruction::select(\n            right_type_id,\n            divisor_id,\n            divisor_selector_id,\n            composite_one_id,\n            rhs_id,\n        ));\n        let op = match (op, scalar.kind) {\n            (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,\n            (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,\n            (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,\n            (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,\n            _ => unreachable!(),\n        };\n        let return_id = self.id_gen.next();\n        block.body.push(Instruction::binary(\n            op,\n            return_type_id,\n            return_id,\n            lhs_id,\n            divisor_id,\n        ));\n\n        function.consume(block, Instruction::return_value(return_id));\n        function.to_words(&mut self.logical_layout.function_definitions);\n        Ok(())\n    }\n\n    /// Writes a wrapper function to convert from a std140 compat type to its\n    /// corresponding regular type.\n    ///\n    /// See [`Self::write_std140_compat_type_declaration`] for more details.\n    fn write_wrapped_convert_from_std140_compat_type(\n        &mut self,\n        ir_module: &crate::Module,\n        r#type: Handle<crate::Type>,\n    ) -> Result<(), Error> {\n        // Check if we've already emitted this function.\n        let wrapped = WrappedFunction::ConvertFromStd140CompatType { r#type };\n        let function_id = match self.wrapped_functions.entry(wrapped) {\n            Entry::Occupied(_) => return Ok(()),\n            Entry::Vacant(e) => *e.insert(self.id_gen.next()),\n        };\n        if self.flags.contains(WriterFlags::DEBUG) {\n            self.debugs.push(Instruction::name(\n                function_id,\n                &format!(\"{:?}_from_std140\", r#type.for_debug(&ir_module.types)),\n            ));\n        }\n        let param_type_id = self.std140_compat_uniform_types[&r#type].type_id;\n        let return_type_id = self.get_handle_type_id(r#type);\n\n        let mut function = Function::default();\n        let function_type_id = self.get_function_type(LookupFunctionType {\n            parameter_type_ids: vec![param_type_id],\n            return_type_id,\n        });\n        function.signature = Some(Instruction::function(\n            return_type_id,\n            function_id,\n            spirv::FunctionControl::empty(),\n            function_type_id,\n        ));\n        let param_id = self.id_gen.next();\n        function.parameters.push(FunctionArgument {\n            instruction: Instruction::function_parameter(param_type_id, param_id),\n            handle_id: 0,\n        });\n\n        let label_id = self.id_gen.next();\n        let mut block = Block::new(label_id);\n\n        let result_id = match ir_module.types[r#type].inner {\n            // Param is struct containing a vector member for each of the\n            // matrix's columns. Extract each column from the struct then\n            // composite into a matrix.\n            crate::TypeInner::Matrix {\n                columns,\n                rows: rows @ crate::VectorSize::Bi,\n                scalar,\n            } => {\n                let column_type_id =\n                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n\n                let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();\n                for column in 0..columns as u32 {\n                    let column_id = self.id_gen.next();\n                    block.body.push(Instruction::composite_extract(\n                        column_type_id,\n                        column_id,\n                        param_id,\n                        &[column],\n                    ));\n                    column_ids.push(column_id);\n                }\n                let result_id = self.id_gen.next();\n                block.body.push(Instruction::composite_construct(\n                    return_type_id,\n                    result_id,\n                    &column_ids,\n                ));\n                result_id\n            }\n            // Param is an array where the base type is the std140 compatible\n            // type corresponding to `base`. Iterate through each element and\n            // call its conversion function, then composite into a new array.\n            crate::TypeInner::Array { base, size, .. } => {\n                // Ensure the conversion function for the array's base type is\n                // declared.\n                self.write_wrapped_convert_from_std140_compat_type(ir_module, base)?;\n\n                let element_type_id = self.get_handle_type_id(base);\n                let std140_element_type_id = self.std140_compat_uniform_types[&base].type_id;\n                let element_conversion_function_id = self.wrapped_functions\n                    [&WrappedFunction::ConvertFromStd140CompatType { r#type: base }];\n                let mut element_ids = Vec::new();\n                let size = match size.resolve(ir_module.to_ctx())? {\n                    crate::proc::IndexableLength::Known(size) => size,\n                    crate::proc::IndexableLength::Dynamic => {\n                        return Err(Error::Validation(\n                            \"Uniform buffers cannot contain dynamic arrays\",\n                        ))\n                    }\n                };\n                for i in 0..size {\n                    let std140_element_id = self.id_gen.next();\n                    block.body.push(Instruction::composite_extract(\n                        std140_element_type_id,\n                        std140_element_id,\n                        param_id,\n                        &[i],\n                    ));\n                    let element_id = self.id_gen.next();\n                    block.body.push(Instruction::function_call(\n                        element_type_id,\n                        element_id,\n                        element_conversion_function_id,\n                        &[std140_element_id],\n                    ));\n                    element_ids.push(element_id);\n                }\n                let result_id = self.id_gen.next();\n                block.body.push(Instruction::composite_construct(\n                    return_type_id,\n                    result_id,\n                    &element_ids,\n                ));\n                result_id\n            }\n            // Param is a struct where each two-row matrix member has been\n            // decomposed in to separate vector members for each column.\n            // Other members use their std140 compatible type if one exists, or\n            // else their regular type. Iterate through each member, converting\n            // or composing any matrices if required, then finally compose into\n            // the struct.\n            crate::TypeInner::Struct { ref members, .. } => {\n                let mut member_ids = Vec::new();\n                let mut next_index = 0;\n                for member in members {\n                    let member_id = self.id_gen.next();\n                    let member_type_id = self.get_handle_type_id(member.ty);\n                    match ir_module.types[member.ty].inner {\n                        crate::TypeInner::Matrix {\n                            columns,\n                            rows: rows @ crate::VectorSize::Bi,\n                            scalar,\n                        } => {\n                            let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();\n                            let column_type_id = self\n                                .get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n                            for _ in 0..columns as u32 {\n                                let column_id = self.id_gen.next();\n                                block.body.push(Instruction::composite_extract(\n                                    column_type_id,\n                                    column_id,\n                                    param_id,\n                                    &[next_index],\n                                ));\n                                column_ids.push(column_id);\n                                next_index += 1;\n                            }\n                            block.body.push(Instruction::composite_construct(\n                                member_type_id,\n                                member_id,\n                                &column_ids,\n                            ));\n                        }\n                        _ => {\n                            // Ensure the conversion function for the member's\n                            // type is declared.\n                            self.write_wrapped_convert_from_std140_compat_type(\n                                ir_module, member.ty,\n                            )?;\n                            match self.std140_compat_uniform_types.get(&member.ty) {\n                                Some(std140_type_info) => {\n                                    let std140_member_id = self.id_gen.next();\n                                    block.body.push(Instruction::composite_extract(\n                                        std140_type_info.type_id,\n                                        std140_member_id,\n                                        param_id,\n                                        &[next_index],\n                                    ));\n                                    let function_id = self.wrapped_functions\n                                        [&WrappedFunction::ConvertFromStd140CompatType {\n                                            r#type: member.ty,\n                                        }];\n                                    block.body.push(Instruction::function_call(\n                                        member_type_id,\n                                        member_id,\n                                        function_id,\n                                        &[std140_member_id],\n                                    ));\n                                    next_index += 1;\n                                }\n                                None => {\n                                    let member_id = self.id_gen.next();\n                                    block.body.push(Instruction::composite_extract(\n                                        member_type_id,\n                                        member_id,\n                                        param_id,\n                                        &[next_index],\n                                    ));\n                                    next_index += 1;\n                                }\n                            }\n                        }\n                    }\n                    member_ids.push(member_id);\n                }\n                let result_id = self.id_gen.next();\n                block.body.push(Instruction::composite_construct(\n                    return_type_id,\n                    result_id,\n                    &member_ids,\n                ));\n                result_id\n            }\n            _ => unreachable!(),\n        };\n\n        function.consume(block, Instruction::return_value(result_id));\n        function.to_words(&mut self.logical_layout.function_definitions);\n        Ok(())\n    }\n\n    /// Writes a wrapper function to get an `OpTypeVector` column from an\n    /// `OpTypeMatrix` with a dynamic index.\n    ///\n    /// This is used when accessing a column of a [`TypeInner::Matrix`] through\n    /// a [`Uniform`] address space pointer. In such cases, the matrix will have\n    /// been declared in SPIR-V using an alternative type where each column is a\n    /// member of a containing struct. SPIR-V is unable to dynamically access\n    /// struct members, so instead we load the matrix then call this function to\n    /// access a column from the loaded value.\n    ///\n    /// [`TypeInner::Matrix`]: crate::TypeInner::Matrix\n    /// [`Uniform`]: crate::AddressSpace::Uniform\n    fn write_wrapped_matcx2_get_column(\n        &mut self,\n        ir_module: &crate::Module,\n        r#type: Handle<crate::Type>,\n    ) -> Result<(), Error> {\n        let wrapped = WrappedFunction::MatCx2GetColumn { r#type };\n        let function_id = match self.wrapped_functions.entry(wrapped) {\n            Entry::Occupied(_) => return Ok(()),\n            Entry::Vacant(e) => *e.insert(self.id_gen.next()),\n        };\n        if self.flags.contains(WriterFlags::DEBUG) {\n            self.debugs.push(Instruction::name(\n                function_id,\n                &format!(\"{:?}_get_column\", r#type.for_debug(&ir_module.types)),\n            ));\n        }\n\n        let crate::TypeInner::Matrix {\n            columns,\n            rows: rows @ crate::VectorSize::Bi,\n            scalar,\n        } = ir_module.types[r#type].inner\n        else {\n            unreachable!();\n        };\n\n        let mut function = Function::default();\n        let matrix_type_id = self.get_handle_type_id(r#type);\n        let column_index_type_id = self.get_u32_type_id();\n        let column_type_id = self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n        let matrix_param_id = self.id_gen.next();\n        let column_index_param_id = self.id_gen.next();\n        function.parameters.push(FunctionArgument {\n            instruction: Instruction::function_parameter(matrix_type_id, matrix_param_id),\n            handle_id: 0,\n        });\n        function.parameters.push(FunctionArgument {\n            instruction: Instruction::function_parameter(\n                column_index_type_id,\n                column_index_param_id,\n            ),\n            handle_id: 0,\n        });\n        let function_type_id = self.get_function_type(LookupFunctionType {\n            parameter_type_ids: vec![matrix_type_id, column_index_type_id],\n            return_type_id: column_type_id,\n        });\n        function.signature = Some(Instruction::function(\n            column_type_id,\n            function_id,\n            spirv::FunctionControl::empty(),\n            function_type_id,\n        ));\n\n        let label_id = self.id_gen.next();\n        let mut block = Block::new(label_id);\n\n        // Create a switch case for each column in the matrix, where each case\n        // extracts its column from the matrix. Finally we use OpPhi to return\n        // the correct column.\n        let merge_id = self.id_gen.next();\n        block.body.push(Instruction::selection_merge(\n            merge_id,\n            spirv::SelectionControl::NONE,\n        ));\n        let cases = (0..columns as u32)\n            .map(|i| super::instructions::Case {\n                value: i,\n                label_id: self.id_gen.next(),\n            })\n            .collect::<ArrayVec<_, 4>>();\n\n        // Which label we branch to in the default (column index out-of-bounds)\n        // case depends on our bounds check policy.\n        let default_id = match self.bounds_check_policies.index {\n            // For `Restrict`, treat the same as the final column.\n            crate::proc::BoundsCheckPolicy::Restrict => cases.last().unwrap().label_id,\n            // For `ReadZeroSkipWrite`, branch directly to the merge block. This\n            // will be handled in the `OpPhi` below to produce a zero value.\n            crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => merge_id,\n            // For `Unchecked` we create a new block containing an\n            // `OpUnreachable`.\n            crate::proc::BoundsCheckPolicy::Unchecked => self.id_gen.next(),\n        };\n        function.consume(\n            block,\n            Instruction::switch(column_index_param_id, default_id, &cases),\n        );\n\n        // Emit a block for each case, and produce a list of variable and parent\n        // block IDs that will be used in an `OpPhi` below to select the right\n        // value.\n        let mut var_parent_pairs = cases\n            .into_iter()\n            .map(|case| {\n                let mut block = Block::new(case.label_id);\n                let column_id = self.id_gen.next();\n                block.body.push(Instruction::composite_extract(\n                    column_type_id,\n                    column_id,\n                    matrix_param_id,\n                    &[case.value],\n                ));\n                function.consume(block, Instruction::branch(merge_id));\n                (column_id, case.label_id)\n            })\n            // Need capacity for up to 4 columns plus possibly a default case.\n            .collect::<ArrayVec<_, 5>>();\n\n        // Emit a block or append the variable and parent `OpPhi` pair for the\n        // column index out-of-bounds case, if required.\n        match self.bounds_check_policies.index {\n            // Don't need to do anything for `Restrict` as we have branched from\n            // the final column case's block.\n            crate::proc::BoundsCheckPolicy::Restrict => {}\n            // For `ReadZeroSkipWrite` we have branched directly from the block\n            // containing the `OpSwitch`. The `OpPhi` should produce a zero\n            // value.\n            crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => {\n                var_parent_pairs.push((self.get_constant_null(column_type_id), label_id));\n            }\n            // For `Unchecked` create a new block containing `OpUnreachable`.\n            // This does not need to be handled by the `OpPhi`.\n            crate::proc::BoundsCheckPolicy::Unchecked => {\n                function.consume(\n                    Block::new(default_id),\n                    Instruction::new(spirv::Op::Unreachable),\n                );\n            }\n        }\n\n        let mut block = Block::new(merge_id);\n        let result_id = self.id_gen.next();\n        block.body.push(Instruction::phi(\n            column_type_id,\n            result_id,\n            &var_parent_pairs,\n        ));\n\n        function.consume(block, Instruction::return_value(result_id));\n        function.to_words(&mut self.logical_layout.function_definitions);\n        Ok(())\n    }\n\n    fn write_function(\n        &mut self,\n        ir_function: &crate::Function,\n        info: &FunctionInfo,\n        ir_module: &crate::Module,\n        mut interface: Option<FunctionInterface>,\n        debug_info: &Option<DebugInfoInner>,\n    ) -> Result<Word, Error> {\n        self.write_wrapped_functions(ir_function, info, ir_module)?;\n\n        log::trace!(\"Generating code for {:?}\", ir_function.name);\n        let mut function = Function::default();\n\n        let prelude_id = self.id_gen.next();\n        let mut prelude = Block::new(prelude_id);\n        let mut ep_context = EntryPointContext {\n            argument_ids: Vec::new(),\n            results: Vec::new(),\n            task_payload_variable_id: if let Some(ref i) = interface {\n                i.task_payload.map(|a| self.global_variables[a].var_id)\n            } else {\n                None\n            },\n            mesh_state: None,\n        };\n\n        let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());\n\n        let mut local_invocation_index_var_id = None;\n        let mut local_invocation_index_id = None;\n\n        for argument in ir_function.arguments.iter() {\n            let class = spirv::StorageClass::Input;\n            let handle_ty = ir_module.types[argument.ty].inner.is_handle();\n            let argument_type_id = if handle_ty {\n                self.get_handle_pointer_type_id(argument.ty, spirv::StorageClass::UniformConstant)\n            } else {\n                self.get_handle_type_id(argument.ty)\n            };\n\n            if let Some(ref mut iface) = interface {\n                let id = if let Some(ref binding) = argument.binding {\n                    let name = argument.name.as_deref();\n\n                    let varying_id = self.write_varying(\n                        ir_module,\n                        iface.stage,\n                        class,\n                        name,\n                        argument.ty,\n                        binding,\n                    )?;\n                    iface.varying_ids.push(varying_id);\n                    let id = self.load_io_with_f16_polyfill(\n                        &mut prelude.body,\n                        varying_id,\n                        argument_type_id,\n                    );\n                    if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex) {\n                        local_invocation_index_id = Some(id);\n                        local_invocation_index_var_id = Some(varying_id);\n                    }\n\n                    id\n                } else if let crate::TypeInner::Struct { ref members, .. } =\n                    ir_module.types[argument.ty].inner\n                {\n                    let struct_id = self.id_gen.next();\n                    let mut constituent_ids = Vec::with_capacity(members.len());\n                    for member in members {\n                        let type_id = self.get_handle_type_id(member.ty);\n                        let name = member.name.as_deref();\n                        let binding = member.binding.as_ref().unwrap();\n                        let varying_id = self.write_varying(\n                            ir_module,\n                            iface.stage,\n                            class,\n                            name,\n                            member.ty,\n                            binding,\n                        )?;\n                        iface.varying_ids.push(varying_id);\n                        let id =\n                            self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id);\n                        constituent_ids.push(id);\n                        if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex)\n                        {\n                            local_invocation_index_id = Some(id);\n                            local_invocation_index_var_id = Some(varying_id);\n                        }\n                    }\n                    prelude.body.push(Instruction::composite_construct(\n                        argument_type_id,\n                        struct_id,\n                        &constituent_ids,\n                    ));\n                    struct_id\n                } else {\n                    unreachable!(\"Missing argument binding on an entry point\");\n                };\n                ep_context.argument_ids.push(id);\n            } else {\n                let argument_id = self.id_gen.next();\n                let instruction = Instruction::function_parameter(argument_type_id, argument_id);\n                if self.flags.contains(WriterFlags::DEBUG) {\n                    if let Some(ref name) = argument.name {\n                        self.debugs.push(Instruction::name(argument_id, name));\n                    }\n                }\n                function.parameters.push(FunctionArgument {\n                    instruction,\n                    handle_id: if handle_ty {\n                        let id = self.id_gen.next();\n                        prelude.body.push(Instruction::load(\n                            self.get_handle_type_id(argument.ty),\n                            id,\n                            argument_id,\n                            None,\n                        ));\n                        id\n                    } else {\n                        0\n                    },\n                });\n                parameter_type_ids.push(argument_type_id);\n            };\n        }\n\n        let return_type_id = match ir_function.result {\n            Some(ref result) => {\n                if let Some(ref mut iface) = interface {\n                    let mut has_point_size = false;\n                    let class = spirv::StorageClass::Output;\n                    if let Some(ref binding) = result.binding {\n                        has_point_size |=\n                            *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);\n                        let type_id = self.get_handle_type_id(result.ty);\n                        let varying_id =\n                            if *binding == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize) {\n                                0\n                            } else {\n                                let varying_id = self.write_varying(\n                                    ir_module,\n                                    iface.stage,\n                                    class,\n                                    None,\n                                    result.ty,\n                                    binding,\n                                )?;\n                                iface.varying_ids.push(varying_id);\n                                varying_id\n                            };\n                        ep_context.results.push(ResultMember {\n                            id: varying_id,\n                            type_id,\n                            built_in: binding.to_built_in(),\n                        });\n                    } else if let crate::TypeInner::Struct { ref members, .. } =\n                        ir_module.types[result.ty].inner\n                    {\n                        for member in members {\n                            let type_id = self.get_handle_type_id(member.ty);\n                            let name = member.name.as_deref();\n                            let binding = member.binding.as_ref().unwrap();\n                            has_point_size |=\n                                *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);\n                            // This isn't an actual builtin in SPIR-V. It can only appear as the\n                            // output of a task shader and the output is used when writing the\n                            // entry point return, in which case the id is ignored anyway.\n                            let varying_id = if *binding\n                                == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize)\n                            {\n                                0\n                            } else {\n                                let varying_id = self.write_varying(\n                                    ir_module,\n                                    iface.stage,\n                                    class,\n                                    name,\n                                    member.ty,\n                                    binding,\n                                )?;\n                                iface.varying_ids.push(varying_id);\n                                varying_id\n                            };\n                            ep_context.results.push(ResultMember {\n                                id: varying_id,\n                                type_id,\n                                built_in: binding.to_built_in(),\n                            });\n                        }\n                    } else {\n                        unreachable!(\"Missing result binding on an entry point\");\n                    }\n\n                    if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)\n                        && iface.stage == crate::ShaderStage::Vertex\n                        && !has_point_size\n                    {\n                        // add point size artificially\n                        let varying_id = self.id_gen.next();\n                        let pointer_type_id = self.get_f32_pointer_type_id(class);\n                        Instruction::variable(pointer_type_id, varying_id, class, None)\n                            .to_words(&mut self.logical_layout.declarations);\n                        self.decorate(\n                            varying_id,\n                            spirv::Decoration::BuiltIn,\n                            &[spirv::BuiltIn::PointSize as u32],\n                        );\n                        iface.varying_ids.push(varying_id);\n\n                        let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));\n                        prelude\n                            .body\n                            .push(Instruction::store(varying_id, default_value_id, None));\n                    }\n                    if iface.stage == crate::ShaderStage::Task {\n                        self.get_vec3u_type_id()\n                    } else {\n                        self.void_type\n                    }\n                } else {\n                    self.get_handle_type_id(result.ty)\n                }\n            }\n            None => self.void_type,\n        };\n\n        if let Some(ref mut iface) = interface {\n            if let Some(task_payload) = iface.task_payload {\n                iface\n                    .varying_ids\n                    .push(self.global_variables[task_payload].var_id);\n            }\n            self.write_entry_point_mesh_shader_info(\n                iface,\n                local_invocation_index_var_id,\n                ir_module,\n                &mut ep_context,\n            )?;\n        }\n\n        let lookup_function_type = LookupFunctionType {\n            parameter_type_ids,\n            return_type_id,\n        };\n\n        let function_id = self.id_gen.next();\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(ref name) = ir_function.name {\n                self.debugs.push(Instruction::name(function_id, name));\n            }\n        }\n\n        let function_type = self.get_function_type(lookup_function_type);\n        function.signature = Some(Instruction::function(\n            return_type_id,\n            function_id,\n            spirv::FunctionControl::empty(),\n            function_type,\n        ));\n\n        if interface.is_some() {\n            function.entry_point_context = Some(ep_context);\n        }\n\n        // fill up the `GlobalVariable::access_id`\n        for gv in self.global_variables.iter_mut() {\n            gv.reset_for_function();\n        }\n        for (handle, var) in ir_module.global_variables.iter() {\n            if info[handle].is_empty() {\n                continue;\n            }\n\n            let mut gv = self.global_variables[handle].clone();\n            if let Some(ref mut iface) = interface {\n                // Have to include global variables in the interface\n                if self.physical_layout.version >= 0x10400 && iface.task_payload != Some(handle) {\n                    iface.varying_ids.push(gv.var_id);\n                }\n            }\n\n            match ir_module.types[var.ty].inner {\n                // Any that are binding arrays we skip as we cannot load the array, we must load the result after indexing.\n                crate::TypeInner::BindingArray { .. } => {\n                    gv.access_id = gv.var_id;\n                }\n                _ => {\n                    // Handle globals are pre-emitted and should be loaded automatically.\n                    if var.space == crate::AddressSpace::Handle {\n                        let var_type_id = self.get_handle_type_id(var.ty);\n                        let id = self.id_gen.next();\n                        prelude\n                            .body\n                            .push(Instruction::load(var_type_id, id, gv.var_id, None));\n                        gv.access_id = gv.var_id;\n                        gv.handle_id = id;\n                    } else if global_needs_wrapper(ir_module, var) {\n                        let class = map_storage_class(var.space);\n                        let pointer_type_id = match self.std140_compat_uniform_types.get(&var.ty) {\n                            Some(std140_type_info) if var.space == crate::AddressSpace::Uniform => {\n                                self.get_pointer_type_id(std140_type_info.type_id, class)\n                            }\n                            _ => self.get_handle_pointer_type_id(var.ty, class),\n                        };\n                        let index_id = self.get_index_constant(0);\n                        let id = self.id_gen.next();\n                        prelude.body.push(Instruction::access_chain(\n                            pointer_type_id,\n                            id,\n                            gv.var_id,\n                            &[index_id],\n                        ));\n                        gv.access_id = id;\n                    } else {\n                        // by default, the variable ID is accessed as is\n                        gv.access_id = gv.var_id;\n                    };\n                }\n            }\n\n            // work around borrow checking in the presence of `self.xxx()` calls\n            self.global_variables[handle] = gv;\n        }\n\n        // Create a `BlockContext` for generating SPIR-V for the function's\n        // body.\n        let mut context = BlockContext {\n            ir_module,\n            ir_function,\n            fun_info: info,\n            function: &mut function,\n            // Re-use the cached expression table from prior functions.\n            cached: core::mem::take(&mut self.saved_cached),\n\n            // Steal the Writer's temp list for a bit.\n            temp_list: core::mem::take(&mut self.temp_list),\n            force_loop_bounding: self.force_loop_bounding,\n            writer: self,\n            expression_constness: super::ExpressionConstnessTracker::from_arena(\n                &ir_function.expressions,\n            ),\n            ray_query_tracker_expr: crate::FastHashMap::default(),\n        };\n\n        // fill up the pre-emitted and const expressions\n        context.cached.reset(ir_function.expressions.len());\n        for (handle, expr) in ir_function.expressions.iter() {\n            if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))\n                || context.expression_constness.is_const(handle)\n            {\n                context.cache_expression_value(handle, &mut prelude)?;\n            }\n        }\n\n        for (handle, variable) in ir_function.local_variables.iter() {\n            let id = context.gen_id();\n\n            if context.writer.flags.contains(WriterFlags::DEBUG) {\n                if let Some(ref name) = variable.name {\n                    context.writer.debugs.push(Instruction::name(id, name));\n                }\n            }\n\n            let init_word = variable.init.map(|constant| context.cached[constant]);\n            let pointer_type_id = context\n                .writer\n                .get_handle_pointer_type_id(variable.ty, spirv::StorageClass::Function);\n            let instruction = Instruction::variable(\n                pointer_type_id,\n                id,\n                spirv::StorageClass::Function,\n                init_word.or_else(|| match ir_module.types[variable.ty].inner {\n                    crate::TypeInner::RayQuery { .. } => None,\n                    _ => {\n                        let type_id = context.get_handle_type_id(variable.ty);\n                        Some(context.writer.write_constant_null(type_id))\n                    }\n                }),\n            );\n\n            context\n                .function\n                .variables\n                .insert(handle, LocalVariable { id, instruction });\n\n            if let crate::TypeInner::RayQuery { .. } = ir_module.types[variable.ty].inner {\n                // Don't refactor this into a struct: Although spirv itself allows opaque types in structs,\n                // the vulkan environment for spirv does not. Putting ray queries into structs can cause\n                // confusing bugs.\n                let u32_type_id = context.writer.get_u32_type_id();\n                let ptr_u32_type_id = context\n                    .writer\n                    .get_pointer_type_id(u32_type_id, spirv::StorageClass::Function);\n                let tracker_id = context.gen_id();\n                let tracker_init_id = context.writer.get_constant_scalar(crate::Literal::U32(\n                    crate::back::RayQueryPoint::empty().bits(),\n                ));\n                let tracker_instruction = Instruction::variable(\n                    ptr_u32_type_id,\n                    tracker_id,\n                    spirv::StorageClass::Function,\n                    Some(tracker_init_id),\n                );\n\n                context\n                    .function\n                    .ray_query_initialization_tracker_variables\n                    .insert(\n                        handle,\n                        LocalVariable {\n                            id: tracker_id,\n                            instruction: tracker_instruction,\n                        },\n                    );\n                let f32_type_id = context.writer.get_f32_type_id();\n                let ptr_f32_type_id = context\n                    .writer\n                    .get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);\n                let t_max_tracker_id = context.gen_id();\n                let t_max_tracker_init_id =\n                    context.writer.get_constant_scalar(crate::Literal::F32(0.0));\n                let t_max_tracker_instruction = Instruction::variable(\n                    ptr_f32_type_id,\n                    t_max_tracker_id,\n                    spirv::StorageClass::Function,\n                    Some(t_max_tracker_init_id),\n                );\n\n                context.function.ray_query_t_max_tracker_variables.insert(\n                    handle,\n                    LocalVariable {\n                        id: t_max_tracker_id,\n                        instruction: t_max_tracker_instruction,\n                    },\n                );\n            }\n        }\n\n        for (handle, expr) in ir_function.expressions.iter() {\n            match *expr {\n                crate::Expression::LocalVariable(_) => {\n                    // Cache the `OpVariable` instruction we generated above as\n                    // the value of this expression.\n                    context.cache_expression_value(handle, &mut prelude)?;\n                }\n                crate::Expression::Access { base, .. }\n                | crate::Expression::AccessIndex { base, .. } => {\n                    // Count references to `base` by `Access` and `AccessIndex`\n                    // instructions. See `access_uses` for details.\n                    *context.function.access_uses.entry(base).or_insert(0) += 1;\n                }\n                _ => {}\n            }\n        }\n\n        let next_id = context.gen_id();\n\n        context\n            .function\n            .consume(prelude, Instruction::branch(next_id));\n\n        let workgroup_vars_init_exit_block_id =\n            match (context.writer.zero_initialize_workgroup_memory, interface) {\n                (\n                    super::ZeroInitializeWorkgroupMemoryMode::Polyfill,\n                    Some(\n                        ref mut interface @ FunctionInterface {\n                            stage:\n                                crate::ShaderStage::Compute\n                                | crate::ShaderStage::Mesh\n                                | crate::ShaderStage::Task,\n                            ..\n                        },\n                    ),\n                ) => context.writer.generate_workgroup_vars_init_block(\n                    next_id,\n                    ir_module,\n                    info,\n                    local_invocation_index_id,\n                    interface,\n                    context.function,\n                ),\n                _ => None,\n            };\n\n        let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {\n            exit_id\n        } else {\n            next_id\n        };\n\n        context.write_function_body(main_id, debug_info.as_ref())?;\n\n        // Consume the `BlockContext`, ending its borrows and letting the\n        // `Writer` steal back its cached expression table and temp_list.\n        let BlockContext {\n            cached, temp_list, ..\n        } = context;\n        self.saved_cached = cached;\n        self.temp_list = temp_list;\n\n        function.to_words(&mut self.logical_layout.function_definitions);\n\n        if let Some(EntryPointContext {\n            mesh_state: Some(ref mesh_state),\n            ..\n        }) = function.entry_point_context\n        {\n            self.write_mesh_shader_wrapper(mesh_state, function_id)\n        } else if let Some(EntryPointContext {\n            task_payload_variable_id: Some(tp),\n            ..\n        }) = function.entry_point_context\n        {\n            self.write_task_shader_wrapper(tp, function_id)\n        } else {\n            Ok(function_id)\n        }\n    }\n\n    fn write_execution_mode(\n        &mut self,\n        function_id: Word,\n        mode: spirv::ExecutionMode,\n    ) -> Result<(), Error> {\n        //self.check(mode.required_capabilities())?;\n        Instruction::execution_mode(function_id, mode, &[])\n            .to_words(&mut self.logical_layout.execution_modes);\n        Ok(())\n    }\n\n    // TODO Move to instructions module\n    fn write_entry_point(\n        &mut self,\n        entry_point: &crate::EntryPoint,\n        info: &FunctionInfo,\n        ir_module: &crate::Module,\n        debug_info: &Option<DebugInfoInner>,\n    ) -> Result<Instruction, Error> {\n        let mut interface_ids = Vec::new();\n\n        let function_id = self.write_function(\n            &entry_point.function,\n            info,\n            ir_module,\n            Some(FunctionInterface {\n                varying_ids: &mut interface_ids,\n                stage: entry_point.stage,\n                task_payload: entry_point.task_payload,\n                mesh_info: entry_point.mesh_info.clone(),\n                workgroup_size: entry_point.workgroup_size,\n            }),\n            debug_info,\n        )?;\n\n        let exec_model = match entry_point.stage {\n            crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,\n            crate::ShaderStage::Fragment => {\n                self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;\n                match entry_point.early_depth_test {\n                    Some(crate::EarlyDepthTest::Force) => {\n                        self.write_execution_mode(\n                            function_id,\n                            spirv::ExecutionMode::EarlyFragmentTests,\n                        )?;\n                    }\n                    Some(crate::EarlyDepthTest::Allow { conservative }) => {\n                        // TODO: Consider emitting EarlyAndLateFragmentTestsAMD here, if available.\n                        // https://github.khronos.org/SPIRV-Registry/extensions/AMD/SPV_AMD_shader_early_and_late_fragment_tests.html\n                        // This permits early depth tests even if the shader writes to a storage\n                        // binding\n                        match conservative {\n                            crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(\n                                function_id,\n                                spirv::ExecutionMode::DepthGreater,\n                            )?,\n                            crate::ConservativeDepth::LessEqual => self.write_execution_mode(\n                                function_id,\n                                spirv::ExecutionMode::DepthLess,\n                            )?,\n                            crate::ConservativeDepth::Unchanged => self.write_execution_mode(\n                                function_id,\n                                spirv::ExecutionMode::DepthUnchanged,\n                            )?,\n                        }\n                    }\n                    None => {}\n                }\n                if let Some(ref result) = entry_point.function.result {\n                    if contains_builtin(\n                        result.binding.as_ref(),\n                        result.ty,\n                        &ir_module.types,\n                        crate::BuiltIn::FragDepth,\n                    ) {\n                        self.write_execution_mode(\n                            function_id,\n                            spirv::ExecutionMode::DepthReplacing,\n                        )?;\n                    }\n                }\n                spirv::ExecutionModel::Fragment\n            }\n            crate::ShaderStage::Compute => {\n                let execution_mode = spirv::ExecutionMode::LocalSize;\n                Instruction::execution_mode(\n                    function_id,\n                    execution_mode,\n                    &entry_point.workgroup_size,\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                spirv::ExecutionModel::GLCompute\n            }\n            crate::ShaderStage::Task => {\n                let execution_mode = spirv::ExecutionMode::LocalSize;\n                Instruction::execution_mode(\n                    function_id,\n                    execution_mode,\n                    &entry_point.workgroup_size,\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                spirv::ExecutionModel::TaskEXT\n            }\n            crate::ShaderStage::Mesh => {\n                let execution_mode = spirv::ExecutionMode::LocalSize;\n                Instruction::execution_mode(\n                    function_id,\n                    execution_mode,\n                    &entry_point.workgroup_size,\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                let mesh_info = entry_point.mesh_info.as_ref().unwrap();\n                Instruction::execution_mode(\n                    function_id,\n                    match mesh_info.topology {\n                        crate::MeshOutputTopology::Points => spirv::ExecutionMode::OutputPoints,\n                        crate::MeshOutputTopology::Lines => spirv::ExecutionMode::OutputLinesEXT,\n                        crate::MeshOutputTopology::Triangles => {\n                            spirv::ExecutionMode::OutputTrianglesEXT\n                        }\n                    },\n                    &[],\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                Instruction::execution_mode(\n                    function_id,\n                    spirv::ExecutionMode::OutputVertices,\n                    core::slice::from_ref(&mesh_info.max_vertices),\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                Instruction::execution_mode(\n                    function_id,\n                    spirv::ExecutionMode::OutputPrimitivesEXT,\n                    core::slice::from_ref(&mesh_info.max_primitives),\n                )\n                .to_words(&mut self.logical_layout.execution_modes);\n                spirv::ExecutionModel::MeshEXT\n            }\n            crate::ShaderStage::RayGeneration\n            | crate::ShaderStage::AnyHit\n            | crate::ShaderStage::ClosestHit\n            | crate::ShaderStage::Miss => unreachable!(),\n        };\n        //self.check(exec_model.required_capabilities())?;\n\n        Ok(Instruction::entry_point(\n            exec_model,\n            function_id,\n            &entry_point.name,\n            interface_ids.as_slice(),\n        ))\n    }\n\n    fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {\n        use crate::ScalarKind as Sk;\n\n        let bits = (scalar.width * BITS_PER_BYTE) as u32;\n        match scalar.kind {\n            Sk::Sint | Sk::Uint => {\n                let signedness = if scalar.kind == Sk::Sint {\n                    super::instructions::Signedness::Signed\n                } else {\n                    super::instructions::Signedness::Unsigned\n                };\n                let cap = match bits {\n                    8 => Some(spirv::Capability::Int8),\n                    16 => Some(spirv::Capability::Int16),\n                    64 => Some(spirv::Capability::Int64),\n                    _ => None,\n                };\n                if let Some(cap) = cap {\n                    self.capabilities_used.insert(cap);\n                }\n                Instruction::type_int(id, bits, signedness)\n            }\n            Sk::Float => {\n                if bits == 64 {\n                    self.capabilities_used.insert(spirv::Capability::Float64);\n                }\n                if bits == 16 {\n                    self.capabilities_used.insert(spirv::Capability::Float16);\n                    self.capabilities_used\n                        .insert(spirv::Capability::StorageBuffer16BitAccess);\n                    self.capabilities_used\n                        .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);\n                    if self.use_storage_input_output_16 {\n                        self.capabilities_used\n                            .insert(spirv::Capability::StorageInputOutput16);\n                    }\n                }\n                Instruction::type_float(id, bits)\n            }\n            Sk::Bool => Instruction::type_bool(id),\n            Sk::AbstractInt | Sk::AbstractFloat => {\n                unreachable!(\"abstract types should never reach the backend\");\n            }\n        }\n    }\n\n    fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {\n        match *inner {\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                let sampled = match class {\n                    crate::ImageClass::Sampled { .. } => true,\n                    crate::ImageClass::Depth { .. } => true,\n                    crate::ImageClass::Storage { format, .. } => {\n                        self.request_image_format_capabilities(format.into())?;\n                        false\n                    }\n                    crate::ImageClass::External => unimplemented!(),\n                };\n\n                match dim {\n                    crate::ImageDimension::D1 => {\n                        if sampled {\n                            self.require_any(\"sampled 1D images\", &[spirv::Capability::Sampled1D])?;\n                        } else {\n                            self.require_any(\"1D storage images\", &[spirv::Capability::Image1D])?;\n                        }\n                    }\n                    crate::ImageDimension::Cube if arrayed => {\n                        if sampled {\n                            self.require_any(\n                                \"sampled cube array images\",\n                                &[spirv::Capability::SampledCubeArray],\n                            )?;\n                        } else {\n                            self.require_any(\n                                \"cube array storage images\",\n                                &[spirv::Capability::ImageCubeArray],\n                            )?;\n                        }\n                    }\n                    _ => {}\n                }\n            }\n            crate::TypeInner::AccelerationStructure { .. } => {\n                self.require_any(\"Acceleration Structure\", &[spirv::Capability::RayQueryKHR])?;\n            }\n            crate::TypeInner::RayQuery { .. } => {\n                self.require_any(\"Ray Query\", &[spirv::Capability::RayQueryKHR])?;\n            }\n            crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {\n                self.require_any(\"64 bit integer atomics\", &[spirv::Capability::Int64Atomics])?;\n            }\n            crate::TypeInner::Atomic(crate::Scalar {\n                width: 4,\n                kind: crate::ScalarKind::Float,\n            }) => {\n                self.require_any(\n                    \"32 bit floating-point atomics\",\n                    &[spirv::Capability::AtomicFloat32AddEXT],\n                )?;\n                self.use_extension(\"SPV_EXT_shader_atomic_float_add\");\n            }\n            // 16 bit floating-point support requires Float16 capability\n            crate::TypeInner::Matrix {\n                scalar: crate::Scalar::F16,\n                ..\n            }\n            | crate::TypeInner::Vector {\n                scalar: crate::Scalar::F16,\n                ..\n            }\n            | crate::TypeInner::Scalar(crate::Scalar::F16) => {\n                self.require_any(\"16 bit floating-point\", &[spirv::Capability::Float16])?;\n                self.use_extension(\"SPV_KHR_16bit_storage\");\n            }\n            // Cooperative types and ops\n            crate::TypeInner::CooperativeMatrix { .. } => {\n                self.require_any(\n                    \"cooperative matrix\",\n                    &[spirv::Capability::CooperativeMatrixKHR],\n                )?;\n                self.require_any(\"memory model\", &[spirv::Capability::VulkanMemoryModel])?;\n                self.use_extension(\"SPV_KHR_cooperative_matrix\");\n                self.use_extension(\"SPV_KHR_vulkan_memory_model\");\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {\n        let instruction = match numeric {\n            NumericType::Scalar(scalar) => self.make_scalar(id, scalar),\n            NumericType::Vector { size, scalar } => {\n                let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));\n                Instruction::type_vector(id, scalar_id, size)\n            }\n            NumericType::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                let column_id =\n                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n                Instruction::type_matrix(id, column_id, columns)\n            }\n        };\n\n        instruction.to_words(&mut self.logical_layout.declarations);\n    }\n\n    fn write_cooperative_type_declaration_local(&mut self, id: Word, coop: CooperativeType) {\n        let instruction = match coop {\n            CooperativeType::Matrix {\n                columns,\n                rows,\n                scalar,\n                role,\n            } => {\n                let scalar_id =\n                    self.get_localtype_id(LocalType::Numeric(NumericType::Scalar(scalar)));\n                let scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);\n                let columns_id = self.get_index_constant(columns as u32);\n                let rows_id = self.get_index_constant(rows as u32);\n                let role_id =\n                    self.get_index_constant(spirv::CooperativeMatrixUse::from(role) as u32);\n                Instruction::type_coop_matrix(id, scalar_id, scope_id, rows_id, columns_id, role_id)\n            }\n        };\n\n        instruction.to_words(&mut self.logical_layout.declarations);\n    }\n\n    fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {\n        let instruction = match local_ty {\n            LocalType::Numeric(numeric) => {\n                self.write_numeric_type_declaration_local(id, numeric);\n                return;\n            }\n            LocalType::Cooperative(coop) => {\n                self.write_cooperative_type_declaration_local(id, coop);\n                return;\n            }\n            LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),\n            LocalType::Image(image) => {\n                let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));\n                let type_id = self.get_localtype_id(local_type);\n                Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)\n            }\n            LocalType::Sampler => Instruction::type_sampler(id),\n            LocalType::SampledImage { image_type_id } => {\n                Instruction::type_sampled_image(id, image_type_id)\n            }\n            LocalType::BindingArray { base, size } => {\n                let inner_ty = self.get_handle_type_id(base);\n                let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));\n                Instruction::type_array(id, inner_ty, scalar_id)\n            }\n            LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),\n            LocalType::RayQuery => Instruction::type_ray_query(id),\n        };\n\n        instruction.to_words(&mut self.logical_layout.declarations);\n    }\n\n    fn write_type_declaration_arena(\n        &mut self,\n        module: &crate::Module,\n        handle: Handle<crate::Type>,\n    ) -> Result<Word, Error> {\n        let ty = &module.types[handle];\n        // If it's a type that needs SPIR-V capabilities, request them now.\n        // This needs to happen regardless of the LocalType lookup succeeding,\n        // because some types which map to the same LocalType have different\n        // capability requirements. See https://github.com/gfx-rs/wgpu/issues/5569\n        self.request_type_capabilities(&ty.inner)?;\n        let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {\n            // This type can be represented as a `LocalType`, so check if we've\n            // already written an instruction for it. If not, do so now, with\n            // `write_type_declaration_local`.\n            match self.lookup_type.entry(LookupType::Local(local)) {\n                // We already have an id for this `LocalType`.\n                Entry::Occupied(e) => *e.get(),\n\n                // It's a type we haven't seen before.\n                Entry::Vacant(e) => {\n                    let id = self.id_gen.next();\n                    e.insert(id);\n\n                    self.write_type_declaration_local(id, local);\n\n                    id\n                }\n            }\n        } else {\n            use spirv::Decoration;\n\n            let id = self.id_gen.next();\n            let instruction = match ty.inner {\n                crate::TypeInner::Array { base, size, stride } => {\n                    self.decorate(id, Decoration::ArrayStride, &[stride]);\n\n                    let type_id = self.get_handle_type_id(base);\n                    match size.resolve(module.to_ctx())? {\n                        crate::proc::IndexableLength::Known(length) => {\n                            let length_id = self.get_index_constant(length);\n                            Instruction::type_array(id, type_id, length_id)\n                        }\n                        crate::proc::IndexableLength::Dynamic => {\n                            Instruction::type_runtime_array(id, type_id)\n                        }\n                    }\n                }\n                crate::TypeInner::BindingArray { base, size } => {\n                    let type_id = self.get_handle_type_id(base);\n                    match size.resolve(module.to_ctx())? {\n                        crate::proc::IndexableLength::Known(length) => {\n                            let length_id = self.get_index_constant(length);\n                            Instruction::type_array(id, type_id, length_id)\n                        }\n                        crate::proc::IndexableLength::Dynamic => {\n                            Instruction::type_runtime_array(id, type_id)\n                        }\n                    }\n                }\n                crate::TypeInner::Struct {\n                    ref members,\n                    span: _,\n                } => {\n                    let mut has_runtime_array = false;\n                    let mut member_ids = Vec::with_capacity(members.len());\n                    for (index, member) in members.iter().enumerate() {\n                        let member_ty = &module.types[member.ty];\n                        match member_ty.inner {\n                            crate::TypeInner::Array {\n                                base: _,\n                                size: crate::ArraySize::Dynamic,\n                                stride: _,\n                            } => {\n                                has_runtime_array = true;\n                            }\n                            _ => (),\n                        }\n                        self.decorate_struct_member(id, index, member, &module.types)?;\n                        let member_id = self.get_handle_type_id(member.ty);\n                        member_ids.push(member_id);\n                    }\n                    if has_runtime_array {\n                        self.decorate(id, Decoration::Block, &[]);\n                    }\n                    Instruction::type_struct(id, member_ids.as_slice())\n                }\n\n                // These all have TypeLocal representations, so they should have been\n                // handled by `write_type_declaration_local` above.\n                crate::TypeInner::Scalar(_)\n                | crate::TypeInner::Atomic(_)\n                | crate::TypeInner::Vector { .. }\n                | crate::TypeInner::Matrix { .. }\n                | crate::TypeInner::CooperativeMatrix { .. }\n                | crate::TypeInner::Pointer { .. }\n                | crate::TypeInner::ValuePointer { .. }\n                | crate::TypeInner::Image { .. }\n                | crate::TypeInner::Sampler { .. }\n                | crate::TypeInner::AccelerationStructure { .. }\n                | crate::TypeInner::RayQuery { .. } => unreachable!(),\n            };\n\n            instruction.to_words(&mut self.logical_layout.declarations);\n            id\n        };\n\n        // Add this handle as a new alias for that type.\n        self.lookup_type.insert(LookupType::Handle(handle), id);\n\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(ref name) = ty.name {\n                self.debugs.push(Instruction::name(id, name));\n            }\n        }\n\n        Ok(id)\n    }\n\n    /// Writes a std140 layout compatible type declaration for a type. Returns\n    /// the ID of the declared type, or None if no declaration is required.\n    ///\n    /// This should be called for any type for which there exists a\n    /// [`GlobalVariable`] in the [`Uniform`] address space. If the type already\n    /// adheres to std140 layout rules it will return without declaring any\n    /// types. If the type contains another type which requires a std140\n    /// compatible type declaration, it will recursively call itself.\n    ///\n    /// When `handle` refers to a [`TypeInner::Matrix`] with 2 rows, the\n    /// declared type will be an `OpTypeStruct` containing an `OpVector` for\n    /// each of the matrix's columns.\n    ///\n    /// When `handle` refers to a [`TypeInner::Array`] whose base type is a\n    /// matrix with 2 rows, this will declare an `OpTypeArray` whose element\n    /// type is the matrix's corresponding std140 compatible type.\n    ///\n    /// When `handle` refers to a [`TypeInner::Struct`] and any of its members\n    /// require a std140 compatible type declaration, this will declare a new\n    /// struct with the following rules:\n    /// * Struct or array members will be declared with their std140 compatible\n    ///   type declaration, if one is required.\n    /// * Two-row matrix members will have each of their columns hoisted\n    ///   directly into the struct as 2-component vector members.\n    /// * All other members will be declared with their normal type.\n    ///\n    /// Note that this means the Naga IR index of a struct member may not match\n    /// the index in the generated SPIR-V. The mapping can be obtained via\n    /// `Std140TypeInfo::member_indices`.\n    ///\n    /// [`GlobalVariable`]: crate::GlobalVariable\n    /// [`Uniform`]: crate::AddressSpace::Uniform\n    /// [`TypeInner::Matrix`]: crate::TypeInner::Matrix\n    /// [`TypeInner::Array`]: crate::TypeInner::Array\n    /// [`TypeInner::Struct`]: crate::TypeInner::Struct\n    fn write_std140_compat_type_declaration(\n        &mut self,\n        module: &crate::Module,\n        handle: Handle<crate::Type>,\n    ) -> Result<Option<Word>, Error> {\n        if let Some(std140_type_info) = self.std140_compat_uniform_types.get(&handle) {\n            return Ok(Some(std140_type_info.type_id));\n        }\n\n        let type_inner = &module.types[handle].inner;\n        let std140_type_id = match *type_inner {\n            crate::TypeInner::Matrix {\n                columns,\n                rows: rows @ crate::VectorSize::Bi,\n                scalar,\n            } => {\n                let std140_type_id = self.id_gen.next();\n                let mut member_type_ids: ArrayVec<Word, 4> = ArrayVec::new();\n                let column_type_id =\n                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });\n                for column in 0..columns as u32 {\n                    member_type_ids.push(column_type_id);\n                    self.annotations.push(Instruction::member_decorate(\n                        std140_type_id,\n                        column,\n                        spirv::Decoration::Offset,\n                        &[column * rows as u32 * scalar.width as u32],\n                    ));\n                    if self.flags.contains(WriterFlags::DEBUG) {\n                        self.debugs.push(Instruction::member_name(\n                            std140_type_id,\n                            column,\n                            &format!(\"col{column}\"),\n                        ));\n                    }\n                }\n                Instruction::type_struct(std140_type_id, &member_type_ids)\n                    .to_words(&mut self.logical_layout.declarations);\n                self.std140_compat_uniform_types.insert(\n                    handle,\n                    Std140CompatTypeInfo {\n                        type_id: std140_type_id,\n                        member_indices: Vec::new(),\n                    },\n                );\n                Some(std140_type_id)\n            }\n            crate::TypeInner::Array { base, size, stride } => {\n                match self.write_std140_compat_type_declaration(module, base)? {\n                    Some(std140_base_type_id) => {\n                        let std140_type_id = self.id_gen.next();\n                        self.decorate(std140_type_id, spirv::Decoration::ArrayStride, &[stride]);\n                        let instruction = match size.resolve(module.to_ctx())? {\n                            crate::proc::IndexableLength::Known(length) => {\n                                let length_id = self.get_index_constant(length);\n                                Instruction::type_array(\n                                    std140_type_id,\n                                    std140_base_type_id,\n                                    length_id,\n                                )\n                            }\n                            crate::proc::IndexableLength::Dynamic => {\n                                unreachable!()\n                            }\n                        };\n                        instruction.to_words(&mut self.logical_layout.declarations);\n                        self.std140_compat_uniform_types.insert(\n                            handle,\n                            Std140CompatTypeInfo {\n                                type_id: std140_type_id,\n                                member_indices: Vec::new(),\n                            },\n                        );\n                        Some(std140_type_id)\n                    }\n                    None => None,\n                }\n            }\n            crate::TypeInner::Struct { ref members, .. } => {\n                let mut needs_std140_type = false;\n                for member in members {\n                    match module.types[member.ty].inner {\n                        // We don't need to write a std140 type for the matrix itself as\n                        // it will be decomposed into the parent struct. As a result, the\n                        // struct does need a std140 type, however.\n                        crate::TypeInner::Matrix {\n                            rows: crate::VectorSize::Bi,\n                            ..\n                        } => needs_std140_type = true,\n                        // If an array member needs a std140 type, because it is an array\n                        // (of an array, etc) of `matCx2`s, then the struct also needs\n                        // a std140 type which uses the std140 type for this member.\n                        crate::TypeInner::Array { .. }\n                            if self\n                                .write_std140_compat_type_declaration(module, member.ty)?\n                                .is_some() =>\n                        {\n                            needs_std140_type = true;\n                        }\n                        _ => {}\n                    }\n                }\n\n                if needs_std140_type {\n                    let std140_type_id = self.id_gen.next();\n                    let mut member_ids = Vec::new();\n                    let mut member_indices = Vec::new();\n                    let mut next_index = 0;\n\n                    for member in members {\n                        member_indices.push(next_index);\n                        match module.types[member.ty].inner {\n                            crate::TypeInner::Matrix {\n                                columns,\n                                rows: rows @ crate::VectorSize::Bi,\n                                scalar,\n                            } => {\n                                let vector_type_id =\n                                    self.get_numeric_type_id(NumericType::Vector {\n                                        size: rows,\n                                        scalar,\n                                    });\n                                for column in 0..columns as u32 {\n                                    self.annotations.push(Instruction::member_decorate(\n                                        std140_type_id,\n                                        next_index,\n                                        spirv::Decoration::Offset,\n                                        &[member.offset\n                                            + column * rows as u32 * scalar.width as u32],\n                                    ));\n                                    if self.flags.contains(WriterFlags::DEBUG) {\n                                        if let Some(ref name) = member.name {\n                                            self.debugs.push(Instruction::member_name(\n                                                std140_type_id,\n                                                next_index,\n                                                &format!(\"{name}_col{column}\"),\n                                            ));\n                                        }\n                                    }\n                                    member_ids.push(vector_type_id);\n                                    next_index += 1;\n                                }\n                            }\n                            _ => {\n                                let member_id =\n                                    match self.std140_compat_uniform_types.get(&member.ty) {\n                                        Some(std140_member_type_info) => {\n                                            self.annotations.push(Instruction::member_decorate(\n                                                std140_type_id,\n                                                next_index,\n                                                spirv::Decoration::Offset,\n                                                &[member.offset],\n                                            ));\n                                            if self.flags.contains(WriterFlags::DEBUG) {\n                                                if let Some(ref name) = member.name {\n                                                    self.debugs.push(Instruction::member_name(\n                                                        std140_type_id,\n                                                        next_index,\n                                                        name,\n                                                    ));\n                                                }\n                                            }\n                                            std140_member_type_info.type_id\n                                        }\n                                        None => {\n                                            self.decorate_struct_member(\n                                                std140_type_id,\n                                                next_index as usize,\n                                                member,\n                                                &module.types,\n                                            )?;\n                                            self.get_handle_type_id(member.ty)\n                                        }\n                                    };\n                                member_ids.push(member_id);\n                                next_index += 1;\n                            }\n                        }\n                    }\n\n                    Instruction::type_struct(std140_type_id, &member_ids)\n                        .to_words(&mut self.logical_layout.declarations);\n                    self.std140_compat_uniform_types.insert(\n                        handle,\n                        Std140CompatTypeInfo {\n                            type_id: std140_type_id,\n                            member_indices,\n                        },\n                    );\n                    Some(std140_type_id)\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        };\n\n        if let Some(std140_type_id) = std140_type_id {\n            if self.flags.contains(WriterFlags::DEBUG) {\n                let name = format!(\"std140_{:?}\", handle.for_debug(&module.types));\n                self.debugs.push(Instruction::name(std140_type_id, &name));\n            }\n        }\n        Ok(std140_type_id)\n    }\n\n    fn request_image_format_capabilities(\n        &mut self,\n        format: spirv::ImageFormat,\n    ) -> Result<(), Error> {\n        use spirv::ImageFormat as If;\n        match format {\n            If::Rg32f\n            | If::Rg16f\n            | If::R11fG11fB10f\n            | If::R16f\n            | If::Rgba16\n            | If::Rgb10A2\n            | If::Rg16\n            | If::Rg8\n            | If::R16\n            | If::R8\n            | If::Rgba16Snorm\n            | If::Rg16Snorm\n            | If::Rg8Snorm\n            | If::R16Snorm\n            | If::R8Snorm\n            | If::Rg32i\n            | If::Rg16i\n            | If::Rg8i\n            | If::R16i\n            | If::R8i\n            | If::Rgb10a2ui\n            | If::Rg32ui\n            | If::Rg16ui\n            | If::Rg8ui\n            | If::R16ui\n            | If::R8ui => self.require_any(\n                \"storage image format\",\n                &[spirv::Capability::StorageImageExtendedFormats],\n            ),\n            If::R64ui | If::R64i => {\n                self.use_extension(\"SPV_EXT_shader_image_int64\");\n                self.require_any(\n                    \"64-bit integer storage image format\",\n                    &[spirv::Capability::Int64ImageEXT],\n                )\n            }\n            If::Unknown\n            | If::Rgba32f\n            | If::Rgba16f\n            | If::R32f\n            | If::Rgba8\n            | If::Rgba8Snorm\n            | If::Rgba32i\n            | If::Rgba16i\n            | If::Rgba8i\n            | If::R32i\n            | If::Rgba32ui\n            | If::Rgba16ui\n            | If::Rgba8ui\n            | If::R32ui => Ok(()),\n        }\n    }\n\n    pub(super) fn get_index_constant(&mut self, index: Word) -> Word {\n        self.get_constant_scalar(crate::Literal::U32(index))\n    }\n\n    pub(super) fn get_constant_scalar_with(\n        &mut self,\n        value: u8,\n        scalar: crate::Scalar,\n    ) -> Result<Word, Error> {\n        Ok(\n            self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(\n                Error::Validation(\"Unexpected kind and/or width for Literal\"),\n            )?),\n        )\n    }\n\n    pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {\n        let scalar = CachedConstant::Literal(value.into());\n        if let Some(&id) = self.cached_constants.get(&scalar) {\n            return id;\n        }\n        let id = self.id_gen.next();\n        self.write_constant_scalar(id, &value, None);\n        self.cached_constants.insert(scalar, id);\n        id\n    }\n\n    fn write_constant_scalar(\n        &mut self,\n        id: Word,\n        value: &crate::Literal,\n        debug_name: Option<&String>,\n    ) {\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(name) = debug_name {\n                self.debugs.push(Instruction::name(id, name));\n            }\n        }\n        let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));\n        let instruction = match *value {\n            crate::Literal::F64(value) => {\n                let bits = value.to_bits();\n                Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)\n            }\n            crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),\n            crate::Literal::F16(value) => {\n                let low = value.to_bits();\n                Instruction::constant_16bit(type_id, id, low as u32)\n            }\n            crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),\n            crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),\n            crate::Literal::U64(value) => {\n                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)\n            }\n            crate::Literal::I64(value) => {\n                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)\n            }\n            crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),\n            crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),\n            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {\n                unreachable!(\"Abstract types should not appear in IR presented to backends\");\n            }\n        };\n\n        instruction.to_words(&mut self.logical_layout.declarations);\n    }\n\n    pub(super) fn get_constant_composite(\n        &mut self,\n        ty: LookupType,\n        constituent_ids: &[Word],\n    ) -> Word {\n        let composite = CachedConstant::Composite {\n            ty,\n            constituent_ids: constituent_ids.to_vec(),\n        };\n        if let Some(&id) = self.cached_constants.get(&composite) {\n            return id;\n        }\n        let id = self.id_gen.next();\n        self.write_constant_composite(id, ty, constituent_ids, None);\n        self.cached_constants.insert(composite, id);\n        id\n    }\n\n    fn write_constant_composite(\n        &mut self,\n        id: Word,\n        ty: LookupType,\n        constituent_ids: &[Word],\n        debug_name: Option<&String>,\n    ) {\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(name) = debug_name {\n                self.debugs.push(Instruction::name(id, name));\n            }\n        }\n        let type_id = self.get_type_id(ty);\n        Instruction::constant_composite(type_id, id, constituent_ids)\n            .to_words(&mut self.logical_layout.declarations);\n    }\n\n    pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {\n        let null = CachedConstant::ZeroValue(type_id);\n        if let Some(&id) = self.cached_constants.get(&null) {\n            return id;\n        }\n        let id = self.write_constant_null(type_id);\n        self.cached_constants.insert(null, id);\n        id\n    }\n\n    pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {\n        let null_id = self.id_gen.next();\n        Instruction::constant_null(type_id, null_id)\n            .to_words(&mut self.logical_layout.declarations);\n        null_id\n    }\n\n    fn write_constant_expr(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        ir_module: &crate::Module,\n        mod_info: &ModuleInfo,\n    ) -> Result<Word, Error> {\n        let id = match ir_module.global_expressions[handle] {\n            crate::Expression::Literal(literal) => self.get_constant_scalar(literal),\n            crate::Expression::Constant(constant) => {\n                let constant = &ir_module.constants[constant];\n                self.constant_ids[constant.init]\n            }\n            crate::Expression::ZeroValue(ty) => {\n                let type_id = self.get_handle_type_id(ty);\n                self.get_constant_null(type_id)\n            }\n            crate::Expression::Compose { ty, ref components } => {\n                let component_ids: Vec<_> = crate::proc::flatten_compose(\n                    ty,\n                    components,\n                    &ir_module.global_expressions,\n                    &ir_module.types,\n                )\n                .map(|component| self.constant_ids[component])\n                .collect();\n                self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())\n            }\n            crate::Expression::Splat { size, value } => {\n                let value_id = self.constant_ids[value];\n                let component_ids = &[value_id; 4][..size as usize];\n\n                let ty = self.get_expression_lookup_type(&mod_info[handle]);\n\n                self.get_constant_composite(ty, component_ids)\n            }\n            _ => {\n                return Err(Error::Override);\n            }\n        };\n\n        self.constant_ids[handle] = id;\n\n        Ok(id)\n    }\n\n    pub(super) fn write_control_barrier(\n        &mut self,\n        flags: crate::Barrier,\n        body: &mut Vec<Instruction>,\n    ) {\n        let memory_scope = if flags.contains(crate::Barrier::STORAGE) {\n            spirv::Scope::Device\n        } else if flags.contains(crate::Barrier::SUB_GROUP) {\n            spirv::Scope::Subgroup\n        } else {\n            spirv::Scope::Workgroup\n        };\n        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;\n        semantics.set(\n            spirv::MemorySemantics::UNIFORM_MEMORY,\n            flags.contains(crate::Barrier::STORAGE),\n        );\n        semantics.set(\n            spirv::MemorySemantics::WORKGROUP_MEMORY,\n            flags.contains(crate::Barrier::WORK_GROUP),\n        );\n        semantics.set(\n            spirv::MemorySemantics::SUBGROUP_MEMORY,\n            flags.contains(crate::Barrier::SUB_GROUP),\n        );\n        semantics.set(\n            spirv::MemorySemantics::IMAGE_MEMORY,\n            flags.contains(crate::Barrier::TEXTURE),\n        );\n        let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {\n            self.get_index_constant(spirv::Scope::Subgroup as u32)\n        } else {\n            self.get_index_constant(spirv::Scope::Workgroup as u32)\n        };\n        let mem_scope_id = self.get_index_constant(memory_scope as u32);\n        let semantics_id = self.get_index_constant(semantics.bits());\n        body.push(Instruction::control_barrier(\n            exec_scope_id,\n            mem_scope_id,\n            semantics_id,\n        ));\n    }\n\n    pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {\n        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;\n        semantics.set(\n            spirv::MemorySemantics::UNIFORM_MEMORY,\n            flags.contains(crate::Barrier::STORAGE),\n        );\n        semantics.set(\n            spirv::MemorySemantics::WORKGROUP_MEMORY,\n            flags.contains(crate::Barrier::WORK_GROUP),\n        );\n        semantics.set(\n            spirv::MemorySemantics::SUBGROUP_MEMORY,\n            flags.contains(crate::Barrier::SUB_GROUP),\n        );\n        semantics.set(\n            spirv::MemorySemantics::IMAGE_MEMORY,\n            flags.contains(crate::Barrier::TEXTURE),\n        );\n        let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {\n            self.get_index_constant(spirv::Scope::Device as u32)\n        } else if flags.contains(crate::Barrier::SUB_GROUP) {\n            self.get_index_constant(spirv::Scope::Subgroup as u32)\n        } else {\n            self.get_index_constant(spirv::Scope::Workgroup as u32)\n        };\n        let semantics_id = self.get_index_constant(semantics.bits());\n        block\n            .body\n            .push(Instruction::memory_barrier(mem_scope_id, semantics_id));\n    }\n\n    fn generate_workgroup_vars_init_block(\n        &mut self,\n        entry_id: Word,\n        ir_module: &crate::Module,\n        info: &FunctionInfo,\n        local_invocation_index: Option<Word>,\n        interface: &mut FunctionInterface,\n        function: &mut Function,\n    ) -> Option<Word> {\n        let body = ir_module\n            .global_variables\n            .iter()\n            .filter(|&(handle, var)| {\n                let task_exception = (var.space == crate::AddressSpace::TaskPayload)\n                    && interface.stage == crate::ShaderStage::Task;\n                !info[handle].is_empty()\n                    && (var.space == crate::AddressSpace::WorkGroup || task_exception)\n            })\n            .map(|(handle, var)| {\n                // It's safe to use `var_id` here, not `access_id`, because only\n                // variables in the `Uniform` and `StorageBuffer` address spaces\n                // get wrapped, and we're initializing `WorkGroup` variables.\n                let var_id = self.global_variables[handle].var_id;\n                let var_type_id = self.get_handle_type_id(var.ty);\n                let init_word = self.get_constant_null(var_type_id);\n                Instruction::store(var_id, init_word, None)\n            })\n            .collect::<Vec<_>>();\n\n        if body.is_empty() {\n            return None;\n        }\n\n        let mut pre_if_block = Block::new(entry_id);\n\n        let local_invocation_index = if let Some(local_invocation_index) = local_invocation_index {\n            local_invocation_index\n        } else {\n            let varying_id = self.id_gen.next();\n            let class = spirv::StorageClass::Input;\n            let u32_ty_id = self.get_u32_type_id();\n            let pointer_type_id = self.get_pointer_type_id(u32_ty_id, class);\n\n            Instruction::variable(pointer_type_id, varying_id, class, None)\n                .to_words(&mut self.logical_layout.declarations);\n\n            self.decorate(\n                varying_id,\n                spirv::Decoration::BuiltIn,\n                &[spirv::BuiltIn::LocalInvocationIndex as u32],\n            );\n\n            interface.varying_ids.push(varying_id);\n            let id = self.id_gen.next();\n            pre_if_block\n                .body\n                .push(Instruction::load(u32_ty_id, id, varying_id, None));\n\n            id\n        };\n\n        let zero_id = self.get_constant_scalar(crate::Literal::U32(0));\n\n        let eq_id = self.id_gen.next();\n        pre_if_block.body.push(Instruction::binary(\n            spirv::Op::IEqual,\n            self.get_bool_type_id(),\n            eq_id,\n            local_invocation_index,\n            zero_id,\n        ));\n\n        let merge_id = self.id_gen.next();\n        pre_if_block.body.push(Instruction::selection_merge(\n            merge_id,\n            spirv::SelectionControl::NONE,\n        ));\n\n        let accept_id = self.id_gen.next();\n        function.consume(\n            pre_if_block,\n            Instruction::branch_conditional(eq_id, accept_id, merge_id),\n        );\n\n        let accept_block = Block {\n            label_id: accept_id,\n            body,\n        };\n        function.consume(accept_block, Instruction::branch(merge_id));\n\n        let mut post_if_block = Block::new(merge_id);\n\n        self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block.body);\n\n        let next_id = self.id_gen.next();\n        function.consume(post_if_block, Instruction::branch(next_id));\n        Some(next_id)\n    }\n\n    /// Generate an `OpVariable` for one value in an [`EntryPoint`]'s IO interface.\n    ///\n    /// The [`Binding`]s of the arguments and result of an [`EntryPoint`]'s\n    /// [`Function`] describe a SPIR-V shader interface. In SPIR-V, the\n    /// interface is represented by global variables in the `Input` and `Output`\n    /// storage classes, with decorations indicating which builtin or location\n    /// each variable corresponds to.\n    ///\n    /// This function emits a single global `OpVariable` for a single value from\n    /// the interface, and adds appropriate decorations to indicate which\n    /// builtin or location it represents, how it should be interpolated, and so\n    /// on. The `class` argument gives the variable's SPIR-V storage class,\n    /// which should be either [`Input`] or [`Output`].\n    ///\n    /// [`Binding`]: crate::Binding\n    /// [`Function`]: crate::Function\n    /// [`EntryPoint`]: crate::EntryPoint\n    /// [`Input`]: spirv::StorageClass::Input\n    /// [`Output`]: spirv::StorageClass::Output\n    fn write_varying(\n        &mut self,\n        ir_module: &crate::Module,\n        stage: crate::ShaderStage,\n        class: spirv::StorageClass,\n        debug_name: Option<&str>,\n        ty: Handle<crate::Type>,\n        binding: &crate::Binding,\n    ) -> Result<Word, Error> {\n        let id = self.id_gen.next();\n        let ty_inner = &ir_module.types[ty].inner;\n        let needs_polyfill = self.needs_f16_polyfill(ty_inner);\n\n        let pointer_type_id = if needs_polyfill {\n            let f32_value_local =\n                super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)\n                    .expect(\"needs_polyfill returned true but create_polyfill_type returned None\");\n\n            let f32_type_id = self.get_localtype_id(f32_value_local);\n            let ptr_id = self.get_pointer_type_id(f32_type_id, class);\n            self.io_f16_polyfills.register_io_var(id, f32_type_id);\n\n            ptr_id\n        } else {\n            self.get_handle_pointer_type_id(ty, class)\n        };\n\n        Instruction::variable(pointer_type_id, id, class, None)\n            .to_words(&mut self.logical_layout.declarations);\n\n        if self\n            .flags\n            .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)\n        {\n            if let Some(name) = debug_name {\n                self.debugs.push(Instruction::name(id, name));\n            }\n        }\n\n        let binding = self.map_binding(ir_module, stage, class, ty, binding)?;\n        self.write_binding(id, binding);\n\n        Ok(id)\n    }\n\n    pub fn write_binding(&mut self, id: Word, binding: BindingDecorations) {\n        match binding {\n            BindingDecorations::None => (),\n            BindingDecorations::BuiltIn(bi, others) => {\n                self.decorate(id, spirv::Decoration::BuiltIn, &[bi as u32]);\n                for other in others {\n                    self.decorate(id, other, &[]);\n                }\n            }\n            BindingDecorations::Location {\n                location,\n                others,\n                blend_src,\n            } => {\n                self.decorate(id, spirv::Decoration::Location, &[location]);\n                for other in others {\n                    self.decorate(id, other, &[]);\n                }\n                if let Some(blend_src) = blend_src {\n                    self.decorate(id, spirv::Decoration::Index, &[blend_src]);\n                }\n            }\n        }\n    }\n\n    pub fn write_binding_struct_member(\n        &mut self,\n        struct_id: Word,\n        member_idx: Word,\n        binding_info: BindingDecorations,\n    ) {\n        match binding_info {\n            BindingDecorations::None => (),\n            BindingDecorations::BuiltIn(bi, others) => {\n                self.annotations.push(Instruction::member_decorate(\n                    struct_id,\n                    member_idx,\n                    spirv::Decoration::BuiltIn,\n                    &[bi as Word],\n                ));\n                for other in others {\n                    self.annotations.push(Instruction::member_decorate(\n                        struct_id,\n                        member_idx,\n                        other,\n                        &[],\n                    ));\n                }\n            }\n            BindingDecorations::Location {\n                location,\n                others,\n                blend_src,\n            } => {\n                self.annotations.push(Instruction::member_decorate(\n                    struct_id,\n                    member_idx,\n                    spirv::Decoration::Location,\n                    &[location],\n                ));\n                for other in others {\n                    self.annotations.push(Instruction::member_decorate(\n                        struct_id,\n                        member_idx,\n                        other,\n                        &[],\n                    ));\n                }\n                if let Some(blend_src) = blend_src {\n                    self.annotations.push(Instruction::member_decorate(\n                        struct_id,\n                        member_idx,\n                        spirv::Decoration::Index,\n                        &[blend_src],\n                    ));\n                }\n            }\n        }\n    }\n\n    pub fn map_binding(\n        &mut self,\n        ir_module: &crate::Module,\n        stage: crate::ShaderStage,\n        class: spirv::StorageClass,\n        ty: Handle<crate::Type>,\n        binding: &crate::Binding,\n    ) -> Result<BindingDecorations, Error> {\n        use spirv::BuiltIn;\n        use spirv::Decoration;\n        match *binding {\n            crate::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src,\n                per_primitive,\n            } => {\n                let mut others = ArrayVec::new();\n\n                let no_decorations =\n                    // VUID-StandaloneSpirv-Flat-06202\n                    // > The Flat, NoPerspective, Sample, and Centroid decorations\n                    // > must not be used on variables with the Input storage class in a vertex shader\n                    (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||\n                    // VUID-StandaloneSpirv-Flat-06201\n                    // > The Flat, NoPerspective, Sample, and Centroid decorations\n                    // > must not be used on variables with the Output storage class in a fragment shader\n                    (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);\n\n                if !no_decorations {\n                    match interpolation {\n                        // Perspective-correct interpolation is the default in SPIR-V.\n                        None | Some(crate::Interpolation::Perspective) => (),\n                        Some(crate::Interpolation::Flat) => {\n                            others.push(Decoration::Flat);\n                        }\n                        Some(crate::Interpolation::Linear) => {\n                            others.push(Decoration::NoPerspective);\n                        }\n                        Some(crate::Interpolation::PerVertex) => {\n                            others.push(Decoration::PerVertexKHR);\n                            self.require_any(\n                                \"`per_vertex` interpolation\",\n                                &[spirv::Capability::FragmentBarycentricKHR],\n                            )?;\n                            self.use_extension(\"SPV_KHR_fragment_shader_barycentric\");\n                        }\n                    }\n                    match sampling {\n                        // Center sampling is the default in SPIR-V.\n                        None\n                        | Some(\n                            crate::Sampling::Center\n                            | crate::Sampling::First\n                            | crate::Sampling::Either,\n                        ) => (),\n                        Some(crate::Sampling::Centroid) => {\n                            others.push(Decoration::Centroid);\n                        }\n                        Some(crate::Sampling::Sample) => {\n                            self.require_any(\n                                \"per-sample interpolation\",\n                                &[spirv::Capability::SampleRateShading],\n                            )?;\n                            others.push(Decoration::Sample);\n                        }\n                    }\n                }\n                if per_primitive && stage == crate::ShaderStage::Fragment {\n                    others.push(Decoration::PerPrimitiveEXT);\n                }\n                Ok(BindingDecorations::Location {\n                    location,\n                    others,\n                    blend_src,\n                })\n            }\n            crate::Binding::BuiltIn(built_in) => {\n                use crate::BuiltIn as Bi;\n                let mut others = ArrayVec::new();\n\n                let built_in = match built_in {\n                    Bi::Position { invariant } => {\n                        if invariant {\n                            others.push(Decoration::Invariant);\n                        }\n\n                        if class == spirv::StorageClass::Output {\n                            BuiltIn::Position\n                        } else {\n                            BuiltIn::FragCoord\n                        }\n                    }\n                    Bi::ViewIndex => {\n                        self.require_any(\"`view_index` built-in\", &[spirv::Capability::MultiView])?;\n                        BuiltIn::ViewIndex\n                    }\n                    // vertex\n                    Bi::BaseInstance => BuiltIn::BaseInstance,\n                    Bi::BaseVertex => BuiltIn::BaseVertex,\n                    Bi::ClipDistances => {\n                        self.require_any(\n                            \"`clip_distances` built-in\",\n                            &[spirv::Capability::ClipDistance],\n                        )?;\n                        BuiltIn::ClipDistance\n                    }\n                    Bi::CullDistance => {\n                        self.require_any(\n                            \"`cull_distance` built-in\",\n                            &[spirv::Capability::CullDistance],\n                        )?;\n                        BuiltIn::CullDistance\n                    }\n                    Bi::InstanceIndex => BuiltIn::InstanceIndex,\n                    Bi::PointSize => BuiltIn::PointSize,\n                    Bi::VertexIndex => BuiltIn::VertexIndex,\n                    Bi::DrawIndex => {\n                        self.use_extension(\"SPV_KHR_shader_draw_parameters\");\n                        self.require_any(\n                            \"`draw_index built-in\",\n                            &[spirv::Capability::DrawParameters],\n                        )?;\n                        BuiltIn::DrawIndex\n                    }\n                    // fragment\n                    Bi::FragDepth => BuiltIn::FragDepth,\n                    Bi::PointCoord => BuiltIn::PointCoord,\n                    Bi::FrontFacing => BuiltIn::FrontFacing,\n                    Bi::PrimitiveIndex => {\n                        // Geometry shader capability is required for primitive index\n                        self.require_any(\n                            \"`primitive_index` built-in\",\n                            &[spirv::Capability::Geometry],\n                        )?;\n                        if stage == crate::ShaderStage::Mesh {\n                            others.push(Decoration::PerPrimitiveEXT);\n                        }\n                        BuiltIn::PrimitiveId\n                    }\n                    Bi::Barycentric { perspective } => {\n                        self.require_any(\n                            \"`barycentric` built-in\",\n                            &[spirv::Capability::FragmentBarycentricKHR],\n                        )?;\n                        self.use_extension(\"SPV_KHR_fragment_shader_barycentric\");\n                        if perspective {\n                            BuiltIn::BaryCoordKHR\n                        } else {\n                            BuiltIn::BaryCoordNoPerspKHR\n                        }\n                    }\n                    Bi::SampleIndex => {\n                        self.require_any(\n                            \"`sample_index` built-in\",\n                            &[spirv::Capability::SampleRateShading],\n                        )?;\n\n                        BuiltIn::SampleId\n                    }\n                    Bi::SampleMask => BuiltIn::SampleMask,\n                    // compute\n                    Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,\n                    Bi::LocalInvocationId => BuiltIn::LocalInvocationId,\n                    Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,\n                    Bi::WorkGroupId => BuiltIn::WorkgroupId,\n                    Bi::WorkGroupSize => BuiltIn::WorkgroupSize,\n                    Bi::NumWorkGroups => BuiltIn::NumWorkgroups,\n                    // Subgroup\n                    Bi::NumSubgroups => {\n                        self.require_any(\n                            \"`num_subgroups` built-in\",\n                            &[spirv::Capability::GroupNonUniform],\n                        )?;\n                        BuiltIn::NumSubgroups\n                    }\n                    Bi::SubgroupId => {\n                        self.require_any(\n                            \"`subgroup_id` built-in\",\n                            &[spirv::Capability::GroupNonUniform],\n                        )?;\n                        BuiltIn::SubgroupId\n                    }\n                    Bi::SubgroupSize => {\n                        self.require_any(\n                            \"`subgroup_size` built-in\",\n                            &[\n                                spirv::Capability::GroupNonUniform,\n                                spirv::Capability::SubgroupBallotKHR,\n                            ],\n                        )?;\n                        BuiltIn::SubgroupSize\n                    }\n                    Bi::SubgroupInvocationId => {\n                        self.require_any(\n                            \"`subgroup_invocation_id` built-in\",\n                            &[\n                                spirv::Capability::GroupNonUniform,\n                                spirv::Capability::SubgroupBallotKHR,\n                            ],\n                        )?;\n                        BuiltIn::SubgroupLocalInvocationId\n                    }\n                    Bi::CullPrimitive => {\n                        others.push(Decoration::PerPrimitiveEXT);\n                        BuiltIn::CullPrimitiveEXT\n                    }\n                    Bi::PointIndex => BuiltIn::PrimitivePointIndicesEXT,\n                    Bi::LineIndices => BuiltIn::PrimitiveLineIndicesEXT,\n                    Bi::TriangleIndices => BuiltIn::PrimitiveTriangleIndicesEXT,\n                    // No decoration, this EmitMeshTasksEXT is called at function return\n                    Bi::MeshTaskSize => return Ok(BindingDecorations::None),\n                    // These aren't normal builtins and don't occur in function output\n                    Bi::VertexCount | Bi::Vertices | Bi::PrimitiveCount | Bi::Primitives => {\n                        unreachable!()\n                    }\n                    Bi::RayInvocationId\n                    | Bi::NumRayInvocations\n                    | Bi::InstanceCustomData\n                    | Bi::GeometryIndex\n                    | Bi::WorldRayOrigin\n                    | Bi::WorldRayDirection\n                    | Bi::ObjectRayOrigin\n                    | Bi::ObjectRayDirection\n                    | Bi::RayTmin\n                    | Bi::RayTCurrentMax\n                    | Bi::ObjectToWorld\n                    | Bi::WorldToObject\n                    | Bi::HitKind => unreachable!(),\n                };\n\n                use crate::ScalarKind as Sk;\n\n                // Per the Vulkan spec, `VUID-StandaloneSpirv-Flat-04744`:\n                //\n                // > Any variable with integer or double-precision floating-\n                // > point type and with Input storage class in a fragment\n                // > shader, must be decorated Flat\n                if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {\n                    let is_flat = match ir_module.types[ty].inner {\n                        crate::TypeInner::Scalar(scalar)\n                        | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {\n                            Sk::Uint | Sk::Sint | Sk::Bool => true,\n                            Sk::Float => false,\n                            Sk::AbstractInt | Sk::AbstractFloat => {\n                                return Err(Error::Validation(\n                                    \"Abstract types should not appear in IR presented to backends\",\n                                ))\n                            }\n                        },\n                        _ => false,\n                    };\n\n                    if is_flat {\n                        others.push(Decoration::Flat);\n                    }\n                }\n                Ok(BindingDecorations::BuiltIn(built_in, others))\n            }\n        }\n    }\n\n    /// Load an IO variable, converting from `f32` to `f16` if polyfill is active.\n    /// Returns the id of the loaded value matching `target_type_id`.\n    pub(super) fn load_io_with_f16_polyfill(\n        &mut self,\n        body: &mut Vec<Instruction>,\n        varying_id: Word,\n        target_type_id: Word,\n    ) -> Word {\n        let tmp = self.id_gen.next();\n        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {\n            body.push(Instruction::load(f32_ty, tmp, varying_id, None));\n            let converted = self.id_gen.next();\n            super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(\n                tmp,\n                target_type_id,\n                converted,\n                body,\n            );\n            converted\n        } else {\n            body.push(Instruction::load(target_type_id, tmp, varying_id, None));\n            tmp\n        }\n    }\n\n    /// Store an IO variable, converting from `f16` to `f32` if polyfill is active.\n    pub(super) fn store_io_with_f16_polyfill(\n        &mut self,\n        body: &mut Vec<Instruction>,\n        varying_id: Word,\n        value_id: Word,\n    ) {\n        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {\n            let converted = self.id_gen.next();\n            super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(\n                value_id, f32_ty, converted, body,\n            );\n            body.push(Instruction::store(varying_id, converted, None));\n        } else {\n            body.push(Instruction::store(varying_id, value_id, None));\n        }\n    }\n\n    fn write_global_variable(\n        &mut self,\n        ir_module: &crate::Module,\n        global_variable: &crate::GlobalVariable,\n    ) -> Result<Word, Error> {\n        use spirv::Decoration;\n\n        let id = self.id_gen.next();\n        let class = map_storage_class(global_variable.space);\n\n        //self.check(class.required_capabilities())?;\n\n        if global_variable\n            .memory_decorations\n            .contains(crate::MemoryDecorations::COHERENT)\n        {\n            self.decorate(id, Decoration::Coherent, &[]);\n        }\n        if global_variable\n            .memory_decorations\n            .contains(crate::MemoryDecorations::VOLATILE)\n        {\n            self.decorate(id, Decoration::Volatile, &[]);\n        }\n\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(ref name) = global_variable.name {\n                self.debugs.push(Instruction::name(id, name));\n            }\n        }\n\n        let storage_access = match global_variable.space {\n            crate::AddressSpace::Storage { access } => Some(access),\n            _ => match ir_module.types[global_variable.ty].inner {\n                crate::TypeInner::Image {\n                    class: crate::ImageClass::Storage { access, .. },\n                    ..\n                } => Some(access),\n                _ => None,\n            },\n        };\n        if let Some(storage_access) = storage_access {\n            if !storage_access.contains(crate::StorageAccess::LOAD) {\n                self.decorate(id, Decoration::NonReadable, &[]);\n            }\n            if !storage_access.contains(crate::StorageAccess::STORE) {\n                self.decorate(id, Decoration::NonWritable, &[]);\n            }\n        }\n\n        // Note: we should be able to substitute `binding_array<Foo, 0>`,\n        // but there is still code that tries to register the pre-substituted type,\n        // and it is failing on 0.\n        let mut substitute_inner_type_lookup = None;\n        if let Some(ref res_binding) = global_variable.binding {\n            let bind_target = self.resolve_resource_binding(res_binding)?;\n            self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);\n            self.decorate(id, Decoration::Binding, &[bind_target.binding]);\n\n            if let Some(remapped_binding_array_size) = bind_target.binding_array_size {\n                if let crate::TypeInner::BindingArray { base, .. } =\n                    ir_module.types[global_variable.ty].inner\n                {\n                    let binding_array_type_id =\n                        self.get_type_id(LookupType::Local(LocalType::BindingArray {\n                            base,\n                            size: remapped_binding_array_size,\n                        }));\n                    substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {\n                        base: binding_array_type_id,\n                        class,\n                    }));\n                }\n            }\n        };\n\n        let init_word = global_variable\n            .init\n            .map(|constant| self.constant_ids[constant]);\n        let inner_type_id = self.get_type_id(\n            substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),\n        );\n\n        // generate the wrapping structure if needed\n        let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {\n            let wrapper_type_id = self.id_gen.next();\n\n            self.decorate(wrapper_type_id, Decoration::Block, &[]);\n\n            match self.std140_compat_uniform_types.get(&global_variable.ty) {\n                Some(std140_type_info) if global_variable.space == crate::AddressSpace::Uniform => {\n                    self.annotations.push(Instruction::member_decorate(\n                        wrapper_type_id,\n                        0,\n                        Decoration::Offset,\n                        &[0],\n                    ));\n                    Instruction::type_struct(wrapper_type_id, &[std140_type_info.type_id])\n                        .to_words(&mut self.logical_layout.declarations);\n                }\n                _ => {\n                    let member = crate::StructMember {\n                        name: None,\n                        ty: global_variable.ty,\n                        binding: None,\n                        offset: 0,\n                    };\n                    self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;\n\n                    Instruction::type_struct(wrapper_type_id, &[inner_type_id])\n                        .to_words(&mut self.logical_layout.declarations);\n                }\n            }\n\n            let pointer_type_id = self.id_gen.next();\n            Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)\n                .to_words(&mut self.logical_layout.declarations);\n\n            pointer_type_id\n        } else {\n            // This is a global variable in the Storage address space. The only\n            // way it could have `global_needs_wrapper() == false` is if it has\n            // a runtime-sized or binding array.\n            // Runtime-sized arrays were decorated when iterating through struct content.\n            // Now binding arrays require Block decorating.\n            if let crate::AddressSpace::Storage { .. } = global_variable.space {\n                match ir_module.types[global_variable.ty].inner {\n                    crate::TypeInner::BindingArray { base, .. } => {\n                        let ty = &ir_module.types[base];\n                        let mut should_decorate = true;\n                        // Check if the type has a runtime array.\n                        // A normal runtime array gets validated out,\n                        // so only structs can be with runtime arrays\n                        if let crate::TypeInner::Struct { ref members, .. } = ty.inner {\n                            // only the last member in a struct can be dynamically sized\n                            if let Some(last_member) = members.last() {\n                                if let &crate::TypeInner::Array {\n                                    size: crate::ArraySize::Dynamic,\n                                    ..\n                                } = &ir_module.types[last_member.ty].inner\n                                {\n                                    should_decorate = false;\n                                }\n                            }\n                        }\n                        if should_decorate {\n                            let decorated_id = self.get_handle_type_id(base);\n                            self.decorate(decorated_id, Decoration::Block, &[]);\n                        }\n                    }\n                    _ => (),\n                };\n            }\n            if substitute_inner_type_lookup.is_some() {\n                inner_type_id\n            } else {\n                self.get_handle_pointer_type_id(global_variable.ty, class)\n            }\n        };\n\n        let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {\n            (crate::AddressSpace::Private, _)\n            | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {\n                init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))\n            }\n            _ => init_word,\n        };\n\n        Instruction::variable(pointer_type_id, id, class, init_word)\n            .to_words(&mut self.logical_layout.declarations);\n        Ok(id)\n    }\n\n    /// Write the necessary decorations for a struct member.\n    ///\n    /// Emit decorations for the `index`'th member of the struct type\n    /// designated by `struct_id`, described by `member`.\n    fn decorate_struct_member(\n        &mut self,\n        struct_id: Word,\n        index: usize,\n        member: &crate::StructMember,\n        arena: &UniqueArena<crate::Type>,\n    ) -> Result<(), Error> {\n        use spirv::Decoration;\n\n        self.annotations.push(Instruction::member_decorate(\n            struct_id,\n            index as u32,\n            Decoration::Offset,\n            &[member.offset],\n        ));\n\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(ref name) = member.name {\n                self.debugs\n                    .push(Instruction::member_name(struct_id, index as u32, name));\n            }\n        }\n\n        // Matrices and (potentially nested) arrays of matrices both require decorations,\n        // so \"see through\" any arrays to determine if they're needed.\n        let mut member_array_subty_inner = &arena[member.ty].inner;\n        while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {\n            member_array_subty_inner = &arena[base].inner;\n        }\n\n        if let crate::TypeInner::Matrix {\n            columns: _,\n            rows,\n            scalar,\n        } = *member_array_subty_inner\n        {\n            let byte_stride = Alignment::from(rows) * scalar.width as u32;\n            self.annotations.push(Instruction::member_decorate(\n                struct_id,\n                index as u32,\n                Decoration::ColMajor,\n                &[],\n            ));\n            self.annotations.push(Instruction::member_decorate(\n                struct_id,\n                index as u32,\n                Decoration::MatrixStride,\n                &[byte_stride],\n            ));\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {\n        match self\n            .lookup_function_type\n            .entry(lookup_function_type.clone())\n        {\n            Entry::Occupied(e) => *e.get(),\n            Entry::Vacant(_) => {\n                let id = self.id_gen.next();\n                let instruction = Instruction::type_function(\n                    id,\n                    lookup_function_type.return_type_id,\n                    &lookup_function_type.parameter_type_ids,\n                );\n                instruction.to_words(&mut self.logical_layout.declarations);\n                self.lookup_function_type.insert(lookup_function_type, id);\n                id\n            }\n        }\n    }\n\n    const fn write_physical_layout(&mut self) {\n        self.physical_layout.bound = self.id_gen.0 + 1;\n    }\n\n    fn write_logical_layout(\n        &mut self,\n        ir_module: &crate::Module,\n        mod_info: &ModuleInfo,\n        ep_index: Option<usize>,\n        debug_info: &Option<DebugInfo>,\n    ) -> Result<(), Error> {\n        fn has_view_index_check(\n            ir_module: &crate::Module,\n            binding: Option<&crate::Binding>,\n            ty: Handle<crate::Type>,\n        ) -> bool {\n            match ir_module.types[ty].inner {\n                crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {\n                    has_view_index_check(ir_module, member.binding.as_ref(), member.ty)\n                }),\n                _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),\n            }\n        }\n\n        let has_storage_buffers =\n            ir_module\n                .global_variables\n                .iter()\n                .any(|(_, var)| match var.space {\n                    crate::AddressSpace::Storage { .. } => true,\n                    _ => false,\n                });\n        let has_view_index = ir_module\n            .entry_points\n            .iter()\n            .flat_map(|entry| entry.function.arguments.iter())\n            .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));\n        let mut has_ray_query = ir_module.special_types.ray_desc.is_some()\n            | ir_module.special_types.ray_intersection.is_some();\n        let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();\n\n        for (_, &crate::Type { ref inner, .. }) in ir_module.types.iter() {\n            // spirv does not know whether these have vertex return - that is done by us\n            if let &crate::TypeInner::AccelerationStructure { .. }\n            | &crate::TypeInner::RayQuery { .. } = inner\n            {\n                has_ray_query = true\n            }\n        }\n\n        if self.physical_layout.version < 0x10300 && has_storage_buffers {\n            // enable the storage buffer class on < SPV-1.3\n            Instruction::extension(\"SPV_KHR_storage_buffer_storage_class\")\n                .to_words(&mut self.logical_layout.extensions);\n        }\n        if has_view_index {\n            Instruction::extension(\"SPV_KHR_multiview\")\n                .to_words(&mut self.logical_layout.extensions)\n        }\n        if has_ray_query {\n            Instruction::extension(\"SPV_KHR_ray_query\")\n                .to_words(&mut self.logical_layout.extensions)\n        }\n        if has_vertex_return {\n            Instruction::extension(\"SPV_KHR_ray_tracing_position_fetch\")\n                .to_words(&mut self.logical_layout.extensions);\n        }\n        if ir_module.uses_mesh_shaders() {\n            self.use_extension(\"SPV_EXT_mesh_shader\");\n            self.require_any(\"Mesh Shaders\", &[spirv::Capability::MeshShadingEXT])?;\n            let lang_version = self.lang_version();\n            if lang_version.0 <= 1 && lang_version.1 < 4 {\n                return Err(Error::SpirvVersionTooLow(1, 4));\n            }\n        }\n        Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);\n        Instruction::ext_inst_import(self.gl450_ext_inst_id, \"GLSL.std.450\")\n            .to_words(&mut self.logical_layout.ext_inst_imports);\n\n        let mut debug_info_inner = None;\n        if self.flags.contains(WriterFlags::DEBUG) {\n            if let Some(debug_info) = debug_info.as_ref() {\n                let source_file_id = self.id_gen.next();\n                self.debugs\n                    .push(Instruction::string(debug_info.file_name, source_file_id));\n\n                debug_info_inner = Some(DebugInfoInner {\n                    source_code: debug_info.source_code,\n                    source_file_id,\n                });\n                self.debugs.append(&mut Instruction::source_auto_continued(\n                    debug_info.language,\n                    0,\n                    &debug_info_inner,\n                ));\n            }\n        }\n\n        // write all types\n        for (handle, _) in ir_module.types.iter() {\n            self.write_type_declaration_arena(ir_module, handle)?;\n        }\n\n        // write std140 layout compatible types required by uniforms\n        for (_, var) in ir_module.global_variables.iter() {\n            if var.space == crate::AddressSpace::Uniform {\n                self.write_std140_compat_type_declaration(ir_module, var.ty)?;\n            }\n        }\n\n        // write all const-expressions as constants\n        self.constant_ids\n            .resize(ir_module.global_expressions.len(), 0);\n        for (handle, _) in ir_module.global_expressions.iter() {\n            self.write_constant_expr(handle, ir_module, mod_info)?;\n        }\n        debug_assert!(self.constant_ids.iter().all(|&id| id != 0));\n\n        // write the name of constants on their respective const-expression initializer\n        if self.flags.contains(WriterFlags::DEBUG) {\n            for (_, constant) in ir_module.constants.iter() {\n                if let Some(ref name) = constant.name {\n                    let id = self.constant_ids[constant.init];\n                    self.debugs.push(Instruction::name(id, name));\n                }\n            }\n        }\n\n        // write all global variables\n        for (handle, var) in ir_module.global_variables.iter() {\n            // If a single entry point was specified, only write `OpVariable` instructions\n            // for the globals it actually uses. Emit dummies for the others,\n            // to preserve the indices in `global_variables`.\n            let gvar = match ep_index {\n                Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {\n                    GlobalVariable::dummy()\n                }\n                _ => {\n                    let id = self.write_global_variable(ir_module, var)?;\n                    GlobalVariable::new(id)\n                }\n            };\n            self.global_variables.insert(handle, gvar);\n        }\n\n        // write all functions\n        for (handle, ir_function) in ir_module.functions.iter() {\n            let info = &mod_info[handle];\n            if let Some(index) = ep_index {\n                let ep_info = mod_info.get_entry_point(index);\n                // If this function uses globals that we omitted from the SPIR-V\n                // because the entry point and its callees didn't use them,\n                // then we must skip it.\n                if !ep_info.dominates_global_use(info) {\n                    log::debug!(\"Skip function {:?}\", ir_function.name);\n                    continue;\n                }\n\n                // Skip functions that that are not compatible with this entry point's stage.\n                //\n                // When validation is enabled, it rejects modules whose entry points try to call\n                // incompatible functions, so if we got this far, then any functions incompatible\n                // with our selected entry point must not be used.\n                //\n                // When validation is disabled, `fun_info.available_stages` is always just\n                // `ShaderStages::all()`, so this will write all functions in the module, and\n                // the downstream GLSL compiler will catch any problems.\n                if !info.available_stages.contains(ep_info.available_stages) {\n                    continue;\n                }\n            }\n            let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;\n            self.lookup_function.insert(handle, id);\n        }\n\n        // write all or one entry points\n        for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {\n            if ep_index.is_some() && ep_index != Some(index) {\n                continue;\n            }\n            let info = mod_info.get_entry_point(index);\n            let ep_instruction =\n                self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;\n            ep_instruction.to_words(&mut self.logical_layout.entry_points);\n        }\n\n        for capability in self.capabilities_used.iter() {\n            Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);\n        }\n        for extension in self.extensions_used.iter() {\n            Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);\n        }\n        if ir_module.entry_points.is_empty() {\n            // SPIR-V doesn't like modules without entry points\n            Instruction::capability(spirv::Capability::Linkage)\n                .to_words(&mut self.logical_layout.capabilities);\n        }\n\n        let addressing_model = spirv::AddressingModel::Logical;\n        let memory_model = if self\n            .capabilities_used\n            .contains(&spirv::Capability::VulkanMemoryModel)\n        {\n            spirv::MemoryModel::Vulkan\n        } else {\n            spirv::MemoryModel::GLSL450\n        };\n        //self.check(addressing_model.required_capabilities())?;\n        //self.check(memory_model.required_capabilities())?;\n\n        Instruction::memory_model(addressing_model, memory_model)\n            .to_words(&mut self.logical_layout.memory_model);\n\n        for debug_string in self.debug_strings.iter() {\n            debug_string.to_words(&mut self.logical_layout.debugs);\n        }\n\n        if self.flags.contains(WriterFlags::DEBUG) {\n            for debug in self.debugs.iter() {\n                debug.to_words(&mut self.logical_layout.debugs);\n            }\n        }\n\n        for annotation in self.annotations.iter() {\n            annotation.to_words(&mut self.logical_layout.annotations);\n        }\n\n        Ok(())\n    }\n\n    pub fn write(\n        &mut self,\n        ir_module: &crate::Module,\n        info: &ModuleInfo,\n        pipeline_options: Option<&PipelineOptions>,\n        debug_info: &Option<DebugInfo>,\n        words: &mut Vec<Word>,\n    ) -> Result<(), Error> {\n        self.reset();\n\n        // Try to find the entry point and corresponding index\n        let ep_index = match pipeline_options {\n            Some(po) => {\n                let index = ir_module\n                    .entry_points\n                    .iter()\n                    .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)\n                    .ok_or(Error::EntryPointNotFound)?;\n                Some(index)\n            }\n            None => None,\n        };\n\n        self.write_logical_layout(ir_module, info, ep_index, debug_info)?;\n        self.write_physical_layout();\n\n        self.physical_layout.in_words(words);\n        self.logical_layout.in_words(words);\n        Ok(())\n    }\n\n    /// Return the set of capabilities the last module written used.\n    pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {\n        &self.capabilities_used\n    }\n\n    pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {\n        self.require_any(\"NonUniformEXT\", &[spirv::Capability::ShaderNonUniform])?;\n        self.use_extension(\"SPV_EXT_descriptor_indexing\");\n        self.decorate(id, spirv::Decoration::NonUniform, &[]);\n        Ok(())\n    }\n\n    pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {\n        self.io_f16_polyfills.needs_polyfill(ty_inner)\n    }\n\n    pub(super) fn write_debug_printf(\n        &mut self,\n        block: &mut Block,\n        string: &str,\n        format_params: &[Word],\n    ) {\n        if self.debug_printf.is_none() {\n            self.use_extension(\"SPV_KHR_non_semantic_info\");\n            let import_id = self.id_gen.next();\n            Instruction::ext_inst_import(import_id, \"NonSemantic.DebugPrintf\")\n                .to_words(&mut self.logical_layout.ext_inst_imports);\n            self.debug_printf = Some(import_id)\n        }\n\n        let import_id = self.debug_printf.unwrap();\n\n        let string_id = self.id_gen.next();\n        self.debug_strings\n            .push(Instruction::string(string, string_id));\n\n        let mut operands = Vec::with_capacity(1 + format_params.len());\n        operands.push(string_id);\n        operands.extend(format_params.iter());\n\n        let print_id = self.id_gen.next();\n        block.body.push(Instruction::ext_inst(\n            import_id,\n            1,\n            self.void_type,\n            print_id,\n            &operands,\n        ));\n    }\n}\n\n#[test]\nfn test_write_physical_layout() {\n    let mut writer = Writer::new(&Options::default()).unwrap();\n    assert_eq!(writer.physical_layout.bound, 0);\n    writer.write_physical_layout();\n    assert_eq!(writer.physical_layout.bound, 3);\n}\n"
  },
  {
    "path": "naga/src/back/wgsl/mod.rs",
    "content": "/*!\nBackend for [WGSL][wgsl] (WebGPU Shading Language).\n\n[wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html\n*/\n\nmod polyfill;\nmod writer;\n\nuse alloc::format;\nuse alloc::string::String;\n\nuse thiserror::Error;\n\npub use writer::{Writer, WriterFlags};\n\nuse crate::common::wgsl;\n\n#[derive(Error, Debug)]\npub enum Error {\n    #[error(transparent)]\n    FmtError(#[from] core::fmt::Error),\n    #[error(\"{0}\")]\n    Custom(String),\n    #[error(\"{0}\")]\n    Unimplemented(String), // TODO: Error used only during development\n    #[error(\"Unsupported relational function: {0:?}\")]\n    UnsupportedRelationalFunction(crate::RelationalFunction),\n    #[error(\"Unsupported {kind}: {value}\")]\n    Unsupported {\n        /// What kind of unsupported thing this is: interpolation, builtin, etc.\n        kind: &'static str,\n\n        /// The debug form of the Naga IR value that this backend can't express.\n        value: String,\n    },\n}\n\nimpl Error {\n    /// Produce an [`Unsupported`] error for `value`.\n    ///\n    /// [`Unsupported`]: Error::Unsupported\n    fn unsupported<T: core::fmt::Debug>(kind: &'static str, value: T) -> Error {\n        Error::Unsupported {\n            kind,\n            value: format!(\"{value:?}\"),\n        }\n    }\n}\n\ntrait ToWgslIfImplemented {\n    fn to_wgsl_if_implemented(self) -> Result<&'static str, Error>;\n}\n\nimpl<T> ToWgslIfImplemented for T\nwhere\n    T: wgsl::TryToWgsl + core::fmt::Debug + Copy,\n{\n    fn to_wgsl_if_implemented(self) -> Result<&'static str, Error> {\n        self.try_to_wgsl()\n            .ok_or_else(|| Error::unsupported(T::DESCRIPTION, self))\n    }\n}\n\npub fn write_string(\n    module: &crate::Module,\n    info: &crate::valid::ModuleInfo,\n    flags: WriterFlags,\n) -> Result<String, Error> {\n    let mut w = Writer::new(String::new(), flags);\n    w.write(module, info)?;\n    let output = w.finish();\n    Ok(output)\n}\n\nimpl crate::AtomicFunction {\n    const fn to_wgsl(self) -> &'static str {\n        match self {\n            Self::Add => \"Add\",\n            Self::Subtract => \"Sub\",\n            Self::And => \"And\",\n            Self::InclusiveOr => \"Or\",\n            Self::ExclusiveOr => \"Xor\",\n            Self::Min => \"Min\",\n            Self::Max => \"Max\",\n            Self::Exchange { compare: None } => \"Exchange\",\n            Self::Exchange { .. } => \"CompareExchangeWeak\",\n        }\n    }\n}\n\npub const fn supported_capabilities() -> crate::valid::Capabilities {\n    // WGSL regurgitation supports almost everything, though browser webgpu can't parse most of these.\n    use crate::valid::Capabilities as Caps;\n    Caps::all()\n}\n"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_2x2_f16.wgsl",
    "content": "fn _naga_inverse_2x2_f16(m: mat2x2<f16>) -> mat2x2<f16> {\n    var adj: mat2x2<f16>;\n    adj[0][0] = m[1][1];\n    adj[0][1] = -m[0][1];\n    adj[1][0] = -m[1][0];\n    adj[1][1] = m[0][0];\n\n    let det: f16 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n    return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_2x2_f32.wgsl",
    "content": "fn _naga_inverse_2x2_f32(m: mat2x2<f32>) -> mat2x2<f32> {\n    var adj: mat2x2<f32>;\n    adj[0][0] = m[1][1];\n    adj[0][1] = -m[0][1];\n    adj[1][0] = -m[1][0];\n    adj[1][1] = m[0][0];\n\n    let det: f32 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n    return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_3x3_f16.wgsl",
    "content": "fn _naga_inverse_3x3_f16(m: mat3x3<f16>) -> mat3x3<f16> {\n    var adj: mat3x3<f16>;\n\n    adj[0][0] =   (m[1][1] * m[2][2] - m[2][1] * m[1][2]);\n    adj[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n    adj[2][0] =   (m[1][0] * m[2][1] - m[2][0] * m[1][1]);\n    adj[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n    adj[1][1] =   (m[0][0] * m[2][2] - m[2][0] * m[0][2]);\n    adj[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n    adj[0][2] =   (m[0][1] * m[1][2] - m[1][1] * m[0][2]);\n    adj[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n    adj[2][2] =   (m[0][0] * m[1][1] - m[1][0] * m[0][1]);\n\n    let det: f16 = (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])\n    \t\t- m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])\n    \t\t+ m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]));\n\n    return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_3x3_f32.wgsl",
    "content": "fn _naga_inverse_3x3_f32(m: mat3x3<f32>) -> mat3x3<f32> {\n    var adj: mat3x3<f32>;\n\n    adj[0][0] =   (m[1][1] * m[2][2] - m[2][1] * m[1][2]);\n    adj[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n    adj[2][0] =   (m[1][0] * m[2][1] - m[2][0] * m[1][1]);\n    adj[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n    adj[1][1] =   (m[0][0] * m[2][2] - m[2][0] * m[0][2]);\n    adj[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n    adj[0][2] =   (m[0][1] * m[1][2] - m[1][1] * m[0][2]);\n    adj[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n    adj[2][2] =   (m[0][0] * m[1][1] - m[1][0] * m[0][1]);\n\n    let det: f32 = (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])\n    \t\t- m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])\n    \t\t+ m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]));\n\n    return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_4x4_f16.wgsl",
    "content": "fn _naga_inverse_4x4_f16(m: mat4x4<f16>) -> mat4x4<f16> {\n   let sub_factor00: f16 = m[2][2] * m[3][3] - m[3][2] * m[2][3];\n   let sub_factor01: f16 = m[2][1] * m[3][3] - m[3][1] * m[2][3];\n   let sub_factor02: f16 = m[2][1] * m[3][2] - m[3][1] * m[2][2];\n   let sub_factor03: f16 = m[2][0] * m[3][3] - m[3][0] * m[2][3];\n   let sub_factor04: f16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];\n   let sub_factor05: f16 = m[2][0] * m[3][1] - m[3][0] * m[2][1];\n   let sub_factor06: f16 = m[1][2] * m[3][3] - m[3][2] * m[1][3];\n   let sub_factor07: f16 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor08: f16 = m[1][1] * m[3][2] - m[3][1] * m[1][2];\n   let sub_factor09: f16 = m[1][0] * m[3][3] - m[3][0] * m[1][3];\n   let sub_factor10: f16 = m[1][0] * m[3][2] - m[3][0] * m[1][2];\n   let sub_factor11: f16 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor12: f16 = m[1][0] * m[3][1] - m[3][0] * m[1][1];\n   let sub_factor13: f16 = m[1][2] * m[2][3] - m[2][2] * m[1][3];\n   let sub_factor14: f16 = m[1][1] * m[2][3] - m[2][1] * m[1][3];\n   let sub_factor15: f16 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n   let sub_factor16: f16 = m[1][0] * m[2][3] - m[2][0] * m[1][3];\n   let sub_factor17: f16 = m[1][0] * m[2][2] - m[2][0] * m[1][2];\n   let sub_factor18: f16 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n\n   var adj: mat4x4<f16>;\n   adj[0][0] =   (m[1][1] * sub_factor00 - m[1][2] * sub_factor01 + m[1][3] * sub_factor02);\n   adj[1][0] = - (m[1][0] * sub_factor00 - m[1][2] * sub_factor03 + m[1][3] * sub_factor04);\n   adj[2][0] =   (m[1][0] * sub_factor01 - m[1][1] * sub_factor03 + m[1][3] * sub_factor05);\n   adj[3][0] = - (m[1][0] * sub_factor02 - m[1][1] * sub_factor04 + m[1][2] * sub_factor05);\n   adj[0][1] = - (m[0][1] * sub_factor00 - m[0][2] * sub_factor01 + m[0][3] * sub_factor02);\n   adj[1][1] =   (m[0][0] * sub_factor00 - m[0][2] * sub_factor03 + m[0][3] * sub_factor04);\n   adj[2][1] = - (m[0][0] * sub_factor01 - m[0][1] * sub_factor03 + m[0][3] * sub_factor05);\n   adj[3][1] =   (m[0][0] * sub_factor02 - m[0][1] * sub_factor04 + m[0][2] * sub_factor05);\n   adj[0][2] =   (m[0][1] * sub_factor06 - m[0][2] * sub_factor07 + m[0][3] * sub_factor08);\n   adj[1][2] = - (m[0][0] * sub_factor06 - m[0][2] * sub_factor09 + m[0][3] * sub_factor10);\n   adj[2][2] =   (m[0][0] * sub_factor11 - m[0][1] * sub_factor09 + m[0][3] * sub_factor12);\n   adj[3][2] = - (m[0][0] * sub_factor08 - m[0][1] * sub_factor10 + m[0][2] * sub_factor12);\n   adj[0][3] = - (m[0][1] * sub_factor13 - m[0][2] * sub_factor14 + m[0][3] * sub_factor15);\n   adj[1][3] =   (m[0][0] * sub_factor13 - m[0][2] * sub_factor16 + m[0][3] * sub_factor17);\n   adj[2][3] = - (m[0][0] * sub_factor14 - m[0][1] * sub_factor16 + m[0][3] * sub_factor18);\n   adj[3][3] =   (m[0][0] * sub_factor15 - m[0][1] * sub_factor17 + m[0][2] * sub_factor18);\n\n   let det = (m[0][0] * adj[0][0] + m[0][1] * adj[1][0] + m[0][2] * adj[2][0] + m[0][3] * adj[3][0]);\n\n   return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/inverse/inverse_4x4_f32.wgsl",
    "content": "fn _naga_inverse_4x4_f32(m: mat4x4<f32>) -> mat4x4<f32> {\n   let sub_factor00: f32 = m[2][2] * m[3][3] - m[3][2] * m[2][3];\n   let sub_factor01: f32 = m[2][1] * m[3][3] - m[3][1] * m[2][3];\n   let sub_factor02: f32 = m[2][1] * m[3][2] - m[3][1] * m[2][2];\n   let sub_factor03: f32 = m[2][0] * m[3][3] - m[3][0] * m[2][3];\n   let sub_factor04: f32 = m[2][0] * m[3][2] - m[3][0] * m[2][2];\n   let sub_factor05: f32 = m[2][0] * m[3][1] - m[3][0] * m[2][1];\n   let sub_factor06: f32 = m[1][2] * m[3][3] - m[3][2] * m[1][3];\n   let sub_factor07: f32 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor08: f32 = m[1][1] * m[3][2] - m[3][1] * m[1][2];\n   let sub_factor09: f32 = m[1][0] * m[3][3] - m[3][0] * m[1][3];\n   let sub_factor10: f32 = m[1][0] * m[3][2] - m[3][0] * m[1][2];\n   let sub_factor11: f32 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor12: f32 = m[1][0] * m[3][1] - m[3][0] * m[1][1];\n   let sub_factor13: f32 = m[1][2] * m[2][3] - m[2][2] * m[1][3];\n   let sub_factor14: f32 = m[1][1] * m[2][3] - m[2][1] * m[1][3];\n   let sub_factor15: f32 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n   let sub_factor16: f32 = m[1][0] * m[2][3] - m[2][0] * m[1][3];\n   let sub_factor17: f32 = m[1][0] * m[2][2] - m[2][0] * m[1][2];\n   let sub_factor18: f32 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n\n   var adj: mat4x4<f32>;\n   adj[0][0] =   (m[1][1] * sub_factor00 - m[1][2] * sub_factor01 + m[1][3] * sub_factor02);\n   adj[1][0] = - (m[1][0] * sub_factor00 - m[1][2] * sub_factor03 + m[1][3] * sub_factor04);\n   adj[2][0] =   (m[1][0] * sub_factor01 - m[1][1] * sub_factor03 + m[1][3] * sub_factor05);\n   adj[3][0] = - (m[1][0] * sub_factor02 - m[1][1] * sub_factor04 + m[1][2] * sub_factor05);\n   adj[0][1] = - (m[0][1] * sub_factor00 - m[0][2] * sub_factor01 + m[0][3] * sub_factor02);\n   adj[1][1] =   (m[0][0] * sub_factor00 - m[0][2] * sub_factor03 + m[0][3] * sub_factor04);\n   adj[2][1] = - (m[0][0] * sub_factor01 - m[0][1] * sub_factor03 + m[0][3] * sub_factor05);\n   adj[3][1] =   (m[0][0] * sub_factor02 - m[0][1] * sub_factor04 + m[0][2] * sub_factor05);\n   adj[0][2] =   (m[0][1] * sub_factor06 - m[0][2] * sub_factor07 + m[0][3] * sub_factor08);\n   adj[1][2] = - (m[0][0] * sub_factor06 - m[0][2] * sub_factor09 + m[0][3] * sub_factor10);\n   adj[2][2] =   (m[0][0] * sub_factor11 - m[0][1] * sub_factor09 + m[0][3] * sub_factor12);\n   adj[3][2] = - (m[0][0] * sub_factor08 - m[0][1] * sub_factor10 + m[0][2] * sub_factor12);\n   adj[0][3] = - (m[0][1] * sub_factor13 - m[0][2] * sub_factor14 + m[0][3] * sub_factor15);\n   adj[1][3] =   (m[0][0] * sub_factor13 - m[0][2] * sub_factor16 + m[0][3] * sub_factor17);\n   adj[2][3] = - (m[0][0] * sub_factor14 - m[0][1] * sub_factor16 + m[0][3] * sub_factor18);\n   adj[3][3] =   (m[0][0] * sub_factor15 - m[0][1] * sub_factor17 + m[0][2] * sub_factor18);\n\n   let det = (m[0][0] * adj[0][0] + m[0][1] * adj[1][0] + m[0][2] * adj[2][0] + m[0][3] * adj[3][0]);\n\n   return adj * (1 / det);\n}"
  },
  {
    "path": "naga/src/back/wgsl/polyfill/mod.rs",
    "content": "use crate::{ScalarKind, TypeInner, VectorSize};\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub struct InversePolyfill {\n    pub fun_name: &'static str,\n    pub source: &'static str,\n}\n\nimpl InversePolyfill {\n    pub fn find_overload(ty: &TypeInner) -> Option<InversePolyfill> {\n        let &TypeInner::Matrix {\n            columns,\n            rows,\n            scalar,\n        } = ty\n        else {\n            return None;\n        };\n\n        if columns != rows || scalar.kind != ScalarKind::Float {\n            return None;\n        };\n\n        Self::polyfill_overload(columns, scalar.width)\n    }\n\n    const fn polyfill_overload(\n        dimension: VectorSize,\n        width: crate::Bytes,\n    ) -> Option<InversePolyfill> {\n        const INVERSE_2X2_F32: &str = include_str!(\"inverse/inverse_2x2_f32.wgsl\");\n        const INVERSE_3X3_F32: &str = include_str!(\"inverse/inverse_3x3_f32.wgsl\");\n        const INVERSE_4X4_F32: &str = include_str!(\"inverse/inverse_4x4_f32.wgsl\");\n        const INVERSE_2X2_F16: &str = include_str!(\"inverse/inverse_2x2_f16.wgsl\");\n        const INVERSE_3X3_F16: &str = include_str!(\"inverse/inverse_3x3_f16.wgsl\");\n        const INVERSE_4X4_F16: &str = include_str!(\"inverse/inverse_4x4_f16.wgsl\");\n\n        match (dimension, width) {\n            (VectorSize::Bi, 4) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_2x2_f32\",\n                source: INVERSE_2X2_F32,\n            }),\n            (VectorSize::Tri, 4) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_3x3_f32\",\n                source: INVERSE_3X3_F32,\n            }),\n            (VectorSize::Quad, 4) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_4x4_f32\",\n                source: INVERSE_4X4_F32,\n            }),\n            (VectorSize::Bi, 2) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_2x2_f16\",\n                source: INVERSE_2X2_F16,\n            }),\n            (VectorSize::Tri, 2) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_3x3_f16\",\n                source: INVERSE_3X3_F16,\n            }),\n            (VectorSize::Quad, 2) => Some(InversePolyfill {\n                fun_name: \"_naga_inverse_4x4_f16\",\n                source: INVERSE_4X4_F16,\n            }),\n            _ => None,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/back/wgsl/writer.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::fmt::Write;\n\nuse super::Error;\nuse super::ToWgslIfImplemented as _;\nuse crate::{back::wgsl::polyfill::InversePolyfill, common::wgsl::TypeContext};\nuse crate::{\n    back::{self, Baked},\n    common::{\n        self,\n        wgsl::{address_space_str, ToWgsl, TryToWgsl},\n    },\n    proc::{self, NameKey},\n    valid, Handle, Module, ShaderStage, TypeInner,\n};\n\n/// Shorthand result used internally by the backend\ntype BackendResult = Result<(), Error>;\n\n/// WGSL [attribute](https://gpuweb.github.io/gpuweb/wgsl/#attributes)\nenum Attribute {\n    Binding(u32),\n    BuiltIn(crate::BuiltIn),\n    Group(u32),\n    Invariant,\n    Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),\n    Location(u32),\n    BlendSrc(u32),\n    Stage(ShaderStage),\n    WorkGroupSize([u32; 3]),\n    MeshStage(String),\n    TaskPayload(String),\n    PerPrimitive,\n    IncomingRayPayload(String),\n}\n\n/// The WGSL form that `write_expr_with_indirection` should use to render a Naga\n/// expression.\n///\n/// Sometimes a Naga `Expression` alone doesn't provide enough information to\n/// choose the right rendering for it in WGSL. For example, one natural WGSL\n/// rendering of a Naga `LocalVariable(x)` expression might be `&x`, since\n/// `LocalVariable` produces a pointer to the local variable's storage. But when\n/// rendering a `Store` statement, the `pointer` operand must be the left hand\n/// side of a WGSL assignment, so the proper rendering is `x`.\n///\n/// The caller of `write_expr_with_indirection` must provide an `Expected` value\n/// to indicate how ambiguous expressions should be rendered.\n#[derive(Clone, Copy, Debug)]\nenum Indirection {\n    /// Render pointer-construction expressions as WGSL `ptr`-typed expressions.\n    ///\n    /// This is the right choice for most cases. Whenever a Naga pointer\n    /// expression is not the `pointer` operand of a `Load` or `Store`, it\n    /// must be a WGSL pointer expression.\n    Ordinary,\n\n    /// Render pointer-construction expressions as WGSL reference-typed\n    /// expressions.\n    ///\n    /// For example, this is the right choice for the `pointer` operand when\n    /// rendering a `Store` statement as a WGSL assignment.\n    Reference,\n}\n\nbitflags::bitflags! {\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct WriterFlags: u32 {\n        /// Always annotate the type information instead of inferring.\n        const EXPLICIT_TYPES = 0x1;\n    }\n}\n\npub struct Writer<W> {\n    out: W,\n    flags: WriterFlags,\n    names: crate::FastHashMap<NameKey, String>,\n    namer: proc::Namer,\n    named_expressions: crate::NamedExpressions,\n    required_polyfills: crate::FastIndexSet<InversePolyfill>,\n}\n\nimpl<W: Write> Writer<W> {\n    pub fn new(out: W, flags: WriterFlags) -> Self {\n        Writer {\n            out,\n            flags,\n            names: crate::FastHashMap::default(),\n            namer: proc::Namer::default(),\n            named_expressions: crate::NamedExpressions::default(),\n            required_polyfills: crate::FastIndexSet::default(),\n        }\n    }\n\n    fn reset(&mut self, module: &Module) {\n        self.names.clear();\n        self.namer.reset(\n            module,\n            &crate::keywords::wgsl::RESERVED_SET,\n            &crate::keywords::wgsl::BUILTIN_IDENTIFIER_SET,\n            // an identifier must not start with two underscore\n            proc::CaseInsensitiveKeywordSet::empty(),\n            &[\"__\", \"_naga\"],\n            &mut self.names,\n        );\n        self.named_expressions.clear();\n        self.required_polyfills.clear();\n    }\n\n    /// Determine if `ty` is the Naga IR presentation of a WGSL builtin type.\n    ///\n    /// Return true if `ty` refers to the Naga IR form of a WGSL builtin type\n    /// like `__atomic_compare_exchange_result`.\n    ///\n    /// Even though the module may use the type, the WGSL backend should avoid\n    /// emitting a definition for it, since it is [predeclared] in WGSL.\n    ///\n    /// This also covers types like [`NagaExternalTextureParams`], which other\n    /// backends use to lower WGSL constructs like external textures to their\n    /// implementations. WGSL can express these directly, so the types need not\n    /// be emitted.\n    ///\n    /// [predeclared]: https://www.w3.org/TR/WGSL/#predeclared\n    /// [`NagaExternalTextureParams`]: crate::ir::SpecialTypes::external_texture_params\n    fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle<crate::Type>) -> bool {\n        module\n            .special_types\n            .predeclared_types\n            .values()\n            .any(|t| *t == ty)\n            || Some(ty) == module.special_types.external_texture_params\n            || Some(ty) == module.special_types.external_texture_transfer_function\n    }\n\n    pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {\n        self.reset(module);\n\n        // Write all `enable` declarations\n        self.write_enable_declarations(module)?;\n\n        // Write all structs\n        for (handle, ty) in module.types.iter() {\n            if let TypeInner::Struct { ref members, .. } = ty.inner {\n                {\n                    if !self.is_builtin_wgsl_struct(module, handle) {\n                        self.write_struct(module, handle, members)?;\n                        writeln!(self.out)?;\n                    }\n                }\n            }\n        }\n\n        // Write all named constants\n        let mut constants = module\n            .constants\n            .iter()\n            .filter(|&(_, c)| c.name.is_some())\n            .peekable();\n        while let Some((handle, _)) = constants.next() {\n            self.write_global_constant(module, handle)?;\n            // Add extra newline for readability on last iteration\n            if constants.peek().is_none() {\n                writeln!(self.out)?;\n            }\n        }\n\n        // Write all overrides\n        let mut overrides = module.overrides.iter().peekable();\n        while let Some((handle, _)) = overrides.next() {\n            self.write_override(module, handle)?;\n            // Add extra newline for readability on last iteration\n            if overrides.peek().is_none() {\n                writeln!(self.out)?;\n            }\n        }\n\n        // Write all globals\n        for (ty, global) in module.global_variables.iter() {\n            self.write_global(module, global, ty)?;\n        }\n\n        if !module.global_variables.is_empty() {\n            // Add extra newline for readability\n            writeln!(self.out)?;\n        }\n\n        // Write all regular functions\n        for (handle, function) in module.functions.iter() {\n            let fun_info = &info[handle];\n\n            let func_ctx = back::FunctionCtx {\n                ty: back::FunctionType::Function(handle),\n                info: fun_info,\n                expressions: &function.expressions,\n                named_expressions: &function.named_expressions,\n            };\n\n            // Write the function\n            self.write_function(module, function, &func_ctx)?;\n\n            writeln!(self.out)?;\n        }\n\n        // Write all entry points\n        for (index, ep) in module.entry_points.iter().enumerate() {\n            let attributes = match ep.stage {\n                ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],\n                ShaderStage::Compute => vec![\n                    Attribute::Stage(ShaderStage::Compute),\n                    Attribute::WorkGroupSize(ep.workgroup_size),\n                ],\n                ShaderStage::Mesh => {\n                    let mesh_output_name = module.global_variables\n                        [ep.mesh_info.as_ref().unwrap().output_variable]\n                        .name\n                        .clone()\n                        .unwrap();\n                    let mut mesh_attrs = vec![\n                        Attribute::MeshStage(mesh_output_name),\n                        Attribute::WorkGroupSize(ep.workgroup_size),\n                    ];\n                    if let Some(task_payload) = ep.task_payload {\n                        let payload_name =\n                            module.global_variables[task_payload].name.clone().unwrap();\n                        mesh_attrs.push(Attribute::TaskPayload(payload_name));\n                    }\n                    mesh_attrs\n                }\n                ShaderStage::Task => {\n                    let payload_name = module.global_variables[ep.task_payload.unwrap()]\n                        .name\n                        .clone()\n                        .unwrap();\n                    vec![\n                        Attribute::Stage(ShaderStage::Task),\n                        Attribute::TaskPayload(payload_name),\n                        Attribute::WorkGroupSize(ep.workgroup_size),\n                    ]\n                }\n                ShaderStage::RayGeneration => vec![Attribute::Stage(ShaderStage::RayGeneration)],\n                ShaderStage::AnyHit | ShaderStage::ClosestHit | ShaderStage::Miss => {\n                    let payload_name = module.global_variables[ep.incoming_ray_payload.unwrap()]\n                        .name\n                        .clone()\n                        .unwrap();\n                    vec![\n                        Attribute::Stage(ep.stage),\n                        Attribute::IncomingRayPayload(payload_name),\n                    ]\n                }\n            };\n            self.write_attributes(&attributes)?;\n            // Add a newline after attribute\n            writeln!(self.out)?;\n\n            let func_ctx = back::FunctionCtx {\n                ty: back::FunctionType::EntryPoint(index as u16),\n                info: info.get_entry_point(index),\n                expressions: &ep.function.expressions,\n                named_expressions: &ep.function.named_expressions,\n            };\n            self.write_function(module, &ep.function, &func_ctx)?;\n\n            if index < module.entry_points.len() - 1 {\n                writeln!(self.out)?;\n            }\n        }\n\n        // Write any polyfills that were required.\n        for polyfill in &self.required_polyfills {\n            writeln!(self.out)?;\n            write!(self.out, \"{}\", polyfill.source)?;\n            writeln!(self.out)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method which writes all the `enable` declarations\n    /// needed for a module.\n    fn write_enable_declarations(&mut self, module: &Module) -> BackendResult {\n        #[derive(Default)]\n        struct RequiredEnabled {\n            f16: bool,\n            dual_source_blending: bool,\n            clip_distances: bool,\n            mesh_shaders: bool,\n            primitive_index: bool,\n            cooperative_matrix: bool,\n            draw_index: bool,\n            ray_tracing_pipeline: bool,\n        }\n        let mut needed = RequiredEnabled {\n            mesh_shaders: module.uses_mesh_shaders(),\n            ..Default::default()\n        };\n\n        let check_binding = |binding: &crate::Binding, needed: &mut RequiredEnabled| match *binding\n        {\n            crate::Binding::Location {\n                blend_src: Some(_), ..\n            } => {\n                needed.dual_source_blending = true;\n            }\n            crate::Binding::BuiltIn(crate::BuiltIn::ClipDistances) => {\n                needed.clip_distances = true;\n            }\n            crate::Binding::BuiltIn(crate::BuiltIn::PrimitiveIndex) => {\n                needed.primitive_index = true;\n            }\n            crate::Binding::Location {\n                per_primitive: true,\n                ..\n            } => {\n                needed.mesh_shaders = true;\n            }\n            crate::Binding::BuiltIn(crate::BuiltIn::DrawIndex) => needed.draw_index = true,\n            crate::Binding::BuiltIn(\n                crate::BuiltIn::RayInvocationId\n                | crate::BuiltIn::NumRayInvocations\n                | crate::BuiltIn::InstanceCustomData\n                | crate::BuiltIn::GeometryIndex\n                | crate::BuiltIn::WorldRayOrigin\n                | crate::BuiltIn::WorldRayDirection\n                | crate::BuiltIn::ObjectRayOrigin\n                | crate::BuiltIn::ObjectRayDirection\n                | crate::BuiltIn::RayTmin\n                | crate::BuiltIn::RayTCurrentMax\n                | crate::BuiltIn::ObjectToWorld\n                | crate::BuiltIn::WorldToObject,\n            ) => {\n                needed.ray_tracing_pipeline = true;\n            }\n            _ => {}\n        };\n\n        // Determine which `enable` declarations are needed\n        for (_, ty) in module.types.iter() {\n            match ty.inner {\n                TypeInner::Scalar(scalar)\n                | TypeInner::Vector { scalar, .. }\n                | TypeInner::Matrix { scalar, .. } => {\n                    needed.f16 |= scalar == crate::Scalar::F16;\n                }\n                TypeInner::Struct { ref members, .. } => {\n                    for binding in members.iter().filter_map(|m| m.binding.as_ref()) {\n                        check_binding(binding, &mut needed);\n                    }\n                }\n                TypeInner::CooperativeMatrix { .. } => {\n                    needed.cooperative_matrix = true;\n                }\n                TypeInner::AccelerationStructure { .. } => {\n                    needed.ray_tracing_pipeline = true;\n                }\n                _ => {}\n            }\n        }\n\n        for ep in &module.entry_points {\n            if let Some(res) = ep.function.result.as_ref().and_then(|a| a.binding.as_ref()) {\n                check_binding(res, &mut needed);\n            }\n            for arg in ep\n                .function\n                .arguments\n                .iter()\n                .filter_map(|a| a.binding.as_ref())\n            {\n                check_binding(arg, &mut needed);\n            }\n        }\n\n        if module.global_variables.iter().any(|gv| {\n            gv.1.space == crate::AddressSpace::IncomingRayPayload\n                || gv.1.space == crate::AddressSpace::RayPayload\n        }) {\n            needed.ray_tracing_pipeline = true;\n        }\n\n        if module.entry_points.iter().any(|ep| {\n            matches!(\n                ep.stage,\n                ShaderStage::RayGeneration\n                    | ShaderStage::AnyHit\n                    | ShaderStage::ClosestHit\n                    | ShaderStage::Miss\n            )\n        }) {\n            needed.ray_tracing_pipeline = true;\n        }\n\n        if module.global_variables.iter().any(|gv| {\n            gv.1.space == crate::AddressSpace::IncomingRayPayload\n                || gv.1.space == crate::AddressSpace::RayPayload\n        }) {\n            needed.ray_tracing_pipeline = true;\n        }\n\n        if module.entry_points.iter().any(|ep| {\n            matches!(\n                ep.stage,\n                ShaderStage::RayGeneration\n                    | ShaderStage::AnyHit\n                    | ShaderStage::ClosestHit\n                    | ShaderStage::Miss\n            )\n        }) {\n            needed.ray_tracing_pipeline = true;\n        }\n\n        // Write required declarations\n        let mut any_written = false;\n        if needed.f16 {\n            writeln!(self.out, \"enable f16;\")?;\n            any_written = true;\n        }\n        if needed.dual_source_blending {\n            writeln!(self.out, \"enable dual_source_blending;\")?;\n            any_written = true;\n        }\n        if needed.clip_distances {\n            writeln!(self.out, \"enable clip_distances;\")?;\n            any_written = true;\n        }\n        if module.uses_mesh_shaders() {\n            writeln!(self.out, \"enable wgpu_mesh_shader;\")?;\n            any_written = true;\n        }\n        if needed.draw_index {\n            writeln!(self.out, \"enable draw_index;\")?;\n            any_written = true;\n        }\n        if needed.primitive_index {\n            writeln!(self.out, \"enable primitive_index;\")?;\n            any_written = true;\n        }\n        if needed.cooperative_matrix {\n            writeln!(self.out, \"enable wgpu_cooperative_matrix;\")?;\n            any_written = true;\n        }\n        if needed.ray_tracing_pipeline {\n            writeln!(self.out, \"enable wgpu_ray_tracing_pipeline;\")?;\n            any_written = true;\n        }\n        if any_written {\n            // Empty line for readability\n            writeln!(self.out)?;\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write\n    /// [functions](https://gpuweb.github.io/gpuweb/wgsl/#functions)\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_function(\n        &mut self,\n        module: &Module,\n        func: &crate::Function,\n        func_ctx: &back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        let func_name = match func_ctx.ty {\n            back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],\n            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],\n        };\n\n        // Write function name\n        write!(self.out, \"fn {func_name}(\")?;\n\n        // Write function arguments\n        for (index, arg) in func.arguments.iter().enumerate() {\n            // Write argument attribute if a binding is present\n            if let Some(ref binding) = arg.binding {\n                self.write_attributes(&map_binding_to_attribute(binding))?;\n            }\n            // Write argument name\n            let argument_name = &self.names[&func_ctx.argument_key(index as u32)];\n\n            write!(self.out, \"{argument_name}: \")?;\n            // Write argument type\n            self.write_type(module, arg.ty)?;\n            if index < func.arguments.len() - 1 {\n                // Add a separator between args\n                write!(self.out, \", \")?;\n            }\n        }\n\n        write!(self.out, \")\")?;\n\n        // Write function return type\n        if let Some(ref result) = func.result {\n            write!(self.out, \" -> \")?;\n            if let Some(ref binding) = result.binding {\n                self.write_attributes(&map_binding_to_attribute(binding))?;\n            }\n            self.write_type(module, result.ty)?;\n        }\n\n        write!(self.out, \" {{\")?;\n        writeln!(self.out)?;\n\n        // Write function local variables\n        for (handle, local) in func.local_variables.iter() {\n            // Write indentation (only for readability)\n            write!(self.out, \"{}\", back::INDENT)?;\n\n            // Write the local name\n            // The leading space is important\n            write!(self.out, \"var {}: \", self.names[&func_ctx.name_key(handle)])?;\n\n            // Write the local type\n            self.write_type(module, local.ty)?;\n\n            // Write the local initializer if needed\n            if let Some(init) = local.init {\n                // Put the equal signal only if there's a initializer\n                // The leading and trailing spaces aren't needed but help with readability\n                write!(self.out, \" = \")?;\n\n                // Write the constant\n                // `write_constant` adds no trailing or leading space/newline\n                self.write_expr(module, init, func_ctx)?;\n            }\n\n            // Finish the local with `;` and add a newline (only for readability)\n            writeln!(self.out, \";\")?\n        }\n\n        if !func.local_variables.is_empty() {\n            writeln!(self.out)?;\n        }\n\n        // Write the function body (statement list)\n        for sta in func.body.iter() {\n            // The indentation should always be 1 when writing the function body\n            self.write_stmt(module, sta, func_ctx, back::Level(1))?;\n        }\n\n        writeln!(self.out, \"}}\")?;\n\n        self.named_expressions.clear();\n\n        Ok(())\n    }\n\n    /// Helper method to write a attribute\n    fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {\n        for attribute in attributes {\n            match *attribute {\n                Attribute::Location(id) => write!(self.out, \"@location({id}) \")?,\n                Attribute::BlendSrc(blend_src) => write!(self.out, \"@blend_src({blend_src}) \")?,\n                Attribute::BuiltIn(builtin_attrib) => {\n                    let builtin = builtin_attrib.to_wgsl_if_implemented()?;\n                    write!(self.out, \"@builtin({builtin}) \")?;\n                }\n                Attribute::Stage(shader_stage) => {\n                    let stage_str = match shader_stage {\n                        ShaderStage::Vertex => \"vertex\",\n                        ShaderStage::Fragment => \"fragment\",\n                        ShaderStage::Compute => \"compute\",\n                        ShaderStage::Task => \"task\",\n                        //Handled by another variant in the Attribute enum, so this code should never be hit.\n                        ShaderStage::Mesh => unreachable!(),\n                        ShaderStage::RayGeneration => \"ray_generation\",\n                        ShaderStage::AnyHit => \"any_hit\",\n                        ShaderStage::ClosestHit => \"closest_hit\",\n                        ShaderStage::Miss => \"miss\",\n                    };\n\n                    write!(self.out, \"@{stage_str} \")?;\n                }\n                Attribute::WorkGroupSize(size) => {\n                    write!(\n                        self.out,\n                        \"@workgroup_size({}, {}, {}) \",\n                        size[0], size[1], size[2]\n                    )?;\n                }\n                Attribute::Binding(id) => write!(self.out, \"@binding({id}) \")?,\n                Attribute::Group(id) => write!(self.out, \"@group({id}) \")?,\n                Attribute::Invariant => write!(self.out, \"@invariant \")?,\n                Attribute::Interpolate(interpolation, sampling) => {\n                    if sampling.is_some() && sampling != Some(crate::Sampling::Center) {\n                        let interpolation = interpolation\n                            .unwrap_or(crate::Interpolation::Perspective)\n                            .to_wgsl();\n                        let sampling = sampling.unwrap_or(crate::Sampling::Center).to_wgsl();\n                        write!(self.out, \"@interpolate({interpolation}, {sampling}) \")?;\n                    } else if interpolation.is_some()\n                        && interpolation != Some(crate::Interpolation::Perspective)\n                    {\n                        let interpolation = interpolation\n                            .unwrap_or(crate::Interpolation::Perspective)\n                            .to_wgsl();\n                        write!(self.out, \"@interpolate({interpolation}) \")?;\n                    }\n                }\n                Attribute::MeshStage(ref name) => {\n                    write!(self.out, \"@mesh({name}) \")?;\n                }\n                Attribute::TaskPayload(ref payload_name) => {\n                    write!(self.out, \"@payload({payload_name}) \")?;\n                }\n                Attribute::PerPrimitive => write!(self.out, \"@per_primitive \")?,\n                Attribute::IncomingRayPayload(ref payload_name) => {\n                    write!(self.out, \"@incoming_payload({payload_name}) \")?;\n                }\n            };\n        }\n        Ok(())\n    }\n\n    /// Helper method used to write structs\n    /// Write the full declaration of a struct type.\n    ///\n    /// Write out a definition of the struct type referred to by\n    /// `handle` in `module`. The output will be an instance of the\n    /// `struct_decl` production in the WGSL grammar.\n    ///\n    /// Use `members` as the list of `handle`'s members. (This\n    /// function is usually called after matching a `TypeInner`, so\n    /// the callers already have the members at hand.)\n    fn write_struct(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Type>,\n        members: &[crate::StructMember],\n    ) -> BackendResult {\n        write!(self.out, \"struct {}\", self.names[&NameKey::Type(handle)])?;\n        write!(self.out, \" {{\")?;\n        writeln!(self.out)?;\n        for (index, member) in members.iter().enumerate() {\n            // The indentation is only for readability\n            write!(self.out, \"{}\", back::INDENT)?;\n            if let Some(ref binding) = member.binding {\n                self.write_attributes(&map_binding_to_attribute(binding))?;\n            }\n            // Write struct member name and type\n            let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];\n            write!(self.out, \"{member_name}: \")?;\n            self.write_type(module, member.ty)?;\n            write!(self.out, \",\")?;\n            writeln!(self.out)?;\n        }\n\n        writeln!(self.out, \"}}\")?;\n\n        Ok(())\n    }\n\n    fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {\n        // This actually can't be factored out into a nice constructor method,\n        // because the borrow checker needs to be able to see that the borrows\n        // of `self.names` and `self.out` are disjoint.\n        let type_context = WriterTypeContext {\n            module,\n            names: &self.names,\n        };\n        type_context.write_type(ty, &mut self.out)?;\n\n        Ok(())\n    }\n\n    fn write_type_resolution(\n        &mut self,\n        module: &Module,\n        resolution: &proc::TypeResolution,\n    ) -> BackendResult {\n        // This actually can't be factored out into a nice constructor method,\n        // because the borrow checker needs to be able to see that the borrows\n        // of `self.names` and `self.out` are disjoint.\n        let type_context = WriterTypeContext {\n            module,\n            names: &self.names,\n        };\n        type_context.write_type_resolution(resolution, &mut self.out)?;\n\n        Ok(())\n    }\n\n    /// Helper method used to write statements\n    ///\n    /// # Notes\n    /// Always adds a newline\n    fn write_stmt(\n        &mut self,\n        module: &Module,\n        stmt: &crate::Statement,\n        func_ctx: &back::FunctionCtx<'_>,\n        level: back::Level,\n    ) -> BackendResult {\n        use crate::{Expression, Statement};\n\n        match *stmt {\n            Statement::Emit(ref range) => {\n                for handle in range.clone() {\n                    let info = &func_ctx.info[handle];\n                    let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {\n                        // Front end provides names for all variables at the start of writing.\n                        // But we write them to step by step. We need to recache them\n                        // Otherwise, we could accidentally write variable name instead of full expression.\n                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.\n                        Some(self.namer.call(name))\n                    } else {\n                        let expr = &func_ctx.expressions[handle];\n                        let min_ref_count = expr.bake_ref_count();\n                        // Forcefully creating baking expressions in some cases to help with readability\n                        let required_baking_expr = match *expr {\n                            Expression::ImageLoad { .. }\n                            | Expression::ImageQuery { .. }\n                            | Expression::ImageSample { .. } => true,\n                            _ => false,\n                        };\n                        if min_ref_count <= info.ref_count || required_baking_expr {\n                            Some(Baked(handle).to_string())\n                        } else {\n                            None\n                        }\n                    };\n\n                    if let Some(name) = expr_name {\n                        write!(self.out, \"{level}\")?;\n                        self.start_named_expr(module, handle, func_ctx, &name)?;\n                        self.write_expr(module, handle, func_ctx)?;\n                        self.named_expressions.insert(handle, name);\n                        writeln!(self.out, \";\")?;\n                    }\n                }\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::If {\n                condition,\n                ref accept,\n                ref reject,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"if \")?;\n                self.write_expr(module, condition, func_ctx)?;\n                writeln!(self.out, \" {{\")?;\n\n                let l2 = level.next();\n                for sta in accept {\n                    // Increase indentation to help with readability\n                    self.write_stmt(module, sta, func_ctx, l2)?;\n                }\n\n                // If there are no statements in the reject block we skip writing it\n                // This is only for readability\n                if !reject.is_empty() {\n                    writeln!(self.out, \"{level}}} else {{\")?;\n\n                    for sta in reject {\n                        // Increase indentation to help with readability\n                        self.write_stmt(module, sta, func_ctx, l2)?;\n                    }\n                }\n\n                writeln!(self.out, \"{level}}}\")?\n            }\n            Statement::Return { value } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"return\")?;\n                if let Some(return_value) = value {\n                    // The leading space is important\n                    write!(self.out, \" \")?;\n                    self.write_expr(module, return_value, func_ctx)?;\n                }\n                writeln!(self.out, \";\")?;\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::Kill => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"discard;\")?\n            }\n            Statement::Store { pointer, value } => {\n                write!(self.out, \"{level}\")?;\n\n                let is_atomic_pointer = func_ctx\n                    .resolve_type(pointer, &module.types)\n                    .is_atomic_pointer(&module.types);\n\n                if is_atomic_pointer {\n                    write!(self.out, \"atomicStore(\")?;\n                    self.write_expr(module, pointer, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, value, func_ctx)?;\n                    write!(self.out, \")\")?;\n                } else {\n                    self.write_expr_with_indirection(\n                        module,\n                        pointer,\n                        func_ctx,\n                        Indirection::Reference,\n                    )?;\n                    write!(self.out, \" = \")?;\n                    self.write_expr(module, value, func_ctx)?;\n                }\n                writeln!(self.out, \";\")?\n            }\n            Statement::Call {\n                function,\n                ref arguments,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                if let Some(expr) = result {\n                    let name = Baked(expr).to_string();\n                    self.start_named_expr(module, expr, func_ctx, &name)?;\n                    self.named_expressions.insert(expr, name);\n                }\n                let func_name = &self.names[&NameKey::Function(function)];\n                write!(self.out, \"{func_name}(\")?;\n                for (index, &argument) in arguments.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    self.write_expr(module, argument, func_ctx)?;\n                }\n                writeln!(self.out, \");\")?\n            }\n            Statement::Atomic {\n                pointer,\n                ref fun,\n                value,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                if let Some(result) = result {\n                    let res_name = Baked(result).to_string();\n                    self.start_named_expr(module, result, func_ctx, &res_name)?;\n                    self.named_expressions.insert(result, res_name);\n                }\n\n                let fun_str = fun.to_wgsl();\n                write!(self.out, \"atomic{fun_str}(\")?;\n                self.write_expr(module, pointer, func_ctx)?;\n                if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, cmp, func_ctx)?;\n                }\n                write!(self.out, \", \")?;\n                self.write_expr(module, value, func_ctx)?;\n                writeln!(self.out, \");\")?\n            }\n            Statement::ImageAtomic {\n                image,\n                coordinate,\n                array_index,\n                ref fun,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let fun_str = fun.to_wgsl();\n                write!(self.out, \"textureAtomic{fun_str}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n                if let Some(array_index_expr) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, array_index_expr, func_ctx)?;\n                }\n                write!(self.out, \", \")?;\n                self.write_expr(module, value, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::WorkGroupUniformLoad { pointer, result } => {\n                write!(self.out, \"{level}\")?;\n                // TODO: Obey named expressions here.\n                let res_name = Baked(result).to_string();\n                self.start_named_expr(module, result, func_ctx, &res_name)?;\n                self.named_expressions.insert(result, res_name);\n                write!(self.out, \"workgroupUniformLoad(\")?;\n                self.write_expr(module, pointer, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::ImageStore {\n                image,\n                coordinate,\n                array_index,\n                value,\n            } => {\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"textureStore(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n                if let Some(array_index_expr) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, array_index_expr, func_ctx)?;\n                }\n                write!(self.out, \", \")?;\n                self.write_expr(module, value, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            // TODO: copy-paste from glsl-out\n            Statement::Block(ref block) => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"{{\")?;\n                for sta in block.iter() {\n                    // Increase the indentation to help with readability\n                    self.write_stmt(module, sta, func_ctx, level.next())?\n                }\n                writeln!(self.out, \"{level}}}\")?\n            }\n            Statement::Switch {\n                selector,\n                ref cases,\n            } => {\n                // Start the switch\n                write!(self.out, \"{level}\")?;\n                write!(self.out, \"switch \")?;\n                self.write_expr(module, selector, func_ctx)?;\n                writeln!(self.out, \" {{\")?;\n\n                let l2 = level.next();\n                let mut new_case = true;\n                for case in cases {\n                    if case.fall_through && !case.body.is_empty() {\n                        // TODO: we could do the same workaround as we did for the HLSL backend\n                        return Err(Error::Unimplemented(\n                            \"fall-through switch case block\".into(),\n                        ));\n                    }\n\n                    match case.value {\n                        crate::SwitchValue::I32(value) => {\n                            if new_case {\n                                write!(self.out, \"{l2}case \")?;\n                            }\n                            write!(self.out, \"{value}\")?;\n                        }\n                        crate::SwitchValue::U32(value) => {\n                            if new_case {\n                                write!(self.out, \"{l2}case \")?;\n                            }\n                            write!(self.out, \"{value}u\")?;\n                        }\n                        crate::SwitchValue::Default => {\n                            if new_case {\n                                if case.fall_through {\n                                    write!(self.out, \"{l2}case \")?;\n                                } else {\n                                    write!(self.out, \"{l2}\")?;\n                                }\n                            }\n                            write!(self.out, \"default\")?;\n                        }\n                    }\n\n                    new_case = !case.fall_through;\n\n                    if case.fall_through {\n                        write!(self.out, \", \")?;\n                    } else {\n                        writeln!(self.out, \": {{\")?;\n                    }\n\n                    for sta in case.body.iter() {\n                        self.write_stmt(module, sta, func_ctx, l2.next())?;\n                    }\n\n                    if !case.fall_through {\n                        writeln!(self.out, \"{l2}}}\")?;\n                    }\n                }\n\n                writeln!(self.out, \"{level}}}\")?\n            }\n            Statement::Loop {\n                ref body,\n                ref continuing,\n                break_if,\n            } => {\n                write!(self.out, \"{level}\")?;\n                writeln!(self.out, \"loop {{\")?;\n\n                let l2 = level.next();\n                for sta in body.iter() {\n                    self.write_stmt(module, sta, func_ctx, l2)?;\n                }\n\n                // The continuing is optional so we don't need to write it if\n                // it is empty, but the `break if` counts as a continuing statement\n                // so even if `continuing` is empty we must generate it if a\n                // `break if` exists\n                if !continuing.is_empty() || break_if.is_some() {\n                    writeln!(self.out, \"{l2}continuing {{\")?;\n                    for sta in continuing.iter() {\n                        self.write_stmt(module, sta, func_ctx, l2.next())?;\n                    }\n\n                    // The `break if` is always the last\n                    // statement of the `continuing` block\n                    if let Some(condition) = break_if {\n                        // The trailing space is important\n                        write!(self.out, \"{}break if \", l2.next())?;\n                        self.write_expr(module, condition, func_ctx)?;\n                        // Close the `break if` statement\n                        writeln!(self.out, \";\")?;\n                    }\n\n                    writeln!(self.out, \"{l2}}}\")?;\n                }\n\n                writeln!(self.out, \"{level}}}\")?\n            }\n            Statement::Break => {\n                writeln!(self.out, \"{level}break;\")?;\n            }\n            Statement::Continue => {\n                writeln!(self.out, \"{level}continue;\")?;\n            }\n            Statement::ControlBarrier(barrier) | Statement::MemoryBarrier(barrier) => {\n                if barrier.contains(crate::Barrier::STORAGE) {\n                    writeln!(self.out, \"{level}storageBarrier();\")?;\n                }\n\n                if barrier.contains(crate::Barrier::WORK_GROUP) {\n                    writeln!(self.out, \"{level}workgroupBarrier();\")?;\n                }\n\n                if barrier.contains(crate::Barrier::SUB_GROUP) {\n                    writeln!(self.out, \"{level}subgroupBarrier();\")?;\n                }\n\n                if barrier.contains(crate::Barrier::TEXTURE) {\n                    writeln!(self.out, \"{level}textureBarrier();\")?;\n                }\n            }\n            Statement::RayQuery { .. } => unreachable!(),\n            Statement::SubgroupBallot { result, predicate } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                self.start_named_expr(module, result, func_ctx, &res_name)?;\n                self.named_expressions.insert(result, res_name);\n\n                write!(self.out, \"subgroupBallot(\")?;\n                if let Some(predicate) = predicate {\n                    self.write_expr(module, predicate, func_ctx)?;\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupCollectiveOperation {\n                op,\n                collective_op,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                self.start_named_expr(module, result, func_ctx, &res_name)?;\n                self.named_expressions.insert(result, res_name);\n\n                match (collective_op, op) {\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {\n                        write!(self.out, \"subgroupAll(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {\n                        write!(self.out, \"subgroupAny(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupAdd(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupMul(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {\n                        write!(self.out, \"subgroupMax(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {\n                        write!(self.out, \"subgroupMin(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {\n                        write!(self.out, \"subgroupAnd(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {\n                        write!(self.out, \"subgroupOr(\")?\n                    }\n                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {\n                        write!(self.out, \"subgroupXor(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupExclusiveAdd(\")?\n                    }\n                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupExclusiveMul(\")?\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {\n                        write!(self.out, \"subgroupInclusiveAdd(\")?\n                    }\n                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {\n                        write!(self.out, \"subgroupInclusiveMul(\")?\n                    }\n                    _ => unimplemented!(),\n                }\n                self.write_expr(module, argument, func_ctx)?;\n                writeln!(self.out, \");\")?;\n            }\n            Statement::SubgroupGather {\n                mode,\n                argument,\n                result,\n            } => {\n                write!(self.out, \"{level}\")?;\n                let res_name = Baked(result).to_string();\n                self.start_named_expr(module, result, func_ctx, &res_name)?;\n                self.named_expressions.insert(result, res_name);\n\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {\n                        write!(self.out, \"subgroupBroadcastFirst(\")?;\n                    }\n                    crate::GatherMode::Broadcast(_) => {\n                        write!(self.out, \"subgroupBroadcast(\")?;\n                    }\n                    crate::GatherMode::Shuffle(_) => {\n                        write!(self.out, \"subgroupShuffle(\")?;\n                    }\n                    crate::GatherMode::ShuffleDown(_) => {\n                        write!(self.out, \"subgroupShuffleDown(\")?;\n                    }\n                    crate::GatherMode::ShuffleUp(_) => {\n                        write!(self.out, \"subgroupShuffleUp(\")?;\n                    }\n                    crate::GatherMode::ShuffleXor(_) => {\n                        write!(self.out, \"subgroupShuffleXor(\")?;\n                    }\n                    crate::GatherMode::QuadBroadcast(_) => {\n                        write!(self.out, \"quadBroadcast(\")?;\n                    }\n                    crate::GatherMode::QuadSwap(direction) => match direction {\n                        crate::Direction::X => {\n                            write!(self.out, \"quadSwapX(\")?;\n                        }\n                        crate::Direction::Y => {\n                            write!(self.out, \"quadSwapY(\")?;\n                        }\n                        crate::Direction::Diagonal => {\n                            write!(self.out, \"quadSwapDiagonal(\")?;\n                        }\n                    },\n                }\n                self.write_expr(module, argument, func_ctx)?;\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {}\n                    crate::GatherMode::Broadcast(index)\n                    | crate::GatherMode::Shuffle(index)\n                    | crate::GatherMode::ShuffleDown(index)\n                    | crate::GatherMode::ShuffleUp(index)\n                    | crate::GatherMode::ShuffleXor(index)\n                    | crate::GatherMode::QuadBroadcast(index) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, index, func_ctx)?;\n                    }\n                    crate::GatherMode::QuadSwap(_) => {}\n                }\n                writeln!(self.out, \");\")?;\n            }\n            Statement::CooperativeStore { target, ref data } => {\n                let suffix = if data.row_major { \"T\" } else { \"\" };\n                write!(self.out, \"{level}coopStore{suffix}(\")?;\n                self.write_expr(module, target, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, data.pointer, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, data.stride, func_ctx)?;\n                writeln!(self.out, \");\")?\n            }\n            Statement::RayPipelineFunction(fun) => match fun {\n                crate::RayPipelineFunction::TraceRay {\n                    acceleration_structure,\n                    descriptor,\n                    payload,\n                } => {\n                    write!(self.out, \"{level}traceRay(\")?;\n                    self.write_expr(module, acceleration_structure, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, descriptor, func_ctx)?;\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, payload, func_ctx)?;\n                    writeln!(self.out, \");\")?\n                }\n            },\n        }\n\n        Ok(())\n    }\n\n    /// Return the sort of indirection that `expr`'s plain form evaluates to.\n    ///\n    /// An expression's 'plain form' is the most general rendition of that\n    /// expression into WGSL, lacking `&` or `*` operators:\n    ///\n    /// - The plain form of `LocalVariable(x)` is simply `x`, which is a reference\n    ///   to the local variable's storage.\n    ///\n    /// - The plain form of `GlobalVariable(g)` is simply `g`, which is usually a\n    ///   reference to the global variable's storage. However, globals in the\n    ///   `Handle` address space are immutable, and `GlobalVariable` expressions for\n    ///   those produce the value directly, not a pointer to it. Such\n    ///   `GlobalVariable` expressions are `Ordinary`.\n    ///\n    /// - `Access` and `AccessIndex` are `Reference` when their `base` operand is a\n    ///   pointer. If they are applied directly to a composite value, they are\n    ///   `Ordinary`.\n    ///\n    /// Note that `FunctionArgument` expressions are never `Reference`, even when\n    /// the argument's type is `Pointer`. `FunctionArgument` always evaluates to the\n    /// argument's value directly, so any pointer it produces is merely the value\n    /// passed by the caller.\n    fn plain_form_indirection(\n        &self,\n        expr: Handle<crate::Expression>,\n        module: &Module,\n        func_ctx: &back::FunctionCtx<'_>,\n    ) -> Indirection {\n        use crate::Expression as Ex;\n\n        // Named expressions are `let` expressions, which apply the Load Rule,\n        // so if their type is a Naga pointer, then that must be a WGSL pointer\n        // as well.\n        if self.named_expressions.contains_key(&expr) {\n            return Indirection::Ordinary;\n        }\n\n        match func_ctx.expressions[expr] {\n            Ex::LocalVariable(_) => Indirection::Reference,\n            Ex::GlobalVariable(handle) => {\n                let global = &module.global_variables[handle];\n                match global.space {\n                    crate::AddressSpace::Handle => Indirection::Ordinary,\n                    _ => Indirection::Reference,\n                }\n            }\n            Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {\n                let base_ty = func_ctx.resolve_type(base, &module.types);\n                match *base_ty {\n                    TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {\n                        Indirection::Reference\n                    }\n                    _ => Indirection::Ordinary,\n                }\n            }\n            _ => Indirection::Ordinary,\n        }\n    }\n\n    fn start_named_expr(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx,\n        name: &str,\n    ) -> BackendResult {\n        // Write variable name\n        write!(self.out, \"let {name}\")?;\n        if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {\n            write!(self.out, \": \")?;\n            // Write variable type\n            self.write_type_resolution(module, &func_ctx.info[handle].ty)?;\n        }\n\n        write!(self.out, \" = \")?;\n        Ok(())\n    }\n\n    /// Write the ordinary WGSL form of `expr`.\n    ///\n    /// See `write_expr_with_indirection` for details.\n    fn write_expr(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx<'_>,\n    ) -> BackendResult {\n        self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)\n    }\n\n    /// Write `expr` as a WGSL expression with the requested indirection.\n    ///\n    /// In terms of the WGSL grammar, the resulting expression is a\n    /// `singular_expression`. It may be parenthesized. This makes it suitable\n    /// for use as the operand of a unary or binary operator without worrying\n    /// about precedence.\n    ///\n    /// This does not produce newlines or indentation.\n    ///\n    /// The `requested` argument indicates (roughly) whether Naga\n    /// `Pointer`-valued expressions represent WGSL references or pointers. See\n    /// `Indirection` for details.\n    fn write_expr_with_indirection(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx<'_>,\n        requested: Indirection,\n    ) -> BackendResult {\n        // If the plain form of the expression is not what we need, emit the\n        // operator necessary to correct that.\n        let plain = self.plain_form_indirection(expr, module, func_ctx);\n        log::trace!(\n            \"expression {:?}={:?} is {:?}, expected {:?}\",\n            expr,\n            func_ctx.expressions[expr],\n            plain,\n            requested,\n        );\n        match (requested, plain) {\n            (Indirection::Ordinary, Indirection::Reference) => {\n                write!(self.out, \"(&\")?;\n                self.write_expr_plain_form(module, expr, func_ctx, plain)?;\n                write!(self.out, \")\")?;\n            }\n            (Indirection::Reference, Indirection::Ordinary) => {\n                write!(self.out, \"(*\")?;\n                self.write_expr_plain_form(module, expr, func_ctx, plain)?;\n                write!(self.out, \")\")?;\n            }\n            (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,\n        }\n\n        Ok(())\n    }\n\n    fn write_const_expression(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        arena: &crate::Arena<crate::Expression>,\n    ) -> BackendResult {\n        self.write_possibly_const_expression(module, expr, arena, |writer, expr| {\n            writer.write_const_expression(module, expr, arena)\n        })\n    }\n\n    fn write_possibly_const_expression<E>(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        expressions: &crate::Arena<crate::Expression>,\n        write_expression: E,\n    ) -> BackendResult\n    where\n        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,\n    {\n        use crate::Expression;\n\n        match expressions[expr] {\n            Expression::Literal(literal) => match literal {\n                crate::Literal::F16(value) => write!(self.out, \"{value}h\")?,\n                crate::Literal::F32(value) => write!(self.out, \"{value}f\")?,\n                crate::Literal::U32(value) => write!(self.out, \"{value}u\")?,\n                crate::Literal::I32(value) => {\n                    // `-2147483648i` is not valid WGSL. The most negative `i32`\n                    // value can only be expressed in WGSL using AbstractInt and\n                    // a unary negation operator.\n                    if value == i32::MIN {\n                        write!(self.out, \"i32({value})\")?;\n                    } else {\n                        write!(self.out, \"{value}i\")?;\n                    }\n                }\n                crate::Literal::Bool(value) => write!(self.out, \"{value}\")?,\n                crate::Literal::F64(value) => write!(self.out, \"{value:?}lf\")?,\n                crate::Literal::I64(value) => {\n                    // `-9223372036854775808li` is not valid WGSL. Nor can we simply use the\n                    // AbstractInt trick above, as AbstractInt also cannot represent\n                    // `9223372036854775808`. Instead construct the second most negative\n                    // AbstractInt, subtract one from it, then cast to i64.\n                    if value == i64::MIN {\n                        write!(self.out, \"i64({} - 1)\", value + 1)?;\n                    } else {\n                        write!(self.out, \"{value}li\")?;\n                    }\n                }\n                crate::Literal::U64(value) => write!(self.out, \"{value:?}lu\")?,\n                crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {\n                    return Err(Error::Custom(\n                        \"Abstract types should not appear in IR presented to backends\".into(),\n                    ));\n                }\n            },\n            Expression::Constant(handle) => {\n                let constant = &module.constants[handle];\n                if constant.name.is_some() {\n                    write!(self.out, \"{}\", self.names[&NameKey::Constant(handle)])?;\n                } else {\n                    self.write_const_expression(module, constant.init, &module.global_expressions)?;\n                }\n            }\n            Expression::ZeroValue(ty) => {\n                self.write_type(module, ty)?;\n                write!(self.out, \"()\")?;\n            }\n            Expression::Compose { ty, ref components } => {\n                self.write_type(module, ty)?;\n                write!(self.out, \"(\")?;\n                for (index, component) in components.iter().enumerate() {\n                    if index != 0 {\n                        write!(self.out, \", \")?;\n                    }\n                    write_expression(self, *component)?;\n                }\n                write!(self.out, \")\")?\n            }\n            Expression::Splat { size, value } => {\n                let size = common::vector_size_str(size);\n                write!(self.out, \"vec{size}(\")?;\n                write_expression(self, value)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::Override(handle) => {\n                write!(self.out, \"{}\", self.names[&NameKey::Override(handle)])?;\n            }\n            _ => unreachable!(),\n        }\n\n        Ok(())\n    }\n\n    /// Write the 'plain form' of `expr`.\n    ///\n    /// An expression's 'plain form' is the most general rendition of that\n    /// expression into WGSL, lacking `&` or `*` operators. The plain forms of\n    /// `LocalVariable(x)` and `GlobalVariable(g)` are simply `x` and `g`. Such\n    /// Naga expressions represent both WGSL pointers and references; it's the\n    /// caller's responsibility to distinguish those cases appropriately.\n    fn write_expr_plain_form(\n        &mut self,\n        module: &Module,\n        expr: Handle<crate::Expression>,\n        func_ctx: &back::FunctionCtx<'_>,\n        indirection: Indirection,\n    ) -> BackendResult {\n        use crate::Expression;\n\n        if let Some(name) = self.named_expressions.get(&expr) {\n            write!(self.out, \"{name}\")?;\n            return Ok(());\n        }\n\n        let expression = &func_ctx.expressions[expr];\n\n        // Write the plain WGSL form of a Naga expression.\n        //\n        // The plain form of `LocalVariable` and `GlobalVariable` expressions is\n        // simply the variable name; `*` and `&` operators are never emitted.\n        //\n        // The plain form of `Access` and `AccessIndex` expressions are WGSL\n        // `postfix_expression` forms for member/component access and\n        // subscripting.\n        match *expression {\n            Expression::Literal(_)\n            | Expression::Constant(_)\n            | Expression::ZeroValue(_)\n            | Expression::Compose { .. }\n            | Expression::Splat { .. } => {\n                self.write_possibly_const_expression(\n                    module,\n                    expr,\n                    func_ctx.expressions,\n                    |writer, expr| writer.write_expr(module, expr, func_ctx),\n                )?;\n            }\n            Expression::Override(handle) => {\n                write!(self.out, \"{}\", self.names[&NameKey::Override(handle)])?;\n            }\n            Expression::FunctionArgument(pos) => {\n                let name_key = func_ctx.argument_key(pos);\n                let name = &self.names[&name_key];\n                write!(self.out, \"{name}\")?;\n            }\n            Expression::Binary { op, left, right } => {\n                write!(self.out, \"(\")?;\n                self.write_expr(module, left, func_ctx)?;\n                write!(self.out, \" {} \", back::binary_operation_str(op))?;\n                self.write_expr(module, right, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::Access { base, index } => {\n                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;\n                write!(self.out, \"[\")?;\n                self.write_expr(module, index, func_ctx)?;\n                write!(self.out, \"]\")?\n            }\n            Expression::AccessIndex { base, index } => {\n                let base_ty_res = &func_ctx.info[base].ty;\n                let mut resolved = base_ty_res.inner_with(&module.types);\n\n                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;\n\n                let base_ty_handle = match *resolved {\n                    TypeInner::Pointer { base, space: _ } => {\n                        resolved = &module.types[base].inner;\n                        Some(base)\n                    }\n                    _ => base_ty_res.handle(),\n                };\n\n                match *resolved {\n                    TypeInner::Vector { .. } => {\n                        // Write vector access as a swizzle\n                        write!(self.out, \".{}\", back::COMPONENTS[index as usize])?\n                    }\n                    TypeInner::Matrix { .. }\n                    | TypeInner::Array { .. }\n                    | TypeInner::BindingArray { .. }\n                    | TypeInner::ValuePointer { .. } => write!(self.out, \"[{index}]\")?,\n                    TypeInner::Struct { .. } => {\n                        // This will never panic in case the type is a `Struct`, this is not true\n                        // for other types so we can only check while inside this match arm\n                        let ty = base_ty_handle.unwrap();\n\n                        write!(\n                            self.out,\n                            \".{}\",\n                            &self.names[&NameKey::StructMember(ty, index)]\n                        )?\n                    }\n                    ref other => return Err(Error::Custom(format!(\"Cannot index {other:?}\"))),\n                }\n            }\n            Expression::ImageSample {\n                image,\n                sampler,\n                gather: None,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n            } => {\n                use crate::SampleLevel as Sl;\n\n                let suffix_cmp = match depth_ref {\n                    Some(_) => \"Compare\",\n                    None => \"\",\n                };\n                let suffix_level = match level {\n                    Sl::Auto => \"\",\n                    Sl::Zero if clamp_to_edge => \"BaseClampToEdge\",\n                    Sl::Zero | Sl::Exact(_) => \"Level\",\n                    Sl::Bias(_) => \"Bias\",\n                    Sl::Gradient { .. } => \"Grad\",\n                };\n\n                write!(self.out, \"textureSample{suffix_cmp}{suffix_level}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, sampler, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n\n                if let Some(array_index) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, array_index, func_ctx)?;\n                }\n\n                if let Some(depth_ref) = depth_ref {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, depth_ref, func_ctx)?;\n                }\n\n                match level {\n                    Sl::Auto => {}\n                    Sl::Zero => {\n                        // Level 0 is implied for depth comparison and BaseClampToEdge\n                        if depth_ref.is_none() && !clamp_to_edge {\n                            write!(self.out, \", 0.0\")?;\n                        }\n                    }\n                    Sl::Exact(expr) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, expr, func_ctx)?;\n                    }\n                    Sl::Bias(expr) => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, expr, func_ctx)?;\n                    }\n                    Sl::Gradient { x, y } => {\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, x, func_ctx)?;\n                        write!(self.out, \", \")?;\n                        self.write_expr(module, y, func_ctx)?;\n                    }\n                }\n\n                if let Some(offset) = offset {\n                    write!(self.out, \", \")?;\n                    self.write_const_expression(module, offset, func_ctx.expressions)?;\n                }\n\n                write!(self.out, \")\")?;\n            }\n\n            Expression::ImageSample {\n                image,\n                sampler,\n                gather: Some(component),\n                coordinate,\n                array_index,\n                offset,\n                level: _,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                let suffix_cmp = match depth_ref {\n                    Some(_) => \"Compare\",\n                    None => \"\",\n                };\n\n                write!(self.out, \"textureGather{suffix_cmp}(\")?;\n                match *func_ctx.resolve_type(image, &module.types) {\n                    TypeInner::Image {\n                        class: crate::ImageClass::Depth { multi: _ },\n                        ..\n                    } => {}\n                    _ => {\n                        write!(self.out, \"{}, \", component as u8)?;\n                    }\n                }\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, sampler, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n\n                if let Some(array_index) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, array_index, func_ctx)?;\n                }\n\n                if let Some(depth_ref) = depth_ref {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, depth_ref, func_ctx)?;\n                }\n\n                if let Some(offset) = offset {\n                    write!(self.out, \", \")?;\n                    self.write_const_expression(module, offset, func_ctx.expressions)?;\n                }\n\n                write!(self.out, \")\")?;\n            }\n            Expression::ImageQuery { image, query } => {\n                use crate::ImageQuery as Iq;\n\n                let texture_function = match query {\n                    Iq::Size { .. } => \"textureDimensions\",\n                    Iq::NumLevels => \"textureNumLevels\",\n                    Iq::NumLayers => \"textureNumLayers\",\n                    Iq::NumSamples => \"textureNumSamples\",\n                };\n\n                write!(self.out, \"{texture_function}(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                if let Iq::Size { level: Some(level) } = query {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, level, func_ctx)?;\n                };\n                write!(self.out, \")\")?;\n            }\n\n            Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                write!(self.out, \"textureLoad(\")?;\n                self.write_expr(module, image, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, coordinate, func_ctx)?;\n                if let Some(array_index) = array_index {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, array_index, func_ctx)?;\n                }\n                if let Some(index) = sample.or(level) {\n                    write!(self.out, \", \")?;\n                    self.write_expr(module, index, func_ctx)?;\n                }\n                write!(self.out, \")\")?;\n            }\n            Expression::GlobalVariable(handle) => {\n                let name = &self.names[&NameKey::GlobalVariable(handle)];\n                write!(self.out, \"{name}\")?;\n            }\n\n            Expression::As {\n                expr,\n                kind,\n                convert,\n            } => {\n                let inner = func_ctx.resolve_type(expr, &module.types);\n                match *inner {\n                    TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    } => {\n                        let scalar = crate::Scalar {\n                            kind,\n                            width: convert.unwrap_or(scalar.width),\n                        };\n                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;\n                        write!(\n                            self.out,\n                            \"mat{}x{}<{}>\",\n                            common::vector_size_str(columns),\n                            common::vector_size_str(rows),\n                            scalar_kind_str\n                        )?;\n                    }\n                    TypeInner::Vector {\n                        size,\n                        scalar: crate::Scalar { width, .. },\n                    } => {\n                        let scalar = crate::Scalar {\n                            kind,\n                            width: convert.unwrap_or(width),\n                        };\n                        let vector_size_str = common::vector_size_str(size);\n                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;\n                        if convert.is_some() {\n                            write!(self.out, \"vec{vector_size_str}<{scalar_kind_str}>\")?;\n                        } else {\n                            write!(self.out, \"bitcast<vec{vector_size_str}<{scalar_kind_str}>>\")?;\n                        }\n                    }\n                    TypeInner::Scalar(crate::Scalar { width, .. }) => {\n                        let scalar = crate::Scalar {\n                            kind,\n                            width: convert.unwrap_or(width),\n                        };\n                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;\n                        if convert.is_some() {\n                            write!(self.out, \"{scalar_kind_str}\")?\n                        } else {\n                            write!(self.out, \"bitcast<{scalar_kind_str}>\")?\n                        }\n                    }\n                    _ => {\n                        return Err(Error::Unimplemented(format!(\n                            \"write_expr expression::as {inner:?}\"\n                        )));\n                    }\n                };\n                write!(self.out, \"(\")?;\n                self.write_expr(module, expr, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::Load { pointer } => {\n                let is_atomic_pointer = func_ctx\n                    .resolve_type(pointer, &module.types)\n                    .is_atomic_pointer(&module.types);\n\n                if is_atomic_pointer {\n                    write!(self.out, \"atomicLoad(\")?;\n                    self.write_expr(module, pointer, func_ctx)?;\n                    write!(self.out, \")\")?;\n                } else {\n                    self.write_expr_with_indirection(\n                        module,\n                        pointer,\n                        func_ctx,\n                        Indirection::Reference,\n                    )?;\n                }\n            }\n            Expression::LocalVariable(handle) => {\n                write!(self.out, \"{}\", self.names[&func_ctx.name_key(handle)])?\n            }\n            Expression::ArrayLength(expr) => {\n                write!(self.out, \"arrayLength(\")?;\n                self.write_expr(module, expr, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n\n            Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                use crate::MathFunction as Mf;\n\n                enum Function {\n                    Regular(&'static str),\n                    InversePolyfill(InversePolyfill),\n                }\n\n                let function = match fun.try_to_wgsl() {\n                    Some(name) => Function::Regular(name),\n                    None => match fun {\n                        Mf::Inverse => {\n                            let ty = func_ctx.resolve_type(arg, &module.types);\n                            let Some(overload) = InversePolyfill::find_overload(ty) else {\n                                return Err(Error::unsupported(\"math function\", fun));\n                            };\n\n                            Function::InversePolyfill(overload)\n                        }\n                        _ => return Err(Error::unsupported(\"math function\", fun)),\n                    },\n                };\n\n                match function {\n                    Function::Regular(fun_name) => {\n                        write!(self.out, \"{fun_name}(\")?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {\n                            write!(self.out, \", \")?;\n                            self.write_expr(module, arg, func_ctx)?;\n                        }\n                        write!(self.out, \")\")?\n                    }\n                    Function::InversePolyfill(inverse) => {\n                        write!(self.out, \"{}(\", inverse.fun_name)?;\n                        self.write_expr(module, arg, func_ctx)?;\n                        write!(self.out, \")\")?;\n                        self.required_polyfills.insert(inverse);\n                    }\n                }\n            }\n\n            Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                self.write_expr(module, vector, func_ctx)?;\n                write!(self.out, \".\")?;\n                for &sc in pattern[..size as usize].iter() {\n                    self.out.write_char(back::COMPONENTS[sc as usize])?;\n                }\n            }\n            Expression::Unary { op, expr } => {\n                let unary = match op {\n                    crate::UnaryOperator::Negate => \"-\",\n                    crate::UnaryOperator::LogicalNot => \"!\",\n                    crate::UnaryOperator::BitwiseNot => \"~\",\n                };\n\n                write!(self.out, \"{unary}(\")?;\n                self.write_expr(module, expr, func_ctx)?;\n\n                write!(self.out, \")\")?\n            }\n\n            Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                write!(self.out, \"select(\")?;\n                self.write_expr(module, reject, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, accept, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, condition, func_ctx)?;\n                write!(self.out, \")\")?\n            }\n            Expression::Derivative { axis, ctrl, expr } => {\n                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n                let op = match (axis, ctrl) {\n                    (Axis::X, Ctrl::Coarse) => \"dpdxCoarse\",\n                    (Axis::X, Ctrl::Fine) => \"dpdxFine\",\n                    (Axis::X, Ctrl::None) => \"dpdx\",\n                    (Axis::Y, Ctrl::Coarse) => \"dpdyCoarse\",\n                    (Axis::Y, Ctrl::Fine) => \"dpdyFine\",\n                    (Axis::Y, Ctrl::None) => \"dpdy\",\n                    (Axis::Width, Ctrl::Coarse) => \"fwidthCoarse\",\n                    (Axis::Width, Ctrl::Fine) => \"fwidthFine\",\n                    (Axis::Width, Ctrl::None) => \"fwidth\",\n                };\n                write!(self.out, \"{op}(\")?;\n                self.write_expr(module, expr, func_ctx)?;\n                write!(self.out, \")\")?\n            }\n            Expression::Relational { fun, argument } => {\n                use crate::RelationalFunction as Rf;\n\n                let fun_name = match fun {\n                    Rf::All => \"all\",\n                    Rf::Any => \"any\",\n                    _ => return Err(Error::UnsupportedRelationalFunction(fun)),\n                };\n                write!(self.out, \"{fun_name}(\")?;\n\n                self.write_expr(module, argument, func_ctx)?;\n\n                write!(self.out, \")\")?\n            }\n            // Not supported yet\n            Expression::RayQueryGetIntersection { .. }\n            | Expression::RayQueryVertexPositions { .. } => unreachable!(),\n            // Nothing to do here, since call expression already cached\n            Expression::CallResult(_)\n            | Expression::AtomicResult { .. }\n            | Expression::RayQueryProceedResult\n            | Expression::SubgroupBallotResult\n            | Expression::SubgroupOperationResult { .. }\n            | Expression::WorkGroupUniformLoadResult { .. } => {}\n            Expression::CooperativeLoad {\n                columns,\n                rows,\n                role,\n                ref data,\n            } => {\n                let suffix = if data.row_major { \"T\" } else { \"\" };\n                let scalar = func_ctx.info[data.pointer]\n                    .ty\n                    .inner_with(&module.types)\n                    .pointer_base_type()\n                    .unwrap()\n                    .inner_with(&module.types)\n                    .scalar()\n                    .unwrap();\n                write!(\n                    self.out,\n                    \"coopLoad{suffix}<coop_mat{}x{}<{},{:?}>>(\",\n                    columns as u32,\n                    rows as u32,\n                    scalar.try_to_wgsl().unwrap(),\n                    role,\n                )?;\n                self.write_expr(module, data.pointer, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, data.stride, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n            Expression::CooperativeMultiplyAdd { a, b, c } => {\n                write!(self.out, \"coopMultiplyAdd(\")?;\n                self.write_expr(module, a, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, b, func_ctx)?;\n                write!(self.out, \", \")?;\n                self.write_expr(module, c, func_ctx)?;\n                write!(self.out, \")\")?;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Helper method used to write global variables\n    /// # Notes\n    /// Always adds a newline\n    fn write_global(\n        &mut self,\n        module: &Module,\n        global: &crate::GlobalVariable,\n        handle: Handle<crate::GlobalVariable>,\n    ) -> BackendResult {\n        // Write group and binding attributes if present\n        if let Some(ref binding) = global.binding {\n            self.write_attributes(&[\n                Attribute::Group(binding.group),\n                Attribute::Binding(binding.binding),\n            ])?;\n            writeln!(self.out)?;\n        }\n\n        if global\n            .memory_decorations\n            .contains(crate::MemoryDecorations::COHERENT)\n        {\n            write!(self.out, \"@coherent \")?;\n        }\n        if global\n            .memory_decorations\n            .contains(crate::MemoryDecorations::VOLATILE)\n        {\n            write!(self.out, \"@volatile \")?;\n        }\n\n        // First write global name and address space if supported\n        write!(self.out, \"var\")?;\n        let (address, maybe_access) = address_space_str(global.space);\n        if let Some(space) = address {\n            write!(self.out, \"<{space}\")?;\n            if let Some(access) = maybe_access {\n                write!(self.out, \", {access}\")?;\n            }\n            write!(self.out, \">\")?;\n        }\n        write!(\n            self.out,\n            \" {}: \",\n            &self.names[&NameKey::GlobalVariable(handle)]\n        )?;\n\n        // Write global type\n        self.write_type(module, global.ty)?;\n\n        // Write initializer\n        if let Some(init) = global.init {\n            write!(self.out, \" = \")?;\n            self.write_const_expression(module, init, &module.global_expressions)?;\n        }\n\n        // End with semicolon\n        writeln!(self.out, \";\")?;\n\n        Ok(())\n    }\n\n    /// Helper method used to write global constants\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_global_constant(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Constant>,\n    ) -> BackendResult {\n        let name = &self.names[&NameKey::Constant(handle)];\n        // First write only constant name\n        write!(self.out, \"const {name}: \")?;\n        self.write_type(module, module.constants[handle].ty)?;\n        write!(self.out, \" = \")?;\n        let init = module.constants[handle].init;\n        self.write_const_expression(module, init, &module.global_expressions)?;\n        writeln!(self.out, \";\")?;\n\n        Ok(())\n    }\n\n    /// Helper method used to write overrides\n    ///\n    /// # Notes\n    /// Ends in a newline\n    fn write_override(\n        &mut self,\n        module: &Module,\n        handle: Handle<crate::Override>,\n    ) -> BackendResult {\n        let override_ = &module.overrides[handle];\n        let name = &self.names[&NameKey::Override(handle)];\n\n        // Write @id attribute if present\n        if let Some(id) = override_.id {\n            write!(self.out, \"@id({id}) \")?;\n        }\n\n        // Write override declaration\n        write!(self.out, \"override {name}: \")?;\n        self.write_type(module, override_.ty)?;\n\n        // Write initializer if present\n        if let Some(init) = override_.init {\n            write!(self.out, \" = \")?;\n            self.write_const_expression(module, init, &module.global_expressions)?;\n        }\n\n        writeln!(self.out, \";\")?;\n\n        Ok(())\n    }\n\n    // See https://github.com/rust-lang/rust-clippy/issues/4979.\n    pub fn finish(self) -> W {\n        self.out\n    }\n}\n\nstruct WriterTypeContext<'m> {\n    module: &'m Module,\n    names: &'m crate::FastHashMap<NameKey, String>,\n}\n\nimpl TypeContext for WriterTypeContext<'_> {\n    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {\n        &self.module.types[handle]\n    }\n\n    fn type_name(&self, handle: Handle<crate::Type>) -> &str {\n        self.names[&NameKey::Type(handle)].as_str()\n    }\n\n    fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {\n        unreachable!(\"the WGSL back end should always provide type handles\");\n    }\n\n    fn write_override<W: Write>(\n        &self,\n        handle: Handle<crate::Override>,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        write!(out, \"{}\", self.names[&NameKey::Override(handle)])\n    }\n\n    fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {\n        unreachable!(\"backends should only be passed validated modules\");\n    }\n\n    fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {\n        unreachable!(\"backends should only be passed validated modules\");\n    }\n}\n\nfn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {\n    match *binding {\n        crate::Binding::BuiltIn(built_in) => {\n            if let crate::BuiltIn::Position { invariant: true } = built_in {\n                vec![Attribute::BuiltIn(built_in), Attribute::Invariant]\n            } else {\n                vec![Attribute::BuiltIn(built_in)]\n            }\n        }\n        crate::Binding::Location {\n            location,\n            interpolation,\n            sampling,\n            blend_src: None,\n            per_primitive,\n        } => {\n            let mut attrs = vec![\n                Attribute::Location(location),\n                Attribute::Interpolate(interpolation, sampling),\n            ];\n            if per_primitive {\n                attrs.push(Attribute::PerPrimitive);\n            }\n            attrs\n        }\n        crate::Binding::Location {\n            location,\n            interpolation,\n            sampling,\n            blend_src: Some(blend_src),\n            per_primitive,\n        } => {\n            let mut attrs = vec![\n                Attribute::Location(location),\n                Attribute::BlendSrc(blend_src),\n                Attribute::Interpolate(interpolation, sampling),\n            ];\n            if per_primitive {\n                attrs.push(Attribute::PerPrimitive);\n            }\n            attrs\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/common/diagnostic_debug.rs",
    "content": "//! Displaying Naga IR terms in debugging output.\n\n#[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\nuse crate::common::wgsl::TypeContext;\n\nuse crate::proc::TypeResolution;\nuse crate::{Handle, Scalar, Type, TypeInner, UniqueArena};\n\nuse core::fmt;\n\n/// A wrapper for displaying Naga IR terms in debugging output.\n///\n/// This is like [`DiagnosticDisplay`], but requires weaker context\n/// and produces correspondingly lower-fidelity output. For example,\n/// this cannot show the override names for override-sized array\n/// lengths.\n///\n/// [`DiagnosticDisplay`]: super::DiagnosticDisplay\npub struct DiagnosticDebug<T>(pub T);\n\nimpl fmt::Debug for DiagnosticDebug<(Handle<Type>, &UniqueArena<Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (handle, ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type(handle, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{handle:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Debug for DiagnosticDebug<(&TypeInner, &UniqueArena<Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (inner, ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type_inner(inner, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{inner:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Debug for DiagnosticDebug<(&TypeResolution, &UniqueArena<Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (resolution, ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type_resolution(resolution, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{resolution:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Debug for DiagnosticDebug<Scalar> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let scalar = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        f.write_str(&crate::common::wgsl::TryToWgsl::to_wgsl_for_diagnostics(\n            scalar,\n        ))?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        write!(f, \"{scalar:?}\")?;\n\n        Ok(())\n    }\n}\n\npub trait ForDebug: Sized {\n    /// Format this type using [`core::fmt::Debug`].\n    ///\n    /// Return a value that implements the [`core::fmt::Debug`] trait\n    /// by displaying `self` in a language-appropriate way. For\n    /// example:\n    ///\n    ///     # use naga::common::ForDebug;\n    ///     # let scalar: naga::Scalar = naga::Scalar::F32;\n    ///     log::debug!(\"My scalar: {:?}\", scalar.for_debug());\n    fn for_debug(self) -> DiagnosticDebug<Self> {\n        DiagnosticDebug(self)\n    }\n}\n\nimpl ForDebug for Scalar {}\n\npub trait ForDebugWithTypes: Sized {\n    /// Format this type using [`core::fmt::Debug`].\n    ///\n    /// Given an arena to look up type handles in, return a value that\n    /// implements the [`core::fmt::Debug`] trait by displaying `self`\n    /// in a language-appropriate way. For example:\n    ///\n    ///     # use naga::{Span, Type, TypeInner, Scalar, UniqueArena};\n    ///     # use naga::common::ForDebugWithTypes;\n    ///     # let mut types = UniqueArena::<Type>::default();\n    ///     # let inner = TypeInner::Scalar(Scalar::F32);\n    ///     # let span = Span::UNDEFINED;\n    ///     # let handle = types.insert(Type { name: None, inner }, span);\n    ///     log::debug!(\"My type: {:?}\", handle.for_debug(&types));\n    fn for_debug(self, types: &UniqueArena<Type>) -> DiagnosticDebug<(Self, &UniqueArena<Type>)> {\n        DiagnosticDebug((self, types))\n    }\n}\n\nimpl ForDebugWithTypes for Handle<Type> {}\nimpl ForDebugWithTypes for &TypeInner {}\nimpl ForDebugWithTypes for &TypeResolution {}\n"
  },
  {
    "path": "naga/src/common/diagnostic_display.rs",
    "content": "//! Displaying Naga IR terms in diagnostic output.\n\nuse crate::proc::{GlobalCtx, Rule, TypeResolution};\nuse crate::{Handle, Scalar, Type};\n\n#[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\nuse crate::common::wgsl::TypeContext;\n\nuse core::fmt;\n\n/// A wrapper for displaying Naga IR terms in diagnostic output.\n///\n/// For some Naga IR type `T`, `DiagnosticDisplay<T>` implements\n/// [`core::fmt::Display`] in a way that displays values of type `T`\n/// appropriately for diagnostic messages presented to human readers.\n///\n/// For example, the implementation of [`Display`] for\n/// `DiagnosticDisplay<Scalar>` formats the type represented by the\n/// given [`Scalar`] appropriately for users.\n///\n/// Some types like `Handle<Type>` require contextual information like\n/// a type arena to be displayed. In such cases, we implement [`Display`]\n/// for a type like `DiagnosticDisplay<(Handle<Type>, GlobalCtx)>`, where\n/// the [`GlobalCtx`] type provides the necessary context.\n///\n/// Do not implement this type for [`TypeInner`], as that does not\n/// have enough information to display struct types correctly.\n///\n/// If you only need debugging output, [`DiagnosticDebug`] uses\n/// easier-to-obtain context types but still does a good enough job\n/// for logging or debugging.\n///\n/// [`Display`]: core::fmt::Display\n/// [`GlobalCtx`]: crate::proc::GlobalCtx\n/// [`TypeInner`]: crate::ir::TypeInner\n/// [`DiagnosticDebug`]: super::DiagnosticDebug\n///\n/// ## Language-sensitive diagnostics\n///\n/// Diagnostic output ought to depend on the source language from\n/// which the IR was produced: diagnostics resulting from processing\n/// GLSL code should use GLSL type syntax, for example. That means\n/// that `DiagnosticDisplay` ought to include some indication of which\n/// notation to use.\n///\n/// For the moment, only WGSL output is implemented, so\n/// `DiagnosticDisplay` lacks any support for this (#7268). However,\n/// the plan is that all language-independent code in Naga should use\n/// `DiagnosticDisplay` wherever appropriate, such that when its\n/// definition is expanded to include some indication of the right\n/// source language to use, any use site that does not supply this\n/// indication will provoke a compile-time error.\npub struct DiagnosticDisplay<T>(pub T);\n\nimpl fmt::Display for DiagnosticDisplay<(&TypeResolution, GlobalCtx<'_>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (resolution, ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type_resolution(resolution, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{resolution:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Display for DiagnosticDisplay<(Handle<Type>, GlobalCtx<'_>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (handle, ref ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type(handle, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{handle:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Display for DiagnosticDisplay<(&str, &Rule, GlobalCtx<'_>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (name, rule, ref ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        ctx.write_type_rule(name, rule, f)?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{name}({:?}) -> {:?}\", rule.arguments, rule.conclusion)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Display for DiagnosticDisplay<Scalar> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let scalar = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        f.write_str(&crate::common::wgsl::TryToWgsl::to_wgsl_for_diagnostics(\n            scalar,\n        ))?;\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        write!(f, \"{scalar:?}\")?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/common/mod.rs",
    "content": "//! Code common to the front and backends for specific languages.\n\nmod diagnostic_debug;\nmod diagnostic_display;\npub mod predeclared;\npub mod wgsl;\n\npub use diagnostic_debug::{DiagnosticDebug, ForDebug, ForDebugWithTypes};\npub use diagnostic_display::DiagnosticDisplay;\n\n// Re-exported here for backwards compatibility\npub use super::proc::vector_size_str;\n"
  },
  {
    "path": "naga/src/common/predeclared.rs",
    "content": "//! Generating names for predeclared types.\n\nuse crate::ir;\n\nuse alloc::format;\nuse alloc::string::String;\n\nimpl ir::PredeclaredType {\n    pub fn struct_name(&self) -> String {\n        use crate::PredeclaredType as Pt;\n        match *self {\n            Pt::AtomicCompareExchangeWeakResult(scalar) => {\n                format!(\n                    \"__atomic_compare_exchange_result<{:?},{}>\",\n                    scalar.kind, scalar.width,\n                )\n            }\n            Pt::ModfResult { size, scalar } => frexp_mod_name(\"modf\", size, scalar),\n            Pt::FrexpResult { size, scalar } => frexp_mod_name(\"frexp\", size, scalar),\n        }\n    }\n}\n\nfn frexp_mod_name(function: &str, size: Option<ir::VectorSize>, scalar: ir::Scalar) -> String {\n    let bits = 8 * scalar.width;\n    match size {\n        Some(size) => {\n            let size = size as u8;\n            format!(\"__{function}_result_vec{size}_f{bits}\")\n        }\n        None => format!(\"__{function}_result_f{bits}\"),\n    }\n}\n"
  },
  {
    "path": "naga/src/common/wgsl/diagnostics.rs",
    "content": "//! WGSL diagnostic filters and severities.\n\nuse core::fmt::{self, Display, Formatter};\n\nuse crate::diagnostic_filter::{\n    FilterableTriggeringRule, Severity, StandardFilterableTriggeringRule,\n};\n\nimpl Severity {\n    const ERROR: &'static str = \"error\";\n    const WARNING: &'static str = \"warning\";\n    const INFO: &'static str = \"info\";\n    const OFF: &'static str = \"off\";\n\n    /// Convert from a sentinel word in WGSL into its associated [`Severity`], if possible.\n    pub fn from_wgsl_ident(s: &str) -> Option<Self> {\n        Some(match s {\n            Self::ERROR => Self::Error,\n            Self::WARNING => Self::Warning,\n            Self::INFO => Self::Info,\n            Self::OFF => Self::Off,\n            _ => return None,\n        })\n    }\n}\n\npub struct DisplayFilterableTriggeringRule<'a>(&'a FilterableTriggeringRule);\n\nimpl Display for DisplayFilterableTriggeringRule<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        let &Self(inner) = self;\n        match *inner {\n            FilterableTriggeringRule::Standard(rule) => write!(f, \"{}\", rule.to_wgsl_ident()),\n            FilterableTriggeringRule::Unknown(ref rule) => write!(f, \"{rule}\"),\n            FilterableTriggeringRule::User(ref rules) => {\n                let &[ref seg1, ref seg2] = rules.as_ref();\n                write!(f, \"{seg1}.{seg2}\")\n            }\n        }\n    }\n}\n\nimpl FilterableTriggeringRule {\n    /// [`Display`] this rule's identifiers in WGSL.\n    pub const fn display_wgsl_ident(&self) -> impl Display + '_ {\n        DisplayFilterableTriggeringRule(self)\n    }\n}\n\nimpl StandardFilterableTriggeringRule {\n    const DERIVATIVE_UNIFORMITY: &'static str = \"derivative_uniformity\";\n\n    /// Convert from a sentinel word in WGSL into its associated\n    /// [`StandardFilterableTriggeringRule`], if possible.\n    pub fn from_wgsl_ident(s: &str) -> Option<Self> {\n        Some(match s {\n            Self::DERIVATIVE_UNIFORMITY => Self::DerivativeUniformity,\n            _ => return None,\n        })\n    }\n\n    /// Maps this [`StandardFilterableTriggeringRule`] into the sentinel word associated with it in\n    /// WGSL.\n    pub const fn to_wgsl_ident(self) -> &'static str {\n        match self {\n            Self::DerivativeUniformity => Self::DERIVATIVE_UNIFORMITY,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/common/wgsl/mod.rs",
    "content": "//! Code shared between the WGSL front and back ends.\n\nmod diagnostics;\nmod to_wgsl;\nmod types;\n\npub use diagnostics::DisplayFilterableTriggeringRule;\npub use to_wgsl::{address_space_str, ToWgsl, TryToWgsl};\npub use types::TypeContext;\n"
  },
  {
    "path": "naga/src/common/wgsl/to_wgsl.rs",
    "content": "//! Generating WGSL source code for Naga IR types.\n\nuse alloc::format;\nuse alloc::string::{String, ToString};\n\n/// Types that can return the WGSL source representation of their\n/// values as a `'static` string.\n///\n/// This trait is specifically for types whose WGSL forms are simple\n/// enough that they can always be returned as a static string.\n///\n/// - If only some values have a WGSL representation, consider\n///   implementing [`TryToWgsl`] instead.\n///\n/// - If a type's WGSL form requires dynamic formatting, so that\n///   returning a `&'static str` isn't feasible, consider implementing\n///   [`core::fmt::Display`] on some wrapper type instead.\npub trait ToWgsl: Sized {\n    /// Return WGSL source code representation of `self`.\n    fn to_wgsl(self) -> &'static str;\n}\n\n/// Types that may be able to return the WGSL source representation\n/// for their values as a `'static` string.\n///\n/// This trait is specifically for types whose values are either\n/// simple enough that their WGSL form can be represented a static\n/// string, or aren't representable in WGSL at all.\n///\n/// - If all values in the type have `&'static str` representations in\n///   WGSL, consider implementing [`ToWgsl`] instead.\n///\n/// - If a type's WGSL form requires dynamic formatting, so that\n///   returning a `&'static str` isn't feasible, consider implementing\n///   [`core::fmt::Display`] on some wrapper type instead.\npub trait TryToWgsl: Sized {\n    /// Return the WGSL form of `self` as a `'static` string.\n    ///\n    /// If `self` doesn't have a representation in WGSL (standard or\n    /// as extended by Naga), then return `None`.\n    fn try_to_wgsl(self) -> Option<&'static str>;\n\n    /// What kind of WGSL thing `Self` represents.\n    const DESCRIPTION: &'static str;\n\n    /// Return the WGSL form of `self` as appropriate for diagnostics.\n    ///\n    /// If `self` can be expressed in WGSL, return that form as a\n    /// [`String`]. Otherwise, return some representation of `self`\n    /// that is appropriate for use in diagnostic messages.\n    ///\n    /// The default implementation of this function falls back to\n    /// `self`'s [`Debug`] form.\n    ///\n    /// [`Debug`]: core::fmt::Debug\n    fn to_wgsl_for_diagnostics(self) -> String\n    where\n        Self: core::fmt::Debug + Copy,\n    {\n        match self.try_to_wgsl() {\n            Some(static_string) => static_string.to_string(),\n            None => format!(\"{{non-WGSL {} {self:?}}}\", Self::DESCRIPTION),\n        }\n    }\n}\n\nimpl TryToWgsl for crate::MathFunction {\n    const DESCRIPTION: &'static str = \"math function\";\n\n    fn try_to_wgsl(self) -> Option<&'static str> {\n        use crate::MathFunction as Mf;\n\n        Some(match self {\n            Mf::Abs => \"abs\",\n            Mf::Min => \"min\",\n            Mf::Max => \"max\",\n            Mf::Clamp => \"clamp\",\n            Mf::Saturate => \"saturate\",\n            Mf::Cos => \"cos\",\n            Mf::Cosh => \"cosh\",\n            Mf::Sin => \"sin\",\n            Mf::Sinh => \"sinh\",\n            Mf::Tan => \"tan\",\n            Mf::Tanh => \"tanh\",\n            Mf::Acos => \"acos\",\n            Mf::Asin => \"asin\",\n            Mf::Atan => \"atan\",\n            Mf::Atan2 => \"atan2\",\n            Mf::Asinh => \"asinh\",\n            Mf::Acosh => \"acosh\",\n            Mf::Atanh => \"atanh\",\n            Mf::Radians => \"radians\",\n            Mf::Degrees => \"degrees\",\n            Mf::Ceil => \"ceil\",\n            Mf::Floor => \"floor\",\n            Mf::Round => \"round\",\n            Mf::Fract => \"fract\",\n            Mf::Trunc => \"trunc\",\n            Mf::Modf => \"modf\",\n            Mf::Frexp => \"frexp\",\n            Mf::Ldexp => \"ldexp\",\n            Mf::Exp => \"exp\",\n            Mf::Exp2 => \"exp2\",\n            Mf::Log => \"log\",\n            Mf::Log2 => \"log2\",\n            Mf::Pow => \"pow\",\n            Mf::Dot => \"dot\",\n            Mf::Dot4I8Packed => \"dot4I8Packed\",\n            Mf::Dot4U8Packed => \"dot4U8Packed\",\n            Mf::Cross => \"cross\",\n            Mf::Distance => \"distance\",\n            Mf::Length => \"length\",\n            Mf::Normalize => \"normalize\",\n            Mf::FaceForward => \"faceForward\",\n            Mf::Reflect => \"reflect\",\n            Mf::Refract => \"refract\",\n            Mf::Sign => \"sign\",\n            Mf::Fma => \"fma\",\n            Mf::Mix => \"mix\",\n            Mf::Step => \"step\",\n            Mf::SmoothStep => \"smoothstep\",\n            Mf::Sqrt => \"sqrt\",\n            Mf::InverseSqrt => \"inverseSqrt\",\n            Mf::Transpose => \"transpose\",\n            Mf::Determinant => \"determinant\",\n            Mf::QuantizeToF16 => \"quantizeToF16\",\n            Mf::CountTrailingZeros => \"countTrailingZeros\",\n            Mf::CountLeadingZeros => \"countLeadingZeros\",\n            Mf::CountOneBits => \"countOneBits\",\n            Mf::ReverseBits => \"reverseBits\",\n            Mf::ExtractBits => \"extractBits\",\n            Mf::InsertBits => \"insertBits\",\n            Mf::FirstTrailingBit => \"firstTrailingBit\",\n            Mf::FirstLeadingBit => \"firstLeadingBit\",\n            Mf::Pack4x8snorm => \"pack4x8snorm\",\n            Mf::Pack4x8unorm => \"pack4x8unorm\",\n            Mf::Pack2x16snorm => \"pack2x16snorm\",\n            Mf::Pack2x16unorm => \"pack2x16unorm\",\n            Mf::Pack2x16float => \"pack2x16float\",\n            Mf::Pack4xI8 => \"pack4xI8\",\n            Mf::Pack4xU8 => \"pack4xU8\",\n            Mf::Pack4xI8Clamp => \"pack4xI8Clamp\",\n            Mf::Pack4xU8Clamp => \"pack4xU8Clamp\",\n            Mf::Unpack4x8snorm => \"unpack4x8snorm\",\n            Mf::Unpack4x8unorm => \"unpack4x8unorm\",\n            Mf::Unpack2x16snorm => \"unpack2x16snorm\",\n            Mf::Unpack2x16unorm => \"unpack2x16unorm\",\n            Mf::Unpack2x16float => \"unpack2x16float\",\n            Mf::Unpack4xI8 => \"unpack4xI8\",\n            Mf::Unpack4xU8 => \"unpack4xU8\",\n\n            // Non-standard math functions.\n            Mf::Inverse | Mf::Outer => return None,\n        })\n    }\n}\n\nimpl TryToWgsl for crate::BuiltIn {\n    const DESCRIPTION: &'static str = \"builtin value\";\n\n    fn try_to_wgsl(self) -> Option<&'static str> {\n        use crate::BuiltIn as Bi;\n        Some(match self {\n            Bi::Position { .. } => \"position\",\n            Bi::ViewIndex => \"view_index\",\n            Bi::InstanceIndex => \"instance_index\",\n            Bi::VertexIndex => \"vertex_index\",\n            Bi::ClipDistances => \"clip_distances\",\n            Bi::FragDepth => \"frag_depth\",\n            Bi::FrontFacing => \"front_facing\",\n            Bi::PrimitiveIndex => \"primitive_index\",\n            Bi::DrawIndex => \"draw_index\",\n            Bi::Barycentric { perspective: true } => \"barycentric\",\n            Bi::Barycentric { perspective: false } => \"barycentric_no_perspective\",\n            Bi::SampleIndex => \"sample_index\",\n            Bi::SampleMask => \"sample_mask\",\n            Bi::GlobalInvocationId => \"global_invocation_id\",\n            Bi::LocalInvocationId => \"local_invocation_id\",\n            Bi::LocalInvocationIndex => \"local_invocation_index\",\n            Bi::WorkGroupId => \"workgroup_id\",\n            Bi::NumWorkGroups => \"num_workgroups\",\n            Bi::NumSubgroups => \"num_subgroups\",\n            Bi::SubgroupId => \"subgroup_id\",\n            Bi::SubgroupSize => \"subgroup_size\",\n            Bi::SubgroupInvocationId => \"subgroup_invocation_id\",\n\n            // Non-standard built-ins.\n            Bi::MeshTaskSize => \"mesh_task_size\",\n            Bi::TriangleIndices => \"triangle_indices\",\n            Bi::LineIndices => \"line_indices\",\n            Bi::PointIndex => \"point_index\",\n            Bi::Vertices => \"vertices\",\n            Bi::Primitives => \"primitives\",\n            Bi::VertexCount => \"vertex_count\",\n            Bi::PrimitiveCount => \"primitive_count\",\n            Bi::CullPrimitive => \"cull_primitive\",\n\n            Bi::RayInvocationId => \"ray_invocation_id\",\n            Bi::NumRayInvocations => \"num_ray_invocations\",\n            Bi::InstanceCustomData => \"instance_custom_data\",\n            Bi::GeometryIndex => \"geometry_index\",\n            Bi::WorldRayOrigin => \"world_ray_origin\",\n            Bi::WorldRayDirection => \"world_ray_direction\",\n            Bi::ObjectRayOrigin => \"object_ray_origin\",\n            Bi::ObjectRayDirection => \"object_ray_direction\",\n            Bi::RayTmin => \"ray_t_min\",\n            Bi::RayTCurrentMax => \"ray_t_current_max\",\n            Bi::ObjectToWorld => \"object_to_world\",\n            Bi::WorldToObject => \"world_to_object\",\n            Bi::HitKind => \"hit_kind\",\n\n            Bi::BaseInstance\n            | Bi::BaseVertex\n            | Bi::CullDistance\n            | Bi::PointSize\n            | Bi::PointCoord\n            | Bi::WorkGroupSize => return None,\n        })\n    }\n}\n\nimpl ToWgsl for crate::Interpolation {\n    fn to_wgsl(self) -> &'static str {\n        match self {\n            crate::Interpolation::Perspective => \"perspective\",\n            crate::Interpolation::Linear => \"linear\",\n            crate::Interpolation::Flat => \"flat\",\n            crate::Interpolation::PerVertex => \"per_vertex\",\n        }\n    }\n}\n\nimpl ToWgsl for crate::Sampling {\n    fn to_wgsl(self) -> &'static str {\n        match self {\n            crate::Sampling::Center => \"center\",\n            crate::Sampling::Centroid => \"centroid\",\n            crate::Sampling::Sample => \"sample\",\n            crate::Sampling::First => \"first\",\n            crate::Sampling::Either => \"either\",\n        }\n    }\n}\n\nimpl ToWgsl for crate::StorageFormat {\n    fn to_wgsl(self) -> &'static str {\n        use crate::StorageFormat as Sf;\n\n        match self {\n            Sf::R8Unorm => \"r8unorm\",\n            Sf::R8Snorm => \"r8snorm\",\n            Sf::R8Uint => \"r8uint\",\n            Sf::R8Sint => \"r8sint\",\n            Sf::R16Uint => \"r16uint\",\n            Sf::R16Sint => \"r16sint\",\n            Sf::R16Float => \"r16float\",\n            Sf::Rg8Unorm => \"rg8unorm\",\n            Sf::Rg8Snorm => \"rg8snorm\",\n            Sf::Rg8Uint => \"rg8uint\",\n            Sf::Rg8Sint => \"rg8sint\",\n            Sf::R32Uint => \"r32uint\",\n            Sf::R32Sint => \"r32sint\",\n            Sf::R32Float => \"r32float\",\n            Sf::Rg16Uint => \"rg16uint\",\n            Sf::Rg16Sint => \"rg16sint\",\n            Sf::Rg16Float => \"rg16float\",\n            Sf::Rgba8Unorm => \"rgba8unorm\",\n            Sf::Rgba8Snorm => \"rgba8snorm\",\n            Sf::Rgba8Uint => \"rgba8uint\",\n            Sf::Rgba8Sint => \"rgba8sint\",\n            Sf::Bgra8Unorm => \"bgra8unorm\",\n            Sf::Rgb10a2Uint => \"rgb10a2uint\",\n            Sf::Rgb10a2Unorm => \"rgb10a2unorm\",\n            Sf::Rg11b10Ufloat => \"rg11b10ufloat\",\n            Sf::R64Uint => \"r64uint\",\n            Sf::Rg32Uint => \"rg32uint\",\n            Sf::Rg32Sint => \"rg32sint\",\n            Sf::Rg32Float => \"rg32float\",\n            Sf::Rgba16Uint => \"rgba16uint\",\n            Sf::Rgba16Sint => \"rgba16sint\",\n            Sf::Rgba16Float => \"rgba16float\",\n            Sf::Rgba32Uint => \"rgba32uint\",\n            Sf::Rgba32Sint => \"rgba32sint\",\n            Sf::Rgba32Float => \"rgba32float\",\n            Sf::R16Unorm => \"r16unorm\",\n            Sf::R16Snorm => \"r16snorm\",\n            Sf::Rg16Unorm => \"rg16unorm\",\n            Sf::Rg16Snorm => \"rg16snorm\",\n            Sf::Rgba16Unorm => \"rgba16unorm\",\n            Sf::Rgba16Snorm => \"rgba16snorm\",\n        }\n    }\n}\n\nimpl TryToWgsl for crate::Scalar {\n    const DESCRIPTION: &'static str = \"scalar type\";\n\n    fn try_to_wgsl(self) -> Option<&'static str> {\n        use crate::Scalar;\n\n        Some(match self {\n            Scalar::F16 => \"f16\",\n            Scalar::F32 => \"f32\",\n            Scalar::F64 => \"f64\",\n            Scalar::I32 => \"i32\",\n            Scalar::U32 => \"u32\",\n            Scalar::I64 => \"i64\",\n            Scalar::U64 => \"u64\",\n            Scalar::BOOL => \"bool\",\n            _ => return None,\n        })\n    }\n\n    fn to_wgsl_for_diagnostics(self) -> String {\n        match self.try_to_wgsl() {\n            Some(static_string) => static_string.to_string(),\n            None => match self.kind {\n                crate::ScalarKind::Sint\n                | crate::ScalarKind::Uint\n                | crate::ScalarKind::Float\n                | crate::ScalarKind::Bool => format!(\"{{non-WGSL scalar {self:?}}}\"),\n                crate::ScalarKind::AbstractInt => \"{AbstractInt}\".to_string(),\n                crate::ScalarKind::AbstractFloat => \"{AbstractFloat}\".to_string(),\n            },\n        }\n    }\n}\n\nimpl ToWgsl for crate::CooperativeRole {\n    fn to_wgsl(self) -> &'static str {\n        match self {\n            Self::A => \"A\",\n            Self::B => \"B\",\n            Self::C => \"C\",\n        }\n    }\n}\n\nimpl ToWgsl for crate::ImageDimension {\n    fn to_wgsl(self) -> &'static str {\n        match self {\n            Self::D1 => \"1d\",\n            Self::D2 => \"2d\",\n            Self::D3 => \"3d\",\n            Self::Cube => \"cube\",\n        }\n    }\n}\n\n/// Return the WGSL address space and access mode strings for `space`.\n///\n/// Why don't we implement [`ToWgsl`] for [`AddressSpace`]?\n///\n/// In WGSL, the full form of a pointer type is `ptr<AS, T, AM>`, where:\n/// - `AS` is the address space,\n/// - `T` is the store type, and\n/// - `AM` is the access mode.\n///\n/// Since the type `T` intervenes between the address space and the\n/// access mode, there isn't really any individual WGSL grammar\n/// production that corresponds to an [`AddressSpace`], so [`ToWgsl`]\n/// is too simple-minded for this case.\n///\n/// Furthermore, we want to write `var<AS[, AM]>` for most address\n/// spaces, but we want to just write `var foo: T` for handle types.\n///\n/// [`AddressSpace`]: crate::AddressSpace\npub const fn address_space_str(\n    space: crate::AddressSpace,\n) -> (Option<&'static str>, Option<&'static str>) {\n    use crate::AddressSpace as As;\n\n    (\n        Some(match space {\n            As::Private => \"private\",\n            As::Uniform => \"uniform\",\n            As::Storage { access } => {\n                if access.contains(crate::StorageAccess::ATOMIC) {\n                    return (Some(\"storage\"), Some(\"atomic\"));\n                } else if access.contains(crate::StorageAccess::STORE) {\n                    return (Some(\"storage\"), Some(\"read_write\"));\n                } else {\n                    \"storage\"\n                }\n            }\n            As::Immediate => \"immediate\",\n            As::WorkGroup => \"workgroup\",\n            As::Handle => return (None, None),\n            As::Function => \"function\",\n            As::TaskPayload => \"task_payload\",\n            As::IncomingRayPayload => \"incoming_ray_payload\",\n            As::RayPayload => \"ray_payload\",\n        }),\n        None,\n    )\n}\n"
  },
  {
    "path": "naga/src/common/wgsl/types.rs",
    "content": "//! Code for formatting Naga IR types as WGSL source code.\n\nuse super::{address_space_str, ToWgsl, TryToWgsl};\nuse crate::common;\nuse crate::proc::TypeResolution;\nuse crate::{Handle, Scalar, TypeInner};\n\nuse alloc::string::String;\nuse core::fmt::Write;\n\n/// A context for printing Naga IR types as WGSL.\n///\n/// This trait's default methods [`write_type`] and\n/// [`write_type_inner`] do the work of formatting types as WGSL.\n/// Implementors must provide the remaining methods, to customize\n/// behavior for the context at hand.\n///\n/// For example, the WGSL backend would provide an implementation of\n/// [`type_name`] that handles hygienic renaming, whereas the WGSL\n/// front end would simply show the name that was given in the source.\n///\n/// [`write_type`]: TypeContext::write_type\n/// [`write_type_inner`]: TypeContext::write_type_inner\n/// [`type_name`]: TypeContext::type_name\npub trait TypeContext {\n    /// Return the [`Type`] referred to by `handle`.\n    ///\n    /// [`Type`]: crate::Type\n    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type;\n\n    /// Return the name to be used for the type referred to by\n    /// `handle`.\n    fn type_name(&self, handle: Handle<crate::Type>) -> &str;\n\n    /// Write the WGSL form of `override` to `out`.\n    fn write_override<W: Write>(\n        &self,\n        r#override: Handle<crate::Override>,\n        out: &mut W,\n    ) -> core::fmt::Result;\n\n    /// Write a [`TypeInner::Struct`] for which we are unable to find a name.\n    ///\n    /// The names of struct types are only available if we have `Handle<Type>`,\n    /// not from [`TypeInner`]. For logging and debugging, it's fine to just\n    /// write something helpful to the developer, but for generating WGSL,\n    /// this should be unreachable.\n    fn write_unnamed_struct<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result;\n\n    /// Write a [`TypeInner`] that has no representation as WGSL source,\n    /// even including Naga extensions.\n    ///\n    /// A backend might implement this with a call to the [`unreachable!`]\n    /// macro, since backends are allowed to assume that the module has passed\n    /// validation.\n    ///\n    /// The default implementation is appropriate for generating type names to\n    /// appear in error messages. It punts to `TypeInner`'s [`core::fmt::Debug`]\n    /// implementation, since it's probably best to show the user something they\n    /// can act on.\n    fn write_non_wgsl_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {\n        write!(out, \"{{non-WGSL Naga type {inner:?}}}\")\n    }\n\n    /// Write a [`Scalar`] that has no representation as WGSL source,\n    /// even including Naga extensions.\n    ///\n    /// A backend might implement this with a call to the [`unreachable!`]\n    /// macro, since backends are allowed to assume that the module has passed\n    /// validation.\n    ///\n    /// The default implementation is appropriate for generating type names to\n    /// appear in error messages. It punts to `Scalar`'s [`core::fmt::Debug`]\n    /// implementation, since it's probably best to show the user something they\n    /// can act on.\n    fn write_non_wgsl_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {\n        match scalar.kind {\n            crate::ScalarKind::Sint\n            | crate::ScalarKind::Uint\n            | crate::ScalarKind::Float\n            | crate::ScalarKind::Bool => write!(out, \"{{non-WGSL Naga scalar {scalar:?}}}\"),\n\n            // The abstract types are kind of an odd quasi-WGSL category:\n            // they are definitely part of the spec, but they are not expressible\n            // in WGSL itself. So we want to call them out by name in error messages,\n            // but the WGSL backend should never generate these.\n            crate::ScalarKind::AbstractInt => out.write_str(\"{AbstractInt}\"),\n            crate::ScalarKind::AbstractFloat => out.write_str(\"{AbstractFloat}\"),\n        }\n    }\n\n    /// Write the type `ty` as it would appear in a value's declaration.\n    ///\n    /// Write the type referred to by `ty` in `module` as it would appear in\n    /// a `var`, `let`, etc. declaration, or in a function's argument list.\n    fn write_type<W: Write>(&self, handle: Handle<crate::Type>, out: &mut W) -> core::fmt::Result {\n        let ty = self.lookup_type(handle);\n        match ty.inner {\n            TypeInner::Struct { .. } => out.write_str(self.type_name(handle))?,\n            ref other => self.write_type_inner(other, out)?,\n        }\n\n        Ok(())\n    }\n\n    /// Write the [`TypeInner`] `inner` as it would appear in a value's declaration.\n    ///\n    /// Write `inner` as it would appear in a `var`, `let`, etc.\n    /// declaration, or in a function's argument list.\n    ///\n    /// Note that this cannot handle writing [`Struct`] types: those\n    /// must be referred to by name, but the name isn't available in\n    /// [`TypeInner`].\n    ///\n    /// [`Struct`]: TypeInner::Struct\n    fn write_type_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {\n        match try_write_type_inner(self, inner, out) {\n            Ok(()) => Ok(()),\n            Err(WriteTypeError::Format(err)) => Err(err),\n            Err(WriteTypeError::NonWgsl) => self.write_non_wgsl_inner(inner, out),\n        }\n    }\n\n    /// Write the [`Scalar`] `scalar` as a WGSL type.\n    fn write_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {\n        match scalar.try_to_wgsl() {\n            Some(string) => out.write_str(string),\n            None => self.write_non_wgsl_scalar(scalar, out),\n        }\n    }\n\n    /// Write the [`TypeResolution`] `resolution` as a WGSL type.\n    fn write_type_resolution<W: Write>(\n        &self,\n        resolution: &TypeResolution,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        match *resolution {\n            TypeResolution::Handle(handle) => self.write_type(handle, out),\n            TypeResolution::Value(ref inner) => self.write_type_inner(inner, out),\n        }\n    }\n\n    fn write_type_conclusion<W: Write>(\n        &self,\n        conclusion: &crate::proc::Conclusion,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        use crate::proc::Conclusion as Co;\n\n        match *conclusion {\n            Co::Value(ref inner) => self.write_type_inner(inner, out),\n            Co::Predeclared(ref predeclared) => out.write_str(&predeclared.struct_name()),\n        }\n    }\n\n    fn write_type_rule<W: Write>(\n        &self,\n        name: &str,\n        rule: &crate::proc::Rule,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        write!(out, \"fn {name}(\")?;\n        for (i, arg) in rule.arguments.iter().enumerate() {\n            if i > 0 {\n                out.write_str(\", \")?;\n            }\n            self.write_type_resolution(arg, out)?\n        }\n        out.write_str(\") -> \")?;\n        self.write_type_conclusion(&rule.conclusion, out)?;\n        Ok(())\n    }\n\n    fn type_to_string(&self, handle: Handle<crate::Type>) -> String {\n        let mut buf = String::new();\n        self.write_type(handle, &mut buf).unwrap();\n        buf\n    }\n\n    fn type_resolution_to_string(&self, resolution: &TypeResolution) -> String {\n        let mut buf = String::new();\n        self.write_type_resolution(resolution, &mut buf).unwrap();\n        buf\n    }\n\n    fn type_rule_to_string(&self, name: &str, rule: &crate::proc::Rule) -> String {\n        let mut buf = String::new();\n        self.write_type_rule(name, rule, &mut buf).unwrap();\n        buf\n    }\n}\n\nfn try_write_type_inner<C, W>(ctx: &C, inner: &TypeInner, out: &mut W) -> Result<(), WriteTypeError>\nwhere\n    C: TypeContext + ?Sized,\n    W: Write,\n{\n    match *inner {\n        TypeInner::Vector { size, scalar } => {\n            write!(out, \"vec{}<\", common::vector_size_str(size))?;\n            ctx.write_scalar(scalar, out)?;\n            out.write_str(\">\")?;\n        }\n        TypeInner::Sampler { comparison: false } => {\n            write!(out, \"sampler\")?;\n        }\n        TypeInner::Sampler { comparison: true } => {\n            write!(out, \"sampler_comparison\")?;\n        }\n        TypeInner::Image {\n            dim,\n            arrayed,\n            class,\n        } => {\n            // More about texture types: https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type\n            use crate::ImageClass as Ic;\n\n            let dim_str = dim.to_wgsl();\n            let arrayed_str = if arrayed { \"_array\" } else { \"\" };\n            match class {\n                Ic::Sampled { kind, multi } => {\n                    let multisampled_str = if multi { \"multisampled_\" } else { \"\" };\n                    write!(out, \"texture_{multisampled_str}{dim_str}{arrayed_str}<\")?;\n                    ctx.write_scalar(Scalar { kind, width: 4 }, out)?;\n                    out.write_str(\">\")?;\n                }\n                Ic::Depth { multi } => {\n                    let multisampled_str = if multi { \"multisampled_\" } else { \"\" };\n                    write!(\n                        out,\n                        \"texture_depth_{multisampled_str}{dim_str}{arrayed_str}\"\n                    )?;\n                }\n                Ic::Storage { format, access } => {\n                    let format_str = format.to_wgsl();\n                    let access_str = if access.contains(crate::StorageAccess::ATOMIC) {\n                        \",atomic\"\n                    } else if access\n                        .contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)\n                    {\n                        \",read_write\"\n                    } else if access.contains(crate::StorageAccess::LOAD) {\n                        \",read\"\n                    } else {\n                        \",write\"\n                    };\n                    write!(\n                        out,\n                        \"texture_storage_{dim_str}{arrayed_str}<{format_str}{access_str}>\"\n                    )?;\n                }\n                Ic::External => {\n                    write!(out, \"texture_external\")?;\n                }\n            }\n        }\n        TypeInner::Scalar(scalar) => {\n            ctx.write_scalar(scalar, out)?;\n        }\n        TypeInner::Atomic(scalar) => {\n            out.write_str(\"atomic<\")?;\n            ctx.write_scalar(scalar, out)?;\n            out.write_str(\">\")?;\n        }\n        TypeInner::Array {\n            base,\n            size,\n            stride: _,\n        } => {\n            // More info https://gpuweb.github.io/gpuweb/wgsl/#array-types\n            // array<A, 3> -- Constant array\n            // array<A> -- Dynamic array\n            write!(out, \"array<\")?;\n            match size {\n                crate::ArraySize::Constant(len) => {\n                    ctx.write_type(base, out)?;\n                    write!(out, \", {len}\")?;\n                }\n                crate::ArraySize::Pending(r#override) => {\n                    ctx.write_override(r#override, out)?;\n                }\n                crate::ArraySize::Dynamic => {\n                    ctx.write_type(base, out)?;\n                }\n            }\n            write!(out, \">\")?;\n        }\n        TypeInner::BindingArray { base, size } => {\n            // More info https://github.com/gpuweb/gpuweb/issues/2105\n            write!(out, \"binding_array<\")?;\n            match size {\n                crate::ArraySize::Constant(len) => {\n                    ctx.write_type(base, out)?;\n                    write!(out, \", {len}\")?;\n                }\n                crate::ArraySize::Pending(r#override) => {\n                    ctx.write_override(r#override, out)?;\n                }\n                crate::ArraySize::Dynamic => {\n                    ctx.write_type(base, out)?;\n                }\n            }\n            write!(out, \">\")?;\n        }\n        TypeInner::Matrix {\n            columns,\n            rows,\n            scalar,\n        } => {\n            write!(\n                out,\n                \"mat{}x{}<\",\n                common::vector_size_str(columns),\n                common::vector_size_str(rows),\n            )?;\n            ctx.write_scalar(scalar, out)?;\n            out.write_str(\">\")?;\n        }\n        TypeInner::CooperativeMatrix {\n            columns,\n            rows,\n            scalar,\n            role,\n        } => {\n            write!(\n                out,\n                \"coop_mat{}x{}<{},{}>\",\n                columns as u32,\n                rows as u32,\n                scalar.try_to_wgsl().unwrap_or_default(),\n                role.to_wgsl(),\n            )?;\n        }\n        TypeInner::Pointer { base, space } => {\n            let (address, maybe_access) = address_space_str(space);\n            // Everything but `AddressSpace::Handle` gives us a `address` name, but\n            // Naga IR never produces pointers to handles, so it doesn't matter much\n            // how we write such a type. Just write it as the base type alone.\n            if let Some(space) = address {\n                write!(out, \"ptr<{space}, \")?;\n            }\n            ctx.write_type(base, out)?;\n            if address.is_some() {\n                if let Some(access) = maybe_access {\n                    write!(out, \", {access}\")?;\n                }\n                write!(out, \">\")?;\n            }\n        }\n        TypeInner::ValuePointer {\n            size: None,\n            scalar,\n            space,\n        } => {\n            let (address, maybe_access) = address_space_str(space);\n            if let Some(space) = address {\n                write!(out, \"ptr<{space}, \")?;\n                ctx.write_scalar(scalar, out)?;\n                if let Some(access) = maybe_access {\n                    write!(out, \", {access}\")?;\n                }\n                write!(out, \">\")?;\n            } else {\n                return Err(WriteTypeError::NonWgsl);\n            }\n        }\n        TypeInner::ValuePointer {\n            size: Some(size),\n            scalar,\n            space,\n        } => {\n            let (address, maybe_access) = address_space_str(space);\n            if let Some(space) = address {\n                write!(out, \"ptr<{}, vec{}<\", space, common::vector_size_str(size),)?;\n                ctx.write_scalar(scalar, out)?;\n                out.write_str(\">\")?;\n                if let Some(access) = maybe_access {\n                    write!(out, \", {access}\")?;\n                }\n                write!(out, \">\")?;\n            } else {\n                return Err(WriteTypeError::NonWgsl);\n            }\n            write!(out, \">\")?;\n        }\n        TypeInner::AccelerationStructure { vertex_return } => {\n            let caps = if vertex_return { \"<vertex_return>\" } else { \"\" };\n            write!(out, \"acceleration_structure{caps}\")?\n        }\n        TypeInner::Struct { .. } => {\n            ctx.write_unnamed_struct(inner, out)?;\n        }\n        TypeInner::RayQuery { vertex_return } => {\n            let caps = if vertex_return { \"<vertex_return>\" } else { \"\" };\n            write!(out, \"ray_query{caps}\")?\n        }\n    }\n\n    Ok(())\n}\n\n/// Error type returned by `try_write_type_inner`.\n///\n/// This type is private to the module.\nenum WriteTypeError {\n    Format(core::fmt::Error),\n    NonWgsl,\n}\n\nimpl From<core::fmt::Error> for WriteTypeError {\n    fn from(err: core::fmt::Error) -> Self {\n        Self::Format(err)\n    }\n}\n\n/// Format types as WGSL based on a [`GlobalCtx`].\n///\n/// This is probably good enough for diagnostic output, but it has some\n/// limitations:\n///\n/// - It does not apply [`Namer`] renamings, to avoid collisions.\n///\n/// - It generates invalid WGSL for anonymous struct types.\n///\n/// - It doesn't write the lengths of override-expression-sized arrays\n///   correctly, unless the expression is just the override identifier.\n///\n/// [`GlobalCtx`]: crate::proc::GlobalCtx\n/// [`Namer`]: crate::proc::Namer\nimpl TypeContext for crate::proc::GlobalCtx<'_> {\n    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {\n        &self.types[handle]\n    }\n\n    fn type_name(&self, handle: Handle<crate::Type>) -> &str {\n        self.types[handle]\n            .name\n            .as_deref()\n            .unwrap_or(\"{anonymous type}\")\n    }\n\n    fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, out: &mut W) -> core::fmt::Result {\n        write!(out, \"{{unnamed struct}}\")\n    }\n\n    fn write_override<W: Write>(\n        &self,\n        handle: Handle<crate::Override>,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        match self.overrides[handle].name {\n            Some(ref name) => out.write_str(name),\n            None => write!(out, \"{{anonymous override {handle:?}}}\"),\n        }\n    }\n}\n\n/// Format types as WGSL based on a `UniqueArena<Type>`.\n///\n/// This is probably only good enough for logging:\n///\n/// - It does not apply any kind of [`Namer`] renamings.\n///\n/// - It generates invalid WGSL for anonymous struct types.\n///\n/// - It doesn't write override-sized arrays properly.\n///\n/// [`Namer`]: crate::proc::Namer\nimpl TypeContext for crate::UniqueArena<crate::Type> {\n    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {\n        &self[handle]\n    }\n\n    fn type_name(&self, handle: Handle<crate::Type>) -> &str {\n        self[handle].name.as_deref().unwrap_or(\"{anonymous type}\")\n    }\n\n    fn write_unnamed_struct<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {\n        write!(out, \"{{unnamed struct {inner:?}}}\")\n    }\n\n    fn write_override<W: Write>(\n        &self,\n        handle: Handle<crate::Override>,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        write!(out, \"{{override {handle:?}}}\")\n    }\n}\n"
  },
  {
    "path": "naga/src/compact/expressions.rs",
    "content": "use super::{HandleMap, HandleSet, ModuleMap};\nuse crate::arena::{Arena, Handle};\n\npub struct ExpressionTracer<'tracer> {\n    pub constants: &'tracer Arena<crate::Constant>,\n    pub overrides: &'tracer Arena<crate::Override>,\n\n    /// The arena in which we are currently tracing expressions.\n    pub expressions: &'tracer Arena<crate::Expression>,\n\n    /// The used map for `types`.\n    pub types_used: &'tracer mut HandleSet<crate::Type>,\n\n    /// The used map for global variables.\n    pub global_variables_used: &'tracer mut HandleSet<crate::GlobalVariable>,\n\n    /// The used map for `constants`.\n    pub constants_used: &'tracer mut HandleSet<crate::Constant>,\n\n    /// The used map for `overrides`.\n    pub overrides_used: &'tracer mut HandleSet<crate::Override>,\n\n    /// The used set for `arena`.\n    ///\n    /// This points to whatever arena holds the expressions we are\n    /// currently tracing: either a function's expression arena, or\n    /// the module's constant expression arena.\n    pub expressions_used: &'tracer mut HandleSet<crate::Expression>,\n\n    /// The used set for the module's `global_expressions` arena.\n    ///\n    /// If `None`, we are already tracing the constant expressions,\n    /// and `expressions_used` already refers to their handle set.\n    pub global_expressions_used: Option<&'tracer mut HandleSet<crate::Expression>>,\n}\n\nimpl ExpressionTracer<'_> {\n    /// Propagate usage through `self.expressions`, starting with `self.expressions_used`.\n    ///\n    /// Treat `self.expressions_used` as the initial set of \"known\n    /// live\" expressions, and follow through to identify all\n    /// transitively used expressions.\n    ///\n    /// Mark types, constants, and constant expressions used directly\n    /// by `self.expressions` as used. Items used indirectly are not\n    /// marked.\n    ///\n    /// [fe]: crate::Function::expressions\n    /// [ce]: crate::Module::global_expressions\n    pub fn trace_expressions(&mut self) {\n        log::trace!(\n            \"entering trace_expression of {}\",\n            if self.global_expressions_used.is_some() {\n                \"function expressions\"\n            } else {\n                \"const expressions\"\n            }\n        );\n\n        // We don't need recursion or a work list. Because an\n        // expression may only refer to other expressions that precede\n        // it in the arena, it suffices to make a single pass over the\n        // arena from back to front, marking the referents of used\n        // expressions as used themselves.\n        for (handle, expr) in self.expressions.iter().rev() {\n            // If this expression isn't used, it doesn't matter what it uses.\n            if !self.expressions_used.contains(handle) {\n                continue;\n            }\n\n            log::trace!(\"tracing new expression {expr:?}\");\n            self.trace_expression(expr);\n        }\n    }\n\n    pub fn trace_expression(&mut self, expr: &crate::Expression) {\n        use crate::Expression as Ex;\n        match *expr {\n            // Expressions that do not contain handles that need to be traced.\n            Ex::Literal(_)\n            | Ex::FunctionArgument(_)\n            | Ex::LocalVariable(_)\n            | Ex::SubgroupBallotResult\n            | Ex::RayQueryProceedResult => {}\n\n            // Expressions can refer to constants and overrides, which can refer\n            // in turn to expressions, which complicates our nice one-pass\n            // algorithm. But since constants and overrides don't refer to each\n            // other directly, only via expressions, we can get around this by\n            // looking *through* each constant/override and marking its\n            // initializer expression as used immediately. Since `expr` refers\n            // to the constant/override, which then refers to the initializer,\n            // the initializer must precede `expr` in the arena, so we know we\n            // have yet to visit the initializer, so it's not too late to mark\n            // it.\n            Ex::Constant(handle) => {\n                self.constants_used.insert(handle);\n                let constant = &self.constants[handle];\n                self.types_used.insert(constant.ty);\n                match self.global_expressions_used {\n                    Some(ref mut used) => used.insert(constant.init),\n                    None => self.expressions_used.insert(constant.init),\n                };\n            }\n            Ex::Override(handle) => {\n                self.overrides_used.insert(handle);\n                let r#override = &self.overrides[handle];\n                self.types_used.insert(r#override.ty);\n                if let Some(init) = r#override.init {\n                    match self.global_expressions_used {\n                        Some(ref mut used) => used.insert(init),\n                        None => self.expressions_used.insert(init),\n                    };\n                }\n            }\n            Ex::ZeroValue(ty) => {\n                self.types_used.insert(ty);\n            }\n            Ex::Compose { ty, ref components } => {\n                self.types_used.insert(ty);\n                self.expressions_used\n                    .insert_iter(components.iter().cloned());\n            }\n            Ex::Access { base, index } => self.expressions_used.insert_iter([base, index]),\n            Ex::AccessIndex { base, index: _ } => {\n                self.expressions_used.insert(base);\n            }\n            Ex::Splat { size: _, value } => {\n                self.expressions_used.insert(value);\n            }\n            Ex::Swizzle {\n                size: _,\n                vector,\n                pattern: _,\n            } => {\n                self.expressions_used.insert(vector);\n            }\n            Ex::GlobalVariable(handle) => {\n                self.global_variables_used.insert(handle);\n            }\n            Ex::Load { pointer } => {\n                self.expressions_used.insert(pointer);\n            }\n            Ex::ImageSample {\n                image,\n                sampler,\n                gather: _,\n                coordinate,\n                array_index,\n                offset,\n                ref level,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                self.expressions_used\n                    .insert_iter([image, sampler, coordinate]);\n                self.expressions_used.insert_iter(array_index);\n                self.expressions_used.insert_iter(offset);\n                use crate::SampleLevel as Sl;\n                match *level {\n                    Sl::Auto | Sl::Zero => {}\n                    Sl::Exact(expr) | Sl::Bias(expr) => {\n                        self.expressions_used.insert(expr);\n                    }\n                    Sl::Gradient { x, y } => self.expressions_used.insert_iter([x, y]),\n                }\n                self.expressions_used.insert_iter(depth_ref);\n            }\n            Ex::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                self.expressions_used.insert(image);\n                self.expressions_used.insert(coordinate);\n                self.expressions_used.insert_iter(array_index);\n                self.expressions_used.insert_iter(sample);\n                self.expressions_used.insert_iter(level);\n            }\n            Ex::ImageQuery { image, ref query } => {\n                self.expressions_used.insert(image);\n                use crate::ImageQuery as Iq;\n                match *query {\n                    Iq::Size { level } => self.expressions_used.insert_iter(level),\n                    Iq::NumLevels | Iq::NumLayers | Iq::NumSamples => {}\n                }\n            }\n            Ex::RayQueryVertexPositions {\n                query,\n                committed: _,\n            } => {\n                self.expressions_used.insert(query);\n            }\n            Ex::Unary { op: _, expr } => {\n                self.expressions_used.insert(expr);\n            }\n            Ex::Binary { op: _, left, right } => {\n                self.expressions_used.insert_iter([left, right]);\n            }\n            Ex::Select {\n                condition,\n                accept,\n                reject,\n            } => self\n                .expressions_used\n                .insert_iter([condition, accept, reject]),\n            Ex::Derivative {\n                axis: _,\n                ctrl: _,\n                expr,\n            } => {\n                self.expressions_used.insert(expr);\n            }\n            Ex::Relational { fun: _, argument } => {\n                self.expressions_used.insert(argument);\n            }\n            Ex::Math {\n                fun: _,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                self.expressions_used.insert(arg);\n                self.expressions_used.insert_iter(arg1);\n                self.expressions_used.insert_iter(arg2);\n                self.expressions_used.insert_iter(arg3);\n            }\n            Ex::As {\n                expr,\n                kind: _,\n                convert: _,\n            } => {\n                self.expressions_used.insert(expr);\n            }\n            Ex::ArrayLength(expr) => {\n                self.expressions_used.insert(expr);\n            }\n            // `CallResult` expressions do contain a function handle, but any used\n            // `CallResult` expression should have an associated `ir::Statement::Call`\n            // that we will trace.\n            Ex::CallResult(_) => {}\n            Ex::AtomicResult { ty, comparison: _ }\n            | Ex::WorkGroupUniformLoadResult { ty }\n            | Ex::SubgroupOperationResult { ty } => {\n                self.types_used.insert(ty);\n            }\n            Ex::RayQueryGetIntersection {\n                query,\n                committed: _,\n            } => {\n                self.expressions_used.insert(query);\n            }\n            Ex::CooperativeLoad { ref data, .. } => {\n                self.expressions_used.insert(data.pointer);\n                self.expressions_used.insert(data.stride);\n            }\n            Ex::CooperativeMultiplyAdd { a, b, c } => {\n                self.expressions_used.insert(a);\n                self.expressions_used.insert(b);\n                self.expressions_used.insert(c);\n            }\n        }\n    }\n}\n\nimpl ModuleMap {\n    /// Fix up all handles in `expr`.\n    ///\n    /// Use the expression handle remappings in `operand_map`, and all\n    /// other mappings from `self`.\n    pub fn adjust_expression(\n        &self,\n        expr: &mut crate::Expression,\n        operand_map: &HandleMap<crate::Expression>,\n    ) {\n        let adjust = |expr: &mut Handle<crate::Expression>| {\n            operand_map.adjust(expr);\n        };\n\n        use crate::Expression as Ex;\n        match *expr {\n            // Expressions that do not contain handles that need to be adjusted.\n            Ex::Literal(_)\n            | Ex::FunctionArgument(_)\n            | Ex::LocalVariable(_)\n            | Ex::SubgroupBallotResult\n            | Ex::RayQueryProceedResult => {}\n\n            // Expressions that contain handles that need to be adjusted.\n            Ex::Constant(ref mut constant) => self.constants.adjust(constant),\n            Ex::Override(ref mut r#override) => self.overrides.adjust(r#override),\n            Ex::ZeroValue(ref mut ty) => self.types.adjust(ty),\n            Ex::Compose {\n                ref mut ty,\n                ref mut components,\n            } => {\n                self.types.adjust(ty);\n                for component in components {\n                    adjust(component);\n                }\n            }\n            Ex::Access {\n                ref mut base,\n                ref mut index,\n            } => {\n                adjust(base);\n                adjust(index);\n            }\n            Ex::AccessIndex {\n                ref mut base,\n                index: _,\n            } => adjust(base),\n            Ex::Splat {\n                size: _,\n                ref mut value,\n            } => adjust(value),\n            Ex::Swizzle {\n                size: _,\n                ref mut vector,\n                pattern: _,\n            } => adjust(vector),\n            Ex::GlobalVariable(ref mut handle) => self.globals.adjust(handle),\n            Ex::Load { ref mut pointer } => adjust(pointer),\n            Ex::ImageSample {\n                ref mut image,\n                ref mut sampler,\n                gather: _,\n                ref mut coordinate,\n                ref mut array_index,\n                ref mut offset,\n                ref mut level,\n                ref mut depth_ref,\n                clamp_to_edge: _,\n            } => {\n                adjust(image);\n                adjust(sampler);\n                adjust(coordinate);\n                operand_map.adjust_option(array_index);\n                operand_map.adjust_option(offset);\n                self.adjust_sample_level(level, operand_map);\n                operand_map.adjust_option(depth_ref);\n            }\n            Ex::ImageLoad {\n                ref mut image,\n                ref mut coordinate,\n                ref mut array_index,\n                ref mut sample,\n                ref mut level,\n            } => {\n                adjust(image);\n                adjust(coordinate);\n                operand_map.adjust_option(array_index);\n                operand_map.adjust_option(sample);\n                operand_map.adjust_option(level);\n            }\n            Ex::ImageQuery {\n                ref mut image,\n                ref mut query,\n            } => {\n                adjust(image);\n                self.adjust_image_query(query, operand_map);\n            }\n            Ex::Unary {\n                op: _,\n                ref mut expr,\n            } => adjust(expr),\n            Ex::Binary {\n                op: _,\n                ref mut left,\n                ref mut right,\n            } => {\n                adjust(left);\n                adjust(right);\n            }\n            Ex::Select {\n                ref mut condition,\n                ref mut accept,\n                ref mut reject,\n            } => {\n                adjust(condition);\n                adjust(accept);\n                adjust(reject);\n            }\n            Ex::Derivative {\n                axis: _,\n                ctrl: _,\n                ref mut expr,\n            } => adjust(expr),\n            Ex::Relational {\n                fun: _,\n                ref mut argument,\n            } => adjust(argument),\n            Ex::Math {\n                fun: _,\n                ref mut arg,\n                ref mut arg1,\n                ref mut arg2,\n                ref mut arg3,\n            } => {\n                adjust(arg);\n                operand_map.adjust_option(arg1);\n                operand_map.adjust_option(arg2);\n                operand_map.adjust_option(arg3);\n            }\n            Ex::As {\n                ref mut expr,\n                kind: _,\n                convert: _,\n            } => adjust(expr),\n            Ex::CallResult(ref mut function) => {\n                self.functions.adjust(function);\n            }\n            Ex::AtomicResult {\n                ref mut ty,\n                comparison: _,\n            } => self.types.adjust(ty),\n            Ex::WorkGroupUniformLoadResult { ref mut ty } => self.types.adjust(ty),\n            Ex::SubgroupOperationResult { ref mut ty } => self.types.adjust(ty),\n            Ex::ArrayLength(ref mut expr) => adjust(expr),\n            Ex::RayQueryGetIntersection {\n                ref mut query,\n                committed: _,\n            } => adjust(query),\n            Ex::RayQueryVertexPositions {\n                ref mut query,\n                committed: _,\n            } => adjust(query),\n            Ex::CooperativeLoad { ref mut data, .. } => {\n                adjust(&mut data.pointer);\n                adjust(&mut data.stride);\n            }\n            Ex::CooperativeMultiplyAdd {\n                ref mut a,\n                ref mut b,\n                ref mut c,\n            } => {\n                adjust(a);\n                adjust(b);\n                adjust(c);\n            }\n        }\n    }\n\n    fn adjust_sample_level(\n        &self,\n        level: &mut crate::SampleLevel,\n        operand_map: &HandleMap<crate::Expression>,\n    ) {\n        let adjust = |expr: &mut Handle<crate::Expression>| operand_map.adjust(expr);\n\n        use crate::SampleLevel as Sl;\n        match *level {\n            Sl::Auto | Sl::Zero => {}\n            Sl::Exact(ref mut expr) => adjust(expr),\n            Sl::Bias(ref mut expr) => adjust(expr),\n            Sl::Gradient {\n                ref mut x,\n                ref mut y,\n            } => {\n                adjust(x);\n                adjust(y);\n            }\n        }\n    }\n\n    fn adjust_image_query(\n        &self,\n        query: &mut crate::ImageQuery,\n        operand_map: &HandleMap<crate::Expression>,\n    ) {\n        use crate::ImageQuery as Iq;\n\n        match *query {\n            Iq::Size { ref mut level } => operand_map.adjust_option(level),\n            Iq::NumLevels | Iq::NumLayers | Iq::NumSamples => {}\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/compact/functions.rs",
    "content": "use super::arena::HandleSet;\nuse super::{FunctionMap, ModuleMap};\n\npub struct FunctionTracer<'a> {\n    pub function: &'a crate::Function,\n    pub constants: &'a crate::Arena<crate::Constant>,\n    pub overrides: &'a crate::Arena<crate::Override>,\n\n    pub functions_pending: &'a mut HandleSet<crate::Function>,\n    pub functions_used: &'a mut HandleSet<crate::Function>,\n    pub types_used: &'a mut HandleSet<crate::Type>,\n    pub global_variables_used: &'a mut HandleSet<crate::GlobalVariable>,\n    pub constants_used: &'a mut HandleSet<crate::Constant>,\n    pub overrides_used: &'a mut HandleSet<crate::Override>,\n    pub global_expressions_used: &'a mut HandleSet<crate::Expression>,\n\n    /// Function-local expressions used.\n    pub expressions_used: HandleSet<crate::Expression>,\n}\n\nimpl FunctionTracer<'_> {\n    pub fn trace_call(&mut self, function: crate::Handle<crate::Function>) {\n        if !self.functions_used.contains(function) {\n            self.functions_used.insert(function);\n            self.functions_pending.insert(function);\n        }\n    }\n\n    pub fn trace(&mut self) {\n        for argument in self.function.arguments.iter() {\n            self.types_used.insert(argument.ty);\n        }\n\n        if let Some(ref result) = self.function.result {\n            self.types_used.insert(result.ty);\n        }\n\n        for (_, local) in self.function.local_variables.iter() {\n            self.types_used.insert(local.ty);\n            if let Some(init) = local.init {\n                self.expressions_used.insert(init);\n            }\n        }\n\n        // Treat named expressions as alive, for the sake of our test suite,\n        // which uses `let blah = expr;` to exercise lots of things.\n        for (&value, _name) in &self.function.named_expressions {\n            self.expressions_used.insert(value);\n        }\n\n        self.trace_block(&self.function.body);\n\n        // Given that `trace_block` has marked the expressions used\n        // directly by statements, walk the arena to find all\n        // expressions used, directly or indirectly.\n        self.as_expression().trace_expressions();\n    }\n\n    const fn as_expression(&mut self) -> super::expressions::ExpressionTracer<'_> {\n        super::expressions::ExpressionTracer {\n            constants: self.constants,\n            overrides: self.overrides,\n            expressions: &self.function.expressions,\n\n            types_used: self.types_used,\n            global_variables_used: self.global_variables_used,\n            constants_used: self.constants_used,\n            overrides_used: self.overrides_used,\n            expressions_used: &mut self.expressions_used,\n            global_expressions_used: Some(&mut self.global_expressions_used),\n        }\n    }\n}\n\nimpl FunctionMap {\n    pub fn compact(\n        &self,\n        function: &mut crate::Function,\n        module_map: &ModuleMap,\n        reuse: &mut crate::NamedExpressions,\n    ) {\n        assert!(reuse.is_empty());\n\n        for argument in function.arguments.iter_mut() {\n            module_map.types.adjust(&mut argument.ty);\n        }\n\n        if let Some(ref mut result) = function.result {\n            module_map.types.adjust(&mut result.ty);\n        }\n\n        for (_, local) in function.local_variables.iter_mut() {\n            log::trace!(\"adjusting local variable {:?}\", local.name);\n            module_map.types.adjust(&mut local.ty);\n            if let Some(ref mut init) = local.init {\n                self.expressions.adjust(init);\n            }\n        }\n\n        // Drop unused expressions, reusing existing storage.\n        function.expressions.retain_mut(|handle, expr| {\n            if self.expressions.used(handle) {\n                module_map.adjust_expression(expr, &self.expressions);\n                true\n            } else {\n                false\n            }\n        });\n\n        // Adjust named expressions.\n        for (mut handle, name) in function.named_expressions.drain(..) {\n            self.expressions.adjust(&mut handle);\n            reuse.insert(handle, name);\n        }\n        core::mem::swap(&mut function.named_expressions, reuse);\n        assert!(reuse.is_empty());\n\n        // Adjust statements.\n        self.adjust_body(function, &module_map.functions);\n    }\n}\n"
  },
  {
    "path": "naga/src/compact/handle_set_map.rs",
    "content": "use alloc::vec::Vec;\n\nuse crate::arena::{Arena, Handle, HandleSet, Range};\n\ntype Index = crate::non_max_u32::NonMaxU32;\n\n/// A map keyed by handles.\n///\n/// In most cases, this is used to map from old handle indices to new,\n/// compressed handle indices.\n#[derive(Debug)]\npub struct HandleMap<T, U = Index> {\n    /// The indices assigned to handles in the compacted module.\n    ///\n    /// If `new_index[i]` is `Some(n)`, then `n` is the `Index` of the\n    /// compacted `Handle` corresponding to the pre-compacted `Handle`\n    /// whose index is `i`.\n    new_index: Vec<Option<U>>,\n\n    /// This type is indexed by values of type `T`.\n    as_keys: core::marker::PhantomData<T>,\n}\n\nimpl<T, U> HandleMap<T, U> {\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            new_index: Vec::with_capacity(capacity),\n            as_keys: core::marker::PhantomData,\n        }\n    }\n\n    pub fn get(&self, handle: Handle<T>) -> Option<&U> {\n        self.new_index.get(handle.index()).unwrap_or(&None).as_ref()\n    }\n\n    pub fn insert(&mut self, handle: Handle<T>, value: U) -> Option<U> {\n        if self.new_index.len() <= handle.index() {\n            self.new_index.resize_with(handle.index() + 1, || None);\n        }\n        self.new_index[handle.index()].replace(value)\n    }\n}\n\nimpl<T: 'static> HandleMap<T> {\n    pub fn from_set(set: HandleSet<T>) -> Self {\n        let mut next_index = Index::new(0).unwrap();\n        Self {\n            new_index: set\n                .all_possible()\n                .map(|handle| {\n                    if set.contains(handle) {\n                        // This handle will be retained in the compacted version,\n                        // so assign it a new index.\n                        let this = next_index;\n                        next_index = next_index.checked_add(1).unwrap();\n                        Some(this)\n                    } else {\n                        // This handle will be omitted in the compacted version.\n                        None\n                    }\n                })\n                .collect(),\n            as_keys: core::marker::PhantomData,\n        }\n    }\n\n    /// Return true if `old` is used in the compacted module.\n    pub fn used(&self, old: Handle<T>) -> bool {\n        self.new_index[old.index()].is_some()\n    }\n\n    /// Return the counterpart to `old` in the compacted module.\n    ///\n    /// If we thought `old` wouldn't be used in the compacted module, return\n    /// `None`.\n    pub fn try_adjust(&self, old: Handle<T>) -> Option<Handle<T>> {\n        log::trace!(\n            \"adjusting {} handle [{}] -> [{:?}]\",\n            core::any::type_name::<T>(),\n            old.index(),\n            self.new_index[old.index()]\n        );\n        self.new_index[old.index()].map(Handle::new)\n    }\n\n    /// Return the counterpart to `old` in the compacted module.\n    ///\n    /// If we thought `old` wouldn't be used in the compacted module, panic.\n    pub fn adjust(&self, handle: &mut Handle<T>) {\n        *handle = self.try_adjust(*handle).unwrap();\n    }\n\n    /// Like `adjust`, but for optional handles.\n    pub fn adjust_option(&self, handle: &mut Option<Handle<T>>) {\n        if let Some(ref mut handle) = *handle {\n            self.adjust(handle);\n        }\n    }\n\n    /// Shrink `range` to include only used handles.\n    ///\n    /// Fortunately, compaction doesn't arbitrarily scramble the expressions\n    /// in the arena, but instead preserves the order of the elements while\n    /// squeezing out unused ones. That means that a contiguous range in the\n    /// pre-compacted arena always maps to a contiguous range in the\n    /// post-compacted arena. So we just need to adjust the endpoints.\n    ///\n    /// Compaction may have eliminated the endpoints themselves.\n    ///\n    /// Use `compacted_arena` to bounds-check the result.\n    pub fn adjust_range(&self, range: &mut Range<T>, compacted_arena: &Arena<T>) {\n        let mut index_range = range.index_range();\n        let compacted;\n        if let Some(first) = index_range.find_map(|i| self.new_index[i as usize]) {\n            // The first call to `find_map` mutated `index_range` to hold the\n            // remainder of original range, which is exactly the range we need\n            // to search for the new last handle.\n            if let Some(last) = index_range.rev().find_map(|i| self.new_index[i as usize]) {\n                // Build an end-exclusive range, given the two included indices\n                // `first` and `last`.\n                compacted = first.get()..last.get() + 1;\n            } else {\n                // The range contains only a single live handle, which\n                // we identified with the first `find_map` call.\n                compacted = first.get()..first.get() + 1;\n            }\n        } else {\n            compacted = 0..0;\n        };\n        *range = Range::from_index_range(compacted, compacted_arena);\n    }\n}\n"
  },
  {
    "path": "naga/src/compact/mod.rs",
    "content": "mod expressions;\nmod functions;\nmod handle_set_map;\nmod statements;\nmod types;\n\nuse alloc::vec::Vec;\n\nuse crate::{\n    arena::{self, HandleSet},\n    compact::functions::FunctionTracer,\n    ir,\n};\nuse handle_set_map::HandleMap;\n\n#[cfg(test)]\nuse alloc::{format, string::ToString};\n\n/// Configuration option for [`compact`]. See [`compact`] for details.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum KeepUnused {\n    No,\n    Yes,\n}\n\nimpl From<KeepUnused> for bool {\n    fn from(keep_unused: KeepUnused) -> Self {\n        match keep_unused {\n            KeepUnused::No => false,\n            KeepUnused::Yes => true,\n        }\n    }\n}\n\n/// Remove most unused objects from `module`, which must be valid.\n///\n/// Always removes the following unused objects:\n/// - anonymous types, overrides, and constants\n/// - abstract-typed constants\n/// - expressions\n///\n/// If `keep_unused` is `Yes`, the following are never considered unused,\n/// otherwise, they will also be removed if unused:\n/// - functions\n/// - global variables\n/// - named types and overrides\n///\n/// The following are never removed:\n/// - named constants with a concrete type\n/// - special types\n/// - entry points\n/// - within an entry point or a used function:\n///     - arguments\n///     - local variables\n///     - named expressions\n///\n/// After removing items according to the rules above, all handles in the\n/// remaining objects are adjusted as necessary. When `KeepUnused` is `Yes`, the\n/// resulting module should have all the named objects (except abstract-typed\n/// constants) present in the original, and those objects should be functionally\n/// identical. When `KeepUnused` is `No`, the resulting module should have the\n/// entry points present in the original, and those entry points should be\n/// functionally identical.\n///\n/// # Panics\n///\n/// If `module` would not pass validation, this may panic.\npub fn compact(module: &mut crate::Module, keep_unused: KeepUnused) {\n    // The trickiest part of compaction is determining what is used and what is\n    // not. Once we have computed that correctly, it's easy enough to call\n    // `retain_mut` on each arena, drop unused elements, and fix up the handles\n    // in what's left.\n    //\n    // For every compactable arena in a `Module`, whether global to the `Module`\n    // or local to a function or entry point, the `ModuleTracer` type holds a\n    // bitmap indicating which elements of that arena are used. Our task is to\n    // populate those bitmaps correctly.\n    //\n    // First, we mark everything that is considered used by definition, as\n    // described in this function's documentation.\n    //\n    // Since functions and entry points are considered used by definition, we\n    // traverse their statement trees, and mark the referents of all handles\n    // appearing in those statements as used.\n    //\n    // Once we've marked which elements of an arena are referred to directly by\n    // handles elsewhere (for example, which of a function's expressions are\n    // referred to by handles in its body statements), we can mark all the other\n    // arena elements that are used indirectly in a single pass, traversing the\n    // arena from back to front. Since Naga allows arena elements to refer only\n    // to prior elements, we know that by the time we reach an element, all\n    // other elements that could possibly refer to it have already been visited.\n    // Thus, if the present element has not been marked as used, then it is\n    // definitely unused, and compaction can remove it. Otherwise, the element\n    // is used and must be retained, so we must mark everything it refers to.\n    //\n    // The final step is to mark the global expressions and types, which must be\n    // traversed simultaneously; see `ModuleTracer::type_expression_tandem`'s\n    // documentation for details.\n    //\n    // # A definition and a rule of thumb\n    //\n    // In this module, to \"trace\" something is to mark everything else it refers\n    // to as used, on the assumption that the thing itself is used. For example,\n    // to trace an `Expression` is to mark its subexpressions as used, as well\n    // as any types, constants, overrides, etc. that it refers to. This is what\n    // `ExpressionTracer::trace_expression` does.\n    //\n    // Given that we we want to visit each thing only once (to keep compaction\n    // linear in the size of the module), this definition of \"trace\" implies\n    // that things that are not \"used by definition\" must be marked as used\n    // *before* we trace them.\n    //\n    // Thus, whenever you are marking something as used, it's a good idea to ask\n    // yourself how you know that thing will be traced in the future. If you're\n    // not sure, then you could be marking it too late to be noticed. The thing\n    // itself will be retained by compaction, but since it will not be traced,\n    // anything it refers to could be compacted away.\n    let mut module_tracer = ModuleTracer::new(module);\n\n    // Observe what each entry point actually uses.\n    log::trace!(\"tracing entry points\");\n    let entry_point_maps = module\n        .entry_points\n        .iter()\n        .map(|e| {\n            log::trace!(\"tracing entry point {:?}\", e.function.name);\n\n            if let Some(sizes) = e.workgroup_size_overrides {\n                for size in sizes.iter().filter_map(|x| *x) {\n                    module_tracer.global_expressions_used.insert(size);\n                }\n            }\n\n            if let Some(task_payload) = e.task_payload {\n                module_tracer.global_variables_used.insert(task_payload);\n            }\n            if let Some(ref mesh_info) = e.mesh_info {\n                module_tracer\n                    .global_variables_used\n                    .insert(mesh_info.output_variable);\n                module_tracer\n                    .types_used\n                    .insert(mesh_info.vertex_output_type);\n                module_tracer\n                    .types_used\n                    .insert(mesh_info.primitive_output_type);\n                if let Some(max_vertices_override) = mesh_info.max_vertices_override {\n                    module_tracer\n                        .global_expressions_used\n                        .insert(max_vertices_override);\n                }\n                if let Some(max_primitives_override) = mesh_info.max_primitives_override {\n                    module_tracer\n                        .global_expressions_used\n                        .insert(max_primitives_override);\n                }\n            }\n            if e.stage == crate::ShaderStage::Task || e.stage == crate::ShaderStage::Mesh {\n                // Mesh shaders always need a u32 type, as it is e.g. the type of some\n                // expressions. We tolerate its absence here because compaction is\n                // infallible, but the module will fail validation.\n                if let Some(u32_type) = module.types.iter().find_map(|tuple| {\n                    (tuple.1.inner == crate::TypeInner::Scalar(crate::Scalar::U32))\n                        .then_some(tuple.0)\n                }) {\n                    module_tracer.types_used.insert(u32_type);\n                }\n            }\n\n            let mut used = module_tracer.as_function(&e.function);\n            used.trace();\n            FunctionMap::from(used)\n        })\n        .collect::<Vec<_>>();\n\n    // Observe which types, constant expressions, constants, and expressions\n    // each function uses, and produce maps for each function from\n    // pre-compaction to post-compaction expression handles.\n    //\n    // The function tracing logic here works in conjunction with\n    // `FunctionTracer::trace_call`, which, when tracing a `Statement::Call`\n    // to a function not already identified as used, adds the called function\n    // to both `functions_used` and `functions_pending`.\n    //\n    // Called functions are required to appear before their callers in the\n    // functions arena (recursion is disallowed). We have already traced the\n    // entry point(s) and added any functions called directly by the entry\n    // point(s) to `functions_pending`. We proceed by repeatedly tracing the\n    // last function in `functions_pending`. By an inductive argument, any\n    // functions after the last function in `functions_pending` must be unused.\n    //\n    // When `KeepUnused` is active, we simply mark all functions as pending,\n    // and then trace all of them.\n    log::trace!(\"tracing functions\");\n    let mut function_maps = HandleMap::with_capacity(module.functions.len());\n    if keep_unused.into() {\n        module_tracer.functions_used.add_all();\n        module_tracer.functions_pending.add_all();\n    }\n    while let Some(handle) = module_tracer.functions_pending.pop() {\n        let function = &module.functions[handle];\n        log::trace!(\"tracing function {function:?}\");\n        let mut function_tracer = module_tracer.as_function(function);\n        function_tracer.trace();\n        function_maps.insert(handle, FunctionMap::from(function_tracer));\n    }\n\n    // We treat all special types as used by definition.\n    log::trace!(\"tracing special types\");\n    module_tracer.trace_special_types(&module.special_types);\n\n    log::trace!(\"tracing global variables\");\n    if keep_unused.into() {\n        module_tracer.global_variables_used.add_all();\n    }\n    for global in module_tracer.global_variables_used.iter() {\n        log::trace!(\"tracing global {:?}\", module.global_variables[global].name);\n        module_tracer\n            .types_used\n            .insert(module.global_variables[global].ty);\n        if let Some(init) = module.global_variables[global].init {\n            module_tracer.global_expressions_used.insert(init);\n        }\n    }\n\n    // We treat all named constants as used by definition, unless they have an\n    // abstract type as we do not want those reaching the validator.\n    log::trace!(\"tracing named constants\");\n    for (handle, constant) in module.constants.iter() {\n        if constant.name.is_none() || module.types[constant.ty].inner.is_abstract(&module.types) {\n            continue;\n        }\n\n        log::trace!(\"tracing constant {:?}\", constant.name.as_ref().unwrap());\n        module_tracer.constants_used.insert(handle);\n        module_tracer.types_used.insert(constant.ty);\n        module_tracer.global_expressions_used.insert(constant.init);\n    }\n\n    if keep_unused.into() {\n        // Treat all named overrides as used.\n        for (handle, r#override) in module.overrides.iter() {\n            if r#override.name.is_some() && module_tracer.overrides_used.insert(handle) {\n                module_tracer.types_used.insert(r#override.ty);\n                if let Some(init) = r#override.init {\n                    module_tracer.global_expressions_used.insert(init);\n                }\n            }\n        }\n\n        // Treat all named types as used.\n        for (handle, ty) in module.types.iter() {\n            if ty.name.is_some() {\n                module_tracer.types_used.insert(handle);\n            }\n        }\n    }\n\n    module_tracer.type_expression_tandem();\n\n    // Now that we know what is used and what is never touched,\n    // produce maps from the `Handle`s that appear in `module` now to\n    // the corresponding `Handle`s that will refer to the same items\n    // in the compacted module.\n    let module_map = ModuleMap::from(module_tracer);\n\n    // Drop unused types from the type arena.\n    //\n    // `FastIndexSet`s don't have an underlying Vec<T> that we can\n    // steal, compact in place, and then rebuild the `FastIndexSet`\n    // from. So we have to rebuild the type arena from scratch.\n    log::trace!(\"compacting types\");\n    let mut new_types = arena::UniqueArena::new();\n    for (old_handle, mut ty, span) in module.types.drain_all() {\n        if let Some(expected_new_handle) = module_map.types.try_adjust(old_handle) {\n            module_map.adjust_type(&mut ty);\n            let actual_new_handle = new_types.insert(ty, span);\n            assert_eq!(actual_new_handle, expected_new_handle);\n        }\n    }\n    module.types = new_types;\n    log::trace!(\"adjusting special types\");\n    module_map.adjust_special_types(&mut module.special_types);\n\n    // Drop unused constant expressions, reusing existing storage.\n    log::trace!(\"adjusting constant expressions\");\n    module.global_expressions.retain_mut(|handle, expr| {\n        if module_map.global_expressions.used(handle) {\n            module_map.adjust_expression(expr, &module_map.global_expressions);\n            true\n        } else {\n            false\n        }\n    });\n\n    // Drop unused constants in place, reusing existing storage.\n    log::trace!(\"adjusting constants\");\n    module.constants.retain_mut(|handle, constant| {\n        if module_map.constants.used(handle) {\n            module_map.types.adjust(&mut constant.ty);\n            module_map.global_expressions.adjust(&mut constant.init);\n            true\n        } else {\n            false\n        }\n    });\n\n    // Drop unused overrides in place, reusing existing storage.\n    log::trace!(\"adjusting overrides\");\n    module.overrides.retain_mut(|handle, r#override| {\n        if module_map.overrides.used(handle) {\n            module_map.types.adjust(&mut r#override.ty);\n            if let Some(ref mut init) = r#override.init {\n                module_map.global_expressions.adjust(init);\n            }\n            true\n        } else {\n            false\n        }\n    });\n\n    // Adjust workgroup_size_overrides\n    log::trace!(\"adjusting workgroup_size_overrides\");\n    for e in module.entry_points.iter_mut() {\n        if let Some(sizes) = e.workgroup_size_overrides.as_mut() {\n            for size in sizes.iter_mut() {\n                if let Some(expr) = size.as_mut() {\n                    module_map.global_expressions.adjust(expr);\n                }\n            }\n        }\n    }\n\n    // Drop unused global variables, reusing existing storage.\n    // Adjust used global variables' types and initializers.\n    log::trace!(\"adjusting global variables\");\n    module.global_variables.retain_mut(|handle, global| {\n        if module_map.globals.used(handle) {\n            log::trace!(\"retaining global variable {:?}\", global.name);\n            module_map.types.adjust(&mut global.ty);\n            if let Some(ref mut init) = global.init {\n                module_map.global_expressions.adjust(init);\n            }\n            true\n        } else {\n            log::trace!(\"dropping global variable {:?}\", global.name);\n            false\n        }\n    });\n\n    // Adjust doc comments\n    if let Some(ref mut doc_comments) = module.doc_comments {\n        module_map.adjust_doc_comments(doc_comments.as_mut());\n    }\n\n    // Temporary storage to help us reuse allocations of existing\n    // named expression tables.\n    let mut reused_named_expressions = crate::NamedExpressions::default();\n\n    // Drop unused functions. Compact and adjust used functions.\n    module.functions.retain_mut(|handle, function| {\n        if let Some(map) = function_maps.get(handle) {\n            log::trace!(\"retaining and compacting function {:?}\", function.name);\n            map.compact(function, &module_map, &mut reused_named_expressions);\n            true\n        } else {\n            log::trace!(\"dropping function {:?}\", function.name);\n            false\n        }\n    });\n\n    // Compact each entry point.\n    for (entry, map) in module.entry_points.iter_mut().zip(entry_point_maps.iter()) {\n        log::trace!(\"compacting entry point {:?}\", entry.function.name);\n        map.compact(\n            &mut entry.function,\n            &module_map,\n            &mut reused_named_expressions,\n        );\n        if let Some(ref mut task_payload) = entry.task_payload {\n            module_map.globals.adjust(task_payload);\n        }\n        if let Some(ref mut mesh_info) = entry.mesh_info {\n            module_map.globals.adjust(&mut mesh_info.output_variable);\n            module_map.types.adjust(&mut mesh_info.vertex_output_type);\n            module_map\n                .types\n                .adjust(&mut mesh_info.primitive_output_type);\n            if let Some(ref mut max_vertices_override) = mesh_info.max_vertices_override {\n                module_map.global_expressions.adjust(max_vertices_override);\n            }\n            if let Some(ref mut max_primitives_override) = mesh_info.max_primitives_override {\n                module_map\n                    .global_expressions\n                    .adjust(max_primitives_override);\n            }\n        }\n    }\n}\n\nstruct ModuleTracer<'module> {\n    module: &'module crate::Module,\n\n    /// The subset of functions in `functions_used` that have not yet been\n    /// traced.\n    functions_pending: HandleSet<crate::Function>,\n\n    functions_used: HandleSet<crate::Function>,\n    types_used: HandleSet<crate::Type>,\n    global_variables_used: HandleSet<crate::GlobalVariable>,\n    constants_used: HandleSet<crate::Constant>,\n    overrides_used: HandleSet<crate::Override>,\n    global_expressions_used: HandleSet<crate::Expression>,\n}\n\nimpl<'module> ModuleTracer<'module> {\n    fn new(module: &'module crate::Module) -> Self {\n        Self {\n            module,\n            functions_pending: HandleSet::for_arena(&module.functions),\n            functions_used: HandleSet::for_arena(&module.functions),\n            types_used: HandleSet::for_arena(&module.types),\n            global_variables_used: HandleSet::for_arena(&module.global_variables),\n            constants_used: HandleSet::for_arena(&module.constants),\n            overrides_used: HandleSet::for_arena(&module.overrides),\n            global_expressions_used: HandleSet::for_arena(&module.global_expressions),\n        }\n    }\n\n    fn trace_special_types(&mut self, special_types: &crate::SpecialTypes) {\n        let crate::SpecialTypes {\n            ref ray_desc,\n            ref ray_intersection,\n            ref ray_vertex_return,\n            ref predeclared_types,\n            ref external_texture_params,\n            ref external_texture_transfer_function,\n        } = *special_types;\n\n        if let Some(ray_desc) = *ray_desc {\n            self.types_used.insert(ray_desc);\n        }\n        if let Some(ray_intersection) = *ray_intersection {\n            self.types_used.insert(ray_intersection);\n        }\n        if let Some(ray_vertex_return) = *ray_vertex_return {\n            self.types_used.insert(ray_vertex_return);\n        }\n        // The `external_texture_params` type is generated purely as a\n        // convenience to the backends. While it will never actually be used in\n        // the IR, it must be marked as used so that it survives compaction.\n        if let Some(external_texture_params) = *external_texture_params {\n            self.types_used.insert(external_texture_params);\n        }\n        if let Some(external_texture_transfer_function) = *external_texture_transfer_function {\n            self.types_used.insert(external_texture_transfer_function);\n        }\n        for (_, &handle) in predeclared_types {\n            self.types_used.insert(handle);\n        }\n    }\n\n    /// Traverse types and global expressions in tandem to determine which are used.\n    ///\n    /// Assuming that all types and global expressions used by other parts of\n    /// the module have been added to [`types_used`] and\n    /// [`global_expressions_used`], expand those sets to include all types and\n    /// global expressions reachable from those.\n    ///\n    /// [`types_used`]: ModuleTracer::types_used\n    /// [`global_expressions_used`]: ModuleTracer::global_expressions_used\n    fn type_expression_tandem(&mut self) {\n        // For each type T, compute the latest global expression E that T and\n        // its predecessors refer to. Given the ordering rules on types and\n        // global expressions in valid modules, we can do this with a single\n        // forward scan of the type arena. The rules further imply that T can\n        // only be referred to by expressions after E.\n        let mut max_dep = Vec::with_capacity(self.module.types.len());\n        let mut previous = None;\n        for (_handle, ty) in self.module.types.iter() {\n            previous = core::cmp::max(\n                previous,\n                match ty.inner {\n                    crate::TypeInner::Array { size, .. }\n                    | crate::TypeInner::BindingArray { size, .. } => match size {\n                        crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => None,\n                        crate::ArraySize::Pending(handle) => self.module.overrides[handle].init,\n                    },\n                    _ => None,\n                },\n            );\n            max_dep.push(previous);\n        }\n\n        // Visit types and global expressions from youngest to oldest.\n        //\n        // The outer loop visits types. Before visiting each type, the inner\n        // loop ensures that all global expressions that could possibly refer to\n        // it have been visited. And since the inner loop stop at the latest\n        // expression that the type could possibly refer to, we know that we\n        // have previously visited any types that might refer to each expression\n        // we visit.\n        //\n        // This lets us assume that any type or expression that is *not* marked\n        // as used by the time we visit it is genuinely unused, and can be\n        // ignored.\n        let mut exprs = self.module.global_expressions.iter().rev().peekable();\n\n        for ((ty_handle, ty), dep) in self.module.types.iter().zip(max_dep).rev() {\n            while let Some((expr_handle, expr)) = exprs.next_if(|&(h, _)| Some(h) > dep) {\n                if self.global_expressions_used.contains(expr_handle) {\n                    self.as_const_expression().trace_expression(expr);\n                }\n            }\n            if self.types_used.contains(ty_handle) {\n                self.as_type().trace_type(ty);\n            }\n        }\n        // Visit any remaining expressions.\n        for (expr_handle, expr) in exprs {\n            if self.global_expressions_used.contains(expr_handle) {\n                self.as_const_expression().trace_expression(expr);\n            }\n        }\n    }\n\n    const fn as_type(&mut self) -> types::TypeTracer<'_> {\n        types::TypeTracer {\n            overrides: &self.module.overrides,\n            types_used: &mut self.types_used,\n            expressions_used: &mut self.global_expressions_used,\n            overrides_used: &mut self.overrides_used,\n        }\n    }\n\n    const fn as_const_expression(&mut self) -> expressions::ExpressionTracer<'_> {\n        expressions::ExpressionTracer {\n            constants: &self.module.constants,\n            overrides: &self.module.overrides,\n            expressions: &self.module.global_expressions,\n            types_used: &mut self.types_used,\n            global_variables_used: &mut self.global_variables_used,\n            constants_used: &mut self.constants_used,\n            expressions_used: &mut self.global_expressions_used,\n            overrides_used: &mut self.overrides_used,\n            global_expressions_used: None,\n        }\n    }\n\n    pub fn as_function<'tracer>(\n        &'tracer mut self,\n        function: &'tracer crate::Function,\n    ) -> FunctionTracer<'tracer> {\n        FunctionTracer {\n            function,\n            constants: &self.module.constants,\n            overrides: &self.module.overrides,\n            functions_pending: &mut self.functions_pending,\n            functions_used: &mut self.functions_used,\n            types_used: &mut self.types_used,\n            global_variables_used: &mut self.global_variables_used,\n            constants_used: &mut self.constants_used,\n            overrides_used: &mut self.overrides_used,\n            global_expressions_used: &mut self.global_expressions_used,\n            expressions_used: HandleSet::for_arena(&function.expressions),\n        }\n    }\n}\n\nstruct ModuleMap {\n    functions: HandleMap<crate::Function>,\n    types: HandleMap<crate::Type>,\n    globals: HandleMap<crate::GlobalVariable>,\n    constants: HandleMap<crate::Constant>,\n    overrides: HandleMap<crate::Override>,\n    global_expressions: HandleMap<crate::Expression>,\n}\n\nimpl From<ModuleTracer<'_>> for ModuleMap {\n    fn from(used: ModuleTracer) -> Self {\n        ModuleMap {\n            functions: HandleMap::from_set(used.functions_used),\n            types: HandleMap::from_set(used.types_used),\n            globals: HandleMap::from_set(used.global_variables_used),\n            constants: HandleMap::from_set(used.constants_used),\n            overrides: HandleMap::from_set(used.overrides_used),\n            global_expressions: HandleMap::from_set(used.global_expressions_used),\n        }\n    }\n}\n\nimpl ModuleMap {\n    fn adjust_special_types(&self, special: &mut crate::SpecialTypes) {\n        let crate::SpecialTypes {\n            ref mut ray_desc,\n            ref mut ray_intersection,\n            ref mut ray_vertex_return,\n            ref mut predeclared_types,\n            ref mut external_texture_params,\n            ref mut external_texture_transfer_function,\n        } = *special;\n\n        if let Some(ref mut ray_desc) = *ray_desc {\n            self.types.adjust(ray_desc);\n        }\n        if let Some(ref mut ray_intersection) = *ray_intersection {\n            self.types.adjust(ray_intersection);\n        }\n\n        if let Some(ref mut ray_vertex_return) = *ray_vertex_return {\n            self.types.adjust(ray_vertex_return);\n        }\n\n        if let Some(ref mut external_texture_params) = *external_texture_params {\n            self.types.adjust(external_texture_params);\n        }\n\n        if let Some(ref mut external_texture_transfer_function) =\n            *external_texture_transfer_function\n        {\n            self.types.adjust(external_texture_transfer_function);\n        }\n\n        for handle in predeclared_types.values_mut() {\n            self.types.adjust(handle);\n        }\n    }\n\n    fn adjust_doc_comments(&self, doc_comments: &mut ir::DocComments) {\n        let crate::DocComments {\n            module: _,\n            types: ref mut doc_types,\n            struct_members: ref mut doc_struct_members,\n            entry_points: _,\n            functions: ref mut doc_functions,\n            constants: ref mut doc_constants,\n            global_variables: ref mut doc_globals,\n        } = *doc_comments;\n        log::trace!(\"adjusting doc comments for types\");\n        for (mut ty, doc_comment) in core::mem::take(doc_types) {\n            if !self.types.used(ty) {\n                continue;\n            }\n            self.types.adjust(&mut ty);\n            doc_types.insert(ty, doc_comment);\n        }\n        log::trace!(\"adjusting doc comments for struct members\");\n        for ((mut ty, index), doc_comment) in core::mem::take(doc_struct_members) {\n            if !self.types.used(ty) {\n                continue;\n            }\n            self.types.adjust(&mut ty);\n            doc_struct_members.insert((ty, index), doc_comment);\n        }\n        log::trace!(\"adjusting doc comments for functions\");\n        for (mut handle, doc_comment) in core::mem::take(doc_functions) {\n            if !self.functions.used(handle) {\n                continue;\n            }\n            self.functions.adjust(&mut handle);\n            doc_functions.insert(handle, doc_comment);\n        }\n        log::trace!(\"adjusting doc comments for constants\");\n        for (mut constant, doc_comment) in core::mem::take(doc_constants) {\n            if !self.constants.used(constant) {\n                continue;\n            }\n            self.constants.adjust(&mut constant);\n            doc_constants.insert(constant, doc_comment);\n        }\n        log::trace!(\"adjusting doc comments for globals\");\n        for (mut handle, doc_comment) in core::mem::take(doc_globals) {\n            if !self.globals.used(handle) {\n                continue;\n            }\n            self.globals.adjust(&mut handle);\n            doc_globals.insert(handle, doc_comment);\n        }\n    }\n}\n\nstruct FunctionMap {\n    expressions: HandleMap<crate::Expression>,\n}\n\nimpl From<FunctionTracer<'_>> for FunctionMap {\n    fn from(used: FunctionTracer) -> Self {\n        FunctionMap {\n            expressions: HandleMap::from_set(used.expressions_used),\n        }\n    }\n}\n\n#[test]\nfn type_expression_interdependence() {\n    let mut module: crate::Module = Default::default();\n    let u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Uint,\n                width: 4,\n            }),\n        },\n        crate::Span::default(),\n    );\n    let expr = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(0)),\n        crate::Span::default(),\n    );\n    let type_needs_expression = |module: &mut crate::Module, handle| {\n        let override_handle = module.overrides.append(\n            crate::Override {\n                name: None,\n                id: None,\n                ty: u32,\n                init: Some(handle),\n            },\n            crate::Span::default(),\n        );\n        module.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Array {\n                    base: u32,\n                    size: crate::ArraySize::Pending(override_handle),\n                    stride: 4,\n                },\n            },\n            crate::Span::default(),\n        )\n    };\n    let expression_needs_type = |module: &mut crate::Module, handle| {\n        module\n            .global_expressions\n            .append(crate::Expression::ZeroValue(handle), crate::Span::default())\n    };\n    let expression_needs_expression = |module: &mut crate::Module, handle| {\n        module.global_expressions.append(\n            crate::Expression::Load { pointer: handle },\n            crate::Span::default(),\n        )\n    };\n    let type_needs_type = |module: &mut crate::Module, handle| {\n        module.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Array {\n                    base: handle,\n                    size: crate::ArraySize::Dynamic,\n                    stride: 0,\n                },\n            },\n            crate::Span::default(),\n        )\n    };\n    let mut type_name_counter = 0;\n    let mut type_needed = |module: &mut crate::Module, handle| {\n        let name = Some(format!(\"type{type_name_counter}\"));\n        type_name_counter += 1;\n        module.types.insert(\n            crate::Type {\n                name,\n                inner: crate::TypeInner::Array {\n                    base: handle,\n                    size: crate::ArraySize::Dynamic,\n                    stride: 0,\n                },\n            },\n            crate::Span::default(),\n        )\n    };\n    let mut override_name_counter = 0;\n    let mut expression_needed = |module: &mut crate::Module, handle| {\n        let name = Some(format!(\"override{override_name_counter}\"));\n        override_name_counter += 1;\n        module.overrides.append(\n            crate::Override {\n                name,\n                id: None,\n                ty: u32,\n                init: Some(handle),\n            },\n            crate::Span::default(),\n        )\n    };\n    let cmp_modules = |mod0: &crate::Module, mod1: &crate::Module| {\n        (mod0.types.iter().collect::<Vec<_>>() == mod1.types.iter().collect::<Vec<_>>())\n            && (mod0.global_expressions.iter().collect::<Vec<_>>()\n                == mod1.global_expressions.iter().collect::<Vec<_>>())\n    };\n    // borrow checker breaks without the tmp variables as of Rust 1.83.0\n    let expr_end = type_needs_expression(&mut module, expr);\n    let ty_trace = type_needs_type(&mut module, expr_end);\n    let expr_init = expression_needs_type(&mut module, ty_trace);\n    expression_needed(&mut module, expr_init);\n    let ty_end = expression_needs_type(&mut module, u32);\n    let expr_trace = expression_needs_expression(&mut module, ty_end);\n    let ty_init = type_needs_expression(&mut module, expr_trace);\n    type_needed(&mut module, ty_init);\n    let untouched = module.clone();\n    compact(&mut module, KeepUnused::Yes);\n    assert!(cmp_modules(&module, &untouched));\n    let unused_expr = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(1)),\n        crate::Span::default(),\n    );\n    type_needs_expression(&mut module, unused_expr);\n    assert!(!cmp_modules(&module, &untouched));\n    compact(&mut module, KeepUnused::Yes);\n    assert!(cmp_modules(&module, &untouched));\n}\n\n#[test]\nfn array_length_override() {\n    let mut module: crate::Module = Default::default();\n    let ty_bool = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::BOOL),\n        },\n        crate::Span::default(),\n    );\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        crate::Span::default(),\n    );\n    let one = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(1)),\n        crate::Span::default(),\n    );\n    let _unused_override = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(40),\n            ty: ty_u32,\n            init: None,\n        },\n        crate::Span::default(),\n    );\n    let o = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(42),\n            ty: ty_u32,\n            init: Some(one),\n        },\n        crate::Span::default(),\n    );\n    let _ty_array = module.types.insert(\n        crate::Type {\n            name: Some(\"array<bool, o>\".to_string()),\n            inner: crate::TypeInner::Array {\n                base: ty_bool,\n                size: crate::ArraySize::Pending(o),\n                stride: 4,\n            },\n        },\n        crate::Span::default(),\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n/// Test mutual references between types and expressions via override\n/// lengths.\n#[test]\nfn array_length_override_mutual() {\n    use crate::Expression as Ex;\n    use crate::Scalar as Sc;\n    use crate::TypeInner as Ti;\n\n    let nowhere = crate::Span::default();\n    let mut module = crate::Module::default();\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: Ti::Scalar(Sc::U32),\n        },\n        nowhere,\n    );\n\n    // This type is only referred to by the override's init\n    // expression, so if we visit that too early, this type will be\n    // removed incorrectly.\n    let ty_i32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: Ti::Scalar(Sc::I32),\n        },\n        nowhere,\n    );\n\n    // An override that the other override's init can refer to.\n    let first_override = module.overrides.append(\n        crate::Override {\n            name: None, // so it is not considered used by definition\n            id: Some(41),\n            ty: ty_i32,\n            init: None,\n        },\n        nowhere,\n    );\n\n    // Initializer expression for the override:\n    //\n    //     (first_override + 0) as u32\n    //\n    // The `first_override` makes it an override expression; the `0`\n    // gets a use of `ty_i32` in there; and the `as` makes it match\n    // the type of `second_override` without actually making\n    // `second_override` point at `ty_i32` directly.\n    let first_override_expr = module\n        .global_expressions\n        .append(Ex::Override(first_override), nowhere);\n    let zero = module\n        .global_expressions\n        .append(Ex::ZeroValue(ty_i32), nowhere);\n    let sum = module.global_expressions.append(\n        Ex::Binary {\n            op: crate::BinaryOperator::Add,\n            left: first_override_expr,\n            right: zero,\n        },\n        nowhere,\n    );\n    let init = module.global_expressions.append(\n        Ex::As {\n            expr: sum,\n            kind: crate::ScalarKind::Uint,\n            convert: None,\n        },\n        nowhere,\n    );\n\n    // Override that serves as the array's length.\n    let second_override = module.overrides.append(\n        crate::Override {\n            name: None, // so it is not considered used by definition\n            id: Some(42),\n            ty: ty_u32,\n            init: Some(init),\n        },\n        nowhere,\n    );\n\n    // Array type that uses the overload as its length.\n    // Since this is named, it is considered used by definition.\n    let _ty_array = module.types.insert(\n        crate::Type {\n            name: Some(\"delicious_array\".to_string()),\n            inner: Ti::Array {\n                base: ty_u32,\n                size: crate::ArraySize::Pending(second_override),\n                stride: 4,\n            },\n        },\n        nowhere,\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n#[test]\nfn array_length_expression() {\n    let mut module: crate::Module = Default::default();\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        crate::Span::default(),\n    );\n    let _unused_zero = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(0)),\n        crate::Span::default(),\n    );\n    let one = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(1)),\n        crate::Span::default(),\n    );\n    let override_one = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: None,\n            ty: ty_u32,\n            init: Some(one),\n        },\n        crate::Span::default(),\n    );\n    let _ty_array = module.types.insert(\n        crate::Type {\n            name: Some(\"array<u32, 1>\".to_string()),\n            inner: crate::TypeInner::Array {\n                base: ty_u32,\n                size: crate::ArraySize::Pending(override_one),\n                stride: 4,\n            },\n        },\n        crate::Span::default(),\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n#[test]\nfn global_expression_override() {\n    let mut module: crate::Module = Default::default();\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        crate::Span::default(),\n    );\n\n    // This will only be retained if we trace the initializers\n    // of overrides referred to by `Expression::Override`\n    // in global expressions.\n    let expr1 = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(1)),\n        crate::Span::default(),\n    );\n\n    // This will only be traced via a global `Expression::Override`.\n    let o = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(42),\n            ty: ty_u32,\n            init: Some(expr1),\n        },\n        crate::Span::default(),\n    );\n\n    // This is retained by _p.\n    let expr2 = module\n        .global_expressions\n        .append(crate::Expression::Override(o), crate::Span::default());\n\n    // Since this is named, it will be retained.\n    let _p = module.overrides.append(\n        crate::Override {\n            name: Some(\"p\".to_string()),\n            id: None,\n            ty: ty_u32,\n            init: Some(expr2),\n        },\n        crate::Span::default(),\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n#[test]\nfn local_expression_override() {\n    let mut module: crate::Module = Default::default();\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        crate::Span::default(),\n    );\n\n    // This will only be retained if we trace the initializers\n    // of overrides referred to by `Expression::Override` in a function.\n    let expr1 = module.global_expressions.append(\n        crate::Expression::Literal(crate::Literal::U32(1)),\n        crate::Span::default(),\n    );\n\n    // This will be removed by compaction.\n    let _unused_override = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(41),\n            ty: ty_u32,\n            init: None,\n        },\n        crate::Span::default(),\n    );\n\n    // This will only be traced via an `Expression::Override` in a function.\n    let o = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(42),\n            ty: ty_u32,\n            init: Some(expr1),\n        },\n        crate::Span::default(),\n    );\n\n    let mut fun = crate::Function {\n        result: Some(crate::FunctionResult {\n            ty: ty_u32,\n            binding: None,\n        }),\n        ..crate::Function::default()\n    };\n\n    // This is used by the `Return` statement.\n    let o_expr = fun\n        .expressions\n        .append(crate::Expression::Override(o), crate::Span::default());\n    fun.body.push(\n        crate::Statement::Return {\n            value: Some(o_expr),\n        },\n        crate::Span::default(),\n    );\n\n    module.functions.append(fun, crate::Span::default());\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n#[test]\nfn unnamed_constant_type() {\n    let mut module = crate::Module::default();\n    let nowhere = crate::Span::default();\n\n    // This type is used only by the unnamed constant.\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        nowhere,\n    );\n\n    // This type is used by the named constant.\n    let ty_vec_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Vector {\n                size: crate::VectorSize::Bi,\n                scalar: crate::Scalar::U32,\n            },\n        },\n        nowhere,\n    );\n\n    let unnamed_init = module\n        .global_expressions\n        .append(crate::Expression::Literal(crate::Literal::U32(0)), nowhere);\n\n    let unnamed_constant = module.constants.append(\n        crate::Constant {\n            name: None,\n            ty: ty_u32,\n            init: unnamed_init,\n        },\n        nowhere,\n    );\n\n    // The named constant is initialized using a Splat expression, to\n    // give the named constant a type distinct from the unnamed\n    // constant's.\n    let unnamed_constant_expr = module\n        .global_expressions\n        .append(crate::Expression::Constant(unnamed_constant), nowhere);\n    let named_init = module.global_expressions.append(\n        crate::Expression::Splat {\n            size: crate::VectorSize::Bi,\n            value: unnamed_constant_expr,\n        },\n        nowhere,\n    );\n\n    let _named_constant = module.constants.append(\n        crate::Constant {\n            name: Some(\"totally_named\".to_string()),\n            ty: ty_vec_u32,\n            init: named_init,\n        },\n        nowhere,\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n\n#[test]\nfn unnamed_override_type() {\n    let mut module = crate::Module::default();\n    let nowhere = crate::Span::default();\n\n    // This type is used only by the unnamed override.\n    let ty_u32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n        },\n        nowhere,\n    );\n\n    // This type is used by the named override.\n    let ty_i32 = module.types.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Scalar(crate::Scalar::I32),\n        },\n        nowhere,\n    );\n\n    let unnamed_init = module\n        .global_expressions\n        .append(crate::Expression::Literal(crate::Literal::U32(0)), nowhere);\n\n    let unnamed_override = module.overrides.append(\n        crate::Override {\n            name: None,\n            id: Some(42),\n            ty: ty_u32,\n            init: Some(unnamed_init),\n        },\n        nowhere,\n    );\n\n    // The named override is initialized using a Splat expression, to\n    // give the named override a type distinct from the unnamed\n    // override's.\n    let unnamed_override_expr = module\n        .global_expressions\n        .append(crate::Expression::Override(unnamed_override), nowhere);\n    let named_init = module.global_expressions.append(\n        crate::Expression::As {\n            expr: unnamed_override_expr,\n            kind: crate::ScalarKind::Sint,\n            convert: None,\n        },\n        nowhere,\n    );\n\n    let _named_override = module.overrides.append(\n        crate::Override {\n            name: Some(\"totally_named\".to_string()),\n            id: None,\n            ty: ty_i32,\n            init: Some(named_init),\n        },\n        nowhere,\n    );\n\n    let mut validator = super::valid::Validator::new(\n        super::valid::ValidationFlags::all(),\n        super::valid::Capabilities::all(),\n    );\n\n    assert!(validator.validate(&module).is_ok());\n    compact(&mut module, KeepUnused::Yes);\n    assert!(validator.validate(&module).is_ok());\n}\n"
  },
  {
    "path": "naga/src/compact/statements.rs",
    "content": "use alloc::{vec, vec::Vec};\n\nuse super::functions::FunctionTracer;\nuse super::FunctionMap;\nuse crate::arena::Handle;\nuse crate::compact::handle_set_map::HandleMap;\n\nimpl FunctionTracer<'_> {\n    pub fn trace_block(&mut self, block: &[crate::Statement]) {\n        let mut worklist: Vec<&[crate::Statement]> = vec![block];\n        while let Some(last) = worklist.pop() {\n            for stmt in last {\n                use crate::Statement as St;\n                match *stmt {\n                    St::Emit(ref _range) => {\n                        // If we come across a statement that actually uses an\n                        // expression in this range, it'll get traced from\n                        // there. But since evaluating expressions has no\n                        // effect, we don't need to assume that everything\n                        // emitted is live.\n                    }\n                    St::Block(ref block) => worklist.push(block),\n                    St::If {\n                        condition,\n                        ref accept,\n                        ref reject,\n                    } => {\n                        self.expressions_used.insert(condition);\n                        worklist.push(accept);\n                        worklist.push(reject);\n                    }\n                    St::Switch {\n                        selector,\n                        ref cases,\n                    } => {\n                        self.expressions_used.insert(selector);\n                        for case in cases {\n                            worklist.push(&case.body);\n                        }\n                    }\n                    St::Loop {\n                        ref body,\n                        ref continuing,\n                        break_if,\n                    } => {\n                        if let Some(break_if) = break_if {\n                            self.expressions_used.insert(break_if);\n                        }\n                        worklist.push(body);\n                        worklist.push(continuing);\n                    }\n                    St::Return { value: Some(value) } => {\n                        self.expressions_used.insert(value);\n                    }\n                    St::Store { pointer, value } => {\n                        self.expressions_used.insert(pointer);\n                        self.expressions_used.insert(value);\n                    }\n                    St::ImageStore {\n                        image,\n                        coordinate,\n                        array_index,\n                        value,\n                    } => {\n                        self.expressions_used.insert(image);\n                        self.expressions_used.insert(coordinate);\n                        if let Some(array_index) = array_index {\n                            self.expressions_used.insert(array_index);\n                        }\n                        self.expressions_used.insert(value);\n                    }\n                    St::Atomic {\n                        pointer,\n                        ref fun,\n                        value,\n                        result,\n                    } => {\n                        self.expressions_used.insert(pointer);\n                        self.trace_atomic_function(fun);\n                        self.expressions_used.insert(value);\n                        if let Some(result) = result {\n                            self.expressions_used.insert(result);\n                        }\n                    }\n                    St::ImageAtomic {\n                        image,\n                        coordinate,\n                        array_index,\n                        fun: _,\n                        value,\n                    } => {\n                        self.expressions_used.insert(image);\n                        self.expressions_used.insert(coordinate);\n                        if let Some(array_index) = array_index {\n                            self.expressions_used.insert(array_index);\n                        }\n                        self.expressions_used.insert(value);\n                    }\n                    St::WorkGroupUniformLoad { pointer, result } => {\n                        self.expressions_used.insert(pointer);\n                        self.expressions_used.insert(result);\n                    }\n                    St::Call {\n                        function,\n                        ref arguments,\n                        result,\n                    } => {\n                        self.trace_call(function);\n                        for expr in arguments {\n                            self.expressions_used.insert(*expr);\n                        }\n                        if let Some(result) = result {\n                            self.expressions_used.insert(result);\n                        }\n                    }\n                    St::RayQuery { query, ref fun } => {\n                        self.expressions_used.insert(query);\n                        self.trace_ray_query_function(fun);\n                    }\n                    St::SubgroupBallot { result, predicate } => {\n                        if let Some(predicate) = predicate {\n                            self.expressions_used.insert(predicate);\n                        }\n                        self.expressions_used.insert(result);\n                    }\n                    St::SubgroupCollectiveOperation {\n                        op: _,\n                        collective_op: _,\n                        argument,\n                        result,\n                    } => {\n                        self.expressions_used.insert(argument);\n                        self.expressions_used.insert(result);\n                    }\n                    St::SubgroupGather {\n                        mode,\n                        argument,\n                        result,\n                    } => {\n                        match mode {\n                            crate::GatherMode::BroadcastFirst => {}\n                            crate::GatherMode::Broadcast(index)\n                            | crate::GatherMode::Shuffle(index)\n                            | crate::GatherMode::ShuffleDown(index)\n                            | crate::GatherMode::ShuffleUp(index)\n                            | crate::GatherMode::ShuffleXor(index)\n                            | crate::GatherMode::QuadBroadcast(index) => {\n                                self.expressions_used.insert(index);\n                            }\n                            crate::GatherMode::QuadSwap(_) => {}\n                        }\n                        self.expressions_used.insert(argument);\n                        self.expressions_used.insert(result);\n                    }\n                    St::CooperativeStore { target, ref data } => {\n                        self.expressions_used.insert(target);\n                        self.expressions_used.insert(data.pointer);\n                        self.expressions_used.insert(data.stride);\n                    }\n                    St::RayPipelineFunction(func) => match func {\n                        crate::RayPipelineFunction::TraceRay {\n                            acceleration_structure,\n                            descriptor,\n                            payload,\n                        } => {\n                            self.expressions_used.insert(acceleration_structure);\n                            self.expressions_used.insert(descriptor);\n                            self.expressions_used.insert(payload);\n                        }\n                    },\n\n                    // Trivial statements.\n                    St::Break\n                    | St::Continue\n                    | St::Kill\n                    | St::ControlBarrier(_)\n                    | St::MemoryBarrier(_)\n                    | St::Return { value: None } => {}\n                }\n            }\n        }\n    }\n\n    fn trace_atomic_function(&mut self, fun: &crate::AtomicFunction) {\n        use crate::AtomicFunction as Af;\n        match *fun {\n            Af::Exchange {\n                compare: Some(expr),\n            } => {\n                self.expressions_used.insert(expr);\n            }\n            Af::Exchange { compare: None }\n            | Af::Add\n            | Af::Subtract\n            | Af::And\n            | Af::ExclusiveOr\n            | Af::InclusiveOr\n            | Af::Min\n            | Af::Max => {}\n        }\n    }\n\n    fn trace_ray_query_function(&mut self, fun: &crate::RayQueryFunction) {\n        use crate::RayQueryFunction as Qf;\n        match *fun {\n            Qf::Initialize {\n                acceleration_structure,\n                descriptor,\n            } => {\n                self.expressions_used.insert(acceleration_structure);\n                self.expressions_used.insert(descriptor);\n            }\n            Qf::Proceed { result } => {\n                self.expressions_used.insert(result);\n            }\n            Qf::GenerateIntersection { hit_t } => {\n                self.expressions_used.insert(hit_t);\n            }\n            Qf::ConfirmIntersection => {}\n            Qf::Terminate => {}\n        }\n    }\n}\n\nimpl FunctionMap {\n    /// Adjust statements in the body of `function`.\n    ///\n    /// Adjusts expressions using `self.expressions`, and adjusts calls to other\n    /// functions using `function_map`.\n    pub fn adjust_body(\n        &self,\n        function: &mut crate::Function,\n        function_map: &HandleMap<crate::Function>,\n    ) {\n        let block = &mut function.body;\n        let mut worklist: Vec<&mut [crate::Statement]> = vec![block];\n        let adjust = |handle: &mut Handle<crate::Expression>| {\n            self.expressions.adjust(handle);\n        };\n        while let Some(last) = worklist.pop() {\n            for stmt in last {\n                use crate::Statement as St;\n                match *stmt {\n                    St::Emit(ref mut range) => {\n                        self.expressions.adjust_range(range, &function.expressions);\n                    }\n                    St::Block(ref mut block) => worklist.push(block),\n                    St::If {\n                        ref mut condition,\n                        ref mut accept,\n                        ref mut reject,\n                    } => {\n                        adjust(condition);\n                        worklist.push(accept);\n                        worklist.push(reject);\n                    }\n                    St::Switch {\n                        ref mut selector,\n                        ref mut cases,\n                    } => {\n                        adjust(selector);\n                        for case in cases {\n                            worklist.push(&mut case.body);\n                        }\n                    }\n                    St::Loop {\n                        ref mut body,\n                        ref mut continuing,\n                        ref mut break_if,\n                    } => {\n                        if let Some(ref mut break_if) = *break_if {\n                            adjust(break_if);\n                        }\n                        worklist.push(body);\n                        worklist.push(continuing);\n                    }\n                    St::Return {\n                        value: Some(ref mut value),\n                    } => adjust(value),\n                    St::Store {\n                        ref mut pointer,\n                        ref mut value,\n                    } => {\n                        adjust(pointer);\n                        adjust(value);\n                    }\n                    St::ImageStore {\n                        ref mut image,\n                        ref mut coordinate,\n                        ref mut array_index,\n                        ref mut value,\n                    } => {\n                        adjust(image);\n                        adjust(coordinate);\n                        if let Some(ref mut array_index) = *array_index {\n                            adjust(array_index);\n                        }\n                        adjust(value);\n                    }\n                    St::Atomic {\n                        ref mut pointer,\n                        ref mut fun,\n                        ref mut value,\n                        ref mut result,\n                    } => {\n                        adjust(pointer);\n                        self.adjust_atomic_function(fun);\n                        adjust(value);\n                        if let Some(ref mut result) = *result {\n                            adjust(result);\n                        }\n                    }\n                    St::ImageAtomic {\n                        ref mut image,\n                        ref mut coordinate,\n                        ref mut array_index,\n                        fun: _,\n                        ref mut value,\n                    } => {\n                        adjust(image);\n                        adjust(coordinate);\n                        if let Some(ref mut array_index) = *array_index {\n                            adjust(array_index);\n                        }\n                        adjust(value);\n                    }\n                    St::WorkGroupUniformLoad {\n                        ref mut pointer,\n                        ref mut result,\n                    } => {\n                        adjust(pointer);\n                        adjust(result);\n                    }\n                    St::Call {\n                        ref mut function,\n                        ref mut arguments,\n                        ref mut result,\n                    } => {\n                        function_map.adjust(function);\n                        for expr in arguments {\n                            adjust(expr);\n                        }\n                        if let Some(ref mut result) = *result {\n                            adjust(result);\n                        }\n                    }\n                    St::RayQuery {\n                        ref mut query,\n                        ref mut fun,\n                    } => {\n                        adjust(query);\n                        self.adjust_ray_query_function(fun);\n                    }\n                    St::SubgroupBallot {\n                        ref mut result,\n                        ref mut predicate,\n                    } => {\n                        if let Some(ref mut predicate) = *predicate {\n                            adjust(predicate);\n                        }\n                        adjust(result);\n                    }\n                    St::SubgroupCollectiveOperation {\n                        op: _,\n                        collective_op: _,\n                        ref mut argument,\n                        ref mut result,\n                    } => {\n                        adjust(argument);\n                        adjust(result);\n                    }\n                    St::SubgroupGather {\n                        ref mut mode,\n                        ref mut argument,\n                        ref mut result,\n                    } => {\n                        match *mode {\n                            crate::GatherMode::BroadcastFirst => {}\n                            crate::GatherMode::Broadcast(ref mut index)\n                            | crate::GatherMode::Shuffle(ref mut index)\n                            | crate::GatherMode::ShuffleDown(ref mut index)\n                            | crate::GatherMode::ShuffleUp(ref mut index)\n                            | crate::GatherMode::ShuffleXor(ref mut index)\n                            | crate::GatherMode::QuadBroadcast(ref mut index) => adjust(index),\n                            crate::GatherMode::QuadSwap(_) => {}\n                        }\n                        adjust(argument);\n                        adjust(result);\n                    }\n                    St::CooperativeStore {\n                        ref mut target,\n                        ref mut data,\n                    } => {\n                        adjust(target);\n                        adjust(&mut data.pointer);\n                        adjust(&mut data.stride);\n                    }\n                    St::RayPipelineFunction(ref mut func) => match *func {\n                        crate::RayPipelineFunction::TraceRay {\n                            ref mut acceleration_structure,\n                            ref mut descriptor,\n                            ref mut payload,\n                        } => {\n                            adjust(acceleration_structure);\n                            adjust(descriptor);\n                            adjust(payload);\n                        }\n                    },\n\n                    // Trivial statements.\n                    St::Break\n                    | St::Continue\n                    | St::Kill\n                    | St::ControlBarrier(_)\n                    | St::MemoryBarrier(_)\n                    | St::Return { value: None } => {}\n                }\n            }\n        }\n    }\n\n    fn adjust_atomic_function(&self, fun: &mut crate::AtomicFunction) {\n        use crate::AtomicFunction as Af;\n        match *fun {\n            Af::Exchange {\n                compare: Some(ref mut expr),\n            } => {\n                self.expressions.adjust(expr);\n            }\n            Af::Exchange { compare: None }\n            | Af::Add\n            | Af::Subtract\n            | Af::And\n            | Af::ExclusiveOr\n            | Af::InclusiveOr\n            | Af::Min\n            | Af::Max => {}\n        }\n    }\n\n    fn adjust_ray_query_function(&self, fun: &mut crate::RayQueryFunction) {\n        use crate::RayQueryFunction as Qf;\n        match *fun {\n            Qf::Initialize {\n                ref mut acceleration_structure,\n                ref mut descriptor,\n            } => {\n                self.expressions.adjust(acceleration_structure);\n                self.expressions.adjust(descriptor);\n            }\n            Qf::Proceed { ref mut result } => {\n                self.expressions.adjust(result);\n            }\n            Qf::GenerateIntersection { ref mut hit_t } => {\n                self.expressions.adjust(hit_t);\n            }\n            Qf::ConfirmIntersection => {}\n            Qf::Terminate => {}\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/compact/types.rs",
    "content": "use super::{HandleSet, ModuleMap};\nuse crate::Handle;\n\npub struct TypeTracer<'a> {\n    pub overrides: &'a crate::Arena<crate::Override>,\n    pub types_used: &'a mut HandleSet<crate::Type>,\n    pub expressions_used: &'a mut HandleSet<crate::Expression>,\n    pub overrides_used: &'a mut HandleSet<crate::Override>,\n}\n\nimpl TypeTracer<'_> {\n    pub fn trace_type(&mut self, ty: &crate::Type) {\n        use crate::TypeInner as Ti;\n        match ty.inner {\n            // Types that do not contain handles.\n            Ti::Scalar { .. }\n            | Ti::Vector { .. }\n            | Ti::Matrix { .. }\n            | Ti::CooperativeMatrix { .. }\n            | Ti::Atomic { .. }\n            | Ti::ValuePointer { .. }\n            | Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::RayQuery { .. } => {}\n\n            // Types that do contain handles.\n            Ti::Array {\n                base,\n                size,\n                stride: _,\n            }\n            | Ti::BindingArray { base, size } => {\n                self.types_used.insert(base);\n                match size {\n                    crate::ArraySize::Pending(handle) => {\n                        self.overrides_used.insert(handle);\n                        let r#override = &self.overrides[handle];\n                        self.types_used.insert(r#override.ty);\n                        if let Some(expr) = r#override.init {\n                            self.expressions_used.insert(expr);\n                        }\n                    }\n                    crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => {}\n                }\n            }\n            Ti::Pointer { base, space: _ } => {\n                self.types_used.insert(base);\n            }\n            Ti::Struct {\n                ref members,\n                span: _,\n            } => {\n                self.types_used.insert_iter(members.iter().map(|m| m.ty));\n            }\n        }\n    }\n}\n\nimpl ModuleMap {\n    pub fn adjust_type(&self, ty: &mut crate::Type) {\n        let adjust = |ty: &mut Handle<crate::Type>| self.types.adjust(ty);\n\n        use crate::TypeInner as Ti;\n        match ty.inner {\n            // Types that do not contain handles.\n            Ti::Scalar(_)\n            | Ti::Vector { .. }\n            | Ti::Matrix { .. }\n            | Ti::CooperativeMatrix { .. }\n            | Ti::Atomic(_)\n            | Ti::ValuePointer { .. }\n            | Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::RayQuery { .. } => {}\n\n            // Types that do contain handles.\n            Ti::Pointer {\n                ref mut base,\n                space: _,\n            } => adjust(base),\n            Ti::Array {\n                ref mut base,\n                ref mut size,\n                stride: _,\n            }\n            | Ti::BindingArray {\n                ref mut base,\n                ref mut size,\n            } => {\n                adjust(base);\n                match *size {\n                    crate::ArraySize::Pending(ref mut r#override) => {\n                        self.overrides.adjust(r#override);\n                    }\n                    crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => {}\n                }\n            }\n            Ti::Struct {\n                ref mut members,\n                span: _,\n            } => {\n                for member in members {\n                    self.types.adjust(&mut member.ty);\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "naga/src/diagnostic_filter.rs",
    "content": "//! [`DiagnosticFilter`]s and supporting functionality.\n\nuse alloc::boxed::Box;\n\nuse crate::{Arena, Handle};\n\n#[cfg(feature = \"wgsl-in\")]\nuse crate::FastIndexMap;\n#[cfg(feature = \"wgsl-in\")]\nuse crate::Span;\n#[cfg(feature = \"arbitrary\")]\nuse arbitrary::Arbitrary;\n#[cfg(feature = \"deserialize\")]\nuse serde::Deserialize;\n#[cfg(feature = \"serialize\")]\nuse serde::Serialize;\n\n/// A severity set on a [`DiagnosticFilter`].\n///\n/// <https://www.w3.org/TR/WGSL/#diagnostic-severity>\n#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Severity {\n    Off,\n    Info,\n    Warning,\n    Error,\n}\n\nimpl Severity {\n    /// Checks whether this severity is [`Self::Error`].\n    ///\n    /// Naga does not yet support diagnostic items at lesser severities than\n    /// [`Severity::Error`]. When this is implemented, this method should be deleted, and the\n    /// severity should be used directly for reporting diagnostics.\n    pub(crate) fn report_diag<E>(\n        self,\n        err: E,\n        log_handler: impl FnOnce(E, log::Level),\n    ) -> Result<(), E> {\n        let log_level = match self {\n            Severity::Off => return Ok(()),\n\n            // NOTE: These severities are not yet reported.\n            Severity::Info => log::Level::Info,\n            Severity::Warning => log::Level::Warn,\n\n            Severity::Error => return Err(err),\n        };\n        log_handler(err, log_level);\n        Ok(())\n    }\n}\n\n/// A filterable triggering rule in a [`DiagnosticFilter`].\n///\n/// <https://www.w3.org/TR/WGSL/#filterable-triggering-rules>\n#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum FilterableTriggeringRule {\n    Standard(StandardFilterableTriggeringRule),\n    Unknown(Box<str>),\n    User(Box<[Box<str>; 2]>),\n}\n\n/// A filterable triggering rule in a [`DiagnosticFilter`].\n///\n/// <https://www.w3.org/TR/WGSL/#filterable-triggering-rules>\n#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum StandardFilterableTriggeringRule {\n    DerivativeUniformity,\n}\n\nimpl StandardFilterableTriggeringRule {\n    /// The default severity associated with this triggering rule.\n    ///\n    /// See <https://www.w3.org/TR/WGSL/#filterable-triggering-rules> for a table of default\n    /// severities.\n    pub(crate) const fn default_severity(self) -> Severity {\n        match self {\n            Self::DerivativeUniformity => Severity::Error,\n        }\n    }\n}\n\n/// A filtering rule that modifies how diagnostics are emitted for shaders.\n///\n/// <https://www.w3.org/TR/WGSL/#diagnostic-filter>\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct DiagnosticFilter {\n    pub new_severity: Severity,\n    pub triggering_rule: FilterableTriggeringRule,\n}\n\n/// Determines whether [`DiagnosticFilterMap::add`] should consider full duplicates a conflict.\n///\n/// In WGSL, directive position does not consider this case a conflict, while attribute position\n/// does.\n#[cfg(feature = \"wgsl-in\")]\npub(crate) enum ShouldConflictOnFullDuplicate {\n    /// Use this for attributes in WGSL.\n    Yes,\n    /// Use this for directives in WGSL.\n    No,\n}\n\n/// A map from diagnostic filters to their severity and span.\n///\n/// Front ends can use this to collect the set of filters applied to a\n/// particular language construct, and detect duplicate/conflicting filters.\n///\n/// For example, WGSL has global diagnostic filters that apply to the entire\n/// module, and diagnostic range filter attributes that apply to a specific\n/// function, statement, or other smaller construct. The set of filters applied\n/// to any given construct must not conflict, but they can be overridden by\n/// filters on other constructs nested within it. A front end can use a\n/// `DiagnosticFilterMap` to collect the filters applied to a single construct,\n/// using the [`add`] method's error checking to forbid conflicts.\n///\n/// For each filter it contains, a `DiagnosticFilterMap` records the requested\n/// severity, and the source span of the filter itself.\n///\n/// [`add`]: DiagnosticFilterMap::add\n#[derive(Clone, Debug, Default)]\n#[cfg(feature = \"wgsl-in\")]\npub(crate) struct DiagnosticFilterMap(FastIndexMap<FilterableTriggeringRule, (Severity, Span)>);\n\n#[cfg(feature = \"wgsl-in\")]\nimpl DiagnosticFilterMap {\n    pub(crate) fn new() -> Self {\n        Self::default()\n    }\n\n    /// Add the given `diagnostic_filter` parsed at the given `span` to this map.\n    pub(crate) fn add(\n        &mut self,\n        diagnostic_filter: DiagnosticFilter,\n        span: Span,\n        should_conflict_on_full_duplicate: ShouldConflictOnFullDuplicate,\n    ) -> Result<(), ConflictingDiagnosticRuleError> {\n        use indexmap::map::Entry;\n\n        let &mut Self(ref mut diagnostic_filters) = self;\n        let DiagnosticFilter {\n            new_severity,\n            triggering_rule,\n        } = diagnostic_filter;\n\n        match diagnostic_filters.entry(triggering_rule.clone()) {\n            Entry::Vacant(entry) => {\n                entry.insert((new_severity, span));\n            }\n            Entry::Occupied(entry) => {\n                let &(first_severity, first_span) = entry.get();\n                let should_conflict_on_full_duplicate = match should_conflict_on_full_duplicate {\n                    ShouldConflictOnFullDuplicate::Yes => true,\n                    ShouldConflictOnFullDuplicate::No => false,\n                };\n                if first_severity != new_severity || should_conflict_on_full_duplicate {\n                    return Err(ConflictingDiagnosticRuleError {\n                        triggering_rule_spans: [first_span, span],\n                    });\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Were any rules specified?\n    pub(crate) fn is_empty(&self) -> bool {\n        let &Self(ref map) = self;\n        map.is_empty()\n    }\n\n    /// Returns the spans of all contained rules.\n    pub(crate) fn spans(&self) -> impl Iterator<Item = Span> + '_ {\n        let &Self(ref map) = self;\n        map.iter().map(|(_, &(_, span))| span)\n    }\n}\n\n#[cfg(feature = \"wgsl-in\")]\nimpl IntoIterator for DiagnosticFilterMap {\n    type Item = (FilterableTriggeringRule, (Severity, Span));\n\n    type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        let Self(this) = self;\n        this.into_iter()\n    }\n}\n\n/// An error returned by [`DiagnosticFilterMap::add`] when it encounters conflicting rules.\n#[cfg(feature = \"wgsl-in\")]\n#[derive(Clone, Debug)]\npub(crate) struct ConflictingDiagnosticRuleError {\n    pub triggering_rule_spans: [Span; 2],\n}\n\n/// Represents a single parent-linking node in a tree of [`DiagnosticFilter`]s backed by a\n/// [`crate::Arena`].\n///\n/// A single element of a _tree_ of diagnostic filter rules stored in\n/// [`crate::Module::diagnostic_filters`]. When nodes are built by a front-end, module-applicable\n/// filter rules are chained together in runs based on parse site.  For instance, given the\n/// following:\n///\n/// - Module-applicable rules `a` and `b`.\n/// - Rules `c` and `d`, applicable to an entry point called `c_and_d_func`.\n/// - Rule `e`, applicable to an entry point called `e_func`.\n///\n/// The tree would be represented as follows:\n///\n/// ```text\n/// a <- b\n///      ^\n///      |- c <- d\n///      |\n///      \\- e\n/// ```\n///\n/// ...where:\n///\n/// - `d` is the first leaf consulted by validation in `c_and_d_func`.\n/// - `e` is the first leaf consulted by validation in `e_func`.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct DiagnosticFilterNode {\n    pub inner: DiagnosticFilter,\n    pub parent: Option<Handle<DiagnosticFilterNode>>,\n}\n\nimpl DiagnosticFilterNode {\n    /// Finds the most specific filter rule applicable to `triggering_rule` from the chain of\n    /// diagnostic filter rules in `arena`, starting with `node`, and returns its severity. If none\n    /// is found, return the value of [`StandardFilterableTriggeringRule::default_severity`].\n    ///\n    /// When `triggering_rule` is not applicable to this node, its parent is consulted recursively.\n    pub(crate) fn search(\n        node: Option<Handle<Self>>,\n        arena: &Arena<Self>,\n        triggering_rule: StandardFilterableTriggeringRule,\n    ) -> Severity {\n        let mut next = node;\n        while let Some(handle) = next {\n            let node = &arena[handle];\n            let &Self { ref inner, parent } = node;\n            let &DiagnosticFilter {\n                triggering_rule: ref rule,\n                new_severity,\n            } = inner;\n\n            if rule == &FilterableTriggeringRule::Standard(triggering_rule) {\n                return new_severity;\n            }\n\n            next = parent;\n        }\n        triggering_rule.default_severity()\n    }\n}\n"
  },
  {
    "path": "naga/src/error.rs",
    "content": "use alloc::{borrow::Cow, boxed::Box, string::String};\nuse core::{error::Error, fmt};\n\n#[derive(Clone, Debug)]\npub struct ShaderError<E> {\n    /// The source code of the shader.\n    pub source: String,\n    pub label: Option<String>,\n    pub inner: Box<E>,\n}\n\n#[cfg(feature = \"wgsl-in\")]\nimpl fmt::Display for ShaderError<crate::front::wgsl::ParseError> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let label = self.label.as_deref().unwrap_or_default();\n        let string = self.inner.emit_to_string(&self.source);\n        write!(f, \"\\nShader '{label}' parsing {string}\")\n    }\n}\n\n#[cfg(feature = \"glsl-in\")]\nimpl fmt::Display for ShaderError<crate::front::glsl::ParseErrors> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let label = self.label.as_deref().unwrap_or_default();\n        let string = self.inner.emit_to_string(&self.source);\n        write!(f, \"\\nShader '{label}' parsing {string}\")\n    }\n}\n\n#[cfg(feature = \"spv-in\")]\nimpl fmt::Display for ShaderError<crate::front::spv::Error> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let label = self.label.as_deref().unwrap_or_default();\n        let string = self.inner.emit_to_string(&self.source);\n        write!(f, \"\\nShader '{label}' parsing {string}\")\n    }\n}\n\nimpl fmt::Display for ShaderError<crate::WithSpan<crate::valid::ValidationError>> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        use codespan_reporting::{files::SimpleFile, term};\n\n        let label = self.label.as_deref().unwrap_or_default();\n        let files = SimpleFile::new(label, replace_control_chars(&self.source));\n        let config = term::Config::default();\n\n        let mut writer = DiagnosticBuffer::new();\n        writer\n            .emit_to_self(&config, &files, &self.inner.diagnostic())\n            .expect(\"cannot write error\");\n        let writer = writer.into_string();\n\n        write!(f, \"\\nShader validation {writer}\")\n    }\n}\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"termcolor\")] {\n        type DiagnosticBufferInner = codespan_reporting::term::termcolor::NoColor<alloc::vec::Vec<u8>>;\n    } else if #[cfg(feature = \"stderr\")] {\n        type DiagnosticBufferInner = alloc::vec::Vec<u8>;\n    } else {\n        type DiagnosticBufferInner = String;\n    }\n}\n\ncfg_if::cfg_if! {\n    if #[cfg(all(feature = \"stderr\", feature = \"termcolor\"))] {\n        pub(crate) use codespan_reporting::term::termcolor::WriteColor as _ErrorWrite;\n    } else if #[cfg(feature = \"stderr\")] {\n        pub(crate) use std::io::Write as _ErrorWrite;\n    }\n}\n\n#[cfg(feature = \"stderr\")]\npub(crate) use _ErrorWrite as ErrorWrite;\n\n#[cfg(feature = \"stderr\")]\n#[cfg_attr(\n    not(any(feature = \"spv-in\", feature = \"glsl-in\")),\n    expect(\n        dead_code,\n        reason = \"only need `emit_to_writer` with an appropriate front-end.\"\n    )\n)]\npub(crate) fn emit_to_writer<'files, F: codespan_reporting::files::Files<'files> + ?Sized>(\n    writer: &mut impl ErrorWrite,\n    config: &codespan_reporting::term::Config,\n    files: &'files F,\n    diagnostic: &codespan_reporting::diagnostic::Diagnostic<F::FileId>,\n) -> Result<(), codespan_reporting::files::Error> {\n    cfg_if::cfg_if! {\n        if #[cfg(feature = \"termcolor\")] {\n            codespan_reporting::term::emit_to_write_style(writer, config, files, diagnostic)\n        } else {\n            codespan_reporting::term::emit_to_io_write(writer, config, files, diagnostic)\n        }\n    }\n}\n\npub(crate) struct DiagnosticBuffer {\n    inner: DiagnosticBufferInner,\n}\n\nimpl DiagnosticBuffer {\n    #[cfg_attr(\n        not(feature = \"termcolor\"),\n        expect(\n            clippy::missing_const_for_fn,\n            reason = \"`NoColor::new` isn't `const`, but other `inner`s are.\"\n        )\n    )]\n    pub fn new() -> Self {\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                let inner = codespan_reporting::term::termcolor::NoColor::new(alloc::vec::Vec::new());\n            } else if #[cfg(feature = \"stderr\")] {\n                let inner = alloc::vec::Vec::new();\n            } else {\n                let inner = String::new();\n            }\n        };\n\n        Self { inner }\n    }\n\n    pub fn emit_to_self<'files, F: codespan_reporting::files::Files<'files> + ?Sized>(\n        &mut self,\n        config: &codespan_reporting::term::Config,\n        files: &'files F,\n        diagnostic: &codespan_reporting::diagnostic::Diagnostic<F::FileId>,\n    ) -> Result<(), codespan_reporting::files::Error> {\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                codespan_reporting::term::emit_to_write_style(&mut self.inner, config, files, diagnostic)\n            } else if #[cfg(feature = \"stderr\")] {\n                codespan_reporting::term::emit_to_io_write(&mut self.inner, config, files, diagnostic)\n            } else {\n                codespan_reporting::term::emit_to_string(&mut self.inner, config, files, diagnostic)\n            }\n        }\n    }\n\n    pub fn into_string(self) -> String {\n        let Self { inner } = self;\n\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                String::from_utf8(inner.into_inner()).unwrap()\n            } else if #[cfg(feature = \"stderr\")] {\n                String::from_utf8(inner).unwrap()\n            } else {\n                inner\n            }\n        }\n    }\n}\n\nimpl<E> Error for ShaderError<E>\nwhere\n    ShaderError<E>: fmt::Display,\n    E: Error + 'static,\n{\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        self.inner.source()\n    }\n}\n\npub(crate) fn replace_control_chars(s: &str) -> Cow<'_, str> {\n    const REPLACEMENT_CHAR: &str = \"\\u{FFFD}\";\n    debug_assert_eq!(\n        REPLACEMENT_CHAR.chars().next().unwrap(),\n        char::REPLACEMENT_CHARACTER\n    );\n\n    let mut res = Cow::Borrowed(s);\n    let mut offset = 0;\n\n    while let Some(found_pos) = res[offset..].find(|c: char| c.is_control() && !c.is_whitespace()) {\n        offset += found_pos;\n        let found_len = res[offset..].chars().next().unwrap().len_utf8();\n        res.to_mut()\n            .replace_range(offset..offset + found_len, REPLACEMENT_CHAR);\n        offset += REPLACEMENT_CHAR.len();\n    }\n\n    res\n}\n\n#[test]\nfn test_replace_control_chars() {\n    // The UTF-8 encoding of \\u{0080} is multiple bytes.\n    let input = \"Foo\\u{0080}Bar\\u{0001}Baz\\n\";\n    let expected = \"Foo\\u{FFFD}Bar\\u{FFFD}Baz\\n\";\n    assert_eq!(replace_control_chars(input), expected);\n}\n"
  },
  {
    "path": "naga/src/front/atomic_upgrade.rs",
    "content": "//! Upgrade the types of scalars observed to be accessed as atomics to [`Atomic`] types.\n//!\n//! In SPIR-V, atomic operations can be applied to any scalar value, but in Naga\n//! IR atomic operations can only be applied to values of type [`Atomic`]. Naga\n//! IR's restriction matches Metal Shading Language and WGSL, so we don't want\n//! to relax that. Instead, when the SPIR-V front end observes a value being\n//! accessed using atomic instructions, it promotes the value's type from\n//! [`Scalar`] to [`Atomic`]. This module implements `Module::upgrade_atomics`,\n//! the function that makes that change.\n//!\n//! Atomics can only appear in global variables in the [`Storage`] and\n//! [`Workgroup`] address spaces. These variables can either have `Atomic` types\n//! themselves, or be [`Array`]s of such, or be [`Struct`]s containing such.\n//! So we only need to change the types of globals and struct fields.\n//!\n//! Naga IR [`Load`] expressions and [`Store`] statements can operate directly\n//! on [`Atomic`] values, retrieving and depositing ordinary [`Scalar`] values,\n//! so changing the types doesn't have much effect on the code that operates on\n//! those values.\n//!\n//! Future work:\n//!\n//! - The GLSL front end could use this transformation as well.\n//!\n//! [`Atomic`]: TypeInner::Atomic\n//! [`Scalar`]: TypeInner::Scalar\n//! [`Storage`]: crate::AddressSpace::Storage\n//! [`WorkGroup`]: crate::AddressSpace::WorkGroup\n//! [`Array`]: TypeInner::Array\n//! [`Struct`]: TypeInner::Struct\n//! [`Load`]: crate::Expression::Load\n//! [`Store`]: crate::Statement::Store\n\nuse alloc::{format, sync::Arc};\nuse core::sync::atomic::AtomicUsize;\n\nuse crate::{GlobalVariable, Handle, Module, Type, TypeInner};\n\n#[derive(Clone, Debug, thiserror::Error)]\npub enum Error {\n    #[error(\"encountered an unsupported expression\")]\n    Unsupported,\n    #[error(\"unexpected end of struct field access indices\")]\n    UnexpectedEndOfIndices,\n    #[error(\"encountered unsupported global initializer in an atomic variable\")]\n    GlobalInitUnsupported,\n    #[error(\"expected to find a global variable\")]\n    GlobalVariableMissing,\n    #[error(\"atomic compare exchange requires a scalar base type\")]\n    CompareExchangeNonScalarBaseType,\n}\n\n#[derive(Clone, Default)]\nstruct Padding(Arc<AtomicUsize>);\n\nimpl core::fmt::Display for Padding {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        for _ in 0..self.0.load(core::sync::atomic::Ordering::Relaxed) {\n            f.write_str(\"  \")?;\n        }\n        Ok(())\n    }\n}\n\nimpl Drop for Padding {\n    fn drop(&mut self) {\n        let _ = self.0.fetch_sub(1, core::sync::atomic::Ordering::Relaxed);\n    }\n}\n\nimpl Padding {\n    fn trace(&self, msg: impl core::fmt::Display, t: impl core::fmt::Debug) {\n        format!(\"{msg} {t:#?}\")\n            .split('\\n')\n            .for_each(|ln| log::trace!(\"{self}{ln}\"));\n    }\n\n    fn debug(&self, msg: impl core::fmt::Display, t: impl core::fmt::Debug) {\n        format!(\"{msg} {t:#?}\")\n            .split('\\n')\n            .for_each(|ln| log::debug!(\"{self}{ln}\"));\n    }\n\n    fn inc_padding(&self) -> Padding {\n        let _ = self.0.fetch_add(1, core::sync::atomic::Ordering::Relaxed);\n        self.clone()\n    }\n}\n\n#[derive(Debug, Default)]\npub struct Upgrades {\n    /// Global variables that we've accessed using atomic operations.\n    ///\n    /// This includes globals with composite types (arrays, structs) where we've\n    /// only accessed some components (elements, fields) atomically.\n    globals: crate::arena::HandleSet<GlobalVariable>,\n\n    /// Struct fields that we've accessed using atomic operations.\n    ///\n    /// Each key refers to some [`Struct`] type, and each value is a set of\n    /// the indices of the fields in that struct that have been accessed\n    /// atomically.\n    ///\n    /// This includes fields with composite types (arrays, structs)\n    /// of which we've only accessed some components (elements, fields)\n    /// atomically.\n    ///\n    /// [`Struct`]: crate::TypeInner::Struct\n    fields: crate::FastHashMap<Handle<Type>, bit_set::BitSet>,\n}\n\nimpl Upgrades {\n    pub fn insert_global(&mut self, global: Handle<GlobalVariable>) {\n        self.globals.insert(global);\n    }\n\n    pub fn insert_field(&mut self, struct_type: Handle<Type>, field: usize) {\n        self.fields.entry(struct_type).or_default().insert(field);\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.globals.is_empty()\n    }\n}\n\nstruct UpgradeState<'a> {\n    padding: Padding,\n    module: &'a mut Module,\n\n    /// A map from old types to their upgraded versions.\n    ///\n    /// This ensures we never try to rebuild a type more than once.\n    upgraded_types: crate::FastHashMap<Handle<Type>, Handle<Type>>,\n}\n\nimpl UpgradeState<'_> {\n    fn inc_padding(&self) -> Padding {\n        self.padding.inc_padding()\n    }\n\n    /// Get a type equivalent to `ty`, but with [`Scalar`] leaves upgraded to [`Atomic`] scalars.\n    ///\n    /// If such a type already exists in `self.module.types`, return its handle.\n    /// Otherwise, construct a new one and return that handle.\n    ///\n    /// If `ty` is a [`Pointer`], [`Array`], [`BindingArray`], recurse into the\n    /// type and upgrade its leaf types.\n    ///\n    /// If `ty` is a [`Struct`], recurse into it and upgrade only those fields\n    /// whose indices appear in `field_indices`.\n    ///\n    /// The existing type is not affected.\n    ///\n    /// [`Scalar`]: crate::TypeInner::Scalar\n    /// [`Atomic`]: crate::TypeInner::Atomic\n    /// [`Pointer`]: crate::TypeInner::Pointer\n    /// [`Array`]: crate::TypeInner::Array\n    /// [`Struct`]: crate::TypeInner::Struct\n    /// [`BindingArray`]: crate::TypeInner::BindingArray\n    fn upgrade_type(\n        &mut self,\n        ty: Handle<Type>,\n        upgrades: &Upgrades,\n    ) -> Result<Handle<Type>, Error> {\n        let padding = self.inc_padding();\n        padding.trace(\"visiting type: \", ty);\n\n        // If we've already upgraded this type, return the handle we produced at\n        // the time.\n        if let Some(&new) = self.upgraded_types.get(&ty) {\n            return Ok(new);\n        }\n\n        let inner = match self.module.types[ty].inner {\n            TypeInner::Scalar(scalar) => {\n                log::trace!(\"{padding}hit the scalar leaf, replacing with an atomic\");\n                TypeInner::Atomic(scalar)\n            }\n            TypeInner::Pointer { base, space } => TypeInner::Pointer {\n                base: self.upgrade_type(base, upgrades)?,\n                space,\n            },\n            TypeInner::Array { base, size, stride } => TypeInner::Array {\n                base: self.upgrade_type(base, upgrades)?,\n                size,\n                stride,\n            },\n            TypeInner::Struct { ref members, span } => {\n                // If no field or subfield of this struct was ever accessed\n                // atomically, no change is needed. We should never have arrived here.\n                let Some(fields) = upgrades.fields.get(&ty) else {\n                    unreachable!(\"global or field incorrectly flagged as atomically accessed\");\n                };\n\n                let mut new_members = members.clone();\n                for field in fields {\n                    new_members[field].ty = self.upgrade_type(new_members[field].ty, upgrades)?;\n                }\n\n                TypeInner::Struct {\n                    members: new_members,\n                    span,\n                }\n            }\n            TypeInner::BindingArray { base, size } => TypeInner::BindingArray {\n                base: self.upgrade_type(base, upgrades)?,\n                size,\n            },\n            _ => return Ok(ty),\n        };\n\n        // At this point, we have a `TypeInner` that is the upgraded version of\n        // `ty`. Find a suitable `Type` for this, creating a new one if\n        // necessary, and return its handle.\n        let r#type = &self.module.types[ty];\n        let span = self.module.types.get_span(ty);\n        let new_type = Type {\n            name: r#type.name.clone(),\n            inner,\n        };\n        padding.debug(\"ty: \", ty);\n        padding.debug(\"from: \", r#type);\n        padding.debug(\"to:   \", &new_type);\n        let new_handle = self.module.types.insert(new_type, span);\n        self.upgraded_types.insert(ty, new_handle);\n        Ok(new_handle)\n    }\n\n    fn upgrade_all(&mut self, upgrades: &Upgrades) -> Result<(), Error> {\n        for handle in upgrades.globals.iter() {\n            let padding = self.inc_padding();\n\n            let global = &self.module.global_variables[handle];\n            padding.trace(\"visiting global variable: \", handle);\n            padding.trace(\"var: \", global);\n\n            if global.init.is_some() {\n                return Err(Error::GlobalInitUnsupported);\n            }\n\n            let var_ty = global.ty;\n            let new_ty = self.upgrade_type(var_ty, upgrades)?;\n            if new_ty != var_ty {\n                padding.debug(\"upgrading global variable: \", handle);\n                padding.debug(\"from ty: \", var_ty);\n                padding.debug(\"to ty:   \", new_ty);\n                self.module.global_variables[handle].ty = new_ty;\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl Module {\n    /// Upgrade `global_var_handles` to have [`Atomic`] leaf types.\n    ///\n    /// [`Atomic`]: TypeInner::Atomic\n    pub(crate) fn upgrade_atomics(&mut self, upgrades: &Upgrades) -> Result<(), Error> {\n        let mut state = UpgradeState {\n            padding: Default::default(),\n            module: self,\n            upgraded_types: crate::FastHashMap::with_capacity_and_hasher(\n                upgrades.fields.len(),\n                Default::default(),\n            ),\n        };\n\n        state.upgrade_all(upgrades)?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/ast.rs",
    "content": "use alloc::{borrow::Cow, string::String, vec::Vec};\nuse core::fmt;\n\nuse super::{builtins::MacroCall, Span};\nuse crate::{\n    AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,\n    Interpolation, Literal, Override, Sampling, StorageAccess, Type, UnaryOperator,\n};\n\n#[derive(Debug, Clone, Copy)]\npub enum GlobalLookupKind {\n    Variable(Handle<GlobalVariable>),\n    Constant(Handle<Constant>, Handle<Type>),\n    Override(Handle<Override>, Handle<Type>),\n    BlockSelect(Handle<GlobalVariable>, u32),\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct GlobalLookup {\n    pub kind: GlobalLookupKind,\n    pub entry_arg: Option<usize>,\n    pub mutable: bool,\n}\n\n#[derive(Debug, Clone)]\npub struct ParameterInfo {\n    pub qualifier: ParameterQualifier,\n    /// Whether the parameter should be treated as a depth image instead of a\n    /// sampled image.\n    pub depth: bool,\n}\n\n/// How the function is implemented\n#[derive(Clone, Copy)]\npub enum FunctionKind {\n    /// The function is user defined\n    Call(Handle<Function>),\n    /// The function is a builtin\n    Macro(MacroCall),\n}\n\nimpl fmt::Debug for FunctionKind {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            Self::Call(_) => write!(f, \"Call\"),\n            Self::Macro(_) => write!(f, \"Macro\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct Overload {\n    /// Normalized function parameters, modifiers are not applied\n    pub parameters: Vec<Handle<Type>>,\n    pub parameters_info: Vec<ParameterInfo>,\n    /// How the function is implemented\n    pub kind: FunctionKind,\n    /// Whether this function was already defined or is just a prototype\n    pub defined: bool,\n    /// Whether this overload is the one provided by the language or has\n    /// been redeclared by the user (builtins only)\n    pub internal: bool,\n    /// Whether or not this function returns void (nothing)\n    pub void: bool,\n}\n\nbitflags::bitflags! {\n    /// Tracks the variations of the builtin already generated, this is needed because some\n    /// builtins overloads can't be generated unless explicitly used, since they might cause\n    /// unneeded capabilities to be requested\n    #[derive(Default)]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct BuiltinVariations: u32 {\n        /// Request the standard overloads\n        const STANDARD = 1 << 0;\n        /// Request overloads that use the double type\n        const DOUBLE = 1 << 1;\n        /// Request overloads that use `samplerCubeArray(Shadow)`\n        const CUBE_TEXTURES_ARRAY = 1 << 2;\n        /// Request overloads that use `sampler2DMSArray`\n        const D2_MULTI_TEXTURES_ARRAY = 1 << 3;\n    }\n}\n\n#[derive(Debug, Default)]\npub struct FunctionDeclaration {\n    pub overloads: Vec<Overload>,\n    /// Tracks the builtin overload variations that were already generated\n    pub variations: BuiltinVariations,\n}\n\n#[derive(Debug)]\npub struct EntryArg {\n    pub name: Option<String>,\n    pub binding: Binding,\n    pub handle: Handle<GlobalVariable>,\n    pub storage: StorageQualifier,\n}\n\n#[derive(Debug, Clone)]\npub struct VariableReference {\n    pub expr: Handle<Expression>,\n    /// Whether the variable is of a pointer type (and needs loading) or not\n    pub load: bool,\n    /// Whether the value of the variable can be changed or not\n    pub mutable: bool,\n    pub constant: Option<(Handle<Constant>, Handle<Type>)>,\n    pub entry_arg: Option<usize>,\n}\n\n#[derive(Debug, Clone)]\npub struct HirExpr {\n    pub kind: HirExprKind,\n    pub meta: Span,\n}\n\n#[derive(Debug, Clone)]\npub enum HirExprKind {\n    /// Represents a sequence of expressions. It returns the type and value of the last (i.e. right-most) expression.\n    Sequence {\n        exprs: Vec<Handle<HirExpr>>,\n    },\n    Access {\n        base: Handle<HirExpr>,\n        index: Handle<HirExpr>,\n    },\n    Select {\n        base: Handle<HirExpr>,\n        field: String,\n    },\n    Literal(Literal),\n    Binary {\n        left: Handle<HirExpr>,\n        op: BinaryOperator,\n        right: Handle<HirExpr>,\n    },\n    Unary {\n        op: UnaryOperator,\n        expr: Handle<HirExpr>,\n    },\n    Variable(VariableReference),\n    Call(FunctionCall),\n    /// Represents the ternary operator in glsl (`:?`)\n    Conditional {\n        /// The expression that will decide which branch to take, must evaluate to a boolean\n        condition: Handle<HirExpr>,\n        /// The expression that will be evaluated if [`condition`] returns `true`\n        ///\n        /// [`condition`]: Self::Conditional::condition\n        accept: Handle<HirExpr>,\n        /// The expression that will be evaluated if [`condition`] returns `false`\n        ///\n        /// [`condition`]: Self::Conditional::condition\n        reject: Handle<HirExpr>,\n    },\n    Assign {\n        tgt: Handle<HirExpr>,\n        value: Handle<HirExpr>,\n    },\n    /// A prefix/postfix operator like `++`\n    PrePostfix {\n        /// The operation to be performed\n        op: BinaryOperator,\n        /// Whether this is a postfix or a prefix\n        postfix: bool,\n        /// The target expression\n        expr: Handle<HirExpr>,\n    },\n    /// A method call like `what.something(a, b, c)`\n    Method {\n        /// expression the method call applies to (`what` in the example)\n        expr: Handle<HirExpr>,\n        /// the method name (`something` in the example)\n        name: String,\n        /// the arguments to the method (`a`, `b`, and `c` in the example)\n        args: Vec<Handle<HirExpr>>,\n    },\n}\n\n#[derive(Debug, Hash, PartialEq, Eq)]\npub enum QualifierKey<'a> {\n    String(Cow<'a, str>),\n    /// Used for `std140` and `std430` layout qualifiers\n    Layout,\n    /// Used for image formats\n    Format,\n    /// Used for `index` layout qualifiers\n    Index,\n}\n\n#[derive(Debug)]\npub enum QualifierValue {\n    None,\n    Uint(u32),\n    Layout(StructLayout),\n    Format(crate::StorageFormat),\n}\n\n#[derive(Debug, Default)]\npub struct TypeQualifiers<'a> {\n    pub span: Span,\n    pub storage: (StorageQualifier, Span),\n    pub invariant: Option<Span>,\n    pub interpolation: Option<(Interpolation, Span)>,\n    pub precision: Option<(Precision, Span)>,\n    pub sampling: Option<(Sampling, Span)>,\n    /// Memory qualifiers used in the declaration to set the storage access to be used\n    /// in declarations that support it (storage images and buffers)\n    pub storage_access: Option<(StorageAccess, Span)>,\n    pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,\n}\n\nimpl<'a> TypeQualifiers<'a> {\n    /// Appends `errors` with errors for all unused qualifiers\n    pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {\n        if let Some(meta) = self.invariant {\n            errors.push(super::Error {\n                kind: super::ErrorKind::SemanticError(\n                    \"Invariant qualifier can only be used in in/out variables\".into(),\n                ),\n                meta,\n            });\n        }\n\n        if let Some((_, meta)) = self.interpolation {\n            errors.push(super::Error {\n                kind: super::ErrorKind::SemanticError(\n                    \"Interpolation qualifiers can only be used in in/out variables\".into(),\n                ),\n                meta,\n            });\n        }\n\n        if let Some((_, meta)) = self.sampling {\n            errors.push(super::Error {\n                kind: super::ErrorKind::SemanticError(\n                    \"Sampling qualifiers can only be used in in/out variables\".into(),\n                ),\n                meta,\n            });\n        }\n\n        if let Some((_, meta)) = self.storage_access {\n            errors.push(super::Error {\n                kind: super::ErrorKind::SemanticError(\n                    \"Memory qualifiers can only be used in storage variables\".into(),\n                ),\n                meta,\n            });\n        }\n\n        for &(_, meta) in self.layout_qualifiers.values() {\n            errors.push(super::Error {\n                kind: super::ErrorKind::SemanticError(\"Unexpected qualifier\".into()),\n                meta,\n            });\n        }\n    }\n\n    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't\n    /// a [`QualifierValue::Uint`]\n    pub fn uint_layout_qualifier(\n        &mut self,\n        name: &'a str,\n        errors: &mut Vec<super::Error>,\n    ) -> Option<u32> {\n        match self\n            .layout_qualifiers\n            .remove(&QualifierKey::String(name.into()))\n        {\n            Some((QualifierValue::Uint(v), _)) => Some(v),\n            Some((_, meta)) => {\n                errors.push(super::Error {\n                    kind: super::ErrorKind::SemanticError(\"Qualifier expects a uint value\".into()),\n                    meta,\n                });\n                // Return a dummy value instead of `None` to differentiate from\n                // the qualifier not existing, since some parts might require the\n                // qualifier to exist and throwing another error that it doesn't\n                // exist would be unhelpful\n                Some(0)\n            }\n            _ => None,\n        }\n    }\n\n    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't\n    /// a [`QualifierValue::None`]\n    pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {\n        match self\n            .layout_qualifiers\n            .remove(&QualifierKey::String(name.into()))\n        {\n            Some((QualifierValue::None, _)) => true,\n            Some((_, meta)) => {\n                errors.push(super::Error {\n                    kind: super::ErrorKind::SemanticError(\n                        \"Qualifier doesn't expect a value\".into(),\n                    ),\n                    meta,\n                });\n                // Return a `true` to since the qualifier is defined and adding\n                // another error for it not being defined would be unhelpful\n                true\n            }\n            _ => false,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub enum FunctionCallKind {\n    TypeConstructor(Handle<Type>),\n    Function(String),\n}\n\n#[derive(Debug, Clone)]\npub struct FunctionCall {\n    pub kind: FunctionCallKind,\n    pub args: Vec<Handle<HirExpr>>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum StorageQualifier {\n    AddressSpace(AddressSpace),\n    Input,\n    Output,\n    Const,\n}\n\nimpl Default for StorageQualifier {\n    fn default() -> Self {\n        StorageQualifier::AddressSpace(AddressSpace::Function)\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum StructLayout {\n    Std140,\n    Std430,\n}\n\n// TODO: Encode precision hints in the IR\n/// A precision hint used in GLSL declarations.\n///\n/// Precision hints can be used to either speed up shader execution or control\n/// the precision of arithmetic operations.\n///\n/// To use a precision hint simply add it before the type in the declaration.\n/// ```glsl\n/// mediump float a;\n/// ```\n///\n/// The default when no precision is declared is `highp` which means that all\n/// operations operate with the type defined width.\n///\n/// For `mediump` and `lowp` operations follow the spir-v\n/// [`RelaxedPrecision`][RelaxedPrecision] decoration semantics.\n///\n/// [RelaxedPrecision]: https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#_a_id_relaxedprecisionsection_a_relaxed_precision\n#[derive(Debug, Clone, PartialEq, Copy)]\npub enum Precision {\n    /// `lowp` precision\n    Low,\n    /// `mediump` precision\n    Medium,\n    /// `highp` precision\n    High,\n}\n\n#[derive(Debug, Clone, PartialEq, Copy)]\npub enum ParameterQualifier {\n    In,\n    Out,\n    InOut,\n    Const,\n}\n\nimpl ParameterQualifier {\n    /// Returns true if the argument should be passed as a lhs expression\n    pub const fn is_lhs(&self) -> bool {\n        match *self {\n            ParameterQualifier::Out | ParameterQualifier::InOut => true,\n            _ => false,\n        }\n    }\n}\n\n/// The GLSL profile used by a shader.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum Profile {\n    /// The `core` profile, default when no profile is specified.\n    Core,\n}\n"
  },
  {
    "path": "naga/src/front/glsl/builtins.rs",
    "content": "use alloc::{vec, vec::Vec};\n\nuse super::{\n    ast::{\n        BuiltinVariations, FunctionDeclaration, FunctionKind, Overload, ParameterInfo,\n        ParameterQualifier,\n    },\n    context::Context,\n    Error, ErrorKind, Frontend, Result,\n};\nuse crate::{\n    BinaryOperator, DerivativeAxis as Axis, DerivativeControl as Ctrl, Expression, Handle,\n    ImageClass, ImageDimension as Dim, ImageQuery, MathFunction, Module, RelationalFunction,\n    SampleLevel, Scalar, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize,\n};\n\nimpl crate::ScalarKind {\n    const fn dummy_storage_format(&self) -> crate::StorageFormat {\n        match *self {\n            Sk::Sint => crate::StorageFormat::R16Sint,\n            Sk::Uint => crate::StorageFormat::R16Uint,\n            _ => crate::StorageFormat::R16Float,\n        }\n    }\n}\n\nimpl Module {\n    /// Helper function, to create a function prototype for a builtin\n    fn add_builtin(&mut self, args: Vec<TypeInner>, builtin: MacroCall) -> Overload {\n        let mut parameters = Vec::with_capacity(args.len());\n        let mut parameters_info = Vec::with_capacity(args.len());\n\n        for arg in args {\n            parameters.push(self.types.insert(\n                Type {\n                    name: None,\n                    inner: arg,\n                },\n                Span::default(),\n            ));\n            parameters_info.push(ParameterInfo {\n                qualifier: ParameterQualifier::In,\n                depth: false,\n            });\n        }\n\n        Overload {\n            parameters,\n            parameters_info,\n            kind: FunctionKind::Macro(builtin),\n            defined: false,\n            internal: true,\n            void: false,\n        }\n    }\n}\n\nconst fn make_coords_arg(number_of_components: usize, kind: Sk) -> TypeInner {\n    let scalar = Scalar { kind, width: 4 };\n\n    match number_of_components {\n        1 => TypeInner::Scalar(scalar),\n        _ => TypeInner::Vector {\n            size: match number_of_components {\n                2 => VectorSize::Bi,\n                3 => VectorSize::Tri,\n                _ => VectorSize::Quad,\n            },\n            scalar,\n        },\n    }\n}\n\n/// Inject builtins into the declaration\n///\n/// This is done to not add a large startup cost and not increase memory\n/// usage if it isn't needed.\npub fn inject_builtin(\n    declaration: &mut FunctionDeclaration,\n    module: &mut Module,\n    name: &str,\n    mut variations: BuiltinVariations,\n) {\n    log::trace!(\n        \"{} variations: {:?} {:?}\",\n        name,\n        variations,\n        declaration.variations\n    );\n    // Don't regeneate variations\n    variations.remove(declaration.variations);\n    declaration.variations |= variations;\n\n    if variations.contains(BuiltinVariations::STANDARD) {\n        inject_standard_builtins(declaration, module, name)\n    }\n\n    if variations.contains(BuiltinVariations::DOUBLE) {\n        inject_double_builtin(declaration, module, name)\n    }\n\n    match name {\n        \"texture\"\n        | \"textureGrad\"\n        | \"textureGradOffset\"\n        | \"textureLod\"\n        | \"textureLodOffset\"\n        | \"textureOffset\"\n        | \"textureProj\"\n        | \"textureProjGrad\"\n        | \"textureProjGradOffset\"\n        | \"textureProjLod\"\n        | \"textureProjLodOffset\"\n        | \"textureProjOffset\" => {\n            let f = |kind, dim, arrayed, multi, shadow| {\n                for bits in 0..=0b11 {\n                    let variant = bits & 0b1 != 0;\n                    let bias = bits & 0b10 != 0;\n\n                    let (proj, offset, level_type) = match name {\n                        // texture(gsampler, gvec P, [float bias]);\n                        \"texture\" => (false, false, TextureLevelType::None),\n                        // textureGrad(gsampler, gvec P, gvec dPdx, gvec dPdy);\n                        \"textureGrad\" => (false, false, TextureLevelType::Grad),\n                        // textureGradOffset(gsampler, gvec P, gvec dPdx, gvec dPdy, ivec offset);\n                        \"textureGradOffset\" => (false, true, TextureLevelType::Grad),\n                        // textureLod(gsampler, gvec P, float lod);\n                        \"textureLod\" => (false, false, TextureLevelType::Lod),\n                        // textureLodOffset(gsampler, gvec P, float lod, ivec offset);\n                        \"textureLodOffset\" => (false, true, TextureLevelType::Lod),\n                        // textureOffset(gsampler, gvec+1 P, ivec offset, [float bias]);\n                        \"textureOffset\" => (false, true, TextureLevelType::None),\n                        // textureProj(gsampler, gvec+1 P, [float bias]);\n                        \"textureProj\" => (true, false, TextureLevelType::None),\n                        // textureProjGrad(gsampler, gvec+1 P, gvec dPdx, gvec dPdy);\n                        \"textureProjGrad\" => (true, false, TextureLevelType::Grad),\n                        // textureProjGradOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);\n                        \"textureProjGradOffset\" => (true, true, TextureLevelType::Grad),\n                        // textureProjLod(gsampler, gvec+1 P, float lod);\n                        \"textureProjLod\" => (true, false, TextureLevelType::Lod),\n                        // textureProjLodOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);\n                        \"textureProjLodOffset\" => (true, true, TextureLevelType::Lod),\n                        // textureProjOffset(gsampler, gvec+1 P, ivec offset, [float bias]);\n                        \"textureProjOffset\" => (true, true, TextureLevelType::None),\n                        _ => unreachable!(),\n                    };\n\n                    let builtin = MacroCall::Texture {\n                        proj,\n                        offset,\n                        shadow,\n                        level_type,\n                    };\n\n                    // Parse out the variant settings.\n                    let grad = level_type == TextureLevelType::Grad;\n                    let lod = level_type == TextureLevelType::Lod;\n\n                    let supports_variant = proj && !shadow;\n                    if variant && !supports_variant {\n                        continue;\n                    }\n\n                    if bias && !matches!(level_type, TextureLevelType::None) {\n                        continue;\n                    }\n\n                    // Proj doesn't work with arrayed or Cube\n                    if proj && (arrayed || dim == Dim::Cube) {\n                        continue;\n                    }\n\n                    // texture operations with offset are not supported for cube maps\n                    if dim == Dim::Cube && offset {\n                        continue;\n                    }\n\n                    // sampler2DArrayShadow can't be used in textureLod or in texture with bias\n                    if (lod || bias) && arrayed && shadow && dim == Dim::D2 {\n                        continue;\n                    }\n\n                    // TODO: glsl supports using bias with depth samplers but naga doesn't\n                    if bias && shadow {\n                        continue;\n                    }\n\n                    let class = match shadow {\n                        true => ImageClass::Depth { multi },\n                        false => ImageClass::Sampled { kind, multi },\n                    };\n\n                    let image = TypeInner::Image {\n                        dim,\n                        arrayed,\n                        class,\n                    };\n\n                    let num_coords_from_dim = image_dims_to_coords_size(dim).min(3);\n                    let mut num_coords = num_coords_from_dim;\n\n                    if shadow && proj {\n                        num_coords = 4;\n                    } else if dim == Dim::D1 && shadow {\n                        num_coords = 3;\n                    } else if shadow {\n                        num_coords += 1;\n                    } else if proj {\n                        if variant && num_coords == 4 {\n                            // Normal form already has 4 components, no need to have a variant form.\n                            continue;\n                        } else if variant {\n                            num_coords = 4;\n                        } else {\n                            num_coords += 1;\n                        }\n                    }\n\n                    if !(dim == Dim::D1 && shadow) {\n                        num_coords += arrayed as usize;\n                    }\n\n                    // Special case: texture(gsamplerCubeArrayShadow) kicks the shadow compare ref to a separate argument,\n                    // since it would otherwise take five arguments. It also can't take a bias, nor can it be proj/grad/lod/offset\n                    // (presumably because nobody asked for it, and implementation complexity?)\n                    if num_coords >= 5 {\n                        if lod || grad || offset || proj || bias {\n                            continue;\n                        }\n                        debug_assert!(dim == Dim::Cube && shadow && arrayed);\n                    }\n                    debug_assert!(num_coords <= 5);\n\n                    let vector = make_coords_arg(num_coords, Sk::Float);\n                    let mut args = vec![image, vector];\n\n                    if num_coords == 5 {\n                        args.push(TypeInner::Scalar(Scalar::F32));\n                    }\n\n                    match level_type {\n                        TextureLevelType::Lod => {\n                            args.push(TypeInner::Scalar(Scalar::F32));\n                        }\n                        TextureLevelType::Grad => {\n                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));\n                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));\n                        }\n                        TextureLevelType::None => {}\n                    };\n\n                    if offset {\n                        args.push(make_coords_arg(num_coords_from_dim, Sk::Sint));\n                    }\n\n                    if bias {\n                        args.push(TypeInner::Scalar(Scalar::F32));\n                    }\n\n                    declaration\n                        .overloads\n                        .push(module.add_builtin(args, builtin));\n                }\n            };\n\n            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)\n        }\n        \"textureSize\" => {\n            let f = |kind, dim, arrayed, multi, shadow| {\n                let class = match shadow {\n                    true => ImageClass::Depth { multi },\n                    false => ImageClass::Sampled { kind, multi },\n                };\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                };\n\n                let mut args = vec![image];\n\n                if !multi {\n                    args.push(TypeInner::Scalar(Scalar::I32))\n                }\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::TextureSize { arrayed }))\n            };\n\n            texture_args_generator(\n                TextureArgsOptions::SHADOW | TextureArgsOptions::MULTI | variations.into(),\n                f,\n            )\n        }\n        \"textureQueryLevels\" => {\n            let f = |kind, dim, arrayed, multi, shadow| {\n                let class = match shadow {\n                    true => ImageClass::Depth { multi },\n                    false => ImageClass::Sampled { kind, multi },\n                };\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                };\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(vec![image], MacroCall::TextureQueryLevels))\n            };\n\n            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)\n        }\n        \"texelFetch\" | \"texelFetchOffset\" => {\n            let offset = \"texelFetchOffset\" == name;\n            let f = |kind, dim, arrayed, multi, _shadow| {\n                // Cube images aren't supported\n                if let Dim::Cube = dim {\n                    return;\n                }\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class: ImageClass::Sampled { kind, multi },\n                };\n\n                let dim_value = image_dims_to_coords_size(dim);\n                let coordinates = make_coords_arg(dim_value + arrayed as usize, Sk::Sint);\n\n                let mut args = vec![image, coordinates, TypeInner::Scalar(Scalar::I32)];\n\n                if offset {\n                    args.push(make_coords_arg(dim_value, Sk::Sint));\n                }\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi }))\n            };\n\n            // Don't generate shadow images since they aren't supported\n            texture_args_generator(TextureArgsOptions::MULTI | variations.into(), f)\n        }\n        \"imageSize\" => {\n            let f = |kind: Sk, dim, arrayed, _, _| {\n                // Naga doesn't support cube images and it's usefulness\n                // is questionable, so they won't be supported for now\n                if dim == Dim::Cube {\n                    return;\n                }\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class: ImageClass::Storage {\n                        format: kind.dummy_storage_format(),\n                        access: crate::StorageAccess::empty(),\n                    },\n                };\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(vec![image], MacroCall::TextureSize { arrayed }))\n            };\n\n            texture_args_generator(variations.into(), f)\n        }\n        \"imageLoad\" => {\n            let f = |kind: Sk, dim, arrayed, _, _| {\n                // Naga doesn't support cube images and it's usefulness\n                // is questionable, so they won't be supported for now\n                if dim == Dim::Cube {\n                    return;\n                }\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class: ImageClass::Storage {\n                        format: kind.dummy_storage_format(),\n                        access: crate::StorageAccess::LOAD,\n                    },\n                };\n\n                let dim_value = image_dims_to_coords_size(dim);\n                let mut coord_size = dim_value + arrayed as usize;\n                // > Every OpenGL API call that operates on cubemap array\n                // > textures takes layer-faces, not array layers\n                //\n                // So this means that imageCubeArray only takes a three component\n                // vector coordinate and the third component is a layer index.\n                if Dim::Cube == dim && arrayed {\n                    coord_size = 3\n                }\n                let coordinates = make_coords_arg(coord_size, Sk::Sint);\n\n                let args = vec![image, coordinates];\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi: false }))\n            };\n\n            // Don't generate shadow nor multisampled images since they aren't supported\n            texture_args_generator(variations.into(), f)\n        }\n        \"imageStore\" => {\n            let f = |kind: Sk, dim, arrayed, _, _| {\n                // Naga doesn't support cube images and it's usefulness\n                // is questionable, so they won't be supported for now\n                if dim == Dim::Cube {\n                    return;\n                }\n\n                let image = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class: ImageClass::Storage {\n                        format: kind.dummy_storage_format(),\n                        access: crate::StorageAccess::STORE,\n                    },\n                };\n\n                let dim_value = image_dims_to_coords_size(dim);\n                let mut coord_size = dim_value + arrayed as usize;\n                // > Every OpenGL API call that operates on cubemap array\n                // > textures takes layer-faces, not array layers\n                //\n                // So this means that imageCubeArray only takes a three component\n                // vector coordinate and the third component is a layer index.\n                if Dim::Cube == dim && arrayed {\n                    coord_size = 3\n                }\n                let coordinates = make_coords_arg(coord_size, Sk::Sint);\n\n                let args = vec![\n                    image,\n                    coordinates,\n                    TypeInner::Vector {\n                        size: VectorSize::Quad,\n                        scalar: Scalar { kind, width: 4 },\n                    },\n                ];\n\n                let mut overload = module.add_builtin(args, MacroCall::ImageStore);\n                overload.void = true;\n                declaration.overloads.push(overload)\n            };\n\n            // Don't generate shadow nor multisampled images since they aren't supported\n            texture_args_generator(variations.into(), f)\n        }\n        _ => {}\n    }\n}\n\n/// Injects the builtins into declaration that don't need any special variations\nfn inject_standard_builtins(\n    declaration: &mut FunctionDeclaration,\n    module: &mut Module,\n    name: &str,\n) {\n    // Some samplers (sampler1D, etc...) can be float, int, or uint\n    let anykind_sampler = if name.starts_with(\"sampler\") {\n        Some((name, Sk::Float))\n    } else if name.starts_with(\"usampler\") {\n        Some((&name[1..], Sk::Uint))\n    } else if name.starts_with(\"isampler\") {\n        Some((&name[1..], Sk::Sint))\n    } else {\n        None\n    };\n    if let Some((sampler, kind)) = anykind_sampler {\n        match sampler {\n            \"sampler1D\" | \"sampler1DArray\" | \"sampler2D\" | \"sampler2DArray\" | \"sampler2DMS\"\n            | \"sampler2DMSArray\" | \"sampler3D\" | \"samplerCube\" | \"samplerCubeArray\" => {\n                declaration.overloads.push(module.add_builtin(\n                    vec![\n                        TypeInner::Image {\n                            dim: match sampler {\n                                \"sampler1D\" | \"sampler1DArray\" => Dim::D1,\n                                \"sampler2D\" | \"sampler2DArray\" | \"sampler2DMS\"\n                                | \"sampler2DMSArray\" => Dim::D2,\n                                \"sampler3D\" => Dim::D3,\n                                _ => Dim::Cube,\n                            },\n                            arrayed: matches!(\n                                sampler,\n                                \"sampler1DArray\"\n                                    | \"sampler2DArray\"\n                                    | \"sampler2DMSArray\"\n                                    | \"samplerCubeArray\"\n                            ),\n                            class: ImageClass::Sampled {\n                                kind,\n                                multi: matches!(sampler, \"sampler2DMS\" | \"sampler2DMSArray\"),\n                            },\n                        },\n                        TypeInner::Sampler { comparison: false },\n                    ],\n                    MacroCall::Sampler,\n                ));\n                return;\n            }\n            _ => (),\n        }\n    }\n\n    match name {\n        // Shadow sampler can only be of kind `Sk::Float`\n        \"sampler1DShadow\"\n        | \"sampler1DArrayShadow\"\n        | \"sampler2DShadow\"\n        | \"sampler2DArrayShadow\"\n        | \"samplerCubeShadow\"\n        | \"samplerCubeArrayShadow\" => {\n            let dim = match name {\n                \"sampler1DShadow\" | \"sampler1DArrayShadow\" => Dim::D1,\n                \"sampler2DShadow\" | \"sampler2DArrayShadow\" => Dim::D2,\n                _ => Dim::Cube,\n            };\n            let arrayed = matches!(\n                name,\n                \"sampler1DArrayShadow\" | \"sampler2DArrayShadow\" | \"samplerCubeArrayShadow\"\n            );\n\n            for i in 0..2 {\n                let ty = TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class: match i {\n                        0 => ImageClass::Sampled {\n                            kind: Sk::Float,\n                            multi: false,\n                        },\n                        _ => ImageClass::Depth { multi: false },\n                    },\n                };\n\n                declaration.overloads.push(module.add_builtin(\n                    vec![ty, TypeInner::Sampler { comparison: true }],\n                    MacroCall::SamplerShadow,\n                ))\n            }\n        }\n        \"sin\" | \"exp\" | \"exp2\" | \"sinh\" | \"cos\" | \"cosh\" | \"tan\" | \"tanh\" | \"acos\" | \"asin\"\n        | \"log\" | \"log2\" | \"radians\" | \"degrees\" | \"asinh\" | \"acosh\" | \"atanh\"\n        | \"floatBitsToInt\" | \"floatBitsToUint\" | \"dFdx\" | \"dFdxFine\" | \"dFdxCoarse\" | \"dFdy\"\n        | \"dFdyFine\" | \"dFdyCoarse\" | \"fwidth\" | \"fwidthFine\" | \"fwidthCoarse\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = Scalar::F32;\n\n                declaration.overloads.push(module.add_builtin(\n                    vec![match size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    }],\n                    match name {\n                        \"sin\" => MacroCall::MathFunction(MathFunction::Sin),\n                        \"exp\" => MacroCall::MathFunction(MathFunction::Exp),\n                        \"exp2\" => MacroCall::MathFunction(MathFunction::Exp2),\n                        \"sinh\" => MacroCall::MathFunction(MathFunction::Sinh),\n                        \"cos\" => MacroCall::MathFunction(MathFunction::Cos),\n                        \"cosh\" => MacroCall::MathFunction(MathFunction::Cosh),\n                        \"tan\" => MacroCall::MathFunction(MathFunction::Tan),\n                        \"tanh\" => MacroCall::MathFunction(MathFunction::Tanh),\n                        \"acos\" => MacroCall::MathFunction(MathFunction::Acos),\n                        \"asin\" => MacroCall::MathFunction(MathFunction::Asin),\n                        \"log\" => MacroCall::MathFunction(MathFunction::Log),\n                        \"log2\" => MacroCall::MathFunction(MathFunction::Log2),\n                        \"asinh\" => MacroCall::MathFunction(MathFunction::Asinh),\n                        \"acosh\" => MacroCall::MathFunction(MathFunction::Acosh),\n                        \"atanh\" => MacroCall::MathFunction(MathFunction::Atanh),\n                        \"radians\" => MacroCall::MathFunction(MathFunction::Radians),\n                        \"degrees\" => MacroCall::MathFunction(MathFunction::Degrees),\n                        \"floatBitsToInt\" => MacroCall::BitCast(Sk::Sint),\n                        \"floatBitsToUint\" => MacroCall::BitCast(Sk::Uint),\n                        \"dFdxCoarse\" => MacroCall::Derivate(Axis::X, Ctrl::Coarse),\n                        \"dFdyCoarse\" => MacroCall::Derivate(Axis::Y, Ctrl::Coarse),\n                        \"fwidthCoarse\" => MacroCall::Derivate(Axis::Width, Ctrl::Coarse),\n                        \"dFdxFine\" => MacroCall::Derivate(Axis::X, Ctrl::Fine),\n                        \"dFdyFine\" => MacroCall::Derivate(Axis::Y, Ctrl::Fine),\n                        \"fwidthFine\" => MacroCall::Derivate(Axis::Width, Ctrl::Fine),\n                        \"dFdx\" => MacroCall::Derivate(Axis::X, Ctrl::None),\n                        \"dFdy\" => MacroCall::Derivate(Axis::Y, Ctrl::None),\n                        \"fwidth\" => MacroCall::Derivate(Axis::Width, Ctrl::None),\n                        _ => unreachable!(),\n                    },\n                ))\n            }\n        }\n        \"intBitsToFloat\" | \"uintBitsToFloat\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = match name {\n                    \"intBitsToFloat\" => Scalar::I32,\n                    _ => Scalar::U32,\n                };\n\n                declaration.overloads.push(module.add_builtin(\n                    vec![match size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    }],\n                    MacroCall::BitCast(Sk::Float),\n                ))\n            }\n        }\n        \"pow\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = Scalar::F32;\n                let ty = || match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n\n                declaration.overloads.push(\n                    module\n                        .add_builtin(vec![ty(), ty()], MacroCall::MathFunction(MathFunction::Pow)),\n                )\n            }\n        }\n        \"abs\" | \"sign\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            // bit 2 - float/sint\n            for bits in 0..0b1000 {\n                let size = match bits & 0b11 {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = match bits >> 2 {\n                    0b0 => Scalar::F32,\n                    _ => Scalar::I32,\n                };\n\n                let args = vec![match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                }];\n\n                declaration.overloads.push(module.add_builtin(\n                    args,\n                    MacroCall::MathFunction(match name {\n                        \"abs\" => MathFunction::Abs,\n                        \"sign\" => MathFunction::Sign,\n                        _ => unreachable!(),\n                    }),\n                ))\n            }\n        }\n        \"bitCount\" | \"bitfieldReverse\" | \"bitfieldExtract\" | \"bitfieldInsert\" | \"findLSB\"\n        | \"findMSB\" => {\n            let fun = match name {\n                \"bitCount\" => MathFunction::CountOneBits,\n                \"bitfieldReverse\" => MathFunction::ReverseBits,\n                \"bitfieldExtract\" => MathFunction::ExtractBits,\n                \"bitfieldInsert\" => MathFunction::InsertBits,\n                \"findLSB\" => MathFunction::FirstTrailingBit,\n                \"findMSB\" => MathFunction::FirstLeadingBit,\n                _ => unreachable!(),\n            };\n\n            let mc = match fun {\n                MathFunction::ExtractBits => MacroCall::BitfieldExtract,\n                MathFunction::InsertBits => MacroCall::BitfieldInsert,\n                _ => MacroCall::MathFunction(fun),\n            };\n\n            // bits layout\n            // bit 0 - int/uint\n            // bit 1 through 2 - dims\n            for bits in 0..0b1000 {\n                let scalar = match bits & 0b1 {\n                    0b0 => Scalar::I32,\n                    _ => Scalar::U32,\n                };\n                let size = match bits >> 1 {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n\n                let ty = || match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n\n                let mut args = vec![ty()];\n\n                match fun {\n                    MathFunction::ExtractBits => {\n                        args.push(TypeInner::Scalar(Scalar::I32));\n                        args.push(TypeInner::Scalar(Scalar::I32));\n                    }\n                    MathFunction::InsertBits => {\n                        args.push(ty());\n                        args.push(TypeInner::Scalar(Scalar::I32));\n                        args.push(TypeInner::Scalar(Scalar::I32));\n                    }\n                    _ => {}\n                }\n\n                // we need to cast the return type of findLsb / findMsb\n                let mc = if scalar.kind == Sk::Uint {\n                    match mc {\n                        MacroCall::MathFunction(MathFunction::FirstTrailingBit) => {\n                            MacroCall::FindLsbUint\n                        }\n                        MacroCall::MathFunction(MathFunction::FirstLeadingBit) => {\n                            MacroCall::FindMsbUint\n                        }\n                        mc => mc,\n                    }\n                } else {\n                    mc\n                };\n\n                declaration.overloads.push(module.add_builtin(args, mc))\n            }\n        }\n        \"packSnorm4x8\" | \"packUnorm4x8\" | \"packSnorm2x16\" | \"packUnorm2x16\" | \"packHalf2x16\" => {\n            let fun = match name {\n                \"packSnorm4x8\" => MathFunction::Pack4x8snorm,\n                \"packUnorm4x8\" => MathFunction::Pack4x8unorm,\n                \"packSnorm2x16\" => MathFunction::Pack2x16unorm,\n                \"packUnorm2x16\" => MathFunction::Pack2x16snorm,\n                \"packHalf2x16\" => MathFunction::Pack2x16float,\n                _ => unreachable!(),\n            };\n\n            let ty = match fun {\n                MathFunction::Pack4x8snorm | MathFunction::Pack4x8unorm => TypeInner::Vector {\n                    size: VectorSize::Quad,\n                    scalar: Scalar::F32,\n                },\n                MathFunction::Pack2x16unorm\n                | MathFunction::Pack2x16snorm\n                | MathFunction::Pack2x16float => TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: Scalar::F32,\n                },\n                _ => unreachable!(),\n            };\n\n            let args = vec![ty];\n\n            declaration\n                .overloads\n                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));\n        }\n        \"unpackSnorm4x8\" | \"unpackUnorm4x8\" | \"unpackSnorm2x16\" | \"unpackUnorm2x16\"\n        | \"unpackHalf2x16\" => {\n            let fun = match name {\n                \"unpackSnorm4x8\" => MathFunction::Unpack4x8snorm,\n                \"unpackUnorm4x8\" => MathFunction::Unpack4x8unorm,\n                \"unpackSnorm2x16\" => MathFunction::Unpack2x16snorm,\n                \"unpackUnorm2x16\" => MathFunction::Unpack2x16unorm,\n                \"unpackHalf2x16\" => MathFunction::Unpack2x16float,\n                _ => unreachable!(),\n            };\n\n            let args = vec![TypeInner::Scalar(Scalar::U32)];\n\n            declaration\n                .overloads\n                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));\n        }\n        \"atan\" => {\n            // bits layout\n            // bit 0 - atan/atan2\n            // bit 1 through 2 - dims\n            for bits in 0..0b1000 {\n                let fun = match bits & 0b1 {\n                    0b0 => MathFunction::Atan,\n                    _ => MathFunction::Atan2,\n                };\n                let size = match bits >> 1 {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = Scalar::F32;\n                let ty = || match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n\n                let mut args = vec![ty()];\n\n                if fun == MathFunction::Atan2 {\n                    args.push(ty())\n                }\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::MathFunction(fun)))\n            }\n        }\n        \"all\" | \"any\" | \"not\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b11 {\n                let size = match bits {\n                    0b00 => VectorSize::Bi,\n                    0b01 => VectorSize::Tri,\n                    _ => VectorSize::Quad,\n                };\n\n                let args = vec![TypeInner::Vector {\n                    size,\n                    scalar: Scalar::BOOL,\n                }];\n\n                let fun = match name {\n                    \"all\" => MacroCall::Relational(RelationalFunction::All),\n                    \"any\" => MacroCall::Relational(RelationalFunction::Any),\n                    \"not\" => MacroCall::Unary(UnaryOperator::LogicalNot),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"lessThan\" | \"greaterThan\" | \"lessThanEqual\" | \"greaterThanEqual\" => {\n            for bits in 0..0b1001 {\n                let (size, scalar) = match bits {\n                    0b0000 => (VectorSize::Bi, Scalar::F32),\n                    0b0001 => (VectorSize::Tri, Scalar::F32),\n                    0b0010 => (VectorSize::Quad, Scalar::F32),\n                    0b0011 => (VectorSize::Bi, Scalar::I32),\n                    0b0100 => (VectorSize::Tri, Scalar::I32),\n                    0b0101 => (VectorSize::Quad, Scalar::I32),\n                    0b0110 => (VectorSize::Bi, Scalar::U32),\n                    0b0111 => (VectorSize::Tri, Scalar::U32),\n                    _ => (VectorSize::Quad, Scalar::U32),\n                };\n\n                let ty = || TypeInner::Vector { size, scalar };\n                let args = vec![ty(), ty()];\n\n                let fun = MacroCall::Binary(match name {\n                    \"lessThan\" => BinaryOperator::Less,\n                    \"greaterThan\" => BinaryOperator::Greater,\n                    \"lessThanEqual\" => BinaryOperator::LessEqual,\n                    \"greaterThanEqual\" => BinaryOperator::GreaterEqual,\n                    _ => unreachable!(),\n                });\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"equal\" | \"notEqual\" => {\n            for bits in 0..0b1100 {\n                let (size, scalar) = match bits {\n                    0b0000 => (VectorSize::Bi, Scalar::F32),\n                    0b0001 => (VectorSize::Tri, Scalar::F32),\n                    0b0010 => (VectorSize::Quad, Scalar::F32),\n                    0b0011 => (VectorSize::Bi, Scalar::I32),\n                    0b0100 => (VectorSize::Tri, Scalar::I32),\n                    0b0101 => (VectorSize::Quad, Scalar::I32),\n                    0b0110 => (VectorSize::Bi, Scalar::U32),\n                    0b0111 => (VectorSize::Tri, Scalar::U32),\n                    0b1000 => (VectorSize::Quad, Scalar::U32),\n                    0b1001 => (VectorSize::Bi, Scalar::BOOL),\n                    0b1010 => (VectorSize::Tri, Scalar::BOOL),\n                    _ => (VectorSize::Quad, Scalar::BOOL),\n                };\n\n                let ty = || TypeInner::Vector { size, scalar };\n                let args = vec![ty(), ty()];\n\n                let fun = MacroCall::Binary(match name {\n                    \"equal\" => BinaryOperator::Equal,\n                    \"notEqual\" => BinaryOperator::NotEqual,\n                    _ => unreachable!(),\n                });\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"min\" | \"max\" => {\n            // bits layout\n            // bit 0 through 1 - scalar kind\n            // bit 2 through 4 - dims\n            for bits in 0..0b11100 {\n                let scalar = match bits & 0b11 {\n                    0b00 => Scalar::F32,\n                    0b01 => Scalar::I32,\n                    0b10 => Scalar::U32,\n                    _ => continue,\n                };\n                let (size, second_size) = match bits >> 2 {\n                    0b000 => (None, None),\n                    0b001 => (Some(VectorSize::Bi), None),\n                    0b010 => (Some(VectorSize::Tri), None),\n                    0b011 => (Some(VectorSize::Quad), None),\n                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),\n                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),\n                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),\n                };\n\n                let args = vec![\n                    match size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    },\n                    match second_size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    },\n                ];\n\n                let fun = match name {\n                    \"max\" => MacroCall::Splatted(MathFunction::Max, size, 1),\n                    \"min\" => MacroCall::Splatted(MathFunction::Min, size, 1),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"mix\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            // bit 2 through 4 - types\n            //\n            // 0b10011 is the last element since splatted single elements\n            // were already added\n            for bits in 0..0b10011 {\n                let size = match bits & 0b11 {\n                    0b00 => Some(VectorSize::Bi),\n                    0b01 => Some(VectorSize::Tri),\n                    0b10 => Some(VectorSize::Quad),\n                    _ => None,\n                };\n                let (scalar, splatted, boolean) = match bits >> 2 {\n                    0b000 => (Scalar::I32, false, true),\n                    0b001 => (Scalar::U32, false, true),\n                    0b010 => (Scalar::F32, false, true),\n                    0b011 => (Scalar::F32, false, false),\n                    _ => (Scalar::F32, true, false),\n                };\n\n                let ty = |scalar| match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n                let args = vec![\n                    ty(scalar),\n                    ty(scalar),\n                    match (boolean, splatted) {\n                        (true, _) => ty(Scalar::BOOL),\n                        (_, false) => TypeInner::Scalar(scalar),\n                        _ => ty(scalar),\n                    },\n                ];\n\n                declaration.overloads.push(module.add_builtin(\n                    args,\n                    match boolean {\n                        true => MacroCall::MixBoolean,\n                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),\n                    },\n                ))\n            }\n        }\n        \"clamp\" => {\n            // bits layout\n            // bit 0 through 1 - float/int/uint\n            // bit 2 through 3 - dims\n            // bit 4 - splatted\n            //\n            // 0b11010 is the last element since splatted single elements\n            // were already added\n            for bits in 0..0b11011 {\n                let scalar = match bits & 0b11 {\n                    0b00 => Scalar::F32,\n                    0b01 => Scalar::I32,\n                    0b10 => Scalar::U32,\n                    _ => continue,\n                };\n                let size = match (bits >> 2) & 0b11 {\n                    0b00 => Some(VectorSize::Bi),\n                    0b01 => Some(VectorSize::Tri),\n                    0b10 => Some(VectorSize::Quad),\n                    _ => None,\n                };\n                let splatted = bits & 0b10000 == 0b10000;\n\n                let base_ty = || match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n                let limit_ty = || match splatted {\n                    true => TypeInner::Scalar(scalar),\n                    false => base_ty(),\n                };\n\n                let args = vec![base_ty(), limit_ty(), limit_ty()];\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::Clamp(size)))\n            }\n        }\n        \"barrier\" => declaration\n            .overloads\n            .push(module.add_builtin(Vec::new(), MacroCall::Barrier)),\n        // Add common builtins with floats\n        _ => inject_common_builtin(declaration, module, name, 4),\n    }\n}\n\n/// Injects the builtins into declaration that need doubles\nfn inject_double_builtin(declaration: &mut FunctionDeclaration, module: &mut Module, name: &str) {\n    match name {\n        \"abs\" | \"sign\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let scalar = Scalar::F64;\n\n                let args = vec![match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                }];\n\n                declaration.overloads.push(module.add_builtin(\n                    args,\n                    MacroCall::MathFunction(match name {\n                        \"abs\" => MathFunction::Abs,\n                        \"sign\" => MathFunction::Sign,\n                        _ => unreachable!(),\n                    }),\n                ))\n            }\n        }\n        \"min\" | \"max\" => {\n            // bits layout\n            // bit 0 through 2 - dims\n            for bits in 0..0b111 {\n                let (size, second_size) = match bits {\n                    0b000 => (None, None),\n                    0b001 => (Some(VectorSize::Bi), None),\n                    0b010 => (Some(VectorSize::Tri), None),\n                    0b011 => (Some(VectorSize::Quad), None),\n                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),\n                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),\n                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),\n                };\n                let scalar = Scalar::F64;\n\n                let args = vec![\n                    match size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    },\n                    match second_size {\n                        Some(size) => TypeInner::Vector { size, scalar },\n                        None => TypeInner::Scalar(scalar),\n                    },\n                ];\n\n                let fun = match name {\n                    \"max\" => MacroCall::Splatted(MathFunction::Max, size, 1),\n                    \"min\" => MacroCall::Splatted(MathFunction::Min, size, 1),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"mix\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            // bit 2 through 3 - splatted/boolean\n            //\n            // 0b1010 is the last element since splatted with single elements\n            // is equal to normal single elements\n            for bits in 0..0b1011 {\n                let size = match bits & 0b11 {\n                    0b00 => Some(VectorSize::Quad),\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => None,\n                };\n                let scalar = Scalar::F64;\n                let (splatted, boolean) = match bits >> 2 {\n                    0b00 => (false, false),\n                    0b01 => (false, true),\n                    _ => (true, false),\n                };\n\n                let ty = |scalar| match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n                let args = vec![\n                    ty(scalar),\n                    ty(scalar),\n                    match (boolean, splatted) {\n                        (true, _) => ty(Scalar::BOOL),\n                        (_, false) => TypeInner::Scalar(scalar),\n                        _ => ty(scalar),\n                    },\n                ];\n\n                declaration.overloads.push(module.add_builtin(\n                    args,\n                    match boolean {\n                        true => MacroCall::MixBoolean,\n                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),\n                    },\n                ))\n            }\n        }\n        \"clamp\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            // bit 2 - splatted\n            //\n            // 0b110 is the last element since splatted with single elements\n            // is equal to normal single elements\n            for bits in 0..0b111 {\n                let scalar = Scalar::F64;\n                let size = match bits & 0b11 {\n                    0b00 => Some(VectorSize::Bi),\n                    0b01 => Some(VectorSize::Tri),\n                    0b10 => Some(VectorSize::Quad),\n                    _ => None,\n                };\n                let splatted = bits & 0b100 == 0b100;\n\n                let base_ty = || match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n                let limit_ty = || match splatted {\n                    true => TypeInner::Scalar(scalar),\n                    false => base_ty(),\n                };\n\n                let args = vec![base_ty(), limit_ty(), limit_ty()];\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::Clamp(size)))\n            }\n        }\n        \"lessThan\" | \"greaterThan\" | \"lessThanEqual\" | \"greaterThanEqual\" | \"equal\"\n        | \"notEqual\" => {\n            let scalar = Scalar::F64;\n            for bits in 0..0b11 {\n                let size = match bits {\n                    0b00 => VectorSize::Bi,\n                    0b01 => VectorSize::Tri,\n                    _ => VectorSize::Quad,\n                };\n\n                let ty = || TypeInner::Vector { size, scalar };\n                let args = vec![ty(), ty()];\n\n                let fun = MacroCall::Binary(match name {\n                    \"lessThan\" => BinaryOperator::Less,\n                    \"greaterThan\" => BinaryOperator::Greater,\n                    \"lessThanEqual\" => BinaryOperator::LessEqual,\n                    \"greaterThanEqual\" => BinaryOperator::GreaterEqual,\n                    \"equal\" => BinaryOperator::Equal,\n                    \"notEqual\" => BinaryOperator::NotEqual,\n                    _ => unreachable!(),\n                });\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        // Add common builtins with doubles\n        _ => inject_common_builtin(declaration, module, name, 8),\n    }\n}\n\n/// Injects the builtins into declaration that can used either float or doubles\nfn inject_common_builtin(\n    declaration: &mut FunctionDeclaration,\n    module: &mut Module,\n    name: &str,\n    float_width: crate::Bytes,\n) {\n    let float_scalar = Scalar {\n        kind: Sk::Float,\n        width: float_width,\n    };\n    match name {\n        \"ceil\" | \"round\" | \"roundEven\" | \"floor\" | \"fract\" | \"trunc\" | \"sqrt\" | \"inversesqrt\"\n        | \"normalize\" | \"length\" | \"isinf\" | \"isnan\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n\n                let args = vec![match size {\n                    Some(size) => TypeInner::Vector {\n                        size,\n                        scalar: float_scalar,\n                    },\n                    None => TypeInner::Scalar(float_scalar),\n                }];\n\n                let fun = match name {\n                    \"ceil\" => MacroCall::MathFunction(MathFunction::Ceil),\n                    \"round\" | \"roundEven\" => MacroCall::MathFunction(MathFunction::Round),\n                    \"floor\" => MacroCall::MathFunction(MathFunction::Floor),\n                    \"fract\" => MacroCall::MathFunction(MathFunction::Fract),\n                    \"trunc\" => MacroCall::MathFunction(MathFunction::Trunc),\n                    \"sqrt\" => MacroCall::MathFunction(MathFunction::Sqrt),\n                    \"inversesqrt\" => MacroCall::MathFunction(MathFunction::InverseSqrt),\n                    \"normalize\" => MacroCall::MathFunction(MathFunction::Normalize),\n                    \"length\" => MacroCall::MathFunction(MathFunction::Length),\n                    \"isinf\" => MacroCall::Relational(RelationalFunction::IsInf),\n                    \"isnan\" => MacroCall::Relational(RelationalFunction::IsNan),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"dot\" | \"reflect\" | \"distance\" | \"ldexp\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n                let ty = |scalar| match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n\n                let fun = match name {\n                    \"dot\" => MacroCall::MathFunction(MathFunction::Dot),\n                    \"reflect\" => MacroCall::MathFunction(MathFunction::Reflect),\n                    \"distance\" => MacroCall::MathFunction(MathFunction::Distance),\n                    \"ldexp\" => MacroCall::MathFunction(MathFunction::Ldexp),\n                    _ => unreachable!(),\n                };\n\n                let second_scalar = match fun {\n                    MacroCall::MathFunction(MathFunction::Ldexp) => Scalar::I32,\n                    _ => float_scalar,\n                };\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(vec![ty(float_scalar), ty(second_scalar)], fun))\n            }\n        }\n        \"transpose\" => {\n            // bits layout\n            // bit 0 through 3 - dims\n            for bits in 0..0b1001 {\n                let (rows, columns) = match bits {\n                    0b0000 => (VectorSize::Bi, VectorSize::Bi),\n                    0b0001 => (VectorSize::Bi, VectorSize::Tri),\n                    0b0010 => (VectorSize::Bi, VectorSize::Quad),\n                    0b0011 => (VectorSize::Tri, VectorSize::Bi),\n                    0b0100 => (VectorSize::Tri, VectorSize::Tri),\n                    0b0101 => (VectorSize::Tri, VectorSize::Quad),\n                    0b0110 => (VectorSize::Quad, VectorSize::Bi),\n                    0b0111 => (VectorSize::Quad, VectorSize::Tri),\n                    _ => (VectorSize::Quad, VectorSize::Quad),\n                };\n\n                declaration.overloads.push(module.add_builtin(\n                    vec![TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar: float_scalar,\n                    }],\n                    MacroCall::MathFunction(MathFunction::Transpose),\n                ))\n            }\n        }\n        \"inverse\" | \"determinant\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b11 {\n                let (rows, columns) = match bits {\n                    0b00 => (VectorSize::Bi, VectorSize::Bi),\n                    0b01 => (VectorSize::Tri, VectorSize::Tri),\n                    _ => (VectorSize::Quad, VectorSize::Quad),\n                };\n\n                let args = vec![TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar: float_scalar,\n                }];\n\n                declaration.overloads.push(module.add_builtin(\n                    args,\n                    MacroCall::MathFunction(match name {\n                        \"inverse\" => MathFunction::Inverse,\n                        \"determinant\" => MathFunction::Determinant,\n                        _ => unreachable!(),\n                    }),\n                ))\n            }\n        }\n        \"mod\" | \"step\" => {\n            // bits layout\n            // bit 0 through 2 - dims\n            for bits in 0..0b111 {\n                let (size, second_size) = match bits {\n                    0b000 => (None, None),\n                    0b001 => (Some(VectorSize::Bi), None),\n                    0b010 => (Some(VectorSize::Tri), None),\n                    0b011 => (Some(VectorSize::Quad), None),\n                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),\n                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),\n                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),\n                };\n\n                let mut args = Vec::with_capacity(2);\n                let step = name == \"step\";\n\n                for i in 0..2 {\n                    let maybe_size = match i == step as u32 {\n                        true => size,\n                        false => second_size,\n                    };\n\n                    args.push(match maybe_size {\n                        Some(size) => TypeInner::Vector {\n                            size,\n                            scalar: float_scalar,\n                        },\n                        None => TypeInner::Scalar(float_scalar),\n                    })\n                }\n\n                let fun = match name {\n                    \"mod\" => MacroCall::Mod(size),\n                    \"step\" => MacroCall::Splatted(MathFunction::Step, size, 0),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        // TODO: https://github.com/gfx-rs/naga/issues/2526\n        // \"modf\" | \"frexp\" => { ... }\n        \"cross\" => {\n            let args = vec![\n                TypeInner::Vector {\n                    size: VectorSize::Tri,\n                    scalar: float_scalar,\n                },\n                TypeInner::Vector {\n                    size: VectorSize::Tri,\n                    scalar: float_scalar,\n                },\n            ];\n\n            declaration\n                .overloads\n                .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Cross)))\n        }\n        \"outerProduct\" => {\n            // bits layout\n            // bit 0 through 3 - dims\n            for bits in 0..0b1001 {\n                let (size1, size2) = match bits {\n                    0b0000 => (VectorSize::Bi, VectorSize::Bi),\n                    0b0001 => (VectorSize::Bi, VectorSize::Tri),\n                    0b0010 => (VectorSize::Bi, VectorSize::Quad),\n                    0b0011 => (VectorSize::Tri, VectorSize::Bi),\n                    0b0100 => (VectorSize::Tri, VectorSize::Tri),\n                    0b0101 => (VectorSize::Tri, VectorSize::Quad),\n                    0b0110 => (VectorSize::Quad, VectorSize::Bi),\n                    0b0111 => (VectorSize::Quad, VectorSize::Tri),\n                    _ => (VectorSize::Quad, VectorSize::Quad),\n                };\n\n                let args = vec![\n                    TypeInner::Vector {\n                        size: size1,\n                        scalar: float_scalar,\n                    },\n                    TypeInner::Vector {\n                        size: size2,\n                        scalar: float_scalar,\n                    },\n                ];\n\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Outer)))\n            }\n        }\n        \"faceforward\" | \"fma\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n\n                let ty = || match size {\n                    Some(size) => TypeInner::Vector {\n                        size,\n                        scalar: float_scalar,\n                    },\n                    None => TypeInner::Scalar(float_scalar),\n                };\n                let args = vec![ty(), ty(), ty()];\n\n                let fun = match name {\n                    \"faceforward\" => MacroCall::MathFunction(MathFunction::FaceForward),\n                    \"fma\" => MacroCall::MathFunction(MathFunction::Fma),\n                    _ => unreachable!(),\n                };\n\n                declaration.overloads.push(module.add_builtin(args, fun))\n            }\n        }\n        \"refract\" => {\n            // bits layout\n            // bit 0 through 1 - dims\n            for bits in 0..0b100 {\n                let size = match bits {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n\n                let ty = || match size {\n                    Some(size) => TypeInner::Vector {\n                        size,\n                        scalar: float_scalar,\n                    },\n                    None => TypeInner::Scalar(float_scalar),\n                };\n                let args = vec![ty(), ty(), TypeInner::Scalar(Scalar::F32)];\n                declaration\n                    .overloads\n                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Refract)))\n            }\n        }\n        \"smoothstep\" => {\n            // bit 0 - splatted\n            // bit 1 through 2 - dims\n            for bits in 0..0b1000 {\n                let splatted = bits & 0b1 == 0b1;\n                let size = match bits >> 1 {\n                    0b00 => None,\n                    0b01 => Some(VectorSize::Bi),\n                    0b10 => Some(VectorSize::Tri),\n                    _ => Some(VectorSize::Quad),\n                };\n\n                if splatted && size.is_none() {\n                    continue;\n                }\n\n                let base_ty = || match size {\n                    Some(size) => TypeInner::Vector {\n                        size,\n                        scalar: float_scalar,\n                    },\n                    None => TypeInner::Scalar(float_scalar),\n                };\n                let ty = || match splatted {\n                    true => TypeInner::Scalar(float_scalar),\n                    false => base_ty(),\n                };\n                declaration.overloads.push(module.add_builtin(\n                    vec![ty(), ty(), base_ty()],\n                    MacroCall::SmoothStep { splatted: size },\n                ))\n            }\n        }\n        // The function isn't a builtin or we don't yet support it\n        _ => {}\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum TextureLevelType {\n    None,\n    Lod,\n    Grad,\n}\n\n/// A compiler defined builtin function\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum MacroCall {\n    Sampler,\n    SamplerShadow,\n    Texture {\n        proj: bool,\n        offset: bool,\n        shadow: bool,\n        level_type: TextureLevelType,\n    },\n    TextureSize {\n        arrayed: bool,\n    },\n    TextureQueryLevels,\n    ImageLoad {\n        multi: bool,\n    },\n    ImageStore,\n    MathFunction(MathFunction),\n    FindLsbUint,\n    FindMsbUint,\n    BitfieldExtract,\n    BitfieldInsert,\n    Relational(RelationalFunction),\n    Unary(UnaryOperator),\n    Binary(BinaryOperator),\n    Mod(Option<VectorSize>),\n    Splatted(MathFunction, Option<VectorSize>, usize),\n    MixBoolean,\n    Clamp(Option<VectorSize>),\n    BitCast(Sk),\n    Derivate(Axis, Ctrl),\n    Barrier,\n    /// SmoothStep needs a separate variant because it might need it's inputs\n    /// to be splatted depending on the overload\n    SmoothStep {\n        /// The size of the splat operation if some\n        splatted: Option<VectorSize>,\n    },\n}\n\nimpl MacroCall {\n    /// Adds the necessary expressions and statements to the passed body and\n    /// finally returns the final expression with the correct result\n    pub fn call(\n        &self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        args: &mut [Handle<Expression>],\n        meta: Span,\n    ) -> Result<Option<Handle<Expression>>> {\n        Ok(Some(match *self {\n            MacroCall::Sampler => {\n                ctx.samplers.insert(args[0], args[1]);\n                args[0]\n            }\n            MacroCall::SamplerShadow => {\n                sampled_to_depth(ctx, args[0], meta, &mut frontend.errors);\n                ctx.invalidate_expression(args[0], meta)?;\n                ctx.samplers.insert(args[0], args[1]);\n                args[0]\n            }\n            MacroCall::Texture {\n                proj,\n                offset,\n                shadow,\n                level_type,\n            } => {\n                let mut coords = args[1];\n\n                if proj {\n                    let size = match *ctx.resolve_type(coords, meta)? {\n                        TypeInner::Vector { size, .. } => size,\n                        _ => unreachable!(),\n                    };\n                    let mut right = ctx.add_expression(\n                        Expression::AccessIndex {\n                            base: coords,\n                            index: size as u32 - 1,\n                        },\n                        Span::default(),\n                    )?;\n                    let left = if let VectorSize::Bi = size {\n                        ctx.add_expression(\n                            Expression::AccessIndex {\n                                base: coords,\n                                index: 0,\n                            },\n                            Span::default(),\n                        )?\n                    } else {\n                        let size = match size {\n                            VectorSize::Tri => VectorSize::Bi,\n                            _ => VectorSize::Tri,\n                        };\n                        right = ctx.add_expression(\n                            Expression::Splat { size, value: right },\n                            Span::default(),\n                        )?;\n                        ctx.vector_resize(size, coords, Span::default())?\n                    };\n                    coords = ctx.add_expression(\n                        Expression::Binary {\n                            op: BinaryOperator::Divide,\n                            left,\n                            right,\n                        },\n                        Span::default(),\n                    )?;\n                }\n\n                let extra = args.get(2).copied();\n                let comps = frontend.coordinate_components(ctx, args[0], coords, extra, meta)?;\n\n                let mut num_args = 2;\n\n                if comps.used_extra {\n                    num_args += 1;\n                };\n\n                // Parse out explicit texture level.\n                let mut level = match level_type {\n                    TextureLevelType::None => SampleLevel::Auto,\n\n                    TextureLevelType::Lod => {\n                        num_args += 1;\n\n                        if shadow {\n                            log::debug!(\"Assuming LOD {:?} is zero\", args[2],);\n\n                            SampleLevel::Zero\n                        } else {\n                            SampleLevel::Exact(args[2])\n                        }\n                    }\n\n                    TextureLevelType::Grad => {\n                        num_args += 2;\n\n                        if shadow {\n                            log::debug!(\n                                \"Assuming gradients {:?} and {:?} are not greater than 1\",\n                                args[2],\n                                args[3],\n                            );\n                            SampleLevel::Zero\n                        } else {\n                            SampleLevel::Gradient {\n                                x: args[2],\n                                y: args[3],\n                            }\n                        }\n                    }\n                };\n\n                let texture_offset = match offset {\n                    true => {\n                        let offset_arg = args[num_args];\n                        num_args += 1;\n                        Some(offset_arg)\n                    }\n                    false => None,\n                };\n\n                // Now go back and look for optional bias arg (if available)\n                if let TextureLevelType::None = level_type {\n                    level = args\n                        .get(num_args)\n                        .copied()\n                        .map_or(SampleLevel::Auto, SampleLevel::Bias);\n                }\n\n                texture_call(ctx, args[0], level, comps, texture_offset, meta)?\n            }\n\n            MacroCall::TextureSize { arrayed } => {\n                let mut expr = ctx.add_expression(\n                    Expression::ImageQuery {\n                        image: args[0],\n                        query: ImageQuery::Size {\n                            level: args.get(1).copied(),\n                        },\n                    },\n                    Span::default(),\n                )?;\n\n                if arrayed {\n                    let mut components = Vec::with_capacity(4);\n\n                    let size = match *ctx.resolve_type(expr, meta)? {\n                        TypeInner::Vector { size: ori_size, .. } => {\n                            for index in 0..(ori_size as u32) {\n                                components.push(ctx.add_expression(\n                                    Expression::AccessIndex { base: expr, index },\n                                    Span::default(),\n                                )?)\n                            }\n\n                            match ori_size {\n                                VectorSize::Bi => VectorSize::Tri,\n                                _ => VectorSize::Quad,\n                            }\n                        }\n                        _ => {\n                            components.push(expr);\n                            VectorSize::Bi\n                        }\n                    };\n\n                    components.push(ctx.add_expression(\n                        Expression::ImageQuery {\n                            image: args[0],\n                            query: ImageQuery::NumLayers,\n                        },\n                        Span::default(),\n                    )?);\n\n                    let ty = ctx.module.types.insert(\n                        Type {\n                            name: None,\n                            inner: TypeInner::Vector {\n                                size,\n                                scalar: Scalar::U32,\n                            },\n                        },\n                        Span::default(),\n                    );\n\n                    expr = ctx.add_expression(Expression::Compose { components, ty }, meta)?\n                }\n\n                ctx.add_expression(\n                    Expression::As {\n                        expr,\n                        kind: Sk::Sint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::TextureQueryLevels => {\n                let expr = ctx.add_expression(\n                    Expression::ImageQuery {\n                        image: args[0],\n                        query: ImageQuery::NumLevels,\n                    },\n                    Span::default(),\n                )?;\n\n                ctx.add_expression(\n                    Expression::As {\n                        expr,\n                        kind: Sk::Sint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::ImageLoad { multi } => {\n                let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;\n                let (sample, level) = match (multi, args.get(2)) {\n                    (_, None) => (None, None),\n                    (true, Some(&arg)) => (Some(arg), None),\n                    (false, Some(&arg)) => (None, Some(arg)),\n                };\n                ctx.add_expression(\n                    Expression::ImageLoad {\n                        image: args[0],\n                        coordinate: comps.coordinate,\n                        array_index: comps.array_index,\n                        sample,\n                        level,\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::ImageStore => {\n                let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;\n                ctx.emit_restart();\n                ctx.body.push(\n                    crate::Statement::ImageStore {\n                        image: args[0],\n                        coordinate: comps.coordinate,\n                        array_index: comps.array_index,\n                        value: args[2],\n                    },\n                    meta,\n                );\n                return Ok(None);\n            }\n            MacroCall::MathFunction(fun) => ctx.add_expression(\n                Expression::Math {\n                    fun,\n                    arg: args[0],\n                    arg1: args.get(1).copied(),\n                    arg2: args.get(2).copied(),\n                    arg3: args.get(3).copied(),\n                },\n                Span::default(),\n            )?,\n            mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => {\n                let fun = match mc {\n                    MacroCall::FindLsbUint => MathFunction::FirstTrailingBit,\n                    MacroCall::FindMsbUint => MathFunction::FirstLeadingBit,\n                    _ => unreachable!(),\n                };\n                let res = ctx.add_expression(\n                    Expression::Math {\n                        fun,\n                        arg: args[0],\n                        arg1: None,\n                        arg2: None,\n                        arg3: None,\n                    },\n                    Span::default(),\n                )?;\n                ctx.add_expression(\n                    Expression::As {\n                        expr: res,\n                        kind: Sk::Sint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::BitfieldInsert => {\n                let conv_arg_2 = ctx.add_expression(\n                    Expression::As {\n                        expr: args[2],\n                        kind: Sk::Uint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?;\n                let conv_arg_3 = ctx.add_expression(\n                    Expression::As {\n                        expr: args[3],\n                        kind: Sk::Uint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?;\n                ctx.add_expression(\n                    Expression::Math {\n                        fun: MathFunction::InsertBits,\n                        arg: args[0],\n                        arg1: Some(args[1]),\n                        arg2: Some(conv_arg_2),\n                        arg3: Some(conv_arg_3),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::BitfieldExtract => {\n                let conv_arg_1 = ctx.add_expression(\n                    Expression::As {\n                        expr: args[1],\n                        kind: Sk::Uint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?;\n                let conv_arg_2 = ctx.add_expression(\n                    Expression::As {\n                        expr: args[2],\n                        kind: Sk::Uint,\n                        convert: Some(4),\n                    },\n                    Span::default(),\n                )?;\n                ctx.add_expression(\n                    Expression::Math {\n                        fun: MathFunction::ExtractBits,\n                        arg: args[0],\n                        arg1: Some(conv_arg_1),\n                        arg2: Some(conv_arg_2),\n                        arg3: None,\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::Relational(fun) => ctx.add_expression(\n                Expression::Relational {\n                    fun,\n                    argument: args[0],\n                },\n                Span::default(),\n            )?,\n            MacroCall::Unary(op) => {\n                ctx.add_expression(Expression::Unary { op, expr: args[0] }, Span::default())?\n            }\n            MacroCall::Binary(op) => ctx.add_expression(\n                Expression::Binary {\n                    op,\n                    left: args[0],\n                    right: args[1],\n                },\n                Span::default(),\n            )?,\n            MacroCall::Mod(size) => {\n                ctx.implicit_splat(&mut args[1], meta, size)?;\n\n                // x - y * floor(x / y)\n\n                let div = ctx.add_expression(\n                    Expression::Binary {\n                        op: BinaryOperator::Divide,\n                        left: args[0],\n                        right: args[1],\n                    },\n                    Span::default(),\n                )?;\n                let floor = ctx.add_expression(\n                    Expression::Math {\n                        fun: MathFunction::Floor,\n                        arg: div,\n                        arg1: None,\n                        arg2: None,\n                        arg3: None,\n                    },\n                    Span::default(),\n                )?;\n                let mult = ctx.add_expression(\n                    Expression::Binary {\n                        op: BinaryOperator::Multiply,\n                        left: floor,\n                        right: args[1],\n                    },\n                    Span::default(),\n                )?;\n                ctx.add_expression(\n                    Expression::Binary {\n                        op: BinaryOperator::Subtract,\n                        left: args[0],\n                        right: mult,\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::Splatted(fun, size, i) => {\n                ctx.implicit_splat(&mut args[i], meta, size)?;\n\n                ctx.add_expression(\n                    Expression::Math {\n                        fun,\n                        arg: args[0],\n                        arg1: args.get(1).copied(),\n                        arg2: args.get(2).copied(),\n                        arg3: args.get(3).copied(),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::MixBoolean => ctx.add_expression(\n                Expression::Select {\n                    condition: args[2],\n                    accept: args[1],\n                    reject: args[0],\n                },\n                Span::default(),\n            )?,\n            MacroCall::Clamp(size) => {\n                ctx.implicit_splat(&mut args[1], meta, size)?;\n                ctx.implicit_splat(&mut args[2], meta, size)?;\n\n                ctx.add_expression(\n                    Expression::Math {\n                        fun: MathFunction::Clamp,\n                        arg: args[0],\n                        arg1: args.get(1).copied(),\n                        arg2: args.get(2).copied(),\n                        arg3: args.get(3).copied(),\n                    },\n                    Span::default(),\n                )?\n            }\n            MacroCall::BitCast(kind) => ctx.add_expression(\n                Expression::As {\n                    expr: args[0],\n                    kind,\n                    convert: None,\n                },\n                Span::default(),\n            )?,\n            MacroCall::Derivate(axis, ctrl) => ctx.add_expression(\n                Expression::Derivative {\n                    axis,\n                    ctrl,\n                    expr: args[0],\n                },\n                Span::default(),\n            )?,\n            MacroCall::Barrier => {\n                ctx.emit_restart();\n                ctx.body.push(\n                    crate::Statement::ControlBarrier(crate::Barrier::all()),\n                    meta,\n                );\n                return Ok(None);\n            }\n            MacroCall::SmoothStep { splatted } => {\n                ctx.implicit_splat(&mut args[0], meta, splatted)?;\n                ctx.implicit_splat(&mut args[1], meta, splatted)?;\n\n                ctx.add_expression(\n                    Expression::Math {\n                        fun: MathFunction::SmoothStep,\n                        arg: args[0],\n                        arg1: args.get(1).copied(),\n                        arg2: args.get(2).copied(),\n                        arg3: None,\n                    },\n                    Span::default(),\n                )?\n            }\n        }))\n    }\n}\n\nfn texture_call(\n    ctx: &mut Context,\n    image: Handle<Expression>,\n    level: SampleLevel,\n    comps: CoordComponents,\n    offset: Option<Handle<Expression>>,\n    meta: Span,\n) -> Result<Handle<Expression>> {\n    if let Some(sampler) = ctx.samplers.get(&image).copied() {\n        let mut array_index = comps.array_index;\n\n        if let Some(ref mut array_index_expr) = array_index {\n            ctx.conversion(array_index_expr, meta, Scalar::I32)?;\n        }\n\n        Ok(ctx.add_expression(\n            Expression::ImageSample {\n                image,\n                sampler,\n                gather: None, //TODO\n                coordinate: comps.coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref: comps.depth_ref,\n                clamp_to_edge: false,\n            },\n            meta,\n        )?)\n    } else {\n        Err(Error {\n            kind: ErrorKind::SemanticError(\"Bad call\".into()),\n            meta,\n        })\n    }\n}\n\n/// Helper struct for texture calls with the separate components from the vector argument\n///\n/// Obtained by calling [`coordinate_components`](Frontend::coordinate_components)\n#[derive(Debug)]\nstruct CoordComponents {\n    coordinate: Handle<Expression>,\n    depth_ref: Option<Handle<Expression>>,\n    array_index: Option<Handle<Expression>>,\n    used_extra: bool,\n}\n\nimpl Frontend {\n    /// Helper function for texture calls, splits the vector argument into it's components\n    fn coordinate_components(\n        &mut self,\n        ctx: &mut Context,\n        image: Handle<Expression>,\n        coord: Handle<Expression>,\n        extra: Option<Handle<Expression>>,\n        meta: Span,\n    ) -> Result<CoordComponents> {\n        if let TypeInner::Image {\n            dim,\n            arrayed,\n            class,\n        } = *ctx.resolve_type(image, meta)?\n        {\n            let image_size = match dim {\n                Dim::D1 => None,\n                Dim::D2 => Some(VectorSize::Bi),\n                Dim::D3 => Some(VectorSize::Tri),\n                Dim::Cube => Some(VectorSize::Tri),\n            };\n            let coord_size = match *ctx.resolve_type(coord, meta)? {\n                TypeInner::Vector { size, .. } => Some(size),\n                _ => None,\n            };\n            let (shadow, storage) = match class {\n                ImageClass::Depth { .. } => (true, false),\n                ImageClass::Storage { .. } => (false, true),\n                ImageClass::Sampled { .. } => (false, false),\n                ImageClass::External => unreachable!(),\n            };\n\n            let coordinate = match (image_size, coord_size) {\n                (Some(size), Some(coord_s)) if size != coord_s => {\n                    ctx.vector_resize(size, coord, Span::default())?\n                }\n                (None, Some(_)) => ctx.add_expression(\n                    Expression::AccessIndex {\n                        base: coord,\n                        index: 0,\n                    },\n                    Span::default(),\n                )?,\n                _ => coord,\n            };\n\n            let mut coord_index = image_size.map_or(1, |s| s as u32);\n\n            let array_index = if arrayed && !(storage && dim == Dim::Cube) {\n                let index = coord_index;\n                coord_index += 1;\n\n                Some(ctx.add_expression(\n                    Expression::AccessIndex { base: coord, index },\n                    Span::default(),\n                )?)\n            } else {\n                None\n            };\n            let mut used_extra = false;\n            let depth_ref = match shadow {\n                true => {\n                    let index = coord_index;\n\n                    if index == 4 {\n                        used_extra = true;\n                        extra\n                    } else {\n                        Some(ctx.add_expression(\n                            Expression::AccessIndex { base: coord, index },\n                            Span::default(),\n                        )?)\n                    }\n                }\n                false => None,\n            };\n\n            Ok(CoordComponents {\n                coordinate,\n                depth_ref,\n                array_index,\n                used_extra,\n            })\n        } else {\n            self.errors.push(Error {\n                kind: ErrorKind::SemanticError(\"Type is not an image\".into()),\n                meta,\n            });\n\n            Ok(CoordComponents {\n                coordinate: coord,\n                depth_ref: None,\n                array_index: None,\n                used_extra: false,\n            })\n        }\n    }\n}\n\n/// Helper function to cast a expression holding a sampled image to a\n/// depth image.\npub fn sampled_to_depth(\n    ctx: &mut Context,\n    image: Handle<Expression>,\n    meta: Span,\n    errors: &mut Vec<Error>,\n) {\n    // Get the a mutable type handle of the underlying image storage\n    let ty = match ctx[image] {\n        Expression::GlobalVariable(handle) => &mut ctx.module.global_variables.get_mut(handle).ty,\n        Expression::FunctionArgument(i) => {\n            // Mark the function argument as carrying a depth texture\n            ctx.parameters_info[i as usize].depth = true;\n            // NOTE: We need to later also change the parameter type\n            &mut ctx.arguments[i as usize].ty\n        }\n        _ => {\n            // Only globals and function arguments are allowed to carry an image\n            return errors.push(Error {\n                kind: ErrorKind::SemanticError(\"Not a valid texture expression\".into()),\n                meta,\n            });\n        }\n    };\n\n    match ctx.module.types[*ty].inner {\n        // Update the image class to depth in case it already isn't\n        TypeInner::Image {\n            class,\n            dim,\n            arrayed,\n        } => match class {\n            ImageClass::Sampled { multi, .. } => {\n                *ty = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Image {\n                            dim,\n                            arrayed,\n                            class: ImageClass::Depth { multi },\n                        },\n                    },\n                    Span::default(),\n                )\n            }\n            ImageClass::Depth { .. } => {}\n            // Other image classes aren't allowed to be transformed to depth\n            ImageClass::Storage { .. } => errors.push(Error {\n                kind: ErrorKind::SemanticError(\"Not a texture\".into()),\n                meta,\n            }),\n            ImageClass::External => unreachable!(),\n        },\n        _ => errors.push(Error {\n            kind: ErrorKind::SemanticError(\"Not a texture\".into()),\n            meta,\n        }),\n    };\n\n    // Copy the handle to allow borrowing the `ctx` again\n    let ty = *ty;\n\n    // If the image was passed through a function argument we also need to change\n    // the corresponding parameter\n    if let Expression::FunctionArgument(i) = ctx[image] {\n        ctx.parameters[i as usize] = ty;\n    }\n}\n\nbitflags::bitflags! {\n    /// Influences the operation [`texture_args_generator`]\n    struct TextureArgsOptions: u32 {\n        /// Generates multisampled variants of images\n        const MULTI = 1 << 0;\n        /// Generates shadow variants of images\n        const SHADOW = 1 << 1;\n        /// Generates standard images\n        const STANDARD = 1 << 2;\n        /// Generates cube arrayed images\n        const CUBE_ARRAY = 1 << 3;\n        /// Generates cube arrayed images\n        const D2_MULTI_ARRAY = 1 << 4;\n    }\n}\n\nimpl From<BuiltinVariations> for TextureArgsOptions {\n    fn from(variations: BuiltinVariations) -> Self {\n        let mut options = TextureArgsOptions::empty();\n        if variations.contains(BuiltinVariations::STANDARD) {\n            options |= TextureArgsOptions::STANDARD\n        }\n        if variations.contains(BuiltinVariations::CUBE_TEXTURES_ARRAY) {\n            options |= TextureArgsOptions::CUBE_ARRAY\n        }\n        if variations.contains(BuiltinVariations::D2_MULTI_TEXTURES_ARRAY) {\n            options |= TextureArgsOptions::D2_MULTI_ARRAY\n        }\n        options\n    }\n}\n\n/// Helper function to generate the image components for texture/image builtins\n///\n/// Calls the passed function `f` with:\n/// ```text\n/// f(ScalarKind, ImageDimension, arrayed, multi, shadow)\n/// ```\n///\n/// `options` controls extra image variants generation like multisampling and depth,\n/// see the struct documentation\nfn texture_args_generator(\n    options: TextureArgsOptions,\n    mut f: impl FnMut(crate::ScalarKind, Dim, bool, bool, bool),\n) {\n    for kind in [Sk::Float, Sk::Uint, Sk::Sint].iter().copied() {\n        for dim in [Dim::D1, Dim::D2, Dim::D3, Dim::Cube].iter().copied() {\n            for arrayed in [false, true].iter().copied() {\n                if dim == Dim::Cube && arrayed {\n                    if !options.contains(TextureArgsOptions::CUBE_ARRAY) {\n                        continue;\n                    }\n                } else if Dim::D2 == dim\n                    && options.contains(TextureArgsOptions::MULTI)\n                    && arrayed\n                    && options.contains(TextureArgsOptions::D2_MULTI_ARRAY)\n                {\n                    // multisampling for sampler2DMSArray\n                    f(kind, dim, arrayed, true, false);\n                } else if !options.contains(TextureArgsOptions::STANDARD) {\n                    continue;\n                }\n\n                f(kind, dim, arrayed, false, false);\n\n                // 3D images can't be neither arrayed nor shadow\n                // so we break out early, this way arrayed will always\n                // be false and we won't hit the shadow branch\n                if let Dim::D3 = dim {\n                    break;\n                }\n\n                if Dim::D2 == dim && options.contains(TextureArgsOptions::MULTI) && !arrayed {\n                    // multisampling\n                    f(kind, dim, arrayed, true, false);\n                }\n\n                if Sk::Float == kind && options.contains(TextureArgsOptions::SHADOW) {\n                    // shadow\n                    f(kind, dim, arrayed, false, true);\n                }\n            }\n        }\n    }\n}\n\n/// Helper functions used to convert from a image dimension into a integer representing the\n/// number of components needed for the coordinates vector (1 means scalar instead of vector)\nconst fn image_dims_to_coords_size(dim: Dim) -> usize {\n    match dim {\n        Dim::D1 => 1,\n        Dim::D2 => 2,\n        _ => 3,\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/context.rs",
    "content": "use alloc::{format, string::String, vec::Vec};\nuse core::ops::Index;\n\nuse super::{\n    ast::{\n        GlobalLookup, GlobalLookupKind, HirExpr, HirExprKind, ParameterInfo, ParameterQualifier,\n        VariableReference,\n    },\n    error::{Error, ErrorKind},\n    types::{scalar_components, type_power},\n    Frontend, Result,\n};\nuse crate::{\n    front::Typifier, proc::Emitter, proc::Layouter, AddressSpace, Arena, BinaryOperator, Block,\n    Expression, FastHashMap, FunctionArgument, Handle, Literal, LocalVariable, RelationalFunction,\n    Scalar, Span, Statement, Type, TypeInner, VectorSize,\n};\n\n/// The position at which an expression is, used while lowering\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum ExprPos {\n    /// The expression is in the left hand side of an assignment\n    Lhs,\n    /// The expression is in the right hand side of an assignment\n    Rhs,\n    /// The expression is an array being indexed, needed to allow constant\n    /// arrays to be dynamically indexed\n    AccessBase {\n        /// The index is a constant\n        constant_index: bool,\n    },\n}\n\nimpl ExprPos {\n    /// Returns an lhs position if the current position is lhs otherwise AccessBase\n    const fn maybe_access_base(&self, constant_index: bool) -> Self {\n        match *self {\n            ExprPos::Lhs\n            | ExprPos::AccessBase {\n                constant_index: false,\n            } => *self,\n            _ => ExprPos::AccessBase { constant_index },\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct Context<'a> {\n    pub expressions: Arena<Expression>,\n    pub locals: Arena<LocalVariable>,\n\n    /// The [`FunctionArgument`]s for the final [`crate::Function`].\n    ///\n    /// Parameters with the `out` and `inout` qualifiers have [`Pointer`] types\n    /// here. For example, an `inout vec2 a` argument would be a [`Pointer`] to\n    /// a [`Vector`].\n    ///\n    /// [`Pointer`]: crate::TypeInner::Pointer\n    /// [`Vector`]: crate::TypeInner::Vector\n    pub arguments: Vec<FunctionArgument>,\n\n    /// The parameter types given in the source code.\n    ///\n    /// The `out` and `inout` qualifiers don't affect the types that appear\n    /// here. For example, an `inout vec2 a` argument would simply be a\n    /// [`Vector`], not a pointer to one.\n    ///\n    /// [`Vector`]: crate::TypeInner::Vector\n    pub parameters: Vec<Handle<Type>>,\n    pub parameters_info: Vec<ParameterInfo>,\n\n    pub symbol_table: crate::front::SymbolTable<String, VariableReference>,\n    pub samplers: FastHashMap<Handle<Expression>, Handle<Expression>>,\n\n    pub const_typifier: Typifier,\n    pub typifier: Typifier,\n    layouter: Layouter,\n    emitter: Emitter,\n    stmt_ctx: Option<StmtContext>,\n    pub body: Block,\n    pub module: &'a mut crate::Module,\n    pub is_const: bool,\n    /// Tracks the expression kind of `Expression`s residing in `self.expressions`\n    pub local_expression_kind_tracker: crate::proc::ExpressionKindTracker,\n    /// Tracks the expression kind of `Expression`s residing in `self.module.global_expressions`\n    pub global_expression_kind_tracker: &'a mut crate::proc::ExpressionKindTracker,\n}\n\nimpl<'a> Context<'a> {\n    pub fn new(\n        frontend: &Frontend,\n        module: &'a mut crate::Module,\n        is_const: bool,\n        global_expression_kind_tracker: &'a mut crate::proc::ExpressionKindTracker,\n    ) -> Result<Self> {\n        let mut this = Context {\n            expressions: Arena::new(),\n            locals: Arena::new(),\n            arguments: Vec::new(),\n\n            parameters: Vec::new(),\n            parameters_info: Vec::new(),\n\n            symbol_table: crate::front::SymbolTable::default(),\n            samplers: FastHashMap::default(),\n\n            const_typifier: Typifier::new(),\n            typifier: Typifier::new(),\n            layouter: Layouter::default(),\n            emitter: Emitter::default(),\n            stmt_ctx: Some(StmtContext::new()),\n            body: Block::new(),\n            module,\n            is_const: false,\n            local_expression_kind_tracker: crate::proc::ExpressionKindTracker::new(),\n            global_expression_kind_tracker,\n        };\n\n        this.emit_start();\n\n        for &(ref name, lookup) in frontend.global_variables.iter() {\n            this.add_global(name, lookup)?\n        }\n        this.is_const = is_const;\n\n        Ok(this)\n    }\n\n    pub fn new_body<F>(&mut self, cb: F) -> Result<Block>\n    where\n        F: FnOnce(&mut Self) -> Result<()>,\n    {\n        self.new_body_with_ret(cb).map(|(b, _)| b)\n    }\n\n    pub fn new_body_with_ret<F, R>(&mut self, cb: F) -> Result<(Block, R)>\n    where\n        F: FnOnce(&mut Self) -> Result<R>,\n    {\n        self.emit_restart();\n        let old_body = core::mem::replace(&mut self.body, Block::new());\n        let res = cb(self);\n        self.emit_restart();\n        let new_body = core::mem::replace(&mut self.body, old_body);\n        res.map(|r| (new_body, r))\n    }\n\n    pub fn with_body<F>(&mut self, body: Block, cb: F) -> Result<Block>\n    where\n        F: FnOnce(&mut Self) -> Result<()>,\n    {\n        self.emit_restart();\n        let old_body = core::mem::replace(&mut self.body, body);\n        let res = cb(self);\n        self.emit_restart();\n        let body = core::mem::replace(&mut self.body, old_body);\n        res.map(|_| body)\n    }\n\n    pub fn add_global(\n        &mut self,\n        name: &str,\n        GlobalLookup {\n            kind,\n            entry_arg,\n            mutable,\n        }: GlobalLookup,\n    ) -> Result<()> {\n        let (expr, load, constant) = match kind {\n            GlobalLookupKind::Variable(v) => {\n                let span = self.module.global_variables.get_span(v);\n                (\n                    self.add_expression(Expression::GlobalVariable(v), span)?,\n                    self.module.global_variables[v].space != AddressSpace::Handle,\n                    None,\n                )\n            }\n            GlobalLookupKind::BlockSelect(handle, index) => {\n                let span = self.module.global_variables.get_span(handle);\n                let base = self.add_expression(Expression::GlobalVariable(handle), span)?;\n                let expr = self.add_expression(Expression::AccessIndex { base, index }, span)?;\n\n                (\n                    expr,\n                    {\n                        let ty = self.module.global_variables[handle].ty;\n\n                        match self.module.types[ty].inner {\n                            TypeInner::Struct { ref members, .. } => {\n                                if let TypeInner::Array {\n                                    size: crate::ArraySize::Dynamic,\n                                    ..\n                                } = self.module.types[members[index as usize].ty].inner\n                                {\n                                    false\n                                } else {\n                                    true\n                                }\n                            }\n                            _ => true,\n                        }\n                    },\n                    None,\n                )\n            }\n            GlobalLookupKind::Constant(v, ty) => {\n                let span = self.module.constants.get_span(v);\n                (\n                    self.add_expression(Expression::Constant(v), span)?,\n                    false,\n                    Some((v, ty)),\n                )\n            }\n            GlobalLookupKind::Override(v, _ty) => {\n                let span = self.module.overrides.get_span(v);\n                (\n                    self.add_expression(Expression::Override(v), span)?,\n                    false,\n                    None,\n                )\n            }\n        };\n\n        let var = VariableReference {\n            expr,\n            load,\n            mutable,\n            constant,\n            entry_arg,\n        };\n\n        self.symbol_table.add(name.into(), var);\n\n        Ok(())\n    }\n\n    /// Starts the expression emitter\n    ///\n    /// # Panics\n    ///\n    /// - If called twice in a row without calling [`emit_end`][Self::emit_end].\n    #[inline]\n    pub fn emit_start(&mut self) {\n        self.emitter.start(&self.expressions)\n    }\n\n    /// Emits all the expressions captured by the emitter to the current body\n    ///\n    /// # Panics\n    ///\n    /// - If called before calling [`emit_start`].\n    /// - If called twice in a row without calling [`emit_start`].\n    ///\n    /// [`emit_start`]: Self::emit_start\n    pub fn emit_end(&mut self) {\n        self.body.extend(self.emitter.finish(&self.expressions))\n    }\n\n    /// Emits all the expressions captured by the emitter to the current body\n    /// and starts the emitter again\n    ///\n    /// # Panics\n    ///\n    /// - If called before calling [`emit_start`][Self::emit_start].\n    pub fn emit_restart(&mut self) {\n        self.emit_end();\n        self.emit_start()\n    }\n\n    pub fn add_expression(&mut self, expr: Expression, meta: Span) -> Result<Handle<Expression>> {\n        let mut eval = if self.is_const {\n            crate::proc::ConstantEvaluator::for_glsl_module(\n                self.module,\n                self.global_expression_kind_tracker,\n                &mut self.layouter,\n            )\n        } else {\n            crate::proc::ConstantEvaluator::for_glsl_function(\n                self.module,\n                &mut self.expressions,\n                &mut self.local_expression_kind_tracker,\n                &mut self.layouter,\n                &mut self.emitter,\n                &mut self.body,\n            )\n        };\n\n        eval.try_eval_and_append(expr, meta).map_err(|e| Error {\n            kind: e.into(),\n            meta,\n        })\n    }\n\n    /// Add variable to current scope\n    ///\n    /// Returns a variable if a variable with the same name was already defined,\n    /// otherwise returns `None`\n    pub fn add_local_var(\n        &mut self,\n        name: String,\n        expr: Handle<Expression>,\n        mutable: bool,\n    ) -> Option<VariableReference> {\n        let var = VariableReference {\n            expr,\n            load: true,\n            mutable,\n            constant: None,\n            entry_arg: None,\n        };\n\n        self.symbol_table.add(name, var)\n    }\n\n    /// Add function argument to current scope\n    pub fn add_function_arg(\n        &mut self,\n        name_meta: Option<(String, Span)>,\n        ty: Handle<Type>,\n        qualifier: ParameterQualifier,\n    ) -> Result<()> {\n        let index = self.arguments.len();\n        let mut arg = FunctionArgument {\n            name: name_meta.as_ref().map(|&(ref name, _)| name.clone()),\n            ty,\n            binding: None,\n        };\n        self.parameters.push(ty);\n\n        let opaque = match self.module.types[ty].inner {\n            TypeInner::Image { .. } | TypeInner::Sampler { .. } => true,\n            _ => false,\n        };\n\n        if qualifier.is_lhs() {\n            let span = self.module.types.get_span(arg.ty);\n            arg.ty = self.module.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Pointer {\n                        base: arg.ty,\n                        space: AddressSpace::Function,\n                    },\n                },\n                span,\n            )\n        }\n\n        self.arguments.push(arg);\n\n        self.parameters_info.push(ParameterInfo {\n            qualifier,\n            depth: false,\n        });\n\n        if let Some((name, meta)) = name_meta {\n            let expr = self.add_expression(Expression::FunctionArgument(index as u32), meta)?;\n            let mutable = qualifier != ParameterQualifier::Const && !opaque;\n            let load = qualifier.is_lhs();\n\n            let var = if mutable && !load {\n                let handle = self.locals.append(\n                    LocalVariable {\n                        name: Some(name.clone()),\n                        ty,\n                        init: None,\n                    },\n                    meta,\n                );\n                let local_expr = self.add_expression(Expression::LocalVariable(handle), meta)?;\n\n                self.emit_restart();\n\n                self.body.push(\n                    Statement::Store {\n                        pointer: local_expr,\n                        value: expr,\n                    },\n                    meta,\n                );\n\n                VariableReference {\n                    expr: local_expr,\n                    load: true,\n                    mutable,\n                    constant: None,\n                    entry_arg: None,\n                }\n            } else {\n                VariableReference {\n                    expr,\n                    load,\n                    mutable,\n                    constant: None,\n                    entry_arg: None,\n                }\n            };\n\n            self.symbol_table.add(name, var);\n        }\n\n        Ok(())\n    }\n\n    /// Returns a [`StmtContext`] to be used in parsing and lowering\n    ///\n    /// # Panics\n    ///\n    /// - If more than one [`StmtContext`] are active at the same time or if the\n    ///   previous call didn't use it in lowering.\n    #[must_use]\n    pub const fn stmt_ctx(&mut self) -> StmtContext {\n        self.stmt_ctx.take().unwrap()\n    }\n\n    /// Lowers a [`HirExpr`] which might produce a [`Expression`].\n    ///\n    /// consumes a [`StmtContext`] returning it to the context so that it can be\n    /// used again later.\n    pub fn lower(\n        &mut self,\n        mut stmt: StmtContext,\n        frontend: &mut Frontend,\n        expr: Handle<HirExpr>,\n        pos: ExprPos,\n    ) -> Result<(Option<Handle<Expression>>, Span)> {\n        let res = self.lower_inner(&stmt, frontend, expr, pos);\n\n        stmt.hir_exprs.clear();\n        self.stmt_ctx = Some(stmt);\n\n        res\n    }\n\n    /// Similar to [`lower`](Self::lower) but returns an error if the expression\n    /// returns void (ie. doesn't produce a [`Expression`]).\n    ///\n    /// consumes a [`StmtContext`] returning it to the context so that it can be\n    /// used again later.\n    pub fn lower_expect(\n        &mut self,\n        mut stmt: StmtContext,\n        frontend: &mut Frontend,\n        expr: Handle<HirExpr>,\n        pos: ExprPos,\n    ) -> Result<(Handle<Expression>, Span)> {\n        let res = self.lower_expect_inner(&stmt, frontend, expr, pos);\n\n        stmt.hir_exprs.clear();\n        self.stmt_ctx = Some(stmt);\n\n        res\n    }\n\n    /// internal implementation of [`lower_expect`](Self::lower_expect)\n    ///\n    /// this method is only public because it's used in\n    /// [`function_call`](Frontend::function_call), unless you know what\n    /// you're doing use [`lower_expect`](Self::lower_expect)\n    pub fn lower_expect_inner(\n        &mut self,\n        stmt: &StmtContext,\n        frontend: &mut Frontend,\n        expr: Handle<HirExpr>,\n        pos: ExprPos,\n    ) -> Result<(Handle<Expression>, Span)> {\n        let (maybe_expr, meta) = self.lower_inner(stmt, frontend, expr, pos)?;\n\n        let expr = match maybe_expr {\n            Some(e) => e,\n            None => {\n                return Err(Error {\n                    kind: ErrorKind::SemanticError(\"Expression returns void\".into()),\n                    meta,\n                })\n            }\n        };\n\n        Ok((expr, meta))\n    }\n\n    fn lower_store(\n        &mut self,\n        pointer: Handle<Expression>,\n        value: Handle<Expression>,\n        meta: Span,\n    ) -> Result<()> {\n        if let Expression::Swizzle {\n            size,\n            mut vector,\n            pattern,\n        } = self.expressions[pointer]\n        {\n            // Stores to swizzled values are not directly supported,\n            // lower them as series of per-component stores.\n            let size = match size {\n                VectorSize::Bi => 2,\n                VectorSize::Tri => 3,\n                VectorSize::Quad => 4,\n            };\n\n            if let Expression::Load { pointer } = self.expressions[vector] {\n                vector = pointer;\n            }\n\n            #[allow(clippy::needless_range_loop)]\n            for index in 0..size {\n                let dst = self.add_expression(\n                    Expression::AccessIndex {\n                        base: vector,\n                        index: pattern[index].index(),\n                    },\n                    meta,\n                )?;\n                let src = self.add_expression(\n                    Expression::AccessIndex {\n                        base: value,\n                        index: index as u32,\n                    },\n                    meta,\n                )?;\n\n                self.emit_restart();\n\n                self.body.push(\n                    Statement::Store {\n                        pointer: dst,\n                        value: src,\n                    },\n                    meta,\n                );\n            }\n        } else {\n            self.emit_restart();\n\n            self.body.push(Statement::Store { pointer, value }, meta);\n        }\n\n        Ok(())\n    }\n\n    /// Internal implementation of [`lower`](Self::lower)\n    fn lower_inner(\n        &mut self,\n        stmt: &StmtContext,\n        frontend: &mut Frontend,\n        expr: Handle<HirExpr>,\n        pos: ExprPos,\n    ) -> Result<(Option<Handle<Expression>>, Span)> {\n        let HirExpr { ref kind, meta } = stmt.hir_exprs[expr];\n\n        log::debug!(\"Lowering {expr:?} (kind {kind:?}, pos {pos:?})\");\n\n        let handle = match *kind {\n            HirExprKind::Access { base, index } => {\n                let (index, _) = self.lower_expect_inner(stmt, frontend, index, ExprPos::Rhs)?;\n                let maybe_constant_index = match pos {\n                    // Don't try to generate `AccessIndex` if in a LHS position, since it\n                    // wouldn't produce a pointer.\n                    ExprPos::Lhs => None,\n                    _ => self\n                        .module\n                        .to_ctx()\n                        .get_const_val_from(index, &self.expressions)\n                        .ok(),\n                };\n\n                let base = self\n                    .lower_expect_inner(\n                        stmt,\n                        frontend,\n                        base,\n                        pos.maybe_access_base(maybe_constant_index.is_some()),\n                    )?\n                    .0;\n\n                let pointer = maybe_constant_index\n                    .map(|index| self.add_expression(Expression::AccessIndex { base, index }, meta))\n                    .unwrap_or_else(|| {\n                        self.add_expression(Expression::Access { base, index }, meta)\n                    })?;\n\n                if ExprPos::Rhs == pos {\n                    let resolved = self.resolve_type(pointer, meta)?;\n                    if resolved.pointer_space().is_some() {\n                        return Ok((\n                            Some(self.add_expression(Expression::Load { pointer }, meta)?),\n                            meta,\n                        ));\n                    }\n                }\n\n                pointer\n            }\n            HirExprKind::Select { base, ref field } => {\n                let base = self.lower_expect_inner(stmt, frontend, base, pos)?.0;\n\n                frontend.field_selection(self, pos, base, field, meta)?\n            }\n            HirExprKind::Literal(literal) if pos != ExprPos::Lhs => {\n                self.add_expression(Expression::Literal(literal), meta)?\n            }\n            HirExprKind::Binary { left, op, right } if pos != ExprPos::Lhs => {\n                let (mut left, left_meta) =\n                    self.lower_expect_inner(stmt, frontend, left, ExprPos::Rhs)?;\n                let (mut right, right_meta) =\n                    self.lower_expect_inner(stmt, frontend, right, ExprPos::Rhs)?;\n\n                match op {\n                    BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => {\n                        self.implicit_conversion(&mut right, right_meta, Scalar::U32)?\n                    }\n                    _ => self\n                        .binary_implicit_conversion(&mut left, left_meta, &mut right, right_meta)?,\n                }\n\n                self.typifier_grow(left, left_meta)?;\n                self.typifier_grow(right, right_meta)?;\n\n                let left_inner = self.get_type(left);\n                let right_inner = self.get_type(right);\n\n                match (left_inner, right_inner) {\n                    (\n                        &TypeInner::Matrix {\n                            columns: left_columns,\n                            rows: left_rows,\n                            scalar: left_scalar,\n                        },\n                        &TypeInner::Matrix {\n                            columns: right_columns,\n                            rows: right_rows,\n                            scalar: right_scalar,\n                        },\n                    ) => {\n                        let dimensions_ok = if op == BinaryOperator::Multiply {\n                            left_columns == right_rows\n                        } else {\n                            left_columns == right_columns && left_rows == right_rows\n                        };\n\n                        // Check that the two arguments have the same dimensions\n                        if !dimensions_ok || left_scalar != right_scalar {\n                            frontend.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\n                                        \"Cannot apply operation to {left_inner:?} and {right_inner:?}\"\n                                    )\n                                    .into(),\n                                ),\n                                meta,\n                            })\n                        }\n\n                        match op {\n                            BinaryOperator::Divide => {\n                                // Naga IR doesn't support matrix division so we need to\n                                // divide the columns individually and reassemble the matrix\n                                let mut components = Vec::with_capacity(left_columns as usize);\n\n                                for index in 0..left_columns as u32 {\n                                    // Get the column vectors\n                                    let left_vector = self.add_expression(\n                                        Expression::AccessIndex { base: left, index },\n                                        meta,\n                                    )?;\n                                    let right_vector = self.add_expression(\n                                        Expression::AccessIndex { base: right, index },\n                                        meta,\n                                    )?;\n\n                                    // Divide the vectors\n                                    let column = self.add_expression(\n                                        Expression::Binary {\n                                            op,\n                                            left: left_vector,\n                                            right: right_vector,\n                                        },\n                                        meta,\n                                    )?;\n\n                                    components.push(column)\n                                }\n\n                                let ty = self.module.types.insert(\n                                    Type {\n                                        name: None,\n                                        inner: TypeInner::Matrix {\n                                            columns: left_columns,\n                                            rows: left_rows,\n                                            scalar: left_scalar,\n                                        },\n                                    },\n                                    Span::default(),\n                                );\n\n                                // Rebuild the matrix from the divided vectors\n                                self.add_expression(Expression::Compose { ty, components }, meta)?\n                            }\n                            BinaryOperator::Equal | BinaryOperator::NotEqual => {\n                                // Naga IR doesn't support matrix comparisons so we need to\n                                // compare the columns individually and then fold them together\n                                //\n                                // The folding is done using a logical and for equality and\n                                // a logical or for inequality\n                                let equals = op == BinaryOperator::Equal;\n\n                                let (op, combine, fun) = match equals {\n                                    true => (\n                                        BinaryOperator::Equal,\n                                        BinaryOperator::LogicalAnd,\n                                        RelationalFunction::All,\n                                    ),\n                                    false => (\n                                        BinaryOperator::NotEqual,\n                                        BinaryOperator::LogicalOr,\n                                        RelationalFunction::Any,\n                                    ),\n                                };\n\n                                let mut root = None;\n\n                                for index in 0..left_columns as u32 {\n                                    // Get the column vectors\n                                    let left_vector = self.add_expression(\n                                        Expression::AccessIndex { base: left, index },\n                                        meta,\n                                    )?;\n                                    let right_vector = self.add_expression(\n                                        Expression::AccessIndex { base: right, index },\n                                        meta,\n                                    )?;\n\n                                    let argument = self.add_expression(\n                                        Expression::Binary {\n                                            op,\n                                            left: left_vector,\n                                            right: right_vector,\n                                        },\n                                        meta,\n                                    )?;\n\n                                    // The result of comparing two vectors is a boolean vector\n                                    // so use a relational function like all to get a single\n                                    // boolean value\n                                    let compare = self.add_expression(\n                                        Expression::Relational { fun, argument },\n                                        meta,\n                                    )?;\n\n                                    // Fold the result\n                                    root = Some(match root {\n                                        Some(right) => self.add_expression(\n                                            Expression::Binary {\n                                                op: combine,\n                                                left: compare,\n                                                right,\n                                            },\n                                            meta,\n                                        )?,\n                                        None => compare,\n                                    });\n                                }\n\n                                root.unwrap()\n                            }\n                            _ => {\n                                self.add_expression(Expression::Binary { left, op, right }, meta)?\n                            }\n                        }\n                    }\n                    (&TypeInner::Vector { .. }, &TypeInner::Vector { .. }) => match op {\n                        BinaryOperator::Equal | BinaryOperator::NotEqual => {\n                            let equals = op == BinaryOperator::Equal;\n\n                            let (op, fun) = match equals {\n                                true => (BinaryOperator::Equal, RelationalFunction::All),\n                                false => (BinaryOperator::NotEqual, RelationalFunction::Any),\n                            };\n\n                            let argument =\n                                self.add_expression(Expression::Binary { op, left, right }, meta)?;\n\n                            self.add_expression(Expression::Relational { fun, argument }, meta)?\n                        }\n                        _ => self.add_expression(Expression::Binary { left, op, right }, meta)?,\n                    },\n                    (&TypeInner::Vector { size, .. }, &TypeInner::Scalar { .. }) => match op {\n                        BinaryOperator::Add\n                        | BinaryOperator::Subtract\n                        | BinaryOperator::Divide\n                        | BinaryOperator::And\n                        | BinaryOperator::ExclusiveOr\n                        | BinaryOperator::InclusiveOr\n                        | BinaryOperator::ShiftLeft\n                        | BinaryOperator::ShiftRight => {\n                            let scalar_vector = self\n                                .add_expression(Expression::Splat { size, value: right }, meta)?;\n\n                            self.add_expression(\n                                Expression::Binary {\n                                    op,\n                                    left,\n                                    right: scalar_vector,\n                                },\n                                meta,\n                            )?\n                        }\n                        _ => self.add_expression(Expression::Binary { left, op, right }, meta)?,\n                    },\n                    (&TypeInner::Scalar { .. }, &TypeInner::Vector { size, .. }) => match op {\n                        BinaryOperator::Add\n                        | BinaryOperator::Subtract\n                        | BinaryOperator::Divide\n                        | BinaryOperator::And\n                        | BinaryOperator::ExclusiveOr\n                        | BinaryOperator::InclusiveOr => {\n                            let scalar_vector =\n                                self.add_expression(Expression::Splat { size, value: left }, meta)?;\n\n                            self.add_expression(\n                                Expression::Binary {\n                                    op,\n                                    left: scalar_vector,\n                                    right,\n                                },\n                                meta,\n                            )?\n                        }\n                        _ => self.add_expression(Expression::Binary { left, op, right }, meta)?,\n                    },\n                    (\n                        &TypeInner::Scalar(left_scalar),\n                        &TypeInner::Matrix {\n                            rows,\n                            columns,\n                            scalar: right_scalar,\n                        },\n                    ) => {\n                        // Check that the two arguments have the same scalar type\n                        if left_scalar != right_scalar {\n                            frontend.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\n                                        \"Cannot apply operation to {left_inner:?} and {right_inner:?}\"\n                                    )\n                                    .into(),\n                                ),\n                                meta,\n                            })\n                        }\n\n                        match op {\n                            BinaryOperator::Divide\n                            | BinaryOperator::Add\n                            | BinaryOperator::Subtract => {\n                                // Naga IR doesn't support all matrix by scalar operations so\n                                // we need for some to turn the scalar into a vector by\n                                // splatting it and then for each column vector apply the\n                                // operation and finally reconstruct the matrix\n                                let scalar_vector = self.add_expression(\n                                    Expression::Splat {\n                                        size: rows,\n                                        value: left,\n                                    },\n                                    meta,\n                                )?;\n\n                                let mut components = Vec::with_capacity(columns as usize);\n\n                                for index in 0..columns as u32 {\n                                    // Get the column vector\n                                    let matrix_column = self.add_expression(\n                                        Expression::AccessIndex { base: right, index },\n                                        meta,\n                                    )?;\n\n                                    // Apply the operation to the splatted vector and\n                                    // the column vector\n                                    let column = self.add_expression(\n                                        Expression::Binary {\n                                            op,\n                                            left: scalar_vector,\n                                            right: matrix_column,\n                                        },\n                                        meta,\n                                    )?;\n\n                                    components.push(column)\n                                }\n\n                                let ty = self.module.types.insert(\n                                    Type {\n                                        name: None,\n                                        inner: TypeInner::Matrix {\n                                            columns,\n                                            rows,\n                                            scalar: left_scalar,\n                                        },\n                                    },\n                                    Span::default(),\n                                );\n\n                                // Rebuild the matrix from the operation result vectors\n                                self.add_expression(Expression::Compose { ty, components }, meta)?\n                            }\n                            _ => {\n                                self.add_expression(Expression::Binary { left, op, right }, meta)?\n                            }\n                        }\n                    }\n                    (\n                        &TypeInner::Matrix {\n                            rows,\n                            columns,\n                            scalar: left_scalar,\n                        },\n                        &TypeInner::Scalar(right_scalar),\n                    ) => {\n                        // Check that the two arguments have the same scalar type\n                        if left_scalar != right_scalar {\n                            frontend.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\n                                        \"Cannot apply operation to {left_inner:?} and {right_inner:?}\"\n                                    )\n                                    .into(),\n                                ),\n                                meta,\n                            })\n                        }\n\n                        match op {\n                            BinaryOperator::Divide\n                            | BinaryOperator::Add\n                            | BinaryOperator::Subtract => {\n                                // Naga IR doesn't support all matrix by scalar operations so\n                                // we need for some to turn the scalar into a vector by\n                                // splatting it and then for each column vector apply the\n                                // operation and finally reconstruct the matrix\n\n                                let scalar_vector = self.add_expression(\n                                    Expression::Splat {\n                                        size: rows,\n                                        value: right,\n                                    },\n                                    meta,\n                                )?;\n\n                                let mut components = Vec::with_capacity(columns as usize);\n\n                                for index in 0..columns as u32 {\n                                    // Get the column vector\n                                    let matrix_column = self.add_expression(\n                                        Expression::AccessIndex { base: left, index },\n                                        meta,\n                                    )?;\n\n                                    // Apply the operation to the splatted vector and\n                                    // the column vector\n                                    let column = self.add_expression(\n                                        Expression::Binary {\n                                            op,\n                                            left: matrix_column,\n                                            right: scalar_vector,\n                                        },\n                                        meta,\n                                    )?;\n\n                                    components.push(column)\n                                }\n\n                                let ty = self.module.types.insert(\n                                    Type {\n                                        name: None,\n                                        inner: TypeInner::Matrix {\n                                            columns,\n                                            rows,\n                                            scalar: left_scalar,\n                                        },\n                                    },\n                                    Span::default(),\n                                );\n\n                                // Rebuild the matrix from the operation result vectors\n                                self.add_expression(Expression::Compose { ty, components }, meta)?\n                            }\n                            _ => {\n                                self.add_expression(Expression::Binary { left, op, right }, meta)?\n                            }\n                        }\n                    }\n                    _ => self.add_expression(Expression::Binary { left, op, right }, meta)?,\n                }\n            }\n            HirExprKind::Unary { op, expr } if pos != ExprPos::Lhs => {\n                let expr = self\n                    .lower_expect_inner(stmt, frontend, expr, ExprPos::Rhs)?\n                    .0;\n\n                if let TypeInner::Matrix { scalar, .. } = *self.resolve_type(expr, meta)? {\n                    // Naga IR doesn't support matrix negation, so we need to turn it into\n                    // multiplication by scalar -1.\n                    let minus_one = Literal::minus_one(scalar).ok_or_else(|| Error {\n                        kind: ErrorKind::SemanticError(\n                            format!(\"Cannot apply operator {op:?} to type {scalar:?}\").into(),\n                        ),\n                        meta,\n                    })?;\n                    let lhs = self.add_expression(Expression::Literal(minus_one), meta)?;\n                    self.add_expression(\n                        Expression::Binary {\n                            op: BinaryOperator::Multiply,\n                            left: lhs,\n                            right: expr,\n                        },\n                        meta,\n                    )?\n                } else {\n                    self.add_expression(Expression::Unary { op, expr }, meta)?\n                }\n            }\n            HirExprKind::Variable(ref var) => match pos {\n                ExprPos::Lhs => {\n                    if !var.mutable {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Variable cannot be used in LHS position\".into(),\n                            ),\n                            meta,\n                        })\n                    }\n\n                    var.expr\n                }\n                ExprPos::AccessBase { constant_index } => {\n                    // If the index isn't constant all accesses backed by a constant base need\n                    // to be done through a proxy local variable, since constants have a non\n                    // pointer type which is required for dynamic indexing\n                    if !constant_index {\n                        if let Some((constant, ty)) = var.constant {\n                            let init = self\n                                .add_expression(Expression::Constant(constant), Span::default())?;\n                            let local = self.locals.append(\n                                LocalVariable {\n                                    name: None,\n                                    ty,\n                                    init: Some(init),\n                                },\n                                Span::default(),\n                            );\n\n                            self.add_expression(Expression::LocalVariable(local), Span::default())?\n                        } else {\n                            var.expr\n                        }\n                    } else {\n                        var.expr\n                    }\n                }\n                _ if var.load => {\n                    self.add_expression(Expression::Load { pointer: var.expr }, meta)?\n                }\n                ExprPos::Rhs => {\n                    if let Some((constant, _)) = self.is_const.then_some(var.constant).flatten() {\n                        self.add_expression(Expression::Constant(constant), meta)?\n                    } else {\n                        // Check if this is an Override expression in const context\n                        if self.is_const {\n                            if let Expression::Override(o) = self.expressions[var.expr] {\n                                // Need to add the Override expression to the global arena\n                                self.add_expression(Expression::Override(o), meta)?\n                            } else {\n                                var.expr\n                            }\n                        } else {\n                            var.expr\n                        }\n                    }\n                }\n            },\n            HirExprKind::Call(ref call) if pos != ExprPos::Lhs => {\n                let maybe_expr = frontend.function_or_constructor_call(\n                    self,\n                    stmt,\n                    call.kind.clone(),\n                    &call.args,\n                    meta,\n                )?;\n                return Ok((maybe_expr, meta));\n            }\n            // `HirExprKind::Conditional` represents the ternary operator in glsl (`:?`)\n            //\n            // The ternary operator is defined to only evaluate one of the two possible\n            // expressions which means that it's behavior is that of an `if` statement,\n            // and it's merely syntactic sugar for it.\n            HirExprKind::Conditional {\n                condition,\n                accept,\n                reject,\n            } if ExprPos::Lhs != pos => {\n                // Given an expression `a ? b : c`, we need to produce a Naga\n                // statement roughly like:\n                //\n                //     var temp;\n                //     if a {\n                //         temp = convert(b);\n                //     } else  {\n                //         temp = convert(c);\n                //     }\n                //\n                // where `convert` stands for type conversions to bring `b` and `c` to\n                // the same type, and then use `temp` to represent the value of the whole\n                // conditional expression in subsequent code.\n\n                // Lower the condition first to the current bodyy\n                let condition = self\n                    .lower_expect_inner(stmt, frontend, condition, ExprPos::Rhs)?\n                    .0;\n\n                let (mut accept_body, (mut accept, accept_meta)) =\n                    self.new_body_with_ret(|ctx| {\n                        // Lower the `true` branch\n                        ctx.lower_expect_inner(stmt, frontend, accept, pos)\n                    })?;\n\n                let (mut reject_body, (mut reject, reject_meta)) =\n                    self.new_body_with_ret(|ctx| {\n                        // Lower the `false` branch\n                        ctx.lower_expect_inner(stmt, frontend, reject, pos)\n                    })?;\n\n                // We need to do some custom implicit conversions since the two target expressions\n                // are in different bodies\n                if let (Some((accept_power, accept_scalar)), Some((reject_power, reject_scalar))) = (\n                    // Get the components of both branches and calculate the type power\n                    self.expr_scalar_components(accept, accept_meta)?\n                        .and_then(|scalar| Some((type_power(scalar)?, scalar))),\n                    self.expr_scalar_components(reject, reject_meta)?\n                        .and_then(|scalar| Some((type_power(scalar)?, scalar))),\n                ) {\n                    match accept_power.cmp(&reject_power) {\n                        core::cmp::Ordering::Less => {\n                            accept_body = self.with_body(accept_body, |ctx| {\n                                ctx.conversion(&mut accept, accept_meta, reject_scalar)?;\n                                Ok(())\n                            })?;\n                        }\n                        core::cmp::Ordering::Equal => {}\n                        core::cmp::Ordering::Greater => {\n                            reject_body = self.with_body(reject_body, |ctx| {\n                                ctx.conversion(&mut reject, reject_meta, accept_scalar)?;\n                                Ok(())\n                            })?;\n                        }\n                    }\n                }\n\n                // We need to get the type of the resulting expression to create the local,\n                // this must be done after implicit conversions to ensure both branches have\n                // the same type.\n                let ty = self.resolve_type_handle(accept, accept_meta)?;\n\n                // Add the local that will hold the result of our conditional\n                let local = self.locals.append(\n                    LocalVariable {\n                        name: None,\n                        ty,\n                        init: None,\n                    },\n                    meta,\n                );\n\n                let local_expr = self.add_expression(Expression::LocalVariable(local), meta)?;\n\n                // Add to each  the store to the result variable\n                accept_body.push(\n                    Statement::Store {\n                        pointer: local_expr,\n                        value: accept,\n                    },\n                    accept_meta,\n                );\n                reject_body.push(\n                    Statement::Store {\n                        pointer: local_expr,\n                        value: reject,\n                    },\n                    reject_meta,\n                );\n\n                // Finally add the `If` to the main body with the `condition` we lowered\n                // earlier and the branches we prepared.\n                self.body.push(\n                    Statement::If {\n                        condition,\n                        accept: accept_body,\n                        reject: reject_body,\n                    },\n                    meta,\n                );\n\n                // Note: `Expression::Load` must be emitted before it's used so make\n                // sure the emitter is active here.\n                self.add_expression(\n                    Expression::Load {\n                        pointer: local_expr,\n                    },\n                    meta,\n                )?\n            }\n            HirExprKind::Assign { tgt, value } if ExprPos::Lhs != pos => {\n                let (pointer, ptr_meta) =\n                    self.lower_expect_inner(stmt, frontend, tgt, ExprPos::Lhs)?;\n                let (mut value, value_meta) =\n                    self.lower_expect_inner(stmt, frontend, value, ExprPos::Rhs)?;\n\n                let ty = match *self.resolve_type(pointer, ptr_meta)? {\n                    TypeInner::Pointer { base, .. } => &self.module.types[base].inner,\n                    ref ty => ty,\n                };\n\n                if let Some(scalar) = scalar_components(ty) {\n                    self.implicit_conversion(&mut value, value_meta, scalar)?;\n                }\n\n                self.lower_store(pointer, value, meta)?;\n\n                value\n            }\n            HirExprKind::PrePostfix { op, postfix, expr } if ExprPos::Lhs != pos => {\n                let (pointer, _) = self.lower_expect_inner(stmt, frontend, expr, ExprPos::Lhs)?;\n                let left = if let Expression::Swizzle { .. } = self.expressions[pointer] {\n                    pointer\n                } else {\n                    self.add_expression(Expression::Load { pointer }, meta)?\n                };\n\n                let res = match *self.resolve_type(left, meta)? {\n                    TypeInner::Scalar(scalar) => {\n                        let ty = TypeInner::Scalar(scalar);\n                        Literal::one(scalar).map(|i| (ty, i, None, None))\n                    }\n                    TypeInner::Vector { size, scalar } => {\n                        let ty = TypeInner::Vector { size, scalar };\n                        Literal::one(scalar).map(|i| (ty, i, Some(size), None))\n                    }\n                    TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    } => {\n                        let ty = TypeInner::Matrix {\n                            columns,\n                            rows,\n                            scalar,\n                        };\n                        Literal::one(scalar).map(|i| (ty, i, Some(rows), Some(columns)))\n                    }\n                    _ => None,\n                };\n                let (ty_inner, literal, rows, columns) = match res {\n                    Some(res) => res,\n                    None => {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Increment/decrement only works on scalar/vector/matrix\".into(),\n                            ),\n                            meta,\n                        });\n                        return Ok((Some(left), meta));\n                    }\n                };\n\n                let mut right = self.add_expression(Expression::Literal(literal), meta)?;\n\n                // Glsl allows pre/postfixes operations on vectors and matrices, so if the\n                // target is either of them change the right side of the addition to be splatted\n                // to the same size as the target, furthermore if the target is a matrix\n                // use a composed matrix using the splatted value.\n                if let Some(size) = rows {\n                    right = self.add_expression(Expression::Splat { size, value: right }, meta)?;\n\n                    if let Some(cols) = columns {\n                        let ty = self.module.types.insert(\n                            Type {\n                                name: None,\n                                inner: ty_inner,\n                            },\n                            meta,\n                        );\n\n                        right = self.add_expression(\n                            Expression::Compose {\n                                ty,\n                                components: core::iter::repeat_n(right, cols as usize).collect(),\n                            },\n                            meta,\n                        )?;\n                    }\n                }\n\n                let value = self.add_expression(Expression::Binary { op, left, right }, meta)?;\n\n                self.lower_store(pointer, value, meta)?;\n\n                if postfix {\n                    left\n                } else {\n                    value\n                }\n            }\n            HirExprKind::Method {\n                expr: object,\n                ref name,\n                ref args,\n            } if ExprPos::Lhs != pos => {\n                let args = args\n                    .iter()\n                    .map(|e| self.lower_expect_inner(stmt, frontend, *e, ExprPos::Rhs))\n                    .collect::<Result<Vec<_>>>()?;\n                match name.as_ref() {\n                    \"length\" => {\n                        if !args.is_empty() {\n                            frontend.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    \".length() doesn't take any arguments\".into(),\n                                ),\n                                meta,\n                            });\n                        }\n                        let lowered_array = self.lower_expect_inner(stmt, frontend, object, pos)?.0;\n                        let array_type = self.resolve_type(lowered_array, meta)?;\n\n                        match *array_type {\n                            TypeInner::Array {\n                                size: crate::ArraySize::Constant(size),\n                                ..\n                            } => {\n                                let mut array_length = self.add_expression(\n                                    Expression::Literal(Literal::U32(size.get())),\n                                    meta,\n                                )?;\n                                self.forced_conversion(&mut array_length, meta, Scalar::I32)?;\n                                array_length\n                            }\n                            // let the error be handled in type checking if it's not a dynamic array\n                            _ => {\n                                let mut array_length = self\n                                    .add_expression(Expression::ArrayLength(lowered_array), meta)?;\n                                self.conversion(&mut array_length, meta, Scalar::I32)?;\n                                array_length\n                            }\n                        }\n                    }\n\n                    _ => {\n                        return Err(Error {\n                            kind: ErrorKind::SemanticError(\n                                format!(\"unknown method '{name}'\").into(),\n                            ),\n                            meta,\n                        });\n                    }\n                }\n            }\n            HirExprKind::Sequence { ref exprs } if pos != ExprPos::Lhs => {\n                let mut last_handle = None;\n                for expr in exprs.iter() {\n                    let (handle, _) =\n                        self.lower_expect_inner(stmt, frontend, *expr, ExprPos::Rhs)?;\n                    last_handle = Some(handle);\n                }\n                match last_handle {\n                    Some(handle) => handle,\n                    None => unreachable!(),\n                }\n            }\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::SemanticError(\n                        format!(\"{:?} cannot be in the left hand side\", stmt.hir_exprs[expr])\n                            .into(),\n                    ),\n                    meta,\n                })\n            }\n        };\n\n        log::trace!(\"Lowered {expr:?}\\n\\tKind = {kind:?}\\n\\tPos = {pos:?}\\n\\tResult = {handle:?}\");\n\n        Ok((Some(handle), meta))\n    }\n\n    pub fn expr_scalar_components(\n        &mut self,\n        expr: Handle<Expression>,\n        meta: Span,\n    ) -> Result<Option<Scalar>> {\n        let ty = self.resolve_type(expr, meta)?;\n        Ok(scalar_components(ty))\n    }\n\n    pub fn expr_power(&mut self, expr: Handle<Expression>, meta: Span) -> Result<Option<u32>> {\n        Ok(self\n            .expr_scalar_components(expr, meta)?\n            .and_then(type_power))\n    }\n\n    pub fn conversion(\n        &mut self,\n        expr: &mut Handle<Expression>,\n        meta: Span,\n        scalar: Scalar,\n    ) -> Result<()> {\n        *expr = self.add_expression(\n            Expression::As {\n                expr: *expr,\n                kind: scalar.kind,\n                convert: Some(scalar.width),\n            },\n            meta,\n        )?;\n\n        Ok(())\n    }\n\n    pub fn implicit_conversion(\n        &mut self,\n        expr: &mut Handle<Expression>,\n        meta: Span,\n        scalar: Scalar,\n    ) -> Result<()> {\n        if let (Some(tgt_power), Some(expr_power)) =\n            (type_power(scalar), self.expr_power(*expr, meta)?)\n        {\n            if tgt_power > expr_power {\n                self.conversion(expr, meta, scalar)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn forced_conversion(\n        &mut self,\n        expr: &mut Handle<Expression>,\n        meta: Span,\n        scalar: Scalar,\n    ) -> Result<()> {\n        if let Some(expr_scalar) = self.expr_scalar_components(*expr, meta)? {\n            if expr_scalar != scalar {\n                self.conversion(expr, meta, scalar)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn binary_implicit_conversion(\n        &mut self,\n        left: &mut Handle<Expression>,\n        left_meta: Span,\n        right: &mut Handle<Expression>,\n        right_meta: Span,\n    ) -> Result<()> {\n        let left_components = self.expr_scalar_components(*left, left_meta)?;\n        let right_components = self.expr_scalar_components(*right, right_meta)?;\n\n        if let (Some((left_power, left_scalar)), Some((right_power, right_scalar))) = (\n            left_components.and_then(|scalar| Some((type_power(scalar)?, scalar))),\n            right_components.and_then(|scalar| Some((type_power(scalar)?, scalar))),\n        ) {\n            match left_power.cmp(&right_power) {\n                core::cmp::Ordering::Less => {\n                    self.conversion(left, left_meta, right_scalar)?;\n                }\n                core::cmp::Ordering::Equal => {}\n                core::cmp::Ordering::Greater => {\n                    self.conversion(right, right_meta, left_scalar)?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn implicit_splat(\n        &mut self,\n        expr: &mut Handle<Expression>,\n        meta: Span,\n        vector_size: Option<VectorSize>,\n    ) -> Result<()> {\n        let expr_type = self.resolve_type(*expr, meta)?;\n\n        if let (&TypeInner::Scalar { .. }, Some(size)) = (expr_type, vector_size) {\n            *expr = self.add_expression(Expression::Splat { size, value: *expr }, meta)?\n        }\n\n        Ok(())\n    }\n\n    pub fn vector_resize(\n        &mut self,\n        size: VectorSize,\n        vector: Handle<Expression>,\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        self.add_expression(\n            Expression::Swizzle {\n                size,\n                vector,\n                pattern: crate::SwizzleComponent::XYZW,\n            },\n            meta,\n        )\n    }\n}\n\nimpl Index<Handle<Expression>> for Context<'_> {\n    type Output = Expression;\n\n    fn index(&self, index: Handle<Expression>) -> &Self::Output {\n        if self.is_const {\n            &self.module.global_expressions[index]\n        } else {\n            &self.expressions[index]\n        }\n    }\n}\n\n/// Helper struct passed when parsing expressions\n///\n/// This struct should only be obtained through [`stmt_ctx`](Context::stmt_ctx)\n/// and only one of these may be active at any time per context.\n#[derive(Debug)]\npub struct StmtContext {\n    /// A arena of high level expressions which can be lowered through a\n    /// [`Context`] to Naga's [`Expression`]s\n    pub hir_exprs: Arena<HirExpr>,\n}\n\nimpl StmtContext {\n    const fn new() -> Self {\n        StmtContext {\n            hir_exprs: Arena::new(),\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/error.rs",
    "content": "use alloc::{\n    borrow::Cow,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\n\nuse codespan_reporting::diagnostic::{Diagnostic, Label};\nuse codespan_reporting::files::SimpleFile;\nuse codespan_reporting::term;\nuse pp_rs::token::PreprocessorError;\nuse thiserror::Error;\n\nuse super::token::TokenValue;\n#[cfg(feature = \"stderr\")]\nuse crate::error::ErrorWrite;\nuse crate::{error::replace_control_chars, SourceLocation};\nuse crate::{proc::ConstantEvaluatorError, Span};\n\nfn join_with_comma(list: &[ExpectedToken]) -> String {\n    let mut string = \"\".to_string();\n    for (i, val) in list.iter().enumerate() {\n        string.push_str(&val.to_string());\n        match i {\n            i if i == list.len() - 1 => {}\n            i if i == list.len() - 2 => string.push_str(\" or \"),\n            _ => string.push_str(\", \"),\n        }\n    }\n    string\n}\n\n/// One of the expected tokens returned in [`InvalidToken`](ErrorKind::InvalidToken).\n#[derive(Clone, Debug, PartialEq)]\npub enum ExpectedToken {\n    /// A specific token was expected.\n    Token(TokenValue),\n    /// A type was expected.\n    TypeName,\n    /// An identifier was expected.\n    Identifier,\n    /// An integer literal was expected.\n    IntLiteral,\n    /// A float literal was expected.\n    FloatLiteral,\n    /// A boolean literal was expected.\n    BoolLiteral,\n    /// The end of file was expected.\n    Eof,\n}\nimpl From<TokenValue> for ExpectedToken {\n    fn from(token: TokenValue) -> Self {\n        ExpectedToken::Token(token)\n    }\n}\nimpl core::fmt::Display for ExpectedToken {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match *self {\n            ExpectedToken::Token(ref token) => write!(f, \"{token:?}\"),\n            ExpectedToken::TypeName => write!(f, \"a type\"),\n            ExpectedToken::Identifier => write!(f, \"identifier\"),\n            ExpectedToken::IntLiteral => write!(f, \"integer literal\"),\n            ExpectedToken::FloatLiteral => write!(f, \"float literal\"),\n            ExpectedToken::BoolLiteral => write!(f, \"bool literal\"),\n            ExpectedToken::Eof => write!(f, \"end of file\"),\n        }\n    }\n}\n\n/// Information about the cause of an error.\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ErrorKind {\n    /// Whilst parsing as encountered an unexpected EOF.\n    #[error(\"Unexpected end of file\")]\n    EndOfFile,\n    /// The shader specified an unsupported or invalid profile.\n    #[error(\"Invalid profile: {0}\")]\n    InvalidProfile(String),\n    /// The shader requested an unsupported or invalid version.\n    #[error(\"Invalid version: {0}\")]\n    InvalidVersion(u64),\n    /// Whilst parsing an unexpected token was encountered.\n    ///\n    /// A list of expected tokens is also returned.\n    #[error(\"Expected {expected_tokens}, found {found_token:?}\", found_token = .0, expected_tokens = join_with_comma(.1))]\n    InvalidToken(TokenValue, Vec<ExpectedToken>),\n    /// A specific feature is not yet implemented.\n    ///\n    /// To help prioritize work please open an issue in the github issue tracker\n    /// if none exist already or react to the already existing one.\n    #[error(\"Not implemented: {0}\")]\n    NotImplemented(&'static str),\n    /// A reference to a variable that wasn't declared was used.\n    #[error(\"Unknown variable: {0}\")]\n    UnknownVariable(String),\n    /// A reference to a type that wasn't declared was used.\n    #[error(\"Unknown type: {0}\")]\n    UnknownType(String),\n    /// A reference to a non existent member of a type was made.\n    #[error(\"Unknown field: {0}\")]\n    UnknownField(String),\n    /// An unknown layout qualifier was used.\n    ///\n    /// If the qualifier does exist please open an issue in the github issue tracker\n    /// if none exist already or react to the already existing one to help\n    /// prioritize work.\n    #[error(\"Unknown layout qualifier: {0}\")]\n    UnknownLayoutQualifier(String),\n    /// Unsupported matrix of the form matCx2\n    ///\n    /// Our IR expects matrices of the form matCx2 to have a stride of 8 however\n    /// matrices in the std140 layout have a stride of at least 16.\n    #[error(\"unsupported matrix of the form matCx2 (in this case mat{columns}x2) in std140 block layout. See https://github.com/gfx-rs/wgpu/issues/4375\")]\n    UnsupportedMatrixWithTwoRowsInStd140 { columns: u8 },\n    /// Unsupported matrix of the form f16matCxR\n    ///\n    /// Our IR expects matrices of the form f16matCxR to have a stride of 4/8/8 depending on row-count,\n    /// however matrices in the std140 layout have a stride of at least 16.\n    #[error(\"unsupported matrix of the form f16matCxR (in this case f16mat{columns}x{rows}) in std140 block layout. See https://github.com/gfx-rs/wgpu/issues/4375\")]\n    UnsupportedF16MatrixInStd140 { columns: u8, rows: u8 },\n    /// A variable with the same name already exists in the current scope.\n    #[error(\"Variable already declared: {0}\")]\n    VariableAlreadyDeclared(String),\n    /// A semantic error was detected in the shader.\n    #[error(\"{0}\")]\n    SemanticError(Cow<'static, str>),\n    /// An error was returned by the preprocessor.\n    #[error(\"{0:?}\")]\n    PreprocessorError(PreprocessorError),\n    /// The parser entered an illegal state and exited\n    ///\n    /// This obviously is a bug and as such should be reported in the github issue tracker\n    #[error(\"Internal error: {0}\")]\n    InternalError(&'static str),\n}\n\nimpl From<ConstantEvaluatorError> for ErrorKind {\n    fn from(err: ConstantEvaluatorError) -> Self {\n        ErrorKind::SemanticError(err.to_string().into())\n    }\n}\n\n/// Error returned during shader parsing.\n#[derive(Clone, Debug, Error)]\n#[error(\"{kind}\")]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Error {\n    /// Holds the information about the error itself.\n    pub kind: ErrorKind,\n    /// Holds information about the range of the source code where the error happened.\n    pub meta: Span,\n}\n\nimpl Error {\n    /// Returns a [`SourceLocation`] for the error message.\n    pub fn location(&self, source: &str) -> Option<SourceLocation> {\n        Some(self.meta.location(source))\n    }\n}\n\n/// A collection of errors returned during shader parsing.\n#[derive(Clone, Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub struct ParseErrors {\n    pub errors: Vec<Error>,\n}\n\nimpl ParseErrors {\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {\n        self.emit_to_writer_with_path(writer, source, \"glsl\");\n    }\n\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {\n        let path = path.to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        for err in &self.errors {\n            let diagnostic = Self::make_diagnostic(err);\n            crate::error::emit_to_writer(writer, &config, &files, &diagnostic)\n                .expect(\"cannot write error\");\n        }\n    }\n\n    /// Emits a summary of the errors to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr(&self, source: &str) {\n        self.emit_to_stderr_with_path(source, \"glsl\")\n    }\n\n    /// Emits a summary of the errors to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr_with_path(&self, source: &str, path: &str) {\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);\n                self.emit_to_writer_with_path(&mut writer.lock(), source, path);\n            } else {\n                let writer = std::io::stderr();\n                self.emit_to_writer_with_path(&mut writer.lock(), source, path);\n            }\n        }\n    }\n\n    pub fn emit_to_string(&self, source: &str) -> String {\n        self.emit_to_string_with_path(source, \"glsl\")\n    }\n\n    pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String {\n        let path = path.to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        let mut writer = crate::error::DiagnosticBuffer::new();\n        for err in &self.errors {\n            let diagnostic = Self::make_diagnostic(err);\n            writer\n                .emit_to_self(&config, &files, &diagnostic)\n                .expect(\"cannot write error\");\n        }\n        writer.into_string()\n    }\n\n    fn make_diagnostic(err: &Error) -> Diagnostic<()> {\n        let mut diagnostic = Diagnostic::error().with_message(err.kind.to_string());\n        if let Some(range) = err.meta.to_range() {\n            diagnostic = diagnostic.with_labels(vec![Label::primary((), range)]);\n        }\n        diagnostic\n    }\n}\n\nimpl core::fmt::Display for ParseErrors {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        self.errors.iter().try_for_each(|e| write!(f, \"{e:?}\"))\n    }\n}\n\nimpl core::error::Error for ParseErrors {}\n\nimpl From<Vec<Error>> for ParseErrors {\n    fn from(errors: Vec<Error>) -> Self {\n        Self { errors }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/functions.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::iter;\n\nuse super::{\n    ast::*,\n    builtins::{inject_builtin, sampled_to_depth},\n    context::{Context, ExprPos, StmtContext},\n    error::{Error, ErrorKind},\n    types::scalar_components,\n    Frontend, Result,\n};\nuse crate::{\n    front::glsl::types::type_power, proc::ensure_block_returns, AddressSpace, Block, EntryPoint,\n    Expression, Function, FunctionArgument, FunctionResult, Handle, Literal, LocalVariable, Scalar,\n    ScalarKind, Span, Statement, StructMember, Type, TypeInner,\n};\n\n/// Struct detailing a store operation that must happen after a function call\nstruct ProxyWrite {\n    /// The store target\n    target: Handle<Expression>,\n    /// A pointer to read the value of the store\n    value: Handle<Expression>,\n    /// An optional conversion to be applied\n    convert: Option<Scalar>,\n}\n\nimpl Frontend {\n    pub(crate) fn function_or_constructor_call(\n        &mut self,\n        ctx: &mut Context,\n        stmt: &StmtContext,\n        fc: FunctionCallKind,\n        raw_args: &[Handle<HirExpr>],\n        meta: Span,\n    ) -> Result<Option<Handle<Expression>>> {\n        let args: Vec<_> = raw_args\n            .iter()\n            .map(|e| ctx.lower_expect_inner(stmt, self, *e, ExprPos::Rhs))\n            .collect::<Result<_>>()?;\n\n        match fc {\n            FunctionCallKind::TypeConstructor(ty) => {\n                if args.len() == 1 {\n                    self.constructor_single(ctx, ty, args[0], meta).map(Some)\n                } else {\n                    self.constructor_many(ctx, ty, args, meta).map(Some)\n                }\n            }\n            FunctionCallKind::Function(name) => {\n                self.function_call(ctx, stmt, name, args, raw_args, meta)\n            }\n        }\n    }\n\n    fn constructor_single(\n        &mut self,\n        ctx: &mut Context,\n        ty: Handle<Type>,\n        (mut value, expr_meta): (Handle<Expression>, Span),\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let expr_type = ctx.resolve_type(value, expr_meta)?;\n\n        let vector_size = match *expr_type {\n            TypeInner::Vector { size, .. } => Some(size),\n            _ => None,\n        };\n\n        let expr_is_bool = expr_type.scalar_kind() == Some(ScalarKind::Bool);\n\n        // Special case: if casting from a bool, we need to use Select and not As.\n        match ctx.module.types[ty].inner.scalar() {\n            Some(result_scalar) if expr_is_bool && result_scalar.kind != ScalarKind::Bool => {\n                let result_scalar = Scalar {\n                    width: 4,\n                    ..result_scalar\n                };\n                let l0 = Literal::zero(result_scalar).unwrap();\n                let l1 = Literal::one(result_scalar).unwrap();\n                let mut reject = ctx.add_expression(Expression::Literal(l0), expr_meta)?;\n                let mut accept = ctx.add_expression(Expression::Literal(l1), expr_meta)?;\n\n                ctx.implicit_splat(&mut reject, meta, vector_size)?;\n                ctx.implicit_splat(&mut accept, meta, vector_size)?;\n\n                let h = ctx.add_expression(\n                    Expression::Select {\n                        accept,\n                        reject,\n                        condition: value,\n                    },\n                    expr_meta,\n                )?;\n\n                return Ok(h);\n            }\n            _ => {}\n        }\n\n        Ok(match ctx.module.types[ty].inner {\n            TypeInner::Vector { size, scalar } if vector_size.is_none() => {\n                ctx.forced_conversion(&mut value, expr_meta, scalar)?;\n\n                if let TypeInner::Scalar { .. } = *ctx.resolve_type(value, expr_meta)? {\n                    ctx.add_expression(Expression::Splat { size, value }, meta)?\n                } else {\n                    self.vector_constructor(ctx, ty, size, scalar, &[(value, expr_meta)], meta)?\n                }\n            }\n            TypeInner::Scalar(scalar) => {\n                let mut expr = value;\n                if let TypeInner::Vector { .. } | TypeInner::Matrix { .. } =\n                    *ctx.resolve_type(value, expr_meta)?\n                {\n                    expr = ctx.add_expression(\n                        Expression::AccessIndex {\n                            base: expr,\n                            index: 0,\n                        },\n                        meta,\n                    )?;\n                }\n\n                if let TypeInner::Matrix { .. } = *ctx.resolve_type(value, expr_meta)? {\n                    expr = ctx.add_expression(\n                        Expression::AccessIndex {\n                            base: expr,\n                            index: 0,\n                        },\n                        meta,\n                    )?;\n                }\n\n                ctx.add_expression(\n                    Expression::As {\n                        kind: scalar.kind,\n                        expr,\n                        convert: Some(scalar.width),\n                    },\n                    meta,\n                )?\n            }\n            TypeInner::Vector { size, scalar } => {\n                if vector_size != Some(size) {\n                    value = ctx.vector_resize(size, value, expr_meta)?;\n                }\n\n                ctx.add_expression(\n                    Expression::As {\n                        kind: scalar.kind,\n                        expr: value,\n                        convert: Some(scalar.width),\n                    },\n                    meta,\n                )?\n            }\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => self.matrix_one_arg(ctx, ty, columns, rows, scalar, (value, expr_meta), meta)?,\n            TypeInner::Struct { ref members, .. } => {\n                let scalar_components = members\n                    .first()\n                    .and_then(|member| scalar_components(&ctx.module.types[member.ty].inner));\n                if let Some(scalar) = scalar_components {\n                    ctx.implicit_conversion(&mut value, expr_meta, scalar)?;\n                }\n\n                ctx.add_expression(\n                    Expression::Compose {\n                        ty,\n                        components: vec![value],\n                    },\n                    meta,\n                )?\n            }\n\n            TypeInner::Array { base, .. } => {\n                let scalar_components = scalar_components(&ctx.module.types[base].inner);\n                if let Some(scalar) = scalar_components {\n                    ctx.implicit_conversion(&mut value, expr_meta, scalar)?;\n                }\n\n                ctx.add_expression(\n                    Expression::Compose {\n                        ty,\n                        components: vec![value],\n                    },\n                    meta,\n                )?\n            }\n            _ => {\n                self.errors.push(Error {\n                    kind: ErrorKind::SemanticError(\"Bad type constructor\".into()),\n                    meta,\n                });\n\n                value\n            }\n        })\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn matrix_one_arg(\n        &mut self,\n        ctx: &mut Context,\n        ty: Handle<Type>,\n        columns: crate::VectorSize,\n        rows: crate::VectorSize,\n        element_scalar: Scalar,\n        (mut value, expr_meta): (Handle<Expression>, Span),\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let mut components = Vec::with_capacity(columns as usize);\n        // TODO: casts\n        // `Expression::As` doesn't support matrix width\n        // casts so we need to do some extra work for casts\n\n        ctx.forced_conversion(&mut value, expr_meta, element_scalar)?;\n        match *ctx.resolve_type(value, expr_meta)? {\n            TypeInner::Scalar(_) => {\n                // If a matrix is constructed with a single scalar value, then that\n                // value is used to initialize all the values along the diagonal of\n                // the matrix; the rest are given zeros.\n                let vector_ty = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector {\n                            size: rows,\n                            scalar: element_scalar,\n                        },\n                    },\n                    meta,\n                );\n\n                let zero_literal = Literal::zero(element_scalar).unwrap();\n                let zero = ctx.add_expression(Expression::Literal(zero_literal), meta)?;\n\n                for i in 0..columns as u32 {\n                    components.push(\n                        ctx.add_expression(\n                            Expression::Compose {\n                                ty: vector_ty,\n                                components: (0..rows as u32)\n                                    .map(|r| match r == i {\n                                        true => value,\n                                        false => zero,\n                                    })\n                                    .collect(),\n                            },\n                            meta,\n                        )?,\n                    )\n                }\n            }\n            TypeInner::Matrix {\n                rows: ori_rows,\n                columns: ori_cols,\n                ..\n            } => {\n                // If a matrix is constructed from a matrix, then each component\n                // (column i, row j) in the result that has a corresponding component\n                // (column i, row j) in the argument will be initialized from there. All\n                // other components will be initialized to the identity matrix.\n\n                let zero_literal = Literal::zero(element_scalar).unwrap();\n                let one_literal = Literal::one(element_scalar).unwrap();\n\n                let zero = ctx.add_expression(Expression::Literal(zero_literal), meta)?;\n                let one = ctx.add_expression(Expression::Literal(one_literal), meta)?;\n\n                let vector_ty = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector {\n                            size: rows,\n                            scalar: element_scalar,\n                        },\n                    },\n                    meta,\n                );\n\n                for i in 0..columns as u32 {\n                    if i < ori_cols as u32 {\n                        use core::cmp::Ordering;\n\n                        let vector = ctx.add_expression(\n                            Expression::AccessIndex {\n                                base: value,\n                                index: i,\n                            },\n                            meta,\n                        )?;\n\n                        components.push(match ori_rows.cmp(&rows) {\n                            Ordering::Less => {\n                                let components = (0..rows as u32)\n                                    .map(|r| {\n                                        if r < ori_rows as u32 {\n                                            ctx.add_expression(\n                                                Expression::AccessIndex {\n                                                    base: vector,\n                                                    index: r,\n                                                },\n                                                meta,\n                                            )\n                                        } else if r == i {\n                                            Ok(one)\n                                        } else {\n                                            Ok(zero)\n                                        }\n                                    })\n                                    .collect::<Result<_>>()?;\n\n                                ctx.add_expression(\n                                    Expression::Compose {\n                                        ty: vector_ty,\n                                        components,\n                                    },\n                                    meta,\n                                )?\n                            }\n                            Ordering::Equal => vector,\n                            Ordering::Greater => ctx.vector_resize(rows, vector, meta)?,\n                        })\n                    } else {\n                        let compose_expr = Expression::Compose {\n                            ty: vector_ty,\n                            components: (0..rows as u32)\n                                .map(|r| match r == i {\n                                    true => one,\n                                    false => zero,\n                                })\n                                .collect(),\n                        };\n\n                        let vec = ctx.add_expression(compose_expr, meta)?;\n\n                        components.push(vec)\n                    }\n                }\n            }\n            _ => {\n                components = iter::repeat_n(value, columns as usize).collect();\n            }\n        }\n\n        ctx.add_expression(Expression::Compose { ty, components }, meta)\n    }\n\n    fn vector_constructor(\n        &mut self,\n        ctx: &mut Context,\n        ty: Handle<Type>,\n        size: crate::VectorSize,\n        scalar: Scalar,\n        args: &[(Handle<Expression>, Span)],\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let mut components = Vec::with_capacity(size as usize);\n\n        for (mut arg, expr_meta) in args.iter().copied() {\n            ctx.forced_conversion(&mut arg, expr_meta, scalar)?;\n\n            if components.len() >= size as usize {\n                break;\n            }\n\n            match *ctx.resolve_type(arg, expr_meta)? {\n                TypeInner::Scalar { .. } => components.push(arg),\n                TypeInner::Matrix { rows, columns, .. } => {\n                    components.reserve(rows as usize * columns as usize);\n                    for c in 0..(columns as u32) {\n                        let base = ctx.add_expression(\n                            Expression::AccessIndex {\n                                base: arg,\n                                index: c,\n                            },\n                            expr_meta,\n                        )?;\n                        for r in 0..(rows as u32) {\n                            components.push(ctx.add_expression(\n                                Expression::AccessIndex { base, index: r },\n                                expr_meta,\n                            )?)\n                        }\n                    }\n                }\n                TypeInner::Vector { size: ori_size, .. } => {\n                    components.reserve(ori_size as usize);\n                    for index in 0..(ori_size as u32) {\n                        components.push(ctx.add_expression(\n                            Expression::AccessIndex { base: arg, index },\n                            expr_meta,\n                        )?)\n                    }\n                }\n                _ => components.push(arg),\n            }\n        }\n\n        components.truncate(size as usize);\n\n        ctx.add_expression(Expression::Compose { ty, components }, meta)\n    }\n\n    fn constructor_many(\n        &mut self,\n        ctx: &mut Context,\n        ty: Handle<Type>,\n        args: Vec<(Handle<Expression>, Span)>,\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let mut components = Vec::with_capacity(args.len());\n\n        let struct_member_data = match ctx.module.types[ty].inner {\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar: element_scalar,\n            } => {\n                let mut flattened = Vec::with_capacity(columns as usize * rows as usize);\n\n                for (mut arg, meta) in args.iter().copied() {\n                    ctx.forced_conversion(&mut arg, meta, element_scalar)?;\n\n                    match *ctx.resolve_type(arg, meta)? {\n                        TypeInner::Vector { size, .. } => {\n                            for i in 0..(size as u32) {\n                                flattened.push(ctx.add_expression(\n                                    Expression::AccessIndex {\n                                        base: arg,\n                                        index: i,\n                                    },\n                                    meta,\n                                )?)\n                            }\n                        }\n                        _ => flattened.push(arg),\n                    }\n                }\n\n                let ty = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector {\n                            size: rows,\n                            scalar: element_scalar,\n                        },\n                    },\n                    meta,\n                );\n\n                for chunk in flattened.chunks(rows as usize) {\n                    components.push(ctx.add_expression(\n                        Expression::Compose {\n                            ty,\n                            components: Vec::from(chunk),\n                        },\n                        meta,\n                    )?)\n                }\n                None\n            }\n            TypeInner::Vector { size, scalar } => {\n                return self.vector_constructor(ctx, ty, size, scalar, &args, meta)\n            }\n            TypeInner::Array { base, .. } => {\n                for (mut arg, meta) in args.iter().copied() {\n                    let scalar_components = scalar_components(&ctx.module.types[base].inner);\n                    if let Some(scalar) = scalar_components {\n                        ctx.implicit_conversion(&mut arg, meta, scalar)?;\n                    }\n\n                    components.push(arg)\n                }\n                None\n            }\n            TypeInner::Struct { ref members, .. } => Some(\n                members\n                    .iter()\n                    .map(|member| scalar_components(&ctx.module.types[member.ty].inner))\n                    .collect::<Vec<_>>(),\n            ),\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::SemanticError(\"Constructor: Too many arguments\".into()),\n                    meta,\n                })\n            }\n        };\n\n        if let Some(struct_member_data) = struct_member_data {\n            for ((mut arg, meta), scalar_components) in\n                args.iter().copied().zip(struct_member_data.iter().copied())\n            {\n                if let Some(scalar) = scalar_components {\n                    ctx.implicit_conversion(&mut arg, meta, scalar)?;\n                }\n\n                components.push(arg)\n            }\n        }\n\n        ctx.add_expression(Expression::Compose { ty, components }, meta)\n    }\n\n    fn function_call(\n        &mut self,\n        ctx: &mut Context,\n        stmt: &StmtContext,\n        name: String,\n        args: Vec<(Handle<Expression>, Span)>,\n        raw_args: &[Handle<HirExpr>],\n        meta: Span,\n    ) -> Result<Option<Handle<Expression>>> {\n        // Grow the typifier to be able to index it later without needing\n        // to hold the context mutably\n        for &(expr, span) in args.iter() {\n            ctx.typifier_grow(expr, span)?;\n        }\n\n        // Check if the passed arguments require any special variations\n        let mut variations =\n            builtin_required_variations(args.iter().map(|&(expr, _)| ctx.get_type(expr)));\n\n        // Initiate the declaration if it wasn't previously initialized and inject builtins\n        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {\n            variations |= BuiltinVariations::STANDARD;\n            Default::default()\n        });\n        inject_builtin(declaration, ctx.module, &name, variations);\n\n        // Borrow again but without mutability, at this point a declaration is guaranteed\n        let declaration = self.lookup_function.get(&name).unwrap();\n\n        // Possibly contains the overload to be used in the call\n        let mut maybe_overload = None;\n        // The conversions needed for the best analyzed overload, this is initialized all to\n        // `NONE` to make sure that conversions always pass the first time without ambiguity\n        let mut old_conversions = vec![Conversion::None; args.len()];\n        // Tracks whether the comparison between overloads lead to an ambiguity\n        let mut ambiguous = false;\n\n        // Iterate over all the available overloads to select either an exact match or a\n        // overload which has suitable implicit conversions\n        'outer: for (overload_idx, overload) in declaration.overloads.iter().enumerate() {\n            // If the overload and the function call don't have the same number of arguments\n            // continue to the next overload\n            if args.len() != overload.parameters.len() {\n                continue;\n            }\n\n            log::trace!(\"Testing overload {overload_idx}\");\n\n            // Stores whether the current overload matches exactly the function call\n            let mut exact = true;\n            // State of the selection\n            // If None we still don't know what is the best overload\n            // If Some(true) the new overload is better\n            // If Some(false) the old overload is better\n            let mut superior = None;\n            // Store the conversions for the current overload so that later they can replace the\n            // conversions used for querying the best overload\n            let mut new_conversions = vec![Conversion::None; args.len()];\n\n            // Loop through the overload parameters and check if the current overload is better\n            // compared to the previous best overload.\n            for (i, overload_parameter) in overload.parameters.iter().enumerate() {\n                let call_argument = &args[i];\n                let parameter_info = &overload.parameters_info[i];\n\n                // If the image is used in the overload as a depth texture convert it\n                // before comparing, otherwise exact matches wouldn't be reported\n                if parameter_info.depth {\n                    sampled_to_depth(ctx, call_argument.0, call_argument.1, &mut self.errors);\n                    ctx.invalidate_expression(call_argument.0, call_argument.1)?\n                }\n\n                ctx.typifier_grow(call_argument.0, call_argument.1)?;\n\n                let overload_param_ty = &ctx.module.types[*overload_parameter].inner;\n                let call_arg_ty = ctx.get_type(call_argument.0);\n\n                log::trace!(\n                    \"Testing parameter {i}\\n\\tOverload = {overload_param_ty:?}\\n\\tCall = {call_arg_ty:?}\"\n                );\n\n                // Storage images cannot be directly compared since while the access is part of the\n                // type in naga's IR, in glsl they are a qualifier and don't enter in the match as\n                // long as the access needed is satisfied.\n                if let (\n                    &TypeInner::Image {\n                        class:\n                            crate::ImageClass::Storage {\n                                format: overload_format,\n                                access: overload_access,\n                            },\n                        dim: overload_dim,\n                        arrayed: overload_arrayed,\n                    },\n                    &TypeInner::Image {\n                        class:\n                            crate::ImageClass::Storage {\n                                format: call_format,\n                                access: call_access,\n                            },\n                        dim: call_dim,\n                        arrayed: call_arrayed,\n                    },\n                ) = (overload_param_ty, call_arg_ty)\n                {\n                    // Images size must match otherwise the overload isn't what we want\n                    let good_size = call_dim == overload_dim && call_arrayed == overload_arrayed;\n                    // Glsl requires the formats to strictly match unless you are builtin\n                    // function overload and have not been replaced, in which case we only\n                    // check that the format scalar kind matches\n                    let good_format = overload_format == call_format\n                        || (overload.internal\n                            && Scalar::from(overload_format) == Scalar::from(call_format));\n                    if !(good_size && good_format) {\n                        continue 'outer;\n                    }\n\n                    // While storage access mismatch is an error it isn't one that causes\n                    // the overload matching to fail so we defer the error and consider\n                    // that the images match exactly\n                    if !call_access.contains(overload_access) {\n                        self.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                format!(\n                                    \"'{name}': image needs {overload_access:?} access but only {call_access:?} was provided\"\n                                )\n                                .into(),\n                            ),\n                            meta,\n                        });\n                    }\n\n                    // The images satisfy the conditions to be considered as an exact match\n                    new_conversions[i] = Conversion::Exact;\n                    continue;\n                } else if overload_param_ty == call_arg_ty {\n                    // If the types match there's no need to check for conversions so continue\n                    new_conversions[i] = Conversion::Exact;\n                    continue;\n                }\n\n                // Glsl defines that inout follows both the conversions for input parameters and\n                // output parameters, this means that the type must have a conversion from both the\n                // call argument to the function parameter and the function parameter to the call\n                // argument, the only way this is possible is for the conversion to be an identity\n                // (i.e. call argument = function parameter)\n                if let ParameterQualifier::InOut = parameter_info.qualifier {\n                    continue 'outer;\n                }\n\n                // The function call argument and the function definition\n                // parameter are not equal at this point, so we need to try\n                // implicit conversions.\n                //\n                // Now there are two cases, the argument is defined as a normal\n                // parameter (`in` or `const`), in this case an implicit\n                // conversion is made from the calling argument to the\n                // definition argument. If the parameter is `out` the\n                // opposite needs to be done, so the implicit conversion is made\n                // from the definition argument to the calling argument.\n                let maybe_conversion = if parameter_info.qualifier.is_lhs() {\n                    conversion(call_arg_ty, overload_param_ty)\n                } else {\n                    conversion(overload_param_ty, call_arg_ty)\n                };\n\n                let conversion = match maybe_conversion {\n                    Some(info) => info,\n                    None => continue 'outer,\n                };\n\n                // At this point a conversion will be needed so the overload no longer\n                // exactly matches the call arguments\n                exact = false;\n\n                // Compare the conversions needed for this overload parameter to that of the\n                // last overload analyzed respective parameter, the value is:\n                // - `true` when the new overload argument has a better conversion\n                // - `false` when the old overload argument has a better conversion\n                let best_arg = match (conversion, old_conversions[i]) {\n                    // An exact match is always better, we don't need to check this for the\n                    // current overload since it was checked earlier\n                    (_, Conversion::Exact) => false,\n                    // No overload was yet analyzed so this one is the best yet\n                    (_, Conversion::None) => true,\n                    // A conversion from a float to a double is the best possible conversion\n                    (Conversion::FloatToDouble, _) => true,\n                    (_, Conversion::FloatToDouble) => false,\n                    // A conversion from a float to an integer is preferred than one\n                    // from double to an integer\n                    (Conversion::IntToFloat, Conversion::IntToDouble) => true,\n                    (Conversion::IntToDouble, Conversion::IntToFloat) => false,\n                    // This case handles things like no conversion and exact which were already\n                    // treated and other cases which no conversion is better than the other\n                    _ => continue,\n                };\n\n                // Check if the best parameter corresponds to the current selected overload\n                // to pass to the next comparison, if this isn't true mark it as ambiguous\n                match best_arg {\n                    true => match superior {\n                        Some(false) => ambiguous = true,\n                        _ => {\n                            superior = Some(true);\n                            new_conversions[i] = conversion\n                        }\n                    },\n                    false => match superior {\n                        Some(true) => ambiguous = true,\n                        _ => superior = Some(false),\n                    },\n                }\n            }\n\n            // The overload matches exactly the function call so there's no ambiguity (since\n            // repeated overload aren't allowed) and the current overload is selected, no\n            // further querying is needed.\n            if exact {\n                maybe_overload = Some(overload);\n                ambiguous = false;\n                break;\n            }\n\n            match superior {\n                // New overload is better keep it\n                Some(true) => {\n                    maybe_overload = Some(overload);\n                    // Replace the conversions\n                    old_conversions = new_conversions;\n                }\n                // Old overload is better do nothing\n                Some(false) => {}\n                // No overload was better than the other this can be caused\n                // when all conversions are ambiguous in which the overloads themselves are\n                // ambiguous.\n                None => {\n                    ambiguous = true;\n                    // Assign the new overload, this helps ensures that in this case of\n                    // ambiguity the parsing won't end immediately and allow for further\n                    // collection of errors.\n                    maybe_overload = Some(overload);\n                }\n            }\n        }\n\n        if ambiguous {\n            self.errors.push(Error {\n                kind: ErrorKind::SemanticError(\n                    format!(\"Ambiguous best function for '{name}'\").into(),\n                ),\n                meta,\n            })\n        }\n\n        let overload = maybe_overload.ok_or_else(|| Error {\n            kind: ErrorKind::SemanticError(format!(\"Unknown function '{name}'\").into()),\n            meta,\n        })?;\n\n        let parameters_info = overload.parameters_info.clone();\n        let parameters = overload.parameters.clone();\n        let is_void = overload.void;\n        let kind = overload.kind;\n\n        let mut arguments = Vec::with_capacity(args.len());\n        let mut proxy_writes = Vec::new();\n\n        // Iterate through the function call arguments applying transformations as needed\n        for (((parameter_info, call_argument), expr), parameter) in parameters_info\n            .iter()\n            .zip(&args)\n            .zip(raw_args)\n            .zip(&parameters)\n        {\n            if parameter_info.qualifier.is_lhs() {\n                // Reprocess argument in LHS position\n                let (handle, meta) = ctx.lower_expect_inner(stmt, self, *expr, ExprPos::Lhs)?;\n\n                self.process_lhs_argument(\n                    ctx,\n                    meta,\n                    *parameter,\n                    parameter_info,\n                    handle,\n                    call_argument,\n                    &mut proxy_writes,\n                    &mut arguments,\n                )?;\n\n                continue;\n            }\n\n            let (mut handle, meta) = *call_argument;\n\n            let scalar_comps = scalar_components(&ctx.module.types[*parameter].inner);\n\n            // Apply implicit conversions as needed\n            if let Some(scalar) = scalar_comps {\n                ctx.implicit_conversion(&mut handle, meta, scalar)?;\n            }\n\n            arguments.push(handle)\n        }\n\n        match kind {\n            FunctionKind::Call(function) => {\n                ctx.emit_end();\n\n                let result = if !is_void {\n                    Some(ctx.add_expression(Expression::CallResult(function), meta)?)\n                } else {\n                    None\n                };\n\n                ctx.body.push(\n                    Statement::Call {\n                        function,\n                        arguments,\n                        result,\n                    },\n                    meta,\n                );\n\n                ctx.emit_start();\n\n                // Write back all the variables that were scheduled to their original place\n                for proxy_write in proxy_writes {\n                    let mut value = ctx.add_expression(\n                        Expression::Load {\n                            pointer: proxy_write.value,\n                        },\n                        meta,\n                    )?;\n\n                    if let Some(scalar) = proxy_write.convert {\n                        ctx.conversion(&mut value, meta, scalar)?;\n                    }\n\n                    ctx.emit_restart();\n\n                    ctx.body.push(\n                        Statement::Store {\n                            pointer: proxy_write.target,\n                            value,\n                        },\n                        meta,\n                    );\n                }\n\n                Ok(result)\n            }\n            FunctionKind::Macro(builtin) => builtin.call(self, ctx, arguments.as_mut_slice(), meta),\n        }\n    }\n\n    /// Processes a function call argument that appears in place of an output\n    /// parameter.\n    #[allow(clippy::too_many_arguments)]\n    fn process_lhs_argument(\n        &mut self,\n        ctx: &mut Context,\n        meta: Span,\n        parameter_ty: Handle<Type>,\n        parameter_info: &ParameterInfo,\n        original: Handle<Expression>,\n        call_argument: &(Handle<Expression>, Span),\n        proxy_writes: &mut Vec<ProxyWrite>,\n        arguments: &mut Vec<Handle<Expression>>,\n    ) -> Result<()> {\n        let original_ty = ctx.resolve_type(original, meta)?;\n        let original_pointer_space = original_ty.pointer_space();\n\n        // The type of a possible spill variable needed for a proxy write\n        let mut maybe_ty = match *original_ty {\n            // If the argument is to be passed as a pointer but the type of the\n            // expression returns a vector it must mean that it was for example\n            // swizzled and it must be spilled into a local before calling\n            TypeInner::Vector { size, scalar } => Some(ctx.module.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Vector { size, scalar },\n                },\n                Span::default(),\n            )),\n            // If the argument is a pointer whose address space isn't `Function`, an\n            // indirection through a local variable is needed to align the address\n            // spaces of the call argument and the overload parameter.\n            TypeInner::Pointer { base, space } if space != AddressSpace::Function => Some(base),\n            TypeInner::ValuePointer {\n                size,\n                scalar,\n                space,\n            } if space != AddressSpace::Function => {\n                let inner = match size {\n                    Some(size) => TypeInner::Vector { size, scalar },\n                    None => TypeInner::Scalar(scalar),\n                };\n\n                Some(\n                    ctx.module\n                        .types\n                        .insert(Type { name: None, inner }, Span::default()),\n                )\n            }\n            _ => None,\n        };\n\n        // Since the original expression might be a pointer and we want a value\n        // for the proxy writes, we might need to load the pointer.\n        let value = if original_pointer_space.is_some() {\n            ctx.add_expression(Expression::Load { pointer: original }, Span::default())?\n        } else {\n            original\n        };\n\n        ctx.typifier_grow(call_argument.0, call_argument.1)?;\n\n        let overload_param_ty = &ctx.module.types[parameter_ty].inner;\n        let call_arg_ty = ctx.get_type(call_argument.0);\n        let needs_conversion = call_arg_ty != overload_param_ty;\n\n        let arg_scalar_comps = scalar_components(call_arg_ty);\n\n        // Since output parameters also allow implicit conversions from the\n        // parameter to the argument, we need to spill the conversion to a\n        // variable and create a proxy write for the original variable.\n        if needs_conversion {\n            maybe_ty = Some(parameter_ty);\n        }\n\n        if let Some(ty) = maybe_ty {\n            // Create the spill variable\n            let spill_var = ctx.locals.append(\n                LocalVariable {\n                    name: None,\n                    ty,\n                    init: None,\n                },\n                Span::default(),\n            );\n            let spill_expr =\n                ctx.add_expression(Expression::LocalVariable(spill_var), Span::default())?;\n\n            // If the argument is also copied in we must store the value of the\n            // original variable to the spill variable.\n            if let ParameterQualifier::InOut = parameter_info.qualifier {\n                ctx.body.push(\n                    Statement::Store {\n                        pointer: spill_expr,\n                        value,\n                    },\n                    Span::default(),\n                );\n            }\n\n            // Add the spill variable as an argument to the function call\n            arguments.push(spill_expr);\n\n            let convert = if needs_conversion {\n                arg_scalar_comps\n            } else {\n                None\n            };\n\n            // Register the temporary local to be written back to it's original\n            // place after the function call\n            if let Expression::Swizzle {\n                size,\n                mut vector,\n                pattern,\n            } = ctx.expressions[original]\n            {\n                if let Expression::Load { pointer } = ctx.expressions[vector] {\n                    vector = pointer;\n                }\n\n                for (i, component) in pattern.iter().take(size as usize).enumerate() {\n                    let original = ctx.add_expression(\n                        Expression::AccessIndex {\n                            base: vector,\n                            index: *component as u32,\n                        },\n                        Span::default(),\n                    )?;\n\n                    let spill_component = ctx.add_expression(\n                        Expression::AccessIndex {\n                            base: spill_expr,\n                            index: i as u32,\n                        },\n                        Span::default(),\n                    )?;\n\n                    proxy_writes.push(ProxyWrite {\n                        target: original,\n                        value: spill_component,\n                        convert,\n                    });\n                }\n            } else {\n                proxy_writes.push(ProxyWrite {\n                    target: original,\n                    value: spill_expr,\n                    convert,\n                });\n            }\n        } else {\n            arguments.push(original);\n        }\n\n        Ok(())\n    }\n\n    pub(crate) fn add_function(\n        &mut self,\n        mut ctx: Context,\n        name: String,\n        result: Option<FunctionResult>,\n        meta: Span,\n    ) {\n        ensure_block_returns(&mut ctx.body);\n\n        let void = result.is_none();\n\n        // Check if the passed arguments require any special variations\n        let mut variations = builtin_required_variations(\n            ctx.parameters\n                .iter()\n                .map(|&arg| &ctx.module.types[arg].inner),\n        );\n\n        // Initiate the declaration if it wasn't previously initialized and inject builtins\n        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {\n            variations |= BuiltinVariations::STANDARD;\n            Default::default()\n        });\n        inject_builtin(declaration, ctx.module, &name, variations);\n\n        let Context {\n            expressions,\n            locals,\n            arguments,\n            parameters,\n            parameters_info,\n            body,\n            module,\n            ..\n        } = ctx;\n\n        let function = Function {\n            name: Some(name),\n            arguments,\n            result,\n            local_variables: locals,\n            expressions,\n            named_expressions: crate::NamedExpressions::default(),\n            body,\n            diagnostic_filter_leaf: None,\n        };\n\n        'outer: for decl in declaration.overloads.iter_mut() {\n            if parameters.len() != decl.parameters.len() {\n                continue;\n            }\n\n            for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter()) {\n                let new_inner = &module.types[*new_parameter].inner;\n                let old_inner = &module.types[*old_parameter].inner;\n\n                if new_inner != old_inner {\n                    continue 'outer;\n                }\n            }\n\n            if decl.defined {\n                return self.errors.push(Error {\n                    kind: ErrorKind::SemanticError(\"Function already defined\".into()),\n                    meta,\n                });\n            }\n\n            decl.defined = true;\n            decl.parameters_info = parameters_info;\n            match decl.kind {\n                FunctionKind::Call(handle) => *module.functions.get_mut(handle) = function,\n                FunctionKind::Macro(_) => {\n                    let handle = module.functions.append(function, meta);\n                    decl.kind = FunctionKind::Call(handle)\n                }\n            }\n            return;\n        }\n\n        let handle = module.functions.append(function, meta);\n        declaration.overloads.push(Overload {\n            parameters,\n            parameters_info,\n            kind: FunctionKind::Call(handle),\n            defined: true,\n            internal: false,\n            void,\n        });\n    }\n\n    pub(crate) fn add_prototype(\n        &mut self,\n        ctx: Context,\n        name: String,\n        result: Option<FunctionResult>,\n        meta: Span,\n    ) {\n        let void = result.is_none();\n\n        // Check if the passed arguments require any special variations\n        let mut variations = builtin_required_variations(\n            ctx.parameters\n                .iter()\n                .map(|&arg| &ctx.module.types[arg].inner),\n        );\n\n        // Initiate the declaration if it wasn't previously initialized and inject builtins\n        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {\n            variations |= BuiltinVariations::STANDARD;\n            Default::default()\n        });\n        inject_builtin(declaration, ctx.module, &name, variations);\n\n        let Context {\n            arguments,\n            parameters,\n            parameters_info,\n            module,\n            ..\n        } = ctx;\n\n        let function = Function {\n            name: Some(name),\n            arguments,\n            result,\n            ..Default::default()\n        };\n\n        'outer: for decl in declaration.overloads.iter() {\n            if parameters.len() != decl.parameters.len() {\n                continue;\n            }\n\n            for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter()) {\n                let new_inner = &module.types[*new_parameter].inner;\n                let old_inner = &module.types[*old_parameter].inner;\n\n                if new_inner != old_inner {\n                    continue 'outer;\n                }\n            }\n\n            return self.errors.push(Error {\n                kind: ErrorKind::SemanticError(\"Prototype already defined\".into()),\n                meta,\n            });\n        }\n\n        let handle = module.functions.append(function, meta);\n        declaration.overloads.push(Overload {\n            parameters,\n            parameters_info,\n            kind: FunctionKind::Call(handle),\n            defined: false,\n            internal: false,\n            void,\n        });\n    }\n\n    /// Create a Naga [`EntryPoint`] that calls the GLSL `main` function.\n    ///\n    /// We compile the GLSL `main` function as an ordinary Naga [`Function`].\n    /// This function synthesizes a Naga [`EntryPoint`] to call that.\n    ///\n    /// Each GLSL input and output variable (including builtins) becomes a Naga\n    /// [`GlobalVariable`]s in the [`Private`] address space, which `main` can\n    /// access in the usual way.\n    ///\n    /// The `EntryPoint` we synthesize here has an argument for each GLSL input\n    /// variable, and returns a struct with a member for each GLSL output\n    /// variable. The entry point contains code to:\n    ///\n    /// - copy its arguments into the Naga globals representing the GLSL input\n    ///   variables,\n    ///\n    /// - call the Naga `Function` representing the GLSL `main` function, and then\n    ///\n    /// - build its return value from whatever values the GLSL `main` left in\n    ///   the Naga globals representing GLSL `output` variables.\n    ///\n    /// Upon entry, [`ctx.body`] should contain code, accumulated by prior calls\n    /// to [`ParsingContext::parse_external_declaration`][pxd], to initialize\n    /// private global variables as needed. This code gets spliced into the\n    /// entry point before the call to `main`.\n    ///\n    /// [`GlobalVariable`]: crate::GlobalVariable\n    /// [`Private`]: crate::AddressSpace::Private\n    /// [`ctx.body`]: Context::body\n    /// [pxd]: super::ParsingContext::parse_external_declaration\n    pub(crate) fn add_entry_point(\n        &mut self,\n        function: Handle<Function>,\n        mut ctx: Context,\n    ) -> Result<()> {\n        let mut arguments = Vec::new();\n\n        let body = Block::with_capacity(\n            // global init body\n            ctx.body.len() +\n            // prologue and epilogue\n            self.entry_args.len() * 2\n            // Call, Emit for composing struct and return\n            + 3,\n        );\n\n        let global_init_body = core::mem::replace(&mut ctx.body, body);\n\n        for arg in self.entry_args.iter() {\n            if arg.storage != StorageQualifier::Input {\n                continue;\n            }\n\n            let pointer = ctx\n                .expressions\n                .append(Expression::GlobalVariable(arg.handle), Default::default());\n            ctx.local_expression_kind_tracker\n                .insert(pointer, crate::proc::ExpressionKind::Runtime);\n\n            let ty = ctx.module.global_variables[arg.handle].ty;\n\n            ctx.arg_type_walker(\n                arg.name.clone(),\n                arg.binding.clone(),\n                pointer,\n                ty,\n                &mut |ctx, name, pointer, ty, binding| {\n                    let idx = arguments.len() as u32;\n\n                    arguments.push(FunctionArgument {\n                        name,\n                        ty,\n                        binding: Some(binding),\n                    });\n\n                    let value = ctx\n                        .expressions\n                        .append(Expression::FunctionArgument(idx), Default::default());\n                    ctx.local_expression_kind_tracker\n                        .insert(value, crate::proc::ExpressionKind::Runtime);\n                    ctx.body\n                        .push(Statement::Store { pointer, value }, Default::default());\n                },\n            )?\n        }\n\n        ctx.body.extend_block(global_init_body);\n\n        ctx.body.push(\n            Statement::Call {\n                function,\n                arguments: Vec::new(),\n                result: None,\n            },\n            Default::default(),\n        );\n\n        let mut span = 0;\n        let mut members = Vec::new();\n        let mut components = Vec::new();\n\n        for arg in self.entry_args.iter() {\n            if arg.storage != StorageQualifier::Output {\n                continue;\n            }\n\n            let pointer = ctx\n                .expressions\n                .append(Expression::GlobalVariable(arg.handle), Default::default());\n            ctx.local_expression_kind_tracker\n                .insert(pointer, crate::proc::ExpressionKind::Runtime);\n\n            let ty = ctx.module.global_variables[arg.handle].ty;\n\n            ctx.arg_type_walker(\n                arg.name.clone(),\n                arg.binding.clone(),\n                pointer,\n                ty,\n                &mut |ctx, name, pointer, ty, binding| {\n                    members.push(StructMember {\n                        name,\n                        ty,\n                        binding: Some(binding),\n                        offset: span,\n                    });\n\n                    span += ctx.module.types[ty].inner.size(ctx.module.to_ctx());\n\n                    let len = ctx.expressions.len();\n                    let load = ctx\n                        .expressions\n                        .append(Expression::Load { pointer }, Default::default());\n                    ctx.local_expression_kind_tracker\n                        .insert(load, crate::proc::ExpressionKind::Runtime);\n                    ctx.body.push(\n                        Statement::Emit(ctx.expressions.range_from(len)),\n                        Default::default(),\n                    );\n                    components.push(load)\n                },\n            )?\n        }\n\n        let (ty, value) = if !components.is_empty() {\n            let ty = ctx.module.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Struct { members, span },\n                },\n                Default::default(),\n            );\n\n            let len = ctx.expressions.len();\n            let res = ctx\n                .expressions\n                .append(Expression::Compose { ty, components }, Default::default());\n            ctx.local_expression_kind_tracker\n                .insert(res, crate::proc::ExpressionKind::Runtime);\n            ctx.body.push(\n                Statement::Emit(ctx.expressions.range_from(len)),\n                Default::default(),\n            );\n\n            (Some(ty), Some(res))\n        } else {\n            (None, None)\n        };\n\n        ctx.body\n            .push(Statement::Return { value }, Default::default());\n\n        let Context {\n            body, expressions, ..\n        } = ctx;\n\n        ctx.module.entry_points.push(EntryPoint {\n            name: \"main\".to_string(),\n            stage: self.meta.stage,\n            early_depth_test: Some(crate::EarlyDepthTest::Force)\n                .filter(|_| self.meta.early_fragment_tests),\n            workgroup_size: self.meta.workgroup_size,\n            workgroup_size_overrides: None,\n            function: Function {\n                arguments,\n                expressions,\n                body,\n                result: ty.map(|ty| FunctionResult { ty, binding: None }),\n                ..Default::default()\n            },\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        });\n\n        Ok(())\n    }\n}\n\nimpl Context<'_> {\n    /// Helper function for building the input/output interface of the entry point\n    ///\n    /// Calls `f` with the data of the entry point argument, flattening composite types\n    /// recursively\n    ///\n    /// The passed arguments to the callback are:\n    /// - The ctx\n    /// - The name\n    /// - The pointer expression to the global storage\n    /// - The handle to the type of the entry point argument\n    /// - The binding of the entry point argument\n    fn arg_type_walker(\n        &mut self,\n        name: Option<String>,\n        binding: crate::Binding,\n        pointer: Handle<Expression>,\n        ty: Handle<Type>,\n        f: &mut impl FnMut(\n            &mut Context,\n            Option<String>,\n            Handle<Expression>,\n            Handle<Type>,\n            crate::Binding,\n        ),\n    ) -> Result<()> {\n        match self.module.types[ty].inner {\n            // TODO: Better error reporting\n            // right now we just don't walk the array if the size isn't known at\n            // compile time and let validation catch it\n            TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                ..\n            } => {\n                let mut location = match binding {\n                    crate::Binding::Location { location, .. } => location,\n                    crate::Binding::BuiltIn(_) => return Ok(()),\n                };\n\n                let interpolation =\n                    self.module.types[base]\n                        .inner\n                        .scalar_kind()\n                        .map(|kind| match kind {\n                            ScalarKind::Float => crate::Interpolation::Perspective,\n                            _ => crate::Interpolation::Flat,\n                        });\n\n                for index in 0..size.get() {\n                    let member_pointer = self.add_expression(\n                        Expression::AccessIndex {\n                            base: pointer,\n                            index,\n                        },\n                        Span::default(),\n                    )?;\n\n                    let binding = crate::Binding::Location {\n                        location,\n                        interpolation,\n                        sampling: None,\n                        blend_src: None,\n                        per_primitive: false,\n                    };\n                    location += 1;\n\n                    self.arg_type_walker(name.clone(), binding, member_pointer, base, f)?\n                }\n            }\n            TypeInner::Struct { ref members, .. } => {\n                let mut location = match binding {\n                    crate::Binding::Location { location, .. } => location,\n                    crate::Binding::BuiltIn(_) => return Ok(()),\n                };\n\n                for (i, member) in members.clone().into_iter().enumerate() {\n                    let member_pointer = self.add_expression(\n                        Expression::AccessIndex {\n                            base: pointer,\n                            index: i as u32,\n                        },\n                        Span::default(),\n                    )?;\n\n                    let binding = match member.binding {\n                        Some(binding) => binding,\n                        None => {\n                            let interpolation = self.module.types[member.ty]\n                                .inner\n                                .scalar_kind()\n                                .map(|kind| match kind {\n                                    ScalarKind::Float => crate::Interpolation::Perspective,\n                                    _ => crate::Interpolation::Flat,\n                                });\n                            let binding = crate::Binding::Location {\n                                location,\n                                interpolation,\n                                sampling: None,\n                                blend_src: None,\n                                per_primitive: false,\n                            };\n                            location += 1;\n                            binding\n                        }\n                    };\n\n                    self.arg_type_walker(member.name, binding, member_pointer, member.ty, f)?\n                }\n            }\n            _ => f(self, name, pointer, ty, binding),\n        }\n\n        Ok(())\n    }\n}\n\n/// Helper enum containing the type of conversion need for a call\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\nenum Conversion {\n    /// No conversion needed\n    Exact,\n    /// Float to double conversion needed\n    FloatToDouble,\n    /// Int or uint to float conversion needed\n    IntToFloat,\n    /// Int or uint to double conversion needed\n    IntToDouble,\n    /// Other type of conversion needed\n    Other,\n    /// No conversion was yet registered\n    None,\n}\n\n/// Helper function, returns the type of conversion from `source` to `target`, if a\n/// conversion is not possible returns None.\nfn conversion(target: &TypeInner, source: &TypeInner) -> Option<Conversion> {\n    use ScalarKind::*;\n\n    // Gather the `ScalarKind` and scalar width from both the target and the source\n    let (target_scalar, source_scalar) = match (target, source) {\n        // Conversions between scalars are allowed\n        (&TypeInner::Scalar(tgt_scalar), &TypeInner::Scalar(src_scalar)) => {\n            (tgt_scalar, src_scalar)\n        }\n        // Conversions between vectors of the same size are allowed\n        (\n            &TypeInner::Vector {\n                size: tgt_size,\n                scalar: tgt_scalar,\n            },\n            &TypeInner::Vector {\n                size: src_size,\n                scalar: src_scalar,\n            },\n        ) if tgt_size == src_size => (tgt_scalar, src_scalar),\n        // Conversions between matrices of the same size are allowed\n        (\n            &TypeInner::Matrix {\n                rows: tgt_rows,\n                columns: tgt_cols,\n                scalar: tgt_scalar,\n            },\n            &TypeInner::Matrix {\n                rows: src_rows,\n                columns: src_cols,\n                scalar: src_scalar,\n            },\n        ) if tgt_cols == src_cols && tgt_rows == src_rows => (tgt_scalar, src_scalar),\n        _ => return None,\n    };\n\n    // Check if source can be converted into target, if this is the case then the type\n    // power of target must be higher than that of source\n    let target_power = type_power(target_scalar);\n    let source_power = type_power(source_scalar);\n    if target_power < source_power {\n        return None;\n    }\n\n    Some(match (target_scalar, source_scalar) {\n        // A conversion from a float to a double is special\n        (Scalar::F64, Scalar::F32) => Conversion::FloatToDouble,\n        // A conversion from an integer to a float is special\n        (\n            Scalar::F32,\n            Scalar {\n                kind: Sint | Uint,\n                width: _,\n            },\n        ) => Conversion::IntToFloat,\n        // A conversion from an integer to a double is special\n        (\n            Scalar::F64,\n            Scalar {\n                kind: Sint | Uint,\n                width: _,\n            },\n        ) => Conversion::IntToDouble,\n        _ => Conversion::Other,\n    })\n}\n\n/// Helper method returning all the non standard builtin variations needed\n/// to process the function call with the passed arguments\nfn builtin_required_variations<'a>(args: impl Iterator<Item = &'a TypeInner>) -> BuiltinVariations {\n    let mut variations = BuiltinVariations::empty();\n\n    for ty in args {\n        match *ty {\n            TypeInner::ValuePointer { scalar, .. }\n            | TypeInner::Scalar(scalar)\n            | TypeInner::Vector { scalar, .. }\n            | TypeInner::Matrix { scalar, .. } => {\n                if scalar == Scalar::F64 {\n                    variations |= BuiltinVariations::DOUBLE\n                }\n            }\n            TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                if dim == crate::ImageDimension::Cube && arrayed {\n                    variations |= BuiltinVariations::CUBE_TEXTURES_ARRAY\n                }\n\n                if dim == crate::ImageDimension::D2 && arrayed && class.is_multisampled() {\n                    variations |= BuiltinVariations::D2_MULTI_TEXTURES_ARRAY\n                }\n            }\n            _ => {}\n        }\n    }\n\n    variations\n}\n"
  },
  {
    "path": "naga/src/front/glsl/lex.rs",
    "content": "use alloc::string::String;\n\nuse pp_rs::{\n    pp::Preprocessor,\n    token::{PreprocessorError, Punct, TokenValue as PPTokenValue},\n};\n\nuse super::{\n    ast::Precision,\n    token::{Directive, DirectiveKind, Token, TokenValue},\n    types::parse_type,\n};\nuse crate::{FastHashMap, Span, StorageAccess};\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub struct LexerResult {\n    pub kind: LexerResultKind,\n    pub meta: Span,\n}\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum LexerResultKind {\n    Token(Token),\n    Directive(Directive),\n    Error(PreprocessorError),\n}\n\npub struct Lexer<'a> {\n    pp: Preprocessor<'a>,\n}\n\nimpl<'a> Lexer<'a> {\n    pub fn new(input: &'a str, defines: &'a FastHashMap<String, String>) -> Self {\n        let mut pp = Preprocessor::new(input);\n        for (define, value) in defines {\n            pp.add_define(define, value).unwrap(); //TODO: handle error\n        }\n        Lexer { pp }\n    }\n}\n\nimpl Iterator for Lexer<'_> {\n    type Item = LexerResult;\n    fn next(&mut self) -> Option<Self::Item> {\n        let pp_token = match self.pp.next()? {\n            Ok(t) => t,\n            Err((err, loc)) => {\n                return Some(LexerResult {\n                    kind: LexerResultKind::Error(err),\n                    meta: loc.into(),\n                });\n            }\n        };\n\n        let meta = pp_token.location.into();\n        let value = match pp_token.value {\n            PPTokenValue::Extension(extension) => {\n                return Some(LexerResult {\n                    kind: LexerResultKind::Directive(Directive {\n                        kind: DirectiveKind::Extension,\n                        tokens: extension.tokens,\n                    }),\n                    meta,\n                })\n            }\n            PPTokenValue::Float(float) => TokenValue::FloatConstant(float),\n            PPTokenValue::Ident(ident) => {\n                match ident.as_str() {\n                    // Qualifiers\n                    \"layout\" => TokenValue::Layout,\n                    \"in\" => TokenValue::In,\n                    \"out\" => TokenValue::Out,\n                    \"uniform\" => TokenValue::Uniform,\n                    \"buffer\" => TokenValue::Buffer,\n                    \"shared\" => TokenValue::Shared,\n                    \"invariant\" => TokenValue::Invariant,\n                    \"flat\" => TokenValue::Interpolation(crate::Interpolation::Flat),\n                    \"noperspective\" => TokenValue::Interpolation(crate::Interpolation::Linear),\n                    \"smooth\" => TokenValue::Interpolation(crate::Interpolation::Perspective),\n                    \"centroid\" => TokenValue::Sampling(crate::Sampling::Centroid),\n                    \"sample\" => TokenValue::Sampling(crate::Sampling::Sample),\n                    \"const\" => TokenValue::Const,\n                    \"inout\" => TokenValue::InOut,\n                    \"precision\" => TokenValue::Precision,\n                    \"highp\" => TokenValue::PrecisionQualifier(Precision::High),\n                    \"mediump\" => TokenValue::PrecisionQualifier(Precision::Medium),\n                    \"lowp\" => TokenValue::PrecisionQualifier(Precision::Low),\n                    \"restrict\" => TokenValue::Restrict,\n                    \"readonly\" => TokenValue::MemoryQualifier(StorageAccess::LOAD),\n                    \"writeonly\" => TokenValue::MemoryQualifier(StorageAccess::STORE),\n                    // values\n                    \"true\" => TokenValue::BoolConstant(true),\n                    \"false\" => TokenValue::BoolConstant(false),\n                    // jump statements\n                    \"continue\" => TokenValue::Continue,\n                    \"break\" => TokenValue::Break,\n                    \"return\" => TokenValue::Return,\n                    \"discard\" => TokenValue::Discard,\n                    // selection statements\n                    \"if\" => TokenValue::If,\n                    \"else\" => TokenValue::Else,\n                    \"switch\" => TokenValue::Switch,\n                    \"case\" => TokenValue::Case,\n                    \"default\" => TokenValue::Default,\n                    // iteration statements\n                    \"while\" => TokenValue::While,\n                    \"do\" => TokenValue::Do,\n                    \"for\" => TokenValue::For,\n                    // types\n                    \"void\" => TokenValue::Void,\n                    \"struct\" => TokenValue::Struct,\n                    word => match parse_type(word) {\n                        Some(t) => TokenValue::TypeName(t),\n                        None => TokenValue::Identifier(String::from(word)),\n                    },\n                }\n            }\n            PPTokenValue::Integer(integer) => TokenValue::IntConstant(integer),\n            PPTokenValue::Punct(punct) => match punct {\n                // Compound assignments\n                Punct::AddAssign => TokenValue::AddAssign,\n                Punct::SubAssign => TokenValue::SubAssign,\n                Punct::MulAssign => TokenValue::MulAssign,\n                Punct::DivAssign => TokenValue::DivAssign,\n                Punct::ModAssign => TokenValue::ModAssign,\n                Punct::LeftShiftAssign => TokenValue::LeftShiftAssign,\n                Punct::RightShiftAssign => TokenValue::RightShiftAssign,\n                Punct::AndAssign => TokenValue::AndAssign,\n                Punct::XorAssign => TokenValue::XorAssign,\n                Punct::OrAssign => TokenValue::OrAssign,\n\n                // Two character punctuation\n                Punct::Increment => TokenValue::Increment,\n                Punct::Decrement => TokenValue::Decrement,\n                Punct::LogicalAnd => TokenValue::LogicalAnd,\n                Punct::LogicalOr => TokenValue::LogicalOr,\n                Punct::LogicalXor => TokenValue::LogicalXor,\n                Punct::LessEqual => TokenValue::LessEqual,\n                Punct::GreaterEqual => TokenValue::GreaterEqual,\n                Punct::EqualEqual => TokenValue::Equal,\n                Punct::NotEqual => TokenValue::NotEqual,\n                Punct::LeftShift => TokenValue::LeftShift,\n                Punct::RightShift => TokenValue::RightShift,\n\n                // Parenthesis or similar\n                Punct::LeftBrace => TokenValue::LeftBrace,\n                Punct::RightBrace => TokenValue::RightBrace,\n                Punct::LeftParen => TokenValue::LeftParen,\n                Punct::RightParen => TokenValue::RightParen,\n                Punct::LeftBracket => TokenValue::LeftBracket,\n                Punct::RightBracket => TokenValue::RightBracket,\n\n                // Other one character punctuation\n                Punct::LeftAngle => TokenValue::LeftAngle,\n                Punct::RightAngle => TokenValue::RightAngle,\n                Punct::Semicolon => TokenValue::Semicolon,\n                Punct::Comma => TokenValue::Comma,\n                Punct::Colon => TokenValue::Colon,\n                Punct::Dot => TokenValue::Dot,\n                Punct::Equal => TokenValue::Assign,\n                Punct::Bang => TokenValue::Bang,\n                Punct::Minus => TokenValue::Dash,\n                Punct::Tilde => TokenValue::Tilde,\n                Punct::Plus => TokenValue::Plus,\n                Punct::Star => TokenValue::Star,\n                Punct::Slash => TokenValue::Slash,\n                Punct::Percent => TokenValue::Percent,\n                Punct::Pipe => TokenValue::VerticalBar,\n                Punct::Caret => TokenValue::Caret,\n                Punct::Ampersand => TokenValue::Ampersand,\n                Punct::Question => TokenValue::Question,\n            },\n            PPTokenValue::Pragma(pragma) => {\n                return Some(LexerResult {\n                    kind: LexerResultKind::Directive(Directive {\n                        kind: DirectiveKind::Pragma,\n                        tokens: pragma.tokens,\n                    }),\n                    meta,\n                })\n            }\n            PPTokenValue::Version(version) => {\n                return Some(LexerResult {\n                    kind: LexerResultKind::Directive(Directive {\n                        kind: DirectiveKind::Version {\n                            is_first_directive: version.is_first_directive,\n                        },\n                        tokens: version.tokens,\n                    }),\n                    meta,\n                })\n            }\n        };\n\n        Some(LexerResult {\n            kind: LexerResultKind::Token(Token { value, meta }),\n            meta,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use alloc::vec;\n\n    use pp_rs::token::{Integer, Location, Token as PPToken, TokenValue as PPTokenValue};\n\n    use super::{\n        super::token::{Directive, DirectiveKind, Token, TokenValue},\n        Lexer, LexerResult, LexerResultKind,\n    };\n    use crate::Span;\n\n    #[test]\n    fn lex_tokens() {\n        let defines = crate::FastHashMap::default();\n\n        // line comments\n        let mut lex = Lexer::new(\"#version 450\\nvoid main () {}\", &defines);\n        let mut location = Location::default();\n        location.start = 9;\n        location.end = 12;\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Directive(Directive {\n                    kind: DirectiveKind::Version {\n                        is_first_directive: true\n                    },\n                    tokens: vec![PPToken {\n                        value: PPTokenValue::Integer(Integer {\n                            signed: true,\n                            value: 450,\n                            width: 32\n                        }),\n                        location\n                    }]\n                }),\n                meta: Span::new(1, 8)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::Void,\n                    meta: Span::new(13, 17)\n                }),\n                meta: Span::new(13, 17)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::Identifier(\"main\".into()),\n                    meta: Span::new(18, 22)\n                }),\n                meta: Span::new(18, 22)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::LeftParen,\n                    meta: Span::new(23, 24)\n                }),\n                meta: Span::new(23, 24)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::RightParen,\n                    meta: Span::new(24, 25)\n                }),\n                meta: Span::new(24, 25)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::LeftBrace,\n                    meta: Span::new(26, 27)\n                }),\n                meta: Span::new(26, 27)\n            }\n        );\n        assert_eq!(\n            lex.next().unwrap(),\n            LexerResult {\n                kind: LexerResultKind::Token(Token {\n                    value: TokenValue::RightBrace,\n                    meta: Span::new(27, 28)\n                }),\n                meta: Span::new(27, 28)\n            }\n        );\n        assert_eq!(lex.next(), None);\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/mod.rs",
    "content": "/*!\nFrontend for [GLSL][glsl] (OpenGL Shading Language).\n\nTo begin, take a look at the documentation for the [`Frontend`].\n\n# Supported versions\n## Vulkan\n- 440 (partial)\n- 450\n- 460\n\n[glsl]: https://www.khronos.org/registry/OpenGL/index_gl.php\n*/\n\npub use ast::{Precision, Profile};\npub use error::{Error, ErrorKind, ExpectedToken, ParseErrors};\npub use token::TokenValue;\n\nuse alloc::{string::String, vec::Vec};\n\nuse crate::{proc::Layouter, FastHashMap, FastHashSet, Handle, Module, ShaderStage, Span, Type};\nuse ast::{EntryArg, FunctionDeclaration, GlobalLookup};\nuse parser::ParsingContext;\n\nmod ast;\nmod builtins;\nmod context;\nmod error;\nmod functions;\nmod lex;\nmod offset;\nmod parser;\n#[cfg(test)]\nmod parser_tests;\nmod token;\nmod types;\nmod variables;\n\ntype Result<T> = core::result::Result<T, Error>;\n\n/// Per-shader options passed to [`parse`](Frontend::parse).\n///\n/// The [`From`] trait is implemented for [`ShaderStage`] to provide a quick way\n/// to create an `Options` instance.\n///\n/// ```rust\n/// # use naga::ShaderStage;\n/// # use naga::front::glsl::Options;\n/// Options::from(ShaderStage::Vertex);\n/// ```\n#[derive(Debug)]\npub struct Options {\n    /// The shader stage in the pipeline.\n    pub stage: ShaderStage,\n    /// Preprocessor definitions to be used, akin to having\n    /// ```glsl\n    /// #define key value\n    /// ```\n    /// for each key value pair in the map.\n    pub defines: FastHashMap<String, String>,\n}\n\nimpl From<ShaderStage> for Options {\n    fn from(stage: ShaderStage) -> Self {\n        Options {\n            stage,\n            defines: FastHashMap::default(),\n        }\n    }\n}\n\n/// Additional information about the GLSL shader.\n///\n/// Stores additional information about the GLSL shader which might not be\n/// stored in the shader [`Module`].\n#[derive(Debug)]\npub struct ShaderMetadata {\n    /// The GLSL version specified in the shader through the use of the\n    /// `#version` preprocessor directive.\n    pub version: u16,\n    /// The GLSL profile specified in the shader through the use of the\n    /// `#version` preprocessor directive.\n    pub profile: Profile,\n    /// The shader stage in the pipeline, passed to the [`parse`](Frontend::parse)\n    /// method via the [`Options`] struct.\n    pub stage: ShaderStage,\n\n    /// The workgroup size for compute shaders, defaults to `[1; 3]` for\n    /// compute shaders and `[0; 3]` for non compute shaders.\n    pub workgroup_size: [u32; 3],\n    /// Whether or not early fragment tests where requested by the shader.\n    /// Defaults to `false`.\n    pub early_fragment_tests: bool,\n\n    /// The shader can request extensions via the\n    /// `#extension` preprocessor directive, in the directive a behavior\n    /// parameter is used to control whether the extension should be disabled,\n    /// warn on usage, enabled if possible or required.\n    ///\n    /// This field only stores extensions which were required or requested to\n    /// be enabled if possible and they are supported.\n    pub extensions: FastHashSet<String>,\n}\n\nimpl ShaderMetadata {\n    fn reset(&mut self, stage: ShaderStage) {\n        self.version = 0;\n        self.profile = Profile::Core;\n        self.stage = stage;\n        self.workgroup_size = [u32::from(stage.compute_like()); 3];\n        self.early_fragment_tests = false;\n        self.extensions.clear();\n    }\n}\n\nimpl Default for ShaderMetadata {\n    fn default() -> Self {\n        ShaderMetadata {\n            version: 0,\n            profile: Profile::Core,\n            stage: ShaderStage::Vertex,\n            workgroup_size: [0; 3],\n            early_fragment_tests: false,\n            extensions: FastHashSet::default(),\n        }\n    }\n}\n\n/// The `Frontend` is the central structure of the GLSL frontend.\n///\n/// To instantiate a new `Frontend` the [`Default`] trait is used, so a\n/// call to the associated function [`Frontend::default`](Frontend::default) will\n/// return a new `Frontend` instance.\n///\n/// To parse a shader simply call the [`parse`](Frontend::parse) method with a\n/// [`Options`] struct and a [`&str`](str) holding the glsl code.\n///\n/// The `Frontend` also provides the [`metadata`](Frontend::metadata) to get some\n/// further information about the previously parsed shader, like version and\n/// extensions used (see the documentation for\n/// [`ShaderMetadata`] to see all the returned information)\n///\n/// # Example usage\n/// ```rust\n/// use naga::ShaderStage;\n/// use naga::front::glsl::{Frontend, Options};\n///\n/// let glsl = r#\"\n///     #version 450 core\n///\n///     void main() {}\n/// \"#;\n///\n/// let mut frontend = Frontend::default();\n/// let options = Options::from(ShaderStage::Vertex);\n/// frontend.parse(&options, glsl);\n/// ```\n///\n/// # Reusability\n///\n/// If there's a need to parse more than one shader reusing the same `Frontend`\n/// instance may be beneficial since internal allocations will be reused.\n///\n/// Calling the [`parse`](Frontend::parse) method multiple times will reset the\n/// `Frontend` so no extra care is needed when reusing.\n#[derive(Debug, Default)]\npub struct Frontend {\n    meta: ShaderMetadata,\n\n    lookup_function: FastHashMap<String, FunctionDeclaration>,\n    lookup_type: FastHashMap<String, Handle<Type>>,\n\n    global_variables: Vec<(String, GlobalLookup)>,\n\n    entry_args: Vec<EntryArg>,\n\n    layouter: Layouter,\n\n    errors: Vec<Error>,\n}\n\nimpl Frontend {\n    fn reset(&mut self, stage: ShaderStage) {\n        self.meta.reset(stage);\n\n        self.lookup_function.clear();\n        self.lookup_type.clear();\n        self.global_variables.clear();\n        self.entry_args.clear();\n        self.layouter.clear();\n    }\n\n    /// Parses a shader either outputting a shader [`Module`] or a list of\n    /// [`Error`]s.\n    ///\n    /// Multiple calls using the same `Frontend` and different shaders are supported.\n    pub fn parse(\n        &mut self,\n        options: &Options,\n        source: &str,\n    ) -> core::result::Result<Module, ParseErrors> {\n        self.reset(options.stage);\n\n        let lexer = lex::Lexer::new(source, &options.defines);\n        let mut ctx = ParsingContext::new(lexer);\n\n        match ctx.parse(self) {\n            Ok(module) => {\n                if self.errors.is_empty() {\n                    Ok(module)\n                } else {\n                    Err(core::mem::take(&mut self.errors).into())\n                }\n            }\n            Err(e) => {\n                self.errors.push(e);\n                Err(core::mem::take(&mut self.errors).into())\n            }\n        }\n    }\n\n    /// Returns additional information about the parsed shader which might not\n    /// be stored in the [`Module`], see the documentation for\n    /// [`ShaderMetadata`] for more information about the returned data.\n    ///\n    /// # Notes\n    ///\n    /// Following an unsuccessful parsing the state of the returned information\n    /// is undefined, it might contain only partial information about the\n    /// current shader, the previous shader or both.\n    pub const fn metadata(&self) -> &ShaderMetadata {\n        &self.meta\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/offset.rs",
    "content": "/*!\nModule responsible for calculating the offset and span for types.\n\nThere exists two types of layouts std140 and std430 (there's technically\ntwo more layouts, shared and packed. Shared is not supported by spirv. Packed is\nimplementation dependent and for now it's just implemented as an alias to\nstd140).\n\nThe OpenGl spec (the layout rules are defined by the OpenGl spec in section\n7.6.2.2 as opposed to the GLSL spec) uses the term basic machine units which are\nequivalent to bytes.\n*/\n\nuse alloc::vec::Vec;\n\nuse super::{\n    ast::StructLayout,\n    error::{Error, ErrorKind},\n    Span,\n};\nuse crate::{proc::Alignment, Handle, Scalar, Type, TypeInner, UniqueArena};\n\n/// Struct with information needed for defining a struct member.\n///\n/// Returned by [`calculate_offset`].\n#[derive(Debug)]\npub struct TypeAlignSpan {\n    /// The handle to the type, this might be the same handle passed to\n    /// [`calculate_offset`] or a new such a new array type with a different\n    /// stride set.\n    pub ty: Handle<Type>,\n    /// The alignment required by the type.\n    pub align: Alignment,\n    /// The size of the type.\n    pub span: u32,\n}\n\n/// Returns the type, alignment and span of a struct member according to a [`StructLayout`].\n///\n/// The functions returns a [`TypeAlignSpan`] which has a `ty` member this\n/// should be used as the struct member type because for example arrays may have\n/// to change the stride and as such need to have a different type.\npub fn calculate_offset(\n    mut ty: Handle<Type>,\n    meta: Span,\n    layout: StructLayout,\n    types: &mut UniqueArena<Type>,\n    errors: &mut Vec<Error>,\n) -> TypeAlignSpan {\n    // When using the std430 storage layout, shader storage blocks will be laid out in buffer storage\n    // identically to uniform and shader storage blocks using the std140 layout, except\n    // that the base alignment and stride of arrays of scalars and vectors in rule 4 and of\n    // structures in rule 9 are not rounded up a multiple of the base alignment of a vec4.\n\n    let (align, span) = match types[ty].inner {\n        // 1. If the member is a scalar consuming N basic machine units,\n        // the base alignment is N.\n        TypeInner::Scalar(Scalar { width, .. }) => (Alignment::from_width(width), width as u32),\n        // 2. If the member is a two- or four-component vector with components\n        // consuming N basic machine units, the base alignment is 2N or 4N, respectively.\n        // 3. If the member is a three-component vector with components consuming N\n        // basic machine units, the base alignment is 4N.\n        TypeInner::Vector {\n            size,\n            scalar: Scalar { width, .. },\n        } => (\n            Alignment::from(size) * Alignment::from_width(width),\n            size as u32 * width as u32,\n        ),\n        // 4. If the member is an array of scalars or vectors, the base alignment and array\n        // stride are set to match the base alignment of a single array element, according\n        // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4.\n        // TODO: Matrices array\n        TypeInner::Array { base, size, .. } => {\n            let info = calculate_offset(base, meta, layout, types, errors);\n\n            let name = types[ty].name.clone();\n\n            // See comment at the beginning of the function\n            let (align, stride) = if StructLayout::Std430 == layout {\n                (info.align, info.align.round_up(info.span))\n            } else {\n                let align = info.align.max(Alignment::MIN_UNIFORM);\n                (align, align.round_up(info.span))\n            };\n\n            let span = match size {\n                crate::ArraySize::Constant(size) => size.get() * stride,\n                crate::ArraySize::Pending(_) => unreachable!(),\n                crate::ArraySize::Dynamic => stride,\n            };\n\n            let ty_span = types.get_span(ty);\n            ty = types.insert(\n                Type {\n                    name,\n                    inner: TypeInner::Array {\n                        base: info.ty,\n                        size,\n                        stride,\n                    },\n                },\n                ty_span,\n            );\n\n            (align, span)\n        }\n        // 5. If the member is a column-major matrix with C columns and R rows, the\n        // matrix is stored identically to an array of C column vectors with R\n        // components each, according to rule (4)\n        // TODO: Row major matrices\n        TypeInner::Matrix {\n            columns,\n            rows,\n            scalar,\n        } => {\n            let mut align = Alignment::from(rows) * Alignment::from_width(scalar.width);\n\n            // See comment at the beginning of the function\n            if StructLayout::Std430 != layout {\n                align = align.max(Alignment::MIN_UNIFORM);\n            }\n\n            // See comment on the error kind\n            if StructLayout::Std140 == layout {\n                // Do the f16 test first, as it's more specific\n                if scalar == Scalar::F16 {\n                    errors.push(Error {\n                        kind: ErrorKind::UnsupportedF16MatrixInStd140 {\n                            columns: columns as u8,\n                            rows: rows as u8,\n                        },\n                        meta,\n                    });\n                }\n                if rows == crate::VectorSize::Bi {\n                    errors.push(Error {\n                        kind: ErrorKind::UnsupportedMatrixWithTwoRowsInStd140 {\n                            columns: columns as u8,\n                        },\n                        meta,\n                    });\n                }\n            }\n\n            (align, align * columns as u32)\n        }\n        TypeInner::Struct { ref members, .. } => {\n            let mut span = 0;\n            let mut align = Alignment::ONE;\n            let mut members = members.clone();\n            let name = types[ty].name.clone();\n\n            for member in members.iter_mut() {\n                let info = calculate_offset(member.ty, meta, layout, types, errors);\n\n                let member_alignment = info.align;\n                span = member_alignment.round_up(span);\n                align = member_alignment.max(align);\n\n                member.ty = info.ty;\n                member.offset = span;\n\n                span += info.span;\n            }\n\n            span = align.round_up(span);\n\n            let ty_span = types.get_span(ty);\n            ty = types.insert(\n                Type {\n                    name,\n                    inner: TypeInner::Struct { members, span },\n                },\n                ty_span,\n            );\n\n            (align, span)\n        }\n        _ => {\n            errors.push(Error {\n                kind: ErrorKind::SemanticError(\"Invalid struct member type\".into()),\n                meta,\n            });\n            (Alignment::ONE, 0)\n        }\n    };\n\n    TypeAlignSpan { ty, align, span }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser/declarations.rs",
    "content": "use alloc::{string::String, vec, vec::Vec};\n\nuse super::{DeclarationContext, ParsingContext, Result};\nuse crate::{\n    front::glsl::{\n        ast::{\n            GlobalLookup, GlobalLookupKind, Precision, QualifierKey, QualifierValue,\n            StorageQualifier, StructLayout, TypeQualifiers,\n        },\n        context::{Context, ExprPos},\n        error::ExpectedToken,\n        offset,\n        token::{Token, TokenValue},\n        types::scalar_components,\n        variables::{GlobalOrConstant, VarDeclaration},\n        Error, ErrorKind, Frontend, Span,\n    },\n    proc::Alignment,\n    AddressSpace, Expression, FunctionResult, Handle, Scalar, ScalarKind, Statement, StructMember,\n    Type, TypeInner,\n};\n\n/// Helper method used to retrieve the child type of `ty` at\n/// index `i`.\n///\n/// # Note\n///\n/// Does not check if the index is valid and returns the same type\n/// when indexing out-of-bounds a struct or indexing a non indexable\n/// type.\nfn element_or_member_type(\n    ty: Handle<Type>,\n    i: usize,\n    types: &mut crate::UniqueArena<Type>,\n) -> Handle<Type> {\n    match types[ty].inner {\n        // The child type of a vector is a scalar of the same kind and width\n        TypeInner::Vector { scalar, .. } => types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(scalar),\n            },\n            Default::default(),\n        ),\n        // The child type of a matrix is a vector of floats with the same\n        // width and the size of the matrix rows.\n        TypeInner::Matrix { rows, scalar, .. } => types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector { size: rows, scalar },\n            },\n            Default::default(),\n        ),\n        // The child type of an array is the base type of the array\n        TypeInner::Array { base, .. } => base,\n        // The child type of a struct at index `i` is the type of it's\n        // member at that same index.\n        //\n        // In case the index is out of bounds the same type is returned\n        TypeInner::Struct { ref members, .. } => {\n            members.get(i).map(|member| member.ty).unwrap_or(ty)\n        }\n        // The type isn't indexable, the same type is returned\n        _ => ty,\n    }\n}\n\nimpl ParsingContext<'_> {\n    pub fn parse_external_declaration(\n        &mut self,\n        frontend: &mut Frontend,\n        global_ctx: &mut Context,\n    ) -> Result<()> {\n        if self\n            .parse_declaration(frontend, global_ctx, true, false)?\n            .is_none()\n        {\n            let token = self.bump(frontend)?;\n            match token.value {\n                TokenValue::Semicolon if frontend.meta.version == 460 => Ok(()),\n                _ => {\n                    let expected = match frontend.meta.version {\n                        460 => vec![TokenValue::Semicolon.into(), ExpectedToken::Eof],\n                        _ => vec![ExpectedToken::Eof],\n                    };\n                    Err(Error {\n                        kind: ErrorKind::InvalidToken(token.value, expected),\n                        meta: token.meta,\n                    })\n                }\n            }\n        } else {\n            Ok(())\n        }\n    }\n\n    pub fn parse_initializer(\n        &mut self,\n        frontend: &mut Frontend,\n        ty: Handle<Type>,\n        ctx: &mut Context,\n    ) -> Result<(Handle<Expression>, Span)> {\n        // initializer:\n        //     assignment_expression\n        //     LEFT_BRACE initializer_list RIGHT_BRACE\n        //     LEFT_BRACE initializer_list COMMA RIGHT_BRACE\n        //\n        // initializer_list:\n        //     initializer\n        //     initializer_list COMMA initializer\n        if let Some(Token { mut meta, .. }) = self.bump_if(frontend, TokenValue::LeftBrace) {\n            // initializer_list\n            let mut components = Vec::new();\n            loop {\n                // The type expected to be parsed inside the initializer list\n                let new_ty = element_or_member_type(ty, components.len(), &mut ctx.module.types);\n\n                components.push(self.parse_initializer(frontend, new_ty, ctx)?.0);\n\n                let token = self.bump(frontend)?;\n                match token.value {\n                    TokenValue::Comma => {\n                        if let Some(Token { meta: end_meta, .. }) =\n                            self.bump_if(frontend, TokenValue::RightBrace)\n                        {\n                            meta.subsume(end_meta);\n                            break;\n                        }\n                    }\n                    TokenValue::RightBrace => {\n                        meta.subsume(token.meta);\n                        break;\n                    }\n                    _ => {\n                        return Err(Error {\n                            kind: ErrorKind::InvalidToken(\n                                token.value,\n                                vec![TokenValue::Comma.into(), TokenValue::RightBrace.into()],\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n                }\n            }\n\n            Ok((\n                ctx.add_expression(Expression::Compose { ty, components }, meta)?,\n                meta,\n            ))\n        } else {\n            let mut stmt = ctx.stmt_ctx();\n            let expr = self.parse_assignment(frontend, ctx, &mut stmt)?;\n            let (mut init, init_meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;\n\n            let scalar_components = scalar_components(&ctx.module.types[ty].inner);\n            if let Some(scalar) = scalar_components {\n                ctx.implicit_conversion(&mut init, init_meta, scalar)?;\n            }\n\n            Ok((init, init_meta))\n        }\n    }\n\n    // Note: caller preparsed the type and qualifiers\n    // Note: caller skips this if the fallthrough token is not expected to be consumed here so this\n    // produced Error::InvalidToken if it isn't consumed\n    pub fn parse_init_declarator_list(\n        &mut self,\n        frontend: &mut Frontend,\n        mut ty: Handle<Type>,\n        ctx: &mut DeclarationContext,\n    ) -> Result<()> {\n        // init_declarator_list:\n        //     single_declaration\n        //     init_declarator_list COMMA IDENTIFIER\n        //     init_declarator_list COMMA IDENTIFIER array_specifier\n        //     init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer\n        //     init_declarator_list COMMA IDENTIFIER EQUAL initializer\n        //\n        // single_declaration:\n        //     fully_specified_type\n        //     fully_specified_type IDENTIFIER\n        //     fully_specified_type IDENTIFIER array_specifier\n        //     fully_specified_type IDENTIFIER array_specifier EQUAL initializer\n        //     fully_specified_type IDENTIFIER EQUAL initializer\n\n        // Consume any leading comma, e.g. this is valid: `float, a=1;`\n        if self\n            .peek(frontend)\n            .is_some_and(|t| t.value == TokenValue::Comma)\n        {\n            self.next(frontend);\n        }\n\n        loop {\n            let token = self.bump(frontend)?;\n            let name = match token.value {\n                TokenValue::Semicolon => break,\n                TokenValue::Identifier(name) => name,\n                _ => {\n                    return Err(Error {\n                        kind: ErrorKind::InvalidToken(\n                            token.value,\n                            vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],\n                        ),\n                        meta: token.meta,\n                    })\n                }\n            };\n            let mut meta = token.meta;\n\n            // array_specifier\n            // array_specifier EQUAL initializer\n            // EQUAL initializer\n\n            // parse an array specifier if it exists\n            // NOTE: unlike other parse methods this one doesn't expect an array specifier and\n            // returns Ok(None) rather than an error if there is not one\n            self.parse_array_specifier(frontend, ctx.ctx, &mut meta, &mut ty)?;\n\n            let is_global_const =\n                ctx.qualifiers.storage.0 == StorageQualifier::Const && ctx.external;\n\n            let init = self\n                .bump_if(frontend, TokenValue::Assign)\n                .map::<Result<_>, _>(|_| {\n                    let prev_const = ctx.ctx.is_const;\n                    ctx.ctx.is_const = is_global_const;\n\n                    let (mut expr, init_meta) = self.parse_initializer(frontend, ty, ctx.ctx)?;\n\n                    let scalar_components = scalar_components(&ctx.ctx.module.types[ty].inner);\n                    if let Some(scalar) = scalar_components {\n                        ctx.ctx.implicit_conversion(&mut expr, init_meta, scalar)?;\n                    }\n\n                    ctx.ctx.is_const = prev_const;\n\n                    meta.subsume(init_meta);\n\n                    Ok(expr)\n                })\n                .transpose()?;\n\n            let decl_initializer;\n            let late_initializer;\n            if is_global_const {\n                decl_initializer = init;\n                late_initializer = None;\n            } else if ctx.external {\n                decl_initializer =\n                    init.and_then(|expr| ctx.ctx.lift_up_const_expression(expr).ok());\n                late_initializer = None;\n            } else if let Some(init) = init {\n                if ctx.is_inside_loop || !ctx.ctx.local_expression_kind_tracker.is_const(init) {\n                    decl_initializer = None;\n                    late_initializer = Some(init);\n                } else {\n                    decl_initializer = Some(init);\n                    late_initializer = None;\n                }\n            } else {\n                decl_initializer = None;\n                late_initializer = None;\n            };\n\n            let pointer = ctx.add_var(frontend, ty, name, decl_initializer, meta)?;\n\n            if let Some(value) = late_initializer {\n                ctx.ctx.emit_restart();\n                ctx.ctx.body.push(Statement::Store { pointer, value }, meta);\n            }\n\n            let token = self.bump(frontend)?;\n            match token.value {\n                TokenValue::Semicolon => break,\n                TokenValue::Comma => {}\n                _ => {\n                    return Err(Error {\n                        kind: ErrorKind::InvalidToken(\n                            token.value,\n                            vec![TokenValue::Comma.into(), TokenValue::Semicolon.into()],\n                        ),\n                        meta: token.meta,\n                    })\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// `external` whether or not we are in a global or local context\n    pub fn parse_declaration(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        external: bool,\n        is_inside_loop: bool,\n    ) -> Result<Option<Span>> {\n        //declaration:\n        //    function_prototype  SEMICOLON\n        //\n        //    init_declarator_list SEMICOLON\n        //    PRECISION precision_qualifier type_specifier SEMICOLON\n        //\n        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON\n        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON\n        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON\n        //    type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON\n        //    type_qualifier IDENTIFIER identifier_list SEMICOLON\n\n        if self.peek_type_qualifier(frontend) || self.peek_type_name(frontend) {\n            let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;\n\n            if self.peek_type_name(frontend) {\n                // This branch handles variables and function prototypes and if\n                // external is true also function definitions\n                let (ty, mut meta) = self.parse_type(frontend, ctx)?;\n\n                let token = self.bump(frontend)?;\n                let token_fallthrough = match token.value {\n                    TokenValue::Identifier(name) => match self.expect_peek(frontend)?.value {\n                        TokenValue::LeftParen => {\n                            // This branch handles function definition and prototypes\n                            self.bump(frontend)?;\n\n                            let result = ty.map(|ty| FunctionResult { ty, binding: None });\n\n                            let mut context = Context::new(\n                                frontend,\n                                ctx.module,\n                                false,\n                                ctx.global_expression_kind_tracker,\n                            )?;\n\n                            self.parse_function_args(frontend, &mut context)?;\n\n                            let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;\n                            meta.subsume(end_meta);\n\n                            let token = self.bump(frontend)?;\n                            return match token.value {\n                                TokenValue::Semicolon => {\n                                    // This branch handles function prototypes\n                                    frontend.add_prototype(context, name, result, meta);\n\n                                    Ok(Some(meta))\n                                }\n                                TokenValue::LeftBrace if external => {\n                                    // This branch handles function definitions\n                                    // as you can see by the guard this branch\n                                    // only happens if external is also true\n\n                                    // parse the body\n                                    self.parse_compound_statement(\n                                        token.meta,\n                                        frontend,\n                                        &mut context,\n                                        &mut None,\n                                        false,\n                                    )?;\n\n                                    frontend.add_function(context, name, result, meta);\n\n                                    Ok(Some(meta))\n                                }\n                                _ if external => Err(Error {\n                                    kind: ErrorKind::InvalidToken(\n                                        token.value,\n                                        vec![\n                                            TokenValue::LeftBrace.into(),\n                                            TokenValue::Semicolon.into(),\n                                        ],\n                                    ),\n                                    meta: token.meta,\n                                }),\n                                _ => Err(Error {\n                                    kind: ErrorKind::InvalidToken(\n                                        token.value,\n                                        vec![TokenValue::Semicolon.into()],\n                                    ),\n                                    meta: token.meta,\n                                }),\n                            };\n                        }\n                        // Pass the token to the init_declarator_list parser\n                        _ => Token {\n                            value: TokenValue::Identifier(name),\n                            meta: token.meta,\n                        },\n                    },\n                    // Pass the token to the init_declarator_list parser\n                    _ => token,\n                };\n\n                // If program execution has reached here then this will be a\n                // init_declarator_list\n                // token_fallthrough will have a token that was already bumped\n                if let Some(ty) = ty {\n                    let mut ctx = DeclarationContext {\n                        qualifiers,\n                        external,\n                        is_inside_loop,\n                        ctx,\n                    };\n\n                    self.backtrack(token_fallthrough)?;\n                    self.parse_init_declarator_list(frontend, ty, &mut ctx)?;\n                } else {\n                    frontend.errors.push(Error {\n                        kind: ErrorKind::SemanticError(\"Declaration cannot have void type\".into()),\n                        meta,\n                    })\n                }\n\n                Ok(Some(meta))\n            } else {\n                // This branch handles struct definitions and modifiers like\n                // ```glsl\n                // layout(early_fragment_tests);\n                // ```\n                let token = self.bump(frontend)?;\n                match token.value {\n                    TokenValue::Identifier(ty_name) => {\n                        if self.bump_if(frontend, TokenValue::LeftBrace).is_some() {\n                            self.parse_block_declaration(\n                                frontend,\n                                ctx,\n                                &mut qualifiers,\n                                ty_name,\n                                token.meta,\n                            )\n                            .map(Some)\n                        } else {\n                            if qualifiers.invariant.take().is_some() {\n                                frontend.make_variable_invariant(ctx, &ty_name, token.meta)?;\n\n                                qualifiers.unused_errors(&mut frontend.errors);\n                                self.expect(frontend, TokenValue::Semicolon)?;\n                                return Ok(Some(qualifiers.span));\n                            }\n\n                            //TODO: declaration\n                            // type_qualifier IDENTIFIER SEMICOLON\n                            // type_qualifier IDENTIFIER identifier_list SEMICOLON\n                            Err(Error {\n                                kind: ErrorKind::NotImplemented(\"variable qualifier\"),\n                                meta: token.meta,\n                            })\n                        }\n                    }\n                    TokenValue::Semicolon => {\n                        if let Some(value) =\n                            qualifiers.uint_layout_qualifier(\"local_size_x\", &mut frontend.errors)\n                        {\n                            frontend.meta.workgroup_size[0] = value;\n                        }\n                        if let Some(value) =\n                            qualifiers.uint_layout_qualifier(\"local_size_y\", &mut frontend.errors)\n                        {\n                            frontend.meta.workgroup_size[1] = value;\n                        }\n                        if let Some(value) =\n                            qualifiers.uint_layout_qualifier(\"local_size_z\", &mut frontend.errors)\n                        {\n                            frontend.meta.workgroup_size[2] = value;\n                        }\n\n                        frontend.meta.early_fragment_tests |= qualifiers\n                            .none_layout_qualifier(\"early_fragment_tests\", &mut frontend.errors);\n\n                        qualifiers.unused_errors(&mut frontend.errors);\n\n                        Ok(Some(qualifiers.span))\n                    }\n                    _ => Err(Error {\n                        kind: ErrorKind::InvalidToken(\n                            token.value,\n                            vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],\n                        ),\n                        meta: token.meta,\n                    }),\n                }\n            }\n        } else {\n            match self.peek(frontend).map(|t| &t.value) {\n                Some(&TokenValue::Precision) => {\n                    // PRECISION precision_qualifier type_specifier SEMICOLON\n                    self.bump(frontend)?;\n\n                    let token = self.bump(frontend)?;\n                    let _ = match token.value {\n                        TokenValue::PrecisionQualifier(p) => p,\n                        _ => {\n                            return Err(Error {\n                                kind: ErrorKind::InvalidToken(\n                                    token.value,\n                                    vec![\n                                        TokenValue::PrecisionQualifier(Precision::High).into(),\n                                        TokenValue::PrecisionQualifier(Precision::Medium).into(),\n                                        TokenValue::PrecisionQualifier(Precision::Low).into(),\n                                    ],\n                                ),\n                                meta: token.meta,\n                            })\n                        }\n                    };\n\n                    let (ty, meta) = self.parse_type_non_void(frontend, ctx)?;\n\n                    match ctx.module.types[ty].inner {\n                        TypeInner::Scalar(Scalar {\n                            kind: ScalarKind::Float | ScalarKind::Sint,\n                            ..\n                        }) => {}\n                        _ => frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Precision statement can only work on floats and ints\".into(),\n                            ),\n                            meta,\n                        }),\n                    }\n\n                    self.expect(frontend, TokenValue::Semicolon)?;\n\n                    Ok(Some(meta))\n                }\n                _ => Ok(None),\n            }\n        }\n    }\n\n    pub fn parse_block_declaration(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        qualifiers: &mut TypeQualifiers,\n        ty_name: String,\n        mut meta: Span,\n    ) -> Result<Span> {\n        let layout = match qualifiers.layout_qualifiers.remove(&QualifierKey::Layout) {\n            Some((QualifierValue::Layout(l), _)) => l,\n            None => {\n                if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) =\n                    qualifiers.storage.0\n                {\n                    StructLayout::Std430\n                } else {\n                    StructLayout::Std140\n                }\n            }\n            _ => unreachable!(),\n        };\n\n        let mut members = Vec::new();\n        let span = self.parse_struct_declaration_list(frontend, ctx, &mut members, layout)?;\n        self.expect(frontend, TokenValue::RightBrace)?;\n\n        let mut ty = ctx.module.types.insert(\n            Type {\n                name: Some(ty_name),\n                inner: TypeInner::Struct {\n                    members: members.clone(),\n                    span,\n                },\n            },\n            Default::default(),\n        );\n\n        let token = self.bump(frontend)?;\n        let name = match token.value {\n            TokenValue::Semicolon => None,\n            TokenValue::Identifier(name) => {\n                self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;\n\n                self.expect(frontend, TokenValue::Semicolon)?;\n\n                Some(name)\n            }\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::InvalidToken(\n                        token.value,\n                        vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],\n                    ),\n                    meta: token.meta,\n                })\n            }\n        };\n\n        let global = frontend.add_global_var(\n            ctx,\n            VarDeclaration {\n                qualifiers,\n                ty,\n                name,\n                init: None,\n                meta,\n            },\n        )?;\n\n        for (i, k, ty) in members.into_iter().enumerate().filter_map(|(i, m)| {\n            let ty = m.ty;\n            m.name.map(|s| (i as u32, s, ty))\n        }) {\n            let lookup = GlobalLookup {\n                kind: match global {\n                    GlobalOrConstant::Global(handle) => GlobalLookupKind::BlockSelect(handle, i),\n                    GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle, ty),\n                    GlobalOrConstant::Override(handle) => GlobalLookupKind::Override(handle, ty),\n                },\n                entry_arg: None,\n                mutable: true,\n            };\n            ctx.add_global(&k, lookup)?;\n\n            frontend.global_variables.push((k, lookup));\n        }\n\n        Ok(meta)\n    }\n\n    // TODO: Accept layout arguments\n    pub fn parse_struct_declaration_list(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        members: &mut Vec<StructMember>,\n        layout: StructLayout,\n    ) -> Result<u32> {\n        let mut span = 0;\n        let mut align = Alignment::ONE;\n\n        loop {\n            // TODO: type_qualifier\n\n            let (base_ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;\n\n            loop {\n                let (name, name_meta) = self.expect_ident(frontend)?;\n                let mut ty = base_ty;\n                self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;\n\n                meta.subsume(name_meta);\n\n                let info = offset::calculate_offset(\n                    ty,\n                    meta,\n                    layout,\n                    &mut ctx.module.types,\n                    &mut frontend.errors,\n                );\n\n                let member_alignment = info.align;\n                span = member_alignment.round_up(span);\n                align = member_alignment.max(align);\n\n                members.push(StructMember {\n                    name: Some(name),\n                    ty: info.ty,\n                    binding: None,\n                    offset: span,\n                });\n\n                span += info.span;\n\n                if self.bump_if(frontend, TokenValue::Comma).is_none() {\n                    break;\n                }\n            }\n\n            self.expect(frontend, TokenValue::Semicolon)?;\n\n            if let TokenValue::RightBrace = self.expect_peek(frontend)?.value {\n                break;\n            }\n        }\n\n        span = align.round_up(span);\n\n        Ok(span)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser/expressions.rs",
    "content": "use alloc::{vec, vec::Vec};\nuse core::num::NonZeroU32;\n\nuse crate::{\n    front::glsl::{\n        ast::{FunctionCall, FunctionCallKind, HirExpr, HirExprKind},\n        context::{Context, StmtContext},\n        error::{ErrorKind, ExpectedToken},\n        parser::ParsingContext,\n        token::{Token, TokenValue},\n        Error, Frontend, Result, Span,\n    },\n    ArraySize, BinaryOperator, Handle, Literal, Type, TypeInner, UnaryOperator,\n};\n\nimpl ParsingContext<'_> {\n    pub fn parse_primary(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n    ) -> Result<Handle<HirExpr>> {\n        let mut token = self.bump(frontend)?;\n\n        let literal = match token.value {\n            TokenValue::IntConstant(int) => {\n                if int.width != 32 {\n                    frontend.errors.push(Error {\n                        kind: ErrorKind::SemanticError(\"Unsupported non-32bit integer\".into()),\n                        meta: token.meta,\n                    });\n                }\n                if int.signed {\n                    Literal::I32(int.value as i32)\n                } else {\n                    Literal::U32(int.value as u32)\n                }\n            }\n            TokenValue::FloatConstant(float) => {\n                if float.width != 32 {\n                    frontend.errors.push(Error {\n                        kind: ErrorKind::SemanticError(\n                            concat!(\n                                \"Unsupported floating-point value \",\n                                \"(expected single-precision floating-point number)\"\n                            )\n                            .into(),\n                        ),\n                        meta: token.meta,\n                    });\n                }\n                Literal::F32(float.value)\n            }\n            TokenValue::BoolConstant(value) => Literal::Bool(value),\n            TokenValue::LeftParen => {\n                let expr = self.parse_expression(frontend, ctx, stmt)?;\n                let meta = self.expect(frontend, TokenValue::RightParen)?.meta;\n\n                token.meta.subsume(meta);\n\n                return Ok(expr);\n            }\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::InvalidToken(\n                        token.value,\n                        vec![\n                            TokenValue::LeftParen.into(),\n                            ExpectedToken::IntLiteral,\n                            ExpectedToken::FloatLiteral,\n                            ExpectedToken::BoolLiteral,\n                        ],\n                    ),\n                    meta: token.meta,\n                });\n            }\n        };\n\n        Ok(stmt.hir_exprs.append(\n            HirExpr {\n                kind: HirExprKind::Literal(literal),\n                meta: token.meta,\n            },\n            Default::default(),\n        ))\n    }\n\n    pub fn parse_function_call_args(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n        meta: &mut Span,\n    ) -> Result<Vec<Handle<HirExpr>>> {\n        let mut args = Vec::new();\n        if let Some(token) = self.bump_if(frontend, TokenValue::RightParen) {\n            meta.subsume(token.meta);\n        } else {\n            loop {\n                args.push(self.parse_assignment(frontend, ctx, stmt)?);\n\n                let token = self.bump(frontend)?;\n                match token.value {\n                    TokenValue::Comma => {}\n                    TokenValue::RightParen => {\n                        meta.subsume(token.meta);\n                        break;\n                    }\n                    _ => {\n                        return Err(Error {\n                            kind: ErrorKind::InvalidToken(\n                                token.value,\n                                vec![TokenValue::Comma.into(), TokenValue::RightParen.into()],\n                            ),\n                            meta: token.meta,\n                        });\n                    }\n                }\n            }\n        }\n\n        Ok(args)\n    }\n\n    pub fn parse_postfix(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n    ) -> Result<Handle<HirExpr>> {\n        let mut base = if self.peek_type_name(frontend) {\n            let (mut handle, mut meta) = self.parse_type_non_void(frontend, ctx)?;\n\n            self.expect(frontend, TokenValue::LeftParen)?;\n            let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;\n\n            if let TypeInner::Array {\n                size: ArraySize::Dynamic,\n                stride,\n                base,\n            } = ctx.module.types[handle].inner\n            {\n                let span = ctx.module.types.get_span(handle);\n\n                let size = u32::try_from(args.len())\n                    .ok()\n                    .and_then(NonZeroU32::new)\n                    .ok_or(Error {\n                        kind: ErrorKind::SemanticError(\n                            \"There must be at least one argument\".into(),\n                        ),\n                        meta,\n                    })?;\n\n                handle = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Array {\n                            stride,\n                            base,\n                            size: ArraySize::Constant(size),\n                        },\n                    },\n                    span,\n                )\n            }\n\n            stmt.hir_exprs.append(\n                HirExpr {\n                    kind: HirExprKind::Call(FunctionCall {\n                        kind: FunctionCallKind::TypeConstructor(handle),\n                        args,\n                    }),\n                    meta,\n                },\n                Default::default(),\n            )\n        } else if let TokenValue::Identifier(_) = self.expect_peek(frontend)?.value {\n            let (name, mut meta) = self.expect_ident(frontend)?;\n\n            let expr = if self.bump_if(frontend, TokenValue::LeftParen).is_some() {\n                let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;\n\n                let kind = match frontend.lookup_type.get(&name) {\n                    Some(ty) => FunctionCallKind::TypeConstructor(*ty),\n                    None => FunctionCallKind::Function(name),\n                };\n\n                HirExpr {\n                    kind: HirExprKind::Call(FunctionCall { kind, args }),\n                    meta,\n                }\n            } else {\n                let var = match frontend.lookup_variable(ctx, &name, meta)? {\n                    Some(var) => var,\n                    None => {\n                        return Err(Error {\n                            kind: ErrorKind::UnknownVariable(name),\n                            meta,\n                        })\n                    }\n                };\n\n                HirExpr {\n                    kind: HirExprKind::Variable(var),\n                    meta,\n                }\n            };\n\n            stmt.hir_exprs.append(expr, Default::default())\n        } else {\n            self.parse_primary(frontend, ctx, stmt)?\n        };\n\n        while let TokenValue::LeftBracket\n        | TokenValue::Dot\n        | TokenValue::Increment\n        | TokenValue::Decrement = self.expect_peek(frontend)?.value\n        {\n            let Token { value, mut meta } = self.bump(frontend)?;\n\n            match value {\n                TokenValue::LeftBracket => {\n                    let index = self.parse_expression(frontend, ctx, stmt)?;\n                    let end_meta = self.expect(frontend, TokenValue::RightBracket)?.meta;\n\n                    meta.subsume(end_meta);\n                    base = stmt.hir_exprs.append(\n                        HirExpr {\n                            kind: HirExprKind::Access { base, index },\n                            meta,\n                        },\n                        Default::default(),\n                    )\n                }\n                TokenValue::Dot => {\n                    let (field, end_meta) = self.expect_ident(frontend)?;\n\n                    if self.bump_if(frontend, TokenValue::LeftParen).is_some() {\n                        let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;\n\n                        base = stmt.hir_exprs.append(\n                            HirExpr {\n                                kind: HirExprKind::Method {\n                                    expr: base,\n                                    name: field,\n                                    args,\n                                },\n                                meta,\n                            },\n                            Default::default(),\n                        );\n                        continue;\n                    }\n\n                    meta.subsume(end_meta);\n                    base = stmt.hir_exprs.append(\n                        HirExpr {\n                            kind: HirExprKind::Select { base, field },\n                            meta,\n                        },\n                        Default::default(),\n                    )\n                }\n                TokenValue::Increment | TokenValue::Decrement => {\n                    base = stmt.hir_exprs.append(\n                        HirExpr {\n                            kind: HirExprKind::PrePostfix {\n                                op: match value {\n                                    TokenValue::Increment => BinaryOperator::Add,\n                                    _ => BinaryOperator::Subtract,\n                                },\n                                postfix: true,\n                                expr: base,\n                            },\n                            meta,\n                        },\n                        Default::default(),\n                    )\n                }\n                _ => unreachable!(),\n            }\n        }\n\n        Ok(base)\n    }\n\n    pub fn parse_unary(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n    ) -> Result<Handle<HirExpr>> {\n        Ok(match self.expect_peek(frontend)?.value {\n            TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => {\n                let Token { value, mut meta } = self.bump(frontend)?;\n\n                let expr = self.parse_unary(frontend, ctx, stmt)?;\n                let end_meta = stmt.hir_exprs[expr].meta;\n\n                let kind = match value {\n                    TokenValue::Dash => HirExprKind::Unary {\n                        op: UnaryOperator::Negate,\n                        expr,\n                    },\n                    TokenValue::Bang => HirExprKind::Unary {\n                        op: UnaryOperator::LogicalNot,\n                        expr,\n                    },\n                    TokenValue::Tilde => HirExprKind::Unary {\n                        op: UnaryOperator::BitwiseNot,\n                        expr,\n                    },\n                    _ => return Ok(expr),\n                };\n\n                meta.subsume(end_meta);\n                stmt.hir_exprs\n                    .append(HirExpr { kind, meta }, Default::default())\n            }\n            TokenValue::Increment | TokenValue::Decrement => {\n                let Token { value, meta } = self.bump(frontend)?;\n\n                let expr = self.parse_unary(frontend, ctx, stmt)?;\n\n                stmt.hir_exprs.append(\n                    HirExpr {\n                        kind: HirExprKind::PrePostfix {\n                            op: match value {\n                                TokenValue::Increment => BinaryOperator::Add,\n                                _ => BinaryOperator::Subtract,\n                            },\n                            postfix: false,\n                            expr,\n                        },\n                        meta,\n                    },\n                    Default::default(),\n                )\n            }\n            _ => self.parse_postfix(frontend, ctx, stmt)?,\n        })\n    }\n\n    pub fn parse_binary(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n        passthrough: Option<Handle<HirExpr>>,\n        min_bp: u8,\n    ) -> Result<Handle<HirExpr>> {\n        let mut left = passthrough\n            .ok_or(ErrorKind::EndOfFile /* Dummy error */)\n            .or_else(|_| self.parse_unary(frontend, ctx, stmt))?;\n        let mut meta = stmt.hir_exprs[left].meta;\n\n        while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek(frontend)?.value) {\n            if l_bp < min_bp {\n                break;\n            }\n\n            let Token { value, .. } = self.bump(frontend)?;\n\n            let right = self.parse_binary(frontend, ctx, stmt, None, r_bp)?;\n            let end_meta = stmt.hir_exprs[right].meta;\n\n            meta.subsume(end_meta);\n            left = stmt.hir_exprs.append(\n                HirExpr {\n                    kind: HirExprKind::Binary {\n                        left,\n                        op: match value {\n                            TokenValue::LogicalOr => BinaryOperator::LogicalOr,\n                            TokenValue::LogicalXor => BinaryOperator::NotEqual,\n                            TokenValue::LogicalAnd => BinaryOperator::LogicalAnd,\n                            TokenValue::VerticalBar => BinaryOperator::InclusiveOr,\n                            TokenValue::Caret => BinaryOperator::ExclusiveOr,\n                            TokenValue::Ampersand => BinaryOperator::And,\n                            TokenValue::Equal => BinaryOperator::Equal,\n                            TokenValue::NotEqual => BinaryOperator::NotEqual,\n                            TokenValue::GreaterEqual => BinaryOperator::GreaterEqual,\n                            TokenValue::LessEqual => BinaryOperator::LessEqual,\n                            TokenValue::LeftAngle => BinaryOperator::Less,\n                            TokenValue::RightAngle => BinaryOperator::Greater,\n                            TokenValue::LeftShift => BinaryOperator::ShiftLeft,\n                            TokenValue::RightShift => BinaryOperator::ShiftRight,\n                            TokenValue::Plus => BinaryOperator::Add,\n                            TokenValue::Dash => BinaryOperator::Subtract,\n                            TokenValue::Star => BinaryOperator::Multiply,\n                            TokenValue::Slash => BinaryOperator::Divide,\n                            TokenValue::Percent => BinaryOperator::Modulo,\n                            _ => unreachable!(),\n                        },\n                        right,\n                    },\n                    meta,\n                },\n                Default::default(),\n            )\n        }\n\n        Ok(left)\n    }\n\n    pub fn parse_conditional(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n        passthrough: Option<Handle<HirExpr>>,\n    ) -> Result<Handle<HirExpr>> {\n        let mut condition = self.parse_binary(frontend, ctx, stmt, passthrough, 0)?;\n        let mut meta = stmt.hir_exprs[condition].meta;\n\n        if self.bump_if(frontend, TokenValue::Question).is_some() {\n            let accept = self.parse_expression(frontend, ctx, stmt)?;\n            self.expect(frontend, TokenValue::Colon)?;\n            let reject = self.parse_assignment(frontend, ctx, stmt)?;\n            let end_meta = stmt.hir_exprs[reject].meta;\n\n            meta.subsume(end_meta);\n            condition = stmt.hir_exprs.append(\n                HirExpr {\n                    kind: HirExprKind::Conditional {\n                        condition,\n                        accept,\n                        reject,\n                    },\n                    meta,\n                },\n                Default::default(),\n            )\n        }\n\n        Ok(condition)\n    }\n\n    pub fn parse_assignment(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n    ) -> Result<Handle<HirExpr>> {\n        let tgt = self.parse_unary(frontend, ctx, stmt)?;\n        let mut meta = stmt.hir_exprs[tgt].meta;\n\n        Ok(match self.expect_peek(frontend)?.value {\n            TokenValue::Assign => {\n                self.bump(frontend)?;\n                let value = self.parse_assignment(frontend, ctx, stmt)?;\n                let end_meta = stmt.hir_exprs[value].meta;\n\n                meta.subsume(end_meta);\n                stmt.hir_exprs.append(\n                    HirExpr {\n                        kind: HirExprKind::Assign { tgt, value },\n                        meta,\n                    },\n                    Default::default(),\n                )\n            }\n            TokenValue::OrAssign\n            | TokenValue::AndAssign\n            | TokenValue::AddAssign\n            | TokenValue::DivAssign\n            | TokenValue::ModAssign\n            | TokenValue::SubAssign\n            | TokenValue::MulAssign\n            | TokenValue::LeftShiftAssign\n            | TokenValue::RightShiftAssign\n            | TokenValue::XorAssign => {\n                let token = self.bump(frontend)?;\n                let right = self.parse_assignment(frontend, ctx, stmt)?;\n                let end_meta = stmt.hir_exprs[right].meta;\n\n                meta.subsume(end_meta);\n                let value = stmt.hir_exprs.append(\n                    HirExpr {\n                        meta,\n                        kind: HirExprKind::Binary {\n                            left: tgt,\n                            op: match token.value {\n                                TokenValue::OrAssign => BinaryOperator::InclusiveOr,\n                                TokenValue::AndAssign => BinaryOperator::And,\n                                TokenValue::AddAssign => BinaryOperator::Add,\n                                TokenValue::DivAssign => BinaryOperator::Divide,\n                                TokenValue::ModAssign => BinaryOperator::Modulo,\n                                TokenValue::SubAssign => BinaryOperator::Subtract,\n                                TokenValue::MulAssign => BinaryOperator::Multiply,\n                                TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft,\n                                TokenValue::RightShiftAssign => BinaryOperator::ShiftRight,\n                                TokenValue::XorAssign => BinaryOperator::ExclusiveOr,\n                                _ => unreachable!(),\n                            },\n                            right,\n                        },\n                    },\n                    Default::default(),\n                );\n\n                stmt.hir_exprs.append(\n                    HirExpr {\n                        kind: HirExprKind::Assign { tgt, value },\n                        meta,\n                    },\n                    Default::default(),\n                )\n            }\n            _ => self.parse_conditional(frontend, ctx, stmt, Some(tgt))?,\n        })\n    }\n\n    pub fn parse_expression(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        stmt: &mut StmtContext,\n    ) -> Result<Handle<HirExpr>> {\n        let mut exprs = Vec::new();\n        let mut expr = self.parse_assignment(frontend, ctx, stmt)?;\n        exprs.push(expr);\n\n        while let TokenValue::Comma = self.expect_peek(frontend)?.value {\n            self.bump(frontend)?;\n            expr = self.parse_assignment(frontend, ctx, stmt)?;\n            exprs.push(expr);\n        }\n\n        if exprs.len() == 1 {\n            Ok(expr)\n        } else {\n            let mut meta = stmt.hir_exprs[exprs[0]].meta;\n            for &e in &exprs[1..] {\n                meta.subsume(stmt.hir_exprs[e].meta);\n            }\n            expr = stmt.hir_exprs.append(\n                HirExpr {\n                    kind: HirExprKind::Sequence { exprs },\n                    meta,\n                },\n                Default::default(),\n            );\n\n            Ok(expr)\n        }\n    }\n}\n\nconst fn binding_power(value: &TokenValue) -> Option<(u8, u8)> {\n    Some(match *value {\n        TokenValue::LogicalOr => (1, 2),\n        TokenValue::LogicalXor => (3, 4),\n        TokenValue::LogicalAnd => (5, 6),\n        TokenValue::VerticalBar => (7, 8),\n        TokenValue::Caret => (9, 10),\n        TokenValue::Ampersand => (11, 12),\n        TokenValue::Equal | TokenValue::NotEqual => (13, 14),\n        TokenValue::GreaterEqual\n        | TokenValue::LessEqual\n        | TokenValue::LeftAngle\n        | TokenValue::RightAngle => (15, 16),\n        TokenValue::LeftShift | TokenValue::RightShift => (17, 18),\n        TokenValue::Plus | TokenValue::Dash => (19, 20),\n        TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22),\n        _ => return None,\n    })\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser/functions.rs",
    "content": "use alloc::{vec, vec::Vec};\n\nuse crate::front::glsl::context::ExprPos;\nuse crate::front::glsl::Span;\nuse crate::Literal;\nuse crate::{\n    front::glsl::{\n        ast::ParameterQualifier,\n        context::Context,\n        parser::ParsingContext,\n        token::{Token, TokenValue},\n        variables::VarDeclaration,\n        Error, ErrorKind, Frontend, Result,\n    },\n    Block, Expression, Statement, SwitchCase, UnaryOperator,\n};\n\nimpl ParsingContext<'_> {\n    pub fn peek_parameter_qualifier(&mut self, frontend: &mut Frontend) -> bool {\n        self.peek(frontend).is_some_and(|t| match t.value {\n            TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true,\n            _ => false,\n        })\n    }\n\n    /// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In`\n    pub fn parse_parameter_qualifier(&mut self, frontend: &mut Frontend) -> ParameterQualifier {\n        if self.peek_parameter_qualifier(frontend) {\n            match self.bump(frontend).unwrap().value {\n                TokenValue::In => ParameterQualifier::In,\n                TokenValue::Out => ParameterQualifier::Out,\n                TokenValue::InOut => ParameterQualifier::InOut,\n                TokenValue::Const => ParameterQualifier::Const,\n                _ => unreachable!(),\n            }\n        } else {\n            ParameterQualifier::In\n        }\n    }\n\n    pub fn parse_statement(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        terminator: &mut Option<usize>,\n        is_inside_loop: bool,\n    ) -> Result<Option<Span>> {\n        // Type qualifiers always identify a declaration statement\n        if self.peek_type_qualifier(frontend) {\n            return self.parse_declaration(frontend, ctx, false, is_inside_loop);\n        }\n\n        // Type names can identify either declaration statements or type constructors\n        // depending on whether the token following the type name is a `(` (LeftParen)\n        if self.peek_type_name(frontend) {\n            // Start by consuming the type name so that we can peek the token after it\n            let token = self.bump(frontend)?;\n            // Peek the next token and check if it's a `(` (LeftParen) if so the statement\n            // is a constructor, otherwise it's a declaration. We need to do the check\n            // beforehand and not in the if since we will backtrack before the if\n            let declaration = TokenValue::LeftParen != self.expect_peek(frontend)?.value;\n\n            self.backtrack(token)?;\n\n            if declaration {\n                return self.parse_declaration(frontend, ctx, false, is_inside_loop);\n            }\n        }\n\n        let new_break = || {\n            let mut block = Block::new();\n            block.push(Statement::Break, Span::default());\n            block\n        };\n\n        let &Token {\n            ref value,\n            mut meta,\n        } = self.expect_peek(frontend)?;\n\n        let meta_rest = match *value {\n            TokenValue::Continue => {\n                let meta = self.bump(frontend)?.meta;\n                ctx.body.push(Statement::Continue, meta);\n                terminator.get_or_insert(ctx.body.len());\n                self.expect(frontend, TokenValue::Semicolon)?.meta\n            }\n            TokenValue::Break => {\n                let meta = self.bump(frontend)?.meta;\n                ctx.body.push(Statement::Break, meta);\n                terminator.get_or_insert(ctx.body.len());\n                self.expect(frontend, TokenValue::Semicolon)?.meta\n            }\n            TokenValue::Return => {\n                self.bump(frontend)?;\n                let (value, meta) = match self.expect_peek(frontend)?.value {\n                    TokenValue::Semicolon => (None, self.bump(frontend)?.meta),\n                    _ => {\n                        // TODO: Implicit conversions\n                        let mut stmt = ctx.stmt_ctx();\n                        let expr = self.parse_expression(frontend, ctx, &mut stmt)?;\n                        self.expect(frontend, TokenValue::Semicolon)?;\n                        let (handle, meta) =\n                            ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;\n                        (Some(handle), meta)\n                    }\n                };\n\n                ctx.emit_restart();\n\n                ctx.body.push(Statement::Return { value }, meta);\n                terminator.get_or_insert(ctx.body.len());\n\n                meta\n            }\n            TokenValue::Discard => {\n                let meta = self.bump(frontend)?.meta;\n                ctx.body.push(Statement::Kill, meta);\n                terminator.get_or_insert(ctx.body.len());\n\n                self.expect(frontend, TokenValue::Semicolon)?.meta\n            }\n            TokenValue::If => {\n                let mut meta = self.bump(frontend)?.meta;\n\n                self.expect(frontend, TokenValue::LeftParen)?;\n                let condition = {\n                    let mut stmt = ctx.stmt_ctx();\n                    let expr = self.parse_expression(frontend, ctx, &mut stmt)?;\n                    let (handle, more_meta) =\n                        ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;\n                    meta.subsume(more_meta);\n                    handle\n                };\n                self.expect(frontend, TokenValue::RightParen)?;\n\n                let accept = ctx.new_body(|ctx| {\n                    if let Some(more_meta) =\n                        self.parse_statement(frontend, ctx, &mut None, is_inside_loop)?\n                    {\n                        meta.subsume(more_meta);\n                    }\n                    Ok(())\n                })?;\n\n                let reject = ctx.new_body(|ctx| {\n                    if self.bump_if(frontend, TokenValue::Else).is_some() {\n                        if let Some(more_meta) =\n                            self.parse_statement(frontend, ctx, &mut None, is_inside_loop)?\n                        {\n                            meta.subsume(more_meta);\n                        }\n                    }\n                    Ok(())\n                })?;\n\n                ctx.body.push(\n                    Statement::If {\n                        condition,\n                        accept,\n                        reject,\n                    },\n                    meta,\n                );\n\n                meta\n            }\n            TokenValue::Switch => {\n                let mut meta = self.bump(frontend)?.meta;\n                let end_meta;\n\n                self.expect(frontend, TokenValue::LeftParen)?;\n\n                let (selector, uint) = {\n                    let mut stmt = ctx.stmt_ctx();\n                    let expr = self.parse_expression(frontend, ctx, &mut stmt)?;\n                    let (root, meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;\n                    let uint = ctx.resolve_type(root, meta)?.scalar_kind()\n                        == Some(crate::ScalarKind::Uint);\n                    (root, uint)\n                };\n\n                self.expect(frontend, TokenValue::RightParen)?;\n\n                ctx.emit_restart();\n\n                let mut cases = Vec::new();\n                // Track if any default case is present in the switch statement.\n                let mut default_present = false;\n\n                self.expect(frontend, TokenValue::LeftBrace)?;\n                loop {\n                    let value = match self.expect_peek(frontend)?.value {\n                        TokenValue::Case => {\n                            self.bump(frontend)?;\n\n                            let (const_expr, meta) = self.parse_constant_expression(\n                                frontend,\n                                ctx.module,\n                                ctx.global_expression_kind_tracker,\n                            )?;\n\n                            match ctx.module.global_expressions[const_expr] {\n                                Expression::Literal(Literal::I32(value)) => match uint {\n                                    // This unchecked cast isn't good, but since\n                                    // we only reach this code when the selector\n                                    // is unsigned but the case label is signed,\n                                    // verification will reject the module\n                                    // anyway (which also matches GLSL's rules).\n                                    true => crate::SwitchValue::U32(value as u32),\n                                    false => crate::SwitchValue::I32(value),\n                                },\n                                Expression::Literal(Literal::U32(value)) => {\n                                    crate::SwitchValue::U32(value)\n                                }\n                                _ => {\n                                    frontend.errors.push(Error {\n                                        kind: ErrorKind::SemanticError(\n                                            \"Case values can only be integers\".into(),\n                                        ),\n                                        meta,\n                                    });\n\n                                    crate::SwitchValue::I32(0)\n                                }\n                            }\n                        }\n                        TokenValue::Default => {\n                            self.bump(frontend)?;\n                            default_present = true;\n                            crate::SwitchValue::Default\n                        }\n                        TokenValue::RightBrace => {\n                            end_meta = self.bump(frontend)?.meta;\n                            break;\n                        }\n                        _ => {\n                            let Token { value, meta } = self.bump(frontend)?;\n                            return Err(Error {\n                                kind: ErrorKind::InvalidToken(\n                                    value,\n                                    vec![\n                                        TokenValue::Case.into(),\n                                        TokenValue::Default.into(),\n                                        TokenValue::RightBrace.into(),\n                                    ],\n                                ),\n                                meta,\n                            });\n                        }\n                    };\n\n                    self.expect(frontend, TokenValue::Colon)?;\n\n                    let mut fall_through = true;\n\n                    let body = ctx.new_body(|ctx| {\n                        let mut case_terminator = None;\n                        loop {\n                            match self.expect_peek(frontend)?.value {\n                                TokenValue::Case | TokenValue::Default | TokenValue::RightBrace => {\n                                    break\n                                }\n                                _ => {\n                                    self.parse_statement(\n                                        frontend,\n                                        ctx,\n                                        &mut case_terminator,\n                                        is_inside_loop,\n                                    )?;\n                                }\n                            }\n                        }\n\n                        if let Some(mut idx) = case_terminator {\n                            if let Statement::Break = ctx.body[idx - 1] {\n                                fall_through = false;\n                                idx -= 1;\n                            }\n\n                            ctx.body.cull(idx..)\n                        }\n\n                        Ok(())\n                    })?;\n\n                    cases.push(SwitchCase {\n                        value,\n                        body,\n                        fall_through,\n                    })\n                }\n\n                meta.subsume(end_meta);\n\n                // NOTE: do not unwrap here since a switch statement isn't required\n                // to have any cases.\n                if let Some(case) = cases.last_mut() {\n                    // GLSL requires that the last case not be empty, so we check\n                    // that here and produce an error otherwise (fall_through must\n                    // also be checked because `break`s count as statements but\n                    // they aren't added to the body)\n                    if case.body.is_empty() && case.fall_through {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"last case/default label must be followed by statements\".into(),\n                            ),\n                            meta,\n                        })\n                    }\n\n                    // GLSL allows the last case to not have any `break` statement,\n                    // this would mark it as fall through but naga's IR requires that\n                    // the last case must not be fall through, so we mark need to mark\n                    // the last case as not fall through always.\n                    case.fall_through = false;\n                }\n\n                // Add an empty default case in case non was present, this is needed because\n                // naga's IR requires that all switch statements must have a default case but\n                // GLSL doesn't require that, so we might need to add an empty default case.\n                if !default_present {\n                    cases.push(SwitchCase {\n                        value: crate::SwitchValue::Default,\n                        body: Block::new(),\n                        fall_through: false,\n                    })\n                }\n\n                ctx.body.push(Statement::Switch { selector, cases }, meta);\n\n                meta\n            }\n            TokenValue::While => {\n                let mut meta = self.bump(frontend)?.meta;\n\n                let loop_body = ctx.new_body(|ctx| {\n                    let mut stmt = ctx.stmt_ctx();\n                    self.expect(frontend, TokenValue::LeftParen)?;\n                    let root = self.parse_expression(frontend, ctx, &mut stmt)?;\n                    meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);\n\n                    let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;\n                    let condition = ctx.add_expression(\n                        Expression::Unary {\n                            op: UnaryOperator::LogicalNot,\n                            expr,\n                        },\n                        expr_meta,\n                    )?;\n\n                    ctx.emit_restart();\n\n                    ctx.body.push(\n                        Statement::If {\n                            condition,\n                            accept: new_break(),\n                            reject: Block::new(),\n                        },\n                        Span::default(),\n                    );\n\n                    meta.subsume(expr_meta);\n\n                    if let Some(body_meta) = self.parse_statement(frontend, ctx, &mut None, true)? {\n                        meta.subsume(body_meta);\n                    }\n                    Ok(())\n                })?;\n\n                ctx.body.push(\n                    Statement::Loop {\n                        body: loop_body,\n                        continuing: Block::new(),\n                        break_if: None,\n                    },\n                    meta,\n                );\n\n                meta\n            }\n            TokenValue::Do => {\n                let mut meta = self.bump(frontend)?.meta;\n\n                let loop_body = ctx.new_body(|ctx| {\n                    let mut terminator = None;\n                    self.parse_statement(frontend, ctx, &mut terminator, true)?;\n\n                    let mut stmt = ctx.stmt_ctx();\n\n                    self.expect(frontend, TokenValue::While)?;\n                    self.expect(frontend, TokenValue::LeftParen)?;\n                    let root = self.parse_expression(frontend, ctx, &mut stmt)?;\n                    let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;\n\n                    meta.subsume(end_meta);\n\n                    let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;\n                    let condition = ctx.add_expression(\n                        Expression::Unary {\n                            op: UnaryOperator::LogicalNot,\n                            expr,\n                        },\n                        expr_meta,\n                    )?;\n\n                    ctx.emit_restart();\n\n                    ctx.body.push(\n                        Statement::If {\n                            condition,\n                            accept: new_break(),\n                            reject: Block::new(),\n                        },\n                        Span::default(),\n                    );\n\n                    if let Some(idx) = terminator {\n                        ctx.body.cull(idx..)\n                    }\n                    Ok(())\n                })?;\n\n                ctx.body.push(\n                    Statement::Loop {\n                        body: loop_body,\n                        continuing: Block::new(),\n                        break_if: None,\n                    },\n                    meta,\n                );\n\n                meta\n            }\n            TokenValue::For => {\n                let mut meta = self.bump(frontend)?.meta;\n\n                ctx.symbol_table.push_scope();\n                self.expect(frontend, TokenValue::LeftParen)?;\n\n                if self.bump_if(frontend, TokenValue::Semicolon).is_none() {\n                    if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) {\n                        self.parse_declaration(frontend, ctx, false, is_inside_loop)?;\n                    } else {\n                        let mut stmt = ctx.stmt_ctx();\n                        let expr = self.parse_expression(frontend, ctx, &mut stmt)?;\n                        ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;\n                        self.expect(frontend, TokenValue::Semicolon)?;\n                    }\n                }\n\n                let loop_body = ctx.new_body(|ctx| {\n                    if self.bump_if(frontend, TokenValue::Semicolon).is_none() {\n                        let (expr, expr_meta) = if self.peek_type_name(frontend)\n                            || self.peek_type_qualifier(frontend)\n                        {\n                            let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;\n                            let (ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;\n                            let name = self.expect_ident(frontend)?.0;\n\n                            self.expect(frontend, TokenValue::Assign)?;\n\n                            let (value, end_meta) = self.parse_initializer(frontend, ty, ctx)?;\n                            meta.subsume(end_meta);\n\n                            let decl = VarDeclaration {\n                                qualifiers: &mut qualifiers,\n                                ty,\n                                name: Some(name),\n                                init: None,\n                                meta,\n                            };\n\n                            let pointer = frontend.add_local_var(ctx, decl)?;\n\n                            ctx.emit_restart();\n\n                            ctx.body.push(Statement::Store { pointer, value }, meta);\n\n                            (value, end_meta)\n                        } else {\n                            let mut stmt = ctx.stmt_ctx();\n                            let root = self.parse_expression(frontend, ctx, &mut stmt)?;\n                            ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?\n                        };\n\n                        let condition = ctx.add_expression(\n                            Expression::Unary {\n                                op: UnaryOperator::LogicalNot,\n                                expr,\n                            },\n                            expr_meta,\n                        )?;\n\n                        ctx.emit_restart();\n\n                        ctx.body.push(\n                            Statement::If {\n                                condition,\n                                accept: new_break(),\n                                reject: Block::new(),\n                            },\n                            Span::default(),\n                        );\n\n                        self.expect(frontend, TokenValue::Semicolon)?;\n                    }\n                    Ok(())\n                })?;\n\n                let continuing = ctx.new_body(|ctx| {\n                    match self.expect_peek(frontend)?.value {\n                        TokenValue::RightParen => {}\n                        _ => {\n                            let mut stmt = ctx.stmt_ctx();\n                            let rest = self.parse_expression(frontend, ctx, &mut stmt)?;\n                            ctx.lower(stmt, frontend, rest, ExprPos::Rhs)?;\n                        }\n                    }\n                    Ok(())\n                })?;\n\n                meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);\n\n                let loop_body = ctx.with_body(loop_body, |ctx| {\n                    if let Some(stmt_meta) = self.parse_statement(frontend, ctx, &mut None, true)? {\n                        meta.subsume(stmt_meta);\n                    }\n                    Ok(())\n                })?;\n\n                ctx.body.push(\n                    Statement::Loop {\n                        body: loop_body,\n                        continuing,\n                        break_if: None,\n                    },\n                    meta,\n                );\n\n                ctx.symbol_table.pop_scope();\n\n                meta\n            }\n            TokenValue::LeftBrace => {\n                let mut meta = self.bump(frontend)?.meta;\n\n                let mut block_terminator = None;\n\n                let block = ctx.new_body(|ctx| {\n                    let block_meta = self.parse_compound_statement(\n                        meta,\n                        frontend,\n                        ctx,\n                        &mut block_terminator,\n                        is_inside_loop,\n                    )?;\n                    meta.subsume(block_meta);\n                    Ok(())\n                })?;\n\n                ctx.body.push(Statement::Block(block), meta);\n                if block_terminator.is_some() {\n                    terminator.get_or_insert(ctx.body.len());\n                }\n\n                meta\n            }\n            TokenValue::Semicolon => self.bump(frontend)?.meta,\n            _ => {\n                // Attempt to force expression parsing for remainder of the\n                // tokens. Unknown or invalid tokens will be caught there and\n                // turned into an error.\n                let mut stmt = ctx.stmt_ctx();\n                let expr = self.parse_expression(frontend, ctx, &mut stmt)?;\n                ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;\n                self.expect(frontend, TokenValue::Semicolon)?.meta\n            }\n        };\n\n        meta.subsume(meta_rest);\n        Ok(Some(meta))\n    }\n\n    pub fn parse_compound_statement(\n        &mut self,\n        mut meta: Span,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        terminator: &mut Option<usize>,\n        is_inside_loop: bool,\n    ) -> Result<Span> {\n        ctx.symbol_table.push_scope();\n\n        loop {\n            if let Some(Token {\n                meta: brace_meta, ..\n            }) = self.bump_if(frontend, TokenValue::RightBrace)\n            {\n                meta.subsume(brace_meta);\n                break;\n            }\n\n            let stmt = self.parse_statement(frontend, ctx, terminator, is_inside_loop)?;\n\n            if let Some(stmt_meta) = stmt {\n                meta.subsume(stmt_meta);\n            }\n        }\n\n        if let Some(idx) = *terminator {\n            ctx.body.cull(idx..)\n        }\n\n        ctx.symbol_table.pop_scope();\n\n        Ok(meta)\n    }\n\n    pub fn parse_function_args(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n    ) -> Result<()> {\n        if self.bump_if(frontend, TokenValue::Void).is_some() {\n            return Ok(());\n        }\n\n        loop {\n            if self.peek_type_name(frontend) || self.peek_parameter_qualifier(frontend) {\n                let qualifier = self.parse_parameter_qualifier(frontend);\n                let mut ty = self.parse_type_non_void(frontend, ctx)?.0;\n\n                match self.expect_peek(frontend)?.value {\n                    TokenValue::Comma => {\n                        self.bump(frontend)?;\n                        ctx.add_function_arg(None, ty, qualifier)?;\n                        continue;\n                    }\n                    TokenValue::Identifier(_) => {\n                        let mut name = self.expect_ident(frontend)?;\n                        self.parse_array_specifier(frontend, ctx, &mut name.1, &mut ty)?;\n\n                        ctx.add_function_arg(Some(name), ty, qualifier)?;\n\n                        if self.bump_if(frontend, TokenValue::Comma).is_some() {\n                            continue;\n                        }\n\n                        break;\n                    }\n                    _ => break,\n                }\n            }\n\n            break;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser/types.rs",
    "content": "use alloc::{vec, vec::Vec};\nuse core::num::NonZeroU32;\n\nuse crate::{\n    front::glsl::{\n        ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers},\n        context::Context,\n        error::ExpectedToken,\n        parser::ParsingContext,\n        token::{Token, TokenValue},\n        Error, ErrorKind, Frontend, Result,\n    },\n    AddressSpace, ArraySize, Handle, Span, Type, TypeInner,\n};\n\nimpl ParsingContext<'_> {\n    /// Parses an optional array_specifier returning whether or not it's present\n    /// and modifying the type handle if it exists\n    pub fn parse_array_specifier(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        span: &mut Span,\n        ty: &mut Handle<Type>,\n    ) -> Result<()> {\n        while self.parse_array_specifier_single(frontend, ctx, span, ty)? {}\n        Ok(())\n    }\n\n    /// Implementation of [`Self::parse_array_specifier`] for a single array_specifier\n    fn parse_array_specifier_single(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        span: &mut Span,\n        ty: &mut Handle<Type>,\n    ) -> Result<bool> {\n        if self.bump_if(frontend, TokenValue::LeftBracket).is_some() {\n            let size = if let Some(Token { meta, .. }) =\n                self.bump_if(frontend, TokenValue::RightBracket)\n            {\n                span.subsume(meta);\n                ArraySize::Dynamic\n            } else {\n                let (value, constant_span) = self.parse_uint_constant(frontend, ctx)?;\n                let size = NonZeroU32::new(value).ok_or(Error {\n                    kind: ErrorKind::SemanticError(\"Array size must be greater than zero\".into()),\n                    meta: constant_span,\n                })?;\n                let end_span = self.expect(frontend, TokenValue::RightBracket)?.meta;\n                span.subsume(end_span);\n                ArraySize::Constant(size)\n            };\n\n            frontend.layouter.update(ctx.module.to_ctx()).unwrap();\n            let stride = frontend.layouter[*ty].to_stride();\n            *ty = ctx.module.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Array {\n                        base: *ty,\n                        size,\n                        stride,\n                    },\n                },\n                *span,\n            );\n\n            Ok(true)\n        } else {\n            Ok(false)\n        }\n    }\n\n    pub fn parse_type(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n    ) -> Result<(Option<Handle<Type>>, Span)> {\n        let token = self.bump(frontend)?;\n        let mut handle = match token.value {\n            TokenValue::Void => return Ok((None, token.meta)),\n            TokenValue::TypeName(ty) => ctx.module.types.insert(ty, token.meta),\n            TokenValue::Struct => {\n                let mut meta = token.meta;\n                let ty_name = self.expect_ident(frontend)?.0;\n                self.expect(frontend, TokenValue::LeftBrace)?;\n                let mut members = Vec::new();\n                let span = self.parse_struct_declaration_list(\n                    frontend,\n                    ctx,\n                    &mut members,\n                    StructLayout::Std140,\n                )?;\n                let end_meta = self.expect(frontend, TokenValue::RightBrace)?.meta;\n                meta.subsume(end_meta);\n                let ty = ctx.module.types.insert(\n                    Type {\n                        name: Some(ty_name.clone()),\n                        inner: TypeInner::Struct { members, span },\n                    },\n                    meta,\n                );\n                frontend.lookup_type.insert(ty_name, ty);\n                ty\n            }\n            TokenValue::Identifier(ident) => match frontend.lookup_type.get(&ident) {\n                Some(ty) => *ty,\n                None => {\n                    return Err(Error {\n                        kind: ErrorKind::UnknownType(ident),\n                        meta: token.meta,\n                    })\n                }\n            },\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::InvalidToken(\n                        token.value,\n                        vec![\n                            TokenValue::Void.into(),\n                            TokenValue::Struct.into(),\n                            ExpectedToken::TypeName,\n                        ],\n                    ),\n                    meta: token.meta,\n                });\n            }\n        };\n\n        let mut span = token.meta;\n        self.parse_array_specifier(frontend, ctx, &mut span, &mut handle)?;\n        Ok((Some(handle), span))\n    }\n\n    pub fn parse_type_non_void(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n    ) -> Result<(Handle<Type>, Span)> {\n        let (maybe_ty, meta) = self.parse_type(frontend, ctx)?;\n        let ty = maybe_ty.ok_or_else(|| Error {\n            kind: ErrorKind::SemanticError(\"Type can't be void\".into()),\n            meta,\n        })?;\n\n        Ok((ty, meta))\n    }\n\n    pub fn peek_type_qualifier(&mut self, frontend: &mut Frontend) -> bool {\n        self.peek(frontend).is_some_and(|t| match t.value {\n            TokenValue::Invariant\n            | TokenValue::Interpolation(_)\n            | TokenValue::Sampling(_)\n            | TokenValue::PrecisionQualifier(_)\n            | TokenValue::Const\n            | TokenValue::In\n            | TokenValue::Out\n            | TokenValue::Uniform\n            | TokenValue::Shared\n            | TokenValue::Buffer\n            | TokenValue::Restrict\n            | TokenValue::MemoryQualifier(_)\n            | TokenValue::Layout => true,\n            _ => false,\n        })\n    }\n\n    pub fn parse_type_qualifiers<'a>(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n    ) -> Result<TypeQualifiers<'a>> {\n        let mut qualifiers = TypeQualifiers::default();\n\n        while self.peek_type_qualifier(frontend) {\n            let token = self.bump(frontend)?;\n\n            // Handle layout qualifiers outside the match since this can push multiple values\n            if token.value == TokenValue::Layout {\n                self.parse_layout_qualifier_id_list(frontend, ctx, &mut qualifiers)?;\n                continue;\n            }\n\n            qualifiers.span.subsume(token.meta);\n\n            match token.value {\n                TokenValue::Invariant => {\n                    if qualifiers.invariant.is_some() {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Cannot use more than one invariant qualifier per declaration\"\n                                    .into(),\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n\n                    qualifiers.invariant = Some(token.meta);\n                }\n                TokenValue::Interpolation(i) => {\n                    if qualifiers.interpolation.is_some() {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Cannot use more than one interpolation qualifier per declaration\"\n                                    .into(),\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n\n                    qualifiers.interpolation = Some((i, token.meta));\n                }\n                TokenValue::Const\n                | TokenValue::In\n                | TokenValue::Out\n                | TokenValue::Uniform\n                | TokenValue::Shared\n                | TokenValue::Buffer => {\n                    let storage = match token.value {\n                        TokenValue::Const => StorageQualifier::Const,\n                        TokenValue::In => StorageQualifier::Input,\n                        TokenValue::Out => StorageQualifier::Output,\n                        TokenValue::Uniform => {\n                            StorageQualifier::AddressSpace(AddressSpace::Uniform)\n                        }\n                        TokenValue::Shared => {\n                            StorageQualifier::AddressSpace(AddressSpace::WorkGroup)\n                        }\n                        TokenValue::Buffer => {\n                            StorageQualifier::AddressSpace(AddressSpace::Storage {\n                                access: crate::StorageAccess::LOAD | crate::StorageAccess::STORE,\n                            })\n                        }\n                        _ => unreachable!(),\n                    };\n\n                    if StorageQualifier::AddressSpace(AddressSpace::Function)\n                        != qualifiers.storage.0\n                    {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Cannot use more than one storage qualifier per declaration\".into(),\n                            ),\n                            meta: token.meta,\n                        });\n                    }\n\n                    qualifiers.storage = (storage, token.meta);\n                }\n                TokenValue::Sampling(s) => {\n                    if qualifiers.sampling.is_some() {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Cannot use more than one sampling qualifier per declaration\"\n                                    .into(),\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n\n                    qualifiers.sampling = Some((s, token.meta));\n                }\n                TokenValue::PrecisionQualifier(p) => {\n                    if qualifiers.precision.is_some() {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"Cannot use more than one precision qualifier per declaration\"\n                                    .into(),\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n\n                    qualifiers.precision = Some((p, token.meta));\n                }\n                TokenValue::MemoryQualifier(access) => {\n                    let load_store = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;\n                    let storage_access = qualifiers\n                        .storage_access\n                        .get_or_insert((load_store, Span::default()));\n\n                    if !storage_access.0.contains(!access & load_store) {\n                        frontend.errors.push(Error {\n                            kind: ErrorKind::SemanticError(\n                                \"The same memory qualifier can only be used once\".into(),\n                            ),\n                            meta: token.meta,\n                        })\n                    }\n\n                    storage_access.0 &= access;\n                    storage_access.1.subsume(token.meta);\n                }\n                TokenValue::Restrict => continue,\n                _ => unreachable!(),\n            };\n        }\n\n        Ok(qualifiers)\n    }\n\n    pub fn parse_layout_qualifier_id_list(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        qualifiers: &mut TypeQualifiers,\n    ) -> Result<()> {\n        self.expect(frontend, TokenValue::LeftParen)?;\n        loop {\n            self.parse_layout_qualifier_id(frontend, ctx, &mut qualifiers.layout_qualifiers)?;\n\n            if self.bump_if(frontend, TokenValue::Comma).is_some() {\n                continue;\n            }\n\n            break;\n        }\n        let token = self.expect(frontend, TokenValue::RightParen)?;\n        qualifiers.span.subsume(token.meta);\n\n        Ok(())\n    }\n\n    pub fn parse_layout_qualifier_id(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n        qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>,\n    ) -> Result<()> {\n        // layout_qualifier_id:\n        //     IDENTIFIER\n        //     IDENTIFIER EQUAL constant_expression\n        //     SHARED\n        let mut token = self.bump(frontend)?;\n        match token.value {\n            TokenValue::Identifier(name) => {\n                let (key, value) = match name.as_str() {\n                    \"std140\" => (\n                        QualifierKey::Layout,\n                        QualifierValue::Layout(StructLayout::Std140),\n                    ),\n                    \"std430\" => (\n                        QualifierKey::Layout,\n                        QualifierValue::Layout(StructLayout::Std430),\n                    ),\n                    \"index\" => {\n                        self.expect(frontend, TokenValue::Assign)?;\n                        let (value, end_meta) = self.parse_uint_constant(frontend, ctx)?;\n                        token.meta.subsume(end_meta);\n\n                        (QualifierKey::Index, QualifierValue::Uint(value))\n                    }\n                    word => {\n                        if let Some(format) = map_image_format(word) {\n                            (QualifierKey::Format, QualifierValue::Format(format))\n                        } else {\n                            let key = QualifierKey::String(name.into());\n                            let value = if self.bump_if(frontend, TokenValue::Assign).is_some() {\n                                let (value, end_meta) =\n                                    match self.parse_uint_constant(frontend, ctx) {\n                                        Ok(v) => v,\n                                        Err(e) => {\n                                            frontend.errors.push(e);\n                                            (0, Span::default())\n                                        }\n                                    };\n                                token.meta.subsume(end_meta);\n\n                                QualifierValue::Uint(value)\n                            } else {\n                                QualifierValue::None\n                            };\n\n                            (key, value)\n                        }\n                    }\n                };\n\n                qualifiers.insert(key, (value, token.meta));\n            }\n            _ => frontend.errors.push(Error {\n                kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]),\n                meta: token.meta,\n            }),\n        }\n\n        Ok(())\n    }\n\n    pub fn peek_type_name(&mut self, frontend: &mut Frontend) -> bool {\n        self.peek(frontend).is_some_and(|t| match t.value {\n            TokenValue::TypeName(_) | TokenValue::Void => true,\n            TokenValue::Struct => true,\n            TokenValue::Identifier(ref ident) => frontend.lookup_type.contains_key(ident),\n            _ => false,\n        })\n    }\n}\n\nfn map_image_format(word: &str) -> Option<crate::StorageFormat> {\n    use crate::StorageFormat as Sf;\n\n    let format = match word {\n        // float-image-format-qualifier:\n        \"rgba32f\" => Sf::Rgba32Float,\n        \"rgba16f\" => Sf::Rgba16Float,\n        \"rg32f\" => Sf::Rg32Float,\n        \"rg16f\" => Sf::Rg16Float,\n        \"r11f_g11f_b10f\" => Sf::Rg11b10Ufloat,\n        \"r32f\" => Sf::R32Float,\n        \"r16f\" => Sf::R16Float,\n        \"rgba16\" => Sf::Rgba16Unorm,\n        \"rgb10_a2ui\" => Sf::Rgb10a2Uint,\n        \"rgb10_a2\" => Sf::Rgb10a2Unorm,\n        \"rgba8\" => Sf::Rgba8Unorm,\n        \"rg16\" => Sf::Rg16Unorm,\n        \"rg8\" => Sf::Rg8Unorm,\n        \"r16\" => Sf::R16Unorm,\n        \"r8\" => Sf::R8Unorm,\n        \"rgba16_snorm\" => Sf::Rgba16Snorm,\n        \"rgba8_snorm\" => Sf::Rgba8Snorm,\n        \"rg16_snorm\" => Sf::Rg16Snorm,\n        \"rg8_snorm\" => Sf::Rg8Snorm,\n        \"r16_snorm\" => Sf::R16Snorm,\n        \"r8_snorm\" => Sf::R8Snorm,\n        // int-image-format-qualifier:\n        \"rgba32i\" => Sf::Rgba32Sint,\n        \"rgba16i\" => Sf::Rgba16Sint,\n        \"rgba8i\" => Sf::Rgba8Sint,\n        \"rg32i\" => Sf::Rg32Sint,\n        \"rg16i\" => Sf::Rg16Sint,\n        \"rg8i\" => Sf::Rg8Sint,\n        \"r32i\" => Sf::R32Sint,\n        \"r16i\" => Sf::R16Sint,\n        \"r8i\" => Sf::R8Sint,\n        // uint-image-format-qualifier:\n        \"rgba32ui\" => Sf::Rgba32Uint,\n        \"rgba16ui\" => Sf::Rgba16Uint,\n        \"rgba8ui\" => Sf::Rgba8Uint,\n        \"r64ui\" => Sf::R64Uint,\n        \"rg32ui\" => Sf::Rg32Uint,\n        \"rg16ui\" => Sf::Rg16Uint,\n        \"rg8ui\" => Sf::Rg8Uint,\n        \"r32ui\" => Sf::R32Uint,\n        \"r16ui\" => Sf::R16Uint,\n        \"r8ui\" => Sf::R8Uint,\n        // TODO: These next ones seem incorrect to me\n        // \"rgb10_a2ui\" => Sf::Rgb10a2Unorm,\n        _ => return None,\n    };\n\n    Some(format)\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser.rs",
    "content": "use alloc::{string::String, vec};\nuse core::iter::Peekable;\n\nuse pp_rs::token::{PreprocessorError, Token as PPToken, TokenValue as PPTokenValue};\n\nuse super::{\n    ast::{FunctionKind, Profile, TypeQualifiers},\n    context::{Context, ExprPos},\n    error::ExpectedToken,\n    error::{Error, ErrorKind},\n    lex::{Lexer, LexerResultKind},\n    token::{Directive, DirectiveKind},\n    token::{Token, TokenValue},\n    variables::{GlobalOrConstant, VarDeclaration},\n    Frontend, Result,\n};\nuse crate::{arena::Handle, proc::ConstValueError, Expression, Module, Span, Type};\n\nmod declarations;\nmod expressions;\nmod functions;\nmod types;\n\npub struct ParsingContext<'source> {\n    lexer: Peekable<Lexer<'source>>,\n    /// Used to store tokens already consumed by the parser but that need to be backtracked\n    backtracked_token: Option<Token>,\n    last_meta: Span,\n}\n\nimpl<'source> ParsingContext<'source> {\n    pub fn new(lexer: Lexer<'source>) -> Self {\n        ParsingContext {\n            lexer: lexer.peekable(),\n            backtracked_token: None,\n            last_meta: Span::default(),\n        }\n    }\n\n    /// Helper method for backtracking from a consumed token\n    ///\n    /// This method should always be used instead of assigning to `backtracked_token` since\n    /// it validates that backtracking hasn't occurred more than one time in a row\n    ///\n    /// # Panics\n    /// - If the parser already backtracked without bumping in between\n    pub fn backtrack(&mut self, token: Token) -> Result<()> {\n        // This should never happen\n        if let Some(ref prev_token) = self.backtracked_token {\n            return Err(Error {\n                kind: ErrorKind::InternalError(\"The parser tried to backtrack twice in a row\"),\n                meta: prev_token.meta,\n            });\n        }\n\n        self.backtracked_token = Some(token);\n\n        Ok(())\n    }\n\n    pub fn expect_ident(&mut self, frontend: &mut Frontend) -> Result<(String, Span)> {\n        let token = self.bump(frontend)?;\n\n        match token.value {\n            TokenValue::Identifier(name) => Ok((name, token.meta)),\n            _ => Err(Error {\n                kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]),\n                meta: token.meta,\n            }),\n        }\n    }\n\n    pub fn expect(&mut self, frontend: &mut Frontend, value: TokenValue) -> Result<Token> {\n        let token = self.bump(frontend)?;\n\n        if token.value != value {\n            Err(Error {\n                kind: ErrorKind::InvalidToken(token.value, vec![value.into()]),\n                meta: token.meta,\n            })\n        } else {\n            Ok(token)\n        }\n    }\n\n    pub fn next(&mut self, frontend: &mut Frontend) -> Option<Token> {\n        loop {\n            if let Some(token) = self.backtracked_token.take() {\n                self.last_meta = token.meta;\n                break Some(token);\n            }\n\n            let res = self.lexer.next()?;\n\n            match res.kind {\n                LexerResultKind::Token(token) => {\n                    self.last_meta = token.meta;\n                    break Some(token);\n                }\n                LexerResultKind::Directive(directive) => {\n                    frontend.handle_directive(directive, res.meta)\n                }\n                LexerResultKind::Error(error) => frontend.errors.push(Error {\n                    kind: ErrorKind::PreprocessorError(error),\n                    meta: res.meta,\n                }),\n            }\n        }\n    }\n\n    pub fn bump(&mut self, frontend: &mut Frontend) -> Result<Token> {\n        self.next(frontend).ok_or(Error {\n            kind: ErrorKind::EndOfFile,\n            meta: self.last_meta,\n        })\n    }\n\n    /// Returns None on the end of the file rather than an error like other methods\n    pub fn bump_if(&mut self, frontend: &mut Frontend, value: TokenValue) -> Option<Token> {\n        if self.peek(frontend).filter(|t| t.value == value).is_some() {\n            self.bump(frontend).ok()\n        } else {\n            None\n        }\n    }\n\n    pub fn peek(&mut self, frontend: &mut Frontend) -> Option<&Token> {\n        loop {\n            if let Some(ref token) = self.backtracked_token {\n                break Some(token);\n            }\n\n            match self.lexer.peek()?.kind {\n                LexerResultKind::Token(_) => {\n                    let res = self.lexer.peek()?;\n\n                    match res.kind {\n                        LexerResultKind::Token(ref token) => break Some(token),\n                        _ => unreachable!(),\n                    }\n                }\n                LexerResultKind::Error(_) | LexerResultKind::Directive(_) => {\n                    let res = self.lexer.next()?;\n\n                    match res.kind {\n                        LexerResultKind::Directive(directive) => {\n                            frontend.handle_directive(directive, res.meta)\n                        }\n                        LexerResultKind::Error(error) => frontend.errors.push(Error {\n                            kind: ErrorKind::PreprocessorError(error),\n                            meta: res.meta,\n                        }),\n                        LexerResultKind::Token(_) => unreachable!(),\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn expect_peek(&mut self, frontend: &mut Frontend) -> Result<&Token> {\n        let meta = self.last_meta;\n        self.peek(frontend).ok_or(Error {\n            kind: ErrorKind::EndOfFile,\n            meta,\n        })\n    }\n\n    pub fn parse(&mut self, frontend: &mut Frontend) -> Result<Module> {\n        let mut module = Module::default();\n        let mut global_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();\n\n        // Body and expression arena for global initialization\n        let mut ctx = Context::new(\n            frontend,\n            &mut module,\n            false,\n            &mut global_expression_kind_tracker,\n        )?;\n\n        while self.peek(frontend).is_some() {\n            self.parse_external_declaration(frontend, &mut ctx)?;\n        }\n\n        // Add an `EntryPoint` to `parser.module` for `main`, if a\n        // suitable overload exists. Error out if we can't find one.\n        if let Some(declaration) = frontend.lookup_function.get(\"main\") {\n            for decl in declaration.overloads.iter() {\n                if let FunctionKind::Call(handle) = decl.kind {\n                    if decl.defined && decl.parameters.is_empty() {\n                        frontend.add_entry_point(handle, ctx)?;\n                        return Ok(module);\n                    }\n                }\n            }\n        }\n\n        Err(Error {\n            kind: ErrorKind::SemanticError(\"Missing entry point\".into()),\n            meta: Span::default(),\n        })\n    }\n\n    fn parse_uint_constant(\n        &mut self,\n        frontend: &mut Frontend,\n        ctx: &mut Context,\n    ) -> Result<(u32, Span)> {\n        let (const_expr, meta) = self.parse_constant_expression(\n            frontend,\n            ctx.module,\n            ctx.global_expression_kind_tracker,\n        )?;\n\n        let res = ctx.module.to_ctx().get_const_val(const_expr);\n\n        let int = match res {\n            Ok(value) => Ok(value),\n            Err(ConstValueError::Negative) => Err(Error {\n                kind: ErrorKind::SemanticError(\"int constant overflows\".into()),\n                meta,\n            }),\n            Err(ConstValueError::NonConst | ConstValueError::InvalidType) => Err(Error {\n                kind: ErrorKind::SemanticError(\"Expected a uint constant\".into()),\n                meta,\n            }),\n        }?;\n\n        Ok((int, meta))\n    }\n\n    fn parse_constant_expression(\n        &mut self,\n        frontend: &mut Frontend,\n        module: &mut Module,\n        global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker,\n    ) -> Result<(Handle<Expression>, Span)> {\n        let mut ctx = Context::new(frontend, module, true, global_expression_kind_tracker)?;\n\n        let mut stmt_ctx = ctx.stmt_ctx();\n        let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, None)?;\n        let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs)?;\n\n        Ok((root, meta))\n    }\n}\n\nimpl Frontend {\n    fn handle_directive(&mut self, directive: Directive, meta: Span) {\n        let mut tokens = directive.tokens.into_iter();\n\n        match directive.kind {\n            DirectiveKind::Version { is_first_directive } => {\n                if !is_first_directive {\n                    self.errors.push(Error {\n                        kind: ErrorKind::SemanticError(\n                            \"#version must occur first in shader\".into(),\n                        ),\n                        meta,\n                    })\n                }\n\n                match tokens.next() {\n                    Some(PPToken {\n                        value: PPTokenValue::Integer(int),\n                        location,\n                    }) => match int.value {\n                        440 | 450 | 460 => self.meta.version = int.value as u16,\n                        _ => self.errors.push(Error {\n                            kind: ErrorKind::InvalidVersion(int.value),\n                            meta: location.into(),\n                        }),\n                    },\n                    Some(PPToken { value, location }) => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    }),\n                    None => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),\n                        meta,\n                    }),\n                };\n\n                match tokens.next() {\n                    Some(PPToken {\n                        value: PPTokenValue::Ident(name),\n                        location,\n                    }) => match name.as_str() {\n                        \"core\" => self.meta.profile = Profile::Core,\n                        _ => self.errors.push(Error {\n                            kind: ErrorKind::InvalidProfile(name),\n                            meta: location.into(),\n                        }),\n                    },\n                    Some(PPToken { value, location }) => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    }),\n                    None => {}\n                };\n\n                if let Some(PPToken { value, location }) = tokens.next() {\n                    self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    })\n                }\n            }\n            DirectiveKind::Extension => {\n                // TODO: Proper extension handling\n                // - Checking for extension support in the compiler\n                // - Handle behaviors such as warn\n                // - Handle the all extension\n                let name = match tokens.next() {\n                    Some(PPToken {\n                        value: PPTokenValue::Ident(name),\n                        ..\n                    }) => Some(name),\n                    Some(PPToken { value, location }) => {\n                        self.errors.push(Error {\n                            kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                                value,\n                            )),\n                            meta: location.into(),\n                        });\n\n                        None\n                    }\n                    None => {\n                        self.errors.push(Error {\n                            kind: ErrorKind::PreprocessorError(\n                                PreprocessorError::UnexpectedNewLine,\n                            ),\n                            meta,\n                        });\n\n                        None\n                    }\n                };\n\n                match tokens.next() {\n                    Some(PPToken {\n                        value: PPTokenValue::Punct(pp_rs::token::Punct::Colon),\n                        ..\n                    }) => {}\n                    Some(PPToken { value, location }) => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    }),\n                    None => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),\n                        meta,\n                    }),\n                };\n\n                match tokens.next() {\n                    Some(PPToken {\n                        value: PPTokenValue::Ident(behavior),\n                        location,\n                    }) => match behavior.as_str() {\n                        \"require\" | \"enable\" | \"warn\" | \"disable\" => {\n                            if let Some(name) = name {\n                                self.meta.extensions.insert(name);\n                            }\n                        }\n                        _ => self.errors.push(Error {\n                            kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                                PPTokenValue::Ident(behavior),\n                            )),\n                            meta: location.into(),\n                        }),\n                    },\n                    Some(PPToken { value, location }) => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    }),\n                    None => self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),\n                        meta,\n                    }),\n                }\n\n                if let Some(PPToken { value, location }) = tokens.next() {\n                    self.errors.push(Error {\n                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(\n                            value,\n                        )),\n                        meta: location.into(),\n                    })\n                }\n            }\n            DirectiveKind::Pragma => {\n                // TODO: handle some common pragmas?\n            }\n        }\n    }\n}\n\npub struct DeclarationContext<'ctx, 'qualifiers, 'a> {\n    qualifiers: TypeQualifiers<'qualifiers>,\n    /// Indicates a global declaration\n    external: bool,\n    is_inside_loop: bool,\n    ctx: &'ctx mut Context<'a>,\n}\n\nimpl DeclarationContext<'_, '_, '_> {\n    fn add_var(\n        &mut self,\n        frontend: &mut Frontend,\n        ty: Handle<Type>,\n        name: String,\n        init: Option<Handle<Expression>>,\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let decl = VarDeclaration {\n            qualifiers: &mut self.qualifiers,\n            ty,\n            name: Some(name),\n            init,\n            meta,\n        };\n\n        match self.external {\n            true => {\n                let global = frontend.add_global_var(self.ctx, decl)?;\n                let expr = match global {\n                    GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle),\n                    GlobalOrConstant::Constant(handle) => Expression::Constant(handle),\n                    GlobalOrConstant::Override(handle) => Expression::Override(handle),\n                };\n                Ok(self.ctx.add_expression(expr, meta)?)\n            }\n            false => frontend.add_local_var(self.ctx, decl),\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/parser_tests.rs",
    "content": "use alloc::{borrow::ToOwned, vec};\n\nuse pp_rs::token::PreprocessorError;\n\nuse super::{\n    ast::Profile,\n    error::ExpectedToken,\n    error::{Error, ErrorKind, ParseErrors},\n    token::TokenValue,\n    Frontend, Options, Span,\n};\nuse crate::ShaderStage;\n\n#[cfg(test)]\nuse std::println;\n\n#[test]\nfn version() {\n    let mut frontend = Frontend::default();\n\n    // invalid versions\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                \"#version 99000\\n void main(){}\",\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::InvalidVersion(99000),\n                meta: Span::new(9, 14)\n            }],\n        },\n    );\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                \"#version 449\\n void main(){}\",\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::InvalidVersion(449),\n                meta: Span::new(9, 12)\n            }]\n        },\n    );\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                \"#version 450 smart\\n void main(){}\",\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::InvalidProfile(\"smart\".into()),\n                meta: Span::new(13, 18),\n            }]\n        },\n    );\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                \"#version 450\\nvoid main(){} #version 450\",\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![\n                Error {\n                    kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedHash,),\n                    meta: Span::new(27, 28),\n                },\n                Error {\n                    kind: ErrorKind::InvalidToken(\n                        TokenValue::Identifier(\"version\".into()),\n                        vec![ExpectedToken::Eof]\n                    ),\n                    meta: Span::new(28, 35)\n                }\n            ]\n        },\n    );\n\n    // valid versions\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            \"  #  version 450\\nvoid main() {}\",\n        )\n        .unwrap();\n    assert_eq!(\n        (frontend.metadata().version, frontend.metadata().profile),\n        (450, Profile::Core)\n    );\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            \"#version 450\\nvoid main() {}\",\n        )\n        .unwrap();\n    assert_eq!(\n        (frontend.metadata().version, frontend.metadata().profile),\n        (450, Profile::Core)\n    );\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            \"#version 450 core\\nvoid main(void) {}\",\n        )\n        .unwrap();\n    assert_eq!(\n        (frontend.metadata().version, frontend.metadata().profile),\n        (450, Profile::Core)\n    );\n}\n\n#[test]\nfn control_flow() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            if (true) {\n                return 1;\n            } else {\n                return 2;\n            }\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            if (true) {\n                return 1;\n            }\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            int x;\n            int y = 3;\n            switch (5) {\n                case 2:\n                    x = 2;\n                case 5:\n                    x = 5;\n                    y = 2;\n                    break;\n                default:\n                    x = 0;\n            }\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            int x = 0;\n            while(x < 5) {\n                x = x + 1;\n            }\n            do {\n                x = x - 1;\n            } while(x >= 4)\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            int x = 0;\n            for(int i = 0; i < 10;) {\n                x = x + 2;\n            }\n            for(;;);\n            return x;\n        }\n        \"#,\n        )\n        .unwrap();\n}\n\n#[test]\nfn declarations() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(location = 0) in vec2 v_uv;\n        layout(location = 0) out vec4 o_color;\n        layout(set = 1, binding = 1) uniform texture2D tex;\n        layout(set = 1, binding = 2) uniform sampler tex_sampler;\n\n        layout(early_fragment_tests) in;\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(std140, set = 2, binding = 0)\n        uniform u_locals {\n            vec3 model_offs;\n            float load_time;\n            ivec4 atlas_offs;\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(push_constant)\n        uniform u_locals {\n            vec3 model_offs;\n            float load_time;\n            ivec4 atlas_offs;\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(std430, set = 2, binding = 0)\n        uniform u_locals {\n            vec3 model_offs;\n            float load_time;\n            ivec4 atlas_offs;\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(std140, set = 2, binding = 0)\n        uniform u_locals {\n            vec3 model_offs;\n            float load_time;\n        } block_var;\n\n        void main() {\n            load_time * model_offs;\n            block_var.load_time * block_var.model_offs;\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        float vector = vec4(1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0);\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        precision highp float;\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n}\n\n#[test]\nfn textures() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #version 450\n        layout(location = 0) in vec2 v_uv;\n        layout(location = 0) out vec4 o_color;\n        layout(set = 1, binding = 1) uniform texture2D tex;\n        layout(set = 1, binding = 2) uniform sampler tex_sampler;\n        void main() {\n            o_color = texture(sampler2D(tex, tex_sampler), v_uv);\n            o_color.a = texture(sampler2D(tex, tex_sampler), v_uv, 2.0).a;\n        }\n        \"#,\n        )\n        .unwrap();\n}\n\n#[test]\nfn functions() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void test1(float);\n        void test1(float) {}\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void test2(float a) {}\n        void test3(float a, float b) {}\n        void test4(float, float) {}\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        float test(float a) { return a; }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        float test(vec4 p) {\n            return p.x;\n        }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    // Function overloading\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        float test(vec2 p);\n        float test(vec3 p);\n        float test(vec4 p);\n\n        float test(vec2 p) {\n            return p.x;\n        }\n\n        float test(vec3 p) {\n            return p.x;\n        }\n\n        float test(vec4 p) {\n            return p.x;\n        }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                r#\"\n                #  version 450\n                int test(vec4 p) {\n                    return p.x;\n                }\n\n                float test(vec4 p) {\n                    return p.x;\n                }\n\n                void main() {}\n                \"#,\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::SemanticError(\"Function already defined\".into()),\n                meta: Span::new(134, 152),\n            }]\n        },\n    );\n\n    println!();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        float callee(uint q) {\n            return float(q);\n        }\n\n        float caller() {\n            callee(1u);\n        }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    // Nested function call\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n            #  version 450\n            layout(set = 0, binding = 1) uniform texture2D t_noise;\n            layout(set = 0, binding = 2) uniform sampler s_noise;\n\n            void main() {\n                textureLod(sampler2D(t_noise, s_noise), vec2(1.0), 0);\n            }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void fun(vec2 in_parameter, out float out_parameter) {\n            ivec2 _ = ivec2(in_parameter);\n        }\n\n        void main() {\n            float a;\n            fun(vec2(1.0), a);\n        }\n        \"#,\n        )\n        .unwrap();\n}\n\n#[test]\nfn constants() {\n    use crate::{Constant, Expression, Type, TypeInner};\n\n    let mut frontend = Frontend::default();\n\n    let module = frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        const float a = 1.0;\n        float global = a;\n        const float b = a;\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    let mut types = module.types.iter();\n    let mut constants = module.constants.iter();\n    let mut global_expressions = module.global_expressions.iter();\n\n    let (ty_handle, ty) = types.next().unwrap();\n    assert_eq!(\n        ty,\n        &Type {\n            name: None,\n            inner: TypeInner::Scalar(crate::Scalar::F32)\n        }\n    );\n\n    let (init_handle, init) = global_expressions.next().unwrap();\n    assert_eq!(init, &Expression::Literal(crate::Literal::F32(1.0)));\n\n    assert_eq!(\n        constants.next().unwrap().1,\n        &Constant {\n            name: Some(\"a\".to_owned()),\n            ty: ty_handle,\n            init: init_handle\n        }\n    );\n\n    assert_eq!(\n        constants.next().unwrap().1,\n        &Constant {\n            name: Some(\"b\".to_owned()),\n            ty: ty_handle,\n            init: init_handle\n        }\n    );\n\n    assert!(constants.next().is_none());\n}\n\n#[test]\nfn function_overloading() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n\n        float saturate(float v) { return clamp(v, 0.0, 1.0); }\n        vec2 saturate(vec2 v) { return clamp(v, vec2(0.0), vec2(1.0)); }\n        vec3 saturate(vec3 v) { return clamp(v, vec3(0.0), vec3(1.0)); }\n        vec4 saturate(vec4 v) { return clamp(v, vec4(0.0), vec4(1.0)); }\n\n        void main() {\n            float v1 = saturate(1.5);\n            vec2 v2 = saturate(vec2(0.5, 1.5));\n            vec3 v3 = saturate(vec3(0.5, 1.5, 2.5));\n            vec3 v4 = saturate(vec4(0.5, 1.5, 2.5, 3.5));\n        }\n        \"#,\n        )\n        .unwrap();\n}\n\n#[test]\nfn implicit_conversions() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            mat4 a = mat4(1);\n            float b = 1u;\n            float c = 1 + 2.0;\n        }\n        \"#,\n        )\n        .unwrap();\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                r#\"\n                #  version 450\n                void test(int a) {}\n                void test(uint a) {}\n\n                void main() {\n                    test(1.0);\n                }\n                \"#,\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::SemanticError(\"Unknown function \\'test\\'\".into()),\n                meta: Span::new(156, 165),\n            }]\n        },\n    );\n\n    assert_eq!(\n        frontend\n            .parse(\n                &Options::from(ShaderStage::Vertex),\n                r#\"\n                #  version 450\n                void test(float a) {}\n                void test(uint a) {}\n\n                void main() {\n                    test(1);\n                }\n                \"#,\n            )\n            .err()\n            .unwrap(),\n        ParseErrors {\n            errors: vec![Error {\n                kind: ErrorKind::SemanticError(\"Ambiguous best function for \\'test\\'\".into()),\n                meta: Span::new(158, 165),\n            }]\n        }\n    );\n}\n\n#[test]\nfn structs() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        Test {\n            vec4 pos;\n          } xx;\n\n        void main() {}\n        \"#,\n        )\n        .unwrap_err();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        struct Test {\n            vec4 pos;\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        const int NUM_VECS = 42;\n        struct Test {\n            vec4 vecs[NUM_VECS];\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        struct Hello {\n            vec4 test;\n        } test() {\n            return Hello( vec4(1.0) );\n        }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        struct Test {};\n\n        void main() {}\n        \"#,\n        )\n        .unwrap_err();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        inout struct Test {\n            vec4 x;\n        };\n\n        void main() {}\n        \"#,\n        )\n        .unwrap_err();\n}\n\n#[test]\nfn swizzles() {\n    let mut frontend = Frontend::default();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            vec4 v = vec4(1);\n            v.xyz = vec3(2);\n            v.x = 5.0;\n            v.xyz.zxy.yx.xy = vec2(5.0, 1.0);\n        }\n        \"#,\n        )\n        .unwrap();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            vec4 v = vec4(1);\n            v.xx = vec2(5.0);\n        }\n        \"#,\n        )\n        .unwrap_err();\n\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            vec3 v = vec3(1);\n            v.w = 2.0;\n        }\n        \"#,\n        )\n        .unwrap_err();\n}\n\n#[test]\nfn expressions() {\n    let mut frontend = Frontend::default();\n\n    // Vector indexing\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        float test(int index) {\n            vec4 v = vec4(1.0, 2.0, 3.0, 4.0);\n            return v[index] + 1.0;\n        }\n\n        void main() {}\n        \"#,\n        )\n        .unwrap();\n\n    // Prefix increment/decrement\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            uint index = 0;\n\n            --index;\n            ++index;\n        }\n        \"#,\n        )\n        .unwrap();\n\n    // Dynamic indexing of array\n    frontend\n        .parse(\n            &Options::from(ShaderStage::Vertex),\n            r#\"\n        #  version 450\n        void main() {\n            const vec4 positions[1] = { vec4(0) };\n\n            gl_Position = positions[gl_VertexIndex];\n        }\n        \"#,\n        )\n        .unwrap();\n}\n"
  },
  {
    "path": "naga/src/front/glsl/token.rs",
    "content": "pub use pp_rs::token::{Float, Integer, Location, Token as PPToken};\n\nuse alloc::{string::String, vec::Vec};\n\nuse super::ast::Precision;\nuse crate::{Interpolation, Sampling, Span, Type};\n\nimpl From<Location> for Span {\n    fn from(loc: Location) -> Self {\n        Span::new(loc.start, loc.end)\n    }\n}\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Token {\n    pub value: TokenValue,\n    pub meta: Span,\n}\n\n/// A token passed from the lexing used in the parsing.\n///\n/// This type is exported since it's returned in the\n/// [`InvalidToken`](super::ErrorKind::InvalidToken) error.\n#[derive(Clone, Debug, PartialEq)]\npub enum TokenValue {\n    Identifier(String),\n\n    FloatConstant(Float),\n    IntConstant(Integer),\n    BoolConstant(bool),\n\n    Layout,\n    In,\n    Out,\n    InOut,\n    Uniform,\n    Buffer,\n    Const,\n    Shared,\n\n    Restrict,\n    /// A `glsl` memory qualifier such as `writeonly`\n    ///\n    /// The associated [`crate::StorageAccess`] is the access being allowed\n    /// (for example `writeonly` has an associated value of [`crate::StorageAccess::STORE`])\n    MemoryQualifier(crate::StorageAccess),\n\n    Invariant,\n    Interpolation(Interpolation),\n    Sampling(Sampling),\n    Precision,\n    PrecisionQualifier(Precision),\n\n    Continue,\n    Break,\n    Return,\n    Discard,\n\n    If,\n    Else,\n    Switch,\n    Case,\n    Default,\n    While,\n    Do,\n    For,\n\n    Void,\n    Struct,\n    TypeName(Type),\n\n    Assign,\n    AddAssign,\n    SubAssign,\n    MulAssign,\n    DivAssign,\n    ModAssign,\n    LeftShiftAssign,\n    RightShiftAssign,\n    AndAssign,\n    XorAssign,\n    OrAssign,\n\n    Increment,\n    Decrement,\n\n    LogicalOr,\n    LogicalAnd,\n    LogicalXor,\n\n    LessEqual,\n    GreaterEqual,\n    Equal,\n    NotEqual,\n\n    LeftShift,\n    RightShift,\n\n    LeftBrace,\n    RightBrace,\n    LeftParen,\n    RightParen,\n    LeftBracket,\n    RightBracket,\n    LeftAngle,\n    RightAngle,\n\n    Comma,\n    Semicolon,\n    Colon,\n    Dot,\n    Bang,\n    Dash,\n    Tilde,\n    Plus,\n    Star,\n    Slash,\n    Percent,\n    VerticalBar,\n    Caret,\n    Ampersand,\n    Question,\n}\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Directive {\n    pub kind: DirectiveKind,\n    pub tokens: Vec<PPToken>,\n}\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum DirectiveKind {\n    Version { is_first_directive: bool },\n    Extension,\n    Pragma,\n}\n"
  },
  {
    "path": "naga/src/front/glsl/types.rs",
    "content": "use alloc::format;\n\nuse super::{context::Context, Error, ErrorKind, Result, Span};\nuse crate::{\n    proc::ResolveContext, Expression, Handle, ImageClass, ImageDimension, Scalar, ScalarKind, Type,\n    TypeInner, VectorSize,\n};\n\npub fn parse_type(type_name: &str) -> Option<Type> {\n    match type_name {\n        \"bool\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::BOOL),\n        }),\n        \"float16_t\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::F16),\n        }),\n        \"float\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::F32),\n        }),\n        \"double\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::F64),\n        }),\n        \"int\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::I32),\n        }),\n        \"uint\" => Some(Type {\n            name: None,\n            inner: TypeInner::Scalar(Scalar::U32),\n        }),\n        \"sampler\" | \"samplerShadow\" => Some(Type {\n            name: None,\n            inner: TypeInner::Sampler {\n                comparison: type_name == \"samplerShadow\",\n            },\n        }),\n        word => {\n            fn kind_width_parse(ty: &str) -> Option<Scalar> {\n                Some(match ty {\n                    \"\" => Scalar::F32,\n                    \"b\" => Scalar::BOOL,\n                    \"i\" => Scalar::I32,\n                    \"u\" => Scalar::U32,\n                    \"d\" => Scalar::F64,\n                    \"f16\" => Scalar::F16,\n                    _ => return None,\n                })\n            }\n\n            fn size_parse(n: &str) -> Option<VectorSize> {\n                Some(match n {\n                    \"2\" => VectorSize::Bi,\n                    \"3\" => VectorSize::Tri,\n                    \"4\" => VectorSize::Quad,\n                    _ => return None,\n                })\n            }\n\n            let vec_parse = |word: &str| {\n                let mut iter = word.split(\"vec\");\n\n                let kind = iter.next()?;\n                let size = iter.next()?;\n                let scalar = kind_width_parse(kind)?;\n                let size = size_parse(size)?;\n\n                Some(Type {\n                    name: None,\n                    inner: TypeInner::Vector { size, scalar },\n                })\n            };\n\n            let mat_parse = |word: &str| {\n                let mut iter = word.split(\"mat\");\n\n                let kind = iter.next()?;\n                let size = iter.next()?;\n                let scalar = kind_width_parse(kind)?;\n\n                let (columns, rows) = if let Some(size) = size_parse(size) {\n                    (size, size)\n                } else {\n                    let mut iter = size.split('x');\n                    match (iter.next()?, iter.next()?, iter.next()) {\n                        (col, row, None) => (size_parse(col)?, size_parse(row)?),\n                        _ => return None,\n                    }\n                };\n\n                Some(Type {\n                    name: None,\n                    inner: TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    },\n                })\n            };\n\n            let texture_parse = |word: &str| {\n                let mut iter = word.split(\"texture\");\n\n                let texture_kind = |ty| {\n                    Some(match ty {\n                        \"\" => ScalarKind::Float,\n                        \"i\" => ScalarKind::Sint,\n                        \"u\" => ScalarKind::Uint,\n                        _ => return None,\n                    })\n                };\n\n                let kind = iter.next()?;\n                let size = iter.next()?;\n                let kind = texture_kind(kind)?;\n\n                let sampled = |multi| ImageClass::Sampled { kind, multi };\n\n                let (dim, arrayed, class) = match size {\n                    \"1D\" => (ImageDimension::D1, false, sampled(false)),\n                    \"1DArray\" => (ImageDimension::D1, true, sampled(false)),\n                    \"2D\" => (ImageDimension::D2, false, sampled(false)),\n                    \"2DArray\" => (ImageDimension::D2, true, sampled(false)),\n                    \"2DMS\" => (ImageDimension::D2, false, sampled(true)),\n                    \"2DMSArray\" => (ImageDimension::D2, true, sampled(true)),\n                    \"3D\" => (ImageDimension::D3, false, sampled(false)),\n                    \"Cube\" => (ImageDimension::Cube, false, sampled(false)),\n                    \"CubeArray\" => (ImageDimension::Cube, true, sampled(false)),\n                    _ => return None,\n                };\n\n                Some(Type {\n                    name: None,\n                    inner: TypeInner::Image {\n                        dim,\n                        arrayed,\n                        class,\n                    },\n                })\n            };\n\n            let image_parse = |word: &str| {\n                let mut iter = word.split(\"image\");\n\n                let texture_kind = |ty| {\n                    Some(match ty {\n                        \"\" => ScalarKind::Float,\n                        \"i\" => ScalarKind::Sint,\n                        \"u\" => ScalarKind::Uint,\n                        _ => return None,\n                    })\n                };\n\n                let kind = iter.next()?;\n                let size = iter.next()?;\n                // TODO: Check that the texture format and the kind match\n                let _ = texture_kind(kind)?;\n\n                let class = ImageClass::Storage {\n                    format: crate::StorageFormat::R8Uint,\n                    access: crate::StorageAccess::LOAD | crate::StorageAccess::STORE,\n                };\n\n                // TODO: glsl support multisampled storage images, naga doesn't\n                let (dim, arrayed) = match size {\n                    \"1D\" => (ImageDimension::D1, false),\n                    \"1DArray\" => (ImageDimension::D1, true),\n                    \"2D\" => (ImageDimension::D2, false),\n                    \"2DArray\" => (ImageDimension::D2, true),\n                    \"3D\" => (ImageDimension::D3, false),\n                    // Naga doesn't support cube images and it's usefulness\n                    // is questionable, so they won't be supported for now\n                    // \"Cube\" => (ImageDimension::Cube, false),\n                    // \"CubeArray\" => (ImageDimension::Cube, true),\n                    _ => return None,\n                };\n\n                Some(Type {\n                    name: None,\n                    inner: TypeInner::Image {\n                        dim,\n                        arrayed,\n                        class,\n                    },\n                })\n            };\n\n            vec_parse(word)\n                .or_else(|| mat_parse(word))\n                .or_else(|| texture_parse(word))\n                .or_else(|| image_parse(word))\n        }\n    }\n}\n\npub const fn scalar_components(ty: &TypeInner) -> Option<Scalar> {\n    match *ty {\n        TypeInner::Scalar(scalar)\n        | TypeInner::Vector { scalar, .. }\n        | TypeInner::ValuePointer { scalar, .. }\n        | TypeInner::Matrix { scalar, .. } => Some(scalar),\n        _ => None,\n    }\n}\n\npub const fn type_power(scalar: Scalar) -> Option<u32> {\n    Some(match scalar.kind {\n        ScalarKind::Sint => 0,\n        ScalarKind::Uint => 1,\n        ScalarKind::Float if scalar.width == 4 => 2,\n        ScalarKind::Float => 3,\n        ScalarKind::Bool | ScalarKind::AbstractInt | ScalarKind::AbstractFloat => return None,\n    })\n}\n\nimpl Context<'_> {\n    /// Resolves the types of the expressions until `expr` (inclusive)\n    ///\n    /// This needs to be done before the [`typifier`] can be queried for\n    /// the types of the expressions in the range between the last grow and `expr`.\n    ///\n    /// # Note\n    ///\n    /// The `resolve_type*` methods (like [`resolve_type`]) automatically\n    /// grow the [`typifier`] so calling this method is not necessary when using\n    /// them.\n    ///\n    /// [`typifier`]: Context::typifier\n    /// [`resolve_type`]: Self::resolve_type\n    pub(crate) fn typifier_grow(&mut self, expr: Handle<Expression>, meta: Span) -> Result<()> {\n        let resolve_ctx = ResolveContext::with_locals(self.module, &self.locals, &self.arguments);\n\n        let typifier = if self.is_const {\n            &mut self.const_typifier\n        } else {\n            &mut self.typifier\n        };\n\n        let expressions = if self.is_const {\n            &self.module.global_expressions\n        } else {\n            &self.expressions\n        };\n\n        typifier\n            .grow(expr, expressions, &resolve_ctx)\n            .map_err(|error| Error {\n                kind: ErrorKind::SemanticError(format!(\"Can't resolve type: {error:?}\").into()),\n                meta,\n            })\n    }\n\n    pub(crate) fn get_type(&self, expr: Handle<Expression>) -> &TypeInner {\n        let typifier = if self.is_const {\n            &self.const_typifier\n        } else {\n            &self.typifier\n        };\n\n        typifier.get(expr, &self.module.types)\n    }\n\n    /// Gets the type for the result of the `expr` expression\n    ///\n    /// Automatically grows the [`typifier`] to `expr` so calling\n    /// [`typifier_grow`] is not necessary\n    ///\n    /// [`typifier`]: Context::typifier\n    /// [`typifier_grow`]: Self::typifier_grow\n    pub(crate) fn resolve_type(\n        &mut self,\n        expr: Handle<Expression>,\n        meta: Span,\n    ) -> Result<&TypeInner> {\n        self.typifier_grow(expr, meta)?;\n        Ok(self.get_type(expr))\n    }\n\n    /// Gets the type handle for the result of the `expr` expression\n    ///\n    /// Automatically grows the [`typifier`] to `expr` so calling\n    /// [`typifier_grow`] is not necessary\n    ///\n    /// # Note\n    ///\n    /// Consider using [`resolve_type`] whenever possible\n    /// since it doesn't require adding each type to the [`types`] arena\n    /// and it doesn't need to mutably borrow the [`Parser`][Self]\n    ///\n    /// [`types`]: crate::Module::types\n    /// [`typifier`]: Context::typifier\n    /// [`typifier_grow`]: Self::typifier_grow\n    /// [`resolve_type`]: Self::resolve_type\n    pub(crate) fn resolve_type_handle(\n        &mut self,\n        expr: Handle<Expression>,\n        meta: Span,\n    ) -> Result<Handle<Type>> {\n        self.typifier_grow(expr, meta)?;\n\n        let typifier = if self.is_const {\n            &mut self.const_typifier\n        } else {\n            &mut self.typifier\n        };\n\n        Ok(typifier.register_type(expr, &mut self.module.types))\n    }\n\n    /// Invalidates the cached type resolution for `expr` forcing a recomputation\n    pub(crate) fn invalidate_expression(\n        &mut self,\n        expr: Handle<Expression>,\n        meta: Span,\n    ) -> Result<()> {\n        let resolve_ctx = ResolveContext::with_locals(self.module, &self.locals, &self.arguments);\n\n        let typifier = if self.is_const {\n            &mut self.const_typifier\n        } else {\n            &mut self.typifier\n        };\n\n        typifier\n            .invalidate(expr, &self.expressions, &resolve_ctx)\n            .map_err(|error| Error {\n                kind: ErrorKind::SemanticError(format!(\"Can't resolve type: {error:?}\").into()),\n                meta,\n            })\n    }\n\n    pub(crate) fn lift_up_const_expression(\n        &mut self,\n        expr: Handle<Expression>,\n    ) -> Result<Handle<Expression>> {\n        let meta = self.expressions.get_span(expr);\n        let h = match self.expressions[expr] {\n            ref expr @ (Expression::Literal(_)\n            | Expression::Constant(_)\n            | Expression::ZeroValue(_)) => {\n                self.module.global_expressions.append(expr.clone(), meta)\n            }\n            Expression::Compose { ty, ref components } => {\n                let mut components = components.clone();\n                for component in &mut components {\n                    *component = self.lift_up_const_expression(*component)?;\n                }\n                self.module\n                    .global_expressions\n                    .append(Expression::Compose { ty, components }, meta)\n            }\n            Expression::Splat { size, value } => {\n                let value = self.lift_up_const_expression(value)?;\n                self.module\n                    .global_expressions\n                    .append(Expression::Splat { size, value }, meta)\n            }\n            _ => {\n                return Err(Error {\n                    kind: ErrorKind::SemanticError(\"Expression is not const-expression\".into()),\n                    meta,\n                })\n            }\n        };\n        self.global_expression_kind_tracker\n            .insert(h, crate::proc::ExpressionKind::Const);\n        Ok(h)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/glsl/variables.rs",
    "content": "use alloc::{format, string::String, vec::Vec};\n\nuse super::{\n    ast::*,\n    context::{Context, ExprPos},\n    error::{Error, ErrorKind},\n    Frontend, Result, Span,\n};\nuse crate::{\n    AddressSpace, Binding, BuiltIn, Constant, Expression, GlobalVariable, Handle, Interpolation,\n    LocalVariable, Override, ResourceBinding, Scalar, ScalarKind, ShaderStage, SwizzleComponent,\n    Type, TypeInner, VectorSize,\n};\n\npub struct VarDeclaration<'a, 'key> {\n    pub qualifiers: &'a mut TypeQualifiers<'key>,\n    pub ty: Handle<Type>,\n    pub name: Option<String>,\n    pub init: Option<Handle<Expression>>,\n    pub meta: Span,\n}\n\n/// Information about a builtin used in [`add_builtin`](Frontend::add_builtin).\nstruct BuiltInData {\n    /// The type of the builtin.\n    inner: TypeInner,\n    /// The associated builtin class.\n    builtin: BuiltIn,\n    /// Whether the builtin can be written to or not.\n    mutable: bool,\n    /// The storage used for the builtin.\n    storage: StorageQualifier,\n}\n\npub enum GlobalOrConstant {\n    Global(Handle<GlobalVariable>),\n    Constant(Handle<Constant>),\n    Override(Handle<Override>),\n}\n\nimpl Frontend {\n    /// Adds a builtin and returns a variable reference to it\n    fn add_builtin(\n        &mut self,\n        ctx: &mut Context,\n        name: &str,\n        data: BuiltInData,\n        meta: Span,\n    ) -> Result<Option<VariableReference>> {\n        let ty = ctx.module.types.insert(\n            Type {\n                name: None,\n                inner: data.inner,\n            },\n            meta,\n        );\n\n        let handle = ctx.module.global_variables.append(\n            GlobalVariable {\n                name: Some(name.into()),\n                space: AddressSpace::Private,\n                binding: None,\n                ty,\n                init: None,\n                memory_decorations: crate::MemoryDecorations::empty(),\n            },\n            meta,\n        );\n\n        let idx = self.entry_args.len();\n        self.entry_args.push(EntryArg {\n            name: Some(name.into()),\n            binding: Binding::BuiltIn(data.builtin),\n            handle,\n            storage: data.storage,\n        });\n\n        self.global_variables.push((\n            name.into(),\n            GlobalLookup {\n                kind: GlobalLookupKind::Variable(handle),\n                entry_arg: Some(idx),\n                mutable: data.mutable,\n            },\n        ));\n\n        let expr = ctx.add_expression(Expression::GlobalVariable(handle), meta)?;\n\n        let var = VariableReference {\n            expr,\n            load: true,\n            mutable: data.mutable,\n            constant: None,\n            entry_arg: Some(idx),\n        };\n\n        ctx.symbol_table.add_root(name.into(), var.clone());\n\n        Ok(Some(var))\n    }\n\n    pub(crate) fn lookup_variable(\n        &mut self,\n        ctx: &mut Context,\n        name: &str,\n        meta: Span,\n    ) -> Result<Option<VariableReference>> {\n        if let Some(var) = ctx.symbol_table.lookup(name).cloned() {\n            return Ok(Some(var));\n        }\n\n        let data = match name {\n            \"gl_Position\" => BuiltInData {\n                inner: TypeInner::Vector {\n                    size: VectorSize::Quad,\n                    scalar: Scalar::F32,\n                },\n                builtin: BuiltIn::Position { invariant: false },\n                mutable: true,\n                storage: StorageQualifier::Output,\n            },\n            \"gl_FragCoord\" => BuiltInData {\n                inner: TypeInner::Vector {\n                    size: VectorSize::Quad,\n                    scalar: Scalar::F32,\n                },\n                builtin: BuiltIn::Position { invariant: false },\n                mutable: false,\n                storage: StorageQualifier::Input,\n            },\n            \"gl_PointCoord\" => BuiltInData {\n                inner: TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: Scalar::F32,\n                },\n                builtin: BuiltIn::PointCoord,\n                mutable: false,\n                storage: StorageQualifier::Input,\n            },\n            \"gl_GlobalInvocationID\"\n            | \"gl_NumWorkGroups\"\n            | \"gl_WorkGroupSize\"\n            | \"gl_WorkGroupID\"\n            | \"gl_LocalInvocationID\" => BuiltInData {\n                inner: TypeInner::Vector {\n                    size: VectorSize::Tri,\n                    scalar: Scalar::U32,\n                },\n                builtin: match name {\n                    \"gl_GlobalInvocationID\" => BuiltIn::GlobalInvocationId,\n                    \"gl_NumWorkGroups\" => BuiltIn::NumWorkGroups,\n                    \"gl_WorkGroupSize\" => BuiltIn::WorkGroupSize,\n                    \"gl_WorkGroupID\" => BuiltIn::WorkGroupId,\n                    \"gl_LocalInvocationID\" => BuiltIn::LocalInvocationId,\n                    _ => unreachable!(),\n                },\n                mutable: false,\n                storage: StorageQualifier::Input,\n            },\n            \"gl_FrontFacing\" => BuiltInData {\n                inner: TypeInner::Scalar(Scalar::BOOL),\n                builtin: BuiltIn::FrontFacing,\n                mutable: false,\n                storage: StorageQualifier::Input,\n            },\n            \"gl_PointSize\" | \"gl_FragDepth\" => BuiltInData {\n                inner: TypeInner::Scalar(Scalar::F32),\n                builtin: match name {\n                    \"gl_PointSize\" => BuiltIn::PointSize,\n                    \"gl_FragDepth\" => BuiltIn::FragDepth,\n                    _ => unreachable!(),\n                },\n                mutable: true,\n                storage: StorageQualifier::Output,\n            },\n            \"gl_ClipDistance\" | \"gl_CullDistance\" => {\n                let base = ctx.module.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Scalar(Scalar::F32),\n                    },\n                    meta,\n                );\n\n                BuiltInData {\n                    inner: TypeInner::Array {\n                        base,\n                        size: crate::ArraySize::Dynamic,\n                        stride: 4,\n                    },\n                    builtin: match name {\n                        \"gl_ClipDistance\" => BuiltIn::ClipDistances,\n                        \"gl_CullDistance\" => BuiltIn::CullDistance,\n                        _ => unreachable!(),\n                    },\n                    mutable: self.meta.stage == ShaderStage::Vertex,\n                    storage: StorageQualifier::Output,\n                }\n            }\n            _ => {\n                let builtin = match name {\n                    \"gl_BaseVertex\" => BuiltIn::BaseVertex,\n                    \"gl_BaseInstance\" => BuiltIn::BaseInstance,\n                    \"gl_PrimitiveID\" => BuiltIn::PrimitiveIndex,\n                    \"gl_BaryCoordEXT\" => BuiltIn::Barycentric { perspective: true },\n                    \"gl_BaryCoordNoPerspEXT\" => BuiltIn::Barycentric { perspective: false },\n                    \"gl_InstanceIndex\" => BuiltIn::InstanceIndex,\n                    \"gl_VertexIndex\" => BuiltIn::VertexIndex,\n                    \"gl_SampleID\" => BuiltIn::SampleIndex,\n                    \"gl_LocalInvocationIndex\" => BuiltIn::LocalInvocationIndex,\n                    \"gl_DrawID\" => BuiltIn::DrawIndex,\n                    _ => return Ok(None),\n                };\n\n                BuiltInData {\n                    inner: TypeInner::Scalar(Scalar::U32),\n                    builtin,\n                    mutable: false,\n                    storage: StorageQualifier::Input,\n                }\n            }\n        };\n\n        self.add_builtin(ctx, name, data, meta)\n    }\n\n    pub(crate) fn make_variable_invariant(\n        &mut self,\n        ctx: &mut Context,\n        name: &str,\n        meta: Span,\n    ) -> Result<()> {\n        if let Some(var) = self.lookup_variable(ctx, name, meta)? {\n            if let Some(index) = var.entry_arg {\n                if let Binding::BuiltIn(BuiltIn::Position { ref mut invariant }) =\n                    self.entry_args[index].binding\n                {\n                    *invariant = true;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub(crate) fn field_selection(\n        &mut self,\n        ctx: &mut Context,\n        pos: ExprPos,\n        expression: Handle<Expression>,\n        name: &str,\n        meta: Span,\n    ) -> Result<Handle<Expression>> {\n        let (ty, is_pointer) = match *ctx.resolve_type(expression, meta)? {\n            TypeInner::Pointer { base, .. } => (&ctx.module.types[base].inner, true),\n            ref ty => (ty, false),\n        };\n        match *ty {\n            TypeInner::Struct { ref members, .. } => {\n                let index = members\n                    .iter()\n                    .position(|m| m.name == Some(name.into()))\n                    .ok_or_else(|| Error {\n                        kind: ErrorKind::UnknownField(name.into()),\n                        meta,\n                    })?;\n                let pointer = ctx.add_expression(\n                    Expression::AccessIndex {\n                        base: expression,\n                        index: index as u32,\n                    },\n                    meta,\n                )?;\n\n                Ok(match pos {\n                    ExprPos::Rhs if is_pointer => {\n                        ctx.add_expression(Expression::Load { pointer }, meta)?\n                    }\n                    _ => pointer,\n                })\n            }\n            // swizzles (xyzw, rgba, stpq)\n            TypeInner::Vector { size, .. } => {\n                let check_swizzle_components = |comps: &str| {\n                    name.chars()\n                        .map(|c| {\n                            comps\n                                .find(c)\n                                .filter(|i| *i < size as usize)\n                                .map(|i| SwizzleComponent::from_index(i as u32))\n                        })\n                        .collect::<Option<Vec<SwizzleComponent>>>()\n                };\n\n                let components = check_swizzle_components(\"xyzw\")\n                    .or_else(|| check_swizzle_components(\"rgba\"))\n                    .or_else(|| check_swizzle_components(\"stpq\"));\n\n                if let Some(components) = components {\n                    if let ExprPos::Lhs = pos {\n                        let not_unique = (1..components.len())\n                            .any(|i| components[i..].contains(&components[i - 1]));\n                        if not_unique {\n                            self.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\n                                        concat!(\n                                            \"swizzle cannot have duplicate components in \",\n                                            \"left-hand-side expression for \\\"{:?}\\\"\"\n                                        ),\n                                        name\n                                    )\n                                    .into(),\n                                ),\n                                meta,\n                            })\n                        }\n                    }\n\n                    let mut pattern = [SwizzleComponent::X; 4];\n                    for (pat, component) in pattern.iter_mut().zip(&components) {\n                        *pat = *component;\n                    }\n\n                    // flatten nested swizzles (vec.zyx.xy.x => vec.z)\n                    let mut expression = expression;\n                    if let Expression::Swizzle {\n                        size: _,\n                        vector,\n                        pattern: ref src_pattern,\n                    } = ctx[expression]\n                    {\n                        expression = vector;\n                        for pat in &mut pattern {\n                            *pat = src_pattern[pat.index() as usize];\n                        }\n                    }\n\n                    let size = match components.len() {\n                        // Swizzles with just one component are accesses and not swizzles\n                        1 => {\n                            match pos {\n                                // If the position is in the right hand side and the base\n                                // vector is a pointer, load it, otherwise the swizzle would\n                                // produce a pointer\n                                ExprPos::Rhs if is_pointer => {\n                                    expression = ctx.add_expression(\n                                        Expression::Load {\n                                            pointer: expression,\n                                        },\n                                        meta,\n                                    )?;\n                                }\n                                _ => {}\n                            };\n                            return ctx.add_expression(\n                                Expression::AccessIndex {\n                                    base: expression,\n                                    index: pattern[0].index(),\n                                },\n                                meta,\n                            );\n                        }\n                        2 => VectorSize::Bi,\n                        3 => VectorSize::Tri,\n                        4 => VectorSize::Quad,\n                        _ => {\n                            self.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\"Bad swizzle size for \\\"{name:?}\\\"\").into(),\n                                ),\n                                meta,\n                            });\n\n                            VectorSize::Quad\n                        }\n                    };\n\n                    if is_pointer {\n                        // NOTE: for lhs expression, this extra load ends up as an unused expr, because the\n                        // assignment will extract the pointer and use it directly anyway. Unfortunately we\n                        // need it for validation to pass, as swizzles cannot operate on pointer values.\n                        expression = ctx.add_expression(\n                            Expression::Load {\n                                pointer: expression,\n                            },\n                            meta,\n                        )?;\n                    }\n\n                    Ok(ctx.add_expression(\n                        Expression::Swizzle {\n                            size,\n                            vector: expression,\n                            pattern,\n                        },\n                        meta,\n                    )?)\n                } else {\n                    Err(Error {\n                        kind: ErrorKind::SemanticError(\n                            format!(\"Invalid swizzle for vector \\\"{name}\\\"\").into(),\n                        ),\n                        meta,\n                    })\n                }\n            }\n            _ => Err(Error {\n                kind: ErrorKind::SemanticError(\n                    format!(\"Can't lookup field on this type \\\"{name}\\\"\").into(),\n                ),\n                meta,\n            }),\n        }\n    }\n\n    pub(crate) fn add_global_var(\n        &mut self,\n        ctx: &mut Context,\n        VarDeclaration {\n            qualifiers,\n            mut ty,\n            name,\n            init,\n            meta,\n        }: VarDeclaration,\n    ) -> Result<GlobalOrConstant> {\n        let storage = qualifiers.storage.0;\n        let (ret, lookup) = match storage {\n            StorageQualifier::Input | StorageQualifier::Output => {\n                let input = storage == StorageQualifier::Input;\n                // TODO: glslang seems to use a counter for variables without\n                // explicit location (even if that causes collisions)\n                let location = qualifiers\n                    .uint_layout_qualifier(\"location\", &mut self.errors)\n                    .unwrap_or(0);\n                let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| {\n                    let kind = ctx.module.types[ty].inner.scalar_kind()?;\n                    Some(match kind {\n                        ScalarKind::Float => Interpolation::Perspective,\n                        _ => Interpolation::Flat,\n                    })\n                });\n                let sampling = qualifiers.sampling.take().map(|(s, _)| s);\n\n                let handle = ctx.module.global_variables.append(\n                    GlobalVariable {\n                        name: name.clone(),\n                        space: AddressSpace::Private,\n                        binding: None,\n                        ty,\n                        init,\n                        memory_decorations: crate::MemoryDecorations::empty(),\n                    },\n                    meta,\n                );\n\n                let blend_src = qualifiers\n                    .layout_qualifiers\n                    .remove(&QualifierKey::Index)\n                    .and_then(|(value, _span)| match value {\n                        QualifierValue::Uint(index) => Some(index),\n                        _ => None,\n                    });\n\n                let idx = self.entry_args.len();\n                self.entry_args.push(EntryArg {\n                    name: name.clone(),\n                    binding: Binding::Location {\n                        location,\n                        interpolation,\n                        sampling,\n                        blend_src,\n                        per_primitive: false,\n                    },\n                    handle,\n                    storage,\n                });\n\n                let lookup = GlobalLookup {\n                    kind: GlobalLookupKind::Variable(handle),\n                    entry_arg: Some(idx),\n                    mutable: !input,\n                };\n\n                (GlobalOrConstant::Global(handle), lookup)\n            }\n            StorageQualifier::Const => {\n                // Check if this is a specialization constant with constant_id\n                let constant_id = qualifiers.uint_layout_qualifier(\"constant_id\", &mut self.errors);\n\n                if let Some(id) = constant_id {\n                    // This is a specialization constant - convert to Override\n                    let id: Option<u16> = match id.try_into() {\n                        Ok(v) => Some(v),\n                        Err(_) => {\n                            self.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    format!(\n                                        \"constant_id value {id} is too high (maximum is {})\",\n                                        u16::MAX\n                                    )\n                                    .into(),\n                                ),\n                                meta,\n                            });\n                            None\n                        }\n                    };\n\n                    let override_handle = ctx.module.overrides.append(\n                        Override {\n                            name: name.clone(),\n                            id,\n                            ty,\n                            init,\n                        },\n                        meta,\n                    );\n\n                    let lookup = GlobalLookup {\n                        kind: GlobalLookupKind::Override(override_handle, ty),\n                        entry_arg: None,\n                        mutable: false,\n                    };\n\n                    (GlobalOrConstant::Override(override_handle), lookup)\n                } else {\n                    // Regular constant\n                    let init = init.ok_or_else(|| Error {\n                        kind: ErrorKind::SemanticError(\n                            \"const values must have an initializer\".into(),\n                        ),\n                        meta,\n                    })?;\n\n                    let constant = Constant {\n                        name: name.clone(),\n                        ty,\n                        init,\n                    };\n                    let handle = ctx.module.constants.append(constant, meta);\n\n                    let lookup = GlobalLookup {\n                        kind: GlobalLookupKind::Constant(handle, ty),\n                        entry_arg: None,\n                        mutable: false,\n                    };\n\n                    (GlobalOrConstant::Constant(handle), lookup)\n                }\n            }\n            StorageQualifier::AddressSpace(mut space) => {\n                match space {\n                    AddressSpace::Storage { ref mut access } => {\n                        if let Some((allowed_access, _)) = qualifiers.storage_access.take() {\n                            *access = allowed_access;\n                        }\n                    }\n                    AddressSpace::Uniform => match ctx.module.types[ty].inner {\n                        TypeInner::Image {\n                            class,\n                            dim,\n                            arrayed,\n                        } => {\n                            if let crate::ImageClass::Storage {\n                                mut access,\n                                mut format,\n                            } = class\n                            {\n                                if let Some((allowed_access, _)) = qualifiers.storage_access.take()\n                                {\n                                    access = allowed_access;\n                                }\n\n                                match qualifiers.layout_qualifiers.remove(&QualifierKey::Format) {\n                                    Some((QualifierValue::Format(f), _)) => format = f,\n                                    // TODO: glsl supports images without format qualifier\n                                    // if they are `writeonly`\n                                    None => self.errors.push(Error {\n                                        kind: ErrorKind::SemanticError(\n                                            \"image types require a format layout qualifier\".into(),\n                                        ),\n                                        meta,\n                                    }),\n                                    _ => unreachable!(),\n                                }\n\n                                ty = ctx.module.types.insert(\n                                    Type {\n                                        name: None,\n                                        inner: TypeInner::Image {\n                                            dim,\n                                            arrayed,\n                                            class: crate::ImageClass::Storage { format, access },\n                                        },\n                                    },\n                                    meta,\n                                );\n                            }\n\n                            space = AddressSpace::Handle\n                        }\n                        TypeInner::Sampler { .. } => space = AddressSpace::Handle,\n                        _ => {\n                            if qualifiers.none_layout_qualifier(\"push_constant\", &mut self.errors) {\n                                space = AddressSpace::Immediate\n                            }\n                        }\n                    },\n                    AddressSpace::Function => space = AddressSpace::Private,\n                    _ => {}\n                };\n\n                let binding = match space {\n                    AddressSpace::Uniform | AddressSpace::Storage { .. } | AddressSpace::Handle => {\n                        let binding = qualifiers.uint_layout_qualifier(\"binding\", &mut self.errors);\n                        if binding.is_none() {\n                            self.errors.push(Error {\n                                kind: ErrorKind::SemanticError(\n                                    \"uniform/buffer blocks require layout(binding=X)\".into(),\n                                ),\n                                meta,\n                            });\n                        }\n                        let set = qualifiers.uint_layout_qualifier(\"set\", &mut self.errors);\n                        binding.map(|binding| ResourceBinding {\n                            group: set.unwrap_or(0),\n                            binding,\n                        })\n                    }\n                    _ => None,\n                };\n\n                let handle = ctx.module.global_variables.append(\n                    GlobalVariable {\n                        name: name.clone(),\n                        space,\n                        binding,\n                        ty,\n                        init,\n                        memory_decorations: crate::MemoryDecorations::empty(),\n                    },\n                    meta,\n                );\n\n                let lookup = GlobalLookup {\n                    kind: GlobalLookupKind::Variable(handle),\n                    entry_arg: None,\n                    mutable: true,\n                };\n\n                (GlobalOrConstant::Global(handle), lookup)\n            }\n        };\n\n        if let Some(name) = name {\n            ctx.add_global(&name, lookup)?;\n\n            self.global_variables.push((name, lookup));\n        }\n\n        qualifiers.unused_errors(&mut self.errors);\n\n        Ok(ret)\n    }\n\n    pub(crate) fn add_local_var(\n        &mut self,\n        ctx: &mut Context,\n        decl: VarDeclaration,\n    ) -> Result<Handle<Expression>> {\n        let storage = decl.qualifiers.storage;\n        let mutable = match storage.0 {\n            StorageQualifier::AddressSpace(AddressSpace::Function) => true,\n            StorageQualifier::Const => false,\n            _ => {\n                self.errors.push(Error {\n                    kind: ErrorKind::SemanticError(\"Locals cannot have a storage qualifier\".into()),\n                    meta: storage.1,\n                });\n                true\n            }\n        };\n\n        let handle = ctx.locals.append(\n            LocalVariable {\n                name: decl.name.clone(),\n                ty: decl.ty,\n                init: decl.init,\n            },\n            decl.meta,\n        );\n        let expr = ctx.add_expression(Expression::LocalVariable(handle), decl.meta)?;\n\n        if let Some(name) = decl.name {\n            let maybe_var = ctx.add_local_var(name.clone(), expr, mutable);\n\n            if maybe_var.is_some() {\n                self.errors.push(Error {\n                    kind: ErrorKind::VariableAlreadyDeclared(name),\n                    meta: decl.meta,\n                })\n            }\n        }\n\n        decl.qualifiers.unused_errors(&mut self.errors);\n\n        Ok(expr)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/interpolator.rs",
    "content": "/*!\nInterpolation defaults.\n*/\n\nimpl crate::Binding {\n    /// Apply the usual default interpolation for `ty` to `binding`.\n    ///\n    /// This function is a utility front ends may use to satisfy the Naga IR's\n    /// requirement, meant to ensure that input languages' policies have been\n    /// applied appropriately, that all I/O `Binding`s from the vertex shader to the\n    /// fragment shader must have non-`None` `interpolation` values.\n    ///\n    /// All the shader languages Naga supports have similar rules:\n    /// perspective-correct, center-sampled interpolation is the default for any\n    /// binding that can vary, and everything else either defaults to flat, or\n    /// requires an explicit flat qualifier/attribute/what-have-you.\n    ///\n    /// If `binding` is not a [`Location`] binding, or if its [`interpolation`] is\n    /// already set, then make no changes. Otherwise, set `binding`'s interpolation\n    /// and sampling to reasonable defaults depending on `ty`, the type of the value\n    /// being interpolated:\n    ///\n    /// - If `ty` is a floating-point scalar, vector, or matrix type, then\n    ///   default to [`Perspective`] interpolation and [`Center`] sampling.\n    ///\n    /// - If `ty` is an integral scalar or vector, then default to [`Flat`]\n    ///   interpolation, which has no associated sampling.\n    ///\n    /// - For any other types, make no change. Such types are not permitted as\n    ///   user-defined IO values, and will probably be flagged by the verifier\n    ///\n    /// When structs appear in input or output types, each member ought to have its\n    /// own [`Binding`], so structs are simply covered by the third case.\n    ///\n    /// [`Binding`]: crate::Binding\n    /// [`Location`]: crate::Binding::Location\n    /// [`interpolation`]: crate::Binding::Location::interpolation\n    /// [`Perspective`]: crate::Interpolation::Perspective\n    /// [`Flat`]: crate::Interpolation::Flat\n    /// [`Center`]: crate::Sampling::Center\n    pub fn apply_default_interpolation(&mut self, ty: &crate::TypeInner) {\n        if let crate::Binding::Location {\n            location: _,\n            interpolation: ref mut interpolation @ None,\n            ref mut sampling,\n            blend_src: _,\n            per_primitive: _,\n        } = *self\n        {\n            match ty.scalar_kind() {\n                Some(crate::ScalarKind::Float) => {\n                    *interpolation = Some(crate::Interpolation::Perspective);\n                    *sampling = Some(crate::Sampling::Center);\n                }\n                Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {\n                    *interpolation = Some(crate::Interpolation::Flat);\n                    *sampling = None;\n                }\n                Some(_) | None => {}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/mod.rs",
    "content": "/*!\nFrontend parsers that consume binary and text shaders and load them into [`Module`](super::Module)s.\n*/\n\nmod interpolator;\nmod type_gen;\n\n#[cfg(feature = \"spv-in\")]\npub mod atomic_upgrade;\n#[cfg(feature = \"glsl-in\")]\npub mod glsl;\n#[cfg(feature = \"spv-in\")]\npub mod spv;\n#[cfg(feature = \"wgsl-in\")]\npub mod wgsl;\n\nuse alloc::{boxed::Box, vec, vec::Vec};\nuse core::ops;\n\nuse crate::{\n    arena::{Arena, Handle, HandleVec, UniqueArena},\n    proc::{ResolveContext, ResolveError, TypeResolution},\n    FastHashMap,\n};\n\n/// A table of types for an `Arena<Expression>`.\n///\n/// A front end can use a `Typifier` to get types for an arena's expressions\n/// while it is still contributing expressions to it. At any point, you can call\n/// [`typifier.grow(expr, arena, ctx)`], where `expr` is a `Handle<Expression>`\n/// referring to something in `arena`, and the `Typifier` will resolve the types\n/// of all the expressions up to and including `expr`. Then you can write\n/// `typifier[handle]` to get the type of any handle at or before `expr`.\n///\n/// Note that `Typifier` does *not* build an `Arena<Type>` as a part of its\n/// usual operation. Ideally, a module's type arena should only contain types\n/// actually needed by `Handle<Type>`s elsewhere in the module — functions,\n/// variables, [`Compose`] expressions, other types, and so on — so we don't\n/// want every little thing that occurs as the type of some intermediate\n/// expression to show up there.\n///\n/// Instead, `Typifier` accumulates a [`TypeResolution`] for each expression,\n/// which refers to the `Arena<Type>` in the [`ResolveContext`] passed to `grow`\n/// as needed. [`TypeResolution`] is a lightweight representation for\n/// intermediate types like this; see its documentation for details.\n///\n/// If you do need to register a `Typifier`'s conclusion in an `Arena<Type>`\n/// (say, for a [`LocalVariable`] whose type you've inferred), you can use\n/// [`register_type`] to do so.\n///\n/// [`typifier.grow(expr, arena)`]: Typifier::grow\n/// [`register_type`]: Typifier::register_type\n/// [`Compose`]: crate::Expression::Compose\n/// [`LocalVariable`]: crate::LocalVariable\n#[derive(Debug, Default)]\npub struct Typifier {\n    resolutions: HandleVec<crate::Expression, TypeResolution>,\n}\n\nimpl Typifier {\n    pub const fn new() -> Self {\n        Typifier {\n            resolutions: HandleVec::new(),\n        }\n    }\n\n    pub fn reset(&mut self) {\n        self.resolutions.clear()\n    }\n\n    pub fn get<'a>(\n        &'a self,\n        expr_handle: Handle<crate::Expression>,\n        types: &'a UniqueArena<crate::Type>,\n    ) -> &'a crate::TypeInner {\n        self.resolutions[expr_handle].inner_with(types)\n    }\n\n    /// Add an expression's type to an `Arena<Type>`.\n    ///\n    /// Add the type of `expr_handle` to `types`, and return a `Handle<Type>`\n    /// referring to it.\n    ///\n    /// # Note\n    ///\n    /// If you just need a [`TypeInner`] for `expr_handle`'s type, consider\n    /// using `typifier[expression].inner_with(types)` instead. Calling\n    /// [`TypeResolution::inner_with`] often lets us avoid adding anything to\n    /// the arena, which can significantly reduce the number of types that end\n    /// up in the final module.\n    ///\n    /// [`TypeInner`]: crate::TypeInner\n    pub fn register_type(\n        &self,\n        expr_handle: Handle<crate::Expression>,\n        types: &mut UniqueArena<crate::Type>,\n    ) -> Handle<crate::Type> {\n        match self[expr_handle].clone() {\n            TypeResolution::Handle(handle) => handle,\n            TypeResolution::Value(inner) => {\n                types.insert(crate::Type { name: None, inner }, crate::Span::UNDEFINED)\n            }\n        }\n    }\n\n    /// Grow this typifier until it contains a type for `expr_handle`.\n    pub fn grow(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        expressions: &Arena<crate::Expression>,\n        ctx: &ResolveContext,\n    ) -> Result<(), ResolveError> {\n        if self.resolutions.len() <= expr_handle.index() {\n            for (eh, expr) in expressions.iter().skip(self.resolutions.len()) {\n                //Note: the closure can't `Err` by construction\n                let resolution = ctx.resolve(expr, |h| Ok(&self.resolutions[h]))?;\n                log::debug!(\"Resolving {eh:?} = {expr:?} : {resolution:?}\");\n                self.resolutions.insert(eh, resolution);\n            }\n        }\n        Ok(())\n    }\n\n    /// Recompute the type resolution for `expr_handle`.\n    ///\n    /// If the type of `expr_handle` hasn't yet been calculated, call\n    /// [`grow`](Self::grow) to ensure it is covered.\n    ///\n    /// In either case, when this returns, `self[expr_handle]` should be an\n    /// updated type resolution for `expr_handle`.\n    pub fn invalidate(\n        &mut self,\n        expr_handle: Handle<crate::Expression>,\n        expressions: &Arena<crate::Expression>,\n        ctx: &ResolveContext,\n    ) -> Result<(), ResolveError> {\n        if self.resolutions.len() <= expr_handle.index() {\n            self.grow(expr_handle, expressions, ctx)\n        } else {\n            let expr = &expressions[expr_handle];\n            //Note: the closure can't `Err` by construction\n            let resolution = ctx.resolve(expr, |h| Ok(&self.resolutions[h]))?;\n            self.resolutions[expr_handle] = resolution;\n            Ok(())\n        }\n    }\n}\n\nimpl ops::Index<Handle<crate::Expression>> for Typifier {\n    type Output = TypeResolution;\n    fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {\n        &self.resolutions[handle]\n    }\n}\n\n/// Type representing a lexical scope, associating a name to a single variable\n///\n/// The scope is generic over the variable representation and name representation\n/// in order to allow larger flexibility on the frontends on how they might\n/// represent them.\ntype Scope<Name, Var> = FastHashMap<Name, Var>;\n\n/// Structure responsible for managing variable lookups and keeping track of\n/// lexical scopes\n///\n/// The symbol table is generic over the variable representation and its name\n/// to allow larger flexibility on the frontends on how they might represent them.\n///\n/// ```\n/// use naga::front::SymbolTable;\n///\n/// // Create a new symbol table with `u32`s representing the variable\n/// let mut symbol_table: SymbolTable<&str, u32> = SymbolTable::default();\n///\n/// // Add two variables named `var1` and `var2` with 0 and 2 respectively\n/// symbol_table.add(\"var1\", 0);\n/// symbol_table.add(\"var2\", 2);\n///\n/// // Check that `var1` exists and is `0`\n/// assert_eq!(symbol_table.lookup(\"var1\"), Some(&0));\n///\n/// // Push a new scope and add a variable to it named `var1` shadowing the\n/// // variable of our previous scope\n/// symbol_table.push_scope();\n/// symbol_table.add(\"var1\", 1);\n///\n/// // Check that `var1` now points to the new value of `1` and `var2` still\n/// // exists with its value of `2`\n/// assert_eq!(symbol_table.lookup(\"var1\"), Some(&1));\n/// assert_eq!(symbol_table.lookup(\"var2\"), Some(&2));\n///\n/// // Pop the scope\n/// symbol_table.pop_scope();\n///\n/// // Check that `var1` now refers to our initial variable with value `0`\n/// assert_eq!(symbol_table.lookup(\"var1\"), Some(&0));\n/// ```\n///\n/// Scopes are ordered as a LIFO stack so a variable defined in a later scope\n/// with the same name as another variable defined in a earlier scope will take\n/// precedence in the lookup. Scopes can be added with [`push_scope`] and\n/// removed with [`pop_scope`].\n///\n/// A root scope is added when the symbol table is created and must always be\n/// present. Trying to pop it will result in a panic.\n///\n/// Variables can be added with [`add`] and looked up with [`lookup`]. Adding a\n/// variable will do so in the currently active scope and as mentioned\n/// previously a lookup will search from the current scope to the root scope.\n///\n/// [`push_scope`]: Self::push_scope\n/// [`pop_scope`]: Self::push_scope\n/// [`add`]: Self::add\n/// [`lookup`]: Self::lookup\npub struct SymbolTable<Name, Var> {\n    /// Stack of lexical scopes. Not all scopes are active; see [`cursor`].\n    ///\n    /// [`cursor`]: Self::cursor\n    scopes: Vec<Scope<Name, Var>>,\n    /// Limit of the [`scopes`] stack (exclusive). By using a separate value for\n    /// the stack length instead of `Vec`'s own internal length, the scopes can\n    /// be reused to cache memory allocations.\n    ///\n    /// [`scopes`]: Self::scopes\n    cursor: usize,\n    lookup_cursor_is_one_behind: bool,\n}\n\nimpl<Name, Var> SymbolTable<Name, Var> {\n    /// Adds a new lexical scope.\n    ///\n    /// All variables declared after this point will be added to this scope\n    /// until another scope is pushed or [`pop_scope`] is called, causing this\n    /// scope to be removed along with all variables added to it.\n    ///\n    /// # PANICS\n    /// - If the current lookup scope doesn't match the current scope\n    ///\n    /// [`pop_scope`]: Self::pop_scope\n    pub fn push_scope(&mut self) {\n        self.check_lookup_scope_matches_current_scope();\n\n        // If the cursor is equal to the scope's stack length then we need to\n        // push another empty scope. Otherwise we can reuse the already existing\n        // scope.\n        if self.scopes.len() == self.cursor {\n            self.scopes.push(FastHashMap::default())\n        } else {\n            self.scopes[self.cursor].clear();\n        }\n\n        self.cursor += 1;\n    }\n\n    /// Removes the current lexical scope and all its variables\n    ///\n    /// # PANICS\n    /// - If the current lexical scope is the root scope\n    /// - If the current lookup scope doesn't match the current scope\n    pub fn pop_scope(&mut self) {\n        // Despite the method title, the variables are only deleted when the\n        // scope is reused. This is because while a clear is inevitable if the\n        // scope needs to be reused, there are cases where the scope might be\n        // popped and not reused, i.e. if another scope with the same nesting\n        // level is never pushed again.\n        assert!(self.cursor != 1, \"Tried to pop the root scope\");\n        self.check_lookup_scope_matches_current_scope();\n\n        self.cursor -= 1;\n    }\n\n    /// Reduces the lookup scope by one level.\n    ///\n    /// # PANICS\n    /// - If the current lookup scope doesn't match the current scope\n    pub fn reduce_lookup_scope(&mut self) {\n        self.check_lookup_scope_matches_current_scope();\n        self.lookup_cursor_is_one_behind = true;\n    }\n\n    /// Resets the lookup scope to the current scope.\n    ///\n    /// # PANICS\n    /// - If the current lookup scope already matches the current scope\n    pub fn reset_lookup_scope(&mut self) {\n        assert!(\n            self.lookup_cursor_is_one_behind,\n            \"current lookup scope already matches the current scope\"\n        );\n        self.lookup_cursor_is_one_behind = false;\n    }\n\n    fn check_lookup_scope_matches_current_scope(&self) {\n        assert!(\n            !self.lookup_cursor_is_one_behind,\n            \"current lookup scope doesn't match the current scope\"\n        );\n    }\n}\n\nimpl<Name, Var> SymbolTable<Name, Var>\nwhere\n    Name: core::hash::Hash + Eq,\n{\n    /// Perform a lookup for a variable named `name`.\n    ///\n    /// As stated in the struct level documentation the lookup will proceed from\n    /// the current scope to the root scope, returning `Some` when a variable is\n    /// found or `None` if there doesn't exist a variable with `name` in any\n    /// scope.\n    pub fn lookup<Q>(&self, name: &Q) -> Option<&Var>\n    where\n        Name: core::borrow::Borrow<Q>,\n        Q: core::hash::Hash + Eq + ?Sized,\n    {\n        let cursor = self\n            .cursor\n            .saturating_sub(self.lookup_cursor_is_one_behind.into());\n        // Iterate backwards through the scopes and try to find the variable\n        for scope in self.scopes[..cursor].iter().rev() {\n            if let Some(var) = scope.get(name) {\n                return Some(var);\n            }\n        }\n\n        None\n    }\n\n    /// Adds a new variable to the current scope.\n    ///\n    /// Returns the previous variable with the same name in this scope if it\n    /// exists, so that the frontend might handle it in case variable shadowing\n    /// is disallowed.\n    pub fn add(&mut self, name: Name, var: Var) -> Option<Var> {\n        self.scopes[self.cursor - 1].insert(name, var)\n    }\n\n    /// Adds a new variable to the root scope.\n    ///\n    /// This is used in GLSL for builtins which aren't known in advance and only\n    /// when used for the first time, so there must be a way to add those\n    /// declarations to the root unconditionally from the current scope.\n    ///\n    /// Returns the previous variable with the same name in the root scope if it\n    /// exists, so that the frontend might handle it in case variable shadowing\n    /// is disallowed.\n    pub fn add_root(&mut self, name: Name, var: Var) -> Option<Var> {\n        self.scopes[0].insert(name, var)\n    }\n}\n\nimpl<Name, Var> Default for SymbolTable<Name, Var> {\n    /// Constructs a new symbol table with a root scope\n    fn default() -> Self {\n        Self {\n            scopes: vec![FastHashMap::default()],\n            cursor: 1,\n            lookup_cursor_is_one_behind: false,\n        }\n    }\n}\n\nuse core::fmt;\n\nimpl<Name: fmt::Debug, Var: fmt::Debug> fmt::Debug for SymbolTable<Name, Var> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"SymbolTable \")?;\n        f.debug_list()\n            .entries(self.scopes[..self.cursor].iter())\n            .finish()\n    }\n}\n\nimpl crate::Module {\n    pub fn get_or_insert_default_doc_comments(&mut self) -> &mut Box<crate::DocComments> {\n        self.doc_comments\n            .get_or_insert_with(|| Box::new(crate::DocComments::default()))\n    }\n}\n"
  },
  {
    "path": "naga/src/front/spv/convert.rs",
    "content": "use core::convert::TryInto;\n\nuse super::error::Error;\n\npub(super) const fn map_binary_operator(word: spirv::Op) -> Result<crate::BinaryOperator, Error> {\n    use crate::BinaryOperator;\n    use spirv::Op;\n\n    match word {\n        // Arithmetic Instructions +, -, *, /, %\n        Op::IAdd | Op::FAdd => Ok(BinaryOperator::Add),\n        Op::ISub | Op::FSub => Ok(BinaryOperator::Subtract),\n        Op::IMul | Op::FMul => Ok(BinaryOperator::Multiply),\n        Op::UDiv | Op::SDiv | Op::FDiv => Ok(BinaryOperator::Divide),\n        Op::SRem => Ok(BinaryOperator::Modulo),\n        // Relational and Logical Instructions\n        Op::IEqual | Op::FOrdEqual | Op::FUnordEqual | Op::LogicalEqual => {\n            Ok(BinaryOperator::Equal)\n        }\n        Op::INotEqual | Op::FOrdNotEqual | Op::FUnordNotEqual | Op::LogicalNotEqual => {\n            Ok(BinaryOperator::NotEqual)\n        }\n        Op::ULessThan | Op::SLessThan | Op::FOrdLessThan | Op::FUnordLessThan => {\n            Ok(BinaryOperator::Less)\n        }\n        Op::ULessThanEqual\n        | Op::SLessThanEqual\n        | Op::FOrdLessThanEqual\n        | Op::FUnordLessThanEqual => Ok(BinaryOperator::LessEqual),\n        Op::UGreaterThan | Op::SGreaterThan | Op::FOrdGreaterThan | Op::FUnordGreaterThan => {\n            Ok(BinaryOperator::Greater)\n        }\n        Op::UGreaterThanEqual\n        | Op::SGreaterThanEqual\n        | Op::FOrdGreaterThanEqual\n        | Op::FUnordGreaterThanEqual => Ok(BinaryOperator::GreaterEqual),\n        Op::BitwiseOr => Ok(BinaryOperator::InclusiveOr),\n        Op::BitwiseXor => Ok(BinaryOperator::ExclusiveOr),\n        Op::BitwiseAnd => Ok(BinaryOperator::And),\n        _ => Err(Error::UnknownBinaryOperator(word)),\n    }\n}\n\npub(super) const fn map_relational_fun(\n    word: spirv::Op,\n) -> Result<crate::RelationalFunction, Error> {\n    use crate::RelationalFunction as Rf;\n    use spirv::Op;\n\n    match word {\n        Op::All => Ok(Rf::All),\n        Op::Any => Ok(Rf::Any),\n        Op::IsNan => Ok(Rf::IsNan),\n        Op::IsInf => Ok(Rf::IsInf),\n        _ => Err(Error::UnknownRelationalFunction(word)),\n    }\n}\n\npub(super) const fn map_vector_size(word: spirv::Word) -> Result<crate::VectorSize, Error> {\n    match word {\n        2 => Ok(crate::VectorSize::Bi),\n        3 => Ok(crate::VectorSize::Tri),\n        4 => Ok(crate::VectorSize::Quad),\n        _ => Err(Error::InvalidVectorSize(word)),\n    }\n}\n\npub(super) fn map_image_dim(word: spirv::Word) -> Result<crate::ImageDimension, Error> {\n    use spirv::Dim as D;\n    match D::from_u32(word) {\n        Some(D::Dim1D) => Ok(crate::ImageDimension::D1),\n        Some(D::Dim2D) => Ok(crate::ImageDimension::D2),\n        Some(D::Dim3D) => Ok(crate::ImageDimension::D3),\n        Some(D::DimCube) => Ok(crate::ImageDimension::Cube),\n        _ => Err(Error::UnsupportedImageDim(word)),\n    }\n}\n\npub(super) fn map_image_format(word: spirv::Word) -> Result<crate::StorageFormat, Error> {\n    match spirv::ImageFormat::from_u32(word) {\n        Some(spirv::ImageFormat::R8) => Ok(crate::StorageFormat::R8Unorm),\n        Some(spirv::ImageFormat::R8Snorm) => Ok(crate::StorageFormat::R8Snorm),\n        Some(spirv::ImageFormat::R8ui) => Ok(crate::StorageFormat::R8Uint),\n        Some(spirv::ImageFormat::R8i) => Ok(crate::StorageFormat::R8Sint),\n        Some(spirv::ImageFormat::R16) => Ok(crate::StorageFormat::R16Unorm),\n        Some(spirv::ImageFormat::R16Snorm) => Ok(crate::StorageFormat::R16Snorm),\n        Some(spirv::ImageFormat::R16ui) => Ok(crate::StorageFormat::R16Uint),\n        Some(spirv::ImageFormat::R16i) => Ok(crate::StorageFormat::R16Sint),\n        Some(spirv::ImageFormat::R16f) => Ok(crate::StorageFormat::R16Float),\n        Some(spirv::ImageFormat::Rg8) => Ok(crate::StorageFormat::Rg8Unorm),\n        Some(spirv::ImageFormat::Rg8Snorm) => Ok(crate::StorageFormat::Rg8Snorm),\n        Some(spirv::ImageFormat::Rg8ui) => Ok(crate::StorageFormat::Rg8Uint),\n        Some(spirv::ImageFormat::Rg8i) => Ok(crate::StorageFormat::Rg8Sint),\n        Some(spirv::ImageFormat::R32ui) => Ok(crate::StorageFormat::R32Uint),\n        Some(spirv::ImageFormat::R32i) => Ok(crate::StorageFormat::R32Sint),\n        Some(spirv::ImageFormat::R32f) => Ok(crate::StorageFormat::R32Float),\n        Some(spirv::ImageFormat::Rg16) => Ok(crate::StorageFormat::Rg16Unorm),\n        Some(spirv::ImageFormat::Rg16Snorm) => Ok(crate::StorageFormat::Rg16Snorm),\n        Some(spirv::ImageFormat::Rg16ui) => Ok(crate::StorageFormat::Rg16Uint),\n        Some(spirv::ImageFormat::Rg16i) => Ok(crate::StorageFormat::Rg16Sint),\n        Some(spirv::ImageFormat::Rg16f) => Ok(crate::StorageFormat::Rg16Float),\n        Some(spirv::ImageFormat::Rgba8) => Ok(crate::StorageFormat::Rgba8Unorm),\n        Some(spirv::ImageFormat::Rgba8Snorm) => Ok(crate::StorageFormat::Rgba8Snorm),\n        Some(spirv::ImageFormat::Rgba8ui) => Ok(crate::StorageFormat::Rgba8Uint),\n        Some(spirv::ImageFormat::Rgba8i) => Ok(crate::StorageFormat::Rgba8Sint),\n        Some(spirv::ImageFormat::Rgb10a2ui) => Ok(crate::StorageFormat::Rgb10a2Uint),\n        Some(spirv::ImageFormat::Rgb10A2) => Ok(crate::StorageFormat::Rgb10a2Unorm),\n        Some(spirv::ImageFormat::R11fG11fB10f) => Ok(crate::StorageFormat::Rg11b10Ufloat),\n        Some(spirv::ImageFormat::R64ui) => Ok(crate::StorageFormat::R64Uint),\n        Some(spirv::ImageFormat::Rg32ui) => Ok(crate::StorageFormat::Rg32Uint),\n        Some(spirv::ImageFormat::Rg32i) => Ok(crate::StorageFormat::Rg32Sint),\n        Some(spirv::ImageFormat::Rg32f) => Ok(crate::StorageFormat::Rg32Float),\n        Some(spirv::ImageFormat::Rgba16) => Ok(crate::StorageFormat::Rgba16Unorm),\n        Some(spirv::ImageFormat::Rgba16Snorm) => Ok(crate::StorageFormat::Rgba16Snorm),\n        Some(spirv::ImageFormat::Rgba16ui) => Ok(crate::StorageFormat::Rgba16Uint),\n        Some(spirv::ImageFormat::Rgba16i) => Ok(crate::StorageFormat::Rgba16Sint),\n        Some(spirv::ImageFormat::Rgba16f) => Ok(crate::StorageFormat::Rgba16Float),\n        Some(spirv::ImageFormat::Rgba32ui) => Ok(crate::StorageFormat::Rgba32Uint),\n        Some(spirv::ImageFormat::Rgba32i) => Ok(crate::StorageFormat::Rgba32Sint),\n        Some(spirv::ImageFormat::Rgba32f) => Ok(crate::StorageFormat::Rgba32Float),\n        _ => Err(Error::UnsupportedImageFormat(word)),\n    }\n}\n\npub(super) fn map_width(word: spirv::Word) -> Result<crate::Bytes, Error> {\n    (word >> 3) // bits to bytes\n        .try_into()\n        .map_err(|_| Error::InvalidTypeWidth(word))\n}\n\npub(super) fn map_builtin(word: spirv::Word, invariant: bool) -> Result<crate::BuiltIn, Error> {\n    use spirv::BuiltIn as Bi;\n    Ok(match spirv::BuiltIn::from_u32(word) {\n        Some(Bi::Position | Bi::FragCoord) => crate::BuiltIn::Position { invariant },\n        Some(Bi::ViewIndex) => crate::BuiltIn::ViewIndex,\n        // vertex\n        Some(Bi::BaseInstance) => crate::BuiltIn::BaseInstance,\n        Some(Bi::BaseVertex) => crate::BuiltIn::BaseVertex,\n        Some(Bi::ClipDistance) => crate::BuiltIn::ClipDistances,\n        Some(Bi::CullDistance) => crate::BuiltIn::CullDistance,\n        Some(Bi::InstanceIndex) => crate::BuiltIn::InstanceIndex,\n        Some(Bi::PointSize) => crate::BuiltIn::PointSize,\n        Some(Bi::VertexIndex) => crate::BuiltIn::VertexIndex,\n        Some(Bi::DrawIndex) => crate::BuiltIn::DrawIndex,\n        // fragment\n        Some(Bi::FragDepth) => crate::BuiltIn::FragDepth,\n        Some(Bi::PointCoord) => crate::BuiltIn::PointCoord,\n        Some(Bi::FrontFacing) => crate::BuiltIn::FrontFacing,\n        Some(Bi::PrimitiveId) => crate::BuiltIn::PrimitiveIndex,\n        Some(Bi::BaryCoordKHR) => crate::BuiltIn::Barycentric { perspective: true },\n        Some(Bi::BaryCoordNoPerspKHR) => crate::BuiltIn::Barycentric { perspective: false },\n        Some(Bi::SampleId) => crate::BuiltIn::SampleIndex,\n        Some(Bi::SampleMask) => crate::BuiltIn::SampleMask,\n        // compute\n        Some(Bi::GlobalInvocationId) => crate::BuiltIn::GlobalInvocationId,\n        Some(Bi::LocalInvocationId) => crate::BuiltIn::LocalInvocationId,\n        Some(Bi::LocalInvocationIndex) => crate::BuiltIn::LocalInvocationIndex,\n        Some(Bi::WorkgroupId) => crate::BuiltIn::WorkGroupId,\n        Some(Bi::WorkgroupSize) => crate::BuiltIn::WorkGroupSize,\n        Some(Bi::NumWorkgroups) => crate::BuiltIn::NumWorkGroups,\n        // subgroup\n        Some(Bi::NumSubgroups) => crate::BuiltIn::NumSubgroups,\n        Some(Bi::SubgroupId) => crate::BuiltIn::SubgroupId,\n        Some(Bi::SubgroupSize) => crate::BuiltIn::SubgroupSize,\n        Some(Bi::SubgroupLocalInvocationId) => crate::BuiltIn::SubgroupInvocationId,\n        _ => return Err(Error::UnsupportedBuiltIn(word)),\n    })\n}\n\npub(super) fn map_storage_class(word: spirv::Word) -> Result<super::ExtendedClass, Error> {\n    use super::ExtendedClass as Ec;\n    use spirv::StorageClass as Sc;\n    Ok(match Sc::from_u32(word) {\n        Some(Sc::Function) => Ec::Global(crate::AddressSpace::Function),\n        Some(Sc::Input) => Ec::Input,\n        Some(Sc::Output) => Ec::Output,\n        Some(Sc::Private) => Ec::Global(crate::AddressSpace::Private),\n        Some(Sc::UniformConstant) => Ec::Global(crate::AddressSpace::Handle),\n        Some(Sc::StorageBuffer) => Ec::Global(crate::AddressSpace::Storage {\n            //Note: this is restricted by decorations later\n            access: crate::StorageAccess::LOAD | crate::StorageAccess::STORE,\n        }),\n        // we expect the `Storage` case to be filtered out before calling this function.\n        Some(Sc::Uniform) => Ec::Global(crate::AddressSpace::Uniform),\n        Some(Sc::Workgroup) => Ec::Global(crate::AddressSpace::WorkGroup),\n        Some(Sc::PushConstant) => Ec::Global(crate::AddressSpace::Immediate),\n        _ => return Err(Error::UnsupportedStorageClass(word)),\n    })\n}\n"
  },
  {
    "path": "naga/src/front/spv/error.rs",
    "content": "use alloc::{\n    format,\n    string::{String, ToString},\n};\n\nuse codespan_reporting::diagnostic::Diagnostic;\nuse codespan_reporting::files::SimpleFile;\nuse codespan_reporting::term;\n\nuse super::ModuleState;\n#[cfg(feature = \"stderr\")]\nuse crate::error::ErrorWrite;\nuse crate::{arena::Handle, error::replace_control_chars, front::atomic_upgrade};\n\n#[derive(Clone, Debug, thiserror::Error)]\npub enum Error {\n    #[error(\"invalid header\")]\n    InvalidHeader,\n    #[error(\"invalid word count\")]\n    InvalidWordCount,\n    #[error(\"unknown instruction {0}\")]\n    UnknownInstruction(u16),\n    #[error(\"unknown capability %{0}\")]\n    UnknownCapability(spirv::Word),\n    #[error(\"unsupported instruction {1:?} at {0:?}\")]\n    UnsupportedInstruction(ModuleState, spirv::Op),\n    #[error(\"unsupported capability {0:?}\")]\n    UnsupportedCapability(spirv::Capability),\n    #[error(\"unsupported extension {0}\")]\n    UnsupportedExtension(String),\n    #[error(\"unsupported extension set {0}\")]\n    UnsupportedExtSet(String),\n    #[error(\"unsupported extension instantiation set %{0}\")]\n    UnsupportedExtInstSet(spirv::Word),\n    #[error(\"unsupported extension instantiation %{0}\")]\n    UnsupportedExtInst(spirv::Word),\n    #[error(\"unsupported type {0:?}\")]\n    UnsupportedType(Handle<crate::Type>),\n    #[error(\"unsupported execution model %{0}\")]\n    UnsupportedExecutionModel(spirv::Word),\n    #[error(\"unsupported execution mode %{0}\")]\n    UnsupportedExecutionMode(spirv::Word),\n    #[error(\"unsupported storage class %{0}\")]\n    UnsupportedStorageClass(spirv::Word),\n    #[error(\"unsupported image dimension %{0}\")]\n    UnsupportedImageDim(spirv::Word),\n    #[error(\"unsupported image format %{0}\")]\n    UnsupportedImageFormat(spirv::Word),\n    #[error(\"unsupported builtin %{0}\")]\n    UnsupportedBuiltIn(spirv::Word),\n    #[error(\"unsupported control flow %{0}\")]\n    UnsupportedControlFlow(spirv::Word),\n    #[error(\"unsupported binary operator %{0}\")]\n    UnsupportedBinaryOperator(spirv::Word),\n    #[error(\"Naga supports OpTypeRuntimeArray in the StorageBuffer storage class only\")]\n    UnsupportedRuntimeArrayStorageClass,\n    #[error(\n        \"unsupported matrix stride {} for a {}x{} matrix with scalar width={}\",\n        stride,\n        columns,\n        rows,\n        width\n    )]\n    UnsupportedMatrixStride {\n        stride: u32,\n        columns: u8,\n        rows: u8,\n        width: u8,\n    },\n    #[error(\"unknown binary operator {0:?}\")]\n    UnknownBinaryOperator(spirv::Op),\n    #[error(\"unknown relational function {0:?}\")]\n    UnknownRelationalFunction(spirv::Op),\n    #[error(\"unsupported group operation %{0}\")]\n    UnsupportedGroupOperation(spirv::Word),\n    #[error(\"invalid parameter {0:?}\")]\n    InvalidParameter(spirv::Op),\n    #[error(\"invalid operand count {1} for {0:?}\")]\n    InvalidOperandCount(spirv::Op, u16),\n    #[error(\"invalid operand\")]\n    InvalidOperand,\n    #[error(\"invalid id %{0}\")]\n    InvalidId(spirv::Word),\n    #[error(\"invalid decoration %{0}\")]\n    InvalidDecoration(spirv::Word),\n    #[error(\"invalid type width %{0}\")]\n    InvalidTypeWidth(spirv::Word),\n    #[error(\"invalid sign %{0}\")]\n    InvalidSign(spirv::Word),\n    #[error(\"invalid inner type %{0}\")]\n    InvalidInnerType(spirv::Word),\n    #[error(\"invalid vector size %{0}\")]\n    InvalidVectorSize(spirv::Word),\n    #[error(\"invalid access type %{0}\")]\n    InvalidAccessType(spirv::Word),\n    #[error(\"invalid access {0:?}\")]\n    InvalidAccess(crate::Expression),\n    #[error(\"invalid access index %{0}\")]\n    InvalidAccessIndex(spirv::Word),\n    #[error(\"invalid index type %{0}\")]\n    InvalidIndexType(spirv::Word),\n    #[error(\"invalid binding %{0}\")]\n    InvalidBinding(spirv::Word),\n    #[error(\"invalid global var {0:?}\")]\n    InvalidGlobalVar(crate::Expression),\n    #[error(\"invalid image/sampler expression {0:?}\")]\n    InvalidImageExpression(crate::Expression),\n    #[error(\"cannot create a OpTypeImage as both a depth and storage image\")]\n    InvalidImageDepthStorage,\n    #[error(\"image read/write without format is not currently supported. See https://github.com/gfx-rs/wgpu/issues/6797\")]\n    InvalidStorageImageWithoutFormat,\n    #[error(\"invalid image base type {0:?}\")]\n    InvalidImageBaseType(Handle<crate::Type>),\n    #[error(\"invalid image {0:?}\")]\n    InvalidImage(Handle<crate::Type>),\n    #[error(\"invalid as type {0:?}\")]\n    InvalidAsType(Handle<crate::Type>),\n    #[error(\"invalid vector type {0:?}\")]\n    InvalidVectorType(Handle<crate::Type>),\n    #[error(\"inconsistent comparison sampling {0:?}\")]\n    InconsistentComparisonSampling(Handle<crate::GlobalVariable>),\n    #[error(\"wrong function result type %{0}\")]\n    WrongFunctionResultType(spirv::Word),\n    #[error(\"wrong function argument type %{0}\")]\n    WrongFunctionArgumentType(spirv::Word),\n    #[error(\"missing decoration {0:?}\")]\n    MissingDecoration(spirv::Decoration),\n    #[error(\"bad string\")]\n    BadString,\n    #[error(\"incomplete data\")]\n    IncompleteData,\n    #[error(\"invalid terminator\")]\n    InvalidTerminator,\n    #[error(\"invalid edge classification\")]\n    InvalidEdgeClassification,\n    #[error(\"cycle detected in the CFG during traversal at {0}\")]\n    ControlFlowGraphCycle(crate::front::spv::BlockId),\n    #[error(\"recursive function call %{0}\")]\n    FunctionCallCycle(spirv::Word),\n    #[error(\"invalid array size %{0}\")]\n    InvalidArraySize(spirv::Word),\n    #[error(\"invalid barrier scope %{0}\")]\n    InvalidBarrierScope(spirv::Word),\n    #[error(\"invalid barrier memory semantics %{0}\")]\n    InvalidBarrierMemorySemantics(spirv::Word),\n    #[error(\n        \"arrays of images / samplers are supported only through bindings for \\\n         now (i.e. you can't create an array of images or samplers that doesn't \\\n         come from a binding)\"\n    )]\n    NonBindingArrayOfImageOrSamplers,\n    #[error(\"naga only supports specialization constant IDs up to 65535 but was given {0}\")]\n    SpecIdTooHigh(u32),\n\n    #[error(\"atomic upgrade error: {0}\")]\n    AtomicUpgradeError(atomic_upgrade::Error),\n}\n\nimpl Error {\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {\n        self.emit_to_writer_with_path(writer, source, \"spv\");\n    }\n\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {\n        let path = path.to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n        let diagnostic = Diagnostic::error().with_message(format!(\"{self:?}\"));\n\n        crate::error::emit_to_writer(writer, &config, &files, &diagnostic)\n            .expect(\"cannot write error\");\n    }\n\n    pub fn emit_to_string(&self, source: &str) -> String {\n        self.emit_to_string_with_path(source, \"spv\")\n    }\n\n    pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String {\n        let path = path.to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n        let diagnostic = Diagnostic::error().with_message(format!(\"{self:?}\"));\n\n        let mut writer = crate::error::DiagnosticBuffer::new();\n        writer\n            .emit_to_self(&config, &files, &diagnostic)\n            .expect(\"cannot write error\");\n        writer.into_string()\n    }\n}\n\nimpl From<atomic_upgrade::Error> for Error {\n    fn from(source: atomic_upgrade::Error) -> Self {\n        Error::AtomicUpgradeError(source)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/spv/function.rs",
    "content": "use alloc::{format, vec, vec::Vec};\n\nuse super::{Error, Instruction, LookupExpression, LookupHelper as _};\nuse crate::proc::Emitter;\nuse crate::{\n    arena::{Arena, Handle},\n    front::spv::{BlockContext, BodyIndex},\n};\n\npub type BlockId = u32;\n\nimpl<I: Iterator<Item = u32>> super::Frontend<I> {\n    // Registers a function call. It will generate a dummy handle to call, which\n    // gets resolved after all the functions are processed.\n    pub(super) fn add_call(\n        &mut self,\n        from: spirv::Word,\n        to: spirv::Word,\n    ) -> Handle<crate::Function> {\n        let dummy_handle = self\n            .dummy_functions\n            .append(crate::Function::default(), Default::default());\n        self.deferred_function_calls.push(to);\n        self.function_call_graph.add_edge(from, to, ());\n        dummy_handle\n    }\n\n    pub(super) fn parse_function(&mut self, module: &mut crate::Module) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.lookup_expression.clear();\n        self.lookup_load_override.clear();\n        self.lookup_sampled_image.clear();\n\n        let result_type_id = self.next()?;\n        let fun_id = self.next()?;\n        let _fun_control = self.next()?;\n        let fun_type_id = self.next()?;\n\n        let mut fun = {\n            let ft = self.lookup_function_type.lookup(fun_type_id)?;\n            if ft.return_type_id != result_type_id {\n                return Err(Error::WrongFunctionResultType(result_type_id));\n            }\n            crate::Function {\n                name: self.future_decor.remove(&fun_id).and_then(|dec| dec.name),\n                arguments: Vec::with_capacity(ft.parameter_type_ids.len()),\n                result: if self.lookup_void_type == Some(result_type_id) {\n                    None\n                } else {\n                    let lookup_result_ty = self.lookup_type.lookup(result_type_id)?;\n                    Some(crate::FunctionResult {\n                        ty: lookup_result_ty.handle,\n                        binding: None,\n                    })\n                },\n                local_variables: Arena::new(),\n                expressions: self.make_expression_storage(\n                    &module.global_variables,\n                    &module.constants,\n                    &module.overrides,\n                ),\n                named_expressions: crate::NamedExpressions::default(),\n                body: crate::Block::new(),\n                diagnostic_filter_leaf: None,\n            }\n        };\n\n        // read parameters\n        for i in 0..fun.arguments.capacity() {\n            let start = self.data_offset;\n            match self.next_inst()? {\n                Instruction {\n                    op: spirv::Op::FunctionParameter,\n                    wc: 3,\n                } => {\n                    let type_id = self.next()?;\n                    let id = self.next()?;\n                    let handle = fun.expressions.append(\n                        crate::Expression::FunctionArgument(i as u32),\n                        self.span_from(start),\n                    );\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle,\n                            type_id,\n                            // Setting this to an invalid id will cause get_expr_handle\n                            // to default to the main body making sure no load/stores\n                            // are added.\n                            block_id: 0,\n                        },\n                    );\n                    //Note: we redo the lookup in order to work around `self` borrowing\n\n                    if type_id\n                        != self\n                            .lookup_function_type\n                            .lookup(fun_type_id)?\n                            .parameter_type_ids[i]\n                    {\n                        return Err(Error::WrongFunctionArgumentType(type_id));\n                    }\n                    let ty = self.lookup_type.lookup(type_id)?.handle;\n                    let decor = self.future_decor.remove(&id).unwrap_or_default();\n                    fun.arguments.push(crate::FunctionArgument {\n                        name: decor.name,\n                        ty,\n                        binding: None,\n                    });\n                }\n                Instruction { op, .. } => return Err(Error::InvalidParameter(op)),\n            }\n        }\n\n        // Note the index this function's handle will be assigned, for tracing.\n        let function_index = module.functions.len();\n\n        // Read body\n        self.function_call_graph.add_node(fun_id);\n        let mut parameters_sampling =\n            vec![super::image::SamplingFlags::empty(); fun.arguments.len()];\n\n        let mut block_ctx = BlockContext {\n            phis: Default::default(),\n            blocks: Default::default(),\n            body_for_label: Default::default(),\n            mergers: Default::default(),\n            bodies: Default::default(),\n            module,\n            function_id: fun_id,\n            expressions: &mut fun.expressions,\n            local_arena: &mut fun.local_variables,\n            arguments: &fun.arguments,\n            parameter_sampling: &mut parameters_sampling,\n        };\n        // Insert the main body whose parent is also himself\n        block_ctx.bodies.push(super::Body::with_parent(0));\n\n        // Scan the blocks and add them as nodes\n        loop {\n            let fun_inst = self.next_inst()?;\n            log::debug!(\"{:?}\", fun_inst.op);\n            match fun_inst.op {\n                spirv::Op::Line => {\n                    fun_inst.expect(4)?;\n                    let _file_id = self.next()?;\n                    let _row_id = self.next()?;\n                    let _col_id = self.next()?;\n                }\n                spirv::Op::Label => {\n                    // Read the label ID\n                    fun_inst.expect(2)?;\n                    let block_id = self.next()?;\n\n                    self.next_block(block_id, &mut block_ctx)?;\n                }\n                spirv::Op::FunctionEnd => {\n                    fun_inst.expect(1)?;\n                    break;\n                }\n                spirv::Op::ExtInst => {\n                    let _ = self.next()?;\n                    let _ = self.next()?;\n                    let set_id = self.next()?;\n                    if Some(set_id) == self.ext_non_semantic_id {\n                        for _ in 0..fun_inst.wc - 4 {\n                            self.next()?;\n                        }\n                    } else {\n                        return Err(Error::UnsupportedInstruction(self.state, fun_inst.op));\n                    }\n                }\n                _ => {\n                    return Err(Error::UnsupportedInstruction(self.state, fun_inst.op));\n                }\n            }\n        }\n\n        if let Some(ref prefix) = self.options.block_ctx_dump_prefix {\n            let dump_suffix = match self.lookup_entry_point.get(&fun_id) {\n                Some(ep) => format!(\"block_ctx.{:?}-{}.txt\", ep.stage, ep.name),\n                None => format!(\"block_ctx.Fun-{function_index}.txt\"),\n            };\n\n            cfg_if::cfg_if! {\n                if #[cfg(feature = \"fs\")] {\n                    let prefix: &std::path::Path = prefix.as_ref();\n                    let dest = prefix.join(dump_suffix);\n                    let dump = format!(\"{block_ctx:#?}\");\n                    if let Err(e) = std::fs::write(&dest, dump) {\n                        log::error!(\"Unable to dump the block context into {dest:?}: {e}\");\n                    }\n                } else {\n                    log::error!(\"Unable to dump the block context into {prefix:?}/{dump_suffix}: file system integration was not enabled with the `fs` feature\");\n                }\n            }\n        }\n\n        // Emit `Store` statements to properly initialize all the local variables we\n        // created for `phi` expressions.\n        //\n        // Note that get_expr_handle also contributes slightly odd entries to this table,\n        // to get the spill.\n        for phi in block_ctx.phis.iter() {\n            // Get a pointer to the local variable for the phi's value.\n            let phi_pointer: Handle<crate::Expression> = block_ctx.expressions.append(\n                crate::Expression::LocalVariable(phi.local),\n                crate::Span::default(),\n            );\n\n            // At the end of each of `phi`'s predecessor blocks, store the corresponding\n            // source value in the phi's local variable.\n            for &(source, predecessor) in phi.expressions.iter() {\n                let source_lexp = &self.lookup_expression[&source];\n                let predecessor_body_idx = block_ctx.body_for_label[&predecessor];\n                // If the expression is a global/argument it will have a 0 block\n                // id so we must use a default value instead of panicking\n                let source_body_idx = block_ctx\n                    .body_for_label\n                    .get(&source_lexp.block_id)\n                    .copied()\n                    .unwrap_or(0);\n\n                // If the Naga `Expression` generated for `source` is in scope, then we\n                // can simply store that in the phi's local variable.\n                //\n                // Otherwise, spill the source value to a local variable in the block that\n                // defines it. (We know this store dominates the predecessor; otherwise,\n                // the phi wouldn't have been able to refer to that source expression in\n                // the first place.) Then, the predecessor block can count on finding the\n                // source's value in that local variable.\n                let value = if super::is_parent(predecessor_body_idx, source_body_idx, &block_ctx) {\n                    source_lexp.handle\n                } else {\n                    // The source SPIR-V expression is not defined in the phi's\n                    // predecessor block, nor is it a globally available expression. So it\n                    // must be defined off in some other block that merely dominates the\n                    // predecessor. This means that the corresponding Naga `Expression`\n                    // may not be in scope in the predecessor block.\n                    //\n                    // In the block that defines `source`, spill it to a fresh local\n                    // variable, to ensure we can still use it at the end of the\n                    // predecessor.\n                    let ty = self.lookup_type[&source_lexp.type_id].handle;\n                    let local = block_ctx.local_arena.append(\n                        crate::LocalVariable {\n                            name: None,\n                            ty,\n                            init: None,\n                        },\n                        crate::Span::default(),\n                    );\n\n                    let pointer = block_ctx.expressions.append(\n                        crate::Expression::LocalVariable(local),\n                        crate::Span::default(),\n                    );\n\n                    // Get the spilled value of the source expression.\n                    let start = block_ctx.expressions.len();\n                    let expr = block_ctx\n                        .expressions\n                        .append(crate::Expression::Load { pointer }, crate::Span::default());\n                    let range = block_ctx.expressions.range_from(start);\n\n                    block_ctx\n                        .blocks\n                        .get_mut(&predecessor)\n                        .unwrap()\n                        .push(crate::Statement::Emit(range), crate::Span::default());\n\n                    // At the end of the block that defines it, spill the source\n                    // expression's value.\n                    block_ctx\n                        .blocks\n                        .get_mut(&source_lexp.block_id)\n                        .unwrap()\n                        .push(\n                            crate::Statement::Store {\n                                pointer,\n                                value: source_lexp.handle,\n                            },\n                            crate::Span::default(),\n                        );\n\n                    expr\n                };\n\n                // At the end of the phi predecessor block, store the source\n                // value in the phi's value.\n                block_ctx.blocks.get_mut(&predecessor).unwrap().push(\n                    crate::Statement::Store {\n                        pointer: phi_pointer,\n                        value,\n                    },\n                    crate::Span::default(),\n                )\n            }\n        }\n\n        fun.body = block_ctx.lower();\n\n        // done\n        let fun_handle = module.functions.append(fun, self.span_from_with_op(start));\n        self.lookup_function.insert(\n            fun_id,\n            super::LookupFunction {\n                handle: fun_handle,\n                parameters_sampling,\n            },\n        );\n\n        if let Some(ep) = self.lookup_entry_point.remove(&fun_id) {\n            self.deferred_entry_points.push((ep, fun_id));\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn process_entry_point(\n        &mut self,\n        module: &mut crate::Module,\n        ep: super::EntryPoint,\n        fun_id: u32,\n    ) -> Result<(), Error> {\n        // create a wrapping function\n        let mut function = crate::Function {\n            name: Some(format!(\"{}_wrap\", ep.name)),\n            arguments: Vec::new(),\n            result: None,\n            local_variables: Arena::new(),\n            expressions: Arena::new(),\n            named_expressions: crate::NamedExpressions::default(),\n            body: crate::Block::new(),\n            diagnostic_filter_leaf: None,\n        };\n\n        // 1. copy the inputs from arguments to privates\n        for &v_id in ep.variable_ids.iter() {\n            let lvar = self.lookup_variable.lookup(v_id)?;\n            if let super::Variable::Input(ref arg) = lvar.inner {\n                let span = module.global_variables.get_span(lvar.handle);\n                let arg_expr = function.expressions.append(\n                    crate::Expression::FunctionArgument(function.arguments.len() as u32),\n                    span,\n                );\n                let load_expr = if arg.ty == module.global_variables[lvar.handle].ty {\n                    arg_expr\n                } else {\n                    // The only case where the type is different is if we need to treat\n                    // unsigned integer as signed.\n                    let mut emitter = Emitter::default();\n                    emitter.start(&function.expressions);\n                    let handle = function.expressions.append(\n                        crate::Expression::As {\n                            expr: arg_expr,\n                            kind: crate::ScalarKind::Sint,\n                            convert: Some(4),\n                        },\n                        span,\n                    );\n                    function.body.extend(emitter.finish(&function.expressions));\n                    handle\n                };\n                function.body.push(\n                    crate::Statement::Store {\n                        pointer: function\n                            .expressions\n                            .append(crate::Expression::GlobalVariable(lvar.handle), span),\n                        value: load_expr,\n                    },\n                    span,\n                );\n\n                let mut arg = arg.clone();\n                if ep.stage == crate::ShaderStage::Fragment {\n                    if let Some(ref mut binding) = arg.binding {\n                        binding.apply_default_interpolation(&module.types[arg.ty].inner);\n                    }\n                }\n                function.arguments.push(arg);\n            }\n        }\n        // 2. call the wrapped function\n        let fake_id = !(module.entry_points.len() as u32); // doesn't matter, as long as it's not a collision\n        let dummy_handle = self.add_call(fake_id, fun_id);\n        function.body.push(\n            crate::Statement::Call {\n                function: dummy_handle,\n                arguments: Vec::new(),\n                result: None,\n            },\n            crate::Span::default(),\n        );\n\n        // 3. copy the outputs from privates to the result\n        //\n        // It would be nice to share struct layout code here with `parse_type_struct`,\n        // but that case needs to take into account offset decorations, which makes an\n        // abstraction harder to follow than just writing out what we mean. `Layouter`\n        // and `Alignment` cover the worst parts already.\n        let mut members = Vec::new();\n        self.layouter.update(module.to_ctx()).unwrap();\n        let mut next_member_offset = 0;\n        let mut struct_alignment = crate::proc::Alignment::ONE;\n        let mut components = Vec::new();\n        for &v_id in ep.variable_ids.iter() {\n            let lvar = self.lookup_variable.lookup(v_id)?;\n            if let super::Variable::Output(ref result) = lvar.inner {\n                let span = module.global_variables.get_span(lvar.handle);\n                let expr_handle = function\n                    .expressions\n                    .append(crate::Expression::GlobalVariable(lvar.handle), span);\n\n                // Cull problematic builtins of gl_PerVertex.\n                // See the docs for `Frontend::gl_per_vertex_builtin_access`.\n                {\n                    let ty = &module.types[result.ty];\n                    if let crate::TypeInner::Struct {\n                        members: ref original_members,\n                        span,\n                    } = ty.inner\n                    {\n                        let mut new_members = None;\n                        for (idx, member) in original_members.iter().enumerate() {\n                            if let Some(crate::Binding::BuiltIn(built_in)) = member.binding {\n                                if !self.gl_per_vertex_builtin_access.contains(&built_in) {\n                                    new_members.get_or_insert_with(|| original_members.clone())\n                                        [idx]\n                                        .binding = None;\n                                }\n                            }\n                        }\n                        if let Some(new_members) = new_members {\n                            module.types.replace(\n                                result.ty,\n                                crate::Type {\n                                    name: ty.name.clone(),\n                                    inner: crate::TypeInner::Struct {\n                                        members: new_members,\n                                        span,\n                                    },\n                                },\n                            );\n                        }\n                    }\n                }\n\n                match module.types[result.ty].inner {\n                    crate::TypeInner::Struct {\n                        members: ref sub_members,\n                        ..\n                    } => {\n                        for (index, sm) in sub_members.iter().enumerate() {\n                            if sm.binding.is_none() {\n                                continue;\n                            }\n                            let mut sm = sm.clone();\n\n                            if let Some(ref mut binding) = sm.binding {\n                                if ep.stage == crate::ShaderStage::Vertex {\n                                    binding.apply_default_interpolation(&module.types[sm.ty].inner);\n                                }\n                            }\n\n                            let member_alignment = self.layouter[sm.ty].alignment;\n                            next_member_offset = member_alignment.round_up(next_member_offset);\n                            sm.offset = next_member_offset;\n                            struct_alignment = struct_alignment.max(member_alignment);\n                            next_member_offset += self.layouter[sm.ty].size;\n                            members.push(sm);\n\n                            components.push(function.expressions.append(\n                                crate::Expression::AccessIndex {\n                                    base: expr_handle,\n                                    index: index as u32,\n                                },\n                                span,\n                            ));\n                        }\n                    }\n                    ref inner => {\n                        let mut binding = result.binding.clone();\n                        if let Some(ref mut binding) = binding {\n                            if ep.stage == crate::ShaderStage::Vertex {\n                                binding.apply_default_interpolation(inner);\n                            }\n                        }\n\n                        let member_alignment = self.layouter[result.ty].alignment;\n                        next_member_offset = member_alignment.round_up(next_member_offset);\n                        members.push(crate::StructMember {\n                            name: None,\n                            ty: result.ty,\n                            binding,\n                            offset: next_member_offset,\n                        });\n                        struct_alignment = struct_alignment.max(member_alignment);\n                        next_member_offset += self.layouter[result.ty].size;\n                        // populate just the globals first, then do `Load` in a\n                        // separate step, so that we can get a range.\n                        components.push(expr_handle);\n                    }\n                }\n            }\n        }\n\n        for (member_index, member) in members.iter().enumerate() {\n            match member.binding {\n                Some(crate::Binding::BuiltIn(crate::BuiltIn::Position { .. }))\n                    if self.options.adjust_coordinate_space =>\n                {\n                    let mut emitter = Emitter::default();\n                    emitter.start(&function.expressions);\n                    let global_expr = components[member_index];\n                    let span = function.expressions.get_span(global_expr);\n                    let access_expr = function.expressions.append(\n                        crate::Expression::AccessIndex {\n                            base: global_expr,\n                            index: 1,\n                        },\n                        span,\n                    );\n                    let load_expr = function.expressions.append(\n                        crate::Expression::Load {\n                            pointer: access_expr,\n                        },\n                        span,\n                    );\n                    let neg_expr = function.expressions.append(\n                        crate::Expression::Unary {\n                            op: crate::UnaryOperator::Negate,\n                            expr: load_expr,\n                        },\n                        span,\n                    );\n                    function.body.extend(emitter.finish(&function.expressions));\n                    function.body.push(\n                        crate::Statement::Store {\n                            pointer: access_expr,\n                            value: neg_expr,\n                        },\n                        span,\n                    );\n                }\n                _ => {}\n            }\n        }\n\n        let mut emitter = Emitter::default();\n        emitter.start(&function.expressions);\n        for component in components.iter_mut() {\n            let load_expr = crate::Expression::Load {\n                pointer: *component,\n            };\n            let span = function.expressions.get_span(*component);\n            *component = function.expressions.append(load_expr, span);\n        }\n\n        match members[..] {\n            [] => {}\n            [ref member] => {\n                function.body.extend(emitter.finish(&function.expressions));\n                let span = function.expressions.get_span(components[0]);\n                function.body.push(\n                    crate::Statement::Return {\n                        value: components.first().cloned(),\n                    },\n                    span,\n                );\n                function.result = Some(crate::FunctionResult {\n                    ty: member.ty,\n                    binding: member.binding.clone(),\n                });\n            }\n            _ => {\n                let span = crate::Span::total_span(\n                    components.iter().map(|h| function.expressions.get_span(*h)),\n                );\n                let ty = module.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Struct {\n                            members,\n                            span: struct_alignment.round_up(next_member_offset),\n                        },\n                    },\n                    span,\n                );\n                let result_expr = function\n                    .expressions\n                    .append(crate::Expression::Compose { ty, components }, span);\n                function.body.extend(emitter.finish(&function.expressions));\n                function.body.push(\n                    crate::Statement::Return {\n                        value: Some(result_expr),\n                    },\n                    span,\n                );\n                function.result = Some(crate::FunctionResult { ty, binding: None });\n            }\n        }\n\n        module.entry_points.push(crate::EntryPoint {\n            name: ep.name,\n            stage: ep.stage,\n            early_depth_test: ep.early_depth_test,\n            workgroup_size: ep.workgroup_size,\n            workgroup_size_overrides: None,\n            function,\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        });\n\n        Ok(())\n    }\n}\n\nimpl BlockContext<'_> {\n    pub(super) const fn gctx(&self) -> crate::proc::GlobalCtx<'_> {\n        crate::proc::GlobalCtx {\n            types: &self.module.types,\n            constants: &self.module.constants,\n            overrides: &self.module.overrides,\n            global_expressions: &self.module.global_expressions,\n        }\n    }\n\n    /// Consumes the `BlockContext` producing a Ir [`Block`](crate::Block)\n    fn lower(mut self) -> crate::Block {\n        fn lower_impl(\n            blocks: &mut crate::FastHashMap<spirv::Word, crate::Block>,\n            bodies: &[super::Body],\n            body_idx: BodyIndex,\n        ) -> crate::Block {\n            let mut block = crate::Block::new();\n\n            for item in bodies[body_idx].data.iter() {\n                match *item {\n                    super::BodyFragment::BlockId(id) => block.append(blocks.get_mut(&id).unwrap()),\n                    super::BodyFragment::If {\n                        condition,\n                        accept,\n                        reject,\n                    } => {\n                        let accept = lower_impl(blocks, bodies, accept);\n                        let reject = lower_impl(blocks, bodies, reject);\n\n                        block.push(\n                            crate::Statement::If {\n                                condition,\n                                accept,\n                                reject,\n                            },\n                            crate::Span::default(),\n                        )\n                    }\n                    super::BodyFragment::Loop {\n                        body,\n                        continuing,\n                        break_if,\n                    } => {\n                        let body = lower_impl(blocks, bodies, body);\n                        let continuing = lower_impl(blocks, bodies, continuing);\n\n                        block.push(\n                            crate::Statement::Loop {\n                                body,\n                                continuing,\n                                break_if,\n                            },\n                            crate::Span::default(),\n                        )\n                    }\n                    super::BodyFragment::Switch {\n                        selector,\n                        ref cases,\n                        default,\n                    } => {\n                        let mut ir_cases: Vec<_> = cases\n                            .iter()\n                            .map(|&(value, body_idx)| {\n                                let body = lower_impl(blocks, bodies, body_idx);\n\n                                // Handle simple cases that would make a fallthrough statement unreachable code\n                                let fall_through = body.last().is_none_or(|s| !s.is_terminator());\n\n                                crate::SwitchCase {\n                                    value: crate::SwitchValue::I32(value),\n                                    body,\n                                    fall_through,\n                                }\n                            })\n                            .collect();\n                        ir_cases.push(crate::SwitchCase {\n                            value: crate::SwitchValue::Default,\n                            body: lower_impl(blocks, bodies, default),\n                            fall_through: false,\n                        });\n\n                        block.push(\n                            crate::Statement::Switch {\n                                selector,\n                                cases: ir_cases,\n                            },\n                            crate::Span::default(),\n                        )\n                    }\n                    super::BodyFragment::Break => {\n                        block.push(crate::Statement::Break, crate::Span::default())\n                    }\n                    super::BodyFragment::Continue => {\n                        block.push(crate::Statement::Continue, crate::Span::default())\n                    }\n                }\n            }\n\n            block\n        }\n\n        lower_impl(&mut self.blocks, &self.bodies, 0)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/spv/image.rs",
    "content": "use alloc::vec::Vec;\n\nuse crate::{\n    arena::{Handle, UniqueArena},\n    Scalar,\n};\n\nuse super::{Error, LookupExpression, LookupHelper as _};\n\n#[derive(Clone, Debug)]\npub(super) struct LookupSampledImage {\n    image: Handle<crate::Expression>,\n    sampler: Handle<crate::Expression>,\n}\n\nbitflags::bitflags! {\n    /// Flags describing sampling method.\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct SamplingFlags: u32 {\n        /// Regular sampling.\n        const REGULAR = 0x1;\n        /// Comparison sampling.\n        const COMPARISON = 0x2;\n    }\n}\n\nimpl super::BlockContext<'_> {\n    fn get_image_expr_ty(\n        &self,\n        handle: Handle<crate::Expression>,\n    ) -> Result<Handle<crate::Type>, Error> {\n        match self.expressions[handle] {\n            crate::Expression::GlobalVariable(handle) => {\n                Ok(self.module.global_variables[handle].ty)\n            }\n            crate::Expression::FunctionArgument(i) => Ok(self.arguments[i as usize].ty),\n            crate::Expression::Access { base, .. } => Ok(self.get_image_expr_ty(base)?),\n            ref other => Err(Error::InvalidImageExpression(other.clone())),\n        }\n    }\n}\n\n/// Options of a sampling operation.\n#[derive(Debug)]\npub struct SamplingOptions {\n    /// Projection sampling: the division by W is expected to happen\n    /// in the texture unit.\n    pub project: bool,\n    /// Depth comparison sampling with a reference value.\n    pub compare: bool,\n    /// Gather sampling: Operates on four samples of one channel.\n    pub gather: bool,\n}\n\nenum ExtraCoordinate {\n    ArrayLayer,\n    Projection,\n    Garbage,\n}\n\n/// Return the texture coordinates separated from the array layer,\n/// and/or divided by the projection term.\n///\n/// The Proj sampling ops expect an extra coordinate for the W.\n/// The arrayed (can't be Proj!) images expect an extra coordinate for the layer.\nfn extract_image_coordinates(\n    image_dim: crate::ImageDimension,\n    extra_coordinate: ExtraCoordinate,\n    base: Handle<crate::Expression>,\n    coordinate_ty: Handle<crate::Type>,\n    ctx: &mut super::BlockContext,\n) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>) {\n    let (given_size, kind) = match ctx.module.types[coordinate_ty].inner {\n        crate::TypeInner::Scalar(Scalar { kind, .. }) => (None, kind),\n        crate::TypeInner::Vector {\n            size,\n            scalar: Scalar { kind, .. },\n        } => (Some(size), kind),\n        ref other => unreachable!(\"Unexpected texture coordinate {:?}\", other),\n    };\n\n    let required_size = image_dim.required_coordinate_size();\n    let required_ty = required_size.map(|size| {\n        ctx.module\n            .types\n            .get(&crate::Type {\n                name: None,\n                inner: crate::TypeInner::Vector {\n                    size,\n                    scalar: Scalar { kind, width: 4 },\n                },\n            })\n            .expect(\"Required coordinate type should have been set up by `parse_type_image`!\")\n    });\n    let extra_expr = crate::Expression::AccessIndex {\n        base,\n        index: required_size.map_or(1, |size| size as u32),\n    };\n\n    let base_span = ctx.expressions.get_span(base);\n\n    match extra_coordinate {\n        ExtraCoordinate::ArrayLayer => {\n            let extracted = match required_size {\n                None => ctx\n                    .expressions\n                    .append(crate::Expression::AccessIndex { base, index: 0 }, base_span),\n                Some(size) => {\n                    let mut components = Vec::with_capacity(size as usize);\n                    for index in 0..size as u32 {\n                        let comp = ctx\n                            .expressions\n                            .append(crate::Expression::AccessIndex { base, index }, base_span);\n                        components.push(comp);\n                    }\n                    ctx.expressions.append(\n                        crate::Expression::Compose {\n                            ty: required_ty.unwrap(),\n                            components,\n                        },\n                        base_span,\n                    )\n                }\n            };\n            let array_index_f32 = ctx.expressions.append(extra_expr, base_span);\n            let array_index = ctx.expressions.append(\n                crate::Expression::As {\n                    kind: crate::ScalarKind::Sint,\n                    expr: array_index_f32,\n                    convert: Some(4),\n                },\n                base_span,\n            );\n            (extracted, Some(array_index))\n        }\n        ExtraCoordinate::Projection => {\n            let projection = ctx.expressions.append(extra_expr, base_span);\n            let divided = match required_size {\n                None => {\n                    let temp = ctx\n                        .expressions\n                        .append(crate::Expression::AccessIndex { base, index: 0 }, base_span);\n                    ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Divide,\n                            left: temp,\n                            right: projection,\n                        },\n                        base_span,\n                    )\n                }\n                Some(size) => {\n                    let mut components = Vec::with_capacity(size as usize);\n                    for index in 0..size as u32 {\n                        let temp = ctx\n                            .expressions\n                            .append(crate::Expression::AccessIndex { base, index }, base_span);\n                        let comp = ctx.expressions.append(\n                            crate::Expression::Binary {\n                                op: crate::BinaryOperator::Divide,\n                                left: temp,\n                                right: projection,\n                            },\n                            base_span,\n                        );\n                        components.push(comp);\n                    }\n                    ctx.expressions.append(\n                        crate::Expression::Compose {\n                            ty: required_ty.unwrap(),\n                            components,\n                        },\n                        base_span,\n                    )\n                }\n            };\n            (divided, None)\n        }\n        ExtraCoordinate::Garbage if given_size == required_size => (base, None),\n        ExtraCoordinate::Garbage => {\n            use crate::SwizzleComponent as Sc;\n            let cut_expr = match required_size {\n                None => crate::Expression::AccessIndex { base, index: 0 },\n                Some(size) => crate::Expression::Swizzle {\n                    size,\n                    vector: base,\n                    pattern: [Sc::X, Sc::Y, Sc::Z, Sc::W],\n                },\n            };\n            (ctx.expressions.append(cut_expr, base_span), None)\n        }\n    }\n}\n\npub(super) fn patch_comparison_type(\n    flags: SamplingFlags,\n    var: &mut crate::GlobalVariable,\n    arena: &mut UniqueArena<crate::Type>,\n) -> bool {\n    if !flags.contains(SamplingFlags::COMPARISON) {\n        return true;\n    }\n    if flags == SamplingFlags::all() {\n        return false;\n    }\n\n    log::debug!(\"Flipping comparison for {var:?}\");\n    let original_ty = &arena[var.ty];\n    let original_ty_span = arena.get_span(var.ty);\n    let ty_inner = match original_ty.inner {\n        crate::TypeInner::Image {\n            class: crate::ImageClass::Sampled { multi, .. },\n            dim,\n            arrayed,\n        } => crate::TypeInner::Image {\n            class: crate::ImageClass::Depth { multi },\n            dim,\n            arrayed,\n        },\n        crate::TypeInner::Sampler { .. } => crate::TypeInner::Sampler { comparison: true },\n        ref other => unreachable!(\"Unexpected type for comparison mutation: {:?}\", other),\n    };\n\n    let name = original_ty.name.clone();\n    var.ty = arena.insert(\n        crate::Type {\n            name,\n            inner: ty_inner,\n        },\n        original_ty_span,\n    );\n    true\n}\n\nimpl<I: Iterator<Item = u32>> super::Frontend<I> {\n    pub(super) fn parse_image_couple(&mut self) -> Result<(), Error> {\n        let _result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let image_id = self.next()?;\n        let sampler_id = self.next()?;\n        let image_lexp = self.lookup_expression.lookup(image_id)?;\n        let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;\n        self.lookup_sampled_image.insert(\n            result_id,\n            LookupSampledImage {\n                image: image_lexp.handle,\n                sampler: sampler_lexp.handle,\n            },\n        );\n        Ok(())\n    }\n\n    pub(super) fn parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error> {\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let sampled_image_id = self.next()?;\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: self.lookup_sampled_image.lookup(sampled_image_id)?.image,\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    pub(super) fn parse_image_write(\n        &mut self,\n        words_left: u16,\n        ctx: &mut super::BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        body_idx: usize,\n    ) -> Result<crate::Statement, Error> {\n        let image_id = self.next()?;\n        let coordinate_id = self.next()?;\n        let value_id = self.next()?;\n\n        let image_ops = if words_left != 0 { self.next()? } else { 0 };\n\n        if image_ops != 0 {\n            let other = spirv::ImageOperands::from_bits_truncate(image_ops);\n            log::warn!(\"Unknown image write ops {other:?}\");\n            for _ in 1..words_left {\n                self.next()?;\n            }\n        }\n\n        let image_lexp = self.lookup_expression.lookup(image_id)?;\n        let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;\n\n        let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;\n        let coord_handle =\n            self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);\n        let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;\n        let (coordinate, array_index) = match ctx.module.types[image_ty].inner {\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class: _,\n            } => extract_image_coordinates(\n                dim,\n                if arrayed {\n                    ExtraCoordinate::ArrayLayer\n                } else {\n                    ExtraCoordinate::Garbage\n                },\n                coord_handle,\n                coord_type_handle,\n                ctx,\n            ),\n            _ => return Err(Error::InvalidImage(image_ty)),\n        };\n\n        let value_lexp = self.lookup_expression.lookup(value_id)?;\n        let value = self.get_expr_handle(value_id, value_lexp, ctx, emitter, block, body_idx);\n        let value_type = self.lookup_type.lookup(value_lexp.type_id)?.handle;\n\n        // In hlsl etc, the write value may not be the vector 4.\n        let expanded_value = match ctx.module.types[value_type].inner {\n            crate::TypeInner::Scalar(_) => Some(crate::Expression::Splat {\n                value,\n                size: crate::VectorSize::Quad,\n            }),\n            crate::TypeInner::Vector { size, .. } => match size {\n                crate::VectorSize::Bi => Some(crate::Expression::Swizzle {\n                    size: crate::VectorSize::Quad,\n                    vector: value,\n                    pattern: [\n                        crate::SwizzleComponent::X,\n                        crate::SwizzleComponent::Y,\n                        crate::SwizzleComponent::Y,\n                        crate::SwizzleComponent::Y,\n                    ],\n                }),\n                crate::VectorSize::Tri => Some(crate::Expression::Swizzle {\n                    size: crate::VectorSize::Quad,\n                    vector: value,\n                    pattern: [\n                        crate::SwizzleComponent::X,\n                        crate::SwizzleComponent::Y,\n                        crate::SwizzleComponent::Z,\n                        crate::SwizzleComponent::Z,\n                    ],\n                }),\n                crate::VectorSize::Quad => None,\n            },\n            _ => return Err(Error::InvalidVectorType(value_type)),\n        };\n\n        let value_patched = if let Some(s) = expanded_value {\n            ctx.expressions.append(s, crate::Span::default())\n        } else {\n            value\n        };\n\n        Ok(crate::Statement::ImageStore {\n            image: image_lexp.handle,\n            coordinate,\n            array_index,\n            value: value_patched,\n        })\n    }\n\n    pub(super) fn parse_image_load(\n        &mut self,\n        mut words_left: u16,\n        ctx: &mut super::BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let image_id = self.next()?;\n        let coordinate_id = self.next()?;\n\n        let mut image_ops = if words_left != 0 {\n            words_left -= 1;\n            self.next()?\n        } else {\n            0\n        };\n\n        let mut sample = None;\n        let mut level = None;\n        while image_ops != 0 {\n            let bit = 1 << image_ops.trailing_zeros();\n            match spirv::ImageOperands::from_bits_truncate(bit) {\n                spirv::ImageOperands::LOD => {\n                    let lod_expr = self.next()?;\n                    let lod_lexp = self.lookup_expression.lookup(lod_expr)?;\n                    let lod_handle =\n                        self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);\n                    level = Some(lod_handle);\n                    words_left -= 1;\n                }\n                spirv::ImageOperands::SAMPLE => {\n                    let sample_expr = self.next()?;\n                    let sample_handle = self.lookup_expression.lookup(sample_expr)?.handle;\n                    sample = Some(sample_handle);\n                    words_left -= 1;\n                }\n                other => {\n                    log::warn!(\"Unknown image load op {other:?}\");\n                    for _ in 0..words_left {\n                        self.next()?;\n                    }\n                    break;\n                }\n            }\n            image_ops ^= bit;\n        }\n\n        // No need to call get_expr_handle here since only globals/arguments are\n        // allowed as images and they are always in the root scope\n        let image_lexp = self.lookup_expression.lookup(image_id)?;\n        let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;\n\n        let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;\n        let coord_handle =\n            self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);\n        let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;\n        let (coordinate, array_index, is_depth) = match ctx.module.types[image_ty].inner {\n            crate::TypeInner::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                let (coord, array_index) = extract_image_coordinates(\n                    dim,\n                    if arrayed {\n                        ExtraCoordinate::ArrayLayer\n                    } else {\n                        ExtraCoordinate::Garbage\n                    },\n                    coord_handle,\n                    coord_type_handle,\n                    ctx,\n                );\n                (coord, array_index, class.is_depth())\n            }\n            _ => return Err(Error::InvalidImage(image_ty)),\n        };\n\n        let image_load_expr = crate::Expression::ImageLoad {\n            image: image_lexp.handle,\n            coordinate,\n            array_index,\n            sample,\n            level,\n        };\n        let image_load_handle = ctx\n            .expressions\n            .append(image_load_expr, self.span_from_with_op(start));\n\n        let handle = if is_depth {\n            let result_ty = self.lookup_type.lookup(result_type_id)?;\n            // The return type of `OpImageRead` can be a scalar or vector.\n            match ctx.module.types[result_ty.handle].inner {\n                crate::TypeInner::Vector { size, .. } => {\n                    let splat_expr = crate::Expression::Splat {\n                        size,\n                        value: image_load_handle,\n                    };\n                    ctx.expressions\n                        .append(splat_expr, self.span_from_with_op(start))\n                }\n                _ => image_load_handle,\n            }\n        } else {\n            image_load_handle\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle,\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn parse_image_sample(\n        &mut self,\n        mut words_left: u16,\n        options: SamplingOptions,\n        ctx: &mut super::BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let sampled_image_id = self.next()?;\n        let coordinate_id = self.next()?;\n        let (component_id, dref_id) = match (options.gather, options.compare) {\n            (true, false) => (Some(self.next()?), None),\n            (_, true) => (None, Some(self.next()?)),\n            (_, _) => (None, None),\n        };\n        let span = self.span_from_with_op(start);\n\n        let mut image_ops = if words_left != 0 {\n            words_left -= 1;\n            self.next()?\n        } else {\n            0\n        };\n\n        let mut level = crate::SampleLevel::Auto;\n        let mut offset = None;\n        while image_ops != 0 {\n            let bit = 1 << image_ops.trailing_zeros();\n            match spirv::ImageOperands::from_bits_truncate(bit) {\n                spirv::ImageOperands::BIAS => {\n                    let bias_expr = self.next()?;\n                    let bias_lexp = self.lookup_expression.lookup(bias_expr)?;\n                    let bias_handle =\n                        self.get_expr_handle(bias_expr, bias_lexp, ctx, emitter, block, body_idx);\n                    level = crate::SampleLevel::Bias(bias_handle);\n                    words_left -= 1;\n                }\n                spirv::ImageOperands::LOD => {\n                    let lod_expr = self.next()?;\n                    let lod_lexp = self.lookup_expression.lookup(lod_expr)?;\n                    let lod_handle =\n                        self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);\n\n                    let is_depth_image = {\n                        let image_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;\n                        let image_ty = ctx.get_image_expr_ty(image_lexp.image)?;\n                        matches!(\n                            ctx.module.types[image_ty].inner,\n                            crate::TypeInner::Image {\n                                class: crate::ImageClass::Depth { .. },\n                                ..\n                            }\n                        )\n                    };\n\n                    level = if options.compare {\n                        log::debug!(\"Assuming {lod_handle:?} is zero\");\n                        crate::SampleLevel::Zero\n                    } else if is_depth_image {\n                        log::debug!(\n                            \"Assuming level {lod_handle:?} converts losslessly to an integer\"\n                        );\n                        let expr = crate::Expression::As {\n                            expr: lod_handle,\n                            kind: crate::ScalarKind::Sint,\n                            convert: Some(4),\n                        };\n                        let s32_lod_handle = ctx.expressions.append(expr, span);\n                        crate::SampleLevel::Exact(s32_lod_handle)\n                    } else {\n                        crate::SampleLevel::Exact(lod_handle)\n                    };\n                    words_left -= 1;\n                }\n                spirv::ImageOperands::GRAD => {\n                    let grad_x_expr = self.next()?;\n                    let grad_x_lexp = self.lookup_expression.lookup(grad_x_expr)?;\n                    let grad_x_handle = self.get_expr_handle(\n                        grad_x_expr,\n                        grad_x_lexp,\n                        ctx,\n                        emitter,\n                        block,\n                        body_idx,\n                    );\n                    let grad_y_expr = self.next()?;\n                    let grad_y_lexp = self.lookup_expression.lookup(grad_y_expr)?;\n                    let grad_y_handle = self.get_expr_handle(\n                        grad_y_expr,\n                        grad_y_lexp,\n                        ctx,\n                        emitter,\n                        block,\n                        body_idx,\n                    );\n                    level = if options.compare {\n                        log::debug!(\n                            \"Assuming gradients {grad_x_handle:?} and {grad_y_handle:?} are not greater than 1\"\n                        );\n                        crate::SampleLevel::Zero\n                    } else {\n                        crate::SampleLevel::Gradient {\n                            x: grad_x_handle,\n                            y: grad_y_handle,\n                        }\n                    };\n                    words_left -= 2;\n                }\n                spirv::ImageOperands::CONST_OFFSET => {\n                    let offset_expr = self.next()?;\n                    let offset_lexp = self.lookup_expression.lookup(offset_expr)?;\n                    let offset_handle = self.get_expr_handle(\n                        offset_expr,\n                        offset_lexp,\n                        ctx,\n                        emitter,\n                        block,\n                        body_idx,\n                    );\n                    offset = Some(offset_handle);\n                    words_left -= 1;\n                }\n                other => {\n                    log::warn!(\"Unknown image sample operand {other:?}\");\n                    for _ in 0..words_left {\n                        self.next()?;\n                    }\n                    break;\n                }\n            }\n            image_ops ^= bit;\n        }\n\n        let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;\n        let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;\n        let coord_handle =\n            self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);\n        let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;\n\n        let gather = match (options.gather, component_id) {\n            (true, Some(component_id)) => {\n                let component_lexp = self.lookup_expression.lookup(component_id)?;\n\n                let component_value = match ctx.expressions[component_lexp.handle] {\n                    // VUID-StandaloneSpirv-OpImageGather-04664:\n                    // The “Component” operand of OpImageGather, and OpImageSparseGather must be the\n                    // <id> of a constant instruction.\n                    crate::Expression::Constant(const_handle) => {\n                        let constant = &ctx.module.constants[const_handle];\n                        match ctx.module.global_expressions[constant.init] {\n                            // SPIR-V specification: \"It must be a 32-bit integer type scalar.\"\n                            crate::Expression::Literal(crate::Literal::U32(value)) => value,\n                            crate::Expression::Literal(crate::Literal::I32(value)) => value as u32,\n                            _ => {\n                                log::error!(\n                                    \"Image gather component constant must be a 32-bit integer literal\"\n                                );\n                                return Err(Error::InvalidOperand);\n                            }\n                        }\n                    }\n                    _ => {\n                        log::error!(\"Image gather component must be a constant\");\n                        return Err(Error::InvalidOperand);\n                    }\n                };\n\n                debug_assert_eq!(level, crate::SampleLevel::Auto);\n                level = crate::SampleLevel::Zero;\n\n                // SPIR-V specification: \"Behavior is undefined if its value is not 0, 1, 2 or 3.\"\n                match component_value {\n                    0 => Some(crate::SwizzleComponent::X),\n                    1 => Some(crate::SwizzleComponent::Y),\n                    2 => Some(crate::SwizzleComponent::Z),\n                    3 => Some(crate::SwizzleComponent::W),\n                    other => {\n                        log::error!(\"Invalid gather component operand: {other}\");\n                        return Err(Error::InvalidOperand);\n                    }\n                }\n            }\n            (true, None) => {\n                debug_assert_eq!(level, crate::SampleLevel::Auto);\n                level = crate::SampleLevel::Zero;\n\n                Some(crate::SwizzleComponent::X)\n            }\n            (_, _) => None,\n        };\n\n        let sampling_bit = if options.compare {\n            SamplingFlags::COMPARISON\n        } else {\n            SamplingFlags::REGULAR\n        };\n\n        let image_ty = match ctx.expressions[si_lexp.image] {\n            crate::Expression::GlobalVariable(handle) => {\n                if let Some(flags) = self.handle_sampling.get_mut(&handle) {\n                    *flags |= sampling_bit;\n                }\n\n                ctx.module.global_variables[handle].ty\n            }\n\n            crate::Expression::FunctionArgument(i) => {\n                ctx.parameter_sampling[i as usize] |= sampling_bit;\n                ctx.arguments[i as usize].ty\n            }\n\n            crate::Expression::Access { base, .. } => match ctx.expressions[base] {\n                crate::Expression::GlobalVariable(handle) => {\n                    if let Some(flags) = self.handle_sampling.get_mut(&handle) {\n                        *flags |= sampling_bit;\n                    }\n\n                    match ctx.module.types[ctx.module.global_variables[handle].ty].inner {\n                        crate::TypeInner::BindingArray { base, .. } => base,\n                        _ => return Err(Error::InvalidGlobalVar(ctx.expressions[base].clone())),\n                    }\n                }\n\n                ref other => return Err(Error::InvalidGlobalVar(other.clone())),\n            },\n\n            ref other => return Err(Error::InvalidGlobalVar(other.clone())),\n        };\n\n        match ctx.expressions[si_lexp.sampler] {\n            crate::Expression::GlobalVariable(handle) => {\n                *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;\n            }\n\n            crate::Expression::FunctionArgument(i) => {\n                ctx.parameter_sampling[i as usize] |= sampling_bit;\n            }\n\n            crate::Expression::Access { base, .. } => match ctx.expressions[base] {\n                crate::Expression::GlobalVariable(handle) => {\n                    *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;\n                }\n\n                ref other => return Err(Error::InvalidGlobalVar(other.clone())),\n            },\n\n            ref other => return Err(Error::InvalidGlobalVar(other.clone())),\n        }\n\n        let ((coordinate, array_index), depth_ref, is_depth) =\n            match ctx.module.types[image_ty].inner {\n                crate::TypeInner::Image {\n                    dim,\n                    arrayed,\n                    class,\n                } => (\n                    extract_image_coordinates(\n                        dim,\n                        if options.project {\n                            ExtraCoordinate::Projection\n                        } else if arrayed {\n                            ExtraCoordinate::ArrayLayer\n                        } else {\n                            ExtraCoordinate::Garbage\n                        },\n                        coord_handle,\n                        coord_type_handle,\n                        ctx,\n                    ),\n                    {\n                        match dref_id {\n                            Some(id) => {\n                                let expr_lexp = self.lookup_expression.lookup(id)?;\n                                let mut expr = self\n                                    .get_expr_handle(id, expr_lexp, ctx, emitter, block, body_idx);\n\n                                if options.project {\n                                    let required_size = dim.required_coordinate_size();\n                                    let right = ctx.expressions.append(\n                                        crate::Expression::AccessIndex {\n                                            base: coord_handle,\n                                            index: required_size.map_or(1, |size| size as u32),\n                                        },\n                                        crate::Span::default(),\n                                    );\n                                    expr = ctx.expressions.append(\n                                        crate::Expression::Binary {\n                                            op: crate::BinaryOperator::Divide,\n                                            left: expr,\n                                            right,\n                                        },\n                                        crate::Span::default(),\n                                    )\n                                };\n                                Some(expr)\n                            }\n                            None => None,\n                        }\n                    },\n                    class.is_depth(),\n                ),\n                _ => return Err(Error::InvalidImage(image_ty)),\n            };\n\n        let expr = crate::Expression::ImageSample {\n            image: si_lexp.image,\n            sampler: si_lexp.sampler,\n            gather,\n            coordinate,\n            array_index,\n            offset,\n            level,\n            depth_ref,\n            clamp_to_edge: false,\n        };\n        let image_sample_handle = ctx.expressions.append(expr, self.span_from_with_op(start));\n        let handle = if is_depth && depth_ref.is_none() {\n            let splat_expr = crate::Expression::Splat {\n                size: crate::VectorSize::Quad,\n                value: image_sample_handle,\n            };\n            ctx.expressions\n                .append(splat_expr, self.span_from_with_op(start))\n        } else {\n            image_sample_handle\n        };\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle,\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    pub(super) fn parse_image_query_size(\n        &mut self,\n        at_level: bool,\n        ctx: &mut super::BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let image_id = self.next()?;\n        let level = if at_level {\n            let level_id = self.next()?;\n            let level_lexp = self.lookup_expression.lookup(level_id)?;\n            Some(self.get_expr_handle(level_id, level_lexp, ctx, emitter, block, body_idx))\n        } else {\n            None\n        };\n\n        // No need to call get_expr_handle here since only globals/arguments are\n        // allowed as images and they are always in the root scope\n        //TODO: handle arrays and cubes\n        let image_lexp = self.lookup_expression.lookup(image_id)?;\n\n        let expr = crate::Expression::ImageQuery {\n            image: image_lexp.handle,\n            query: crate::ImageQuery::Size { level },\n        };\n\n        let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;\n        let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();\n\n        let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {\n            crate::Expression::As {\n                expr: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                kind: crate::ScalarKind::Sint,\n                convert: Some(4),\n            }\n        } else {\n            expr\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n\n        Ok(())\n    }\n\n    pub(super) fn parse_image_query_other(\n        &mut self,\n        query: crate::ImageQuery,\n        ctx: &mut super::BlockContext,\n        block_id: spirv::Word,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let image_id = self.next()?;\n\n        // No need to call get_expr_handle here since only globals/arguments are\n        // allowed as images and they are always in the root scope\n        let image_lexp = self.lookup_expression.lookup(image_id)?.clone();\n\n        let expr = crate::Expression::ImageQuery {\n            image: image_lexp.handle,\n            query,\n        };\n\n        let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;\n        let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();\n\n        let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {\n            crate::Expression::As {\n                expr: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                kind: crate::ScalarKind::Sint,\n                convert: Some(4),\n            }\n        } else {\n            expr\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/front/spv/mod.rs",
    "content": "/*!\nFrontend for [SPIR-V][spv] (Standard Portable Intermediate Representation).\n\n## ID lookups\n\nOur IR links to everything with `Handle`, while SPIR-V uses IDs.\nIn order to keep track of the associations, the parser has many lookup tables.\nThere map `spv::Word` into a specific IR handle, plus potentially a bit of\nextra info, such as the related SPIR-V type ID.\nTODO: would be nice to find ways that avoid looking up as much\n\n## Inputs/Outputs\n\nWe create a private variable for each input/output. The relevant inputs are\npopulated at the start of an entry point. The outputs are saved at the end.\n\nThe function associated with an entry point is wrapped in another function,\nsuch that we can handle any `Return` statements without problems.\n\n## Row-major matrices\n\nWe don't handle them natively, since the IR only expects column majority.\nInstead, we detect when such matrix is accessed in the `OpAccessChain`,\nand we generate a parallel expression that loads the value, but transposed.\nThis value then gets used instead of `OpLoad` result later on.\n\n[spv]: https://www.khronos.org/registry/SPIR-V/\n*/\n\nmod convert;\nmod error;\nmod function;\nmod image;\nmod next_block;\nmod null;\n\npub use error::Error;\n\nuse alloc::{borrow::ToOwned, string::String, vec, vec::Vec};\nuse core::{convert::TryInto, mem, num::NonZeroU32};\n\nuse half::f16;\nuse petgraph::graphmap::GraphMap;\n\nuse super::atomic_upgrade::Upgrades;\nuse crate::{\n    arena::{Arena, Handle, UniqueArena},\n    proc::{Alignment, Layouter},\n    FastHashMap, FastHashSet, FastIndexMap,\n};\nuse convert::*;\nuse function::*;\n\npub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[\n    spirv::Capability::Shader,\n    spirv::Capability::VulkanMemoryModel,\n    spirv::Capability::ClipDistance,\n    spirv::Capability::CullDistance,\n    spirv::Capability::SampleRateShading,\n    spirv::Capability::DerivativeControl,\n    spirv::Capability::Matrix,\n    spirv::Capability::ImageQuery,\n    spirv::Capability::Sampled1D,\n    spirv::Capability::Image1D,\n    spirv::Capability::SampledCubeArray,\n    spirv::Capability::ImageCubeArray,\n    spirv::Capability::StorageImageExtendedFormats,\n    spirv::Capability::Int8,\n    spirv::Capability::Int16,\n    spirv::Capability::Int64,\n    spirv::Capability::Int64Atomics,\n    spirv::Capability::Float16,\n    spirv::Capability::AtomicFloat32AddEXT,\n    spirv::Capability::Float64,\n    spirv::Capability::Geometry,\n    spirv::Capability::MultiView,\n    spirv::Capability::StorageBuffer16BitAccess,\n    spirv::Capability::UniformAndStorageBuffer16BitAccess,\n    spirv::Capability::GroupNonUniform,\n    spirv::Capability::GroupNonUniformVote,\n    spirv::Capability::GroupNonUniformArithmetic,\n    spirv::Capability::GroupNonUniformBallot,\n    spirv::Capability::GroupNonUniformShuffle,\n    spirv::Capability::GroupNonUniformShuffleRelative,\n    spirv::Capability::RuntimeDescriptorArray,\n    spirv::Capability::StorageImageMultisample,\n    spirv::Capability::FragmentBarycentricKHR,\n    // tricky ones\n    spirv::Capability::UniformBufferArrayDynamicIndexing,\n    spirv::Capability::StorageBufferArrayDynamicIndexing,\n];\npub const SUPPORTED_EXTENSIONS: &[&str] = &[\n    \"SPV_KHR_storage_buffer_storage_class\",\n    \"SPV_KHR_vulkan_memory_model\",\n    \"SPV_KHR_multiview\",\n    \"SPV_EXT_descriptor_indexing\",\n    \"SPV_EXT_shader_atomic_float_add\",\n    \"SPV_KHR_16bit_storage\",\n    \"SPV_KHR_non_semantic_info\",\n    \"SPV_KHR_fragment_shader_barycentric\",\n];\n\n#[derive(Copy, Clone)]\npub struct Instruction {\n    op: spirv::Op,\n    wc: u16,\n}\n\nimpl Instruction {\n    const fn expect(self, count: u16) -> Result<(), Error> {\n        if self.wc == count {\n            Ok(())\n        } else {\n            Err(Error::InvalidOperandCount(self.op, self.wc))\n        }\n    }\n\n    fn expect_at_least(self, count: u16) -> Result<u16, Error> {\n        self.wc\n            .checked_sub(count)\n            .ok_or(Error::InvalidOperandCount(self.op, self.wc))\n    }\n}\n\nimpl crate::TypeInner {\n    fn can_comparison_sample(&self, module: &crate::Module) -> bool {\n        match *self {\n            crate::TypeInner::Image {\n                class:\n                    crate::ImageClass::Sampled {\n                        kind: crate::ScalarKind::Float,\n                        multi: false,\n                    },\n                ..\n            } => true,\n            crate::TypeInner::Sampler { .. } => true,\n            crate::TypeInner::BindingArray { base, .. } => {\n                module.types[base].inner.can_comparison_sample(module)\n            }\n            _ => false,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]\npub enum ModuleState {\n    Empty,\n    Capability,\n    Extension,\n    ExtInstImport,\n    MemoryModel,\n    EntryPoint,\n    ExecutionMode,\n    Source,\n    Name,\n    ModuleProcessed,\n    Annotation,\n    Type,\n    Function,\n}\n\ntrait LookupHelper {\n    type Target;\n    fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>;\n}\n\nimpl<T> LookupHelper for FastHashMap<spirv::Word, T> {\n    type Target = T;\n    fn lookup(&self, key: spirv::Word) -> Result<&T, Error> {\n        self.get(&key).ok_or(Error::InvalidId(key))\n    }\n}\n\nimpl crate::ImageDimension {\n    const fn required_coordinate_size(&self) -> Option<crate::VectorSize> {\n        match *self {\n            crate::ImageDimension::D1 => None,\n            crate::ImageDimension::D2 => Some(crate::VectorSize::Bi),\n            crate::ImageDimension::D3 => Some(crate::VectorSize::Tri),\n            crate::ImageDimension::Cube => Some(crate::VectorSize::Tri),\n        }\n    }\n}\n\ntype MemberIndex = u32;\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, Debug, Default)]\n    struct DecorationFlags: u32 {\n        const NON_READABLE = 0x1;\n        const NON_WRITABLE = 0x2;\n        const COHERENT = 0x4;\n        const VOLATILE = 0x8;\n    }\n}\n\nimpl DecorationFlags {\n    fn to_storage_access(self) -> crate::StorageAccess {\n        let mut access = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;\n        if self.contains(DecorationFlags::NON_READABLE) {\n            access &= !crate::StorageAccess::LOAD;\n        }\n        if self.contains(DecorationFlags::NON_WRITABLE) {\n            access &= !crate::StorageAccess::STORE;\n        }\n        access\n    }\n\n    fn to_memory_decorations(self) -> crate::MemoryDecorations {\n        let mut decorations = crate::MemoryDecorations::empty();\n        if self.contains(DecorationFlags::COHERENT) {\n            decorations |= crate::MemoryDecorations::COHERENT;\n        }\n        if self.contains(DecorationFlags::VOLATILE) {\n            decorations |= crate::MemoryDecorations::VOLATILE;\n        }\n        decorations\n    }\n}\n\n#[derive(Debug, PartialEq)]\nenum Majority {\n    Column,\n    Row,\n}\n\n#[derive(Debug, Default)]\nstruct Decoration {\n    name: Option<String>,\n    built_in: Option<spirv::Word>,\n    location: Option<spirv::Word>,\n    index: Option<spirv::Word>,\n    desc_set: Option<spirv::Word>,\n    desc_index: Option<spirv::Word>,\n    specialization_constant_id: Option<spirv::Word>,\n    storage_buffer: bool,\n    offset: Option<spirv::Word>,\n    array_stride: Option<NonZeroU32>,\n    matrix_stride: Option<NonZeroU32>,\n    matrix_major: Option<Majority>,\n    invariant: bool,\n    interpolation: Option<crate::Interpolation>,\n    sampling: Option<crate::Sampling>,\n    flags: DecorationFlags,\n}\n\nimpl Decoration {\n    const fn debug_name(&self) -> &str {\n        match self.name {\n            Some(ref name) => name.as_str(),\n            None => \"?\",\n        }\n    }\n\n    const fn resource_binding(&self) -> Option<crate::ResourceBinding> {\n        match *self {\n            Decoration {\n                desc_set: Some(group),\n                desc_index: Some(binding),\n                ..\n            } => Some(crate::ResourceBinding { group, binding }),\n            _ => None,\n        }\n    }\n\n    fn io_binding(&self) -> Result<crate::Binding, Error> {\n        match *self {\n            Decoration {\n                built_in: Some(built_in),\n                location: None,\n                invariant,\n                ..\n            } => Ok(crate::Binding::BuiltIn(map_builtin(built_in, invariant)?)),\n            Decoration {\n                built_in: None,\n                location: Some(location),\n                index: Some(index),\n                ..\n            } => Ok(crate::Binding::Location {\n                location,\n                interpolation: None,\n                sampling: None,\n                blend_src: Some(index),\n                per_primitive: false,\n            }),\n            Decoration {\n                built_in: None,\n                location: Some(location),\n                interpolation,\n                sampling,\n                ..\n            } => Ok(crate::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src: None,\n                per_primitive: false,\n            }),\n            _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),\n        }\n    }\n}\n\n#[derive(Debug)]\nstruct LookupFunctionType {\n    parameter_type_ids: Vec<spirv::Word>,\n    return_type_id: spirv::Word,\n}\n\nstruct LookupFunction {\n    handle: Handle<crate::Function>,\n    parameters_sampling: Vec<image::SamplingFlags>,\n}\n\n#[derive(Debug)]\nstruct EntryPoint {\n    stage: crate::ShaderStage,\n    name: String,\n    early_depth_test: Option<crate::EarlyDepthTest>,\n    workgroup_size: [u32; 3],\n    variable_ids: Vec<spirv::Word>,\n}\n\n#[derive(Clone, Debug)]\nstruct LookupType {\n    handle: Handle<crate::Type>,\n    base_id: Option<spirv::Word>,\n}\n\n#[derive(Debug)]\nenum Constant {\n    Constant(Handle<crate::Constant>),\n    Override(Handle<crate::Override>),\n}\n\nimpl Constant {\n    const fn to_expr(&self) -> crate::Expression {\n        match *self {\n            Self::Constant(c) => crate::Expression::Constant(c),\n            Self::Override(o) => crate::Expression::Override(o),\n        }\n    }\n}\n\n#[derive(Debug)]\nstruct LookupConstant {\n    inner: Constant,\n    type_id: spirv::Word,\n}\n\n#[derive(Debug)]\nenum Variable {\n    Global,\n    Input(crate::FunctionArgument),\n    Output(crate::FunctionResult),\n}\n\n#[derive(Debug)]\nstruct LookupVariable {\n    inner: Variable,\n    handle: Handle<crate::GlobalVariable>,\n    type_id: spirv::Word,\n}\n\n/// Information about SPIR-V result ids, stored in `Frontend::lookup_expression`.\n#[derive(Clone, Debug)]\nstruct LookupExpression {\n    /// The `Expression` constructed for this result.\n    ///\n    /// Note that, while a SPIR-V result id can be used in any block dominated\n    /// by its definition, a Naga `Expression` is only in scope for the rest of\n    /// its subtree. `Frontend::get_expr_handle` takes care of spilling the result\n    /// to a `LocalVariable` which can then be used anywhere.\n    handle: Handle<crate::Expression>,\n\n    /// The SPIR-V type of this result.\n    type_id: spirv::Word,\n\n    /// The label id of the block that defines this expression.\n    ///\n    /// This is zero for globals, constants, and function parameters, since they\n    /// originate outside any function's block.\n    block_id: spirv::Word,\n}\n\n#[derive(Debug)]\nstruct LookupMember {\n    type_id: spirv::Word,\n    // This is true for either matrices, or arrays of matrices (yikes).\n    row_major: bool,\n}\n\n#[derive(Clone, Debug)]\nenum LookupLoadOverride {\n    /// For arrays of matrices, we track them but not loading yet.\n    Pending,\n    /// For matrices, vectors, and scalars, we pre-load the data.\n    Loaded(Handle<crate::Expression>),\n}\n\n#[derive(PartialEq)]\nenum ExtendedClass {\n    Global(crate::AddressSpace),\n    Input,\n    Output,\n}\n\n#[derive(Clone, Debug)]\npub struct Options {\n    /// The IR coordinate space matches all the APIs except SPIR-V,\n    /// so by default we flip the Y coordinate of the `BuiltIn::Position`.\n    /// This flag can be used to avoid this.\n    pub adjust_coordinate_space: bool,\n    /// Only allow shaders with the known set of capabilities.\n    pub strict_capabilities: bool,\n    pub block_ctx_dump_prefix: Option<String>,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Options {\n            adjust_coordinate_space: true,\n            strict_capabilities: true,\n            block_ctx_dump_prefix: None,\n        }\n    }\n}\n\n/// An index into the `BlockContext::bodies` table.\ntype BodyIndex = usize;\n\n/// An intermediate representation of a Naga [`Statement`].\n///\n/// `Body` and `BodyFragment` values form a tree: the `BodyIndex` fields of the\n/// variants are indices of the child `Body` values in [`BlockContext::bodies`].\n/// The `lower` function assembles the final `Statement` tree from this `Body`\n/// tree. See [`BlockContext`] for details.\n///\n/// [`Statement`]: crate::Statement\n#[derive(Debug)]\nenum BodyFragment {\n    BlockId(spirv::Word),\n    If {\n        condition: Handle<crate::Expression>,\n        accept: BodyIndex,\n        reject: BodyIndex,\n    },\n    Loop {\n        /// The body of the loop. Its [`Body::parent`] is the block containing\n        /// this `Loop` fragment.\n        body: BodyIndex,\n\n        /// The loop's continuing block. This is a grandchild: its\n        /// [`Body::parent`] is the loop body block, whose index is above.\n        continuing: BodyIndex,\n\n        /// If the SPIR-V loop's back-edge branch is conditional, this is the\n        /// expression that must be `false` for the back-edge to be taken, with\n        /// `true` being for the \"loop merge\" (which breaks out of the loop).\n        break_if: Option<Handle<crate::Expression>>,\n    },\n    Switch {\n        selector: Handle<crate::Expression>,\n        cases: Vec<(i32, BodyIndex)>,\n        default: BodyIndex,\n    },\n    Break,\n    Continue,\n}\n\n/// An intermediate representation of a Naga [`Block`].\n///\n/// This will be assembled into a `Block` once we've added spills for phi nodes\n/// and out-of-scope expressions. See [`BlockContext`] for details.\n///\n/// [`Block`]: crate::Block\n#[derive(Debug)]\nstruct Body {\n    /// The index of the direct parent of this body\n    parent: usize,\n    data: Vec<BodyFragment>,\n}\n\nimpl Body {\n    /// Creates a new empty `Body` with the specified `parent`\n    pub const fn with_parent(parent: usize) -> Self {\n        Body {\n            parent,\n            data: Vec::new(),\n        }\n    }\n}\n\n#[derive(Debug)]\nstruct PhiExpression {\n    /// The local variable used for the phi node\n    local: Handle<crate::LocalVariable>,\n    /// List of (expression, block)\n    expressions: Vec<(spirv::Word, spirv::Word)>,\n}\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\nenum MergeBlockInformation {\n    LoopMerge,\n    LoopContinue,\n    SelectionMerge,\n    SwitchMerge,\n}\n\n/// Fragments of Naga IR, to be assembled into `Statements` once data flow is\n/// resolved.\n///\n/// We can't build a Naga `Statement` tree directly from SPIR-V blocks for three\n/// main reasons:\n///\n/// - We parse a function's SPIR-V blocks in the order they appear in the file.\n///   Within a function, SPIR-V requires that a block must precede any blocks it\n///   structurally dominates, but doesn't say much else about the order in which\n///   they must appear. So while we know we'll see control flow header blocks\n///   before their child constructs and merge blocks, those children and the\n///   merge blocks may appear in any order - perhaps even intermingled with\n///   children of other constructs.\n///\n/// - A SPIR-V expression can be used in any SPIR-V block dominated by its\n///   definition, whereas Naga expressions are scoped to the rest of their\n///   subtree. This means that discovering an expression use later in the\n///   function retroactively requires us to have spilled that expression into a\n///   local variable back before we left its scope. (The docs for\n///   [`Frontend::get_expr_handle`] explain this in more detail.)\n///\n/// - We translate SPIR-V OpPhi expressions as Naga local variables in which we\n///   store the appropriate value before jumping to the OpPhi's block.\n///\n/// All these cases require us to go back and amend previously generated Naga IR\n/// based on things we discover later. But modifying old blocks in arbitrary\n/// spots in a `Statement` tree is awkward.\n///\n/// Instead, as we iterate through the function's body, we accumulate\n/// control-flow-free fragments of Naga IR in the [`blocks`] table, while\n/// building a skeleton of the Naga `Statement` tree in [`bodies`]. We note any\n/// spills and temporaries we must introduce in [`phis`].\n///\n/// Finally, once we've processed the entire function, we add temporaries and\n/// spills to the fragmentary `Blocks` as directed by `phis`, and assemble them\n/// into the final Naga `Statement` tree as directed by `bodies`.\n///\n/// [`blocks`]: BlockContext::blocks\n/// [`bodies`]: BlockContext::bodies\n/// [`phis`]: BlockContext::phis\n#[derive(Debug)]\nstruct BlockContext<'function> {\n    /// Phi nodes encountered when parsing the function, used to generate spills\n    /// to local variables.\n    phis: Vec<PhiExpression>,\n\n    /// Fragments of control-flow-free Naga IR.\n    ///\n    /// These will be stitched together into a proper [`Statement`] tree according\n    /// to `bodies`, once parsing is complete.\n    ///\n    /// [`Statement`]: crate::Statement\n    blocks: FastHashMap<spirv::Word, crate::Block>,\n\n    /// Map from each SPIR-V block's label id to the index of the [`Body`] in\n    /// [`bodies`] the block should append its contents to.\n    ///\n    /// Since each statement in a Naga [`Block`] dominates the next, we are sure\n    /// to encounter their SPIR-V blocks in order. Thus, by having this table\n    /// map a SPIR-V structured control flow construct's merge block to the same\n    /// body index as its header block, when we encounter the merge block, we\n    /// will simply pick up building the [`Body`] where the header left off.\n    ///\n    /// A function's first block is special: it is the only block we encounter\n    /// without having seen its label mentioned in advance. (It's simply the\n    /// first `OpLabel` after the `OpFunction`.) We thus assume that any block\n    /// missing an entry here must be the first block, which always has body\n    /// index zero.\n    ///\n    /// [`bodies`]: BlockContext::bodies\n    /// [`Block`]: crate::Block\n    body_for_label: FastHashMap<spirv::Word, BodyIndex>,\n\n    /// SPIR-V metadata about merge/continue blocks.\n    mergers: FastHashMap<spirv::Word, MergeBlockInformation>,\n\n    /// A table of `Body` values, each representing a block in the final IR.\n    ///\n    /// The first element is always the function's top-level block.\n    bodies: Vec<Body>,\n\n    /// The module we're building.\n    module: &'function mut crate::Module,\n\n    /// Id of the function currently being processed\n    function_id: spirv::Word,\n    /// Expression arena of the function currently being processed\n    expressions: &'function mut Arena<crate::Expression>,\n    /// Local variables arena of the function currently being processed\n    local_arena: &'function mut Arena<crate::LocalVariable>,\n    /// Arguments of the function currently being processed\n    arguments: &'function [crate::FunctionArgument],\n    /// Metadata about the usage of function parameters as sampling objects\n    parameter_sampling: &'function mut [image::SamplingFlags],\n}\n\nenum SignAnchor {\n    Result,\n    Operand,\n}\n\npub struct Frontend<I> {\n    data: I,\n    data_offset: usize,\n    state: ModuleState,\n    layouter: Layouter,\n    temp_bytes: Vec<u8>,\n    ext_glsl_id: Option<spirv::Word>,\n    ext_non_semantic_id: Option<spirv::Word>,\n    future_decor: FastHashMap<spirv::Word, Decoration>,\n    future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,\n    lookup_member: FastHashMap<(Handle<crate::Type>, MemberIndex), LookupMember>,\n    handle_sampling: FastHashMap<Handle<crate::GlobalVariable>, image::SamplingFlags>,\n\n    /// A record of what is accessed by [`Atomic`] statements we've\n    /// generated, so we can upgrade the types of their operands.\n    ///\n    /// [`Atomic`]: crate::Statement::Atomic\n    upgrade_atomics: Upgrades,\n\n    lookup_type: FastHashMap<spirv::Word, LookupType>,\n    lookup_void_type: Option<spirv::Word>,\n    lookup_storage_buffer_types: FastHashMap<Handle<crate::Type>, crate::StorageAccess>,\n    lookup_constant: FastHashMap<spirv::Word, LookupConstant>,\n    lookup_variable: FastHashMap<spirv::Word, LookupVariable>,\n    lookup_expression: FastHashMap<spirv::Word, LookupExpression>,\n    // Load overrides are used to work around row-major matrices\n    lookup_load_override: FastHashMap<spirv::Word, LookupLoadOverride>,\n    lookup_sampled_image: FastHashMap<spirv::Word, image::LookupSampledImage>,\n    lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,\n    lookup_function: FastHashMap<spirv::Word, LookupFunction>,\n    lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,\n    // When parsing functions, each entry point function gets an entry here so that additional\n    // processing for them can be performed after all function parsing.\n    deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,\n    //Note: each `OpFunctionCall` gets a single entry here, indexed by the\n    // dummy `Handle<crate::Function>` of the call site.\n    deferred_function_calls: Vec<spirv::Word>,\n    dummy_functions: Arena<crate::Function>,\n    // Graph of all function calls through the module.\n    // It's used to sort the functions (as nodes) topologically,\n    // so that in the IR any called function is already known.\n    function_call_graph: GraphMap<\n        spirv::Word,\n        (),\n        petgraph::Directed,\n        core::hash::BuildHasherDefault<rustc_hash::FxHasher>,\n    >,\n    options: Options,\n\n    /// Maps for a switch from a case target to the respective body and associated literals that\n    /// use that target block id.\n    ///\n    /// Used to preserve allocations between instruction parsing.\n    switch_cases: FastIndexMap<spirv::Word, (BodyIndex, Vec<i32>)>,\n\n    /// Tracks access to gl_PerVertex's builtins, it is used to cull unused builtins since initializing those can\n    /// affect performance and the mere presence of some of these builtins might cause backends to error since they\n    /// might be unsupported.\n    ///\n    /// The problematic builtins are: PointSize, ClipDistance and CullDistance.\n    ///\n    /// glslang declares those by default even though they are never written to\n    /// (see <https://github.com/KhronosGroup/glslang/issues/1868>)\n    gl_per_vertex_builtin_access: FastHashSet<crate::BuiltIn>,\n}\n\nimpl<I: Iterator<Item = u32>> Frontend<I> {\n    pub fn new(data: I, options: &Options) -> Self {\n        Frontend {\n            data,\n            data_offset: 0,\n            state: ModuleState::Empty,\n            layouter: Layouter::default(),\n            temp_bytes: Vec::new(),\n            ext_glsl_id: None,\n            ext_non_semantic_id: None,\n            future_decor: FastHashMap::default(),\n            future_member_decor: FastHashMap::default(),\n            handle_sampling: FastHashMap::default(),\n            lookup_member: FastHashMap::default(),\n            upgrade_atomics: Default::default(),\n            lookup_type: FastHashMap::default(),\n            lookup_void_type: None,\n            lookup_storage_buffer_types: FastHashMap::default(),\n            lookup_constant: FastHashMap::default(),\n            lookup_variable: FastHashMap::default(),\n            lookup_expression: FastHashMap::default(),\n            lookup_load_override: FastHashMap::default(),\n            lookup_sampled_image: FastHashMap::default(),\n            lookup_function_type: FastHashMap::default(),\n            lookup_function: FastHashMap::default(),\n            lookup_entry_point: FastHashMap::default(),\n            deferred_entry_points: Vec::default(),\n            deferred_function_calls: Vec::default(),\n            dummy_functions: Arena::new(),\n            function_call_graph: GraphMap::new(),\n            options: options.clone(),\n            switch_cases: FastIndexMap::default(),\n            gl_per_vertex_builtin_access: FastHashSet::default(),\n        }\n    }\n\n    fn span_from(&self, from: usize) -> crate::Span {\n        crate::Span::from(from..self.data_offset)\n    }\n\n    fn span_from_with_op(&self, from: usize) -> crate::Span {\n        crate::Span::from((from - 4)..self.data_offset)\n    }\n\n    fn next(&mut self) -> Result<u32, Error> {\n        if let Some(res) = self.data.next() {\n            self.data_offset += 4;\n            Ok(res)\n        } else {\n            Err(Error::IncompleteData)\n        }\n    }\n\n    fn next_inst(&mut self) -> Result<Instruction, Error> {\n        let word = self.next()?;\n        let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16);\n        if wc == 0 {\n            return Err(Error::InvalidWordCount);\n        }\n        let op = spirv::Op::from_u32(opcode as u32).ok_or(Error::UnknownInstruction(opcode))?;\n\n        Ok(Instruction { op, wc })\n    }\n\n    fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> {\n        self.temp_bytes.clear();\n        loop {\n            if count == 0 {\n                return Err(Error::BadString);\n            }\n            count -= 1;\n            let chars = self.next()?.to_le_bytes();\n            let pos = chars.iter().position(|&c| c == 0).unwrap_or(4);\n            self.temp_bytes.extend_from_slice(&chars[..pos]);\n            if pos < 4 {\n                break;\n            }\n        }\n        core::str::from_utf8(&self.temp_bytes)\n            .map(|s| (s.to_owned(), count))\n            .map_err(|_| Error::BadString)\n    }\n\n    fn next_decoration(\n        &mut self,\n        inst: Instruction,\n        base_words: u16,\n        dec: &mut Decoration,\n    ) -> Result<(), Error> {\n        let raw = self.next()?;\n        let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?;\n        log::trace!(\"\\t\\t{}: {:?}\", dec.debug_name(), dec_typed);\n        match dec_typed {\n            spirv::Decoration::BuiltIn => {\n                inst.expect(base_words + 2)?;\n                dec.built_in = Some(self.next()?);\n            }\n            spirv::Decoration::Location => {\n                inst.expect(base_words + 2)?;\n                dec.location = Some(self.next()?);\n            }\n            spirv::Decoration::Index => {\n                inst.expect(base_words + 2)?;\n                dec.index = Some(self.next()?);\n            }\n            spirv::Decoration::DescriptorSet => {\n                inst.expect(base_words + 2)?;\n                dec.desc_set = Some(self.next()?);\n            }\n            spirv::Decoration::Binding => {\n                inst.expect(base_words + 2)?;\n                dec.desc_index = Some(self.next()?);\n            }\n            spirv::Decoration::BufferBlock => {\n                dec.storage_buffer = true;\n            }\n            spirv::Decoration::Offset => {\n                inst.expect(base_words + 2)?;\n                dec.offset = Some(self.next()?);\n            }\n            spirv::Decoration::ArrayStride => {\n                inst.expect(base_words + 2)?;\n                dec.array_stride = NonZeroU32::new(self.next()?);\n            }\n            spirv::Decoration::MatrixStride => {\n                inst.expect(base_words + 2)?;\n                dec.matrix_stride = NonZeroU32::new(self.next()?);\n            }\n            spirv::Decoration::Invariant => {\n                dec.invariant = true;\n            }\n            spirv::Decoration::NoPerspective => {\n                dec.interpolation = Some(crate::Interpolation::Linear);\n            }\n            spirv::Decoration::Flat => {\n                dec.interpolation = Some(crate::Interpolation::Flat);\n            }\n            spirv::Decoration::PerVertexKHR => {\n                dec.interpolation = Some(crate::Interpolation::PerVertex);\n            }\n            spirv::Decoration::Centroid => {\n                dec.sampling = Some(crate::Sampling::Centroid);\n            }\n            spirv::Decoration::Sample => {\n                dec.sampling = Some(crate::Sampling::Sample);\n            }\n            spirv::Decoration::NonReadable => {\n                dec.flags |= DecorationFlags::NON_READABLE;\n            }\n            spirv::Decoration::NonWritable => {\n                dec.flags |= DecorationFlags::NON_WRITABLE;\n            }\n            spirv::Decoration::Coherent => {\n                dec.flags |= DecorationFlags::COHERENT;\n            }\n            spirv::Decoration::Volatile => {\n                dec.flags |= DecorationFlags::VOLATILE;\n            }\n            spirv::Decoration::ColMajor => {\n                dec.matrix_major = Some(Majority::Column);\n            }\n            spirv::Decoration::RowMajor => {\n                dec.matrix_major = Some(Majority::Row);\n            }\n            spirv::Decoration::SpecId => {\n                dec.specialization_constant_id = Some(self.next()?);\n            }\n            other => {\n                let level = match other {\n                    // Block decorations show up everywhere and we don't\n                    // really care about them, so to prevent log spam\n                    // we demote them to debug level.\n                    spirv::Decoration::Block => log::Level::Debug,\n                    _ => log::Level::Warn,\n                };\n\n                log::log!(level, \"Unknown decoration {other:?}\");\n                for _ in base_words + 1..inst.wc {\n                    let _var = self.next()?;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Return the Naga [`Expression`] to use in `body_idx` to refer to the SPIR-V result `id`.\n    ///\n    /// Ideally, we would just have a map from each SPIR-V instruction id to the\n    /// [`Handle`] for the Naga [`Expression`] we generated for it.\n    /// Unfortunately, SPIR-V and Naga IR are different enough that such a\n    /// straightforward relationship isn't possible.\n    ///\n    /// In SPIR-V, an instruction's result id can be used by any instruction\n    /// dominated by that instruction. In Naga, an [`Expression`] is only in\n    /// scope for the remainder of its [`Block`]. In pseudocode:\n    ///\n    /// ```ignore\n    ///     loop {\n    ///         a = f();\n    ///         g(a);\n    ///         break;\n    ///     }\n    ///     h(a);\n    /// ```\n    ///\n    /// Suppose the calls to `f`, `g`, and `h` are SPIR-V instructions. In\n    /// SPIR-V, both the `g` and `h` instructions are allowed to refer to `a`,\n    /// because the loop body, including `f`, dominates both of them.\n    ///\n    /// But if `a` is a Naga [`Expression`], its scope ends at the end of the\n    /// block it's evaluated in: the loop body. Thus, while the [`Expression`]\n    /// we generate for `g` can refer to `a`, the one we generate for `h`\n    /// cannot.\n    ///\n    /// Instead, the SPIR-V front end must generate Naga IR like this:\n    ///\n    /// ```ignore\n    ///     var temp; // INTRODUCED\n    ///     loop {\n    ///         a = f();\n    ///         g(a);\n    ///         temp = a; // INTRODUCED\n    ///     }\n    ///     h(temp); // ADJUSTED\n    /// ```\n    ///\n    /// In other words, where `a` is in scope, [`Expression`]s can refer to it\n    /// directly; but once it is out of scope, we need to spill it to a\n    /// temporary and refer to that instead.\n    ///\n    /// Given a SPIR-V expression `id` and the index `body_idx` of the [body]\n    /// that wants to refer to it:\n    ///\n    /// - If the Naga [`Expression`] we generated for `id` is in scope in\n    ///   `body_idx`, then we simply return its `Handle<Expression>`.\n    ///\n    /// - Otherwise, introduce a new [`LocalVariable`], and add an entry to\n    ///   [`BlockContext::phis`] to arrange for `id`'s value to be spilled to\n    ///   it. Then emit a fresh [`Load`] of that temporary variable for use in\n    ///   `body_idx`'s block, and return its `Handle`.\n    ///\n    /// The SPIR-V domination rule ensures that the introduced [`LocalVariable`]\n    /// will always have been initialized before it is used.\n    ///\n    /// `lookup` must be the [`LookupExpression`] for `id`.\n    ///\n    /// `body_idx` argument must be the index of the [`Body`] that hopes to use\n    /// `id`'s [`Expression`].\n    ///\n    /// [`Expression`]: crate::Expression\n    /// [`Handle`]: crate::Handle\n    /// [`Block`]: crate::Block\n    /// [body]: BlockContext::bodies\n    /// [`LocalVariable`]: crate::LocalVariable\n    /// [`Load`]: crate::Expression::Load\n    fn get_expr_handle(\n        &self,\n        id: spirv::Word,\n        lookup: &LookupExpression,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        body_idx: BodyIndex,\n    ) -> Handle<crate::Expression> {\n        // What `Body` was `id` defined in?\n        let expr_body_idx = ctx\n            .body_for_label\n            .get(&lookup.block_id)\n            .copied()\n            .unwrap_or(0);\n\n        // Don't need to do a load/store if the expression is in the main body\n        // or if the expression is in the same body as where the query was\n        // requested. The body_idx might actually not be the final one if a loop\n        // or conditional occurs but in those cases we know that the new body\n        // will be a subscope of the body that was passed so we can still reuse\n        // the handle and not issue a load/store.\n        if is_parent(body_idx, expr_body_idx, ctx) {\n            lookup.handle\n        } else {\n            // Add a temporary variable of the same type which will be used to\n            // store the original expression and used in the current block\n            let ty = self.lookup_type[&lookup.type_id].handle;\n            let local = ctx.local_arena.append(\n                crate::LocalVariable {\n                    name: None,\n                    ty,\n                    init: None,\n                },\n                crate::Span::default(),\n            );\n\n            block.extend(emitter.finish(ctx.expressions));\n            let pointer = ctx.expressions.append(\n                crate::Expression::LocalVariable(local),\n                crate::Span::default(),\n            );\n            emitter.start(ctx.expressions);\n            let expr = ctx\n                .expressions\n                .append(crate::Expression::Load { pointer }, crate::Span::default());\n\n            // Add a slightly odd entry to the phi table, so that while `id`'s\n            // `Expression` is still in scope, the usual phi processing will\n            // spill its value to `local`, where we can find it later.\n            //\n            // This pretends that the block in which `id` is defined is the\n            // predecessor of some other block with a phi in it that cites id as\n            // one of its sources, and uses `local` as its variable. There is no\n            // such phi, but nobody needs to know that.\n            ctx.phis.push(PhiExpression {\n                local,\n                expressions: vec![(id, lookup.block_id)],\n            });\n\n            expr\n        }\n    }\n\n    fn parse_expr_unary_op(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::UnaryOperator,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p_id = self.next()?;\n\n        let p_lexp = self.lookup_expression.lookup(p_id)?;\n        let handle = self.get_expr_handle(p_id, p_lexp, ctx, emitter, block, body_idx);\n\n        let expr = crate::Expression::Unary { op, expr: handle };\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_expr_binary_op(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::BinaryOperator,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p1_id = self.next()?;\n        let p2_id = self.next()?;\n\n        let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);\n        let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);\n\n        let expr = crate::Expression::Binary { op, left, right };\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    /// A more complicated version of the unary op,\n    /// where we force the operand to have the same type as the result.\n    fn parse_expr_unary_op_sign_adjusted(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::UnaryOperator,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p1_id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);\n\n        let result_lookup_ty = self.lookup_type.lookup(result_type_id)?;\n        let kind = ctx.module.types[result_lookup_ty.handle]\n            .inner\n            .scalar_kind()\n            .unwrap();\n\n        let expr = crate::Expression::Unary {\n            op,\n            expr: if p1_lexp.type_id == result_type_id {\n                left\n            } else {\n                ctx.expressions.append(\n                    crate::Expression::As {\n                        expr: left,\n                        kind,\n                        convert: None,\n                    },\n                    span,\n                )\n            },\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, span),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    /// A more complicated version of the binary op,\n    /// where we force the operand to have the same type as the result.\n    /// This is mostly needed for \"i++\" and \"i--\" coming from GLSL.\n    #[allow(clippy::too_many_arguments)]\n    fn parse_expr_binary_op_sign_adjusted(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::BinaryOperator,\n        // For arithmetic operations, we need the sign of operands to match the result.\n        // For boolean operations, however, the operands need to match the signs, but\n        // result is always different - a boolean.\n        anchor: SignAnchor,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p1_id = self.next()?;\n        let p2_id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);\n        let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);\n\n        let expected_type_id = match anchor {\n            SignAnchor::Result => result_type_id,\n            SignAnchor::Operand => p1_lexp.type_id,\n        };\n        let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;\n        let kind = ctx.module.types[expected_lookup_ty.handle]\n            .inner\n            .scalar_kind()\n            .unwrap();\n\n        let expr = crate::Expression::Binary {\n            op,\n            left: if p1_lexp.type_id == expected_type_id {\n                left\n            } else {\n                ctx.expressions.append(\n                    crate::Expression::As {\n                        expr: left,\n                        kind,\n                        convert: None,\n                    },\n                    span,\n                )\n            },\n            right: if p2_lexp.type_id == expected_type_id {\n                right\n            } else {\n                ctx.expressions.append(\n                    crate::Expression::As {\n                        expr: right,\n                        kind,\n                        convert: None,\n                    },\n                    span,\n                )\n            },\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, span),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    /// A version of the binary op where one or both of the arguments might need to be casted to a\n    /// specific integer kind (unsigned or signed), used for operations like OpINotEqual or\n    /// OpUGreaterThan.\n    #[allow(clippy::too_many_arguments)]\n    fn parse_expr_int_comparison(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::BinaryOperator,\n        kind: crate::ScalarKind,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p1_id = self.next()?;\n        let p2_id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);\n        let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;\n        let p1_kind = ctx.module.types[p1_lookup_ty.handle]\n            .inner\n            .scalar_kind()\n            .unwrap();\n        let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);\n        let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;\n        let p2_kind = ctx.module.types[p2_lookup_ty.handle]\n            .inner\n            .scalar_kind()\n            .unwrap();\n\n        let expr = crate::Expression::Binary {\n            op,\n            left: if p1_kind == kind {\n                left\n            } else {\n                ctx.expressions.append(\n                    crate::Expression::As {\n                        expr: left,\n                        kind,\n                        convert: None,\n                    },\n                    span,\n                )\n            },\n            right: if p2_kind == kind {\n                right\n            } else {\n                ctx.expressions.append(\n                    crate::Expression::As {\n                        expr: right,\n                        kind,\n                        convert: None,\n                    },\n                    span,\n                )\n            },\n        };\n\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, span),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_expr_shift_op(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        op: crate::BinaryOperator,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let p1_id = self.next()?;\n        let p2_id = self.next()?;\n\n        let span = self.span_from_with_op(start);\n\n        let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);\n        let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n        let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);\n        // convert the shift to Uint\n        let right = ctx.expressions.append(\n            crate::Expression::As {\n                expr: p2_handle,\n                kind: crate::ScalarKind::Uint,\n                convert: None,\n            },\n            span,\n        );\n\n        let expr = crate::Expression::Binary { op, left, right };\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, span),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_expr_derivative(\n        &mut self,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let arg_id = self.next()?;\n\n        let arg_lexp = self.lookup_expression.lookup(arg_id)?;\n        let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);\n\n        let expr = crate::Expression::Derivative {\n            axis,\n            ctrl,\n            expr: arg_handle,\n        };\n        self.lookup_expression.insert(\n            result_id,\n            LookupExpression {\n                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),\n                type_id: result_type_id,\n                block_id,\n            },\n        );\n        Ok(())\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn insert_composite(\n        &self,\n        root_expr: Handle<crate::Expression>,\n        root_type_id: spirv::Word,\n        object_expr: Handle<crate::Expression>,\n        selections: &[spirv::Word],\n        type_arena: &UniqueArena<crate::Type>,\n        expressions: &mut Arena<crate::Expression>,\n        span: crate::Span,\n    ) -> Result<Handle<crate::Expression>, Error> {\n        let selection = match selections.first() {\n            Some(&index) => index,\n            None => return Ok(object_expr),\n        };\n        let root_span = expressions.get_span(root_expr);\n        let root_lookup = self.lookup_type.lookup(root_type_id)?;\n\n        let (count, child_type_id) = match type_arena[root_lookup.handle].inner {\n            crate::TypeInner::Struct { ref members, .. } => {\n                let child_member = self\n                    .lookup_member\n                    .get(&(root_lookup.handle, selection))\n                    .ok_or(Error::InvalidAccessType(root_type_id))?;\n                (members.len(), child_member.type_id)\n            }\n            crate::TypeInner::Array { size, .. } => {\n                let size = match size {\n                    crate::ArraySize::Constant(size) => size.get(),\n                    crate::ArraySize::Pending(_) => {\n                        unreachable!();\n                    }\n                    // A runtime sized array is not a composite type\n                    crate::ArraySize::Dynamic => {\n                        return Err(Error::InvalidAccessType(root_type_id))\n                    }\n                };\n\n                let child_type_id = root_lookup\n                    .base_id\n                    .ok_or(Error::InvalidAccessType(root_type_id))?;\n\n                (size as usize, child_type_id)\n            }\n            crate::TypeInner::Vector { size, .. }\n            | crate::TypeInner::Matrix { columns: size, .. } => {\n                let child_type_id = root_lookup\n                    .base_id\n                    .ok_or(Error::InvalidAccessType(root_type_id))?;\n                (size as usize, child_type_id)\n            }\n            _ => return Err(Error::InvalidAccessType(root_type_id)),\n        };\n\n        let mut components = Vec::with_capacity(count);\n        for index in 0..count as u32 {\n            let expr = expressions.append(\n                crate::Expression::AccessIndex {\n                    base: root_expr,\n                    index,\n                },\n                if index == selection { span } else { root_span },\n            );\n            components.push(expr);\n        }\n        components[selection as usize] = self.insert_composite(\n            components[selection as usize],\n            child_type_id,\n            object_expr,\n            &selections[1..],\n            type_arena,\n            expressions,\n            span,\n        )?;\n\n        Ok(expressions.append(\n            crate::Expression::Compose {\n                ty: root_lookup.handle,\n                components,\n            },\n            span,\n        ))\n    }\n\n    /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].\n    ///\n    /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of\n    /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it\n    /// is a pointer.\n    ///\n    /// This may entail spilling `pointer_id`'s value to a temporary:\n    /// see [`get_expr_handle`]'s documentation.\n    ///\n    /// [`Expression`]: crate::Expression\n    /// [`Type`]: crate::Type\n    /// [`Handle`]: crate::Handle\n    /// [`get_expr_handle`]: Frontend::get_expr_handle\n    fn get_exp_and_base_ty_handles(\n        &self,\n        pointer_id: spirv::Word,\n        ctx: &mut BlockContext,\n        emitter: &mut crate::proc::Emitter,\n        block: &mut crate::Block,\n        body_idx: usize,\n    ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {\n        log::trace!(\"\\t\\t\\tlooking up pointer expr {pointer_id:?}\");\n        let p_lexp_handle;\n        let p_lexp_ty_id;\n        {\n            let lexp = self.lookup_expression.lookup(pointer_id)?;\n            p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);\n            p_lexp_ty_id = lexp.type_id;\n        };\n\n        log::trace!(\"\\t\\t\\tlooking up pointer type {pointer_id:?}\");\n        let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;\n        let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;\n\n        log::trace!(\"\\t\\t\\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}\");\n        let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;\n\n        Ok((p_lexp_handle, p_base_ty.handle))\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn parse_atomic_expr_with_value(\n        &mut self,\n        inst: Instruction,\n        emitter: &mut crate::proc::Emitter,\n        ctx: &mut BlockContext,\n        block: &mut crate::Block,\n        block_id: spirv::Word,\n        body_idx: usize,\n        atomic_function: crate::AtomicFunction,\n    ) -> Result<(), Error> {\n        inst.expect(7)?;\n        let start = self.data_offset;\n        let result_type_id = self.next()?;\n        let result_id = self.next()?;\n        let pointer_id = self.next()?;\n        let _scope_id = self.next()?;\n        let _memory_semantics_id = self.next()?;\n        let value_id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let (p_lexp_handle, p_base_ty_handle) =\n            self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;\n\n        log::trace!(\"\\t\\t\\tlooking up value expr {value_id:?}\");\n        let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;\n\n        block.extend(emitter.finish(ctx.expressions));\n        // Create an expression for our result\n        let r_lexp_handle = {\n            let expr = crate::Expression::AtomicResult {\n                ty: p_base_ty_handle,\n                comparison: false,\n            };\n            let handle = ctx.expressions.append(expr, span);\n            self.lookup_expression.insert(\n                result_id,\n                LookupExpression {\n                    handle,\n                    type_id: result_type_id,\n                    block_id,\n                },\n            );\n            handle\n        };\n        emitter.start(ctx.expressions);\n\n        // Create a statement for the op itself\n        let stmt = crate::Statement::Atomic {\n            pointer: p_lexp_handle,\n            fun: atomic_function,\n            value: v_lexp_handle,\n            result: Some(r_lexp_handle),\n        };\n        block.push(stmt, span);\n\n        // Store any associated global variables so we can upgrade their types later\n        self.record_atomic_access(ctx, p_lexp_handle)?;\n\n        Ok(())\n    }\n\n    fn make_expression_storage(\n        &mut self,\n        globals: &Arena<crate::GlobalVariable>,\n        constants: &Arena<crate::Constant>,\n        overrides: &Arena<crate::Override>,\n    ) -> Arena<crate::Expression> {\n        let mut expressions = Arena::new();\n        assert!(self.lookup_expression.is_empty());\n        // register global variables\n        for (&id, var) in self.lookup_variable.iter() {\n            let span = globals.get_span(var.handle);\n            let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);\n            self.lookup_expression.insert(\n                id,\n                LookupExpression {\n                    type_id: var.type_id,\n                    handle,\n                    // Setting this to an invalid id will cause get_expr_handle\n                    // to default to the main body making sure no load/stores\n                    // are added.\n                    block_id: 0,\n                },\n            );\n        }\n        // register constants\n        for (&id, con) in self.lookup_constant.iter() {\n            let (expr, span) = match con.inner {\n                Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),\n                Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),\n            };\n            let handle = expressions.append(expr, span);\n            self.lookup_expression.insert(\n                id,\n                LookupExpression {\n                    type_id: con.type_id,\n                    handle,\n                    // Setting this to an invalid id will cause get_expr_handle\n                    // to default to the main body making sure no load/stores\n                    // are added.\n                    block_id: 0,\n                },\n            );\n        }\n        // done\n        expressions\n    }\n\n    fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {\n        if state < self.state {\n            Err(Error::UnsupportedInstruction(self.state, op))\n        } else {\n            self.state = state;\n            Ok(())\n        }\n    }\n\n    /// Walk the statement tree and patch it in the following cases:\n    /// 1. Function call targets are replaced by `deferred_function_calls` map\n    fn patch_statements(\n        &mut self,\n        statements: &mut crate::Block,\n        expressions: &mut Arena<crate::Expression>,\n        fun_parameter_sampling: &mut [image::SamplingFlags],\n    ) -> Result<(), Error> {\n        use crate::Statement as S;\n        let mut i = 0usize;\n        while i < statements.len() {\n            match statements[i] {\n                S::Emit(_) => {}\n                S::Block(ref mut block) => {\n                    self.patch_statements(block, expressions, fun_parameter_sampling)?;\n                }\n                S::If {\n                    condition: _,\n                    ref mut accept,\n                    ref mut reject,\n                } => {\n                    self.patch_statements(reject, expressions, fun_parameter_sampling)?;\n                    self.patch_statements(accept, expressions, fun_parameter_sampling)?;\n                }\n                S::Switch {\n                    selector: _,\n                    ref mut cases,\n                } => {\n                    for case in cases.iter_mut() {\n                        self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;\n                    }\n                }\n                S::Loop {\n                    ref mut body,\n                    ref mut continuing,\n                    break_if: _,\n                } => {\n                    self.patch_statements(body, expressions, fun_parameter_sampling)?;\n                    self.patch_statements(continuing, expressions, fun_parameter_sampling)?;\n                }\n                S::Break\n                | S::Continue\n                | S::Return { .. }\n                | S::Kill\n                | S::ControlBarrier(_)\n                | S::MemoryBarrier(_)\n                | S::Store { .. }\n                | S::ImageStore { .. }\n                | S::Atomic { .. }\n                | S::ImageAtomic { .. }\n                | S::RayQuery { .. }\n                | S::SubgroupBallot { .. }\n                | S::SubgroupCollectiveOperation { .. }\n                | S::SubgroupGather { .. }\n                | S::RayPipelineFunction(..) => {}\n                S::Call {\n                    function: ref mut callee,\n                    ref arguments,\n                    ..\n                } => {\n                    let fun_id = self.deferred_function_calls[callee.index()];\n                    let fun_lookup = self.lookup_function.lookup(fun_id)?;\n                    *callee = fun_lookup.handle;\n\n                    // Patch sampling flags\n                    for (arg_index, arg) in arguments.iter().enumerate() {\n                        let flags = match fun_lookup.parameters_sampling.get(arg_index) {\n                            Some(&flags) if !flags.is_empty() => flags,\n                            _ => continue,\n                        };\n\n                        match expressions[*arg] {\n                            crate::Expression::GlobalVariable(handle) => {\n                                if let Some(sampling) = self.handle_sampling.get_mut(&handle) {\n                                    *sampling |= flags\n                                }\n                            }\n                            crate::Expression::FunctionArgument(i) => {\n                                fun_parameter_sampling[i as usize] |= flags;\n                            }\n                            ref other => return Err(Error::InvalidGlobalVar(other.clone())),\n                        }\n                    }\n                }\n                S::WorkGroupUniformLoad { .. } => unreachable!(),\n                S::CooperativeStore { .. } => unreachable!(),\n            }\n            i += 1;\n        }\n        Ok(())\n    }\n\n    fn patch_function(\n        &mut self,\n        handle: Option<Handle<crate::Function>>,\n        fun: &mut crate::Function,\n    ) -> Result<(), Error> {\n        // Note: this search is a bit unfortunate\n        let (fun_id, mut parameters_sampling) = match handle {\n            Some(h) => {\n                let (&fun_id, lookup) = self\n                    .lookup_function\n                    .iter_mut()\n                    .find(|&(_, ref lookup)| lookup.handle == h)\n                    .unwrap();\n                (fun_id, mem::take(&mut lookup.parameters_sampling))\n            }\n            None => (0, Vec::new()),\n        };\n\n        for (_, expr) in fun.expressions.iter_mut() {\n            if let crate::Expression::CallResult(ref mut function) = *expr {\n                let fun_id = self.deferred_function_calls[function.index()];\n                *function = self.lookup_function.lookup(fun_id)?.handle;\n            }\n        }\n\n        self.patch_statements(\n            &mut fun.body,\n            &mut fun.expressions,\n            &mut parameters_sampling,\n        )?;\n\n        if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {\n            lookup.parameters_sampling = parameters_sampling;\n        }\n        Ok(())\n    }\n\n    pub fn parse(mut self) -> Result<crate::Module, Error> {\n        let mut module = {\n            if self.next()? != spirv::MAGIC_NUMBER {\n                return Err(Error::InvalidHeader);\n            }\n            let version_raw = self.next()?;\n            let generator = self.next()?;\n            let _bound = self.next()?;\n            let _schema = self.next()?;\n            log::debug!(\"Generated by {generator} version {version_raw:x}\");\n            crate::Module::default()\n        };\n\n        self.layouter.clear();\n        self.dummy_functions = Arena::new();\n        self.lookup_function.clear();\n        self.function_call_graph.clear();\n\n        loop {\n            use spirv::Op;\n\n            let inst = match self.next_inst() {\n                Ok(inst) => inst,\n                Err(Error::IncompleteData) => break,\n                Err(other) => return Err(other),\n            };\n            log::debug!(\"\\t{:?} [{}]\", inst.op, inst.wc);\n\n            match inst.op {\n                Op::Capability => self.parse_capability(inst),\n                Op::Extension => self.parse_extension(inst),\n                Op::ExtInstImport => self.parse_ext_inst_import(inst),\n                Op::MemoryModel => self.parse_memory_model(inst),\n                Op::EntryPoint => self.parse_entry_point(inst),\n                Op::ExecutionMode => self.parse_execution_mode(inst),\n                Op::String => self.parse_string(inst),\n                Op::Source => self.parse_source(inst),\n                Op::SourceExtension => self.parse_source_extension(inst),\n                Op::Name => self.parse_name(inst),\n                Op::MemberName => self.parse_member_name(inst),\n                Op::ModuleProcessed => self.parse_module_processed(inst),\n                Op::Decorate => self.parse_decorate(inst),\n                Op::MemberDecorate => self.parse_member_decorate(inst),\n                Op::TypeVoid => self.parse_type_void(inst),\n                Op::TypeBool => self.parse_type_bool(inst, &mut module),\n                Op::TypeInt => self.parse_type_int(inst, &mut module),\n                Op::TypeFloat => self.parse_type_float(inst, &mut module),\n                Op::TypeVector => self.parse_type_vector(inst, &mut module),\n                Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),\n                Op::TypeFunction => self.parse_type_function(inst),\n                Op::TypePointer => self.parse_type_pointer(inst, &mut module),\n                Op::TypeArray => self.parse_type_array(inst, &mut module),\n                Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),\n                Op::TypeStruct => self.parse_type_struct(inst, &mut module),\n                Op::TypeImage => self.parse_type_image(inst, &mut module),\n                Op::TypeSampledImage => self.parse_type_sampled_image(inst),\n                Op::TypeSampler => self.parse_type_sampler(inst, &mut module),\n                Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),\n                Op::ConstantComposite | Op::SpecConstantComposite => {\n                    self.parse_composite_constant(inst, &mut module)\n                }\n                Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),\n                Op::ConstantTrue | Op::SpecConstantTrue => {\n                    self.parse_bool_constant(inst, true, &mut module)\n                }\n                Op::ConstantFalse | Op::SpecConstantFalse => {\n                    self.parse_bool_constant(inst, false, &mut module)\n                }\n                Op::Variable => self.parse_global_variable(inst, &mut module),\n                Op::Function => {\n                    self.switch(ModuleState::Function, inst.op)?;\n                    inst.expect(5)?;\n                    self.parse_function(&mut module)\n                }\n                Op::ExtInst => {\n                    // Ignore the result type and result id\n                    let _ = self.next()?;\n                    let _ = self.next()?;\n                    let set_id = self.next()?;\n                    if Some(set_id) == self.ext_non_semantic_id {\n                        // We've already skipped the instruction byte, result type, result id, and instruction set id\n                        for _ in 0..inst.wc - 4 {\n                            self.next()?;\n                        }\n                        Ok(())\n                    } else {\n                        return Err(Error::UnsupportedInstruction(self.state, inst.op));\n                    }\n                }\n                _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO\n            }?;\n        }\n\n        if !self.upgrade_atomics.is_empty() {\n            log::debug!(\"Upgrading atomic pointers...\");\n            module.upgrade_atomics(&self.upgrade_atomics)?;\n        }\n\n        // Do entry point specific processing after all functions are parsed so that we can\n        // cull unused problematic builtins of gl_PerVertex.\n        for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {\n            self.process_entry_point(&mut module, ep, fun_id)?;\n        }\n\n        log::debug!(\"Patching...\");\n        {\n            let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)\n                .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;\n            nodes.reverse(); // we need dominated first\n            let mut functions = mem::take(&mut module.functions);\n            for fun_id in nodes {\n                if fun_id > !(functions.len() as u32) {\n                    // skip all the fake IDs registered for the entry points\n                    continue;\n                }\n                let lookup = self.lookup_function.get_mut(&fun_id).unwrap();\n                // take out the function from the old array\n                let fun = mem::take(&mut functions[lookup.handle]);\n                // add it to the newly formed arena, and adjust the lookup\n                lookup.handle = module\n                    .functions\n                    .append(fun, functions.get_span(lookup.handle));\n            }\n        }\n        // patch all the functions\n        for (handle, fun) in module.functions.iter_mut() {\n            self.patch_function(Some(handle), fun)?;\n        }\n        for ep in module.entry_points.iter_mut() {\n            self.patch_function(None, &mut ep.function)?;\n        }\n\n        // Check all the images and samplers to have consistent comparison property.\n        for (handle, flags) in self.handle_sampling.drain() {\n            if !image::patch_comparison_type(\n                flags,\n                module.global_variables.get_mut(handle),\n                &mut module.types,\n            ) {\n                return Err(Error::InconsistentComparisonSampling(handle));\n            }\n        }\n\n        if !self.future_decor.is_empty() {\n            log::debug!(\"Unused item decorations: {:?}\", self.future_decor);\n            self.future_decor.clear();\n        }\n        if !self.future_member_decor.is_empty() {\n            log::debug!(\"Unused member decorations: {:?}\", self.future_member_decor);\n            self.future_member_decor.clear();\n        }\n\n        Ok(module)\n    }\n\n    fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Capability, inst.op)?;\n        inst.expect(2)?;\n        let capability = self.next()?;\n        let cap =\n            spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;\n        if !SUPPORTED_CAPABILITIES.contains(&cap) {\n            if self.options.strict_capabilities {\n                return Err(Error::UnsupportedCapability(cap));\n            } else {\n                log::warn!(\"Unknown capability {cap:?}\");\n            }\n        }\n        Ok(())\n    }\n\n    fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Extension, inst.op)?;\n        inst.expect_at_least(2)?;\n        let (name, left) = self.next_string(inst.wc - 1)?;\n        if left != 0 {\n            return Err(Error::InvalidOperand);\n        }\n        if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {\n            return Err(Error::UnsupportedExtension(name));\n        }\n        Ok(())\n    }\n\n    fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Extension, inst.op)?;\n        inst.expect_at_least(3)?;\n        let result_id = self.next()?;\n        let (name, left) = self.next_string(inst.wc - 2)?;\n        if left != 0 {\n            return Err(Error::InvalidOperand);\n        }\n        if &name == \"GLSL.std.450\" {\n            self.ext_glsl_id = Some(result_id);\n        } else if &name == \"NonSemantic.Shader.DebugInfo.100\" {\n            // We completely ignore this extension. All related instructions are\n            // non-semantic and only for debug purposes, and the spec says they\n            // are ignorable. Many compilers (dxc, slang, etc) will emit these\n            // instructions depending on configuration.\n            self.ext_non_semantic_id = Some(result_id);\n        } else {\n            return Err(Error::UnsupportedExtSet(name));\n        }\n        Ok(())\n    }\n\n    fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::MemoryModel, inst.op)?;\n        inst.expect(3)?;\n        let _addressing_model = self.next()?;\n        let _memory_model = self.next()?;\n        Ok(())\n    }\n\n    fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::EntryPoint, inst.op)?;\n        inst.expect_at_least(4)?;\n        let exec_model = self.next()?;\n        let exec_model = spirv::ExecutionModel::from_u32(exec_model)\n            .ok_or(Error::UnsupportedExecutionModel(exec_model))?;\n        let function_id = self.next()?;\n        let (name, left) = self.next_string(inst.wc - 3)?;\n        let ep = EntryPoint {\n            stage: match exec_model {\n                spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,\n                spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,\n                spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,\n                spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,\n                spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,\n                _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),\n            },\n            name,\n            early_depth_test: None,\n            workgroup_size: [0; 3],\n            variable_ids: self.data.by_ref().take(left as usize).collect(),\n        };\n        self.lookup_entry_point.insert(function_id, ep);\n        Ok(())\n    }\n\n    fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {\n        use spirv::ExecutionMode;\n\n        self.switch(ModuleState::ExecutionMode, inst.op)?;\n        inst.expect_at_least(3)?;\n\n        let ep_id = self.next()?;\n        let mode_id = self.next()?;\n        let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();\n\n        let ep = self\n            .lookup_entry_point\n            .get_mut(&ep_id)\n            .ok_or(Error::InvalidId(ep_id))?;\n        let mode =\n            ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;\n\n        match mode {\n            ExecutionMode::EarlyFragmentTests => {\n                ep.early_depth_test = Some(crate::EarlyDepthTest::Force);\n            }\n            ExecutionMode::DepthUnchanged => {\n                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {\n                    if let &mut crate::EarlyDepthTest::Allow {\n                        ref mut conservative,\n                    } = early_depth_test\n                    {\n                        *conservative = crate::ConservativeDepth::Unchanged;\n                    }\n                } else {\n                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {\n                        conservative: crate::ConservativeDepth::Unchanged,\n                    });\n                }\n            }\n            ExecutionMode::DepthGreater => {\n                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {\n                    if let &mut crate::EarlyDepthTest::Allow {\n                        ref mut conservative,\n                    } = early_depth_test\n                    {\n                        *conservative = crate::ConservativeDepth::GreaterEqual;\n                    }\n                } else {\n                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {\n                        conservative: crate::ConservativeDepth::GreaterEqual,\n                    });\n                }\n            }\n            ExecutionMode::DepthLess => {\n                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {\n                    if let &mut crate::EarlyDepthTest::Allow {\n                        ref mut conservative,\n                    } = early_depth_test\n                    {\n                        *conservative = crate::ConservativeDepth::LessEqual;\n                    }\n                } else {\n                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {\n                        conservative: crate::ConservativeDepth::LessEqual,\n                    });\n                }\n            }\n            ExecutionMode::DepthReplacing => {\n                // Ignored because it can be deduced from the IR.\n            }\n            ExecutionMode::OriginUpperLeft => {\n                // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode.\n            }\n            ExecutionMode::LocalSize => {\n                ep.workgroup_size = [args[0], args[1], args[2]];\n            }\n            _ => {\n                return Err(Error::UnsupportedExecutionMode(mode_id));\n            }\n        }\n\n        Ok(())\n    }\n\n    fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Source, inst.op)?;\n        inst.expect_at_least(3)?;\n        let _id = self.next()?;\n        let (_name, _) = self.next_string(inst.wc - 2)?;\n        Ok(())\n    }\n\n    fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Source, inst.op)?;\n        for _ in 1..inst.wc {\n            let _ = self.next()?;\n        }\n        Ok(())\n    }\n\n    fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Source, inst.op)?;\n        inst.expect_at_least(2)?;\n        let (_name, _) = self.next_string(inst.wc - 1)?;\n        Ok(())\n    }\n\n    fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Name, inst.op)?;\n        inst.expect_at_least(3)?;\n        let id = self.next()?;\n        let (name, left) = self.next_string(inst.wc - 2)?;\n        if left != 0 {\n            return Err(Error::InvalidOperand);\n        }\n        self.future_decor.entry(id).or_default().name = Some(name);\n        Ok(())\n    }\n\n    fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Name, inst.op)?;\n        inst.expect_at_least(4)?;\n        let id = self.next()?;\n        let member = self.next()?;\n        let (name, left) = self.next_string(inst.wc - 3)?;\n        if left != 0 {\n            return Err(Error::InvalidOperand);\n        }\n\n        self.future_member_decor\n            .entry((id, member))\n            .or_default()\n            .name = Some(name);\n        Ok(())\n    }\n\n    fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Name, inst.op)?;\n        inst.expect_at_least(2)?;\n        let (_info, left) = self.next_string(inst.wc - 1)?;\n        //Note: string is ignored\n        if left != 0 {\n            return Err(Error::InvalidOperand);\n        }\n        Ok(())\n    }\n\n    fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Annotation, inst.op)?;\n        inst.expect_at_least(3)?;\n        let id = self.next()?;\n        let mut dec = self.future_decor.remove(&id).unwrap_or_default();\n        self.next_decoration(inst, 2, &mut dec)?;\n        self.future_decor.insert(id, dec);\n        Ok(())\n    }\n\n    fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Annotation, inst.op)?;\n        inst.expect_at_least(4)?;\n        let id = self.next()?;\n        let member = self.next()?;\n\n        let mut dec = self\n            .future_member_decor\n            .remove(&(id, member))\n            .unwrap_or_default();\n        self.next_decoration(inst, 3, &mut dec)?;\n        self.future_member_decor.insert((id, member), dec);\n        Ok(())\n    }\n\n    fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(2)?;\n        let id = self.next()?;\n        self.lookup_void_type = Some(id);\n        Ok(())\n    }\n\n    fn parse_type_bool(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(2)?;\n        let id = self.next()?;\n        let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: None,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_int(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(4)?;\n        let id = self.next()?;\n        let width = self.next()?;\n        let sign = self.next()?;\n        let inner = crate::TypeInner::Scalar(crate::Scalar {\n            kind: match sign {\n                0 => crate::ScalarKind::Uint,\n                1 => crate::ScalarKind::Sint,\n                _ => return Err(Error::InvalidSign(sign)),\n            },\n            width: map_width(width)?,\n        });\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: None,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_float(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(3)?;\n        let id = self.next()?;\n        let width = self.next()?;\n        let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: None,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_vector(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(4)?;\n        let id = self.next()?;\n        let type_id = self.next()?;\n        let type_lookup = self.lookup_type.lookup(type_id)?;\n        let scalar = match module.types[type_lookup.handle].inner {\n            crate::TypeInner::Scalar(scalar) => scalar,\n            _ => return Err(Error::InvalidInnerType(type_id)),\n        };\n        let component_count = self.next()?;\n        let inner = crate::TypeInner::Vector {\n            size: map_vector_size(component_count)?,\n            scalar,\n        };\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: Some(type_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_matrix(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(4)?;\n        let id = self.next()?;\n        let vector_type_id = self.next()?;\n        let num_columns = self.next()?;\n        let decor = self.future_decor.remove(&id);\n\n        let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;\n        let inner = match module.types[vector_type_lookup.handle].inner {\n            crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {\n                columns: map_vector_size(num_columns)?,\n                rows: size,\n                scalar,\n            },\n            _ => return Err(Error::InvalidInnerType(vector_type_id)),\n        };\n\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: decor.and_then(|dec| dec.name),\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: Some(vector_type_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect_at_least(3)?;\n        let id = self.next()?;\n        let return_type_id = self.next()?;\n        let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();\n        self.lookup_function_type.insert(\n            id,\n            LookupFunctionType {\n                parameter_type_ids,\n                return_type_id,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_pointer(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(4)?;\n        let id = self.next()?;\n        let storage_class = self.next()?;\n        let type_id = self.next()?;\n\n        let decor = self.future_decor.remove(&id);\n        let base_lookup_ty = self.lookup_type.lookup(type_id)?;\n        let base_inner = &module.types[base_lookup_ty.handle].inner;\n\n        let space = if let Some(space) = base_inner.pointer_space() {\n            space\n        } else if self\n            .lookup_storage_buffer_types\n            .contains_key(&base_lookup_ty.handle)\n        {\n            crate::AddressSpace::Storage {\n                access: crate::StorageAccess::default(),\n            }\n        } else {\n            match map_storage_class(storage_class)? {\n                ExtendedClass::Global(space) => space,\n                ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,\n            }\n        };\n\n        // We don't support pointers to runtime-sized arrays in the `Uniform`\n        // storage class with the `BufferBlock` decoration. Runtime-sized arrays\n        // should be in the StorageBuffer class.\n        if let crate::TypeInner::Array {\n            size: crate::ArraySize::Dynamic,\n            ..\n        } = *base_inner\n        {\n            match space {\n                crate::AddressSpace::Storage { .. } => {}\n                _ => {\n                    return Err(Error::UnsupportedRuntimeArrayStorageClass);\n                }\n            }\n        }\n\n        // Don't bother with pointer stuff for `Handle` types.\n        let lookup_ty = if space == crate::AddressSpace::Handle {\n            base_lookup_ty.clone()\n        } else {\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: decor.and_then(|dec| dec.name),\n                        inner: crate::TypeInner::Pointer {\n                            base: base_lookup_ty.handle,\n                            space,\n                        },\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: Some(type_id),\n            }\n        };\n        self.lookup_type.insert(id, lookup_ty);\n        Ok(())\n    }\n\n    fn parse_type_array(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(4)?;\n        let id = self.next()?;\n        let type_id = self.next()?;\n        let length_id = self.next()?;\n        let length_const = self.lookup_constant.lookup(length_id)?;\n\n        let size = resolve_constant(module.to_ctx(), &length_const.inner)\n            .and_then(NonZeroU32::new)\n            .ok_or(Error::InvalidArraySize(length_id))?;\n\n        let decor = self.future_decor.remove(&id).unwrap_or_default();\n        let base = self.lookup_type.lookup(type_id)?.handle;\n\n        self.layouter.update(module.to_ctx()).unwrap();\n\n        // HACK if the underlying type is an image or a sampler, let's assume\n        //      that we're dealing with a binding-array\n        //\n        // Note that it's not a strictly correct assumption, but rather a trade\n        // off caused by an impedance mismatch between SPIR-V's and Naga's type\n        // systems - Naga distinguishes between arrays and binding-arrays via\n        // types (i.e. both kinds of arrays are just different types), while\n        // SPIR-V distinguishes between them through usage - e.g. given:\n        //\n        // ```\n        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f\n        // %uint_256 = OpConstant %uint 256\n        // %image_array = OpTypeArray %image %uint_256\n        // ```\n        //\n        // ```\n        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f\n        // %uint_256 = OpConstant %uint 256\n        // %image_array = OpTypeArray %image %uint_256\n        // %image_array_ptr = OpTypePointer UniformConstant %image_array\n        // ```\n        //\n        // ... in the first case, `%image_array` should technically correspond\n        // to `TypeInner::Array`, while in the second case it should say\n        // `TypeInner::BindingArray` (kinda, depending on whether `%image_array`\n        // is ever used as a freestanding type or rather always through the\n        // pointer-indirection).\n        //\n        // Anyway, at the moment we don't support other kinds of image / sampler\n        // arrays than those binding-based, so this assumption is pretty safe\n        // for now.\n        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =\n            module.types[base].inner\n        {\n            crate::TypeInner::BindingArray {\n                base,\n                size: crate::ArraySize::Constant(size),\n            }\n        } else {\n            crate::TypeInner::Array {\n                base,\n                size: crate::ArraySize::Constant(size),\n                stride: match decor.array_stride {\n                    Some(stride) => stride.get(),\n                    None => self.layouter[base].to_stride(),\n                },\n            }\n        };\n\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: decor.name,\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: Some(type_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_runtime_array(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(3)?;\n        let id = self.next()?;\n        let type_id = self.next()?;\n\n        let decor = self.future_decor.remove(&id).unwrap_or_default();\n        let base = self.lookup_type.lookup(type_id)?.handle;\n\n        self.layouter.update(module.to_ctx()).unwrap();\n\n        // HACK same case as in `parse_type_array()`\n        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =\n            module.types[base].inner\n        {\n            crate::TypeInner::BindingArray {\n                base: self.lookup_type.lookup(type_id)?.handle,\n                size: crate::ArraySize::Dynamic,\n            }\n        } else {\n            crate::TypeInner::Array {\n                base: self.lookup_type.lookup(type_id)?.handle,\n                size: crate::ArraySize::Dynamic,\n                stride: match decor.array_stride {\n                    Some(stride) => stride.get(),\n                    None => self.layouter[base].to_stride(),\n                },\n            }\n        };\n\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: module.types.insert(\n                    crate::Type {\n                        name: decor.name,\n                        inner,\n                    },\n                    self.span_from_with_op(start),\n                ),\n                base_id: Some(type_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_struct(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect_at_least(2)?;\n        let id = self.next()?;\n        let parent_decor = self.future_decor.remove(&id);\n        let is_storage_buffer = parent_decor\n            .as_ref()\n            .is_some_and(|decor| decor.storage_buffer);\n\n        self.layouter.update(module.to_ctx()).unwrap();\n\n        let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);\n        let mut member_lookups = Vec::with_capacity(members.capacity());\n        let mut storage_access = crate::StorageAccess::empty();\n        let mut span = 0;\n        let mut alignment = Alignment::ONE;\n        for i in 0..u32::from(inst.wc) - 2 {\n            let type_id = self.next()?;\n            let ty = self.lookup_type.lookup(type_id)?.handle;\n            let decor = self\n                .future_member_decor\n                .remove(&(id, i))\n                .unwrap_or_default();\n\n            storage_access |= decor.flags.to_storage_access();\n\n            member_lookups.push(LookupMember {\n                type_id,\n                row_major: decor.matrix_major == Some(Majority::Row),\n            });\n\n            let member_alignment = self.layouter[ty].alignment;\n            span = member_alignment.round_up(span);\n            alignment = member_alignment.max(alignment);\n\n            let binding = decor.io_binding().ok();\n            if let Some(offset) = decor.offset {\n                span = offset;\n            }\n            let offset = span;\n\n            span += self.layouter[ty].size;\n\n            let inner = &module.types[ty].inner;\n            if let crate::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } = *inner\n            {\n                if let Some(stride) = decor.matrix_stride {\n                    let expected_stride = Alignment::from(rows) * scalar.width as u32;\n                    if stride.get() != expected_stride {\n                        return Err(Error::UnsupportedMatrixStride {\n                            stride: stride.get(),\n                            columns: columns as u8,\n                            rows: rows as u8,\n                            width: scalar.width,\n                        });\n                    }\n                }\n            }\n\n            members.push(crate::StructMember {\n                name: decor.name,\n                ty,\n                binding,\n                offset,\n            });\n        }\n\n        span = alignment.round_up(span);\n\n        let inner = crate::TypeInner::Struct { span, members };\n\n        let ty_handle = module.types.insert(\n            crate::Type {\n                name: parent_decor.and_then(|dec| dec.name),\n                inner,\n            },\n            self.span_from_with_op(start),\n        );\n\n        if is_storage_buffer {\n            self.lookup_storage_buffer_types\n                .insert(ty_handle, storage_access);\n        }\n        for (i, member_lookup) in member_lookups.into_iter().enumerate() {\n            self.lookup_member\n                .insert((ty_handle, i as u32), member_lookup);\n        }\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: ty_handle,\n                base_id: None,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_image(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(9)?;\n\n        let id = self.next()?;\n        let sample_type_id = self.next()?;\n        let dim = self.next()?;\n        let is_depth = self.next()?;\n        let is_array = self.next()? != 0;\n        let is_msaa = self.next()? != 0;\n        let is_sampled = self.next()?;\n        let format = self.next()?;\n\n        let dim = map_image_dim(dim)?;\n        let decor = self.future_decor.remove(&id).unwrap_or_default();\n\n        // ensure there is a type for texture coordinate without extra components\n        module.types.insert(\n            crate::Type {\n                name: None,\n                inner: {\n                    let scalar = crate::Scalar::F32;\n                    match dim.required_coordinate_size() {\n                        None => crate::TypeInner::Scalar(scalar),\n                        Some(size) => crate::TypeInner::Vector { size, scalar },\n                    }\n                },\n            },\n            Default::default(),\n        );\n\n        let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;\n        let kind = module.types[base_handle]\n            .inner\n            .scalar_kind()\n            .ok_or(Error::InvalidImageBaseType(base_handle))?;\n\n        let inner = crate::TypeInner::Image {\n            class: if is_depth == 1 {\n                if is_sampled == 2 {\n                    return Err(Error::InvalidImageDepthStorage);\n                }\n\n                crate::ImageClass::Depth { multi: is_msaa }\n            }\n            // If we have an unknown format and storage texture, this is\n            // StorageRead/WriteWithoutFormat. We don't currently support\n            // this.\n            else if is_sampled == 2 && format == 0 {\n                return Err(Error::InvalidStorageImageWithoutFormat);\n            }\n            // If we have explicit class information (is_sampled = 2 = Storage), use it.\n            //\n            // If we have unknown class information (is_sampled = 0 = Unknown), infer the\n            // class from the presence of an explicit format.\n            else if format != 0 && (is_sampled == 0 || is_sampled == 2) {\n                crate::ImageClass::Storage {\n                    format: map_image_format(format)?,\n                    access: crate::StorageAccess::default(),\n                }\n            }\n            // We will hit this case either when sampled is 1, or if we have unknown\n            // sampling information or when sampled is 0 and we have no explicit format.\n            else {\n                crate::ImageClass::Sampled {\n                    kind,\n                    multi: is_msaa,\n                }\n            },\n            dim,\n            arrayed: is_array,\n        };\n\n        let handle = module.types.insert(\n            crate::Type {\n                name: decor.name,\n                inner,\n            },\n            self.span_from_with_op(start),\n        );\n\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle,\n                base_id: Some(sample_type_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(3)?;\n        let id = self.next()?;\n        let image_id = self.next()?;\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle: self.lookup_type.lookup(image_id)?.handle,\n                base_id: Some(image_id),\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_type_sampler(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(2)?;\n        let id = self.next()?;\n        let decor = self.future_decor.remove(&id).unwrap_or_default();\n        let handle = module.types.insert(\n            crate::Type {\n                name: decor.name,\n                inner: crate::TypeInner::Sampler { comparison: false },\n            },\n            self.span_from_with_op(start),\n        );\n        self.lookup_type.insert(\n            id,\n            LookupType {\n                handle,\n                base_id: None,\n            },\n        );\n        Ok(())\n    }\n\n    fn parse_constant(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect_at_least(4)?;\n        let type_id = self.next()?;\n        let id = self.next()?;\n        let type_lookup = self.lookup_type.lookup(type_id)?;\n        let ty = type_lookup.handle;\n\n        let literal = match module.types[ty].inner {\n            crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Uint,\n                width,\n            }) => {\n                let low = self.next()?;\n                match width {\n                    4 => crate::Literal::U32(low),\n                    8 => {\n                        inst.expect(5)?;\n                        let high = self.next()?;\n                        crate::Literal::U64((u64::from(high) << 32) | u64::from(low))\n                    }\n                    _ => return Err(Error::InvalidTypeWidth(width as u32)),\n                }\n            }\n            crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Sint,\n                width,\n            }) => {\n                let low = self.next()?;\n                match width {\n                    4 => crate::Literal::I32(low as i32),\n                    8 => {\n                        inst.expect(5)?;\n                        let high = self.next()?;\n                        crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)\n                    }\n                    _ => return Err(Error::InvalidTypeWidth(width as u32)),\n                }\n            }\n            crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Float,\n                width,\n            }) => {\n                let low = self.next()?;\n                match width {\n                    // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Literal\n                    // If a numeric type’s bit width is less than 32-bits, the value appears in the low-order bits of the word.\n                    2 => crate::Literal::F16(f16::from_bits(low as u16)),\n                    4 => crate::Literal::F32(f32::from_bits(low)),\n                    8 => {\n                        inst.expect(5)?;\n                        let high = self.next()?;\n                        crate::Literal::F64(f64::from_bits(\n                            (u64::from(high) << 32) | u64::from(low),\n                        ))\n                    }\n                    _ => return Err(Error::InvalidTypeWidth(width as u32)),\n                }\n            }\n            _ => return Err(Error::UnsupportedType(type_lookup.handle)),\n        };\n\n        let span = self.span_from_with_op(start);\n\n        let init = module\n            .global_expressions\n            .append(crate::Expression::Literal(literal), span);\n\n        self.insert_parsed_constant(module, id, type_id, ty, init, span)\n    }\n\n    fn parse_composite_constant(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect_at_least(3)?;\n        let type_id = self.next()?;\n        let id = self.next()?;\n\n        let type_lookup = self.lookup_type.lookup(type_id)?;\n        let ty = type_lookup.handle;\n\n        let mut components = Vec::with_capacity(inst.wc as usize - 3);\n        for _ in 0..components.capacity() {\n            let start = self.data_offset;\n            let component_id = self.next()?;\n            let span = self.span_from_with_op(start);\n            let constant = self.lookup_constant.lookup(component_id)?;\n            let expr = module\n                .global_expressions\n                .append(constant.inner.to_expr(), span);\n            components.push(expr);\n        }\n\n        let span = self.span_from_with_op(start);\n\n        let init = module\n            .global_expressions\n            .append(crate::Expression::Compose { ty, components }, span);\n\n        self.insert_parsed_constant(module, id, type_id, ty, init, span)\n    }\n\n    fn parse_null_constant(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(3)?;\n        let type_id = self.next()?;\n        let id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let type_lookup = self.lookup_type.lookup(type_id)?;\n        let ty = type_lookup.handle;\n\n        let init = module\n            .global_expressions\n            .append(crate::Expression::ZeroValue(ty), span);\n\n        self.insert_parsed_constant(module, id, type_id, ty, init, span)\n    }\n\n    fn parse_bool_constant(\n        &mut self,\n        inst: Instruction,\n        value: bool,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect(3)?;\n        let type_id = self.next()?;\n        let id = self.next()?;\n        let span = self.span_from_with_op(start);\n\n        let type_lookup = self.lookup_type.lookup(type_id)?;\n        let ty = type_lookup.handle;\n\n        let init = module.global_expressions.append(\n            crate::Expression::Literal(crate::Literal::Bool(value)),\n            span,\n        );\n\n        self.insert_parsed_constant(module, id, type_id, ty, init, span)\n    }\n\n    fn insert_parsed_constant(\n        &mut self,\n        module: &mut crate::Module,\n        id: u32,\n        type_id: u32,\n        ty: Handle<crate::Type>,\n        init: Handle<crate::Expression>,\n        span: crate::Span,\n    ) -> Result<(), Error> {\n        let decor = self.future_decor.remove(&id).unwrap_or_default();\n\n        let inner = if let Some(id) = decor.specialization_constant_id {\n            let o = crate::Override {\n                name: decor.name,\n                id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),\n                ty,\n                init: Some(init),\n            };\n            Constant::Override(module.overrides.append(o, span))\n        } else {\n            let c = crate::Constant {\n                name: decor.name,\n                ty,\n                init,\n            };\n            Constant::Constant(module.constants.append(c, span))\n        };\n\n        self.lookup_constant\n            .insert(id, LookupConstant { inner, type_id });\n        Ok(())\n    }\n\n    fn parse_global_variable(\n        &mut self,\n        inst: Instruction,\n        module: &mut crate::Module,\n    ) -> Result<(), Error> {\n        let start = self.data_offset;\n        self.switch(ModuleState::Type, inst.op)?;\n        inst.expect_at_least(4)?;\n        let type_id = self.next()?;\n        let id = self.next()?;\n        let storage_class = self.next()?;\n        let init = if inst.wc > 4 {\n            inst.expect(5)?;\n            let start = self.data_offset;\n            let init_id = self.next()?;\n            let span = self.span_from_with_op(start);\n            let lconst = self.lookup_constant.lookup(init_id)?;\n            let expr = module\n                .global_expressions\n                .append(lconst.inner.to_expr(), span);\n            Some(expr)\n        } else {\n            None\n        };\n        let span = self.span_from_with_op(start);\n        let dec = self.future_decor.remove(&id).unwrap_or_default();\n\n        let original_ty = self.lookup_type.lookup(type_id)?.handle;\n        let mut ty = original_ty;\n\n        if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {\n            ty = base;\n        }\n\n        if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {\n            // Inside `parse_type_array()` we guess that an array of images or\n            // samplers must be a binding array, and here we validate that guess\n            if dec.desc_set.is_none() || dec.desc_index.is_none() {\n                return Err(Error::NonBindingArrayOfImageOrSamplers);\n            }\n        }\n\n        if let crate::TypeInner::Image {\n            dim,\n            arrayed,\n            class: crate::ImageClass::Storage { format, access: _ },\n        } = module.types[ty].inner\n        {\n            // Storage image types in IR have to contain the access, but not in the SPIR-V.\n            // The same image type in SPIR-V can be used (and has to be used) for multiple images.\n            // So we copy the type out and apply the variable access decorations.\n            let access = dec.flags.to_storage_access();\n\n            ty = module.types.insert(\n                crate::Type {\n                    name: None,\n                    inner: crate::TypeInner::Image {\n                        dim,\n                        arrayed,\n                        class: crate::ImageClass::Storage { format, access },\n                    },\n                },\n                Default::default(),\n            );\n        }\n\n        let ext_class = match self.lookup_storage_buffer_types.get(&ty) {\n            Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),\n            None => map_storage_class(storage_class)?,\n        };\n\n        let (inner, var) = match ext_class {\n            ExtendedClass::Global(mut space) => {\n                if let crate::AddressSpace::Storage { ref mut access } = space {\n                    *access &= dec.flags.to_storage_access();\n                }\n                let var = crate::GlobalVariable {\n                    binding: dec.resource_binding(),\n                    name: dec.name,\n                    space,\n                    ty,\n                    init,\n                    memory_decorations: dec.flags.to_memory_decorations(),\n                };\n                (Variable::Global, var)\n            }\n            ExtendedClass::Input => {\n                let binding = dec.io_binding()?;\n                let mut unsigned_ty = ty;\n                if let crate::Binding::BuiltIn(built_in) = binding {\n                    let needs_inner_uint = match built_in {\n                        crate::BuiltIn::BaseInstance\n                        | crate::BuiltIn::BaseVertex\n                        | crate::BuiltIn::InstanceIndex\n                        | crate::BuiltIn::SampleIndex\n                        | crate::BuiltIn::VertexIndex\n                        | crate::BuiltIn::PrimitiveIndex\n                        | crate::BuiltIn::LocalInvocationIndex => {\n                            Some(crate::TypeInner::Scalar(crate::Scalar::U32))\n                        }\n                        crate::BuiltIn::GlobalInvocationId\n                        | crate::BuiltIn::LocalInvocationId\n                        | crate::BuiltIn::WorkGroupId\n                        | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {\n                            size: crate::VectorSize::Tri,\n                            scalar: crate::Scalar::U32,\n                        }),\n                        crate::BuiltIn::Barycentric { perspective: false } => {\n                            Some(crate::TypeInner::Vector {\n                                size: crate::VectorSize::Tri,\n                                scalar: crate::Scalar::F32,\n                            })\n                        }\n                        _ => None,\n                    };\n                    if let (Some(inner), Some(crate::ScalarKind::Sint)) =\n                        (needs_inner_uint, module.types[ty].inner.scalar_kind())\n                    {\n                        unsigned_ty = module\n                            .types\n                            .insert(crate::Type { name: None, inner }, Default::default());\n                    }\n                }\n\n                let var = crate::GlobalVariable {\n                    name: dec.name.clone(),\n                    space: crate::AddressSpace::Private,\n                    binding: None,\n                    ty,\n                    init: None,\n                    memory_decorations: crate::MemoryDecorations::empty(),\n                };\n\n                let inner = Variable::Input(crate::FunctionArgument {\n                    name: dec.name,\n                    ty: unsigned_ty,\n                    binding: Some(binding),\n                });\n                (inner, var)\n            }\n            ExtendedClass::Output => {\n                // For output interface blocks, this would be a structure.\n                let binding = dec.io_binding().ok();\n                let init = match binding {\n                    Some(crate::Binding::BuiltIn(built_in)) => {\n                        match null::generate_default_built_in(\n                            Some(built_in),\n                            ty,\n                            &mut module.global_expressions,\n                            span,\n                        ) {\n                            Ok(handle) => Some(handle),\n                            Err(e) => {\n                                log::warn!(\"Failed to initialize output built-in: {e}\");\n                                None\n                            }\n                        }\n                    }\n                    Some(crate::Binding::Location { .. }) => None,\n                    None => match module.types[ty].inner {\n                        crate::TypeInner::Struct { ref members, .. } => {\n                            let mut components = Vec::with_capacity(members.len());\n                            for member in members.iter() {\n                                let built_in = match member.binding {\n                                    Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),\n                                    _ => None,\n                                };\n                                let handle = null::generate_default_built_in(\n                                    built_in,\n                                    member.ty,\n                                    &mut module.global_expressions,\n                                    span,\n                                )?;\n                                components.push(handle);\n                            }\n                            Some(\n                                module\n                                    .global_expressions\n                                    .append(crate::Expression::Compose { ty, components }, span),\n                            )\n                        }\n                        _ => None,\n                    },\n                };\n\n                let var = crate::GlobalVariable {\n                    name: dec.name,\n                    space: crate::AddressSpace::Private,\n                    binding: None,\n                    ty,\n                    init,\n                    memory_decorations: crate::MemoryDecorations::empty(),\n                };\n                let inner = Variable::Output(crate::FunctionResult { ty, binding });\n                (inner, var)\n            }\n        };\n\n        let handle = module.global_variables.append(var, span);\n\n        if module.types[ty].inner.can_comparison_sample(module) {\n            log::debug!(\"\\t\\ttracking {handle:?} for sampling properties\");\n\n            self.handle_sampling\n                .insert(handle, image::SamplingFlags::empty());\n        }\n\n        self.lookup_variable.insert(\n            id,\n            LookupVariable {\n                inner,\n                handle,\n                type_id,\n            },\n        );\n        Ok(())\n    }\n\n    /// Record an atomic access to some component of a global variable.\n    ///\n    /// Given `handle`, an expression referring to a scalar that has had an\n    /// atomic operation applied to it, descend into the expression, noting\n    /// which global variable it ultimately refers to, and which struct fields\n    /// of that global's value it accesses.\n    ///\n    /// Return the handle of the type of the expression.\n    ///\n    /// If the expression doesn't actually refer to something in a global\n    /// variable, we can't upgrade its type in a way that Naga validation would\n    /// pass, so reject the input instead.\n    fn record_atomic_access(\n        &mut self,\n        ctx: &BlockContext,\n        handle: Handle<crate::Expression>,\n    ) -> Result<Handle<crate::Type>, Error> {\n        log::debug!(\"\\t\\tlocating global variable in {handle:?}\");\n        match ctx.expressions[handle] {\n            crate::Expression::Access { base, index } => {\n                log::debug!(\"\\t\\t  access {handle:?} {index:?}\");\n                let ty = self.record_atomic_access(ctx, base)?;\n                let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {\n                    unreachable!(\"Atomic operations on Access expressions only work for arrays\");\n                };\n                Ok(base)\n            }\n            crate::Expression::AccessIndex { base, index } => {\n                log::debug!(\"\\t\\t  access index {handle:?} {index:?}\");\n                let ty = self.record_atomic_access(ctx, base)?;\n                match ctx.module.types[ty].inner {\n                    crate::TypeInner::Struct { ref members, .. } => {\n                        let index = index as usize;\n                        self.upgrade_atomics.insert_field(ty, index);\n                        Ok(members[index].ty)\n                    }\n                    crate::TypeInner::Array { base, .. } => {\n                        Ok(base)\n                    }\n                    _ => unreachable!(\"Atomic operations on AccessIndex expressions only work for structs and arrays\"),\n                }\n            }\n            crate::Expression::GlobalVariable(h) => {\n                log::debug!(\"\\t\\t  found {h:?}\");\n                self.upgrade_atomics.insert_global(h);\n                Ok(ctx.module.global_variables[h].ty)\n            }\n            _ => Err(Error::AtomicUpgradeError(\n                crate::front::atomic_upgrade::Error::GlobalVariableMissing,\n            )),\n        }\n    }\n}\n\nfn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {\n    let constant = match *constant {\n        Constant::Constant(constant) => constant,\n        Constant::Override(_) => return None,\n    };\n    match gctx.global_expressions[gctx.constants[constant].init] {\n        crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),\n        crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),\n        _ => None,\n    }\n}\n\npub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {\n    if !data.len().is_multiple_of(4) {\n        return Err(Error::IncompleteData);\n    }\n\n    let words = data\n        .chunks(4)\n        .map(|c| u32::from_le_bytes(c.try_into().unwrap()));\n    Frontend::new(words, options).parse()\n}\n\n/// Helper function to check if `child` is in the scope of `parent`\nfn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {\n    loop {\n        if child == parent {\n            // The child is in the scope parent\n            break true;\n        } else if child == 0 {\n            // Searched finished at the root the child isn't in the parent's body\n            break false;\n        }\n\n        child = block_ctx.bodies[child].parent;\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use alloc::vec;\n\n    #[test]\n    fn parse() {\n        let bin = vec![\n            // Magic number.           Version number: 1.0.\n            0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,\n            // Generator number: 0.    Bound: 0.\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0.\n            0x00, 0x00, 0x00, 0x00, // OpMemoryModel.          Logical.\n            0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450.\n            0x01, 0x00, 0x00, 0x00,\n        ];\n        let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();\n    }\n}\n"
  },
  {
    "path": "naga/src/front/spv/next_block.rs",
    "content": "//! Implementation of [`Frontend::next_block()`].\n//!\n//! This method is split out into its own module purely because it is so long.\n\nuse alloc::{format, vec, vec::Vec};\n\nuse crate::front::spv::{\n    convert::{map_binary_operator, map_relational_fun},\n    image, resolve_constant, BlockContext, Body, BodyFragment, Constant, Error, Frontend,\n    LookupExpression, LookupHelper as _, LookupLoadOverride, MergeBlockInformation, PhiExpression,\n    SignAnchor,\n};\nuse crate::Handle;\n\nimpl<I: Iterator<Item = u32>> Frontend<I> {\n    /// Add the next SPIR-V block's contents to `block_ctx`.\n    ///\n    /// Except for the function's entry block, `block_id` should be the label of\n    /// a block we've seen mentioned before, with an entry in\n    /// `block_ctx.body_for_label` to tell us which `Body` it contributes to.\n    pub(in crate::front::spv) fn next_block(\n        &mut self,\n        block_id: spirv::Word,\n        ctx: &mut BlockContext,\n    ) -> Result<(), Error> {\n        // Extend `body` with the correct form for a branch to `target`.\n        fn merger(body: &mut Body, target: &MergeBlockInformation) {\n            body.data.push(match *target {\n                MergeBlockInformation::LoopContinue => BodyFragment::Continue,\n                MergeBlockInformation::LoopMerge | MergeBlockInformation::SwitchMerge => {\n                    BodyFragment::Break\n                }\n\n                // Finishing a selection merge means just falling off the end of\n                // the `accept` or `reject` block of the `If` statement.\n                MergeBlockInformation::SelectionMerge => return,\n            })\n        }\n\n        let mut emitter = crate::proc::Emitter::default();\n        emitter.start(ctx.expressions);\n\n        // Find the `Body` to which this block contributes.\n        //\n        // If this is some SPIR-V structured control flow construct's merge\n        // block, then `body_idx` will refer to the same `Body` as the header,\n        // so that we simply pick up accumulating the `Body` where the header\n        // left off. Each of the statements in a block dominates the next, so\n        // we're sure to encounter their SPIR-V blocks in order, ensuring that\n        // the `Body` will be assembled in the proper order.\n        //\n        // Note that, unlike every other kind of SPIR-V block, we don't know the\n        // function's first block's label in advance. Thus, we assume that if\n        // this block has no entry in `ctx.body_for_label`, it must be the\n        // function's first block. This always has body index zero.\n        let mut body_idx = *ctx.body_for_label.entry(block_id).or_default();\n\n        // The Naga IR block this call builds. This will end up as\n        // `ctx.blocks[&block_id]`, and `ctx.bodies[body_idx]` will refer to it\n        // via a `BodyFragment::BlockId`.\n        let mut block = crate::Block::new();\n\n        // Stores the merge block as defined by a `OpSelectionMerge` otherwise is `None`\n        //\n        // This is used in `OpSwitch` to promote the `MergeBlockInformation` from\n        // `SelectionMerge` to `SwitchMerge` to allow `Break`s this isn't desirable for\n        // `LoopMerge`s because otherwise `Continue`s wouldn't be allowed\n        let mut selection_merge_block = None;\n\n        macro_rules! get_expr_handle {\n            ($id:expr, $lexp:expr) => {\n                self.get_expr_handle($id, $lexp, ctx, &mut emitter, &mut block, body_idx)\n            };\n        }\n        macro_rules! parse_expr_op {\n            ($op:expr, BINARY) => {\n                self.parse_expr_binary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)\n            };\n\n            ($op:expr, SHIFT) => {\n                self.parse_expr_shift_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)\n            };\n            ($op:expr, UNARY) => {\n                self.parse_expr_unary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)\n            };\n            ($axis:expr, $ctrl:expr, DERIVATIVE) => {\n                self.parse_expr_derivative(\n                    ctx,\n                    &mut emitter,\n                    &mut block,\n                    block_id,\n                    body_idx,\n                    ($axis, $ctrl),\n                )\n            };\n        }\n\n        let terminator = loop {\n            use spirv::Op;\n            let start = self.data_offset;\n            let inst = self.next_inst()?;\n            let span = crate::Span::from(start..(start + 4 * (inst.wc as usize)));\n            log::debug!(\"\\t\\t{:?} [{}]\", inst.op, inst.wc);\n\n            match inst.op {\n                Op::Line => {\n                    inst.expect(4)?;\n                    let _file_id = self.next()?;\n                    let _row_id = self.next()?;\n                    let _col_id = self.next()?;\n                }\n                Op::NoLine => inst.expect(1)?,\n                Op::Undef => {\n                    inst.expect(3)?;\n                    let type_id = self.next()?;\n                    let id = self.next()?;\n                    let type_lookup = self.lookup_type.lookup(type_id)?;\n                    let ty = type_lookup.handle;\n\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle: ctx\n                                .expressions\n                                .append(crate::Expression::ZeroValue(ty), span),\n                            type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Variable => {\n                    inst.expect_at_least(4)?;\n                    block.extend(emitter.finish(ctx.expressions));\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let _storage_class = self.next()?;\n                    let init = if inst.wc > 4 {\n                        inst.expect(5)?;\n                        let init_id = self.next()?;\n                        let lconst = self.lookup_constant.lookup(init_id)?;\n                        Some(ctx.expressions.append(lconst.inner.to_expr(), span))\n                    } else {\n                        None\n                    };\n\n                    let name = self\n                        .future_decor\n                        .remove(&result_id)\n                        .and_then(|decor| decor.name);\n                    if let Some(ref name) = name {\n                        log::debug!(\"\\t\\t\\tid={result_id} name={name}\");\n                    }\n                    let lookup_ty = self.lookup_type.lookup(result_type_id)?;\n                    let var_handle = ctx.local_arena.append(\n                        crate::LocalVariable {\n                            name,\n                            ty: match ctx.module.types[lookup_ty.handle].inner {\n                                crate::TypeInner::Pointer { base, .. } => base,\n                                _ => lookup_ty.handle,\n                            },\n                            init,\n                        },\n                        span,\n                    );\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx\n                                .expressions\n                                .append(crate::Expression::LocalVariable(var_handle), span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::Phi => {\n                    inst.expect_at_least(3)?;\n                    block.extend(emitter.finish(ctx.expressions));\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n\n                    let name = format!(\"phi_{result_id}\");\n                    let local = ctx.local_arena.append(\n                        crate::LocalVariable {\n                            name: Some(name),\n                            ty: self.lookup_type.lookup(result_type_id)?.handle,\n                            init: None,\n                        },\n                        self.span_from(start),\n                    );\n                    let pointer = ctx\n                        .expressions\n                        .append(crate::Expression::LocalVariable(local), span);\n\n                    let in_count = (inst.wc - 3) / 2;\n                    let mut phi = PhiExpression {\n                        local,\n                        expressions: Vec::with_capacity(in_count as usize),\n                    };\n                    for _ in 0..in_count {\n                        let expr = self.next()?;\n                        let block = self.next()?;\n                        phi.expressions.push((expr, block));\n                    }\n\n                    ctx.phis.push(phi);\n                    emitter.start(ctx.expressions);\n\n                    // Associate the lookup with an actual value, which is emitted\n                    // into the current block.\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx\n                                .expressions\n                                .append(crate::Expression::Load { pointer }, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::AccessChain | Op::InBoundsAccessChain => {\n                    struct AccessExpression {\n                        base_handle: Handle<crate::Expression>,\n                        type_id: spirv::Word,\n                        load_override: Option<LookupLoadOverride>,\n                    }\n\n                    inst.expect_at_least(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let base_id = self.next()?;\n                    log::trace!(\"\\t\\t\\tlooking up expr {base_id:?}\");\n\n                    let mut acex = {\n                        let lexp = self.lookup_expression.lookup(base_id)?;\n                        let lty = self.lookup_type.lookup(lexp.type_id)?;\n\n                        // HACK `OpAccessChain` and `OpInBoundsAccessChain`\n                        // require for the result type to be a pointer, but if\n                        // we're given a pointer to an image / sampler, it will\n                        // be *already* dereferenced, since we do that early\n                        // during `parse_type_pointer()`.\n                        //\n                        // This can happen only through `BindingArray`, since\n                        // that's the only case where one can obtain a pointer\n                        // to an image / sampler, and so let's match on that:\n                        let dereference = match ctx.module.types[lty.handle].inner {\n                            crate::TypeInner::BindingArray { .. } => false,\n                            _ => true,\n                        };\n\n                        let type_id = if dereference {\n                            lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?\n                        } else {\n                            lexp.type_id\n                        };\n\n                        AccessExpression {\n                            base_handle: get_expr_handle!(base_id, lexp),\n                            type_id,\n                            load_override: self.lookup_load_override.get(&base_id).cloned(),\n                        }\n                    };\n\n                    for _ in 4..inst.wc {\n                        let access_id = self.next()?;\n                        log::trace!(\"\\t\\t\\tlooking up index expr {access_id:?}\");\n                        let index_expr = self.lookup_expression.lookup(access_id)?.clone();\n                        let index_expr_handle = get_expr_handle!(access_id, &index_expr);\n                        let index_expr_data = &ctx.expressions[index_expr.handle];\n                        let index_maybe = match *index_expr_data {\n                            crate::Expression::Constant(const_handle) => Some(\n                                ctx.gctx()\n                                    .get_const_val(ctx.module.constants[const_handle].init)\n                                    .map_err(|_| {\n                                        Error::InvalidAccess(crate::Expression::Constant(\n                                            const_handle,\n                                        ))\n                                    })?,\n                            ),\n                            _ => None,\n                        };\n\n                        log::trace!(\"\\t\\t\\tlooking up type {:?}\", acex.type_id);\n                        let type_lookup = self.lookup_type.lookup(acex.type_id)?;\n                        let ty = &ctx.module.types[type_lookup.handle];\n                        acex = match ty.inner {\n                            // can only index a struct with a constant\n                            crate::TypeInner::Struct { ref members, .. } => {\n                                let index = index_maybe\n                                    .ok_or_else(|| Error::InvalidAccess(index_expr_data.clone()))?;\n\n                                let lookup_member = self\n                                    .lookup_member\n                                    .get(&(type_lookup.handle, index))\n                                    .ok_or(Error::InvalidAccessType(acex.type_id))?;\n                                let base_handle = ctx.expressions.append(\n                                    crate::Expression::AccessIndex {\n                                        base: acex.base_handle,\n                                        index,\n                                    },\n                                    span,\n                                );\n\n                                if let Some(crate::Binding::BuiltIn(built_in)) =\n                                    members[index as usize].binding\n                                {\n                                    self.gl_per_vertex_builtin_access.insert(built_in);\n                                }\n\n                                AccessExpression {\n                                    base_handle,\n                                    type_id: lookup_member.type_id,\n                                    load_override: if lookup_member.row_major {\n                                        debug_assert!(acex.load_override.is_none());\n                                        let sub_type_lookup =\n                                            self.lookup_type.lookup(lookup_member.type_id)?;\n                                        Some(match ctx.module.types[sub_type_lookup.handle].inner {\n                                            // load it transposed, to match column major expectations\n                                            crate::TypeInner::Matrix { .. } => {\n                                                let loaded = ctx.expressions.append(\n                                                    crate::Expression::Load {\n                                                        pointer: base_handle,\n                                                    },\n                                                    span,\n                                                );\n                                                let transposed = ctx.expressions.append(\n                                                    crate::Expression::Math {\n                                                        fun: crate::MathFunction::Transpose,\n                                                        arg: loaded,\n                                                        arg1: None,\n                                                        arg2: None,\n                                                        arg3: None,\n                                                    },\n                                                    span,\n                                                );\n                                                LookupLoadOverride::Loaded(transposed)\n                                            }\n                                            _ => LookupLoadOverride::Pending,\n                                        })\n                                    } else {\n                                        None\n                                    },\n                                }\n                            }\n                            crate::TypeInner::Matrix { .. } => {\n                                let load_override = match acex.load_override {\n                                    // We are indexing inside a row-major matrix\n                                    Some(LookupLoadOverride::Loaded(load_expr)) => {\n                                        let index = index_maybe.ok_or_else(|| {\n                                            Error::InvalidAccess(index_expr_data.clone())\n                                        })?;\n                                        let sub_handle = ctx.expressions.append(\n                                            crate::Expression::AccessIndex {\n                                                base: load_expr,\n                                                index,\n                                            },\n                                            span,\n                                        );\n                                        Some(LookupLoadOverride::Loaded(sub_handle))\n                                    }\n                                    _ => None,\n                                };\n                                let sub_expr = match index_maybe {\n                                    Some(index) => crate::Expression::AccessIndex {\n                                        base: acex.base_handle,\n                                        index,\n                                    },\n                                    None => crate::Expression::Access {\n                                        base: acex.base_handle,\n                                        index: index_expr_handle,\n                                    },\n                                };\n                                AccessExpression {\n                                    base_handle: ctx.expressions.append(sub_expr, span),\n                                    type_id: type_lookup\n                                        .base_id\n                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,\n                                    load_override,\n                                }\n                            }\n                            // This must be a vector or an array.\n                            _ => {\n                                let base_handle = ctx.expressions.append(\n                                    crate::Expression::Access {\n                                        base: acex.base_handle,\n                                        index: index_expr_handle,\n                                    },\n                                    span,\n                                );\n                                let load_override = match acex.load_override {\n                                    // If there is a load override in place, then we always end up\n                                    // with a side-loaded value here.\n                                    Some(lookup_load_override) => {\n                                        let sub_expr = match lookup_load_override {\n                                            // We must be indexing into the array of row-major matrices.\n                                            // Let's load the result of indexing and transpose it.\n                                            LookupLoadOverride::Pending => {\n                                                let loaded = ctx.expressions.append(\n                                                    crate::Expression::Load {\n                                                        pointer: base_handle,\n                                                    },\n                                                    span,\n                                                );\n                                                ctx.expressions.append(\n                                                    crate::Expression::Math {\n                                                        fun: crate::MathFunction::Transpose,\n                                                        arg: loaded,\n                                                        arg1: None,\n                                                        arg2: None,\n                                                        arg3: None,\n                                                    },\n                                                    span,\n                                                )\n                                            }\n                                            // We are indexing inside a row-major matrix.\n                                            LookupLoadOverride::Loaded(load_expr) => {\n                                                ctx.expressions.append(\n                                                    crate::Expression::Access {\n                                                        base: load_expr,\n                                                        index: index_expr_handle,\n                                                    },\n                                                    span,\n                                                )\n                                            }\n                                        };\n                                        Some(LookupLoadOverride::Loaded(sub_expr))\n                                    }\n                                    None => None,\n                                };\n                                AccessExpression {\n                                    base_handle,\n                                    type_id: type_lookup\n                                        .base_id\n                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,\n                                    load_override,\n                                }\n                            }\n                        };\n                    }\n\n                    if let Some(load_expr) = acex.load_override {\n                        self.lookup_load_override.insert(result_id, load_expr);\n                    }\n                    let lookup_expression = LookupExpression {\n                        handle: acex.base_handle,\n                        type_id: result_type_id,\n                        block_id,\n                    };\n                    self.lookup_expression.insert(result_id, lookup_expression);\n                }\n                Op::VectorExtractDynamic => {\n                    inst.expect(5)?;\n\n                    let result_type_id = self.next()?;\n                    let id = self.next()?;\n                    let composite_id = self.next()?;\n                    let index_id = self.next()?;\n\n                    let root_lexp = self.lookup_expression.lookup(composite_id)?;\n                    let root_handle = get_expr_handle!(composite_id, root_lexp);\n                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;\n                    let index_lexp = self.lookup_expression.lookup(index_id)?;\n                    let index_handle = get_expr_handle!(index_id, index_lexp);\n                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;\n\n                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {\n                        crate::TypeInner::Vector { size, .. } => size as u32,\n                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),\n                    };\n\n                    let mut make_index = |ctx: &mut BlockContext, index: u32| {\n                        make_index_literal(\n                            ctx,\n                            index,\n                            &mut block,\n                            &mut emitter,\n                            index_type,\n                            index_lexp.type_id,\n                            span,\n                        )\n                    };\n\n                    let index_expr = make_index(ctx, 0)?;\n                    let mut handle = ctx.expressions.append(\n                        crate::Expression::Access {\n                            base: root_handle,\n                            index: index_expr,\n                        },\n                        span,\n                    );\n                    for index in 1..num_components {\n                        let index_expr = make_index(ctx, index)?;\n                        let access_expr = ctx.expressions.append(\n                            crate::Expression::Access {\n                                base: root_handle,\n                                index: index_expr,\n                            },\n                            span,\n                        );\n                        let cond = ctx.expressions.append(\n                            crate::Expression::Binary {\n                                op: crate::BinaryOperator::Equal,\n                                left: index_expr,\n                                right: index_handle,\n                            },\n                            span,\n                        );\n                        handle = ctx.expressions.append(\n                            crate::Expression::Select {\n                                condition: cond,\n                                accept: access_expr,\n                                reject: handle,\n                            },\n                            span,\n                        );\n                    }\n\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::VectorInsertDynamic => {\n                    inst.expect(6)?;\n\n                    let result_type_id = self.next()?;\n                    let id = self.next()?;\n                    let composite_id = self.next()?;\n                    let object_id = self.next()?;\n                    let index_id = self.next()?;\n\n                    let object_lexp = self.lookup_expression.lookup(object_id)?;\n                    let object_handle = get_expr_handle!(object_id, object_lexp);\n                    let root_lexp = self.lookup_expression.lookup(composite_id)?;\n                    let root_handle = get_expr_handle!(composite_id, root_lexp);\n                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;\n                    let index_lexp = self.lookup_expression.lookup(index_id)?;\n                    let index_handle = get_expr_handle!(index_id, index_lexp);\n                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;\n\n                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {\n                        crate::TypeInner::Vector { size, .. } => size as u32,\n                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),\n                    };\n\n                    let mut components = Vec::with_capacity(num_components as usize);\n                    for index in 0..num_components {\n                        let index_expr = make_index_literal(\n                            ctx,\n                            index,\n                            &mut block,\n                            &mut emitter,\n                            index_type,\n                            index_lexp.type_id,\n                            span,\n                        )?;\n                        let access_expr = ctx.expressions.append(\n                            crate::Expression::Access {\n                                base: root_handle,\n                                index: index_expr,\n                            },\n                            span,\n                        );\n                        let cond = ctx.expressions.append(\n                            crate::Expression::Binary {\n                                op: crate::BinaryOperator::Equal,\n                                left: index_expr,\n                                right: index_handle,\n                            },\n                            span,\n                        );\n                        let handle = ctx.expressions.append(\n                            crate::Expression::Select {\n                                condition: cond,\n                                accept: object_handle,\n                                reject: access_expr,\n                            },\n                            span,\n                        );\n                        components.push(handle);\n                    }\n                    let handle = ctx.expressions.append(\n                        crate::Expression::Compose {\n                            ty: root_type_lookup.handle,\n                            components,\n                        },\n                        span,\n                    );\n\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::CompositeExtract => {\n                    inst.expect_at_least(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let base_id = self.next()?;\n                    log::trace!(\"\\t\\t\\tlooking up expr {base_id:?}\");\n                    let mut lexp = self.lookup_expression.lookup(base_id)?.clone();\n                    lexp.handle = get_expr_handle!(base_id, &lexp);\n                    for _ in 4..inst.wc {\n                        let index = self.next()?;\n                        log::trace!(\"\\t\\t\\tlooking up type {:?}\", lexp.type_id);\n                        let type_lookup = self.lookup_type.lookup(lexp.type_id)?;\n                        let type_id = match ctx.module.types[type_lookup.handle].inner {\n                            crate::TypeInner::Struct { .. } => {\n                                self.lookup_member\n                                    .get(&(type_lookup.handle, index))\n                                    .ok_or(Error::InvalidAccessType(lexp.type_id))?\n                                    .type_id\n                            }\n                            crate::TypeInner::Array { .. }\n                            | crate::TypeInner::Vector { .. }\n                            | crate::TypeInner::Matrix { .. } => type_lookup\n                                .base_id\n                                .ok_or(Error::InvalidAccessType(lexp.type_id))?,\n                            ref other => {\n                                log::warn!(\"composite type {other:?}\");\n                                return Err(Error::UnsupportedType(type_lookup.handle));\n                            }\n                        };\n                        lexp = LookupExpression {\n                            handle: ctx.expressions.append(\n                                crate::Expression::AccessIndex {\n                                    base: lexp.handle,\n                                    index,\n                                },\n                                span,\n                            ),\n                            type_id,\n                            block_id,\n                        };\n                    }\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: lexp.handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::CompositeInsert => {\n                    inst.expect_at_least(5)?;\n\n                    let result_type_id = self.next()?;\n                    let id = self.next()?;\n                    let object_id = self.next()?;\n                    let composite_id = self.next()?;\n                    let mut selections = Vec::with_capacity(inst.wc as usize - 5);\n                    for _ in 5..inst.wc {\n                        selections.push(self.next()?);\n                    }\n\n                    let object_lexp = self.lookup_expression.lookup(object_id)?.clone();\n                    let object_handle = get_expr_handle!(object_id, &object_lexp);\n                    let root_lexp = self.lookup_expression.lookup(composite_id)?.clone();\n                    let root_handle = get_expr_handle!(composite_id, &root_lexp);\n                    let handle = self.insert_composite(\n                        root_handle,\n                        result_type_id,\n                        object_handle,\n                        &selections,\n                        &ctx.module.types,\n                        ctx.expressions,\n                        span,\n                    )?;\n\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::CompositeConstruct => {\n                    inst.expect_at_least(3)?;\n\n                    let result_type_id = self.next()?;\n                    let id = self.next()?;\n                    let mut components = Vec::with_capacity(inst.wc as usize - 2);\n                    for _ in 3..inst.wc {\n                        let comp_id = self.next()?;\n                        log::trace!(\"\\t\\t\\tlooking up expr {comp_id:?}\");\n                        let lexp = self.lookup_expression.lookup(comp_id)?;\n                        let handle = get_expr_handle!(comp_id, lexp);\n                        components.push(handle);\n                    }\n                    let ty = self.lookup_type.lookup(result_type_id)?.handle;\n                    let first = components[0];\n                    let expr = match ctx.module.types[ty].inner {\n                        // this is an optimization to detect the splat\n                        crate::TypeInner::Vector { size, .. }\n                            if components.len() == size as usize\n                                && components[1..].iter().all(|&c| c == first) =>\n                        {\n                            crate::Expression::Splat { size, value: first }\n                        }\n                        _ => crate::Expression::Compose { ty, components },\n                    };\n                    self.lookup_expression.insert(\n                        id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Load => {\n                    inst.expect_at_least(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let pointer_id = self.next()?;\n                    if inst.wc != 4 {\n                        inst.expect(5)?;\n                        let _memory_access = self.next()?;\n                    }\n\n                    let base_lexp = self.lookup_expression.lookup(pointer_id)?;\n                    let base_handle = get_expr_handle!(pointer_id, base_lexp);\n                    let type_lookup = self.lookup_type.lookup(base_lexp.type_id)?;\n                    let handle = match ctx.module.types[type_lookup.handle].inner {\n                        crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => {\n                            base_handle\n                        }\n                        _ => match self.lookup_load_override.get(&pointer_id) {\n                            Some(&LookupLoadOverride::Loaded(handle)) => handle,\n                            //Note: we aren't handling `LookupLoadOverride::Pending` properly here\n                            _ => ctx.expressions.append(\n                                crate::Expression::Load {\n                                    pointer: base_handle,\n                                },\n                                span,\n                            ),\n                        },\n                    };\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Store => {\n                    inst.expect_at_least(3)?;\n\n                    let pointer_id = self.next()?;\n                    let value_id = self.next()?;\n                    if inst.wc != 3 {\n                        inst.expect(4)?;\n                        let _memory_access = self.next()?;\n                    }\n                    let base_expr = self.lookup_expression.lookup(pointer_id)?;\n                    let base_handle = get_expr_handle!(pointer_id, base_expr);\n                    let value_expr = self.lookup_expression.lookup(value_id)?;\n                    let value_handle = get_expr_handle!(value_id, value_expr);\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    block.push(\n                        crate::Statement::Store {\n                            pointer: base_handle,\n                            value: value_handle,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                // Arithmetic Instructions +, -, *, /, %\n                Op::SNegate | Op::FNegate => {\n                    inst.expect(4)?;\n                    self.parse_expr_unary_op_sign_adjusted(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        crate::UnaryOperator::Negate,\n                    )?;\n                }\n                Op::IAdd\n                | Op::ISub\n                | Op::IMul\n                | Op::BitwiseOr\n                | Op::BitwiseXor\n                | Op::BitwiseAnd\n                | Op::SDiv\n                | Op::SRem => {\n                    inst.expect(5)?;\n                    let operator = map_binary_operator(inst.op)?;\n                    self.parse_expr_binary_op_sign_adjusted(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        operator,\n                        SignAnchor::Result,\n                    )?;\n                }\n                Op::IEqual | Op::INotEqual => {\n                    inst.expect(5)?;\n                    let operator = map_binary_operator(inst.op)?;\n                    self.parse_expr_binary_op_sign_adjusted(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        operator,\n                        SignAnchor::Operand,\n                    )?;\n                }\n                Op::FAdd => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Add, BINARY)?;\n                }\n                Op::FSub => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Subtract, BINARY)?;\n                }\n                Op::FMul => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;\n                }\n                Op::UDiv | Op::FDiv => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Divide, BINARY)?;\n                }\n                Op::UMod | Op::FRem => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Modulo, BINARY)?;\n                }\n                Op::SMod => {\n                    inst.expect(5)?;\n\n                    // x - y * int(floor(float(x) / float(y)))\n\n                    let start = self.data_offset;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let p1_id = self.next()?;\n                    let p2_id = self.next()?;\n                    let span = self.span_from_with_op(start);\n\n                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n                    let left = self.get_expr_handle(\n                        p1_id,\n                        p1_lexp,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    );\n                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n                    let right = self.get_expr_handle(\n                        p2_id,\n                        p2_lexp,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    );\n\n                    let result_ty = self.lookup_type.lookup(result_type_id)?;\n                    let inner = &ctx.module.types[result_ty.handle].inner;\n                    let kind = inner.scalar_kind().unwrap();\n                    let size = inner.size(ctx.gctx()) as u8;\n\n                    let left_cast = ctx.expressions.append(\n                        crate::Expression::As {\n                            expr: left,\n                            kind: crate::ScalarKind::Float,\n                            convert: Some(size),\n                        },\n                        span,\n                    );\n                    let right_cast = ctx.expressions.append(\n                        crate::Expression::As {\n                            expr: right,\n                            kind: crate::ScalarKind::Float,\n                            convert: Some(size),\n                        },\n                        span,\n                    );\n                    let div = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Divide,\n                            left: left_cast,\n                            right: right_cast,\n                        },\n                        span,\n                    );\n                    let floor = ctx.expressions.append(\n                        crate::Expression::Math {\n                            fun: crate::MathFunction::Floor,\n                            arg: div,\n                            arg1: None,\n                            arg2: None,\n                            arg3: None,\n                        },\n                        span,\n                    );\n                    let cast = ctx.expressions.append(\n                        crate::Expression::As {\n                            expr: floor,\n                            kind,\n                            convert: Some(size),\n                        },\n                        span,\n                    );\n                    let mult = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Multiply,\n                            left: cast,\n                            right,\n                        },\n                        span,\n                    );\n                    let sub = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Subtract,\n                            left,\n                            right: mult,\n                        },\n                        span,\n                    );\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: sub,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::FMod => {\n                    inst.expect(5)?;\n\n                    // x - y * floor(x / y)\n\n                    let start = self.data_offset;\n                    let span = self.span_from_with_op(start);\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let p1_id = self.next()?;\n                    let p2_id = self.next()?;\n\n                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;\n                    let left = self.get_expr_handle(\n                        p1_id,\n                        p1_lexp,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    );\n                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;\n                    let right = self.get_expr_handle(\n                        p2_id,\n                        p2_lexp,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    );\n\n                    let div = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Divide,\n                            left,\n                            right,\n                        },\n                        span,\n                    );\n                    let floor = ctx.expressions.append(\n                        crate::Expression::Math {\n                            fun: crate::MathFunction::Floor,\n                            arg: div,\n                            arg1: None,\n                            arg2: None,\n                            arg3: None,\n                        },\n                        span,\n                    );\n                    let mult = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Multiply,\n                            left: floor,\n                            right,\n                        },\n                        span,\n                    );\n                    let sub = ctx.expressions.append(\n                        crate::Expression::Binary {\n                            op: crate::BinaryOperator::Subtract,\n                            left,\n                            right: mult,\n                        },\n                        span,\n                    );\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: sub,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::VectorTimesScalar\n                | Op::VectorTimesMatrix\n                | Op::MatrixTimesScalar\n                | Op::MatrixTimesVector\n                | Op::MatrixTimesMatrix => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;\n                }\n                Op::Transpose => {\n                    inst.expect(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let matrix_id = self.next()?;\n                    let matrix_lexp = self.lookup_expression.lookup(matrix_id)?;\n                    let matrix_handle = get_expr_handle!(matrix_id, matrix_lexp);\n                    let expr = crate::Expression::Math {\n                        fun: crate::MathFunction::Transpose,\n                        arg: matrix_handle,\n                        arg1: None,\n                        arg2: None,\n                        arg3: None,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Dot => {\n                    inst.expect(5)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let left_id = self.next()?;\n                    let right_id = self.next()?;\n                    let left_lexp = self.lookup_expression.lookup(left_id)?;\n                    let left_handle = get_expr_handle!(left_id, left_lexp);\n                    let right_lexp = self.lookup_expression.lookup(right_id)?;\n                    let right_handle = get_expr_handle!(right_id, right_lexp);\n                    let expr = crate::Expression::Math {\n                        fun: crate::MathFunction::Dot,\n                        arg: left_handle,\n                        arg1: Some(right_handle),\n                        arg2: None,\n                        arg3: None,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::BitFieldInsert => {\n                    inst.expect(7)?;\n\n                    let start = self.data_offset;\n                    let span = self.span_from_with_op(start);\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let base_id = self.next()?;\n                    let insert_id = self.next()?;\n                    let offset_id = self.next()?;\n                    let count_id = self.next()?;\n                    let base_lexp = self.lookup_expression.lookup(base_id)?;\n                    let base_handle = get_expr_handle!(base_id, base_lexp);\n                    let insert_lexp = self.lookup_expression.lookup(insert_id)?;\n                    let insert_handle = get_expr_handle!(insert_id, insert_lexp);\n                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;\n                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);\n                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;\n                    let count_lexp = self.lookup_expression.lookup(count_id)?;\n                    let count_handle = get_expr_handle!(count_id, count_lexp);\n                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;\n\n                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]\n                        .inner\n                        .scalar_kind()\n                        .unwrap();\n                    let count_kind = ctx.module.types[count_lookup_ty.handle]\n                        .inner\n                        .scalar_kind()\n                        .unwrap();\n\n                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {\n                        ctx.expressions.append(\n                            crate::Expression::As {\n                                expr: offset_handle,\n                                kind: crate::ScalarKind::Uint,\n                                convert: None,\n                            },\n                            span,\n                        )\n                    } else {\n                        offset_handle\n                    };\n\n                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {\n                        ctx.expressions.append(\n                            crate::Expression::As {\n                                expr: count_handle,\n                                kind: crate::ScalarKind::Uint,\n                                convert: None,\n                            },\n                            span,\n                        )\n                    } else {\n                        count_handle\n                    };\n\n                    let expr = crate::Expression::Math {\n                        fun: crate::MathFunction::InsertBits,\n                        arg: base_handle,\n                        arg1: Some(insert_handle),\n                        arg2: Some(offset_cast_handle),\n                        arg3: Some(count_cast_handle),\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::BitFieldSExtract | Op::BitFieldUExtract => {\n                    inst.expect(6)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let base_id = self.next()?;\n                    let offset_id = self.next()?;\n                    let count_id = self.next()?;\n                    let base_lexp = self.lookup_expression.lookup(base_id)?;\n                    let base_handle = get_expr_handle!(base_id, base_lexp);\n                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;\n                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);\n                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;\n                    let count_lexp = self.lookup_expression.lookup(count_id)?;\n                    let count_handle = get_expr_handle!(count_id, count_lexp);\n                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;\n\n                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]\n                        .inner\n                        .scalar_kind()\n                        .unwrap();\n                    let count_kind = ctx.module.types[count_lookup_ty.handle]\n                        .inner\n                        .scalar_kind()\n                        .unwrap();\n\n                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {\n                        ctx.expressions.append(\n                            crate::Expression::As {\n                                expr: offset_handle,\n                                kind: crate::ScalarKind::Uint,\n                                convert: None,\n                            },\n                            span,\n                        )\n                    } else {\n                        offset_handle\n                    };\n\n                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {\n                        ctx.expressions.append(\n                            crate::Expression::As {\n                                expr: count_handle,\n                                kind: crate::ScalarKind::Uint,\n                                convert: None,\n                            },\n                            span,\n                        )\n                    } else {\n                        count_handle\n                    };\n\n                    let expr = crate::Expression::Math {\n                        fun: crate::MathFunction::ExtractBits,\n                        arg: base_handle,\n                        arg1: Some(offset_cast_handle),\n                        arg2: Some(count_cast_handle),\n                        arg3: None,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::BitReverse | Op::BitCount => {\n                    inst.expect(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let base_id = self.next()?;\n                    let base_lexp = self.lookup_expression.lookup(base_id)?;\n                    let base_handle = get_expr_handle!(base_id, base_lexp);\n                    let expr = crate::Expression::Math {\n                        fun: match inst.op {\n                            Op::BitReverse => crate::MathFunction::ReverseBits,\n                            Op::BitCount => crate::MathFunction::CountOneBits,\n                            _ => unreachable!(),\n                        },\n                        arg: base_handle,\n                        arg1: None,\n                        arg2: None,\n                        arg3: None,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::OuterProduct => {\n                    inst.expect(5)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let left_id = self.next()?;\n                    let right_id = self.next()?;\n                    let left_lexp = self.lookup_expression.lookup(left_id)?;\n                    let left_handle = get_expr_handle!(left_id, left_lexp);\n                    let right_lexp = self.lookup_expression.lookup(right_id)?;\n                    let right_handle = get_expr_handle!(right_id, right_lexp);\n                    let expr = crate::Expression::Math {\n                        fun: crate::MathFunction::Outer,\n                        arg: left_handle,\n                        arg1: Some(right_handle),\n                        arg2: None,\n                        arg3: None,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                // Bitwise instructions\n                Op::Not => {\n                    inst.expect(4)?;\n                    self.parse_expr_unary_op_sign_adjusted(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        crate::UnaryOperator::BitwiseNot,\n                    )?;\n                }\n                Op::ShiftRightLogical => {\n                    inst.expect(5)?;\n                    //TODO: convert input and result to unsigned\n                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;\n                }\n                Op::ShiftRightArithmetic => {\n                    inst.expect(5)?;\n                    //TODO: convert input and result to signed\n                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;\n                }\n                Op::ShiftLeftLogical => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::ShiftLeft, SHIFT)?;\n                }\n                // Sampling\n                Op::Image => {\n                    inst.expect(4)?;\n                    self.parse_image_uncouple(block_id)?;\n                }\n                Op::SampledImage => {\n                    inst.expect(5)?;\n                    self.parse_image_couple()?;\n                }\n                Op::ImageWrite => {\n                    let extra = inst.expect_at_least(4)?;\n                    let stmt =\n                        self.parse_image_write(extra, ctx, &mut emitter, &mut block, body_idx)?;\n                    block.extend(emitter.finish(ctx.expressions));\n                    block.push(stmt, span);\n                    emitter.start(ctx.expressions);\n                }\n                Op::ImageFetch | Op::ImageRead => {\n                    let extra = inst.expect_at_least(5)?;\n                    self.parse_image_load(\n                        extra,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageSampleImplicitLod | Op::ImageSampleExplicitLod => {\n                    let extra = inst.expect_at_least(5)?;\n                    let options = image::SamplingOptions {\n                        compare: false,\n                        project: false,\n                        gather: false,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageSampleProjImplicitLod | Op::ImageSampleProjExplicitLod => {\n                    let extra = inst.expect_at_least(5)?;\n                    let options = image::SamplingOptions {\n                        compare: false,\n                        project: true,\n                        gather: false,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageSampleDrefImplicitLod | Op::ImageSampleDrefExplicitLod => {\n                    let extra = inst.expect_at_least(6)?;\n                    let options = image::SamplingOptions {\n                        compare: true,\n                        project: false,\n                        gather: false,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageSampleProjDrefImplicitLod | Op::ImageSampleProjDrefExplicitLod => {\n                    let extra = inst.expect_at_least(6)?;\n                    let options = image::SamplingOptions {\n                        compare: true,\n                        project: true,\n                        gather: false,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageGather => {\n                    let extra = inst.expect_at_least(6)?;\n                    let options = image::SamplingOptions {\n                        compare: false,\n                        project: false,\n                        gather: true,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageDrefGather => {\n                    let extra = inst.expect_at_least(6)?;\n                    let options = image::SamplingOptions {\n                        compare: true,\n                        project: false,\n                        gather: true,\n                    };\n                    self.parse_image_sample(\n                        extra,\n                        options,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageQuerySize => {\n                    inst.expect(4)?;\n                    self.parse_image_query_size(\n                        false,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageQuerySizeLod => {\n                    inst.expect(5)?;\n                    self.parse_image_query_size(\n                        true,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                    )?;\n                }\n                Op::ImageQueryLevels => {\n                    inst.expect(4)?;\n                    self.parse_image_query_other(crate::ImageQuery::NumLevels, ctx, block_id)?;\n                }\n                Op::ImageQuerySamples => {\n                    inst.expect(4)?;\n                    self.parse_image_query_other(crate::ImageQuery::NumSamples, ctx, block_id)?;\n                }\n                // other ops\n                Op::Select => {\n                    inst.expect(6)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let condition = self.next()?;\n                    let o1_id = self.next()?;\n                    let o2_id = self.next()?;\n\n                    let cond_lexp = self.lookup_expression.lookup(condition)?;\n                    let cond_handle = get_expr_handle!(condition, cond_lexp);\n                    let o1_lexp = self.lookup_expression.lookup(o1_id)?;\n                    let o1_handle = get_expr_handle!(o1_id, o1_lexp);\n                    let o2_lexp = self.lookup_expression.lookup(o2_id)?;\n                    let o2_handle = get_expr_handle!(o2_id, o2_lexp);\n\n                    let expr = crate::Expression::Select {\n                        condition: cond_handle,\n                        accept: o1_handle,\n                        reject: o2_handle,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::VectorShuffle => {\n                    inst.expect_at_least(5)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let v1_id = self.next()?;\n                    let v2_id = self.next()?;\n\n                    let v1_lexp = self.lookup_expression.lookup(v1_id)?;\n                    let v1_lty = self.lookup_type.lookup(v1_lexp.type_id)?;\n                    let v1_handle = get_expr_handle!(v1_id, v1_lexp);\n                    let n1 = match ctx.module.types[v1_lty.handle].inner {\n                        crate::TypeInner::Vector { size, .. } => size as u32,\n                        _ => return Err(Error::InvalidInnerType(v1_lexp.type_id)),\n                    };\n                    let v2_lexp = self.lookup_expression.lookup(v2_id)?;\n                    let v2_lty = self.lookup_type.lookup(v2_lexp.type_id)?;\n                    let v2_handle = get_expr_handle!(v2_id, v2_lexp);\n                    let n2 = match ctx.module.types[v2_lty.handle].inner {\n                        crate::TypeInner::Vector { size, .. } => size as u32,\n                        _ => return Err(Error::InvalidInnerType(v2_lexp.type_id)),\n                    };\n\n                    self.temp_bytes.clear();\n                    let mut max_component = 0;\n                    for _ in 5..inst.wc as usize {\n                        let mut index = self.next()?;\n                        if index == u32::MAX {\n                            // treat Undefined as X\n                            index = 0;\n                        }\n                        max_component = max_component.max(index);\n                        self.temp_bytes.push(index as u8);\n                    }\n\n                    // Check for swizzle first.\n                    let expr = if max_component < n1 {\n                        use crate::SwizzleComponent as Sc;\n                        let size = match self.temp_bytes.len() {\n                            2 => crate::VectorSize::Bi,\n                            3 => crate::VectorSize::Tri,\n                            _ => crate::VectorSize::Quad,\n                        };\n                        let mut pattern = [Sc::X; 4];\n                        for (pat, index) in pattern.iter_mut().zip(self.temp_bytes.drain(..)) {\n                            *pat = match index {\n                                0 => Sc::X,\n                                1 => Sc::Y,\n                                2 => Sc::Z,\n                                _ => Sc::W,\n                            };\n                        }\n                        crate::Expression::Swizzle {\n                            size,\n                            vector: v1_handle,\n                            pattern,\n                        }\n                    } else {\n                        // Fall back to access + compose\n                        let mut components = Vec::with_capacity(self.temp_bytes.len());\n                        for index in self.temp_bytes.drain(..).map(|i| i as u32) {\n                            let expr = if index < n1 {\n                                crate::Expression::AccessIndex {\n                                    base: v1_handle,\n                                    index,\n                                }\n                            } else if index < n1 + n2 {\n                                crate::Expression::AccessIndex {\n                                    base: v2_handle,\n                                    index: index - n1,\n                                }\n                            } else {\n                                return Err(Error::InvalidAccessIndex(index));\n                            };\n                            components.push(ctx.expressions.append(expr, span));\n                        }\n                        crate::Expression::Compose {\n                            ty: self.lookup_type.lookup(result_type_id)?.handle,\n                            components,\n                        }\n                    };\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Bitcast\n                | Op::ConvertSToF\n                | Op::ConvertUToF\n                | Op::ConvertFToU\n                | Op::ConvertFToS\n                | Op::FConvert\n                | Op::UConvert\n                | Op::SConvert => {\n                    inst.expect(4)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let value_id = self.next()?;\n\n                    let value_lexp = self.lookup_expression.lookup(value_id)?;\n                    let ty_lookup = self.lookup_type.lookup(result_type_id)?;\n                    let scalar = match ctx.module.types[ty_lookup.handle].inner {\n                        crate::TypeInner::Scalar(scalar)\n                        | crate::TypeInner::Vector { scalar, .. }\n                        | crate::TypeInner::Matrix { scalar, .. } => scalar,\n                        _ => return Err(Error::InvalidAsType(ty_lookup.handle)),\n                    };\n\n                    let expr = crate::Expression::As {\n                        expr: get_expr_handle!(value_id, value_lexp),\n                        kind: scalar.kind,\n                        convert: if scalar.kind == crate::ScalarKind::Bool {\n                            Some(crate::BOOL_WIDTH)\n                        } else if inst.op == Op::Bitcast {\n                            None\n                        } else {\n                            Some(scalar.width)\n                        },\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::FunctionCall => {\n                    inst.expect_at_least(4)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let func_id = self.next()?;\n\n                    let mut arguments = Vec::with_capacity(inst.wc as usize - 4);\n                    for _ in 0..arguments.capacity() {\n                        let arg_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(arg_id)?;\n                        arguments.push(get_expr_handle!(arg_id, lexp));\n                    }\n\n                    block.extend(emitter.finish(ctx.expressions));\n\n                    // We just need an unique handle here, nothing more.\n                    let function = self.add_call(ctx.function_id, func_id);\n\n                    let result = if self.lookup_void_type == Some(result_type_id) {\n                        None\n                    } else {\n                        let expr_handle = ctx\n                            .expressions\n                            .append(crate::Expression::CallResult(function), span);\n                        self.lookup_expression.insert(\n                            result_id,\n                            LookupExpression {\n                                handle: expr_handle,\n                                type_id: result_type_id,\n                                block_id,\n                            },\n                        );\n                        Some(expr_handle)\n                    };\n                    block.push(\n                        crate::Statement::Call {\n                            function,\n                            arguments,\n                            result,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::ExtInst => {\n                    use crate::MathFunction as Mf;\n                    use spirv::GlslStd450Op as Glo;\n\n                    let base_wc = 5;\n                    inst.expect_at_least(base_wc)?;\n\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let set_id = self.next()?;\n                    if Some(set_id) == self.ext_non_semantic_id {\n                        for _ in 0..inst.wc - 4 {\n                            self.next()?;\n                        }\n                        continue;\n                    } else if Some(set_id) != self.ext_glsl_id {\n                        return Err(Error::UnsupportedExtInstSet(set_id));\n                    }\n                    let inst_id = self.next()?;\n                    let gl_op = Glo::from_u32(inst_id).ok_or(Error::UnsupportedExtInst(inst_id))?;\n\n                    let fun = match gl_op {\n                        Glo::Round => Mf::Round,\n                        Glo::RoundEven => Mf::Round,\n                        Glo::Trunc => Mf::Trunc,\n                        Glo::FAbs | Glo::SAbs => Mf::Abs,\n                        Glo::FSign | Glo::SSign => Mf::Sign,\n                        Glo::Floor => Mf::Floor,\n                        Glo::Ceil => Mf::Ceil,\n                        Glo::Fract => Mf::Fract,\n                        Glo::Sin => Mf::Sin,\n                        Glo::Cos => Mf::Cos,\n                        Glo::Tan => Mf::Tan,\n                        Glo::Asin => Mf::Asin,\n                        Glo::Acos => Mf::Acos,\n                        Glo::Atan => Mf::Atan,\n                        Glo::Sinh => Mf::Sinh,\n                        Glo::Cosh => Mf::Cosh,\n                        Glo::Tanh => Mf::Tanh,\n                        Glo::Atan2 => Mf::Atan2,\n                        Glo::Asinh => Mf::Asinh,\n                        Glo::Acosh => Mf::Acosh,\n                        Glo::Atanh => Mf::Atanh,\n                        Glo::Radians => Mf::Radians,\n                        Glo::Degrees => Mf::Degrees,\n                        Glo::Pow => Mf::Pow,\n                        Glo::Exp => Mf::Exp,\n                        Glo::Log => Mf::Log,\n                        Glo::Exp2 => Mf::Exp2,\n                        Glo::Log2 => Mf::Log2,\n                        Glo::Sqrt => Mf::Sqrt,\n                        Glo::InverseSqrt => Mf::InverseSqrt,\n                        Glo::MatrixInverse => Mf::Inverse,\n                        Glo::Determinant => Mf::Determinant,\n                        Glo::ModfStruct => Mf::Modf,\n                        Glo::FMin | Glo::UMin | Glo::SMin | Glo::NMin => Mf::Min,\n                        Glo::FMax | Glo::UMax | Glo::SMax | Glo::NMax => Mf::Max,\n                        Glo::FClamp | Glo::UClamp | Glo::SClamp | Glo::NClamp => Mf::Clamp,\n                        Glo::FMix => Mf::Mix,\n                        Glo::Step => Mf::Step,\n                        Glo::SmoothStep => Mf::SmoothStep,\n                        Glo::Fma => Mf::Fma,\n                        Glo::FrexpStruct => Mf::Frexp,\n                        Glo::Ldexp => Mf::Ldexp,\n                        Glo::Length => Mf::Length,\n                        Glo::Distance => Mf::Distance,\n                        Glo::Cross => Mf::Cross,\n                        Glo::Normalize => Mf::Normalize,\n                        Glo::FaceForward => Mf::FaceForward,\n                        Glo::Reflect => Mf::Reflect,\n                        Glo::Refract => Mf::Refract,\n                        Glo::PackUnorm4x8 => Mf::Pack4x8unorm,\n                        Glo::PackSnorm4x8 => Mf::Pack4x8snorm,\n                        Glo::PackHalf2x16 => Mf::Pack2x16float,\n                        Glo::PackUnorm2x16 => Mf::Pack2x16unorm,\n                        Glo::PackSnorm2x16 => Mf::Pack2x16snorm,\n                        Glo::UnpackUnorm4x8 => Mf::Unpack4x8unorm,\n                        Glo::UnpackSnorm4x8 => Mf::Unpack4x8snorm,\n                        Glo::UnpackHalf2x16 => Mf::Unpack2x16float,\n                        Glo::UnpackUnorm2x16 => Mf::Unpack2x16unorm,\n                        Glo::UnpackSnorm2x16 => Mf::Unpack2x16snorm,\n                        Glo::FindILsb => Mf::FirstTrailingBit,\n                        Glo::FindUMsb | Glo::FindSMsb => Mf::FirstLeadingBit,\n                        // TODO: https://github.com/gfx-rs/naga/issues/2526\n                        Glo::Modf | Glo::Frexp => return Err(Error::UnsupportedExtInst(inst_id)),\n                        Glo::IMix\n                        | Glo::PackDouble2x32\n                        | Glo::UnpackDouble2x32\n                        | Glo::InterpolateAtCentroid\n                        | Glo::InterpolateAtSample\n                        | Glo::InterpolateAtOffset => {\n                            return Err(Error::UnsupportedExtInst(inst_id))\n                        }\n                    };\n\n                    let arg_count = fun.argument_count();\n                    inst.expect(base_wc + arg_count as u16)?;\n                    let arg = {\n                        let arg_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(arg_id)?;\n                        get_expr_handle!(arg_id, lexp)\n                    };\n                    let arg1 = if arg_count > 1 {\n                        let arg_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(arg_id)?;\n                        Some(get_expr_handle!(arg_id, lexp))\n                    } else {\n                        None\n                    };\n                    let arg2 = if arg_count > 2 {\n                        let arg_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(arg_id)?;\n                        Some(get_expr_handle!(arg_id, lexp))\n                    } else {\n                        None\n                    };\n                    let arg3 = if arg_count > 3 {\n                        let arg_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(arg_id)?;\n                        Some(get_expr_handle!(arg_id, lexp))\n                    } else {\n                        None\n                    };\n\n                    let expr = crate::Expression::Math {\n                        fun,\n                        arg,\n                        arg1,\n                        arg2,\n                        arg3,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                // Relational and Logical Instructions\n                Op::LogicalNot => {\n                    inst.expect(4)?;\n                    parse_expr_op!(crate::UnaryOperator::LogicalNot, UNARY)?;\n                }\n                Op::LogicalOr => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::LogicalOr, BINARY)?;\n                }\n                Op::LogicalAnd => {\n                    inst.expect(5)?;\n                    parse_expr_op!(crate::BinaryOperator::LogicalAnd, BINARY)?;\n                }\n                Op::SGreaterThan | Op::SGreaterThanEqual | Op::SLessThan | Op::SLessThanEqual => {\n                    inst.expect(5)?;\n                    self.parse_expr_int_comparison(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        map_binary_operator(inst.op)?,\n                        crate::ScalarKind::Sint,\n                    )?;\n                }\n                Op::UGreaterThan | Op::UGreaterThanEqual | Op::ULessThan | Op::ULessThanEqual => {\n                    inst.expect(5)?;\n                    self.parse_expr_int_comparison(\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        block_id,\n                        body_idx,\n                        map_binary_operator(inst.op)?,\n                        crate::ScalarKind::Uint,\n                    )?;\n                }\n                Op::FOrdEqual\n                | Op::FUnordEqual\n                | Op::FOrdNotEqual\n                | Op::FUnordNotEqual\n                | Op::FOrdLessThan\n                | Op::FUnordLessThan\n                | Op::FOrdGreaterThan\n                | Op::FUnordGreaterThan\n                | Op::FOrdLessThanEqual\n                | Op::FUnordLessThanEqual\n                | Op::FOrdGreaterThanEqual\n                | Op::FUnordGreaterThanEqual\n                | Op::LogicalEqual\n                | Op::LogicalNotEqual => {\n                    inst.expect(5)?;\n                    let operator = map_binary_operator(inst.op)?;\n                    parse_expr_op!(operator, BINARY)?;\n                }\n                Op::Any | Op::All | Op::IsNan | Op::IsInf | Op::IsFinite | Op::IsNormal => {\n                    inst.expect(4)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let arg_id = self.next()?;\n\n                    let arg_lexp = self.lookup_expression.lookup(arg_id)?;\n                    let arg_handle = get_expr_handle!(arg_id, arg_lexp);\n\n                    let expr = crate::Expression::Relational {\n                        fun: map_relational_fun(inst.op)?,\n                        argument: arg_handle,\n                    };\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: ctx.expressions.append(expr, span),\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::Kill => {\n                    inst.expect(1)?;\n                    break Some(crate::Statement::Kill);\n                }\n                Op::Unreachable => {\n                    inst.expect(1)?;\n                    break None;\n                }\n                Op::Return => {\n                    inst.expect(1)?;\n                    break Some(crate::Statement::Return { value: None });\n                }\n                Op::ReturnValue => {\n                    inst.expect(2)?;\n                    let value_id = self.next()?;\n                    let value_lexp = self.lookup_expression.lookup(value_id)?;\n                    let value_handle = get_expr_handle!(value_id, value_lexp);\n                    break Some(crate::Statement::Return {\n                        value: Some(value_handle),\n                    });\n                }\n                Op::Branch => {\n                    inst.expect(2)?;\n                    let target_id = self.next()?;\n\n                    // If this is a branch to a merge or continue block, then\n                    // that ends the current body.\n                    //\n                    // Why can we count on finding an entry here when it's\n                    // needed? SPIR-V requires dominators to appear before\n                    // blocks they dominate, so we will have visited a\n                    // structured control construct's header block before\n                    // anything that could exit it.\n                    if let Some(info) = ctx.mergers.get(&target_id) {\n                        block.extend(emitter.finish(ctx.expressions));\n                        ctx.blocks.insert(block_id, block);\n                        let body = &mut ctx.bodies[body_idx];\n                        body.data.push(BodyFragment::BlockId(block_id));\n\n                        merger(body, info);\n\n                        return Ok(());\n                    }\n\n                    // If `target_id` has no entry in `ctx.body_for_label`, then\n                    // this must be the only branch to it:\n                    //\n                    // - We've already established that it's not anybody's merge\n                    //   block.\n                    //\n                    // - It can't be a switch case. Only switch header blocks\n                    //   and other switch cases can branch to a switch case.\n                    //   Switch header blocks must dominate all their cases, so\n                    //   they must appear in the file before them, and when we\n                    //   see `Op::Switch` we populate `ctx.body_for_label` for\n                    //   every switch case.\n                    //\n                    // Thus, `target_id` must be a simple extension of the\n                    // current block, which we dominate, so we know we'll\n                    // encounter it later in the file.\n                    ctx.body_for_label.entry(target_id).or_insert(body_idx);\n\n                    break None;\n                }\n                Op::BranchConditional => {\n                    inst.expect_at_least(4)?;\n\n                    let condition = {\n                        let condition_id = self.next()?;\n                        let lexp = self.lookup_expression.lookup(condition_id)?;\n                        get_expr_handle!(condition_id, lexp)\n                    };\n\n                    // HACK(eddyb) Naga doesn't seem to have this helper,\n                    // so it's declared on the fly here for convenience.\n                    #[derive(Copy, Clone)]\n                    struct BranchTarget {\n                        label_id: spirv::Word,\n                        merge_info: Option<MergeBlockInformation>,\n                    }\n                    let branch_target = |label_id| BranchTarget {\n                        label_id,\n                        merge_info: ctx.mergers.get(&label_id).copied(),\n                    };\n\n                    let true_target = branch_target(self.next()?);\n                    let false_target = branch_target(self.next()?);\n\n                    // Consume branch weights\n                    for _ in 4..inst.wc {\n                        let _ = self.next()?;\n                    }\n\n                    // Handle `OpBranchConditional`s used at the end of a loop\n                    // body's \"continuing\" section as a \"conditional backedge\",\n                    // i.e. a `do`-`while` condition, or `break if` in WGSL.\n\n                    // HACK(eddyb) this has to go to the parent *twice*, because\n                    // `OpLoopMerge` left the \"continuing\" section nested in the\n                    // loop body in terms of `parent`, but not `BodyFragment`.\n                    let parent_body_idx = ctx.bodies[body_idx].parent;\n                    let parent_parent_body_idx = ctx.bodies[parent_body_idx].parent;\n                    match ctx.bodies[parent_parent_body_idx].data[..] {\n                        // The `OpLoopMerge`'s `continuing` block and the loop's\n                        // backedge block may not be the same, but they'll both\n                        // belong to the same body.\n                        [.., BodyFragment::Loop {\n                            body: loop_body_idx,\n                            continuing: loop_continuing_idx,\n                            break_if: ref mut break_if_slot @ None,\n                        }] if body_idx == loop_continuing_idx => {\n                            // Try both orderings of break-vs-backedge, because\n                            // SPIR-V is symmetrical here, unlike WGSL `break if`.\n                            let break_if_cond = [true, false].into_iter().find_map(|true_breaks| {\n                                let (break_candidate, backedge_candidate) = if true_breaks {\n                                    (true_target, false_target)\n                                } else {\n                                    (false_target, true_target)\n                                };\n\n                                if break_candidate.merge_info\n                                    != Some(MergeBlockInformation::LoopMerge)\n                                {\n                                    return None;\n                                }\n\n                                // HACK(eddyb) since Naga doesn't explicitly track\n                                // backedges, this is checking for the outcome of\n                                // `OpLoopMerge` below (even if it looks weird).\n                                let backedge_candidate_is_backedge =\n                                    backedge_candidate.merge_info.is_none()\n                                        && ctx.body_for_label.get(&backedge_candidate.label_id)\n                                            == Some(&loop_body_idx);\n                                if !backedge_candidate_is_backedge {\n                                    return None;\n                                }\n\n                                Some(if true_breaks {\n                                    condition\n                                } else {\n                                    ctx.expressions.append(\n                                        crate::Expression::Unary {\n                                            op: crate::UnaryOperator::LogicalNot,\n                                            expr: condition,\n                                        },\n                                        span,\n                                    )\n                                })\n                            });\n\n                            if let Some(break_if_cond) = break_if_cond {\n                                *break_if_slot = Some(break_if_cond);\n\n                                // This `OpBranchConditional` ends the \"continuing\"\n                                // section of the loop body as normal, with the\n                                // `break if` condition having been stashed above.\n                                break None;\n                            }\n                        }\n                        _ => {}\n                    }\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    ctx.blocks.insert(block_id, block);\n                    let body = &mut ctx.bodies[body_idx];\n                    body.data.push(BodyFragment::BlockId(block_id));\n\n                    let same_target = true_target.label_id == false_target.label_id;\n\n                    // Start a body block for the `accept` branch.\n                    let accept = ctx.bodies.len();\n                    let mut accept_block = Body::with_parent(body_idx);\n\n                    // If the `OpBranchConditional` target is somebody else's\n                    // merge or continue block, then put a `Break` or `Continue`\n                    // statement in this new body block.\n                    if let Some(info) = true_target.merge_info {\n                        merger(\n                            match same_target {\n                                true => &mut ctx.bodies[body_idx],\n                                false => &mut accept_block,\n                            },\n                            &info,\n                        )\n                    } else {\n                        // Note the body index for the block we're branching to.\n                        let prev = ctx.body_for_label.insert(\n                            true_target.label_id,\n                            match same_target {\n                                true => body_idx,\n                                false => accept,\n                            },\n                        );\n                        debug_assert!(prev.is_none());\n                    }\n\n                    if same_target {\n                        return Ok(());\n                    }\n\n                    ctx.bodies.push(accept_block);\n\n                    // Handle the `reject` branch just like the `accept` block.\n                    let reject = ctx.bodies.len();\n                    let mut reject_block = Body::with_parent(body_idx);\n\n                    if let Some(info) = false_target.merge_info {\n                        merger(&mut reject_block, &info)\n                    } else {\n                        let prev = ctx.body_for_label.insert(false_target.label_id, reject);\n                        debug_assert!(prev.is_none());\n                    }\n\n                    ctx.bodies.push(reject_block);\n\n                    let body = &mut ctx.bodies[body_idx];\n                    body.data.push(BodyFragment::If {\n                        condition,\n                        accept,\n                        reject,\n                    });\n\n                    return Ok(());\n                }\n                Op::Switch => {\n                    inst.expect_at_least(3)?;\n                    let selector = self.next()?;\n                    let default_id = self.next()?;\n\n                    // If the previous instruction was a `OpSelectionMerge` then we must\n                    // promote the `MergeBlockInformation` to a `SwitchMerge`\n                    if let Some(merge) = selection_merge_block {\n                        ctx.mergers\n                            .insert(merge, MergeBlockInformation::SwitchMerge);\n                    }\n\n                    let default = ctx.bodies.len();\n                    ctx.bodies.push(Body::with_parent(body_idx));\n                    ctx.body_for_label.entry(default_id).or_insert(default);\n\n                    let selector_lexp = &self.lookup_expression[&selector];\n                    let selector_lty = self.lookup_type.lookup(selector_lexp.type_id)?;\n                    let selector_handle = get_expr_handle!(selector, selector_lexp);\n                    let selector = match ctx.module.types[selector_lty.handle].inner {\n                        crate::TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Uint,\n                            width: _,\n                        }) => {\n                            // IR expects a signed integer, so do a bitcast\n                            ctx.expressions.append(\n                                crate::Expression::As {\n                                    kind: crate::ScalarKind::Sint,\n                                    expr: selector_handle,\n                                    convert: None,\n                                },\n                                span,\n                            )\n                        }\n                        crate::TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Sint,\n                            width: _,\n                        }) => selector_handle,\n                        ref other => unimplemented!(\"Unexpected selector {:?}\", other),\n                    };\n\n                    // Clear past switch cases to prevent them from entering this one\n                    self.switch_cases.clear();\n\n                    for _ in 0..(inst.wc - 3) / 2 {\n                        let literal = self.next()?;\n                        let target = self.next()?;\n\n                        let case_body_idx = ctx.bodies.len();\n\n                        // Check if any previous case already used this target block id, if so\n                        // group them together to reorder them later so that no weird\n                        // fallthrough cases happen.\n                        if let Some(&mut (_, ref mut literals)) = self.switch_cases.get_mut(&target)\n                        {\n                            literals.push(literal as i32);\n                            continue;\n                        }\n\n                        let mut body = Body::with_parent(body_idx);\n\n                        if let Some(info) = ctx.mergers.get(&target) {\n                            merger(&mut body, info);\n                        }\n\n                        ctx.bodies.push(body);\n                        ctx.body_for_label.entry(target).or_insert(case_body_idx);\n\n                        // Register this target block id as already having been processed and\n                        // the respective body index assigned and the first case value\n                        self.switch_cases\n                            .insert(target, (case_body_idx, vec![literal as i32]));\n                    }\n\n                    // Loop through the collected target blocks creating a new case for each\n                    // literal pointing to it, only one case will have the true body and all the\n                    // others will be empty fallthrough so that they all execute the same body\n                    // without duplicating code.\n                    //\n                    // Since `switch_cases` is an indexmap the order of insertion is preserved\n                    // this is needed because spir-v defines fallthrough order in the switch\n                    // instruction.\n                    let mut cases = Vec::with_capacity((inst.wc as usize - 3) / 2);\n                    for &(case_body_idx, ref literals) in self.switch_cases.values() {\n                        let value = literals[0];\n\n                        for &literal in literals.iter().skip(1) {\n                            let empty_body_idx = ctx.bodies.len();\n                            let body = Body::with_parent(body_idx);\n\n                            ctx.bodies.push(body);\n\n                            cases.push((literal, empty_body_idx));\n                        }\n\n                        cases.push((value, case_body_idx));\n                    }\n\n                    block.extend(emitter.finish(ctx.expressions));\n\n                    let body = &mut ctx.bodies[body_idx];\n                    ctx.blocks.insert(block_id, block);\n                    // Make sure the vector has space for at least two more allocations\n                    body.data.reserve(2);\n                    body.data.push(BodyFragment::BlockId(block_id));\n                    body.data.push(BodyFragment::Switch {\n                        selector,\n                        cases,\n                        default,\n                    });\n\n                    return Ok(());\n                }\n                Op::SelectionMerge => {\n                    inst.expect(3)?;\n                    let merge_block_id = self.next()?;\n                    // TODO: Selection Control Mask\n                    let _selection_control = self.next()?;\n\n                    // Indicate that the merge block is a continuation of the\n                    // current `Body`.\n                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);\n\n                    // Let subsequent branches to the merge block know that\n                    // they've reached the end of the selection construct.\n                    ctx.mergers\n                        .insert(merge_block_id, MergeBlockInformation::SelectionMerge);\n\n                    selection_merge_block = Some(merge_block_id);\n                }\n                Op::LoopMerge => {\n                    inst.expect_at_least(4)?;\n                    let merge_block_id = self.next()?;\n                    let continuing = self.next()?;\n\n                    // TODO: Loop Control Parameters\n                    for _ in 0..inst.wc - 3 {\n                        self.next()?;\n                    }\n\n                    // Indicate that the merge block is a continuation of the\n                    // current `Body`.\n                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);\n                    // Let subsequent branches to the merge block know that\n                    // they're `Break` statements.\n                    ctx.mergers\n                        .insert(merge_block_id, MergeBlockInformation::LoopMerge);\n\n                    let loop_body_idx = ctx.bodies.len();\n                    ctx.bodies.push(Body::with_parent(body_idx));\n\n                    let continue_idx = ctx.bodies.len();\n                    // The continue block inherits the scope of the loop body\n                    ctx.bodies.push(Body::with_parent(loop_body_idx));\n                    ctx.body_for_label.entry(continuing).or_insert(continue_idx);\n                    // Let subsequent branches to the continue block know that\n                    // they're `Continue` statements.\n                    ctx.mergers\n                        .insert(continuing, MergeBlockInformation::LoopContinue);\n\n                    // The loop header always belongs to the loop body\n                    ctx.body_for_label.insert(block_id, loop_body_idx);\n\n                    let parent_body = &mut ctx.bodies[body_idx];\n                    parent_body.data.push(BodyFragment::Loop {\n                        body: loop_body_idx,\n                        continuing: continue_idx,\n                        break_if: None,\n                    });\n                    body_idx = loop_body_idx;\n                }\n                Op::DPdxCoarse => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::X,\n                        crate::DerivativeControl::Coarse,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::DPdyCoarse => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Y,\n                        crate::DerivativeControl::Coarse,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::FwidthCoarse => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Width,\n                        crate::DerivativeControl::Coarse,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::DPdxFine => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::X,\n                        crate::DerivativeControl::Fine,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::DPdyFine => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Y,\n                        crate::DerivativeControl::Fine,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::FwidthFine => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Width,\n                        crate::DerivativeControl::Fine,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::DPdx => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::X,\n                        crate::DerivativeControl::None,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::DPdy => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Y,\n                        crate::DerivativeControl::None,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::Fwidth => {\n                    parse_expr_op!(\n                        crate::DerivativeAxis::Width,\n                        crate::DerivativeControl::None,\n                        DERIVATIVE\n                    )?;\n                }\n                Op::ArrayLength => {\n                    inst.expect(5)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let structure_id = self.next()?;\n                    let member_index = self.next()?;\n\n                    // We're assuming that the validation pass, if it's run, will catch if the\n                    // wrong types or parameters are supplied here.\n\n                    let structure_ptr = self.lookup_expression.lookup(structure_id)?;\n                    let structure_handle = get_expr_handle!(structure_id, structure_ptr);\n\n                    let member_ptr = ctx.expressions.append(\n                        crate::Expression::AccessIndex {\n                            base: structure_handle,\n                            index: member_index,\n                        },\n                        span,\n                    );\n\n                    let length = ctx\n                        .expressions\n                        .append(crate::Expression::ArrayLength(member_ptr), span);\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: length,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::CopyMemory => {\n                    inst.expect_at_least(3)?;\n                    let target_id = self.next()?;\n                    let source_id = self.next()?;\n                    let _memory_access = if inst.wc != 3 {\n                        inst.expect(4)?;\n                        spirv::MemoryAccess::from_bits(self.next()?)\n                            .ok_or(Error::InvalidParameter(Op::CopyMemory))?\n                    } else {\n                        spirv::MemoryAccess::NONE\n                    };\n\n                    // TODO: check if the source and target types are the same?\n                    let target = self.lookup_expression.lookup(target_id)?;\n                    let target_handle = get_expr_handle!(target_id, target);\n                    let source = self.lookup_expression.lookup(source_id)?;\n                    let source_handle = get_expr_handle!(source_id, source);\n\n                    // This operation is practically the same as loading and then storing, I think.\n                    let value_expr = ctx.expressions.append(\n                        crate::Expression::Load {\n                            pointer: source_handle,\n                        },\n                        span,\n                    );\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    block.push(\n                        crate::Statement::Store {\n                            pointer: target_handle,\n                            value: value_expr,\n                        },\n                        span,\n                    );\n\n                    emitter.start(ctx.expressions);\n                }\n                Op::ControlBarrier => {\n                    inst.expect(4)?;\n                    let exec_scope_id = self.next()?;\n                    let _mem_scope_raw = self.next()?;\n                    let semantics_id = self.next()?;\n                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;\n                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;\n\n                    let exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)\n                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;\n                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)\n                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;\n\n                    if exec_scope == spirv::Scope::Workgroup as u32\n                        || exec_scope == spirv::Scope::Subgroup as u32\n                    {\n                        let mut flags = crate::Barrier::empty();\n                        flags.set(\n                            crate::Barrier::STORAGE,\n                            semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,\n                        );\n                        flags.set(\n                            crate::Barrier::WORK_GROUP,\n                            semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,\n                        );\n                        flags.set(\n                            crate::Barrier::SUB_GROUP,\n                            semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,\n                        );\n                        flags.set(\n                            crate::Barrier::TEXTURE,\n                            semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,\n                        );\n\n                        block.extend(emitter.finish(ctx.expressions));\n                        block.push(crate::Statement::ControlBarrier(flags), span);\n                        emitter.start(ctx.expressions);\n                    } else {\n                        log::warn!(\"Unsupported barrier execution scope: {exec_scope}\");\n                    }\n                }\n                Op::MemoryBarrier => {\n                    inst.expect(3)?;\n                    let mem_scope_id = self.next()?;\n                    let semantics_id = self.next()?;\n                    let mem_scope_const = self.lookup_constant.lookup(mem_scope_id)?;\n                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;\n\n                    let mem_scope = resolve_constant(ctx.gctx(), &mem_scope_const.inner)\n                        .ok_or(Error::InvalidBarrierScope(mem_scope_id))?;\n                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)\n                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;\n\n                    let mut flags = if mem_scope == spirv::Scope::Device as u32 {\n                        crate::Barrier::STORAGE\n                    } else if mem_scope == spirv::Scope::Workgroup as u32 {\n                        crate::Barrier::WORK_GROUP\n                    } else if mem_scope == spirv::Scope::Subgroup as u32 {\n                        crate::Barrier::SUB_GROUP\n                    } else {\n                        crate::Barrier::empty()\n                    };\n                    flags.set(\n                        crate::Barrier::STORAGE,\n                        semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,\n                    );\n                    flags.set(\n                        crate::Barrier::WORK_GROUP,\n                        semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,\n                    );\n                    flags.set(\n                        crate::Barrier::SUB_GROUP,\n                        semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,\n                    );\n                    flags.set(\n                        crate::Barrier::TEXTURE,\n                        semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,\n                    );\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    block.push(crate::Statement::MemoryBarrier(flags), span);\n                    emitter.start(ctx.expressions);\n                }\n                Op::CopyObject => {\n                    inst.expect(4)?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let operand_id = self.next()?;\n\n                    let lookup = self.lookup_expression.lookup(operand_id)?;\n                    let handle = get_expr_handle!(operand_id, lookup);\n\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n                }\n                Op::GroupNonUniformBallot => {\n                    inst.expect(5)?;\n                    block.extend(emitter.finish(ctx.expressions));\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let exec_scope_id = self.next()?;\n                    let predicate_id = self.next()?;\n\n                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;\n                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)\n                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)\n                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;\n\n                    let predicate = if self\n                        .lookup_constant\n                        .lookup(predicate_id)\n                        .ok()\n                        .filter(|predicate_const| match predicate_const.inner {\n                            Constant::Constant(constant) => matches!(\n                                ctx.gctx().global_expressions[ctx.gctx().constants[constant].init],\n                                crate::Expression::Literal(crate::Literal::Bool(true)),\n                            ),\n                            Constant::Override(_) => false,\n                        })\n                        .is_some()\n                    {\n                        None\n                    } else {\n                        let predicate_lookup = self.lookup_expression.lookup(predicate_id)?;\n                        let predicate_handle = get_expr_handle!(predicate_id, predicate_lookup);\n                        Some(predicate_handle)\n                    };\n\n                    let result_handle = ctx\n                        .expressions\n                        .append(crate::Expression::SubgroupBallotResult, span);\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: result_handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n\n                    block.push(\n                        crate::Statement::SubgroupBallot {\n                            result: result_handle,\n                            predicate,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::GroupNonUniformAll\n                | Op::GroupNonUniformAny\n                | Op::GroupNonUniformIAdd\n                | Op::GroupNonUniformFAdd\n                | Op::GroupNonUniformIMul\n                | Op::GroupNonUniformFMul\n                | Op::GroupNonUniformSMax\n                | Op::GroupNonUniformUMax\n                | Op::GroupNonUniformFMax\n                | Op::GroupNonUniformSMin\n                | Op::GroupNonUniformUMin\n                | Op::GroupNonUniformFMin\n                | Op::GroupNonUniformBitwiseAnd\n                | Op::GroupNonUniformBitwiseOr\n                | Op::GroupNonUniformBitwiseXor\n                | Op::GroupNonUniformLogicalAnd\n                | Op::GroupNonUniformLogicalOr\n                | Op::GroupNonUniformLogicalXor => {\n                    block.extend(emitter.finish(ctx.expressions));\n                    inst.expect(\n                        if matches!(inst.op, Op::GroupNonUniformAll | Op::GroupNonUniformAny) {\n                            5\n                        } else {\n                            6\n                        },\n                    )?;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let exec_scope_id = self.next()?;\n                    let collective_op_id = match inst.op {\n                        Op::GroupNonUniformAll | Op::GroupNonUniformAny => {\n                            crate::CollectiveOperation::Reduce\n                        }\n                        _ => {\n                            let group_op_id = self.next()?;\n                            match spirv::GroupOperation::from_u32(group_op_id) {\n                                Some(spirv::GroupOperation::Reduce) => {\n                                    crate::CollectiveOperation::Reduce\n                                }\n                                Some(spirv::GroupOperation::InclusiveScan) => {\n                                    crate::CollectiveOperation::InclusiveScan\n                                }\n                                Some(spirv::GroupOperation::ExclusiveScan) => {\n                                    crate::CollectiveOperation::ExclusiveScan\n                                }\n                                _ => return Err(Error::UnsupportedGroupOperation(group_op_id)),\n                            }\n                        }\n                    };\n                    let argument_id = self.next()?;\n\n                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;\n                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);\n\n                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;\n                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)\n                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)\n                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;\n\n                    let op_id = match inst.op {\n                        Op::GroupNonUniformAll => crate::SubgroupOperation::All,\n                        Op::GroupNonUniformAny => crate::SubgroupOperation::Any,\n                        Op::GroupNonUniformIAdd | Op::GroupNonUniformFAdd => {\n                            crate::SubgroupOperation::Add\n                        }\n                        Op::GroupNonUniformIMul | Op::GroupNonUniformFMul => {\n                            crate::SubgroupOperation::Mul\n                        }\n                        Op::GroupNonUniformSMax\n                        | Op::GroupNonUniformUMax\n                        | Op::GroupNonUniformFMax => crate::SubgroupOperation::Max,\n                        Op::GroupNonUniformSMin\n                        | Op::GroupNonUniformUMin\n                        | Op::GroupNonUniformFMin => crate::SubgroupOperation::Min,\n                        Op::GroupNonUniformBitwiseAnd | Op::GroupNonUniformLogicalAnd => {\n                            crate::SubgroupOperation::And\n                        }\n                        Op::GroupNonUniformBitwiseOr | Op::GroupNonUniformLogicalOr => {\n                            crate::SubgroupOperation::Or\n                        }\n                        Op::GroupNonUniformBitwiseXor | Op::GroupNonUniformLogicalXor => {\n                            crate::SubgroupOperation::Xor\n                        }\n                        _ => unreachable!(),\n                    };\n\n                    let result_type = self.lookup_type.lookup(result_type_id)?;\n\n                    let result_handle = ctx.expressions.append(\n                        crate::Expression::SubgroupOperationResult {\n                            ty: result_type.handle,\n                        },\n                        span,\n                    );\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: result_handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n\n                    block.push(\n                        crate::Statement::SubgroupCollectiveOperation {\n                            result: result_handle,\n                            op: op_id,\n                            collective_op: collective_op_id,\n                            argument: argument_handle,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::GroupNonUniformBroadcastFirst\n                | Op::GroupNonUniformBroadcast\n                | Op::GroupNonUniformShuffle\n                | Op::GroupNonUniformShuffleDown\n                | Op::GroupNonUniformShuffleUp\n                | Op::GroupNonUniformShuffleXor\n                | Op::GroupNonUniformQuadBroadcast => {\n                    inst.expect(if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {\n                        5\n                    } else {\n                        6\n                    })?;\n                    block.extend(emitter.finish(ctx.expressions));\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let exec_scope_id = self.next()?;\n                    let argument_id = self.next()?;\n\n                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;\n                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);\n\n                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;\n                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)\n                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)\n                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;\n\n                    let mode = if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {\n                        crate::GatherMode::BroadcastFirst\n                    } else {\n                        let index_id = self.next()?;\n                        let index_lookup = self.lookup_expression.lookup(index_id)?;\n                        let index_handle = get_expr_handle!(index_id, index_lookup);\n                        match inst.op {\n                            Op::GroupNonUniformBroadcast => {\n                                crate::GatherMode::Broadcast(index_handle)\n                            }\n                            Op::GroupNonUniformShuffle => crate::GatherMode::Shuffle(index_handle),\n                            Op::GroupNonUniformShuffleDown => {\n                                crate::GatherMode::ShuffleDown(index_handle)\n                            }\n                            Op::GroupNonUniformShuffleUp => {\n                                crate::GatherMode::ShuffleUp(index_handle)\n                            }\n                            Op::GroupNonUniformShuffleXor => {\n                                crate::GatherMode::ShuffleXor(index_handle)\n                            }\n                            Op::GroupNonUniformQuadBroadcast => {\n                                crate::GatherMode::QuadBroadcast(index_handle)\n                            }\n                            _ => unreachable!(),\n                        }\n                    };\n\n                    let result_type = self.lookup_type.lookup(result_type_id)?;\n\n                    let result_handle = ctx.expressions.append(\n                        crate::Expression::SubgroupOperationResult {\n                            ty: result_type.handle,\n                        },\n                        span,\n                    );\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: result_handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n\n                    block.push(\n                        crate::Statement::SubgroupGather {\n                            result: result_handle,\n                            mode,\n                            argument: argument_handle,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::GroupNonUniformQuadSwap => {\n                    inst.expect(6)?;\n                    block.extend(emitter.finish(ctx.expressions));\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let exec_scope_id = self.next()?;\n                    let argument_id = self.next()?;\n                    let direction_id = self.next()?;\n\n                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;\n                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);\n\n                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;\n                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)\n                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)\n                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;\n\n                    let direction_const = self.lookup_constant.lookup(direction_id)?;\n                    let direction_const = resolve_constant(ctx.gctx(), &direction_const.inner)\n                        .ok_or(Error::InvalidOperand)?;\n                    let direction = match direction_const {\n                        0 => crate::Direction::X,\n                        1 => crate::Direction::Y,\n                        2 => crate::Direction::Diagonal,\n                        _ => unreachable!(),\n                    };\n\n                    let result_type = self.lookup_type.lookup(result_type_id)?;\n\n                    let result_handle = ctx.expressions.append(\n                        crate::Expression::SubgroupOperationResult {\n                            ty: result_type.handle,\n                        },\n                        span,\n                    );\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle: result_handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n\n                    block.push(\n                        crate::Statement::SubgroupGather {\n                            mode: crate::GatherMode::QuadSwap(direction),\n                            result: result_handle,\n                            argument: argument_handle,\n                        },\n                        span,\n                    );\n                    emitter.start(ctx.expressions);\n                }\n                Op::AtomicLoad => {\n                    inst.expect(6)?;\n                    let start = self.data_offset;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let pointer_id = self.next()?;\n                    let _scope_id = self.next()?;\n                    let _memory_semantics_id = self.next()?;\n                    let span = self.span_from_with_op(start);\n\n                    log::trace!(\"\\t\\t\\tlooking up expr {pointer_id:?}\");\n                    let p_lexp_handle =\n                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);\n\n                    // Create an expression for our result\n                    let expr = crate::Expression::Load {\n                        pointer: p_lexp_handle,\n                    };\n                    let handle = ctx.expressions.append(expr, span);\n                    self.lookup_expression.insert(\n                        result_id,\n                        LookupExpression {\n                            handle,\n                            type_id: result_type_id,\n                            block_id,\n                        },\n                    );\n\n                    // Store any associated global variables so we can upgrade their types later\n                    self.record_atomic_access(ctx, p_lexp_handle)?;\n                }\n                Op::AtomicStore => {\n                    inst.expect(5)?;\n                    let start = self.data_offset;\n                    let pointer_id = self.next()?;\n                    let _scope_id = self.next()?;\n                    let _memory_semantics_id = self.next()?;\n                    let value_id = self.next()?;\n                    let span = self.span_from_with_op(start);\n\n                    log::trace!(\"\\t\\t\\tlooking up pointer expr {pointer_id:?}\");\n                    let p_lexp_handle =\n                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);\n\n                    log::trace!(\"\\t\\t\\tlooking up value expr {pointer_id:?}\");\n                    let v_lexp_handle =\n                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    // Create a statement for the op itself\n                    let stmt = crate::Statement::Store {\n                        pointer: p_lexp_handle,\n                        value: v_lexp_handle,\n                    };\n                    block.push(stmt, span);\n                    emitter.start(ctx.expressions);\n\n                    // Store any associated global variables so we can upgrade their types later\n                    self.record_atomic_access(ctx, p_lexp_handle)?;\n                }\n                Op::AtomicIIncrement | Op::AtomicIDecrement => {\n                    inst.expect(6)?;\n                    let start = self.data_offset;\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let pointer_id = self.next()?;\n                    let _scope_id = self.next()?;\n                    let _memory_semantics_id = self.next()?;\n                    let span = self.span_from_with_op(start);\n\n                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(\n                        pointer_id,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    )?;\n\n                    block.extend(emitter.finish(ctx.expressions));\n                    // Create an expression for our result\n                    let r_lexp_handle = {\n                        let expr = crate::Expression::AtomicResult {\n                            ty: p_base_ty_h,\n                            comparison: false,\n                        };\n                        let handle = ctx.expressions.append(expr, span);\n                        self.lookup_expression.insert(\n                            result_id,\n                            LookupExpression {\n                                handle,\n                                type_id: result_type_id,\n                                block_id,\n                            },\n                        );\n                        handle\n                    };\n                    emitter.start(ctx.expressions);\n\n                    // Create a literal \"1\" to use as our value\n                    let one_lexp_handle = make_index_literal(\n                        ctx,\n                        1,\n                        &mut block,\n                        &mut emitter,\n                        p_base_ty_h,\n                        result_type_id,\n                        span,\n                    )?;\n\n                    // Create a statement for the op itself\n                    let stmt = crate::Statement::Atomic {\n                        pointer: p_exp_h,\n                        fun: match inst.op {\n                            Op::AtomicIIncrement => crate::AtomicFunction::Add,\n                            _ => crate::AtomicFunction::Subtract,\n                        },\n                        value: one_lexp_handle,\n                        result: Some(r_lexp_handle),\n                    };\n                    block.push(stmt, span);\n\n                    // Store any associated global variables so we can upgrade their types later\n                    self.record_atomic_access(ctx, p_exp_h)?;\n                }\n                Op::AtomicCompareExchange => {\n                    inst.expect(9)?;\n\n                    let start = self.data_offset;\n                    let span = self.span_from_with_op(start);\n                    let result_type_id = self.next()?;\n                    let result_id = self.next()?;\n                    let pointer_id = self.next()?;\n                    let _memory_scope_id = self.next()?;\n                    let _equal_memory_semantics_id = self.next()?;\n                    let _unequal_memory_semantics_id = self.next()?;\n                    let value_id = self.next()?;\n                    let comparator_id = self.next()?;\n\n                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(\n                        pointer_id,\n                        ctx,\n                        &mut emitter,\n                        &mut block,\n                        body_idx,\n                    )?;\n\n                    log::trace!(\"\\t\\t\\tlooking up value expr {value_id:?}\");\n                    let v_lexp_handle =\n                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);\n\n                    log::trace!(\"\\t\\t\\tlooking up comparator expr {value_id:?}\");\n                    let c_lexp_handle = get_expr_handle!(\n                        comparator_id,\n                        self.lookup_expression.lookup(comparator_id)?\n                    );\n\n                    // We know from the SPIR-V spec that the result type must be an integer\n                    // scalar, and we'll need the type itself to get a handle to the atomic\n                    // result struct.\n                    let crate::TypeInner::Scalar(scalar) = ctx.module.types[p_base_ty_h].inner\n                    else {\n                        return Err(\n                            crate::front::atomic_upgrade::Error::CompareExchangeNonScalarBaseType\n                                .into(),\n                        );\n                    };\n\n                    // Get a handle to the atomic result struct type.\n                    let atomic_result_struct_ty_h = ctx.module.generate_predeclared_type(\n                        crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),\n                    );\n\n                    block.extend(emitter.finish(ctx.expressions));\n\n                    // Create an expression for our atomic result\n                    let atomic_lexp_handle = {\n                        let expr = crate::Expression::AtomicResult {\n                            ty: atomic_result_struct_ty_h,\n                            comparison: true,\n                        };\n                        ctx.expressions.append(expr, span)\n                    };\n\n                    // Create an dot accessor to extract the value from the\n                    // result struct __atomic_compare_exchange_result<T> and use that\n                    // as the expression for the result_id\n                    {\n                        let expr = crate::Expression::AccessIndex {\n                            base: atomic_lexp_handle,\n                            index: 0,\n                        };\n                        let handle = ctx.expressions.append(expr, span);\n                        // Use this dot accessor as the result id's expression\n                        let _ = self.lookup_expression.insert(\n                            result_id,\n                            LookupExpression {\n                                handle,\n                                type_id: result_type_id,\n                                block_id,\n                            },\n                        );\n                    }\n\n                    emitter.start(ctx.expressions);\n\n                    // Create a statement for the op itself\n                    let stmt = crate::Statement::Atomic {\n                        pointer: p_exp_h,\n                        fun: crate::AtomicFunction::Exchange {\n                            compare: Some(c_lexp_handle),\n                        },\n                        value: v_lexp_handle,\n                        result: Some(atomic_lexp_handle),\n                    };\n                    block.push(stmt, span);\n\n                    // Store any associated global variables so we can upgrade their types later\n                    self.record_atomic_access(ctx, p_exp_h)?;\n                }\n                Op::AtomicExchange\n                | Op::AtomicIAdd\n                | Op::AtomicISub\n                | Op::AtomicSMin\n                | Op::AtomicUMin\n                | Op::AtomicSMax\n                | Op::AtomicUMax\n                | Op::AtomicAnd\n                | Op::AtomicOr\n                | Op::AtomicXor\n                | Op::AtomicFAddEXT => self.parse_atomic_expr_with_value(\n                    inst,\n                    &mut emitter,\n                    ctx,\n                    &mut block,\n                    block_id,\n                    body_idx,\n                    match inst.op {\n                        Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },\n                        Op::AtomicIAdd | Op::AtomicFAddEXT => crate::AtomicFunction::Add,\n                        Op::AtomicISub => crate::AtomicFunction::Subtract,\n                        Op::AtomicSMin => crate::AtomicFunction::Min,\n                        Op::AtomicUMin => crate::AtomicFunction::Min,\n                        Op::AtomicSMax => crate::AtomicFunction::Max,\n                        Op::AtomicUMax => crate::AtomicFunction::Max,\n                        Op::AtomicAnd => crate::AtomicFunction::And,\n                        Op::AtomicOr => crate::AtomicFunction::InclusiveOr,\n                        Op::AtomicXor => crate::AtomicFunction::ExclusiveOr,\n                        _ => unreachable!(),\n                    },\n                )?,\n\n                _ => {\n                    return Err(Error::UnsupportedInstruction(self.state, inst.op));\n                }\n            }\n        };\n\n        block.extend(emitter.finish(ctx.expressions));\n        if let Some(stmt) = terminator {\n            block.push(stmt, crate::Span::default());\n        }\n\n        // Save this block fragment in `block_ctx.blocks`, and mark it to be\n        // incorporated into the current body at `Statement` assembly time.\n        ctx.blocks.insert(block_id, block);\n        let body = &mut ctx.bodies[body_idx];\n        body.data.push(BodyFragment::BlockId(block_id));\n        Ok(())\n    }\n}\n\nfn make_index_literal(\n    ctx: &mut BlockContext,\n    index: u32,\n    block: &mut crate::Block,\n    emitter: &mut crate::proc::Emitter,\n    index_type: Handle<crate::Type>,\n    index_type_id: spirv::Word,\n    span: crate::Span,\n) -> Result<Handle<crate::Expression>, Error> {\n    block.extend(emitter.finish(ctx.expressions));\n\n    let literal = match ctx.module.types[index_type].inner.scalar_kind() {\n        Some(crate::ScalarKind::Uint) => crate::Literal::U32(index),\n        Some(crate::ScalarKind::Sint) => crate::Literal::I32(index as i32),\n        _ => return Err(Error::InvalidIndexType(index_type_id)),\n    };\n    let expr = ctx\n        .expressions\n        .append(crate::Expression::Literal(literal), span);\n\n    emitter.start(ctx.expressions);\n    Ok(expr)\n}\n"
  },
  {
    "path": "naga/src/front/spv/null.rs",
    "content": "use alloc::vec;\n\nuse super::Error;\nuse crate::arena::{Arena, Handle};\n\n/// Create a default value for an output built-in.\npub fn generate_default_built_in(\n    built_in: Option<crate::BuiltIn>,\n    ty: Handle<crate::Type>,\n    global_expressions: &mut Arena<crate::Expression>,\n    span: crate::Span,\n) -> Result<Handle<crate::Expression>, Error> {\n    let expr = match built_in {\n        Some(crate::BuiltIn::Position { .. }) => {\n            let zero = global_expressions\n                .append(crate::Expression::Literal(crate::Literal::F32(0.0)), span);\n            let one = global_expressions\n                .append(crate::Expression::Literal(crate::Literal::F32(1.0)), span);\n            crate::Expression::Compose {\n                ty,\n                components: vec![zero, zero, zero, one],\n            }\n        }\n        Some(crate::BuiltIn::PointSize) => crate::Expression::Literal(crate::Literal::F32(1.0)),\n        Some(crate::BuiltIn::FragDepth) => crate::Expression::Literal(crate::Literal::F32(0.0)),\n        Some(crate::BuiltIn::SampleMask) => {\n            crate::Expression::Literal(crate::Literal::U32(u32::MAX))\n        }\n        // Note: `crate::BuiltIn::ClipDistance` is intentionally left for the default path\n        _ => crate::Expression::ZeroValue(ty),\n    };\n    Ok(global_expressions.append(expr, span))\n}\n"
  },
  {
    "path": "naga/src/front/type_gen.rs",
    "content": "/*!\nType generators.\n*/\n\nuse alloc::{string::ToString, vec};\n\nuse crate::{arena::Handle, span::Span};\n\nimpl crate::Module {\n    /// Populate this module's [`SpecialTypes::ray_desc`] type.\n    ///\n    /// [`SpecialTypes::ray_desc`] is the type of the [`descriptor`] operand of\n    /// an [`Initialize`] [`RayQuery`] statement. In WGSL, it is a struct type\n    /// referred to as `RayDesc`.\n    ///\n    /// Backends consume values of this type to drive platform APIs, so if you\n    /// change any its fields, you must update the backends to match. Look for\n    /// backend code dealing with [`RayQueryFunction::Initialize`].\n    ///\n    /// [`SpecialTypes::ray_desc`]: crate::SpecialTypes::ray_desc\n    /// [`descriptor`]: crate::RayQueryFunction::Initialize::descriptor\n    /// [`Initialize`]: crate::RayQueryFunction::Initialize\n    /// [`RayQuery`]: crate::Statement::RayQuery\n    /// [`RayQueryFunction::Initialize`]: crate::RayQueryFunction::Initialize\n    pub fn generate_ray_desc_type(&mut self) -> Handle<crate::Type> {\n        if let Some(handle) = self.special_types.ray_desc {\n            return handle;\n        }\n\n        let ty_flag = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_scalar = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::F32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_vector = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Vector {\n                    size: crate::VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n\n        let handle = self.types.insert(\n            crate::Type {\n                name: Some(\"RayDesc\".to_string()),\n                inner: crate::TypeInner::Struct {\n                    members: vec![\n                        crate::StructMember {\n                            name: Some(\"flags\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 0,\n                        },\n                        crate::StructMember {\n                            name: Some(\"cull_mask\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 4,\n                        },\n                        crate::StructMember {\n                            name: Some(\"tmin\".to_string()),\n                            ty: ty_scalar,\n                            binding: None,\n                            offset: 8,\n                        },\n                        crate::StructMember {\n                            name: Some(\"tmax\".to_string()),\n                            ty: ty_scalar,\n                            binding: None,\n                            offset: 12,\n                        },\n                        crate::StructMember {\n                            name: Some(\"origin\".to_string()),\n                            ty: ty_vector,\n                            binding: None,\n                            offset: 16,\n                        },\n                        crate::StructMember {\n                            name: Some(\"dir\".to_string()),\n                            ty: ty_vector,\n                            binding: None,\n                            offset: 32,\n                        },\n                    ],\n                    span: 48,\n                },\n            },\n            Span::UNDEFINED,\n        );\n\n        self.special_types.ray_desc = Some(handle);\n        handle\n    }\n\n    /// Make sure the types for the vertex return are in the module's type\n    pub fn generate_vertex_return_type(&mut self) -> Handle<crate::Type> {\n        if let Some(handle) = self.special_types.ray_vertex_return {\n            return handle;\n        }\n        let ty_vec3f = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Vector {\n                    size: crate::VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        let array = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Array {\n                    base: ty_vec3f,\n                    size: crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap()),\n                    stride: 16,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        self.special_types.ray_vertex_return = Some(array);\n        array\n    }\n\n    /// Populate this module's [`SpecialTypes::ray_intersection`] type.\n    ///\n    /// [`SpecialTypes::ray_intersection`] is the type of a\n    /// `RayQueryGetIntersection` expression. In WGSL, it is a struct type\n    /// referred to as `RayIntersection`.\n    ///\n    /// Backends construct values of this type based on platform APIs, so if you\n    /// change any its fields, you must update the backends to match. Look for\n    /// the backend's handling for [`Expression::RayQueryGetIntersection`].\n    ///\n    /// [`SpecialTypes::ray_intersection`]: crate::SpecialTypes::ray_intersection\n    /// [`Expression::RayQueryGetIntersection`]: crate::Expression::RayQueryGetIntersection\n    pub fn generate_ray_intersection_type(&mut self) -> Handle<crate::Type> {\n        if let Some(handle) = self.special_types.ray_intersection {\n            return handle;\n        }\n\n        let ty_flag = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_scalar = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::F32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_barycentrics = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Vector {\n                    size: crate::VectorSize::Bi,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        let ty_bool = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::BOOL),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_transform = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Matrix {\n                    columns: crate::VectorSize::Quad,\n                    rows: crate::VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n\n        let handle = self.types.insert(\n            crate::Type {\n                name: Some(\"RayIntersection\".to_string()),\n                inner: crate::TypeInner::Struct {\n                    members: vec![\n                        crate::StructMember {\n                            name: Some(\"kind\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 0,\n                        },\n                        crate::StructMember {\n                            name: Some(\"t\".to_string()),\n                            ty: ty_scalar,\n                            binding: None,\n                            offset: 4,\n                        },\n                        crate::StructMember {\n                            name: Some(\"instance_custom_data\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 8,\n                        },\n                        crate::StructMember {\n                            name: Some(\"instance_index\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 12,\n                        },\n                        crate::StructMember {\n                            name: Some(\"sbt_record_offset\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 16,\n                        },\n                        crate::StructMember {\n                            name: Some(\"geometry_index\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 20,\n                        },\n                        crate::StructMember {\n                            name: Some(\"primitive_index\".to_string()),\n                            ty: ty_flag,\n                            binding: None,\n                            offset: 24,\n                        },\n                        crate::StructMember {\n                            name: Some(\"barycentrics\".to_string()),\n                            ty: ty_barycentrics,\n                            binding: None,\n                            offset: 28,\n                        },\n                        crate::StructMember {\n                            name: Some(\"front_face\".to_string()),\n                            ty: ty_bool,\n                            binding: None,\n                            offset: 36,\n                        },\n                        crate::StructMember {\n                            name: Some(\"object_to_world\".to_string()),\n                            ty: ty_transform,\n                            binding: None,\n                            offset: 48,\n                        },\n                        crate::StructMember {\n                            name: Some(\"world_to_object\".to_string()),\n                            ty: ty_transform,\n                            binding: None,\n                            offset: 112,\n                        },\n                    ],\n                    span: 176,\n                },\n            },\n            Span::UNDEFINED,\n        );\n\n        self.special_types.ray_intersection = Some(handle);\n        handle\n    }\n\n    /// Generate [`SpecialTypes::external_texture_params`] and\n    /// [`SpecialTypes::external_texture_transfer_function`].\n    ///\n    /// Other than the WGSL backend, every backend that supports external\n    /// textures does so by lowering them to a set of ordinary textures and\n    /// some parameters saying how to sample from them. These types are used\n    /// for said parameters. Note that they are not used by the IR, but\n    /// generated purely as a convenience for the backends.\n    ///\n    /// [`SpecialTypes::external_texture_params`]: crate::ir::SpecialTypes::external_texture_params\n    /// [`SpecialTypes::external_texture_transfer_function`]: crate::ir::SpecialTypes::external_texture_transfer_function\n    pub fn generate_external_texture_types(&mut self) {\n        if self.special_types.external_texture_params.is_some() {\n            return;\n        }\n\n        let ty_f32 = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::F32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_u32 = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Scalar(crate::Scalar::U32),\n            },\n            Span::UNDEFINED,\n        );\n        let ty_vec2u = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Vector {\n                    size: crate::VectorSize::Bi,\n                    scalar: crate::Scalar::U32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        let ty_mat3x2f = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Matrix {\n                    columns: crate::VectorSize::Tri,\n                    rows: crate::VectorSize::Bi,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        let ty_mat3x3f = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Matrix {\n                    columns: crate::VectorSize::Tri,\n                    rows: crate::VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        let ty_mat4x4f = self.types.insert(\n            crate::Type {\n                name: None,\n                inner: crate::TypeInner::Matrix {\n                    columns: crate::VectorSize::Quad,\n                    rows: crate::VectorSize::Quad,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Span::UNDEFINED,\n        );\n\n        let transfer_fn_handle = self.types.insert(\n            crate::Type {\n                name: Some(\"NagaExternalTextureTransferFn\".to_string()),\n                inner: crate::TypeInner::Struct {\n                    members: vec![\n                        crate::StructMember {\n                            name: Some(\"a\".to_string()),\n                            ty: ty_f32,\n                            binding: None,\n                            offset: 0,\n                        },\n                        crate::StructMember {\n                            name: Some(\"b\".to_string()),\n                            ty: ty_f32,\n                            binding: None,\n                            offset: 4,\n                        },\n                        crate::StructMember {\n                            name: Some(\"g\".to_string()),\n                            ty: ty_f32,\n                            binding: None,\n                            offset: 8,\n                        },\n                        crate::StructMember {\n                            name: Some(\"k\".to_string()),\n                            ty: ty_f32,\n                            binding: None,\n                            offset: 12,\n                        },\n                    ],\n                    span: 16,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        self.special_types.external_texture_transfer_function = Some(transfer_fn_handle);\n\n        let params_handle = self.types.insert(\n            crate::Type {\n                name: Some(\"NagaExternalTextureParams\".to_string()),\n                inner: crate::TypeInner::Struct {\n                    members: vec![\n                        crate::StructMember {\n                            name: Some(\"yuv_conversion_matrix\".to_string()),\n                            ty: ty_mat4x4f,\n                            binding: None,\n                            offset: 0,\n                        },\n                        crate::StructMember {\n                            name: Some(\"gamut_conversion_matrix\".to_string()),\n                            ty: ty_mat3x3f,\n                            binding: None,\n                            offset: 64,\n                        },\n                        crate::StructMember {\n                            name: Some(\"src_tf\".to_string()),\n                            ty: transfer_fn_handle,\n                            binding: None,\n                            offset: 112,\n                        },\n                        crate::StructMember {\n                            name: Some(\"dst_tf\".to_string()),\n                            ty: transfer_fn_handle,\n                            binding: None,\n                            offset: 128,\n                        },\n                        crate::StructMember {\n                            name: Some(\"sample_transform\".to_string()),\n                            ty: ty_mat3x2f,\n                            binding: None,\n                            offset: 144,\n                        },\n                        crate::StructMember {\n                            name: Some(\"load_transform\".to_string()),\n                            ty: ty_mat3x2f,\n                            binding: None,\n                            offset: 168,\n                        },\n                        crate::StructMember {\n                            name: Some(\"size\".to_string()),\n                            ty: ty_vec2u,\n                            binding: None,\n                            offset: 192,\n                        },\n                        crate::StructMember {\n                            name: Some(\"num_planes\".to_string()),\n                            ty: ty_u32,\n                            binding: None,\n                            offset: 200,\n                        },\n                    ],\n                    span: 208,\n                },\n            },\n            Span::UNDEFINED,\n        );\n        self.special_types.external_texture_params = Some(params_handle);\n    }\n\n    /// Populate this module's [`SpecialTypes::predeclared_types`] type and return the handle.\n    ///\n    /// [`SpecialTypes::predeclared_types`]: crate::SpecialTypes::predeclared_types\n    pub fn generate_predeclared_type(\n        &mut self,\n        special_type: crate::PredeclaredType,\n    ) -> Handle<crate::Type> {\n        if let Some(value) = self.special_types.predeclared_types.get(&special_type) {\n            return *value;\n        }\n\n        let name = special_type.struct_name();\n        let ty = match special_type {\n            crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar) => {\n                let bool_ty = self.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Scalar(crate::Scalar::BOOL),\n                    },\n                    Span::UNDEFINED,\n                );\n                let scalar_ty = self.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Scalar(scalar),\n                    },\n                    Span::UNDEFINED,\n                );\n\n                crate::Type {\n                    name: Some(name),\n                    inner: crate::TypeInner::Struct {\n                        members: vec![\n                            crate::StructMember {\n                                name: Some(\"old_value\".to_string()),\n                                ty: scalar_ty,\n                                binding: None,\n                                offset: 0,\n                            },\n                            crate::StructMember {\n                                name: Some(\"exchanged\".to_string()),\n                                ty: bool_ty,\n                                binding: None,\n                                offset: scalar.width as u32,\n                            },\n                        ],\n                        span: scalar.width as u32 * 2,\n                    },\n                }\n            }\n            crate::PredeclaredType::ModfResult { size, scalar } => {\n                let float_ty = self.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Scalar(scalar),\n                    },\n                    Span::UNDEFINED,\n                );\n\n                let (member_ty, second_offset) = if let Some(size) = size {\n                    let vec_ty = self.types.insert(\n                        crate::Type {\n                            name: None,\n                            inner: crate::TypeInner::Vector { size, scalar },\n                        },\n                        Span::UNDEFINED,\n                    );\n                    (vec_ty, size as u32 * scalar.width as u32)\n                } else {\n                    (float_ty, scalar.width as u32)\n                };\n\n                crate::Type {\n                    name: Some(name),\n                    inner: crate::TypeInner::Struct {\n                        members: vec![\n                            crate::StructMember {\n                                name: Some(\"fract\".to_string()),\n                                ty: member_ty,\n                                binding: None,\n                                offset: 0,\n                            },\n                            crate::StructMember {\n                                name: Some(\"whole\".to_string()),\n                                ty: member_ty,\n                                binding: None,\n                                offset: second_offset,\n                            },\n                        ],\n                        span: second_offset * 2,\n                    },\n                }\n            }\n            crate::PredeclaredType::FrexpResult { size, scalar } => {\n                let float_ty = self.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Scalar(scalar),\n                    },\n                    Span::UNDEFINED,\n                );\n\n                let int_ty = self.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Sint,\n                            width: scalar.width,\n                        }),\n                    },\n                    Span::UNDEFINED,\n                );\n\n                let (fract_member_ty, exp_member_ty, second_offset) = if let Some(size) = size {\n                    let vec_float_ty = self.types.insert(\n                        crate::Type {\n                            name: None,\n                            inner: crate::TypeInner::Vector { size, scalar },\n                        },\n                        Span::UNDEFINED,\n                    );\n                    let vec_int_ty = self.types.insert(\n                        crate::Type {\n                            name: None,\n                            inner: crate::TypeInner::Vector {\n                                size,\n                                scalar: crate::Scalar {\n                                    kind: crate::ScalarKind::Sint,\n                                    width: scalar.width,\n                                },\n                            },\n                        },\n                        Span::UNDEFINED,\n                    );\n                    (vec_float_ty, vec_int_ty, size as u32 * scalar.width as u32)\n                } else {\n                    (float_ty, int_ty, scalar.width as u32)\n                };\n\n                crate::Type {\n                    name: Some(name),\n                    inner: crate::TypeInner::Struct {\n                        members: vec![\n                            crate::StructMember {\n                                name: Some(\"fract\".to_string()),\n                                ty: fract_member_ty,\n                                binding: None,\n                                offset: 0,\n                            },\n                            crate::StructMember {\n                                name: Some(\"exp\".to_string()),\n                                ty: exp_member_ty,\n                                binding: None,\n                                offset: second_offset,\n                            },\n                        ],\n                        span: second_offset * 2,\n                    },\n                }\n            }\n        };\n\n        let handle = self.types.insert(ty, Span::UNDEFINED);\n        self.special_types\n            .predeclared_types\n            .insert(special_type, handle);\n        handle\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/error.rs",
    "content": "//! Formatting WGSL front end error messages.\n\nuse crate::common::wgsl::TryToWgsl;\nuse crate::diagnostic_filter::ConflictingDiagnosticRuleError;\nuse crate::error::replace_control_chars;\nuse crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};\nuse crate::{Scalar, SourceLocation, Span};\n\nuse super::parse::directive::enable_extension::{EnableExtension, UnimplementedEnableExtension};\nuse super::parse::directive::language_extension::{\n    LanguageExtension, UnimplementedLanguageExtension,\n};\nuse super::parse::lexer::Token;\n\nuse codespan_reporting::diagnostic::{Diagnostic, Label};\nuse codespan_reporting::files::SimpleFile;\nuse codespan_reporting::term;\nuse thiserror::Error;\n\nuse alloc::{\n    borrow::Cow,\n    boxed::Box,\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::ops::Range;\n\n#[derive(Clone, Debug)]\npub struct ParseError {\n    message: String,\n    // The first span should be the primary span, and the other ones should be complementary.\n    labels: Vec<(Span, Cow<'static, str>)>,\n    notes: Vec<String>,\n}\n\nimpl ParseError {\n    pub fn labels(&self) -> impl ExactSizeIterator<Item = (Span, &str)> + '_ {\n        self.labels\n            .iter()\n            .map(|&(span, ref msg)| (span, msg.as_ref()))\n    }\n\n    pub fn message(&self) -> &str {\n        &self.message\n    }\n\n    fn diagnostic(&self) -> Diagnostic<()> {\n        let diagnostic = Diagnostic::error()\n            .with_message(self.message.to_string())\n            .with_labels(\n                self.labels\n                    .iter()\n                    .filter_map(|label| label.0.to_range().map(|range| (label, range)))\n                    .map(|(label, range)| {\n                        Label::primary((), range).with_message(label.1.to_string())\n                    })\n                    .collect(),\n            )\n            .with_notes(\n                self.notes\n                    .iter()\n                    .map(|note| format!(\"note: {note}\"))\n                    .collect(),\n            );\n        diagnostic\n    }\n\n    /// Emits a summary of the error to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr(&self, source: &str) {\n        self.emit_to_stderr_with_path(source, \"wgsl\")\n    }\n\n    /// Emits a summary of the error to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr_with_path<P>(&self, source: &str, path: P)\n    where\n        P: AsRef<std::path::Path>,\n    {\n        let path = path.as_ref().display().to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);\n                term::emit_to_write_style(&mut writer.lock(), &config, &files, &self.diagnostic())\n                    .expect(\"cannot write error\");\n            } else {\n                let writer = std::io::stderr();\n                term::emit_to_io_write(&mut writer.lock(), &config, &files, &self.diagnostic())\n                    .expect(\"cannot write error\");\n            }\n        }\n    }\n\n    /// Emits a summary of the error to a string.\n    pub fn emit_to_string(&self, source: &str) -> String {\n        self.emit_to_string_with_path(source, \"wgsl\")\n    }\n\n    /// Emits a summary of the error to a string.\n    pub fn emit_to_string_with_path<P>(&self, source: &str, path: P) -> String\n    where\n        P: AsRef<std::path::Path>,\n    {\n        let path = path.as_ref().display().to_string();\n        let files = SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        let mut writer = crate::error::DiagnosticBuffer::new();\n        writer\n            .emit_to_self(&config, &files, &self.diagnostic())\n            .expect(\"cannot write error\");\n        writer.into_string()\n    }\n\n    /// Returns a [`SourceLocation`] for the first label in the error message.\n    pub fn location(&self, source: &str) -> Option<SourceLocation> {\n        self.labels.first().map(|label| label.0.location(source))\n    }\n}\n\nimpl core::fmt::Display for ParseError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{}\", self.message)\n    }\n}\n\nimpl core::error::Error for ParseError {}\n\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum ExpectedToken<'a> {\n    Token(Token<'a>),\n    Identifier,\n    AfterIdentListComma,\n    AfterIdentListArg,\n    /// LHS expression (identifier component_or_swizzle_specifier?, (`lhs_expression`) component_or_swizzle_specifier?, &`lhs_expression`, *`lhs_expression`)\n    LhsExpression,\n    /// Expected: constant, parenthesized expression, identifier\n    PrimaryExpression,\n    /// Expected: assignment, increment/decrement expression\n    Assignment,\n    /// Expected: 'case', 'default', '}'\n    SwitchItem,\n    /// Expected: ',', ')'\n    WorkgroupSizeSeparator,\n    /// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof\n    GlobalItem,\n    /// Access of `var`, `let`, `const`.\n    Variable,\n    /// Access of a function\n    Function,\n    /// The `diagnostic` identifier of the `@diagnostic(…)` attribute.\n    DiagnosticAttribute,\n    /// statement\n    Statement,\n    /// for loop init statement (variable_or_value_statement, variable_updating_statement, func_call_statement)\n    ForInit,\n    /// for loop update statement (variable_updating_statement, func_call_statement)\n    ForUpdate,\n}\n\n#[derive(Clone, Copy, Debug, Error, PartialEq)]\npub enum NumberError {\n    #[error(\"invalid numeric literal format\")]\n    Invalid,\n    #[error(\"numeric literal not representable by target type\")]\n    NotRepresentable,\n}\n\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum InvalidAssignmentType {\n    Other,\n    Swizzle,\n    ImmutableBinding(Span),\n}\n\n#[derive(Clone, Debug)]\npub(crate) enum Error<'a> {\n    Unexpected(Span, ExpectedToken<'a>),\n    UnexpectedComponents(Span),\n    UnexpectedOperationInConstContext(Span),\n    BadNumber(Span, NumberError),\n    BadMatrixScalarKind(Span, Scalar),\n    BadAccessor(Span),\n    BadTexture(Span),\n    BadTypeCast {\n        span: Span,\n        from_type: String,\n        to_type: String,\n    },\n    NotStorageTexture(Span),\n    BadTextureSampleType {\n        span: Span,\n        scalar: Scalar,\n    },\n    BadIncrDecrReferenceType(Span),\n    InvalidResolve(ResolveError),\n    /// A break if appeared outside of a continuing block\n    InvalidBreakIf(Span),\n    InvalidGatherComponent(Span),\n    InvalidConstructorComponentType(Span, i32),\n    InvalidIdentifierUnderscore(Span),\n    ReservedIdentifierPrefix(Span),\n    UnknownAddressSpace(Span),\n    InvalidLocalVariableAddressSpace(Span),\n    UnknownRayFlag(Span),\n    RepeatedAttribute(Span),\n    UnknownAttribute(Span),\n    UnknownBuiltin(Span),\n    UnknownAccess(Span),\n    UnknownIdent(Span, &'a str),\n    UnknownScalarType(Span),\n    UnknownStorageFormat(Span),\n    UnknownConservativeDepth(Span),\n    UnknownEnableExtension(Span, &'a str),\n    UnknownLanguageExtension(Span, &'a str),\n    UnknownDiagnosticRuleName(Span),\n    SizeAttributeTooLow(Span, u32),\n    AlignAttributeTooLow(Span, Alignment),\n    NonPowerOfTwoAlignAttribute(Span),\n    InconsistentBinding(Span),\n    TypeNotConstructible(Span),\n    TypeNotInferable(Span),\n    InitializationTypeMismatch {\n        name: Span,\n        expected: String,\n        got: String,\n    },\n    DeclMissingTypeAndInit(Span),\n    MissingAttribute(&'static str, Span),\n    InvalidAddrOfOperand(Span),\n    InvalidAtomicPointer(Span),\n    InvalidAtomicOperandType(Span),\n    InvalidRayQueryPointer(Span),\n    NotPointer(Span),\n    NotReference(&'static str, Span),\n    InvalidAssignment {\n        span: Span,\n        ty: InvalidAssignmentType,\n    },\n    ReservedKeyword(Span),\n    /// Redefinition of an identifier (used for both module-scope and local redefinitions).\n    Redefinition {\n        /// Span of the identifier in the previous definition.\n        previous: Span,\n\n        /// Span of the identifier in the new definition.\n        current: Span,\n    },\n    /// A declaration refers to itself directly.\n    RecursiveDeclaration {\n        /// The location of the name of the declaration.\n        ident: Span,\n\n        /// The point at which it is used.\n        usage: Span,\n    },\n    /// A declaration refers to itself indirectly, through one or more other\n    /// definitions.\n    CyclicDeclaration {\n        /// The location of the name of some declaration in the cycle.\n        ident: Span,\n\n        /// The edges of the cycle of references.\n        ///\n        /// Each `(decl, reference)` pair indicates that the declaration whose\n        /// name is `decl` has an identifier at `reference` whose definition is\n        /// the next declaration in the cycle. The last pair's `reference` is\n        /// the same identifier as `ident`, above.\n        path: Box<[(Span, Span)]>,\n    },\n    InvalidSwitchSelector {\n        span: Span,\n    },\n    InvalidSwitchCase {\n        span: Span,\n    },\n    SwitchCaseTypeMismatch {\n        span: Span,\n    },\n    CalledEntryPoint(Span),\n    CalledLocalDecl(Span),\n    WrongArgumentCount {\n        span: Span,\n        expected: Range<u32>,\n        found: u32,\n    },\n    /// No overload of this function accepts this many arguments.\n    TooManyArguments {\n        /// The name of the function being called.\n        function: String,\n\n        /// The function name in the call expression.\n        call_span: Span,\n\n        /// The first argument that is unacceptable.\n        arg_span: Span,\n\n        /// Maximum number of arguments accepted by any overload of\n        /// this function.\n        max_arguments: u32,\n    },\n    /// A value passed to a builtin function has a type that is not\n    /// accepted by any overload of the function.\n    WrongArgumentType {\n        /// The name of the function being called.\n        function: String,\n\n        /// The function name in the call expression.\n        call_span: Span,\n\n        /// The first argument whose type is unacceptable.\n        arg_span: Span,\n\n        /// The index of the first argument whose type is unacceptable.\n        arg_index: u32,\n\n        /// That argument's actual type.\n        arg_ty: String,\n\n        /// The set of argument types that would have been accepted for\n        /// this argument, given the prior arguments.\n        allowed: Vec<String>,\n    },\n    /// A value passed to a builtin function has a type that is not\n    /// accepted, given the earlier arguments' types.\n    InconsistentArgumentType {\n        /// The name of the function being called.\n        function: String,\n\n        /// The function name in the call expression.\n        call_span: Span,\n\n        /// The first unacceptable argument.\n        arg_span: Span,\n\n        /// The index of the first unacceptable argument.\n        arg_index: u32,\n\n        /// The actual type of the first unacceptable argument.\n        arg_ty: String,\n\n        /// The prior argument whose type made the `arg_span` argument\n        /// unacceptable.\n        inconsistent_span: Span,\n\n        /// The index of the `inconsistent_span` argument.\n        inconsistent_index: u32,\n\n        /// The type of the `inconsistent_span` argument.\n        inconsistent_ty: String,\n\n        /// The types that would have been accepted instead of the\n        /// first unacceptable argument.\n        allowed: Vec<String>,\n    },\n    FunctionReturnsVoid(Span),\n    FunctionMustUseUnused(Span),\n    FunctionMustUseReturnsVoid(Span, Span),\n    InvalidWorkGroupUniformLoad(Span),\n    Internal(&'static str),\n    ExpectedConstExprConcreteIntegerScalar(Span),\n    ExpectedNonNegative(Span),\n    ExpectedPositiveArrayLength(Span),\n    MissingWorkgroupSize(Span),\n    ConstantEvaluatorError(Box<ConstantEvaluatorError>, Span),\n    AutoConversion(Box<AutoConversionError>),\n    AutoConversionLeafScalar(Box<AutoConversionLeafScalarError>),\n    ConcretizationFailed(Box<ConcretizationFailedError>),\n    ExceededLimitForNestedBraces {\n        span: Span,\n        limit: u8,\n    },\n    PipelineConstantIDValue(Span),\n    NotBool(Span),\n    ConstAssertFailed(Span),\n    DirectiveAfterFirstGlobalDecl {\n        directive_span: Span,\n    },\n    EnableExtensionNotYetImplemented {\n        kind: UnimplementedEnableExtension,\n        span: Span,\n    },\n    EnableExtensionNotEnabled {\n        kind: EnableExtension,\n        span: Span,\n    },\n    EnableExtensionNotSupported {\n        kind: EnableExtension,\n        span: Span,\n    },\n    LanguageExtensionNotYetImplemented {\n        kind: UnimplementedLanguageExtension,\n        span: Span,\n    },\n    DiagnosticInvalidSeverity {\n        severity_control_name_span: Span,\n    },\n    DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError),\n    DiagnosticAttributeNotYetImplementedAtParseSite {\n        site_name_plural: &'static str,\n        spans: Vec<Span>,\n    },\n    DiagnosticAttributeNotSupported {\n        on_what: DiagnosticAttributeNotSupportedPosition,\n        spans: Vec<Span>,\n    },\n    SelectUnexpectedArgumentType {\n        arg_span: Span,\n        arg_type: String,\n    },\n    SelectRejectAndAcceptHaveNoCommonType {\n        reject_span: Span,\n        reject_type: String,\n        accept_span: Span,\n        accept_type: String,\n    },\n    ExpectedGlobalVariable {\n        name_span: Span,\n    },\n    StructMemberTooLarge {\n        member_name_span: Span,\n    },\n    TypeTooLarge {\n        span: Span,\n    },\n    UnderspecifiedCooperativeMatrix,\n    InvalidCooperativeLoadType(Span),\n    UnsupportedCooperativeScalar(Span),\n    UnexpectedIdentForEnumerant(Span),\n    UnexpectedExprForEnumerant(Span),\n    UnusedArgsForTemplate(Vec<Span>),\n    UnexpectedTemplate(Span),\n    MissingTemplateArg {\n        span: Span,\n        description: &'static str,\n    },\n    UnexpectedExprForTypeExpression(Span),\n    MissingIncomingPayload(Span),\n}\n\nimpl From<ConflictingDiagnosticRuleError> for Error<'_> {\n    fn from(value: ConflictingDiagnosticRuleError) -> Self {\n        Self::DiagnosticDuplicateTriggeringRule(value)\n    }\n}\n\n/// Used for diagnostic refinement in [`Error::DiagnosticAttributeNotSupported`].\n#[derive(Clone, Copy, Debug)]\npub(crate) enum DiagnosticAttributeNotSupportedPosition {\n    SemicolonInModulePosition,\n    Other { display_plural: &'static str },\n}\n\nimpl From<&'static str> for DiagnosticAttributeNotSupportedPosition {\n    fn from(display_plural: &'static str) -> Self {\n        Self::Other { display_plural }\n    }\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct AutoConversionError {\n    pub dest_span: Span,\n    pub dest_type: String,\n    pub source_span: Span,\n    pub source_type: String,\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct AutoConversionLeafScalarError {\n    pub dest_span: Span,\n    pub dest_scalar: String,\n    pub source_span: Span,\n    pub source_type: String,\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct ConcretizationFailedError {\n    pub expr_span: Span,\n    pub expr_type: String,\n    pub concretization_preferences: Vec<(String, ConstantEvaluatorError)>,\n}\n\nimpl<'a> Error<'a> {\n    #[cold]\n    #[inline(never)]\n    pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {\n        match *self {\n            Error::Unexpected(unexpected_span, expected) => {\n                let expected_str = match expected {\n                    ExpectedToken::Token(token) => match token {\n                        Token::Separator(c) => format!(\"`{c}`\"),\n                        Token::Paren(c) => format!(\"`{c}`\"),\n                        Token::Attribute => \"@\".to_string(),\n                        Token::Number(_) => \"number\".to_string(),\n                        Token::Word(s) => s.to_string(),\n                        Token::Operation(c) => format!(\"operation (`{c}`)\"),\n                        Token::LogicalOperation(c) => format!(\"logical operation (`{c}`)\"),\n                        Token::ShiftOperation(c) => format!(\"bitshift (`{c}{c}`)\"),\n                        Token::AssignmentOperation(c) if c == '<' || c == '>' => {\n                            format!(\"bitshift (`{c}{c}=`)\")\n                        }\n                        Token::AssignmentOperation(c) => format!(\"operation (`{c}=`)\"),\n                        Token::IncrementOperation => \"increment operation\".to_string(),\n                        Token::DecrementOperation => \"decrement operation\".to_string(),\n                        Token::Arrow => \"->\".to_string(),\n                        Token::TemplateArgsStart => \"template args start\".to_string(),\n                        Token::TemplateArgsEnd => \"template args end\".to_string(),\n                        Token::Unknown(c) => format!(\"unknown (`{c}`)\"),\n                        Token::Trivia => \"trivia\".to_string(),\n                        Token::DocComment(s) => format!(\"doc comment ('{s}')\"),\n                        Token::ModuleDocComment(s) => format!(\"module doc comment ('{s}')\"),\n                        Token::End => \"end\".to_string(),\n                    },\n                    ExpectedToken::Identifier => \"identifier\".to_string(),\n                    ExpectedToken::LhsExpression => \"LHS expression (identifier component_or_swizzle_specifier?, (`lhs_expression`) component_or_swizzle_specifier?, &`lhs_expression`, *`lhs_expression`)\".to_string(),\n                    ExpectedToken::PrimaryExpression => \"expression\".to_string(),\n                    ExpectedToken::Assignment => \"assignment or increment/decrement\".to_string(),\n                    ExpectedToken::SwitchItem => concat!(\n                        \"switch item (`case` or `default`) or a closing curly bracket \",\n                        \"to signify the end of the switch statement (`}`)\"\n                    )\n                    .to_string(),\n                    ExpectedToken::WorkgroupSizeSeparator => {\n                        \"workgroup size separator (`,`) or a closing parenthesis\".to_string()\n                    }\n                    ExpectedToken::GlobalItem => concat!(\n                        \"global item (`struct`, `const`, `var`, `alias`, \",\n                        \"`fn`, `diagnostic`, `enable`, `requires`, `;`) \",\n                        \"or the end of the file\"\n                    )\n                    .to_string(),\n                    ExpectedToken::Variable => \"variable access\".to_string(),\n                    ExpectedToken::Function => \"function name\".to_string(),\n                    ExpectedToken::AfterIdentListArg => {\n                        \"next argument, trailing comma, or end of list (`,` or `;`)\".to_string()\n                    }\n                    ExpectedToken::AfterIdentListComma => {\n                        \"next argument or end of list (`;`)\".to_string()\n                    }\n                    ExpectedToken::DiagnosticAttribute => {\n                        \"the `diagnostic` attribute identifier\".to_string()\n                    }\n                    ExpectedToken::Statement => \"statement\".to_string(),\n                    ExpectedToken::ForInit => \"for loop initializer statement (`var`/`let`/`const` declaration, assignment, `i++`/`i--` statement, function call)\".to_string(),\n                    ExpectedToken::ForUpdate => \"for loop update statement (assignment, `i++`/`i--` statement, function call)\".to_string(),\n                };\n                ParseError {\n                    message: format!(\n                        \"expected {}, found {:?}\",\n                        expected_str, &source[unexpected_span],\n                    ),\n                    labels: vec![(unexpected_span, format!(\"expected {expected_str}\").into())],\n                    notes: vec![],\n                }\n            }\n            Error::UnexpectedComponents(bad_span) => ParseError {\n                message: \"unexpected components\".to_string(),\n                labels: vec![(bad_span, \"unexpected components\".into())],\n                notes: vec![],\n            },\n            Error::UnexpectedOperationInConstContext(span) => ParseError {\n                message: \"this operation is not supported in a const context\".to_string(),\n                labels: vec![(span, \"operation not supported here\".into())],\n                notes: vec![],\n            },\n            Error::BadNumber(bad_span, ref err) => ParseError {\n                message: format!(\"{}: `{}`\", err, &source[bad_span],),\n                labels: vec![(bad_span, err.to_string().into())],\n                notes: vec![],\n            },\n            Error::BadMatrixScalarKind(span, scalar) => ParseError {\n                message: format!(\n                    \"matrix scalar type must be floating-point, but found `{}`\",\n                    scalar.to_wgsl_for_diagnostics()\n                ),\n                labels: vec![(span, \"must be floating-point (e.g. `f32`)\".into())],\n                notes: vec![],\n            },\n            Error::BadAccessor(accessor_span) => ParseError {\n                message: format!(\"invalid field accessor `{}`\", &source[accessor_span],),\n                labels: vec![(accessor_span, \"invalid accessor\".into())],\n                notes: vec![],\n            },\n            Error::UnknownIdent(ident_span, ident) => ParseError {\n                message: format!(\"no definition in scope for identifier: `{ident}`\"),\n                labels: vec![(ident_span, \"unknown identifier\".into())],\n                notes: vec![],\n            },\n            Error::UnknownScalarType(bad_span) => ParseError {\n                message: format!(\"unknown scalar type: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown scalar type\".into())],\n                notes: vec![\"Valid scalar types are f32, f64, i32, u32, bool\".into()],\n            },\n            Error::NotStorageTexture(bad_span) => ParseError {\n                message: \"textureStore can only be applied to storage textures\".to_string(),\n                labels: vec![(bad_span, \"not a storage texture\".into())],\n                notes: vec![],\n            },\n            Error::BadTextureSampleType { span, scalar } => ParseError {\n                message: format!(\n                    \"texture sample type must be one of f32, i32 or u32, but found {}\",\n                    scalar.to_wgsl_for_diagnostics()\n                ),\n                labels: vec![(span, \"must be one of f32, i32 or u32\".into())],\n                notes: vec![],\n            },\n            Error::BadIncrDecrReferenceType(span) => ParseError {\n                message: concat!(\n                    \"increment/decrement operation requires \",\n                    \"reference type to be one of i32 or u32\"\n                )\n                .to_string(),\n                labels: vec![(span, \"must be a reference type of i32 or u32\".into())],\n                notes: vec![],\n            },\n            Error::BadTexture(bad_span) => ParseError {\n                message: format!(\n                    \"expected an image, but found `{}` which is not an image\",\n                    &source[bad_span]\n                ),\n                labels: vec![(bad_span, \"not an image\".into())],\n                notes: vec![],\n            },\n            Error::BadTypeCast {\n                span,\n                ref from_type,\n                ref to_type,\n            } => {\n                let msg = format!(\"cannot cast a {from_type} to a {to_type}\");\n                ParseError {\n                    message: msg.clone(),\n                    labels: vec![(span, msg.into())],\n                    notes: vec![],\n                }\n            }\n            Error::InvalidResolve(ref resolve_error) => ParseError {\n                message: resolve_error.to_string(),\n                labels: vec![],\n                notes: vec![],\n            },\n            Error::InvalidBreakIf(bad_span) => ParseError {\n                message: \"A break if is only allowed in a continuing block\".to_string(),\n                labels: vec![(bad_span, \"not in a continuing block\".into())],\n                notes: vec![],\n            },\n            Error::InvalidGatherComponent(bad_span) => ParseError {\n                message: format!(\n                    \"textureGather component `{}` doesn't exist, must be 0, 1, 2, or 3\",\n                    &source[bad_span]\n                ),\n                labels: vec![(bad_span, \"invalid component\".into())],\n                notes: vec![],\n            },\n            Error::InvalidConstructorComponentType(bad_span, component) => ParseError {\n                message: format!(\"invalid type for constructor component at index [{component}]\"),\n                labels: vec![(bad_span, \"invalid component type\".into())],\n                notes: vec![],\n            },\n            Error::InvalidIdentifierUnderscore(bad_span) => ParseError {\n                message: \"Identifier can't be `_`\".to_string(),\n                labels: vec![(bad_span, \"invalid identifier\".into())],\n                notes: vec![\n                    \"Use phony assignment instead (`_ =` notice the absence of `let` or `var`)\"\n                        .to_string(),\n                ],\n            },\n            Error::ReservedIdentifierPrefix(bad_span) => ParseError {\n                message: format!(\n                    \"Identifier starts with a reserved prefix: `{}`\",\n                    &source[bad_span]\n                ),\n                labels: vec![(bad_span, \"invalid identifier\".into())],\n                notes: vec![],\n            },\n            Error::UnknownAddressSpace(bad_span) => ParseError {\n                message: format!(\"unknown address space: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown address space\".into())],\n                notes: vec![],\n            },\n            Error::InvalidLocalVariableAddressSpace(bad_span) => ParseError {\n                message: format!(\"invalid address space for local variable: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"local variables can only use 'function' address space\".into())],\n                notes: vec![],\n            },\n            Error::UnknownRayFlag(bad_span) => ParseError {\n                message: format!(\"unknown ray flag: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown ray flag\".into())],\n                notes: vec![],\n            },\n            Error::RepeatedAttribute(bad_span) => ParseError {\n                message: format!(\"repeated attribute: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"repeated attribute\".into())],\n                notes: vec![],\n            },\n            Error::UnknownAttribute(bad_span) => ParseError {\n                message: format!(\"unknown attribute: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown attribute\".into())],\n                notes: vec![],\n            },\n            Error::UnknownBuiltin(bad_span) => ParseError {\n                message: format!(\"unknown builtin: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown builtin\".into())],\n                notes: vec![],\n            },\n            Error::UnknownAccess(bad_span) => ParseError {\n                message: format!(\"unknown access: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown access\".into())],\n                notes: vec![],\n            },\n            Error::UnknownStorageFormat(bad_span) => ParseError {\n                message: format!(\"unknown storage format: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown storage format\".into())],\n                notes: vec![],\n            },\n            Error::UnknownConservativeDepth(bad_span) => ParseError {\n                message: format!(\"unknown conservative depth: `{}`\", &source[bad_span]),\n                labels: vec![(bad_span, \"unknown conservative depth\".into())],\n                notes: vec![],\n            },\n            Error::UnknownEnableExtension(span, word) => ParseError {\n                message: format!(\"unknown enable-extension `{word}`\"),\n                labels: vec![(span, \"\".into())],\n                notes: vec![\n                    \"See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>.\"\n                        .into(),\n                ],\n            },\n            Error::UnknownLanguageExtension(span, name) => ParseError {\n                message: format!(\"unknown language extension `{name}`\"),\n                labels: vec![(span, \"\".into())],\n                notes: vec![concat!(\n                    \"See available extensions at \",\n                    \"<https://www.w3.org/TR/WGSL/#language-extensions-sec>.\"\n                )\n                .into()],\n            },\n            Error::UnknownDiagnosticRuleName(span) => ParseError {\n                message: format!(\"unknown `diagnostic(…)` rule name `{}`\", &source[span]),\n                labels: vec![(span, \"not a valid diagnostic rule name\".into())],\n                notes: vec![concat!(\n                    \"See available trigger rules at \",\n                    \"<https://www.w3.org/TR/WGSL/#filterable-triggering-rules>.\"\n                )\n                .into()],\n            },\n            Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {\n                message: format!(\"struct member size must be at least {min_size}\"),\n                labels: vec![(bad_span, format!(\"must be at least {min_size}\").into())],\n                notes: vec![],\n            },\n            Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {\n                message: format!(\"struct member alignment must be at least {min_align}\"),\n                labels: vec![(bad_span, format!(\"must be at least {min_align}\").into())],\n                notes: vec![],\n            },\n            Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {\n                message: \"struct member alignment must be a power of 2\".to_string(),\n                labels: vec![(bad_span, \"must be a power of 2\".into())],\n                notes: vec![],\n            },\n            Error::InconsistentBinding(span) => ParseError {\n                message: \"input/output binding is not consistent\".to_string(),\n                labels: vec![(span, \"input/output binding is not consistent\".into())],\n                notes: vec![],\n            },\n            Error::TypeNotConstructible(span) => ParseError {\n                message: format!(\"type `{}` is not constructible\", &source[span]),\n                labels: vec![(span, \"type is not constructible\".into())],\n                notes: vec![],\n            },\n            Error::TypeNotInferable(span) => ParseError {\n                message: \"type can't be inferred\".to_string(),\n                labels: vec![(span, \"type can't be inferred\".into())],\n                notes: vec![],\n            },\n            Error::InitializationTypeMismatch {\n                name,\n                ref expected,\n                ref got,\n            } => ParseError {\n                message: format!(\n                    \"the type of `{}` is expected to be `{}`, but got `{}`\",\n                    &source[name], expected, got,\n                ),\n                labels: vec![(name, format!(\"definition of `{}`\", &source[name]).into())],\n                notes: vec![],\n            },\n            Error::DeclMissingTypeAndInit(name_span) => ParseError {\n                message: format!(\n                    \"declaration of `{}` needs a type specifier or initializer\",\n                    &source[name_span]\n                ),\n                labels: vec![(name_span, \"needs a type specifier or initializer\".into())],\n                notes: vec![],\n            },\n            Error::MissingAttribute(name, name_span) => ParseError {\n                message: format!(\n                    \"variable `{}` needs a '{}' attribute\",\n                    &source[name_span], name\n                ),\n                labels: vec![(\n                    name_span,\n                    format!(\"definition of `{}`\", &source[name_span]).into(),\n                )],\n                notes: vec![],\n            },\n            Error::InvalidAddrOfOperand(span) => ParseError {\n                message: \"cannot take the address of a vector component\".to_string(),\n                labels: vec![(span, \"invalid operand for address-of\".into())],\n                notes: vec![],\n            },\n            Error::InvalidAtomicPointer(span) => ParseError {\n                message: \"atomic operation is done on a pointer to a non-atomic\".to_string(),\n                labels: vec![(span, \"atomic pointer is invalid\".into())],\n                notes: vec![],\n            },\n            Error::InvalidAtomicOperandType(span) => ParseError {\n                message: \"atomic operand type is inconsistent with the operation\".to_string(),\n                labels: vec![(span, \"atomic operand type is invalid\".into())],\n                notes: vec![],\n            },\n            Error::InvalidRayQueryPointer(span) => ParseError {\n                message: \"ray query operation is done on a pointer to a non-ray-query\".to_string(),\n                labels: vec![(span, \"ray query pointer is invalid\".into())],\n                notes: vec![],\n            },\n            Error::NotPointer(span) => ParseError {\n                message: \"the operand of the `*` operator must be a pointer\".to_string(),\n                labels: vec![(span, \"expression is not a pointer\".into())],\n                notes: vec![],\n            },\n            Error::NotReference(what, span) => ParseError {\n                message: format!(\"{what} must be a reference\"),\n                labels: vec![(span, \"expression is not a reference\".into())],\n                notes: vec![],\n            },\n            Error::InvalidAssignment { span, ty } => {\n                let (extra_label, notes) = match ty {\n                    InvalidAssignmentType::Swizzle => (\n                        None,\n                        vec![\n                            \"WGSL does not support assignments to swizzles\".into(),\n                            \"consider assigning each component individually\".into(),\n                        ],\n                    ),\n                    InvalidAssignmentType::ImmutableBinding(binding_span) => (\n                        Some((binding_span, \"this is an immutable binding\".into())),\n                        vec![format!(\n                            \"consider declaring `{}` with `var` instead of `let`\",\n                            &source[binding_span]\n                        )],\n                    ),\n                    InvalidAssignmentType::Other => (None, vec![]),\n                };\n\n                ParseError {\n                    message: \"invalid left-hand side of assignment\".into(),\n                    labels: core::iter::once((span, \"cannot assign to this expression\".into()))\n                        .chain(extra_label)\n                        .collect(),\n                    notes,\n                }\n            }\n            Error::ReservedKeyword(name_span) => ParseError {\n                message: format!(\"name `{}` is a reserved keyword\", &source[name_span]),\n                labels: vec![(\n                    name_span,\n                    format!(\"definition of `{}`\", &source[name_span]).into(),\n                )],\n                notes: vec![],\n            },\n            Error::Redefinition { previous, current } => ParseError {\n                message: format!(\"redefinition of `{}`\", &source[current]),\n                labels: vec![\n                    (\n                        current,\n                        format!(\"redefinition of `{}`\", &source[current]).into(),\n                    ),\n                    (\n                        previous,\n                        format!(\"previous definition of `{}`\", &source[previous]).into(),\n                    ),\n                ],\n                notes: vec![],\n            },\n            Error::RecursiveDeclaration { ident, usage } => ParseError {\n                message: format!(\"declaration of `{}` is recursive\", &source[ident]),\n                labels: vec![(ident, \"\".into()), (usage, \"uses itself here\".into())],\n                notes: vec![],\n            },\n            Error::CyclicDeclaration { ident, ref path } => ParseError {\n                message: format!(\"declaration of `{}` is cyclic\", &source[ident]),\n                labels: path\n                    .iter()\n                    .enumerate()\n                    .flat_map(|(i, &(ident, usage))| {\n                        [\n                            (ident, \"\".into()),\n                            (\n                                usage,\n                                if i == path.len() - 1 {\n                                    \"ending the cycle\".into()\n                                } else {\n                                    format!(\"uses `{}`\", &source[ident]).into()\n                                },\n                            ),\n                        ]\n                    })\n                    .collect(),\n                notes: vec![],\n            },\n            Error::InvalidSwitchSelector { span } => ParseError {\n                message: \"invalid `switch` selector\".to_string(),\n                labels: vec![(\n                    span,\n                    \"`switch` selector must be a scalar integer\"\n                    .into(),\n                )],\n                notes: vec![],\n            },\n            Error::InvalidSwitchCase { span } => ParseError {\n                message: \"invalid `switch` case selector value\".to_string(),\n                labels: vec![(\n                    span,\n                    \"`switch` case selector must be a scalar integer const expression\"\n                    .into(),\n                )],\n                notes: vec![],\n            },\n            Error::SwitchCaseTypeMismatch { span } => ParseError {\n                message: \"invalid `switch` case selector value\".to_string(),\n                labels: vec![(\n                    span,\n                    \"`switch` case selector must have the same type as the `switch` selector expression\"\n                    .into(),\n                )],\n                notes: vec![],\n            },\n            Error::CalledEntryPoint(span) => ParseError {\n                message: \"entry point cannot be called\".to_string(),\n                labels: vec![(span, \"entry point cannot be called\".into())],\n                notes: vec![],\n            },\n            Error::CalledLocalDecl(span) => ParseError {\n                message: \"local declaration cannot be called\".to_string(),\n                labels: vec![(span, \"local declaration cannot be called\".into())],\n                notes: vec![],\n            },\n            Error::WrongArgumentCount {\n                span,\n                ref expected,\n                found,\n            } => ParseError {\n                message: format!(\n                    \"wrong number of arguments: expected {}, found {}\",\n                    if expected.len() < 2 {\n                        format!(\"{}\", expected.start)\n                    } else {\n                        format!(\"{}..{}\", expected.start, expected.end)\n                    },\n                    found\n                ),\n                labels: vec![(span, \"wrong number of arguments\".into())],\n                notes: vec![],\n            },\n            Error::TooManyArguments {\n                ref function,\n                call_span,\n                arg_span,\n                max_arguments,\n            } => ParseError {\n                message: format!(\"too many arguments passed to `{function}`\"),\n                labels: vec![\n                    (call_span, \"\".into()),\n                    (arg_span, format!(\"unexpected argument #{}\", max_arguments + 1).into())\n                ],\n                notes: vec![\n                    format!(\"The `{function}` function accepts at most {max_arguments} argument(s)\")\n                ],\n            },\n            Error::WrongArgumentType {\n                ref function,\n                call_span,\n                arg_span,\n                arg_index,\n                ref arg_ty,\n                ref allowed,\n            } => {\n                let message = format!(\n                    \"wrong type passed as argument #{} to `{function}`\",\n                    arg_index + 1,\n                );\n                let labels = vec![\n                    (call_span, \"\".into()),\n                    (arg_span, format!(\"argument #{} has type `{arg_ty}`\", arg_index + 1).into())\n                ];\n\n                let mut notes = vec![];\n                notes.push(format!(\"`{function}` accepts the following types for argument #{}:\", arg_index + 1));\n                notes.extend(allowed.iter().map(|ty| format!(\"allowed type: {ty}\")));\n\n                ParseError { message, labels, notes }\n            },\n            Error::InconsistentArgumentType {\n                ref function,\n                call_span,\n                arg_span,\n                arg_index,\n                ref arg_ty,\n                inconsistent_span,\n                inconsistent_index,\n                ref inconsistent_ty,\n                ref allowed\n            } => {\n                let message = format!(\n                    \"inconsistent type passed as argument #{} to `{function}`\",\n                    arg_index + 1,\n                );\n                let labels = vec![\n                    (call_span, \"\".into()),\n                    (arg_span, format!(\"argument #{} has type {arg_ty}\", arg_index + 1).into()),\n                    (inconsistent_span, format!(\n                        \"this argument has type {inconsistent_ty}, which constrains subsequent arguments\"\n                    ).into()),\n                ];\n                let mut notes = vec![\n                    format!(\"Because argument #{} has type {inconsistent_ty}, only the following types\", inconsistent_index + 1),\n                    format!(\"(or types that automatically convert to them) are accepted for argument #{}:\", arg_index + 1),\n                ];\n                notes.extend(allowed.iter().map(|ty| format!(\"allowed type: {ty}\")));\n\n                ParseError { message, labels, notes }\n            }\n            Error::FunctionReturnsVoid(span) => ParseError {\n                message: \"function does not return any value\".to_string(),\n                labels: vec![(span, \"\".into())],\n                notes: vec![\n                    \"perhaps you meant to call the function in a separate statement?\".into(),\n                ],\n            },\n            Error::FunctionMustUseUnused(call) => ParseError {\n                message: \"unused return value from function annotated with @must_use\".into(),\n                labels: vec![(call, \"\".into())],\n                notes: vec![\n                    format!(\n                        \"function '{}' is declared with `@must_use` attribute\",\n                        &source[call],\n                    ),\n                    \"use a phony assignment or declare a value using the function call as the initializer\".into(),\n                ],\n            },\n            Error::FunctionMustUseReturnsVoid(attr, signature) => ParseError {\n                message: \"function annotated with @must_use but does not return any value\".into(),\n                labels: vec![\n                    (attr, \"\".into()),\n                    (signature, \"\".into()),\n                ],\n                notes: vec![\n                    \"declare a return type or remove the attribute\".into(),\n                ],\n            },\n            Error::InvalidWorkGroupUniformLoad(span) => ParseError {\n                message: \"incorrect type passed to workgroupUniformLoad\".into(),\n                labels: vec![(span, \"\".into())],\n                notes: vec![\"passed type must be a workgroup pointer\".into()],\n            },\n            Error::Internal(message) => ParseError {\n                message: \"internal WGSL front end error\".to_string(),\n                labels: vec![],\n                notes: vec![message.into()],\n            },\n            Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError {\n                message: concat!(\n                    \"must be a const-expression that \",\n                    \"resolves to a concrete integer scalar (`u32` or `i32`)\"\n                )\n                .to_string(),\n                labels: vec![(span, \"must resolve to `u32` or `i32`\".into())],\n                notes: vec![],\n            },\n            Error::ExpectedNonNegative(span) => ParseError {\n                message: \"must be non-negative (>= 0)\".to_string(),\n                labels: vec![(span, \"must be non-negative\".into())],\n                notes: vec![],\n            },\n            Error::ExpectedPositiveArrayLength(span) => ParseError {\n                message: \"array element count must be positive (> 0)\".to_string(),\n                labels: vec![(span, \"must be positive\".into())],\n                notes: vec![],\n            },\n            Error::ConstantEvaluatorError(ref e, span) => ParseError {\n                message: e.to_string(),\n                labels: vec![(span, \"see msg\".into())],\n                notes: vec![],\n            },\n            Error::MissingWorkgroupSize(span) => ParseError {\n                message: \"workgroup size is missing on compute shader entry point\".to_string(),\n                labels: vec![(\n                    span,\n                    \"must be paired with a `@workgroup_size` attribute\".into(),\n                )],\n                notes: vec![],\n            },\n            Error::AutoConversion(ref error) => {\n                // destructuring ensures all fields are handled\n                let AutoConversionError {\n                    dest_span,\n                    ref dest_type,\n                    source_span,\n                    ref source_type,\n                } = **error;\n                ParseError {\n                    message: format!(\n                        \"automatic conversions cannot convert `{source_type}` to `{dest_type}`\"\n                    ),\n                    labels: vec![\n                        (\n                            dest_span,\n                            format!(\"a value of type {dest_type} is required here\").into(),\n                        ),\n                        (\n                            source_span,\n                            format!(\"this expression has type {source_type}\").into(),\n                        ),\n                    ],\n                    notes: vec![],\n                }\n            }\n            Error::AutoConversionLeafScalar(ref error) => {\n                let AutoConversionLeafScalarError {\n                    dest_span,\n                    ref dest_scalar,\n                    source_span,\n                    ref source_type,\n                } = **error;\n                ParseError {\n                    message: format!(\n                        \"automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`\"\n                    ),\n                    labels: vec![\n                        (\n                            dest_span,\n                            format!(\n                                \"a value with elements of type {dest_scalar} is required here\"\n                            )\n                            .into(),\n                        ),\n                        (\n                            source_span,\n                            format!(\"this expression has type {source_type}\").into(),\n                        ),\n                    ],\n                    notes: vec![],\n                }\n            }\n            Error::ConcretizationFailed(ref error) => {\n                let ConcretizationFailedError {\n                    expr_span,\n                    ref expr_type,\n                    ref concretization_preferences,\n                } = **error;\n                ParseError {\n                    message: \"failed to convert expression to a concrete type\".to_string(),\n                    labels: vec![(\n                        expr_span,\n                        format!(\"this expression has type {expr_type}\").into(),\n                    )],\n                    notes: concretization_preferences\n                        .iter()\n                        .map(|&(ref scalar, ref err)|\n                            format!(\"the expression couldn't be converted to have {scalar} scalar type: {err}\")\n                        )\n                        .collect(),\n                }\n            }\n            Error::ExceededLimitForNestedBraces { span, limit } => ParseError {\n                message: \"brace nesting limit reached\".into(),\n                labels: vec![(span, \"limit reached at this brace\".into())],\n                notes: vec![format!(\"nesting limit is currently set to {limit}\")],\n            },\n            Error::PipelineConstantIDValue(span) => ParseError {\n                message: \"pipeline constant ID must be between 0 and 65535 inclusive\".to_string(),\n                labels: vec![(span, \"must be between 0 and 65535 inclusive\".into())],\n                notes: vec![],\n            },\n            Error::NotBool(span) => ParseError {\n                message: \"must be a const-expression that resolves to a `bool`\".to_string(),\n                labels: vec![(span, \"must resolve to `bool`\".into())],\n                notes: vec![],\n            },\n            Error::ConstAssertFailed(span) => ParseError {\n                message: \"`const_assert` failure\".to_string(),\n                labels: vec![(span, \"evaluates to `false`\".into())],\n                notes: vec![],\n            },\n            Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {\n                message: \"expected global declaration, but found a global directive\".into(),\n                labels: vec![(\n                    directive_span,\n                    \"written after first global declaration\".into(),\n                )],\n                notes: vec![concat!(\n                    \"global directives are only allowed before global declarations; \",\n                    \"maybe hoist this closer to the top of the shader module?\"\n                )\n                .into()],\n            },\n            Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {\n                message: format!(\n                    \"the `{}` enable-extension is not yet supported\",\n                    EnableExtension::Unimplemented(kind).to_ident()\n                ),\n                labels: vec![(\n                    span,\n                    concat!(\n                        \"this enable-extension specifies standard functionality \",\n                        \"which is not yet implemented in Naga\"\n                    )\n                    .into(),\n                )],\n                notes: vec![format!(\n                    concat!(\n                        \"Let Naga maintainers know that you ran into this at \",\n                        \"<https://github.com/gfx-rs/wgpu/issues/{}>, \",\n                        \"so they can prioritize it!\"\n                    ),\n                    kind.tracking_issue_num()\n                )],\n            },\n            Error::EnableExtensionNotEnabled { kind, span } => ParseError {\n                message: format!(\"the `{}` enable extension is not enabled\", kind.to_ident()),\n                labels: vec![(\n                    span,\n                    format!(\n                        concat!(\n                            \"the `{}` \\\"Enable Extension\\\" is needed for this functionality, \",\n                            \"but it is not currently enabled.\"\n                        ),\n                        kind.to_ident()\n                    )\n                    .into(),\n                )],\n                notes: if let EnableExtension::Unimplemented(kind) = kind {\n                    vec![format!(\n                        concat!(\n                            \"This \\\"Enable Extension\\\" is not yet implemented. \",\n                            \"Let Naga maintainers know that you ran into this at \",\n                            \"<https://github.com/gfx-rs/wgpu/issues/{}>, \",\n                            \"so they can prioritize it!\"\n                        ),\n                        kind.tracking_issue_num()\n                    )]\n                } else {\n                    vec![\n                        format!(\n                            \"You can enable this extension by adding `enable {};` at the top of the shader, before any other items.\",\n                            kind.to_ident()\n                        ),\n                    ]\n                },\n            },\n            Error::EnableExtensionNotSupported { kind, span } => ParseError {\n                message: format!(\n                    \"the `{}` extension is not supported in the current environment\",\n                    kind.to_ident()\n                ),\n                labels: vec![(\n                    span,\n                    \"unsupported enable-extension\".into(),\n                )],\n                notes: vec![],\n            },\n            Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {\n                message: format!(\n                    \"the `{}` language extension is not yet supported\",\n                    LanguageExtension::Unimplemented(kind).to_ident()\n                ),\n                labels: vec![(span, \"\".into())],\n                notes: vec![format!(\n                    concat!(\n                        \"Let Naga maintainers know that you ran into this at \",\n                        \"<https://github.com/gfx-rs/wgpu/issues/{}>, \",\n                        \"so they can prioritize it!\"\n                    ),\n                    kind.tracking_issue_num()\n                )],\n            },\n            Error::DiagnosticInvalidSeverity {\n                severity_control_name_span,\n            } => ParseError {\n                message: \"invalid `diagnostic(…)` severity\".into(),\n                labels: vec![(\n                    severity_control_name_span,\n                    \"not a valid severity level\".into(),\n                )],\n                notes: vec![concat!(\n                    \"See available severities at \",\n                    \"<https://www.w3.org/TR/WGSL/#diagnostic-severity>.\"\n                )\n                .into()],\n            },\n            Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError {\n                triggering_rule_spans,\n            }) => {\n                let [first_span, second_span] = triggering_rule_spans;\n                ParseError {\n                    message: \"found conflicting `diagnostic(…)` rule(s)\".into(),\n                    labels: vec![\n                        (first_span, \"first rule\".into()),\n                        (second_span, \"second rule\".into()),\n                    ],\n                    notes: vec![\n                        concat!(\n                            \"Multiple `diagnostic(…)` rules with the same rule name \",\n                            \"conflict unless they are directives and the severity is the same.\",\n                        )\n                        .into(),\n                        \"You should delete the rule you don't want.\".into(),\n                    ],\n                }\n            }\n            Error::DiagnosticAttributeNotYetImplementedAtParseSite {\n                site_name_plural,\n                ref spans,\n            } => ParseError {\n                message: \"`@diagnostic(…)` attribute(s) not yet implemented\".into(),\n                labels: {\n                    let mut spans = spans.iter().cloned();\n                    let first = spans\n                        .next()\n                        .map(|span| {\n                            (\n                                span,\n                                format!(\"can't use this on {site_name_plural} (yet)\").into(),\n                            )\n                        })\n                        .expect(\"internal error: diag. attr. rejection on empty map\");\n                    core::iter::once(first)\n                        .chain(spans.map(|span| (span, \"\".into())))\n                        .collect()\n                },\n                notes: vec![format!(concat!(\n                    \"Let Naga maintainers know that you ran into this at \",\n                    \"<https://github.com/gfx-rs/wgpu/issues/5320>, \",\n                    \"so they can prioritize it!\"\n                ))],\n            },\n            Error::DiagnosticAttributeNotSupported { on_what, ref spans } => {\n                // In this case the user may have intended to create a global diagnostic filter directive,\n                // so display a note to them suggesting the correct syntax.\n                let intended_diagnostic_directive = match on_what {\n                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => true,\n                    DiagnosticAttributeNotSupportedPosition::Other { .. } => false,\n                };\n                let on_what_plural = match on_what {\n                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => {\n                        \"semicolons\"\n                    }\n                    DiagnosticAttributeNotSupportedPosition::Other { display_plural } => {\n                        display_plural\n                    }\n                };\n                ParseError {\n                    message: format!(\n                        \"`@diagnostic(…)` attribute(s) on {on_what_plural} are not supported\",\n                    ),\n                    labels: spans\n                        .iter()\n                        .cloned()\n                        .map(|span| (span, \"\".into()))\n                        .collect(),\n                    notes: vec![\n                        concat!(\n                            \"`@diagnostic(…)` attributes are only permitted on `fn`s, \",\n                            \"some statements, and `switch`/`loop` bodies.\"\n                        )\n                        .into(),\n                        {\n                            if intended_diagnostic_directive {\n                                concat!(\n                                    \"If you meant to declare a diagnostic filter that \",\n                                    \"applies to the entire module, move this line to \",\n                                    \"the top of the file and remove the `@` symbol.\"\n                                )\n                                .into()\n                            } else {\n                                concat!(\n                                    \"These attributes are well-formed, \",\n                                    \"you likely just need to move them.\"\n                                )\n                                .into()\n                            }\n                        },\n                    ],\n                }\n            }\n            Error::SelectUnexpectedArgumentType { arg_span, ref arg_type } => ParseError {\n                message: \"unexpected argument type for `select` call\".into(),\n                labels: vec![(arg_span, format!(\"this value of type {arg_type}\").into())],\n                notes: vec![\"expected a scalar or a `vecN` of scalars\".into()],\n            },\n            Error::SelectRejectAndAcceptHaveNoCommonType {\n                reject_span,\n                ref reject_type,\n                accept_span,\n                ref accept_type,\n            } => ParseError {\n                message: \"type mismatch for reject and accept values in `select` call\".into(),\n                labels: vec![\n                    (reject_span, format!(\"reject value of type {reject_type}\").into()),\n                    (accept_span, format!(\"accept value of type {accept_type}\").into()),\n                ],\n                notes: vec![],\n            },\n            Error::ExpectedGlobalVariable { name_span } => ParseError {\n                message: \"expected global variable\".to_string(),\n                labels: vec![(name_span, \"variable used here\".into())],\n                notes: vec![],\n            },\n            Error::StructMemberTooLarge { member_name_span } => ParseError {\n                message: \"struct member is too large\".into(),\n                labels: vec![(member_name_span, \"this member exceeds the maximum size\".into())],\n                notes: vec![format!(\n                    \"the maximum size is {} bytes\",\n                    crate::valid::MAX_TYPE_SIZE\n                )],\n            },\n            Error::TypeTooLarge { span } => ParseError {\n                message: \"type is too large\".into(),\n                labels: vec![(span, \"this type exceeds the maximum size\".into())],\n                notes: vec![format!(\n                    \"the maximum size is {} bytes\",\n                    crate::valid::MAX_TYPE_SIZE\n                )],\n            },\n            Error::UnderspecifiedCooperativeMatrix => ParseError {\n                message: \"cooperative matrix constructor is underspecified\".into(),\n                labels: vec![],\n                notes: vec![format!(\"must be F32\")],\n            },\n            Error::InvalidCooperativeLoadType(span) => ParseError {\n                message: \"cooperative load should have a generic type for coop_mat\".into(),\n                labels: vec![(span, \"type needs the coop_mat<...>\".into())],\n                notes: vec![format!(\"must be a valid cooperative type\")],\n            },\n            Error::UnsupportedCooperativeScalar(span) => ParseError {\n                message: \"cooperative scalar type is not supported\".into(),\n                labels: vec![(span, \"type needs the scalar type specified\".into())],\n                notes: vec![format!(\"must be F32\")],\n            },\n            Error::UnexpectedIdentForEnumerant(ident_span) => ParseError {\n                message: format!(\n                    \"identifier `{}` resolves to a declaration\",\n                    &source[ident_span]\n                ),\n                labels: vec![(ident_span, \"needs to resolve to a predeclared enumerant\".into())],\n                notes: vec![],\n            },\n            Error::UnexpectedExprForEnumerant(expr_span) => ParseError {\n                message: \"unexpected expression\".to_string(),\n                labels: vec![(expr_span, \"needs to be an identifier resolving to a predeclared enumerant\".into())],\n                notes: vec![],\n            },\n            Error::UnusedArgsForTemplate(ref expr_spans) => ParseError {\n                message: \"unused expressions for template\".to_string(),\n                labels: expr_spans.iter().cloned().map(|span| -> (_, _){ (span, \"unused\".into()) }).collect(),\n                notes: vec![],\n            },\n            Error::UnexpectedTemplate(span) => ParseError {\n                message: \"unexpected template\".to_string(),\n                labels: vec![(span, \"expected identifier\".into())],\n                notes: vec![],\n            },\n            Error::MissingTemplateArg {\n                span,\n                description: arg,\n            } => ParseError {\n                message: format!(\n                    \"`{}` needs a template argument specified: {arg}\",\n                    &source[span]\n                ),\n                labels: vec![(span, \"is missing a template argument\".into())],\n                notes: vec![],\n            },\n            Error::UnexpectedExprForTypeExpression(expr_span) => ParseError {\n                message: \"unexpected expression\".to_string(),\n                labels: vec![(expr_span, \"needs to be an identifier resolving to a type declaration (alias or struct) or predeclared type(-generator)\".into())],\n                notes: vec![],\n            },\n            Error::MissingIncomingPayload(span) => ParseError {\n                message: \"incoming payload is missing on a `closest_hit`, `any_hit` or `miss` shader entry point\".to_string(),\n                labels: vec![(\n                    span,\n                    \"must be paired with a `@incoming_payload` attribute\".into(),\n                )],\n                notes: vec![],\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/index.rs",
    "content": "use alloc::{boxed::Box, vec, vec::Vec};\n\nuse super::{Error, Result};\nuse crate::front::wgsl::parse::ast;\nuse crate::{FastHashMap, Handle, Span};\n\n/// A `GlobalDecl` list in which each definition occurs before all its uses.\npub struct Index<'a> {\n    dependency_order: Vec<Handle<ast::GlobalDecl<'a>>>,\n}\n\nimpl<'a> Index<'a> {\n    /// Generate an `Index` for the given translation unit.\n    ///\n    /// Perform a topological sort on `tu`'s global declarations, placing\n    /// referents before the definitions that refer to them.\n    ///\n    /// Return an error if the graph of references between declarations contains\n    /// any cycles.\n    pub fn generate(tu: &ast::TranslationUnit<'a>) -> Result<'a, Self> {\n        // Produce a map from global definitions' names to their `Handle<GlobalDecl>`s.\n        // While doing so, reject conflicting definitions.\n        let mut globals = FastHashMap::with_capacity_and_hasher(tu.decls.len(), Default::default());\n        for (handle, decl) in tu.decls.iter() {\n            if let Some(ident) = decl_ident(decl) {\n                let name = ident.name;\n                if let Some(old) = globals.insert(name, handle) {\n                    return Err(Box::new(Error::Redefinition {\n                        previous: decl_ident(&tu.decls[old])\n                            .expect(\"decl should have ident for redefinition\")\n                            .span,\n                        current: ident.span,\n                    }));\n                }\n            }\n        }\n\n        let len = tu.decls.len();\n        let solver = DependencySolver {\n            globals: &globals,\n            module: tu,\n            visited: vec![false; len],\n            temp_visited: vec![false; len],\n            path: Vec::new(),\n            out: Vec::with_capacity(len),\n        };\n        let dependency_order = solver.solve()?;\n\n        Ok(Self { dependency_order })\n    }\n\n    /// Iterate over `GlobalDecl`s, visiting each definition before all its uses.\n    ///\n    /// Produce handles for all of the `GlobalDecl`s of the `TranslationUnit`\n    /// passed to `Index::generate`, ordered so that a given declaration is\n    /// produced before any other declaration that uses it.\n    pub fn visit_ordered(&self) -> impl Iterator<Item = Handle<ast::GlobalDecl<'a>>> + '_ {\n        self.dependency_order.iter().copied()\n    }\n}\n\n/// An edge from a reference to its referent in the current depth-first\n/// traversal.\n///\n/// This is like `ast::Dependency`, except that we've determined which\n/// `GlobalDecl` it refers to.\nstruct ResolvedDependency<'a> {\n    /// The referent of some identifier used in the current declaration.\n    decl: Handle<ast::GlobalDecl<'a>>,\n\n    /// Where that use occurs within the current declaration.\n    usage: Span,\n}\n\n/// Local state for ordering a `TranslationUnit`'s module-scope declarations.\n///\n/// Values of this type are used temporarily by `Index::generate`\n/// to perform a depth-first sort on the declarations.\n/// Technically, what we want is a topological sort, but a depth-first sort\n/// has one key benefit - it's much more efficient in storing\n/// the path of each node for error generation.\nstruct DependencySolver<'source, 'temp> {\n    /// A map from module-scope definitions' names to their handles.\n    globals: &'temp FastHashMap<&'source str, Handle<ast::GlobalDecl<'source>>>,\n\n    /// The translation unit whose declarations we're ordering.\n    module: &'temp ast::TranslationUnit<'source>,\n\n    /// For each handle, whether we have pushed it onto `out` yet.\n    visited: Vec<bool>,\n\n    /// For each handle, whether it is an predecessor in the current depth-first\n    /// traversal. This is used to detect cycles in the reference graph.\n    temp_visited: Vec<bool>,\n\n    /// The current path in our depth-first traversal. Used for generating\n    /// error messages for non-trivial reference cycles.\n    path: Vec<ResolvedDependency<'source>>,\n\n    /// The list of declaration handles, with declarations before uses.\n    out: Vec<Handle<ast::GlobalDecl<'source>>>,\n}\n\nimpl<'a> DependencySolver<'a, '_> {\n    /// Produce the sorted list of declaration handles, and check for cycles.\n    fn solve(mut self) -> Result<'a, Vec<Handle<ast::GlobalDecl<'a>>>> {\n        for (id, _) in self.module.decls.iter() {\n            if self.visited[id.index()] {\n                continue;\n            }\n\n            self.dfs(id)?;\n        }\n\n        Ok(self.out)\n    }\n\n    /// Ensure that all declarations used by `id` have been added to the\n    /// ordering, and then append `id` itself.\n    fn dfs(&mut self, id: Handle<ast::GlobalDecl<'a>>) -> Result<'a, ()> {\n        let decl = &self.module.decls[id];\n        let id_usize = id.index();\n\n        self.temp_visited[id_usize] = true;\n        for dep in decl.dependencies.iter() {\n            if let Some(&dep_id) = self.globals.get(dep.ident) {\n                self.path.push(ResolvedDependency {\n                    decl: dep_id,\n                    usage: dep.usage,\n                });\n                let dep_id_usize = dep_id.index();\n\n                if self.temp_visited[dep_id_usize] {\n                    // Found a cycle.\n                    return if dep_id == id {\n                        // A declaration refers to itself directly.\n                        Err(Box::new(Error::RecursiveDeclaration {\n                            ident: decl_ident(decl).expect(\"decl should have ident\").span,\n                            usage: dep.usage,\n                        }))\n                    } else {\n                        // A declaration refers to itself indirectly, through\n                        // one or more other definitions. Report the entire path\n                        // of references.\n                        let start_at = self\n                            .path\n                            .iter()\n                            .rev()\n                            .enumerate()\n                            .find_map(|(i, dep)| (dep.decl == dep_id).then_some(i))\n                            .unwrap_or(0);\n\n                        Err(Box::new(Error::CyclicDeclaration {\n                            ident: decl_ident(&self.module.decls[dep_id])\n                                .expect(\"decl should have ident\")\n                                .span,\n                            path: self.path[start_at..]\n                                .iter()\n                                .map(|curr_dep| {\n                                    let curr_id = curr_dep.decl;\n                                    let curr_decl = &self.module.decls[curr_id];\n\n                                    (\n                                        decl_ident(curr_decl).expect(\"decl should have ident\").span,\n                                        curr_dep.usage,\n                                    )\n                                })\n                                .collect(),\n                        }))\n                    };\n                } else if !self.visited[dep_id_usize] {\n                    self.dfs(dep_id)?;\n                }\n\n                // Remove this edge from the current path.\n                self.path.pop();\n            }\n\n            // Ignore unresolved identifiers; they may be predeclared objects.\n        }\n\n        // Remove this node from the current path.\n        self.temp_visited[id_usize] = false;\n\n        // Now everything this declaration uses has been visited, and is already\n        // present in `out`. That means we can append this one to the\n        // ordering, and mark it as visited.\n        self.out.push(id);\n        self.visited[id_usize] = true;\n\n        Ok(())\n    }\n}\n\nconst fn decl_ident<'a>(decl: &ast::GlobalDecl<'a>) -> Option<ast::Ident<'a>> {\n    match decl.kind {\n        ast::GlobalDeclKind::Fn(ref f) => Some(f.name),\n        ast::GlobalDeclKind::Var(ref v) => Some(v.name),\n        ast::GlobalDeclKind::Const(ref c) => Some(c.name),\n        ast::GlobalDeclKind::Override(ref o) => Some(o.name),\n        ast::GlobalDeclKind::Struct(ref s) => Some(s.name),\n        ast::GlobalDeclKind::Type(ref t) => Some(t.name),\n        ast::GlobalDeclKind::ConstAssert(_) => None,\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/lower/construction.rs",
    "content": "use alloc::{boxed::Box, vec, vec::Vec};\nuse core::num::NonZeroU32;\n\nuse crate::common::wgsl::{TryToWgsl, TypeContext};\nuse crate::front::wgsl::lower::{ExpressionContext, Lowerer};\nuse crate::front::wgsl::parse::ast;\nuse crate::front::wgsl::{Error, Result};\nuse crate::{Handle, Span};\n\n/// A [`constructor built-in function`].\n///\n/// WGSL has two types of such functions:\n///\n/// - Those that fully specify the type being constructed, like\n///   `vec3<f32>(x,y,z)`, which obviously constructs a `vec3<f32>`.\n///\n/// - Those that leave the component type of the composite being constructed\n///   implicit, to be inferred from the argument types, like `vec3(x,y,z)`,\n///   which constructs a `vec3<T>` where `T` is the type of `x`, `y`, and `z`.\n///\n/// This enum represents both cases. The `PartialFoo` variants\n/// represent the second case, where the component type is implicit.\n///\n/// [`constructor built-in function`]: https://gpuweb.github.io/gpuweb/wgsl/#constructor-builtin-function\npub enum Constructor<T> {\n    /// A vector construction whose component type is inferred from the\n    /// argument: `vec3(1.0)`.\n    PartialVector { size: crate::VectorSize },\n\n    /// A matrix construction whose component type is inferred from the\n    /// argument: `mat2x2(1,2,3,4)`.\n    PartialMatrix {\n        columns: crate::VectorSize,\n        rows: crate::VectorSize,\n    },\n\n    /// An array whose component type and size are inferred from the arguments:\n    /// `array(3,4,5)`.\n    PartialArray,\n\n    /// A known Naga type.\n    ///\n    /// When we match on this type, we need to see the `TypeInner` here, but at\n    /// the point that we build this value we'll still need mutable access to\n    /// the module later. To avoid borrowing from the module, the type parameter\n    /// `T` is `Handle<Type>` initially. Then we use `borrow_inner` to produce a\n    /// version holding a tuple `(Handle<Type>, &TypeInner)`.\n    Type(T),\n}\n\nimpl Constructor<Handle<crate::Type>> {\n    /// Return an equivalent `Constructor` value that includes borrowed\n    /// `TypeInner` values alongside any type handles.\n    ///\n    /// The returned form is more convenient to match on, since the patterns\n    /// can actually see what the handle refers to.\n    fn borrow_inner(\n        self,\n        module: &crate::Module,\n    ) -> Constructor<(Handle<crate::Type>, &crate::TypeInner)> {\n        match self {\n            Constructor::PartialVector { size } => Constructor::PartialVector { size },\n            Constructor::PartialMatrix { columns, rows } => {\n                Constructor::PartialMatrix { columns, rows }\n            }\n            Constructor::PartialArray => Constructor::PartialArray,\n            Constructor::Type(handle) => Constructor::Type((handle, &module.types[handle].inner)),\n        }\n    }\n}\n\nenum Components<'a> {\n    None,\n    One {\n        component: Handle<crate::Expression>,\n        span: Span,\n        ty_inner: &'a crate::TypeInner,\n    },\n    Many {\n        components: Vec<Handle<crate::Expression>>,\n        spans: Vec<Span>,\n    },\n}\n\nimpl Components<'_> {\n    fn into_components_vec(self) -> Vec<Handle<crate::Expression>> {\n        match self {\n            Self::None => vec![],\n            Self::One { component, .. } => vec![component],\n            Self::Many { components, .. } => components,\n        }\n    }\n}\n\nimpl<'source> Lowerer<'source, '_> {\n    /// Generate Naga IR for a type constructor expression.\n    ///\n    /// The `constructor` value represents the head of the constructor\n    /// expression, which is at least a hint of which type is being built; if\n    /// it's one of the `Partial` variants, we need to consider the argument\n    /// types as well.\n    ///\n    /// This is used for [`Call`] expressions, once we've determined that\n    /// the \"callable\" (in WGSL spec terms) is actually a type.\n    ///\n    /// [`Call`]: ast::Expression::Call\n    pub fn construct(\n        &mut self,\n        span: Span,\n        constructor: Constructor<Handle<crate::Type>>,\n        ty_span: Span,\n        components: &[Handle<ast::Expression<'source>>],\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<crate::Expression>> {\n        use crate::proc::TypeResolution as Tr;\n\n        let components = match *components {\n            [] => Components::None,\n            [component] => {\n                let span = ctx.ast_expressions.get_span(component);\n                let component = self.expression_for_abstract(component, ctx)?;\n                let ty_inner = super::resolve_inner!(ctx, component);\n\n                Components::One {\n                    component,\n                    span,\n                    ty_inner,\n                }\n            }\n            ref ast_components @ [_, _, ..] => {\n                let components = ast_components\n                    .iter()\n                    .map(|&expr| self.expression_for_abstract(expr, ctx))\n                    .collect::<Result<_>>()?;\n                let spans = ast_components\n                    .iter()\n                    .map(|&expr| ctx.ast_expressions.get_span(expr))\n                    .collect();\n\n                for &component in &components {\n                    ctx.grow_types(component)?;\n                }\n\n                Components::Many { components, spans }\n            }\n        };\n\n        // Even though we computed `constructor` above, wait until now to borrow\n        // a reference to the `TypeInner`, so that the component-handling code\n        // above can have mutable access to the type arena.\n        let constructor = constructor.borrow_inner(ctx.module);\n\n        let expr;\n        match (components, constructor) {\n            // Zero-value constructor with explicit type.\n            (Components::None, Constructor::Type((result_ty, inner)))\n                if inner.is_constructible(&ctx.module.types) =>\n            {\n                expr = crate::Expression::ZeroValue(result_ty);\n            }\n            // Zero-value constructor, vector with type inference\n            (Components::None, Constructor::PartialVector { size }) => {\n                // vec2(), vec3(), vec4() return vectors of abstractInts; the same\n                // is not true of the similar constructors for matrices or arrays.\n                // See https://www.w3.org/TR/WGSL/#vec2-builtin et seq.\n                let result_ty = ctx.module.types.insert(\n                    crate::Type {\n                        name: None,\n                        inner: crate::TypeInner::Vector {\n                            size,\n                            scalar: crate::Scalar::ABSTRACT_INT,\n                        },\n                    },\n                    span,\n                );\n                expr = crate::Expression::ZeroValue(result_ty);\n            }\n            // Zero-value constructor, matrix or array with type inference\n            (Components::None, Constructor::PartialMatrix { .. } | Constructor::PartialArray) => {\n                // We have no arguments from which to infer the result type, so\n                // partial constructors aren't acceptable here.\n                return Err(Box::new(Error::TypeNotInferable(ty_span)));\n            }\n\n            // Scalar constructor & conversion (scalar -> scalar)\n            (\n                Components::One {\n                    component,\n                    ty_inner: &crate::TypeInner::Scalar { .. },\n                    ..\n                },\n                Constructor::Type((_, &crate::TypeInner::Scalar(scalar))),\n            ) => {\n                expr = crate::Expression::As {\n                    expr: component,\n                    kind: scalar.kind,\n                    convert: Some(scalar.width),\n                };\n            }\n\n            // Vector conversion (vector -> vector)\n            (\n                Components::One {\n                    component,\n                    ty_inner: &crate::TypeInner::Vector { size: src_size, .. },\n                    ..\n                },\n                Constructor::Type((\n                    _,\n                    &crate::TypeInner::Vector {\n                        size: dst_size,\n                        scalar: dst_scalar,\n                    },\n                )),\n            ) if dst_size == src_size => {\n                expr = crate::Expression::As {\n                    expr: component,\n                    kind: dst_scalar.kind,\n                    convert: Some(dst_scalar.width),\n                };\n            }\n\n            // Vector conversion (vector -> vector) - partial\n            (\n                Components::One {\n                    component,\n                    ty_inner: &crate::TypeInner::Vector { size: src_size, .. },\n                    ..\n                },\n                Constructor::PartialVector { size: dst_size },\n            ) if dst_size == src_size => {\n                // This is a trivial conversion: the sizes match, and a Partial\n                // constructor doesn't specify a scalar type, so nothing can\n                // possibly happen.\n                return Ok(component);\n            }\n\n            // Matrix conversion (matrix -> matrix)\n            (\n                Components::One {\n                    component,\n                    ty_inner:\n                        &crate::TypeInner::Matrix {\n                            columns: src_columns,\n                            rows: src_rows,\n                            ..\n                        },\n                    ..\n                },\n                Constructor::Type((\n                    _,\n                    &crate::TypeInner::Matrix {\n                        columns: dst_columns,\n                        rows: dst_rows,\n                        scalar: dst_scalar,\n                    },\n                )),\n            ) if dst_columns == src_columns && dst_rows == src_rows => {\n                expr = crate::Expression::As {\n                    expr: component,\n                    kind: dst_scalar.kind,\n                    convert: Some(dst_scalar.width),\n                };\n            }\n\n            // Matrix conversion (matrix -> matrix) - partial\n            (\n                Components::One {\n                    component,\n                    ty_inner:\n                        &crate::TypeInner::Matrix {\n                            columns: src_columns,\n                            rows: src_rows,\n                            ..\n                        },\n                    ..\n                },\n                Constructor::PartialMatrix {\n                    columns: dst_columns,\n                    rows: dst_rows,\n                },\n            ) if dst_columns == src_columns && dst_rows == src_rows => {\n                // This is a trivial conversion: the sizes match, and a Partial\n                // constructor doesn't specify a scalar type, so nothing can\n                // possibly happen.\n                return Ok(component);\n            }\n\n            // Vector constructor (splat) - infer type\n            (\n                Components::One {\n                    component,\n                    ty_inner: &crate::TypeInner::Scalar { .. },\n                    ..\n                },\n                Constructor::PartialVector { size },\n            ) => {\n                expr = crate::Expression::Splat {\n                    size,\n                    value: component,\n                };\n            }\n\n            // Vector constructor (splat)\n            (\n                Components::One {\n                    mut component,\n                    ty_inner: &crate::TypeInner::Scalar(component_scalar),\n                    span,\n                },\n                Constructor::Type((\n                    type_handle,\n                    &crate::TypeInner::Vector {\n                        size,\n                        scalar: vec_scalar,\n                    },\n                )),\n            ) => {\n                // Splat only allows automatic conversions of the component's scalar.\n                if !component_scalar.automatically_converts_to(vec_scalar) {\n                    let component_ty = &ctx.typifier()[component];\n                    let arg_ty = ctx.type_resolution_to_string(component_ty);\n                    return Err(Box::new(Error::WrongArgumentType {\n                        function: ctx.type_to_string(type_handle),\n                        call_span: ty_span,\n                        arg_span: span,\n                        arg_index: 0,\n                        arg_ty,\n                        allowed: vec![vec_scalar.to_wgsl_for_diagnostics()],\n                    }));\n                }\n                ctx.convert_slice_to_common_leaf_scalar(\n                    core::slice::from_mut(&mut component),\n                    vec_scalar,\n                )?;\n                expr = crate::Expression::Splat {\n                    size,\n                    value: component,\n                };\n            }\n\n            // Vector constructor (by elements), partial\n            (\n                Components::Many {\n                    mut components,\n                    spans,\n                },\n                Constructor::PartialVector { size },\n            ) => {\n                let consensus_scalar = ctx\n                    .automatic_conversion_consensus(None, &components)\n                    .map_err(|index| {\n                        Error::InvalidConstructorComponentType(spans[index], index as i32)\n                    })?;\n                ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;\n                let inner = consensus_scalar.to_inner_vector(size);\n                let ty = ctx.ensure_type_exists(inner);\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Vector constructor (by elements), full type given\n            (\n                Components::Many { mut components, .. },\n                Constructor::Type((ty, &crate::TypeInner::Vector { scalar, .. })),\n            ) => {\n                ctx.try_automatic_conversions_for_vector(&mut components, scalar, ty_span)?;\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Matrix constructor (by elements), partial\n            (\n                Components::Many {\n                    mut components,\n                    spans,\n                },\n                Constructor::PartialMatrix { columns, rows },\n            ) if components.len() == columns as usize * rows as usize => {\n                let consensus_scalar = ctx\n                    .automatic_conversion_consensus(\n                        Some(crate::Scalar::ABSTRACT_FLOAT),\n                        &components,\n                    )\n                    .map_err(|index| {\n                        Error::InvalidConstructorComponentType(spans[index], index as i32)\n                    })?;\n                ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;\n                let vec_ty = ctx.ensure_type_exists(consensus_scalar.to_inner_vector(rows));\n\n                let components = components\n                    .chunks(rows as usize)\n                    .map(|vec_components| {\n                        ctx.append_expression(\n                            crate::Expression::Compose {\n                                ty: vec_ty,\n                                components: Vec::from(vec_components),\n                            },\n                            Default::default(),\n                        )\n                    })\n                    .collect::<Result<Vec<_>>>()?;\n\n                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar: consensus_scalar,\n                });\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Matrix constructor (by elements), type given\n            (\n                Components::Many { mut components, .. },\n                Constructor::Type((\n                    _,\n                    &crate::TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    },\n                )),\n            ) if components.len() == columns as usize * rows as usize => {\n                let element = Tr::Value(crate::TypeInner::Scalar(scalar));\n                ctx.try_automatic_conversions_slice(&mut components, &element, ty_span)?;\n                let vec_ty = ctx.ensure_type_exists(scalar.to_inner_vector(rows));\n\n                let components = components\n                    .chunks(rows as usize)\n                    .map(|vec_components| {\n                        ctx.append_expression(\n                            crate::Expression::Compose {\n                                ty: vec_ty,\n                                components: Vec::from(vec_components),\n                            },\n                            Default::default(),\n                        )\n                    })\n                    .collect::<Result<Vec<_>>>()?;\n\n                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar,\n                });\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Matrix constructor (by columns), partial\n            (\n                Components::Many {\n                    mut components,\n                    spans,\n                },\n                Constructor::PartialMatrix { columns, rows },\n            ) if components.len() == columns as usize => {\n                let consensus_scalar = ctx\n                    .automatic_conversion_consensus(\n                        Some(crate::Scalar::ABSTRACT_FLOAT),\n                        &components,\n                    )\n                    .map_err(|index| {\n                        Error::InvalidConstructorComponentType(spans[index], index as i32)\n                    })?;\n\n                let component_ty = crate::TypeInner::Vector {\n                    size: rows,\n                    scalar: consensus_scalar,\n                };\n                ctx.try_automatic_conversions_slice(\n                    &mut components,\n                    &Tr::Value(component_ty),\n                    ty_span,\n                )?;\n\n                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar: consensus_scalar,\n                });\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Matrix constructor (by columns), type given\n            (\n                Components::Many { mut components, .. },\n                Constructor::Type((\n                    ty,\n                    &crate::TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    },\n                )),\n            ) if components.len() == columns as usize => {\n                let component_ty = crate::TypeInner::Vector { size: rows, scalar };\n                ctx.try_automatic_conversions_slice(\n                    &mut components,\n                    &Tr::Value(component_ty),\n                    ty_span,\n                )?;\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Array constructor - infer type\n            (components, Constructor::PartialArray) => {\n                let mut components = components.into_components_vec();\n                if let Ok(consensus_scalar) = ctx.automatic_conversion_consensus(None, &components)\n                {\n                    // Note that this will *not* necessarily convert all the\n                    // components to the same type! The `automatic_conversion_consensus`\n                    // method only considers the parameters' leaf scalar\n                    // types; the parameters themselves could be any mix of\n                    // vectors, matrices, and scalars.\n                    //\n                    // But *if* it is possible for this array construction\n                    // expression to be well-typed at all, then all the\n                    // parameters must have the same type constructors (vec,\n                    // matrix, scalar) applied to their leaf scalars, so\n                    // reconciling their scalars is always the right thing to\n                    // do. And if this array construction is not well-typed,\n                    // these conversions will not make it so, and we can let\n                    // validation catch the error.\n                    ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;\n                } else {\n                    // There's no consensus scalar. Emit the `Compose`\n                    // expression anyway, and let validation catch the problem.\n                }\n\n                let base = ctx.register_type(components[0])?;\n\n                let inner = crate::TypeInner::Array {\n                    base,\n                    size: crate::ArraySize::Constant(\n                        NonZeroU32::new(u32::try_from(components.len()).unwrap()).unwrap(),\n                    ),\n                    stride: {\n                        ctx.layouter.update(ctx.module.to_ctx()).unwrap();\n                        ctx.layouter[base].to_stride()\n                    },\n                };\n                let ty = ctx.ensure_type_exists(inner);\n\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Array constructor, explicit type.\n            (\n                components,\n                Constructor::Type((ty, inner @ &crate::TypeInner::Array { base, .. })),\n            ) if inner.is_constructible(&ctx.module.types) => {\n                let mut components = components.into_components_vec();\n                ctx.try_automatic_conversions_slice(&mut components, &Tr::Handle(base), ty_span)?;\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // Struct constructor\n            (\n                components,\n                Constructor::Type((ty, inner @ &crate::TypeInner::Struct { ref members, .. })),\n            ) if inner.is_constructible(&ctx.module.types) => {\n                let mut components = components.into_components_vec();\n                let struct_ty_span = ctx.module.types.get_span(ty);\n\n                // Make a vector of the members' type handles in advance, to\n                // avoid borrowing `members` from `ctx` while we generate\n                // new code.\n                let members: Vec<Handle<crate::Type>> = members.iter().map(|m| m.ty).collect();\n\n                for (component, &ty) in components.iter_mut().zip(&members) {\n                    *component =\n                        ctx.try_automatic_conversions(*component, &Tr::Handle(ty), struct_ty_span)?;\n                }\n                expr = crate::Expression::Compose { ty, components };\n            }\n\n            // ERRORS\n\n            // Bad conversion (type cast)\n            (\n                Components::One {\n                    span, component, ..\n                },\n                Constructor::Type((\n                    ty,\n                    &(crate::TypeInner::Scalar { .. }\n                    | crate::TypeInner::Vector { .. }\n                    | crate::TypeInner::Matrix { .. }),\n                )),\n            ) => {\n                let component_ty = &ctx.typifier()[component];\n                let from_type = ctx.type_resolution_to_string(component_ty);\n                return Err(Box::new(Error::BadTypeCast {\n                    span,\n                    from_type,\n                    to_type: ctx.type_to_string(ty),\n                }));\n            }\n\n            // Too many parameters for scalar constructor\n            (\n                Components::Many { spans, .. },\n                Constructor::Type((_, &crate::TypeInner::Scalar { .. })),\n            ) => {\n                let span = spans[1].until(spans.last().unwrap());\n                return Err(Box::new(Error::UnexpectedComponents(span)));\n            }\n\n            // Other types can't be constructed\n            _ => return Err(Box::new(Error::TypeNotConstructible(ty_span))),\n        }\n\n        let expr = ctx.append_expression(expr, span)?;\n        Ok(expr)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/lower/conversion.rs",
    "content": "//! WGSL's automatic conversions for abstract types.\n\nuse alloc::{boxed::Box, string::String, vec::Vec};\n\nuse crate::common::wgsl::{TryToWgsl, TypeContext};\nuse crate::front::wgsl::error::{\n    AutoConversionError, AutoConversionLeafScalarError, ConcretizationFailedError,\n};\nuse crate::front::wgsl::Result;\nuse crate::{Handle, Span};\n\nimpl<'source> super::ExpressionContext<'source, '_, '_> {\n    /// Try to use WGSL's automatic conversions to convert `expr` to `goal_ty`.\n    ///\n    /// If no conversions are necessary, return `expr` unchanged.\n    ///\n    /// If automatic conversions cannot convert `expr` to `goal_ty`, return an\n    /// [`AutoConversion`] error.\n    ///\n    /// Although the Load Rule is one of the automatic conversions, this\n    /// function assumes it has already been applied if appropriate, as\n    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.\n    ///\n    /// [`AutoConversion`]: super::Error::AutoConversion\n    pub fn try_automatic_conversions(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        goal_ty: &crate::proc::TypeResolution,\n        goal_span: Span,\n    ) -> Result<'source, Handle<crate::Expression>> {\n        let expr_span = self.get_expression_span(expr);\n        // Keep the TypeResolution so we can get type names for\n        // structs in error messages.\n        let expr_resolution = super::resolve!(self, expr);\n        let types = &self.module.types;\n        let expr_inner = expr_resolution.inner_with(types);\n        let goal_inner = goal_ty.inner_with(types);\n\n        // We can only convert abstract types, so if `expr` is not abstract do not even\n        // attempt conversion. This allows the validator to catch type errors correctly\n        // rather than them being misreported as type conversion errors.\n        // If the type is an array (of an array, etc) then we must check whether the\n        // type of the innermost array's base type is abstract.\n        if !expr_inner.is_abstract(types) {\n            return Ok(expr);\n        }\n\n        // If `expr` already has the requested type, we're done.\n        if self.module.compare_types(expr_resolution, goal_ty) {\n            return Ok(expr);\n        }\n\n        let (_expr_scalar, goal_scalar) =\n            match expr_inner.automatically_converts_to(goal_inner, types) {\n                Some(scalars) => scalars,\n                None => {\n                    let source_type = self.type_resolution_to_string(expr_resolution);\n                    let dest_type = self.type_resolution_to_string(goal_ty);\n\n                    return Err(Box::new(super::Error::AutoConversion(Box::new(\n                        AutoConversionError {\n                            dest_span: goal_span,\n                            dest_type,\n                            source_span: expr_span,\n                            source_type,\n                        },\n                    ))));\n                }\n            };\n\n        self.convert_leaf_scalar(expr, expr_span, goal_scalar)\n    }\n\n    /// Try to convert `expr`'s leaf scalar to `goal_scalar` using automatic conversions.\n    ///\n    /// If no conversions are necessary, return `expr` unchanged.\n    ///\n    /// If automatic conversions cannot convert `expr` to `goal_scalar`, return\n    /// an [`AutoConversionLeafScalar`] error.\n    ///\n    /// Although the Load Rule is one of the automatic conversions, this\n    /// function assumes it has already been applied if appropriate, as\n    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.\n    ///\n    /// [`AutoConversionLeafScalar`]: super::Error::AutoConversionLeafScalar\n    pub fn try_automatic_conversion_for_leaf_scalar(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        goal_scalar: crate::Scalar,\n        goal_span: Span,\n    ) -> Result<'source, Handle<crate::Expression>> {\n        let expr_span = self.get_expression_span(expr);\n        let expr_resolution = super::resolve!(self, expr);\n        let types = &self.module.types;\n        let expr_inner = expr_resolution.inner_with(types);\n\n        let make_error = || {\n            let source_type = self.type_resolution_to_string(expr_resolution);\n            super::Error::AutoConversionLeafScalar(Box::new(AutoConversionLeafScalarError {\n                dest_span: goal_span,\n                dest_scalar: goal_scalar.to_wgsl_for_diagnostics(),\n                source_span: expr_span,\n                source_type,\n            }))\n        };\n\n        let expr_scalar = match expr_inner.automatically_convertible_scalar(&self.module.types) {\n            Some(scalar) => scalar,\n            None => return Err(Box::new(make_error())),\n        };\n\n        if expr_scalar == goal_scalar {\n            return Ok(expr);\n        }\n\n        if !expr_scalar.automatically_converts_to(goal_scalar) {\n            return Err(Box::new(make_error()));\n        }\n\n        assert!(expr_scalar.is_abstract());\n\n        self.convert_leaf_scalar(expr, expr_span, goal_scalar)\n    }\n\n    fn convert_leaf_scalar(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        expr_span: Span,\n        goal_scalar: crate::Scalar,\n    ) -> Result<'source, Handle<crate::Expression>> {\n        let expr_inner = super::resolve_inner!(self, expr);\n        if let crate::TypeInner::Array { .. } = *expr_inner {\n            self.as_const_evaluator()\n                .cast_array(expr, goal_scalar, expr_span)\n                .map_err(|err| {\n                    Box::new(super::Error::ConstantEvaluatorError(err.into(), expr_span))\n                })\n        } else {\n            let cast = crate::Expression::As {\n                expr,\n                kind: goal_scalar.kind,\n                convert: Some(goal_scalar.width),\n            };\n            self.append_expression(cast, expr_span)\n        }\n    }\n\n    /// Try to convert `exprs` to `goal_ty` using WGSL's automatic conversions.\n    pub fn try_automatic_conversions_slice(\n        &mut self,\n        exprs: &mut [Handle<crate::Expression>],\n        goal_ty: &crate::proc::TypeResolution,\n        goal_span: Span,\n    ) -> Result<'source, ()> {\n        for expr in exprs.iter_mut() {\n            *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;\n        }\n\n        Ok(())\n    }\n\n    /// Apply WGSL's automatic conversions to a vector constructor's arguments.\n    ///\n    /// When calling a vector constructor like `vec3<f32>(...)`, the parameters\n    /// can be a mix of scalars and vectors, with the latter being spread out to\n    /// contribute each of their components as a component of the new value.\n    /// When the element type is explicit, as with `<f32>` in the example above,\n    /// WGSL's automatic conversions should convert abstract scalar and vector\n    /// parameters to the constructor's required scalar type.\n    pub fn try_automatic_conversions_for_vector(\n        &mut self,\n        exprs: &mut [Handle<crate::Expression>],\n        goal_scalar: crate::Scalar,\n        goal_span: Span,\n    ) -> Result<'source, ()> {\n        use crate::proc::TypeResolution as Tr;\n        use crate::TypeInner as Ti;\n        let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));\n\n        for (i, expr) in exprs.iter_mut().enumerate() {\n            // Keep the TypeResolution so we can get full type names\n            // in error messages.\n            let expr_resolution = super::resolve!(self, *expr);\n            let types = &self.module.types;\n            let expr_inner = expr_resolution.inner_with(types);\n\n            match *expr_inner {\n                Ti::Scalar(_) => {\n                    *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;\n                }\n                Ti::Vector { size, scalar: _ } => {\n                    let goal_vector_res = Tr::Value(Ti::Vector {\n                        size,\n                        scalar: goal_scalar,\n                    });\n                    *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;\n                }\n                _ => {\n                    let span = self.get_expression_span(*expr);\n                    return Err(Box::new(super::Error::InvalidConstructorComponentType(\n                        span, i as i32,\n                    )));\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Convert `expr` to the leaf scalar type `scalar`.\n    pub fn convert_to_leaf_scalar(\n        &mut self,\n        expr: &mut Handle<crate::Expression>,\n        goal: crate::Scalar,\n    ) -> Result<'source, ()> {\n        let inner = super::resolve_inner!(self, *expr);\n        // Do nothing if `inner` doesn't even have leaf scalars;\n        // it's a type error that validation will catch.\n        if inner.scalar() != Some(goal) {\n            let cast = crate::Expression::As {\n                expr: *expr,\n                kind: goal.kind,\n                convert: Some(goal.width),\n            };\n            let expr_span = self.get_expression_span(*expr);\n            *expr = self.append_expression(cast, expr_span)?;\n        }\n\n        Ok(())\n    }\n\n    /// Convert all expressions in `exprs` to a common scalar type.\n    ///\n    /// Note that the caller is responsible for making sure these\n    /// conversions are actually justified. This function simply\n    /// generates `As` expressions, regardless of whether they are\n    /// permitted WGSL automatic conversions. Callers intending to\n    /// implement automatic conversions need to determine for\n    /// themselves whether the casts we we generate are justified,\n    /// perhaps by calling `TypeInner::automatically_converts_to` or\n    /// `Scalar::automatic_conversion_combine`.\n    pub fn convert_slice_to_common_leaf_scalar(\n        &mut self,\n        exprs: &mut [Handle<crate::Expression>],\n        goal: crate::Scalar,\n    ) -> Result<'source, ()> {\n        for expr in exprs.iter_mut() {\n            self.convert_to_leaf_scalar(expr, goal)?;\n        }\n\n        Ok(())\n    }\n\n    /// Return an expression for the concretized value of `expr`.\n    ///\n    /// If `expr` is already concrete, return it unchanged.\n    pub fn concretize(\n        &mut self,\n        expr: Handle<crate::Expression>,\n    ) -> Result<'source, Handle<crate::Expression>> {\n        let inner = super::resolve_inner!(self, expr);\n        if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {\n            use crate::ScalarKind as Sk;\n            let concretization_preferences = match scalar.kind {\n                // already concrete\n                Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => return Ok(expr),\n                Sk::AbstractInt => {\n                    [crate::Scalar::I32, crate::Scalar::U32, crate::Scalar::F32].as_slice()\n                }\n                Sk::AbstractFloat => [crate::Scalar::F32].as_slice(),\n            };\n            let expr_span = self.get_expression_span(expr);\n            let mut errors = Vec::new();\n            for concrete_scalar in concretization_preferences {\n                match self\n                    .as_const_evaluator()\n                    .cast_array(expr, *concrete_scalar, expr_span)\n                {\n                    Ok(expr) => return Ok(expr),\n                    Err(err) => {\n                        errors.push((concrete_scalar.to_wgsl_for_diagnostics(), err));\n                    }\n                }\n            }\n            if !errors.is_empty() {\n                // A `TypeResolution` includes the type's full name, if\n                // it has one. Also, avoid holding the borrow of `inner`\n                // across the call to `cast_array`.\n                let expr_type = &self.typifier()[expr];\n                return Err(Box::new(super::Error::ConcretizationFailed(Box::new(\n                    ConcretizationFailedError {\n                        expr_span,\n                        expr_type: self.type_resolution_to_string(expr_type),\n                        concretization_preferences: errors,\n                    },\n                ))));\n            }\n        }\n\n        Ok(expr)\n    }\n\n    /// Find the consensus scalar of `components` under WGSL's automatic\n    /// conversions.\n    ///\n    /// If `components` can all be converted to any common scalar via\n    /// WGSL's automatic conversions, return the best such scalar.\n    ///\n    /// The `components` slice must not be empty. All elements' types must\n    /// have been resolved.\n    ///\n    /// If `components` are definitely not acceptable as arguments to such\n    /// constructors, return `Err(i)`, where `i` is the index in\n    /// `components` of some problematic argument.\n    ///\n    /// If `base` is `Some(scalar)`, the consensus scalar must also be\n    /// compatible with that `scalar`. This is used to restrict matrix\n    /// initializers to floating-point types.\n    ///\n    /// This function doesn't fully type-check the arguments - it only\n    /// considers their leaf scalar types. This means it may return `Ok`\n    /// even when the Naga validator will reject the resulting\n    /// construction expression later.\n    pub fn automatic_conversion_consensus<'handle, I>(\n        &self,\n        base: Option<crate::Scalar>,\n        components: I,\n    ) -> core::result::Result<crate::Scalar, usize>\n    where\n        I: IntoIterator<Item = &'handle Handle<crate::Expression>>,\n        I::IntoIter: Clone, // for debugging\n    {\n        let types = &self.module.types;\n        let components_iter = components.into_iter();\n        log::debug!(\n            \"wgsl automatic_conversion_consensus: {}\",\n            components_iter\n                .clone()\n                .map(|&expr| {\n                    let res = &self.typifier()[expr];\n                    self.type_resolution_to_string(res)\n                })\n                .collect::<Vec<String>>()\n                .join(\", \")\n        );\n        let mut components_iter = components_iter\n            .map(|&c| self.typifier()[c].inner_with(types).scalar())\n            .enumerate();\n        let base = base\n            .or_else(|| components_iter.next().unwrap().1)\n            .ok_or(0usize)?;\n        let best = components_iter.try_fold(base, |best, (i, scalar)| {\n            scalar\n                .and_then(|scalar| best.automatic_conversion_combine(scalar))\n                .ok_or(i)\n        })?;\n        log::debug!(\"    consensus: {}\", best.to_wgsl_for_diagnostics());\n        Ok(best)\n    }\n}\n\nimpl crate::TypeInner {\n    fn automatically_convertible_scalar(\n        &self,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> Option<crate::Scalar> {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {\n                Some(scalar)\n            }\n            Ti::CooperativeMatrix { .. } => None,\n            Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),\n            Ti::Atomic(_)\n            | Ti::Pointer { .. }\n            | Ti::ValuePointer { .. }\n            | Ti::Struct { .. }\n            | Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::RayQuery { .. }\n            | Ti::BindingArray { .. } => None,\n        }\n    }\n\n    /// Return the leaf scalar type of `pointer`.\n    ///\n    /// `pointer` must be a `TypeInner` representing a pointer type.\n    pub fn pointer_automatically_convertible_scalar(\n        &self,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> Option<crate::Scalar> {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {\n                Some(scalar)\n            }\n            Ti::CooperativeMatrix { .. } => None,\n            Ti::Atomic(_) => None,\n            Ti::Pointer { base, .. } | Ti::Array { base, .. } => {\n                types[base].inner.automatically_convertible_scalar(types)\n            }\n            Ti::ValuePointer { scalar, .. } => Some(scalar),\n            Ti::Struct { .. }\n            | Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::RayQuery { .. }\n            | Ti::BindingArray { .. } => None,\n        }\n    }\n}\n\nimpl crate::Scalar {\n    /// Find the common type of `self` and `other` under WGSL's\n    /// automatic conversions.\n    ///\n    /// If there are any scalars to which WGSL's automatic conversions\n    /// will convert both `self` and `other`, return the best such\n    /// scalar. Otherwise, return `None`.\n    pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {\n        use crate::ScalarKind as Sk;\n\n        match (self.kind, other.kind) {\n            // When the kinds match...\n            (Sk::AbstractFloat, Sk::AbstractFloat)\n            | (Sk::AbstractInt, Sk::AbstractInt)\n            | (Sk::Sint, Sk::Sint)\n            | (Sk::Uint, Sk::Uint)\n            | (Sk::Float, Sk::Float)\n            | (Sk::Bool, Sk::Bool) => {\n                if self.width == other.width {\n                    // ... either no conversion is necessary ...\n                    Some(self)\n                } else {\n                    // ... or no conversion is possible.\n                    // We never convert concrete to concrete, and\n                    // abstract types should have only one size.\n                    None\n                }\n            }\n\n            // AbstractInt converts to AbstractFloat.\n            (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),\n            (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),\n\n            // AbstractFloat converts to Float.\n            (Sk::AbstractFloat, Sk::Float) => Some(other),\n            (Sk::Float, Sk::AbstractFloat) => Some(self),\n\n            // AbstractInt converts to concrete integer or float.\n            (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),\n            (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),\n\n            // AbstractFloat can't be reconciled with concrete integer types.\n            (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {\n                None\n            }\n\n            // Nothing can be reconciled with `bool`.\n            (Sk::Bool, _) | (_, Sk::Bool) => None,\n\n            // Different concrete types cannot be reconciled.\n            (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,\n        }\n    }\n\n    /// Return `true` if automatic conversions will covert `self` to `goal`.\n    pub fn automatically_converts_to(self, goal: Self) -> bool {\n        self.automatic_conversion_combine(goal) == Some(goal)\n    }\n\n    pub(in crate::front::wgsl) const fn concretize(self) -> Self {\n        use crate::ScalarKind as Sk;\n        match self.kind {\n            Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,\n            Sk::AbstractInt => Self::I32,\n            Sk::AbstractFloat => Self::F32,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/lower/mod.rs",
    "content": "use alloc::{\n    borrow::ToOwned,\n    boxed::Box,\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::num::NonZeroU32;\n\nuse crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType};\nuse crate::front::wgsl::index::Index;\nuse crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;\nuse crate::front::wgsl::parse::number::Number;\nuse crate::front::wgsl::parse::{ast, conv};\nuse crate::front::wgsl::Result;\nuse crate::front::Typifier;\nuse crate::{\n    common::wgsl::{TryToWgsl, TypeContext},\n    compact::KeepUnused,\n};\nuse crate::{common::ForDebugWithTypes, proc::LayoutErrorInner};\nuse crate::{ir, proc};\nuse crate::{Arena, FastHashMap, FastIndexMap, Handle, Span};\n\nuse construction::Constructor;\nuse template_list::TemplateListIter;\n\nmod construction;\nmod conversion;\nmod template_list;\n\n/// Resolves the inner type of a given expression.\n///\n/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].\n///\n/// Returns a &[`ir::TypeInner`].\n///\n/// Ideally, we would simply have a function that takes a `&mut ExpressionContext`\n/// and returns a `&TypeResolution`. Unfortunately, this leads the borrow checker\n/// to conclude that the mutable borrow lasts for as long as we are using the\n/// `&TypeResolution`, so we can't use the `ExpressionContext` for anything else -\n/// like, say, resolving another operand's type. Using a macro that expands to\n/// two separate calls, only the first of which needs a `&mut`,\n/// lets the borrow checker see that the mutable borrow is over.\nmacro_rules! resolve_inner {\n    ($ctx:ident, $expr:expr) => {{\n        $ctx.grow_types($expr)?;\n        $ctx.typifier()[$expr].inner_with(&$ctx.module.types)\n    }};\n}\npub(super) use resolve_inner;\n\n/// Resolves the inner types of two given expressions.\n///\n/// Expects a &mut [`ExpressionContext`] and two [`Handle<Expression>`]s.\n///\n/// Returns a tuple containing two &[`ir::TypeInner`].\n///\n/// See the documentation of [`resolve_inner!`] for why this macro is necessary.\nmacro_rules! resolve_inner_binary {\n    ($ctx:ident, $left:expr, $right:expr) => {{\n        $ctx.grow_types($left)?;\n        $ctx.grow_types($right)?;\n        (\n            $ctx.typifier()[$left].inner_with(&$ctx.module.types),\n            $ctx.typifier()[$right].inner_with(&$ctx.module.types),\n        )\n    }};\n}\n\n/// Resolves the type of a given expression.\n///\n/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].\n///\n/// Returns a &[`TypeResolution`].\n///\n/// See the documentation of [`resolve_inner!`] for why this macro is necessary.\n///\n/// [`TypeResolution`]: proc::TypeResolution\nmacro_rules! resolve {\n    ($ctx:ident, $expr:expr) => {{\n        let expr = $expr;\n        $ctx.grow_types(expr)?;\n        &$ctx.typifier()[expr]\n    }};\n}\npub(super) use resolve;\n\n/// State for constructing a `ir::Module`.\npub struct GlobalContext<'source, 'temp, 'out> {\n    enable_extensions: EnableExtensions,\n\n    /// The `TranslationUnit`'s expressions arena.\n    ast_expressions: &'temp Arena<ast::Expression<'source>>,\n\n    // Naga IR values.\n    /// The map from the names of module-scope declarations to the Naga IR\n    /// `Handle`s we have built for them, owned by `Lowerer::lower`.\n    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,\n\n    /// The module we're constructing.\n    module: &'out mut ir::Module,\n\n    const_typifier: &'temp mut Typifier,\n\n    layouter: &'temp mut proc::Layouter,\n\n    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,\n}\n\nimpl<'source> GlobalContext<'source, '_, '_> {\n    const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {\n        ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            ast_expressions: self.ast_expressions,\n            globals: self.globals,\n            module: self.module,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            expr_type: ExpressionContextType::Constant(None),\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        }\n    }\n\n    const fn as_override(&mut self) -> ExpressionContext<'source, '_, '_> {\n        ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            ast_expressions: self.ast_expressions,\n            globals: self.globals,\n            module: self.module,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            expr_type: ExpressionContextType::Override,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        }\n    }\n\n    fn ensure_type_exists(\n        &mut self,\n        name: Option<String>,\n        inner: ir::TypeInner,\n    ) -> Handle<ir::Type> {\n        self.module\n            .types\n            .insert(ir::Type { inner, name }, Span::UNDEFINED)\n    }\n}\n\n/// State for lowering a statement within a function.\npub struct StatementContext<'source, 'temp, 'out> {\n    enable_extensions: EnableExtensions,\n\n    // WGSL AST values.\n    /// A reference to [`TranslationUnit::expressions`] for the translation unit\n    /// we're lowering.\n    ///\n    /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions\n    ast_expressions: &'temp Arena<ast::Expression<'source>>,\n\n    // Naga IR values.\n    /// The map from the names of module-scope declarations to the Naga IR\n    /// `Handle`s we have built for them, owned by `Lowerer::lower`.\n    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,\n\n    /// A map from each `ast::Local` handle to the Naga expression\n    /// we've built for it:\n    ///\n    /// - WGSL function arguments become Naga [`FunctionArgument`] expressions.\n    ///\n    /// - WGSL `var` declarations become Naga [`LocalVariable`] expressions.\n    ///\n    /// - WGSL `let` declararations become arbitrary Naga expressions.\n    ///\n    /// This always borrows the `local_table` local variable in\n    /// [`Lowerer::function`].\n    ///\n    /// [`LocalVariable`]: ir::Expression::LocalVariable\n    /// [`FunctionArgument`]: ir::Expression::FunctionArgument\n    local_table:\n        &'temp mut FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,\n\n    const_typifier: &'temp mut Typifier,\n    typifier: &'temp mut Typifier,\n    layouter: &'temp mut proc::Layouter,\n    function: &'out mut ir::Function,\n    /// Stores the names of expressions that are assigned in `let` statement\n    /// Also stores the spans of the names, for use in errors.\n    named_expressions: &'out mut FastIndexMap<Handle<ir::Expression>, (String, Span)>,\n    module: &'out mut ir::Module,\n\n    /// Which `Expression`s in `self.naga_expressions` are const expressions, in\n    /// the WGSL sense.\n    ///\n    /// According to the WGSL spec, a const expression must not refer to any\n    /// `let` declarations, even if those declarations' initializers are\n    /// themselves const expressions. So this tracker is not simply concerned\n    /// with the form of the expressions; it is also tracking whether WGSL says\n    /// we should consider them to be const. See the use of `force_non_const` in\n    /// the code for lowering `let` bindings.\n    local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,\n    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,\n}\n\nimpl<'a, 'temp> StatementContext<'a, 'temp, '_> {\n    const fn as_const<'t>(\n        &'t mut self,\n        block: &'t mut ir::Block,\n        emitter: &'t mut proc::Emitter,\n    ) -> ExpressionContext<'a, 't, 't>\n    where\n        'temp: 't,\n    {\n        ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            globals: self.globals,\n            ast_expressions: self.ast_expressions,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n            module: self.module,\n            expr_type: ExpressionContextType::Constant(Some(LocalExpressionContext {\n                local_table: self.local_table,\n                function: self.function,\n                block,\n                emitter,\n                typifier: self.typifier,\n                local_expression_kind_tracker: self.local_expression_kind_tracker,\n            })),\n        }\n    }\n\n    const fn as_expression<'t>(\n        &'t mut self,\n        block: &'t mut ir::Block,\n        emitter: &'t mut proc::Emitter,\n    ) -> ExpressionContext<'a, 't, 't>\n    where\n        'temp: 't,\n    {\n        ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            globals: self.globals,\n            ast_expressions: self.ast_expressions,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n            module: self.module,\n            expr_type: ExpressionContextType::Runtime(LocalExpressionContext {\n                local_table: self.local_table,\n                function: self.function,\n                block,\n                emitter,\n                typifier: self.typifier,\n                local_expression_kind_tracker: self.local_expression_kind_tracker,\n            }),\n        }\n    }\n\n    #[allow(dead_code)]\n    const fn as_global(&mut self) -> GlobalContext<'a, '_, '_> {\n        GlobalContext {\n            enable_extensions: self.enable_extensions,\n            ast_expressions: self.ast_expressions,\n            globals: self.globals,\n            module: self.module,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        }\n    }\n\n    fn invalid_assignment_type(&self, expr: Handle<ir::Expression>) -> InvalidAssignmentType {\n        if let Some(&(_, span)) = self.named_expressions.get(&expr) {\n            InvalidAssignmentType::ImmutableBinding(span)\n        } else {\n            match self.function.expressions[expr] {\n                ir::Expression::Swizzle { .. } => InvalidAssignmentType::Swizzle,\n                ir::Expression::Access { base, .. } => self.invalid_assignment_type(base),\n                ir::Expression::AccessIndex { base, .. } => self.invalid_assignment_type(base),\n                _ => InvalidAssignmentType::Other,\n            }\n        }\n    }\n}\n\npub struct LocalExpressionContext<'temp, 'out> {\n    /// A map from [`ast::Local`] handles to the Naga expressions we've built for them.\n    ///\n    /// This is always [`StatementContext::local_table`] for the\n    /// enclosing statement; see that documentation for details.\n    local_table: &'temp FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,\n\n    function: &'out mut ir::Function,\n    block: &'temp mut ir::Block,\n    emitter: &'temp mut proc::Emitter,\n    typifier: &'temp mut Typifier,\n\n    /// Which `Expression`s in `self.naga_expressions` are const expressions, in\n    /// the WGSL sense.\n    ///\n    /// See [`StatementContext::local_expression_kind_tracker`] for details.\n    local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,\n}\n\n/// The type of Naga IR expression we are lowering an [`ast::Expression`] to.\npub enum ExpressionContextType<'temp, 'out> {\n    /// We are lowering to an arbitrary runtime expression, to be\n    /// included in a function's body.\n    ///\n    /// The given [`LocalExpressionContext`] holds information about local\n    /// variables, arguments, and other definitions available only to runtime\n    /// expressions, not constant or override expressions.\n    Runtime(LocalExpressionContext<'temp, 'out>),\n\n    /// We are lowering to a constant expression, to be included in the module's\n    /// constant expression arena.\n    ///\n    /// Everything global constant expressions are allowed to refer to is\n    /// available in the [`ExpressionContext`], but local constant expressions can\n    /// also refer to other\n    Constant(Option<LocalExpressionContext<'temp, 'out>>),\n\n    /// We are lowering to an override expression, to be included in the module's\n    /// constant expression arena.\n    ///\n    /// Everything override expressions are allowed to refer to is\n    /// available in the [`ExpressionContext`], so this variant\n    /// carries no further information.\n    Override,\n}\n\n/// State for lowering an [`ast::Expression`] to Naga IR.\n///\n/// [`ExpressionContext`]s come in two kinds, distinguished by\n/// the value of the [`expr_type`] field:\n///\n/// - A [`Runtime`] context contributes [`naga::Expression`]s to a [`naga::Function`]'s\n///   runtime expression arena.\n///\n/// - A [`Constant`] context contributes [`naga::Expression`]s to a [`naga::Module`]'s\n///   constant expression arena.\n///\n/// [`ExpressionContext`]s are constructed in restricted ways:\n///\n/// - To get a [`Runtime`] [`ExpressionContext`], call\n///   [`StatementContext::as_expression`].\n///\n/// - To get a [`Constant`] [`ExpressionContext`], call\n///   [`GlobalContext::as_const`].\n///\n/// - You can demote a [`Runtime`] context to a [`Constant`] context\n///   by calling [`as_const`], but there's no way to go in the other\n///   direction, producing a runtime context from a constant one. This\n///   is because runtime expressions can refer to constant\n///   expressions, via [`Expression::Constant`], but constant\n///   expressions can't refer to a function's expressions.\n///\n/// Not to be confused with `wgsl::parse::ExpressionContext`, which is\n/// for parsing the `ast::Expression` in the first place.\n///\n/// [`expr_type`]: ExpressionContext::expr_type\n/// [`Runtime`]: ExpressionContextType::Runtime\n/// [`naga::Expression`]: ir::Expression\n/// [`naga::Function`]: ir::Function\n/// [`Constant`]: ExpressionContextType::Constant\n/// [`naga::Module`]: ir::Module\n/// [`as_const`]: ExpressionContext::as_const\n/// [`Expression::Constant`]: ir::Expression::Constant\npub struct ExpressionContext<'source, 'temp, 'out> {\n    enable_extensions: EnableExtensions,\n\n    // WGSL AST values.\n    ast_expressions: &'temp Arena<ast::Expression<'source>>,\n\n    // Naga IR values.\n    /// The map from the names of module-scope declarations to the Naga IR\n    /// `Handle`s we have built for them, owned by `Lowerer::lower`.\n    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,\n\n    /// The IR [`Module`] we're constructing.\n    ///\n    /// [`Module`]: ir::Module\n    module: &'out mut ir::Module,\n\n    /// Type judgments for [`module::global_expressions`].\n    ///\n    /// [`module::global_expressions`]: ir::Module::global_expressions\n    const_typifier: &'temp mut Typifier,\n    layouter: &'temp mut proc::Layouter,\n    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,\n\n    /// Whether we are lowering a constant expression or a general\n    /// runtime expression, and the data needed in each case.\n    expr_type: ExpressionContextType<'temp, 'out>,\n}\n\nimpl TypeContext for ExpressionContext<'_, '_, '_> {\n    fn lookup_type(&self, handle: Handle<ir::Type>) -> &ir::Type {\n        &self.module.types[handle]\n    }\n\n    fn type_name(&self, handle: Handle<ir::Type>) -> &str {\n        self.module.types[handle]\n            .name\n            .as_deref()\n            .unwrap_or(\"{anonymous type}\")\n    }\n\n    fn write_override<W: core::fmt::Write>(\n        &self,\n        handle: Handle<ir::Override>,\n        out: &mut W,\n    ) -> core::fmt::Result {\n        match self.module.overrides[handle].name {\n            Some(ref name) => out.write_str(name),\n            None => write!(out, \"{{anonymous override {handle:?}}}\"),\n        }\n    }\n\n    fn write_unnamed_struct<W: core::fmt::Write>(\n        &self,\n        _: &ir::TypeInner,\n        _: &mut W,\n    ) -> core::fmt::Result {\n        unreachable!(\"the WGSL front end should always know the type name\");\n    }\n}\n\nimpl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {\n    const fn is_runtime(&self) -> bool {\n        match self.expr_type {\n            ExpressionContextType::Runtime(_) => true,\n            ExpressionContextType::Constant(_) | ExpressionContextType::Override => false,\n        }\n    }\n\n    #[allow(dead_code)]\n    const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {\n        ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            globals: self.globals,\n            ast_expressions: self.ast_expressions,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            module: self.module,\n            expr_type: ExpressionContextType::Constant(match self.expr_type {\n                ExpressionContextType::Runtime(ref mut local_expression_context)\n                | ExpressionContextType::Constant(Some(ref mut local_expression_context)) => {\n                    Some(LocalExpressionContext {\n                        local_table: local_expression_context.local_table,\n                        function: local_expression_context.function,\n                        block: local_expression_context.block,\n                        emitter: local_expression_context.emitter,\n                        typifier: local_expression_context.typifier,\n                        local_expression_kind_tracker: local_expression_context\n                            .local_expression_kind_tracker,\n                    })\n                }\n                ExpressionContextType::Constant(None) | ExpressionContextType::Override => None,\n            }),\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        }\n    }\n\n    const fn as_global(&mut self) -> GlobalContext<'source, '_, '_> {\n        GlobalContext {\n            enable_extensions: self.enable_extensions,\n            ast_expressions: self.ast_expressions,\n            globals: self.globals,\n            module: self.module,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        }\n    }\n\n    const fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator<'_> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref mut rctx) => {\n                proc::ConstantEvaluator::for_wgsl_function(\n                    self.module,\n                    &mut rctx.function.expressions,\n                    rctx.local_expression_kind_tracker,\n                    self.layouter,\n                    rctx.emitter,\n                    rctx.block,\n                    false,\n                )\n            }\n            ExpressionContextType::Constant(Some(ref mut rctx)) => {\n                proc::ConstantEvaluator::for_wgsl_function(\n                    self.module,\n                    &mut rctx.function.expressions,\n                    rctx.local_expression_kind_tracker,\n                    self.layouter,\n                    rctx.emitter,\n                    rctx.block,\n                    true,\n                )\n            }\n            ExpressionContextType::Constant(None) => proc::ConstantEvaluator::for_wgsl_module(\n                self.module,\n                self.global_expression_kind_tracker,\n                self.layouter,\n                false,\n            ),\n            ExpressionContextType::Override => proc::ConstantEvaluator::for_wgsl_module(\n                self.module,\n                self.global_expression_kind_tracker,\n                self.layouter,\n                true,\n            ),\n        }\n    }\n\n    /// Return a wrapper around `value` suitable for formatting.\n    ///\n    /// Return a wrapper around `value` that implements\n    /// [`core::fmt::Display`] in a form suitable for use in\n    /// diagnostic messages.\n    const fn as_diagnostic_display<T>(\n        &self,\n        value: T,\n    ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx<'_>)> {\n        let ctx = self.module.to_ctx();\n        crate::common::DiagnosticDisplay((value, ctx))\n    }\n\n    fn append_expression(\n        &mut self,\n        expr: ir::Expression,\n        span: Span,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let mut eval = self.as_const_evaluator();\n        eval.try_eval_and_append(expr, span)\n            .map_err(|e| Box::new(Error::ConstantEvaluatorError(e.into(), span)))\n    }\n\n    fn get_const_val<T: TryFrom<crate::Literal, Error = proc::ConstValueError>>(\n        &self,\n        handle: Handle<ir::Expression>,\n    ) -> core::result::Result<T, proc::ConstValueError> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx) => {\n                if !ctx.local_expression_kind_tracker.is_const(handle) {\n                    return Err(proc::ConstValueError::NonConst);\n                }\n\n                self.module\n                    .to_ctx()\n                    .get_const_val_from(handle, &ctx.function.expressions)\n            }\n            ExpressionContextType::Constant(Some(ref ctx)) => {\n                assert!(ctx.local_expression_kind_tracker.is_const(handle));\n                self.module\n                    .to_ctx()\n                    .get_const_val_from(handle, &ctx.function.expressions)\n            }\n            ExpressionContextType::Constant(None) => self.module.to_ctx().get_const_val(handle),\n            ExpressionContextType::Override => Err(proc::ConstValueError::NonConst),\n        }\n    }\n\n    /// Return `true` if `handle` is a constant expression.\n    fn is_const(&self, handle: Handle<ir::Expression>) -> bool {\n        use ExpressionContextType as Ect;\n        match self.expr_type {\n            Ect::Runtime(ref ctx) | Ect::Constant(Some(ref ctx)) => {\n                ctx.local_expression_kind_tracker.is_const(handle)\n            }\n            Ect::Constant(None) | Ect::Override => {\n                self.global_expression_kind_tracker.is_const(handle)\n            }\n        }\n    }\n\n    fn get_expression_span(&self, handle: Handle<ir::Expression>) -> Span {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx)\n            | ExpressionContextType::Constant(Some(ref ctx)) => {\n                ctx.function.expressions.get_span(handle)\n            }\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {\n                self.module.global_expressions.get_span(handle)\n            }\n        }\n    }\n\n    const fn typifier(&self) -> &Typifier {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx)\n            | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {\n                self.const_typifier\n            }\n        }\n    }\n\n    fn get(&self, handle: Handle<crate::Expression>) -> &crate::Expression {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx)\n            | ExpressionContextType::Constant(Some(ref ctx)) => &ctx.function.expressions[handle],\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {\n                &self.module.global_expressions[handle]\n            }\n        }\n    }\n\n    fn local(\n        &mut self,\n        local: &Handle<ast::Local>,\n        span: Span,\n    ) -> Result<'source, Typed<Handle<ir::Expression>>> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx) => Ok(ctx.local_table[local].runtime()),\n            ExpressionContextType::Constant(Some(ref ctx)) => ctx.local_table[local]\n                .const_time()\n                .ok_or(Box::new(Error::UnexpectedOperationInConstContext(span))),\n            _ => Err(Box::new(Error::UnexpectedOperationInConstContext(span))),\n        }\n    }\n\n    fn runtime_expression_ctx(\n        &mut self,\n        span: Span,\n    ) -> Result<'source, &mut LocalExpressionContext<'temp, 'out>> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref mut ctx) => Ok(ctx),\n            ExpressionContextType::Constant(_) | ExpressionContextType::Override => {\n                Err(Box::new(Error::UnexpectedOperationInConstContext(span)))\n            }\n        }\n    }\n\n    fn with_nested_runtime_expression_ctx<'a, F, T>(\n        &mut self,\n        span: Span,\n        f: F,\n    ) -> Result<'source, (T, crate::Block)>\n    where\n        for<'t> F: FnOnce(&mut ExpressionContext<'source, 't, 't>) -> Result<'source, T>,\n    {\n        let mut block = crate::Block::new();\n        let rctx = match self.expr_type {\n            ExpressionContextType::Runtime(ref mut rctx) => Ok(rctx),\n            ExpressionContextType::Constant(_) | ExpressionContextType::Override => {\n                Err(Error::UnexpectedOperationInConstContext(span))\n            }\n        }?;\n\n        rctx.block\n            .extend(rctx.emitter.finish(&rctx.function.expressions));\n        rctx.emitter.start(&rctx.function.expressions);\n\n        let nested_rctx = LocalExpressionContext {\n            local_table: rctx.local_table,\n            function: rctx.function,\n            block: &mut block,\n            emitter: rctx.emitter,\n            typifier: rctx.typifier,\n            local_expression_kind_tracker: rctx.local_expression_kind_tracker,\n        };\n        let mut nested_ctx = ExpressionContext {\n            enable_extensions: self.enable_extensions,\n            expr_type: ExpressionContextType::Runtime(nested_rctx),\n            ast_expressions: self.ast_expressions,\n            globals: self.globals,\n            module: self.module,\n            const_typifier: self.const_typifier,\n            layouter: self.layouter,\n            global_expression_kind_tracker: self.global_expression_kind_tracker,\n        };\n        let ret = f(&mut nested_ctx)?;\n\n        block.extend(rctx.emitter.finish(&rctx.function.expressions));\n        rctx.emitter.start(&rctx.function.expressions);\n\n        Ok((ret, block))\n    }\n\n    fn gather_component(\n        &mut self,\n        expr: Handle<ir::Expression>,\n        component_span: Span,\n        gather_span: Span,\n    ) -> Result<'source, ir::SwizzleComponent> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref rctx) => {\n                if !rctx.local_expression_kind_tracker.is_const(expr) {\n                    return Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(\n                        component_span,\n                    )));\n                }\n\n                let index = self\n                    .module\n                    .to_ctx()\n                    .get_const_val_from::<u32, _>(expr, &rctx.function.expressions)\n                    .map_err(|err| match err {\n                        proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {\n                            Error::ExpectedConstExprConcreteIntegerScalar(component_span)\n                        }\n                        proc::ConstValueError::Negative => {\n                            Error::ExpectedNonNegative(component_span)\n                        }\n                    })?;\n                ir::SwizzleComponent::XYZW\n                    .get(index as usize)\n                    .copied()\n                    .ok_or(Box::new(Error::InvalidGatherComponent(component_span)))\n            }\n            // This means a `gather` operation appeared in a constant expression.\n            // This error refers to the `gather` itself, not its \"component\" argument.\n            ExpressionContextType::Constant(_) | ExpressionContextType::Override => Err(Box::new(\n                Error::UnexpectedOperationInConstContext(gather_span),\n            )),\n        }\n    }\n\n    /// Determine the type of `handle`, and add it to the module's arena.\n    ///\n    /// If you just need a `TypeInner` for `handle`'s type, use the\n    /// [`resolve_inner!`] macro instead. This function\n    /// should only be used when the type of `handle` needs to appear\n    /// in the module's final `Arena<Type>`, for example, if you're\n    /// creating a [`LocalVariable`] whose type is inferred from its\n    /// initializer.\n    ///\n    /// [`LocalVariable`]: ir::LocalVariable\n    fn register_type(\n        &mut self,\n        handle: Handle<ir::Expression>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        self.grow_types(handle)?;\n        // This is equivalent to calling ExpressionContext::typifier(),\n        // except that this lets the borrow checker see that it's okay\n        // to also borrow self.module.types mutably below.\n        let typifier = match self.expr_type {\n            ExpressionContextType::Runtime(ref ctx)\n            | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {\n                &*self.const_typifier\n            }\n        };\n        Ok(typifier.register_type(handle, &mut self.module.types))\n    }\n\n    /// Resolve the types of all expressions up through `handle`.\n    ///\n    /// Ensure that [`self.typifier`] has a [`TypeResolution`] for\n    /// every expression in `self.function.expressions`.\n    ///\n    /// This does not add types to any arena. The [`Typifier`]\n    /// documentation explains the steps we take to avoid filling\n    /// arenas with intermediate types.\n    ///\n    /// This function takes `&mut self`, so it can't conveniently\n    /// return a shared reference to the resulting `TypeResolution`:\n    /// the shared reference would extend the mutable borrow, and you\n    /// wouldn't be able to use `self` for anything else. Instead, you\n    /// should use [`register_type`] or one of [`resolve!`],\n    /// [`resolve_inner!`] or [`resolve_inner_binary!`].\n    ///\n    /// [`self.typifier`]: ExpressionContext::typifier\n    /// [`TypeResolution`]: proc::TypeResolution\n    /// [`register_type`]: Self::register_type\n    /// [`Typifier`]: Typifier\n    fn grow_types(&mut self, handle: Handle<ir::Expression>) -> Result<'source, &mut Self> {\n        let empty_arena = Arena::new();\n        let resolve_ctx;\n        let typifier;\n        let expressions;\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref mut ctx)\n            | ExpressionContextType::Constant(Some(ref mut ctx)) => {\n                resolve_ctx = proc::ResolveContext::with_locals(\n                    self.module,\n                    &ctx.function.local_variables,\n                    &ctx.function.arguments,\n                );\n                typifier = &mut *ctx.typifier;\n                expressions = &ctx.function.expressions;\n            }\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {\n                resolve_ctx = proc::ResolveContext::with_locals(self.module, &empty_arena, &[]);\n                typifier = self.const_typifier;\n                expressions = &self.module.global_expressions;\n            }\n        };\n        typifier\n            .grow(handle, expressions, &resolve_ctx)\n            .map_err(Error::InvalidResolve)?;\n\n        Ok(self)\n    }\n\n    fn image_data(\n        &mut self,\n        image: Handle<ir::Expression>,\n        span: Span,\n    ) -> Result<'source, (ir::ImageClass, bool)> {\n        match *resolve_inner!(self, image) {\n            ir::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)),\n            _ => Err(Box::new(Error::BadTexture(span))),\n        }\n    }\n\n    fn prepare_args<'b>(\n        &mut self,\n        args: &'b [Handle<ast::Expression<'source>>],\n        min_args: u32,\n        span: Span,\n    ) -> ArgumentContext<'b, 'source> {\n        ArgumentContext {\n            args: args.iter(),\n            min_args,\n            args_used: 0,\n            total_args: args.len() as u32,\n            span,\n        }\n    }\n\n    /// Insert splats, if needed by the non-'*' operations.\n    ///\n    /// See the \"Binary arithmetic expressions with mixed scalar and vector operands\"\n    /// table in the WebGPU Shading Language specification for relevant operators.\n    ///\n    /// Multiply is not handled here as backends are expected to handle vec*scalar\n    /// operations, so inserting splats into the IR increases size needlessly.\n    fn binary_op_splat(\n        &mut self,\n        op: ir::BinaryOperator,\n        left: &mut Handle<ir::Expression>,\n        right: &mut Handle<ir::Expression>,\n    ) -> Result<'source, ()> {\n        if matches!(\n            op,\n            ir::BinaryOperator::Add\n                | ir::BinaryOperator::Subtract\n                | ir::BinaryOperator::Divide\n                | ir::BinaryOperator::Modulo\n        ) {\n            match resolve_inner_binary!(self, *left, *right) {\n                (&ir::TypeInner::Vector { size, .. }, &ir::TypeInner::Scalar { .. }) => {\n                    *right = self.append_expression(\n                        ir::Expression::Splat {\n                            size,\n                            value: *right,\n                        },\n                        self.get_expression_span(*right),\n                    )?;\n                }\n                (&ir::TypeInner::Scalar { .. }, &ir::TypeInner::Vector { size, .. }) => {\n                    *left = self.append_expression(\n                        ir::Expression::Splat { size, value: *left },\n                        self.get_expression_span(*left),\n                    )?;\n                }\n                _ => {}\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Add a single expression to the expression table that is not covered by `self.emitter`.\n    ///\n    /// This is useful for `CallResult` and `AtomicResult` expressions, which should not be covered by\n    /// `Emit` statements.\n    fn interrupt_emitter(\n        &mut self,\n        expression: ir::Expression,\n        span: Span,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref mut rctx)\n            | ExpressionContextType::Constant(Some(ref mut rctx)) => {\n                rctx.block\n                    .extend(rctx.emitter.finish(&rctx.function.expressions));\n            }\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}\n        }\n        let result = self.append_expression(expression, span);\n        match self.expr_type {\n            ExpressionContextType::Runtime(ref mut rctx)\n            | ExpressionContextType::Constant(Some(ref mut rctx)) => {\n                rctx.emitter.start(&rctx.function.expressions);\n            }\n            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}\n        }\n        result\n    }\n\n    /// Apply the WGSL Load Rule to `expr`.\n    ///\n    /// If `expr` is has type `ref<SC, T, A>`, perform a load to produce a value of type\n    /// `T`. Otherwise, return `expr` unchanged.\n    fn apply_load_rule(\n        &mut self,\n        expr: Typed<Handle<ir::Expression>>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        match expr {\n            Typed::Reference(pointer) => {\n                let load = ir::Expression::Load { pointer };\n                let span = self.get_expression_span(pointer);\n                self.append_expression(load, span)\n            }\n            Typed::Plain(handle) => Ok(handle),\n        }\n    }\n\n    fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> {\n        self.as_global().ensure_type_exists(None, inner)\n    }\n\n    /// Check that `expr` is an identifier resolving to a predeclared enumerant.\n    ///\n    /// The identifier must not have any template parameters.\n    ///\n    /// Return the name of the identifier, together with its span.\n    ///\n    /// Actually, this only checks that the identifier refers to some\n    /// predeclared object, not necessarily an enumerant. This should be good\n    /// enough, since the caller is going to compare the name against some list\n    /// of permitted enumerants anyway.\n    fn enumerant(\n        &self,\n        expr: Handle<ast::Expression<'source>>,\n    ) -> Result<'source, (&'source str, Span)> {\n        let span = self.ast_expressions.get_span(expr);\n        let expr = &self.ast_expressions[expr];\n\n        let ast::Expression::Ident(ref ident) = *expr else {\n            return Err(Box::new(Error::UnexpectedExprForEnumerant(span)));\n        };\n\n        let ast::TemplateElaboratedIdent {\n            ident: ast::IdentExpr::Unresolved(name),\n            ref template_list,\n            ..\n        } = *ident\n        else {\n            return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));\n        };\n\n        if self.globals.get(name).is_some() {\n            return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));\n        }\n\n        if !template_list.is_empty() {\n            return Err(Box::new(Error::UnexpectedTemplate(span)));\n        }\n\n        Ok((name, span))\n    }\n\n    fn var_address_space(\n        &self,\n        template_list: &[Handle<ast::Expression<'source>>],\n    ) -> Result<'source, ir::AddressSpace> {\n        let mut tl = TemplateListIter::new(Span::UNDEFINED, template_list);\n        let mut address_space = tl.maybe_address_space(self)?;\n        if let Some(ref mut address_space) = address_space {\n            tl.maybe_access_mode(address_space, self)?;\n        }\n        tl.finish(self)?;\n        Ok(address_space.unwrap_or(ir::AddressSpace::Handle))\n    }\n}\n\nstruct ArgumentContext<'ctx, 'source> {\n    args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,\n    min_args: u32,\n    args_used: u32,\n    total_args: u32,\n    span: Span,\n}\n\nimpl<'source> ArgumentContext<'_, 'source> {\n    pub fn finish(self) -> Result<'source, ()> {\n        if self.args.len() == 0 {\n            Ok(())\n        } else {\n            Err(Box::new(Error::WrongArgumentCount {\n                found: self.total_args,\n                expected: self.min_args..self.args_used + 1,\n                span: self.span,\n            }))\n        }\n    }\n\n    pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> {\n        match self.args.next().copied() {\n            Some(arg) => {\n                self.args_used += 1;\n                Ok(arg)\n            }\n            None => Err(Box::new(Error::WrongArgumentCount {\n                found: self.total_args,\n                expected: self.min_args..self.args_used + 1,\n                span: self.span,\n            })),\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\nenum Declared<T> {\n    /// Value declared as const\n    Const(T),\n\n    /// Value declared as non-const\n    Runtime(T),\n}\n\nimpl<T> Declared<T> {\n    fn runtime(self) -> T {\n        match self {\n            Declared::Const(t) | Declared::Runtime(t) => t,\n        }\n    }\n\n    fn const_time(self) -> Option<T> {\n        match self {\n            Declared::Const(t) => Some(t),\n            Declared::Runtime(_) => None,\n        }\n    }\n}\n\n/// WGSL type annotations on expressions, types, values, etc.\n///\n/// Naga and WGSL types are very close, but Naga lacks WGSL's `ref` types, which\n/// we need to know to apply the Load Rule. This enum carries some WGSL or Naga\n/// datum along with enough information to determine its corresponding WGSL\n/// type.\n///\n/// The `T` type parameter can be any expression-like thing:\n///\n/// - `Typed<Handle<ir::Type>>` can represent a full WGSL type. For example,\n///   given some Naga `Pointer` type `ptr`, a WGSL reference type is a\n///   `Typed::Reference(ptr)` whereas a WGSL pointer type is a\n///   `Typed::Plain(ptr)`.\n///\n/// - `Typed<ir::Expression>` or `Typed<Handle<ir::Expression>>` can\n///   represent references similarly.\n///\n/// Use the `map` and `try_map` methods to convert from one expression\n/// representation to another.\n///\n/// [`Expression`]: ir::Expression\n#[derive(Debug, Copy, Clone)]\nenum Typed<T> {\n    /// A WGSL reference.\n    Reference(T),\n\n    /// A WGSL plain type.\n    Plain(T),\n}\n\nimpl<T> Typed<T> {\n    fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {\n        match self {\n            Self::Reference(v) => Typed::Reference(f(v)),\n            Self::Plain(v) => Typed::Plain(f(v)),\n        }\n    }\n\n    fn try_map<U, E>(\n        self,\n        mut f: impl FnMut(T) -> core::result::Result<U, E>,\n    ) -> core::result::Result<Typed<U>, E> {\n        Ok(match self {\n            Self::Reference(expr) => Typed::Reference(f(expr)?),\n            Self::Plain(expr) => Typed::Plain(f(expr)?),\n        })\n    }\n\n    fn ref_or<E>(self, error: E) -> core::result::Result<T, E> {\n        match self {\n            Self::Reference(v) => Ok(v),\n            Self::Plain(_) => Err(error),\n        }\n    }\n}\n\n/// A single vector component or swizzle.\n///\n/// This represents the things that can appear after the `.` in a vector access\n/// expression: either a single component name, or a series of them,\n/// representing a swizzle.\nenum Components {\n    Single(u32),\n    Swizzle {\n        size: ir::VectorSize,\n        pattern: [ir::SwizzleComponent; 4],\n    },\n}\n\nimpl Components {\n    const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> {\n        use ir::SwizzleComponent as Sc;\n        match letter {\n            'x' | 'r' => Some(Sc::X),\n            'y' | 'g' => Some(Sc::Y),\n            'z' | 'b' => Some(Sc::Z),\n            'w' | 'a' => Some(Sc::W),\n            _ => None,\n        }\n    }\n\n    fn single_component(name: &str, name_span: Span) -> Result<'_, u32> {\n        let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;\n        match Self::letter_component(ch) {\n            Some(sc) => Ok(sc as u32),\n            None => Err(Box::new(Error::BadAccessor(name_span))),\n        }\n    }\n\n    /// Construct a `Components` value from a 'member' name, like `\"wzy\"` or `\"x\"`.\n    ///\n    /// Use `name_span` for reporting errors in parsing the component string.\n    fn new(name: &str, name_span: Span) -> Result<'_, Self> {\n        let size = match name.len() {\n            1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),\n            2 => ir::VectorSize::Bi,\n            3 => ir::VectorSize::Tri,\n            4 => ir::VectorSize::Quad,\n            _ => return Err(Box::new(Error::BadAccessor(name_span))),\n        };\n\n        let mut pattern = [ir::SwizzleComponent::X; 4];\n        for (comp, ch) in pattern.iter_mut().zip(name.chars()) {\n            *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;\n        }\n\n        if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w'))\n            || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a'))\n        {\n            Ok(Components::Swizzle { size, pattern })\n        } else {\n            Err(Box::new(Error::BadAccessor(name_span)))\n        }\n    }\n}\n\n/// An `ast::GlobalDecl` for which we have built the Naga IR equivalent.\nenum LoweredGlobalDecl {\n    Function {\n        handle: Handle<ir::Function>,\n        must_use: bool,\n    },\n    Var(Handle<ir::GlobalVariable>),\n    Const(Handle<ir::Constant>),\n    Override(Handle<ir::Override>),\n    Type(Handle<ir::Type>),\n    EntryPoint(usize),\n}\n\nenum Texture {\n    Gather,\n    GatherCompare,\n\n    Sample,\n    SampleBias,\n    SampleCompare,\n    SampleCompareLevel,\n    SampleGrad,\n    SampleLevel,\n    SampleBaseClampToEdge,\n}\n\nimpl Texture {\n    pub fn map(word: &str) -> Option<Self> {\n        Some(match word {\n            \"textureGather\" => Self::Gather,\n            \"textureGatherCompare\" => Self::GatherCompare,\n\n            \"textureSample\" => Self::Sample,\n            \"textureSampleBias\" => Self::SampleBias,\n            \"textureSampleCompare\" => Self::SampleCompare,\n            \"textureSampleCompareLevel\" => Self::SampleCompareLevel,\n            \"textureSampleGrad\" => Self::SampleGrad,\n            \"textureSampleLevel\" => Self::SampleLevel,\n            \"textureSampleBaseClampToEdge\" => Self::SampleBaseClampToEdge,\n            _ => return None,\n        })\n    }\n\n    pub const fn min_argument_count(&self) -> u32 {\n        match *self {\n            Self::Gather => 3,\n            Self::GatherCompare => 4,\n\n            Self::Sample => 3,\n            Self::SampleBias => 5,\n            Self::SampleCompare => 5,\n            Self::SampleCompareLevel => 5,\n            Self::SampleGrad => 6,\n            Self::SampleLevel => 5,\n            Self::SampleBaseClampToEdge => 3,\n        }\n    }\n}\n\nenum SubgroupGather {\n    BroadcastFirst,\n    Broadcast,\n    Shuffle,\n    ShuffleDown,\n    ShuffleUp,\n    ShuffleXor,\n    QuadBroadcast,\n}\n\nimpl SubgroupGather {\n    pub fn map(word: &str) -> Option<Self> {\n        Some(match word {\n            \"subgroupBroadcastFirst\" => Self::BroadcastFirst,\n            \"subgroupBroadcast\" => Self::Broadcast,\n            \"subgroupShuffle\" => Self::Shuffle,\n            \"subgroupShuffleDown\" => Self::ShuffleDown,\n            \"subgroupShuffleUp\" => Self::ShuffleUp,\n            \"subgroupShuffleXor\" => Self::ShuffleXor,\n            \"quadBroadcast\" => Self::QuadBroadcast,\n            _ => return None,\n        })\n    }\n}\n\n/// Whether a declaration accepts abstract types, or concretizes.\nenum AbstractRule {\n    /// This declaration concretizes its initialization expression.\n    Concretize,\n\n    /// This declaration can accept initializers with abstract types.\n    Allow,\n}\n\n/// Whether `@must_use` applies to a call expression.\n#[derive(Debug, Copy, Clone)]\nenum MustUse {\n    Yes,\n    No,\n}\n\nimpl From<bool> for MustUse {\n    fn from(value: bool) -> Self {\n        if value {\n            MustUse::Yes\n        } else {\n            MustUse::No\n        }\n    }\n}\n\npub struct Lowerer<'source, 'temp> {\n    index: &'temp Index<'source>,\n}\n\nimpl<'source, 'temp> Lowerer<'source, 'temp> {\n    pub const fn new(index: &'temp Index<'source>) -> Self {\n        Self { index }\n    }\n\n    pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> {\n        let mut module = ir::Module {\n            diagnostic_filters: tu.diagnostic_filters,\n            diagnostic_filter_leaf: tu.diagnostic_filter_leaf,\n            ..Default::default()\n        };\n\n        let mut ctx = GlobalContext {\n            enable_extensions: tu.enable_extensions,\n            ast_expressions: &tu.expressions,\n            globals: &mut FastHashMap::default(),\n            module: &mut module,\n            const_typifier: &mut Typifier::new(),\n            layouter: &mut proc::Layouter::default(),\n            global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(),\n        };\n        if !tu.doc_comments.is_empty() {\n            ctx.module.get_or_insert_default_doc_comments().module =\n                tu.doc_comments.iter().map(|s| s.to_string()).collect();\n        }\n\n        for decl_handle in self.index.visit_ordered() {\n            let span = tu.decls.get_span(decl_handle);\n            let decl = &tu.decls[decl_handle];\n\n            match decl.kind {\n                ast::GlobalDeclKind::Fn(ref f) => {\n                    let lowered_decl = self.function(f, span, &mut ctx)?;\n                    if !f.doc_comments.is_empty() {\n                        match lowered_decl {\n                            LoweredGlobalDecl::Function { handle, .. } => {\n                                ctx.module\n                                    .get_or_insert_default_doc_comments()\n                                    .functions\n                                    .insert(\n                                        handle,\n                                        f.doc_comments.iter().map(|s| s.to_string()).collect(),\n                                    );\n                            }\n                            LoweredGlobalDecl::EntryPoint(index) => {\n                                ctx.module\n                                    .get_or_insert_default_doc_comments()\n                                    .entry_points\n                                    .insert(\n                                        index,\n                                        f.doc_comments.iter().map(|s| s.to_string()).collect(),\n                                    );\n                            }\n                            _ => {}\n                        }\n                    }\n                    ctx.globals.insert(f.name.name, lowered_decl);\n                }\n                ast::GlobalDeclKind::Var(ref v) => {\n                    let explicit_ty =\n                        v.ty.as_ref()\n                            .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))\n                            .transpose()?;\n\n                    let (ty, initializer) = self.type_and_init(\n                        v.name,\n                        v.init,\n                        explicit_ty,\n                        AbstractRule::Concretize,\n                        &mut ctx.as_override(),\n                    )?;\n\n                    let binding = if let Some(ref binding) = v.binding {\n                        Some(ir::ResourceBinding {\n                            group: self.const_u32(binding.group, &mut ctx.as_const())?.0,\n                            binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,\n                        })\n                    } else {\n                        None\n                    };\n\n                    let space = ctx.as_const().var_address_space(&v.template_list)?;\n\n                    let handle = ctx.module.global_variables.append(\n                        ir::GlobalVariable {\n                            name: Some(v.name.name.to_string()),\n                            space,\n                            binding,\n                            ty,\n                            init: initializer,\n                            memory_decorations: v.memory_decorations,\n                        },\n                        span,\n                    );\n\n                    if !v.doc_comments.is_empty() {\n                        ctx.module\n                            .get_or_insert_default_doc_comments()\n                            .global_variables\n                            .insert(\n                                handle,\n                                v.doc_comments.iter().map(|s| s.to_string()).collect(),\n                            );\n                    }\n                    ctx.globals\n                        .insert(v.name.name, LoweredGlobalDecl::Var(handle));\n                }\n                ast::GlobalDeclKind::Const(ref c) => {\n                    let mut ectx = ctx.as_const();\n\n                    let explicit_ty =\n                        c.ty.as_ref()\n                            .map(|ast| self.resolve_ast_type(ast, &mut ectx))\n                            .transpose()?;\n\n                    let (ty, init) = self.type_and_init(\n                        c.name,\n                        Some(c.init),\n                        explicit_ty,\n                        AbstractRule::Allow,\n                        &mut ectx,\n                    )?;\n                    let init = init.expect(\"Global const must have init\");\n\n                    let handle = ctx.module.constants.append(\n                        ir::Constant {\n                            name: Some(c.name.name.to_string()),\n                            ty,\n                            init,\n                        },\n                        span,\n                    );\n\n                    ctx.globals\n                        .insert(c.name.name, LoweredGlobalDecl::Const(handle));\n                    if !c.doc_comments.is_empty() {\n                        ctx.module\n                            .get_or_insert_default_doc_comments()\n                            .constants\n                            .insert(\n                                handle,\n                                c.doc_comments.iter().map(|s| s.to_string()).collect(),\n                            );\n                    }\n                }\n                ast::GlobalDeclKind::Override(ref o) => {\n                    let explicit_ty =\n                        o.ty.as_ref()\n                            .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))\n                            .transpose()?;\n\n                    let mut ectx = ctx.as_override();\n\n                    let (ty, init) = self.type_and_init(\n                        o.name,\n                        o.init,\n                        explicit_ty,\n                        AbstractRule::Concretize,\n                        &mut ectx,\n                    )?;\n\n                    let id =\n                        o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))\n                            .transpose()?;\n\n                    let id = if let Some((id, id_span)) = id {\n                        Some(\n                            u16::try_from(id)\n                                .map_err(|_| Error::PipelineConstantIDValue(id_span))?,\n                        )\n                    } else {\n                        None\n                    };\n\n                    let handle = ctx.module.overrides.append(\n                        ir::Override {\n                            name: Some(o.name.name.to_string()),\n                            id,\n                            ty,\n                            init,\n                        },\n                        span,\n                    );\n\n                    ctx.globals\n                        .insert(o.name.name, LoweredGlobalDecl::Override(handle));\n                }\n                ast::GlobalDeclKind::Struct(ref s) => {\n                    let handle = self.r#struct(s, span, &mut ctx)?;\n                    ctx.globals\n                        .insert(s.name.name, LoweredGlobalDecl::Type(handle));\n                    if !s.doc_comments.is_empty() {\n                        ctx.module\n                            .get_or_insert_default_doc_comments()\n                            .types\n                            .insert(\n                                handle,\n                                s.doc_comments.iter().map(|s| s.to_string()).collect(),\n                            );\n                    }\n                }\n                ast::GlobalDeclKind::Type(ref alias) => {\n                    let ty = self.resolve_named_ast_type(\n                        &alias.ty,\n                        alias.name.name.to_string(),\n                        &mut ctx.as_const(),\n                    )?;\n                    ctx.globals\n                        .insert(alias.name.name, LoweredGlobalDecl::Type(ty));\n                }\n                ast::GlobalDeclKind::ConstAssert(condition) => {\n                    let condition = self.expression(condition, &mut ctx.as_const())?;\n\n                    let span = ctx.module.global_expressions.get_span(condition);\n                    match ctx\n                        .module\n                        .to_ctx()\n                        .get_const_val_from(condition, &ctx.module.global_expressions)\n                    {\n                        Ok(true) => Ok(()),\n                        Ok(false) => Err(Error::ConstAssertFailed(span)),\n                        Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {\n                            unreachable!()\n                        }\n                        Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),\n                    }?;\n                }\n            }\n        }\n\n        // Constant evaluation may leave abstract-typed literals and\n        // compositions in expression arenas, so we need to compact the module\n        // to remove unused expressions and types.\n        crate::compact::compact(&mut module, KeepUnused::Yes);\n\n        Ok(module)\n    }\n\n    /// Obtain (inferred) type and initializer after automatic conversion\n    fn type_and_init(\n        &mut self,\n        name: ast::Ident<'source>,\n        init: Option<Handle<ast::Expression<'source>>>,\n        explicit_ty: Option<Handle<ir::Type>>,\n        abstract_rule: AbstractRule,\n        ectx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {\n        let ty;\n        let initializer;\n        match (init, explicit_ty) {\n            (Some(init), Some(explicit_ty)) => {\n                let init = self.expression_for_abstract(init, ectx)?;\n                let ty_res = proc::TypeResolution::Handle(explicit_ty);\n                let init = ectx\n                    .try_automatic_conversions(init, &ty_res, name.span)\n                    .map_err(|error| match *error {\n                        Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {\n                            name: name.span,\n                            expected: e.dest_type,\n                            got: e.source_type,\n                        }),\n                        _ => error,\n                    })?;\n\n                let init_ty = ectx.register_type(init)?;\n                if !ectx.module.compare_types(\n                    &proc::TypeResolution::Handle(explicit_ty),\n                    &proc::TypeResolution::Handle(init_ty),\n                ) {\n                    return Err(Box::new(Error::InitializationTypeMismatch {\n                        name: name.span,\n                        expected: ectx.type_to_string(explicit_ty),\n                        got: ectx.type_to_string(init_ty),\n                    }));\n                }\n                ty = explicit_ty;\n                initializer = Some(init);\n            }\n            (Some(init), None) => {\n                let mut init = self.expression_for_abstract(init, ectx)?;\n                if let AbstractRule::Concretize = abstract_rule {\n                    init = ectx.concretize(init)?;\n                }\n                ty = ectx.register_type(init)?;\n                initializer = Some(init);\n            }\n            (None, Some(explicit_ty)) => {\n                ty = explicit_ty;\n                initializer = None;\n            }\n            (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),\n        }\n        Ok((ty, initializer))\n    }\n\n    fn function(\n        &mut self,\n        f: &ast::Function<'source>,\n        span: Span,\n        ctx: &mut GlobalContext<'source, '_, '_>,\n    ) -> Result<'source, LoweredGlobalDecl> {\n        let mut local_table = FastHashMap::default();\n        let mut expressions = Arena::new();\n        let mut named_expressions = FastIndexMap::default();\n        let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();\n\n        let arguments = f\n            .arguments\n            .iter()\n            .enumerate()\n            .map(|(i, arg)| -> Result<'_, _> {\n                let ty = self.resolve_ast_type(&arg.ty, &mut ctx.as_const())?;\n                let expr =\n                    expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);\n                local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));\n                named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));\n                local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);\n\n                Ok(ir::FunctionArgument {\n                    name: Some(arg.name.name.to_string()),\n                    ty,\n                    binding: self.binding(&arg.binding, ty, ctx)?,\n                })\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        let result = f\n            .result\n            .as_ref()\n            .map(|res| -> Result<'_, _> {\n                let ty = self.resolve_ast_type(&res.ty, &mut ctx.as_const())?;\n                Ok(ir::FunctionResult {\n                    ty,\n                    binding: self.binding(&res.binding, ty, ctx)?,\n                })\n            })\n            .transpose()?;\n\n        let mut function = ir::Function {\n            name: Some(f.name.name.to_string()),\n            arguments,\n            result,\n            local_variables: Arena::new(),\n            expressions,\n            named_expressions: crate::NamedExpressions::default(),\n            body: ir::Block::default(),\n            diagnostic_filter_leaf: f.diagnostic_filter_leaf,\n        };\n\n        let mut typifier = Typifier::default();\n        let mut stmt_ctx = StatementContext {\n            enable_extensions: ctx.enable_extensions,\n            local_table: &mut local_table,\n            globals: ctx.globals,\n            ast_expressions: ctx.ast_expressions,\n            const_typifier: ctx.const_typifier,\n            typifier: &mut typifier,\n            layouter: ctx.layouter,\n            function: &mut function,\n            named_expressions: &mut named_expressions,\n            module: ctx.module,\n            local_expression_kind_tracker: &mut local_expression_kind_tracker,\n            global_expression_kind_tracker: ctx.global_expression_kind_tracker,\n        };\n        let mut body = self.block(&f.body, false, &mut stmt_ctx)?;\n        proc::ensure_block_returns(&mut body);\n\n        function.body = body;\n        function.named_expressions = named_expressions\n            .into_iter()\n            .map(|(key, (name, _))| (key, name))\n            .collect();\n\n        if let Some(ref entry) = f.entry_point {\n            let (workgroup_size, workgroup_size_overrides) =\n                if let Some(workgroup_size) = entry.workgroup_size {\n                    // TODO: replace with try_map once stabilized\n                    let mut workgroup_size_out = [1; 3];\n                    let mut workgroup_size_overrides_out = [None; 3];\n                    for (i, size) in workgroup_size.into_iter().enumerate() {\n                        if let Some(size_expr) = size {\n                            match self.const_u32(size_expr, &mut ctx.as_const()) {\n                                Ok(value) => {\n                                    workgroup_size_out[i] = value.0;\n                                }\n                                Err(err) => {\n                                    if let Error::ConstantEvaluatorError(ref ty, _) = *err {\n                                        match **ty {\n                                            proc::ConstantEvaluatorError::OverrideExpr => {\n                                                workgroup_size_overrides_out[i] =\n                                                    Some(self.workgroup_size_override(\n                                                        size_expr,\n                                                        &mut ctx.as_override(),\n                                                    )?);\n                                            }\n                                            _ => {\n                                                return Err(err);\n                                            }\n                                        }\n                                    } else {\n                                        return Err(err);\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {\n                        (workgroup_size_out, None)\n                    } else {\n                        (workgroup_size_out, Some(workgroup_size_overrides_out))\n                    }\n                } else {\n                    ([0; 3], None)\n                };\n\n            let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {\n                let var = match ctx.globals.get(var_name) {\n                    Some(&LoweredGlobalDecl::Var(handle)) => handle,\n                    Some(_) => {\n                        return Err(Box::new(Error::ExpectedGlobalVariable {\n                            name_span: var_span,\n                        }))\n                    }\n                    None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),\n                };\n\n                let mut info = ctx.module.analyze_mesh_shader_info(var);\n                if let Some(h) = info.1[0] {\n                    info.0.max_vertices_override = Some(\n                        ctx.module\n                            .global_expressions\n                            .append(crate::Expression::Override(h), Span::UNDEFINED),\n                    );\n                }\n                if let Some(h) = info.1[1] {\n                    info.0.max_primitives_override = Some(\n                        ctx.module\n                            .global_expressions\n                            .append(crate::Expression::Override(h), Span::UNDEFINED),\n                    );\n                }\n\n                Some(info.0)\n            } else {\n                None\n            };\n\n            let task_payload = if let Some((var_name, var_span)) = entry.task_payload {\n                Some(match ctx.globals.get(var_name) {\n                    Some(&LoweredGlobalDecl::Var(handle)) => handle,\n                    Some(_) => {\n                        return Err(Box::new(Error::ExpectedGlobalVariable {\n                            name_span: var_span,\n                        }))\n                    }\n                    None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),\n                })\n            } else {\n                None\n            };\n\n            let incoming_ray_payload =\n                if let Some((var_name, var_span)) = entry.ray_incoming_payload {\n                    Some(match ctx.globals.get(var_name) {\n                        Some(&LoweredGlobalDecl::Var(handle)) => handle,\n                        Some(_) => {\n                            return Err(Box::new(Error::ExpectedGlobalVariable {\n                                name_span: var_span,\n                            }))\n                        }\n                        None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),\n                    })\n                } else {\n                    None\n                };\n\n            ctx.module.entry_points.push(ir::EntryPoint {\n                name: f.name.name.to_string(),\n                stage: entry.stage,\n                early_depth_test: entry.early_depth_test,\n                workgroup_size,\n                workgroup_size_overrides,\n                function,\n                mesh_info,\n                task_payload,\n                incoming_ray_payload,\n            });\n            Ok(LoweredGlobalDecl::EntryPoint(\n                ctx.module.entry_points.len() - 1,\n            ))\n        } else {\n            let handle = ctx.module.functions.append(function, span);\n            Ok(LoweredGlobalDecl::Function {\n                handle,\n                must_use: f.result.as_ref().is_some_and(|res| res.must_use),\n            })\n        }\n    }\n\n    fn workgroup_size_override(\n        &mut self,\n        size_expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let span = ctx.ast_expressions.get_span(size_expr);\n        let expr = self.expression(size_expr, ctx)?;\n        match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {\n            Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),\n            _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(\n                span,\n            ))),\n        }\n    }\n\n    fn block(\n        &mut self,\n        b: &ast::Block<'source>,\n        is_inside_loop: bool,\n        ctx: &mut StatementContext<'source, '_, '_>,\n    ) -> Result<'source, ir::Block> {\n        let mut block = ir::Block::default();\n\n        for stmt in b.stmts.iter() {\n            self.statement(stmt, &mut block, is_inside_loop, ctx)?;\n        }\n\n        Ok(block)\n    }\n\n    fn statement(\n        &mut self,\n        stmt: &ast::Statement<'source>,\n        block: &mut ir::Block,\n        is_inside_loop: bool,\n        ctx: &mut StatementContext<'source, '_, '_>,\n    ) -> Result<'source, ()> {\n        let out = match stmt.kind {\n            ast::StatementKind::Block(ref block) => {\n                let block = self.block(block, is_inside_loop, ctx)?;\n                ir::Statement::Block(block)\n            }\n            ast::StatementKind::LocalDecl(ref decl) => match *decl {\n                ast::LocalDecl::Let(ref l) => {\n                    let mut emitter = proc::Emitter::default();\n                    emitter.start(&ctx.function.expressions);\n\n                    let explicit_ty = l\n                        .ty\n                        .as_ref()\n                        .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))\n                        .transpose()?;\n\n                    let mut ectx = ctx.as_expression(block, &mut emitter);\n\n                    let (ty, initializer) = self.type_and_init(\n                        l.name,\n                        Some(l.init),\n                        explicit_ty,\n                        AbstractRule::Concretize,\n                        &mut ectx,\n                    )?;\n\n                    // We have this special check here for `let` declarations because the\n                    // validator doesn't check them (they are comingled with other things in\n                    // `named_expressions`; see <https://github.com/gfx-rs/wgpu/issues/7393>).\n                    // The check could go in `type_and_init`, but then we'd have to\n                    // distinguish whether override-sized is allowed. The error ought to use\n                    // the type's span, but `module.types.get_span(ty)` is `Span::UNDEFINED`\n                    // (see <https://github.com/gfx-rs/wgpu/issues/7951>).\n                    if ctx.module.types[ty]\n                        .inner\n                        .is_dynamically_sized(&ctx.module.types)\n                    {\n                        return Err(Box::new(Error::TypeNotConstructible(l.name.span)));\n                    }\n\n                    // We passed `Some()` to `type_and_init`, so we\n                    // will get a lowered initializer expression back.\n                    let initializer =\n                        initializer.expect(\"type_and_init did not return an initializer\");\n\n                    // The WGSL spec says that any expression that refers to a\n                    // `let`-bound variable is not a const expression. This\n                    // affects when errors must be reported, so we can't even\n                    // treat suitable `let` bindings as constant as an\n                    // optimization.\n                    ctx.local_expression_kind_tracker\n                        .force_non_const(initializer);\n\n                    block.extend(emitter.finish(&ctx.function.expressions));\n                    ctx.local_table\n                        .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));\n                    ctx.named_expressions\n                        .insert(initializer, (l.name.name.to_string(), l.name.span));\n\n                    return Ok(());\n                }\n                ast::LocalDecl::Var(ref v) => {\n                    let mut emitter = proc::Emitter::default();\n                    emitter.start(&ctx.function.expressions);\n\n                    let explicit_ty =\n                        v.ty.as_ref()\n                            .map(|ast| {\n                                self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))\n                            })\n                            .transpose()?;\n\n                    let mut ectx = ctx.as_expression(block, &mut emitter);\n                    let (ty, initializer) = self.type_and_init(\n                        v.name,\n                        v.init,\n                        explicit_ty,\n                        AbstractRule::Concretize,\n                        &mut ectx,\n                    )?;\n\n                    let (const_initializer, initializer) = {\n                        match initializer {\n                            Some(init) => {\n                                // It's not correct to hoist the initializer up\n                                // to the top of the function if:\n                                // - the initialization is inside a loop, and should\n                                //   take place on every iteration, or\n                                // - the initialization is not a constant\n                                //   expression, so its value depends on the\n                                //   state at the point of initialization.\n                                if is_inside_loop\n                                    || !ctx.local_expression_kind_tracker.is_const_or_override(init)\n                                {\n                                    (None, Some(init))\n                                } else {\n                                    (Some(init), None)\n                                }\n                            }\n                            None => (None, None),\n                        }\n                    };\n\n                    let var = ctx.function.local_variables.append(\n                        ir::LocalVariable {\n                            name: Some(v.name.name.to_string()),\n                            ty,\n                            init: const_initializer,\n                        },\n                        stmt.span,\n                    );\n\n                    let handle = ctx\n                        .as_expression(block, &mut emitter)\n                        .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;\n                    block.extend(emitter.finish(&ctx.function.expressions));\n                    ctx.local_table\n                        .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));\n\n                    match initializer {\n                        Some(initializer) => ir::Statement::Store {\n                            pointer: handle,\n                            value: initializer,\n                        },\n                        None => return Ok(()),\n                    }\n                }\n                ast::LocalDecl::Const(ref c) => {\n                    let mut emitter = proc::Emitter::default();\n                    emitter.start(&ctx.function.expressions);\n\n                    let ectx = &mut ctx.as_const(block, &mut emitter);\n\n                    let explicit_ty =\n                        c.ty.as_ref()\n                            .map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))\n                            .transpose()?;\n\n                    let (_ty, init) = self.type_and_init(\n                        c.name,\n                        Some(c.init),\n                        explicit_ty,\n                        AbstractRule::Allow,\n                        &mut ectx.as_const(),\n                    )?;\n                    let init = init.expect(\"Local const must have init\");\n\n                    block.extend(emitter.finish(&ctx.function.expressions));\n                    ctx.local_table\n                        .insert(c.handle, Declared::Const(Typed::Plain(init)));\n                    return Ok(());\n                }\n            },\n            ast::StatementKind::If {\n                condition,\n                ref accept,\n                ref reject,\n            } => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let condition =\n                    self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;\n                block.extend(emitter.finish(&ctx.function.expressions));\n\n                let accept = self.block(accept, is_inside_loop, ctx)?;\n                let reject = self.block(reject, is_inside_loop, ctx)?;\n\n                ir::Statement::If {\n                    condition,\n                    accept,\n                    reject,\n                }\n            }\n            ast::StatementKind::Switch {\n                selector,\n                ref cases,\n            } => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let mut ectx = ctx.as_expression(block, &mut emitter);\n\n                // Determine the scalar type of the selector and case expressions, find the\n                // consensus type for automatic conversion, then convert them.\n                let (mut exprs, spans) = core::iter::once(selector)\n                    .chain(cases.iter().filter_map(|case| match case.value {\n                        ast::SwitchValue::Expr(expr) => Some(expr),\n                        ast::SwitchValue::Default => None,\n                    }))\n                    .enumerate()\n                    .map(|(i, expr)| {\n                        let span = ectx.ast_expressions.get_span(expr);\n                        let expr = self.expression_for_abstract(expr, &mut ectx)?;\n                        let ty = resolve_inner!(ectx, expr);\n                        match *ty {\n                            ir::TypeInner::Scalar(\n                                ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,\n                            ) => Ok((expr, span)),\n                            _ => match i {\n                                0 => Err(Box::new(Error::InvalidSwitchSelector { span })),\n                                _ => Err(Box::new(Error::InvalidSwitchCase { span })),\n                            },\n                        }\n                    })\n                    .collect::<Result<(Vec<_>, Vec<_>)>>()?;\n\n                let mut consensus =\n                    ectx.automatic_conversion_consensus(None, &exprs)\n                        .map_err(|span_idx| Error::SwitchCaseTypeMismatch {\n                            span: spans[span_idx],\n                        })?;\n                // Concretize to I32 if the selector and all cases were abstract\n                if consensus == ir::Scalar::ABSTRACT_INT {\n                    consensus = ir::Scalar::I32;\n                }\n                for expr in &mut exprs {\n                    ectx.convert_to_leaf_scalar(expr, consensus)?;\n                }\n\n                block.extend(emitter.finish(&ctx.function.expressions));\n\n                let mut exprs = exprs.into_iter();\n                let selector = exprs\n                    .next()\n                    .expect(\"First element should be selector expression\");\n\n                let cases = cases\n                    .iter()\n                    .map(|case| {\n                        Ok(ir::SwitchCase {\n                            value: match case.value {\n                                ast::SwitchValue::Expr(expr) => {\n                                    let span = ctx.ast_expressions.get_span(expr);\n                                    let expr = exprs.next().expect(\n                                        \"Should yield expression for each SwitchValue::Expr case\",\n                                    );\n                                    match ctx\n                                        .module\n                                        .to_ctx()\n                                        .get_const_val_from(expr, &ctx.function.expressions)\n                                    {\n                                        Ok(ir::Literal::I32(value)) => ir::SwitchValue::I32(value),\n                                        Ok(ir::Literal::U32(value)) => ir::SwitchValue::U32(value),\n                                        _ => {\n                                            return Err(Box::new(Error::InvalidSwitchCase {\n                                                span,\n                                            }));\n                                        }\n                                    }\n                                }\n                                ast::SwitchValue::Default => ir::SwitchValue::Default,\n                            },\n                            body: self.block(&case.body, is_inside_loop, ctx)?,\n                            fall_through: case.fall_through,\n                        })\n                    })\n                    .collect::<Result<_>>()?;\n\n                ir::Statement::Switch { selector, cases }\n            }\n            ast::StatementKind::Loop {\n                ref body,\n                ref continuing,\n                break_if,\n            } => {\n                let body = self.block(body, true, ctx)?;\n                let mut continuing = self.block(continuing, true, ctx)?;\n\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n                let break_if = break_if\n                    .map(|expr| {\n                        self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))\n                    })\n                    .transpose()?;\n                continuing.extend(emitter.finish(&ctx.function.expressions));\n\n                ir::Statement::Loop {\n                    body,\n                    continuing,\n                    break_if,\n                }\n            }\n            ast::StatementKind::Break => ir::Statement::Break,\n            ast::StatementKind::Continue => ir::Statement::Continue,\n            ast::StatementKind::Return { value: ast_value } => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let value;\n                if let Some(ast_expr) = ast_value {\n                    let result_ty = ctx.function.result.as_ref().map(|r| r.ty);\n                    let mut ectx = ctx.as_expression(block, &mut emitter);\n                    let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;\n\n                    if let Some(result_ty) = result_ty {\n                        let mut ectx = ctx.as_expression(block, &mut emitter);\n                        let resolution = proc::TypeResolution::Handle(result_ty);\n                        let converted =\n                            ectx.try_automatic_conversions(expr, &resolution, Span::default())?;\n                        value = Some(converted);\n                    } else {\n                        value = Some(expr);\n                    }\n                } else {\n                    value = None;\n                }\n                block.extend(emitter.finish(&ctx.function.expressions));\n\n                ir::Statement::Return { value }\n            }\n            ast::StatementKind::Kill => ir::Statement::Kill,\n            ast::StatementKind::Call(ref call_phrase) => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let _ = self.call(\n                    call_phrase,\n                    stmt.span,\n                    &mut ctx.as_expression(block, &mut emitter),\n                    true,\n                )?;\n                block.extend(emitter.finish(&ctx.function.expressions));\n                return Ok(());\n            }\n            ast::StatementKind::Assign {\n                target: ast_target,\n                op,\n                value,\n            } => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n                let target_span = ctx.ast_expressions.get_span(ast_target);\n\n                let mut ectx = ctx.as_expression(block, &mut emitter);\n                let target = self.expression_for_reference(ast_target, &mut ectx)?;\n                let target_handle = match target {\n                    Typed::Reference(handle) => handle,\n                    Typed::Plain(handle) => {\n                        let ty = ctx.invalid_assignment_type(handle);\n                        return Err(Box::new(Error::InvalidAssignment {\n                            span: target_span,\n                            ty,\n                        }));\n                    }\n                };\n\n                // Usually the value needs to be converted to match the type of\n                // the memory view you're assigning it to. The bit shift\n                // operators are exceptions, in that the right operand is always\n                // a `u32` or `vecN<u32>`.\n                let target_scalar = match op {\n                    Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {\n                        Some(ir::Scalar::U32)\n                    }\n                    _ => resolve_inner!(ectx, target_handle)\n                        .pointer_automatically_convertible_scalar(&ectx.module.types),\n                };\n\n                // Need to emit the LHS _before_ the RHS so that it is evaluated first.\n                let op_assign = if let Some(op) = op {\n                    Some((op, ectx.apply_load_rule(target)?))\n                } else {\n                    None\n                };\n\n                let value = self.expression_for_abstract(value, &mut ectx)?;\n                let mut value = match target_scalar {\n                    Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(\n                        value,\n                        target_scalar,\n                        target_span,\n                    )?,\n                    None => value,\n                };\n\n                let value = match op_assign {\n                    Some((op, mut left)) => {\n                        ectx.binary_op_splat(op, &mut left, &mut value)?;\n                        ectx.append_expression(\n                            ir::Expression::Binary {\n                                op,\n                                left,\n                                right: value,\n                            },\n                            stmt.span,\n                        )?\n                    }\n                    None => value,\n                };\n                block.extend(emitter.finish(&ctx.function.expressions));\n\n                ir::Statement::Store {\n                    pointer: target_handle,\n                    value,\n                }\n            }\n            ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let op = match stmt.kind {\n                    ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,\n                    ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,\n                    _ => unreachable!(),\n                };\n\n                let value_span = ctx.ast_expressions.get_span(value);\n                let target = self\n                    .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;\n                let target_handle = target.ref_or(Error::BadIncrDecrReferenceType(value_span))?;\n\n                let mut ectx = ctx.as_expression(block, &mut emitter);\n                let scalar = match *resolve_inner!(ectx, target_handle) {\n                    ir::TypeInner::ValuePointer {\n                        size: None, scalar, ..\n                    } => scalar,\n                    ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {\n                        ir::TypeInner::Scalar(scalar) => scalar,\n                        _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),\n                    },\n                    _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),\n                };\n                let literal = match scalar.kind {\n                    ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)\n                        .ok_or(Error::BadIncrDecrReferenceType(value_span))?,\n                    _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),\n                };\n\n                let right =\n                    ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;\n                let rctx = ectx.runtime_expression_ctx(stmt.span)?;\n                let left = rctx.function.expressions.append(\n                    ir::Expression::Load {\n                        pointer: target_handle,\n                    },\n                    value_span,\n                );\n                let value = rctx\n                    .function\n                    .expressions\n                    .append(ir::Expression::Binary { op, left, right }, stmt.span);\n                rctx.local_expression_kind_tracker\n                    .insert(left, proc::ExpressionKind::Runtime);\n                rctx.local_expression_kind_tracker\n                    .insert(value, proc::ExpressionKind::Runtime);\n\n                block.extend(emitter.finish(&ctx.function.expressions));\n                ir::Statement::Store {\n                    pointer: target_handle,\n                    value,\n                }\n            }\n            ast::StatementKind::ConstAssert(condition) => {\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let condition =\n                    self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;\n\n                let span = ctx.function.expressions.get_span(condition);\n                match ctx\n                    .module\n                    .to_ctx()\n                    .get_const_val_from(condition, &ctx.function.expressions)\n                {\n                    Ok(true) => Ok(()),\n                    Ok(false) => Err(Error::ConstAssertFailed(span)),\n                    Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {\n                        unreachable!()\n                    }\n                    Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),\n                }?;\n\n                block.extend(emitter.finish(&ctx.function.expressions));\n\n                return Ok(());\n            }\n            ast::StatementKind::Phony(expr) => {\n                // Remembered the RHS of the phony assignment as a named expression. This\n                // is important (1) to preserve the RHS for validation, (2) to track any\n                // referenced globals.\n                let mut emitter = proc::Emitter::default();\n                emitter.start(&ctx.function.expressions);\n\n                let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;\n                block.extend(emitter.finish(&ctx.function.expressions));\n                ctx.named_expressions\n                    .insert(value, (\"phony\".to_string(), stmt.span));\n                return Ok(());\n            }\n        };\n\n        block.push(out, stmt.span);\n\n        Ok(())\n    }\n\n    /// Lower `expr` and apply the Load Rule if possible.\n    ///\n    /// For the time being, this concretizes abstract values, to support\n    /// consumers that haven't been adapted to consume them yet. Consumers\n    /// prepared for abstract values can call [`expression_for_abstract`].\n    ///\n    /// [`expression_for_abstract`]: Lowerer::expression_for_abstract\n    fn expression(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let expr = self.expression_for_abstract(expr, ctx)?;\n        ctx.concretize(expr)\n    }\n\n    fn expression_for_abstract(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let expr = self.expression_for_reference(expr, ctx)?;\n        ctx.apply_load_rule(expr)\n    }\n\n    fn expression_with_leaf_scalar(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        scalar: ir::Scalar,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let unconverted = self.expression_for_abstract(expr, ctx)?;\n        ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())\n    }\n\n    fn expression_for_reference(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Typed<Handle<ir::Expression>>> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let expr = &ctx.ast_expressions[expr];\n\n        let expr: Typed<ir::Expression> = match *expr {\n            ast::Expression::Literal(literal) => {\n                let literal = match literal {\n                    ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),\n                    ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),\n                    ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),\n                    ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),\n                    ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),\n                    ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),\n                    ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),\n                    ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),\n                    ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),\n                    ast::Literal::Bool(b) => ir::Literal::Bool(b),\n                };\n                let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;\n                return Ok(Typed::Plain(handle));\n            }\n            ast::Expression::Ident(ast::TemplateElaboratedIdent {\n                ref template_list, ..\n            }) if !template_list.is_empty() => {\n                return Err(Box::new(Error::UnexpectedTemplate(span)))\n            }\n            ast::Expression::Ident(ast::TemplateElaboratedIdent {\n                ident: ast::IdentExpr::Local(local),\n                ..\n            }) => {\n                return ctx.local(&local, span);\n            }\n            ast::Expression::Ident(ast::TemplateElaboratedIdent {\n                ident: ast::IdentExpr::Unresolved(name),\n                ..\n            }) => {\n                let global = ctx\n                    .globals\n                    .get(name)\n                    .ok_or(Error::UnknownIdent(span, name))?;\n                let expr = match *global {\n                    LoweredGlobalDecl::Var(handle) => {\n                        let expr = ir::Expression::GlobalVariable(handle);\n                        let v = &ctx.module.global_variables[handle];\n                        match v.space {\n                            ir::AddressSpace::Handle => Typed::Plain(expr),\n                            _ => Typed::Reference(expr),\n                        }\n                    }\n                    LoweredGlobalDecl::Const(handle) => {\n                        Typed::Plain(ir::Expression::Constant(handle))\n                    }\n                    LoweredGlobalDecl::Override(handle) => {\n                        Typed::Plain(ir::Expression::Override(handle))\n                    }\n                    LoweredGlobalDecl::Function { .. }\n                    | LoweredGlobalDecl::Type(_)\n                    | LoweredGlobalDecl::EntryPoint(_) => {\n                        return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));\n                    }\n                };\n\n                return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));\n            }\n            ast::Expression::Unary { op, expr } => {\n                let expr = self.expression_for_abstract(expr, ctx)?;\n                Typed::Plain(ir::Expression::Unary { op, expr })\n            }\n            ast::Expression::AddrOf(expr) => {\n                // The `&` operator simply converts a reference to a pointer. And since a\n                // reference is required, the Load Rule is not applied.\n                match self.expression_for_reference(expr, ctx)? {\n                    Typed::Reference(handle) => {\n                        let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];\n                        if let &ir::Expression::Access { base, .. }\n                        | &ir::Expression::AccessIndex { base, .. } = expr\n                        {\n                            if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {\n                                if matches!(\n                                    *ty.inner_with(&ctx.module.types),\n                                    ir::TypeInner::Vector { .. },\n                                ) {\n                                    return Err(Box::new(Error::InvalidAddrOfOperand(\n                                        ctx.get_expression_span(handle),\n                                    )));\n                                }\n                            }\n                        }\n                        // No code is generated. We just declare the reference a pointer now.\n                        return Ok(Typed::Plain(handle));\n                    }\n                    Typed::Plain(_) => {\n                        return Err(Box::new(Error::NotReference(\n                            \"the operand of the `&` operator\",\n                            span,\n                        )));\n                    }\n                }\n            }\n            ast::Expression::Deref(expr) => {\n                // The pointer we dereference must be loaded.\n                let pointer = self.expression(expr, ctx)?;\n\n                if resolve_inner!(ctx, pointer).pointer_space().is_none() {\n                    return Err(Box::new(Error::NotPointer(span)));\n                }\n\n                // No code is generated. We just declare the pointer a reference now.\n                return Ok(Typed::Reference(pointer));\n            }\n            ast::Expression::Binary { op, left, right } => {\n                self.binary(op, left, right, span, ctx)?\n            }\n            ast::Expression::Call(ref call_phrase) => {\n                let handle = self\n                    .call(call_phrase, span, ctx, false)?\n                    .ok_or(Error::FunctionReturnsVoid(span))?;\n                return Ok(Typed::Plain(handle));\n            }\n            ast::Expression::Index { base, index } => {\n                let mut lowered_base = self.expression_for_reference(base, ctx)?;\n                let index = self.expression(index, ctx)?;\n\n                // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>\n                // Declare pointer as reference\n                if let Typed::Plain(handle) = lowered_base {\n                    if resolve_inner!(ctx, handle).pointer_space().is_some() {\n                        lowered_base = Typed::Reference(handle);\n                    }\n                }\n\n                lowered_base.try_map(|base| match ctx.get_const_val(index).ok() {\n                    Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),\n                    None => {\n                        // When an abstract array value e is indexed by an expression\n                        // that is not a const-expression, then the array is concretized\n                        // before the index is applied.\n                        // https://www.w3.org/TR/WGSL/#array-access-expr\n                        // Also applies to vectors and matrices.\n                        let base = ctx.concretize(base)?;\n                        Ok(ir::Expression::Access { base, index })\n                    }\n                })?\n            }\n            ast::Expression::Member { base, ref field } => {\n                let mut lowered_base = self.expression_for_reference(base, ctx)?;\n\n                // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>\n                // Declare pointer as reference\n                if let Typed::Plain(handle) = lowered_base {\n                    if resolve_inner!(ctx, handle).pointer_space().is_some() {\n                        lowered_base = Typed::Reference(handle);\n                    }\n                }\n\n                let temp_ty;\n                let composite_type: &ir::TypeInner = match lowered_base {\n                    Typed::Reference(handle) => {\n                        temp_ty = resolve_inner!(ctx, handle)\n                            .pointer_base_type()\n                            .expect(\"In Typed::Reference(handle), handle must be a Naga pointer\");\n                        temp_ty.inner_with(&ctx.module.types)\n                    }\n\n                    Typed::Plain(handle) => {\n                        resolve_inner!(ctx, handle)\n                    }\n                };\n\n                let access = match *composite_type {\n                    ir::TypeInner::Struct { ref members, .. } => {\n                        let index = members\n                            .iter()\n                            .position(|m| m.name.as_deref() == Some(field.name))\n                            .ok_or(Error::BadAccessor(field.span))?\n                            as u32;\n\n                        lowered_base.map(|base| ir::Expression::AccessIndex { base, index })\n                    }\n                    ir::TypeInner::Vector { size: vec_size, .. } => {\n                        match Components::new(field.name, field.span)? {\n                            Components::Swizzle { size, pattern } => {\n                                for &component in pattern[..size as usize].iter() {\n                                    if component as u8 >= vec_size as u8 {\n                                        return Err(Box::new(Error::BadAccessor(field.span)));\n                                    }\n                                }\n                                Typed::Plain(ir::Expression::Swizzle {\n                                    size,\n                                    vector: ctx.apply_load_rule(lowered_base)?,\n                                    pattern,\n                                })\n                            }\n                            Components::Single(index) => {\n                                if index >= vec_size as u32 {\n                                    return Err(Box::new(Error::BadAccessor(field.span)));\n                                }\n                                lowered_base.map(|base| ir::Expression::AccessIndex { base, index })\n                            }\n                        }\n                    }\n                    _ => return Err(Box::new(Error::BadAccessor(field.span))),\n                };\n\n                access\n            }\n        };\n\n        expr.try_map(|handle| ctx.append_expression(handle, span))\n    }\n\n    /// Generate IR for the short-circuiting operators `&&` and `||`.\n    ///\n    /// `binary` has already lowered the LHS expression and resolved its type.\n    fn logical(\n        &mut self,\n        op: crate::BinaryOperator,\n        left: Handle<crate::Expression>,\n        right: Handle<ast::Expression<'source>>,\n        span: Span,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Typed<crate::Expression>> {\n        debug_assert!(\n            op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr\n        );\n\n        if ctx.is_runtime() {\n            // To simulate short-circuiting behavior, we want to generate IR\n            // like the following for `&&`. For `||`, the condition is `!_lhs`\n            // and the else value is `true`.\n            //\n            // var _e0: bool;\n            // if _lhs {\n            //     _e0 = _rhs;\n            // } else {\n            //     _e0 = false;\n            // }\n\n            let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {\n                let condition = left;\n                let else_val = ctx.append_expression(\n                    crate::Expression::Literal(crate::Literal::Bool(false)),\n                    span,\n                )?;\n                (condition, else_val)\n            } else {\n                let condition = ctx.append_expression(\n                    crate::Expression::Unary {\n                        op: crate::UnaryOperator::LogicalNot,\n                        expr: left,\n                    },\n                    span,\n                )?;\n                let else_val = ctx.append_expression(\n                    crate::Expression::Literal(crate::Literal::Bool(true)),\n                    span,\n                )?;\n                (condition, else_val)\n            };\n\n            let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));\n\n            let rctx = ctx.runtime_expression_ctx(span)?;\n            let result_var = rctx.function.local_variables.append(\n                crate::LocalVariable {\n                    name: None,\n                    ty: bool_ty,\n                    init: None,\n                },\n                span,\n            );\n            let pointer =\n                ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;\n\n            let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {\n                let right = self.expression_for_abstract(right, ctx)?;\n                ctx.grow_types(right)?;\n                Ok(right)\n            })?;\n\n            accept.push(\n                crate::Statement::Store {\n                    pointer,\n                    value: right,\n                },\n                span,\n            );\n\n            let mut reject = crate::Block::with_capacity(1);\n            reject.push(\n                crate::Statement::Store {\n                    pointer,\n                    value: else_val,\n                },\n                span,\n            );\n\n            let rctx = ctx.runtime_expression_ctx(span)?;\n            rctx.block.push(\n                crate::Statement::If {\n                    condition,\n                    accept,\n                    reject,\n                },\n                span,\n            );\n\n            Ok(Typed::Reference(crate::Expression::LocalVariable(\n                result_var,\n            )))\n        } else {\n            let left_val: Option<bool> = ctx.get_const_val(left).ok();\n\n            if left_val.is_some_and(|left_val| {\n                op == crate::BinaryOperator::LogicalAnd && !left_val\n                    || op == crate::BinaryOperator::LogicalOr && left_val\n            }) {\n                // Short-circuit behavior: don't evaluate the RHS.\n\n                // TODO(https://github.com/gfx-rs/wgpu/issues/8440): We shouldn't ignore the\n                // RHS completely, it should still be type-checked. Preserving it for type\n                // checking is a bit tricky, because we're trying to produce an expression\n                // for a const context, but the RHS is allowed to have things that aren't\n                // const.\n\n                Ok(Typed::Plain(ctx.get(left).clone()))\n            } else {\n                // Evaluate the RHS and construct the entire binary expression as we\n                // normally would. This case applies to well-formed constant logical\n                // expressions that don't short-circuit (handled by the constant evaluator\n                // shortly), to override expressions (handled when overrides are processed)\n                // and to non-well-formed expressions (rejected by type checking).\n                let right = self.expression_for_abstract(right, ctx)?;\n                ctx.grow_types(right)?;\n\n                Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))\n            }\n        }\n    }\n\n    fn type_expression(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let expr = &ctx.ast_expressions[expr];\n\n        let ident = match *expr {\n            ast::Expression::Ident(ref ident) => ident,\n            _ => return Err(Box::new(Error::UnexpectedExprForTypeExpression(span))),\n        };\n\n        self.type_specifier(ident, ctx, None)\n    }\n\n    fn type_specifier(\n        &mut self,\n        ident: &ast::TemplateElaboratedIdent<'source>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n        alias_name: Option<String>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        let &ast::TemplateElaboratedIdent {\n            ref ident,\n            ident_span,\n            ref template_list,\n            ..\n        } = ident;\n\n        let ident = match *ident {\n            ast::IdentExpr::Unresolved(ident) => ident,\n            ast::IdentExpr::Local(_) => {\n                // Since WGSL only supports module-scope type definitions and\n                // aliases, a local identifier can't possibly refer to a type.\n                return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));\n            }\n        };\n\n        let mut tl = TemplateListIter::new(ident_span, template_list);\n\n        if let Some(global) = ctx.globals.get(ident) {\n            let &LoweredGlobalDecl::Type(handle) = global else {\n                return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));\n            };\n\n            // Type generators can only be predeclared, so since `ident` refers\n            // to a module-scope declaration, the template parameter list should\n            // be empty.\n            tl.finish(ctx)?;\n            return Ok(handle);\n        }\n\n        // If `ident` doesn't resolve to a module-scope declaration, then it\n        // must resolve to a predeclared type or type generator.\n        let ty = conv::map_predeclared_type(&ctx.enable_extensions, ident_span, ident)?\n            .ok_or_else(|| Box::new(Error::UnknownIdent(ident_span, ident)))?;\n        let ty = self.finalize_type(ctx, ty, &mut tl, alias_name)?;\n\n        tl.finish(ctx)?;\n\n        Ok(ty)\n    }\n\n    /// Construct an [`ir::Type`] from a [`conv::PredeclaredType`] and a list of\n    /// template parameters.\n    ///\n    /// If we're processing a type alias, then `alias_name` is the name we\n    /// should use in the new `ir::Type`.\n    ///\n    /// For example, when parsing `vec3<f32>`, the caller would pass:\n    ///\n    /// - for `ty`, [`TypeGenerator::Vector`], and\n    ///\n    /// - for `tl`, an iterator producing a single [`Expression::Ident`] representing `f32`.\n    ///\n    /// From those arguments this function will return a handle for the\n    /// [`ir::Type`] representing `vec3<f32>`.\n    ///\n    /// [`TypeGenerator::Vector`]: conv::TypeGenerator::Vector\n    /// [`Expression::Ident`]: crate::front::wgsl::parse::ast::Expression::Ident\n    fn finalize_type(\n        &mut self,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n        ty: conv::PredeclaredType,\n        tl: &mut TemplateListIter<'_, 'source>,\n        alias_name: Option<String>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        let ty = match ty {\n            conv::PredeclaredType::TypeInner(ty_inner) => {\n                if let ir::TypeInner::Image {\n                    class: ir::ImageClass::External,\n                    ..\n                } = ty_inner\n                {\n                    // Other than the WGSL backend, every backend that supports\n                    // external textures does so by lowering them to a set of\n                    // ordinary textures and some parameters saying how to\n                    // sample from them. We don't know which backend will\n                    // consume the `Module` we're building, but in case it's not\n                    // WGSL, populate `SpecialTypes::external_texture_params`\n                    // and `SpecialTypes::external_texture_transfer_function`\n                    // with the types the backend will use for the parameter\n                    // buffer.\n                    //\n                    // Neither of these are the type we are lowering here:\n                    // that's an ordinary `TypeInner::Image`. But the fact we\n                    // are lowering a `texture_external` implies the backends\n                    // may need these additional types too.\n                    ctx.module.generate_external_texture_types();\n                }\n\n                ctx.as_global().ensure_type_exists(alias_name, ty_inner)\n            }\n            conv::PredeclaredType::RayDesc => ctx.module.generate_ray_desc_type(),\n            conv::PredeclaredType::RayIntersection => ctx.module.generate_ray_intersection_type(),\n            conv::PredeclaredType::TypeGenerator(type_generator) => {\n                let ty_inner = match type_generator {\n                    conv::TypeGenerator::Vector { size } => {\n                        let (scalar, _) = tl.scalar_ty(self, ctx)?;\n                        ir::TypeInner::Vector { size, scalar }\n                    }\n                    conv::TypeGenerator::Matrix { columns, rows } => {\n                        let (scalar, span) = tl.scalar_ty(self, ctx)?;\n                        if scalar.kind != ir::ScalarKind::Float {\n                            return Err(Box::new(Error::BadMatrixScalarKind(span, scalar)));\n                        }\n                        ir::TypeInner::Matrix {\n                            columns,\n                            rows,\n                            scalar,\n                        }\n                    }\n                    conv::TypeGenerator::Array => {\n                        let base = tl.ty(self, ctx)?;\n                        let size = tl.maybe_array_size(self, ctx)?;\n\n                        // Determine the size of the base type, if needed.\n                        ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {\n                            let LayoutErrorInner::TooLarge = err.inner else {\n                                unreachable!(\"unexpected layout error: {err:?}\");\n                            };\n                            // Lots of type definitions don't get spans, so this error\n                            // message may not be very useful.\n                            Box::new(Error::TypeTooLarge {\n                                span: ctx.module.types.get_span(err.ty),\n                            })\n                        })?;\n                        let stride = ctx.layouter[base].to_stride();\n\n                        ir::TypeInner::Array { base, size, stride }\n                    }\n                    conv::TypeGenerator::Atomic => {\n                        let (scalar, _) = tl.scalar_ty(self, ctx)?;\n                        ir::TypeInner::Atomic(scalar)\n                    }\n                    conv::TypeGenerator::Pointer => {\n                        let mut space = tl.address_space(ctx)?;\n                        let base = tl.ty(self, ctx)?;\n                        tl.maybe_access_mode(&mut space, ctx)?;\n                        ir::TypeInner::Pointer { base, space }\n                    }\n                    conv::TypeGenerator::SampledTexture {\n                        dim,\n                        arrayed,\n                        multi,\n                    } => {\n                        let (scalar, span) = tl.scalar_ty(self, ctx)?;\n                        let ir::Scalar { kind, width } = scalar;\n                        if width != 4 {\n                            return Err(Box::new(Error::BadTextureSampleType { span, scalar }));\n                        }\n                        ir::TypeInner::Image {\n                            dim,\n                            arrayed,\n                            class: ir::ImageClass::Sampled { kind, multi },\n                        }\n                    }\n                    conv::TypeGenerator::StorageTexture { dim, arrayed } => {\n                        let format = tl.storage_format(ctx)?;\n                        let access = tl.access_mode(ctx)?;\n                        ir::TypeInner::Image {\n                            dim,\n                            arrayed,\n                            class: ir::ImageClass::Storage { format, access },\n                        }\n                    }\n                    conv::TypeGenerator::BindingArray => {\n                        let base = tl.ty(self, ctx)?;\n                        let size = tl.maybe_array_size(self, ctx)?;\n                        ir::TypeInner::BindingArray { base, size }\n                    }\n                    conv::TypeGenerator::AccelerationStructure => {\n                        let vertex_return = tl.maybe_vertex_return(ctx)?;\n                        ir::TypeInner::AccelerationStructure { vertex_return }\n                    }\n                    conv::TypeGenerator::RayQuery => {\n                        let vertex_return = tl.maybe_vertex_return(ctx)?;\n                        ir::TypeInner::RayQuery { vertex_return }\n                    }\n                    conv::TypeGenerator::CooperativeMatrix { columns, rows } => {\n                        let (ty, span) = tl.ty_with_span(self, ctx)?;\n                        let ir::TypeInner::Scalar(scalar) = ctx.module.types[ty].inner else {\n                            return Err(Box::new(Error::UnsupportedCooperativeScalar(span)));\n                        };\n                        let role = tl.cooperative_role(ctx)?;\n                        ir::TypeInner::CooperativeMatrix {\n                            columns,\n                            rows,\n                            scalar,\n                            role,\n                        }\n                    }\n                };\n                ctx.as_global().ensure_type_exists(alias_name, ty_inner)\n            }\n        };\n        Ok(ty)\n    }\n\n    fn binary(\n        &mut self,\n        op: ir::BinaryOperator,\n        left: Handle<ast::Expression<'source>>,\n        right: Handle<ast::Expression<'source>>,\n        span: Span,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Typed<ir::Expression>> {\n        if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {\n            let left = self.expression_for_abstract(left, ctx)?;\n            ctx.grow_types(left)?;\n\n            if !matches!(\n                resolve_inner!(ctx, left),\n                &ir::TypeInner::Scalar(ir::Scalar::BOOL)\n            ) {\n                // Pass it through as-is, will fail validation\n                let right = self.expression_for_abstract(right, ctx)?;\n                ctx.grow_types(right)?;\n                Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))\n            } else {\n                self.logical(op, left, right, span, ctx)\n            }\n        } else {\n            // Load both operands.\n            let mut left = self.expression_for_abstract(left, ctx)?;\n            let mut right = self.expression_for_abstract(right, ctx)?;\n\n            // Convert `scalar op vector` to `vector op vector` by introducing\n            // `Splat` expressions.\n            ctx.binary_op_splat(op, &mut left, &mut right)?;\n\n            // Apply automatic conversions.\n            match op {\n                ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {\n                    // Shift operators require the right operand to be `u32` or\n                    // `vecN<u32>`. We can let the validator sort out vector length\n                    // issues, but the right operand must be, or convert to, a u32 leaf\n                    // scalar.\n                    right =\n                        ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;\n\n                    // Additionally, we must concretize the left operand if the right operand\n                    // is not a const-expression.\n                    // See https://www.w3.org/TR/WGSL/#overload-resolution-section.\n                    //\n                    // 2. Eliminate any candidate where one of its subexpressions resolves to\n                    // an abstract type after feasible automatic conversions, but another of\n                    // the candidate’s subexpressions is not a const-expression.\n                    //\n                    // We only have to explicitly do so for shifts as their operands may be\n                    // of different types - for other binary ops this is achieved by finding\n                    // the conversion consensus for both operands.\n                    if !ctx.is_const(right) {\n                        left = ctx.concretize(left)?;\n                    }\n                }\n\n                // All other operators follow the same pattern: reconcile the\n                // scalar leaf types. If there's no reconciliation possible,\n                // leave the expressions as they are: validation will report the\n                // problem.\n                _ => {\n                    ctx.grow_types(left)?;\n                    ctx.grow_types(right)?;\n                    if let Ok(consensus_scalar) =\n                        ctx.automatic_conversion_consensus(None, [left, right].iter())\n                    {\n                        ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;\n                        ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;\n                    }\n                }\n            }\n\n            Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))\n        }\n    }\n\n    /// Generate Naga IR for a call to a WGSL builtin function.\n    #[allow(clippy::too_many_arguments)]\n    fn call_builtin<'phrase>(\n        &mut self,\n        function_name: &'source str,\n        function_span: Span,\n        arguments: &[Handle<ast::Expression<'source>>],\n        template_params: &mut TemplateListIter<'phrase, 'source>,\n        call_span: Span,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n        is_statement: bool,\n    ) -> Result<'source, Option<(Handle<ir::Expression>, MustUse)>> {\n        let (expr, must_use) = if let Some(fun) = conv::map_relational_fun(function_name) {\n            let mut args = ctx.prepare_args(arguments, 1, function_span);\n            let argument = self.expression(args.next()?, ctx)?;\n            args.finish()?;\n\n            // Check for no-op all(bool) and any(bool):\n            let argument_unmodified = matches!(\n                fun,\n                ir::RelationalFunction::All | ir::RelationalFunction::Any\n            ) && {\n                matches!(\n                    resolve_inner!(ctx, argument),\n                    &ir::TypeInner::Scalar(ir::Scalar {\n                        kind: ir::ScalarKind::Bool,\n                        ..\n                    })\n                )\n            };\n\n            if argument_unmodified {\n                return Ok(Some((argument, MustUse::Yes)));\n            } else {\n                (ir::Expression::Relational { fun, argument }, MustUse::Yes)\n            }\n        } else if let Some((axis, ctrl)) = conv::map_derivative(function_name) {\n            let mut args = ctx.prepare_args(arguments, 1, function_span);\n            let expr = self.expression(args.next()?, ctx)?;\n            args.finish()?;\n\n            (\n                ir::Expression::Derivative { axis, ctrl, expr },\n                MustUse::Yes,\n            )\n        } else if let Some(fun) = conv::map_standard_fun(function_name) {\n            (\n                self.math_function_helper(function_span, fun, arguments, ctx)?,\n                MustUse::Yes,\n            )\n        } else if let Some(fun) = Texture::map(function_name) {\n            (\n                self.texture_sample_helper(fun, arguments, function_span, ctx)?,\n                MustUse::Yes,\n            )\n        } else if let Some((op, cop)) = conv::map_subgroup_operation(function_name) {\n            return Ok(Some((\n                self.subgroup_operation_helper(function_span, op, cop, arguments, ctx)?,\n                MustUse::Yes,\n            )));\n        } else if let Some(mode) = SubgroupGather::map(function_name) {\n            return Ok(Some((\n                self.subgroup_gather_helper(function_span, mode, arguments, ctx)?,\n                MustUse::Yes,\n            )));\n        } else if let Some(fun) = ir::AtomicFunction::map(function_name) {\n            return Ok(self\n                .atomic_helper(function_span, fun, arguments, is_statement, ctx)?\n                .map(|result| (result, MustUse::No)));\n        } else {\n            match function_name {\n                \"bitcast\" => {\n                    let ty = template_params.ty(self, ctx)?;\n\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let expr = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let element_scalar = match ctx.module.types[ty].inner {\n                        ir::TypeInner::Scalar(scalar) => scalar,\n                        ir::TypeInner::Vector { scalar, .. } => scalar,\n                        _ => {\n                            let ty_resolution = resolve!(ctx, expr);\n                            return Err(Box::new(Error::BadTypeCast {\n                                from_type: ctx.type_resolution_to_string(ty_resolution),\n                                span: function_span,\n                                to_type: ctx.type_to_string(ty),\n                            }));\n                        }\n                    };\n\n                    (\n                        ir::Expression::As {\n                            expr,\n                            kind: element_scalar.kind,\n                            convert: None,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"coopLoad\" | \"coopLoadT\" => {\n                    let row_major = function_name.ends_with(\"T\");\n                    let (matrix_ty, matrix_span) = template_params.ty_with_span(self, ctx)?;\n\n                    let mut args = ctx.prepare_args(arguments, 1, call_span);\n                    let pointer = self.expression(args.next()?, ctx)?;\n                    let (columns, rows, role) = match ctx.module.types[matrix_ty].inner {\n                        ir::TypeInner::CooperativeMatrix {\n                            columns,\n                            rows,\n                            role,\n                            ..\n                        } => (columns, rows, role),\n                        _ => return Err(Box::new(Error::InvalidCooperativeLoadType(matrix_span))),\n                    };\n                    let stride = if args.total_args > 1 {\n                        self.expression(args.next()?, ctx)?\n                    } else {\n                        // Infer the stride from the matrix type\n                        let stride = if row_major {\n                            columns as u32\n                        } else {\n                            rows as u32\n                        };\n                        ctx.append_expression(\n                            ir::Expression::Literal(ir::Literal::U32(stride)),\n                            Span::UNDEFINED,\n                        )?\n                    };\n                    args.finish()?;\n\n                    (\n                        crate::Expression::CooperativeLoad {\n                            columns,\n                            rows,\n                            role,\n                            data: crate::CooperativeData {\n                                pointer,\n                                stride,\n                                row_major,\n                            },\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"select\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n\n                    let reject_orig = args.next()?;\n                    let accept_orig = args.next()?;\n                    let mut values = [\n                        self.expression_for_abstract(reject_orig, ctx)?,\n                        self.expression_for_abstract(accept_orig, ctx)?,\n                    ];\n                    let condition = self.expression(args.next()?, ctx)?;\n\n                    args.finish()?;\n\n                    let diagnostic_details =\n                        |ctx: &ExpressionContext<'_, '_, '_>,\n                         ty_res: &proc::TypeResolution,\n                         orig_expr| {\n                            (\n                                ctx.ast_expressions.get_span(orig_expr),\n                                format!(\"`{}`\", ctx.as_diagnostic_display(ty_res)),\n                            )\n                        };\n                    for (&value, orig_value) in values.iter().zip([reject_orig, accept_orig]) {\n                        let value_ty_res = resolve!(ctx, value);\n                        if value_ty_res\n                            .inner_with(&ctx.module.types)\n                            .vector_size_and_scalar()\n                            .is_none()\n                        {\n                            let (arg_span, arg_type) =\n                                diagnostic_details(ctx, value_ty_res, orig_value);\n                            return Err(Box::new(Error::SelectUnexpectedArgumentType {\n                                arg_span,\n                                arg_type,\n                            }));\n                        }\n                    }\n                    let mut consensus_scalar = ctx\n                        .automatic_conversion_consensus(None, &values)\n                        .map_err(|_idx| {\n                            let [reject, accept] = values;\n                            let [(reject_span, reject_type), (accept_span, accept_type)] =\n                                [(reject_orig, reject), (accept_orig, accept)].map(\n                                    |(orig_expr, expr)| {\n                                        let ty_res = &ctx.typifier()[expr];\n                                        diagnostic_details(ctx, ty_res, orig_expr)\n                                    },\n                                );\n                            Error::SelectRejectAndAcceptHaveNoCommonType {\n                                reject_span,\n                                reject_type,\n                                accept_span,\n                                accept_type,\n                            }\n                        })?;\n                    if !ctx.is_const(condition) {\n                        consensus_scalar = consensus_scalar.concretize();\n                    }\n\n                    ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;\n\n                    let [reject, accept] = values;\n\n                    (\n                        ir::Expression::Select {\n                            reject,\n                            accept,\n                            condition,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"arrayLength\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let expr = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (ir::Expression::ArrayLength(expr), MustUse::Yes)\n                }\n                \"atomicLoad\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (ir::Expression::Load { pointer }, MustUse::No)\n                }\n                \"atomicStore\" => {\n                    let mut args = ctx.prepare_args(arguments, 2, function_span);\n                    let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;\n                    let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;\n                    args.finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .extend(rctx.emitter.finish(&rctx.function.expressions));\n                    rctx.emitter.start(&rctx.function.expressions);\n                    rctx.block\n                        .push(ir::Statement::Store { pointer, value }, function_span);\n                    return Ok(None);\n                }\n                \"atomicCompareExchangeWeak\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n\n                    let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;\n\n                    let compare = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;\n\n                    let value = args.next()?;\n                    let value_span = ctx.ast_expressions.get_span(value);\n                    let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;\n\n                    args.finish()?;\n\n                    let expression = match *resolve_inner!(ctx, value) {\n                        ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {\n                            ty: ctx.module.generate_predeclared_type(\n                                ir::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),\n                            ),\n                            comparison: true,\n                        },\n                        _ => return Err(Box::new(Error::InvalidAtomicOperandType(value_span))),\n                    };\n\n                    let result = ctx.interrupt_emitter(expression, function_span)?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::Atomic {\n                            pointer,\n                            fun: ir::AtomicFunction::Exchange {\n                                compare: Some(compare),\n                            },\n                            value,\n                            result: Some(result),\n                        },\n                        function_span,\n                    );\n                    return Ok(Some((result, MustUse::No)));\n                }\n                \"textureAtomicMin\" | \"textureAtomicMax\" | \"textureAtomicAdd\"\n                | \"textureAtomicAnd\" | \"textureAtomicOr\" | \"textureAtomicXor\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n\n                    let image = args.next()?;\n                    let image_span = ctx.ast_expressions.get_span(image);\n                    let image = self.expression(image, ctx)?;\n\n                    let coordinate = self.expression(args.next()?, ctx)?;\n\n                    let (_, arrayed) = ctx.image_data(image, image_span)?;\n                    let array_index = arrayed\n                        .then(|| {\n                            args.min_args += 1;\n                            self.expression(args.next()?, ctx)\n                        })\n                        .transpose()?;\n\n                    let value = self.expression(args.next()?, ctx)?;\n\n                    args.finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .extend(rctx.emitter.finish(&rctx.function.expressions));\n                    rctx.emitter.start(&rctx.function.expressions);\n                    let stmt = ir::Statement::ImageAtomic {\n                        image,\n                        coordinate,\n                        array_index,\n                        fun: match function_name {\n                            \"textureAtomicMin\" => ir::AtomicFunction::Min,\n                            \"textureAtomicMax\" => ir::AtomicFunction::Max,\n                            \"textureAtomicAdd\" => ir::AtomicFunction::Add,\n                            \"textureAtomicAnd\" => ir::AtomicFunction::And,\n                            \"textureAtomicOr\" => ir::AtomicFunction::InclusiveOr,\n                            \"textureAtomicXor\" => ir::AtomicFunction::ExclusiveOr,\n                            _ => unreachable!(),\n                        },\n                        value,\n                    };\n                    rctx.block.push(stmt, function_span);\n                    return Ok(None);\n                }\n                \"storageBarrier\" => {\n                    ctx.prepare_args(arguments, 0, function_span).finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::ControlBarrier(ir::Barrier::STORAGE),\n                        function_span,\n                    );\n                    return Ok(None);\n                }\n                \"workgroupBarrier\" => {\n                    ctx.prepare_args(arguments, 0, function_span).finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP),\n                        function_span,\n                    );\n                    return Ok(None);\n                }\n                \"subgroupBarrier\" => {\n                    ctx.prepare_args(arguments, 0, function_span).finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP),\n                        function_span,\n                    );\n                    return Ok(None);\n                }\n                \"textureBarrier\" => {\n                    ctx.prepare_args(arguments, 0, function_span).finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::ControlBarrier(ir::Barrier::TEXTURE),\n                        function_span,\n                    );\n                    return Ok(None);\n                }\n                \"workgroupUniformLoad\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let expr = args.next()?;\n                    args.finish()?;\n\n                    let pointer = self.expression(expr, ctx)?;\n                    let result_ty = match *resolve_inner!(ctx, pointer) {\n                        ir::TypeInner::Pointer {\n                            base,\n                            space: ir::AddressSpace::WorkGroup,\n                        } => match ctx.module.types[base].inner {\n                            // Match `Expression::Load` semantics:\n                            // loading through a pointer to `atomic<T>` produces a `T`.\n                            ir::TypeInner::Atomic(scalar) => ctx.module.types.insert(\n                                ir::Type {\n                                    name: None,\n                                    inner: ir::TypeInner::Scalar(scalar),\n                                },\n                                function_span,\n                            ),\n                            _ => base,\n                        },\n                        ir::TypeInner::ValuePointer {\n                            size,\n                            scalar,\n                            space: ir::AddressSpace::WorkGroup,\n                        } => ctx.module.types.insert(\n                            ir::Type {\n                                name: None,\n                                inner: match size {\n                                    Some(size) => ir::TypeInner::Vector { size, scalar },\n                                    None => ir::TypeInner::Scalar(scalar),\n                                },\n                            },\n                            function_span,\n                        ),\n                        _ => {\n                            let span = ctx.ast_expressions.get_span(expr);\n                            return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));\n                        }\n                    };\n                    let result = ctx.interrupt_emitter(\n                        ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },\n                        function_span,\n                    )?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::WorkGroupUniformLoad { pointer, result },\n                        function_span,\n                    );\n\n                    return Ok(Some((result, MustUse::Yes)));\n                }\n                \"textureStore\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n\n                    let image = args.next()?;\n                    let image_span = ctx.ast_expressions.get_span(image);\n                    let image = self.expression(image, ctx)?;\n\n                    let coordinate = self.expression(args.next()?, ctx)?;\n\n                    let (class, arrayed) = ctx.image_data(image, image_span)?;\n                    let array_index = arrayed\n                        .then(|| {\n                            args.min_args += 1;\n                            self.expression(args.next()?, ctx)\n                        })\n                        .transpose()?;\n                    let scalar = if let ir::ImageClass::Storage { format, .. } = class {\n                        format.into()\n                    } else {\n                        return Err(Box::new(Error::NotStorageTexture(image_span)));\n                    };\n\n                    let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;\n\n                    args.finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .extend(rctx.emitter.finish(&rctx.function.expressions));\n                    rctx.emitter.start(&rctx.function.expressions);\n                    let stmt = ir::Statement::ImageStore {\n                        image,\n                        coordinate,\n                        array_index,\n                        value,\n                    };\n                    rctx.block.push(stmt, function_span);\n                    return Ok(None);\n                }\n                \"textureLoad\" => {\n                    let mut args = ctx.prepare_args(arguments, 2, function_span);\n\n                    let image = args.next()?;\n                    let image_span = ctx.ast_expressions.get_span(image);\n                    let image = self.expression(image, ctx)?;\n\n                    let coordinate = self.expression(args.next()?, ctx)?;\n\n                    let (class, arrayed) = ctx.image_data(image, image_span)?;\n                    let array_index = arrayed\n                        .then(|| {\n                            args.min_args += 1;\n                            self.expression(args.next()?, ctx)\n                        })\n                        .transpose()?;\n\n                    let level = class\n                        .is_mipmapped()\n                        .then(|| {\n                            args.min_args += 1;\n                            self.expression(args.next()?, ctx)\n                        })\n                        .transpose()?;\n\n                    let sample = class\n                        .is_multisampled()\n                        .then(|| self.expression(args.next()?, ctx))\n                        .transpose()?;\n\n                    args.finish()?;\n\n                    (\n                        ir::Expression::ImageLoad {\n                            image,\n                            coordinate,\n                            array_index,\n                            level,\n                            sample,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"textureDimensions\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let image = self.expression(args.next()?, ctx)?;\n                    let level = args\n                        .next()\n                        .map(|arg| self.expression(arg, ctx))\n                        .ok()\n                        .transpose()?;\n                    args.finish()?;\n\n                    (\n                        ir::Expression::ImageQuery {\n                            image,\n                            query: ir::ImageQuery::Size { level },\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"textureNumLevels\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let image = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (\n                        ir::Expression::ImageQuery {\n                            image,\n                            query: ir::ImageQuery::NumLevels,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"textureNumLayers\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let image = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (\n                        ir::Expression::ImageQuery {\n                            image,\n                            query: ir::ImageQuery::NumLayers,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"textureNumSamples\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let image = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (\n                        ir::Expression::ImageQuery {\n                            image,\n                            query: ir::ImageQuery::NumSamples,\n                        },\n                        MustUse::Yes,\n                    )\n                }\n                \"rayQueryInitialize\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    let acceleration_structure = self.expression(args.next()?, ctx)?;\n                    let descriptor = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_ray_desc_type();\n                    let fun = ir::RayQueryFunction::Initialize {\n                        acceleration_structure,\n                        descriptor,\n                    };\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .extend(rctx.emitter.finish(&rctx.function.expressions));\n                    rctx.emitter.start(&rctx.function.expressions);\n                    rctx.block\n                        .push(ir::Statement::RayQuery { query, fun }, function_span);\n                    return Ok(None);\n                }\n                \"getCommittedHitVertexPositions\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_vertex_return_type();\n\n                    (\n                        ir::Expression::RayQueryVertexPositions {\n                            query,\n                            committed: true,\n                        },\n                        MustUse::No,\n                    )\n                }\n                \"getCandidateHitVertexPositions\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_vertex_return_type();\n\n                    (\n                        ir::Expression::RayQueryVertexPositions {\n                            query,\n                            committed: false,\n                        },\n                        MustUse::No,\n                    )\n                }\n                \"rayQueryProceed\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let result = ctx\n                        .interrupt_emitter(ir::Expression::RayQueryProceedResult, function_span)?;\n                    let fun = ir::RayQueryFunction::Proceed { result };\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .push(ir::Statement::RayQuery { query, fun }, function_span);\n                    return Ok(Some((result, MustUse::No)));\n                }\n                \"rayQueryGenerateIntersection\" => {\n                    let mut args = ctx.prepare_args(arguments, 2, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    let hit_t = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .push(ir::Statement::RayQuery { query, fun }, function_span);\n                    return Ok(None);\n                }\n                \"rayQueryConfirmIntersection\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let fun = ir::RayQueryFunction::ConfirmIntersection;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .push(ir::Statement::RayQuery { query, fun }, function_span);\n                    return Ok(None);\n                }\n                \"rayQueryTerminate\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let fun = ir::RayQueryFunction::Terminate;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .push(ir::Statement::RayQuery { query, fun }, function_span);\n                    return Ok(None);\n                }\n                \"rayQueryGetCommittedIntersection\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_ray_intersection_type();\n                    (\n                        ir::Expression::RayQueryGetIntersection {\n                            query,\n                            committed: true,\n                        },\n                        MustUse::No,\n                    )\n                }\n                \"rayQueryGetCandidateIntersection\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n                    let query = self.ray_query_pointer(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_ray_intersection_type();\n                    (\n                        ir::Expression::RayQueryGetIntersection {\n                            query,\n                            committed: false,\n                        },\n                        MustUse::No,\n                    )\n                }\n                \"subgroupBallot\" => {\n                    let mut args = ctx.prepare_args(arguments, 0, function_span);\n                    let predicate = if arguments.len() == 1 {\n                        Some(self.expression(args.next()?, ctx)?)\n                    } else {\n                        None\n                    };\n                    args.finish()?;\n\n                    let result =\n                        ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, function_span)?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        ir::Statement::SubgroupBallot { result, predicate },\n                        function_span,\n                    );\n                    return Ok(Some((result, MustUse::Yes)));\n                }\n                \"quadSwapX\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n\n                    let argument = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let ty = ctx.register_type(argument)?;\n\n                    let result = ctx.interrupt_emitter(\n                        crate::Expression::SubgroupOperationResult { ty },\n                        function_span,\n                    )?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        crate::Statement::SubgroupGather {\n                            mode: crate::GatherMode::QuadSwap(crate::Direction::X),\n                            argument,\n                            result,\n                        },\n                        function_span,\n                    );\n                    return Ok(Some((result, MustUse::Yes)));\n                }\n                \"quadSwapY\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n\n                    let argument = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let ty = ctx.register_type(argument)?;\n\n                    let result = ctx.interrupt_emitter(\n                        crate::Expression::SubgroupOperationResult { ty },\n                        function_span,\n                    )?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        crate::Statement::SubgroupGather {\n                            mode: crate::GatherMode::QuadSwap(crate::Direction::Y),\n                            argument,\n                            result,\n                        },\n                        function_span,\n                    );\n                    return Ok(Some((result, MustUse::Yes)));\n                }\n                \"quadSwapDiagonal\" => {\n                    let mut args = ctx.prepare_args(arguments, 1, function_span);\n\n                    let argument = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let ty = ctx.register_type(argument)?;\n\n                    let result = ctx.interrupt_emitter(\n                        crate::Expression::SubgroupOperationResult { ty },\n                        function_span,\n                    )?;\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        crate::Statement::SubgroupGather {\n                            mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),\n                            argument,\n                            result,\n                        },\n                        function_span,\n                    );\n                    return Ok(Some((result, MustUse::Yes)));\n                }\n                \"coopStore\" | \"coopStoreT\" => {\n                    let row_major = function_name.ends_with(\"T\");\n\n                    let mut args = ctx.prepare_args(arguments, 2, function_span);\n                    let target = self.expression(args.next()?, ctx)?;\n                    let pointer = self.expression(args.next()?, ctx)?;\n                    let stride = if args.total_args > 2 {\n                        self.expression(args.next()?, ctx)?\n                    } else {\n                        // Infer the stride from the matrix type\n                        let stride = match *resolve_inner!(ctx, target) {\n                            ir::TypeInner::CooperativeMatrix { columns, rows, .. } => {\n                                if row_major {\n                                    columns as u32\n                                } else {\n                                    rows as u32\n                                }\n                            }\n                            _ => 0,\n                        };\n                        ctx.append_expression(\n                            ir::Expression::Literal(ir::Literal::U32(stride)),\n                            Span::UNDEFINED,\n                        )?\n                    };\n                    args.finish()?;\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block.push(\n                        crate::Statement::CooperativeStore {\n                            target,\n                            data: crate::CooperativeData {\n                                pointer,\n                                stride,\n                                row_major,\n                            },\n                        },\n                        function_span,\n                    );\n                    return Ok(None);\n                }\n                \"coopMultiplyAdd\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n                    let a = self.expression(args.next()?, ctx)?;\n                    let b = self.expression(args.next()?, ctx)?;\n                    let c = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    (\n                        ir::Expression::CooperativeMultiplyAdd { a, b, c },\n                        MustUse::Yes,\n                    )\n                }\n                \"traceRay\" => {\n                    let mut args = ctx.prepare_args(arguments, 3, function_span);\n                    let acceleration_structure = self.expression(args.next()?, ctx)?;\n                    let descriptor = self.expression(args.next()?, ctx)?;\n                    let payload = self.expression(args.next()?, ctx)?;\n                    args.finish()?;\n\n                    let _ = ctx.module.generate_ray_desc_type();\n                    let fun = ir::RayPipelineFunction::TraceRay {\n                        acceleration_structure,\n                        descriptor,\n                        payload,\n                    };\n\n                    let rctx = ctx.runtime_expression_ctx(function_span)?;\n                    rctx.block\n                        .extend(rctx.emitter.finish(&rctx.function.expressions));\n                    rctx.emitter.start(&rctx.function.expressions);\n                    rctx.block\n                        .push(ir::Statement::RayPipelineFunction(fun), function_span);\n                    return Ok(None);\n                }\n                _ => return Err(Box::new(Error::UnknownIdent(function_span, function_name))),\n            }\n        };\n\n        let expr = ctx.append_expression(expr, function_span)?;\n        Ok(Some((expr, must_use)))\n    }\n\n    /// Generate Naga IR for call expressions and statements, and type\n    /// constructor expressions.\n    ///\n    /// The \"function\" being called is simply an `Ident` that we know refers to\n    /// some module-scope definition.\n    ///\n    /// - If it is the name of a type, then the expression is a type constructor\n    ///   expression: either constructing a value from components, a conversion\n    ///   expression, or a zero value expression.\n    ///\n    /// - If it is the name of a function, then we're generating a [`Call`]\n    ///   statement. We may be in the midst of generating code for an\n    ///   expression, in which case we must generate an `Emit` statement to\n    ///   force evaluation of the IR expressions we've generated so far, add the\n    ///   `Call` statement to the current block, and then resume generating\n    ///   expressions.\n    ///\n    /// [`Call`]: ir::Statement::Call\n    fn call(\n        &mut self,\n        call_phrase: &ast::CallPhrase<'source>,\n        span: Span,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n        is_statement: bool,\n    ) -> Result<'source, Option<Handle<ir::Expression>>> {\n        let function_name = match call_phrase.function.ident {\n            ast::IdentExpr::Unresolved(name) => name,\n            ast::IdentExpr::Local(_) => {\n                return Err(Box::new(Error::CalledLocalDecl(\n                    call_phrase.function.ident_span,\n                )))\n            }\n        };\n        let mut function_span = call_phrase.function.ident_span;\n        function_span.subsume(call_phrase.function.template_list_span);\n        let arguments = call_phrase.arguments.as_slice();\n\n        let mut tl = TemplateListIter::new(function_span, &call_phrase.function.template_list);\n\n        let result = match ctx.globals.get(function_name) {\n            Some(&LoweredGlobalDecl::Type(ty)) => {\n                // user-declared types can't make use of template lists\n                tl.finish(ctx)?;\n\n                let handle =\n                    self.construct(span, Constructor::Type(ty), function_span, arguments, ctx)?;\n                Some((handle, MustUse::Yes))\n            }\n            Some(\n                &LoweredGlobalDecl::Const(_)\n                | &LoweredGlobalDecl::Override(_)\n                | &LoweredGlobalDecl::Var(_),\n            ) => {\n                return Err(Box::new(Error::Unexpected(\n                    function_span,\n                    ExpectedToken::Function,\n                )))\n            }\n            Some(&LoweredGlobalDecl::EntryPoint(_)) => {\n                return Err(Box::new(Error::CalledEntryPoint(function_span)));\n            }\n            Some(&LoweredGlobalDecl::Function {\n                handle: function,\n                must_use,\n            }) => {\n                // user-declared functions can't make use of template lists\n                tl.finish(ctx)?;\n\n                let arguments = arguments\n                    .iter()\n                    .enumerate()\n                    .map(|(i, &arg)| {\n                        // Try to convert abstract values to the known argument types\n                        let Some(&ir::FunctionArgument {\n                            ty: parameter_ty, ..\n                        }) = ctx.module.functions[function].arguments.get(i)\n                        else {\n                            // Wrong number of arguments... just concretize the type here\n                            // and let the validator report the error.\n                            return self.expression(arg, ctx);\n                        };\n\n                        let expr = self.expression_for_abstract(arg, ctx)?;\n                        ctx.try_automatic_conversions(\n                            expr,\n                            &proc::TypeResolution::Handle(parameter_ty),\n                            ctx.ast_expressions.get_span(arg),\n                        )\n                    })\n                    .collect::<Result<Vec<_>>>()?;\n\n                let has_result = ctx.module.functions[function].result.is_some();\n\n                let rctx = ctx.runtime_expression_ctx(span)?;\n                // we need to always do this before a fn call since all arguments need to be emitted before the fn call\n                rctx.block\n                    .extend(rctx.emitter.finish(&rctx.function.expressions));\n                let result = has_result.then(|| {\n                    let result = rctx\n                        .function\n                        .expressions\n                        .append(ir::Expression::CallResult(function), span);\n                    rctx.local_expression_kind_tracker\n                        .insert(result, proc::ExpressionKind::Runtime);\n                    (result, must_use.into())\n                });\n                rctx.emitter.start(&rctx.function.expressions);\n                rctx.block.push(\n                    ir::Statement::Call {\n                        function,\n                        arguments,\n                        result: result.map(|(expr, _)| expr),\n                    },\n                    span,\n                );\n\n                result\n            }\n            None => {\n                // If the name refers to a predeclared type, this is a construction expression.\n                let ty = conv::map_predeclared_type(\n                    &ctx.enable_extensions,\n                    function_span,\n                    function_name,\n                )?;\n                if let Some(ty) = ty {\n                    let empty_template_list = call_phrase.function.template_list.is_empty();\n                    let constructor_ty = match ty {\n                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Vector {\n                            size,\n                        }) if empty_template_list => Constructor::PartialVector { size },\n                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Matrix {\n                            columns,\n                            rows,\n                        }) if empty_template_list => Constructor::PartialMatrix { columns, rows },\n                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Array)\n                            if empty_template_list =>\n                        {\n                            Constructor::PartialArray\n                        }\n                        conv::PredeclaredType::TypeGenerator(\n                            conv::TypeGenerator::CooperativeMatrix { .. },\n                        ) if empty_template_list => {\n                            return Err(Box::new(Error::UnderspecifiedCooperativeMatrix));\n                        }\n                        _ => Constructor::Type(self.finalize_type(ctx, ty, &mut tl, None)?),\n                    };\n                    tl.finish(ctx)?;\n                    let handle =\n                        self.construct(span, constructor_ty, function_span, arguments, ctx)?;\n                    Some((handle, MustUse::Yes))\n                } else {\n                    // Otherwise, it must be a call to a builtin function.\n                    let result = self.call_builtin(\n                        function_name,\n                        function_span,\n                        arguments,\n                        &mut tl,\n                        span,\n                        ctx,\n                        is_statement,\n                    )?;\n                    tl.finish(ctx)?;\n                    result\n                }\n            }\n        };\n\n        let result_used = !is_statement;\n        if matches!(result, Some((_, MustUse::Yes))) && !result_used {\n            return Err(Box::new(Error::FunctionMustUseUnused(function_span)));\n        }\n        Ok(result.map(|(expr, _)| expr))\n    }\n\n    /// Generate a Naga IR [`Math`] expression.\n    ///\n    /// Generate Naga IR for a call to the [`MathFunction`] `fun`, whose\n    /// unlowered arguments are `ast_arguments`.\n    ///\n    /// The `span` argument should give the span of the function name in the\n    /// call expression.\n    ///\n    /// [`Math`]: ir::Expression::Math\n    /// [`MathFunction`]: ir::MathFunction\n    fn math_function_helper(\n        &mut self,\n        span: Span,\n        fun: ir::MathFunction,\n        ast_arguments: &[Handle<ast::Expression<'source>>],\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::Expression> {\n        let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());\n        for &arg in ast_arguments {\n            let lowered = self.expression_for_abstract(arg, ctx)?;\n            ctx.grow_types(lowered)?;\n            lowered_arguments.push(lowered);\n        }\n\n        let fun_overloads = fun.overloads();\n        let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;\n        self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;\n\n        // If this function returns a predeclared type, register it\n        // in `Module::special_types`. The typifier will expect to\n        // be able to find it there.\n        if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {\n            ctx.module.generate_predeclared_type(predeclared);\n        }\n\n        Ok(ir::Expression::Math {\n            fun,\n            arg: lowered_arguments[0],\n            arg1: lowered_arguments.get(1).cloned(),\n            arg2: lowered_arguments.get(2).cloned(),\n            arg3: lowered_arguments.get(3).cloned(),\n        })\n    }\n\n    /// Choose the right overload for a function call.\n    ///\n    /// Return a [`Rule`] representing the most preferred overload in\n    /// `overloads` to apply to `arguments`, or return an error explaining why\n    /// the call is not valid.\n    ///\n    /// Use `fun` to identify the function being called in error messages;\n    /// `span` should be the span of the function name in the call expression.\n    ///\n    /// [`Rule`]: proc::Rule\n    fn resolve_overloads<O, F>(\n        &self,\n        span: Span,\n        fun: F,\n        overloads: O,\n        arguments: &[Handle<ir::Expression>],\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, proc::Rule>\n    where\n        O: proc::OverloadSet,\n        F: TryToWgsl + core::fmt::Debug + Copy,\n    {\n        let mut remaining_overloads = overloads.clone();\n        let min_arguments = remaining_overloads.min_arguments();\n        let max_arguments = remaining_overloads.max_arguments();\n        if arguments.len() < min_arguments {\n            return Err(Box::new(Error::WrongArgumentCount {\n                span,\n                expected: min_arguments as u32..max_arguments as u32,\n                found: arguments.len() as u32,\n            }));\n        }\n        if arguments.len() > max_arguments {\n            return Err(Box::new(Error::TooManyArguments {\n                function: fun.to_wgsl_for_diagnostics(),\n                call_span: span,\n                arg_span: ctx.get_expression_span(arguments[max_arguments]),\n                max_arguments: max_arguments as _,\n            }));\n        }\n\n        log::debug!(\n            \"Initial overloads: {:#?}\",\n            remaining_overloads.for_debug(&ctx.module.types)\n        );\n\n        for (arg_index, &arg) in arguments.iter().enumerate() {\n            let arg_type_resolution = &ctx.typifier()[arg];\n            let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);\n            log::debug!(\n                \"Supplying argument {arg_index} of type {:?}\",\n                arg_type_resolution.for_debug(&ctx.module.types)\n            );\n            let next_remaining_overloads =\n                remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);\n\n            // If any argument is not a constant expression, then no overloads\n            // that accept abstract values should be considered.\n            // (`OverloadSet::concrete_only` is supposed to help impose this\n            // restriction.) However, no `MathFunction` accepts a mix of\n            // abstract and concrete arguments, so we don't need to worry\n            // about that here.\n\n            log::debug!(\n                \"Remaining overloads: {:#?}\",\n                next_remaining_overloads.for_debug(&ctx.module.types)\n            );\n\n            // If the set of remaining overloads is empty, then this argument's type\n            // was unacceptable. Diagnose the problem and produce an error message.\n            if next_remaining_overloads.is_empty() {\n                let function = fun.to_wgsl_for_diagnostics();\n                let call_span = span;\n                let arg_span = ctx.get_expression_span(arg);\n                let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();\n\n                // Is this type *ever* permitted for the arg_index'th argument?\n                // For example, `bool` is never permitted for `max`.\n                let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);\n                if only_this_argument.is_empty() {\n                    // No overload of `fun` accepts this type as the\n                    // arg_index'th argument. Determine the set of types that\n                    // would ever be allowed there.\n                    let allowed: Vec<String> = overloads\n                        .allowed_args(arg_index, &ctx.module.to_ctx())\n                        .iter()\n                        .map(|ty| ctx.type_resolution_to_string(ty))\n                        .collect();\n\n                    if allowed.is_empty() {\n                        // No overload of `fun` accepts any argument at this\n                        // index, so it's a simple case of excess arguments.\n                        // However, since each `MathFunction`'s overloads all\n                        // have the same arity, we should have detected this\n                        // earlier.\n                        unreachable!(\"expected all overloads to have the same arity\");\n                    }\n\n                    // Some overloads of `fun` do accept this many arguments,\n                    // but none accept one of this type.\n                    return Err(Box::new(Error::WrongArgumentType {\n                        function,\n                        call_span,\n                        arg_span,\n                        arg_index: arg_index as u32,\n                        arg_ty,\n                        allowed,\n                    }));\n                }\n\n                // This argument's type is accepted by some overloads---just\n                // not those overloads that remain, given the prior arguments.\n                // For example, `max` accepts `f32` as its second argument -\n                // but not if the first was `i32`.\n\n                // Build a list of the types that would have been accepted here,\n                // given the prior arguments.\n                let allowed: Vec<String> = remaining_overloads\n                    .allowed_args(arg_index, &ctx.module.to_ctx())\n                    .iter()\n                    .map(|ty| ctx.type_resolution_to_string(ty))\n                    .collect();\n\n                // Re-run the argument list to determine which prior argument\n                // made this one unacceptable.\n                let mut remaining_overloads = overloads;\n                for (prior_index, &prior_expr) in arguments.iter().enumerate() {\n                    let prior_type_resolution = &ctx.typifier()[prior_expr];\n                    let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);\n                    remaining_overloads =\n                        remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);\n                    if remaining_overloads\n                        .arg(arg_index, arg_inner, &ctx.module.types)\n                        .is_empty()\n                    {\n                        // This is the argument that killed our dreams.\n                        let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);\n                        let inconsistent_ty =\n                            ctx.as_diagnostic_display(prior_type_resolution).to_string();\n\n                        if allowed.is_empty() {\n                            // Some overloads did accept `ty` at `arg_index`, but\n                            // given the arguments up through `prior_expr`, we see\n                            // no types acceptable at `arg_index`. This means that some\n                            // overloads expect fewer arguments than others. However,\n                            // each `MathFunction`'s overloads have the same arity, so this\n                            // should be impossible.\n                            unreachable!(\"expected all overloads to have the same arity\");\n                        }\n\n                        // Report `arg`'s type as inconsistent with `prior_expr`'s\n                        return Err(Box::new(Error::InconsistentArgumentType {\n                            function,\n                            call_span,\n                            arg_span,\n                            arg_index: arg_index as u32,\n                            arg_ty,\n                            inconsistent_span,\n                            inconsistent_index: prior_index as u32,\n                            inconsistent_ty,\n                            allowed,\n                        }));\n                    }\n                }\n                unreachable!(\"Failed to eliminate argument type when re-tried\");\n            }\n            remaining_overloads = next_remaining_overloads;\n        }\n\n        // Select the most preferred type rule for this call,\n        // given the argument types supplied above.\n        Ok(remaining_overloads.most_preferred())\n    }\n\n    /// Apply automatic type conversions for a function call.\n    ///\n    /// Apply whatever automatic conversions are needed to pass `arguments` to\n    /// the function overload described by `rule`. Update `arguments` to refer\n    /// to the converted arguments.\n    fn apply_automatic_conversions_for_call(\n        &self,\n        rule: &proc::Rule,\n        arguments: &mut [Handle<ir::Expression>],\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ()> {\n        for (i, argument) in arguments.iter_mut().enumerate() {\n            let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);\n            let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {\n                Some(goal_scalar) => {\n                    let arg_span = ctx.get_expression_span(*argument);\n                    ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?\n                }\n                // No conversion is necessary.\n                None => *argument,\n            };\n\n            *argument = converted;\n        }\n\n        Ok(())\n    }\n\n    fn atomic_pointer(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let pointer = self.expression(expr, ctx)?;\n\n        match *resolve_inner!(ctx, pointer) {\n            ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {\n                ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),\n                ref other => {\n                    log::error!(\"Pointer type to {other:?} passed to atomic op\");\n                    Err(Box::new(Error::InvalidAtomicPointer(span)))\n                }\n            },\n            ref other => {\n                log::error!(\"Type {other:?} passed to atomic op\");\n                Err(Box::new(Error::InvalidAtomicPointer(span)))\n            }\n        }\n    }\n\n    fn atomic_helper(\n        &mut self,\n        span: Span,\n        fun: ir::AtomicFunction,\n        args: &[Handle<ast::Expression<'source>>],\n        is_statement: bool,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Option<Handle<ir::Expression>>> {\n        let mut args = ctx.prepare_args(args, 2, span);\n\n        let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;\n        let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;\n        let value_inner = resolve_inner!(ctx, value);\n        args.finish()?;\n\n        // If we don't use the return value of a 64-bit `min` or `max`\n        // operation, generate a no-result form of the `Atomic` statement, so\n        // that we can pass validation with only `SHADER_INT64_ATOMIC_MIN_MAX`\n        // whenever possible.\n        let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)\n            && matches!(\n                *value_inner,\n                ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })\n            );\n        let result = if is_64_bit_min_max && is_statement {\n            let rctx = ctx.runtime_expression_ctx(span)?;\n            rctx.block\n                .extend(rctx.emitter.finish(&rctx.function.expressions));\n            rctx.emitter.start(&rctx.function.expressions);\n            None\n        } else {\n            let ty = ctx.register_type(value)?;\n            Some(ctx.interrupt_emitter(\n                ir::Expression::AtomicResult {\n                    ty,\n                    comparison: false,\n                },\n                span,\n            )?)\n        };\n        let rctx = ctx.runtime_expression_ctx(span)?;\n        rctx.block.push(\n            ir::Statement::Atomic {\n                pointer,\n                fun,\n                value,\n                result,\n            },\n            span,\n        );\n        Ok(result)\n    }\n\n    fn texture_sample_helper(\n        &mut self,\n        fun: Texture,\n        args: &[Handle<ast::Expression<'source>>],\n        span: Span,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::Expression> {\n        let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);\n\n        fn get_image_and_span<'source>(\n            lowerer: &mut Lowerer<'source, '_>,\n            args: &mut ArgumentContext<'_, 'source>,\n            ctx: &mut ExpressionContext<'source, '_, '_>,\n        ) -> Result<'source, (Handle<ir::Expression>, Span)> {\n            let image = args.next()?;\n            let image_span = ctx.ast_expressions.get_span(image);\n            let image = lowerer.expression_for_abstract(image, ctx)?;\n            Ok((image, image_span))\n        }\n\n        let image;\n        let image_span;\n        let gather;\n        match fun {\n            Texture::Gather => {\n                let image_or_component = args.next()?;\n                let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);\n                // Gathers from depth textures don't take an initial `component` argument.\n                let lowered_image_or_component = self.expression(image_or_component, ctx)?;\n\n                match *resolve_inner!(ctx, lowered_image_or_component) {\n                    ir::TypeInner::Image {\n                        class: ir::ImageClass::Depth { .. },\n                        ..\n                    } => {\n                        image = lowered_image_or_component;\n                        image_span = image_or_component_span;\n                        gather = Some(ir::SwizzleComponent::X);\n                    }\n                    _ => {\n                        (image, image_span) = get_image_and_span(self, &mut args, ctx)?;\n                        gather = Some(ctx.gather_component(\n                            lowered_image_or_component,\n                            image_or_component_span,\n                            span,\n                        )?);\n                    }\n                }\n            }\n            Texture::GatherCompare => {\n                (image, image_span) = get_image_and_span(self, &mut args, ctx)?;\n                gather = Some(ir::SwizzleComponent::X);\n            }\n\n            _ => {\n                (image, image_span) = get_image_and_span(self, &mut args, ctx)?;\n                gather = None;\n            }\n        };\n\n        let sampler = self.expression_for_abstract(args.next()?, ctx)?;\n\n        let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n        let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);\n\n        let (class, arrayed) = ctx.image_data(image, image_span)?;\n        let array_index = arrayed\n            .then(|| self.expression(args.next()?, ctx))\n            .transpose()?;\n\n        let level;\n        let depth_ref;\n        match fun {\n            Texture::Gather => {\n                level = ir::SampleLevel::Zero;\n                depth_ref = None;\n            }\n            Texture::GatherCompare => {\n                let reference =\n                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                level = ir::SampleLevel::Zero;\n                depth_ref = Some(reference);\n            }\n\n            Texture::Sample => {\n                level = ir::SampleLevel::Auto;\n                depth_ref = None;\n            }\n            Texture::SampleBias => {\n                let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                level = ir::SampleLevel::Bias(bias);\n                depth_ref = None;\n            }\n            Texture::SampleCompare => {\n                let reference =\n                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                level = ir::SampleLevel::Auto;\n                depth_ref = Some(reference);\n            }\n            Texture::SampleCompareLevel => {\n                let reference =\n                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                level = ir::SampleLevel::Zero;\n                depth_ref = Some(reference);\n            }\n            Texture::SampleGrad => {\n                let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;\n                level = ir::SampleLevel::Gradient { x, y };\n                depth_ref = None;\n            }\n            Texture::SampleLevel => {\n                let exact = match class {\n                    // When applied to depth textures, `textureSampleLevel`'s\n                    // `level` argument is an `i32` or `u32`.\n                    ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,\n\n                    // When applied to other sampled types, its `level` argument\n                    // is an `f32`.\n                    ir::ImageClass::Sampled { .. } => {\n                        self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?\n                    }\n\n                    // Sampling `External` textures with a specified level isn't\n                    // allowed, and sampling `Storage` textures isn't allowed at\n                    // all. Let the validator report the error.\n                    ir::ImageClass::Storage { .. } | ir::ImageClass::External => {\n                        self.expression(args.next()?, ctx)?\n                    }\n                };\n                level = ir::SampleLevel::Exact(exact);\n                depth_ref = None;\n            }\n            Texture::SampleBaseClampToEdge => {\n                level = crate::SampleLevel::Zero;\n                depth_ref = None;\n            }\n        };\n\n        let offset = args\n            .next()\n            .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))\n            .ok()\n            .transpose()?;\n\n        args.finish()?;\n\n        Ok(ir::Expression::ImageSample {\n            image,\n            sampler,\n            gather,\n            coordinate,\n            array_index,\n            offset,\n            level,\n            depth_ref,\n            clamp_to_edge,\n        })\n    }\n\n    fn subgroup_operation_helper(\n        &mut self,\n        span: Span,\n        op: ir::SubgroupOperation,\n        collective_op: ir::CollectiveOperation,\n        arguments: &[Handle<ast::Expression<'source>>],\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let mut args = ctx.prepare_args(arguments, 1, span);\n\n        let argument = self.expression(args.next()?, ctx)?;\n        args.finish()?;\n\n        let ty = ctx.register_type(argument)?;\n\n        let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;\n        let rctx = ctx.runtime_expression_ctx(span)?;\n        rctx.block.push(\n            ir::Statement::SubgroupCollectiveOperation {\n                op,\n                collective_op,\n                argument,\n                result,\n            },\n            span,\n        );\n        Ok(result)\n    }\n\n    fn subgroup_gather_helper(\n        &mut self,\n        span: Span,\n        mode: SubgroupGather,\n        arguments: &[Handle<ast::Expression<'source>>],\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let mut args = ctx.prepare_args(arguments, 2, span);\n\n        let argument = self.expression(args.next()?, ctx)?;\n\n        use SubgroupGather as Sg;\n        let mode = if let Sg::BroadcastFirst = mode {\n            ir::GatherMode::BroadcastFirst\n        } else {\n            let index = self.expression(args.next()?, ctx)?;\n            match mode {\n                Sg::BroadcastFirst => unreachable!(),\n                Sg::Broadcast => ir::GatherMode::Broadcast(index),\n                Sg::Shuffle => ir::GatherMode::Shuffle(index),\n                Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),\n                Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),\n                Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),\n                Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),\n            }\n        };\n\n        args.finish()?;\n\n        let ty = ctx.register_type(argument)?;\n\n        let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;\n        let rctx = ctx.runtime_expression_ctx(span)?;\n        rctx.block.push(\n            ir::Statement::SubgroupGather {\n                mode,\n                argument,\n                result,\n            },\n            span,\n        );\n        Ok(result)\n    }\n\n    fn r#struct(\n        &mut self,\n        s: &ast::Struct<'source>,\n        span: Span,\n        ctx: &mut GlobalContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        let mut offset = 0;\n        let mut struct_alignment = proc::Alignment::ONE;\n        let mut members = Vec::with_capacity(s.members.len());\n\n        let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();\n\n        for member in s.members.iter() {\n            let ty = self.resolve_ast_type(&member.ty, &mut ctx.as_const())?;\n\n            ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {\n                let LayoutErrorInner::TooLarge = err.inner else {\n                    unreachable!(\"unexpected layout error: {err:?}\");\n                };\n                // Since anonymous types of struct members don't get a span,\n                // associate the error with the member. The layouter could have\n                // failed on any type that was pending layout, but if it wasn't\n                // the current struct member, it wasn't a struct member at all,\n                // because we resolve struct members one-by-one.\n                if ty == err.ty {\n                    Box::new(Error::StructMemberTooLarge {\n                        member_name_span: member.name.span,\n                    })\n                } else {\n                    // Lots of type definitions don't get spans, so this error\n                    // message may not be very useful.\n                    Box::new(Error::TypeTooLarge {\n                        span: ctx.module.types.get_span(err.ty),\n                    })\n                }\n            })?;\n\n            let member_min_size = ctx.layouter[ty].size;\n            let member_min_alignment = ctx.layouter[ty].alignment;\n\n            let member_size = if let Some(size_expr) = member.size {\n                let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;\n                if size < member_min_size {\n                    return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));\n                } else {\n                    size\n                }\n            } else {\n                member_min_size\n            };\n\n            let member_alignment = if let Some(align_expr) = member.align {\n                let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;\n                if let Some(alignment) = proc::Alignment::new(align) {\n                    if alignment < member_min_alignment {\n                        return Err(Box::new(Error::AlignAttributeTooLow(\n                            span,\n                            member_min_alignment,\n                        )));\n                    } else {\n                        alignment\n                    }\n                } else {\n                    return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));\n                }\n            } else {\n                member_min_alignment\n            };\n\n            let binding = self.binding(&member.binding, ty, ctx)?;\n\n            offset = member_alignment.round_up(offset);\n            struct_alignment = struct_alignment.max(member_alignment);\n\n            if !member.doc_comments.is_empty() {\n                doc_comments.push(Some(\n                    member.doc_comments.iter().map(|s| s.to_string()).collect(),\n                ));\n            }\n            members.push(ir::StructMember {\n                name: Some(member.name.name.to_owned()),\n                ty,\n                binding,\n                offset,\n            });\n\n            offset += member_size;\n            if offset > crate::valid::MAX_TYPE_SIZE {\n                return Err(Box::new(Error::TypeTooLarge { span }));\n            }\n        }\n\n        let size = struct_alignment.round_up(offset);\n        let inner = ir::TypeInner::Struct {\n            members,\n            span: size,\n        };\n\n        let handle = ctx.module.types.insert(\n            ir::Type {\n                name: Some(s.name.name.to_string()),\n                inner,\n            },\n            span,\n        );\n        for (i, c) in doc_comments.drain(..).enumerate() {\n            if let Some(comment) = c {\n                ctx.module\n                    .get_or_insert_default_doc_comments()\n                    .struct_members\n                    .insert((handle, i), comment);\n            }\n        }\n        Ok(handle)\n    }\n\n    fn const_u32(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, (u32, Span)> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let expr = self.expression(expr, ctx)?;\n        let value = ctx\n            .module\n            .to_ctx()\n            .get_const_val(expr)\n            .map_err(|err| match err {\n                proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {\n                    Error::ExpectedConstExprConcreteIntegerScalar(span)\n                }\n                proc::ConstValueError::Negative => Error::ExpectedNonNegative(span),\n            })?;\n        Ok((value, span))\n    }\n\n    fn array_size(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::ArraySize> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let const_ctx = &mut ctx.as_const();\n        let const_expr = self.expression(expr, const_ctx);\n        match const_expr {\n            Ok(value) => {\n                let len = const_ctx.get_const_val(value).map_err(|err| {\n                    Box::new(match err {\n                        proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {\n                            Error::ExpectedConstExprConcreteIntegerScalar(span)\n                        }\n                        proc::ConstValueError::Negative => Error::ExpectedPositiveArrayLength(span),\n                    })\n                })?;\n                let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;\n                Ok(ir::ArraySize::Constant(size))\n            }\n            Err(err) => {\n                // If the error is simply that `expr` was an override expression, then we\n                // can represent that as an array length.\n                let Error::ConstantEvaluatorError(ref ty, _) = *err else {\n                    return Err(err);\n                };\n\n                let proc::ConstantEvaluatorError::OverrideExpr = **ty else {\n                    return Err(err);\n                };\n\n                Ok(ir::ArraySize::Pending(self.array_size_override(\n                    expr,\n                    &mut ctx.as_global().as_override(),\n                    span,\n                )?))\n            }\n        }\n    }\n\n    fn array_size_override(\n        &mut self,\n        size_expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n        span: Span,\n    ) -> Result<'source, Handle<ir::Override>> {\n        let expr = self.expression(size_expr, ctx)?;\n        match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {\n            Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({\n                if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {\n                    handle\n                } else {\n                    let ty = ctx.register_type(expr)?;\n                    ctx.module.overrides.append(\n                        ir::Override {\n                            name: None,\n                            id: None,\n                            ty,\n                            init: Some(expr),\n                        },\n                        span,\n                    )\n                }\n            }),\n            _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(\n                span,\n            ))),\n        }\n    }\n\n    /// Build the Naga equivalent of a named AST type.\n    ///\n    /// Return a Naga `Handle<Type>` representing the front-end type\n    /// `handle`, which should be named `name`, if given.\n    ///\n    /// If `handle` refers to a type cached in [`SpecialTypes`],\n    /// `name` may be ignored.\n    ///\n    /// [`SpecialTypes`]: ir::SpecialTypes\n    fn resolve_named_ast_type(\n        &mut self,\n        ident: &ast::TemplateElaboratedIdent<'source>,\n        name: String,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        self.type_specifier(ident, ctx, Some(name))\n    }\n\n    /// Return a Naga `Handle<Type>` representing the front-end type `handle`.\n    fn resolve_ast_type(\n        &mut self,\n        ident: &ast::TemplateElaboratedIdent<'source>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        self.type_specifier(ident, ctx, None)\n    }\n\n    fn binding(\n        &mut self,\n        binding: &Option<ast::Binding<'source>>,\n        ty: Handle<ir::Type>,\n        ctx: &mut GlobalContext<'source, '_, '_>,\n    ) -> Result<'source, Option<ir::Binding>> {\n        Ok(match *binding {\n            Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),\n            Some(ast::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src,\n                per_primitive,\n            }) => {\n                let blend_src = if let Some(blend_src) = blend_src {\n                    Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)\n                } else {\n                    None\n                };\n\n                let mut binding = ir::Binding::Location {\n                    location: self.const_u32(location, &mut ctx.as_const())?.0,\n                    interpolation,\n                    sampling,\n                    blend_src,\n                    per_primitive,\n                };\n                binding.apply_default_interpolation(&ctx.module.types[ty].inner);\n                Some(binding)\n            }\n            None => None,\n        })\n    }\n\n    fn ray_query_pointer(\n        &mut self,\n        expr: Handle<ast::Expression<'source>>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Expression>> {\n        let span = ctx.ast_expressions.get_span(expr);\n        let pointer = self.expression(expr, ctx)?;\n\n        match *resolve_inner!(ctx, pointer) {\n            ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {\n                ir::TypeInner::RayQuery { .. } => Ok(pointer),\n                ref other => {\n                    log::error!(\"Pointer type to {other:?} passed to ray query op\");\n                    Err(Box::new(Error::InvalidRayQueryPointer(span)))\n                }\n            },\n            ref other => {\n                log::error!(\"Type {other:?} passed to ray query op\");\n                Err(Box::new(Error::InvalidRayQueryPointer(span)))\n            }\n        }\n    }\n}\n\nimpl ir::AtomicFunction {\n    pub fn map(word: &str) -> Option<Self> {\n        Some(match word {\n            \"atomicAdd\" => ir::AtomicFunction::Add,\n            \"atomicSub\" => ir::AtomicFunction::Subtract,\n            \"atomicAnd\" => ir::AtomicFunction::And,\n            \"atomicOr\" => ir::AtomicFunction::InclusiveOr,\n            \"atomicXor\" => ir::AtomicFunction::ExclusiveOr,\n            \"atomicMin\" => ir::AtomicFunction::Min,\n            \"atomicMax\" => ir::AtomicFunction::Max,\n            \"atomicExchange\" => ir::AtomicFunction::Exchange { compare: None },\n            _ => return None,\n        })\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/lower/template_list.rs",
    "content": "use alloc::{boxed::Box, vec::Vec};\n\nuse crate::{\n    front::wgsl::{\n        error::Error,\n        lower::{ExpressionContext, Lowerer, Result},\n        parse::{ast, conv},\n    },\n    ir, Handle, Span,\n};\n\n/// Iterator over a template list.\n///\n/// All functions will attempt to consume an element in the list.\n///\n/// Function variants prefixed with \"maybe\" will not return an error if there\n/// are no more elements left in the list.\npub struct TemplateListIter<'iter, 'source> {\n    ident_span: Span,\n    template_list: core::slice::Iter<'iter, Handle<ast::Expression<'source>>>,\n}\n\nimpl<'iter, 'source> TemplateListIter<'iter, 'source> {\n    pub fn new(ident_span: Span, template_list: &'iter [Handle<ast::Expression<'source>>]) -> Self {\n        Self {\n            ident_span,\n            template_list: template_list.iter(),\n        }\n    }\n\n    pub fn finish(self, ctx: &ExpressionContext<'source, '_, '_>) -> Result<'source, ()> {\n        let unused_args: Vec<Span> = self\n            .template_list\n            .map(|expr| ctx.ast_expressions.get_span(*expr))\n            .collect();\n        if unused_args.is_empty() {\n            Ok(())\n        } else {\n            Err(Box::new(Error::UnusedArgsForTemplate(unused_args)))\n        }\n    }\n\n    fn expect_next(\n        &mut self,\n        description: &'static str,\n    ) -> Result<'source, Handle<ast::Expression<'source>>> {\n        if let Some(expr) = self.template_list.next() {\n            Ok(*expr)\n        } else {\n            Err(Box::new(Error::MissingTemplateArg {\n                span: self.ident_span,\n                description,\n            }))\n        }\n    }\n\n    pub fn ty(\n        &mut self,\n        lowerer: &mut Lowerer<'source, '_>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Handle<ir::Type>> {\n        let expr = self.expect_next(\"`T`, a type\")?;\n        lowerer.type_expression(expr, ctx)\n    }\n\n    /// Lower the next template list element as a type, and return its span.\n    ///\n    /// This returns the span of the template list element. This is generally\n    /// different from the span of the returned `Handle<ir::Type>`, as the\n    /// latter may refer to the type's definition, not its use in the template list.\n    pub fn ty_with_span(\n        &mut self,\n        lowerer: &mut Lowerer<'source, '_>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, (Handle<ir::Type>, Span)> {\n        let expr = self.expect_next(\"`T`, a type\")?;\n        let span = ctx.ast_expressions.get_span(expr);\n        let ty = lowerer.type_expression(expr, ctx)?;\n        Ok((ty, span))\n    }\n\n    pub fn scalar_ty(\n        &mut self,\n        lowerer: &mut Lowerer<'source, '_>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, (ir::Scalar, Span)> {\n        let expr = self.expect_next(\"`T`, a scalar type\")?;\n        let ty = lowerer.type_expression(expr, ctx)?;\n        let span = ctx.ast_expressions.get_span(expr);\n        match ctx.module.types[ty].inner {\n            ir::TypeInner::Scalar(scalar) => Ok((scalar, span)),\n            _ => Err(Box::new(Error::UnknownScalarType(span))),\n        }\n    }\n\n    pub fn maybe_array_size(\n        &mut self,\n        lowerer: &mut Lowerer<'source, '_>,\n        ctx: &mut ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::ArraySize> {\n        if let Some(expr) = self.template_list.next() {\n            lowerer.array_size(*expr, ctx)\n        } else {\n            Ok(ir::ArraySize::Dynamic)\n        }\n    }\n\n    pub fn address_space(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::AddressSpace> {\n        let expr = self.expect_next(\"`AS`, an address space\")?;\n        let (enumerant, span) = ctx.enumerant(expr)?;\n        conv::map_address_space(enumerant, span, &ctx.enable_extensions)\n    }\n    pub fn maybe_address_space(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, Option<ir::AddressSpace>> {\n        let Some(expr) = self.template_list.next() else {\n            return Ok(None);\n        };\n\n        let (enumerant, span) = ctx.enumerant(*expr)?;\n        Ok(Some(conv::map_address_space(\n            enumerant,\n            span,\n            &ctx.enable_extensions,\n        )?))\n    }\n\n    pub fn access_mode(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::StorageAccess> {\n        let expr = self.expect_next(\"`Access`, an access mode\")?;\n        let (enumerant, span) = ctx.enumerant(expr)?;\n        conv::map_access_mode(enumerant, span)\n    }\n\n    /// Update `space` with an access mode from `self`, if appropriate.\n    ///\n    /// If `space` is [`Storage`], and there is another template parameter in\n    /// `self`, parse it as a storage mode and mutate `space` accordingly. If\n    /// there are no more template parameters, treat that like `read`.\n    ///\n    /// If `space` is some other address space, don't consume any template\n    /// parameters.\n    ///\n    /// [`Storage`]: ir::AddressSpace::Storage\n    pub fn maybe_access_mode(\n        &mut self,\n        space: &mut ir::AddressSpace,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ()> {\n        if let &mut ir::AddressSpace::Storage { ref mut access } = space {\n            if let Some(expr) = self.template_list.next() {\n                let (enumerant, span) = ctx.enumerant(*expr)?;\n                let access_mode = conv::map_access_mode(enumerant, span)?;\n                *access = access_mode;\n            } else {\n                // defaulting to `read`\n                *access = ir::StorageAccess::LOAD\n            }\n        }\n        Ok(())\n    }\n\n    pub fn storage_format(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, ir::StorageFormat> {\n        let expr = self.expect_next(\"`Format`, a texel format\")?;\n        let (enumerant, span) = ctx.enumerant(expr)?;\n        conv::map_storage_format(enumerant, span)\n    }\n\n    pub fn maybe_vertex_return(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, bool> {\n        let Some(expr) = self.template_list.next() else {\n            return Ok(false);\n        };\n\n        let (enumerant, span) = ctx.enumerant(*expr)?;\n        conv::map_ray_flag(&ctx.enable_extensions, enumerant, span)?;\n        Ok(true)\n    }\n\n    pub fn cooperative_role(\n        &mut self,\n        ctx: &ExpressionContext<'source, '_, '_>,\n    ) -> Result<'source, crate::CooperativeRole> {\n        let role_expr = self.expect_next(\"`Role`, a cooperative matrix role\")?;\n        let (enumerant, span) = ctx.enumerant(role_expr)?;\n        let role = conv::map_cooperative_role(enumerant, span)?;\n        Ok(role)\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/mod.rs",
    "content": "/*!\nFrontend for [WGSL][wgsl] (WebGPU Shading Language).\n\n[wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html\n*/\n\nmod error;\nmod index;\nmod lower;\nmod parse;\n#[cfg(test)]\nmod tests;\n\npub use parse::directive::enable_extension::{\n    EnableExtension, ImplementedEnableExtension, UnimplementedEnableExtension,\n};\n\npub use crate::front::wgsl::error::ParseError;\npub use crate::front::wgsl::parse::directive::language_extension::{\n    ImplementedLanguageExtension, LanguageExtension, UnimplementedLanguageExtension,\n};\npub use crate::front::wgsl::parse::Options;\n\nuse alloc::boxed::Box;\nuse thiserror::Error;\n\nuse crate::front::wgsl::error::Error;\nuse crate::front::wgsl::lower::Lowerer;\nuse crate::front::wgsl::parse::Parser;\nuse crate::Scalar;\n\n#[cfg(test)]\nuse std::println;\n\npub(crate) type Result<'a, T> = core::result::Result<T, Box<Error<'a>>>;\n\npub struct Frontend {\n    parser: Parser,\n    options: Options,\n}\n\nimpl Frontend {\n    pub const fn new() -> Self {\n        Self {\n            parser: Parser::new(),\n            options: Options::new(),\n        }\n    }\n\n    pub const fn new_with_options(options: Options) -> Self {\n        Self {\n            parser: Parser::new(),\n            options,\n        }\n    }\n\n    pub const fn set_options(&mut self, options: Options) {\n        self.options = options;\n    }\n\n    pub fn parse(&mut self, source: &str) -> core::result::Result<crate::Module, ParseError> {\n        self.inner(source).map_err(|x| x.as_parse_error(source))\n    }\n\n    fn inner<'a>(&mut self, source: &'a str) -> Result<'a, crate::Module> {\n        let tu = self.parser.parse(source, &self.options)?;\n        let index = index::Index::generate(&tu)?;\n        let module = Lowerer::new(&index).lower(tu)?;\n\n        Ok(module)\n    }\n}\n\n/// <div class=\"warning\">\n// NOTE: Keep this in sync with `wgpu::Device::create_shader_module`!\n// NOTE: Keep this in sync with `wgpu_core::Global::device_create_shader_module`!\n///\n/// This function may consume a lot of stack space. Compiler-enforced limits for parsing recursion\n/// exist; if shader compilation runs into them, it will return an error gracefully. However, on\n/// some build profiles and platforms, the default stack size for a thread may be exceeded before\n/// this limit is reached during parsing. Callers should ensure that there is enough stack space\n/// for this, particularly if calls to this method are exposed to user input.\n///\n/// </div>\npub fn parse_str(source: &str) -> core::result::Result<crate::Module, ParseError> {\n    Frontend::new().parse(source)\n}\n\n#[cfg(test)]\n#[track_caller]\npub fn assert_parse_err(input: &str, snapshot: &str) {\n    let output = parse_str(input)\n        .expect_err(\"expected parser error\")\n        .emit_to_string(input);\n    if output != snapshot {\n        for diff in diff::lines(snapshot, &output) {\n            match diff {\n                diff::Result::Left(l) => println!(\"-{l}\"),\n                diff::Result::Both(l, _) => println!(\" {l}\"),\n                diff::Result::Right(r) => println!(\"+{r}\"),\n            }\n        }\n        panic!(\"Error snapshot failed\");\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/ast.rs",
    "content": "use alloc::vec::Vec;\nuse core::hash::Hash;\n\nuse crate::diagnostic_filter::DiagnosticFilterNode;\nuse crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;\nuse crate::front::wgsl::parse::number::Number;\nuse crate::{Arena, FastIndexSet, Handle, Span};\n\n#[derive(Debug, Default)]\npub struct TranslationUnit<'a> {\n    pub enable_extensions: EnableExtensions,\n    pub decls: Arena<GlobalDecl<'a>>,\n    /// The common expressions arena for the entire translation unit.\n    ///\n    /// All functions, global initializers, array lengths, etc. store their\n    /// expressions here. We apportion these out to individual Naga\n    /// [`Function`]s' expression arenas at lowering time. Keeping them all in a\n    /// single arena simplifies handling of things like array lengths (which are\n    /// effectively global and thus don't clearly belong to any function) and\n    /// initializers (which can appear in both function-local and module-scope\n    /// contexts).\n    ///\n    /// [`Function`]: crate::Function\n    pub expressions: Arena<Expression<'a>>,\n\n    /// Arena for all diagnostic filter rules parsed in this module, including those in functions.\n    ///\n    /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in\n    /// validation.\n    pub diagnostic_filters: Arena<DiagnosticFilterNode>,\n    /// The leaf of all `diagnostic(…)` directives in this module.\n    ///\n    /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in\n    /// validation.\n    pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n\n    /// Doc comments appearing first in the file.\n    /// This serves as documentation for the whole TranslationUnit.\n    pub doc_comments: Vec<&'a str>,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct Ident<'a> {\n    pub name: &'a str,\n    pub span: Span,\n}\n\n/// An identifier that [resolves] to some declaration.\n///\n/// This does not cover context-dependent names: attributes, built-in values,\n/// and so on. We map those to their Naga IR equivalents as soon as they're\n/// parsed, so they never need to appear as identifiers in the AST.\n///\n/// [resolves]: https://gpuweb.github.io/gpuweb/wgsl/#resolves\n#[derive(Debug)]\npub enum IdentExpr<'a> {\n    /// An identifier referring to a module-scope declaration or predeclared\n    /// object.\n    ///\n    /// We need to collect the entire module before we can resolve this, to\n    /// distinguish between predeclared objects and module-scope declarations\n    /// that appear after their uses.\n    ///\n    /// Whenever you create one of these values, you almost certainly want to\n    /// insert the `&str` into [`ExpressionContext::unresolved`][ECu], to ensure\n    /// that [indexing] knows that the name's declaration must be lowered before\n    /// the one containing this use. Using [`Parser::ident_expr`][ie] to build\n    /// `IdentExpr` will take care of that for you.\n    ///\n    /// [ECu]: super::ExpressionContext::unresolved\n    /// [ie]: super::Parser::ident_expr\n    /// [indexing]: crate::front::wgsl::index::Index::generate\n    Unresolved(&'a str),\n\n    /// An identifier that has been resolved to a non-module-scope declaration.\n    Local(Handle<Local>),\n}\n\n/// An identifier with optional template parameters.\n///\n/// Following the WGSL specification (see the [`template_list`] non-terminal),\n/// `TemplateElaboratedIdent` represents all template parameters as expressions:\n/// even parameters to type generators, like the `f32` in `vec3<f32>`, are [Type\n/// Expressions].\n///\n/// # Examples\n///\n/// - A use of a global variable `colors` would be an [`Expression::Ident(v)`][EI],\n///   where `v` is an `TemplateElaboratedIdent` whose `ident` is\n///   [`IdentExpr::Unresolved(\"colors\")`][IEU]. Lowering will resolve this to a\n///   reference to the global variable.\n///\n/// - The type `f32` in a variable declaration is represented as a\n///   `TemplateElaboratedIdent` whose `ident` is\n///   [`IdentExpr::Unresolved(\"f32\")`][IEU]. Lowering will resolve this to\n///   WGSL's predeclared `f32` type.\n///\n/// - The type `vec3<f32>` can be represented as a `TemplateElaboratedIdent`\n///   whose `ident` is [`IdentExpr::Unresolved(\"vec3\")`][IEU], and whose\n///   `template_list` has one element: an [`ExpressionIdent(v)`][EI] where `v` is a\n///   nested `TemplateElaboratedIdent` representing `f32` as described above.\n///\n/// - The type `array<vec3<f32>, 4>` has `\"array\"` as its `ident`, and then\n///   a two-element `template_list`:\n///\n///     - `template_list[0]` is an [`Expression::Ident(v)`][EI] where `v` is a nested\n///       `TemplateElaboratedIdent` representing `vec3<f32>` as described above.\n///\n///     - `template_list[1]` is an [`Expression`] representing `4`.\n///\n/// After [indexing] the module to ensure that declarations appear before uses,\n/// lowering can see which declaration a given `TemplateElaboratedIdent`s\n/// `ident` refers to. The declaration then determines how to interpret the\n/// `template_list`.\n///\n/// [`template_list`]: https://gpuweb.github.io/gpuweb/wgsl/#syntax-template_list\n/// [Type Expressions]: https://gpuweb.github.io/gpuweb/wgsl/#type-expr\n/// [IEU]: IdentExpr::Unresolved\n/// [EI]: Expression::Ident\n/// [indexing]: crate::front::wgsl::index::Index::generate\n#[derive(Debug)]\npub struct TemplateElaboratedIdent<'a> {\n    pub ident: IdentExpr<'a>,\n    pub ident_span: Span,\n\n    /// If non-empty, the template parameters following the identifier.\n    pub template_list: Vec<Handle<Expression<'a>>>,\n    pub template_list_span: Span,\n}\n\n/// A function call or value constructor expression.\n///\n/// We can't tell whether an expression like `IDENTIFIER(EXPR, ...)` is a\n/// construction expression or a function call until we know `IDENTIFIER`'s\n/// definition, so we represent everything of that form as one of these\n/// expressions until lowering. At that point, [`Lowerer::call`] has\n/// everything's definition in hand, and can decide whether to emit a Naga\n/// [`Constant`], [`As`], [`Splat`], or [`Compose`] expression.\n///\n/// [`Lowerer::call`]: Lowerer::call\n/// [`Constant`]: crate::Expression::Constant\n/// [`As`]: crate::Expression::As\n/// [`Splat`]: crate::Expression::Splat\n/// [`Compose`]: crate::Expression::Compose\n#[derive(Debug)]\npub struct CallPhrase<'a> {\n    pub function: TemplateElaboratedIdent<'a>,\n    pub arguments: Vec<Handle<Expression<'a>>>,\n}\n\n/// A reference to a module-scope definition or predeclared object.\n///\n/// Each [`GlobalDecl`] holds a set of these values, to be resolved to\n/// specific definitions later. To support de-duplication, `Eq` and\n/// `Hash` on a `Dependency` value consider only the name, not the\n/// source location at which the reference occurs.\n#[derive(Debug)]\npub struct Dependency<'a> {\n    /// The name referred to.\n    pub ident: &'a str,\n\n    /// The location at which the reference to that name occurs.\n    pub usage: Span,\n}\n\nimpl Hash for Dependency<'_> {\n    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n        self.ident.hash(state);\n    }\n}\n\nimpl PartialEq for Dependency<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        self.ident == other.ident\n    }\n}\n\nimpl Eq for Dependency<'_> {}\n\n/// A module-scope declaration.\n#[derive(Debug)]\npub struct GlobalDecl<'a> {\n    pub kind: GlobalDeclKind<'a>,\n\n    /// Names of all module-scope or predeclared objects this\n    /// declaration uses.\n    pub dependencies: FastIndexSet<Dependency<'a>>,\n}\n\n#[derive(Debug)]\npub enum GlobalDeclKind<'a> {\n    Fn(Function<'a>),\n    Var(GlobalVariable<'a>),\n    Const(Const<'a>),\n    Override(Override<'a>),\n    Struct(Struct<'a>),\n    Type(TypeAlias<'a>),\n    ConstAssert(Handle<Expression<'a>>),\n}\n\n#[derive(Debug)]\npub struct FunctionArgument<'a> {\n    pub name: Ident<'a>,\n    pub ty: TemplateElaboratedIdent<'a>,\n    pub binding: Option<Binding<'a>>,\n    pub handle: Handle<Local>,\n}\n\n#[derive(Debug)]\npub struct FunctionResult<'a> {\n    pub ty: TemplateElaboratedIdent<'a>,\n    pub binding: Option<Binding<'a>>,\n    pub must_use: bool,\n}\n\n#[derive(Debug)]\npub struct EntryPoint<'a> {\n    pub stage: crate::ShaderStage,\n    pub early_depth_test: Option<crate::EarlyDepthTest>,\n    pub workgroup_size: Option<[Option<Handle<Expression<'a>>>; 3]>,\n    pub mesh_output_variable: Option<(&'a str, Span)>,\n    pub task_payload: Option<(&'a str, Span)>,\n    pub ray_incoming_payload: Option<(&'a str, Span)>,\n}\n\n#[cfg(doc)]\nuse crate::front::wgsl::lower::{LocalExpressionContext, StatementContext};\n\n#[derive(Debug)]\npub struct Function<'a> {\n    pub entry_point: Option<EntryPoint<'a>>,\n    pub name: Ident<'a>,\n    pub arguments: Vec<FunctionArgument<'a>>,\n    pub result: Option<FunctionResult<'a>>,\n    pub body: Block<'a>,\n    pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n    pub doc_comments: Vec<&'a str>,\n}\n\n#[derive(Debug)]\npub enum Binding<'a> {\n    BuiltIn(crate::BuiltIn),\n    Location {\n        location: Handle<Expression<'a>>,\n        interpolation: Option<crate::Interpolation>,\n        sampling: Option<crate::Sampling>,\n        blend_src: Option<Handle<Expression<'a>>>,\n        per_primitive: bool,\n    },\n}\n\n#[derive(Debug)]\npub struct ResourceBinding<'a> {\n    pub group: Handle<Expression<'a>>,\n    pub binding: Handle<Expression<'a>>,\n}\n\n#[derive(Debug)]\npub struct GlobalVariable<'a> {\n    pub name: Ident<'a>,\n\n    /// The template list parameters for the `var`, giving the variable's\n    /// address space and access mode, if present.\n    pub template_list: Vec<Handle<Expression<'a>>>,\n\n    /// The `@group` and `@binding` attributes, if present.\n    pub binding: Option<ResourceBinding<'a>>,\n\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Option<Handle<Expression<'a>>>,\n    pub doc_comments: Vec<&'a str>,\n\n    /// Memory decorations for this variable (`@coherent`, `@volatile`).\n    pub memory_decorations: crate::MemoryDecorations,\n}\n\n#[derive(Debug)]\npub struct StructMember<'a> {\n    pub name: Ident<'a>,\n    pub ty: TemplateElaboratedIdent<'a>,\n    pub binding: Option<Binding<'a>>,\n    pub align: Option<Handle<Expression<'a>>>,\n    pub size: Option<Handle<Expression<'a>>>,\n    pub doc_comments: Vec<&'a str>,\n}\n\n#[derive(Debug)]\npub struct Struct<'a> {\n    pub name: Ident<'a>,\n    pub members: Vec<StructMember<'a>>,\n    pub doc_comments: Vec<&'a str>,\n}\n\n#[derive(Debug)]\npub struct TypeAlias<'a> {\n    pub name: Ident<'a>,\n    pub ty: TemplateElaboratedIdent<'a>,\n}\n\n#[derive(Debug)]\npub struct Const<'a> {\n    pub name: Ident<'a>,\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Handle<Expression<'a>>,\n    pub doc_comments: Vec<&'a str>,\n}\n\n#[derive(Debug)]\npub struct Override<'a> {\n    pub name: Ident<'a>,\n    pub id: Option<Handle<Expression<'a>>>,\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Option<Handle<Expression<'a>>>,\n}\n\n#[derive(Debug, Default)]\npub struct Block<'a> {\n    pub stmts: Vec<Statement<'a>>,\n}\n\n#[derive(Debug)]\npub struct Statement<'a> {\n    pub kind: StatementKind<'a>,\n    pub span: Span,\n}\n\n#[derive(Debug)]\npub enum StatementKind<'a> {\n    LocalDecl(LocalDecl<'a>),\n    Block(Block<'a>),\n    If {\n        condition: Handle<Expression<'a>>,\n        accept: Block<'a>,\n        reject: Block<'a>,\n    },\n    Switch {\n        selector: Handle<Expression<'a>>,\n        cases: Vec<SwitchCase<'a>>,\n    },\n    Loop {\n        body: Block<'a>,\n        continuing: Block<'a>,\n        break_if: Option<Handle<Expression<'a>>>,\n    },\n    Break,\n    Continue,\n    Return {\n        value: Option<Handle<Expression<'a>>>,\n    },\n    Kill,\n    Call(CallPhrase<'a>),\n    Assign {\n        target: Handle<Expression<'a>>,\n        op: Option<crate::BinaryOperator>,\n        value: Handle<Expression<'a>>,\n    },\n    Increment(Handle<Expression<'a>>),\n    Decrement(Handle<Expression<'a>>),\n    Phony(Handle<Expression<'a>>),\n    ConstAssert(Handle<Expression<'a>>),\n}\n\n#[derive(Debug)]\npub enum SwitchValue<'a> {\n    Expr(Handle<Expression<'a>>),\n    Default,\n}\n\n#[derive(Debug)]\npub struct SwitchCase<'a> {\n    pub value: SwitchValue<'a>,\n    pub body: Block<'a>,\n    pub fall_through: bool,\n}\n\n#[derive(Debug, Copy, Clone)]\npub enum Literal {\n    Bool(bool),\n    Number(Number),\n}\n\n#[cfg(doc)]\nuse crate::front::wgsl::lower::Lowerer;\n\n#[derive(Debug)]\npub enum Expression<'a> {\n    Literal(Literal),\n    Ident(TemplateElaboratedIdent<'a>),\n    Unary {\n        op: crate::UnaryOperator,\n        expr: Handle<Expression<'a>>,\n    },\n    AddrOf(Handle<Expression<'a>>),\n    Deref(Handle<Expression<'a>>),\n    Binary {\n        op: crate::BinaryOperator,\n        left: Handle<Expression<'a>>,\n        right: Handle<Expression<'a>>,\n    },\n    Call(CallPhrase<'a>),\n    Index {\n        base: Handle<Expression<'a>>,\n        index: Handle<Expression<'a>>,\n    },\n    Member {\n        base: Handle<Expression<'a>>,\n        field: Ident<'a>,\n    },\n}\n\n#[derive(Debug)]\npub struct LocalVariable<'a> {\n    pub name: Ident<'a>,\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Option<Handle<Expression<'a>>>,\n    pub handle: Handle<Local>,\n}\n\n#[derive(Debug)]\npub struct Let<'a> {\n    pub name: Ident<'a>,\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Handle<Expression<'a>>,\n    pub handle: Handle<Local>,\n}\n\n#[derive(Debug)]\npub struct LocalConst<'a> {\n    pub name: Ident<'a>,\n    pub ty: Option<TemplateElaboratedIdent<'a>>,\n    pub init: Handle<Expression<'a>>,\n    pub handle: Handle<Local>,\n}\n\n#[derive(Debug)]\npub enum LocalDecl<'a> {\n    Var(LocalVariable<'a>),\n    Let(Let<'a>),\n    Const(LocalConst<'a>),\n}\n\n#[derive(Debug)]\n/// A placeholder for a local variable declaration.\n///\n/// See [`super::ExpressionContext::locals`] for more information.\npub struct Local;\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/conv.rs",
    "content": "use crate::front::wgsl::parse::directive::enable_extension::{\n    EnableExtensions, ImplementedEnableExtension,\n};\nuse crate::front::wgsl::{Error, Result, Scalar};\nuse crate::{ImageClass, ImageDimension, Span, TypeInner, VectorSize};\n\nuse alloc::boxed::Box;\n\npub fn map_address_space<'a>(\n    word: &str,\n    span: Span,\n    enable_extensions: &EnableExtensions,\n) -> Result<'a, crate::AddressSpace> {\n    match word {\n        \"private\" => Ok(crate::AddressSpace::Private),\n        \"workgroup\" => Ok(crate::AddressSpace::WorkGroup),\n        \"uniform\" => Ok(crate::AddressSpace::Uniform),\n        \"storage\" => Ok(crate::AddressSpace::Storage {\n            access: crate::StorageAccess::default(),\n        }),\n        \"immediate\" => Ok(crate::AddressSpace::Immediate),\n        \"function\" => Ok(crate::AddressSpace::Function),\n        \"task_payload\" => {\n            enable_extensions.require(ImplementedEnableExtension::WgpuMeshShader, span)?;\n            Ok(crate::AddressSpace::TaskPayload)\n        }\n        \"ray_payload\" => {\n            if enable_extensions.contains(ImplementedEnableExtension::WgpuRayTracingPipeline) {\n                Ok(crate::AddressSpace::RayPayload)\n            } else {\n                Err(Box::new(Error::EnableExtensionNotEnabled {\n                    span,\n                    kind: ImplementedEnableExtension::WgpuRayTracingPipeline.into(),\n                }))\n            }\n        }\n        \"incoming_ray_payload\" => {\n            if enable_extensions.contains(ImplementedEnableExtension::WgpuRayTracingPipeline) {\n                Ok(crate::AddressSpace::IncomingRayPayload)\n            } else {\n                Err(Box::new(Error::EnableExtensionNotEnabled {\n                    span,\n                    kind: ImplementedEnableExtension::WgpuRayTracingPipeline.into(),\n                }))\n            }\n        }\n        _ => Err(Box::new(Error::UnknownAddressSpace(span))),\n    }\n}\n\npub fn map_access_mode(word: &str, span: Span) -> Result<'_, crate::StorageAccess> {\n    match word {\n        \"read\" => Ok(crate::StorageAccess::LOAD),\n        \"write\" => Ok(crate::StorageAccess::STORE),\n        \"read_write\" => Ok(crate::StorageAccess::LOAD | crate::StorageAccess::STORE),\n        \"atomic\" => Ok(crate::StorageAccess::ATOMIC\n            | crate::StorageAccess::LOAD\n            | crate::StorageAccess::STORE),\n        _ => Err(Box::new(Error::UnknownAccess(span))),\n    }\n}\n\npub fn map_ray_flag(\n    enable_extensions: &EnableExtensions,\n    word: &str,\n    span: Span,\n) -> Result<'static, ()> {\n    match word {\n        \"vertex_return\" => {\n            if !enable_extensions.contains(ImplementedEnableExtension::WgpuRayQueryVertexReturn) {\n                return Err(Box::new(Error::EnableExtensionNotEnabled {\n                    span,\n                    kind: ImplementedEnableExtension::WgpuRayQueryVertexReturn.into(),\n                }));\n            }\n            Ok(())\n        }\n        _ => Err(Box::new(Error::UnknownRayFlag(span))),\n    }\n}\n\npub fn map_cooperative_role(word: &str, span: Span) -> Result<'_, crate::CooperativeRole> {\n    match word {\n        \"A\" => Ok(crate::CooperativeRole::A),\n        \"B\" => Ok(crate::CooperativeRole::B),\n        \"C\" => Ok(crate::CooperativeRole::C),\n        _ => Err(Box::new(Error::UnknownAccess(span))),\n    }\n}\n\npub fn map_built_in(\n    enable_extensions: &EnableExtensions,\n    word: &str,\n    span: Span,\n) -> Result<'static, crate::BuiltIn> {\n    let built_in = match word {\n        \"position\" => crate::BuiltIn::Position { invariant: false },\n        // vertex\n        \"vertex_index\" => crate::BuiltIn::VertexIndex,\n        \"instance_index\" => crate::BuiltIn::InstanceIndex,\n        \"view_index\" => crate::BuiltIn::ViewIndex,\n        \"clip_distances\" => crate::BuiltIn::ClipDistances,\n        // fragment\n        \"front_facing\" => crate::BuiltIn::FrontFacing,\n        \"frag_depth\" => crate::BuiltIn::FragDepth,\n        \"primitive_index\" => crate::BuiltIn::PrimitiveIndex,\n        \"draw_index\" => crate::BuiltIn::DrawIndex,\n        \"barycentric\" => crate::BuiltIn::Barycentric { perspective: true },\n        \"barycentric_no_perspective\" => crate::BuiltIn::Barycentric { perspective: false },\n        \"sample_index\" => crate::BuiltIn::SampleIndex,\n        \"sample_mask\" => crate::BuiltIn::SampleMask,\n        // compute\n        \"global_invocation_id\" => crate::BuiltIn::GlobalInvocationId,\n        \"local_invocation_id\" => crate::BuiltIn::LocalInvocationId,\n        \"local_invocation_index\" => crate::BuiltIn::LocalInvocationIndex,\n        \"workgroup_id\" => crate::BuiltIn::WorkGroupId,\n        \"num_workgroups\" => crate::BuiltIn::NumWorkGroups,\n        // subgroup\n        \"num_subgroups\" => crate::BuiltIn::NumSubgroups,\n        \"subgroup_id\" => crate::BuiltIn::SubgroupId,\n        \"subgroup_size\" => crate::BuiltIn::SubgroupSize,\n        \"subgroup_invocation_id\" => crate::BuiltIn::SubgroupInvocationId,\n        // mesh\n        \"cull_primitive\" => crate::BuiltIn::CullPrimitive,\n        \"point_index\" => crate::BuiltIn::PointIndex,\n        \"line_indices\" => crate::BuiltIn::LineIndices,\n        \"triangle_indices\" => crate::BuiltIn::TriangleIndices,\n        \"mesh_task_size\" => crate::BuiltIn::MeshTaskSize,\n        // mesh global variable\n        \"vertex_count\" => crate::BuiltIn::VertexCount,\n        \"vertices\" => crate::BuiltIn::Vertices,\n        \"primitive_count\" => crate::BuiltIn::PrimitiveCount,\n        \"primitives\" => crate::BuiltIn::Primitives,\n        // ray tracing pipeline\n        \"ray_invocation_id\" => crate::BuiltIn::RayInvocationId,\n        \"num_ray_invocations\" => crate::BuiltIn::NumRayInvocations,\n        \"instance_custom_data\" => crate::BuiltIn::InstanceCustomData,\n        \"geometry_index\" => crate::BuiltIn::GeometryIndex,\n        \"world_ray_origin\" => crate::BuiltIn::WorldRayOrigin,\n        \"world_ray_direction\" => crate::BuiltIn::WorldRayDirection,\n        \"object_ray_origin\" => crate::BuiltIn::ObjectRayOrigin,\n        \"object_ray_direction\" => crate::BuiltIn::ObjectRayDirection,\n        \"ray_t_min\" => crate::BuiltIn::RayTmin,\n        \"ray_t_current_max\" => crate::BuiltIn::RayTCurrentMax,\n        \"object_to_world\" => crate::BuiltIn::ObjectToWorld,\n        \"world_to_object\" => crate::BuiltIn::WorldToObject,\n        \"hit_kind\" => crate::BuiltIn::HitKind,\n        _ => return Err(Box::new(Error::UnknownBuiltin(span))),\n    };\n    match built_in {\n        crate::BuiltIn::ClipDistances => {\n            enable_extensions.require(ImplementedEnableExtension::ClipDistances, span)?\n        }\n        crate::BuiltIn::PrimitiveIndex => {\n            enable_extensions.require(ImplementedEnableExtension::PrimitiveIndex, span)?\n        }\n        crate::BuiltIn::DrawIndex => {\n            enable_extensions.require(ImplementedEnableExtension::DrawIndex, span)?\n        }\n        crate::BuiltIn::CullPrimitive\n        | crate::BuiltIn::PointIndex\n        | crate::BuiltIn::LineIndices\n        | crate::BuiltIn::TriangleIndices\n        | crate::BuiltIn::VertexCount\n        | crate::BuiltIn::Vertices\n        | crate::BuiltIn::PrimitiveCount\n        | crate::BuiltIn::Primitives => {\n            enable_extensions.require(ImplementedEnableExtension::WgpuMeshShader, span)?\n        }\n        _ => {}\n    }\n    Ok(built_in)\n}\n\npub fn map_interpolation(word: &str, span: Span) -> Result<'_, crate::Interpolation> {\n    match word {\n        \"linear\" => Ok(crate::Interpolation::Linear),\n        \"flat\" => Ok(crate::Interpolation::Flat),\n        \"perspective\" => Ok(crate::Interpolation::Perspective),\n        \"per_vertex\" => Ok(crate::Interpolation::PerVertex),\n        _ => Err(Box::new(Error::UnknownAttribute(span))),\n    }\n}\n\npub fn map_sampling(word: &str, span: Span) -> Result<'_, crate::Sampling> {\n    match word {\n        \"center\" => Ok(crate::Sampling::Center),\n        \"centroid\" => Ok(crate::Sampling::Centroid),\n        \"sample\" => Ok(crate::Sampling::Sample),\n        \"first\" => Ok(crate::Sampling::First),\n        \"either\" => Ok(crate::Sampling::Either),\n        _ => Err(Box::new(Error::UnknownAttribute(span))),\n    }\n}\n\npub fn map_storage_format(word: &str, span: Span) -> Result<'_, crate::StorageFormat> {\n    use crate::StorageFormat as Sf;\n    Ok(match word {\n        \"r8unorm\" => Sf::R8Unorm,\n        \"r8snorm\" => Sf::R8Snorm,\n        \"r8uint\" => Sf::R8Uint,\n        \"r8sint\" => Sf::R8Sint,\n        \"r16unorm\" => Sf::R16Unorm,\n        \"r16snorm\" => Sf::R16Snorm,\n        \"r16uint\" => Sf::R16Uint,\n        \"r16sint\" => Sf::R16Sint,\n        \"r16float\" => Sf::R16Float,\n        \"rg8unorm\" => Sf::Rg8Unorm,\n        \"rg8snorm\" => Sf::Rg8Snorm,\n        \"rg8uint\" => Sf::Rg8Uint,\n        \"rg8sint\" => Sf::Rg8Sint,\n        \"r32uint\" => Sf::R32Uint,\n        \"r32sint\" => Sf::R32Sint,\n        \"r32float\" => Sf::R32Float,\n        \"rg16unorm\" => Sf::Rg16Unorm,\n        \"rg16snorm\" => Sf::Rg16Snorm,\n        \"rg16uint\" => Sf::Rg16Uint,\n        \"rg16sint\" => Sf::Rg16Sint,\n        \"rg16float\" => Sf::Rg16Float,\n        \"rgba8unorm\" => Sf::Rgba8Unorm,\n        \"rgba8snorm\" => Sf::Rgba8Snorm,\n        \"rgba8uint\" => Sf::Rgba8Uint,\n        \"rgba8sint\" => Sf::Rgba8Sint,\n        \"rgb10a2uint\" => Sf::Rgb10a2Uint,\n        \"rgb10a2unorm\" => Sf::Rgb10a2Unorm,\n        \"rg11b10ufloat\" => Sf::Rg11b10Ufloat,\n        \"r64uint\" => Sf::R64Uint,\n        \"rg32uint\" => Sf::Rg32Uint,\n        \"rg32sint\" => Sf::Rg32Sint,\n        \"rg32float\" => Sf::Rg32Float,\n        \"rgba16unorm\" => Sf::Rgba16Unorm,\n        \"rgba16snorm\" => Sf::Rgba16Snorm,\n        \"rgba16uint\" => Sf::Rgba16Uint,\n        \"rgba16sint\" => Sf::Rgba16Sint,\n        \"rgba16float\" => Sf::Rgba16Float,\n        \"rgba32uint\" => Sf::Rgba32Uint,\n        \"rgba32sint\" => Sf::Rgba32Sint,\n        \"rgba32float\" => Sf::Rgba32Float,\n        \"bgra8unorm\" => Sf::Bgra8Unorm,\n        _ => return Err(Box::new(Error::UnknownStorageFormat(span))),\n    })\n}\n\npub fn map_derivative(word: &str) -> Option<(crate::DerivativeAxis, crate::DerivativeControl)> {\n    use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};\n    match word {\n        \"dpdxCoarse\" => Some((Axis::X, Ctrl::Coarse)),\n        \"dpdyCoarse\" => Some((Axis::Y, Ctrl::Coarse)),\n        \"fwidthCoarse\" => Some((Axis::Width, Ctrl::Coarse)),\n        \"dpdxFine\" => Some((Axis::X, Ctrl::Fine)),\n        \"dpdyFine\" => Some((Axis::Y, Ctrl::Fine)),\n        \"fwidthFine\" => Some((Axis::Width, Ctrl::Fine)),\n        \"dpdx\" => Some((Axis::X, Ctrl::None)),\n        \"dpdy\" => Some((Axis::Y, Ctrl::None)),\n        \"fwidth\" => Some((Axis::Width, Ctrl::None)),\n        _ => None,\n    }\n}\n\npub fn map_relational_fun(word: &str) -> Option<crate::RelationalFunction> {\n    match word {\n        \"any\" => Some(crate::RelationalFunction::Any),\n        \"all\" => Some(crate::RelationalFunction::All),\n        _ => None,\n    }\n}\n\npub fn map_standard_fun(word: &str) -> Option<crate::MathFunction> {\n    use crate::MathFunction as Mf;\n    Some(match word {\n        // comparison\n        \"abs\" => Mf::Abs,\n        \"min\" => Mf::Min,\n        \"max\" => Mf::Max,\n        \"clamp\" => Mf::Clamp,\n        \"saturate\" => Mf::Saturate,\n        // trigonometry\n        \"cos\" => Mf::Cos,\n        \"cosh\" => Mf::Cosh,\n        \"sin\" => Mf::Sin,\n        \"sinh\" => Mf::Sinh,\n        \"tan\" => Mf::Tan,\n        \"tanh\" => Mf::Tanh,\n        \"acos\" => Mf::Acos,\n        \"acosh\" => Mf::Acosh,\n        \"asin\" => Mf::Asin,\n        \"asinh\" => Mf::Asinh,\n        \"atan\" => Mf::Atan,\n        \"atanh\" => Mf::Atanh,\n        \"atan2\" => Mf::Atan2,\n        \"radians\" => Mf::Radians,\n        \"degrees\" => Mf::Degrees,\n        // decomposition\n        \"ceil\" => Mf::Ceil,\n        \"floor\" => Mf::Floor,\n        \"round\" => Mf::Round,\n        \"fract\" => Mf::Fract,\n        \"trunc\" => Mf::Trunc,\n        \"modf\" => Mf::Modf,\n        \"frexp\" => Mf::Frexp,\n        \"ldexp\" => Mf::Ldexp,\n        // exponent\n        \"exp\" => Mf::Exp,\n        \"exp2\" => Mf::Exp2,\n        \"log\" => Mf::Log,\n        \"log2\" => Mf::Log2,\n        \"pow\" => Mf::Pow,\n        // geometry\n        \"dot\" => Mf::Dot,\n        \"dot4I8Packed\" => Mf::Dot4I8Packed,\n        \"dot4U8Packed\" => Mf::Dot4U8Packed,\n        \"cross\" => Mf::Cross,\n        \"distance\" => Mf::Distance,\n        \"length\" => Mf::Length,\n        \"normalize\" => Mf::Normalize,\n        \"faceForward\" => Mf::FaceForward,\n        \"reflect\" => Mf::Reflect,\n        \"refract\" => Mf::Refract,\n        // computational\n        \"sign\" => Mf::Sign,\n        \"fma\" => Mf::Fma,\n        \"mix\" => Mf::Mix,\n        \"step\" => Mf::Step,\n        \"smoothstep\" => Mf::SmoothStep,\n        \"sqrt\" => Mf::Sqrt,\n        \"inverseSqrt\" => Mf::InverseSqrt,\n        \"transpose\" => Mf::Transpose,\n        \"determinant\" => Mf::Determinant,\n        \"quantizeToF16\" => Mf::QuantizeToF16,\n        // bits\n        \"countTrailingZeros\" => Mf::CountTrailingZeros,\n        \"countLeadingZeros\" => Mf::CountLeadingZeros,\n        \"countOneBits\" => Mf::CountOneBits,\n        \"reverseBits\" => Mf::ReverseBits,\n        \"extractBits\" => Mf::ExtractBits,\n        \"insertBits\" => Mf::InsertBits,\n        \"firstTrailingBit\" => Mf::FirstTrailingBit,\n        \"firstLeadingBit\" => Mf::FirstLeadingBit,\n        // data packing\n        \"pack4x8snorm\" => Mf::Pack4x8snorm,\n        \"pack4x8unorm\" => Mf::Pack4x8unorm,\n        \"pack2x16snorm\" => Mf::Pack2x16snorm,\n        \"pack2x16unorm\" => Mf::Pack2x16unorm,\n        \"pack2x16float\" => Mf::Pack2x16float,\n        \"pack4xI8\" => Mf::Pack4xI8,\n        \"pack4xU8\" => Mf::Pack4xU8,\n        \"pack4xI8Clamp\" => Mf::Pack4xI8Clamp,\n        \"pack4xU8Clamp\" => Mf::Pack4xU8Clamp,\n        // data unpacking\n        \"unpack4x8snorm\" => Mf::Unpack4x8snorm,\n        \"unpack4x8unorm\" => Mf::Unpack4x8unorm,\n        \"unpack2x16snorm\" => Mf::Unpack2x16snorm,\n        \"unpack2x16unorm\" => Mf::Unpack2x16unorm,\n        \"unpack2x16float\" => Mf::Unpack2x16float,\n        \"unpack4xI8\" => Mf::Unpack4xI8,\n        \"unpack4xU8\" => Mf::Unpack4xU8,\n        _ => return None,\n    })\n}\n\npub fn map_conservative_depth(word: &str, span: Span) -> Result<'_, crate::ConservativeDepth> {\n    use crate::ConservativeDepth as Cd;\n    match word {\n        \"greater_equal\" => Ok(Cd::GreaterEqual),\n        \"less_equal\" => Ok(Cd::LessEqual),\n        \"unchanged\" => Ok(Cd::Unchanged),\n        _ => Err(Box::new(Error::UnknownConservativeDepth(span))),\n    }\n}\n\npub fn map_subgroup_operation(\n    word: &str,\n) -> Option<(crate::SubgroupOperation, crate::CollectiveOperation)> {\n    use crate::CollectiveOperation as co;\n    use crate::SubgroupOperation as sg;\n    Some(match word {\n        \"subgroupAll\" => (sg::All, co::Reduce),\n        \"subgroupAny\" => (sg::Any, co::Reduce),\n        \"subgroupAdd\" => (sg::Add, co::Reduce),\n        \"subgroupMul\" => (sg::Mul, co::Reduce),\n        \"subgroupMin\" => (sg::Min, co::Reduce),\n        \"subgroupMax\" => (sg::Max, co::Reduce),\n        \"subgroupAnd\" => (sg::And, co::Reduce),\n        \"subgroupOr\" => (sg::Or, co::Reduce),\n        \"subgroupXor\" => (sg::Xor, co::Reduce),\n        \"subgroupExclusiveAdd\" => (sg::Add, co::ExclusiveScan),\n        \"subgroupExclusiveMul\" => (sg::Mul, co::ExclusiveScan),\n        \"subgroupInclusiveAdd\" => (sg::Add, co::InclusiveScan),\n        \"subgroupInclusiveMul\" => (sg::Mul, co::InclusiveScan),\n        _ => return None,\n    })\n}\n\npub enum TypeGenerator {\n    Vector {\n        size: VectorSize,\n    },\n    Matrix {\n        columns: VectorSize,\n        rows: VectorSize,\n    },\n    Array,\n    Atomic,\n    Pointer,\n    SampledTexture {\n        dim: ImageDimension,\n        arrayed: bool,\n        multi: bool,\n    },\n    StorageTexture {\n        dim: ImageDimension,\n        arrayed: bool,\n    },\n    BindingArray,\n    AccelerationStructure,\n    RayQuery,\n    CooperativeMatrix {\n        columns: crate::CooperativeSize,\n        rows: crate::CooperativeSize,\n    },\n}\n\npub enum PredeclaredType {\n    TypeInner(TypeInner),\n    RayDesc,\n    RayIntersection,\n    TypeGenerator(TypeGenerator),\n}\nimpl From<TypeInner> for PredeclaredType {\n    fn from(value: TypeInner) -> Self {\n        Self::TypeInner(value)\n    }\n}\nimpl From<TypeGenerator> for PredeclaredType {\n    fn from(value: TypeGenerator) -> Self {\n        Self::TypeGenerator(value)\n    }\n}\n\npub fn map_predeclared_type(\n    enable_extensions: &EnableExtensions,\n    span: Span,\n    word: &str,\n) -> Result<'static, Option<PredeclaredType>> {\n    use Scalar as Sc;\n    use TypeInner as Ti;\n    use VectorSize as Vs;\n\n    #[rustfmt::skip]\n    let ty = match word {\n        // predeclared types\n\n        // scalars\n        \"bool\" => Ti::Scalar(Sc::BOOL).into(),\n        \"i32\" => Ti::Scalar(Sc::I32).into(),\n        \"u32\" => Ti::Scalar(Sc::U32).into(),\n        \"f32\" => Ti::Scalar(Sc::F32).into(),\n        \"f16\" => Ti::Scalar(Sc::F16).into(),\n        \"i64\" => Ti::Scalar(Sc::I64).into(),\n        \"u64\" => Ti::Scalar(Sc::U64).into(),\n        \"f64\" => Ti::Scalar(Sc::F64).into(),\n        // vector aliases\n        \"vec2i\" => Ti::Vector { size: Vs::Bi,   scalar: Sc::I32 }.into(),\n        \"vec3i\" => Ti::Vector { size: Vs::Tri,  scalar: Sc::I32 }.into(),\n        \"vec4i\" => Ti::Vector { size: Vs::Quad, scalar: Sc::I32 }.into(),\n        \"vec2u\" => Ti::Vector { size: Vs::Bi,   scalar: Sc::U32 }.into(),\n        \"vec3u\" => Ti::Vector { size: Vs::Tri,  scalar: Sc::U32 }.into(),\n        \"vec4u\" => Ti::Vector { size: Vs::Quad, scalar: Sc::U32 }.into(),\n        \"vec2f\" => Ti::Vector { size: Vs::Bi,   scalar: Sc::F32 }.into(),\n        \"vec3f\" => Ti::Vector { size: Vs::Tri,  scalar: Sc::F32 }.into(),\n        \"vec4f\" => Ti::Vector { size: Vs::Quad, scalar: Sc::F32 }.into(),\n        \"vec2h\" => Ti::Vector { size: Vs::Bi,   scalar: Sc::F16 }.into(),\n        \"vec3h\" => Ti::Vector { size: Vs::Tri,  scalar: Sc::F16 }.into(),\n        \"vec4h\" => Ti::Vector { size: Vs::Quad, scalar: Sc::F16 }.into(),\n        // matrix aliases\n        \"mat2x2f\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Bi,   scalar: Sc::F32 }.into(),\n        \"mat2x3f\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Tri,  scalar: Sc::F32 }.into(),\n        \"mat2x4f\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Quad, scalar: Sc::F32 }.into(),\n        \"mat3x2f\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Bi,   scalar: Sc::F32 }.into(),\n        \"mat3x3f\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Tri,  scalar: Sc::F32 }.into(),\n        \"mat3x4f\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Quad, scalar: Sc::F32 }.into(),\n        \"mat4x2f\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Bi,   scalar: Sc::F32 }.into(),\n        \"mat4x3f\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Tri,  scalar: Sc::F32 }.into(),\n        \"mat4x4f\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Quad, scalar: Sc::F32 }.into(),\n        \"mat2x2h\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Bi,   scalar: Sc::F16 }.into(),\n        \"mat2x3h\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Tri,  scalar: Sc::F16 }.into(),\n        \"mat2x4h\" => Ti::Matrix { columns: Vs::Bi,   rows: Vs::Quad, scalar: Sc::F16 }.into(),\n        \"mat3x2h\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Bi,   scalar: Sc::F16 }.into(),\n        \"mat3x3h\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Tri,  scalar: Sc::F16 }.into(),\n        \"mat3x4h\" => Ti::Matrix { columns: Vs::Tri,  rows: Vs::Quad, scalar: Sc::F16 }.into(),\n        \"mat4x2h\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Bi,   scalar: Sc::F16 }.into(),\n        \"mat4x3h\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Tri,  scalar: Sc::F16 }.into(),\n        \"mat4x4h\" => Ti::Matrix { columns: Vs::Quad, rows: Vs::Quad, scalar: Sc::F16 }.into(),\n        // samplers\n        \"sampler\" =>            Ti::Sampler { comparison: false }.into(),\n        \"sampler_comparison\" => Ti::Sampler { comparison: true }.into(),\n        // depth textures\n        \"texture_depth_2d\" =>              Ti::Image { dim: ImageDimension::D2,   arrayed: false, class: ImageClass::Depth { multi: false } }.into(),\n        \"texture_depth_2d_array\" =>        Ti::Image { dim: ImageDimension::D2,   arrayed: true,  class: ImageClass::Depth { multi: false } }.into(),\n        \"texture_depth_cube\" =>            Ti::Image { dim: ImageDimension::Cube, arrayed: false, class: ImageClass::Depth { multi: false } }.into(),\n        \"texture_depth_cube_array\" =>      Ti::Image { dim: ImageDimension::Cube, arrayed: true,  class: ImageClass::Depth { multi: false } }.into(),\n        \"texture_depth_multisampled_2d\" => Ti::Image { dim: ImageDimension::D2,   arrayed: false, class: ImageClass::Depth { multi: true  } }.into(),\n        // external texture\n        \"texture_external\" => Ti::Image { dim: ImageDimension::D2, arrayed: false, class: ImageClass::External }.into(),\n        // ray desc\n        \"RayDesc\" => PredeclaredType::RayDesc,\n        // ray intersection\n        \"RayIntersection\" => PredeclaredType::RayIntersection,\n\n        // predeclared type generators\n\n        // vector\n        \"vec2\" => TypeGenerator::Vector { size: Vs::Bi   }.into(),\n        \"vec3\" => TypeGenerator::Vector { size: Vs::Tri  }.into(),\n        \"vec4\" => TypeGenerator::Vector { size: Vs::Quad }.into(),\n        // matrix\n        \"mat2x2\" => TypeGenerator::Matrix { columns: Vs::Bi,   rows: Vs::Bi   }.into(),\n        \"mat2x3\" => TypeGenerator::Matrix { columns: Vs::Bi,   rows: Vs::Tri  }.into(),\n        \"mat2x4\" => TypeGenerator::Matrix { columns: Vs::Bi,   rows: Vs::Quad }.into(),\n        \"mat3x2\" => TypeGenerator::Matrix { columns: Vs::Tri,  rows: Vs::Bi   }.into(),\n        \"mat3x3\" => TypeGenerator::Matrix { columns: Vs::Tri,  rows: Vs::Tri  }.into(),\n        \"mat3x4\" => TypeGenerator::Matrix { columns: Vs::Tri,  rows: Vs::Quad }.into(),\n        \"mat4x2\" => TypeGenerator::Matrix { columns: Vs::Quad, rows: Vs::Bi   }.into(),\n        \"mat4x3\" => TypeGenerator::Matrix { columns: Vs::Quad, rows: Vs::Tri  }.into(),\n        \"mat4x4\" => TypeGenerator::Matrix { columns: Vs::Quad, rows: Vs::Quad }.into(),\n        // array\n        \"array\" => TypeGenerator::Array.into(),\n        // atomic\n        \"atomic\" => TypeGenerator::Atomic.into(),\n        // pointer\n        \"ptr\" => TypeGenerator::Pointer.into(),\n        // sampled textures\n        \"texture_1d\" =>               TypeGenerator::SampledTexture { dim: ImageDimension::D1,   arrayed: false, multi: false }.into(),\n        \"texture_2d\" =>               TypeGenerator::SampledTexture { dim: ImageDimension::D2,   arrayed: false, multi: false }.into(),\n        \"texture_2d_array\" =>         TypeGenerator::SampledTexture { dim: ImageDimension::D2,   arrayed: true,  multi: false }.into(),\n        \"texture_3d\" =>               TypeGenerator::SampledTexture { dim: ImageDimension::D3,   arrayed: false, multi: false }.into(),\n        \"texture_cube\" =>             TypeGenerator::SampledTexture { dim: ImageDimension::Cube, arrayed: false, multi: false }.into(),\n        \"texture_cube_array\" =>       TypeGenerator::SampledTexture { dim: ImageDimension::Cube, arrayed: true,  multi: false }.into(),\n        \"texture_multisampled_2d\" =>  TypeGenerator::SampledTexture { dim: ImageDimension::D2,   arrayed: false, multi: true  }.into(),\n        // storage textures\n        \"texture_storage_1d\" =>       TypeGenerator::StorageTexture { dim: ImageDimension::D1,   arrayed: false }.into(),\n        \"texture_storage_2d\" =>       TypeGenerator::StorageTexture { dim: ImageDimension::D2,   arrayed: false }.into(),\n        \"texture_storage_2d_array\" => TypeGenerator::StorageTexture { dim: ImageDimension::D2,   arrayed: true  }.into(),\n        \"texture_storage_3d\" =>       TypeGenerator::StorageTexture { dim: ImageDimension::D3,   arrayed: false }.into(),\n        // binding array\n        \"binding_array\" => TypeGenerator::BindingArray.into(),\n        // acceleration structure\n        \"acceleration_structure\" => TypeGenerator::AccelerationStructure.into(),\n        // ray query\n        \"ray_query\" => TypeGenerator::RayQuery.into(),\n        // cooperative matrix\n        \"coop_mat8x8\" => TypeGenerator::CooperativeMatrix {\n            columns: crate::CooperativeSize::Eight,\n            rows: crate::CooperativeSize::Eight,\n        }.into(),\n        \"coop_mat16x16\" => TypeGenerator::CooperativeMatrix {\n            columns: crate::CooperativeSize::Sixteen,\n            rows: crate::CooperativeSize::Sixteen,\n        }.into(),\n        _ => return Ok(None),\n    };\n\n    // Check for the enable extension required to use this type, if any.\n    // Slice should be at least len one otherwise extension_needed should be None.\n    let extensions_needed: Option<&[_]> = match ty {\n        PredeclaredType::TypeInner(ref ty) if ty.scalar() == Some(Sc::F16) => {\n            Some(&[ImplementedEnableExtension::F16])\n        }\n        PredeclaredType::RayDesc\n        | PredeclaredType::RayIntersection\n        | PredeclaredType::TypeGenerator(TypeGenerator::AccelerationStructure)\n        | PredeclaredType::TypeGenerator(TypeGenerator::RayQuery) => Some(&[\n            ImplementedEnableExtension::WgpuRayQuery,\n            ImplementedEnableExtension::WgpuRayTracingPipeline,\n        ]),\n        PredeclaredType::TypeGenerator(TypeGenerator::CooperativeMatrix { .. }) => {\n            Some(&[ImplementedEnableExtension::WgpuCooperativeMatrix])\n        }\n        _ => None,\n    };\n    if let Some(extensions_needed) = extensions_needed {\n        let mut any_extension_enabled = false;\n        for extension_needed in extensions_needed {\n            if enable_extensions.contains(*extension_needed) {\n                any_extension_enabled = true;\n            }\n        }\n        if !any_extension_enabled {\n            return Err(Box::new(Error::EnableExtensionNotEnabled {\n                span,\n                kind: extensions_needed[0].into(),\n            }));\n        }\n    }\n\n    Ok(Some(ty))\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/directive/enable_extension.rs",
    "content": "//! `enable …;` extensions in WGSL.\n//!\n//! The focal point of this module is the [`EnableExtension`] API.\n\nuse crate::front::wgsl::{Error, Result};\nuse crate::Span;\n\nuse alloc::boxed::Box;\n\n/// Tracks the status of every enable-extension known to Naga.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub(crate) struct EnableExtensions {\n    wgpu_mesh_shader: bool,\n    wgpu_ray_query: bool,\n    wgpu_ray_query_vertex_return: bool,\n    wgpu_ray_tracing_pipelines: bool,\n    dual_source_blending: bool,\n    /// Whether `enable f16;` was written earlier in the shader module.\n    f16: bool,\n    clip_distances: bool,\n    wgpu_cooperative_matrix: bool,\n    draw_index: bool,\n    primitive_index: bool,\n}\n\nimpl EnableExtensions {\n    pub(crate) const fn empty() -> Self {\n        Self {\n            wgpu_mesh_shader: false,\n            wgpu_ray_query: false,\n            wgpu_ray_query_vertex_return: false,\n            wgpu_ray_tracing_pipelines: false,\n            f16: false,\n            dual_source_blending: false,\n            clip_distances: false,\n            wgpu_cooperative_matrix: false,\n            draw_index: false,\n            primitive_index: false,\n        }\n    }\n\n    /// Add an enable-extension to the set requested by a module.\n    pub(crate) const fn add(&mut self, ext: ImplementedEnableExtension) {\n        let field = match ext {\n            ImplementedEnableExtension::WgpuMeshShader => &mut self.wgpu_mesh_shader,\n            ImplementedEnableExtension::WgpuRayQuery => &mut self.wgpu_ray_query,\n            ImplementedEnableExtension::WgpuRayQueryVertexReturn => {\n                &mut self.wgpu_ray_query_vertex_return\n            }\n            ImplementedEnableExtension::WgpuRayTracingPipeline => {\n                &mut self.wgpu_ray_tracing_pipelines\n            }\n            ImplementedEnableExtension::DualSourceBlending => &mut self.dual_source_blending,\n            ImplementedEnableExtension::F16 => &mut self.f16,\n            ImplementedEnableExtension::ClipDistances => &mut self.clip_distances,\n            ImplementedEnableExtension::WgpuCooperativeMatrix => &mut self.wgpu_cooperative_matrix,\n            ImplementedEnableExtension::DrawIndex => &mut self.draw_index,\n            ImplementedEnableExtension::PrimitiveIndex => &mut self.primitive_index,\n        };\n        *field = true;\n    }\n\n    /// Query whether an enable-extension tracked here has been requested.\n    pub(crate) const fn contains(&self, ext: ImplementedEnableExtension) -> bool {\n        match ext {\n            ImplementedEnableExtension::WgpuMeshShader => self.wgpu_mesh_shader,\n            ImplementedEnableExtension::WgpuRayQuery => self.wgpu_ray_query,\n            ImplementedEnableExtension::WgpuRayQueryVertexReturn => {\n                self.wgpu_ray_query_vertex_return\n            }\n            ImplementedEnableExtension::WgpuRayTracingPipeline => self.wgpu_ray_tracing_pipelines,\n            ImplementedEnableExtension::DualSourceBlending => self.dual_source_blending,\n            ImplementedEnableExtension::F16 => self.f16,\n            ImplementedEnableExtension::ClipDistances => self.clip_distances,\n            ImplementedEnableExtension::WgpuCooperativeMatrix => self.wgpu_cooperative_matrix,\n            ImplementedEnableExtension::DrawIndex => self.draw_index,\n            ImplementedEnableExtension::PrimitiveIndex => self.primitive_index,\n        }\n    }\n\n    pub(crate) fn require(\n        &self,\n        ext: ImplementedEnableExtension,\n        span: Span,\n    ) -> Result<'static, ()> {\n        if !self.contains(ext) {\n            Err(Box::new(Error::EnableExtensionNotEnabled {\n                span,\n                kind: ext.into(),\n            }))\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl Default for EnableExtensions {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\n/// An enable-extension not guaranteed to be present in all environments.\n///\n/// WGSL spec.: <https://www.w3.org/TR/WGSL/#enable-extensions-sec>\n#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]\npub enum EnableExtension {\n    Implemented(ImplementedEnableExtension),\n    Unimplemented(UnimplementedEnableExtension),\n}\n\nimpl From<ImplementedEnableExtension> for EnableExtension {\n    fn from(value: ImplementedEnableExtension) -> Self {\n        Self::Implemented(value)\n    }\n}\n\nimpl EnableExtension {\n    const F16: &'static str = \"f16\";\n    const CLIP_DISTANCES: &'static str = \"clip_distances\";\n    const DUAL_SOURCE_BLENDING: &'static str = \"dual_source_blending\";\n    const MESH_SHADER: &'static str = \"wgpu_mesh_shader\";\n    const RAY_QUERY: &'static str = \"wgpu_ray_query\";\n    const RAY_QUERY_VERTEX_RETURN: &'static str = \"wgpu_ray_query_vertex_return\";\n    const RAY_TRACING_PIPELINE: &'static str = \"wgpu_ray_tracing_pipeline\";\n    const COOPERATIVE_MATRIX: &'static str = \"wgpu_cooperative_matrix\";\n    const SUBGROUPS: &'static str = \"subgroups\";\n    const PRIMITIVE_INDEX: &'static str = \"primitive_index\";\n    const DRAW_INDEX: &'static str = \"draw_index\";\n\n    /// Convert from a sentinel word in WGSL into its associated [`EnableExtension`], if possible.\n    pub(crate) fn from_ident(word: &str, span: Span) -> Result<'_, Self> {\n        Ok(match word {\n            Self::F16 => Self::Implemented(ImplementedEnableExtension::F16),\n            Self::CLIP_DISTANCES => Self::Implemented(ImplementedEnableExtension::ClipDistances),\n            Self::DUAL_SOURCE_BLENDING => {\n                Self::Implemented(ImplementedEnableExtension::DualSourceBlending)\n            }\n            Self::MESH_SHADER => Self::Implemented(ImplementedEnableExtension::WgpuMeshShader),\n            Self::RAY_QUERY => Self::Implemented(ImplementedEnableExtension::WgpuRayQuery),\n            Self::RAY_QUERY_VERTEX_RETURN => {\n                Self::Implemented(ImplementedEnableExtension::WgpuRayQueryVertexReturn)\n            }\n            Self::RAY_TRACING_PIPELINE => {\n                Self::Implemented(ImplementedEnableExtension::WgpuRayTracingPipeline)\n            }\n            Self::COOPERATIVE_MATRIX => {\n                Self::Implemented(ImplementedEnableExtension::WgpuCooperativeMatrix)\n            }\n            Self::SUBGROUPS => Self::Unimplemented(UnimplementedEnableExtension::Subgroups),\n            Self::DRAW_INDEX => Self::Implemented(ImplementedEnableExtension::DrawIndex),\n            Self::PRIMITIVE_INDEX => Self::Implemented(ImplementedEnableExtension::PrimitiveIndex),\n            _ => return Err(Box::new(Error::UnknownEnableExtension(span, word))),\n        })\n    }\n\n    /// Maps this [`EnableExtension`] into the sentinel word associated with it in WGSL.\n    pub const fn to_ident(self) -> &'static str {\n        match self {\n            Self::Implemented(kind) => match kind {\n                ImplementedEnableExtension::WgpuMeshShader => Self::MESH_SHADER,\n                ImplementedEnableExtension::WgpuRayQuery => Self::RAY_QUERY,\n                ImplementedEnableExtension::WgpuRayQueryVertexReturn => {\n                    Self::RAY_QUERY_VERTEX_RETURN\n                }\n                ImplementedEnableExtension::WgpuCooperativeMatrix => Self::COOPERATIVE_MATRIX,\n                ImplementedEnableExtension::DualSourceBlending => Self::DUAL_SOURCE_BLENDING,\n                ImplementedEnableExtension::F16 => Self::F16,\n                ImplementedEnableExtension::ClipDistances => Self::CLIP_DISTANCES,\n                ImplementedEnableExtension::DrawIndex => Self::DRAW_INDEX,\n                ImplementedEnableExtension::PrimitiveIndex => Self::PRIMITIVE_INDEX,\n                ImplementedEnableExtension::WgpuRayTracingPipeline => Self::RAY_TRACING_PIPELINE,\n            },\n            Self::Unimplemented(kind) => match kind {\n                UnimplementedEnableExtension::Subgroups => Self::SUBGROUPS,\n            },\n        }\n    }\n}\n\n/// A variant of [`EnableExtension::Implemented`].\n#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]\n#[cfg_attr(test, derive(strum::VariantArray))]\npub enum ImplementedEnableExtension {\n    /// Enables `f16`/`half` primitive support in all shader languages.\n    ///\n    /// In the WGSL standard, this corresponds to [`enable f16;`].\n    ///\n    /// [`enable f16;`]: https://www.w3.org/TR/WGSL/#extension-f16\n    F16,\n    /// Enables the `blend_src` attribute in WGSL.\n    ///\n    /// In the WGSL standard, this corresponds to [`enable dual_source_blending;`].\n    ///\n    /// [`enable dual_source_blending;`]: https://www.w3.org/TR/WGSL/#extension-dual_source_blending\n    DualSourceBlending,\n    /// Enables the `clip_distances` variable in WGSL.\n    ///\n    /// In the WGSL standard, this corresponds to [`enable clip_distances;`].\n    ///\n    /// [`enable clip_distances;`]: https://www.w3.org/TR/WGSL/#extension-clip_distances\n    ClipDistances,\n    /// Enables the `wgpu_mesh_shader` extension, native only\n    WgpuMeshShader,\n    /// Enables the `wgpu_ray_query` extension, native only.\n    WgpuRayQuery,\n    /// Enables the `wgpu_ray_query_vertex_return` extension, native only.\n    WgpuRayQueryVertexReturn,\n    /// Enables the `wgpu_ray_tracing_pipeline` extension, native only.\n    WgpuRayTracingPipeline,\n    /// Enables the `wgpu_cooperative_matrix` extension, native only.\n    WgpuCooperativeMatrix,\n    /// Enables the `draw_index` builtin. Not currently part of the WGSL spec but probably will be at some point.\n    DrawIndex,\n    /// Enables the `@builtin(primitive_index)` attribute in WGSL.\n    ///\n    /// In the WGSL standard, this corresponds to [`enable primitive-index;`].\n    ///\n    /// [`enable primitive-index;`]: https://www.w3.org/TR/WGSL/#extension-primitive_index\n    PrimitiveIndex,\n}\n\nimpl ImplementedEnableExtension {\n    /// A slice of all variants of [`ImplementedEnableExtension`].\n    pub const VARIANTS: &'static [Self] = &[\n        Self::F16,\n        Self::DualSourceBlending,\n        Self::ClipDistances,\n        Self::WgpuMeshShader,\n        Self::WgpuRayQuery,\n        Self::WgpuRayQueryVertexReturn,\n        Self::WgpuRayTracingPipeline,\n        Self::WgpuCooperativeMatrix,\n        Self::DrawIndex,\n        Self::PrimitiveIndex,\n    ];\n\n    /// Returns slice of all variants of [`ImplementedEnableExtension`].\n    pub const fn all() -> &'static [Self] {\n        Self::VARIANTS\n    }\n\n    /// Returns the capability required for this enable extension.\n    pub const fn capability(self) -> crate::valid::Capabilities {\n        use crate::valid::Capabilities as C;\n        match self {\n            Self::F16 => C::SHADER_FLOAT16,\n            Self::DualSourceBlending => C::DUAL_SOURCE_BLENDING,\n            Self::ClipDistances => C::CLIP_DISTANCES,\n            Self::WgpuMeshShader => C::MESH_SHADER,\n            Self::WgpuRayQuery => C::RAY_QUERY,\n            Self::WgpuRayQueryVertexReturn => C::RAY_HIT_VERTEX_POSITION,\n            Self::WgpuCooperativeMatrix => C::COOPERATIVE_MATRIX,\n            Self::WgpuRayTracingPipeline => C::RAY_TRACING_PIPELINE,\n            Self::DrawIndex => C::DRAW_INDEX,\n            Self::PrimitiveIndex => C::PRIMITIVE_INDEX,\n        }\n    }\n}\n\n#[test]\n/// Asserts that the manual implementation of VARIANTS is the same as the derived strum version would be\n/// while still allowing strum to be a dev-only dependency\nfn test_manual_variants_array_is_correct() {\n    assert_eq!(\n        <ImplementedEnableExtension as strum::VariantArray>::VARIANTS,\n        ImplementedEnableExtension::VARIANTS\n    );\n}\n\n/// A variant of [`EnableExtension::Unimplemented`].\n#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]\npub enum UnimplementedEnableExtension {\n    /// Enables subgroup built-ins in all languages.\n    ///\n    /// In the WGSL standard, this corresponds to [`enable subgroups;`].\n    ///\n    /// [`enable subgroups;`]: https://www.w3.org/TR/WGSL/#extension-subgroups\n    Subgroups,\n}\n\nimpl UnimplementedEnableExtension {\n    pub(crate) const fn tracking_issue_num(self) -> u16 {\n        match self {\n            Self::Subgroups => 5555,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/directive/language_extension.rs",
    "content": "//! `requires …;` extensions in WGSL.\n//!\n//! The focal point of this module is the [`LanguageExtension`] API.\n\n/// A language extension recognized by Naga, but not guaranteed to be present in all environments.\n///\n/// WGSL spec.: <https://www.w3.org/TR/WGSL/#language-extensions-sec>\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub enum LanguageExtension {\n    Implemented(ImplementedLanguageExtension),\n    Unimplemented(UnimplementedLanguageExtension),\n}\n\nimpl LanguageExtension {\n    const READONLY_AND_READWRITE_STORAGE_TEXTURES: &'static str =\n        \"readonly_and_readwrite_storage_textures\";\n    const PACKED4X8_INTEGER_DOT_PRODUCT: &'static str = \"packed_4x8_integer_dot_product\";\n    const UNRESTRICTED_POINTER_PARAMETERS: &'static str = \"unrestricted_pointer_parameters\";\n    const POINTER_COMPOSITE_ACCESS: &'static str = \"pointer_composite_access\";\n\n    /// Convert from a sentinel word in WGSL into its associated [`LanguageExtension`], if possible.\n    pub fn from_ident(s: &str) -> Option<Self> {\n        Some(match s {\n            Self::READONLY_AND_READWRITE_STORAGE_TEXTURES => {\n                Self::Implemented(ImplementedLanguageExtension::ReadOnlyAndReadWriteStorageTextures)\n            }\n            Self::PACKED4X8_INTEGER_DOT_PRODUCT => {\n                Self::Implemented(ImplementedLanguageExtension::Packed4x8IntegerDotProduct)\n            }\n            Self::UNRESTRICTED_POINTER_PARAMETERS => {\n                Self::Unimplemented(UnimplementedLanguageExtension::UnrestrictedPointerParameters)\n            }\n            Self::POINTER_COMPOSITE_ACCESS => {\n                Self::Implemented(ImplementedLanguageExtension::PointerCompositeAccess)\n            }\n            _ => return None,\n        })\n    }\n\n    /// Maps this [`LanguageExtension`] into the sentinel word associated with it in WGSL.\n    pub const fn to_ident(self) -> &'static str {\n        match self {\n            Self::Implemented(kind) => kind.to_ident(),\n            Self::Unimplemented(kind) => match kind {\n                UnimplementedLanguageExtension::UnrestrictedPointerParameters => {\n                    Self::UNRESTRICTED_POINTER_PARAMETERS\n                }\n            },\n        }\n    }\n}\n\n/// A variant of [`LanguageExtension::Implemented`].\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(test, derive(strum::VariantArray))]\npub enum ImplementedLanguageExtension {\n    ReadOnlyAndReadWriteStorageTextures,\n    Packed4x8IntegerDotProduct,\n    PointerCompositeAccess,\n}\n\nimpl ImplementedLanguageExtension {\n    /// A slice of all variants of [`ImplementedLanguageExtension`].\n    pub const VARIANTS: &'static [Self] = &[\n        Self::ReadOnlyAndReadWriteStorageTextures,\n        Self::Packed4x8IntegerDotProduct,\n        Self::PointerCompositeAccess,\n    ];\n\n    /// Returns slice of all variants of [`ImplementedLanguageExtension`].\n    pub const fn all() -> &'static [Self] {\n        Self::VARIANTS\n    }\n\n    /// Maps this [`ImplementedLanguageExtension`] into the sentinel word associated with it in WGSL.\n    pub const fn to_ident(self) -> &'static str {\n        match self {\n            ImplementedLanguageExtension::ReadOnlyAndReadWriteStorageTextures => {\n                LanguageExtension::READONLY_AND_READWRITE_STORAGE_TEXTURES\n            }\n            ImplementedLanguageExtension::Packed4x8IntegerDotProduct => {\n                LanguageExtension::PACKED4X8_INTEGER_DOT_PRODUCT\n            }\n            ImplementedLanguageExtension::PointerCompositeAccess => {\n                LanguageExtension::POINTER_COMPOSITE_ACCESS\n            }\n        }\n    }\n}\n\n#[test]\n/// Asserts that the manual implementation of VARIANTS is the same as the derived strum version would be\n/// while still allowing strum to be a dev-only dependency\nfn test_manual_variants_array_is_correct() {\n    assert_eq!(\n        <ImplementedLanguageExtension as strum::VariantArray>::VARIANTS,\n        ImplementedLanguageExtension::VARIANTS\n    );\n}\n\n/// A variant of [`LanguageExtension::Unimplemented`].\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub enum UnimplementedLanguageExtension {\n    UnrestrictedPointerParameters,\n}\n\nimpl UnimplementedLanguageExtension {\n    pub(crate) const fn tracking_issue_num(self) -> u16 {\n        match self {\n            Self::UnrestrictedPointerParameters => 5158,\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/directive.rs",
    "content": "//! WGSL directives. The focal point of this API is [`DirectiveKind`].\n//!\n//! See also <https://www.w3.org/TR/WGSL/#directives>.\n\npub mod enable_extension;\npub(crate) mod language_extension;\n\nuse alloc::boxed::Box;\n\n/// A parsed sentinel word indicating the type of directive to be parsed next.\n#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]\n#[cfg_attr(test, derive(strum::EnumIter))]\npub(crate) enum DirectiveKind {\n    /// A [`crate::diagnostic_filter`].\n    Diagnostic,\n    /// An [`enable_extension`].\n    Enable,\n    /// A [`language_extension`].\n    Requires,\n}\n\nimpl DirectiveKind {\n    const DIAGNOSTIC: &'static str = \"diagnostic\";\n    const ENABLE: &'static str = \"enable\";\n    const REQUIRES: &'static str = \"requires\";\n\n    /// Convert from a sentinel word in WGSL into its associated [`DirectiveKind`], if possible.\n    pub fn from_ident(s: &str) -> Option<Self> {\n        Some(match s {\n            Self::DIAGNOSTIC => Self::Diagnostic,\n            Self::ENABLE => Self::Enable,\n            Self::REQUIRES => Self::Requires,\n            _ => return None,\n        })\n    }\n}\n\nimpl crate::diagnostic_filter::Severity {\n    #[cfg(feature = \"wgsl-in\")]\n    pub(crate) fn report_wgsl_parse_diag<'a>(\n        self,\n        err: Box<crate::front::wgsl::error::Error<'a>>,\n        source: &str,\n    ) -> crate::front::wgsl::Result<'a, ()> {\n        self.report_diag(err, |e, level| {\n            let e = e.as_parse_error(source);\n            log::log!(level, \"{}\", e.emit_to_string(source));\n        })\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use alloc::format;\n\n    use strum::IntoEnumIterator;\n\n    use super::DirectiveKind;\n    use crate::front::wgsl::assert_parse_err;\n\n    #[test]\n    fn directive_after_global_decl() {\n        for unsupported_shader in DirectiveKind::iter() {\n            let directive;\n            let expected_msg;\n            match unsupported_shader {\n                DirectiveKind::Diagnostic => {\n                    directive = \"diagnostic(off,derivative_uniformity)\";\n                    expected_msg = \"\\\nerror: expected global declaration, but found a global directive\n  ┌─ wgsl:2:1\n  │\n2 │ diagnostic(off,derivative_uniformity);\n  │ ^^^^^^^^^^ written after first global declaration\n  │\n  = note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?\n\n\";\n                }\n                DirectiveKind::Enable => {\n                    directive = \"enable f16\";\n                    expected_msg = \"\\\nerror: expected global declaration, but found a global directive\n  ┌─ wgsl:2:1\n  │\n2 │ enable f16;\n  │ ^^^^^^ written after first global declaration\n  │\n  = note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?\n\n\";\n                }\n                DirectiveKind::Requires => {\n                    directive = \"requires readonly_and_readwrite_storage_textures\";\n                    expected_msg = \"\\\nerror: expected global declaration, but found a global directive\n  ┌─ wgsl:2:1\n  │\n2 │ requires readonly_and_readwrite_storage_textures;\n  │ ^^^^^^^^ written after first global declaration\n  │\n  = note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?\n\n\";\n                }\n            }\n\n            let shader = format!(\n                \"\\\n@group(0) @binding(0) var<storage> thing: i32;\n{directive};\n\"\n            );\n            assert_parse_err(&shader, expected_msg);\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/lexer.rs",
    "content": "use super::{number::consume_number, Error, ExpectedToken, Result};\nuse crate::front::wgsl::error::NumberError;\nuse crate::front::wgsl::parse::directive::enable_extension::{\n    EnableExtensions, ImplementedEnableExtension,\n};\nuse crate::front::wgsl::parse::Number;\nuse crate::Span;\n\nuse alloc::{boxed::Box, vec::Vec};\n\npub type TokenSpan<'a> = (Token<'a>, Span);\n\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum Token<'a> {\n    /// A separator character: `:;,`, and `.` when not part of a numeric\n    /// literal.\n    Separator(char),\n\n    /// A parenthesis-like character: `()[]{}`, and also `<>`.\n    ///\n    /// Note that `<>` representing template argument brackets are distinguished\n    /// using WGSL's [template list discovery algorithm][tlda], and are returned\n    /// as [`Token::TemplateArgsStart`] and [`Token::TemplateArgsEnd`]. That is,\n    /// we use `Paren` for `<>` when they are *not* parens.\n    ///\n    /// [tlda]: https://gpuweb.github.io/gpuweb/wgsl/#template-list-discovery\n    Paren(char),\n\n    /// The attribute introduction character `@`.\n    Attribute,\n\n    /// A numeric literal, either integral or floating-point, including any\n    /// type suffix.\n    Number(core::result::Result<Number, NumberError>),\n\n    /// An identifier, possibly a reserved word.\n    Word(&'a str),\n\n    /// A miscellaneous single-character operator, like an arithmetic unary or\n    /// binary operator. This includes `=`, for assignment and initialization.\n    Operation(char),\n\n    /// Certain multi-character logical operators: `!=`, `==`, `&&`,\n    /// `||`, `<=` and `>=`. The value gives the operator's first\n    /// character.\n    ///\n    /// For `<` and `>` operators, see [`Token::Paren`].\n    LogicalOperation(char),\n\n    /// A shift operator: `>>` or `<<`.\n    ShiftOperation(char),\n\n    /// A compound assignment operator like `+=`.\n    ///\n    /// When the given character is `<` or `>`, those represent the left shift\n    /// and right shift assignment operators, `<<=` and `>>=`.\n    AssignmentOperation(char),\n\n    /// The `++` operator.\n    IncrementOperation,\n\n    /// The `--` operator.\n    DecrementOperation,\n\n    /// The `->` token.\n    Arrow,\n\n    /// A `<` representing the start of a template argument list, according to\n    /// WGSL's [template list discovery algorithm][tlda].\n    ///\n    /// [tlda]: https://gpuweb.github.io/gpuweb/wgsl/#template-list-discovery\n    TemplateArgsStart,\n\n    /// A `>` representing the end of a template argument list, according to\n    /// WGSL's [template list discovery algorithm][tlda].\n    ///\n    /// [tlda]: https://gpuweb.github.io/gpuweb/wgsl/#template-list-discovery\n    TemplateArgsEnd,\n\n    /// A character that does not represent a legal WGSL token.\n    Unknown(char),\n\n    /// Comment or whitespace.\n    Trivia,\n\n    /// A doc comment, beginning with `///` or `/**`.\n    DocComment(&'a str),\n\n    /// A module-level doc comment, beginning with `//!` or `/*!`.\n    ModuleDocComment(&'a str),\n\n    /// The end of the input.\n    End,\n}\n\nfn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str) {\n    let pos = input.find(|c| !what(c)).unwrap_or(input.len());\n    input.split_at(pos)\n}\n\nstruct UnclosedCandidate {\n    index: usize,\n    depth: usize,\n}\n\n/// Produce at least one token, distinguishing [template lists] from other uses\n/// of `<` and `>`.\n///\n/// Consume one or more tokens from `input` and store them in `tokens`, updating\n/// `input` to refer to the remaining text. Apply WGSL's [template list\n/// discovery algorithm] to decide what sort of tokens `<` and `>` characters in\n/// the input actually represent.\n///\n/// Store the tokens in `tokens` in the *reverse* of the order they appear in\n/// the text, such that the caller can pop from the end of the vector to see the\n/// tokens in textual order.\n///\n/// The `tokens` vector must be empty on entry. The idea is for the caller to\n/// use it as a buffer of unconsumed tokens, and call this function to refill it\n/// when it's empty.\n///\n/// The `source` argument must be the whole original source code, used to\n/// compute spans.\n///\n/// If `ignore_doc_comments` is true, then doc comments are returned as\n/// [`Token::Trivia`], like ordinary comments.\n///\n/// [template lists]: https://gpuweb.github.io/gpuweb/wgsl/#template-lists-sec\n/// [template list discovery algorithm]: https://gpuweb.github.io/gpuweb/wgsl/#template-list-discovery\nfn discover_template_lists<'a>(\n    tokens: &mut Vec<(TokenSpan<'a>, &'a str)>,\n    source: &'a str,\n    mut input: &'a str,\n    ignore_doc_comments: bool,\n) {\n    assert!(tokens.is_empty());\n\n    let mut looking_for_template_start = false;\n    let mut pending: Vec<UnclosedCandidate> = Vec::new();\n\n    // Current nesting depth of `()` and `[]` brackets. (`{}` brackets\n    // exit all template list processing.)\n    let mut depth = 0;\n\n    fn pop_until(pending: &mut Vec<UnclosedCandidate>, depth: usize) {\n        while pending\n            .last()\n            .map(|candidate| candidate.depth >= depth)\n            .unwrap_or(false)\n        {\n            pending.pop();\n        }\n    }\n\n    loop {\n        // Decide whether `consume_token` should treat a `>` character as\n        // `TemplateArgsEnd`, without considering the characters that follow.\n        //\n        // This condition matches the one that determines whether the spec's\n        // template list discovery algorithm looks past a `>` character for a\n        // `=`. By passing this flag to `consume_token`, we ensure it follows\n        // that behavior.\n        let waiting_for_template_end = pending\n            .last()\n            .is_some_and(|candidate| candidate.depth == depth);\n\n        // Ask `consume_token` for the next token and add it to `tokens`, along\n        // with its span.\n        //\n        // This means that `<` enters the buffer as `Token::Paren('<')`, the\n        // ordinary comparison operator. We'll change that to\n        // `Token::TemplateArgsStart` later if appropriate.\n        let (token, rest) = consume_token(input, waiting_for_template_end, ignore_doc_comments);\n        let span = Span::from(source.len() - input.len()..source.len() - rest.len());\n        tokens.push(((token, span), rest));\n        input = rest;\n\n        // Since `consume_token` treats `<<=`, `<<` and `<=` as operators, not\n        // `Token::Paren`, that takes care of the WGSL algorithm's post-'<' lookahead\n        // for us.\n        match token {\n            Token::Word(_) => {\n                looking_for_template_start = true;\n                continue;\n            }\n            Token::Trivia | Token::DocComment(_) | Token::ModuleDocComment(_)\n                if looking_for_template_start =>\n            {\n                continue;\n            }\n            Token::Paren('<') if looking_for_template_start => {\n                pending.push(UnclosedCandidate {\n                    index: tokens.len() - 1,\n                    depth,\n                });\n            }\n            Token::TemplateArgsEnd => {\n                // The `consume_token` function only returns `TemplateArgsEnd`\n                // if `waiting_for_template_end` is true, so we know `pending`\n                // has a top entry at the appropriate depth.\n                //\n                // Find the matching `<` token and change its type to\n                // `TemplateArgsStart`.\n                let candidate = pending.pop().unwrap();\n                let &mut ((ref mut token, _), _) = tokens.get_mut(candidate.index).unwrap();\n                *token = Token::TemplateArgsStart;\n            }\n            Token::Paren('(' | '[') => {\n                depth += 1;\n            }\n            Token::Paren(')' | ']') => {\n                pop_until(&mut pending, depth);\n                depth = depth.saturating_sub(1);\n            }\n            Token::Operation('=') | Token::Separator(':' | ';') | Token::Paren('{') => {\n                pending.clear();\n                depth = 0;\n            }\n            Token::LogicalOperation('&') | Token::LogicalOperation('|') => {\n                pop_until(&mut pending, depth);\n            }\n            Token::End => break,\n            _ => {}\n        }\n\n        looking_for_template_start = false;\n\n        // The WGSL spec's template list discovery algorithm processes the\n        // entire source at once, but Naga would rather limit its lookahead to\n        // the actual text that could possibly be a template parameter list.\n        // This is usually less than a line.\n        if pending.is_empty() {\n            break;\n        }\n    }\n\n    tokens.reverse();\n}\n\n/// Return the token at the start of `input`.\n///\n/// The `waiting_for_template_end` flag enables some special handling to help out\n/// `discover_template_lists`:\n///\n/// - If `waiting_for_template_end` is `true`, then return text starting with\n///   '>` as [`Token::TemplateArgsEnd`] and consume only the `>` character,\n///   regardless of what characters follow it. This is required by the [template\n///   list discovery algorithm][tlda] when the `>` would end a template argument list.\n///\n/// - If `waiting_for_template_end` is false, recognize multi-character tokens\n///   beginning with `>` as usual.\n///\n/// If `ignore_doc_comments` is true, then doc comments are returned as\n/// [`Token::Trivia`], like ordinary comments.\n///\n/// [tlda]: https://gpuweb.github.io/gpuweb/wgsl/#template-list-discovery\nfn consume_token(\n    input: &str,\n    waiting_for_template_end: bool,\n    ignore_doc_comments: bool,\n) -> (Token<'_>, &str) {\n    let mut chars = input.chars();\n    let cur = match chars.next() {\n        Some(c) => c,\n        None => return (Token::End, \"\"),\n    };\n    match cur {\n        ':' | ';' | ',' => (Token::Separator(cur), chars.as_str()),\n        '.' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('0'..='9') => consume_number(input),\n                _ => (Token::Separator(cur), og_chars),\n            }\n        }\n        '@' => (Token::Attribute, chars.as_str()),\n        '(' | ')' | '{' | '}' | '[' | ']' => (Token::Paren(cur), chars.as_str()),\n        '<' | '>' => {\n            let og_chars = chars.as_str();\n            if cur == '>' && waiting_for_template_end {\n                return (Token::TemplateArgsEnd, og_chars);\n            }\n            match chars.next() {\n                Some('=') => (Token::LogicalOperation(cur), chars.as_str()),\n                Some(c) if c == cur => {\n                    let og_chars = chars.as_str();\n                    match chars.next() {\n                        Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                        _ => (Token::ShiftOperation(cur), og_chars),\n                    }\n                }\n                _ => (Token::Paren(cur), og_chars),\n            }\n        }\n        '0'..='9' => consume_number(input),\n        '/' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('/') => {\n                    let mut input_chars = input.char_indices();\n                    let doc_comment_end = input_chars\n                        .find_map(|(index, c)| is_comment_end(c).then_some(index))\n                        .unwrap_or(input.len());\n                    let token = match chars.next() {\n                        Some('/') if !ignore_doc_comments => {\n                            Token::DocComment(&input[..doc_comment_end])\n                        }\n                        Some('!') if !ignore_doc_comments => {\n                            Token::ModuleDocComment(&input[..doc_comment_end])\n                        }\n                        _ => Token::Trivia,\n                    };\n                    (token, input_chars.as_str())\n                }\n                Some('*') => {\n                    let next_c = chars.next();\n\n                    enum CommentType {\n                        Doc,\n                        ModuleDoc,\n                        Normal,\n                    }\n                    let comment_type = match next_c {\n                        Some('*') if !ignore_doc_comments => CommentType::Doc,\n                        Some('!') if !ignore_doc_comments => CommentType::ModuleDoc,\n                        _ => CommentType::Normal,\n                    };\n\n                    let mut depth = 1;\n                    let mut prev = next_c;\n\n                    for c in &mut chars {\n                        match (prev, c) {\n                            (Some('*'), '/') => {\n                                prev = None;\n                                depth -= 1;\n                                if depth == 0 {\n                                    let rest = chars.as_str();\n                                    let token = match comment_type {\n                                        CommentType::Doc => {\n                                            let doc_comment_end = input.len() - rest.len();\n                                            Token::DocComment(&input[..doc_comment_end])\n                                        }\n                                        CommentType::ModuleDoc => {\n                                            let doc_comment_end = input.len() - rest.len();\n                                            Token::ModuleDocComment(&input[..doc_comment_end])\n                                        }\n                                        CommentType::Normal => Token::Trivia,\n                                    };\n                                    return (token, rest);\n                                }\n                            }\n                            (Some('/'), '*') => {\n                                prev = None;\n                                depth += 1;\n                            }\n                            _ => {\n                                prev = Some(c);\n                            }\n                        }\n                    }\n\n                    (Token::End, \"\")\n                }\n                Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        '-' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('>') => (Token::Arrow, chars.as_str()),\n                Some('-') => (Token::DecrementOperation, chars.as_str()),\n                Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        '+' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('+') => (Token::IncrementOperation, chars.as_str()),\n                Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        '*' | '%' | '^' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        '~' => (Token::Operation(cur), chars.as_str()),\n        '=' | '!' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some('=') => (Token::LogicalOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        '&' | '|' => {\n            let og_chars = chars.as_str();\n            match chars.next() {\n                Some(c) if c == cur => (Token::LogicalOperation(cur), chars.as_str()),\n                Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),\n                _ => (Token::Operation(cur), og_chars),\n            }\n        }\n        _ if is_blankspace(cur) => {\n            let (_, rest) = consume_any(input, is_blankspace);\n            (Token::Trivia, rest)\n        }\n        _ if is_word_start(cur) => {\n            let (word, rest) = consume_any(input, is_word_part);\n            (Token::Word(word), rest)\n        }\n        _ => (Token::Unknown(cur), chars.as_str()),\n    }\n}\n\n/// Returns whether or not a char is a comment end\n/// (Unicode Pattern_White_Space excluding U+0020, U+0009, U+200E and U+200F)\n/// <https://www.w3.org/TR/WGSL/#line-break>\nconst fn is_comment_end(c: char) -> bool {\n    match c {\n        '\\u{000a}'..='\\u{000d}' | '\\u{0085}' | '\\u{2028}' | '\\u{2029}' => true,\n        _ => false,\n    }\n}\n\n/// Returns whether or not a char is a blankspace (Unicode Pattern_White_Space)\nconst fn is_blankspace(c: char) -> bool {\n    match c {\n        '\\u{0020}'\n        | '\\u{0009}'..='\\u{000d}'\n        | '\\u{0085}'\n        | '\\u{200e}'\n        | '\\u{200f}'\n        | '\\u{2028}'\n        | '\\u{2029}' => true,\n        _ => false,\n    }\n}\n\n/// Returns whether or not a char is a word start (Unicode XID_Start + '_')\nfn is_word_start(c: char) -> bool {\n    c == '_' || unicode_ident::is_xid_start(c)\n}\n\n/// Returns whether or not a char is a word part (Unicode XID_Continue)\nfn is_word_part(c: char) -> bool {\n    unicode_ident::is_xid_continue(c)\n}\n\npub(in crate::front::wgsl) struct Lexer<'a> {\n    /// The remaining unconsumed input.\n    input: &'a str,\n\n    /// The full original source code.\n    ///\n    /// We compare `input` against this to compute the lexer's current offset in\n    /// the source.\n    pub(in crate::front::wgsl) source: &'a str,\n\n    /// The byte offset of the end of the most recently returned non-trivia\n    /// token.\n    ///\n    /// This is consulted by the `span_from` function, for finding the\n    /// end of the span for larger structures like expressions or\n    /// statements.\n    last_end_offset: usize,\n\n    /// A stack of unconsumed tokens to which template list discovery has been\n    /// applied.\n    ///\n    /// This is a stack: the next token is at the *end* of the vector, not the\n    /// start. So tokens appear here in the reverse of the order they appear in\n    /// the source.\n    ///\n    /// This doesn't contain the whole source, only those tokens produced by\n    /// [`discover_template_lists`]'s look-ahead, or that have been produced by\n    /// other look-ahead functions like `peek` and `next_if`. When this is empty,\n    /// we call [`discover_template_lists`] to get more.\n    tokens: Vec<(TokenSpan<'a>, &'a str)>,\n\n    /// Whether or not to ignore doc comments.\n    /// If `true`, doc comments are treated as [`Token::Trivia`].\n    ignore_doc_comments: bool,\n\n    /// The set of [enable-extensions] present in the module, determined in a pre-pass.\n    ///\n    /// [enable-extensions]: https://gpuweb.github.io/gpuweb/wgsl/#enable-extensions-sec\n    pub(in crate::front::wgsl) enable_extensions: EnableExtensions,\n}\n\nimpl<'a> Lexer<'a> {\n    pub(in crate::front::wgsl) const fn new(input: &'a str, ignore_doc_comments: bool) -> Self {\n        Lexer {\n            input,\n            source: input,\n            last_end_offset: 0,\n            tokens: Vec::new(),\n            enable_extensions: EnableExtensions::empty(),\n            ignore_doc_comments,\n        }\n    }\n\n    /// Check that `extension` is enabled in `self`.\n    pub(in crate::front::wgsl) fn require_enable_extension(\n        &self,\n        extension: ImplementedEnableExtension,\n        span: Span,\n    ) -> Result<'static, ()> {\n        self.enable_extensions.require(extension, span)\n    }\n\n    /// Calls the function with a lexer and returns the result of the function as well as the span for everything the function parsed\n    ///\n    /// # Examples\n    /// ```ignore\n    /// let lexer = Lexer::new(\"5\");\n    /// let (value, span) = lexer.capture_span(Lexer::next_uint_literal);\n    /// assert_eq!(value, 5);\n    /// ```\n    #[inline]\n    pub fn capture_span<T, E>(\n        &mut self,\n        inner: impl FnOnce(&mut Self) -> core::result::Result<T, E>,\n    ) -> core::result::Result<(T, Span), E> {\n        let start = self.current_byte_offset();\n        let res = inner(self)?;\n        let end = self.current_byte_offset();\n        Ok((res, Span::from(start..end)))\n    }\n\n    pub(in crate::front::wgsl) fn start_byte_offset(&mut self) -> usize {\n        loop {\n            // Eat all trivia because `next` doesn't eat trailing trivia.\n            let (token, rest) = consume_token(self.input, false, true);\n            if let Token::Trivia = token {\n                self.input = rest;\n            } else {\n                return self.current_byte_offset();\n            }\n        }\n    }\n\n    /// Collect all module doc comments until a non doc token is found.\n    pub(in crate::front::wgsl) fn accumulate_module_doc_comments(&mut self) -> Vec<&'a str> {\n        let mut doc_comments = Vec::new();\n        loop {\n            // ignore blankspace\n            self.input = consume_any(self.input, is_blankspace).1;\n\n            let (token, rest) = consume_token(self.input, false, self.ignore_doc_comments);\n            if let Token::ModuleDocComment(doc_comment) = token {\n                self.input = rest;\n                doc_comments.push(doc_comment);\n            } else {\n                return doc_comments;\n            }\n        }\n    }\n\n    /// Collect all doc comments until a non doc token is found.\n    pub(in crate::front::wgsl) fn accumulate_doc_comments(&mut self) -> Vec<&'a str> {\n        let mut doc_comments = Vec::new();\n        loop {\n            // ignore blankspace\n            self.input = consume_any(self.input, is_blankspace).1;\n\n            let (token, rest) = consume_token(self.input, false, self.ignore_doc_comments);\n            if let Token::DocComment(doc_comment) = token {\n                self.input = rest;\n                doc_comments.push(doc_comment);\n            } else {\n                return doc_comments;\n            }\n        }\n    }\n\n    const fn current_byte_offset(&self) -> usize {\n        self.source.len() - self.input.len()\n    }\n\n    pub(in crate::front::wgsl) fn span_from(&self, offset: usize) -> Span {\n        Span::from(offset..self.last_end_offset)\n    }\n    pub(in crate::front::wgsl) fn span_with_start(&self, span: Span) -> Span {\n        span.until(&Span::from(0..self.last_end_offset))\n    }\n\n    /// Return the next non-whitespace token from `self`.\n    ///\n    /// Assume we are a parse state where bit shift operators may\n    /// occur, but not angle brackets.\n    #[must_use]\n    pub(in crate::front::wgsl) fn next(&mut self) -> TokenSpan<'a> {\n        self.next_impl(true)\n    }\n\n    #[cfg(test)]\n    pub fn next_with_unignored_doc_comments(&mut self) -> TokenSpan<'a> {\n        self.next_impl(false)\n    }\n\n    /// Return the next non-whitespace token from `self`, with a span.\n    fn next_impl(&mut self, ignore_doc_comments: bool) -> TokenSpan<'a> {\n        loop {\n            if self.tokens.is_empty() {\n                discover_template_lists(\n                    &mut self.tokens,\n                    self.source,\n                    self.input,\n                    ignore_doc_comments || self.ignore_doc_comments,\n                );\n            }\n            assert!(!self.tokens.is_empty());\n            let (token, rest) = self.tokens.pop().unwrap();\n\n            self.input = rest;\n            self.last_end_offset = self.current_byte_offset();\n\n            match token.0 {\n                Token::Trivia => {}\n                _ => return token,\n            }\n        }\n    }\n\n    #[must_use]\n    pub(in crate::front::wgsl) fn peek(&mut self) -> TokenSpan<'a> {\n        let input = self.input;\n        let last_end_offset = self.last_end_offset;\n        let token = self.next();\n        self.tokens.push((token, self.input));\n        self.input = input;\n        self.last_end_offset = last_end_offset;\n        token\n    }\n\n    /// If the next token matches it's consumed and true is returned\n    pub(in crate::front::wgsl) fn next_if(&mut self, what: Token<'_>) -> bool {\n        let input = self.input;\n        let last_end_offset = self.last_end_offset;\n        let token = self.next();\n        if token.0 == what {\n            true\n        } else {\n            self.tokens.push((token, self.input));\n            self.input = input;\n            self.last_end_offset = last_end_offset;\n            false\n        }\n    }\n\n    pub(in crate::front::wgsl) fn expect_span(&mut self, expected: Token<'a>) -> Result<'a, Span> {\n        let next = self.next();\n        if next.0 == expected {\n            Ok(next.1)\n        } else {\n            Err(Box::new(Error::Unexpected(\n                next.1,\n                ExpectedToken::Token(expected),\n            )))\n        }\n    }\n\n    pub(in crate::front::wgsl) fn expect(&mut self, expected: Token<'a>) -> Result<'a, ()> {\n        self.expect_span(expected)?;\n        Ok(())\n    }\n\n    pub(in crate::front::wgsl) fn next_ident_with_span(&mut self) -> Result<'a, (&'a str, Span)> {\n        match self.next() {\n            (Token::Word(\"_\"), span) => Err(Box::new(Error::InvalidIdentifierUnderscore(span))),\n            (Token::Word(word), span) => {\n                if word.starts_with(\"__\") {\n                    Err(Box::new(Error::ReservedIdentifierPrefix(span)))\n                } else {\n                    Ok((word, span))\n                }\n            }\n            (_, span) => Err(Box::new(Error::Unexpected(span, ExpectedToken::Identifier))),\n        }\n    }\n\n    pub(in crate::front::wgsl) fn next_ident(&mut self) -> Result<'a, super::ast::Ident<'a>> {\n        self.next_ident_with_span()\n            .and_then(|(word, span)| Self::word_as_ident(word, span))\n            .map(|(name, span)| super::ast::Ident { name, span })\n    }\n\n    fn word_as_ident(word: &'a str, span: Span) -> Result<'a, (&'a str, Span)> {\n        if crate::keywords::wgsl::RESERVED.contains(&word) {\n            Err(Box::new(Error::ReservedKeyword(span)))\n        } else {\n            Ok((word, span))\n        }\n    }\n\n    pub(in crate::front::wgsl) fn open_arguments(&mut self) -> Result<'a, ()> {\n        self.expect(Token::Paren('('))\n    }\n\n    pub(in crate::front::wgsl) fn next_argument(&mut self) -> Result<'a, bool> {\n        let paren = Token::Paren(')');\n        if self.next_if(Token::Separator(',')) {\n            Ok(!self.next_if(paren))\n        } else {\n            self.expect(paren).map(|()| false)\n        }\n    }\n}\n\n#[cfg(test)]\n#[track_caller]\nfn sub_test(source: &str, expected_tokens: &[Token]) {\n    sub_test_with(true, source, expected_tokens);\n}\n\n#[cfg(test)]\n#[track_caller]\nfn sub_test_with_and_without_doc_comments(source: &str, expected_tokens: &[Token]) {\n    sub_test_with(false, source, expected_tokens);\n    sub_test_with(\n        true,\n        source,\n        expected_tokens\n            .iter()\n            .filter(|v| !matches!(**v, Token::DocComment(_) | Token::ModuleDocComment(_)))\n            .cloned()\n            .collect::<Vec<_>>()\n            .as_slice(),\n    );\n}\n\n#[cfg(test)]\n#[track_caller]\nfn sub_test_with(ignore_doc_comments: bool, source: &str, expected_tokens: &[Token]) {\n    let mut lex = Lexer::new(source, ignore_doc_comments);\n    for &token in expected_tokens {\n        assert_eq!(lex.next_with_unignored_doc_comments().0, token);\n    }\n    assert_eq!(lex.next().0, Token::End);\n}\n\n#[test]\nfn test_numbers() {\n    use half::f16;\n    // WGSL spec examples //\n\n    // decimal integer\n    sub_test(\n        \"0x123 0X123u 1u 123 0 0i 0x3f\",\n        &[\n            Token::Number(Ok(Number::AbstractInt(291))),\n            Token::Number(Ok(Number::U32(291))),\n            Token::Number(Ok(Number::U32(1))),\n            Token::Number(Ok(Number::AbstractInt(123))),\n            Token::Number(Ok(Number::AbstractInt(0))),\n            Token::Number(Ok(Number::I32(0))),\n            Token::Number(Ok(Number::AbstractInt(63))),\n        ],\n    );\n    // decimal floating point\n    sub_test(\n        \"0.e+4f 01. .01 12.34 .0f 0h 1e-3 0xa.fp+2 0x1P+4f 0X.3 0x3p+2h 0X1.fp-4 0x3.2p+2h\",\n        &[\n            Token::Number(Ok(Number::F32(0.))),\n            Token::Number(Ok(Number::AbstractFloat(1.))),\n            Token::Number(Ok(Number::AbstractFloat(0.01))),\n            Token::Number(Ok(Number::AbstractFloat(12.34))),\n            Token::Number(Ok(Number::F32(0.))),\n            Token::Number(Ok(Number::F16(f16::from_f32(0.)))),\n            Token::Number(Ok(Number::AbstractFloat(0.001))),\n            Token::Number(Ok(Number::AbstractFloat(43.75))),\n            Token::Number(Ok(Number::F32(16.))),\n            Token::Number(Ok(Number::AbstractFloat(0.1875))),\n            // https://github.com/gfx-rs/wgpu/issues/7046\n            Token::Number(Err(NumberError::NotRepresentable)), // Should be 0.75\n            Token::Number(Ok(Number::AbstractFloat(0.12109375))),\n            // https://github.com/gfx-rs/wgpu/issues/7046\n            Token::Number(Err(NumberError::NotRepresentable)), // Should be 12.5\n        ],\n    );\n\n    // MIN / MAX //\n\n    // min / max decimal integer\n    sub_test(\n        \"0i 2147483647i 2147483648i\",\n        &[\n            Token::Number(Ok(Number::I32(0))),\n            Token::Number(Ok(Number::I32(i32::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n    // min / max decimal unsigned integer\n    sub_test(\n        \"0u 4294967295u 4294967296u\",\n        &[\n            Token::Number(Ok(Number::U32(u32::MIN))),\n            Token::Number(Ok(Number::U32(u32::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n\n    // min / max hexadecimal signed integer\n    sub_test(\n        \"0x0i 0x7FFFFFFFi 0x80000000i\",\n        &[\n            Token::Number(Ok(Number::I32(0))),\n            Token::Number(Ok(Number::I32(i32::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n    // min / max hexadecimal unsigned integer\n    sub_test(\n        \"0x0u 0xFFFFFFFFu 0x100000000u\",\n        &[\n            Token::Number(Ok(Number::U32(u32::MIN))),\n            Token::Number(Ok(Number::U32(u32::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n\n    // min/max decimal abstract int\n    sub_test(\n        \"0 9223372036854775807 9223372036854775808\",\n        &[\n            Token::Number(Ok(Number::AbstractInt(0))),\n            Token::Number(Ok(Number::AbstractInt(i64::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n\n    // min/max hexadecimal abstract int\n    sub_test(\n        \"0 0x7fffffffffffffff 0x8000000000000000\",\n        &[\n            Token::Number(Ok(Number::AbstractInt(0))),\n            Token::Number(Ok(Number::AbstractInt(i64::MAX))),\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n\n    /// ≈ 2^-126 * 2^−23 (= 2^−149)\n    const SMALLEST_POSITIVE_SUBNORMAL_F32: f32 = 1e-45;\n    /// ≈ 2^-126 * (1 − 2^−23)\n    const LARGEST_SUBNORMAL_F32: f32 = 1.1754942e-38;\n    /// ≈ 2^-126\n    const SMALLEST_POSITIVE_NORMAL_F32: f32 = f32::MIN_POSITIVE;\n    /// ≈ 1 − 2^−24\n    const LARGEST_F32_LESS_THAN_ONE: f32 = 0.99999994;\n    /// ≈ 1 + 2^−23\n    const SMALLEST_F32_LARGER_THAN_ONE: f32 = 1.0000001;\n    /// ≈ 2^127 * (2 − 2^−23)\n    const LARGEST_NORMAL_F32: f32 = f32::MAX;\n\n    // decimal floating point\n    sub_test(\n        \"1e-45f 1.1754942e-38f 1.17549435e-38f 0.99999994f 1.0000001f 3.40282347e+38f\",\n        &[\n            Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_SUBNORMAL_F32))),\n            Token::Number(Ok(Number::F32(LARGEST_SUBNORMAL_F32))),\n            Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_NORMAL_F32))),\n            Token::Number(Ok(Number::F32(LARGEST_F32_LESS_THAN_ONE))),\n            Token::Number(Ok(Number::F32(SMALLEST_F32_LARGER_THAN_ONE))),\n            Token::Number(Ok(Number::F32(LARGEST_NORMAL_F32))),\n        ],\n    );\n    sub_test(\n        \"3.40282367e+38f\",\n        &[\n            Token::Number(Err(NumberError::NotRepresentable)), // ≈ 2^128\n        ],\n    );\n\n    // hexadecimal floating point\n    sub_test(\n        \"0x1p-149f 0x7FFFFFp-149f 0x1p-126f 0xFFFFFFp-24f 0x800001p-23f 0xFFFFFFp+104f\",\n        &[\n            Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_SUBNORMAL_F32))),\n            Token::Number(Ok(Number::F32(LARGEST_SUBNORMAL_F32))),\n            Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_NORMAL_F32))),\n            Token::Number(Ok(Number::F32(LARGEST_F32_LESS_THAN_ONE))),\n            Token::Number(Ok(Number::F32(SMALLEST_F32_LARGER_THAN_ONE))),\n            Token::Number(Ok(Number::F32(LARGEST_NORMAL_F32))),\n        ],\n    );\n    sub_test(\n        \"0x1p128f 0x1.000001p0f\",\n        &[\n            Token::Number(Err(NumberError::NotRepresentable)), // = 2^128\n            Token::Number(Err(NumberError::NotRepresentable)),\n        ],\n    );\n}\n\n#[test]\nfn double_floats() {\n    sub_test(\n        \"0x1.2p4lf 0x1p8lf 0.0625lf 625e-4lf 10lf 10l\",\n        &[\n            Token::Number(Ok(Number::F64(18.0))),\n            Token::Number(Ok(Number::F64(256.0))),\n            Token::Number(Ok(Number::F64(0.0625))),\n            Token::Number(Ok(Number::F64(0.0625))),\n            Token::Number(Ok(Number::F64(10.0))),\n            Token::Number(Ok(Number::AbstractInt(10))),\n            Token::Word(\"l\"),\n        ],\n    )\n}\n\n#[test]\nfn test_tokens() {\n    sub_test(\"id123_OK\", &[Token::Word(\"id123_OK\")]);\n    sub_test(\n        \"92No\",\n        &[\n            Token::Number(Ok(Number::AbstractInt(92))),\n            Token::Word(\"No\"),\n        ],\n    );\n    sub_test(\n        \"2u3o\",\n        &[\n            Token::Number(Ok(Number::U32(2))),\n            Token::Number(Ok(Number::AbstractInt(3))),\n            Token::Word(\"o\"),\n        ],\n    );\n    sub_test(\n        \"2.4f44po\",\n        &[\n            Token::Number(Ok(Number::F32(2.4))),\n            Token::Number(Ok(Number::AbstractInt(44))),\n            Token::Word(\"po\"),\n        ],\n    );\n    sub_test(\n        \"Δέλτα réflexion Кызыл 𐰓𐰏𐰇 朝焼け سلام 검정 שָׁלוֹם गुलाबी փիրուզ\",\n        &[\n            Token::Word(\"Δέλτα\"),\n            Token::Word(\"réflexion\"),\n            Token::Word(\"Кызыл\"),\n            Token::Word(\"𐰓𐰏𐰇\"),\n            Token::Word(\"朝焼け\"),\n            Token::Word(\"سلام\"),\n            Token::Word(\"검정\"),\n            Token::Word(\"שָׁלוֹם\"),\n            Token::Word(\"गुलाबी\"),\n            Token::Word(\"փիրուզ\"),\n        ],\n    );\n    sub_test(\"æNoø\", &[Token::Word(\"æNoø\")]);\n    sub_test(\"No¾\", &[Token::Word(\"No\"), Token::Unknown('¾')]);\n    sub_test(\"No好\", &[Token::Word(\"No好\")]);\n    sub_test(\"_No\", &[Token::Word(\"_No\")]);\n\n    sub_test_with_and_without_doc_comments(\n        \"*/*/***/*//=/*****//\",\n        &[\n            Token::Operation('*'),\n            Token::AssignmentOperation('/'),\n            Token::DocComment(\"/*****/\"),\n            Token::Operation('/'),\n        ],\n    );\n\n    // Type suffixes are only allowed on hex float literals\n    // if you provided an exponent.\n    sub_test(\n        \"0x1.2f 0x1.2f 0x1.2h 0x1.2H 0x1.2lf\",\n        &[\n            // The 'f' suffixes are taken as a hex digit:\n            // the fractional part is 0x2f / 256.\n            Token::Number(Ok(Number::AbstractFloat(1.0 + 0x2f as f64 / 256.0))),\n            Token::Number(Ok(Number::AbstractFloat(1.0 + 0x2f as f64 / 256.0))),\n            Token::Number(Ok(Number::AbstractFloat(1.125))),\n            Token::Word(\"h\"),\n            Token::Number(Ok(Number::AbstractFloat(1.125))),\n            Token::Word(\"H\"),\n            Token::Number(Ok(Number::AbstractFloat(1.125))),\n            Token::Word(\"lf\"),\n        ],\n    )\n}\n\n#[test]\nfn test_variable_decl() {\n    sub_test(\n        \"@group(0 ) var< uniform> texture:   texture_multisampled_2d <f32 >;\",\n        &[\n            Token::Attribute,\n            Token::Word(\"group\"),\n            Token::Paren('('),\n            Token::Number(Ok(Number::AbstractInt(0))),\n            Token::Paren(')'),\n            Token::Word(\"var\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"uniform\"),\n            Token::TemplateArgsEnd,\n            Token::Word(\"texture\"),\n            Token::Separator(':'),\n            Token::Word(\"texture_multisampled_2d\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"f32\"),\n            Token::TemplateArgsEnd,\n            Token::Separator(';'),\n        ],\n    );\n    sub_test(\n        \"var<storage,read_write> buffer: array<u32>;\",\n        &[\n            Token::Word(\"var\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"storage\"),\n            Token::Separator(','),\n            Token::Word(\"read_write\"),\n            Token::TemplateArgsEnd,\n            Token::Word(\"buffer\"),\n            Token::Separator(':'),\n            Token::Word(\"array\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"u32\"),\n            Token::TemplateArgsEnd,\n            Token::Separator(';'),\n        ],\n    );\n}\n\n#[test]\nfn test_template_list() {\n    sub_test(\n        \"A<B||C>D\",\n        &[\n            Token::Word(\"A\"),\n            Token::Paren('<'),\n            Token::Word(\"B\"),\n            Token::LogicalOperation('|'),\n            Token::Word(\"C\"),\n            Token::Paren('>'),\n            Token::Word(\"D\"),\n        ],\n    );\n    sub_test(\n        \"A(B<C,D>(E))\",\n        &[\n            Token::Word(\"A\"),\n            Token::Paren('('),\n            Token::Word(\"B\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"C\"),\n            Token::Separator(','),\n            Token::Word(\"D\"),\n            Token::TemplateArgsEnd,\n            Token::Paren('('),\n            Token::Word(\"E\"),\n            Token::Paren(')'),\n            Token::Paren(')'),\n        ],\n    );\n    sub_test(\n        \"array<i32,select(2,3,A>B)>\",\n        &[\n            Token::Word(\"array\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"i32\"),\n            Token::Separator(','),\n            Token::Word(\"select\"),\n            Token::Paren('('),\n            Token::Number(Ok(Number::AbstractInt(2))),\n            Token::Separator(','),\n            Token::Number(Ok(Number::AbstractInt(3))),\n            Token::Separator(','),\n            Token::Word(\"A\"),\n            Token::Paren('>'),\n            Token::Word(\"B\"),\n            Token::Paren(')'),\n            Token::TemplateArgsEnd,\n        ],\n    );\n    sub_test(\n        \"A[B<C]>D\",\n        &[\n            Token::Word(\"A\"),\n            Token::Paren('['),\n            Token::Word(\"B\"),\n            Token::Paren('<'),\n            Token::Word(\"C\"),\n            Token::Paren(']'),\n            Token::Paren('>'),\n            Token::Word(\"D\"),\n        ],\n    );\n    sub_test(\n        \"A<B<<C>\",\n        &[\n            Token::Word(\"A\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"B\"),\n            Token::ShiftOperation('<'),\n            Token::Word(\"C\"),\n            Token::TemplateArgsEnd,\n        ],\n    );\n    sub_test(\n        \"A<(B>=C)>\",\n        &[\n            Token::Word(\"A\"),\n            Token::TemplateArgsStart,\n            Token::Paren('('),\n            Token::Word(\"B\"),\n            Token::LogicalOperation('>'),\n            Token::Word(\"C\"),\n            Token::Paren(')'),\n            Token::TemplateArgsEnd,\n        ],\n    );\n    sub_test(\n        \"A<B>=C>\",\n        &[\n            Token::Word(\"A\"),\n            Token::TemplateArgsStart,\n            Token::Word(\"B\"),\n            Token::TemplateArgsEnd,\n            Token::Operation('='),\n            Token::Word(\"C\"),\n            Token::Paren('>'),\n        ],\n    );\n}\n\n#[test]\nfn test_comments() {\n    sub_test(\"// Single comment\", &[]);\n\n    sub_test(\n        \"/* multi\n    line\n    comment */\",\n        &[],\n    );\n    sub_test(\n        \"/* multi\n    line\n    comment */\n    // and another\",\n        &[],\n    );\n}\n\n#[test]\nfn test_doc_comments() {\n    sub_test_with_and_without_doc_comments(\n        \"/// Single comment\",\n        &[Token::DocComment(\"/// Single comment\")],\n    );\n\n    sub_test_with_and_without_doc_comments(\n        \"/** multi\n    line\n    comment */\",\n        &[Token::DocComment(\n            \"/** multi\n    line\n    comment */\",\n        )],\n    );\n    sub_test_with_and_without_doc_comments(\n        \"/** multi\n    line\n    comment */\n    /// and another\",\n        &[\n            Token::DocComment(\n                \"/** multi\n    line\n    comment */\",\n            ),\n            Token::DocComment(\"/// and another\"),\n        ],\n    );\n}\n\n#[test]\nfn test_doc_comment_nested() {\n    sub_test_with_and_without_doc_comments(\n        \"/**\n    a comment with nested one /**\n        nested comment\n    */\n    */\n    const a : i32 = 2;\",\n        &[\n            Token::DocComment(\n                \"/**\n    a comment with nested one /**\n        nested comment\n    */\n    */\",\n            ),\n            Token::Word(\"const\"),\n            Token::Word(\"a\"),\n            Token::Separator(':'),\n            Token::Word(\"i32\"),\n            Token::Operation('='),\n            Token::Number(Ok(Number::AbstractInt(2))),\n            Token::Separator(';'),\n        ],\n    );\n}\n\n#[test]\nfn test_doc_comment_long_character() {\n    sub_test_with_and_without_doc_comments(\n        \"/// π/2\n        ///     D(𝐡) = ───────────────────────────────────────────────────\n///            παₜα_b((𝐡 ⋅ 𝐭)² / αₜ²) + (𝐡 ⋅ 𝐛)² / α_b² +`\n    const a : i32 = 2;\",\n        &[\n            Token::DocComment(\"/// π/2\"),\n            Token::DocComment(\"///     D(𝐡) = ───────────────────────────────────────────────────\"),\n            Token::DocComment(\"///            παₜα_b((𝐡 ⋅ 𝐭)² / αₜ²) + (𝐡 ⋅ 𝐛)² / α_b² +`\"),\n            Token::Word(\"const\"),\n            Token::Word(\"a\"),\n            Token::Separator(':'),\n            Token::Word(\"i32\"),\n            Token::Operation('='),\n            Token::Number(Ok(Number::AbstractInt(2))),\n            Token::Separator(';'),\n        ],\n    );\n}\n\n#[test]\nfn test_doc_comments_module() {\n    sub_test_with_and_without_doc_comments(\n        \"//! Comment Module\n        //! Another one.\n        /*! Different module comment */\n        /// Trying to break module comment\n        // Trying to break module comment again\n        //! After a regular comment is ok.\n        /*! Different module comment again */\n\n        //! After a break is supported.\n        const\n        //! After anything else is not.\",\n        &[\n            Token::ModuleDocComment(\"//! Comment Module\"),\n            Token::ModuleDocComment(\"//! Another one.\"),\n            Token::ModuleDocComment(\"/*! Different module comment */\"),\n            Token::DocComment(\"/// Trying to break module comment\"),\n            Token::ModuleDocComment(\"//! After a regular comment is ok.\"),\n            Token::ModuleDocComment(\"/*! Different module comment again */\"),\n            Token::ModuleDocComment(\"//! After a break is supported.\"),\n            Token::Word(\"const\"),\n            Token::ModuleDocComment(\"//! After anything else is not.\"),\n        ],\n    );\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/mod.rs",
    "content": "use alloc::{boxed::Box, vec::Vec};\nuse directive::enable_extension::ImplementedEnableExtension;\n\nuse crate::diagnostic_filter::{\n    self, DiagnosticFilter, DiagnosticFilterMap, DiagnosticFilterNode, FilterableTriggeringRule,\n    ShouldConflictOnFullDuplicate, StandardFilterableTriggeringRule,\n};\nuse crate::front::wgsl::error::{DiagnosticAttributeNotSupportedPosition, Error, ExpectedToken};\nuse crate::front::wgsl::parse::directive::enable_extension::{EnableExtension, EnableExtensions};\nuse crate::front::wgsl::parse::directive::language_extension::LanguageExtension;\nuse crate::front::wgsl::parse::directive::DirectiveKind;\nuse crate::front::wgsl::parse::lexer::{Lexer, Token, TokenSpan};\nuse crate::front::wgsl::parse::number::Number;\nuse crate::front::wgsl::Result;\nuse crate::front::SymbolTable;\nuse crate::{Arena, FastHashSet, FastIndexSet, Handle, ShaderStage, Span};\n\npub mod ast;\npub mod conv;\npub mod directive;\npub mod lexer;\npub mod number;\n\n/// State for constructing an AST expression.\n///\n/// Not to be confused with [`lower::ExpressionContext`], which is for producing\n/// Naga IR from the AST we produce here.\n///\n/// [`lower::ExpressionContext`]: super::lower::ExpressionContext\nstruct ExpressionContext<'input, 'temp, 'out> {\n    /// The [`TranslationUnit::expressions`] arena to which we should contribute\n    /// expressions.\n    ///\n    /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions\n    expressions: &'out mut Arena<ast::Expression<'input>>,\n\n    /// A map from identifiers in scope to the locals/arguments they represent.\n    ///\n    /// The handles refer to the [`locals`] arena; see that field's\n    /// documentation for details.\n    ///\n    /// [`locals`]: ExpressionContext::locals\n    local_table: &'temp mut SymbolTable<&'input str, Handle<ast::Local>>,\n\n    /// Local variable and function argument arena for the function we're building.\n    ///\n    /// Note that the [`ast::Local`] here is actually a zero-sized type. This\n    /// `Arena`'s only role is to assign a unique `Handle` to each local\n    /// identifier, and track its definition's span for use in diagnostics. All\n    /// the detailed information about locals - names, types, etc. - is kept in\n    /// the [`LocalDecl`] statements we parsed from their declarations. For\n    /// arguments, that information is kept in [`arguments`].\n    ///\n    /// In the AST, when an [`Ident`] expression refers to a local variable or\n    /// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this\n    /// arena.\n    ///\n    /// During lowering, [`LocalDecl`] statements add entries to a per-function\n    /// table that maps `Handle<Local>` values to their Naga representations,\n    /// accessed via [`StatementContext::local_table`] and\n    /// [`LocalExpressionContext::local_table`]. This table is then consulted when\n    /// lowering subsequent [`Ident`] expressions.\n    ///\n    /// [`LocalDecl`]: ast::StatementKind::LocalDecl\n    /// [`arguments`]: ast::Function::arguments\n    /// [`Ident`]: ast::Expression::Ident\n    /// [`IdentExpr`]: ast::IdentExpr\n    /// [`StatementContext::local_table`]: super::lower::StatementContext::local_table\n    /// [`LocalExpressionContext::local_table`]: super::lower::LocalExpressionContext::local_table\n    locals: &'out mut Arena<ast::Local>,\n\n    /// Identifiers used by the current global declaration that have no local definition.\n    ///\n    /// This becomes the [`GlobalDecl`]'s [`dependencies`] set.\n    ///\n    /// Note that we don't know at parse time what kind of [`GlobalDecl`] the\n    /// name refers to. We can't look up names until we've seen the entire\n    /// translation unit.\n    ///\n    /// [`GlobalDecl`]: ast::GlobalDecl\n    /// [`dependencies`]: ast::GlobalDecl::dependencies\n    unresolved: &'out mut FastIndexSet<ast::Dependency<'input>>,\n}\n\nimpl<'a> ExpressionContext<'a, '_, '_> {\n    fn parse_binary_op(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        classifier: impl Fn(Token<'a>) -> Option<crate::BinaryOperator>,\n        mut parser: impl FnMut(&mut Lexer<'a>, &mut Self) -> Result<'a, Handle<ast::Expression<'a>>>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        let start = lexer.start_byte_offset();\n        let mut accumulator = parser(lexer, self)?;\n        while let Some(op) = classifier(lexer.peek().0) {\n            let _ = lexer.next();\n            let left = accumulator;\n            let right = parser(lexer, self)?;\n            accumulator = self.expressions.append(\n                ast::Expression::Binary { op, left, right },\n                lexer.span_from(start),\n            );\n        }\n        Ok(accumulator)\n    }\n\n    fn declare_local(&mut self, name: ast::Ident<'a>) -> Result<'a, Handle<ast::Local>> {\n        let handle = self.locals.append(ast::Local, name.span);\n        if let Some(old) = self.local_table.add(name.name, handle) {\n            Err(Box::new(Error::Redefinition {\n                previous: self.locals.get_span(old),\n                current: name.span,\n            }))\n        } else {\n            Ok(handle)\n        }\n    }\n}\n\n/// Which grammar rule we are in the midst of parsing.\n///\n/// This is used for error checking. `Parser` maintains a stack of\n/// these and (occasionally) checks that it is being pushed and popped\n/// as expected.\n#[derive(Copy, Clone, Debug, PartialEq)]\nenum Rule {\n    Attribute,\n    VariableDecl,\n    FunctionDecl,\n    Block,\n    Statement,\n    PrimaryExpr,\n    SingularExpr,\n    UnaryExpr,\n    GeneralExpr,\n    Directive,\n    GenericExpr,\n    EnclosedExpr,\n    LhsExpr,\n}\n\nstruct ParsedAttribute<T> {\n    value: Option<T>,\n}\n\nimpl<T> Default for ParsedAttribute<T> {\n    fn default() -> Self {\n        Self { value: None }\n    }\n}\n\nimpl<T> ParsedAttribute<T> {\n    fn set(&mut self, value: T, name_span: Span) -> Result<'static, ()> {\n        if self.value.is_some() {\n            return Err(Box::new(Error::RepeatedAttribute(name_span)));\n        }\n        self.value = Some(value);\n        Ok(())\n    }\n}\n\n#[derive(Default)]\nstruct BindingParser<'a> {\n    location: ParsedAttribute<Handle<ast::Expression<'a>>>,\n    built_in: ParsedAttribute<crate::BuiltIn>,\n    interpolation: ParsedAttribute<crate::Interpolation>,\n    sampling: ParsedAttribute<crate::Sampling>,\n    invariant: ParsedAttribute<bool>,\n    blend_src: ParsedAttribute<Handle<ast::Expression<'a>>>,\n    per_primitive: ParsedAttribute<()>,\n}\n\nimpl<'a> BindingParser<'a> {\n    fn parse(\n        &mut self,\n        parser: &mut Parser,\n        lexer: &mut Lexer<'a>,\n        name: &'a str,\n        name_span: Span,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, ()> {\n        match name {\n            \"location\" => {\n                lexer.expect(Token::Paren('('))?;\n                self.location\n                    .set(parser.expression(lexer, ctx)?, name_span)?;\n                lexer.next_if(Token::Separator(','));\n                lexer.expect(Token::Paren(')'))?;\n            }\n            \"builtin\" => {\n                lexer.expect(Token::Paren('('))?;\n                let (raw, span) = lexer.next_ident_with_span()?;\n                self.built_in.set(\n                    conv::map_built_in(&lexer.enable_extensions, raw, span)?,\n                    name_span,\n                )?;\n                lexer.next_if(Token::Separator(','));\n                lexer.expect(Token::Paren(')'))?;\n            }\n            \"interpolate\" => {\n                lexer.expect(Token::Paren('('))?;\n                let (raw, span) = lexer.next_ident_with_span()?;\n                self.interpolation\n                    .set(conv::map_interpolation(raw, span)?, name_span)?;\n                if lexer.next_if(Token::Separator(',')) {\n                    let (raw, span) = lexer.next_ident_with_span()?;\n                    self.sampling\n                        .set(conv::map_sampling(raw, span)?, name_span)?;\n                }\n                lexer.next_if(Token::Separator(','));\n                lexer.expect(Token::Paren(')'))?;\n            }\n\n            \"invariant\" => {\n                self.invariant.set(true, name_span)?;\n            }\n            \"blend_src\" => {\n                lexer.require_enable_extension(\n                    ImplementedEnableExtension::DualSourceBlending,\n                    name_span,\n                )?;\n\n                lexer.expect(Token::Paren('('))?;\n                self.blend_src\n                    .set(parser.expression(lexer, ctx)?, name_span)?;\n                lexer.next_if(Token::Separator(','));\n                lexer.expect(Token::Paren(')'))?;\n            }\n            \"per_primitive\" => {\n                lexer.require_enable_extension(\n                    ImplementedEnableExtension::WgpuMeshShader,\n                    name_span,\n                )?;\n                self.per_primitive.set((), name_span)?;\n            }\n            _ => return Err(Box::new(Error::UnknownAttribute(name_span))),\n        }\n        Ok(())\n    }\n\n    fn finish(self, span: Span) -> Result<'a, Option<ast::Binding<'a>>> {\n        match (\n            self.location.value,\n            self.built_in.value,\n            self.interpolation.value,\n            self.sampling.value,\n            self.invariant.value.unwrap_or_default(),\n            self.blend_src.value,\n            self.per_primitive.value,\n        ) {\n            (None, None, None, None, false, None, None) => Ok(None),\n            (Some(location), None, interpolation, sampling, false, blend_src, per_primitive) => {\n                // Before handing over the completed `Module`, we call\n                // `apply_default_interpolation` to ensure that the interpolation and\n                // sampling have been explicitly specified on all vertex shader output and fragment\n                // shader input user bindings, so leaving them potentially `None` here is fine.\n                Ok(Some(ast::Binding::Location {\n                    location,\n                    interpolation,\n                    sampling,\n                    blend_src,\n                    per_primitive: per_primitive.is_some(),\n                }))\n            }\n            (None, Some(crate::BuiltIn::Position { .. }), None, None, invariant, None, None) => {\n                Ok(Some(ast::Binding::BuiltIn(crate::BuiltIn::Position {\n                    invariant,\n                })))\n            }\n            (None, Some(built_in), None, None, false, None, None) => {\n                Ok(Some(ast::Binding::BuiltIn(built_in)))\n            }\n            (_, _, _, _, _, _, _) => Err(Box::new(Error::InconsistentBinding(span))),\n        }\n    }\n}\n\n/// Configuration for the whole parser run.\npub struct Options {\n    /// Controls whether the parser should parse doc comments.\n    pub parse_doc_comments: bool,\n    /// Capabilities to enable during parsing.\n    pub capabilities: crate::valid::Capabilities,\n}\n\nimpl Options {\n    /// Creates a new default [`Options`].\n    pub const fn new() -> Self {\n        Options {\n            parse_doc_comments: false,\n            capabilities: crate::valid::Capabilities::all(),\n        }\n    }\n}\n\npub struct Parser {\n    rules: Vec<(Rule, usize)>,\n    recursion_depth: u32,\n}\n\nimpl Parser {\n    pub const fn new() -> Self {\n        Parser {\n            rules: Vec::new(),\n            recursion_depth: 0,\n        }\n    }\n\n    fn reset(&mut self) {\n        self.rules.clear();\n        self.recursion_depth = 0;\n    }\n\n    fn push_rule_span(&mut self, rule: Rule, lexer: &mut Lexer<'_>) {\n        self.rules.push((rule, lexer.start_byte_offset()));\n    }\n\n    fn pop_rule_span(&mut self, lexer: &Lexer<'_>) -> Span {\n        let (_, initial) = self.rules.pop().unwrap();\n        lexer.span_from(initial)\n    }\n\n    fn peek_rule_span(&mut self, lexer: &Lexer<'_>) -> Span {\n        let &(_, initial) = self.rules.last().unwrap();\n        lexer.span_from(initial)\n    }\n\n    fn race_rules(&self, rule0: Rule, rule1: Rule) -> Option<Rule> {\n        Some(\n            self.rules\n                .iter()\n                .rev()\n                .find(|&x| x.0 == rule0 || x.0 == rule1)?\n                .0,\n        )\n    }\n\n    fn track_recursion<'a, F, R>(&mut self, f: F) -> Result<'a, R>\n    where\n        F: FnOnce(&mut Self) -> Result<'a, R>,\n    {\n        self.recursion_depth += 1;\n        if self.recursion_depth >= 256 {\n            return Err(Box::new(Error::Internal(\"Parser recursion limit exceeded\")));\n        }\n        let ret = f(self);\n        self.recursion_depth -= 1;\n        ret\n    }\n\n    fn switch_value<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, ast::SwitchValue<'a>> {\n        if lexer.next_if(Token::Word(\"default\")) {\n            return Ok(ast::SwitchValue::Default);\n        }\n\n        let expr = self.expression(lexer, ctx)?;\n        Ok(ast::SwitchValue::Expr(expr))\n    }\n\n    /// Expects `name` to be consumed (not in lexer).\n    fn arguments<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Vec<Handle<ast::Expression<'a>>>> {\n        self.push_rule_span(Rule::EnclosedExpr, lexer);\n        lexer.open_arguments()?;\n        let mut arguments = Vec::new();\n        loop {\n            if !arguments.is_empty() {\n                if !lexer.next_argument()? {\n                    break;\n                }\n            } else if lexer.next_if(Token::Paren(')')) {\n                break;\n            }\n            let arg = self.expression(lexer, ctx)?;\n            arguments.push(arg);\n        }\n\n        self.pop_rule_span(lexer);\n        Ok(arguments)\n    }\n\n    fn enclosed_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.push_rule_span(Rule::EnclosedExpr, lexer);\n        let expr = self.expression(lexer, ctx)?;\n        self.pop_rule_span(lexer);\n        Ok(expr)\n    }\n\n    fn ident_expr<'a>(\n        &mut self,\n        name: &'a str,\n        name_span: Span,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> ast::IdentExpr<'a> {\n        match ctx.local_table.lookup(name) {\n            Some(&local) => ast::IdentExpr::Local(local),\n            None => {\n                ctx.unresolved.insert(ast::Dependency {\n                    ident: name,\n                    usage: name_span,\n                });\n                ast::IdentExpr::Unresolved(name)\n            }\n        }\n    }\n\n    fn primary_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        token: TokenSpan<'a>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.push_rule_span(Rule::PrimaryExpr, lexer);\n\n        const fn literal_ray_flag<'b>(flag: crate::RayFlag) -> ast::Expression<'b> {\n            ast::Expression::Literal(ast::Literal::Number(Number::U32(flag.bits())))\n        }\n        const fn literal_ray_intersection<'b>(\n            intersection: crate::RayQueryIntersection,\n        ) -> ast::Expression<'b> {\n            ast::Expression::Literal(ast::Literal::Number(Number::U32(intersection as u32)))\n        }\n\n        let expr = match token {\n            (Token::Paren('('), _) => {\n                let expr = self.enclosed_expression(lexer, ctx)?;\n                lexer.expect(Token::Paren(')'))?;\n                self.pop_rule_span(lexer);\n                return Ok(expr);\n            }\n            (Token::Word(\"true\"), _) => ast::Expression::Literal(ast::Literal::Bool(true)),\n            (Token::Word(\"false\"), _) => ast::Expression::Literal(ast::Literal::Bool(false)),\n            (Token::Number(res), span) => {\n                let num = res.map_err(|err| Error::BadNumber(span, err))?;\n\n                if let Some(enable_extension) = num.requires_enable_extension() {\n                    lexer.require_enable_extension(enable_extension, span)?;\n                }\n\n                ast::Expression::Literal(ast::Literal::Number(num))\n            }\n            (Token::Word(\"RAY_FLAG_NONE\"), _) => literal_ray_flag(crate::RayFlag::empty()),\n            (Token::Word(\"RAY_FLAG_FORCE_OPAQUE\"), _) => {\n                literal_ray_flag(crate::RayFlag::FORCE_OPAQUE)\n            }\n            (Token::Word(\"RAY_FLAG_FORCE_NO_OPAQUE\"), _) => {\n                literal_ray_flag(crate::RayFlag::FORCE_NO_OPAQUE)\n            }\n            (Token::Word(\"RAY_FLAG_TERMINATE_ON_FIRST_HIT\"), _) => {\n                literal_ray_flag(crate::RayFlag::TERMINATE_ON_FIRST_HIT)\n            }\n            (Token::Word(\"RAY_FLAG_SKIP_CLOSEST_HIT_SHADER\"), _) => {\n                literal_ray_flag(crate::RayFlag::SKIP_CLOSEST_HIT_SHADER)\n            }\n            (Token::Word(\"RAY_FLAG_CULL_BACK_FACING\"), _) => {\n                literal_ray_flag(crate::RayFlag::CULL_BACK_FACING)\n            }\n            (Token::Word(\"RAY_FLAG_CULL_FRONT_FACING\"), _) => {\n                literal_ray_flag(crate::RayFlag::CULL_FRONT_FACING)\n            }\n            (Token::Word(\"RAY_FLAG_CULL_OPAQUE\"), _) => {\n                literal_ray_flag(crate::RayFlag::CULL_OPAQUE)\n            }\n            (Token::Word(\"RAY_FLAG_CULL_NO_OPAQUE\"), _) => {\n                literal_ray_flag(crate::RayFlag::CULL_NO_OPAQUE)\n            }\n            (Token::Word(\"RAY_FLAG_SKIP_TRIANGLES\"), _) => {\n                literal_ray_flag(crate::RayFlag::SKIP_TRIANGLES)\n            }\n            (Token::Word(\"RAY_FLAG_SKIP_AABBS\"), _) => literal_ray_flag(crate::RayFlag::SKIP_AABBS),\n            (Token::Word(\"RAY_QUERY_INTERSECTION_NONE\"), _) => {\n                literal_ray_intersection(crate::RayQueryIntersection::None)\n            }\n            (Token::Word(\"RAY_QUERY_INTERSECTION_TRIANGLE\"), _) => {\n                literal_ray_intersection(crate::RayQueryIntersection::Triangle)\n            }\n            (Token::Word(\"RAY_QUERY_INTERSECTION_GENERATED\"), _) => {\n                literal_ray_intersection(crate::RayQueryIntersection::Generated)\n            }\n            (Token::Word(\"RAY_QUERY_INTERSECTION_AABB\"), _) => {\n                literal_ray_intersection(crate::RayQueryIntersection::Aabb)\n            }\n            (Token::Word(word), span) => {\n                let ident = self.template_elaborated_ident(word, span, lexer, ctx)?;\n\n                if let Token::Paren('(') = lexer.peek().0 {\n                    let arguments = self.arguments(lexer, ctx)?;\n                    ast::Expression::Call(ast::CallPhrase {\n                        function: ident,\n                        arguments,\n                    })\n                } else {\n                    ast::Expression::Ident(ident)\n                }\n            }\n            other => {\n                return Err(Box::new(Error::Unexpected(\n                    other.1,\n                    ExpectedToken::PrimaryExpression,\n                )))\n            }\n        };\n\n        self.pop_rule_span(lexer);\n        let span = lexer.span_with_start(token.1);\n        let expr = ctx.expressions.append(expr, span);\n        Ok(expr)\n    }\n\n    fn component_or_swizzle_specifier<'a>(\n        &mut self,\n        expr_start: Span,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        expr: Handle<ast::Expression<'a>>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        let mut expr = expr;\n\n        loop {\n            let expression = match lexer.peek().0 {\n                Token::Separator('.') => {\n                    let _ = lexer.next();\n                    let field = lexer.next_ident()?;\n\n                    ast::Expression::Member { base: expr, field }\n                }\n                Token::Paren('[') => {\n                    let _ = lexer.next();\n                    let index = self.enclosed_expression(lexer, ctx)?;\n                    lexer.expect(Token::Paren(']'))?;\n\n                    ast::Expression::Index { base: expr, index }\n                }\n                _ => break,\n            };\n\n            let span = lexer.span_with_start(expr_start);\n            expr = ctx.expressions.append(expression, span);\n        }\n\n        Ok(expr)\n    }\n\n    /// Parse a `unary_expression`.\n    fn unary_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.push_rule_span(Rule::UnaryExpr, lexer);\n\n        enum UnaryOp {\n            Negate,\n            LogicalNot,\n            BitwiseNot,\n            Deref,\n            AddrOf,\n        }\n\n        let mut ops = Vec::new();\n        let mut expr;\n\n        loop {\n            match lexer.next() {\n                (Token::Operation('-'), span) => {\n                    ops.push((UnaryOp::Negate, span));\n                }\n                (Token::Operation('!'), span) => {\n                    ops.push((UnaryOp::LogicalNot, span));\n                }\n                (Token::Operation('~'), span) => {\n                    ops.push((UnaryOp::BitwiseNot, span));\n                }\n                (Token::Operation('*'), span) => {\n                    ops.push((UnaryOp::Deref, span));\n                }\n                (Token::Operation('&'), span) => {\n                    ops.push((UnaryOp::AddrOf, span));\n                }\n                token => {\n                    expr = self.singular_expression(lexer, ctx, token)?;\n                    break;\n                }\n            };\n        }\n\n        for (op, span) in ops.into_iter().rev() {\n            let e = match op {\n                UnaryOp::Negate => ast::Expression::Unary {\n                    op: crate::UnaryOperator::Negate,\n                    expr,\n                },\n                UnaryOp::LogicalNot => ast::Expression::Unary {\n                    op: crate::UnaryOperator::LogicalNot,\n                    expr,\n                },\n                UnaryOp::BitwiseNot => ast::Expression::Unary {\n                    op: crate::UnaryOperator::BitwiseNot,\n                    expr,\n                },\n                UnaryOp::Deref => ast::Expression::Deref(expr),\n                UnaryOp::AddrOf => ast::Expression::AddrOf(expr),\n            };\n            let span = lexer.span_with_start(span);\n            expr = ctx.expressions.append(e, span);\n        }\n\n        self.pop_rule_span(lexer);\n        Ok(expr)\n    }\n\n    /// Parse a `lhs_expression`.\n    ///\n    /// LHS expressions only support the `&` and `*` operators and\n    /// the `[]` and `.` postfix selectors.\n    fn lhs_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        token: Option<TokenSpan<'a>>,\n        expected_token: ExpectedToken<'a>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.track_recursion(|this| {\n            this.push_rule_span(Rule::LhsExpr, lexer);\n            let token = token.unwrap_or_else(|| lexer.next());\n            let expr = match token {\n                (Token::Operation('*'), _) => {\n                    let expr =\n                        this.lhs_expression(lexer, ctx, None, ExpectedToken::LhsExpression)?;\n                    let expr = ast::Expression::Deref(expr);\n                    let span = this.peek_rule_span(lexer);\n                    ctx.expressions.append(expr, span)\n                }\n                (Token::Operation('&'), _) => {\n                    let expr =\n                        this.lhs_expression(lexer, ctx, None, ExpectedToken::LhsExpression)?;\n                    let expr = ast::Expression::AddrOf(expr);\n                    let span = this.peek_rule_span(lexer);\n                    ctx.expressions.append(expr, span)\n                }\n                (Token::Paren('('), span) => {\n                    let expr =\n                        this.lhs_expression(lexer, ctx, None, ExpectedToken::LhsExpression)?;\n                    lexer.expect(Token::Paren(')'))?;\n                    this.component_or_swizzle_specifier(span, lexer, ctx, expr)?\n                }\n                (Token::Word(word), span) => {\n                    let ident = this.ident_expr(word, span, ctx);\n                    let ident = ast::TemplateElaboratedIdent {\n                        ident,\n                        ident_span: span,\n                        template_list: Vec::new(),\n                        template_list_span: Span::UNDEFINED,\n                    };\n                    let ident = ctx.expressions.append(ast::Expression::Ident(ident), span);\n                    this.component_or_swizzle_specifier(span, lexer, ctx, ident)?\n                }\n                (_, span) => {\n                    return Err(Box::new(Error::Unexpected(span, expected_token)));\n                }\n            };\n\n            this.pop_rule_span(lexer);\n            Ok(expr)\n        })\n    }\n\n    /// Parse a `singular_expression`.\n    fn singular_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        token: TokenSpan<'a>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.push_rule_span(Rule::SingularExpr, lexer);\n        let primary_expr = self.primary_expression(lexer, ctx, token)?;\n        let singular_expr =\n            self.component_or_swizzle_specifier(token.1, lexer, ctx, primary_expr)?;\n        self.pop_rule_span(lexer);\n\n        Ok(singular_expr)\n    }\n\n    fn equality_expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        context: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        // equality_expression\n        context.parse_binary_op(\n            lexer,\n            |token| match token {\n                Token::LogicalOperation('=') => Some(crate::BinaryOperator::Equal),\n                Token::LogicalOperation('!') => Some(crate::BinaryOperator::NotEqual),\n                _ => None,\n            },\n            // relational_expression\n            |lexer, context| {\n                let enclosing = self.race_rules(Rule::GenericExpr, Rule::EnclosedExpr);\n                context.parse_binary_op(\n                    lexer,\n                    match enclosing {\n                        Some(Rule::GenericExpr) => |token| match token {\n                            Token::LogicalOperation('<') => Some(crate::BinaryOperator::LessEqual),\n                            _ => None,\n                        },\n                        _ => |token| match token {\n                            Token::Paren('<') => Some(crate::BinaryOperator::Less),\n                            Token::Paren('>') => Some(crate::BinaryOperator::Greater),\n                            Token::LogicalOperation('<') => Some(crate::BinaryOperator::LessEqual),\n                            Token::LogicalOperation('>') => {\n                                Some(crate::BinaryOperator::GreaterEqual)\n                            }\n                            _ => None,\n                        },\n                    },\n                    // shift_expression\n                    |lexer, context| {\n                        context.parse_binary_op(\n                            lexer,\n                            match enclosing {\n                                Some(Rule::GenericExpr) => |token| match token {\n                                    Token::ShiftOperation('<') => {\n                                        Some(crate::BinaryOperator::ShiftLeft)\n                                    }\n                                    _ => None,\n                                },\n                                _ => |token| match token {\n                                    Token::ShiftOperation('<') => {\n                                        Some(crate::BinaryOperator::ShiftLeft)\n                                    }\n                                    Token::ShiftOperation('>') => {\n                                        Some(crate::BinaryOperator::ShiftRight)\n                                    }\n                                    _ => None,\n                                },\n                            },\n                            // additive_expression\n                            |lexer, context| {\n                                context.parse_binary_op(\n                                    lexer,\n                                    |token| match token {\n                                        Token::Operation('+') => Some(crate::BinaryOperator::Add),\n                                        Token::Operation('-') => {\n                                            Some(crate::BinaryOperator::Subtract)\n                                        }\n                                        _ => None,\n                                    },\n                                    // multiplicative_expression\n                                    |lexer, context| {\n                                        context.parse_binary_op(\n                                            lexer,\n                                            |token| match token {\n                                                Token::Operation('*') => {\n                                                    Some(crate::BinaryOperator::Multiply)\n                                                }\n                                                Token::Operation('/') => {\n                                                    Some(crate::BinaryOperator::Divide)\n                                                }\n                                                Token::Operation('%') => {\n                                                    Some(crate::BinaryOperator::Modulo)\n                                                }\n                                                _ => None,\n                                            },\n                                            |lexer, context| self.unary_expression(lexer, context),\n                                        )\n                                    },\n                                )\n                            },\n                        )\n                    },\n                )\n            },\n        )\n    }\n\n    fn expression<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        context: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Handle<ast::Expression<'a>>> {\n        self.push_rule_span(Rule::GeneralExpr, lexer);\n        // logical_or_expression\n        let handle = context.parse_binary_op(\n            lexer,\n            |token| match token {\n                Token::LogicalOperation('|') => Some(crate::BinaryOperator::LogicalOr),\n                _ => None,\n            },\n            // logical_and_expression\n            |lexer, context| {\n                context.parse_binary_op(\n                    lexer,\n                    |token| match token {\n                        Token::LogicalOperation('&') => Some(crate::BinaryOperator::LogicalAnd),\n                        _ => None,\n                    },\n                    // inclusive_or_expression\n                    |lexer, context| {\n                        context.parse_binary_op(\n                            lexer,\n                            |token| match token {\n                                Token::Operation('|') => Some(crate::BinaryOperator::InclusiveOr),\n                                _ => None,\n                            },\n                            // exclusive_or_expression\n                            |lexer, context| {\n                                context.parse_binary_op(\n                                    lexer,\n                                    |token| match token {\n                                        Token::Operation('^') => {\n                                            Some(crate::BinaryOperator::ExclusiveOr)\n                                        }\n                                        _ => None,\n                                    },\n                                    // and_expression\n                                    |lexer, context| {\n                                        context.parse_binary_op(\n                                            lexer,\n                                            |token| match token {\n                                                Token::Operation('&') => {\n                                                    Some(crate::BinaryOperator::And)\n                                                }\n                                                _ => None,\n                                            },\n                                            |lexer, context| {\n                                                self.equality_expression(lexer, context)\n                                            },\n                                        )\n                                    },\n                                )\n                            },\n                        )\n                    },\n                )\n            },\n        )?;\n        self.pop_rule_span(lexer);\n        Ok(handle)\n    }\n\n    fn optionally_typed_ident<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, (ast::Ident<'a>, Option<ast::TemplateElaboratedIdent<'a>>)> {\n        let name = lexer.next_ident()?;\n\n        let ty = if lexer.next_if(Token::Separator(':')) {\n            Some(self.type_specifier(lexer, ctx)?)\n        } else {\n            None\n        };\n\n        Ok((name, ty))\n    }\n\n    /// 'var' _disambiguate_template template_list? optionally_typed_ident\n    fn variable_decl<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, ast::GlobalVariable<'a>> {\n        self.push_rule_span(Rule::VariableDecl, lexer);\n        let (template_list, _) = self.maybe_template_list(lexer, ctx)?;\n        let (name, ty) = self.optionally_typed_ident(lexer, ctx)?;\n\n        let init = if lexer.next_if(Token::Operation('=')) {\n            let handle = self.expression(lexer, ctx)?;\n            Some(handle)\n        } else {\n            None\n        };\n        lexer.expect(Token::Separator(';'))?;\n        self.pop_rule_span(lexer);\n\n        Ok(ast::GlobalVariable {\n            name,\n            template_list,\n            binding: None,\n            ty,\n            init,\n            doc_comments: Vec::new(),\n            memory_decorations: crate::MemoryDecorations::empty(),\n        })\n    }\n\n    fn struct_body<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Vec<ast::StructMember<'a>>> {\n        let mut members = Vec::new();\n        let mut member_names = FastHashSet::default();\n\n        lexer.expect(Token::Paren('{'))?;\n        let mut ready = true;\n        while !lexer.next_if(Token::Paren('}')) {\n            if !ready {\n                return Err(Box::new(Error::Unexpected(\n                    lexer.next().1,\n                    ExpectedToken::Token(Token::Separator(',')),\n                )));\n            }\n\n            let doc_comments = lexer.accumulate_doc_comments();\n\n            let (mut size, mut align) = (ParsedAttribute::default(), ParsedAttribute::default());\n            self.push_rule_span(Rule::Attribute, lexer);\n            let mut bind_parser = BindingParser::default();\n            while lexer.next_if(Token::Attribute) {\n                match lexer.next_ident_with_span()? {\n                    (\"size\", name_span) => {\n                        lexer.expect(Token::Paren('('))?;\n                        let expr = self.expression(lexer, ctx)?;\n                        lexer.next_if(Token::Separator(','));\n                        lexer.expect(Token::Paren(')'))?;\n                        size.set(expr, name_span)?;\n                    }\n                    (\"align\", name_span) => {\n                        lexer.expect(Token::Paren('('))?;\n                        let expr = self.expression(lexer, ctx)?;\n                        lexer.next_if(Token::Separator(','));\n                        lexer.expect(Token::Paren(')'))?;\n                        align.set(expr, name_span)?;\n                    }\n                    (word, word_span) => bind_parser.parse(self, lexer, word, word_span, ctx)?,\n                }\n            }\n\n            let bind_span = self.pop_rule_span(lexer);\n            let binding = bind_parser.finish(bind_span)?;\n\n            let name = lexer.next_ident()?;\n            lexer.expect(Token::Separator(':'))?;\n            let ty = self.type_specifier(lexer, ctx)?;\n            ready = lexer.next_if(Token::Separator(','));\n\n            members.push(ast::StructMember {\n                name,\n                ty,\n                binding,\n                size: size.value,\n                align: align.value,\n                doc_comments,\n            });\n\n            if !member_names.insert(name.name) {\n                return Err(Box::new(Error::Redefinition {\n                    previous: members\n                        .iter()\n                        .find(|x| x.name.name == name.name)\n                        .map(|x| x.name.span)\n                        .unwrap(),\n                    current: name.span,\n                }));\n            }\n        }\n\n        Ok(members)\n    }\n\n    fn maybe_template_list<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, (Vec<Handle<ast::Expression<'a>>>, Span)> {\n        let start = lexer.start_byte_offset();\n        if lexer.next_if(Token::TemplateArgsStart) {\n            let mut args = Vec::new();\n            args.push(self.expression(lexer, ctx)?);\n            while lexer.next_if(Token::Separator(',')) && lexer.peek().0 != Token::TemplateArgsEnd {\n                args.push(self.expression(lexer, ctx)?);\n            }\n            lexer.expect(Token::TemplateArgsEnd)?;\n            let span = lexer.span_from(start);\n            Ok((args, span))\n        } else {\n            Ok((Vec::new(), Span::UNDEFINED))\n        }\n    }\n\n    fn template_elaborated_ident<'a>(\n        &mut self,\n        word: &'a str,\n        span: Span,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, ast::TemplateElaboratedIdent<'a>> {\n        let ident = self.ident_expr(word, span, ctx);\n        let (template_list, template_list_span) = self.maybe_template_list(lexer, ctx)?;\n        Ok(ast::TemplateElaboratedIdent {\n            ident,\n            ident_span: span,\n            template_list,\n            template_list_span,\n        })\n    }\n\n    fn type_specifier<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, ast::TemplateElaboratedIdent<'a>> {\n        let (name, span) = lexer.next_ident_with_span()?;\n        self.template_elaborated_ident(name, span, lexer, ctx)\n    }\n\n    /// Parses assignment, increment and decrement statements\n    ///\n    /// This does not consume or require a final `;` token. In the update\n    /// expression of a C-style `for` loop header, there is no terminating `;`.\n    fn variable_updating_statement<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        block: &mut ast::Block<'a>,\n        token: TokenSpan<'a>,\n        expected_token: ExpectedToken<'a>,\n    ) -> Result<'a, ()> {\n        match token {\n            (Token::Word(\"_\"), span) => {\n                lexer.expect(Token::Operation('='))?;\n                let expr = self.expression(lexer, ctx)?;\n                let span = lexer.span_with_start(span);\n                block.stmts.push(ast::Statement {\n                    kind: ast::StatementKind::Phony(expr),\n                    span,\n                });\n                return Ok(());\n            }\n            _ => {}\n        }\n        let target = self.lhs_expression(lexer, ctx, Some(token), expected_token)?;\n\n        let (op, value) = match lexer.next() {\n            (Token::Operation('='), _) => {\n                let value = self.expression(lexer, ctx)?;\n                (None, value)\n            }\n            (Token::AssignmentOperation(c), _) => {\n                use crate::BinaryOperator as Bo;\n                let op = match c {\n                    '<' => Bo::ShiftLeft,\n                    '>' => Bo::ShiftRight,\n                    '+' => Bo::Add,\n                    '-' => Bo::Subtract,\n                    '*' => Bo::Multiply,\n                    '/' => Bo::Divide,\n                    '%' => Bo::Modulo,\n                    '&' => Bo::And,\n                    '|' => Bo::InclusiveOr,\n                    '^' => Bo::ExclusiveOr,\n                    // Note: `consume_token` shouldn't produce any other assignment ops\n                    _ => unreachable!(),\n                };\n\n                let value = self.expression(lexer, ctx)?;\n                (Some(op), value)\n            }\n            op_token @ (Token::IncrementOperation | Token::DecrementOperation, _) => {\n                let op = match op_token.0 {\n                    Token::IncrementOperation => ast::StatementKind::Increment,\n                    Token::DecrementOperation => ast::StatementKind::Decrement,\n                    _ => unreachable!(),\n                };\n\n                let span = lexer.span_with_start(token.1);\n                block.stmts.push(ast::Statement {\n                    kind: op(target),\n                    span,\n                });\n                return Ok(());\n            }\n            (_, span) => return Err(Box::new(Error::Unexpected(span, ExpectedToken::Assignment))),\n        };\n\n        let span = lexer.span_with_start(token.1);\n        block.stmts.push(ast::Statement {\n            kind: ast::StatementKind::Assign { target, op, value },\n            span,\n        });\n        Ok(())\n    }\n\n    /// Parse a function call statement.\n    ///\n    /// This assumes that `token` has been consumed from the lexer.\n    ///\n    /// This does not consume or require a final `;` token. In the update\n    /// expression of a C-style `for` loop header, there is no terminating `;`.\n    fn maybe_func_call_statement<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        context: &mut ExpressionContext<'a, '_, '_>,\n        block: &mut ast::Block<'a>,\n        token: TokenSpan<'a>,\n    ) -> Result<'a, bool> {\n        let (name, name_span) = match token {\n            (Token::Word(name), span) => (name, span),\n            _ => return Ok(false),\n        };\n        let ident = self.template_elaborated_ident(name, name_span, lexer, context)?;\n        if ident.template_list.is_empty() && !matches!(lexer.peek(), (Token::Paren('('), _)) {\n            return Ok(false);\n        }\n\n        self.push_rule_span(Rule::SingularExpr, lexer);\n\n        let arguments = self.arguments(lexer, context)?;\n        let span = lexer.span_with_start(name_span);\n\n        block.stmts.push(ast::Statement {\n            kind: ast::StatementKind::Call(ast::CallPhrase {\n                function: ident,\n                arguments,\n            }),\n            span,\n        });\n\n        self.pop_rule_span(lexer);\n\n        Ok(true)\n    }\n\n    /// Parses func_call_statement and variable_updating_statement\n    ///\n    /// This does not consume or require a final `;` token. In the update\n    /// expression of a C-style `for` loop header, there is no terminating `;`.\n    fn func_call_or_variable_updating_statement<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        context: &mut ExpressionContext<'a, '_, '_>,\n        block: &mut ast::Block<'a>,\n        token: TokenSpan<'a>,\n        expected_token: ExpectedToken<'a>,\n    ) -> Result<'a, ()> {\n        if !self.maybe_func_call_statement(lexer, context, block, token)? {\n            self.variable_updating_statement(lexer, context, block, token, expected_token)?;\n        }\n        Ok(())\n    }\n\n    /// Parses variable_or_value_statement, func_call_statement and variable_updating_statement.\n    ///\n    /// This is equivalent to the `for_init` production in the WGSL spec,\n    /// but it's also used for parsing these forms when they appear within a block,\n    /// hence the longer name.\n    ///\n    /// This does not consume the following `;` token.\n    fn variable_or_value_or_func_call_or_variable_updating_statement<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        block: &mut ast::Block<'a>,\n        token: TokenSpan<'a>,\n        expected_token: ExpectedToken<'a>,\n    ) -> Result<'a, ()> {\n        let local_decl = match token {\n            (Token::Word(\"let\"), _) => {\n                let (name, given_ty) = self.optionally_typed_ident(lexer, ctx)?;\n\n                lexer.expect(Token::Operation('='))?;\n                let expr_id = self.expression(lexer, ctx)?;\n\n                let handle = ctx.declare_local(name)?;\n                ast::LocalDecl::Let(ast::Let {\n                    name,\n                    ty: given_ty,\n                    init: expr_id,\n                    handle,\n                })\n            }\n            (Token::Word(\"const\"), _) => {\n                let (name, given_ty) = self.optionally_typed_ident(lexer, ctx)?;\n\n                lexer.expect(Token::Operation('='))?;\n                let expr_id = self.expression(lexer, ctx)?;\n\n                let handle = ctx.declare_local(name)?;\n                ast::LocalDecl::Const(ast::LocalConst {\n                    name,\n                    ty: given_ty,\n                    init: expr_id,\n                    handle,\n                })\n            }\n            (Token::Word(\"var\"), _) => {\n                if lexer.next_if(Token::TemplateArgsStart) {\n                    let (class_str, span) = lexer.next_ident_with_span()?;\n                    if class_str != \"function\" {\n                        return Err(Box::new(Error::InvalidLocalVariableAddressSpace(span)));\n                    }\n                    lexer.expect(Token::TemplateArgsEnd)?;\n                }\n\n                let (name, ty) = self.optionally_typed_ident(lexer, ctx)?;\n\n                let init = if lexer.next_if(Token::Operation('=')) {\n                    let init = self.expression(lexer, ctx)?;\n                    Some(init)\n                } else {\n                    None\n                };\n\n                let handle = ctx.declare_local(name)?;\n                ast::LocalDecl::Var(ast::LocalVariable {\n                    name,\n                    ty,\n                    init,\n                    handle,\n                })\n            }\n            token => {\n                return self.func_call_or_variable_updating_statement(\n                    lexer,\n                    ctx,\n                    block,\n                    token,\n                    expected_token,\n                );\n            }\n        };\n\n        let span = lexer.span_with_start(token.1);\n        block.stmts.push(ast::Statement {\n            kind: ast::StatementKind::LocalDecl(local_decl),\n            span,\n        });\n\n        Ok(())\n    }\n\n    fn statement<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        block: &mut ast::Block<'a>,\n        brace_nesting_level: u8,\n    ) -> Result<'a, ()> {\n        self.track_recursion(|this| {\n            this.push_rule_span(Rule::Statement, lexer);\n\n            // We peek here instead of eagerly getting the next token since\n            // `Parser::block` expects its first token to be `{`.\n            //\n            // Most callers have a single path leading to the start of the block;\n            // `statement` is the only exception where there are multiple choices.\n            match lexer.peek() {\n                (token, _) if is_start_of_compound_statement(token) => {\n                    let (inner, span) = this.block(lexer, ctx, brace_nesting_level)?;\n                    block.stmts.push(ast::Statement {\n                        kind: ast::StatementKind::Block(inner),\n                        span,\n                    });\n                    this.pop_rule_span(lexer);\n                    return Ok(());\n                }\n                _ => {}\n            }\n\n            let kind = match lexer.next() {\n                (Token::Separator(';'), _) => {\n                    this.pop_rule_span(lexer);\n                    return Ok(());\n                }\n                (Token::Word(\"return\"), _) => {\n                    let value = if lexer.peek().0 != Token::Separator(';') {\n                        let handle = this.expression(lexer, ctx)?;\n                        Some(handle)\n                    } else {\n                        None\n                    };\n                    lexer.expect(Token::Separator(';'))?;\n                    ast::StatementKind::Return { value }\n                }\n                (Token::Word(\"if\"), _) => {\n                    let condition = this.expression(lexer, ctx)?;\n\n                    let accept = this.block(lexer, ctx, brace_nesting_level)?.0;\n\n                    let mut elsif_stack = Vec::new();\n                    let mut elseif_span_start = lexer.start_byte_offset();\n                    let mut reject = loop {\n                        if !lexer.next_if(Token::Word(\"else\")) {\n                            break ast::Block::default();\n                        }\n\n                        if !lexer.next_if(Token::Word(\"if\")) {\n                            // ... else { ... }\n                            break this.block(lexer, ctx, brace_nesting_level)?.0;\n                        }\n\n                        // ... else if (...) { ... }\n                        let other_condition = this.expression(lexer, ctx)?;\n                        let other_block = this.block(lexer, ctx, brace_nesting_level)?;\n                        elsif_stack.push((elseif_span_start, other_condition, other_block));\n                        elseif_span_start = lexer.start_byte_offset();\n                    };\n\n                    // reverse-fold the else-if blocks\n                    //Note: we may consider uplifting this to the IR\n                    for (other_span_start, other_cond, other_block) in elsif_stack.into_iter().rev()\n                    {\n                        let sub_stmt = ast::StatementKind::If {\n                            condition: other_cond,\n                            accept: other_block.0,\n                            reject,\n                        };\n                        reject = ast::Block::default();\n                        let span = lexer.span_from(other_span_start);\n                        reject.stmts.push(ast::Statement {\n                            kind: sub_stmt,\n                            span,\n                        })\n                    }\n\n                    ast::StatementKind::If {\n                        condition,\n                        accept,\n                        reject,\n                    }\n                }\n                (Token::Word(\"switch\"), _) => {\n                    let selector = this.expression(lexer, ctx)?;\n                    let brace_span = lexer.expect_span(Token::Paren('{'))?;\n                    let brace_nesting_level =\n                        Self::increase_brace_nesting(brace_nesting_level, brace_span)?;\n                    let mut cases = Vec::new();\n\n                    loop {\n                        // cases + default\n                        match lexer.next() {\n                            (Token::Word(\"case\"), _) => {\n                                // parse a list of values\n                                let value = loop {\n                                    let value = this.switch_value(lexer, ctx)?;\n                                    if lexer.next_if(Token::Separator(',')) {\n                                        // list of values ends with ':' or a compound statement\n                                        let next_token = lexer.peek().0;\n                                        if next_token == Token::Separator(':')\n                                            || is_start_of_compound_statement(next_token)\n                                        {\n                                            break value;\n                                        }\n                                    } else {\n                                        break value;\n                                    }\n                                    cases.push(ast::SwitchCase {\n                                        value,\n                                        body: ast::Block::default(),\n                                        fall_through: true,\n                                    });\n                                };\n\n                                lexer.next_if(Token::Separator(':'));\n\n                                let body = this.block(lexer, ctx, brace_nesting_level)?.0;\n\n                                cases.push(ast::SwitchCase {\n                                    value,\n                                    body,\n                                    fall_through: false,\n                                });\n                            }\n                            (Token::Word(\"default\"), _) => {\n                                lexer.next_if(Token::Separator(':'));\n                                let body = this.block(lexer, ctx, brace_nesting_level)?.0;\n                                cases.push(ast::SwitchCase {\n                                    value: ast::SwitchValue::Default,\n                                    body,\n                                    fall_through: false,\n                                });\n                            }\n                            (Token::Paren('}'), _) => break,\n                            (_, span) => {\n                                return Err(Box::new(Error::Unexpected(\n                                    span,\n                                    ExpectedToken::SwitchItem,\n                                )))\n                            }\n                        }\n                    }\n\n                    ast::StatementKind::Switch { selector, cases }\n                }\n                (Token::Word(\"loop\"), _) => this.r#loop(lexer, ctx, brace_nesting_level)?,\n                (Token::Word(\"while\"), _) => {\n                    let mut body = ast::Block::default();\n\n                    let (condition, span) =\n                        lexer.capture_span(|lexer| this.expression(lexer, ctx))?;\n                    let mut reject = ast::Block::default();\n                    reject.stmts.push(ast::Statement {\n                        kind: ast::StatementKind::Break,\n                        span,\n                    });\n\n                    body.stmts.push(ast::Statement {\n                        kind: ast::StatementKind::If {\n                            condition,\n                            accept: ast::Block::default(),\n                            reject,\n                        },\n                        span,\n                    });\n\n                    let (block, span) = this.block(lexer, ctx, brace_nesting_level)?;\n                    body.stmts.push(ast::Statement {\n                        kind: ast::StatementKind::Block(block),\n                        span,\n                    });\n\n                    ast::StatementKind::Loop {\n                        body,\n                        continuing: ast::Block::default(),\n                        break_if: None,\n                    }\n                }\n                (Token::Word(\"for\"), _) => {\n                    lexer.expect(Token::Paren('('))?;\n\n                    ctx.local_table.push_scope();\n\n                    if !lexer.next_if(Token::Separator(';')) {\n                        let token = lexer.next();\n                        this.variable_or_value_or_func_call_or_variable_updating_statement(\n                            lexer,\n                            ctx,\n                            block,\n                            token,\n                            ExpectedToken::ForInit,\n                        )?;\n                        lexer.expect(Token::Separator(';'))?;\n                    };\n\n                    let mut body = ast::Block::default();\n                    if !lexer.next_if(Token::Separator(';')) {\n                        let (condition, span) = lexer.capture_span(|lexer| -> Result<'_, _> {\n                            let condition = this.expression(lexer, ctx)?;\n                            lexer.expect(Token::Separator(';'))?;\n                            Ok(condition)\n                        })?;\n                        let mut reject = ast::Block::default();\n                        reject.stmts.push(ast::Statement {\n                            kind: ast::StatementKind::Break,\n                            span,\n                        });\n                        body.stmts.push(ast::Statement {\n                            kind: ast::StatementKind::If {\n                                condition,\n                                accept: ast::Block::default(),\n                                reject,\n                            },\n                            span,\n                        });\n                    };\n\n                    let mut continuing = ast::Block::default();\n                    if !lexer.next_if(Token::Paren(')')) {\n                        let token = lexer.next();\n                        this.func_call_or_variable_updating_statement(\n                            lexer,\n                            ctx,\n                            &mut continuing,\n                            token,\n                            ExpectedToken::ForUpdate,\n                        )?;\n                        lexer.expect(Token::Paren(')'))?;\n                    }\n\n                    let (block, span) = this.block(lexer, ctx, brace_nesting_level)?;\n                    body.stmts.push(ast::Statement {\n                        kind: ast::StatementKind::Block(block),\n                        span,\n                    });\n\n                    ctx.local_table.pop_scope();\n\n                    ast::StatementKind::Loop {\n                        body,\n                        continuing,\n                        break_if: None,\n                    }\n                }\n                (Token::Word(\"break\"), span) => {\n                    // Check if the next token is an `if`, this indicates\n                    // that the user tried to type out a `break if` which\n                    // is illegal in this position.\n                    let (peeked_token, peeked_span) = lexer.peek();\n                    if let Token::Word(\"if\") = peeked_token {\n                        let span = span.until(&peeked_span);\n                        return Err(Box::new(Error::InvalidBreakIf(span)));\n                    }\n                    lexer.expect(Token::Separator(';'))?;\n                    ast::StatementKind::Break\n                }\n                (Token::Word(\"continue\"), _) => {\n                    lexer.expect(Token::Separator(';'))?;\n                    ast::StatementKind::Continue\n                }\n                (Token::Word(\"discard\"), _) => {\n                    lexer.expect(Token::Separator(';'))?;\n                    ast::StatementKind::Kill\n                }\n                // https://www.w3.org/TR/WGSL/#const-assert-statement\n                (Token::Word(\"const_assert\"), _) => {\n                    // parentheses are optional\n                    let paren = lexer.next_if(Token::Paren('('));\n\n                    let condition = this.expression(lexer, ctx)?;\n\n                    if paren {\n                        lexer.expect(Token::Paren(')'))?;\n                    }\n                    lexer.expect(Token::Separator(';'))?;\n                    ast::StatementKind::ConstAssert(condition)\n                }\n                token => {\n                    this.variable_or_value_or_func_call_or_variable_updating_statement(\n                        lexer,\n                        ctx,\n                        block,\n                        token,\n                        ExpectedToken::Statement,\n                    )?;\n                    lexer.expect(Token::Separator(';'))?;\n                    this.pop_rule_span(lexer);\n                    return Ok(());\n                }\n            };\n\n            let span = this.pop_rule_span(lexer);\n            block.stmts.push(ast::Statement { kind, span });\n\n            Ok(())\n        })\n    }\n\n    fn r#loop<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        brace_nesting_level: u8,\n    ) -> Result<'a, ast::StatementKind<'a>> {\n        let mut body = ast::Block::default();\n        let mut continuing = ast::Block::default();\n        let mut break_if = None;\n\n        let brace_span = lexer.expect_span(Token::Paren('{'))?;\n        let brace_nesting_level = Self::increase_brace_nesting(brace_nesting_level, brace_span)?;\n\n        ctx.local_table.push_scope();\n\n        loop {\n            if lexer.next_if(Token::Word(\"continuing\")) {\n                // Branch for the `continuing` block, this must be\n                // the last thing in the loop body\n\n                // Expect a opening brace to start the continuing block\n                let brace_span = lexer.expect_span(Token::Paren('{'))?;\n                let brace_nesting_level =\n                    Self::increase_brace_nesting(brace_nesting_level, brace_span)?;\n                loop {\n                    if lexer.next_if(Token::Word(\"break\")) {\n                        // Branch for the `break if` statement, this statement\n                        // has the form `break if <expr>;` and must be the last\n                        // statement in a continuing block\n\n                        // The break must be followed by an `if` to form\n                        // the break if\n                        lexer.expect(Token::Word(\"if\"))?;\n\n                        let condition = self.expression(lexer, ctx)?;\n                        // Set the condition of the break if to the newly parsed\n                        // expression\n                        break_if = Some(condition);\n\n                        // Expect a semicolon to close the statement\n                        lexer.expect(Token::Separator(';'))?;\n                        // Expect a closing brace to close the continuing block,\n                        // since the break if must be the last statement\n                        lexer.expect(Token::Paren('}'))?;\n                        // Stop parsing the continuing block\n                        break;\n                    } else if lexer.next_if(Token::Paren('}')) {\n                        // If we encounter a closing brace it means we have reached\n                        // the end of the continuing block and should stop processing\n                        break;\n                    } else {\n                        // Otherwise try to parse a statement\n                        self.statement(lexer, ctx, &mut continuing, brace_nesting_level)?;\n                    }\n                }\n                // Since the continuing block must be the last part of the loop body,\n                // we expect to see a closing brace to end the loop body\n                lexer.expect(Token::Paren('}'))?;\n                break;\n            }\n            if lexer.next_if(Token::Paren('}')) {\n                // If we encounter a closing brace it means we have reached\n                // the end of the loop body and should stop processing\n                break;\n            }\n            // Otherwise try to parse a statement\n            self.statement(lexer, ctx, &mut body, brace_nesting_level)?;\n        }\n\n        ctx.local_table.pop_scope();\n\n        Ok(ast::StatementKind::Loop {\n            body,\n            continuing,\n            break_if,\n        })\n    }\n\n    /// compound_statement\n    fn block<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n        brace_nesting_level: u8,\n    ) -> Result<'a, (ast::Block<'a>, Span)> {\n        self.push_rule_span(Rule::Block, lexer);\n\n        ctx.local_table.push_scope();\n\n        let mut diagnostic_filters = DiagnosticFilterMap::new();\n\n        self.push_rule_span(Rule::Attribute, lexer);\n        while lexer.next_if(Token::Attribute) {\n            let (name, name_span) = lexer.next_ident_with_span()?;\n            if let Some(DirectiveKind::Diagnostic) = DirectiveKind::from_ident(name) {\n                let filter = self.diagnostic_filter(lexer)?;\n                let span = self.peek_rule_span(lexer);\n                diagnostic_filters\n                    .add(filter, span, ShouldConflictOnFullDuplicate::Yes)\n                    .map_err(|e| Box::new(e.into()))?;\n            } else {\n                return Err(Box::new(Error::Unexpected(\n                    name_span,\n                    ExpectedToken::DiagnosticAttribute,\n                )));\n            }\n        }\n        self.pop_rule_span(lexer);\n\n        if !diagnostic_filters.is_empty() {\n            return Err(Box::new(\n                Error::DiagnosticAttributeNotYetImplementedAtParseSite {\n                    site_name_plural: \"compound statements\",\n                    spans: diagnostic_filters.spans().collect(),\n                },\n            ));\n        }\n\n        let brace_span = lexer.expect_span(Token::Paren('{'))?;\n        let brace_nesting_level = Self::increase_brace_nesting(brace_nesting_level, brace_span)?;\n        let mut block = ast::Block::default();\n        while !lexer.next_if(Token::Paren('}')) {\n            self.statement(lexer, ctx, &mut block, brace_nesting_level)?;\n        }\n\n        ctx.local_table.pop_scope();\n\n        let span = self.pop_rule_span(lexer);\n        Ok((block, span))\n    }\n\n    fn varying_binding<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        ctx: &mut ExpressionContext<'a, '_, '_>,\n    ) -> Result<'a, Option<ast::Binding<'a>>> {\n        let mut bind_parser = BindingParser::default();\n        self.push_rule_span(Rule::Attribute, lexer);\n\n        while lexer.next_if(Token::Attribute) {\n            let (word, span) = lexer.next_ident_with_span()?;\n            bind_parser.parse(self, lexer, word, span, ctx)?;\n        }\n\n        let span = self.pop_rule_span(lexer);\n        bind_parser.finish(span)\n    }\n\n    fn function_decl<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n        must_use: Option<Span>,\n        out: &mut ast::TranslationUnit<'a>,\n        dependencies: &mut FastIndexSet<ast::Dependency<'a>>,\n    ) -> Result<'a, ast::Function<'a>> {\n        self.push_rule_span(Rule::FunctionDecl, lexer);\n        // read function name\n        let fun_name = lexer.next_ident()?;\n\n        let mut locals = Arena::new();\n\n        let mut ctx = ExpressionContext {\n            expressions: &mut out.expressions,\n            local_table: &mut SymbolTable::default(),\n            locals: &mut locals,\n            unresolved: dependencies,\n        };\n\n        // start a scope that contains arguments as well as the function body\n        ctx.local_table.push_scope();\n        // Reduce lookup scope to parse the parameter list and return type\n        // avoiding identifier lookup to match newly declared param names.\n        ctx.local_table.reduce_lookup_scope();\n\n        // read parameter list\n        let mut arguments = Vec::new();\n        lexer.expect(Token::Paren('('))?;\n        let mut ready = true;\n        while !lexer.next_if(Token::Paren(')')) {\n            if !ready {\n                return Err(Box::new(Error::Unexpected(\n                    lexer.next().1,\n                    ExpectedToken::Token(Token::Separator(',')),\n                )));\n            }\n            let binding = self.varying_binding(lexer, &mut ctx)?;\n\n            let param_name = lexer.next_ident()?;\n\n            lexer.expect(Token::Separator(':'))?;\n            let param_type = self.type_specifier(lexer, &mut ctx)?;\n\n            let handle = ctx.declare_local(param_name)?;\n            arguments.push(ast::FunctionArgument {\n                name: param_name,\n                ty: param_type,\n                binding,\n                handle,\n            });\n            ready = lexer.next_if(Token::Separator(','));\n        }\n        // read return type\n        let result = if lexer.next_if(Token::Arrow) {\n            let binding = self.varying_binding(lexer, &mut ctx)?;\n            let ty = self.type_specifier(lexer, &mut ctx)?;\n            let must_use = must_use.is_some();\n            Some(ast::FunctionResult {\n                ty,\n                binding,\n                must_use,\n            })\n        } else if let Some(must_use) = must_use {\n            return Err(Box::new(Error::FunctionMustUseReturnsVoid(\n                must_use,\n                self.peek_rule_span(lexer),\n            )));\n        } else {\n            None\n        };\n\n        ctx.local_table.reset_lookup_scope();\n\n        // do not use `self.block` here, since we must not push a new scope\n        lexer.expect(Token::Paren('{'))?;\n        let brace_nesting_level = 1;\n        let mut body = ast::Block::default();\n        while !lexer.next_if(Token::Paren('}')) {\n            self.statement(lexer, &mut ctx, &mut body, brace_nesting_level)?;\n        }\n\n        ctx.local_table.pop_scope();\n\n        let fun = ast::Function {\n            entry_point: None,\n            name: fun_name,\n            arguments,\n            result,\n            body,\n            diagnostic_filter_leaf,\n            doc_comments: Vec::new(),\n        };\n\n        // done\n        self.pop_rule_span(lexer);\n\n        Ok(fun)\n    }\n\n    fn directive_ident_list<'a>(\n        &self,\n        lexer: &mut Lexer<'a>,\n        handler: impl FnMut(&'a str, Span) -> Result<'a, ()>,\n    ) -> Result<'a, ()> {\n        let mut handler = handler;\n        'next_arg: loop {\n            let (ident, span) = lexer.next_ident_with_span()?;\n            handler(ident, span)?;\n\n            let expected_token = match lexer.peek().0 {\n                Token::Separator(',') => {\n                    let _ = lexer.next();\n                    if matches!(lexer.peek().0, Token::Word(..)) {\n                        continue 'next_arg;\n                    }\n                    ExpectedToken::AfterIdentListComma\n                }\n                _ => ExpectedToken::AfterIdentListArg,\n            };\n\n            if !matches!(lexer.next().0, Token::Separator(';')) {\n                return Err(Box::new(Error::Unexpected(span, expected_token)));\n            }\n\n            break Ok(());\n        }\n    }\n\n    fn global_decl<'a>(\n        &mut self,\n        lexer: &mut Lexer<'a>,\n        out: &mut ast::TranslationUnit<'a>,\n    ) -> Result<'a, ()> {\n        let doc_comments = lexer.accumulate_doc_comments();\n\n        // read attributes\n        let mut binding = None;\n        let mut stage = ParsedAttribute::default();\n        // Span in case we need to report an error for a shader stage missing something (e.g. its workgroup size).\n        // Doesn't need to be set in the vertex and fragment stages because they don't have errors like that.\n        let mut shader_stage_error_span = Span::new(0, 0);\n        let mut workgroup_size = ParsedAttribute::default();\n        let mut early_depth_test = ParsedAttribute::default();\n        let (mut bind_index, mut bind_group) =\n            (ParsedAttribute::default(), ParsedAttribute::default());\n        let mut id = ParsedAttribute::default();\n        // the payload variable for a mesh shader\n        let mut payload = ParsedAttribute::default();\n        // the incoming payload from a traceRay call\n        let mut incoming_payload = ParsedAttribute::default();\n        let mut mesh_output = ParsedAttribute::default();\n\n        let mut must_use: ParsedAttribute<Span> = ParsedAttribute::default();\n        let mut memory_decorations = crate::MemoryDecorations::empty();\n\n        let mut dependencies = FastIndexSet::default();\n        let mut ctx = ExpressionContext {\n            expressions: &mut out.expressions,\n            local_table: &mut SymbolTable::default(),\n            locals: &mut Arena::new(),\n            unresolved: &mut dependencies,\n        };\n        let mut diagnostic_filters = DiagnosticFilterMap::new();\n        let ensure_no_diag_attrs = |on_what, filters: DiagnosticFilterMap| -> Result<()> {\n            if filters.is_empty() {\n                Ok(())\n            } else {\n                Err(Box::new(Error::DiagnosticAttributeNotSupported {\n                    on_what,\n                    spans: filters.spans().collect(),\n                }))\n            }\n        };\n\n        self.push_rule_span(Rule::Attribute, lexer);\n        while lexer.next_if(Token::Attribute) {\n            let (name, name_span) = lexer.next_ident_with_span()?;\n            if let Some(DirectiveKind::Diagnostic) = DirectiveKind::from_ident(name) {\n                let filter = self.diagnostic_filter(lexer)?;\n                let span = self.peek_rule_span(lexer);\n                diagnostic_filters\n                    .add(filter, span, ShouldConflictOnFullDuplicate::Yes)\n                    .map_err(|e| Box::new(e.into()))?;\n                continue;\n            }\n            match name {\n                \"binding\" => {\n                    lexer.expect(Token::Paren('('))?;\n                    bind_index.set(self.expression(lexer, &mut ctx)?, name_span)?;\n                    lexer.next_if(Token::Separator(','));\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"group\" => {\n                    lexer.expect(Token::Paren('('))?;\n                    bind_group.set(self.expression(lexer, &mut ctx)?, name_span)?;\n                    lexer.next_if(Token::Separator(','));\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"id\" => {\n                    lexer.expect(Token::Paren('('))?;\n                    id.set(self.expression(lexer, &mut ctx)?, name_span)?;\n                    lexer.next_if(Token::Separator(','));\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"vertex\" => {\n                    stage.set(ShaderStage::Vertex, name_span)?;\n                }\n                \"fragment\" => {\n                    stage.set(ShaderStage::Fragment, name_span)?;\n                }\n                \"compute\" => {\n                    stage.set(ShaderStage::Compute, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"task\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuMeshShader,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::Task, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"mesh\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuMeshShader,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::Mesh, name_span)?;\n                    shader_stage_error_span = name_span;\n\n                    lexer.expect(Token::Paren('('))?;\n                    mesh_output.set(lexer.next_ident_with_span()?, name_span)?;\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"ray_generation\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuRayTracingPipeline,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::RayGeneration, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"any_hit\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuRayTracingPipeline,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::AnyHit, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"closest_hit\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuRayTracingPipeline,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::ClosestHit, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"miss\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuRayTracingPipeline,\n                        name_span,\n                    )?;\n                    stage.set(ShaderStage::Miss, name_span)?;\n                    shader_stage_error_span = name_span;\n                }\n                \"payload\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuMeshShader,\n                        name_span,\n                    )?;\n                    lexer.expect(Token::Paren('('))?;\n                    payload.set(lexer.next_ident_with_span()?, name_span)?;\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"incoming_payload\" => {\n                    lexer.require_enable_extension(\n                        ImplementedEnableExtension::WgpuRayTracingPipeline,\n                        name_span,\n                    )?;\n                    lexer.expect(Token::Paren('('))?;\n                    incoming_payload.set(lexer.next_ident_with_span()?, name_span)?;\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                \"workgroup_size\" => {\n                    lexer.expect(Token::Paren('('))?;\n                    let mut new_workgroup_size = [None; 3];\n                    for size in new_workgroup_size.iter_mut() {\n                        *size = Some(self.expression(lexer, &mut ctx)?);\n                        match lexer.next() {\n                            (Token::Paren(')'), _) => break,\n                            (Token::Separator(','), _) => {\n                                if lexer.next_if(Token::Paren(')')) {\n                                    break;\n                                }\n                            }\n                            other => {\n                                return Err(Box::new(Error::Unexpected(\n                                    other.1,\n                                    ExpectedToken::WorkgroupSizeSeparator,\n                                )))\n                            }\n                        }\n                    }\n                    workgroup_size.set(new_workgroup_size, name_span)?;\n                }\n                \"early_depth_test\" => {\n                    lexer.expect(Token::Paren('('))?;\n                    let (ident, ident_span) = lexer.next_ident_with_span()?;\n                    let value = if ident == \"force\" {\n                        crate::EarlyDepthTest::Force\n                    } else {\n                        crate::EarlyDepthTest::Allow {\n                            conservative: conv::map_conservative_depth(ident, ident_span)?,\n                        }\n                    };\n                    lexer.expect(Token::Paren(')'))?;\n                    early_depth_test.set(value, name_span)?;\n                }\n                \"must_use\" => {\n                    must_use.set(name_span, name_span)?;\n                }\n                \"coherent\" => {\n                    memory_decorations |= crate::MemoryDecorations::COHERENT;\n                }\n                \"volatile\" => {\n                    memory_decorations |= crate::MemoryDecorations::VOLATILE;\n                }\n                _ => return Err(Box::new(Error::UnknownAttribute(name_span))),\n            }\n        }\n\n        let attrib_span = self.pop_rule_span(lexer);\n        match (bind_group.value, bind_index.value) {\n            (Some(group), Some(index)) => {\n                binding = Some(ast::ResourceBinding {\n                    group,\n                    binding: index,\n                });\n            }\n            (Some(_), None) => {\n                return Err(Box::new(Error::MissingAttribute(\"binding\", attrib_span)))\n            }\n            (None, Some(_)) => return Err(Box::new(Error::MissingAttribute(\"group\", attrib_span))),\n            (None, None) => {}\n        }\n\n        // read item\n        let start = lexer.start_byte_offset();\n        let kind = match lexer.next() {\n            (Token::Separator(';'), _) => {\n                ensure_no_diag_attrs(\n                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition,\n                    diagnostic_filters,\n                )?;\n                None\n            }\n            (Token::Word(word), directive_span) if DirectiveKind::from_ident(word).is_some() => {\n                return Err(Box::new(Error::DirectiveAfterFirstGlobalDecl {\n                    directive_span,\n                }));\n            }\n            (Token::Word(\"struct\"), _) => {\n                ensure_no_diag_attrs(\"`struct`s\".into(), diagnostic_filters)?;\n\n                let name = lexer.next_ident()?;\n\n                let members = self.struct_body(lexer, &mut ctx)?;\n\n                Some(ast::GlobalDeclKind::Struct(ast::Struct {\n                    name,\n                    members,\n                    doc_comments,\n                }))\n            }\n            (Token::Word(\"alias\"), _) => {\n                ensure_no_diag_attrs(\"`alias`es\".into(), diagnostic_filters)?;\n\n                let name = lexer.next_ident()?;\n\n                lexer.expect(Token::Operation('='))?;\n                let ty = self.type_specifier(lexer, &mut ctx)?;\n                lexer.expect(Token::Separator(';'))?;\n                Some(ast::GlobalDeclKind::Type(ast::TypeAlias { name, ty }))\n            }\n            (Token::Word(\"const\"), _) => {\n                ensure_no_diag_attrs(\"`const`s\".into(), diagnostic_filters)?;\n\n                let (name, ty) = self.optionally_typed_ident(lexer, &mut ctx)?;\n\n                lexer.expect(Token::Operation('='))?;\n                let init = self.expression(lexer, &mut ctx)?;\n                lexer.expect(Token::Separator(';'))?;\n\n                Some(ast::GlobalDeclKind::Const(ast::Const {\n                    name,\n                    ty,\n                    init,\n                    doc_comments,\n                }))\n            }\n            (Token::Word(\"override\"), _) => {\n                ensure_no_diag_attrs(\"`override`s\".into(), diagnostic_filters)?;\n\n                let (name, ty) = self.optionally_typed_ident(lexer, &mut ctx)?;\n\n                let init = if lexer.next_if(Token::Operation('=')) {\n                    Some(self.expression(lexer, &mut ctx)?)\n                } else {\n                    None\n                };\n\n                lexer.expect(Token::Separator(';'))?;\n\n                Some(ast::GlobalDeclKind::Override(ast::Override {\n                    name,\n                    id: id.value,\n                    ty,\n                    init,\n                }))\n            }\n            (Token::Word(\"var\"), _) => {\n                ensure_no_diag_attrs(\"`var`s\".into(), diagnostic_filters)?;\n\n                let mut var = self.variable_decl(lexer, &mut ctx)?;\n                var.binding = binding.take();\n                var.doc_comments = doc_comments;\n                var.memory_decorations = memory_decorations;\n                Some(ast::GlobalDeclKind::Var(var))\n            }\n            (Token::Word(\"fn\"), _) => {\n                let diagnostic_filter_leaf = Self::write_diagnostic_filters(\n                    &mut out.diagnostic_filters,\n                    diagnostic_filters,\n                    out.diagnostic_filter_leaf,\n                );\n\n                let function = self.function_decl(\n                    lexer,\n                    diagnostic_filter_leaf,\n                    must_use.value,\n                    out,\n                    &mut dependencies,\n                )?;\n                Some(ast::GlobalDeclKind::Fn(ast::Function {\n                    entry_point: if let Some(stage) = stage.value {\n                        if stage.compute_like() && workgroup_size.value.is_none() {\n                            return Err(Box::new(Error::MissingWorkgroupSize(\n                                shader_stage_error_span,\n                            )));\n                        }\n\n                        match stage {\n                            ShaderStage::AnyHit | ShaderStage::ClosestHit | ShaderStage::Miss => {\n                                if incoming_payload.value.is_none() {\n                                    return Err(Box::new(Error::MissingIncomingPayload(\n                                        shader_stage_error_span,\n                                    )));\n                                }\n                            }\n                            _ => {}\n                        }\n\n                        Some(ast::EntryPoint {\n                            stage,\n                            early_depth_test: early_depth_test.value,\n                            workgroup_size: workgroup_size.value,\n                            mesh_output_variable: mesh_output.value,\n                            task_payload: payload.value,\n                            ray_incoming_payload: incoming_payload.value,\n                        })\n                    } else {\n                        None\n                    },\n                    doc_comments,\n                    ..function\n                }))\n            }\n            (Token::Word(\"const_assert\"), _) => {\n                ensure_no_diag_attrs(\"`const_assert`s\".into(), diagnostic_filters)?;\n\n                // parentheses are optional\n                let paren = lexer.next_if(Token::Paren('('));\n\n                let condition = self.expression(lexer, &mut ctx)?;\n\n                if paren {\n                    lexer.expect(Token::Paren(')'))?;\n                }\n                lexer.expect(Token::Separator(';'))?;\n                Some(ast::GlobalDeclKind::ConstAssert(condition))\n            }\n            (Token::End, _) => return Ok(()),\n            other => {\n                return Err(Box::new(Error::Unexpected(\n                    other.1,\n                    ExpectedToken::GlobalItem,\n                )))\n            }\n        };\n\n        if let Some(kind) = kind {\n            out.decls.append(\n                ast::GlobalDecl { kind, dependencies },\n                lexer.span_from(start),\n            );\n        }\n\n        if !self.rules.is_empty() {\n            log::error!(\"Reached the end of global decl, but rule stack is not empty\");\n            log::error!(\"Rules: {:?}\", self.rules);\n            return Err(Box::new(Error::Internal(\"rule stack is not empty\")));\n        };\n\n        match binding {\n            None => Ok(()),\n            Some(_) => Err(Box::new(Error::Internal(\n                \"we had the attribute but no var?\",\n            ))),\n        }\n    }\n\n    pub fn parse<'a>(\n        &mut self,\n        source: &'a str,\n        options: &Options,\n    ) -> Result<'a, ast::TranslationUnit<'a>> {\n        self.reset();\n\n        let mut lexer = Lexer::new(source, !options.parse_doc_comments);\n        let mut tu = ast::TranslationUnit::default();\n        let mut enable_extensions = EnableExtensions::empty();\n        let mut diagnostic_filters = DiagnosticFilterMap::new();\n\n        // Parse module doc comments.\n        tu.doc_comments = lexer.accumulate_module_doc_comments();\n\n        // Parse directives.\n        while let (Token::Word(word), _) = lexer.peek() {\n            if let Some(kind) = DirectiveKind::from_ident(word) {\n                self.push_rule_span(Rule::Directive, &mut lexer);\n                let _ = lexer.next_ident_with_span().unwrap();\n                match kind {\n                    DirectiveKind::Diagnostic => {\n                        let diagnostic_filter = self.diagnostic_filter(&mut lexer)?;\n                        let span = self.peek_rule_span(&lexer);\n                        diagnostic_filters\n                            .add(diagnostic_filter, span, ShouldConflictOnFullDuplicate::No)\n                            .map_err(|e| Box::new(e.into()))?;\n                        lexer.expect(Token::Separator(';'))?;\n                    }\n                    DirectiveKind::Enable => {\n                        self.directive_ident_list(&mut lexer, |ident, span| {\n                            let kind = EnableExtension::from_ident(ident, span)?;\n                            let extension = match kind {\n                                EnableExtension::Implemented(kind) => kind,\n                                EnableExtension::Unimplemented(kind) => {\n                                    return Err(Box::new(Error::EnableExtensionNotYetImplemented {\n                                        kind,\n                                        span,\n                                    }))\n                                }\n                            };\n                            // Check if the required capability is supported\n                            let required_capability = extension.capability();\n                            if !options.capabilities.contains(required_capability) {\n                                return Err(Box::new(Error::EnableExtensionNotSupported {\n                                    kind,\n                                    span,\n                                }));\n                            }\n                            enable_extensions.add(extension);\n                            Ok(())\n                        })?;\n                    }\n                    DirectiveKind::Requires => {\n                        self.directive_ident_list(&mut lexer, |ident, span| {\n                            match LanguageExtension::from_ident(ident) {\n                                Some(LanguageExtension::Implemented(_kind)) => {\n                                    // NOTE: No further validation is needed for an extension, so\n                                    // just throw parsed information away. If we ever want to apply\n                                    // what we've parsed to diagnostics, maybe we'll want to refer\n                                    // to enabled extensions later?\n                                    Ok(())\n                                }\n                                Some(LanguageExtension::Unimplemented(kind)) => {\n                                    Err(Box::new(Error::LanguageExtensionNotYetImplemented {\n                                        kind,\n                                        span,\n                                    }))\n                                }\n                                None => Err(Box::new(Error::UnknownLanguageExtension(span, ident))),\n                            }\n                        })?;\n                    }\n                }\n                self.pop_rule_span(&lexer);\n            } else {\n                break;\n            }\n        }\n\n        lexer.enable_extensions = enable_extensions;\n        tu.enable_extensions = enable_extensions;\n        tu.diagnostic_filter_leaf =\n            Self::write_diagnostic_filters(&mut tu.diagnostic_filters, diagnostic_filters, None);\n\n        loop {\n            match self.global_decl(&mut lexer, &mut tu) {\n                Err(error) => return Err(error),\n                Ok(()) => {\n                    if lexer.peek().0 == Token::End {\n                        break;\n                    }\n                }\n            }\n        }\n\n        Ok(tu)\n    }\n\n    fn increase_brace_nesting(brace_nesting_level: u8, brace_span: Span) -> Result<'static, u8> {\n        // From [spec.](https://gpuweb.github.io/gpuweb/wgsl/#limits):\n        //\n        // > § 2.4. Limits\n        // >\n        // > …\n        // >\n        // > Maximum nesting depth of brace-enclosed statements in a function[:] 127\n        const BRACE_NESTING_MAXIMUM: u8 = 127;\n        if brace_nesting_level + 1 > BRACE_NESTING_MAXIMUM {\n            return Err(Box::new(Error::ExceededLimitForNestedBraces {\n                span: brace_span,\n                limit: BRACE_NESTING_MAXIMUM,\n            }));\n        }\n        Ok(brace_nesting_level + 1)\n    }\n\n    fn diagnostic_filter<'a>(&self, lexer: &mut Lexer<'a>) -> Result<'a, DiagnosticFilter> {\n        lexer.expect(Token::Paren('('))?;\n\n        let (severity_control_name, severity_control_name_span) = lexer.next_ident_with_span()?;\n        let new_severity = diagnostic_filter::Severity::from_wgsl_ident(severity_control_name)\n            .ok_or(Error::DiagnosticInvalidSeverity {\n                severity_control_name_span,\n            })?;\n\n        lexer.expect(Token::Separator(','))?;\n\n        let (diagnostic_name_token, diagnostic_name_token_span) = lexer.next_ident_with_span()?;\n        let triggering_rule = if lexer.next_if(Token::Separator('.')) {\n            let (ident, _span) = lexer.next_ident_with_span()?;\n            FilterableTriggeringRule::User(Box::new([diagnostic_name_token.into(), ident.into()]))\n        } else {\n            let diagnostic_rule_name = diagnostic_name_token;\n            let diagnostic_rule_name_span = diagnostic_name_token_span;\n            if let Some(triggering_rule) =\n                StandardFilterableTriggeringRule::from_wgsl_ident(diagnostic_rule_name)\n            {\n                FilterableTriggeringRule::Standard(triggering_rule)\n            } else {\n                diagnostic_filter::Severity::Warning.report_wgsl_parse_diag(\n                    Box::new(Error::UnknownDiagnosticRuleName(diagnostic_rule_name_span)),\n                    lexer.source,\n                )?;\n                FilterableTriggeringRule::Unknown(diagnostic_rule_name.into())\n            }\n        };\n        let filter = DiagnosticFilter {\n            triggering_rule,\n            new_severity,\n        };\n        lexer.next_if(Token::Separator(','));\n        lexer.expect(Token::Paren(')'))?;\n\n        Ok(filter)\n    }\n\n    pub(crate) fn write_diagnostic_filters(\n        arena: &mut Arena<DiagnosticFilterNode>,\n        filters: DiagnosticFilterMap,\n        parent: Option<Handle<DiagnosticFilterNode>>,\n    ) -> Option<Handle<DiagnosticFilterNode>> {\n        filters\n            .into_iter()\n            .fold(parent, |parent, (triggering_rule, (new_severity, span))| {\n                Some(arena.append(\n                    DiagnosticFilterNode {\n                        inner: DiagnosticFilter {\n                            new_severity,\n                            triggering_rule,\n                        },\n                        parent,\n                    },\n                    span,\n                ))\n            })\n    }\n}\n\nconst fn is_start_of_compound_statement<'a>(token: Token<'a>) -> bool {\n    matches!(token, Token::Attribute | Token::Paren('{'))\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/parse/number.rs",
    "content": "use alloc::format;\n\nuse crate::front::wgsl::error::NumberError;\nuse crate::front::wgsl::parse::directive::enable_extension::ImplementedEnableExtension;\nuse crate::front::wgsl::parse::lexer::Token;\nuse half::f16;\n\n/// When using this type assume no Abstract Int/Float for now\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum Number {\n    /// Abstract Int (-2^63 ≤ i < 2^63)\n    AbstractInt(i64),\n    /// Abstract Float (IEEE-754 binary64)\n    AbstractFloat(f64),\n    /// Concrete i32\n    I32(i32),\n    /// Concrete u32\n    U32(u32),\n    /// Concrete i64\n    I64(i64),\n    /// Concrete u64\n    U64(u64),\n    /// Concrete f16\n    F16(f16),\n    /// Concrete f32\n    F32(f32),\n    /// Concrete f64\n    F64(f64),\n}\n\nimpl Number {\n    pub(super) const fn requires_enable_extension(&self) -> Option<ImplementedEnableExtension> {\n        match *self {\n            Number::F16(_) => Some(ImplementedEnableExtension::F16),\n            _ => None,\n        }\n    }\n}\n\npub(in crate::front::wgsl) fn consume_number(input: &str) -> (Token<'_>, &str) {\n    let (result, rest) = parse(input);\n    (Token::Number(result), rest)\n}\n\nenum Kind {\n    Int(IntKind),\n    Float(FloatKind),\n}\n\nenum IntKind {\n    I32,\n    U32,\n    I64,\n    U64,\n}\n\n#[derive(Debug)]\nenum FloatKind {\n    F16,\n    F32,\n    F64,\n}\n\n// The following regexes (from the WGSL spec) will be matched:\n\n// int_literal:\n// | / 0                                                                [iu]?   /\n// | / [1-9][0-9]*                                                      [iu]?   /\n// | / 0[xX][0-9a-fA-F]+                                                [iu]?   /\n\n// decimal_float_literal:\n// | / 0                                                                [fh]    /\n// | / [1-9][0-9]*                                                      [fh]    /\n// | / [0-9]*               \\.[0-9]+            ([eE][+-]?[0-9]+)?      [fh]?   /\n// | / [0-9]+               \\.[0-9]*            ([eE][+-]?[0-9]+)?      [fh]?   /\n// | / [0-9]+                                    [eE][+-]?[0-9]+        [fh]?   /\n\n// hex_float_literal:\n// | / 0[xX][0-9a-fA-F]*    \\.[0-9a-fA-F]+      ([pP][+-]?[0-9]+        [fh]?)? /\n// | / 0[xX][0-9a-fA-F]+    \\.[0-9a-fA-F]*      ([pP][+-]?[0-9]+        [fh]?)? /\n// | / 0[xX][0-9a-fA-F]+                         [pP][+-]?[0-9]+        [fh]?   /\n\n// You could visualize the regex below via https://debuggex.com to get a rough idea what `parse` is doing\n// (?:0[xX](?:([0-9a-fA-F]+\\.[0-9a-fA-F]*|[0-9a-fA-F]*\\.[0-9a-fA-F]+)(?:([pP][+-]?[0-9]+)([fh]?))?|([0-9a-fA-F]+)([pP][+-]?[0-9]+)([fh]?)|([0-9a-fA-F]+)([iu]?))|((?:[0-9]+[eE][+-]?[0-9]+|(?:[0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)(?:[eE][+-]?[0-9]+)?))([fh]?)|((?:[0-9]|[1-9][0-9]+))([iufh]?))\n\n// Leading signs are handled as unary operators.\n\nfn parse(input: &str) -> (Result<Number, NumberError>, &str) {\n    /// returns `true` and consumes `X` bytes from the given byte buffer\n    /// if the given `X` nr of patterns are found at the start of the buffer\n    macro_rules! consume {\n        ($bytes:ident, $($pattern:pat),*) => {\n            match $bytes {\n                &[$($pattern),*, ref rest @ ..] => { $bytes = rest; true },\n                _ => false,\n            }\n        };\n    }\n\n    /// consumes one byte from the given byte buffer\n    /// if one of the given patterns are found at the start of the buffer\n    /// returning the corresponding expr for the matched pattern\n    macro_rules! consume_map {\n        ($bytes:ident, [$( $($pattern:pat_param),* => $to:expr),* $(,)?]) => {\n            match $bytes {\n                $( &[ $($pattern),*, ref rest @ ..] => { $bytes = rest; Some($to) }, )*\n                _ => None,\n            }\n        };\n    }\n\n    /// consumes all consecutive bytes matched by the `0-9` pattern from the given byte buffer\n    /// returning the number of consumed bytes\n    macro_rules! consume_dec_digits {\n        ($bytes:ident) => {{\n            let start_len = $bytes.len();\n            while let &[b'0'..=b'9', ref rest @ ..] = $bytes {\n                $bytes = rest;\n            }\n            start_len - $bytes.len()\n        }};\n    }\n\n    /// consumes all consecutive bytes matched by the `0-9 | a-f | A-F` pattern from the given byte buffer\n    /// returning the number of consumed bytes\n    macro_rules! consume_hex_digits {\n        ($bytes:ident) => {{\n            let start_len = $bytes.len();\n            while let &[b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F', ref rest @ ..] = $bytes {\n                $bytes = rest;\n            }\n            start_len - $bytes.len()\n        }};\n    }\n\n    macro_rules! consume_float_suffix {\n        ($bytes:ident) => {\n            consume_map!($bytes, [\n                b'h' => FloatKind::F16,\n                b'f' => FloatKind::F32,\n                b'l', b'f' => FloatKind::F64,\n            ])\n        };\n    }\n\n    /// maps the given `&[u8]` (tail of the initial `input: &str`) to a `&str`\n    macro_rules! rest_to_str {\n        ($bytes:ident) => {\n            &input[input.len() - $bytes.len()..]\n        };\n    }\n\n    struct ExtractSubStr<'a>(&'a str);\n\n    impl<'a> ExtractSubStr<'a> {\n        /// given an `input` and a `start` (tail of the `input`)\n        /// creates a new [`ExtractSubStr`](`Self`)\n        fn start(input: &'a str, start: &'a [u8]) -> Self {\n            let start = input.len() - start.len();\n            Self(&input[start..])\n        }\n        /// given an `end` (tail of the initial `input`)\n        /// returns a substring of `input`\n        fn end(&self, end: &'a [u8]) -> &'a str {\n            let end = self.0.len() - end.len();\n            &self.0[..end]\n        }\n    }\n\n    let mut bytes = input.as_bytes();\n\n    let general_extract = ExtractSubStr::start(input, bytes);\n\n    if consume!(bytes, b'0', b'x' | b'X') {\n        let digits_extract = ExtractSubStr::start(input, bytes);\n\n        let consumed = consume_hex_digits!(bytes);\n\n        if consume!(bytes, b'.') {\n            let consumed_after_period = consume_hex_digits!(bytes);\n\n            if consumed + consumed_after_period == 0 {\n                return (Err(NumberError::Invalid), rest_to_str!(bytes));\n            }\n\n            let significand = general_extract.end(bytes);\n\n            if consume!(bytes, b'p' | b'P') {\n                consume!(bytes, b'+' | b'-');\n                let consumed = consume_dec_digits!(bytes);\n\n                if consumed == 0 {\n                    return (Err(NumberError::Invalid), rest_to_str!(bytes));\n                }\n\n                let number = general_extract.end(bytes);\n\n                let kind = consume_float_suffix!(bytes);\n\n                (parse_hex_float(number, kind), rest_to_str!(bytes))\n            } else {\n                (\n                    parse_hex_float_missing_exponent(significand, None),\n                    rest_to_str!(bytes),\n                )\n            }\n        } else {\n            if consumed == 0 {\n                return (Err(NumberError::Invalid), rest_to_str!(bytes));\n            }\n\n            let significand = general_extract.end(bytes);\n            let digits = digits_extract.end(bytes);\n\n            let exp_extract = ExtractSubStr::start(input, bytes);\n\n            if consume!(bytes, b'p' | b'P') {\n                consume!(bytes, b'+' | b'-');\n                let consumed = consume_dec_digits!(bytes);\n\n                if consumed == 0 {\n                    return (Err(NumberError::Invalid), rest_to_str!(bytes));\n                }\n\n                let exponent = exp_extract.end(bytes);\n\n                let kind = consume_float_suffix!(bytes);\n\n                (\n                    parse_hex_float_missing_period(significand, exponent, kind),\n                    rest_to_str!(bytes),\n                )\n            } else {\n                let kind = consume_map!(bytes, [\n                    b'i' => IntKind::I32,\n                    b'u' => IntKind::U32,\n                    b'l', b'i' => IntKind::I64,\n                    b'l', b'u' => IntKind::U64,\n                ]);\n\n                (parse_hex_int(digits, kind), rest_to_str!(bytes))\n            }\n        }\n    } else {\n        let is_first_zero = bytes.first() == Some(&b'0');\n\n        let consumed = consume_dec_digits!(bytes);\n\n        if consume!(bytes, b'.') {\n            let consumed_after_period = consume_dec_digits!(bytes);\n\n            if consumed + consumed_after_period == 0 {\n                return (Err(NumberError::Invalid), rest_to_str!(bytes));\n            }\n\n            if consume!(bytes, b'e' | b'E') {\n                consume!(bytes, b'+' | b'-');\n                let consumed = consume_dec_digits!(bytes);\n\n                if consumed == 0 {\n                    return (Err(NumberError::Invalid), rest_to_str!(bytes));\n                }\n            }\n\n            let number = general_extract.end(bytes);\n\n            let kind = consume_float_suffix!(bytes);\n\n            (parse_dec_float(number, kind), rest_to_str!(bytes))\n        } else {\n            if consumed == 0 {\n                return (Err(NumberError::Invalid), rest_to_str!(bytes));\n            }\n\n            if consume!(bytes, b'e' | b'E') {\n                consume!(bytes, b'+' | b'-');\n                let consumed = consume_dec_digits!(bytes);\n\n                if consumed == 0 {\n                    return (Err(NumberError::Invalid), rest_to_str!(bytes));\n                }\n\n                let number = general_extract.end(bytes);\n\n                let kind = consume_float_suffix!(bytes);\n\n                (parse_dec_float(number, kind), rest_to_str!(bytes))\n            } else {\n                // make sure the multi-digit numbers don't start with zero\n                if consumed > 1 && is_first_zero {\n                    return (Err(NumberError::Invalid), rest_to_str!(bytes));\n                }\n\n                let digits = general_extract.end(bytes);\n\n                let kind = consume_map!(bytes, [\n                    b'i' => Kind::Int(IntKind::I32),\n                    b'u' => Kind::Int(IntKind::U32),\n                    b'l', b'i' => Kind::Int(IntKind::I64),\n                    b'l', b'u' => Kind::Int(IntKind::U64),\n                    b'h' => Kind::Float(FloatKind::F16),\n                    b'f' => Kind::Float(FloatKind::F32),\n                    b'l', b'f' => Kind::Float(FloatKind::F64),\n                ]);\n\n                (parse_dec(digits, kind), rest_to_str!(bytes))\n            }\n        }\n    }\n}\n\nfn parse_hex_float_missing_exponent(\n    // format: 0[xX] ( [0-9a-fA-F]+\\.[0-9a-fA-F]* | [0-9a-fA-F]*\\.[0-9a-fA-F]+ )\n    significand: &str,\n    kind: Option<FloatKind>,\n) -> Result<Number, NumberError> {\n    let hexf_input = format!(\"{}{}\", significand, \"p0\");\n    parse_hex_float(&hexf_input, kind)\n}\n\nfn parse_hex_float_missing_period(\n    // format: 0[xX] [0-9a-fA-F]+\n    significand: &str,\n    // format: [pP][+-]?[0-9]+\n    exponent: &str,\n    kind: Option<FloatKind>,\n) -> Result<Number, NumberError> {\n    let hexf_input = format!(\"{significand}.{exponent}\");\n    parse_hex_float(&hexf_input, kind)\n}\n\nfn parse_hex_int(\n    // format: [0-9a-fA-F]+\n    digits: &str,\n    kind: Option<IntKind>,\n) -> Result<Number, NumberError> {\n    parse_int(digits, kind, 16)\n}\n\nfn parse_dec(\n    // format: ( [0-9] | [1-9][0-9]+ )\n    digits: &str,\n    kind: Option<Kind>,\n) -> Result<Number, NumberError> {\n    match kind {\n        None => parse_int(digits, None, 10),\n        Some(Kind::Int(kind)) => parse_int(digits, Some(kind), 10),\n        Some(Kind::Float(kind)) => parse_dec_float(digits, Some(kind)),\n    }\n}\n\n// Float parsing notes\n\n// The following chapters of IEEE 754-2019 are relevant:\n//\n// 7.4 Overflow (largest finite number is exceeded by what would have been\n//     the rounded floating-point result were the exponent range unbounded)\n//\n// 7.5 Underflow (tiny non-zero result is detected;\n//     for decimal formats tininess is detected before rounding when a non-zero result\n//     computed as though both the exponent range and the precision were unbounded\n//     would lie strictly between 2^−126)\n//\n// 7.6 Inexact (rounded result differs from what would have been computed\n//     were both exponent range and precision unbounded)\n\n// The WGSL spec requires us to error:\n//   on overflow for decimal floating point literals\n//   on overflow and inexact for hexadecimal floating point literals\n// (underflow is not mentioned)\n\n// hexf_parse errors on overflow, underflow, inexact\n// rust std lib float from str handles overflow, underflow, inexact transparently (rounds and will not error)\n\n// Therefore we only check for overflow manually for decimal floating point literals\n\n// input format: 0[xX] ( [0-9a-fA-F]+\\.[0-9a-fA-F]* | [0-9a-fA-F]*\\.[0-9a-fA-F]+ ) [pP][+-]?[0-9]+\nfn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {\n    match kind {\n        None => match hexf_parse::parse_hexf64(input, false) {\n            Ok(num) => Ok(Number::AbstractFloat(num)),\n            // can only be ParseHexfErrorKind::Inexact but we can't check since it's private\n            _ => Err(NumberError::NotRepresentable),\n        },\n        // TODO: f16 is not supported by hexf_parse\n        Some(FloatKind::F16) => Err(NumberError::NotRepresentable),\n        Some(FloatKind::F32) => match hexf_parse::parse_hexf32(input, false) {\n            Ok(num) => Ok(Number::F32(num)),\n            // can only be ParseHexfErrorKind::Inexact but we can't check since it's private\n            _ => Err(NumberError::NotRepresentable),\n        },\n        Some(FloatKind::F64) => match hexf_parse::parse_hexf64(input, false) {\n            Ok(num) => Ok(Number::F64(num)),\n            // can only be ParseHexfErrorKind::Inexact but we can't check since it's private\n            _ => Err(NumberError::NotRepresentable),\n        },\n    }\n}\n\n// input format: ( [0-9]+\\.[0-9]* | [0-9]*\\.[0-9]+ ) ([eE][+-]?[0-9]+)?\n//             | [0-9]+ [eE][+-]?[0-9]+\nfn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {\n    match kind {\n        None => {\n            let num = input.parse::<f64>().unwrap(); // will never fail\n            num.is_finite()\n                .then_some(Number::AbstractFloat(num))\n                .ok_or(NumberError::NotRepresentable)\n        }\n        Some(FloatKind::F32) => {\n            let num = input.parse::<f32>().unwrap(); // will never fail\n            num.is_finite()\n                .then_some(Number::F32(num))\n                .ok_or(NumberError::NotRepresentable)\n        }\n        Some(FloatKind::F64) => {\n            let num = input.parse::<f64>().unwrap(); // will never fail\n            num.is_finite()\n                .then_some(Number::F64(num))\n                .ok_or(NumberError::NotRepresentable)\n        }\n        Some(FloatKind::F16) => {\n            let num = input.parse::<f16>().unwrap(); // will never fail\n            num.is_finite()\n                .then_some(Number::F16(num))\n                .ok_or(NumberError::NotRepresentable)\n        }\n    }\n}\n\nfn parse_int(input: &str, kind: Option<IntKind>, radix: u32) -> Result<Number, NumberError> {\n    fn map_err(e: core::num::ParseIntError) -> NumberError {\n        match *e.kind() {\n            core::num::IntErrorKind::PosOverflow | core::num::IntErrorKind::NegOverflow => {\n                NumberError::NotRepresentable\n            }\n            _ => unreachable!(),\n        }\n    }\n    match kind {\n        None => match i64::from_str_radix(input, radix) {\n            Ok(num) => Ok(Number::AbstractInt(num)),\n            Err(e) => Err(map_err(e)),\n        },\n        Some(IntKind::I32) => match i32::from_str_radix(input, radix) {\n            Ok(num) => Ok(Number::I32(num)),\n            Err(e) => Err(map_err(e)),\n        },\n        Some(IntKind::U32) => match u32::from_str_radix(input, radix) {\n            Ok(num) => Ok(Number::U32(num)),\n            Err(e) => Err(map_err(e)),\n        },\n        Some(IntKind::I64) => match i64::from_str_radix(input, radix) {\n            Ok(num) => Ok(Number::I64(num)),\n            Err(e) => Err(map_err(e)),\n        },\n        Some(IntKind::U64) => match u64::from_str_radix(input, radix) {\n            Ok(num) => Ok(Number::U64(num)),\n            Err(e) => Err(map_err(e)),\n        },\n    }\n}\n"
  },
  {
    "path": "naga/src/front/wgsl/tests.rs",
    "content": "use alloc::format;\n\nuse super::parse_str;\n\n#[test]\nfn parse_comment() {\n    parse_str(\n        \"//\n        ////\n        ///////////////////////////////////////////////////////// asda\n        //////////////////// dad ////////// /\n        /////////////////////////////////////////////////////////////////////////////////////////////////////\n        //\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_types() {\n    parse_str(\"const a : i32 = 2;\").unwrap();\n    parse_str(\"const a : u64 = 2lu;\").unwrap();\n    assert!(parse_str(\"const a : x32 = 2;\").is_err());\n    parse_str(\"var t: texture_2d<f32>;\").unwrap();\n    parse_str(\"var t: texture_cube_array<i32>;\").unwrap();\n    parse_str(\"var t: texture_multisampled_2d<u32>;\").unwrap();\n    parse_str(\"var t: texture_storage_1d<rgba8uint,write>;\").unwrap();\n    parse_str(\"var t: texture_storage_3d<r32float,read>;\").unwrap();\n}\n\n#[test]\nfn parse_type_inference() {\n    parse_str(\n        \"\n        fn foo() {\n            let a = 2u;\n            let b: u32 = a;\n            var x = 3.;\n            var y = vec2<f32>(1, 2);\n        }\",\n    )\n    .unwrap();\n    assert!(parse_str(\n        \"\n        fn foo() { let c : i32 = 2.0; }\",\n    )\n    .is_err());\n}\n\n#[test]\nfn parse_type_cast() {\n    parse_str(\n        \"\n        const a : i32 = 2;\n        fn main() {\n            var x: f32 = f32(a);\n            x = f32(i32(a + 1) / 2);\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            let x: vec2<f32> = vec2<f32>(1.0, 2.0);\n            let y: vec2<u32> = vec2<u32>(x);\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            let x: vec2<f32> = vec2<f32>(0.0);\n        }\n    \",\n    )\n    .unwrap();\n    assert!(parse_str(\n        \"\n        fn main() {\n            let x: vec2<i32> = vec2<i32>(0.0, 0.0);\n        }\n    \",\n    )\n    .is_err());\n}\n\n#[test]\nfn parse_type_coercion() {\n    parse_str(\n        \"\n        fn foo(bar: f32) {}\n        fn main() {\n            foo(0);\n        }\n    \",\n    )\n    .unwrap();\n    assert!(parse_str(\n        \"\n        fn foo(bar: i32) {}\n        fn main() {\n            foo(0.0);\n        }\n    \",\n    )\n    .is_err());\n}\n\n#[test]\nfn parse_struct() {\n    parse_str(\n        \"\n        struct Foo { x: i32 }\n        struct Bar {\n            @size(16) x: vec2<i32>,\n            @align(16) y: f32,\n            @size(32) @align(128) z: vec3<f32>,\n        };\n        struct Empty {}\n        var<storage,read_write> s: Foo;\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_standard_fun() {\n    parse_str(\n        \"\n        fn main() {\n            var x: i32 = min(max(1, 2), 3);\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_statement() {\n    parse_str(\n        \"\n        fn main() {\n            ;\n            {}\n            {;}\n        }\n    \",\n    )\n    .unwrap();\n\n    parse_str(\n        \"\n        fn foo() {}\n        fn bar() { foo(); }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_if() {\n    parse_str(\n        \"\n        fn main() {\n            if true {\n                discard;\n            } else {}\n            if 0 != 1 {}\n            if false {\n                return;\n            } else if true {\n                return;\n            } else {}\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_parentheses_if() {\n    parse_str(\n        \"\n        fn main() {\n            if (true) {\n                discard;\n            } else {}\n            if (0 != 1) {}\n            if (false) {\n                return;\n            } else if (true) {\n                return;\n            } else {}\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_loop() {\n    parse_str(\n        \"\n        fn main() {\n            var i: i32 = 0;\n            loop {\n                if i == 1 { break; }\n                continuing { i = 1; }\n            }\n            loop {\n                if i == 0 { continue; }\n                break;\n            }\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            var found: bool = false;\n            var i: i32 = 0;\n            while !found {\n                if i == 10 {\n                    found = true;\n                }\n\n                i = i + 1;\n            }\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            while true {\n                break;\n            }\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            var a: i32 = 0;\n            for(var i: i32 = 0; i < 4; i = i + 1) {\n                a = a + 2;\n            }\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn main() {\n            for(;;) {\n                break;\n            }\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_switch() {\n    parse_str(\n        \"\n        fn main() {\n            var pos: f32;\n            switch (3) {\n                case 0, 1: { pos = 0.0; }\n                case 2: { pos = 1.0; }\n                default: { pos = 3.0; }\n            }\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_switch_optional_colon_in_case() {\n    parse_str(\n        \"\n        fn main() {\n            var pos: f32;\n            switch (3) {\n                case 0, 1 { pos = 0.0; }\n                case 2 { pos = 1.0; }\n                default { pos = 3.0; }\n            }\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_switch_default_in_case() {\n    parse_str(\n        \"\n        fn main() {\n            var pos: f32;\n            switch (3) {\n                case 0, 1: { pos = 0.0; }\n                case 2: {}\n                case default, 3: { pos = 3.0; }\n            }\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_parentheses_switch() {\n    parse_str(\n        \"\n        fn main() {\n            var pos: i32;\n            switch pos + 1 {\n                default: { pos = 3; }\n            }\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_texture_load() {\n    parse_str(\n        \"\n        var t: texture_3d<u32>;\n        fn foo() {\n            let r: vec4<u32> = textureLoad(t, vec3<u32>(0u, 1u, 2u), 1);\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        var t: texture_2d_array<i32>;\n        fn foo() {\n            let r: vec4<i32> = textureLoad(t, vec2<i32>(10, 20), 2, 3);\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        var t: texture_storage_1d<r32float,read>;\n        fn foo() {\n            let r: vec4<f32> = textureLoad(t, 10);\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_texture_store() {\n    parse_str(\n        \"\n        var t: texture_storage_2d<rgba8unorm,write>;\n        fn foo() {\n            textureStore(t, vec2<i32>(10, 20), vec4<f32>(0.0, 1.0, 2.0, 3.0));\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_texture_query() {\n    parse_str(\n        \"\n        var t: texture_multisampled_2d<f32>;\n        fn foo() {\n            let dim = textureDimensions(t);\n            let samples = textureNumSamples(t);\n        }\n    \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        var t: texture_2d_array<f32>;\n        fn foo() {\n            let dim = textureDimensions(t);\n            let levels = textureNumLevels(t);\n            let layers = textureNumLayers(t);\n        }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_postfix() {\n    parse_str(\n        \"fn foo() {\n        let x: f32 = vec4<f32>(1.0, 2.0, 3.0, 4.0).xyz.rgbr.aaaa.wz.g;\n        let y: f32 = fract(vec2<f32>(0.5, x)).x;\n    }\",\n    )\n    .unwrap();\n\n    let err = parse_str(\n        \"fn foo() {\n        let v = mat4x4<f32>().x;\n    }\",\n    )\n    .unwrap_err();\n    assert_eq!(err.message(), \"invalid field accessor `x`\");\n}\n\n#[test]\nfn parse_expressions() {\n    parse_str(\"fn foo() {\n        let x: f32 = select(0.0, 1.0, true);\n        let y: vec2<f32> = select(vec2<f32>(1.0, 1.0), vec2<f32>(x, x), vec2<bool>((x < 0.5), (x > 0.5)));\n        let z: bool = !(0.0 == 1.0);\n    }\").unwrap();\n}\n\n#[test]\nfn parse_assignment_statements() {\n    parse_str(\n        \"\n        struct Foo { x: i32 };\n\n        fn foo() {\n            var x: u32 = 0u;\n            x++;\n            x--;\n            x = 1u;\n            x += 1u;\n            var v: vec2<f32> = vec2<f32>(1.0, 1.0);\n            v[0] += 1.0;\n            (v)[0] += 1.0;\n            var s: Foo = Foo(0);\n            s.x -= 1;\n            (s.x) -= 1;\n            (s).x -= 1;\n            _ = 5u;\n    }\",\n    )\n    .unwrap();\n\n    let error = parse_str(\n        \"fn foo() {\n        x|x++;\n    }\",\n    )\n    .unwrap_err();\n    assert_eq!(\n        error.message(),\n        \"expected assignment or increment/decrement, found \\\"|\\\"\",\n    );\n}\n\n#[test]\nfn parse_local_var_address_space() {\n    parse_str(\n        \"\n        fn foo() {\n            var<function> a = true;\n            var<function> b: i32 = 5;\n            var c = 10;\n        }\",\n    )\n    .unwrap();\n\n    let error = parse_str(\n        \"fn foo() {\n            var<private> x: i32 = 5;\n        }\",\n    )\n    .unwrap_err();\n    assert_eq!(\n        error.message(),\n        \"invalid address space for local variable: `private`\",\n    );\n\n    let error = parse_str(\n        \"fn foo() {\n            var<storage> x: i32 = 5;\n        }\",\n    )\n    .unwrap_err();\n    assert_eq!(\n        error.message(),\n        \"invalid address space for local variable: `storage`\",\n    );\n}\n\n#[test]\nfn binary_expression_mixed_scalar_and_vector_operands() {\n    for (operand, expect_splat) in [\n        ('<', false),\n        ('>', false),\n        ('&', false),\n        ('|', false),\n        ('+', true),\n        ('-', true),\n        ('*', false),\n        ('/', true),\n        ('%', true),\n    ] {\n        let module = parse_str(&format!(\n            \"\n            @fragment\n            fn main(@location(0) some_vec: vec3<f32>) -> @location(0) vec4<f32> {{\n                if (all(1.0 {operand} some_vec)) {{\n                    return vec4(0.0);\n                }}\n                return vec4(1.0);\n            }}\n            \"\n        ))\n        .unwrap();\n\n        let expressions = &&module.entry_points[0].function.expressions;\n\n        let found_expressions = expressions\n            .iter()\n            .filter(|&(_, e)| {\n                if let crate::Expression::Binary { left, .. } = *e {\n                    matches!(\n                        (expect_splat, &expressions[left]),\n                        (false, &crate::Expression::Literal(crate::Literal::F32(..)))\n                            | (true, &crate::Expression::Splat { .. })\n                    )\n                } else {\n                    false\n                }\n            })\n            .count();\n\n        assert_eq!(\n            found_expressions,\n            1,\n            \"expected `{operand}` expression {} splat\",\n            if expect_splat { \"with\" } else { \"without\" }\n        );\n    }\n\n    let module = parse_str(\n        \"@fragment\n        fn main(mat: mat3x3<f32>) {\n            let vec = vec3<f32>(1.0, 1.0, 1.0);\n            let result = mat / vec;\n        }\",\n    )\n    .unwrap();\n    let expressions = &&module.entry_points[0].function.expressions;\n    let found_splat = expressions.iter().any(|(_, e)| {\n        if let crate::Expression::Binary { left, .. } = *e {\n            matches!(&expressions[left], &crate::Expression::Splat { .. })\n        } else {\n            false\n        }\n    });\n    assert!(!found_splat, \"'mat / vec' should not be splatted\");\n}\n\n#[test]\nfn parse_pointers() {\n    parse_str(\n        \"fn foo(a: ptr<function, f32>) -> f32 { return *a; }\n    fn bar() {\n        var x: f32 = 1.0;\n        let px = &x;\n        let py = foo(px);\n    }\",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_struct_instantiation() {\n    parse_str(\n        \"\n    struct Foo {\n        a: f32,\n        b: vec3<f32>,\n    }\n\n    @fragment\n    fn fs_main() {\n        var foo: Foo = Foo(0.0, vec3<f32>(0.0, 1.0, 42.0));\n    }\n    \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_array_length() {\n    parse_str(\n        \"\n        struct Foo {\n            data: array<u32>\n        } // this is used as both input and output for convenience\n\n        @group(0) @binding(0)\n        var<storage> foo: Foo;\n\n        @group(0) @binding(1)\n        var<storage> bar: array<u32>;\n\n        fn baz() {\n            var x: u32 = arrayLength(foo.data);\n            var y: u32 = arrayLength(bar);\n        }\n        \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_storage_buffers() {\n    parse_str(\n        \"\n        @group(0) @binding(0)\n        var<storage> foo: array<u32>;\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        @group(0) @binding(0)\n        var<storage,read> foo: array<u32>;\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        @group(0) @binding(0)\n        var<storage,write> foo: array<u32>;\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        @group(0) @binding(0)\n        var<storage,read_write> foo: array<u32>;\n        \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_alias() {\n    parse_str(\n        \"\n        alias Vec4 = vec4<f32>;\n        \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn shadowing_predeclared_types() {\n    parse_str(\n        \"\n        fn test(f32: vec2f) -> vec2f { return f32; }\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        fn test(vec2: vec2f) -> vec2f { return vec2; }\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        alias vec2f = vec2u;\n        fn test(v: vec2f) -> vec2u { return v; }\n        \",\n    )\n    .unwrap();\n    parse_str(\n        \"\n        struct vec2f { inner: vec2<f32> };\n        fn test(v: vec2f) -> vec2<f32> { return v.inner; }\n        \",\n    )\n    .unwrap();\n}\n\n#[test]\nfn parse_texture_load_store_expecting_four_args() {\n    for (func, texture) in [\n        (\n            \"textureStore\",\n            \"texture_storage_2d_array<rg11b10ufloat, write>\",\n        ),\n        (\"textureLoad\", \"texture_2d_array<i32>\"),\n    ] {\n        let error = parse_str(&format!(\n            \"\n            @group(0) @binding(0) var tex_los_res: {texture};\n            @compute\n            @workgroup_size(1)\n            fn main(@builtin(global_invocation_id) id: vec3<u32>) {{\n                var color = vec4(1, 1, 1, 1);\n                {func}(tex_los_res, id, color);\n            }}\n            \"\n        ))\n        .unwrap_err();\n        assert_eq!(\n            error.message(),\n            \"wrong number of arguments: expected 4, found 3\"\n        );\n    }\n}\n\n#[test]\nfn parse_repeated_attributes() {\n    use crate::{\n        front::wgsl::{error::Error, Frontend},\n        Span,\n    };\n\n    let template_vs = \"@vertex fn vs() -> __REPLACE__ vec4<f32> { return vec4<f32>(0.0); }\";\n    let template_struct = \"struct A { __REPLACE__ data: vec3<f32> }\";\n    let template_resource = \"__REPLACE__ var tex_los_res: texture_2d_array<i32>;\";\n    let template_stage = \"__REPLACE__ fn vs() -> vec4<f32> { return vec4<f32>(0.0); }\";\n    for (attribute, template) in [\n        (\"align(16)\", template_struct),\n        (\"binding(0)\", template_resource),\n        (\"builtin(position)\", template_vs),\n        (\"compute\", template_stage),\n        (\"fragment\", template_stage),\n        (\"group(0)\", template_resource),\n        (\"interpolate(flat)\", template_vs),\n        (\"invariant\", template_vs),\n        (\"location(0)\", template_vs),\n        (\"size(16)\", template_struct),\n        (\"vertex\", template_stage),\n        (\"early_depth_test(less_equal)\", template_resource),\n        (\"workgroup_size(1)\", template_stage),\n    ] {\n        let shader = template.replace(\"__REPLACE__\", &format!(\"@{attribute} @{attribute}\"));\n        let name_length = attribute.rfind('(').unwrap_or(attribute.len()) as u32;\n        let span_start = shader.rfind(attribute).unwrap() as u32;\n        let span_end = span_start + name_length;\n        let expected_span = Span::new(span_start, span_end);\n\n        let result = Frontend::new().inner(&shader);\n        assert!(matches!(\n            *result.unwrap_err(),\n            Error::RepeatedAttribute(span) if span == expected_span\n        ));\n    }\n}\n\n#[test]\nfn parse_missing_workgroup_size() {\n    use crate::{\n        front::wgsl::{error::Error, Frontend},\n        Span,\n    };\n\n    let shader = \"@compute fn vs() -> vec4<f32> { return vec4<f32>(0.0); }\";\n    let result = Frontend::new().inner(shader);\n    assert!(matches!(\n        *result.unwrap_err(),\n        Error::MissingWorkgroupSize(span) if span == Span::new(1, 8)\n    ));\n}\n\nmod diagnostic_filter {\n    use crate::front::wgsl::assert_parse_err;\n\n    #[test]\n    fn intended_global_directive() {\n        let shader = \"@diagnostic(off, my.lint);\";\n        assert_parse_err(\n            shader,\n            \"\\\nerror: `@diagnostic(…)` attribute(s) on semicolons are not supported\n  ┌─ wgsl:1:1\n  │\n1 │ @diagnostic(off, my.lint);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^\n  │\n  = note: `@diagnostic(…)` attributes are only permitted on `fn`s, some statements, and `switch`/`loop` bodies.\n  = note: If you meant to declare a diagnostic filter that applies to the entire module, move this line to the top of the file and remove the `@` symbol.\n\n\"\n        );\n    }\n\n    mod parse_sites_not_yet_supported {\n        use crate::front::wgsl::assert_parse_err;\n\n        #[test]\n        fn user_rules() {\n            let shader = \"\nfn myfunc() {\n    if (true) @diagnostic(off, my.lint) {\n        //    ^^^^^^^^^^^^^^^^^^^^^^^^^ not yet supported, should report an error\n    }\n}\n\";\n            assert_parse_err(shader, \"\\\nerror: `@diagnostic(…)` attribute(s) not yet implemented\n  ┌─ wgsl:3:15\n  │\n3 │     if (true) @diagnostic(off, my.lint) {\n  │               ^^^^^^^^^^^^^^^^^^^^^^^^^ can't use this on compound statements (yet)\n  │\n  = note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it!\n\n\");\n        }\n\n        #[test]\n        fn unknown_rules() {\n            let shader = \"\nfn myfunc() {\n\tif (true) @diagnostic(off, wat_is_this) {\n\t\t//    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ should emit a warning\n\t}\n}\n\";\n            assert_parse_err(shader, \"\\\nerror: `@diagnostic(…)` attribute(s) not yet implemented\n  ┌─ wgsl:3:12\n  │\n3 │     if (true) @diagnostic(off, wat_is_this) {\n  │               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't use this on compound statements (yet)\n  │\n  = note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it!\n\n\");\n        }\n    }\n\n    mod directive_conflict {\n        use crate::front::wgsl::assert_parse_err;\n\n        #[test]\n        fn user_rules() {\n            let shader = \"\ndiagnostic(off, my.lint);\ndiagnostic(warning, my.lint);\n\";\n            assert_parse_err(shader, \"\\\nerror: found conflicting `diagnostic(…)` rule(s)\n  ┌─ wgsl:2:1\n  │\n2 │ diagnostic(off, my.lint);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^ first rule\n3 │ diagnostic(warning, my.lint);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second rule\n  │\n  = note: Multiple `diagnostic(…)` rules with the same rule name conflict unless they are directives and the severity is the same.\n  = note: You should delete the rule you don't want.\n\n\");\n        }\n\n        #[test]\n        fn unknown_rules() {\n            let shader = \"\ndiagnostic(off, wat_is_this);\ndiagnostic(warning, wat_is_this);\n\";\n            assert_parse_err(shader, \"\\\nerror: found conflicting `diagnostic(…)` rule(s)\n  ┌─ wgsl:2:1\n  │\n2 │ diagnostic(off, wat_is_this);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first rule\n3 │ diagnostic(warning, wat_is_this);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second rule\n  │\n  = note: Multiple `diagnostic(…)` rules with the same rule name conflict unless they are directives and the severity is the same.\n  = note: You should delete the rule you don't want.\n\n\");\n        }\n    }\n\n    mod attribute_conflict {\n        use crate::front::wgsl::assert_parse_err;\n\n        #[test]\n        fn user_rules() {\n            let shader = \"\ndiagnostic(off, my.lint);\ndiagnostic(warning, my.lint);\n\";\n            assert_parse_err(shader, \"\\\nerror: found conflicting `diagnostic(…)` rule(s)\n  ┌─ wgsl:2:1\n  │\n2 │ diagnostic(off, my.lint);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^ first rule\n3 │ diagnostic(warning, my.lint);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second rule\n  │\n  = note: Multiple `diagnostic(…)` rules with the same rule name conflict unless they are directives and the severity is the same.\n  = note: You should delete the rule you don't want.\n\n\");\n        }\n\n        #[test]\n        fn unknown_rules() {\n            let shader = \"\ndiagnostic(off, wat_is_this);\ndiagnostic(warning, wat_is_this);\n\";\n            assert_parse_err(shader, \"\\\nerror: found conflicting `diagnostic(…)` rule(s)\n  ┌─ wgsl:2:1\n  │\n2 │ diagnostic(off, wat_is_this);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first rule\n3 │ diagnostic(warning, wat_is_this);\n  │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second rule\n  │\n  = note: Multiple `diagnostic(…)` rules with the same rule name conflict unless they are directives and the severity is the same.\n  = note: You should delete the rule you don't want.\n\n\");\n        }\n    }\n}\n\nmod template {\n    use crate::front::wgsl::assert_parse_err;\n\n    #[test]\n    fn missing_template_end() {\n        assert_parse_err(\n            \"\nfn storage() {}\nvar<storage\n\",\n            \"\\\nerror: expected identifier, found \\\"<\\\"\n  ┌─ wgsl:3:4\n  │\n3 │ var<storage\n  │    ^ expected identifier\n\n\",\n        );\n    }\n\n    #[test]\n    fn enumerant_shadowing() {\n        assert_parse_err(\n            \"\nfn storage() {}\nvar<storage> s: u32;\n\",\n            \"\\\nerror: identifier `storage` resolves to a declaration\n  ┌─ wgsl:3:5\n  │\n3 │ var<storage> s: u32;\n  │     ^^^^^^^ needs to resolve to a predeclared enumerant\n\n\",\n        );\n    }\n\n    #[test]\n    fn unexpected_expr_as_enumerant() {\n        assert_parse_err(\n            \"\nvar<1 + 1> s: u32;\n\",\n            \"\\\nerror: unexpected expression\n  ┌─ wgsl:2:5\n  │\n2 │ var<1 + 1> s: u32;\n  │     ^^^^^ needs to be an identifier resolving to a predeclared enumerant\n\n\",\n        );\n    }\n\n    #[test]\n    fn unused_exprs_for_template() {\n        assert_parse_err(\n            \"\nvar<storage, read_write, extra0, extra1> s: u32;\n\",\n            \"\\\nerror: unused expressions for template\n  ┌─ wgsl:2:26\n  │\n2 │ var<storage, read_write, extra0, extra1> s: u32;\n  │                          ^^^^^^  ^^^^^^ unused\n  │                          │\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\n  │                          unused\n\n\",\n        );\n    }\n\n    #[test]\n    fn unused_template_list_for_fn() {\n        assert_parse_err(\n            \"\nfn inner_test() {}\nfn test() {\n    inner_test<unused_template_arg>();\n}\n\",\n            \"\\\nerror: unused expressions for template\n  ┌─ wgsl:4:16\n  │\n4 │     inner_test<unused_template_arg>();\n  │                ^^^^^^^^^^^^^^^^^^^ unused\n\n\",\n        );\n    }\n\n    #[test]\n    fn unused_template_list_for_struct() {\n        assert_parse_err(\n            \"\nstruct test_struct {}\nfn test() {\n    _ = test_struct<unused_template_arg>();\n}\n\",\n            \"\\\nerror: unused expressions for template\n  ┌─ wgsl:4:21\n  │\n4 │     _ = test_struct<unused_template_arg>();\n  │                     ^^^^^^^^^^^^^^^^^^^ unused\n\n\",\n        );\n    }\n\n    #[test]\n    fn unused_template_list_for_alias() {\n        assert_parse_err(\n            \"\nalias test_alias = f32;\nfn test() {\n    _ = test_alias<unused_template_arg>();\n}\n\",\n            \"\\\nerror: unused expressions for template\n  ┌─ wgsl:4:20\n  │\n4 │     _ = test_alias<unused_template_arg>();\n  │                    ^^^^^^^^^^^^^^^^^^^ unused\n\n\",\n        );\n    }\n\n    #[test]\n    fn unexpected_template() {\n        assert_parse_err(\n            \"\nfn vertex() -> vec4<f32> {\n    return vec4<f32>;\n}\n\",\n            \"\\\nerror: unexpected template\n  ┌─ wgsl:3:12\n  │\n3 │     return vec4<f32>;\n  │            ^^^^^^^^^ expected identifier\n\n\",\n        );\n    }\n\n    #[test]\n    fn expected_template_arg() {\n        assert_parse_err(\n            \"\nfn test() {\n    bitcast(8);\n}\n\",\n            \"\\\nerror: `bitcast` needs a template argument specified: `T`, a type\n  ┌─ wgsl:3:5\n  │\n3 │     bitcast(8);\n  │     ^^^^^^^ is missing a template argument\n\n\",\n        );\n    }\n}\n"
  },
  {
    "path": "naga/src/ir/block.rs",
    "content": "use alloc::vec::Vec;\nuse core::ops::{Deref, DerefMut, RangeBounds};\n\nuse crate::{Span, Statement};\n\n/// A code block is a vector of statements, with maybe a vector of spans.\n#[derive(Debug, Clone, Default)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"serialize\", serde(transparent))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub struct Block {\n    body: Vec<Statement>,\n    #[cfg_attr(feature = \"serialize\", serde(skip))]\n    span_info: Vec<Span>,\n}\n\nimpl Block {\n    pub const fn new() -> Self {\n        Self {\n            body: Vec::new(),\n            span_info: Vec::new(),\n        }\n    }\n\n    pub fn from_vec(body: Vec<Statement>) -> Self {\n        let span_info = core::iter::repeat_n(Span::default(), body.len()).collect();\n        Self { body, span_info }\n    }\n\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            body: Vec::with_capacity(capacity),\n            span_info: Vec::with_capacity(capacity),\n        }\n    }\n\n    #[allow(unused_variables)]\n    pub fn push(&mut self, end: Statement, span: Span) {\n        self.body.push(end);\n        self.span_info.push(span);\n    }\n\n    pub fn extend(&mut self, item: Option<(Statement, Span)>) {\n        if let Some((end, span)) = item {\n            self.push(end, span)\n        }\n    }\n\n    pub fn extend_block(&mut self, other: Self) {\n        self.span_info.extend(other.span_info);\n        self.body.extend(other.body);\n    }\n\n    pub fn append(&mut self, other: &mut Self) {\n        self.span_info.append(&mut other.span_info);\n        self.body.append(&mut other.body);\n    }\n\n    pub fn cull<R: RangeBounds<usize> + Clone>(&mut self, range: R) {\n        self.span_info.drain(range.clone());\n        self.body.drain(range);\n    }\n\n    pub fn splice<R: RangeBounds<usize> + Clone>(&mut self, range: R, other: Self) {\n        self.span_info.splice(range.clone(), other.span_info);\n        self.body.splice(range, other.body);\n    }\n\n    pub fn span_into_iter(self) -> impl Iterator<Item = (Statement, Span)> {\n        let Block { body, span_info } = self;\n        body.into_iter().zip(span_info)\n    }\n\n    pub fn span_iter(&self) -> impl Iterator<Item = (&Statement, &Span)> {\n        let span_iter = self.span_info.iter();\n        self.body.iter().zip(span_iter)\n    }\n\n    pub fn span_iter_mut(&mut self) -> impl Iterator<Item = (&mut Statement, Option<&mut Span>)> {\n        let span_iter = self.span_info.iter_mut().map(Some);\n        self.body.iter_mut().zip(span_iter)\n    }\n\n    pub const fn is_empty(&self) -> bool {\n        self.body.is_empty()\n    }\n\n    pub const fn len(&self) -> usize {\n        self.body.len()\n    }\n}\n\nimpl Deref for Block {\n    type Target = [Statement];\n    fn deref(&self) -> &[Statement] {\n        &self.body\n    }\n}\n\nimpl DerefMut for Block {\n    fn deref_mut(&mut self) -> &mut [Statement] {\n        &mut self.body\n    }\n}\n\nimpl<'a> IntoIterator for &'a Block {\n    type Item = &'a Statement;\n    type IntoIter = core::slice::Iter<'a, Statement>;\n\n    fn into_iter(self) -> core::slice::Iter<'a, Statement> {\n        self.iter()\n    }\n}\n\n#[cfg(feature = \"deserialize\")]\nimpl<'de> serde::Deserialize<'de> for Block {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        Ok(Self::from_vec(Vec::deserialize(deserializer)?))\n    }\n}\n\nimpl From<Vec<Statement>> for Block {\n    fn from(body: Vec<Statement>) -> Self {\n        Self::from_vec(body)\n    }\n}\n"
  },
  {
    "path": "naga/src/ir/mod.rs",
    "content": "/*!\nThe Intermediate Representation shared by all frontends and backends.\n\nThe central structure of the IR, and the crate, is [`Module`]. A `Module` contains:\n\n- [`Function`]s, which have arguments, a return type, local variables, and a body,\n\n- [`EntryPoint`]s, which are specialized functions that can serve as the entry\n  point for pipeline stages like vertex shading or fragment shading,\n\n- [`Constant`]s and [`GlobalVariable`]s used by `EntryPoint`s and `Function`s, and\n\n- [`Type`]s used by the above.\n\nThe body of an `EntryPoint` or `Function` is represented using two types:\n\n- An [`Expression`] produces a value, but has no side effects or control flow.\n  `Expressions` include variable references, unary and binary operators, and so\n  on.\n\n- A [`Statement`] can have side effects and structured control flow.\n  `Statement`s do not produce a value, other than by storing one in some\n  designated place. `Statements` include blocks, conditionals, and loops, but also\n  operations that have side effects, like stores and function calls.\n\n`Statement`s form a tree, with pointers into the DAG of `Expression`s.\n\nRestricting side effects to statements simplifies analysis and code generation.\nA Naga backend can generate code to evaluate an `Expression` however and\nwhenever it pleases, as long as it is certain to observe the side effects of all\npreviously executed `Statement`s.\n\nMany `Statement` variants use the [`Block`] type, which is `Vec<Statement>`,\nwith optional span info, representing a series of statements executed in order. The body of an\n`EntryPoint`s or `Function` is a `Block`, and `Statement` has a\n[`Block`][Statement::Block] variant.\n\n## Function Calls\n\nNaga's representation of function calls is unusual. Most languages treat\nfunction calls as expressions, but because calls may have side effects, Naga\nrepresents them as a kind of statement, [`Statement::Call`]. If the function\nreturns a value, a call statement designates a particular [`Expression::CallResult`]\nexpression to represent its return value, for use by subsequent statements and\nexpressions.\n\n## `Expression` evaluation time\n\nIt is essential to know when an [`Expression`] should be evaluated, because its\nvalue may depend on previous [`Statement`]s' effects. But whereas the order of\nexecution for a tree of `Statement`s is apparent from its structure, it is not\nso clear for `Expressions`, since an expression may be referred to by any number\nof `Statement`s and other `Expression`s.\n\nNaga's rules for when `Expression`s are evaluated are as follows:\n\n-   [`Literal`], [`Constant`], and [`ZeroValue`] expressions are\n    considered to be implicitly evaluated before execution begins.\n\n-   [`FunctionArgument`] and [`LocalVariable`] expressions are considered\n    implicitly evaluated upon entry to the function to which they belong.\n    Function arguments cannot be assigned to, and `LocalVariable` expressions\n    produce a *pointer to* the variable's value (for use with [`Load`] and\n    [`Store`]). Neither varies while the function executes, so it suffices to\n    consider these expressions evaluated once on entry.\n\n-   Similarly, [`GlobalVariable`] expressions are considered implicitly\n    evaluated before execution begins, since their value does not change while\n    code executes, for one of two reasons:\n\n    -   Most `GlobalVariable` expressions produce a pointer to the variable's\n        value, for use with [`Load`] and [`Store`], as `LocalVariable`\n        expressions do. Although the variable's value may change, its address\n        does not.\n\n    -   A `GlobalVariable` expression referring to a global in the\n        [`AddressSpace::Handle`] address space produces the value directly, not\n        a pointer. Such global variables hold opaque types like shaders or\n        images, and cannot be assigned to.\n\n-   A [`CallResult`] expression that is the `result` of a [`Statement::Call`],\n    representing the call's return value, is evaluated when the `Call` statement\n    is executed.\n\n-   Similarly, an [`AtomicResult`] expression that is the `result` of an\n    [`Atomic`] statement, representing the result of the atomic operation, is\n    evaluated when the `Atomic` statement is executed.\n\n-   A [`RayQueryProceedResult`] expression, which is a boolean\n    indicating if the ray query is finished, is evaluated when the\n    [`RayQuery`] statement whose [`Proceed::result`] points to it is\n    executed.\n\n-   All other expressions are evaluated when the (unique) [`Statement::Emit`]\n    statement that covers them is executed.\n\nNow, strictly speaking, not all `Expression` variants actually care when they're\nevaluated. For example, you can evaluate a [`BinaryOperator::Add`] expression\nany time you like, as long as you give it the right operands. It's really only a\nvery small set of expressions that are affected by timing:\n\n-   [`Load`], [`ImageSample`], and [`ImageLoad`] expressions are influenced by\n    stores to the variables or images they access, and must execute at the\n    proper time relative to them.\n\n-   [`Derivative`] expressions are sensitive to control flow uniformity: they\n    must not be moved out of an area of uniform control flow into a non-uniform\n    area.\n\n-   More generally, any expression that's used by more than one other expression\n    or statement should probably be evaluated only once, and then stored in a\n    variable to be cited at each point of use.\n\nNaga tries to help back ends handle all these cases correctly in a somewhat\ncircuitous way. The [`ModuleInfo`] structure returned by [`Validator::validate`]\nprovides a reference count for each expression in each function in the module.\nNaturally, any expression with a reference count of two or more deserves to be\nevaluated and stored in a temporary variable at the point that the `Emit`\nstatement covering it is executed. But if we selectively lower the reference\ncount threshold to _one_ for the sensitive expression types listed above, so\nthat we _always_ generate a temporary variable and save their value, then the\nsame code that manages multiply referenced expressions will take care of\nintroducing temporaries for time-sensitive expressions as well. The\n`Expression::bake_ref_count` method (private to the back ends) is meant to help\nwith this.\n\n## `Expression` scope\n\nEach `Expression` has a *scope*, which is the region of the function within\nwhich it can be used by `Statement`s and other `Expression`s. It is a validation\nerror to use an `Expression` outside its scope.\n\nAn expression's scope is defined as follows:\n\n-   The scope of a [`Constant`], [`GlobalVariable`], [`FunctionArgument`] or\n    [`LocalVariable`] expression covers the entire `Function` in which it\n    occurs.\n\n-   The scope of an expression evaluated by an [`Emit`] statement covers the\n    subsequent expressions in that `Emit`, the subsequent statements in the `Block`\n    to which that `Emit` belongs (if any) and their sub-statements (if any).\n\n-   The `result` expression of a [`Call`] or [`Atomic`] statement has a scope\n    covering the subsequent statements in the `Block` in which the statement\n    occurs (if any) and their sub-statements (if any).\n\nFor example, this implies that an expression evaluated by some statement in a\nnested `Block` is not available in the `Block`'s parents. Such a value would\nneed to be stored in a local variable to be carried upwards in the statement\ntree.\n\n## Constant expressions\n\nA Naga *constant expression* is one of the following [`Expression`]\nvariants, whose operands (if any) are also constant expressions:\n- [`Literal`]\n- [`Constant`], for [`Constant`]s\n- [`ZeroValue`], for fixed-size types\n- [`Compose`]\n- [`Access`]\n- [`AccessIndex`]\n- [`Splat`]\n- [`Swizzle`]\n- [`Unary`]\n- [`Binary`]\n- [`Select`]\n- [`Relational`]\n- [`Math`]\n- [`As`]\n\nA constant expression can be evaluated at module translation time.\n\n## Override expressions\n\nA Naga *override expression* is the same as a [constant expression],\nexcept that it is also allowed to reference other [`Override`]s.\n\nAn override expression can be evaluated at pipeline creation time.\n\n[`AtomicResult`]: Expression::AtomicResult\n[`RayQueryProceedResult`]: Expression::RayQueryProceedResult\n[`CallResult`]: Expression::CallResult\n[`Constant`]: Expression::Constant\n[`ZeroValue`]: Expression::ZeroValue\n[`Literal`]: Expression::Literal\n[`Derivative`]: Expression::Derivative\n[`FunctionArgument`]: Expression::FunctionArgument\n[`GlobalVariable`]: Expression::GlobalVariable\n[`ImageLoad`]: Expression::ImageLoad\n[`ImageSample`]: Expression::ImageSample\n[`Load`]: Expression::Load\n[`LocalVariable`]: Expression::LocalVariable\n\n[`Atomic`]: Statement::Atomic\n[`Call`]: Statement::Call\n[`Emit`]: Statement::Emit\n[`Store`]: Statement::Store\n[`RayQuery`]: Statement::RayQuery\n\n[`Proceed::result`]: RayQueryFunction::Proceed::result\n\n[`Validator::validate`]: crate::valid::Validator::validate\n[`ModuleInfo`]: crate::valid::ModuleInfo\n\n[`Literal`]: Expression::Literal\n[`ZeroValue`]: Expression::ZeroValue\n[`Compose`]: Expression::Compose\n[`Access`]: Expression::Access\n[`AccessIndex`]: Expression::AccessIndex\n[`Splat`]: Expression::Splat\n[`Swizzle`]: Expression::Swizzle\n[`Unary`]: Expression::Unary\n[`Binary`]: Expression::Binary\n[`Select`]: Expression::Select\n[`Relational`]: Expression::Relational\n[`Math`]: Expression::Math\n[`As`]: Expression::As\n\n[constant expression]: #constant-expressions\n*/\n\nmod block;\n\nuse alloc::{boxed::Box, string::String, vec::Vec};\n\n#[cfg(feature = \"arbitrary\")]\nuse arbitrary::Arbitrary;\nuse half::f16;\n#[cfg(feature = \"deserialize\")]\nuse serde::Deserialize;\n#[cfg(feature = \"serialize\")]\nuse serde::Serialize;\n\nuse crate::arena::{Arena, Handle, Range, UniqueArena};\nuse crate::diagnostic_filter::DiagnosticFilterNode;\nuse crate::{FastIndexMap, NamedExpressions};\n\npub use block::Block;\n\n/// Explicitly allows early depth/stencil tests.\n///\n/// Normally, depth/stencil tests are performed after fragment shading. However, as an optimization,\n/// most drivers will move the depth/stencil tests before fragment shading if this does not\n/// have any observable consequences. This optimization is disabled under the following\n/// circumstances:\n///   - `discard` is called in the fragment shader.\n///   - The fragment shader writes to the depth buffer.\n///   - The fragment shader writes to any storage bindings.\n///\n/// When `EarlyDepthTest` is set, it is allowed to perform an early depth/stencil test even if the\n/// above conditions are not met. When [`EarlyDepthTest::Force`] is used, depth/stencil tests\n/// **must** be performed before fragment shading.\n///\n/// To force early depth/stencil tests in a shader:\n///   - GLSL: `layout(early_fragment_tests) in;`\n///   - HLSL: `Attribute earlydepthstencil`\n///   - SPIR-V: `ExecutionMode EarlyFragmentTests`\n///   - WGSL: `@early_depth_test(force)`\n///\n/// This may also be enabled in a shader by specifying a [`ConservativeDepth`].\n///\n/// For more, see:\n///   - <https://www.khronos.org/opengl/wiki/Early_Fragment_Test#Explicit_specification>\n///   - <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-earlydepthstencil>\n///   - <https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Execution_Mode>\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum EarlyDepthTest {\n    /// Requires depth/stencil tests to be performed before fragment shading.\n    ///\n    /// This will disable depth/stencil tests after fragment shading, so discarding the fragment\n    /// or overwriting the fragment depth will have no effect.\n    Force,\n\n    /// Allows an additional depth/stencil test to be performed before fragment shading.\n    ///\n    /// It is up to the driver to decide whether early tests are performed. Unlike `Force`, this\n    /// does not disable depth/stencil tests after fragment shading.\n    Allow {\n        /// Specifies restrictions on how the depth value can be modified within the fragment\n        /// shader.\n        ///\n        /// This may be taken into account when deciding whether to perform early tests.\n        conservative: ConservativeDepth,\n    },\n}\n\n/// Enables adjusting depth without disabling early Z.\n///\n/// To use in a shader:\n///   - GLSL: `layout (depth_<greater/less/unchanged/any>) out float gl_FragDepth;`\n///     - `depth_any` option behaves as if the layout qualifier was not present.\n///   - HLSL: `SV_DepthGreaterEqual`/`SV_DepthLessEqual`/`SV_Depth`\n///   - SPIR-V: `ExecutionMode Depth<Greater/Less/Unchanged>`\n///   - WGSL: `@early_depth_test(greater_equal/less_equal/unchanged)`\n///\n/// For more, see:\n///   - <https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt>\n///   - <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics>\n///   - <https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Execution_Mode>\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ConservativeDepth {\n    /// Shader may rewrite depth only with a value greater than calculated.\n    GreaterEqual,\n\n    /// Shader may rewrite depth smaller than one that would have been written without the modification.\n    LessEqual,\n\n    /// Shader may not rewrite depth value.\n    Unchanged,\n}\n\n/// Stage of the programmable pipeline.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ShaderStage {\n    /// A vertex shader, in a render pipeline.\n    Vertex,\n\n    /// A task shader, in a mesh render pipeline.\n    Task,\n\n    /// A mesh shader, in a mesh render pipeline.\n    Mesh,\n\n    /// A fragment shader, in a render pipeline.\n    Fragment,\n\n    /// Compute pipeline shader.\n    Compute,\n\n    /// A ray generation shader, in a ray tracing pipeline.\n    RayGeneration,\n\n    /// A miss shader, in a ray tracing pipeline.\n    Miss,\n\n    /// A any hit shader, in a ray tracing pipeline.\n    AnyHit,\n\n    /// A closest hit shader, in a ray tracing pipeline.\n    ClosestHit,\n}\n\n/// Addressing space of variables.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum AddressSpace {\n    /// Function locals.\n    Function,\n    /// Private data, per invocation, mutable.\n    Private,\n    /// Workgroup shared data, mutable.\n    WorkGroup,\n    /// Uniform buffer data.\n    Uniform,\n    /// Storage buffer data, potentially mutable.\n    Storage { access: StorageAccess },\n    /// Opaque handles, such as samplers and images.\n    Handle,\n\n    /// Immediate data.\n    ///\n    /// A [`Module`] may contain at most one [`GlobalVariable`] in\n    /// this address space. Its contents are provided not by a buffer\n    /// but by `SetImmediates` pass commands, allowing the CPU to\n    /// establish different values for each draw/dispatch.\n    ///\n    /// `Immediate` variables may not contain `f16` values, even if\n    /// the [`SHADER_FLOAT16`] capability is enabled.\n    ///\n    /// Backends generally place tight limits on the size of\n    /// `Immediate` variables.\n    ///\n    /// [`SHADER_FLOAT16`]: crate::valid::Capabilities::SHADER_FLOAT16\n    Immediate,\n    /// Task shader to mesh shader payload\n    TaskPayload,\n\n    /// Ray tracing payload, for inputting in TraceRays\n    RayPayload,\n    /// Ray tracing payload, for entrypoints invoked by a TraceRays call\n    ///\n    /// Each entrypoint may reference only one variable in this scope, as\n    /// only one may be passed as a payload.\n    IncomingRayPayload,\n}\n\n/// Built-in inputs and outputs.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum BuiltIn {\n    // This must be at the top so that it gets sorted to the top. PrimitiveIndex is considered a non SV\n    // by FXC so it must appear before any other SVs.\n    /// Read in fragment shaders, written in mesh shaders, read in any and closest hit shaders.\n    PrimitiveIndex,\n\n    /// Written in vertex/mesh shaders, read in fragment shaders\n    Position { invariant: bool },\n    /// Read in task, mesh, vertex, and fragment shaders\n    ViewIndex,\n\n    /// Read in vertex shaders\n    BaseInstance,\n    /// Read in vertex shaders\n    BaseVertex,\n    /// Written in vertex & mesh shaders\n    ClipDistances,\n    /// Written in vertex & mesh shaders\n    CullDistance,\n    /// Read in vertex, any- and closest-hit shaders\n    InstanceIndex,\n    /// Written in vertex & mesh shaders\n    PointSize,\n    /// Read in vertex shaders\n    VertexIndex,\n    /// Read in vertex & task shaders, or mesh shaders in pipelines without task shaders\n    DrawIndex,\n\n    /// Written in fragment shaders\n    FragDepth,\n    /// Read in fragment shaders\n    PointCoord,\n    /// Read in fragment shaders\n    FrontFacing,\n    /// Read in fragment shaders\n    Barycentric { perspective: bool },\n    /// Read in fragment shaders\n    SampleIndex,\n    /// Read or written in fragment shaders\n    SampleMask,\n\n    /// Read in compute, task, and mesh shaders\n    GlobalInvocationId,\n    /// Read in compute, task, and mesh shaders\n    LocalInvocationId,\n    /// Read in compute, task, and mesh shaders\n    LocalInvocationIndex,\n    /// Read in compute, task, and mesh shaders\n    WorkGroupId,\n    /// Read in compute, task, and mesh shaders\n    WorkGroupSize,\n    /// Read in compute, task, and mesh shaders\n    NumWorkGroups,\n\n    /// Read in compute, task, and mesh shaders\n    NumSubgroups,\n    /// Read in compute, task, and mesh shaders\n    SubgroupId,\n    /// Read in compute, fragment, task, and mesh shaders\n    SubgroupSize,\n    /// Read in compute, fragment, task, and mesh shaders\n    SubgroupInvocationId,\n\n    /// Written in task shaders\n    MeshTaskSize,\n    /// Written in mesh shaders\n    CullPrimitive,\n    /// Written in mesh shaders\n    PointIndex,\n    /// Written in mesh shaders\n    LineIndices,\n    /// Written in mesh shaders\n    TriangleIndices,\n\n    /// Written to a workgroup variable in mesh shaders\n    VertexCount,\n    /// Written to a workgroup variable in mesh shaders\n    Vertices,\n    /// Written to a workgroup variable in mesh shaders\n    PrimitiveCount,\n    /// Written to a workgroup variable in mesh shaders\n    Primitives,\n\n    /// Read in all ray tracing pipeline shaders, the id within the number of\n    /// rays that this current ray is.\n    RayInvocationId,\n    /// Read in all ray tracing pipeline shaders, the number of rays created.\n    NumRayInvocations,\n    /// Read in closest hit and any hit shaders, the custom data in the tlas\n    /// instance\n    InstanceCustomData,\n    /// Read in closest hit and any hit shaders, the index of the geometry in\n    /// the blas.\n    GeometryIndex,\n    /// Read in closest hit, any hit, and miss shaders, the origin of the ray.\n    WorldRayOrigin,\n    /// Read in closest hit, any hit, and miss shaders, the direction of the\n    /// ray.\n    WorldRayDirection,\n    /// Read in closest hit and any hit shaders, the direction of the ray in\n    /// object space.\n    ObjectRayOrigin,\n    /// Read in closest hit and any hit shaders, the direction of the ray in\n    /// object space.\n    ObjectRayDirection,\n    /// Read in closest hit, any hit, and miss shaders, the t min provided by\n    /// in the ray desc.\n    RayTmin,\n    /// Read in closest hit, any hit, and miss shaders, the final bounds at which\n    /// a hit is accepted (the closest committed hit if there is one otherwise, t\n    /// max provided in the ray desc).\n    RayTCurrentMax,\n    /// Read in closest hit and any hit shaders, the matrix for converting from\n    /// object space to world space\n    ObjectToWorld,\n    /// Read in closest hit and any hit shaders, the matrix for converting from\n    /// world space to object space\n    WorldToObject,\n    /// Read in closest hit and any hit shaders, the type of hit as provided by\n    /// the intersection function if any, otherwise this is 254 (0xFE) for a\n    /// front facing triangle and 255 (0xFF) for a back facing triangle\n    HitKind,\n}\n\n/// Number of bytes per scalar.\npub type Bytes = u8;\n\n/// Number of components in a vector.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum VectorSize {\n    /// 2D vector\n    Bi = 2,\n    /// 3D vector\n    Tri = 3,\n    /// 4D vector\n    Quad = 4,\n}\n\nimpl VectorSize {\n    pub const MAX: usize = Self::Quad as usize;\n}\n\nimpl From<VectorSize> for u8 {\n    fn from(size: VectorSize) -> u8 {\n        size as u8\n    }\n}\n\nimpl From<VectorSize> for u32 {\n    fn from(size: VectorSize) -> u32 {\n        size as u32\n    }\n}\n\n/// Number of components in a cooperative vector.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum CooperativeSize {\n    Eight = 8,\n    Sixteen = 16,\n}\n\n/// Primitive type for a scalar.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ScalarKind {\n    /// Signed integer type.\n    Sint,\n    /// Unsigned integer type.\n    Uint,\n    /// Floating point type.\n    Float,\n    /// Boolean type.\n    Bool,\n\n    /// WGSL abstract integer type.\n    ///\n    /// These are forbidden by validation, and should never reach backends.\n    AbstractInt,\n\n    /// Abstract floating-point type.\n    ///\n    /// These are forbidden by validation, and should never reach backends.\n    AbstractFloat,\n}\n\n/// Role of a cooperative variable in the equation \"A * B + C\"\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum CooperativeRole {\n    A,\n    B,\n    C,\n}\n\n/// Characteristics of a scalar type.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Scalar {\n    /// How the value's bits are to be interpreted.\n    pub kind: ScalarKind,\n\n    /// This size of the value in bytes.\n    pub width: Bytes,\n}\n\n/// Size of an array.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ArraySize {\n    /// The array size is constant.\n    Constant(core::num::NonZeroU32),\n    /// The array size is an override-expression.\n    Pending(Handle<Override>),\n    /// The array size can change at runtime.\n    Dynamic,\n}\n\n/// The interpolation qualifier of a binding or struct field.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Interpolation {\n    /// The value will be interpolated in a perspective-correct fashion.\n    /// Also known as \"smooth\" in glsl.\n    Perspective,\n    /// Indicates that linear, non-perspective, correct\n    /// interpolation must be used.\n    /// Also known as \"no_perspective\" in glsl.\n    Linear,\n    /// Indicates that no interpolation will be performed.\n    Flat,\n    /// Indicates the fragment input binding holds an array of per-vertex values.\n    /// This is typically used with barycentrics.\n    PerVertex,\n}\n\n/// The sampling qualifiers of a binding or struct field.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Sampling {\n    /// Interpolate the value at the center of the pixel.\n    Center,\n\n    /// Interpolate the value at a point that lies within all samples covered by\n    /// the fragment within the current primitive. In multisampling, use a\n    /// single value for all samples in the primitive.\n    Centroid,\n\n    /// Interpolate the value at each sample location. In multisampling, invoke\n    /// the fragment shader once per sample.\n    Sample,\n\n    /// Use the value provided by the first vertex of the current primitive.\n    First,\n\n    /// Use the value provided by the first or last vertex of the current primitive. The exact\n    /// choice is implementation-dependent.\n    Either,\n}\n\n/// Member of a user-defined structure.\n// Clone is used only for error reporting and is not intended for end users\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct StructMember {\n    pub name: Option<String>,\n    /// Type of the field.\n    pub ty: Handle<Type>,\n    /// For I/O structs, defines the binding.\n    pub binding: Option<Binding>,\n    /// Offset from the beginning from the struct.\n    pub offset: u32,\n}\n\n/// The number of dimensions an image has.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ImageDimension {\n    /// 1D image\n    D1,\n    /// 2D image\n    D2,\n    /// 3D image\n    D3,\n    /// Cube map\n    Cube,\n}\n\nbitflags::bitflags! {\n    /// Flags describing an image.\n    #[cfg_attr(feature = \"serialize\", derive(Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n    #[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]\n    pub struct StorageAccess: u32 {\n        /// Storage can be used as a source for load ops.\n        const LOAD = 0x1;\n        /// Storage can be used as a target for store ops.\n        const STORE = 0x2;\n        /// Storage can be used as a target for atomic ops.\n        const ATOMIC = 0x4;\n    }\n}\n\nbitflags::bitflags! {\n    /// Memory decorations for global variables.\n    #[cfg_attr(feature = \"serialize\", derive(Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n    #[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]\n    pub struct MemoryDecorations: u8 {\n        /// Reads and writes are automatically visible to other invocations\n        /// without explicit barriers.\n        const COHERENT = 0x1;\n        /// The variable may be modified by something external to the shader,\n        /// preventing certain compiler optimizations.\n        const VOLATILE = 0x2;\n    }\n}\n\n/// Image storage format.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum StorageFormat {\n    // 8-bit formats\n    R8Unorm,\n    R8Snorm,\n    R8Uint,\n    R8Sint,\n\n    // 16-bit formats\n    R16Uint,\n    R16Sint,\n    R16Float,\n    Rg8Unorm,\n    Rg8Snorm,\n    Rg8Uint,\n    Rg8Sint,\n\n    // 32-bit formats\n    R32Uint,\n    R32Sint,\n    R32Float,\n    Rg16Uint,\n    Rg16Sint,\n    Rg16Float,\n    Rgba8Unorm,\n    Rgba8Snorm,\n    Rgba8Uint,\n    Rgba8Sint,\n    Bgra8Unorm,\n\n    // Packed 32-bit formats\n    Rgb10a2Uint,\n    Rgb10a2Unorm,\n    Rg11b10Ufloat,\n\n    // 64-bit formats\n    R64Uint,\n    Rg32Uint,\n    Rg32Sint,\n    Rg32Float,\n    Rgba16Uint,\n    Rgba16Sint,\n    Rgba16Float,\n\n    // 128-bit formats\n    Rgba32Uint,\n    Rgba32Sint,\n    Rgba32Float,\n\n    // Normalized 16-bit per channel formats\n    R16Unorm,\n    R16Snorm,\n    Rg16Unorm,\n    Rg16Snorm,\n    Rgba16Unorm,\n    Rgba16Snorm,\n}\n\n/// Sub-class of the image type.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ImageClass {\n    /// Regular sampled image.\n    Sampled {\n        /// Kind of values to sample.\n        kind: ScalarKind,\n        /// Multi-sampled image.\n        ///\n        /// A multi-sampled image holds several samples per texel. Multi-sampled\n        /// images cannot have mipmaps.\n        multi: bool,\n    },\n    /// Depth comparison image.\n    Depth {\n        /// Multi-sampled depth image.\n        multi: bool,\n    },\n    /// External texture.\n    External,\n    /// Storage image.\n    Storage {\n        format: StorageFormat,\n        access: StorageAccess,\n    },\n}\n\n/// A data type declared in the module.\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Type {\n    /// The name of the type, if any.\n    pub name: Option<String>,\n    /// Inner structure that depends on the kind of the type.\n    pub inner: TypeInner,\n}\n\n/// Enum with additional information, depending on the kind of type.\n///\n/// Comparison using `==` is not reliable in the case of [`Pointer`],\n/// [`ValuePointer`], or [`Struct`] variants. For these variants,\n/// use [`TypeInner::non_struct_equivalent`] or [`compare_types`].\n///\n/// [`compare_types`]: crate::proc::compare_types\n/// [`ValuePointer`]: TypeInner::ValuePointer\n/// [`Pointer`]: TypeInner::Pointer\n/// [`Struct`]: TypeInner::Struct\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum TypeInner {\n    /// Number of integral or floating-point kind.\n    Scalar(Scalar),\n    /// Vector of numbers.\n    Vector { size: VectorSize, scalar: Scalar },\n    /// Matrix of numbers.\n    Matrix {\n        columns: VectorSize,\n        rows: VectorSize,\n        scalar: Scalar,\n    },\n    /// Matrix that is cooperatively processed by all the threads\n    /// in an opaque mapping.\n    CooperativeMatrix {\n        columns: CooperativeSize,\n        rows: CooperativeSize,\n        scalar: Scalar,\n        role: CooperativeRole,\n    },\n    /// Atomic scalar.\n    Atomic(Scalar),\n    /// Pointer to another type.\n    ///\n    /// Pointers to scalars and vectors should be treated as equivalent to\n    /// [`ValuePointer`] types. Use either [`TypeInner::non_struct_equivalent`]\n    /// or [`compare_types`] to compare types in a way that treats pointers\n    /// correctly.\n    ///\n    /// ## Pointers to non-`SIZED` types\n    ///\n    /// The `base` type of a pointer may be a non-[`SIZED`] type like a\n    /// dynamically-sized [`Array`], or a [`Struct`] whose last member is a\n    /// dynamically sized array. Such pointers occur as the types of\n    /// [`GlobalVariable`] or [`AccessIndex`] expressions referring to\n    /// dynamically-sized arrays.\n    ///\n    /// However, among pointers to non-`SIZED` types, only pointers to `Struct`s\n    /// are [`DATA`]. Pointers to dynamically sized `Array`s cannot be passed as\n    /// arguments, stored in variables, or held in arrays or structures. Their\n    /// only use is as the types of `AccessIndex` expressions.\n    ///\n    /// [`SIZED`]: crate::valid::TypeFlags::SIZED\n    /// [`DATA`]: crate::valid::TypeFlags::DATA\n    /// [`Array`]: TypeInner::Array\n    /// [`Struct`]: TypeInner::Struct\n    /// [`ValuePointer`]: TypeInner::ValuePointer\n    /// [`GlobalVariable`]: Expression::GlobalVariable\n    /// [`AccessIndex`]: Expression::AccessIndex\n    /// [`compare_types`]: crate::proc::compare_types\n    Pointer {\n        base: Handle<Type>,\n        space: AddressSpace,\n    },\n\n    /// Pointer to a scalar or vector.\n    ///\n    /// A `ValuePointer` type is equivalent to a `Pointer` whose `base` is a\n    /// `Scalar` or `Vector` type. This is for use in [`TypeResolution::Value`]\n    /// variants; see the documentation for [`TypeResolution`] for details.\n    ///\n    /// Use [`TypeInner::non_struct_equivalent`] or [`compare_types`] to compare\n    /// types that could be pointers, to ensure that `Pointer` and\n    /// `ValuePointer` types are recognized as equivalent.\n    ///\n    /// [`TypeResolution`]: crate::proc::TypeResolution\n    /// [`TypeResolution::Value`]: crate::proc::TypeResolution::Value\n    /// [`compare_types`]: crate::proc::compare_types\n    ValuePointer {\n        size: Option<VectorSize>,\n        scalar: Scalar,\n        space: AddressSpace,\n    },\n\n    /// Homogeneous list of elements.\n    ///\n    /// The `base` type must be a [`SIZED`], [`DATA`] type.\n    ///\n    /// ## Dynamically sized arrays\n    ///\n    /// An `Array` is [`SIZED`] unless its `size` is [`Dynamic`].\n    /// Dynamically-sized arrays may only appear in a few situations:\n    ///\n    /// -   They may appear as the type of a [`GlobalVariable`], or as the last\n    ///     member of a [`Struct`].\n    ///\n    /// -   They may appear as the base type of a [`Pointer`]. An\n    ///     [`AccessIndex`] expression referring to a struct's final\n    ///     unsized array member would have such a pointer type. However, such\n    ///     pointer types may only appear as the types of such intermediate\n    ///     expressions. They are not [`DATA`], and cannot be stored in\n    ///     variables, held in arrays or structs, or passed as parameters.\n    ///\n    /// [`SIZED`]: crate::valid::TypeFlags::SIZED\n    /// [`DATA`]: crate::valid::TypeFlags::DATA\n    /// [`Dynamic`]: ArraySize::Dynamic\n    /// [`Struct`]: TypeInner::Struct\n    /// [`Pointer`]: TypeInner::Pointer\n    /// [`AccessIndex`]: Expression::AccessIndex\n    Array {\n        base: Handle<Type>,\n        size: ArraySize,\n        stride: u32,\n    },\n\n    /// User-defined structure.\n    ///\n    /// There must always be at least one member.\n    ///\n    /// A `Struct` type is [`DATA`], and the types of its members must be\n    /// `DATA` as well.\n    ///\n    /// Member types must be [`SIZED`], except for the final member of a\n    /// struct, which may be a dynamically sized [`Array`]. The\n    /// `Struct` type itself is `SIZED` when all its members are `SIZED`.\n    ///\n    /// Two structure types with different names are not equivalent. Because\n    /// this variant does not contain the name, it is not possible to use it\n    /// to compare struct types. Use [`compare_types`] to compare two types\n    /// that may be structs.\n    ///\n    /// [`DATA`]: crate::valid::TypeFlags::DATA\n    /// [`SIZED`]: crate::∅TypeFlags::SIZED\n    /// [`Array`]: TypeInner::Array\n    /// [`compare_types`]: crate::proc::compare_types\n    Struct {\n        members: Vec<StructMember>,\n        //TODO: should this be unaligned?\n        span: u32,\n    },\n    /// Possibly multidimensional array of texels.\n    Image {\n        dim: ImageDimension,\n        arrayed: bool,\n        //TODO: consider moving `multisampled: bool` out\n        class: ImageClass,\n    },\n    /// Can be used to sample values from images.\n    Sampler { comparison: bool },\n\n    /// Opaque object representing an acceleration structure of geometry.\n    AccelerationStructure { vertex_return: bool },\n\n    /// Locally used handle for ray queries.\n    RayQuery { vertex_return: bool },\n\n    /// Array of bindings.\n    ///\n    /// A `BindingArray` represents an array where each element draws its value\n    /// from a separate bound resource. The array's element type `base` may be\n    /// [`Image`], [`Sampler`], or any type that would be permitted for a global\n    /// in the [`Uniform`] or [`Storage`] address spaces. Only global variables\n    /// may be binding arrays; on the host side, their values are provided by\n    /// [`TextureViewArray`], [`SamplerArray`], or [`BufferArray`]\n    /// bindings.\n    ///\n    /// Since each element comes from a distinct resource, a binding array of\n    /// images could have images of varying sizes (but not varying dimensions;\n    /// they must all have the same `Image` type). Or, a binding array of\n    /// buffers could have elements that are dynamically sized arrays, each with\n    /// a different length.\n    ///\n    /// Binding arrays are in the same address spaces as their underlying type.\n    /// As such, referring to an array of images produces an [`Image`] value\n    /// directly (as opposed to a pointer). The only operation permitted on\n    /// `BindingArray` values is indexing, which works transparently: indexing\n    /// a binding array of samplers yields a [`Sampler`], indexing a pointer to the\n    /// binding array of storage buffers produces a pointer to the storage struct.\n    ///\n    /// Unlike textures and samplers, binding arrays are not [`ARGUMENT`], so\n    /// they cannot be passed as arguments to functions.\n    ///\n    /// Naga's WGSL front end supports binding arrays with the type syntax\n    /// `binding_array<T, N>`.\n    ///\n    /// [`Image`]: TypeInner::Image\n    /// [`Sampler`]: TypeInner::Sampler\n    /// [`Uniform`]: AddressSpace::Uniform\n    /// [`Storage`]: AddressSpace::Storage\n    /// [`TextureViewArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.TextureViewArray\n    /// [`SamplerArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.SamplerArray\n    /// [`BufferArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.BufferArray\n    /// [`DATA`]: crate::valid::TypeFlags::DATA\n    /// [`ARGUMENT`]: crate::valid::TypeFlags::ARGUMENT\n    /// [naga#1864]: https://github.com/gfx-rs/naga/issues/1864\n    BindingArray { base: Handle<Type>, size: ArraySize },\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Literal {\n    /// May not be NaN or infinity.\n    F64(f64),\n    /// May not be NaN or infinity.\n    F32(f32),\n    /// May not be NaN or infinity.\n    F16(f16),\n    U32(u32),\n    I32(i32),\n    U64(u64),\n    I64(i64),\n    Bool(bool),\n    AbstractInt(i64),\n    AbstractFloat(f64),\n}\n\n/// Pipeline-overridable constant.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Override {\n    pub name: Option<String>,\n    /// Pipeline Constant ID.\n    pub id: Option<u16>,\n    pub ty: Handle<Type>,\n\n    /// The default value of the pipeline-overridable constant.\n    ///\n    /// This [`Handle`] refers to [`Module::global_expressions`], not\n    /// any [`Function::expressions`] arena.\n    pub init: Option<Handle<Expression>>,\n}\n\n/// Constant value.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Constant {\n    pub name: Option<String>,\n    pub ty: Handle<Type>,\n\n    /// The value of the constant.\n    ///\n    /// This [`Handle`] refers to [`Module::global_expressions`], not\n    /// any [`Function::expressions`] arena.\n    pub init: Handle<Expression>,\n}\n\n/// Describes how an input/output variable is to be bound.\n#[derive(Clone, Debug, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Binding {\n    /// Built-in shader variable.\n    BuiltIn(BuiltIn),\n\n    /// Indexed location.\n    ///\n    /// This is a value passed to a [`Fragment`] shader from a [`Vertex`] or\n    /// [`Mesh`] shader.\n    ///\n    /// Values passed from the [`Vertex`] stage to the [`Fragment`] stage must\n    /// have their `interpolation` defaulted (i.e. not `None`) by the front end\n    /// as appropriate for that language.\n    ///\n    /// For other stages, we permit interpolations even though they're ignored.\n    /// When a front end is parsing a struct type, it usually doesn't know what\n    /// stages will be using it for IO, so it's easiest if it can apply the\n    /// defaults to anything with a `Location` binding, just in case.\n    ///\n    /// For anything other than floating-point scalars and vectors, the\n    /// interpolation must be `Flat`.\n    ///\n    /// [`Vertex`]: crate::ShaderStage::Vertex\n    /// [`Mesh`]: crate::ShaderStage::Mesh\n    /// [`Fragment`]: crate::ShaderStage::Fragment\n    Location {\n        location: u32,\n        interpolation: Option<Interpolation>,\n        sampling: Option<Sampling>,\n\n        /// Optional `blend_src` index used for dual source blending.\n        /// See <https://www.w3.org/TR/WGSL/#attribute-blend_src>\n        blend_src: Option<u32>,\n\n        /// Whether the binding is a per-primitive binding for use with mesh shaders.\n        ///\n        /// This must be `true` if this binding is a mesh shader primitive output, or such\n        /// an output's corresponding fragment shader input. It must be `false` otherwise.\n        ///\n        /// A stage's outputs must all have unique `location` numbers, regardless of\n        /// whether they are per-primitive; a mesh shader's per-vertex and per-primitive\n        /// outputs share the same location numbering space.\n        ///\n        /// Per-primitive values are not interpolated at all and are not dependent on the\n        /// vertices or pixel location. For example, it may be used to store a\n        /// non-interpolated normal vector.\n        per_primitive: bool,\n    },\n}\n\n/// Pipeline binding information for global resources.\n#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct ResourceBinding {\n    /// The bind group index.\n    pub group: u32,\n    /// Binding number within the group.\n    pub binding: u32,\n}\n\n/// Variable defined at module level.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct GlobalVariable {\n    /// Name of the variable, if any.\n    pub name: Option<String>,\n    /// How this variable is to be stored.\n    pub space: AddressSpace,\n    /// For resources, defines the binding point.\n    pub binding: Option<ResourceBinding>,\n    /// The type of this variable.\n    pub ty: Handle<Type>,\n    /// Initial value for this variable.\n    ///\n    /// This refers to an [`Expression`] in [`Module::global_expressions`].\n    pub init: Option<Handle<Expression>>,\n    /// Memory decorations for this variable.\n    ///\n    /// These are meaningful for storage address space variables in SPIR-V,\n    /// where they map to SPIR-V memory decorations on the variable.\n    ///\n    /// In WGSL, these can be set with attributes like `@coherent` or `@volatile`.\n    pub memory_decorations: MemoryDecorations,\n}\n\n/// Variable defined at function level.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct LocalVariable {\n    /// Name of the variable, if any.\n    pub name: Option<String>,\n    /// The type of this variable.\n    pub ty: Handle<Type>,\n    /// Initial value for this variable.\n    ///\n    /// This handle refers to an expression in this `LocalVariable`'s function's\n    /// [`expressions`] arena, but it is required to be an evaluated override\n    /// expression.\n    ///\n    /// [`expressions`]: Function::expressions\n    pub init: Option<Handle<Expression>>,\n}\n\n/// Operation that can be applied on a single value.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum UnaryOperator {\n    Negate,\n    LogicalNot,\n    BitwiseNot,\n}\n\n/// Operation that can be applied on two values.\n///\n/// ## Arithmetic type rules\n///\n/// The arithmetic operations `Add`, `Subtract`, `Multiply`, `Divide`, and\n/// `Modulo` can all be applied to [`Scalar`] types other than [`Bool`], or\n/// [`Vector`]s thereof. Both operands must have the same type.\n///\n/// `Add` and `Subtract` can also be applied to [`Matrix`] values. Both operands\n/// must have the same type.\n///\n/// `Multiply` supports additional cases:\n///\n/// -   A [`Matrix`] or [`Vector`] can be multiplied by a scalar [`Float`],\n///     either on the left or the right.\n///\n/// -   A [`Matrix`] on the left can be multiplied by a [`Vector`] on the right\n///     if the matrix has as many columns as the vector has components\n///     (`matCxR * VecC`).\n///\n/// -   A [`Vector`] on the left can be multiplied by a [`Matrix`] on the right\n///     if the matrix has as many rows as the vector has components\n///     (`VecR * matCxR`).\n///\n/// -   Two matrices can be multiplied if the left operand has as many columns\n///     as the right operand has rows (`matNxR * matCxN`).\n///\n/// In all the above `Multiply` cases, the byte widths of the underlying scalar\n/// types of both operands must be the same.\n///\n/// Note that `Multiply` supports mixed vector and scalar operations directly,\n/// whereas the other arithmetic operations require an explicit [`Splat`] for\n/// mixed-type use.\n///\n/// [`Scalar`]: TypeInner::Scalar\n/// [`Vector`]: TypeInner::Vector\n/// [`Matrix`]: TypeInner::Matrix\n/// [`Float`]: ScalarKind::Float\n/// [`Bool`]: ScalarKind::Bool\n/// [`Splat`]: Expression::Splat\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum BinaryOperator {\n    Add,\n    Subtract,\n    Multiply,\n    Divide,\n    /// Equivalent of the WGSL's `%` operator or SPIR-V's `OpFRem`\n    Modulo,\n    Equal,\n    NotEqual,\n    Less,\n    LessEqual,\n    Greater,\n    GreaterEqual,\n    And,\n    ExclusiveOr,\n    InclusiveOr,\n    LogicalAnd,\n    LogicalOr,\n    ShiftLeft,\n    /// Right shift carries the sign of signed integers only.\n    ShiftRight,\n}\n\n/// Function on an atomic value.\n///\n/// Note: these do not include load/store, which use the existing\n/// [`Expression::Load`] and [`Statement::Store`].\n///\n/// All `Handle<Expression>` values here refer to an expression in\n/// [`Function::expressions`].\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum AtomicFunction {\n    Add,\n    Subtract,\n    And,\n    ExclusiveOr,\n    InclusiveOr,\n    Min,\n    Max,\n    Exchange { compare: Option<Handle<Expression>> },\n}\n\n/// Hint at which precision to compute a derivative.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum DerivativeControl {\n    Coarse,\n    Fine,\n    None,\n}\n\n/// Axis on which to compute a derivative.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum DerivativeAxis {\n    X,\n    Y,\n    Width,\n}\n\n/// Built-in shader function for testing relation between values.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum RelationalFunction {\n    All,\n    Any,\n    IsNan,\n    IsInf,\n}\n\n/// Built-in shader function for math.\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum MathFunction {\n    // comparison\n    Abs,\n    Min,\n    Max,\n    Clamp,\n    Saturate,\n    // trigonometry\n    Cos,\n    Cosh,\n    Sin,\n    Sinh,\n    Tan,\n    Tanh,\n    Acos,\n    Asin,\n    Atan,\n    Atan2,\n    Asinh,\n    Acosh,\n    Atanh,\n    Radians,\n    Degrees,\n    // decomposition\n    Ceil,\n    Floor,\n    Round,\n    Fract,\n    Trunc,\n    Modf,\n    Frexp,\n    Ldexp,\n    // exponent\n    Exp,\n    Exp2,\n    Log,\n    Log2,\n    Pow,\n    // geometry\n    Dot,\n    Dot4I8Packed,\n    Dot4U8Packed,\n    Outer,\n    Cross,\n    Distance,\n    Length,\n    Normalize,\n    FaceForward,\n    Reflect,\n    Refract,\n    // computational\n    Sign,\n    Fma,\n    Mix,\n    Step,\n    SmoothStep,\n    Sqrt,\n    InverseSqrt,\n    Inverse,\n    Transpose,\n    Determinant,\n    QuantizeToF16,\n    // bits\n    CountTrailingZeros,\n    CountLeadingZeros,\n    CountOneBits,\n    ReverseBits,\n    ExtractBits,\n    InsertBits,\n    FirstTrailingBit,\n    FirstLeadingBit,\n    // data packing\n    Pack4x8snorm,\n    Pack4x8unorm,\n    Pack2x16snorm,\n    Pack2x16unorm,\n    Pack2x16float,\n    Pack4xI8,\n    Pack4xU8,\n    Pack4xI8Clamp,\n    Pack4xU8Clamp,\n    // data unpacking\n    Unpack4x8snorm,\n    Unpack4x8unorm,\n    Unpack2x16snorm,\n    Unpack2x16unorm,\n    Unpack2x16float,\n    Unpack4xI8,\n    Unpack4xU8,\n}\n\n/// Sampling modifier to control the level of detail.\n///\n/// All `Handle<Expression>` values here refer to an expression in\n/// [`Function::expressions`].\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum SampleLevel {\n    Auto,\n    Zero,\n    Exact(Handle<Expression>),\n    Bias(Handle<Expression>),\n    Gradient {\n        x: Handle<Expression>,\n        y: Handle<Expression>,\n    },\n}\n\n/// Type of an image query.\n///\n/// All `Handle<Expression>` values here refer to an expression in\n/// [`Function::expressions`].\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum ImageQuery {\n    /// Get the size at the specified level.\n    ///\n    /// The return value is a `u32` for 1D images, and a `vecN<u32>`\n    /// for an image with dimensions N > 2.\n    Size {\n        /// If `None`, the base level is considered.\n        level: Option<Handle<Expression>>,\n    },\n    /// Get the number of mipmap levels, a `u32`.\n    NumLevels,\n    /// Get the number of array layers, a `u32`.\n    NumLayers,\n    /// Get the number of samples, a `u32`.\n    NumSamples,\n}\n\n/// Component selection for a vector swizzle.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum SwizzleComponent {\n    X = 0,\n    Y = 1,\n    Z = 2,\n    W = 3,\n}\n\n/// The specific behavior of a [`SubgroupGather`] statement.\n///\n/// All `Handle<Expression>` values here refer to an expression in\n/// [`Function::expressions`].\n///\n/// [`SubgroupGather`]: Statement::SubgroupGather\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum GatherMode {\n    /// All gather from the active lane with the smallest index\n    BroadcastFirst,\n    /// All gather from the same lane at the index given by the expression\n    Broadcast(Handle<Expression>),\n    /// Each gathers from a different lane at the index given by the expression\n    Shuffle(Handle<Expression>),\n    /// Each gathers from their lane plus the shift given by the expression\n    ShuffleDown(Handle<Expression>),\n    /// Each gathers from their lane minus the shift given by the expression\n    ShuffleUp(Handle<Expression>),\n    /// Each gathers from their lane xored with the given by the expression\n    ShuffleXor(Handle<Expression>),\n    /// All gather from the same quad lane at the index given by the expression\n    QuadBroadcast(Handle<Expression>),\n    /// Each gathers from the opposite quad lane along the given direction\n    QuadSwap(Direction),\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Direction {\n    X = 0,\n    Y = 1,\n    Diagonal = 2,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum SubgroupOperation {\n    All = 0,\n    Any = 1,\n    Add = 2,\n    Mul = 3,\n    Min = 4,\n    Max = 5,\n    And = 6,\n    Or = 7,\n    Xor = 8,\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum CollectiveOperation {\n    Reduce = 0,\n    InclusiveScan = 1,\n    ExclusiveScan = 2,\n}\n\nbitflags::bitflags! {\n    /// Memory barrier flags.\n    #[cfg_attr(feature = \"serialize\", derive(Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n    #[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\n    pub struct Barrier: u32 {\n        /// Barrier affects all [`AddressSpace::Storage`] accesses.\n        const STORAGE = 1 << 0;\n        /// Barrier affects all [`AddressSpace::WorkGroup`] accesses.\n        const WORK_GROUP = 1 << 1;\n        /// Barrier synchronizes execution across all invocations within a subgroup that execute this instruction.\n        const SUB_GROUP = 1 << 2;\n        /// Barrier synchronizes texture memory accesses in a workgroup.\n        const TEXTURE = 1 << 3;\n    }\n}\n\n#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct CooperativeData {\n    pub pointer: Handle<Expression>,\n    pub stride: Handle<Expression>,\n    pub row_major: bool,\n}\n\n/// An expression that can be evaluated to obtain a value.\n///\n/// This is a Single Static Assignment (SSA) scheme similar to SPIR-V.\n///\n/// When an `Expression` variant holds `Handle<Expression>` fields, they refer\n/// to another expression in the same arena, unless explicitly noted otherwise.\n/// One `Arena<Expression>` may only refer to a different arena indirectly, via\n/// [`Constant`] or [`Override`] expressions, which hold handles for their\n/// respective types.\n///\n/// [`Constant`]: Expression::Constant\n/// [`Override`]: Expression::Override\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Expression {\n    /// Literal.\n    Literal(Literal),\n    /// Constant value.\n    Constant(Handle<Constant>),\n    /// Pipeline-overridable constant.\n    Override(Handle<Override>),\n    /// Zero value of a type.\n    ZeroValue(Handle<Type>),\n    /// Composite expression.\n    Compose {\n        ty: Handle<Type>,\n        components: Vec<Handle<Expression>>,\n    },\n\n    /// Array access with a computed index.\n    ///\n    /// ## Typing rules\n    ///\n    /// The `base` operand must be some composite type: [`Vector`], [`Matrix`],\n    /// [`Array`], a [`Pointer`] to one of those, or a [`ValuePointer`] with a\n    /// `size`.\n    ///\n    /// The `index` operand must be an integer, signed or unsigned.\n    ///\n    /// Indexing a [`Vector`] or [`Array`] produces a value of its element type.\n    /// Indexing a [`Matrix`] produces a [`Vector`].\n    ///\n    /// Indexing a [`Pointer`] to any of the above produces a pointer to the\n    /// element/component type, in the same [`space`]. In the case of [`Array`],\n    /// the result is an actual [`Pointer`], but for vectors and matrices, there\n    /// may not be any type in the arena representing the component's type, so\n    /// those produce [`ValuePointer`] types equivalent to the appropriate\n    /// [`Pointer`].\n    ///\n    /// ## Dynamic indexing restrictions\n    ///\n    /// To accommodate restrictions in some of the shader languages that Naga\n    /// targets, it is not permitted to subscript a matrix with a dynamically\n    /// computed index unless that matrix appears behind a pointer. In other\n    /// words, if the inner type of `base` is [`Matrix`], then `index` must be a\n    /// constant. But if the type of `base` is a [`Pointer`] to an matrix, then\n    /// the index may be any expression of integer type.\n    ///\n    /// You can use the [`Expression::is_dynamic_index`] method to determine\n    /// whether a given index expression requires matrix base operands to be\n    /// behind a pointer.\n    ///\n    /// (It would be simpler to always require the use of `AccessIndex` when\n    /// subscripting matrices that are not behind pointers, but to accommodate\n    /// existing front ends, Naga also permits `Access`, with a restricted\n    /// `index`.)\n    ///\n    /// [`Vector`]: TypeInner::Vector\n    /// [`Matrix`]: TypeInner::Matrix\n    /// [`Array`]: TypeInner::Array\n    /// [`Pointer`]: TypeInner::Pointer\n    /// [`space`]: TypeInner::Pointer::space\n    /// [`ValuePointer`]: TypeInner::ValuePointer\n    /// [`Float`]: ScalarKind::Float\n    Access {\n        base: Handle<Expression>,\n        index: Handle<Expression>,\n    },\n    /// Access the same types as [`Access`], plus [`Struct`] with a known index.\n    ///\n    /// [`Access`]: Expression::Access\n    /// [`Struct`]: TypeInner::Struct\n    AccessIndex {\n        base: Handle<Expression>,\n        index: u32,\n    },\n    /// Splat scalar into a vector.\n    Splat {\n        size: VectorSize,\n        value: Handle<Expression>,\n    },\n    /// Vector swizzle.\n    Swizzle {\n        size: VectorSize,\n        vector: Handle<Expression>,\n        pattern: [SwizzleComponent; 4],\n    },\n\n    /// Reference a function parameter, by its index.\n    ///\n    /// A `FunctionArgument` expression evaluates to the argument's value.\n    FunctionArgument(u32),\n\n    /// Reference a global variable.\n    ///\n    /// If the given `GlobalVariable`'s [`space`] is [`AddressSpace::Handle`],\n    /// then the variable stores some opaque type like a sampler or an image,\n    /// and a `GlobalVariable` expression referring to it produces the\n    /// variable's value directly.\n    ///\n    /// For any other address space, a `GlobalVariable` expression produces a\n    /// pointer to the variable's value. You must use a [`Load`] expression to\n    /// retrieve its value, or a [`Store`] statement to assign it a new value.\n    ///\n    /// [`space`]: GlobalVariable::space\n    /// [`Load`]: Expression::Load\n    /// [`Store`]: Statement::Store\n    GlobalVariable(Handle<GlobalVariable>),\n\n    /// Reference a local variable.\n    ///\n    /// A `LocalVariable` expression evaluates to a pointer to the variable's value.\n    /// You must use a [`Load`](Expression::Load) expression to retrieve its value,\n    /// or a [`Store`](Statement::Store) statement to assign it a new value.\n    LocalVariable(Handle<LocalVariable>),\n\n    /// Load a value indirectly.\n    ///\n    /// For [`TypeInner::Atomic`] the result is a corresponding scalar.\n    /// For other types behind the `pointer<T>`, the result is `T`.\n    Load { pointer: Handle<Expression> },\n    /// Sample a point from a sampled or a depth image.\n    ImageSample {\n        image: Handle<Expression>,\n        sampler: Handle<Expression>,\n        /// If Some(), this operation is a gather operation\n        /// on the selected component.\n        gather: Option<SwizzleComponent>,\n        coordinate: Handle<Expression>,\n        array_index: Option<Handle<Expression>>,\n        /// This must be a const-expression.\n        offset: Option<Handle<Expression>>,\n        level: SampleLevel,\n        depth_ref: Option<Handle<Expression>>,\n        /// Whether the sampling operation should clamp each component of\n        /// `coordinate` to the range `[half_texel, 1 - half_texel]`, regardless\n        /// of `sampler`.\n        clamp_to_edge: bool,\n    },\n\n    /// Load a texel from an image.\n    ///\n    /// For most images, this returns a four-element vector of the same\n    /// [`ScalarKind`] as the image. If the format of the image does not have\n    /// four components, default values are provided: the first three components\n    /// (typically R, G, and B) default to zero, and the final component\n    /// (typically alpha) defaults to one.\n    ///\n    /// However, if the image's [`class`] is [`Depth`], then this returns a\n    /// [`Float`] scalar value.\n    ///\n    /// [`ScalarKind`]: ScalarKind\n    /// [`class`]: TypeInner::Image::class\n    /// [`Depth`]: ImageClass::Depth\n    /// [`Float`]: ScalarKind::Float\n    ImageLoad {\n        /// The image to load a texel from. This must have type [`Image`]. (This\n        /// will necessarily be a [`GlobalVariable`] or [`FunctionArgument`]\n        /// expression, since no other expressions are allowed to have that\n        /// type.)\n        ///\n        /// [`Image`]: TypeInner::Image\n        /// [`GlobalVariable`]: Expression::GlobalVariable\n        /// [`FunctionArgument`]: Expression::FunctionArgument\n        image: Handle<Expression>,\n\n        /// The coordinate of the texel we wish to load. This must be a scalar\n        /// for [`D1`] images, a [`Bi`] vector for [`D2`] images, and a [`Tri`]\n        /// vector for [`D3`] images. (Array indices, sample indices, and\n        /// explicit level-of-detail values are supplied separately.) Its\n        /// component type must be [`Sint`].\n        ///\n        /// [`D1`]: ImageDimension::D1\n        /// [`D2`]: ImageDimension::D2\n        /// [`D3`]: ImageDimension::D3\n        /// [`Bi`]: VectorSize::Bi\n        /// [`Tri`]: VectorSize::Tri\n        /// [`Sint`]: ScalarKind::Sint\n        coordinate: Handle<Expression>,\n\n        /// The index into an arrayed image. If the [`arrayed`] flag in\n        /// `image`'s type is `true`, then this must be `Some(expr)`, where\n        /// `expr` is a [`Sint`] scalar. Otherwise, it must be `None`.\n        ///\n        /// [`arrayed`]: TypeInner::Image::arrayed\n        /// [`Sint`]: ScalarKind::Sint\n        array_index: Option<Handle<Expression>>,\n\n        /// A sample index, for multisampled [`Sampled`] and [`Depth`] images.\n        ///\n        /// [`Sampled`]: ImageClass::Sampled\n        /// [`Depth`]: ImageClass::Depth\n        sample: Option<Handle<Expression>>,\n\n        /// A level of detail, for mipmapped images.\n        ///\n        /// This must be present when accessing non-multisampled\n        /// [`Sampled`] and [`Depth`] images, even if only the\n        /// full-resolution level is present (in which case the only\n        /// valid level is zero).\n        ///\n        /// [`Sampled`]: ImageClass::Sampled\n        /// [`Depth`]: ImageClass::Depth\n        level: Option<Handle<Expression>>,\n    },\n\n    /// Query information from an image.\n    ImageQuery {\n        image: Handle<Expression>,\n        query: ImageQuery,\n    },\n    /// Apply an unary operator.\n    Unary {\n        op: UnaryOperator,\n        expr: Handle<Expression>,\n    },\n    /// Apply a binary operator.\n    Binary {\n        op: BinaryOperator,\n        left: Handle<Expression>,\n        right: Handle<Expression>,\n    },\n    /// Select between two values based on a condition.\n    ///\n    /// Note that, because expressions have no side effects, it is unobservable\n    /// whether the non-selected branch is evaluated.\n    Select {\n        /// Boolean expression\n        condition: Handle<Expression>,\n        accept: Handle<Expression>,\n        reject: Handle<Expression>,\n    },\n    /// Compute the derivative on an axis.\n    Derivative {\n        axis: DerivativeAxis,\n        ctrl: DerivativeControl,\n        expr: Handle<Expression>,\n    },\n    /// Call a relational function.\n    Relational {\n        fun: RelationalFunction,\n        argument: Handle<Expression>,\n    },\n    /// Call a math function\n    Math {\n        fun: MathFunction,\n        arg: Handle<Expression>,\n        arg1: Option<Handle<Expression>>,\n        arg2: Option<Handle<Expression>>,\n        arg3: Option<Handle<Expression>>,\n    },\n    /// Cast a simple type to another kind.\n    As {\n        /// Source expression, which can only be a scalar or a vector.\n        expr: Handle<Expression>,\n        /// Target scalar kind.\n        kind: ScalarKind,\n        /// If provided, converts to the specified byte width.\n        /// Otherwise, bitcast.\n        convert: Option<Bytes>,\n    },\n    /// Result of calling another function.\n    CallResult(Handle<Function>),\n\n    /// Result of an atomic operation.\n    ///\n    /// This expression must be referred to by the [`result`] field of exactly one\n    /// [`Atomic`][stmt] statement somewhere in the same function. Let `T` be the\n    /// scalar type contained by the [`Atomic`][type] value that the statement\n    /// operates on.\n    ///\n    /// If `comparison` is `false`, then `ty` must be the scalar type `T`.\n    ///\n    /// If `comparison` is `true`, then `ty` must be a [`Struct`] with two members:\n    ///\n    /// - A member named `old_value`, whose type is `T`, and\n    ///\n    /// - A member named `exchanged`, of type [`BOOL`].\n    ///\n    /// [`result`]: Statement::Atomic::result\n    /// [stmt]: Statement::Atomic\n    /// [type]: TypeInner::Atomic\n    /// [`Struct`]: TypeInner::Struct\n    /// [`BOOL`]: Scalar::BOOL\n    AtomicResult { ty: Handle<Type>, comparison: bool },\n\n    /// Result of a [`WorkGroupUniformLoad`] statement.\n    ///\n    /// [`WorkGroupUniformLoad`]: Statement::WorkGroupUniformLoad\n    WorkGroupUniformLoadResult {\n        /// The type of the result\n        ty: Handle<Type>,\n    },\n    /// Get the length of an array.\n    /// The expression must resolve to a pointer to an array with a dynamic size.\n    ///\n    /// This doesn't match the semantics of spirv's `OpArrayLength`, which must be passed\n    /// a pointer to a structure containing a runtime array in its' last field.\n    ArrayLength(Handle<Expression>),\n\n    /// Get the Positions of the triangle hit by the [`RayQuery`]\n    ///\n    /// [`RayQuery`]: Statement::RayQuery\n    RayQueryVertexPositions {\n        query: Handle<Expression>,\n        committed: bool,\n    },\n\n    /// Result of a [`Proceed`] [`RayQuery`] statement.\n    ///\n    /// [`Proceed`]: RayQueryFunction::Proceed\n    /// [`RayQuery`]: Statement::RayQuery\n    RayQueryProceedResult,\n\n    /// Return an intersection found by `query`.\n    ///\n    /// If `committed` is true, return the committed result available when\n    RayQueryGetIntersection {\n        query: Handle<Expression>,\n        committed: bool,\n    },\n\n    /// Result of a [`SubgroupBallot`] statement.\n    ///\n    /// [`SubgroupBallot`]: Statement::SubgroupBallot\n    SubgroupBallotResult,\n\n    /// Result of a [`SubgroupCollectiveOperation`] or [`SubgroupGather`] statement.\n    ///\n    /// [`SubgroupCollectiveOperation`]: Statement::SubgroupCollectiveOperation\n    /// [`SubgroupGather`]: Statement::SubgroupGather\n    SubgroupOperationResult { ty: Handle<Type> },\n\n    /// Load a cooperative primitive from memory.\n    CooperativeLoad {\n        columns: CooperativeSize,\n        rows: CooperativeSize,\n        role: CooperativeRole,\n        data: CooperativeData,\n    },\n    /// Compute `a * b + c`\n    CooperativeMultiplyAdd {\n        a: Handle<Expression>,\n        b: Handle<Expression>,\n        c: Handle<Expression>,\n    },\n}\n\n/// The value of the switch case.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum SwitchValue {\n    I32(i32),\n    U32(u32),\n    Default,\n}\n\n/// A case for a switch statement.\n// Clone is used only for error reporting and is not intended for end users\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct SwitchCase {\n    /// Value, upon which the case is considered true.\n    pub value: SwitchValue,\n    /// Body of the case.\n    pub body: Block,\n    /// If true, the control flow continues to the next case in the list,\n    /// or default.\n    pub fall_through: bool,\n}\n\n/// An operation that a [`RayQuery` statement] applies to its [`query`] operand.\n///\n/// [`RayQuery` statement]: Statement::RayQuery\n/// [`query`]: Statement::RayQuery::query\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum RayQueryFunction {\n    /// Initialize the `RayQuery` object.\n    Initialize {\n        /// The acceleration structure within which this query should search for hits.\n        ///\n        /// The expression must be an [`AccelerationStructure`].\n        ///\n        /// [`AccelerationStructure`]: TypeInner::AccelerationStructure\n        acceleration_structure: Handle<Expression>,\n\n        #[allow(rustdoc::private_intra_doc_links)]\n        /// A struct of detailed parameters for the ray query.\n        ///\n        /// This expression should have the struct type given in\n        /// [`SpecialTypes::ray_desc`]. This is available in the WGSL\n        /// front end as the `RayDesc` type.\n        descriptor: Handle<Expression>,\n    },\n\n    /// Start or continue the query given by the statement's [`query`] operand.\n    ///\n    /// After executing this statement, the `result` expression is a\n    /// [`Bool`] scalar indicating whether there are more intersection\n    /// candidates to consider.\n    ///\n    /// [`query`]: Statement::RayQuery::query\n    /// [`Bool`]: ScalarKind::Bool\n    Proceed {\n        result: Handle<Expression>,\n    },\n\n    /// Add a candidate generated intersection to be included\n    /// in the determination of the closest hit for a ray query.\n    GenerateIntersection {\n        hit_t: Handle<Expression>,\n    },\n\n    /// Confirm a triangle intersection to be included in the determination of\n    /// the closest hit for a ray query.\n    ConfirmIntersection,\n\n    Terminate,\n}\n\n//TODO: consider removing `Clone`. It's not valid to clone `Statement::Emit` anyway.\n/// Instructions which make up an executable block.\n///\n/// `Handle<Expression>` and `Range<Expression>` values in `Statement` variants\n/// refer to expressions in [`Function::expressions`], unless otherwise noted.\n// Clone is used only for error reporting and is not intended for end users\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum Statement {\n    /// Emit a range of expressions, visible to all statements that follow in this block.\n    ///\n    /// See the [module-level documentation][emit] for details.\n    ///\n    /// [emit]: index.html#expression-evaluation-time\n    Emit(Range<Expression>),\n    /// A block containing more statements, to be executed sequentially.\n    Block(Block),\n    /// Conditionally executes one of two blocks, based on the value of the condition.\n    ///\n    /// Naga IR does not have \"phi\" instructions. If you need to use\n    /// values computed in an `accept` or `reject` block after the `If`,\n    /// store them in a [`LocalVariable`].\n    If {\n        condition: Handle<Expression>, //bool\n        accept: Block,\n        reject: Block,\n    },\n    /// Conditionally executes one of multiple blocks, based on the value of the selector.\n    ///\n    /// Each case must have a distinct [`value`], exactly one of which must be\n    /// [`Default`]. The `Default` may appear at any position, and covers all\n    /// values not explicitly appearing in other cases. A `Default` appearing in\n    /// the midst of the list of cases does not shadow the cases that follow.\n    ///\n    /// Some backend languages don't support fallthrough (HLSL due to FXC,\n    /// WGSL), and may translate fallthrough cases in the IR by duplicating\n    /// code. However, all backend languages do support cases selected by\n    /// multiple values, like `case 1: case 2: case 3: { ... }`. This is\n    /// represented in the IR as a series of fallthrough cases with empty\n    /// bodies, except for the last.\n    ///\n    /// Naga IR does not have \"phi\" instructions. If you need to use\n    /// values computed in a [`SwitchCase::body`] block after the `Switch`,\n    /// store them in a [`LocalVariable`].\n    ///\n    /// [`value`]: SwitchCase::value\n    /// [`body`]: SwitchCase::body\n    /// [`Default`]: SwitchValue::Default\n    Switch {\n        selector: Handle<Expression>,\n        cases: Vec<SwitchCase>,\n    },\n\n    /// Executes a block repeatedly.\n    ///\n    /// Each iteration of the loop executes the `body` block, followed by the\n    /// `continuing` block.\n    ///\n    /// Executing a [`Break`], [`Return`] or [`Kill`] statement exits the loop.\n    ///\n    /// A [`Continue`] statement in `body` jumps to the `continuing` block. The\n    /// `continuing` block is meant to be used to represent structures like the\n    /// third expression of a C-style `for` loop head, to which `continue`\n    /// statements in the loop's body jump.\n    ///\n    /// The `continuing` block and its substatements must not contain `Return`\n    /// or `Kill` statements, or any `Break` or `Continue` statements targeting\n    /// this loop. (It may have `Break` and `Continue` statements targeting\n    /// loops or switches nested within the `continuing` block.) Expressions\n    /// emitted in `body` are in scope in `continuing`.\n    ///\n    /// If present, `break_if` is an expression which is evaluated after the\n    /// continuing block. Expressions emitted in `body` or `continuing` are\n    /// considered to be in scope. If the expression's value is true, control\n    /// continues after the `Loop` statement, rather than branching back to the\n    /// top of body as usual. The `break_if` expression corresponds to a \"break\n    /// if\" statement in WGSL, or a loop whose back edge is an\n    /// `OpBranchConditional` instruction in SPIR-V.\n    ///\n    /// Naga IR does not have \"phi\" instructions. If you need to use\n    /// values computed in a `body` or `continuing` block after the\n    /// `Loop`, store them in a [`LocalVariable`].\n    ///\n    /// [`Break`]: Statement::Break\n    /// [`Continue`]: Statement::Continue\n    /// [`Kill`]: Statement::Kill\n    /// [`Return`]: Statement::Return\n    /// [`break if`]: Self::Loop::break_if\n    Loop {\n        body: Block,\n        continuing: Block,\n        break_if: Option<Handle<Expression>>,\n    },\n\n    /// Exits the innermost enclosing [`Loop`] or [`Switch`].\n    ///\n    /// A `Break` statement may only appear within a [`Loop`] or [`Switch`]\n    /// statement. It may not break out of a [`Loop`] from within the loop's\n    /// `continuing` block.\n    ///\n    /// [`Loop`]: Statement::Loop\n    /// [`Switch`]: Statement::Switch\n    Break,\n\n    /// Skips to the `continuing` block of the innermost enclosing [`Loop`].\n    ///\n    /// A `Continue` statement may only appear within the `body` block of the\n    /// innermost enclosing [`Loop`] statement. It must not appear within that\n    /// loop's `continuing` block.\n    ///\n    /// [`Loop`]: Statement::Loop\n    Continue,\n\n    /// Returns from the function (possibly with a value).\n    ///\n    /// `Return` statements are forbidden within the `continuing` block of a\n    /// [`Loop`] statement.\n    ///\n    /// [`Loop`]: Statement::Loop\n    Return { value: Option<Handle<Expression>> },\n\n    /// Aborts the current shader execution.\n    ///\n    /// `Kill` statements are forbidden within the `continuing` block of a\n    /// [`Loop`] statement.\n    ///\n    /// [`Loop`]: Statement::Loop\n    Kill,\n\n    /// Synchronize invocations within the work group.\n    /// The `Barrier` flags control which memory accesses should be synchronized.\n    /// If empty, this becomes purely an execution barrier.\n    ControlBarrier(Barrier),\n\n    /// Synchronize invocations within the work group.\n    /// The `Barrier` flags control which memory accesses should be synchronized.\n    MemoryBarrier(Barrier),\n\n    /// Stores a value at an address.\n    ///\n    /// For [`TypeInner::Atomic`] type behind the pointer, the value\n    /// has to be a corresponding scalar.\n    /// For other types behind the `pointer<T>`, the value is `T`.\n    ///\n    /// This statement is a barrier for any operations on the\n    /// `Expression::LocalVariable` or `Expression::GlobalVariable`\n    /// that is the destination of an access chain, started\n    /// from the `pointer`.\n    Store {\n        pointer: Handle<Expression>,\n        value: Handle<Expression>,\n    },\n    /// Stores a texel value to an image.\n    ///\n    /// The `image`, `coordinate`, and `array_index` fields have the same\n    /// meanings as the corresponding operands of an [`ImageLoad`] expression;\n    /// see that documentation for details. Storing into multisampled images or\n    /// images with mipmaps is not supported, so there are no `level` or\n    /// `sample` operands.\n    ///\n    /// This statement is a barrier for any operations on the corresponding\n    /// [`Expression::GlobalVariable`] for this image.\n    ///\n    /// [`ImageLoad`]: Expression::ImageLoad\n    ImageStore {\n        image: Handle<Expression>,\n        coordinate: Handle<Expression>,\n        array_index: Option<Handle<Expression>>,\n        value: Handle<Expression>,\n    },\n    /// Atomic function.\n    Atomic {\n        /// Pointer to an atomic value.\n        ///\n        /// This must be a [`Pointer`] to an [`Atomic`] value. The atomic's\n        /// scalar type may be [`I32`] or [`U32`].\n        ///\n        /// If [`SHADER_INT64_ATOMIC_MIN_MAX`] or [`SHADER_INT64_ATOMIC_ALL_OPS`] are\n        /// enabled, this may also be [`I64`] or [`U64`].\n        ///\n        /// If [`SHADER_FLOAT32_ATOMIC`] is enabled, this may be [`F32`].\n        ///\n        /// [`Pointer`]: TypeInner::Pointer\n        /// [`Atomic`]: TypeInner::Atomic\n        /// [`I32`]: Scalar::I32\n        /// [`U32`]: Scalar::U32\n        /// [`SHADER_INT64_ATOMIC_MIN_MAX`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX\n        /// [`SHADER_INT64_ATOMIC_ALL_OPS`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS\n        /// [`SHADER_FLOAT32_ATOMIC`]: crate::valid::Capabilities::SHADER_FLOAT32_ATOMIC\n        /// [`I64`]: Scalar::I64\n        /// [`U64`]: Scalar::U64\n        /// [`F32`]: Scalar::F32\n        pointer: Handle<Expression>,\n\n        /// Function to run on the atomic value.\n        ///\n        /// If [`pointer`] refers to a 64-bit atomic value, then:\n        ///\n        /// - The [`SHADER_INT64_ATOMIC_ALL_OPS`] capability allows any [`AtomicFunction`]\n        ///   value here.\n        ///\n        /// - The [`SHADER_INT64_ATOMIC_MIN_MAX`] capability allows\n        ///   [`AtomicFunction::Min`] and [`AtomicFunction::Max`]\n        ///   in the [`Storage`] address space here.\n        ///\n        /// - If neither of those capabilities are present, then 64-bit scalar\n        ///   atomics are not allowed.\n        ///\n        /// If [`pointer`] refers to a 32-bit floating-point atomic value, then:\n        ///\n        /// - The [`SHADER_FLOAT32_ATOMIC`] capability allows [`AtomicFunction::Add`],\n        ///   [`AtomicFunction::Subtract`], and [`AtomicFunction::Exchange { compare: None }`]\n        ///   in the [`Storage`] address space here.\n        ///\n        /// [`AtomicFunction::Exchange { compare: None }`]: AtomicFunction::Exchange\n        /// [`pointer`]: Statement::Atomic::pointer\n        /// [`Storage`]: AddressSpace::Storage\n        /// [`SHADER_INT64_ATOMIC_MIN_MAX`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX\n        /// [`SHADER_INT64_ATOMIC_ALL_OPS`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS\n        /// [`SHADER_FLOAT32_ATOMIC`]: crate::valid::Capabilities::SHADER_FLOAT32_ATOMIC\n        fun: AtomicFunction,\n\n        /// Value to use in the function.\n        ///\n        /// This must be a scalar of the same type as [`pointer`]'s atomic's scalar type.\n        ///\n        /// [`pointer`]: Statement::Atomic::pointer\n        value: Handle<Expression>,\n\n        /// [`AtomicResult`] expression representing this function's result.\n        ///\n        /// If [`fun`] is [`Exchange { compare: None }`], this must be `Some`,\n        /// as otherwise that operation would be equivalent to a simple [`Store`]\n        /// to the atomic.\n        ///\n        /// Otherwise, this may be `None` if the return value of the operation is not needed.\n        ///\n        /// If `pointer` refers to a 64-bit atomic value, [`SHADER_INT64_ATOMIC_MIN_MAX`]\n        /// is enabled, and [`SHADER_INT64_ATOMIC_ALL_OPS`] is not, this must be `None`.\n        ///\n        /// [`AtomicResult`]: crate::Expression::AtomicResult\n        /// [`fun`]: Statement::Atomic::fun\n        /// [`Store`]: Statement::Store\n        /// [`Exchange { compare: None }`]: AtomicFunction::Exchange\n        /// [`SHADER_INT64_ATOMIC_MIN_MAX`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX\n        /// [`SHADER_INT64_ATOMIC_ALL_OPS`]: crate::valid::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS\n        result: Option<Handle<Expression>>,\n    },\n    /// Performs an atomic operation on a texel value of an image.\n    ///\n    /// Doing atomics on images with mipmaps is not supported, so there is no\n    /// `level` operand.\n    ImageAtomic {\n        /// The image to perform an atomic operation on. This must have type\n        /// [`Image`]. (This will necessarily be a [`GlobalVariable`] or\n        /// [`FunctionArgument`] expression, since no other expressions are\n        /// allowed to have that type.)\n        ///\n        /// [`Image`]: TypeInner::Image\n        /// [`GlobalVariable`]: Expression::GlobalVariable\n        /// [`FunctionArgument`]: Expression::FunctionArgument\n        image: Handle<Expression>,\n\n        /// The coordinate of the texel we wish to load. This must be a scalar\n        /// for [`D1`] images, a [`Bi`] vector for [`D2`] images, and a [`Tri`]\n        /// vector for [`D3`] images. (Array indices, sample indices, and\n        /// explicit level-of-detail values are supplied separately.) Its\n        /// component type must be [`Sint`].\n        ///\n        /// [`D1`]: ImageDimension::D1\n        /// [`D2`]: ImageDimension::D2\n        /// [`D3`]: ImageDimension::D3\n        /// [`Bi`]: VectorSize::Bi\n        /// [`Tri`]: VectorSize::Tri\n        /// [`Sint`]: ScalarKind::Sint\n        coordinate: Handle<Expression>,\n\n        /// The index into an arrayed image. If the [`arrayed`] flag in\n        /// `image`'s type is `true`, then this must be `Some(expr)`, where\n        /// `expr` is a [`Sint`] scalar. Otherwise, it must be `None`.\n        ///\n        /// [`arrayed`]: TypeInner::Image::arrayed\n        /// [`Sint`]: ScalarKind::Sint\n        array_index: Option<Handle<Expression>>,\n\n        /// The kind of atomic operation to perform on the texel.\n        fun: AtomicFunction,\n\n        /// The value with which to perform the atomic operation.\n        value: Handle<Expression>,\n    },\n    /// Load uniformly from a uniform pointer in the workgroup address space.\n    ///\n    /// Corresponds to the [`workgroupUniformLoad`](https://www.w3.org/TR/WGSL/#workgroupUniformLoad-builtin)\n    /// built-in function of wgsl, and has the same barrier semantics\n    WorkGroupUniformLoad {\n        /// This must be of type [`Pointer`] in the [`WorkGroup`] address space\n        ///\n        /// [`Pointer`]: TypeInner::Pointer\n        /// [`WorkGroup`]: AddressSpace::WorkGroup\n        pointer: Handle<Expression>,\n        /// The [`WorkGroupUniformLoadResult`] expression representing this load's result.\n        ///\n        /// [`WorkGroupUniformLoadResult`]: Expression::WorkGroupUniformLoadResult\n        result: Handle<Expression>,\n    },\n    /// Calls a function.\n    ///\n    /// If the `result` is `Some`, the corresponding expression has to be\n    /// `Expression::CallResult`, and this statement serves as a barrier for any\n    /// operations on that expression.\n    Call {\n        function: Handle<Function>,\n        arguments: Vec<Handle<Expression>>,\n        result: Option<Handle<Expression>>,\n    },\n    RayQuery {\n        /// The [`RayQuery`] object this statement operates on.\n        ///\n        /// [`RayQuery`]: TypeInner::RayQuery\n        query: Handle<Expression>,\n\n        /// The specific operation we're performing on `query`.\n        fun: RayQueryFunction,\n    },\n    /// A ray tracing pipeline shader intrinsic.\n    RayPipelineFunction(RayPipelineFunction),\n    /// Calculate a bitmask using a boolean from each active thread in the subgroup\n    SubgroupBallot {\n        /// The [`SubgroupBallotResult`] expression representing this load's result.\n        ///\n        /// [`SubgroupBallotResult`]: Expression::SubgroupBallotResult\n        result: Handle<Expression>,\n        /// The value from this thread to store in the ballot\n        predicate: Option<Handle<Expression>>,\n    },\n    /// Gather a value from another active thread in the subgroup\n    SubgroupGather {\n        /// Specifies which thread to gather from\n        mode: GatherMode,\n        /// The value to broadcast over\n        argument: Handle<Expression>,\n        /// The [`SubgroupOperationResult`] expression representing this load's result.\n        ///\n        /// [`SubgroupOperationResult`]: Expression::SubgroupOperationResult\n        result: Handle<Expression>,\n    },\n    /// Compute a collective operation across all active threads in the subgroup\n    SubgroupCollectiveOperation {\n        /// What operation to compute\n        op: SubgroupOperation,\n        /// How to combine the results\n        collective_op: CollectiveOperation,\n        /// The value to compute over\n        argument: Handle<Expression>,\n        /// The [`SubgroupOperationResult`] expression representing this load's result.\n        ///\n        /// [`SubgroupOperationResult`]: Expression::SubgroupOperationResult\n        result: Handle<Expression>,\n    },\n    /// Store a cooperative primitive into memory.\n    CooperativeStore {\n        target: Handle<Expression>,\n        data: CooperativeData,\n    },\n}\n\n/// A function argument.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct FunctionArgument {\n    /// Name of the argument, if any.\n    pub name: Option<String>,\n    /// Type of the argument.\n    pub ty: Handle<Type>,\n    /// For entry points, an argument has to have a binding\n    /// unless it's a structure.\n    pub binding: Option<Binding>,\n}\n\n/// A function result.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct FunctionResult {\n    /// Type of the result.\n    pub ty: Handle<Type>,\n    /// For entry points, the result has to have a binding\n    /// unless it's a structure.\n    pub binding: Option<Binding>,\n}\n\n/// A function defined in the module.\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Function {\n    /// Name of the function, if any.\n    pub name: Option<String>,\n    /// Information about function argument.\n    pub arguments: Vec<FunctionArgument>,\n    /// The result of this function, if any.\n    pub result: Option<FunctionResult>,\n    /// Local variables defined and used in the function.\n    pub local_variables: Arena<LocalVariable>,\n    /// Expressions used inside this function.\n    ///\n    /// Unless explicitly stated otherwise, if an [`Expression`] is in this\n    /// arena, then its subexpressions are in this arena too. In other words,\n    /// every `Handle<Expression>` in this arena refers to an [`Expression`] in\n    /// this arena too.\n    ///\n    /// The main ways this arena refers to [`Module::global_expressions`] are:\n    ///\n    /// - [`Constant`], [`Override`], and [`GlobalVariable`] expressions hold\n    ///   handles for their respective types, whose initializer expressions are\n    ///   in [`Module::global_expressions`].\n    ///\n    /// - Various expressions hold [`Type`] handles, and [`Type`]s may refer to\n    ///   global expressions, for things like array lengths.\n    ///\n    /// An [`Expression`] must occur before all other [`Expression`]s that use\n    /// its value.\n    ///\n    /// [`Constant`]: Expression::Constant\n    /// [`Override`]: Expression::Override\n    /// [`GlobalVariable`]: Expression::GlobalVariable\n    pub expressions: Arena<Expression>,\n    /// Map of expressions that have associated variable names\n    pub named_expressions: NamedExpressions,\n    /// Block of instructions comprising the body of the function.\n    pub body: Block,\n    /// The leaf of all diagnostic filter rules tree (stored in [`Module::diagnostic_filters`])\n    /// parsed on this function.\n    ///\n    /// In WGSL, this corresponds to `@diagnostic(…)` attributes.\n    ///\n    /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in\n    /// validation.\n    pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n}\n\n/// The main function for a pipeline stage.\n///\n/// An [`EntryPoint`] is a [`Function`] that serves as the main function for a\n/// graphics or compute pipeline stage. For example, an `EntryPoint` whose\n/// [`stage`] is [`ShaderStage::Vertex`] can serve as a graphics pipeline's\n/// vertex shader.\n///\n/// Since an entry point is called directly by the graphics or compute pipeline,\n/// not by other WGSL functions, you must specify what the pipeline should pass\n/// as the entry point's arguments, and what values it will return. For example,\n/// a vertex shader needs a vertex's attributes as its arguments, but if it's\n/// used for instanced draw calls, it will also want to know the instance id.\n/// The vertex shader's return value will usually include an output vertex\n/// position, and possibly other attributes to be interpolated and passed along\n/// to a fragment shader.\n///\n/// To specify this, the arguments and result of an `EntryPoint`'s [`function`]\n/// must each have a [`Binding`], or be structs whose members all have\n/// `Binding`s. This associates every value passed to or returned from the entry\n/// point with either a [`BuiltIn`] or a [`Location`]:\n///\n/// -   A [`BuiltIn`] has special semantics, usually specific to its pipeline\n///     stage. For example, the result of a vertex shader can include a\n///     [`BuiltIn::Position`] value, which determines the position of a vertex\n///     of a rendered primitive. Or, a compute shader might take an argument\n///     whose binding is [`BuiltIn::WorkGroupSize`], through which the compute\n///     pipeline would pass the number of invocations in your workgroup.\n///\n/// -   A [`Location`] indicates user-defined IO to be passed from one pipeline\n///     stage to the next. For example, a vertex shader might also produce a\n///     `uv` texture location as a user-defined IO value.\n///\n/// In other words, the pipeline stage's input and output interface are\n/// determined by the bindings of the arguments and result of the `EntryPoint`'s\n/// [`function`].\n///\n/// [`Function`]: crate::Function\n/// [`Location`]: Binding::Location\n/// [`function`]: EntryPoint::function\n/// [`stage`]: EntryPoint::stage\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct EntryPoint {\n    /// Name of this entry point, visible externally.\n    ///\n    /// Entry point names for a given `stage` must be distinct within a module.\n    pub name: String,\n    /// Shader stage.\n    pub stage: ShaderStage,\n    /// Early depth test for fragment stages.\n    pub early_depth_test: Option<EarlyDepthTest>,\n    /// Workgroup size for compute stages\n    pub workgroup_size: [u32; 3],\n    /// Override expressions for workgroup size in the global_expressions arena\n    pub workgroup_size_overrides: Option<[Option<Handle<Expression>>; 3]>,\n    /// The entrance function.\n    pub function: Function,\n    /// Information for [`Mesh`] shaders.\n    ///\n    /// [`Mesh`]: ShaderStage::Mesh\n    pub mesh_info: Option<MeshStageInfo>,\n    /// The unique global variable used as a task payload from task shader to mesh shader\n    pub task_payload: Option<Handle<GlobalVariable>>,\n    /// The unique global variable used as an incoming ray payload going into any hit, closest hit and miss shaders.\n    /// Unlike the outgoing ray payload, an incoming ray payload must be unique\n    pub incoming_ray_payload: Option<Handle<GlobalVariable>>,\n}\n\n/// Return types predeclared for the frexp, modf, and atomicCompareExchangeWeak built-in functions.\n///\n/// These cannot be spelled in WGSL source.\n///\n/// Stored in [`SpecialTypes::predeclared_types`] and created by [`Module::generate_predeclared_type`].\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum PredeclaredType {\n    AtomicCompareExchangeWeakResult(Scalar),\n    ModfResult {\n        size: Option<VectorSize>,\n        scalar: Scalar,\n    },\n    FrexpResult {\n        size: Option<VectorSize>,\n        scalar: Scalar,\n    },\n}\n\n/// Set of special types that can be optionally generated by the frontends.\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct SpecialTypes {\n    /// Type for `RayDesc`.\n    ///\n    /// Call [`Module::generate_ray_desc_type`] to populate this if\n    /// needed and return the handle.\n    pub ray_desc: Option<Handle<Type>>,\n\n    /// Type for `RayIntersection`.\n    ///\n    /// Call [`Module::generate_ray_intersection_type`] to populate\n    /// this if needed and return the handle.\n    pub ray_intersection: Option<Handle<Type>>,\n\n    /// Type for `RayVertexReturn`.\n    ///\n    /// Call [`Module::generate_vertex_return_type`]\n    pub ray_vertex_return: Option<Handle<Type>>,\n\n    /// Struct containing parameters required by some backends to emit code for\n    /// [`ImageClass::External`] textures.\n    ///\n    /// See `wgpu_core::device::resource::ExternalTextureParams` for the\n    /// documentation of each field.\n    ///\n    /// In WGSL, this type would be:\n    ///\n    /// ```ignore\n    /// struct NagaExternalTextureParams {         // align size offset\n    ///     yuv_conversion_matrix: mat4x4<f32>,    //    16   64      0\n    ///     gamut_conversion_matrix: mat3x3<f32>,  //    16   48     64\n    ///     src_tf: NagaExternalTextureTransferFn, //     4   16    112\n    ///     dst_tf: NagaExternalTextureTransferFn, //     4   16    128\n    ///     sample_transform: mat3x2<f32>,         //     8   24    144\n    ///     load_transform: mat3x2<f32>,           //     8   24    168\n    ///     size: vec2<u32>,                       //     8    8    192\n    ///     num_planes: u32,                       //     4    4    200\n    /// }                            // whole struct:    16  208\n    /// ```\n    ///\n    /// Call [`Module::generate_external_texture_types`] to populate this if\n    /// needed.\n    pub external_texture_params: Option<Handle<Type>>,\n\n    /// Struct describing a gamma encoding transfer function. Member of\n    /// `NagaExternalTextureParams`, describing how the backend should perform\n    /// color space conversion when sampling from [`ImageClass::External`]\n    /// textures.\n    ///\n    /// In WGSL, this type would be:\n    ///\n    /// ```ignore\n    /// struct NagaExternalTextureTransferFn { // align size offset\n    ///     a: f32,                            //     4    4      0\n    ///     b: f32,                            //     4    4      4\n    ///     g: f32,                            //     4    4      8\n    ///     k: f32,                            //     4    4     12\n    /// }                         // whole struct:    4   16\n    /// ```\n    ///\n    /// Call [`Module::generate_external_texture_types`] to populate this if\n    /// needed.\n    pub external_texture_transfer_function: Option<Handle<Type>>,\n\n    /// Types for predeclared wgsl types instantiated on demand.\n    ///\n    /// Call [`Module::generate_predeclared_type`] to populate this if\n    /// needed and return the handle.\n    pub predeclared_types: FastIndexMap<PredeclaredType, Handle<Type>>,\n}\n\nbitflags::bitflags! {\n    /// Ray flags used when casting rays.\n    /// Matching vulkan constants can be found in\n    /// https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/ray_common/ray_flags_section.txt\n    #[cfg_attr(feature = \"serialize\", derive(Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n    #[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]\n    pub struct RayFlag: u32 {\n        /// Force all intersections to be treated as opaque.\n        const FORCE_OPAQUE = 0x1;\n        /// Force all intersections to be treated as non-opaque.\n        const FORCE_NO_OPAQUE = 0x2;\n        /// Stop traversal after the first hit.\n        const TERMINATE_ON_FIRST_HIT = 0x4;\n        /// Don't execute the closest hit shader.\n        const SKIP_CLOSEST_HIT_SHADER = 0x8;\n        /// Cull back facing geometry.\n        const CULL_BACK_FACING = 0x10;\n        /// Cull front facing geometry.\n        const CULL_FRONT_FACING = 0x20;\n        /// Cull opaque geometry.\n        const CULL_OPAQUE = 0x40;\n        /// Cull non-opaque geometry.\n        const CULL_NO_OPAQUE = 0x80;\n        /// Skip triangular geometry.\n        const SKIP_TRIANGLES = 0x100;\n        /// Skip axis-aligned bounding boxes.\n        const SKIP_AABBS = 0x200;\n    }\n}\n\n/// Type of a ray query intersection.\n/// Matching vulkan constants can be found in\n/// <https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/SPV_KHR_ray_query.asciidoc>\n/// but the actual values are different for candidate intersections.\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]\npub enum RayQueryIntersection {\n    /// No intersection found.\n    /// Matches `RayQueryCommittedIntersectionNoneKHR`.\n    #[default]\n    None = 0,\n    /// Intersecting with triangles.\n    /// Matches `RayQueryCommittedIntersectionTriangleKHR` and `RayQueryCandidateIntersectionTriangleKHR`.\n    Triangle = 1,\n    /// Intersecting with generated primitives.\n    /// Matches `RayQueryCommittedIntersectionGeneratedKHR`.\n    Generated = 2,\n    /// Intersecting with Axis Aligned Bounding Boxes.\n    /// Matches `RayQueryCandidateIntersectionAABBKHR`.\n    Aabb = 3,\n}\n\n/// Doc comments preceding items.\n///\n/// These can be used to generate automated documentation,\n/// IDE hover information or translate shaders with their context comments.\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct DocComments {\n    pub types: FastIndexMap<Handle<Type>, Vec<String>>,\n    // The key is:\n    // - key.0: the handle to the Struct\n    // - key.1: the index of the `StructMember`.\n    pub struct_members: FastIndexMap<(Handle<Type>, usize), Vec<String>>,\n    pub entry_points: FastIndexMap<usize, Vec<String>>,\n    pub functions: FastIndexMap<Handle<Function>, Vec<String>>,\n    pub constants: FastIndexMap<Handle<Constant>, Vec<String>>,\n    pub global_variables: FastIndexMap<Handle<GlobalVariable>, Vec<String>>,\n    // Top level comments, appearing before any space.\n    pub module: Vec<String>,\n}\n\n/// The output topology for a mesh shader. Note that mesh shaders don't allow things like triangle-strips.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum MeshOutputTopology {\n    /// Outputs individual vertices to be rendered as points.\n    Points,\n    /// Outputs groups of 2 vertices to be renderedas lines .\n    Lines,\n    /// Outputs groups of 3 vertices to be rendered as triangles.\n    Triangles,\n}\n\n/// Information specific to mesh shader entry points.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\n#[allow(dead_code)]\npub struct MeshStageInfo {\n    /// The type of primitive outputted.\n    pub topology: MeshOutputTopology,\n    /// The maximum number of vertices a mesh shader may output.\n    pub max_vertices: u32,\n    /// If pipeline constants are used, the expressions that override `max_vertices`\n    pub max_vertices_override: Option<Handle<Expression>>,\n    /// The maximum number of primitives a mesh shader may output.\n    pub max_primitives: u32,\n    /// If pipeline constants are used, the expressions that override `max_primitives`\n    pub max_primitives_override: Option<Handle<Expression>>,\n    /// The type used by vertex outputs, i.e. what is passed to `setVertex`.\n    pub vertex_output_type: Handle<Type>,\n    /// The type used by primitive outputs, i.e. what is passed to `setPrimitive`.\n    pub primitive_output_type: Handle<Type>,\n    /// The global variable holding the outputted vertices, primitives, and counts\n    pub output_variable: Handle<GlobalVariable>,\n}\n\n/// Ray tracing pipeline intrinsics\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub enum RayPipelineFunction {\n    /// Traces a ray through the given acceleration structure\n    TraceRay {\n        /// The acceleration structure within which this ray should search for hits.\n        ///\n        /// The expression must be an [`AccelerationStructure`].\n        ///\n        /// [`AccelerationStructure`]: TypeInner::AccelerationStructure\n        acceleration_structure: Handle<Expression>,\n\n        #[allow(rustdoc::private_intra_doc_links)]\n        /// A struct of detailed parameters for the ray query.\n        ///\n        /// This expression should have the struct type given in\n        /// [`SpecialTypes::ray_desc`]. This is available in the WGSL\n        /// front end as the `RayDesc` type.\n        descriptor: Handle<Expression>,\n\n        /// A pointer in the ray_payload or incoming_ray_payload address spaces\n        payload: Handle<Expression>,\n        // Do we want miss index? What about sbt offset and sbt stride (could be hard to validate)?\n        // https://github.com/gfx-rs/wgpu/issues/8894\n    },\n}\n\n/// Shader module.\n///\n/// A module is a set of constants, global variables and functions, as well as\n/// the types required to define them.\n///\n/// Some functions are marked as entry points, to be used in a certain shader stage.\n///\n/// To create a new module, use the `Default` implementation.\n/// Alternatively, you can load an existing shader using one of the [available front ends].\n///\n/// When finished, you can export modules using one of the [available backends].\n///\n/// ## Module arenas\n///\n/// Most module contents are stored in [`Arena`]s. In a valid module, arena\n/// elements only refer to prior arena elements. That is, whenever an element in\n/// some `Arena<T>` contains a `Handle<T>` referring to another element the same\n/// arena, the handle's referent always precedes the element containing the\n/// handle.\n///\n/// The elements of [`Module::types`] may refer to [`Expression`]s in\n/// [`Module::global_expressions`], and those expressions may in turn refer back\n/// to [`Type`]s in [`Module::types`]. In a valid module, there exists an order\n/// in which all types and global expressions can be visited such that:\n///\n/// - types and expressions are visited in the order in which they appear in\n///   their arenas, and\n///\n/// - every element refers only to previously visited elements.\n///\n/// This implies that the graph of types and global expressions is acyclic.\n/// (However, it is a stronger condition: there are cycle-free arrangements of\n/// types and expressions for which an order like the one described above does\n/// not exist. Modules arranged in such a way are not valid.)\n///\n/// [available front ends]: crate::front\n/// [available backends]: crate::back\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(Arbitrary))]\npub struct Module {\n    /// Arena for the types defined in this module.\n    ///\n    /// See the [`Module`] docs for more details about this field.\n    pub types: UniqueArena<Type>,\n    /// Dictionary of special type handles.\n    pub special_types: SpecialTypes,\n    /// Arena for the constants defined in this module.\n    pub constants: Arena<Constant>,\n    /// Arena for the pipeline-overridable constants defined in this module.\n    pub overrides: Arena<Override>,\n    /// Arena for the global variables defined in this module.\n    pub global_variables: Arena<GlobalVariable>,\n    /// [Constant expressions] and [override expressions] used by this module.\n    ///\n    /// If an expression is in this arena, then its subexpressions are in this\n    /// arena too. In other words, every `Handle<Expression>` in this arena\n    /// refers to an [`Expression`] in this arena too.\n    ///\n    /// See the [`Module`] docs for more details about this field.\n    ///\n    /// [Constant expressions]: index.html#constant-expressions\n    /// [override expressions]: index.html#override-expressions\n    pub global_expressions: Arena<Expression>,\n    /// Arena for the functions defined in this module.\n    ///\n    /// Each function must appear in this arena strictly before all its callers.\n    /// Recursion is not supported.\n    pub functions: Arena<Function>,\n    /// Entry points.\n    pub entry_points: Vec<EntryPoint>,\n    /// Arena for all diagnostic filter rules parsed in this module, including those in functions\n    /// and statements.\n    ///\n    /// This arena contains elements of a _tree_ of diagnostic filter rules. When nodes are built\n    /// by a front-end, they refer to a parent scope\n    pub diagnostic_filters: Arena<DiagnosticFilterNode>,\n    /// The leaf of all diagnostic filter rules tree parsed from directives in this module.\n    ///\n    /// In WGSL, this corresponds to `diagnostic(…);` directives.\n    ///\n    /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in\n    /// validation.\n    pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n    /// Doc comments.\n    pub doc_comments: Option<Box<DocComments>>,\n}\n"
  },
  {
    "path": "naga/src/keywords/mod.rs",
    "content": "/*!\nLists of reserved keywords for each shading language with a [frontend][crate::front] or [backend][crate::back].\n*/\n\n#[cfg(any(feature = \"wgsl-in\", wgsl_out))]\npub mod wgsl;\n"
  },
  {
    "path": "naga/src/keywords/wgsl.rs",
    "content": "/*!\nKeywords for [WGSL][wgsl] (WebGPU Shading Language).\n\n[wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html\n*/\n\nuse crate::proc::KeywordSet;\nuse crate::racy_lock::RacyLock;\n\n// last sync: https://www.w3.org/TR/2025/CRD-WGSL-20250809/#keyword-summary\npub const RESERVED: &[&str] = &[\n    // Keywords\n    \"alias\",\n    \"break\",\n    \"case\",\n    \"const\",\n    \"const_assert\",\n    \"continue\",\n    \"continuing\",\n    \"default\",\n    \"diagnostic\",\n    \"discard\",\n    \"else\",\n    \"enable\",\n    \"false\",\n    \"fn\",\n    \"for\",\n    \"if\",\n    \"let\",\n    \"loop\",\n    \"override\",\n    \"requires\",\n    \"return\",\n    \"struct\",\n    \"switch\",\n    \"true\",\n    \"var\",\n    \"while\",\n    // Reserved\n    \"NULL\",\n    \"Self\",\n    \"abstract\",\n    \"active\",\n    \"alignas\",\n    \"alignof\",\n    \"as\",\n    \"asm\",\n    \"asm_fragment\",\n    \"async\",\n    \"attribute\",\n    \"auto\",\n    \"await\",\n    \"become\",\n    \"cast\",\n    \"catch\",\n    \"class\",\n    \"co_await\",\n    \"co_return\",\n    \"co_yield\",\n    \"coherent\",\n    \"column_major\",\n    \"common\",\n    \"compile\",\n    \"compile_fragment\",\n    \"concept\",\n    \"const_cast\",\n    \"consteval\",\n    \"constexpr\",\n    \"constinit\",\n    \"crate\",\n    \"debugger\",\n    \"decltype\",\n    \"delete\",\n    \"demote\",\n    \"demote_to_helper\",\n    \"do\",\n    \"dynamic_cast\",\n    \"enum\",\n    \"explicit\",\n    \"export\",\n    \"extends\",\n    \"extern\",\n    \"external\",\n    \"fallthrough\",\n    \"filter\",\n    \"final\",\n    \"finally\",\n    \"friend\",\n    \"from\",\n    \"fxgroup\",\n    \"get\",\n    \"goto\",\n    \"groupshared\",\n    \"highp\",\n    \"impl\",\n    \"implements\",\n    \"import\",\n    \"inline\",\n    \"instanceof\",\n    \"interface\",\n    \"layout\",\n    \"lowp\",\n    \"macro\",\n    \"macro_rules\",\n    \"match\",\n    \"mediump\",\n    \"meta\",\n    \"mod\",\n    \"module\",\n    \"move\",\n    \"mut\",\n    \"mutable\",\n    \"namespace\",\n    \"new\",\n    \"nil\",\n    \"noexcept\",\n    \"noinline\",\n    \"nointerpolation\",\n    \"non_coherent\",\n    \"noncoherent\",\n    \"noperspective\",\n    \"null\",\n    \"nullptr\",\n    \"of\",\n    \"operator\",\n    \"package\",\n    \"packoffset\",\n    \"partition\",\n    \"pass\",\n    \"patch\",\n    \"pixelfragment\",\n    \"precise\",\n    \"precision\",\n    \"premerge\",\n    \"priv\",\n    \"protected\",\n    \"pub\",\n    \"public\",\n    \"readonly\",\n    \"ref\",\n    \"regardless\",\n    \"register\",\n    \"reinterpret_cast\",\n    \"require\",\n    \"resource\",\n    \"restrict\",\n    \"self\",\n    \"set\",\n    \"shared\",\n    \"sizeof\",\n    \"smooth\",\n    \"snorm\",\n    \"static\",\n    \"static_assert\",\n    \"static_cast\",\n    \"std\",\n    \"subroutine\",\n    \"super\",\n    \"target\",\n    \"template\",\n    \"this\",\n    \"thread_local\",\n    \"throw\",\n    \"trait\",\n    \"try\",\n    \"type\",\n    \"typedef\",\n    \"typeid\",\n    \"typename\",\n    \"typeof\",\n    \"union\",\n    \"unless\",\n    \"unorm\",\n    \"unsafe\",\n    \"unsized\",\n    \"use\",\n    \"using\",\n    \"varying\",\n    \"virtual\",\n    \"volatile\",\n    \"wgsl\",\n    \"where\",\n    \"with\",\n    \"writeonly\",\n    \"yield\",\n];\n\n/// The above set of reserved keywords, turned into a cached HashSet. This saves\n/// significant time during [`Namer::reset`](crate::proc::Namer::reset).\n///\n/// See <https://github.com/gfx-rs/wgpu/pull/7338> for benchmarks.\npub static RESERVED_SET: RacyLock<KeywordSet> = RacyLock::new(|| KeywordSet::from_iter(RESERVED));\n\n/// Shadowable words that the WGSL backend should avoid using for declarations.\n///\n/// Includes:\n/// - [6.9. Predeclared Types and Type-Generators]\n/// - [6.3.1. Predeclared enumerants]\n/// - [17. Built-in Functions]\n///\n/// This set must be separate from the [`RESERVED`] set above since the\n/// [`Namer`](crate::proc::Namer) must ignore these identifiers if they appear\n/// as struct member names. This is because this set contains `fract` and `exp`\n/// which are also names used by return types of the `frexp` and `modf` built-in functions.\n///\n/// [6.9. Predeclared Types and Type-Generators]: https://www.w3.org/TR/WGSL/#predeclared-types\n/// [6.3.1. Predeclared enumerants]: https://www.w3.org/TR/WGSL/#predeclared-enumerants\n/// [17. Built-in Functions]: https://www.w3.org/TR/WGSL/#builtin-functions\npub const BUILTIN_IDENTIFIERS: &[&str] = &[\n    // types\n    \"bool\",\n    \"i32\",\n    \"u32\",\n    \"f32\",\n    \"f16\",\n    \"array\",\n    \"atomic\",\n    \"vec2\",\n    \"vec3\",\n    \"vec4\",\n    \"mat2x2\",\n    \"mat2x3\",\n    \"mat2x4\",\n    \"mat3x2\",\n    \"mat3x3\",\n    \"mat3x4\",\n    \"mat4x2\",\n    \"mat4x3\",\n    \"mat4x4\",\n    \"ptr\",\n    \"sampler\",\n    \"sampler_comparison\",\n    \"texture_1d\",\n    \"texture_2d\",\n    \"texture_2d_array\",\n    \"texture_3d\",\n    \"texture_cube\",\n    \"texture_cube_array\",\n    \"texture_multisampled_2d\",\n    \"texture_depth_multisampled_2d\",\n    \"texture_external\",\n    \"texture_storage_1d\",\n    \"texture_storage_2d\",\n    \"texture_storage_2d_array\",\n    \"texture_storage_3d\",\n    \"texture_depth_2d\",\n    \"texture_depth_2d_array\",\n    \"texture_depth_cube\",\n    \"texture_depth_cube_array\",\n    // enumerants\n    \"read\",\n    \"write\",\n    \"read_write\",\n    \"function\",\n    \"private\",\n    \"workgroup\",\n    \"uniform\",\n    \"storage\",\n    \"rgba8unorm\",\n    \"rgba8snorm\",\n    \"rgba8uint\",\n    \"rgba8sint\",\n    \"rgba16unorm\",\n    \"rgba16snorm\",\n    \"rgba16uint\",\n    \"rgba16sint\",\n    \"rgba16float\",\n    \"rg8unorm\",\n    \"rg8snorm\",\n    \"rg8uint\",\n    \"rg8sint\",\n    \"rg16unorm\",\n    \"rg16snorm\",\n    \"rg16uint\",\n    \"rg16sint\",\n    \"rg16float\",\n    \"r32uint\",\n    \"r32sint\",\n    \"r32float\",\n    \"rg32uint\",\n    \"rg32sint\",\n    \"rg32float\",\n    \"rgba32uint\",\n    \"rgba32sint\",\n    \"rgba32float\",\n    \"bgra8unorm\",\n    \"r8unorm\",\n    \"r8snorm\",\n    \"r8uint\",\n    \"r8sint\",\n    \"r16unorm\",\n    \"r16snorm\",\n    \"r16uint\",\n    \"r16sint\",\n    \"r16float\",\n    \"rgb10a2unorm\",\n    \"rgb10a2uint\",\n    \"rg11b10ufloat\",\n    // functions\n    \"bitcast\",\n    \"all\",\n    \"any\",\n    \"select\",\n    \"arrayLength\",\n    \"abs\",\n    \"acos\",\n    \"acosh\",\n    \"asin\",\n    \"asinh\",\n    \"atan\",\n    \"atanh\",\n    \"atan2\",\n    \"ceil\",\n    \"clamp\",\n    \"cos\",\n    \"cosh\",\n    \"countLeadingZeros\",\n    \"countOneBits\",\n    \"countTrailingZeros\",\n    \"cross\",\n    \"degrees\",\n    \"determinant\",\n    \"distance\",\n    \"dot\",\n    \"dot4U8Packed\",\n    \"dot4I8Packed\",\n    \"exp\",\n    \"exp2\",\n    \"extractBits\",\n    \"faceForward\",\n    \"firstLeadingBit\",\n    \"firstTrailingBit\",\n    \"floor\",\n    \"fma\",\n    \"fract\",\n    \"frexp\",\n    \"insertBits\",\n    \"inverseSqrt\",\n    \"ldexp\",\n    \"length\",\n    \"log\",\n    \"log2\",\n    \"max\",\n    \"min\",\n    \"mix\",\n    \"modf\",\n    \"normalize\",\n    \"pow\",\n    \"quantizeToF16\",\n    \"radians\",\n    \"reflect\",\n    \"refract\",\n    \"reverseBits\",\n    \"round\",\n    \"saturate\",\n    \"sign\",\n    \"sin\",\n    \"sinh\",\n    \"smoothstep\",\n    \"sqrt\",\n    \"step\",\n    \"tan\",\n    \"tanh\",\n    \"transpose\",\n    \"trunc\",\n    \"dpdx\",\n    \"dpdxCoarse\",\n    \"dpdxFine\",\n    \"dpdy\",\n    \"dpdyCoarse\",\n    \"dpdyFine\",\n    \"fwidth\",\n    \"fwidthCoarse\",\n    \"fwidthFine\",\n    \"textureDimensions\",\n    \"textureGather\",\n    \"textureGatherCompare\",\n    \"textureLoad\",\n    \"textureNumLayers\",\n    \"textureNumLevels\",\n    \"textureNumSamples\",\n    \"textureSample\",\n    \"textureSampleBias\",\n    \"textureSampleCompare\",\n    \"textureSampleCompareLevel\",\n    \"textureSampleGrad\",\n    \"textureSampleLevel\",\n    \"textureSampleBaseClampToEdge\",\n    \"textureStore\",\n    \"atomicLoad\",\n    \"atomicStore\",\n    \"atomicAdd\",\n    \"atomicSub\",\n    \"atomicMax\",\n    \"atomicMin\",\n    \"atomicAnd\",\n    \"atomicOr\",\n    \"atomicXor\",\n    \"atomicExchange\",\n    \"atomicCompareExchangeWeak\",\n    \"pack4x8snorm\",\n    \"pack4x8unorm\",\n    \"pack4xI8\",\n    \"pack4xU8\",\n    \"pack4xI8Clamp\",\n    \"pack4xU8Clamp\",\n    \"pack2x16snorm\",\n    \"pack2x16unorm\",\n    \"pack2x16float\",\n    \"unpack4x8snorm\",\n    \"unpack4x8unorm\",\n    \"unpack4xI8\",\n    \"unpack4xU8\",\n    \"unpack2x16snorm\",\n    \"unpack2x16unorm\",\n    \"unpack2x16float\",\n    \"storageBarrier\",\n    \"textureBarrier\",\n    \"workgroupBarrier\",\n    \"workgroupUniformLoad\",\n    \"subgroupAdd\",\n    \"subgroupExclusiveAdd\",\n    \"subgroupInclusiveAdd\",\n    \"subgroupAll\",\n    \"subgroupAnd\",\n    \"subgroupAny\",\n    \"subgroupBallot\",\n    \"subgroupBroadcast\",\n    \"subgroupBroadcastFirst\",\n    \"subgroupElect\",\n    \"subgroupMax\",\n    \"subgroupMin\",\n    \"subgroupMul\",\n    \"subgroupExclusiveMul\",\n    \"subgroupInclusiveMul\",\n    \"subgroupOr\",\n    \"subgroupShuffle\",\n    \"subgroupShuffleDown\",\n    \"subgroupShuffleUp\",\n    \"subgroupShuffleXor\",\n    \"subgroupXor\",\n    \"quadBroadcast\",\n    \"quadSwapDiagonal\",\n    \"quadSwapX\",\n    \"quadSwapY\",\n    // not in the WGSL spec\n    \"i64\",\n    \"u64\",\n    \"f64\",\n    \"push_constant\",\n    \"r64uint\",\n];\n\npub static BUILTIN_IDENTIFIER_SET: RacyLock<KeywordSet> =\n    RacyLock::new(|| KeywordSet::from_iter(BUILTIN_IDENTIFIERS));\n"
  },
  {
    "path": "naga/src/lib.rs",
    "content": "/*!\nNaga can be used to translate source code written in one shading language to another.\n\n# Example\n\nThe following example translates WGSL to GLSL.\nIt requires the features `\"wgsl-in\"` and `\"glsl-out\"` to be enabled.\n\n*/\n// If we don't have the required front- and backends, don't try to build this example.\n#![cfg_attr(all(feature = \"wgsl-in\", feature = \"glsl-out\"), doc = \"```\")]\n#![cfg_attr(not(all(feature = \"wgsl-in\", feature = \"glsl-out\")), doc = \"```ignore\")]\n/*!\nlet wgsl_source = \"\n@fragment\nfn main_fs() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n\";\n\n// Parse the source into a Module.\nlet module: naga::Module = naga::front::wgsl::parse_str(wgsl_source)?;\n\n// Validate the module.\n// Validation can be made less restrictive by changing the ValidationFlags.\nlet module_info: naga::valid::ModuleInfo =\n    naga::valid::Validator::new(\n        naga::valid::ValidationFlags::all(),\n        naga::valid::Capabilities::all(),\n    )\n    .subgroup_stages(naga::valid::ShaderStages::all())\n    .subgroup_operations(naga::valid::SubgroupOperationSet::all())\n    .validate(&module)?;\n\n// Translate the module.\nuse naga::back::glsl;\nlet mut glsl_source = String::new();\nglsl::Writer::new(\n    &mut glsl_source,\n    &module,\n    &module_info,\n    &glsl::Options::default(),\n    &glsl::PipelineOptions {\n        entry_point: \"main_fs\".into(),\n        shader_stage: naga::ShaderStage::Fragment,\n        multiview: None,\n    },\n    naga::proc::BoundsCheckPolicies::default(),\n)?.write()?;\n\nassert_eq!(glsl_source, \"\\\n#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    _fs2p_location0 = vec4(1.0, 1.0, 1.0, 1.0);\n    return;\n}\n\n\");\n\n# Ok::<(), Box<dyn core::error::Error>>(())\n```\n*/\n\n#![allow(\n    clippy::new_without_default,\n    clippy::unneeded_field_pattern,\n    clippy::match_like_matches_macro,\n    clippy::collapsible_if,\n    clippy::derive_partial_eq_without_eq,\n    clippy::needless_borrowed_reference,\n    clippy::single_match,\n    clippy::enum_variant_names\n)]\n#![warn(\n    trivial_casts,\n    trivial_numeric_casts,\n    unused_extern_crates,\n    unused_qualifications,\n    clippy::pattern_type_mismatch,\n    clippy::missing_const_for_fn,\n    clippy::rest_pat_in_fully_bound_structs,\n    clippy::match_wildcard_for_single_variants\n)]\n#![deny(clippy::exit)]\n#![cfg_attr(\n    not(test),\n    warn(\n        clippy::dbg_macro,\n        clippy::panic,\n        clippy::print_stderr,\n        clippy::print_stdout,\n        clippy::todo\n    )\n)]\n#![no_std]\n#![forbid(unsafe_code)]\n\n#[cfg(std)]\nextern crate std;\n\nextern crate alloc;\n\nmod arena;\npub mod back;\npub mod common;\npub mod compact;\npub mod diagnostic_filter;\npub mod error;\npub mod front;\npub mod ir;\npub mod keywords;\nmod non_max_u32;\npub mod proc;\nmod racy_lock;\nmod span;\npub mod valid;\n\nuse alloc::string::String;\n\npub use crate::arena::{Arena, Handle, Range, UniqueArena};\npub use crate::span::{SourceLocation, Span, SpanContext, WithSpan};\n\n// TODO: Eliminate this re-export and migrate uses of `crate::Foo` to `use crate::ir; ir::Foo`.\npub use ir::*;\n\n/// Width of a boolean type, in bytes.\npub const BOOL_WIDTH: Bytes = 1;\n\n/// Width of abstract types, in bytes.\npub const ABSTRACT_WIDTH: Bytes = 8;\n\n/// Hash map that is faster but not resilient to DoS attacks.\n/// (Similar to rustc_hash::FxHashMap but using hashbrown::HashMap instead of alloc::collections::HashMap.)\n/// To construct a new instance: `FastHashMap::default()`\npub type FastHashMap<K, T> =\n    hashbrown::HashMap<K, T, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n\n/// Hash set that is faster but not resilient to DoS attacks.\n/// (Similar to rustc_hash::FxHashSet but using hashbrown::HashSet instead of alloc::collections::HashMap.)\npub type FastHashSet<K> =\n    hashbrown::HashSet<K, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n\n/// Insertion-order-preserving hash set (`IndexSet<K>`), but with the same\n/// hasher as `FastHashSet<K>` (faster but not resilient to DoS attacks).\npub type FastIndexSet<K> =\n    indexmap::IndexSet<K, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n\n/// Insertion-order-preserving hash map (`IndexMap<K, V>`), but with the same\n/// hasher as `FastHashMap<K, V>` (faster but not resilient to DoS attacks).\npub type FastIndexMap<K, V> =\n    indexmap::IndexMap<K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n\n/// Map of expressions that have associated variable names\npub(crate) type NamedExpressions = FastIndexMap<Handle<Expression>, String>;\n"
  },
  {
    "path": "naga/src/non_max_u32.rs",
    "content": "//! [`NonMaxU32`], a 32-bit type that can represent any value except [`u32::MAX`].\n//!\n//! Naga would like `Option<Handle<T>>` to be a 32-bit value, which means we\n//! need to exclude some index value for use in representing [`None`]. We could\n//! have [`Handle`] store a [`NonZeroU32`], but zero is a very useful value for\n//! indexing. We could have a [`Handle`] store a value one greater than its index,\n//! but it turns out that it's not uncommon to want to work with [`Handle`]s'\n//! indices, so that bias of 1 becomes more visible than one would like.\n//!\n//! This module defines the type [`NonMaxU32`], for which `Option<NonMaxU32>` is\n//! still a 32-bit value, but which is directly usable as a [`Handle`] index\n//! type. It still uses a bias of 1 under the hood, but that fact is isolated\n//! within the implementation.\n//!\n//! [`Handle`]: crate::arena::Handle\n//! [`NonZeroU32`]: core::num::NonZeroU32\n#![allow(dead_code)]\n\nuse core::num::NonZeroU32;\n\n/// An unsigned 32-bit value known not to be [`u32::MAX`].\n///\n/// A `NonMaxU32` value can represent any value in the range `0 .. u32::MAX -\n/// 1`, and an `Option<NonMaxU32>` is still a 32-bit value. In other words,\n/// `NonMaxU32` is just like [`NonZeroU32`], except that a different value is\n/// missing from the full `u32` range.\n///\n/// Since zero is a very useful value in indexing, `NonMaxU32` is more useful\n/// for representing indices than [`NonZeroU32`].\n///\n/// `NonMaxU32` values and `Option<NonMaxU32>` values both occupy 32 bits.\n///\n/// # Serialization and Deserialization\n///\n/// When the appropriate Cargo features are enabled, `NonMaxU32` implements\n/// [`serde::Serialize`] and [`serde::Deserialize`] in the natural way, as the\n/// integer value it represents. For example, serializing\n/// `NonMaxU32::new(0).unwrap()` as JSON or RON yields the string `\"0\"`. This is\n/// the case despite `NonMaxU32`'s implementation, described below.\n///\n/// # Implementation\n///\n/// Although this should not be observable to its users, a `NonMaxU32` whose\n/// value is `n` is a newtype around a [`NonZeroU32`] whose value is `n + 1`.\n/// This way, the range of values that `NonMaxU32` can represent,\n/// `0..=u32::MAX - 1`, is mapped to the range `1..=u32::MAX`, which is the\n/// range that /// [`NonZeroU32`] can represent. (And conversely, since\n/// [`u32`] addition wraps around, the value unrepresentable in `NonMaxU32`,\n/// [`u32::MAX`], becomes the value unrepresentable in [`NonZeroU32`], `0`.)\n///\n/// [`NonZeroU32`]: core::num::NonZeroU32\n#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub struct NonMaxU32(NonZeroU32);\n\nimpl NonMaxU32 {\n    /// Construct a [`NonMaxU32`] whose value is `n`, if possible.\n    pub const fn new(n: u32) -> Option<Self> {\n        // If `n` is `u32::MAX`, then `n.wrapping_add(1)` is `0`,\n        // so `NonZeroU32::new` returns `None` in exactly the case\n        // where we must return `None`.\n        match NonZeroU32::new(n.wrapping_add(1)) {\n            Some(non_zero) => Some(NonMaxU32(non_zero)),\n            None => None,\n        }\n    }\n\n    /// Return the value of `self` as a [`u32`].\n    pub const fn get(self) -> u32 {\n        self.0.get() - 1\n    }\n\n    pub fn checked_add(self, n: u32) -> Option<Self> {\n        // Adding `n` to `self` produces `u32::MAX` if and only if\n        // adding `n` to `self.0` produces `0`. So we can simply\n        // call `NonZeroU32::checked_add` and let its check for zero\n        // determine whether our add would have produced `u32::MAX`.\n        Some(NonMaxU32(self.0.checked_add(n)?))\n    }\n}\n\nimpl core::fmt::Debug for NonMaxU32 {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.get().fmt(f)\n    }\n}\n\nimpl core::fmt::Display for NonMaxU32 {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.get().fmt(f)\n    }\n}\n\n#[cfg(feature = \"serialize\")]\nimpl serde::Serialize for NonMaxU32 {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_u32(self.get())\n    }\n}\n\n#[cfg(feature = \"deserialize\")]\nimpl<'de> serde::Deserialize<'de> for NonMaxU32 {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        // Defer to `u32`'s `Deserialize` implementation.\n        let n = <u32 as serde::Deserialize>::deserialize(deserializer)?;\n\n        // Constrain the range of the value further.\n        NonMaxU32::new(n).ok_or_else(|| {\n            <D::Error as serde::de::Error>::invalid_value(\n                serde::de::Unexpected::Unsigned(n as u64),\n                &\"a value no less than 0 and no greater than 4294967294 (2^32 - 2)\",\n            )\n        })\n    }\n}\n\n#[test]\nfn size() {\n    assert_eq!(size_of::<Option<NonMaxU32>>(), size_of::<u32>());\n}\n"
  },
  {
    "path": "naga/src/proc/constant_evaluator.rs",
    "content": "// Code in this file intentionally uses `for` loops and `.push()` rather than\n// `ArrayVec::from_iter`, because the latter is monomorphized by all three of\n// the item type, the capacity, and the iterator type, which can easily bloat\n// the compiled executable (by ~260 KiB, when it was removed).\n\nuse alloc::{\n    format,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse core::iter;\n\nuse arrayvec::ArrayVec;\nuse half::f16;\nuse num_traits::{real::Real, FromPrimitive, One, ToPrimitive, Zero};\n\nuse crate::{\n    arena::{Arena, Handle, HandleVec, UniqueArena},\n    ArraySize, BinaryOperator, Constant, Expression, Literal, Override, RelationalFunction,\n    ScalarKind, Span, Type, TypeInner, UnaryOperator,\n};\n\n#[cfg(feature = \"wgsl-in\")]\nuse crate::common::wgsl::TryToWgsl;\n\n/// A macro that allows dollar signs (`$`) to be emitted by other macros. Useful for generating\n/// `macro_rules!` items that, in turn, emit their own `macro_rules!` items.\n///\n/// Technique stolen directly from\n/// <https://github.com/rust-lang/rust/issues/35853#issuecomment-415993963>.\nmacro_rules! with_dollar_sign {\n    ($($body:tt)*) => {\n        macro_rules! __with_dollar_sign { $($body)* }\n        __with_dollar_sign!($);\n    }\n}\n\nmacro_rules! gen_component_wise_extractor {\n    (\n        $ident:ident -> $target:ident,\n        literals: [$( $literal:ident => $mapping:ident: $ty:ident ),+ $(,)?],\n        scalar_kinds: [$( $scalar_kind:ident ),* $(,)?],\n    ) => {\n        /// A subset of [`Literal`]s intended to be used for implementing numeric built-ins.\n        #[derive(Debug)]\n        #[cfg_attr(test, derive(PartialEq))]\n        enum $target<const N: usize> {\n            $(\n                #[doc = concat!(\n                    \"Maps to [`Literal::\",\n                    stringify!($literal),\n                    \"`]\",\n                )]\n                $mapping([$ty; N]),\n            )+\n        }\n\n        impl From<$target<1>> for Expression {\n            fn from(value: $target<1>) -> Self {\n                match value {\n                    $(\n                        $target::$mapping([value]) => {\n                            Expression::Literal(Literal::$literal(value))\n                        }\n                    )+\n                }\n            }\n        }\n\n        #[doc = concat!(\n            \"Attempts to evaluate multiple `exprs` as a combined [`\",\n            stringify!($target),\n            \"`] to pass to `handler`. \",\n        )]\n        /// If `exprs` are vectors of the same length, `handler` is called for each corresponding\n        /// component of each vector.\n        ///\n        /// `handler`'s output is registered as a new expression. If `exprs` are vectors of the\n        /// same length, a new vector expression is registered, composed of each component emitted\n        /// by `handler`.\n        fn $ident<const N: usize, const M: usize>(\n            eval: &mut ConstantEvaluator<'_>,\n            span: Span,\n            exprs: [Handle<Expression>; N],\n            handler: fn($target<N>) -> Result<$target<M>, ConstantEvaluatorError>,\n        ) -> Result<Handle<Expression>, ConstantEvaluatorError>\n        where\n            $target<M>: Into<Expression>,\n        {\n            assert!(N > 0);\n            let err = ConstantEvaluatorError::InvalidMathArg;\n            let mut exprs = exprs.into_iter();\n\n            macro_rules! sanitize {\n                ($expr:expr) => {\n                    eval.eval_zero_value_and_splat($expr, span)\n                        .map(|expr| &eval.expressions[expr])\n                };\n            }\n\n            let new_expr: Result<Expression, ConstantEvaluatorError> = match sanitize!(exprs.next().unwrap())? {\n                $(\n                    &Expression::Literal(Literal::$literal(x)) => {\n                        let mut arr = ArrayVec::<_, N>::new();\n                        arr.push(x);\n                        for expr in exprs {\n                            match sanitize!(expr)? {\n                                &Expression::Literal(Literal::$literal(val)) => arr.push(val),\n                                _ => return Err(err),\n                            }\n                        }\n                        let comps = $target::$mapping(arr.into_inner().unwrap());\n                        Ok(handler(comps)?.into())\n                    },\n                )+\n                &Expression::Compose { ty, ref components } => match &eval.types[ty].inner {\n                    &TypeInner::Vector { size, scalar } => match scalar.kind {\n                        $(ScalarKind::$scalar_kind)|* => {\n                            let first_ty = ty;\n                            let mut component_groups =\n                                ArrayVec::<ArrayVec<_, { crate::VectorSize::MAX }>, N>::new();\n                            {\n                                let mut inner = ArrayVec::new();\n                                for item in crate::proc::flatten_compose(\n                                    first_ty,\n                                    components,\n                                    eval.expressions,\n                                    eval.types,\n                                ) {\n                                    inner.push(item);\n                                }\n                                component_groups.push(inner);\n                            }\n                            for expr in exprs {\n                                match sanitize!(expr)? {\n                                    &Expression::Compose { ty, ref components }\n                                        if &eval.types[ty].inner\n                                            == &eval.types[first_ty].inner =>\n                                    {\n                                        let mut inner = ArrayVec::new();\n                                        for item in crate::proc::flatten_compose(\n                                            ty,\n                                            components,\n                                            eval.expressions,\n                                            eval.types,\n                                        ) {\n                                            inner.push(item);\n                                        }\n                                        component_groups.push(inner);\n                                    }\n                                    _ => return Err(err),\n                                }\n                            }\n                            let component_groups = component_groups.into_inner().unwrap();\n                            let mut new_components =\n                                ArrayVec::<_, { crate::VectorSize::MAX }>::new();\n                            for idx in 0..(size as u8).into() {\n                                let mut group_arr = ArrayVec::<_, N>::new();\n                                for cs in component_groups.iter() {\n                                    group_arr.push(\n                                        cs.get(idx).cloned().ok_or_else(|| err.clone())?,\n                                    );\n                                }\n                                let group = group_arr.into_inner().unwrap();\n                                new_components.push($ident(\n                                    eval,\n                                    span,\n                                    group,\n                                    handler,\n                                )?);\n                            }\n                            Ok(Expression::Compose {\n                                ty: first_ty,\n                                components: new_components.into_iter().collect(),\n                            })\n                        }\n                        _ => return Err(err),\n                    },\n                    _ => return Err(err),\n                },\n                _ => return Err(err),\n            };\n            eval.register_evaluated_expr(new_expr?, span)\n        }\n\n        with_dollar_sign! {\n            ($d:tt) => {\n                #[allow(unused)]\n                #[doc = concat!(\n                    \"A convenience macro for using the same RHS for each [`\",\n                    stringify!($target),\n                    \"`] variant in a call to [`\",\n                    stringify!($ident),\n                    \"`].\",\n                )]\n                macro_rules! $ident {\n                    (\n                        $eval:expr,\n                        $span:expr,\n                        [$d ($d expr:expr),+ $d (,)?],\n                        |$d ($d arg:ident),+| $d tt:tt\n                    ) => {\n                        $ident($eval, $span, [$d ($d expr),+], |args| match args {\n                            $(\n                                $target::$mapping([$d ($d arg),+]) => {\n                                    let res = $d tt;\n                                    Result::map(res, $target::$mapping)\n                                },\n                            )+\n                        })\n                    };\n                }\n            };\n        }\n    };\n}\n\ngen_component_wise_extractor! {\n    component_wise_scalar -> Scalar,\n    literals: [\n        AbstractFloat => AbstractFloat: f64,\n        F32 => F32: f32,\n        F16 => F16: f16,\n        AbstractInt => AbstractInt: i64,\n        U32 => U32: u32,\n        I32 => I32: i32,\n        U64 => U64: u64,\n        I64 => I64: i64,\n    ],\n    scalar_kinds: [\n        Float,\n        AbstractFloat,\n        Sint,\n        Uint,\n        AbstractInt,\n    ],\n}\n\ngen_component_wise_extractor! {\n    component_wise_float -> Float,\n    literals: [\n        AbstractFloat => Abstract: f64,\n        F32 => F32: f32,\n        F16 => F16: f16,\n    ],\n    scalar_kinds: [\n        Float,\n        AbstractFloat,\n    ],\n}\n\ngen_component_wise_extractor! {\n    component_wise_concrete_int -> ConcreteInt,\n    literals: [\n        U32 => U32: u32,\n        I32 => I32: i32,\n    ],\n    scalar_kinds: [\n        Sint,\n        Uint,\n    ],\n}\n\ngen_component_wise_extractor! {\n    component_wise_signed -> Signed,\n    literals: [\n        AbstractFloat => AbstractFloat: f64,\n        AbstractInt => AbstractInt: i64,\n        F32 => F32: f32,\n        F16 => F16: f16,\n        I32 => I32: i32,\n    ],\n    scalar_kinds: [\n        Sint,\n        AbstractInt,\n        Float,\n        AbstractFloat,\n    ],\n}\n\n/// Vectors with a concrete element type.\n#[derive(Debug)]\nenum LiteralVector {\n    F64(ArrayVec<f64, { crate::VectorSize::MAX }>),\n    F32(ArrayVec<f32, { crate::VectorSize::MAX }>),\n    F16(ArrayVec<f16, { crate::VectorSize::MAX }>),\n    U32(ArrayVec<u32, { crate::VectorSize::MAX }>),\n    I32(ArrayVec<i32, { crate::VectorSize::MAX }>),\n    U64(ArrayVec<u64, { crate::VectorSize::MAX }>),\n    I64(ArrayVec<i64, { crate::VectorSize::MAX }>),\n    Bool(ArrayVec<bool, { crate::VectorSize::MAX }>),\n    AbstractInt(ArrayVec<i64, { crate::VectorSize::MAX }>),\n    AbstractFloat(ArrayVec<f64, { crate::VectorSize::MAX }>),\n}\n\nimpl LiteralVector {\n    #[allow(clippy::missing_const_for_fn, reason = \"MSRV\")]\n    fn len(&self) -> usize {\n        match *self {\n            LiteralVector::F64(ref v) => v.len(),\n            LiteralVector::F32(ref v) => v.len(),\n            LiteralVector::F16(ref v) => v.len(),\n            LiteralVector::U32(ref v) => v.len(),\n            LiteralVector::I32(ref v) => v.len(),\n            LiteralVector::U64(ref v) => v.len(),\n            LiteralVector::I64(ref v) => v.len(),\n            LiteralVector::Bool(ref v) => v.len(),\n            LiteralVector::AbstractInt(ref v) => v.len(),\n            LiteralVector::AbstractFloat(ref v) => v.len(),\n        }\n    }\n\n    /// Creates [`LiteralVector`] of size 1 from single [`Literal`]\n    fn from_literal(literal: Literal) -> Self {\n        fn arrayvec_of<T, const N: usize>(val: T) -> ArrayVec<T, N> {\n            let mut v = ArrayVec::new();\n            v.push(val);\n            v\n        }\n        match literal {\n            Literal::F64(e) => Self::F64(arrayvec_of(e)),\n            Literal::F32(e) => Self::F32(arrayvec_of(e)),\n            Literal::U32(e) => Self::U32(arrayvec_of(e)),\n            Literal::I32(e) => Self::I32(arrayvec_of(e)),\n            Literal::U64(e) => Self::U64(arrayvec_of(e)),\n            Literal::I64(e) => Self::I64(arrayvec_of(e)),\n            Literal::Bool(e) => Self::Bool(arrayvec_of(e)),\n            Literal::AbstractInt(e) => Self::AbstractInt(arrayvec_of(e)),\n            Literal::AbstractFloat(e) => Self::AbstractFloat(arrayvec_of(e)),\n            Literal::F16(e) => Self::F16(arrayvec_of(e)),\n        }\n    }\n\n    /// Creates [`LiteralVector`] from [`ArrayVec`] of [`Literal`]s.\n    /// Returns error if components types do not match.\n    /// # Panics\n    /// Panics if vector is empty\n    fn from_literal_vec(\n        components: ArrayVec<Literal, { crate::VectorSize::MAX }>,\n    ) -> Result<Self, ConstantEvaluatorError> {\n        assert!(!components.is_empty());\n        // TODO: should a vector of i32 be constructible from abstract int?\n        macro_rules! compose_literals {\n            ($components:expr, $variant:ident, $self_variant:ident) => {{\n                let mut out = ArrayVec::new();\n                for l in &$components {\n                    match l {\n                        &Literal::$variant(v) => out.push(v),\n                        _ => return Err(ConstantEvaluatorError::InvalidMathArg),\n                    }\n                }\n                Self::$self_variant(out)\n            }};\n        }\n        Ok(match components[0] {\n            Literal::I32(_) => compose_literals!(components, I32, I32),\n            Literal::U32(_) => compose_literals!(components, U32, U32),\n            Literal::I64(_) => compose_literals!(components, I64, I64),\n            Literal::U64(_) => compose_literals!(components, U64, U64),\n            Literal::F32(_) => compose_literals!(components, F32, F32),\n            Literal::F64(_) => compose_literals!(components, F64, F64),\n            Literal::Bool(_) => compose_literals!(components, Bool, Bool),\n            Literal::AbstractInt(_) => compose_literals!(components, AbstractInt, AbstractInt),\n            Literal::AbstractFloat(_) => {\n                compose_literals!(components, AbstractFloat, AbstractFloat)\n            }\n            Literal::F16(_) => compose_literals!(components, F16, F16),\n        })\n    }\n\n    #[allow(dead_code)]\n    /// Returns [`ArrayVec`] of [`Literal`]s\n    fn to_literal_vec(&self) -> ArrayVec<Literal, { crate::VectorSize::MAX }> {\n        macro_rules! decompose_literals {\n            ($v:expr, $variant:ident) => {{\n                let mut out = ArrayVec::new();\n                for e in $v {\n                    out.push(Literal::$variant(*e));\n                }\n                out\n            }};\n        }\n        match *self {\n            LiteralVector::F64(ref v) => decompose_literals!(v, F64),\n            LiteralVector::F32(ref v) => decompose_literals!(v, F32),\n            LiteralVector::F16(ref v) => decompose_literals!(v, F16),\n            LiteralVector::U32(ref v) => decompose_literals!(v, U32),\n            LiteralVector::I32(ref v) => decompose_literals!(v, I32),\n            LiteralVector::U64(ref v) => decompose_literals!(v, U64),\n            LiteralVector::I64(ref v) => decompose_literals!(v, I64),\n            LiteralVector::Bool(ref v) => decompose_literals!(v, Bool),\n            LiteralVector::AbstractInt(ref v) => decompose_literals!(v, AbstractInt),\n            LiteralVector::AbstractFloat(ref v) => decompose_literals!(v, AbstractFloat),\n        }\n    }\n\n    #[allow(dead_code)]\n    /// Puts self into eval's expressions arena and returns handle to it\n    fn register_as_evaluated_expr(\n        &self,\n        eval: &mut ConstantEvaluator<'_>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let lit_vec = self.to_literal_vec();\n        assert!(!lit_vec.is_empty());\n        let expr = if lit_vec.len() == 1 {\n            Expression::Literal(lit_vec[0])\n        } else {\n            Expression::Compose {\n                ty: eval.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector {\n                            size: match lit_vec.len() {\n                                2 => crate::VectorSize::Bi,\n                                3 => crate::VectorSize::Tri,\n                                4 => crate::VectorSize::Quad,\n                                _ => unreachable!(),\n                            },\n                            scalar: lit_vec[0].scalar(),\n                        },\n                    },\n                    Span::UNDEFINED,\n                ),\n                components: lit_vec\n                    .iter()\n                    .map(|&l| eval.register_evaluated_expr(Expression::Literal(l), span))\n                    .collect::<Result<_, _>>()?,\n            }\n        };\n        eval.register_evaluated_expr(expr, span)\n    }\n}\n\n/// A macro for matching on [`LiteralVector`] variants.\n///\n/// `Float` variant expands to `F16`, `F32`, `F64` and `AbstractFloat`.\n/// `Integer` variant expands to `I32`, `I64`, `U32`, `U64` and `AbstractInt`.\n///\n/// For output both [`Literal`] (fold) and [`LiteralVector`] (map) are supported.\n///\n/// Example usage:\n///\n/// ```rust,ignore\n/// match_literal_vector!(match v => Literal {\n///     F16 => |v| {v.sum()},\n///     Integer => |v| {v.sum()},\n///     U32 => |v| -> I32 {v.sum()}, // optionally override return type\n/// })\n/// ```\n///\n/// ```rust,ignore\n/// match_literal_vector!(match (e1, e2) => LiteralVector {\n///     F16 => |e1, e2| {e1+e2},\n///     Integer => |e1, e2| {e1+e2},\n///     U32 => |e1, e2| -> I32 {e1+e2}, // optionally override return type\n/// })\n/// ```\nmacro_rules! match_literal_vector {\n    (match $lit_vec:expr => $out:ident {\n        $(\n            $ty:ident => |$($var:ident),+| $(-> $ret:ident)? { $body:expr }\n        ),+\n        $(,)?\n    }) => {\n        match_literal_vector!(@inner_start $lit_vec; $out; [$($ty),+]; [$({ $($var),+ ; $($ret)? ; $body }),+])\n    };\n\n    (@inner_start\n        $lit_vec:expr;\n        $out:ident;\n        [$($ty:ident),+];\n        [$({ $($var:ident),+ ; $($ret:ident)? ; $body:expr }),+]\n    ) => {\n        match_literal_vector!(@inner\n            $lit_vec;\n            $out;\n            [$($ty),+];\n            [] <> [$({ $($var),+ ; $($ret)? ; $body }),+]\n        )\n    };\n\n    (@inner\n        $lit_vec:expr;\n        $out:ident;\n        [$ty:ident $(, $ty1:ident)*];\n        [$({$_ty:ident ; $($_var:ident),+ ; $($_ret:ident)? ; $_body:expr}),*] <>\n        [$({ $($var:ident),+ ; $($ret:ident)? ; $body:expr }),+]\n    ) => {\n        match_literal_vector!(@inner\n            $ty;\n            $lit_vec;\n            $out;\n            [$($ty1),*];\n            [$({$_ty ; $($_var),+ ; $($_ret)? ; $_body}),*] <>\n            [$({ $($var),+ ; $($ret)? ; $body }),+]\n        )\n    };\n    (@inner\n        Integer;\n        $lit_vec:expr;\n        $out:ident;\n        [$($ty:ident),*];\n        [$({$_ty:ident ; $($_var:ident),+ ; $($_ret:ident)? ; $_body:expr}),*] <>\n        [\n            { $($var:ident),+ ; $($ret:ident)? ; $body:expr }\n            $(,{ $($var1:ident),+ ; $($ret1:ident)? ; $body1:expr })*\n        ]\n    ) => {\n        match_literal_vector!(@inner\n            $lit_vec;\n            $out;\n            [U32, I32, U64, I64, AbstractInt $(, $ty)*];\n            [$({$_ty ; $($_var),+ ; $($_ret)? ; $_body}),*] <>\n            [\n                { $($var),+ ; $($ret)? ; $body }, // U32\n                { $($var),+ ; $($ret)? ; $body }, // I32\n                { $($var),+ ; $($ret)? ; $body }, // U64\n                { $($var),+ ; $($ret)? ; $body }, // I64\n                { $($var),+ ; $($ret)? ; $body }  // AbstractInt\n                $(,{ $($var1),+ ; $($ret1)? ; $body1 })*\n            ]\n        )\n    };\n    (@inner\n        Float;\n        $lit_vec:expr;\n        $out:ident;\n        [$($ty:ident),*];\n        [$({$_ty:ident ; $($_var:ident),+ ; $($_ret:ident)? ; $_body:expr}),*] <>\n        [\n            { $($var:ident),+ ; $($ret:ident)? ; $body:expr }\n            $(,{ $($var1:ident),+ ; $($ret1:ident)? ; $body1:expr })*\n        ]\n    ) => {\n        match_literal_vector!(@inner\n            $lit_vec;\n            $out;\n            [F16, F32, F64, AbstractFloat $(, $ty)*];\n            [$({$_ty ; $($_var),+ ; $($_ret)? ; $_body}),*] <>\n            [\n                { $($var),+ ; $($ret)? ; $body }, // F16\n                { $($var),+ ; $($ret)? ; $body }, // F32\n                { $($var),+ ; $($ret)? ; $body }, // F64\n                { $($var),+ ; $($ret)? ; $body }  // AbstractFloat\n                $(,{ $($var1),+ ; $($ret1)? ; $body1 })*\n            ]\n        )\n    };\n    (@inner\n        $ty:ident;\n        $lit_vec:expr;\n        $out:ident;\n        [$ty1:ident $(,$ty2:ident)*];\n        [$({$_ty:ident ; $($_var:ident),+ ; $($_ret:ident)? ; $_body:expr}),*] <> [\n            { $($var:ident),+ ; $($ret:ident)? ; $body:expr }\n            $(, { $($var1:ident),+ ; $($ret1:ident)? ; $body1:expr })*\n        ]\n    ) => {\n        match_literal_vector!(@inner\n            $ty1;\n            $lit_vec;\n            $out;\n            [$($ty2),*];\n            [\n                $({$_ty ; $($_var),+ ; $($_ret)? ; $_body},)*\n                { $ty; $($var),+ ; $($ret)? ; $body }\n            ] <>\n            [$({ $($var1),+ ; $($ret1)? ; $body1 }),*]\n\n        )\n    };\n    (@inner\n        $ty:ident;\n        $lit_vec:expr;\n        $out:ident;\n        [];\n        [$({$_ty:ident ; $($_var:ident),+ ; $($_ret:ident)? ; $_body:expr}),*] <>\n        [{ $($var:ident),+ ; $($ret:ident)? ; $body:expr }]\n    ) => {\n        match_literal_vector!(@inner_finish\n            $lit_vec;\n            $out;\n            [\n                $({ $_ty ; $($_var),+ ; $($_ret)? ; $_body },)*\n                { $ty; $($var),+ ; $($ret)? ; $body }\n            ]\n        )\n    };\n    (@inner_finish\n        $lit_vec:expr;\n        $out:ident;\n        [$({$ty:ident ; $($var:ident),+ ; $($ret:ident)? ; $body:expr}),+]\n    ) => {\n        match $lit_vec {\n            $(\n                #[allow(unused_parens)]\n                ($(LiteralVector::$ty(ref $var)),+) => { Ok(match_literal_vector!(@expand_ret $out; $ty $(; $ret)? ; $body)) }\n            )+\n            _ => Err(ConstantEvaluatorError::InvalidMathArg),\n        }\n    };\n    (@expand_ret $out:ident; $ty:ident; $body:expr) => {\n        $out::$ty($body)\n    };\n    (@expand_ret $out:ident; $_ty:ident; $ret:ident; $body:expr) => {\n        $out::$ret($body)\n    };\n}\n\n#[derive(Debug)]\nenum Behavior<'a> {\n    Wgsl(WgslRestrictions<'a>),\n    Glsl(GlslRestrictions<'a>),\n}\n\nimpl Behavior<'_> {\n    /// Returns `true` if the inner WGSL/GLSL restrictions are runtime restrictions.\n    const fn has_runtime_restrictions(&self) -> bool {\n        matches!(\n            self,\n            &Behavior::Wgsl(WgslRestrictions::Runtime(_))\n                | &Behavior::Glsl(GlslRestrictions::Runtime(_))\n        )\n    }\n}\n\n/// A context for evaluating constant expressions.\n///\n/// A `ConstantEvaluator` points at an expression arena to which it can append\n/// newly evaluated expressions: you pass [`try_eval_and_append`] whatever kind\n/// of Naga [`Expression`] you like, and if its value can be computed at compile\n/// time, `try_eval_and_append` appends an expression representing the computed\n/// value - a tree of [`Literal`], [`Compose`], [`ZeroValue`], and [`Swizzle`]\n/// expressions - to the arena. See the [`try_eval_and_append`] method for details.\n///\n/// A `ConstantEvaluator` also holds whatever information we need to carry out\n/// that evaluation: types, other constants, and so on.\n///\n/// [`try_eval_and_append`]: ConstantEvaluator::try_eval_and_append\n/// [`Compose`]: Expression::Compose\n/// [`ZeroValue`]: Expression::ZeroValue\n/// [`Literal`]: Expression::Literal\n/// [`Swizzle`]: Expression::Swizzle\n#[derive(Debug)]\npub struct ConstantEvaluator<'a> {\n    /// Which language's evaluation rules we should follow.\n    behavior: Behavior<'a>,\n\n    /// The module's type arena.\n    ///\n    /// Because expressions like [`Splat`] contain type handles, we need to be\n    /// able to add new types to produce those expressions.\n    ///\n    /// [`Splat`]: Expression::Splat\n    types: &'a mut UniqueArena<Type>,\n\n    /// The module's constant arena.\n    constants: &'a Arena<Constant>,\n\n    /// The module's override arena.\n    overrides: &'a Arena<Override>,\n\n    /// The arena to which we are contributing expressions.\n    expressions: &'a mut Arena<Expression>,\n\n    /// Tracks the constness of expressions residing in [`Self::expressions`]\n    expression_kind_tracker: &'a mut ExpressionKindTracker,\n\n    layouter: &'a mut crate::proc::Layouter,\n}\n\n#[derive(Debug)]\nenum WgslRestrictions<'a> {\n    /// - const-expressions will be evaluated and inserted in the arena\n    Const(Option<FunctionLocalData<'a>>),\n    /// - const-expressions will be evaluated and inserted in the arena\n    /// - override-expressions will be inserted in the arena\n    Override,\n    /// - const-expressions will be evaluated and inserted in the arena\n    /// - override-expressions will be inserted in the arena\n    /// - runtime-expressions will be inserted in the arena\n    Runtime(FunctionLocalData<'a>),\n}\n\n#[derive(Debug)]\nenum GlslRestrictions<'a> {\n    /// - const-expressions will be evaluated and inserted in the arena\n    Const,\n    /// - const-expressions will be evaluated and inserted in the arena\n    /// - override-expressions will be inserted in the arena\n    /// - runtime-expressions will be inserted in the arena\n    Runtime(FunctionLocalData<'a>),\n}\n\n#[derive(Debug)]\nstruct FunctionLocalData<'a> {\n    /// Global constant expressions\n    global_expressions: &'a Arena<Expression>,\n    emitter: &'a mut super::Emitter,\n    block: &'a mut crate::Block,\n}\n\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]\npub enum ExpressionKind {\n    Const,\n    Override,\n    Runtime,\n}\n\n#[derive(Debug)]\npub struct ExpressionKindTracker {\n    inner: HandleVec<Expression, ExpressionKind>,\n}\n\nimpl ExpressionKindTracker {\n    pub const fn new() -> Self {\n        Self {\n            inner: HandleVec::new(),\n        }\n    }\n\n    /// Forces the the expression to not be const\n    pub fn force_non_const(&mut self, value: Handle<Expression>) {\n        self.inner[value] = ExpressionKind::Runtime;\n    }\n\n    pub fn insert(&mut self, value: Handle<Expression>, expr_type: ExpressionKind) {\n        self.inner.insert(value, expr_type);\n    }\n\n    pub fn is_const(&self, h: Handle<Expression>) -> bool {\n        matches!(self.type_of(h), ExpressionKind::Const)\n    }\n\n    pub fn is_const_or_override(&self, h: Handle<Expression>) -> bool {\n        matches!(\n            self.type_of(h),\n            ExpressionKind::Const | ExpressionKind::Override\n        )\n    }\n\n    fn type_of(&self, value: Handle<Expression>) -> ExpressionKind {\n        self.inner[value]\n    }\n\n    pub fn from_arena(arena: &Arena<Expression>) -> Self {\n        let mut tracker = Self {\n            inner: HandleVec::with_capacity(arena.len()),\n        };\n        for (handle, expr) in arena.iter() {\n            tracker\n                .inner\n                .insert(handle, tracker.type_of_with_expr(expr));\n        }\n        tracker\n    }\n\n    fn type_of_with_expr(&self, expr: &Expression) -> ExpressionKind {\n        match *expr {\n            Expression::Literal(_) | Expression::ZeroValue(_) | Expression::Constant(_) => {\n                ExpressionKind::Const\n            }\n            Expression::Override(_) => ExpressionKind::Override,\n            Expression::Compose { ref components, .. } => {\n                let mut expr_type = ExpressionKind::Const;\n                for component in components {\n                    expr_type = expr_type.max(self.type_of(*component))\n                }\n                expr_type\n            }\n            Expression::Splat { value, .. } => self.type_of(value),\n            Expression::AccessIndex { base, .. } => self.type_of(base),\n            Expression::Access { base, index } => self.type_of(base).max(self.type_of(index)),\n            Expression::Swizzle { vector, .. } => self.type_of(vector),\n            Expression::Unary { expr, .. } => self.type_of(expr),\n            Expression::Binary { left, right, .. } => self.type_of(left).max(self.type_of(right)),\n            Expression::Math {\n                arg,\n                arg1,\n                arg2,\n                arg3,\n                ..\n            } => self\n                .type_of(arg)\n                .max(\n                    arg1.map(|arg| self.type_of(arg))\n                        .unwrap_or(ExpressionKind::Const),\n                )\n                .max(\n                    arg2.map(|arg| self.type_of(arg))\n                        .unwrap_or(ExpressionKind::Const),\n                )\n                .max(\n                    arg3.map(|arg| self.type_of(arg))\n                        .unwrap_or(ExpressionKind::Const),\n                ),\n            Expression::As { expr, .. } => self.type_of(expr),\n            Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => self\n                .type_of(condition)\n                .max(self.type_of(accept))\n                .max(self.type_of(reject)),\n            Expression::Relational { argument, .. } => self.type_of(argument),\n            Expression::ArrayLength(expr) => self.type_of(expr),\n            _ => ExpressionKind::Runtime,\n        }\n    }\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ConstantEvaluatorError {\n    #[error(\"Constants cannot access function arguments\")]\n    FunctionArg,\n    #[error(\"Constants cannot access global variables\")]\n    GlobalVariable,\n    #[error(\"Constants cannot access local variables\")]\n    LocalVariable,\n    #[error(\"Cannot get the array length of a non array type\")]\n    InvalidArrayLengthArg,\n    #[error(\"Constants cannot get the array length of a dynamically sized array\")]\n    ArrayLengthDynamic,\n    #[error(\"Cannot call arrayLength on array sized by override-expression\")]\n    ArrayLengthOverridden,\n    #[error(\"Constants cannot call functions\")]\n    Call,\n    #[error(\"Constants don't support workGroupUniformLoad\")]\n    WorkGroupUniformLoadResult,\n    #[error(\"Constants don't support atomic functions\")]\n    Atomic,\n    #[error(\"Constants don't support derivative functions\")]\n    Derivative,\n    #[error(\"Constants don't support load expressions\")]\n    Load,\n    #[error(\"Constants don't support image expressions\")]\n    ImageExpression,\n    #[error(\"Constants don't support ray query expressions\")]\n    RayQueryExpression,\n    #[error(\"Constants don't support subgroup expressions\")]\n    SubgroupExpression,\n    #[error(\"Cannot access the type\")]\n    InvalidAccessBase,\n    #[error(\"Cannot access at the index\")]\n    InvalidAccessIndex,\n    #[error(\"Cannot access with index of type\")]\n    InvalidAccessIndexTy,\n    #[error(\"Constants don't support array length expressions\")]\n    ArrayLength,\n    #[error(\"Cannot cast scalar components of expression `{from}` to type `{to}`\")]\n    InvalidCastArg { from: String, to: String },\n    #[error(\"Cannot apply the unary op to the argument\")]\n    InvalidUnaryOpArg,\n    #[error(\"Cannot apply the binary op to the arguments\")]\n    InvalidBinaryOpArgs,\n    #[error(\"Cannot apply math function to type\")]\n    InvalidMathArg,\n    #[error(\"{0:?} built-in function expects {1:?} arguments but {2:?} were supplied\")]\n    InvalidMathArgCount(crate::MathFunction, usize, usize),\n    #[error(\"{0} built-in function argument is out of valid range\")]\n    InvalidMathArgValue(String),\n    #[error(\"Cannot apply relational function to type\")]\n    InvalidRelationalArg(RelationalFunction),\n    #[error(\"value of `low` is greater than `high` for clamp built-in function\")]\n    InvalidClamp,\n    #[error(\"Constructor expects {expected} components, found {actual}\")]\n    InvalidVectorComposeLength { expected: usize, actual: usize },\n    #[error(\"Constructor must only contain vector or scalar arguments\")]\n    InvalidVectorComposeComponent,\n    #[error(\"Splat is defined only on scalar values\")]\n    SplatScalarOnly,\n    #[error(\"Can only swizzle vector constants\")]\n    SwizzleVectorOnly,\n    #[error(\"swizzle component not present in source expression\")]\n    SwizzleOutOfBounds,\n    #[error(\"Type is not constructible\")]\n    TypeNotConstructible,\n    #[error(\"Subexpression(s) are not constant\")]\n    SubexpressionsAreNotConstant,\n    #[error(\"Not implemented as constant expression: {0}\")]\n    NotImplemented(String),\n    #[error(\"{0} operation overflowed\")]\n    Overflow(String),\n    #[error(\n        \"the concrete type `{to_type}` cannot represent the abstract value `{value}` accurately\"\n    )]\n    AutomaticConversionLossy {\n        value: String,\n        to_type: &'static str,\n    },\n    #[error(\"Division by zero\")]\n    DivisionByZero,\n    #[error(\"Remainder by zero\")]\n    RemainderByZero,\n    #[error(\"RHS of shift operation is greater than or equal to 32\")]\n    ShiftedMoreThan32Bits,\n    #[error(transparent)]\n    Literal(#[from] crate::valid::LiteralError),\n    #[error(\"Can't use pipeline-overridable constants in const-expressions\")]\n    Override,\n    #[error(\"Unexpected runtime-expression\")]\n    RuntimeExpr,\n    #[error(\"Unexpected override-expression\")]\n    OverrideExpr,\n    #[error(\"Expected boolean expression for condition argument of `select`, got something else\")]\n    SelectScalarConditionNotABool,\n    #[error(\n        \"Expected vectors of the same size for reject and accept args., got {:?} and {:?}\",\n        reject,\n        accept\n    )]\n    SelectVecRejectAcceptSizeMismatch {\n        reject: crate::VectorSize,\n        accept: crate::VectorSize,\n    },\n    #[error(\"Expected boolean vector for condition arg., got something else\")]\n    SelectConditionNotAVecBool,\n    #[error(\n        \"Expected same number of vector components between condition, accept, and reject args., got something else\",\n    )]\n    SelectConditionVecSizeMismatch,\n    #[error(\n        \"Expected reject and accept args. to be scalars of vectors of the same type, got something else\",\n    )]\n    SelectAcceptRejectTypeMismatch,\n    #[error(\"Cooperative operations can't be constant\")]\n    CooperativeOperation,\n}\n\nimpl<'a> ConstantEvaluator<'a> {\n    /// Return a [`ConstantEvaluator`] that will add expressions to `module`'s\n    /// constant expression arena.\n    ///\n    /// Report errors according to WGSL's rules for constant evaluation.\n    pub const fn for_wgsl_module(\n        module: &'a mut crate::Module,\n        global_expression_kind_tracker: &'a mut ExpressionKindTracker,\n        layouter: &'a mut crate::proc::Layouter,\n        in_override_ctx: bool,\n    ) -> Self {\n        Self::for_module(\n            Behavior::Wgsl(if in_override_ctx {\n                WgslRestrictions::Override\n            } else {\n                WgslRestrictions::Const(None)\n            }),\n            module,\n            global_expression_kind_tracker,\n            layouter,\n        )\n    }\n\n    /// Return a [`ConstantEvaluator`] that will add expressions to `module`'s\n    /// constant expression arena.\n    ///\n    /// Report errors according to GLSL's rules for constant evaluation.\n    pub const fn for_glsl_module(\n        module: &'a mut crate::Module,\n        global_expression_kind_tracker: &'a mut ExpressionKindTracker,\n        layouter: &'a mut crate::proc::Layouter,\n    ) -> Self {\n        Self::for_module(\n            Behavior::Glsl(GlslRestrictions::Const),\n            module,\n            global_expression_kind_tracker,\n            layouter,\n        )\n    }\n\n    const fn for_module(\n        behavior: Behavior<'a>,\n        module: &'a mut crate::Module,\n        global_expression_kind_tracker: &'a mut ExpressionKindTracker,\n        layouter: &'a mut crate::proc::Layouter,\n    ) -> Self {\n        Self {\n            behavior,\n            types: &mut module.types,\n            constants: &module.constants,\n            overrides: &module.overrides,\n            expressions: &mut module.global_expressions,\n            expression_kind_tracker: global_expression_kind_tracker,\n            layouter,\n        }\n    }\n\n    /// Return a [`ConstantEvaluator`] that will add expressions to `function`'s\n    /// expression arena.\n    ///\n    /// Report errors according to WGSL's rules for constant evaluation.\n    pub const fn for_wgsl_function(\n        module: &'a mut crate::Module,\n        expressions: &'a mut Arena<Expression>,\n        local_expression_kind_tracker: &'a mut ExpressionKindTracker,\n        layouter: &'a mut crate::proc::Layouter,\n        emitter: &'a mut super::Emitter,\n        block: &'a mut crate::Block,\n        is_const: bool,\n    ) -> Self {\n        let local_data = FunctionLocalData {\n            global_expressions: &module.global_expressions,\n            emitter,\n            block,\n        };\n        Self {\n            behavior: Behavior::Wgsl(if is_const {\n                WgslRestrictions::Const(Some(local_data))\n            } else {\n                WgslRestrictions::Runtime(local_data)\n            }),\n            types: &mut module.types,\n            constants: &module.constants,\n            overrides: &module.overrides,\n            expressions,\n            expression_kind_tracker: local_expression_kind_tracker,\n            layouter,\n        }\n    }\n\n    /// Return a [`ConstantEvaluator`] that will add expressions to `function`'s\n    /// expression arena.\n    ///\n    /// Report errors according to GLSL's rules for constant evaluation.\n    pub const fn for_glsl_function(\n        module: &'a mut crate::Module,\n        expressions: &'a mut Arena<Expression>,\n        local_expression_kind_tracker: &'a mut ExpressionKindTracker,\n        layouter: &'a mut crate::proc::Layouter,\n        emitter: &'a mut super::Emitter,\n        block: &'a mut crate::Block,\n    ) -> Self {\n        Self {\n            behavior: Behavior::Glsl(GlslRestrictions::Runtime(FunctionLocalData {\n                global_expressions: &module.global_expressions,\n                emitter,\n                block,\n            })),\n            types: &mut module.types,\n            constants: &module.constants,\n            overrides: &module.overrides,\n            expressions,\n            expression_kind_tracker: local_expression_kind_tracker,\n            layouter,\n        }\n    }\n\n    pub const fn to_ctx(&self) -> crate::proc::GlobalCtx<'_> {\n        crate::proc::GlobalCtx {\n            types: self.types,\n            constants: self.constants,\n            overrides: self.overrides,\n            global_expressions: match self.function_local_data() {\n                Some(data) => data.global_expressions,\n                None => self.expressions,\n            },\n        }\n    }\n\n    fn check(&self, expr: Handle<Expression>) -> Result<(), ConstantEvaluatorError> {\n        if !self.expression_kind_tracker.is_const(expr) {\n            log::debug!(\"check: SubexpressionsAreNotConstant\");\n            return Err(ConstantEvaluatorError::SubexpressionsAreNotConstant);\n        }\n        Ok(())\n    }\n\n    fn check_and_get(\n        &mut self,\n        expr: Handle<Expression>,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expressions[expr] {\n            Expression::Constant(c) => {\n                // Are we working in a function's expression arena, or the\n                // module's constant expression arena?\n                if let Some(function_local_data) = self.function_local_data() {\n                    // Deep-copy the constant's value into our arena.\n                    self.copy_from(\n                        self.constants[c].init,\n                        function_local_data.global_expressions,\n                    )\n                } else {\n                    // \"See through\" the constant and use its initializer.\n                    Ok(self.constants[c].init)\n                }\n            }\n            _ => {\n                self.check(expr)?;\n                Ok(expr)\n            }\n        }\n    }\n\n    /// Try to evaluate `expr` at compile time.\n    ///\n    /// The `expr` argument can be any sort of Naga [`Expression`] you like. If\n    /// we can determine its value at compile time, we append an expression\n    /// representing its value - a tree of [`Literal`], [`Compose`],\n    /// [`ZeroValue`], and [`Swizzle`] expressions - to the expression arena\n    /// `self` contributes to.\n    ///\n    /// If `expr`'s value cannot be determined at compile time, and `self` is\n    /// contributing to some function's expression arena, then append `expr` to\n    /// that arena unchanged (and thus unevaluated). Otherwise, `self` must be\n    /// contributing to the module's constant expression arena; since `expr`'s\n    /// value is not a constant, return an error.\n    ///\n    /// We only consider `expr` itself, without recursing into its operands. Its\n    /// operands must all have been produced by prior calls to\n    /// `try_eval_and_append`, to ensure that they have already been reduced to\n    /// an evaluated form if possible.\n    ///\n    /// [`Literal`]: Expression::Literal\n    /// [`Compose`]: Expression::Compose\n    /// [`ZeroValue`]: Expression::ZeroValue\n    /// [`Swizzle`]: Expression::Swizzle\n    pub fn try_eval_and_append(\n        &mut self,\n        expr: Expression,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expression_kind_tracker.type_of_with_expr(&expr) {\n            ExpressionKind::Const => {\n                let eval_result = self.try_eval_and_append_impl(&expr, span);\n                // We should be able to evaluate `Const` expressions at this\n                // point. If we failed to, then that probably means we just\n                // haven't implemented that part of constant evaluation. Work\n                // around this by simply emitting it as a run-time expression.\n                if self.behavior.has_runtime_restrictions()\n                    && matches!(\n                        eval_result,\n                        Err(ConstantEvaluatorError::NotImplemented(_)\n                            | ConstantEvaluatorError::InvalidBinaryOpArgs,)\n                    )\n                {\n                    Ok(self.append_expr(expr, span, ExpressionKind::Runtime))\n                } else {\n                    eval_result\n                }\n            }\n            ExpressionKind::Override => match self.behavior {\n                Behavior::Wgsl(WgslRestrictions::Override | WgslRestrictions::Runtime(_)) => {\n                    Ok(self.append_expr(expr, span, ExpressionKind::Override))\n                }\n                Behavior::Wgsl(WgslRestrictions::Const(_)) => {\n                    Err(ConstantEvaluatorError::OverrideExpr)\n                }\n\n                // GLSL specialization constants (constant_id) become Override expressions\n                Behavior::Glsl(GlslRestrictions::Runtime(_)) => {\n                    Ok(self.append_expr(expr, span, ExpressionKind::Override))\n                }\n                Behavior::Glsl(GlslRestrictions::Const) => {\n                    Err(ConstantEvaluatorError::OverrideExpr)\n                }\n            },\n            ExpressionKind::Runtime => {\n                if self.behavior.has_runtime_restrictions() {\n                    Ok(self.append_expr(expr, span, ExpressionKind::Runtime))\n                } else {\n                    Err(ConstantEvaluatorError::RuntimeExpr)\n                }\n            }\n        }\n    }\n\n    /// Is the [`Self::expressions`] arena the global module expression arena?\n    const fn is_global_arena(&self) -> bool {\n        matches!(\n            self.behavior,\n            Behavior::Wgsl(WgslRestrictions::Const(None) | WgslRestrictions::Override)\n                | Behavior::Glsl(GlslRestrictions::Const)\n        )\n    }\n\n    const fn function_local_data(&self) -> Option<&FunctionLocalData<'a>> {\n        match self.behavior {\n            Behavior::Wgsl(\n                WgslRestrictions::Runtime(ref function_local_data)\n                | WgslRestrictions::Const(Some(ref function_local_data)),\n            )\n            | Behavior::Glsl(GlslRestrictions::Runtime(ref function_local_data)) => {\n                Some(function_local_data)\n            }\n            _ => None,\n        }\n    }\n\n    fn try_eval_and_append_impl(\n        &mut self,\n        expr: &Expression,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        log::trace!(\"try_eval_and_append: {expr:?}\");\n        match *expr {\n            Expression::Constant(c) if self.is_global_arena() => {\n                // \"See through\" the constant and use its initializer.\n                // This is mainly done to avoid having constants pointing to other constants.\n                Ok(self.constants[c].init)\n            }\n            Expression::Override(_) => Err(ConstantEvaluatorError::Override),\n            Expression::Literal(_) | Expression::ZeroValue(_) | Expression::Constant(_) => {\n                self.register_evaluated_expr(expr.clone(), span)\n            }\n            Expression::Compose { ty, ref components } => {\n                let components = components\n                    .iter()\n                    .map(|component| self.check_and_get(*component))\n                    .collect::<Result<Vec<_>, _>>()?;\n                self.register_evaluated_expr(Expression::Compose { ty, components }, span)\n            }\n            Expression::Splat { size, value } => {\n                let value = self.check_and_get(value)?;\n                self.register_evaluated_expr(Expression::Splat { size, value }, span)\n            }\n            Expression::AccessIndex { base, index } => {\n                let base = self.check_and_get(base)?;\n\n                self.access(base, index as usize, span)\n            }\n            Expression::Access { base, index } => {\n                let base = self.check_and_get(base)?;\n                let index = self.check_and_get(index)?;\n\n                let index_val: u32 = self\n                    .to_ctx()\n                    .get_const_val_from(index, self.expressions)\n                    .map_err(|_| ConstantEvaluatorError::InvalidAccessIndexTy)?;\n                self.access(base, index_val as usize, span)\n            }\n            Expression::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                let vector = self.check_and_get(vector)?;\n\n                self.swizzle(size, span, vector, pattern)\n            }\n            Expression::Unary { expr, op } => {\n                let expr = self.check_and_get(expr)?;\n\n                self.unary_op(op, expr, span)\n            }\n            Expression::Binary { left, right, op } => {\n                let left = self.check_and_get(left)?;\n                let right = self.check_and_get(right)?;\n\n                self.binary_op(op, left, right, span)\n            }\n            Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                let arg = self.check_and_get(arg)?;\n                let arg1 = arg1.map(|arg| self.check_and_get(arg)).transpose()?;\n                let arg2 = arg2.map(|arg| self.check_and_get(arg)).transpose()?;\n                let arg3 = arg3.map(|arg| self.check_and_get(arg)).transpose()?;\n\n                self.math(arg, arg1, arg2, arg3, fun, span)\n            }\n            Expression::As {\n                convert,\n                expr,\n                kind,\n            } => {\n                let expr = self.check_and_get(expr)?;\n\n                match convert {\n                    Some(width) => self.cast(expr, crate::Scalar { kind, width }, span),\n                    None => Err(ConstantEvaluatorError::NotImplemented(\n                        \"bitcast built-in function\".into(),\n                    )),\n                }\n            }\n            Expression::Select {\n                reject,\n                accept,\n                condition,\n            } => {\n                let mut arg = |expr| self.check_and_get(expr);\n\n                let reject = arg(reject)?;\n                let accept = arg(accept)?;\n                let condition = arg(condition)?;\n\n                self.select(reject, accept, condition, span)\n            }\n            Expression::Relational { fun, argument } => {\n                let argument = self.check_and_get(argument)?;\n                self.relational(fun, argument, span)\n            }\n            Expression::ArrayLength(expr) => match self.behavior {\n                Behavior::Wgsl(_) => Err(ConstantEvaluatorError::ArrayLength),\n                Behavior::Glsl(_) => {\n                    let expr = self.check_and_get(expr)?;\n                    self.array_length(expr, span)\n                }\n            },\n            Expression::Load { .. } => Err(ConstantEvaluatorError::Load),\n            Expression::LocalVariable(_) => Err(ConstantEvaluatorError::LocalVariable),\n            Expression::Derivative { .. } => Err(ConstantEvaluatorError::Derivative),\n            Expression::CallResult { .. } => Err(ConstantEvaluatorError::Call),\n            Expression::WorkGroupUniformLoadResult { .. } => {\n                Err(ConstantEvaluatorError::WorkGroupUniformLoadResult)\n            }\n            Expression::AtomicResult { .. } => Err(ConstantEvaluatorError::Atomic),\n            Expression::FunctionArgument(_) => Err(ConstantEvaluatorError::FunctionArg),\n            Expression::GlobalVariable(_) => Err(ConstantEvaluatorError::GlobalVariable),\n            Expression::ImageSample { .. }\n            | Expression::ImageLoad { .. }\n            | Expression::ImageQuery { .. } => Err(ConstantEvaluatorError::ImageExpression),\n            Expression::RayQueryProceedResult\n            | Expression::RayQueryGetIntersection { .. }\n            | Expression::RayQueryVertexPositions { .. } => {\n                Err(ConstantEvaluatorError::RayQueryExpression)\n            }\n            Expression::SubgroupBallotResult => Err(ConstantEvaluatorError::SubgroupExpression),\n            Expression::SubgroupOperationResult { .. } => {\n                Err(ConstantEvaluatorError::SubgroupExpression)\n            }\n            Expression::CooperativeLoad { .. } | Expression::CooperativeMultiplyAdd { .. } => {\n                Err(ConstantEvaluatorError::CooperativeOperation)\n            }\n        }\n    }\n\n    /// Splat `value` to `size`, without using [`Splat`] expressions.\n    ///\n    /// This constructs [`Compose`] or [`ZeroValue`] expressions to\n    /// build a vector with the given `size` whose components are all\n    /// `value`.\n    ///\n    /// Use `span` as the span of the inserted expressions and\n    /// resulting types.\n    ///\n    /// [`Splat`]: Expression::Splat\n    /// [`Compose`]: Expression::Compose\n    /// [`ZeroValue`]: Expression::ZeroValue\n    fn splat(\n        &mut self,\n        value: Handle<Expression>,\n        size: crate::VectorSize,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expressions[value] {\n            Expression::Literal(literal) => {\n                let scalar = literal.scalar();\n                let ty = self.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector { size, scalar },\n                    },\n                    span,\n                );\n                let expr = Expression::Compose {\n                    ty,\n                    components: vec![value; size as usize],\n                };\n                self.register_evaluated_expr(expr, span)\n            }\n            Expression::ZeroValue(ty) => {\n                let inner = match self.types[ty].inner {\n                    TypeInner::Scalar(scalar) => TypeInner::Vector { size, scalar },\n                    _ => return Err(ConstantEvaluatorError::SplatScalarOnly),\n                };\n                let res_ty = self.types.insert(Type { name: None, inner }, span);\n                let expr = Expression::ZeroValue(res_ty);\n                self.register_evaluated_expr(expr, span)\n            }\n            _ => Err(ConstantEvaluatorError::SplatScalarOnly),\n        }\n    }\n\n    fn swizzle(\n        &mut self,\n        size: crate::VectorSize,\n        span: Span,\n        src_constant: Handle<Expression>,\n        pattern: [crate::SwizzleComponent; 4],\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let mut get_dst_ty = |ty| match self.types[ty].inner {\n            TypeInner::Vector { size: _, scalar } => Ok(self.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Vector { size, scalar },\n                },\n                span,\n            )),\n            _ => Err(ConstantEvaluatorError::SwizzleVectorOnly),\n        };\n\n        match self.expressions[src_constant] {\n            Expression::ZeroValue(ty) => {\n                let dst_ty = get_dst_ty(ty)?;\n                let expr = Expression::ZeroValue(dst_ty);\n                self.register_evaluated_expr(expr, span)\n            }\n            Expression::Splat { value, .. } => {\n                let expr = Expression::Splat { size, value };\n                self.register_evaluated_expr(expr, span)\n            }\n            Expression::Compose { ty, ref components } => {\n                let dst_ty = get_dst_ty(ty)?;\n\n                let mut flattened = [src_constant; 4]; // dummy value\n                let len =\n                    crate::proc::flatten_compose(ty, components, self.expressions, self.types)\n                        .zip(flattened.iter_mut())\n                        .map(|(component, elt)| *elt = component)\n                        .count();\n                let flattened = &flattened[..len];\n\n                let swizzled_components = pattern[..size as usize]\n                    .iter()\n                    .map(|&sc| {\n                        let sc = sc as usize;\n                        if let Some(elt) = flattened.get(sc) {\n                            Ok(*elt)\n                        } else {\n                            Err(ConstantEvaluatorError::SwizzleOutOfBounds)\n                        }\n                    })\n                    .collect::<Result<Vec<Handle<Expression>>, _>>()?;\n                let expr = Expression::Compose {\n                    ty: dst_ty,\n                    components: swizzled_components,\n                };\n                self.register_evaluated_expr(expr, span)\n            }\n            _ => Err(ConstantEvaluatorError::SwizzleVectorOnly),\n        }\n    }\n\n    fn math(\n        &mut self,\n        arg: Handle<Expression>,\n        arg1: Option<Handle<Expression>>,\n        arg2: Option<Handle<Expression>>,\n        arg3: Option<Handle<Expression>>,\n        fun: crate::MathFunction,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let expected = fun.argument_count();\n        let given = Some(arg)\n            .into_iter()\n            .chain(arg1)\n            .chain(arg2)\n            .chain(arg3)\n            .count();\n        if expected != given {\n            return Err(ConstantEvaluatorError::InvalidMathArgCount(\n                fun, expected, given,\n            ));\n        }\n\n        // NOTE: We try to match the declaration order of `MathFunction` here.\n        match fun {\n            // comparison\n            crate::MathFunction::Abs => {\n                component_wise_scalar(self, span, [arg], |args| match args {\n                    Scalar::AbstractFloat([e]) => Ok(Scalar::AbstractFloat([e.abs()])),\n                    Scalar::F32([e]) => Ok(Scalar::F32([e.abs()])),\n                    Scalar::F16([e]) => Ok(Scalar::F16([e.abs()])),\n                    Scalar::AbstractInt([e]) => Ok(Scalar::AbstractInt([e.wrapping_abs()])),\n                    Scalar::I32([e]) => Ok(Scalar::I32([e.wrapping_abs()])),\n                    Scalar::U32([e]) => Ok(Scalar::U32([e])), // TODO: just re-use the expression, ezpz\n                    Scalar::I64([e]) => Ok(Scalar::I64([e.wrapping_abs()])),\n                    Scalar::U64([e]) => Ok(Scalar::U64([e])),\n                })\n            }\n            crate::MathFunction::Min => {\n                component_wise_scalar!(self, span, [arg, arg1.unwrap()], |e1, e2| {\n                    Ok([e1.min(e2)])\n                })\n            }\n            crate::MathFunction::Max => {\n                component_wise_scalar!(self, span, [arg, arg1.unwrap()], |e1, e2| {\n                    Ok([e1.max(e2)])\n                })\n            }\n            crate::MathFunction::Clamp => {\n                component_wise_scalar!(\n                    self,\n                    span,\n                    [arg, arg1.unwrap(), arg2.unwrap()],\n                    |e, low, high| {\n                        if low > high {\n                            Err(ConstantEvaluatorError::InvalidClamp)\n                        } else {\n                            Ok([e.clamp(low, high)])\n                        }\n                    }\n                )\n            }\n            crate::MathFunction::Saturate => component_wise_float(self, span, [arg], |e| match e {\n                Float::F16([e]) => Ok(Float::F16(\n                    [e.clamp(f16::from_f32(0.0), f16::from_f32(1.0))],\n                )),\n                Float::F32([e]) => Ok(Float::F32([e.clamp(0., 1.)])),\n                Float::Abstract([e]) => Ok(Float::Abstract([e.clamp(0., 1.)])),\n            }),\n\n            // trigonometry\n            crate::MathFunction::Cos => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.cos()]) })\n            }\n            crate::MathFunction::Cosh => {\n                component_wise_float!(self, span, [arg], |e| {\n                    let result = e.cosh();\n                    if result.is_finite() {\n                        Ok([result])\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"cosh\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Sin => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.sin()]) })\n            }\n            crate::MathFunction::Sinh => {\n                component_wise_float!(self, span, [arg], |e| {\n                    let result = e.sinh();\n                    if result.is_finite() {\n                        Ok([result])\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"sinh\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Tan => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.tan()]) })\n            }\n            crate::MathFunction::Tanh => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.tanh()]) })\n            }\n            crate::MathFunction::Acos => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e.abs() <= One::one() {\n                        Ok([e.acos()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"acos\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Asin => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e.abs() <= One::one() {\n                        Ok([e.asin()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"asin\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Atan => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.atan()]) })\n            }\n            crate::MathFunction::Atan2 => {\n                component_wise_float!(self, span, [arg, arg1.unwrap()], |y, x| {\n                    Ok([y.atan2(x)])\n                })\n            }\n            crate::MathFunction::Asinh => component_wise_float(self, span, [arg], |e| match e {\n                Float::Abstract([e]) => Ok(Float::Abstract([libm::asinh(e)])),\n                Float::F32([e]) => Ok(Float::F32([(e as f64).asinh() as f32])),\n                Float::F16([e]) => Ok(Float::F16([e.asinh()])),\n            }),\n            crate::MathFunction::Acosh => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.acosh()]) })\n            }\n            crate::MathFunction::Atanh => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e.abs() < One::one() {\n                        Ok([e.atanh()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"atanh\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Radians => {\n                component_wise_float!(self, span, [arg], |e1| { Ok([e1.to_radians()]) })\n            }\n            crate::MathFunction::Degrees => {\n                component_wise_float!(self, span, [arg], |e| {\n                    let result = e.to_degrees();\n                    if result.is_finite() {\n                        Ok([result])\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"degrees\".into()))\n                    }\n                })\n            }\n\n            // decomposition\n            crate::MathFunction::Ceil => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.ceil()]) })\n            }\n            crate::MathFunction::Floor => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.floor()]) })\n            }\n            crate::MathFunction::Round => {\n                component_wise_float(self, span, [arg], |e| match e {\n                    Float::Abstract([e]) => Ok(Float::Abstract([libm::rint(e)])),\n                    Float::F32([e]) => Ok(Float::F32([libm::rintf(e)])),\n                    Float::F16([e]) => {\n                        // TODO: `round_ties_even` is not available on `half::f16` yet.\n                        //\n                        // This polyfill is shamelessly [~~stolen from~~ inspired by `ndarray-image`][polyfill source],\n                        // which has licensing compatible with ours. See also\n                        // <https://github.com/rust-lang/rust/issues/96710>.\n                        //\n                        // [polyfill source]: https://github.com/imeka/ndarray-ndimage/blob/8b14b4d6ecfbc96a8a052f802e342a7049c68d8f/src/lib.rs#L98\n                        fn round_ties_even(x: f64) -> f64 {\n                            let i = x as i64;\n                            let f = (x - i as f64).abs();\n                            if f == 0.5 {\n                                if i & 1 == 1 {\n                                    // -1.5, 1.5, 3.5, ...\n                                    (x.abs() + 0.5).copysign(x)\n                                } else {\n                                    (x.abs() - 0.5).copysign(x)\n                                }\n                            } else {\n                                x.round()\n                            }\n                        }\n\n                        Ok(Float::F16([(f16::from_f64(round_ties_even(f64::from(e))))]))\n                    }\n                })\n            }\n            crate::MathFunction::Fract => {\n                component_wise_float!(self, span, [arg], |e| {\n                    // N.B., Rust's definition of `fract` is `e - e.trunc()`, so we can't use that\n                    // here.\n                    Ok([e - e.floor()])\n                })\n            }\n            crate::MathFunction::Trunc => {\n                component_wise_float!(self, span, [arg], |e| { Ok([e.trunc()]) })\n            }\n\n            // exponent\n            crate::MathFunction::Exp => {\n                component_wise_float!(self, span, [arg], |e| {\n                    let result = e.exp();\n                    if result.is_finite() {\n                        Ok([result])\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"exp\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Exp2 => {\n                component_wise_float!(self, span, [arg], |e| {\n                    let result = e.exp2();\n                    if result.is_finite() {\n                        Ok([result])\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"exp2\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Log => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e > Zero::zero() {\n                        Ok([e.ln()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"log\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Log2 => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e > Zero::zero() {\n                        Ok([e.log2()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"log2\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::Pow => {\n                component_wise_float!(self, span, [arg, arg1.unwrap()], |e1, e2| {\n                    Ok([e1.powf(e2)])\n                })\n            }\n\n            // computational\n            crate::MathFunction::Sign => {\n                component_wise_signed!(self, span, [arg], |e| {\n                    Ok([if e.is_zero() {\n                        Zero::zero()\n                    } else {\n                        e.signum()\n                    }])\n                })\n            }\n            crate::MathFunction::Fma => {\n                component_wise_float!(\n                    self,\n                    span,\n                    [arg, arg1.unwrap(), arg2.unwrap()],\n                    |e1, e2, e3| { Ok([e1.mul_add(e2, e3)]) }\n                )\n            }\n            crate::MathFunction::Step => {\n                component_wise_float(self, span, [arg, arg1.unwrap()], |x| match x {\n                    Float::Abstract([edge, x]) => {\n                        Ok(Float::Abstract([if edge <= x { 1.0 } else { 0.0 }]))\n                    }\n                    Float::F32([edge, x]) => Ok(Float::F32([if edge <= x { 1.0 } else { 0.0 }])),\n                    Float::F16([edge, x]) => Ok(Float::F16([if edge <= x {\n                        f16::one()\n                    } else {\n                        f16::zero()\n                    }])),\n                })\n            }\n            crate::MathFunction::Sqrt => {\n                component_wise_float!(self, span, [arg], |e| {\n                    if e >= Zero::zero() {\n                        Ok([e.sqrt()])\n                    } else {\n                        Err(ConstantEvaluatorError::InvalidMathArgValue(\"sqrt\".into()))\n                    }\n                })\n            }\n            crate::MathFunction::InverseSqrt => {\n                component_wise_float(self, span, [arg], |e| match e {\n                    Float::Abstract([e]) => Ok(Float::Abstract([1. / e.sqrt()])),\n                    Float::F32([e]) => Ok(Float::F32([1. / e.sqrt()])),\n                    Float::F16([e]) => Ok(Float::F16([f16::from_f32(1. / f32::from(e).sqrt())])),\n                })\n            }\n\n            // bits\n            crate::MathFunction::CountTrailingZeros => {\n                component_wise_concrete_int!(self, span, [arg], |e| {\n                    #[allow(clippy::useless_conversion)]\n                    Ok([e\n                        .trailing_zeros()\n                        .try_into()\n                        .expect(\"bit count overflowed 32 bits, somehow!?\")])\n                })\n            }\n            crate::MathFunction::CountLeadingZeros => {\n                component_wise_concrete_int!(self, span, [arg], |e| {\n                    #[allow(clippy::useless_conversion)]\n                    Ok([e\n                        .leading_zeros()\n                        .try_into()\n                        .expect(\"bit count overflowed 32 bits, somehow!?\")])\n                })\n            }\n            crate::MathFunction::CountOneBits => {\n                component_wise_concrete_int!(self, span, [arg], |e| {\n                    #[allow(clippy::useless_conversion)]\n                    Ok([e\n                        .count_ones()\n                        .try_into()\n                        .expect(\"bit count overflowed 32 bits, somehow!?\")])\n                })\n            }\n            crate::MathFunction::ReverseBits => {\n                component_wise_concrete_int!(self, span, [arg], |e| { Ok([e.reverse_bits()]) })\n            }\n            crate::MathFunction::FirstTrailingBit => {\n                component_wise_concrete_int(self, span, [arg], |ci| Ok(first_trailing_bit(ci)))\n            }\n            crate::MathFunction::FirstLeadingBit => {\n                component_wise_concrete_int(self, span, [arg], |ci| Ok(first_leading_bit(ci)))\n            }\n\n            // vector\n            crate::MathFunction::Dot4I8Packed => {\n                self.packed_dot_product(arg, arg1.unwrap(), span, true)\n            }\n            crate::MathFunction::Dot4U8Packed => {\n                self.packed_dot_product(arg, arg1.unwrap(), span, false)\n            }\n            crate::MathFunction::Cross => self.cross_product(arg, arg1.unwrap(), span),\n            crate::MathFunction::Dot => {\n                // https://www.w3.org/TR/WGSL/#dot-builtin\n                let e1 = self.extract_vec(arg, false)?;\n                let e2 = self.extract_vec(arg1.unwrap(), false)?;\n                if e1.len() != e2.len() {\n                    return Err(ConstantEvaluatorError::InvalidMathArg);\n                }\n\n                fn float_dot_checked<P>(a: &[P], b: &[P]) -> Result<P, ConstantEvaluatorError>\n                where\n                    P: num_traits::Float,\n                {\n                    let result = a\n                        .iter()\n                        .zip(b.iter())\n                        .map(|(&aa, &bb)| aa * bb)\n                        .fold(P::zero(), |acc, x| acc + x);\n                    if result.is_finite() {\n                        Ok(result)\n                    } else {\n                        Err(ConstantEvaluatorError::Overflow(\"in dot built-in\".into()))\n                    }\n                }\n\n                fn int_dot_checked<P>(a: &[P], b: &[P]) -> Result<P, ConstantEvaluatorError>\n                where\n                    P: num_traits::PrimInt + num_traits::CheckedAdd + num_traits::CheckedMul,\n                {\n                    a.iter()\n                        .zip(b.iter())\n                        .map(|(&aa, bb)| aa.checked_mul(bb))\n                        .try_fold(P::zero(), |acc, x| {\n                            if let Some(x) = x {\n                                acc.checked_add(&x)\n                            } else {\n                                None\n                            }\n                        })\n                        .ok_or(ConstantEvaluatorError::Overflow(\n                            \"in dot built-in\".to_string(),\n                        ))\n                }\n\n                fn int_dot_wrapping<P>(a: &[P], b: &[P]) -> P\n                where\n                    P: num_traits::PrimInt + num_traits::WrappingAdd + num_traits::WrappingMul,\n                {\n                    a.iter()\n                        .zip(b.iter())\n                        .map(|(&aa, bb)| aa.wrapping_mul(bb))\n                        .fold(P::zero(), |acc, x| acc.wrapping_add(&x))\n                }\n\n                let result = match_literal_vector!(match (e1, e2) => Literal {\n                    Float => |e1, e2| { float_dot_checked(e1, e2)? },\n                    AbstractInt => |e1, e2 | { int_dot_checked(e1, e2)? },\n                    I32 => |e1, e2| { int_dot_wrapping(e1, e2) },\n                    U32 => |e1, e2| { int_dot_wrapping(e1, e2) },\n                })?;\n                self.register_evaluated_expr(Expression::Literal(result), span)\n            }\n            crate::MathFunction::Length => {\n                // https://www.w3.org/TR/WGSL/#length-builtin\n                let e1 = self.extract_vec(arg, true)?;\n\n                fn float_length<F>(e: &[F]) -> F\n                where\n                    F: core::ops::Mul<F>,\n                    F: num_traits::Float + iter::Sum,\n                {\n                    if e.len() == 1 {\n                        // Avoids possible overflow in squaring\n                        e[0].abs()\n                    } else {\n                        e.iter().map(|&ei| ei * ei).sum::<F>().sqrt()\n                    }\n                }\n\n                let result = match_literal_vector!(match e1 => Literal {\n                    Float => |e1| { float_length(e1) },\n                })?;\n                self.register_evaluated_expr(Expression::Literal(result), span)\n            }\n            crate::MathFunction::Distance => {\n                // https://www.w3.org/TR/WGSL/#distance-builtin\n                let e1 = self.extract_vec(arg, true)?;\n                let e2 = self.extract_vec(arg1.unwrap(), true)?;\n                if e1.len() != e2.len() {\n                    return Err(ConstantEvaluatorError::InvalidMathArg);\n                }\n\n                fn float_distance<F>(a: &[F], b: &[F]) -> F\n                where\n                    F: core::ops::Mul<F>,\n                    F: num_traits::Float + iter::Sum + core::ops::Sub,\n                {\n                    if a.len() == 1 {\n                        // Avoids possible overflow in squaring\n                        (a[0] - b[0]).abs()\n                    } else {\n                        a.iter()\n                            .zip(b.iter())\n                            .map(|(&aa, &bb)| aa - bb)\n                            .map(|ei| ei * ei)\n                            .sum::<F>()\n                            .sqrt()\n                    }\n                }\n                let result = match_literal_vector!(match (e1, e2) => Literal {\n                    Float => |e1, e2| { float_distance(e1, e2) },\n                })?;\n                self.register_evaluated_expr(Expression::Literal(result), span)\n            }\n            crate::MathFunction::Normalize => {\n                // https://www.w3.org/TR/WGSL/#normalize-builtin\n                let e1 = self.extract_vec(arg, true)?;\n\n                fn float_normalize<F>(e: &[F]) -> ArrayVec<F, { crate::VectorSize::MAX }>\n                where\n                    F: core::ops::Mul<F>,\n                    F: num_traits::Float + iter::Sum,\n                {\n                    let len = e.iter().map(|&ei| ei * ei).sum::<F>().sqrt();\n                    let mut out = ArrayVec::new();\n                    for &ei in e {\n                        out.push(ei / len);\n                    }\n                    out\n                }\n\n                let result = match_literal_vector!(match e1 => LiteralVector {\n                    Float => |e1| { float_normalize(e1) },\n                })?;\n                result.register_as_evaluated_expr(self, span)\n            }\n\n            // unimplemented\n            crate::MathFunction::Modf\n            | crate::MathFunction::Frexp\n            | crate::MathFunction::Ldexp\n            | crate::MathFunction::Outer\n            | crate::MathFunction::FaceForward\n            | crate::MathFunction::Reflect\n            | crate::MathFunction::Refract\n            | crate::MathFunction::Mix\n            | crate::MathFunction::SmoothStep\n            | crate::MathFunction::Inverse\n            | crate::MathFunction::Transpose\n            | crate::MathFunction::Determinant\n            | crate::MathFunction::QuantizeToF16\n            | crate::MathFunction::ExtractBits\n            | crate::MathFunction::InsertBits\n            | crate::MathFunction::Pack4x8snorm\n            | crate::MathFunction::Pack4x8unorm\n            | crate::MathFunction::Pack2x16snorm\n            | crate::MathFunction::Pack2x16unorm\n            | crate::MathFunction::Pack2x16float\n            | crate::MathFunction::Pack4xI8\n            | crate::MathFunction::Pack4xU8\n            | crate::MathFunction::Pack4xI8Clamp\n            | crate::MathFunction::Pack4xU8Clamp\n            | crate::MathFunction::Unpack4x8snorm\n            | crate::MathFunction::Unpack4x8unorm\n            | crate::MathFunction::Unpack2x16snorm\n            | crate::MathFunction::Unpack2x16unorm\n            | crate::MathFunction::Unpack2x16float\n            | crate::MathFunction::Unpack4xI8\n            | crate::MathFunction::Unpack4xU8 => Err(ConstantEvaluatorError::NotImplemented(\n                format!(\"{fun:?} built-in function\"),\n            )),\n        }\n    }\n\n    /// Dot product of two packed vectors (`dot4I8Packed` and `dot4U8Packed`)\n    fn packed_dot_product(\n        &mut self,\n        a: Handle<Expression>,\n        b: Handle<Expression>,\n        span: Span,\n        signed: bool,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let Expression::Literal(Literal::U32(a)) = self.expressions[a] else {\n            return Err(ConstantEvaluatorError::InvalidMathArg);\n        };\n        let Expression::Literal(Literal::U32(b)) = self.expressions[b] else {\n            return Err(ConstantEvaluatorError::InvalidMathArg);\n        };\n\n        let result = if signed {\n            Literal::I32(\n                (a & 0xFF) as i8 as i32 * (b & 0xFF) as i8 as i32\n                    + ((a >> 8) & 0xFF) as i8 as i32 * ((b >> 8) & 0xFF) as i8 as i32\n                    + ((a >> 16) & 0xFF) as i8 as i32 * ((b >> 16) & 0xFF) as i8 as i32\n                    + ((a >> 24) & 0xFF) as i8 as i32 * ((b >> 24) & 0xFF) as i8 as i32,\n            )\n        } else {\n            Literal::U32(\n                (a & 0xFF) * (b & 0xFF)\n                    + ((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)\n                    + ((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)\n                    + ((a >> 24) & 0xFF) * ((b >> 24) & 0xFF),\n            )\n        };\n\n        self.register_evaluated_expr(Expression::Literal(result), span)\n    }\n\n    /// Vector cross product.\n    fn cross_product(\n        &mut self,\n        a: Handle<Expression>,\n        b: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        use Literal as Li;\n\n        let (a, ty) = self.extract_vec_with_size::<3>(a)?;\n        let (b, _) = self.extract_vec_with_size::<3>(b)?;\n\n        let product = match (a, b) {\n            (\n                [Li::AbstractInt(a0), Li::AbstractInt(a1), Li::AbstractInt(a2)],\n                [Li::AbstractInt(b0), Li::AbstractInt(b1), Li::AbstractInt(b2)],\n            ) => {\n                // `cross` has no overload for AbstractInt, so AbstractInt\n                // arguments are automatically converted to AbstractFloat. Since\n                // `f64` has a much wider range than `i64`, there's no danger of\n                // overflow here.\n                let p = cross_product(\n                    [a0 as f64, a1 as f64, a2 as f64],\n                    [b0 as f64, b1 as f64, b2 as f64],\n                );\n                [\n                    Li::AbstractFloat(p[0]),\n                    Li::AbstractFloat(p[1]),\n                    Li::AbstractFloat(p[2]),\n                ]\n            }\n            (\n                [Li::AbstractFloat(a0), Li::AbstractFloat(a1), Li::AbstractFloat(a2)],\n                [Li::AbstractFloat(b0), Li::AbstractFloat(b1), Li::AbstractFloat(b2)],\n            ) => {\n                let p = cross_product([a0, a1, a2], [b0, b1, b2]);\n                [\n                    Li::AbstractFloat(p[0]),\n                    Li::AbstractFloat(p[1]),\n                    Li::AbstractFloat(p[2]),\n                ]\n            }\n            ([Li::F16(a0), Li::F16(a1), Li::F16(a2)], [Li::F16(b0), Li::F16(b1), Li::F16(b2)]) => {\n                let p = cross_product([a0, a1, a2], [b0, b1, b2]);\n                [Li::F16(p[0]), Li::F16(p[1]), Li::F16(p[2])]\n            }\n            ([Li::F32(a0), Li::F32(a1), Li::F32(a2)], [Li::F32(b0), Li::F32(b1), Li::F32(b2)]) => {\n                let p = cross_product([a0, a1, a2], [b0, b1, b2]);\n                [Li::F32(p[0]), Li::F32(p[1]), Li::F32(p[2])]\n            }\n            ([Li::F64(a0), Li::F64(a1), Li::F64(a2)], [Li::F64(b0), Li::F64(b1), Li::F64(b2)]) => {\n                let p = cross_product([a0, a1, a2], [b0, b1, b2]);\n                [Li::F64(p[0]), Li::F64(p[1]), Li::F64(p[2])]\n            }\n            _ => return Err(ConstantEvaluatorError::InvalidMathArg),\n        };\n\n        let p0 = self.register_evaluated_expr(Expression::Literal(product[0]), span)?;\n        let p1 = self.register_evaluated_expr(Expression::Literal(product[1]), span)?;\n        let p2 = self.register_evaluated_expr(Expression::Literal(product[2]), span)?;\n\n        self.register_evaluated_expr(\n            Expression::Compose {\n                ty,\n                components: vec![p0, p1, p2],\n            },\n            span,\n        )\n    }\n\n    /// Extract the values of a `vecN` from `expr`.\n    ///\n    /// Return the value of `expr`, whose type is `vecN<S>` for some\n    /// vector size `N` and scalar `S`, as an array of `N` [`Literal`]\n    /// values.\n    ///\n    /// Also return the type handle from the `Compose` expression.\n    fn extract_vec_with_size<const N: usize>(\n        &mut self,\n        expr: Handle<Expression>,\n    ) -> Result<([Literal; N], Handle<Type>), ConstantEvaluatorError> {\n        let span = self.expressions.get_span(expr);\n        let expr = self.eval_zero_value_and_splat(expr, span)?;\n        let Expression::Compose { ty, ref components } = self.expressions[expr] else {\n            return Err(ConstantEvaluatorError::InvalidMathArg);\n        };\n\n        let mut value = [Literal::Bool(false); N];\n        for (component, elt) in\n            crate::proc::flatten_compose(ty, components, self.expressions, self.types)\n                .zip(value.iter_mut())\n        {\n            let Expression::Literal(literal) = self.expressions[component] else {\n                return Err(ConstantEvaluatorError::InvalidMathArg);\n            };\n            *elt = literal;\n        }\n\n        Ok((value, ty))\n    }\n\n    /// Extract the values of a `vecN` from `expr`.\n    ///\n    /// Return the value of `expr`, whose type is `vecN<S>` for some\n    /// vector size `N` and scalar `S`, as an array of `N` [`Literal`]\n    /// values.\n    ///\n    /// Also return the type handle from the `Compose` expression.\n    fn extract_vec(\n        &mut self,\n        expr: Handle<Expression>,\n        allow_single: bool,\n    ) -> Result<LiteralVector, ConstantEvaluatorError> {\n        let span = self.expressions.get_span(expr);\n        let expr = self.eval_zero_value_and_splat(expr, span)?;\n\n        match self.expressions[expr] {\n            Expression::Literal(literal) if allow_single => {\n                Ok(LiteralVector::from_literal(literal))\n            }\n            Expression::Compose { ty, ref components } => {\n                let mut components_out = ArrayVec::<Literal, { crate::VectorSize::MAX }>::new();\n                for expr in\n                    crate::proc::flatten_compose(ty, components, self.expressions, self.types)\n                {\n                    match self.expressions[expr] {\n                        Expression::Literal(l) => components_out.push(l),\n                        _ => return Err(ConstantEvaluatorError::InvalidMathArg),\n                    }\n                }\n                LiteralVector::from_literal_vec(components_out)\n            }\n            _ => Err(ConstantEvaluatorError::InvalidMathArg),\n        }\n    }\n\n    fn array_length(\n        &mut self,\n        array: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expressions[array] {\n            Expression::ZeroValue(ty) | Expression::Compose { ty, .. } => {\n                match self.types[ty].inner {\n                    TypeInner::Array { size, .. } => match size {\n                        ArraySize::Constant(len) => {\n                            let expr = Expression::Literal(Literal::U32(len.get()));\n                            self.register_evaluated_expr(expr, span)\n                        }\n                        ArraySize::Pending(_) => Err(ConstantEvaluatorError::ArrayLengthOverridden),\n                        ArraySize::Dynamic => Err(ConstantEvaluatorError::ArrayLengthDynamic),\n                    },\n                    _ => Err(ConstantEvaluatorError::InvalidArrayLengthArg),\n                }\n            }\n            _ => Err(ConstantEvaluatorError::InvalidArrayLengthArg),\n        }\n    }\n\n    fn access(\n        &mut self,\n        base: Handle<Expression>,\n        index: usize,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expressions[base] {\n            Expression::ZeroValue(ty) => {\n                let ty_inner = &self.types[ty].inner;\n                let components = ty_inner\n                    .components()\n                    .ok_or(ConstantEvaluatorError::InvalidAccessBase)?;\n\n                if index >= components as usize {\n                    Err(ConstantEvaluatorError::InvalidAccessBase)\n                } else {\n                    let ty_res = ty_inner\n                        .component_type(index)\n                        .ok_or(ConstantEvaluatorError::InvalidAccessIndex)?;\n                    let ty = match ty_res {\n                        crate::proc::TypeResolution::Handle(ty) => ty,\n                        crate::proc::TypeResolution::Value(inner) => {\n                            self.types.insert(Type { name: None, inner }, span)\n                        }\n                    };\n                    self.register_evaluated_expr(Expression::ZeroValue(ty), span)\n                }\n            }\n            Expression::Splat { size, value } => {\n                if index >= size as usize {\n                    Err(ConstantEvaluatorError::InvalidAccessBase)\n                } else {\n                    Ok(value)\n                }\n            }\n            Expression::Compose { ty, ref components } => {\n                let _ = self.types[ty]\n                    .inner\n                    .components()\n                    .ok_or(ConstantEvaluatorError::InvalidAccessBase)?;\n\n                crate::proc::flatten_compose(ty, components, self.expressions, self.types)\n                    .nth(index)\n                    .ok_or(ConstantEvaluatorError::InvalidAccessIndex)\n            }\n            _ => Err(ConstantEvaluatorError::InvalidAccessBase),\n        }\n    }\n\n    /// Lower [`ZeroValue`] and [`Splat`] expressions to [`Literal`] and [`Compose`] expressions.\n    ///\n    /// [`ZeroValue`]: Expression::ZeroValue\n    /// [`Splat`]: Expression::Splat\n    /// [`Literal`]: Expression::Literal\n    /// [`Compose`]: Expression::Compose\n    fn eval_zero_value_and_splat(\n        &mut self,\n        mut expr: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        // If expr is a Compose expression, eliminate ZeroValue and Splat expressions for\n        // each of its components.\n        if let Expression::Compose { ty, ref components } = self.expressions[expr] {\n            let components = components\n                .clone()\n                .iter()\n                .map(|component| self.eval_zero_value_and_splat(*component, span))\n                .collect::<Result<_, _>>()?;\n            expr = self.register_evaluated_expr(Expression::Compose { ty, components }, span)?;\n        }\n\n        // The result of the splat() for a Splat of a scalar ZeroValue is a\n        // vector ZeroValue, so we must call eval_zero_value_impl() after\n        // splat() in order to ensure we have no ZeroValues remaining.\n        if let Expression::Splat { size, value } = self.expressions[expr] {\n            expr = self.splat(value, size, span)?;\n        }\n        if let Expression::ZeroValue(ty) = self.expressions[expr] {\n            expr = self.eval_zero_value_impl(ty, span)?;\n        }\n        Ok(expr)\n    }\n\n    /// Lower [`ZeroValue`] expressions to [`Literal`] and [`Compose`] expressions.\n    ///\n    /// [`ZeroValue`]: Expression::ZeroValue\n    /// [`Literal`]: Expression::Literal\n    /// [`Compose`]: Expression::Compose\n    fn eval_zero_value(\n        &mut self,\n        expr: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.expressions[expr] {\n            Expression::ZeroValue(ty) => self.eval_zero_value_impl(ty, span),\n            _ => Ok(expr),\n        }\n    }\n\n    /// Lower [`ZeroValue`] expressions to [`Literal`] and [`Compose`] expressions.\n    ///\n    /// [`ZeroValue`]: Expression::ZeroValue\n    /// [`Literal`]: Expression::Literal\n    /// [`Compose`]: Expression::Compose\n    fn eval_zero_value_impl(\n        &mut self,\n        ty: Handle<Type>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        match self.types[ty].inner {\n            TypeInner::Scalar(scalar) => {\n                let expr = Expression::Literal(\n                    Literal::zero(scalar).ok_or(ConstantEvaluatorError::TypeNotConstructible)?,\n                );\n                self.register_evaluated_expr(expr, span)\n            }\n            TypeInner::Vector { size, scalar } => {\n                let scalar_ty = self.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Scalar(scalar),\n                    },\n                    span,\n                );\n                let el = self.eval_zero_value_impl(scalar_ty, span)?;\n                let expr = Expression::Compose {\n                    ty,\n                    components: vec![el; size as usize],\n                };\n                self.register_evaluated_expr(expr, span)\n            }\n            TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => {\n                let vec_ty = self.types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector { size: rows, scalar },\n                    },\n                    span,\n                );\n                let el = self.eval_zero_value_impl(vec_ty, span)?;\n                let expr = Expression::Compose {\n                    ty,\n                    components: vec![el; columns as usize],\n                };\n                self.register_evaluated_expr(expr, span)\n            }\n            TypeInner::Array {\n                base,\n                size: ArraySize::Constant(size),\n                ..\n            } => {\n                let el = self.eval_zero_value_impl(base, span)?;\n                let expr = Expression::Compose {\n                    ty,\n                    components: vec![el; size.get() as usize],\n                };\n                self.register_evaluated_expr(expr, span)\n            }\n            TypeInner::Struct { ref members, .. } => {\n                let types: Vec<_> = members.iter().map(|m| m.ty).collect();\n                let mut components = Vec::with_capacity(members.len());\n                for ty in types {\n                    components.push(self.eval_zero_value_impl(ty, span)?);\n                }\n                let expr = Expression::Compose { ty, components };\n                self.register_evaluated_expr(expr, span)\n            }\n            _ => Err(ConstantEvaluatorError::TypeNotConstructible),\n        }\n    }\n\n    /// Convert the scalar components of `expr` to `target`.\n    ///\n    /// Treat `span` as the location of the resulting expression.\n    pub fn cast(\n        &mut self,\n        expr: Handle<Expression>,\n        target: crate::Scalar,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        use crate::Scalar as Sc;\n\n        let expr = self.eval_zero_value(expr, span)?;\n\n        let make_error = || -> Result<_, ConstantEvaluatorError> {\n            let from = format!(\"{:?} {:?}\", expr, self.expressions[expr]);\n\n            #[cfg(feature = \"wgsl-in\")]\n            let to = target.to_wgsl_for_diagnostics();\n\n            #[cfg(not(feature = \"wgsl-in\"))]\n            let to = format!(\"{target:?}\");\n\n            Err(ConstantEvaluatorError::InvalidCastArg { from, to })\n        };\n\n        use crate::proc::type_methods::IntFloatLimits;\n\n        let expr = match self.expressions[expr] {\n            Expression::Literal(literal) => {\n                let literal = match target {\n                    Sc::I32 => Literal::I32(match literal {\n                        Literal::I32(v) => v,\n                        Literal::U32(v) => v as i32,\n                        Literal::F32(v) => v.clamp(i32::min_float(), i32::max_float()) as i32,\n                        Literal::F16(v) => f16::to_i32(&v).unwrap(), //Only None on NaN or Inf\n                        Literal::Bool(v) => v as i32,\n                        Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {\n                            return make_error();\n                        }\n                        Literal::AbstractInt(v) => i32::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => i32::try_from_abstract(v)?,\n                    }),\n                    Sc::U32 => Literal::U32(match literal {\n                        Literal::I32(v) => v as u32,\n                        Literal::U32(v) => v,\n                        Literal::F32(v) => v.clamp(u32::min_float(), u32::max_float()) as u32,\n                        // max(0) avoids None due to negative, therefore only None on NaN or Inf\n                        Literal::F16(v) => f16::to_u32(&v.max(f16::ZERO)).unwrap(),\n                        Literal::Bool(v) => v as u32,\n                        Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {\n                            return make_error();\n                        }\n                        Literal::AbstractInt(v) => u32::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => u32::try_from_abstract(v)?,\n                    }),\n                    Sc::I64 => Literal::I64(match literal {\n                        Literal::I32(v) => v as i64,\n                        Literal::U32(v) => v as i64,\n                        Literal::F32(v) => v.clamp(i64::min_float(), i64::max_float()) as i64,\n                        Literal::Bool(v) => v as i64,\n                        Literal::F64(v) => v.clamp(i64::min_float(), i64::max_float()) as i64,\n                        Literal::I64(v) => v,\n                        Literal::U64(v) => v as i64,\n                        Literal::F16(v) => f16::to_i64(&v).unwrap(), //Only None on NaN or Inf\n                        Literal::AbstractInt(v) => i64::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => i64::try_from_abstract(v)?,\n                    }),\n                    Sc::U64 => Literal::U64(match literal {\n                        Literal::I32(v) => v as u64,\n                        Literal::U32(v) => v as u64,\n                        Literal::F32(v) => v.clamp(u64::min_float(), u64::max_float()) as u64,\n                        Literal::Bool(v) => v as u64,\n                        Literal::F64(v) => v.clamp(u64::min_float(), u64::max_float()) as u64,\n                        Literal::I64(v) => v as u64,\n                        Literal::U64(v) => v,\n                        // max(0) avoids None due to negative, therefore only None on NaN or Inf\n                        Literal::F16(v) => f16::to_u64(&v.max(f16::ZERO)).unwrap(),\n                        Literal::AbstractInt(v) => u64::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => u64::try_from_abstract(v)?,\n                    }),\n                    Sc::F16 => Literal::F16(match literal {\n                        Literal::F16(v) => v,\n                        Literal::F32(v) => f16::from_f32(v),\n                        Literal::F64(v) => f16::from_f64(v),\n                        Literal::Bool(v) => f16::from_u32(v as u32).unwrap(),\n                        Literal::I64(v) => f16::from_i64(v).unwrap(),\n                        Literal::U64(v) => f16::from_u64(v).unwrap(),\n                        Literal::I32(v) => f16::from_i32(v).unwrap(),\n                        Literal::U32(v) => f16::from_u32(v).unwrap(),\n                        Literal::AbstractFloat(v) => f16::try_from_abstract(v)?,\n                        Literal::AbstractInt(v) => f16::try_from_abstract(v)?,\n                    }),\n                    Sc::F32 => Literal::F32(match literal {\n                        Literal::I32(v) => v as f32,\n                        Literal::U32(v) => v as f32,\n                        Literal::F32(v) => v,\n                        Literal::Bool(v) => v as u32 as f32,\n                        Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {\n                            return make_error();\n                        }\n                        Literal::F16(v) => f16::to_f32(v),\n                        Literal::AbstractInt(v) => f32::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => f32::try_from_abstract(v)?,\n                    }),\n                    Sc::F64 => Literal::F64(match literal {\n                        Literal::I32(v) => v as f64,\n                        Literal::U32(v) => v as f64,\n                        Literal::F16(v) => f16::to_f64(v),\n                        Literal::F32(v) => v as f64,\n                        Literal::F64(v) => v,\n                        Literal::Bool(v) => v as u32 as f64,\n                        Literal::I64(_) | Literal::U64(_) => return make_error(),\n                        Literal::AbstractInt(v) => f64::try_from_abstract(v)?,\n                        Literal::AbstractFloat(v) => f64::try_from_abstract(v)?,\n                    }),\n                    Sc::BOOL => Literal::Bool(match literal {\n                        Literal::I32(v) => v != 0,\n                        Literal::U32(v) => v != 0,\n                        Literal::F32(v) => v != 0.0,\n                        Literal::F16(v) => v != f16::zero(),\n                        Literal::Bool(v) => v,\n                        Literal::AbstractInt(v) => v != 0,\n                        Literal::AbstractFloat(v) => v != 0.0,\n                        Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {\n                            return make_error();\n                        }\n                    }),\n                    Sc::ABSTRACT_FLOAT => Literal::AbstractFloat(match literal {\n                        Literal::AbstractInt(v) => {\n                            // Overflow is forbidden, but inexact conversions\n                            // are fine. The range of f64 is far larger than\n                            // that of i64, so we don't have to check anything\n                            // here.\n                            v as f64\n                        }\n                        Literal::AbstractFloat(v) => v,\n                        _ => return make_error(),\n                    }),\n                    Sc::ABSTRACT_INT => Literal::AbstractInt(match literal {\n                        Literal::AbstractInt(v) => v,\n                        _ => return make_error(),\n                    }),\n                    _ => {\n                        log::debug!(\"Constant evaluator refused to convert value to {target:?}\");\n                        return make_error();\n                    }\n                };\n                Expression::Literal(literal)\n            }\n            Expression::Compose {\n                ty,\n                components: ref src_components,\n            } => {\n                let ty_inner = match self.types[ty].inner {\n                    TypeInner::Vector { size, .. } => TypeInner::Vector {\n                        size,\n                        scalar: target,\n                    },\n                    TypeInner::Matrix { columns, rows, .. } => TypeInner::Matrix {\n                        columns,\n                        rows,\n                        scalar: target,\n                    },\n                    _ => return make_error(),\n                };\n\n                let mut components = src_components.clone();\n                for component in &mut components {\n                    *component = self.cast(*component, target, span)?;\n                }\n\n                let ty = self.types.insert(\n                    Type {\n                        name: None,\n                        inner: ty_inner,\n                    },\n                    span,\n                );\n\n                Expression::Compose { ty, components }\n            }\n            Expression::Splat { size, value } => {\n                let value_span = self.expressions.get_span(value);\n                let cast_value = self.cast(value, target, value_span)?;\n                Expression::Splat {\n                    size,\n                    value: cast_value,\n                }\n            }\n            _ => return make_error(),\n        };\n\n        self.register_evaluated_expr(expr, span)\n    }\n\n    /// Convert the scalar leaves of  `expr` to `target`, handling arrays.\n    ///\n    /// `expr` must be a `Compose` expression whose type is a scalar, vector,\n    /// matrix, or nested arrays of such.\n    ///\n    /// This is basically the same as the [`cast`] method, except that that\n    /// should only handle Naga [`As`] expressions, which cannot convert arrays.\n    ///\n    /// Treat `span` as the location of the resulting expression.\n    ///\n    /// [`cast`]: ConstantEvaluator::cast\n    /// [`As`]: crate::Expression::As\n    pub fn cast_array(\n        &mut self,\n        expr: Handle<Expression>,\n        target: crate::Scalar,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let expr = self.check_and_get(expr)?;\n\n        let Expression::Compose { ty, ref components } = self.expressions[expr] else {\n            return self.cast(expr, target, span);\n        };\n\n        let TypeInner::Array {\n            base: _,\n            size,\n            stride: _,\n        } = self.types[ty].inner\n        else {\n            return self.cast(expr, target, span);\n        };\n\n        let mut components = components.clone();\n        for component in &mut components {\n            *component = self.cast_array(*component, target, span)?;\n        }\n\n        let first = components.first().unwrap();\n        let new_base = match self.resolve_type(*first)? {\n            crate::proc::TypeResolution::Handle(ty) => ty,\n            crate::proc::TypeResolution::Value(inner) => {\n                self.types.insert(Type { name: None, inner }, span)\n            }\n        };\n        let mut layouter = core::mem::take(self.layouter);\n        layouter.update(self.to_ctx()).unwrap();\n        *self.layouter = layouter;\n\n        let new_base_stride = self.layouter[new_base].to_stride();\n        let new_array_ty = self.types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Array {\n                    base: new_base,\n                    size,\n                    stride: new_base_stride,\n                },\n            },\n            span,\n        );\n\n        let compose = Expression::Compose {\n            ty: new_array_ty,\n            components,\n        };\n        self.register_evaluated_expr(compose, span)\n    }\n\n    fn unary_op(\n        &mut self,\n        op: UnaryOperator,\n        expr: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let expr = self.eval_zero_value_and_splat(expr, span)?;\n\n        let expr = match self.expressions[expr] {\n            Expression::Literal(value) => Expression::Literal(match op {\n                UnaryOperator::Negate => match value {\n                    Literal::I32(v) => Literal::I32(v.wrapping_neg()),\n                    Literal::I64(v) => Literal::I64(v.wrapping_neg()),\n                    Literal::F32(v) => Literal::F32(-v),\n                    Literal::F16(v) => Literal::F16(-v),\n                    Literal::F64(v) => Literal::F64(-v),\n                    Literal::AbstractInt(v) => Literal::AbstractInt(v.wrapping_neg()),\n                    Literal::AbstractFloat(v) => Literal::AbstractFloat(-v),\n                    _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),\n                },\n                UnaryOperator::LogicalNot => match value {\n                    Literal::Bool(v) => Literal::Bool(!v),\n                    _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),\n                },\n                UnaryOperator::BitwiseNot => match value {\n                    Literal::I32(v) => Literal::I32(!v),\n                    Literal::I64(v) => Literal::I64(!v),\n                    Literal::U32(v) => Literal::U32(!v),\n                    Literal::U64(v) => Literal::U64(!v),\n                    Literal::AbstractInt(v) => Literal::AbstractInt(!v),\n                    _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),\n                },\n            }),\n            Expression::Compose {\n                ty,\n                components: ref src_components,\n            } => {\n                match self.types[ty].inner {\n                    TypeInner::Vector { .. } | TypeInner::Matrix { .. } => (),\n                    _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),\n                }\n\n                let mut components = src_components.clone();\n                for component in &mut components {\n                    *component = self.unary_op(op, *component, span)?;\n                }\n\n                Expression::Compose { ty, components }\n            }\n            _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),\n        };\n\n        self.register_evaluated_expr(expr, span)\n    }\n\n    fn binary_op(\n        &mut self,\n        op: BinaryOperator,\n        left: Handle<Expression>,\n        right: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let left = self.eval_zero_value_and_splat(left, span)?;\n        let right = self.eval_zero_value_and_splat(right, span)?;\n\n        // Note: in most cases constant evaluation checks for overflow, but for\n        // i32/u32, it uses wrapping arithmetic. See\n        // <https://gpuweb.github.io/gpuweb/wgsl/#integer-types>.\n\n        let expr = match (&self.expressions[left], &self.expressions[right]) {\n            (&Expression::Literal(left_value), &Expression::Literal(right_value)) => {\n                if !matches!(op, BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight)\n                    && core::mem::discriminant(&left_value) != core::mem::discriminant(&right_value)\n                {\n                    return Err(ConstantEvaluatorError::InvalidBinaryOpArgs);\n                }\n\n                let literal = match op {\n                    BinaryOperator::Equal => Literal::Bool(left_value == right_value),\n                    BinaryOperator::NotEqual => Literal::Bool(left_value != right_value),\n                    BinaryOperator::Less => Literal::Bool(left_value < right_value),\n                    BinaryOperator::LessEqual => Literal::Bool(left_value <= right_value),\n                    BinaryOperator::Greater => Literal::Bool(left_value > right_value),\n                    BinaryOperator::GreaterEqual => Literal::Bool(left_value >= right_value),\n\n                    _ => match (left_value, right_value) {\n                        (Literal::I32(a), Literal::I32(b)) => Literal::I32(match op {\n                            BinaryOperator::Add => a.wrapping_add(b),\n                            BinaryOperator::Subtract => a.wrapping_sub(b),\n                            BinaryOperator::Multiply => a.wrapping_mul(b),\n                            BinaryOperator::Divide => {\n                                if b == 0 {\n                                    return Err(ConstantEvaluatorError::DivisionByZero);\n                                } else {\n                                    a.wrapping_div(b)\n                                }\n                            }\n                            BinaryOperator::Modulo => {\n                                if b == 0 {\n                                    return Err(ConstantEvaluatorError::RemainderByZero);\n                                } else {\n                                    a.wrapping_rem(b)\n                                }\n                            }\n                            BinaryOperator::And => a & b,\n                            BinaryOperator::ExclusiveOr => a ^ b,\n                            BinaryOperator::InclusiveOr => a | b,\n                            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                        }),\n                        (Literal::I32(a), Literal::U32(b)) => Literal::I32(match op {\n                            BinaryOperator::ShiftLeft => {\n                                if (if a.is_negative() { !a } else { a }).leading_zeros() <= b {\n                                    return Err(ConstantEvaluatorError::Overflow(\"<<\".to_string()));\n                                }\n                                a.checked_shl(b)\n                                    .ok_or(ConstantEvaluatorError::ShiftedMoreThan32Bits)?\n                            }\n                            BinaryOperator::ShiftRight => a\n                                .checked_shr(b)\n                                .ok_or(ConstantEvaluatorError::ShiftedMoreThan32Bits)?,\n                            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                        }),\n                        (Literal::U32(a), Literal::U32(b)) => Literal::U32(match op {\n                            BinaryOperator::Add => a.wrapping_add(b),\n                            BinaryOperator::Subtract => a.wrapping_sub(b),\n                            BinaryOperator::Multiply => a.wrapping_mul(b),\n                            BinaryOperator::Divide => a\n                                .checked_div(b)\n                                .ok_or(ConstantEvaluatorError::DivisionByZero)?,\n                            BinaryOperator::Modulo => a\n                                .checked_rem(b)\n                                .ok_or(ConstantEvaluatorError::RemainderByZero)?,\n                            BinaryOperator::And => a & b,\n                            BinaryOperator::ExclusiveOr => a ^ b,\n                            BinaryOperator::InclusiveOr => a | b,\n                            BinaryOperator::ShiftLeft => a\n                                .checked_mul(\n                                    1u32.checked_shl(b)\n                                        .ok_or(ConstantEvaluatorError::ShiftedMoreThan32Bits)?,\n                                )\n                                .ok_or(ConstantEvaluatorError::Overflow(\"<<\".to_string()))?,\n                            BinaryOperator::ShiftRight => a\n                                .checked_shr(b)\n                                .ok_or(ConstantEvaluatorError::ShiftedMoreThan32Bits)?,\n                            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                        }),\n                        (Literal::F32(a), Literal::F32(b)) => Literal::F32(match op {\n                            BinaryOperator::Add => a + b,\n                            BinaryOperator::Subtract => a - b,\n                            BinaryOperator::Multiply => a * b,\n                            BinaryOperator::Divide => a / b,\n                            BinaryOperator::Modulo => a % b,\n                            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                        }),\n                        (Literal::AbstractInt(a), Literal::U32(b)) => {\n                            Literal::AbstractInt(match op {\n                                BinaryOperator::ShiftLeft => {\n                                    if (if a.is_negative() { !a } else { a }).leading_zeros() <= b {\n                                        return Err(ConstantEvaluatorError::Overflow(\n                                            \"<<\".to_string(),\n                                        ));\n                                    }\n                                    a.checked_shl(b).unwrap_or(0)\n                                }\n                                BinaryOperator::ShiftRight => a.checked_shr(b).unwrap_or(0),\n                                _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                            })\n                        }\n                        (Literal::F16(a), Literal::F16(b)) => {\n                            let result = match op {\n                                BinaryOperator::Add => a + b,\n                                BinaryOperator::Subtract => a - b,\n                                BinaryOperator::Multiply => a * b,\n                                BinaryOperator::Divide => a / b,\n                                BinaryOperator::Modulo => a % b,\n                                _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                            };\n                            if !result.is_finite() {\n                                return Err(ConstantEvaluatorError::Overflow(format!(\"{op:?}\")));\n                            }\n                            Literal::F16(result)\n                        }\n                        (Literal::AbstractInt(a), Literal::AbstractInt(b)) => {\n                            Literal::AbstractInt(match op {\n                                BinaryOperator::Add => a.checked_add(b).ok_or_else(|| {\n                                    ConstantEvaluatorError::Overflow(\"addition\".into())\n                                })?,\n                                BinaryOperator::Subtract => a.checked_sub(b).ok_or_else(|| {\n                                    ConstantEvaluatorError::Overflow(\"subtraction\".into())\n                                })?,\n                                BinaryOperator::Multiply => a.checked_mul(b).ok_or_else(|| {\n                                    ConstantEvaluatorError::Overflow(\"multiplication\".into())\n                                })?,\n                                BinaryOperator::Divide => a.checked_div(b).ok_or_else(|| {\n                                    if b == 0 {\n                                        ConstantEvaluatorError::DivisionByZero\n                                    } else {\n                                        ConstantEvaluatorError::Overflow(\"division\".into())\n                                    }\n                                })?,\n                                BinaryOperator::Modulo => a.checked_rem(b).ok_or_else(|| {\n                                    if b == 0 {\n                                        ConstantEvaluatorError::RemainderByZero\n                                    } else {\n                                        ConstantEvaluatorError::Overflow(\"remainder\".into())\n                                    }\n                                })?,\n                                BinaryOperator::And => a & b,\n                                BinaryOperator::ExclusiveOr => a ^ b,\n                                BinaryOperator::InclusiveOr => a | b,\n                                _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                            })\n                        }\n                        (Literal::AbstractFloat(a), Literal::AbstractFloat(b)) => {\n                            let result = match op {\n                                BinaryOperator::Add => a + b,\n                                BinaryOperator::Subtract => a - b,\n                                BinaryOperator::Multiply => a * b,\n                                BinaryOperator::Divide => a / b,\n                                BinaryOperator::Modulo => a % b,\n                                _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                            };\n                            if !result.is_finite() {\n                                return Err(ConstantEvaluatorError::Overflow(format!(\"{op:?}\")));\n                            }\n                            Literal::AbstractFloat(result)\n                        }\n                        (Literal::Bool(a), Literal::Bool(b)) => Literal::Bool(match op {\n                            BinaryOperator::LogicalAnd => a && b,\n                            BinaryOperator::LogicalOr => a || b,\n                            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                        }),\n                        _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n                    },\n                };\n                Expression::Literal(literal)\n            }\n            (\n                &Expression::Compose {\n                    components: ref src_components,\n                    ty,\n                },\n                &Expression::Literal(_),\n            ) => {\n                if !is_allowed_compose_literal_op(&self.types[ty].inner, op) {\n                    return Err(ConstantEvaluatorError::InvalidBinaryOpArgs);\n                }\n                let mut components = src_components.clone();\n                for component in &mut components {\n                    *component = self.binary_op(op, *component, right, span)?;\n                }\n                Expression::Compose { ty, components }\n            }\n            (\n                &Expression::Literal(_),\n                &Expression::Compose {\n                    components: ref src_components,\n                    ty,\n                },\n            ) => {\n                if !is_allowed_compose_literal_op(&self.types[ty].inner, op) {\n                    return Err(ConstantEvaluatorError::InvalidBinaryOpArgs);\n                }\n                let mut components = src_components.clone();\n                for component in &mut components {\n                    *component = self.binary_op(op, left, *component, span)?;\n                }\n                Expression::Compose { ty, components }\n            }\n            (\n                &Expression::Compose {\n                    components: ref left_components,\n                    ty: left_ty,\n                },\n                &Expression::Compose {\n                    components: ref right_components,\n                    ty: right_ty,\n                },\n            ) => {\n                // We have to make a copy of the component lists, because the\n                // call to `binary_op_vector` needs `&mut self`, but `self` owns\n                // the component lists.\n                let left_flattened = crate::proc::flatten_compose(\n                    left_ty,\n                    left_components,\n                    self.expressions,\n                    self.types,\n                )\n                .collect::<Vec<_>>();\n                let right_flattened = crate::proc::flatten_compose(\n                    right_ty,\n                    right_components,\n                    self.expressions,\n                    self.types,\n                )\n                .collect::<Vec<_>>();\n\n                self.binary_op_compose(\n                    op,\n                    &left_flattened,\n                    &right_flattened,\n                    left_ty,\n                    right_ty,\n                    span,\n                )?\n            }\n            _ => return Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n        };\n\n        return self.register_evaluated_expr(expr, span);\n\n        fn is_allowed_compose_literal_op(compose_ty: &TypeInner, op: BinaryOperator) -> bool {\n            let is_numeric_vec = matches!(\n                compose_ty, TypeInner::Vector { scalar, .. }\n                if scalar.kind != ScalarKind::Bool\n            );\n            let is_allowed_vec_scalar_op = matches!(\n                op,\n                BinaryOperator::Add\n                    | BinaryOperator::Subtract\n                    | BinaryOperator::Multiply\n                    | BinaryOperator::Divide\n                    | BinaryOperator::Modulo\n            );\n            let is_mat = matches!(compose_ty, TypeInner::Matrix { .. });\n            let is_allowed_mat_scalar_op = matches!(op, BinaryOperator::Multiply);\n            is_numeric_vec && is_allowed_vec_scalar_op || is_mat && is_allowed_mat_scalar_op\n        }\n    }\n\n    fn binary_op_compose(\n        &mut self,\n        op: BinaryOperator,\n        left_components: &[Handle<Expression>],\n        right_components: &[Handle<Expression>],\n        left_ty: Handle<Type>,\n        right_ty: Handle<Type>,\n        span: Span,\n    ) -> Result<Expression, ConstantEvaluatorError> {\n        match (&self.types[left_ty].inner, &self.types[right_ty].inner) {\n            // Binary operation on vector-vector\n            (\n                &TypeInner::Vector {\n                    size: left_size, ..\n                },\n                &TypeInner::Vector {\n                    size: right_size, ..\n                },\n            ) if left_size == right_size => self.binary_op_vector(\n                op,\n                left_size,\n                left_components,\n                right_components,\n                left_ty,\n                span,\n            ),\n            // Binary operation on vector-matrix\n            (\n                &TypeInner::Vector { size, .. },\n                &TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar,\n                },\n            ) if op == BinaryOperator::Multiply && size == rows => self.multiply_vector_matrix(\n                left_components,\n                right_components,\n                columns,\n                scalar,\n                span,\n            ),\n            // Binary operation on matrix-vector\n            (\n                &TypeInner::Matrix {\n                    columns,\n                    rows,\n                    scalar,\n                },\n                &TypeInner::Vector { size, .. },\n            ) if op == BinaryOperator::Multiply && size == columns => {\n                self.multiply_matrix_vector(left_components, right_components, rows, scalar, span)\n            }\n            // Binary operation on matrix-matrix\n            (\n                &TypeInner::Matrix {\n                    columns: left_columns,\n                    rows: left_rows,\n                    scalar,\n                },\n                &TypeInner::Matrix {\n                    columns: right_columns,\n                    rows: right_rows,\n                    ..\n                },\n            ) => match op {\n                BinaryOperator::Add | BinaryOperator::Subtract\n                    if left_columns == right_columns && left_rows == right_rows =>\n                {\n                    let components = left_components\n                        .iter()\n                        .zip(right_components)\n                        .map(|(&left, &right)| self.binary_op(op, left, right, span))\n                        .collect::<Result<Vec<_>, _>>()?;\n                    Ok(Expression::Compose {\n                        ty: left_ty,\n                        components,\n                    })\n                }\n                BinaryOperator::Multiply if left_columns == right_rows => self\n                    .multiply_matrix_matrix(\n                        left_components,\n                        right_components,\n                        left_rows,\n                        right_columns,\n                        scalar,\n                        span,\n                    ),\n                _ => Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n            },\n            _ => Err(ConstantEvaluatorError::InvalidBinaryOpArgs),\n        }\n    }\n\n    fn binary_op_vector(\n        &mut self,\n        op: BinaryOperator,\n        size: crate::VectorSize,\n        left_components: &[Handle<Expression>],\n        right_components: &[Handle<Expression>],\n        left_ty: Handle<Type>,\n        span: Span,\n    ) -> Result<Expression, ConstantEvaluatorError> {\n        let ty = match op {\n            // Relational operators produce vectors of booleans.\n            BinaryOperator::Equal\n            | BinaryOperator::NotEqual\n            | BinaryOperator::Less\n            | BinaryOperator::LessEqual\n            | BinaryOperator::Greater\n            | BinaryOperator::GreaterEqual => self.types.insert(\n                Type {\n                    name: None,\n                    inner: TypeInner::Vector {\n                        size,\n                        scalar: crate::Scalar::BOOL,\n                    },\n                },\n                span,\n            ),\n\n            // Other operators produce the same type as their left\n            // operand.\n            BinaryOperator::Add\n            | BinaryOperator::Subtract\n            | BinaryOperator::Multiply\n            | BinaryOperator::Divide\n            | BinaryOperator::Modulo\n            | BinaryOperator::And\n            | BinaryOperator::ExclusiveOr\n            | BinaryOperator::InclusiveOr\n            | BinaryOperator::ShiftLeft\n            | BinaryOperator::ShiftRight => left_ty,\n\n            BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr => {\n                // Not supported on vectors\n                return Err(ConstantEvaluatorError::InvalidBinaryOpArgs);\n            }\n        };\n\n        let components = left_components\n            .iter()\n            .zip(right_components)\n            .map(|(&left, &right)| self.binary_op(op, left, right, span))\n            .collect::<Result<Vec<_>, _>>()?;\n\n        Ok(Expression::Compose { ty, components })\n    }\n\n    fn multiply_vector_matrix(\n        &mut self,\n        vec_components: &[Handle<Expression>],\n        mat_components: &[Handle<Expression>],\n        mat_columns: crate::VectorSize,\n        scalar: crate::Scalar,\n        span: Span,\n    ) -> Result<Expression, ConstantEvaluatorError> {\n        let ty = self.types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: mat_columns,\n                    scalar,\n                },\n            },\n            span,\n        );\n        let components = mat_components\n            .iter()\n            .map(|&column| {\n                let Expression::Compose { ref components, .. } = self.expressions[column] else {\n                    unreachable!()\n                };\n                self.dot_exprs(\n                    vec_components.iter().cloned(),\n                    components.clone().into_iter(),\n                    span,\n                )\n            })\n            .collect::<Result<Vec<_>, _>>()?;\n        Ok(Expression::Compose { ty, components })\n    }\n\n    fn multiply_matrix_vector(\n        &mut self,\n        mat_components: &[Handle<Expression>],\n        vec_components: &[Handle<Expression>],\n        mat_rows: crate::VectorSize,\n        scalar: crate::Scalar,\n        span: Span,\n    ) -> Result<Expression, ConstantEvaluatorError> {\n        let ty = self.types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: mat_rows,\n                    scalar,\n                },\n            },\n            span,\n        );\n\n        let flatten = self.flatten_matrix(mat_components);\n        let nr = mat_rows as usize;\n        let components = (0..nr)\n            .map(|r| {\n                let row = flatten.iter().skip(r).step_by(nr).cloned();\n                self.dot_exprs(row, vec_components.iter().cloned(), span)\n            })\n            .collect::<Result<Vec<_>, _>>()?;\n        Ok(Expression::Compose { ty, components })\n    }\n\n    fn multiply_matrix_matrix(\n        &mut self,\n        left_components: &[Handle<Expression>],\n        right_components: &[Handle<Expression>],\n        left_rows: crate::VectorSize,\n        right_columns: crate::VectorSize,\n        scalar: crate::Scalar,\n        span: Span,\n    ) -> Result<Expression, ConstantEvaluatorError> {\n        let left_nc = left_components.len();\n        let left_nr = left_rows as usize;\n        let right_nc = right_columns as usize;\n        let right_nr = left_nc;\n\n        let mut result = Vec::with_capacity(right_nc);\n        let result_ty = self.types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Matrix {\n                    columns: right_columns,\n                    rows: left_rows,\n                    scalar,\n                },\n            },\n            span,\n        );\n        let result_column_ty = self.types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: left_rows,\n                    scalar,\n                },\n            },\n            span,\n        );\n\n        let left_flattened = self.flatten_matrix(left_components);\n        let right_flattened = self.flatten_matrix(right_components);\n        for c in 0..right_nc {\n            let result_column = (0..left_nr)\n                .map(|r| {\n                    let row = left_flattened.iter().skip(r).step_by(left_nr);\n                    let column = right_flattened.iter().skip(c * right_nr).take(right_nr);\n                    self.dot_exprs(row.cloned(), column.cloned(), span)\n                })\n                .collect::<Result<Vec<_>, _>>()?;\n            let expr = Expression::Compose {\n                ty: result_column_ty,\n                components: result_column,\n            };\n            let handle = self.register_evaluated_expr(expr, span)?;\n            result.push(handle);\n        }\n        Ok(Expression::Compose {\n            ty: result_ty,\n            components: result,\n        })\n    }\n\n    fn flatten_matrix(&self, columns: &[Handle<Expression>]) -> ArrayVec<Handle<Expression>, 16> {\n        let mut flattened = ArrayVec::<_, 16>::new();\n        for &column in columns {\n            let Expression::Compose { ref components, .. } = self.expressions[column] else {\n                unreachable!()\n            };\n            flattened.extend(components.iter().cloned());\n        }\n        flattened\n    }\n\n    fn dot_exprs(\n        &mut self,\n        left: impl Iterator<Item = Handle<Expression>>,\n        right: impl Iterator<Item = Handle<Expression>>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let mut acc = None;\n        for (l, r) in left.zip(right) {\n            let result = self.binary_op(BinaryOperator::Multiply, l, r, span)?;\n            match acc.as_mut() {\n                Some(acc) => *acc = self.binary_op(BinaryOperator::Add, *acc, result, span)?,\n                None => acc = Some(result),\n            }\n        }\n        Ok(acc.unwrap())\n    }\n\n    fn relational(\n        &mut self,\n        fun: RelationalFunction,\n        arg: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let arg = self.eval_zero_value_and_splat(arg, span)?;\n        match fun {\n            RelationalFunction::All | RelationalFunction::Any => match self.expressions[arg] {\n                Expression::Literal(Literal::Bool(_)) => Ok(arg),\n                Expression::Compose { ty, ref components }\n                    if matches!(self.types[ty].inner, TypeInner::Vector { .. }) =>\n                {\n                    let mut bool_components = ArrayVec::<bool, { crate::VectorSize::MAX }>::new();\n                    for component in\n                        crate::proc::flatten_compose(ty, components, self.expressions, self.types)\n                    {\n                        match self.expressions[component] {\n                            Expression::Literal(Literal::Bool(val)) => {\n                                bool_components.push(val);\n                            }\n                            _ => {\n                                return Err(ConstantEvaluatorError::InvalidRelationalArg(fun));\n                            }\n                        }\n                    }\n                    let components = bool_components;\n                    let result = match fun {\n                        RelationalFunction::All => components.iter().all(|c| *c),\n                        RelationalFunction::Any => components.iter().any(|c| *c),\n                        _ => unreachable!(),\n                    };\n                    self.register_evaluated_expr(Expression::Literal(Literal::Bool(result)), span)\n                }\n                _ => Err(ConstantEvaluatorError::InvalidRelationalArg(fun)),\n            },\n            _ => Err(ConstantEvaluatorError::NotImplemented(format!(\n                \"{fun:?} built-in function\"\n            ))),\n        }\n    }\n\n    /// Deep copy `expr` from `expressions` into `self.expressions`.\n    ///\n    /// Return the root of the new copy.\n    ///\n    /// This is used when we're evaluating expressions in a function's\n    /// expression arena that refer to a constant: we need to copy the\n    /// constant's value into the function's arena so we can operate on it.\n    fn copy_from(\n        &mut self,\n        expr: Handle<Expression>,\n        expressions: &Arena<Expression>,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let span = expressions.get_span(expr);\n        match expressions[expr] {\n            ref expr @ (Expression::Literal(_)\n            | Expression::Constant(_)\n            | Expression::ZeroValue(_)) => self.register_evaluated_expr(expr.clone(), span),\n            Expression::Compose { ty, ref components } => {\n                let mut components = components.clone();\n                for component in &mut components {\n                    *component = self.copy_from(*component, expressions)?;\n                }\n                self.register_evaluated_expr(Expression::Compose { ty, components }, span)\n            }\n            Expression::Splat { size, value } => {\n                let value = self.copy_from(value, expressions)?;\n                self.register_evaluated_expr(Expression::Splat { size, value }, span)\n            }\n            _ => {\n                log::debug!(\"copy_from: SubexpressionsAreNotConstant\");\n                Err(ConstantEvaluatorError::SubexpressionsAreNotConstant)\n            }\n        }\n    }\n\n    /// Returns the total number of components, after flattening, of a vector compose expression.\n    fn vector_compose_flattened_size(\n        &self,\n        components: &[Handle<Expression>],\n    ) -> Result<usize, ConstantEvaluatorError> {\n        components\n            .iter()\n            .try_fold(0, |acc, c| -> Result<_, ConstantEvaluatorError> {\n                let size = match *self.resolve_type(*c)?.inner_with(self.types) {\n                    TypeInner::Scalar(_) => 1,\n                    // We trust that the vector size of `component` is correct,\n                    // as it will have already been validated when `component`\n                    // was registered.\n                    TypeInner::Vector { size, .. } => size as usize,\n                    _ => return Err(ConstantEvaluatorError::InvalidVectorComposeComponent),\n                };\n                Ok(acc + size)\n            })\n    }\n\n    fn register_evaluated_expr(\n        &mut self,\n        expr: Expression,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        // It suffices to only check_literal_value() for `Literal` expressions,\n        // since we only register one expression at a time, `Compose`\n        // expressions can only refer to other expressions, and `ZeroValue`\n        // expressions are always okay.\n        if let Expression::Literal(literal) = expr {\n            crate::valid::check_literal_value(literal)?;\n        }\n\n        // Ensure vector composes contain the correct number of components. We\n        // do so here when each compose is registered to avoid having to deal\n        // with the mess each time the compose is used in another expression.\n        if let Expression::Compose { ty, ref components } = expr {\n            if let TypeInner::Vector { size, scalar: _ } = self.types[ty].inner {\n                let expected = size as usize;\n                let actual = self.vector_compose_flattened_size(components)?;\n                if expected != actual {\n                    return Err(ConstantEvaluatorError::InvalidVectorComposeLength {\n                        expected,\n                        actual,\n                    });\n                }\n            }\n        }\n\n        Ok(self.append_expr(expr, span, ExpressionKind::Const))\n    }\n\n    fn append_expr(\n        &mut self,\n        expr: Expression,\n        span: Span,\n        expr_type: ExpressionKind,\n    ) -> Handle<Expression> {\n        let h = match self.behavior {\n            Behavior::Wgsl(\n                WgslRestrictions::Runtime(ref mut function_local_data)\n                | WgslRestrictions::Const(Some(ref mut function_local_data)),\n            )\n            | Behavior::Glsl(GlslRestrictions::Runtime(ref mut function_local_data)) => {\n                let is_running = function_local_data.emitter.is_running();\n                let needs_pre_emit = expr.needs_pre_emit();\n                if is_running && needs_pre_emit {\n                    function_local_data\n                        .block\n                        .extend(function_local_data.emitter.finish(self.expressions));\n                    let h = self.expressions.append(expr, span);\n                    function_local_data.emitter.start(self.expressions);\n                    h\n                } else {\n                    self.expressions.append(expr, span)\n                }\n            }\n            _ => self.expressions.append(expr, span),\n        };\n        self.expression_kind_tracker.insert(h, expr_type);\n        h\n    }\n\n    /// Resolve the type of `expr` if it is a constant expression.\n    ///\n    /// If `expr` was evaluated to a constant, returns its type.\n    /// Otherwise, returns an error.\n    fn resolve_type(\n        &self,\n        expr: Handle<Expression>,\n    ) -> Result<crate::proc::TypeResolution, ConstantEvaluatorError> {\n        use crate::proc::TypeResolution as Tr;\n        use crate::Expression as Ex;\n        let resolution = match self.expressions[expr] {\n            Ex::Literal(ref literal) => Tr::Value(literal.ty_inner()),\n            Ex::Constant(c) => Tr::Handle(self.constants[c].ty),\n            Ex::ZeroValue(ty) | Ex::Compose { ty, .. } => Tr::Handle(ty),\n            Ex::Splat { size, value } => {\n                let Tr::Value(TypeInner::Scalar(scalar)) = self.resolve_type(value)? else {\n                    return Err(ConstantEvaluatorError::SplatScalarOnly);\n                };\n                Tr::Value(TypeInner::Vector { scalar, size })\n            }\n            _ => {\n                log::debug!(\"resolve_type: SubexpressionsAreNotConstant\");\n                return Err(ConstantEvaluatorError::SubexpressionsAreNotConstant);\n            }\n        };\n\n        Ok(resolution)\n    }\n\n    fn select(\n        &mut self,\n        reject: Handle<Expression>,\n        accept: Handle<Expression>,\n        condition: Handle<Expression>,\n        span: Span,\n    ) -> Result<Handle<Expression>, ConstantEvaluatorError> {\n        let mut arg = |arg| self.eval_zero_value_and_splat(arg, span);\n\n        let reject = arg(reject)?;\n        let accept = arg(accept)?;\n        let condition = arg(condition)?;\n\n        let select_single_component =\n            |this: &mut Self, reject_scalar, reject, accept, condition| {\n                let accept = this.cast(accept, reject_scalar, span)?;\n                if condition {\n                    Ok(accept)\n                } else {\n                    Ok(reject)\n                }\n            };\n\n        match (&self.expressions[reject], &self.expressions[accept]) {\n            (&Expression::Literal(reject_lit), &Expression::Literal(_accept_lit)) => {\n                let reject_scalar = reject_lit.scalar();\n                let &Expression::Literal(Literal::Bool(condition)) = &self.expressions[condition]\n                else {\n                    return Err(ConstantEvaluatorError::SelectScalarConditionNotABool);\n                };\n                select_single_component(self, reject_scalar, reject, accept, condition)\n            }\n            (\n                &Expression::Compose {\n                    ty: reject_ty,\n                    components: ref reject_components,\n                },\n                &Expression::Compose {\n                    ty: accept_ty,\n                    components: ref accept_components,\n                },\n            ) => {\n                let ty_deets = |ty| {\n                    let (size, scalar) = self.types[ty].inner.vector_size_and_scalar().unwrap();\n                    (size.unwrap(), scalar)\n                };\n\n                let expected_vec_size = {\n                    let [(reject_vec_size, _), (accept_vec_size, _)] =\n                        [reject_ty, accept_ty].map(ty_deets);\n\n                    if reject_vec_size != accept_vec_size {\n                        return Err(ConstantEvaluatorError::SelectVecRejectAcceptSizeMismatch {\n                            reject: reject_vec_size,\n                            accept: accept_vec_size,\n                        });\n                    }\n                    reject_vec_size\n                };\n\n                let condition_components = match self.expressions[condition] {\n                    Expression::Literal(Literal::Bool(condition)) => {\n                        vec![condition; (expected_vec_size as u8).into()]\n                    }\n                    Expression::Compose {\n                        ty: condition_ty,\n                        components: ref condition_components,\n                    } => {\n                        let (condition_vec_size, condition_scalar) = ty_deets(condition_ty);\n                        if condition_scalar.kind != ScalarKind::Bool {\n                            return Err(ConstantEvaluatorError::SelectConditionNotAVecBool);\n                        }\n                        if condition_vec_size != expected_vec_size {\n                            return Err(ConstantEvaluatorError::SelectConditionVecSizeMismatch);\n                        }\n                        condition_components\n                            .iter()\n                            .copied()\n                            .map(|component| match &self.expressions[component] {\n                                &Expression::Literal(Literal::Bool(condition)) => condition,\n                                _ => unreachable!(),\n                            })\n                            .collect()\n                    }\n\n                    _ => return Err(ConstantEvaluatorError::SelectConditionNotAVecBool),\n                };\n\n                let evaluated = Expression::Compose {\n                    ty: reject_ty,\n                    components: reject_components\n                        .clone()\n                        .into_iter()\n                        .zip(accept_components.clone().into_iter())\n                        .zip(condition_components.into_iter())\n                        .map(|((reject, accept), condition)| {\n                            let reject_scalar = match &self.expressions[reject] {\n                                &Expression::Literal(lit) => lit.scalar(),\n                                _ => unreachable!(),\n                            };\n                            select_single_component(self, reject_scalar, reject, accept, condition)\n                        })\n                        .collect::<Result<_, _>>()?,\n                };\n                self.register_evaluated_expr(evaluated, span)\n            }\n            _ => Err(ConstantEvaluatorError::SelectAcceptRejectTypeMismatch),\n        }\n    }\n}\n\nfn first_trailing_bit(concrete_int: ConcreteInt<1>) -> ConcreteInt<1> {\n    // NOTE: Bit indices for this built-in start at 0 at the \"right\" (or LSB). For example, a value\n    // of 1 means the least significant bit is set. Therefore, an input of `0x[80 00…]` would\n    // return a right-to-left bit index of 0.\n    let trailing_zeros_to_bit_idx = |e: u32| -> u32 {\n        match e {\n            idx @ 0..=31 => idx,\n            32 => u32::MAX,\n            _ => unreachable!(),\n        }\n    };\n    match concrete_int {\n        ConcreteInt::U32([e]) => ConcreteInt::U32([trailing_zeros_to_bit_idx(e.trailing_zeros())]),\n        ConcreteInt::I32([e]) => {\n            ConcreteInt::I32([trailing_zeros_to_bit_idx(e.trailing_zeros()) as i32])\n        }\n    }\n}\n\n#[test]\nfn first_trailing_bit_smoke() {\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([0])),\n        ConcreteInt::I32([-1])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([1])),\n        ConcreteInt::I32([0])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([2])),\n        ConcreteInt::I32([1])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([-1])),\n        ConcreteInt::I32([0]),\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([i32::MIN])),\n        ConcreteInt::I32([31]),\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::I32([i32::MAX])),\n        ConcreteInt::I32([0]),\n    );\n    for idx in 0..32 {\n        assert_eq!(\n            first_trailing_bit(ConcreteInt::I32([1 << idx])),\n            ConcreteInt::I32([idx])\n        )\n    }\n\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::U32([0])),\n        ConcreteInt::U32([u32::MAX])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::U32([1])),\n        ConcreteInt::U32([0])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::U32([2])),\n        ConcreteInt::U32([1])\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::U32([1 << 31])),\n        ConcreteInt::U32([31]),\n    );\n    assert_eq!(\n        first_trailing_bit(ConcreteInt::U32([u32::MAX])),\n        ConcreteInt::U32([0]),\n    );\n    for idx in 0..32 {\n        assert_eq!(\n            first_trailing_bit(ConcreteInt::U32([1 << idx])),\n            ConcreteInt::U32([idx])\n        )\n    }\n}\n\nfn first_leading_bit(concrete_int: ConcreteInt<1>) -> ConcreteInt<1> {\n    // NOTE: Bit indices for this built-in start at 0 at the \"right\" (or LSB). For example, 1 means\n    // the least significant bit is set. Therefore, an input of 1 would return a right-to-left bit\n    // index of 0.\n    let rtl_to_ltr_bit_idx = |e: u32| -> u32 {\n        match e {\n            idx @ 0..=31 => 31 - idx,\n            32 => u32::MAX,\n            _ => unreachable!(),\n        }\n    };\n    match concrete_int {\n        ConcreteInt::I32([e]) => ConcreteInt::I32([{\n            let rtl_bit_index = if e.is_negative() {\n                e.leading_ones()\n            } else {\n                e.leading_zeros()\n            };\n            rtl_to_ltr_bit_idx(rtl_bit_index) as i32\n        }]),\n        ConcreteInt::U32([e]) => ConcreteInt::U32([rtl_to_ltr_bit_idx(e.leading_zeros())]),\n    }\n}\n\n#[test]\nfn first_leading_bit_smoke() {\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([-1])),\n        ConcreteInt::I32([-1])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([0])),\n        ConcreteInt::I32([-1])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([1])),\n        ConcreteInt::I32([0])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([-2])),\n        ConcreteInt::I32([0])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([1234 + 4567])),\n        ConcreteInt::I32([12])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([i32::MAX])),\n        ConcreteInt::I32([30])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::I32([i32::MIN])),\n        ConcreteInt::I32([30])\n    );\n    // NOTE: Ignore the sign bit, which is a separate (above) case.\n    for idx in 0..(32 - 1) {\n        assert_eq!(\n            first_leading_bit(ConcreteInt::I32([1 << idx])),\n            ConcreteInt::I32([idx])\n        );\n    }\n    for idx in 1..(32 - 1) {\n        assert_eq!(\n            first_leading_bit(ConcreteInt::I32([-(1 << idx)])),\n            ConcreteInt::I32([idx - 1])\n        );\n    }\n\n    assert_eq!(\n        first_leading_bit(ConcreteInt::U32([0])),\n        ConcreteInt::U32([u32::MAX])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::U32([1])),\n        ConcreteInt::U32([0])\n    );\n    assert_eq!(\n        first_leading_bit(ConcreteInt::U32([u32::MAX])),\n        ConcreteInt::U32([31])\n    );\n    for idx in 0..32 {\n        assert_eq!(\n            first_leading_bit(ConcreteInt::U32([1 << idx])),\n            ConcreteInt::U32([idx])\n        )\n    }\n}\n\n/// Trait for conversions of abstract values to concrete types.\ntrait TryFromAbstract<T>: Sized {\n    /// Convert an abstract literal `value` to `Self`.\n    ///\n    /// Since Naga's [`AbstractInt`] and [`AbstractFloat`] exist to support\n    /// WGSL, we follow WGSL's conversion rules here:\n    ///\n    /// - WGSL §6.1.2. Conversion Rank says that automatic conversions\n    ///   from [`AbstractInt`] to an integer type are either lossless or an\n    ///   error.\n    ///\n    /// - WGSL §15.7.6 Floating Point Conversion says that conversions\n    ///   to floating point in constant expressions and override\n    ///   expressions are errors if the value is out of range for the\n    ///   destination type, but rounding is okay.\n    ///\n    /// - WGSL §17.1.2 i32()/u32() constructors treat AbstractFloat as any\n    ///   other floating point type, following the scalar floating point to\n    ///   integral conversion algorithm (§15.7.6). There is no automatic\n    ///   conversion from AbstractFloat to integer types.\n    ///\n    /// [`AbstractInt`]: crate::Literal::AbstractInt\n    /// [`AbstractFloat`]: crate::Literal::AbstractFloat\n    fn try_from_abstract(value: T) -> Result<Self, ConstantEvaluatorError>;\n}\n\nimpl TryFromAbstract<i64> for i32 {\n    fn try_from_abstract(value: i64) -> Result<i32, ConstantEvaluatorError> {\n        i32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {\n            value: format!(\"{value:?}\"),\n            to_type: \"i32\",\n        })\n    }\n}\n\nimpl TryFromAbstract<i64> for u32 {\n    fn try_from_abstract(value: i64) -> Result<u32, ConstantEvaluatorError> {\n        u32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {\n            value: format!(\"{value:?}\"),\n            to_type: \"u32\",\n        })\n    }\n}\n\nimpl TryFromAbstract<i64> for u64 {\n    fn try_from_abstract(value: i64) -> Result<u64, ConstantEvaluatorError> {\n        u64::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {\n            value: format!(\"{value:?}\"),\n            to_type: \"u64\",\n        })\n    }\n}\n\nimpl TryFromAbstract<i64> for i64 {\n    fn try_from_abstract(value: i64) -> Result<i64, ConstantEvaluatorError> {\n        Ok(value)\n    }\n}\n\nimpl TryFromAbstract<i64> for f32 {\n    fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {\n        let f = value as f32;\n        // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of\n        // `f32` is roughly ±3.4 × 10³⁸, so there's no opportunity for\n        // overflow here.\n        Ok(f)\n    }\n}\n\nimpl TryFromAbstract<f64> for f32 {\n    fn try_from_abstract(value: f64) -> Result<f32, ConstantEvaluatorError> {\n        let f = value as f32;\n        if f.is_infinite() {\n            return Err(ConstantEvaluatorError::AutomaticConversionLossy {\n                value: format!(\"{value:?}\"),\n                to_type: \"f32\",\n            });\n        }\n        Ok(f)\n    }\n}\n\nimpl TryFromAbstract<i64> for f64 {\n    fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {\n        let f = value as f64;\n        // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of\n        // `f64` is roughly ±1.8 × 10³⁰⁸, so there's no opportunity for\n        // overflow here.\n        Ok(f)\n    }\n}\n\nimpl TryFromAbstract<f64> for f64 {\n    fn try_from_abstract(value: f64) -> Result<f64, ConstantEvaluatorError> {\n        Ok(value)\n    }\n}\n\nimpl TryFromAbstract<f64> for i32 {\n    fn try_from_abstract(value: f64) -> Result<Self, ConstantEvaluatorError> {\n        // https://www.w3.org/TR/WGSL/#floating-point-conversion\n        // To convert a floating point scalar value X to an integer scalar type T:\n        // * If X is a NaN, the result is an indeterminate value in T.\n        // * If X is exactly representable in the target type T, then the\n        //   result is that value.\n        // * Otherwise, the result is the value in T closest to truncate(X) and\n        //   also exactly representable in the original floating point type.\n        //\n        // A rust cast satisfies these requirements apart from \"the result\n        // is... exactly representable in the original floating point type\".\n        // However, i32::MIN and i32::MAX are exactly representable by f64, so\n        // we're all good.\n        Ok(value as i32)\n    }\n}\n\nimpl TryFromAbstract<f64> for u32 {\n    fn try_from_abstract(value: f64) -> Result<Self, ConstantEvaluatorError> {\n        // As above, u32::MIN and u32::MAX are exactly representable by f64,\n        // so a simple rust cast is sufficient.\n        Ok(value as u32)\n    }\n}\n\nimpl TryFromAbstract<f64> for i64 {\n    fn try_from_abstract(value: f64) -> Result<Self, ConstantEvaluatorError> {\n        // As above, except we clamp to the minimum and maximum values\n        // representable by both f64 and i64.\n        use crate::proc::type_methods::IntFloatLimits;\n        Ok(value.clamp(i64::min_float(), i64::max_float()) as i64)\n    }\n}\n\nimpl TryFromAbstract<f64> for u64 {\n    fn try_from_abstract(value: f64) -> Result<Self, ConstantEvaluatorError> {\n        // As above, this time clamping to the minimum and maximum values\n        // representable by both f64 and u64.\n        use crate::proc::type_methods::IntFloatLimits;\n        Ok(value.clamp(u64::min_float(), u64::max_float()) as u64)\n    }\n}\n\nimpl TryFromAbstract<f64> for f16 {\n    fn try_from_abstract(value: f64) -> Result<f16, ConstantEvaluatorError> {\n        let f = f16::from_f64(value);\n        if f.is_infinite() {\n            return Err(ConstantEvaluatorError::AutomaticConversionLossy {\n                value: format!(\"{value:?}\"),\n                to_type: \"f16\",\n            });\n        }\n        Ok(f)\n    }\n}\n\nimpl TryFromAbstract<i64> for f16 {\n    fn try_from_abstract(value: i64) -> Result<f16, ConstantEvaluatorError> {\n        let f = f16::from_i64(value);\n        if f.is_none() {\n            return Err(ConstantEvaluatorError::AutomaticConversionLossy {\n                value: format!(\"{value:?}\"),\n                to_type: \"f16\",\n            });\n        }\n        Ok(f.unwrap())\n    }\n}\n\nfn cross_product<T>(a: [T; 3], b: [T; 3]) -> [T; 3]\nwhere\n    T: Copy,\n    T: core::ops::Mul<T, Output = T>,\n    T: core::ops::Sub<T, Output = T>,\n{\n    [\n        a[1] * b[2] - a[2] * b[1],\n        a[2] * b[0] - a[0] * b[2],\n        a[0] * b[1] - a[1] * b[0],\n    ]\n}\n\n#[cfg(test)]\nmod tests {\n    use alloc::{vec, vec::Vec};\n\n    use crate::{\n        Arena, BinaryOperator, Constant, Expression, FastHashMap, Handle, Literal, ScalarKind,\n        Type, TypeInner, UnaryOperator, UniqueArena, VectorSize,\n    };\n\n    use super::{Behavior, ConstantEvaluator, ExpressionKindTracker, WgslRestrictions};\n\n    #[test]\n    fn unary_op() {\n        let mut types = UniqueArena::new();\n        let mut constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let scalar_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(crate::Scalar::I32),\n            },\n            Default::default(),\n        );\n\n        let vec_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: crate::Scalar::I32,\n                },\n            },\n            Default::default(),\n        );\n\n        let h = constants.append(\n            Constant {\n                name: None,\n                ty: scalar_ty,\n                init: global_expressions\n                    .append(Expression::Literal(Literal::I32(4)), Default::default()),\n            },\n            Default::default(),\n        );\n\n        let h1 = constants.append(\n            Constant {\n                name: None,\n                ty: scalar_ty,\n                init: global_expressions\n                    .append(Expression::Literal(Literal::I32(8)), Default::default()),\n            },\n            Default::default(),\n        );\n\n        let vec_h = constants.append(\n            Constant {\n                name: None,\n                ty: vec_ty,\n                init: global_expressions.append(\n                    Expression::Compose {\n                        ty: vec_ty,\n                        components: vec![constants[h].init, constants[h1].init],\n                    },\n                    Default::default(),\n                ),\n            },\n            Default::default(),\n        );\n\n        let expr = global_expressions.append(Expression::Constant(h), Default::default());\n        let expr1 = global_expressions.append(Expression::Constant(vec_h), Default::default());\n\n        let expr2 = Expression::Unary {\n            op: UnaryOperator::Negate,\n            expr,\n        };\n\n        let expr3 = Expression::Unary {\n            op: UnaryOperator::BitwiseNot,\n            expr,\n        };\n\n        let expr4 = Expression::Unary {\n            op: UnaryOperator::BitwiseNot,\n            expr: expr1,\n        };\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let res1 = solver\n            .try_eval_and_append(expr2, Default::default())\n            .unwrap();\n        let res2 = solver\n            .try_eval_and_append(expr3, Default::default())\n            .unwrap();\n        let res3 = solver\n            .try_eval_and_append(expr4, Default::default())\n            .unwrap();\n\n        assert_eq!(\n            global_expressions[res1],\n            Expression::Literal(Literal::I32(-4))\n        );\n\n        assert_eq!(\n            global_expressions[res2],\n            Expression::Literal(Literal::I32(!4))\n        );\n\n        let res3_inner = &global_expressions[res3];\n\n        match *res3_inner {\n            Expression::Compose {\n                ref ty,\n                ref components,\n            } => {\n                assert_eq!(*ty, vec_ty);\n                let mut components_iter = components.iter().copied();\n                assert_eq!(\n                    global_expressions[components_iter.next().unwrap()],\n                    Expression::Literal(Literal::I32(!4))\n                );\n                assert_eq!(\n                    global_expressions[components_iter.next().unwrap()],\n                    Expression::Literal(Literal::I32(!8))\n                );\n                assert!(components_iter.next().is_none());\n            }\n            _ => panic!(\"Expected vector\"),\n        }\n    }\n\n    #[test]\n    fn matrix_op() {\n        let mut helper = MatrixTestHelper::new();\n\n        for nc in 2..=4 {\n            for nr in 2..=4 {\n                // Validates multiplication on vector-matrix.\n                // vecR(0, 1, .., r) * matCxR(0, 1, .., nc * nr)\n                let evaluated = helper.eval_vector_multiply_matrix(nc, nr);\n                let expected = (0..nc)\n                    .map(|c| (0..nr).map(|r| (r * (c * nr + r)) as f32).sum())\n                    .collect::<Vec<f32>>();\n                assert_eq!(evaluated, expected);\n\n                // Validates multiplication on matrix-vector.\n                // matCxR(0, 1, .., nc * nr) * vecC(0, 1, .., nc)\n                let evaluated = helper.eval_matrix_multiply_vector(nc, nr);\n                let expected = (0..nr)\n                    .map(|r| (0..nc).map(|c| (c * (c * nr + r)) as f32).sum())\n                    .collect::<Vec<f32>>();\n                assert_eq!(evaluated, expected);\n\n                for k in 2..=4 {\n                    // Validates multiplication on matrix-matrix.\n                    // matKxR(0, 1, .., k * nr) * matCxK(0, 1, .., nc * k)\n                    let evaluated = helper.eval_matrix_multiply_matrix(nr, nc, k);\n                    let expected = (0..nc)\n                        .flat_map(|c| {\n                            (0..nr).map(move |r| {\n                                (0..k).map(|v| ((v * nr + r) * (c * k + v)) as f32).sum()\n                            })\n                        })\n                        .collect::<Vec<f32>>();\n                    assert_eq!(evaluated, expected);\n                }\n            }\n        }\n    }\n\n    /// Test fixture providing pre-built f32 vector and matrix constant\n    /// expressions with sequential element values, used to evaluate and verify\n    /// matrix operations.\n    struct MatrixTestHelper {\n        types: UniqueArena<Type>,\n        expressions: Arena<Expression>,\n        /// Vector expressions from [0, 1] to [0, 1, 2, 3].\n        vec_exprs: FastHashMap<usize, Handle<Expression>>,\n        /// Matrix expressions from [0, .., 3] to [0, .., 15].\n        mat_exprs: FastHashMap<(usize, usize), Handle<Expression>>,\n    }\n\n    impl MatrixTestHelper {\n        fn new() -> Self {\n            let mut types = UniqueArena::new();\n            let mut expressions = Arena::new();\n            let span = crate::Span::default();\n\n            let (mut vec_tys, mut mat_tys) = (FastHashMap::default(), FastHashMap::default());\n            for c in 2..=4 {\n                let vec_ty = types.insert(\n                    Type {\n                        name: None,\n                        inner: TypeInner::Vector {\n                            size: Self::int_to_vector_size(c),\n                            scalar: crate::Scalar::F32,\n                        },\n                    },\n                    span,\n                );\n                vec_tys.insert(c, vec_ty);\n                for r in 2..=4 {\n                    let mat_ty = types.insert(\n                        Type {\n                            name: None,\n                            inner: TypeInner::Matrix {\n                                columns: Self::int_to_vector_size(c),\n                                rows: Self::int_to_vector_size(r),\n                                scalar: crate::Scalar::F32,\n                            },\n                        },\n                        span,\n                    );\n                    mat_tys.insert((c, r), mat_ty);\n                }\n            }\n\n            let mut lit_exprs = FastHashMap::default();\n            for i in 0..16 {\n                let expr = expressions.append(Expression::Literal(Literal::F32(i as f32)), span);\n                lit_exprs.insert(i, expr);\n            }\n\n            let mut vec_exprs = FastHashMap::default();\n            for c in 2..=4 {\n                let expr = expressions.append(\n                    Expression::Compose {\n                        ty: *vec_tys.get(&c).unwrap(),\n                        components: (0..c)\n                            .map(|i| *lit_exprs.get(&i).unwrap())\n                            .collect::<Vec<_>>(),\n                    },\n                    span,\n                );\n                vec_exprs.insert(c, expr);\n            }\n\n            let mut mat_exprs = FastHashMap::default();\n            for c in 2..=4 {\n                for r in 2..=4 {\n                    let mut columns = Vec::with_capacity(c);\n                    for cc in 0..c {\n                        let start = cc * r;\n                        let expr = expressions.append(\n                            Expression::Compose {\n                                ty: *vec_tys.get(&r).unwrap(),\n                                components: (start..start + r)\n                                    .map(|i| *lit_exprs.get(&i).unwrap())\n                                    .collect::<Vec<_>>(),\n                            },\n                            span,\n                        );\n                        columns.push(expr);\n                    }\n\n                    let expr = expressions.append(\n                        Expression::Compose {\n                            ty: *mat_tys.get(&(c, r)).unwrap(),\n                            components: columns,\n                        },\n                        span,\n                    );\n                    mat_exprs.insert((c, r), expr);\n                }\n            }\n\n            Self {\n                types,\n                expressions,\n                vec_exprs,\n                mat_exprs,\n            }\n        }\n\n        /// Evaluates vec[0..nr] * mat[0..nc*nr] and returns the result as f32s.\n        fn eval_vector_multiply_matrix(&mut self, nc: usize, nr: usize) -> Vec<f32> {\n            let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&self.expressions);\n            let mut solver = ConstantEvaluator {\n                behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n                types: &mut self.types,\n                constants: &Arena::new(),\n                overrides: &Arena::new(),\n                expressions: &mut self.expressions,\n                expression_kind_tracker,\n                layouter: &mut crate::proc::Layouter::default(),\n            };\n\n            let result = solver\n                .try_eval_and_append(\n                    Expression::Binary {\n                        op: BinaryOperator::Multiply,\n                        left: *self.vec_exprs.get(&nr).unwrap(),\n                        right: *self.mat_exprs.get(&(nc, nr)).unwrap(),\n                    },\n                    Default::default(),\n                )\n                .unwrap();\n            self.flatten(result)\n        }\n\n        /// Evaluates mat[0..nc*nr] * vec[0..nc] and returns the result as f32s.\n        fn eval_matrix_multiply_vector(&mut self, nc: usize, nr: usize) -> Vec<f32> {\n            let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&self.expressions);\n            let mut solver = ConstantEvaluator {\n                behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n                types: &mut self.types,\n                constants: &Arena::new(),\n                overrides: &Arena::new(),\n                expressions: &mut self.expressions,\n                expression_kind_tracker,\n                layouter: &mut crate::proc::Layouter::default(),\n            };\n\n            let result = solver\n                .try_eval_and_append(\n                    Expression::Binary {\n                        op: BinaryOperator::Multiply,\n                        left: *self.mat_exprs.get(&(nc, nr)).unwrap(),\n                        right: *self.vec_exprs.get(&nc).unwrap(),\n                    },\n                    Default::default(),\n                )\n                .unwrap();\n            self.flatten(result)\n        }\n\n        /// Evaluates mat[0..k*l_nr] * mat[0..r_nc*k] and returns the result as\n        /// f32s.\n        fn eval_matrix_multiply_matrix(&mut self, l_nr: usize, r_nc: usize, k: usize) -> Vec<f32> {\n            let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&self.expressions);\n            let mut solver = ConstantEvaluator {\n                behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n                types: &mut self.types,\n                constants: &Arena::new(),\n                overrides: &Arena::new(),\n                expressions: &mut self.expressions,\n                expression_kind_tracker,\n                layouter: &mut crate::proc::Layouter::default(),\n            };\n\n            let result = solver\n                .try_eval_and_append(\n                    Expression::Binary {\n                        op: BinaryOperator::Multiply,\n                        left: *self.mat_exprs.get(&(k, l_nr)).unwrap(),\n                        right: *self.mat_exprs.get(&(r_nc, k)).unwrap(),\n                    },\n                    Default::default(),\n                )\n                .unwrap();\n            self.flatten(result)\n        }\n\n        fn flatten(&self, expr: Handle<Expression>) -> Vec<f32> {\n            let Expression::Compose {\n                ref components,\n                ref ty,\n            } = self.expressions[expr]\n            else {\n                unreachable!()\n            };\n\n            match self.types[*ty].inner {\n                TypeInner::Vector { .. } => components\n                    .iter()\n                    .map(|&comp| {\n                        let Expression::Literal(Literal::F32(v)) = self.expressions[comp] else {\n                            unreachable!()\n                        };\n                        v\n                    })\n                    .collect(),\n                TypeInner::Matrix { .. } => components\n                    .iter()\n                    .flat_map(|&comp| self.flatten(comp))\n                    .collect(),\n                _ => unreachable!(),\n            }\n        }\n\n        fn int_to_vector_size(int: usize) -> VectorSize {\n            match int {\n                2 => VectorSize::Bi,\n                3 => VectorSize::Tri,\n                4 => VectorSize::Quad,\n                _ => unreachable!(),\n            }\n        }\n    }\n\n    #[test]\n    fn cast() {\n        let mut types = UniqueArena::new();\n        let mut constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let scalar_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(crate::Scalar::I32),\n            },\n            Default::default(),\n        );\n\n        let h = constants.append(\n            Constant {\n                name: None,\n                ty: scalar_ty,\n                init: global_expressions\n                    .append(Expression::Literal(Literal::I32(4)), Default::default()),\n            },\n            Default::default(),\n        );\n\n        let expr = global_expressions.append(Expression::Constant(h), Default::default());\n\n        let root = Expression::As {\n            expr,\n            kind: ScalarKind::Bool,\n            convert: Some(crate::BOOL_WIDTH),\n        };\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let res = solver\n            .try_eval_and_append(root, Default::default())\n            .unwrap();\n\n        assert_eq!(\n            global_expressions[res],\n            Expression::Literal(Literal::Bool(true))\n        );\n    }\n\n    #[test]\n    fn access() {\n        let mut types = UniqueArena::new();\n        let mut constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let matrix_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Matrix {\n                    columns: VectorSize::Bi,\n                    rows: VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Default::default(),\n        );\n\n        let vec_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: VectorSize::Tri,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Default::default(),\n        );\n\n        let mut vec1_components = Vec::with_capacity(3);\n        let mut vec2_components = Vec::with_capacity(3);\n\n        for i in 0..3 {\n            let h = global_expressions.append(\n                Expression::Literal(Literal::F32(i as f32)),\n                Default::default(),\n            );\n\n            vec1_components.push(h)\n        }\n\n        for i in 3..6 {\n            let h = global_expressions.append(\n                Expression::Literal(Literal::F32(i as f32)),\n                Default::default(),\n            );\n\n            vec2_components.push(h)\n        }\n\n        let vec1 = constants.append(\n            Constant {\n                name: None,\n                ty: vec_ty,\n                init: global_expressions.append(\n                    Expression::Compose {\n                        ty: vec_ty,\n                        components: vec1_components,\n                    },\n                    Default::default(),\n                ),\n            },\n            Default::default(),\n        );\n\n        let vec2 = constants.append(\n            Constant {\n                name: None,\n                ty: vec_ty,\n                init: global_expressions.append(\n                    Expression::Compose {\n                        ty: vec_ty,\n                        components: vec2_components,\n                    },\n                    Default::default(),\n                ),\n            },\n            Default::default(),\n        );\n\n        let h = constants.append(\n            Constant {\n                name: None,\n                ty: matrix_ty,\n                init: global_expressions.append(\n                    Expression::Compose {\n                        ty: matrix_ty,\n                        components: vec![constants[vec1].init, constants[vec2].init],\n                    },\n                    Default::default(),\n                ),\n            },\n            Default::default(),\n        );\n\n        let base = global_expressions.append(Expression::Constant(h), Default::default());\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let root1 = Expression::AccessIndex { base, index: 1 };\n\n        let res1 = solver\n            .try_eval_and_append(root1, Default::default())\n            .unwrap();\n\n        let root2 = Expression::AccessIndex {\n            base: res1,\n            index: 2,\n        };\n\n        let res2 = solver\n            .try_eval_and_append(root2, Default::default())\n            .unwrap();\n\n        match global_expressions[res1] {\n            Expression::Compose {\n                ref ty,\n                ref components,\n            } => {\n                assert_eq!(*ty, vec_ty);\n                let mut components_iter = components.iter().copied();\n                assert_eq!(\n                    global_expressions[components_iter.next().unwrap()],\n                    Expression::Literal(Literal::F32(3.))\n                );\n                assert_eq!(\n                    global_expressions[components_iter.next().unwrap()],\n                    Expression::Literal(Literal::F32(4.))\n                );\n                assert_eq!(\n                    global_expressions[components_iter.next().unwrap()],\n                    Expression::Literal(Literal::F32(5.))\n                );\n                assert!(components_iter.next().is_none());\n            }\n            _ => panic!(\"Expected vector\"),\n        }\n\n        assert_eq!(\n            global_expressions[res2],\n            Expression::Literal(Literal::F32(5.))\n        );\n    }\n\n    #[test]\n    fn compose_of_constants() {\n        let mut types = UniqueArena::new();\n        let mut constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let i32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(crate::Scalar::I32),\n            },\n            Default::default(),\n        );\n\n        let vec2_i32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: crate::Scalar::I32,\n                },\n            },\n            Default::default(),\n        );\n\n        let h = constants.append(\n            Constant {\n                name: None,\n                ty: i32_ty,\n                init: global_expressions\n                    .append(Expression::Literal(Literal::I32(4)), Default::default()),\n            },\n            Default::default(),\n        );\n\n        let h_expr = global_expressions.append(Expression::Constant(h), Default::default());\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let solved_compose = solver\n            .try_eval_and_append(\n                Expression::Compose {\n                    ty: vec2_i32_ty,\n                    components: vec![h_expr, h_expr],\n                },\n                Default::default(),\n            )\n            .unwrap();\n        let solved_negate = solver\n            .try_eval_and_append(\n                Expression::Unary {\n                    op: UnaryOperator::Negate,\n                    expr: solved_compose,\n                },\n                Default::default(),\n            )\n            .unwrap();\n\n        let pass = match global_expressions[solved_negate] {\n            Expression::Compose { ty, ref components } => {\n                ty == vec2_i32_ty\n                    && components.iter().all(|&component| {\n                        let component = &global_expressions[component];\n                        matches!(*component, Expression::Literal(Literal::I32(-4)))\n                    })\n            }\n            _ => false,\n        };\n        if !pass {\n            panic!(\"unexpected evaluation result\")\n        }\n    }\n\n    #[test]\n    fn splat_of_constant() {\n        let mut types = UniqueArena::new();\n        let mut constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let i32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(crate::Scalar::I32),\n            },\n            Default::default(),\n        );\n\n        let vec2_i32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: crate::Scalar::I32,\n                },\n            },\n            Default::default(),\n        );\n\n        let h = constants.append(\n            Constant {\n                name: None,\n                ty: i32_ty,\n                init: global_expressions\n                    .append(Expression::Literal(Literal::I32(4)), Default::default()),\n            },\n            Default::default(),\n        );\n\n        let h_expr = global_expressions.append(Expression::Constant(h), Default::default());\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let solved_compose = solver\n            .try_eval_and_append(\n                Expression::Splat {\n                    size: VectorSize::Bi,\n                    value: h_expr,\n                },\n                Default::default(),\n            )\n            .unwrap();\n        let solved_negate = solver\n            .try_eval_and_append(\n                Expression::Unary {\n                    op: UnaryOperator::Negate,\n                    expr: solved_compose,\n                },\n                Default::default(),\n            )\n            .unwrap();\n\n        let pass = match global_expressions[solved_negate] {\n            Expression::Compose { ty, ref components } => {\n                ty == vec2_i32_ty\n                    && components.iter().all(|&component| {\n                        let component = &global_expressions[component];\n                        matches!(*component, Expression::Literal(Literal::I32(-4)))\n                    })\n            }\n            _ => false,\n        };\n        if !pass {\n            panic!(\"unexpected evaluation result\")\n        }\n    }\n\n    #[test]\n    fn splat_of_zero_value() {\n        let mut types = UniqueArena::new();\n        let constants = Arena::new();\n        let overrides = Arena::new();\n        let mut global_expressions = Arena::new();\n\n        let f32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Scalar(crate::Scalar::F32),\n            },\n            Default::default(),\n        );\n\n        let vec2_f32_ty = types.insert(\n            Type {\n                name: None,\n                inner: TypeInner::Vector {\n                    size: VectorSize::Bi,\n                    scalar: crate::Scalar::F32,\n                },\n            },\n            Default::default(),\n        );\n\n        let five =\n            global_expressions.append(Expression::Literal(Literal::F32(5.0)), Default::default());\n        let five_splat = global_expressions.append(\n            Expression::Splat {\n                size: VectorSize::Bi,\n                value: five,\n            },\n            Default::default(),\n        );\n        let zero = global_expressions.append(Expression::ZeroValue(f32_ty), Default::default());\n        let zero_splat = global_expressions.append(\n            Expression::Splat {\n                size: VectorSize::Bi,\n                value: zero,\n            },\n            Default::default(),\n        );\n\n        let expression_kind_tracker = &mut ExpressionKindTracker::from_arena(&global_expressions);\n        let mut solver = ConstantEvaluator {\n            behavior: Behavior::Wgsl(WgslRestrictions::Const(None)),\n            types: &mut types,\n            constants: &constants,\n            overrides: &overrides,\n            expressions: &mut global_expressions,\n            expression_kind_tracker,\n            layouter: &mut crate::proc::Layouter::default(),\n        };\n\n        let solved_add = solver\n            .try_eval_and_append(\n                Expression::Binary {\n                    op: BinaryOperator::Add,\n                    left: zero_splat,\n                    right: five_splat,\n                },\n                Default::default(),\n            )\n            .unwrap();\n\n        let pass = match global_expressions[solved_add] {\n            Expression::Compose { ty, ref components } => {\n                ty == vec2_f32_ty\n                    && components.iter().all(|&component| {\n                        let component = &global_expressions[component];\n                        matches!(*component, Expression::Literal(Literal::F32(5.0)))\n                    })\n            }\n            _ => false,\n        };\n        if !pass {\n            panic!(\"unexpected evaluation result\")\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/emitter.rs",
    "content": "use crate::arena::Arena;\n\n/// Helper class to emit expressions\n#[derive(Default, Debug)]\npub struct Emitter {\n    start_len: Option<usize>,\n}\n\nimpl Emitter {\n    pub fn start(&mut self, arena: &Arena<crate::Expression>) {\n        if self.start_len.is_some() {\n            unreachable!(\"Emitting has already started!\");\n        }\n        self.start_len = Some(arena.len());\n    }\n    pub const fn is_running(&self) -> bool {\n        self.start_len.is_some()\n    }\n    #[must_use]\n    pub fn finish(\n        &mut self,\n        arena: &Arena<crate::Expression>,\n    ) -> Option<(crate::Statement, crate::span::Span)> {\n        let start_len = self.start_len.take().unwrap();\n        if start_len != arena.len() {\n            let mut span = crate::span::Span::default();\n            let range = arena.range_from(start_len);\n            for handle in range.clone() {\n                span.subsume(arena.get_span(handle))\n            }\n            Some((crate::Statement::Emit(range), span))\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/index.rs",
    "content": "/*!\nDefinitions for index bounds checking.\n*/\n\nuse core::iter::{self, zip};\n\nuse crate::arena::{Handle, HandleSet, UniqueArena};\nuse crate::{valid, FastHashSet};\n\n/// How should code generated by Naga do bounds checks?\n///\n/// When a vector, matrix, or array index is out of bounds—either negative, or\n/// greater than or equal to the number of elements in the type—WGSL requires\n/// that some other index of the implementation's choice that is in bounds is\n/// used instead. (There are no types with zero elements.)\n///\n/// Similarly, when out-of-bounds coordinates, array indices, or sample indices\n/// are presented to the WGSL `textureLoad` and `textureStore` operations, the\n/// operation is redirected to do something safe.\n///\n/// Different users of Naga will prefer different defaults:\n///\n/// -   When used as part of a WebGPU implementation, the WGSL specification\n///     requires the `Restrict` behavior for array, vector, and matrix accesses,\n///     and either the `Restrict` or `ReadZeroSkipWrite` behaviors for texture\n///     accesses.\n///\n/// -   When used by the `wgpu` crate for native development, `wgpu` selects\n///     `ReadZeroSkipWrite` as its default.\n///\n/// -   Naga's own default is `Unchecked`, so that shader translations\n///     are as faithful to the original as possible.\n///\n/// Sometimes the underlying hardware and drivers can perform bounds checks\n/// themselves, in a way that performs better than the checks Naga would inject.\n/// If you're using native checks like this, then having Naga inject its own\n/// checks as well would be redundant, and the `Unchecked` policy is\n/// appropriate.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum BoundsCheckPolicy {\n    /// Replace out-of-bounds indexes with some arbitrary in-bounds index.\n    ///\n    /// (This does not necessarily mean clamping. For example, interpreting the\n    /// index as unsigned and taking the minimum with the largest valid index\n    /// would also be a valid implementation. That would map negative indices to\n    /// the last element, not the first.)\n    Restrict,\n\n    /// Out-of-bounds reads return zero, and writes have no effect.\n    ///\n    /// When applied to a chain of accesses, like `a[i][j].b[k]`, all index\n    /// expressions are evaluated, regardless of whether prior or later index\n    /// expressions were in bounds. But all the accesses per se are skipped\n    /// if any index is out of bounds.\n    ReadZeroSkipWrite,\n\n    /// Naga adds no checks to indexing operations. Generate the fastest code\n    /// possible. This is the default for Naga, as a translator, but consumers\n    /// should consider defaulting to a safer behavior.\n    Unchecked,\n}\n\n/// Policies for injecting bounds checks during code generation.\n#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(feature = \"deserialize\", serde(default))]\npub struct BoundsCheckPolicies {\n    /// How should the generated code handle array, vector, or matrix indices\n    /// that are out of range?\n    pub index: BoundsCheckPolicy,\n\n    /// How should the generated code handle array, vector, or matrix indices\n    /// that are out of range, when those values live in a [`GlobalVariable`] in\n    /// the [`Storage`] or [`Uniform`] address spaces?\n    ///\n    /// Some graphics hardware provides \"robust buffer access\", a feature that\n    /// ensures that using a pointer cannot access memory outside the 'buffer'\n    /// that it was derived from. In Naga terms, this means that the hardware\n    /// ensures that pointers computed by applying [`Access`] and\n    /// [`AccessIndex`] expressions to a [`GlobalVariable`] whose [`space`] is\n    /// [`Storage`] or [`Uniform`] will never read or write memory outside that\n    /// global variable.\n    ///\n    /// When hardware offers such a feature, it is probably undesirable to have\n    /// Naga inject bounds checking code for such accesses, since the hardware\n    /// can probably provide the same protection more efficiently. However,\n    /// bounds checks are still needed on accesses to indexable values that do\n    /// not live in buffers, like local variables.\n    ///\n    /// So, this option provides a separate policy that applies only to accesses\n    /// to storage and uniform globals. When depending on hardware bounds\n    /// checking, this policy can be `Unchecked` to avoid unnecessary overhead.\n    ///\n    /// When special hardware support is not available, this should probably be\n    /// the same as `index_bounds_check_policy`.\n    ///\n    /// [`GlobalVariable`]: crate::GlobalVariable\n    /// [`space`]: crate::GlobalVariable::space\n    /// [`Restrict`]: crate::proc::BoundsCheckPolicy::Restrict\n    /// [`ReadZeroSkipWrite`]: crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    /// [`Storage`]: crate::AddressSpace::Storage\n    /// [`Uniform`]: crate::AddressSpace::Uniform\n    pub buffer: BoundsCheckPolicy,\n\n    /// How should the generated code handle image texel loads that are out\n    /// of range?\n    ///\n    /// This controls the behavior of [`ImageLoad`] expressions when a coordinate,\n    /// texture array index, level of detail, or multisampled sample number is out of range.\n    ///\n    /// There is no corresponding policy for [`ImageStore`] statements. All the\n    /// platforms we support already discard out-of-bounds image stores,\n    /// effectively implementing the \"skip write\" part of [`ReadZeroSkipWrite`].\n    ///\n    /// [`ImageLoad`]: crate::Expression::ImageLoad\n    /// [`ImageStore`]: crate::Statement::ImageStore\n    /// [`ReadZeroSkipWrite`]: BoundsCheckPolicy::ReadZeroSkipWrite\n    pub image_load: BoundsCheckPolicy,\n\n    /// How should the generated code handle binding array indexes that are out of bounds.\n    pub binding_array: BoundsCheckPolicy,\n}\n\n/// The default `BoundsCheckPolicy` is `Unchecked`.\nimpl Default for BoundsCheckPolicy {\n    fn default() -> Self {\n        BoundsCheckPolicy::Unchecked\n    }\n}\n\nimpl BoundsCheckPolicies {\n    /// Determine which policy applies to `base`.\n    ///\n    /// `base` is the \"base\" expression (the expression being indexed) of a `Access`\n    /// and `AccessIndex` expression. This is either a pointer, a value, being directly\n    /// indexed, or a binding array.\n    ///\n    /// See the documentation for [`BoundsCheckPolicy`] for details about\n    /// when each policy applies.\n    pub fn choose_policy(\n        &self,\n        base: Handle<crate::Expression>,\n        types: &UniqueArena<crate::Type>,\n        info: &valid::FunctionInfo,\n    ) -> BoundsCheckPolicy {\n        let ty = info[base].ty.inner_with(types);\n\n        if let crate::TypeInner::BindingArray { .. } = *ty {\n            return self.binding_array;\n        }\n\n        match ty.pointer_space() {\n            Some(crate::AddressSpace::Storage { access: _ } | crate::AddressSpace::Uniform) => {\n                self.buffer\n            }\n            // This covers other address spaces, but also accessing vectors and\n            // matrices by value, where no pointer is involved.\n            _ => self.index,\n        }\n    }\n\n    /// Return `true` if any of `self`'s policies are `policy`.\n    pub fn contains(&self, policy: BoundsCheckPolicy) -> bool {\n        self.index == policy || self.buffer == policy || self.image_load == policy\n    }\n}\n\n/// An index that may be statically known, or may need to be computed at runtime.\n///\n/// This enum lets us handle both [`Access`] and [`AccessIndex`] expressions\n/// with the same code.\n///\n/// [`Access`]: crate::Expression::Access\n/// [`AccessIndex`]: crate::Expression::AccessIndex\n#[derive(Clone, Copy, Debug)]\npub enum GuardedIndex {\n    Known(u32),\n    Expression(Handle<crate::Expression>),\n}\n\n/// Build a set of expressions used as indices, to cache in temporary variables when\n/// emitted.\n///\n/// Given the bounds-check policies `policies`, construct a `HandleSet` containing the handle\n/// indices of all the expressions in `function` that are ever used as guarded indices\n/// under the [`ReadZeroSkipWrite`] policy. The `module` argument must be the module to\n/// which `function` belongs, and `info` should be that function's analysis results.\n///\n/// Such index expressions will be used twice in the generated code: first for the\n/// comparison to see if the index is in bounds, and then for the access itself, should\n/// the comparison succeed. To avoid computing the expressions twice, the generated code\n/// should cache them in temporary variables.\n///\n/// Why do we need to build such a set in advance, instead of just processing access\n/// expressions as we encounter them? Whether an expression needs to be cached depends on\n/// whether it appears as something like the [`index`] operand of an [`Access`] expression\n/// or the [`level`] operand of an [`ImageLoad`] expression, and on the index bounds check\n/// policies that apply to those accesses. But [`Emit`] statements just identify a range\n/// of expressions by index; there's no good way to tell what an expression is used\n/// for. The only way to do it is to just iterate over all the expressions looking for\n/// relevant `Access` expressions --- which is what this function does.\n///\n/// Simple expressions like variable loads and constants don't make sense to cache: it's\n/// no better than just re-evaluating them. But constants are not covered by `Emit`\n/// statements, and `Load`s are always cached to ensure they occur at the right time, so\n/// we don't bother filtering them out from this set.\n///\n/// Fortunately, we don't need to deal with [`ImageStore`] statements here. When we emit\n/// code for a statement, the writer isn't in the middle of an expression, so we can just\n/// emit declarations for temporaries, initialized appropriately.\n///\n/// None of these concerns apply for SPIR-V output, since it's easy to just reuse an\n/// instruction ID in two places; that has the same semantics as a temporary variable, and\n/// it's inherent in the design of SPIR-V. This function is more useful for text-based\n/// back ends.\n///\n/// [`ReadZeroSkipWrite`]: BoundsCheckPolicy::ReadZeroSkipWrite\n/// [`index`]: crate::Expression::Access::index\n/// [`Access`]: crate::Expression::Access\n/// [`level`]: crate::Expression::ImageLoad::level\n/// [`ImageLoad`]: crate::Expression::ImageLoad\n/// [`Emit`]: crate::Statement::Emit\n/// [`ImageStore`]: crate::Statement::ImageStore\npub fn find_checked_indexes(\n    module: &crate::Module,\n    function: &crate::Function,\n    info: &valid::FunctionInfo,\n    policies: BoundsCheckPolicies,\n) -> HandleSet<crate::Expression> {\n    use crate::Expression as Ex;\n\n    let mut guarded_indices = HandleSet::for_arena(&function.expressions);\n\n    // Don't bother scanning if we never need `ReadZeroSkipWrite`.\n    if policies.contains(BoundsCheckPolicy::ReadZeroSkipWrite) {\n        for (_handle, expr) in function.expressions.iter() {\n            // There's no need to handle `AccessIndex` expressions, as their\n            // indices never need to be cached.\n            match *expr {\n                Ex::Access { base, index } => {\n                    if policies.choose_policy(base, &module.types, info)\n                        == BoundsCheckPolicy::ReadZeroSkipWrite\n                        && access_needs_check(\n                            base,\n                            GuardedIndex::Expression(index),\n                            module,\n                            &function.expressions,\n                            info,\n                        )\n                        .is_some()\n                    {\n                        guarded_indices.insert(index);\n                    }\n                }\n                Ex::ImageLoad {\n                    coordinate,\n                    array_index,\n                    sample,\n                    level,\n                    ..\n                } => {\n                    if policies.image_load == BoundsCheckPolicy::ReadZeroSkipWrite {\n                        guarded_indices.insert(coordinate);\n                        if let Some(array_index) = array_index {\n                            guarded_indices.insert(array_index);\n                        }\n                        if let Some(sample) = sample {\n                            guarded_indices.insert(sample);\n                        }\n                        if let Some(level) = level {\n                            guarded_indices.insert(level);\n                        }\n                    }\n                }\n                _ => {}\n            }\n        }\n    }\n\n    guarded_indices\n}\n\n/// Determine whether `index` is statically known to be in bounds for `base`.\n///\n/// If we can't be sure that the index is in bounds, return the limit within\n/// which valid indices must fall.\n///\n/// The return value is one of the following:\n///\n/// - `Some(Known(n))` indicates that `n` is the largest valid index.\n///\n/// - `Some(Computed(global))` indicates that the largest valid index is one\n///   less than the length of the array that is the last member of the\n///   struct held in `global`.\n///\n/// - `None` indicates that the index need not be checked, either because it\n///   is statically known to be in bounds, or because the applicable policy\n///   is `Unchecked`.\n///\n/// This function only handles subscriptable types: arrays, vectors, and\n/// matrices. It does not handle struct member indices; those never require\n/// run-time checks, so it's best to deal with them further up the call\n/// chain.\n///\n/// This function assumes that any relevant overrides have fully-evaluated\n/// constants as their values (as arranged by [`process_overrides`], for\n/// example).\n///\n/// [`process_overrides`]: crate::back::pipeline_constants::process_overrides\n///\n/// # Panics\n///\n/// - If `base` is not an indexable type, panic.\n///\n/// - If `base` is an override-sized array, but the override's value is not a\n///   fully-evaluated constant expression, panic.\npub fn access_needs_check(\n    base: Handle<crate::Expression>,\n    mut index: GuardedIndex,\n    module: &crate::Module,\n    expressions: &crate::Arena<crate::Expression>,\n    info: &valid::FunctionInfo,\n) -> Option<IndexableLength> {\n    let base_inner = info[base].ty.inner_with(&module.types);\n    // Unwrap safety: `Err` here indicates unindexable base types and invalid\n    // length constants, but `access_needs_check` is only used by back ends, so\n    // validation should have caught those problems.\n    let length = base_inner.indexable_length_resolved(module).unwrap();\n    index.try_resolve_to_constant(expressions, module);\n    if let (&GuardedIndex::Known(index), &IndexableLength::Known(length)) = (&index, &length) {\n        if index < length {\n            // Index is statically known to be in bounds, no check needed.\n            return None;\n        }\n    };\n\n    Some(length)\n}\n\n/// Items returned by the [`bounds_check_iter`] iterator.\n#[cfg_attr(not(feature = \"msl-out\"), allow(dead_code))]\npub(crate) struct BoundsCheck {\n    /// The base of the [`Access`] or [`AccessIndex`] expression.\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    pub base: Handle<crate::Expression>,\n\n    /// The index being accessed.\n    pub index: GuardedIndex,\n\n    /// The length of `base`.\n    pub length: IndexableLength,\n}\n\n/// Returns an iterator of accesses within the chain of `Access` and\n/// `AccessIndex` expressions starting from `chain` that may need to be\n/// bounds-checked at runtime.\n///\n/// Items are yielded as [`BoundsCheck`] instances.\n///\n/// Accesses through a struct are omitted, since you never need a bounds check\n/// for accessing a struct field.\n///\n/// If `chain` isn't an `Access` or `AccessIndex` expression at all, the\n/// iterator is empty.\npub(crate) fn bounds_check_iter<'a>(\n    mut chain: Handle<crate::Expression>,\n    module: &'a crate::Module,\n    function: &'a crate::Function,\n    info: &'a valid::FunctionInfo,\n) -> impl Iterator<Item = BoundsCheck> + 'a {\n    iter::from_fn(move || {\n        let (next_expr, result) = match function.expressions[chain] {\n            crate::Expression::Access { base, index } => {\n                (base, Some((base, GuardedIndex::Expression(index))))\n            }\n            crate::Expression::AccessIndex { base, index } => {\n                // Don't try to check indices into structs. Validation already took\n                // care of them, and access_needs_check doesn't handle that case.\n                let mut base_inner = info[base].ty.inner_with(&module.types);\n                if let crate::TypeInner::Pointer { base, .. } = *base_inner {\n                    base_inner = &module.types[base].inner;\n                }\n                match *base_inner {\n                    crate::TypeInner::Struct { .. } => (base, None),\n                    _ => (base, Some((base, GuardedIndex::Known(index)))),\n                }\n            }\n            _ => return None,\n        };\n        chain = next_expr;\n        Some(result)\n    })\n    .flatten()\n    .filter_map(|(base, index)| {\n        access_needs_check(base, index, module, &function.expressions, info).map(|length| {\n            BoundsCheck {\n                base,\n                index,\n                length,\n            }\n        })\n    })\n}\n\n/// Returns all the types which we need out-of-bounds locals for; that is,\n/// all of the types which the code might attempt to get an out-of-bounds\n/// pointer to, in which case we yield a pointer to the out-of-bounds local\n/// of the correct type.\npub fn oob_local_types(\n    module: &crate::Module,\n    function: &crate::Function,\n    info: &valid::FunctionInfo,\n    policies: BoundsCheckPolicies,\n) -> FastHashSet<Handle<crate::Type>> {\n    let mut result = FastHashSet::default();\n\n    if policies.index != BoundsCheckPolicy::ReadZeroSkipWrite {\n        return result;\n    }\n\n    for statement in &function.body {\n        // The only situation in which we end up actually needing to create an\n        // out-of-bounds pointer is when passing one to a function.\n        //\n        // This is because pointers are never baked; they're just inlined everywhere\n        // they're used. That means that loads can just return 0, and stores can just do\n        // nothing; functions are the only case where you actually *have* to produce a\n        // pointer.\n        if let crate::Statement::Call {\n            function: callee,\n            ref arguments,\n            ..\n        } = *statement\n        {\n            // Now go through the arguments of the function looking for pointers which need bounds checks.\n            for (arg_info, &arg) in zip(&module.functions[callee].arguments, arguments) {\n                match module.types[arg_info.ty].inner {\n                    crate::TypeInner::ValuePointer { .. } => {\n                        // `ValuePointer`s should only ever be used when resolving the types of\n                        // expressions, since the arena can no longer be modified at that point; things\n                        // in the arena should always use proper `Pointer`s.\n                        unreachable!(\"`ValuePointer` found in arena\")\n                    }\n                    crate::TypeInner::Pointer { base, .. } => {\n                        if bounds_check_iter(arg, module, function, info)\n                            .next()\n                            .is_some()\n                        {\n                            result.insert(base);\n                        }\n                    }\n                    _ => continue,\n                };\n            }\n        }\n    }\n    result\n}\n\nimpl GuardedIndex {\n    /// Make a `GuardedIndex::Known` from a `GuardedIndex::Expression` if possible.\n    ///\n    /// Return values that are already `Known` unchanged.\n    pub(crate) fn try_resolve_to_constant(\n        &mut self,\n        expressions: &crate::Arena<crate::Expression>,\n        module: &crate::Module,\n    ) {\n        if let GuardedIndex::Expression(expr) = *self {\n            *self = GuardedIndex::from_expression(expr, expressions, module);\n        }\n    }\n\n    pub(crate) fn from_expression(\n        expr: Handle<crate::Expression>,\n        expressions: &crate::Arena<crate::Expression>,\n        module: &crate::Module,\n    ) -> Self {\n        match module.to_ctx().get_const_val_from(expr, expressions) {\n            Ok(value) => Self::Known(value),\n            Err(_) => Self::Expression(expr),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq)]\npub enum IndexableLengthError {\n    #[error(\"Type is not indexable, and has no length (validation error)\")]\n    TypeNotIndexable,\n    #[error(transparent)]\n    ResolveArraySizeError(#[from] super::ResolveArraySizeError),\n    #[error(\"Array size is still pending\")]\n    Pending(crate::ArraySize),\n}\n\nimpl crate::TypeInner {\n    /// Return the length of a subscriptable type.\n    ///\n    /// The `self` parameter should be a handle to a vector, matrix, or array\n    /// type, a pointer to one of those, or a value pointer. Arrays may be\n    /// fixed-size, dynamically sized, or sized by a specializable constant.\n    /// This function does not handle struct member references, as with\n    /// `AccessIndex`.\n    ///\n    /// The value returned is appropriate for bounds checks on subscripting.\n    ///\n    /// Return an error if `self` does not describe a subscriptable type at all.\n    pub fn indexable_length(\n        &self,\n        module: &crate::Module,\n    ) -> Result<IndexableLength, IndexableLengthError> {\n        use crate::TypeInner as Ti;\n        let known_length = match *self {\n            Ti::Vector { size, .. } => size as _,\n            Ti::Matrix { columns, .. } => columns as _,\n            Ti::Array { size, .. } | Ti::BindingArray { size, .. } => {\n                return size.to_indexable_length(module);\n            }\n            Ti::ValuePointer {\n                size: Some(size), ..\n            } => size as _,\n            Ti::Pointer { base, .. } => {\n                // When assigning types to expressions, ResolveContext::Resolve\n                // does a separate sub-match here instead of a full recursion,\n                // so we'll do the same.\n                let base_inner = &module.types[base].inner;\n                match *base_inner {\n                    Ti::Vector { size, .. } => size as _,\n                    Ti::Matrix { columns, .. } => columns as _,\n                    Ti::Array { size, .. } | Ti::BindingArray { size, .. } => {\n                        return size.to_indexable_length(module)\n                    }\n                    _ => return Err(IndexableLengthError::TypeNotIndexable),\n                }\n            }\n            _ => return Err(IndexableLengthError::TypeNotIndexable),\n        };\n        Ok(IndexableLength::Known(known_length))\n    }\n\n    /// Return the length of `self`, assuming overrides are yet to be supplied.\n    ///\n    /// Return the number of elements in `self`:\n    ///\n    /// - If `self` is a runtime-sized array, then return\n    ///   [`IndexableLength::Dynamic`].\n    ///\n    /// - If `self` is an override-sized array, then assume that override values\n    ///   have not yet been supplied, and return [`IndexableLength::Dynamic`].\n    ///\n    /// - Otherwise, the type simply tells us the length of `self`, so return\n    ///   [`IndexableLength::Known`].\n    ///\n    /// If `self` is not an indexable type at all, return an error.\n    ///\n    /// The difference between this and `indexable_length_resolved` is that we\n    /// treat override-sized arrays and dynamically-sized arrays both as\n    /// [`Dynamic`], on the assumption that our callers want to treat both cases\n    /// as \"not yet possible to check\".\n    ///\n    /// [`Dynamic`]: IndexableLength::Dynamic\n    pub fn indexable_length_pending(\n        &self,\n        module: &crate::Module,\n    ) -> Result<IndexableLength, IndexableLengthError> {\n        let length = self.indexable_length(module);\n        if let Err(IndexableLengthError::Pending(_)) = length {\n            return Ok(IndexableLength::Dynamic);\n        }\n        length\n    }\n\n    /// Return the length of `self`, assuming overrides have been resolved.\n    ///\n    /// Return the number of elements in `self`:\n    ///\n    /// - If `self` is a runtime-sized array, then return\n    ///   [`IndexableLength::Dynamic`].\n    ///\n    /// - If `self` is an override-sized array, then assume that the override's\n    ///   value is a fully-evaluated constant expression, and return\n    ///   [`IndexableLength::Known`]. Otherwise, return an error.\n    ///\n    /// - Otherwise, the type simply tells us the length of `self`, so return\n    ///   [`IndexableLength::Known`].\n    ///\n    /// If `self` is not an indexable type at all, return an error.\n    ///\n    /// The difference between this and `indexable_length_pending` is\n    /// that if `self` is override-sized, we require the override's\n    /// value to be known.\n    pub fn indexable_length_resolved(\n        &self,\n        module: &crate::Module,\n    ) -> Result<IndexableLength, IndexableLengthError> {\n        let length = self.indexable_length(module);\n\n        // If the length is override-based, then try to compute its value now.\n        if let Err(IndexableLengthError::Pending(size)) = length {\n            if let IndexableLength::Known(computed) = size.resolve(module.to_ctx())? {\n                return Ok(IndexableLength::Known(computed));\n            }\n        }\n        length\n    }\n}\n\n/// The number of elements in an indexable type.\n///\n/// This summarizes the length of vectors, matrices, and arrays in a way that is\n/// convenient for indexing and bounds-checking code.\n#[derive(Debug)]\npub enum IndexableLength {\n    /// Values of this type always have the given number of elements.\n    Known(u32),\n\n    /// The number of elements is determined at runtime.\n    Dynamic,\n}\n\nimpl crate::ArraySize {\n    pub const fn to_indexable_length(\n        self,\n        _module: &crate::Module,\n    ) -> Result<IndexableLength, IndexableLengthError> {\n        match self {\n            Self::Constant(length) => Ok(IndexableLength::Known(length.get())),\n            Self::Pending(_) => Err(IndexableLengthError::Pending(self)),\n            Self::Dynamic => Ok(IndexableLength::Dynamic),\n        }\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/keyword_set.rs",
    "content": "use core::{fmt, hash};\n\nuse crate::racy_lock::RacyLock;\nuse crate::FastHashSet;\n\n/// A case-sensitive set of strings,\n/// for use with [`Namer`][crate::proc::Namer] to avoid collisions with keywords and other reserved\n/// identifiers.\n///\n/// This is currently implemented as a hash table.\n/// Future versions of Naga may change the implementation based on speed and code size\n/// considerations.\n#[derive(Clone, Debug, Default, Eq, PartialEq)]\npub struct KeywordSet(FastHashSet<&'static str>);\n\nimpl KeywordSet {\n    /// Returns a new mutable empty set.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Returns a reference to the empty set.\n    pub fn empty() -> &'static Self {\n        static EMPTY: RacyLock<KeywordSet> = RacyLock::new(Default::default);\n        &EMPTY\n    }\n\n    /// Returns whether the set contains the given string.\n    #[inline]\n    pub fn contains(&self, identifier: &str) -> bool {\n        self.0.contains(identifier)\n    }\n}\n\nimpl Default for &'static KeywordSet {\n    fn default() -> Self {\n        KeywordSet::empty()\n    }\n}\n\nimpl FromIterator<&'static str> for KeywordSet {\n    fn from_iter<T: IntoIterator<Item = &'static str>>(iter: T) -> Self {\n        Self(iter.into_iter().collect())\n    }\n}\n\n/// Accepts double references so that `KeywordSet::from_iter(&[\"foo\"])` works.\nimpl<'a> FromIterator<&'a &'static str> for KeywordSet {\n    fn from_iter<T: IntoIterator<Item = &'a &'static str>>(iter: T) -> Self {\n        Self::from_iter(iter.into_iter().copied())\n    }\n}\n\nimpl Extend<&'static str> for KeywordSet {\n    #[expect(\n        clippy::useless_conversion,\n        reason = \"doing .into_iter() sooner reduces distinct monomorphizations\"\n    )]\n    fn extend<T: IntoIterator<Item = &'static str>>(&mut self, iter: T) {\n        self.0.extend(iter.into_iter())\n    }\n}\n\n/// Accepts double references so that `.extend(&[\"foo\"])` works.\nimpl<'a> Extend<&'a &'static str> for KeywordSet {\n    fn extend<T: IntoIterator<Item = &'a &'static str>>(&mut self, iter: T) {\n        self.extend(iter.into_iter().copied())\n    }\n}\n\n/// A case-insensitive, ASCII-only set of strings,\n/// for use with [`Namer`][crate::proc::Namer] to avoid collisions with keywords and other reserved\n/// identifiers.\n///\n/// This is currently implemented as a hash table.\n/// Future versions of Naga may change the implementation based on speed and code size\n/// considerations.\n#[derive(Clone, Debug, Default, Eq, PartialEq)]\npub struct CaseInsensitiveKeywordSet(FastHashSet<AsciiUniCase<&'static str>>);\n\nimpl CaseInsensitiveKeywordSet {\n    /// Returns a new mutable empty set.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Returns a reference to the empty set.\n    pub fn empty() -> &'static Self {\n        static EMPTY: RacyLock<CaseInsensitiveKeywordSet> = RacyLock::new(Default::default);\n        &EMPTY\n    }\n\n    /// Returns whether the set contains the given string, with comparison\n    /// by [`str::eq_ignore_ascii_case()`].\n    #[inline]\n    pub fn contains(&self, identifier: &str) -> bool {\n        self.0.contains(&AsciiUniCase(identifier))\n    }\n}\n\nimpl Default for &'static CaseInsensitiveKeywordSet {\n    fn default() -> Self {\n        CaseInsensitiveKeywordSet::empty()\n    }\n}\n\nimpl FromIterator<&'static str> for CaseInsensitiveKeywordSet {\n    fn from_iter<T: IntoIterator<Item = &'static str>>(iter: T) -> Self {\n        Self(\n            iter.into_iter()\n                .inspect(debug_assert_ascii)\n                .map(AsciiUniCase)\n                .collect(),\n        )\n    }\n}\n\n/// Accepts double references so that `CaseInsensitiveKeywordSet::from_iter(&[\"foo\"])` works.\nimpl<'a> FromIterator<&'a &'static str> for CaseInsensitiveKeywordSet {\n    fn from_iter<T: IntoIterator<Item = &'a &'static str>>(iter: T) -> Self {\n        Self::from_iter(iter.into_iter().copied())\n    }\n}\n\nimpl Extend<&'static str> for CaseInsensitiveKeywordSet {\n    fn extend<T: IntoIterator<Item = &'static str>>(&mut self, iter: T) {\n        self.0.extend(\n            iter.into_iter()\n                .inspect(debug_assert_ascii)\n                .map(AsciiUniCase),\n        )\n    }\n}\n\n/// Accepts double references so that `.extend(&[\"foo\"])` works.\nimpl<'a> Extend<&'a &'static str> for CaseInsensitiveKeywordSet {\n    fn extend<T: IntoIterator<Item = &'a &'static str>>(&mut self, iter: T) {\n        self.extend(iter.into_iter().copied())\n    }\n}\n\n/// A string wrapper type with an ascii case insensitive Eq and Hash impl\n#[derive(Clone, Copy)]\nstruct AsciiUniCase<S: AsRef<str> + ?Sized>(S);\n\nimpl<S: ?Sized + AsRef<str>> fmt::Debug for AsciiUniCase<S> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.as_ref().fmt(f)\n    }\n}\n\nimpl<S: AsRef<str>> PartialEq<Self> for AsciiUniCase<S> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref())\n    }\n}\n\nimpl<S: AsRef<str>> Eq for AsciiUniCase<S> {}\n\nimpl<S: AsRef<str>> hash::Hash for AsciiUniCase<S> {\n    #[inline]\n    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {\n        for byte in self\n            .0\n            .as_ref()\n            .as_bytes()\n            .iter()\n            .map(|b| b.to_ascii_lowercase())\n        {\n            hasher.write_u8(byte);\n        }\n    }\n}\n\nfn debug_assert_ascii(s: &&'static str) {\n    debug_assert!(s.is_ascii(), \"{s:?} not ASCII\")\n}\n"
  },
  {
    "path": "naga/src/proc/layouter.rs",
    "content": "use core::{fmt::Display, num::NonZeroU32, ops};\n\nuse crate::{\n    arena::{Handle, HandleVec},\n    valid::MAX_TYPE_SIZE,\n};\n\n/// A newtype struct where its only valid values are powers of 2\n#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct Alignment(NonZeroU32);\n\nimpl Alignment {\n    pub const ONE: Self = Self(NonZeroU32::new(1).unwrap());\n    pub const TWO: Self = Self(NonZeroU32::new(2).unwrap());\n    pub const FOUR: Self = Self(NonZeroU32::new(4).unwrap());\n    pub const EIGHT: Self = Self(NonZeroU32::new(8).unwrap());\n    pub const SIXTEEN: Self = Self(NonZeroU32::new(16).unwrap());\n\n    pub const MIN_UNIFORM: Self = Self::SIXTEEN;\n\n    pub const fn new(n: u32) -> Option<Self> {\n        if n.is_power_of_two() {\n            // Value can't be 0 since we just checked if it's a power of 2.\n            Some(Self(NonZeroU32::new(n).unwrap()))\n        } else {\n            None\n        }\n    }\n\n    /// # Panics\n    /// If `width` is not a power of 2\n    pub const fn from_width(width: u8) -> Self {\n        Self::new(width as u32).unwrap()\n    }\n\n    /// Returns whether or not `n` is a multiple of this alignment.\n    pub const fn is_aligned(&self, n: u32) -> bool {\n        // equivalent to: `n % self.0.get() == 0` but much faster\n        n & (self.0.get() - 1) == 0\n    }\n\n    /// Round `n` up to the nearest alignment boundary.\n    pub const fn round_up(&self, n: u32) -> u32 {\n        // equivalent to:\n        // match n % self.0.get() {\n        //     0 => n,\n        //     rem => n + (self.0.get() - rem),\n        // }\n        let mask = self.0.get() - 1;\n        (n + mask) & !mask\n    }\n}\n\nimpl Display for Alignment {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.0.get().fmt(f)\n    }\n}\n\nimpl ops::Mul<u32> for Alignment {\n    type Output = u32;\n\n    fn mul(self, rhs: u32) -> Self::Output {\n        self.0.get() * rhs\n    }\n}\n\nimpl ops::Mul for Alignment {\n    type Output = Alignment;\n\n    fn mul(self, rhs: Alignment) -> Self::Output {\n        // Both lhs and rhs are powers of 2, the result will be a power of 2.\n        Self(NonZeroU32::new(self.0.get() * rhs.0.get()).unwrap())\n    }\n}\n\nimpl From<crate::VectorSize> for Alignment {\n    fn from(size: crate::VectorSize) -> Self {\n        match size {\n            crate::VectorSize::Bi => Alignment::TWO,\n            crate::VectorSize::Tri => Alignment::FOUR,\n            crate::VectorSize::Quad => Alignment::FOUR,\n        }\n    }\n}\n\nimpl From<crate::CooperativeSize> for Alignment {\n    fn from(size: crate::CooperativeSize) -> Self {\n        Self(NonZeroU32::new(size as u32).unwrap())\n    }\n}\n\n/// Size and alignment information for a type.\n#[derive(Clone, Copy, Debug, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct TypeLayout {\n    pub size: u32,\n    pub alignment: Alignment,\n}\n\nimpl TypeLayout {\n    /// Produce the stride as if this type is a base of an array.\n    pub const fn to_stride(&self) -> u32 {\n        self.alignment.round_up(self.size)\n    }\n}\n\n/// Helper processor that derives the sizes of all types.\n///\n/// `Layouter` uses the default layout algorithm/table, described in\n/// [WGSL §4.3.7, \"Memory Layout\"]\n///\n/// A `Layouter` may be indexed by `Handle<Type>` values: `layouter[handle]` is the\n/// layout of the type whose handle is `handle`.\n///\n/// [WGSL §4.3.7, \"Memory Layout\"](https://gpuweb.github.io/gpuweb/wgsl/#memory-layouts)\n#[derive(Debug, Default)]\npub struct Layouter {\n    /// Layouts for types in an arena.\n    layouts: HandleVec<crate::Type, TypeLayout>,\n}\n\nimpl ops::Index<Handle<crate::Type>> for Layouter {\n    type Output = TypeLayout;\n    fn index(&self, handle: Handle<crate::Type>) -> &TypeLayout {\n        &self.layouts[handle]\n    }\n}\n\n/// Errors generated by the `Layouter`.\n///\n/// All of these errors can be produced when validating an arbitrary module.\n/// When processing WGSL source, only the `TooLarge` error should be\n/// produced by the `Layouter`, as the front-end should not produce IR\n/// that would result in the other errors.\n#[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)]\npub enum LayoutErrorInner {\n    #[error(\"Array element type {0:?} doesn't exist\")]\n    InvalidArrayElementType(Handle<crate::Type>),\n    #[error(\"Struct member[{0}] type {1:?} doesn't exist\")]\n    InvalidStructMemberType(u32, Handle<crate::Type>),\n    #[error(\"Type width must be a power of two\")]\n    NonPowerOfTwoWidth,\n    #[error(\"Size exceeds limit of {MAX_TYPE_SIZE} bytes\")]\n    TooLarge,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)]\n#[error(\"Error laying out type {ty:?}: {inner}\")]\npub struct LayoutError {\n    pub ty: Handle<crate::Type>,\n    pub inner: LayoutErrorInner,\n}\n\nimpl LayoutErrorInner {\n    const fn with(self, ty: Handle<crate::Type>) -> LayoutError {\n        LayoutError { ty, inner: self }\n    }\n}\n\nimpl Layouter {\n    /// Remove all entries from this `Layouter`, retaining storage.\n    pub fn clear(&mut self) {\n        self.layouts.clear();\n    }\n\n    #[expect(rustdoc::private_intra_doc_links)]\n    /// Extend this `Layouter` with layouts for any new entries in `gctx.types`.\n    ///\n    /// Ensure that every type in `gctx.types` has a corresponding [TypeLayout]\n    /// in [`Self::layouts`].\n    ///\n    /// Some front ends need to be able to compute layouts for existing types\n    /// while module construction is still in progress and new types are still\n    /// being added. This function assumes that the `TypeLayout` values already\n    /// present in `self.layouts` cover their corresponding entries in `types`,\n    /// and extends `self.layouts` as needed to cover the rest. Thus, a front\n    /// end can call this function at any time, passing its current type and\n    /// constant arenas, and then assume that layouts are available for all\n    /// types.\n    #[allow(clippy::or_fun_call)]\n    pub fn update(&mut self, gctx: super::GlobalCtx) -> Result<(), LayoutError> {\n        use crate::TypeInner as Ti;\n\n        for (ty_handle, ty) in gctx.types.iter().skip(self.layouts.len()) {\n            let size = ty\n                .inner\n                .try_size(gctx)\n                .ok_or_else(|| LayoutErrorInner::TooLarge.with(ty_handle))?;\n            let layout = match ty.inner {\n                Ti::Scalar(scalar) | Ti::Atomic(scalar) => {\n                    let alignment = Alignment::new(scalar.width as u32)\n                        .ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;\n                    TypeLayout { size, alignment }\n                }\n                Ti::Vector {\n                    size: vec_size,\n                    scalar,\n                } => {\n                    let alignment = Alignment::new(scalar.width as u32)\n                        .ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;\n                    TypeLayout {\n                        size,\n                        alignment: Alignment::from(vec_size) * alignment,\n                    }\n                }\n                Ti::Matrix {\n                    columns: _,\n                    rows,\n                    scalar,\n                } => {\n                    let alignment = Alignment::new(scalar.width as u32)\n                        .ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;\n                    TypeLayout {\n                        size,\n                        alignment: Alignment::from(rows) * alignment,\n                    }\n                }\n                Ti::CooperativeMatrix {\n                    columns: _,\n                    rows,\n                    scalar,\n                    role: _,\n                } => {\n                    let alignment = Alignment::new(scalar.width as u32)\n                        .ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;\n                    TypeLayout {\n                        size,\n                        alignment: Alignment::from(rows) * alignment,\n                    }\n                }\n                Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout {\n                    size,\n                    alignment: Alignment::ONE,\n                },\n                Ti::Array {\n                    base,\n                    stride: _,\n                    size: _,\n                } => TypeLayout {\n                    size,\n                    alignment: if base < ty_handle {\n                        self[base].alignment\n                    } else {\n                        return Err(LayoutErrorInner::InvalidArrayElementType(base).with(ty_handle));\n                    },\n                },\n                Ti::Struct { span, ref members } => {\n                    let mut alignment = Alignment::ONE;\n                    for (index, member) in members.iter().enumerate() {\n                        alignment = if member.ty < ty_handle {\n                            alignment.max(self[member.ty].alignment)\n                        } else {\n                            return Err(LayoutErrorInner::InvalidStructMemberType(\n                                index as u32,\n                                member.ty,\n                            )\n                            .with(ty_handle));\n                        };\n                    }\n                    TypeLayout {\n                        size: span,\n                        alignment,\n                    }\n                }\n                Ti::Image { .. }\n                | Ti::Sampler { .. }\n                | Ti::AccelerationStructure { .. }\n                | Ti::RayQuery { .. }\n                | Ti::BindingArray { .. } => TypeLayout {\n                    size,\n                    alignment: Alignment::ONE,\n                },\n            };\n            debug_assert!(size <= layout.size);\n            self.layouts.insert(ty_handle, layout);\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/mod.rs",
    "content": "/*!\n[`Module`](super::Module) processing functionality.\n*/\n\nmod constant_evaluator;\nmod emitter;\npub mod index;\nmod keyword_set;\nmod layouter;\nmod namer;\nmod overloads;\nmod terminator;\nmod type_methods;\nmod typifier;\n\npub use constant_evaluator::{\n    ConstantEvaluator, ConstantEvaluatorError, ExpressionKind, ExpressionKindTracker,\n};\npub use emitter::Emitter;\npub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError};\npub use keyword_set::{CaseInsensitiveKeywordSet, KeywordSet};\npub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayout};\npub use namer::{EntryPointIndex, ExternalTextureNameKey, NameKey, Namer};\npub use overloads::{Conclusion, MissingSpecialType, OverloadSet, Rule};\npub use terminator::ensure_block_returns;\nuse thiserror::Error;\npub use type_methods::{\n    concrete_int_scalars, min_max_float_representable_by, vector_size_str, vector_sizes,\n};\npub use typifier::{compare_types, ResolveContext, ResolveError, TypeResolution};\n\nuse crate::non_max_u32::NonMaxU32;\n\nimpl From<super::StorageFormat> for super::Scalar {\n    fn from(format: super::StorageFormat) -> Self {\n        use super::{ScalarKind as Sk, StorageFormat as Sf};\n        let kind = match format {\n            Sf::R8Unorm => Sk::Float,\n            Sf::R8Snorm => Sk::Float,\n            Sf::R8Uint => Sk::Uint,\n            Sf::R8Sint => Sk::Sint,\n            Sf::R16Uint => Sk::Uint,\n            Sf::R16Sint => Sk::Sint,\n            Sf::R16Float => Sk::Float,\n            Sf::Rg8Unorm => Sk::Float,\n            Sf::Rg8Snorm => Sk::Float,\n            Sf::Rg8Uint => Sk::Uint,\n            Sf::Rg8Sint => Sk::Sint,\n            Sf::R32Uint => Sk::Uint,\n            Sf::R32Sint => Sk::Sint,\n            Sf::R32Float => Sk::Float,\n            Sf::Rg16Uint => Sk::Uint,\n            Sf::Rg16Sint => Sk::Sint,\n            Sf::Rg16Float => Sk::Float,\n            Sf::Rgba8Unorm => Sk::Float,\n            Sf::Rgba8Snorm => Sk::Float,\n            Sf::Rgba8Uint => Sk::Uint,\n            Sf::Rgba8Sint => Sk::Sint,\n            Sf::Bgra8Unorm => Sk::Float,\n            Sf::Rgb10a2Uint => Sk::Uint,\n            Sf::Rgb10a2Unorm => Sk::Float,\n            Sf::Rg11b10Ufloat => Sk::Float,\n            Sf::R64Uint => Sk::Uint,\n            Sf::Rg32Uint => Sk::Uint,\n            Sf::Rg32Sint => Sk::Sint,\n            Sf::Rg32Float => Sk::Float,\n            Sf::Rgba16Uint => Sk::Uint,\n            Sf::Rgba16Sint => Sk::Sint,\n            Sf::Rgba16Float => Sk::Float,\n            Sf::Rgba32Uint => Sk::Uint,\n            Sf::Rgba32Sint => Sk::Sint,\n            Sf::Rgba32Float => Sk::Float,\n            Sf::R16Unorm => Sk::Float,\n            Sf::R16Snorm => Sk::Float,\n            Sf::Rg16Unorm => Sk::Float,\n            Sf::Rg16Snorm => Sk::Float,\n            Sf::Rgba16Unorm => Sk::Float,\n            Sf::Rgba16Snorm => Sk::Float,\n        };\n        let width = match format {\n            Sf::R64Uint => 8,\n            _ => 4,\n        };\n        super::Scalar { kind, width }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum HashableLiteral {\n    F64(u64),\n    F32(u32),\n    F16(u16),\n    U32(u32),\n    I32(i32),\n    U64(u64),\n    I64(i64),\n    Bool(bool),\n    AbstractInt(i64),\n    AbstractFloat(u64),\n}\n\nimpl From<crate::Literal> for HashableLiteral {\n    fn from(l: crate::Literal) -> Self {\n        match l {\n            crate::Literal::F64(v) => Self::F64(v.to_bits()),\n            crate::Literal::F32(v) => Self::F32(v.to_bits()),\n            crate::Literal::F16(v) => Self::F16(v.to_bits()),\n            crate::Literal::U32(v) => Self::U32(v),\n            crate::Literal::I32(v) => Self::I32(v),\n            crate::Literal::U64(v) => Self::U64(v),\n            crate::Literal::I64(v) => Self::I64(v),\n            crate::Literal::Bool(v) => Self::Bool(v),\n            crate::Literal::AbstractInt(v) => Self::AbstractInt(v),\n            crate::Literal::AbstractFloat(v) => Self::AbstractFloat(v.to_bits()),\n        }\n    }\n}\n\nimpl crate::Literal {\n    pub const fn new(value: u8, scalar: crate::Scalar) -> Option<Self> {\n        match (value, scalar.kind, scalar.width) {\n            (value, crate::ScalarKind::Float, 8) => Some(Self::F64(value as _)),\n            (value, crate::ScalarKind::Float, 4) => Some(Self::F32(value as _)),\n            (value, crate::ScalarKind::Float, 2) => {\n                Some(Self::F16(half::f16::from_f32_const(value as _)))\n            }\n            (value, crate::ScalarKind::Uint, 4) => Some(Self::U32(value as _)),\n            (value, crate::ScalarKind::Sint, 4) => Some(Self::I32(value as _)),\n            (value, crate::ScalarKind::Uint, 8) => Some(Self::U64(value as _)),\n            (value, crate::ScalarKind::Sint, 8) => Some(Self::I64(value as _)),\n            (1, crate::ScalarKind::Bool, crate::BOOL_WIDTH) => Some(Self::Bool(true)),\n            (0, crate::ScalarKind::Bool, crate::BOOL_WIDTH) => Some(Self::Bool(false)),\n            (value, crate::ScalarKind::AbstractInt, 8) => Some(Self::AbstractInt(value as _)),\n            (value, crate::ScalarKind::AbstractFloat, 8) => Some(Self::AbstractFloat(value as _)),\n            _ => None,\n        }\n    }\n\n    pub const fn zero(scalar: crate::Scalar) -> Option<Self> {\n        Self::new(0, scalar)\n    }\n\n    pub const fn one(scalar: crate::Scalar) -> Option<Self> {\n        Self::new(1, scalar)\n    }\n\n    pub const fn minus_one(scalar: crate::Scalar) -> Option<Self> {\n        match (scalar.kind, scalar.width) {\n            (crate::ScalarKind::Float, 8) => Some(Self::F64(-1.0)),\n            (crate::ScalarKind::Float, 4) => Some(Self::F32(-1.0)),\n            (crate::ScalarKind::Float, 2) => Some(Self::F16(half::f16::from_f32_const(-1.0))),\n            (crate::ScalarKind::Sint, 8) => Some(Self::I64(-1)),\n            (crate::ScalarKind::Sint, 4) => Some(Self::I32(-1)),\n            (crate::ScalarKind::AbstractInt, 8) => Some(Self::AbstractInt(-1)),\n            _ => None,\n        }\n    }\n\n    pub const fn width(&self) -> crate::Bytes {\n        match *self {\n            Self::F64(_) | Self::I64(_) | Self::U64(_) => 8,\n            Self::F32(_) | Self::U32(_) | Self::I32(_) => 4,\n            Self::F16(_) => 2,\n            Self::Bool(_) => crate::BOOL_WIDTH,\n            Self::AbstractInt(_) | Self::AbstractFloat(_) => crate::ABSTRACT_WIDTH,\n        }\n    }\n    pub const fn scalar(&self) -> crate::Scalar {\n        match *self {\n            Self::F64(_) => crate::Scalar::F64,\n            Self::F32(_) => crate::Scalar::F32,\n            Self::F16(_) => crate::Scalar::F16,\n            Self::U32(_) => crate::Scalar::U32,\n            Self::I32(_) => crate::Scalar::I32,\n            Self::U64(_) => crate::Scalar::U64,\n            Self::I64(_) => crate::Scalar::I64,\n            Self::Bool(_) => crate::Scalar::BOOL,\n            Self::AbstractInt(_) => crate::Scalar::ABSTRACT_INT,\n            Self::AbstractFloat(_) => crate::Scalar::ABSTRACT_FLOAT,\n        }\n    }\n    pub const fn scalar_kind(&self) -> crate::ScalarKind {\n        self.scalar().kind\n    }\n    pub const fn ty_inner(&self) -> crate::TypeInner {\n        crate::TypeInner::Scalar(self.scalar())\n    }\n}\n\nimpl TryFrom<crate::Literal> for u32 {\n    type Error = ConstValueError;\n\n    fn try_from(value: crate::Literal) -> Result<Self, Self::Error> {\n        match value {\n            crate::Literal::U32(value) => Ok(value),\n            crate::Literal::I32(value) => value.try_into().map_err(|_| ConstValueError::Negative),\n            _ => Err(ConstValueError::InvalidType),\n        }\n    }\n}\n\nimpl TryFrom<crate::Literal> for bool {\n    type Error = ConstValueError;\n\n    fn try_from(value: crate::Literal) -> Result<Self, Self::Error> {\n        match value {\n            crate::Literal::Bool(value) => Ok(value),\n            _ => Err(ConstValueError::InvalidType),\n        }\n    }\n}\n\nimpl super::AddressSpace {\n    pub fn access(self) -> crate::StorageAccess {\n        use crate::StorageAccess as Sa;\n        match self {\n            crate::AddressSpace::Function\n            | crate::AddressSpace::Private\n            | crate::AddressSpace::WorkGroup => Sa::LOAD | Sa::STORE,\n            crate::AddressSpace::Uniform => Sa::LOAD,\n            crate::AddressSpace::Storage { access } => access,\n            crate::AddressSpace::Handle => Sa::LOAD,\n            crate::AddressSpace::Immediate => Sa::LOAD,\n            // TaskPayload isn't always writable, but this is checked for elsewhere,\n            // when not using multiple payloads and matching the entry payload is checked.\n            crate::AddressSpace::TaskPayload => Sa::LOAD | Sa::STORE,\n            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {\n                Sa::LOAD | Sa::STORE\n            }\n        }\n    }\n}\n\nimpl super::MathFunction {\n    pub const fn argument_count(&self) -> usize {\n        match *self {\n            // comparison\n            Self::Abs => 1,\n            Self::Min => 2,\n            Self::Max => 2,\n            Self::Clamp => 3,\n            Self::Saturate => 1,\n            // trigonometry\n            Self::Cos => 1,\n            Self::Cosh => 1,\n            Self::Sin => 1,\n            Self::Sinh => 1,\n            Self::Tan => 1,\n            Self::Tanh => 1,\n            Self::Acos => 1,\n            Self::Asin => 1,\n            Self::Atan => 1,\n            Self::Atan2 => 2,\n            Self::Asinh => 1,\n            Self::Acosh => 1,\n            Self::Atanh => 1,\n            Self::Radians => 1,\n            Self::Degrees => 1,\n            // decomposition\n            Self::Ceil => 1,\n            Self::Floor => 1,\n            Self::Round => 1,\n            Self::Fract => 1,\n            Self::Trunc => 1,\n            Self::Modf => 1,\n            Self::Frexp => 1,\n            Self::Ldexp => 2,\n            // exponent\n            Self::Exp => 1,\n            Self::Exp2 => 1,\n            Self::Log => 1,\n            Self::Log2 => 1,\n            Self::Pow => 2,\n            // geometry\n            Self::Dot => 2,\n            Self::Dot4I8Packed => 2,\n            Self::Dot4U8Packed => 2,\n            Self::Outer => 2,\n            Self::Cross => 2,\n            Self::Distance => 2,\n            Self::Length => 1,\n            Self::Normalize => 1,\n            Self::FaceForward => 3,\n            Self::Reflect => 2,\n            Self::Refract => 3,\n            // computational\n            Self::Sign => 1,\n            Self::Fma => 3,\n            Self::Mix => 3,\n            Self::Step => 2,\n            Self::SmoothStep => 3,\n            Self::Sqrt => 1,\n            Self::InverseSqrt => 1,\n            Self::Inverse => 1,\n            Self::Transpose => 1,\n            Self::Determinant => 1,\n            Self::QuantizeToF16 => 1,\n            // bits\n            Self::CountTrailingZeros => 1,\n            Self::CountLeadingZeros => 1,\n            Self::CountOneBits => 1,\n            Self::ReverseBits => 1,\n            Self::ExtractBits => 3,\n            Self::InsertBits => 4,\n            Self::FirstTrailingBit => 1,\n            Self::FirstLeadingBit => 1,\n            // data packing\n            Self::Pack4x8snorm => 1,\n            Self::Pack4x8unorm => 1,\n            Self::Pack2x16snorm => 1,\n            Self::Pack2x16unorm => 1,\n            Self::Pack2x16float => 1,\n            Self::Pack4xI8 => 1,\n            Self::Pack4xU8 => 1,\n            Self::Pack4xI8Clamp => 1,\n            Self::Pack4xU8Clamp => 1,\n            // data unpacking\n            Self::Unpack4x8snorm => 1,\n            Self::Unpack4x8unorm => 1,\n            Self::Unpack2x16snorm => 1,\n            Self::Unpack2x16unorm => 1,\n            Self::Unpack2x16float => 1,\n            Self::Unpack4xI8 => 1,\n            Self::Unpack4xU8 => 1,\n        }\n    }\n}\n\nimpl crate::Expression {\n    /// Returns true if the expression is considered emitted at the start of a function.\n    pub const fn needs_pre_emit(&self) -> bool {\n        match *self {\n            Self::Literal(_)\n            | Self::Constant(_)\n            | Self::Override(_)\n            | Self::ZeroValue(_)\n            | Self::FunctionArgument(_)\n            | Self::GlobalVariable(_)\n            | Self::LocalVariable(_) => true,\n            _ => false,\n        }\n    }\n\n    /// Return true if this expression is a dynamic array/vector/matrix index,\n    /// for [`Access`].\n    ///\n    /// This method returns true if this expression is a dynamically computed\n    /// index, and as such can only be used to index matrices when they appear\n    /// behind a pointer. See the documentation for [`Access`] for details.\n    ///\n    /// Note, this does not check the _type_ of the given expression. It's up to\n    /// the caller to establish that the `Access` expression is well-typed\n    /// through other means, like [`ResolveContext`].\n    ///\n    /// [`Access`]: crate::Expression::Access\n    /// [`ResolveContext`]: crate::proc::ResolveContext\n    pub const fn is_dynamic_index(&self) -> bool {\n        match *self {\n            Self::Literal(_) | Self::ZeroValue(_) | Self::Constant(_) => false,\n            _ => true,\n        }\n    }\n}\n\nimpl crate::Function {\n    /// Return the global variable being accessed by the expression `pointer`.\n    ///\n    /// Assuming that `pointer` is a series of `Access` and `AccessIndex`\n    /// expressions that ultimately access some part of a `GlobalVariable`,\n    /// return a handle for that global.\n    ///\n    /// If the expression does not ultimately access a global variable, return\n    /// `None`.\n    pub fn originating_global(\n        &self,\n        mut pointer: crate::Handle<crate::Expression>,\n    ) -> Option<crate::Handle<crate::GlobalVariable>> {\n        loop {\n            pointer = match self.expressions[pointer] {\n                crate::Expression::Access { base, .. } => base,\n                crate::Expression::AccessIndex { base, .. } => base,\n                crate::Expression::GlobalVariable(handle) => return Some(handle),\n                crate::Expression::LocalVariable(_) => return None,\n                crate::Expression::FunctionArgument(_) => return None,\n                // There are no other expressions that produce pointer values.\n                _ => unreachable!(),\n            }\n        }\n    }\n}\n\nimpl crate::SampleLevel {\n    pub const fn implicit_derivatives(&self) -> bool {\n        match *self {\n            Self::Auto | Self::Bias(_) => true,\n            Self::Zero | Self::Exact(_) | Self::Gradient { .. } => false,\n        }\n    }\n}\n\nimpl crate::Binding {\n    pub const fn to_built_in(&self) -> Option<crate::BuiltIn> {\n        match *self {\n            crate::Binding::BuiltIn(built_in) => Some(built_in),\n            Self::Location { .. } => None,\n        }\n    }\n}\n\nimpl super::SwizzleComponent {\n    pub const XYZW: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];\n\n    pub const fn index(&self) -> u32 {\n        match *self {\n            Self::X => 0,\n            Self::Y => 1,\n            Self::Z => 2,\n            Self::W => 3,\n        }\n    }\n    pub const fn from_index(idx: u32) -> Self {\n        match idx {\n            0 => Self::X,\n            1 => Self::Y,\n            2 => Self::Z,\n            _ => Self::W,\n        }\n    }\n}\n\nimpl super::ImageClass {\n    pub const fn is_multisampled(self) -> bool {\n        match self {\n            crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => multi,\n            crate::ImageClass::Storage { .. } => false,\n            crate::ImageClass::External => false,\n        }\n    }\n\n    pub const fn is_mipmapped(self) -> bool {\n        match self {\n            crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => !multi,\n            crate::ImageClass::Storage { .. } => false,\n            crate::ImageClass::External => false,\n        }\n    }\n\n    pub const fn is_depth(self) -> bool {\n        matches!(self, crate::ImageClass::Depth { .. })\n    }\n}\n\nimpl crate::Module {\n    pub const fn to_ctx(&self) -> GlobalCtx<'_> {\n        GlobalCtx {\n            types: &self.types,\n            constants: &self.constants,\n            overrides: &self.overrides,\n            global_expressions: &self.global_expressions,\n        }\n    }\n\n    pub fn compare_types(&self, lhs: &TypeResolution, rhs: &TypeResolution) -> bool {\n        compare_types(lhs, rhs, &self.types)\n    }\n}\n\n#[derive(Debug)]\npub enum ConstValueError {\n    NonConst,\n    Negative,\n    InvalidType,\n}\n\nimpl From<core::convert::Infallible> for ConstValueError {\n    fn from(_: core::convert::Infallible) -> Self {\n        unreachable!()\n    }\n}\n\n#[derive(Clone, Copy)]\npub struct GlobalCtx<'a> {\n    pub types: &'a crate::UniqueArena<crate::Type>,\n    pub constants: &'a crate::Arena<crate::Constant>,\n    pub overrides: &'a crate::Arena<crate::Override>,\n    pub global_expressions: &'a crate::Arena<crate::Expression>,\n}\n\nimpl GlobalCtx<'_> {\n    /// Try to evaluate the expression in `self.global_expressions` using its `handle`\n    /// and return it as a `T: TryFrom<ir::Literal>`.\n    ///\n    /// This currently only evaluates scalar expressions. If adding support for vectors,\n    /// consider changing `valid::expression::validate_constant_shift_amounts` to use that\n    /// support.\n    #[cfg_attr(\n        not(any(\n            feature = \"glsl-in\",\n            feature = \"spv-in\",\n            feature = \"wgsl-in\",\n            glsl_out,\n            hlsl_out,\n            msl_out,\n            wgsl_out\n        )),\n        allow(dead_code)\n    )]\n    pub(super) fn get_const_val<T, E>(\n        &self,\n        handle: crate::Handle<crate::Expression>,\n    ) -> Result<T, ConstValueError>\n    where\n        T: TryFrom<crate::Literal, Error = E>,\n        E: Into<ConstValueError>,\n    {\n        self.get_const_val_from(handle, self.global_expressions)\n    }\n\n    pub(super) fn get_const_val_from<T, E>(\n        &self,\n        handle: crate::Handle<crate::Expression>,\n        arena: &crate::Arena<crate::Expression>,\n    ) -> Result<T, ConstValueError>\n    where\n        T: TryFrom<crate::Literal, Error = E>,\n        E: Into<ConstValueError>,\n    {\n        fn get(\n            gctx: GlobalCtx,\n            handle: crate::Handle<crate::Expression>,\n            arena: &crate::Arena<crate::Expression>,\n        ) -> Option<crate::Literal> {\n            match arena[handle] {\n                crate::Expression::Literal(literal) => Some(literal),\n                crate::Expression::ZeroValue(ty) => match gctx.types[ty].inner {\n                    crate::TypeInner::Scalar(scalar) => crate::Literal::zero(scalar),\n                    _ => None,\n                },\n                _ => None,\n            }\n        }\n        let value = match arena[handle] {\n            crate::Expression::Constant(c) => {\n                get(*self, self.constants[c].init, self.global_expressions)\n            }\n            _ => get(*self, handle, arena),\n        };\n        match value {\n            Some(v) => v.try_into().map_err(Into::into),\n            None => Err(ConstValueError::NonConst),\n        }\n    }\n\n    pub fn compare_types(&self, lhs: &TypeResolution, rhs: &TypeResolution) -> bool {\n        compare_types(lhs, rhs, self.types)\n    }\n}\n\n#[derive(Error, Debug, Clone, Copy, PartialEq)]\npub enum ResolveArraySizeError {\n    #[error(\"array element count must be positive (> 0)\")]\n    ExpectedPositiveArrayLength,\n    #[error(\"internal: array size override has not been resolved\")]\n    NonConstArrayLength,\n}\n\nimpl crate::ArraySize {\n    /// Return the number of elements that `size` represents, if known at code generation time.\n    ///\n    /// If `size` is override-based, return an error unless the override's\n    /// initializer is a fully evaluated constant expression. You can call\n    /// [`pipeline_constants::process_overrides`] to supply values for a\n    /// module's overrides and ensure their initializers are fully evaluated, as\n    /// this function expects.\n    ///\n    /// [`pipeline_constants::process_overrides`]: crate::back::pipeline_constants::process_overrides\n    pub fn resolve(&self, gctx: GlobalCtx) -> Result<IndexableLength, ResolveArraySizeError> {\n        match *self {\n            crate::ArraySize::Constant(length) => Ok(IndexableLength::Known(length.get())),\n            crate::ArraySize::Pending(handle) => {\n                let Some(expr) = gctx.overrides[handle].init else {\n                    return Err(ResolveArraySizeError::NonConstArrayLength);\n                };\n                let length = gctx.get_const_val(expr).map_err(|err| match err {\n                    ConstValueError::NonConst => ResolveArraySizeError::NonConstArrayLength,\n                    ConstValueError::Negative | ConstValueError::InvalidType => {\n                        ResolveArraySizeError::ExpectedPositiveArrayLength\n                    }\n                })?;\n\n                if length == 0 {\n                    return Err(ResolveArraySizeError::ExpectedPositiveArrayLength);\n                }\n\n                Ok(IndexableLength::Known(length))\n            }\n            crate::ArraySize::Dynamic => Ok(IndexableLength::Dynamic),\n        }\n    }\n}\n\n/// Return an iterator over the individual components assembled by a\n/// `Compose` expression.\n///\n/// Given `ty` and `components` from an `Expression::Compose`, return an\n/// iterator over the components of the resulting value.\n///\n/// Normally, this would just be an iterator over `components`. However,\n/// `Compose` expressions can concatenate vectors, in which case the i'th\n/// value being composed is not generally the i'th element of `components`.\n/// This function consults `ty` to decide if this concatenation is occurring,\n/// and returns an iterator that produces the components of the result of\n/// the `Compose` expression in either case.\npub fn flatten_compose<'arenas>(\n    ty: crate::Handle<crate::Type>,\n    components: &'arenas [crate::Handle<crate::Expression>],\n    expressions: &'arenas crate::Arena<crate::Expression>,\n    types: &'arenas crate::UniqueArena<crate::Type>,\n) -> impl Iterator<Item = crate::Handle<crate::Expression>> + 'arenas {\n    // Returning `impl Iterator` is a bit tricky. We may or may not\n    // want to flatten the components, but we have to settle on a\n    // single concrete type to return. This function returns a single\n    // iterator chain that handles both the flattening and\n    // non-flattening cases.\n    let (size, is_vector) = if let crate::TypeInner::Vector { size, .. } = types[ty].inner {\n        (size as usize, true)\n    } else {\n        (components.len(), false)\n    };\n\n    /// Flatten `Compose` expressions if `is_vector` is true.\n    fn flatten_compose<'c>(\n        component: &'c crate::Handle<crate::Expression>,\n        is_vector: bool,\n        expressions: &'c crate::Arena<crate::Expression>,\n    ) -> &'c [crate::Handle<crate::Expression>] {\n        if is_vector {\n            if let crate::Expression::Compose {\n                ty: _,\n                components: ref subcomponents,\n            } = expressions[*component]\n            {\n                return subcomponents;\n            }\n        }\n        core::slice::from_ref(component)\n    }\n\n    /// Flatten `Splat` expressions if `is_vector` is true.\n    fn flatten_splat<'c>(\n        component: &'c crate::Handle<crate::Expression>,\n        is_vector: bool,\n        expressions: &'c crate::Arena<crate::Expression>,\n    ) -> impl Iterator<Item = crate::Handle<crate::Expression>> {\n        let mut expr = *component;\n        let mut count = 1;\n        if is_vector {\n            if let crate::Expression::Splat { size, value } = expressions[expr] {\n                expr = value;\n                count = size as usize;\n            }\n        }\n        core::iter::repeat_n(expr, count)\n    }\n\n    // Expressions like `vec4(vec3(vec2(6, 7), 8), 9)` require us to\n    // flatten up to two levels of `Compose` expressions.\n    //\n    // Expressions like `vec4(vec3(1.0), 1.0)` require us to flatten\n    // `Splat` expressions. Fortunately, the operand of a `Splat` must\n    // be a scalar, so we can stop there.\n    components\n        .iter()\n        .flat_map(move |component| flatten_compose(component, is_vector, expressions))\n        .flat_map(move |component| flatten_compose(component, is_vector, expressions))\n        .flat_map(move |component| flatten_splat(component, is_vector, expressions))\n        .take(size)\n}\n\nimpl super::ShaderStage {\n    pub const fn compute_like(self) -> bool {\n        match self {\n            Self::Vertex | Self::Fragment => false,\n            Self::Compute | Self::Task | Self::Mesh => true,\n            Self::RayGeneration | Self::AnyHit | Self::ClosestHit | Self::Miss => false,\n        }\n    }\n\n    /// Mesh or task shader\n    pub const fn mesh_like(self) -> bool {\n        match self {\n            Self::Task | Self::Mesh => true,\n            _ => false,\n        }\n    }\n}\n\n#[test]\nfn test_matrix_size() {\n    let module = crate::Module::default();\n    assert_eq!(\n        crate::TypeInner::Matrix {\n            columns: crate::VectorSize::Tri,\n            rows: crate::VectorSize::Tri,\n            scalar: crate::Scalar::F32,\n        }\n        .size(module.to_ctx()),\n        48,\n    );\n}\n\nimpl crate::Module {\n    /// Extracts mesh shader info from a mesh output global variable. Used in frontends\n    /// and by validators. This only validates the output variable itself, and not the\n    /// vertex and primitive output types.\n    ///\n    /// The output contains the extracted mesh stage info, with overrides unset,\n    /// and then the overrides separately. This is because the overrides should be\n    /// treated as expressions elsewhere, but that requires mutably modifying the\n    /// module and the expressions should only be created at parse time, not validation\n    /// time.\n    #[allow(clippy::type_complexity)]\n    pub fn analyze_mesh_shader_info(\n        &self,\n        gv: crate::Handle<crate::GlobalVariable>,\n    ) -> (\n        crate::MeshStageInfo,\n        [Option<crate::Handle<crate::Override>>; 2],\n        Option<crate::WithSpan<crate::valid::EntryPointError>>,\n    ) {\n        use crate::span::AddSpan;\n        use crate::valid::EntryPointError;\n        #[derive(Default)]\n        struct OutError {\n            pub inner: Option<EntryPointError>,\n        }\n        impl OutError {\n            pub fn set(&mut self, err: EntryPointError) {\n                if self.inner.is_none() {\n                    self.inner = Some(err);\n                }\n            }\n        }\n\n        // Used to temporarily initialize stuff\n        let null_type = crate::Handle::new(NonMaxU32::new(0).unwrap());\n        let mut output = crate::MeshStageInfo {\n            topology: crate::MeshOutputTopology::Triangles,\n            max_vertices: 0,\n            max_vertices_override: None,\n            max_primitives: 0,\n            max_primitives_override: None,\n            vertex_output_type: null_type,\n            primitive_output_type: null_type,\n            output_variable: gv,\n        };\n        // Stores the error to output, if any.\n        let mut error = OutError::default();\n        let r#type = &self.types[self.global_variables[gv].ty].inner;\n\n        let mut topology = output.topology;\n        // Max, max override, type\n        let mut vertex_info = (0, None, null_type);\n        let mut primitive_info = (0, None, null_type);\n\n        match r#type {\n            &crate::TypeInner::Struct { ref members, .. } => {\n                let mut builtins = crate::FastHashSet::default();\n                for member in members {\n                    match member.binding {\n                        Some(crate::Binding::BuiltIn(crate::BuiltIn::VertexCount)) => {\n                            // Must have type u32\n                            if self.types[member.ty].inner.scalar() != Some(crate::Scalar::U32) {\n                                error.set(EntryPointError::BadMeshOutputVariableField);\n                            }\n                            // Each builtin should only occur once\n                            if builtins.contains(&crate::BuiltIn::VertexCount) {\n                                error.set(EntryPointError::BadMeshOutputVariableType);\n                            }\n                            builtins.insert(crate::BuiltIn::VertexCount);\n                        }\n                        Some(crate::Binding::BuiltIn(crate::BuiltIn::PrimitiveCount)) => {\n                            // Must have type u32\n                            if self.types[member.ty].inner.scalar() != Some(crate::Scalar::U32) {\n                                error.set(EntryPointError::BadMeshOutputVariableField);\n                            }\n                            // Each builtin should only occur once\n                            if builtins.contains(&crate::BuiltIn::PrimitiveCount) {\n                                error.set(EntryPointError::BadMeshOutputVariableType);\n                            }\n                            builtins.insert(crate::BuiltIn::PrimitiveCount);\n                        }\n                        Some(crate::Binding::BuiltIn(\n                            crate::BuiltIn::Vertices | crate::BuiltIn::Primitives,\n                        )) => {\n                            let ty = &self.types[member.ty].inner;\n                            // Analyze the array type to determine size and vertex/primitive type\n                            let (a, b, c) = match ty {\n                                &crate::TypeInner::Array { base, size, .. } => {\n                                    let ty = base;\n                                    let (max, max_override) = match size {\n                                        crate::ArraySize::Constant(a) => (a.get(), None),\n                                        crate::ArraySize::Pending(o) => (0, Some(o)),\n                                        crate::ArraySize::Dynamic => {\n                                            error.set(EntryPointError::BadMeshOutputVariableField);\n                                            (0, None)\n                                        }\n                                    };\n                                    (max, max_override, ty)\n                                }\n                                _ => {\n                                    error.set(EntryPointError::BadMeshOutputVariableField);\n                                    (0, None, null_type)\n                                }\n                            };\n                            if matches!(\n                                member.binding,\n                                Some(crate::Binding::BuiltIn(crate::BuiltIn::Primitives))\n                            ) {\n                                // Primitives require special analysis to determine topology\n                                primitive_info = (a, b, c);\n                                match self.types[c].inner {\n                                    crate::TypeInner::Struct { ref members, .. } => {\n                                        for member in members {\n                                            match member.binding {\n                                                Some(crate::Binding::BuiltIn(\n                                                    crate::BuiltIn::PointIndex,\n                                                )) => {\n                                                    topology = crate::MeshOutputTopology::Points;\n                                                }\n                                                Some(crate::Binding::BuiltIn(\n                                                    crate::BuiltIn::LineIndices,\n                                                )) => {\n                                                    topology = crate::MeshOutputTopology::Lines;\n                                                }\n                                                Some(crate::Binding::BuiltIn(\n                                                    crate::BuiltIn::TriangleIndices,\n                                                )) => {\n                                                    topology = crate::MeshOutputTopology::Triangles;\n                                                }\n                                                _ => (),\n                                            }\n                                        }\n                                    }\n                                    _ => (),\n                                }\n                                // Each builtin should only occur once\n                                if builtins.contains(&crate::BuiltIn::Primitives) {\n                                    error.set(EntryPointError::BadMeshOutputVariableType);\n                                }\n                                builtins.insert(crate::BuiltIn::Primitives);\n                            } else {\n                                vertex_info = (a, b, c);\n                                // Each builtin should only occur once\n                                if builtins.contains(&crate::BuiltIn::Vertices) {\n                                    error.set(EntryPointError::BadMeshOutputVariableType);\n                                }\n                                builtins.insert(crate::BuiltIn::Vertices);\n                            }\n                        }\n                        _ => error.set(EntryPointError::BadMeshOutputVariableType),\n                    }\n                }\n                output = crate::MeshStageInfo {\n                    topology,\n                    max_vertices: vertex_info.0,\n                    max_vertices_override: None,\n                    vertex_output_type: vertex_info.2,\n                    max_primitives: primitive_info.0,\n                    max_primitives_override: None,\n                    primitive_output_type: primitive_info.2,\n                    ..output\n                }\n            }\n            _ => error.set(EntryPointError::BadMeshOutputVariableType),\n        }\n        (\n            output,\n            [vertex_info.1, primitive_info.1],\n            error\n                .inner\n                .map(|a| a.with_span_handle(self.global_variables[gv].ty, &self.types)),\n        )\n    }\n\n    pub fn uses_mesh_shaders(&self) -> bool {\n        let binding_uses_mesh = |b: &crate::Binding| {\n            matches!(\n                b,\n                crate::Binding::BuiltIn(\n                    crate::BuiltIn::MeshTaskSize\n                        | crate::BuiltIn::CullPrimitive\n                        | crate::BuiltIn::PointIndex\n                        | crate::BuiltIn::LineIndices\n                        | crate::BuiltIn::TriangleIndices\n                        | crate::BuiltIn::VertexCount\n                        | crate::BuiltIn::Vertices\n                        | crate::BuiltIn::PrimitiveCount\n                        | crate::BuiltIn::Primitives,\n                ) | crate::Binding::Location {\n                    per_primitive: true,\n                    ..\n                }\n            )\n        };\n        for (_, ty) in self.types.iter() {\n            match ty.inner {\n                crate::TypeInner::Struct { ref members, .. } => {\n                    for binding in members.iter().filter_map(|m| m.binding.as_ref()) {\n                        if binding_uses_mesh(binding) {\n                            return true;\n                        }\n                    }\n                }\n                _ => (),\n            }\n        }\n        for ep in &self.entry_points {\n            if matches!(\n                ep.stage,\n                crate::ShaderStage::Mesh | crate::ShaderStage::Task\n            ) {\n                return true;\n            }\n            for binding in ep\n                .function\n                .arguments\n                .iter()\n                .filter_map(|arg| arg.binding.as_ref())\n                .chain(\n                    ep.function\n                        .result\n                        .iter()\n                        .filter_map(|res| res.binding.as_ref()),\n                )\n            {\n                if binding_uses_mesh(binding) {\n                    return true;\n                }\n            }\n        }\n        if self\n            .global_variables\n            .iter()\n            .any(|gv| gv.1.space == crate::AddressSpace::TaskPayload)\n        {\n            return true;\n        }\n        false\n    }\n}\n\nimpl crate::MeshOutputTopology {\n    pub const fn to_builtin(self) -> crate::BuiltIn {\n        match self {\n            Self::Points => crate::BuiltIn::PointIndex,\n            Self::Lines => crate::BuiltIn::LineIndices,\n            Self::Triangles => crate::BuiltIn::TriangleIndices,\n        }\n    }\n}\n\nimpl crate::AddressSpace {\n    pub const fn is_workgroup_like(self) -> bool {\n        matches!(self, Self::WorkGroup | Self::TaskPayload)\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/namer.rs",
    "content": "use alloc::{\n    borrow::Cow,\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\n\nuse crate::{\n    arena::Handle,\n    proc::{keyword_set::CaseInsensitiveKeywordSet, KeywordSet},\n    FastHashMap,\n};\n\npub type EntryPointIndex = u16;\nconst SEPARATOR: char = '_';\n\n/// A component of a lowered external texture.\n///\n/// Whereas the WGSL backend implements [`ImageClass::External`]\n/// images directly, most other Naga backends lower them to a\n/// collection of ordinary textures that represent individual planes\n/// (as received from a video decoder, perhaps), together with a\n/// struct of parameters saying how they should be cropped, sampled,\n/// and color-converted.\n///\n/// This lowering means that individual globals and function\n/// parameters in Naga IR must be split out by the backends into\n/// collections of globals and parameters of simpler types.\n///\n/// A value of this enum serves as a name key for one specific\n/// component in the lowered representation of an external texture.\n/// That is, these keys are for variables/parameters that do not exist\n/// in the Naga IR, only in its lowered form.\n///\n/// [`ImageClass::External`]: crate::ir::ImageClass::External\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub enum ExternalTextureNameKey {\n    Plane(usize),\n    Params,\n}\n\nimpl ExternalTextureNameKey {\n    const ALL: &[(&str, ExternalTextureNameKey)] = &[\n        (\"_plane0\", ExternalTextureNameKey::Plane(0)),\n        (\"_plane1\", ExternalTextureNameKey::Plane(1)),\n        (\"_plane2\", ExternalTextureNameKey::Plane(2)),\n        (\"_params\", ExternalTextureNameKey::Params),\n    ];\n}\n\n#[derive(Debug, Eq, Hash, PartialEq)]\npub enum NameKey {\n    Constant(Handle<crate::Constant>),\n    Override(Handle<crate::Override>),\n    GlobalVariable(Handle<crate::GlobalVariable>),\n    Type(Handle<crate::Type>),\n    StructMember(Handle<crate::Type>, u32),\n    Function(Handle<crate::Function>),\n    FunctionArgument(Handle<crate::Function>, u32),\n    FunctionLocal(Handle<crate::Function>, Handle<crate::LocalVariable>),\n\n    /// A local variable used by ReadZeroSkipWrite bounds-check policy\n    /// when it needs to produce a pointer-typed result for an OOB access.\n    /// These are unique per accessed type, so the second element is a\n    /// type handle. See docs for [`crate::back::msl`].\n    FunctionOobLocal(Handle<crate::Function>, Handle<crate::Type>),\n\n    EntryPoint(EntryPointIndex),\n    EntryPointLocal(EntryPointIndex, Handle<crate::LocalVariable>),\n    EntryPointArgument(EntryPointIndex, u32),\n\n    /// Entry point version of `FunctionOobLocal`.\n    EntryPointOobLocal(EntryPointIndex, Handle<crate::Type>),\n\n    /// A global variable holding a component of a lowered external texture.\n    ///\n    /// See [`ExternalTextureNameKey`] for details.\n    ExternalTextureGlobalVariable(Handle<crate::GlobalVariable>, ExternalTextureNameKey),\n\n    /// A function argument holding a component of a lowered external\n    /// texture.\n    ///\n    /// See [`ExternalTextureNameKey`] for details.\n    ExternalTextureFunctionArgument(Handle<crate::Function>, u32, ExternalTextureNameKey),\n}\n\n/// This processor assigns names to all the things in a module\n/// that may need identifiers in a textual backend.\n#[derive(Default)]\npub struct Namer {\n    /// The last numeric suffix used for each base name. Zero means \"no suffix\".\n    unique: FastHashMap<String, u32>,\n    keywords: &'static KeywordSet,\n    builtin_identifiers: &'static KeywordSet,\n    keywords_case_insensitive: &'static CaseInsensitiveKeywordSet,\n    reserved_prefixes: Vec<&'static str>,\n}\n\nimpl Namer {\n    /// Return a form of `string` suitable for use as the base of an identifier.\n    ///\n    /// - Drop leading digits.\n    /// - Retain only alphanumeric and `_` characters.\n    /// - Avoid prefixes in [`Namer::reserved_prefixes`].\n    /// - Replace consecutive `_` characters with a single `_` character.\n    ///\n    /// The return value is a valid identifier prefix in all of Naga's output languages,\n    /// and it never ends with a `SEPARATOR` character.\n    /// It is used as a key into the unique table.\n    fn sanitize<'s>(&self, string: &'s str) -> Cow<'s, str> {\n        let string = string\n            .trim_start_matches(|c: char| c.is_numeric())\n            .trim_end_matches(SEPARATOR);\n\n        let base = if !string.is_empty()\n            && !string.contains(\"__\")\n            && string\n                .chars()\n                .all(|c: char| c.is_ascii_alphanumeric() || c == '_')\n        {\n            Cow::Borrowed(string)\n        } else {\n            let mut filtered = string.chars().fold(String::new(), |mut s, c| {\n                let c = match c {\n                    // Make several common characters in C++-ish types become snake case\n                    // separators.\n                    ':' | '<' | '>' | ',' => '_',\n                    c => c,\n                };\n                let had_underscore_at_end = s.ends_with('_');\n                if had_underscore_at_end && c == '_' {\n                    return s;\n                }\n                if c.is_ascii_alphanumeric() || c == '_' {\n                    s.push(c);\n                } else {\n                    use core::fmt::Write as _;\n                    if !s.is_empty() && !had_underscore_at_end {\n                        s.push('_');\n                    }\n                    write!(s, \"u{:04x}_\", c as u32).unwrap();\n                }\n                s\n            });\n            let stripped_len = filtered.trim_end_matches(SEPARATOR).len();\n            filtered.truncate(stripped_len);\n            if filtered.is_empty() {\n                filtered.push_str(\"unnamed\");\n            } else if filtered.starts_with(|c: char| c.is_ascii_digit()) {\n                unreachable!(\n                    \"internal error: invalid identifier starting with ASCII digit {:?}\",\n                    filtered.chars().nth(0)\n                )\n            }\n            Cow::Owned(filtered)\n        };\n\n        for prefix in &self.reserved_prefixes {\n            if base.starts_with(prefix) {\n                return format!(\"gen_{base}\").into();\n            }\n        }\n\n        base\n    }\n\n    /// Return a new identifier based on `label_raw`.\n    ///\n    /// The result:\n    /// - is a valid identifier even if `label_raw` is not\n    /// - conflicts with no keywords listed in `Namer::keywords`, and\n    /// - is different from any identifier previously constructed by this\n    ///   `Namer`.\n    ///\n    /// Guarantee uniqueness by applying a numeric suffix when necessary. If `label_raw`\n    /// itself ends with digits, separate them from the suffix with an underscore.\n    pub fn call(&mut self, label_raw: &str) -> String {\n        use core::fmt::Write as _; // for write!-ing to Strings\n\n        let base = self.sanitize(label_raw);\n        debug_assert!(!base.is_empty() && !base.ends_with(SEPARATOR));\n\n        // This would seem to be a natural place to use `HashMap::entry`. However, `entry`\n        // requires an owned key, and we'd like to avoid heap-allocating strings we're\n        // just going to throw away. The approach below double-hashes only when we create\n        // a new entry, in which case the heap allocation of the owned key was more\n        // expensive anyway.\n        match self.unique.get_mut(base.as_ref()) {\n            Some(count) => {\n                *count += 1;\n                // Add the suffix. This may fit in base's existing allocation.\n                let mut suffixed = base.into_owned();\n                write!(suffixed, \"{}{}\", SEPARATOR, *count).unwrap();\n                suffixed\n            }\n            None => {\n                let mut suffixed = base.to_string();\n                if base.ends_with(char::is_numeric)\n                    || self.keywords.contains(base.as_ref())\n                    || self.keywords_case_insensitive.contains(base.as_ref())\n                    || self.builtin_identifiers.contains(base.as_ref())\n                {\n                    suffixed.push(SEPARATOR);\n                }\n                debug_assert!(!self.keywords.contains(&suffixed));\n                // `self.unique` wants to own its keys. This allocates only if we haven't\n                // already done so earlier.\n                self.unique.insert(base.into_owned(), 0);\n                suffixed\n            }\n        }\n    }\n\n    pub fn call_or(&mut self, label: &Option<String>, fallback: &str) -> String {\n        self.call(match *label {\n            Some(ref name) => name,\n            None => fallback,\n        })\n    }\n\n    /// Enter a local namespace for things like structs.\n    ///\n    /// Struct member names only need to be unique amongst themselves, not\n    /// globally. This function temporarily establishes a fresh, empty naming\n    /// context for the duration of the call to `body`.\n    fn namespace(&mut self, capacity: usize, body: impl FnOnce(&mut Self)) {\n        let empty_unique = FastHashMap::with_capacity_and_hasher(capacity, Default::default());\n        let saved_unique = core::mem::replace(&mut self.unique, empty_unique);\n        let saved_builtin_identifiers = core::mem::take(&mut self.builtin_identifiers);\n        body(self);\n        self.unique = saved_unique;\n        self.builtin_identifiers = saved_builtin_identifiers;\n    }\n\n    pub fn reset(\n        &mut self,\n        module: &crate::Module,\n        reserved_keywords: &'static KeywordSet,\n        builtin_identifiers: &'static KeywordSet,\n        reserved_keywords_case_insensitive: &'static CaseInsensitiveKeywordSet,\n        reserved_prefixes: &[&'static str],\n        output: &mut FastHashMap<NameKey, String>,\n    ) {\n        self.reserved_prefixes.clear();\n        self.reserved_prefixes.extend(reserved_prefixes.iter());\n\n        self.unique.clear();\n        self.keywords = reserved_keywords;\n        self.builtin_identifiers = builtin_identifiers;\n        self.keywords_case_insensitive = reserved_keywords_case_insensitive;\n\n        // Choose fallback names for anonymous entry point return types.\n        let mut entrypoint_type_fallbacks = FastHashMap::default();\n        for ep in &module.entry_points {\n            if let Some(ref result) = ep.function.result {\n                if let crate::Type {\n                    name: None,\n                    inner: crate::TypeInner::Struct { .. },\n                } = module.types[result.ty]\n                {\n                    let label = match ep.stage {\n                        crate::ShaderStage::Vertex => \"VertexOutput\",\n                        crate::ShaderStage::Fragment => \"FragmentOutput\",\n                        crate::ShaderStage::Compute => \"ComputeOutput\",\n                        crate::ShaderStage::Task\n                        | crate::ShaderStage::Mesh\n                        | crate::ShaderStage::RayGeneration\n                        | crate::ShaderStage::ClosestHit\n                        | crate::ShaderStage::AnyHit\n                        | crate::ShaderStage::Miss => unreachable!(),\n                    };\n                    entrypoint_type_fallbacks.insert(result.ty, label);\n                }\n            }\n        }\n\n        let mut temp = String::new();\n\n        for (ty_handle, ty) in module.types.iter() {\n            // If the type is anonymous, check `entrypoint_types` for\n            // something better than just `\"type\"`.\n            let raw_label = match ty.name {\n                Some(ref given_name) => given_name.as_str(),\n                None => entrypoint_type_fallbacks\n                    .get(&ty_handle)\n                    .cloned()\n                    .unwrap_or(\"type\"),\n            };\n            let ty_name = self.call(raw_label);\n            output.insert(NameKey::Type(ty_handle), ty_name);\n\n            if let crate::TypeInner::Struct { ref members, .. } = ty.inner {\n                // struct members have their own namespace, because access is always prefixed\n                self.namespace(members.len(), |namer| {\n                    for (index, member) in members.iter().enumerate() {\n                        let name = namer.call_or(&member.name, \"member\");\n                        output.insert(NameKey::StructMember(ty_handle, index as u32), name);\n                    }\n                })\n            }\n        }\n\n        for (ep_index, ep) in module.entry_points.iter().enumerate() {\n            let ep_name = self.call(&ep.name);\n            output.insert(NameKey::EntryPoint(ep_index as _), ep_name);\n            for (index, arg) in ep.function.arguments.iter().enumerate() {\n                let name = self.call_or(&arg.name, \"param\");\n                output.insert(\n                    NameKey::EntryPointArgument(ep_index as _, index as u32),\n                    name,\n                );\n            }\n            for (handle, var) in ep.function.local_variables.iter() {\n                let name = self.call_or(&var.name, \"local\");\n                output.insert(NameKey::EntryPointLocal(ep_index as _, handle), name);\n            }\n        }\n\n        for (fun_handle, fun) in module.functions.iter() {\n            let fun_name = self.call_or(&fun.name, \"function\");\n            output.insert(NameKey::Function(fun_handle), fun_name);\n            for (index, arg) in fun.arguments.iter().enumerate() {\n                let name = self.call_or(&arg.name, \"param\");\n                output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name);\n\n                if matches!(\n                    module.types[arg.ty].inner,\n                    crate::TypeInner::Image {\n                        class: crate::ImageClass::External,\n                        ..\n                    }\n                ) {\n                    let base = arg.name.as_deref().unwrap_or(\"param\");\n                    for &(suffix, ext_key) in ExternalTextureNameKey::ALL {\n                        let name = self.call(&format!(\"{base}_{suffix}\"));\n                        output.insert(\n                            NameKey::ExternalTextureFunctionArgument(\n                                fun_handle,\n                                index as u32,\n                                ext_key,\n                            ),\n                            name,\n                        );\n                    }\n                }\n            }\n            for (handle, var) in fun.local_variables.iter() {\n                let name = self.call_or(&var.name, \"local\");\n                output.insert(NameKey::FunctionLocal(fun_handle, handle), name);\n            }\n        }\n\n        for (handle, var) in module.global_variables.iter() {\n            let name = self.call_or(&var.name, \"global\");\n            output.insert(NameKey::GlobalVariable(handle), name);\n\n            if matches!(\n                module.types[var.ty].inner,\n                crate::TypeInner::Image {\n                    class: crate::ImageClass::External,\n                    ..\n                }\n            ) {\n                let base = var.name.as_deref().unwrap_or(\"global\");\n                for &(suffix, ext_key) in ExternalTextureNameKey::ALL {\n                    let name = self.call(&format!(\"{base}_{suffix}\"));\n                    output.insert(\n                        NameKey::ExternalTextureGlobalVariable(handle, ext_key),\n                        name,\n                    );\n                }\n            }\n        }\n\n        for (handle, constant) in module.constants.iter() {\n            let label = match constant.name {\n                Some(ref name) => name,\n                None => {\n                    use core::fmt::Write;\n                    // Try to be more descriptive about the constant values\n                    temp.clear();\n                    write!(temp, \"const_{}\", output[&NameKey::Type(constant.ty)]).unwrap();\n                    &temp\n                }\n            };\n            let name = self.call(label);\n            output.insert(NameKey::Constant(handle), name);\n        }\n\n        for (handle, override_) in module.overrides.iter() {\n            let label = match override_.name {\n                Some(ref name) => name,\n                None => {\n                    use core::fmt::Write;\n                    // Try to be more descriptive about the override values\n                    temp.clear();\n                    write!(temp, \"override_{}\", output[&NameKey::Type(override_.ty)]).unwrap();\n                    &temp\n                }\n            };\n            let name = self.call(label);\n            output.insert(NameKey::Override(handle), name);\n        }\n    }\n}\n\n#[test]\nfn test() {\n    let mut namer = Namer::default();\n    assert_eq!(namer.call(\"x\"), \"x\");\n    assert_eq!(namer.call(\"x\"), \"x_1\");\n    assert_eq!(namer.call(\"x1\"), \"x1_\");\n    assert_eq!(namer.call(\"__x\"), \"_x\");\n    assert_eq!(namer.call(\"1___x\"), \"_x_1\");\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/any_overload_set.rs",
    "content": "//! Dynamically dispatched [`OverloadSet`]s.\n\nuse crate::common::DiagnosticDebug;\nuse crate::ir;\nuse crate::proc::overloads::{list, regular, OverloadSet, Rule};\nuse crate::proc::{GlobalCtx, TypeResolution};\n\nuse alloc::vec::Vec;\nuse core::fmt;\n\nmacro_rules! define_any_overload_set {\n    { $( $module:ident :: $name:ident, )* } => {\n        /// An [`OverloadSet`] that dynamically dispatches to concrete implementations.\n        #[derive(Clone)]\n        pub(in crate::proc::overloads) enum AnyOverloadSet {\n            $(\n                $name ( $module :: $name ),\n            )*\n        }\n\n        $(\n            impl From<$module::$name> for AnyOverloadSet {\n                fn from(concrete: $module::$name) -> Self {\n                    AnyOverloadSet::$name(concrete)\n                }\n            }\n        )*\n\n        impl OverloadSet for AnyOverloadSet {\n            fn is_empty(&self) -> bool {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.is_empty(),\n                    )*\n                }\n            }\n\n            fn min_arguments(&self) -> usize {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.min_arguments(),\n                    )*\n                }\n            }\n\n            fn max_arguments(&self) -> usize {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.max_arguments(),\n                    )*\n                }\n            }\n\n            fn arg(\n                &self,\n                i: usize,\n                ty: &ir::TypeInner,\n                types: &crate::UniqueArena<ir::Type>,\n            ) -> Self {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => AnyOverloadSet::$name(x.arg(i, ty, types)),\n                    )*\n                }\n            }\n\n            fn concrete_only(self, types: &crate::UniqueArena<ir::Type>) -> Self {\n                match self {\n                    $(\n                        AnyOverloadSet::$name(x) => AnyOverloadSet::$name(x.concrete_only(types)),\n                    )*\n                }\n            }\n\n            fn most_preferred(&self) -> Rule {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.most_preferred(),\n                    )*\n                }\n            }\n\n            fn overload_list(&self, gctx: &GlobalCtx<'_>) -> Vec<Rule> {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.overload_list(gctx),\n                    )*\n                }\n            }\n\n            fn allowed_args(&self, i: usize, gctx: &GlobalCtx<'_>) -> Vec<TypeResolution> {\n                match *self {\n                    $(\n                        AnyOverloadSet::$name(ref x) => x.allowed_args(i, gctx),\n                    )*\n                }\n            }\n\n            fn for_debug(&self, types: &crate::UniqueArena<ir::Type>) -> impl fmt::Debug {\n                DiagnosticDebug((self, types))\n            }\n        }\n\n        impl fmt::Debug for DiagnosticDebug<(&AnyOverloadSet, &crate::UniqueArena<ir::Type>)> {\n            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n                let (set, types) = self.0;\n                match *set {\n                    $(\n                        AnyOverloadSet::$name(ref x) => DiagnosticDebug((x, types)).fmt(f),\n                    )*\n                }\n            }\n        }\n    }\n}\n\ndefine_any_overload_set! {\n    list::List,\n    regular::Regular,\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/constructor_set.rs",
    "content": "//! A set of type constructors, represented as a bitset.\n\nuse crate::ir;\nuse crate::proc::overloads::one_bits_iter::OneBitsIter;\n\nbitflags::bitflags! {\n    /// A set of type constructors.\n    #[derive(Copy, Clone, Debug, PartialEq)]\n    pub(crate) struct ConstructorSet: u16 {\n        const SCALAR = 1 << 0;\n        const VEC2 = 1 << 1;\n        const VEC3 = 1 << 2;\n        const VEC4 = 1 << 3;\n        const MAT2X2 = 1 << 4;\n        const MAT2X3 = 1 << 5;\n        const MAT2X4 = 1 << 6;\n        const MAT3X2 = 1 << 7;\n        const MAT3X3 = 1 << 8;\n        const MAT3X4 = 1 << 9;\n        const MAT4X2 = 1 << 10;\n        const MAT4X3 = 1 << 11;\n        const MAT4X4 = 1 << 12;\n\n        const VECN = Self::VEC2.bits()\n            | Self::VEC3.bits()\n            | Self::VEC4.bits();\n    }\n}\n\nimpl ConstructorSet {\n    /// Return the single-member set containing `inner`'s constructor.\n    pub const fn singleton(inner: &ir::TypeInner) -> ConstructorSet {\n        use ir::TypeInner as Ti;\n        use ir::VectorSize as Vs;\n        match *inner {\n            Ti::Scalar(_) => Self::SCALAR,\n            Ti::Vector { size, scalar: _ } => match size {\n                Vs::Bi => Self::VEC2,\n                Vs::Tri => Self::VEC3,\n                Vs::Quad => Self::VEC4,\n            },\n            Ti::Matrix {\n                columns,\n                rows,\n                scalar: _,\n            } => match (columns, rows) {\n                (Vs::Bi, Vs::Bi) => Self::MAT2X2,\n                (Vs::Bi, Vs::Tri) => Self::MAT2X3,\n                (Vs::Bi, Vs::Quad) => Self::MAT2X4,\n                (Vs::Tri, Vs::Bi) => Self::MAT3X2,\n                (Vs::Tri, Vs::Tri) => Self::MAT3X3,\n                (Vs::Tri, Vs::Quad) => Self::MAT3X4,\n                (Vs::Quad, Vs::Bi) => Self::MAT4X2,\n                (Vs::Quad, Vs::Tri) => Self::MAT4X3,\n                (Vs::Quad, Vs::Quad) => Self::MAT4X4,\n            },\n            _ => Self::empty(),\n        }\n    }\n\n    pub const fn is_singleton(self) -> bool {\n        self.bits().is_power_of_two()\n    }\n\n    /// Return an iterator over this set's members.\n    ///\n    /// Members are produced as singleton, in order from most general to least.\n    pub fn members(self) -> impl Iterator<Item = ConstructorSet> {\n        OneBitsIter::new(self.bits() as u64).map(|bit| Self::from_bits(bit as u16).unwrap())\n    }\n\n    /// Return the size of the sole element of `self`.\n    ///\n    /// # Panics\n    ///\n    /// Panic if `self` is not a singleton.\n    pub fn size(self) -> ConstructorSize {\n        use ir::VectorSize as Vs;\n        use ConstructorSize as Cs;\n\n        match self {\n            ConstructorSet::SCALAR => Cs::Scalar,\n            ConstructorSet::VEC2 => Cs::Vector(Vs::Bi),\n            ConstructorSet::VEC3 => Cs::Vector(Vs::Tri),\n            ConstructorSet::VEC4 => Cs::Vector(Vs::Quad),\n            ConstructorSet::MAT2X2 => Cs::Matrix {\n                columns: Vs::Bi,\n                rows: Vs::Bi,\n            },\n            ConstructorSet::MAT2X3 => Cs::Matrix {\n                columns: Vs::Bi,\n                rows: Vs::Tri,\n            },\n            ConstructorSet::MAT2X4 => Cs::Matrix {\n                columns: Vs::Bi,\n                rows: Vs::Quad,\n            },\n            ConstructorSet::MAT3X2 => Cs::Matrix {\n                columns: Vs::Tri,\n                rows: Vs::Bi,\n            },\n            ConstructorSet::MAT3X3 => Cs::Matrix {\n                columns: Vs::Tri,\n                rows: Vs::Tri,\n            },\n            ConstructorSet::MAT3X4 => Cs::Matrix {\n                columns: Vs::Tri,\n                rows: Vs::Quad,\n            },\n            ConstructorSet::MAT4X2 => Cs::Matrix {\n                columns: Vs::Quad,\n                rows: Vs::Bi,\n            },\n            ConstructorSet::MAT4X3 => Cs::Matrix {\n                columns: Vs::Quad,\n                rows: Vs::Tri,\n            },\n            ConstructorSet::MAT4X4 => Cs::Matrix {\n                columns: Vs::Quad,\n                rows: Vs::Quad,\n            },\n            _ => unreachable!(\"ConstructorSet was not a singleton\"),\n        }\n    }\n}\n\n/// The sizes a member of [`ConstructorSet`] might have.\n#[derive(Clone, Copy)]\npub enum ConstructorSize {\n    /// The constructor is [`SCALAR`].\n    ///\n    /// [`SCALAR`]: ConstructorSet::SCALAR\n    Scalar,\n\n    /// The constructor is `VECN` for some `N`.\n    Vector(ir::VectorSize),\n\n    /// The constructor is `MATCXR` for some `C` and `R`.\n    Matrix {\n        columns: ir::VectorSize,\n        rows: ir::VectorSize,\n    },\n}\n\nimpl ConstructorSize {\n    /// Construct a [`TypeInner`] for a type with this size and the given `scalar`.\n    ///\n    /// [`TypeInner`]: ir::TypeInner\n    pub const fn to_inner(self, scalar: ir::Scalar) -> ir::TypeInner {\n        match self {\n            Self::Scalar => ir::TypeInner::Scalar(scalar),\n            Self::Vector(size) => ir::TypeInner::Vector { size, scalar },\n            Self::Matrix { columns, rows } => ir::TypeInner::Matrix {\n                columns,\n                rows,\n                scalar,\n            },\n        }\n    }\n}\n\nmacro_rules! constructor_set {\n    ( $( $constr:ident )|* ) => {\n        {\n            use $crate::proc::overloads::constructor_set::ConstructorSet;\n            ConstructorSet::empty()\n                $(\n                    .union(ConstructorSet::$constr)\n                )*\n        }\n    }\n}\n\npub(in crate::proc::overloads) use constructor_set;\n"
  },
  {
    "path": "naga/src/proc/overloads/list.rs",
    "content": "//! An [`OverloadSet`] represented as a vector of rules.\n//!\n//! [`OverloadSet`]: crate::proc::overloads::OverloadSet\n\nuse crate::common::{DiagnosticDebug, ForDebug, ForDebugWithTypes};\nuse crate::ir;\nuse crate::proc::overloads::one_bits_iter::OneBitsIter;\nuse crate::proc::overloads::Rule;\nuse crate::proc::{GlobalCtx, TypeResolution};\n\nuse alloc::rc::Rc;\nuse alloc::vec::Vec;\nuse core::fmt;\n\n/// A simple list of overloads.\n///\n/// Note that this type is not quite as general as it looks, in that\n/// the implementation of `most_preferred` doesn't work for arbitrary\n/// lists of overloads. See the documentation for [`List::rules`] for\n/// details.\n#[derive(Clone)]\npub(in crate::proc::overloads) struct List {\n    /// A bitmask of which elements of `rules` are included in the set.\n    members: u64,\n\n    /// A list of type rules that are members of the set.\n    ///\n    /// These must be listed in order such that every rule in the list\n    /// is always more preferred than all subsequent rules in the\n    /// list. If there is no such arrangement of rules, then you\n    /// cannot use `List` to represent the overload set.\n    rules: Rc<Vec<Rule>>,\n}\n\nimpl List {\n    pub(in crate::proc::overloads) fn from_rules(rules: Vec<Rule>) -> List {\n        List {\n            members: len_to_full_mask(rules.len()),\n            rules: Rc::new(rules),\n        }\n    }\n\n    fn members(&self) -> impl Iterator<Item = (u64, &Rule)> {\n        OneBitsIter::new(self.members).map(|mask| {\n            let index = mask.trailing_zeros() as usize;\n            (mask, &self.rules[index])\n        })\n    }\n\n    fn filter<F>(&self, mut pred: F) -> List\n    where\n        F: FnMut(&Rule) -> bool,\n    {\n        let mut filtered_members = 0;\n        for (mask, rule) in self.members() {\n            if pred(rule) {\n                filtered_members |= mask;\n            }\n        }\n\n        List {\n            members: filtered_members,\n            rules: self.rules.clone(),\n        }\n    }\n}\n\nimpl crate::proc::overloads::OverloadSet for List {\n    fn is_empty(&self) -> bool {\n        self.members == 0\n    }\n\n    fn min_arguments(&self) -> usize {\n        self.members()\n            .fold(None, |best, (_, rule)| {\n                // This is different from `max_arguments` because\n                // `<Option as PartialOrd>` doesn't work the way we'd like.\n                let len = rule.arguments.len();\n                Some(match best {\n                    Some(best) => core::cmp::max(best, len),\n                    None => len,\n                })\n            })\n            .unwrap()\n    }\n\n    fn max_arguments(&self) -> usize {\n        self.members()\n            .fold(None, |n, (_, rule)| {\n                core::cmp::max(n, Some(rule.arguments.len()))\n            })\n            .unwrap()\n    }\n\n    fn arg(&self, i: usize, arg_ty: &ir::TypeInner, types: &crate::UniqueArena<ir::Type>) -> Self {\n        log::debug!(\"arg {i} of type {:?}\", arg_ty.for_debug(types));\n        self.filter(|rule| {\n            if log::log_enabled!(log::Level::Debug) {\n                log::debug!(\"    considering rule {:?}\", rule.for_debug(types));\n                match rule.arguments.get(i) {\n                    Some(rule_ty) => {\n                        let rule_ty = rule_ty.inner_with(types);\n                        if arg_ty.non_struct_equivalent(rule_ty, types) {\n                            log::debug!(\"    types are equivalent\");\n                        } else {\n                            match arg_ty.automatically_converts_to(rule_ty, types) {\n                                Some((from, to)) => {\n                                    log::debug!(\n                                        \"    converts automatically from {:?} to {:?}\",\n                                        from.for_debug(),\n                                        to.for_debug()\n                                    );\n                                }\n                                None => {\n                                    log::debug!(\"    no conversion possible\");\n                                }\n                            }\n                        }\n                    }\n                    None => {\n                        log::debug!(\"    argument index {i} out of bounds\");\n                    }\n                }\n            }\n            rule.arguments.get(i).is_some_and(|rule_ty| {\n                let rule_ty = rule_ty.inner_with(types);\n                arg_ty.non_struct_equivalent(rule_ty, types)\n                    || arg_ty.automatically_converts_to(rule_ty, types).is_some()\n            })\n        })\n    }\n\n    fn concrete_only(self, types: &crate::UniqueArena<ir::Type>) -> Self {\n        self.filter(|rule| {\n            rule.arguments\n                .iter()\n                .all(|arg_ty| !arg_ty.inner_with(types).is_abstract(types))\n        })\n    }\n\n    fn most_preferred(&self) -> Rule {\n        // As documented for `Self::rules`, whatever rule is first is\n        // the most preferred. `OverloadSet` documents this method to\n        // panic if the set is empty.\n        let (_, rule) = self.members().next().unwrap();\n        rule.clone()\n    }\n\n    fn overload_list(&self, _gctx: &GlobalCtx<'_>) -> Vec<Rule> {\n        self.members().map(|(_, rule)| rule.clone()).collect()\n    }\n\n    fn allowed_args(&self, i: usize, _gctx: &GlobalCtx<'_>) -> Vec<TypeResolution> {\n        self.members()\n            .map(|(_, rule)| rule.arguments[i].clone())\n            .collect()\n    }\n\n    fn for_debug(&self, types: &crate::UniqueArena<ir::Type>) -> impl fmt::Debug {\n        DiagnosticDebug((self, types))\n    }\n}\n\nconst fn len_to_full_mask(n: usize) -> u64 {\n    // This is a const function, which _sometimes_ gets called,\n    // so this lint is _sometimes_ triggered, depending on feature set.\n    #[expect(clippy::allow_attributes)]\n    #[allow(clippy::panic)]\n    if n >= 64 {\n        panic!(\"List::rules can only hold up to 63 rules\");\n    }\n\n    (1_u64 << n) - 1\n}\n\nimpl ForDebugWithTypes for &List {}\n\nimpl fmt::Debug for DiagnosticDebug<(&List, &crate::UniqueArena<ir::Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (list, types) = self.0;\n\n        f.debug_list()\n            .entries(list.members().map(|(_mask, rule)| rule.for_debug(types)))\n            .finish()\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/mathfunction.rs",
    "content": "//! Overload sets for [`ir::MathFunction`].\n\nuse crate::proc::overloads::any_overload_set::AnyOverloadSet;\nuse crate::proc::overloads::list::List;\nuse crate::proc::overloads::regular::regular;\nuse crate::proc::overloads::utils::{\n    float_scalars, float_scalars_unimplemented_abstract, list, pairs, rule, scalar_or_vecn, triples,\n};\nuse crate::proc::overloads::OverloadSet;\nuse crate::proc::type_methods::{concrete_int_scalars, vector_sizes};\n\nuse crate::ir;\n\nimpl ir::MathFunction {\n    pub fn overloads(self) -> impl OverloadSet {\n        use ir::MathFunction as Mf;\n\n        let set: AnyOverloadSet = match self {\n            // Component-wise unary numeric operations\n            Mf::Abs | Mf::Sign => regular!(1, SCALAR|VECN of NUMERIC).into(),\n\n            // Component-wise binary numeric operations\n            Mf::Min | Mf::Max => regular!(2, SCALAR|VECN of NUMERIC).into(),\n\n            // Component-wise ternary numeric operations\n            Mf::Clamp => regular!(3, SCALAR|VECN of NUMERIC).into(),\n\n            // Component-wise unary floating-point operations\n            Mf::Sin\n            | Mf::Cos\n            | Mf::Tan\n            | Mf::Asin\n            | Mf::Acos\n            | Mf::Atan\n            | Mf::Sinh\n            | Mf::Cosh\n            | Mf::Tanh\n            | Mf::Asinh\n            | Mf::Acosh\n            | Mf::Atanh\n            | Mf::Saturate\n            | Mf::Radians\n            | Mf::Degrees\n            | Mf::Ceil\n            | Mf::Floor\n            | Mf::Round\n            | Mf::Fract\n            | Mf::Trunc\n            | Mf::Exp\n            | Mf::Exp2\n            | Mf::Log\n            | Mf::Log2\n            | Mf::Sqrt\n            | Mf::InverseSqrt => regular!(1, SCALAR|VECN of FLOAT).into(),\n\n            // Component-wise binary floating-point operations\n            Mf::Atan2 | Mf::Pow | Mf::Step => regular!(2, SCALAR|VECN of FLOAT).into(),\n\n            // Component-wise ternary floating-point operations\n            Mf::Fma | Mf::SmoothStep => regular!(3, SCALAR|VECN of FLOAT).into(),\n\n            // Component-wise unary concrete integer operations\n            Mf::CountTrailingZeros\n            | Mf::CountLeadingZeros\n            | Mf::CountOneBits\n            | Mf::ReverseBits\n            | Mf::FirstTrailingBit\n            | Mf::FirstLeadingBit => regular!(1, SCALAR|VECN of CONCRETE_INTEGER).into(),\n\n            // Packing functions\n            Mf::Pack4x8snorm | Mf::Pack4x8unorm => regular!(1, VEC4 of F32 -> U32).into(),\n            Mf::Pack2x16snorm | Mf::Pack2x16unorm | Mf::Pack2x16float => {\n                regular!(1, VEC2 of F32 -> U32).into()\n            }\n            Mf::Pack4xI8 => regular!(1, VEC4 of I32 -> U32).into(),\n            Mf::Pack4xU8 => regular!(1, VEC4 of U32 -> U32).into(),\n            Mf::Pack4xI8Clamp => regular!(1, VEC4 of I32 -> U32).into(),\n            Mf::Pack4xU8Clamp => regular!(1, VEC4 of U32 -> U32).into(),\n\n            // Unpacking functions\n            Mf::Unpack4x8snorm | Mf::Unpack4x8unorm => regular!(1, SCALAR of U32 -> Vec4F).into(),\n            Mf::Unpack2x16snorm | Mf::Unpack2x16unorm | Mf::Unpack2x16float => {\n                regular!(1, SCALAR of U32 -> Vec2F).into()\n            }\n            Mf::Unpack4xI8 => regular!(1, SCALAR of U32 -> Vec4I).into(),\n            Mf::Unpack4xU8 => regular!(1, SCALAR of U32 -> Vec4U).into(),\n            Mf::Dot4I8Packed => regular!(2, SCALAR of U32 -> I32).into(),\n            Mf::Dot4U8Packed => regular!(2, SCALAR of U32 -> U32).into(),\n\n            // One-off operations\n            Mf::Dot => regular!(2, VECN of NUMERIC -> Scalar).into(),\n            Mf::Modf => regular!(1, SCALAR|VECN of FLOAT_ABSTRACT_UNIMPLEMENTED -> Modf).into(),\n            Mf::Frexp => regular!(1, SCALAR|VECN of FLOAT_ABSTRACT_UNIMPLEMENTED -> Frexp).into(),\n            Mf::Ldexp => ldexp().into(),\n            Mf::Outer => outer().into(),\n            Mf::Cross => regular!(2, VEC3 of FLOAT).into(),\n            Mf::Distance => {\n                regular!(2, SCALAR|VECN of FLOAT_ABSTRACT_UNIMPLEMENTED -> Scalar).into()\n            }\n            Mf::Length => regular!(1, SCALAR|VECN of FLOAT_ABSTRACT_UNIMPLEMENTED -> Scalar).into(),\n            Mf::Normalize => regular!(1, VECN of FLOAT_ABSTRACT_UNIMPLEMENTED).into(),\n            Mf::FaceForward => regular!(3, VECN of FLOAT_ABSTRACT_UNIMPLEMENTED).into(),\n            Mf::Reflect => regular!(2, VECN of FLOAT_ABSTRACT_UNIMPLEMENTED).into(),\n            Mf::Refract => refract().into(),\n            Mf::Mix => mix().into(),\n            Mf::Inverse => regular!(1, MAT2X2|MAT3X3|MAT4X4 of FLOAT).into(),\n            Mf::Transpose => transpose().into(),\n            Mf::Determinant => regular!(1, MAT2X2|MAT3X3|MAT4X4 of FLOAT -> Scalar).into(),\n            Mf::QuantizeToF16 => regular!(1, SCALAR|VECN of F32).into(),\n            Mf::ExtractBits => extract_bits().into(),\n            Mf::InsertBits => insert_bits().into(),\n        };\n\n        set\n    }\n}\n\nfn ldexp() -> List {\n    /// Construct the exponent scalar given the mantissa's inner.\n    fn exponent_from_mantissa(mantissa: ir::Scalar) -> ir::Scalar {\n        match mantissa.kind {\n            ir::ScalarKind::AbstractFloat => ir::Scalar::ABSTRACT_INT,\n            ir::ScalarKind::Float => ir::Scalar::I32,\n            _ => unreachable!(\"not a float scalar\"),\n        }\n    }\n\n    list(\n        // The ldexp mantissa argument can be any floating-point type.\n        float_scalars_unimplemented_abstract().flat_map(|mantissa_scalar| {\n            // The exponent type is the integer counterpart of the mantissa type.\n            let exponent_scalar = exponent_from_mantissa(mantissa_scalar);\n            // There are scalar and vector component-wise overloads.\n            scalar_or_vecn(mantissa_scalar)\n                .zip(scalar_or_vecn(exponent_scalar))\n                .map(move |(mantissa, exponent)| {\n                    let result = mantissa.clone();\n                    rule([mantissa, exponent], result)\n                })\n        }),\n    )\n}\n\nfn outer() -> List {\n    list(\n        triples(\n            vector_sizes(),\n            vector_sizes(),\n            float_scalars_unimplemented_abstract(),\n        )\n        .map(|(cols, rows, scalar)| {\n            let left = ir::TypeInner::Vector { size: cols, scalar };\n            let right = ir::TypeInner::Vector { size: rows, scalar };\n            let result = ir::TypeInner::Matrix {\n                columns: cols,\n                rows,\n                scalar,\n            };\n            rule([left, right], result)\n        }),\n    )\n}\n\nfn refract() -> List {\n    list(\n        pairs(vector_sizes(), float_scalars_unimplemented_abstract()).map(|(size, scalar)| {\n            let incident = ir::TypeInner::Vector { size, scalar };\n            let normal = incident.clone();\n            let ratio = ir::TypeInner::Scalar(scalar);\n            let result = incident.clone();\n            rule([incident, normal, ratio], result)\n        }),\n    )\n}\n\nfn transpose() -> List {\n    list(\n        triples(vector_sizes(), vector_sizes(), float_scalars()).map(|(a, b, scalar)| {\n            let input = ir::TypeInner::Matrix {\n                columns: a,\n                rows: b,\n                scalar,\n            };\n            let output = ir::TypeInner::Matrix {\n                columns: b,\n                rows: a,\n                scalar,\n            };\n            rule([input], output)\n        }),\n    )\n}\n\nfn extract_bits() -> List {\n    list(concrete_int_scalars().flat_map(|scalar| {\n        scalar_or_vecn(scalar).map(|input| {\n            let offset = ir::TypeInner::Scalar(ir::Scalar::U32);\n            let count = ir::TypeInner::Scalar(ir::Scalar::U32);\n            let output = input.clone();\n            rule([input, offset, count], output)\n        })\n    }))\n}\n\nfn insert_bits() -> List {\n    list(concrete_int_scalars().flat_map(|scalar| {\n        scalar_or_vecn(scalar).map(|input| {\n            let newbits = input.clone();\n            let offset = ir::TypeInner::Scalar(ir::Scalar::U32);\n            let count = ir::TypeInner::Scalar(ir::Scalar::U32);\n            let output = input.clone();\n            rule([input, newbits, offset, count], output)\n        })\n    }))\n}\n\nfn mix() -> List {\n    list(float_scalars().flat_map(|scalar| {\n        scalar_or_vecn(scalar).flat_map(move |input| {\n            let scalar_ratio = ir::TypeInner::Scalar(scalar);\n            [\n                rule([input.clone(), input.clone(), input.clone()], input.clone()),\n                rule([input.clone(), input.clone(), scalar_ratio], input),\n            ]\n        })\n    }))\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/mod.rs",
    "content": "/*! Overload resolution for builtin functions.\n\nThis module defines the [`OverloadSet`] trait, which provides methods the\nvalidator and typifier can use to check the types to builtin functions,\ndetermine their result types, and produce diagnostics that explain why a given\napplication is not allowed and suggest fixes.\n\nYou can call [`MathFunction::overloads`] to obtain an `impl OverloadSet`\nrepresenting the given `MathFunction`'s overloads.\n\n[`MathFunction::overloads`]: crate::ir::MathFunction::overloads\n\n*/\n\nmod constructor_set;\nmod regular;\nmod scalar_set;\n\nmod any_overload_set;\nmod list;\nmod mathfunction;\nmod one_bits_iter;\nmod rule;\nmod utils;\n\npub use rule::{Conclusion, MissingSpecialType, Rule};\n\nuse crate::ir;\nuse crate::proc::TypeResolution;\n\nuse alloc::vec::Vec;\nuse core::fmt;\n\n#[expect(rustdoc::private_intra_doc_links)]\n/// A trait for types representing of a set of Naga IR type rules.\n///\n/// Given an expression like `max(x, y)`, there are multiple type rules that\n/// could apply, depending on the types of `x` and `y`, like:\n///\n/// - `max(i32, i32) -> i32`\n/// - `max(vec4<f32>, vec4<f32>) -> vec4<f32>`\n///\n/// and so on. Borrowing WGSL's terminology, Naga calls the full set of type\n/// rules that might apply to a given expression its \"overload candidates\", or\n/// \"overloads\" for short.\n///\n/// This trait is meant to be implemented by types that represent a set of\n/// overload candidates. For example, [`MathFunction::overloads`] returns an\n/// `impl OverloadSet` describing the overloads for the given Naga IR math\n/// function. Naga's typifier, validator, and WGSL front end use this trait for\n/// their work.\n///\n/// [`MathFunction::overloads`]: ir::MathFunction::overloads\n///\n/// # Automatic conversions\n///\n/// In principle, overload sets are easy: you simply list all the overloads the\n/// function supports, and then when you're presented with a call to typecheck,\n/// you just see if the actual argument types presented in the source code match\n/// some overload from the list.\n///\n/// However, Naga supports languages like WGSL, which apply certain [automatic\n/// conversions] if necessary to make a call fit some overload's requirements.\n/// This means that the set of calls that are effectively allowed by a given set\n/// of overloads can be quite large, since any combination of automatic\n/// conversions might be applied.\n///\n/// For example, if `x` is a `u32`, and `100` is an abstract integer, then even\n/// though `max` has no overload like `max(u32, AbstractInt) -> ...`, the\n/// expression `max(x, 100)` is still allowed, because AbstractInt automatically\n/// converts to `u32`.\n///\n/// [automatic conversions]: https://gpuweb.github.io/gpuweb/wgsl/#feasible-automatic-conversion\n///\n/// # How to use `OverloadSet`\n///\n/// The general process of using an `OverloadSet` is as follows:\n///\n/// - Obtain an `OverloadSet` for a given operation (say, by calling\n///   [`MathFunction::overloads`]). This set is fixed by Naga IR's semantics.\n///\n/// - Call its [`arg`] method, supplying the type of the argument passed to the\n///   operation at a certain index. This returns a new `OverloadSet` containing\n///   only those overloads that could accept the given type for the given\n///   argument. This includes overloads that only match if automatic conversions\n///   are applied.\n///\n/// - If, at some point, the overload set becomes empty, then the set of\n///   arguments is not allowed for this operation, and the program is invalid.\n///   The `OverloadSet` trait provides an [`is_empty`] method.\n///\n/// - After all arguments have been supplied, if the overload set is still\n///   non-empty, you can call its [`most_preferred`] method to find out which\n///   overload has the least cost in terms of automatic conversions.\n///\n/// - If the call is rejected, you can use `OverloadSet` to help produce\n///   diagnostic messages that explain exactly what went wrong. `OverloadSet`\n///   implementations are meant to be cheap to [`Clone`], so it is fine to keep\n///   the original overload set value around, and re-run the selection process,\n///   attempting to supply the rejected argument at each step to see exactly\n///   which preceding argument ruled it out. The [`overload_list`] and\n///   [`allowed_args`] methods are helpful for this.\n///\n/// [`arg`]: OverloadSet::arg\n/// [`is_empty`]: OverloadSet::is_empty\n/// [`most_preferred`]: OverloadSet::most_preferred\n/// [`overload_list`]: OverloadSet::overload_list\n/// [`allowed_args`]: OverloadSet::allowed_args\n///\n/// # Concrete implementations\n///\n/// This module contains two private implementations of `OverloadSet`:\n///\n/// - The [`List`] type is a straightforward list of overloads. It is general,\n///   but verbose to use. The [`utils`] module exports functions that construct\n///   `List` overload sets for the functions that need this.\n///\n/// - The [`Regular`] type is a compact, efficient representation for the kinds\n///   of overload sets commonly seen for Naga IR mathematical functions.\n///   However, in return for its simplicity, it is not as flexible as [`List`].\n///   This module use the [`regular!`] macro as a legible notation for `Regular`\n///   sets.\n///\n/// [`List`]: list::List\n/// [`Regular`]: regular::Regular\n/// [`regular!`]: regular::regular\npub trait OverloadSet: Clone {\n    /// Return true if `self` is the empty set of overloads.\n    fn is_empty(&self) -> bool;\n\n    /// Return the smallest number of arguments in any type rule in the set.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self` is empty.\n    fn min_arguments(&self) -> usize;\n\n    /// Return the largest number of arguments in any type rule in the set.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self` is empty.\n    fn max_arguments(&self) -> usize;\n\n    /// Find the overloads that could accept a given argument.\n    ///\n    /// Return a new overload set containing those members of `self` that could\n    /// accept a value of type `ty` for their `i`'th argument, once\n    /// feasible automatic conversions have been applied.\n    fn arg(&self, i: usize, ty: &ir::TypeInner, types: &crate::UniqueArena<ir::Type>) -> Self;\n\n    /// Limit `self` to overloads whose arguments are all concrete types.\n    ///\n    /// Naga's overload resolution is based on WGSL's [overload resolution\n    /// algorithm][ora], which includes the following step:\n    ///\n    /// > Eliminate any candidate where one of its subexpressions resolves to\n    /// > an abstract type after feasible automatic conversions, but another of\n    /// > the candidate’s subexpressions is not a const-expression.\n    /// >\n    /// > Note: As a consequence, if any subexpression in the phrase is not a\n    /// > const-expression, then all subexpressions in the phrase must have a\n    /// > concrete type.\n    ///\n    /// Essentially, if any one of the arguments is not a constant expression,\n    /// then the operation is going to be evaluated at runtime, so all its\n    /// arguments must be converted to a concrete type. If you determine that an\n    /// argument is non-constant, you can use this trait method to toss out any\n    /// overloads that would accept abstract types.\n    ///\n    /// In almost all cases, this operation has no effect. Only constant\n    /// expressions can have abstract types, so if any argument is not a\n    /// constant expression, it must have a concrete type. Although many\n    /// operations in Naga IR have overloads for both abstract types and\n    /// concrete types, few operations have overloads that accept a mix of\n    /// abstract and concrete types. Thus, a single concrete argument will\n    /// usually have eliminated all overloads that accept abstract types anyway.\n    /// (The exceptions are oddities like `Expression::Select`, where the\n    /// `condition` operand could be a runtime expression even as the `accept`\n    /// and `reject` operands have abstract types.)\n    ///\n    /// Note that it is *not* correct to just [concretize] all arguments once\n    /// you've noticed that some argument is non-constant. The type to which\n    /// each argument is converted depends on the overloads available, not just\n    /// the argument's own type.\n    ///\n    /// [ora]: https://gpuweb.github.io/gpuweb/wgsl/#overload-resolution-section\n    /// [concretize]: https://gpuweb.github.io/gpuweb/wgsl/#concretization\n    fn concrete_only(self, types: &crate::UniqueArena<ir::Type>) -> Self;\n\n    /// Return the most preferred candidate.\n    ///\n    /// Rank the candidates in `self` as described in WGSL's [overload\n    /// resolution algorithm][ora], and return a singleton set containing the\n    /// most preferred candidate.\n    ///\n    /// # Simplifications versus WGSL\n    ///\n    /// Naga's process for selecting the most preferred candidate is simpler\n    /// than WGSL's:\n    ///\n    /// - WGSL allows for the possibility of ambiguous calls, where multiple\n    ///   overload candidates exist, no one candidate is clearly better than all\n    ///   the others. For example, if a function has the two overloads `(i32,\n    ///   f32) -> bool` and `(f32, i32) -> bool`, and the arguments are both\n    ///   AbstractInt, neither overload is preferred over the other. Ambiguous\n    ///   calls are errors.\n    ///\n    ///   However, Naga IR includes no operations whose overload sets allow such\n    ///   situations to arise, so there is always a most preferred candidate.\n    ///   Thus, this method infallibly returns a `Rule`, and has no way to\n    ///   indicate ambiguity.\n    ///\n    /// - WGSL says that the most preferred candidate depends on the conversion\n    ///   rank for each argument, as determined by the types of the arguments\n    ///   being passed.\n    ///\n    ///   However, the overloads of every operation in Naga IR can be ranked\n    ///   even without knowing the argument types. So this method does not\n    ///   require the argument types as a parameter.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self` is empty, or if no argument types have been supplied.\n    ///\n    /// [ora]: https://gpuweb.github.io/gpuweb/wgsl/#overload-resolution-section\n    fn most_preferred(&self) -> Rule;\n\n    /// Return a type rule for each of the overloads in `self`.\n    fn overload_list(&self, gctx: &crate::proc::GlobalCtx<'_>) -> Vec<Rule>;\n\n    /// Return a list of the types allowed for argument `i`.\n    fn allowed_args(&self, i: usize, gctx: &crate::proc::GlobalCtx<'_>) -> Vec<TypeResolution>;\n\n    /// Return an object that can be formatted with [`core::fmt::Debug`].\n    fn for_debug(&self, types: &crate::UniqueArena<ir::Type>) -> impl fmt::Debug;\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/one_bits_iter.rs",
    "content": "//! An iterator over bitmasks.\n\n/// An iterator that produces the set bits in the given `u64`.\n///\n/// `OneBitsIter(n)` is an [`Iterator`] that produces each of the set bits in\n/// `n`, as a bitmask, in order of increasing value. In other words, it produces\n/// the unique sequence of distinct powers of two that adds up to `n`.\n///\n/// For example, iterating over `OneBitsIter(21)` produces the values `1`, `4`,\n/// and `16`, in that order, because `21` is `0xb10101`.\n///\n/// When `n` is the bits of a bitmask, this iterates over the set bits in the\n/// bitmask, in order of increasing bit value. `bitflags` does define an `iter`\n/// method, but it's not well-specified or well-implemented.\n///\n/// The values produced are masks, not bit numbers. Use `u64::trailing_zeros` if\n/// you need bit numbers.\npub struct OneBitsIter(u64);\n\nimpl OneBitsIter {\n    pub const fn new(bits: u64) -> Self {\n        Self(bits)\n    }\n}\n\nimpl Iterator for OneBitsIter {\n    type Item = u64;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.0 == 0 {\n            return None;\n        }\n\n        // Subtracting one from a value in binary clears the lowest `1` bit\n        // (call it `B`), and sets all the bits below that.\n        let mask = self.0 - 1;\n\n        // Complementing that means that we've instead *set* `B`, *cleared*\n        // everything below it, and *complemented* everything above it.\n        //\n        // Masking with the original value clears everything above and below\n        // `B`, leaving only `B` set. This is the value we produce.\n        let item = self.0 & !mask;\n\n        // Now that we've produced this bit, remove it from `self.0`.\n        self.0 &= mask;\n\n        Some(item)\n    }\n}\n\n#[test]\nfn empty() {\n    assert_eq!(OneBitsIter(0).next(), None);\n}\n\n#[test]\nfn all() {\n    let mut obi = OneBitsIter(!0);\n    for bit in 0..64 {\n        assert_eq!(obi.next(), Some(1 << bit));\n    }\n    assert_eq!(obi.next(), None);\n}\n\n#[test]\nfn first() {\n    let mut obi = OneBitsIter(1);\n    assert_eq!(obi.next(), Some(1));\n    assert_eq!(obi.next(), None);\n}\n\n#[test]\nfn last() {\n    let mut obi = OneBitsIter(1 << 63);\n    assert_eq!(obi.next(), Some(1 << 63));\n    assert_eq!(obi.next(), None);\n}\n\n#[test]\nfn in_order() {\n    let mut obi = OneBitsIter(0b11011000001);\n    assert_eq!(obi.next(), Some(1));\n    assert_eq!(obi.next(), Some(64));\n    assert_eq!(obi.next(), Some(128));\n    assert_eq!(obi.next(), Some(512));\n    assert_eq!(obi.next(), Some(1024));\n    assert_eq!(obi.next(), None);\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/regular.rs",
    "content": "/*! A representation for highly regular overload sets common in Naga IR.\n\nMany Naga builtin functions' overload sets have a highly regular\nstructure. For example, many arithmetic functions can be applied to\nany floating-point type, or any vector thereof. This module defines a\nhandful of types for representing such simple overload sets that is\nsimple and efficient.\n\n*/\n\nuse crate::common::{DiagnosticDebug, ForDebugWithTypes};\nuse crate::ir;\nuse crate::proc::overloads::constructor_set::{ConstructorSet, ConstructorSize};\nuse crate::proc::overloads::rule::{Conclusion, Rule};\nuse crate::proc::overloads::scalar_set::ScalarSet;\nuse crate::proc::overloads::OverloadSet;\nuse crate::proc::{GlobalCtx, TypeResolution};\nuse crate::UniqueArena;\n\nuse alloc::vec::Vec;\nuse core::fmt;\n\n/// Overload sets represented as sets of scalars and constructors.\n///\n/// This type represents an [`OverloadSet`] using a bitset of scalar\n/// types and a bitset of type constructors that might be applied to\n/// those scalars. The overload set contains a rule for every possible\n/// combination of scalars and constructors, essentially the cartesian\n/// product of the two sets.\n///\n/// For example, if the arity is 2, set of scalars is { AbstractFloat,\n/// `f32` }, and the set of constructors is { `vec2`, `vec3` }, then\n/// that represents the set of overloads:\n///\n/// - (`vec2<AbstractFloat>`, `vec2<AbstractFloat>`) -> `vec2<AbstractFloat>`\n/// - (`vec2<f32>`, `vec2<f32>`) -> `vec2<f32>`\n/// - (`vec3<AbstractFloat>`, `vec3<AbstractFloat>`) -> `vec3<AbstractFloat>`\n/// - (`vec3<f32>`, `vec3<f32>`) -> `vec3<f32>`\n///\n/// The `conclude` value says how to determine the return type from\n/// the argument type.\n///\n/// Restrictions:\n///\n/// - All overloads must take the same number of arguments.\n///\n/// - For any given overload, all its arguments must have the same\n///   type.\n#[derive(Clone)]\npub(in crate::proc::overloads) struct Regular {\n    /// The number of arguments in the rules.\n    pub arity: usize,\n\n    /// The set of type constructors to apply.\n    pub constructors: ConstructorSet,\n\n    /// The set of scalars to apply them to.\n    pub scalars: ScalarSet,\n\n    /// How to determine a member rule's return type given the type of\n    /// its arguments.\n    pub conclude: ConclusionRule,\n}\n\nimpl Regular {\n    pub(in crate::proc::overloads) const EMPTY: Regular = Regular {\n        arity: 0,\n        constructors: ConstructorSet::empty(),\n        scalars: ScalarSet::empty(),\n        conclude: ConclusionRule::ArgumentType,\n    };\n\n    /// Return an iterator over all the argument types allowed by `self`.\n    ///\n    /// Return an iterator that produces, for each overload in `self`, the\n    /// constructor and scalar of its argument types and return type.\n    ///\n    /// A [`Regular`] value can only represent overload sets where, in\n    /// each overload, all the arguments have the same type, and the\n    /// return type is always going to be a determined by the argument\n    /// types, so giving the constructor and scalar is sufficient to\n    /// characterize the entire rule.\n    fn members(&self) -> impl Iterator<Item = (ConstructorSize, ir::Scalar)> {\n        let scalars = self.scalars;\n        self.constructors.members().flat_map(move |constructor| {\n            let size = constructor.size();\n            // Technically, we don't need the \"most general\" `TypeInner` here,\n            // but since `ScalarSet::members` only produces singletons anyway,\n            // the effect is the same.\n            scalars\n                .members()\n                .map(move |singleton| (size, singleton.most_general_scalar()))\n        })\n    }\n\n    fn rules(&self) -> impl Iterator<Item = Rule> {\n        let arity = self.arity;\n        let conclude = self.conclude;\n        self.members()\n            .map(move |(size, scalar)| make_rule(arity, size, scalar, conclude))\n    }\n}\n\nimpl OverloadSet for Regular {\n    fn is_empty(&self) -> bool {\n        self.constructors.is_empty() || self.scalars.is_empty()\n    }\n\n    fn min_arguments(&self) -> usize {\n        assert!(!self.is_empty());\n        self.arity\n    }\n\n    fn max_arguments(&self) -> usize {\n        assert!(!self.is_empty());\n        self.arity\n    }\n\n    fn arg(&self, i: usize, ty: &ir::TypeInner, types: &UniqueArena<ir::Type>) -> Self {\n        if i >= self.arity {\n            return Self::EMPTY;\n        }\n\n        let constructor = ConstructorSet::singleton(ty);\n\n        let scalars = match ty.scalar_for_conversions(types) {\n            Some(ty_scalar) => ScalarSet::convertible_from(ty_scalar),\n            None => ScalarSet::empty(),\n        };\n\n        Self {\n            arity: self.arity,\n\n            // Constrain all member rules' constructors to match `ty`'s.\n            constructors: self.constructors & constructor,\n\n            // Constrain all member rules' arguments to be something\n            // that `ty` can be converted to.\n            scalars: self.scalars & scalars,\n\n            conclude: self.conclude,\n        }\n    }\n\n    fn concrete_only(self, _types: &UniqueArena<ir::Type>) -> Self {\n        Self {\n            scalars: self.scalars & ScalarSet::CONCRETE,\n            ..self\n        }\n    }\n\n    fn most_preferred(&self) -> Rule {\n        assert!(!self.is_empty());\n\n        // If there is more than one constructor allowed, then we must\n        // not have had any arguments supplied at all. In any case, we\n        // don't have any unambiguously preferred candidate.\n        assert!(self.constructors.is_singleton());\n\n        let size = self.constructors.size();\n        let scalar = self.scalars.most_general_scalar();\n        make_rule(self.arity, size, scalar, self.conclude)\n    }\n\n    fn overload_list(&self, _gctx: &GlobalCtx<'_>) -> Vec<Rule> {\n        self.rules().collect()\n    }\n\n    fn allowed_args(&self, i: usize, _gctx: &GlobalCtx<'_>) -> Vec<TypeResolution> {\n        if i >= self.arity {\n            return Vec::new();\n        }\n        self.members()\n            .map(|(size, scalar)| TypeResolution::Value(size.to_inner(scalar)))\n            .collect()\n    }\n\n    fn for_debug(&self, types: &UniqueArena<ir::Type>) -> impl fmt::Debug {\n        DiagnosticDebug((self, types))\n    }\n}\n\n/// Construct a [`Regular`] member [`Rule`] for the given arity and type.\n///\n/// [`Regular`] can only represent rules where all the argument types and the\n/// return type are the same, so just knowing `arity` and `inner` is sufficient.\n///\n/// [`Rule`]: crate::proc::overloads::Rule\nfn make_rule(\n    arity: usize,\n    size: ConstructorSize,\n    scalar: ir::Scalar,\n    conclusion_rule: ConclusionRule,\n) -> Rule {\n    let inner = size.to_inner(scalar);\n    let arg = TypeResolution::Value(inner.clone());\n    Rule {\n        arguments: core::iter::repeat_n(arg.clone(), arity).collect(),\n        conclusion: conclusion_rule.conclude(size, scalar),\n    }\n}\n\n/// Conclusion-computing rules.\n#[derive(Clone, Copy, Debug)]\n#[repr(u8)]\npub(in crate::proc::overloads) enum ConclusionRule {\n    ArgumentType,\n    Scalar,\n    Frexp,\n    Modf,\n    U32,\n    I32,\n    Vec2F,\n    Vec4F,\n    Vec4I,\n    Vec4U,\n}\n\nimpl ConclusionRule {\n    fn conclude(self, size: ConstructorSize, scalar: ir::Scalar) -> Conclusion {\n        match self {\n            Self::ArgumentType => Conclusion::Value(size.to_inner(scalar)),\n            Self::Scalar => Conclusion::Value(ir::TypeInner::Scalar(scalar)),\n            Self::Frexp => Conclusion::for_frexp_modf(ir::MathFunction::Frexp, size, scalar),\n            Self::Modf => Conclusion::for_frexp_modf(ir::MathFunction::Modf, size, scalar),\n            Self::U32 => Conclusion::Value(ir::TypeInner::Scalar(ir::Scalar::U32)),\n            Self::I32 => Conclusion::Value(ir::TypeInner::Scalar(ir::Scalar::I32)),\n            Self::Vec2F => Conclusion::Value(ir::TypeInner::Vector {\n                size: ir::VectorSize::Bi,\n                scalar: ir::Scalar::F32,\n            }),\n            Self::Vec4F => Conclusion::Value(ir::TypeInner::Vector {\n                size: ir::VectorSize::Quad,\n                scalar: ir::Scalar::F32,\n            }),\n            Self::Vec4I => Conclusion::Value(ir::TypeInner::Vector {\n                size: ir::VectorSize::Quad,\n                scalar: ir::Scalar::I32,\n            }),\n            Self::Vec4U => Conclusion::Value(ir::TypeInner::Vector {\n                size: ir::VectorSize::Quad,\n                scalar: ir::Scalar::U32,\n            }),\n        }\n    }\n}\n\nimpl fmt::Debug for DiagnosticDebug<(&Regular, &UniqueArena<ir::Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (regular, types) = self.0;\n        let rules: Vec<Rule> = regular.rules().collect();\n        f.debug_struct(\"List\")\n            .field(\"rules\", &rules.for_debug(types))\n            .field(\"conclude\", &regular.conclude)\n            .finish()\n    }\n}\n\nimpl ForDebugWithTypes for &Regular {}\n\nimpl fmt::Debug for DiagnosticDebug<(&[Rule], &UniqueArena<ir::Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (rules, types) = self.0;\n        f.debug_list()\n            .entries(rules.iter().map(|rule| rule.for_debug(types)))\n            .finish()\n    }\n}\n\nimpl ForDebugWithTypes for &[Rule] {}\n\n/// Construct a [`Regular`] [`OverloadSet`].\n///\n/// Examples:\n///\n/// - `regular!(2, SCALAR|VECN of FLOAT)`: An overload set whose rules take two\n///   arguments of the same type: a floating-point scalar (possibly abstract) or\n///   a vector of such. The return type is the same as the argument type.\n///\n/// - `regular!(1, VECN of FLOAT -> Scalar)`: An overload set whose rules take\n///   one argument that is a vector of floats, and whose return type is the leaf\n///   scalar type of the argument type.\n///\n/// The constructor values (before the `<` angle brackets `>`) are\n/// constants from [`ConstructorSet`].\n///\n/// The scalar values (inside the `<` angle brackets `>`) are\n/// constants from [`ScalarSet`].\n///\n/// When a return type identifier is given, it is treated as a variant\n/// of the the [`ConclusionRule`] enum.\nmacro_rules! regular {\n    // regular!(ARITY, CONSTRUCTOR of SCALAR)\n    ( $arity:literal , $( $constr:ident )|* of $( $scalar:ident )|*) => {\n        {\n            use $crate::proc::overloads;\n            use overloads::constructor_set::constructor_set;\n            use overloads::regular::{Regular, ConclusionRule};\n            use overloads::scalar_set::scalar_set;\n            Regular {\n                arity: $arity,\n                constructors: constructor_set!( $( $constr )|* ),\n                scalars: scalar_set!( $( $scalar )|* ),\n                conclude: ConclusionRule::ArgumentType,\n            }\n        }\n    };\n\n    // regular!(ARITY, CONSTRUCTOR of SCALAR -> CONCLUSION_RULE)\n    ( $arity:literal , $( $constr:ident )|* of $( $scalar:ident )|* -> $conclude:ident) => {\n        {\n            use $crate::proc::overloads;\n            use overloads::constructor_set::constructor_set;\n            use overloads::regular::{Regular, ConclusionRule};\n            use overloads::scalar_set::scalar_set;\n            Regular {\n                arity: $arity,\n                constructors:constructor_set!( $( $constr )|* ),\n                scalars: scalar_set!( $( $scalar )|* ),\n                conclude: ConclusionRule::$conclude,\n            }\n        }\n    };\n}\n\npub(in crate::proc::overloads) use regular;\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::ir;\n\n    const fn scalar(scalar: ir::Scalar) -> ir::TypeInner {\n        ir::TypeInner::Scalar(scalar)\n    }\n\n    const fn vec2(scalar: ir::Scalar) -> ir::TypeInner {\n        ir::TypeInner::Vector {\n            scalar,\n            size: ir::VectorSize::Bi,\n        }\n    }\n\n    const fn vec3(scalar: ir::Scalar) -> ir::TypeInner {\n        ir::TypeInner::Vector {\n            scalar,\n            size: ir::VectorSize::Tri,\n        }\n    }\n\n    /// Assert that `set` has a most preferred candidate whose type\n    /// conclusion is `expected`.\n    #[track_caller]\n    fn check_return_type(set: &Regular, expected: &ir::TypeInner, arena: &UniqueArena<ir::Type>) {\n        assert!(!set.is_empty());\n\n        let special_types = ir::SpecialTypes::default();\n\n        let preferred = set.most_preferred();\n        let conclusion = preferred.conclusion;\n        let resolution = conclusion\n            .into_resolution(&special_types)\n            .expect(\"special types should have been pre-registered\");\n        let inner = resolution.inner_with(arena);\n\n        assert!(\n            inner.non_struct_equivalent(expected, arena),\n            \"Expected {:?}, got {:?}\",\n            expected.for_debug(arena),\n            inner.for_debug(arena),\n        );\n    }\n\n    #[test]\n    fn unary_vec_or_scalar_numeric_scalar() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(1, SCALAR of NUMERIC);\n\n        let ok = builtin.arg(0, &scalar(ir::Scalar::U32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::U32), &arena);\n\n        let err = builtin.arg(0, &scalar(ir::Scalar::BOOL), &arena);\n        assert!(err.is_empty());\n    }\n\n    #[test]\n    fn unary_vec_or_scalar_numeric_vector() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(1, VECN|SCALAR of NUMERIC);\n\n        let ok = builtin.arg(0, &vec3(ir::Scalar::F64), &arena);\n        check_return_type(&ok, &vec3(ir::Scalar::F64), &arena);\n\n        let err = builtin.arg(0, &vec3(ir::Scalar::BOOL), &arena);\n        assert!(err.is_empty());\n    }\n\n    #[test]\n    fn unary_vec_or_scalar_numeric_matrix() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(1, VECN|SCALAR of NUMERIC);\n\n        let err = builtin.arg(\n            0,\n            &ir::TypeInner::Matrix {\n                columns: ir::VectorSize::Tri,\n                rows: ir::VectorSize::Tri,\n                scalar: ir::Scalar::F32,\n            },\n            &arena,\n        );\n        assert!(err.is_empty());\n    }\n\n    #[test]\n    #[rustfmt::skip]\n    fn binary_vec_or_scalar_numeric_scalar() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(2, VECN|SCALAR of NUMERIC);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::F32), &arena)\n            .arg(1, &scalar(ir::Scalar::F32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::F32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::ABSTRACT_FLOAT), &arena)\n            .arg(1, &scalar(ir::Scalar::F32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::F32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::F32), &arena)\n            .arg(1, &scalar(ir::Scalar::ABSTRACT_INT), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::F32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::U32), &arena)\n            .arg(1, &scalar(ir::Scalar::U32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::U32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::U32), &arena)\n            .arg(1, &scalar(ir::Scalar::ABSTRACT_INT), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::U32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::ABSTRACT_INT), &arena)\n            .arg(1, &scalar(ir::Scalar::U32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::U32), &arena);\n\n        // Not numeric.\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::BOOL), &arena)\n            .arg(1, &scalar(ir::Scalar::BOOL), &arena);\n        assert!(err.is_empty());\n\n        // Different floating-point types.\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::F32), &arena)\n            .arg(1, &scalar(ir::Scalar::F64), &arena);\n        assert!(err.is_empty());\n\n        // Different constructor.\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::F32), &arena)\n            .arg(1, &vec2(ir::Scalar::F32), &arena);\n        assert!(err.is_empty());\n\n        // Different vector size\n        let err = builtin\n            .arg(0, &vec2(ir::Scalar::F32), &arena)\n            .arg(1, &vec3(ir::Scalar::F32), &arena);\n        assert!(err.is_empty());\n    }\n\n    #[test]\n    #[rustfmt::skip]\n    fn binary_vec_or_scalar_numeric_vector() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(2, VECN|SCALAR of NUMERIC);\n\n        let ok = builtin\n            .arg(0, &vec3(ir::Scalar::F32), &arena)\n            .arg(1, &vec3(ir::Scalar::F32), &arena);\n        check_return_type(&ok, &vec3(ir::Scalar::F32), &arena);\n\n        // Different vector sizes.\n        let err = builtin\n            .arg(0, &vec2(ir::Scalar::F32), &arena)\n            .arg(1, &vec3(ir::Scalar::F32), &arena);\n        assert!(err.is_empty());\n\n        // Different vector scalars.\n        let err = builtin\n            .arg(0, &vec3(ir::Scalar::F32), &arena)\n            .arg(1, &vec3(ir::Scalar::F64), &arena);\n        assert!(err.is_empty());\n\n        // Mix of vectors and scalars.\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::F32), &arena)\n            .arg(1, &vec3(ir::Scalar::F32), &arena);\n        assert!(err.is_empty());\n    }\n\n    #[test]\n    #[rustfmt::skip]\n    fn binary_vec_or_scalar_numeric_vector_abstract() {\n        let arena = UniqueArena::default();\n\n        let builtin = regular!(2, VECN|SCALAR of NUMERIC);\n\n        let ok = builtin\n            .arg(0, &vec2(ir::Scalar::ABSTRACT_INT), &arena)\n            .arg(1, &vec2(ir::Scalar::U32), &arena);\n        check_return_type(&ok, &vec2(ir::Scalar::U32), &arena);\n\n        let ok = builtin\n            .arg(0, &vec3(ir::Scalar::ABSTRACT_INT), &arena)\n            .arg(1, &vec3(ir::Scalar::F32), &arena);\n        check_return_type(&ok, &vec3(ir::Scalar::F32), &arena);\n\n        let ok = builtin\n            .arg(0, &scalar(ir::Scalar::ABSTRACT_FLOAT), &arena)\n            .arg(1, &scalar(ir::Scalar::F32), &arena);\n        check_return_type(&ok, &scalar(ir::Scalar::F32), &arena);\n\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::ABSTRACT_FLOAT), &arena)\n            .arg(1, &scalar(ir::Scalar::U32), &arena);\n        assert!(err.is_empty());\n\n        let err = builtin\n            .arg(0, &scalar(ir::Scalar::I32), &arena)\n            .arg(1, &scalar(ir::Scalar::U32), &arena);\n        assert!(err.is_empty());\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/rule.rs",
    "content": "/*! Type rules.\n\nAn implementation of [`OverloadSet`] represents a set of type rules, each of\nwhich has a list of types for its arguments, and a conclusion about the\ntype of the expression as a whole.\n\nThis module defines the [`Rule`] type, representing a type rule from an\n[`OverloadSet`], and the [`Conclusion`] type, a specialized enum for\nrepresenting a type rule's conclusion.\n\n[`OverloadSet`]: crate::proc::overloads::OverloadSet\n\n*/\n\nuse crate::common::{DiagnosticDebug, ForDebugWithTypes};\nuse crate::ir;\nuse crate::proc::overloads::constructor_set::ConstructorSize;\nuse crate::proc::TypeResolution;\nuse crate::UniqueArena;\n\nuse alloc::vec::Vec;\nuse core::fmt;\nuse core::result::Result;\n\n/// A single type rule.\n#[derive(Clone)]\npub struct Rule {\n    pub arguments: Vec<TypeResolution>,\n    pub conclusion: Conclusion,\n}\n\n/// The result type of a [`Rule`].\n///\n/// A `Conclusion` value represents the return type of some operation\n/// in the builtin function database.\n///\n/// This is very similar to [`TypeInner`], except that it represents\n/// predeclared types using [`PredeclaredType`], so that overload\n/// resolution can delegate registering predeclared types to its users.\n///\n/// [`TypeInner`]: ir::TypeInner\n/// [`PredeclaredType`]: ir::PredeclaredType\n#[derive(Clone, Debug)]\npub enum Conclusion {\n    /// A type that can be entirely characterized by a [`TypeInner`] value.\n    ///\n    /// [`TypeInner`]: ir::TypeInner\n    Value(ir::TypeInner),\n\n    /// A type that should be registered in the module's\n    /// [`SpecialTypes::predeclared_types`] table.\n    ///\n    /// This is used for operations like [`Frexp`] and [`Modf`].\n    ///\n    /// [`SpecialTypes::predeclared_types`]: ir::SpecialTypes::predeclared_types\n    /// [`Frexp`]: crate::ir::MathFunction::Frexp\n    /// [`Modf`]: crate::ir::MathFunction::Modf\n    Predeclared(ir::PredeclaredType),\n}\n\nimpl Conclusion {\n    pub fn for_frexp_modf(\n        function: ir::MathFunction,\n        size: ConstructorSize,\n        scalar: ir::Scalar,\n    ) -> Self {\n        use ir::MathFunction as Mf;\n        use ir::PredeclaredType as Pt;\n\n        let size = match size {\n            ConstructorSize::Scalar => None,\n            ConstructorSize::Vector(size) => Some(size),\n            ConstructorSize::Matrix { .. } => {\n                unreachable!(\"FrexpModf only supports scalars and vectors\");\n            }\n        };\n\n        let predeclared = match function {\n            Mf::Frexp => Pt::FrexpResult { size, scalar },\n            Mf::Modf => Pt::ModfResult { size, scalar },\n            _ => {\n                unreachable!(\"FrexpModf only supports Frexp and Modf\");\n            }\n        };\n\n        Conclusion::Predeclared(predeclared)\n    }\n\n    pub fn into_resolution(\n        self,\n        special_types: &ir::SpecialTypes,\n    ) -> Result<TypeResolution, MissingSpecialType> {\n        match self {\n            Conclusion::Value(inner) => Ok(TypeResolution::Value(inner)),\n            Conclusion::Predeclared(predeclared) => {\n                let handle = *special_types\n                    .predeclared_types\n                    .get(&predeclared)\n                    .ok_or(MissingSpecialType)?;\n                Ok(TypeResolution::Handle(handle))\n            }\n        }\n    }\n}\n\n#[derive(Debug, thiserror::Error)]\n#[error(\"Special type is not registered within the module\")]\npub struct MissingSpecialType;\n\nimpl ForDebugWithTypes for &Rule {}\n\nimpl fmt::Debug for DiagnosticDebug<(&Rule, &UniqueArena<ir::Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (rule, arena) = self.0;\n        f.write_str(\"(\")?;\n        for (i, argument) in rule.arguments.iter().enumerate() {\n            if i > 0 {\n                f.write_str(\", \")?;\n            }\n            write!(f, \"{:?}\", argument.for_debug(arena))?;\n        }\n        write!(f, \") -> {:?}\", rule.conclusion.for_debug(arena))\n    }\n}\n\nimpl ForDebugWithTypes for &Conclusion {}\n\nimpl fmt::Debug for DiagnosticDebug<(&Conclusion, &UniqueArena<ir::Type>)> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let (conclusion, ctx) = self.0;\n\n        #[cfg(any(feature = \"wgsl-in\", feature = \"wgsl-out\"))]\n        {\n            use crate::common::wgsl::TypeContext;\n            ctx.write_type_conclusion(conclusion, f)?;\n        }\n\n        #[cfg(not(any(feature = \"wgsl-in\", feature = \"wgsl-out\")))]\n        {\n            let _ = ctx;\n            write!(f, \"{conclusion:?}\")?;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/overloads/scalar_set.rs",
    "content": "//! A set of scalar types, represented as a bitset.\n\nuse crate::ir::Scalar;\nuse crate::proc::overloads::one_bits_iter::OneBitsIter;\n\nmacro_rules! define_scalar_set {\n    { $( $scalar:ident, )* } => {\n        /// An enum used to assign distinct bit numbers to [`ScalarSet`] elements.\n        #[expect(non_camel_case_types, clippy::upper_case_acronyms)]\n        #[repr(u32)]\n        enum ScalarSetBits {\n            $( $scalar, )*\n            Count,\n        }\n\n        /// A table mapping bit numbers to the [`Scalar`] values they represent.\n        static SCALARS_FOR_BITS: [Scalar; ScalarSetBits::Count as usize] = [\n            $(\n                Scalar::$scalar,\n            )*\n        ];\n\n        bitflags::bitflags! {\n            /// A set of scalar types.\n            ///\n            /// This represents a set of [`Scalar`] types.\n            ///\n            /// The Naga IR conversion rules arrange scalar types into a\n            /// lattice. The scalar types' bit values are chosen such that, if\n            /// A is convertible to B, then A's bit value is less than B's.\n            #[derive(Copy, Clone, Debug)]\n            pub(crate) struct ScalarSet: u16 {\n                $(\n                    const $scalar = 1 << (ScalarSetBits::$scalar as u32);\n                )*\n            }\n        }\n\n        impl ScalarSet {\n            /// Return the set of scalars containing only `scalar`.\n            #[expect(dead_code)]\n            pub const fn singleton(scalar: Scalar) -> Self {\n                match scalar {\n                    $(\n                        Scalar::$scalar => Self::$scalar,\n                    )*\n                    _ => Self::empty(),\n                }\n            }\n        }\n    }\n}\n\ndefine_scalar_set! {\n    // Scalar types must be listed here in an order such that, if A is\n    // convertible to B, then A appears before B.\n    //\n    // In the concrete types, the 32-bit types *must* appear before\n    // other sizes, since that is how we represent conversion rank.\n    ABSTRACT_INT, ABSTRACT_FLOAT,\n    I32, I64,\n    U32, U64,\n    F32, F16, F64,\n    BOOL,\n}\n\nimpl ScalarSet {\n    /// Return the set of scalars to which `scalar` can be automatically\n    /// converted.\n    pub fn convertible_from(scalar: Scalar) -> Self {\n        use Scalar as Sc;\n        match scalar {\n            Sc::I32 => Self::I32,\n            Sc::I64 => Self::I64,\n            Sc::U32 => Self::U32,\n            Sc::U64 => Self::U64,\n            Sc::F16 => Self::F16,\n            Sc::F32 => Self::F32,\n            Sc::F64 => Self::F64,\n            Sc::BOOL => Self::BOOL,\n            Sc::ABSTRACT_INT => Self::INTEGER | Self::FLOAT,\n            Sc::ABSTRACT_FLOAT => Self::FLOAT,\n            _ => Self::empty(),\n        }\n    }\n\n    /// Return the lowest-ranked member of `self` as a [`Scalar`].\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self` is empty.\n    pub fn most_general_scalar(self) -> Scalar {\n        // If the set is empty, this returns the full bit-length of\n        // `self.bits()`, an index which is out of bounds for\n        // `SCALARS_FOR_BITS`.\n        let lowest = self.bits().trailing_zeros();\n        *SCALARS_FOR_BITS.get(lowest as usize).unwrap()\n    }\n\n    /// Return an iterator over this set's members.\n    ///\n    /// Members are produced as singleton, in order from most general to least.\n    pub fn members(self) -> impl Iterator<Item = ScalarSet> {\n        OneBitsIter::new(self.bits() as u64).map(|bit| Self::from_bits(bit as u16).unwrap())\n    }\n\n    pub const FLOAT: Self = Self::ABSTRACT_FLOAT\n        .union(Self::F16)\n        .union(Self::F32)\n        .union(Self::F64);\n\n    pub const INTEGER: Self = Self::ABSTRACT_INT\n        .union(Self::I32)\n        .union(Self::I64)\n        .union(Self::U32)\n        .union(Self::U64);\n\n    pub const NUMERIC: Self = Self::FLOAT.union(Self::INTEGER);\n    pub const ABSTRACT: Self = Self::ABSTRACT_INT.union(Self::ABSTRACT_FLOAT);\n    pub const CONCRETE: Self = Self::all().difference(Self::ABSTRACT);\n    pub const CONCRETE_INTEGER: Self = Self::INTEGER.intersection(Self::CONCRETE);\n    pub const CONCRETE_FLOAT: Self = Self::FLOAT.intersection(Self::CONCRETE);\n\n    /// Floating-point scalars, with the abstract floats omitted for\n    /// #7405.\n    pub const FLOAT_ABSTRACT_UNIMPLEMENTED: Self = Self::CONCRETE_FLOAT;\n}\n\nmacro_rules! scalar_set {\n    ( $( $scalar:ident )|* ) => {\n        {\n            use $crate::proc::overloads::scalar_set::ScalarSet;\n            ScalarSet::empty()\n                $(\n                    .union(ScalarSet::$scalar)\n                )*\n        }\n    }\n}\n\npub(in crate::proc::overloads) use scalar_set;\n"
  },
  {
    "path": "naga/src/proc/overloads/utils.rs",
    "content": "//! Utility functions for constructing [`List`] overload sets.\n//!\n//! [`List`]: crate::proc::overloads::list::List\n\nuse crate::ir;\nuse crate::proc::overloads::list::List;\nuse crate::proc::overloads::rule::{Conclusion, Rule};\nuse crate::proc::TypeResolution;\n\nuse alloc::vec::Vec;\n\n/// Produce all the floating-point [`ir::Scalar`]s.\n///\n/// Note that `F32` must appear before other sizes; this is how we\n/// represent conversion rank.\npub fn float_scalars() -> impl Iterator<Item = ir::Scalar> + Clone {\n    [\n        ir::Scalar::ABSTRACT_FLOAT,\n        ir::Scalar::F32,\n        ir::Scalar::F16,\n        ir::Scalar::F64,\n    ]\n    .into_iter()\n}\n\n/// Produce all the floating-point [`ir::Scalar`]s, but omit\n/// abstract types, for #7405.\npub fn float_scalars_unimplemented_abstract() -> impl Iterator<Item = ir::Scalar> + Clone {\n    [ir::Scalar::F32, ir::Scalar::F16, ir::Scalar::F64].into_iter()\n}\n\n/// Produce the scalar and vector [`ir::TypeInner`]s that have `s` as\n/// their scalar.\npub fn scalar_or_vecn(scalar: ir::Scalar) -> impl Iterator<Item = ir::TypeInner> {\n    [\n        ir::TypeInner::Scalar(scalar),\n        ir::TypeInner::Vector {\n            size: ir::VectorSize::Bi,\n            scalar,\n        },\n        ir::TypeInner::Vector {\n            size: ir::VectorSize::Tri,\n            scalar,\n        },\n        ir::TypeInner::Vector {\n            size: ir::VectorSize::Quad,\n            scalar,\n        },\n    ]\n    .into_iter()\n}\n\n/// Construct a [`Rule`] for an operation with the given\n/// argument types and return type.\npub fn rule<const N: usize>(args: [ir::TypeInner; N], ret: ir::TypeInner) -> Rule {\n    Rule {\n        arguments: Vec::from_iter(args.into_iter().map(TypeResolution::Value)),\n        conclusion: Conclusion::Value(ret),\n    }\n}\n\n/// Construct a [`List`] from the given rules.\npub fn list(rules: impl Iterator<Item = Rule>) -> List {\n    List::from_rules(rules.collect())\n}\n\n/// Return the cartesian product of two iterators.\npub fn pairs<T: Clone, U>(\n    left: impl Iterator<Item = T>,\n    right: impl Iterator<Item = U> + Clone,\n) -> impl Iterator<Item = (T, U)> {\n    left.flat_map(move |t| right.clone().map(move |u| (t.clone(), u)))\n}\n\n/// Return the cartesian product of three iterators.\npub fn triples<T: Clone, U: Clone, V>(\n    left: impl Iterator<Item = T>,\n    middle: impl Iterator<Item = U> + Clone,\n    right: impl Iterator<Item = V> + Clone,\n) -> impl Iterator<Item = (T, U, V)> {\n    left.flat_map(move |t| {\n        let right = right.clone();\n        middle.clone().flat_map(move |u| {\n            let t = t.clone();\n            right.clone().map(move |v| (t.clone(), u.clone(), v))\n        })\n    })\n}\n"
  },
  {
    "path": "naga/src/proc/terminator.rs",
    "content": "/// Ensure that the given block has return statements\n/// at the end of its control flow.\n///\n/// Note: we don't want to blindly append a return statement\n/// to the end, because it may be either redundant or invalid,\n/// e.g. when the user already has returns in if/else branches.\npub fn ensure_block_returns(block: &mut crate::Block) {\n    use crate::Statement as S;\n    match block.last_mut() {\n        Some(&mut S::Block(ref mut b)) => {\n            ensure_block_returns(b);\n        }\n        Some(&mut S::If {\n            condition: _,\n            ref mut accept,\n            ref mut reject,\n        }) => {\n            ensure_block_returns(accept);\n            ensure_block_returns(reject);\n        }\n        Some(&mut S::Switch {\n            selector: _,\n            ref mut cases,\n        }) => {\n            for case in cases.iter_mut() {\n                if !case.fall_through {\n                    ensure_block_returns(&mut case.body);\n                }\n            }\n        }\n        Some(&mut (S::Break | S::Continue | S::Return { .. } | S::Kill)) => (),\n        Some(\n            &mut (S::Emit(_)\n            | S::Loop { .. }\n            | S::Store { .. }\n            | S::ImageStore { .. }\n            | S::Call { .. }\n            | S::RayQuery { .. }\n            | S::Atomic { .. }\n            | S::ImageAtomic { .. }\n            | S::WorkGroupUniformLoad { .. }\n            | S::SubgroupBallot { .. }\n            | S::SubgroupCollectiveOperation { .. }\n            | S::SubgroupGather { .. }\n            | S::ControlBarrier(_)\n            | S::MemoryBarrier(_)\n            | S::CooperativeStore { .. }\n            | S::RayPipelineFunction(_)),\n        )\n        | None => block.push(S::Return { value: None }, Default::default()),\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/type_methods.rs",
    "content": "//! Methods on or related to [`TypeInner`], [`Scalar`], [`ScalarKind`], and [`VectorSize`].\n//!\n//! [`TypeInner`]: crate::TypeInner\n//! [`Scalar`]: crate::Scalar\n//! [`ScalarKind`]: crate::ScalarKind\n//! [`VectorSize`]: crate::VectorSize\n\nuse crate::{ir, valid::MAX_TYPE_SIZE};\n\nuse super::TypeResolution;\n\nimpl crate::ScalarKind {\n    pub const fn is_numeric(self) -> bool {\n        match self {\n            crate::ScalarKind::Sint\n            | crate::ScalarKind::Uint\n            | crate::ScalarKind::Float\n            | crate::ScalarKind::AbstractInt\n            | crate::ScalarKind::AbstractFloat => true,\n            crate::ScalarKind::Bool => false,\n        }\n    }\n}\n\nimpl crate::Scalar {\n    pub const I32: Self = Self {\n        kind: crate::ScalarKind::Sint,\n        width: 4,\n    };\n    pub const U32: Self = Self {\n        kind: crate::ScalarKind::Uint,\n        width: 4,\n    };\n    pub const F16: Self = Self {\n        kind: crate::ScalarKind::Float,\n        width: 2,\n    };\n    pub const F32: Self = Self {\n        kind: crate::ScalarKind::Float,\n        width: 4,\n    };\n    pub const F64: Self = Self {\n        kind: crate::ScalarKind::Float,\n        width: 8,\n    };\n    pub const I64: Self = Self {\n        kind: crate::ScalarKind::Sint,\n        width: 8,\n    };\n    pub const U64: Self = Self {\n        kind: crate::ScalarKind::Uint,\n        width: 8,\n    };\n    pub const BOOL: Self = Self {\n        kind: crate::ScalarKind::Bool,\n        width: crate::BOOL_WIDTH,\n    };\n    pub const ABSTRACT_INT: Self = Self {\n        kind: crate::ScalarKind::AbstractInt,\n        width: crate::ABSTRACT_WIDTH,\n    };\n    pub const ABSTRACT_FLOAT: Self = Self {\n        kind: crate::ScalarKind::AbstractFloat,\n        width: crate::ABSTRACT_WIDTH,\n    };\n\n    pub const fn is_abstract(self) -> bool {\n        match self.kind {\n            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,\n            crate::ScalarKind::Sint\n            | crate::ScalarKind::Uint\n            | crate::ScalarKind::Float\n            | crate::ScalarKind::Bool => false,\n        }\n    }\n\n    /// Construct a float `Scalar` with the given width.\n    ///\n    /// This is especially common when dealing with\n    /// `TypeInner::Matrix`, where the scalar kind is implicit.\n    pub const fn float(width: crate::Bytes) -> Self {\n        Self {\n            kind: crate::ScalarKind::Float,\n            width,\n        }\n    }\n\n    pub const fn to_inner_scalar(self) -> crate::TypeInner {\n        crate::TypeInner::Scalar(self)\n    }\n\n    pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {\n        crate::TypeInner::Vector { size, scalar: self }\n    }\n\n    pub const fn to_inner_atomic(self) -> crate::TypeInner {\n        crate::TypeInner::Atomic(self)\n    }\n}\n\n/// Produce all concrete integer [`ir::Scalar`]s.\n///\n/// Note that `I32` and `U32` must come first; this represents conversion rank\n/// in overload resolution.\npub fn concrete_int_scalars() -> impl Iterator<Item = ir::Scalar> {\n    [\n        ir::Scalar::I32,\n        ir::Scalar::U32,\n        ir::Scalar::I64,\n        ir::Scalar::U64,\n    ]\n    .into_iter()\n}\n\n/// Produce all vector sizes.\npub fn vector_sizes() -> impl Iterator<Item = ir::VectorSize> + Clone {\n    static SIZES: [ir::VectorSize; 3] = [\n        ir::VectorSize::Bi,\n        ir::VectorSize::Tri,\n        ir::VectorSize::Quad,\n    ];\n\n    SIZES.iter().cloned()\n}\n\nconst POINTER_SPAN: u32 = 4;\n\nimpl crate::TypeInner {\n    /// Return the scalar type of `self`.\n    ///\n    /// If `inner` is a scalar, vector, or matrix type, return\n    /// its scalar type. Otherwise, return `None`.\n    ///\n    /// Note that this doesn't inspect [`Array`] types, as required\n    /// for automatic conversions. For that, see [`scalar_for_conversions`].\n    ///\n    /// [`Array`]: crate::TypeInner::Array\n    /// [`scalar_for_conversions`]: crate::TypeInner::scalar_for_conversions\n    pub const fn scalar(&self) -> Option<crate::Scalar> {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),\n            Ti::Matrix { scalar, .. } => Some(scalar),\n            Ti::CooperativeMatrix { scalar, .. } => Some(scalar),\n            _ => None,\n        }\n    }\n\n    pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {\n        self.scalar().map(|scalar| scalar.kind)\n    }\n\n    /// Returns the scalar width in bytes\n    pub fn scalar_width(&self) -> Option<u8> {\n        self.scalar().map(|scalar| scalar.width)\n    }\n\n    /// Return the leaf scalar type of `self`, as needed for automatic conversions.\n    ///\n    /// Unlike the [`scalar`] method, which only retrieves scalars for\n    /// [`Scalar`], [`Vector`], and [`Matrix`] this also looks into\n    /// [`Array`] types to find the leaf scalar.\n    ///\n    /// [`scalar`]: crate::TypeInner::scalar\n    /// [`Scalar`]: crate::TypeInner::Scalar\n    /// [`Vector`]: crate::TypeInner::Vector\n    /// [`Matrix`]: crate::TypeInner::Matrix\n    /// [`Array`]: crate::TypeInner::Array\n    pub fn scalar_for_conversions(\n        &self,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> Option<crate::Scalar> {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {\n                Some(scalar)\n            }\n            Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),\n            _ => None,\n        }\n    }\n\n    pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {\n        match *self {\n            Self::Pointer { space, .. } => Some(space),\n            Self::ValuePointer { space, .. } => Some(space),\n            _ => None,\n        }\n    }\n\n    /// If `self` is a pointer type, return its base type.\n    pub const fn pointer_base_type(&self) -> Option<TypeResolution> {\n        match *self {\n            crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),\n            crate::TypeInner::ValuePointer {\n                size: None, scalar, ..\n            } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),\n            crate::TypeInner::ValuePointer {\n                size: Some(size),\n                scalar,\n                ..\n            } => Some(TypeResolution::Value(crate::TypeInner::Vector {\n                size,\n                scalar,\n            })),\n            _ => None,\n        }\n    }\n\n    pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {\n        match *self {\n            Self::Pointer { base, .. } => match types[base].inner {\n                Self::Atomic { .. } => true,\n                _ => false,\n            },\n            _ => false,\n        }\n    }\n\n    /// Attempt to calculate the size of this type. Returns `None` if the size\n    /// exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`].\n    pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {\n        match *self {\n            Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),\n            Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),\n            // matrices are treated as arrays of aligned columns\n            Self::Matrix {\n                columns,\n                rows,\n                scalar,\n            } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),\n            Self::CooperativeMatrix {\n                columns,\n                rows,\n                scalar,\n                role: _,\n            } => Some(columns as u32 * rows as u32 * scalar.width as u32),\n            Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),\n            Self::Array {\n                base: _,\n                size,\n                stride,\n            } => {\n                let count = match size.resolve(gctx) {\n                    Ok(crate::proc::IndexableLength::Known(count)) => count,\n                    // any struct member or array element needing a size at pipeline-creation time\n                    // must have a creation-fixed footprint\n                    Err(_) => 0,\n                    // A dynamically-sized array has to have at least one element\n                    Ok(crate::proc::IndexableLength::Dynamic) => 1,\n                };\n                if count > MAX_TYPE_SIZE {\n                    // It shouldn't be possible to have an array of a zero-sized type, but\n                    // let's check just in case.\n                    None\n                } else {\n                    count\n                        .checked_mul(stride)\n                        .filter(|size| *size <= MAX_TYPE_SIZE)\n                }\n            }\n            Self::Struct { span, .. } => Some(span),\n            Self::Image { .. }\n            | Self::Sampler { .. }\n            | Self::AccelerationStructure { .. }\n            | Self::RayQuery { .. }\n            | Self::BindingArray { .. } => Some(0),\n        }\n    }\n\n    /// Get the size of this type.\n    ///\n    /// Panics if the size exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`].\n    /// Validated modules should not contain such types. Code working with\n    /// modules prior to validation should use [`Self::try_size`] and handle the\n    /// error appropriately.\n    pub fn size(&self, gctx: super::GlobalCtx) -> u32 {\n        self.try_size(gctx).expect(\"type is too large\")\n    }\n\n    /// Return the canonical form of `self`, or `None` if it's already in\n    /// canonical form.\n    ///\n    /// Certain types have multiple representations in `TypeInner`. This\n    /// function converts all forms of equivalent types to a single\n    /// representative of their class, so that simply applying `Eq` to the\n    /// result indicates whether the types are equivalent, as far as Naga IR is\n    /// concerned.\n    pub fn canonical_form(\n        &self,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> Option<crate::TypeInner> {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Pointer { base, space } => match types[base].inner {\n                Ti::Scalar(scalar) => Some(Ti::ValuePointer {\n                    size: None,\n                    scalar,\n                    space,\n                }),\n                Ti::Vector { size, scalar } => Some(Ti::ValuePointer {\n                    size: Some(size),\n                    scalar,\n                    space,\n                }),\n                _ => None,\n            },\n            _ => None,\n        }\n    }\n\n    /// Compare value type `self` and `rhs` as types.\n    ///\n    /// This is mostly the same as `<TypeInner as Eq>::eq`, but it treats\n    /// [`ValuePointer`] and [`Pointer`] types as equivalent. This method\n    /// cannot be used for structs, because it cannot distinguish two\n    /// structs with different names but the same members. For structs,\n    /// use [`compare_types`].\n    ///\n    /// When you know that one side of the comparison is never a pointer or\n    /// struct, it's fine to not bother with canonicalization, and just\n    /// compare `TypeInner` values with `==`.\n    ///\n    /// # Panics\n    ///\n    /// If both `self` and `rhs` are structs.\n    ///\n    /// [`compare_types`]: crate::proc::compare_types\n    /// [`ValuePointer`]: ir::TypeInner::ValuePointer\n    /// [`Pointer`]: ir::TypeInner::Pointer\n    pub fn non_struct_equivalent(\n        &self,\n        rhs: &ir::TypeInner,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> bool {\n        let left = self.canonical_form(types);\n        let right = rhs.canonical_form(types);\n\n        let left_struct = matches!(*self, ir::TypeInner::Struct { .. });\n        let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });\n\n        assert!(!left_struct || !right_struct);\n\n        left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)\n    }\n\n    /// Returns true if `self` is runtime- or override-sized.\n    pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Array {\n                size: crate::ArraySize::Constant(_),\n                ..\n            } => false,\n            Ti::Array {\n                size: crate::ArraySize::Pending(_) | crate::ArraySize::Dynamic,\n                ..\n            } => true,\n            Ti::Struct { ref members, .. } => members\n                .last()\n                .map(|last| types[last.ty].inner.is_dynamically_sized(types))\n                .unwrap_or(false),\n            _ => false,\n        }\n    }\n\n    /// Returns true if `self` is a constructible type.\n    pub fn is_constructible(&self, types: &crate::UniqueArena<crate::Type>) -> bool {\n        use crate::TypeInner as Ti;\n        match *self {\n            Ti::Array { base, size, .. } => {\n                let fixed_size = match size {\n                    ir::ArraySize::Constant(_) => true,\n                    ir::ArraySize::Pending(_) | ir::ArraySize::Dynamic => false,\n                };\n                fixed_size && types[base].inner.is_constructible(types)\n            }\n            Ti::Struct { ref members, .. } => members\n                .iter()\n                .all(|member| types[member.ty].inner.is_constructible(types)),\n            Ti::Atomic(_)\n            | Ti::Pointer { .. }\n            | Ti::ValuePointer { .. }\n            | Ti::Image { .. }\n            | Ti::Sampler { .. }\n            | Ti::AccelerationStructure { .. }\n            | Ti::BindingArray { .. } => false,\n            Ti::Scalar(_)\n            | Ti::Vector { .. }\n            | Ti::Matrix { .. }\n            | Ti::RayQuery { .. }\n            | Ti::CooperativeMatrix { .. } => true,\n        }\n    }\n\n    pub const fn components(&self) -> Option<u32> {\n        Some(match *self {\n            Self::Vector { size, .. } => size as u32,\n            Self::Matrix { columns, .. } => columns as u32,\n            Self::Array {\n                size: crate::ArraySize::Constant(len),\n                ..\n            } => len.get(),\n            Self::Struct { ref members, .. } => members.len() as u32,\n            _ => return None,\n        })\n    }\n\n    pub fn component_type(&self, index: usize) -> Option<TypeResolution> {\n        Some(match *self {\n            Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),\n            Self::Matrix { rows, scalar, .. } => {\n                TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })\n            }\n            Self::Array {\n                base,\n                size: crate::ArraySize::Constant(_),\n                ..\n            } => TypeResolution::Handle(base),\n            Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),\n            _ => return None,\n        })\n    }\n\n    /// If the type is a Vector or a Scalar return a tuple of the vector size (or None\n    /// for Scalars), and the scalar kind. Returns (None, None) for other types.\n    pub const fn vector_size_and_scalar(\n        &self,\n    ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {\n        match *self {\n            crate::TypeInner::Scalar(scalar) => Some((None, scalar)),\n            crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),\n            crate::TypeInner::Matrix { .. }\n            | crate::TypeInner::CooperativeMatrix { .. }\n            | crate::TypeInner::Atomic(_)\n            | crate::TypeInner::Pointer { .. }\n            | crate::TypeInner::ValuePointer { .. }\n            | crate::TypeInner::Array { .. }\n            | crate::TypeInner::Struct { .. }\n            | crate::TypeInner::Image { .. }\n            | crate::TypeInner::Sampler { .. }\n            | crate::TypeInner::AccelerationStructure { .. }\n            | crate::TypeInner::RayQuery { .. }\n            | crate::TypeInner::BindingArray { .. } => None,\n        }\n    }\n\n    /// Return true if `self` is an abstract type.\n    ///\n    /// Use `types` to look up type handles. This is necessary to\n    /// recognize abstract arrays.\n    pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {\n        match *self {\n            crate::TypeInner::Scalar(scalar)\n            | crate::TypeInner::Vector { scalar, .. }\n            | crate::TypeInner::Matrix { scalar, .. }\n            | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),\n            crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),\n            crate::TypeInner::CooperativeMatrix { .. }\n            | crate::TypeInner::ValuePointer { .. }\n            | crate::TypeInner::Pointer { .. }\n            | crate::TypeInner::Struct { .. }\n            | crate::TypeInner::Image { .. }\n            | crate::TypeInner::Sampler { .. }\n            | crate::TypeInner::AccelerationStructure { .. }\n            | crate::TypeInner::RayQuery { .. }\n            | crate::TypeInner::BindingArray { .. } => false,\n        }\n    }\n\n    /// Determine whether `self` automatically converts to `goal`.\n    ///\n    /// If Naga IR's automatic conversions will convert `self` to\n    /// `goal`, then return a pair `(from, to)`, where `from` and `to`\n    /// are the scalar types of the leaf values of `self` and `goal`.\n    ///\n    /// If `self` and `goal` are the same type, this will simply return\n    /// a pair `(S, S)`.\n    ///\n    /// If the automatic conversions cannot convert `self` to `goal`,\n    /// return `None`.\n    ///\n    /// Naga IR's automatic conversions will convert:\n    ///\n    /// - [`AbstractInt`] scalars to [`AbstractFloat`] or any numeric scalar type\n    ///\n    /// - [`AbstractFloat`] scalars to any floating-point scalar type\n    ///\n    /// - A [`Vector`] `{ size, scalar: S }` to `{ size, scalar: T }`\n    ///   if they would convert `S` to `T`\n    ///\n    /// - An [`Array`] `{ base: S, size, stride }` to `{ base: T, size, stride }`\n    ///   if they would convert `S` to `T`\n    ///\n    /// [`AbstractInt`]: crate::ScalarKind::AbstractInt\n    /// [`AbstractFloat`]: crate::ScalarKind::AbstractFloat\n    /// [`Vector`]: crate::TypeInner::Vector\n    /// [`Array`]: crate::TypeInner::Array\n    pub fn automatically_converts_to(\n        &self,\n        goal: &Self,\n        types: &crate::UniqueArena<crate::Type>,\n    ) -> Option<(crate::Scalar, crate::Scalar)> {\n        use crate::ScalarKind as Sk;\n        use crate::TypeInner as Ti;\n\n        // Automatic conversions only change the scalar type of a value's leaves\n        // (e.g., `vec4<AbstractFloat>` to `vec4<f32>`), never the type\n        // constructors applied to those scalar types (e.g., never scalar to\n        // `vec4`, or `vec2` to `vec3`). So first we check that the type\n        // constructors match, extracting the leaf scalar types in the process.\n        let expr_scalar;\n        let goal_scalar;\n        match (self, goal) {\n            (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {\n                expr_scalar = expr;\n                goal_scalar = goal;\n            }\n            (\n                &Ti::Vector {\n                    size: expr_size,\n                    scalar: expr,\n                },\n                &Ti::Vector {\n                    size: goal_size,\n                    scalar: goal,\n                },\n            ) if expr_size == goal_size => {\n                expr_scalar = expr;\n                goal_scalar = goal;\n            }\n            (\n                &Ti::Matrix {\n                    rows: expr_rows,\n                    columns: expr_columns,\n                    scalar: expr,\n                },\n                &Ti::Matrix {\n                    rows: goal_rows,\n                    columns: goal_columns,\n                    scalar: goal,\n                },\n            ) if expr_rows == goal_rows && expr_columns == goal_columns => {\n                expr_scalar = expr;\n                goal_scalar = goal;\n            }\n            (\n                &Ti::Array {\n                    base: expr_base,\n                    size: expr_size,\n                    stride: _,\n                },\n                &Ti::Array {\n                    base: goal_base,\n                    size: goal_size,\n                    stride: _,\n                },\n            ) if expr_size == goal_size => {\n                return types[expr_base]\n                    .inner\n                    .automatically_converts_to(&types[goal_base].inner, types);\n            }\n            _ => return None,\n        }\n\n        match (expr_scalar.kind, goal_scalar.kind) {\n            (Sk::AbstractFloat, Sk::Float) => {}\n            (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}\n            _ => return None,\n        }\n\n        log::trace!(\"      okay: expr {expr_scalar:?}, goal {goal_scalar:?}\");\n        Some((expr_scalar, goal_scalar))\n    }\n}\n\n/// Helper trait for providing the min and max values exactly representable by\n/// the integer type `Self` and floating point type `F`.\npub trait IntFloatLimits<F>\nwhere\n    F: num_traits::Float,\n{\n    /// Returns the minimum value exactly representable by the integer type\n    /// `Self` and floating point type `F`.\n    fn min_float() -> F;\n    /// Returns the maximum value exactly representable by the integer type\n    /// `Self` and floating point type `F`.\n    fn max_float() -> F;\n}\n\nmacro_rules! define_int_float_limits {\n    ($int:ty, $float:ty, $min:expr, $max:expr) => {\n        impl IntFloatLimits<$float> for $int {\n            fn min_float() -> $float {\n                $min\n            }\n            fn max_float() -> $float {\n                $max\n            }\n        }\n    };\n}\n\ndefine_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);\ndefine_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);\ndefine_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);\ndefine_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);\ndefine_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);\ndefine_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);\ndefine_int_float_limits!(\n    i64,\n    f32,\n    -9223372036854775808.0f32,\n    9223371487098961920.0f32\n);\ndefine_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);\ndefine_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);\ndefine_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);\ndefine_int_float_limits!(\n    i64,\n    f64,\n    -9223372036854775808.0f64,\n    9223372036854774784.0f64\n);\ndefine_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);\n\n/// Returns a tuple of [`crate::Literal`]s representing the minimum and maximum\n/// float values exactly representable by the provided float and integer types.\n/// Panics if `float` is not one of `F16`, `F32`, or `F64`, or `int` is\n/// not one of `I32`, `U32`, `I64`, or `U64`.\npub fn min_max_float_representable_by(\n    float: crate::Scalar,\n    int: crate::Scalar,\n) -> (crate::Literal, crate::Literal) {\n    match (float, int) {\n        (crate::Scalar::F16, crate::Scalar::I32) => (\n            crate::Literal::F16(i32::min_float()),\n            crate::Literal::F16(i32::max_float()),\n        ),\n        (crate::Scalar::F16, crate::Scalar::U32) => (\n            crate::Literal::F16(u32::min_float()),\n            crate::Literal::F16(u32::max_float()),\n        ),\n        (crate::Scalar::F16, crate::Scalar::I64) => (\n            crate::Literal::F16(i64::min_float()),\n            crate::Literal::F16(i64::max_float()),\n        ),\n        (crate::Scalar::F16, crate::Scalar::U64) => (\n            crate::Literal::F16(u64::min_float()),\n            crate::Literal::F16(u64::max_float()),\n        ),\n        (crate::Scalar::F32, crate::Scalar::I32) => (\n            crate::Literal::F32(i32::min_float()),\n            crate::Literal::F32(i32::max_float()),\n        ),\n        (crate::Scalar::F32, crate::Scalar::U32) => (\n            crate::Literal::F32(u32::min_float()),\n            crate::Literal::F32(u32::max_float()),\n        ),\n        (crate::Scalar::F32, crate::Scalar::I64) => (\n            crate::Literal::F32(i64::min_float()),\n            crate::Literal::F32(i64::max_float()),\n        ),\n        (crate::Scalar::F32, crate::Scalar::U64) => (\n            crate::Literal::F32(u64::min_float()),\n            crate::Literal::F32(u64::max_float()),\n        ),\n        (crate::Scalar::F64, crate::Scalar::I32) => (\n            crate::Literal::F64(i32::min_float()),\n            crate::Literal::F64(i32::max_float()),\n        ),\n        (crate::Scalar::F64, crate::Scalar::U32) => (\n            crate::Literal::F64(u32::min_float()),\n            crate::Literal::F64(u32::max_float()),\n        ),\n        (crate::Scalar::F64, crate::Scalar::I64) => (\n            crate::Literal::F64(i64::min_float()),\n            crate::Literal::F64(i64::max_float()),\n        ),\n        (crate::Scalar::F64, crate::Scalar::U64) => (\n            crate::Literal::F64(u64::min_float()),\n            crate::Literal::F64(u64::max_float()),\n        ),\n        _ => unreachable!(),\n    }\n}\n\n/// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize)\npub const fn vector_size_str(size: crate::VectorSize) -> &'static str {\n    match size {\n        crate::VectorSize::Bi => \"2\",\n        crate::VectorSize::Tri => \"3\",\n        crate::VectorSize::Quad => \"4\",\n    }\n}\n"
  },
  {
    "path": "naga/src/proc/typifier.rs",
    "content": "use alloc::{format, string::String};\n\nuse thiserror::Error;\n\nuse crate::{\n    arena::{Arena, Handle, UniqueArena},\n    common::ForDebugWithTypes,\n    ir,\n};\n\n/// The result of computing an expression's type.\n///\n/// This is the (Rust) type returned by [`ResolveContext::resolve`] to represent\n/// the (Naga) type it ascribes to some expression.\n///\n/// You might expect such a function to simply return a `Handle<Type>`. However,\n/// we want type resolution to be a read-only process, and that would limit the\n/// possible results to types already present in the expression's associated\n/// `UniqueArena<Type>`. Naga IR does have certain expressions whose types are\n/// not certain to be present.\n///\n/// So instead, type resolution returns a `TypeResolution` enum: either a\n/// [`Handle`], referencing some type in the arena, or a [`Value`], holding a\n/// free-floating [`TypeInner`]. This extends the range to cover anything that\n/// can be represented with a `TypeInner` referring to the existing arena.\n///\n/// What sorts of expressions can have types not available in the arena?\n///\n/// -   An [`Access`] or [`AccessIndex`] expression applied to a [`Vector`] or\n///     [`Matrix`] must have a [`Scalar`] or [`Vector`] type. But since `Vector`\n///     and `Matrix` represent their element and column types implicitly, not\n///     via a handle, there may not be a suitable type in the expression's\n///     associated arena. Instead, resolving such an expression returns a\n///     `TypeResolution::Value(TypeInner::X { ... })`, where `X` is `Scalar` or\n///     `Vector`.\n///\n/// -   Similarly, the type of an [`Access`] or [`AccessIndex`] expression\n///     applied to a *pointer to* a vector or matrix must produce a *pointer to*\n///     a scalar or vector type. These cannot be represented with a\n///     [`TypeInner::Pointer`], since the `Pointer`'s `base` must point into the\n///     arena, and as before, we cannot assume that a suitable scalar or vector\n///     type is there. So we take things one step further and provide\n///     [`TypeInner::ValuePointer`], specifically for the case of pointers to\n///     scalars or vectors. This type fits in a `TypeInner` and is exactly\n///     equivalent to a `Pointer` to a `Vector` or `Scalar`.\n///\n/// So, for example, the type of an `Access` expression applied to a value of type:\n///\n/// ```ignore\n/// TypeInner::Matrix { columns, rows, width }\n/// ```\n///\n/// might be:\n///\n/// ```ignore\n/// TypeResolution::Value(TypeInner::Vector {\n///     size: rows,\n///     kind: ScalarKind::Float,\n///     width,\n/// })\n/// ```\n///\n/// and the type of an access to a pointer of address space `space` to such a\n/// matrix might be:\n///\n/// ```ignore\n/// TypeResolution::Value(TypeInner::ValuePointer {\n///     size: Some(rows),\n///     kind: ScalarKind::Float,\n///     width,\n///     space,\n/// })\n/// ```\n///\n/// [`Handle`]: TypeResolution::Handle\n/// [`Value`]: TypeResolution::Value\n///\n/// [`Access`]: crate::Expression::Access\n/// [`AccessIndex`]: crate::Expression::AccessIndex\n///\n/// [`TypeInner`]: crate::TypeInner\n/// [`Matrix`]: crate::TypeInner::Matrix\n/// [`Pointer`]: crate::TypeInner::Pointer\n/// [`Scalar`]: crate::TypeInner::Scalar\n/// [`ValuePointer`]: crate::TypeInner::ValuePointer\n/// [`Vector`]: crate::TypeInner::Vector\n///\n/// [`TypeInner::Pointer`]: crate::TypeInner::Pointer\n/// [`TypeInner::ValuePointer`]: crate::TypeInner::ValuePointer\n#[derive(Debug, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub enum TypeResolution {\n    /// A type stored in the associated arena.\n    Handle(Handle<crate::Type>),\n\n    /// A free-floating [`TypeInner`], representing a type that may not be\n    /// available in the associated arena. However, the `TypeInner` itself may\n    /// contain `Handle<Type>` values referring to types from the arena.\n    ///\n    /// The inner type must only be one of the following variants:\n    /// - TypeInner::Pointer\n    /// - TypeInner::ValuePointer\n    /// - TypeInner::Matrix (generated by matrix multiplication)\n    /// - TypeInner::Vector\n    /// - TypeInner::Scalar\n    ///\n    /// [`TypeInner`]: crate::TypeInner\n    Value(crate::TypeInner),\n}\n\nimpl TypeResolution {\n    pub const fn handle(&self) -> Option<Handle<crate::Type>> {\n        match *self {\n            Self::Handle(handle) => Some(handle),\n            Self::Value(_) => None,\n        }\n    }\n\n    pub fn inner_with<'a>(&'a self, arena: &'a UniqueArena<crate::Type>) -> &'a crate::TypeInner {\n        match *self {\n            Self::Handle(handle) => &arena[handle].inner,\n            Self::Value(ref inner) => inner,\n        }\n    }\n}\n\n// Clone is only implemented for numeric variants of `TypeInner`.\nimpl Clone for TypeResolution {\n    fn clone(&self) -> Self {\n        use crate::TypeInner as Ti;\n        match *self {\n            Self::Handle(handle) => Self::Handle(handle),\n            Self::Value(ref v) => Self::Value(match *v {\n                Ti::Scalar(scalar) => Ti::Scalar(scalar),\n                Ti::Vector { size, scalar } => Ti::Vector { size, scalar },\n                Ti::Matrix {\n                    rows,\n                    columns,\n                    scalar,\n                } => Ti::Matrix {\n                    rows,\n                    columns,\n                    scalar,\n                },\n                Ti::CooperativeMatrix {\n                    columns,\n                    rows,\n                    scalar,\n                    role,\n                } => Ti::CooperativeMatrix {\n                    columns,\n                    rows,\n                    scalar,\n                    role,\n                },\n                Ti::Pointer { base, space } => Ti::Pointer { base, space },\n                Ti::ValuePointer {\n                    size,\n                    scalar,\n                    space,\n                } => Ti::ValuePointer {\n                    size,\n                    scalar,\n                    space,\n                },\n                Ti::Array { base, size, stride } => Ti::Array { base, size, stride },\n                _ => unreachable!(\"Unexpected clone type: {:?}\", v),\n            }),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error, PartialEq)]\npub enum ResolveError {\n    #[error(\"Index {index} is out of bounds for expression {expr:?}\")]\n    OutOfBoundsIndex {\n        expr: Handle<crate::Expression>,\n        index: u32,\n    },\n    #[error(\"Invalid access into expression {expr:?}, indexed: {indexed}\")]\n    InvalidAccess {\n        expr: Handle<crate::Expression>,\n        indexed: bool,\n    },\n    #[error(\"Invalid sub-access into type {ty:?}, indexed: {indexed}\")]\n    InvalidSubAccess {\n        ty: Handle<crate::Type>,\n        indexed: bool,\n    },\n    #[error(\"Invalid scalar {0:?}\")]\n    InvalidScalar(Handle<crate::Expression>),\n    #[error(\"Invalid vector {0:?}\")]\n    InvalidVector(Handle<crate::Expression>),\n    #[error(\"Invalid pointer {0:?}\")]\n    InvalidPointer(Handle<crate::Expression>),\n    #[error(\"Invalid image {0:?}\")]\n    InvalidImage(Handle<crate::Expression>),\n    #[error(\"Function {name} not defined\")]\n    FunctionNotDefined { name: String },\n    #[error(\"Function without return type\")]\n    FunctionReturnsVoid,\n    #[error(\"Incompatible operands: {0}\")]\n    IncompatibleOperands(String),\n    #[error(\"Function argument {0} doesn't exist\")]\n    FunctionArgumentNotFound(u32),\n    #[error(\"Special type is not registered within the module\")]\n    MissingSpecialType,\n    #[error(\"Call to builtin {0} has incorrect or ambiguous arguments\")]\n    BuiltinArgumentsInvalid(String),\n}\n\nimpl From<crate::proc::MissingSpecialType> for ResolveError {\n    fn from(_unit_struct: crate::proc::MissingSpecialType) -> Self {\n        ResolveError::MissingSpecialType\n    }\n}\n\npub struct ResolveContext<'a> {\n    pub constants: &'a Arena<crate::Constant>,\n    pub overrides: &'a Arena<crate::Override>,\n    pub types: &'a UniqueArena<crate::Type>,\n    pub special_types: &'a crate::SpecialTypes,\n    pub global_vars: &'a Arena<crate::GlobalVariable>,\n    pub local_vars: &'a Arena<crate::LocalVariable>,\n    pub functions: &'a Arena<crate::Function>,\n    pub arguments: &'a [crate::FunctionArgument],\n}\n\nimpl<'a> ResolveContext<'a> {\n    /// Initialize a resolve context from the module.\n    pub const fn with_locals(\n        module: &'a crate::Module,\n        local_vars: &'a Arena<crate::LocalVariable>,\n        arguments: &'a [crate::FunctionArgument],\n    ) -> Self {\n        Self {\n            constants: &module.constants,\n            overrides: &module.overrides,\n            types: &module.types,\n            special_types: &module.special_types,\n            global_vars: &module.global_variables,\n            local_vars,\n            functions: &module.functions,\n            arguments,\n        }\n    }\n\n    /// Determine the type of `expr`.\n    ///\n    /// The `past` argument must be a closure that can resolve the types of any\n    /// expressions that `expr` refers to. These can be gathered by caching the\n    /// results of prior calls to `resolve`, perhaps as done by the\n    /// [`front::Typifier`] utility type.\n    ///\n    /// Type resolution is a read-only process: this method takes `self` by\n    /// shared reference. However, this means that we cannot add anything to\n    /// `self.types` that we might need to describe `expr`. To work around this,\n    /// this method returns a [`TypeResolution`], rather than simply returning a\n    /// `Handle<Type>`; see the documentation for [`TypeResolution`] for\n    /// details.\n    ///\n    /// [`front::Typifier`]: crate::front::Typifier\n    pub fn resolve(\n        &self,\n        expr: &crate::Expression,\n        past: impl Fn(Handle<crate::Expression>) -> Result<&'a TypeResolution, ResolveError>,\n    ) -> Result<TypeResolution, ResolveError> {\n        use crate::TypeInner as Ti;\n        let types = self.types;\n        Ok(match *expr {\n            crate::Expression::Access { base, .. } => match *past(base)?.inner_with(types) {\n                // Arrays and matrices can only be indexed dynamically behind a\n                // pointer, but that's a validation error, not a type error, so\n                // go ahead provide a type here.\n                Ti::Array { base, .. } => TypeResolution::Handle(base),\n                Ti::Matrix { rows, scalar, .. } => {\n                    TypeResolution::Value(Ti::Vector { size: rows, scalar })\n                }\n                Ti::Vector { size: _, scalar } => TypeResolution::Value(Ti::Scalar(scalar)),\n                Ti::ValuePointer {\n                    size: Some(_),\n                    scalar,\n                    space,\n                } => TypeResolution::Value(Ti::ValuePointer {\n                    size: None,\n                    scalar,\n                    space,\n                }),\n                Ti::Pointer { base, space } => {\n                    TypeResolution::Value(match types[base].inner {\n                        Ti::Array { base, .. } => Ti::Pointer { base, space },\n                        Ti::Vector { size: _, scalar } => Ti::ValuePointer {\n                            size: None,\n                            scalar,\n                            space,\n                        },\n                        // Matrices are only dynamically indexed behind a pointer\n                        Ti::Matrix {\n                            columns: _,\n                            rows,\n                            scalar,\n                        } => Ti::ValuePointer {\n                            size: Some(rows),\n                            scalar,\n                            space,\n                        },\n                        Ti::BindingArray { base, .. } => Ti::Pointer { base, space },\n                        ref other => {\n                            log::error!(\"Access sub-type {other:?}\");\n                            return Err(ResolveError::InvalidSubAccess {\n                                ty: base,\n                                indexed: false,\n                            });\n                        }\n                    })\n                }\n                Ti::BindingArray { base, .. } => TypeResolution::Handle(base),\n                ref other => {\n                    log::error!(\"Access type {other:?}\");\n                    return Err(ResolveError::InvalidAccess {\n                        expr: base,\n                        indexed: false,\n                    });\n                }\n            },\n            crate::Expression::AccessIndex { base, index } => {\n                match *past(base)?.inner_with(types) {\n                    Ti::Vector { size, scalar } => {\n                        if index >= size as u32 {\n                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });\n                        }\n                        TypeResolution::Value(Ti::Scalar(scalar))\n                    }\n                    Ti::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    } => {\n                        if index >= columns as u32 {\n                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });\n                        }\n                        TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })\n                    }\n                    Ti::Array { base, .. } => TypeResolution::Handle(base),\n                    Ti::Struct { ref members, .. } => {\n                        let member = members\n                            .get(index as usize)\n                            .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;\n                        TypeResolution::Handle(member.ty)\n                    }\n                    Ti::ValuePointer {\n                        size: Some(size),\n                        scalar,\n                        space,\n                    } => {\n                        if index >= size as u32 {\n                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });\n                        }\n                        TypeResolution::Value(Ti::ValuePointer {\n                            size: None,\n                            scalar,\n                            space,\n                        })\n                    }\n                    Ti::Pointer {\n                        base: ty_base,\n                        space,\n                    } => TypeResolution::Value(match types[ty_base].inner {\n                        Ti::Array { base, .. } => Ti::Pointer { base, space },\n                        Ti::Vector { size, scalar } => {\n                            if index >= size as u32 {\n                                return Err(ResolveError::OutOfBoundsIndex { expr: base, index });\n                            }\n                            Ti::ValuePointer {\n                                size: None,\n                                scalar,\n                                space,\n                            }\n                        }\n                        Ti::Matrix {\n                            rows,\n                            columns,\n                            scalar,\n                        } => {\n                            if index >= columns as u32 {\n                                return Err(ResolveError::OutOfBoundsIndex { expr: base, index });\n                            }\n                            Ti::ValuePointer {\n                                size: Some(rows),\n                                scalar,\n                                space,\n                            }\n                        }\n                        Ti::Struct { ref members, .. } => {\n                            let member = members\n                                .get(index as usize)\n                                .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;\n                            Ti::Pointer {\n                                base: member.ty,\n                                space,\n                            }\n                        }\n                        Ti::BindingArray { base, .. } => Ti::Pointer { base, space },\n                        ref other => {\n                            log::error!(\"Access index sub-type {other:?}\");\n                            return Err(ResolveError::InvalidSubAccess {\n                                ty: ty_base,\n                                indexed: true,\n                            });\n                        }\n                    }),\n                    Ti::BindingArray { base, .. } => TypeResolution::Handle(base),\n                    ref other => {\n                        log::error!(\"Access index type {other:?}\");\n                        return Err(ResolveError::InvalidAccess {\n                            expr: base,\n                            indexed: true,\n                        });\n                    }\n                }\n            }\n            crate::Expression::Splat { size, value } => match *past(value)?.inner_with(types) {\n                Ti::Scalar(scalar) => TypeResolution::Value(Ti::Vector { size, scalar }),\n                ref other => {\n                    log::error!(\"Scalar type {other:?}\");\n                    return Err(ResolveError::InvalidScalar(value));\n                }\n            },\n            crate::Expression::Swizzle {\n                size,\n                vector,\n                pattern: _,\n            } => match *past(vector)?.inner_with(types) {\n                Ti::Vector { size: _, scalar } => {\n                    TypeResolution::Value(Ti::Vector { size, scalar })\n                }\n                ref other => {\n                    log::error!(\"Vector type {other:?}\");\n                    return Err(ResolveError::InvalidVector(vector));\n                }\n            },\n            crate::Expression::Literal(lit) => TypeResolution::Value(lit.ty_inner()),\n            crate::Expression::Constant(h) => TypeResolution::Handle(self.constants[h].ty),\n            crate::Expression::Override(h) => TypeResolution::Handle(self.overrides[h].ty),\n            crate::Expression::ZeroValue(ty) => TypeResolution::Handle(ty),\n            crate::Expression::Compose { ty, .. } => TypeResolution::Handle(ty),\n            crate::Expression::FunctionArgument(index) => {\n                let arg = self\n                    .arguments\n                    .get(index as usize)\n                    .ok_or(ResolveError::FunctionArgumentNotFound(index))?;\n                TypeResolution::Handle(arg.ty)\n            }\n            crate::Expression::GlobalVariable(h) => {\n                let var = &self.global_vars[h];\n                if var.space == crate::AddressSpace::Handle {\n                    TypeResolution::Handle(var.ty)\n                } else {\n                    TypeResolution::Value(Ti::Pointer {\n                        base: var.ty,\n                        space: var.space,\n                    })\n                }\n            }\n            crate::Expression::LocalVariable(h) => {\n                let var = &self.local_vars[h];\n                TypeResolution::Value(Ti::Pointer {\n                    base: var.ty,\n                    space: crate::AddressSpace::Function,\n                })\n            }\n            crate::Expression::Load { pointer } => match *past(pointer)?.inner_with(types) {\n                Ti::Pointer { base, space: _ } => {\n                    if let Ti::Atomic(scalar) = types[base].inner {\n                        TypeResolution::Value(Ti::Scalar(scalar))\n                    } else {\n                        TypeResolution::Handle(base)\n                    }\n                }\n                Ti::ValuePointer {\n                    size,\n                    scalar,\n                    space: _,\n                } => TypeResolution::Value(match size {\n                    Some(size) => Ti::Vector { size, scalar },\n                    None => Ti::Scalar(scalar),\n                }),\n                ref other => {\n                    log::error!(\"Pointer {pointer:?} type {other:?}\");\n                    return Err(ResolveError::InvalidPointer(pointer));\n                }\n            },\n            crate::Expression::ImageSample {\n                image,\n                gather: Some(_),\n                ..\n            } => match *past(image)?.inner_with(types) {\n                Ti::Image { class, .. } => TypeResolution::Value(Ti::Vector {\n                    scalar: crate::Scalar {\n                        kind: match class {\n                            crate::ImageClass::Sampled { kind, multi: _ } => kind,\n                            _ => crate::ScalarKind::Float,\n                        },\n                        width: 4,\n                    },\n                    size: crate::VectorSize::Quad,\n                }),\n                ref other => {\n                    log::error!(\"Image type {other:?}\");\n                    return Err(ResolveError::InvalidImage(image));\n                }\n            },\n            crate::Expression::ImageSample { image, .. }\n            | crate::Expression::ImageLoad { image, .. } => match *past(image)?.inner_with(types) {\n                Ti::Image { class, .. } => TypeResolution::Value(match class {\n                    crate::ImageClass::Depth { multi: _ } => Ti::Scalar(crate::Scalar::F32),\n                    crate::ImageClass::Sampled { kind, multi: _ } => Ti::Vector {\n                        scalar: crate::Scalar { kind, width: 4 },\n                        size: crate::VectorSize::Quad,\n                    },\n                    crate::ImageClass::Storage { format, .. } => Ti::Vector {\n                        scalar: format.into(),\n                        size: crate::VectorSize::Quad,\n                    },\n                    crate::ImageClass::External => Ti::Vector {\n                        scalar: crate::Scalar::F32,\n                        size: crate::VectorSize::Quad,\n                    },\n                }),\n                ref other => {\n                    log::error!(\"Image type {other:?}\");\n                    return Err(ResolveError::InvalidImage(image));\n                }\n            },\n            crate::Expression::ImageQuery { image, query } => TypeResolution::Value(match query {\n                crate::ImageQuery::Size { level: _ } => match *past(image)?.inner_with(types) {\n                    Ti::Image { dim, .. } => match dim {\n                        crate::ImageDimension::D1 => Ti::Scalar(crate::Scalar::U32),\n                        crate::ImageDimension::D2 | crate::ImageDimension::Cube => Ti::Vector {\n                            size: crate::VectorSize::Bi,\n                            scalar: crate::Scalar::U32,\n                        },\n                        crate::ImageDimension::D3 => Ti::Vector {\n                            size: crate::VectorSize::Tri,\n                            scalar: crate::Scalar::U32,\n                        },\n                    },\n                    ref other => {\n                        log::error!(\"Image type {other:?}\");\n                        return Err(ResolveError::InvalidImage(image));\n                    }\n                },\n                crate::ImageQuery::NumLevels\n                | crate::ImageQuery::NumLayers\n                | crate::ImageQuery::NumSamples => Ti::Scalar(crate::Scalar::U32),\n            }),\n            crate::Expression::Unary { expr, .. } => past(expr)?.clone(),\n            crate::Expression::Binary { op, left, right } => match op {\n                crate::BinaryOperator::Add\n                | crate::BinaryOperator::Subtract\n                | crate::BinaryOperator::Divide\n                | crate::BinaryOperator::Modulo => past(left)?.clone(),\n                crate::BinaryOperator::Multiply => {\n                    let (res_left, res_right) = (past(left)?, past(right)?);\n                    match (res_left.inner_with(types), res_right.inner_with(types)) {\n                        (\n                            &Ti::Matrix {\n                                columns: _,\n                                rows,\n                                scalar,\n                            },\n                            &Ti::Matrix { columns, .. },\n                        ) => TypeResolution::Value(Ti::Matrix {\n                            columns,\n                            rows,\n                            scalar,\n                        }),\n                        (\n                            &Ti::Matrix {\n                                columns: _,\n                                rows,\n                                scalar,\n                            },\n                            &Ti::Vector { .. },\n                        ) => TypeResolution::Value(Ti::Vector { size: rows, scalar }),\n                        (\n                            &Ti::Vector { .. },\n                            &Ti::Matrix {\n                                columns,\n                                rows: _,\n                                scalar,\n                            },\n                        ) => TypeResolution::Value(Ti::Vector {\n                            size: columns,\n                            scalar,\n                        }),\n                        (&Ti::Scalar { .. }, _) => res_right.clone(),\n                        (_, &Ti::Scalar { .. }) => res_left.clone(),\n                        (&Ti::Vector { .. }, &Ti::Vector { .. }) => res_left.clone(),\n                        (\n                            &Ti::CooperativeMatrix {\n                                columns: _,\n                                rows,\n                                scalar,\n                                role,\n                            },\n                            &Ti::CooperativeMatrix { columns, .. },\n                        ) => TypeResolution::Value(Ti::CooperativeMatrix {\n                            columns,\n                            rows,\n                            scalar,\n                            role,\n                        }),\n                        (tl, tr) => {\n                            return Err(ResolveError::IncompatibleOperands(format!(\n                                \"{tl:?} * {tr:?}\"\n                            )))\n                        }\n                    }\n                }\n                crate::BinaryOperator::Equal\n                | crate::BinaryOperator::NotEqual\n                | crate::BinaryOperator::Less\n                | crate::BinaryOperator::LessEqual\n                | crate::BinaryOperator::Greater\n                | crate::BinaryOperator::GreaterEqual => {\n                    // These accept scalars or vectors.\n                    let scalar = crate::Scalar::BOOL;\n                    let inner = match *past(left)?.inner_with(types) {\n                        Ti::Scalar { .. } => Ti::Scalar(scalar),\n                        Ti::Vector { size, .. } => Ti::Vector { size, scalar },\n                        ref other => {\n                            return Err(ResolveError::IncompatibleOperands(format!(\n                                \"{op:?}({other:?}, _)\"\n                            )))\n                        }\n                    };\n                    TypeResolution::Value(inner)\n                }\n                crate::BinaryOperator::LogicalAnd | crate::BinaryOperator::LogicalOr => {\n                    // These accept scalars only.\n                    let bool = Ti::Scalar(crate::Scalar::BOOL);\n                    let ty = past(left)?.inner_with(types);\n                    if *ty == bool {\n                        TypeResolution::Value(bool)\n                    } else {\n                        return Err(ResolveError::IncompatibleOperands(format!(\n                            \"{op:?}({:?}, _)\",\n                            ty.for_debug(types),\n                        )));\n                    }\n                }\n                crate::BinaryOperator::And\n                | crate::BinaryOperator::ExclusiveOr\n                | crate::BinaryOperator::InclusiveOr\n                | crate::BinaryOperator::ShiftLeft\n                | crate::BinaryOperator::ShiftRight => past(left)?.clone(),\n            },\n            crate::Expression::AtomicResult { ty, .. } => TypeResolution::Handle(ty),\n            crate::Expression::SubgroupOperationResult { ty } => TypeResolution::Handle(ty),\n            crate::Expression::WorkGroupUniformLoadResult { ty } => TypeResolution::Handle(ty),\n            crate::Expression::Select { accept, .. } => past(accept)?.clone(),\n            crate::Expression::Derivative { expr, .. } => past(expr)?.clone(),\n            crate::Expression::Relational { fun, argument } => match fun {\n                crate::RelationalFunction::All | crate::RelationalFunction::Any => {\n                    TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL))\n                }\n                crate::RelationalFunction::IsNan | crate::RelationalFunction::IsInf => {\n                    match *past(argument)?.inner_with(types) {\n                        Ti::Scalar { .. } => TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL)),\n                        Ti::Vector { size, .. } => TypeResolution::Value(Ti::Vector {\n                            scalar: crate::Scalar::BOOL,\n                            size,\n                        }),\n                        ref other => {\n                            return Err(ResolveError::IncompatibleOperands(format!(\n                                \"{fun:?}({other:?})\"\n                            )))\n                        }\n                    }\n                }\n            },\n            crate::Expression::Math {\n                fun,\n                arg,\n                arg1,\n                arg2: _,\n                arg3: _,\n            } => {\n                use crate::proc::OverloadSet as _;\n\n                let mut overloads = fun.overloads();\n                log::debug!(\n                    \"initial overloads for {fun:?}, {:#?}\",\n                    overloads.for_debug(types)\n                );\n\n                // If any argument is not a constant expression, then no\n                // overloads that accept abstract values should be considered.\n                // `OverloadSet::concrete_only` is supposed to help impose this\n                // restriction. However, no `MathFunction` accepts a mix of\n                // abstract and concrete arguments, so we don't need to worry\n                // about that here.\n\n                let res_arg = past(arg)?;\n                overloads = overloads.arg(0, res_arg.inner_with(types), types);\n                log::debug!(\n                    \"overloads after arg 0 of type {:?}: {:#?}\",\n                    res_arg.for_debug(types),\n                    overloads.for_debug(types)\n                );\n\n                if let Some(arg1) = arg1 {\n                    let res_arg1 = past(arg1)?;\n                    overloads = overloads.arg(1, res_arg1.inner_with(types), types);\n                    log::debug!(\n                        \"overloads after arg 1 of type {:?}: {:#?}\",\n                        res_arg1.for_debug(types),\n                        overloads.for_debug(types)\n                    );\n                }\n\n                if overloads.is_empty() {\n                    return Err(ResolveError::BuiltinArgumentsInvalid(format!(\"{fun:?}\")));\n                }\n\n                let rule = overloads.most_preferred();\n\n                rule.conclusion.into_resolution(self.special_types)?\n            }\n            crate::Expression::As {\n                expr,\n                kind,\n                convert,\n            } => match *past(expr)?.inner_with(types) {\n                Ti::Scalar(crate::Scalar { width, .. }) => {\n                    TypeResolution::Value(Ti::Scalar(crate::Scalar {\n                        kind,\n                        width: convert.unwrap_or(width),\n                    }))\n                }\n                Ti::Vector {\n                    size,\n                    scalar: crate::Scalar { kind: _, width },\n                } => TypeResolution::Value(Ti::Vector {\n                    size,\n                    scalar: crate::Scalar {\n                        kind,\n                        width: convert.unwrap_or(width),\n                    },\n                }),\n                Ti::Matrix {\n                    columns,\n                    rows,\n                    mut scalar,\n                } => {\n                    if let Some(width) = convert {\n                        scalar.width = width;\n                    }\n                    TypeResolution::Value(Ti::Matrix {\n                        columns,\n                        rows,\n                        scalar,\n                    })\n                }\n                ref other => {\n                    return Err(ResolveError::IncompatibleOperands(format!(\n                        \"{other:?} as {kind:?}\"\n                    )))\n                }\n            },\n            crate::Expression::CallResult(function) => {\n                let result = self.functions[function]\n                    .result\n                    .as_ref()\n                    .ok_or(ResolveError::FunctionReturnsVoid)?;\n                TypeResolution::Handle(result.ty)\n            }\n            crate::Expression::ArrayLength(_) => {\n                TypeResolution::Value(Ti::Scalar(crate::Scalar::U32))\n            }\n            crate::Expression::RayQueryProceedResult => {\n                TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL))\n            }\n            crate::Expression::RayQueryGetIntersection { .. } => {\n                let result = self\n                    .special_types\n                    .ray_intersection\n                    .ok_or(ResolveError::MissingSpecialType)?;\n                TypeResolution::Handle(result)\n            }\n            crate::Expression::RayQueryVertexPositions { .. } => {\n                let result = self\n                    .special_types\n                    .ray_vertex_return\n                    .ok_or(ResolveError::MissingSpecialType)?;\n                TypeResolution::Handle(result)\n            }\n            crate::Expression::SubgroupBallotResult => TypeResolution::Value(Ti::Vector {\n                scalar: crate::Scalar::U32,\n                size: crate::VectorSize::Quad,\n            }),\n            crate::Expression::CooperativeLoad {\n                columns,\n                rows,\n                role,\n                ref data,\n            } => {\n                let scalar = past(data.pointer)?\n                    .inner_with(types)\n                    .pointer_base_type()\n                    .and_then(|tr| tr.inner_with(types).scalar())\n                    .ok_or(ResolveError::InvalidPointer(data.pointer))?;\n                TypeResolution::Value(Ti::CooperativeMatrix {\n                    columns,\n                    rows,\n                    scalar,\n                    role,\n                })\n            }\n            crate::Expression::CooperativeMultiplyAdd { a: _, b: _, c } => past(c)?.clone(),\n        })\n    }\n}\n\n/// Compare two types.\n///\n/// This is the most general way of comparing two types, as it can distinguish\n/// two structs with different names but the same members. For other ways, see\n/// [`TypeInner::non_struct_equivalent`] and [`TypeInner::eq`].\n///\n/// In Naga code, this is usually called via the like-named methods on [`Module`],\n/// [`GlobalCtx`], and `BlockContext`.\n///\n/// [`TypeInner::non_struct_equivalent`]: crate::ir::TypeInner::non_struct_equivalent\n/// [`TypeInner::eq`]: crate::ir::TypeInner\n/// [`Module`]: crate::ir::Module\n/// [`GlobalCtx`]: crate::proc::GlobalCtx\npub fn compare_types(\n    lhs: &TypeResolution,\n    rhs: &TypeResolution,\n    types: &UniqueArena<crate::Type>,\n) -> bool {\n    match lhs {\n        &TypeResolution::Handle(lhs_handle)\n            if matches!(\n                types[lhs_handle],\n                ir::Type {\n                    inner: ir::TypeInner::Struct { .. },\n                    ..\n                }\n            ) =>\n        {\n            // Structs can only be in the arena, not in a TypeResolution::Value\n            rhs.handle()\n                .is_some_and(|rhs_handle| lhs_handle == rhs_handle)\n        }\n        _ => lhs\n            .inner_with(types)\n            .non_struct_equivalent(rhs.inner_with(types), types),\n    }\n}\n\n#[test]\nfn test_error_size() {\n    assert_eq!(size_of::<ResolveError>(), 32);\n}\n"
  },
  {
    "path": "naga/src/racy_lock.rs",
    "content": "#[cfg(no_std)]\nuse alloc::boxed::Box;\n#[cfg(no_std)]\nuse once_cell::race::OnceBox;\n#[cfg(std)]\nuse std::sync::LazyLock;\n\n#[cfg(std)]\ntype Inner<T> = LazyLock<T, fn() -> T>;\n#[cfg(no_std)]\ntype Inner<T> = OnceBox<T>;\n\n/// Lazy static helper that uses [`LazyLock`] with `std` and [`OnceBox`] otherwise.\n///\n/// [`LazyLock`]: https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html\n/// [`OnceBox`]: https://docs.rs/once_cell/latest/once_cell/race/struct.OnceBox.html\npub struct RacyLock<T: 'static> {\n    inner: Inner<T>,\n    #[cfg(no_std)]\n    init: fn() -> T,\n}\n\nimpl<T: 'static> RacyLock<T> {\n    #[cfg(std)]\n    /// Creates a new [`RacyLock`], which will initialize using the provided `init` function.\n    pub const fn new(init: fn() -> T) -> Self {\n        Self {\n            inner: LazyLock::new(init),\n        }\n    }\n\n    #[cfg(no_std)]\n    /// Creates a new [`RacyLock`], which will initialize using the provided `init` function.\n    pub const fn new(init: fn() -> T) -> Self {\n        Self {\n            inner: OnceBox::new(),\n            init,\n        }\n    }\n}\n\n#[cfg(std)]\nimpl<T: 'static> core::ops::Deref for RacyLock<T> {\n    type Target = T;\n\n    /// Loads the internal value, initializing it if required.\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\n#[cfg(no_std)]\nimpl<T: 'static> core::ops::Deref for RacyLock<T> {\n    type Target = T;\n\n    /// Loads the internal value, initializing it if required.\n    fn deref(&self) -> &Self::Target {\n        self.inner.get_or_init(|| Box::new((self.init)()))\n    }\n}\n"
  },
  {
    "path": "naga/src/span.rs",
    "content": "use alloc::{\n    borrow::ToOwned,\n    format,\n    string::{String, ToString},\n    vec::Vec,\n};\nuse core::{error::Error, fmt, ops::Range};\n\nuse crate::{error::replace_control_chars, Arena, Handle, UniqueArena};\n\n/// A source code span, used for error reporting.\n#[derive(Clone, Copy, Debug, PartialEq, Default)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub struct Span {\n    start: u32,\n    end: u32,\n}\n\nimpl Span {\n    pub const UNDEFINED: Self = Self { start: 0, end: 0 };\n\n    /// Creates a new `Span` from a range of byte indices\n    ///\n    /// Note: end is exclusive, it doesn't belong to the `Span`\n    pub const fn new(start: u32, end: u32) -> Self {\n        Span { start, end }\n    }\n\n    /// Returns a new `Span` starting at `self` and ending at `other`\n    pub const fn until(&self, other: &Self) -> Self {\n        Span {\n            start: self.start,\n            end: other.end,\n        }\n    }\n\n    /// Modifies `self` to contain the smallest `Span` possible that\n    /// contains both `self` and `other`\n    pub fn subsume(&mut self, other: Self) {\n        *self = if !self.is_defined() {\n            // self isn't defined so use other\n            other\n        } else if !other.is_defined() {\n            // other isn't defined so don't try to subsume\n            *self\n        } else {\n            // Both self and other are defined so calculate the span that contains them both\n            Span {\n                start: self.start.min(other.start),\n                end: self.end.max(other.end),\n            }\n        }\n    }\n\n    /// Returns the smallest `Span` possible that contains all the `Span`s\n    /// defined in the `from` iterator\n    pub fn total_span<T: Iterator<Item = Self>>(from: T) -> Self {\n        let mut span: Self = Default::default();\n        for other in from {\n            span.subsume(other);\n        }\n        span\n    }\n\n    /// Converts `self` to a range if the span is not unknown\n    pub fn to_range(self) -> Option<Range<usize>> {\n        if self.is_defined() {\n            Some(self.start as usize..self.end as usize)\n        } else {\n            None\n        }\n    }\n\n    /// Check whether `self` was defined or is a default/unknown span\n    pub fn is_defined(&self) -> bool {\n        *self != Self::default()\n    }\n\n    /// Return a [`SourceLocation`] for this span in the provided source.\n    pub fn location(&self, source: &str) -> SourceLocation {\n        let prefix = &source[..self.start as usize];\n        let line_number = prefix.matches('\\n').count() as u32 + 1;\n        let line_start = prefix.rfind('\\n').map(|pos| pos + 1).unwrap_or(0) as u32;\n        let line_position = self.start - line_start + 1;\n\n        SourceLocation {\n            line_number,\n            line_position,\n            offset: self.start,\n            length: self.end - self.start,\n        }\n    }\n}\n\nimpl From<Range<usize>> for Span {\n    fn from(range: Range<usize>) -> Self {\n        Span {\n            start: range.start as u32,\n            end: range.end as u32,\n        }\n    }\n}\n\nimpl core::ops::Index<Span> for str {\n    type Output = str;\n\n    #[inline]\n    fn index(&self, span: Span) -> &str {\n        &self[span.start as usize..span.end as usize]\n    }\n}\n\n/// A human-readable representation for a span, tailored for text source.\n///\n/// Roughly corresponds to the positional members of [`GPUCompilationMessage`][gcm] from\n/// the WebGPU specification, except\n/// - `offset` and `length` are in bytes (UTF-8 code units), instead of UTF-16 code units.\n/// - `line_position` is in bytes (UTF-8 code units), instead of UTF-16 code units.\n///\n/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct SourceLocation {\n    /// 1-based line number.\n    pub line_number: u32,\n    /// 1-based column in code units (in bytes) of the start of the span.\n    pub line_position: u32,\n    /// 0-based Offset in code units (in bytes) of the start of the span.\n    pub offset: u32,\n    /// Length in code units (in bytes) of the span.\n    pub length: u32,\n}\n\n/// A source code span together with \"context\", a user-readable description of what part of the error it refers to.\npub type SpanContext = (Span, String);\n\n/// Wrapper class for [`Error`], augmenting it with a list of [`SpanContext`]s.\n#[derive(Debug, Clone)]\npub struct WithSpan<E> {\n    inner: E,\n    spans: Vec<SpanContext>,\n}\n\nimpl<E> fmt::Display for WithSpan<E>\nwhere\n    E: fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\n#[cfg(test)]\nimpl<E> PartialEq for WithSpan<E>\nwhere\n    E: PartialEq,\n{\n    fn eq(&self, other: &Self) -> bool {\n        self.inner.eq(&other.inner)\n    }\n}\n\nimpl<E> Error for WithSpan<E>\nwhere\n    E: Error,\n{\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        self.inner.source()\n    }\n}\n\nimpl<E> WithSpan<E> {\n    /// Create a new [`WithSpan`] from an [`Error`], containing no spans.\n    pub const fn new(inner: E) -> Self {\n        Self {\n            inner,\n            spans: Vec::new(),\n        }\n    }\n\n    /// Reverse of [`Self::new`], discards span information and returns an inner error.\n    pub fn into_inner(self) -> E {\n        self.inner\n    }\n\n    pub const fn as_inner(&self) -> &E {\n        &self.inner\n    }\n\n    /// Iterator over stored [`SpanContext`]s.\n    pub fn spans(&self) -> impl ExactSizeIterator<Item = &SpanContext> {\n        self.spans.iter()\n    }\n\n    /// Add a new span with description.\n    pub fn with_span<S>(mut self, span: Span, description: S) -> Self\n    where\n        S: ToString,\n    {\n        if span.is_defined() {\n            self.spans.push((span, description.to_string()));\n        }\n        self\n    }\n\n    /// Add a [`SpanContext`].\n    pub fn with_context(self, span_context: SpanContext) -> Self {\n        let (span, description) = span_context;\n        self.with_span(span, description)\n    }\n\n    /// Add a [`Handle`] from either [`Arena`] or [`UniqueArena`], borrowing its span information from there\n    /// and annotating with a type and the handle representation.\n    pub(crate) fn with_handle<T, A: SpanProvider<T>>(self, handle: Handle<T>, arena: &A) -> Self {\n        self.with_context(arena.get_span_context(handle))\n    }\n\n    /// Convert inner error using [`From`].\n    pub fn into_other<E2>(self) -> WithSpan<E2>\n    where\n        E2: From<E>,\n    {\n        WithSpan {\n            inner: self.inner.into(),\n            spans: self.spans,\n        }\n    }\n\n    /// Convert inner error into another type. Joins span information contained in `self`\n    /// with what is returned from `func`.\n    pub fn and_then<F, E2>(self, func: F) -> WithSpan<E2>\n    where\n        F: FnOnce(E) -> WithSpan<E2>,\n    {\n        let mut res = func(self.inner);\n        res.spans.extend(self.spans);\n        res\n    }\n\n    /// Return a [`SourceLocation`] for our first span, if we have one.\n    pub fn location(&self, source: &str) -> Option<SourceLocation> {\n        if self.spans.is_empty() || source.is_empty() {\n            return None;\n        }\n\n        Some(self.spans[0].0.location(source))\n    }\n\n    pub(crate) fn diagnostic(&self) -> codespan_reporting::diagnostic::Diagnostic<()>\n    where\n        E: Error,\n    {\n        use codespan_reporting::diagnostic::{Diagnostic, Label};\n        let diagnostic = Diagnostic::error()\n            .with_message(self.inner.to_string())\n            .with_labels(\n                self.spans()\n                    .map(|&(span, ref desc)| {\n                        Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned())\n                    })\n                    .collect(),\n            )\n            .with_notes({\n                let mut notes = Vec::new();\n                let mut source: &dyn Error = &self.inner;\n                while let Some(next) = Error::source(source) {\n                    notes.push(next.to_string());\n                    source = next;\n                }\n                notes\n            });\n        diagnostic\n    }\n\n    /// Emits a summary of the error to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr(&self, source: &str)\n    where\n        E: Error,\n    {\n        self.emit_to_stderr_with_path(source, \"wgsl\")\n    }\n\n    /// Emits a summary of the error to standard error stream.\n    #[cfg(feature = \"stderr\")]\n    pub fn emit_to_stderr_with_path(&self, source: &str, path: &str)\n    where\n        E: Error,\n    {\n        use codespan_reporting::{files, term};\n\n        let files = files::SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"termcolor\")] {\n                let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);\n                term::emit_to_write_style(&mut writer.lock(), &config, &files, &self.diagnostic())\n                    .expect(\"cannot write error\");\n            } else {\n                let writer = std::io::stderr();\n                term::emit_to_io_write(&mut writer.lock(), &config, &files, &self.diagnostic())\n                    .expect(\"cannot write error\");\n            }\n        }\n    }\n\n    /// Emits a summary of the error to a string.\n    pub fn emit_to_string(&self, source: &str) -> String\n    where\n        E: Error,\n    {\n        self.emit_to_string_with_path(source, \"wgsl\")\n    }\n\n    /// Emits a summary of the error to a string.\n    pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String\n    where\n        E: Error,\n    {\n        use codespan_reporting::{files, term};\n\n        let files = files::SimpleFile::new(path, replace_control_chars(source));\n        let config = term::Config::default();\n\n        let mut writer = crate::error::DiagnosticBuffer::new();\n        writer\n            .emit_to_self(&config, &files, &self.diagnostic())\n            .expect(\"cannot write error\");\n        writer.into_string()\n    }\n}\n\n/// Convenience trait for [`Error`] to be able to apply spans to anything.\npub(crate) trait AddSpan: Sized {\n    /// The returned output type.\n    type Output;\n\n    /// See [`WithSpan::new`].\n    fn with_span(self) -> Self::Output;\n    /// See [`WithSpan::with_span`].\n    fn with_span_static(self, span: Span, description: &'static str) -> Self::Output;\n    /// See [`WithSpan::with_context`].\n    fn with_span_context(self, span_context: SpanContext) -> Self::Output;\n    /// See [`WithSpan::with_handle`].\n    fn with_span_handle<T, A: SpanProvider<T>>(self, handle: Handle<T>, arena: &A) -> Self::Output;\n}\n\nimpl<E> AddSpan for E {\n    type Output = WithSpan<Self>;\n\n    fn with_span(self) -> WithSpan<Self> {\n        WithSpan::new(self)\n    }\n\n    fn with_span_static(self, span: Span, description: &'static str) -> WithSpan<Self> {\n        WithSpan::new(self).with_span(span, description)\n    }\n\n    fn with_span_context(self, span_context: SpanContext) -> WithSpan<Self> {\n        WithSpan::new(self).with_context(span_context)\n    }\n\n    fn with_span_handle<T, A: SpanProvider<T>>(\n        self,\n        handle: Handle<T>,\n        arena: &A,\n    ) -> WithSpan<Self> {\n        WithSpan::new(self).with_handle(handle, arena)\n    }\n}\n\n/// Trait abstracting over getting a span from an [`Arena`] or a [`UniqueArena`].\npub(crate) trait SpanProvider<T> {\n    fn get_span(&self, handle: Handle<T>) -> Span;\n    fn get_span_context(&self, handle: Handle<T>) -> SpanContext {\n        match self.get_span(handle) {\n            x if !x.is_defined() => (Default::default(), \"\".to_string()),\n            known => (\n                known,\n                format!(\"{} {:?}\", core::any::type_name::<T>(), handle),\n            ),\n        }\n    }\n}\n\nimpl<T> SpanProvider<T> for Arena<T> {\n    fn get_span(&self, handle: Handle<T>) -> Span {\n        self.get_span(handle)\n    }\n}\n\nimpl<T> SpanProvider<T> for UniqueArena<T> {\n    fn get_span(&self, handle: Handle<T>) -> Span {\n        self.get_span(handle)\n    }\n}\n\n/// Convenience trait for [`Result`], adding a [`MapErrWithSpan::map_err_inner`]\n/// mapping to [`WithSpan::and_then`].\npub(crate) trait MapErrWithSpan<E, E2>: Sized {\n    /// The returned output type.\n    type Output: Sized;\n\n    fn map_err_inner<F, E3>(self, func: F) -> Self::Output\n    where\n        F: FnOnce(E) -> WithSpan<E3>,\n        E2: From<E3>;\n}\n\nimpl<T, E, E2> MapErrWithSpan<E, E2> for Result<T, WithSpan<E>> {\n    type Output = Result<T, WithSpan<E2>>;\n\n    fn map_err_inner<F, E3>(self, func: F) -> Result<T, WithSpan<E2>>\n    where\n        F: FnOnce(E) -> WithSpan<E3>,\n        E2: From<E3>,\n    {\n        self.map_err(|e| e.and_then(func).into_other::<E2>())\n    }\n}\n\n#[test]\nfn span_location() {\n    let source = \"12\\n45\\n\\n89\\n\";\n    assert_eq!(\n        Span { start: 0, end: 1 }.location(source),\n        SourceLocation {\n            line_number: 1,\n            line_position: 1,\n            offset: 0,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 1, end: 2 }.location(source),\n        SourceLocation {\n            line_number: 1,\n            line_position: 2,\n            offset: 1,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 2, end: 3 }.location(source),\n        SourceLocation {\n            line_number: 1,\n            line_position: 3,\n            offset: 2,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 3, end: 5 }.location(source),\n        SourceLocation {\n            line_number: 2,\n            line_position: 1,\n            offset: 3,\n            length: 2\n        }\n    );\n    assert_eq!(\n        Span { start: 4, end: 6 }.location(source),\n        SourceLocation {\n            line_number: 2,\n            line_position: 2,\n            offset: 4,\n            length: 2\n        }\n    );\n    assert_eq!(\n        Span { start: 5, end: 6 }.location(source),\n        SourceLocation {\n            line_number: 2,\n            line_position: 3,\n            offset: 5,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 6, end: 7 }.location(source),\n        SourceLocation {\n            line_number: 3,\n            line_position: 1,\n            offset: 6,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 7, end: 8 }.location(source),\n        SourceLocation {\n            line_number: 4,\n            line_position: 1,\n            offset: 7,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 8, end: 9 }.location(source),\n        SourceLocation {\n            line_number: 4,\n            line_position: 2,\n            offset: 8,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 9, end: 10 }.location(source),\n        SourceLocation {\n            line_number: 4,\n            line_position: 3,\n            offset: 9,\n            length: 1\n        }\n    );\n    assert_eq!(\n        Span { start: 10, end: 11 }.location(source),\n        SourceLocation {\n            line_number: 5,\n            line_position: 1,\n            offset: 10,\n            length: 1\n        }\n    );\n}\n"
  },
  {
    "path": "naga/src/valid/analyzer.rs",
    "content": "//! Module analyzer.\n//!\n//! Figures out the following properties:\n//! - control flow uniformity\n//! - texture/sampler pairs\n//! - expression reference counts\n\nuse alloc::{boxed::Box, vec};\nuse core::ops;\n\nuse super::{ExpressionError, FunctionError, ModuleInfo, ShaderStages, ValidationFlags};\nuse crate::diagnostic_filter::{DiagnosticFilterNode, StandardFilterableTriggeringRule};\nuse crate::span::{AddSpan as _, WithSpan};\nuse crate::{\n    arena::{Arena, Handle},\n    proc::{ResolveContext, TypeResolution},\n};\n\npub type NonUniformResult = Option<Handle<crate::Expression>>;\n\nconst DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE: bool = true;\n\nbitflags::bitflags! {\n    /// Kinds of expressions that require uniform control flow.\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct UniformityRequirements: u8 {\n        const WORK_GROUP_BARRIER = 0x1;\n        const DERIVATIVE = if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { 0 } else { 0x2 };\n        const IMPLICIT_LEVEL = if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { 0 } else { 0x4 };\n        const COOP_OPS = 0x8;\n    }\n}\n\n/// Uniform control flow characteristics.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n#[cfg_attr(test, derive(PartialEq))]\npub struct Uniformity {\n    /// A child expression with non-uniform result.\n    ///\n    /// This means, when the relevant invocations are scheduled on a compute unit,\n    /// they have to use vector registers to store an individual value\n    /// per invocation.\n    ///\n    /// Whenever the control flow is conditioned on such value,\n    /// the hardware needs to keep track of the mask of invocations,\n    /// and process all branches of the control flow.\n    ///\n    /// Any operations that depend on non-uniform results also produce non-uniform.\n    pub non_uniform_result: NonUniformResult,\n    /// If this expression requires uniform control flow, store the reason here.\n    pub requirements: UniformityRequirements,\n}\n\nimpl Uniformity {\n    const fn new() -> Self {\n        Uniformity {\n            non_uniform_result: None,\n            requirements: UniformityRequirements::empty(),\n        }\n    }\n}\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, Debug, PartialEq)]\n    struct ExitFlags: u8 {\n        /// Control flow may return from the function, which makes all the\n        /// subsequent statements within the current function (only!)\n        /// to be executed in a non-uniform control flow.\n        const MAY_RETURN = 0x1;\n        /// Control flow may be killed. Anything after [`Statement::Kill`] is\n        /// considered inside non-uniform context.\n        ///\n        /// [`Statement::Kill`]: crate::Statement::Kill\n        const MAY_KILL = 0x2;\n    }\n}\n\n/// Uniformity characteristics of a function.\n#[cfg_attr(test, derive(Debug, PartialEq))]\nstruct FunctionUniformity {\n    result: Uniformity,\n    exit: ExitFlags,\n}\n\nimpl ops::BitOr for FunctionUniformity {\n    type Output = Self;\n    fn bitor(self, other: Self) -> Self {\n        FunctionUniformity {\n            result: Uniformity {\n                non_uniform_result: self\n                    .result\n                    .non_uniform_result\n                    .or(other.result.non_uniform_result),\n                requirements: self.result.requirements | other.result.requirements,\n            },\n            exit: self.exit | other.exit,\n        }\n    }\n}\n\nimpl FunctionUniformity {\n    const fn new() -> Self {\n        FunctionUniformity {\n            result: Uniformity::new(),\n            exit: ExitFlags::empty(),\n        }\n    }\n\n    /// Returns a disruptor based on the stored exit flags, if any.\n    const fn exit_disruptor(&self) -> Option<UniformityDisruptor> {\n        if self.exit.contains(ExitFlags::MAY_RETURN) {\n            Some(UniformityDisruptor::Return)\n        } else if self.exit.contains(ExitFlags::MAY_KILL) {\n            Some(UniformityDisruptor::Discard)\n        } else {\n            None\n        }\n    }\n}\n\nbitflags::bitflags! {\n    /// Indicates how a global variable is used.\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct GlobalUse: u8 {\n        /// Data will be read from the variable.\n        const READ = 0x1;\n        /// Data will be written to the variable.\n        const WRITE = 0x2;\n        /// The information about the data is queried.\n        const QUERY = 0x4;\n        /// Atomic operations will be performed on the variable.\n        const ATOMIC = 0x8;\n    }\n}\n\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct SamplingKey {\n    pub image: Handle<crate::GlobalVariable>,\n    pub sampler: Handle<crate::GlobalVariable>,\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n/// Information about an expression in a function body.\npub struct ExpressionInfo {\n    /// Whether this expression is uniform, and why.\n    ///\n    /// If this expression's value is not uniform, this is the handle\n    /// of the expression from which this one's non-uniformity\n    /// originates. Otherwise, this is `None`.\n    pub uniformity: Uniformity,\n\n    /// The number of direct references to this expression in statements and\n    /// other expressions.\n    ///\n    /// This is a _local_ reference count only, it may be non-zero for\n    /// expressions that are ultimately unused.\n    pub ref_count: usize,\n\n    /// The global variable into which this expression produces a pointer.\n    ///\n    /// This is `None` unless this expression is either a\n    /// [`GlobalVariable`], or an [`Access`] or [`AccessIndex`] that\n    /// ultimately refers to some part of a global.\n    ///\n    /// [`Load`] expressions applied to pointer-typed arguments could\n    /// refer to globals, but we leave this as `None` for them.\n    ///\n    /// [`GlobalVariable`]: crate::Expression::GlobalVariable\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    /// [`Load`]: crate::Expression::Load\n    assignable_global: Option<Handle<crate::GlobalVariable>>,\n\n    /// The type of this expression.\n    pub ty: TypeResolution,\n}\n\nimpl ExpressionInfo {\n    const fn new() -> Self {\n        ExpressionInfo {\n            uniformity: Uniformity::new(),\n            ref_count: 0,\n            assignable_global: None,\n            // this doesn't matter at this point, will be overwritten\n            ty: TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Bool,\n                width: 0,\n            })),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\nenum GlobalOrArgument {\n    Global(Handle<crate::GlobalVariable>),\n    Argument(u32),\n}\n\nimpl GlobalOrArgument {\n    fn from_expression(\n        expression_arena: &Arena<crate::Expression>,\n        expression: Handle<crate::Expression>,\n    ) -> Result<GlobalOrArgument, ExpressionError> {\n        Ok(match expression_arena[expression] {\n            crate::Expression::GlobalVariable(var) => GlobalOrArgument::Global(var),\n            crate::Expression::FunctionArgument(i) => GlobalOrArgument::Argument(i),\n            crate::Expression::Access { base, .. }\n            | crate::Expression::AccessIndex { base, .. } => match expression_arena[base] {\n                crate::Expression::GlobalVariable(var) => GlobalOrArgument::Global(var),\n                _ => return Err(ExpressionError::ExpectedGlobalOrArgument),\n            },\n            _ => return Err(ExpressionError::ExpectedGlobalOrArgument),\n        })\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\nstruct Sampling {\n    image: GlobalOrArgument,\n    sampler: GlobalOrArgument,\n}\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct FunctionInfo {\n    /// Validation flags.\n    flags: ValidationFlags,\n    /// Set of shader stages where calling this function is valid.\n    pub available_stages: ShaderStages,\n    /// Uniformity characteristics.\n    pub uniformity: Uniformity,\n    /// Function may kill the invocation.\n    pub may_kill: bool,\n\n    /// All pairs of (texture, sampler) globals that may be used together in\n    /// sampling operations by this function and its callees. This includes\n    /// pairings that arise when this function passes textures and samplers as\n    /// arguments to its callees.\n    ///\n    /// This table does not include uses of textures and samplers passed as\n    /// arguments to this function itself, since we do not know which globals\n    /// those will be. However, this table *is* exhaustive when computed for an\n    /// entry point function: entry points never receive textures or samplers as\n    /// arguments, so all an entry point's sampling can be reported in terms of\n    /// globals.\n    ///\n    /// The GLSL back end uses this table to construct reflection info that\n    /// clients need to construct texture-combined sampler values.\n    pub sampling_set: crate::FastHashSet<SamplingKey>,\n\n    /// How this function and its callees use this module's globals.\n    ///\n    /// This is indexed by `Handle<GlobalVariable>` indices. However,\n    /// `FunctionInfo` implements `core::ops::Index<Handle<GlobalVariable>>`,\n    /// so you can simply index this struct with a global handle to retrieve\n    /// its usage information.\n    pub global_uses: Box<[GlobalUse]>,\n\n    /// Information about each expression in this function's body.\n    ///\n    /// This is indexed by `Handle<Expression>` indices. However, `FunctionInfo`\n    /// implements `core::ops::Index<Handle<Expression>>`, so you can simply\n    /// index this struct with an expression handle to retrieve its\n    /// `ExpressionInfo`.\n    expressions: Box<[ExpressionInfo]>,\n\n    /// All (texture, sampler) pairs that may be used together in sampling\n    /// operations by this function and its callees, whether they are accessed\n    /// as globals or passed as arguments.\n    ///\n    /// Participants are represented by [`GlobalVariable`] handles whenever\n    /// possible, and otherwise by indices of this function's arguments.\n    ///\n    /// When analyzing a function call, we combine this data about the callee\n    /// with the actual arguments being passed to produce the callers' own\n    /// `sampling_set` and `sampling` tables.\n    ///\n    /// [`GlobalVariable`]: crate::GlobalVariable\n    sampling: crate::FastHashSet<Sampling>,\n\n    /// Indicates that the function is using dual source blending.\n    pub dual_source_blending: bool,\n\n    /// The leaf of all module-wide diagnostic filter rules tree parsed from directives in this\n    /// module.\n    ///\n    /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in\n    /// validation.\n    diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,\n}\n\nimpl FunctionInfo {\n    pub const fn global_variable_count(&self) -> usize {\n        self.global_uses.len()\n    }\n    pub const fn expression_count(&self) -> usize {\n        self.expressions.len()\n    }\n    pub fn dominates_global_use(&self, other: &Self) -> bool {\n        for (self_global_uses, other_global_uses) in\n            self.global_uses.iter().zip(other.global_uses.iter())\n        {\n            if !self_global_uses.contains(*other_global_uses) {\n                return false;\n            }\n        }\n        true\n    }\n}\n\nimpl ops::Index<Handle<crate::GlobalVariable>> for FunctionInfo {\n    type Output = GlobalUse;\n    fn index(&self, handle: Handle<crate::GlobalVariable>) -> &GlobalUse {\n        &self.global_uses[handle.index()]\n    }\n}\n\nimpl ops::Index<Handle<crate::Expression>> for FunctionInfo {\n    type Output = ExpressionInfo;\n    fn index(&self, handle: Handle<crate::Expression>) -> &ExpressionInfo {\n        &self.expressions[handle.index()]\n    }\n}\n\n/// Disruptor of the uniform control flow.\n#[derive(Clone, Copy, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum UniformityDisruptor {\n    #[error(\"Expression {0:?} produced non-uniform result, and control flow depends on it\")]\n    Expression(Handle<crate::Expression>),\n    #[error(\"There is a Return earlier in the control flow of the function\")]\n    Return,\n    #[error(\"There is a Discard earlier in the entry point across all called functions\")]\n    Discard,\n}\n\nimpl FunctionInfo {\n    /// Record a use of `expr` of the sort given by `global_use`.\n    ///\n    /// Bump `expr`'s reference count, and return its uniformity.\n    ///\n    /// If `expr` is a pointer to a global variable, or some part of\n    /// a global variable, add `global_use` to that global's set of\n    /// uses.\n    #[must_use]\n    fn add_ref_impl(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        global_use: GlobalUse,\n    ) -> NonUniformResult {\n        let info = &mut self.expressions[expr.index()];\n        info.ref_count += 1;\n        // Record usage if this expression may access a global\n        if let Some(global) = info.assignable_global {\n            self.global_uses[global.index()] |= global_use;\n        }\n        info.uniformity.non_uniform_result\n    }\n\n    /// Note an entry point's use of `global` not recorded by [`ModuleInfo::process_function`].\n    ///\n    /// Most global variable usage should be recorded via [`add_ref_impl`] in the process\n    /// of expression behavior analysis by [`ModuleInfo::process_function`]. But that code\n    /// has no access to entrypoint-specific information, so interface analysis uses this\n    /// function to record global uses there (like task shader payloads).\n    ///\n    /// [`add_ref_impl`]: Self::add_ref_impl\n    pub(super) fn insert_global_use(\n        &mut self,\n        global_use: GlobalUse,\n        global: Handle<crate::GlobalVariable>,\n    ) {\n        self.global_uses[global.index()] |= global_use;\n    }\n\n    /// Record a use of `expr` for its value.\n    ///\n    /// This is used for almost all expression references. Anything\n    /// that writes to the value `expr` points to, or otherwise wants\n    /// contribute flags other than `GlobalUse::READ`, should use\n    /// `add_ref_impl` directly.\n    #[must_use]\n    fn add_ref(&mut self, expr: Handle<crate::Expression>) -> NonUniformResult {\n        self.add_ref_impl(expr, GlobalUse::READ)\n    }\n\n    /// Record a use of `expr`, and indicate which global variable it\n    /// refers to, if any.\n    ///\n    /// Bump `expr`'s reference count, and return its uniformity.\n    ///\n    /// If `expr` is a pointer to a global variable, or some part\n    /// thereof, store that global in `*assignable_global`. Leave the\n    /// global's uses unchanged.\n    ///\n    /// This is used to determine the [`assignable_global`] for\n    /// [`Access`] and [`AccessIndex`] expressions that ultimately\n    /// refer to a global variable. Those expressions don't contribute\n    /// any usage to the global themselves; that depends on how other\n    /// expressions use them.\n    ///\n    /// [`assignable_global`]: ExpressionInfo::assignable_global\n    /// [`Access`]: crate::Expression::Access\n    /// [`AccessIndex`]: crate::Expression::AccessIndex\n    #[must_use]\n    fn add_assignable_ref(\n        &mut self,\n        expr: Handle<crate::Expression>,\n        assignable_global: &mut Option<Handle<crate::GlobalVariable>>,\n    ) -> NonUniformResult {\n        let info = &mut self.expressions[expr.index()];\n        info.ref_count += 1;\n        // propagate the assignable global up the chain, till it either hits\n        // a value-type expression, or the assignment statement.\n        if let Some(global) = info.assignable_global {\n            if let Some(_old) = assignable_global.replace(global) {\n                unreachable!()\n            }\n        }\n        info.uniformity.non_uniform_result\n    }\n\n    /// Inherit information from a called function.\n    fn process_call(\n        &mut self,\n        callee: &Self,\n        arguments: &[Handle<crate::Expression>],\n        expression_arena: &Arena<crate::Expression>,\n    ) -> Result<FunctionUniformity, WithSpan<FunctionError>> {\n        self.sampling_set\n            .extend(callee.sampling_set.iter().cloned());\n        for sampling in callee.sampling.iter() {\n            // If the callee was passed the texture or sampler as an argument,\n            // we may now be able to determine which globals those referred to.\n            let image_storage = match sampling.image {\n                GlobalOrArgument::Global(var) => GlobalOrArgument::Global(var),\n                GlobalOrArgument::Argument(i) => {\n                    let Some(handle) = arguments.get(i as usize).cloned() else {\n                        // Argument count mismatch, will be reported later by validate_call\n                        break;\n                    };\n                    GlobalOrArgument::from_expression(expression_arena, handle).map_err(\n                        |source| {\n                            FunctionError::Expression { handle, source }\n                                .with_span_handle(handle, expression_arena)\n                        },\n                    )?\n                }\n            };\n\n            let sampler_storage = match sampling.sampler {\n                GlobalOrArgument::Global(var) => GlobalOrArgument::Global(var),\n                GlobalOrArgument::Argument(i) => {\n                    let Some(handle) = arguments.get(i as usize).cloned() else {\n                        // Argument count mismatch, will be reported later by validate_call\n                        break;\n                    };\n                    GlobalOrArgument::from_expression(expression_arena, handle).map_err(\n                        |source| {\n                            FunctionError::Expression { handle, source }\n                                .with_span_handle(handle, expression_arena)\n                        },\n                    )?\n                }\n            };\n\n            // If we've managed to pin both the image and sampler down to\n            // specific globals, record that in our `sampling_set`. Otherwise,\n            // record as much as we do know in our own `sampling` table, for our\n            // callers to sort out.\n            match (image_storage, sampler_storage) {\n                (GlobalOrArgument::Global(image), GlobalOrArgument::Global(sampler)) => {\n                    self.sampling_set.insert(SamplingKey { image, sampler });\n                }\n                (image, sampler) => {\n                    self.sampling.insert(Sampling { image, sampler });\n                }\n            }\n        }\n\n        // Inherit global use from our callees.\n        for (mine, other) in self.global_uses.iter_mut().zip(callee.global_uses.iter()) {\n            *mine |= *other;\n        }\n\n        Ok(FunctionUniformity {\n            result: callee.uniformity.clone(),\n            exit: if callee.may_kill {\n                ExitFlags::MAY_KILL\n            } else {\n                ExitFlags::empty()\n            },\n        })\n    }\n\n    /// Compute the [`ExpressionInfo`] for `handle`.\n    ///\n    /// Replace the dummy entry in [`self.expressions`] for `handle`\n    /// with a real `ExpressionInfo` value describing that expression.\n    ///\n    /// This function is called as part of a forward sweep through the\n    /// arena, so we can assume that all earlier expressions in the\n    /// arena already have valid info. Since expressions only depend\n    /// on earlier expressions, this includes all our subexpressions.\n    ///\n    /// Adjust the reference counts on all expressions we use.\n    ///\n    /// Also populate the [`sampling_set`], [`sampling`] and\n    /// [`global_uses`] fields of `self`.\n    ///\n    /// [`self.expressions`]: FunctionInfo::expressions\n    /// [`sampling_set`]: FunctionInfo::sampling_set\n    /// [`sampling`]: FunctionInfo::sampling\n    /// [`global_uses`]: FunctionInfo::global_uses\n    #[allow(clippy::or_fun_call)]\n    fn process_expression(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        expression_arena: &Arena<crate::Expression>,\n        other_functions: &[FunctionInfo],\n        resolve_context: &ResolveContext,\n        capabilities: super::Capabilities,\n    ) -> Result<(), ExpressionError> {\n        use crate::{Expression as E, SampleLevel as Sl};\n\n        let expression = &expression_arena[handle];\n        let mut assignable_global = None;\n        let uniformity = match *expression {\n            E::Access { base, index } => {\n                let base_ty = self[base].ty.inner_with(resolve_context.types);\n\n                // build up the caps needed if this is indexed non-uniformly\n                let mut needed_caps = super::Capabilities::empty();\n                let is_binding_array = match *base_ty {\n                    crate::TypeInner::BindingArray {\n                        base: array_element_ty_handle,\n                        ..\n                    } => {\n                        // We're a binding array, so lets use the type of _what_ we are array of to determine if we can non-uniformly index it.\n                        let array_element_ty =\n                            &resolve_context.types[array_element_ty_handle].inner;\n\n                        needed_caps |= match *array_element_ty {\n                            // If we're an image, use the appropriate capability.\n                            crate::TypeInner::Image { class, .. } => match class {\n                                crate::ImageClass::Storage { .. } => {\n                                    super::Capabilities::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING\n                                }\n                                _ => {\n                                    super::Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n                                }\n                            },\n                            crate::TypeInner::Sampler { .. } => {\n                                super::Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n                            }\n                            // If we're anything but an image or sampler, assume we're a buffer and use the address space.\n                            _ => {\n                                if let E::GlobalVariable(global_handle) = expression_arena[base] {\n                                    let global = &resolve_context.global_vars[global_handle];\n                                    match global.space {\n                                        crate::AddressSpace::Uniform => {\n                                            super::Capabilities::BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n                                        }\n                                        crate::AddressSpace::Storage { .. } => {\n                                            super::Capabilities::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n                                        }\n                                        _ => unreachable!(),\n                                    }\n                                } else {\n                                    unreachable!()\n                                }\n                            }\n                        };\n\n                        true\n                    }\n                    _ => false,\n                };\n\n                if self[index].uniformity.non_uniform_result.is_some()\n                    && !capabilities.contains(needed_caps)\n                    && is_binding_array\n                {\n                    return Err(ExpressionError::MissingCapabilities(needed_caps));\n                }\n\n                Uniformity {\n                    non_uniform_result: self\n                        .add_assignable_ref(base, &mut assignable_global)\n                        .or(self.add_ref(index)),\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            E::AccessIndex { base, .. } => Uniformity {\n                non_uniform_result: self.add_assignable_ref(base, &mut assignable_global),\n                requirements: UniformityRequirements::empty(),\n            },\n            // always uniform\n            E::Splat { size: _, value } => Uniformity {\n                non_uniform_result: self.add_ref(value),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Swizzle { vector, .. } => Uniformity {\n                non_uniform_result: self.add_ref(vector),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Literal(_) | E::Constant(_) | E::Override(_) | E::ZeroValue(_) => Uniformity::new(),\n            E::Compose { ref components, .. } => {\n                let non_uniform_result = components\n                    .iter()\n                    .fold(None, |nur, &comp| nur.or(self.add_ref(comp)));\n                Uniformity {\n                    non_uniform_result,\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            // depends on the builtin\n            E::FunctionArgument(index) => {\n                let arg = &resolve_context.arguments[index as usize];\n                let uniform = match arg.binding {\n                    Some(crate::Binding::BuiltIn(\n                        // per-work-group built-ins are uniform\n                        crate::BuiltIn::WorkGroupId\n                        | crate::BuiltIn::WorkGroupSize\n                        | crate::BuiltIn::NumWorkGroups,\n                    )) => true,\n                    _ => false,\n                };\n                Uniformity {\n                    non_uniform_result: if uniform { None } else { Some(handle) },\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            // depends on the address space\n            E::GlobalVariable(gh) => {\n                use crate::AddressSpace as As;\n                assignable_global = Some(gh);\n                let var = &resolve_context.global_vars[gh];\n                let uniform = match var.space {\n                    // local data is non-uniform\n                    As::Function | As::Private | As::RayPayload | As::IncomingRayPayload => false,\n                    // workgroup memory is exclusively accessed by the group\n                    // task payload memory is very similar to workgroup memory\n                    As::WorkGroup | As::TaskPayload => true,\n                    // uniform data\n                    As::Uniform | As::Immediate => true,\n                    // storage data is only uniform when read-only\n                    As::Storage { access } => !access.contains(crate::StorageAccess::STORE),\n                    As::Handle => false,\n                };\n                Uniformity {\n                    non_uniform_result: if uniform { None } else { Some(handle) },\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            E::LocalVariable(_) => Uniformity {\n                non_uniform_result: Some(handle),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Load { pointer } => Uniformity {\n                non_uniform_result: self.add_ref(pointer),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::ImageSample {\n                image,\n                sampler,\n                gather: _,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                let image_storage = GlobalOrArgument::from_expression(expression_arena, image)?;\n                let sampler_storage = GlobalOrArgument::from_expression(expression_arena, sampler)?;\n\n                match (image_storage, sampler_storage) {\n                    (GlobalOrArgument::Global(image), GlobalOrArgument::Global(sampler)) => {\n                        self.sampling_set.insert(SamplingKey { image, sampler });\n                    }\n                    _ => {\n                        self.sampling.insert(Sampling {\n                            image: image_storage,\n                            sampler: sampler_storage,\n                        });\n                    }\n                }\n\n                // \"nur\" == \"Non-Uniform Result\"\n                let array_nur = array_index.and_then(|h| self.add_ref(h));\n                let level_nur = match level {\n                    Sl::Auto | Sl::Zero => None,\n                    Sl::Exact(h) | Sl::Bias(h) => self.add_ref(h),\n                    Sl::Gradient { x, y } => self.add_ref(x).or(self.add_ref(y)),\n                };\n                let dref_nur = depth_ref.and_then(|h| self.add_ref(h));\n                let offset_nur = offset.and_then(|h| self.add_ref(h));\n                Uniformity {\n                    non_uniform_result: self\n                        .add_ref(image)\n                        .or(self.add_ref(sampler))\n                        .or(self.add_ref(coordinate))\n                        .or(array_nur)\n                        .or(level_nur)\n                        .or(dref_nur)\n                        .or(offset_nur),\n                    requirements: if level.implicit_derivatives() {\n                        UniformityRequirements::IMPLICIT_LEVEL\n                    } else {\n                        UniformityRequirements::empty()\n                    },\n                }\n            }\n            E::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                let array_nur = array_index.and_then(|h| self.add_ref(h));\n                let sample_nur = sample.and_then(|h| self.add_ref(h));\n                let level_nur = level.and_then(|h| self.add_ref(h));\n                Uniformity {\n                    non_uniform_result: self\n                        .add_ref(image)\n                        .or(self.add_ref(coordinate))\n                        .or(array_nur)\n                        .or(sample_nur)\n                        .or(level_nur),\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            E::ImageQuery { image, query } => {\n                let query_nur = match query {\n                    crate::ImageQuery::Size { level: Some(h) } => self.add_ref(h),\n                    _ => None,\n                };\n                Uniformity {\n                    non_uniform_result: self.add_ref_impl(image, GlobalUse::QUERY).or(query_nur),\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            E::Unary { expr, .. } => Uniformity {\n                non_uniform_result: self.add_ref(expr),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Binary { left, right, .. } => Uniformity {\n                non_uniform_result: self.add_ref(left).or(self.add_ref(right)),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Select {\n                condition,\n                accept,\n                reject,\n            } => Uniformity {\n                non_uniform_result: self\n                    .add_ref(condition)\n                    .or(self.add_ref(accept))\n                    .or(self.add_ref(reject)),\n                requirements: UniformityRequirements::empty(),\n            },\n            // explicit derivatives require uniform\n            E::Derivative { expr, .. } => Uniformity {\n                //Note: taking a derivative of a uniform doesn't make it non-uniform\n                non_uniform_result: self.add_ref(expr),\n                requirements: UniformityRequirements::DERIVATIVE,\n            },\n            E::Relational { argument, .. } => Uniformity {\n                non_uniform_result: self.add_ref(argument),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::Math {\n                fun: _,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                let arg1_nur = arg1.and_then(|h| self.add_ref(h));\n                let arg2_nur = arg2.and_then(|h| self.add_ref(h));\n                let arg3_nur = arg3.and_then(|h| self.add_ref(h));\n                Uniformity {\n                    non_uniform_result: self.add_ref(arg).or(arg1_nur).or(arg2_nur).or(arg3_nur),\n                    requirements: UniformityRequirements::empty(),\n                }\n            }\n            E::As { expr, .. } => Uniformity {\n                non_uniform_result: self.add_ref(expr),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::CallResult(function) => other_functions[function.index()].uniformity.clone(),\n            E::AtomicResult { .. } | E::RayQueryProceedResult => Uniformity {\n                non_uniform_result: Some(handle),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::WorkGroupUniformLoadResult { .. } => Uniformity {\n                // The result of WorkGroupUniformLoad is always uniform by definition\n                non_uniform_result: None,\n                // The call is what cares about uniformity, not the expression\n                // This expression is never emitted, so this requirement should never be used anyway?\n                requirements: UniformityRequirements::empty(),\n            },\n            E::ArrayLength(expr) => Uniformity {\n                non_uniform_result: self.add_ref_impl(expr, GlobalUse::QUERY),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::RayQueryGetIntersection {\n                query,\n                committed: _,\n            } => Uniformity {\n                non_uniform_result: self.add_ref(query),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::SubgroupBallotResult => Uniformity {\n                non_uniform_result: Some(handle),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::SubgroupOperationResult { .. } => Uniformity {\n                non_uniform_result: Some(handle),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::RayQueryVertexPositions {\n                query,\n                committed: _,\n            } => Uniformity {\n                non_uniform_result: self.add_ref(query),\n                requirements: UniformityRequirements::empty(),\n            },\n            E::CooperativeLoad { ref data, .. } => Uniformity {\n                non_uniform_result: self.add_ref(data.pointer).or(self.add_ref(data.stride)),\n                requirements: UniformityRequirements::COOP_OPS,\n            },\n            E::CooperativeMultiplyAdd { a, b, c } => Uniformity {\n                non_uniform_result: self.add_ref(a).or(self.add_ref(b).or(self.add_ref(c))),\n                requirements: UniformityRequirements::COOP_OPS,\n            },\n        };\n\n        let ty = resolve_context.resolve(expression, |h| Ok(&self[h].ty))?;\n        self.expressions[handle.index()] = ExpressionInfo {\n            uniformity,\n            ref_count: 0,\n            assignable_global,\n            ty,\n        };\n        Ok(())\n    }\n\n    /// Analyzes the uniformity requirements of a block (as a sequence of statements).\n    /// Returns the uniformity characteristics at the *function* level, i.e.\n    /// whether or not the function requires to be called in uniform control flow,\n    /// and whether the produced result is not disrupting the control flow.\n    ///\n    /// The parent control flow is uniform if `disruptor.is_none()`.\n    ///\n    /// Returns a `NonUniformControlFlow` error if any of the expressions in the block\n    /// require uniformity, but the current flow is non-uniform.\n    #[allow(clippy::or_fun_call)]\n    fn process_block(\n        &mut self,\n        statements: &crate::Block,\n        other_functions: &[FunctionInfo],\n        mut disruptor: Option<UniformityDisruptor>,\n        expression_arena: &Arena<crate::Expression>,\n        diagnostic_filter_arena: &Arena<DiagnosticFilterNode>,\n    ) -> Result<FunctionUniformity, WithSpan<FunctionError>> {\n        use crate::Statement as S;\n\n        let mut combined_uniformity = FunctionUniformity::new();\n        for statement in statements {\n            let uniformity = match *statement {\n                S::Emit(ref range) => {\n                    let mut requirements = UniformityRequirements::empty();\n                    for expr in range.clone() {\n                        let req = self.expressions[expr.index()].uniformity.requirements;\n                        if self\n                            .flags\n                            .contains(ValidationFlags::CONTROL_FLOW_UNIFORMITY)\n                            && !req.is_empty()\n                        {\n                            if let Some(cause) = disruptor {\n                                let severity = DiagnosticFilterNode::search(\n                                    self.diagnostic_filter_leaf,\n                                    diagnostic_filter_arena,\n                                    StandardFilterableTriggeringRule::DerivativeUniformity,\n                                );\n                                severity.report_diag(\n                                    FunctionError::NonUniformControlFlow(req, expr, cause)\n                                        .with_span_handle(expr, expression_arena),\n                                    // TODO: Yes, this isn't contextualized with source, because\n                                    // the user is supposed to render what would normally be an\n                                    // error here. Once we actually support warning-level\n                                    // diagnostic items, then we won't need this non-compliant hack:\n                                    // <https://github.com/gfx-rs/wgpu/issues/6458>\n                                    |e, level| log::log!(level, \"{e}\"),\n                                )?;\n                            }\n                        }\n                        requirements |= req;\n                    }\n                    FunctionUniformity {\n                        result: Uniformity {\n                            non_uniform_result: None,\n                            requirements,\n                        },\n                        exit: ExitFlags::empty(),\n                    }\n                }\n                S::Break | S::Continue => FunctionUniformity::new(),\n                S::Kill => FunctionUniformity {\n                    result: Uniformity::new(),\n                    exit: if disruptor.is_some() {\n                        ExitFlags::MAY_KILL\n                    } else {\n                        ExitFlags::empty()\n                    },\n                },\n                S::ControlBarrier(_) | S::MemoryBarrier(_) => FunctionUniformity {\n                    result: Uniformity {\n                        non_uniform_result: None,\n                        requirements: UniformityRequirements::WORK_GROUP_BARRIER,\n                    },\n                    exit: ExitFlags::empty(),\n                },\n                S::WorkGroupUniformLoad { pointer, .. } => {\n                    let _condition_nur = self.add_ref(pointer);\n\n                    // Don't check that this call occurs in uniform control flow until Naga implements WGSL's standard\n                    // uniformity analysis (https://github.com/gfx-rs/naga/issues/1744).\n                    // The uniformity analysis Naga uses now is less accurate than the one in the WGSL standard,\n                    // causing Naga to reject correct uses of `workgroupUniformLoad` in some interesting programs.\n\n                    /*\n                    if self\n                        .flags\n                        .contains(super::ValidationFlags::CONTROL_FLOW_UNIFORMITY)\n                    {\n                        let condition_nur = self.add_ref(pointer);\n                        let this_disruptor =\n                            disruptor.or(condition_nur.map(UniformityDisruptor::Expression));\n                        if let Some(cause) = this_disruptor {\n                            return Err(FunctionError::NonUniformWorkgroupUniformLoad(cause)\n                                .with_span_static(*span, \"WorkGroupUniformLoad\"));\n                        }\n                    } */\n                    FunctionUniformity {\n                        result: Uniformity {\n                            non_uniform_result: None,\n                            requirements: UniformityRequirements::WORK_GROUP_BARRIER,\n                        },\n                        exit: ExitFlags::empty(),\n                    }\n                }\n                S::Block(ref b) => self.process_block(\n                    b,\n                    other_functions,\n                    disruptor,\n                    expression_arena,\n                    diagnostic_filter_arena,\n                )?,\n                S::If {\n                    condition,\n                    ref accept,\n                    ref reject,\n                } => {\n                    let condition_nur = self.add_ref(condition);\n                    let branch_disruptor =\n                        disruptor.or(condition_nur.map(UniformityDisruptor::Expression));\n                    let accept_uniformity = self.process_block(\n                        accept,\n                        other_functions,\n                        branch_disruptor,\n                        expression_arena,\n                        diagnostic_filter_arena,\n                    )?;\n                    let reject_uniformity = self.process_block(\n                        reject,\n                        other_functions,\n                        branch_disruptor,\n                        expression_arena,\n                        diagnostic_filter_arena,\n                    )?;\n                    accept_uniformity | reject_uniformity\n                }\n                S::Switch {\n                    selector,\n                    ref cases,\n                } => {\n                    let selector_nur = self.add_ref(selector);\n                    let branch_disruptor =\n                        disruptor.or(selector_nur.map(UniformityDisruptor::Expression));\n                    let mut uniformity = FunctionUniformity::new();\n                    let mut case_disruptor = branch_disruptor;\n                    for case in cases.iter() {\n                        let case_uniformity = self.process_block(\n                            &case.body,\n                            other_functions,\n                            case_disruptor,\n                            expression_arena,\n                            diagnostic_filter_arena,\n                        )?;\n                        case_disruptor = if case.fall_through {\n                            case_disruptor.or(case_uniformity.exit_disruptor())\n                        } else {\n                            branch_disruptor\n                        };\n                        uniformity = uniformity | case_uniformity;\n                    }\n                    uniformity\n                }\n                S::Loop {\n                    ref body,\n                    ref continuing,\n                    break_if,\n                } => {\n                    let body_uniformity = self.process_block(\n                        body,\n                        other_functions,\n                        disruptor,\n                        expression_arena,\n                        diagnostic_filter_arena,\n                    )?;\n                    let continuing_disruptor = disruptor.or(body_uniformity.exit_disruptor());\n                    let continuing_uniformity = self.process_block(\n                        continuing,\n                        other_functions,\n                        continuing_disruptor,\n                        expression_arena,\n                        diagnostic_filter_arena,\n                    )?;\n                    if let Some(expr) = break_if {\n                        let _ = self.add_ref(expr);\n                    }\n                    body_uniformity | continuing_uniformity\n                }\n                S::Return { value } => FunctionUniformity {\n                    result: Uniformity {\n                        non_uniform_result: value.and_then(|expr| self.add_ref(expr)),\n                        requirements: UniformityRequirements::empty(),\n                    },\n                    exit: if disruptor.is_some() {\n                        ExitFlags::MAY_RETURN\n                    } else {\n                        ExitFlags::empty()\n                    },\n                },\n                // Here and below, the used expressions are already emitted,\n                // and their results do not affect the function return value,\n                // so we can ignore their non-uniformity.\n                S::Store { pointer, value } => {\n                    let _ = self.add_ref_impl(pointer, GlobalUse::WRITE);\n                    let _ = self.add_ref(value);\n                    FunctionUniformity::new()\n                }\n                S::ImageStore {\n                    image,\n                    coordinate,\n                    array_index,\n                    value,\n                } => {\n                    let _ = self.add_ref_impl(image, GlobalUse::WRITE);\n                    if let Some(expr) = array_index {\n                        let _ = self.add_ref(expr);\n                    }\n                    let _ = self.add_ref(coordinate);\n                    let _ = self.add_ref(value);\n                    FunctionUniformity::new()\n                }\n                S::Call {\n                    function,\n                    ref arguments,\n                    result: _,\n                } => {\n                    for &argument in arguments {\n                        let _ = self.add_ref(argument);\n                    }\n                    let info = &other_functions[function.index()];\n                    //Note: the result is validated by the Validator, not here\n                    self.process_call(info, arguments, expression_arena)?\n                }\n                S::Atomic {\n                    pointer,\n                    ref fun,\n                    value,\n                    result: _,\n                } => {\n                    let _ = self.add_ref_impl(pointer, GlobalUse::READ | GlobalUse::WRITE);\n                    let _ = self.add_ref(value);\n                    if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {\n                        let _ = self.add_ref(cmp);\n                    }\n                    FunctionUniformity::new()\n                }\n                S::ImageAtomic {\n                    image,\n                    coordinate,\n                    array_index,\n                    fun: _,\n                    value,\n                } => {\n                    let _ = self.add_ref_impl(image, GlobalUse::ATOMIC);\n                    let _ = self.add_ref(coordinate);\n                    if let Some(expr) = array_index {\n                        let _ = self.add_ref(expr);\n                    }\n                    let _ = self.add_ref(value);\n                    FunctionUniformity::new()\n                }\n                S::RayQuery { query, ref fun } => {\n                    let _ = self.add_ref(query);\n                    match *fun {\n                        crate::RayQueryFunction::Initialize {\n                            acceleration_structure,\n                            descriptor,\n                        } => {\n                            let _ = self.add_ref(acceleration_structure);\n                            let _ = self.add_ref(descriptor);\n                        }\n                        crate::RayQueryFunction::Proceed { result: _ } => {}\n                        crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                            let _ = self.add_ref(hit_t);\n                        }\n                        crate::RayQueryFunction::ConfirmIntersection => {}\n                        crate::RayQueryFunction::Terminate => {}\n                    }\n                    FunctionUniformity::new()\n                }\n                S::SubgroupBallot {\n                    result: _,\n                    predicate,\n                } => {\n                    if let Some(predicate) = predicate {\n                        let _ = self.add_ref(predicate);\n                    }\n                    FunctionUniformity::new()\n                }\n                S::SubgroupCollectiveOperation {\n                    op: _,\n                    collective_op: _,\n                    argument,\n                    result: _,\n                } => {\n                    let _ = self.add_ref(argument);\n                    FunctionUniformity::new()\n                }\n                S::SubgroupGather {\n                    mode,\n                    argument,\n                    result: _,\n                } => {\n                    let _ = self.add_ref(argument);\n                    match mode {\n                        crate::GatherMode::BroadcastFirst => {}\n                        crate::GatherMode::Broadcast(index)\n                        | crate::GatherMode::Shuffle(index)\n                        | crate::GatherMode::ShuffleDown(index)\n                        | crate::GatherMode::ShuffleUp(index)\n                        | crate::GatherMode::ShuffleXor(index)\n                        | crate::GatherMode::QuadBroadcast(index) => {\n                            let _ = self.add_ref(index);\n                        }\n                        crate::GatherMode::QuadSwap(_) => {}\n                    }\n                    FunctionUniformity::new()\n                }\n                S::CooperativeStore { target, ref data } => FunctionUniformity {\n                    result: Uniformity {\n                        non_uniform_result: self\n                            .add_ref(target)\n                            .or(self.add_ref_impl(data.pointer, GlobalUse::WRITE))\n                            .or(self.add_ref(data.stride)),\n                        requirements: UniformityRequirements::COOP_OPS,\n                    },\n                    exit: ExitFlags::empty(),\n                },\n                S::RayPipelineFunction(ref fun) => {\n                    match *fun {\n                        crate::RayPipelineFunction::TraceRay {\n                            acceleration_structure,\n                            descriptor,\n                            payload,\n                        } => {\n                            let _ = self.add_ref(acceleration_structure);\n                            let _ = self.add_ref(descriptor);\n                            let _ = self.add_ref(payload);\n                        }\n                    }\n                    FunctionUniformity::new()\n                }\n            };\n\n            disruptor = disruptor.or(uniformity.exit_disruptor());\n            combined_uniformity = combined_uniformity | uniformity;\n        }\n        Ok(combined_uniformity)\n    }\n}\n\nimpl ModuleInfo {\n    /// Populates `self.const_expression_types`\n    pub(super) fn process_const_expression(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        resolve_context: &ResolveContext,\n        gctx: crate::proc::GlobalCtx,\n    ) -> Result<(), super::ConstExpressionError> {\n        self.const_expression_types[handle.index()] =\n            resolve_context.resolve(&gctx.global_expressions[handle], |h| Ok(&self[h]))?;\n        Ok(())\n    }\n\n    /// Builds the `FunctionInfo` based on the function, and validates the\n    /// uniform control flow if required by the expressions of this function.\n    pub(super) fn process_function(\n        &self,\n        fun: &crate::Function,\n        module: &crate::Module,\n        flags: ValidationFlags,\n        capabilities: super::Capabilities,\n    ) -> Result<FunctionInfo, WithSpan<FunctionError>> {\n        let mut info = FunctionInfo {\n            flags,\n            available_stages: ShaderStages::all(),\n            uniformity: Uniformity::new(),\n            may_kill: false,\n            sampling_set: crate::FastHashSet::default(),\n            global_uses: vec![GlobalUse::empty(); module.global_variables.len()].into_boxed_slice(),\n            expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(),\n            sampling: crate::FastHashSet::default(),\n            dual_source_blending: false,\n            diagnostic_filter_leaf: fun.diagnostic_filter_leaf,\n        };\n        let resolve_context =\n            ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments);\n\n        for (handle, _) in fun.expressions.iter() {\n            if let Err(source) = info.process_expression(\n                handle,\n                &fun.expressions,\n                &self.functions,\n                &resolve_context,\n                capabilities,\n            ) {\n                return Err(FunctionError::Expression { handle, source }\n                    .with_span_handle(handle, &fun.expressions));\n            }\n        }\n\n        for (_, expr) in fun.local_variables.iter() {\n            if let Some(init) = expr.init {\n                let _ = info.add_ref(init);\n            }\n        }\n\n        let uniformity = info.process_block(\n            &fun.body,\n            &self.functions,\n            None,\n            &fun.expressions,\n            &module.diagnostic_filters,\n        )?;\n        info.uniformity = uniformity.result;\n        info.may_kill = uniformity.exit.contains(ExitFlags::MAY_KILL);\n\n        // If there are any globals referenced directly by a named expression,\n        // ensure they are marked as used even if they are not referenced\n        // anywhere else. An important case where this matters is phony\n        // assignments used to include a global in the shader's resource\n        // interface. https://www.w3.org/TR/WGSL/#phony-assignment-section\n        for &handle in fun.named_expressions.keys() {\n            if let Some(global) = info[handle].assignable_global {\n                if info.global_uses[global.index()].is_empty() {\n                    info.global_uses[global.index()] = GlobalUse::QUERY;\n                }\n            }\n        }\n\n        Ok(info)\n    }\n\n    pub fn get_entry_point(&self, index: usize) -> &FunctionInfo {\n        &self.entry_points[index]\n    }\n}\n\n#[test]\nfn uniform_control_flow() {\n    use crate::{Expression as E, Statement as S};\n\n    let mut type_arena = crate::UniqueArena::new();\n    let ty = type_arena.insert(\n        crate::Type {\n            name: None,\n            inner: crate::TypeInner::Vector {\n                size: crate::VectorSize::Bi,\n                scalar: crate::Scalar::F32,\n            },\n        },\n        Default::default(),\n    );\n    let mut global_var_arena = Arena::new();\n    let non_uniform_global = global_var_arena.append(\n        crate::GlobalVariable {\n            name: None,\n            init: None,\n            ty,\n            space: crate::AddressSpace::Handle,\n            binding: None,\n            memory_decorations: crate::MemoryDecorations::empty(),\n        },\n        Default::default(),\n    );\n    let uniform_global = global_var_arena.append(\n        crate::GlobalVariable {\n            name: None,\n            init: None,\n            ty,\n            binding: None,\n            space: crate::AddressSpace::Uniform,\n            memory_decorations: crate::MemoryDecorations::empty(),\n        },\n        Default::default(),\n    );\n\n    let mut expressions = Arena::new();\n    // checks the uniform control flow\n    let constant_expr = expressions.append(E::Literal(crate::Literal::U32(0)), Default::default());\n    // checks the non-uniform control flow\n    let derivative_expr = expressions.append(\n        E::Derivative {\n            axis: crate::DerivativeAxis::X,\n            ctrl: crate::DerivativeControl::None,\n            expr: constant_expr,\n        },\n        Default::default(),\n    );\n    let emit_range_constant_derivative = expressions.range_from(0);\n    let non_uniform_global_expr =\n        expressions.append(E::GlobalVariable(non_uniform_global), Default::default());\n    let uniform_global_expr =\n        expressions.append(E::GlobalVariable(uniform_global), Default::default());\n    let emit_range_globals = expressions.range_from(2);\n\n    // checks the QUERY flag\n    let query_expr = expressions.append(E::ArrayLength(uniform_global_expr), Default::default());\n    // checks the transitive WRITE flag\n    let access_expr = expressions.append(\n        E::AccessIndex {\n            base: non_uniform_global_expr,\n            index: 1,\n        },\n        Default::default(),\n    );\n    let emit_range_query_access_globals = expressions.range_from(2);\n\n    let mut info = FunctionInfo {\n        flags: ValidationFlags::all(),\n        available_stages: ShaderStages::all(),\n        uniformity: Uniformity::new(),\n        may_kill: false,\n        sampling_set: crate::FastHashSet::default(),\n        global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(),\n        expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(),\n        sampling: crate::FastHashSet::default(),\n        dual_source_blending: false,\n        diagnostic_filter_leaf: None,\n    };\n    let resolve_context = ResolveContext {\n        constants: &Arena::new(),\n        overrides: &Arena::new(),\n        types: &type_arena,\n        special_types: &crate::SpecialTypes::default(),\n        global_vars: &global_var_arena,\n        local_vars: &Arena::new(),\n        functions: &Arena::new(),\n        arguments: &[],\n    };\n    for (handle, _) in expressions.iter() {\n        info.process_expression(\n            handle,\n            &expressions,\n            &[],\n            &resolve_context,\n            super::Capabilities::empty(),\n        )\n        .unwrap();\n    }\n    assert_eq!(info[non_uniform_global_expr].ref_count, 1);\n    assert_eq!(info[uniform_global_expr].ref_count, 1);\n    assert_eq!(info[query_expr].ref_count, 0);\n    assert_eq!(info[access_expr].ref_count, 0);\n    assert_eq!(info[non_uniform_global], GlobalUse::empty());\n    assert_eq!(info[uniform_global], GlobalUse::QUERY);\n\n    let stmt_emit1 = S::Emit(emit_range_globals.clone());\n    let stmt_if_uniform = S::If {\n        condition: uniform_global_expr,\n        accept: crate::Block::new(),\n        reject: vec![\n            S::Emit(emit_range_constant_derivative.clone()),\n            S::Store {\n                pointer: constant_expr,\n                value: derivative_expr,\n            },\n        ]\n        .into(),\n    };\n    assert_eq!(\n        info.process_block(\n            &vec![stmt_emit1, stmt_if_uniform].into(),\n            &[],\n            None,\n            &expressions,\n            &Arena::new(),\n        ),\n        Ok(FunctionUniformity {\n            result: Uniformity {\n                non_uniform_result: None,\n                requirements: UniformityRequirements::DERIVATIVE,\n            },\n            exit: ExitFlags::empty(),\n        }),\n    );\n    assert_eq!(info[constant_expr].ref_count, 2);\n    assert_eq!(info[uniform_global], GlobalUse::READ | GlobalUse::QUERY);\n\n    let stmt_emit2 = S::Emit(emit_range_globals.clone());\n    let stmt_if_non_uniform = S::If {\n        condition: non_uniform_global_expr,\n        accept: vec![\n            S::Emit(emit_range_constant_derivative),\n            S::Store {\n                pointer: constant_expr,\n                value: derivative_expr,\n            },\n        ]\n        .into(),\n        reject: crate::Block::new(),\n    };\n    {\n        let block_info = info.process_block(\n            &vec![stmt_emit2.clone(), stmt_if_non_uniform.clone()].into(),\n            &[],\n            None,\n            &expressions,\n            &Arena::new(),\n        );\n        if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE {\n            assert_eq!(info[derivative_expr].ref_count, 2);\n        } else {\n            assert_eq!(\n                block_info,\n                Err(FunctionError::NonUniformControlFlow(\n                    UniformityRequirements::DERIVATIVE,\n                    derivative_expr,\n                    UniformityDisruptor::Expression(non_uniform_global_expr)\n                )\n                .with_span()),\n            );\n            assert_eq!(info[derivative_expr].ref_count, 1);\n\n            // Test that the same thing passes when we disable the `derivative_uniformity`\n            let mut diagnostic_filters = Arena::new();\n            let diagnostic_filter_leaf = diagnostic_filters.append(\n                DiagnosticFilterNode {\n                    inner: crate::diagnostic_filter::DiagnosticFilter {\n                        new_severity: crate::diagnostic_filter::Severity::Off,\n                        triggering_rule:\n                            crate::diagnostic_filter::FilterableTriggeringRule::Standard(\n                                StandardFilterableTriggeringRule::DerivativeUniformity,\n                            ),\n                    },\n                    parent: None,\n                },\n                crate::Span::default(),\n            );\n            let mut info = FunctionInfo {\n                diagnostic_filter_leaf: Some(diagnostic_filter_leaf),\n                ..info.clone()\n            };\n\n            let block_info = info.process_block(\n                &vec![stmt_emit2, stmt_if_non_uniform].into(),\n                &[],\n                None,\n                &expressions,\n                &diagnostic_filters,\n            );\n            assert_eq!(\n                block_info,\n                Ok(FunctionUniformity {\n                    result: Uniformity {\n                        non_uniform_result: None,\n                        requirements: UniformityRequirements::DERIVATIVE,\n                    },\n                    exit: ExitFlags::empty()\n                }),\n            );\n            assert_eq!(info[derivative_expr].ref_count, 2);\n        }\n    }\n    assert_eq!(info[non_uniform_global], GlobalUse::READ);\n\n    let stmt_emit3 = S::Emit(emit_range_globals);\n    let stmt_return_non_uniform = S::Return {\n        value: Some(non_uniform_global_expr),\n    };\n    assert_eq!(\n        info.process_block(\n            &vec![stmt_emit3, stmt_return_non_uniform].into(),\n            &[],\n            Some(UniformityDisruptor::Return),\n            &expressions,\n            &Arena::new(),\n        ),\n        Ok(FunctionUniformity {\n            result: Uniformity {\n                non_uniform_result: Some(non_uniform_global_expr),\n                requirements: UniformityRequirements::empty(),\n            },\n            exit: ExitFlags::MAY_RETURN,\n        }),\n    );\n    assert_eq!(info[non_uniform_global_expr].ref_count, 3);\n\n    // Check that uniformity requirements reach through a pointer\n    let stmt_emit4 = S::Emit(emit_range_query_access_globals);\n    let stmt_assign = S::Store {\n        pointer: access_expr,\n        value: query_expr,\n    };\n    let stmt_return_pointer = S::Return {\n        value: Some(access_expr),\n    };\n    let stmt_kill = S::Kill;\n    assert_eq!(\n        info.process_block(\n            &vec![stmt_emit4, stmt_assign, stmt_kill, stmt_return_pointer].into(),\n            &[],\n            Some(UniformityDisruptor::Discard),\n            &expressions,\n            &Arena::new(),\n        ),\n        Ok(FunctionUniformity {\n            result: Uniformity {\n                non_uniform_result: Some(non_uniform_global_expr),\n                requirements: UniformityRequirements::empty(),\n            },\n            exit: ExitFlags::all(),\n        }),\n    );\n    assert_eq!(info[non_uniform_global], GlobalUse::READ | GlobalUse::WRITE);\n}\n"
  },
  {
    "path": "naga/src/valid/compose.rs",
    "content": "use crate::arena::Handle;\nuse crate::proc::TypeResolution;\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ComposeError {\n    #[error(\"Composing of type {0:?} can't be done\")]\n    Type(Handle<crate::Type>),\n    #[error(\"Composing expects {expected} components but {given} were given\")]\n    ComponentCount { given: u32, expected: u32 },\n    #[error(\"Composing {index}'s component type is not expected\")]\n    ComponentType { index: u32 },\n}\n\npub fn validate_compose(\n    self_ty_handle: Handle<crate::Type>,\n    gctx: crate::proc::GlobalCtx,\n    component_resolutions: impl ExactSizeIterator<Item = TypeResolution>,\n) -> Result<(), ComposeError> {\n    use crate::TypeInner as Ti;\n\n    match gctx.types[self_ty_handle].inner {\n        // vectors are composed from scalars or other vectors\n        Ti::Vector { size, scalar } => {\n            let mut total = 0;\n            for (index, comp_res) in component_resolutions.enumerate() {\n                total += match *comp_res.inner_with(gctx.types) {\n                    Ti::Scalar(comp_scalar) if comp_scalar == scalar => 1,\n                    Ti::Vector {\n                        size: comp_size,\n                        scalar: comp_scalar,\n                    } if comp_scalar == scalar => comp_size as u32,\n                    ref other => {\n                        log::error!(\n                            \"Vector component[{index}] type {other:?}, building {scalar:?}\"\n                        );\n                        return Err(ComposeError::ComponentType {\n                            index: index as u32,\n                        });\n                    }\n                };\n            }\n            if size as u32 != total {\n                return Err(ComposeError::ComponentCount {\n                    expected: size as u32,\n                    given: total,\n                });\n            }\n        }\n        // matrix are composed from column vectors\n        Ti::Matrix {\n            columns,\n            rows,\n            scalar,\n        } => {\n            let inner = Ti::Vector { size: rows, scalar };\n            if columns as usize != component_resolutions.len() {\n                return Err(ComposeError::ComponentCount {\n                    expected: columns as u32,\n                    given: component_resolutions.len() as u32,\n                });\n            }\n            for (index, comp_res) in component_resolutions.enumerate() {\n                if comp_res.inner_with(gctx.types) != &inner {\n                    log::error!(\"Matrix component[{index}] type {comp_res:?}\");\n                    return Err(ComposeError::ComponentType {\n                        index: index as u32,\n                    });\n                }\n            }\n        }\n        Ti::Array {\n            base,\n            size: crate::ArraySize::Constant(count),\n            stride: _,\n        } => {\n            if count.get() as usize != component_resolutions.len() {\n                return Err(ComposeError::ComponentCount {\n                    expected: count.get(),\n                    given: component_resolutions.len() as u32,\n                });\n            }\n            for (index, comp_res) in component_resolutions.enumerate() {\n                if !gctx.compare_types(&TypeResolution::Handle(base), &comp_res) {\n                    log::error!(\"Array component[{index}] type {comp_res:?}\");\n                    return Err(ComposeError::ComponentType {\n                        index: index as u32,\n                    });\n                }\n            }\n        }\n        Ti::Struct { ref members, .. } => {\n            if members.len() != component_resolutions.len() {\n                return Err(ComposeError::ComponentCount {\n                    given: component_resolutions.len() as u32,\n                    expected: members.len() as u32,\n                });\n            }\n            for (index, (member, comp_res)) in members.iter().zip(component_resolutions).enumerate()\n            {\n                if !gctx.compare_types(&TypeResolution::Handle(member.ty), &comp_res) {\n                    log::error!(\"Struct component[{index}] type {comp_res:?}\");\n                    return Err(ComposeError::ComponentType {\n                        index: index as u32,\n                    });\n                }\n            }\n        }\n        ref other => {\n            log::error!(\"Composing of {other:?}\");\n            return Err(ComposeError::Type(self_ty_handle));\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "naga/src/valid/expression.rs",
    "content": "use super::{compose::validate_compose, FunctionInfo, ModuleInfo, ShaderStages, TypeFlags};\nuse crate::arena::UniqueArena;\nuse crate::{\n    arena::Handle,\n    proc::OverloadSet as _,\n    proc::{IndexableLengthError, ResolveError},\n};\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ExpressionError {\n    #[error(\"Used by a statement before it was introduced into the scope by any of the dominating blocks\")]\n    NotInScope,\n    #[error(\"Base type {0:?} is not compatible with this expression\")]\n    InvalidBaseType(Handle<crate::Expression>),\n    #[error(\"Accessing with index {0:?} can't be done\")]\n    InvalidIndexType(Handle<crate::Expression>),\n    #[error(\"Accessing {0:?} via a negative index is invalid\")]\n    NegativeIndex(Handle<crate::Expression>),\n    #[error(\"Accessing index {1} is out of {0:?} bounds\")]\n    IndexOutOfBounds(Handle<crate::Expression>, u32),\n    #[error(\"Function argument {0:?} doesn't exist\")]\n    FunctionArgumentDoesntExist(u32),\n    #[error(\"Loading of {0:?} can't be done\")]\n    InvalidPointerType(Handle<crate::Expression>),\n    #[error(\"Array length of {0:?} can't be done\")]\n    InvalidArrayType(Handle<crate::Expression>),\n    #[error(\"Get intersection of {0:?} can't be done\")]\n    InvalidRayQueryType(Handle<crate::Expression>),\n    #[error(\"Splatting {0:?} can't be done\")]\n    InvalidSplatType(Handle<crate::Expression>),\n    #[error(\"Swizzling {0:?} can't be done\")]\n    InvalidVectorType(Handle<crate::Expression>),\n    #[error(\"Swizzle component {0:?} is outside of vector size {1:?}\")]\n    InvalidSwizzleComponent(crate::SwizzleComponent, crate::VectorSize),\n    #[error(transparent)]\n    Compose(#[from] super::ComposeError),\n    #[error(\"Cannot construct zero value of {0:?} because it is not a constructible type\")]\n    InvalidZeroValue(Handle<crate::Type>),\n    #[error(transparent)]\n    IndexableLength(#[from] IndexableLengthError),\n    #[error(\"Operation {0:?} can't work with {1:?}\")]\n    InvalidUnaryOperandType(crate::UnaryOperator, Handle<crate::Expression>),\n    #[error(\n        \"Operation {:?} can't work with {:?} (of type {:?}) and {:?} (of type {:?})\",\n        op,\n        lhs_expr,\n        lhs_type,\n        rhs_expr,\n        rhs_type\n    )]\n    InvalidBinaryOperandTypes {\n        op: crate::BinaryOperator,\n        lhs_expr: Handle<crate::Expression>,\n        lhs_type: crate::TypeInner,\n        rhs_expr: Handle<crate::Expression>,\n        rhs_type: crate::TypeInner,\n    },\n    #[error(\"Expected selection argument types to match, but reject value of type {reject:?} does not match accept value of value {accept:?}\")]\n    SelectValuesTypeMismatch {\n        accept: crate::TypeInner,\n        reject: crate::TypeInner,\n    },\n    #[error(\"Expected selection condition to be a boolean value, got {actual:?}\")]\n    SelectConditionNotABool { actual: crate::TypeInner },\n    #[error(\"Relational argument {0:?} is not a boolean vector\")]\n    InvalidBooleanVector(Handle<crate::Expression>),\n    #[error(\"Relational argument {0:?} is not a float\")]\n    InvalidFloatArgument(Handle<crate::Expression>),\n    #[error(\"Type resolution failed\")]\n    Type(#[from] ResolveError),\n    #[error(\"Not a global variable\")]\n    ExpectedGlobalVariable,\n    #[error(\"Not a global variable or a function argument\")]\n    ExpectedGlobalOrArgument,\n    #[error(\"Needs to be an binding array instead of {0:?}\")]\n    ExpectedBindingArrayType(Handle<crate::Type>),\n    #[error(\"Needs to be an image instead of {0:?}\")]\n    ExpectedImageType(Handle<crate::Type>),\n    #[error(\"Needs to be an image instead of {0:?}\")]\n    ExpectedSamplerType(Handle<crate::Type>),\n    #[error(\"Unable to operate on image class {0:?}\")]\n    InvalidImageClass(crate::ImageClass),\n    #[error(\"Image atomics are not supported for storage format {0:?}\")]\n    InvalidImageFormat(crate::StorageFormat),\n    #[error(\"Image atomics require atomic storage access, {0:?} is insufficient\")]\n    InvalidImageStorageAccess(crate::StorageAccess),\n    #[error(\"Derivatives can only be taken from scalar and vector floats\")]\n    InvalidDerivative,\n    #[error(\"Image array index parameter is misplaced\")]\n    InvalidImageArrayIndex,\n    #[error(\"Cannot textureLoad from a specific multisample sample on a non-multisampled image.\")]\n    InvalidImageSampleSelector,\n    #[error(\"Cannot textureLoad from a multisampled image without specifying a sample.\")]\n    MissingImageSampleSelector,\n    #[error(\"Cannot textureLoad with a specific mip level on a non-mipmapped image.\")]\n    InvalidImageLevelSelector,\n    #[error(\"Cannot textureLoad from a mipmapped image without specifying a level.\")]\n    MissingImageLevelSelector,\n    #[error(\"Image array index type of {0:?} is not an integer scalar\")]\n    InvalidImageArrayIndexType(Handle<crate::Expression>),\n    #[error(\"Image sample or level-of-detail index's type of {0:?} is not an integer scalar\")]\n    InvalidImageOtherIndexType(Handle<crate::Expression>),\n    #[error(\"Image coordinate type of {1:?} does not match dimension {0:?}\")]\n    InvalidImageCoordinateType(crate::ImageDimension, Handle<crate::Expression>),\n    #[error(\"Comparison sampling mismatch: image has class {image:?}, but the sampler is comparison={sampler}, and the reference was provided={has_ref}\")]\n    ComparisonSamplingMismatch {\n        image: crate::ImageClass,\n        sampler: bool,\n        has_ref: bool,\n    },\n    #[error(\"Sample offset must be a const-expression\")]\n    InvalidSampleOffsetExprType,\n    #[error(\"Sample offset constant {1:?} doesn't match the image dimension {0:?}\")]\n    InvalidSampleOffset(crate::ImageDimension, Handle<crate::Expression>),\n    #[error(\"Depth reference {0:?} is not a scalar float\")]\n    InvalidDepthReference(Handle<crate::Expression>),\n    #[error(\"Depth sample level can only be Auto or Zero\")]\n    InvalidDepthSampleLevel,\n    #[error(\"Gather level can only be Zero\")]\n    InvalidGatherLevel,\n    #[error(\"Gather component {0:?} doesn't exist in the image\")]\n    InvalidGatherComponent(crate::SwizzleComponent),\n    #[error(\"Gather can't be done for image dimension {0:?}\")]\n    InvalidGatherDimension(crate::ImageDimension),\n    #[error(\"Sample level (exact) type {0:?} has an invalid type\")]\n    InvalidSampleLevelExactType(Handle<crate::Expression>),\n    #[error(\"Sample level (bias) type {0:?} is not a scalar float\")]\n    InvalidSampleLevelBiasType(Handle<crate::Expression>),\n    #[error(\"Bias can't be done for image dimension {0:?}\")]\n    InvalidSampleLevelBiasDimension(crate::ImageDimension),\n    #[error(\"Sample level (gradient) of {1:?} doesn't match the image dimension {0:?}\")]\n    InvalidSampleLevelGradientType(crate::ImageDimension, Handle<crate::Expression>),\n    #[error(\"Clamping sample coordinate to edge is not supported with {0}\")]\n    InvalidSampleClampCoordinateToEdge(alloc::string::String),\n    #[error(\"Unable to cast\")]\n    InvalidCastArgument,\n    #[error(\"Invalid argument count for {0:?}\")]\n    WrongArgumentCount(crate::MathFunction),\n    #[error(\"Argument [{1}] to {0:?} as expression {2:?} has an invalid type.\")]\n    InvalidArgumentType(crate::MathFunction, u32, Handle<crate::Expression>),\n    #[error(\n        \"workgroupUniformLoad result type can't be {0:?}. It can only be a constructible type.\"\n    )]\n    InvalidWorkGroupUniformLoadResultType(Handle<crate::Type>),\n    #[error(\"Shader requires capability {0:?}\")]\n    MissingCapabilities(super::Capabilities),\n    #[error(transparent)]\n    Literal(#[from] LiteralError),\n    #[error(\"{0:?} is not supported for Width {2} {1:?} arguments yet, see https://github.com/gfx-rs/wgpu/issues/5276\")]\n    UnsupportedWidth(crate::MathFunction, crate::ScalarKind, crate::Bytes),\n    #[error(\"Invalid operand for cooperative op\")]\n    InvalidCooperativeOperand(Handle<crate::Expression>),\n    #[error(\"Shift amount exceeds the bit width of {lhs_type:?}\")]\n    ShiftAmountTooLarge {\n        lhs_type: crate::TypeInner,\n        rhs_expr: Handle<crate::Expression>,\n    },\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ConstExpressionError {\n    #[error(\"The expression is not a constant or override expression\")]\n    NonConstOrOverride,\n    #[error(\"The expression is not a fully evaluated constant expression\")]\n    NonFullyEvaluatedConst,\n    #[error(transparent)]\n    Compose(#[from] super::ComposeError),\n    #[error(\"Splatting {0:?} can't be done\")]\n    InvalidSplatType(Handle<crate::Expression>),\n    #[error(\"Type resolution failed\")]\n    Type(#[from] ResolveError),\n    #[error(transparent)]\n    Literal(#[from] LiteralError),\n    #[error(transparent)]\n    Width(#[from] super::r#type::WidthError),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum LiteralError {\n    #[error(\"Float literal is NaN\")]\n    NaN,\n    #[error(\"Float literal is infinite\")]\n    Infinity,\n    #[error(transparent)]\n    Width(#[from] super::r#type::WidthError),\n}\n\nstruct ExpressionTypeResolver<'a> {\n    root: Handle<crate::Expression>,\n    types: &'a UniqueArena<crate::Type>,\n    info: &'a FunctionInfo,\n}\n\nimpl core::ops::Index<Handle<crate::Expression>> for ExpressionTypeResolver<'_> {\n    type Output = crate::TypeInner;\n\n    #[allow(clippy::panic)]\n    fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {\n        if handle < self.root {\n            self.info[handle].ty.inner_with(self.types)\n        } else {\n            // `Validator::validate_module_handles` should have caught this.\n            panic!(\n                \"Depends on {:?}, which has not been processed yet\",\n                self.root\n            )\n        }\n    }\n}\n\nimpl super::Validator {\n    pub(super) fn validate_const_expression(\n        &self,\n        handle: Handle<crate::Expression>,\n        gctx: crate::proc::GlobalCtx,\n        mod_info: &ModuleInfo,\n        global_expr_kind: &crate::proc::ExpressionKindTracker,\n    ) -> Result<(), ConstExpressionError> {\n        use crate::Expression as E;\n\n        if !global_expr_kind.is_const_or_override(handle) {\n            return Err(ConstExpressionError::NonConstOrOverride);\n        }\n\n        match gctx.global_expressions[handle] {\n            E::Literal(literal) => {\n                self.validate_literal(literal)?;\n            }\n            E::Constant(_) | E::ZeroValue(_) => {}\n            E::Compose { ref components, ty } => {\n                validate_compose(\n                    ty,\n                    gctx,\n                    components.iter().map(|&handle| mod_info[handle].clone()),\n                )?;\n            }\n            E::Splat { value, .. } => match *mod_info[value].inner_with(gctx.types) {\n                crate::TypeInner::Scalar { .. } => {}\n                _ => return Err(ConstExpressionError::InvalidSplatType(value)),\n            },\n            _ if global_expr_kind.is_const(handle) || self.overrides_resolved => {\n                return Err(ConstExpressionError::NonFullyEvaluatedConst)\n            }\n            // the constant evaluator will report errors about override-expressions\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    /// Return an error if a constant shift amount in `right` exceeds the bit\n    /// width of `left_ty`.\n    ///\n    /// This function promises to return an error in cases where (1) the\n    /// expression is well-typed, (2) `left_ty` is a concrete integer, and\n    /// (3) the shift will overflow. It does not return an error in cases where\n    /// the expression is not well-typed (e.g. vector dimension mismatch),\n    /// because those will be rejected elsewhere.\n    fn validate_constant_shift_amounts(\n        left_ty: &crate::TypeInner,\n        right: Handle<crate::Expression>,\n        module: &crate::Module,\n        function: &crate::Function,\n    ) -> Result<(), ExpressionError> {\n        fn is_overflowing_shift(\n            left_ty: &crate::TypeInner,\n            right: Handle<crate::Expression>,\n            module: &crate::Module,\n            function: &crate::Function,\n        ) -> bool {\n            let Some((vec_size, scalar)) = left_ty.vector_size_and_scalar() else {\n                return false;\n            };\n            if !matches!(\n                scalar.kind,\n                crate::ScalarKind::Sint | crate::ScalarKind::Uint\n            ) {\n                return false;\n            }\n            let lhs_bits = u32::from(8 * scalar.width);\n            if vec_size.is_none() {\n                let shift_amount = module\n                    .to_ctx()\n                    .get_const_val_from::<u32, _>(right, &function.expressions);\n                shift_amount.ok().is_some_and(|s| s >= lhs_bits)\n            } else {\n                match function.expressions[right] {\n                    crate::Expression::ZeroValue(_) => false, // zero shift does not overflow\n                    crate::Expression::Splat { value, .. } => module\n                        .to_ctx()\n                        .get_const_val_from::<u32, _>(value, &function.expressions)\n                        .ok()\n                        .is_some_and(|s| s >= lhs_bits),\n                    crate::Expression::Compose {\n                        ty: _,\n                        ref components,\n                    } => components.iter().any(|comp| {\n                        module\n                            .to_ctx()\n                            .get_const_val_from::<u32, _>(*comp, &function.expressions)\n                            .ok()\n                            .is_some_and(|s| s >= lhs_bits)\n                    }),\n                    _ => false,\n                }\n            }\n        }\n\n        if is_overflowing_shift(left_ty, right, module, function) {\n            Err(ExpressionError::ShiftAmountTooLarge {\n                lhs_type: left_ty.clone(),\n                rhs_expr: right,\n            })\n        } else {\n            Ok(())\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn validate_expression(\n        &self,\n        root: Handle<crate::Expression>,\n        expression: &crate::Expression,\n        function: &crate::Function,\n        module: &crate::Module,\n        info: &FunctionInfo,\n        mod_info: &ModuleInfo,\n        expr_kind: &crate::proc::ExpressionKindTracker,\n    ) -> Result<ShaderStages, ExpressionError> {\n        use crate::{Expression as E, Scalar as Sc, ScalarKind as Sk, TypeInner as Ti};\n\n        let resolver = ExpressionTypeResolver {\n            root,\n            types: &module.types,\n            info,\n        };\n\n        let stages = match *expression {\n            E::Access { base, index } => {\n                let base_type = &resolver[base];\n                match *base_type {\n                    Ti::Matrix { .. }\n                    | Ti::Vector { .. }\n                    | Ti::Array { .. }\n                    | Ti::Pointer { .. }\n                    | Ti::ValuePointer { size: Some(_), .. }\n                    | Ti::BindingArray { .. } => {}\n                    ref other => {\n                        log::debug!(\"Indexing of {other:?}\");\n                        return Err(ExpressionError::InvalidBaseType(base));\n                    }\n                };\n                match resolver[index] {\n                    //TODO: only allow one of these\n                    Ti::Scalar(Sc {\n                        kind: Sk::Sint | Sk::Uint,\n                        ..\n                    }) => {}\n                    ref other => {\n                        log::debug!(\"Indexing by {other:?}\");\n                        return Err(ExpressionError::InvalidIndexType(index));\n                    }\n                }\n\n                // If index is const we can do check for non-negative index\n                match module\n                    .to_ctx()\n                    .get_const_val_from(index, &function.expressions)\n                {\n                    Ok(value) => {\n                        let length = if self.overrides_resolved {\n                            base_type.indexable_length_resolved(module)\n                        } else {\n                            base_type.indexable_length_pending(module)\n                        }?;\n                        // If we know both the length and the index, we can do the\n                        // bounds check now.\n                        if let crate::proc::IndexableLength::Known(known_length) = length {\n                            if value >= known_length {\n                                return Err(ExpressionError::IndexOutOfBounds(base, value));\n                            }\n                        }\n                    }\n                    Err(crate::proc::ConstValueError::Negative) => {\n                        return Err(ExpressionError::NegativeIndex(base))\n                    }\n                    Err(crate::proc::ConstValueError::NonConst) => {}\n                    Err(crate::proc::ConstValueError::InvalidType) => {\n                        return Err(ExpressionError::InvalidIndexType(index))\n                    }\n                }\n\n                ShaderStages::all()\n            }\n            E::AccessIndex { base, index } => {\n                fn resolve_index_limit(\n                    module: &crate::Module,\n                    top: Handle<crate::Expression>,\n                    ty: &crate::TypeInner,\n                    top_level: bool,\n                ) -> Result<u32, ExpressionError> {\n                    let limit = match *ty {\n                        Ti::Vector { size, .. }\n                        | Ti::ValuePointer {\n                            size: Some(size), ..\n                        } => size as u32,\n                        Ti::Matrix { columns, .. } => columns as u32,\n                        Ti::Array {\n                            size: crate::ArraySize::Constant(len),\n                            ..\n                        } => len.get(),\n                        Ti::Array { .. } | Ti::BindingArray { .. } => u32::MAX, // can't statically know, but need run-time checks\n                        Ti::Pointer { base, .. } if top_level => {\n                            resolve_index_limit(module, top, &module.types[base].inner, false)?\n                        }\n                        Ti::Struct { ref members, .. } => members.len() as u32,\n                        ref other => {\n                            log::debug!(\"Indexing of {other:?}\");\n                            return Err(ExpressionError::InvalidBaseType(top));\n                        }\n                    };\n                    Ok(limit)\n                }\n\n                let limit = resolve_index_limit(module, base, &resolver[base], true)?;\n                if index >= limit {\n                    return Err(ExpressionError::IndexOutOfBounds(base, limit));\n                }\n                ShaderStages::all()\n            }\n            E::Splat { size: _, value } => match resolver[value] {\n                Ti::Scalar { .. } => ShaderStages::all(),\n                ref other => {\n                    log::debug!(\"Splat scalar type {other:?}\");\n                    return Err(ExpressionError::InvalidSplatType(value));\n                }\n            },\n            E::Swizzle {\n                size,\n                vector,\n                pattern,\n            } => {\n                let vec_size = match resolver[vector] {\n                    Ti::Vector { size: vec_size, .. } => vec_size,\n                    ref other => {\n                        log::debug!(\"Swizzle vector type {other:?}\");\n                        return Err(ExpressionError::InvalidVectorType(vector));\n                    }\n                };\n                for &sc in pattern[..size as usize].iter() {\n                    if sc as u8 >= vec_size as u8 {\n                        return Err(ExpressionError::InvalidSwizzleComponent(sc, vec_size));\n                    }\n                }\n                ShaderStages::all()\n            }\n            E::Literal(literal) => {\n                self.validate_literal(literal)?;\n                ShaderStages::all()\n            }\n            E::Constant(_) | E::Override(_) => ShaderStages::all(),\n            E::ZeroValue(ty) => {\n                if !mod_info[ty].contains(TypeFlags::CONSTRUCTIBLE) {\n                    return Err(ExpressionError::InvalidZeroValue(ty));\n                }\n                ShaderStages::all()\n            }\n            E::Compose { ref components, ty } => {\n                validate_compose(\n                    ty,\n                    module.to_ctx(),\n                    components.iter().map(|&handle| info[handle].ty.clone()),\n                )?;\n                ShaderStages::all()\n            }\n            E::FunctionArgument(index) => {\n                if index >= function.arguments.len() as u32 {\n                    return Err(ExpressionError::FunctionArgumentDoesntExist(index));\n                }\n                ShaderStages::all()\n            }\n            E::GlobalVariable(_handle) => ShaderStages::all(),\n            E::LocalVariable(_handle) => ShaderStages::all(),\n            E::Load { pointer } => {\n                match resolver[pointer] {\n                    Ti::Pointer { base, .. }\n                        if self.types[base.index()]\n                            .flags\n                            .contains(TypeFlags::SIZED | TypeFlags::DATA) => {}\n                    Ti::ValuePointer { .. } => {}\n                    ref other => {\n                        log::debug!(\"Loading {other:?}\");\n                        return Err(ExpressionError::InvalidPointerType(pointer));\n                    }\n                }\n                ShaderStages::all()\n            }\n            E::ImageSample {\n                image,\n                sampler,\n                gather,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge,\n            } => {\n                // check the validity of expressions\n                let image_ty = Self::global_var_ty(module, function, image)?;\n                let sampler_ty = Self::global_var_ty(module, function, sampler)?;\n\n                let comparison = match module.types[sampler_ty].inner {\n                    Ti::Sampler { comparison } => comparison,\n                    _ => return Err(ExpressionError::ExpectedSamplerType(sampler_ty)),\n                };\n\n                let (class, dim) = match module.types[image_ty].inner {\n                    Ti::Image {\n                        class,\n                        arrayed,\n                        dim,\n                    } => {\n                        // check the array property\n                        if arrayed != array_index.is_some() {\n                            return Err(ExpressionError::InvalidImageArrayIndex);\n                        }\n                        if let Some(expr) = array_index {\n                            match resolver[expr] {\n                                Ti::Scalar(Sc {\n                                    kind: Sk::Sint | Sk::Uint,\n                                    ..\n                                }) => {}\n                                _ => return Err(ExpressionError::InvalidImageArrayIndexType(expr)),\n                            }\n                        }\n                        (class, dim)\n                    }\n                    _ => return Err(ExpressionError::ExpectedImageType(image_ty)),\n                };\n\n                // check sampling and comparison properties\n                let image_depth = match class {\n                    crate::ImageClass::Sampled {\n                        kind: crate::ScalarKind::Float,\n                        multi: false,\n                    } => false,\n                    crate::ImageClass::Sampled {\n                        kind: crate::ScalarKind::Uint | crate::ScalarKind::Sint,\n                        multi: false,\n                    } if gather.is_some() => false,\n                    crate::ImageClass::External => false,\n                    crate::ImageClass::Depth { multi: false } => true,\n                    _ => return Err(ExpressionError::InvalidImageClass(class)),\n                };\n                if comparison != depth_ref.is_some() || (comparison && !image_depth) {\n                    return Err(ExpressionError::ComparisonSamplingMismatch {\n                        image: class,\n                        sampler: comparison,\n                        has_ref: depth_ref.is_some(),\n                    });\n                }\n\n                // check texture coordinates type\n                let num_components = match dim {\n                    crate::ImageDimension::D1 => 1,\n                    crate::ImageDimension::D2 => 2,\n                    crate::ImageDimension::D3 | crate::ImageDimension::Cube => 3,\n                };\n                match resolver[coordinate] {\n                    Ti::Scalar(Sc {\n                        kind: Sk::Float, ..\n                    }) if num_components == 1 => {}\n                    Ti::Vector {\n                        size,\n                        scalar:\n                            Sc {\n                                kind: Sk::Float, ..\n                            },\n                    } if size as u32 == num_components => {}\n                    _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),\n                }\n\n                // check constant offset\n                if let Some(const_expr) = offset {\n                    if !expr_kind.is_const(const_expr) {\n                        return Err(ExpressionError::InvalidSampleOffsetExprType);\n                    }\n\n                    match resolver[const_expr] {\n                        Ti::Scalar(Sc { kind: Sk::Sint, .. }) if num_components == 1 => {}\n                        Ti::Vector {\n                            size,\n                            scalar: Sc { kind: Sk::Sint, .. },\n                        } if size as u32 == num_components => {}\n                        _ => {\n                            return Err(ExpressionError::InvalidSampleOffset(dim, const_expr));\n                        }\n                    }\n                }\n\n                // check depth reference type\n                if let Some(expr) = depth_ref {\n                    match resolver[expr] {\n                        Ti::Scalar(Sc {\n                            kind: Sk::Float, ..\n                        }) => {}\n                        _ => return Err(ExpressionError::InvalidDepthReference(expr)),\n                    }\n                    match level {\n                        crate::SampleLevel::Auto | crate::SampleLevel::Zero => {}\n                        _ => return Err(ExpressionError::InvalidDepthSampleLevel),\n                    }\n                }\n\n                if let Some(component) = gather {\n                    match dim {\n                        crate::ImageDimension::D2 | crate::ImageDimension::Cube => {}\n                        crate::ImageDimension::D1 | crate::ImageDimension::D3 => {\n                            return Err(ExpressionError::InvalidGatherDimension(dim))\n                        }\n                    };\n                    let max_component = match class {\n                        crate::ImageClass::Depth { .. } => crate::SwizzleComponent::X,\n                        _ => crate::SwizzleComponent::W,\n                    };\n                    if component > max_component {\n                        return Err(ExpressionError::InvalidGatherComponent(component));\n                    }\n                    match level {\n                        crate::SampleLevel::Zero => {}\n                        _ => return Err(ExpressionError::InvalidGatherLevel),\n                    }\n                }\n\n                // Clamping coordinate to edge is only supported with 2d non-arrayed, sampled images\n                // when sampling from level Zero without any offset, gather, or depth comparison.\n                if clamp_to_edge {\n                    if !matches!(\n                        class,\n                        crate::ImageClass::Sampled {\n                            kind: crate::ScalarKind::Float,\n                            multi: false\n                        } | crate::ImageClass::External\n                    ) {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            alloc::format!(\"image class `{class:?}`\"),\n                        ));\n                    }\n                    if dim != crate::ImageDimension::D2 {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            alloc::format!(\"image dimension `{dim:?}`\"),\n                        ));\n                    }\n                    if gather.is_some() {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            \"gather\".into(),\n                        ));\n                    }\n                    if array_index.is_some() {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            \"array index\".into(),\n                        ));\n                    }\n                    if offset.is_some() {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            \"offset\".into(),\n                        ));\n                    }\n                    if level != crate::SampleLevel::Zero {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            \"non-zero level\".into(),\n                        ));\n                    }\n                    if depth_ref.is_some() {\n                        return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(\n                            \"depth comparison\".into(),\n                        ));\n                    }\n                }\n\n                // External textures can only be sampled using clamp_to_edge.\n                if matches!(class, crate::ImageClass::External) && !clamp_to_edge {\n                    return Err(ExpressionError::InvalidImageClass(class));\n                }\n\n                // check level properties\n                match level {\n                    crate::SampleLevel::Auto => ShaderStages::FRAGMENT,\n                    crate::SampleLevel::Zero => ShaderStages::all(),\n                    crate::SampleLevel::Exact(expr) => {\n                        match class {\n                            crate::ImageClass::Depth { .. } => match resolver[expr] {\n                                Ti::Scalar(Sc {\n                                    kind: Sk::Sint | Sk::Uint,\n                                    ..\n                                }) => {}\n                                _ => {\n                                    return Err(ExpressionError::InvalidSampleLevelExactType(expr))\n                                }\n                            },\n                            _ => match resolver[expr] {\n                                Ti::Scalar(Sc {\n                                    kind: Sk::Float, ..\n                                }) => {}\n                                _ => {\n                                    return Err(ExpressionError::InvalidSampleLevelExactType(expr))\n                                }\n                            },\n                        }\n                        ShaderStages::all()\n                    }\n                    crate::SampleLevel::Bias(expr) => {\n                        match resolver[expr] {\n                            Ti::Scalar(Sc {\n                                kind: Sk::Float, ..\n                            }) => {}\n                            _ => return Err(ExpressionError::InvalidSampleLevelBiasType(expr)),\n                        }\n                        match class {\n                            crate::ImageClass::Sampled {\n                                kind: Sk::Float,\n                                multi: false,\n                            } => {\n                                if dim == crate::ImageDimension::D1 {\n                                    return Err(ExpressionError::InvalidSampleLevelBiasDimension(\n                                        dim,\n                                    ));\n                                }\n                            }\n                            _ => return Err(ExpressionError::InvalidImageClass(class)),\n                        }\n                        ShaderStages::FRAGMENT\n                    }\n                    crate::SampleLevel::Gradient { x, y } => {\n                        match resolver[x] {\n                            Ti::Scalar(Sc {\n                                kind: Sk::Float, ..\n                            }) if num_components == 1 => {}\n                            Ti::Vector {\n                                size,\n                                scalar:\n                                    Sc {\n                                        kind: Sk::Float, ..\n                                    },\n                            } if size as u32 == num_components => {}\n                            _ => {\n                                return Err(ExpressionError::InvalidSampleLevelGradientType(dim, x))\n                            }\n                        }\n                        match resolver[y] {\n                            Ti::Scalar(Sc {\n                                kind: Sk::Float, ..\n                            }) if num_components == 1 => {}\n                            Ti::Vector {\n                                size,\n                                scalar:\n                                    Sc {\n                                        kind: Sk::Float, ..\n                                    },\n                            } if size as u32 == num_components => {}\n                            _ => {\n                                return Err(ExpressionError::InvalidSampleLevelGradientType(dim, y))\n                            }\n                        }\n                        ShaderStages::all()\n                    }\n                }\n            }\n            E::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                let ty = Self::global_var_ty(module, function, image)?;\n                let Ti::Image {\n                    class,\n                    arrayed,\n                    dim,\n                } = module.types[ty].inner\n                else {\n                    return Err(ExpressionError::ExpectedImageType(ty));\n                };\n\n                match resolver[coordinate].image_storage_coordinates() {\n                    Some(coord_dim) if coord_dim == dim => {}\n                    _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),\n                };\n                if arrayed != array_index.is_some() {\n                    return Err(ExpressionError::InvalidImageArrayIndex);\n                }\n                if let Some(expr) = array_index {\n                    if !matches!(resolver[expr], Ti::Scalar(Sc::I32 | Sc::U32)) {\n                        return Err(ExpressionError::InvalidImageArrayIndexType(expr));\n                    }\n                }\n\n                match (sample, class.is_multisampled()) {\n                    (None, false) => {}\n                    (Some(sample), true) => {\n                        if !matches!(resolver[sample], Ti::Scalar(Sc::I32 | Sc::U32)) {\n                            return Err(ExpressionError::InvalidImageOtherIndexType(sample));\n                        }\n                    }\n                    (Some(_), false) => {\n                        return Err(ExpressionError::InvalidImageSampleSelector);\n                    }\n                    (None, true) => {\n                        return Err(ExpressionError::MissingImageSampleSelector);\n                    }\n                }\n\n                match (level, class.is_mipmapped()) {\n                    (None, false) => {}\n                    (Some(level), true) => match resolver[level] {\n                        Ti::Scalar(Sc {\n                            kind: Sk::Sint | Sk::Uint,\n                            width: _,\n                        }) => {}\n                        _ => return Err(ExpressionError::InvalidImageArrayIndexType(level)),\n                    },\n                    (Some(_), false) => {\n                        return Err(ExpressionError::InvalidImageLevelSelector);\n                    }\n                    (None, true) => {\n                        return Err(ExpressionError::MissingImageLevelSelector);\n                    }\n                }\n                ShaderStages::all()\n            }\n            E::ImageQuery { image, query } => {\n                let ty = Self::global_var_ty(module, function, image)?;\n                match module.types[ty].inner {\n                    Ti::Image { class, arrayed, .. } => {\n                        let good = match query {\n                            crate::ImageQuery::NumLayers => arrayed,\n                            crate::ImageQuery::Size { level: None } => true,\n                            crate::ImageQuery::Size { level: Some(level) } => {\n                                match resolver[level] {\n                                    Ti::Scalar(Sc::I32 | Sc::U32) => {}\n                                    _ => {\n                                        return Err(ExpressionError::InvalidImageOtherIndexType(\n                                            level,\n                                        ))\n                                    }\n                                }\n                                class.is_mipmapped()\n                            }\n                            crate::ImageQuery::NumLevels => class.is_mipmapped(),\n                            crate::ImageQuery::NumSamples => class.is_multisampled(),\n                        };\n                        if !good {\n                            return Err(ExpressionError::InvalidImageClass(class));\n                        }\n                    }\n                    _ => return Err(ExpressionError::ExpectedImageType(ty)),\n                }\n                ShaderStages::all()\n            }\n            E::Unary { op, expr } => {\n                use crate::UnaryOperator as Uo;\n                let Some((_, scalar)) = resolver[expr].vector_size_and_scalar() else {\n                    return Err(ExpressionError::InvalidUnaryOperandType(op, expr));\n                };\n                match (op, scalar.kind) {\n                    (Uo::Negate, Sk::Float | Sk::Sint) => {}\n                    (Uo::LogicalNot, Sk::Bool) => {}\n                    (Uo::BitwiseNot, Sk::Sint | Sk::Uint) => {}\n                    _ => return Err(ExpressionError::InvalidUnaryOperandType(op, expr)),\n                }\n                ShaderStages::all()\n            }\n            E::Binary { op, left, right } => {\n                use crate::BinaryOperator as Bo;\n                let left_inner = &resolver[left];\n                let right_inner = &resolver[right];\n                let good = match op {\n                    Bo::Add | Bo::Subtract => match *left_inner {\n                        Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {\n                            Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,\n                            Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,\n                        },\n                        Ti::Matrix { .. } | Ti::CooperativeMatrix { .. } => {\n                            left_inner == right_inner\n                        }\n                        _ => false,\n                    },\n                    Bo::Divide | Bo::Modulo => match *left_inner {\n                        Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {\n                            Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,\n                            Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,\n                        },\n                        _ => false,\n                    },\n                    Bo::Multiply => {\n                        let kind_allowed = match left_inner.scalar_kind() {\n                            Some(Sk::Uint | Sk::Sint | Sk::Float) => true,\n                            Some(Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat) | None => false,\n                        };\n                        let types_match = match (left_inner, right_inner) {\n                            // Straight scalar and mixed scalar/vector.\n                            (&Ti::Scalar(scalar1), &Ti::Scalar(scalar2))\n                            | (\n                                &Ti::Vector {\n                                    scalar: scalar1, ..\n                                },\n                                &Ti::Scalar(scalar2),\n                            )\n                            | (\n                                &Ti::Scalar(scalar1),\n                                &Ti::Vector {\n                                    scalar: scalar2, ..\n                                },\n                            ) => scalar1 == scalar2,\n                            // Scalar * matrix.\n                            (\n                                &Ti::Scalar(Sc {\n                                    kind: Sk::Float, ..\n                                }),\n                                &Ti::Matrix { .. },\n                            )\n                            | (\n                                &Ti::Matrix { .. },\n                                &Ti::Scalar(Sc {\n                                    kind: Sk::Float, ..\n                                }),\n                            ) => true,\n                            // Vector * vector.\n                            (\n                                &Ti::Vector {\n                                    size: size1,\n                                    scalar: scalar1,\n                                },\n                                &Ti::Vector {\n                                    size: size2,\n                                    scalar: scalar2,\n                                },\n                            ) => scalar1 == scalar2 && size1 == size2,\n                            // Matrix * vector.\n                            (\n                                &Ti::Matrix { columns, .. },\n                                &Ti::Vector {\n                                    size,\n                                    scalar:\n                                        Sc {\n                                            kind: Sk::Float, ..\n                                        },\n                                },\n                            ) => columns == size,\n                            // Vector * matrix.\n                            (\n                                &Ti::Vector {\n                                    size,\n                                    scalar:\n                                        Sc {\n                                            kind: Sk::Float, ..\n                                        },\n                                },\n                                &Ti::Matrix { rows, .. },\n                            ) => size == rows,\n                            // Matrix * matrix.\n                            (&Ti::Matrix { columns, .. }, &Ti::Matrix { rows, .. }) => {\n                                columns == rows\n                            }\n                            // Scalar * coop matrix.\n                            (&Ti::Scalar(s1), &Ti::CooperativeMatrix { scalar: s2, .. })\n                            | (&Ti::CooperativeMatrix { scalar: s1, .. }, &Ti::Scalar(s2)) => {\n                                s1 == s2\n                            }\n                            _ => false,\n                        };\n                        let left_width = left_inner.scalar_width().unwrap_or(0);\n                        let right_width = right_inner.scalar_width().unwrap_or(0);\n                        kind_allowed && types_match && left_width == right_width\n                    }\n                    Bo::Equal | Bo::NotEqual => left_inner.is_sized() && left_inner == right_inner,\n                    Bo::Less | Bo::LessEqual | Bo::Greater | Bo::GreaterEqual => {\n                        match *left_inner {\n                            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {\n                                Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,\n                                Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,\n                            },\n                            ref other => {\n                                log::debug!(\"Op {op:?} left type {other:?}\");\n                                false\n                            }\n                        }\n                    }\n                    Bo::LogicalAnd | Bo::LogicalOr => match *left_inner {\n                        Ti::Scalar(Sc { kind: Sk::Bool, .. })\n                        | Ti::Vector {\n                            scalar: Sc { kind: Sk::Bool, .. },\n                            ..\n                        } => left_inner == right_inner,\n                        ref other => {\n                            log::debug!(\"Op {op:?} left type {other:?}\");\n                            false\n                        }\n                    },\n                    Bo::And | Bo::InclusiveOr => match *left_inner {\n                        Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {\n                            Sk::Bool | Sk::Sint | Sk::Uint => left_inner == right_inner,\n                            Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,\n                        },\n                        ref other => {\n                            log::debug!(\"Op {op:?} left type {other:?}\");\n                            false\n                        }\n                    },\n                    Bo::ExclusiveOr => match *left_inner {\n                        Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {\n                            Sk::Sint | Sk::Uint => left_inner == right_inner,\n                            Sk::Bool | Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,\n                        },\n                        ref other => {\n                            log::debug!(\"Op {op:?} left type {other:?}\");\n                            false\n                        }\n                    },\n                    Bo::ShiftLeft | Bo::ShiftRight => {\n                        let (base_size, base_scalar) = match *left_inner {\n                            Ti::Scalar(scalar) => (Ok(None), scalar),\n                            Ti::Vector { size, scalar } => (Ok(Some(size)), scalar),\n                            ref other => {\n                                log::debug!(\"Op {op:?} base type {other:?}\");\n                                (Err(()), Sc::BOOL)\n                            }\n                        };\n                        let shift_size = match *right_inner {\n                            Ti::Scalar(Sc { kind: Sk::Uint, .. }) => Ok(None),\n                            Ti::Vector {\n                                size,\n                                scalar: Sc { kind: Sk::Uint, .. },\n                            } => Ok(Some(size)),\n                            ref other => {\n                                log::debug!(\"Op {op:?} shift type {other:?}\");\n                                Err(())\n                            }\n                        };\n                        match base_scalar.kind {\n                            Sk::Sint | Sk::Uint => base_size.is_ok() && base_size == shift_size,\n                            Sk::Float | Sk::AbstractInt | Sk::AbstractFloat | Sk::Bool => false,\n                        }\n                    }\n                };\n                if !good {\n                    log::debug!(\n                        \"Left: {:?} of type {:?}\",\n                        function.expressions[left],\n                        left_inner\n                    );\n                    log::debug!(\n                        \"Right: {:?} of type {:?}\",\n                        function.expressions[right],\n                        right_inner\n                    );\n                    return Err(ExpressionError::InvalidBinaryOperandTypes {\n                        op,\n                        lhs_expr: left,\n                        lhs_type: left_inner.clone(),\n                        rhs_expr: right,\n                        rhs_type: right_inner.clone(),\n                    });\n                }\n                // For shift operations, check if the constant shift amount exceeds the bit width\n                if matches!(op, Bo::ShiftLeft | Bo::ShiftRight) {\n                    Self::validate_constant_shift_amounts(left_inner, right, module, function)?;\n                }\n                ShaderStages::all()\n            }\n            E::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                let accept_inner = &resolver[accept];\n                let reject_inner = &resolver[reject];\n                let condition_ty = &resolver[condition];\n                let condition_good = match *condition_ty {\n                    Ti::Scalar(Sc {\n                        kind: Sk::Bool,\n                        width: _,\n                    }) => {\n                        // When `condition` is a single boolean, `accept` and\n                        // `reject` can be vectors or scalars.\n                        match *accept_inner {\n                            Ti::Scalar { .. } | Ti::Vector { .. } => true,\n                            _ => false,\n                        }\n                    }\n                    Ti::Vector {\n                        size,\n                        scalar:\n                            Sc {\n                                kind: Sk::Bool,\n                                width: _,\n                            },\n                    } => match *accept_inner {\n                        Ti::Vector {\n                            size: other_size, ..\n                        } => size == other_size,\n                        _ => false,\n                    },\n                    _ => false,\n                };\n                if accept_inner != reject_inner {\n                    return Err(ExpressionError::SelectValuesTypeMismatch {\n                        accept: accept_inner.clone(),\n                        reject: reject_inner.clone(),\n                    });\n                }\n                if !condition_good {\n                    return Err(ExpressionError::SelectConditionNotABool {\n                        actual: condition_ty.clone(),\n                    });\n                }\n                ShaderStages::all()\n            }\n            E::Derivative { expr, .. } => {\n                match resolver[expr] {\n                    Ti::Scalar(Sc {\n                        kind: Sk::Float, ..\n                    })\n                    | Ti::Vector {\n                        scalar:\n                            Sc {\n                                kind: Sk::Float, ..\n                            },\n                        ..\n                    } => {}\n                    _ => return Err(ExpressionError::InvalidDerivative),\n                }\n                ShaderStages::FRAGMENT\n            }\n            E::Relational { fun, argument } => {\n                use crate::RelationalFunction as Rf;\n                let argument_inner = &resolver[argument];\n                match fun {\n                    Rf::All | Rf::Any => match *argument_inner {\n                        Ti::Vector {\n                            scalar: Sc { kind: Sk::Bool, .. },\n                            ..\n                        } => {}\n                        ref other => {\n                            log::debug!(\"All/Any of type {other:?}\");\n                            return Err(ExpressionError::InvalidBooleanVector(argument));\n                        }\n                    },\n                    Rf::IsNan | Rf::IsInf => match *argument_inner {\n                        Ti::Scalar(scalar) | Ti::Vector { scalar, .. }\n                            if scalar.kind == Sk::Float => {}\n                        ref other => {\n                            log::debug!(\"Float test of type {other:?}\");\n                            return Err(ExpressionError::InvalidFloatArgument(argument));\n                        }\n                    },\n                }\n                ShaderStages::all()\n            }\n            E::Math {\n                fun,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                if matches!(\n                    fun,\n                    crate::MathFunction::QuantizeToF16\n                        | crate::MathFunction::Pack2x16float\n                        | crate::MathFunction::Unpack2x16float\n                ) && !self\n                    .capabilities\n                    .contains(crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32)\n                {\n                    return Err(ExpressionError::MissingCapabilities(\n                        crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32,\n                    ));\n                }\n\n                let actuals: &[_] = match (arg1, arg2, arg3) {\n                    (None, None, None) => &[arg],\n                    (Some(arg1), None, None) => &[arg, arg1],\n                    (Some(arg1), Some(arg2), None) => &[arg, arg1, arg2],\n                    (Some(arg1), Some(arg2), Some(arg3)) => &[arg, arg1, arg2, arg3],\n                    _ => return Err(ExpressionError::WrongArgumentCount(fun)),\n                };\n\n                let resolve = |arg| &resolver[arg];\n                let actual_types: &[_] = match *actuals {\n                    [arg0] => &[resolve(arg0)],\n                    [arg0, arg1] => &[resolve(arg0), resolve(arg1)],\n                    [arg0, arg1, arg2] => &[resolve(arg0), resolve(arg1), resolve(arg2)],\n                    [arg0, arg1, arg2, arg3] => {\n                        &[resolve(arg0), resolve(arg1), resolve(arg2), resolve(arg3)]\n                    }\n                    _ => unreachable!(),\n                };\n\n                // Start with the set of all overloads available for `fun`.\n                let mut overloads = fun.overloads();\n                log::debug!(\n                    \"initial overloads for {:?}: {:#?}\",\n                    fun,\n                    overloads.for_debug(&module.types)\n                );\n\n                // If any argument is not a constant expression, then no\n                // overloads that accept abstract values should be considered.\n                // `OverloadSet::concrete_only` is supposed to help impose this\n                // restriction. However, no `MathFunction` accepts a mix of\n                // abstract and concrete arguments, so we don't need to worry\n                // about that here.\n\n                for (i, (&expr, &ty)) in actuals.iter().zip(actual_types).enumerate() {\n                    // Remove overloads that cannot accept an `i`'th\n                    // argument arguments of type `ty`.\n                    overloads = overloads.arg(i, ty, &module.types);\n                    log::debug!(\n                        \"overloads after arg {i}: {:#?}\",\n                        overloads.for_debug(&module.types)\n                    );\n\n                    if overloads.is_empty() {\n                        log::debug!(\"all overloads eliminated\");\n                        return Err(ExpressionError::InvalidArgumentType(fun, i as u32, expr));\n                    }\n                }\n\n                if actuals.len() < overloads.min_arguments() {\n                    return Err(ExpressionError::WrongArgumentCount(fun));\n                }\n\n                ShaderStages::all()\n            }\n            E::As {\n                expr,\n                kind,\n                convert,\n            } => {\n                let mut base_scalar = match resolver[expr] {\n                    crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => {\n                        scalar\n                    }\n                    crate::TypeInner::Matrix { scalar, .. } => scalar,\n                    _ => return Err(ExpressionError::InvalidCastArgument),\n                };\n                base_scalar.kind = kind;\n                if let Some(width) = convert {\n                    base_scalar.width = width;\n                }\n                if self.check_width(base_scalar).is_err() {\n                    return Err(ExpressionError::InvalidCastArgument);\n                }\n                ShaderStages::all()\n            }\n            E::CallResult(function) => mod_info.functions[function.index()].available_stages,\n            E::AtomicResult { .. } => {\n                // These expressions are validated when we check the `Atomic` statement\n                // that refers to them, because we have all the information we need at\n                // that point. The checks driven by `Validator::needs_visit` ensure\n                // that this expression is indeed visited by one `Atomic` statement.\n                ShaderStages::all()\n            }\n            E::WorkGroupUniformLoadResult { ty } => {\n                if self.types[ty.index()]\n                    .flags\n                    // Sized | Constructible is exactly the types currently supported by\n                    // WorkGroupUniformLoad\n                    .contains(TypeFlags::SIZED | TypeFlags::CONSTRUCTIBLE)\n                {\n                    ShaderStages::COMPUTE_LIKE\n                } else {\n                    return Err(ExpressionError::InvalidWorkGroupUniformLoadResultType(ty));\n                }\n            }\n            E::ArrayLength(expr) => match resolver[expr] {\n                Ti::Pointer { base, .. } => {\n                    let base_ty = &resolver.types[base];\n                    if let Ti::Array {\n                        size: crate::ArraySize::Dynamic,\n                        ..\n                    } = base_ty.inner\n                    {\n                        ShaderStages::all()\n                    } else {\n                        return Err(ExpressionError::InvalidArrayType(expr));\n                    }\n                }\n                ref other => {\n                    log::debug!(\"Array length of {other:?}\");\n                    return Err(ExpressionError::InvalidArrayType(expr));\n                }\n            },\n            E::RayQueryProceedResult => ShaderStages::all(),\n            E::RayQueryGetIntersection {\n                query,\n                committed: _,\n            } => match resolver[query] {\n                Ti::Pointer {\n                    base,\n                    space: crate::AddressSpace::Function,\n                } => match resolver.types[base].inner {\n                    Ti::RayQuery { .. } => ShaderStages::all(),\n                    ref other => {\n                        log::debug!(\"Intersection result of a pointer to {other:?}\");\n                        return Err(ExpressionError::InvalidRayQueryType(query));\n                    }\n                },\n                ref other => {\n                    log::debug!(\"Intersection result of {other:?}\");\n                    return Err(ExpressionError::InvalidRayQueryType(query));\n                }\n            },\n            E::RayQueryVertexPositions {\n                query,\n                committed: _,\n            } => match resolver[query] {\n                Ti::Pointer {\n                    base,\n                    space: crate::AddressSpace::Function,\n                } => match resolver.types[base].inner {\n                    Ti::RayQuery {\n                        vertex_return: true,\n                    } => ShaderStages::all(),\n                    ref other => {\n                        log::debug!(\"Intersection result of a pointer to {other:?}\");\n                        return Err(ExpressionError::InvalidRayQueryType(query));\n                    }\n                },\n                ref other => {\n                    log::debug!(\"Intersection result of {other:?}\");\n                    return Err(ExpressionError::InvalidRayQueryType(query));\n                }\n            },\n            E::SubgroupBallotResult | E::SubgroupOperationResult { .. } => self.subgroup_stages,\n            E::CooperativeLoad { ref data, .. } => {\n                if resolver[data.pointer]\n                    .pointer_base_type()\n                    .and_then(|tr| tr.inner_with(&module.types).scalar())\n                    .is_none()\n                {\n                    return Err(ExpressionError::InvalidPointerType(data.pointer));\n                }\n                ShaderStages::COMPUTE\n            }\n            E::CooperativeMultiplyAdd { a, b, c } => {\n                let roles = [\n                    crate::CooperativeRole::A,\n                    crate::CooperativeRole::B,\n                    crate::CooperativeRole::C,\n                ];\n                for (operand, expected_role) in [a, b, c].into_iter().zip(roles) {\n                    match resolver[operand] {\n                        Ti::CooperativeMatrix { role, .. } if role == expected_role => {}\n                        ref other => {\n                            log::debug!(\"{expected_role:?} operand type: {other:?}\");\n                            return Err(ExpressionError::InvalidCooperativeOperand(a));\n                        }\n                    }\n                }\n                ShaderStages::COMPUTE\n            }\n        };\n        Ok(stages)\n    }\n\n    fn global_var_ty(\n        module: &crate::Module,\n        function: &crate::Function,\n        expr: Handle<crate::Expression>,\n    ) -> Result<Handle<crate::Type>, ExpressionError> {\n        use crate::Expression as Ex;\n\n        match function.expressions[expr] {\n            Ex::GlobalVariable(var_handle) => Ok(module.global_variables[var_handle].ty),\n            Ex::FunctionArgument(i) => Ok(function.arguments[i as usize].ty),\n            Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {\n                match function.expressions[base] {\n                    Ex::GlobalVariable(var_handle) => {\n                        let array_ty = module.global_variables[var_handle].ty;\n\n                        match module.types[array_ty].inner {\n                            crate::TypeInner::BindingArray { base, .. } => Ok(base),\n                            _ => Err(ExpressionError::ExpectedBindingArrayType(array_ty)),\n                        }\n                    }\n                    _ => Err(ExpressionError::ExpectedGlobalVariable),\n                }\n            }\n            _ => Err(ExpressionError::ExpectedGlobalVariable),\n        }\n    }\n\n    pub fn validate_literal(&self, literal: crate::Literal) -> Result<(), LiteralError> {\n        let _ = self.check_width(literal.scalar())?;\n        check_literal_value(literal)?;\n\n        Ok(())\n    }\n}\n\npub const fn check_literal_value(literal: crate::Literal) -> Result<(), LiteralError> {\n    let is_nan = match literal {\n        crate::Literal::F64(v) => v.is_nan(),\n        crate::Literal::F32(v) => v.is_nan(),\n        _ => false,\n    };\n    if is_nan {\n        return Err(LiteralError::NaN);\n    }\n\n    let is_infinite = match literal {\n        crate::Literal::F64(v) => v.is_infinite(),\n        crate::Literal::F32(v) => v.is_infinite(),\n        _ => false,\n    };\n    if is_infinite {\n        return Err(LiteralError::Infinity);\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\n/// Validate a module containing the given expression, expecting an error.\nfn validate_with_expression(\n    expr: crate::Expression,\n    caps: super::Capabilities,\n) -> Result<ModuleInfo, crate::span::WithSpan<super::ValidationError>> {\n    use crate::span::Span;\n\n    let mut function = crate::Function::default();\n    function.expressions.append(expr, Span::default());\n    function.body.push(\n        crate::Statement::Emit(function.expressions.range_from(0)),\n        Span::default(),\n    );\n\n    let mut module = crate::Module::default();\n    module.functions.append(function, Span::default());\n\n    let mut validator = super::Validator::new(super::ValidationFlags::EXPRESSIONS, caps);\n\n    validator.validate(&module)\n}\n\n#[cfg(test)]\n/// Validate a module containing the given constant expression, expecting an error.\nfn validate_with_const_expression(\n    expr: crate::Expression,\n    caps: super::Capabilities,\n) -> Result<ModuleInfo, crate::span::WithSpan<super::ValidationError>> {\n    use crate::span::Span;\n\n    let mut module = crate::Module::default();\n    module.global_expressions.append(expr, Span::default());\n\n    let mut validator = super::Validator::new(super::ValidationFlags::CONSTANTS, caps);\n\n    validator.validate(&module)\n}\n\n/// Using F64 in a function's expression arena is forbidden.\n#[test]\nfn f64_runtime_literals() {\n    let result = validate_with_expression(\n        crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),\n        super::Capabilities::default(),\n    );\n    let error = result.unwrap_err().into_inner();\n    assert!(matches!(\n        error,\n        crate::valid::ValidationError::Function {\n            source: super::FunctionError::Expression {\n                source: ExpressionError::Literal(LiteralError::Width(\n                    super::r#type::WidthError::MissingCapability {\n                        name: \"f64\",\n                        flag: \"FLOAT64\",\n                    }\n                ),),\n                ..\n            },\n            ..\n        }\n    ));\n\n    let result = validate_with_expression(\n        crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),\n        super::Capabilities::default() | super::Capabilities::FLOAT64,\n    );\n    assert!(result.is_ok());\n}\n\n/// Using F64 in a module's constant expression arena is forbidden.\n#[test]\nfn f64_const_literals() {\n    let result = validate_with_const_expression(\n        crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),\n        super::Capabilities::default(),\n    );\n    let error = result.unwrap_err().into_inner();\n    assert!(matches!(\n        error,\n        crate::valid::ValidationError::ConstExpression {\n            source: ConstExpressionError::Literal(LiteralError::Width(\n                super::r#type::WidthError::MissingCapability {\n                    name: \"f64\",\n                    flag: \"FLOAT64\",\n                }\n            )),\n            ..\n        }\n    ));\n\n    let result = validate_with_const_expression(\n        crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),\n        super::Capabilities::default() | super::Capabilities::FLOAT64,\n    );\n    assert!(result.is_ok());\n}\n"
  },
  {
    "path": "naga/src/valid/function.rs",
    "content": "use alloc::{format, string::String};\n\nuse super::{\n    analyzer::{UniformityDisruptor, UniformityRequirements},\n    ExpressionError, FunctionInfo, ModuleInfo,\n};\nuse crate::arena::{Arena, UniqueArena};\nuse crate::arena::{Handle, HandleSet};\nuse crate::proc::TypeResolution;\nuse crate::span::WithSpan;\nuse crate::span::{AddSpan as _, MapErrWithSpan as _};\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum CallError {\n    #[error(\"Argument {index} expression is invalid\")]\n    Argument {\n        index: usize,\n        source: ExpressionError,\n    },\n    #[error(\"Result expression {0:?} has already been introduced earlier\")]\n    ResultAlreadyInScope(Handle<crate::Expression>),\n    #[error(\"Result expression {0:?} is populated by multiple `Call` statements\")]\n    ResultAlreadyPopulated(Handle<crate::Expression>),\n    #[error(\"Requires {required} arguments, but {seen} are provided\")]\n    ArgumentCount { required: usize, seen: usize },\n    #[error(\"Argument {index} value {seen_expression:?} doesn't match the type {required:?}\")]\n    ArgumentType {\n        index: usize,\n        required: Handle<crate::Type>,\n        seen_expression: Handle<crate::Expression>,\n    },\n    #[error(\"The emitted expression doesn't match the call\")]\n    ExpressionMismatch(Option<Handle<crate::Expression>>),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum AtomicError {\n    #[error(\"Pointer {0:?} to atomic is invalid.\")]\n    InvalidPointer(Handle<crate::Expression>),\n    #[error(\"Address space {0:?} is not supported.\")]\n    InvalidAddressSpace(crate::AddressSpace),\n    #[error(\"Operand {0:?} has invalid type.\")]\n    InvalidOperand(Handle<crate::Expression>),\n    #[error(\"Operator {0:?} is not supported.\")]\n    InvalidOperator(crate::AtomicFunction),\n    #[error(\"Result expression {0:?} is not an `AtomicResult` expression\")]\n    InvalidResultExpression(Handle<crate::Expression>),\n    #[error(\"Result expression {0:?} is marked as an `exchange`\")]\n    ResultExpressionExchange(Handle<crate::Expression>),\n    #[error(\"Result expression {0:?} is not marked as an `exchange`\")]\n    ResultExpressionNotExchange(Handle<crate::Expression>),\n    #[error(\"Result type for {0:?} doesn't match the statement\")]\n    ResultTypeMismatch(Handle<crate::Expression>),\n    #[error(\"Exchange operations must return a value\")]\n    MissingReturnValue,\n    #[error(\"Capability {0:?} is required\")]\n    MissingCapability(super::Capabilities),\n    #[error(\"Result expression {0:?} is populated by multiple `Atomic` statements\")]\n    ResultAlreadyPopulated(Handle<crate::Expression>),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum SubgroupError {\n    #[error(\"Operand {0:?} has invalid type.\")]\n    InvalidOperand(Handle<crate::Expression>),\n    #[error(\"Result type for {0:?} doesn't match the statement\")]\n    ResultTypeMismatch(Handle<crate::Expression>),\n    #[error(\"Support for subgroup operation {0:?} is required\")]\n    UnsupportedOperation(super::SubgroupOperationSet),\n    #[error(\"Unknown operation\")]\n    UnknownOperation,\n    #[error(\"Invocation ID must be a const-expression\")]\n    InvalidInvocationIdExprType(Handle<crate::Expression>),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum LocalVariableError {\n    #[error(\"Local variable has a type {0:?} that can't be stored in a local variable.\")]\n    InvalidType(Handle<crate::Type>),\n    #[error(\"Initializer doesn't match the variable type\")]\n    InitializerType,\n    #[error(\"Initializer is not a const or override expression\")]\n    NonConstOrOverrideInitializer,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum FunctionError {\n    #[error(\"Expression {handle:?} is invalid\")]\n    Expression {\n        handle: Handle<crate::Expression>,\n        source: ExpressionError,\n    },\n    #[error(\"Expression {0:?} can't be introduced - it's already in scope\")]\n    ExpressionAlreadyInScope(Handle<crate::Expression>),\n    #[error(\"Local variable {handle:?} '{name}' is invalid\")]\n    LocalVariable {\n        handle: Handle<crate::LocalVariable>,\n        name: String,\n        source: LocalVariableError,\n    },\n    #[error(\"Argument '{name}' at index {index} has a type that can't be passed into functions.\")]\n    InvalidArgumentType { index: usize, name: String },\n    #[error(\"The function's given return type cannot be returned from functions\")]\n    NonConstructibleReturnType,\n    #[error(\"Argument '{name}' at index {index} is a pointer of space {space:?}, which can't be passed into functions.\")]\n    InvalidArgumentPointerSpace {\n        index: usize,\n        name: String,\n        space: crate::AddressSpace,\n    },\n    #[error(\"The `break` is used outside of a `loop` or `switch` context\")]\n    BreakOutsideOfLoopOrSwitch,\n    #[error(\"The `continue` is used outside of a `loop` context\")]\n    ContinueOutsideOfLoop,\n    #[error(\"The `return` is called within a `continuing` block\")]\n    InvalidReturnSpot,\n    #[error(\"The `return` expression {expression:?} does not match the declared return type {expected_ty:?}\")]\n    InvalidReturnType {\n        expression: Option<Handle<crate::Expression>>,\n        expected_ty: Option<Handle<crate::Type>>,\n    },\n    #[error(\"The `if` condition {0:?} is not a boolean scalar\")]\n    InvalidIfType(Handle<crate::Expression>),\n    #[error(\"The `switch` value {0:?} is not an integer scalar\")]\n    InvalidSwitchType(Handle<crate::Expression>),\n    #[error(\"Multiple `switch` cases for {0:?} are present\")]\n    ConflictingSwitchCase(crate::SwitchValue),\n    #[error(\"The `switch` contains cases with conflicting types\")]\n    ConflictingCaseType,\n    #[error(\"The `switch` is missing a `default` case\")]\n    MissingDefaultCase,\n    #[error(\"Multiple `default` cases are present\")]\n    MultipleDefaultCases,\n    #[error(\"The last `switch` case contains a `fallthrough`\")]\n    LastCaseFallTrough,\n    #[error(\"The pointer {0:?} doesn't relate to a valid destination for a store\")]\n    InvalidStorePointer(Handle<crate::Expression>),\n    #[error(\"Image store texture parameter type mismatch\")]\n    InvalidStoreTexture {\n        actual: Handle<crate::Expression>,\n        actual_ty: crate::TypeInner,\n    },\n    #[error(\"Image store value parameter type mismatch\")]\n    InvalidStoreValue {\n        actual: Handle<crate::Expression>,\n        actual_ty: crate::TypeInner,\n        expected_ty: crate::TypeInner,\n    },\n    #[error(\"The type of {value:?} doesn't match the type stored in {pointer:?}\")]\n    InvalidStoreTypes {\n        pointer: Handle<crate::Expression>,\n        value: Handle<crate::Expression>,\n    },\n    #[error(\"Image store parameters are invalid\")]\n    InvalidImageStore(#[source] ExpressionError),\n    #[error(\"Image atomic parameters are invalid\")]\n    InvalidImageAtomic(#[source] ExpressionError),\n    #[error(\"Image atomic function is invalid\")]\n    InvalidImageAtomicFunction(crate::AtomicFunction),\n    #[error(\"Image atomic value is invalid\")]\n    InvalidImageAtomicValue(Handle<crate::Expression>),\n    #[error(\"Call to {function:?} is invalid\")]\n    InvalidCall {\n        function: Handle<crate::Function>,\n        #[source]\n        error: CallError,\n    },\n    #[error(\"Atomic operation is invalid\")]\n    InvalidAtomic(#[from] AtomicError),\n    #[error(\"Ray Query {0:?} is not a local variable\")]\n    InvalidRayQueryExpression(Handle<crate::Expression>),\n    #[error(\"Acceleration structure {0:?} is not a matching expression\")]\n    InvalidAccelerationStructure(Handle<crate::Expression>),\n    #[error(\n        \"Acceleration structure {0:?} is missing flag vertex_return while Ray Query {1:?} does\"\n    )]\n    MissingAccelerationStructureVertexReturn(Handle<crate::Expression>, Handle<crate::Expression>),\n    #[error(\"Ray Query {0:?} is missing flag vertex_return\")]\n    MissingRayQueryVertexReturn(Handle<crate::Expression>),\n    #[error(\"Ray descriptor {0:?} is not a matching expression\")]\n    InvalidRayDescriptor(Handle<crate::Expression>),\n    #[error(\"Ray Query {0:?} does not have a matching type\")]\n    InvalidRayQueryType(Handle<crate::Type>),\n    #[error(\"Hit distance {0:?} must be an f32\")]\n    InvalidHitDistanceType(Handle<crate::Expression>),\n    #[error(\"Shader requires capability {0:?}\")]\n    MissingCapability(super::Capabilities),\n    #[error(\n        \"Required uniformity of control flow for {0:?} in {1:?} is not fulfilled because of {2:?}\"\n    )]\n    NonUniformControlFlow(\n        UniformityRequirements,\n        Handle<crate::Expression>,\n        UniformityDisruptor,\n    ),\n    #[error(\"Functions that are not entry points cannot have `@location` or `@builtin` attributes on their arguments: \\\"{name}\\\" has attributes\")]\n    PipelineInputRegularFunction { name: String },\n    #[error(\"Functions that are not entry points cannot have `@location` or `@builtin` attributes on their return value types\")]\n    PipelineOutputRegularFunction,\n    #[error(\"Required uniformity for WorkGroupUniformLoad is not fulfilled because of {0:?}\")]\n    // The actual load statement will be \"pointed to\" by the span\n    NonUniformWorkgroupUniformLoad(UniformityDisruptor),\n    // This is only possible with a misbehaving frontend\n    #[error(\"The expression {0:?} for a WorkGroupUniformLoad isn't a WorkgroupUniformLoadResult\")]\n    WorkgroupUniformLoadExpressionMismatch(Handle<crate::Expression>),\n    #[error(\"The expression {0:?} is not valid as a WorkGroupUniformLoad argument. It should be a Pointer in Workgroup address space\")]\n    WorkgroupUniformLoadInvalidPointer(Handle<crate::Expression>),\n    #[error(\"Subgroup operation is invalid\")]\n    InvalidSubgroup(#[from] SubgroupError),\n    #[error(\"Invalid target type for a cooperative store\")]\n    InvalidCooperativeStoreTarget(Handle<crate::Expression>),\n    #[error(\"Cooperative load/store data pointer has invalid type\")]\n    InvalidCooperativeDataPointer(Handle<crate::Expression>),\n    #[error(\"Emit statement should not cover \\\"result\\\" expressions like {0:?}\")]\n    EmitResult(Handle<crate::Expression>),\n    #[error(\"Expression not visited by the appropriate statement\")]\n    UnvisitedExpression(Handle<crate::Expression>),\n    #[error(\"Expression {0:?} in mesh shader intrinsic call should be `u32` (is the expression a signed integer?)\")]\n    InvalidMeshFunctionCall(Handle<crate::Expression>),\n    #[error(\"Mesh output types differ from {0:?} to {1:?}\")]\n    ConflictingMeshOutputTypes(Handle<crate::Expression>, Handle<crate::Expression>),\n    #[error(\"Task payload variables differ from {0:?} to {1:?}\")]\n    ConflictingTaskPayloadVariables(Handle<crate::Expression>, Handle<crate::Expression>),\n    #[error(\"Mesh shader output at {0:?} is not a user-defined struct\")]\n    InvalidMeshShaderOutputType(Handle<crate::Expression>),\n    #[error(\"The payload type passed to `traceRay` must be a pointer\")]\n    InvalidPayloadType,\n    #[error(\"The payload type passed to `traceRay` must be a pointer with an address space of `ray_payload` or `incoming_ray_payload`, instead got {0:?}\")]\n    InvalidPayloadAddressSpace(crate::AddressSpace),\n    #[error(\"The payload type ({0:?}) passed to `traceRay` does not match the previous one {1:?}\")]\n    MismatchedPayloadType(Handle<crate::Type>, Handle<crate::Type>),\n}\n\nbitflags::bitflags! {\n    #[repr(transparent)]\n    #[derive(Clone, Copy)]\n    struct ControlFlowAbility: u8 {\n        /// The control can return out of this block.\n        const RETURN = 0x1;\n        /// The control can break.\n        const BREAK = 0x2;\n        /// The control can continue.\n        const CONTINUE = 0x4;\n    }\n}\n\nstruct BlockInfo {\n    stages: super::ShaderStages,\n}\n\nstruct BlockContext<'a> {\n    abilities: ControlFlowAbility,\n    info: &'a FunctionInfo,\n    expressions: &'a Arena<crate::Expression>,\n    types: &'a UniqueArena<crate::Type>,\n    local_vars: &'a Arena<crate::LocalVariable>,\n    global_vars: &'a Arena<crate::GlobalVariable>,\n    functions: &'a Arena<crate::Function>,\n    special_types: &'a crate::SpecialTypes,\n    prev_infos: &'a [FunctionInfo],\n    return_type: Option<Handle<crate::Type>>,\n    local_expr_kind: &'a crate::proc::ExpressionKindTracker,\n}\n\nimpl<'a> BlockContext<'a> {\n    fn new(\n        fun: &'a crate::Function,\n        module: &'a crate::Module,\n        info: &'a FunctionInfo,\n        prev_infos: &'a [FunctionInfo],\n        local_expr_kind: &'a crate::proc::ExpressionKindTracker,\n    ) -> Self {\n        Self {\n            abilities: ControlFlowAbility::RETURN,\n            info,\n            expressions: &fun.expressions,\n            types: &module.types,\n            local_vars: &fun.local_variables,\n            global_vars: &module.global_variables,\n            functions: &module.functions,\n            special_types: &module.special_types,\n            prev_infos,\n            return_type: fun.result.as_ref().map(|fr| fr.ty),\n            local_expr_kind,\n        }\n    }\n\n    const fn with_abilities(&self, abilities: ControlFlowAbility) -> Self {\n        BlockContext { abilities, ..*self }\n    }\n\n    fn get_expression(&self, handle: Handle<crate::Expression>) -> &'a crate::Expression {\n        &self.expressions[handle]\n    }\n\n    fn resolve_type_impl(\n        &self,\n        handle: Handle<crate::Expression>,\n        valid_expressions: &HandleSet<crate::Expression>,\n    ) -> Result<&TypeResolution, WithSpan<ExpressionError>> {\n        if !valid_expressions.contains(handle) {\n            Err(ExpressionError::NotInScope.with_span_handle(handle, self.expressions))\n        } else {\n            Ok(&self.info[handle].ty)\n        }\n    }\n\n    fn resolve_type(\n        &self,\n        handle: Handle<crate::Expression>,\n        valid_expressions: &HandleSet<crate::Expression>,\n    ) -> Result<&TypeResolution, WithSpan<FunctionError>> {\n        self.resolve_type_impl(handle, valid_expressions)\n            .map_err_inner(|source| FunctionError::Expression { handle, source }.with_span())\n    }\n\n    fn resolve_type_inner(\n        &self,\n        handle: Handle<crate::Expression>,\n        valid_expressions: &HandleSet<crate::Expression>,\n    ) -> Result<&crate::TypeInner, WithSpan<FunctionError>> {\n        self.resolve_type(handle, valid_expressions)\n            .map(|tr| tr.inner_with(self.types))\n    }\n\n    fn resolve_pointer_type(&self, handle: Handle<crate::Expression>) -> &crate::TypeInner {\n        self.info[handle].ty.inner_with(self.types)\n    }\n\n    fn compare_types(&self, lhs: &TypeResolution, rhs: &TypeResolution) -> bool {\n        crate::proc::compare_types(lhs, rhs, self.types)\n    }\n}\n\nimpl super::Validator {\n    fn validate_call(\n        &mut self,\n        function: Handle<crate::Function>,\n        arguments: &[Handle<crate::Expression>],\n        result: Option<Handle<crate::Expression>>,\n        context: &BlockContext,\n    ) -> Result<super::ShaderStages, WithSpan<CallError>> {\n        let fun = &context.functions[function];\n        if fun.arguments.len() != arguments.len() {\n            return Err(CallError::ArgumentCount {\n                required: fun.arguments.len(),\n                seen: arguments.len(),\n            }\n            .with_span());\n        }\n        for (index, (arg, &expr)) in fun.arguments.iter().zip(arguments).enumerate() {\n            let ty = context\n                .resolve_type_impl(expr, &self.valid_expression_set)\n                .map_err_inner(|source| {\n                    CallError::Argument { index, source }\n                        .with_span_handle(expr, context.expressions)\n                })?;\n            if !context.compare_types(&TypeResolution::Handle(arg.ty), ty) {\n                return Err(CallError::ArgumentType {\n                    index,\n                    required: arg.ty,\n                    seen_expression: expr,\n                }\n                .with_span_handle(expr, context.expressions));\n            }\n        }\n\n        if let Some(expr) = result {\n            if self.valid_expression_set.insert(expr) {\n                self.valid_expression_list.push(expr);\n            } else {\n                return Err(CallError::ResultAlreadyInScope(expr)\n                    .with_span_handle(expr, context.expressions));\n            }\n            match context.expressions[expr] {\n                crate::Expression::CallResult(callee)\n                    if fun.result.is_some() && callee == function =>\n                {\n                    if !self.needs_visit.remove(expr) {\n                        return Err(CallError::ResultAlreadyPopulated(expr)\n                            .with_span_handle(expr, context.expressions));\n                    }\n                }\n                _ => {\n                    return Err(CallError::ExpressionMismatch(result)\n                        .with_span_handle(expr, context.expressions))\n                }\n            }\n        } else if fun.result.is_some() {\n            return Err(CallError::ExpressionMismatch(result).with_span());\n        }\n\n        let callee_info = &context.prev_infos[function.index()];\n        Ok(callee_info.available_stages)\n    }\n\n    fn emit_expression(\n        &mut self,\n        handle: Handle<crate::Expression>,\n        context: &BlockContext,\n    ) -> Result<(), WithSpan<FunctionError>> {\n        if self.valid_expression_set.insert(handle) {\n            self.valid_expression_list.push(handle);\n            Ok(())\n        } else {\n            Err(FunctionError::ExpressionAlreadyInScope(handle)\n                .with_span_handle(handle, context.expressions))\n        }\n    }\n\n    fn validate_atomic(\n        &mut self,\n        pointer: Handle<crate::Expression>,\n        fun: &crate::AtomicFunction,\n        value: Handle<crate::Expression>,\n        result: Option<Handle<crate::Expression>>,\n        span: crate::Span,\n        context: &BlockContext,\n    ) -> Result<(), WithSpan<FunctionError>> {\n        // The `pointer` operand must be a pointer to an atomic value.\n        let pointer_inner = context.resolve_type_inner(pointer, &self.valid_expression_set)?;\n        let crate::TypeInner::Pointer {\n            base: pointer_base,\n            space: pointer_space,\n        } = *pointer_inner\n        else {\n            log::error!(\"Atomic operation on type {:?}\", *pointer_inner);\n            return Err(AtomicError::InvalidPointer(pointer)\n                .with_span_handle(pointer, context.expressions)\n                .into_other());\n        };\n        let crate::TypeInner::Atomic(pointer_scalar) = context.types[pointer_base].inner else {\n            log::error!(\n                \"Atomic pointer to type {:?}\",\n                context.types[pointer_base].inner\n            );\n            return Err(AtomicError::InvalidPointer(pointer)\n                .with_span_handle(pointer, context.expressions)\n                .into_other());\n        };\n\n        // The `value` operand must be a scalar of the same type as the atomic.\n        let value_inner = context.resolve_type_inner(value, &self.valid_expression_set)?;\n        let crate::TypeInner::Scalar(value_scalar) = *value_inner else {\n            log::error!(\"Atomic operand type {:?}\", *value_inner);\n            return Err(AtomicError::InvalidOperand(value)\n                .with_span_handle(value, context.expressions)\n                .into_other());\n        };\n        if pointer_scalar != value_scalar {\n            log::error!(\"Atomic operand type {:?}\", *value_inner);\n            return Err(AtomicError::InvalidOperand(value)\n                .with_span_handle(value, context.expressions)\n                .into_other());\n        }\n\n        match pointer_scalar {\n            // Check for the special restrictions on 64-bit atomic operations.\n            //\n            // We don't need to consider other widths here: this function has already checked\n            // that `pointer`'s type is an `Atomic`, and `validate_type` has already checked\n            // that `Atomic` type has a permitted scalar width.\n            crate::Scalar::I64 | crate::Scalar::U64 => {\n                // `Capabilities::SHADER_INT64_ATOMIC_ALL_OPS` enables all sorts of 64-bit\n                // atomic operations.\n                if self\n                    .capabilities\n                    .contains(super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS)\n                {\n                    // okay\n                } else {\n                    // `Capabilities::SHADER_INT64_ATOMIC_MIN_MAX` allows `Min` and\n                    // `Max` on operations in `Storage`, without a return value.\n                    if matches!(\n                        *fun,\n                        crate::AtomicFunction::Min | crate::AtomicFunction::Max\n                    ) && matches!(pointer_space, crate::AddressSpace::Storage { .. })\n                        && result.is_none()\n                    {\n                        if !self\n                            .capabilities\n                            .contains(super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX)\n                        {\n                            log::error!(\"Int64 min-max atomic operations are not supported\");\n                            return Err(AtomicError::MissingCapability(\n                                super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,\n                            )\n                            .with_span_handle(value, context.expressions)\n                            .into_other());\n                        }\n                    } else {\n                        // Otherwise, we require the full 64-bit atomic capability.\n                        log::error!(\"Int64 atomic operations are not supported\");\n                        return Err(AtomicError::MissingCapability(\n                            super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,\n                        )\n                        .with_span_handle(value, context.expressions)\n                        .into_other());\n                    }\n                }\n            }\n            // Check for the special restrictions on 32-bit floating-point atomic operations.\n            crate::Scalar::F32 => {\n                // `Capabilities::SHADER_FLOAT32_ATOMIC` allows 32-bit floating-point\n                // atomic operations `Add`, `Subtract`, and `Exchange`\n                // in the `Storage` address space.\n                if !self\n                    .capabilities\n                    .contains(super::Capabilities::SHADER_FLOAT32_ATOMIC)\n                {\n                    log::error!(\"Float32 atomic operations are not supported\");\n                    return Err(AtomicError::MissingCapability(\n                        super::Capabilities::SHADER_FLOAT32_ATOMIC,\n                    )\n                    .with_span_handle(value, context.expressions)\n                    .into_other());\n                }\n                if !matches!(\n                    *fun,\n                    crate::AtomicFunction::Add\n                        | crate::AtomicFunction::Subtract\n                        | crate::AtomicFunction::Exchange { compare: None }\n                ) {\n                    log::error!(\"Float32 atomic operation {fun:?} is not supported\");\n                    return Err(AtomicError::InvalidOperator(*fun)\n                        .with_span_handle(value, context.expressions)\n                        .into_other());\n                }\n                if !matches!(pointer_space, crate::AddressSpace::Storage { .. }) {\n                    log::error!(\n                        \"Float32 atomic operations are only supported in the Storage address space\"\n                    );\n                    return Err(AtomicError::InvalidAddressSpace(pointer_space)\n                        .with_span_handle(value, context.expressions)\n                        .into_other());\n                }\n            }\n            _ => {}\n        }\n\n        // The result expression must be appropriate to the operation.\n        match result {\n            Some(result) => {\n                // The `result` handle must refer to an `AtomicResult` expression.\n                let crate::Expression::AtomicResult {\n                    ty: result_ty,\n                    comparison,\n                } = context.expressions[result]\n                else {\n                    return Err(AtomicError::InvalidResultExpression(result)\n                        .with_span_handle(result, context.expressions)\n                        .into_other());\n                };\n\n                // Note that this expression has been visited by the proper kind\n                // of statement.\n                if !self.needs_visit.remove(result) {\n                    return Err(AtomicError::ResultAlreadyPopulated(result)\n                        .with_span_handle(result, context.expressions)\n                        .into_other());\n                }\n\n                // The constraints on the result type depend on the atomic function.\n                if let crate::AtomicFunction::Exchange {\n                    compare: Some(compare),\n                } = *fun\n                {\n                    // The comparison value must be a scalar of the same type as the\n                    // atomic we're operating on.\n                    let compare_inner =\n                        context.resolve_type_inner(compare, &self.valid_expression_set)?;\n                    if !compare_inner.non_struct_equivalent(value_inner, context.types) {\n                        log::error!(\n                            \"Atomic exchange comparison has a different type from the value\"\n                        );\n                        return Err(AtomicError::InvalidOperand(compare)\n                            .with_span_handle(compare, context.expressions)\n                            .into_other());\n                    }\n\n                    // The result expression must be an `__atomic_compare_exchange_result`\n                    // struct whose `old_value` member is of the same type as the atomic\n                    // we're operating on.\n                    let crate::TypeInner::Struct { ref members, .. } =\n                        context.types[result_ty].inner\n                    else {\n                        return Err(AtomicError::ResultTypeMismatch(result)\n                            .with_span_handle(result, context.expressions)\n                            .into_other());\n                    };\n                    if !super::validate_atomic_compare_exchange_struct(\n                        context.types,\n                        members,\n                        |ty: &crate::TypeInner| *ty == crate::TypeInner::Scalar(pointer_scalar),\n                    ) {\n                        return Err(AtomicError::ResultTypeMismatch(result)\n                            .with_span_handle(result, context.expressions)\n                            .into_other());\n                    }\n\n                    // The result expression must be for a comparison operation.\n                    if !comparison {\n                        return Err(AtomicError::ResultExpressionNotExchange(result)\n                            .with_span_handle(result, context.expressions)\n                            .into_other());\n                    }\n                } else {\n                    // The result expression must be a scalar of the same type as the\n                    // atomic we're operating on.\n                    let result_inner = &context.types[result_ty].inner;\n                    if !result_inner.non_struct_equivalent(value_inner, context.types) {\n                        return Err(AtomicError::ResultTypeMismatch(result)\n                            .with_span_handle(result, context.expressions)\n                            .into_other());\n                    }\n\n                    // The result expression must not be for a comparison.\n                    if comparison {\n                        return Err(AtomicError::ResultExpressionExchange(result)\n                            .with_span_handle(result, context.expressions)\n                            .into_other());\n                    }\n                }\n                self.emit_expression(result, context)?;\n            }\n\n            None => {\n                // Exchange operations must always produce a value.\n                if let crate::AtomicFunction::Exchange { compare: None } = *fun {\n                    log::error!(\"Atomic exchange's value is unused\");\n                    return Err(AtomicError::MissingReturnValue\n                        .with_span_static(span, \"atomic exchange operation\")\n                        .into_other());\n                }\n            }\n        }\n\n        Ok(())\n    }\n    fn validate_subgroup_operation(\n        &mut self,\n        op: &crate::SubgroupOperation,\n        collective_op: &crate::CollectiveOperation,\n        argument: Handle<crate::Expression>,\n        result: Handle<crate::Expression>,\n        context: &BlockContext,\n    ) -> Result<(), WithSpan<FunctionError>> {\n        let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;\n\n        let (is_scalar, scalar) = match *argument_inner {\n            crate::TypeInner::Scalar(scalar) => (true, scalar),\n            crate::TypeInner::Vector { scalar, .. } => (false, scalar),\n            _ => {\n                log::error!(\"Subgroup operand type {argument_inner:?}\");\n                return Err(SubgroupError::InvalidOperand(argument)\n                    .with_span_handle(argument, context.expressions)\n                    .into_other());\n            }\n        };\n\n        use crate::ScalarKind as sk;\n        use crate::SubgroupOperation as sg;\n        match (scalar.kind, *op) {\n            (sk::Bool, sg::All | sg::Any) if is_scalar => {}\n            (sk::Sint | sk::Uint | sk::Float, sg::Add | sg::Mul | sg::Min | sg::Max) => {}\n            (sk::Sint | sk::Uint, sg::And | sg::Or | sg::Xor) => {}\n\n            (_, _) => {\n                log::error!(\"Subgroup operand type {argument_inner:?}\");\n                return Err(SubgroupError::InvalidOperand(argument)\n                    .with_span_handle(argument, context.expressions)\n                    .into_other());\n            }\n        };\n\n        use crate::CollectiveOperation as co;\n        match (*collective_op, *op) {\n            (\n                co::Reduce,\n                sg::All\n                | sg::Any\n                | sg::Add\n                | sg::Mul\n                | sg::Min\n                | sg::Max\n                | sg::And\n                | sg::Or\n                | sg::Xor,\n            ) => {}\n            (co::InclusiveScan | co::ExclusiveScan, sg::Add | sg::Mul) => {}\n\n            (_, _) => {\n                return Err(SubgroupError::UnknownOperation.with_span().into_other());\n            }\n        };\n\n        self.emit_expression(result, context)?;\n        match context.expressions[result] {\n            crate::Expression::SubgroupOperationResult { ty }\n                if { &context.types[ty].inner == argument_inner } => {}\n            _ => {\n                return Err(SubgroupError::ResultTypeMismatch(result)\n                    .with_span_handle(result, context.expressions)\n                    .into_other())\n            }\n        }\n        Ok(())\n    }\n    fn validate_subgroup_gather(\n        &mut self,\n        mode: &crate::GatherMode,\n        argument: Handle<crate::Expression>,\n        result: Handle<crate::Expression>,\n        context: &BlockContext,\n    ) -> Result<(), WithSpan<FunctionError>> {\n        match *mode {\n            crate::GatherMode::BroadcastFirst => {}\n            crate::GatherMode::Broadcast(index)\n            | crate::GatherMode::Shuffle(index)\n            | crate::GatherMode::ShuffleDown(index)\n            | crate::GatherMode::ShuffleUp(index)\n            | crate::GatherMode::ShuffleXor(index)\n            | crate::GatherMode::QuadBroadcast(index) => {\n                let index_ty = context.resolve_type_inner(index, &self.valid_expression_set)?;\n                match *index_ty {\n                    crate::TypeInner::Scalar(crate::Scalar::U32) => {}\n                    _ => {\n                        log::error!(\n                            \"Subgroup gather index type {index_ty:?}, expected unsigned int\"\n                        );\n                        return Err(SubgroupError::InvalidOperand(argument)\n                            .with_span_handle(index, context.expressions)\n                            .into_other());\n                    }\n                }\n            }\n            crate::GatherMode::QuadSwap(_) => {}\n        }\n        match *mode {\n            crate::GatherMode::Broadcast(index) | crate::GatherMode::QuadBroadcast(index) => {\n                if !context.local_expr_kind.is_const(index) {\n                    return Err(SubgroupError::InvalidInvocationIdExprType(index)\n                        .with_span_handle(index, context.expressions)\n                        .into_other());\n                }\n            }\n            _ => {}\n        }\n        let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;\n        if !matches!(*argument_inner,\n            crate::TypeInner::Scalar ( scalar, .. ) | crate::TypeInner::Vector { scalar, .. }\n            if matches!(scalar.kind, crate::ScalarKind::Uint | crate::ScalarKind::Sint | crate::ScalarKind::Float)\n        ) {\n            log::error!(\"Subgroup gather operand type {argument_inner:?}\");\n            return Err(SubgroupError::InvalidOperand(argument)\n                .with_span_handle(argument, context.expressions)\n                .into_other());\n        }\n\n        self.emit_expression(result, context)?;\n        match context.expressions[result] {\n            crate::Expression::SubgroupOperationResult { ty }\n                if { &context.types[ty].inner == argument_inner } => {}\n            _ => {\n                return Err(SubgroupError::ResultTypeMismatch(result)\n                    .with_span_handle(result, context.expressions)\n                    .into_other())\n            }\n        }\n        Ok(())\n    }\n\n    fn validate_block_impl(\n        &mut self,\n        statements: &crate::Block,\n        context: &BlockContext,\n    ) -> Result<BlockInfo, WithSpan<FunctionError>> {\n        use crate::{AddressSpace, Statement as S, TypeInner as Ti};\n        let mut stages = super::ShaderStages::all();\n        for (statement, &span) in statements.span_iter() {\n            match *statement {\n                S::Emit(ref range) => {\n                    for handle in range.clone() {\n                        use crate::Expression as Ex;\n                        match context.expressions[handle] {\n                            Ex::Literal(_)\n                            | Ex::Constant(_)\n                            | Ex::Override(_)\n                            | Ex::ZeroValue(_)\n                            | Ex::Compose { .. }\n                            | Ex::Access { .. }\n                            | Ex::AccessIndex { .. }\n                            | Ex::Splat { .. }\n                            | Ex::Swizzle { .. }\n                            | Ex::FunctionArgument(_)\n                            | Ex::GlobalVariable(_)\n                            | Ex::LocalVariable(_)\n                            | Ex::Load { .. }\n                            | Ex::ImageSample { .. }\n                            | Ex::ImageLoad { .. }\n                            | Ex::ImageQuery { .. }\n                            | Ex::Unary { .. }\n                            | Ex::Binary { .. }\n                            | Ex::Select { .. }\n                            | Ex::Derivative { .. }\n                            | Ex::Relational { .. }\n                            | Ex::Math { .. }\n                            | Ex::As { .. }\n                            | Ex::ArrayLength(_)\n                            | Ex::RayQueryGetIntersection { .. }\n                            | Ex::RayQueryVertexPositions { .. }\n                            | Ex::CooperativeLoad { .. }\n                            | Ex::CooperativeMultiplyAdd { .. } => {\n                                self.emit_expression(handle, context)?\n                            }\n                            Ex::CallResult(_)\n                            | Ex::AtomicResult { .. }\n                            | Ex::WorkGroupUniformLoadResult { .. }\n                            | Ex::RayQueryProceedResult\n                            | Ex::SubgroupBallotResult\n                            | Ex::SubgroupOperationResult { .. } => {\n                                return Err(FunctionError::EmitResult(handle)\n                                    .with_span_handle(handle, context.expressions));\n                            }\n                        }\n                    }\n                }\n                S::Block(ref block) => {\n                    let info = self.validate_block(block, context)?;\n                    stages &= info.stages;\n                }\n                S::If {\n                    condition,\n                    ref accept,\n                    ref reject,\n                } => {\n                    match *context.resolve_type_inner(condition, &self.valid_expression_set)? {\n                        Ti::Scalar(crate::Scalar {\n                            kind: crate::ScalarKind::Bool,\n                            width: _,\n                        }) => {}\n                        _ => {\n                            return Err(FunctionError::InvalidIfType(condition)\n                                .with_span_handle(condition, context.expressions))\n                        }\n                    }\n                    stages &= self.validate_block(accept, context)?.stages;\n                    stages &= self.validate_block(reject, context)?.stages;\n                }\n                S::Switch {\n                    selector,\n                    ref cases,\n                } => {\n                    let uint = match context\n                        .resolve_type_inner(selector, &self.valid_expression_set)?\n                        .scalar_kind()\n                    {\n                        Some(crate::ScalarKind::Uint) => true,\n                        Some(crate::ScalarKind::Sint) => false,\n                        _ => {\n                            return Err(FunctionError::InvalidSwitchType(selector)\n                                .with_span_handle(selector, context.expressions))\n                        }\n                    };\n                    self.switch_values.clear();\n                    for case in cases {\n                        match case.value {\n                            crate::SwitchValue::I32(_) if !uint => {}\n                            crate::SwitchValue::U32(_) if uint => {}\n                            crate::SwitchValue::Default => {}\n                            _ => {\n                                return Err(FunctionError::ConflictingCaseType.with_span_static(\n                                    case.body\n                                        .span_iter()\n                                        .next()\n                                        .map_or(Default::default(), |(_, s)| *s),\n                                    \"conflicting switch arm here\",\n                                ));\n                            }\n                        };\n                        if !self.switch_values.insert(case.value) {\n                            return Err(match case.value {\n                                crate::SwitchValue::Default => FunctionError::MultipleDefaultCases\n                                    .with_span_static(\n                                        case.body\n                                            .span_iter()\n                                            .next()\n                                            .map_or(Default::default(), |(_, s)| *s),\n                                        \"duplicated switch arm here\",\n                                    ),\n                                _ => FunctionError::ConflictingSwitchCase(case.value)\n                                    .with_span_static(\n                                        case.body\n                                            .span_iter()\n                                            .next()\n                                            .map_or(Default::default(), |(_, s)| *s),\n                                        \"conflicting switch arm here\",\n                                    ),\n                            });\n                        }\n                    }\n                    if !self.switch_values.contains(&crate::SwitchValue::Default) {\n                        return Err(FunctionError::MissingDefaultCase\n                            .with_span_static(span, \"missing default case\"));\n                    }\n                    if let Some(case) = cases.last() {\n                        if case.fall_through {\n                            return Err(FunctionError::LastCaseFallTrough.with_span_static(\n                                case.body\n                                    .span_iter()\n                                    .next()\n                                    .map_or(Default::default(), |(_, s)| *s),\n                                \"bad switch arm here\",\n                            ));\n                        }\n                    }\n                    let pass_through_abilities = context.abilities\n                        & (ControlFlowAbility::RETURN | ControlFlowAbility::CONTINUE);\n                    let sub_context =\n                        context.with_abilities(pass_through_abilities | ControlFlowAbility::BREAK);\n                    for case in cases {\n                        stages &= self.validate_block(&case.body, &sub_context)?.stages;\n                    }\n                }\n                S::Loop {\n                    ref body,\n                    ref continuing,\n                    break_if,\n                } => {\n                    // special handling for block scoping is needed here,\n                    // because the continuing{} block inherits the scope\n                    let base_expression_count = self.valid_expression_list.len();\n                    let pass_through_abilities = context.abilities & ControlFlowAbility::RETURN;\n                    stages &= self\n                        .validate_block_impl(\n                            body,\n                            &context.with_abilities(\n                                pass_through_abilities\n                                    | ControlFlowAbility::BREAK\n                                    | ControlFlowAbility::CONTINUE,\n                            ),\n                        )?\n                        .stages;\n                    stages &= self\n                        .validate_block_impl(\n                            continuing,\n                            &context.with_abilities(ControlFlowAbility::empty()),\n                        )?\n                        .stages;\n\n                    if let Some(condition) = break_if {\n                        match *context.resolve_type_inner(condition, &self.valid_expression_set)? {\n                            Ti::Scalar(crate::Scalar {\n                                kind: crate::ScalarKind::Bool,\n                                width: _,\n                            }) => {}\n                            _ => {\n                                return Err(FunctionError::InvalidIfType(condition)\n                                    .with_span_handle(condition, context.expressions))\n                            }\n                        }\n                    }\n\n                    for handle in self.valid_expression_list.drain(base_expression_count..) {\n                        self.valid_expression_set.remove(handle);\n                    }\n                }\n                S::Break => {\n                    if !context.abilities.contains(ControlFlowAbility::BREAK) {\n                        return Err(FunctionError::BreakOutsideOfLoopOrSwitch\n                            .with_span_static(span, \"invalid break\"));\n                    }\n                }\n                S::Continue => {\n                    if !context.abilities.contains(ControlFlowAbility::CONTINUE) {\n                        return Err(FunctionError::ContinueOutsideOfLoop\n                            .with_span_static(span, \"invalid continue\"));\n                    }\n                }\n                S::Return { value } => {\n                    if !context.abilities.contains(ControlFlowAbility::RETURN) {\n                        return Err(FunctionError::InvalidReturnSpot\n                            .with_span_static(span, \"invalid return\"));\n                    }\n                    let value_ty = value\n                        .map(|expr| context.resolve_type(expr, &self.valid_expression_set))\n                        .transpose()?;\n                    // We can't return pointers, but it seems best not to embed that\n                    // assumption here, so use `TypeInner::equivalent` for comparison.\n                    let okay = match (value_ty, context.return_type) {\n                        (None, None) => true,\n                        (Some(value_inner), Some(expected_ty)) => {\n                            context.compare_types(value_inner, &TypeResolution::Handle(expected_ty))\n                        }\n                        (_, _) => false,\n                    };\n\n                    if !okay {\n                        log::error!(\n                            \"Returning {:?} where {:?} is expected\",\n                            value_ty,\n                            context.return_type,\n                        );\n                        if let Some(handle) = value {\n                            return Err(FunctionError::InvalidReturnType {\n                                expression: value,\n                                expected_ty: context.return_type,\n                            }\n                            .with_span_handle(handle, context.expressions));\n                        } else {\n                            return Err(FunctionError::InvalidReturnType {\n                                expression: value,\n                                expected_ty: context.return_type,\n                            }\n                            .with_span_static(span, \"invalid return\"));\n                        }\n                    }\n                }\n                S::Kill => {\n                    stages &= super::ShaderStages::FRAGMENT;\n                }\n                S::ControlBarrier(barrier) | S::MemoryBarrier(barrier) => {\n                    stages &= super::ShaderStages::COMPUTE_LIKE;\n                    if barrier.contains(crate::Barrier::SUB_GROUP) {\n                        if !self.capabilities.contains(\n                            super::Capabilities::SUBGROUP | super::Capabilities::SUBGROUP_BARRIER,\n                        ) {\n                            return Err(FunctionError::MissingCapability(\n                                super::Capabilities::SUBGROUP\n                                    | super::Capabilities::SUBGROUP_BARRIER,\n                            )\n                            .with_span_static(span, \"missing capability for this operation\"));\n                        }\n                        if !self\n                            .subgroup_operations\n                            .contains(super::SubgroupOperationSet::BASIC)\n                        {\n                            return Err(FunctionError::InvalidSubgroup(\n                                SubgroupError::UnsupportedOperation(\n                                    super::SubgroupOperationSet::BASIC,\n                                ),\n                            )\n                            .with_span_static(span, \"support for this operation is not present\"));\n                        }\n                    }\n                }\n                S::Store { pointer, value } => {\n                    let mut current = pointer;\n                    loop {\n                        match context.expressions[current] {\n                            crate::Expression::Access { base, .. }\n                            | crate::Expression::AccessIndex { base, .. } => current = base,\n                            crate::Expression::LocalVariable(_)\n                            | crate::Expression::GlobalVariable(_)\n                            | crate::Expression::FunctionArgument(_) => break,\n                            _ => {\n                                return Err(FunctionError::InvalidStorePointer(current)\n                                    .with_span_handle(pointer, context.expressions))\n                            }\n                        }\n                    }\n\n                    let value_tr = context.resolve_type(value, &self.valid_expression_set)?;\n                    let value_ty = value_tr.inner_with(context.types);\n                    match *value_ty {\n                        Ti::Image { .. } | Ti::Sampler { .. } => {\n                            return Err(FunctionError::InvalidStoreTexture {\n                                actual: value,\n                                actual_ty: value_ty.clone(),\n                            }\n                            .with_span_context((\n                                context.expressions.get_span(value),\n                                format!(\"this value is of type {value_ty:?}\"),\n                            ))\n                            .with_span(span, \"expects a texture argument\"));\n                        }\n                        _ => {}\n                    }\n\n                    let pointer_ty = context.resolve_pointer_type(pointer);\n                    let pointer_base_tr = pointer_ty.pointer_base_type();\n                    let pointer_base_ty = pointer_base_tr\n                        .as_ref()\n                        .map(|ty| ty.inner_with(context.types));\n                    let good = if let Some(&Ti::Atomic(ref scalar)) = pointer_base_ty {\n                        // The Naga IR allows storing a scalar to an atomic.\n                        *value_ty == Ti::Scalar(*scalar)\n                    } else if let Some(tr) = pointer_base_tr {\n                        context.compare_types(value_tr, &tr)\n                    } else {\n                        false\n                    };\n\n                    if !good {\n                        return Err(FunctionError::InvalidStoreTypes { pointer, value }\n                            .with_span()\n                            .with_handle(pointer, context.expressions)\n                            .with_handle(value, context.expressions));\n                    }\n\n                    if let Some(space) = pointer_ty.pointer_space() {\n                        if !space.access().contains(crate::StorageAccess::STORE) {\n                            return Err(FunctionError::InvalidStorePointer(pointer)\n                                .with_span_static(\n                                    context.expressions.get_span(pointer),\n                                    \"writing to this location is not permitted\",\n                                ));\n                        }\n                    }\n                }\n                S::ImageStore {\n                    image,\n                    coordinate,\n                    array_index,\n                    value,\n                } => {\n                    //Note: this code uses a lot of `FunctionError::InvalidImageStore`,\n                    // and could probably be refactored.\n                    let global_var;\n                    let image_ty;\n                    match *context.get_expression(image) {\n                        crate::Expression::GlobalVariable(var_handle) => {\n                            global_var = &context.global_vars[var_handle];\n                            image_ty = global_var.ty;\n                        }\n                        // The `image` operand is indexing into a binding array,\n                        // so punch through the `Access`* expression and look at\n                        // the global behind it.\n                        crate::Expression::Access { base, .. }\n                        | crate::Expression::AccessIndex { base, .. } => {\n                            let crate::Expression::GlobalVariable(var_handle) =\n                                *context.get_expression(base)\n                            else {\n                                return Err(FunctionError::InvalidImageStore(\n                                    ExpressionError::ExpectedGlobalVariable,\n                                )\n                                .with_span_handle(image, context.expressions));\n                            };\n                            global_var = &context.global_vars[var_handle];\n\n                            // The global variable must be a binding array.\n                            let Ti::BindingArray { base, .. } = context.types[global_var.ty].inner\n                            else {\n                                return Err(FunctionError::InvalidImageStore(\n                                    ExpressionError::ExpectedBindingArrayType(global_var.ty),\n                                )\n                                .with_span_handle(global_var.ty, context.types));\n                            };\n\n                            image_ty = base;\n                        }\n                        _ => {\n                            return Err(FunctionError::InvalidImageStore(\n                                ExpressionError::ExpectedGlobalVariable,\n                            )\n                            .with_span_handle(image, context.expressions))\n                        }\n                    };\n\n                    // The `image` operand must be an `Image`.\n                    let Ti::Image {\n                        class,\n                        arrayed,\n                        dim,\n                    } = context.types[image_ty].inner\n                    else {\n                        return Err(FunctionError::InvalidImageStore(\n                            ExpressionError::ExpectedImageType(global_var.ty),\n                        )\n                        .with_span()\n                        .with_handle(global_var.ty, context.types)\n                        .with_handle(image, context.expressions));\n                    };\n\n                    // It had better be a storage image, since we're writing to it.\n                    let crate::ImageClass::Storage { format, .. } = class else {\n                        return Err(FunctionError::InvalidImageStore(\n                            ExpressionError::InvalidImageClass(class),\n                        )\n                        .with_span_handle(image, context.expressions));\n                    };\n\n                    // The `coordinate` operand must be a vector of the appropriate size.\n                    if context\n                        .resolve_type_inner(coordinate, &self.valid_expression_set)?\n                        .image_storage_coordinates()\n                        .is_none_or(|coord_dim| coord_dim != dim)\n                    {\n                        return Err(FunctionError::InvalidImageStore(\n                            ExpressionError::InvalidImageCoordinateType(dim, coordinate),\n                        )\n                        .with_span_handle(coordinate, context.expressions));\n                    }\n\n                    // The `array_index` operand should be present if and only if\n                    // the image itself is arrayed.\n                    if arrayed != array_index.is_some() {\n                        return Err(FunctionError::InvalidImageStore(\n                            ExpressionError::InvalidImageArrayIndex,\n                        )\n                        .with_span_handle(coordinate, context.expressions));\n                    }\n\n                    // If present, `array_index` must be a scalar integer type.\n                    if let Some(expr) = array_index {\n                        if !matches!(\n                            *context.resolve_type_inner(expr, &self.valid_expression_set)?,\n                            Ti::Scalar(crate::Scalar {\n                                kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                                width: _,\n                            })\n                        ) {\n                            return Err(FunctionError::InvalidImageStore(\n                                ExpressionError::InvalidImageArrayIndexType(expr),\n                            )\n                            .with_span_handle(expr, context.expressions));\n                        }\n                    }\n\n                    let value_ty = crate::TypeInner::Vector {\n                        size: crate::VectorSize::Quad,\n                        scalar: format.into(),\n                    };\n\n                    // The value we're writing had better match the scalar type\n                    // for `image`'s format.\n                    let actual_value_ty =\n                        context.resolve_type_inner(value, &self.valid_expression_set)?;\n                    if actual_value_ty != &value_ty {\n                        return Err(FunctionError::InvalidStoreValue {\n                            actual: value,\n                            actual_ty: actual_value_ty.clone(),\n                            expected_ty: value_ty.clone(),\n                        }\n                        .with_span_context((\n                            context.expressions.get_span(value),\n                            format!(\"this value is of type {actual_value_ty:?}\"),\n                        ))\n                        .with_span(\n                            span,\n                            format!(\"expects a value argument of type {value_ty:?}\"),\n                        ));\n                    }\n                }\n                S::Call {\n                    function,\n                    ref arguments,\n                    result,\n                } => match self.validate_call(function, arguments, result, context) {\n                    Ok(callee_stages) => stages &= callee_stages,\n                    Err(error) => {\n                        return Err(error.and_then(|error| {\n                            FunctionError::InvalidCall { function, error }\n                                .with_span_static(span, \"invalid function call\")\n                        }))\n                    }\n                },\n                S::Atomic {\n                    pointer,\n                    ref fun,\n                    value,\n                    result,\n                } => {\n                    self.validate_atomic(pointer, fun, value, result, span, context)?;\n                }\n                S::ImageAtomic {\n                    image,\n                    coordinate,\n                    array_index,\n                    fun,\n                    value,\n                } => {\n                    let var = match *context.get_expression(image) {\n                        crate::Expression::GlobalVariable(var_handle) => {\n                            &context.global_vars[var_handle]\n                        }\n                        // We're looking at a binding index situation, so punch through the index and look at the global behind it.\n                        crate::Expression::Access { base, .. }\n                        | crate::Expression::AccessIndex { base, .. } => {\n                            match *context.get_expression(base) {\n                                crate::Expression::GlobalVariable(var_handle) => {\n                                    &context.global_vars[var_handle]\n                                }\n                                _ => {\n                                    return Err(FunctionError::InvalidImageAtomic(\n                                        ExpressionError::ExpectedGlobalVariable,\n                                    )\n                                    .with_span_handle(image, context.expressions))\n                                }\n                            }\n                        }\n                        _ => {\n                            return Err(FunctionError::InvalidImageAtomic(\n                                ExpressionError::ExpectedGlobalVariable,\n                            )\n                            .with_span_handle(image, context.expressions))\n                        }\n                    };\n\n                    // Punch through a binding array to get the underlying type\n                    let global_ty = match context.types[var.ty].inner {\n                        Ti::BindingArray { base, .. } => &context.types[base].inner,\n                        ref inner => inner,\n                    };\n\n                    let value_ty = match *global_ty {\n                        Ti::Image {\n                            class,\n                            arrayed,\n                            dim,\n                        } => {\n                            match context\n                                .resolve_type_inner(coordinate, &self.valid_expression_set)?\n                                .image_storage_coordinates()\n                            {\n                                Some(coord_dim) if coord_dim == dim => {}\n                                _ => {\n                                    return Err(FunctionError::InvalidImageAtomic(\n                                        ExpressionError::InvalidImageCoordinateType(\n                                            dim, coordinate,\n                                        ),\n                                    )\n                                    .with_span_handle(coordinate, context.expressions));\n                                }\n                            };\n                            if arrayed != array_index.is_some() {\n                                return Err(FunctionError::InvalidImageAtomic(\n                                    ExpressionError::InvalidImageArrayIndex,\n                                )\n                                .with_span_handle(coordinate, context.expressions));\n                            }\n                            if let Some(expr) = array_index {\n                                match *context\n                                    .resolve_type_inner(expr, &self.valid_expression_set)?\n                                {\n                                    Ti::Scalar(crate::Scalar {\n                                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                                        width: _,\n                                    }) => {}\n                                    _ => {\n                                        return Err(FunctionError::InvalidImageAtomic(\n                                            ExpressionError::InvalidImageArrayIndexType(expr),\n                                        )\n                                        .with_span_handle(expr, context.expressions));\n                                    }\n                                }\n                            }\n                            match class {\n                                crate::ImageClass::Storage { format, access } => {\n                                    if !access.contains(crate::StorageAccess::ATOMIC) {\n                                        return Err(FunctionError::InvalidImageAtomic(\n                                            ExpressionError::InvalidImageStorageAccess(access),\n                                        )\n                                        .with_span_handle(image, context.expressions));\n                                    }\n                                    match format {\n                                        crate::StorageFormat::R64Uint => {\n                                            if !self.capabilities.intersects(\n                                                super::Capabilities::TEXTURE_INT64_ATOMIC,\n                                            ) {\n                                                return Err(FunctionError::MissingCapability(\n                                                    super::Capabilities::TEXTURE_INT64_ATOMIC,\n                                                )\n                                                .with_span_static(\n                                                    span,\n                                                    \"missing capability for this operation\",\n                                                ));\n                                            }\n                                            match fun {\n                                                crate::AtomicFunction::Min\n                                                | crate::AtomicFunction::Max => {}\n                                                _ => {\n                                                    return Err(\n                                                        FunctionError::InvalidImageAtomicFunction(\n                                                            fun,\n                                                        )\n                                                        .with_span_handle(\n                                                            image,\n                                                            context.expressions,\n                                                        ),\n                                                    );\n                                                }\n                                            }\n                                        }\n                                        crate::StorageFormat::R32Sint\n                                        | crate::StorageFormat::R32Uint => {\n                                            if !self\n                                                .capabilities\n                                                .intersects(super::Capabilities::TEXTURE_ATOMIC)\n                                            {\n                                                return Err(FunctionError::MissingCapability(\n                                                    super::Capabilities::TEXTURE_ATOMIC,\n                                                )\n                                                .with_span_static(\n                                                    span,\n                                                    \"missing capability for this operation\",\n                                                ));\n                                            }\n                                            match fun {\n                                                crate::AtomicFunction::Add\n                                                | crate::AtomicFunction::And\n                                                | crate::AtomicFunction::ExclusiveOr\n                                                | crate::AtomicFunction::InclusiveOr\n                                                | crate::AtomicFunction::Min\n                                                | crate::AtomicFunction::Max => {}\n                                                _ => {\n                                                    return Err(\n                                                        FunctionError::InvalidImageAtomicFunction(\n                                                            fun,\n                                                        )\n                                                        .with_span_handle(\n                                                            image,\n                                                            context.expressions,\n                                                        ),\n                                                    );\n                                                }\n                                            }\n                                        }\n                                        _ => {\n                                            return Err(FunctionError::InvalidImageAtomic(\n                                                ExpressionError::InvalidImageFormat(format),\n                                            )\n                                            .with_span_handle(image, context.expressions));\n                                        }\n                                    }\n                                    crate::TypeInner::Scalar(format.into())\n                                }\n                                _ => {\n                                    return Err(FunctionError::InvalidImageAtomic(\n                                        ExpressionError::InvalidImageClass(class),\n                                    )\n                                    .with_span_handle(image, context.expressions));\n                                }\n                            }\n                        }\n                        _ => {\n                            return Err(FunctionError::InvalidImageAtomic(\n                                ExpressionError::ExpectedImageType(var.ty),\n                            )\n                            .with_span()\n                            .with_handle(var.ty, context.types)\n                            .with_handle(image, context.expressions))\n                        }\n                    };\n\n                    if *context.resolve_type_inner(value, &self.valid_expression_set)? != value_ty {\n                        return Err(FunctionError::InvalidImageAtomicValue(value)\n                            .with_span_handle(value, context.expressions));\n                    }\n                }\n                S::WorkGroupUniformLoad { pointer, result } => {\n                    stages &= super::ShaderStages::COMPUTE_LIKE;\n                    let pointer_inner =\n                        context.resolve_type_inner(pointer, &self.valid_expression_set)?;\n                    match *pointer_inner {\n                        Ti::Pointer {\n                            space: AddressSpace::WorkGroup,\n                            ..\n                        } => {}\n                        Ti::ValuePointer {\n                            space: AddressSpace::WorkGroup,\n                            ..\n                        } => {}\n                        _ => {\n                            return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)\n                                .with_span_static(span, \"WorkGroupUniformLoad\"))\n                        }\n                    }\n                    self.emit_expression(result, context)?;\n                    let ty = match &context.expressions[result] {\n                        &crate::Expression::WorkGroupUniformLoadResult { ty } => ty,\n                        _ => {\n                            return Err(FunctionError::WorkgroupUniformLoadExpressionMismatch(\n                                result,\n                            )\n                            .with_span_static(span, \"WorkGroupUniformLoad\"));\n                        }\n                    };\n                    let expected_pointer_inner = Ti::Pointer {\n                        base: ty,\n                        space: AddressSpace::WorkGroup,\n                    };\n                    // workgroupUniformLoad on atomic<T> returns T, not atomic<T>.\n                    // Verify the pointer's atomic scalar matches the result scalar.\n                    let atomic_specialization_ok = match *pointer_inner {\n                        Ti::Pointer {\n                            base: pointer_base,\n                            space: AddressSpace::WorkGroup,\n                        } => match (&context.types[pointer_base].inner, &context.types[ty].inner) {\n                            (&Ti::Atomic(pointer_scalar), &Ti::Scalar(result_scalar)) => {\n                                pointer_scalar == result_scalar\n                            }\n                            _ => false,\n                        },\n                        _ => false,\n                    };\n                    if !expected_pointer_inner.non_struct_equivalent(pointer_inner, context.types)\n                        && !atomic_specialization_ok\n                    {\n                        return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)\n                            .with_span_static(span, \"WorkGroupUniformLoad\"));\n                    }\n                }\n                S::RayQuery { query, ref fun } => {\n                    let query_var = match *context.get_expression(query) {\n                        crate::Expression::LocalVariable(var) => &context.local_vars[var],\n                        ref other => {\n                            log::error!(\"Unexpected ray query expression {other:?}\");\n                            return Err(FunctionError::InvalidRayQueryExpression(query)\n                                .with_span_static(span, \"invalid query expression\"));\n                        }\n                    };\n                    let rq_vertex_return = match context.types[query_var.ty].inner {\n                        Ti::RayQuery { vertex_return } => vertex_return,\n                        ref other => {\n                            log::error!(\"Unexpected ray query type {other:?}\");\n                            return Err(FunctionError::InvalidRayQueryType(query_var.ty)\n                                .with_span_static(span, \"invalid query type\"));\n                        }\n                    };\n                    match *fun {\n                        crate::RayQueryFunction::Initialize {\n                            acceleration_structure,\n                            descriptor,\n                        } => {\n                            match *context.resolve_type_inner(\n                                acceleration_structure,\n                                &self.valid_expression_set,\n                            )? {\n                                Ti::AccelerationStructure { vertex_return } => {\n                                    if (!vertex_return) && rq_vertex_return {\n                                        return Err(FunctionError::MissingAccelerationStructureVertexReturn(acceleration_structure, query).with_span_static(span, \"invalid acceleration structure\"));\n                                    }\n                                }\n                                _ => {\n                                    return Err(FunctionError::InvalidAccelerationStructure(\n                                        acceleration_structure,\n                                    )\n                                    .with_span_static(span, \"invalid acceleration structure\"))\n                                }\n                            }\n                            let desc_ty_given = context\n                                .resolve_type_inner(descriptor, &self.valid_expression_set)?;\n                            let desc_ty_expected = context\n                                .special_types\n                                .ray_desc\n                                .map(|handle| &context.types[handle].inner);\n                            if Some(desc_ty_given) != desc_ty_expected {\n                                return Err(FunctionError::InvalidRayDescriptor(descriptor)\n                                    .with_span_static(span, \"invalid ray descriptor\"));\n                            }\n                        }\n                        crate::RayQueryFunction::Proceed { result } => {\n                            self.emit_expression(result, context)?;\n                        }\n                        crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                            match *context.resolve_type_inner(hit_t, &self.valid_expression_set)? {\n                                Ti::Scalar(crate::Scalar {\n                                    kind: crate::ScalarKind::Float,\n                                    width: _,\n                                }) => {}\n                                _ => {\n                                    return Err(FunctionError::InvalidHitDistanceType(hit_t)\n                                        .with_span_static(span, \"invalid hit_t\"))\n                                }\n                            }\n                        }\n                        crate::RayQueryFunction::ConfirmIntersection => {}\n                        crate::RayQueryFunction::Terminate => {}\n                    }\n                }\n                S::SubgroupBallot { result, predicate } => {\n                    stages &= self.subgroup_stages;\n                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {\n                        return Err(FunctionError::MissingCapability(\n                            super::Capabilities::SUBGROUP,\n                        )\n                        .with_span_static(span, \"missing capability for this operation\"));\n                    }\n                    if !self\n                        .subgroup_operations\n                        .contains(super::SubgroupOperationSet::BALLOT)\n                    {\n                        return Err(FunctionError::InvalidSubgroup(\n                            SubgroupError::UnsupportedOperation(\n                                super::SubgroupOperationSet::BALLOT,\n                            ),\n                        )\n                        .with_span_static(span, \"support for this operation is not present\"));\n                    }\n                    if let Some(predicate) = predicate {\n                        let predicate_inner =\n                            context.resolve_type_inner(predicate, &self.valid_expression_set)?;\n                        if !matches!(\n                            *predicate_inner,\n                            crate::TypeInner::Scalar(crate::Scalar::BOOL,)\n                        ) {\n                            log::error!(\n                                \"Subgroup ballot predicate type {predicate_inner:?} expected bool\"\n                            );\n                            return Err(SubgroupError::InvalidOperand(predicate)\n                                .with_span_handle(predicate, context.expressions)\n                                .into_other());\n                        }\n                    }\n                    self.emit_expression(result, context)?;\n                }\n                S::SubgroupCollectiveOperation {\n                    ref op,\n                    ref collective_op,\n                    argument,\n                    result,\n                } => {\n                    stages &= self.subgroup_stages;\n                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {\n                        return Err(FunctionError::MissingCapability(\n                            super::Capabilities::SUBGROUP,\n                        )\n                        .with_span_static(span, \"missing capability for this operation\"));\n                    }\n                    let operation = op.required_operations();\n                    if !self.subgroup_operations.contains(operation) {\n                        return Err(FunctionError::InvalidSubgroup(\n                            SubgroupError::UnsupportedOperation(operation),\n                        )\n                        .with_span_static(span, \"support for this operation is not present\"));\n                    }\n                    self.validate_subgroup_operation(op, collective_op, argument, result, context)?;\n                }\n                S::SubgroupGather {\n                    ref mode,\n                    argument,\n                    result,\n                } => {\n                    stages &= self.subgroup_stages;\n                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {\n                        return Err(FunctionError::MissingCapability(\n                            super::Capabilities::SUBGROUP,\n                        )\n                        .with_span_static(span, \"missing capability for this operation\"));\n                    }\n                    let operation = mode.required_operations();\n                    if !self.subgroup_operations.contains(operation) {\n                        return Err(FunctionError::InvalidSubgroup(\n                            SubgroupError::UnsupportedOperation(operation),\n                        )\n                        .with_span_static(span, \"support for this operation is not present\"));\n                    }\n                    self.validate_subgroup_gather(mode, argument, result, context)?;\n                }\n                S::CooperativeStore { target, ref data } => {\n                    stages &= super::ShaderStages::COMPUTE;\n\n                    let target_scalar =\n                        match *context.resolve_type_inner(target, &self.valid_expression_set)? {\n                            Ti::CooperativeMatrix { scalar, .. } => scalar,\n                            ref other => {\n                                log::error!(\"Target operand type: {other:?}\");\n                                return Err(FunctionError::InvalidCooperativeStoreTarget(target)\n                                    .with_span_handle(target, context.expressions));\n                            }\n                        };\n\n                    let ptr_ty = context.resolve_pointer_type(data.pointer);\n                    let ptr_scalar = ptr_ty\n                        .pointer_base_type()\n                        .and_then(|tr| tr.inner_with(context.types).scalar());\n                    if ptr_scalar != Some(target_scalar) {\n                        return Err(FunctionError::InvalidCooperativeDataPointer(data.pointer)\n                            .with_span_handle(data.pointer, context.expressions));\n                    }\n\n                    let ptr_space = ptr_ty.pointer_space().unwrap_or(AddressSpace::Handle);\n                    if !ptr_space.access().contains(crate::StorageAccess::STORE) {\n                        return Err(FunctionError::InvalidStorePointer(data.pointer)\n                            .with_span_static(\n                                context.expressions.get_span(data.pointer),\n                                \"writing to this location is not permitted\",\n                            ));\n                    }\n                }\n                S::RayPipelineFunction(ref fun) => match *fun {\n                    crate::RayPipelineFunction::TraceRay {\n                        acceleration_structure,\n                        descriptor,\n                        payload,\n                    } => {\n                        match *context.resolve_type_inner(\n                            acceleration_structure,\n                            &self.valid_expression_set,\n                        )? {\n                            crate::TypeInner::AccelerationStructure { vertex_return } => {\n                                if !vertex_return {\n                                    self.trace_rays_vertex_return =\n                                        super::TraceRayVertexReturnState::NoVertexReturn(span);\n                                } else if let super::TraceRayVertexReturnState::NoTraceRays =\n                                    self.trace_rays_vertex_return\n                                {\n                                    self.trace_rays_vertex_return =\n                                        super::TraceRayVertexReturnState::VertexReturn;\n                                }\n                            }\n                            _ => {\n                                return Err(FunctionError::InvalidAccelerationStructure(\n                                    acceleration_structure,\n                                )\n                                .with_span_handle(acceleration_structure, context.expressions))\n                            }\n                        }\n\n                        let current_payload_ty = match *context\n                            .resolve_type_inner(payload, &self.valid_expression_set)?\n                        {\n                            crate::TypeInner::Pointer { base, space } => {\n                                match space {\n                                    AddressSpace::RayPayload | AddressSpace::IncomingRayPayload => {\n                                    }\n                                    space => {\n                                        return Err(FunctionError::InvalidPayloadAddressSpace(\n                                            space,\n                                        )\n                                        .with_span_handle(payload, context.expressions))\n                                    }\n                                }\n                                base\n                            }\n                            _ => {\n                                return Err(FunctionError::InvalidPayloadType\n                                    .with_span_handle(payload, context.expressions))\n                            }\n                        };\n\n                        let ty = *self\n                            .trace_rays_payload_type\n                            .get_or_insert(current_payload_ty);\n\n                        if ty != current_payload_ty {\n                            return Err(FunctionError::MismatchedPayloadType(\n                                current_payload_ty,\n                                ty,\n                            )\n                            .with_span_handle(ty, context.types));\n                        }\n\n                        let desc_ty_given =\n                            context.resolve_type_inner(descriptor, &self.valid_expression_set)?;\n                        let desc_ty_expected = context\n                            .special_types\n                            .ray_desc\n                            .map(|handle| &context.types[handle].inner);\n                        if Some(desc_ty_given) != desc_ty_expected {\n                            return Err(FunctionError::InvalidRayDescriptor(descriptor)\n                                .with_span_static(span, \"invalid ray descriptor\"));\n                        }\n                    }\n                },\n            }\n        }\n        Ok(BlockInfo { stages })\n    }\n\n    fn validate_block(\n        &mut self,\n        statements: &crate::Block,\n        context: &BlockContext,\n    ) -> Result<BlockInfo, WithSpan<FunctionError>> {\n        let base_expression_count = self.valid_expression_list.len();\n        let info = self.validate_block_impl(statements, context)?;\n        for handle in self.valid_expression_list.drain(base_expression_count..) {\n            self.valid_expression_set.remove(handle);\n        }\n        Ok(info)\n    }\n\n    fn validate_local_var(\n        &self,\n        var: &crate::LocalVariable,\n        gctx: crate::proc::GlobalCtx,\n        fun_info: &FunctionInfo,\n        local_expr_kind: &crate::proc::ExpressionKindTracker,\n    ) -> Result<(), LocalVariableError> {\n        log::debug!(\"var {var:?}\");\n        let type_info = self\n            .types\n            .get(var.ty.index())\n            .ok_or(LocalVariableError::InvalidType(var.ty))?;\n        if !type_info.flags.contains(super::TypeFlags::CONSTRUCTIBLE) {\n            return Err(LocalVariableError::InvalidType(var.ty));\n        }\n\n        if let Some(init) = var.init {\n            if !gctx.compare_types(&TypeResolution::Handle(var.ty), &fun_info[init].ty) {\n                return Err(LocalVariableError::InitializerType);\n            }\n\n            if !local_expr_kind.is_const_or_override(init) {\n                return Err(LocalVariableError::NonConstOrOverrideInitializer);\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn validate_function(\n        &mut self,\n        fun: &crate::Function,\n        module: &crate::Module,\n        mod_info: &ModuleInfo,\n        entry_point: bool,\n    ) -> Result<FunctionInfo, WithSpan<FunctionError>> {\n        let mut info = mod_info.process_function(fun, module, self.flags, self.capabilities)?;\n\n        let local_expr_kind = crate::proc::ExpressionKindTracker::from_arena(&fun.expressions);\n\n        for (var_handle, var) in fun.local_variables.iter() {\n            self.validate_local_var(var, module.to_ctx(), &info, &local_expr_kind)\n                .map_err(|source| {\n                    FunctionError::LocalVariable {\n                        handle: var_handle,\n                        name: var.name.clone().unwrap_or_default(),\n                        source,\n                    }\n                    .with_span_handle(var.ty, &module.types)\n                    .with_handle(var_handle, &fun.local_variables)\n                })?;\n        }\n\n        for (index, argument) in fun.arguments.iter().enumerate() {\n            match module.types[argument.ty].inner.pointer_space() {\n                Some(crate::AddressSpace::Private | crate::AddressSpace::Function) | None => {}\n                Some(other) => {\n                    return Err(FunctionError::InvalidArgumentPointerSpace {\n                        index,\n                        name: argument.name.clone().unwrap_or_default(),\n                        space: other,\n                    }\n                    .with_span_handle(argument.ty, &module.types))\n                }\n            }\n            // Check for the least informative error last.\n            if !self.types[argument.ty.index()]\n                .flags\n                .contains(super::TypeFlags::ARGUMENT)\n            {\n                return Err(FunctionError::InvalidArgumentType {\n                    index,\n                    name: argument.name.clone().unwrap_or_default(),\n                }\n                .with_span_handle(argument.ty, &module.types));\n            }\n\n            if !entry_point && argument.binding.is_some() {\n                return Err(FunctionError::PipelineInputRegularFunction {\n                    name: argument.name.clone().unwrap_or_default(),\n                }\n                .with_span_handle(argument.ty, &module.types));\n            }\n        }\n\n        if let Some(ref result) = fun.result {\n            if !self.types[result.ty.index()]\n                .flags\n                .contains(super::TypeFlags::CONSTRUCTIBLE)\n            {\n                return Err(FunctionError::NonConstructibleReturnType\n                    .with_span_handle(result.ty, &module.types));\n            }\n\n            if !entry_point && result.binding.is_some() {\n                return Err(FunctionError::PipelineOutputRegularFunction\n                    .with_span_handle(result.ty, &module.types));\n            }\n        }\n\n        self.valid_expression_set.clear_for_arena(&fun.expressions);\n        self.valid_expression_list.clear();\n        self.needs_visit.clear_for_arena(&fun.expressions);\n        for (handle, expr) in fun.expressions.iter() {\n            if expr.needs_pre_emit() {\n                self.valid_expression_set.insert(handle);\n            }\n            if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {\n                // Mark expressions that need to be visited by a particular kind of\n                // statement.\n                if let crate::Expression::CallResult(_) | crate::Expression::AtomicResult { .. } =\n                    *expr\n                {\n                    self.needs_visit.insert(handle);\n                }\n\n                match self.validate_expression(\n                    handle,\n                    expr,\n                    fun,\n                    module,\n                    &info,\n                    mod_info,\n                    &local_expr_kind,\n                ) {\n                    Ok(stages) => info.available_stages &= stages,\n                    Err(source) => {\n                        return Err(FunctionError::Expression { handle, source }\n                            .with_span_handle(handle, &fun.expressions))\n                    }\n                }\n            }\n        }\n\n        if self.flags.contains(super::ValidationFlags::BLOCKS) {\n            let stages = self\n                .validate_block(\n                    &fun.body,\n                    &BlockContext::new(fun, module, &info, &mod_info.functions, &local_expr_kind),\n                )?\n                .stages;\n            info.available_stages &= stages;\n\n            if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {\n                if let Some(handle) = self.needs_visit.iter().next() {\n                    return Err(FunctionError::UnvisitedExpression(handle)\n                        .with_span_handle(handle, &fun.expressions));\n                }\n            }\n        }\n        Ok(info)\n    }\n}\n"
  },
  {
    "path": "naga/src/valid/handles.rs",
    "content": "//! Implementation of `Validator::validate_module_handles`.\n\nuse core::{convert::TryInto, hash::Hash};\n\nuse super::{TypeError, ValidationError};\nuse crate::non_max_u32::NonMaxU32;\nuse crate::{\n    arena::{BadHandle, BadRangeError},\n    diagnostic_filter::DiagnosticFilterNode,\n    EntryPoint, Handle,\n};\nuse crate::{Arena, UniqueArena};\n\nuse alloc::string::ToString;\n\nimpl super::Validator {\n    /// Validates that all handles within `module` are:\n    ///\n    /// * Valid, in the sense that they contain indices within each arena structure inside the\n    ///   [`crate::Module`] type.\n    /// * No arena contents contain any items that have forward dependencies; that is, the value\n    ///   associated with a handle only may contain references to handles in the same arena that\n    ///   were constructed before it.\n    ///\n    /// By validating the above conditions, we free up subsequent logic to assume that handle\n    /// accesses are infallible.\n    ///\n    /// # Errors\n    ///\n    /// Errors returned by this method are intentionally sparse, for simplicity of implementation.\n    /// It is expected that only buggy frontends or fuzzers should ever emit IR that fails this\n    /// validation pass.\n    pub(super) fn validate_module_handles(module: &crate::Module) -> Result<(), ValidationError> {\n        let &crate::Module {\n            ref constants,\n            ref overrides,\n            ref entry_points,\n            ref functions,\n            ref global_variables,\n            ref types,\n            ref special_types,\n            ref global_expressions,\n            ref diagnostic_filters,\n            ref diagnostic_filter_leaf,\n            ref doc_comments,\n        } = module;\n\n        // Because types can refer to global expressions and vice versa, to\n        // ensure the overall structure is free of cycles, we must traverse them\n        // both in tandem.\n        //\n        // Try to visit all types and global expressions in an order such that\n        // each item refers only to previously visited items. If we succeed,\n        // that shows that there cannot be any cycles, since walking any edge\n        // advances you towards the beginning of the visiting order.\n        //\n        // Validate all the handles in types and expressions as we traverse the\n        // arenas.\n        let mut global_exprs_iter = global_expressions.iter().peekable();\n        for (th, t) in types.iter() {\n            // Imagine the `for` loop and `global_exprs_iter` as two fingers\n            // walking the type and global expression arenas. They don't visit\n            // elements at the same rate: sometimes one processes a bunch of\n            // elements while the other one stays still. But at each point, they\n            // check that the two ranges of elements they've visited only refer\n            // to other elements in those ranges.\n            //\n            // For brevity, we'll say 'handles behind `global_exprs_iter`' to\n            // mean handles that have already been produced by\n            // `global_exprs_iter`. Once `global_exprs_iter` returns `None`, all\n            // global expression handles are 'behind' it.\n            //\n            // At this point:\n            //\n            // - All types visited by prior iterations (that is, before\n            //   `th`/`t`) refer only to expressions behind `global_exprs_iter`.\n            //\n            //   On the first iteration, this is obviously true: there are no\n            //   prior iterations, and `global_exprs_iter` hasn't produced\n            //   anything yet. At the bottom of the loop, we'll claim that it's\n            //   true for `th`/`t` as well, so the condition remains true when\n            //   we advance to the next type.\n            //\n            // - All expressions behind `global_exprs_iter` refer only to\n            //   previously visited types.\n            //\n            //   Again, trivially true at the start, and we'll show it's true\n            //   about each expression that `global_exprs_iter` produces.\n            //\n            // Once we also check that arena elements only refer to prior\n            // elements in that arena, we can see that `th`/`t` does not\n            // participate in a cycle: it only refers to previously visited\n            // types and expressions behind `global_exprs_iter`, and none of\n            // those refer to `th`/`t`, because they passed the same checks\n            // before we reached `th`/`t`.\n            if let Some(max_expr) = Self::validate_type_handles((th, t), overrides)? {\n                max_expr.check_valid_for(global_expressions)?;\n                // Since `t` refers to `max_expr`, if we want our invariants to\n                // remain true, we must advance `global_exprs_iter` beyond\n                // `max_expr`.\n                while let Some((eh, e)) = global_exprs_iter.next_if(|&(eh, _)| eh <= max_expr) {\n                    if let Some(max_type) =\n                        Self::validate_const_expression_handles((eh, e), constants, overrides)?\n                    {\n                        // Show that `eh` refers only to previously visited types.\n                        th.check_dep(max_type)?;\n                    }\n                    // We've advanced `global_exprs_iter` past `eh` already. But\n                    // since we now know that `eh` refers only to previously\n                    // visited types, it is again true that all expressions\n                    // behind `global_exprs_iter` refer only to previously\n                    // visited types. So we can continue to the next expression.\n                }\n            }\n\n            // Here we know that if `th` refers to any expressions at all,\n            // `max_expr` is the latest one. And we know that `max_expr` is\n            // behind `global_exprs_iter`. So `th` refers only to expressions\n            // behind `global_exprs_iter`, and the invariants will still be\n            // true on the next iteration.\n        }\n\n        // Since we also enforced the usual intra-arena rules that expressions\n        // refer only to prior expressions, expressions can only form cycles if\n        // they include types. But we've shown that all types are acyclic, so\n        // all expressions must be acyclic as well.\n        //\n        // Validate the remaining expressions normally.\n        for handle_and_expr in global_exprs_iter {\n            Self::validate_const_expression_handles(handle_and_expr, constants, overrides)?;\n        }\n\n        let validate_type = |handle| Self::validate_type_handle(handle, types);\n        let validate_const_expr =\n            |handle| Self::validate_expression_handle(handle, global_expressions);\n\n        for (_handle, constant) in constants.iter() {\n            let &crate::Constant { name: _, ty, init } = constant;\n            validate_type(ty)?;\n            validate_const_expr(init)?;\n        }\n\n        for (_handle, r#override) in overrides.iter() {\n            let &crate::Override {\n                name: _,\n                id: _,\n                ty,\n                init,\n            } = r#override;\n            validate_type(ty)?;\n            if let Some(init_expr) = init {\n                validate_const_expr(init_expr)?;\n            }\n        }\n\n        for (_handle, global_variable) in global_variables.iter() {\n            let &crate::GlobalVariable {\n                name: _,\n                space: _,\n                binding: _,\n                ty,\n                init,\n                memory_decorations: _,\n            } = global_variable;\n            validate_type(ty)?;\n            if let Some(init_expr) = init {\n                validate_const_expr(init_expr)?;\n            }\n        }\n\n        let validate_function = |function_handle, function: &_| -> Result<_, InvalidHandleError> {\n            let &crate::Function {\n                name: _,\n                ref arguments,\n                ref result,\n                ref local_variables,\n                ref expressions,\n                ref named_expressions,\n                ref body,\n                ref diagnostic_filter_leaf,\n            } = function;\n\n            for arg in arguments.iter() {\n                let &crate::FunctionArgument {\n                    name: _,\n                    ty,\n                    binding: _,\n                } = arg;\n                validate_type(ty)?;\n            }\n\n            if let &Some(crate::FunctionResult { ty, binding: _ }) = result {\n                validate_type(ty)?;\n            }\n\n            for (_handle, local_variable) in local_variables.iter() {\n                let &crate::LocalVariable { name: _, ty, init } = local_variable;\n                validate_type(ty)?;\n                if let Some(init) = init {\n                    Self::validate_expression_handle(init, expressions)?;\n                }\n            }\n\n            for handle in named_expressions.keys().copied() {\n                Self::validate_expression_handle(handle, expressions)?;\n            }\n\n            for handle_and_expr in expressions.iter() {\n                Self::validate_expression_handles(\n                    handle_and_expr,\n                    constants,\n                    overrides,\n                    types,\n                    local_variables,\n                    global_variables,\n                    functions,\n                    function_handle,\n                )?;\n            }\n\n            Self::validate_block_handles(body, expressions, functions)?;\n\n            if let Some(handle) = *diagnostic_filter_leaf {\n                handle.check_valid_for(diagnostic_filters)?;\n            }\n\n            Ok(())\n        };\n\n        for entry_point in entry_points.iter() {\n            validate_function(None, &entry_point.function)?;\n            if let Some(sizes) = entry_point.workgroup_size_overrides {\n                for size in sizes.iter().filter_map(|x| *x) {\n                    validate_const_expr(size)?;\n                }\n            }\n            if let Some(task_payload) = entry_point.task_payload {\n                Self::validate_global_variable_handle(task_payload, global_variables)?;\n            }\n            if let Some(ref mesh_info) = entry_point.mesh_info {\n                Self::validate_global_variable_handle(mesh_info.output_variable, global_variables)?;\n                validate_type(mesh_info.vertex_output_type)?;\n                validate_type(mesh_info.primitive_output_type)?;\n                for ov in mesh_info\n                    .max_vertices_override\n                    .iter()\n                    .chain(mesh_info.max_primitives_override.iter())\n                {\n                    validate_const_expr(*ov)?;\n                }\n            }\n        }\n\n        for (function_handle, function) in functions.iter() {\n            validate_function(Some(function_handle), function)?;\n        }\n\n        if let Some(ty) = special_types.ray_desc {\n            validate_type(ty)?;\n        }\n        if let Some(ty) = special_types.ray_intersection {\n            validate_type(ty)?;\n        }\n        if let Some(ty) = special_types.ray_vertex_return {\n            validate_type(ty)?;\n        }\n\n        for (handle, _node) in diagnostic_filters.iter() {\n            let DiagnosticFilterNode { inner: _, parent } = diagnostic_filters[handle];\n            handle.check_dep_opt(parent)?;\n        }\n        if let Some(handle) = *diagnostic_filter_leaf {\n            handle.check_valid_for(diagnostic_filters)?;\n        }\n\n        if let Some(doc_comments) = doc_comments.as_ref() {\n            let crate::DocComments {\n                module: _,\n                types: ref doc_comments_for_types,\n                struct_members: ref doc_comments_for_struct_members,\n                entry_points: ref doc_comments_for_entry_points,\n                functions: ref doc_comments_for_functions,\n                constants: ref doc_comments_for_constants,\n                global_variables: ref doc_comments_for_global_variables,\n            } = **doc_comments;\n\n            for (&ty, _) in doc_comments_for_types.iter() {\n                validate_type(ty)?;\n            }\n\n            for (&(ty, struct_member_index), _) in doc_comments_for_struct_members.iter() {\n                validate_type(ty)?;\n                let struct_type = types.get_handle(ty).unwrap();\n                match struct_type.inner {\n                    crate::TypeInner::Struct {\n                        ref members,\n                        span: ref _span,\n                    } => {\n                        (0..members.len())\n                            .contains(&struct_member_index)\n                            .then_some(())\n                            // TODO: what errors should this be?\n                            .ok_or_else(|| ValidationError::Type {\n                                handle: ty,\n                                name: struct_type.name.as_ref().map_or_else(\n                                    || \"members length incorrect\".to_string(),\n                                    |name| name.to_string(),\n                                ),\n                                source: TypeError::InvalidData(ty),\n                            })?;\n                    }\n                    _ => {\n                        // TODO: internal error ? We should never get here.\n                        // If entering there, it's probably that we forgot to adjust a handle in the compact phase.\n                        return Err(ValidationError::Type {\n                            handle: ty,\n                            name: struct_type\n                                .name\n                                .as_ref()\n                                .map_or_else(|| \"Unknown\".to_string(), |name| name.to_string()),\n                            source: TypeError::InvalidData(ty),\n                        });\n                    }\n                }\n                for (&function, _) in doc_comments_for_functions.iter() {\n                    Self::validate_function_handle(function, functions)?;\n                }\n                for (&entry_point_index, _) in doc_comments_for_entry_points.iter() {\n                    Self::validate_entry_point_index(entry_point_index, entry_points)?;\n                }\n                for (&constant, _) in doc_comments_for_constants.iter() {\n                    Self::validate_constant_handle(constant, constants)?;\n                }\n                for (&global_variable, _) in doc_comments_for_global_variables.iter() {\n                    Self::validate_global_variable_handle(global_variable, global_variables)?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn validate_type_handle(\n        handle: Handle<crate::Type>,\n        types: &UniqueArena<crate::Type>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for_uniq(types).map(|_| ())\n    }\n\n    fn validate_constant_handle(\n        handle: Handle<crate::Constant>,\n        constants: &Arena<crate::Constant>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for(constants).map(|_| ())\n    }\n\n    fn validate_global_variable_handle(\n        handle: Handle<crate::GlobalVariable>,\n        global_variables: &Arena<crate::GlobalVariable>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for(global_variables).map(|_| ())\n    }\n\n    fn validate_override_handle(\n        handle: Handle<crate::Override>,\n        overrides: &Arena<crate::Override>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for(overrides).map(|_| ())\n    }\n\n    fn validate_expression_handle(\n        handle: Handle<crate::Expression>,\n        expressions: &Arena<crate::Expression>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for(expressions).map(|_| ())\n    }\n\n    fn validate_function_handle(\n        handle: Handle<crate::Function>,\n        functions: &Arena<crate::Function>,\n    ) -> Result<(), InvalidHandleError> {\n        handle.check_valid_for(functions).map(|_| ())\n    }\n\n    /// Validate all handles that occur in `ty`, whose handle is `handle`.\n    ///\n    /// If `ty` refers to any expressions, return the highest-indexed expression\n    /// handle that it uses. This is used for detecting cycles between the\n    /// expression and type arenas.\n    fn validate_type_handles(\n        (handle, ty): (Handle<crate::Type>, &crate::Type),\n        overrides: &Arena<crate::Override>,\n    ) -> Result<Option<Handle<crate::Expression>>, InvalidHandleError> {\n        let max_expr = match ty.inner {\n            crate::TypeInner::Scalar { .. }\n            | crate::TypeInner::Vector { .. }\n            | crate::TypeInner::Matrix { .. }\n            | crate::TypeInner::CooperativeMatrix { .. }\n            | crate::TypeInner::ValuePointer { .. }\n            | crate::TypeInner::Atomic { .. }\n            | crate::TypeInner::Image { .. }\n            | crate::TypeInner::Sampler { .. }\n            | crate::TypeInner::AccelerationStructure { .. }\n            | crate::TypeInner::RayQuery { .. } => None,\n            crate::TypeInner::Pointer { base, space: _ } => {\n                handle.check_dep(base)?;\n                None\n            }\n            crate::TypeInner::Array { base, size, .. }\n            | crate::TypeInner::BindingArray { base, size, .. } => {\n                handle.check_dep(base)?;\n                match size {\n                    crate::ArraySize::Pending(h) => {\n                        Self::validate_override_handle(h, overrides)?;\n                        let r#override = &overrides[h];\n                        handle.check_dep(r#override.ty)?;\n                        r#override.init\n                    }\n                    crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => None,\n                }\n            }\n            crate::TypeInner::Struct {\n                ref members,\n                span: _,\n            } => {\n                handle.check_dep_iter(members.iter().map(|m| m.ty))?;\n                None\n            }\n        };\n\n        Ok(max_expr)\n    }\n\n    fn validate_entry_point_index(\n        entry_point_index: usize,\n        entry_points: &[EntryPoint],\n    ) -> Result<(), InvalidHandleError> {\n        (0..entry_points.len())\n            .contains(&entry_point_index)\n            .then_some(())\n            .ok_or_else(|| {\n                BadHandle {\n                    kind: \"EntryPoint\",\n                    index: entry_point_index,\n                }\n                .into()\n            })\n    }\n\n    /// Validate all handles that occur in `expression`, whose handle is `handle`.\n    ///\n    /// If `expression` refers to any `Type`s, return the highest-indexed type\n    /// handle that it uses. This is used for detecting cycles between the\n    /// expression and type arenas.\n    fn validate_const_expression_handles(\n        (handle, expression): (Handle<crate::Expression>, &crate::Expression),\n        constants: &Arena<crate::Constant>,\n        overrides: &Arena<crate::Override>,\n    ) -> Result<Option<Handle<crate::Type>>, InvalidHandleError> {\n        let validate_constant = |handle| Self::validate_constant_handle(handle, constants);\n        let validate_override = |handle| Self::validate_override_handle(handle, overrides);\n\n        let max_type = match *expression {\n            crate::Expression::Literal(_) => None,\n            crate::Expression::Constant(constant) => {\n                validate_constant(constant)?;\n                handle.check_dep(constants[constant].init)?;\n                None\n            }\n            crate::Expression::Override(r#override) => {\n                validate_override(r#override)?;\n                if let Some(init) = overrides[r#override].init {\n                    handle.check_dep(init)?;\n                }\n                None\n            }\n            crate::Expression::ZeroValue(ty) => Some(ty),\n            crate::Expression::Compose { ty, ref components } => {\n                handle.check_dep_iter(components.iter().copied())?;\n                Some(ty)\n            }\n            _ => None,\n        };\n        Ok(max_type)\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn validate_expression_handles(\n        (handle, expression): (Handle<crate::Expression>, &crate::Expression),\n        constants: &Arena<crate::Constant>,\n        overrides: &Arena<crate::Override>,\n        types: &UniqueArena<crate::Type>,\n        local_variables: &Arena<crate::LocalVariable>,\n        global_variables: &Arena<crate::GlobalVariable>,\n        functions: &Arena<crate::Function>,\n        // The handle of the current function or `None` if it's an entry point\n        current_function: Option<Handle<crate::Function>>,\n    ) -> Result<(), InvalidHandleError> {\n        let validate_constant = |handle| Self::validate_constant_handle(handle, constants);\n        let validate_override = |handle| Self::validate_override_handle(handle, overrides);\n        let validate_type = |handle| Self::validate_type_handle(handle, types);\n\n        match *expression {\n            crate::Expression::Access { base, index } => {\n                handle.check_dep(base)?.check_dep(index)?;\n            }\n            crate::Expression::AccessIndex { base, .. } => {\n                handle.check_dep(base)?;\n            }\n            crate::Expression::Splat { value, .. } => {\n                handle.check_dep(value)?;\n            }\n            crate::Expression::Swizzle { vector, .. } => {\n                handle.check_dep(vector)?;\n            }\n            crate::Expression::Literal(_) => {}\n            crate::Expression::Constant(constant) => {\n                validate_constant(constant)?;\n            }\n            crate::Expression::Override(r#override) => {\n                validate_override(r#override)?;\n            }\n            crate::Expression::ZeroValue(ty) => {\n                validate_type(ty)?;\n            }\n            crate::Expression::Compose { ty, ref components } => {\n                validate_type(ty)?;\n                handle.check_dep_iter(components.iter().copied())?;\n            }\n            crate::Expression::FunctionArgument(_arg_idx) => (),\n            crate::Expression::GlobalVariable(global_variable) => {\n                global_variable.check_valid_for(global_variables)?;\n            }\n            crate::Expression::LocalVariable(local_variable) => {\n                local_variable.check_valid_for(local_variables)?;\n            }\n            crate::Expression::Load { pointer } => {\n                handle.check_dep(pointer)?;\n            }\n            crate::Expression::ImageSample {\n                image,\n                sampler,\n                gather: _,\n                coordinate,\n                array_index,\n                offset,\n                level,\n                depth_ref,\n                clamp_to_edge: _,\n            } => {\n                handle\n                    .check_dep(image)?\n                    .check_dep(sampler)?\n                    .check_dep(coordinate)?\n                    .check_dep_opt(array_index)?\n                    .check_dep_opt(offset)?;\n\n                match level {\n                    crate::SampleLevel::Auto | crate::SampleLevel::Zero => (),\n                    crate::SampleLevel::Exact(expr) => {\n                        handle.check_dep(expr)?;\n                    }\n                    crate::SampleLevel::Bias(expr) => {\n                        handle.check_dep(expr)?;\n                    }\n                    crate::SampleLevel::Gradient { x, y } => {\n                        handle.check_dep(x)?.check_dep(y)?;\n                    }\n                };\n\n                handle.check_dep_opt(depth_ref)?;\n            }\n            crate::Expression::ImageLoad {\n                image,\n                coordinate,\n                array_index,\n                sample,\n                level,\n            } => {\n                handle\n                    .check_dep(image)?\n                    .check_dep(coordinate)?\n                    .check_dep_opt(array_index)?\n                    .check_dep_opt(sample)?\n                    .check_dep_opt(level)?;\n            }\n            crate::Expression::ImageQuery { image, query } => {\n                handle.check_dep(image)?;\n                match query {\n                    crate::ImageQuery::Size { level } => {\n                        handle.check_dep_opt(level)?;\n                    }\n                    crate::ImageQuery::NumLevels\n                    | crate::ImageQuery::NumLayers\n                    | crate::ImageQuery::NumSamples => (),\n                };\n            }\n            crate::Expression::Unary {\n                op: _,\n                expr: operand,\n            } => {\n                handle.check_dep(operand)?;\n            }\n            crate::Expression::Binary { op: _, left, right } => {\n                handle.check_dep(left)?.check_dep(right)?;\n            }\n            crate::Expression::Select {\n                condition,\n                accept,\n                reject,\n            } => {\n                handle\n                    .check_dep(condition)?\n                    .check_dep(accept)?\n                    .check_dep(reject)?;\n            }\n            crate::Expression::Derivative { expr: argument, .. } => {\n                handle.check_dep(argument)?;\n            }\n            crate::Expression::Relational { fun: _, argument } => {\n                handle.check_dep(argument)?;\n            }\n            crate::Expression::Math {\n                fun: _,\n                arg,\n                arg1,\n                arg2,\n                arg3,\n            } => {\n                handle\n                    .check_dep(arg)?\n                    .check_dep_opt(arg1)?\n                    .check_dep_opt(arg2)?\n                    .check_dep_opt(arg3)?;\n            }\n            crate::Expression::As {\n                expr: input,\n                kind: _,\n                convert: _,\n            } => {\n                handle.check_dep(input)?;\n            }\n            crate::Expression::CallResult(function) => {\n                Self::validate_function_handle(function, functions)?;\n                if let Some(handle) = current_function {\n                    handle.check_dep(function)?;\n                }\n            }\n            crate::Expression::AtomicResult { .. }\n            | crate::Expression::RayQueryProceedResult\n            | crate::Expression::SubgroupBallotResult\n            | crate::Expression::SubgroupOperationResult { .. }\n            | crate::Expression::WorkGroupUniformLoadResult { .. } => (),\n            crate::Expression::ArrayLength(array) => {\n                handle.check_dep(array)?;\n            }\n            crate::Expression::RayQueryGetIntersection {\n                query,\n                committed: _,\n            }\n            | crate::Expression::RayQueryVertexPositions {\n                query,\n                committed: _,\n            } => {\n                handle.check_dep(query)?;\n            }\n            crate::Expression::CooperativeLoad { ref data, .. } => {\n                handle.check_dep(data.pointer)?.check_dep(data.stride)?;\n            }\n            crate::Expression::CooperativeMultiplyAdd { a, b, c } => {\n                handle.check_dep(a)?.check_dep(b)?.check_dep(c)?;\n            }\n        }\n        Ok(())\n    }\n\n    fn validate_block_handles(\n        block: &crate::Block,\n        expressions: &Arena<crate::Expression>,\n        functions: &Arena<crate::Function>,\n    ) -> Result<(), InvalidHandleError> {\n        let validate_block = |block| Self::validate_block_handles(block, expressions, functions);\n        let validate_expr = |handle| Self::validate_expression_handle(handle, expressions);\n        let validate_expr_opt = |handle_opt| {\n            if let Some(handle) = handle_opt {\n                validate_expr(handle)?;\n            }\n            Ok(())\n        };\n\n        block.iter().try_for_each(|stmt| match *stmt {\n            crate::Statement::Emit(ref expr_range) => {\n                expr_range.check_valid_for(expressions)?;\n                Ok(())\n            }\n            crate::Statement::Block(ref block) => {\n                validate_block(block)?;\n                Ok(())\n            }\n            crate::Statement::If {\n                condition,\n                ref accept,\n                ref reject,\n            } => {\n                validate_expr(condition)?;\n                validate_block(accept)?;\n                validate_block(reject)?;\n                Ok(())\n            }\n            crate::Statement::Switch {\n                selector,\n                ref cases,\n            } => {\n                validate_expr(selector)?;\n                for &crate::SwitchCase {\n                    value: _,\n                    ref body,\n                    fall_through: _,\n                } in cases\n                {\n                    validate_block(body)?;\n                }\n                Ok(())\n            }\n            crate::Statement::Loop {\n                ref body,\n                ref continuing,\n                break_if,\n            } => {\n                validate_block(body)?;\n                validate_block(continuing)?;\n                validate_expr_opt(break_if)?;\n                Ok(())\n            }\n            crate::Statement::Return { value } => validate_expr_opt(value),\n            crate::Statement::Store { pointer, value } => {\n                validate_expr(pointer)?;\n                validate_expr(value)?;\n                Ok(())\n            }\n            crate::Statement::ImageStore {\n                image,\n                coordinate,\n                array_index,\n                value,\n            } => {\n                validate_expr(image)?;\n                validate_expr(coordinate)?;\n                validate_expr_opt(array_index)?;\n                validate_expr(value)?;\n                Ok(())\n            }\n            crate::Statement::Atomic {\n                pointer,\n                fun,\n                value,\n                result,\n            } => {\n                validate_expr(pointer)?;\n                match fun {\n                    crate::AtomicFunction::Add\n                    | crate::AtomicFunction::Subtract\n                    | crate::AtomicFunction::And\n                    | crate::AtomicFunction::ExclusiveOr\n                    | crate::AtomicFunction::InclusiveOr\n                    | crate::AtomicFunction::Min\n                    | crate::AtomicFunction::Max => (),\n                    crate::AtomicFunction::Exchange { compare } => validate_expr_opt(compare)?,\n                };\n                validate_expr(value)?;\n                if let Some(result) = result {\n                    validate_expr(result)?;\n                }\n                Ok(())\n            }\n            crate::Statement::ImageAtomic {\n                image,\n                coordinate,\n                array_index,\n                fun: _,\n                value,\n            } => {\n                validate_expr(image)?;\n                validate_expr(coordinate)?;\n                validate_expr_opt(array_index)?;\n                validate_expr(value)?;\n                Ok(())\n            }\n            crate::Statement::WorkGroupUniformLoad { pointer, result } => {\n                validate_expr(pointer)?;\n                validate_expr(result)?;\n                Ok(())\n            }\n            crate::Statement::Call {\n                function,\n                ref arguments,\n                result,\n            } => {\n                Self::validate_function_handle(function, functions)?;\n                for arg in arguments.iter().copied() {\n                    validate_expr(arg)?;\n                }\n                validate_expr_opt(result)?;\n                Ok(())\n            }\n            crate::Statement::RayQuery { query, ref fun } => {\n                validate_expr(query)?;\n                match *fun {\n                    crate::RayQueryFunction::Initialize {\n                        acceleration_structure,\n                        descriptor,\n                    } => {\n                        validate_expr(acceleration_structure)?;\n                        validate_expr(descriptor)?;\n                    }\n                    crate::RayQueryFunction::Proceed { result } => {\n                        validate_expr(result)?;\n                    }\n                    crate::RayQueryFunction::GenerateIntersection { hit_t } => {\n                        validate_expr(hit_t)?;\n                    }\n                    crate::RayQueryFunction::ConfirmIntersection => {}\n                    crate::RayQueryFunction::Terminate => {}\n                }\n                Ok(())\n            }\n            crate::Statement::SubgroupBallot { result, predicate } => {\n                validate_expr_opt(predicate)?;\n                validate_expr(result)?;\n                Ok(())\n            }\n            crate::Statement::SubgroupCollectiveOperation {\n                op: _,\n                collective_op: _,\n                argument,\n                result,\n            } => {\n                validate_expr(argument)?;\n                validate_expr(result)?;\n                Ok(())\n            }\n            crate::Statement::SubgroupGather {\n                mode,\n                argument,\n                result,\n            } => {\n                validate_expr(argument)?;\n                match mode {\n                    crate::GatherMode::BroadcastFirst => {}\n                    crate::GatherMode::Broadcast(index)\n                    | crate::GatherMode::Shuffle(index)\n                    | crate::GatherMode::ShuffleDown(index)\n                    | crate::GatherMode::ShuffleUp(index)\n                    | crate::GatherMode::ShuffleXor(index)\n                    | crate::GatherMode::QuadBroadcast(index) => validate_expr(index)?,\n                    crate::GatherMode::QuadSwap(_) => {}\n                }\n                validate_expr(result)?;\n                Ok(())\n            }\n            crate::Statement::CooperativeStore { target, ref data } => {\n                validate_expr(target)?;\n                validate_expr(data.pointer)?;\n                validate_expr(data.stride)?;\n                Ok(())\n            }\n            crate::Statement::RayPipelineFunction(fun) => match fun {\n                crate::RayPipelineFunction::TraceRay {\n                    acceleration_structure,\n                    descriptor,\n                    payload,\n                } => {\n                    validate_expr(acceleration_structure)?;\n                    validate_expr(descriptor)?;\n                    validate_expr(payload)?;\n                    Ok(())\n                }\n            },\n            crate::Statement::Break\n            | crate::Statement::Continue\n            | crate::Statement::Kill\n            | crate::Statement::ControlBarrier(_)\n            | crate::Statement::MemoryBarrier(_) => Ok(()),\n        })\n    }\n}\n\nimpl From<BadHandle> for ValidationError {\n    fn from(source: BadHandle) -> Self {\n        Self::InvalidHandle(source.into())\n    }\n}\n\nimpl From<FwdDepError> for ValidationError {\n    fn from(source: FwdDepError) -> Self {\n        Self::InvalidHandle(source.into())\n    }\n}\n\nimpl From<BadRangeError> for ValidationError {\n    fn from(source: BadRangeError) -> Self {\n        Self::InvalidHandle(source.into())\n    }\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum InvalidHandleError {\n    #[error(transparent)]\n    BadHandle(#[from] BadHandle),\n    #[error(transparent)]\n    ForwardDependency(#[from] FwdDepError),\n    #[error(transparent)]\n    BadRange(#[from] BadRangeError),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\n#[error(\n    \"{subject:?} of kind {subject_kind:?} depends on {depends_on:?} of kind {depends_on_kind}, \\\n    which has not been processed yet\"\n)]\npub struct FwdDepError {\n    // This error is used for many `Handle` types, but there's no point in making this generic, so\n    // we just flatten them all to `Handle<()>` here.\n    subject: Handle<()>,\n    subject_kind: &'static str,\n    depends_on: Handle<()>,\n    depends_on_kind: &'static str,\n}\n\nimpl<T> Handle<T> {\n    /// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`].\n    pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {\n        arena.check_contains_handle(self)?;\n        Ok(())\n    }\n\n    /// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`].\n    pub(self) fn check_valid_for_uniq(\n        self,\n        arena: &UniqueArena<T>,\n    ) -> Result<(), InvalidHandleError>\n    where\n        T: Eq + Hash,\n    {\n        arena.check_contains_handle(self)?;\n        Ok(())\n    }\n\n    /// Check that `depends_on` was constructed before `self` by comparing handle indices.\n    ///\n    /// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`])\n    /// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid.\n    /// In [`naga`](crate)'s current arena-based implementation, this is useful for validating\n    /// recursive definitions of arena-based values in linear time.\n    ///\n    /// # Errors\n    ///\n    /// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier\n    /// than `self`'s, this function returns an error.\n    pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> {\n        if depends_on < self {\n            Ok(self)\n        } else {\n            let erase_handle_type = |handle: Handle<_>| {\n                Handle::new(NonMaxU32::new((handle.index()).try_into().unwrap()).unwrap())\n            };\n            Err(FwdDepError {\n                subject: erase_handle_type(self),\n                subject_kind: core::any::type_name::<T>(),\n                depends_on: erase_handle_type(depends_on),\n                depends_on_kind: core::any::type_name::<T>(),\n            })\n        }\n    }\n\n    /// Like [`Self::check_dep`], except for [`Option`]al handle values.\n    pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> {\n        self.check_dep_iter(depends_on.into_iter())\n    }\n\n    /// Like [`Self::check_dep`], except for [`Iterator`]s over handle values.\n    pub(self) fn check_dep_iter(\n        self,\n        depends_on: impl Iterator<Item = Self>,\n    ) -> Result<Self, FwdDepError> {\n        for handle in depends_on {\n            self.check_dep(handle)?;\n        }\n        Ok(self)\n    }\n}\n\nimpl<T> crate::arena::Range<T> {\n    pub(self) fn check_valid_for(&self, arena: &Arena<T>) -> Result<(), BadRangeError> {\n        arena.check_contains_range(self)\n    }\n}\n\n#[test]\nfn constant_deps() {\n    use crate::{Constant, Expression, Literal, Span, Type, TypeInner};\n\n    let nowhere = Span::default();\n\n    let mut types = UniqueArena::new();\n    let mut const_exprs = Arena::new();\n    let mut fun_exprs = Arena::new();\n    let mut constants = Arena::new();\n    let overrides = Arena::new();\n\n    let i32_handle = types.insert(\n        Type {\n            name: None,\n            inner: TypeInner::Scalar(crate::Scalar::I32),\n        },\n        nowhere,\n    );\n\n    // Construct a self-referential constant by misusing a handle to\n    // fun_exprs as a constant initializer.\n    let fun_expr = fun_exprs.append(Expression::Literal(Literal::I32(42)), nowhere);\n    let self_referential_const = constants.append(\n        Constant {\n            name: None,\n            ty: i32_handle,\n            init: fun_expr,\n        },\n        nowhere,\n    );\n    let _self_referential_expr =\n        const_exprs.append(Expression::Constant(self_referential_const), nowhere);\n\n    for handle_and_expr in const_exprs.iter() {\n        assert!(super::Validator::validate_const_expression_handles(\n            handle_and_expr,\n            &constants,\n            &overrides,\n        )\n        .is_err());\n    }\n}\n\n#[test]\nfn array_size_deps() {\n    use super::Validator;\n    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};\n\n    let nowhere = Span::default();\n\n    let mut m = crate::Module::default();\n\n    let ty_u32 = m.types.insert(\n        Type {\n            name: Some(\"u32\".to_string()),\n            inner: TypeInner::Scalar(Scalar::U32),\n        },\n        nowhere,\n    );\n    let ex_zero = m\n        .global_expressions\n        .append(Expression::ZeroValue(ty_u32), nowhere);\n    let ty_handle = m.overrides.append(\n        Override {\n            name: None,\n            id: None,\n            ty: ty_u32,\n            init: Some(ex_zero),\n        },\n        nowhere,\n    );\n    let ty_arr = m.types.insert(\n        Type {\n            name: Some(\"bad_array\".to_string()),\n            inner: TypeInner::Array {\n                base: ty_u32,\n                size: ArraySize::Pending(ty_handle),\n                stride: 4,\n            },\n        },\n        nowhere,\n    );\n\n    // Everything should be okay now.\n    assert!(Validator::validate_module_handles(&m).is_ok());\n\n    // Mutate `ex_zero`'s type to `ty_arr`, introducing a cycle.\n    // Validation should catch the cycle.\n    m.global_expressions[ex_zero] = Expression::ZeroValue(ty_arr);\n    assert!(Validator::validate_module_handles(&m).is_err());\n}\n\n#[test]\nfn array_size_override() {\n    use super::Validator;\n    use crate::{ArraySize, Override, Scalar, Span, Type, TypeInner};\n\n    let nowhere = Span::default();\n\n    let mut m = crate::Module::default();\n\n    let ty_u32 = m.types.insert(\n        Type {\n            name: Some(\"u32\".to_string()),\n            inner: TypeInner::Scalar(Scalar::U32),\n        },\n        nowhere,\n    );\n\n    let bad_override: Handle<Override> = Handle::new(NonMaxU32::new(1000).unwrap());\n    let _ty_arr = m.types.insert(\n        Type {\n            name: Some(\"bad_array\".to_string()),\n            inner: TypeInner::Array {\n                base: ty_u32,\n                size: ArraySize::Pending(bad_override),\n                stride: 4,\n            },\n        },\n        nowhere,\n    );\n\n    assert!(Validator::validate_module_handles(&m).is_err());\n}\n\n#[test]\nfn override_init_deps() {\n    use super::Validator;\n    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};\n\n    let nowhere = Span::default();\n\n    let mut m = crate::Module::default();\n\n    let ty_u32 = m.types.insert(\n        Type {\n            name: Some(\"u32\".to_string()),\n            inner: TypeInner::Scalar(Scalar::U32),\n        },\n        nowhere,\n    );\n    let ex_zero = m\n        .global_expressions\n        .append(Expression::ZeroValue(ty_u32), nowhere);\n    let r#override = m.overrides.append(\n        Override {\n            name: Some(\"bad_override\".into()),\n            id: None,\n            ty: ty_u32,\n            init: Some(ex_zero),\n        },\n        nowhere,\n    );\n    let ty_arr = m.types.insert(\n        Type {\n            name: Some(\"bad_array\".to_string()),\n            inner: TypeInner::Array {\n                base: ty_u32,\n                size: ArraySize::Pending(r#override),\n                stride: 4,\n            },\n        },\n        nowhere,\n    );\n    let ex_arr = m\n        .global_expressions\n        .append(Expression::ZeroValue(ty_arr), nowhere);\n\n    assert!(Validator::validate_module_handles(&m).is_ok());\n\n    // Mutate `r#override`'s initializer to `ex_arr`, introducing a cycle.\n    // Validation should catch the cycle.\n    m.overrides[r#override].init = Some(ex_arr);\n    assert!(Validator::validate_module_handles(&m).is_err());\n}\n"
  },
  {
    "path": "naga/src/valid/interface.rs",
    "content": "use alloc::vec::Vec;\n\nuse bit_set::BitSet;\n\nuse super::{\n    analyzer::{FunctionInfo, GlobalUse},\n    Capabilities, Disalignment, FunctionError, ImmediateError, ModuleInfo,\n};\nuse crate::arena::{Handle, UniqueArena};\nuse crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};\n\nconst MAX_WORKGROUP_SIZE: u32 = 0x4000;\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum GlobalVariableError {\n    #[error(\"Usage isn't compatible with address space {0:?}\")]\n    InvalidUsage(crate::AddressSpace),\n    #[error(\"Type isn't compatible with address space {0:?}\")]\n    InvalidType(crate::AddressSpace),\n    #[error(\"Type {0:?} isn't compatible with binding arrays\")]\n    InvalidBindingArray(Handle<crate::Type>),\n    #[error(\"Type flags {seen:?} do not meet the required {required:?}\")]\n    MissingTypeFlags {\n        required: super::TypeFlags,\n        seen: super::TypeFlags,\n    },\n    #[error(\"Capability {0:?} is not supported\")]\n    UnsupportedCapability(Capabilities),\n    #[error(\"Binding decoration is missing or not applicable\")]\n    InvalidBinding,\n    #[error(\"Alignment requirements for address space {0:?} are not met by {1:?}\")]\n    Alignment(\n        crate::AddressSpace,\n        Handle<crate::Type>,\n        #[source] Disalignment,\n    ),\n    #[error(\"Initializer must be an override-expression\")]\n    InitializerExprType,\n    #[error(\"Initializer doesn't match the variable type\")]\n    InitializerType,\n    #[error(\"Initializer can't be used with address space {0:?}\")]\n    InitializerNotAllowed(crate::AddressSpace),\n    #[error(\"Storage address space doesn't support write-only access\")]\n    StorageAddressSpaceWriteOnlyNotSupported,\n    #[error(\"Type is not valid for use as a immediate data\")]\n    InvalidImmediateType(#[source] ImmediateError),\n    #[error(\"Task payload must not be zero-sized\")]\n    ZeroSizedTaskPayload,\n    #[error(\"Memory decorations (`@coherent`, `@volatile`) are only valid for variables in the `storage` address space\")]\n    InvalidMemoryDecorationsAddressSpace,\n    #[error(\"`@coherent` requires the MEMORY_DECORATION_COHERENT capability\")]\n    CoherentNotSupported,\n    #[error(\"`@volatile` requires the MEMORY_DECORATION_VOLATILE capability\")]\n    VolatileNotSupported,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum VaryingError {\n    #[error(\"The type {0:?} does not match the varying\")]\n    InvalidType(Handle<crate::Type>),\n    #[error(\n        \"The type {0:?} cannot be used for user-defined entry point inputs or outputs. \\\n        Only numeric scalars and vectors are allowed.\"\n    )]\n    NotIOShareableType(Handle<crate::Type>),\n    #[error(\"Interpolation is not valid\")]\n    InvalidInterpolation,\n    #[error(\"Interpolation {0:?} is only valid for stage {1:?}\")]\n    InvalidInterpolationInStage(crate::Interpolation, crate::ShaderStage),\n    #[error(\"Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type\")]\n    InvalidInterpolationSamplingCombination {\n        interpolation: crate::Interpolation,\n        sampling: crate::Sampling,\n    },\n    #[error(\"Interpolation must be specified on vertex shader outputs and fragment shader inputs\")]\n    MissingInterpolation,\n    #[error(\"Built-in {0:?} is not available at this stage\")]\n    InvalidBuiltInStage(crate::BuiltIn),\n    #[error(\"Built-in type for {0:?} is invalid. Found {1:?}\")]\n    InvalidBuiltInType(crate::BuiltIn, crate::TypeInner),\n    #[error(\"Entry point arguments and return values must all have bindings\")]\n    MissingBinding,\n    #[error(\"Struct member {0} is missing a binding\")]\n    MemberMissingBinding(u32),\n    #[error(\"Multiple bindings at location {location} are present\")]\n    BindingCollision { location: u32 },\n    #[error(\"Multiple bindings use the same `blend_src` {blend_src}\")]\n    BindingCollisionBlendSrc { blend_src: u32 },\n    #[error(\"Built-in {0:?} is present more than once\")]\n    DuplicateBuiltIn(crate::BuiltIn),\n    #[error(\"Capability {0:?} is not supported\")]\n    UnsupportedCapability(Capabilities),\n    #[error(\"The attribute {0:?} is only valid as an output for stage {1:?}\")]\n    InvalidInputAttributeInStage(&'static str, crate::ShaderStage),\n    #[error(\"The attribute {0:?} is not valid for stage {1:?}\")]\n    InvalidAttributeInStage(&'static str, crate::ShaderStage),\n    #[error(\"`@blend_src` can only be used at location 0, indices 0 and 1. Found `@location({location}) @blend_src({blend_src})`.\")]\n    InvalidBlendSrcIndex { location: u32, blend_src: u32 },\n    #[error(\n        \"`@blend_src` structure must specify two sources. \\\n        Found `@blend_src({present_blend_src})` but not `@blend_src({absent_blend_src})`.\",\n        absent_blend_src = if *present_blend_src == 0 { 1 } else { 0 },\n    )]\n    IncompleteBlendSrcUsage { present_blend_src: u32 },\n    #[error(\"Structure using `@blend_src` may not specify `@location` on any other members. Found a binding at `@location({location})`.\")]\n    InvalidBlendSrcWithOtherBindings { location: u32 },\n    #[error(\"Both `@blend_src` structure members must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.\")]\n    BlendSrcOutputTypeMismatch {\n        blend_src_0_type: Handle<crate::Type>,\n        blend_src_1_type: Handle<crate::Type>,\n    },\n    #[error(\"`@blend_src` can only be used on struct members, not directly on entry point I/O\")]\n    BlendSrcNotOnStructMember,\n    #[error(\"Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.\")]\n    InvalidMultiDimensionalSubgroupBuiltIn,\n    #[error(\"The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs\")]\n    InvalidPerPrimitive,\n    #[error(\"Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`\")]\n    MissingPerPrimitive,\n    #[error(\"Per vertex fragment inputs must be an array of length 3.\")]\n    PerVertexNotArrayOfThree,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum EntryPointError {\n    #[error(\"Multiple conflicting entry points\")]\n    Conflict,\n    #[error(\"Vertex shaders must return a `@builtin(position)` output value\")]\n    MissingVertexOutputPosition,\n    #[error(\"Early depth test is not applicable\")]\n    UnexpectedEarlyDepthTest,\n    #[error(\"Workgroup size is not applicable\")]\n    UnexpectedWorkgroupSize,\n    #[error(\"Workgroup size is out of range\")]\n    OutOfRangeWorkgroupSize,\n    #[error(\"Uses operations forbidden at this stage\")]\n    ForbiddenStageOperations,\n    #[error(\"Global variable {0:?} is used incorrectly as {1:?}\")]\n    InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),\n    #[error(\"More than 1 immediate data variable is used\")]\n    MoreThanOneImmediateUsed,\n    #[error(\"Bindings for {0:?} conflict with other resource\")]\n    BindingCollision(Handle<crate::GlobalVariable>),\n    #[error(\"Argument {0} varying error\")]\n    Argument(u32, #[source] VaryingError),\n    #[error(transparent)]\n    Result(#[from] VaryingError),\n    #[error(\"Location {location} interpolation of an integer has to be flat\")]\n    InvalidIntegerInterpolation { location: u32 },\n    #[error(transparent)]\n    Function(#[from] FunctionError),\n    #[error(\"Capability {0:?} is not supported\")]\n    UnsupportedCapability(Capabilities),\n\n    #[error(\"mesh shader entry point missing mesh shader attributes\")]\n    ExpectedMeshShaderAttributes,\n    #[error(\"Non mesh shader entry point cannot have mesh shader attributes\")]\n    UnexpectedMeshShaderAttributes,\n    #[error(\"Non mesh/task shader entry point cannot have task payload attribute\")]\n    UnexpectedTaskPayload,\n    #[error(\"Task payload must be declared with `var<task_payload>`\")]\n    TaskPayloadWrongAddressSpace,\n    #[error(\"For a task payload to be used, it must be declared with @payload\")]\n    WrongTaskPayloadUsed,\n    #[error(\"Task shader entry point must return @builtin(mesh_task_size) vec3<u32>\")]\n    WrongTaskShaderEntryResult,\n    #[error(\"Task shaders must declare a task payload output\")]\n    ExpectedTaskPayload,\n    #[error(\n        \"Mesh shader output variable must be a struct with fields that are all allowed builtins\"\n    )]\n    BadMeshOutputVariableType,\n    #[error(\"Mesh shader output variable fields must have types that are in accordance with the mesh shader spec\")]\n    BadMeshOutputVariableField,\n    #[error(\"Mesh shader entry point cannot have a return type\")]\n    UnexpectedMeshShaderEntryResult,\n    #[error(\n        \"Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec\"\n    )]\n    InvalidMeshOutputType,\n    #[error(\"Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`\")]\n    InvalidMeshPrimitiveOutputType,\n    #[error(\"Mesh output global variable must live in the workgroup address space\")]\n    WrongMeshOutputAddressSpace,\n    #[error(\"Task payload must be at least 4 bytes, but is {0} bytes\")]\n    TaskPayloadTooSmall(u32),\n    #[error(\"Only the `ray_generation`, `closest_hit`, and `any_hit` shader stages can access a global variable in the `ray_payload` address space\")]\n    RayPayloadInInvalidStage(crate::ShaderStage),\n    #[error(\"Only the `closest_hit`, `any_hit`, and `miss` shader stages can access a global variable in the `incoming_ray_payload` address space\")]\n    IncomingRayPayloadInInvalidStage(crate::ShaderStage),\n}\n\nfn storage_usage(access: crate::StorageAccess) -> GlobalUse {\n    let mut storage_usage = GlobalUse::QUERY;\n    if access.contains(crate::StorageAccess::LOAD) {\n        storage_usage |= GlobalUse::READ;\n    }\n    if access.contains(crate::StorageAccess::STORE) {\n        storage_usage |= GlobalUse::WRITE;\n    }\n    if access.contains(crate::StorageAccess::ATOMIC) {\n        storage_usage |= GlobalUse::ATOMIC;\n    }\n    storage_usage\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nenum MeshOutputType {\n    None,\n    VertexOutput,\n    PrimitiveOutput,\n}\n\nstruct VaryingContext<'a> {\n    stage: crate::ShaderStage,\n    output: bool,\n    types: &'a UniqueArena<crate::Type>,\n    type_info: &'a Vec<super::r#type::TypeInfo>,\n    location_mask: &'a mut BitSet,\n    dual_source_blending: Option<&'a mut bool>,\n    built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,\n    capabilities: Capabilities,\n    flags: super::ValidationFlags,\n    mesh_output_type: MeshOutputType,\n    has_task_payload: bool,\n}\n\nimpl VaryingContext<'_> {\n    fn validate_impl(\n        &mut self,\n        ep: &crate::EntryPoint,\n        ty: Handle<crate::Type>,\n        binding: &crate::Binding,\n    ) -> Result<(), VaryingError> {\n        use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};\n\n        let ty_inner = &self.types[ty].inner;\n        match *binding {\n            crate::Binding::BuiltIn(built_in) => {\n                // Ignore the `invariant` field for the sake of duplicate checks,\n                // but use the original in error messages.\n                let canonical = match built_in {\n                    crate::BuiltIn::Position { .. } => {\n                        crate::BuiltIn::Position { invariant: false }\n                    }\n                    crate::BuiltIn::Barycentric { .. } => {\n                        crate::BuiltIn::Barycentric { perspective: false }\n                    }\n                    x => x,\n                };\n\n                if self.built_ins.contains(&canonical) {\n                    return Err(VaryingError::DuplicateBuiltIn(built_in));\n                }\n                self.built_ins.insert(canonical);\n\n                let required = match built_in {\n                    Bi::ClipDistances => Capabilities::CLIP_DISTANCES,\n                    Bi::CullDistance => Capabilities::CULL_DISTANCE,\n                    // Primitive index is allowed w/o any other extensions in any- and closest-hit shaders\n                    Bi::PrimitiveIndex if !matches!(ep.stage, St::AnyHit | St::ClosestHit) => {\n                        Capabilities::PRIMITIVE_INDEX\n                    }\n                    Bi::Barycentric { .. } => Capabilities::SHADER_BARYCENTRICS,\n                    Bi::ViewIndex => Capabilities::MULTIVIEW,\n                    Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,\n                    Bi::NumSubgroups\n                    | Bi::SubgroupId\n                    | Bi::SubgroupSize\n                    | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,\n                    Bi::DrawIndex => Capabilities::DRAW_INDEX,\n                    _ => Capabilities::empty(),\n                };\n                if !self.capabilities.contains(required) {\n                    return Err(VaryingError::UnsupportedCapability(required));\n                }\n\n                if matches!(\n                    built_in,\n                    crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId\n                ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)\n                {\n                    return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);\n                }\n\n                let (visible, type_good) = match built_in {\n                    Bi::BaseInstance | Bi::BaseVertex | Bi::VertexIndex => (\n                        self.stage == St::Vertex && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::InstanceIndex => (\n                        matches!(self.stage, St::Vertex | St::AnyHit | St::ClosestHit)\n                            && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::DrawIndex => (\n                        // Always allowed in task/vertex stage. Allowed in mesh stage if there is no task stage in the pipeline.\n                        (self.stage == St::Vertex\n                            || self.stage == St::Task\n                            || (self.stage == St::Mesh && !self.has_task_payload))\n                            && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::ClipDistances | Bi::CullDistance => (\n                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,\n                        match *ty_inner {\n                            Ti::Array { base, size, .. } => {\n                                self.types[base].inner == Ti::Scalar(crate::Scalar::F32)\n                                    && match size {\n                                        crate::ArraySize::Constant(non_zero) => non_zero.get() <= 8,\n                                        _ => false,\n                                    }\n                            }\n                            _ => false,\n                        },\n                    ),\n                    Bi::PointSize => (\n                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::F32),\n                    ),\n                    Bi::PointCoord => (\n                        self.stage == St::Fragment && !self.output,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Bi,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::Position { .. } => (\n                        match self.stage {\n                            St::Vertex | St::Mesh => self.output,\n                            St::Fragment => !self.output,\n                            St::Compute | St::Task => false,\n                            St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => false,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Quad,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::ViewIndex => (\n                        match self.stage {\n                            St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,\n                            St::Compute\n                            | St::RayGeneration\n                            | St::AnyHit\n                            | St::ClosestHit\n                            | St::Miss => false,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::FragDepth => (\n                        self.stage == St::Fragment && self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::F32),\n                    ),\n                    Bi::FrontFacing => (\n                        self.stage == St::Fragment && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),\n                    ),\n                    Bi::PrimitiveIndex => (\n                        (matches!(self.stage, St::Fragment | St::AnyHit | St::ClosestHit)\n                            && !self.output)\n                            || (self.stage == St::Mesh\n                                && self.output\n                                && self.mesh_output_type == MeshOutputType::PrimitiveOutput),\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::Barycentric { .. } => (\n                        self.stage == St::Fragment && !self.output,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::SampleIndex => (\n                        self.stage == St::Fragment && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::SampleMask => (\n                        self.stage == St::Fragment,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::LocalInvocationIndex => (\n                        self.stage.compute_like() && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::GlobalInvocationId\n                    | Bi::LocalInvocationId\n                    | Bi::WorkGroupId\n                    | Bi::WorkGroupSize\n                    | Bi::NumWorkGroups => (\n                        self.stage.compute_like() && !self.output,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::NumSubgroups | Bi::SubgroupId => (\n                        self.stage.compute_like() && !self.output,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::SubgroupSize | Bi::SubgroupInvocationId => (\n                        match self.stage {\n                            St::Compute\n                            | St::Fragment\n                            | St::Task\n                            | St::Mesh\n                            | St::RayGeneration\n                            | St::AnyHit\n                            | St::ClosestHit\n                            | St::Miss => !self.output,\n                            St::Vertex => false,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::CullPrimitive => (\n                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,\n                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),\n                    ),\n                    Bi::PointIndex => (\n                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::LineIndices => (\n                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Bi,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::TriangleIndices => (\n                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::MeshTaskSize => (\n                        self.stage == St::Task && self.output,\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::RayInvocationId => (\n                        match self.stage {\n                            St::Vertex | St::Fragment | St::Compute | St::Mesh | St::Task => false,\n                            St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::NumRayInvocations => (\n                        match self.stage {\n                            St::Vertex | St::Fragment | St::Compute | St::Mesh | St::Task => false,\n                            St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::U32,\n                            },\n                    ),\n                    Bi::InstanceCustomData => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::GeometryIndex => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    Bi::WorldRayOrigin => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::WorldRayDirection => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::ObjectRayOrigin => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::ObjectRayDirection => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner\n                            == Ti::Vector {\n                                size: Vs::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::RayTmin => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::F32),\n                    ),\n                    Bi::RayTCurrentMax => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit | St::Miss => true,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::F32),\n                    ),\n                    Bi::ObjectToWorld => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner\n                            == Ti::Matrix {\n                                columns: crate::VectorSize::Quad,\n                                rows: crate::VectorSize::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::WorldToObject => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner\n                            == Ti::Matrix {\n                                columns: crate::VectorSize::Quad,\n                                rows: crate::VectorSize::Tri,\n                                scalar: crate::Scalar::F32,\n                            },\n                    ),\n                    Bi::HitKind => (\n                        match self.stage {\n                            St::RayGeneration\n                            | St::Miss\n                            | St::Vertex\n                            | St::Fragment\n                            | St::Compute\n                            | St::Mesh\n                            | St::Task => false,\n                            St::AnyHit | St::ClosestHit => true,\n                        },\n                        *ty_inner == Ti::Scalar(crate::Scalar::U32),\n                    ),\n                    // Validated elsewhere, shouldn't be here\n                    Bi::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {\n                        (false, true)\n                    }\n                };\n                match built_in {\n                    Bi::CullPrimitive\n                    | Bi::PointIndex\n                    | Bi::LineIndices\n                    | Bi::TriangleIndices\n                    | Bi::MeshTaskSize\n                    | Bi::VertexCount\n                    | Bi::PrimitiveCount\n                    | Bi::Vertices\n                    | Bi::Primitives => {\n                        if !self.capabilities.contains(Capabilities::MESH_SHADER) {\n                            return Err(VaryingError::UnsupportedCapability(\n                                Capabilities::MESH_SHADER,\n                            ));\n                        }\n                    }\n                    _ => (),\n                }\n\n                if !visible {\n                    return Err(VaryingError::InvalidBuiltInStage(built_in));\n                }\n                if !type_good {\n                    return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));\n                }\n            }\n            crate::Binding::Location {\n                location,\n                interpolation,\n                sampling,\n                blend_src,\n                per_primitive,\n            } => {\n                if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {\n                    return Err(VaryingError::UnsupportedCapability(\n                        Capabilities::MESH_SHADER,\n                    ));\n                }\n                if interpolation == Some(crate::Interpolation::PerVertex) {\n                    if self.stage != crate::ShaderStage::Fragment {\n                        return Err(VaryingError::InvalidInterpolationInStage(\n                            crate::Interpolation::PerVertex,\n                            crate::ShaderStage::Fragment,\n                        ));\n                    }\n                    if !self.capabilities.contains(Capabilities::PER_VERTEX) {\n                        return Err(VaryingError::UnsupportedCapability(\n                            Capabilities::PER_VERTEX,\n                        ));\n                    }\n                }\n                // If this is per-vertex, we change the type we validate to the inner type, otherwise we leave it be.\n                // This lets all validation be done on the inner type once we've ensured the per-vertex is array<T, 3>\n                let (ty, ty_inner) = if interpolation == Some(crate::Interpolation::PerVertex) {\n                    let three = crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap());\n                    match ty_inner {\n                        &Ti::Array { base, size, .. } if size == three => {\n                            (base, &self.types[base].inner)\n                        }\n                        _ => return Err(VaryingError::PerVertexNotArrayOfThree),\n                    }\n                } else {\n                    (ty, ty_inner)\n                };\n\n                // Only IO-shareable types may be stored in locations.\n                if !self.type_info[ty.index()]\n                    .flags\n                    .contains(super::TypeFlags::IO_SHAREABLE)\n                {\n                    return Err(VaryingError::NotIOShareableType(ty));\n                }\n\n                // Check whether `per_primitive` is appropriate for this stage and direction.\n                if self.mesh_output_type == MeshOutputType::PrimitiveOutput {\n                    // All mesh shader `Location` outputs must be `per_primitive`.\n                    if !per_primitive {\n                        return Err(VaryingError::MissingPerPrimitive);\n                    }\n                } else if self.stage == crate::ShaderStage::Fragment && !self.output {\n                    // Fragment stage inputs may be `per_primitive`. We'll only\n                    // know if these are correct when the whole mesh pipeline is\n                    // created and we're paired with a specific mesh or vertex\n                    // shader.\n                } else if per_primitive {\n                    // All other `Location` bindings must not be `per_primitive`.\n                    return Err(VaryingError::InvalidPerPrimitive);\n                }\n\n                if blend_src.is_some() {\n                    return Err(VaryingError::BlendSrcNotOnStructMember);\n                } else if !self.location_mask.insert(location as usize)\n                    && self.flags.contains(super::ValidationFlags::BINDINGS)\n                {\n                    return Err(VaryingError::BindingCollision { location });\n                }\n\n                if let Some(interpolation) = interpolation {\n                    let invalid_sampling = match (interpolation, sampling) {\n                        (_, None)\n                        | (\n                            crate::Interpolation::Perspective | crate::Interpolation::Linear,\n                            Some(\n                                crate::Sampling::Center\n                                | crate::Sampling::Centroid\n                                | crate::Sampling::Sample,\n                            ),\n                        )\n                        | (\n                            crate::Interpolation::Flat,\n                            Some(crate::Sampling::First | crate::Sampling::Either),\n                        ) => None,\n                        (_, Some(invalid_sampling)) => Some(invalid_sampling),\n                    };\n                    if let Some(sampling) = invalid_sampling {\n                        return Err(VaryingError::InvalidInterpolationSamplingCombination {\n                            interpolation,\n                            sampling,\n                        });\n                    }\n                }\n\n                let needs_interpolation = match self.stage {\n                    crate::ShaderStage::Vertex => self.output,\n                    crate::ShaderStage::Fragment => !self.output && !per_primitive,\n                    crate::ShaderStage::Compute\n                    | crate::ShaderStage::Task\n                    | crate::ShaderStage::RayGeneration\n                    | crate::ShaderStage::AnyHit\n                    | crate::ShaderStage::ClosestHit\n                    | crate::ShaderStage::Miss => false,\n                    crate::ShaderStage::Mesh => self.output,\n                };\n\n                // It doesn't make sense to specify a sampling when `interpolation` is `Flat`, but\n                // SPIR-V and GLSL both explicitly tolerate such combinations of decorators /\n                // qualifiers, so we won't complain about that here.\n                let _ = sampling;\n\n                let required = match sampling {\n                    Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,\n                    _ => Capabilities::empty(),\n                };\n                if !self.capabilities.contains(required) {\n                    return Err(VaryingError::UnsupportedCapability(required));\n                }\n\n                if interpolation != Some(crate::Interpolation::PerVertex) {\n                    match ty_inner.scalar_kind() {\n                        Some(crate::ScalarKind::Float) => {\n                            if needs_interpolation && interpolation.is_none() {\n                                return Err(VaryingError::MissingInterpolation);\n                            }\n                        }\n                        Some(_) => {\n                            if needs_interpolation\n                                && interpolation != Some(crate::Interpolation::Flat)\n                            {\n                                return Err(VaryingError::InvalidInterpolation);\n                            }\n                        }\n                        None => return Err(VaryingError::InvalidType(ty)),\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn validate(\n        &mut self,\n        ep: &crate::EntryPoint,\n        ty: Handle<crate::Type>,\n        binding: Option<&crate::Binding>,\n    ) -> Result<(), WithSpan<VaryingError>> {\n        let span_context = self.types.get_span_context(ty);\n        match binding {\n            Some(binding) => self\n                .validate_impl(ep, ty, binding)\n                .map_err(|e| e.with_span_context(span_context)),\n            None => {\n                let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {\n                    if self.flags.contains(super::ValidationFlags::BINDINGS) {\n                        return Err(VaryingError::MissingBinding.with_span());\n                    } else {\n                        return Ok(());\n                    }\n                };\n\n                if self.type_info[ty.index()]\n                    .flags\n                    .contains(super::TypeFlags::IO_SHAREABLE)\n                {\n                    // `@blend_src` is the only case where `IO_SHAREABLE` is set on a struct (as\n                    // opposed to members of a struct). The struct definition is validated during\n                    // type validation.\n                    if self.stage != crate::ShaderStage::Fragment {\n                        return Err(\n                            VaryingError::InvalidAttributeInStage(\"blend_src\", self.stage)\n                                .with_span(),\n                        );\n                    }\n                    if !self.output {\n                        return Err(VaryingError::InvalidInputAttributeInStage(\n                            \"blend_src\",\n                            self.stage,\n                        )\n                        .with_span());\n                    }\n                    // Dual blend sources must always be at location 0.\n                    if !self.location_mask.insert(0)\n                        && self.flags.contains(super::ValidationFlags::BINDINGS)\n                    {\n                        return Err(VaryingError::BindingCollision { location: 0 }.with_span());\n                    }\n\n                    **self\n                        .dual_source_blending\n                        .as_mut()\n                        .expect(\"unexpected dual source blending\") = true;\n                } else {\n                    for (index, member) in members.iter().enumerate() {\n                        let span_context = self.types.get_span_context(ty);\n                        match member.binding {\n                            None => {\n                                if self.flags.contains(super::ValidationFlags::BINDINGS) {\n                                    return Err(VaryingError::MemberMissingBinding(index as u32)\n                                        .with_span_context(span_context));\n                                }\n                            }\n                            Some(ref binding) => self\n                                .validate_impl(ep, member.ty, binding)\n                                .map_err(|e| e.with_span_context(span_context))?,\n                        }\n                    }\n                }\n                Ok(())\n            }\n        }\n    }\n}\n\nimpl super::Validator {\n    pub(super) fn validate_global_var(\n        &self,\n        var: &crate::GlobalVariable,\n        gctx: crate::proc::GlobalCtx,\n        mod_info: &ModuleInfo,\n        global_expr_kind: &crate::proc::ExpressionKindTracker,\n    ) -> Result<(), GlobalVariableError> {\n        use super::TypeFlags;\n\n        log::debug!(\"var {var:?}\");\n        let inner_ty = match gctx.types[var.ty].inner {\n            // A binding array is (mostly) supposed to behave the same as a\n            // series of individually bound resources, so we can (mostly)\n            // validate a `binding_array<T>` as if it were just a plain `T`.\n            crate::TypeInner::BindingArray { base, .. } => match var.space {\n                crate::AddressSpace::Storage { .. } => {\n                    if !self\n                        .capabilities\n                        .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)\n                    {\n                        return Err(GlobalVariableError::UnsupportedCapability(\n                            Capabilities::STORAGE_BUFFER_BINDING_ARRAY,\n                        ));\n                    }\n                    base\n                }\n                crate::AddressSpace::Uniform => {\n                    if !self\n                        .capabilities\n                        .contains(Capabilities::BUFFER_BINDING_ARRAY)\n                    {\n                        return Err(GlobalVariableError::UnsupportedCapability(\n                            Capabilities::BUFFER_BINDING_ARRAY,\n                        ));\n                    }\n                    base\n                }\n                crate::AddressSpace::Handle => {\n                    match gctx.types[base].inner {\n                        crate::TypeInner::Image { class, .. } => match class {\n                            crate::ImageClass::Storage { .. } => {\n                                if !self\n                                    .capabilities\n                                    .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)\n                                {\n                                    return Err(GlobalVariableError::UnsupportedCapability(\n                                        Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,\n                                    ));\n                                }\n                            }\n                            crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {\n                                if !self\n                                    .capabilities\n                                    .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)\n                                {\n                                    return Err(GlobalVariableError::UnsupportedCapability(\n                                        Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,\n                                    ));\n                                }\n                            }\n                            crate::ImageClass::External => {\n                                // This should have been rejected in `validate_type`.\n                                unreachable!(\"binding arrays of external images are not supported\");\n                            }\n                        },\n                        crate::TypeInner::Sampler { .. } => {\n                            if !self\n                                .capabilities\n                                .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)\n                            {\n                                return Err(GlobalVariableError::UnsupportedCapability(\n                                    Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,\n                                ));\n                            }\n                        }\n                        crate::TypeInner::AccelerationStructure { .. } => {\n                            if !self\n                                .capabilities\n                                .contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)\n                            {\n                                return Err(GlobalVariableError::UnsupportedCapability(\n                                    Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,\n                                ));\n                            }\n                        }\n                        crate::TypeInner::RayQuery { .. } => {\n                            // This should have been rejected in `validate_type`.\n                            unreachable!(\"binding arrays of ray queries are not supported\");\n                        }\n                        _ => {\n                            // Fall through to the regular validation, which will reject `base`\n                            // as invalid in `AddressSpace::Handle`.\n                        }\n                    }\n                    base\n                }\n                _ => return Err(GlobalVariableError::InvalidUsage(var.space)),\n            },\n            _ => var.ty,\n        };\n        let type_info = &self.types[inner_ty.index()];\n\n        let (required_type_flags, is_resource) = match var.space {\n            crate::AddressSpace::Function => {\n                return Err(GlobalVariableError::InvalidUsage(var.space))\n            }\n            crate::AddressSpace::Storage { access } => {\n                if let Err((ty_handle, disalignment)) = type_info.storage_layout {\n                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {\n                        return Err(GlobalVariableError::Alignment(\n                            var.space,\n                            ty_handle,\n                            disalignment,\n                        ));\n                    }\n                }\n                if access == crate::StorageAccess::STORE {\n                    return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);\n                }\n                (\n                    TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,\n                    true,\n                )\n            }\n            crate::AddressSpace::Uniform => {\n                if let Err((ty_handle, disalignment)) = type_info.uniform_layout {\n                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {\n                        return Err(GlobalVariableError::Alignment(\n                            var.space,\n                            ty_handle,\n                            disalignment,\n                        ));\n                    }\n                }\n                (\n                    TypeFlags::DATA\n                        | TypeFlags::COPY\n                        | TypeFlags::SIZED\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::CREATION_RESOLVED,\n                    true,\n                )\n            }\n            crate::AddressSpace::Handle => {\n                match gctx.types[inner_ty].inner {\n                    crate::TypeInner::Image { class, .. } => match class {\n                        crate::ImageClass::Storage {\n                            format:\n                                crate::StorageFormat::R16Unorm\n                                | crate::StorageFormat::R16Snorm\n                                | crate::StorageFormat::Rg16Unorm\n                                | crate::StorageFormat::Rg16Snorm\n                                | crate::StorageFormat::Rgba16Unorm\n                                | crate::StorageFormat::Rgba16Snorm,\n                            ..\n                        } => {\n                            if !self\n                                .capabilities\n                                .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)\n                            {\n                                return Err(GlobalVariableError::UnsupportedCapability(\n                                    Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,\n                                ));\n                            }\n                        }\n                        _ => {}\n                    },\n                    crate::TypeInner::Sampler { .. }\n                    | crate::TypeInner::AccelerationStructure { .. }\n                    | crate::TypeInner::RayQuery { .. } => {}\n                    _ => {\n                        return Err(GlobalVariableError::InvalidType(var.space));\n                    }\n                }\n\n                (TypeFlags::empty(), true)\n            }\n            crate::AddressSpace::Private => (\n                TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,\n                false,\n            ),\n            crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),\n            crate::AddressSpace::TaskPayload => {\n                if !self.capabilities.contains(Capabilities::MESH_SHADER) {\n                    return Err(GlobalVariableError::UnsupportedCapability(\n                        Capabilities::MESH_SHADER,\n                    ));\n                }\n                (TypeFlags::DATA | TypeFlags::SIZED, false)\n            }\n            crate::AddressSpace::Immediate => {\n                if !self.capabilities.contains(Capabilities::IMMEDIATES) {\n                    return Err(GlobalVariableError::UnsupportedCapability(\n                        Capabilities::IMMEDIATES,\n                    ));\n                }\n                if let Err(ref err) = type_info.immediates_compatibility {\n                    return Err(GlobalVariableError::InvalidImmediateType(err.clone()));\n                }\n                (\n                    TypeFlags::DATA\n                        | TypeFlags::COPY\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::SIZED,\n                    false,\n                )\n            }\n            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {\n                if !self\n                    .capabilities\n                    .contains(Capabilities::RAY_TRACING_PIPELINE)\n                {\n                    return Err(GlobalVariableError::UnsupportedCapability(\n                        Capabilities::RAY_TRACING_PIPELINE,\n                    ));\n                }\n                (TypeFlags::DATA | TypeFlags::SIZED, false)\n            }\n        };\n\n        if !type_info.flags.contains(required_type_flags) {\n            return Err(GlobalVariableError::MissingTypeFlags {\n                seen: type_info.flags,\n                required: required_type_flags,\n            });\n        }\n\n        if is_resource != var.binding.is_some() {\n            if self.flags.contains(super::ValidationFlags::BINDINGS) {\n                return Err(GlobalVariableError::InvalidBinding);\n            }\n        }\n\n        if var.space == crate::AddressSpace::TaskPayload {\n            let ty = &gctx.types[var.ty].inner;\n            // HLSL doesn't allow zero sized payloads.\n            if ty.try_size(gctx) == Some(0) {\n                return Err(GlobalVariableError::ZeroSizedTaskPayload);\n            }\n        }\n\n        if !var.memory_decorations.is_empty()\n            && !matches!(var.space, crate::AddressSpace::Storage { .. })\n        {\n            return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);\n        }\n        if var\n            .memory_decorations\n            .contains(crate::MemoryDecorations::COHERENT)\n            && !self\n                .capabilities\n                .contains(Capabilities::MEMORY_DECORATION_COHERENT)\n        {\n            return Err(GlobalVariableError::CoherentNotSupported);\n        }\n        if var\n            .memory_decorations\n            .contains(crate::MemoryDecorations::VOLATILE)\n            && !self\n                .capabilities\n                .contains(Capabilities::MEMORY_DECORATION_VOLATILE)\n        {\n            return Err(GlobalVariableError::VolatileNotSupported);\n        }\n\n        if let Some(init) = var.init {\n            match var.space {\n                crate::AddressSpace::Private | crate::AddressSpace::Function => {}\n                _ => {\n                    return Err(GlobalVariableError::InitializerNotAllowed(var.space));\n                }\n            }\n\n            if !global_expr_kind.is_const_or_override(init) {\n                return Err(GlobalVariableError::InitializerExprType);\n            }\n\n            if !gctx.compare_types(\n                &crate::proc::TypeResolution::Handle(var.ty),\n                &mod_info[init],\n            ) {\n                return Err(GlobalVariableError::InitializerType);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Validate the mesh shader output type `ty`, used as `mesh_output_type`.\n    fn validate_mesh_output_type(\n        &mut self,\n        ep: &crate::EntryPoint,\n        module: &crate::Module,\n        ty: Handle<crate::Type>,\n        mesh_output_type: MeshOutputType,\n    ) -> Result<(), WithSpan<EntryPointError>> {\n        if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {\n            return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));\n        }\n        let mut result_built_ins = crate::FastHashSet::default();\n        let mut ctx = VaryingContext {\n            stage: ep.stage,\n            output: true,\n            types: &module.types,\n            type_info: &self.types,\n            location_mask: &mut self.location_mask,\n            dual_source_blending: None,\n            built_ins: &mut result_built_ins,\n            capabilities: self.capabilities,\n            flags: self.flags,\n            mesh_output_type,\n            has_task_payload: ep.task_payload.is_some(),\n        };\n        ctx.validate(ep, ty, None)\n            .map_err_inner(|e| EntryPointError::Result(e).with_span())?;\n        if mesh_output_type == MeshOutputType::PrimitiveOutput {\n            let mut num_indices_builtins = 0;\n            if result_built_ins.contains(&crate::BuiltIn::PointIndex) {\n                num_indices_builtins += 1;\n            }\n            if result_built_ins.contains(&crate::BuiltIn::LineIndices) {\n                num_indices_builtins += 1;\n            }\n            if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {\n                num_indices_builtins += 1;\n            }\n            if num_indices_builtins != 1 {\n                return Err(EntryPointError::InvalidMeshPrimitiveOutputType\n                    .with_span_handle(ty, &module.types));\n            }\n        } else if mesh_output_type == MeshOutputType::VertexOutput\n            && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })\n        {\n            return Err(\n                EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)\n            );\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn validate_entry_point(\n        &mut self,\n        ep: &crate::EntryPoint,\n        module: &crate::Module,\n        mod_info: &ModuleInfo,\n    ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {\n        match ep.stage {\n            crate::ShaderStage::Task | crate::ShaderStage::Mesh\n                if !self.capabilities.contains(Capabilities::MESH_SHADER) =>\n            {\n                return Err(\n                    EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),\n                );\n            }\n            crate::ShaderStage::RayGeneration\n            | crate::ShaderStage::AnyHit\n            | crate::ShaderStage::ClosestHit\n            | crate::ShaderStage::Miss\n                if !self\n                    .capabilities\n                    .contains(Capabilities::RAY_TRACING_PIPELINE) =>\n            {\n                return Err(EntryPointError::UnsupportedCapability(\n                    Capabilities::RAY_TRACING_PIPELINE,\n                )\n                .with_span());\n            }\n            _ => {}\n        }\n        if ep.early_depth_test.is_some() {\n            let required = Capabilities::EARLY_DEPTH_TEST;\n            if !self.capabilities.contains(required) {\n                return Err(\n                    EntryPointError::Result(VaryingError::UnsupportedCapability(required))\n                        .with_span(),\n                );\n            }\n\n            if ep.stage != crate::ShaderStage::Fragment {\n                return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());\n            }\n        }\n\n        if ep.stage.compute_like() {\n            if ep\n                .workgroup_size\n                .iter()\n                .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)\n            {\n                return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());\n            }\n        } else if ep.workgroup_size != [0; 3] {\n            return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());\n        }\n\n        match (ep.stage, &ep.mesh_info) {\n            (crate::ShaderStage::Mesh, &None) => {\n                return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());\n            }\n            (crate::ShaderStage::Mesh, &Some(..)) => {}\n            (_, &Some(_)) => {\n                return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());\n            }\n            (_, _) => {}\n        }\n\n        let mut info = self\n            .validate_function(&ep.function, module, mod_info, true)\n            .map_err(WithSpan::into_other)?;\n\n        // Validate the task shader payload.\n        match ep.stage {\n            // Task shaders must produce a payload.\n            crate::ShaderStage::Task => {\n                let Some(handle) = ep.task_payload else {\n                    return Err(EntryPointError::ExpectedTaskPayload.with_span());\n                };\n                if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {\n                    return Err(EntryPointError::TaskPayloadWrongAddressSpace\n                        .with_span_handle(handle, &module.global_variables));\n                }\n                info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);\n            }\n\n            // Mesh shaders may accept a payload.\n            crate::ShaderStage::Mesh => {\n                if let Some(handle) = ep.task_payload {\n                    if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {\n                        return Err(EntryPointError::TaskPayloadWrongAddressSpace\n                            .with_span_handle(handle, &module.global_variables));\n                    }\n                    info.insert_global_use(GlobalUse::READ, handle);\n                }\n                if let Some(ref mesh_info) = ep.mesh_info {\n                    info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);\n                }\n            }\n\n            // Other stages must not have a payload.\n            _ => {\n                if let Some(handle) = ep.task_payload {\n                    return Err(EntryPointError::UnexpectedTaskPayload\n                        .with_span_handle(handle, &module.global_variables));\n                }\n            }\n        }\n\n        {\n            use super::ShaderStages;\n\n            let stage_bit = match ep.stage {\n                crate::ShaderStage::Vertex => ShaderStages::VERTEX,\n                crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,\n                crate::ShaderStage::Compute => ShaderStages::COMPUTE,\n                crate::ShaderStage::Mesh => ShaderStages::MESH,\n                crate::ShaderStage::Task => ShaderStages::TASK,\n                crate::ShaderStage::RayGeneration => ShaderStages::RAY_GENERATION,\n                crate::ShaderStage::AnyHit => ShaderStages::ANY_HIT,\n                crate::ShaderStage::ClosestHit => ShaderStages::CLOSEST_HIT,\n                crate::ShaderStage::Miss => ShaderStages::MISS,\n            };\n\n            if !info.available_stages.contains(stage_bit) {\n                return Err(EntryPointError::ForbiddenStageOperations.with_span());\n            }\n        }\n\n        self.location_mask.make_empty();\n        let mut argument_built_ins = crate::FastHashSet::default();\n        // TODO: add span info to function arguments\n        for (index, fa) in ep.function.arguments.iter().enumerate() {\n            let mut ctx = VaryingContext {\n                stage: ep.stage,\n                output: false,\n                types: &module.types,\n                type_info: &self.types,\n                location_mask: &mut self.location_mask,\n                dual_source_blending: Some(&mut info.dual_source_blending),\n                built_ins: &mut argument_built_ins,\n                capabilities: self.capabilities,\n                flags: self.flags,\n                mesh_output_type: MeshOutputType::None,\n                has_task_payload: ep.task_payload.is_some(),\n            };\n            ctx.validate(ep, fa.ty, fa.binding.as_ref())\n                .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;\n        }\n\n        self.location_mask.make_empty();\n        if let Some(ref fr) = ep.function.result {\n            let mut result_built_ins = crate::FastHashSet::default();\n            let mut ctx = VaryingContext {\n                stage: ep.stage,\n                output: true,\n                types: &module.types,\n                type_info: &self.types,\n                location_mask: &mut self.location_mask,\n                dual_source_blending: Some(&mut info.dual_source_blending),\n                built_ins: &mut result_built_ins,\n                capabilities: self.capabilities,\n                flags: self.flags,\n                mesh_output_type: MeshOutputType::None,\n                has_task_payload: ep.task_payload.is_some(),\n            };\n            ctx.validate(ep, fr.ty, fr.binding.as_ref())\n                .map_err_inner(|e| EntryPointError::Result(e).with_span())?;\n            if ep.stage == crate::ShaderStage::Vertex\n                && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })\n            {\n                return Err(EntryPointError::MissingVertexOutputPosition.with_span());\n            }\n            if ep.stage == crate::ShaderStage::Mesh {\n                return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());\n            }\n            // Task shaders must have a single `MeshTaskSize` output, and nothing else.\n            if ep.stage == crate::ShaderStage::Task {\n                let ok = module.types[fr.ty].inner\n                    == crate::TypeInner::Vector {\n                        size: crate::VectorSize::Tri,\n                        scalar: crate::Scalar::U32,\n                    };\n                if !ok {\n                    return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());\n                }\n            }\n        } else if ep.stage == crate::ShaderStage::Vertex {\n            return Err(EntryPointError::MissingVertexOutputPosition.with_span());\n        } else if ep.stage == crate::ShaderStage::Task {\n            return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());\n        }\n\n        {\n            let mut used_immediates = module\n                .global_variables\n                .iter()\n                .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)\n                .map(|(handle, _)| handle)\n                .filter(|&handle| !info[handle].is_empty());\n            // Check if there is more than one immediate data, and error if so.\n            // Use a loop for when returning multiple errors is supported.\n            if let Some(handle) = used_immediates.nth(1) {\n                return Err(EntryPointError::MoreThanOneImmediateUsed\n                    .with_span_handle(handle, &module.global_variables));\n            }\n        }\n\n        self.ep_resource_bindings.clear();\n        for (var_handle, var) in module.global_variables.iter() {\n            let usage = info[var_handle];\n            if usage.is_empty() {\n                continue;\n            }\n\n            if var.space == crate::AddressSpace::TaskPayload {\n                if ep.task_payload != Some(var_handle) {\n                    return Err(EntryPointError::WrongTaskPayloadUsed\n                        .with_span_handle(var_handle, &module.global_variables));\n                }\n                let size = module.types[var.ty].inner.size(module.to_ctx());\n                if size < 4 {\n                    return Err(EntryPointError::TaskPayloadTooSmall(size)\n                        .with_span_handle(var_handle, &module.global_variables));\n                }\n            }\n\n            let allowed_usage = match var.space {\n                crate::AddressSpace::Function => unreachable!(),\n                crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,\n                crate::AddressSpace::Storage { access } => storage_usage(access),\n                crate::AddressSpace::Handle => match module.types[var.ty].inner {\n                    crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {\n                        crate::TypeInner::Image {\n                            class: crate::ImageClass::Storage { access, .. },\n                            ..\n                        } => storage_usage(access),\n                        _ => GlobalUse::READ | GlobalUse::QUERY,\n                    },\n                    crate::TypeInner::Image {\n                        class: crate::ImageClass::Storage { access, .. },\n                        ..\n                    } => storage_usage(access),\n                    _ => GlobalUse::READ | GlobalUse::QUERY,\n                },\n                crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {\n                    GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY\n                }\n                crate::AddressSpace::TaskPayload => {\n                    GlobalUse::READ\n                        | GlobalUse::QUERY\n                        | if ep.stage == crate::ShaderStage::Task {\n                            GlobalUse::WRITE\n                        } else {\n                            GlobalUse::empty()\n                        }\n                }\n                crate::AddressSpace::Immediate => GlobalUse::READ,\n                crate::AddressSpace::RayPayload => {\n                    if !matches!(\n                        ep.stage,\n                        crate::ShaderStage::RayGeneration\n                            | crate::ShaderStage::ClosestHit\n                            | crate::ShaderStage::Miss\n                    ) {\n                        return Err(EntryPointError::RayPayloadInInvalidStage(ep.stage)\n                            .with_span_handle(var_handle, &module.global_variables));\n                    }\n                    GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE\n                }\n                crate::AddressSpace::IncomingRayPayload => {\n                    if !matches!(\n                        ep.stage,\n                        crate::ShaderStage::AnyHit\n                            | crate::ShaderStage::ClosestHit\n                            | crate::ShaderStage::Miss\n                    ) {\n                        return Err(EntryPointError::IncomingRayPayloadInInvalidStage(ep.stage)\n                            .with_span_handle(var_handle, &module.global_variables));\n                    }\n                    GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE\n                }\n            };\n            if !allowed_usage.contains(usage) {\n                log::warn!(\"\\tUsage error for: {var:?}\");\n                log::warn!(\"\\tAllowed usage: {allowed_usage:?}, requested: {usage:?}\");\n                return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)\n                    .with_span_handle(var_handle, &module.global_variables));\n            }\n\n            if let Some(ref bind) = var.binding {\n                if !self.ep_resource_bindings.insert(*bind) {\n                    if self.flags.contains(super::ValidationFlags::BINDINGS) {\n                        return Err(EntryPointError::BindingCollision(var_handle)\n                            .with_span_handle(var_handle, &module.global_variables));\n                    }\n                }\n            }\n        }\n\n        // If this is a `Mesh` entry point, check its vertex and primitive output types.\n        // We verified previously that only mesh shaders can have `mesh_info`.\n        if let &Some(ref mesh_info) = &ep.mesh_info {\n            if module.global_variables[mesh_info.output_variable].space\n                != crate::AddressSpace::WorkGroup\n            {\n                return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());\n            }\n\n            let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);\n            if let Some(e) = implied.2 {\n                return Err(e);\n            }\n\n            if let Some(e) = mesh_info.max_vertices_override {\n                if let crate::Expression::Override(o) = module.global_expressions[e] {\n                    if implied.1[0] != Some(o) {\n                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());\n                    }\n                }\n            }\n            if let Some(e) = mesh_info.max_primitives_override {\n                if let crate::Expression::Override(o) = module.global_expressions[e] {\n                    if implied.1[1] != Some(o) {\n                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());\n                    }\n                }\n            }\n\n            implied.0.max_vertices_override = mesh_info.max_vertices_override;\n            implied.0.max_primitives_override = mesh_info.max_primitives_override;\n            if implied.0 != *mesh_info {\n                return Err(EntryPointError::BadMeshOutputVariableType.with_span());\n            }\n            if mesh_info.topology == crate::MeshOutputTopology::Points\n                && !self\n                    .capabilities\n                    .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)\n            {\n                return Err(EntryPointError::UnsupportedCapability(\n                    Capabilities::MESH_SHADER_POINT_TOPOLOGY,\n                )\n                .with_span());\n            }\n\n            self.validate_mesh_output_type(\n                ep,\n                module,\n                mesh_info.vertex_output_type,\n                MeshOutputType::VertexOutput,\n            )?;\n            self.validate_mesh_output_type(\n                ep,\n                module,\n                mesh_info.primitive_output_type,\n                MeshOutputType::PrimitiveOutput,\n            )?;\n        }\n\n        Ok(info)\n    }\n}\n"
  },
  {
    "path": "naga/src/valid/mod.rs",
    "content": "/*!\nShader validator.\n*/\n\nmod analyzer;\nmod compose;\nmod expression;\nmod function;\nmod handles;\nmod interface;\nmod r#type;\n\nuse alloc::{boxed::Box, string::String, vec, vec::Vec};\nuse core::ops;\n\nuse bit_set::BitSet;\n\nuse crate::{\n    arena::{Handle, HandleSet},\n    proc::{ExpressionKindTracker, LayoutError, Layouter, TypeResolution},\n    FastHashSet,\n};\n\n//TODO: analyze the model at the same time as we validate it,\n// merge the corresponding matches over expressions and statements.\n\nuse crate::span::{AddSpan as _, WithSpan};\npub use analyzer::{ExpressionInfo, FunctionInfo, GlobalUse, Uniformity, UniformityRequirements};\npub use compose::ComposeError;\npub use expression::{check_literal_value, LiteralError};\npub use expression::{ConstExpressionError, ExpressionError};\npub use function::{CallError, FunctionError, LocalVariableError, SubgroupError};\npub use interface::{EntryPointError, GlobalVariableError, VaryingError};\npub use r#type::{Disalignment, ImmediateError, TypeError, TypeFlags, WidthError};\n\nuse self::handles::InvalidHandleError;\n\n/// Maximum size of a type, in bytes.\npub const MAX_TYPE_SIZE: u32 = 0x4000_0000; // 1GB\n\nbitflags::bitflags! {\n    /// Validation flags.\n    ///\n    /// If you are working with trusted shaders, then you may be able\n    /// to save some time by skipping validation.\n    ///\n    /// If you do not perform full validation, invalid shaders may\n    /// cause Naga to panic. If you do perform full validation and\n    /// [`Validator::validate`] returns `Ok`, then Naga promises that\n    /// code generation will either succeed or return an error; it\n    /// should never panic.\n    ///\n    /// The default value for `ValidationFlags` is\n    /// `ValidationFlags::all()`.\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct ValidationFlags: u8 {\n        /// Expressions.\n        const EXPRESSIONS = 0x1;\n        /// Statements and blocks of them.\n        const BLOCKS = 0x2;\n        /// Uniformity of control flow for operations that require it.\n        const CONTROL_FLOW_UNIFORMITY = 0x4;\n        /// Host-shareable structure layouts.\n        const STRUCT_LAYOUTS = 0x8;\n        /// Constants.\n        const CONSTANTS = 0x10;\n        /// Group, binding, and location attributes.\n        const BINDINGS = 0x20;\n    }\n}\n\nimpl Default for ValidationFlags {\n    fn default() -> Self {\n        Self::all()\n    }\n}\n\nbitflags::bitflags! {\n    /// Allowed IR capabilities.\n    #[must_use]\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct Capabilities: u64 {\n        /// Support for [`AddressSpace::Immediate`][1].\n        ///\n        /// [1]: crate::AddressSpace::Immediate\n        const IMMEDIATES = 1 << 0;\n        /// Float values with width = 8.\n        const FLOAT64 = 1 << 1;\n        /// Support for [`BuiltIn::PrimitiveIndex`][1].\n        ///\n        /// [1]: crate::BuiltIn::PrimitiveIndex\n        const PRIMITIVE_INDEX = 1 << 2;\n        /// Support for binding arrays of sampled textures and samplers.\n        const TEXTURE_AND_SAMPLER_BINDING_ARRAY = 1 << 3;\n        /// Support for binding arrays of uniform buffers.\n        const BUFFER_BINDING_ARRAY = 1 << 4;\n        /// Support for binding arrays of storage textures.\n        const STORAGE_TEXTURE_BINDING_ARRAY = 1 << 5;\n        /// Support for binding arrays of storage buffers.\n        const STORAGE_BUFFER_BINDING_ARRAY = 1 << 6;\n        /// Support for [`BuiltIn::ClipDistances`].\n        ///\n        /// [`BuiltIn::ClipDistances`]: crate::BuiltIn::ClipDistances\n        const CLIP_DISTANCES = 1 << 7;\n        /// Support for [`BuiltIn::CullDistance`].\n        ///\n        /// [`BuiltIn::CullDistance`]: crate::BuiltIn::CullDistance\n        const CULL_DISTANCE = 1 << 8;\n        /// Support for 16-bit normalized storage texture formats.\n        const STORAGE_TEXTURE_16BIT_NORM_FORMATS = 1 << 9;\n        /// Support for [`BuiltIn::ViewIndex`].\n        ///\n        /// [`BuiltIn::ViewIndex`]: crate::BuiltIn::ViewIndex\n        const MULTIVIEW = 1 << 10;\n        /// Support for `early_depth_test`.\n        const EARLY_DEPTH_TEST = 1 << 11;\n        /// Support for [`BuiltIn::SampleIndex`] and [`Sampling::Sample`].\n        ///\n        /// [`BuiltIn::SampleIndex`]: crate::BuiltIn::SampleIndex\n        /// [`Sampling::Sample`]: crate::Sampling::Sample\n        const MULTISAMPLED_SHADING = 1 << 12;\n        /// Support for ray queries and acceleration structures.\n        const RAY_QUERY = 1 << 13;\n        /// Support for generating two sources for blending from fragment shaders.\n        const DUAL_SOURCE_BLENDING = 1 << 14;\n        /// Support for arrayed cube textures.\n        const CUBE_ARRAY_TEXTURES = 1 << 15;\n        /// Support for 64-bit signed and unsigned integers.\n        const SHADER_INT64 = 1 << 16;\n        /// Support for subgroup operations (except barriers) in fragment and compute shaders.\n        ///\n        /// Subgroup operations in the vertex stage require\n        /// [`Capabilities::SUBGROUP_VERTEX_STAGE`] in addition to `Capabilities::SUBGROUP`.\n        /// (But note that `create_validator` automatically sets\n        /// `Capabilities::SUBGROUP` whenever `Features::SUBGROUP_VERTEX` is\n        /// available.)\n        ///\n        /// Subgroup barriers require [`Capabilities::SUBGROUP_BARRIER`] in addition to\n        /// `Capabilities::SUBGROUP`.\n        const SUBGROUP = 1 << 17;\n        /// Support for subgroup barriers in compute shaders.\n        ///\n        /// Requires [`Capabilities::SUBGROUP`]. Without it, enables nothing.\n        const SUBGROUP_BARRIER = 1 << 18;\n        /// Support for subgroup operations (not including barriers) in the vertex stage.\n        ///\n        /// Without [`Capabilities::SUBGROUP`], enables nothing. (But note that\n        /// `create_validator` automatically sets `Capabilities::SUBGROUP`\n        /// whenever `Features::SUBGROUP_VERTEX` is available.)\n        const SUBGROUP_VERTEX_STAGE = 1 << 19;\n        /// Support for [`AtomicFunction::Min`] and [`AtomicFunction::Max`] on\n        /// 64-bit integers in the [`Storage`] address space, when the return\n        /// value is not used.\n        ///\n        /// This is the only 64-bit atomic functionality available on Metal 3.1.\n        ///\n        /// [`AtomicFunction::Min`]: crate::AtomicFunction::Min\n        /// [`AtomicFunction::Max`]: crate::AtomicFunction::Max\n        /// [`Storage`]: crate::AddressSpace::Storage\n        const SHADER_INT64_ATOMIC_MIN_MAX = 1 << 20;\n        /// Support for all atomic operations on 64-bit integers.\n        const SHADER_INT64_ATOMIC_ALL_OPS = 1 << 21;\n        /// Support for [`AtomicFunction::Add`], [`AtomicFunction::Sub`],\n        /// and [`AtomicFunction::Exchange { compare: None }`] on 32-bit floating-point numbers\n        /// in the [`Storage`] address space.\n        ///\n        /// [`AtomicFunction::Add`]: crate::AtomicFunction::Add\n        /// [`AtomicFunction::Sub`]: crate::AtomicFunction::Sub\n        /// [`AtomicFunction::Exchange { compare: None }`]: crate::AtomicFunction::Exchange\n        /// [`Storage`]: crate::AddressSpace::Storage\n        const SHADER_FLOAT32_ATOMIC = 1 << 22;\n        /// Support for atomic operations on images.\n        const TEXTURE_ATOMIC = 1 << 23;\n        /// Support for atomic operations on 64-bit images.\n        const TEXTURE_INT64_ATOMIC = 1 << 24;\n        /// Support for ray queries returning vertex position\n        const RAY_HIT_VERTEX_POSITION = 1 << 25;\n        /// Support for 16-bit floating-point types.\n        const SHADER_FLOAT16 = 1 << 26;\n        /// Support for [`ImageClass::External`]\n        const TEXTURE_EXTERNAL = 1 << 27;\n        /// Support for `quantizeToF16`, `pack2x16float`, and `unpack2x16float`, which store\n        /// `f16`-precision values in `f32`s.\n        const SHADER_FLOAT16_IN_FLOAT32 = 1 << 28;\n        /// Support for fragment shader barycentric coordinates.\n        const SHADER_BARYCENTRICS = 1 << 29;\n        /// Support for task shaders, mesh shaders, and per-primitive fragment inputs\n        const MESH_SHADER = 1 << 30;\n        /// Support for mesh shaders which output points.\n        const MESH_SHADER_POINT_TOPOLOGY = 1 << 31;\n        /// Support for non-uniform indexing of binding arrays of sampled textures and samplers.\n        const TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 32;\n        /// Support for non-uniform indexing of binding arrays of uniform buffers.\n        const BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 33;\n        /// Support for non-uniform indexing of binding arrays of storage textures.\n        const STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 34;\n        /// Support for non-uniform indexing of binding arrays of storage buffers.\n        const STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 35;\n        /// Support for cooperative matrix types and operations\n        const COOPERATIVE_MATRIX = 1 << 36;\n        /// Support for per-vertex fragment input.\n        const PER_VERTEX = 1 << 37;\n        /// Support for ray generation, any hit, closest hit, and miss shaders.\n        const RAY_TRACING_PIPELINE = 1 << 38;\n        /// Support for draw index builtin\n        const DRAW_INDEX = 1 << 39;\n        /// Support for binding arrays of acceleration structures.\n        const ACCELERATION_STRUCTURE_BINDING_ARRAY = 1 << 40;\n        /// Support for the `@coherent` memory decoration on storage buffers.\n        const MEMORY_DECORATION_COHERENT = 1 << 41;\n        /// Support for the `@volatile` memory decoration on storage buffers.\n        const MEMORY_DECORATION_VOLATILE = 1 << 42;\n    }\n}\n\nimpl Capabilities {\n    /// Returns the extension corresponding to this capability, if there is one.\n    ///\n    /// This is used by integration tests.\n    #[cfg(feature = \"wgsl-in\")]\n    #[doc(hidden)]\n    pub const fn extension(&self) -> Option<crate::front::wgsl::ImplementedEnableExtension> {\n        use crate::front::wgsl::ImplementedEnableExtension as Ext;\n        match *self {\n            Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending),\n            // NOTE: `SHADER_FLOAT16_IN_FLOAT32` _does not_ require the `f16` extension\n            Self::SHADER_FLOAT16 => Some(Ext::F16),\n            Self::CLIP_DISTANCES => Some(Ext::ClipDistances),\n            Self::MESH_SHADER => Some(Ext::WgpuMeshShader),\n            Self::RAY_QUERY => Some(Ext::WgpuRayQuery),\n            Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),\n            Self::COOPERATIVE_MATRIX => Some(Ext::WgpuCooperativeMatrix),\n            Self::RAY_TRACING_PIPELINE => Some(Ext::WgpuRayTracingPipeline),\n            _ => None,\n        }\n    }\n}\n\nimpl Default for Capabilities {\n    fn default() -> Self {\n        Self::MULTISAMPLED_SHADING | Self::CUBE_ARRAY_TEXTURES\n    }\n}\n\nbitflags::bitflags! {\n    /// Supported subgroup operations\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\n    pub struct SubgroupOperationSet: u8 {\n        /// Barriers\n        // Possibly elections, when that is supported.\n        // https://github.com/gfx-rs/wgpu/issues/6042#issuecomment-3272603431\n        // Contrary to what the name \"basic\" suggests, HLSL/DX12 support the\n        // other subgroup operations, but do not support subgroup barriers.\n        const BASIC = 1 << 0;\n        /// Any, All\n        const VOTE = 1 << 1;\n        /// reductions, scans\n        const ARITHMETIC = 1 << 2;\n        /// ballot, broadcast\n        const BALLOT = 1 << 3;\n        /// shuffle, shuffle xor\n        const SHUFFLE = 1 << 4;\n        /// shuffle up, down\n        const SHUFFLE_RELATIVE = 1 << 5;\n        // We don't support these operations yet\n        // /// Clustered\n        // const CLUSTERED = 1 << 6;\n        /// Quad supported\n        const QUAD_FRAGMENT_COMPUTE = 1 << 7;\n        // /// Quad supported in all stages\n        // const QUAD_ALL_STAGES = 1 << 8;\n    }\n}\n\nimpl super::SubgroupOperation {\n    const fn required_operations(&self) -> SubgroupOperationSet {\n        use SubgroupOperationSet as S;\n        match *self {\n            Self::All | Self::Any => S::VOTE,\n            Self::Add | Self::Mul | Self::Min | Self::Max | Self::And | Self::Or | Self::Xor => {\n                S::ARITHMETIC\n            }\n        }\n    }\n}\n\nimpl super::GatherMode {\n    const fn required_operations(&self) -> SubgroupOperationSet {\n        use SubgroupOperationSet as S;\n        match *self {\n            Self::BroadcastFirst | Self::Broadcast(_) => S::BALLOT,\n            Self::Shuffle(_) | Self::ShuffleXor(_) => S::SHUFFLE,\n            Self::ShuffleUp(_) | Self::ShuffleDown(_) => S::SHUFFLE_RELATIVE,\n            Self::QuadBroadcast(_) | Self::QuadSwap(_) => S::QUAD_FRAGMENT_COMPUTE,\n        }\n    }\n}\n\nbitflags::bitflags! {\n    /// Validation flags.\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct ShaderStages: u16 {\n        const VERTEX = 0x1;\n        const FRAGMENT = 0x2;\n        const COMPUTE = 0x4;\n        const MESH = 0x8;\n        const TASK = 0x10;\n        const RAY_GENERATION = 0x20;\n        const ANY_HIT = 0x40;\n        const CLOSEST_HIT = 0x80;\n        const MISS = 0x100;\n        const COMPUTE_LIKE = Self::COMPUTE.bits() | Self::TASK.bits() | Self::MESH.bits();\n    }\n}\n\n#[derive(Debug, Clone, Default)]\n#[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n#[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\npub struct ModuleInfo {\n    type_flags: Vec<TypeFlags>,\n    functions: Vec<FunctionInfo>,\n    entry_points: Vec<FunctionInfo>,\n    const_expression_types: Box<[TypeResolution]>,\n}\n\nimpl ops::Index<Handle<crate::Type>> for ModuleInfo {\n    type Output = TypeFlags;\n    fn index(&self, handle: Handle<crate::Type>) -> &Self::Output {\n        &self.type_flags[handle.index()]\n    }\n}\n\nimpl ops::Index<Handle<crate::Function>> for ModuleInfo {\n    type Output = FunctionInfo;\n    fn index(&self, handle: Handle<crate::Function>) -> &Self::Output {\n        &self.functions[handle.index()]\n    }\n}\n\nimpl ops::Index<Handle<crate::Expression>> for ModuleInfo {\n    type Output = TypeResolution;\n    fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {\n        &self.const_expression_types[handle.index()]\n    }\n}\n\n#[derive(Debug)]\npub struct Validator {\n    flags: ValidationFlags,\n    capabilities: Capabilities,\n    subgroup_stages: ShaderStages,\n    subgroup_operations: SubgroupOperationSet,\n    types: Vec<r#type::TypeInfo>,\n    layouter: Layouter,\n    location_mask: BitSet,\n    ep_resource_bindings: FastHashSet<crate::ResourceBinding>,\n    switch_values: FastHashSet<crate::SwitchValue>,\n    valid_expression_list: Vec<Handle<crate::Expression>>,\n    valid_expression_set: HandleSet<crate::Expression>,\n    override_ids: FastHashSet<u16>,\n\n    /// Treat overrides whose initializers are not fully-evaluated\n    /// constant expressions as errors.\n    overrides_resolved: bool,\n\n    /// A checklist of expressions that must be visited by a specific kind of\n    /// statement.\n    ///\n    /// For example:\n    ///\n    /// - [`CallResult`] expressions must be visited by a [`Call`] statement.\n    /// - [`AtomicResult`] expressions must be visited by an [`Atomic`] statement.\n    ///\n    /// Be sure not to remove any [`Expression`] handle from this set unless\n    /// you've explicitly checked that it is the right kind of expression for\n    /// the visiting [`Statement`].\n    ///\n    /// [`CallResult`]: crate::Expression::CallResult\n    /// [`Call`]: crate::Statement::Call\n    /// [`AtomicResult`]: crate::Expression::AtomicResult\n    /// [`Atomic`]: crate::Statement::Atomic\n    /// [`Expression`]: crate::Expression\n    /// [`Statement`]: crate::Statement\n    needs_visit: HandleSet<crate::Expression>,\n\n    /// Whether any trace rays call is called, and whether all have vertex return.\n    /// If one call doesn't use vertex ruturn, builtins for triangle vertex positions\n    /// (not yet implemented) are not allowed.\n    trace_rays_vertex_return: TraceRayVertexReturnState,\n\n    /// The type of the ray payload, this must always be the same type in a particular\n    /// entrypoint\n    trace_rays_payload_type: Option<Handle<crate::Type>>,\n}\n\n#[derive(Debug)]\nenum TraceRayVertexReturnState {\n    /// No trace ray calls yet have been found.\n    NoTraceRays,\n    /// Trace ray calls have been found, at least\n    /// one uses an acceleration structure that\n    /// does not have the flag enabling vertex return.\n    // Don't yet have vertex return builtins to return.\n    // this error for.\n    #[expect(unused)]\n    NoVertexReturn(crate::Span),\n    /// Trace ray calls have been found, all\n    /// acceleration structures have the flag enabling\n    /// vertex return.\n    VertexReturn,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ConstantError {\n    #[error(\"Initializer must be a const-expression\")]\n    InitializerExprType,\n    #[error(\"The type doesn't match the constant\")]\n    InvalidType,\n    #[error(\"The type is not constructible\")]\n    NonConstructibleType,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum OverrideError {\n    #[error(\"Override name and ID are missing\")]\n    MissingNameAndID,\n    #[error(\"Override ID must be unique\")]\n    DuplicateID,\n    #[error(\"Initializer must be a const-expression or override-expression\")]\n    InitializerExprType,\n    #[error(\"The type doesn't match the override\")]\n    InvalidType,\n    #[error(\"The type is not constructible\")]\n    NonConstructibleType,\n    #[error(\"The type is not a scalar\")]\n    TypeNotScalar,\n    #[error(\"Override declarations are not allowed\")]\n    NotAllowed,\n    #[error(\"Override is uninitialized\")]\n    UninitializedOverride,\n    #[error(\"Constant expression {handle:?} is invalid\")]\n    ConstExpression {\n        handle: Handle<crate::Expression>,\n        source: ConstExpressionError,\n    },\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ValidationError {\n    #[error(transparent)]\n    InvalidHandle(#[from] InvalidHandleError),\n    #[error(transparent)]\n    Layouter(#[from] LayoutError),\n    #[error(\"Type {handle:?} '{name}' is invalid\")]\n    Type {\n        handle: Handle<crate::Type>,\n        name: String,\n        source: TypeError,\n    },\n    #[error(\"Constant expression {handle:?} is invalid\")]\n    ConstExpression {\n        handle: Handle<crate::Expression>,\n        source: ConstExpressionError,\n    },\n    #[error(\"Array size expression {handle:?} is not strictly positive\")]\n    ArraySizeError { handle: Handle<crate::Expression> },\n    #[error(\"Constant {handle:?} '{name}' is invalid\")]\n    Constant {\n        handle: Handle<crate::Constant>,\n        name: String,\n        source: ConstantError,\n    },\n    #[error(\"Override {handle:?} '{name}' is invalid\")]\n    Override {\n        handle: Handle<crate::Override>,\n        name: String,\n        source: OverrideError,\n    },\n    #[error(\"Global variable {handle:?} '{name}' is invalid\")]\n    GlobalVariable {\n        handle: Handle<crate::GlobalVariable>,\n        name: String,\n        source: GlobalVariableError,\n    },\n    #[error(\"Function {handle:?} '{name}' is invalid\")]\n    Function {\n        handle: Handle<crate::Function>,\n        name: String,\n        source: FunctionError,\n    },\n    #[error(\"Entry point {name} at {stage:?} is invalid\")]\n    EntryPoint {\n        stage: crate::ShaderStage,\n        name: String,\n        source: EntryPointError,\n    },\n    #[error(\"Module is corrupted\")]\n    Corrupted,\n}\n\nimpl crate::TypeInner {\n    const fn is_sized(&self) -> bool {\n        match *self {\n            Self::Scalar { .. }\n            | Self::Vector { .. }\n            | Self::Matrix { .. }\n            | Self::CooperativeMatrix { .. }\n            | Self::Array {\n                size: crate::ArraySize::Constant(_),\n                ..\n            }\n            | Self::Atomic { .. }\n            | Self::Pointer { .. }\n            | Self::ValuePointer { .. }\n            | Self::Struct { .. } => true,\n            Self::Array { .. }\n            | Self::Image { .. }\n            | Self::Sampler { .. }\n            | Self::AccelerationStructure { .. }\n            | Self::RayQuery { .. }\n            | Self::BindingArray { .. } => false,\n        }\n    }\n\n    /// Return the `ImageDimension` for which `self` is an appropriate coordinate.\n    const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {\n        match *self {\n            Self::Scalar(crate::Scalar {\n                kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                ..\n            }) => Some(crate::ImageDimension::D1),\n            Self::Vector {\n                size: crate::VectorSize::Bi,\n                scalar:\n                    crate::Scalar {\n                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                        ..\n                    },\n            } => Some(crate::ImageDimension::D2),\n            Self::Vector {\n                size: crate::VectorSize::Tri,\n                scalar:\n                    crate::Scalar {\n                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                        ..\n                    },\n            } => Some(crate::ImageDimension::D3),\n            _ => None,\n        }\n    }\n}\n\nimpl Validator {\n    /// Create a validator for Naga [`Module`]s.\n    ///\n    /// The `flags` argument indicates which stages of validation the\n    /// returned `Validator` should perform. Skipping stages can make\n    /// validation somewhat faster, but the validator may not reject some\n    /// invalid modules. Regardless of `flags`, validation always returns\n    /// a usable [`ModuleInfo`] value on success.\n    ///\n    /// If `flags` contains everything in `ValidationFlags::default()`,\n    /// then the returned Naga [`Validator`] will reject any [`Module`]\n    /// that would use capabilities not included in `capabilities`.\n    ///\n    /// [`Module`]: crate::Module\n    pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self {\n        let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) {\n            use SubgroupOperationSet as S;\n            S::BASIC\n                | S::VOTE\n                | S::ARITHMETIC\n                | S::BALLOT\n                | S::SHUFFLE\n                | S::SHUFFLE_RELATIVE\n                | S::QUAD_FRAGMENT_COMPUTE\n        } else {\n            SubgroupOperationSet::empty()\n        };\n        let subgroup_stages = {\n            let mut stages = ShaderStages::empty();\n            if capabilities.contains(Capabilities::SUBGROUP_VERTEX_STAGE) {\n                stages |= ShaderStages::VERTEX;\n            }\n            if capabilities.contains(Capabilities::SUBGROUP) {\n                stages |= ShaderStages::FRAGMENT | ShaderStages::COMPUTE_LIKE;\n            }\n            stages\n        };\n\n        Validator {\n            flags,\n            capabilities,\n            subgroup_stages,\n            subgroup_operations,\n            types: Vec::new(),\n            layouter: Layouter::default(),\n            location_mask: BitSet::new(),\n            ep_resource_bindings: FastHashSet::default(),\n            switch_values: FastHashSet::default(),\n            valid_expression_list: Vec::new(),\n            valid_expression_set: HandleSet::new(),\n            override_ids: FastHashSet::default(),\n            overrides_resolved: false,\n            needs_visit: HandleSet::new(),\n            trace_rays_vertex_return: TraceRayVertexReturnState::NoTraceRays,\n            trace_rays_payload_type: None,\n        }\n    }\n\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8207): Consider removing this\n    pub const fn subgroup_stages(&mut self, stages: ShaderStages) -> &mut Self {\n        self.subgroup_stages = stages;\n        self\n    }\n\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8207): Consider removing this\n    pub const fn subgroup_operations(&mut self, operations: SubgroupOperationSet) -> &mut Self {\n        self.subgroup_operations = operations;\n        self\n    }\n\n    /// Reset the validator internals\n    pub fn reset(&mut self) {\n        self.types.clear();\n        self.layouter.clear();\n        self.location_mask.make_empty();\n        self.ep_resource_bindings.clear();\n        self.switch_values.clear();\n        self.valid_expression_list.clear();\n        self.valid_expression_set.clear();\n        self.override_ids.clear();\n    }\n\n    fn validate_constant(\n        &self,\n        handle: Handle<crate::Constant>,\n        gctx: crate::proc::GlobalCtx,\n        mod_info: &ModuleInfo,\n        global_expr_kind: &ExpressionKindTracker,\n    ) -> Result<(), ConstantError> {\n        let con = &gctx.constants[handle];\n\n        let type_info = &self.types[con.ty.index()];\n        if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {\n            return Err(ConstantError::NonConstructibleType);\n        }\n\n        if !global_expr_kind.is_const(con.init) {\n            return Err(ConstantError::InitializerExprType);\n        }\n\n        if !gctx.compare_types(&TypeResolution::Handle(con.ty), &mod_info[con.init]) {\n            return Err(ConstantError::InvalidType);\n        }\n\n        Ok(())\n    }\n\n    fn validate_override(\n        &mut self,\n        handle: Handle<crate::Override>,\n        gctx: crate::proc::GlobalCtx,\n        mod_info: &ModuleInfo,\n    ) -> Result<(), OverrideError> {\n        let o = &gctx.overrides[handle];\n\n        if let Some(id) = o.id {\n            if !self.override_ids.insert(id) {\n                return Err(OverrideError::DuplicateID);\n            }\n        }\n\n        let type_info = &self.types[o.ty.index()];\n        if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {\n            return Err(OverrideError::NonConstructibleType);\n        }\n\n        match gctx.types[o.ty].inner {\n            crate::TypeInner::Scalar(\n                crate::Scalar::BOOL\n                | crate::Scalar::I32\n                | crate::Scalar::U32\n                | crate::Scalar::F16\n                | crate::Scalar::F32\n                | crate::Scalar::F64,\n            ) => {}\n            _ => return Err(OverrideError::TypeNotScalar),\n        }\n\n        if let Some(init) = o.init {\n            if !gctx.compare_types(&TypeResolution::Handle(o.ty), &mod_info[init]) {\n                return Err(OverrideError::InvalidType);\n            }\n        } else if self.overrides_resolved {\n            return Err(OverrideError::UninitializedOverride);\n        }\n\n        Ok(())\n    }\n\n    /// Check the given module to be valid.\n    pub fn validate(\n        &mut self,\n        module: &crate::Module,\n    ) -> Result<ModuleInfo, WithSpan<ValidationError>> {\n        self.overrides_resolved = false;\n        self.validate_impl(module)\n    }\n\n    /// Check the given module to be valid, requiring overrides to be resolved.\n    ///\n    /// This is the same as [`validate`], except that any override\n    /// whose value is not a fully-evaluated constant expression is\n    /// treated as an error.\n    ///\n    /// [`validate`]: Validator::validate\n    pub fn validate_resolved_overrides(\n        &mut self,\n        module: &crate::Module,\n    ) -> Result<ModuleInfo, WithSpan<ValidationError>> {\n        self.overrides_resolved = true;\n        self.validate_impl(module)\n    }\n\n    fn validate_impl(\n        &mut self,\n        module: &crate::Module,\n    ) -> Result<ModuleInfo, WithSpan<ValidationError>> {\n        self.reset();\n        self.reset_types(module.types.len());\n\n        Self::validate_module_handles(module).map_err(|e| e.with_span())?;\n\n        self.layouter.update(module.to_ctx()).map_err(|e| {\n            let handle = e.ty;\n            ValidationError::from(e).with_span_handle(handle, &module.types)\n        })?;\n\n        // These should all get overwritten.\n        let placeholder = TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {\n            kind: crate::ScalarKind::Bool,\n            width: 0,\n        }));\n\n        let mut mod_info = ModuleInfo {\n            type_flags: Vec::with_capacity(module.types.len()),\n            functions: Vec::with_capacity(module.functions.len()),\n            entry_points: Vec::with_capacity(module.entry_points.len()),\n            const_expression_types: vec![placeholder; module.global_expressions.len()]\n                .into_boxed_slice(),\n        };\n\n        for (handle, ty) in module.types.iter() {\n            let ty_info = self\n                .validate_type(handle, module.to_ctx())\n                .map_err(|source| {\n                    ValidationError::Type {\n                        handle,\n                        name: ty.name.clone().unwrap_or_default(),\n                        source,\n                    }\n                    .with_span_handle(handle, &module.types)\n                })?;\n            debug_assert!(\n                ty_info.flags.contains(TypeFlags::CONSTRUCTIBLE)\n                    == module.types[handle].inner.is_constructible(&module.types)\n            );\n            mod_info.type_flags.push(ty_info.flags);\n            self.types[handle.index()] = ty_info;\n        }\n\n        {\n            let t = crate::Arena::new();\n            let resolve_context = crate::proc::ResolveContext::with_locals(module, &t, &[]);\n            for (handle, _) in module.global_expressions.iter() {\n                mod_info\n                    .process_const_expression(handle, &resolve_context, module.to_ctx())\n                    .map_err(|source| {\n                        ValidationError::ConstExpression { handle, source }\n                            .with_span_handle(handle, &module.global_expressions)\n                    })?\n            }\n        }\n\n        let global_expr_kind = ExpressionKindTracker::from_arena(&module.global_expressions);\n\n        if self.flags.contains(ValidationFlags::CONSTANTS) {\n            for (handle, _) in module.global_expressions.iter() {\n                self.validate_const_expression(\n                    handle,\n                    module.to_ctx(),\n                    &mod_info,\n                    &global_expr_kind,\n                )\n                .map_err(|source| {\n                    ValidationError::ConstExpression { handle, source }\n                        .with_span_handle(handle, &module.global_expressions)\n                })?\n            }\n\n            for (handle, constant) in module.constants.iter() {\n                self.validate_constant(handle, module.to_ctx(), &mod_info, &global_expr_kind)\n                    .map_err(|source| {\n                        ValidationError::Constant {\n                            handle,\n                            name: constant.name.clone().unwrap_or_default(),\n                            source,\n                        }\n                        .with_span_handle(handle, &module.constants)\n                    })?\n            }\n\n            for (handle, r#override) in module.overrides.iter() {\n                self.validate_override(handle, module.to_ctx(), &mod_info)\n                    .map_err(|source| {\n                        ValidationError::Override {\n                            handle,\n                            name: r#override.name.clone().unwrap_or_default(),\n                            source,\n                        }\n                        .with_span_handle(handle, &module.overrides)\n                    })?;\n            }\n        }\n\n        for (var_handle, var) in module.global_variables.iter() {\n            self.validate_global_var(var, module.to_ctx(), &mod_info, &global_expr_kind)\n                .map_err(|source| {\n                    ValidationError::GlobalVariable {\n                        handle: var_handle,\n                        name: var.name.clone().unwrap_or_default(),\n                        source,\n                    }\n                    .with_span_handle(var_handle, &module.global_variables)\n                })?;\n        }\n\n        for (handle, fun) in module.functions.iter() {\n            match self.validate_function(fun, module, &mod_info, false) {\n                Ok(info) => mod_info.functions.push(info),\n                Err(error) => {\n                    return Err(error.and_then(|source| {\n                        ValidationError::Function {\n                            handle,\n                            name: fun.name.clone().unwrap_or_default(),\n                            source,\n                        }\n                        .with_span_handle(handle, &module.functions)\n                    }))\n                }\n            }\n        }\n\n        let mut ep_map = FastHashSet::default();\n        for ep in module.entry_points.iter() {\n            if !ep_map.insert((ep.stage, &ep.name)) {\n                return Err(ValidationError::EntryPoint {\n                    stage: ep.stage,\n                    name: ep.name.clone(),\n                    source: EntryPointError::Conflict,\n                }\n                .with_span()); // TODO: keep some EP span information?\n            }\n\n            match self.validate_entry_point(ep, module, &mod_info) {\n                Ok(info) => mod_info.entry_points.push(info),\n                Err(error) => {\n                    return Err(error.and_then(|source| {\n                        ValidationError::EntryPoint {\n                            stage: ep.stage,\n                            name: ep.name.clone(),\n                            source,\n                        }\n                        .with_span()\n                    }));\n                }\n            }\n        }\n\n        Ok(mod_info)\n    }\n}\n\nfn validate_atomic_compare_exchange_struct(\n    types: &crate::UniqueArena<crate::Type>,\n    members: &[crate::StructMember],\n    scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,\n) -> bool {\n    members.len() == 2\n        && members[0].name.as_deref() == Some(\"old_value\")\n        && scalar_predicate(&types[members[0].ty].inner)\n        && members[1].name.as_deref() == Some(\"exchanged\")\n        && types[members[1].ty].inner == crate::TypeInner::Scalar(crate::Scalar::BOOL)\n}\n"
  },
  {
    "path": "naga/src/valid/type.rs",
    "content": "use alloc::string::String;\n\nuse super::Capabilities;\nuse crate::{arena::Handle, ir, proc::Alignment};\n\nbitflags::bitflags! {\n    /// Flags associated with [`Type`]s by [`Validator`].\n    ///\n    /// [`Type`]: crate::Type\n    /// [`Validator`]: crate::valid::Validator\n    #[cfg_attr(feature = \"serialize\", derive(serde::Serialize))]\n    #[cfg_attr(feature = \"deserialize\", derive(serde::Deserialize))]\n    #[repr(transparent)]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct TypeFlags: u8 {\n        /// Can be used for data variables.\n        ///\n        /// This flag is required on types of local variables, function\n        /// arguments, array elements, and struct members.\n        ///\n        /// This includes all types except [`Image`], [`Sampler`],\n        /// and some [`Pointer`] types.\n        ///\n        /// [`Image`]: crate::TypeInner::Image\n        /// [`Sampler`]: crate::TypeInner::Sampler\n        /// [`Pointer`]: crate::TypeInner::Pointer\n        const DATA = 0x1;\n\n        /// The data type has a size known by pipeline creation time.\n        ///\n        /// Unsized types are quite restricted. The only unsized types permitted\n        /// by Naga, other than the non-[`DATA`] types like [`Image`] and\n        /// [`Sampler`], are dynamically-sized [`Array`]s, and [`Struct`]s whose\n        /// last members are such arrays. See the documentation for those types\n        /// for details.\n        ///\n        /// [`DATA`]: TypeFlags::DATA\n        /// [`Image`]: crate::TypeInner::Image\n        /// [`Sampler`]: crate::TypeInner::Sampler\n        /// [`Array`]: crate::TypeInner::Array\n        /// [`Struct`]: crate::TypeInner::Struct\n        const SIZED = 0x2;\n\n        /// The data can be copied around.\n        const COPY = 0x4;\n\n        /// Can be be used in pipeline stage I/O.\n        ///\n        /// Applies to the following:\n        ///   - Types that may be used in a [`Location`] binding (numeric scalars and vectors)\n        ///   - `@blend_src` structs\n        ///\n        /// See [location-attr] and [input-output].\n        ///\n        /// [`Location`]: crate::Binding::Location\n        /// [location-attr]: https://gpuweb.github.io/gpuweb/wgsl/#location-attr\n        /// [input-output]: https://gpuweb.github.io/gpuweb/wgsl/#input-output-locations\n        /// https://gpuweb.github.io/gpuweb/wgsl/#location-attr\n        const IO_SHAREABLE = 0x8;\n\n        /// Can be used for host-shareable structures.\n        const HOST_SHAREABLE = 0x10;\n\n        /// The set of types with a fixed size at shader-creation time (ie. everything\n        /// except arrays sized by an override-expression)\n        const CREATION_RESOLVED = 0x20;\n\n        /// This type can be passed as a function argument.\n        const ARGUMENT = 0x40;\n\n        /// A WGSL [constructible] type.\n        ///\n        /// The constructible types are scalars, vectors, matrices, fixed-size\n        /// arrays of constructible types, and structs whose members are all\n        /// constructible.\n        ///\n        /// [constructible]: https://gpuweb.github.io/gpuweb/wgsl/#constructible\n        const CONSTRUCTIBLE = 0x80;\n    }\n}\n\n#[derive(Clone, Copy, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum Disalignment {\n    #[error(\"The array stride {stride} is not a multiple of the required alignment {alignment}\")]\n    ArrayStride { stride: u32, alignment: Alignment },\n    #[error(\"The struct span {span}, is not a multiple of the required alignment {alignment}\")]\n    StructSpan { span: u32, alignment: Alignment },\n    #[error(\"The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}\")]\n    MemberOffset {\n        index: u32,\n        offset: u32,\n        alignment: Alignment,\n    },\n    #[error(\"The struct member[{index}] offset {offset} must be at least {expected}\")]\n    MemberOffsetAfterStruct {\n        index: u32,\n        offset: u32,\n        expected: u32,\n    },\n    #[error(\"The struct member[{index}] is not statically sized\")]\n    UnsizedMember { index: u32 },\n    #[error(\"The type is not host-shareable\")]\n    NonHostShareable,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum TypeError {\n    #[error(\"Capability {0:?} is required\")]\n    MissingCapability(Capabilities),\n    #[error(\"The {0:?} scalar width {1} is not supported for an atomic\")]\n    InvalidAtomicWidth(crate::ScalarKind, crate::Bytes),\n    #[error(\"Invalid type for pointer target {0:?}\")]\n    InvalidPointerBase(Handle<crate::Type>),\n    #[error(\"Unsized types like {base:?} must be in the `Storage` address space, not `{space:?}`\")]\n    InvalidPointerToUnsized {\n        base: Handle<crate::Type>,\n        space: crate::AddressSpace,\n    },\n    #[error(\"Expected data type, found {0:?}\")]\n    InvalidData(Handle<crate::Type>),\n    #[error(\"Base type {0:?} for the array is invalid\")]\n    InvalidArrayBaseType(Handle<crate::Type>),\n    #[error(\"Matrix elements must always be floating-point types\")]\n    MatrixElementNotFloat,\n    #[error(\"The constant {0:?} is specialized, and cannot be used as an array size\")]\n    UnsupportedSpecializedArrayLength(Handle<crate::Constant>),\n    #[error(\"{} of dimensionality {dim:?} and class {class:?} are not supported\", if *.arrayed {\"Arrayed images\"} else {\"Images\"})]\n    UnsupportedImageType {\n        dim: crate::ImageDimension,\n        arrayed: bool,\n        class: crate::ImageClass,\n    },\n    #[error(\"Array stride {stride} does not match the expected {expected}\")]\n    InvalidArrayStride { stride: u32, expected: u32 },\n    #[error(\"Field '{0}' can't be dynamically-sized, has type {1:?}\")]\n    InvalidDynamicArray(String, Handle<crate::Type>),\n    #[error(\"The base handle {0:?} has to be a struct\")]\n    BindingArrayBaseTypeNotStruct(Handle<crate::Type>),\n    #[error(\"Binding arrays of external textures are not yet supported\")]\n    BindingArrayBaseExternalTextures,\n    #[error(\"Structure member[{index}] at {offset} overlaps the previous member\")]\n    MemberOverlap { index: u32, offset: u32 },\n    #[error(\n        \"Structure member[{index}] at {offset} and size {size} crosses the structure boundary of size {span}\"\n    )]\n    MemberOutOfBounds {\n        index: u32,\n        offset: u32,\n        size: u32,\n        span: u32,\n    },\n    #[error(\"Structure types must have at least one member\")]\n    EmptyStruct,\n    #[error(\"Invalid `@blend_src` structure: {0}\")]\n    InvalidBlendSrc(super::VaryingError),\n    #[error(transparent)]\n    WidthError(#[from] WidthError),\n    #[error(\n        \"The base handle {0:?} has an override-expression that didn't get resolved to a constant\"\n    )]\n    UnresolvedOverride(Handle<crate::Type>),\n    #[error(\"Override-sized array type {0:?} does not have a positive size\")]\n    InvalidArraySize(Handle<crate::Type>),\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum WidthError {\n    #[error(\"The {0:?} scalar width {1} is not supported\")]\n    Invalid(crate::ScalarKind, crate::Bytes),\n    #[error(\"Using `{name}` values requires the `naga::valid::Capabilities::{flag}` flag\")]\n    MissingCapability {\n        name: &'static str,\n        flag: &'static str,\n    },\n\n    #[error(\"Abstract types may only appear in constant expressions\")]\n    Abstract,\n}\n\n#[derive(Clone, Debug, thiserror::Error)]\n#[cfg_attr(test, derive(PartialEq))]\npub enum ImmediateError {\n    #[error(\"The scalar type {0:?} is not supported in immediates\")]\n    InvalidScalar(crate::Scalar),\n}\n\n// Only makes sense if `flags.contains(HOST_SHAREABLE)`\ntype LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;\ntype ImmediateCompatibility = Result<(), ImmediateError>;\n\nfn check_member_layout(\n    accum: &mut LayoutCompatibility,\n    member: &crate::StructMember,\n    member_index: u32,\n    member_layout: LayoutCompatibility,\n    parent_handle: Handle<crate::Type>,\n) {\n    *accum = match (*accum, member_layout) {\n        (Ok(cur_alignment), Ok(alignment)) => {\n            if alignment.is_aligned(member.offset) {\n                Ok(cur_alignment.max(alignment))\n            } else {\n                Err((\n                    parent_handle,\n                    Disalignment::MemberOffset {\n                        index: member_index,\n                        offset: member.offset,\n                        alignment,\n                    },\n                ))\n            }\n        }\n        (Err(e), _) | (_, Err(e)) => Err(e),\n    };\n}\n\n/// Determine whether a pointer in `space` can be passed as an argument.\n///\n/// If a pointer in `space` is permitted to be passed as an argument to a\n/// user-defined function, return `TypeFlags::ARGUMENT`. Otherwise, return\n/// `TypeFlags::empty()`.\n///\n/// Pointers passed as arguments to user-defined functions must be in the\n/// `Function` or `Private` address space.\nconst fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags {\n    use crate::AddressSpace as As;\n    match space {\n        As::Function | As::Private | As::RayPayload | As::IncomingRayPayload => TypeFlags::ARGUMENT,\n        As::Uniform\n        | As::Storage { .. }\n        | As::Handle\n        | As::Immediate\n        | As::WorkGroup\n        | As::TaskPayload => TypeFlags::empty(),\n    }\n}\n\n#[derive(Clone, Debug)]\npub(super) struct TypeInfo {\n    pub flags: TypeFlags,\n    pub uniform_layout: LayoutCompatibility,\n    pub storage_layout: LayoutCompatibility,\n    pub immediates_compatibility: ImmediateCompatibility,\n}\n\nimpl TypeInfo {\n    const fn dummy() -> Self {\n        TypeInfo {\n            flags: TypeFlags::empty(),\n            uniform_layout: Ok(Alignment::ONE),\n            storage_layout: Ok(Alignment::ONE),\n            immediates_compatibility: Ok(()),\n        }\n    }\n\n    const fn new(flags: TypeFlags, alignment: Alignment) -> Self {\n        TypeInfo {\n            flags,\n            uniform_layout: Ok(alignment),\n            storage_layout: Ok(alignment),\n            immediates_compatibility: Ok(()),\n        }\n    }\n}\n\nimpl super::Validator {\n    const fn require_type_capability(&self, capability: Capabilities) -> Result<(), TypeError> {\n        if self.capabilities.contains(capability) {\n            Ok(())\n        } else {\n            Err(TypeError::MissingCapability(capability))\n        }\n    }\n\n    /// Check whether `scalar` is a permitted scalar width.\n    ///\n    /// If `scalar` is not a width allowed by the selected [`Capabilities`],\n    /// return an error explaining why.\n    ///\n    /// If `scalar` is allowed, return a [`ImmediateCompatibility`] result\n    /// that says whether `scalar` is allowed specifically in immediates.\n    ///\n    /// [`Capabilities`]: crate::valid::Capabilities\n    pub(super) const fn check_width(\n        &self,\n        scalar: crate::Scalar,\n    ) -> Result<ImmediateCompatibility, WidthError> {\n        let mut immediates_compatibility = Ok(());\n        let good = match scalar.kind {\n            crate::ScalarKind::Bool => scalar.width == crate::BOOL_WIDTH,\n            crate::ScalarKind::Float => match scalar.width {\n                8 => {\n                    if !self.capabilities.contains(Capabilities::FLOAT64) {\n                        return Err(WidthError::MissingCapability {\n                            name: \"f64\",\n                            flag: \"FLOAT64\",\n                        });\n                    }\n                    true\n                }\n                2 => {\n                    if !self.capabilities.contains(Capabilities::SHADER_FLOAT16) {\n                        return Err(WidthError::MissingCapability {\n                            name: \"f16\",\n                            flag: \"FLOAT16\",\n                        });\n                    }\n\n                    immediates_compatibility = Err(ImmediateError::InvalidScalar(scalar));\n\n                    true\n                }\n                _ => scalar.width == 4,\n            },\n            crate::ScalarKind::Sint => {\n                if scalar.width == 8 {\n                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {\n                        return Err(WidthError::MissingCapability {\n                            name: \"i64\",\n                            flag: \"SHADER_INT64\",\n                        });\n                    }\n                    true\n                } else {\n                    scalar.width == 4\n                }\n            }\n            crate::ScalarKind::Uint => {\n                if scalar.width == 8 {\n                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {\n                        return Err(WidthError::MissingCapability {\n                            name: \"u64\",\n                            flag: \"SHADER_INT64\",\n                        });\n                    }\n                    true\n                } else {\n                    scalar.width == 4\n                }\n            }\n            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {\n                return Err(WidthError::Abstract);\n            }\n        };\n        if good {\n            Ok(immediates_compatibility)\n        } else {\n            Err(WidthError::Invalid(scalar.kind, scalar.width))\n        }\n    }\n\n    pub(super) fn reset_types(&mut self, size: usize) {\n        self.types.clear();\n        self.types.resize(size, TypeInfo::dummy());\n        self.layouter.clear();\n    }\n\n    pub(super) fn validate_type(\n        &self,\n        handle: Handle<crate::Type>,\n        gctx: crate::proc::GlobalCtx,\n    ) -> Result<TypeInfo, TypeError> {\n        use crate::TypeInner as Ti;\n        Ok(match gctx.types[handle].inner {\n            Ti::Scalar(scalar) => {\n                let immediates_compatibility = self.check_width(scalar)?;\n                let shareable = if scalar.kind.is_numeric() {\n                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE\n                } else {\n                    TypeFlags::empty()\n                };\n                let mut type_info = TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::ARGUMENT\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::CREATION_RESOLVED\n                        | shareable,\n                    Alignment::from_width(scalar.width),\n                );\n                type_info.immediates_compatibility = immediates_compatibility;\n                type_info\n            }\n            Ti::Vector { size, scalar } => {\n                let immediates_compatibility = self.check_width(scalar)?;\n                let shareable = if scalar.kind.is_numeric() {\n                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE\n                } else {\n                    TypeFlags::empty()\n                };\n                let mut type_info = TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::ARGUMENT\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::CREATION_RESOLVED\n                        | shareable,\n                    Alignment::from(size) * Alignment::from_width(scalar.width),\n                );\n                type_info.immediates_compatibility = immediates_compatibility;\n                type_info\n            }\n            Ti::Matrix {\n                columns: _,\n                rows,\n                scalar,\n            } => {\n                if scalar.kind != crate::ScalarKind::Float {\n                    return Err(TypeError::MatrixElementNotFloat);\n                }\n                let immediates_compatibility = self.check_width(scalar)?;\n                let mut type_info = TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::ARGUMENT\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::from(rows) * Alignment::from_width(scalar.width),\n                );\n                type_info.immediates_compatibility = immediates_compatibility;\n                type_info\n            }\n            Ti::CooperativeMatrix {\n                columns: _,\n                rows: _,\n                scalar,\n                role: _,\n            } => {\n                self.require_type_capability(Capabilities::COOPERATIVE_MATRIX)?;\n                // Allow f16 (width 2) and f32 (width 4) for cooperative matrices\n                if scalar.kind != crate::ScalarKind::Float\n                    || (scalar.width != 2 && scalar.width != 4)\n                {\n                    return Err(TypeError::MatrixElementNotFloat);\n                }\n                TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::ARGUMENT\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::from_width(scalar.width),\n                )\n            }\n            Ti::Atomic(scalar) => {\n                match scalar {\n                    crate::Scalar {\n                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,\n                        width: _,\n                    } => {\n                        if scalar.width == 8\n                            && !self.capabilities.intersects(\n                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS\n                                    | Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,\n                            )\n                        {\n                            return Err(TypeError::MissingCapability(\n                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,\n                            ));\n                        }\n                    }\n                    crate::Scalar::F32 => {\n                        if !self\n                            .capabilities\n                            .contains(Capabilities::SHADER_FLOAT32_ATOMIC)\n                        {\n                            return Err(TypeError::MissingCapability(\n                                Capabilities::SHADER_FLOAT32_ATOMIC,\n                            ));\n                        }\n                    }\n                    _ => return Err(TypeError::InvalidAtomicWidth(scalar.kind, scalar.width)),\n                };\n                TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::from_width(scalar.width),\n                )\n            }\n            Ti::Pointer { base, space } => {\n                use crate::AddressSpace as As;\n\n                let base_info = &self.types[base.index()];\n                if !base_info.flags.contains(TypeFlags::DATA) {\n                    return Err(TypeError::InvalidPointerBase(base));\n                }\n\n                // Runtime-sized values can only live in the `Storage` address\n                // space, so it's useless to have a pointer to such a type in\n                // any other space.\n                //\n                // Detecting this problem here prevents the definition of\n                // functions like:\n                //\n                //     fn f(p: ptr<workgroup, UnsizedType>) -> ... { ... }\n                //\n                // which would otherwise be permitted, but uncallable. (They\n                // may also present difficulties in code generation).\n                if !base_info.flags.contains(TypeFlags::SIZED) {\n                    match space {\n                        As::Storage { .. } => {}\n                        _ => {\n                            return Err(TypeError::InvalidPointerToUnsized { base, space });\n                        }\n                    }\n                }\n\n                // `Validator::validate_function` actually checks the address\n                // space of pointer arguments explicitly before checking the\n                // `ARGUMENT` flag, to give better error messages. But it seems\n                // best to set `ARGUMENT` accurately anyway.\n                let argument_flag = ptr_space_argument_flag(space);\n\n                // Pointers cannot be stored in variables, structure members, or\n                // array elements, so we do not mark them as `DATA`.\n                TypeInfo::new(\n                    argument_flag\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                )\n            }\n            Ti::ValuePointer {\n                size: _,\n                scalar,\n                space,\n            } => {\n                // ValuePointer should be treated the same way as the equivalent\n                // Pointer / Scalar / Vector combination, so each step in those\n                // variants' match arms should have a counterpart here.\n                //\n                // However, some cases are trivial: All our implicit base types\n                // are DATA and SIZED, so we can never return\n                // `InvalidPointerBase` or `InvalidPointerToUnsized`.\n                let _ = self.check_width(scalar)?;\n\n                // `Validator::validate_function` actually checks the address\n                // space of pointer arguments explicitly before checking the\n                // `ARGUMENT` flag, to give better error messages. But it seems\n                // best to set `ARGUMENT` accurately anyway.\n                let argument_flag = ptr_space_argument_flag(space);\n\n                // Pointers cannot be stored in variables, structure members, or\n                // array elements, so we do not mark them as `DATA`.\n                TypeInfo::new(\n                    argument_flag\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                )\n            }\n            Ti::Array { base, size, stride } => {\n                let base_info = &self.types[base.index()];\n                if !base_info\n                    .flags\n                    .contains(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::CREATION_RESOLVED)\n                {\n                    return Err(TypeError::InvalidArrayBaseType(base));\n                }\n\n                if self.overrides_resolved {\n                    // This check only makes sense for override-sized arrays.\n                    // `ArraySize::Constant` holds a `NonZeroU32`.\n                    if let crate::ArraySize::Pending(_) = size {\n                        size.resolve(gctx)\n                            .map_err(|_| TypeError::InvalidArraySize(handle))?;\n                    }\n                }\n\n                let base_layout = self.layouter[base];\n                let general_alignment = base_layout.alignment;\n                let uniform_layout = match base_info.uniform_layout {\n                    Ok(base_alignment) => {\n                        let alignment = base_alignment\n                            .max(general_alignment)\n                            .max(Alignment::MIN_UNIFORM);\n                        if alignment.is_aligned(stride) {\n                            Ok(alignment)\n                        } else {\n                            Err((handle, Disalignment::ArrayStride { stride, alignment }))\n                        }\n                    }\n                    Err(e) => Err(e),\n                };\n                let storage_layout = match base_info.storage_layout {\n                    Ok(base_alignment) => {\n                        let alignment = base_alignment.max(general_alignment);\n                        if alignment.is_aligned(stride) {\n                            Ok(alignment)\n                        } else {\n                            Err((handle, Disalignment::ArrayStride { stride, alignment }))\n                        }\n                    }\n                    Err(e) => Err(e),\n                };\n\n                let type_info_mask = match size {\n                    crate::ArraySize::Constant(_) => {\n                        TypeFlags::DATA\n                            | TypeFlags::SIZED\n                            | TypeFlags::COPY\n                            | TypeFlags::HOST_SHAREABLE\n                            | TypeFlags::ARGUMENT\n                            | TypeFlags::CONSTRUCTIBLE\n                            | TypeFlags::CREATION_RESOLVED\n                    }\n                    crate::ArraySize::Pending(_) => {\n                        TypeFlags::DATA\n                            | TypeFlags::SIZED\n                            | TypeFlags::COPY\n                            | TypeFlags::HOST_SHAREABLE\n                            | TypeFlags::ARGUMENT\n                    }\n                    crate::ArraySize::Dynamic => {\n                        // Non-SIZED types may only appear as the last element of a structure.\n                        // This is enforced by checks for SIZED-ness for all compound types,\n                        // and a special case for structs.\n                        TypeFlags::DATA\n                            | TypeFlags::COPY\n                            | TypeFlags::HOST_SHAREABLE\n                            | TypeFlags::CREATION_RESOLVED\n                    }\n                };\n\n                TypeInfo {\n                    flags: base_info.flags & type_info_mask,\n                    uniform_layout,\n                    storage_layout,\n                    immediates_compatibility: base_info.immediates_compatibility.clone(),\n                }\n            }\n            Ti::Struct { ref members, span } => {\n                if members.is_empty() {\n                    return Err(TypeError::EmptyStruct);\n                }\n\n                let mut blend_src_types = [None, None];\n                let mut non_blend_src_location = None;\n\n                let mut ti = TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::SIZED\n                        | TypeFlags::COPY\n                        | TypeFlags::HOST_SHAREABLE\n                        | TypeFlags::ARGUMENT\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                );\n                ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);\n\n                let mut min_offset = 0;\n                let mut prev_struct_data: Option<(u32, u32)> = None;\n\n                for (i, member) in members.iter().enumerate() {\n                    let base_info = &self.types[member.ty.index()];\n                    if !base_info\n                        .flags\n                        .contains(TypeFlags::DATA | TypeFlags::CREATION_RESOLVED)\n                    {\n                        return Err(TypeError::InvalidData(member.ty));\n                    }\n                    if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) {\n                        if ti.uniform_layout.is_ok() {\n                            ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable));\n                        }\n                        if ti.storage_layout.is_ok() {\n                            ti.storage_layout = Err((member.ty, Disalignment::NonHostShareable));\n                        }\n                    }\n                    ti.flags &= base_info.flags;\n\n                    match member.binding {\n                        Some(ir::Binding::Location {\n                            location,\n                            blend_src: Some(blend_src),\n                            ..\n                        }) => {\n                            // `blend_src` is only valid if dual source blending was explicitly enabled,\n                            // see https://www.w3.org/TR/WGSL/#extension-dual_source_blending\n                            if !self\n                                .capabilities\n                                .contains(Capabilities::DUAL_SOURCE_BLENDING)\n                            {\n                                return Err(TypeError::MissingCapability(\n                                    Capabilities::DUAL_SOURCE_BLENDING,\n                                ));\n                            }\n                            if !(location == 0 && (blend_src == 0 || blend_src == 1)) {\n                                return Err(TypeError::InvalidBlendSrc(\n                                    super::VaryingError::InvalidBlendSrcIndex {\n                                        location,\n                                        blend_src,\n                                    },\n                                ));\n                            }\n                            if blend_src_types[blend_src as usize]\n                                .replace(member.ty)\n                                .is_some()\n                            {\n                                // @blend_src(i) appeared multiple times\n                                return Err(TypeError::InvalidBlendSrc(\n                                    super::VaryingError::BindingCollisionBlendSrc { blend_src },\n                                ));\n                            }\n                        }\n                        Some(ir::Binding::Location {\n                            location,\n                            blend_src: None,\n                            ..\n                        }) => non_blend_src_location = Some(location),\n                        _ => {}\n                    }\n\n                    if member.offset < min_offset {\n                        // HACK: this could be nicer. We want to allow some structures\n                        // to not bother with offsets/alignments if they are never\n                        // used for host sharing.\n                        if member.offset == 0 {\n                            ti.flags.set(TypeFlags::HOST_SHAREABLE, false);\n                        } else {\n                            return Err(TypeError::MemberOverlap {\n                                index: i as u32,\n                                offset: member.offset,\n                            });\n                        }\n                    }\n\n                    let base_size = gctx.types[member.ty].inner.size(gctx);\n                    min_offset = member.offset + base_size;\n                    if min_offset > span {\n                        return Err(TypeError::MemberOutOfBounds {\n                            index: i as u32,\n                            offset: member.offset,\n                            size: base_size,\n                            span,\n                        });\n                    }\n\n                    check_member_layout(\n                        &mut ti.uniform_layout,\n                        member,\n                        i as u32,\n                        base_info.uniform_layout,\n                        handle,\n                    );\n                    check_member_layout(\n                        &mut ti.storage_layout,\n                        member,\n                        i as u32,\n                        base_info.storage_layout,\n                        handle,\n                    );\n                    if base_info.immediates_compatibility.is_err() {\n                        ti.immediates_compatibility = base_info.immediates_compatibility.clone();\n                    }\n\n                    // Validate rule: If a structure member itself has a structure type S,\n                    // then the number of bytes between the start of that member and\n                    // the start of any following member must be at least roundUp(16, SizeOf(S)).\n                    if let Some((span, offset)) = prev_struct_data {\n                        let diff = member.offset - offset;\n                        let min = Alignment::MIN_UNIFORM.round_up(span);\n                        if diff < min {\n                            ti.uniform_layout = Err((\n                                handle,\n                                Disalignment::MemberOffsetAfterStruct {\n                                    index: i as u32,\n                                    offset: member.offset,\n                                    expected: offset + min,\n                                },\n                            ));\n                        }\n                    };\n\n                    prev_struct_data = match gctx.types[member.ty].inner {\n                        crate::TypeInner::Struct { span, .. } => Some((span, member.offset)),\n                        _ => None,\n                    };\n\n                    // The last field may be an unsized array.\n                    if !base_info.flags.contains(TypeFlags::SIZED) {\n                        let is_array = match gctx.types[member.ty].inner {\n                            crate::TypeInner::Array { .. } => true,\n                            _ => false,\n                        };\n                        if !is_array || i + 1 != members.len() {\n                            let name = member.name.clone().unwrap_or_default();\n                            return Err(TypeError::InvalidDynamicArray(name, member.ty));\n                        }\n                        if ti.uniform_layout.is_ok() {\n                            ti.uniform_layout =\n                                Err((handle, Disalignment::UnsizedMember { index: i as u32 }));\n                        }\n                    }\n                }\n\n                match blend_src_types {\n                    [None, None] => {}\n                    [Some(ty0), Some(ty1)] => {\n                        if let Some(location) = non_blend_src_location {\n                            // If `@blend_src` members are present, then `@location`\n                            // may only be used for those members.\n                            return Err(TypeError::InvalidBlendSrc(\n                                super::VaryingError::InvalidBlendSrcWithOtherBindings { location },\n                            ));\n                        }\n                        let ty0_inner = &gctx.types[ty0].inner;\n                        let ty1_inner = &gctx.types[ty1].inner;\n                        // The two blend sources must have the same type...\n                        if !ty0_inner.non_struct_equivalent(ty1_inner, gctx.types) {\n                            return Err(TypeError::InvalidBlendSrc(\n                                super::VaryingError::BlendSrcOutputTypeMismatch {\n                                    blend_src_0_type: ty0,\n                                    blend_src_1_type: ty1,\n                                },\n                            ));\n                        }\n                        // ... and that type must be I/O-shareable.\n                        if !self.types[ty0.index()]\n                            .flags\n                            .contains(TypeFlags::IO_SHAREABLE)\n                        {\n                            return Err(TypeError::InvalidBlendSrc(\n                                super::VaryingError::NotIOShareableType(ty0),\n                            ));\n                        }\n\n                        // `@blend_src` is the only case where we classify a struct as\n                        // I/O-shareable. (In the case of a struct with `@location` bindings, we\n                        // process the members individually in interface validation, and do not\n                        // classify the struct as I/O-shareable.)\n                        ti.flags |= TypeFlags::IO_SHAREABLE;\n                    }\n                    [None, Some(_)] | [Some(_), None] => {\n                        // Only one of the blend sources was specified.\n                        return Err(TypeError::InvalidBlendSrc(\n                            super::VaryingError::IncompleteBlendSrcUsage {\n                                present_blend_src: blend_src_types\n                                    .iter()\n                                    .position(|src| src.is_some())\n                                    .unwrap()\n                                    as u32,\n                            },\n                        ));\n                    }\n                }\n\n                let alignment = self.layouter[handle].alignment;\n                if !alignment.is_aligned(span) {\n                    ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));\n                    ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));\n                }\n\n                ti\n            }\n            Ti::Image {\n                dim,\n                arrayed,\n                class,\n            } => {\n                if arrayed && matches!(dim, crate::ImageDimension::D3) {\n                    return Err(TypeError::UnsupportedImageType {\n                        dim,\n                        arrayed,\n                        class,\n                    });\n                }\n                if arrayed && matches!(dim, crate::ImageDimension::Cube) {\n                    self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;\n                }\n                if matches!(class, crate::ImageClass::External) {\n                    if dim != crate::ImageDimension::D2 || arrayed {\n                        return Err(TypeError::UnsupportedImageType {\n                            dim,\n                            arrayed,\n                            class,\n                        });\n                    }\n                    self.require_type_capability(Capabilities::TEXTURE_EXTERNAL)?;\n                }\n                TypeInfo::new(\n                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                )\n            }\n            Ti::Sampler { .. } => TypeInfo::new(\n                TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,\n                Alignment::ONE,\n            ),\n            Ti::AccelerationStructure { vertex_return } => {\n                self.require_type_capability(Capabilities::RAY_TRACING_PIPELINE)\n                    .or_else(|_| self.require_type_capability(Capabilities::RAY_QUERY))?;\n                if vertex_return {\n                    self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;\n                }\n                TypeInfo::new(\n                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                )\n            }\n            Ti::RayQuery { vertex_return } => {\n                self.require_type_capability(Capabilities::RAY_QUERY)?;\n                if vertex_return {\n                    self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;\n                }\n                TypeInfo::new(\n                    TypeFlags::DATA\n                        | TypeFlags::CONSTRUCTIBLE\n                        | TypeFlags::SIZED\n                        | TypeFlags::CREATION_RESOLVED,\n                    Alignment::ONE,\n                )\n            }\n            Ti::BindingArray { base, size } => {\n                let type_info_mask = match size {\n                    crate::ArraySize::Constant(_) => {\n                        TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED\n                    }\n                    crate::ArraySize::Pending(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,\n                    crate::ArraySize::Dynamic => {\n                        // Final type is non-sized\n                        TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED\n                    }\n                };\n                let base_info = &self.types[base.index()];\n\n                if base_info.flags.contains(TypeFlags::DATA) {\n                    // Currently Naga only supports binding arrays of structs for non-handle types.\n                    // `validate_global_var` relies on ray queries (which are `DATA`) being rejected here\n                    match gctx.types[base].inner {\n                        crate::TypeInner::Struct { .. } => {}\n                        _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),\n                    };\n                }\n                if matches!(\n                    gctx.types[base].inner,\n                    crate::TypeInner::Image {\n                        class: crate::ImageClass::External,\n                        ..\n                    }\n                ) {\n                    // Binding arrays of external textures are not yet supported.\n                    // See <https://github.com/gfx-rs/wgpu/issues/8027>. Note that\n                    // `validate_global_var` relies on this error being raised here.\n                    return Err(TypeError::BindingArrayBaseExternalTextures);\n                }\n\n                if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) {\n                    return Err(TypeError::InvalidData(base));\n                }\n\n                TypeInfo::new(base_info.flags & type_info_mask, Alignment::ONE)\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/210-bevy-2d-shader.frag",
    "content": "// AUTHOR: mrk-its\n// ISSUE: #210\n// FIX: #898\n#version 450\n\nlayout(location = 0) in vec2 v_Uv;\n\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform ColorMaterial_color {\n    vec4 Color;\n};\n\n# ifdef COLORMATERIAL_TEXTURE\nlayout(set = 1, binding = 1) uniform texture2D ColorMaterial_texture;\nlayout(set = 1, binding = 2) uniform sampler ColorMaterial_texture_sampler;\n# endif\n\nvoid main() {\n    vec4 color = Color;\n# ifdef COLORMATERIAL_TEXTURE\n    color *= texture(\n        sampler2D(ColorMaterial_texture, ColorMaterial_texture_sampler),\n        v_Uv);\n# endif\n    o_Target = color;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/210-bevy-2d-shader.vert",
    "content": "// AUTHOR: mrk-its\n// ISSUE: #210\n// FIX: #898\n#version 450\n\nlayout(location = 0) in vec3 Vertex_Position;\nlayout(location = 1) in vec3 Vertex_Normal;\nlayout(location = 2) in vec2 Vertex_Uv;\n\nlayout(location = 0) out vec2 v_Uv;\n\nlayout(set = 0, binding = 0) uniform Camera {\n    mat4 ViewProj;\n};\n\nlayout(set = 2, binding = 0) uniform Transform {\n    mat4 Model;\n};\nlayout(set = 2, binding = 1) uniform Sprite_size {\n    vec2 size;\n};\n\nvoid main() {\n    v_Uv = Vertex_Uv;\n    vec3 position = Vertex_Position * vec3(size, 1.0);\n    gl_Position = ViewProj * Model * vec4(position, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/210-bevy-shader.vert",
    "content": "// AUTHOR: enfipy\n// ISSUE: #210\n// FIX: #898\n#version 450\n\nlayout(location = 0) in vec3 Vertex_Position;\nlayout(location = 1) in vec3 Vertex_Normal;\nlayout(location = 2) in vec2 Vertex_Uv;\n\nlayout(location = 0) out vec3 v_Position;\nlayout(location = 1) out vec3 v_Normal;\nlayout(location = 2) out vec2 v_Uv;\n\nlayout(set = 0, binding = 0) uniform Camera {\n    mat4 ViewProj;\n};\n\nlayout(set = 2, binding = 0) uniform Transform {\n    mat4 Model;\n};\n\nvoid main() {\n    v_Normal = (Model * vec4(Vertex_Normal, 1.0)).xyz;\n    v_Normal = mat3(Model) * Vertex_Normal;\n    v_Position = (Model * vec4(Vertex_Position, 1.0)).xyz;\n    v_Uv = Vertex_Uv;\n    gl_Position = ViewProj * vec4(v_Position, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/246-collatz.comp",
    "content": "// AUTHOR: Unknown\n// ISSUE: #246\n// NOTE: Taken from the wgpu repo\n#version 450\nlayout(local_size_x = 1) in;\n\nlayout(set = 0, binding = 0) buffer PrimeIndices {\n    uint[] indices;\n}; // this is used as both input and output for convenience\n\n// The Collatz Conjecture states that for any integer n:\n// If n is even, n = n/2\n// If n is odd, n = 3n+1\n// And repeat this process for each new n, you will always eventually reach 1.\n// Though the conjecture has not been proven, no counterexample has ever been found.\n// This function returns how many times this recurrence needs to be applied to reach 1.\nuint collatz_iterations(uint n) {\n    uint i = 0;\n    while(n != 1) {\n        if (mod(n, 2) == 0) {\n            n = n / 2;\n        }\n        else {\n            n = (3 * n) + 1;\n        }\n        i++;\n    }\n    return i;\n}\n\nvoid main() {\n    uint index = gl_GlobalInvocationID.x;\n    indices[index] = collatz_iterations(indices[index]);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/277-casting.frag",
    "content": "// AUTHOR: Napokue\n// ISSUE: #277\n// FIX: #278\n#version 450\n\nvoid main() {\n    float a = float(1);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/280-matrix-cast.frag",
    "content": "// AUTHOR: pjoe\n// ISSUE: #280\n// FIX: #898\n#version 450\n\nvoid main() {\n    mat4 a = mat4(1);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/484-preprocessor-if.frag",
    "content": "// AUTHOR: fintelia\n// ISSUE: #484\n// FIX: https://github.com/Kangz/glslpp-rs/pull/30\n// NOTE: Shader altered to use correct syntax\n#version 450 core\n\n#if 0\n#endif\n\nvoid main() { }\n"
  },
  {
    "path": "naga/tests/in/glsl/5246-dual-iteration.frag",
    "content": "// AUTHOR: cwfitzgerald\n// ISSUE: #5246\n\nvoid main() {\n    for (int x = 0; x < 10; x++) {\n        for (int y = 0; y < 10; y++) {\n            for (int z = 0; z < 10; z++) {\n                ;\n            }\n        }\n    }\n}"
  },
  {
    "path": "naga/tests/in/glsl/800-out-of-bounds-panic.toml",
    "content": "capabilities = \"IMMEDIATES\"\n"
  },
  {
    "path": "naga/tests/in/glsl/800-out-of-bounds-panic.vert",
    "content": "// AUTHOR: Herschel\n// ISSUE: #800\n// FIX: #901\n#version 450\n\n// Set 0: globals\nlayout(set = 0, binding = 0) uniform Globals {\n    mat4 view_matrix;\n};\n\n// Push constants: matrix + color\nlayout(push_constant) uniform VertexPushConstants {\n    mat4 world_matrix;\n};\n\nlayout(location = 0) in vec2 position;\nlayout(location = 1) in vec4 color;\n\nlayout(location = 0) out vec4 frag_color;\n\nvoid main() {\n    frag_color = color;\n    gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);\n    gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/896-push-constant.frag",
    "content": "// AUTHOR: Foltik\n// ISSUE: #896\n// FIX: #897\n#version 450\n\nlayout(push_constant) uniform PushConstants {\n    float example;\n} c;\n\nvoid main() {}\n"
  },
  {
    "path": "naga/tests/in/glsl/896-push-constant.toml",
    "content": "capabilities = \"IMMEDIATES\"\n"
  },
  {
    "path": "naga/tests/in/glsl/900-implicit-conversions.frag",
    "content": "// ISSUE: #900\n#version 450\n\n// Signature match call the second overload\nvoid exact(float a) {}\nvoid exact(int a) {}\n\n// No signature match but one overload satisfies the cast rules\nvoid implicit(float a) {}\nvoid implicit(int a) {}\n\n// All satisfy the kind condition but they have different dimensions\nvoid implicit_dims(float v) {  }\nvoid implicit_dims(vec2 v) {  }\nvoid implicit_dims(vec3 v) {  }\nvoid implicit_dims(vec4 v) {  }\n\nvoid main() {\n  exact(1);\n  implicit(1u);\n  implicit_dims(ivec3(1));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/901-lhs-field-select.frag",
    "content": "// AUTHOR: JCapucho\n// ISSUE: #901\n// FIX: #948\n#version 450\n\nvoid main() {\n    vec4 a = vec4(1.0);\n    a.x = 2.0;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/931-constant-emitting.frag",
    "content": "// AUTHOR: jakobhellermann\n// ISSUE: #931\n// FIX: #933\n#version 450\n\nconst int constant = 10;\n\nfloat function() {\n    return 0.0;\n}\n\nvoid main() {\n    function();\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/932-for-loop-if.frag",
    "content": "// AUTHOR: jakobhellermann\n// ISSUE: #932\n// FIX: #935\n#version 450\n\nvoid main() {\n    for (int i = 0; i < 1; i += 1) {}\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/anonymous-entry-point-type.frag",
    "content": "layout(location = 0) out vec4 o_Target;\n\nstruct FragmentOutput {\n  float x;\n};\n\nvoid main() {\n    o_Target = vec4(0.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/bevy-pbr.frag",
    "content": "// MIT License\n//\n// Copyright (c) 2020 Carter Anderson\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n// NOTE: Taken from the bevy repo\n#version 450\n\nconst int MAX_POINT_LIGHTS = 10;\nconst int MAX_DIRECTIONAL_LIGHTS = 1;\n\nstruct PointLight {\n    vec4 pos;\n    vec4 color;\n    vec4 lightParams;\n};\n \nstruct DirectionalLight {\n    vec4 direction;\n    vec4 color;\n};\n\nlayout(location = 0) in vec3 v_WorldPosition;\nlayout(location = 1) in vec3 v_WorldNormal;\nlayout(location = 2) in vec2 v_Uv;\nlayout(location = 3) in vec4 v_WorldTangent;\n\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 0, binding = 0) uniform CameraViewProj {\n    mat4 ViewProj;\n};\nlayout(std140, set = 0, binding = 1) uniform CameraPosition {\n    vec4 CameraPos;\n};\n\nlayout(std140, set = 1, binding = 0) uniform Lights {\n    vec4 AmbientColor;\n    uvec4 NumLights; // x = point lights, y = directional lights\n    PointLight PointLights[MAX_POINT_LIGHTS];\n    DirectionalLight DirectionalLights[MAX_DIRECTIONAL_LIGHTS];\n};\n\nlayout(set = 3, binding = 0) uniform StandardMaterial_base_color {\n    vec4 base_color;\n};\n\nlayout(set = 3, binding = 1) uniform texture2D StandardMaterial_base_color_texture;\nlayout(set = 3,\n       binding = 2) uniform sampler StandardMaterial_base_color_texture_sampler;\n\nlayout(set = 3, binding = 3) uniform StandardMaterial_roughness {\n    float perceptual_roughness;\n};\n\nlayout(set = 3, binding = 4) uniform StandardMaterial_metallic {\n    float metallic;\n};\n\nlayout(set = 3, binding = 5) uniform texture2D StandardMaterial_metallic_roughness_texture;\nlayout(set = 3,\n       binding = 6) uniform sampler StandardMaterial_metallic_roughness_texture_sampler;\n\nlayout(set = 3, binding = 7) uniform StandardMaterial_reflectance {\n    float reflectance;\n};\n\nlayout(set = 3, binding = 8) uniform texture2D StandardMaterial_normal_map;\nlayout(set = 3,\n       binding = 9) uniform sampler StandardMaterial_normal_map_sampler;\n\nlayout(set = 3, binding = 10) uniform texture2D StandardMaterial_occlusion_texture;\nlayout(set = 3,\n       binding = 11) uniform sampler StandardMaterial_occlusion_texture_sampler;\n\nlayout(set = 3, binding = 12) uniform StandardMaterial_emissive {\n    vec4 emissive;\n};\n\nlayout(set = 3, binding = 13) uniform texture2D StandardMaterial_emissive_texture;\nlayout(set = 3,\n       binding = 14) uniform sampler StandardMaterial_emissive_texture_sampler;\n\n#    define saturate(x) clamp(x, 0.0, 1.0)\nconst float PI = 3.141592653589793;\n\nfloat pow5(float x) {\n    float x2 = x * x;\n    return x2 * x2 * x;\n}\n\n// distanceAttenuation is simply the square falloff of light intensity\n// combined with a smooth attenuation at the edge of the light radius\n//\n// light radius is a non-physical construct for efficiency purposes,\n// because otherwise every light affects every fragment in the scene\nfloat getDistanceAttenuation(float distanceSquare, float inverseRangeSquared) {\n    float factor = distanceSquare * inverseRangeSquared;\n    float smoothFactor = saturate(1.0 - factor * factor);\n    float attenuation = smoothFactor * smoothFactor;\n    return attenuation * 1.0 / max(distanceSquare, 1e-3);\n}\n\n// Normal distribution function (specular D)\n// Based on https://google.github.io/filament/Filament.html#citation-walter07\n\n// D_GGX(h,α) = α^2 / { π ((n⋅h)^2 (α2−1) + 1)^2 }\n\n// Simple implementation, has precision problems when using fp16 instead of fp32\n// see https://google.github.io/filament/Filament.html#listing_speculardfp16\nfloat D_GGX(float roughness, float NoH, const vec3 h) {\n    float oneMinusNoHSquared = 1.0 - NoH * NoH;\n    float a = NoH * roughness;\n    float k = roughness / (oneMinusNoHSquared + a * a);\n    float d = k * k * (1.0 / PI);\n    return d;\n}\n\n// Visibility function (Specular G)\n// V(v,l,a) = G(v,l,α) / { 4 (n⋅v) (n⋅l) }\n// such that f_r becomes\n// f_r(v,l) = D(h,α) V(v,l,α) F(v,h,f0)\n// where\n// V(v,l,α) = 0.5 / { n⋅l sqrt((n⋅v)^2 (1−α2) + α2) + n⋅v sqrt((n⋅l)^2 (1−α2) + α2) }\n// Note the two sqrt's, that may be slow on mobile, see https://google.github.io/filament/Filament.html#listing_approximatedspecularv\nfloat V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {\n    float a2 = roughness * roughness;\n    float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);\n    float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);\n    float v = 0.5 / (lambdaV + lambdaL);\n    return v;\n}\n\n// Fresnel function\n// see https://google.github.io/filament/Filament.html#citation-schlick94\n// F_Schlick(v,h,f_0,f_90) = f_0 + (f_90 − f_0) (1 − v⋅h)^5\nvec3 F_Schlick(const vec3 f0, float f90, float VoH) {\n    // not using mix to keep the vec3 and float versions identical\n    return f0 + (vec3(f90) - f0) * pow5(1.0 - VoH);\n}\n\nfloat F_Schlick(float f0, float f90, float VoH) {\n    // not using mix to keep the vec3 and float versions identical\n    return f0 + (f90 - f0) * pow5(1.0 - VoH);\n}\n\nvec3 fresnel(vec3 f0, float LoH) {\n    // f_90 suitable for ambient occlusion\n    // see https://google.github.io/filament/Filament.html#lighting/occlusion\n    float f90 = saturate(dot(f0, vec3(50.0 * 0.33)));\n    return F_Schlick(f0, f90, LoH);\n}\n\n// Specular BRDF\n// https://google.github.io/filament/Filament.html#materialsystem/specularbrdf\n\n// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m\n// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) }\nvec3 specular(vec3 f0, float roughness, const vec3 h, float NoV, float NoL,\n              float NoH, float LoH, float specularIntensity) {\n    float D = D_GGX(roughness, NoH, h);\n    float V = V_SmithGGXCorrelated(roughness, NoV, NoL);\n    vec3 F = fresnel(f0, LoH);\n\n    return (specularIntensity * D * V) * F;\n}\n\n// Diffuse BRDF\n// https://google.github.io/filament/Filament.html#materialsystem/diffusebrdf\n// fd(v,l) = σ/π * 1 / { |n⋅v||n⋅l| } ∫Ω D(m,α) G(v,l,m) (v⋅m) (l⋅m) dm\n\n// simplest approximation\n// float Fd_Lambert() {\n//     return 1.0 / PI;\n// }\n//\n// vec3 Fd = diffuseColor * Fd_Lambert();\n\n// Disney approximation\n// See https://google.github.io/filament/Filament.html#citation-burley12\n// minimal quality difference\nfloat Fd_Burley(float roughness, float NoV, float NoL, float LoH) {\n    float f90 = 0.5 + 2.0 * roughness * LoH * LoH;\n    float lightScatter = F_Schlick(1.0, f90, NoL);\n    float viewScatter = F_Schlick(1.0, f90, NoV);\n    return lightScatter * viewScatter * (1.0 / PI);\n}\n\n// From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile\nvec3 EnvBRDFApprox(vec3 f0, float perceptual_roughness, float NoV) {\n    const vec4 c0 = { -1.0, -0.0275, -0.572, 0.022 };\n    const vec4 c1 = { 1.0, 0.0425, 1.04, -0.04 };\n    vec4 r = vec4(perceptual_roughness) * c0 + c1;\n    float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;\n    vec2 AB = vec2(-1.04, 1.04) * vec2(a004) + r.zw;\n    return f0 * vec3(AB.x) + vec3(AB.y);\n}\n\nfloat perceptualRoughnessToRoughness(float perceptualRoughness) {\n    // clamp perceptual roughness to prevent precision problems\n    // According to Filament design 0.089 is recommended for mobile\n    // Filament uses 0.045 for non-mobile\n    float clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0);\n    return clampedPerceptualRoughness * clampedPerceptualRoughness;\n}\n\n// from https://64.github.io/tonemapping/\n// reinhard on RGB oversaturates colors\nvec3 reinhard(vec3 color) {\n    return color / (vec3(1.0) + color);\n}\n\nvec3 reinhard_extended(vec3 color, float max_white) {\n    vec3 numerator = color * (vec3(1.0) + (color / vec3(max_white * max_white)));\n    return numerator / (vec3(1.0) + color);\n}\n\n// luminance coefficients from Rec. 709.\n// https://en.wikipedia.org/wiki/Rec._709\nfloat luminance(vec3 v) {\n    return dot(v, vec3(0.2126, 0.7152, 0.0722));\n}\n\nvec3 change_luminance(vec3 c_in, float l_out) {\n    float l_in = luminance(c_in);\n    return c_in * (l_out / l_in);\n}\n\nvec3 reinhard_luminance(vec3 color) {\n    float l_old = luminance(color);\n    float l_new = l_old / (1.0f + l_old);\n    return change_luminance(color, l_new);\n}\n\nvec3 reinhard_extended_luminance(vec3 color, float max_white_l) {\n    float l_old = luminance(color);\n    float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l)));\n    float l_new = numerator / (1.0f + l_old);\n    return change_luminance(color, l_new);\n}\n\nvec3 point_light(PointLight light, float roughness, float NdotV, vec3 N, vec3 V, vec3 R, vec3 F0, vec3 diffuseColor) {\n    vec3 light_to_frag = light.pos.xyz - v_WorldPosition.xyz;\n    float distance_square = dot(light_to_frag, light_to_frag);\n    float rangeAttenuation =\n        getDistanceAttenuation(distance_square, light.lightParams.r);\n\n    // Specular.\n    // Representative Point Area Lights.\n    // see http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16\n    float a = roughness;\n    float radius = light.lightParams.g;\n    vec3 centerToRay = dot(light_to_frag, R) * R - light_to_frag;\n    vec3 closestPoint = light_to_frag + centerToRay * saturate(radius * inversesqrt(dot(centerToRay, centerToRay)));\n    float LspecLengthInverse = inversesqrt(dot(closestPoint, closestPoint));\n    float normalizationFactor = a / saturate(a + (radius * 0.5 * LspecLengthInverse));\n    float specularIntensity = normalizationFactor * normalizationFactor;\n\n    vec3 L = closestPoint * LspecLengthInverse; // normalize() equivalent?\n    vec3 H = normalize(L + V);\n    float NoL = saturate(dot(N, L));\n    float NoH = saturate(dot(N, H));\n    float LoH = saturate(dot(L, H));\n\n    vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH, specularIntensity);\n\n    // Diffuse.\n    // Comes after specular since its NoL is used in the lighting equation.\n    L = normalize(light_to_frag);\n    H = normalize(L + V);\n    NoL = saturate(dot(N, L));\n    NoH = saturate(dot(N, H));\n    LoH = saturate(dot(L, H));\n\n    vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH);\n\n    // Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩\n    // where\n    // f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color\n    // Φ is light intensity\n\n    // our rangeAttenuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius\n    // It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out\n\n    // See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation\n    // TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance\n    // light.color.rgb is premultiplied with light.intensity on the CPU\n    return ((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL);\n}\n\nvec3 dir_light(DirectionalLight light, float roughness, float NdotV, vec3 normal, vec3 view, vec3 R, vec3 F0, vec3 diffuseColor) {\n    vec3 incident_light = light.direction.xyz;\n\n    vec3 half_vector = normalize(incident_light + view);\n    float NoL = saturate(dot(normal, incident_light));\n    float NoH = saturate(dot(normal, half_vector));\n    float LoH = saturate(dot(incident_light, half_vector));\n\n    vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH);\n    float specularIntensity = 1.0;\n    vec3 specular = specular(F0, roughness, half_vector, NdotV, NoL, NoH, LoH, specularIntensity);\n\n    return (specular + diffuse) * light.color.rgb * NoL;\n}\n\nvoid main() {\n    vec4 output_color = base_color;\n    output_color *= texture(sampler2D(StandardMaterial_base_color_texture,\n                                      StandardMaterial_base_color_texture_sampler),\n                            v_Uv);\n\n    // calculate non-linear roughness from linear perceptualRoughness\n    vec4 metallic_roughness = texture(sampler2D(StandardMaterial_metallic_roughness_texture, StandardMaterial_metallic_roughness_texture_sampler), v_Uv);\n    // Sampling from GLTF standard channels for now\n    float metallic = metallic * metallic_roughness.b;\n    float perceptual_roughness = perceptual_roughness * metallic_roughness.g;\n\n    float roughness = perceptualRoughnessToRoughness(perceptual_roughness);\n\n    vec3 N = normalize(v_WorldNormal);\n\n    vec3 T = normalize(v_WorldTangent.xyz);\n    vec3 B = cross(N, T) * v_WorldTangent.w;\n\n    N = gl_FrontFacing ? N : -N;\n    T = gl_FrontFacing ? T : -T;\n    B = gl_FrontFacing ? B : -B;\n\n    mat3 TBN = mat3(T, B, N);\n    N = TBN * normalize(texture(sampler2D(StandardMaterial_normal_map, StandardMaterial_normal_map_sampler), v_Uv).rgb * 2.0 - vec3(1.0));\n\n    float occlusion = texture(sampler2D(StandardMaterial_occlusion_texture, StandardMaterial_occlusion_texture_sampler), v_Uv).r;\n\n    vec4 emissive = emissive;\n    // TODO use .a for exposure compensation in HDR\n    emissive.rgb *= texture(sampler2D(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler), v_Uv).rgb;\n    vec3 V = normalize(CameraPos.xyz - v_WorldPosition.xyz);\n    // Neubelt and Pettineo 2013, \"Crafting a Next-gen Material Pipeline for The Order: 1886\"\n    float NdotV = max(dot(N, V), 1e-3);\n\n    // Remapping [0,1] reflectance to F0\n    // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping\n    vec3 F0 = vec3(0.16 * reflectance * reflectance * (1.0 - metallic)) + output_color.rgb * vec3(metallic);\n\n    // Diffuse strength inversely related to metallicity\n    vec3 diffuseColor = output_color.rgb * vec3(1.0 - metallic);\n\n    vec3 R = reflect(-V, N);\n\n    // accumulate color\n    vec3 light_accum = vec3(0.0);\n    for (int i = 0; i < int(NumLights.x) && i < MAX_POINT_LIGHTS; ++i) {\n        light_accum += point_light(PointLights[i], roughness, NdotV, N, V, R, F0, diffuseColor);\n    }\n    for (int i = 0; i < int(NumLights.y) && i < MAX_DIRECTIONAL_LIGHTS; ++i) {\n        light_accum += dir_light(DirectionalLights[i], roughness, NdotV, N, V, R, F0, diffuseColor);\n    }\n\n    vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV);\n    vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV);\n\n    output_color.rgb = light_accum;\n    output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor.xyz * occlusion;\n    output_color.rgb += emissive.rgb * output_color.a;\n\n    // tone_mapping\n    output_color.rgb = reinhard_luminance(output_color.rgb);\n    // Gamma correction.\n    // Not needed with sRGB buffer\n    // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2));\n\n    o_Target = output_color;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/bevy-pbr.vert",
    "content": "// MIT License\n//\n// Copyright (c) 2020 Carter Anderson\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n// NOTE: Taken from the bevy repo\n#version 450\n\nlayout(location = 0) in vec3 Vertex_Position;\nlayout(location = 1) in vec3 Vertex_Normal;\nlayout(location = 2) in vec2 Vertex_Uv;\n\nlayout(location = 3) in vec4 Vertex_Tangent;\n\nlayout(location = 0) out vec3 v_WorldPosition;\nlayout(location = 1) out vec3 v_WorldNormal;\nlayout(location = 2) out vec2 v_Uv;\n\nlayout(set = 0, binding = 0) uniform CameraViewProj {\n    mat4 ViewProj;\n};\n\nlayout(location = 3) out vec4 v_WorldTangent;\n\nlayout(set = 2, binding = 0) uniform Transform {\n    mat4 Model;\n};\n\nvoid main() {\n    vec4 world_position = Model * vec4(Vertex_Position, 1.0);\n    v_WorldPosition = world_position.xyz;\n    v_WorldNormal = mat3(Model) * Vertex_Normal;\n    v_Uv = Vertex_Uv;\n    v_WorldTangent = vec4(mat3(Model) * Vertex_Tangent.xyz, Vertex_Tangent.w);\n    gl_Position = ViewProj * world_position;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/bits.frag",
    "content": "#version 450\n\nvoid main() {\n    int i = 0;\n    ivec2 i2 = ivec2(0);\n    ivec3 i3 = ivec3(0);\n    ivec4 i4 = ivec4(0);\n    uint u = 0;\n    uvec2 u2 = uvec2(0);\n    uvec3 u3 = uvec3(0);\n    uvec4 u4 = uvec4(0);\n    vec2 f2 = vec2(0.0);\n    vec4 f4 = vec4(0.0);\n    u = packSnorm4x8(f4);\n    u = packUnorm4x8(f4);\n    u = packSnorm2x16(f2);\n    u = packUnorm2x16(f2);\n    u = packHalf2x16(f2);\n    f4 = unpackSnorm4x8(u);\n    f4 = unpackUnorm4x8(u);\n    f2 = unpackSnorm2x16(u);\n    f2 = unpackUnorm2x16(u);\n    f2 = unpackHalf2x16(u);\n    i = bitfieldInsert(i, i, 5, 10);\n    i2 = bitfieldInsert(i2, i2, 5, 10);\n    i3 = bitfieldInsert(i3, i3, 5, 10);\n    i4 = bitfieldInsert(i4, i4, 5, 10);\n    u = bitfieldInsert(u, u, 5, 10);\n    u2 = bitfieldInsert(u2, u2, 5, 10);\n    u3 = bitfieldInsert(u3, u3, 5, 10);\n    u4 = bitfieldInsert(u4, u4, 5, 10);\n    i = bitfieldExtract(i, 5, 10);\n    i2 = bitfieldExtract(i2, 5, 10);\n    i3 = bitfieldExtract(i3, 5, 10);\n    i4 = bitfieldExtract(i4, 5, 10);\n    u = bitfieldExtract(u, 5, 10);\n    u2 = bitfieldExtract(u2, 5, 10);\n    u3 = bitfieldExtract(u3, 5, 10);\n    u4 = bitfieldExtract(u4, 5, 10);\n    i = findLSB(i);\n    i2 = findLSB(i2);\n    i3 = findLSB(i3);\n    i4 = findLSB(i4);\n    i = findLSB(u);\n    i2 = findLSB(u2);\n    i3 = findLSB(u3);\n    i4 = findLSB(u4);\n    i = findMSB(i);\n    i2 = findMSB(i2);\n    i3 = findMSB(i3);\n    i4 = findMSB(i4);\n    i = findMSB(u);\n    i2 = findMSB(u2);\n    i3 = findMSB(u3);\n    i4 = findMSB(u4);\n}"
  },
  {
    "path": "naga/tests/in/glsl/bits.toml",
    "content": "capabilities = \"SHADER_FLOAT16_IN_FLOAT32\"\n"
  },
  {
    "path": "naga/tests/in/glsl/bool-select.frag",
    "content": "#version 440 core\nprecision highp float;\n\nlayout(location = 0) out vec4 o_color;\n\nfloat TevPerCompGT(float a, float b) {\n    return float(a > b);\n}\n\nvec3 TevPerCompGT(vec3 a, vec3 b) {\n    return vec3(greaterThan(a, b));\n}\n\nvoid main() {\n    o_color.rgb = TevPerCompGT(vec3(3.0), vec3(5.0));\n    o_color.a = TevPerCompGT(3.0, 5.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/buffer.frag",
    "content": "#version 450\n\nlayout(set = 0, binding = 0) buffer testBufferBlock {\n    uint[] data;\n} testBuffer;\n\nlayout(set = 0, binding = 2) readonly buffer testBufferReadOnlyBlock {\n    uint[] data;\n} testBufferReadOnly;\n\nvoid main() {\n    uint a = testBuffer.data[0];\n    testBuffer.data[1] = 2;\n\n    uint b = testBufferReadOnly.data[0];\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/clamp-splat.vert",
    "content": "#version 450\nlayout(location = 0) in vec2 a_pos;\n\nvoid main() {\n  gl_Position = vec4(clamp(a_pos, 0.0, 1.0), 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/const-global-swizzle.frag",
    "content": "// ISSUE: #4773\n#version 450\n\n#define MIX2(c) c.xy\n\nlayout(location = 0) in vec2 v_Uv;\n\nlayout(location = 0) out vec4 o_Target;\n\nconst vec2 blank = MIX2(vec2(0.0, 1.0));\n\nvoid main() {\n    vec2 col = MIX2(v_Uv) * blank;\n    o_Target = vec4(col, 0.0, 1.0);\n}"
  },
  {
    "path": "naga/tests/in/glsl/constant-array-size.frag",
    "content": "#version 450\n\nconst int NUM_VECS = 42;\nlayout(std140, set = 1, binding = 0) uniform Data {\n    vec4 vecs[NUM_VECS];\n};\n\nvec4 function() {\n    vec4 sum = vec4(0);\n    for (int i = 0; i < NUM_VECS; i++) {\n        sum += vecs[i];\n    }\n    return sum;\n}\n\nvoid main() {\n    function();\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/declarations.frag",
    "content": "#version 450\n\nlayout(location = 0) in VertexData {\n    vec2 position;\n    vec2 a;\n} vert;\n\nlayout(location = 0) out FragmentData {\n    vec2 position;\n    vec2 a;\n} frag;\n\nlayout(location = 2) in  vec4  in_array[2];\nlayout(location = 2) out vec4 out_array[2];\n\nstruct TestStruct {\n    float a;\n    float b;\n};\n\nfloat array_2d[2][2];\nfloat array_toomanyd[2][2][2][2][2][2][2];\n\nstruct LightScatteringParams {\n    float BetaRay, BetaMie[3], HGg, DistanceMul[4], BlendCoeff;\n    vec3 SunDirection, SunColor;\n};\n\nvoid main() {\n    const vec3 positions[2] = vec3[2](\n        vec3(-1.0, 1.0, 0.0),\n        vec3(-1.0, -1.0, 0.0)\n    );\n    const TestStruct strct = TestStruct( 1, 2 );\n    const vec4 from_input_array = in_array[1];\n    const float a = array_2d[0][0];\n    const float b = array_toomanyd[0][0][0][0][0][0][0];\n    const LightScatteringParams light_scattering_params;\n    out_array[0] = vec4(2.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/double-math-functions.frag",
    "content": "#version 450\n\nvoid main() {\n    dvec4 a = dvec4(1.0);\n    dvec4 b = dvec4(2.0);\n    dmat4 m = dmat4(a, b, a, b);\n    int i = 5;\n\n    dvec4 ceilOut = ceil(a);\n    dvec4 roundOut = round(a);\n    dvec4 floorOut = floor(a);\n    dvec4 fractOut = fract(a);\n    dvec4 truncOut = trunc(a);\n    dvec4 absOut = abs(a);\n    dvec4 sqrtOut = sqrt(a);\n    dvec4 inversesqrtOut = inversesqrt(a);\n    dvec4 signOut = sign(a);\n    dmat4 transposeOut = transpose(m);\n    dvec4 normalizeOut = normalize(a);\n    double lengthOut = length(a);\n    double determinantOut = determinant(m);\n    double modOut = mod(a.x, b.x);\n    double dotOut = dot(a, b);\n    dvec4 maxOut = max(a, b);\n    dvec4 minOut = min(a, b);\n    dvec4 reflectOut = reflect(a, b);\n    dvec3 crossOut = cross(a.xyz, b.xyz);\n    double distanceOut = distance(a, b);\n    dvec4 stepOut = step(a, b);\n    double ldexpOut = ldexp(a.x, i);\n    double smoothStepScalar = smoothstep(0.0, 1.0, 0.5);\n    dvec4 smoothStepVector = smoothstep(dvec4(0.0), dvec4(1.0), dvec4(0.5));\n    dvec4 smoothStepMixed = smoothstep(0.0, 1.0, dvec4(0.5));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/double-math-functions.toml",
    "content": "capabilities = \"FLOAT64\"\n"
  },
  {
    "path": "naga/tests/in/glsl/dual-source-blending.frag",
    "content": "#version 450\n\nlayout(location = 0, index = 0) out vec4 output0;\nlayout(location = 0, index = 1) out vec4 output1;\n\nvoid main() {\n  output0 = vec4(1.0, 0.0, 1.0, 0.0);\n  output1 = vec4(0.0, 1.0, 0.0, 1.0);\n}"
  },
  {
    "path": "naga/tests/in/glsl/dual-source-blending.toml",
    "content": "# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `DUAL_SOURCE_BLENDING` capability\ncapabilities = \"DUAL_SOURCE_BLENDING\"\n"
  },
  {
    "path": "naga/tests/in/glsl/expressions.frag",
    "content": "#version 440 core\n\nvoid testBinOpVecFloat(vec4 a, float b) {\n    vec4 v;\n    v = a * 2.0;\n    v = a / 2.0;\n    v = a + 2.0;\n    v = a - 2.0;\n}\n\nvoid testBinOpFloatVec(vec4 a, float b) {\n    vec4 v;\n    v = a * b;\n    v = a / b;\n    v = a + b;\n    v = a - b;\n}\n\nvoid testBinOpIVecInt(ivec4 a, int b) {\n    ivec4 v;\n    v = a * b;\n    v = a / b;\n    v = a + b;\n    v = a - b;\n    v = a & b;\n    v = a | b;\n    v = a ^ b;\n    v = a >> b;\n    v = a << b;\n}\n\nvoid testBinOpIntIVec(int a, ivec4 b) {\n    ivec4 v;\n    v = a * b;\n    v = a + b;\n    v = a - b;\n    v = a & b;\n    v = a | b;\n    v = a ^ b;\n}\n\nvoid testBinOpUVecUint(uvec4 a, uint b) {\n    uvec4 v;\n    v = a * b;\n    v = a / b;\n    v = a + b;\n    v = a - b;\n    v = a & b;\n    v = a | b;\n    v = a ^ b;\n    v = a >> b;\n    v = a << b;\n}\n\nvoid testBinOpUintUVec(uint a, uvec4 b) {\n    uvec4 v;\n    v = a * b;\n    v = a + b;\n    v = a - b;\n    v = a & b;\n    v = a | b;\n    v = a ^ b;\n}\n\nvoid testBinOpMatMat(mat3 a, mat3 b) {\n    mat3 v;\n    bool c;\n    v = a / b;\n    v = a * b;\n    v = a + b;\n    v = a - b;\n    c = a == b;\n    c = a != b;\n}\n\nvoid testBinOpMatFloat(float a, mat3 b) {\n    mat3 v;\n    v = a / b;\n    v = a * b;\n    v = a + b;\n    v = a - b;\n\n    v = b / a;\n    v = b * a;\n    v = b + a;\n    v = b - a;\n}\n\nvoid testUnaryOpMat(mat3 a) {\n    mat3 v;\n    v = -a;\n    v = --a;\n    v = a--;\n}\n\nvoid testStructConstructor() {\n    struct BST {\n        int data;\n    };\n\n    BST tree = BST(1);\n}\n\nvoid testNonScalarToScalarConstructor() {\n    float f = float(mat2(1.0));\n}\n\nvoid testArrayConstructor() {\n    float tree[1] = float[1](0.0);\n}\n\nvoid testFreestandingConstructor() {\n    vec4(1.0);\n}\n\nvoid testNonImplicitCastVectorCast() {\n    uint a = 1;\n    ivec4 b = ivec4(a);\n}\n\nfloat global;\nvoid privatePointer(inout float a) {}\n\nvoid ternary(bool a) {\n    uint b = a ? 0 : 1u;\n    uint c = a ? 0u : 1;\n\n    uint nested = a ? (a ? (a ? 2u : 3) : 4u) : 5;\n}\n\nvoid testMatrixMultiplication(mat4x3 a, mat4x4 b) {\n    mat4x3 c = a * b;\n}\n\nlayout(std430, binding = 0) buffer a_buf {\n    float a[];\n};\n\nvoid testLength() {\n    int len = a.length();\n}\n\nvoid testConstantLength(float a[4u]) {\n    int len = a.length();\n}\n\nstruct TestStruct { uvec4 array[2]; };\nconst TestStruct strct = { { uvec4(0), uvec4(1) } };\n\nvoid indexConstantNonConstantIndex(int i) {\n    const uvec4 a = strct.array[i];\n}\n\nvoid testSwizzleWrites(vec3 a) {\n    a.zxy.xy = vec2(3.0, 4.0);\n    a.rg *= 5.0;\n    a.zy++;\n}\n\nout vec4 o_color;\nvoid main() {\n    testBinOpVecFloat(vec4(0), 1.0);\n    testBinOpFloatVec(vec4(0), 1.0);\n    testBinOpIVecInt(ivec4(0), 1);\n    testBinOpIntIVec(1, ivec4(0));\n    testBinOpUVecUint(uvec4(0), 1);\n    testBinOpUintUVec(1, uvec4(0));\n    testBinOpMatMat(mat3(0), mat3(1));\n    testBinOpMatFloat(1, mat3(1));\n    testUnaryOpMat(mat3(1));\n    testStructConstructor();\n    testNonScalarToScalarConstructor();\n    testArrayConstructor();\n    testFreestandingConstructor();\n    testNonImplicitCastVectorCast();\n    privatePointer(global);\n    ternary(false);\n    testMatrixMultiplication(mat4x3(0), mat4x4(1));\n    testLength();\n    testConstantLength(float[4](0.0, 1.0, 2.0, 3.0));\n    indexConstantNonConstantIndex(1);\n    testSwizzleWrites(vec3(0));\n\n    o_color.rgba = vec4(1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/f16-glsl.comp",
    "content": "#version 460\n\n#extension GL_AMD_gpu_shader_half_float: enable \n\nlayout(set = 0, binding = 0) uniform A {\n    float16_t a_1;\n    f16vec2 a_vec2;\n    f16vec3 a_vec3;\n    f16vec4 a_vec4;\n    // So the rules here are particularly nasty for any f16 matries in uniform buffers\n    // as the stride is always rounded up to 16, meaning that _every_ f16 matrix in a uniform\n    // buffer is over-aligned to what naga-ir wants.\n    // \n    // This is https://github.com/gfx-rs/wgpu/issues/4375.\n\n    // f16mat2 a_mat2;\n    // f16mat2x3 a_mat2x3;\n    // f16mat2x4 a_mat2x4;\n    // f16mat3x2 a_mat3x2;\n    // f16mat3 a_mat3;\n    // f16mat3x4 a_mat3x4;\n    // f16mat4x2 a_mat4x2;\n    // f16mat4x3 a_mat4x3;\n    // f16mat4 a_mat4;\n};\n\nlayout(set = 0, binding = 1) buffer B {\n    float16_t b_1;\n    f16vec2 b_vec2;\n    f16vec3 b_vec3;\n    f16vec4 b_vec4;\n    f16mat2 b_mat2;\n    f16mat2x3 b_mat2x3;\n    f16mat2x4 b_mat2x4;\n    f16mat3x2 b_mat3x2;\n    f16mat3 b_mat3;\n    f16mat3x4 b_mat3x4;\n    f16mat4x2 b_mat4x2;\n    f16mat4x3 b_mat4x3;\n    f16mat4 b_mat4;\n};\n\nvoid main() {\n    b_1 = a_1;\n    b_vec2 = a_vec2;\n    b_vec3 = a_vec3;\n    b_vec4 = a_vec4;\n    // b_mat2 = a_mat2;\n    // b_mat2x3 = a_mat2x3;\n    // b_mat2x4 = a_mat2x4;\n    // b_mat3x2 = a_mat3x2;\n    // b_mat3 = a_mat3;\n    // b_mat3x4 = a_mat3x4;\n    // b_mat4x2 = a_mat4x2;\n    // b_mat4x3 = a_mat4x3;\n    // b_mat4 = a_mat4;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/f16-glsl.toml",
    "content": "capabilities = \"SHADER_FLOAT16\"\n"
  },
  {
    "path": "naga/tests/in/glsl/fma.frag",
    "content": "#version 440 core\n\nstruct Mat4x3 { vec4 mx; vec4 my; vec4 mz; };\nvoid Fma(inout Mat4x3 d, Mat4x3 m, float s) { d.mx += m.mx * s; d.my += m.my * s; d.mz += m.mz * s; }\n\nout vec4 o_color;\nvoid main() {\n    Mat4x3 m1 = {\n        vec4(0),\n        vec4(1),\n        vec4(2),\n    };\n    Mat4x3 m2 = {\n        vec4(0),\n        vec4(1),\n        vec4(2),\n    };\n\n    Fma(m1, m2, 2.0);\n    o_color.rgba = vec4(1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/functions_call.frag",
    "content": "#version 450\n\nvoid swizzleCallee(inout vec2 a) {}\n\nvoid swizzleCaller(vec3 a) {\n    swizzleCallee(a.xz);\n}\n\nvoid outImplicitCastCallee(out uint a) {}\n\nvoid outImplicitCastCaller(float a) {\n    outImplicitCastCallee(a);\n}\n\nvoid swizzleImplicitCastCallee(out uvec2 a) {}\n\nvoid swizzleImplicitCastCaller(vec3 a) {\n    swizzleImplicitCastCallee(a.xz);\n}\n\nvoid main() {\n    swizzleCaller(vec3(0));\n    uint a;\n    outImplicitCastCallee(a);\n    outImplicitCastCaller(1.0);\n    uvec2 b;\n    swizzleImplicitCastCallee(b);\n    swizzleImplicitCastCaller(vec3(0));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/global-constant-array.frag",
    "content": "#version 450 core\n\nuint i;\nconst float[2] array = { 1.0, 2.0 };\n\nvoid main() { array[i]; }\n"
  },
  {
    "path": "naga/tests/in/glsl/images.frag",
    "content": "#version 460 core\n\n// WGSL doesn't have 1D array textures.\n#define HAS_1D_ARRAY_TEXTURES 0\n\nlayout(rgba8, binding = 0) uniform image1D img1D;\nlayout(rgba8, binding = 1) uniform image2D img2D;\nlayout(rgba8, binding = 2) uniform image3D img3D;\n// layout(rgba8, binding = 3) uniform imageCube imgCube;\n#if HAS_1D_ARRAY_TEXTURES\nlayout(rgba8, binding = 4) uniform image1DArray img1DArray;\n#endif\nlayout(rgba8, binding = 5) uniform image2DArray img2DArray;\n// layout(rgba8, binding = 6) uniform imageCubeArray imgCubeArray;\n\nlayout(rgba8, binding = 7) readonly uniform image2D imgReadOnly;\nlayout(rgba8, binding = 8) writeonly uniform image2D imgWriteOnly;\nlayout(rgba8, binding = 9) writeonly readonly uniform image2D imgWriteReadOnly;\n\nvoid testImg1D(in int coord) {\n    int size = imageSize(img1D);\n    imageStore(img1D, coord, vec4(2));\n    vec4 c = imageLoad(img1D, coord);\n}\n\n#if HAS_1D_ARRAY_TEXTURES\nvoid testImg1DArray(in ivec2 coord) {\n    vec2 size = imageSize(img1DArray);\n    vec4 c = imageLoad(img1DArray, coord);\n    imageStore(img1DArray, coord, vec4(2));\n}\n#endif\n\nvoid testImg2D(in ivec2 coord) {\n    vec2 size = imageSize(img2D);\n    vec4 c = imageLoad(img2D, coord);\n    imageStore(img2D, coord, vec4(2));\n}\n\nvoid testImg2DArray(in ivec3 coord) {\n    vec3 size = imageSize(img2DArray);\n    vec4 c = imageLoad(img2DArray, coord);\n    imageStore(img2DArray, coord, vec4(2));\n}\n\nvoid testImg3D(in ivec3 coord) {\n    vec3 size = imageSize(img3D);\n    vec4 c = imageLoad(img3D, coord);\n    imageStore(img3D, coord, vec4(2));\n}\n\n// Naga doesn't support cube images and it's usefulness\n// is questionable, so they won't be supported for now\n// void testImgCube(in ivec3 coord) {\n//     vec2 size = imageSize(imgCube);\n//     vec4 c = imageLoad(imgCube, coord);\n//     imageStore(imgCube, coord, vec4(2));\n// }\n//\n// void testImgCubeArray(in ivec3 coord) {\n//    vec3 size = imageSize(imgCubeArray);\n//     vec4 c = imageLoad(imgCubeArray, coord);\n//     imageStore(imgCubeArray, coord, vec4(2));\n// }\n\nvoid testImgReadOnly(in ivec2 coord) {\n    vec2 size = imageSize(img2D);\n    vec4 c = imageLoad(imgReadOnly, coord);\n}\n\nvoid testImgWriteOnly(in ivec2 coord) {\n    vec2 size = imageSize(img2D);\n    imageStore(imgWriteOnly, coord, vec4(2));\n}\n\nvoid testImgWriteReadOnly(in ivec2 coord) {\n    vec2 size = imageSize(imgWriteReadOnly);\n}\n\nvoid main() {\n    testImg1D(1);\n#if HAS_1D_ARRAY_TEXTURES\n    testImg1DArray(ivec2(0));\n#endif\n    testImg2D(ivec2(0));\n    testImg2DArray(ivec3(0));\n    testImg3D(ivec3(0));\n    testImgReadOnly(ivec2(0));\n    testImgWriteOnly(ivec2(0));\n    testImgWriteReadOnly(ivec2(0));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/inverse-polyfill.frag",
    "content": "#version 450\n\nvoid main() {\n    vec4 a4 = vec4(1.0);\n    vec4 b4 = vec4(2.0);\n    mat4 m4 = mat4(a4, b4, a4, b4);\n\n    vec3 a3 = vec3(1.0);\n    vec3 b3 = vec3(2.0);\n    mat3 m3 = mat3(a3, b3, a3);\n\n    mat2 m2 = mat2(1.0, 2.0, 3.0, 4.0);\n\n    mat4 m4_inverse = inverse(m4);\n    mat3 m3_inverse = inverse(m3);\n    mat2 m2_inverse = inverse(m2);\n}"
  },
  {
    "path": "naga/tests/in/glsl/local-var-init-in-loop.comp",
    "content": "void main() {\n    vec4 sum = vec4(0);\n    for (int i = 0; i < 4; i++) {\n        vec4 a = vec4(1);\n        sum += a;\n    }\n}"
  },
  {
    "path": "naga/tests/in/glsl/long-form-matrix.frag",
    "content": "// ISSUE: #1064\n#version 450\n\nvoid main() {\n    // Sane ways to build a matrix\n    mat2 splat = mat2(1);\n    mat2 normal = mat2(vec2(1), vec2(2));\n    mat2x4 from_matrix = mat2x4(mat3(1.0));\n\n    // This is a little bit weirder but still makes some sense\n    // Since this matrix has 2 rows we take two numbers to make a column\n    // and we do this twice because we 2 columns.\n    // Final result in wgsl should be:\n    // mat2x2<f32>(vec2<f32>(1.0, 2.0), vec2<f32>(3.0, 4.0))\n    mat2 a = mat2(1, 2, 3, 4);\n\n    // ???\n    // Glsl has decided that for it's matrix constructor arguments it doesn't\n    // take them as is but instead flattens them so the `b` matrix is\n    // equivalent to the `a` matrix but in value and semantics\n    mat2 b = mat2(1, vec2(2, 3), 4);\n    mat3 c = mat3(1, 2, 3, vec3(1), vec3(1));\n    mat3 d = mat3(vec2(2), 1, vec3(1), vec3(1));\n    mat4 e = mat4(vec2(2), vec4(1), vec2(2), vec4(1), vec4(1));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/math-functions.frag",
    "content": "#version 450\n\nvoid main() {\n    vec4 a = vec4(1.0);\n    vec4 b = vec4(2.0);\n    mat4 m = mat4(a, b, a, b);\n    int i = 5;\n\n    vec4 ceilOut = ceil(a);\n    vec4 roundOut = round(a);\n    vec4 floorOut = floor(a);\n    vec4 fractOut = fract(a);\n    vec4 truncOut = trunc(a);\n    vec4 sinOut = sin(a);\n    vec4 absOut = abs(a);\n    vec4 sqrtOut = sqrt(a);\n    vec4 inversesqrtOut = inversesqrt(a);\n    vec4 expOut = exp(a);\n    vec4 exp2Out = exp2(a);\n    vec4 signOut = sign(a);\n    mat4 transposeOut = transpose(m);\n    // TODO: support inverse function in wgsl output\n    // mat4 inverseOut = inverse(m);\n    vec4 normalizeOut = normalize(a);\n    vec4 sinhOut = sinh(a);\n    vec4 cosOut = cos(a);\n    vec4 coshOut = cosh(a);\n    vec4 tanOut = tan(a);\n    vec4 tanhOut = tanh(a);\n    vec4 acosOut = acos(a);\n    vec4 asinOut = asin(a);\n    vec4 logOut = log(a);\n    vec4 log2Out = log2(a);\n    float lengthOut = length(a);\n    float determinantOut = determinant(m);\n    int bitCountOut = bitCount(i);\n    int bitfieldReverseOut = bitfieldReverse(i);\n    float atanOut = atan(a.x);\n    float atan2Out = atan(a.x, a.y);\n    float modOut = mod(a.x, b.x);\n    vec4 powOut = pow(a, b);\n    float dotOut = dot(a, b);\n    vec4 maxOut = max(a, b);\n    vec4 minOut = min(a, b);\n    vec4 reflectOut = reflect(a, b);\n    vec3 crossOut = cross(a.xyz, b.xyz);\n    // TODO: support outerProduct function in wgsl output\n    // mat4 outerProductOut = outerProduct(a, b);\n    float distanceOut = distance(a, b);\n    vec4 stepOut = step(a, b);\n    // TODO: support out params in wgsl output\n    // vec4 modfOut = modf(a, b);\n    // vec4 frexpOut = frexp(a, b);\n    float ldexpOut = ldexp(a.x, i);\n    vec4 rad = radians(a);\n    float deg = degrees(a.x);\n    float smoothStepScalar = smoothstep(0.0, 1.0, 0.5);\n    vec4 smoothStepVector = smoothstep(vec4(0.0), vec4(1.0), vec4(0.5));\n    vec4 smoothStepMixed = smoothstep(0.0, 1.0, vec4(0.5));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/multipart-for-loop.frag",
    "content": "// issue #6208 https://github.com/gfx-rs/wgpu/issues/6208\n# version 460\n\nvoid main() {\n    float a = 1.0;\n    float b = 0.25;\n    float c = 1.5;\n    int i = 20;\n\n    // tests for multiple expressions in first part (if it's a expression, not declaration)!\n    // also the third part!\n    for (i = 0, c-=1.0; i < 25; i++, b+=0.01) {\n        a -= 0.02;\n    }\n\n    // a, b and c should be all ~0.5!\n}"
  },
  {
    "path": "naga/tests/in/glsl/prepostfix.frag",
    "content": "#version 450 core\n\nvoid main() {\n\tint scalar_target;\n\tint scalar = 1;\n\tscalar_target = scalar++;\n\tscalar_target = --scalar;\n\n\tuvec2 vec_target;\n\tuvec2 vec = uvec2(1);\n\tvec_target = vec--;\n\tvec_target = ++vec;\n\n\tmat4x3 mat_target;\n\tmat4x3 mat = mat4x3(1);\n\tmat_target = mat++;\n\tmat_target = --mat;\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/quad_glsl.frag",
    "content": "#version 450\nlayout(location = 0) in vec2 v_uv;\n#ifdef TEXTURE\nlayout(set = 0, binding = 0) uniform texture2D u_texture;\nlayout(set = 0, binding = 1) uniform sampler u_sampler;\n#endif\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n#ifdef TEXTURE\n  o_color = texture(sampler2D(u_texture, u_sampler), v_uv);\n#else\n  o_color = vec4(1.0, 1.0, 1.0, 1.0);\n#endif\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/quad_glsl.vert",
    "content": "#version 450\nconst float c_scale = 1.2;\n\nlayout(location = 0) in vec2 a_pos;\nlayout(location = 1) in vec2 a_uv;\nlayout(location = 0) out vec2 v_uv;\n\nvoid main() {\n  v_uv = a_uv;\n  gl_Position = vec4(c_scale * a_pos, 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/sampler-functions.frag",
    "content": "#version 440\nprecision mediump float;\n\nlayout(set = 1, binding = 0) uniform texture2D tex2D;\nlayout(set = 1, binding = 1) uniform samplerShadow sampShadow;\n\nfloat CalcShadowPCF1(texture2D T_P_t_TextureDepth, samplerShadow S_P_t_TextureDepth, in vec3 t_ProjCoord) {\n    float t_Res = 0.0f;\n    t_Res += texture(sampler2DShadow(T_P_t_TextureDepth, S_P_t_TextureDepth), t_ProjCoord.xyz) * (1.0 / 5.0);\n    return t_Res;\n}\n\nfloat CalcShadowPCF(texture2D T_P_t_TextureDepth, samplerShadow S_P_t_TextureDepth, in vec3 t_ProjCoord, in float t_Bias) {\n    t_ProjCoord.z += t_Bias;\n    return CalcShadowPCF1(T_P_t_TextureDepth, S_P_t_TextureDepth, t_ProjCoord.xyz);\n}\n\nvoid main() {\n    CalcShadowPCF1(tex2D, sampShadow, vec3(0));\n    CalcShadowPCF(tex2D, sampShadow, vec3(0), 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/samplers.frag",
    "content": "#version 440 core\nprecision mediump float;\n\n// WGSL doesn't have 1D depth, 1D array or 2D multisampled array textures.\n#define HAS_1D_DEPTH_TEXTURES 0\n#define HAS_1D_ARRAY_TEXTURES 0\n#define HAS_2D_MS_ARRAY_TEXTURES 0\n\nlayout(set = 1, binding = 0) uniform texture1D tex1D;\n#if HAS_1D_ARRAY_TEXTURES\nlayout(set = 1, binding = 1) uniform texture1DArray tex1DArray;\n#endif\nlayout(set = 1, binding = 2) uniform texture2D tex2D;\nlayout(set = 1, binding = 3) uniform texture2DArray tex2DArray;\nlayout(set = 1, binding = 4) uniform textureCube texCube;\nlayout(set = 1, binding = 5) uniform textureCubeArray texCubeArray;\nlayout(set = 1, binding = 6) uniform texture3D tex3D;\n\nlayout(set = 1, binding = 7) uniform utexture2D utex2D;\nlayout(set = 1, binding = 8) uniform itexture2D itex2D;\n\nlayout(set = 2, binding = 0) uniform sampler samp;\n\n#if HAS_1D_DEPTH_TEXTURES\nlayout(set = 1, binding = 10) uniform texture1D tex1DShadow;\nlayout(set = 1, binding = 11) uniform texture1DArray tex1DArrayShadow;\n#endif\n\nlayout(set = 1, binding = 12) uniform texture2D tex2DShadow;\nlayout(set = 1, binding = 13) uniform texture2DArray tex2DArrayShadow;\nlayout(set = 1, binding = 14) uniform textureCube texCubeShadow;\nlayout(set = 1, binding = 15) uniform textureCubeArray texCubeArrayShadow;\nlayout(set = 1, binding = 16) uniform texture3D tex3DShadow;\nlayout(set = 1, binding = 17) uniform samplerShadow sampShadow;\n\nlayout(binding = 18) uniform texture2DMS tex2DMS;\n#if HAS_2D_MS_ARRAY_TEXTURES\nlayout(binding = 19) uniform texture2DMSArray tex2DMSArray;\n#endif\n\n// Conventions for readability:\n//   1.0 = Shadow Ref\n//   2.0 = LOD Bias\n//   3.0 = Explicit LOD\n//   4.0 = Grad Derivatives\n//   5 = Offset\n//   6.0 = Proj W\n\nvoid testTex1D(in float coord) {\n    int size1D = textureSize(sampler1D(tex1D, samp), 0);\n    int levels = textureQueryLevels(sampler1D(tex1D, samp));\n    vec4 c;\n    c = texture(sampler1D(tex1D, samp), coord);\n    /* 1 dimensional textures are not supported in the WGSL specification with `textureSampleBias()` */\n    // c = texture(sampler1D(tex1D, samp), coord, 2.0);\n    c = textureGrad(sampler1D(tex1D, samp), coord, 4.0, 4.0);\n    c = textureGradOffset(sampler1D(tex1D, samp), coord, 4.0, 4.0, 5);\n    c = textureLod(sampler1D(tex1D, samp), coord, 3.0);\n    c = textureLodOffset(sampler1D(tex1D, samp), coord, 3.0, 5);\n    c = textureOffset(sampler1D(tex1D, samp), coord, 5);\n    // c = textureOffset(sampler1D(tex1D, samp), coord, 5, 2.0);\n    c = textureProj(sampler1D(tex1D, samp), vec2(coord, 6.0));\n    c = textureProj(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0));\n    // c = textureProj(sampler1D(tex1D, samp), vec2(coord, 6.0), 2.0);\n    // c = textureProj(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 2.0);\n    c = textureProjGrad(sampler1D(tex1D, samp), vec2(coord, 6.0), 4.0, 4.0);\n    c = textureProjGrad(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 4.0, 4.0);\n    c = textureProjGradOffset(sampler1D(tex1D, samp), vec2(coord, 6.0), 4.0, 4.0, 5);\n    c = textureProjGradOffset(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 4.0, 4.0, 5);\n    c = textureProjLod(sampler1D(tex1D, samp), vec2(coord, 6.0), 3.0);\n    c = textureProjLod(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 3.0);\n    c = textureProjLodOffset(sampler1D(tex1D, samp), vec2(coord, 6.0), 3.0, 5);\n    c = textureProjLodOffset(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 3.0, 5);\n    c = textureProjOffset(sampler1D(tex1D, samp), vec2(coord, 6.0), 5);\n    c = textureProjOffset(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 5);\n    // c = textureProjOffset(sampler1D(tex1D, samp), vec2(coord, 6.0), 5, 2.0);\n    // c = textureProjOffset(sampler1D(tex1D, samp), vec4(coord, 0.0, 0.0, 6.0), 5, 2.0);\n    c = texelFetch(sampler1D(tex1D, samp), int(coord), 3);\n    c = texelFetchOffset(sampler1D(tex1D, samp), int(coord), 3, 5);\n}\n\n#if HAS_1D_DEPTH_TEXTURES\nvoid testTex1DShadow(float coord) {\n    int size1DShadow = textureSize(sampler1DShadow(tex1DShadow, sampShadow), 0);\n    int levels = textureQueryLevels(sampler1DShadow(tex1DShadow, sampShadow));\n    float d;\n    d = texture(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0));\n    // d = texture(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 2.0);\n    d = textureGrad(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 4.0, 4.0);\n    d = textureGradOffset(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 4.0, 4.0, 5);\n    d = textureLod(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 3.0);\n    d = textureLodOffset(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 3.0, 5);\n    d = textureOffset(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 5);\n    // d = textureOffset(sampler1DShadow(tex1DShadow, sampShadow), vec3(coord, 1.0, 1.0), 5, 2.0);\n    d = textureProj(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0));\n    // d = textureProj(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 2.0);\n    d = textureProjGrad(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 4.0, 4.0);\n    d = textureProjGradOffset(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 4.0, 4.0, 5);\n    d = textureProjLod(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 3.0);\n    d = textureProjLodOffset(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 3.0, 5);\n    d = textureProjOffset(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 5);\n    // d = textureProjOffset(sampler1DShadow(tex1DShadow, sampShadow), vec4(coord, 0.0, 1.0, 6.0), 5, 2.0);\n}\n#endif\n\n#if HAS_1D_ARRAY_TEXTURES\nvoid testTex1DArray(in vec2 coord) {\n    ivec2 size1DArray = textureSize(sampler1DArray(tex1DArray, samp), 0);\n    int levels = textureQueryLevels(sampler1DArray(tex1DArray, samp));\n    vec4 c;\n    c = texture(sampler1DArray(tex1DArray, samp), coord);\n    // c = texture(sampler1DArray(tex1DArray, samp), coord, 2.0);\n    c = textureGrad(sampler1DArray(tex1DArray, samp), coord, 4.0, 4.0);\n    c = textureGradOffset(sampler1DArray(tex1DArray, samp), coord, 4.0, 4.0, 5);\n    c = textureLod(sampler1DArray(tex1DArray, samp), coord, 3.0);\n    c = textureLodOffset(sampler1DArray(tex1DArray, samp), coord, 3.0, 5);\n    c = textureOffset(sampler1DArray(tex1DArray, samp), coord, 5);\n    // c = textureOffset(sampler1DArray(tex1DArray, samp), coord, 5, 2.0);\n    c = texelFetch(sampler1DArray(tex1DArray, samp), ivec2(coord), 3);\n    c = texelFetchOffset(sampler1DArray(tex1DArray, samp), ivec2(coord), 3, 5);\n}\n#endif\n\n#if HAS_1D_DEPTH_TEXTURES\nvoid testTex1DArrayShadow(in vec2 coord) {\n    ivec2 size1DArrayShadow = textureSize(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), 0);\n    int levels = textureQueryLevels(sampler1DArrayShadow(tex1DArrayShadow, sampShadow));\n    float d;\n    d = texture(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0));\n    d = textureGrad(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 4.0, 4.0);\n    d = textureGradOffset(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 4.0, 4.0, 5);\n    d = textureLod(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 3.0);\n    d = textureLodOffset(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 3.0, 5);\n    d = textureOffset(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 5);\n    // d = textureOffset(sampler1DArrayShadow(tex1DArrayShadow, sampShadow), vec3(coord, 1.0), 5, 2.0);\n}\n#endif\n\nvoid testTex2D(in vec2 coord) {\n    ivec2 size2D = textureSize(sampler2D(tex2D, samp), 0);\n    int levels = textureQueryLevels(sampler2D(tex2D, samp));\n    vec4 c;\n    c = texture(sampler2D(tex2D, samp), coord);\n    c = texture(sampler2D(tex2D, samp), coord, 2.0);\n    /* Signed/Unsigned samplers not supported in the WGSL specification with `textureSample()` */\n\n    c = textureGrad(sampler2D(tex2D, samp), coord, vec2(4.0), vec2(4.0));\n    /* Signed/Unsigned samplers not supported in the WGSL specification with `textureSampleGrad()` */\n\n    c = textureGradOffset(sampler2D(tex2D, samp), coord, vec2(4.0), vec2(4.0), ivec2(5));\n    /* Signed/Unsigned samplers not supported in the WGSL specification with `textureSampleGrad()` */\n\n    c = textureLod(sampler2D(tex2D, samp), coord, 3.0);\n    /* Signed/Unsigned samplers not supported in the WGSL specification with `textureSampleLevel()` */\n\n    c = textureLodOffset(sampler2D(tex2D, samp), coord, 3.0, ivec2(5));\n    c = textureOffset(sampler2D(tex2D, samp), coord, ivec2(5));\n    c = textureOffset(sampler2D(tex2D, samp), coord, ivec2(5), 2.0);\n\n    c = textureProj(sampler2D(tex2D, samp), vec3(coord, 6.0));\n    c = textureProj(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0));\n    c = textureProj(sampler2D(tex2D, samp), vec3(coord, 6.0), 2.0);\n    c = textureProj(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), 2.0);\n\n    c = textureProjGrad(sampler2D(tex2D, samp), vec3(coord, 6.0), vec2(4.0), vec2(4.0));\n    c = textureProjGrad(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), vec2(4.0), vec2(4.0));\n    c = textureProjGradOffset(sampler2D(tex2D, samp), vec3(coord, 6.0), vec2(4.0), vec2(4.0), ivec2(5));\n    c = textureProjGradOffset(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), vec2(4.0), vec2(4.0), ivec2(5));\n    c = textureProjLod(sampler2D(tex2D, samp), vec3(coord, 6.0), 3.0);\n    c = textureProjLod(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), 3.0);\n    c = textureProjLodOffset(sampler2D(tex2D, samp), vec3(coord, 6.0), 3.0, ivec2(5));\n    c = textureProjLodOffset(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), 3.0, ivec2(5));\n    c = textureProjOffset(sampler2D(tex2D, samp), vec3(coord, 6.0), ivec2(5));\n    c = textureProjOffset(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), ivec2(5));\n    c = textureProjOffset(sampler2D(tex2D, samp), vec3(coord, 6.0), ivec2(5), 2.0);\n    c = textureProjOffset(sampler2D(tex2D, samp), vec4(coord, 0.0, 6.0), ivec2(5), 2.0);\n\n    c = texelFetch(sampler2D(tex2D, samp), ivec2(coord), 3);\n    c = vec4(texelFetch(usampler2D(utex2D, samp), ivec2(coord), 3));\n    c = vec4(texelFetch(isampler2D(itex2D, samp), ivec2(coord), 3));\n\n    c = texelFetchOffset(sampler2D(tex2D, samp), ivec2(coord), 3, ivec2(5));\n    c = vec4(texelFetchOffset(usampler2D(utex2D, samp), ivec2(coord), 3, ivec2(5)));\n    c = vec4(texelFetchOffset(isampler2D(itex2D, samp), ivec2(coord), 3, ivec2(5)));\n}\n\nvoid testTex2DShadow(vec2 coord) {\n    ivec2 size2DShadow = textureSize(sampler2DShadow(tex2DShadow, sampShadow), 0);\n    int levels = textureQueryLevels(sampler2DShadow(tex2DShadow, sampShadow));\n    float d;\n    d = texture(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0));\n    // d = texture(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), 2.0);\n    d = textureGrad(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), vec2(4.0), vec2(4.0));\n    d = textureGradOffset(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), vec2(4.0), vec2(4.0), ivec2(5));\n    d = textureLod(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), 3.0);\n    d = textureLodOffset(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), 3.0, ivec2(5));\n    d = textureOffset(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), ivec2(5));\n    // d = textureOffset(sampler2DShadow(tex2DShadow, sampShadow), vec3(coord, 1.0), ivec2(5), 2.0);\n    d = textureProj(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0));\n    // d = textureProj(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), 2.0);\n    d = textureProjGrad(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), vec2(4.0), vec2(4.0));\n    d = textureProjGradOffset(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), vec2(4.0), vec2(4.0), ivec2(5));\n    d = textureProjLod(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), 3.0);\n    d = textureProjLodOffset(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), 3.0, ivec2(5));\n    d = textureProjOffset(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), ivec2(5));\n    // d = textureProjOffset(sampler2DShadow(tex2DShadow, sampShadow), vec4(coord, 1.0, 6.0), ivec2(5), 2.0);\n}\n\nvoid testTex2DArray(in vec3 coord) {\n    ivec3 size2DArray = textureSize(sampler2DArray(tex2DArray, samp), 0);\n    int levels = textureQueryLevels(sampler2DArray(tex2DArray, samp));\n    vec4 c;\n    c = texture(sampler2DArray(tex2DArray, samp), coord);\n    c = texture(sampler2DArray(tex2DArray, samp), coord, 2.0);\n    c = textureGrad(sampler2DArray(tex2DArray, samp), coord, vec2(4.0), vec2(4.0));\n    c = textureGradOffset(sampler2DArray(tex2DArray, samp), coord, vec2(4.0), vec2(4.0), ivec2(5));\n    c = textureLod(sampler2DArray(tex2DArray, samp), coord, 3.0);\n    c = textureLodOffset(sampler2DArray(tex2DArray, samp), coord, 3.0, ivec2(5));\n    c = textureOffset(sampler2DArray(tex2DArray, samp), coord, ivec2(5));\n    c = textureOffset(sampler2DArray(tex2DArray, samp), coord, ivec2(5), 2.0);\n    c = texelFetch(sampler2DArray(tex2DArray, samp), ivec3(coord), 3);\n    c = texelFetchOffset(sampler2DArray(tex2DArray, samp), ivec3(coord), 3, ivec2(5));\n}\n\nvoid testTex2DArrayShadow(in vec3 coord) {\n    ivec3 size2DArrayShadow = textureSize(sampler2DArrayShadow(tex2DArrayShadow, sampShadow), 0);\n    int levels = textureQueryLevels(sampler2DArrayShadow(tex2DArrayShadow, sampShadow));\n    float d;\n    d = texture(sampler2DArrayShadow(tex2DArrayShadow, sampShadow), vec4(coord, 1.0));\n    d = textureGrad(sampler2DArrayShadow(tex2DArrayShadow, sampShadow), vec4(coord, 1.0), vec2(4.0), vec2(4.0));\n    d = textureGradOffset(sampler2DArrayShadow(tex2DArrayShadow, sampShadow), vec4(coord, 1.0), vec2(4.0), vec2(4.0), ivec2(5));\n    d = textureOffset(sampler2DArrayShadow(tex2DArrayShadow, sampShadow), vec4(coord, 1.0), ivec2(5));\n}\n\nvoid testTexCube(in vec3 coord) {\n    ivec2 sizeCube = textureSize(samplerCube(texCube, samp), 0);\n    int levels = textureQueryLevels(samplerCube(texCube, samp));\n    vec4 c;\n    c = texture(samplerCube(texCube, samp), coord);\n    c = texture(samplerCube(texCube, samp), coord, 2.0);\n    c = textureGrad(samplerCube(texCube, samp), coord, vec3(4.0), vec3(4.0));\n    c = textureLod(samplerCube(texCube, samp), coord, 3.0);\n}\n\nvoid testTexCubeShadow(in vec3 coord) {\n    ivec2 sizeCubeShadow = textureSize(samplerCubeShadow(texCubeShadow, sampShadow), 0);\n    int levels = textureQueryLevels(samplerCubeShadow(texCubeShadow, sampShadow));\n    float d;\n    d = texture(samplerCubeShadow(texCubeShadow, sampShadow), vec4(coord, 1.0));\n    d = textureGrad(samplerCubeShadow(texCubeShadow, sampShadow), vec4(coord, 1.0), vec3(4.0), vec3(4.0));\n}\n\nvoid testTexCubeArray(in vec4 coord) {\n    ivec3 sizeCubeArray = textureSize(samplerCubeArray(texCubeArray, samp), 0);\n    int levels = textureQueryLevels(samplerCubeArray(texCubeArray, samp));\n    vec4 c;\n    c = texture(samplerCubeArray(texCubeArray, samp), coord);\n    c = texture(samplerCubeArray(texCubeArray, samp), coord, 2.0);\n    c = textureGrad(samplerCubeArray(texCubeArray, samp), coord, vec3(4.0), vec3(4.0));\n    c = textureLod(samplerCubeArray(texCubeArray, samp), coord, 3.0);\n}\n\nvoid testTexCubeArrayShadow(in vec4 coord) {\n    ivec3 sizeCubeArrayShadow = textureSize(samplerCubeArrayShadow(texCubeArrayShadow, sampShadow), 0);\n    int levels = textureQueryLevels(samplerCubeArrayShadow(texCubeArrayShadow, sampShadow));\n    float d;\n    d = texture(samplerCubeArrayShadow(texCubeArrayShadow, sampShadow), coord, 1.0);\n    // The rest of the variants aren't defined by GLSL.\n}\n\nvoid testTex3D(in vec3 coord) {\n    ivec3 size3D = textureSize(sampler3D(tex3D, samp), 0);\n    int levels = textureQueryLevels(sampler3D(tex3D, samp));\n    vec4 c;\n    c = texture(sampler3D(tex3D, samp), coord);\n    c = texture(sampler3D(tex3D, samp), coord, 2.0);\n    c = textureProj(sampler3D(tex3D, samp), vec4(coord, 6.0));\n    c = textureProj(sampler3D(tex3D, samp), vec4(coord, 6.0), 2.0);\n    c = textureProjOffset(sampler3D(tex3D, samp), vec4(coord, 6.0), ivec3(5));\n    c = textureProjOffset(sampler3D(tex3D, samp), vec4(coord, 6.0), ivec3(5), 2.0);\n    c = textureProjLod(sampler3D(tex3D, samp), vec4(coord, 6.0), 3.0);\n    c = textureProjLodOffset(sampler3D(tex3D, samp), vec4(coord, 6.0), 3.0, ivec3(5));\n    c = textureProjGrad(sampler3D(tex3D, samp), vec4(coord, 6.0), vec3(4.0), vec3(4.0));\n    c = textureProjGradOffset(sampler3D(tex3D, samp), vec4(coord, 6.0), vec3(4.0), vec3(4.0), ivec3(5));\n    c = textureGrad(sampler3D(tex3D, samp), coord, vec3(4.0), vec3(4.0));\n    c = textureGradOffset(sampler3D(tex3D, samp), coord, vec3(4.0), vec3(4.0), ivec3(5));\n    c = textureLod(sampler3D(tex3D, samp), coord, 3.0);\n    c = textureLodOffset(sampler3D(tex3D, samp), coord, 3.0, ivec3(5));\n    c = textureOffset(sampler3D(tex3D, samp), coord, ivec3(5));\n    c = textureOffset(sampler3D(tex3D, samp), coord, ivec3(5), 2.0);\n    c = texelFetch(sampler3D(tex3D, samp), ivec3(coord), 3);\n    c = texelFetchOffset(sampler3D(tex3D, samp), ivec3(coord), 3, ivec3(5));\n}\n\nvoid testTex2DMS(in vec2 coord) {\n    ivec2 size2DMS = textureSize(sampler2DMS(tex2DMS, samp));\n    vec4 c;\n    c = texelFetch(sampler2DMS(tex2DMS, samp), ivec2(coord), 3);\n}\n\n#if HAS_2D_MS_ARRAY_TEXTURES\nvoid testTex2DMSArray(in vec3 coord) {\n    ivec3 size2DMSArray = textureSize(sampler2DMSArray(tex2DMSArray, samp));\n    vec4 c;\n    c = texelFetch(sampler2DMSArray(tex2DMSArray, samp), ivec3(coord), 3);\n}\n#endif\n\nvoid main() {\n    testTex1D(1.0);\n#if HAS_1D_DEPTH_TEXTURES\n    testTex1DShadow(2.0);\n#endif\n#if HAS_1D_ARRAY_TEXTURES\n    testTex1DArray(vec2(3.0));\n#endif\n#if HAS_1D_DEPTH_TEXTURES\n    testTex1DArrayShadow(vec2(4.0));\n#endif\n    testTex2D(vec2(1.0));\n    testTex2DShadow(vec2(1.0));\n    testTex2DArray(vec3(1.0));\n    testTex2DArrayShadow(vec3(1.0));\n    testTexCube(vec3(1.0));\n    testTexCubeShadow(vec3(1.0));\n    testTexCubeArray(vec4(1.0));\n    testTexCubeArrayShadow(vec4(1.0));\n    testTex3D(vec3(1.0));\n    testTex2DMS(vec2(1.0));\n#if HAS_2D_MS_ARRAY_TEXTURES\n    testTex2DMSArray(vec3(1.0));\n#endif\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/spec-constant.frag",
    "content": "#version 450\n\n// Specialization constants with constant_id layout qualifier\nlayout(constant_id = 0) const bool SPEC_CONST_BOOL = true;\nlayout(constant_id = 1) const int SPEC_CONST_INT = 42;\nlayout(constant_id = 2) const uint SPEC_CONST_UINT = 10u;\nlayout(constant_id = 3) const float SPEC_CONST_FLOAT = 3.14;\n\n// NOTE: Naga does not yet support GLSL const variables depending on specialization constants (constant_id).\n// const vec3 scVec = vec3(SPEC_CONST_FLOAT, 1, 1); // Would cause error\n\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n    float result = 0.0;\n    \n    if (SPEC_CONST_BOOL) {\n        result += float(SPEC_CONST_INT);\n    }\n    \n    result += float(SPEC_CONST_UINT) * SPEC_CONST_FLOAT;\n    \n    o_color = vec4(result, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/spec-constant.toml",
    "content": "targets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/glsl/statements.frag",
    "content": "#version 460 core\n\nvoid switchEmpty(int a) {\n    switch (a) {}\n\n    return;\n}\n\nvoid switchNoDefault(int a) {\n    switch (a) {\n        case 0:\n            break;\n    }\n\n    return;\n}\n\nvoid switchCaseImplConv(uint a) {\n    switch (a) {\n        case 0:\n            break;\n    }\n\n    return;\n}\n\nvoid switchNoLastBreak(int a) {\n    switch (a) {\n        default:\n            int b = a;\n    }\n\n    return;\n}\n\nvoid main() {\n    switchEmpty(1);\n    switchNoDefault(2);\n    switchCaseImplConv(3);\n    switchNoLastBreak(4);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/variations.frag",
    "content": "#version 460 core\n\nlayout(set = 0, binding = 0) uniform textureCube texCube;\nlayout(set = 0, binding = 1) uniform sampler samp;\n\nvoid main() {\n    ivec2 sizeCube = textureSize(samplerCube(texCube, samp), 0);\n    float a = ceil(1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/variations.toml",
    "content": "targets = \"GLSL\"\n"
  },
  {
    "path": "naga/tests/in/glsl/vector-functions.frag",
    "content": "#version 450\n\nvoid ftest(vec4 a, vec4 b) {\n\tbvec4 c = lessThan(a, b);\n\tbvec4 d = lessThanEqual(a, b);\n\tbvec4 e = greaterThan(a, b);\n\tbvec4 f = greaterThanEqual(a, b);\n\tbvec4 g = equal(a, b);\n\tbvec4 h = notEqual(a, b);\n}\n\nvoid dtest(dvec4 a, dvec4 b) {\n\tbvec4 c = lessThan(a, b);\n\tbvec4 d = lessThanEqual(a, b);\n\tbvec4 e = greaterThan(a, b);\n\tbvec4 f = greaterThanEqual(a, b);\n\tbvec4 g = equal(a, b);\n\tbvec4 h = notEqual(a, b);\n}\n\nvoid itest(ivec4 a, ivec4 b) {\n\tbvec4 c = lessThan(a, b);\n\tbvec4 d = lessThanEqual(a, b);\n\tbvec4 e = greaterThan(a, b);\n\tbvec4 f = greaterThanEqual(a, b);\n\tbvec4 g = equal(a, b);\n\tbvec4 h = notEqual(a, b);\n}\n\nvoid utest(uvec4 a, uvec4 b) {\n\tbvec4 c = lessThan(a, b);\n\tbvec4 d = lessThanEqual(a, b);\n\tbvec4 e = greaterThan(a, b);\n\tbvec4 f = greaterThanEqual(a, b);\n\tbvec4 g = equal(a, b);\n\tbvec4 h = notEqual(a, b);\n}\n\nvoid btest(bvec4 a, bvec4 b) {\n\tbvec4 c = equal(a, b);\n\tbvec4 d = notEqual(a, b);\n\tbool e = any(a);\n\tbool f = all(a);\n\tbvec4 g = not(a);\n}\n\nvoid main() {\n    ftest(vec4(0), vec4(0));\n    dtest(dvec4(0), dvec4(0));\n    itest(ivec4(0), ivec4(0));\n    utest(uvec4(0), uvec4(0));\n    btest(bvec4(false), bvec4(false));\n}\n"
  },
  {
    "path": "naga/tests/in/glsl/vector-functions.toml",
    "content": "capabilities = \"FLOAT64\"\n"
  },
  {
    "path": "naga/tests/in/spv/8151-barrier-reorder.spvasm",
    "content": "; SPIR-V\n; Version: 1.3\n; Generator: Google rspirv; 0\n; Bound: 45\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpExtension \"SPV_KHR_vulkan_memory_model\"\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"barrier_reorder_bug\" %gl_LocalInvocationID\n               OpExecutionMode %1 LocalSize 2 1 1\n               OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId\n               OpDecorate %_runtimearr_uint ArrayStride 4\n               OpDecorate %_struct_8 Block\n               OpMemberDecorate %_struct_8 0 Offset 0\n               OpDecorate %5 Binding 0\n               OpDecorate %5 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n     %v3uint = OpTypeVector %uint 3\n%_ptr_Input_v3uint = OpTypePointer Input %v3uint\n       %void = OpTypeVoid\n         %13 = OpTypeFunction %void\n%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input\n%_runtimearr_uint = OpTypeRuntimeArray %uint\n  %_struct_8 = OpTypeStruct %_runtimearr_uint\n%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8\n          %5 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer\n     %uint_0 = OpConstant %uint 0\n       %bool = OpTypeBool\n%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n          %6 = OpVariable %_ptr_Workgroup_uint Workgroup\n     %uint_1 = OpConstant %uint 1\n     %uint_2 = OpConstant %uint 2\n   %uint_264 = OpConstant %uint 264\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n          %1 = OpFunction %void None %13\n         %23 = OpLabel\n         %24 = OpLoad %v3uint %gl_LocalInvocationID\n         %27 = OpCompositeExtract %uint %24 0\n         %28 = OpIEqual %bool %27 %uint_0\n               OpSelectionMerge %29 None\n               OpBranchConditional %28 %30 %31\n         %30 = OpLabel\n               OpStore %6 %uint_1\n               OpBranch %29\n         %31 = OpLabel\n               OpBranch %29\n         %29 = OpLabel\n               OpControlBarrier %uint_2 %uint_2 %uint_264\n         %32 = OpLoad %uint %6\n               OpControlBarrier %uint_2 %uint_2 %uint_264\n         %39 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %5 %uint_0 %27\n               OpStore %39 %32\n               OpSelectionMerge %42 None\n               OpBranchConditional %28 %43 %44\n         %43 = OpLabel\n               OpStore %6 %uint_2\n               OpBranch %42\n         %44 = OpLabel\n               OpBranch %42\n         %42 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_compare_exchange.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 65\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_compare_exchange\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpMemberDecorate %_struct_9 0 Offset 0\n               OpMemberDecorate %_struct_9 1 Offset 4\n               OpDecorate %_struct_10 Block\n               OpMemberDecorate %_struct_10 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 NonWritable\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n         %13 = OpTypeFunction %void\n       %bool = OpTypeBool\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n      %false = OpConstantFalse %bool\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n     %uint_1 = OpConstant %uint 1\n  %_struct_9 = OpTypeStruct %uint %uint\n         %20 = OpUndef %_struct_9\n     %uint_3 = OpConstant %uint 3\n        %int = OpTypeInt 32 1\n         %23 = OpUndef %bool\n       %true = OpConstantTrue %bool\n %_struct_10 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_10 = OpTypePointer StorageBuffer %_struct_10\n          %2 = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer\n   %uint_256 = OpConstant %uint 256\n          %1 = OpFunction %void None %13\n         %27 = OpLabel\n         %28 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %29 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %3 %uint_0\n         %30 = OpLoad %uint %29\n         %31 = OpCompositeConstruct %_struct_9 %uint_0 %30\n               OpBranch %32\n         %32 = OpLabel\n         %33 = OpPhi %_struct_9 %31 %27 %34 %35\n               OpLoopMerge %36 %35 None\n               OpBranch %37\n         %37 = OpLabel\n         %38 = OpCompositeExtract %uint %33 0\n         %39 = OpCompositeExtract %uint %33 1\n         %40 = OpULessThan %bool %38 %39\n               OpSelectionMerge %41 None\n               OpBranchConditional %40 %42 %43\n         %42 = OpLabel\n         %45 = OpIAdd %uint %38 %uint_1\n         %46 = OpCompositeInsert %_struct_9 %45 %33 0\n         %47 = OpCompositeConstruct %_struct_9 %uint_1 %38\n               OpBranch %41\n         %43 = OpLabel\n         %48 = OpCompositeInsert %_struct_9 %uint_0 %20 0\n               OpBranch %41\n         %41 = OpLabel\n         %34 = OpPhi %_struct_9 %46 %42 %33 %43\n         %49 = OpPhi %_struct_9 %47 %42 %48 %43\n         %50 = OpCompositeExtract %uint %49 0\n         %51 = OpCompositeExtract %uint %49 1\n         %52 = OpBitcast %int %50\n               OpSelectionMerge %53 None\n               OpSwitch %52 %54 0 %55 1 %56\n         %54 = OpLabel\n               OpBranch %53\n         %55 = OpLabel\n               OpBranch %53\n         %56 = OpLabel\n         %57 = OpAtomicCompareExchange %uint %28 %uint_2 %uint_256 %uint_256 %51 %uint_3\n         %58 = OpIEqual %bool %57 %uint_3\n         %64 = OpSelect %bool %58 %false %true\n               OpBranch %53\n         %53 = OpLabel\n         %63 = OpPhi %bool %23 %54 %false %55 %64 %56\n               OpBranch %35\n         %35 = OpLabel\n               OpBranchConditional %63 %32 %36\n         %36 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_exchange.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 63\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_exchange\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpMemberDecorate %_struct_11 0 Offset 0\n               OpMemberDecorate %_struct_11 1 Offset 4\n               OpDecorate %_struct_12 Block\n               OpMemberDecorate %_struct_12 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 NonWritable\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n         %15 = OpTypeFunction %void\n       %bool = OpTypeBool\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n      %false = OpConstantFalse %bool\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n     %uint_1 = OpConstant %uint 1\n %_struct_11 = OpTypeStruct %uint %uint\n         %22 = OpUndef %_struct_11\n        %int = OpTypeInt 32 1\n       %true = OpConstantTrue %bool\n %_struct_12 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12\n          %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer\n         %26 = OpUndef %uint\n          %1 = OpFunction %void None %15\n         %27 = OpLabel\n         %28 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0\n         %30 = OpLoad %uint %29\n         %31 = OpCompositeConstruct %_struct_11 %uint_0 %30\n               OpBranch %32\n         %32 = OpLabel\n         %33 = OpPhi %_struct_11 %31 %27 %34 %35\n         %36 = OpPhi %uint %uint_0 %27 %37 %35\n               OpLoopMerge %38 %35 None\n               OpBranch %39\n         %39 = OpLabel\n         %40 = OpCompositeExtract %uint %33 0\n         %41 = OpCompositeExtract %uint %33 1\n         %42 = OpULessThan %bool %40 %41\n               OpSelectionMerge %43 None\n               OpBranchConditional %42 %44 %45\n         %44 = OpLabel\n         %47 = OpIAdd %uint %40 %uint_1\n         %49 = OpCompositeInsert %_struct_11 %47 %33 0\n         %50 = OpCompositeConstruct %_struct_11 %uint_1 %40\n               OpBranch %43\n         %45 = OpLabel\n         %51 = OpCompositeInsert %_struct_11 %uint_0 %22 0\n               OpBranch %43\n         %43 = OpLabel\n         %52 = OpPhi %_struct_11 %49 %44 %33 %45\n         %53 = OpPhi %_struct_11 %50 %44 %51 %45\n         %54 = OpCompositeExtract %uint %53 0\n         %55 = OpBitcast %int %54\n               OpSelectionMerge %56 None\n               OpSwitch %55 %57 0 %58 1 %59\n         %57 = OpLabel\n               OpBranch %56\n         %58 = OpLabel\n               OpBranch %56\n         %59 = OpLabel\n         %60 = OpAtomicExchange %uint %28 %uint_2 %uint_0 %36\n         %61 = OpIAdd %uint %36 %60\n               OpBranch %56\n         %56 = OpLabel\n         %62 = OpPhi %bool %false %57 %false %58 %true %59\n         %34 = OpPhi %_struct_11 %22 %57 %22 %58 %52 %59\n         %37 = OpPhi %uint %26 %57 %26 %58 %61 %59\n               OpBranch %35\n         %35 = OpLabel\n               OpBranchConditional %62 %32 %38\n         %38 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_global_struct_field_vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 44\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint Vertex %1 \"global_field_vertex\" %2 %gl_Position\n               OpMemberDecorate %_struct_9 0 Offset 0\n               OpMemberDecorate %_struct_9 1 Offset 8\n               OpMemberDecorate %_struct_9 2 Offset 16\n               OpDecorate %_struct_10 Block\n               OpMemberDecorate %_struct_10 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %gl_Position BuiltIn Position\n       %uint = OpTypeInt 32 0\n      %float = OpTypeFloat 32\n    %v2float = OpTypeVector %float 2\n  %_struct_9 = OpTypeStruct %uint %v2float %uint\n %_struct_10 = OpTypeStruct %_struct_9\n%_ptr_StorageBuffer__struct_10 = OpTypePointer StorageBuffer %_struct_10\n    %v4float = OpTypeVector %float 4\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n       %void = OpTypeVoid\n         %18 = OpTypeFunction %void\n          %2 = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer\n     %uint_0 = OpConstant %uint 0\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n     %uint_2 = OpConstant %uint 2\n     %uint_5 = OpConstant %uint 5\n%_ptr_StorageBuffer_v2float = OpTypePointer StorageBuffer %v2float\n     %uint_1 = OpConstant %uint 1\n    %float_0 = OpConstant %float 0\n%gl_Position = OpVariable %_ptr_Output_v4float Output\n          %1 = OpFunction %void None %18\n         %28 = OpLabel\n         %30 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 %uint_2\n         %31 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 %uint_0\n         %32 = OpLoad %uint %31\n         %33 = OpAtomicIAdd %uint %30 %uint_5 %uint_0 %32\n         %34 = OpConvertUToF %float %33\n         %35 = OpInBoundsAccessChain %_ptr_StorageBuffer_v2float %2 %uint_0 %uint_1\n         %36 = OpLoad %v2float %35\n         %37 = OpCompositeExtract %float %36 0\n         %38 = OpCompositeExtract %float %36 1\n         %39 = OpFMul %float %34 %37\n         %40 = OpFMul %float %34 %38\n         %43 = OpCompositeConstruct %v4float %39 %40 %float_0 %34\n               OpStore %gl_Position %43\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_i_add_sub.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 30\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_i_add_sub\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpDecorate %_runtimearr_uint ArrayStride 4\n               OpDecorate %_struct_7 Block\n               OpMemberDecorate %_struct_7 0 Offset 0\n               OpDecorate %_struct_8 Block\n               OpMemberDecorate %_struct_8 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n         %11 = OpTypeFunction %void\n       %bool = OpTypeBool\n%_runtimearr_uint = OpTypeRuntimeArray %uint\n  %_struct_7 = OpTypeStruct %_runtimearr_uint\n%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n  %_struct_8 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8\n          %2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer\n          %1 = OpFunction %void None %11\n         %19 = OpLabel\n         %20 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %22 = OpArrayLength %uint %3 0\n         %23 = OpAtomicIAdd %uint %20 %uint_2 %uint_0 %uint_2\n         %24 = OpAtomicISub %uint %20 %uint_2 %uint_0 %23\n         %25 = OpULessThan %bool %23 %22\n               OpSelectionMerge %26 None\n               OpBranchConditional %25 %27 %28\n         %27 = OpLabel\n         %29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %23\n               OpStore %29 %24\n               OpBranch %26\n         %28 = OpLabel\n               OpBranch %26\n         %26 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_i_decrement.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 42\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_i_decrement\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpDecorate %_runtimearr_uint ArrayStride 4\n               OpDecorate %_struct_7 Block\n               OpMemberDecorate %_struct_7 0 Offset 0\n               OpDecorate %_struct_8 Block\n               OpMemberDecorate %_struct_8 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n         %11 = OpTypeFunction %void\n       %bool = OpTypeBool\n%_runtimearr_uint = OpTypeRuntimeArray %uint\n  %_struct_7 = OpTypeStruct %_runtimearr_uint\n%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n      %false = OpConstantFalse %bool\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n       %true = OpConstantTrue %bool\n  %_struct_8 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8\n          %2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer\n          %1 = OpFunction %void None %11\n         %21 = OpLabel\n         %22 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %24 = OpArrayLength %uint %3 0\n               OpBranch %25\n         %25 = OpLabel\n               OpLoopMerge %26 %27 None\n               OpBranch %28\n         %28 = OpLabel\n         %29 = OpAtomicIDecrement %uint %22 %uint_2 %uint_0\n         %30 = OpULessThan %bool %29 %24\n               OpSelectionMerge %31 None\n               OpBranchConditional %30 %32 %33\n         %32 = OpLabel\n         %34 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %29\n               OpStore %34 %29\n         %35 = OpIEqual %bool %29 %uint_0\n         %41 = OpSelect %bool %35 %false %true\n               OpBranch %31\n         %33 = OpLabel\n               OpBranch %31\n         %31 = OpLabel\n         %40 = OpPhi %bool %41 %32 %false %33\n               OpBranch %27\n         %27 = OpLabel\n               OpBranchConditional %40 %25 %26\n         %26 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_i_increment.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 37\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_i_increment\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpDecorate %_struct_6 Block\n               OpMemberDecorate %_struct_6 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 NonWritable\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n          %9 = OpTypeFunction %void\n       %bool = OpTypeBool\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n      %false = OpConstantFalse %bool\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n     %uint_1 = OpConstant %uint 1\n       %true = OpConstantTrue %bool\n  %_struct_6 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_6 = OpTypePointer StorageBuffer %_struct_6\n          %2 = OpVariable %_ptr_StorageBuffer__struct_6 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_6 StorageBuffer\n         %18 = OpUndef %uint\n          %1 = OpFunction %void None %9\n         %19 = OpLabel\n         %20 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %21 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0\n               OpBranch %22\n         %22 = OpLabel\n         %23 = OpPhi %uint %uint_0 %19 %24 %25\n               OpLoopMerge %26 %25 None\n               OpBranch %27\n         %27 = OpLabel\n         %28 = OpLoad %uint %21\n         %29 = OpUGreaterThanEqual %bool %23 %28\n               OpSelectionMerge %30 None\n               OpBranchConditional %29 %31 %32\n         %31 = OpLabel\n               OpBranch %30\n         %32 = OpLabel\n         %33 = OpAtomicIIncrement %uint %20 %uint_2 %uint_0\n         %34 = OpIAdd %uint %23 %uint_1\n               OpBranch %30\n         %30 = OpLabel\n         %24 = OpPhi %uint %18 %31 %34 %32\n         %36 = OpSelect %bool %29 %false %true\n               OpBranch %25\n         %25 = OpLabel\n               OpBranchConditional %36 %22 %26\n         %26 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/atomic_load_and_store.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 60\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"stage::test_atomic_load_and_store\" %2 %3\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpMemberDecorate %_struct_11 0 Offset 0\n               OpMemberDecorate %_struct_11 1 Offset 4\n               OpDecorate %_struct_12 Block\n               OpMemberDecorate %_struct_12 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 NonWritable\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n       %void = OpTypeVoid\n         %15 = OpTypeFunction %void\n       %bool = OpTypeBool\n     %uint_0 = OpConstant %uint 0\n     %uint_2 = OpConstant %uint 2\n      %false = OpConstantFalse %bool\n%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint\n     %uint_1 = OpConstant %uint 1\n %_struct_11 = OpTypeStruct %uint %uint\n         %22 = OpUndef %_struct_11\n        %int = OpTypeInt 32 1\n       %true = OpConstantTrue %bool\n %_struct_12 = OpTypeStruct %uint\n%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12\n          %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer\n          %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer\n          %1 = OpFunction %void None %15\n         %26 = OpLabel\n         %27 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0\n         %28 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0\n         %29 = OpLoad %uint %28\n         %30 = OpCompositeConstruct %_struct_11 %uint_0 %29\n               OpBranch %31\n         %31 = OpLabel\n         %32 = OpPhi %_struct_11 %30 %26 %33 %34\n               OpLoopMerge %35 %34 None\n               OpBranch %36\n         %36 = OpLabel\n         %37 = OpCompositeExtract %uint %32 0\n         %38 = OpCompositeExtract %uint %32 1\n         %39 = OpULessThan %bool %37 %38\n               OpSelectionMerge %40 None\n               OpBranchConditional %39 %41 %42\n         %41 = OpLabel\n         %44 = OpIAdd %uint %37 %uint_1\n         %46 = OpCompositeInsert %_struct_11 %44 %32 0\n         %47 = OpCompositeConstruct %_struct_11 %uint_1 %37\n               OpBranch %40\n         %42 = OpLabel\n         %48 = OpCompositeInsert %_struct_11 %uint_0 %22 0\n               OpBranch %40\n         %40 = OpLabel\n         %49 = OpPhi %_struct_11 %46 %41 %32 %42\n         %50 = OpPhi %_struct_11 %47 %41 %48 %42\n         %51 = OpCompositeExtract %uint %50 0\n         %52 = OpBitcast %int %51\n               OpSelectionMerge %53 None\n               OpSwitch %52 %54 0 %55 1 %56\n         %54 = OpLabel\n               OpBranch %53\n         %55 = OpLabel\n               OpBranch %53\n         %56 = OpLabel\n         %57 = OpAtomicLoad %uint %27 %uint_2 %uint_0\n         %58 = OpIAdd %uint %57 %uint_2\n               OpAtomicStore %27 %uint_2 %uint_0 %58\n               OpBranch %53\n         %53 = OpLabel\n         %59 = OpPhi %bool %false %54 %false %55 %true %56\n         %33 = OpPhi %_struct_11 %22 %54 %22 %55 %49 %56\n               OpBranch %34\n         %34 = OpLabel\n               OpBranchConditional %59 %31 %35\n         %35 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/barrier.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 14\n; Schema: 0\n               OpCapability Shader\n               OpMemoryModel Logical Simple\n               OpEntryPoint GLCompute %1 \"main\"\n               OpExecutionMode %1 LocalSize 64 1 1\n       %void = OpTypeVoid\n          %6 = OpTypeFunction %void\n       %uint = OpTypeInt 32 0\n     %uint_2 = OpConstant %uint 2\n   %uint_264 = OpConstant %uint 264\n     %uint_1 = OpConstant %uint 1\n  %uint_2120 = OpConstant %uint 2120\n  %uint_2376 = OpConstant %uint 2376\n          %1 = OpFunction %void None %6\n         %13 = OpLabel\n               OpMemoryBarrier %uint_2 %uint_264\n               OpControlBarrier %uint_2 %uint_2 %uint_264\n               OpMemoryBarrier %uint_1 %uint_2120\n               OpControlBarrier %uint_2 %uint_1 %uint_2120\n               OpMemoryBarrier %uint_1 %uint_2376\n               OpControlBarrier %uint_2 %uint_1 %uint_2376\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/barrier.toml",
    "content": "targets = \"WGSL | SPIRV | GLSL | HLSL | METAL\"\n\n[msl]\nlang_version = [2, 0]\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.dynamic.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n\n;; Make sure that we promote `OpTypeRuntimeArray` of textures and samplers into\n;; `TypeInner::BindingArray` and support indexing it through `OpAccessChain`\n;; and `OpInBoundsAccessChain`.\n;;\n;; Code in here corresponds to, more or less:\n;;\n;; ```rust\n;; #[spirv(fragment)]\n;; pub fn main(\n;;     #[spirv(descriptor_set = 0, binding = 0)]\n;;     images: &RuntimeArray<Image!(2D, type=f32, sampled)>,\n;;     #[spirv(descriptor_set = 0, binding = 1)]\n;;     samplers: &RuntimeArray<Sampler>,\n;;     out: &mut Vec4,\n;; ) {\n;;     let image = images[1];\n;;     let sampler = samplers[1];\n;;\n;;     *out = image.sample_by_lod(sampler, vec2(0.5, 0.5), 0.0);\n;; }\n;; ```\n\n                     OpCapability Shader\n                     OpMemoryModel Logical Simple\n                     OpEntryPoint Fragment %main \"main\" %fn_param_images %fn_param_samplers %fn_param_out\n                     OpExecutionMode %main OriginUpperLeft\n                     OpDecorate %images ArrayStride 4\n                     OpDecorate %samplers ArrayStride 4\n                     OpDecorate %fn_param_images DescriptorSet 0\n                     OpDecorate %fn_param_images Binding 0\n                     OpDecorate %fn_param_samplers DescriptorSet 0\n                     OpDecorate %fn_param_samplers Binding 1\n                     OpDecorate %fn_param_out Location 0\n\n             %void = OpTypeVoid\n\n            %float = OpTypeFloat 32\n          %v2float = OpTypeVector %float 2\n          %v4float = OpTypeVector %float 4\n      %v4float_ptr = OpTypePointer Output %v4float\n        %float_0_5 = OpConstant %float 0.5\n    %float_0_5_0_5 = OpConstantComposite %v2float %float_0_5 %float_0_5\n          %float_0 = OpConstant %float 0\n\n              %int = OpTypeInt 32 1\n            %int_1 = OpConstant %int 1\n\n            %image = OpTypeImage %float 2D 2 0 0 1 Unknown\n        %image_ptr = OpTypePointer UniformConstant %image\n           %images = OpTypeRuntimeArray %image\n       %images_ptr = OpTypePointer UniformConstant %images\n\n          %sampler = OpTypeSampler\n      %sampler_ptr = OpTypePointer UniformConstant %sampler\n         %samplers = OpTypeRuntimeArray %sampler\n     %samplers_ptr = OpTypePointer UniformConstant %samplers\n\n    %sampled_image = OpTypeSampledImage %image\n\n          %fn_void = OpTypeFunction %void\n%fn_param_images   = OpVariable %images_ptr UniformConstant\n%fn_param_samplers = OpVariable %samplers_ptr UniformConstant\n     %fn_param_out = OpVariable %v4float_ptr Output\n\n             %main = OpFunction %void None %fn_void\n     %main_prelude = OpLabel\n                %1 = OpAccessChain %image_ptr %fn_param_images %int_1\n                %2 = OpInBoundsAccessChain %sampler_ptr %fn_param_samplers %int_1\n                %3 = OpLoad %sampler %2\n                %4 = OpLoad %image %1\n                %5 = OpSampledImage %sampled_image %4 %3\n                %6 = OpImageSampleExplicitLod %v4float %5 %float_0_5_0_5 Lod %float_0\n                     OpStore %fn_param_out %6\n                     OpReturn\n                     OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.dynamic.toml",
    "content": "capabilities = \"TEXTURE_AND_SAMPLER_BINDING_ARRAY\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.runtime.slang",
    "content": "// Compiled with:\n// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/binding-arrays.runtime.spv naga/tests/in/spv/binding-arrays.runtime.slang\n// Disassembled with:\n// spirv-dis naga/tests/in/spv/binding-arrays.runtime.spv -o naga/tests/in/spv/binding-arrays.runtime.spvasm\n\n#language slang 2026\n\n[[vk::binding(0, 0)]] var textures: Texture2D[];\n[[vk::binding(1, 0)]] var linear_sampler: SamplerState;\n\nstruct VertexOutput {\n    var texture_coordinates: float2;\n    var texture_index: uint;\n};\n\n[[shader(\"pixel\")]]\nfunc main(input: VertexOutput) -> float4 {\n    return textures[input.texture_index].Sample(linear_sampler, input.texture_coordinates);\n}\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.runtime.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Khronos Slang Compiler; 0\n; Bound: 33\n; Schema: 0\n               OpCapability RuntimeDescriptorArray\n               OpCapability Shader\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\" %textures %linear_sampler %entryPointParam_main %input_texture_coordinates %input_texture_index\n               OpExecutionMode %main OriginUpperLeft\n               OpSource Slang 1\n               OpName %input_texture_coordinates \"input.texture_coordinates\"\n               OpName %input_texture_index \"input.texture_index\"\n               OpName %textures \"textures\"\n               OpName %linear_sampler \"linear_sampler\"\n               OpName %sampledImage \"sampledImage\"\n               OpName %sampled \"sampled\"\n               OpName %entryPointParam_main \"entryPointParam_main\"\n               OpName %main \"main\"\n               OpDecorate %input_texture_coordinates Location 0\n               OpDecorate %input_texture_index Location 1\n               OpDecorate %input_texture_index Flat\n               OpDecorate %textures Binding 0\n               OpDecorate %textures DescriptorSet 0\n               OpDecorate %linear_sampler Binding 1\n               OpDecorate %linear_sampler DescriptorSet 0\n               OpDecorate %entryPointParam_main Location 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v2float = OpTypeVector %float 2\n%_ptr_Input_v2float = OpTypePointer Input %v2float\n       %uint = OpTypeInt 32 0\n%_ptr_Input_uint = OpTypePointer Input %uint\n         %15 = OpTypeImage %float 2D 2 0 0 1 Unknown\n%_runtimearr_15 = OpTypeRuntimeArray %15\n%_ptr_UniformConstant__runtimearr_15 = OpTypePointer UniformConstant %_runtimearr_15\n%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15\n         %22 = OpTypeSampler\n%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22\n         %26 = OpTypeSampledImage %15\n    %v4float = OpTypeVector %float 4\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input\n%input_texture_index = OpVariable %_ptr_Input_uint Input\n   %textures = OpVariable %_ptr_UniformConstant__runtimearr_15 UniformConstant\n%linear_sampler = OpVariable %_ptr_UniformConstant_22 UniformConstant\n%entryPointParam_main = OpVariable %_ptr_Output_v4float Output\n       %main = OpFunction %void None %3\n          %4 = OpLabel\n          %7 = OpLoad %v2float %input_texture_coordinates\n         %11 = OpLoad %uint %input_texture_index\n         %19 = OpAccessChain %_ptr_UniformConstant_15 %textures %11\n         %21 = OpLoad %15 %19\n         %23 = OpLoad %22 %linear_sampler\n%sampledImage = OpSampledImage %26 %21 %23\n    %sampled = OpImageSampleImplicitLod %v4float %sampledImage %7 None\n               OpStore %entryPointParam_main %sampled\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.runtime.toml",
    "content": "capabilities = \"\"\"\n  TEXTURE_AND_SAMPLER_BINDING_ARRAY\n  | TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n\"\"\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.static.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n\n;; Make sure that we promote `OpTypeArray` of textures and samplers into\n;; `TypeInner::BindingArray` and support indexing it through `OpAccessChain`\n;; and `OpInBoundsAccessChain`.\n;;\n;; Code in here corresponds to, more or less:\n;;\n;; ```rust\n;; #[spirv(fragment)]\n;; pub fn main(\n;;     #[spirv(descriptor_set = 0, binding = 0)]\n;;     images: &[Image!(2D, type=f32, sampled); 256],\n;;     #[spirv(descriptor_set = 0, binding = 1)]\n;;     samplers: &[Sampler; 256],\n;;     out: &mut Vec4,\n;; ) {\n;;     let image = images[1];\n;;     let sampler = samplers[1];\n;;\n;;     *out = image.sample_by_lod(sampler, vec2(0.5, 0.5), 0.0);\n;; }\n;; ```\n\n                     OpCapability Shader\n                     OpMemoryModel Logical Simple\n                     OpEntryPoint Fragment %main \"main\" %fn_param_images %fn_param_samplers %fn_param_out\n                     OpExecutionMode %main OriginUpperLeft\n                     OpDecorate %images ArrayStride 4\n                     OpDecorate %samplers ArrayStride 4\n                     OpDecorate %fn_param_images DescriptorSet 0\n                     OpDecorate %fn_param_images Binding 0\n                     OpDecorate %fn_param_samplers DescriptorSet 0\n                     OpDecorate %fn_param_samplers Binding 1\n                     OpDecorate %fn_param_out Location 0\n\n             %void = OpTypeVoid\n\n            %float = OpTypeFloat 32\n          %v2float = OpTypeVector %float 2\n          %v4float = OpTypeVector %float 4\n      %v4float_ptr = OpTypePointer Output %v4float\n        %float_0_5 = OpConstant %float 0.5\n    %float_0_5_0_5 = OpConstantComposite %v2float %float_0_5 %float_0_5\n          %float_0 = OpConstant %float 0\n\n              %int = OpTypeInt 32 1\n            %int_1 = OpConstant %int 1\n\n             %uint = OpTypeInt 32 0\n         %uint_256 = OpConstant %uint 256\n\n            %image = OpTypeImage %float 2D 2 0 0 1 Unknown\n        %image_ptr = OpTypePointer UniformConstant %image\n           %images = OpTypeArray %image %uint_256\n       %images_ptr = OpTypePointer UniformConstant %images\n\n          %sampler = OpTypeSampler\n      %sampler_ptr = OpTypePointer UniformConstant %sampler\n         %samplers = OpTypeArray %sampler %uint_256\n     %samplers_ptr = OpTypePointer UniformConstant %samplers\n\n    %sampled_image = OpTypeSampledImage %image\n\n          %fn_void = OpTypeFunction %void\n%fn_param_images   = OpVariable %images_ptr UniformConstant\n%fn_param_samplers = OpVariable %samplers_ptr UniformConstant\n     %fn_param_out = OpVariable %v4float_ptr Output\n\n             %main = OpFunction %void None %fn_void\n     %main_prelude = OpLabel\n                %1 = OpAccessChain %image_ptr %fn_param_images %int_1\n                %2 = OpInBoundsAccessChain %sampler_ptr %fn_param_samplers %int_1\n                %3 = OpLoad %sampler %2\n                %4 = OpLoad %image %1\n                %5 = OpSampledImage %sampled_image %4 %3\n                %6 = OpImageSampleExplicitLod %v4float %5 %float_0_5_0_5 Lod %float_0\n                     OpStore %fn_param_out %6\n                     OpReturn\n                     OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/binding-arrays.static.toml",
    "content": "capabilities = \"TEXTURE_AND_SAMPLER_BINDING_ARRAY\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/builtin-accessed-outside-entrypoint.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n\n;; Ensure builtin binding isn't removed by unused gl_PerVertex builtin culling when\n;; the builtin is used in a function defined after (in the SPIRV) the entry point.\n;;\n;; Generated from the following glsl via `glslc` (without `-O` flag):\n;;\n;; ```glsl\n;; #version 450\n;;\n;; void builtin_usage() {\n;;     gl_Position = vec4(\n;;         (gl_VertexIndex == 0) ? -4.0 : 1.0,\n;;         (gl_VertexIndex == 2) ? 4.0 : -1.0,\n;;         0.0,\n;;         1.0\n;;     );\n;; }\n;;\n;; void main()\n;; {\n;;     builtin_usage();\n;; }\n;; ```\n;;\n; SPIR-V\n; Version: 1.0\n; Generator: Google Shaderc over Glslang; 11\n; Bound: 37\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Vertex %main \"main\" %_ %gl_VertexIndex\n               OpSource GLSL 450\n               OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n               OpSourceExtension \"GL_GOOGLE_include_directive\"\n               OpName %main \"main\"\n               OpName %builtin_usage_ \"builtin_usage(\"\n               OpName %gl_PerVertex \"gl_PerVertex\"\n               OpMemberName %gl_PerVertex 0 \"gl_Position\"\n               OpMemberName %gl_PerVertex 1 \"gl_PointSize\"\n               OpMemberName %gl_PerVertex 2 \"gl_ClipDistance\"\n               OpMemberName %gl_PerVertex 3 \"gl_CullDistance\"\n               OpName %_ \"\"\n               OpName %gl_VertexIndex \"gl_VertexIndex\"\n               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position\n               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize\n               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance\n               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance\n               OpDecorate %gl_PerVertex Block\n               OpDecorate %gl_VertexIndex BuiltIn VertexIndex\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n       %uint = OpTypeInt 32 0\n     %uint_1 = OpConstant %uint 1\n%_arr_float_uint_1 = OpTypeArray %float %uint_1\n%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1\n%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex\n          %_ = OpVariable %_ptr_Output_gl_PerVertex Output\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n%_ptr_Input_int = OpTypePointer Input %int\n%gl_VertexIndex = OpVariable %_ptr_Input_int Input\n       %bool = OpTypeBool\n   %float_n4 = OpConstant %float -4\n    %float_1 = OpConstant %float 1\n      %int_2 = OpConstant %int 2\n    %float_4 = OpConstant %float 4\n   %float_n1 = OpConstant %float -1\n    %float_0 = OpConstant %float 0\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n         %36 = OpFunctionCall %void %builtin_usage_\n               OpReturn\n               OpFunctionEnd\n%builtin_usage_ = OpFunction %void None %3\n          %7 = OpLabel\n         %20 = OpLoad %int %gl_VertexIndex\n         %22 = OpIEqual %bool %20 %int_0\n         %25 = OpSelect %float %22 %float_n4 %float_1\n         %26 = OpLoad %int %gl_VertexIndex\n         %28 = OpIEqual %bool %26 %int_2\n         %31 = OpSelect %float %28 %float_4 %float_n1\n         %33 = OpCompositeConstruct %v4float %25 %31 %float_0 %float_1\n         %35 = OpAccessChain %_ptr_Output_v4float %_ %int_0\n               OpStore %35 %33\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/builtin-accessed-outside-entrypoint.toml",
    "content": "[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/degrees.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Khronos Glslang Reference Front End; 10\n; Bound: 27\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\" %colour\n               OpSource GLSL 450\n               OpName %main \"main\"\n               OpName %deg \"deg\"\n               OpName %rad \"rad\"\n               OpName %deg_again \"deg_again\"\n               OpName %colour \"colour\"\n               OpDecorate %colour Location 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n%_ptr_Function_float = OpTypePointer Function %float\n   %float_15 = OpConstant %float 15\n    %v4float = OpTypeVector %float 4\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n     %colour = OpVariable %_ptr_Output_v4float Output\n    %v3float = OpTypeVector %float 3\n    %float_1 = OpConstant %float 1\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n        %deg = OpVariable %_ptr_Function_float Function\n        %rad = OpVariable %_ptr_Function_float Function\n  %deg_again = OpVariable %_ptr_Function_float Function\n               OpStore %deg %float_15\n         %11 = OpLoad %float %deg\n         %12 = OpExtInst %float %1 Radians %11\n               OpStore %rad %12\n         %14 = OpLoad %float %rad\n         %15 = OpExtInst %float %1 Degrees %14\n               OpStore %deg_again %15\n         %19 = OpLoad %float %deg_again\n         %21 = OpCompositeConstruct %v3float %19 %19 %19\n         %23 = OpCompositeExtract %float %21 0\n         %24 = OpCompositeExtract %float %21 1\n         %25 = OpCompositeExtract %float %21 2\n         %26 = OpCompositeConstruct %v4float %23 %24 %25 %float_1\n               OpStore %colour %26\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/degrees.toml",
    "content": "targets = \"\"\n"
  },
  {
    "path": "naga/tests/in/spv/do-while.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n\n;; Ensure that `do`-`while`-style loops, with conditional backedges, are properly\n;; supported, via `break if` (as `continuing { ... if c { break; } }` is illegal).\n;;\n;; The SPIR-V below was compiled from this GLSL fragment shader:\n;; ```glsl\n;; #version 450\n;;\n;; void f(bool cond) {\n;;     do {} while(cond);\n;; }\n;;\n;; void main() {\n;;     f(false);\n;; }\n;; ```\n\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\"\n               OpExecutionMode %main OriginUpperLeft\n               OpSource GLSL 450\n               OpName %main \"main\"\n               OpName %f_b1_ \"f(b1;\"\n               OpName %cond \"cond\"\n               OpName %param \"param\"\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n       %bool = OpTypeBool\n%_ptr_Function_bool = OpTypePointer Function %bool\n          %8 = OpTypeFunction %void %_ptr_Function_bool\n      %false = OpConstantFalse %bool\n\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n      %param = OpVariable %_ptr_Function_bool Function\n               OpStore %param %false\n         %19 = OpFunctionCall %void %f_b1_ %param\n               OpReturn\n               OpFunctionEnd\n\n      %f_b1_ = OpFunction %void None %8\n       %cond = OpFunctionParameter %_ptr_Function_bool\n\n         %11 = OpLabel\n               OpBranch %12\n\n         %12 = OpLabel\n               OpLoopMerge %14 %15 None\n               OpBranch %13\n\n         %13 = OpLabel\n               OpBranch %15\n\n;; This is the \"continuing\" block, and it contains a conditional branch between\n;; the backedge (back to the loop header) and the loop merge (\"break\") target.\n         %15 = OpLabel\n         %16 = OpLoad %bool %cond\n               OpBranchConditional %16 %12 %14\n\n         %14 = OpLabel\n               OpReturn\n\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/do-while.toml",
    "content": "targets = \"METAL | GLSL | HLSL | WGSL\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/dual-source-blending.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Khronos SPIR-V Tools Assembler; 0\n; Bound: 22\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\" %output0 %output1\n               OpExecutionMode %main OriginUpperLeft\n               OpSource GLSL 450\n               OpName %main \"main\"\n               OpName %output0 \"output0\"\n               OpName %output1 \"output1\"\n               OpDecorate %output0 Location 0\n               OpDecorate %output0 Index 0\n               OpDecorate %output1 Location 0\n               OpDecorate %output1 Index 1\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n    %output0 = OpVariable %_ptr_Output_v4float Output\n    %float_1 = OpConstant %float 1\n    %float_0 = OpConstant %float 0\n         %13 = OpConstantComposite %v4float %float_1 %float_0 %float_1 %float_0\n    %output1 = OpVariable %_ptr_Output_v4float Output\n         %15 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n               OpStore %output0 %13\n               OpStore %output1 %15\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/dual-source-blending.toml",
    "content": "capabilities = \"DUAL_SOURCE_BLENDING\"\n"
  },
  {
    "path": "naga/tests/in/spv/empty-global-name.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n\n;; Make sure we handle globals whose assigned name is \"\".\n;;\n;; In MSL, the anonymous global sometimes ends up looking like\n;;\n;;     struct Blah { int member; } ;\n;;\n;; where the null name just becomes an empty string before that last semicolon.\n;; This is, unfortunately, valid MSL, simply declaring the type Blah, so it will\n;; pass validation. However, an attempt to *use* the global will generate a\n;; garbage expression like \".member\", so we include a function that returns the\n;; member's value.\n\n                        OpCapability Shader\n                        OpMemoryModel Logical GLSL450\n                        OpEntryPoint GLCompute %main \"main\" %global\n                        OpExecutionMode %main LocalSize 1 1 1\n\n                        OpName %global \"\"\n                        OpDecorate %block Block\n                        OpMemberDecorate %block 0 Offset 0\n                        OpDecorate %global DescriptorSet 0\n                        OpDecorate %global Binding 0\n\n                %void = OpTypeVoid\n                 %int = OpTypeInt 32 1\n               %block = OpTypeStruct %int\n             %ptr_int = OpTypePointer StorageBuffer %int\n           %ptr_block = OpTypePointer StorageBuffer %block\n             %fn_void = OpTypeFunction %void\n              %fn_int = OpTypeFunction %int\n                %zero = OpConstant %int 0\n                 %one = OpConstant %int 1\n\n;; This global is said to have a name of \"\".\n              %global = OpVariable %ptr_block StorageBuffer\n\n                %main = OpFunction %void None %fn_void\n        %main_prelude = OpLabel\n          %member_ptr = OpAccessChain %ptr_int %global %zero\n          %member_val = OpLoad %int %member_ptr\n            %plus_one = OpIAdd %int %member_val %one\n                        OpStore %member_ptr %plus_one\n                        OpReturn\n                        OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/empty-global-name.toml",
    "content": "targets = \"HLSL | WGSL | METAL\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/f16-spv.comp",
    "content": "#version 460\n\n#extension GL_AMD_gpu_shader_half_float: enable \n\nlayout(set = 0, binding = 0) uniform A {\n    float16_t a_1;\n    f16vec2 a_vec2;\n    f16vec3 a_vec3;\n    f16vec4 a_vec4;\n    // So the rules here are particularly nasty for any f16 matries in uniform buffers\n    // as the stride is always rounded up to 16, meaning that _every_ f16 matrix in a uniform\n    // buffer is over-aligned to what naga-ir wants.\n    // \n    // This is https://github.com/gfx-rs/wgpu/issues/4375.\n\n    // f16mat2 a_mat2;\n    // f16mat2x3 a_mat2x3;\n    // f16mat2x4 a_mat2x4;\n    // f16mat3x2 a_mat3x2;\n    // f16mat3 a_mat3;\n    // f16mat3x4 a_mat3x4;\n    // f16mat4x2 a_mat4x2;\n    // f16mat4x3 a_mat4x3;\n    // f16mat4 a_mat4;\n};\n\nlayout(set = 0, binding = 1) buffer B {\n    float16_t b_1;\n    f16vec2 b_vec2;\n    f16vec3 b_vec3;\n    f16vec4 b_vec4;\n    f16mat2 b_mat2;\n    f16mat2x3 b_mat2x3;\n    f16mat2x4 b_mat2x4;\n    f16mat3x2 b_mat3x2;\n    f16mat3 b_mat3;\n    f16mat3x4 b_mat3x4;\n    f16mat4x2 b_mat4x2;\n    f16mat4x3 b_mat4x3;\n    f16mat4 b_mat4;\n};\n\nvoid main() {\n    b_1 = a_1;\n    b_vec2 = a_vec2;\n    b_vec3 = a_vec3;\n    b_vec4 = a_vec4;\n    // b_mat2 = a_mat2;\n    // b_mat2x3 = a_mat2x3;\n    // b_mat2x4 = a_mat2x4;\n    // b_mat3x2 = a_mat3x2;\n    // b_mat3 = a_mat3;\n    // b_mat3x4 = a_mat3x4;\n    // b_mat4x2 = a_mat4x2;\n    // b_mat4x3 = a_mat4x3;\n    // b_mat4 = a_mat4;\n}\n"
  },
  {
    "path": "naga/tests/in/spv/f16-spv.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Google Shaderc over Glslang; 11\n; Bound: 46\n; Schema: 0\n               OpCapability Shader\n               OpCapability StorageBuffer16BitAccess\n               OpCapability UniformAndStorageBuffer16BitAccess\n               OpExtension \"SPV_KHR_16bit_storage\"\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint GLCompute %main \"main\"\n               OpExecutionMode %main LocalSize 1 1 1\n               OpSource GLSL 460\n               OpSourceExtension \"GL_AMD_gpu_shader_half_float\"\n               OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n               OpSourceExtension \"GL_GOOGLE_include_directive\"\n               OpName %main \"main\"\n               OpName %B \"B\"\n               OpMemberName %B 0 \"b_1\"\n               OpMemberName %B 1 \"b_vec2\"\n               OpMemberName %B 2 \"b_vec3\"\n               OpMemberName %B 3 \"b_vec4\"\n               OpMemberName %B 4 \"b_mat2\"\n               OpMemberName %B 5 \"b_mat2x3\"\n               OpMemberName %B 6 \"b_mat2x4\"\n               OpMemberName %B 7 \"b_mat3x2\"\n               OpMemberName %B 8 \"b_mat3\"\n               OpMemberName %B 9 \"b_mat3x4\"\n               OpMemberName %B 10 \"b_mat4x2\"\n               OpMemberName %B 11 \"b_mat4x3\"\n               OpMemberName %B 12 \"b_mat4\"\n               OpName %_ \"\"\n               OpName %A \"A\"\n               OpMemberName %A 0 \"a_1\"\n               OpMemberName %A 1 \"a_vec2\"\n               OpMemberName %A 2 \"a_vec3\"\n               OpMemberName %A 3 \"a_vec4\"\n               OpName %__0 \"\"\n               OpDecorate %B BufferBlock\n               OpMemberDecorate %B 0 Offset 0\n               OpMemberDecorate %B 1 Offset 4\n               OpMemberDecorate %B 2 Offset 8\n               OpMemberDecorate %B 3 Offset 16\n               OpMemberDecorate %B 4 ColMajor\n               OpMemberDecorate %B 4 MatrixStride 4\n               OpMemberDecorate %B 4 Offset 24\n               OpMemberDecorate %B 5 ColMajor\n               OpMemberDecorate %B 5 MatrixStride 8\n               OpMemberDecorate %B 5 Offset 32\n               OpMemberDecorate %B 6 ColMajor\n               OpMemberDecorate %B 6 MatrixStride 8\n               OpMemberDecorate %B 6 Offset 48\n               OpMemberDecorate %B 7 ColMajor\n               OpMemberDecorate %B 7 MatrixStride 4\n               OpMemberDecorate %B 7 Offset 64\n               OpMemberDecorate %B 8 ColMajor\n               OpMemberDecorate %B 8 MatrixStride 8\n               OpMemberDecorate %B 8 Offset 80\n               OpMemberDecorate %B 9 ColMajor\n               OpMemberDecorate %B 9 MatrixStride 8\n               OpMemberDecorate %B 9 Offset 104\n               OpMemberDecorate %B 10 ColMajor\n               OpMemberDecorate %B 10 MatrixStride 4\n               OpMemberDecorate %B 10 Offset 128\n               OpMemberDecorate %B 11 ColMajor\n               OpMemberDecorate %B 11 MatrixStride 8\n               OpMemberDecorate %B 11 Offset 144\n               OpMemberDecorate %B 12 ColMajor\n               OpMemberDecorate %B 12 MatrixStride 8\n               OpMemberDecorate %B 12 Offset 176\n               OpDecorate %_ Binding 1\n               OpDecorate %_ DescriptorSet 0\n               OpDecorate %A Block\n               OpMemberDecorate %A 0 Offset 0\n               OpMemberDecorate %A 1 Offset 4\n               OpMemberDecorate %A 2 Offset 8\n               OpMemberDecorate %A 3 Offset 16\n               OpDecorate %__0 Binding 0\n               OpDecorate %__0 DescriptorSet 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n       %half = OpTypeFloat 16\n     %v2half = OpTypeVector %half 2\n     %v3half = OpTypeVector %half 3\n     %v4half = OpTypeVector %half 4\n %mat2v2half = OpTypeMatrix %v2half 2\n %mat2v3half = OpTypeMatrix %v3half 2\n %mat2v4half = OpTypeMatrix %v4half 2\n %mat3v2half = OpTypeMatrix %v2half 3\n %mat3v3half = OpTypeMatrix %v3half 3\n %mat3v4half = OpTypeMatrix %v4half 3\n %mat4v2half = OpTypeMatrix %v2half 4\n %mat4v3half = OpTypeMatrix %v3half 4\n %mat4v4half = OpTypeMatrix %v4half 4\n          %B = OpTypeStruct %half %v2half %v3half %v4half %mat2v2half %mat2v3half %mat2v4half %mat3v2half %mat3v3half %mat3v4half %mat4v2half %mat4v3half %mat4v4half\n%_ptr_Uniform_B = OpTypePointer Uniform %B\n          %_ = OpVariable %_ptr_Uniform_B Uniform\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n          %A = OpTypeStruct %half %v2half %v3half %v4half\n%_ptr_Uniform_A = OpTypePointer Uniform %A\n        %__0 = OpVariable %_ptr_Uniform_A Uniform\n%_ptr_Uniform_half = OpTypePointer Uniform %half\n      %int_1 = OpConstant %int 1\n%_ptr_Uniform_v2half = OpTypePointer Uniform %v2half\n      %int_2 = OpConstant %int 2\n%_ptr_Uniform_v3half = OpTypePointer Uniform %v3half\n      %int_3 = OpConstant %int 3\n%_ptr_Uniform_v4half = OpTypePointer Uniform %v4half\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n         %28 = OpAccessChain %_ptr_Uniform_half %__0 %int_0\n         %29 = OpLoad %half %28\n         %30 = OpAccessChain %_ptr_Uniform_half %_ %int_0\n               OpStore %30 %29\n         %33 = OpAccessChain %_ptr_Uniform_v2half %__0 %int_1\n         %34 = OpLoad %v2half %33\n         %35 = OpAccessChain %_ptr_Uniform_v2half %_ %int_1\n               OpStore %35 %34\n         %38 = OpAccessChain %_ptr_Uniform_v3half %__0 %int_2\n         %39 = OpLoad %v3half %38\n         %40 = OpAccessChain %_ptr_Uniform_v3half %_ %int_2\n               OpStore %40 %39\n         %43 = OpAccessChain %_ptr_Uniform_v4half %__0 %int_3\n         %44 = OpLoad %v4half %43\n         %45 = OpAccessChain %_ptr_Uniform_v4half %_ %int_3\n               OpStore %45 %44\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/f16-spv.toml",
    "content": "capabilities = \"SHADER_FLOAT16\"\n"
  },
  {
    "path": "naga/tests/in/spv/fetch_depth.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 56\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint GLCompute %1 \"cull::fetch_depth\" %2 %3 %4\n               OpExecutionMode %1 LocalSize 32 1 1\n               OpDecorate %_struct_10 Block\n               OpMemberDecorate %_struct_10 0 Offset 0\n               OpDecorate %_struct_11 Block\n               OpMemberDecorate %_struct_11 0 Offset 0\n               OpDecorate %2 Binding 0\n               OpDecorate %2 DescriptorSet 0\n               OpDecorate %3 NonWritable\n               OpDecorate %3 Binding 1\n               OpDecorate %3 DescriptorSet 0\n               OpDecorate %4 Binding 2\n               OpDecorate %4 DescriptorSet 0\n       %uint = OpTypeInt 32 0\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n       %void = OpTypeVoid\n         %16 = OpTypeFunction %void\n     %uint_0 = OpConstant %uint 0\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n %_struct_10 = OpTypeStruct %float\n%_ptr_StorageBuffer__struct_10 = OpTypePointer StorageBuffer %_struct_10\n     %v2uint = OpTypeVector %uint 2\n %_struct_11 = OpTypeStruct %v2uint\n%_ptr_StorageBuffer__struct_11 = OpTypePointer StorageBuffer %_struct_11\n         %24 = OpTypeImage %float 2D 1 0 0 1 Unknown\n%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24\n%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float\n          %2 = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer\n%_ptr_StorageBuffer_v2uint = OpTypePointer StorageBuffer %v2uint\n          %3 = OpVariable %_ptr_StorageBuffer__struct_11 StorageBuffer\n          %4 = OpVariable %_ptr_UniformConstant_24 UniformConstant\n          %1 = OpFunction %void None %16\n         %32 = OpLabel\n         %33 = OpInBoundsAccessChain %_ptr_StorageBuffer_float %2 %uint_0\n         %34 = OpInBoundsAccessChain %_ptr_StorageBuffer_v2uint %3 %uint_0\n         %35 = OpLoad %v2uint %34\n         %54 = OpLoad %24 %4\n         %55 = OpImageFetch %v4float %54 %35 Lod %int_0\n         %38 = OpCompositeExtract %float %55 0\n               OpStore %33 %38\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/fetch_depth.toml",
    "content": "targets = \"IR | SPIRV | METAL | HLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/spv/gather-cmp.slang",
    "content": "// Compiled with:\n// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather-cmp.spv naga/tests/in/spv/gather-cmp.slang\n// Disassembled with:\n// spirv-dis naga/tests/in/spv/gather-cmp.spv -o naga/tests/in/spv/gather-cmp.spvasm\n\n#language slang 2026\n\n[[vk::binding(0, 0)]] var texture: Texture2D;\n[[vk::binding(1, 0)]] var depth_sampler: SamplerComparisonState;\n\nstruct VertexOutput {\n    var texture_coordinates: float2;\n};\n\n[[shader(\"pixel\")]]\nfunc main(input: VertexOutput) -> float4 {\n    return texture.GatherCmp(depth_sampler, input.texture_coordinates, 0.5);\n}\n"
  },
  {
    "path": "naga/tests/in/spv/gather-cmp.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Khronos Slang Compiler; 0\n; Bound: 27\n; Schema: 0\n               OpCapability Shader\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\" %texture %depth_sampler %entryPointParam_main %input_texture_coordinates\n               OpExecutionMode %main OriginUpperLeft\n               OpSource Slang 1\n               OpName %input_texture_coordinates \"input.texture_coordinates\"\n               OpName %texture \"texture\"\n               OpName %depth_sampler \"depth_sampler\"\n               OpName %sampledImage \"sampledImage\"\n               OpName %entryPointParam_main \"entryPointParam_main\"\n               OpName %main \"main\"\n               OpDecorate %input_texture_coordinates Location 0\n               OpDecorate %texture Binding 0\n               OpDecorate %texture DescriptorSet 0\n               OpDecorate %depth_sampler Binding 1\n               OpDecorate %depth_sampler DescriptorSet 0\n               OpDecorate %entryPointParam_main Location 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v2float = OpTypeVector %float 2\n%_ptr_Input_v2float = OpTypePointer Input %v2float\n         %10 = OpTypeImage %float 2D 2 0 0 1 Unknown\n%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10\n         %14 = OpTypeSampler\n%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14\n         %18 = OpTypeSampledImage %10\n    %v4float = OpTypeVector %float 4\n  %float_0_5 = OpConstant %float 0.5\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input\n    %texture = OpVariable %_ptr_UniformConstant_10 UniformConstant\n%depth_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant\n%entryPointParam_main = OpVariable %_ptr_Output_v4float Output\n       %main = OpFunction %void None %3\n          %4 = OpLabel\n          %7 = OpLoad %v2float %input_texture_coordinates\n         %11 = OpLoad %10 %texture\n         %15 = OpLoad %14 %depth_sampler\n%sampledImage = OpSampledImage %18 %11 %15\n         %21 = OpImageDrefGather %v4float %sampledImage %7 %float_0_5\n               OpStore %entryPointParam_main %21\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/gather-cmp.toml",
    "content": "\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/gather.slang",
    "content": "// Compiled with:\n// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather.spv naga/tests/in/spv/gather.slang\n// Disassembled with:\n// spirv-dis naga/tests/in/spv/gather.spv -o naga/tests/in/spv/gather.spvasm\n\n#language slang 2026\n\n[[vk::binding(0, 0)]] var texture: Texture2D;\n[[vk::binding(1, 0)]] var linear_sampler: SamplerState;\n\nstruct VertexOutput {\n    var texture_coordinates: float2;\n};\n\n[[shader(\"pixel\")]]\nfunc main(input: VertexOutput) -> float4 {\n    return texture.GatherGreen(linear_sampler, input.texture_coordinates);\n}\n"
  },
  {
    "path": "naga/tests/in/spv/gather.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Khronos Slang Compiler; 0\n; Bound: 28\n; Schema: 0\n               OpCapability Shader\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\" %texture %linear_sampler %entryPointParam_main %input_texture_coordinates\n               OpExecutionMode %main OriginUpperLeft\n               OpSource Slang 1\n               OpName %input_texture_coordinates \"input.texture_coordinates\"\n               OpName %texture \"texture\"\n               OpName %linear_sampler \"linear_sampler\"\n               OpName %sampledImage \"sampledImage\"\n               OpName %entryPointParam_main \"entryPointParam_main\"\n               OpName %main \"main\"\n               OpDecorate %input_texture_coordinates Location 0\n               OpDecorate %texture Binding 0\n               OpDecorate %texture DescriptorSet 0\n               OpDecorate %linear_sampler Binding 1\n               OpDecorate %linear_sampler DescriptorSet 0\n               OpDecorate %entryPointParam_main Location 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v2float = OpTypeVector %float 2\n%_ptr_Input_v2float = OpTypePointer Input %v2float\n         %10 = OpTypeImage %float 2D 2 0 0 1 Unknown\n%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10\n         %14 = OpTypeSampler\n%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14\n         %18 = OpTypeSampledImage %10\n    %v4float = OpTypeVector %float 4\n        %int = OpTypeInt 32 1\n      %int_1 = OpConstant %int 1\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input\n    %texture = OpVariable %_ptr_UniformConstant_10 UniformConstant\n%linear_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant\n%entryPointParam_main = OpVariable %_ptr_Output_v4float Output\n       %main = OpFunction %void None %3\n          %4 = OpLabel\n          %7 = OpLoad %v2float %input_texture_coordinates\n         %11 = OpLoad %10 %texture\n         %15 = OpLoad %14 %linear_sampler\n%sampledImage = OpSampledImage %18 %11 %15\n         %21 = OpImageGather %v4float %sampledImage %7 %int_1\n               OpStore %entryPointParam_main %21\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/gather.toml",
    "content": "\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/inv-hyperbolic-trig-functions.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Khronos Glslang Reference Front End; 10\n; Bound: 19\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %main \"main\"\n               OpSource GLSL 450\n               OpName %main \"main\"\n               OpName %b \"b\"\n               OpName %a \"a\"\n               OpName %c \"c\"\n               OpName %d \"d\"\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n%_ptr_Function_float = OpTypePointer Function %float\n%_ptr_Private_float = OpTypePointer Private %float\n          %a = OpVariable %_ptr_Private_float Private\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n          %b = OpVariable %_ptr_Function_float Function\n          %c = OpVariable %_ptr_Function_float Function\n          %d = OpVariable %_ptr_Function_float Function\n         %11 = OpLoad %float %a\n         %12 = OpExtInst %float %1 Asinh %11\n               OpStore %b %12\n         %14 = OpLoad %float %a\n         %15 = OpExtInst %float %1 Acosh %14\n               OpStore %c %15\n         %17 = OpLoad %float %a\n         %18 = OpExtInst %float %1 Atanh %17\n               OpStore %d %18\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/inv-hyperbolic-trig-functions.toml",
    "content": "targets = \"HLSL | WGSL\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/load-ms-texture.slang",
    "content": "// Compiled with:\n// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/load-ms-texture.spv naga/tests/in/spv/load-ms-texture.slang\n// Disassembled with:\n// spirv-dis naga/tests/in/spv/load-ms-texture.spv -o naga/tests/in/spv/load-ms-texture.spvasm\n#language slang 2026\n\n[[vk::binding(0, 0)]] var texture: Texture2DMS;\n\n[[shader(\"pixel\")]]\nfunc fs_main(float4 position : SV_Position) -> float4 {\n    let pixel_coord = int2(position.xy);\n    var color: float4;\n\n    for (var index: int = 0; index < 8; index++) {\n        color += texture.Load(pixel_coord, index);\n    }\n\n    color = color / 8.0;\n\n    return color;\n}\n"
  },
  {
    "path": "naga/tests/in/spv/load-ms-texture.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Khronos Slang Compiler; 0\n; Bound: 65\n; Schema: 0\n               OpCapability StorageImageMultisample\n               OpCapability Shader\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %fs_main \"main\" %texture %entryPointParam_fs_main %gl_FragCoord\n               OpExecutionMode %fs_main OriginUpperLeft\n               OpSource Slang 1\n               OpName %index \"index\"\n               OpName %index \"index\"\n               OpName %color \"color\"\n               OpName %color \"color\"\n               OpName %color_0 \"color\"\n               OpName %entryPointParam_fs_main \"entryPointParam_fs_main\"\n               OpName %texture \"texture\"\n               OpName %sampled \"sampled\"\n               OpName %color_1 \"color\"\n               OpName %fs_main \"fs_main\"\n               OpDecorate %gl_FragCoord BuiltIn FragCoord\n               OpDecorate %entryPointParam_fs_main Location 0\n               OpDecorate %texture Binding 0\n               OpDecorate %texture DescriptorSet 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n        %int = OpTypeInt 32 1\n%_ptr_Function_int = OpTypePointer Function %int\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n%_ptr_Function_v4float = OpTypePointer Function %v4float\n%_ptr_Input_v4float = OpTypePointer Input %v4float\n    %v2float = OpTypeVector %float 2\n      %v2int = OpTypeVector %int 2\n      %int_0 = OpConstant %int 0\n       %bool = OpTypeBool\n      %int_8 = OpConstant %int 8\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n         %45 = OpTypeImage %float 2D 2 0 1 1 Unknown\n%_ptr_UniformConstant_45 = OpTypePointer UniformConstant %45\n      %int_1 = OpConstant %int 1\n%gl_FragCoord = OpVariable %_ptr_Input_v4float Input\n%entryPointParam_fs_main = OpVariable %_ptr_Output_v4float Output\n    %texture = OpVariable %_ptr_UniformConstant_45 UniformConstant\n%float_0_125 = OpConstant %float 0.125\n         %64 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125\n    %fs_main = OpFunction %void None %3\n          %4 = OpLabel\n      %index = OpVariable %_ptr_Function_int Function\n      %color = OpVariable %_ptr_Function_v4float Function\n         %22 = OpLoad %v4float %gl_FragCoord\n         %26 = OpVectorShuffle %v2float %22 %22 0 1\n         %28 = OpConvertFToS %v2int %26\n               OpStore %index %int_0\n               OpBranch %12\n         %12 = OpLabel\n               OpLoopMerge %17 %21 None\n               OpBranch %13\n         %13 = OpLabel\n               OpBranch %14\n         %14 = OpLabel\n               OpBranch %15\n         %15 = OpLabel\n         %31 = OpLoad %int %index\n         %33 = OpSLessThan %bool %31 %int_8\n               OpSelectionMerge %18 None\n               OpBranchConditional %33 %18 %16\n         %16 = OpLabel\n               OpBranch %17\n         %18 = OpLabel\n         %46 = OpLoad %45 %texture\n         %49 = OpLoad %int %index\n    %sampled = OpImageFetch %v4float %46 %28 Sample %49\n         %52 = OpLoad %v4float %color\n    %color_1 = OpFAdd %v4float %52 %sampled\n               OpBranch %19\n         %19 = OpLabel\n               OpBranch %20\n         %20 = OpLabel\n         %56 = OpLoad %int %index\n         %57 = OpIAdd %int %56 %int_1\n               OpStore %index %57\n               OpStore %color %color_1\n               OpBranch %21\n         %21 = OpLabel\n               OpBranch %12\n         %17 = OpLabel\n         %37 = OpLoad %v4float %color\n    %color_0 = OpFMul %v4float %37 %64\n               OpStore %entryPointParam_fs_main %color_0\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/load-ms-texture.toml",
    "content": "\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/non-semantic-debug.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Google spiregg; 0\n; Bound: 28\n; Schema: 0\n               OpCapability Shader\n               OpExtension \"SPV_KHR_non_semantic_info\"\n          %1 = OpExtInstImport \"NonSemantic.Shader.DebugInfo.100\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint GLCompute %main \"main\"\n               OpExecutionMode %main LocalSize 1 1 1\n          %3 = OpString \"thing.hlsl\"\n          %4 = OpString \"[numthreads(1, 1, 1)]\nvoid main()\n{\n}\n\"\n          %5 = OpString \"main\"\n          %6 = OpString \"\"\n          %7 = OpString \"__dxc_setup\"\n          %8 = OpString \"3f85295c\"\n          %9 = OpString \" -E main -T cs_6_0 -spirv -fspv-debug=vulkan-with-source -Fo out.spirv -Qembed_debug\"\n               OpName %main \"main\"\n       %void = OpTypeVoid\n       %uint = OpTypeInt 32 0\n     %uint_3 = OpConstant %uint 3\n     %uint_1 = OpConstant %uint 1\n     %uint_4 = OpConstant %uint 4\n     %uint_5 = OpConstant %uint 5\n     %uint_2 = OpConstant %uint 2\n         %22 = OpTypeFunction %void\n         %11 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void\n         %13 = OpExtInst %void %1 DebugSource %3 %4\n         %14 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %13 %uint_5\n         %18 = OpExtInst %void %1 DebugFunction %7 %11 %13 %uint_2 %uint_1 %14 %6 %uint_3 %uint_3\n         %20 = OpExtInst %void %1 DebugEntryPoint %18 %14 %8 %9\n       %main = OpFunction %void None %22\n         %23 = OpLabel\n         %26 = OpExtInst %void %1 DebugScope %18\n         %24 = OpExtInst %void %1 DebugFunctionDefinition %18 %main\n         %25 = OpExtInst %void %1 DebugLine %13 %uint_4 %uint_4 %uint_1 %uint_1\n               OpReturn\n         %27 = OpExtInst %void %1 DebugNoScope\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/per-vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 22\nOpCapability Shader\nOpCapability FragmentBarycentricKHR\nOpExtension \"SPV_KHR_fragment_shader_barycentric\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %14 \"fs_main\" %9 %12\nOpExecutionMode %14 OriginUpperLeft\nOpDecorate %4 ArrayStride 4\nOpDecorate %9 Location 0\nOpDecorate %9 PerVertexKHR\nOpDecorate %12 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  3\n%4 = OpTypeArray %3 %5\n%7 = OpTypeVector %3 4\n%10 = OpTypePointer Input %4\n%9 = OpVariable  %10  Input\n%13 = OpTypePointer Output %7\n%12 = OpVariable  %13  Output\n%15 = OpTypeFunction %2\n%16 = OpConstant  %3  1\n%14 = OpFunction  %2  None %15\n%8 = OpLabel\n%11 = OpLoad  %4  %9\nOpBranch %17\n%17 = OpLabel\n%18 = OpCompositeExtract  %3  %11 0\n%19 = OpCompositeExtract  %3  %11 1\n%20 = OpCompositeExtract  %3  %11 2\n%21 = OpCompositeConstruct  %7  %18 %19 %20 %16\nOpStore %12 %21\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/in/spv/per-vertex.toml",
    "content": "capabilities = \"PER_VERTEX\"\ntargets = \"SPIRV | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/spv/quad-vert.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Khronos Glslang Reference Front End; 10\n; Bound: 31\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Vertex %main \"main\" %v_uv %a_uv %_ %a_pos\n               OpSource GLSL 460\n               OpName %main \"main\"\n               OpName %v_uv \"v_uv\"\n               OpName %a_uv \"a_uv\"\n               OpName %gl_PerVertex \"gl_PerVertex\"\n               OpMemberName %gl_PerVertex 0 \"gl_Position\"\n               OpMemberName %gl_PerVertex 1 \"gl_PointSize\"\n               OpMemberName %gl_PerVertex 2 \"gl_ClipDistance\"\n               OpMemberName %gl_PerVertex 3 \"gl_CullDistance\"\n               OpName %_ \"\"\n               OpName %a_pos \"a_pos\"\n               OpDecorate %v_uv Location 0\n               OpDecorate %a_uv Location 1\n               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position\n               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize\n               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance\n               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance\n               OpDecorate %gl_PerVertex Block\n               OpDecorate %a_pos Location 0\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v2float = OpTypeVector %float 2\n%_ptr_Output_v2float = OpTypePointer Output %v2float\n       %v_uv = OpVariable %_ptr_Output_v2float Output\n%_ptr_Input_v2float = OpTypePointer Input %v2float\n       %a_uv = OpVariable %_ptr_Input_v2float Input\n    %v4float = OpTypeVector %float 4\n       %uint = OpTypeInt 32 0\n     %uint_1 = OpConstant %uint 1\n%_arr_float_uint_1 = OpTypeArray %float %uint_1\n%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1\n%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex\n          %_ = OpVariable %_ptr_Output_gl_PerVertex Output\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n      %a_pos = OpVariable %_ptr_Input_v2float Input\n    %float_0 = OpConstant %float 0\n    %float_1 = OpConstant %float 1\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n         %12 = OpLoad %v2float %a_uv\n               OpStore %v_uv %12\n         %23 = OpLoad %v2float %a_pos\n         %26 = OpCompositeExtract %float %23 0\n         %27 = OpCompositeExtract %float %23 1\n         %28 = OpCompositeConstruct %v4float %26 %27 %float_0 %float_1\n         %30 = OpAccessChain %_ptr_Output_v4float %_ %int_0\n               OpStore %30 %28\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/quad-vert.toml",
    "content": "targets = \"METAL | GLSL | HLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/spv/shadow.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Khronos SPIR-V Tools Assembler; 0\n; Bound: 221\n; Schema: 0\n               OpCapability Shader\n               OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Fragment %fs_main \"fs_main\" %in_normal_fs %in_position_fs %out_color_fs\n               OpExecutionMode %fs_main OriginUpperLeft\n               OpSource GLSL 450\n               OpName %t_shadow \"t_shadow\"\n               OpName %sampler_shadow \"sampler_shadow\"\n               OpName %color \"color\"\n               OpName %i \"i\"\n               OpName %Globals \"Globals\"\n               OpMemberName %Globals 0 \"num_lights\"\n               OpName %u_globals \"u_globals\"\n               OpName %Light \"Light\"\n               OpMemberName %Light 0 \"proj\"\n               OpMemberName %Light 1 \"pos\"\n               OpMemberName %Light 2 \"color\"\n               OpName %Lights \"Lights\"\n               OpMemberName %Lights 0 \"data\"\n               OpName %s_lights \"s_lights\"\n               OpName %in_position_fs \"in_position_fs\"\n               OpName %in_normal_fs \"in_normal_fs\"\n               OpName %out_color_fs \"out_color_fs\"\n               OpName %fs_main \"fs_main\"\n               OpDecorate %t_shadow DescriptorSet 0\n               OpDecorate %t_shadow Binding 2\n               OpDecorate %sampler_shadow DescriptorSet 0\n               OpDecorate %sampler_shadow Binding 3\n               OpDecorate %Globals Block\n               OpMemberDecorate %Globals 0 Offset 0\n               OpDecorate %u_globals DescriptorSet 0\n               OpDecorate %u_globals Binding 0\n               OpMemberDecorate %Light 0 Offset 0\n               OpMemberDecorate %Light 0 ColMajor\n               OpMemberDecorate %Light 0 MatrixStride 16\n               OpMemberDecorate %Light 1 Offset 64\n               OpMemberDecorate %Light 2 Offset 80\n               OpDecorate %_runtimearr_Light ArrayStride 96\n               OpDecorate %Lights BufferBlock\n               OpMemberDecorate %Lights 0 Offset 0\n               OpMemberDecorate %Lights 0 NonWritable\n               OpDecorate %s_lights DescriptorSet 0\n               OpDecorate %s_lights Binding 1\n               OpDecorate %in_position_fs Location 1\n               OpDecorate %in_normal_fs Location 0\n               OpDecorate %out_color_fs Location 0\n       %void = OpTypeVoid\n      %float = OpTypeFloat 32\n    %float_0 = OpConstant %float 0\n    %float_1 = OpConstant %float 1\n  %float_0_5 = OpConstant %float 0.5\n %float_n0_5 = OpConstant %float -0.5\n%float_0_0500000007 = OpConstant %float 0.0500000007\n    %v3float = OpTypeVector %float 3\n          %9 = OpConstantComposite %v3float %float_0_0500000007 %float_0_0500000007 %float_0_0500000007\n       %uint = OpTypeInt 32 0\n    %uint_10 = OpConstant %uint 10\n     %uint_0 = OpConstant %uint 0\n     %uint_1 = OpConstant %uint 1\n    %v4float = OpTypeVector %float 4\n         %19 = OpTypeFunction %float %uint %v4float\n       %bool = OpTypeBool\n         %27 = OpTypeImage %float 2D 1 1 0 1 Unknown\n%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27\n   %t_shadow = OpVariable %_ptr_UniformConstant_27 UniformConstant\n         %31 = OpTypeSampledImage %27\n         %32 = OpTypeSampler\n%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32\n%sampler_shadow = OpVariable %_ptr_UniformConstant_32 UniformConstant\n    %v2float = OpTypeVector %float 2\n        %int = OpTypeInt 32 1\n  %float_0_0 = OpConstant %float 0\n%_ptr_Function_v3float = OpTypePointer Function %v3float\n%_ptr_Function_uint = OpTypePointer Function %uint\n         %65 = OpTypeFunction %void\n     %v4uint = OpTypeVector %uint 4\n    %Globals = OpTypeStruct %v4uint\n%_ptr_Uniform_Globals = OpTypePointer Uniform %Globals\n  %u_globals = OpVariable %_ptr_Uniform_Globals Uniform\n%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint\n      %int_0 = OpConstant %int 0\n%_ptr_Uniform_uint = OpTypePointer Uniform %uint\n    %int_0_0 = OpConstant %int 0\n%mat4v4float = OpTypeMatrix %v4float 4\n      %Light = OpTypeStruct %mat4v4float %v4float %v4float\n%_runtimearr_Light = OpTypeRuntimeArray %Light\n     %Lights = OpTypeStruct %_runtimearr_Light\n%_ptr_StorageBuffer_Lights = OpTypePointer StorageBuffer %Lights\n   %s_lights = OpVariable %_ptr_StorageBuffer_Lights StorageBuffer\n%_ptr_StorageBuffer__runtimearr_Light = OpTypePointer StorageBuffer %_runtimearr_Light\n    %int_0_1 = OpConstant %int 0\n%_ptr_StorageBuffer_Light = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_mat4v4float = OpTypePointer StorageBuffer %mat4v4float\n    %int_0_2 = OpConstant %int 0\n%_ptr_Input_v4float = OpTypePointer Input %v4float\n%in_position_fs = OpVariable %_ptr_Input_v4float Input\n%_ptr_Input_v3float = OpTypePointer Input %v3float\n%in_normal_fs = OpVariable %_ptr_Input_v3float Input\n%_ptr_StorageBuffer__runtimearr_Light_0 = OpTypePointer StorageBuffer %_runtimearr_Light\n    %int_0_3 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_0 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float\n      %int_1 = OpConstant %int 1\n%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float\n    %int_0_4 = OpConstant %int 0\n%_ptr_StorageBuffer__runtimearr_Light_1 = OpTypePointer StorageBuffer %_runtimearr_Light\n    %int_0_5 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_1 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float_0 = OpTypePointer StorageBuffer %v4float\n    %int_1_0 = OpConstant %int 1\n%_ptr_StorageBuffer_float_0 = OpTypePointer StorageBuffer %float\n    %int_1_1 = OpConstant %int 1\n%_ptr_StorageBuffer__runtimearr_Light_2 = OpTypePointer StorageBuffer %_runtimearr_Light\n    %int_0_6 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_2 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float_1 = OpTypePointer StorageBuffer %v4float\n    %int_1_2 = OpConstant %int 1\n%_ptr_StorageBuffer_float_1 = OpTypePointer StorageBuffer %float\n      %int_2 = OpConstant %int 2\n%_ptr_Input_float = OpTypePointer Input %float\n    %int_0_7 = OpConstant %int 0\n%_ptr_Input_float_0 = OpTypePointer Input %float\n    %int_1_3 = OpConstant %int 1\n%_ptr_Input_float_1 = OpTypePointer Input %float\n    %int_2_0 = OpConstant %int 2\n%_ptr_StorageBuffer__runtimearr_Light_3 = OpTypePointer StorageBuffer %_runtimearr_Light\n    %int_0_8 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_3 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float_2 = OpTypePointer StorageBuffer %v4float\n    %int_2_1 = OpConstant %int 2\n%_ptr_StorageBuffer_float_2 = OpTypePointer StorageBuffer %float\n    %int_0_9 = OpConstant %int 0\n%_ptr_StorageBuffer__runtimearr_Light_4 = OpTypePointer StorageBuffer %_runtimearr_Light\n   %int_0_10 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_4 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float_3 = OpTypePointer StorageBuffer %v4float\n    %int_2_2 = OpConstant %int 2\n%_ptr_StorageBuffer_float_3 = OpTypePointer StorageBuffer %float\n    %int_1_4 = OpConstant %int 1\n%_ptr_StorageBuffer__runtimearr_Light_5 = OpTypePointer StorageBuffer %_runtimearr_Light\n   %int_0_11 = OpConstant %int 0\n%_ptr_StorageBuffer_Light_5 = OpTypePointer StorageBuffer %Light\n%_ptr_StorageBuffer_v4float_4 = OpTypePointer StorageBuffer %v4float\n    %int_2_3 = OpConstant %int 2\n%_ptr_StorageBuffer_float_4 = OpTypePointer StorageBuffer %float\n    %int_2_4 = OpConstant %int 2\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n%out_color_fs = OpVariable %_ptr_Output_v4float Output\n         %18 = OpFunction %float None %19\n         %15 = OpFunctionParameter %uint\n         %16 = OpFunctionParameter %v4float\n         %20 = OpLabel\n         %23 = OpCompositeExtract %float %16 3\n         %22 = OpFOrdLessThanEqual %bool %23 %float_0\n               OpSelectionMerge %24 None\n               OpBranchConditional %22 %25 %26\n         %25 = OpLabel\n               OpReturnValue %float_1\n         %26 = OpLabel\n               OpBranch %24\n         %24 = OpLabel\n         %30 = OpLoad %27 %t_shadow\n         %35 = OpLoad %32 %sampler_shadow\n         %40 = OpCompositeExtract %float %16 0\n         %41 = OpCompositeExtract %float %16 1\n         %42 = OpCompositeConstruct %v2float %40 %41\n         %43 = OpCompositeConstruct %v2float %float_0_5 %float_n0_5\n         %39 = OpFMul %v2float %42 %43\n         %45 = OpCompositeExtract %float %16 3\n         %44 = OpFDiv %float %float_1 %45\n         %38 = OpVectorTimesScalar %v2float %39 %44\n         %46 = OpCompositeConstruct %v2float %float_0_5 %float_0_5\n         %37 = OpFAdd %v2float %38 %46\n         %47 = OpCompositeExtract %float %37 0\n         %48 = OpCompositeExtract %float %37 1\n         %51 = OpBitcast %int %15\n         %49 = OpConvertUToF %float %51\n         %52 = OpCompositeConstruct %v3float %47 %48 %49\n         %53 = OpSampledImage %31 %30 %35\n         %56 = OpCompositeExtract %float %16 2\n         %58 = OpCompositeExtract %float %16 3\n         %57 = OpFDiv %float %float_1 %58\n         %55 = OpFMul %float %56 %57\n         %54 = OpImageSampleDrefExplicitLod %float %53 %52 %55 Lod %float_0_0\n               OpReturnValue %54\n               OpFunctionEnd\n    %fs_main = OpFunction %void None %65\n         %66 = OpLabel\n      %color = OpVariable %_ptr_Function_v3float Function %9\n          %i = OpVariable %_ptr_Function_uint Function %uint_0\n               OpBranch %67\n         %67 = OpLabel\n               OpLoopMerge %68 %70 None\n               OpBranch %69\n         %69 = OpLabel\n         %72 = OpLoad %uint %i\n         %75 = OpAccessChain %_ptr_Uniform_v4uint %u_globals %int_0\n         %73 = OpAccessChain %_ptr_Uniform_uint %75 %int_0_0\n         %83 = OpLoad %uint %73\n         %84 = OpExtInst %uint %1 UMin %83 %uint_10\n         %71 = OpUGreaterThanEqual %bool %72 %84\n               OpSelectionMerge %85 None\n               OpBranchConditional %71 %86 %87\n         %86 = OpLabel\n               OpBranch %68\n         %87 = OpLabel\n               OpBranch %85\n         %85 = OpLabel\n         %89 = OpLoad %v3float %color\n         %93 = OpLoad %uint %i\n        %100 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light %s_lights %int_0_1\n        %106 = OpLoad %uint %i\n         %98 = OpAccessChain %_ptr_StorageBuffer_Light %100 %106\n         %96 = OpAccessChain %_ptr_StorageBuffer_mat4v4float %98 %int_0_2\n        %110 = OpLoad %mat4v4float %96\n        %113 = OpLoad %v4float %in_position_fs\n         %94 = OpMatrixTimesVector %v4float %110 %113\n         %92 = OpFunctionCall %float %18 %93 %94\n        %116 = OpLoad %v3float %in_normal_fs\n        %117 = OpExtInst %v3float %1 Normalize %116\n        %122 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_0 %s_lights %int_0_3\n        %125 = OpLoad %uint %i\n        %121 = OpAccessChain %_ptr_StorageBuffer_Light_0 %122 %125\n        %120 = OpAccessChain %_ptr_StorageBuffer_v4float %121 %int_1\n        %119 = OpAccessChain %_ptr_StorageBuffer_float %120 %int_0_4\n        %131 = OpLoad %float %119\n        %135 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_1 %s_lights %int_0_5\n        %138 = OpLoad %uint %i\n        %134 = OpAccessChain %_ptr_StorageBuffer_Light_1 %135 %138\n        %133 = OpAccessChain %_ptr_StorageBuffer_v4float_0 %134 %int_1_0\n        %132 = OpAccessChain %_ptr_StorageBuffer_float_0 %133 %int_1_1\n        %144 = OpLoad %float %132\n        %148 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_2 %s_lights %int_0_6\n        %151 = OpLoad %uint %i\n        %147 = OpAccessChain %_ptr_StorageBuffer_Light_2 %148 %151\n        %146 = OpAccessChain %_ptr_StorageBuffer_v4float_1 %147 %int_1_2\n        %145 = OpAccessChain %_ptr_StorageBuffer_float_1 %146 %int_2\n        %157 = OpLoad %float %145\n        %158 = OpCompositeConstruct %v3float %131 %144 %157\n        %159 = OpAccessChain %_ptr_Input_float %in_position_fs %int_0_7\n        %162 = OpLoad %float %159\n        %163 = OpAccessChain %_ptr_Input_float_0 %in_position_fs %int_1_3\n        %166 = OpLoad %float %163\n        %167 = OpAccessChain %_ptr_Input_float_1 %in_position_fs %int_2_0\n        %170 = OpLoad %float %167\n        %171 = OpCompositeConstruct %v3float %162 %166 %170\n        %118 = OpFSub %v3float %158 %171\n        %172 = OpExtInst %v3float %1 Normalize %118\n        %173 = OpDot %float %117 %172\n        %174 = OpExtInst %float %1 FMax %float_0 %173\n         %91 = OpFMul %float %92 %174\n        %178 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_3 %s_lights %int_0_8\n        %181 = OpLoad %uint %i\n        %177 = OpAccessChain %_ptr_StorageBuffer_Light_3 %178 %181\n        %176 = OpAccessChain %_ptr_StorageBuffer_v4float_2 %177 %int_2_1\n        %175 = OpAccessChain %_ptr_StorageBuffer_float_2 %176 %int_0_9\n        %187 = OpLoad %float %175\n        %191 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_4 %s_lights %int_0_10\n        %194 = OpLoad %uint %i\n        %190 = OpAccessChain %_ptr_StorageBuffer_Light_4 %191 %194\n        %189 = OpAccessChain %_ptr_StorageBuffer_v4float_3 %190 %int_2_2\n        %188 = OpAccessChain %_ptr_StorageBuffer_float_3 %189 %int_1_4\n        %200 = OpLoad %float %188\n        %204 = OpAccessChain %_ptr_StorageBuffer__runtimearr_Light_5 %s_lights %int_0_11\n        %207 = OpLoad %uint %i\n        %203 = OpAccessChain %_ptr_StorageBuffer_Light_5 %204 %207\n        %202 = OpAccessChain %_ptr_StorageBuffer_v4float_4 %203 %int_2_3\n        %201 = OpAccessChain %_ptr_StorageBuffer_float_4 %202 %int_2_4\n        %213 = OpLoad %float %201\n        %214 = OpCompositeConstruct %v3float %187 %200 %213\n         %90 = OpVectorTimesScalar %v3float %214 %91\n         %88 = OpFAdd %v3float %89 %90\n               OpStore %color %88\n               OpBranch %70\n         %70 = OpLabel\n        %216 = OpLoad %uint %i\n        %215 = OpIAdd %uint %216 %uint_1\n               OpStore %i %215\n               OpBranch %67\n         %68 = OpLabel\n        %219 = OpLoad %v3float %color\n        %220 = OpCompositeConstruct %v4float %219 %float_1\n               OpStore %out_color_fs %220\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/shadow.toml",
    "content": "targets = \"IR | ANALYSIS\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/spec-constants-issue-5598.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 68\n; Schema: 0\n               OpCapability Shader\n               OpCapability VulkanMemoryModel\n               OpMemoryModel Logical Vulkan\n               OpEntryPoint Fragment %1 \"fragment\" %gl_FragCoord %3\n               OpEntryPoint Vertex %4 \"vertex\" %gl_VertexIndex %gl_Position\n               OpExecutionMode %1 OriginUpperLeft\n               OpDecorate %gl_FragCoord BuiltIn FragCoord\n               OpDecorate %10 SpecId 100\n               OpDecorate %3 Location 0\n               OpDecorate %_arr_v4float_uint_6 ArrayStride 16\n               OpDecorate %gl_VertexIndex BuiltIn VertexIndex\n               OpDecorate %gl_Position BuiltIn Position\n               OpDecorate %gl_Position Invariant\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n%_ptr_Input_v4float = OpTypePointer Input %v4float\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n       %void = OpTypeVoid\n         %17 = OpTypeFunction %void\n%gl_FragCoord = OpVariable %_ptr_Input_v4float Input\n       %bool = OpTypeBool\n       %uint = OpTypeInt 32 0\n         %10 = OpSpecConstant %uint 2\n     %uint_1 = OpConstant %uint 1\n    %v2float = OpTypeVector %float 2\n%_ptr_Output_float = OpTypePointer Output %float\n          %3 = OpVariable %_ptr_Output_v4float Output\n     %uint_0 = OpConstant %uint 0\n%_ptr_Input_uint = OpTypePointer Input %uint\n     %uint_6 = OpConstant %uint 6\n%_arr_v4float_uint_6 = OpTypeArray %v4float %uint_6\n%_ptr_Function__arr_v4float_uint_6 = OpTypePointer Function %_arr_v4float_uint_6\n%gl_VertexIndex = OpVariable %_ptr_Input_uint Input\n   %float_n1 = OpConstant %float -1\n    %float_0 = OpConstant %float 0\n    %float_1 = OpConstant %float 1\n         %32 = OpConstantComposite %v4float %float_n1 %float_n1 %float_0 %float_1\n         %33 = OpConstantComposite %v4float %float_1 %float_n1 %float_0 %float_1\n         %34 = OpConstantComposite %v4float %float_1 %float_1 %float_0 %float_1\n         %35 = OpConstantComposite %v4float %float_n1 %float_1 %float_0 %float_1\n         %36 = OpConstantComposite %_arr_v4float_uint_6 %32 %33 %34 %34 %35 %32\n%_ptr_Function_v4float = OpTypePointer Function %v4float\n%gl_Position = OpVariable %_ptr_Output_v4float Output\n %float_0_25 = OpConstant %float 0.25\n  %float_0_5 = OpConstant %float 0.5\n          %1 = OpFunction %void None %17\n         %38 = OpLabel\n         %39 = OpLoad %v4float %gl_FragCoord\n         %40 = OpCompositeExtract %float %39 0\n         %41 = OpCompositeExtract %float %39 1\n         %42 = OpIEqual %bool %10 %uint_1\n               OpSelectionMerge %43 None\n               OpBranchConditional %42 %44 %45\n         %44 = OpLabel\n         %46 = OpFMul %float %40 %float_0_5\n         %47 = OpFMul %float %41 %float_0_5\n         %48 = OpCompositeConstruct %v2float %46 %47\n               OpBranch %43\n         %45 = OpLabel\n         %49 = OpFMul %float %40 %float_0_25\n         %50 = OpFMul %float %41 %float_0_25\n         %51 = OpCompositeConstruct %v2float %49 %50\n               OpBranch %43\n         %43 = OpLabel\n         %52 = OpPhi %v2float %48 %44 %51 %45\n         %53 = OpCompositeExtract %float %52 0\n         %54 = OpAccessChain %_ptr_Output_float %3 %uint_0\n               OpStore %54 %53\n         %55 = OpCompositeExtract %float %52 1\n         %56 = OpAccessChain %_ptr_Output_float %3 %uint_1\n               OpStore %56 %55\n               OpReturn\n               OpFunctionEnd\n          %4 = OpFunction %void None %17\n         %57 = OpLabel\n         %58 = OpVariable %_ptr_Function__arr_v4float_uint_6 Function\n         %59 = OpLoad %uint %gl_VertexIndex\n               OpStore %58 %36\n         %60 = OpULessThan %bool %59 %uint_6\n               OpSelectionMerge %61 None\n               OpBranchConditional %60 %62 %63\n         %62 = OpLabel\n         %64 = OpInBoundsAccessChain %_ptr_Function_v4float %58 %59\n         %65 = OpLoad %v4float %64\n               OpStore %gl_Position %65\n               OpBranch %61\n         %63 = OpLabel\n               OpBranch %61\n         %61 = OpLabel\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/spec-constants-issue-5598.toml",
    "content": "targets = \"GLSL\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/spec-constants.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Google Shaderc over Glslang; 11\n; Bound: 74\n; Schema: 0\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Vertex %main \"main\" %v_Uv %Vertex_Uv %Vertex_Position %__0 %Vertex_Normal\n               OpSource GLSL 450\n               OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n               OpSourceExtension \"GL_GOOGLE_include_directive\"\n               OpName %main \"main\"\n               OpName %test_constant \"test_constant\"\n               OpName %TEST_CONSTANT \"TEST_CONSTANT\"\n               OpName %TEST_CONSTANT_TRUE \"TEST_CONSTANT_TRUE\"\n               OpName %TEST_CONSTANT_FALSE \"TEST_CONSTANT_FALSE\"\n               OpName %v_Uv \"v_Uv\"\n               OpName %Vertex_Uv \"Vertex_Uv\"\n               OpName %position \"position\"\n               OpName %Vertex_Position \"Vertex_Position\"\n               OpName %Sprite_size \"Sprite_size\"\n               OpMemberName %Sprite_size 0 \"size\"\n               OpName %_ \"\"\n               OpName %gl_PerVertex \"gl_PerVertex\"\n               OpMemberName %gl_PerVertex 0 \"gl_Position\"\n               OpMemberName %gl_PerVertex 1 \"gl_PointSize\"\n               OpMemberName %gl_PerVertex 2 \"gl_ClipDistance\"\n               OpMemberName %gl_PerVertex 3 \"gl_CullDistance\"\n               OpName %__0 \"\"\n               OpName %Camera \"Camera\"\n               OpMemberName %Camera 0 \"ViewProj\"\n               OpName %__1 \"\"\n               OpName %Transform \"Transform\"\n               OpMemberName %Transform 0 \"Model\"\n               OpName %__2 \"\"\n               OpName %Vertex_Normal \"Vertex_Normal\"\n               OpDecorate %TEST_CONSTANT SpecId 0\n               OpDecorate %TEST_CONSTANT_TRUE SpecId 1\n               OpDecorate %TEST_CONSTANT_FALSE SpecId 2\n               OpDecorate %v_Uv Location 0\n               OpDecorate %Vertex_Uv Location 2\n               OpDecorate %Vertex_Position Location 0\n               OpMemberDecorate %Sprite_size 0 Offset 0\n               OpDecorate %Sprite_size Block\n               OpDecorate %_ DescriptorSet 2\n               OpDecorate %_ Binding 1\n               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position\n               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize\n               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance\n               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance\n               OpDecorate %gl_PerVertex Block\n               OpMemberDecorate %Camera 0 ColMajor\n               OpMemberDecorate %Camera 0 Offset 0\n               OpMemberDecorate %Camera 0 MatrixStride 16\n               OpDecorate %Camera Block\n               OpDecorate %__1 DescriptorSet 0\n               OpDecorate %__1 Binding 0\n               OpMemberDecorate %Transform 0 ColMajor\n               OpMemberDecorate %Transform 0 Offset 0\n               OpMemberDecorate %Transform 0 MatrixStride 16\n               OpDecorate %Transform Block\n               OpDecorate %__2 DescriptorSet 2\n               OpDecorate %__2 Binding 0\n               OpDecorate %Vertex_Normal Location 1\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n%_ptr_Function_float = OpTypePointer Function %float\n%TEST_CONSTANT = OpSpecConstant %float 64\n       %bool = OpTypeBool\n%TEST_CONSTANT_TRUE = OpSpecConstantTrue %bool\n    %float_0 = OpConstant %float 0\n    %float_1 = OpConstant %float 1\n%TEST_CONSTANT_FALSE = OpSpecConstantFalse %bool\n    %v2float = OpTypeVector %float 2\n%_ptr_Output_v2float = OpTypePointer Output %v2float\n       %v_Uv = OpVariable %_ptr_Output_v2float Output\n%_ptr_Input_v2float = OpTypePointer Input %v2float\n  %Vertex_Uv = OpVariable %_ptr_Input_v2float Input\n    %v3float = OpTypeVector %float 3\n%_ptr_Function_v3float = OpTypePointer Function %v3float\n%_ptr_Input_v3float = OpTypePointer Input %v3float\n%Vertex_Position = OpVariable %_ptr_Input_v3float Input\n%Sprite_size = OpTypeStruct %v2float\n%_ptr_Uniform_Sprite_size = OpTypePointer Uniform %Sprite_size\n          %_ = OpVariable %_ptr_Uniform_Sprite_size Uniform\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float\n    %v4float = OpTypeVector %float 4\n       %uint = OpTypeInt 32 0\n     %uint_1 = OpConstant %uint 1\n%_arr_float_uint_1 = OpTypeArray %float %uint_1\n%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1\n%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex\n        %__0 = OpVariable %_ptr_Output_gl_PerVertex Output\n%mat4v4float = OpTypeMatrix %v4float 4\n     %Camera = OpTypeStruct %mat4v4float\n%_ptr_Uniform_Camera = OpTypePointer Uniform %Camera\n        %__1 = OpVariable %_ptr_Uniform_Camera Uniform\n%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float\n  %Transform = OpTypeStruct %mat4v4float\n%_ptr_Uniform_Transform = OpTypePointer Uniform %Transform\n        %__2 = OpVariable %_ptr_Uniform_Transform Uniform\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n%Vertex_Normal = OpVariable %_ptr_Input_v3float Input\n       %main = OpFunction %void None %3\n          %5 = OpLabel\n%test_constant = OpVariable %_ptr_Function_float Function\n   %position = OpVariable %_ptr_Function_v3float Function\n         %14 = OpSelect %float %TEST_CONSTANT_TRUE %float_1 %float_0\n         %15 = OpFMul %float %TEST_CONSTANT %14\n         %17 = OpSelect %float %TEST_CONSTANT_FALSE %float_1 %float_0\n         %18 = OpFMul %float %15 %17\n               OpStore %test_constant %18\n         %24 = OpLoad %v2float %Vertex_Uv\n               OpStore %v_Uv %24\n         %30 = OpLoad %v3float %Vertex_Position\n         %37 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0\n         %38 = OpLoad %v2float %37\n         %39 = OpCompositeExtract %float %38 0\n         %40 = OpCompositeExtract %float %38 1\n         %41 = OpCompositeConstruct %v3float %39 %40 %float_1\n         %42 = OpFMul %v3float %30 %41\n               OpStore %position %42\n         %55 = OpAccessChain %_ptr_Uniform_mat4v4float %__1 %int_0\n         %56 = OpLoad %mat4v4float %55\n         %60 = OpAccessChain %_ptr_Uniform_mat4v4float %__2 %int_0\n         %61 = OpLoad %mat4v4float %60\n         %62 = OpMatrixTimesMatrix %mat4v4float %56 %61\n         %63 = OpLoad %v3float %position\n         %64 = OpCompositeExtract %float %63 0\n         %65 = OpCompositeExtract %float %63 1\n         %66 = OpCompositeExtract %float %63 2\n         %67 = OpCompositeConstruct %v4float %64 %65 %66 %float_1\n         %68 = OpMatrixTimesVector %v4float %62 %67\n         %69 = OpLoad %float %test_constant\n         %70 = OpVectorTimesScalar %v4float %68 %69\n         %72 = OpAccessChain %_ptr_Output_v4float %__0 %int_0\n               OpStore %72 %70\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/spec-constants.toml",
    "content": "targets = \"IR\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/spv/spec-constants.vert",
    "content": "#version 450\n\nlayout (constant_id = 0) const float TEST_CONSTANT = 64.0;\nlayout (constant_id = 1) const bool TEST_CONSTANT_TRUE = true;\nlayout (constant_id = 2) const bool TEST_CONSTANT_FALSE = false;\n// layout (constant_id = 3) const vec2 TEST_CONSTANT_COMPOSITE = vec2(TEST_CONSTANT, 3.0);\n// glslc error: 'constant_id' : can only be applied to a scalar\n\nlayout(location = 0) in vec3 Vertex_Position;\nlayout(location = 1) in vec3 Vertex_Normal;\nlayout(location = 2) in vec2 Vertex_Uv;\n\nlayout(location = 0) out vec2 v_Uv;\n\nlayout(set = 0, binding = 0) uniform Camera {\n    mat4 ViewProj;\n};\nlayout(set = 2, binding = 0) uniform Transform {\n    mat4 Model;\n};\nlayout(set = 2, binding = 1) uniform Sprite_size {\n    vec2 size;\n};\n\nvoid main() {\n    float test_constant = TEST_CONSTANT * float(TEST_CONSTANT_TRUE) * float(TEST_CONSTANT_FALSE)\n        ;//* TEST_CONSTANT_COMPOSITE.x * TEST_CONSTANT_COMPOSITE.y;\n    v_Uv = Vertex_Uv;\n    vec3 position = Vertex_Position * vec3(size, 1.0);\n    gl_Position = ViewProj * Model * vec4(position, 1.0) * test_constant;\n}\n"
  },
  {
    "path": "naga/tests/in/spv/subgroup-barrier.spvasm",
    "content": "; SPIR-V\n; Version: 1.5\n; Generator: Google rspirv; 0\n; Bound: 14\n; Schema: 0\n               OpCapability Shader\n               OpMemoryModel Logical Simple\n               OpEntryPoint GLCompute %1 \"main\"\n               OpExecutionMode %1 LocalSize 64 1 1\n       %void = OpTypeVoid\n          %6 = OpTypeFunction %void\n       %uint = OpTypeInt 32 0\n     %uint_3 = OpConstant %uint 3\n   %uint_136 = OpConstant %uint 136\n          %1 = OpFunction %void None %6\n         %13 = OpLabel\n               OpMemoryBarrier %uint_3 %uint_136\n               OpControlBarrier %uint_3 %uint_3 %uint_136\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/subgroup-barrier.toml",
    "content": "capabilities = \"SUBGROUP | SUBGROUP_BARRIER\"\ntargets = \"WGSL | SPIRV | GLSL | METAL\"\n\n[msl]\nlang_version = [2, 0]\n"
  },
  {
    "path": "naga/tests/in/spv/subgroup-operations-s.spvasm",
    "content": "; SPIR-V\n; Version: 1.3\n; Generator: rspirv\n; Bound: 54\nOpCapability Shader\nOpCapability GroupNonUniform\nOpCapability GroupNonUniformBallot\nOpCapability GroupNonUniformVote\nOpCapability GroupNonUniformArithmetic\nOpCapability GroupNonUniformShuffle\nOpCapability GroupNonUniformShuffleRelative\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %15 \"main\" %6 %9 %11 %13\nOpExecutionMode %15 LocalSize 1 1 1\nOpDecorate %6 BuiltIn NumSubgroups\nOpDecorate %9 BuiltIn SubgroupId\nOpDecorate %11 BuiltIn SubgroupSize\nOpDecorate %13 BuiltIn SubgroupLocalInvocationId\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeBool\n%7 = OpTypePointer Input %3\n%6 = OpVariable  %7  Input\n%9 = OpVariable  %7  Input\n%11 = OpVariable  %7  Input\n%13 = OpVariable  %7  Input\n%16 = OpTypeFunction %2\n%17 = OpConstant  %3  1\n%18 = OpConstant  %3  0\n%19 = OpConstant  %3  4\n%21 = OpConstant  %3  3\n%22 = OpConstant  %3  2\n%23 = OpConstant  %3  8\n%26 = OpTypeVector %3 4\n%28 = OpConstantTrue  %4\n%15 = OpFunction  %2  None %16\n%5 = OpLabel\n%8 = OpLoad  %3  %6\n%10 = OpLoad  %3  %9\n%12 = OpLoad  %3  %11\n%14 = OpLoad  %3  %13\nOpBranch %20\n%20 = OpLabel\nOpControlBarrier %21 %22 %23\n%24 = OpBitwiseAnd  %3  %14 %17\n%25 = OpIEqual  %4  %24 %17\n%27 = OpGroupNonUniformBallot  %26  %21 %25\n%29 = OpGroupNonUniformBallot  %26  %21 %28\n%30 = OpINotEqual  %4  %14 %18\n%31 = OpGroupNonUniformAll  %4  %21 %30\n%32 = OpIEqual  %4  %14 %18\n%33 = OpGroupNonUniformAny  %4  %21 %32\n%34 = OpGroupNonUniformIAdd  %3  %21 Reduce %14\n%35 = OpGroupNonUniformIMul  %3  %21 Reduce %14\n%36 = OpGroupNonUniformUMin  %3  %21 Reduce %14\n%37 = OpGroupNonUniformUMax  %3  %21 Reduce %14\n%38 = OpGroupNonUniformBitwiseAnd  %3  %21 Reduce %14\n%39 = OpGroupNonUniformBitwiseOr  %3  %21 Reduce %14\n%40 = OpGroupNonUniformBitwiseXor  %3  %21 Reduce %14\n%41 = OpGroupNonUniformIAdd  %3  %21 ExclusiveScan %14\n%42 = OpGroupNonUniformIMul  %3  %21 ExclusiveScan %14\n%43 = OpGroupNonUniformIAdd  %3  %21 InclusiveScan %14\n%44 = OpGroupNonUniformIMul  %3  %21 InclusiveScan %14\n%45 = OpGroupNonUniformBroadcastFirst  %3  %21 %14\n%46 = OpGroupNonUniformBroadcast  %3  %21 %14 %19\n%47 = OpISub  %3  %12 %17\n%48 = OpISub  %3  %47 %14\n%49 = OpGroupNonUniformShuffle  %3  %21 %14 %48\n%50 = OpGroupNonUniformShuffleDown  %3  %21 %14 %17\n%51 = OpGroupNonUniformShuffleUp  %3  %21 %14 %17\n%52 = OpISub  %3  %12 %17\n%53 = OpGroupNonUniformShuffleXor  %3  %21 %14 %52\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/in/spv/subgroup-operations-s.toml",
    "content": "capabilities = \"SUBGROUP\"\ntargets = \"METAL | GLSL | HLSL | WGSL\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_0\"\nfake_missing_bindings = true\nrestrict_indexing = true\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 3]\n"
  },
  {
    "path": "naga/tests/in/spv/unnamed-gl-per-vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: Google Shaderc over Glslang; 11\n; Bound: 34\n; Schema: 0\n\n;; Make sure that we don't have a validation error due to lacking capabilities\n;; for bulltins such as ClipDistance when those builtin are not actually used.\n;;\n;; This specifically doesn't name the gl_PerVertex struct to make sure we don't\n;; rely on checks for this name.\n;;\n;; See https://github.com/gfx-rs/wgpu/issues/4915\n;;\n;; Generated via `glslc -O` on this glsl code (taken from https://github.com/gfx-rs/wgpu/issues/4915):\n;;\n;; ```glsl\n;; #version 450\n;;\n;; void main()\n;; {\n;;     gl_Position = vec4(\n;;         (gl_VertexIndex == 0) ? -4.0 : 1.0,\n;;         (gl_VertexIndex == 2) ? 4.0 : -1.0,\n;;         0.0,\n;;         1.0\n;;     );\n;; } \n;; ```\n;;\n               OpCapability Shader\n          %1 = OpExtInstImport \"GLSL.std.450\"\n               OpMemoryModel Logical GLSL450\n               OpEntryPoint Vertex %4 \"main\" %13 %gl_VertexIndex\n               OpMemberDecorate %_struct_11 0 BuiltIn Position\n               OpMemberDecorate %_struct_11 1 BuiltIn PointSize\n               OpMemberDecorate %_struct_11 2 BuiltIn ClipDistance\n               OpMemberDecorate %_struct_11 3 BuiltIn CullDistance\n               OpDecorate %_struct_11 Block\n               OpDecorate %gl_VertexIndex BuiltIn VertexIndex\n       %void = OpTypeVoid\n          %3 = OpTypeFunction %void\n      %float = OpTypeFloat 32\n    %v4float = OpTypeVector %float 4\n       %uint = OpTypeInt 32 0\n     %uint_1 = OpConstant %uint 1\n%_arr_float_uint_1 = OpTypeArray %float %uint_1\n %_struct_11 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1\n%_ptr_Output__struct_11 = OpTypePointer Output %_struct_11\n         %13 = OpVariable %_ptr_Output__struct_11 Output\n        %int = OpTypeInt 32 1\n      %int_0 = OpConstant %int 0\n%_ptr_Input_int = OpTypePointer Input %int\n%gl_VertexIndex = OpVariable %_ptr_Input_int Input\n       %bool = OpTypeBool\n   %float_n4 = OpConstant %float -4\n    %float_1 = OpConstant %float 1\n      %int_2 = OpConstant %int 2\n    %float_4 = OpConstant %float 4\n   %float_n1 = OpConstant %float -1\n    %float_0 = OpConstant %float 0\n%_ptr_Output_v4float = OpTypePointer Output %v4float\n          %4 = OpFunction %void None %3\n          %5 = OpLabel\n         %18 = OpLoad %int %gl_VertexIndex\n         %20 = OpIEqual %bool %18 %int_0\n         %23 = OpSelect %float %20 %float_n4 %float_1\n         %26 = OpIEqual %bool %18 %int_2\n         %29 = OpSelect %float %26 %float_4 %float_n1\n         %31 = OpCompositeConstruct %v4float %23 %29 %float_0 %float_1\n         %33 = OpAccessChain %_ptr_Output_v4float %13 %int_0\n               OpStore %33 %31\n               OpReturn\n               OpFunctionEnd\n"
  },
  {
    "path": "naga/tests/in/spv/unnamed-gl-per-vertex.toml",
    "content": "targets = \"METAL | GLSL | HLSL | WGSL\"\n\n[spv-in]\nadjust_coordinate_space = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/6220-break-from-loop.toml",
    "content": "targets = \"SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/6220-break-from-loop.wgsl",
    "content": "// #6220: Don't generate unreachable SPIR-V blocks that branch into\n// structured control flow constructs.\n//\n// Suppose we have Naga code like this:\n//\n//     Block {\n//       ... prelude\n//       Block { ... nested }\n//       ... postlude\n//     }\n//\n// The SPIR-V back end used to always generate three separate SPIR-V\n// blocks for the sections labeled \"prelude\", \"nested\", and\n// \"postlude\", each block ending with a branch to the next, even if\n// they were empty.\n//\n// However, the function below generates code that includes the\n// following structure:\n//\n//     Loop {\n//       body: Block {\n//         ... prelude\n//         Block { Break }\n//         ... postlude\n//       }\n//       continuing: ...\n//     }\n//\n// In this case, even though the `Break` renders the \"postlude\"\n// unreachable, we used to generate a SPIR-V block for it anyway,\n// ending with a branch to the `Loop`'s \"continuing\" block. However,\n// SPIR-V's structured control flow rules forbid branches to a loop\n// construct's continue target from outside the loop, so the SPIR-V\n// module containing the unreachable block didn't pass validation.\n//\n// One might assume that unreachable blocks shouldn't affect\n// validation, but the spec doesn't clearly agree, and this doesn't\n// seem to be the way validation has been implemented.\nfn break_from_loop() {\n    for (var i = 0; i < 4; i += 1) {\n      break;\n    }\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    break_from_loop();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/6438-conflicting-idents.wgsl",
    "content": "struct OurVertexShaderOutput {\n    @builtin(position) position: vec4f,\n    @location(0) texcoord: vec2f,\n};\n\n@vertex fn vs(\n    @location(0) xy: vec2f\n) -> OurVertexShaderOutput {\n    var vsOutput: OurVertexShaderOutput;\n    vsOutput.position = vec4f(xy, 0.0, 1.0);\n    return vsOutput;\n}\n\n@fragment fn fs() -> @location(0) vec4f {\n    return vec4f(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/6772-unpack-expr-accesses.wgsl",
    "content": "@compute @workgroup_size(1, 1)\nfn main() {\n    let idx = 2;\n    _ = unpack4xI8(12u)[idx];\n    _ = unpack4xU8(12u)[1];\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-1.toml",
    "content": "targets = \"SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-1.wgsl",
    "content": "@compute @workgroup_size(1)\nfn f() {\n    let b = array<vec3f, 2>();\n    var poly = vec4f(0);\n    var k = 0;\n    var j = 0;\n\n    poly.x += b[j].y * b[k].z;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-2.toml",
    "content": "targets = \"SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-2.wgsl",
    "content": "@fragment\nfn fs_main() -> @location(0) vec4f {\n    let my_array = array(\n        vec2f(0.0, 0.0),\n        vec2f(0.0, 0.0),\n    );\n    var index_0 = 0;\n\n    let val_0 = my_array[index_0];\n    let val_1 = my_array[index_0];\n\n    return (val_0 * val_1).xxyy;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-3.toml",
    "content": "targets = \"SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/7048-multiple-dynamic-3.wgsl",
    "content": "struct QEFResult {\n        a: f32,\n        b: vec3<f32>,\n}\n\nfn foobar(normals: array<vec3f, 12>, count: u32) -> QEFResult {\n        for (var i = 0u; i < count; i++) {\n                var n0 = normals[i];\n        }\n\n        for (var j = 0u; j < count; j++) {\n                var n1 = normals[j];\n        }\n\n        return QEFResult(0.0, vec3(0.0));\n}\n\n@fragment\nfn main() {\n    var arr: array<vec3f, 12>;\n    foobar(arr, 1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/7995-unicode-idents.wgsl",
    "content": "// NOTE: This allows us to suppress compaction below, to force the handling of identifiers\n// containing Unicode.\n@group(0) @binding(0)\nvar<storage> asdf: f32;\n\nfn compute() -> f32 {\n  let θ2 = asdf + 9001.0;\n  return θ2;\n}\n\n@compute @workgroup_size(1, 1)\nfn main() {\n  compute();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/8820-multiple-local-invocation-index-id.wgsl",
    "content": "var<workgroup> wg_var: u32;\n\nstruct Input {\n    @builtin(local_invocation_id)\n    local_invocation_id: vec3<u32>,\n    @builtin(local_invocation_index)\n    local_invocation_index: u32,\n}\n\n@compute\n@workgroup_size(1)\nfn compute1(input: Input) {\n    wg_var = input.local_invocation_index * 2;\n    wg_var += input.local_invocation_id.x;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/9105-primitive-index-ordering.toml",
    "content": "targets = \"HLSL\"\ncapabilities = \"PRIMITIVE_INDEX\"\n\n[hlsl]\nshader_model = \"V5_0\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/9105-primitive-index-ordering.wgsl",
    "content": "// FXC requires that all non-system values get written first, but doesn't consider SV_PrimitiveID a system value.\n// This test ensures that the inputs are written in the right order\n\nenable primitive_index;\n\n@fragment\nfn func(@location(0) input_location: f32, @builtin(position) arbitrary_position: vec4<f32>, @builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    return vec4(arbitrary_position.xy, input_location, f32(index));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-atomic.toml",
    "content": "targets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-atomic.wgsl",
    "content": "@group(0) @binding(0)\nvar<storage,read_write> atomic_i32: atomic<i32>;\n@group(0) @binding(1)\nvar<storage,read_write> atomic_u32: atomic<u32>;\n\nfn test_atomic_i32() {\n  atomicStore(&atomic_i32, 1);\n  _ = atomicCompareExchangeWeak(&atomic_i32, 1, 1i);\n  _ = atomicCompareExchangeWeak(&atomic_i32, 1i, 1);\n\n  _ = atomicAdd(&atomic_i32, 1);\n  _ = atomicSub(&atomic_i32, 1);\n  _ = atomicAnd(&atomic_i32, 1);\n  _ = atomicXor(&atomic_i32, 1);\n  _ = atomicOr(&atomic_i32, 1);\n  _ = atomicMin(&atomic_i32, 1);\n  _ = atomicMax(&atomic_i32, 1);\n  _ = atomicExchange(&atomic_i32, 1);\n}\n\nfn test_atomic_u32() {\n  atomicStore(&atomic_u32, 1);\n  _ = atomicCompareExchangeWeak(&atomic_u32, 1, 1u);\n  _ = atomicCompareExchangeWeak(&atomic_u32, 1u, 1);\n\n  _ = atomicAdd(&atomic_u32, 1);\n  _ = atomicSub(&atomic_u32, 1);\n  _ = atomicAnd(&atomic_u32, 1);\n  _ = atomicXor(&atomic_u32, 1);\n  _ = atomicOr(&atomic_u32, 1);\n  _ = atomicMin(&atomic_u32, 1);\n  _ = atomicMax(&atomic_u32, 1);\n  _ = atomicExchange(&atomic_u32, 1);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    test_atomic_i32();\n    test_atomic_u32();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-builtins.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-builtins.wgsl",
    "content": "@compute @workgroup_size(1)\nfn f() {\n  // For calls that return abstract types, we can't write the type we\n  // actually expect, but we can at least require an automatic\n  // conversion.\n  //\n  // Error cases are covered in `wgsl_errors::more_inconsistent_type`.\n\n  // start\n  var clamp_aiaiai: i32 = clamp(1, 1, 1);\n  var clamp_aiaiaf: f32 = clamp(1, 1, 1.0);\n  var clamp_aiaii: i32 = clamp(1, 1, 1i);\n  var clamp_aiaif: f32 = clamp(1, 1, 1f);\n  var clamp_aiafai: f32 = clamp(1, 1.0, 1);\n  var clamp_aiafaf: f32 = clamp(1, 1.0, 1.0);\n//var clamp_aiafi: f32 = clamp(1, 1.0, 1i); error\n  var clamp_aiaff: f32 = clamp(1, 1.0, 1f);\n  var clamp_aiiai: i32 = clamp(1, 1i, 1);\n//var clamp_aiiaf: f32 = clamp(1, 1i, 1.0); error\n  var clamp_aiii: i32 = clamp(1, 1i, 1i);\n//var clamp_aiif: f32 = clamp(1, 1i, 1f); error\n  var clamp_aifai: f32 = clamp(1, 1f, 1);\n  var clamp_aifaf: f32 = clamp(1, 1f, 1.0);\n//var clamp_aifi: f32 = clamp(1, 1f, 1i); error\n  var clamp_aiff: f32 = clamp(1, 1f, 1f);\n  var clamp_afaiai: f32 = clamp(1.0, 1, 1);\n  var clamp_afaiaf: f32 = clamp(1.0, 1, 1.0);\n//var clamp_afaii: f32 = clamp(1.0, 1, 1i); error\n  var clamp_afaif: f32 = clamp(1.0, 1, 1f);\n  var clamp_afafai: f32 = clamp(1.0, 1.0, 1);\n  var clamp_afafaf: f32 = clamp(1.0, 1.0, 1.0);\n//var clamp_afafi: f32 = clamp(1.0, 1.0, 1i); error\n  var clamp_afaff: f32 = clamp(1.0, 1.0, 1f);\n//var clamp_afiai: f32 = clamp(1.0, 1i, 1); error\n//var clamp_afiaf: f32 = clamp(1.0, 1i, 1.0); error\n//var clamp_afii: f32 = clamp(1.0, 1i, 1i); error\n//var clamp_afif: f32 = clamp(1.0, 1i, 1f); error\n  var clamp_affai: f32 = clamp(1.0, 1f, 1);\n  var clamp_affaf: f32 = clamp(1.0, 1f, 1.0);\n//var clamp_affi: f32 = clamp(1.0, 1f, 1i); error\n  var clamp_afff: f32 = clamp(1.0, 1f, 1f);\n  var clamp_iaiai: i32 = clamp(1i, 1, 1);\n//var clamp_iaiaf: f32 = clamp(1i, 1, 1.0); error\n  var clamp_iaii: i32 = clamp(1i, 1, 1i);\n//var clamp_iaif: f32 = clamp(1i, 1, 1f); error\n//var clamp_iafai: f32 = clamp(1i, 1.0, 1); error\n//var clamp_iafaf: f32 = clamp(1i, 1.0, 1.0); error\n//var clamp_iafi: f32 = clamp(1i, 1.0, 1i); error\n//var clamp_iaff: f32 = clamp(1i, 1.0, 1f); error\n  var clamp_iiai: i32 = clamp(1i, 1i, 1);\n//var clamp_iiaf: f32 = clamp(1i, 1i, 1.0); error\n  var clamp_iii: i32 = clamp(1i, 1i, 1i);\n//var clamp_iif: f32 = clamp(1i, 1i, 1f); error\n//var clamp_ifai: f32 = clamp(1i, 1f, 1); error\n//var clamp_ifaf: f32 = clamp(1i, 1f, 1.0); error\n//var clamp_ifi: f32 = clamp(1i, 1f, 1i); error\n//var clamp_iff: f32 = clamp(1i, 1f, 1f); error\n  var clamp_faiai: f32 = clamp(1f, 1, 1);\n  var clamp_faiaf: f32 = clamp(1f, 1, 1.0);\n//var clamp_faii: f32 = clamp(1f, 1, 1i); error\n  var clamp_faif: f32 = clamp(1f, 1, 1f);\n  var clamp_fafai: f32 = clamp(1f, 1.0, 1);\n  var clamp_fafaf: f32 = clamp(1f, 1.0, 1.0);\n//var clamp_fafi: f32 = clamp(1f, 1.0, 1i); error\n  var clamp_faff: f32 = clamp(1f, 1.0, 1f);\n//var clamp_fiai: f32 = clamp(1f, 1i, 1); error\n//var clamp_fiaf: f32 = clamp(1f, 1i, 1.0); error\n//var clamp_fii: f32 = clamp(1f, 1i, 1i); error\n//var clamp_fif: f32 = clamp(1f, 1i, 1f); error\n  var clamp_ffai: f32 = clamp(1f, 1f, 1);\n  var clamp_ffaf: f32 = clamp(1f, 1f, 1.0);\n//var clamp_ffi: f32 = clamp(1f, 1f, 1i); error\n  var clamp_fff: f32 = clamp(1f, 1f, 1f);\n  // end\n\n\n  var min_aiai: i32 = min(1,   1);\n  var min_aiaf: f32 = min(1,   1.0);\n  var min_aii:  i32 = min(1,   1i);\n  var min_aif:  f32 = min(1,   1f);\n  var min_afai: f32 = min(1.0, 1);\n  var min_afaf: f32 = min(1.0, 1.0);\n//var min_afi:  f32 = min(1.0, 1i); error\n  var min_aff:  f32 = min(1.0, 1f);\n  var min_iai:  i32 = min(1i,  1);\n//var min_iaf:  f32 = min(1i,  1.0); error\n  var min_ii:   i32 = min(1i,  1i);\n//var min_if:   f32 = min(1i,  1f); error\n  var min_fai:  f32 = min(1f,  1);\n  var min_faf:  f32 = min(1f,  1.0);\n//var min_fi:   f32 = min(1f,  1i); error\n  var min_ff:   f32 = min(1f,  1f);\n\n  var pow_aiai = pow(1, 1);\n  var pow_aiaf = pow(1, 1.0);\n  var pow_aif = pow(1, 1f);\n  var pow_afai = pow(1.0, 1);\n  var pow_afaf = pow(1.0, 1.0);\n  var pow_aff = pow(1.0, 1f);\n  var pow_fai = pow(1f, 1);\n  var pow_faf = pow(1f, 1.0);\n  var pow_ff = pow(1f, 1f);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-const.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-const.wgsl",
    "content": "// i/x: type inferred / explicit\n// vX/mX/aX: vector / matrix / array of X\n//     where X: u/i/f: u32 / i32 / f32\n// s: vector splat\n// r: vector spread (vector arg to vector constructor)\n// p: \"partial\" constructor (type parameter inferred)\n// u/i/f/ai/af: u32 / i32 / f32 / abstract float / abstract integer as parameter\n// _: just for alignment\n\n// Ensure that:\n// - the inferred type is correct.\n// - all parameters' types are considered.\n// - all parameters are converted to the consensus type.\n\nconst xvipaiai: vec2<i32> = vec2(42, 43);\nconst xvupaiai: vec2<u32> = vec2(44, 45);\nconst xvfpaiai: vec2<f32> = vec2(46, 47);\nconst xvfpafaf: vec2<f32> = vec2(48.0, 49.0);\nconst xvfpaiaf: vec2<f32> = vec2(48, 49.0);\n\nconst xvupuai: vec2<u32> = vec2(42u, 43);\nconst xvupaiu: vec2<u32> = vec2(42, 43u); \n\nconst xvuuai: vec2<u32> = vec2<u32>(42u, 43);\nconst xvuaiu: vec2<u32> = vec2<u32>(42, 43u);\n\nconst xvip____: vec2<i32> = vec2();\nconst xvup____: vec2<u32> = vec2();\nconst xvfp____: vec2<f32> = vec2();\nconst xmfp____: mat2x2f = mat2x2(vec2(), vec2());\n\nconst xmfpaiaiaiai: mat2x2<f32> = mat2x2(1, 2, 3, 4);\nconst xmfpafaiaiai: mat2x2<f32> = mat2x2(1.0, 2, 3, 4);\nconst xmfpaiafaiai: mat2x2<f32> = mat2x2(1, 2.0, 3, 4);\nconst xmfpaiaiafai: mat2x2<f32> = mat2x2(1, 2, 3.0, 4);\nconst xmfpaiaiaiaf: mat2x2<f32> = mat2x2(1, 2, 3, 4.0);\n\nconst imfpaiaiaiai = mat2x2(1, 2, 3, 4);\nconst imfpafaiaiai = mat2x2(1.0, 2, 3, 4);\nconst imfpafafafaf = mat2x2(1.0, 2.0, 3.0, 4.0);\n\nconst ivispai = vec2(1);\nconst ivfspaf = vec2(1.0);\nconst ivis_ai = vec2<i32>(1);\nconst ivus_ai = vec2<u32>(1);\nconst ivfs_ai = vec2<f32>(1);\nconst ivfs_af = vec2<f32>(1.0);\n\nconst iafafaf = array<f32, 2>(1.0, 2.0);\nconst iafaiai = array<f32, 2>(1, 2);\n\nconst iaipaiai = array(1,   2);\nconst iafpaiaf = array(1,   2.0);\nconst iafpafai = array(1.0, 2);\nconst iafpafaf = array(1.0, 2.0);\n\nconst xaipaiai: array<i32, 2> = array(1, 2);\nconst xaupaiai: array<u32, 2> = array(1, 2);\nconst xafpaiaf: array<f32, 2> = array(1,   2.0);\nconst xafpafai: array<f32, 2> = array(1.0, 2);\nconst xafpafaf: array<f32, 2> = array(1.0, 2.0);\n\nstruct S {\n    f: f32,\n    i: i32,\n    u: u32,\n}\n\nconst s_f_i_u: S = S(1.0f, 1i, 1u);\nconst s_f_iai: S = S(1.0f, 1i, 1);\nconst s_fai_u: S = S(1.0f, 1,  1u);\nconst s_faiai: S = S(1.0f, 1,  1);\nconst saf_i_u: S = S(1.0,  1i, 1u);\nconst saf_iai: S = S(1.0,  1i, 1);\nconst safai_u: S = S(1.0,  1,  1u);\nconst safaiai: S = S(1.0,  1,  1);\n\nconst xvisai: vec2<i32> = vec2(1);\nconst xvusai: vec2<u32> = vec2(1);\nconst xvfsai: vec2<f32> = vec2(1);\nconst xvfsaf: vec2<f32> = vec2(1.0);\n\n// Vector construction with spreads\nconst ivfr_f__f = vec3<f32>(vec2<f32>(1.0f, 2.0f), 3.0f);\nconst ivfr_f_af = vec3<f32>(vec2<f32>(1.0f, 2.0f), 3.0 );\nconst ivfraf__f = vec3<f32>(vec2     (1.0 , 2.0 ), 3.0f);\nconst ivfraf_af = vec3<f32>(vec2     (1.0 , 2.0 ), 3.0 );\n\nconst ivf__fr_f = vec3<f32>(1.0f, vec2<f32>(2.0f, 3.0f));\nconst ivf__fraf = vec3<f32>(1.0f, vec2     (2.0 , 3.0 ));\nconst ivf_afr_f = vec3<f32>(1.0 , vec2<f32>(2.0f, 3.0f));\nconst ivf_afraf = vec3<f32>(1.0 , vec2     (2.0 , 3.0 ));\n\nconst ivfr_f_ai = vec3<f32>(vec2<f32>(1.0f, 2.0f), 3   );\nconst ivfrai__f = vec3<f32>(vec2     (1   , 2   ), 3.0f);\nconst ivfrai_ai = vec3<f32>(vec2     (1   , 2   ), 3   );\n\nconst ivf__frai = vec3<f32>(1.0f, vec2     (2   , 3   ));\nconst ivf_air_f = vec3<f32>(1   , vec2<f32>(2.0f, 3.0f));\nconst ivf_airai = vec3<f32>(1   , vec2     (2   , 3   ));\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-function-calls.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-function-calls.wgsl",
    "content": "fn func_f(a: f32) {}\nfn func_i(a: i32) {}\nfn func_u(a: u32) {}\n\nfn func_vf(a: vec2<f32>) {}\nfn func_vi(a: vec2<i32>) {}\nfn func_vu(a: vec2<u32>) {}\n\nfn func_mf(a: mat2x2<f32>) {}\n\nfn func_af(a: array<f32, 2>) {}\nfn func_ai(a: array<i32, 2>) {}\nfn func_au(a: array<u32, 2>) {}\n\nfn func_f_i(a: f32, b: i32) {}\n\nconst const_af = 0;\nconst const_ai = 0;\nconst const_vec_af = vec2(0.0);\nconst const_vec_ai = vec2(0);\nconst const_mat_af = mat2x2(vec2(0.0), vec2(0.0));\nconst const_arr_af = array(0.0, 0.0);\nconst const_arr_ai = array(0, 0);\n\n@compute @workgroup_size(1)\nfn main() {\n    func_f(0.0);\n    func_f(0);\n    func_i(0);\n    func_u(0);\n\n    func_f(const_af);\n    func_f(const_ai);\n    func_i(const_ai);\n    func_u(const_ai);\n\n    func_vf(vec2(0.0));\n    func_vf(vec2(0));\n    func_vi(vec2(0));\n    func_vu(vec2(0));\n\n    func_vf(const_vec_af);\n    func_vf(const_vec_ai);\n    func_vi(const_vec_ai);\n    func_vu(const_vec_ai);\n\n    func_mf(mat2x2(vec2(0.0), vec2(0.0)));\n    func_mf(mat2x2(vec2(0), vec2(0)));\n\n    func_mf(const_mat_af);\n\n    func_af(array(0.0, 0.0));\n    func_af(array(0, 0));\n    func_ai(array(0, 0));\n    func_au(array(0, 0));\n\n    func_af(const_arr_af);\n    func_af(const_arr_ai);\n    func_ai(const_arr_ai);\n    func_au(const_arr_ai);\n\n    func_f_i(0.0, 0);\n    func_f_i(0, 0);\n\n    func_f_i(const_af, const_ai);\n    func_f_i(const_ai, const_ai);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-let.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-let.wgsl",
    "content": "// i/x: type inferred / explicit\n// vX/mX/aX: vector / matrix / array of X\n//     where X: u/i/f: u32 / i32 / f32\n// s: vector splat\n// r: vector spread (vector arg to vector constructor)\n// p: \"partial\" constructor (type parameter inferred)\n// u/i/f/ai/af: u32 / i32 / f32 / abstract float / abstract integer as parameter\n// _: just for alignment\n\n// Ensure that:\n// - the inferred type is correct.\n// - all parameters' types are considered.\n// - all parameters are converted to the consensus type.\n\nfn all_constant_arguments() {\n    let xvipaiai: vec2<i32> = vec2(42, 43);\n    let xvupaiai: vec2<u32> = vec2(44, 45);\n    let xvfpaiai: vec2<f32> = vec2(46, 47);\n    let xvfpafaf: vec2<f32> = vec2(48.0, 49.0);\n    let xvfpaiaf: vec2<f32> = vec2(48, 49.0);\n\n    let xvupuai: vec2<u32> = vec2(42u, 43);\n    let xvupaiu: vec2<u32> = vec2(42, 43u);\n\n    let xvuuai: vec2<u32> = vec2<u32>(42u, 43);\n    let xvuaiu: vec2<u32> = vec2<u32>(42, 43u);\n\n    let xvip____: vec2<i32> = vec2();\n    let xvup____: vec2<u32> = vec2();\n    let xvfp____: vec2<f32> = vec2();\n    let xmfp____: mat2x2f = mat2x2(vec2(), vec2());\n\n    let xmfpaiaiaiai: mat2x2<f32> = mat2x2(1, 2, 3, 4);\n    let xmfpafaiaiai: mat2x2<f32> = mat2x2(1.0, 2, 3, 4);\n    let xmfpaiafaiai: mat2x2<f32> = mat2x2(1, 2.0, 3, 4);\n    let xmfpaiaiafai: mat2x2<f32> = mat2x2(1, 2, 3.0, 4);\n    let xmfpaiaiaiaf: mat2x2<f32> = mat2x2(1, 2, 3, 4.0);\n\n    let xmfp_faiaiai: mat2x2<f32> = mat2x2(1.0f, 2, 3, 4);\n    let xmfpai_faiai: mat2x2<f32> = mat2x2(1, 2.0f, 3, 4);\n    let xmfpaiai_fai: mat2x2<f32> = mat2x2(1, 2, 3.0f, 4);\n    let xmfpaiaiai_f: mat2x2<f32> = mat2x2(1, 2, 3, 4.0f);\n\n    let xvispai: vec2<i32> = vec2(1);\n    let xvfspaf: vec2<f32> = vec2(1.0);\n    let xvis_ai: vec2<i32> = vec2<i32>(1);\n    let xvus_ai: vec2<u32> = vec2<u32>(1);\n    let xvfs_ai: vec2<f32> = vec2<f32>(1);\n    let xvfs_af: vec2<f32> = vec2<f32>(1.0);\n\n    let xafafaf: array<f32, 2> = array<f32, 2>(1.0, 2.0);\n    let xaf_faf: array<f32, 2> = array<f32, 2>(1.0f, 2.0);\n    let xafaf_f: array<f32, 2> = array<f32, 2>(1.0, 2.0f);\n    let xafaiai: array<f32, 2> = array<f32, 2>(1, 2);\n    let xai_iai: array<i32, 2> = array<i32, 2>(1i, 2);\n    let xaiai_i: array<i32, 2> = array<i32, 2>(1, 2i);\n\n    let xaipaiai: array<i32, 2> = array(1,   2);\n    let xafpaiai: array<f32, 2> = array(1,   2);\n    let xafpaiaf: array<f32, 2> = array(1,   2.0);\n    let xafpafai: array<f32, 2> = array(1.0, 2);\n    let xafpafaf: array<f32, 2> = array(1.0, 2.0);\n\n    let xavipai: array<vec3<i32>, 1> = array(vec3(1));\n    let xavfpai: array<vec3<f32>, 1> = array(vec3(1));\n    let xavfpaf: array<vec3<f32>, 1> = array(vec3(1.0));\n\n    // Construction with splats\n    let xvisai: vec2<i32> = vec2(1);\n    let xvusai: vec2<u32> = vec2(1);\n    let xvfsai: vec2<f32> = vec2(1);\n    let xvfsaf: vec2<f32> = vec2(1.0);\n\n    let iaipaiai = array(1,   2);\n    let iafpaiaf = array(1,   2.0);\n    let iafpafai = array(1.0, 2);\n    let iafpafaf = array(1.0, 2.0);\n}\n\nfn mixed_constant_and_runtime_arguments() {\n    var u: u32;\n    var i: i32;\n    var f: f32;\n\n    let xvupuai: vec2<u32> = vec2(u,  43);\n    let xvupaiu: vec2<u32> = vec2(42, u);\n    let xvfpfai: vec2<f32> = vec2(f, 47); // differs slightly from const version\n    let xvfpfaf: vec2<f32> = vec2(f, 49.0);\n\n    let xvuuai: vec2<u32> = vec2<u32>(u, 43);\n    let xvuaiu: vec2<u32> = vec2<u32>(42, u);\n\n    let xmfp_faiaiai: mat2x2<f32> = mat2x2(f, 2, 3, 4);\n    let xmfpai_faiai: mat2x2<f32> = mat2x2(1, f, 3, 4);\n    let xmfpaiai_fai: mat2x2<f32> = mat2x2(1, 2, f, 4);\n    let xmfpaiaiai_f: mat2x2<f32> = mat2x2(1, 2, 3, f);\n\n    let xaf_faf: array<f32, 2> = array<f32, 2>(f, 2.0);\n    let xafaf_f: array<f32, 2> = array<f32, 2>(1.0, f);\n    let xaf_fai: array<f32, 2> = array<f32, 2>(f, 2);\n    let xafai_f: array<f32, 2> = array<f32, 2>(1, f);\n    let xai_iai: array<i32, 2> = array<i32, 2>(i, 2);\n    let xaiai_i: array<i32, 2> = array<i32, 2>(1, i);\n\n    let xafp_faf: array<f32, 2> = array(f, 2.0);\n    let xafpaf_f: array<f32, 2> = array(1.0, f);\n    let xafp_fai: array<f32, 2> = array(f, 2);\n    let xafpai_f: array<f32, 2> = array(1, f);\n    let xaip_iai: array<i32, 2> = array(i, 2);\n    let xaipai_i: array<i32, 2> = array(1, i);\n\n    let xvisi: vec2<i32> = vec2(i);\n    let xvusu: vec2<u32> = vec2(u);\n    let xvfsf: vec2<f32> = vec2(f);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-operators.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-operators.wgsl",
    "content": "const plus_fafaf: f32 = 1.0 + 2.0;\nconst plus_fafai: f32 = 1.0 + 2;\nconst plus_faf_f: f32 = 1.0 + 2f;\nconst plus_faiaf: f32 = 1 + 2.0;\nconst plus_faiai: f32 = 1 + 2;\nconst plus_fai_f: f32 = 1 + 2f;\nconst plus_f_faf: f32 = 1f + 2.0;\nconst plus_f_fai: f32 = 1f + 2;\nconst plus_f_f_f: f32 = 1f + 2f;\n\nconst plus_iaiai: i32 = 1 + 2;\nconst plus_iai_i: i32 = 1 + 2i;\nconst plus_i_iai: i32 = 1i + 2;\nconst plus_i_i_i: i32 = 1i + 2i;\n\nconst plus_uaiai: u32 = 1 + 2;\nconst plus_uai_u: u32 = 1 + 2u;\nconst plus_u_uai: u32 = 1u + 2;\nconst plus_u_u_u: u32 = 1u + 2u;\n\nconst bitflip_u_u: u32 = ~0xffffffffu;\nconst bitflip_uai: u32 = ~0xffffffff & (0x100000000 - 1);\n\nconst least_i32: i32 = -2147483648;\nconst least_f32: f32 = -3.40282347e+38;\n\nconst shl_iaiai: i32 = 1 << 2;\nconst shl_iai_u: i32 = 1 << 2u;\nconst shl_uaiai: u32 = 1 << 2;\nconst shl_uai_u: u32 = 1 << 2u;\nconst shlaiaiai = 1 << 2;\nconst shlaiai_u = 1 << 2u;\n\nconst shr_iaiai: i32 = 1 >> 2;\nconst shr_iai_u: i32 = 1 >> 2u;\nconst shr_uaiai: u32 = 1 >> 2;\nconst shr_uai_u: u32 = 1 >> 2u;\nconst shraiaiai = 1 >> 2;\nconst shraiai_u = 1 >> 2u;\n\nfn runtime_values() {\n  var f: f32 = 42;\n  var i: i32 = 43;\n  var u: u32 = 44;\n\n  var plus_fafaf: f32 = 1.0 + 2.0;\n  var plus_fafai: f32 = 1.0 + 2;\n  var plus_faf_f: f32 = 1.0 + f;\n  var plus_faiaf: f32 = 1 + 2.0;\n  var plus_faiai: f32 = 1 + 2;\n  var plus_fai_f: f32 = 1 + f;\n  var plus_f_faf: f32 = f + 2.0;\n  var plus_f_fai: f32 = f + 2;\n  var plus_f_f_f: f32 = f + f;\n\n  var plus_iaiai: i32 = 1 + 2;\n  var plus_iai_i: i32 = 1 + i;\n  var plus_i_iai: i32 = i + 2;\n  var plus_i_i_i: i32 = i + i;\n\n  var plus_uaiai: u32 = 1 + 2;\n  var plus_uai_u: u32 = 1 + u;\n  var plus_u_uai: u32 = u + 2;\n  var plus_u_u_u: u32 = u + u;\n\n  var shl_iai_u: i32 = 1 << u;\n  var shr_iai_u: i32 = 1 << u;\n}\n\nfn wgpu_4445() {\n  // This ok:\n  let a = (3.0*2.0-(1.0)) * 1.0;\n  let b = (3.0*2.0+1.0) * 1.0;\n  // This fails:\n  let c = (3.0*2.0-1.0) * 1.0;\n}\n\nconst wgpu_4492 = i32(-0x80000000);\nconst wgpu_4492_2 = -2147483648;\n\nvar<workgroup> a: array<u32, 64>;\n\nfn wgpu_4435() {\n    let x = 1;\n    let y = a[x-1];\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    runtime_values();\n    wgpu_4445();\n    wgpu_4435();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-return.wgsl",
    "content": "fn return_i32_ai() -> i32 {\n    return 1;\n}\n\nfn return_u32_ai() -> u32 {\n    return 1;\n}\n\nfn return_f32_ai() -> f32 {\n    return 1;\n}\n\nfn return_f32_af() -> f32 {\n    return 1.0;\n}\n\nfn return_vec2f32_ai() -> vec2<f32> {\n    return vec2(1);\n}\n\nfn return_arrf32_ai() -> array<f32, 4> {\n    return array(1, 1, 1, 1);\n}\n\nconst one = 1;\nfn return_const_f32_const_ai() -> f32 {\n    return one;\n}\n\nfn return_vec2f32_const_ai() -> vec2<f32> {\n    const vec_one = vec2(1);\n    return vec_one;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    return_i32_ai();\n    return_u32_ai();\n    return_f32_ai();\n    return_f32_af();\n    return_vec2f32_ai();\n    return_arrf32_ai();\n    return_const_f32_const_ai();\n    return_vec2f32_const_ai();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-texture.toml",
    "content": "targets = \"METAL\"\n\n[msl]\nlang_version = [1, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-texture.wgsl",
    "content": "@group(0) @binding(0) var t: texture_2d<f32>;\n@group(0) @binding(1) var s: sampler;\n\nfn color() {\n  _ = textureSample(t, s, vec2(1,2));\n  _ = textureSample(t, s, vec2(1,2), vec2(3,4));\n  _ = textureSampleLevel(t, s, vec2(1,2), 0);\n  _ = textureSampleLevel(t, s, vec2(1,2), 0.0);\n  _ = textureSampleGrad(t, s, vec2(1,2), vec2(3,4), vec2(5,6));\n  _ = textureSampleBias(t, s, vec2(1,2), 1);\n}\n\n@group(0) @binding(2) var d: texture_depth_2d;\n@group(0) @binding(3) var c: sampler_comparison;\n\nfn depth() {\n  _ = textureSampleLevel(d, s, vec2(1,2), 1i);\n  _ = textureSampleCompare(d, c, vec2(1,2), 0);\n  _ = textureGatherCompare(d, c, vec2(1,2), 0);\n}\n\n@group(0) @binding(4) var st: texture_storage_2d<rgba8unorm, read_write>;\n\nfn storage() {\n  textureStore(st, vec2(0,1), vec4(2,3,4,5));\n}\n\n@fragment\nfn main() {\n    color();\n    depth();\n    storage();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-var.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/abstract-types-var.wgsl",
    "content": "// i/x: type inferred / explicit\n// vX/mX/aX: vector / matrix / array of X\n//     where X: u/i/f: u32 / i32 / f32\n// s: vector splat\n// r: vector spread (vector arg to vector constructor)\n// p: \"partial\" constructor (type parameter inferred)\n// u/i/f/ai/af: u32 / i32 / f32 / abstract float / abstract integer as parameter\n// _: just for alignment\n\n// Ensure that:\n// - the inferred type is correct.\n// - all parameters' types are considered.\n// - all parameters are converted to the consensus type.\n\nvar<private> xvipaiai: vec2<i32> = vec2(42, 43);\nvar<private> xvupaiai: vec2<u32> = vec2(44, 45);\nvar<private> xvfpaiai: vec2<f32> = vec2(46, 47);\nvar<private> xvfpafaf: vec2<f32> = vec2(48.0, 49.0);\nvar<private> xvfpaiaf: vec2<f32> = vec2(48, 49.0);\n\nvar<private> xvupuai: vec2<u32> = vec2(42u, 43);\nvar<private> xvupaiu: vec2<u32> = vec2(42, 43u); \n\nvar<private> xvuuai: vec2<u32> = vec2<u32>(42u, 43);\nvar<private> xvuaiu: vec2<u32> = vec2<u32>(42, 43u);\n\nvar<private> xvip____: vec2<i32> = vec2();\nvar<private> xvup____: vec2<u32> = vec2();\nvar<private> xvfp____: vec2<f32> = vec2();\nvar<private> xmfp____: mat2x2f = mat2x2(vec2(), vec2());\n\nvar<private> xmfpaiaiaiai: mat2x2<f32> = mat2x2(1, 2, 3, 4);\nvar<private> xmfpafaiaiai: mat2x2<f32> = mat2x2(1.0, 2, 3, 4);\nvar<private> xmfpaiafaiai: mat2x2<f32> = mat2x2(1, 2.0, 3, 4);\nvar<private> xmfpaiaiafai: mat2x2<f32> = mat2x2(1, 2, 3.0, 4);\nvar<private> xmfpaiaiaiaf: mat2x2<f32> = mat2x2(1, 2, 3, 4.0);\n\nvar<private> xvispai: vec2<i32> = vec2(1);\nvar<private> xvfspaf: vec2<f32> = vec2(1.0);\nvar<private> xvis_ai: vec2<i32> = vec2<i32>(1);\nvar<private> xvus_ai: vec2<u32> = vec2<u32>(1);\nvar<private> xvfs_ai: vec2<f32> = vec2<f32>(1);\nvar<private> xvfs_af: vec2<f32> = vec2<f32>(1.0);\n\nvar<private> xafafaf: array<f32, 2> = array<f32, 2>(1.0, 2.0);\nvar<private> xafaiai: array<f32, 2> = array<f32, 2>(1, 2);\n\nvar<private> xaipaiai: array<i32, 2> = array(1,   2);\nvar<private> xaupaiai: array<u32, 2> = array(1,   2);\nvar<private> xafpaiaf: array<f32, 2> = array(1,   2.0);\nvar<private> xafpafai: array<f32, 2> = array(1.0, 2);\nvar<private> xafpafaf: array<f32, 2> = array(1.0, 2.0);\n\nvar<private> xavipai: array<vec3<i32>, 1> = array(vec3(1));\nvar<private> xavfpai: array<vec3<f32>, 1> = array(vec3(1));\nvar<private> xavfpaf: array<vec3<f32>, 1> = array(vec3(1.0));\n\n// Construction with splats\nvar<private> xvisai: vec2<i32> = vec2(1);\nvar<private> xvusai: vec2<u32> = vec2(1);\nvar<private> xvfsai: vec2<f32> = vec2(1);\nvar<private> xvfsaf: vec2<f32> = vec2(1.0);\n\nvar<private> ivispai = vec2(1);\nvar<private> ivfspaf = vec2(1.0);\nvar<private> ivis_ai = vec2<i32>(1);\nvar<private> ivus_ai = vec2<u32>(1);\nvar<private> ivfs_ai = vec2<f32>(1);\nvar<private> ivfs_af = vec2<f32>(1.0);\n\nvar<private> iafafaf = array<f32, 2>(1.0, 2.0);\nvar<private> iafaiai = array<f32, 2>(1, 2);\n\nvar<private> iaipaiai = array(1,   2);\nvar<private> iafpafaf = array(1.0, 2.0);\nvar<private> iafpaiaf = array(1, 2.0);\nvar<private> iafpafai = array(1.0, 2);\n\nvar<private> iavipai = array(vec3(1));\nvar<private> iavfpai = array(vec3(1));\nvar<private> iavfpaf = array(vec3(1.0));\n\nfn globals() {\n    _ = xvipaiai;\n    _ = xvupaiai;\n    _ = xvfpaiai;\n    _ = xvfpafaf;\n    _ = xvfpaiaf;\n\n    _ = xvupuai;\n    _ = xvupaiu;\n\n    _ = xvuuai;\n    _ = xvuaiu;\n\n    _ = xvip____;\n    _ = xvup____;\n    _ = xvfp____;\n    _ = xmfp____;\n\n    _ = xmfpaiaiaiai;\n    _ = xmfpafaiaiai;\n    _ = xmfpaiafaiai;\n    _ = xmfpaiaiafai;\n    _ = xmfpaiaiaiaf;\n\n    _ = xvispai;\n    _ = xvfspaf;\n    _ = xvis_ai;\n    _ = xvus_ai;\n    _ = xvfs_ai;\n    _ = xvfs_af;\n\n    _ = xafafaf;\n    _ = xafaiai;\n\n    _ = xaipaiai;\n    _ = xaupaiai;\n    _ = xafpaiaf;\n    _ = xafpafai;\n    _ = xafpafaf;\n\n    _ = xavipai;\n    _ = xavfpai;\n    _ = xavfpaf;\n\n    // Construction with splats\n    _ = xvisai;\n    _ = xvusai;\n    _ = xvfsai;\n    _ = xvfsaf;\n\n    _ = ivispai;\n    _ = ivfspaf;\n    _ = ivis_ai;\n    _ = ivus_ai;\n    _ = ivfs_ai;\n    _ = ivfs_af;\n\n    _ = iafafaf;\n    _ = iafaiai;\n\n    _ = iaipaiai;\n    _ = iafpafaf;\n    _ = iafpaiaf;\n    _ = iafpafai;\n\n    _ = iavipai;\n    _ = iavfpai;\n    _ = iavfpaf;\n}\n\nfn all_constant_arguments() {\n    var xvipaiai: vec2<i32> = vec2(42, 43);\n    var xvupaiai: vec2<u32> = vec2(44, 45);\n    var xvfpaiai: vec2<f32> = vec2(46, 47);\n    var xvfpafaf: vec2<f32> = vec2(48.0, 49.0);\n    var xvfpaiaf: vec2<f32> = vec2(48, 49.0);\n\n    var xvupuai: vec2<u32> = vec2(42u, 43);\n    var xvupaiu: vec2<u32> = vec2(42, 43u);\n\n    var xvuuai: vec2<u32> = vec2<u32>(42u, 43);\n    var xvuaiu: vec2<u32> = vec2<u32>(42, 43u);\n\n    var xvip____: vec2<i32> = vec2();\n    var xvup____: vec2<u32> = vec2();\n    var xvfp____: vec2<f32> = vec2();\n    var xmfp____: mat2x2f = mat2x2(vec2(), vec2());\n\n    var xmfpaiaiaiai: mat2x2<f32> = mat2x2(1, 2, 3, 4);\n    var xmfpafaiaiai: mat2x2<f32> = mat2x2(1.0, 2, 3, 4);\n    var xmfpaiafaiai: mat2x2<f32> = mat2x2(1, 2.0, 3, 4);\n    var xmfpaiaiafai: mat2x2<f32> = mat2x2(1, 2, 3.0, 4);\n    var xmfpaiaiaiaf: mat2x2<f32> = mat2x2(1, 2, 3, 4.0);\n\n    var xmfp_faiaiai: mat2x2<f32> = mat2x2(1.0f, 2, 3, 4);\n    var xmfpai_faiai: mat2x2<f32> = mat2x2(1, 2.0f, 3, 4);\n    var xmfpaiai_fai: mat2x2<f32> = mat2x2(1, 2, 3.0f, 4);\n    var xmfpaiaiai_f: mat2x2<f32> = mat2x2(1, 2, 3, 4.0f);\n\n    var xvispai: vec2<i32> = vec2(1);\n    var xvfspaf: vec2<f32> = vec2(1.0);\n    var xvis_ai: vec2<i32> = vec2<i32>(1);\n    var xvus_ai: vec2<u32> = vec2<u32>(1);\n    var xvfs_ai: vec2<f32> = vec2<f32>(1);\n    var xvfs_af: vec2<f32> = vec2<f32>(1.0);\n\n    var xafafaf: array<f32, 2> = array<f32, 2>(1.0, 2.0);\n    var xaf_faf: array<f32, 2> = array<f32, 2>(1.0f, 2.0);\n    var xafaf_f: array<f32, 2> = array<f32, 2>(1.0, 2.0f);\n    var xafaiai: array<f32, 2> = array<f32, 2>(1, 2);\n    var xai_iai: array<i32, 2> = array<i32, 2>(1i, 2);\n    var xaiai_i: array<i32, 2> = array<i32, 2>(1, 2i);\n\n    var xaipaiai: array<i32, 2> = array(1,   2);\n    var xafpaiai: array<f32, 2> = array(1,   2);\n    var xafpaiaf: array<f32, 2> = array(1,   2.0);\n    var xafpafai: array<f32, 2> = array(1.0, 2);\n    var xafpafaf: array<f32, 2> = array(1.0, 2.0);\n\n    var xavipai: array<vec3<i32>, 1> = array(vec3(1));\n    var xavfpai: array<vec3<f32>, 1> = array(vec3(1));\n    var xavfpaf: array<vec3<f32>, 1> = array(vec3(1.0));\n\n    // Construction with splats\n    var xvisai: vec2<i32> = vec2(1);\n    var xvusai: vec2<u32> = vec2(1);\n    var xvfsai: vec2<f32> = vec2(1);\n    var xvfsaf: vec2<f32> = vec2(1.0);\n\n    var iaipaiai = array(1,   2);\n    var iafpaiaf = array(1,   2.0);\n    var iafpafai = array(1.0, 2);\n    var iafpafaf = array(1.0, 2.0);\n\n    // Assignments to all of the above.\n    xvipaiai = vec2(42, 43);\n    xvupaiai = vec2(44, 45);\n    xvfpaiai = vec2(46, 47);\n    xvfpafaf = vec2(48.0, 49.0);\n    xvfpaiaf = vec2(48, 49.0);\n\n    xvupuai = vec2(42u, 43);\n    xvupaiu = vec2(42, 43u);\n\n    xvuuai = vec2<u32>(42u, 43);\n    xvuaiu = vec2<u32>(42, 43u);\n\n    xvip____ = vec2();\n    xvup____ = vec2();\n    xvfp____ = vec2();\n    xmfp____ = mat2x2(vec2(), vec2());\n\n    xmfpaiaiaiai = mat2x2(1, 2, 3, 4);\n    xmfpafaiaiai = mat2x2(1.0, 2, 3, 4);\n    xmfpaiafaiai = mat2x2(1, 2.0, 3, 4);\n    xmfpaiaiafai = mat2x2(1, 2, 3.0, 4);\n    xmfpaiaiaiaf = mat2x2(1, 2, 3, 4.0);\n\n    xmfp_faiaiai = mat2x2(1.0f, 2, 3, 4);\n    xmfpai_faiai = mat2x2(1, 2.0f, 3, 4);\n    xmfpaiai_fai = mat2x2(1, 2, 3.0f, 4);\n    xmfpaiaiai_f = mat2x2(1, 2, 3, 4.0f);\n\n    xvispai = vec2(1);\n    xvfspaf = vec2(1.0);\n    xvis_ai = vec2<i32>(1);\n    xvus_ai = vec2<u32>(1);\n    xvfs_ai = vec2<f32>(1);\n    xvfs_af = vec2<f32>(1.0);\n\n    xafafaf = array<f32, 2>(1.0, 2.0);\n    xaf_faf = array<f32, 2>(1.0f, 2.0);\n    xafaf_f = array<f32, 2>(1.0, 2.0f);\n    xafaiai = array<f32, 2>(1, 2);\n    xai_iai = array<i32, 2>(1i, 2);\n    xaiai_i = array<i32, 2>(1, 2i);\n\n    xaipaiai = array(1,   2);\n    xafpaiai = array(1,   2);\n    xafpaiaf = array(1,   2.0);\n    xafpafai = array(1.0, 2);\n    xafpafaf = array(1.0, 2.0);\n\n    xavipai = array(vec3(1));\n    xavfpai = array(vec3(1));\n    xavfpaf = array(vec3(1.0));\n\n    // Construction with splats\n    xvisai = vec2(1);\n    xvusai = vec2(1);\n    xvfsai = vec2(1);\n    xvfsaf = vec2(1.0);\n\n    iaipaiai = array(1,   2);\n    iafpaiaf = array(1,   2.0);\n    iafpafai = array(1.0, 2);\n    iafpafaf = array(1.0, 2.0);\n}\n\nfn mixed_constant_and_runtime_arguments() {\n    var u: u32;\n    var i: i32;\n    var f: f32;\n\n    var xvupuai: vec2<u32> = vec2(u,  43);\n    var xvupaiu: vec2<u32> = vec2(42, u);\n    var xvfpfai: vec2<f32> = vec2(f, 47); // differs slightly from const version\n    var xvfpfaf: vec2<f32> = vec2(f, 49.0);\n\n    var xvuuai: vec2<u32> = vec2<u32>(u, 43);\n    var xvuaiu: vec2<u32> = vec2<u32>(42, u);\n\n    var xmfp_faiaiai: mat2x2<f32> = mat2x2(f, 2, 3, 4);\n    var xmfpai_faiai: mat2x2<f32> = mat2x2(1, f, 3, 4);\n    var xmfpaiai_fai: mat2x2<f32> = mat2x2(1, 2, f, 4);\n    var xmfpaiaiai_f: mat2x2<f32> = mat2x2(1, 2, 3, f);\n\n    var xaf_faf: array<f32, 2> = array<f32, 2>(f, 2.0);\n    var xafaf_f: array<f32, 2> = array<f32, 2>(1.0, f);\n    var xaf_fai: array<f32, 2> = array<f32, 2>(f, 2);\n    var xafai_f: array<f32, 2> = array<f32, 2>(1, f);\n    var xai_iai: array<i32, 2> = array<i32, 2>(i, 2);\n    var xaiai_i: array<i32, 2> = array<i32, 2>(1, i);\n\n    var xafp_faf: array<f32, 2> = array(f, 2.0);\n    var xafpaf_f: array<f32, 2> = array(1.0, f);\n    var xafp_fai: array<f32, 2> = array(f, 2);\n    var xafpai_f: array<f32, 2> = array(1, f);\n    var xaip_iai: array<i32, 2> = array(i, 2);\n    var xaipai_i: array<i32, 2> = array(1, i);\n\n    var xvisi: vec2<i32> = vec2(i);\n    var xvusu: vec2<u32> = vec2(u);\n    var xvfsf: vec2<f32> = vec2(f);\n\n    // Assignments to all of the above.\n    xvupuai = vec2(u,  43);\n    xvupaiu = vec2(42, u);\n\n    xvuuai = vec2<u32>(u, 43);\n    xvuaiu = vec2<u32>(42, u);\n\n    xmfp_faiaiai = mat2x2(f, 2, 3, 4);\n    xmfpai_faiai = mat2x2(1, f, 3, 4);\n    xmfpaiai_fai = mat2x2(1, 2, f, 4);\n    xmfpaiaiai_f = mat2x2(1, 2, 3, f);\n\n    xaf_faf = array<f32, 2>(f, 2.0);\n    xafaf_f = array<f32, 2>(1.0, f);\n    xaf_fai = array<f32, 2>(f, 2);\n    xafai_f = array<f32, 2>(1, f);\n    xai_iai = array<i32, 2>(i, 2);\n    xaiai_i = array<i32, 2>(1, i);\n\n    xafp_faf = array(f, 2.0);\n    xafpaf_f = array(1.0, f);\n    xafp_fai = array(f, 2);\n    xafpai_f = array(1, f);\n    xaip_iai = array(i, 2);\n    xaipai_i = array(1, i);\n\n    xvisi = vec2(i);\n    xvusu = vec2(u);\n    xvfsf = vec2(f);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    globals();\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/access.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | HLSL | WGSL | IR | ANALYSIS\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.atomics]\nresources = [\n    { bind_target = { buffer = 0, mutable = true }, resource_binding = { group = 0, binding = 0 } },\n]\nsizes_buffer = 24\n\n[msl.per_entry_point_map.foo_frag]\nresources = [\n    { bind_target = { buffer = 0, mutable = true }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { buffer = 2, mutable = true }, resource_binding = { group = 0, binding = 2 } },\n]\nsizes_buffer = 24\n\n[msl.per_entry_point_map.foo_vert]\nresources = [\n    { bind_target = { buffer = 0, mutable = false }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { buffer = 1, mutable = false }, resource_binding = { group = 0, binding = 1 } },\n    { bind_target = { buffer = 2, mutable = false }, resource_binding = { group = 0, binding = 2 } },\n    { bind_target = { buffer = 3, mutable = false }, resource_binding = { group = 0, binding = 3 } },\n]\nsizes_buffer = 24\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/access.wgsl",
    "content": "// This snapshot tests accessing various containers, dereferencing pointers.\n\nstruct GlobalConst {\n    a: u32,\n    b: vec3<u32>,\n    c: i32,\n}\n// tests msl padding insertion for global constants\nvar<private> msl_padding_global_const: GlobalConst = GlobalConst(0u, vec3<u32>(0u, 0u, 0u), 0);\n\nstruct AlignedWrapper {\n    @align(8) value: i32\n}\n\nstruct Bar {\n    _matrix: mat4x3<f32>,\n    matrix_array: array<mat2x2<f32>, 2>,\n    atom: atomic<i32>,\n    atom_arr: array<atomic<i32>, 10>,\n    arr: array<vec2<u32>, 2>,\n    data: array<AlignedWrapper>,\n}\n\n@group(0) @binding(0)\nvar<storage,read_write> bar: Bar;\n\nstruct Baz {\n    m: mat3x2<f32>,\n}\n\n@group(0) @binding(1)\nvar<uniform> baz: Baz;\n\n@group(0) @binding(2)\nvar<storage,read_write> qux: vec2<i32>;\n\nfn test_matrix_within_struct_accesses() {\n    // Test accesses to Cx2 matrices. There are additional tests in\n    // `mat_cx2.wgsl`.\n\n    var idx = 1;\n\n    idx--;\n\n    // loads\n    let l0 = baz.m;\n    let l1 = baz.m[0];\n    let l2 = baz.m[idx];\n    let l3 = baz.m[0][1];\n    let l4 = baz.m[0][idx];\n    let l5 = baz.m[idx][1];\n    let l6 = baz.m[idx][idx];\n\n    var t = Baz(mat3x2<f32>(vec2<f32>(1.0), vec2<f32>(2.0), vec2<f32>(3.0)));\n\n    idx++;\n\n    // stores\n    t.m = mat3x2<f32>(vec2<f32>(6.0), vec2<f32>(5.0), vec2<f32>(4.0));\n    t.m[0] = vec2<f32>(9.0);\n    t.m[idx] = vec2<f32>(90.0);\n    t.m[0][1] = 10.0;\n    t.m[0][idx] = 20.0;\n    t.m[idx][1] = 30.0;\n    t.m[idx][idx] = 40.0;\n}\n\nstruct MatCx2InArray {\n    am: array<mat4x2<f32>, 2>,\n}\n\n@group(0) @binding(3)\nvar<uniform> nested_mat_cx2: MatCx2InArray;\n\nfn test_matrix_within_array_within_struct_accesses() {\n    var idx = 1;\n\n    idx--;\n\n    // loads\n    let l0 = nested_mat_cx2.am;\n    let l1 = nested_mat_cx2.am[0];\n    let l2 = nested_mat_cx2.am[0][0];\n    let l3 = nested_mat_cx2.am[0][idx];\n    let l4 = nested_mat_cx2.am[0][0][1];\n    let l5 = nested_mat_cx2.am[0][0][idx];\n    let l6 = nested_mat_cx2.am[0][idx][1];\n    let l7 = nested_mat_cx2.am[0][idx][idx];\n\n    var t = MatCx2InArray(array<mat4x2<f32>, 2>());\n\n    idx++;\n\n    // stores\n    t.am = array<mat4x2<f32>, 2>();\n    t.am[0] = mat4x2<f32>(vec2<f32>(8.0), vec2<f32>(7.0), vec2<f32>(6.0), vec2<f32>(5.0));\n    t.am[0][0] = vec2<f32>(9.0);\n    t.am[0][idx] = vec2<f32>(90.0);\n    t.am[0][0][1] = 10.0;\n    t.am[0][0][idx] = 20.0;\n    t.am[0][idx][1] = 30.0;\n    t.am[0][idx][idx] = 40.0;\n}\n\nfn read_from_private(foo: ptr<function, f32>) -> f32 {\n    return *foo;\n}\n\nfn test_arr_as_arg(a: array<array<f32, 10>, 5>) -> f32 {\n    return a[4][9];\n}\n\n@vertex\nfn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n    var foo: f32 = 0.0;\n    // We should check that backed doesn't skip this expression\n    let baz: f32 = foo;\n    foo = 1.0;\n\n    _ = msl_padding_global_const;\n    test_matrix_within_struct_accesses();\n    test_matrix_within_array_within_struct_accesses();\n\n    // test storage loads\n    let _matrix = bar._matrix;\n    let arr = bar.arr;\n    let index = 3u;\n    let b = bar._matrix[index].x;\n    let a = bar.data[arrayLength(&bar.data) - 2u].value;\n    let c = qux;\n\n    // test pointer types\n    let data_pointer: ptr<storage, i32, read_write> = &bar.data[0].value;\n    let foo_value = read_from_private(&foo);\n\n    // test array indexing\n    var c2 = array<i32, 5>(a, i32(b), 3, 4, 5);\n    c2[vi + 1u] = 42;\n    let value = c2[vi];\n\n    test_arr_as_arg(array<array<f32, 10>, 5>());\n\n    return vec4<f32>(_matrix * vec4<f32>(vec4<i32>(value)), 2.0);\n}\n\n@fragment\nfn foo_frag() -> @location(0) vec4<f32> {\n    // test storage stores\n    bar._matrix[1].z = 1.0;\n    bar._matrix = mat4x3<f32>(vec3<f32>(0.0), vec3<f32>(1.0), vec3<f32>(2.0), vec3<f32>(3.0));\n    bar.arr = array<vec2<u32>, 2>(vec2<u32>(0u), vec2<u32>(1u));\n    bar.data[1].value = 1;\n    qux = vec2<i32>();\n\n    return vec4<f32>(0.0);\n}\n\nfn assign_through_ptr_fn(p: ptr<function, u32>) {\n    *p = 42u;\n}\n\nfn assign_array_through_ptr_fn(foo: ptr<function, array<vec4<f32>, 2>>) {\n    *foo = array<vec4<f32>, 2>(vec4(1.0), vec4(2.0));\n}\n\nfn assign_through_ptr() {\n    var val = 33u;\n    assign_through_ptr_fn(&val);\n\n    var arr = array<vec4<f32>, 2>(vec4(6.0), vec4(7.0));\n    assign_array_through_ptr_fn(&arr);\n}\n\nstruct AssignToMember {\n  x: u32,\n}\n\nfn fetch_arg_ptr_member(p: ptr<function, AssignToMember>) -> u32 {\n  return (*p).x;\n}\n\nfn assign_to_arg_ptr_member(p: ptr<function, AssignToMember>) {\n  (*p).x = 10u;\n}\n\nfn fetch_arg_ptr_array_element(p: ptr<function, array<u32, 4>>) -> u32 {\n  return (*p)[1];\n}\n\nfn assign_to_arg_ptr_array_element(p: ptr<function, array<u32, 4>>) {\n  (*p)[1] = 10u;\n}\n\nfn assign_to_ptr_components() {\n   var s1: AssignToMember;\n   assign_to_arg_ptr_member(&s1);\n   fetch_arg_ptr_member(&s1);\n\n   var a1: array<u32, 4>;\n   assign_to_arg_ptr_array_element(&a1);\n   fetch_arg_ptr_array_element(&a1);\n}\n\nfn index_ptr(value: bool) -> bool {\n    var a = array<bool, 1>(value);\n    let p = &a;\n    return p[0];\n}\n\nstruct S { m: i32 };\n\nfn member_ptr() -> i32 {\n    var s: S = S(42);\n    let p = &s;\n    return p.m;\n}\n\nstruct Inner { delicious: i32 }\n\nstruct Outer { om_nom_nom: Inner, thing: u32 }\n\nfn let_members_of_members() -> i32 {\n    let thing = Outer();\n\n    let inner = thing.om_nom_nom;\n    let delishus = inner.delicious;\n\n    if (thing.thing != u32(delishus)) {\n        // LOL\n    }\n\n    return thing.om_nom_nom.delicious;\n}\n\nfn var_members_of_members() -> i32 {\n    var thing = Outer();\n\n    var inner = thing.om_nom_nom;\n    var delishus = inner.delicious;\n\n    if (thing.thing != u32(delishus)) {\n        // LOL\n    }\n\n    return thing.om_nom_nom.delicious;\n}\n\n@compute @workgroup_size(1)\nfn foo_compute() {\n    assign_through_ptr();\n    assign_to_ptr_components();\n    index_ptr(true);\n    member_ptr();\n    let_members_of_members();\n    var_members_of_members();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/aliased-ray-query.toml",
    "content": "capabilities = \"RAY_QUERY\"\ntargets = \"SPIRV | METAL | HLSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = false\n\n[hlsl]\nshader_model = \"V6_5\"\nfake_missing_bindings = true\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 4]\n"
  },
  {
    "path": "naga/tests/in/wgsl/aliased-ray-query.wgsl",
    "content": "enable wgpu_ray_query;\n\nalias rq = ray_query;\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\n@compute @workgroup_size(1)\nfn main_candidate() {\n    let pos = vec3<f32>(0.0);\n    let dir = vec3<f32>(0.0, 1.0, 0.0);\n\n    var rq: rq;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT, 0xFFu, 0.1, 100.0, pos, dir));\n    let intersection = rayQueryGetCandidateIntersection(&rq);\n    if (intersection.kind == RAY_QUERY_INTERSECTION_AABB) {\n        rayQueryGenerateIntersection(&rq, 10.0);\n    } else if (intersection.kind == RAY_QUERY_INTERSECTION_TRIANGLE) {\n        rayQueryConfirmIntersection(&rq);\n    } else {\n        rayQueryTerminate(&rq);\n    }\n}"
  },
  {
    "path": "naga/tests/in/wgsl/array-in-ctor.wgsl",
    "content": "struct Ah {\n    inner: array<f32, 2>,\n};\n@group(0) @binding(0)\nvar<storage> ah: Ah;\n\n@compute @workgroup_size(1)\nfn cs_main() {\n    let ah = ah;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/array-in-function-return-type.wgsl",
    "content": "fn ret_array() -> array<f32, 2> {\n    return array<f32, 2>(1.0, 2.0);\n}\n\nfn ret_array_array() -> array<array<f32, 2>, 3> {\n    return array<array<f32, 2>, 3>(ret_array(), ret_array(), ret_array());\n}\n\n@fragment\nfn main() -> @location(0) vec4<f32> {\n    let a = ret_array_array();\n    return vec4<f32>(a[0][0], a[0][1], 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicCompareExchange-int64.toml",
    "content": "capabilities = \"SHADER_INT64 | SHADER_INT64_ATOMIC_ALL_OPS\"\ntargets = \"SPIRV | HLSL | WGSL\"\n\n[hlsl]\nshader_model = \"V6_6\"\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicCompareExchange-int64.wgsl",
    "content": "const SIZE: u32 = 128u;\n\n@group(0) @binding(0)\nvar<storage,read_write> arr_i64: array<atomic<i64>, SIZE>;\n@group(0) @binding(1)\nvar<storage,read_write> arr_u64: array<atomic<u64>, SIZE>;\n\n@compute @workgroup_size(1)\nfn test_atomic_compare_exchange_i64() {\n    for(var i = 0u; i < SIZE; i++) {\n        var old : i64 = atomicLoad(&arr_i64[i]);\n        var exchanged = false;\n        while(!exchanged) {\n            let new_ : i64 = bitcast<i64>(old + 10li);\n            let result = atomicCompareExchangeWeak(&arr_i64[i], old, new_);\n            old = result.old_value;\n            exchanged = result.exchanged;\n        }\n    }\n}\n\n@compute @workgroup_size(1)\nfn test_atomic_compare_exchange_u64() {\n    for(var i = 0u; i < SIZE; i++) {\n        var old : u64 = atomicLoad(&arr_u64[i]);\n        var exchanged = false;\n        while(!exchanged) {\n            let new_ : u64 = bitcast<u64>(old + 10lu);\n            let result = atomicCompareExchangeWeak(&arr_u64[i], old, new_);\n            old = result.old_value;\n            exchanged = result.exchanged;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicCompareExchange.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | HLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicCompareExchange.wgsl",
    "content": "const SIZE: u32 = 128u;\n\n@group(0) @binding(0)\nvar<storage,read_write> arr_i32: array<atomic<i32>, SIZE>;\n@group(0) @binding(1)\nvar<storage,read_write> arr_u32: array<atomic<u32>, SIZE>;\n\n@compute @workgroup_size(1)\nfn test_atomic_compare_exchange_i32() {\n    for(var i = 0u; i < SIZE; i++) {\n        var old = atomicLoad(&arr_i32[i]);\n        var exchanged = false;\n        while(!exchanged) {\n            let new_ = bitcast<i32>(bitcast<f32>(old) + 1.0);\n            let result = atomicCompareExchangeWeak(&arr_i32[i], old, new_);\n            old = result.old_value;\n            exchanged = result.exchanged;\n        }\n    }\n}\n\n@compute @workgroup_size(1)\nfn test_atomic_compare_exchange_u32() {\n    for(var i = 0u; i < SIZE; i++) {\n        var old = atomicLoad(&arr_u32[i]);\n        var exchanged = false;\n        while(!exchanged) {\n            let new_ = bitcast<u32>(bitcast<f32>(old) + 1.0);\n            let result = atomicCompareExchangeWeak(&arr_u32[i], old, new_);\n            old = result.old_value;\n            exchanged = result.exchanged;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-float32.toml",
    "content": "capabilities = \"SHADER_FLOAT32_ATOMIC\"\ntargets = \"SPIRV | METAL | WGSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [3, 0]\nzero_initialize_workgroup_memory = false\n\n[spv]\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-float32.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<f32>,\n    atomic_arr: array<atomic<f32>, 2>,\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> storage_atomic_scalar: atomic<f32>;\n@group(0) @binding(1)\nvar<storage, read_write> storage_atomic_arr: array<atomic<f32>, 2>;\n@group(0) @binding(2)\nvar<storage, read_write> storage_struct: Struct;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore(&storage_atomic_scalar, 1.5);\n    atomicStore(&storage_atomic_arr[1], 1.5);\n    atomicStore(&storage_struct.atomic_scalar, 1.5);\n    atomicStore(&storage_struct.atomic_arr[1], 1.5);\n\n    workgroupBarrier();\n\n    let l0 = atomicLoad(&storage_atomic_scalar);\n    let l1 = atomicLoad(&storage_atomic_arr[1]);\n    let l2 = atomicLoad(&storage_struct.atomic_scalar);\n    let l3 = atomicLoad(&storage_struct.atomic_arr[1]);\n\n    workgroupBarrier();\n\n    atomicAdd(&storage_atomic_scalar, 1.5);\n    atomicAdd(&storage_atomic_arr[1], 1.5);\n    atomicAdd(&storage_struct.atomic_scalar, 1.5);\n    atomicAdd(&storage_struct.atomic_arr[1], 1.5);\n\n    workgroupBarrier();\n\n    atomicExchange(&storage_atomic_scalar, 1.5);\n    atomicExchange(&storage_atomic_arr[1], 1.5);\n    atomicExchange(&storage_struct.atomic_scalar, 1.5);\n    atomicExchange(&storage_struct.atomic_arr[1], 1.5);\n\n    // // TODO: https://github.com/gpuweb/gpuweb/issues/2021\n    // atomicCompareExchangeWeak(&storage_atomic_scalar, 1.5);\n    // atomicCompareExchangeWeak(&storage_atomic_arr[1], 1.5);\n    // atomicCompareExchangeWeak(&storage_struct.atomic_scalar, 1.5);\n    // atomicCompareExchangeWeak(&storage_struct.atomic_arr[1], 1.5);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-int64-min-max.toml",
    "content": "capabilities = \"SHADER_INT64 | SHADER_INT64_ATOMIC_MIN_MAX\"\ntargets = \"SPIRV | METAL | HLSL | WGSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_6\"\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-int64-min-max.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<u64>,\n    atomic_arr: array<atomic<u64>, 2>,\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> storage_atomic_scalar: atomic<u64>;\n@group(0) @binding(1)\nvar<storage, read_write> storage_atomic_arr: array<atomic<u64>, 2>;\n@group(0) @binding(2)\nvar<storage, read_write> storage_struct: Struct;\n@group(0) @binding(3)\nvar<uniform> input: u64;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicMax(&storage_atomic_scalar, input);\n    atomicMax(&storage_atomic_arr[1], 1 + input);\n    atomicMax(&storage_struct.atomic_scalar, 1lu);\n    atomicMax(&storage_struct.atomic_arr[1], u64(id.x));\n\n    workgroupBarrier();\n\n    atomicMin(&storage_atomic_scalar, input);\n    atomicMin(&storage_atomic_arr[1], 1 + input);\n    atomicMin(&storage_struct.atomic_scalar, 1lu);\n    atomicMin(&storage_struct.atomic_arr[1], u64(id.x));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-int64.toml",
    "content": "capabilities = \"SHADER_INT64 | SHADER_INT64_ATOMIC_ALL_OPS\"\ntargets = \"SPIRV | HLSL | WGSL\"\n\n[hlsl]\nshader_model = \"V6_6\"\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps-int64.wgsl",
    "content": "// This test covers the cross product of:\n//\n// * All int64 atomic operations.\n// * On all applicable scopes (storage read-write, workgroup).\n// * For all shapes of modeling atomic data.\n\nstruct Struct {\n    atomic_scalar: atomic<u64>,\n    atomic_arr: array<atomic<i64>, 2>,\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> storage_atomic_scalar: atomic<u64>;\n@group(0) @binding(1)\nvar<storage, read_write> storage_atomic_arr: array<atomic<i64>, 2>;\n@group(0) @binding(2)\nvar<storage, read_write> storage_struct: Struct;\n\nvar<workgroup> workgroup_atomic_scalar: atomic<u64>;\nvar<workgroup> workgroup_atomic_arr: array<atomic<i64>, 2>;\nvar<workgroup> workgroup_struct: Struct;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore(&storage_atomic_scalar, 1lu);\n    atomicStore(&storage_atomic_arr[1], 1li);\n    atomicStore(&storage_struct.atomic_scalar, 1lu);\n    atomicStore(&storage_struct.atomic_arr[1], 1li);\n    atomicStore(&workgroup_atomic_scalar, 1lu);\n    atomicStore(&workgroup_atomic_arr[1], 1li);\n    atomicStore(&workgroup_struct.atomic_scalar, 1lu);\n    atomicStore(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    let l0 = atomicLoad(&storage_atomic_scalar);\n    let l1 = atomicLoad(&storage_atomic_arr[1]);\n    let l2 = atomicLoad(&storage_struct.atomic_scalar);\n    let l3 = atomicLoad(&storage_struct.atomic_arr[1]);\n    let l4 = atomicLoad(&workgroup_atomic_scalar);\n    let l5 = atomicLoad(&workgroup_atomic_arr[1]);\n    let l6 = atomicLoad(&workgroup_struct.atomic_scalar);\n    let l7 = atomicLoad(&workgroup_struct.atomic_arr[1]);\n\n    workgroupBarrier();\n\n    atomicAdd(&storage_atomic_scalar, 1lu);\n    atomicAdd(&storage_atomic_arr[1], 1li);\n    atomicAdd(&storage_struct.atomic_scalar, 1lu);\n    atomicAdd(&storage_struct.atomic_arr[1], 1li);\n    atomicAdd(&workgroup_atomic_scalar, 1lu);\n    atomicAdd(&workgroup_atomic_arr[1], 1li);\n    atomicAdd(&workgroup_struct.atomic_scalar, 1lu);\n    atomicAdd(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicSub(&storage_atomic_scalar, 1lu);\n    atomicSub(&storage_atomic_arr[1], 1li);\n    atomicSub(&storage_struct.atomic_scalar, 1lu);\n    atomicSub(&storage_struct.atomic_arr[1], 1li);\n    atomicSub(&workgroup_atomic_scalar, 1lu);\n    atomicSub(&workgroup_atomic_arr[1], 1li);\n    atomicSub(&workgroup_struct.atomic_scalar, 1lu);\n    atomicSub(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicMax(&storage_atomic_scalar, 1lu);\n    atomicMax(&storage_atomic_arr[1], 1li);\n    atomicMax(&storage_struct.atomic_scalar, 1lu);\n    atomicMax(&storage_struct.atomic_arr[1], 1li);\n    atomicMax(&workgroup_atomic_scalar, 1lu);\n    atomicMax(&workgroup_atomic_arr[1], 1li);\n    atomicMax(&workgroup_struct.atomic_scalar, 1lu);\n    atomicMax(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicMin(&storage_atomic_scalar, 1lu);\n    atomicMin(&storage_atomic_arr[1], 1li);\n    atomicMin(&storage_struct.atomic_scalar, 1lu);\n    atomicMin(&storage_struct.atomic_arr[1], 1li);\n    atomicMin(&workgroup_atomic_scalar, 1lu);\n    atomicMin(&workgroup_atomic_arr[1], 1li);\n    atomicMin(&workgroup_struct.atomic_scalar, 1lu);\n    atomicMin(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicAnd(&storage_atomic_scalar, 1lu);\n    atomicAnd(&storage_atomic_arr[1], 1li);\n    atomicAnd(&storage_struct.atomic_scalar, 1lu);\n    atomicAnd(&storage_struct.atomic_arr[1], 1li);\n    atomicAnd(&workgroup_atomic_scalar, 1lu);\n    atomicAnd(&workgroup_atomic_arr[1], 1li);\n    atomicAnd(&workgroup_struct.atomic_scalar, 1lu);\n    atomicAnd(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicOr(&storage_atomic_scalar, 1lu);\n    atomicOr(&storage_atomic_arr[1], 1li);\n    atomicOr(&storage_struct.atomic_scalar, 1lu);\n    atomicOr(&storage_struct.atomic_arr[1], 1li);\n    atomicOr(&workgroup_atomic_scalar, 1lu);\n    atomicOr(&workgroup_atomic_arr[1], 1li);\n    atomicOr(&workgroup_struct.atomic_scalar, 1lu);\n    atomicOr(&workgroup_struct.atomic_arr[1], 1li);\n\n    workgroupBarrier();\n\n    atomicXor(&storage_atomic_scalar, 1lu);\n    atomicXor(&storage_atomic_arr[1], 1li);\n    atomicXor(&storage_struct.atomic_scalar, 1lu);\n    atomicXor(&storage_struct.atomic_arr[1], 1li);\n    atomicXor(&workgroup_atomic_scalar, 1lu);\n    atomicXor(&workgroup_atomic_arr[1], 1li);\n    atomicXor(&workgroup_struct.atomic_scalar, 1lu);\n    atomicXor(&workgroup_struct.atomic_arr[1], 1li);\n\n    atomicExchange(&storage_atomic_scalar, 1lu);\n    atomicExchange(&storage_atomic_arr[1], 1li);\n    atomicExchange(&storage_struct.atomic_scalar, 1lu);\n    atomicExchange(&storage_struct.atomic_arr[1], 1li);\n    atomicExchange(&workgroup_atomic_scalar, 1lu);\n    atomicExchange(&workgroup_atomic_arr[1], 1li);\n    atomicExchange(&workgroup_struct.atomic_scalar, 1lu);\n    atomicExchange(&workgroup_struct.atomic_arr[1], 1li);\n\n    let cas_res_0 = atomicCompareExchangeWeak(&storage_atomic_scalar, 1lu, 2lu);\n    let cas_res_1 = atomicCompareExchangeWeak(&storage_atomic_arr[1], 1li, 2li);\n    let cas_res_2 = atomicCompareExchangeWeak(&storage_struct.atomic_scalar, 1lu, 2lu);\n    let cas_res_3 = atomicCompareExchangeWeak(&storage_struct.atomic_arr[1], 1li, 2li);\n    let cas_res_4 = atomicCompareExchangeWeak(&workgroup_atomic_scalar, 1lu, 2lu);\n    let cas_res_5 = atomicCompareExchangeWeak(&workgroup_atomic_arr[1], 1li, 2li);\n    let cas_res_6 = atomicCompareExchangeWeak(&workgroup_struct.atomic_scalar, 1lu, 2lu);\n    let cas_res_7 = atomicCompareExchangeWeak(&workgroup_struct.atomic_arr[1], 1li, 2li);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicOps.wgsl",
    "content": "// This test covers the cross product of:\n//\n// * All atomic operations.\n// * On all applicable scopes (storage read-write, workgroup).\n// * For all shapes of modeling atomic data.\n\nstruct Struct {\n    atomic_scalar: atomic<u32>,\n    atomic_arr: array<atomic<i32>, 2>,\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> storage_atomic_scalar: atomic<u32>;\n@group(0) @binding(1)\nvar<storage, read_write> storage_atomic_arr: array<atomic<i32>, 2>;\n@group(0) @binding(2)\nvar<storage, read_write> storage_struct: Struct;\n\nvar<workgroup> workgroup_atomic_scalar: atomic<u32>;\nvar<workgroup> workgroup_atomic_arr: array<atomic<i32>, 2>;\nvar<workgroup> workgroup_struct: Struct;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore(&storage_atomic_scalar, 1u);\n    atomicStore(&storage_atomic_arr[1], 1i);\n    atomicStore(&storage_struct.atomic_scalar, 1u);\n    atomicStore(&storage_struct.atomic_arr[1], 1i);\n    atomicStore(&workgroup_atomic_scalar, 1u);\n    atomicStore(&workgroup_atomic_arr[1], 1i);\n    atomicStore(&workgroup_struct.atomic_scalar, 1u);\n    atomicStore(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    let l0 = atomicLoad(&storage_atomic_scalar);\n    let l1 = atomicLoad(&storage_atomic_arr[1]);\n    let l2 = atomicLoad(&storage_struct.atomic_scalar);\n    let l3 = atomicLoad(&storage_struct.atomic_arr[1]);\n    let l4 = atomicLoad(&workgroup_atomic_scalar);\n    let l5 = atomicLoad(&workgroup_atomic_arr[1]);\n    let l6 = atomicLoad(&workgroup_struct.atomic_scalar);\n    let l7 = atomicLoad(&workgroup_struct.atomic_arr[1]);\n\n    workgroupBarrier();\n\n    atomicAdd(&storage_atomic_scalar, 1u);\n    atomicAdd(&storage_atomic_arr[1], 1i);\n    atomicAdd(&storage_struct.atomic_scalar, 1u);\n    atomicAdd(&storage_struct.atomic_arr[1], 1i);\n    atomicAdd(&workgroup_atomic_scalar, 1u);\n    atomicAdd(&workgroup_atomic_arr[1], 1i);\n    atomicAdd(&workgroup_struct.atomic_scalar, 1u);\n    atomicAdd(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicSub(&storage_atomic_scalar, 1u);\n    atomicSub(&storage_atomic_arr[1], 1i);\n    atomicSub(&storage_struct.atomic_scalar, 1u);\n    atomicSub(&storage_struct.atomic_arr[1], 1i);\n    atomicSub(&workgroup_atomic_scalar, 1u);\n    atomicSub(&workgroup_atomic_arr[1], 1i);\n    atomicSub(&workgroup_struct.atomic_scalar, 1u);\n    atomicSub(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicMax(&storage_atomic_scalar, 1u);\n    atomicMax(&storage_atomic_arr[1], 1i);\n    atomicMax(&storage_struct.atomic_scalar, 1u);\n    atomicMax(&storage_struct.atomic_arr[1], 1i);\n    atomicMax(&workgroup_atomic_scalar, 1u);\n    atomicMax(&workgroup_atomic_arr[1], 1i);\n    atomicMax(&workgroup_struct.atomic_scalar, 1u);\n    atomicMax(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicMin(&storage_atomic_scalar, 1u);\n    atomicMin(&storage_atomic_arr[1], 1i);\n    atomicMin(&storage_struct.atomic_scalar, 1u);\n    atomicMin(&storage_struct.atomic_arr[1], 1i);\n    atomicMin(&workgroup_atomic_scalar, 1u);\n    atomicMin(&workgroup_atomic_arr[1], 1i);\n    atomicMin(&workgroup_struct.atomic_scalar, 1u);\n    atomicMin(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicAnd(&storage_atomic_scalar, 1u);\n    atomicAnd(&storage_atomic_arr[1], 1i);\n    atomicAnd(&storage_struct.atomic_scalar, 1u);\n    atomicAnd(&storage_struct.atomic_arr[1], 1i);\n    atomicAnd(&workgroup_atomic_scalar, 1u);\n    atomicAnd(&workgroup_atomic_arr[1], 1i);\n    atomicAnd(&workgroup_struct.atomic_scalar, 1u);\n    atomicAnd(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicOr(&storage_atomic_scalar, 1u);\n    atomicOr(&storage_atomic_arr[1], 1i);\n    atomicOr(&storage_struct.atomic_scalar, 1u);\n    atomicOr(&storage_struct.atomic_arr[1], 1i);\n    atomicOr(&workgroup_atomic_scalar, 1u);\n    atomicOr(&workgroup_atomic_arr[1], 1i);\n    atomicOr(&workgroup_struct.atomic_scalar, 1u);\n    atomicOr(&workgroup_struct.atomic_arr[1], 1i);\n\n    workgroupBarrier();\n\n    atomicXor(&storage_atomic_scalar, 1u);\n    atomicXor(&storage_atomic_arr[1], 1i);\n    atomicXor(&storage_struct.atomic_scalar, 1u);\n    atomicXor(&storage_struct.atomic_arr[1], 1i);\n    atomicXor(&workgroup_atomic_scalar, 1u);\n    atomicXor(&workgroup_atomic_arr[1], 1i);\n    atomicXor(&workgroup_struct.atomic_scalar, 1u);\n    atomicXor(&workgroup_struct.atomic_arr[1], 1i);\n\n    atomicExchange(&storage_atomic_scalar, 1u);\n    atomicExchange(&storage_atomic_arr[1], 1i);\n    atomicExchange(&storage_struct.atomic_scalar, 1u);\n    atomicExchange(&storage_struct.atomic_arr[1], 1i);\n    atomicExchange(&workgroup_atomic_scalar, 1u);\n    atomicExchange(&workgroup_atomic_arr[1], 1i);\n    atomicExchange(&workgroup_struct.atomic_scalar, 1u);\n    atomicExchange(&workgroup_struct.atomic_arr[1], 1i);\n\n    let cas_res_0 = atomicCompareExchangeWeak(&storage_atomic_scalar, 1u, 2u);\n    let cas_res_1 = atomicCompareExchangeWeak(&storage_atomic_arr[1], 1i, 2i);\n    let cas_res_2 = atomicCompareExchangeWeak(&storage_struct.atomic_scalar, 1u, 2u);\n    let cas_res_3 = atomicCompareExchangeWeak(&storage_struct.atomic_arr[1], 1i, 2i);\n    let cas_res_4 = atomicCompareExchangeWeak(&workgroup_atomic_scalar, 1u, 2u);\n    let cas_res_5 = atomicCompareExchangeWeak(&workgroup_atomic_arr[1], 1i, 2i);\n    let cas_res_6 = atomicCompareExchangeWeak(&workgroup_struct.atomic_scalar, 1u, 2u);\n    let cas_res_7 = atomicCompareExchangeWeak(&workgroup_struct.atomic_arr[1], 1i, 2i);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicTexture-int64.toml",
    "content": "capabilities = \"SHADER_INT64 | TEXTURE_INT64_ATOMIC\"\ntargets = \"SPIRV | METAL | HLSL | WGSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [3, 1]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_6\"\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicTexture-int64.wgsl",
    "content": "@group(0) @binding(0)\nvar image: texture_storage_2d<r64uint, atomic>;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    textureAtomicMax(image, vec2<i32>(0, 0), 1lu);\n\n    workgroupBarrier();\n\n    textureAtomicMin(image, vec2<i32>(0, 0), 1lu);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicTexture.toml",
    "content": "capabilities = \"TEXTURE_ATOMIC\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [3, 1]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 420\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/atomicTexture.wgsl",
    "content": "@group(0) @binding(0)\nvar image_u: texture_storage_2d<r32uint, atomic>;\n@group(0) @binding(1)\nvar image_s: texture_storage_2d<r32sint, atomic>;\n\n@compute\n@workgroup_size(2)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    textureAtomicMax(image_u, vec2<i32>(0, 0), 1u);\n    textureAtomicMin(image_u, vec2<i32>(0, 0), 1u);\n    textureAtomicAdd(image_u, vec2<i32>(0, 0), 1u);\n    textureAtomicAnd(image_u, vec2<i32>(0, 0), 1u);\n    textureAtomicOr(image_u, vec2<i32>(0, 0), 1u);\n    textureAtomicXor(image_u, vec2<i32>(0, 0), 1u);\n\n    textureAtomicMax(image_s, vec2<i32>(0, 0), 1i);\n    textureAtomicMin(image_s, vec2<i32>(0, 0), 1i);\n    textureAtomicAdd(image_s, vec2<i32>(0, 0), 1i);\n    textureAtomicAnd(image_s, vec2<i32>(0, 0), 1i);\n    textureAtomicOr(image_s, vec2<i32>(0, 0), 1i);\n    textureAtomicXor(image_s, vec2<i32>(0, 0), 1i);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/barycentrics.toml",
    "content": "capabilities = \"SHADER_BARYCENTRICS\"\n\n[msl]\nlang_version = [2, 3]\n\n[hlsl]\nshader_model = \"V6_1\"\n\n[glsl]\nversion.Desktop = 450\n"
  },
  {
    "path": "naga/tests/in/wgsl/barycentrics.wgsl",
    "content": "@fragment\nfn fs_main(@builtin(barycentric) bary: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4(bary, 1.0);\n}\n\n@fragment\nfn fs_main_no_perspective(@builtin(barycentric_no_perspective) bary: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4(bary, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/binding-arrays.toml",
    "content": "capabilities = \"\"\"\n  TEXTURE_AND_SAMPLER_BINDING_ARRAY\n  | STORAGE_TEXTURE_BINDING_ARRAY\n  | TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n  | STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING\n\"\"\"\ntargets = \"WGSL | HLSL | METAL | SPIRV\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\nimage_load = \"ReadZeroSkipWrite\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [3, 0]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.main]\nresources = [\n    { bind_target = { binding_array_size = 10, buffer = 0, mutable = false }, resource_binding = { group = 0, binding = 0 } },\n]\n\n[hlsl]\nfake_missing_bindings = true\nrestrict_indexing = true\nzero_initialize_workgroup_memory = true\n\n[[hlsl.binding_map]]\nbind_target = { binding_array_size = 10, register = 0, space = 0 }\nresource_binding = { group = 0, binding = 0 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 1 }\nresource_binding = { group = 0, binding = 1 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 2 }\nresource_binding = { group = 0, binding = 2 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 3 }\nresource_binding = { group = 0, binding = 3 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 4 }\nresource_binding = { group = 0, binding = 4 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 5 }\nresource_binding = { group = 0, binding = 5 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 6 }\nresource_binding = { group = 0, binding = 6 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 7 }\nresource_binding = { group = 0, binding = 7 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 8 }\nresource_binding = { group = 0, binding = 8 }\n\n[spv]\nversion = [1, 1]\n\n[[spv.binding_map]]\nbind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 }\nresource_binding = { group = 0, binding = 0 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/binding-arrays.wgsl",
    "content": "struct UniformIndex {\n    index: u32\n};\n\n@group(0) @binding(0)\nvar texture_array_unbounded: binding_array<texture_2d<f32>>;\n@group(0) @binding(1)\nvar texture_array_bounded: binding_array<texture_2d<f32>, 5>;\n@group(0) @binding(2)\nvar texture_array_2darray: binding_array<texture_2d_array<f32>, 5>;\n@group(0) @binding(3)\nvar texture_array_multisampled: binding_array<texture_multisampled_2d<f32>, 5>;\n@group(0) @binding(4)\nvar texture_array_depth: binding_array<texture_depth_2d, 5>;\n@group(0) @binding(5)\nvar texture_array_storage: binding_array<texture_storage_2d<rgba32float, write>, 5>;\n@group(0) @binding(6)\nvar samp: binding_array<sampler, 5>;\n@group(0) @binding(7)\nvar samp_comp: binding_array<sampler_comparison, 5>;\n@group(0) @binding(8)\nvar<uniform> uni: UniformIndex;\n\nstruct FragmentIn {\n    @location(0) index: u32,\n};\n\n@fragment\nfn main(fragment_in: FragmentIn) -> @location(0) vec4<f32> {\n    let uniform_index = uni.index;\n    let non_uniform_index = fragment_in.index;\n\n    var u1 = 0u;\n    var u2 = vec2<u32>(0u);\n    var v1 = 0.0;\n    var v4 = vec4<f32>(0.0);\n    \n    // This example is arranged in the order of the texture definitions in the wgsl spec\n    // \n    // The first function uses texture_array_unbounded, the rest use texture_array_bounded to make sure\n    // they both show up in the output. Functions that need depth use texture_array_2darray.\n    //\n    // We only test 2D f32 textures here as the machinery for binding indexing doesn't care about\n    // texture format or texture dimension.\n\n    let uv = vec2<f32>(0.0);\n    let pix = vec2<i32>(0);\n\n    u2 += textureDimensions(texture_array_unbounded[0]);\n    u2 += textureDimensions(texture_array_unbounded[uniform_index]);\n    u2 += textureDimensions(texture_array_unbounded[non_uniform_index]);\n\n    v4 += textureGather(0, texture_array_bounded[0], samp[0], uv);\n    v4 += textureGather(0, texture_array_bounded[uniform_index], samp[uniform_index], uv);\n    v4 += textureGather(0, texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv); \n\n    v4 += textureGatherCompare(texture_array_depth[0], samp_comp[0], uv, 0.0);\n    v4 += textureGatherCompare(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0.0);\n    v4 += textureGatherCompare(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0.0); \n\n    v4 += textureLoad(texture_array_unbounded[0], pix, 0);\n    v4 += textureLoad(texture_array_unbounded[uniform_index], pix, 0);\n    v4 += textureLoad(texture_array_unbounded[non_uniform_index], pix, 0);\n\n    u1 += textureNumLayers(texture_array_2darray[0]);\n    u1 += textureNumLayers(texture_array_2darray[uniform_index]);\n    u1 += textureNumLayers(texture_array_2darray[non_uniform_index]);\n\n    u1 += textureNumLevels(texture_array_bounded[0]);\n    u1 += textureNumLevels(texture_array_bounded[uniform_index]);\n    u1 += textureNumLevels(texture_array_bounded[non_uniform_index]);\n\n    u1 += textureNumSamples(texture_array_multisampled[0]);\n    u1 += textureNumSamples(texture_array_multisampled[uniform_index]);\n    u1 += textureNumSamples(texture_array_multisampled[non_uniform_index]);\n\n    v4 += textureSample(texture_array_bounded[0], samp[0], uv);\n    v4 += textureSample(texture_array_bounded[uniform_index], samp[uniform_index], uv);\n    v4 += textureSample(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv);\n\n    v4 += textureSampleBias(texture_array_bounded[0], samp[0], uv, 0.0);\n    v4 += textureSampleBias(texture_array_bounded[uniform_index], samp[uniform_index], uv, 0.0);\n    v4 += textureSampleBias(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, 0.0);\n\n    v1 += textureSampleCompare(texture_array_depth[0], samp_comp[0], uv, 0.0);\n    v1 += textureSampleCompare(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0.0);\n    v1 += textureSampleCompare(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0.0);\n\n    v1 += textureSampleCompareLevel(texture_array_depth[0], samp_comp[0], uv, 0.0);\n    v1 += textureSampleCompareLevel(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0.0);\n    v1 += textureSampleCompareLevel(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0.0);\n\n    v4 += textureSampleGrad(texture_array_bounded[0], samp[0], uv, uv, uv);\n    v4 += textureSampleGrad(texture_array_bounded[uniform_index], samp[uniform_index], uv, uv, uv);\n    v4 += textureSampleGrad(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, uv, uv);\n\n    v4 += textureSampleLevel(texture_array_bounded[0], samp[0], uv, 0.0);\n    v4 += textureSampleLevel(texture_array_bounded[uniform_index], samp[uniform_index], uv, 0.0);\n    v4 += textureSampleLevel(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, 0.0);\n\n    textureStore(texture_array_storage[0], pix, v4);\n    textureStore(texture_array_storage[uniform_index], pix, v4);\n    textureStore(texture_array_storage[non_uniform_index], pix, v4);\n\n    let v2 = vec2<f32>(u2 + vec2<u32>(u1));\n\n    return v4 + vec4<f32>(v2.x, v2.y, v2.x, v2.y) + v1;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/binding-buffer-arrays.toml",
    "content": "targets = \"WGSL | SPIRV\"\ncapabilities = \"\"\"\n  STORAGE_BUFFER_BINDING_ARRAY\n  | STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING\n\"\"\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\nimage = \"ReadZeroSkipWrite\"\n\n[spv]\nversion = [1, 1]\n\n[[spv.binding_map]]\nbind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 }\nresource_binding = { group = 0, binding = 0 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/binding-buffer-arrays.wgsl",
    "content": "struct UniformIndex {\n    index: u32\n}\n\nstruct Foo { x: u32, far: array<i32> }\n@group(0) @binding(0)\nvar<storage, read> storage_array: binding_array<Foo, 1>;\n@group(0) @binding(10)\nvar<uniform> uni: UniformIndex;\n\nstruct FragmentIn {\n    @location(0) index: u32,\n}\n\n@fragment\nfn main(fragment_in: FragmentIn) -> @location(0) u32 {\n    let uniform_index = uni.index;\n    let non_uniform_index = fragment_in.index;\n\n    var u1 = 0u;\n\n    u1 += storage_array[0].x;\n    u1 += storage_array[uniform_index].x;\n    u1 += storage_array[non_uniform_index].x;\n\n    u1 += arrayLength(&storage_array[0].far);\n    u1 += arrayLength(&storage_array[uniform_index].far);\n    u1 += arrayLength(&storage_array[non_uniform_index].far);\n\n    return u1;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bitcast.wgsl",
    "content": "@compute @workgroup_size(1)\nfn main() {\n    var i2 = vec2<i32>(0);\n    var i3 = vec3<i32>(0);\n    var i4 = vec4<i32>(0);\n\n    var u2 = vec2<u32>(0u);\n    var u3 = vec3<u32>(0u);\n    var u4 = vec4<u32>(0u);\n\n    var f2 = vec2<f32>(0.0);\n    var f3 = vec3<f32>(0.0);\n    var f4 = vec4<f32>(0.0);\n\n    u2 = bitcast<vec2<u32>>(i2);\n    u3 = bitcast<vec3<u32>>(i3);\n    u4 = bitcast<vec4<u32>>(i4);\n\n    i2 = bitcast<vec2<i32>>(u2);\n    i3 = bitcast<vec3<i32>>(u3);\n    i4 = bitcast<vec4<i32>>(u4);\n\n    f2 = bitcast<vec2<f32>>(i2);\n    f3 = bitcast<vec3<f32>>(i3);\n    f4 = bitcast<vec4<f32>>(i4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits-optimized-msl.toml",
    "content": "capabilities = \"SHADER_FLOAT16_IN_FLOAT32\"\ntargets = \"METAL\"\n\n[msl]\nlang_version = [2, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits-optimized-msl.wgsl",
    "content": "// Keep in sync with `bits_downlevel` and `bits_downlevel_webgl`\n\n@compute @workgroup_size(1)\nfn main() {\n    var i = 0;\n    var i2 = vec2<i32>(0);\n    var i3 = vec3<i32>(0);\n    var i4 = vec4<i32>(0);\n    var u = 0u;\n    var u2 = vec2<u32>(0u);\n    var u3 = vec3<u32>(0u);\n    var u4 = vec4<u32>(0u);\n    var f2 = vec2<f32>(0.0);\n    var f4 = vec4<f32>(0.0);\n    u = pack4x8snorm(f4);\n    u = pack4x8unorm(f4);\n    u = pack2x16snorm(f2);\n    u = pack2x16unorm(f2);\n    u = pack2x16float(f2);\n    u = pack4xI8(i4);\n    u = pack4xU8(u4);\n    u = pack4xI8Clamp(i4);\n    u = pack4xU8Clamp(u4);\n    f4 = unpack4x8snorm(u);\n    f4 = unpack4x8unorm(u);\n    f2 = unpack2x16snorm(u);\n    f2 = unpack2x16unorm(u);\n    f2 = unpack2x16float(u);\n    i4 = unpack4xI8(u);\n    u4 = unpack4xU8(u);\n    i = insertBits(i, i, 5u, 10u);\n    i2 = insertBits(i2, i2, 5u, 10u);\n    i3 = insertBits(i3, i3, 5u, 10u);\n    i4 = insertBits(i4, i4, 5u, 10u);\n    u = insertBits(u, u, 5u, 10u);\n    u2 = insertBits(u2, u2, 5u, 10u);\n    u3 = insertBits(u3, u3, 5u, 10u);\n    u4 = insertBits(u4, u4, 5u, 10u);\n    i = extractBits(i, 5u, 10u);\n    i2 = extractBits(i2, 5u, 10u);\n    i3 = extractBits(i3, 5u, 10u);\n    i4 = extractBits(i4, 5u, 10u);\n    u = extractBits(u, 5u, 10u);\n    u2 = extractBits(u2, 5u, 10u);\n    u3 = extractBits(u3, 5u, 10u);\n    u4 = extractBits(u4, 5u, 10u);\n    i = firstTrailingBit(i);\n    u2 = firstTrailingBit(u2);\n    i3 = firstLeadingBit(i3);\n    u3 = firstLeadingBit(u3);\n    i = firstLeadingBit(i);\n    u = firstLeadingBit(u);\n    i = countOneBits(i);\n    i2 = countOneBits(i2);\n    i3 = countOneBits(i3);\n    i4 = countOneBits(i4);\n    u = countOneBits(u);\n    u2 = countOneBits(u2);\n    u3 = countOneBits(u3);\n    u4 = countOneBits(u4);\n    i = reverseBits(i);\n    i2 = reverseBits(i2);\n    i3 = reverseBits(i3);\n    i4 = reverseBits(i4);\n    u = reverseBits(u);\n    u2 = reverseBits(u2);\n    u3 = reverseBits(u3);\n    u4 = reverseBits(u4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits.toml",
    "content": "# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `F16_IN_F32` capability\ncapabilities = \"SHADER_FLOAT16_IN_FLOAT32\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.main]\nresources = []\nsizes_buffer = 0\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits.wgsl",
    "content": "// Keep in sync with `bits_downlevel` and `bits_downlevel_webgl`\n\n@compute @workgroup_size(1)\nfn main() {\n    var i = 0;\n    var i2 = vec2<i32>(0);\n    var i3 = vec3<i32>(0);\n    var i4 = vec4<i32>(0);\n    var u = 0u;\n    var u2 = vec2<u32>(0u);\n    var u3 = vec3<u32>(0u);\n    var u4 = vec4<u32>(0u);\n    var f2 = vec2<f32>(0.0);\n    var f4 = vec4<f32>(0.0);\n    u = pack4x8snorm(f4);\n    u = pack4x8unorm(f4);\n    u = pack2x16snorm(f2);\n    u = pack2x16unorm(f2);\n    u = pack2x16float(f2);\n    u = pack4xI8(i4);\n    u = pack4xU8(u4);\n    u = pack4xI8Clamp(i4);\n    u = pack4xU8Clamp(u4);\n    f4 = unpack4x8snorm(u);\n    f4 = unpack4x8unorm(u);\n    f2 = unpack2x16snorm(u);\n    f2 = unpack2x16unorm(u);\n    f2 = unpack2x16float(u);\n    i4 = unpack4xI8(u);\n    u4 = unpack4xU8(u);\n    i = insertBits(i, i, 5u, 10u);\n    i2 = insertBits(i2, i2, 5u, 10u);\n    i3 = insertBits(i3, i3, 5u, 10u);\n    i4 = insertBits(i4, i4, 5u, 10u);\n    u = insertBits(u, u, 5u, 10u);\n    u2 = insertBits(u2, u2, 5u, 10u);\n    u3 = insertBits(u3, u3, 5u, 10u);\n    u4 = insertBits(u4, u4, 5u, 10u);\n    i = extractBits(i, 5u, 10u);\n    i2 = extractBits(i2, 5u, 10u);\n    i3 = extractBits(i3, 5u, 10u);\n    i4 = extractBits(i4, 5u, 10u);\n    u = extractBits(u, 5u, 10u);\n    u2 = extractBits(u2, 5u, 10u);\n    u3 = extractBits(u3, 5u, 10u);\n    u4 = extractBits(u4, 5u, 10u);\n    i = firstTrailingBit(i);\n    u2 = firstTrailingBit(u2);\n    i3 = firstLeadingBit(i3);\n    u3 = firstLeadingBit(u3);\n    i = firstLeadingBit(i);\n    u = firstLeadingBit(u);\n    i = countOneBits(i);\n    i2 = countOneBits(i2);\n    i3 = countOneBits(i3);\n    i4 = countOneBits(i4);\n    u = countOneBits(u);\n    u2 = countOneBits(u2);\n    u3 = countOneBits(u3);\n    u4 = countOneBits(u4);\n    i = reverseBits(i);\n    i2 = reverseBits(i2);\n    i3 = reverseBits(i3);\n    i4 = reverseBits(i4);\n    u = reverseBits(u);\n    u2 = reverseBits(u2);\n    u3 = reverseBits(u3);\n    u4 = reverseBits(u4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits_downlevel.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Desktop = 330\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits_downlevel.wgsl",
    "content": "// Keep in sync with bits.wgsl\n\n@fragment\nfn main() {\n    var i = 0;\n    var i2 = vec2<i32>(0);\n    var i3 = vec3<i32>(0);\n    var i4 = vec4<i32>(0);\n    var u = 0u;\n    var u2 = vec2<u32>(0u);\n    var u3 = vec3<u32>(0u);\n    var u4 = vec4<u32>(0u);\n    var f2 = vec2<f32>(0.0);\n    var f4 = vec4<f32>(0.0);\n    // No polyfill for these yet\n    // u = pack4x8snorm(f4);\n    // u = pack4x8unorm(f4);\n    // u = pack2x16snorm(f2);\n    // u = pack2x16unorm(f2);\n    // u = pack2x16float(f2);\n    u = pack4xI8(i4);\n    u = pack4xU8(u4);\n    f4 = unpack4x8snorm(u);\n    f4 = unpack4x8unorm(u);\n    f2 = unpack2x16snorm(u);\n    f2 = unpack2x16unorm(u);\n    // No polyfill for this yet\n    // f2 = unpack2x16float(u);\n    // Polyfill for this is broken in downlevel\n    // i4 = unpack4xI8(u);\n    // u4 = unpack4xU8(u);\n    // Implementation is broken on downlevel\n    // i = insertBits(i, i, 5u, 10u);\n    // i2 = insertBits(i2, i2, 5u, 10u);\n    // i3 = insertBits(i3, i3, 5u, 10u);\n    // i4 = insertBits(i4, i4, 5u, 10u);\n    // u = insertBits(u, u, 5u, 10u);\n    // u2 = insertBits(u2, u2, 5u, 10u);\n    // u3 = insertBits(u3, u3, 5u, 10u);\n    // u4 = insertBits(u4, u4, 5u, 10u);\n    // Implementation is broken on downlevel\n    // i = extractBits(i, 5u, 10u);\n    // i2 = extractBits(i2, 5u, 10u);\n    // i3 = extractBits(i3, 5u, 10u);\n    // i4 = extractBits(i4, 5u, 10u);\n    // u = extractBits(u, 5u, 10u);\n    // u2 = extractBits(u2, 5u, 10u);\n    // u3 = extractBits(u3, 5u, 10u);\n    // u4 = extractBits(u4, 5u, 10u);\n    // Implementation is broken on downlevel\n    // i = firstTrailingBit(i);\n    // u2 = firstTrailingBit(u2);\n    // i3 = firstLeadingBit(i3);\n    // u3 = firstLeadingBit(u3);\n    // Implementation is broken on downlevel\n    // i = firstLeadingBit(i);\n    // u = firstLeadingBit(u);\n    // Implementation is broken on downlevel\n    // i = countOneBits(i);\n    // i2 = countOneBits(i2);\n    // i3 = countOneBits(i3);\n    // i4 = countOneBits(i4);\n    // u = countOneBits(u);\n    // u2 = countOneBits(u2);\n    // u3 = countOneBits(u3);\n    // u4 = countOneBits(u4);\n    // Implementation is broken on downlevel\n    // i = reverseBits(i);\n    // i2 = reverseBits(i2);\n    // i3 = reverseBits(i3);\n    // i4 = reverseBits(i4);\n    // u = reverseBits(u);\n    // u2 = reverseBits(u2);\n    // u3 = reverseBits(u3);\n    // u4 = reverseBits(u4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits_downlevel_webgl.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = true, version = 300 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/bits_downlevel_webgl.wgsl",
    "content": "// Keep in sync with bits.wgsl\n\n@fragment\nfn main() {\n    var i = 0;\n    var i2 = vec2<i32>(0);\n    var i3 = vec3<i32>(0);\n    var i4 = vec4<i32>(0);\n    var u = 0u;\n    var u2 = vec2<u32>(0u);\n    var u3 = vec3<u32>(0u);\n    var u4 = vec4<u32>(0u);\n    var f2 = vec2<f32>(0.0);\n    var f4 = vec4<f32>(0.0);\n    // No polyfill for these yet\n    // u = pack4x8snorm(f4);\n    // u = pack4x8unorm(f4);\n    // u = pack2x16snorm(f2);\n    // u = pack2x16unorm(f2);\n    // u = pack2x16float(f2);\n    u = pack4xI8(i4);\n    u = pack4xU8(u4);\n    f4 = unpack4x8snorm(u);\n    f4 = unpack4x8unorm(u);\n    f2 = unpack2x16snorm(u);\n    f2 = unpack2x16unorm(u);\n    // No polyfill for this yet\n    // f2 = unpack2x16float(u);\n    // Polyfill for this is broken in downlevel\n    // i4 = unpack4xI8(u);\n    // u4 = unpack4xU8(u);\n    // Implementation is broken on downlevel\n    // i = insertBits(i, i, 5u, 10u);\n    // i2 = insertBits(i2, i2, 5u, 10u);\n    // i3 = insertBits(i3, i3, 5u, 10u);\n    // i4 = insertBits(i4, i4, 5u, 10u);\n    // u = insertBits(u, u, 5u, 10u);\n    // u2 = insertBits(u2, u2, 5u, 10u);\n    // u3 = insertBits(u3, u3, 5u, 10u);\n    // u4 = insertBits(u4, u4, 5u, 10u);\n    // Implementation is broken on downlevel\n    // i = extractBits(i, 5u, 10u);\n    // i2 = extractBits(i2, 5u, 10u);\n    // i3 = extractBits(i3, 5u, 10u);\n    // i4 = extractBits(i4, 5u, 10u);\n    // u = extractBits(u, 5u, 10u);\n    // u2 = extractBits(u2, 5u, 10u);\n    // u3 = extractBits(u3, 5u, 10u);\n    // u4 = extractBits(u4, 5u, 10u);\n    // Implementation is broken on downlevel\n    // i = firstTrailingBit(i);\n    // u2 = firstTrailingBit(u2);\n    // Implementation is broken on downlevel\n    // i3 = firstLeadingBit(i3);\n    // u3 = firstLeadingBit(u3);\n    // i = firstLeadingBit(i);\n    // u = firstLeadingBit(u);\n    // Implementation is broken on downlevel\n    // i = countOneBits(i);\n    // i2 = countOneBits(i2);\n    // i3 = countOneBits(i3);\n    // i4 = countOneBits(i4);\n    // u = countOneBits(u);\n    // u2 = countOneBits(u2);\n    // u3 = countOneBits(u3);\n    // u4 = countOneBits(u4);\n    // i = reverseBits(i);\n    // i2 = reverseBits(i2);\n    // i3 = reverseBits(i3);\n    // i4 = reverseBits(i4);\n    // u = reverseBits(u);\n    // u2 = reverseBits(u2);\n    // u3 = reverseBits(u3);\n    // u4 = reverseBits(u4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/boids.toml",
    "content": "[msl]\nfake_missing_bindings = false\nlang_version = [1, 0]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.main]\nresources = [\n    { bind_target = { buffer = 0, mutable = false }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { buffer = 1, mutable = true }, resource_binding = { group = 0, binding = 1 } },\n    { bind_target = { buffer = 2, mutable = true }, resource_binding = { group = 0, binding = 2 } },\n]\nsizes_buffer = 3\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/boids.wgsl",
    "content": "const NUM_PARTICLES: u32 = 1500u;\n\nstruct Particle {\n  pos : vec2<f32>,\n  vel : vec2<f32>,\n}\n\nstruct SimParams {\n  deltaT : f32,\n  rule1Distance : f32,\n  rule2Distance : f32,\n  rule3Distance : f32,\n  rule1Scale : f32,\n  rule2Scale : f32,\n  rule3Scale : f32,\n}\n\nstruct Particles {\n  particles : array<Particle>\n}\n\n@group(0) @binding(0) var<uniform> params : SimParams;\n@group(0) @binding(1) var<storage> particlesSrc : Particles;\n@group(0) @binding(2) var<storage,read_write> particlesDst : Particles;\n\n// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) global_invocation_id : vec3<u32>) {\n  let index : u32 = global_invocation_id.x;\n  if index >= NUM_PARTICLES {\n    return;\n  }\n\n  var vPos = particlesSrc.particles[index].pos;\n  var vVel = particlesSrc.particles[index].vel;\n\n  var cMass = vec2<f32>(0.0, 0.0);\n  var cVel = vec2<f32>(0.0, 0.0);\n  var colVel = vec2<f32>(0.0, 0.0);\n  var cMassCount : i32 = 0;\n  var cVelCount : i32 = 0;\n\n  var pos : vec2<f32>;\n  var vel : vec2<f32>;\n  var i : u32 = 0u;\n  loop {\n    if i >= NUM_PARTICLES {\n      break;\n    }\n    if i == index {\n      continue;\n    }\n\n    pos = particlesSrc.particles[i].pos;\n    vel = particlesSrc.particles[i].vel;\n\n    if distance(pos, vPos) < params.rule1Distance {\n      cMass = cMass + pos;\n      cMassCount = cMassCount + 1;\n    }\n    if distance(pos, vPos) < params.rule2Distance {\n      colVel = colVel - (pos - vPos);\n    }\n    if distance(pos, vPos) < params.rule3Distance {\n      cVel = cVel + vel;\n      cVelCount = cVelCount + 1;\n    }\n\n    continuing {\n      i = i + 1u;\n    }\n  }\n  if cMassCount > 0 {\n    cMass = cMass / f32(cMassCount) - vPos;\n  }\n  if cVelCount > 0 {\n    cVel = cVel / f32(cVelCount);\n  }\n\n  vVel = vVel + (cMass * params.rule1Scale) +\n      (colVel * params.rule2Scale) +\n      (cVel * params.rule3Scale);\n\n  // clamp velocity for a more pleasing simulation\n  vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);\n\n  // kinematic update\n  vPos = vPos + (vVel * params.deltaT);\n\n  // Wrap around boundary\n  if vPos.x < -1.0 {\n    vPos.x = 1.0;\n  }\n  if vPos.x > 1.0 {\n    vPos.x = -1.0;\n  }\n  if vPos.y < -1.0 {\n    vPos.y = 1.0;\n  }\n  if vPos.y > 1.0 {\n    vPos.y = -1.0;\n  }\n\n  // Write back\n  particlesDst.particles[index].pos = vPos;\n  particlesDst.particles[index].vel = vVel;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-dynamic-buffer.toml",
    "content": "targets = \"HLSL\"\n\n[hlsl]\nrestrict_indexing = true\ndynamic_storage_buffer_offsets_targets = [\n    { index = 0, bind_target = { register = 1, size = 2, space = 0 } },\n    { index = 1, bind_target = { register = 2, size = 1, space = 0 } },\n]\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, space = 0 }\nresource_binding = { group = 0, binding = 0 }\n\n[[hlsl.binding_map]]\nbind_target = { dynamic_storage_buffer_offsets_index = 0, register = 4, space = 0 }\nresource_binding = { group = 1, binding = 0 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 1, space = 0 }\nresource_binding = { group = 0, binding = 1 }\n\n[[hlsl.binding_map]]\nbind_target = { register = 0, restrict_indexing = true, space = 0 }\nresource_binding = { group = 0, binding = 2 }\n\n[[hlsl.binding_map]]\nbind_target = { dynamic_storage_buffer_offsets_index = 0, register = 2, space = 0 }\nresource_binding = { group = 0, binding = 3 }\n\n[[hlsl.binding_map]]\nbind_target = { dynamic_storage_buffer_offsets_index = 1, register = 3, space = 0 }\nresource_binding = { group = 0, binding = 4 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-dynamic-buffer.wgsl",
    "content": "@group(0) @binding(0)\nvar<storage, read_write> in: u32;\n@group(0) @binding(1)\nvar<storage, read_write> out: array<u32>;\n\nstruct T {\n    @size(16)\n    t: u32\n}\n\n@group(0) @binding(2)\nvar<uniform> in_data_uniform: array<T, 1>;\n\n@group(0) @binding(3)\nvar<storage, read_write> in_data_storage_g0_b3: array<T, 1>;\n\n@group(0) @binding(4)\nvar<storage, read_write> in_data_storage_g0_b4: array<T, 1>;\n\n@group(1) @binding(0)\nvar<storage, read_write> in_data_storage_g1_b0: array<T, 1>;\n\n@compute @workgroup_size(1)\nfn main() {\n    let i = in;\n    out[0] = in_data_uniform[i].t;\n    out[1] = in_data_storage_g0_b3[i].t;\n    out[2] = in_data_storage_g0_b4[i].t;\n    out[3] = in_data_storage_g1_b0[i].t;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-restrict-depth.toml",
    "content": "targets = \"SPIRV | METAL\"\n\n[bounds_check_policies]\nimage_load = \"Restrict\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-restrict-depth.wgsl",
    "content": "// Cases from bounds-check-image-restrict that GLSL does not yet support.\n\n@group(0) @binding(0)\nvar image_depth_2d: texture_depth_2d;\n\nfn test_textureLoad_depth_2d(coords: vec2<i32>, level: i32) -> f32 {\n   return textureLoad(image_depth_2d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_depth_2d_array: texture_depth_2d_array;\n\nfn test_textureLoad_depth_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_depth_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\n@group(0) @binding(2)\nvar image_depth_multisampled_2d: texture_depth_multisampled_2d;\n\nfn test_textureLoad_depth_multisampled_2d(coords: vec2<i32>, _sample: i32) -> f32 {\n   return textureLoad(image_depth_multisampled_2d, coords, _sample);\n}\n\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_depth_2d(vec2<i32>(), 0);\n    test_textureLoad_depth_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_depth_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_depth_multisampled_2d(vec2<i32>(), 0);\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-restrict.toml",
    "content": "targets = \"SPIRV | METAL | GLSL\"\n\n[bounds_check_policies]\nimage_load = \"Restrict\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-restrict.wgsl",
    "content": "@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n\nfn test_textureLoad_1d(coords: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_1d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n\nfn test_textureLoad_2d(coords: vec2<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d, coords, level);\n}\n\n@group(0) @binding(2)\nvar image_2d_array: texture_2d_array<f32>;\n\nfn test_textureLoad_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\n@group(0) @binding(3)\nvar image_3d: texture_3d<f32>;\n\nfn test_textureLoad_3d(coords: vec3<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_3d, coords, level);\n}\n\n@group(0) @binding(4)\nvar image_multisampled_2d: texture_multisampled_2d<f32>;\n\nfn test_textureLoad_multisampled_2d(coords: vec2<i32>, _sample: i32) -> vec4<f32> {\n   return textureLoad(image_multisampled_2d, coords, _sample);\n}\n\n@group(0) @binding(5)\nvar image_storage_1d: texture_storage_1d<rgba8unorm, write>;\n\nfn test_textureStore_1d(coords: i32, value: vec4<f32>) {\n    textureStore(image_storage_1d, coords, value);\n}\n\n@group(0) @binding(6)\nvar image_storage_2d: texture_storage_2d<rgba8unorm, write>;\n\nfn test_textureStore_2d(coords: vec2<i32>, value: vec4<f32>) {\n    textureStore(image_storage_2d, coords, value);\n}\n\n@group(0) @binding(7)\nvar image_storage_2d_array: texture_storage_2d_array<rgba8unorm, write>;\n\nfn test_textureStore_2d_array_u(coords: vec2<i32>, array_index: u32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\nfn test_textureStore_2d_array_s(coords: vec2<i32>, array_index: i32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\n@group(0) @binding(8)\nvar image_storage_3d: texture_storage_3d<rgba8unorm, write>;\n\nfn test_textureStore_3d(coords: vec3<i32>, value: vec4<f32>) {\n    textureStore(image_storage_3d, coords, value);\n}\n\n// GLSL output requires that we identify an entry point, so\n// that it can tell what \"in\" and \"out\" globals to write.\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_1d(0, 0);\n    test_textureLoad_2d(vec2<i32>(), 0);\n    test_textureLoad_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_3d(vec3<i32>(), 0);\n    test_textureLoad_multisampled_2d(vec2<i32>(), 0);\n    test_textureStore_1d(0, vec4<f32>());\n    test_textureStore_2d(vec2<i32>(), vec4<f32>());\n    test_textureStore_2d_array_u(vec2<i32>(), 0u, vec4<f32>());\n    test_textureStore_2d_array_s(vec2<i32>(), 0, vec4<f32>());\n    test_textureStore_3d(vec3<i32>(), vec4<f32>());\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-rzsw-depth.toml",
    "content": "targets = \"SPIRV | METAL\"\n\n[bounds_check_policies]\nimage_load = \"ReadZeroSkipWrite\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-rzsw-depth.wgsl",
    "content": "// Cases from bounds-check-image-restrict that GLSL does not yet support.\n\n@group(0) @binding(0)\nvar image_depth_2d: texture_depth_2d;\n\nfn test_textureLoad_depth_2d(coords: vec2<i32>, level: i32) -> f32 {\n   return textureLoad(image_depth_2d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_depth_2d_array: texture_depth_2d_array;\n\nfn test_textureLoad_depth_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_depth_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\n@group(0) @binding(2)\nvar image_depth_multisampled_2d: texture_depth_multisampled_2d;\n\nfn test_textureLoad_depth_multisampled_2d(coords: vec2<i32>, _sample: i32) -> f32 {\n   return textureLoad(image_depth_multisampled_2d, coords, _sample);\n}\n\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_depth_2d(vec2<i32>(), 0);\n    test_textureLoad_depth_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_depth_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_depth_multisampled_2d(vec2<i32>(), 0);\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-rzsw.toml",
    "content": "targets = \"SPIRV | METAL | GLSL\"\n\n[bounds_check_policies]\nimage_load = \"ReadZeroSkipWrite\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-image-rzsw.wgsl",
    "content": "@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n\nfn test_textureLoad_1d(coords: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_1d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n\nfn test_textureLoad_2d(coords: vec2<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d, coords, level);\n}\n\n@group(0) @binding(2)\nvar image_2d_array: texture_2d_array<f32>;\n\nfn test_textureLoad_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\n@group(0) @binding(3)\nvar image_3d: texture_3d<f32>;\n\nfn test_textureLoad_3d(coords: vec3<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_3d, coords, level);\n}\n\n@group(0) @binding(4)\nvar image_multisampled_2d: texture_multisampled_2d<f32>;\n\nfn test_textureLoad_multisampled_2d(coords: vec2<i32>, _sample: i32) -> vec4<f32> {\n   return textureLoad(image_multisampled_2d, coords, _sample);\n}\n\n@group(0) @binding(5)\nvar image_storage_1d: texture_storage_1d<rgba8unorm, write>;\n\nfn test_textureStore_1d(coords: i32, value: vec4<f32>) {\n    textureStore(image_storage_1d, coords, value);\n}\n\n@group(0) @binding(6)\nvar image_storage_2d: texture_storage_2d<rgba8unorm, write>;\n\nfn test_textureStore_2d(coords: vec2<i32>, value: vec4<f32>) {\n    textureStore(image_storage_2d, coords, value);\n}\n\n@group(0) @binding(7)\nvar image_storage_2d_array: texture_storage_2d_array<rgba8unorm, write>;\n\nfn test_textureStore_2d_array_u(coords: vec2<i32>, array_index: u32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\nfn test_textureStore_2d_array_s(coords: vec2<i32>, array_index: i32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\n@group(0) @binding(8)\nvar image_storage_3d: texture_storage_3d<rgba8unorm, write>;\n\nfn test_textureStore_3d(coords: vec3<i32>, value: vec4<f32>) {\n    textureStore(image_storage_3d, coords, value);\n}\n\n// GLSL output requires that we identify an entry point, so\n// that it can tell what \"in\" and \"out\" globals to write.\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_1d(0, 0);\n    test_textureLoad_2d(vec2<i32>(), 0);\n    test_textureLoad_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_3d(vec3<i32>(), 0);\n    test_textureLoad_multisampled_2d(vec2<i32>(), 0);\n    test_textureStore_1d(0, vec4<f32>());\n    test_textureStore_2d(vec2<i32>(), vec4<f32>());\n    test_textureStore_2d_array_u(vec2<i32>(), 0u, vec4<f32>());\n    test_textureStore_2d_array_s(vec2<i32>(), 0, vec4<f32>());\n    test_textureStore_3d(vec3<i32>(), vec4<f32>());\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-restrict.toml",
    "content": "targets = \"SPIRV | METAL\"\n\n[bounds_check_policies]\nindex = \"Restrict\"\nbuffer = \"Restrict\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-restrict.wgsl",
    "content": "// Tests for `naga::back::BoundsCheckPolicy::Restrict`.\n\nstruct Globals {\n    a: array<f32, 10>,\n    v: vec4<f32>,\n    m: mat3x4<f32>,\n    d: array<f32>,\n}\n\n@group(0) @binding(0) var<storage, read_write> globals: Globals;\n\nfn index_array(i: i32) -> f32 {\n   return globals.a[i];\n}\n\nfn index_dynamic_array(i: i32) -> f32 {\n   return globals.d[i];\n}\n\nfn index_vector(i: i32) -> f32 {\n   return globals.v[i];\n}\n\nfn index_vector_by_value(v: vec4<f32>, i: i32) -> f32 {\n   return v[i];\n}\n\nfn index_matrix(i: i32) -> vec4<f32> {\n   return globals.m[i];\n}\n\nfn index_twice(i: i32, j: i32) -> f32 {\n   return globals.m[i][j];\n}\n\nfn index_expensive(i: i32) -> f32 {\n   return globals.a[i32(sin(f32(i) / 100.0) * 100.0)];\n}\n\nfn index_in_bounds() -> f32 {\n   return globals.a[9] + globals.v[3] + globals.m[2][3];\n}\n\nfn set_array(i: i32, v: f32) {\n   globals.a[i] = v;\n}\n\nfn set_dynamic_array(i: i32, v: f32) {\n   globals.d[i] = v;\n}\n\nfn set_vector(i: i32, v: f32) {\n   globals.v[i] = v;\n}\n\nfn set_matrix(i: i32, v: vec4<f32>) {\n   globals.m[i] = v;\n}\n\nfn set_index_twice(i: i32, j: i32, v: f32) {\n   globals.m[i][j] = v;\n}\n\nfn set_expensive(i: i32, v: f32) {\n   globals.a[i32(sin(f32(i) / 100.0) * 100.0)] = v;\n}\n\nfn set_in_bounds(v: f32) {\n   globals.a[9] = v;\n   globals.v[3] = v;\n   globals.m[2][3] = v;\n}\n\nfn index_dynamic_array_constant_index() -> f32 {\n   return globals.d[1000];\n}\n\nfn set_dynamic_array_constant_index(v: f32) {\n   globals.d[1000] = v;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    index_array(1);\n    index_dynamic_array(1);\n    index_vector(1);\n    index_vector_by_value(vec4f(2, 3, 4, 5), 6);\n    index_matrix(1);\n    index_twice(1, 2);\n    index_expensive(1);\n    index_in_bounds();\n    set_array(1, 2.);\n    set_dynamic_array(1, 2.);\n    set_vector(1, 2.);\n    set_matrix(1, vec4f(2, 3, 4, 5));\n    set_index_twice(1, 2, 1.);\n    set_expensive(1, 1.);\n    set_in_bounds(1.);\n    index_dynamic_array_constant_index();\n    set_dynamic_array_constant_index(1.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-zero-atomic.toml",
    "content": "targets = \"METAL\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-zero-atomic.wgsl",
    "content": "// Tests for `naga::back::BoundsCheckPolicy::ReadZeroSkipWrite` for atomic types.\n\n// These are separate from `bounds-check-zero.wgsl because SPIR-V does not yet\n// support `ReadZeroSkipWrite` for atomics. Once it does, the test files could\n// be combined.\n\nstruct Globals {\n    a: atomic<u32>,\n    b: array<atomic<u32>, 10>,\n    c: array<atomic<u32>>,\n}\n\n@group(0) @binding(0) var<storage, read_write> globals: Globals;\n\nfn fetch_add_atomic() -> u32 {\n   return atomicAdd(&globals.a, 1u);\n}\n\nfn fetch_add_atomic_static_sized_array(i: i32) -> u32 {\n   return atomicAdd(&globals.b[i], 1u);\n}\n\nfn fetch_add_atomic_dynamic_sized_array(i: i32) -> u32 {\n   return atomicAdd(&globals.c[i], 1u);\n}\n\nfn exchange_atomic() -> u32 {\n   return atomicExchange(&globals.a, 1u);\n}\n\nfn exchange_atomic_static_sized_array(i: i32) -> u32 {\n   return atomicExchange(&globals.b[i], 1u);\n}\n\nfn exchange_atomic_dynamic_sized_array(i: i32) -> u32 {\n   return atomicExchange(&globals.c[i], 1u);\n}\n\nfn fetch_add_atomic_dynamic_sized_array_static_index() -> u32 {\n   return atomicAdd(&globals.c[1000], 1u);\n}\n\nfn exchange_atomic_dynamic_sized_array_static_index() -> u32 {\n   return atomicExchange(&globals.c[1000], 1u);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    fetch_add_atomic();\n    fetch_add_atomic_static_sized_array(1);\n    fetch_add_atomic_dynamic_sized_array(1);\n    exchange_atomic();\n    exchange_atomic_static_sized_array(1);\n    exchange_atomic_dynamic_sized_array(1);\n    fetch_add_atomic_dynamic_sized_array_static_index();\n    exchange_atomic_dynamic_sized_array_static_index();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-zero.toml",
    "content": "targets = \"SPIRV | METAL\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/bounds-check-zero.wgsl",
    "content": "// Tests for `naga::back::BoundsCheckPolicy::ReadZeroSkipWrite`.\n\nstruct Globals {\n    a: array<f32, 10>,\n    v: vec4<f32>,\n    m: mat3x4<f32>,\n    d: array<f32>,\n}\n\n@group(0) @binding(0) var<storage, read_write> globals: Globals;\n\nfn index_array(i: i32) -> f32 {\n   return globals.a[i];\n}\n\nfn index_dynamic_array(i: i32) -> f32 {\n   return globals.d[i];\n}\n\nfn index_vector(i: i32) -> f32 {\n   return globals.v[i];\n}\n\nfn index_vector_by_value(v: vec4<f32>, i: i32) -> f32 {\n   return v[i];\n}\n\nfn index_matrix(i: i32) -> vec4<f32> {\n   return globals.m[i];\n}\n\nfn index_twice(i: i32, j: i32) -> f32 {\n   return globals.m[i][j];\n}\n\nfn index_expensive(i: i32) -> f32 {\n   return globals.a[i32(sin(f32(i) / 100.0) * 100.0)];\n}\n\nfn index_in_bounds() -> f32 {\n   return globals.a[9] + globals.v[3] + globals.m[2][3];\n}\n\nfn set_array(i: i32, v: f32) {\n   globals.a[i] = v;\n}\n\nfn set_dynamic_array(i: i32, v: f32) {\n   globals.d[i] = v;\n}\n\nfn set_vector(i: i32, v: f32) {\n   globals.v[i] = v;\n}\n\nfn set_matrix(i: i32, v: vec4<f32>) {\n   globals.m[i] = v;\n}\n\nfn set_index_twice(i: i32, j: i32, v: f32) {\n   globals.m[i][j] = v;\n}\n\nfn set_expensive(i: i32, v: f32) {\n   globals.a[i32(sin(f32(i) / 100.0) * 100.0)] = v;\n}\n\nfn set_in_bounds(v: f32) {\n   globals.a[9] = v;\n   globals.v[3] = v;\n   globals.m[2][3] = v;\n}\n\nfn index_dynamic_array_constant_index() -> f32 {\n   return globals.d[1000];\n}\n\nfn set_dynamic_array_constant_index(v: f32) {\n   globals.d[1000] = v;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    index_array(1);\n    index_dynamic_array(1);\n    index_vector(1);\n    index_vector_by_value(vec4f(2, 3, 4, 5), 6);\n    index_matrix(1);\n    index_twice(1, 2);\n    index_expensive(1);\n    index_in_bounds();\n    set_array(1, 2.);\n    set_dynamic_array(1, 2.);\n    set_vector(1, 2.);\n    set_matrix(1, vec4f(2, 3, 4, 5));\n    set_index_twice(1, 2, 1.);\n    set_expensive(1, 1.);\n    set_in_bounds(1.);\n    index_dynamic_array_constant_index();\n    set_dynamic_array_constant_index(1.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/break-if.wgsl",
    "content": "fn breakIfEmpty() {\n    loop {\n        continuing {\n            break if true;\n        }\n    }\n}\n\nfn breakIfEmptyBody(a: bool) {\n    loop {\n        continuing {\n            var b = a;\n            var c = a != b;\n\n            break if a == c;\n        }\n    }\n}\n\nfn breakIf(a: bool) {\n    loop {\n        var d = a;\n        var e = a != d;\n\n        continuing {\n            break if a == e;\n        }\n    }\n}\n\nfn breakIfSeparateVariable() {\n    var counter = 0u;\n\n    loop {\n        counter += 1u;\n\n        continuing {\n            break if counter == 5u;\n        }\n    }\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    breakIfEmpty();\n    breakIfEmptyBody(false);\n    breakIf(false);\n    breakIfSeparateVariable();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/clip-distances.toml",
    "content": "capabilities = \"CLIP_DISTANCES\"\ntargets = \"SPIRV | GLSL | WGSL\"\n\n[glsl]\nversion.Desktop = 330\n"
  },
  {
    "path": "naga/tests/in/wgsl/clip-distances.wgsl",
    "content": "enable clip_distances;\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @builtin(clip_distances) clip_distances: array<f32, 1>,\n}\n\n@vertex\nfn main() -> VertexOutput {\n    var out: VertexOutput;\n    out.clip_distances[0] = 0.5;\n    return out;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/collatz.toml",
    "content": "targets = \"SPIRV | METAL | IR | ANALYSIS | HLSL | WGSL\"\n\n[spv]\ndebug = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/collatz.wgsl",
    "content": "struct PrimeIndices {\n    data: array<u32>\n} // this is used as both input and output for convenience\n\n@group(0) @binding(0)\nvar<storage,read_write> v_indices: PrimeIndices;\n\n// The Collatz Conjecture states that for any integer n:\n// If n is even, n = n/2\n// If n is odd, n = 3n+1\n// And repeat this process for each new n, you will always eventually reach 1.\n// Though the conjecture has not been proven, no counterexample has ever been found.\n// This function returns how many times this recurrence needs to be applied to reach 1.\nfn collatz_iterations(n_base: u32) -> u32 {\n    var n = n_base;\n    var i: u32 = 0u;\n    while n > 1u {\n        if n % 2u == 0u {\n            n = n / 2u;\n        }\n        else {\n            n = 3u * n + 1u;\n        }\n        i = i + 1u;\n    }\n    return i;\n}\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    v_indices.data[global_id.x] = collatz_iterations(v_indices.data[global_id.x]);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/const-exprs.wgsl",
    "content": "const TWO: u32 = 2u;\nconst THREE: i32 = 3i;\nconst TRUE = true;\nconst FALSE = false;\n\n@compute @workgroup_size(TWO, THREE, TWO - 1u)\nfn main() {\n    swizzle_of_compose();\n    index_of_compose();\n    compose_three_deep();\n    non_constant_initializers();\n    splat_of_constant();\n    compose_of_constant();\n    map_texture_kind(1);\n    compose_of_splat();\n    test_local_const();\n    compose_vector_zero_val_binop();\n    relational();\n    packed_dot_product();\n    test_local_const();\n    abstract_access(1);\n}\n\n// Swizzle the value of nested Compose expressions.\nfn swizzle_of_compose() {\n    var out = vec4(vec2(1, 2), vec2(3, 4)).wzyx; // should assign vec4(4, 3, 2, 1);\n}\n\n// Index the value of nested Compose expressions.\nfn index_of_compose() {\n    var out = vec4(vec2(1, 2), vec2(3, 4))[1]; // should assign 2\n}\n\n// Index the value of Compose expressions nested three deep\nfn compose_three_deep() {\n    var out = vec4(vec3(vec2(6, 7), 8), 9)[0]; // should assign 6\n}\n\n// While WGSL allows local variables to be declared anywhere in the function,\n// Naga treats them all as appearing at the top of the function. To ensure that\n// WGSL initializer expressions are evaluated at the right time, in the general\n// case they need to be turned into Naga `Store` statements executed at the\n// point of the WGSL declaration.\n//\n// When a variable's initializer is a constant expression, however, it can be\n// evaluated at any time. The WGSL front end thus renders locals with\n// initializers that are constants as Naga locals with initializers. This test\n// checks that Naga local variable initializers are only used when safe.\nfn non_constant_initializers() {\n    var w = 10 + 20;\n    var x = w;\n    var y = x;\n    var z = 30 + 40;\n\n    var out = vec4(w, x, y, z);\n}\n\n// Constant evaluation should be able to see through constants to\n// their values.\nconst FOUR: i32 = 4;\n\nconst FOUR_ALIAS: i32 = FOUR;\n\nconst TEST_CONSTANT_ADDITION: i32 = FOUR + FOUR;\nconst TEST_CONSTANT_ALIAS_ADDITION: i32 = FOUR_ALIAS + FOUR_ALIAS;\n\nfn splat_of_constant() {\n    var out = -vec4(FOUR);\n}\n\nfn compose_of_constant() {\n    var out = -vec4(FOUR, FOUR, FOUR, FOUR);\n}\n\nconst PI: f32 = 3.141;\nconst phi_sun: f32 = PI * 2.0;\n\nconst DIV: vec4f = vec4(4.0 / 9.0, 0.0, 0.0, 0.0);\n\nconst TEXTURE_KIND_REGULAR: i32 = 0;\nconst TEXTURE_KIND_WARP: i32 = 1;\nconst TEXTURE_KIND_SKY: i32 = 2;\n\nfn map_texture_kind(texture_kind: i32) -> u32 {\n    switch (texture_kind) {\n        case TEXTURE_KIND_REGULAR: { return 10u; }\n        case TEXTURE_KIND_WARP: { return 20u; }\n        case TEXTURE_KIND_SKY: { return 30u; }\n        default: { return 0u; }\n    }\n}\n\nfn compose_of_splat() {\n    var x = vec4f(vec3f(1.0), 2.0).wzyx;\n}\n\nconst add_vec = vec2(1.0f) + vec2(3.0f, 4.0f);\nconst compare_vec = vec2(3.0f) == vec2(3.0f, 4.0f);\n\n// Ensure binary ops correctly flatten compositions of vector zero values\nfn compose_vector_zero_val_binop() {\n    var a = vec3(vec2i(), 0) + vec3(1);\n    var b = vec3(vec2i(), 0) + vec3(0, 1, 2);\n    var c = vec3(vec2i(), 2) + vec3(1, vec2i());\n}\n\nfn relational() {\n    // Test scalar and vector forms of any() and all(), with a mixture of\n    // consts, literals, zero-values, composes, and splats.\n    var scalar_any_false = any(false);\n    var scalar_any_true  = any(true);\n    var scalar_all_false = all(false);\n    var scalar_all_true  = all(true);\n    var vec_any_false    = any(vec4<bool>());\n    var vec_any_true     = any(vec4(bool(), true, vec2(FALSE)));\n    var vec_all_false    = all(vec4(vec3(vec2<bool>(), TRUE), false));\n    var vec_all_true     = all(vec4(true));\n}\n\nfn packed_dot_product() {\n    // Test dot product of packed vectors on literals, constants, and\n    // combinations thereof.\n    var signed_four = dot4I8Packed(TWO, TWO);\n    var unsigned_four = dot4U8Packed(TWO, TWO);\n    var signed_twelve = dot4I8Packed(TWO + 1u, TWO + 2u);\n    var unsigned_twelve = dot4U8Packed(TWO + 1u, TWO + 2u);\n    var signed_seventy = dot4I8Packed(0x01020304u, 0x05060708u);\n    var unsigned_seventy = dot4U8Packed(0x01020304u, 0x05060708u);\n\n    // This is equivalent to `dot(vec4(-1, 2, -3, 4), vec4(5, 6, -7, -8))`.\n    var minus_four = dot4I8Packed(0xff02fd04u, 0x0506f9f8u);\n}\n\nfn test_local_const() {\n    const local_const = 2;\n\tvar arr: array<f32, local_const>;\n}\n\nconst ABSTRACT_ARRAY = array(1, 2, 3, 4, 5, 6, 7, 8, 9);\nconst ABSTRACT_VECTOR = vec4(1, 2, 3, 4);\n\nfn abstract_access(i: u32) {\n    // Constant indexing of abstract types is allowed, therefore we can assign\n    // to f32 or u32 vars just fine.\n    var a: f32 = ABSTRACT_ARRAY[0];\n    var b: u32 = ABSTRACT_VECTOR.x;\n\n    // For non constant indices the base type is concretized prior to indexing,\n    // therefore we can only assign to i32 in this case.\n    var c: i32 = ABSTRACT_ARRAY[i];\n    var d: i32 = ABSTRACT_VECTOR[i];\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/const_assert.toml",
    "content": "targets = \"WGSL | IR\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/const_assert.wgsl",
    "content": "// Sourced from https://www.w3.org/TR/WGSL/#const-assert-statement and the CTS.\nconst x = 1;\nconst y = 2;\nconst_assert x < y; // valid at module-scope.\nconst_assert(y != 0); // parentheses are optional.\n\n// Ensure abstract-typed consts can be compared to different concrete types\nconst_assert x == 1i;\nconst_assert x > 0u;\nconst_assert x < 2.0f;\n\nconst g_false = false;\nconst_assert (!((g_false) || (any(vec3(false, false, false)))));\n\n@compute @workgroup_size(1)\nfn foo() {\n  const z = x + y - 2;\n  const l_false = false;\n  const_assert z > 0; // valid in functions.\n  const_assert(z > 0);\n\n  const_assert z == 1i;\n  const_assert z > 0u;\n  const_assert z < 2.0f;\n\n  const_assert (!((g_false) || (any(vec3(false, false, false)))));\n  const_assert (!((l_false) || (any(vec3(false, false, false)))));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/constructors.wgsl",
    "content": "struct Foo {\n    a: vec4<f32>,\n    b: i32,\n}\n\nconst const1 = vec3<f32>(0.0);\nconst const2 = vec3(0.0, 1.0, 2.0);\nconst const3 = mat2x2<f32>(0.0, 1.0, 2.0, 3.0);\nconst const4 = array<mat2x2<f32>, 1>(mat2x2<f32>(0.0, 1.0, 2.0, 3.0));\n\n// zero value constructors\nconst cz0 = bool();\nconst cz1 = i32();\nconst cz2 = u32();\nconst cz3 = f32();\nconst cz4 = vec2<u32>();\nconst cz5 = mat2x2<f32>();\nconst cz6 = array<Foo, 3>();\nconst cz7 = Foo();\n\n// constructors that infer their type from their parameters\nconst cp1 = vec2(0u);\nconst cp2 = mat2x2(vec2(0.), vec2(0.));\nconst cp3 = array(0, 1, 2, 3);\n\n@compute @workgroup_size(1)\nfn main() {\n    var foo: Foo;\n    foo = Foo(vec4<f32>(1.0), 1);\n\n    let m0 = mat2x2<f32>(\n        1.0, 0.0,\n        0.0, 1.0,\n    );\n    let m1 = mat4x4<f32>(\n        1.0, 0.0, 0.0, 0.0,\n        0.0, 1.0, 0.0, 0.0,\n        0.0, 0.0, 1.0, 0.0,\n        0.0, 0.0, 0.0, 1.0,\n    );\n\n    // zero value constructors\n    let zvc0 = bool();\n    let zvc1 = i32();\n    let zvc2 = u32();\n    let zvc3 = f32();\n    let zvc4 = vec2<u32>();\n    let zvc5 = mat2x2<f32>();\n    let zvc6 = array<Foo, 3>();\n    let zvc7 = Foo();\n    let zvc8: vec2<u32> = vec2();\n    let zvc9: vec2<f32> = vec2();\n\n    // constructors that infer their type from their parameters\n    let cit0 = vec2(0u);\n    let cit1 = mat2x2(vec2(0.), vec2(0.));\n    let cit2 = array(0, 1, 2, 3);\n\n    // identity constructors\n    let ic0 = bool(bool());\n    let ic1 = i32(i32());\n    let ic2 = u32(u32());\n    let ic3 = f32(f32());\n    let ic4 = vec2<u32>(vec2<u32>());\n    let ic5 = mat2x3<f32>(mat2x3<f32>());\n    let ic6 = vec2(vec2<u32>());\n    let ic7 = mat2x3(mat2x3<f32>());\n\n    // conversion constructors\n    let cc00 = i32(1u);\n    let cc01 = i32(1f);\n    let cc02 = i32(1);\n    let cc03 = i32(1.0);\n    let cc04 = i32(true);\n    let cc05 = u32(1i);\n    let cc06 = u32(1f);\n    let cc07 = u32(1);\n    let cc08 = u32(1.0);\n    let cc09 = u32(true);\n    let cc10 = f32(1i);\n    let cc11 = f32(1u);\n    let cc12 = f32(1);\n    let cc13 = f32(1.0);\n    let cc14 = f32(true);\n    let cc15 = bool(1i);\n    let cc16 = bool(1u);\n    let cc17 = bool(1f);\n    let cc18 = bool(1);\n    let cc19 = bool(1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/control-flow.toml",
    "content": "[msl]\nlang_version = [1, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/control-flow.wgsl",
    "content": "fn control_flow() {\n    //TODO: execution-only barrier?\n    storageBarrier();\n    workgroupBarrier();\n    textureBarrier();\n\n    var pos: i32;\n    // switch without cases\n    switch 1 {\n        default: {\n            pos = 1;\n        }\n    }\n\n    // non-empty switch *not* in last-statement-in-function position\n    // (return statements might be inserted into the switch cases otherwise)\n    switch pos {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n        }\n        case 3, 4: {\n            pos = 2;\n        }\n        case 5: {\n            pos = 3;\n        }\n        case default, 6: {\n            pos = 4;\n        }\n    }\n\n\t// switch with unsigned integer selectors\n\tswitch(0u) {\n\t\tcase 0u: {\n\t\t}\n        default: {\n        }\n\t}\n\n    // non-empty switch in last-statement-in-function position\n    switch pos {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n        }\n        case 3: {\n            pos = 2;\n        }\n        case 4: {}\n        default: {\n            pos = 3;\n        }\n    }\n\n    // trailing commas\n    switch pos {\n        case 1, {\n            pos = 0;\n        }\n        case 2,: {\n            pos = 1;\n        }\n        case 3, 4, {\n            pos = 2;\n        }\n        case 5, 6,: {\n            pos = 3;\n        }\n        default {\n            pos = 4;\n        }\n    }\n}\n\nfn switch_default_break(i: i32) {\n    switch i {\n        default: {\n            break;\n        }\n    }\n}\n\nfn switch_case_break() {\n    switch(0) {\n        case 0: {\n            break;\n        }\n        default: {}\n    }\n    return;\n}\n\nfn switch_selector_type_conversion() {\n    switch (0u) {\n        case 0: {\n        }\n        default: {\n        }\n    }\n\n    switch (0) {\n        case 0u: {\n        }\n        default: {\n        }\n    }\n}\n\nconst ONE = 1;\nfn switch_const_expr_case_selectors() {\n    const TWO = 2;\n    switch (0) {\n        case i32(): {\n        }\n        case ONE: {\n        }\n        case TWO: {\n        }\n        case 1 + 2: {\n        }\n        case vec4(4).x: {\n        }\n        default: {\n        }\n    }\n}\n\nfn loop_switch_continue(x: i32) {\n    loop {\n        switch x {\n            case 1: {\n                continue;\n            }\n            default: {}\n        }\n    }\n}\n\nfn loop_switch_continue_nesting(x: i32, y: i32, z: i32) {\n    loop {\n        switch x {\n            case 1: {\n                continue;\n            }\n            case 2: {\n                switch y {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        loop {\n                            switch z {\n                                case 1: {\n                                    continue;\n                                }\n                                default: {}\n                            }\n                        }\n                    }\n                }\n            }\n            default: {}\n        }\n\n\n        // Degenerate switch with continue\n        switch y {\n            default: {\n                continue;\n            }\n        }\n    }\n\n    // In separate loop to avoid spv validation error:\n    // See https://github.com/gfx-rs/wgpu/issues/5658\n    loop {\n        // Nested degenerate switch with continue\n        switch y {\n            case 1, default: {\n                switch z {\n                    default: {\n                        continue;\n                    }\n                }\n            }\n        }\n    }\n}\n\n// Cases with some of the loop nested switches not containing continues.\n// See `continue_forward` module in `naga`.\nfn loop_switch_omit_continue_variable_checks(x: i32, y: i32, z: i32, w: i32) {\n    // switch in loop with no continues, we expect checks after the switch\n    // statement to not be generated\n    var pos: i32 = 0;\n    loop {\n        switch x {\n            case 1: {\n                pos = 1;\n            }\n            default: {}\n        }\n        // check here can be omitted\n    }\n\n    loop {\n        switch x {\n            case 1: {}\n            case 2: {\n                switch y {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        switch z {\n                            case 1: {\n                                pos = 2;\n                            }\n                            default: {}\n                        }\n                        // check here can be omitted\n                    }\n                }\n                // check needs to be generated here\n            }\n            default: {}\n        }\n        // check needs to be generated here\n    }\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    control_flow();\n    switch_default_break(1);\n    switch_case_break();\n    switch_selector_type_conversion();\n    switch_const_expr_case_selectors();\n    loop_switch_continue(1);\n    loop_switch_continue_nesting(1, 2, 3);\n    loop_switch_omit_continue_variable_checks(1, 2, 3, 4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/conversion-float-to-int-no-f64.toml",
    "content": "# Other backends are tested by conversion-float-to-int.wgsl which is a\n# superset of this test.\ntargets = \"METAL\"\ncapabilities = \"SHADER_FLOAT16 | SHADER_INT64\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/conversion-float-to-int-no-f64.wgsl",
    "content": "// conversion-float-to-int.wgsl test with `f64` types removed.\n// This means it can be used with the Metal backend.\n\nenable f16;\n\nconst MIN_F16 = -65504h;\nconst MAX_F16 = 65504h;\nconst MIN_F32 = -3.40282347E+38f;\nconst MAX_F32 = 3.40282347E+38f;\nconst MIN_ABSTRACT_FLOAT = -1.7976931348623157E+308;\nconst MAX_ABSTRACT_FLOAT = 1.7976931348623157E+308;\n\n// conversion from float to int during const evaluation. If value is out of\n// range of the destination type we must clamp to a value that is representable\n// by both the source and destination types.\nfn test_const_eval() {\n    var min_f16_to_i32 = i32(MIN_F16);\n    var max_f16_to_i32 = i32(MAX_F16);\n    var min_f16_to_u32 = u32(MIN_F16);\n    var max_f16_to_u32 = u32(MAX_F16);\n    var min_f16_to_i64 = i64(MIN_F16);\n    var max_f16_to_i64 = i64(MAX_F16);\n    var min_f16_to_u64 = u64(MIN_F16);\n    var max_f16_to_u64 = u64(MAX_F16);\n    var min_f32_to_i32 = i32(MIN_F32);\n    var max_f32_to_i32 = i32(MAX_F32);\n    var min_f32_to_u32 = u32(MIN_F32);\n    var max_f32_to_u32 = u32(MAX_F32);\n    var min_f32_to_i64 = i64(MIN_F32);\n    var max_f32_to_i64 = i64(MAX_F32);\n    var min_f32_to_u64 = u64(MIN_F32);\n    var max_f32_to_u64 = u64(MAX_F32);\n    var min_abstract_float_to_i32 = i32(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_i32 = i32(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_u32 = u32(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_u32 = u32(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_i64 = i64(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_i64 = i64(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_u64 = u64(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_u64 = u64(MAX_ABSTRACT_FLOAT);\n}\n\n// conversion from float to int at runtime. Generated code must ensure we avoid\n// undefined behaviour due to casting a value that is out of range of the\n// destination type, and that in such cases the result is a value that is\n// representable by both the source and destination types.\nfn test_f16_to_i32(f: f16) -> i32 {\n  return i32(f);\n}\n\nfn test_f16_to_u32(f: f16) -> u32 {\n  return u32(f);\n}\n\nfn test_f16_to_i64(f: f16) -> i64 {\n  return i64(f);\n}\n\nfn test_f16_to_u64(f: f16) -> u64 {\n  return u64(f);\n}\n\nfn test_f32_to_i32(f: f32) -> i32 {\n  return i32(f);\n}\n\nfn test_f32_to_u32(f: f32) -> u32 {\n  return u32(f);\n}\n\nfn test_f32_to_i64(f: f32) -> i64 {\n  return i64(f);\n}\n\nfn test_f32_to_u64(f: f32) -> u64 {\n  return u64(f);\n}\n\nfn test_f16_to_i32_vec(f: vec2<f16>) -> vec2<i32> {\n  return vec2<i32>(f);\n}\n\nfn test_f16_to_u32_vec(f: vec2<f16>) -> vec2<u32> {\n  return vec2<u32>(f);\n}\n\nfn test_f16_to_i64_vec(f: vec2<f16>) -> vec2<i64> {\n  return vec2<i64>(f);\n}\n\nfn test_f16_to_u64_vec(f: vec2<f16>) -> vec2<u64> {\n  return vec2<u64>(f);\n}\n\nfn test_f32_to_i32_vec(f: vec2<f32>) -> vec2<i32> {\n  return vec2<i32>(f);\n}\n\nfn test_f32_to_u32_vec(f: vec2<f32>) -> vec2<u32> {\n  return vec2<u32>(f);\n}\n\nfn test_f32_to_i64_vec(f: vec2<f32>) -> vec2<i64> {\n  return vec2<i64>(f);\n}\n\nfn test_f32_to_u64_vec(f: vec2<f32>) -> vec2<u64> {\n  return vec2<u64>(f);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    test_const_eval();\n    test_f16_to_i32(1.);\n    test_f16_to_u32(1.);\n    test_f16_to_i64(1.);\n    test_f16_to_u64(1.);\n    test_f32_to_i32(1.);\n    test_f32_to_u32(1.);\n    test_f32_to_i64(1.);\n    test_f32_to_u64(1.);\n    test_f16_to_i32_vec(vec2(1, 2));\n    test_f16_to_u32_vec(vec2(1, 2));\n    test_f16_to_i64_vec(vec2(1, 2));\n    test_f16_to_u64_vec(vec2(1, 2));\n    test_f32_to_i32_vec(vec2(1, 2));\n    test_f32_to_u32_vec(vec2(1, 2));\n    test_f32_to_i64_vec(vec2(1, 2));\n    test_f32_to_u64_vec(vec2(1, 2));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/conversion-float-to-int.toml",
    "content": "# GLSL does not support i64 or f16, but we don't handle float to integer\n# conversion specially there anyway.\n# MSL does not support f64. See conversion-float-to-int-no-f64.wgsl.\ntargets = \"SPIRV | HLSL | WGSL\"\ncapabilities = \"SHADER_FLOAT16 | FLOAT64 | SHADER_INT64\"\n\n[hlsl]\nshader_model = \"V6_0\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/conversion-float-to-int.wgsl",
    "content": "enable f16;\n\nconst MIN_F16 = -65504h;\nconst MAX_F16 = 65504h;\nconst MIN_F32 = -3.40282347E+38f;\nconst MAX_F32 = 3.40282347E+38f;\nconst MIN_F64 = -1.7976931348623157E+308lf;\nconst MAX_F64 = 1.7976931348623157E+308lf;\nconst MIN_ABSTRACT_FLOAT = -1.7976931348623157E+308;\nconst MAX_ABSTRACT_FLOAT = 1.7976931348623157E+308;\n\n// conversion from float to int during const evaluation. If value is out of\n// range of the destination type we must clamp to a value that is representable\n// by both the source and destination types.\nfn test_const_eval() {\n    var min_f16_to_i32 = i32(MIN_F16);\n    var max_f16_to_i32 = i32(MAX_F16);\n    var min_f16_to_u32 = u32(MIN_F16);\n    var max_f16_to_u32 = u32(MAX_F16);\n    var min_f16_to_i64 = i64(MIN_F16);\n    var max_f16_to_i64 = i64(MAX_F16);\n    var min_f16_to_u64 = u64(MIN_F16);\n    var max_f16_to_u64 = u64(MAX_F16);\n    var min_f32_to_i32 = i32(MIN_F32);\n    var max_f32_to_i32 = i32(MAX_F32);\n    var min_f32_to_u32 = u32(MIN_F32);\n    var max_f32_to_u32 = u32(MAX_F32);\n    var min_f32_to_i64 = i64(MIN_F32);\n    var max_f32_to_i64 = i64(MAX_F32);\n    var min_f32_to_u64 = u64(MIN_F32);\n    var max_f32_to_u64 = u64(MAX_F32);\n    var min_f64_to_i64 = i64(MIN_F64);\n    var max_f64_to_i64 = i64(MAX_F64);\n    var min_f64_to_u64 = u64(MIN_F64);\n    var max_f64_to_u64 = u64(MAX_F64);\n    var min_abstract_float_to_i32 = i32(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_i32 = i32(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_u32 = u32(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_u32 = u32(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_i64 = i64(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_i64 = i64(MAX_ABSTRACT_FLOAT);\n    var min_abstract_float_to_u64 = u64(MIN_ABSTRACT_FLOAT);\n    var max_abstract_float_to_u64 = u64(MAX_ABSTRACT_FLOAT);\n}\n\n// conversion from float to int at runtime. Generated code must ensure we avoid\n// undefined behaviour due to casting a value that is out of range of the\n// destination type, and that in such cases the result is a value that is\n// representable by both the source and destination types.\nfn test_f16_to_i32(f: f16) -> i32 {\n  return i32(f);\n}\n\nfn test_f16_to_u32(f: f16) -> u32 {\n  return u32(f);\n}\n\nfn test_f16_to_i64(f: f16) -> i64 {\n  return i64(f);\n}\n\nfn test_f16_to_u64(f: f16) -> u64 {\n  return u64(f);\n}\n\nfn test_f32_to_i32(f: f32) -> i32 {\n  return i32(f);\n}\n\nfn test_f32_to_u32(f: f32) -> u32 {\n  return u32(f);\n}\n\nfn test_f32_to_i64(f: f32) -> i64 {\n  return i64(f);\n}\n\nfn test_f32_to_u64(f: f32) -> u64 {\n  return u64(f);\n}\n\nfn test_f64_to_i32(f: f64) -> i32 {\n  return i32(f);\n}\n\nfn test_f64_to_u32(f: f64) -> u32 {\n  return u32(f);\n}\n\nfn test_f64_to_i64(f: f64) -> i64 {\n  return i64(f);\n}\n\nfn test_f64_to_u64(f: f64) -> u64 {\n  return u64(f);\n}\n\nfn test_f16_to_i32_vec(f: vec2<f16>) -> vec2<i32> {\n  return vec2<i32>(f);\n}\n\nfn test_f16_to_u32_vec(f: vec2<f16>) -> vec2<u32> {\n  return vec2<u32>(f);\n}\n\nfn test_f16_to_i64_vec(f: vec2<f16>) -> vec2<i64> {\n  return vec2<i64>(f);\n}\n\nfn test_f16_to_u64_vec(f: vec2<f16>) -> vec2<u64> {\n  return vec2<u64>(f);\n}\n\nfn test_f32_to_i32_vec(f: vec2<f32>) -> vec2<i32> {\n  return vec2<i32>(f);\n}\n\nfn test_f32_to_u32_vec(f: vec2<f32>) -> vec2<u32> {\n  return vec2<u32>(f);\n}\n\nfn test_f32_to_i64_vec(f: vec2<f32>) -> vec2<i64> {\n  return vec2<i64>(f);\n}\n\nfn test_f32_to_u64_vec(f: vec2<f32>) -> vec2<u64> {\n  return vec2<u64>(f);\n}\n\nfn test_f64_to_i32_vec(f: vec2<f64>) -> vec2<i32> {\n  return vec2<i32>(f);\n}\n\nfn test_f64_to_u32_vec(f: vec2<f64>) -> vec2<u32> {\n  return vec2<u32>(f);\n}\n\nfn test_f64_to_i64_vec(f: vec2<f64>) -> vec2<i64> {\n  return vec2<i64>(f);\n}\n\nfn test_f64_to_u64_vec(f: vec2<f64>) -> vec2<u64> {\n  return vec2<u64>(f);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    test_const_eval();\n    test_f16_to_i32(1.);\n    test_f16_to_u32(1.);\n    test_f16_to_i64(1.);\n    test_f16_to_u64(1.);\n    test_f32_to_i32(1.);\n    test_f32_to_u32(1.);\n    test_f32_to_i64(1.);\n    test_f32_to_u64(1.);\n    test_f64_to_i32(1.);\n    test_f64_to_u32(1.);\n    test_f64_to_i64(1.);\n    test_f64_to_u64(1.);\n    test_f16_to_i32_vec(vec2(1., 2.));\n    test_f16_to_u32_vec(vec2(1., 2.));\n    test_f16_to_i64_vec(vec2(1., 2.));\n    test_f16_to_u64_vec(vec2(1., 2.));\n    test_f32_to_i32_vec(vec2(1., 2.));\n    test_f32_to_u32_vec(vec2(1., 2.));\n    test_f32_to_i64_vec(vec2(1., 2.));\n    test_f32_to_u64_vec(vec2(1., 2.));\n    test_f64_to_i32_vec(vec2(1., 2.));\n    test_f64_to_u32_vec(vec2(1., 2.));\n    test_f64_to_i64_vec(vec2(1., 2.));\n    test_f64_to_u64_vec(vec2(1., 2.));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/conversions.wgsl",
    "content": "// Conversion of initializer expressions\n// (the abstract-types-* tests have a bunch more like this)\nconst ic0: vec2f = vec2(1, 1) + vec2(1.0, 1.0);\n\n// Conversion by value constructors\n//let vc0 = i32(1.0);  // https://github.com/gfx-rs/wgpu/issues/7312\n// etc. (also create the locals versions below)\n\n@compute @workgroup_size(1)\nfn main() {\n    const ic0: vec2f = vec2(1, 1) + vec2(1.0, 1.0);\n\n    let lc0: vec2f = vec2(1, 1) + vec2(1.0, 1.0);\n\n    var vc0: vec2f = vec2(1, 1) + vec2(1.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/cooperative-matrix.toml",
    "content": "targets = \"SPIRV | METAL | WGSL\"\ncapabilities = \"COOPERATIVE_MATRIX\"\n\n[spv]\ndebug = false\nversion = [1, 4]\n\n[msl]\nlang_version = [2, 3]\n"
  },
  {
    "path": "naga/tests/in/wgsl/cooperative-matrix.wgsl",
    "content": "enable wgpu_cooperative_matrix;\n\n// type declarations with different roles\nvar<private> a: coop_mat8x8<f32, A>;\nvar<private> b: coop_mat8x8<f32, B>;\n@group(0) @binding(0)\nvar<storage, read_write> ext: array<f32>;\n\n@compute @workgroup_size(8, 8, 1)\nfn main() {\n    // loading from memory\n    var c = coopLoad<coop_mat8x8<f32, C>>(&ext[4]);\n    // actual multiply-add\n    var d = coopMultiplyAdd(a, b, c);\n    // storing into memory\n    coopStore(d, &ext[0]);\n    // operations on the type\n    c = d;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/cross.wgsl",
    "content": "// NOTE: invalid combinations are tested in the `validation::bad_cross_builtin_args` test.\n@compute @workgroup_size(1) fn main() {\n    let a = cross(vec3(1., 0., 0.), vec3(0., 1., 0.));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/cubeArrayShadow.toml",
    "content": "targets = \"GLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/cubeArrayShadow.wgsl",
    "content": "@group(0) @binding(4)\nvar point_shadow_textures: texture_depth_cube_array;\n@group(0) @binding(5)\nvar point_shadow_textures_sampler: sampler_comparison;\n\n@fragment\nfn fragment() -> @location(0) vec4<f32> {\n    let frag_ls = vec4<f32>(1., 1., 2., 1.).xyz;\n    let a = textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(1), 1.);\n\n    return vec4<f32>(a, 1., 1., 1.);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-large-source.toml",
    "content": "targets = \"SPIRV\"\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-large-source.wgsl",
    "content": "//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 T\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n\n\n// Taken from https://github.com/sotrh/learn-wgpu/blob/11820796f5e1dbce42fb1119f04ddeb4b167d2a0/code/intermediate/tutorial13-terrain/src/terrain.wgsl\n// ============================\n// Terrain Generation\n// ============================\n\n// https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39\n//  MIT License. © Ian McEwan, Stefan Gustavson, Munrocket\n// - Less condensed glsl implementation with comments can be found at https://weber.itn.liu.se/~stegu/jgt2012/article.pdf\n\nfn permute3(x: vec3<f32>) -> vec3<f32> { return (((x * 34.) + 1.) * x) % vec3<f32>(289.); }\n\nfn snoise2(v: vec2<f32>) -> f32 {\n    let C = vec4<f32>(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);\n    var i: vec2<f32> = floor(v + dot(v, C.yy));\n    let x0 = v - i + dot(i, C.xx);\n    // I flipped the condition here from > to < as it fixed some artifacting I was observing\n    var i1: vec2<f32> = select(vec2<f32>(1., 0.), vec2<f32>(0., 1.), (x0.x < x0.y));\n    var x12: vec4<f32> = x0.xyxy + C.xxzz - vec4<f32>(i1, 0., 0.);\n    i = i % vec2<f32>(289.);\n    let p = permute3(permute3(i.y + vec3<f32>(0., i1.y, 1.)) + i.x + vec3<f32>(0., i1.x, 1.));\n    var m: vec3<f32> = max(0.5 - vec3<f32>(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3<f32>(0.));\n    m = m * m;\n    m = m * m;\n    let x = 2. * fract(p * C.www) - 1.;\n    let h = abs(x) - 0.5;\n    //This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n    let ox = floor(x + 0.5);\n    let a0 = x - ox;\n    m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));\n    let g = vec3<f32>(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);\n    return 130. * dot(m, g);\n}\n\n\nfn fbm(p: vec2<f32>) -> f32 {\n    let NUM_OCTAVES: u32 = 5u;\n    var x = p * 0.01;\n    var v = 0.0;\n    var a = 0.5;\n    let shift = vec2<f32>(100.0);\n    let cs = vec2<f32>(cos(0.5), sin(0.5));\n    let rot = mat2x2<f32>(cs.x, cs.y, -cs.y, cs.x);\n\n    for (var i = 0u; i < NUM_OCTAVES; i = i + 1u) {\n        v = v + a * snoise2(x);\n        x = rot * x * 2.0 + shift;\n        a = a * 0.5;\n    }\n\n    return v;\n}\n\nstruct ChunkData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n}\n\nstruct Vertex {\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n}\n\nstruct VertexBuffer {\n    data: array<Vertex>, // stride: 32\n}\n\nstruct IndexBuffer {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) var<uniform> chunk_data: ChunkData;\n@group(0) @binding(1) var<storage, read_write> vertices: VertexBuffer;\n@group(0) @binding(2) var<storage, read_write> indices: IndexBuffer;\n\nfn terrain_point(p: vec2<f32>, min_max_height: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        p.x,\n        mix(min_max_height.x, min_max_height.y, fbm(p)),\n        p.y,\n    );\n}\n\nfn terrain_vertex(p: vec2<f32>, min_max_height: vec2<f32>) -> Vertex {\n    let v = terrain_point(p, min_max_height);\n\n    let tpx = terrain_point(p + vec2<f32>(0.1, 0.0), min_max_height) - v;\n    let tpz = terrain_point(p + vec2<f32>(0.0, 0.1), min_max_height) - v;\n    let tnx = terrain_point(p + vec2<f32>(-0.1, 0.0), min_max_height) - v;\n    let tnz = terrain_point(p + vec2<f32>(0.0, -0.1), min_max_height) - v;\n\n    let pn = normalize(cross(tpz, tpx));\n    let nn = normalize(cross(tnz, tnx));\n\n    let n = (pn + nn) * 0.5;\n\n    return Vertex(v, n);\n}\n\nfn index_to_p(vert_index: u32, chunk_size: vec2<u32>, chunk_corner: vec2<i32>) -> vec2<f32> {\n    return vec2(\n        f32(vert_index) % f32(chunk_size.x + 1u),\n        f32(vert_index / (chunk_size.x + 1u)),\n    ) + vec2<f32>(chunk_corner);\n}\n\n@compute @workgroup_size(64)\nfn gen_terrain_compute(\n    @builtin(global_invocation_id) gid: vec3<u32>\n) {\n    // Create vert_component\n    let vert_index = gid.x;\n\n    let p = index_to_p(vert_index, chunk_data.chunk_size, chunk_data.chunk_corner);\n\n    vertices.data[vert_index] = terrain_vertex(p, chunk_data.min_max_height);\n\n    // Create indices\n    let start_index = gid.x * 6u; // using TriangleList\n\n    if start_index >= (chunk_data.chunk_size.x * chunk_data.chunk_size.y * 6u) { return; }\n\n    let v00 = vert_index + gid.x / chunk_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + chunk_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    indices.data[start_index] = v00;\n    indices.data[start_index + 1u] = v01;\n    indices.data[start_index + 2u] = v11;\n    indices.data[start_index + 3u] = v00;\n    indices.data[start_index + 4u] = v11;\n    indices.data[start_index + 5u] = v10;\n}\n\n// ============================\n// Terrain Gen (Fragment Shader)\n// ============================\n\nstruct GenData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n    texture_size: u32,\n    start_index: u32,\n}\n@group(0)\n@binding(0)\nvar<uniform> gen_data: GenData;\n\nstruct GenVertexOutput {\n    @location(0)\n    index: u32,\n    @builtin(position)\n    position: vec4<f32>,\n    @location(1)\n    uv: vec2<f32>,\n};\n\n@vertex\nfn gen_terrain_vertex(@builtin(vertex_index) vindex: u32) -> GenVertexOutput {\n    let u = f32(((vindex + 2u) / 3u) % 2u);\n    let v = f32(((vindex + 1u) / 3u) % 2u);\n    let uv = vec2<f32>(u, v);\n\n    let position = vec4<f32>(-1.0 + uv * 2.0, 0.0, 1.0);\n\n    // TODO: maybe replace this with u32(dot(uv, vec2(f32(gen_data.texture_dim.x))))\n    let index = u32(uv.x * f32(gen_data.texture_size) + uv.y * f32(gen_data.texture_size)) + gen_data.start_index;\n\n    return GenVertexOutput(index, position, uv);\n}\n\n\nstruct GenFragmentOutput {\n    @location(0) vert_component: u32,\n    @location(1) index: u32,\n}\n\n@fragment\nfn gen_terrain_fragment(in: GenVertexOutput) -> GenFragmentOutput {\n    let i = u32(in.uv.x * f32(gen_data.texture_size) + in.uv.y * f32(gen_data.texture_size * gen_data.texture_size)) + gen_data.start_index;\n    let vert_index = u32(floor(f32(i) / 6.));\n    let comp_index = i % 6u;\n\n    let p = index_to_p(vert_index, gen_data.chunk_size, gen_data.chunk_corner);\n    let v = terrain_vertex(p, gen_data.min_max_height);\n\n    var vert_component: f32 = 0.;\n\n    switch comp_index {\n        case 0u: { vert_component = v.position.x; }\n        case 1u: { vert_component = v.position.y; }\n        case 2u: { vert_component = v.position.z; }\n        case 3u: { vert_component = v.normal.x; }\n        case 4u: { vert_component = v.normal.y; }\n        case 5u: { vert_component = v.normal.z; }\n        default: {}\n    }\n\n    let v00 = vert_index + vert_index / gen_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + gen_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    var index = 0u;\n    switch comp_index {\n        case 0u, 3u: { index = v00; }\n        case 2u, 4u: { index = v11; }\n        case 1u: { index = v01; }\n        case 5u: { index = v10; }\n        default: {}\n    }\n    index = in.index;\n    // index = gen_data.start_index;\n    // indices.data[start_index] = v00;\n    // indices.data[start_index + 1u] = v01;\n    // indices.data[start_index + 2u] = v11;\n    // indices.data[start_index + 3u] = v00;\n    // indices.data[start_index + 4u] = v11;\n    // indices.data[start_index + 5u] = v10;\n\n    let ivert_component = bitcast<u32>(vert_component);\n    return GenFragmentOutput(ivert_component, index);\n}\n\n// ============================\n// Terrain Rendering\n// ============================\n\nstruct Camera {\n    view_pos: vec4<f32>,\n    view_proj: mat4x4<f32>,\n}\n@group(0) @binding(0)\nvar<uniform> camera: Camera;\n\nstruct Light {\n    position: vec3<f32>,\n    color: vec3<f32>,\n}\n@group(1) @binding(0)\nvar<uniform> light: Light;\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) normal: vec3<f32>,\n    @location(1) world_pos: vec3<f32>,\n}\n\n@vertex\nfn vs_main(\n    vertex: Vertex,\n) -> VertexOutput {\n    let clip_position = camera.view_proj * vec4<f32>(vertex.position, 1.);\n    let normal = vertex.normal;\n    return VertexOutput(clip_position, normal, vertex.position);\n}\n\n@group(2) @binding(0)\nvar t_diffuse: texture_2d<f32>;\n@group(2) @binding(1)\nvar s_diffuse: sampler;\n@group(2) @binding(2)\nvar t_normal: texture_2d<f32>;\n@group(2) @binding(3)\nvar s_normal: sampler;\n\nfn color23(p: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        snoise2(p) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(23., 32.)) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(-43., 3.)) * 0.5 + 0.5,\n    );\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    _ = t_diffuse;\n    _ = s_diffuse;\n    _ = t_normal;\n    _ = s_normal;\n\n    color23(vec2(1, 2));\n\n    var color = smoothstep(vec3<f32>(0.0), vec3<f32>(0.1), fract(in.world_pos));\n    color = mix(vec3<f32>(0.5, 0.1, 0.7), vec3<f32>(0.2, 0.2, 0.2), vec3<f32>(color.x * color.y * color.z));\n\n    let ambient_strength = 0.1;\n    let ambient_color = light.color * ambient_strength;\n\n    let light_dir = normalize(light.position - in.world_pos);\n    let view_dir = normalize(camera.view_pos.xyz - in.world_pos);\n    let half_dir = normalize(view_dir + light_dir);\n\n    let diffuse_strength = max(dot(in.normal, light_dir), 0.0);\n    let diffuse_color = diffuse_strength * light.color;\n\n    let specular_strength = pow(max(dot(in.normal, half_dir), 0.0), 32.0);\n    let specular_color = specular_strength * light.color;\n\n    let result = (ambient_color + diffuse_color + specular_color) * color;\n\n    return vec4<f32>(result, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-simple.toml",
    "content": "targets = \"SPIRV\"\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-simple.wgsl",
    "content": "struct VertexInput {\n    @location(0) position: vec3<f32>,\n    @location(1) color: vec3<f32>,\n};\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) color: vec3<f32>,\n};\n\n@vertex\nfn vs_main(\n    model: VertexInput,\n) -> VertexOutput {\n    var out: VertexOutput;\n    out.color = model.color;\n    out.clip_position = vec4<f32>(model.position, 1.0);\n    return out;\n}\n\n// Fragment shader\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    var color = in.color;\n    for (var i = 0; i < 10; i += 1) {\n        var ii = f32(i);\n        color.x += ii*0.001;\n        color.y += ii*0.002;\n    }\n\n    return vec4<f32>(color, 1.0);\n}"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-terrain.toml",
    "content": "targets = \"SPIRV\"\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/debug-symbol-terrain.wgsl",
    "content": "// Taken from https://github.com/sotrh/learn-wgpu/blob/11820796f5e1dbce42fb1119f04ddeb4b167d2a0/code/intermediate/tutorial13-terrain/src/terrain.wgsl\n// ============================\n// Terrain Generation\n// ============================\n\n// https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39\n//  MIT License. © Ian McEwan, Stefan Gustavson, Munrocket\n// - Less condensed glsl implementation with comments can be found at https://weber.itn.liu.se/~stegu/jgt2012/article.pdf\n\nfn permute3(x: vec3<f32>) -> vec3<f32> { return (((x * 34.) + 1.) * x) % vec3<f32>(289.); }\n\nfn snoise2(v: vec2<f32>) -> f32 {\n    let C = vec4<f32>(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);\n    var i: vec2<f32> = floor(v + dot(v, C.yy));\n    let x0 = v - i + dot(i, C.xx);\n    // I flipped the condition here from > to < as it fixed some artifacting I was observing\n    var i1: vec2<f32> = select(vec2<f32>(1., 0.), vec2<f32>(0., 1.), (x0.x < x0.y));\n    var x12: vec4<f32> = x0.xyxy + C.xxzz - vec4<f32>(i1, 0., 0.);\n    i = i % vec2<f32>(289.);\n    let p = permute3(permute3(i.y + vec3<f32>(0., i1.y, 1.)) + i.x + vec3<f32>(0., i1.x, 1.));\n    var m: vec3<f32> = max(0.5 - vec3<f32>(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3<f32>(0.));\n    m = m * m;\n    m = m * m;\n    let x = 2. * fract(p * C.www) - 1.;\n    let h = abs(x) - 0.5;\n    let ox = floor(x + 0.5);\n    let a0 = x - ox;\n    m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));\n    let g = vec3<f32>(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);\n    return 130. * dot(m, g);\n}\n\n\nfn fbm(p: vec2<f32>) -> f32 {\n    let NUM_OCTAVES: u32 = 5u;\n    var x = p * 0.01;\n    var v = 0.0;\n    var a = 0.5;\n    let shift = vec2<f32>(100.0);\n    let cs = vec2<f32>(cos(0.5), sin(0.5));\n    let rot = mat2x2<f32>(cs.x, cs.y, -cs.y, cs.x);\n\n    for (var i = 0u; i < NUM_OCTAVES; i = i + 1u) {\n        v = v + a * snoise2(x);\n        x = rot * x * 2.0 + shift;\n        a = a * 0.5;\n    }\n\n    return v;\n}\n\nstruct ChunkData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n}\n\nstruct Vertex {\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n}\n\nstruct VertexBuffer {\n    data: array<Vertex>, // stride: 32\n}\n\nstruct IndexBuffer {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) var<uniform> chunk_data: ChunkData;\n@group(0) @binding(1) var<storage, read_write> vertices: VertexBuffer;\n@group(0) @binding(2) var<storage, read_write> indices: IndexBuffer;\n\nfn terrain_point(p: vec2<f32>, min_max_height: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        p.x,\n        mix(min_max_height.x, min_max_height.y, fbm(p)),\n        p.y,\n    );\n}\n\nfn terrain_vertex(p: vec2<f32>, min_max_height: vec2<f32>) -> Vertex {\n    let v = terrain_point(p, min_max_height);\n\n    let tpx = terrain_point(p + vec2<f32>(0.1, 0.0), min_max_height) - v;\n    let tpz = terrain_point(p + vec2<f32>(0.0, 0.1), min_max_height) - v;\n    let tnx = terrain_point(p + vec2<f32>(-0.1, 0.0), min_max_height) - v;\n    let tnz = terrain_point(p + vec2<f32>(0.0, -0.1), min_max_height) - v;\n\n    let pn = normalize(cross(tpz, tpx));\n    let nn = normalize(cross(tnz, tnx));\n\n    let n = (pn + nn) * 0.5;\n\n    return Vertex(v, n);\n}\n\nfn index_to_p(vert_index: u32, chunk_size: vec2<u32>, chunk_corner: vec2<i32>) -> vec2<f32> {\n    return vec2(\n        f32(vert_index) % f32(chunk_size.x + 1u),\n        f32(vert_index / (chunk_size.x + 1u)),\n    ) + vec2<f32>(chunk_corner);\n}\n\n@compute @workgroup_size(64)\nfn gen_terrain_compute(\n    @builtin(global_invocation_id) gid: vec3<u32>\n) {\n    // Create vert_component\n    let vert_index = gid.x;\n\n    let p = index_to_p(vert_index, chunk_data.chunk_size, chunk_data.chunk_corner);\n\n    vertices.data[vert_index] = terrain_vertex(p, chunk_data.min_max_height);\n\n    // Create indices\n    let start_index = gid.x * 6u; // using TriangleList\n\n    if (start_index >= (chunk_data.chunk_size.x * chunk_data.chunk_size.y * 6u)) { return; }\n\n    let v00 = vert_index + gid.x / chunk_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + chunk_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    indices.data[start_index] = v00;\n    indices.data[start_index + 1u] = v01;\n    indices.data[start_index + 2u] = v11;\n    indices.data[start_index + 3u] = v00;\n    indices.data[start_index + 4u] = v11;\n    indices.data[start_index + 5u] = v10;\n}\n\n// ============================\n// Terrain Gen (Fragment Shader)\n// ============================\n\nstruct GenData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n    texture_size: u32,\n    start_index: u32,\n}\n@group(0)\n@binding(0)\nvar<uniform> gen_data: GenData;\n\nstruct GenVertexOutput {\n    @location(0)\n    index: u32,\n    @builtin(position)\n    position: vec4<f32>,\n    @location(1)\n    uv: vec2<f32>,\n};\n\n@vertex\nfn gen_terrain_vertex(@builtin(vertex_index) vindex: u32) -> GenVertexOutput {\n    let u = f32(((vindex + 2u) / 3u) % 2u);\n    let v = f32(((vindex + 1u) / 3u) % 2u);\n    let uv = vec2<f32>(u, v);\n\n    let position = vec4<f32>(-1.0 + uv * 2.0, 0.0, 1.0);\n\n    // TODO: maybe replace this with u32(dot(uv, vec2(f32(gen_data.texture_dim.x))))\n    let index = u32(uv.x * f32(gen_data.texture_size) + uv.y * f32(gen_data.texture_size)) + gen_data.start_index;\n\n    return GenVertexOutput(index, position, uv);\n}\n\n\nstruct GenFragmentOutput {\n    @location(0) vert_component: u32,\n    @location(1) index: u32,\n}\n\n@fragment\nfn gen_terrain_fragment(in: GenVertexOutput) -> GenFragmentOutput {\n    let i = u32(in.uv.x * f32(gen_data.texture_size) + in.uv.y * f32(gen_data.texture_size * gen_data.texture_size)) + gen_data.start_index;\n    let vert_index = u32(floor(f32(i) / 6.));\n    let comp_index = i % 6u;\n\n    let p = index_to_p(vert_index, gen_data.chunk_size, gen_data.chunk_corner);\n    let v = terrain_vertex(p, gen_data.min_max_height);\n\n    var vert_component: f32 = 0.;\n\n    switch comp_index {\n        case 0u: { vert_component = v.position.x; }\n        case 1u: { vert_component = v.position.y; }\n        case 2u: { vert_component = v.position.z; }\n        case 3u: { vert_component = v.normal.x; }\n        case 4u: { vert_component = v.normal.y; }\n        case 5u: { vert_component = v.normal.z; }\n        default: {}\n    }\n\n    let v00 = vert_index + vert_index / gen_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + gen_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    var index = 0u;\n    switch comp_index {\n        case 0u, 3u: { index = v00; }\n        case 2u, 4u: { index = v11; }\n        case 1u: { index = v01; }\n        case 5u: { index = v10; }\n        default: {}\n    }\n    index = in.index;\n    // index = gen_data.start_index;\n    // indices.data[start_index] = v00;\n    // indices.data[start_index + 1u] = v01;\n    // indices.data[start_index + 2u] = v11;\n    // indices.data[start_index + 3u] = v00;\n    // indices.data[start_index + 4u] = v11;\n    // indices.data[start_index + 5u] = v10;\n\n    let ivert_component = bitcast<u32>(vert_component);\n    return GenFragmentOutput(ivert_component, index);\n}\n\n// ============================\n// Terrain Rendering\n// ============================\n\nstruct Camera {\n    view_pos: vec4<f32>,\n    view_proj: mat4x4<f32>,\n}\n@group(0) @binding(0)\nvar<uniform> camera: Camera;\n\nstruct Light {\n    position: vec3<f32>,\n    color: vec3<f32>,\n}\n@group(1) @binding(0)\nvar<uniform> light: Light;\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) normal: vec3<f32>,\n    @location(1) world_pos: vec3<f32>,\n}\n\n@vertex\nfn vs_main(\n    vertex: Vertex,\n) -> VertexOutput {\n    let clip_position = camera.view_proj * vec4<f32>(vertex.position, 1.);\n    let normal = vertex.normal;\n    return VertexOutput(clip_position, normal, vertex.position);\n}\n\n@group(2) @binding(0)\nvar t_diffuse: texture_2d<f32>;\n@group(2) @binding(1)\nvar s_diffuse: sampler;\n@group(2) @binding(2)\nvar t_normal: texture_2d<f32>;\n@group(2) @binding(3)\nvar s_normal: sampler;\n\nfn color23(p: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        snoise2(p) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(23., 32.)) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(-43., 3.)) * 0.5 + 0.5,\n    );\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    _ = t_diffuse;\n    _ = s_diffuse;\n    _ = t_normal;\n    _ = s_normal;\n\n    color23(vec2(1, 2));\n\n    var color = smoothstep(vec3<f32>(0.0), vec3<f32>(0.1), fract(in.world_pos));\n    color = mix(vec3<f32>(0.5, 0.1, 0.7), vec3<f32>(0.2, 0.2, 0.2), vec3<f32>(color.x * color.y * color.z));\n\n    let ambient_strength = 0.1;\n    let ambient_color = light.color * ambient_strength;\n\n    let light_dir = normalize(light.position - in.world_pos);\n    let view_dir = normalize(camera.view_pos.xyz - in.world_pos);\n    let half_dir = normalize(view_dir + light_dir);\n\n    let diffuse_strength = max(dot(in.normal, light_dir), 0.0);\n    let diffuse_color = diffuse_strength * light.color;\n\n    let specular_strength = pow(max(dot(in.normal, half_dir), 0.0), 32.0);\n    let specular_color = specular_strength * light.color;\n\n    let result = (ambient_color + diffuse_color + specular_color) * color;\n\n    return vec4<f32>(result, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/diagnostic-filter.toml",
    "content": "targets = \"IR\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/diagnostic-filter.wgsl",
    "content": "diagnostic(off, derivative_uniformity);\n\nfn thing() {}\n\n@diagnostic(warning, derivative_uniformity)\nfn with_diagnostic() {}\n\n@compute @workgroup_size(1)\nfn main() {\n    thing();\n    with_diagnostic();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/draw-index.toml",
    "content": "capabilities = \"DRAW_INDEX\"\ntargets = \"SPIRV | WGSL\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"DrawParameters\"]\n"
  },
  {
    "path": "naga/tests/in/wgsl/draw-index.wgsl",
    "content": "enable draw_index;\n\nstruct Input {\n    @builtin(draw_index) draw_index: u32,\n}\n\n@vertex\nfn vertex(input: Input) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(f32(input.draw_index), 1.0, 1.0, 1.0);\n}"
  },
  {
    "path": "naga/tests/in/wgsl/dualsource.toml",
    "content": "capabilities = \"DUAL_SOURCE_BLENDING\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [1, 2]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/dualsource.wgsl",
    "content": "enable dual_source_blending;\n\nstruct FragmentOutput {\n    @location(0) @blend_src(0) output0: vec4<f32>,\n    @location(0) @blend_src(1) output1: vec4<f32>,\n}\n\n@fragment\nfn main() -> FragmentOutput {\n    return FragmentOutput(vec4(0.4,0.3,0.2,0.1), vec4(0.9,0.8,0.7,0.6));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/early-depth-test-conservative.toml",
    "content": "capabilities = \"EARLY_DEPTH_TEST\"\ntargets = \"SPIRV | GLSL\"\n\n[glsl]\nversion.Desktop = 420\n"
  },
  {
    "path": "naga/tests/in/wgsl/early-depth-test-conservative.wgsl",
    "content": "@fragment\n@early_depth_test(less_equal)\nfn main(@builtin(position) pos: vec4<f32>) -> @builtin(frag_depth) f32 {\n    return pos.z - 0.1;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/early-depth-test-force.toml",
    "content": "capabilities = \"EARLY_DEPTH_TEST\"\ntargets = \"SPIRV | GLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/early-depth-test-force.wgsl",
    "content": "@fragment\n@early_depth_test(force)\nfn main() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.4, 0.3, 0.2, 0.1);\n}"
  },
  {
    "path": "naga/tests/in/wgsl/empty-if.toml",
    "content": "[spv]\nversion = [1, 6]\n"
  },
  {
    "path": "naga/tests/in/wgsl/empty-if.wgsl",
    "content": "@workgroup_size(1)\n@compute\nfn comp(@builtin(global_invocation_id) id: vec3<u32>) {\n    if (id.x == 0) {\n\n    }\n    _ = 1+1; // otherwise, naga generates returns in the if statement.\n    return;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/empty.wgsl",
    "content": "@compute @workgroup_size(1)\nfn main() {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/extra.toml",
    "content": "capabilities = \"IMMEDIATES | PRIMITIVE_INDEX\"\ntargets = \"SPIRV | METAL | WGSL\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 3]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.main]\nimmediates_buffer = 1\n\n[spv]\nversion = [1, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/extra.wgsl",
    "content": "enable primitive_index;\n\nstruct ImmediateData {\n    index: u32,\n    double: vec2<f32>,\n}\nvar<immediate> im: ImmediateData;\n\nstruct FragmentIn {\n    @location(0) color: vec4<f32>,\n    @builtin(primitive_index) primitive_index: u32,\n}\n\n@fragment\nfn main(in: FragmentIn) -> @location(0) vec4<f32> {\n    if in.primitive_index == im.index {\n        return in.color;\n    } else {\n        return vec4<f32>(vec3<f32>(1.0) - in.color.rgb, in.color.a);\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/f16-native.toml",
    "content": "targets = \"SPIRV\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[spv]\ndebug = true\nversion = [1, 1]\nuse_storage_input_output_16 = true\ncapabilities = [\"Float16\"]\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\nimage = \"ReadZeroSkipWrite\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/f16-native.wgsl",
    "content": "enable f16;\n\n@fragment\nfn test_direct(\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = scalar_f16 + 1.0h;\n    output.scalar_f32 = scalar_f32 + 1.0;\n    output.vec2_f16 = vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = vec2_f32 + vec2(1.0);\n    output.vec3_f16 = vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = vec3_f32 + vec3(1.0);\n    output.vec4_f16 = vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = vec4_f32 + vec4(1.0);\n    return output;\n}\n\nstruct F16IO {\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n}\n\n@fragment\nfn test_struct(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_copy_input(input_original: F16IO) -> F16IO {\n    var input = input_original;\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_return_partial(input_original: F16IO) -> @location(0) f16 {\n    var input = input_original;\n    input.scalar_f16 = 0.0h;\n    return input.scalar_f16;\n}\n\n@fragment\nfn test_component_access(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.vec2_f16.x = input.vec2_f16.y;\n    output.vec2_f16.y = input.vec2_f16.x;\n    return output;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/f16-polyfill.toml",
    "content": "targets = \"SPIRV\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[spv]\ndebug = true\nversion = [1, 1]\nuse_storage_input_output_16 = false\ncapabilities = [\"Float16\"]\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\nimage = \"ReadZeroSkipWrite\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/f16-polyfill.wgsl",
    "content": "enable f16;\n\n@fragment\nfn test_direct(\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = scalar_f16 + 1.0h;\n    output.scalar_f32 = scalar_f32 + 1.0;\n    output.vec2_f16 = vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = vec2_f32 + vec2(1.0);\n    output.vec3_f16 = vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = vec3_f32 + vec3(1.0);\n    output.vec4_f16 = vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = vec4_f32 + vec4(1.0);\n    return output;\n}\n\nstruct F16IO {\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n}\n\n@fragment\nfn test_struct(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_copy_input(input_original: F16IO) -> F16IO {\n    var input = input_original;\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_return_partial(input_original: F16IO) -> @location(0) f16 {\n    var input = input_original;\n    input.scalar_f16 = 0.0h;\n    return input.scalar_f16;\n}\n\n@fragment\nfn test_component_access(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.vec2_f16.x = input.vec2_f16.y;\n    output.vec2_f16.y = input.vec2_f16.x;\n    return output;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/f16.toml",
    "content": "# No GLSL support for f16\ntargets = \"SPIRV | METAL | HLSL | WGSL\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[spv]\nversion = [1, 0]\n\n[hlsl]\nshader_model = \"V6_2\"\nspecial_constants_binding = { space = 1, register = 0 }\nimmediates_target = { space = 0, register = 0 }\n\nlang_version = [1, 0]\nper_entry_point_map = {}\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/f16.wgsl",
    "content": "enable f16;\n\nvar<private> private_variable: f16 = 1h;\nconst constant_variable: f16 = f16(15.2);\n\nstruct UniformCompatible {\n   // Other types\n   val_u32: u32,\n   val_i32: i32,\n   val_f32: f32,\n\n   // f16\n   val_f16: f16,\n   val_f16_2: vec2<f16>,\n   val_f16_3: vec3<f16>,\n   val_f16_4: vec4<f16>,\n   final_value: f16,\n\n   val_mat2x2: mat2x2<f16>,\n   val_mat2x3: mat2x3<f16>,\n   val_mat2x4: mat2x4<f16>,\n   val_mat3x2: mat3x2<f16>,\n   val_mat3x3: mat3x3<f16>,\n   val_mat3x4: mat3x4<f16>,\n   val_mat4x2: mat4x2<f16>,\n   val_mat4x3: mat4x3<f16>,\n   val_mat4x4: mat4x4<f16>,\n}\n\nstruct StorageCompatible {\n   val_f16_array_2: array<f16, 2>,\n}\n\nstruct LayoutTest {\n   scalar1: f16, scalar2: f16, v3: vec3<f16>, tuck_in: f16, scalar4: f16, larger: u32\n}\n\n@group(0) @binding(0)\nvar<uniform> input_uniform: UniformCompatible;\n\n@group(0) @binding(1)\nvar<storage> input_storage: UniformCompatible;\n\n@group(0) @binding(2)\nvar<storage> input_arrays: StorageCompatible;\n\n@group(0) @binding(3)\nvar<storage, read_write> output: UniformCompatible;\n\n@group(0) @binding(4)\nvar<storage, read_write> output_arrays: StorageCompatible;\n\nfn f16_function(x: f16) -> f16 {\n   _ = private_variable;\n   var l: LayoutTest;\n\n   var val: f16 = f16(constant_variable);\n   // A number too big for f16\n   val += 1h - 33333h;\n   // Constructing an f16 from an AbstractInt\n   val += val + f16(5.);\n   // Constructing a f16 from other types and other types from f16.\n   val += f16(input_uniform.val_f32 + f32(val));\n   // Constructing a vec3<i64> from a i64\n   val += vec3<f16>(input_uniform.val_f16).z;\n\n   // Cast min and max finite f16 literals to other types. Max value should convert\n   // exactly to other types, but min (or any negative) should clamp to zero for u32.\n   output.val_i32 = i32(65504h);\n   output.val_i32 = i32(-65504h);\n   output.val_u32 = u32(65504h);\n   output.val_u32 = u32(-65504h);\n   output.val_f32 = f32(65504h);\n   output.val_f32 = f32(-65504h);\n\n   // Reading/writing to a uniform/storage buffer\n   output.val_f16 = input_uniform.val_f16 + input_storage.val_f16;\n   output.val_f16_2 = input_uniform.val_f16_2 + input_storage.val_f16_2;\n   output.val_f16_3 = input_uniform.val_f16_3 + input_storage.val_f16_3;\n   output.val_f16_4 = input_uniform.val_f16_4 + input_storage.val_f16_4;\n\n   output.val_mat2x2 = input_uniform.val_mat2x2 + input_storage.val_mat2x2;\n   output.val_mat2x3 = input_uniform.val_mat2x3 + input_storage.val_mat2x3;\n   output.val_mat2x4 = input_uniform.val_mat2x4 + input_storage.val_mat2x4;\n   output.val_mat3x2 = input_uniform.val_mat3x2 + input_storage.val_mat3x2;\n   output.val_mat3x3 = input_uniform.val_mat3x3 + input_storage.val_mat3x3;\n   output.val_mat3x4 = input_uniform.val_mat3x4 + input_storage.val_mat3x4;\n   output.val_mat4x2 = input_uniform.val_mat4x2 + input_storage.val_mat4x2;\n   output.val_mat4x3 = input_uniform.val_mat4x3 + input_storage.val_mat4x3;\n   output.val_mat4x4 = input_uniform.val_mat4x4 + input_storage.val_mat4x4;\n\n   output_arrays.val_f16_array_2 = input_arrays.val_f16_array_2;\n\n   // We make sure not to use 32 in these arguments, so it's clear in the results which are builtin\n   // constants based on the size of the type, and which are arguments.\n\n   // Numeric functions\n   val += abs(val);\n   val += clamp(val, val, val);\n   val += dot(vec2(val), vec2(val));\n   val += max(val, val);\n   val += min(val, val);\n   val += sign(val);\n   \n   val += f16(1.0);\n\n   // We use the shorthand aliases here to ensure the aliases\n   // work correctly.\n\n   // Cast vectors to/from f32\n   let float_vec2 = vec2f(input_uniform.val_f16_2);\n   output.val_f16_2 = vec2h(float_vec2);\n\n   let float_vec3 = vec3f(input_uniform.val_f16_3);\n   output.val_f16_3 = vec3h(float_vec3);\n\n   let float_vec4 = vec4f(input_uniform.val_f16_4);\n   output.val_f16_4 = vec4h(float_vec4);\n\n   // Cast matrices to/from f32\n   output.val_mat2x2 = mat2x2h(mat2x2f(input_uniform.val_mat2x2));\n   output.val_mat2x3 = mat2x3h(mat2x3f(input_uniform.val_mat2x3));\n   output.val_mat2x4 = mat2x4h(mat2x4f(input_uniform.val_mat2x4));\n   output.val_mat3x2 = mat3x2h(mat3x2f(input_uniform.val_mat3x2));\n   output.val_mat3x3 = mat3x3h(mat3x3f(input_uniform.val_mat3x3));\n   output.val_mat3x4 = mat3x4h(mat3x4f(input_uniform.val_mat3x4));\n   output.val_mat4x2 = mat4x2h(mat4x2f(input_uniform.val_mat4x2));\n   output.val_mat4x3 = mat4x3h(mat4x3f(input_uniform.val_mat4x3));\n   output.val_mat4x4 = mat4x4h(mat4x4f(input_uniform.val_mat4x4));\n\n   // Make sure all the variables are used.\n   return val;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n   output.final_value = f16_function(2h);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/f64.toml",
    "content": "capabilities = \"FLOAT64\"\ntargets = \"SPIRV | GLSL | HLSL | WGSL\"\n\n[glsl]\nversion.Desktop = 420\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/f64.wgsl",
    "content": "var<private> v: f64 = 1lf;\nconst k: f64 = 2.0lf;\n\nfn f(x: f64) -> f64 {\n   _ = v;\n   let y: f64 = 3e1lf + 4.0e2lf;\n   var z = y + f64(5);\n   var w = -1.0lf;\n   return x + y + k + 5.0lf;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n   f(6.0lf);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/force_point_size_vertex_shader_webgl.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = true, version = 300 }\nwriter_flags = \"FORCE_POINT_SIZE\"\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/force_point_size_vertex_shader_webgl.wgsl",
    "content": "// AUTHOR: REASY\n// ISSUE: https://github.com/gfx-rs/wgpu/issues/3179\n// FIX: https://github.com/gfx-rs/wgpu/pull/3440\n@vertex\nfn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {\n    let x = f32(i32(in_vertex_index) - 1);\n    let y = f32(i32(in_vertex_index & 1u) * 2 - 1);\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/fragment-output.wgsl",
    "content": "// Split up because some output languages limit number of locations to 8.\nstruct FragmentOutputVec4Vec3 {\n    @location(0) vec4f: vec4<f32>,\n    @location(1) vec4i: vec4<i32>,\n    @location(2) vec4u: vec4<u32>,\n    @location(3) vec3f: vec3<f32>,\n    @location(4) vec3i: vec3<i32>,\n    @location(5) vec3u: vec3<u32>,\n}\n@fragment\nfn main_vec4vec3() -> FragmentOutputVec4Vec3 {\n    var output: FragmentOutputVec4Vec3;\n    output.vec4f = vec4<f32>(0.0);\n    output.vec4i = vec4<i32>(0);\n    output.vec4u = vec4<u32>(0u);\n    output.vec3f = vec3<f32>(0.0);\n    output.vec3i = vec3<i32>(0);\n    output.vec3u = vec3<u32>(0u);\n    return output;\n}\n\nstruct FragmentOutputVec2Scalar {\n    @location(0) vec2f: vec2<f32>,\n    @location(1) vec2i: vec2<i32>,\n    @location(2) vec2u: vec2<u32>,\n    @location(3) scalarf: f32,\n    @location(4) scalari: i32,\n    @location(5) scalaru: u32,\n}\n\n@fragment\nfn main_vec2scalar() -> FragmentOutputVec2Scalar {\n    var output: FragmentOutputVec2Scalar;\n    output.vec2f = vec2<f32>(0.0);\n    output.vec2i = vec2<i32>(0);\n    output.vec2u = vec2<u32>(0u);\n    output.scalarf = 0.0;\n    output.scalari = 0;\n    output.scalaru = 0u;\n    return output;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-optimized-by-capability.toml",
    "content": "# Turn on optimizations for `dot4I8Packed` and `dot4U8Packed` by enabling the\n# required capabilities on a SPIR-V version where these capabilities are only\n# available via the extension \"SPV_KHR_integer_dot_product\".\n\ntargets = \"SPIRV\"\n\n[spv]\ncapabilities = [\"DotProduct\", \"DotProductInput4x8BitPacked\"]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-optimized-by-capability.wgsl",
    "content": "fn test_packed_integer_dot_product() -> u32 {\n    let a_5 = 1u;\n    let b_5 = 2u;\n    let c_5: i32 = dot4I8Packed(a_5, b_5);\n\n    let a_6 = 3u;\n    let b_6 = 4u;\n    let c_6: u32 = dot4U8Packed(a_6, b_6);\n\n    // test baking of arguments\n    let c_7: i32 = dot4I8Packed(5u + c_6, 6u + c_6);\n    let c_8: u32 = dot4U8Packed(7u + c_6, 8u + c_6);\n    return c_8;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    let c = test_packed_integer_dot_product();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-optimized-by-version.toml",
    "content": "# Turn on optimizations for `dot4I8Packed` and `dot4U8Packed` on SPIR-V, HLSL, and Metal\n# by using a language version / shader model that supports these (without any extensions).\n\ntargets = \"SPIRV | HLSL | METAL\"\n\n[spv]\n# We also need to provide the corresponding capabilities (which are part of SPIR-V >= 1.6).\ncapabilities = [\"DotProduct\", \"DotProductInput4x8BitPacked\"]\nversion = [1, 6]\n\n[hlsl]\nshader_model = \"V6_4\"\n\n[msl]\nlang_version = [2, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-optimized-by-version.wgsl",
    "content": "fn test_packed_integer_dot_product() -> u32 {\n    let a_5 = 1u;\n    let b_5 = 2u;\n    let c_5: i32 = dot4I8Packed(a_5, b_5);\n\n    let a_6 = 3u;\n    let b_6 = 4u;\n    let c_6: u32 = dot4U8Packed(a_6, b_6);\n\n    // test baking of arguments\n    let c_7: i32 = dot4I8Packed(5u + c_6, 6u + c_6);\n    let c_8: u32 = dot4U8Packed(7u + c_6, 8u + c_6);\n    return c_8;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    let c = test_packed_integer_dot_product();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-unoptimized.toml",
    "content": "# Explicitly turn off optimizations for `dot4I8Packed` and `dot4U8Packed`\n# on SPIRV, HLSL, and Metal.\n\ntargets = \"SPIRV | HLSL | METAL\"\n\n[spv]\n# Provide some unrelated capability because an empty list of capabilities would\n# get mapped to `None`, which would then be interpreted as \"all capabilities\n# are available\".\ncapabilities = [\"Matrix\"]\n\n[hlsl]\nshader_model = \"V6_3\"\n\n[msl]\nlang_version = [2, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-unoptimized.wgsl",
    "content": "fn test_packed_integer_dot_product() -> u32 {\n    let a_5 = 1u;\n    let b_5 = 2u;\n    let c_5: i32 = dot4I8Packed(a_5, b_5);\n\n    let a_6 = 3u;\n    let b_6 = 4u;\n    let c_6: u32 = dot4U8Packed(a_6, b_6);\n\n    // test baking of arguments\n    let c_7: i32 = dot4I8Packed(5u + c_6, 6u + c_6);\n    let c_8: u32 = dot4U8Packed(7u + c_6, 8u + c_6);\n    return c_8;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    let c = test_packed_integer_dot_product();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-webgl.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = false, version = 320 }\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions-webgl.wgsl",
    "content": "fn test_fma() -> vec2<f32> {\n    let a = vec2<f32>(2.0, 2.0);\n    let b = vec2<f32>(0.5, 0.5);\n    let c = vec2<f32>(0.5, 0.5);\n\n    return fma(a, b, c);\n}\n\n\n@fragment\nfn main() {\n    let a = test_fma();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/functions.wgsl",
    "content": "fn test_fma() -> vec2<f32> {\n    let a = vec2<f32>(2.0, 2.0);\n    let b = vec2<f32>(0.5, 0.5);\n    let c = vec2<f32>(0.5, 0.5);\n\n    // Hazard: HLSL needs a different intrinsic function for f32 and f64\n    // See: https://github.com/gfx-rs/naga/issues/1579\n    return fma(a, b, c);\n}\n\nfn test_integer_dot_product() -> i32 {\n    let a_2 = vec2<i32>(1);\n    let b_2 = vec2<i32>(1);\n    let c_2: i32 = dot(a_2, b_2);\n\n    let a_3 = vec3<u32>(1u);\n    let b_3 = vec3<u32>(1u);\n    let c_3: u32 = dot(a_3, b_3);\n\n    // test baking of arguments\n    let c_4: i32 = dot(vec4<i32>(4), vec4<i32>(2));\n    return c_4;\n}\n\nfn test_packed_integer_dot_product() -> u32 {\n    let a_5 = 1u;\n    let b_5 = 2u;\n    let c_5: i32 = dot4I8Packed(a_5, b_5);\n\n    let a_6 = 3u;\n    let b_6 = 4u;\n    let c_6: u32 = dot4U8Packed(a_6, b_6);\n\n    // test baking of arguments\n    let c_7: i32 = dot4I8Packed(5u + c_6, 6u + c_6);\n    let c_8: u32 = dot4U8Packed(7u + c_6, 8u + c_6);\n    return c_8;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    let a = test_fma();\n    let b = test_integer_dot_product();\n    let c = test_packed_integer_dot_product();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/globals.wgsl",
    "content": "// Global variable & constant declarations\n\nconst Foo: bool = true;\n\nvar<workgroup> wg : array<f32, 10u>;\nvar<workgroup> at: atomic<u32>;\n\nstruct FooStruct {\n    v3: vec3<f32>,\n    // test packed vec3\n    v1: f32,\n}\n@group(0) @binding(1)\nvar<storage, read_write> alignment: FooStruct;\n\n@group(0) @binding(2)\nvar<storage> dummy: array<vec2<f32>>;\n\n@group(0) @binding(3)\nvar<uniform> float_vecs: array<vec4<f32>, 20>;\n\n@group(0) @binding(4)\nvar<uniform> global_vec: vec3<f32>;\n\n@group(0) @binding(5)\nvar<uniform> global_mat: mat3x2<f32>;\n\n@group(0) @binding(6)\nvar<uniform> global_nested_arrays_of_matrices_2x4: array<array<mat2x4<f32>, 2>, 2>;\n\n@group(0) @binding(7)\nvar<uniform> global_nested_arrays_of_matrices_4x2: array<array<mat4x2<f32>, 2>, 2>;\n\nfn test_msl_packed_vec3_as_arg(arg: vec3<f32>) {}\n\nfn test_msl_packed_vec3() {\n    // stores\n    alignment.v3 = vec3<f32>(1.0);\n    var idx = 1;\n    alignment.v3.x = 1.0;\n    alignment.v3[0] = 2.0;\n    alignment.v3[idx] = 3.0;\n\n    // force load to happen here\n    let data = alignment;\n\n    // loads\n    let l0 = data.v3;\n    let l1 = data.v3.zx;\n    test_msl_packed_vec3_as_arg(data.v3);\n\n    // matrix vector multiplication\n    let mvm0 = data.v3 * mat3x3<f32>();\n    let mvm1 = mat3x3<f32>() * data.v3;\n\n    // scalar vector multiplication\n    let svm0 = data.v3 * 2.0;\n    let svm1 = 2.0 * data.v3;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    test_msl_packed_vec3();\n\n    wg[7] = (global_nested_arrays_of_matrices_4x2[0][0] * global_nested_arrays_of_matrices_2x4[0][0][0]).x;\n    wg[6] = (global_mat * global_vec).x;\n    wg[5] = dummy[1].y;\n    wg[4] = float_vecs[0].w;\n    wg[3] = alignment.v1;\n    wg[2] = alignment.v3.x;\n    alignment.v1 = 4.0;\n    wg[1] = f32(arrayLength(&dummy));\n    atomicStore(&at, 2u);\n\n    // Valid, Foo and at is in function scope\n    var Foo: f32 = 1.0;\n    var at: bool = true;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/hlsl-keyword.toml",
    "content": "targets = \"HLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/hlsl-keyword.wgsl",
    "content": "@fragment\nfn fs_main() -> @location(0) vec4f {\n    // Make sure case-insensitive keywords are escaped in HLSL.\n    var Pass = vec4(1.0,1.0,1.0,1.0);\n    return Pass;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/image.toml",
    "content": "glsl_exclude_list = [\"depth_load\", \"depth_no_comparison\"]\ntargets = \"SPIRV | METAL | HLSL | WGSL | GLSL\"\n\n[glsl]\nversion.Desktop = 430\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/image.wgsl",
    "content": "@group(0) @binding(0)\nvar image_mipmapped_src: texture_2d<u32>;\n@group(0) @binding(3)\nvar image_multisampled_src: texture_multisampled_2d<u32>;\n@group(0) @binding(4)\nvar image_depth_multisampled_src: texture_depth_multisampled_2d;\n@group(0) @binding(1)\nvar image_storage_src: texture_storage_2d<rgba8uint, read>;\n@group(0) @binding(5)\nvar image_array_src: texture_2d_array<u32>;\n@group(0) @binding(6)\nvar image_dup_src: texture_storage_1d<r32uint,read>; // for #1307\n@group(0) @binding(7)\nvar image_1d_src: texture_1d<u32>;\n@group(0) @binding(2)\nvar image_dst: texture_storage_1d<r32uint,write>;\n\n@compute @workgroup_size(16)\nfn main(@builtin(local_invocation_id) local_id: vec3<u32>) {\n    let dim = textureDimensions(image_storage_src);\n    let itc = vec2<i32>(dim * local_id.xy) % vec2<i32>(10, 20);\n    // loads with ivec2 coords.\n    let value1 = textureLoad(image_mipmapped_src, itc, i32(local_id.z));\n    // doing the same thing as the line above, but with u32, as textureLoad must also support unsigned integers.\n    let value1_2 = textureLoad(image_mipmapped_src, itc, u32(local_id.z));\n    let value2 = textureLoad(image_multisampled_src, itc, i32(local_id.z));\n    let value3 = textureLoad(image_multisampled_src, itc, u32(local_id.z));\n    let value4 = textureLoad(image_storage_src, itc);\n    let value5 = textureLoad(image_array_src, itc, local_id.z, i32(local_id.z) + 1);\n    let value6 = textureLoad(image_array_src, itc, i32(local_id.z), i32(local_id.z) + 1);\n    let value7 = textureLoad(image_1d_src, i32(local_id.x), i32(local_id.z));\n    let value8 = textureLoad(image_dup_src, i32(local_id.x));\n    // loads with uvec2 coords.\n    let value1u = textureLoad(image_mipmapped_src, vec2<u32>(itc), i32(local_id.z));\n    let value2u = textureLoad(image_multisampled_src, vec2<u32>(itc), i32(local_id.z));\n    let value3u = textureLoad(image_multisampled_src, vec2<u32>(itc), u32(local_id.z));\n    let value4u = textureLoad(image_storage_src, vec2<u32>(itc));\n    let value5u = textureLoad(image_array_src, vec2<u32>(itc), local_id.z, i32(local_id.z) + 1);\n    let value6u = textureLoad(image_array_src, vec2<u32>(itc), i32(local_id.z), i32(local_id.z) + 1);\n    let value7u = textureLoad(image_1d_src, u32(local_id.x), i32(local_id.z));\n    // store with ivec2 coords.\n    textureStore(image_dst, itc.x, value1 + value2 + value4 + value5 + value6);\n    // store with uvec2 coords.\n    textureStore(image_dst, u32(itc.x), value1u + value2u + value4u + value5u + value6u);\n}\n\n@compute @workgroup_size(16, 1, 1)\nfn depth_load(@builtin(local_invocation_id) local_id: vec3<u32>) {\n    let dim: vec2<u32> = textureDimensions(image_storage_src);\n    let itc: vec2<i32> = (vec2<i32>(dim * local_id.xy) % vec2<i32>(10, 20));\n    let val: f32 = textureLoad(image_depth_multisampled_src, itc, i32(local_id.z));\n    textureStore(image_dst, itc.x, vec4<u32>(u32(val)));\n    return;\n}\n\n@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n@group(0) @binding(2)\nvar image_2d_u32: texture_2d<u32>;\n@group(0) @binding(3)\nvar image_2d_i32: texture_2d<i32>;\n@group(0) @binding(4)\nvar image_2d_array: texture_2d_array<f32>;\n@group(0) @binding(5)\nvar image_cube: texture_cube<f32>;\n@group(0) @binding(6)\nvar image_cube_array: texture_cube_array<f32>;\n@group(0) @binding(7)\nvar image_3d: texture_3d<f32>;\n@group(0) @binding(8)\nvar image_aa: texture_multisampled_2d<f32>;\n\n@vertex\nfn queries() -> @builtin(position) vec4<f32> {\n    let dim_1d = textureDimensions(image_1d);\n    let dim_1d_lod = textureDimensions(image_1d, i32(dim_1d));\n    let dim_2d = textureDimensions(image_2d);\n    let dim_2d_lod = textureDimensions(image_2d, 1);\n    let dim_2d_array = textureDimensions(image_2d_array);\n    let dim_2d_array_lod = textureDimensions(image_2d_array, 1);\n    let dim_cube = textureDimensions(image_cube);\n    let dim_cube_lod = textureDimensions(image_cube, 1);\n    let dim_cube_array = textureDimensions(image_cube_array);\n    let dim_cube_array_lod = textureDimensions(image_cube_array, 1);\n    let dim_3d = textureDimensions(image_3d);\n    let dim_3d_lod = textureDimensions(image_3d, 1);\n    let dim_2s_ms = textureDimensions(image_aa);\n\n    let sum = dim_1d + dim_2d.y + dim_2d_lod.y + dim_2d_array.y + dim_2d_array_lod.y + \n        dim_cube.y + dim_cube_lod.y + dim_cube_array.y + dim_cube_array_lod.y +\n        dim_3d.z + dim_3d_lod.z;\n    return vec4<f32>(f32(sum));\n}\n\n@vertex\nfn levels_queries() -> @builtin(position) vec4<f32> {\n    let num_levels_2d = textureNumLevels(image_2d);\n    let num_layers_2d = textureNumLayers(image_2d_array);\n    let num_levels_2d_array = textureNumLevels(image_2d_array);\n    let num_layers_2d_array = textureNumLayers(image_2d_array);\n    let num_levels_cube = textureNumLevels(image_cube);\n    let num_levels_cube_array = textureNumLevels(image_cube_array);\n    let num_layers_cube = textureNumLayers(image_cube_array);\n    let num_levels_3d = textureNumLevels(image_3d);\n    let num_samples_aa = textureNumSamples(image_aa);\n\n    let sum = num_layers_2d + num_layers_cube + num_samples_aa +\n        num_levels_2d + num_levels_2d_array + num_levels_3d + num_levels_cube + num_levels_cube_array;\n    return vec4<f32>(f32(sum));\n}\n\n@group(1) @binding(0)\nvar sampler_reg: sampler;\n\n@fragment\nfn texture_sample() -> @location(0) vec4<f32> {\n    const tc = vec2<f32>(0.5);\n    const tc3 = vec3<f32>(0.5);\n    const offset = vec2<i32>(3, 1);\n    let level = 2.3;\n    var a: vec4<f32>;\n    a += textureSample(image_1d, sampler_reg, tc.x);\n    a += textureSample(image_2d, sampler_reg, tc);\n    a += textureSample(image_2d, sampler_reg, tc, vec2<i32>(3, 1));\n    a += textureSampleLevel(image_2d, sampler_reg, tc, level);\n    a += textureSampleLevel(image_2d, sampler_reg, tc, level, offset);\n    a += textureSampleBias(image_2d, sampler_reg, tc, 2.0, offset);\n    a += textureSampleBaseClampToEdge(image_2d, sampler_reg, tc);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0u);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0u, offset);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0u, level);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0u, level, offset);\n    a += textureSampleBias(image_2d_array, sampler_reg, tc, 0u, 2.0, offset);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0, offset);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0, level);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0, level, offset);\n    a += textureSampleBias(image_2d_array, sampler_reg, tc, 0, 2.0, offset);\n    a += textureSample(image_cube_array, sampler_reg, tc3, 0u);\n    a += textureSampleLevel(image_cube_array, sampler_reg, tc3, 0u, level);\n    a += textureSampleBias(image_cube_array, sampler_reg, tc3, 0u, 2.0);\n    a += textureSample(image_cube_array, sampler_reg, tc3, 0);\n    a += textureSampleLevel(image_cube_array, sampler_reg, tc3, 0, level);\n    a += textureSampleBias(image_cube_array, sampler_reg, tc3, 0, 2.0);\n    return a;\n}\n\n@group(1) @binding(1)\nvar sampler_cmp: sampler_comparison;\n@group(1) @binding(2)\nvar image_2d_depth: texture_depth_2d;\n@group(1) @binding(3)\nvar image_2d_array_depth: texture_depth_2d_array;\n@group(1) @binding(4)\nvar image_cube_depth: texture_depth_cube;\n\n@fragment\nfn texture_sample_comparison() -> @location(0) f32 {\n    let tc = vec2<f32>(0.5);\n    let tc3 = vec3<f32>(0.5);\n    let dref = 0.5;\n    var a: f32;\n    a += textureSampleCompare(image_2d_depth, sampler_cmp, tc, dref);\n    a += textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0u, dref);\n    a += textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0, dref);\n    a += textureSampleCompare(image_cube_depth, sampler_cmp, tc3, dref);\n    a += textureSampleCompareLevel(image_2d_depth, sampler_cmp, tc, dref);\n    a += textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0u, dref);\n    a += textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0, dref);\n    a += textureSampleCompareLevel(image_cube_depth, sampler_cmp, tc3, dref);\n    return a;\n}\n\n@fragment\nfn gather() -> @location(0) vec4<f32> {\n    let tc = vec2<f32>(0.5);\n    let dref = 0.5;\n    let s2d = textureGather(1, image_2d, sampler_reg, tc);\n    let s2d_offset = textureGather(3, image_2d, sampler_reg, tc, vec2<i32>(3, 1));\n    let s2d_depth = textureGatherCompare(image_2d_depth, sampler_cmp, tc, dref);\n    let s2d_depth_offset = textureGatherCompare(image_2d_depth, sampler_cmp, tc, dref, vec2<i32>(3, 1));\n\n    let u = textureGather(0, image_2d_u32, sampler_reg, tc);\n    let i = textureGather(0, image_2d_i32, sampler_reg, tc);\n    let f = vec4<f32>(u) + vec4<f32>(i);\n\n    return s2d + s2d_offset + s2d_depth + s2d_depth_offset + f;\n}\n\n@fragment\nfn depth_no_comparison() -> @location(0) vec4<f32> {\n    let tc = vec2<f32>(0.5);\n    let level = 1;\n    let s2d = textureSample(image_2d_depth, sampler_reg, tc);\n    let s2d_gather = textureGather(image_2d_depth, sampler_reg, tc);\n    let s2d_level = textureSampleLevel(image_2d_depth, sampler_reg, tc, level);\n    return s2d + s2d_gather + s2d_level;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/index-by-value.toml",
    "content": "targets = \"SPIRV | IR\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/index-by-value.wgsl",
    "content": "fn index_arg_array(a: array<i32, 5>, i: i32) -> i32 {\n    return a[i];\n}\n\nfn index_let_array(i: i32, j: i32) -> i32 {\n  let a = array<array<i32, 2>, 2>(array(1, 2), array(3, 4));\n  return a[i][j];\n}\n\nfn index_let_matrix(i: i32, j: i32) -> f32 {\n  let a = mat2x2<f32>(1, 2, 3, 4);\n  return a[i][j];\n}\n\nfn index_let_array_1d(vi: u32) -> vec4<f32> {\n\tlet arr = array<i32, 5>(1, 2, 3, 4, 5);\n\tlet value = arr[vi];\n\treturn vec4<f32>(vec4<i32>(value));\n}\n\n@vertex\nfn main(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n    index_arg_array(array(1, 2, 3, 4, 5), 6);\n    index_let_array(1, 2);\n    index_let_matrix(1, 2);\n    return index_let_array_1d(vi);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/int64.toml",
    "content": "capabilities = \"SHADER_INT64\"\ntargets = \"SPIRV | HLSL | WGSL | METAL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 3]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_0\"\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/int64.wgsl",
    "content": "var<private> private_variable: i64 = 1li;\nconst constant_variable: u64 = 20lu;\n\nstruct UniformCompatible {\n   // Other types\n   val_u32: u32,\n   val_i32: i32,\n   val_f32: f32,\n\n   // u64\n   val_u64: u64,\n   val_u64_2: vec2<u64>,\n   val_u64_3: vec3<u64>,\n   val_u64_4: vec4<u64>,\n\n   // i64\n   val_i64: i64,\n   val_i64_2: vec2<i64>,\n   val_i64_3: vec3<i64>,\n   val_i64_4: vec4<i64>,\n\n   final_value: u64,\n}\n\nstruct StorageCompatible {\n   val_u64_array_2: array<u64, 2>,\n   val_i64_array_2: array<i64, 2>,\n}\n\n@group(0) @binding(0)\nvar<uniform> input_uniform: UniformCompatible;\n@group(0) @binding(1)\nvar<storage> input_storage: UniformCompatible;\n@group(0) @binding(2)\nvar<storage> input_arrays: StorageCompatible;\n@group(0) @binding(3)\nvar<storage, read_write> output: UniformCompatible;\n@group(0) @binding(4)\nvar<storage, read_write> output_arrays: StorageCompatible;\n\nfn int64_function(x: i64) -> i64 {\n   _ = private_variable;\n   var val: i64 = i64(constant_variable);\n   // A number too big for i32\n   val += 31li - 1002003004005006li + -0x7fffffffffffffffli;\n   // Constructing an i64 from an AbstractInt\n   val += val + i64(5);\n   // Constructing a i64 from other types and other types from u64.\n   val += i64(input_uniform.val_u32 + u32(val));\n   val += i64(input_uniform.val_i32 + i32(val));\n   val += i64(input_uniform.val_f32 + f32(val));\n   // Constructing a vec3<i64> from a i64\n   val += vec3<i64>(input_uniform.val_i64).z;\n   // Bitcasting from u64 to i64\n   val += bitcast<i64>(input_uniform.val_u64);\n   val += bitcast<vec2<i64>>(input_uniform.val_u64_2).y;\n   val += bitcast<vec3<i64>>(input_uniform.val_u64_3).z;\n   val += bitcast<vec4<i64>>(input_uniform.val_u64_4).w;\n   // Most negative i64\n   val += i64(-9223372036854775807 - 1);\n\n   // Reading/writing to a uniform/storage buffer\n   output.val_i64 = input_uniform.val_i64 + input_storage.val_i64;\n   output.val_i64_2 = input_uniform.val_i64_2 + input_storage.val_i64_2;\n   output.val_i64_3 = input_uniform.val_i64_3 + input_storage.val_i64_3;\n   output.val_i64_4 = input_uniform.val_i64_4 + input_storage.val_i64_4;\n\n   output_arrays.val_i64_array_2 = input_arrays.val_i64_array_2;\n\n   // We make sure not to use 32 in these arguments, so it's clear in the results which are builtin\n   // constants based on the size of the type, and which are arguments.\n\n   // Numeric functions\n   val += abs(val);\n   val += clamp(val, val, val);\n   //val += countLeadingZeros(val);\n   //val += countOneBits(val);\n   //val += countTrailingZeros(val);\n   val += dot(vec2(val), vec2(val));\n   //val += extractBits(val, 15u, 28u);\n   //val += firstLeadingBit(val);\n   //val += firstTrailingBit(val);\n   //val += insertBits(val, 12li, 15u, 28u);\n   val += max(val, val);\n   val += min(val, val);\n   //val += reverseBits(val);\n   val += sign(val); // only for i64\n\n   // Make sure all the variables are used.\n   return val;\n}\n\nfn uint64_function(x: u64) -> u64 {\n   var val: u64 = u64(constant_variable);\n   // Numbers too big for u32 (u64::MAX)\n   val += 31lu + 18446744073709551615lu - 0xfffffffffffffffflu;\n   // Constructing a u64 from an AbstractInt\n   val += val + u64(5);\n   // Constructing a u64 from other types and other types from u64.\n   val += u64(input_uniform.val_u32 + u32(val));\n   val += u64(input_uniform.val_i32 + i32(val));\n   val += u64(input_uniform.val_f32 + f32(val));\n   // Constructing a vec3<u64> from a u64\n   val += vec3<u64>(input_uniform.val_u64).z;\n   // Bitcasting from i64 to u64\n   val += bitcast<u64>(input_uniform.val_i64);\n   val += bitcast<vec2<u64>>(input_uniform.val_i64_2).y;\n   val += bitcast<vec3<u64>>(input_uniform.val_i64_3).z;\n   val += bitcast<vec4<u64>>(input_uniform.val_i64_4).w;\n\n   // Reading/writing to a uniform/storage buffer\n   output.val_u64 = input_uniform.val_u64 + input_storage.val_u64;\n   output.val_u64_2 = input_uniform.val_u64_2 + input_storage.val_u64_2;\n   output.val_u64_3 = input_uniform.val_u64_3 + input_storage.val_u64_3;\n   output.val_u64_4 = input_uniform.val_u64_4 + input_storage.val_u64_4;\n\n   output_arrays.val_u64_array_2 = input_arrays.val_u64_array_2;\n\n   // We make sure not to use 32 in these arguments, so it's clear in the results which are builtin\n   // constants based on the size of the type, and which are arguments.\n\n   // Numeric functions\n   val += abs(val);\n   val += clamp(val, val, val);\n   //val += countLeadingZeros(val);\n   //val += countOneBits(val);\n   //val += countTrailingZeros(val);\n   val += dot(vec2(val), vec2(val));\n   //val += extractBits(val, 15u, 28u);\n   //val += firstLeadingBit(val);\n   //val += firstTrailingBit(val);\n   //val += insertBits(val, 12lu, 15u, 28u);\n   val += max(val, val);\n   val += min(val, val);\n   //val += reverseBits(val);\n\n   // Make sure all the variables are used.\n   return val;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n   output.final_value = uint64_function(67lu) + bitcast<u64>(int64_function(60li));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/interface.toml",
    "content": "msl_pipeline = { allow_and_force_point_size = true, vertex_buffer_mappings = [\n], vertex_pulling_transform = false }\ntargets = \"SPIRV | METAL | HLSL | WGSL\"\nwgsl = { explicit_types = true }\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 1]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nfake_missing_bindings = false\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[spv]\nadjust_coordinate_space = false\nclamp_frag_depth = true\nforce_point_size = true\nseparate_entry_points = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/interface.wgsl",
    "content": "// Testing various parts of the pipeline interface: locations, built-ins, and entry points\n\nstruct VertexOutput {\n    @builtin(position) @invariant position: vec4<f32>,\n    @location(1) _varying: f32,\n}\n\n@vertex\nfn vertex(\n    @builtin(vertex_index) vertex_index: u32,\n    @builtin(instance_index) instance_index: u32,\n    @location(10) color: u32,\n) -> VertexOutput {\n    let tmp = vertex_index + instance_index + color;\n    return VertexOutput(vec4<f32>(1.0), f32(tmp));\n}\n\nstruct FragmentOutput {\n    @builtin(frag_depth) depth: f32,\n    @builtin(sample_mask) sample_mask: u32,\n    @location(0) color: f32,\n}\n\n@fragment\nfn fragment(\n    in: VertexOutput,\n    @builtin(front_facing) front_facing: bool,\n    @builtin(sample_index) sample_index: u32,\n    @builtin(sample_mask) sample_mask: u32,\n) -> FragmentOutput {\n    let mask = sample_mask & (1u << sample_index);\n    let color = select(0.0, 1.0, front_facing);\n    return FragmentOutput(in._varying, mask, color);\n}\n\nvar<workgroup> output: array<u32, 1>;\n\n@compute @workgroup_size(1)\nfn compute(\n    @builtin(global_invocation_id) global_id: vec3<u32>,\n    @builtin(local_invocation_id) local_id: vec3<u32>,\n    @builtin(local_invocation_index) local_index: u32,\n    @builtin(workgroup_id) wg_id: vec3<u32>,\n    @builtin(num_workgroups) num_wgs: vec3<u32>,\n) {\n    output[0] = global_id.x + local_id.x + local_index + wg_id.x + num_wgs.x;\n}\n\nstruct Input1 {\n    @builtin(vertex_index) index: u32,\n}\n\nstruct Input2 {\n    @builtin(instance_index) index: u32,\n}\n\n@vertex\nfn vertex_two_structs(in1: Input1, in2: Input2) -> @builtin(position) @invariant vec4<f32> {\n    var index = 2u;\n    return vec4<f32>(f32(in1.index), f32(in2.index), f32(index), 0.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/interpolate.toml",
    "content": "targets = \"SPIRV | METAL | HLSL | WGSL\"\n\n[glsl]\nversion.Desktop = 400\nzero_initialize_workgroup_memory = true\n\n[spv]\nadjust_coordinate_space = true\ndebug = true\nforce_point_size = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/interpolate.wgsl",
    "content": "//TODO: merge with \"interface\"?\n\n// NOTE: invalid combinations are tested in the\n// `validation::incompatible_interpolation_and_sampling_types` test.\nstruct FragmentInput {\n  @builtin(position) position: vec4<f32>,\n  @location(0) @interpolate(flat) _flat : u32,\n  @location(1) @interpolate(flat, first) flat_first : u32,\n  @location(2) @interpolate(flat, either) flat_either : u32,\n  @location(3) @interpolate(linear) _linear : f32,\n  @location(4) @interpolate(linear, centroid) linear_centroid : vec2<f32>,\n  @location(6) @interpolate(linear, sample) linear_sample : vec3<f32>,\n  @location(7) @interpolate(linear, center) linear_center : vec3<f32>,\n  @location(8) @interpolate(perspective) perspective : vec4<f32>,\n  @location(9) @interpolate(perspective, centroid) perspective_centroid : f32,\n  @location(10) @interpolate(perspective, sample) perspective_sample : f32,\n  @location(11) @interpolate(perspective, center) perspective_center : f32,\n}\n\n@vertex\nfn vert_main() -> FragmentInput {\n   var out: FragmentInput;\n\n   out.position = vec4<f32>(2.0, 4.0, 5.0, 6.0);\n   out._flat = 8u;\n   out.flat_first = 9u;\n   out.flat_either = 10u;\n   out._linear = 27.0;\n   out.linear_centroid = vec2<f32>(64.0, 125.0);\n   out.linear_sample = vec3<f32>(216.0, 343.0, 512.0);\n   out.linear_center = vec3<f32>(255.0, 511.0, 1024.0);\n   out.perspective = vec4<f32>(729.0, 1000.0, 1331.0, 1728.0);\n   out.perspective_centroid = 2197.0;\n   out.perspective_sample = 2744.0;\n   out.perspective_center = 2812.0;\n\n   return out;\n}\n\n@fragment\nfn frag_main(val : FragmentInput) { }\n"
  },
  {
    "path": "naga/tests/in/wgsl/interpolate_compat.toml",
    "content": "[glsl]\nversion.Desktop = 400\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n\n[spv]\nadjust_coordinate_space = true\ndebug = true\nforce_point_size = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/interpolate_compat.wgsl",
    "content": "// NOTE: This is basically the same as `interpolate.wgsl`, except for the removal of\n// `@interpolate(flat, first)`, which is unsupported in GLSL and `compat`.\n\n// NOTE: invalid combinations are tested in the\n// `validation::incompatible_interpolation_and_sampling_types` test.\nstruct FragmentInput {\n  @builtin(position) position: vec4<f32>,\n  @location(0) @interpolate(flat) _flat : u32,\n  // NOTE: not supported in `compat` or GLSL\n  // // @location(1) @interpolate(flat, first) flat_first : u32,\n  @location(2) @interpolate(flat, either) flat_either : u32,\n  @location(3) @interpolate(linear) _linear : f32,\n  @location(4) @interpolate(linear, centroid) linear_centroid : vec2<f32>,\n  @location(6) @interpolate(linear, sample) linear_sample : vec3<f32>,\n  @location(7) @interpolate(linear, center) linear_center : vec3<f32>,\n  @location(8) @interpolate(perspective) perspective : vec4<f32>,\n  @location(9) @interpolate(perspective, centroid) perspective_centroid : f32,\n  @location(10) @interpolate(perspective, sample) perspective_sample : f32,\n  @location(11) @interpolate(perspective, center) perspective_center : f32,\n}\n\n@vertex\nfn vert_main() -> FragmentInput {\n   var out: FragmentInput;\n\n   out.position = vec4<f32>(2.0, 4.0, 5.0, 6.0);\n   out._flat = 8u;\n   // out.flat_first = 9u;\n   out.flat_either = 10u;\n   out._linear = 27.0;\n   out.linear_centroid = vec2<f32>(64.0, 125.0);\n   out.linear_sample = vec3<f32>(216.0, 343.0, 512.0);\n   out.linear_center = vec3<f32>(255.0, 511.0, 1024.0);\n   out.perspective = vec4<f32>(729.0, 1000.0, 1331.0, 1728.0);\n   out.perspective_centroid = 2197.0;\n   out.perspective_sample = 2744.0;\n   out.perspective_center = 2812.0;\n\n   return out;\n}\n\n@fragment\nfn frag_main(val : FragmentInput) { }\n"
  },
  {
    "path": "naga/tests/in/wgsl/invariant.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = true, version = 300 }\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/invariant.wgsl",
    "content": "@vertex\nfn vs() -> @builtin(position) @invariant vec4<f32> {\n    return vec4<f32>(0.0);\n}\n\n@fragment\nfn fs(@builtin(position) @invariant position: vec4<f32>) { }\n"
  },
  {
    "path": "naga/tests/in/wgsl/lexical-scopes.toml",
    "content": "targets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/lexical-scopes.wgsl",
    "content": "fn blockLexicalScope(a: bool) {\n    {\n        let a = 2;\n        {\n            let a = 2.0;\n        }\n        let test: i32 = a;\n    }\n    let test: bool = a;\n}\n\nfn ifLexicalScope(a: bool) {\n    if (a) {\n        let a = 2.0;\n    }\n    let test: bool = a;\n}\n\n\nfn loopLexicalScope(a: bool) {\n    loop {\n        let a = 2.0;\n    }\n    let test: bool = a;\n}\n\nfn forLexicalScope(a: f32) {\n    for (var a = 0; a < 1; a++) {\n        let a = true;\n    }\n    let test: f32 = a;\n}\n\nfn whileLexicalScope(a: i32) {\n    while (a > 2) {\n        let a = false;\n    }\n    let test: i32 = a;\n}\n\nfn switchLexicalScope(a: i32) {\n    switch (a) {\n        case 0 {\n            let a = false;\n        }\n        case 1 {\n            let a = 2.0;\n        }\n        default {\n            let a = true;\n        }\n    }\n    let test = a == 2;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    blockLexicalScope(false);\n    ifLexicalScope(true);\n    loopLexicalScope(false);\n    forLexicalScope(1.);\n    whileLexicalScope(1);\n    switchLexicalScope(1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/local-const.toml",
    "content": "targets = \"IR | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/local-const.wgsl",
    "content": "const ga = 4;                  // AbstractInt with a value of 4.\nconst gb : i32 = 4;            // i32 with a value of 4.\nconst gc : u32 = 4;            // u32 with a value of 4.\nconst gd : f32 = 4;            // f32 with a value of 4.\nconst ge = vec3(ga, ga, ga);   // vec3 of AbstractInt with a value of (4, 4, 4).\nconst gf = 2.0;                // AbstractFloat with a value of 2.\n\nfn const_in_fn() {\n    const a = 4;                // AbstractInt with a value of 4.\n    const b: i32 = 4;           // i32 with a value of 4.\n    const c: u32 = 4;           // u32 with a value of 4.\n    const d: f32 = 4;           // f32 with a value of 4.\n    const e = vec3(a, a, a);    // vec3 of AbstractInt with a value of (4, 4, 4).\n    const f = 2.0;              // AbstractFloat with a value of 2.\n    // TODO: Make it per spec, currently not possible\n    // because naga does not support automatic conversions\n    // of Abstract types\n\n    // Check that we can access global constants\n    const ag = ga;\n    const bg = gb;\n    const cg = gc;\n    const dg = gd;\n    const eg = ge;\n    const fg = gf;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    const_in_fn();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mat_cx2.toml",
    "content": "targets = \"HLSL | SPIRV\"\n\n[spv]\ndebug = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/mat_cx2.wgsl",
    "content": "// Test handling of N-by-2 matrices.\n// See the doc comments on `naga::back::hlsl` and `naga::back::spv` for details.\n//\n// There are additional tests in `access.wgsl`.\n//\n// Tests that we don't apply this handling to other sizes are in mat_cx3.wgsl.\n\n// Access type (3rd item in variable names)\n// S = Struct\n// M = Matrix\n// C = Column\n// E = Element\n\n// Index type (4th item in variable names)\n// C = Constant\n// V = Variable\n\nalias Mat = mat2x2<f32>;\n\n@group(0) @binding(0)\nvar<storage, read_write> s_m: Mat;\n\n@group(0) @binding(1)\nvar<uniform> u_m: Mat;\n\nfn access_m() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_m = s_m;\n    let l_s_c_c = s_m[0];\n    let l_s_c_v = s_m[idx];\n    let l_s_e_cc = s_m[0][0];\n    let l_s_e_cv = s_m[0][idx];\n    let l_s_e_vc = s_m[idx][0];\n    let l_s_e_vv = s_m[idx][idx];\n\n    // loads from uniform\n    let l_u_m = u_m;\n    let l_u_c_c = u_m[0];\n    let l_u_c_v = u_m[idx];\n    let l_u_e_cc = u_m[0][0];\n    let l_u_e_cv = u_m[0][idx];\n    let l_u_e_vc = u_m[idx][0];\n    let l_u_e_vv = u_m[idx][idx];\n\n    // stores to storage\n    s_m = l_u_m;\n    s_m[0] = l_u_c_c;\n    s_m[idx] = l_u_c_v;\n    s_m[0][0] = l_u_e_cc;\n    s_m[0][idx] = l_u_e_cv;\n    s_m[idx][0] = l_u_e_vc;\n    s_m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithMat {\n    m: Mat,\n}\n\n@group(1) @binding(0)\nvar<storage, read_write> s_sm: StructWithMat;\n\n@group(1) @binding(1)\nvar<uniform> u_sm: StructWithMat;\n\nfn access_sm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sm;\n    let l_s_m = s_sm.m;\n    let l_s_c_c = s_sm.m[0];\n    let l_s_c_v = s_sm.m[idx];\n    let l_s_e_cc = s_sm.m[0][0];\n    let l_s_e_cv = s_sm.m[0][idx];\n    let l_s_e_vc = s_sm.m[idx][0];\n    let l_s_e_vv = s_sm.m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sm;\n    let l_u_m = u_sm.m;\n    let l_u_c_c = u_sm.m[0];\n    let l_u_c_v = u_sm.m[idx];\n    let l_u_e_cc = u_sm.m[0][0];\n    let l_u_e_cv = u_sm.m[0][idx];\n    let l_u_e_vc = u_sm.m[idx][0];\n    let l_u_e_vv = u_sm.m[idx][idx];\n\n    // stores to storage\n    s_sm = l_u_s;\n    s_sm.m = l_u_m;\n    s_sm.m[0] = l_u_c_c;\n    s_sm.m[idx] = l_u_c_v;\n    s_sm.m[0][0] = l_u_e_cc;\n    s_sm.m[0][idx] = l_u_e_cv;\n    s_sm.m[idx][0] = l_u_e_vc;\n    s_sm.m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithArrayOfStructOfMat {\n    a: array<StructWithMat, 4>,\n}\n\n@group(2) @binding(0)\nvar<storage, read_write> s_sasm: StructWithArrayOfStructOfMat;\n\n@group(2) @binding(1)\nvar<uniform> u_sasm: StructWithArrayOfStructOfMat;\n\nfn access_sasm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sasm;\n    let l_s_a = s_sasm.a;\n    let l_s_m_c = s_sasm.a[0].m;\n    let l_s_m_v = s_sasm.a[idx].m;\n    let l_s_c_cc = s_sasm.a[0].m[0];\n    let l_s_c_cv = s_sasm.a[0].m[idx];\n    let l_s_c_vc = s_sasm.a[idx].m[0];\n    let l_s_c_vv = s_sasm.a[idx].m[idx];\n    let l_s_e_ccc = s_sasm.a[0].m[0][0];\n    let l_s_e_ccv = s_sasm.a[0].m[0][idx];\n    let l_s_e_cvc = s_sasm.a[0].m[idx][0];\n    let l_s_e_cvv = s_sasm.a[0].m[idx][idx];\n    let l_s_e_vcc = s_sasm.a[idx].m[0][0];\n    let l_s_e_vcv = s_sasm.a[idx].m[0][idx];\n    let l_s_e_vvc = s_sasm.a[idx].m[idx][0];\n    let l_s_e_vvv = s_sasm.a[idx].m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sasm;\n    let l_u_a = u_sasm.a;\n    let l_u_m_c = u_sasm.a[0].m;\n    let l_u_m_v = u_sasm.a[idx].m;\n    let l_u_c_cc = u_sasm.a[0].m[0];\n    let l_u_c_cv = u_sasm.a[0].m[idx];\n    let l_u_c_vc = u_sasm.a[idx].m[0];\n    let l_u_c_vv = u_sasm.a[idx].m[idx];\n    let l_u_e_ccc = u_sasm.a[0].m[0][0];\n    let l_u_e_ccv = u_sasm.a[0].m[0][idx];\n    let l_u_e_cvc = u_sasm.a[0].m[idx][0];\n    let l_u_e_cvv = u_sasm.a[0].m[idx][idx];\n    let l_u_e_vcc = u_sasm.a[idx].m[0][0];\n    let l_u_e_vcv = u_sasm.a[idx].m[0][idx];\n    let l_u_e_vvc = u_sasm.a[idx].m[idx][0];\n    let l_u_e_vvv = u_sasm.a[idx].m[idx][idx];\n\n    // stores to storage\n    s_sasm = l_u_s;\n    s_sasm.a = l_u_a;\n    s_sasm.a[0].m = l_u_m_c;\n    s_sasm.a[idx].m = l_u_m_v;\n    s_sasm.a[0].m[0] = l_u_c_cc;\n    s_sasm.a[0].m[idx] = l_u_c_cv;\n    s_sasm.a[idx].m[0] = l_u_c_vc;\n    s_sasm.a[idx].m[idx] = l_u_c_vv;\n    s_sasm.a[0].m[0][0] = l_u_e_ccc;\n    s_sasm.a[0].m[0][idx] = l_u_e_ccv;\n    s_sasm.a[0].m[idx][0] = l_u_e_cvc;\n    s_sasm.a[0].m[idx][idx] = l_u_e_cvv;\n    s_sasm.a[idx].m[0][0] = l_u_e_vcc;\n    s_sasm.a[idx].m[0][idx] = l_u_e_vcv;\n    s_sasm.a[idx].m[idx][0] = l_u_e_vvc;\n    s_sasm.a[idx].m[idx][idx] = l_u_e_vvv;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    access_m();\n    access_sm();\n    access_sasm();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mat_cx3.toml",
    "content": "targets = \"HLSL | SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/mat_cx3.wgsl",
    "content": "// Test HLSL handling of N-by-3 matrices. These should not receive the special\n// treatment that N-by-2 matrices receive (which is tested in mat_cx2.wgsl).\n\n// Access type (3rd item in variable names)\n// S = Struct\n// M = Matrix\n// C = Column\n// E = Element\n\n// Index type (4th item in variable names)\n// C = Constant\n// V = Variable\n\nalias Mat = mat3x3<f32>;\n\n@group(0) @binding(0)\nvar<storage, read_write> s_m: Mat;\n\n@group(0) @binding(1)\nvar<uniform> u_m: Mat;\n\nfn access_m() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_m = s_m;\n    let l_s_c_c = s_m[0];\n    let l_s_c_v = s_m[idx];\n    let l_s_e_cc = s_m[0][0];\n    let l_s_e_cv = s_m[0][idx];\n    let l_s_e_vc = s_m[idx][0];\n    let l_s_e_vv = s_m[idx][idx];\n\n    // loads from uniform\n    let l_u_m = u_m;\n    let l_u_c_c = u_m[0];\n    let l_u_c_v = u_m[idx];\n    let l_u_e_cc = u_m[0][0];\n    let l_u_e_cv = u_m[0][idx];\n    let l_u_e_vc = u_m[idx][0];\n    let l_u_e_vv = u_m[idx][idx];\n\n    // stores to storage\n    s_m = l_u_m;\n    s_m[0] = l_u_c_c;\n    s_m[idx] = l_u_c_v;\n    s_m[0][0] = l_u_e_cc;\n    s_m[0][idx] = l_u_e_cv;\n    s_m[idx][0] = l_u_e_vc;\n    s_m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithMat {\n    m: Mat,\n}\n\n@group(1) @binding(0)\nvar<storage, read_write> s_sm: StructWithMat;\n\n@group(1) @binding(1)\nvar<uniform> u_sm: StructWithMat;\n\nfn access_sm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sm;\n    let l_s_m = s_sm.m;\n    let l_s_c_c = s_sm.m[0];\n    let l_s_c_v = s_sm.m[idx];\n    let l_s_e_cc = s_sm.m[0][0];\n    let l_s_e_cv = s_sm.m[0][idx];\n    let l_s_e_vc = s_sm.m[idx][0];\n    let l_s_e_vv = s_sm.m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sm;\n    let l_u_m = u_sm.m;\n    let l_u_c_c = u_sm.m[0];\n    let l_u_c_v = u_sm.m[idx];\n    let l_u_e_cc = u_sm.m[0][0];\n    let l_u_e_cv = u_sm.m[0][idx];\n    let l_u_e_vc = u_sm.m[idx][0];\n    let l_u_e_vv = u_sm.m[idx][idx];\n\n    // stores to storage\n    s_sm = l_u_s;\n    s_sm.m = l_u_m;\n    s_sm.m[0] = l_u_c_c;\n    s_sm.m[idx] = l_u_c_v;\n    s_sm.m[0][0] = l_u_e_cc;\n    s_sm.m[0][idx] = l_u_e_cv;\n    s_sm.m[idx][0] = l_u_e_vc;\n    s_sm.m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithArrayOfStructOfMat {\n    a: array<StructWithMat, 4>,\n}\n\n@group(2) @binding(0)\nvar<storage, read_write> s_sasm: StructWithArrayOfStructOfMat;\n\n@group(2) @binding(1)\nvar<uniform> u_sasm: StructWithArrayOfStructOfMat;\n\nfn access_sasm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sasm;\n    let l_s_a = s_sasm.a;\n    let l_s_m_c = s_sasm.a[0].m;\n    let l_s_m_v = s_sasm.a[idx].m;\n    let l_s_c_cc = s_sasm.a[0].m[0];\n    let l_s_c_cv = s_sasm.a[0].m[idx];\n    let l_s_c_vc = s_sasm.a[idx].m[0];\n    let l_s_c_vv = s_sasm.a[idx].m[idx];\n    let l_s_e_ccc = s_sasm.a[0].m[0][0];\n    let l_s_e_ccv = s_sasm.a[0].m[0][idx];\n    let l_s_e_cvc = s_sasm.a[0].m[idx][0];\n    let l_s_e_cvv = s_sasm.a[0].m[idx][idx];\n    let l_s_e_vcc = s_sasm.a[idx].m[0][0];\n    let l_s_e_vcv = s_sasm.a[idx].m[0][idx];\n    let l_s_e_vvc = s_sasm.a[idx].m[idx][0];\n    let l_s_e_vvv = s_sasm.a[idx].m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sasm;\n    let l_u_a = u_sasm.a;\n    let l_u_m_c = u_sasm.a[0].m;\n    let l_u_m_v = u_sasm.a[idx].m;\n    let l_u_c_cc = u_sasm.a[0].m[0];\n    let l_u_c_cv = u_sasm.a[0].m[idx];\n    let l_u_c_vc = u_sasm.a[idx].m[0];\n    let l_u_c_vv = u_sasm.a[idx].m[idx];\n    let l_u_e_ccc = u_sasm.a[0].m[0][0];\n    let l_u_e_ccv = u_sasm.a[0].m[0][idx];\n    let l_u_e_cvc = u_sasm.a[0].m[idx][0];\n    let l_u_e_cvv = u_sasm.a[0].m[idx][idx];\n    let l_u_e_vcc = u_sasm.a[idx].m[0][0];\n    let l_u_e_vcv = u_sasm.a[idx].m[0][idx];\n    let l_u_e_vvc = u_sasm.a[idx].m[idx][0];\n    let l_u_e_vvv = u_sasm.a[idx].m[idx][idx];\n\n    // stores to storage\n    s_sasm = l_u_s;\n    s_sasm.a = l_u_a;\n    s_sasm.a[0].m = l_u_m_c;\n    s_sasm.a[idx].m = l_u_m_v;\n    s_sasm.a[0].m[0] = l_u_c_cc;\n    s_sasm.a[0].m[idx] = l_u_c_cv;\n    s_sasm.a[idx].m[0] = l_u_c_vc;\n    s_sasm.a[idx].m[idx] = l_u_c_vv;\n    s_sasm.a[0].m[0][0] = l_u_e_ccc;\n    s_sasm.a[0].m[0][idx] = l_u_e_ccv;\n    s_sasm.a[0].m[idx][0] = l_u_e_cvc;\n    s_sasm.a[0].m[idx][idx] = l_u_e_cvv;\n    s_sasm.a[idx].m[0][0] = l_u_e_vcc;\n    s_sasm.a[idx].m[0][idx] = l_u_e_vcv;\n    s_sasm.a[idx].m[idx][0] = l_u_e_vvc;\n    s_sasm.a[idx].m[idx][idx] = l_u_e_vvv;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    access_m();\n    access_sm();\n    access_sasm();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/math-functions.toml",
    "content": "capabilities = \"SHADER_FLOAT16_IN_FLOAT32\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/math-functions.wgsl",
    "content": "@fragment\nfn main() {\n    let f = 1.0;\n    let v = vec4<f32>(0.0);\n    let a = degrees(f);\n    let b = radians(f);\n    let c = degrees(v);\n    let d = radians(v);\n    let e = saturate(v);\n    let g = refract(v, v, f);\n    let sign_a = sign(-1);\n    let sign_b = sign(vec4(-1));\n    let sign_c = sign(-1.0);\n    let sign_d = sign(vec4(-1.0));\n    let sign_e = sign(vec4(0.0));\n    let const_dot = dot(vec2<i32>(), vec2<i32>());\n    let first_leading_bit_abs = firstLeadingBit(abs(0u));\n    let flb_a = firstLeadingBit(-1);\n    let flb_b = firstLeadingBit(vec2(-1));\n    let flb_c = firstLeadingBit(vec2(1u));\n    let ftb_a = firstTrailingBit(-1);\n    let ftb_b = firstTrailingBit(1u);\n    let ftb_c = firstTrailingBit(vec2(-1));\n    let ftb_d = firstTrailingBit(vec2(1u));\n    let ctz_a = countTrailingZeros(0u);\n    let ctz_b = countTrailingZeros(0);\n    let ctz_c = countTrailingZeros(0xFFFFFFFFu);\n    let ctz_d = countTrailingZeros(-1);\n    let ctz_e = countTrailingZeros(vec2(0u));\n    let ctz_f = countTrailingZeros(vec2(0));\n    let ctz_g = countTrailingZeros(vec2(1u));\n    let ctz_h = countTrailingZeros(vec2(1));\n    let clz_a = countLeadingZeros(-1);\n    let clz_b = countLeadingZeros(1u);\n    let clz_c = countLeadingZeros(vec2(-1));\n    let clz_d = countLeadingZeros(vec2(1u));\n    let lde_a = ldexp(1.0, 2);\n    let lde_b = ldexp(vec2(1.0, 2.0), vec2(3, 4));\n    let modf_a = modf(1.5);\n    let modf_b = modf(1.5).fract;\n    let modf_c = modf(1.5).whole;\n    let modf_d = modf(vec2(1.5, 1.5));\n    let modf_e = modf(vec4(1.5, 1.5, 1.5, 1.5)).whole.x;\n    let modf_f: f32 = modf(vec2(1.5, 1.5)).fract.y;\n    let frexp_a = frexp(1.5);\n    let frexp_b = frexp(1.5).fract;\n    let frexp_c: i32 = frexp(1.5).exp;\n    let frexp_d: i32 = frexp(vec4(1.5, 1.5, 1.5, 1.5)).exp.x;\n    let quantizeToF16_a: f32 = quantizeToF16(1.0);\n    let quantizeToF16_b: vec2<f32> = quantizeToF16(vec2(1.0, 1.0));\n    let quantizeToF16_c: vec3<f32> = quantizeToF16(vec3(1.0, 1.0, 1.0));\n    let quantizeToF16_d: vec4<f32> = quantizeToF16(vec4(1.0, 1.0, 1.0, 1.0));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/memory-decorations-coherent.toml",
    "content": "capabilities = \"MEMORY_DECORATION_COHERENT\"\ntargets = \"SPIRV | HLSL | GLSL | WGSL | METAL\"\n\n[spv]\nversion = [1, 0]\n\n[glsl]\nversion = { Desktop = 430 }\n\n[msl]\nlang_version = [3, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/memory-decorations-coherent.wgsl",
    "content": "struct Data {\n    values: array<u32>,\n}\n\n@group(0) @binding(0) @coherent\nvar<storage, read_write> coherent_buf: Data;\n\n@group(0) @binding(1)\nvar<storage, read_write> plain_buf: Data;\n\n@compute @workgroup_size(1)\nfn main() {\n    coherent_buf.values[0] = plain_buf.values[0];\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/memory-decorations.toml",
    "content": "capabilities = \"MEMORY_DECORATION_COHERENT | MEMORY_DECORATION_VOLATILE\"\ntargets = \"SPIRV | GLSL | WGSL\"\n\n[spv]\nversion = [1, 0]\n\n[glsl]\nversion = { Desktop = 430 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/memory-decorations.wgsl",
    "content": "struct Data {\n    values: array<u32>,\n}\n\n@group(0) @binding(0) @coherent\nvar<storage, read_write> coherent_buf: Data;\n\n@group(0) @binding(1) @volatile\nvar<storage, read_write> volatile_buf: Data;\n\n@group(0) @binding(2) @coherent @volatile\nvar<storage, read_write> both_buf: Data;\n\n@group(0) @binding(3)\nvar<storage, read_write> plain_buf: Data;\n\n@compute @workgroup_size(1)\nfn main() {\n    coherent_buf.values[0] = volatile_buf.values[0];\n    both_buf.values[0] = plain_buf.values[0];\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-empty.toml",
    "content": "capabilities = \"MESH_SHADER\"\ntargets = \"WGSL | SPIRV\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"MeshShadingEXT\"]\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-empty.wgsl",
    "content": "// An empty WGSL shader to check that task payload/mesh output\n// are still properly written without being used in the shader\n\nenable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\n\n@task\n@payload(taskPayload)\n@workgroup_size(64)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3(1, 1, 1);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(64)\nfn ms_main() {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-lines.toml",
    "content": "capabilities = \"MESH_SHADER\"\ntargets = \"WGSL | SPIRV\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"MeshShadingEXT\"]\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-lines.wgsl",
    "content": "// An empty WGSL shader to check that task payload/mesh output\n// are still properly written without being used in the shader\n\nenable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(line_indices) indices: vec2<u32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\n\n@task\n@payload(taskPayload)\n@workgroup_size(64)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3(1, 1, 1);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 2>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(64)\nfn ms_main() {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-points.toml",
    "content": "capabilities = \"MESH_SHADER | MESH_SHADER_POINT_TOPOLOGY\"\ntargets = \"WGSL | SPIRV\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"MeshShadingEXT\"]\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader-points.wgsl",
    "content": "// An empty WGSL shader to check that task payload/mesh output\n// are still properly written without being used in the shader\n\nenable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(point_index) indices: u32,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\n\n@task\n@payload(taskPayload)\n@workgroup_size(64)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3(1, 1, 1);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 1>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(64)\nfn ms_main() {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader.toml",
    "content": "capabilities = \"MESH_SHADER\"\ntargets = \"WGSL | SPIRV\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"MeshShadingEXT\"]\n"
  },
  {
    "path": "naga/tests/in/wgsl/mesh-shader.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nconst positions = array(\n    vec4(0., 1., 0., 1.),\n    vec4(-1., -1., 0., 1.),\n    vec4(1., -1., 0., 1.)\n);\nconst colors = array(\n    vec4(0., 1., 0., 1.),\n    vec4(0., 0., 1., 1.),\n    vec4(1., 0., 0., 1.)\n);\n\nstruct TaskPayload {\n    colorMask: vec4<f32>,\n    visible: bool,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) color: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n    @builtin(cull_primitive) cull: bool,\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\nstruct PrimitiveInput {\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> workgroupData: f32;\n\nfn helper_reader() -> bool {\n    return taskPayload.visible;\n}\nfn helper_writer(value: bool) {\n    taskPayload.visible = value;\n}\n\n@task\n@payload(taskPayload)\n@workgroup_size(1)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    workgroupData = 1.0;\n    taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n    helper_writer(true);\n    taskPayload.visible = helper_reader();\n    return vec3(1, 1, 1);\n}\n\n// This tests if we can properly write a task shader that is divergent\n@task\n@payload(taskPayload)\n@workgroup_size(2)\nfn ts_divergent(@builtin(local_invocation_id) thread_id: vec3<u32>) -> @builtin(mesh_task_size) vec3<u32> {\n    if thread_id.x == 0 {\n        taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n        taskPayload.visible = true;\n        return vec3(1, 1, 1);\n    }\n    // Only the first thread's value is taken\n    return vec3(2,2,2);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(1)\nfn ms_main() {\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    workgroupData = 2.0;\n\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0] * taskPayload.colorMask;\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1] * taskPayload.colorMask;\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2] * taskPayload.colorMask;\n\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    mesh_output.primitives[0].cull = !helper_reader();\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n\n@mesh(mesh_output)\n@workgroup_size(1)\nfn ms_no_ts() {\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    workgroupData = 2.0;\n\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0];\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1];\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2];\n\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    mesh_output.primitives[0].cull = false;\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n\n// See ts_divergent comment\n@mesh(mesh_output)\n@workgroup_size(2)\nfn ms_divergent(@builtin(local_invocation_id) thread_id: vec3<u32>) {\n    if thread_id.x == 0 {\n        mesh_output.vertex_count = 3;\n        mesh_output.primitive_count = 1;\n        workgroupData = 2.0;\n\n        mesh_output.vertices[0].position = positions[0];\n        mesh_output.vertices[0].color = colors[0];\n\n        mesh_output.vertices[1].position = positions[1];\n        mesh_output.vertices[1].color = colors[1];\n\n        mesh_output.vertices[2].position = positions[2];\n        mesh_output.vertices[2].color = colors[2];\n\n        mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n        mesh_output.primitives[0].cull = false;\n        mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n        // \"Early\" return\n        return;\n    }\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput, primitive: PrimitiveInput) -> @location(0) vec4<f32> {\n    return vertex.color * primitive.colorMask;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/module-scope.toml",
    "content": "targets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/module-scope.wgsl",
    "content": "fn call() {\n    statement();\n    let x: S = returns();\n    let vf = f32(Value);\n    let s = textureSample(Texture, Sampler, Vec2(vf));\n}\n\nfn statement() {}\n\nfn returns() -> S {\n    return S(Value);\n}\n\nstruct S {\n    x: i32,\n}\n\nconst Value: i32 = 1;\n\n@group(0) @binding(0)\nvar Texture: texture_2d<f32>;\n\n@group(0) @binding(1)\nvar Sampler: sampler;\n\nalias Vec2 = vec2<f32>;\n\n@fragment\nfn main() {\n    call();\n    statement();\n    returns();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-varyings.toml",
    "content": "targets = \"METAL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-varyings.wgsl",
    "content": "struct Vertex {\n    @location(0) position: vec2f\n}\n\nstruct NoteInstance {\n    @location(1) position: vec2f\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4f\n}\n\n@vertex\nfn vs_main(vertex: Vertex, note: NoteInstance) -> VertexOutput {\n    var out: VertexOutput;\n    return out;\n}\n\n@fragment\nfn fs_main(in: VertexOutput, note: NoteInstance) -> @location(0) vec4f {\n    let position = vec3(1f);\n    return in.position;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x1.toml",
    "content": "targets = \"METAL\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[msl_pipeline]\nallow_and_force_point_size = false\nvertex_pulling_transform = true\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [\n    { offset = 0, shader_location = 0, format = \"Uint8\" },\n    { offset = 16, shader_location = 1, format = \"Uint8x2\" },\n    { offset = 32, shader_location = 2, format = \"Uint8x4\" },\n    { offset = 48, shader_location = 3, format = \"Sint8\" },\n    { offset = 64, shader_location = 4, format = \"Sint8x2\" },\n    { offset = 80, shader_location = 5, format = \"Sint8x4\" },\n    { offset = 96, shader_location = 6, format = \"Unorm8\" },\n    { offset = 112, shader_location = 7, format = \"Unorm8x2\" },\n    { offset = 128, shader_location = 8, format = \"Unorm8x4\" },\n    { offset = 144, shader_location = 9, format = \"Snorm8\" },\n    { offset = 160, shader_location = 10, format = \"Snorm8x2\" },\n    { offset = 176, shader_location = 11, format = \"Snorm8x4\" },\n    { offset = 192, shader_location = 12, format = \"Uint16\" },\n    { offset = 208, shader_location = 13, format = \"Uint16x2\" },\n    { offset = 224, shader_location = 14, format = \"Uint16x4\" },\n    { offset = 240, shader_location = 15, format = \"Sint16\" },\n    { offset = 256, shader_location = 16, format = \"Sint16x2\" },\n    { offset = 272, shader_location = 17, format = \"Sint16x4\" },\n    { offset = 288, shader_location = 18, format = \"Unorm16\" },\n    { offset = 304, shader_location = 19, format = \"Unorm16x2\" },\n    { offset = 320, shader_location = 20, format = \"Unorm16x4\" },\n    { offset = 336, shader_location = 21, format = \"Snorm16\" },\n    { offset = 352, shader_location = 22, format = \"Snorm16x2\" },\n    { offset = 368, shader_location = 23, format = \"Snorm16x4\" },\n    { offset = 384, shader_location = 24, format = \"Float16\" },\n    { offset = 400, shader_location = 25, format = \"Float16x2\" },\n    { offset = 416, shader_location = 26, format = \"Float16x4\" },\n    { offset = 432, shader_location = 27, format = \"Float32\" },\n    { offset = 448, shader_location = 28, format = \"Float32x2\" },\n    { offset = 464, shader_location = 29, format = \"Float32x3\" },\n    { offset = 480, shader_location = 30, format = \"Float32x4\" },\n    { offset = 496, shader_location = 31, format = \"Uint32\" },\n    { offset = 512, shader_location = 32, format = \"Uint32x2\" },\n    { offset = 528, shader_location = 33, format = \"Uint32x3\" },\n    { offset = 544, shader_location = 34, format = \"Uint32x4\" },\n    { offset = 560, shader_location = 35, format = \"Sint32\" },\n    { offset = 576, shader_location = 36, format = \"Sint32x2\" },\n    { offset = 592, shader_location = 37, format = \"Sint32x3\" },\n    { offset = 608, shader_location = 38, format = \"Sint32x4\" },\n    { offset = 624, shader_location = 39, format = \"unorm10-10-10-2\" },\n    { offset = 640, shader_location = 40, format = \"unorm8x4-bgra\" },\n    { offset = 656, shader_location = 41, format = \"Float16\" },\n    { offset = 672, shader_location = 42, format = \"Float16x2\" },\n    { offset = 688, shader_location = 43, format = \"Float16x4\" },\n]\nid = 1\nstep_mode = \"ByVertex\"\nstride = 704\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x1.wgsl",
    "content": "// Tests for the vertex pulling transform in the MSL backend.\n//\n// Test loading a scalar from each vertex format.\n//\n// (Note: only the wgsl files are different for each of the -formats-xN tests.\n// The toml files are all the same.)\n\nenable f16;\n\nstruct VertexOutput {\n  @builtin(position) position: vec4<f32>,\n}\n\nstruct VertexInput {\n  @location( 0) v_uint8     : u32,\n  @location( 1) v_uint8x2   : u32,\n  @location( 2) v_uint8x4   : u32,\n  @location( 3) v_sint8     : i32,\n  @location( 4) v_sint8x2   : i32,\n  @location( 5) v_sint8x4   : i32,\n  @location( 6) v_unorm8    : f32,\n  @location( 7) v_unorm8x2  : f32,\n  @location( 8) v_unorm8x4  : f32,\n  @location( 9) v_snorm8    : f32,\n  @location(10) v_snorm8x2  : f32,\n  @location(11) v_snorm8x4  : f32,\n  @location(12) v_uint16    : u32,\n  @location(13) v_uint16x2  : u32,\n  @location(14) v_uint16x4  : u32,\n  @location(15) v_sint16    : i32,\n  @location(16) v_sint16x2  : i32,\n  @location(17) v_sint16x4  : i32,\n  @location(18) v_unorm16   : f32,\n  @location(19) v_unorm16x2 : f32,\n  @location(20) v_unorm16x4 : f32,\n  @location(21) v_snorm16   : f32,\n  @location(22) v_snorm16x2 : f32,\n  @location(23) v_snorm16x4 : f32,\n  @location(24) v_float16   : f32,\n  @location(25) v_float16x2 : f32,\n  @location(26) v_float16x4 : f32,\n  @location(27) v_float32   : f32,\n  @location(28) v_float32x2 : f32,\n  @location(29) v_float32x3 : f32,\n  @location(30) v_float32x4 : f32,\n  @location(31) v_uint32    : u32,\n  @location(32) v_uint32x2  : u32,\n  @location(33) v_uint32x3  : u32,\n  @location(34) v_uint32x4  : u32,\n  @location(35) v_sint32    : i32,\n  @location(36) v_sint32x2  : i32,\n  @location(37) v_sint32x3  : i32,\n  @location(38) v_sint32x4  : i32,\n  @location(39) v_unorm10_10_10_2: f32,\n  @location(40) v_unorm8x4_bgra: f32,\n\n  @location(41) v_float16_as_f16   : f16,\n  @location(42) v_float16x2_as_f16 : f16,\n  @location(43) v_float16x4_as_f16 : f16,\n}\n\n@vertex\nfn render_vertex(\n  v_in: VertexInput,\n) -> VertexOutput\n{\n  return VertexOutput(vec4f(v_in.v_float32));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x2.toml",
    "content": "targets = \"METAL\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[msl_pipeline]\nallow_and_force_point_size = false\nvertex_pulling_transform = true\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [\n    { offset = 0, shader_location = 0, format = \"Uint8\" },\n    { offset = 16, shader_location = 1, format = \"Uint8x2\" },\n    { offset = 32, shader_location = 2, format = \"Uint8x4\" },\n    { offset = 48, shader_location = 3, format = \"Sint8\" },\n    { offset = 64, shader_location = 4, format = \"Sint8x2\" },\n    { offset = 80, shader_location = 5, format = \"Sint8x4\" },\n    { offset = 96, shader_location = 6, format = \"Unorm8\" },\n    { offset = 112, shader_location = 7, format = \"Unorm8x2\" },\n    { offset = 128, shader_location = 8, format = \"Unorm8x4\" },\n    { offset = 144, shader_location = 9, format = \"Snorm8\" },\n    { offset = 160, shader_location = 10, format = \"Snorm8x2\" },\n    { offset = 176, shader_location = 11, format = \"Snorm8x4\" },\n    { offset = 192, shader_location = 12, format = \"Uint16\" },\n    { offset = 208, shader_location = 13, format = \"Uint16x2\" },\n    { offset = 224, shader_location = 14, format = \"Uint16x4\" },\n    { offset = 240, shader_location = 15, format = \"Sint16\" },\n    { offset = 256, shader_location = 16, format = \"Sint16x2\" },\n    { offset = 272, shader_location = 17, format = \"Sint16x4\" },\n    { offset = 288, shader_location = 18, format = \"Unorm16\" },\n    { offset = 304, shader_location = 19, format = \"Unorm16x2\" },\n    { offset = 320, shader_location = 20, format = \"Unorm16x4\" },\n    { offset = 336, shader_location = 21, format = \"Snorm16\" },\n    { offset = 352, shader_location = 22, format = \"Snorm16x2\" },\n    { offset = 368, shader_location = 23, format = \"Snorm16x4\" },\n    { offset = 384, shader_location = 24, format = \"Float16\" },\n    { offset = 400, shader_location = 25, format = \"Float16x2\" },\n    { offset = 416, shader_location = 26, format = \"Float16x4\" },\n    { offset = 432, shader_location = 27, format = \"Float32\" },\n    { offset = 448, shader_location = 28, format = \"Float32x2\" },\n    { offset = 464, shader_location = 29, format = \"Float32x3\" },\n    { offset = 480, shader_location = 30, format = \"Float32x4\" },\n    { offset = 496, shader_location = 31, format = \"Uint32\" },\n    { offset = 512, shader_location = 32, format = \"Uint32x2\" },\n    { offset = 528, shader_location = 33, format = \"Uint32x3\" },\n    { offset = 544, shader_location = 34, format = \"Uint32x4\" },\n    { offset = 560, shader_location = 35, format = \"Sint32\" },\n    { offset = 576, shader_location = 36, format = \"Sint32x2\" },\n    { offset = 592, shader_location = 37, format = \"Sint32x3\" },\n    { offset = 608, shader_location = 38, format = \"Sint32x4\" },\n    { offset = 624, shader_location = 39, format = \"unorm10-10-10-2\" },\n    { offset = 640, shader_location = 40, format = \"unorm8x4-bgra\" },\n    { offset = 656, shader_location = 41, format = \"Float16\" },\n    { offset = 672, shader_location = 42, format = \"Float16x2\" },\n    { offset = 688, shader_location = 43, format = \"Float16x4\" },\n]\nid = 1\nstep_mode = \"ByVertex\"\nstride = 704\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x2.wgsl",
    "content": "// Tests for the vertex pulling transform in the MSL backend.\n//\n// Test loading `vec2` from each vertex format.\n//\n// (Note: only the wgsl files are different for each of the -formats-xN tests.\n// The toml files are all the same.)\n\nenable f16;\n\nstruct VertexOutput {\n  @builtin(position) position: vec4<f32>,\n}\n\nstruct VertexInput {\n  @location( 0) v_uint8     : vec2<u32>,\n  @location( 1) v_uint8x2   : vec2<u32>,\n  @location( 2) v_uint8x4   : vec2<u32>,\n  @location( 3) v_sint8     : vec2<i32>,\n  @location( 4) v_sint8x2   : vec2<i32>,\n  @location( 5) v_sint8x4   : vec2<i32>,\n  @location( 6) v_unorm8    : vec2<f32>,\n  @location( 7) v_unorm8x2  : vec2<f32>,\n  @location( 8) v_unorm8x4  : vec2<f32>,\n  @location( 9) v_snorm8    : vec2<f32>,\n  @location(10) v_snorm8x2  : vec2<f32>,\n  @location(11) v_snorm8x4  : vec2<f32>,\n  @location(12) v_uint16    : vec2<u32>,\n  @location(13) v_uint16x2  : vec2<u32>,\n  @location(14) v_uint16x4  : vec2<u32>,\n  @location(15) v_sint16    : vec2<i32>,\n  @location(16) v_sint16x2  : vec2<i32>,\n  @location(17) v_sint16x4  : vec2<i32>,\n  @location(18) v_unorm16   : vec2<f32>,\n  @location(19) v_unorm16x2 : vec2<f32>,\n  @location(20) v_unorm16x4 : vec2<f32>,\n  @location(21) v_snorm16   : vec2<f32>,\n  @location(22) v_snorm16x2 : vec2<f32>,\n  @location(23) v_snorm16x4 : vec2<f32>,\n  @location(24) v_float16   : vec2<f32>,\n  @location(25) v_float16x2 : vec2<f32>,\n  @location(26) v_float16x4 : vec2<f32>,\n  @location(27) v_float32   : vec2<f32>,\n  @location(28) v_float32x2 : vec2<f32>,\n  @location(29) v_float32x3 : vec2<f32>,\n  @location(30) v_float32x4 : vec2<f32>,\n  @location(31) v_uint32    : vec2<u32>,\n  @location(32) v_uint32x2  : vec2<u32>,\n  @location(33) v_uint32x3  : vec2<u32>,\n  @location(34) v_uint32x4  : vec2<u32>,\n  @location(35) v_sint32    : vec2<i32>,\n  @location(36) v_sint32x2  : vec2<i32>,\n  @location(37) v_sint32x3  : vec2<i32>,\n  @location(38) v_sint32x4  : vec2<i32>,\n  @location(39) v_unorm10_10_10_2: vec2<f32>,\n  @location(40) v_unorm8x4_bgra: vec2<f32>,\n\n  @location(41) v_float16_as_f16   : vec2<f16>,\n  @location(42) v_float16x2_as_f16 : vec2<f16>,\n  @location(43) v_float16x4_as_f16 : vec2<f16>,\n}\n\n@vertex\nfn render_vertex(\n  v_in: VertexInput,\n) -> VertexOutput\n{\n  return VertexOutput(vec4f(v_in.v_float32.x));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x3.toml",
    "content": "targets = \"METAL\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[msl_pipeline]\nallow_and_force_point_size = false\nvertex_pulling_transform = true\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [\n    { offset = 0, shader_location = 0, format = \"Uint8\" },\n    { offset = 16, shader_location = 1, format = \"Uint8x2\" },\n    { offset = 32, shader_location = 2, format = \"Uint8x4\" },\n    { offset = 48, shader_location = 3, format = \"Sint8\" },\n    { offset = 64, shader_location = 4, format = \"Sint8x2\" },\n    { offset = 80, shader_location = 5, format = \"Sint8x4\" },\n    { offset = 96, shader_location = 6, format = \"Unorm8\" },\n    { offset = 112, shader_location = 7, format = \"Unorm8x2\" },\n    { offset = 128, shader_location = 8, format = \"Unorm8x4\" },\n    { offset = 144, shader_location = 9, format = \"Snorm8\" },\n    { offset = 160, shader_location = 10, format = \"Snorm8x2\" },\n    { offset = 176, shader_location = 11, format = \"Snorm8x4\" },\n    { offset = 192, shader_location = 12, format = \"Uint16\" },\n    { offset = 208, shader_location = 13, format = \"Uint16x2\" },\n    { offset = 224, shader_location = 14, format = \"Uint16x4\" },\n    { offset = 240, shader_location = 15, format = \"Sint16\" },\n    { offset = 256, shader_location = 16, format = \"Sint16x2\" },\n    { offset = 272, shader_location = 17, format = \"Sint16x4\" },\n    { offset = 288, shader_location = 18, format = \"Unorm16\" },\n    { offset = 304, shader_location = 19, format = \"Unorm16x2\" },\n    { offset = 320, shader_location = 20, format = \"Unorm16x4\" },\n    { offset = 336, shader_location = 21, format = \"Snorm16\" },\n    { offset = 352, shader_location = 22, format = \"Snorm16x2\" },\n    { offset = 368, shader_location = 23, format = \"Snorm16x4\" },\n    { offset = 384, shader_location = 24, format = \"Float16\" },\n    { offset = 400, shader_location = 25, format = \"Float16x2\" },\n    { offset = 416, shader_location = 26, format = \"Float16x4\" },\n    { offset = 432, shader_location = 27, format = \"Float32\" },\n    { offset = 448, shader_location = 28, format = \"Float32x2\" },\n    { offset = 464, shader_location = 29, format = \"Float32x3\" },\n    { offset = 480, shader_location = 30, format = \"Float32x4\" },\n    { offset = 496, shader_location = 31, format = \"Uint32\" },\n    { offset = 512, shader_location = 32, format = \"Uint32x2\" },\n    { offset = 528, shader_location = 33, format = \"Uint32x3\" },\n    { offset = 544, shader_location = 34, format = \"Uint32x4\" },\n    { offset = 560, shader_location = 35, format = \"Sint32\" },\n    { offset = 576, shader_location = 36, format = \"Sint32x2\" },\n    { offset = 592, shader_location = 37, format = \"Sint32x3\" },\n    { offset = 608, shader_location = 38, format = \"Sint32x4\" },\n    { offset = 624, shader_location = 39, format = \"unorm10-10-10-2\" },\n    { offset = 640, shader_location = 40, format = \"unorm8x4-bgra\" },\n    { offset = 656, shader_location = 41, format = \"Float16\" },\n    { offset = 672, shader_location = 42, format = \"Float16x2\" },\n    { offset = 688, shader_location = 43, format = \"Float16x4\" },\n]\nid = 1\nstep_mode = \"ByVertex\"\nstride = 704\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x3.wgsl",
    "content": "// Tests for the vertex pulling transform in the MSL backend.\n//\n// Test loading `vec3` from each vertex format.\n//\n// (Note: only the wgsl files are different for each of the -formats-xN tests.\n// The toml files are all the same.)\n\nenable f16;\n\nstruct VertexOutput {\n  @builtin(position) position: vec4<f32>,\n}\n\nstruct VertexInput {\n  @location( 0) v_uint8     : vec3<u32>,\n  @location( 1) v_uint8x2   : vec3<u32>,\n  @location( 2) v_uint8x4   : vec3<u32>,\n  @location( 3) v_sint8     : vec3<i32>,\n  @location( 4) v_sint8x2   : vec3<i32>,\n  @location( 5) v_sint8x4   : vec3<i32>,\n  @location( 6) v_unorm8    : vec3<f32>,\n  @location( 7) v_unorm8x2  : vec3<f32>,\n  @location( 8) v_unorm8x4  : vec3<f32>,\n  @location( 9) v_snorm8    : vec3<f32>,\n  @location(10) v_snorm8x2  : vec3<f32>,\n  @location(11) v_snorm8x4  : vec3<f32>,\n  @location(12) v_uint16    : vec3<u32>,\n  @location(13) v_uint16x2  : vec3<u32>,\n  @location(14) v_uint16x4  : vec3<u32>,\n  @location(15) v_sint16    : vec3<i32>,\n  @location(16) v_sint16x2  : vec3<i32>,\n  @location(17) v_sint16x4  : vec3<i32>,\n  @location(18) v_unorm16   : vec3<f32>,\n  @location(19) v_unorm16x2 : vec3<f32>,\n  @location(20) v_unorm16x4 : vec3<f32>,\n  @location(21) v_snorm16   : vec3<f32>,\n  @location(22) v_snorm16x2 : vec3<f32>,\n  @location(23) v_snorm16x4 : vec3<f32>,\n  @location(24) v_float16   : vec3<f32>,\n  @location(25) v_float16x2 : vec3<f32>,\n  @location(26) v_float16x4 : vec3<f32>,\n  @location(27) v_float32   : vec3<f32>,\n  @location(28) v_float32x2 : vec3<f32>,\n  @location(29) v_float32x3 : vec3<f32>,\n  @location(30) v_float32x4 : vec3<f32>,\n  @location(31) v_uint32    : vec3<u32>,\n  @location(32) v_uint32x2  : vec3<u32>,\n  @location(33) v_uint32x3  : vec3<u32>,\n  @location(34) v_uint32x4  : vec3<u32>,\n  @location(35) v_sint32    : vec3<i32>,\n  @location(36) v_sint32x2  : vec3<i32>,\n  @location(37) v_sint32x3  : vec3<i32>,\n  @location(38) v_sint32x4  : vec3<i32>,\n  @location(39) v_unorm10_10_10_2: vec3<f32>,\n  @location(40) v_unorm8x4_bgra: vec3<f32>,\n\n  @location(41) v_float16_as_f16   : vec3<f16>,\n  @location(42) v_float16x2_as_f16 : vec3<f16>,\n  @location(43) v_float16x4_as_f16 : vec3<f16>,\n}\n\n@vertex\nfn render_vertex(\n  v_in: VertexInput,\n) -> VertexOutput\n{\n  return VertexOutput(vec4f(v_in.v_float32.x));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x4.toml",
    "content": "targets = \"METAL\"\ncapabilities = \"SHADER_FLOAT16\"\n\n[msl_pipeline]\nallow_and_force_point_size = false\nvertex_pulling_transform = true\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [\n    { offset = 0, shader_location = 0, format = \"Uint8\" },\n    { offset = 16, shader_location = 1, format = \"Uint8x2\" },\n    { offset = 32, shader_location = 2, format = \"Uint8x4\" },\n    { offset = 48, shader_location = 3, format = \"Sint8\" },\n    { offset = 64, shader_location = 4, format = \"Sint8x2\" },\n    { offset = 80, shader_location = 5, format = \"Sint8x4\" },\n    { offset = 96, shader_location = 6, format = \"Unorm8\" },\n    { offset = 112, shader_location = 7, format = \"Unorm8x2\" },\n    { offset = 128, shader_location = 8, format = \"Unorm8x4\" },\n    { offset = 144, shader_location = 9, format = \"Snorm8\" },\n    { offset = 160, shader_location = 10, format = \"Snorm8x2\" },\n    { offset = 176, shader_location = 11, format = \"Snorm8x4\" },\n    { offset = 192, shader_location = 12, format = \"Uint16\" },\n    { offset = 208, shader_location = 13, format = \"Uint16x2\" },\n    { offset = 224, shader_location = 14, format = \"Uint16x4\" },\n    { offset = 240, shader_location = 15, format = \"Sint16\" },\n    { offset = 256, shader_location = 16, format = \"Sint16x2\" },\n    { offset = 272, shader_location = 17, format = \"Sint16x4\" },\n    { offset = 288, shader_location = 18, format = \"Unorm16\" },\n    { offset = 304, shader_location = 19, format = \"Unorm16x2\" },\n    { offset = 320, shader_location = 20, format = \"Unorm16x4\" },\n    { offset = 336, shader_location = 21, format = \"Snorm16\" },\n    { offset = 352, shader_location = 22, format = \"Snorm16x2\" },\n    { offset = 368, shader_location = 23, format = \"Snorm16x4\" },\n    { offset = 384, shader_location = 24, format = \"Float16\" },\n    { offset = 400, shader_location = 25, format = \"Float16x2\" },\n    { offset = 416, shader_location = 26, format = \"Float16x4\" },\n    { offset = 432, shader_location = 27, format = \"Float32\" },\n    { offset = 448, shader_location = 28, format = \"Float32x2\" },\n    { offset = 464, shader_location = 29, format = \"Float32x3\" },\n    { offset = 480, shader_location = 30, format = \"Float32x4\" },\n    { offset = 496, shader_location = 31, format = \"Uint32\" },\n    { offset = 512, shader_location = 32, format = \"Uint32x2\" },\n    { offset = 528, shader_location = 33, format = \"Uint32x3\" },\n    { offset = 544, shader_location = 34, format = \"Uint32x4\" },\n    { offset = 560, shader_location = 35, format = \"Sint32\" },\n    { offset = 576, shader_location = 36, format = \"Sint32x2\" },\n    { offset = 592, shader_location = 37, format = \"Sint32x3\" },\n    { offset = 608, shader_location = 38, format = \"Sint32x4\" },\n    { offset = 624, shader_location = 39, format = \"unorm10-10-10-2\" },\n    { offset = 640, shader_location = 40, format = \"unorm8x4-bgra\" },\n    { offset = 656, shader_location = 41, format = \"Float16\" },\n    { offset = 672, shader_location = 42, format = \"Float16x2\" },\n    { offset = 688, shader_location = 43, format = \"Float16x4\" },\n]\nid = 1\nstep_mode = \"ByVertex\"\nstride = 704\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt-formats-x4.wgsl",
    "content": "// Tests for the vertex pulling transform in the MSL backend.\n//\n// Test loading `vec4` from each vertex format.\n//\n// (Note: only the wgsl files are different for each of the -formats-xN tests.\n// The toml files are all the same.)\n\nenable f16;\n\nstruct VertexOutput {\n  @builtin(position) position: vec4<f32>,\n}\n\nstruct VertexInput {\n  @location( 0) v_uint8     : vec4<u32>,\n  @location( 1) v_uint8x2   : vec4<u32>,\n  @location( 2) v_uint8x4   : vec4<u32>,\n  @location( 3) v_sint8     : vec4<i32>,\n  @location( 4) v_sint8x2   : vec4<i32>,\n  @location( 5) v_sint8x4   : vec4<i32>,\n  @location( 6) v_unorm8    : vec4<f32>,\n  @location( 7) v_unorm8x2  : vec4<f32>,\n  @location( 8) v_unorm8x4  : vec4<f32>,\n  @location( 9) v_snorm8    : vec4<f32>,\n  @location(10) v_snorm8x2  : vec4<f32>,\n  @location(11) v_snorm8x4  : vec4<f32>,\n  @location(12) v_uint16    : vec4<u32>,\n  @location(13) v_uint16x2  : vec4<u32>,\n  @location(14) v_uint16x4  : vec4<u32>,\n  @location(15) v_sint16    : vec4<i32>,\n  @location(16) v_sint16x2  : vec4<i32>,\n  @location(17) v_sint16x4  : vec4<i32>,\n  @location(18) v_unorm16   : vec4<f32>,\n  @location(19) v_unorm16x2 : vec4<f32>,\n  @location(20) v_unorm16x4 : vec4<f32>,\n  @location(21) v_snorm16   : vec4<f32>,\n  @location(22) v_snorm16x2 : vec4<f32>,\n  @location(23) v_snorm16x4 : vec4<f32>,\n  @location(24) v_float16   : vec4<f32>,\n  @location(25) v_float16x2 : vec4<f32>,\n  @location(26) v_float16x4 : vec4<f32>,\n  @location(27) v_float32   : vec4<f32>,\n  @location(28) v_float32x2 : vec4<f32>,\n  @location(29) v_float32x3 : vec4<f32>,\n  @location(30) v_float32x4 : vec4<f32>,\n  @location(31) v_uint32    : vec4<u32>,\n  @location(32) v_uint32x2  : vec4<u32>,\n  @location(33) v_uint32x3  : vec4<u32>,\n  @location(34) v_uint32x4  : vec4<u32>,\n  @location(35) v_sint32    : vec4<i32>,\n  @location(36) v_sint32x2  : vec4<i32>,\n  @location(37) v_sint32x3  : vec4<i32>,\n  @location(38) v_sint32x4  : vec4<i32>,\n  @location(39) v_unorm10_10_10_2: vec4<f32>,\n  @location(40) v_unorm8x4_bgra: vec4<f32>,\n\n  @location(41) v_float16_as_f16   : vec4<f16>,\n  @location(42) v_float16x2_as_f16 : vec4<f16>,\n  @location(43) v_float16x4_as_f16 : vec4<f16>,\n}\n\n@vertex\nfn render_vertex(\n  v_in: VertexInput,\n) -> VertexOutput\n{\n  return VertexOutput(vec4f(v_in.v_float32.x));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt.toml",
    "content": "targets = \"METAL\"\n\n[msl_pipeline]\nallow_and_force_point_size = false\nvertex_pulling_transform = true\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [\n    { offset = 0, shader_location = 0, format = \"Float32\" },\n    { offset = 4, shader_location = 1, format = \"Float32x4\" },\n]\nid = 1\nstep_mode = \"ByVertex\"\nstride = 20\n\n[[msl_pipeline.vertex_buffer_mappings]]\nattributes = [{ offset = 0, shader_location = 2, format = \"Float32x2\" }]\nid = 2\nstep_mode = \"ByInstance\"\nstride = 16\n"
  },
  {
    "path": "naga/tests/in/wgsl/msl-vpt.wgsl",
    "content": "// Tests for the vertex pulling transform in the MSL backend.\n\nstruct VertexOutput {\n  @builtin(position) position: vec4<f32>,\n  @location(0) color: vec4<f32>,\n  @location(1) texcoord: vec2<f32>,\n}\n\nstruct VertexInput {\n  @location(0) position: vec4<f32>,\n  @location(1) normal: vec3<f32>,\n  @location(2) texcoord: vec2<f32>,\n}\n\n@group(0) @binding(0) var<uniform> mvp_matrix: mat4x4<f32>;\n\n@vertex\nfn render_vertex(\n  v_in: VertexInput,\n  @builtin(vertex_index) v_existing_id: u32,\n) -> VertexOutput\n{\n  var v_out: VertexOutput;\n  v_out.position = v_in.position * mvp_matrix;\n  v_out.color = do_lighting(v_in.position,\n                            v_in.normal);\n  v_out.texcoord = v_in.texcoord;\n  return v_out;\n}\n\nfn do_lighting(position: vec4<f32>, normal: vec3<f32>) -> vec4<f32> {\n  // blah blah blah\n  return vec4<f32>(0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/multiview.toml",
    "content": "glsl_multiview = 2\ncapabilities = \"MULTIVIEW\"\n\n[msl]\nlang_version = [2, 3]\n\n[hlsl]\nshader_model = \"V6_1\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/multiview.wgsl",
    "content": "@fragment\nfn main(@builtin(view_index) view_index: u32) {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/multiview_webgl.toml",
    "content": "glsl_multiview = 2\ncapabilities = \"MULTIVIEW\"\ntargets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = true, version = 300 }\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/multiview_webgl.wgsl",
    "content": "@fragment\nfn main(@builtin(view_index) view_index: u32) {}\n"
  },
  {
    "path": "naga/tests/in/wgsl/must-use.toml",
    "content": "targets = \"IR\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/must-use.wgsl",
    "content": "@must_use\nfn use_me() -> i32 { return 10; }\n\nfn use_return() -> i32 {\n    return use_me();\n}\n\nfn use_assign_var() -> i32 {\n    var q = use_me();\n    return q;\n}\n\nfn use_assign_let() -> i32 {\n    let q = use_me();\n    return q;\n}\n\nfn use_phony_assign() {\n    _ = use_me();\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    use_return();\n    use_assign_var();\n    use_assign_let();\n    use_phony_assign();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/operators.wgsl",
    "content": "const v_f32_one = vec4<f32>(1.0, 1.0, 1.0, 1.0);\nconst v_f32_zero = vec4<f32>(0.0, 0.0, 0.0, 0.0);\nconst v_f32_half = vec4<f32>(0.5, 0.5, 0.5, 0.5);\nconst v_i32_one = vec4<i32>(1, 1, 1, 1);\n\nconst b_false = false;\nconst b_true = true;\n\nfn builtins() -> vec4<f32> {\n    // select()\n    let condition = true;\n    let s1 = select(0, 1, condition);\n    let s2 = select(v_f32_zero, v_f32_one, condition);\n    let s3 = select(v_f32_one, v_f32_zero, vec4<bool>(false, false, false, false));\n    // mix()\n    let m1 = mix(v_f32_zero, v_f32_one, v_f32_half);\n    let m2 = mix(v_f32_zero, v_f32_one, 0.1);\n    // bitcast()\n    let b1 = bitcast<f32>(v_i32_one.x);\n    let b2 = bitcast<vec4<f32>>(v_i32_one);\n    // convert\n    let v_i32_zero = vec4<i32>(v_f32_zero);\n    // done\n    return vec4<f32>(vec4<i32>(s1) + v_i32_zero) + s2 + m1 + m2 + b1 + b2;\n}\n\nfn splat(m: f32, n: i32) -> vec4<f32> {\n    let a = (2.0 + vec2<f32>(m) - 4.0) / 8.0;\n    let b = vec4<i32>(n) % 2;\n    return a.xyxy + vec4<f32>(b);\n}\n\nfn splat_assignment() -> vec2<f32> {\n    var a = vec2<f32>(2.0);\n    a += 1.0;\n    a -= 3.0;\n    a /= 4.0;\n    return a;\n}\n\nfn bool_cast(x: vec3<f32>) -> vec3<f32> {\n    let y = vec3<bool>(x);\n    return vec3<f32>(y);\n}\n\nfn p() -> bool { return true; }\nfn q() -> bool { return false; }\nfn r() -> bool { return true; }\nfn s() -> bool { return false; }\n\nconst short_circuit_1_invalid_rhs = false && sqrt(-1) != 0;\n// TODO(https://github.com/gfx-rs/wgpu/issues/8440):\n// The following should not be accepted, but it currently is.\n// When fixed, move this to a wgsl_errors test.\nconst short_circuit_2_invalid_rhs = false && (0u + 1.0f > 0);\nconst short_circuit_3 = b_false || b_true;\nconst short_circuit_4 = !((b_false) || (any(vec3(false, false, false))));\n\nfn logical() {\n    let t = true;\n    let f = false;\n\n    // unary\n    let neg0 = !t;\n    let neg1 = !vec2(t);\n\n    // binary\n    let or = t || f;\n    let and = t && f;\n    let bitwise_or0 = t | f;\n    let bitwise_or1 = vec3(t) | vec3(f);\n    let bitwise_and0 = t & f;\n    let bitwise_and1 = vec4(t) & vec4(f);\n\n    const short_circuit_1_invalid = false && sqrt(-1) != 0;\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8440):\n    // The following should not be accepted, but it currently is.\n    // When fixed, move this to a wgsl_errors test.\n    const short_circuit_2_invalid_rhs = false && (0u + 1.0f > 0);\n    const short_circuit_3 = b_false || b_true;\n    const short_circuit_4 = !((b_false) || (any(vec3(false, false, false))));\n\n    let short_circuit_5 = !((f) || (any(vec3(false, false, false))));\n    let short_circuit_6 = (p() || q()) && (r() || s());\n    let short_circuit_7 = true || q();\n}\n\nfn arithmetic() {\n    let one_i = 1i;\n    let one_u = 1u;\n    let one_f = 1.0;\n    let two_i = 2i;\n    let two_u = 2u;\n    let two_f = 2.0;\n\n    // unary\n    let neg0 = -one_f;\n    let neg1 = -vec2(one_i);\n    let neg2 = -vec2(one_f);\n\n    // binary\n    // Addition\n    let add0 = two_i + one_i;\n    let add1 = two_u + one_u;\n    let add2 = two_f + one_f;\n    let add3 = vec2(two_i) + vec2(one_i);\n    let add4 = vec3(two_u) + vec3(one_u);\n    let add5 = vec4(two_f) + vec4(one_f);\n\n    // Subtraction\n    let sub0 = two_i - one_i;\n    let sub1 = two_u - one_u;\n    let sub2 = two_f - one_f;\n    let sub3 = vec2(two_i) - vec2(one_i);\n    let sub4 = vec3(two_u) - vec3(one_u);\n    let sub5 = vec4(two_f) - vec4(one_f);\n\n    // Multiplication\n    let mul0 = two_i * one_i;\n    let mul1 = two_u * one_u;\n    let mul2 = two_f * one_f;\n    let mul3 = vec2(two_i) * vec2(one_i);\n    let mul4 = vec3(two_u) * vec3(one_u);\n    let mul5 = vec4(two_f) * vec4(one_f);\n\n    // Division\n    let div0 = two_i / one_i;\n    let div1 = two_u / one_u;\n    let div2 = two_f / one_f;\n    let div3 = vec2(two_i) / vec2(one_i);\n    let div4 = vec3(two_u) / vec3(one_u);\n    let div5 = vec4(two_f) / vec4(one_f);\n\n    // Remainder\n    let rem0 = two_i % one_i;\n    let rem1 = two_u % one_u;\n    let rem2 = two_f % one_f;\n    let rem3 = vec2(two_i) % vec2(one_i);\n    let rem4 = vec3(two_u) % vec3(one_u);\n    let rem5 = vec4(two_f) % vec4(one_f);\n\n    // Binary arithmetic expressions with mixed scalar and vector operands\n    {\n        let add0 = vec2(two_i) + one_i;\n        let add1 = two_i + vec2(one_i);\n        let add2 = vec2(two_u) + one_u;\n        let add3 = two_u + vec2(one_u);\n        let add4 = vec2(two_f) + one_f;\n        let add5 = two_f + vec2(one_f);\n\n        let sub0 = vec2(two_i) - one_i;\n        let sub1 = two_i - vec2(one_i);\n        let sub2 = vec2(two_u) - one_u;\n        let sub3 = two_u - vec2(one_u);\n        let sub4 = vec2(two_f) - one_f;\n        let sub5 = two_f - vec2(one_f);\n\n        let mul0 = vec2(two_i) * one_i;\n        let mul1 = two_i * vec2(one_i);\n        let mul2 = vec2(two_u) * one_u;\n        let mul3 = two_u * vec2(one_u);\n        let mul4 = vec2(two_f) * one_f;\n        let mul5 = two_f * vec2(one_f);\n\n        let div0 = vec2(two_i) / one_i;\n        let div1 = two_i / vec2(one_i);\n        let div2 = vec2(two_u) / one_u;\n        let div3 = two_u / vec2(one_u);\n        let div4 = vec2(two_f) / one_f;\n        let div5 = two_f / vec2(one_f);\n\n        let rem0 = vec2(two_i) % one_i;\n        let rem1 = two_i % vec2(one_i);\n        let rem2 = vec2(two_u) % one_u;\n        let rem3 = two_u % vec2(one_u);\n        let rem4 = vec2(two_f) % one_f;\n        let rem5 = two_f % vec2(one_f);\n    }\n\n    // Matrix arithmetic\n    let add = mat3x3<f32>() + mat3x3<f32>();\n    let sub = mat3x3<f32>() - mat3x3<f32>();\n\n    let mul_scalar0 = mat3x3<f32>() * one_f;\n    let mul_scalar1 = two_f * mat3x3<f32>();\n\n    let mul_vector0 = mat4x3<f32>() * vec4(one_f);\n    let mul_vector1 = vec3f(two_f) * mat4x3f();\n\n    let mul = mat4x3<f32>() * mat3x4<f32>();\n\n    // Arithmetic involving the minimum value i32 literal. What we're really testing here\n    // is how this literal is expressed by Naga backends. eg in Metal, `-2147483648` is\n    // silently promoted to a `long` which we don't want. The addition ensures this would\n    // be caught as a compiler error, as we bitcast the operands to unsigned which fails\n    // if the expression's type has an unexpected width.\n    var prevent_const_eval: i32;\n    var wgpu_7437 = prevent_const_eval + -2147483648;\n}\n\nfn bit() {\n    let one_i = 1i;\n    let one_u = 1u;\n    let two_i = 2i;\n    let two_u = 2u;\n\n    // unary\n    let flip0 = ~one_i;\n    let flip1 = ~one_u;\n    let flip2 = ~vec2(one_i);\n    let flip3 = ~vec3(one_u);\n\n    // binary\n    let or0 = two_i | one_i;\n    let or1 = two_u | one_u;\n    let or2 = vec2(two_i) | vec2(one_i);\n    let or3 = vec3(two_u) | vec3(one_u);\n\n    let and0 = two_i & one_i;\n    let and1 = two_u & one_u;\n    let and2 = vec2(two_i) & vec2(one_i);\n    let and3 = vec3(two_u) & vec3(one_u);\n\n    let xor0 = two_i ^ one_i;\n    let xor1 = two_u ^ one_u;\n    let xor2 = vec2(two_i) ^ vec2(one_i);\n    let xor3 = vec3(two_u) ^ vec3(one_u);\n\n    let shl0 = two_i << one_u;\n    let shl1 = two_u << one_u;\n    let shl2 = vec2(two_i) << vec2(one_u);\n    let shl3 = vec3(two_u) << vec3(one_u);\n\n    let shr0 = two_i >> one_u;\n    let shr1 = two_u >> one_u;\n    let shr2 = vec2(two_i) >> vec2(one_u);\n    let shr3 = vec3(two_u) >> vec3(one_u);\n}\n\nfn comparison() {\n    let one_i = 1i;\n    let one_u = 1u;\n    let one_f = 1.0;\n    let two_i = 2i;\n    let two_u = 2u;\n    let two_f = 2.0;\n\n    let eq0 = two_i == one_i;\n    let eq1 = two_u == one_u;\n    let eq2 = two_f == one_f;\n    let eq3 = vec2(two_i) == vec2(one_i);\n    let eq4 = vec3(two_u) == vec3(one_u);\n    let eq5 = vec4(two_f) == vec4(one_f);\n\n    let neq0 = two_i != one_i;\n    let neq1 = two_u != one_u;\n    let neq2 = two_f != one_f;\n    let neq3 = vec2(two_i) != vec2(one_i);\n    let neq4 = vec3(two_u) != vec3(one_u);\n    let neq5 = vec4(two_f) != vec4(one_f);\n\n    let lt0 = two_i < one_i;\n    let lt1 = two_u < one_u;\n    let lt2 = two_f < one_f;\n    let lt3 = vec2(two_i) < vec2(one_i);\n    let lt4 = vec3(two_u) < vec3(one_u);\n    let lt5 = vec4(two_f) < vec4(one_f);\n\n    let lte0 = two_i <= one_i;\n    let lte1 = two_u <= one_u;\n    let lte2 = two_f <= one_f;\n    let lte3 = vec2(two_i) <= vec2(one_i);\n    let lte4 = vec3(two_u) <= vec3(one_u);\n    let lte5 = vec4(two_f) <= vec4(one_f);\n\n    let gt0 = two_i > one_i;\n    let gt1 = two_u > one_u;\n    let gt2 = two_f > one_f;\n    let gt3 = vec2(two_i) > vec2(one_i);\n    let gt4 = vec3(two_u) > vec3(one_u);\n    let gt5 = vec4(two_f) > vec4(one_f);\n\n    let gte0 = two_i >= one_i;\n    let gte1 = two_u >= one_u;\n    let gte2 = two_f >= one_f;\n    let gte3 = vec2(two_i) >= vec2(one_i);\n    let gte4 = vec3(two_u) >= vec3(one_u);\n    let gte5 = vec4(two_f) >= vec4(one_f);\n}\n\nfn assignment() {\n    let zero_i = 0i;\n    let one_i = 1i;\n    let one_u = 1u;\n    let two_u = 2u;\n\n    var a = one_i;\n\n    a += one_i;\n    a -= one_i;\n    a *= a;\n    a /= a;\n    a %= one_i;\n    a &= zero_i;\n    a |= zero_i;\n    a ^= zero_i;\n    a <<= two_u;\n    a >>= one_u;\n\n    a++;\n    a--;\n\n    var vec0: vec3<i32> = vec3<i32>();\n    vec0[one_i]++;\n    vec0[one_i]--;\n}\n\nfn negation_avoids_prefix_decrement() {\n    let i = 1;\n    let i0 = -i;\n    let i1 = - -i;\n    let i2 = -(-i);\n    let i3 = -(- i);\n    let i4 = - - -i;\n    let i5 = - - - - i;\n    let i6 = - - -(- -i);\n    let i7 = (- - - - -i);\n\n    let f = 1.0;\n    let f0 = -f;\n    let f1 = - -f;\n    let f2 = -(-f);\n    let f3 = -(- f);\n    let f4 = - - -f;\n    let f5 = - - - - f;\n    let f6 = - - -(- -f);\n    let f7 = (- - - - -f);\n}\n\n@compute @workgroup_size(1)\nfn main(@builtin(workgroup_id) id: vec3<u32>) {\n    builtins();\n    splat(f32(id.x), i32(id.y));\n    splat_assignment();\n    bool_cast(v_f32_one.xyz);\n\n    logical();\n    arithmetic();\n    bit();\n    comparison();\n    assignment();\n\n    negation_avoids_prefix_decrement();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides-atomicCompareExchangeWeak.toml",
    "content": "pipeline_constants = { o = 2.0 }\ntargets = \"IR | SPIRV | METAL\"\n\n[spv]\nseparate_entry_points = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides-atomicCompareExchangeWeak.wgsl",
    "content": "override o: i32;\nvar<workgroup> a: atomic<u32>;\n\n@compute @workgroup_size(1)\nfn f() {\n  atomicCompareExchangeWeak(&a, u32(o), 1u);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides-ray-query.toml",
    "content": "capabilities = \"RAY_QUERY\"\npipeline_constants = { o = 2.0 }\ntargets = \"IR | SPIRV | METAL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = false\n\n[spv]\nseparate_entry_points = true\nversion = [1, 4]\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides-ray-query.wgsl",
    "content": "enable wgpu_ray_query;\n\noverride o: f32;\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\n@compute @workgroup_size(1)\nfn main() {\n  var rq: ray_query;\n\n  let desc = RayDesc(\n      RAY_FLAG_TERMINATE_ON_FIRST_HIT,\n      0xFFu,\n      o * 17.0,\n      o * 19.0,\n      vec3<f32>(o * 23.0),\n      vec3<f32>(o * 29.0, o * 31.0, o * 37.0),\n  );\n  rayQueryInitialize(&rq, acc_struct, desc);\n\n  while (rayQueryProceed(&rq)) {}\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides.toml",
    "content": "pipeline_constants = { 0 = nan, 1300 = 1.1, depth = 2.3 }\ntargets = \"IR | ANALYSIS | SPIRV | METAL | HLSL | GLSL\"\n\n[spv]\nseparate_entry_points = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/overrides.wgsl",
    "content": "@id(0)    override has_point_light: bool = true;  // Algorithmic control\n@id(1200) override specular_param: f32 = 2.3;     // Numeric control\n@id(1300) override gain: f32;                     // Must be overridden\n          override width: f32 = 0.0;              // Specified at the API level using\n                                                  // the name \"width\".\n          override depth: f32;                    // Specified at the API level using\n                                                  // the name \"depth\".\n                                                  // Must be overridden.\n          override height = 2 * depth;            // The default value\n                                                  // (if not set at the API level),\n                                                  // depends on another\n                                                  // overridable constant.\n\noverride inferred_f32 = 2.718;\n\noverride auto_conversion: u32 = 0;\n\nvar<private> gain_x_10: f32 = gain * 10.;\nvar<private> store_override: f32;\n\n@compute @workgroup_size(1)\nfn main() {\n    var t = height * 5;\n    let a = !has_point_light;\n    var x = a;\n\n    var gain_x_100 = gain_x_10 * 10.;\n\n    store_override = gain;\n\n    _ = specular_param;\n    _ = width;\n    _ = inferred_f32;\n    _ = auto_conversion;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/padding.toml",
    "content": "[msl]\nfake_missing_bindings = false\nlang_version = [1, 0]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.vertex]\nresources = [\n    { bind_target = { buffer = 0, mutable = false }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { buffer = 1, mutable = false }, resource_binding = { group = 0, binding = 1 } },\n    { bind_target = { buffer = 2, mutable = false }, resource_binding = { group = 0, binding = 2 } },\n]\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/padding.wgsl",
    "content": "struct S {\n    a: vec3<f32>,\n}\n\nstruct Test {\n    a: S,\n    b: f32, // offset: 16\n}\n\nstruct Test2 {\n    a: array<vec3<f32>, 2>,\n    b: f32, // offset: 32\n}\n\nstruct Test3 {\n    a: mat4x3<f32>,\n    b: f32, // offset: 64\n}\n\n@group(0) @binding(0)\nvar<uniform> input1: Test;\n\n@group(0) @binding(1)\nvar<uniform> input2: Test2;\n\n@group(0) @binding(2)\nvar<uniform> input3: Test3;\n\n\n@vertex\nfn vertex() -> @builtin(position) vec4<f32> {\n    return vec4<f32>(1.0) * input1.b * input2.b * input3.b;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/per-vertex.toml",
    "content": "capabilities = \"PER_VERTEX\"\ntargets = \"WGSL | SPIRV\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/per-vertex.wgsl",
    "content": "@fragment\nfn fs_main(@location(0) @interpolate(per_vertex) v: array<f32, 3>) -> @location(0) vec4<f32> {\n    return vec4(v[0], v[1], v[2], 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/phony_assignment.wgsl",
    "content": "@group(0) @binding(0) var<uniform> binding: f32;\n\nfn five() -> i32 {\n  return 5;\n}\n\n@compute @workgroup_size(1) fn main(\n  @builtin(global_invocation_id) id: vec3<u32>\n) {\n    _ = binding;\n    _ = binding;\n    let a = 5;\n    _ = a;\n    _ = five();\n    let b = five();\n    // check for name collision\n    let phony = binding;\n}"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg-restrict.toml",
    "content": "targets = \"METAL\"\n\n[bounds_check_policies]\nindex = \"Restrict\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg-restrict.wgsl",
    "content": "fn takes_ptr(p: ptr<function, i32>) {}\nfn takes_array_ptr(p: ptr<function, array<i32, 4>>) {}\nfn takes_vec_ptr(p: ptr<function, vec2<i32>>) {}\nfn takes_mat_ptr(p: ptr<function, mat2x2<f32>>) {}\n\nfn local_var(i: u32) {\n    var arr = array(1, 2, 3, 4);\n    takes_ptr(&arr[i]);\n    takes_array_ptr(&arr);\n\n}\n\nfn mat_vec_ptrs(\n    pv: ptr<function, array<vec2<i32>, 4>>,\n    pm: ptr<function, array<mat2x2<f32>, 4>>,\n    i: u32,\n) {\n    takes_vec_ptr(&pv[i]);\n    takes_mat_ptr(&pm[i]);\n}\n\nfn argument(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[i]);\n}\n\nfn argument_nested_x2(v: ptr<function, array<array<i32, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][j]);\n\n    // Mixing compile and runtime bounds checks\n    takes_ptr(&v[i][0]);\n    takes_ptr(&v[0][j]);\n\n    takes_array_ptr(&v[i]);\n}\n\nfn argument_nested_x3(v: ptr<function, array<array<array<i32, 4>, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][0][j]);\n    takes_ptr(&v[i][j][0]);\n    takes_ptr(&v[0][i][j]);\n}\n\nfn index_from_self(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[v[i]]);\n}\n\nfn local_var_from_arg(a: array<i32, 4>, i: u32) {\n    var b = a;\n    takes_ptr(&b[i]);\n}\n\nfn let_binding(a: ptr<function, array<i32, 4>>, i: u32) {\n    let p0 = &a[i];\n    takes_ptr(p0);\n\n    let p1 = &a[0];\n    takes_ptr(p1);\n}\n\n// Runtime-sized arrays can only appear in storage buffers, while (in the base\n// language) pointers can only appear in function or private space, so there\n// is no interaction to test.\n\n@compute @workgroup_size(1)\nfn main() {\n    var vec: array<vec2<i32>, 4>;\n    var mat: array<mat2x2<f32>, 4>;\n    var arr1d: array<i32, 4>;\n    var arr2d: array<array<i32, 4>, 4>;\n    var arr3d: array<array<array<i32, 4>, 4>, 4>;\n    local_var(1);\n    mat_vec_ptrs(&vec, &mat, 1);\n    argument(&arr1d, 1);\n    argument_nested_x2(&arr2d, 1, 2);\n    argument_nested_x3(&arr3d, 1, 2);\n    index_from_self(&arr1d, 1);\n    local_var_from_arg(array(1, 2, 3, 4), 5);\n    let_binding(&arr1d, 1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg-rzsw.toml",
    "content": "targets = \"METAL\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg-rzsw.wgsl",
    "content": "fn takes_ptr(p: ptr<function, i32>) {}\nfn takes_array_ptr(p: ptr<function, array<i32, 4>>) {}\nfn takes_vec_ptr(p: ptr<function, vec2<i32>>) {}\nfn takes_mat_ptr(p: ptr<function, mat2x2<f32>>) {}\n\nfn local_var(i: u32) {\n    var arr = array(1, 2, 3, 4);\n    takes_ptr(&arr[i]);\n    takes_array_ptr(&arr);\n\n}\n\nfn mat_vec_ptrs(\n    pv: ptr<function, array<vec2<i32>, 4>>,\n    pm: ptr<function, array<mat2x2<f32>, 4>>,\n    i: u32,\n) {\n    takes_vec_ptr(&pv[i]);\n    takes_mat_ptr(&pm[i]);\n}\n\nfn argument(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[i]);\n}\n\nfn argument_nested_x2(v: ptr<function, array<array<i32, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][j]);\n\n    // Mixing compile and runtime bounds checks\n    takes_ptr(&v[i][0]);\n    takes_ptr(&v[0][j]);\n\n    takes_array_ptr(&v[i]);\n}\n\nfn argument_nested_x3(v: ptr<function, array<array<array<i32, 4>, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][0][j]);\n    takes_ptr(&v[i][j][0]);\n    takes_ptr(&v[0][i][j]);\n}\n\nfn index_from_self(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[v[i]]);\n}\n\nfn local_var_from_arg(a: array<i32, 4>, i: u32) {\n    var b = a;\n    takes_ptr(&b[i]);\n}\n\nfn let_binding(a: ptr<function, array<i32, 4>>, i: u32) {\n    let p0 = &a[i];\n    takes_ptr(p0);\n\n    let p1 = &a[0];\n    takes_ptr(p1);\n}\n\n// Runtime-sized arrays can only appear in storage buffers, while (in the base\n// language) pointers can only appear in function or private space, so there\n// is no interaction to test.\n\n@compute @workgroup_size(1)\nfn main() {\n    var vec: array<vec2<i32>, 4>;\n    var mat: array<mat2x2<f32>, 4>;\n    var arr1d: array<i32, 4>;\n    var arr2d: array<array<i32, 4>, 4>;\n    var arr3d: array<array<array<i32, 4>, 4>, 4>;\n    local_var(1);\n    mat_vec_ptrs(&vec, &mat, 1);\n    argument(&arr1d, 1);\n    argument_nested_x2(&arr2d, 1, 2);\n    argument_nested_x3(&arr3d, 1, 2);\n    index_from_self(&arr1d, 1);\n    local_var_from_arg(array(1, 2, 3, 4), 5);\n    let_binding(&arr1d, 1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg.toml",
    "content": "targets = \"METAL | GLSL | HLSL | WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointer-function-arg.wgsl",
    "content": "fn takes_ptr(p: ptr<function, i32>) {}\nfn takes_array_ptr(p: ptr<function, array<i32, 4>>) {}\nfn takes_vec_ptr(p: ptr<function, vec2<i32>>) {}\nfn takes_mat_ptr(p: ptr<function, mat2x2<f32>>) {}\n\nfn local_var(i: u32) {\n    var arr = array(1, 2, 3, 4);\n    takes_ptr(&arr[i]);\n    takes_array_ptr(&arr);\n\n}\n\nfn mat_vec_ptrs(\n    pv: ptr<function, array<vec2<i32>, 4>>,\n    pm: ptr<function, array<mat2x2<f32>, 4>>,\n    i: u32,\n) {\n    takes_vec_ptr(&pv[i]);\n    takes_mat_ptr(&pm[i]);\n}\n\nfn argument(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[i]);\n}\n\nfn argument_nested_x2(v: ptr<function, array<array<i32, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][j]);\n\n    // Mixing compile and runtime bounds checks\n    takes_ptr(&v[i][0]);\n    takes_ptr(&v[0][j]);\n\n    takes_array_ptr(&v[i]);\n}\n\nfn argument_nested_x3(v: ptr<function, array<array<array<i32, 4>, 4>, 4>>, i: u32, j: u32) {\n    takes_ptr(&v[i][0][j]);\n    takes_ptr(&v[i][j][0]);\n    takes_ptr(&v[0][i][j]);\n}\n\nfn index_from_self(v: ptr<function, array<i32, 4>>, i: u32) {\n    takes_ptr(&v[v[i]]);\n}\n\nfn local_var_from_arg(a: array<i32, 4>, i: u32) {\n    var b = a;\n    takes_ptr(&b[i]);\n}\n\nfn let_binding(a: ptr<function, array<i32, 4>>, i: u32) {\n    let p0 = &a[i];\n    takes_ptr(p0);\n\n    let p1 = &a[0];\n    takes_ptr(p1);\n}\n\n// Runtime-sized arrays can only appear in storage buffers, while (in the base\n// language) pointers can only appear in function or private space, so there\n// is no interaction to test.\n\n@compute @workgroup_size(1)\nfn main() {\n    var vec: array<vec2<i32>, 4>;\n    var mat: array<mat2x2<f32>, 4>;\n    var arr1d: array<i32, 4>;\n    var arr2d: array<array<i32, 4>, 4>;\n    var arr3d: array<array<array<i32, 4>, 4>, 4>;\n    local_var(1);\n    mat_vec_ptrs(&vec, &mat, 1);\n    argument(&arr1d, 1);\n    argument_nested_x2(&arr2d, 1, 2);\n    argument_nested_x3(&arr3d, 1, 2);\n    index_from_self(&arr1d, 1);\n    local_var_from_arg(array(1, 2, 3, 4), 5);\n    let_binding(&arr1d, 1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointers.toml",
    "content": "bounds_check_policies = []\ntargets = \"SPIRV | WGSL\"\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/pointers.wgsl",
    "content": "fn f() {\n   var v: mat2x2<f32>;\n   let px = &v[0];\n   *px = vec2<f32>(10.0);\n}\n\nstruct DynamicArray {\n    arr: array<u32>\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> dynamic_array: DynamicArray;\n\nfn index_unsized(i: i32, v: u32) {\n   let p: ptr<storage, DynamicArray, read_write> = &dynamic_array;\n\n   let val = (*p).arr[i];\n   (*p).arr[i] = val + v;\n}\n\nfn index_dynamic_array(i: i32, v: u32) {\n   let p: ptr<storage, array<u32>, read_write> = &dynamic_array.arr;\n\n   let val = (*p)[i];\n   (*p)[i] = val + v;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    f();\n    index_unsized(1, 1);\n    index_dynamic_array(1, 1);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/policy-mix.toml",
    "content": "targets = \"SPIRV | METAL\"\n\n[bounds_check_policies]\nindex = \"Restrict\"\nbuffer = \"Unchecked\"\nimage_load = \"ReadZeroSkipWrite\"\n\n[spv]\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/policy-mix.wgsl",
    "content": "// Tests that the index, buffer, and texture bounds checks policies are\n// implemented separately.\n\n// Storage and Uniform storage classes\nstruct InStorage {\n  a: array<vec4<f32>, 10>\n}\n@group(0) @binding(0) var<storage> in_storage: InStorage;\n\nstruct InUniform {\n  a: array<vec4<f32>, 20>\n}\n@group(0) @binding(1) var<uniform> in_uniform: InUniform;\n\n// Textures automatically land in the `handle` storage class.\n@group(0) @binding(2) var image_2d_array: texture_2d_array<f32>;\n\n// None of the above.\nvar<workgroup> in_workgroup: array<f32, 30>;\nvar<private> in_private: array<f32, 40>;\n\nfn mock_function(c: vec2<i32>, i: i32, l: i32) -> vec4<f32> {\n  var in_function: array<vec4<f32>, 2> =\n    array<vec4<f32>, 2>(vec4<f32>(0.707, 0.0, 0.0, 1.0),\n                        vec4<f32>(0.0, 0.707, 0.0, 1.0));\n\n  return (in_storage.a[i] +\n          in_uniform.a[i] +\n          textureLoad(image_2d_array, c, i, l) +\n          in_workgroup[i] +\n          in_private[i] +\n          in_function[i]);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    mock_function(vec2(1, 2), 3, 4);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/primitive-index-mesh.toml",
    "content": "# Tests on GLES\n\ncapabilities = \"PRIMITIVE_INDEX\"\ntargets = \"GLSL\"\n\n[glsl]\nversion.Embedded.version = 320\nversion.Embedded.is_webgl = false\n"
  },
  {
    "path": "naga/tests/in/wgsl/primitive-index-mesh.wgsl",
    "content": "enable primitive_index;\n\n@fragment\nfn func(@builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    return vec4(f32(index), 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/primitive-index.toml",
    "content": "capabilities = \"PRIMITIVE_INDEX\"\ntargets = \"WGSL | SPIRV | HLSL | METAL | GLSL\"\n\n[spv]\nversion = [1, 4]\ncapabilities = [\"Geometry\"]\n\n[glsl]\nversion.Desktop = 450\n\n[msl]\nlang_version = [2, 3]\n"
  },
  {
    "path": "naga/tests/in/wgsl/primitive-index.wgsl",
    "content": "enable primitive_index;\n\n@fragment\nfn func(@builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    return vec4(f32(index), 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/push-constants.toml",
    "content": "capabilities = \"IMMEDIATES\"\ntargets = \"GLSL | HLSL\"\n\n[hlsl]\nfake_missing_bindings = true\nimmediates_target = { register = 0, space = 0 }\nrestrict_indexing = true\nspecial_constants_binding = { register = 0, space = 1 }\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Embedded = { is_webgl = false, version = 320 }\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/push-constants.wgsl",
    "content": "struct ImmediateData {\n    multiplier: f32\n}\nvar<immediate> im: ImmediateData;\n\nstruct FragmentIn {\n    @location(0) color: vec4<f32>\n}\n\n@vertex\nfn vert_main(\n  @location(0) pos : vec2<f32>,\n  @builtin(instance_index) ii: u32,\n  @builtin(vertex_index) vi: u32,\n) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(f32(ii) * f32(vi) * im.multiplier * pos, 0.0, 1.0);\n}\n\n@fragment\nfn main(in: FragmentIn) -> @location(0) vec4<f32> {\n    return in.color * im.multiplier;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/quad.toml",
    "content": "targets = \"SPIRV | METAL | GLSL | DOT | HLSL | WGSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = false, version = 300 }\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n\n[spv]\nadjust_coordinate_space = true\ndebug = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/quad.wgsl",
    "content": "// vertex\nconst c_scale: f32 = 1.2;\n\nstruct VertexOutput {\n  @location(0) uv : vec2<f32>,\n  @builtin(position) position : vec4<f32>,\n}\n\n@vertex\nfn vert_main(\n  @location(0) pos : vec2<f32>,\n  @location(1) uv : vec2<f32>,\n) -> VertexOutput {\n  return VertexOutput(uv, vec4<f32>(c_scale * pos, 0.0, 1.0));\n}\n\n// fragment\n@group(0) @binding(0) var u_texture : texture_2d<f32>;\n@group(0) @binding(1) var u_sampler : sampler;\n\n@fragment\nfn frag_main(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {\n  let color = textureSample(u_texture, u_sampler, uv);\n  if color.a == 0.0 {\n    discard;\n  }\n  // forcing the expression here to be emitted in order to check the\n  // uniformity of the control flow a bit more strongly.\n  let premultiplied = color.a * color;\n  return premultiplied;\n}\n\n\n// We need to make sure that backends are successfully handling multiple entry points for the same shader stage.\n@fragment\nfn fs_extra() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0, 0.5, 0.0, 0.5);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-query-no-init-tracking.toml",
    "content": "capabilities = \"RAY_QUERY\"\ntargets = \"SPIRV | METAL | HLSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = false\n\n[hlsl]\nshader_model = \"V6_5\"\nfake_missing_bindings = true\nzero_initialize_workgroup_memory = true\nray_query_initialization_tracking = false\n\n[spv]\nversion = [1, 4]\nray_query_initialization_tracking = false\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-query-no-init-tracking.wgsl",
    "content": "/*\nlet RAY_FLAG_NONE = 0x00u;\nlet RAY_FLAG_FORCE_OPAQUE = 0x01u;\nlet RAY_FLAG_FORCE_NO_OPAQUE = 0x02u;\nlet RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u;\nlet RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u;\nlet RAY_FLAG_CULL_BACK_FACING = 0x10u;\nlet RAY_FLAG_CULL_FRONT_FACING = 0x20u;\nlet RAY_FLAG_CULL_OPAQUE = 0x40u;\nlet RAY_FLAG_CULL_NO_OPAQUE = 0x80u;\nlet RAY_FLAG_SKIP_TRIANGLES = 0x100u;\nlet RAY_FLAG_SKIP_AABBS = 0x200u;\n\nlet RAY_QUERY_INTERSECTION_NONE = 0u;\nlet RAY_QUERY_INTERSECTION_TRIANGLE = 1u;\nlet RAY_QUERY_INTERSECTION_GENERATED = 2u;\nlet RAY_QUERY_INTERSECTION_AABB = 3u;\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    t_min: f32,\n    t_max: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    kind: u32,\n    t: f32,\n    instance_custom_data: u32,\n    instance_index: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: bool,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n*/\n\nenable wgpu_ray_query;\n\nfn query_loop(pos: vec3<f32>, dir: vec3<f32>, acs: acceleration_structure) -> RayIntersection {\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acs, RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT, 0xFFu, 0.1, 100.0, pos, dir));\n\n    while (rayQueryProceed(&rq)) {}\n\n    return rayQueryGetCommittedIntersection(&rq);\n}\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\nstruct Output {\n    visible: u32,\n    normal: vec3<f32>,\n}\n\n@group(0) @binding(1)\nvar<storage, read_write> output: Output;\n\nfn get_torus_normal(world_point: vec3<f32>, intersection: RayIntersection) -> vec3<f32> {\n    let local_point = intersection.world_to_object * vec4<f32>(world_point, 1.0);\n    let point_on_guiding_line = normalize(local_point.xy) * 2.4;\n    let world_point_on_guiding_line = intersection.object_to_world * vec4<f32>(point_on_guiding_line, 0.0, 1.0);\n    return normalize(world_point - world_point_on_guiding_line);\n}\n\n\n\n@compute @workgroup_size(1)\nfn main() {\n    let pos = vec3<f32>(0.0);\n    let dir = vec3<f32>(0.0, 1.0, 0.0);\n    let intersection = query_loop(pos, dir, acc_struct);\n\n    output.visible = u32(intersection.kind == RAY_QUERY_INTERSECTION_NONE);\n    output.normal = get_torus_normal(dir * intersection.t, intersection);\n}\n\n@compute @workgroup_size(1)\nfn main_candidate() {\n    let pos = vec3<f32>(0.0);\n    let dir = vec3<f32>(0.0, 1.0, 0.0);\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT, 0xFFu, 0.1, 100.0, pos, dir));\n    let intersection = rayQueryGetCandidateIntersection(&rq);\n    if (intersection.kind == RAY_QUERY_INTERSECTION_AABB) {\n        rayQueryGenerateIntersection(&rq, 10.0);\n    } else if (intersection.kind == RAY_QUERY_INTERSECTION_TRIANGLE) {\n        rayQueryConfirmIntersection(&rq);\n    } else {\n        rayQueryTerminate(&rq);\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-query.toml",
    "content": "capabilities = \"RAY_QUERY\"\ntargets = \"SPIRV | METAL | HLSL\"\n\n[msl]\nfake_missing_bindings = true\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = false\n\n[hlsl]\nshader_model = \"V6_5\"\nfake_missing_bindings = true\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 4]\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-query.wgsl",
    "content": "/*\nlet RAY_FLAG_NONE = 0x00u;\nlet RAY_FLAG_FORCE_OPAQUE = 0x01u;\nlet RAY_FLAG_FORCE_NO_OPAQUE = 0x02u;\nlet RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u;\nlet RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u;\nlet RAY_FLAG_CULL_BACK_FACING = 0x10u;\nlet RAY_FLAG_CULL_FRONT_FACING = 0x20u;\nlet RAY_FLAG_CULL_OPAQUE = 0x40u;\nlet RAY_FLAG_CULL_NO_OPAQUE = 0x80u;\nlet RAY_FLAG_SKIP_TRIANGLES = 0x100u;\nlet RAY_FLAG_SKIP_AABBS = 0x200u;\n\nlet RAY_QUERY_INTERSECTION_NONE = 0u;\nlet RAY_QUERY_INTERSECTION_TRIANGLE = 1u;\nlet RAY_QUERY_INTERSECTION_GENERATED = 2u;\nlet RAY_QUERY_INTERSECTION_AABB = 3u;\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    t_min: f32,\n    t_max: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nstruct RayIntersection {\n    kind: u32,\n    t: f32,\n    instance_custom_data: u32,\n    instance_index: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: bool,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n*/\n\nenable wgpu_ray_query;\n\nfn query_loop(pos: vec3<f32>, dir: vec3<f32>, acs: acceleration_structure) -> RayIntersection {\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acs, RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT, 0xFFu, 0.1, 100.0, pos, dir));\n\n    while (rayQueryProceed(&rq)) {}\n\n    return rayQueryGetCommittedIntersection(&rq);\n}\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\nstruct Output {\n    visible: u32,\n    normal: vec3<f32>,\n}\n\n@group(0) @binding(1)\nvar<storage, read_write> output: Output;\n\nfn get_torus_normal(world_point: vec3<f32>, intersection: RayIntersection) -> vec3<f32> {\n    let local_point = intersection.world_to_object * vec4<f32>(world_point, 1.0);\n    let point_on_guiding_line = normalize(local_point.xy) * 2.4;\n    let world_point_on_guiding_line = intersection.object_to_world * vec4<f32>(point_on_guiding_line, 0.0, 1.0);\n    return normalize(world_point - world_point_on_guiding_line);\n}\n\n\n\n@compute @workgroup_size(1)\nfn main() {\n    let pos = vec3<f32>(0.0);\n    let dir = vec3<f32>(0.0, 1.0, 0.0);\n    let intersection = query_loop(pos, dir, acc_struct);\n\n    output.visible = u32(intersection.kind == RAY_QUERY_INTERSECTION_NONE);\n    output.normal = get_torus_normal(dir * intersection.t, intersection);\n}\n\n@compute @workgroup_size(1)\nfn main_candidate() {\n    let pos = vec3<f32>(0.0);\n    let dir = vec3<f32>(0.0, 1.0, 0.0);\n\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT, 0xFFu, 0.1, 100.0, pos, dir));\n    let intersection = rayQueryGetCandidateIntersection(&rq);\n    if (intersection.kind == RAY_QUERY_INTERSECTION_AABB) {\n        rayQueryGenerateIntersection(&rq, 10.0);\n    } else if (intersection.kind == RAY_QUERY_INTERSECTION_TRIANGLE) {\n        rayQueryConfirmIntersection(&rq);\n    } else {\n        rayQueryTerminate(&rq);\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-tracing-pipeline.toml",
    "content": "capabilities = \"RAY_TRACING_PIPELINE\"\ntargets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/ray-tracing-pipeline.wgsl",
    "content": "enable wgpu_ray_tracing_pipeline;\n\nstruct HitCounters {\n    hit_num: u32,\n    selected_hit: u32,\n}\n\nvar<ray_payload> hit_num: HitCounters;\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\n@ray_generation\nfn ray_gen_main(@builtin(ray_invocation_id) id: vec3<u32>, @builtin(num_ray_invocations) num_invocations: vec3<u32>) {\n    hit_num = HitCounters();\n    let shift = vec3<f32>(id) / vec3<f32>(num_invocations);\n    let ray_shift = (vec3(shift.x, 0.0, shift.y) * 2.0) - 1.0;\n    traceRay(acc_struct, RayDesc(RAY_FLAG_NONE, 0xff, 0.01, 100.0, vec3(0.0), vec3(0.0, 1.0, 0.0) + ray_shift), &hit_num);\n}\n\nvar<incoming_ray_payload> incoming_hit_num: HitCounters;\n\n@miss\n@incoming_payload(incoming_hit_num)\nfn miss(@builtin(world_ray_origin) origin: vec3<f32>, @builtin(world_ray_direction) dir: vec3<f32>, @builtin(ray_t_min) t_min: f32) {}\n\n@any_hit\n@incoming_payload(incoming_hit_num)\nfn any_hit_main(@builtin(instance_custom_data) data: u32, @builtin(geometry_index) geo_idx: u32, @builtin(ray_t_current_max) max: f32, @builtin(hit_kind) kind: u32) {\n    incoming_hit_num.hit_num++;\n    incoming_hit_num.selected_hit = data;\n}\n\n@closest_hit\n@incoming_payload(incoming_hit_num)\nfn closest_hit_main(@builtin(object_ray_origin) origin: vec3<f32>, @builtin(object_ray_direction) dir: vec3<f32>, @builtin(object_to_world) obj_to_world: mat4x3<f32>, @builtin(world_to_object) world_to_obj: mat4x3<f32>) {}"
  },
  {
    "path": "naga/tests/in/wgsl/resource-binding-map.toml",
    "content": "targets = \"METAL\"\n\n[bounds_check_policies]\nindex = \"ReadZeroSkipWrite\"\nbuffer = \"ReadZeroSkipWrite\"\nimage_load = \"ReadZeroSkipWrite\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [1, 0]\n\n[msl.per_entry_point_map.entry_point_one]\nresources = [\n    { bind_target = { texture = 0 }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { sampler.Inline = 0 }, resource_binding = { group = 0, binding = 2 } },\n    { bind_target = { buffer = 0 }, resource_binding = { group = 0, binding = 4 } },\n]\n\n[msl.per_entry_point_map.entry_point_two]\nresources = [\n    { bind_target = { texture = 0 }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 2 } },\n    { bind_target = { buffer = 0 }, resource_binding = { group = 0, binding = 4 } },\n]\n\n[msl.per_entry_point_map.entry_point_three]\nresources = [\n    { bind_target = { texture = 0 }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { buffer = 1 }, resource_binding = { group = 1, binding = 0 } },\n    { bind_target = { texture = 1 }, resource_binding = { group = 0, binding = 1 } },\n    { bind_target = { sampler.Inline = 0 }, resource_binding = { group = 0, binding = 2 } },\n    { bind_target = { sampler.Resource = 1 }, resource_binding = { group = 0, binding = 3 } },\n    { bind_target = { buffer = 0 }, resource_binding = { group = 0, binding = 4 } },\n]\n\n[[msl.inline_samplers]]\ncoord = \"Normalized\"\naddress = [\"ClampToEdge\", \"ClampToEdge\", \"ClampToEdge\"]\nmag_filter = \"Linear\"\nmin_filter = \"Linear\"\nborder_color = \"TransparentBlack\"\ncompare_func = \"Never\"\nlod_clamp = { start = 0.5, end = 10.0 }\nmax_anisotropy = 8\n"
  },
  {
    "path": "naga/tests/in/wgsl/resource-binding-map.wgsl",
    "content": "@group(0) @binding(0) var t1: texture_2d<f32>;\n@group(0) @binding(1) var t2: texture_2d<f32>;\n@group(0) @binding(2) var s1: sampler;\n@group(0) @binding(3) var s2: sampler;\n\n@group(0) @binding(4) var<uniform> uniformOne: vec2<f32>;\n@group(1) @binding(0) var<uniform> uniformTwo: vec2<f32>;\n\n@fragment\nfn entry_point_one(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {\n    return textureSample(t1, s1, pos.xy);\n}\n\n@fragment\nfn entry_point_two() -> @location(0) vec4<f32> {\n    return textureSample(t1, s1, uniformOne);\n}\n\n@fragment\nfn entry_point_three() -> @location(0) vec4<f32> {\n    return textureSample(t1, s1, uniformTwo + uniformOne) +\n           textureSample(t2, s2, uniformOne);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/sample-cube-array-depth-lod.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = false, version = 320 }\nwriter_flags = \"TEXTURE_SHADOW_LOD\"\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/sample-cube-array-depth-lod.wgsl",
    "content": "// see https://github.com/gfx-rs/wgpu/issues/4455\n\n@group(0) @binding(0) var texture: texture_depth_cube_array;\n@group(0) @binding(1) var texture_sampler: sampler_comparison;\n\n@fragment\nfn main() -> @location(0) f32  {\n    let pos = vec3<f32>(0.0);\n    let array_index: i32 = 0;\n    let depth: f32 = 0.0;\n    return textureSampleCompareLevel(texture, texture_sampler, pos, array_index, depth);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/select.wgsl",
    "content": "const_assert select(0xdeadbeef, 42f, false) == 0xdeadbeef;\nconst_assert select(0xdeadbeefu, 42, false) == 0xdeadbeefu;\nconst_assert select(0xdeadi, 42, false) == 0xdeadi;\n\nconst_assert select(42f, 0xdeadbeef, true) == 0xdeadbeef;\nconst_assert select(42, 0xdeadbeefu, true) == 0xdeadbeefu;\nconst_assert select(42, 0xdeadi, true) == 0xdeadi;\n\nconst_assert select(42f, 9001, true) == 9001;\nconst_assert select(42f, 9001, true) == 9001f;\nconst_assert select(42, 9001i, true) == 9001;\nconst_assert select(42, 9001u, true) == 9001;\n\nconst_assert select(9001, 42f, false) == 9001;\nconst_assert select(9001, 42f, false) == 9001f;\nconst_assert select(9001i, 42, false) == 9001;\nconst_assert select(9001u, 42, false) == 9001;\n\nconst_assert !select(false, true, false);\nconst_assert select(false, true, true);\nconst_assert select(true, false, false);\nconst_assert !select(true, false, true);\n\nconst_assert all(select(vec2(2f), vec2(), true) == vec2(0));\nconst_assert all(select(vec2(1), vec2(2f), false) == vec2(1));\nconst_assert all(select(vec2(1), vec2(2f), false) == vec2(1));\nconst_assert all(select(vec2(1), vec2(2f), vec2(false, false)) == vec2(1));\nconst_assert all(select(vec2(1), vec2(2f), vec2(true)) == vec2(2));\nconst_assert all(select(vec2(1), vec2(2f), vec2(true)) == vec2(2));\nconst_assert all(select(vec2(1), vec2(2f), vec2(true, false)) == vec2(2, 1));\n\nconst_assert all(select(vec3(1), vec3(2f), vec3(true)) == vec3(2));\nconst_assert all(select(vec4(1), vec4(2f), vec4(true)) == vec4(2));\n\n@compute @workgroup_size(1, 1)\nfn main() {\n    _ = select(1, 2f, false);\n\n    var x0 = vec2(1, 2);\n    var i1: vec2<f32> = select(vec2<f32>(1., 0.), vec2<f32>(0., 1.), (x0.x < x0.y));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/separate-entry-points.toml",
    "content": "targets = \"SPIRV | GLSL\"\n\n[spv]\nseparate_entry_points = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/separate-entry-points.wgsl",
    "content": "// only available in the fragment stage\nfn derivatives() {\n    let x = dpdx(0.0);\n    let y = dpdy(0.0);\n    let width = fwidth(0.0);\n}\n\n// only available in the compute stage\nfn barriers() {\n    storageBarrier();\n    workgroupBarrier();\n    textureBarrier();\n}\n\n@fragment\nfn fragment() -> @location(0) vec4<f32> {\n    derivatives();\n    return vec4<f32>();\n}\n\n@compute @workgroup_size(1)\nfn compute() {\n    barriers();\n}"
  },
  {
    "path": "naga/tests/in/wgsl/shadow.toml",
    "content": "[spv]\nadjust_coordinate_space = true\ndebug = true\nversion = [1, 2]\n"
  },
  {
    "path": "naga/tests/in/wgsl/shadow.wgsl",
    "content": "struct Globals {\n    view_proj: mat4x4<f32>,\n    num_lights: vec4<u32>,\n}\n\n@group(0)\n@binding(0)\nvar<uniform> u_globals: Globals;\n\nstruct Entity {\n    world: mat4x4<f32>,\n    color: vec4<f32>,\n}\n\n@group(1)\n@binding(0)\nvar<uniform> u_entity: Entity;\n\n/* Not useful for testing\n@vertex\nfn vs_bake(@location(0) position: vec4<i32>) -> @builtin(position) vec4<f32> {\n    return u_globals.view_proj * u_entity.world * vec4<f32>(position);\n}\n*/\n\nstruct VertexOutput {\n    @builtin(position) proj_position: vec4<f32>,\n    @location(0) world_normal: vec3<f32>,\n    @location(1) world_position: vec4<f32>,\n}\n\n@vertex\nfn vs_main(\n    @location(0) position: vec4<i32>,\n    @location(1) normal: vec4<i32>,\n) -> VertexOutput {\n    let w = u_entity.world;\n    let world_pos = u_entity.world * vec4<f32>(position);\n    var out: VertexOutput;\n    out.world_normal = mat3x3<f32>(w[0].xyz, w[1].xyz, w[2].xyz) * vec3<f32>(normal.xyz);\n    out.world_position = world_pos;\n    out.proj_position = u_globals.view_proj * world_pos;\n    return out;\n}\n\n// fragment shader\n\nstruct Light {\n    proj: mat4x4<f32>,\n    pos: vec4<f32>,\n    color: vec4<f32>,\n}\n\n@group(0)\n@binding(1)\nvar<storage, read> s_lights: array<Light>;\n@group(0)\n@binding(1)\nvar<uniform> u_lights: array<Light, 10>; // Used when storage types are not supported\n@group(0)\n@binding(2)\nvar t_shadow: texture_depth_2d_array;\n@group(0)\n@binding(3)\nvar sampler_shadow: sampler_comparison;\n\nfn fetch_shadow(light_id: u32, homogeneous_coords: vec4<f32>) -> f32 {\n    if (homogeneous_coords.w <= 0.0) {\n        return 1.0;\n    }\n    // compensate for the Y-flip difference between the NDC and texture coordinates\n    let flip_correction = vec2<f32>(0.5, -0.5);\n    // compute texture coordinates for shadow lookup\n    let proj_correction = 1.0 / homogeneous_coords.w;\n    let light_local = homogeneous_coords.xy * flip_correction * proj_correction + vec2<f32>(0.5, 0.5);\n    // do the lookup, using HW PCF and comparison\n    return textureSampleCompareLevel(t_shadow, sampler_shadow, light_local, i32(light_id), homogeneous_coords.z * proj_correction);\n}\n\nconst c_ambient: vec3<f32> = vec3<f32>(0.05, 0.05, 0.05);\nconst c_max_lights: u32 = 10u;\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(in.world_normal);\n    // accumulate color\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i++) {\n        let light = s_lights[i];\n        // project into the light space\n        let shadow = fetch_shadow(i, light.proj * in.world_position);\n        // compute Lambertian diffuse term\n        let light_dir = normalize(light.pos.xyz - in.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        // add light contribution\n        color += shadow * diffuse * light.color.xyz;\n    }\n    // multiply the light by material color\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n\n// The fragment entrypoint used when storage buffers are not available for the lights\n@fragment\nfn fs_main_without_storage(in: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(in.world_normal);\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i++) {\n        // This line is the only difference from the entrypoint above. It uses the lights\n        // uniform instead of the lights storage buffer\n        let light = u_lights[i];\n        let shadow = fetch_shadow(i, light.proj * in.world_position);\n        let light_dir = normalize(light.pos.xyz - in.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        color += shadow * diffuse * light.color.xyz;\n    }\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/skybox.toml",
    "content": "spv_flow_dump_prefix = \"\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 1]\nzero_initialize_workgroup_memory = true\n\n[[msl.inline_samplers]]\ncoord = \"Normalized\"\naddress = [\"ClampToEdge\", \"ClampToEdge\", \"ClampToEdge\"]\nmag_filter = \"Linear\"\nmin_filter = \"Linear\"\nborder_color = \"TransparentBlack\"\ncompare_func = \"Never\"\nlod_clamp = { start = 0.5, end = 10.0 }\nmax_anisotropy = 8\n\n[msl.per_entry_point_map.fs_main]\nresources = [\n    { resource_binding = { group = 0, binding = 1 }, bind_target = { texture = 0 } },\n    { resource_binding = { group = 0, binding = 2 }, bind_target = { sampler.Inline = 0 } },\n]\n[msl.per_entry_point_map.vs_main]\nresources = [\n    { resource_binding = { group = 0, binding = 0 }, bind_target = { buffer = 0 } },\n]\n\n[hlsl]\nfake_missing_bindings = false\nrestrict_indexing = true\nsampler_buffer_binding_map = [\n    { group = 0, bind_target = { register = 0, space = 2 } },\n]\nspecial_constants_binding = { register = 1, space = 0 }\nzero_initialize_workgroup_memory = true\nbinding_map = [\n    { resource_binding = { group = 0, binding = 0 }, bind_target = { register = 0, space = 0 } },\n    { resource_binding = { group = 0, binding = 1 }, bind_target = { register = 0, space = 0 } },\n    { resource_binding = { group = 0, binding = 2 }, bind_target = { register = 0, space = 1 } },\n]\n\n[glsl]\nbinding_map = [\n    { resource_binding = { group = 0, binding = 0 }, bind_target = 0 },\n    { resource_binding = { group = 0, binding = 1 }, bind_target = 0 },\n]\nversion.Embedded = { is_webgl = false, version = 320 }\nwriter_flags = \"\"\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/skybox.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) uv: vec3<f32>,\n}\n\nstruct Data {\n    proj_inv: mat4x4<f32>,\n    view: mat4x4<f32>,\n}\n@group(0) @binding(0)\nvar<uniform> r_data: Data;\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    // hacky way to draw a large triangle\n    var tmp1 = i32(vertex_index) / 2;\n    var tmp2 = i32(vertex_index) & 1;\n    let pos = vec4<f32>(\n        f32(tmp1) * 4.0 - 1.0,\n        f32(tmp2) * 4.0 - 1.0,\n        0.0,\n        1.0,\n    );\n\n    let inv_model_view = transpose(mat3x3<f32>(r_data.view[0].xyz, r_data.view[1].xyz, r_data.view[2].xyz));\n    let unprojected = r_data.proj_inv * pos;\n    return VertexOutput(pos, inv_model_view * unprojected.xyz);\n}\n\n@group(0) @binding(1)\nvar r_texture: texture_cube<f32>;\n@group(0) @binding(2)\nvar r_sampler: sampler;\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(r_texture, r_sampler, in.uv);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/sprite.toml",
    "content": "targets = \"SPIRV\"\n\n[spv]\nversion = [1, 4]\n"
  },
  {
    "path": "naga/tests/in/wgsl/sprite.wgsl",
    "content": "@group(0) @binding(0) var u_texture : texture_2d<f32>;\n@group(0) @binding(1) var u_sampler : sampler;\n\n@fragment\nfn main(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {\n  return textureSample(u_texture, u_sampler, uv);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/standard.wgsl",
    "content": "// Standard functions.\n\nfn test_any_and_all_for_bool() -> bool {\n    let a = any(true);\n    return all(a);\n}\n\n\n@fragment\nfn derivatives(@builtin(position) foo: vec4<f32>) -> @location(0) vec4<f32> {\n    var x = dpdxCoarse(foo);\n    var y = dpdyCoarse(foo);\n    var z = fwidthCoarse(foo);\n\n    x = dpdxFine(foo);\n    y = dpdyFine(foo);\n    z = fwidthFine(foo);\n\n    x = dpdx(foo);\n    y = dpdy(foo);\n    z = fwidth(foo);\n\n    let a = test_any_and_all_for_bool();\n\n    return (x + y) * z;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/storage-textures.toml",
    "content": "targets = \"IR | ANALYSIS | SPIRV | METAL | HLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/storage-textures.wgsl",
    "content": "@group(0) @binding(0) var s_r_r: texture_storage_2d<r32float, read>;\n@group(0) @binding(1) var s_rg_r: texture_storage_2d<rg32float, read>;\n@group(0) @binding(2) var s_rgba_r: texture_storage_2d<rgba32float, read>;\n@compute @workgroup_size(1) fn csLoad() {\n    _ = textureLoad(s_r_r, vec2u(0));\n    _ = textureLoad(s_rg_r, vec2u(0));\n    _ = textureLoad(s_rgba_r, vec2u(0));\n}\n\n@group(1) @binding(0) var s_r_w: texture_storage_2d<r32float, write>;\n@group(1) @binding(1) var s_rg_w: texture_storage_2d<rg32float, write>;\n@group(1) @binding(2) var s_rgba_w: texture_storage_2d<rgba32float, write>;\n@compute @workgroup_size(1) fn csStore() {\n    textureStore(s_r_w, vec2u(0), vec4f(0.0));\n    textureStore(s_rg_w, vec2u(0), vec4f(0.0));\n    textureStore(s_rgba_w, vec2u(0), vec4f(0.0));\n}"
  },
  {
    "path": "naga/tests/in/wgsl/struct-layout.wgsl",
    "content": "// Create several type definitions to test `align` and `size` layout.\n\nstruct NoPadding {\n\t@location(0)\n\tv3: vec3f, // align 16, size 12; no start padding needed\n\t@location(1)\n\tf3: f32, // align 4, size 4; no start padding needed\n}\n@fragment\nfn no_padding_frag(input: NoPadding) -> @location(0) vec4f {\n\t_ = input;\n\treturn vec4f(0.0);\n}\n@vertex\nfn no_padding_vert(input: NoPadding) -> @builtin(position) vec4f {\n\t_ = input;\n\treturn vec4f(0.0);\n}\n@group(0) @binding(0) var<uniform> no_padding_uniform: NoPadding;\n@group(0) @binding(1) var<storage, read_write> no_padding_storage: NoPadding;\n@compute @workgroup_size(16,1,1)\nfn no_padding_comp() {\n\tvar x: NoPadding;\n\tx = no_padding_uniform;\n\tx = no_padding_storage;\n}\n\nstruct NeedsPadding {\n\t@location(0) f3_forces_padding: f32, // align 4, size 4; no start padding needed\n\t@location(1) v3_needs_padding: vec3f, // align 16, size 12; needs 12 bytes of padding\n\t@location(2) f3: f32, // align 4, size 4; no start padding needed\n}\n@fragment\nfn needs_padding_frag(input: NeedsPadding) -> @location(0) vec4f {\n\t_ = input;\n\treturn vec4f(0.0);\n}\n@vertex\nfn needs_padding_vert(input: NeedsPadding) -> @builtin(position) vec4f {\n\t_ = input;\n\treturn vec4f(0.0);\n}\n@group(0) @binding(2) var<uniform> needs_padding_uniform: NeedsPadding;\n@group(0) @binding(3) var<storage, read_write> needs_padding_storage: NeedsPadding;\n@compute @workgroup_size(16,1,1)\nfn needs_padding_comp() {\n\tvar x: NeedsPadding;\n\tx = needs_padding_uniform;\n\tx = needs_padding_storage;\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/subgroup-barrier.toml",
    "content": "capabilities = \"SUBGROUP | SUBGROUP_BARRIER\"\ntargets = \"WGSL | SPIRV | METAL\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_0\"\nfake_missing_bindings = true\nrestrict_indexing = true\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 3]\n"
  },
  {
    "path": "naga/tests/in/wgsl/subgroup-barrier.wgsl",
    "content": "@compute @workgroup_size(1)\nfn main() {\n    subgroupBarrier();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/subgroup-operations.toml",
    "content": "capabilities = \"SUBGROUP\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [2, 4]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[hlsl]\nshader_model = \"V6_0\"\nfake_missing_bindings = true\nrestrict_indexing = true\nzero_initialize_workgroup_memory = true\n\n[glsl]\nversion.Desktop = 430\nzero_initialize_workgroup_memory = true\n\n[spv]\nversion = [1, 3]\n"
  },
  {
    "path": "naga/tests/in/wgsl/subgroup-operations.wgsl",
    "content": "struct Structure {\n    @builtin(num_subgroups) num_subgroups: u32,\n    @builtin(subgroup_size) subgroup_size: u32,\n};\n\n@compute @workgroup_size(1)\nfn main(\n    sizes: Structure,\n    @builtin(subgroup_id) subgroup_id: u32,\n    @builtin(subgroup_invocation_id) subgroup_invocation_id: u32,\n) {\n    _ = subgroupBallot((subgroup_invocation_id & 1u) == 1u);\n    _ = subgroupBallot();\n\n    _ = subgroupAll(subgroup_invocation_id != 0u);\n    _ = subgroupAny(subgroup_invocation_id == 0u);\n    _ = subgroupAdd(subgroup_invocation_id);\n    _ = subgroupMul(subgroup_invocation_id);\n    _ = subgroupMin(subgroup_invocation_id);\n    _ = subgroupMax(subgroup_invocation_id);\n    _ = subgroupAnd(subgroup_invocation_id);\n    _ = subgroupOr(subgroup_invocation_id);\n    _ = subgroupXor(subgroup_invocation_id);\n    _ = subgroupExclusiveAdd(subgroup_invocation_id);\n    _ = subgroupExclusiveMul(subgroup_invocation_id);\n    _ = subgroupInclusiveAdd(subgroup_invocation_id);\n    _ = subgroupInclusiveMul(subgroup_invocation_id);\n\n    _ = subgroupBroadcastFirst(subgroup_invocation_id);\n    _ = subgroupBroadcast(subgroup_invocation_id, 4u);\n    _ = subgroupShuffle(subgroup_invocation_id, sizes.subgroup_size - 1u - subgroup_invocation_id);\n    _ = subgroupShuffleDown(subgroup_invocation_id, 1u);\n    _ = subgroupShuffleUp(subgroup_invocation_id, 1u);\n    _ = subgroupShuffleXor(subgroup_invocation_id, sizes.subgroup_size - 1u);\n\n    _ = quadBroadcast(subgroup_invocation_id, 4u);\n    _ = quadSwapX(subgroup_invocation_id);\n    _ = quadSwapY(subgroup_invocation_id);\n    _ = quadSwapDiagonal(subgroup_invocation_id);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/template-list-ge.toml",
    "content": "targets = \"\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/template-list-ge.wgsl",
    "content": "@group(0) @binding(0)\nvar<storage, read_write> out: array<i32, 2>;\n\n@compute @workgroup_size(1)\nfn main() {\n    var tmp: array<i32, 1 << 1>=array(1, 2);\n    for (var i = 0; i < 2; i++) {\n        out[i] = tmp[i];\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/template-list-trailing-comma.toml",
    "content": "targets = \"IR\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/template-list-trailing-comma.wgsl",
    "content": "var<workgroup> sized_comma: array<u32, 1,>;\nvar<workgroup> sized_no_comma: array<u32, 1>;\n\n@group(0) @binding(0)\nvar<storage, read_write> unsized_comma: array<u32,>;\n\n@group(0) @binding(1)\nvar<storage, read_write> unsized_no_comma: array<u32>;\n\n@compute @workgroup_size(1)\nfn main() {\n    sized_comma[0] = unsized_comma[0];\n    sized_no_comma[0] = unsized_no_comma[0];\n    unsized_no_comma[0] = sized_comma[0] + sized_no_comma[0];\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/texture-arg.toml",
    "content": "[spv]\nadjust_coordinate_space = true\ndebug = true\nversion = [1, 0]\n"
  },
  {
    "path": "naga/tests/in/wgsl/texture-arg.wgsl",
    "content": "@group(0) @binding(0)\nvar Texture: texture_2d<f32>;\n@group(0) @binding(1)\nvar Sampler: sampler;\n\nfn test(Passed_Texture: texture_2d<f32>, Passed_Sampler: sampler) -> vec4<f32> {\n    return textureSample(Passed_Texture, Passed_Sampler, vec2<f32>(0.0, 0.0));\n}\n\n@fragment\nfn main() -> @location(0) vec4<f32> {\n    return test(Texture, Sampler);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/texture-external.toml",
    "content": "capabilities = \"TEXTURE_EXTERNAL\"\ntargets = \"HLSL | IR | METAL | WGSL\"\n\n[msl.per_entry_point_map.compute_main]\nresources = [\n    { bind_target = { external_texture = { planes = [\n        0,\n        1,\n        2,\n    ], params = 0 } }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } },\n]\n\n[msl.per_entry_point_map.fragment_main]\nresources = [\n    { bind_target = { external_texture = { planes = [\n        0,\n        1,\n        2,\n    ], params = 0 } }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } },\n]\n\n[msl.per_entry_point_map.vertex_main]\nresources = [\n    { bind_target = { external_texture = { planes = [\n        0,\n        1,\n        2,\n    ], params = 0 } }, resource_binding = { group = 0, binding = 0 } },\n    { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } },\n]\n\n[[hlsl.binding_map]]\nresource_binding = { group = 0, binding = 1 }\nbind_target = { register = 0, space = 0 }\n\n[[hlsl.external_texture_binding_map]]\nresource_binding = { group = 0, binding = 0 }\nbind_target.planes = [\n    { space = 0, register = 0 },\n    { space = 0, register = 1 },\n    { space = 0, register = 2 },\n]\nbind_target.params = { space = 0, register = 3 }\n"
  },
  {
    "path": "naga/tests/in/wgsl/texture-external.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_external;\n@group(0) @binding(1)\nvar samp: sampler;\n\nfn test(t: texture_external) -> vec4<f32> {\n  var a = textureSampleBaseClampToEdge(t, samp, vec2(0.0f));\n  var b = textureLoad(t, vec2(0i));\n  var c = textureLoad(t, vec2(0u));\n  var d = textureDimensions(t);\n\n  return a + b + c + vec2f(d).xyxy;\n}\n\n@fragment\nfn fragment_main() -> @location(0) vec4<f32> {\n  return test(tex);\n}\n\n@vertex\nfn vertex_main() -> @builtin(position) vec4<f32> {\n  return test(tex);\n}\n\n@compute @workgroup_size(1)\nfn compute_main() {\n  test(tex);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/type-alias.toml",
    "content": "targets = \"WGSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/type-alias.wgsl",
    "content": "alias FVec3 = vec3<f32>;\nalias IVec3 = vec3i;\nalias Mat2 = mat2x2<f32>;\nalias Mat3 = mat3x3f;\nalias I32 = i32;\nalias F32 = f32;\n\n@compute @workgroup_size(1)\nfn main() {\n    let a = FVec3(0.0, 0.0, 0.0);\n    let c = FVec3(0.0);\n    let b = FVec3(vec2<f32>(0.0), 0.0);\n    let d = FVec3(vec2<f32>(0.0), 0.0);\n    let e = IVec3(d);\n\n    let f = Mat2(1.0, 2.0, 3.0, 4.0);\n    let g = Mat3(a, a, a);\n\n    let h = vec2<I32>();\n    let i = mat2x2<F32>();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/type-inference.wgsl",
    "content": "const g0 = 1;\nconst g1 = 1u;\nconst g2 = 1.0;\nconst g3 = 1.0f;\nconst g4 = vec4<i32>();\nconst g5 = vec4(1i);\nconst g6 = mat2x2<f32>(vec2(), vec2());\nconst g7 = mat2x2(vec2(1.0, 1), vec2(1, 1));\n\n@compute @workgroup_size(1)\nfn main() {\n    // Expose some constants that wouldn't otherwise be in the output\n    // because they don't have concrete types.\n    var g0x = g0;\n    var g2x = g2;\n    var g7x = g7;\n\n    const c0 = 1;\n    const c1 = 1u;\n    const c2 = 1.0;\n    const c3 = 1.0f;\n    const c4 = vec4<i32>();\n    const c5 = vec4(1i);\n    const c6 = mat2x2<f32>(vec2(), vec2());\n    const c7 = mat2x2(vec2(1.0, 1), vec2(1, 1));\n\n    // Local constants are not emitted in most cases.\n    // See logic for `Statement::Emit` in `back::wgsl::Writer::write_stmt`.\n    var c0x = c0;\n    var c1x = c1;\n    var c2x = c2;\n    var c3x = c3;\n    var c4x = c4;\n    var c5x = c5;\n    var c6x = c6;\n    var c7x = c7;\n\n    let l0 = 1;\n    let l1 = 1u;\n    let l2 = 1.0;\n    let l3 = 1.0f;\n    let l4 = vec4<i32>();\n    let l5 = vec4(1i);\n    let l6 = mat2x2<f32>(vec2(), vec2());\n    let l7 = mat2x2(vec2(1.0, 1), vec2(1, 1));\n\n    // Let bindings that evaluate to literals or a `ZeroValue` expression are\n    // not emitted. See `ConstantEvaluator::append_expr`. `vec4(1i)` is emitted\n    // because it is translated to a `Splat` expression.\n    var l0x = l0;\n    var l1x = l1;\n    var l2x = l2;\n    var l3x = l3;\n    var l4x = l4;\n\n    var v0 = 1;\n    var v1 = 1u;\n    var v2 = 1.0;\n    var v3 = 1.0f;\n    var v4 = vec4<i32>();\n    var v5 = vec4(1i);\n    var v6 = mat2x2<f32>(vec2(), vec2());\n    var v7 = mat2x2(vec2(1.0, 1), vec2(1, 1));\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/types_with_comments.toml",
    "content": "targets = \"IR\"\nwgsl-in = { parse_doc_comments = true }\n"
  },
  {
    "path": "naga/tests/in/wgsl/types_with_comments.wgsl",
    "content": "//! Module doc comment.\n//! 2nd line of module doc comment.\n\n/**\n 🍽️ /* nested comment */\n */\n@group(0) @binding(0) var<uniform> mvp_matrix: mat4x4<f32>;\n\n/// workgroup var 1 doc comment\n/// 2nd line of workgroup var doc comment\nvar<workgroup> w_mem: mat2x2<f32>;\n\n/// workgroup var 2 doc comment\nvar<workgroup> w_mem2: mat2x2<f32>;\n\n/// constant doc comment\nconst test_c: u32 = 1;\n\n/// struct R doc comment\nstruct TestR {\n    /// member doc comment\n    test_m: u32,\n}\n\n/// struct S doc comment\nstruct TestS {\n    /// member doc comment\n    test_m: u32,\n}\n\n/// function f doc comment\nfn test_f() {}\n\n/// function g doc comment\nfn test_g() {}\n\n/// entry point doc comment\n@compute @workgroup_size(1)\nfn test_ep() {\n    _ = w_mem2;\n    _ = TestS();\n    test_g();\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/unconsumed_vertex_outputs_frag.toml",
    "content": "targets = \"HLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/unconsumed_vertex_outputs_frag.wgsl",
    "content": "// Out of order to test sorting.\nstruct FragmentIn {\n    @location(1) value: f32,\n    @location(3) value2: f32,\n    @builtin(position) position: vec4<f32>,\n    // @location(0) unused_value: f32,\n    // @location(2) unused_value2: vec4<f32>,\n}\n\n@fragment\nfn fs_main(v_out: FragmentIn) -> @location(0) vec4<f32> {\n    return vec4<f32>(v_out.value, v_out.value, v_out.value2, v_out.value2);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/unconsumed_vertex_outputs_vert.toml",
    "content": "fragment_module = { entry_point = \"fs_main\", path = \"unconsumed_vertex_outputs_frag.wgsl\" }\ntargets = \"HLSL\"\n"
  },
  {
    "path": "naga/tests/in/wgsl/unconsumed_vertex_outputs_vert.wgsl",
    "content": "// Out of order to test sorting.\nstruct VertexOut {\n    @builtin(position) position: vec4<f32>,\n    @location(1) value: f32,\n    @location(2) unused_value2: vec4<f32>,\n    @location(0) unused_value: f32,\n    @location(3) value2: f32,\n}\n\n@vertex\nfn vs_main() -> VertexOut {\n    return VertexOut(vec4(1.0), 1.0, vec4(2.0), 1.0, 0.5);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/use-gl-ext-over-grad-workaround-if-instructed.toml",
    "content": "targets = \"GLSL\"\n\n[glsl]\nversion.Embedded = { is_webgl = false, version = 320 }\nwriter_flags = \"TEXTURE_SHADOW_LOD\"\nzero_initialize_workgroup_memory = true\n"
  },
  {
    "path": "naga/tests/in/wgsl/use-gl-ext-over-grad-workaround-if-instructed.wgsl",
    "content": "// see https://github.com/gfx-rs/wgpu/pull/5171\n\n@group(0) @binding(0) var texture: texture_depth_2d_array;\n@group(0) @binding(1) var texture_sampler: sampler_comparison;\n\n@fragment\nfn main() -> @location(0) f32  {\n    let pos = vec2<f32>(0.0);\n    let array_index: i32 = 0;\n    let depth: f32 = 0.0;\n    return textureSampleCompareLevel(texture, texture_sampler, pos, array_index, depth);\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/workgroup-uniform-load-atomic.wgsl",
    "content": "// Test workgroupUniformLoad specialization for atomic<T> -> T\n\nstruct AtomicStruct {\n    atomic_scalar: atomic<u32>,\n    atomic_arr: array<atomic<i32>, 2>,\n}\n\nvar<workgroup> wg_scalar: atomic<u32>;\nvar<workgroup> wg_signed: atomic<i32>;\nvar<workgroup> wg_struct: AtomicStruct;\n\n@compute @workgroup_size(64)\nfn test_atomic_workgroup_uniform_load(\n    @builtin(workgroup_id) workgroup_id: vec3u,\n    @builtin(local_invocation_id) local_id: vec3u\n) {\n    let active_tile_index = workgroup_id.x + workgroup_id.y * 32768;\n    \n    // Each thread may set the atomics\n    atomicOr(&wg_scalar, u32(active_tile_index >= 64));\n    atomicAdd(&wg_signed, 1i);\n    atomicStore(&wg_struct.atomic_scalar, 1u);\n    atomicAdd(&wg_struct.atomic_arr[0], 1i);\n    \n    workgroupBarrier();\n    \n    // workgroupUniformLoad on atomic<u32> should return u32\n    let scalar_val: u32 = workgroupUniformLoad(&wg_scalar);\n    \n    // workgroupUniformLoad on atomic<i32> should return i32\n    let signed_val: i32 = workgroupUniformLoad(&wg_signed);\n    \n    // workgroupUniformLoad on struct.atomic_scalar should return u32\n    let struct_scalar: u32 = workgroupUniformLoad(&wg_struct.atomic_scalar);\n    \n    // workgroupUniformLoad on struct.atomic_arr[i] should return i32\n    let struct_arr_val: i32 = workgroupUniformLoad(&wg_struct.atomic_arr[0]);\n    \n    // Should be able to use all results in comparisons\n    if scalar_val == 0u && signed_val > 0i && struct_scalar > 0u && struct_arr_val > 0i {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/workgroup-uniform-load.wgsl",
    "content": "const SIZE: u32 = 128u;\n\nvar<workgroup> arr_i32: array<i32, SIZE>;\n\n@compute @workgroup_size(4)\nfn test_workgroupUniformLoad(@builtin(workgroup_id) workgroup_id: vec3<u32>) {\n    let x = &arr_i32[workgroup_id.x];\n    let val = workgroupUniformLoad(x);\n    if val > 10 {\n        workgroupBarrier();\n    }\n}\n"
  },
  {
    "path": "naga/tests/in/wgsl/workgroup-var-init.toml",
    "content": "targets = \"WGSL | GLSL | SPIRV | HLSL | METAL\"\n\n[msl]\nfake_missing_bindings = false\nlang_version = [1, 0]\nspirv_cross_compatibility = false\nzero_initialize_workgroup_memory = true\n\n[msl.per_entry_point_map.main]\nresources = [\n    { bind_target = { buffer = 0, mutable = true }, resource_binding = { group = 0, binding = 0 } },\n]\n\n[spv]\nadjust_coordinate_space = false\ndebug = true\nversion = [1, 1]\n"
  },
  {
    "path": "naga/tests/in/wgsl/workgroup-var-init.wgsl",
    "content": "struct WStruct {\n    arr: array<u32, 512>,\n    atom: atomic<i32>,\n    atom_arr: array<array<atomic<i32>, 8>, 8>,\n}\n\nvar<workgroup> w_mem: WStruct;\n\n@group(0) @binding(0)\nvar<storage, read_write> output: array<u32, 512>;\n\n@compute @workgroup_size(1)\nfn main() {\n    output = w_mem.arr;\n}"
  },
  {
    "path": "naga/tests/naga/example_wgsl.rs",
    "content": "#![cfg(feature = \"wgsl-in\")]\n\nuse naga::{front::wgsl, valid::Validator};\nuse std::{ffi::OsStr, fs, path::Path};\n\n/// Runs through all example shaders and ensures they are valid wgsl.\n// While we _can_ run this test under miri, it is extremely slow (>5 minutes),\n// and naga isn't the primary target for miri testing, so we disable it.\n#[cfg_attr(miri, ignore)]\n#[test]\npub fn parse_example_wgsl() {\n    let example_path = Path::new(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .join(\"examples\");\n\n    println!(\"Looking for examples in {}\", example_path.display());\n\n    let mut example_paths = Vec::new();\n    for example_entry in walkdir::WalkDir::new(example_path) {\n        let Ok(dir_entry) = example_entry else {\n            continue;\n        };\n\n        if !dir_entry.file_type().is_file() {\n            continue;\n        }\n\n        let path = dir_entry.path();\n\n        if path.extension().map(OsStr::to_string_lossy).as_deref() == Some(\"wgsl\") {\n            example_paths.push(path.to_path_buf());\n        }\n    }\n\n    assert!(!example_paths.is_empty(), \"No examples found!\");\n\n    println!(\"Found {} examples\", example_paths.len());\n\n    for example_path in example_paths {\n        println!(\"\\tParsing {}\", example_path.display());\n\n        let shader = fs::read_to_string(&example_path).unwrap();\n\n        let module = wgsl::parse_str(&shader).unwrap();\n        //TODO: re-use the validator\n        Validator::new(\n            naga::valid::ValidationFlags::all(),\n            naga::valid::Capabilities::all(),\n        )\n        .validate(&module)\n        .unwrap();\n    }\n}\n"
  },
  {
    "path": "naga/tests/naga/main.rs",
    "content": "mod example_wgsl;\nmod snapshots;\nmod spirv_capabilities;\nmod validation;\nmod wgsl_errors;\n"
  },
  {
    "path": "naga/tests/naga/snapshots.rs",
    "content": "use naga::compact::KeepUnused;\nuse naga_test::*;\n\nconst DIR_IN: &str = concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/tests/in\");\nconst DIR_OUT: &str = concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/tests/out\");\n\n#[allow(unused_variables)]\nfn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&str>) {\n    let params = input.read_parameters(DIR_IN);\n    let name = input.file_name.display().to_string();\n\n    let targets = params.targets.unwrap();\n\n    let mut capabilities = params.capabilities.unwrap_or_default();\n    {\n        let mut allowed_capabilities = naga::valid::Capabilities::all();\n        if targets.contains(Targets::GLSL) {\n            allowed_capabilities &= naga::back::glsl::supported_capabilities();\n        }\n        if targets.contains(Targets::HLSL) {\n            allowed_capabilities &= naga::back::hlsl::supported_capabilities();\n        }\n        if targets.contains(Targets::SPIRV) {\n            allowed_capabilities &= naga::back::spv::supported_capabilities();\n        }\n        if targets.contains(Targets::WGSL) {\n            allowed_capabilities &= naga::back::wgsl::supported_capabilities();\n        }\n        if targets.contains(Targets::METAL) {\n            allowed_capabilities &= naga::back::msl::supported_capabilities();\n        }\n        if capabilities == naga::valid::Capabilities::all() {\n            capabilities = allowed_capabilities;\n        } else {\n            let diff = capabilities - allowed_capabilities;\n            if !diff.is_empty() {\n                panic!(\n                    \"Invalid capabilities for backends on shader {name}: used {diff:?} which aren't supported by one of the targets.\nNote: this is an issue with snapshot configuration, not code. If you added a new capability, add it to `supported_capabilities()` in each backend where it is supported\"\n                );\n            }\n        }\n    }\n\n    {\n        if targets.contains(Targets::IR) {\n            let config = ron::ser::PrettyConfig::default().new_line(\"\\n\".to_string());\n            let string = ron::ser::to_string_pretty(module, config).unwrap();\n            input.write_output_file(\"ir\", \"ron\", string, DIR_OUT);\n        }\n    }\n\n    let validation_flags = if targets.contains(Targets::NO_VALIDATION) {\n        naga::valid::ValidationFlags::empty()\n    } else {\n        naga::valid::ValidationFlags::all()\n    };\n\n    let info = naga::valid::Validator::new(validation_flags, capabilities)\n        .validate(module)\n        .unwrap_or_else(|err| {\n            panic!(\"Naga module validation failed on test `{name}`:\\n{err:?}\");\n        });\n\n    let info = {\n        // Our backends often generate temporary names based on handle indices,\n        // which means that adding or removing unused arena entries can affect\n        // the output even though they have no semantic effect. Such\n        // meaningless changes add noise to snapshot diffs, making accurate\n        // patch review difficult. Compacting the modules before generating\n        // snapshots makes the output independent of unused arena entries.\n        naga::compact::compact(module, KeepUnused::No);\n\n        {\n            if targets.contains(Targets::IR) {\n                let config = ron::ser::PrettyConfig::default().new_line(\"\\n\".to_string());\n                let string = ron::ser::to_string_pretty(module, config).unwrap();\n                input.write_output_file(\"ir\", \"compact.ron\", string, DIR_OUT);\n            }\n        }\n\n        naga::valid::Validator::new(validation_flags, capabilities)\n            .validate(module)\n            .unwrap_or_else(|err| {\n                panic!(\"Post-compaction module validation failed on test '{name}':\\n<{err:?}\")\n            })\n    };\n\n    let shared_info = WriterSharedOptions {\n        mesh_output_validation: params.mesh_output_validation,\n        task_limits: params.task_limits,\n        bounds_checks_policies: params.bounds_check_policies,\n    };\n\n    {\n        if targets.contains(Targets::ANALYSIS) {\n            let config = ron::ser::PrettyConfig::default().new_line(\"\\n\".to_string());\n            let string = ron::ser::to_string_pretty(&info, config).unwrap();\n            input.write_output_file(\"analysis\", \"info.ron\", string, DIR_OUT);\n        }\n    }\n\n    if targets.contains(Targets::SPIRV) {\n        let mut debug_info = None;\n        if let Some(source_code) = source_code {\n            debug_info = Some(naga::back::spv::DebugInfo {\n                source_code,\n                file_name: &name,\n                // wgpu#6266: we technically know all the information here to\n                // produce the valid language but it's not too important for\n                // validation purposes\n                language: naga::back::spv::SourceLanguage::Unknown,\n            })\n        }\n\n        write_output_spv(\n            input,\n            module,\n            &info,\n            debug_info,\n            &params.spv,\n            &params.pipeline_constants,\n            &shared_info,\n        );\n    }\n\n    if targets.contains(Targets::METAL) {\n        write_output_msl(\n            input,\n            module,\n            &info,\n            &params.msl,\n            &params.msl_pipeline,\n            params.bounds_check_policies,\n            &params.pipeline_constants,\n        );\n    }\n\n    if targets.contains(Targets::GLSL) {\n        for ep in module.entry_points.iter() {\n            if params.glsl_exclude_list.contains(&ep.name) {\n                continue;\n            }\n            write_output_glsl(\n                input,\n                module,\n                &info,\n                ep.stage,\n                &ep.name,\n                &params.glsl,\n                params.bounds_check_policies,\n                params.glsl_multiview,\n                &params.pipeline_constants,\n            );\n        }\n    }\n\n    if targets.contains(Targets::DOT) {\n        let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap();\n        input.write_output_file(\"dot\", \"dot\", string, DIR_OUT);\n    }\n\n    if targets.contains(Targets::HLSL) {\n        let frag_module;\n        let mut frag_ep = None;\n        if let Some(ref module_spec) = params.fragment_module {\n            let full_path = input.input_directory(DIR_IN).join(&module_spec.path);\n\n            assert_eq!(\n                full_path.extension().unwrap().to_string_lossy(),\n                \"wgsl\",\n                \"Currently all fragment modules must be in WGSL\"\n            );\n\n            let frag_src = std::fs::read_to_string(full_path).unwrap();\n\n            frag_module =\n                naga::front::wgsl::parse_str(&frag_src).expect(\"Failed to parse fragment module\");\n\n            frag_ep = Some(\n                naga::back::hlsl::FragmentEntryPoint::new(&frag_module, &module_spec.entry_point)\n                    .expect(\"Could not find fragment entry point\"),\n            );\n        }\n\n        write_output_hlsl(\n            input,\n            module,\n            &info,\n            &params.hlsl,\n            &params.pipeline_constants,\n            frag_ep,\n        );\n    }\n\n    if targets.contains(Targets::WGSL) {\n        write_output_wgsl(input, module, &info, &params.wgsl);\n    }\n}\n\nfn write_output_spv(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    debug_info: Option<naga::back::spv::DebugInfo>,\n    params: &SpirvOutParameters,\n    pipeline_constants: &naga::back::PipelineConstants,\n    shared_options: &WriterSharedOptions,\n) {\n    use naga::back::spv;\n\n    let options = params.to_options(shared_options, debug_info);\n\n    let (module, info) =\n        naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants)\n            .expect(\"override evaluation failed\");\n\n    if params.separate_entry_points {\n        for ep in module.entry_points.iter() {\n            let pipeline_options = spv::PipelineOptions {\n                entry_point: ep.name.clone(),\n                shader_stage: ep.stage,\n            };\n            write_output_spv_inner(\n                input,\n                &module,\n                &info,\n                &options,\n                Some(&pipeline_options),\n                &format!(\"{}.spvasm\", ep.name),\n            );\n        }\n    } else {\n        write_output_spv_inner(input, &module, &info, &options, None, \"spvasm\");\n    }\n}\n\nfn write_output_spv_inner(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    options: &naga::back::spv::Options<'_>,\n    pipeline_options: Option<&naga::back::spv::PipelineOptions>,\n    extension: &str,\n) {\n    use naga::back::spv;\n    use rspirv::binary::Disassemble;\n    println!(\"Generating SPIR-V for {:?}\", input.file_name);\n    let spv = spv::write_vec(module, info, options, pipeline_options).unwrap();\n    let dis = rspirv::dr::load_words(spv)\n        .expect(\"Produced invalid SPIR-V\")\n        .disassemble();\n    // HACK escape CR/LF if source code is in side.\n    let dis = if options.debug_info.is_some() {\n        let dis = dis.replace(\"\\\\r\", \"\\r\");\n        dis.replace(\"\\\\n\", \"\\n\")\n    } else {\n        dis\n    };\n    input.write_output_file(\"spv\", extension, dis, DIR_OUT);\n}\n\nfn write_output_msl(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    options: &naga::back::msl::Options,\n    pipeline_options: &naga::back::msl::PipelineOptions,\n    bounds_check_policies: naga::proc::BoundsCheckPolicies,\n    pipeline_constants: &naga::back::PipelineConstants,\n) {\n    use naga::back::msl;\n\n    println!(\"generating MSL\");\n\n    let (module, info) =\n        naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants)\n            .expect(\"override evaluation failed\");\n\n    let mut options = options.clone();\n    options.bounds_check_policies = bounds_check_policies;\n    let (string, tr_info) = msl::write_string(&module, &info, &options, pipeline_options)\n        .unwrap_or_else(|err| panic!(\"Metal write failed: {err}\"));\n\n    for (ep, result) in module.entry_points.iter().zip(tr_info.entry_point_names) {\n        if let Err(error) = result {\n            panic!(\"Failed to translate '{}': {}\", ep.name, error);\n        }\n    }\n\n    input.write_output_file(\"msl\", \"metal\", string, DIR_OUT);\n}\n\n#[allow(clippy::too_many_arguments)]\nfn write_output_glsl(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    stage: naga::ShaderStage,\n    ep_name: &str,\n    options: &naga::back::glsl::Options,\n    bounds_check_policies: naga::proc::BoundsCheckPolicies,\n    multiview: Option<core::num::NonZeroU32>,\n    pipeline_constants: &naga::back::PipelineConstants,\n) {\n    use naga::back::glsl;\n\n    println!(\"generating GLSL\");\n\n    let pipeline_options = glsl::PipelineOptions {\n        shader_stage: stage,\n        entry_point: ep_name.to_string(),\n        multiview,\n    };\n\n    let mut buffer = String::new();\n    let (module, info) =\n        naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants)\n            .expect(\"override evaluation failed\");\n    let mut writer = glsl::Writer::new(\n        &mut buffer,\n        &module,\n        &info,\n        options,\n        &pipeline_options,\n        bounds_check_policies,\n    )\n    .expect(\"GLSL init failed\");\n    writer.write().expect(\"GLSL write failed\");\n\n    let extension = format!(\"{ep_name}.{stage:?}.glsl\");\n    input.write_output_file(\"glsl\", &extension, buffer, DIR_OUT);\n}\n\nfn write_output_hlsl(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    options: &naga::back::hlsl::Options,\n    pipeline_constants: &naga::back::PipelineConstants,\n    frag_ep: Option<naga::back::hlsl::FragmentEntryPoint>,\n) {\n    use naga::back::hlsl;\n\n    println!(\"generating HLSL\");\n\n    let (module, info) =\n        naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants)\n            .expect(\"override evaluation failed\");\n\n    let mut buffer = String::new();\n    let pipeline_options = Default::default();\n    let mut writer = hlsl::Writer::new(&mut buffer, options, &pipeline_options);\n    let reflection_info = writer\n        .write(&module, &info, frag_ep.as_ref())\n        .expect(\"HLSL write failed\");\n\n    input.write_output_file(\"hlsl\", \"hlsl\", buffer, DIR_OUT);\n\n    // We need a config file for validation script\n    // This file contains an info about profiles (shader stages) contains inside generated shader\n    // This info will be passed to dxc\n    let mut config = hlsl_snapshots::Config::empty();\n    for (index, ep) in module.entry_points.iter().enumerate() {\n        let name = match reflection_info.entry_point_names[index] {\n            Ok(ref name) => name,\n            Err(_) => continue,\n        };\n        match ep.stage {\n            naga::ShaderStage::Vertex => &mut config.vertex,\n            naga::ShaderStage::Fragment => &mut config.fragment,\n            naga::ShaderStage::Compute => &mut config.compute,\n            naga::ShaderStage::Task\n            | naga::ShaderStage::Mesh\n            | naga::ShaderStage::RayGeneration\n            | naga::ShaderStage::AnyHit\n            | naga::ShaderStage::ClosestHit\n            | naga::ShaderStage::Miss => unreachable!(),\n        }\n        .push(hlsl_snapshots::ConfigItem {\n            entry_point: name.clone(),\n            target_profile: format!(\n                \"{}_{}\",\n                ep.stage.to_hlsl_str(),\n                options.shader_model.to_str()\n            ),\n        });\n    }\n\n    config\n        .to_file(input.output_path(\"hlsl\", \"ron\", DIR_OUT))\n        .unwrap();\n}\n\nfn write_output_wgsl(\n    input: &Input,\n    module: &naga::Module,\n    info: &naga::valid::ModuleInfo,\n    params: &WgslOutParameters,\n) {\n    use naga::back::wgsl;\n\n    println!(\"generating WGSL\");\n\n    let string = wgsl::write_string(module, info, params.into()).expect(\"WGSL write failed\");\n\n    input.write_output_file(\"wgsl\", \"wgsl\", string, DIR_OUT);\n}\n\n// While we _can_ run this test under miri, it is extremely slow (>5 minutes),\n// and naga isn't the primary target for miri testing, so we disable it.\n#[cfg_attr(miri, ignore)]\n#[test]\nfn convert_snapshots_wgsl() {\n    let _ = env_logger::try_init();\n\n    for input in Input::files_in_dir(\"wgsl\", &[\"wgsl\"], DIR_IN) {\n        let source = input.read_source(DIR_IN, true);\n        // crlf will make the large split output different on different platform\n        let source = source.replace('\\r', \"\");\n\n        let params = input.read_parameters(DIR_IN);\n\n        let mut frontend = naga::front::wgsl::Frontend::new_with_options((&params.wgsl_in).into());\n        match frontend.parse(&source) {\n            Ok(mut module) => check_targets(&input, &mut module, Some(&source)),\n            Err(e) => panic!(\n                \"{}\",\n                e.emit_to_string_with_path(&source, input.input_path(DIR_IN))\n            ),\n        }\n    }\n}\n\n// miri doesn't allow us to shell out to `spirv-as`\n#[cfg_attr(miri, ignore)]\n#[test]\nfn convert_snapshots_spv() {\n    use std::process::Command;\n\n    let _ = env_logger::try_init();\n\n    for input in Input::files_in_dir(\"spv\", &[\"spvasm\"], DIR_IN) {\n        println!(\"Assembling '{}'\", input.file_name.display());\n\n        let command = Command::new(\"spirv-as\")\n            .arg(input.input_path(DIR_IN))\n            .arg(\"-o\")\n            .arg(\"-\")\n            .output()\n            .expect(\n                \"Failed to execute spirv-as. It can be installed \\\n            by installing the Vulkan SDK and adding it to your path.\",\n            );\n\n        println!(\"Processing '{}'\", input.file_name.display());\n\n        if !command.status.success() {\n            panic!(\n                \"spirv-as failed: {}\\n{}\",\n                String::from_utf8_lossy(&command.stdout),\n                String::from_utf8_lossy(&command.stderr)\n            );\n        }\n\n        let params = input.read_parameters(DIR_IN);\n\n        let mut module =\n            naga::front::spv::parse_u8_slice(&command.stdout, &(&params.spv_in).into()).unwrap();\n\n        check_targets(&input, &mut module, None);\n    }\n}\n\n// While we _can_ run this test under miri, it is extremely slow (>5 minutes),\n// and naga isn't the primary target for miri testing, so we disable it.\n#[cfg_attr(miri, ignore)]\n#[test]\nfn convert_snapshots_glsl() {\n    let _ = env_logger::try_init();\n\n    for input in Input::files_in_dir(\"glsl\", &[\"vert\", \"frag\", \"comp\"], DIR_IN) {\n        let input = Input {\n            keep_input_extension: true,\n            ..input\n        };\n        let file_name = &input.file_name;\n\n        let stage = match file_name.extension().and_then(|s| s.to_str()).unwrap() {\n            \"vert\" => naga::ShaderStage::Vertex,\n            \"frag\" => naga::ShaderStage::Fragment,\n            \"comp\" => naga::ShaderStage::Compute,\n            ext => panic!(\"Unknown extension for glsl file {ext}\"),\n        };\n\n        let mut parser = naga::front::glsl::Frontend::default();\n        let mut module = parser\n            .parse(\n                &naga::front::glsl::Options {\n                    stage,\n                    defines: Default::default(),\n                },\n                &input.read_source(DIR_IN, true),\n            )\n            .unwrap();\n\n        check_targets(&input, &mut module, None);\n    }\n}\n"
  },
  {
    "path": "naga/tests/naga/spirv_capabilities.rs",
    "content": "/*!\nTest SPIR-V backend capability checks.\n*/\n\n#![cfg(all(feature = \"wgsl-in\", spv_out))]\n\nuse spirv::Capability as Ca;\n\nuse rspirv::binary::Disassemble;\n\nfn capabilities_used(source: &str) -> naga::FastIndexSet<Ca> {\n    use naga::back::spv;\n    use naga::valid;\n\n    let module = naga::front::wgsl::parse_str(source).unwrap_or_else(|e| {\n        panic!(\n            \"expected WGSL to parse successfully:\\n{}\",\n            e.emit_to_string(source)\n        );\n    });\n\n    let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all())\n        .validate(&module)\n        .expect(\"validation failed\");\n\n    let mut words = vec![];\n    let mut writer = spv::Writer::new(&spv::Options::default()).unwrap();\n    writer\n        .write(&module, &info, None, &None, &mut words)\n        .unwrap();\n    writer.get_capabilities_used().clone()\n}\n\nfn require(capabilities: &[Ca], source: &str) {\n    require_and_forbid(capabilities, &[], source);\n}\n\nfn require_and_forbid(required: &[Ca], forbidden: &[Ca], source: &str) {\n    let caps_used = capabilities_used(source);\n\n    let missing_caps: Vec<_> = required\n        .iter()\n        .filter(|&cap| !caps_used.contains(cap))\n        .cloned()\n        .collect();\n    if !missing_caps.is_empty() {\n        panic!(\"shader code should have requested these caps: {missing_caps:?}\\n\\n{source}\");\n    }\n\n    let forbidden_caps: Vec<_> = forbidden\n        .iter()\n        .filter(|&cap| caps_used.contains(cap))\n        .cloned()\n        .collect();\n    if !forbidden_caps.is_empty() {\n        panic!(\"shader code should not have requested these caps: {forbidden_caps:?}\\n\\n{source}\");\n    }\n}\n\n#[test]\nfn sampler1d() {\n    require(\n        &[Ca::Sampled1D],\n        r#\"\n        @group(0) @binding(0)\n        var image_1d: texture_1d<f32>;\n    \"#,\n    );\n}\n\n#[test]\nfn storage1d() {\n    require(\n        &[Ca::Image1D],\n        r#\"\n        @group(0) @binding(0)\n        var image_1d: texture_storage_1d<rgba8unorm,write>;\n    \"#,\n    );\n}\n\n#[test]\nfn cube_array() {\n    // ImageCubeArray is only for storage cube array images, which WGSL doesn't\n    // support\n    require_and_forbid(\n        &[Ca::SampledCubeArray],\n        &[Ca::ImageCubeArray],\n        r#\"\n        @group(0) @binding(0)\n        var image_cube: texture_cube_array<f32>;\n    \"#,\n    );\n}\n\n#[test]\nfn image_queries() {\n    require(\n        &[Ca::ImageQuery],\n        r#\"\n        fn f(i: texture_2d<f32>) -> vec2<u32> {\n            return textureDimensions(i);\n        }\n    \"#,\n    );\n    require(\n        &[Ca::ImageQuery],\n        r#\"\n        fn f(i: texture_2d_array<f32>) -> u32 {\n            return textureNumLayers(i);\n        }\n    \"#,\n    );\n    require(\n        &[Ca::ImageQuery],\n        r#\"\n        fn f(i: texture_2d<f32>) -> u32 {\n            return textureNumLevels(i);\n        }\n    \"#,\n    );\n    require(\n        &[Ca::ImageQuery],\n        r#\"\n        fn f(i: texture_multisampled_2d<f32>) -> u32 {\n            return textureNumSamples(i);\n        }\n    \"#,\n    );\n}\n\n#[test]\nfn sample_rate_shading() {\n    require(\n        &[Ca::SampleRateShading],\n        r#\"\n        @fragment\n        fn f(@location(0) @interpolate(perspective, sample) x: f32) { }\n    \"#,\n    );\n\n    require(\n        &[Ca::SampleRateShading],\n        r#\"\n        @fragment\n        fn f(@builtin(sample_index) x: u32) { }\n    \"#,\n    );\n}\n\n#[test]\nfn barycentrics() {\n    require(\n        &[Ca::FragmentBarycentricKHR],\n        r#\"\n        @fragment\n        fn f(@builtin(barycentric) x: vec3<f32>) { }\n    \"#,\n    );\n}\n\n#[test]\nfn geometry() {\n    require(\n        &[Ca::Geometry],\n        r#\"\n        enable primitive_index;\n        @fragment\n        fn f(@builtin(primitive_index) x: u32) { }\n    \"#,\n    );\n}\n\n#[test]\nfn storage_image_formats() {\n    require_and_forbid(\n        &[Ca::Shader],\n        &[Ca::StorageImageExtendedFormats],\n        r#\"\n            @group(0) @binding(0)\n            var image_rg32f: texture_storage_2d<rgba16uint, read>;\n        \"#,\n    );\n\n    require(\n        &[Ca::StorageImageExtendedFormats],\n        r#\"\n            @group(0) @binding(0)\n            var image_rg32f: texture_storage_2d<rg32float, read>;\n        \"#,\n    );\n}\n\n#[test]\nfn float64() {\n    require(\n        &[Ca::Float64],\n        r#\"\n            fn f(x: f64) -> f64 {\n                return x;\n            }\n        \"#,\n    );\n}\n\n#[test]\nfn int64() {\n    require(\n        &[Ca::Int64],\n        r#\"\n            fn f(x: i64) -> i64 {\n                return x;\n            }\n        \"#,\n    );\n    require(\n        &[Ca::Int64],\n        r#\"\n            fn f(x: u64) -> u64 {\n                return x;\n            }\n        \"#,\n    );\n}\n\n#[test]\nfn float16() {\n    require(&[Ca::Float16], \"enable f16; fn f(x: f16) { }\");\n}\n\n#[test]\nfn f16_io_capabilities() {\n    let source = r#\"\n        enable f16;\n        \n        struct VertexOutput {\n            @location(0) color: vec3<f16>,\n        }\n        \n        @fragment  \n        fn main(input: VertexOutput) -> @location(0) vec4<f16> {\n            return vec4<f16>(input.color, f16(1.0));\n        }\n    \"#;\n\n    use naga::back::spv;\n    use naga::valid;\n\n    let module = naga::front::wgsl::parse_str(source).unwrap();\n    let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    // Test native path: use_storage_input_output_16 = true\n    let options_native = spv::Options {\n        use_storage_input_output_16: true,\n        ..Default::default()\n    };\n\n    let mut words_native = vec![];\n    let mut writer_native = spv::Writer::new(&options_native).unwrap();\n    writer_native\n        .write(&module, &info, None, &None, &mut words_native)\n        .unwrap();\n    let caps_native = writer_native.get_capabilities_used();\n\n    // Should include `StorageInputOutput16` for native `f16` I/O\n    assert!(caps_native.contains(&Ca::StorageInputOutput16));\n\n    // Test polyfill path: use_storage_input_output_16 = false\n    let options_polyfill = spv::Options {\n        use_storage_input_output_16: false,\n        ..Default::default()\n    };\n\n    let mut words_polyfill = vec![];\n    let mut writer_polyfill = spv::Writer::new(&options_polyfill).unwrap();\n    writer_polyfill\n        .write(&module, &info, None, &None, &mut words_polyfill)\n        .unwrap();\n    let caps_polyfill = writer_polyfill.get_capabilities_used();\n\n    // Should not include `StorageInputOutput16` when polyfilled\n    assert!(!caps_polyfill.contains(&Ca::StorageInputOutput16));\n\n    // But should still include the basic `f16` capabilities\n    assert!(caps_polyfill.contains(&Ca::Float16));\n}\n\n#[test]\nfn f16_io_polyfill_codegen() {\n    let source = r#\"\n        enable f16;\n\n        struct F16IO {\n            @location(0) scalar_f16: f16,\n            @location(1) scalar_f32: f32,\n            @location(2) vec2_f16: vec2<f16>,\n            @location(3) vec2_f32: vec2<f32>,\n        }\n\n        @fragment\n        fn main(input: F16IO) -> F16IO {\n            var output = input;\n            output.scalar_f16 = input.scalar_f16 + 1.0h;\n            output.vec2_f16.x = input.vec2_f16.y;\n            return output;\n        }\n    \"#;\n\n    use naga::{back::spv, valid};\n\n    let module = naga::front::wgsl::parse_str(source).unwrap();\n    let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    // Test Native Path\n    let options_native = spv::Options {\n        use_storage_input_output_16: true,\n        ..Default::default()\n    };\n    let mut words_native = vec![];\n    let mut writer_native = spv::Writer::new(&options_native).unwrap();\n    writer_native\n        .write(&module, &info, None, &None, &mut words_native)\n        .unwrap();\n    let caps_native = writer_native.get_capabilities_used();\n    let dis_native = rspirv::dr::load_words(words_native).unwrap().disassemble();\n\n    // Native path must request the capability and must NOT have conversions.\n    assert!(caps_native.contains(&Ca::StorageInputOutput16));\n    assert!(!dis_native.contains(\"OpFConvert\"));\n\n    // Test Polyfill Path\n    let options_polyfill = spv::Options {\n        use_storage_input_output_16: false,\n        ..Default::default()\n    };\n    let mut words_polyfill = vec![];\n    let mut writer_polyfill = spv::Writer::new(&options_polyfill).unwrap();\n    writer_polyfill\n        .write(&module, &info, None, &None, &mut words_polyfill)\n        .unwrap();\n    let caps_polyfill = writer_polyfill.get_capabilities_used();\n    let dis_polyfill = rspirv::dr::load_words(words_polyfill)\n        .unwrap()\n        .disassemble();\n\n    // Polyfill path should request the capability but not have conversions.\n    assert!(!caps_polyfill.contains(&Ca::StorageInputOutput16));\n    assert!(dis_polyfill.contains(\"OpFConvert\"));\n\n    // Should have 2 input conversions, and 2 output conversions\n    let fconvert_count = dis_polyfill.matches(\"OpFConvert\").count();\n    assert_eq!(\n        fconvert_count, 4,\n        \"Expected 4 OpFConvert instructions for polyfilled I/O\"\n    );\n}\n"
  },
  {
    "path": "naga/tests/naga/validation.rs",
    "content": "//! Tests of the module validator.\n//!\n//! There are also some validation tests in [`wgsl_errors`](super::wgsl_errors).\n\nuse naga::{\n    ir::{self, Expression, Function, Module, Scalar},\n    valid::{self, Capabilities, ModuleInfo, ValidationFlags},\n};\n\n#[derive(Default)]\nstruct TestSpanGenerator(u32);\n\nimpl TestSpanGenerator {\n    fn next(&mut self) -> naga::Span {\n        let span = naga::Span::new(self.0, self.0 + 1);\n        self.0 += 1;\n        span\n    }\n}\n\n#[track_caller]\nfn expect_validation_error_impl<I: IntoIterator<Item = naga::Span>>(\n    module: &Module,\n    validation_flags: valid::ValidationFlags,\n    capabilities: valid::Capabilities,\n    spans: Option<I>,\n) -> naga::valid::ValidationError {\n    let err = valid::Validator::new(validation_flags, capabilities)\n        .validate(module)\n        .expect_err(\"module should be invalid\");\n\n    if let Some(expected_spans_iter) = spans {\n        let actual_spans = err.spans().map(|sctx| sctx.0).collect::<Vec<_>>();\n        let expected_spans = expected_spans_iter.into_iter().collect::<Vec<_>>();\n        assert_eq!(\n            actual_spans, expected_spans,\n            \"expected error spans to be {expected_spans:?}, got {actual_spans:?}\",\n        );\n    }\n\n    err.into_inner()\n}\n\n/// Validate `module` with the given `validation_flags` and `capabilities`.\n///\n/// Panics if validation succeeds or fails with an error not associated with\n/// `span`. Otherwise, returns the validation error.\n///\n/// Note that only the span is checked, not the associated context string.\n#[track_caller]\nfn expect_validation_error_with_span(\n    module: &Module,\n    validation_flags: valid::ValidationFlags,\n    capabilities: valid::Capabilities,\n    span: naga::Span,\n) -> naga::valid::ValidationError {\n    expect_validation_error_impl(\n        module,\n        validation_flags,\n        capabilities,\n        Some(core::iter::once(span)),\n    )\n}\n\n/// Validation should fail if `AtomicResult` expressions are not\n/// populated by `Atomic` statements.\n#[test]\nfn populate_atomic_result() {\n    use naga::{Module, Type, TypeInner};\n\n    /// Different variants of the test case that we want to exercise.\n    enum Variant {\n        /// An `AtomicResult` expression with an `Atomic` statement\n        /// that populates it: valid.\n        Atomic,\n\n        /// An `AtomicResult` expression visited by an `Emit`\n        /// statement: invalid.\n        Emit,\n\n        /// An `AtomicResult` expression visited by no statement at\n        /// all: invalid\n        None,\n    }\n\n    // Looking at uses of `variant` should make it easy to identify\n    // the differences between the test cases.\n    fn try_variant(\n        variant: Variant,\n    ) -> Result<ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {\n        let span = naga::Span::default();\n        let mut module = Module::default();\n        let ty_u32 = module.types.insert(\n            Type {\n                name: Some(\"u32\".into()),\n                inner: TypeInner::Scalar(Scalar::U32),\n            },\n            span,\n        );\n        let ty_atomic_u32 = module.types.insert(\n            Type {\n                name: Some(\"atomic<u32>\".into()),\n                inner: TypeInner::Atomic(Scalar::U32),\n            },\n            span,\n        );\n        let var_atomic = module.global_variables.append(\n            naga::GlobalVariable {\n                name: Some(\"atomic_global\".into()),\n                space: naga::AddressSpace::WorkGroup,\n                binding: None,\n                ty: ty_atomic_u32,\n                init: None,\n                memory_decorations: naga::MemoryDecorations::empty(),\n            },\n            span,\n        );\n\n        let mut fun = Function::default();\n        let ex_global = fun\n            .expressions\n            .append(Expression::GlobalVariable(var_atomic), span);\n        let ex_42 = fun\n            .expressions\n            .append(Expression::Literal(naga::Literal::U32(42)), span);\n        let ex_result = fun.expressions.append(\n            Expression::AtomicResult {\n                ty: ty_u32,\n                comparison: false,\n            },\n            span,\n        );\n\n        match variant {\n            Variant::Atomic => {\n                fun.body.push(\n                    naga::Statement::Atomic {\n                        pointer: ex_global,\n                        fun: naga::AtomicFunction::Add,\n                        value: ex_42,\n                        result: Some(ex_result),\n                    },\n                    span,\n                );\n            }\n            Variant::Emit => {\n                fun.body.push(\n                    naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),\n                    span,\n                );\n            }\n            Variant::None => {}\n        }\n\n        module.functions.append(fun, span);\n\n        valid::Validator::new(\n            valid::ValidationFlags::default(),\n            valid::Capabilities::all(),\n        )\n        .validate(&module)\n    }\n\n    try_variant(Variant::Atomic).expect(\"module should validate\");\n    assert!(try_variant(Variant::Emit).is_err());\n    assert!(try_variant(Variant::None).is_err());\n}\n\n#[test]\nfn populate_call_result() {\n    use naga::{Module, Type, TypeInner};\n\n    /// Different variants of the test case that we want to exercise.\n    enum Variant {\n        /// A `CallResult` expression with an `Call` statement that\n        /// populates it: valid.\n        Call,\n\n        /// A `CallResult` expression visited by an `Emit` statement:\n        /// invalid.\n        Emit,\n\n        /// A `CallResult` expression visited by no statement at all:\n        /// invalid\n        None,\n    }\n\n    // Looking at uses of `variant` should make it easy to identify\n    // the differences between the test cases.\n    fn try_variant(\n        variant: Variant,\n    ) -> Result<ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {\n        let span = naga::Span::default();\n        let mut module = Module::default();\n        let ty_u32 = module.types.insert(\n            Type {\n                name: Some(\"u32\".into()),\n                inner: TypeInner::Scalar(Scalar::U32),\n            },\n            span,\n        );\n\n        let mut fun_callee = Function {\n            result: Some(naga::FunctionResult {\n                ty: ty_u32,\n                binding: None,\n            }),\n            ..Function::default()\n        };\n        let ex_42 = fun_callee\n            .expressions\n            .append(Expression::Literal(naga::Literal::U32(42)), span);\n        fun_callee\n            .body\n            .push(naga::Statement::Return { value: Some(ex_42) }, span);\n        let fun_callee = module.functions.append(fun_callee, span);\n\n        let mut fun_caller = Function::default();\n        let ex_result = fun_caller\n            .expressions\n            .append(Expression::CallResult(fun_callee), span);\n\n        match variant {\n            Variant::Call => {\n                fun_caller.body.push(\n                    naga::Statement::Call {\n                        function: fun_callee,\n                        arguments: vec![],\n                        result: Some(ex_result),\n                    },\n                    span,\n                );\n            }\n            Variant::Emit => {\n                fun_caller.body.push(\n                    naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),\n                    span,\n                );\n            }\n            Variant::None => {}\n        }\n\n        module.functions.append(fun_caller, span);\n\n        valid::Validator::new(\n            valid::ValidationFlags::default(),\n            valid::Capabilities::all(),\n        )\n        .validate(&module)\n    }\n\n    try_variant(Variant::Call).expect(\"should validate\");\n    assert!(try_variant(Variant::Emit).is_err());\n    assert!(try_variant(Variant::None).is_err());\n}\n\n#[test]\nfn emit_workgroup_uniform_load_result() {\n    use naga::{Module, Type, TypeInner};\n\n    // We want to ensure that the *only* problem with the code is the\n    // use of an `Emit` statement instead of an `Atomic` statement. So\n    // validate two versions of the module varying only in that\n    // aspect.\n    //\n    // Looking at uses of the `wg_load` makes it easy to identify the\n    // differences between the two variants.\n    fn variant(wg_load: bool) -> Result<ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {\n        let span = naga::Span::default();\n        let mut module = Module::default();\n        let ty_u32 = module.types.insert(\n            Type {\n                name: Some(\"u32\".into()),\n                inner: TypeInner::Scalar(Scalar::U32),\n            },\n            span,\n        );\n        let var_workgroup = module.global_variables.append(\n            naga::GlobalVariable {\n                name: Some(\"workgroup_global\".into()),\n                space: naga::AddressSpace::WorkGroup,\n                binding: None,\n                ty: ty_u32,\n                init: None,\n                memory_decorations: naga::MemoryDecorations::empty(),\n            },\n            span,\n        );\n\n        let mut fun = Function::default();\n        let ex_global = fun\n            .expressions\n            .append(Expression::GlobalVariable(var_workgroup), span);\n        let ex_result = fun\n            .expressions\n            .append(Expression::WorkGroupUniformLoadResult { ty: ty_u32 }, span);\n\n        if wg_load {\n            fun.body.push(\n                naga::Statement::WorkGroupUniformLoad {\n                    pointer: ex_global,\n                    result: ex_result,\n                },\n                span,\n            );\n        } else {\n            fun.body.push(\n                naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),\n                span,\n            );\n        }\n\n        module.functions.append(fun, span);\n\n        valid::Validator::new(\n            valid::ValidationFlags::default(),\n            valid::Capabilities::all(),\n        )\n        .validate(&module)\n    }\n\n    variant(true).expect(\"module should validate\");\n    assert!(variant(false).is_err());\n}\n\n#[test]\nfn builtin_cross_product_args() {\n    use naga::{MathFunction, Module, Type, TypeInner, VectorSize};\n\n    // We want to ensure that the *only* problem with the code is the\n    // arity of the call, or the size of the vectors passed to\n    // `cross`. So validate different versions of the module varying\n    // only in those aspects.\n    //\n    // Looking at uses of `size` and `arity` makes it easy to identify\n    // the differences between the variants.\n    fn variant(\n        size: VectorSize,\n        arity: usize,\n    ) -> Result<ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {\n        let span = naga::Span::default();\n        let mut module = Module::default();\n        let ty_vec3f = module.types.insert(\n            Type {\n                name: Some(\"vecnf\".into()),\n                inner: TypeInner::Vector {\n                    size: VectorSize::Tri,\n                    scalar: Scalar::F32,\n                },\n            },\n            span,\n        );\n        let ty_vecnf = module.types.insert(\n            Type {\n                name: Some(\"vecnf\".into()),\n                inner: TypeInner::Vector {\n                    size,\n                    scalar: Scalar::F32,\n                },\n            },\n            span,\n        );\n\n        let mut fun = Function {\n            result: Some(naga::ir::FunctionResult {\n                ty: ty_vec3f,\n                binding: None,\n            }),\n            ..Function::default()\n        };\n        let ex_zero = fun\n            .expressions\n            .append(Expression::ZeroValue(ty_vecnf), span);\n        let ex_cross = fun.expressions.append(\n            Expression::Math {\n                fun: MathFunction::Cross,\n                arg: ex_zero,\n                arg1: (arity >= 2).then_some(ex_zero),\n                arg2: (arity >= 3).then_some(ex_zero),\n                arg3: (arity >= 4).then_some(ex_zero),\n            },\n            span,\n        );\n\n        fun.body.push(\n            naga::Statement::Emit(naga::Range::new_from_bounds(ex_cross, ex_cross)),\n            span,\n        );\n        fun.body.push(\n            naga::Statement::Return {\n                value: Some(ex_cross),\n            },\n            span,\n        );\n\n        module.functions.append(fun, span);\n\n        valid::Validator::new(\n            valid::ValidationFlags::default(),\n            valid::Capabilities::all(),\n        )\n        .validate(&module)\n    }\n\n    assert!(variant(VectorSize::Bi, 2).is_err());\n\n    assert!(variant(VectorSize::Tri, 1).is_err());\n    variant(VectorSize::Tri, 2).expect(\"module should validate\");\n    assert!(variant(VectorSize::Tri, 3).is_err());\n    assert!(variant(VectorSize::Tri, 4).is_err());\n\n    assert!(variant(VectorSize::Quad, 2).is_err());\n}\n\n#[test]\nfn incompatible_interpolation_and_sampling_types() {\n    use dummy_interpolation_shader::DummyInterpolationShader;\n\n    // NOTE: Things we expect to actually compile are in the `interpolate` snapshot test.\n    use itertools::Itertools;\n\n    let invalid_shader_module = |interpolation_and_sampling| {\n        let (interpolation, sampling) = interpolation_and_sampling;\n\n        let valid = matches!(\n            (interpolation, sampling),\n            (_, None)\n                | (\n                    naga::Interpolation::Perspective | naga::Interpolation::Linear,\n                    Some(\n                        naga::Sampling::Center | naga::Sampling::Centroid | naga::Sampling::Sample\n                    ),\n                )\n                | (\n                    naga::Interpolation::Flat,\n                    Some(naga::Sampling::First | naga::Sampling::Either)\n                )\n        );\n\n        if valid {\n            None\n        } else {\n            let DummyInterpolationShader {\n                source,\n                module,\n                interpolate_attr,\n                entry_point: _,\n            } = DummyInterpolationShader::new(interpolation, sampling);\n            Some((\n                source,\n                module,\n                interpolation,\n                sampling.expect(\"default interpolation sampling should be valid\"),\n                interpolate_attr,\n            ))\n        }\n    };\n\n    let invalid_cases = [\n        naga::Interpolation::Flat,\n        naga::Interpolation::Linear,\n        naga::Interpolation::Perspective,\n        naga::Interpolation::PerVertex,\n    ]\n    .into_iter()\n    .cartesian_product(\n        [\n            naga::Sampling::Either,\n            naga::Sampling::First,\n            naga::Sampling::Sample,\n            naga::Sampling::Center,\n            naga::Sampling::Centroid,\n        ]\n        .into_iter()\n        .map(Some)\n        .chain([None]),\n    )\n    .filter_map(invalid_shader_module);\n\n    for (invalid_source, invalid_module, interpolation, sampling, interpolate_attr) in invalid_cases\n    {\n        let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n            .validate(&invalid_module)\n            .expect_err(&format!(\n                \"module should be invalid for {interpolate_attr:?}\"\n            ));\n        assert!(dbg!(err.emit_to_string(&invalid_source)).contains(&dbg!(\n            naga::valid::VaryingError::InvalidInterpolationSamplingCombination {\n                interpolation,\n                sampling,\n            }\n            .to_string()\n        )),);\n    }\n}\n\n#[test]\nfn no_flat_first_in_glsl() {\n    use dummy_interpolation_shader::DummyInterpolationShader;\n\n    let DummyInterpolationShader {\n        source: _,\n        module,\n        interpolate_attr,\n        entry_point,\n    } = DummyInterpolationShader::new(naga::Interpolation::Flat, Some(naga::Sampling::First));\n\n    let mut validator = naga::valid::Validator::new(Default::default(), Default::default());\n    let module_info = validator.validate(&module).unwrap();\n\n    let options = Default::default();\n    let pipeline_options = naga::back::glsl::PipelineOptions {\n        shader_stage: naga::ShaderStage::Fragment,\n        entry_point: entry_point.to_owned(),\n        multiview: None,\n    };\n    let mut glsl_writer = naga::back::glsl::Writer::new(\n        String::new(),\n        &module,\n        &module_info,\n        &options,\n        &pipeline_options,\n        Default::default(),\n    )\n    .unwrap();\n\n    let err = glsl_writer.write().expect_err(&format!(\n        \"`{interpolate_attr}` should fail backend validation\"\n    ));\n\n    assert!(matches!(\n        err,\n        naga::back::glsl::Error::FirstSamplingNotSupported\n    ));\n}\n\nmod dummy_interpolation_shader {\n    pub struct DummyInterpolationShader {\n        pub source: String,\n        pub module: naga::Module,\n        pub interpolate_attr: String,\n        pub entry_point: &'static str,\n    }\n\n    impl DummyInterpolationShader {\n        pub fn new(interpolation: naga::Interpolation, sampling: Option<naga::Sampling>) -> Self {\n            // NOTE: If you have to add variants below, make sure to add them to the\n            // `cartesian_product`'d combinations in tests around here!\n            let interpolation_str = match interpolation {\n                naga::Interpolation::Flat => \"flat\",\n                naga::Interpolation::Linear => \"linear\",\n                naga::Interpolation::Perspective => \"perspective\",\n                naga::Interpolation::PerVertex => \"per_vertex\",\n            };\n            let sampling_str = match sampling {\n                None => String::new(),\n                Some(sampling) => format!(\n                    \", {}\",\n                    match sampling {\n                        naga::Sampling::First => \"first\",\n                        naga::Sampling::Either => \"either\",\n                        naga::Sampling::Center => \"center\",\n                        naga::Sampling::Centroid => \"centroid\",\n                        naga::Sampling::Sample => \"sample\",\n                    }\n                ),\n            };\n            let member_type = match interpolation {\n                naga::Interpolation::Perspective | naga::Interpolation::Linear => \"f32\",\n                naga::Interpolation::Flat => \"u32\",\n                naga::Interpolation::PerVertex => \"array<u32, 3>\",\n            };\n\n            let interpolate_attr = format!(\"@interpolate({interpolation_str}{sampling_str})\");\n            let source = format!(\n                \"\\\n                struct VertexOutput {{\n    @location(0) {interpolate_attr} member: {member_type},\n}}\n\n@fragment\nfn main(input: VertexOutput) {{\n    // ...\n}}\n\"\n            );\n            let module = naga::front::wgsl::parse_str(&source).unwrap();\n\n            Self {\n                source,\n                module,\n                interpolate_attr,\n                entry_point: \"main\",\n            }\n        }\n    }\n}\n\nstruct BindingArrayFixture {\n    module: Module,\n    span: naga::Span,\n    ty_u32: naga::Handle<naga::Type>,\n    ty_array: naga::Handle<naga::Type>,\n    ty_struct: naga::Handle<naga::Type>,\n    validator: naga::valid::Validator,\n}\n\nimpl BindingArrayFixture {\n    fn new() -> Self {\n        let mut module = Module::default();\n        let span = naga::Span::default();\n        let ty_u32 = module.types.insert(\n            naga::Type {\n                name: Some(\"u32\".into()),\n                inner: naga::TypeInner::Scalar(naga::Scalar::U32),\n            },\n            span,\n        );\n        let ty_array = module.types.insert(\n            naga::Type {\n                name: Some(\"array<u32, 10>\".into()),\n                inner: naga::TypeInner::Array {\n                    base: ty_u32,\n                    size: naga::ArraySize::Constant(core::num::NonZeroU32::new(10).unwrap()),\n                    stride: 4,\n                },\n            },\n            span,\n        );\n        let ty_struct = module.types.insert(\n            naga::Type {\n                name: Some(\"S\".into()),\n                inner: naga::TypeInner::Struct {\n                    members: vec![naga::StructMember {\n                        name: Some(\"m\".into()),\n                        ty: ty_u32,\n                        binding: None,\n                        offset: 0,\n                    }],\n                    span: 4,\n                },\n            },\n            span,\n        );\n        let validator = naga::valid::Validator::new(Default::default(), Default::default());\n        BindingArrayFixture {\n            module,\n            span,\n            ty_u32,\n            ty_array,\n            ty_struct,\n            validator,\n        }\n    }\n}\n\n#[test]\nfn binding_arrays_hold_structs() {\n    let mut t = BindingArrayFixture::new();\n    let _binding_array = t.module.types.insert(\n        naga::Type {\n            name: Some(\"binding_array_of_struct\".into()),\n            inner: naga::TypeInner::BindingArray {\n                base: t.ty_struct,\n                size: naga::ArraySize::Dynamic,\n            },\n        },\n        t.span,\n    );\n\n    assert!(t.validator.validate(&t.module).is_ok());\n}\n\n#[test]\nfn binding_arrays_cannot_hold_arrays() {\n    let mut t = BindingArrayFixture::new();\n    let _binding_array = t.module.types.insert(\n        naga::Type {\n            name: Some(\"binding_array_of_array\".into()),\n            inner: naga::TypeInner::BindingArray {\n                base: t.ty_array,\n                size: naga::ArraySize::Dynamic,\n            },\n        },\n        t.span,\n    );\n\n    assert!(t.validator.validate(&t.module).is_err());\n}\n\n#[test]\nfn binding_arrays_cannot_hold_scalars() {\n    let mut t = BindingArrayFixture::new();\n    let _binding_array = t.module.types.insert(\n        naga::Type {\n            name: Some(\"binding_array_of_scalar\".into()),\n            inner: naga::TypeInner::BindingArray {\n                base: t.ty_u32,\n                size: naga::ArraySize::Dynamic,\n            },\n        },\n        t.span,\n    );\n\n    assert!(t.validator.validate(&t.module).is_err());\n}\n\n#[test]\nfn validation_error_messages() {\n    let cases = [(\n        r#\"@group(0) @binding(0) var my_sampler: sampler;\n\n                fn foo(tex: texture_2d<f32>) -> vec4<f32> {\n                    return textureSampleLevel(tex, my_sampler, vec2f(0, 0), 0.0);\n                }\n\n                fn main() {\n                    foo();\n                }\n            \"#,\n        \"\\\nerror: Function [1] 'main' is invalid\n  ┌─ wgsl:7:17\n  │\\x20\\x20\n7 │ ╭                 fn main() {\n8 │ │                     foo();\n  │ │                     ^^^^^ invalid function call\n9 │ │                 }\n  │ ╰─────────────────^ naga::ir::Function [1]\n  │\\x20\\x20\n  = Call to [0] is invalid\n  = Requires 1 arguments, but 0 are provided\n\n\",\n    )];\n\n    for (source, expected_err) in cases {\n        let module = naga::front::wgsl::parse_str(source).unwrap();\n        let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n            .validate(&module)\n            .expect_err(\"module should be invalid\");\n        println!(\"{}\", err.emit_to_string(source));\n        assert_eq!(err.emit_to_string(source), expected_err);\n    }\n}\n\n#[test]\nfn bad_texture_dimensions_level() {\n    fn validate(level: &str) -> Result<ModuleInfo, naga::valid::ValidationError> {\n        let source = format!(\n            r#\"\n            @group(0) @binding(0)\n            var t: texture_1d<f32>;\n            fn f() -> u32 {{\n              return textureDimensions(t, {level});\n            }}\n       \"#\n        );\n        let module = naga::front::wgsl::parse_str(&source).expect(\"module should parse\");\n        valid::Validator::new(Default::default(), valid::Capabilities::all())\n            .validate(&module)\n            .map_err(|err| err.into_inner()) // discard spans\n    }\n\n    fn is_bad_level_error(result: Result<ModuleInfo, naga::valid::ValidationError>) -> bool {\n        matches!(\n            result,\n            Err(naga::valid::ValidationError::Function {\n                handle: _,\n                name: _,\n                source: naga::valid::FunctionError::Expression {\n                    handle: _,\n                    source: naga::valid::ExpressionError::InvalidImageOtherIndexType(_),\n                },\n            })\n        )\n    }\n\n    assert!(is_bad_level_error(validate(\"true\")));\n    assert!(is_bad_level_error(validate(\"1.0\")));\n    assert!(validate(\"1\").is_ok());\n    assert!(validate(\"1i\").is_ok());\n    assert!(validate(\"1\").is_ok());\n}\n\n// Adds IR for `override len: u32` and the type `array<u32, len>`.\nfn make_override_array(module: &mut ir::Module) -> naga::Handle<ir::Type> {\n    let span = naga::Span::default();\n\n    let ty_u32 = module.types.insert(\n        ir::Type {\n            name: Some(\"u32\".into()),\n            inner: ir::TypeInner::Scalar(Scalar::U32),\n        },\n        span,\n    );\n\n    let len = module.overrides.append(\n        ir::Override {\n            name: Some(\"len\".into()),\n            id: None,\n            ty: ty_u32,\n            init: None,\n        },\n        span,\n    );\n\n    module.types.insert(\n        ir::Type {\n            name: Some(\"array<u32, len>\".into()),\n            inner: ir::TypeInner::Array {\n                base: ty_u32,\n                size: ir::ArraySize::Pending(len),\n                stride: 4,\n            },\n        },\n        span,\n    )\n}\n\n// Adds IR for type `array<u32>`.\nfn make_runtime_array(module: &mut ir::Module) -> naga::Handle<ir::Type> {\n    let span = naga::Span::default();\n\n    let ty_u32 = module.types.insert(\n        ir::Type {\n            name: Some(\"u32\".into()),\n            inner: ir::TypeInner::Scalar(Scalar::U32),\n        },\n        span,\n    );\n\n    module.types.insert(\n        ir::Type {\n            name: Some(\"array<u32>\".into()),\n            inner: ir::TypeInner::Array {\n                base: ty_u32,\n                size: ir::ArraySize::Dynamic,\n                stride: 4,\n            },\n        },\n        span,\n    )\n}\n\n// Adds a local variable `var x: ty = ty();`.\nfn make_zero_value_local_variable(fun: &mut Function, ty: naga::Handle<ir::Type>) {\n    let span = naga::Span::default();\n\n    let ex_zero = fun.expressions.append(Expression::ZeroValue(ty), span);\n\n    fun.local_variables.append(\n        naga::LocalVariable {\n            name: Some(\"x\".into()),\n            ty,\n            init: Some(ex_zero),\n        },\n        span,\n    );\n}\n\n#[test]\nfn invalid_local_var_override_sized_array() {\n    // Similar to a test in wgsl_errors::invalid_local_vars.\n    // ```\n    // override len: u32;\n    // var<workgroup> arr: array<u32, len>;\n    // fn f() {\n    //     var x: array<u32, len> = arr;\n    // }\n    // ```\n    let span = naga::Span::default();\n    let mut module = ir::Module::default();\n\n    let ty_array = make_override_array(&mut module);\n\n    let var_arr = module.global_variables.append(\n        naga::GlobalVariable {\n            name: Some(\"arr\".into()),\n            space: naga::AddressSpace::WorkGroup,\n            binding: None,\n            ty: ty_array,\n            init: None,\n            memory_decorations: naga::MemoryDecorations::empty(),\n        },\n        span,\n    );\n\n    let mut fun = Function {\n        name: Some(\"f\".into()),\n        ..Default::default()\n    };\n\n    let ex_global = fun\n        .expressions\n        .append(Expression::GlobalVariable(var_arr), span);\n    let ex_load = fun\n        .expressions\n        .append(Expression::Load { pointer: ex_global }, span);\n\n    fun.local_variables.append(\n        naga::LocalVariable {\n            name: Some(\"x\".into()),\n            ty: ty_array,\n            init: Some(ex_load),\n        },\n        span,\n    );\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        } if local_var_name == \"x\"\n    ));\n}\n\n#[test]\nfn invalid_zero_value_runtime_array() {\n    // Similar to a test in wgsl_errors::invalid_zero_value_constructors.\n    // ```\n    // fn main() {\n    //     var x = array<u32>();\n    // }\n    // ```\n    let span = naga::Span::default();\n    let mut module = ir::Module::default();\n\n    let ty_array = make_runtime_array(&mut module);\n\n    let mut fun = Function {\n        name: Some(\"f\".into()),\n        ..Default::default()\n    };\n\n    make_zero_value_local_variable(&mut fun, ty_array);\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        } if local_var_name == \"x\"\n    ));\n}\n\n#[test]\nfn invalid_zero_value_override_array() {\n    // Similar to a test in wgsl_errors::invalid_zero_value_constructors.\n    // ```\n    // override len: u32;\n    // fn main() {\n    //     var x = array<u32, len>();\n    // }\n    // ```\n    let span = naga::Span::default();\n    let mut module = ir::Module::default();\n\n    let ty_array = make_override_array(&mut module);\n\n    let mut fun = Function {\n        name: Some(\"f\".into()),\n        ..Default::default()\n    };\n\n    make_zero_value_local_variable(&mut fun, ty_array);\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        } if local_var_name == \"x\"\n    ));\n}\n\n#[test]\nfn invalid_zero_value_texture() {\n    // Similar to a test in wgsl_errors::invalid_zero_value_constructors.\n    // ```\n    // fn main() {\n    //     var x = texture_2d<f32>();\n    // }\n    // ```\n    use naga::{ImageClass, ImageDimension, Module, Type, TypeInner};\n\n    let span = naga::Span::default();\n    let mut module = Module::default();\n\n    let ty_texture = module.types.insert(\n        Type {\n            name: Some(\"texture_2d<f32>\".into()),\n            inner: TypeInner::Image {\n                dim: ImageDimension::D2,\n                arrayed: false,\n                class: ImageClass::Sampled {\n                    kind: naga::ScalarKind::Float,\n                    multi: false,\n                },\n            },\n        },\n        span,\n    );\n\n    let mut fun = Function {\n        name: Some(\"f\".into()),\n        ..Default::default()\n    };\n\n    make_zero_value_local_variable(&mut fun, ty_texture);\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        } if local_var_name == \"x\"\n    ));\n}\n\n/// Test for non-zero-value runtime-sized array constructor.\n#[test]\nfn invalid_constructor_runtime_array() {\n    // Similar to a test in wgsl_errors::invalid_zero_value_constructors.\n    // ```\n    // fn main() {\n    //     var x = array<u32>(0, 1, 2);\n    // }\n    // ```\n    let span = naga::Span::default();\n    let mut module = ir::Module::default();\n\n    let ty_array = make_runtime_array(&mut module);\n\n    let mut fun = Function {\n        name: Some(\"f\".into()),\n        ..Default::default()\n    };\n\n    // Create component expressions\n    let ex_0 = fun\n        .expressions\n        .append(Expression::Literal(naga::Literal::U32(0)), span);\n    let ex_1 = fun\n        .expressions\n        .append(Expression::Literal(naga::Literal::U32(1)), span);\n    let ex_2 = fun\n        .expressions\n        .append(Expression::Literal(naga::Literal::U32(2)), span);\n\n    // Create a Compose expression to construct the array\n    let ex_compose = fun.expressions.append(\n        Expression::Compose {\n            ty: ty_array,\n            components: vec![ex_0, ex_1, ex_2],\n        },\n        span,\n    );\n\n    fun.local_variables.append(\n        naga::LocalVariable {\n            name: Some(\"x\".into()),\n            ty: ty_array,\n            init: Some(ex_compose),\n        },\n        span,\n    );\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        } if local_var_name == \"x\"\n    ));\n}\n\n#[test]\nfn invalid_constructor_unsized_struct() {\n    // Similar to a test in wgsl_errors::invalid_zero_value_constructors:\n    // ```\n    // struct Unsized { data: array<f32> }\n    // fn main() {\n    //     var x: Unsized = Unsized();\n    // }\n    // ```\n    use naga::{Module, StructMember, Type, TypeInner};\n\n    let span = naga::Span::default();\n    let mut module = Module::default();\n\n    let ty_array = make_runtime_array(&mut module);\n\n    let ty_unsized = module.types.insert(\n        Type {\n            name: Some(\"Unsized\".into()),\n            inner: TypeInner::Struct {\n                members: vec![StructMember {\n                    name: Some(\"data\".into()),\n                    ty: ty_array,\n                    binding: None,\n                    offset: 0,\n                }],\n                span: 4,\n            },\n        },\n        span,\n    );\n\n    let mut fun = Function {\n        name: Some(\"main\".into()),\n        ..Default::default()\n    };\n\n    make_zero_value_local_variable(&mut fun, ty_unsized);\n\n    module.functions.append(fun, span);\n\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .into_inner();\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        },\n    ));\n}\n\n#[test]\nfn arity_check() {\n    use ir::MathFunction as Mf;\n    use naga::Span;\n    let _ = env_logger::builder().is_test(true).try_init();\n\n    type Result = core::result::Result<ModuleInfo, naga::valid::ValidationError>;\n\n    fn validate(fun: ir::MathFunction, args: &[usize]) -> Result {\n        let nowhere = Span::default();\n        let mut module = ir::Module::default();\n        let ty_f32 = module.types.insert(\n            ir::Type {\n                name: Some(\"f32\".to_string()),\n                inner: ir::TypeInner::Scalar(ir::Scalar::F32),\n            },\n            nowhere,\n        );\n        let mut f = ir::Function {\n            result: Some(ir::FunctionResult {\n                ty: ty_f32,\n                binding: None,\n            }),\n            ..ir::Function::default()\n        };\n        let ex_zero = f\n            .expressions\n            .append(ir::Expression::ZeroValue(ty_f32), nowhere);\n        let ex_pow = f.expressions.append(\n            dbg!(ir::Expression::Math {\n                fun,\n                arg: ex_zero,\n                arg1: args.contains(&1).then_some(ex_zero),\n                arg2: args.contains(&2).then_some(ex_zero),\n                arg3: args.contains(&3).then_some(ex_zero),\n            }),\n            nowhere,\n        );\n        f.body = ir::Block::from_vec(vec![\n            ir::Statement::Emit(naga::Range::new_from_bounds(ex_pow, ex_pow)),\n            ir::Statement::Return {\n                value: Some(ex_pow),\n            },\n        ]);\n        module.functions.append(f, nowhere);\n        valid::Validator::new(Default::default(), valid::Capabilities::all())\n            .validate(&module)\n            .map_err(|err| err.into_inner()) // discard spans\n    }\n\n    assert!(validate(Mf::Sin, &[]).is_ok());\n    assert!(validate(Mf::Sin, &[1]).is_err());\n    assert!(validate(Mf::Sin, &[3]).is_err());\n    assert!(validate(Mf::Pow, &[1]).is_ok());\n    assert!(validate(Mf::Pow, &[3]).is_err());\n}\n\n#[test]\nfn global_use_scalar() {\n    let source = \"\n@group(0) @binding(0)\nvar<storage, read_write> global: u32;\n\n@compute @workgroup_size(64)\nfn main() {\n    let used = &global;\n}\n    \";\n\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    let global = module.global_variables.iter().next().unwrap().0;\n    assert_eq!(\n        info.get_entry_point(0)[global],\n        naga::valid::GlobalUse::QUERY\n    );\n}\n\n#[test]\nfn global_use_array() {\n    let source = \"\n@group(0) @binding(0)\nvar<storage, read_write> global: array<f32>;\n\n@compute @workgroup_size(64)\nfn main() {\n    let used = &global;\n}\n    \";\n\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    let global = module.global_variables.iter().next().unwrap().0;\n    assert_eq!(\n        info.get_entry_point(0)[global],\n        naga::valid::GlobalUse::QUERY\n    );\n}\n\n#[test]\nfn global_use_array_index() {\n    let source = \"\n@group(0) @binding(0)\nvar<storage, read_write> global: array<f32>;\n\n@compute @workgroup_size(64)\nfn main() {\n    let used = &global[0];\n}\n    \";\n\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    let global = module.global_variables.iter().next().unwrap().0;\n    assert_eq!(\n        info.get_entry_point(0)[global],\n        naga::valid::GlobalUse::QUERY\n    );\n}\n\n#[test]\nfn global_use_phony() {\n    let source = \"\n@group(0) @binding(0)\nvar<storage, read_write> global: u32;\n\n@compute @workgroup_size(64)\nfn main() {\n    _ = &global;\n}\n    \";\n\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n\n    let global = module.global_variables.iter().next().unwrap().0;\n    assert_eq!(\n        info.get_entry_point(0)[global],\n        naga::valid::GlobalUse::QUERY\n    );\n}\n\n#[test]\nfn global_use_unreachable() {\n    // We should allow statements after `return`, and such statements should\n    // still contribute to global usage. (Unreachable statements should not\n    // contribute to uniformity analysis, but there are multiple issues with\n    // the current implementation of uniformity analysis, see #4369.)\n\n    let source = \"\n@group(0) @binding(0)\nvar<storage, read_write> global: u32;\n\n@compute @workgroup_size(64)\nfn main() {\n    var used: u32;\n    return;\n    used = global;\n}\n    \";\n\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let mut validator = valid::Validator::new(Default::default(), valid::Capabilities::all());\n    let info = validator.validate(&module).unwrap();\n\n    let global = module.global_variables.iter().next().unwrap().0;\n    assert_eq!(\n        info.get_entry_point(0)[global],\n        naga::valid::GlobalUse::READ,\n    );\n}\n\n/// Parse and validate the module defined in `source`.\n///\n/// Panics if unsuccessful.\nfn parse_validate(source: &str) -> (Module, ModuleInfo) {\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .unwrap();\n    (module, info)\n}\n\n/// Helper for `process_overrides` tests.\n///\n/// The goal of these tests is to verify that `process_overrides` accepts cases\n/// where all necessary overrides are specified (even if some unnecessary ones\n/// are not), and does not accept cases where necessary overrides are missing.\n/// \"Necessary\" means that the override is referenced in some way by some\n/// function reachable from the specified entry point.\n///\n/// Each test passes a source snippet containing a compute entry point `used`\n/// that makes use of the override `ov` in some way. We augment that with (1)\n/// the definition of `ov` and (2) a dummy entrypoint that does not use the\n/// override, and then test the matrix of (specified or not) x (used or not).\n///\n/// The optional `unused_body` can introduce additional objects to the module,\n/// to verify that they are adjusted correctly by compaction.\nfn override_test(test_case: &str, unused_body: Option<&str>) {\n    use hashbrown::HashMap;\n    use naga::back::pipeline_constants::PipelineConstantError;\n\n    let source = [\n        \"override ov: u32;\\n\",\n        test_case,\n        \"@compute @workgroup_size(64)\nfn unused() {\n\",\n        unused_body.unwrap_or_default(),\n        \"}\n\",\n    ]\n    .concat();\n    let (module, info) = parse_validate(&source);\n\n    let overrides = HashMap::from([(String::from(\"ov\"), 1.)]);\n\n    // Can translate `unused` with or without the override\n    naga::back::pipeline_constants::process_overrides(\n        &module,\n        &info,\n        Some((ir::ShaderStage::Compute, \"unused\")),\n        &HashMap::new(),\n    )\n    .unwrap();\n    naga::back::pipeline_constants::process_overrides(\n        &module,\n        &info,\n        Some((ir::ShaderStage::Compute, \"unused\")),\n        &overrides,\n    )\n    .unwrap();\n\n    // Cannot translate `used` without the override\n    let err = naga::back::pipeline_constants::process_overrides(\n        &module,\n        &info,\n        Some((ir::ShaderStage::Compute, \"used\")),\n        &HashMap::new(),\n    )\n    .unwrap_err();\n    assert!(matches!(err, PipelineConstantError::MissingValue(name) if name == \"ov\"));\n\n    // Can translate `used` if the override is specified\n    naga::back::pipeline_constants::process_overrides(\n        &module,\n        &info,\n        Some((ir::ShaderStage::Compute, \"used\")),\n        &overrides,\n    )\n    .unwrap();\n}\n\n#[test]\nfn override_in_workgroup_size() {\n    override_test(\n        \"\n@compute @workgroup_size(ov)\nfn used() {\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_in_workgroup_size_nested() {\n    // Initializer for override used in workgroup size refers to another\n    // override.\n    override_test(\n        \"\noverride ov2: u32 = ov + 5;\n\n@compute @workgroup_size(ov2)\nfn used() {\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_in_function() {\n    override_test(\n        \"\nfn foo() -> u32 {\n    return ov;\n}\n\n@compute @workgroup_size(64)\nfn used() {\n    foo();\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_in_entrypoint() {\n    override_test(\n        \"\nfn foo() -> u32 {\n    return ov;\n}\n\n@compute @workgroup_size(64)\nfn used() {\n    foo();\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_in_array_size() {\n    override_test(\n        \"\nvar<workgroup> arr: array<u32, ov>;\n\n@compute @workgroup_size(64)\nfn used() {\n    _ = arr[5];\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_in_global_init() {\n    override_test(\n        \"\nvar<private> foo: u32 = ov;\n\n@compute @workgroup_size(64)\nfn used() {\n    _ = foo;\n}\n\",\n        None,\n    );\n}\n\n#[test]\nfn override_with_multiple_globals() {\n    // Test that when compaction of the `unused` entrypoint removes `arr1`, the\n    // handle to `arr2` is adjusted correctly.\n    override_test(\n        \"\nvar<workgroup> arr1: array<u32, ov>;\nvar<workgroup> arr2: array<u32, 4>;\n\n@compute @workgroup_size(64)\nfn used() {\n    _ = arr1[5];\n}\n\",\n        Some(\"_ = arr2[3];\"),\n    );\n}\n\n/// Expects parsing `input` to succeed and its validation to fail with error equal to `snapshot`.\n#[track_caller]\nfn check_wgsl_validation_error_message(input: &str, snapshot: &str) {\n    let module = naga::front::wgsl::parse_str(input).unwrap();\n    let err = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect_err(\"module should be invalid\")\n        .emit_to_string(input);\n    if err != snapshot {\n        for diff in diff::lines(snapshot, &err) {\n            match diff {\n                diff::Result::Left(l) => println!(\"-{l}\"),\n                diff::Result::Both(l, _) => println!(\" {l}\"),\n                diff::Result::Right(r) => println!(\"+{r}\"),\n            }\n        }\n        panic!(\"Error does not match the expected snapshot\");\n    }\n}\n\n#[test]\nfn image_store_type_mismatch() {\n    check_wgsl_validation_error_message(\n        r#\"\n@group(0) @binding(0)\nvar input_texture: texture_depth_2d;\n@group(0) @binding(1)\nvar input_sampler: sampler;\n@group(0) @binding(2)\nvar output_texture: texture_storage_2d<r32float,write>;\n\n@compute @workgroup_size(1, 1)\nfn main() {\n    let d: vec4<f32> = textureGather(input_texture, input_sampler, vec2f(0.0));\n    let min_d = min(min(d[0], d[1]), min(d[2], d[3]));\n    textureStore(output_texture, vec2u(1), min_d);\n}\n\"#,\n        r#\"error: Entry point main at Compute is invalid\n   ┌─ wgsl:12:17\n   │\n12 │     let min_d = min(min(d[0], d[1]), min(d[2], d[3]));\n   │                 ^^^ this value is of type Scalar(Scalar { kind: Float, width: 4 })\n13 │     textureStore(output_texture, vec2u(1), min_d);\n   │     ^^^^^^^^^^^^ expects a value argument of type Vector { size: Quad, scalar: Scalar { kind: Float, width: 4 } }\n   │\n   = Image store value parameter type mismatch\n\n\"#,\n    );\n}\n\n#[test]\nfn unexpected_task_payload() {\n    let mut test_spans = TestSpanGenerator::default();\n    let mut module = Module::default();\n\n    let ty_payload = module.types.insert(\n        ir::Type {\n            name: Some(\"u32\".into()),\n            inner: ir::TypeInner::Scalar(naga::Scalar::U32),\n        },\n        test_spans.next(),\n    );\n\n    let err_span = test_spans.next();\n    let payload_handle = module.global_variables.append(\n        ir::GlobalVariable {\n            name: Some(\"task_payload\".into()),\n            space: ir::AddressSpace::TaskPayload,\n            binding: None,\n            ty: ty_payload,\n            init: None,\n            memory_decorations: ir::MemoryDecorations::empty(),\n        },\n        err_span,\n    );\n\n    let entry_point = ir::EntryPoint {\n        name: \"main\".into(),\n        stage: ir::ShaderStage::Compute,\n        early_depth_test: None,\n        workgroup_size: [1, 1, 1],\n        workgroup_size_overrides: None,\n        function: ir::Function::default(),\n        mesh_info: None,\n        task_payload: Some(payload_handle), // invalid for compute stage\n        incoming_ray_payload: None,\n    };\n    module.entry_points.push(entry_point);\n\n    let err = expect_validation_error_with_span(\n        &module,\n        ValidationFlags::default(),\n        Capabilities::MESH_SHADER,\n        err_span,\n    );\n\n    assert!(matches!(\n        err,\n        valid::ValidationError::EntryPoint {\n            source: valid::EntryPointError::UnexpectedTaskPayload,\n            ..\n        }\n    ));\n}\n\n#[test]\nfn coherent_requires_capability() {\n    let module = naga::front::wgsl::parse_str(\n        \"struct S { x: u32 }\n         @group(0) @binding(0) @coherent var<storage, read_write> buf: S;\",\n    )\n    .expect(\"module should parse\");\n\n    let err = valid::Validator::new(ValidationFlags::default(), Capabilities::empty())\n        .validate(&module)\n        .expect_err(\"should fail without capability\");\n    assert!(matches!(\n        err.into_inner(),\n        valid::ValidationError::GlobalVariable {\n            source: valid::GlobalVariableError::CoherentNotSupported,\n            ..\n        }\n    ));\n\n    let result = valid::Validator::new(\n        ValidationFlags::default(),\n        Capabilities::MEMORY_DECORATION_COHERENT,\n    )\n    .validate(&module);\n    assert!(result.is_ok(), \"should succeed with capability: {result:?}\");\n}\n\n#[test]\nfn volatile_requires_capability() {\n    let module = naga::front::wgsl::parse_str(\n        \"struct S { x: u32 }\n         @group(0) @binding(0) @volatile var<storage, read_write> buf: S;\",\n    )\n    .expect(\"module should parse\");\n\n    let err = valid::Validator::new(ValidationFlags::default(), Capabilities::empty())\n        .validate(&module)\n        .expect_err(\"should fail without capability\");\n    assert!(matches!(\n        err.into_inner(),\n        valid::ValidationError::GlobalVariable {\n            source: valid::GlobalVariableError::VolatileNotSupported,\n            ..\n        }\n    ));\n\n    let result = valid::Validator::new(\n        ValidationFlags::default(),\n        Capabilities::MEMORY_DECORATION_VOLATILE,\n    )\n    .validate(&module);\n    assert!(result.is_ok(), \"should succeed with capability: {result:?}\");\n}\n\n#[test]\nfn memory_decorations_require_storage_address_space() {\n    let module = naga::front::wgsl::parse_str(\"@coherent var<workgroup> wg: array<u32, 4>;\")\n        .expect(\"module should parse\");\n\n    let err = valid::Validator::new(\n        ValidationFlags::default(),\n        Capabilities::MEMORY_DECORATION_COHERENT | Capabilities::MEMORY_DECORATION_VOLATILE,\n    )\n    .validate(&module)\n    .expect_err(\"should fail on non-storage address space\");\n    assert!(matches!(\n        err.into_inner(),\n        valid::ValidationError::GlobalVariable {\n            source: valid::GlobalVariableError::InvalidMemoryDecorationsAddressSpace,\n            ..\n        }\n    ));\n}\n"
  },
  {
    "path": "naga/tests/naga/wgsl_errors.rs",
    "content": "//! Tests for the WGSL front end.\n//!\n//! This file also contains some tests of the module validator. In some cases,\n//! the validator and the frontend both raise an error, and it is easier to\n//! have both tests in one place. In other cases, it might be more appropriate\n//! for the validator tests to be in the `validation` test suite.\n\n#![cfg(feature = \"wgsl-in\")]\n\nuse naga::{\n    compact::KeepUnused,\n    front::wgsl::{EnableExtension, ImplementedEnableExtension},\n    valid::{self, Capabilities},\n};\n\n#[track_caller]\nfn check(input: &str, snapshot: &str) {\n    let output = match naga::front::wgsl::parse_str(input) {\n        Ok(_) => panic!(\"expected parser error, but parsing succeeded!\"),\n        Err(err) => err.emit_to_string(input),\n    };\n    if output != snapshot {\n        for diff in diff::lines(snapshot, &output) {\n            match diff {\n                diff::Result::Left(l) => println!(\"-{l}\"),\n                diff::Result::Both(l, _) => println!(\" {l}\"),\n                diff::Result::Right(r) => println!(\"+{r}\"),\n            }\n        }\n        panic!(\"Error snapshot failed\");\n    }\n}\n\n#[track_caller]\nfn check_error_matches(input: &str, expected_substring: &str) {\n    let result = naga::front::wgsl::parse_str(input);\n    let Err(ref err) = result else {\n        panic!(\"expected ParseError, got {result:#?}\");\n    };\n    let message = err.message();\n    if !message.contains(expected_substring) {\n        panic!(\"expected error containing '{expected_substring}', got '{message}'\",);\n    }\n}\n\n#[track_caller]\nfn check_success(input: &str) {\n    match naga::front::wgsl::parse_str(input) {\n        Ok(_) => {}\n        Err(err) => {\n            panic!(\n                \"expected success, but parsing failed with:\\n{}\",\n                err.emit_to_string(input)\n            );\n        }\n    }\n}\n\n#[test]\nfn very_negative_integers() {\n    // wgpu#4492\n    check(\n        \"const i32min = -0x80000000i;\",\n        r###\"error: numeric literal not representable by target type: `0x80000000i`\n  ┌─ wgsl:1:17\n  │\n1 │ const i32min = -0x80000000i;\n  │                 ^^^^^^^^^^^ numeric literal not representable by target type\n\n\"###,\n    );\n}\n\n#[test]\nfn reserved_identifier_prefix() {\n    check(\n        \"var __bad;\",\n        r###\"error: Identifier starts with a reserved prefix: `__bad`\n  ┌─ wgsl:1:5\n  │\n1 │ var __bad;\n  │     ^^^^^ invalid identifier\n\n\"###,\n    );\n}\n\n#[test]\nfn function_without_identifier() {\n    check(\n        \"fn () {}\",\n        r###\"error: expected identifier, found \"(\"\n  ┌─ wgsl:1:4\n  │\n1 │ fn () {}\n  │    ^ expected identifier\n\n\"###,\n    );\n}\n\n#[test]\nfn invalid_integer() {\n    check(\n        \"fn foo([location(1.)] x: i32) {}\",\n        r###\"error: expected identifier, found \"[\"\n  ┌─ wgsl:1:8\n  │\n1 │ fn foo([location(1.)] x: i32) {}\n  │        ^ expected identifier\n\n\"###,\n    );\n}\n\n#[test]\nfn invalid_float() {\n    check(\n        \"const scale: f32 = 1.1.;\",\n        r###\"error: expected identifier, found \";\"\n  ┌─ wgsl:1:24\n  │\n1 │ const scale: f32 = 1.1.;\n  │                        ^ expected identifier\n\n\"###,\n    );\n}\n\n#[test]\nfn invalid_texture_sample_type() {\n    check(\n        \"var x: texture_2d<bool>;\",\n        r###\"error: texture sample type must be one of f32, i32 or u32, but found bool\n  ┌─ wgsl:1:19\n  │\n1 │ var x: texture_2d<bool>;\n  │                   ^^^^ must be one of f32, i32 or u32\n\n\"###,\n    );\n}\n\n#[test]\nfn unknown_identifier() {\n    check(\n        r###\"\n              fn f(x: f32) -> f32 {\n                  return x * schmoo;\n              }\n          \"###,\n        r###\"error: no definition in scope for identifier: `schmoo`\n  ┌─ wgsl:3:30\n  │\n3 │                   return x * schmoo;\n  │                              ^^^^^^ unknown identifier\n\n\"###,\n    );\n}\n\n#[test]\nfn bad_texture() {\n    check(\n        r#\"\n            @group(0) @binding(0) var sampler1 : sampler;\n\n            @fragment\n            fn main() -> @location(0) vec4<f32> {\n                let a = 3;\n                return textureSample(a, sampler1, vec2<f32>(0.0));\n            }\n        \"#,\n        r#\"error: expected an image, but found `a` which is not an image\n  ┌─ wgsl:7:38\n  │\n7 │                 return textureSample(a, sampler1, vec2<f32>(0.0));\n  │                                      ^ not an image\n\n\"#,\n    );\n}\n\n#[test]\nfn bad_type_cast() {\n    check(\n        r#\"\n            fn x() -> i32 {\n                return i32(vec2<f32>(0.0));\n            }\n        \"#,\n        r#\"error: cannot cast a vec2<f32> to a i32\n  ┌─ wgsl:3:28\n  │\n3 │                 return i32(vec2<f32>(0.0));\n  │                            ^^^^^^^^^^^^^^ cannot cast a vec2<f32> to a i32\n\n\"#,\n    );\n}\n\n#[test]\nfn cross_vec2() {\n    check(\n        r#\"\n            fn x() -> f32 {\n                return cross(vec2(0., 1.), vec2(0., 1.));\n            }\n        \"#,\n        \"\\\nerror: wrong type passed as argument #1 to `cross`\n  ┌─ wgsl:3:24\n  │\n3 │                 return cross(vec2(0., 1.), vec2(0., 1.));\n  │                        ^^^^^ ^^^^^^^^^^^^ argument #1 has type `vec2<{AbstractFloat}>`\n  │\n  = note: `cross` accepts the following types for argument #1:\n  = note: allowed type: vec3<{AbstractFloat}>\n  = note: allowed type: vec3<f32>\n  = note: allowed type: vec3<f16>\n  = note: allowed type: vec3<f64>\n\n\",\n    );\n}\n\n#[test]\nfn cross_vec4() {\n    check(\n        r#\"\n            fn x() -> f32 {\n                return cross(vec4(0., 1., 2., 3.), vec4(0., 1., 2., 3.));\n            }\n        \"#,\n        \"\\\nerror: wrong type passed as argument #1 to `cross`\n  ┌─ wgsl:3:24\n  │\n3 │                 return cross(vec4(0., 1., 2., 3.), vec4(0., 1., 2., 3.));\n  │                        ^^^^^ ^^^^^^^^^^^^^^^^^^^^ argument #1 has type `vec4<{AbstractFloat}>`\n  │\n  = note: `cross` accepts the following types for argument #1:\n  = note: allowed type: vec3<{AbstractFloat}>\n  = note: allowed type: vec3<f32>\n  = note: allowed type: vec3<f16>\n  = note: allowed type: vec3<f64>\n\n\",\n    );\n}\n\n#[test]\nfn type_not_constructible() {\n    check(\n        r#\"\n            fn x() {\n                _ = atomic<i32>(0);\n            }\n        \"#,\n        r#\"error: type `atomic<i32>` is not constructible\n  ┌─ wgsl:3:21\n  │\n3 │                 _ = atomic<i32>(0);\n  │                     ^^^^^^^^^^^ type is not constructible\n\n\"#,\n    );\n}\n\n#[test]\nfn type_not_inferable() {\n    check(\n        r#\"\n            fn x() {\n                _ = mat2x2();\n            }\n        \"#,\n        r#\"error: type can't be inferred\n  ┌─ wgsl:3:21\n  │\n3 │                 _ = mat2x2();\n  │                     ^^^^^^ type can't be inferred\n\n\"#,\n    );\n}\n\n#[test]\nfn unexpected_constructor_parameters() {\n    check(\n        r#\"\n            fn x() {\n                _ = i32(0, 1);\n            }\n        \"#,\n        r#\"error: unexpected components\n  ┌─ wgsl:3:28\n  │\n3 │                 _ = i32(0, 1);\n  │                            ^ unexpected components\n\n\"#,\n    );\n}\n\n#[test]\nfn constructor_parameter_type_mismatch() {\n    check(\n        r#\"\n            fn x() {\n                _ = mat2x2<f32>(array(0, 1), vec2(2, 3));\n            }\n        \"#,\n        \"error: automatic conversions cannot convert `array<{AbstractInt}, 2>` to `vec2<f32>`\n  ┌─ wgsl:3:21\n  │\n3 │                 _ = mat2x2<f32>(array(0, 1), vec2(2, 3));\n  │                     ^^^^^^^^^^^ ^^^^^^^^^^^ this expression has type array<{AbstractInt}, 2>\n  │                     │\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\n  │                     a value of type vec2<f32> is required here\n\n\",\n    );\n}\n\n#[test]\nfn vector_constructor_incorrect_component_count() {\n    // Too few components\n    check(\n        r#\"\n            fn x() {\n                _ = vec4(1, 2, 3);\n            }\n        \"#,\n        r#\"error: Constructor expects 4 components, found 3\n  ┌─ wgsl:3:21\n  │\n3 │                 _ = vec4(1, 2, 3);\n  │                     ^^^^^^^^^^^^^ see msg\n\n\"#,\n    );\n\n    // Too many components\n    check(\n        r#\"\n            fn x() {\n                _ = vec4(1, 2, 3, 4, 5);\n            }\n        \"#,\n        r#\"error: Constructor expects 4 components, found 5\n  ┌─ wgsl:3:21\n  │\n3 │                 _ = vec4(1, 2, 3, 4, 5);\n  │                     ^^^^^^^^^^^^^^^^^^^ see msg\n\n\"#,\n    );\n\n    // The outer constructor has the correct number of components, but only\n    // because the inner constructor has too many.\n    check(\n        r#\"\n            fn x() {\n                _ = vec4(1, vec2(2, 3, 4));\n            }\n        \"#,\n        r#\"error: Constructor expects 2 components, found 3\n  ┌─ wgsl:3:29\n  │\n3 │                 _ = vec4(1, vec2(2, 3, 4));\n  │                             ^^^^^^^^^^^^^ see msg\n\n\"#,\n    );\n}\n\n#[test]\nfn vector_constructor_type_mismatch() {\n    check(\n        r#\"\n            fn x(a: u32) -> vec2f {\n                return vec2f(a);\n            }\n        \"#,\n        r#\"error: wrong type passed as argument #1 to `vec2<f32>`\n  ┌─ wgsl:3:24\n  │\n3 │                 return vec2f(a);\n  │                        ^^^^^ ^ argument #1 has type `u32`\n  │\n  = note: `vec2<f32>` accepts the following types for argument #1:\n  = note: allowed type: f32\n\n\"#,\n    );\n}\n\n#[test]\nfn bad_texture_sample_type() {\n    check(\n        r#\"\n            @group(0) @binding(0) var sampler1 : sampler;\n            @group(0) @binding(1) var texture : texture_2d<bool>;\n\n            @fragment\n            fn main() -> @location(0) vec4<f32> {\n                return textureSample(texture, sampler1, vec2<f32>(0.0));\n            }\n        \"#,\n        r#\"error: texture sample type must be one of f32, i32 or u32, but found bool\n  ┌─ wgsl:3:60\n  │\n3 │             @group(0) @binding(1) var texture : texture_2d<bool>;\n  │                                                            ^^^^ must be one of f32, i32 or u32\n\n\"#,\n    );\n}\n\n#[test]\nfn bad_for_initializer() {\n    check(\n        r#\"\n            fn x() {\n                for ({};;) {}\n            }\n        \"#,\n        r#\"error: expected for loop initializer statement (`var`/`let`/`const` declaration, assignment, `i++`/`i--` statement, function call), found \"{\"\n  ┌─ wgsl:3:22\n  │\n3 │                 for ({};;) {}\n  │                      ^ expected for loop initializer statement (`var`/`let`/`const` declaration, assignment, `i++`/`i--` statement, function call)\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_storage_class() {\n    check(\n        r#\"\n            @group(0) @binding(0) var<bad> texture: texture_2d<f32>;\n        \"#,\n        r#\"error: unknown address space: `bad`\n  ┌─ wgsl:2:39\n  │\n2 │             @group(0) @binding(0) var<bad> texture: texture_2d<f32>;\n  │                                       ^^^ unknown address space\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_attribute() {\n    check(\n        r#\"\n            @a\n            fn x() {}\n        \"#,\n        r#\"error: unknown attribute: `a`\n  ┌─ wgsl:2:14\n  │\n2 │             @a\n  │              ^ unknown attribute\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_built_in() {\n    check(\n        r#\"\n            fn x(@builtin(unknown_built_in) y: u32) {}\n        \"#,\n        r#\"error: unknown builtin: `unknown_built_in`\n  ┌─ wgsl:2:27\n  │\n2 │             fn x(@builtin(unknown_built_in) y: u32) {}\n  │                           ^^^^^^^^^^^^^^^^ unknown builtin\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_access() {\n    check(\n        r#\"\n            var<storage,unknown_access> x: array<u32>;\n        \"#,\n        r#\"error: unknown access: `unknown_access`\n  ┌─ wgsl:2:25\n  │\n2 │             var<storage,unknown_access> x: array<u32>;\n  │                         ^^^^^^^^^^^^^^ unknown access\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_ident() {\n    check(\n        r#\"\n            fn main() {\n                let a = b;\n            }\n        \"#,\n        r#\"error: no definition in scope for identifier: `b`\n  ┌─ wgsl:3:25\n  │\n3 │                 let a = b;\n  │                         ^ unknown identifier\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_scalar_type() {\n    check(\n        r#\"\n            const a = vec2<vec2f>();\n        \"#,\n        r#\"error: unknown scalar type: `vec2f`\n  ┌─ wgsl:2:28\n  │\n2 │             const a = vec2<vec2f>();\n  │                            ^^^^^ unknown scalar type\n  │\n  = note: Valid scalar types are f32, f64, i32, u32, bool\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_type() {\n    check(\n        r#\"\n            const a: Vec = 10;\n        \"#,\n        r#\"error: no definition in scope for identifier: `Vec`\n  ┌─ wgsl:2:22\n  │\n2 │             const a: Vec = 10;\n  │                      ^^^ unknown identifier\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_storage_format() {\n    check(\n        r#\"\n            var storage1: texture_storage_1d<rgba>;\n        \"#,\n        r#\"error: unknown storage format: `rgba`\n  ┌─ wgsl:2:46\n  │\n2 │             var storage1: texture_storage_1d<rgba>;\n  │                                              ^^^^ unknown storage format\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_conservative_depth() {\n    check(\n        r#\"\n            @early_depth_test(abc) fn main() {}\n        \"#,\n        r#\"error: unknown conservative depth: `abc`\n  ┌─ wgsl:2:31\n  │\n2 │             @early_depth_test(abc) fn main() {}\n  │                               ^^^ unknown conservative depth\n\n\"#,\n    );\n}\n\n#[test]\nfn struct_member_size_too_low() {\n    check(\n        r#\"\n            struct Bar {\n                @size(0) data: array<f32>\n            }\n        \"#,\n        r#\"error: struct member size must be at least 4\n  ┌─ wgsl:3:23\n  │\n3 │                 @size(0) data: array<f32>\n  │                       ^ must be at least 4\n\n\"#,\n    );\n}\n\n#[test]\nfn struct_member_align_too_low() {\n    check(\n        r#\"\n            struct Bar {\n                @align(8) data: vec3<f32>\n            }\n        \"#,\n        r#\"error: struct member alignment must be at least 16\n  ┌─ wgsl:3:24\n  │\n3 │                 @align(8) data: vec3<f32>\n  │                        ^ must be at least 16\n\n\"#,\n    );\n}\n\n#[test]\nfn struct_member_non_po2_align() {\n    check(\n        r#\"\n            struct Bar {\n                @align(7) data: array<f32>\n            }\n        \"#,\n        r#\"error: struct member alignment must be a power of 2\n  ┌─ wgsl:3:24\n  │\n3 │                 @align(7) data: array<f32>\n  │                        ^ must be a power of 2\n\n\"#,\n    );\n}\n\n#[test]\nfn inconsistent_binding() {\n    check(\n        r#\"\n        fn foo(@builtin(vertex_index) @location(0) x: u32) {}\n        \"#,\n        r#\"error: input/output binding is not consistent\n  ┌─ wgsl:2:16\n  │\n2 │         fn foo(@builtin(vertex_index) @location(0) x: u32) {}\n  │                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ input/output binding is not consistent\n\n\"#,\n    );\n}\n\n#[test]\nfn unknown_local_function() {\n    check(\n        r#\"\n            fn x() {\n                for (a();;) {}\n            }\n        \"#,\n        r#\"error: no definition in scope for identifier: `a`\n  ┌─ wgsl:3:22\n  │\n3 │                 for (a();;) {}\n  │                      ^ unknown identifier\n\n\"#,\n    );\n}\n\n#[test]\nfn let_type_mismatch() {\n    check(\n        r#\"\n            const x: i32 = 1.0;\n        \"#,\n        r#\"error: the type of `x` is expected to be `i32`, but got `{AbstractFloat}`\n  ┌─ wgsl:2:19\n  │\n2 │             const x: i32 = 1.0;\n  │                   ^ definition of `x`\n\n\"#,\n    );\n\n    check(\n        r#\"\n            fn foo() {\n                let x: f32 = true;\n            }\n        \"#,\n        r#\"error: the type of `x` is expected to be `f32`, but got `bool`\n  ┌─ wgsl:3:21\n  │\n3 │                 let x: f32 = true;\n  │                     ^ definition of `x`\n\n\"#,\n    );\n}\n\n#[test]\nfn var_type_mismatch() {\n    check(\n        r#\"\n            fn foo() {\n                var x: f32 = 1u;\n            }\n        \"#,\n        r#\"error: the type of `x` is expected to be `f32`, but got `u32`\n  ┌─ wgsl:3:21\n  │\n3 │                 var x: f32 = 1u;\n  │                     ^ definition of `x`\n\n\"#,\n    );\n}\n\n#[test]\nfn local_var_missing_type() {\n    check(\n        r#\"\n            fn foo() {\n                var x;\n            }\n        \"#,\n        r#\"error: declaration of `x` needs a type specifier or initializer\n  ┌─ wgsl:3:21\n  │\n3 │                 var x;\n  │                     ^ needs a type specifier or initializer\n\n\"#,\n    );\n}\n\n#[test]\nfn reserved_keyword() {\n    // global var\n    check(\n        r#\"\n            var case: bool = true;\n        \"#,\n        r###\"error: name `case` is a reserved keyword\n  ┌─ wgsl:2:17\n  │\n2 │             var case: bool = true;\n  │                 ^^^^ definition of `case`\n\n\"###,\n    );\n\n    // global constant\n    check(\n        r#\"\n            const break: bool = true;\n            fn foo() {\n                var foo = break;\n            }\n        \"#,\n        r###\"error: name `break` is a reserved keyword\n  ┌─ wgsl:2:19\n  │\n2 │             const break: bool = true;\n  │                   ^^^^^ definition of `break`\n\n\"###,\n    );\n\n    // local let\n    check(\n        r#\"\n            fn foo() {\n                let enable: f32 = 1.0;\n            }\n        \"#,\n        r###\"error: name `enable` is a reserved keyword\n  ┌─ wgsl:3:21\n  │\n3 │                 let enable: f32 = 1.0;\n  │                     ^^^^^^ definition of `enable`\n\n\"###,\n    );\n\n    // local var\n    check(\n        r#\"\n            fn foo() {\n                var default: f32 = 1.0;\n            }\n        \"#,\n        r###\"error: name `default` is a reserved keyword\n  ┌─ wgsl:3:21\n  │\n3 │                 var default: f32 = 1.0;\n  │                     ^^^^^^^ definition of `default`\n\n\"###,\n    );\n\n    // fn name\n    check(\n        r#\"\n            fn break() {}\n        \"#,\n        r###\"error: name `break` is a reserved keyword\n  ┌─ wgsl:2:16\n  │\n2 │             fn break() {}\n  │                ^^^^^ definition of `break`\n\n\"###,\n    );\n\n    // struct\n    check(\n        r#\"\n            struct override {}\n        \"#,\n        r###\"error: name `override` is a reserved keyword\n  ┌─ wgsl:2:20\n  │\n2 │             struct override {}\n  │                    ^^^^^^^^ definition of `override`\n\n\"###,\n    );\n\n    // struct member\n    check(\n        r#\"\n            struct Foo { switch: f32 }\n        \"#,\n        r###\"error: name `switch` is a reserved keyword\n  ┌─ wgsl:2:26\n  │\n2 │             struct Foo { switch: f32 }\n  │                          ^^^^^^ definition of `switch`\n\n\"###,\n    );\n}\n\n#[test]\nfn module_scope_identifier_redefinition() {\n    // const\n    check(\n        r#\"\n            const foo: bool = true;\n            const foo: bool = true;\n        \"#,\n        r###\"error: redefinition of `foo`\n  ┌─ wgsl:2:19\n  │\n2 │             const foo: bool = true;\n  │                   ^^^ previous definition of `foo`\n3 │             const foo: bool = true;\n  │                   ^^^ redefinition of `foo`\n\n\"###,\n    );\n    // var\n    check(\n        r#\"\n            var foo: bool = true;\n            var foo: bool = true;\n        \"#,\n        r###\"error: redefinition of `foo`\n  ┌─ wgsl:2:17\n  │\n2 │             var foo: bool = true;\n  │                 ^^^ previous definition of `foo`\n3 │             var foo: bool = true;\n  │                 ^^^ redefinition of `foo`\n\n\"###,\n    );\n\n    // let and var\n    check(\n        r#\"\n            var foo: bool = true;\n            const foo: bool = true;\n        \"#,\n        r###\"error: redefinition of `foo`\n  ┌─ wgsl:2:17\n  │\n2 │             var foo: bool = true;\n  │                 ^^^ previous definition of `foo`\n3 │             const foo: bool = true;\n  │                   ^^^ redefinition of `foo`\n\n\"###,\n    );\n\n    // function\n    check(\n        r#\"fn foo() {}\n                fn bar() {}\n                fn foo() {}\"#,\n        r###\"error: redefinition of `foo`\n  ┌─ wgsl:1:4\n  │\n1 │ fn foo() {}\n  │    ^^^ previous definition of `foo`\n2 │                 fn bar() {}\n3 │                 fn foo() {}\n  │                    ^^^ redefinition of `foo`\n\n\"###,\n    );\n\n    // let and function\n    check(\n        r#\"\n            const foo: bool = true;\n            fn foo() {}\n        \"#,\n        r###\"error: redefinition of `foo`\n  ┌─ wgsl:2:19\n  │\n2 │             const foo: bool = true;\n  │                   ^^^ previous definition of `foo`\n3 │             fn foo() {}\n  │                ^^^ redefinition of `foo`\n\n\"###,\n    );\n}\n\n#[test]\nfn matrix_with_bad_type() {\n    check(\n        r#\"\n            fn main() {\n                let m = mat2x2<i32>();\n            }\n        \"#,\n        r#\"error: matrix scalar type must be floating-point, but found `i32`\n  ┌─ wgsl:3:32\n  │\n3 │                 let m = mat2x2<i32>();\n  │                                ^^^ must be floating-point (e.g. `f32`)\n\n\"#,\n    );\n\n    check(\n        r#\"\n            fn main() {\n                var m: mat3x3<i32>;\n            }\n        \"#,\n        r#\"error: matrix scalar type must be floating-point, but found `i32`\n  ┌─ wgsl:3:31\n  │\n3 │                 var m: mat3x3<i32>;\n  │                               ^^^ must be floating-point (e.g. `f32`)\n\n\"#,\n    );\n}\n\n#[test]\nfn matrix_constructor_inferred() {\n    check(\n        r#\"\n            const m: mat2x2<f64> = mat2x2<f32>(vec2(0), vec2(1));\n        \"#,\n        r#\"error: the type of `m` is expected to be `mat2x2<f64>`, but got `mat2x2<f32>`\n  ┌─ wgsl:2:19\n  │\n2 │             const m: mat2x2<f64> = mat2x2<f32>(vec2(0), vec2(1));\n  │                   ^ definition of `m`\n\n\"#,\n    );\n}\n\n/// Check the result of validating a WGSL program against a pattern.\n///\n/// Unless you are generating code programmatically, the\n/// `check_validation` macro will probably be more convenient to\n/// use.\nmacro_rules! check_one_validation {\n    ( $source:expr, $pattern:pat $( if $guard:expr )? ) => {\n        let source = $source;\n        let error = validation_error($source, naga::valid::Capabilities::default());\n        #[allow(clippy::redundant_pattern_matching)]\n        if ! matches!(&error, $pattern $( if $guard )? ) {\n            eprintln!(\"validation error does not match pattern:\\n\\\n                       source code: {}\\n\\\n                       \\n\\\n                       actual result:\\n\\\n                       {:#?}\\n\\\n                       \\n\\\n                       expected match for pattern:\\n\\\n                       {}\",\n                      &source,\n                      error,\n                      stringify!($pattern));\n            $( eprintln!(\"if {}\", stringify!($guard)); )?\n            panic!(\"validation error does not match pattern\");\n        }\n    };\n    ( $source:expr, $pattern:pat $( if $guard:expr )?, $capabilities:expr ) => {\n        let source = $source;\n        let error = validation_error($source, $capabilities);\n        #[allow(clippy::redundant_pattern_matching)]\n        if ! matches!(&error, $pattern $( if $guard )? ) {\n            eprintln!(\"validation error does not match pattern:\\n\\\n                       source code: {}\\n\\\n                       \\n\\\n                       actual result:\\n\\\n                       {:#?}\\n\\\n                       \\n\\\n                       expected match for pattern:\\n\\\n                       {}\",\n                      &source,\n                      error,\n                      stringify!($pattern));\n            $( eprintln!(\"if {}\", stringify!($guard)); )?\n            panic!(\"validation error does not match pattern\");\n        }\n    }\n}\n\n/// Test validation of required extensions and capabilities.\n///\n/// This tests that the shader is rejected either if the required extension is\n/// not declared in an `enable` directive, or if the validator is configured\n/// without the required capability.\n///\n/// For the first case, we use the supplied test case source verbatim (which\n/// should not include the `enable` directive), and check for a parse error\n/// matching the expected error message text. For the second case, we add the\n/// `enable` directive to the supplied test case, and check for a validation\n/// error matching the expected pattern.\n///\n/// The WGSL frontend is not the only way of producing Naga IR, and the\n/// validator must reject an invalid module however produced. So it is important\n/// that the validator check for missing capabilities. Checking missing\n/// extensions in the frontend as well can produce better error messages or\n/// simplify implementation of the frontend by eliminating some cases of invalid\n/// programs earlier.\n///\n/// Multiple capabilities can be specified in the macro argument in the case\n/// where any one of them grants access to a feature (e.g. `SUBGROUP` and\n/// `SUBGROUP_BARRIER` for `subgroupBarrier`). When passing multiple capabilities,\n/// all of the passed capabilities must be covered by the same enable-extension.\n///\n/// NOTE: The only reason we don't use a function for this is because we need to syntactically\n/// re-use `$val_err_pat`.\n///\n/// The optional $other_caps argument at the end specifies capabilities that\n/// allow, the shader or would change the error message if enabled, but do not\n/// get enabled by the specified enable extension. This is only currently the\n/// case for `acceleration_structures` which are enabled by both ray queries\n/// and ray tracing pipelines.\nmacro_rules! check_extension_validation {\n    ( $caps:expr, $source:expr, $parse_err:expr, $val_err_pat:pat $(, $other_caps:expr)? ) => {\n        #[allow(unused_mut, unused_assignments)]\n        let mut other_caps = naga::valid::Capabilities::empty();\n        $(other_caps = $other_caps;)?\n        let caps = $caps;\n        let source = $source;\n        let mut ext = None;\n        for cap in caps.iter() {\n            match cap.extension() {\n                Some(this_ext) if ext.is_none() => ext = Some(this_ext),\n                Some(this_ext) if ext.is_some_and(|ext| ext != this_ext) => {\n                    panic!(\n                        concat!(\n                            \"the capabilities {:?} in `check_extension_validation` \",\n                            \"are not all covered by the same extension \",\n                            \"(found both {:?} and {:?})\",\n                        ),\n                        caps, ext, this_ext,\n                    );\n                }\n                _ => {}\n            }\n        }\n        let Some(ext) = ext else {\n            panic!(\n                concat!(\n                    \"None of the capabilities {:?} in `check_extension_validation` \",\n                    \"are associated with an extension. \",\n                    \"Use `check_validation!` to check validator behavior \",\n                    \"when there isn't a corresponding parse error.\",\n                ),\n                caps\n            );\n        };\n        let directive = format!(\n            \"enable {};\",\n            naga::front::wgsl::EnableExtension::Implemented(ext).to_ident()\n        );\n        assert!(\n            !source.contains(&directive),\n            \"test case for `check_extension_validation!` should not contain the enable directive\",\n        );\n\n        // First check, for the expected WGSL parse error when extension is not enabled\n        check(&source, $parse_err);\n        let source_with_enable = format!(\"{directive}\\n{source}\");\n        let module = match naga::front::wgsl::parse_str(&source_with_enable) {\n            Ok(module) => module,\n            Err(err) => {\n                eprintln!(\"WGSL parse failed:\");\n                panic!(\"{}\", err.emit_to_string(source));\n            }\n        };\n\n        // Second check, for the expected validation error when the capability is not present\n        // Don't check with explicitly allowed caps, as certain things (currently just\n        // `acceleration_structure`s) can be enabled by multiple extensions\n        let error = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), !(caps | other_caps))\n            .validate(&module)\n            .map_err(|e| e.into_inner()); // TODO(https://github.com/gfx-rs/wgpu/issues/8153): Add tests for spans\n        #[allow(clippy::redundant_pattern_matching)]\n        if !matches!(&error, $val_err_pat) {\n            eprintln!(\n                concat!(\n                    \"validation error without {:?} does not match pattern:\\n\",\n                    \"source code: {}\\n\",\n                    \"\\n\",\n                    \"actual result:\\n\",\n                    \"{:#?}\\n\",\n                    \"\\n\",\n                    \"expected match for pattern:\\n\",\n                    \"{}\",\n                ),\n                caps,\n                &source,\n                error,\n                stringify!($val_err_pat)\n            );\n            panic!(\"validation error does not match pattern\");\n        }\n\n        // Also check that when multiple capabililiites can enable a feature,\n        // any one of them is sufficient.\n        if !caps.bits().is_power_of_two() {\n            for cap in caps.iter() {\n                let res = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), cap)\n                    .validate(&module);\n\n                match res {\n                    Ok(_) => {}\n                    Err(err) => panic!(\"Module did not validate with only {cap:?}: {err:?}\"),\n                }\n            }\n        }\n    };\n}\n\nmacro_rules! check_validation {\n    // We want to support an optional guard expression after the pattern, so\n    // that we can check values we can't match against, like strings.\n    // Unfortunately, we can't simply include `$( if $guard:expr )?` in the\n    // pattern, because Rust treats `?` as a repetition operator, and its count\n    // (0 or 1) will not necessarily match `$source`.\n    ( $( $source:literal ),* : $pattern:pat ) => {\n        $(\n            check_one_validation!($source, $pattern);\n        )*\n    };\n    ( $( $source:literal ),* : $pattern:pat, $capabilities:expr ) => {\n        $(\n            check_one_validation!($source, $pattern, $capabilities);\n        )*\n    };\n    ( $( $source:literal ),* : $pattern:pat if $guard:expr ) => {\n        $(\n            check_one_validation!($source, $pattern if $guard);\n        )*\n    };\n    ( $( $source:literal ),* : $pattern:pat if $guard:expr, $capabilities:expr ) => {\n        $(\n            check_one_validation!($source, $pattern if $guard, $capabilities);\n        )*\n    }\n}\n\n#[track_caller]\nfn validation_error(\n    source: &str,\n    caps: naga::valid::Capabilities,\n) -> Result<naga::valid::ModuleInfo, naga::valid::ValidationError> {\n    let module = match naga::front::wgsl::parse_str(source) {\n        Ok(module) => module,\n        Err(err) => {\n            eprintln!(\"WGSL parse failed:\");\n            panic!(\"{}\", err.emit_to_string(source));\n        }\n    };\n    naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps)\n        .validate(&module)\n        .map_err(|e| e.into_inner()) // TODO(https://github.com/gfx-rs/wgpu/issues/8153): Add tests for spans\n}\n\n/// Check that a shader validates successfully.\n///\n/// In a few tests it is useful to check conditions where a validation error\n/// should be absent alongside conditions where it should be present. This\n/// wrapper is less confusing than `validation_error().unwrap()`.\n#[track_caller]\nfn no_validation_error(source: &str, caps: naga::valid::Capabilities) {\n    validation_error(source, caps).unwrap();\n}\n\n#[test]\nfn int64_capability() {\n    check_validation! {\n        \"var input: u64;\",\n        \"var input: i64;\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability {flag: \"SHADER_INT64\",..}),\n            ..\n        })\n    }\n}\n\n#[test]\nfn per_vertex_capability() {\n    check_validation! {\n            r#\"\n            @fragment\n            fn fs_main(@location(0) @interpolate(per_vertex) v: array<f32, 3>) -> @location(0) vec4<f32> {\n                return vec4(v[0], v[1], v[2], 1.0);\n            }\n        \"#:\n            Err(\n        naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: valid::EntryPointError::Argument(\n                0,\n                valid::VaryingError::UnsupportedCapability(\n                    Capabilities::PER_VERTEX,\n\n                ),\n            ),\n            ..\n        },\n    )\n        }\n}\n\n#[test]\nfn multiple_enables_valid() {\n    check_success(\n        r#\"\n            enable f16;\n            enable f16;\n            const a: f16 = 1.0h;\n        \"#,\n    );\n}\n\n#[test]\nfn float16_capability_and_enable() {\n    // A zero value expression\n    check_extension_validation! {\n        Capabilities::SHADER_FLOAT16,\n        r#\"fn foo() {\n            let a = f16();\n        }\n        \"#,\n        r#\"error: the `f16` enable extension is not enabled\n  ┌─ wgsl:2:21\n  │\n2 │             let a = f16();\n  │                     ^^^ the `f16` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: \"FLOAT16\", .. }),\n            ..\n        })\n    }\n\n    // Literals\n    check_extension_validation! {\n        Capabilities::SHADER_FLOAT16,\n        r#\"fn foo() {\n            let a = f16(1);\n        }\n        \"#,\n        r#\"error: the `f16` enable extension is not enabled\n  ┌─ wgsl:2:21\n  │\n2 │             let a = f16(1);\n  │                     ^^^ the `f16` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::Expression {\n                source: naga::valid::ExpressionError::Literal(\n                    naga::valid::LiteralError::Width(\n                        naga::valid::WidthError::MissingCapability { flag: \"FLOAT16\", .. }\n                    )\n                ),\n                ..\n            },\n            ..\n        })\n    }\n    check_extension_validation! {\n        Capabilities::SHADER_FLOAT16,\n        r#\"\n            const a = 1.0h;\n        \"#,\n        r#\"error: the `f16` enable extension is not enabled\n  ┌─ wgsl:2:23\n  │\n2 │             const a = 1.0h;\n  │                       ^^^^ the `f16` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: \"FLOAT16\", .. }),\n            ..\n        })\n    }\n\n    // `f16`-typed declarations\n    check_extension_validation! {\n        Capabilities::SHADER_FLOAT16,\n        r#\"\n            const a: f16 = 1.0;\n        \"#,\n        r#\"error: the `f16` enable extension is not enabled\n  ┌─ wgsl:2:22\n  │\n2 │             const a: f16 = 1.0;\n  │                      ^^^ the `f16` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: \"FLOAT16\", .. }),\n            ..\n        })\n    }\n    check_extension_validation! {\n        Capabilities::SHADER_FLOAT16,\n        \"var input: f16;\",\n        r#\"error: the `f16` enable extension is not enabled\n  ┌─ wgsl:1:12\n  │\n1 │ var input: f16;\n  │            ^^^ the `f16` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: \"FLOAT16\", .. }),\n            ..\n        })\n    }\n\n    // Functions that operate on `f16`-precision values stored in `f32`s.\n    check_validation! {\n        \"fn foo() -> f32 { return quantizeToF16(1.0f); }\",\n        \"fn foo() -> u32 { return pack2x16float(vec2(1.0f, 2.0f)); }\",\n        \"fn foo() -> vec2<f32> { return unpack2x16float(0x7c007c00); }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::Expression {\n                source: naga::valid::ExpressionError::MissingCapabilities(Capabilities::SHADER_FLOAT16_IN_FLOAT32),\n                ..\n            },\n            ..\n        })\n    }\n}\n\n#[test]\nfn float16_in_immediate() {\n    check_validation! {\n        \"enable f16; var<immediate> input: f16;\",\n        \"enable f16; var<immediate> input: vec2<f16>;\",\n        \"enable f16; var<immediate> input: mat4x4<f16>;\",\n        \"enable f16; struct S { a: f16 }; var<immediate> input: S;\",\n        \"enable f16; struct S1 { a: f16 }; struct S2 { a : S1 } var<immediate> input: S2;\":\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::InvalidImmediateType(\n                naga::valid::ImmediateError::InvalidScalar(\n                    naga::Scalar::F16\n                )\n            ),\n            ..\n        }),\n        naga::valid::Capabilities::SHADER_FLOAT16 | naga::valid::Capabilities::IMMEDIATES\n    }\n}\n\n#[test]\nfn float16_in_atomic() {\n    check_validation! {\n        \"enable f16; var<storage> a: atomic<f16>;\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::InvalidAtomicWidth(\n                naga::ScalarKind::Float,\n                2\n            ),\n            ..\n        }),\n        naga::valid::Capabilities::SHADER_FLOAT16\n    }\n}\n\n#[test]\nfn invalid_arrays() {\n    check_validation! {\n        \"alias Bad = array<array<f32>, 4>;\",\n        \"alias Bad = array<sampler, 4>;\",\n        \"alias Bad = array<texture_2d<f32>, 4>;\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::InvalidArrayBaseType(_),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"var<uniform> input: array<u64, 2>;\",\n        \"var<uniform> input: array<vec2<u32>, 2>;\":\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::Alignment(naga::AddressSpace::Uniform,_,_),\n            ..\n        }),\n        naga::valid::Capabilities::SHADER_INT64\n    }\n\n    check_validation! {\n        r#\"\n            fn main() -> f32 {\n                let a = array<f32, 3>(0., 1., 2.);\n                return a[-1];\n            }\n        \"#:\n        Err(\n            naga::valid::ValidationError::Function {\n                name,\n                source: naga::valid::FunctionError::Expression {\n                    source: naga::valid::ExpressionError::NegativeIndex(_),\n                    ..\n                },\n                ..\n            }\n        )\n            if name == \"main\"\n    }\n\n    check(\n        \"alias Bad = array<f32, true>;\",\n        r###\"error: must be a const-expression that resolves to a concrete integer scalar (`u32` or `i32`)\n  ┌─ wgsl:1:24\n  │\n1 │ alias Bad = array<f32, true>;\n  │                        ^^^^ must resolve to `u32` or `i32`\n\n\"###,\n    );\n\n    check(\n        r#\"\n            const length: f32 = 2.718;\n            alias Bad = array<f32, length>;\n        \"#,\n        r###\"error: must be a const-expression that resolves to a concrete integer scalar (`u32` or `i32`)\n  ┌─ wgsl:3:36\n  │\n3 │             alias Bad = array<f32, length>;\n  │                                    ^^^^^^ must resolve to `u32` or `i32`\n\n\"###,\n    );\n\n    check(\n        \"alias Bad = array<f32, 0>;\",\n        r###\"error: array element count must be positive (> 0)\n  ┌─ wgsl:1:24\n  │\n1 │ alias Bad = array<f32, 0>;\n  │                        ^ must be positive\n\n\"###,\n    );\n\n    check(\n        \"alias Bad = array<f32, -1>;\",\n        r###\"error: array element count must be positive (> 0)\n  ┌─ wgsl:1:24\n  │\n1 │ alias Bad = array<f32, -1>;\n  │                        ^^ must be positive\n\n\"###,\n    );\n}\n\n#[test]\nfn discard_in_wrong_stage() {\n    check_validation! {\n        \"@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    if global_id.x == 3u {\n        discard;\n    }\n}\":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Compute,\n            source: naga::valid::EntryPointError::ForbiddenStageOperations,\n            ..\n        })\n    }\n\n    check_validation! {\n        \"@vertex\nfn main() -> @builtin(position) vec4<f32> {\n    if true {\n        discard;\n    }\n    return vec4<f32>();\n}\":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::ForbiddenStageOperations,\n            ..\n        })\n    }\n}\n\n#[test]\nfn invalid_structs() {\n    check_validation! {\n        \"struct Bad { data: sampler }\",\n        \"struct Bad { data: texture_2d<f32> }\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::InvalidData(_),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"struct Bad { data: array<f32>, other: f32, }\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::InvalidDynamicArray(_, _),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"struct Empty {}\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::EmptyStruct,\n            ..\n        })\n    }\n}\n\n#[test]\nfn struct_type_mismatch_in_assignment() {\n    check_validation!(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n        fn main() {\n            var x: Bar = Bar(1);\n            x = Foo(1);\n        }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            handle: _,\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidStoreTypes { .. },\n        })\n        // The validation error is reported at the call, i.e., in `main`\n        if function_name == \"main\"\n    );\n}\n\n#[test]\nfn struct_type_mismatch_in_let_decl() {\n    check(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n        fn main() {\n            let x: Bar = Foo(1);\n        }\n        \",\n        \"error: the type of `x` is expected to be `Bar`, but got `Foo`\n  ┌─ wgsl:5:17\n  │\n5 │             let x: Bar = Foo(1);\n  │                 ^ definition of `x`\n\n\",\n    );\n}\n\n#[test]\nfn struct_type_mismatch_in_return_value() {\n    check_validation!(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n        fn bar() -> Bar {\n            return Foo(1);\n        }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            handle: _,\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidReturnType { .. }\n        }) if function_name == \"bar\"\n    );\n}\n\n#[test]\nfn struct_type_mismatch_in_argument() {\n    check_validation!(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n        fn bar(a: Bar) {}\n        fn main() {\n            bar(Foo(1));\n        }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidCall {\n                function: _,\n                error: naga::valid::CallError::ArgumentType { index, .. },\n            },\n            ..\n        })\n        // The validation error is reported at the call, i.e., in `main`\n        if function_name == \"main\" && *index == 0\n    );\n}\n\n#[test]\nfn struct_type_mismatch_in_global_var() {\n    check(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n\n        var<uniform> foo: Foo = Bar(1);\n        \",\n        \"error: the type of `foo` is expected to be `Foo`, but got `Bar`\n  ┌─ wgsl:5:22\n  │\n5 │         var<uniform> foo: Foo = Bar(1);\n  │                      ^^^ definition of `foo`\n\n\",\n    );\n}\n\n#[test]\nfn struct_type_mismatch_in_global_const() {\n    check(\n        \"\n        struct Foo { a: u32 };\n        struct Bar { a: u32 };\n\n        const foo: Foo = Bar(1);\n        \",\n        \"error: the type of `foo` is expected to be `Foo`, but got `Bar`\n  ┌─ wgsl:5:15\n  │\n5 │         const foo: Foo = Bar(1);\n  │               ^^^ definition of `foo`\n\n\",\n    );\n}\n\n#[test]\nfn invalid_functions() {\n    check_validation! {\n        \"fn unacceptable_unsized(arg: array<f32>) { }\",\n        \"\n        struct Unsized { data: array<f32> }\n        fn unacceptable_unsized(arg: Unsized) { }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidArgumentType {\n                index: 0,\n                name: argument_name,\n            },\n            ..\n        })\n        if function_name == \"unacceptable_unsized\" && argument_name == \"arg\"\n    }\n\n    // Pointer's address space cannot hold unsized data.\n    check_validation! {\n        \"fn unacceptable_unsized(arg: ptr<workgroup, array<f32>>) { }\",\n        \"\n        struct Unsized { data: array<f32> }\n        fn unacceptable_unsized(arg: ptr<workgroup, Unsized>) { }\n        \":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::InvalidPointerToUnsized {\n                base: _,\n                space: naga::AddressSpace::WorkGroup,\n            },\n            ..\n        })\n    }\n\n    // Pointers of these address spaces cannot be passed as arguments.\n    check_validation! {\n        \"fn unacceptable_ptr_space(arg: ptr<storage, array<f32>>) { }\":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidArgumentPointerSpace {\n                index: 0,\n                name: argument_name,\n                space: naga::AddressSpace::Storage { .. },\n            },\n            ..\n        })\n        if function_name == \"unacceptable_ptr_space\" && argument_name == \"arg\"\n    }\n    check_validation! {\n        \"fn unacceptable_ptr_space(arg: ptr<uniform, f32>) { }\":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidArgumentPointerSpace {\n                index: 0,\n                name: argument_name,\n                space: naga::AddressSpace::Uniform,\n            },\n            ..\n        })\n        if function_name == \"unacceptable_ptr_space\" && argument_name == \"arg\"\n    }\n    check_validation! {\n        \"fn unacceptable_ptr_space(arg: ptr<workgroup, f32>) { }\":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::InvalidArgumentPointerSpace {\n                index: 0,\n                name: argument_name,\n                space: naga::AddressSpace::WorkGroup,\n            },\n            ..\n        })\n        if function_name == \"unacceptable_ptr_space\" && argument_name == \"arg\"\n    }\n\n    check_validation! {\n        \"\n        struct AFloat {\n          said_float: f32\n        };\n        @group(0) @binding(0)\n        var<storage> float: AFloat;\n\n        fn return_pointer() -> ptr<storage, f32> {\n           return &float.said_float;\n        }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::NonConstructibleReturnType,\n            ..\n        })\n        if function_name == \"return_pointer\"\n    }\n\n    check_validation! {\n        \"\n        @group(0) @binding(0)\n        var<storage> atom: atomic<u32>;\n\n        fn return_atomic() -> atomic<u32> {\n           return atom;\n        }\n        \":\n        Err(naga::valid::ValidationError::Function {\n            name: function_name,\n            source: naga::valid::FunctionError::NonConstructibleReturnType,\n            ..\n        })\n        if function_name == \"return_atomic\"\n    }\n}\n\n#[test]\nfn invalid_return_type() {\n    check_validation! {\n        \"fn invalid_return_type() -> i32 { return 0u; }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::InvalidReturnType { .. },\n            ..\n        })\n    };\n}\n\n#[test]\nfn pointer_type_equivalence() {\n    check_validation! {\n        r#\"\n            fn f(pv: ptr<function, vec2<f32>>) { }\n\n            fn g() {\n               var m: mat2x2<f32>;\n               let pv: ptr<function, vec2<f32>> = &m[0];\n\n               f(pv);\n            }\n        \"#:\n        Ok(_)\n    }\n}\n\n#[test]\nfn missing_bindings() {\n    check_validation! {\n        \"\n        @fragment\n        fn fragment(_input: vec4<f32>) -> @location(0) vec4<f32> {\n           return _input;\n        }\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: naga::valid::EntryPointError::Argument(\n                0,\n                naga::valid::VaryingError::MissingBinding,\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"\n        @fragment\n        fn fragment(@location(0) _input: vec4<f32>, more_input: f32) -> @location(0) vec4<f32> {\n           return _input + more_input;\n        }\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: naga::valid::EntryPointError::Argument(\n                1,\n                naga::valid::VaryingError::MissingBinding,\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"\n        @fragment\n        fn fragment(@location(0) _input: vec4<f32>) -> vec4<f32> {\n           return _input;\n        }\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: naga::valid::EntryPointError::Result(\n                naga::valid::VaryingError::MissingBinding,\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        \"\n        struct FragmentIn {\n          @location(0) pos: vec4<f32>,\n          uv: vec2<f32>\n        }\n\n        @fragment\n        fn fragment(_input: FragmentIn) -> @location(0) vec4<f32> {\n           return _input.pos;\n        }\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: naga::valid::EntryPointError::Argument(\n                0,\n                naga::valid::VaryingError::MemberMissingBinding(1),\n            ),\n            ..\n        })\n    }\n}\n\n#[test]\nfn missing_bindings2() {\n    check_validation! {\n        \"\n        @vertex\n        fn vertex() {}\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::MissingVertexOutputPosition,\n            ..\n        })\n    }\n\n    check_validation! {\n        \"\n        struct VertexOut {\n            @location(0) a: vec4<f32>,\n        }\n\n        @vertex\n        fn vertex() -> VertexOut {\n            return VertexOut(vec4<f32>());\n        }\n        \":\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::MissingVertexOutputPosition,\n            ..\n        })\n    }\n}\n\n#[test]\nfn invalid_blend_src() {\n    use naga::valid::{TypeError, ValidationError, VaryingError};\n\n    // Missing capability or enable directive\n    check_extension_validation! {\n        Capabilities::DUAL_SOURCE_BLENDING,\n        \"\n        struct FragmentOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n            @location(0) @blend_src(1) output1: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n        \",\n        r###\"error: the `dual_source_blending` enable extension is not enabled\n  ┌─ wgsl:3:27\n  │\n3 │             @location(0) @blend_src(0) output0: vec4<f32>,\n  │                           ^^^^^^^^^ the `dual_source_blending` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable dual_source_blending;` at the top of the shader, before any other items.\n\n\"###,\n        Err(\n            ValidationError::Type {\n                source: TypeError::MissingCapability(Capabilities::DUAL_SOURCE_BLENDING),\n                ..\n            },\n        )\n    }\n\n    // Using blend_src on an input.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        @fragment\n        fn main(@location(0) @blend_src(0) input: f32) -> vec4f { return vec4f(0.0); }\n        \":\n        Err(\n            ValidationError::EntryPoint {\n                stage: naga::ShaderStage::Fragment,\n                source: naga::valid::EntryPointError::Argument(\n                    0,\n                    VaryingError::BlendSrcNotOnStructMember,\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Using blend_src as output on something that isn't a fragment shader.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct VertexOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n            @location(0) @blend_src(1) output1: vec4<f32>,\n        }\n        @vertex\n        fn main() -> VertexOutput { return VertexOutput(vec4(0.0), vec4(1.0)); }\n        \":\n        Err(\n            ValidationError::EntryPoint {\n                stage: naga::ShaderStage::Vertex,\n                source: naga::valid::EntryPointError::Result(\n                    VaryingError::InvalidAttributeInStage(\"blend_src\", naga::ShaderStage::Vertex),\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Invalid blend_src index.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n            @location(0) @blend_src(2) output1: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::InvalidBlendSrcIndex {\n                        location: 0,\n                        blend_src: 2,\n                    }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Using a location other than 0 on blend_src\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n            @location(1) @blend_src(1) output1: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::InvalidBlendSrcIndex {\n                        location: 1,\n                        blend_src: 1,\n                    }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Using same blend_src several times.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(0) @blend_src(1) output0: vec4<f32>,\n            @location(0) @blend_src(1) output1: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::BindingCollisionBlendSrc { blend_src: 1 }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Two attributes, only one has blend_src\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n            @location(1) output1: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::IncompleteBlendSrcUsage {\n                        present_blend_src: 0,\n                    }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Single attribute using blend_src.\n    check_validation! {\n        \"\n            enable dual_source_blending;\n            struct FragmentOutput {\n                @location(0) @blend_src(1) output0: vec4<f32>,\n            }\n            @fragment\n            fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0)); }\n            \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::IncompleteBlendSrcUsage{\n                        present_blend_src: 1,\n                    }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Mixed output types.\n    check_validation! {\n        \"\n            enable dual_source_blending;\n            struct FragmentOutput {\n                @location(0) @blend_src(0) output0: vec4<f32>,\n                @location(0) @blend_src(1) output1: f32,\n            }\n            @fragment\n            fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), 1.0); }\n            \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::BlendSrcOutputTypeMismatch { .. }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // Multiple entrypoints (regression test for https://github.com/gfx-rs/wgpu/issues/9111)\n    check_validation! {\n        \"\n            enable dual_source_blending;\n            struct FragmentOutput {\n                @location(0) @blend_src(0) output0: vec4<f32>,\n                @location(0) @blend_src(1) output1: vec4<f32>,\n            }\n            @fragment\n            fn fs1() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n            @fragment\n            fn fs2() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); }\n            \":\n        Ok(_),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // @blend_src struct with no entry point should still be validated at the type level.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(0) @blend_src(0) output0: vec4<f32>,\n        }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::IncompleteBlendSrcUsage {\n                        present_blend_src: 0,\n                    }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n\n    // First member has @location(1) without @blend_src, followed by two @blend_src members.\n    check_validation! {\n        \"\n        enable dual_source_blending;\n        struct FragmentOutput {\n            @location(1) output0: vec4<f32>,\n            @location(0) @blend_src(0) output1: vec4<f32>,\n            @location(0) @blend_src(1) output2: vec4<f32>,\n        }\n        @fragment\n        fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0), vec4(2.0)); }\n        \":\n        Err(\n            ValidationError::Type {\n                source: TypeError::InvalidBlendSrc(\n                    VaryingError::InvalidBlendSrcWithOtherBindings { location: 1 }\n                ),\n                ..\n            },\n        ),\n        Capabilities::DUAL_SOURCE_BLENDING\n    }\n}\n\n#[test]\nfn invalid_access() {\n    check_validation! {\n        r#\"\n            fn main() -> f32 {\n                let a = array<f32, 3>(0., 1., 2.);\n                return a[3];\n            }\n        \"#:\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::Expression {\n                source: naga::valid::ExpressionError::IndexOutOfBounds(_, _),\n                ..\n            },\n            ..\n        })\n    }\n}\n\n#[test]\nfn valid_access() {\n    check_validation! {\n        \"\n        fn vector_by_value(v: vec4<i32>, i: i32) -> i32 {\n            return v[i];\n        }\n        \",\n        \"\n        fn matrix_dynamic(m: mat4x4<f32>, i: i32, j: i32) -> f32 {\n            var temp: mat4x4<f32> = m;\n            // Dynamically indexing the column vector applies\n            // `Access` to a `ValuePointer`.\n            return temp[i][j];\n        }\n        \",\n        \"\n        fn main() {\n            var v: vec4<f32> = vec4<f32>(1.0, 1.0, 1.0, 1.0);\n            let pv = &v;\n            let a = (*pv)[3];\n        }\n        \":\n        Ok(_)\n    }\n\n    check_validation! {\n        \"\n        fn matrix_by_value(m: mat4x4<f32>, i: i32) -> vec4<f32> {\n            return m[i];\n        }\n        \":\n        Ok(_)\n    }\n}\n\n#[test]\nfn invalid_local_vars() {\n    check_validation! {\n        \"\n        struct Unsized { data: array<f32> }\n        fn local_ptr_dynamic_array(okay: ptr<storage, Unsized>) {\n            var not_okay: ptr<storage, array<f32>> = &(*okay).data;\n        }\n        \":\n        Err(valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        })\n        if local_var_name == \"not_okay\"\n    }\n\n    check_validation! {\n        \"\n        fn f() {\n            var x: atomic<u32>;\n        }\n        \":\n        Err(valid::ValidationError::Function {\n            source: valid::FunctionError::LocalVariable {\n                name: local_var_name,\n                source: valid::LocalVariableError::InvalidType(_),\n                ..\n            },\n            ..\n        })\n        if local_var_name == \"x\"\n    }\n\n    // Rejected in statement lowering\n    // There is a similar validator test in `validation.rs`.\n    check(\n        \"\n        override len: u32;\n        var<workgroup> arr: array<u32, len>;\n        fn f() {\n            let x: array<u32, len> = arr;\n        }\n        \",\n        r#\"error: type `x` is not constructible\n  ┌─ wgsl:5:17\n  │\n5 │             let x: array<u32, len> = arr;\n  │                 ^ type is not constructible\n\n\"#,\n    );\n}\n\n#[test]\nfn invalid_zero_value_constructors() {\n    // There are similar validator tests in `validation.rs`.\n\n    // Rejected in constructor lowering\n    check(\n        \"\n        fn f() {\n            let x = array<u32>();\n        }\n        \",\n        r#\"error: type `array<u32>` is not constructible\n  ┌─ wgsl:3:21\n  │\n3 │             let x = array<u32>();\n  │                     ^^^^^^^^^^ type is not constructible\n\n\"#,\n    );\n\n    // Rejected in constructor lowering\n    check(\n        \"\n        override len: u32;\n        fn f() {\n            let x = array<u32, len>();\n        }\n        \",\n        r#\"error: type `array<u32, len>` is not constructible\n  ┌─ wgsl:4:21\n  │\n4 │             let x = array<u32, len>();\n  │                     ^^^^^^^^^^^^^^^ type is not constructible\n\n\"#,\n    );\n\n    // Rejected in constructor lowering\n    check(\n        \"\n        fn f() {\n            let x = array<u32>(0, 1, 2);\n        }\n        \",\n        r#\"error: type `array<u32>` is not constructible\n  ┌─ wgsl:3:21\n  │\n3 │             let x = array<u32>(0, 1, 2);\n  │                     ^^^^^^^^^^ type is not constructible\n\n\"#,\n    );\n\n    // Rejected in constructor lowering\n    check(\n        \"\n        struct Unsized { data: array<f32> }\n        fn main() {\n            var not_okay: Unsized = Unsized();\n        }\n        \",\n        r#\"error: type `Unsized` is not constructible\n  ┌─ wgsl:4:37\n  │\n4 │             var not_okay: Unsized = Unsized();\n  │                                     ^^^^^^^ type is not constructible\n\n\"#,\n    );\n}\n\n#[test]\nfn invalid_runtime_sized_arrays() {\n    // You can't have structs whose last member is an unsized struct. An unsized\n    // array may only appear as the last member of a struct used directly as a\n    // variable's store type.\n    check_validation! {\n        \"\n        struct Unsized {\n            arr: array<f32>\n        }\n\n        struct Outer {\n            legit: i32,\n            _unsized: Unsized\n        }\n\n        @group(0) @binding(0) var<storage> outer: Outer;\n\n        fn fetch(i: i32) -> f32 {\n           return outer._unsized.arr[i];\n        }\n        \":\n        Err(naga::valid::ValidationError::Type {\n            name: struct_name,\n            source: naga::valid::TypeError::InvalidDynamicArray(member_name, _),\n            ..\n        })\n        if struct_name == \"Outer\" && member_name == \"_unsized\"\n    }\n}\n\n#[test]\nfn select() {\n    let snapshots = [\n        (\n            \"\n        fn select_pointers(which: bool) -> i32 {\n            var x: i32 = 1;\n            var y: i32 = 2;\n            let p = select(&x, &y, which);\n            return *p;\n        }\n        \",\n            \"\\\nerror: unexpected argument type for `select` call\n  ┌─ wgsl:5:28\n  │\n5 │             let p = select(&x, &y, which);\n  │                            ^^ this value of type `ptr<function, i32>`\n  │\n  = note: expected a scalar or a `vecN` of scalars\n\n\",\n        ),\n        (\n            \"\n        fn select_arrays(which: bool) -> i32 {\n            var x: array<i32, 4>;\n            var y: array<i32, 4>;\n            let s = select(x, y, which);\n            return s[0];\n        }\n        \",\n            \"\\\nerror: unexpected argument type for `select` call\n  ┌─ wgsl:5:28\n  │\n5 │             let s = select(x, y, which);\n  │                            ^ this value of type `array<i32, 4>`\n  │\n  = note: expected a scalar or a `vecN` of scalars\n\n\",\n        ),\n        (\n            \"\n        struct S { member: i32 }\n        fn select_structs(which: bool) -> S {\n            var x: S = S(1);\n            var y: S = S(2);\n            let s = select(x, y, which);\n            return s;\n        }\n        \",\n            \"\\\nerror: unexpected argument type for `select` call\n  ┌─ wgsl:6:28\n  │\n6 │             let s = select(x, y, which);\n  │                            ^ this value of type `S`\n  │\n  = note: expected a scalar or a `vecN` of scalars\n\n\",\n        ),\n        (\n            \"\n        @compute @workgroup_size(1, 1)\n        fn main() {\n            // Bad: `9001` isn't a `bool`.\n            _ = select(1, 2, 9001);\n        }\n        \",\n            \"\\\nerror: Expected boolean expression for condition argument of `select`, got something else\n  ┌─ wgsl:5:17\n  │\n5 │             _ = select(1, 2, 9001);\n  │                 ^^^^^^ see msg\n\n\",\n        ),\n        (\n            \"\n        @compute @workgroup_size(1, 1)\n        fn main() {\n            // Bad: `bool` and abstract int args. don't match.\n            _ = select(true, 1, false);\n        }\n        \",\n            \"\\\nerror: type mismatch for reject and accept values in `select` call\n  ┌─ wgsl:5:24\n  │\n5 │             _ = select(true, 1, false);\n  │                        ^^^^  ^ accept value of type `{AbstractInt}`\n  │                        │\\x20\\x20\\x20\\x20\\x20\\x20\n  │                        reject value of type `bool`\n\n\",\n        ),\n    ];\n\n    for (input, snapshot) in snapshots {\n        check(input, snapshot);\n    }\n}\n\n#[test]\nfn missing_default_case() {\n    check_validation! {\n        \"\n        fn test_missing_default_case() {\n          switch(0) {\n            case 0: {}\n          }\n        }\n        \":\n        Err(\n            naga::valid::ValidationError::Function {\n                source: naga::valid::FunctionError::MissingDefaultCase,\n                ..\n            },\n        )\n    }\n}\n\n#[test]\nfn wrong_access_mode() {\n    // The assignments to `global.i` should be forbidden, because they are in\n    // variables whose access mode is `read`, not `read_write`.\n    check_validation! {\n        \"\n            struct Globals {\n                i: i32\n            }\n\n            @group(0) @binding(0)\n            var<storage> globals: Globals;\n\n            fn store(v: i32) {\n                globals.i = v;\n            }\n        \",\n        \"\n            struct Globals {\n                i: i32\n            }\n\n            @group(0) @binding(0)\n            var<uniform> globals: Globals;\n\n            fn store(v: i32) {\n                globals.i = v;\n            }\n        \":\n        Err(\n            naga::valid::ValidationError::Function {\n                name,\n                source: naga::valid::FunctionError::InvalidStorePointer(_),\n                ..\n            },\n        )\n            if name == \"store\"\n    }\n}\n\n#[test]\nfn io_shareable_types() {\n    for numeric in \"i32 u32 f32\".split_whitespace() {\n        let types = format!(\"{numeric} vec2<{numeric}> vec3<{numeric}> vec4<{numeric}>\");\n        for ty in types.split_whitespace() {\n            check_one_validation! {\n                &format!(\"@vertex\n                          fn f(@location(0) arg: {ty}) -> @builtin(position) vec4<f32>\n                          {{ return vec4<f32>(0.0); }}\"),\n                Ok(_module)\n            }\n        }\n    }\n\n    for ty in \"bool\n               vec2<bool> vec3<bool> vec4<bool>\n               array<f32,4>\n               mat2x2<f32>\n               ptr<function,f32>\"\n        .split_whitespace()\n    {\n        check_one_validation! {\n            &format!(\"@vertex\n                          fn f(@location(0) arg: {ty}) -> @builtin(position) vec4<f32>\n                          {{ return vec4<f32>(0.0); }}\"),\n            Err(\n                naga::valid::ValidationError::EntryPoint {\n                    stage: naga::ShaderStage::Vertex,\n                    name,\n                    source: naga::valid::EntryPointError::Argument(\n                        0,\n                        naga::valid::VaryingError::NotIOShareableType(\n                            _,\n                        ),\n                    ),\n                },\n            )\n            if name == \"f\"\n        }\n    }\n}\n\n#[test]\nfn host_shareable_types() {\n    // Host-shareable, constructible types.\n    let types = \"i32 u32 f32\n                 vec2<i32> vec3<u32> vec4<f32>\n                 mat4x4<f32>\n                 array<mat4x4<f32>,4>\n                 AStruct\";\n    for ty in types.split_whitespace() {\n        check_one_validation! {\n            &format!(\"struct AStruct {{ member: array<mat4x4<f32>, 8> }};\n                      @group(0) @binding(0) var<uniform> ubuf: {ty};\n                      @group(0) @binding(1) var<storage> sbuf: {ty};\"),\n            Ok(_module)\n        }\n    }\n\n    // Host-shareable but not constructible types.\n    let types = \"atomic<i32> atomic<u32>\n                 array<atomic<u32>,4>\n                 array<u32>\n                 AStruct\";\n    for ty in types.split_whitespace() {\n        check_one_validation! {\n            &format!(\"struct AStruct {{ member: array<atomic<u32>, 8> }};\n                      @group(0) @binding(1) var<storage> sbuf: {ty};\"),\n            Ok(_module)\n        }\n    }\n\n    // Types that are neither host-shareable nor constructible.\n    for ty in \"bool ptr<storage,i32>\".split_whitespace() {\n        check_one_validation! {\n            &format!(\"@group(0) @binding(0) var<storage> sbuf: {ty};\"),\n            Err(\n                naga::valid::ValidationError::GlobalVariable {\n                    name,\n                    handle: _,\n                    source: naga::valid::GlobalVariableError::MissingTypeFlags { .. },\n                },\n            )\n            if name == \"sbuf\"\n        }\n\n        check_one_validation! {\n            &format!(\"@group(0) @binding(0) var<uniform> ubuf: {ty};\"),\n            Err(naga::valid::ValidationError::GlobalVariable {\n                    name,\n                    handle: _,\n                    source: naga::valid::GlobalVariableError::MissingTypeFlags { .. },\n                },\n            )\n            if name == \"ubuf\"\n        }\n    }\n}\n\n#[test]\nfn var_init() {\n    check_validation! {\n        \"\n        var<workgroup> initialized: u32 = 0u;\n        \":\n        Err(\n            naga::valid::ValidationError::GlobalVariable {\n                source: naga::valid::GlobalVariableError::InitializerNotAllowed(naga::AddressSpace::WorkGroup),\n                ..\n            },\n        )\n    }\n}\n\n#[test]\nfn misplaced_break_if() {\n    check(\n        \"\n        fn test_misplaced_break_if() {\n            loop {\n                break if true;\n            }\n        }\n        \",\n        r###\"error: A break if is only allowed in a continuing block\n  ┌─ wgsl:4:17\n  │\n4 │                 break if true;\n  │                 ^^^^^^^^ not in a continuing block\n\n\"###,\n    );\n}\n\n#[test]\nfn break_if_bad_condition() {\n    check_validation! {\n        \"\n        fn test_break_if_bad_condition() {\n            loop {\n                continuing {\n                    break if 1;\n                }\n            }\n        }\n        \":\n        Err(\n            naga::valid::ValidationError::Function {\n                source: naga::valid::FunctionError::InvalidIfType(_),\n                ..\n            },\n        )\n    }\n}\n\n#[test]\nfn swizzle_assignment() {\n    check(\n        \"\n        fn f() {\n            var v = vec2(0);\n            v.xy = vec2(1);\n        }\n    \",\n        r###\"error: invalid left-hand side of assignment\n  ┌─ wgsl:4:13\n  │\n4 │             v.xy = vec2(1);\n  │             ^^^^ cannot assign to this expression\n  │\n  = note: WGSL does not support assignments to swizzles\n  = note: consider assigning each component individually\n\n\"###,\n    );\n}\n\n#[test]\nfn binary_statement() {\n    check(\n        \"\n        fn f() {\n            3 + 5;\n        }\n    \",\n        r###\"error: expected statement, found \"3\"\n  ┌─ wgsl:3:13\n  │\n3 │             3 + 5;\n  │             ^ expected statement\n\n\"###,\n    );\n}\n\n#[test]\nfn assign_to_expr() {\n    check(\n        \"\n        fn f() {\n            3 + 5 = 10;\n        }\n        \",\n        r###\"error: expected statement, found \"3\"\n  ┌─ wgsl:3:13\n  │\n3 │             3 + 5 = 10;\n  │             ^ expected statement\n\n\"###,\n    );\n}\n\n#[test]\nfn assign_to_let() {\n    check(\n        \"\n        fn f() {\n            let a = 10;\n\t        a = 20;\n        }\n        \",\n        r###\"error: invalid left-hand side of assignment\n  ┌─ wgsl:3:17\n  │\n3 │             let a = 10;\n  │                 ^ this is an immutable binding\n4 │             a = 20;\n  │             ^ cannot assign to this expression\n  │\n  = note: consider declaring `a` with `var` instead of `let`\n\n\"###,\n    );\n\n    check(\n        \"\n        fn f() {\n            let a = array(1, 2);\n\t\t\ta[0] = 1;\n        }\n        \",\n        r###\"error: invalid left-hand side of assignment\n  ┌─ wgsl:3:17\n  │\n3 │             let a = array(1, 2);\n  │                 ^ this is an immutable binding\n4 │             a[0] = 1;\n  │             ^^^^ cannot assign to this expression\n  │\n  = note: consider declaring `a` with `var` instead of `let`\n\n\"###,\n    );\n\n    check(\n        \"\n        struct S { a: i32 }\n\n        fn f() {\n            let a = S(10);\n\t        a.a = 20;\n        }\n        \",\n        r###\"error: invalid left-hand side of assignment\n  ┌─ wgsl:5:17\n  │\n5 │             let a = S(10);\n  │                 ^ this is an immutable binding\n6 │             a.a = 20;\n  │             ^^^ cannot assign to this expression\n  │\n  = note: consider declaring `a` with `var` instead of `let`\n\n\"###,\n    );\n}\n\n#[test]\nfn recursive_function() {\n    check(\n        \"\n        fn f() {\n            f();\n        }\n        \",\n        r###\"error: declaration of `f` is recursive\n  ┌─ wgsl:2:12\n  │\n2 │         fn f() {\n  │            ^\n3 │             f();\n  │             ^ uses itself here\n\n\"###,\n    );\n}\n\n#[test]\nfn cyclic_function() {\n    check(\n        \"\n        fn f() {\n            g();\n        }\n        fn g() {\n            f();\n        }\n        \",\n        r###\"error: declaration of `f` is cyclic\n  ┌─ wgsl:2:12\n  │\n2 │         fn f() {\n  │            ^\n3 │             g();\n  │             ^ uses `g`\n4 │         }\n5 │         fn g() {\n  │            ^\n6 │             f();\n  │             ^ ending the cycle\n\n\"###,\n    );\n}\n\n#[test]\nfn switch_signed_unsigned_mismatch() {\n    check(\n        \"\n        fn x(y: u32) {\n            switch y {\n                case 1i: {}\n            }\n        }\n        \",\n        r###\"error: invalid `switch` case selector value\n  ┌─ wgsl:4:22\n  │\n4 │                 case 1i: {}\n  │                      ^^ `switch` case selector must have the same type as the `switch` selector expression\n\n\"###,\n    );\n\n    check(\n        \"\n        fn x(y: i32) {\n            switch y {\n                case 1u: {}\n            }\n        }\n        \",\n        r###\"error: invalid `switch` case selector value\n  ┌─ wgsl:4:22\n  │\n4 │                 case 1u: {}\n  │                      ^^ `switch` case selector must have the same type as the `switch` selector expression\n\n\"###,\n    );\n}\n\n#[test]\nfn switch_invalid_type() {\n    check(\n        \"\n        fn x(y: f32) {\n            switch y {\n                case 1: {}\n            }\n        }\n        \",\n        r###\"error: invalid `switch` selector\n  ┌─ wgsl:3:20\n  │\n3 │             switch y {\n  │                    ^ `switch` selector must be a scalar integer\n\n\"###,\n    );\n\n    check(\n        \"\n        fn x(y: vec2<i32>) {\n            switch y {\n                case 1: {}\n            }\n        }\n        \",\n        r###\"error: invalid `switch` selector\n  ┌─ wgsl:3:20\n  │\n3 │             switch y {\n  │                    ^ `switch` selector must be a scalar integer\n\n\"###,\n    );\n\n    check(\n        \"\n        fn x() {\n            switch 0 {\n                case 1.0: {}\n            }\n        }\n    \",\n        r###\"error: invalid `switch` case selector value\n  ┌─ wgsl:4:22\n  │\n4 │                 case 1.0: {}\n  │                      ^^^ `switch` case selector must be a scalar integer const expression\n\n\"###,\n    );\n}\n\n#[test]\nfn switch_non_const_case() {\n    check(\n        \"\n        fn x(y: i32) {\n            switch 0 {\n                case y: {}\n            }\n        }\n    \",\n        r###\"error: invalid `switch` case selector value\n  ┌─ wgsl:4:22\n  │\n4 │                 case y: {}\n  │                      ^ `switch` case selector must be a scalar integer const expression\n\n\"###,\n    );\n}\n\n#[test]\nfn function_returns_void() {\n    check(\n        \"\n        fn x() {\n\t        let a = vec2<f32>(1.0, 2.0);\n        }\n\n        fn b() {\n\t        let a = x();\n        }\n    \",\n        r###\"error: function does not return any value\n  ┌─ wgsl:7:18\n  │\n7 │             let a = x();\n  │                     ^^^\n  │\n  = note: perhaps you meant to call the function in a separate statement?\n\n\"###,\n    )\n}\n\n#[test]\nfn function_must_use_unused() {\n    check(\n        r#\"\n@must_use\nfn use_me(a: i32) -> i32 {\n  return 10;\n}\n\nfn useless() -> i32 {\n  use_me(1);\n  return 0;\n}\n\"#,\n        r#\"error: unused return value from function annotated with @must_use\n  ┌─ wgsl:8:3\n  │\n8 │   use_me(1);\n  │   ^^^^^^\n  │\n  = note: function 'use_me' is declared with `@must_use` attribute\n  = note: use a phony assignment or declare a value using the function call as the initializer\n\n\"#,\n    );\n}\n\n#[test]\nfn function_must_use_returns_void() {\n    check(\n        r#\"\n@must_use\nfn use_me(a: i32) {\n  let x = a;\n}\n\"#,\n        r#\"error: function annotated with @must_use but does not return any value\n  ┌─ wgsl:2:2\n  │\n2 │ @must_use\n  │  ^^^^^^^^\n3 │ fn use_me(a: i32) {\n  │    ^^^^^^^^^^^^^^\n  │\n  = note: declare a return type or remove the attribute\n\n\"#,\n    );\n}\n\n#[test]\nfn function_must_use_repeated() {\n    check(\n        r#\"\n@must_use\n@must_use\nfn use_me(a: i32) -> i32 {\n  return 10;\n}\n\"#,\n        r#\"error: repeated attribute: `must_use`\n  ┌─ wgsl:3:2\n  │\n3 │ @must_use\n  │  ^^^^^^^^ repeated attribute\n\n\"#,\n    );\n}\n\n#[test]\nfn struct_member_must_use() {\n    check(\n        r#\"\nstruct S {\n  @must_use a: i32,\n}\n\"#,\n        r#\"error: unknown attribute: `must_use`\n  ┌─ wgsl:3:4\n  │\n3 │   @must_use a: i32,\n  │    ^^^^^^^^ unknown attribute\n\n\"#,\n    )\n}\n\n#[test]\nfn function_param_redefinition_as_param() {\n    check(\n        \"\n        fn x(a: f32, a: vec2<f32>) {}\n    \",\n        \"error: redefinition of `a`\n  ┌─ wgsl:2:14\n  │\n2 │         fn x(a: f32, a: vec2<f32>) {}\n  │              ^       ^ redefinition of `a`\n  │              │\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\n  │              previous definition of `a`\n\n\",\n    )\n}\n\n#[test]\nfn function_param_redefinition_as_local() {\n    check(\n        \"\n        fn x(a: f32) {\n\t\t\tlet a = 0.0;\n\t\t}\n    \",\n        r###\"error: redefinition of `a`\n  ┌─ wgsl:2:14\n  │\n2 │         fn x(a: f32) {\n  │              ^ previous definition of `a`\n3 │             let a = 0.0;\n  │                 ^ redefinition of `a`\n\n\"###,\n    )\n}\n\n#[test]\nfn struct_redefinition() {\n    check(\n        \"\n        struct Foo { a: u32 };\n        struct Foo { a: u32 };\n    \",\n        \"error: redefinition of `Foo`\n  ┌─ wgsl:2:16\n  │\n2 │         struct Foo { a: u32 };\n  │                ^^^ previous definition of `Foo`\n3 │         struct Foo { a: u32 };\n  │                ^^^ redefinition of `Foo`\n\n\",\n    );\n}\n\n#[test]\nfn struct_member_redefinition() {\n    check(\n        \"\n        struct A {\n            a: f32,\n            a: f32,\n        }\n    \",\n        r###\"error: redefinition of `a`\n  ┌─ wgsl:3:13\n  │\n3 │             a: f32,\n  │             ^ previous definition of `a`\n4 │             a: f32,\n  │             ^ redefinition of `a`\n\n\"###,\n    )\n}\n\n#[test]\nfn function_must_return_value() {\n    check_validation!(\n        \"fn func() -> i32 {\n        }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::InvalidReturnType { .. },\n            ..\n        })\n    );\n    check_validation!(\n        \"fn func(x: i32) -> i32 {\n            let y = x + 10;\n        }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::InvalidReturnType { .. },\n            ..\n        })\n    );\n}\n\n#[test]\nfn constructor_type_error_span() {\n    check(\n        \"\n        fn unfortunate() {\n            var a: array<i32, 1> = array<i32, 1>(1.0);\n        }\n    \",\n        \"error: automatic conversions cannot convert `{AbstractFloat}` to `i32`\n  ┌─ wgsl:3:36\n  │\n3 │             var a: array<i32, 1> = array<i32, 1>(1.0);\n  │                                    ^^^^^^^^^^^^^ ^^^ this expression has type {AbstractFloat}\n  │                                    │\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\n  │                                    a value of type i32 is required here\n\n\",\n    )\n}\n\n#[test]\nfn global_initialization_type_mismatch() {\n    check(\n        \"\n        var<private> a: vec2<f32> = vec2<i32>(1i, 2i);\n    \",\n        r###\"error: the type of `a` is expected to be `vec2<f32>`, but got `vec2<i32>`\n  ┌─ wgsl:2:22\n  │\n2 │         var<private> a: vec2<f32> = vec2<i32>(1i, 2i);\n  │                      ^ definition of `a`\n\n\"###,\n    )\n}\n\n#[test]\nfn binding_array_local() {\n    check_validation! {\n        \"fn f() { var x: binding_array<sampler, 4>; }\":\n        Err(_)\n    }\n}\n\n#[test]\nfn binding_array_private() {\n    check_validation! {\n        \"var<private> x: binding_array<sampler, 4>;\":\n        Err(_)\n    }\n}\n\n#[test]\nfn binding_array_non_struct() {\n    check_validation! {\n        \"var<storage> x: binding_array<i32, 4>;\":\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::BindingArrayBaseTypeNotStruct(_),\n            ..\n        })\n    }\n\n    check_validation! {\n        r#\"\n            enable wgpu_ray_query;\n            @group(0) @binding(0)\n            var<storage> ray_query_array: binding_array<ray_query, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::BindingArrayBaseTypeNotStruct(_),\n            ..\n        }),\n        Capabilities::RAY_QUERY\n    }\n}\n\n#[test]\nfn compaction_preserves_spans() {\n    let source = r#\"\n        fn f() {\n           var a: i32 = -(-(-(-42i)));\n           var x: array<i32,1>;\n           var y = x[1.0];\n        }\n        @compute @workgroup_size(1)\n        fn main() {\n            f();\n        }\n    \"#;\n    // The error span should be on `x[1.0]`, which is at characters 108..114.\n    let mut module = naga::front::wgsl::parse_str(source).expect(\"source ought to parse\");\n    naga::compact::compact(&mut module, KeepUnused::No);\n    let err = naga::valid::Validator::new(\n        naga::valid::ValidationFlags::all(),\n        naga::valid::Capabilities::default(),\n    )\n    .validate(&module)\n    .expect_err(\"source ought to fail validation\");\n\n    // Ideally this would all just be a `matches!` with a big pattern,\n    // but the `Span` API is full of opaque structs.\n    let mut spans = err.spans();\n\n    // The first span is the whole function.\n    let _ = spans.next().expect(\"error should have at least one span\");\n\n    // The second span is the invalid indexing expression.\n    let dest_span = spans\n        .next()\n        .expect(\"error should have at least two spans\")\n        .0;\n    if !matches!(\n        dest_span.to_range(),\n        Some(core::ops::Range {\n            start: 108,\n            end: 114\n        })\n    ) {\n        panic!(\"Error message has wrong span:\\n\\n{err:#?}\");\n    }\n}\n\n#[test]\nfn limit_braced_statement_nesting() {\n    let too_many_braces = \"fn f() {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\";\n\n    let expected_diagnostic = r###\"error: brace nesting limit reached\n  ┌─ wgsl:1:135\n  │\n1 │ fn f() {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\n  │                                                                                                                                       ^ limit reached at this brace\n  │\n  = note: nesting limit is currently set to 127\n\n\"###;\n\n    // In debug builds, we might actually overflow the stack before exercising this error case,\n    // depending on the platform and the `RUST_MIN_STACK` env. var. Use a thread with a custom\n    // stack size that works on all platforms.\n    std::thread::Builder::new()\n        .stack_size(1024 * 1024 * 2 /* MB */)\n        .spawn(|| check(too_many_braces, expected_diagnostic))\n        .unwrap()\n        .join()\n        .unwrap()\n}\n\n#[test]\nfn too_many_unclosed_loops() {\n    let too_many_braces = \"fn f() {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n       loop {\n           \";\n\n    let expected_diagnostic = r###\"error: brace nesting limit reached\n    ┌─ wgsl:128:13\n    │\n128 │        loop {\n    │             ^ limit reached at this brace\n    │\n    = note: nesting limit is currently set to 127\n\n\"###;\n\n    // In debug builds, we might actually overflow the stack before exercising this error case,\n    // depending on the platform and the `RUST_MIN_STACK` env. var. Use a thread with a custom\n    // stack size that works on all platforms.\n    std::thread::Builder::new()\n        .stack_size(1024 * 1024 * 2 /* MB */)\n        .spawn(|| check(too_many_braces, expected_diagnostic))\n        .unwrap()\n        .join()\n        .unwrap()\n}\n\n#[test]\nfn local_const_wrong_type() {\n    check(\n        \"\n        fn f() {\n            const c: i32 = 5u;\n        }\n        \",\n        r###\"error: the type of `c` is expected to be `i32`, but got `u32`\n  ┌─ wgsl:3:19\n  │\n3 │             const c: i32 = 5u;\n  │                   ^ definition of `c`\n\n\"###,\n    );\n}\n\n#[test]\nfn local_const_from_let() {\n    check(\n        \"\n        fn f() {\n            let a = 5;\n            const c = a;\n        }\n        \",\n        r###\"error: this operation is not supported in a const context\n  ┌─ wgsl:4:23\n  │\n4 │             const c = a;\n  │                       ^ operation not supported here\n\n\"###,\n    );\n}\n\n#[test]\nfn local_const_from_var() {\n    check(\n        \"\n        fn f() {\n            var a = 5;\n            const c = a;\n        }\n        \",\n        r###\"error: this operation is not supported in a const context\n  ┌─ wgsl:4:23\n  │\n4 │             const c = a;\n  │                       ^ operation not supported here\n\n\"###,\n    );\n}\n\n#[test]\nfn local_const_from_override() {\n    check(\n        \"\n        override o: i32;\n        fn f() {\n            const c = o;\n        }\n        \",\n        r###\"error: Unexpected override-expression\n  ┌─ wgsl:4:23\n  │\n4 │             const c = o;\n  │                       ^ see msg\n\n\"###,\n    );\n}\n\n#[test]\nfn local_const_from_global_var() {\n    check(\n        \"\n        var v: i32;\n        fn f() {\n            const c = v;\n        }\n        \",\n        r###\"error: Unexpected runtime-expression\n  ┌─ wgsl:4:23\n  │\n4 │             const c = v;\n  │                       ^ see msg\n\n\"###,\n    );\n}\n\n#[test]\nfn only_one_swizzle_type() {\n    check(\n        \"\n        const ok1 = vec2(0.0, 0.0).xy;\n        const ok2 = vec2(0.0, 0.0).rg;\n        const err = vec2(0.0, 0.0).xg;\n        \",\n        r###\"error: invalid field accessor `xg`\n  ┌─ wgsl:4:36\n  │\n4 │         const err = vec2(0.0, 0.0).xg;\n  │                                    ^^ invalid accessor\n\n\"###,\n    );\n}\n\n#[test]\nfn swizzle_oob() {\n    // 3-component swizzle from const vec2\n    check(\n        \"\n        @compute @workgroup_size(1)\n        fn main() {\n            const v = vec2<i32>();\n            let r : vec3<i32> = v.xyz;\n        }\n        \",\n        r###\"error: invalid field accessor `xyz`\n  ┌─ wgsl:5:35\n  │\n5 │             let r : vec3<i32> = v.xyz;\n  │                                   ^^^ invalid accessor\n\n\"###,\n    );\n\n    // 4-component swizzle from non-const vec3\n    check(\n        \"\n        @compute @workgroup_size(1)\n        fn main() {\n            var v = vec3<i32>();\n            let r : vec4<i32> = v.xyzw;\n        }\n        \",\n        r###\"error: invalid field accessor `xyzw`\n  ┌─ wgsl:5:35\n  │\n5 │             let r : vec4<i32> = v.xyzw;\n  │                                   ^^^^ invalid accessor\n\n\"###,\n    );\n}\n\n#[test]\nfn const_assert_must_be_const() {\n    check(\n        \"\n        fn foo() {\n            let a = 5;\n            const_assert a != 0;\n        }\n        \",\n        r###\"error: this operation is not supported in a const context\n  ┌─ wgsl:4:26\n  │\n4 │             const_assert a != 0;\n  │                          ^ operation not supported here\n\n\"###,\n    );\n}\n\n#[test]\nfn const_assert_must_be_bool() {\n    check(\n        \"\n            const_assert(5); // 5 is not bool\n        \",\n        r###\"error: must be a const-expression that resolves to a `bool`\n  ┌─ wgsl:2:26\n  │\n2 │             const_assert(5); // 5 is not bool\n  │                          ^ must resolve to `bool`\n\n\"###,\n    );\n}\n\n#[test]\nfn const_assert_failed() {\n    check(\n        \"\n            const_assert(false);\n        \",\n        r###\"error: `const_assert` failure\n  ┌─ wgsl:2:26\n  │\n2 │             const_assert(false);\n  │                          ^^^^^ evaluates to `false`\n\n\"###,\n    );\n}\n\n#[test]\nfn reject_utf8_bom() {\n    check(\n        \"\\u{FEFF}fn main() {}\",\n        r#\"error: expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file, found \"\\u{feff}\"\n  ┌─ wgsl:1:1\n  │\n1 │ ﻿fn main() {}\n  │  expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file\n\n\"#,\n    );\n}\n\n#[test]\nfn matrix_vector_pointers() {\n    check(\n        \"fn foo() {\n            var v: vec2<f32>;\n            let p = &v[0];\n        }\",\n        r#\"error: cannot take the address of a vector component\n  ┌─ wgsl:3:22\n  │\n3 │             let p = &v[0];\n  │                      ^^^^ invalid operand for address-of\n\n\"#,\n    );\n\n    check(\n        \"fn foo() {\n            var v: vec2<f32>;\n            let p = &v.x;\n        }\",\n        r#\"error: cannot take the address of a vector component\n  ┌─ wgsl:3:22\n  │\n3 │             let p = &v.x;\n  │                      ^^^ invalid operand for address-of\n\n\"#,\n    );\n\n    check(\n        \"fn foo() {\n            var m: mat2x2<f32>;\n            let p = &m[0][0];\n        }\",\n        r#\"error: cannot take the address of a vector component\n  ┌─ wgsl:3:22\n  │\n3 │             let p = &m[0][0];\n  │                      ^^^^^^^ invalid operand for address-of\n\n\"#,\n    );\n}\n\n#[test]\nfn vector_logical_ops() {\n    // Const context\n    check(\n        \"const and = vec2(true, false) && vec2(false, false);\",\n        r###\"error: Cannot apply the binary op to the arguments\n  ┌─ wgsl:1:13\n  │\n1 │ const and = vec2(true, false) && vec2(false, false);\n  │             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ see msg\n\n\"###,\n    );\n\n    check(\n        \"const or = vec2(true, false) || vec2(false, false);\",\n        r###\"error: Cannot apply the binary op to the arguments\n  ┌─ wgsl:1:12\n  │\n1 │ const or = vec2(true, false) || vec2(false, false);\n  │            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ see msg\n\n\"###,\n    );\n\n    // Runtime context\n    check(\n        \"fn foo(a: vec2<bool>, b: vec2<bool>) {\n            let y = a && b;\n        }\",\n        r#\"error: Incompatible operands: LogicalAnd(vec2<bool>, _)\n\n\"#,\n    );\n\n    check(\n        \"fn foo(a: vec2<bool>, b: vec2<bool>) {\n            let y = a || b;\n        }\",\n        r#\"error: Incompatible operands: LogicalOr(vec2<bool>, _)\n\n\"#,\n    );\n}\n\n#[test]\nfn issue7165() {\n    // Regression test for https://github.com/gfx-rs/wgpu/issues/7165\n    let shader = \"\n        struct Struct { a: u32 }\n        fn invalid_return_type(a: Struct) -> i32 { return a; }\n    \";\n\n    // We need the span for the error, so have to invoke manually.\n    let module = naga::front::wgsl::parse_str(shader).unwrap();\n    let err = naga::valid::Validator::new(\n        naga::valid::ValidationFlags::all(),\n        naga::valid::Capabilities::default(),\n    )\n    .validate(&module)\n    .unwrap_err();\n\n    // This is a proxy for doing the following (with an error\n    // handler installed so it doesn't immediately panic):\n    //\n    // ```\n    // device.create_shader_module(wgpu::ShaderModuleDescriptor {\n    //     label,\n    //     source: wgpu::ShaderSource::Naga(module),\n    // });\n    // ```\n    //\n    // `ShaderSource::Naga` causes the implementation to proceed with an empty\n    // module source, which (prior to the fix for #7165) could panic when\n    // rendering an error if the module contained spans.\n    let _location = err.location(\"\");\n}\n\n#[test]\nfn wrong_argument_count() {\n    check(\n        \"fn foo() -> f32 {\n            return sin();\n        }\",\n        r#\"error: wrong number of arguments: expected 1, found 0\n  ┌─ wgsl:2:20\n  │\n2 │             return sin();\n  │                    ^^^ wrong number of arguments\n\n\"#,\n    );\n}\n\n#[test]\nfn too_many_arguments() {\n    check(\n        \"fn foo() -> f32 {\n            return sin(1.0, 2.0);\n        }\",\n        r#\"error: too many arguments passed to `sin`\n  ┌─ wgsl:2:20\n  │\n2 │             return sin(1.0, 2.0);\n  │                    ^^^      ^^^ unexpected argument #2\n  │\n  = note: The `sin` function accepts at most 1 argument(s)\n\n\"#,\n    );\n}\n\n#[test]\nfn too_many_arguments_2() {\n    check(\n        \"fn foo() -> f32 {\n            return distance(vec2<f32>(), 0i);\n        }\",\n        r#\"error: wrong type passed as argument #2 to `distance`\n  ┌─ wgsl:2:20\n  │\n2 │             return distance(vec2<f32>(), 0i);\n  │                    ^^^^^^^^              ^^ argument #2 has type `i32`\n  │\n  = note: `distance` accepts the following types for argument #2:\n  = note: allowed type: f32\n  = note: allowed type: f16\n  = note: allowed type: f64\n  = note: allowed type: vec2<f32>\n  = note: allowed type: vec2<f16>\n  = note: allowed type: vec2<f64>\n  = note: allowed type: vec3<f32>\n  = note: allowed type: vec3<f16>\n  = note: allowed type: vec3<f64>\n  = note: allowed type: vec4<f32>\n  = note: allowed type: vec4<f16>\n  = note: allowed type: vec4<f64>\n\n\"#,\n    );\n}\n\n#[test]\nfn inconsistent_type() {\n    check(\n        \"fn foo() -> f32 {\n            return dot(vec4<f32>(), vec3<f32>());\n        }\",\n        \"error: inconsistent type passed as argument #2 to `dot`\n  ┌─ wgsl:2:20\n  │\n2 │             return dot(vec4<f32>(), vec3<f32>());\n  │                    ^^^ ^^^^^^^^^^^  ^^^^^^^^^^^ argument #2 has type vec3<f32>\n  │                        │\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\n  │                        this argument has type vec4<f32>, which constrains subsequent arguments\n  │\n  = note: Because argument #1 has type vec4<f32>, only the following types\n  = note: (or types that automatically convert to them) are accepted for argument #2:\n  = note: allowed type: vec4<f32>\n\n\",\n    );\n}\n\n#[test]\nfn more_inconsistent_type() {\n    #[track_caller]\n    fn variant(call: &str) {\n        check_error_matches(\n            &format!(\"fn f() {{ var x = {call}; }}\"),\n            \"inconsistent type\",\n        );\n    }\n\n    variant(\"min(1.0, 1i)\");\n    variant(\"min(1i, 1.0)\");\n    variant(\"min(1i, 1f)\");\n    variant(\"min(1f, 1i)\");\n\n    variant(\"clamp(1, 1.0, 1i)\");\n    variant(\"clamp(1, 1i, 1.0)\");\n    variant(\"clamp(1, 1i, 1f)\");\n    variant(\"clamp(1, 1f, 1i)\");\n    variant(\"clamp(1.0, 1, 1i)\");\n    variant(\"clamp(1.0, 1.0, 1i)\");\n    variant(\"clamp(1.0, 1i, 1)\");\n    variant(\"clamp(1.0, 1i, 1.0)\");\n    variant(\"clamp(1.0, 1i, 1i)\");\n    variant(\"clamp(1.0, 1i, 1f)\");\n    variant(\"clamp(1.0, 1f, 1i)\");\n    variant(\"clamp(1i, 1, 1.0)\");\n    variant(\"clamp(1i, 1, 1f)\");\n    variant(\"clamp(1i, 1.0, 1)\");\n    variant(\"clamp(1i, 1.0, 1.0)\");\n    variant(\"clamp(1i, 1.0, 1i)\");\n    variant(\"clamp(1i, 1.0, 1f)\");\n    variant(\"clamp(1i, 1i, 1.0)\");\n    variant(\"clamp(1i, 1i, 1f)\");\n    variant(\"clamp(1i, 1f, 1)\");\n    variant(\"clamp(1i, 1f, 1.0)\");\n    variant(\"clamp(1i, 1f, 1i)\");\n    variant(\"clamp(1i, 1f, 1f)\");\n    variant(\"clamp(1f, 1, 1i)\");\n    variant(\"clamp(1f, 1.0, 1i)\");\n    variant(\"clamp(1f, 1i, 1)\");\n    variant(\"clamp(1f, 1i, 1.0)\");\n    variant(\"clamp(1f, 1i, 1i)\");\n    variant(\"clamp(1f, 1i, 1f)\");\n    variant(\"clamp(1f, 1f, 1i)\");\n}\n\n/// Naga should not crash just because the type of a\n/// bad argument is a struct.\n#[test]\nfn struct_names_in_argument_errors() {\n    #[track_caller]\n    fn variant(argument: &str) -> Result<naga::Module, naga::front::wgsl::ParseError> {\n        let input = format!(\n            r#\"\n                struct A {{ x: i32, }};\n                fn f() {{ _ = sin({argument}); }}\n            \"#\n        );\n        naga::front::wgsl::parse_str(&input)\n    }\n\n    assert!(variant(\"1.0\").is_ok());\n    assert!(variant(\"1\").is_ok());\n    assert!(variant(\"1i\").is_err());\n    assert!(variant(\"A()\").is_err());\n}\n\n/// Naga should not crash just because the type of a\n/// bad conversion operand is a struct.\n#[test]\nfn struct_names_in_conversion_errors() {\n    #[track_caller]\n    fn variant(argument: &str) -> Result<naga::Module, naga::front::wgsl::ParseError> {\n        let input = format!(\n            r#\"\n                struct A {{ x: i32, }};\n                fn f() {{ _ = i32({argument}); }}\n            \"#\n        );\n        naga::front::wgsl::parse_str(&input)\n    }\n\n    assert!(variant(\"1.0\").is_ok());\n    assert!(variant(\"1\").is_ok());\n    assert!(variant(\"1i\").is_ok());\n    assert!(variant(\"A()\").is_err());\n}\n\n/// Naga should not crash just because the type of a\n/// bad initializer is a struct.\n#[test]\nfn struct_names_in_init_errors() {\n    #[track_caller]\n    fn variant(init: &str) -> Result<naga::Module, naga::front::wgsl::ParseError> {\n        let input = format!(\n            r#\"\n                struct A {{ x: i32, }};\n                fn f() {{ var y: i32 = {init}; }}\n            \"#\n        );\n        naga::front::wgsl::parse_str(&input)\n    }\n\n    assert!(variant(\"1\").is_ok());\n    assert!(variant(\"1i\").is_ok());\n    assert!(variant(\"1.0\").is_err());\n    assert!(variant(\"A()\").is_err());\n}\n\n/// Constant evaluation with interesting values.\n#[test]\nfn const_eval_value_errors() {\n    #[track_caller]\n    fn variant(expr: &str) -> Result<naga::Module, naga::front::wgsl::ParseError> {\n        let input = format!(\n            r#\"\n                fn f() {{ _ = {expr}; }}\n            \"#\n        );\n        naga::front::wgsl::parse_str(&input)\n    }\n\n    assert!(variant(\"1/1\").is_ok());\n    assert!(variant(\"1/0\").is_err());\n\n    assert!(variant(\"f32(abs(1))\").is_ok());\n    assert!(variant(\"f32(abs(-9223372036854775807))\").is_ok());\n    assert!(variant(\"f32(abs(-9223372036854775807 - 1))\").is_ok());\n}\n\n#[test]\nfn subgroup_capability() {\n    // Some of these tests should be `check_extension_validation` tests that\n    // also check handling of the enable directive, but that handling is not\n    // currently correct. https://github.com/gfx-rs/wgpu/issues/8202\n\n    // Non-barrier subgroup operations...\n\n    // ...in fragment and compute shaders require [`Capabilities::SUBGROUP`]`.\n    for stage in [naga::ShaderStage::Fragment, naga::ShaderStage::Compute] {\n        let stage_attr = match stage {\n            naga::ShaderStage::Fragment => \"@fragment\",\n            naga::ShaderStage::Compute => \"@compute @workgroup_size(1)\",\n            _ => unreachable!(),\n        };\n        check_one_validation! {\n            &format!(\"\n                {stage_attr}\n                fn main() {{\n                    _ = subgroupBallot();\n                }}\n            \"),\n            Err(naga::valid::ValidationError::EntryPoint {\n                stage: err_stage,\n                source: naga::valid::EntryPointError::Function(\n                    naga::valid::FunctionError::MissingCapability(Capabilities::SUBGROUP)\n                ),\n                ..\n            }) if *err_stage == stage\n        }\n    }\n\n    // ...in fragment and compute shaders require *only* [`Capabilities::SUBGROUP`]`.\n    for stage in [naga::ShaderStage::Fragment, naga::ShaderStage::Compute] {\n        let stage_attr = match stage {\n            naga::ShaderStage::Fragment => \"@fragment\",\n            naga::ShaderStage::Compute => \"@compute @workgroup_size(1)\",\n            _ => unreachable!(),\n        };\n        no_validation_error(\n            &format!(\n                \"\n                {stage_attr}\n                fn main() {{\n                    _ = subgroupBallot();\n                }}\n            \"\n            ),\n            Capabilities::SUBGROUP,\n        );\n    }\n\n    // ...in vertex shaders require both [`Capabilities::SUBGROUP`] and\n    // [`Capabilities::SUBGROUP_VERTEX_STAGE`]`. (But note that\n    // `create_validator` automatically sets `Capabilities::SUBGROUP` whenever\n    // `Features::SUBGROUP_VERTEX` is available.)\n    for cap in [Capabilities::SUBGROUP, Capabilities::SUBGROUP_VERTEX_STAGE] {\n        check_validation! {\n            \"\n                @vertex\n                fn main() -> @builtin(position) vec4<f32> {{\n                    _ = subgroupBallot();\n                    return vec4();\n                }}\n            \":\n            Err(_),\n            cap\n        }\n    }\n    no_validation_error(\n        \"\n            @vertex\n            fn main() -> @builtin(position) vec4<f32> {{\n                _ = subgroupBallot();\n                return vec4();\n            }}\n        \",\n        Capabilities::SUBGROUP | Capabilities::SUBGROUP_VERTEX_STAGE,\n    );\n\n    // Subgroup barriers...\n\n    // ...require both SUBGROUP and SUBGROUP_BARRIER.\n    for cap in [Capabilities::SUBGROUP, Capabilities::SUBGROUP_BARRIER] {\n        check_validation! {\n            r#\"\n                @compute @workgroup_size(1)\n                fn main() {\n                    subgroupBarrier();\n                }\n            \"#:\n            Err(naga::valid::ValidationError::EntryPoint {\n                stage: naga::ShaderStage::Compute,\n                source: naga::valid::EntryPointError::Function(\n                    naga::valid::FunctionError::MissingCapability(required_caps)\n                ),\n                ..\n            }) if *required_caps == Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER,\n            cap\n        }\n    }\n\n    // ...are never supported in vertex shaders.\n    check_validation! {\n        r#\"\n            @vertex\n            fn main() -> @builtin(position) vec4<f32> {\n                subgroupBarrier();\n                return vec4();\n            }\n        \"#:\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::ForbiddenStageOperations,\n            ..\n        }),\n        Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER | Capabilities::SUBGROUP_VERTEX_STAGE\n    }\n\n    // ...are never supported in fragment shaders.\n    check_validation! {\n        r#\"\n            @fragment\n            fn main() {\n                subgroupBarrier();\n            }\n        \"#:\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Fragment,\n            source: naga::valid::EntryPointError::ForbiddenStageOperations,\n            ..\n        }),\n        Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER\n    }\n\n    // The `subgroup_id` built-in...\n\n    // ...in compute shaders requires [`Capabilities::SUBGROUP`]`.\n    check_one_validation! {\n        \"\n            @compute @workgroup_size(1)\n            fn main(@builtin(subgroup_id) subgroup_id: u32) {{\n            }}\n        \",\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Compute,\n            source: naga::valid::EntryPointError::Argument(\n                _,\n                naga::valid::VaryingError::UnsupportedCapability(Capabilities::SUBGROUP)\n            ),\n            ..\n        })\n    }\n\n    // ...in compute shaders requires *only* [`Capabilities::SUBGROUP`]`.\n    no_validation_error(\n        \"\n        @compute @workgroup_size(1)\n        fn main(@builtin(subgroup_id) subgroup_id: u32) {{\n        }}\n        \",\n        Capabilities::SUBGROUP,\n    );\n}\n\n#[test]\nfn subgroup_invalid_broadcast() {\n    check_validation! {\n        r#\"\n            fn main(id: u32) {\n                _ = subgroupBroadcast(123, id);\n            }\n        \"#:\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::InvalidSubgroup(\n                naga::valid::SubgroupError::InvalidInvocationIdExprType(_),\n            ),\n            ..\n        }),\n        naga::valid::Capabilities::SUBGROUP\n    }\n    check_validation! {\n        r#\"\n            fn main(id: u32) {\n                _ = quadBroadcast(123, id);\n            }\n        \"#:\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::InvalidSubgroup(\n                naga::valid::SubgroupError::InvalidInvocationIdExprType(_),\n            ),\n            ..\n        }),\n        naga::valid::Capabilities::SUBGROUP\n    }\n}\n\n#[test]\nfn invalid_clip_distances() {\n    // Missing capability or enable directive\n    check_extension_validation! {\n        Capabilities::CLIP_DISTANCES,\n        r#\"\n            @vertex\n            fn vs_main() -> @builtin(clip_distances) array<f32, 8> {\n                var out: array<f32, 8>;\n                return out;\n            }\n        \"#,\n        r###\"error: the `clip_distances` enable extension is not enabled\n  ┌─ wgsl:3:38\n  │\n3 │             fn vs_main() -> @builtin(clip_distances) array<f32, 8> {\n  │                                      ^^^^^^^^^^^^^^ the `clip_distances` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable clip_distances;` at the top of the shader, before any other items.\n\n\"###,\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::Result(\n                naga::valid::VaryingError::UnsupportedCapability(Capabilities::CLIP_DISTANCES)\n            ),\n            ..\n        })\n    }\n\n    // Maximum clip distances exceeded\n    check_validation! {\n        r#\"\n            enable clip_distances;\n            struct VertexOutput {\n                @builtin(position) pos: vec4f,\n                @builtin(clip_distances) clip_distances: array<f32, 9>,\n            }\n\n            @vertex\n            fn vs_main() -> VertexOutput {\n                var out: VertexOutput;\n                return out;\n            }\n        \"#:\n        Err(naga::valid::ValidationError::EntryPoint {\n            stage: naga::ShaderStage::Vertex,\n            source: naga::valid::EntryPointError::Result(\n                naga::valid::VaryingError::InvalidBuiltInType(naga::ir::BuiltIn::ClipDistances, _)\n            ),\n            ..\n        }),\n        naga::valid::Capabilities::CLIP_DISTANCES\n    }\n}\n\n#[test]\nfn recognized_but_unimplemented_enable_extension() {\n    let extension = naga::front::wgsl::UnimplementedEnableExtension::Subgroups;\n    // NOTE: We match exhaustively here to help maintainers add or remove variants to the above\n    // array.\n    let snapshot = match extension {\n            naga::front::wgsl::UnimplementedEnableExtension::Subgroups => \"\\\nerror: the `subgroups` enable-extension is not yet supported\n  ┌─ wgsl:1:8\n  │\n1 │ enable subgroups;\n  │        ^^^^^^^^^ this enable-extension specifies standard functionality which is not yet implemented in Naga\n  │\n  = note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5555>, so they can prioritize it!\n\n\",\n        };\n\n    let shader = {\n        let extension = naga::front::wgsl::EnableExtension::Unimplemented(extension);\n        format!(\"enable {};\", extension.to_ident())\n    };\n\n    check(&shader, snapshot);\n}\n\n#[test]\nfn max_type_size_large_array() {\n    // The total size of an array is not resolved until validation. Type aliases\n    // don't get spans so the error isn't very helpful.\n    check_validation! {\n        \"alias LargeArray = array<u32, (1 << 28) + 1>;\":\n        Err(naga::valid::ValidationError::Layouter(\n                naga::proc::LayoutError {\n                    inner: naga::proc::LayoutErrorInner::TooLarge,\n                    ..\n                }\n        ))\n    }\n}\n\n#[test]\nfn max_type_size_array_of_arrays() {\n    // If the size of the base type of an array is oversize, the error is raised\n    // during lowering. Anonymous types don't get spans so this error isn't very\n    // helpful.\n    check(\n        \"alias ArrayOfArrays = array<array<u32, (1 << 28) + 1>, 22>;\",\n        r#\"error: type is too large\n = note: the maximum size is 1073741824 bytes\n\n\"#,\n    );\n}\n\n#[test]\nfn max_type_size_override_array() {\n    // The validation that occurs after override processing should reject any\n    // arrays that were overridden to be larger than the maximum size. Type\n    // aliases don't get spans so the error isn't very helpful.\n    let source = r#\"\n            override SIZE: u32 = 1;\n            alias ArrayOfOverrideArrays = array<u32, SIZE>;\n\n            var<workgroup> global: ArrayOfOverrideArrays;\n\n            @compute @workgroup_size(64)\n            fn main() {\n                let used = &global;\n            }\n        \"#;\n    let module = naga::front::wgsl::parse_str(source).expect(\"module should parse\");\n    let info = valid::Validator::new(Default::default(), valid::Capabilities::all())\n        .validate(&module)\n        .expect(\"module should validate\");\n\n    let overrides = hashbrown::HashMap::from([(String::from(\"SIZE\"), f64::from((1 << 28) + 1))]);\n    let err = naga::back::pipeline_constants::process_overrides(&module, &info, None, &overrides)\n        .unwrap_err();\n    let naga::back::pipeline_constants::PipelineConstantError::ValidationError(err) = err else {\n        panic!(\"expected a validation error, got {err:?}\");\n    };\n    assert!(matches!(\n        err.into_inner(),\n        naga::valid::ValidationError::Layouter(naga::proc::LayoutError {\n            inner: naga::proc::LayoutErrorInner::TooLarge,\n            ..\n        }),\n    ));\n}\n\n#[test]\nfn max_type_size_array_in_struct() {\n    // If a struct member is oversize, the error is raised during lowering.\n    // For struct members we can associate the error with the member.\n    check(\n        r#\"\n            struct ContainsLargeArray {\n                arr: array<u32, (1 << 28) + 1>,\n            }\n        \"#,\n        r#\"error: struct member is too large\n  ┌─ wgsl:3:17\n  │\n3 │                 arr: array<u32, (1 << 28) + 1>,\n  │                 ^^^ this member exceeds the maximum size\n  │\n  = note: the maximum size is 1073741824 bytes\n\n\"#,\n    );\n}\n\n#[test]\nfn max_type_size_two_arrays_in_struct() {\n    // The total size of a struct is checked during lowering. For a struct,\n    // we can associate the error with the struct itself.\n    check(\n        r#\"\n            struct TwoArrays {\n                arr1: array<u32, 1 << 27>,\n                arr2: array<u32, (1 << 27) + 1>,\n            }\n        \"#,\n        \"error: type is too large\n  ┌─ wgsl:2:13\n  │\\x20\\x20\n2 │ ╭             struct TwoArrays {\n3 │ │                 arr1: array<u32, 1 << 27>,\n4 │ │                 arr2: array<u32, (1 << 27) + 1>,\n5 │ │             }\n  │ ╰─────────────^ this type exceeds the maximum size\n  │\\x20\\x20\n  = note: the maximum size is 1073741824 bytes\n\n\",\n    );\n}\n\n#[test]\nfn max_type_size_array_of_structs() {\n    // The total size of an array is not resolved until validation. Type aliases\n    // don't get spans so the error isn't very helpful.\n    check_validation! {\n        r#\"\n            struct NotVeryBigStruct {\n                data: u32,\n            }\n            alias BigArrayOfStructs = array<NotVeryBigStruct, (1 << 28) + 1>;\n        \"#:\n        Err(naga::valid::ValidationError::Layouter(\n                naga::proc::LayoutError {\n                    inner: naga::proc::LayoutErrorInner::TooLarge,\n                    ..\n                }\n        ))\n    }\n}\n\n#[test]\nfn source_with_control_char() {\n    check(\n        \"\\x07\",\n        \"error: expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file, found \\\"\\\\u{7}\\\"\n  ┌─ wgsl:1:1\n  │\n1 │ �\n  │ ^ expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file\n\n\",\n    );\n}\n\n#[test]\nfn enumerant_with_template_parameters() {\n    check(\n        r#\"var<private<xlerb, 1+2>> x: u32;\"#,\n        \"error: unexpected template\n  ┌─ wgsl:1:5\n  │\n1 │ var<private<xlerb, 1+2>> x: u32;\n  │     ^^^^^^^^^^^^^^^^^^^ expected identifier\n\n\",\n    );\n}\n\n#[test]\nfn ray_types_enable_extension() {\n    check_extension_validation!(\n        Capabilities::RAY_QUERY,\n        r#\"fn foo() {\n            var a: ray_query;\n        }\n        \"#,\n        r#\"error: the `wgpu_ray_query` enable extension is not enabled\n  ┌─ wgsl:2:20\n  │\n2 │             var a: ray_query;\n  │                    ^^^^^^^^^ the `wgpu_ray_query` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_query;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::MissingCapability(Capabilities::RAY_QUERY),\n            ..\n        })\n    );\n\n    // can be enabled by either of these extensions\n    check_extension_validation!(\n        Capabilities::RAY_QUERY,\n        r#\"@group(0) @binding(0)\n        var acc_struct: acceleration_structure;\n        \"#,\n        r#\"error: the `wgpu_ray_query` enable extension is not enabled\n  ┌─ wgsl:2:25\n  │\n2 │         var acc_struct: acceleration_structure;\n  │                         ^^^^^^^^^^^^^^^^^^^^^^ the `wgpu_ray_query` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_query;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::MissingCapability(Capabilities::RAY_QUERY),\n            ..\n        }),\n        Capabilities::RAY_TRACING_PIPELINE\n    );\n    check_extension_validation!(\n        Capabilities::RAY_TRACING_PIPELINE,\n        r#\"@group(0) @binding(0)\n        var acc_struct: acceleration_structure;\n        \"#,\n        r#\"error: the `wgpu_ray_query` enable extension is not enabled\n  ┌─ wgsl:2:25\n  │\n2 │         var acc_struct: acceleration_structure;\n  │                         ^^^^^^^^^^^^^^^^^^^^^^ the `wgpu_ray_query` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_query;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::MissingCapability(Capabilities::RAY_QUERY),\n            ..\n        }),\n        Capabilities::RAY_QUERY\n    );\n}\n\n#[test]\nfn ray_query_vertex_return_enable_extension() {\n    check_extension_validation!(\n        Capabilities::RAY_HIT_VERTEX_POSITION,\n        r#\"enable wgpu_ray_query;\n\n        fn foo() {\n            var a: ray_query<vertex_return>;\n        }\n        \"#,\n        r#\"error: the `wgpu_ray_query_vertex_return` enable extension is not enabled\n  ┌─ wgsl:4:30\n  │\n4 │             var a: ray_query<vertex_return>;\n  │                              ^^^^^^^^^^^^^ the `wgpu_ray_query_vertex_return` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_query_vertex_return;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::MissingCapability(\n                Capabilities::RAY_HIT_VERTEX_POSITION\n            ),\n            ..\n        })\n    );\n\n    check_extension_validation!(\n        Capabilities::RAY_HIT_VERTEX_POSITION,\n        r#\"enable wgpu_ray_query;\n\n        @group(0) @binding(0)\n        var acc_struct: acceleration_structure<vertex_return>;\n        \"#,\n        r#\"error: the `wgpu_ray_query_vertex_return` enable extension is not enabled\n  ┌─ wgsl:4:48\n  │\n4 │         var acc_struct: acceleration_structure<vertex_return>;\n  │                                                ^^^^^^^^^^^^^ the `wgpu_ray_query_vertex_return` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_query_vertex_return;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::MissingCapability(\n                Capabilities::RAY_HIT_VERTEX_POSITION\n            ),\n            ..\n        })\n    );\n}\n\n#[test]\nfn binding_array_requires_capability() {\n    check_validation! {\n        r#\"\n            struct Buffer { data: u32 }\n            @group(0) @binding(0)\n            var<storage> storage_array: binding_array<Buffer, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::STORAGE_BUFFER_BINDING_ARRAY\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        r#\"\n            struct Buffer { data: u32 }\n            @group(0) @binding(0)\n            var<uniform> uniform_array: binding_array<Buffer, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::BUFFER_BINDING_ARRAY\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        r#\"\n            @group(0) @binding(0)\n            var storage_texture_array: binding_array<texture_storage_2d<rgba8unorm, write>, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::STORAGE_TEXTURE_BINDING_ARRAY\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        r#\"\n            @group(0) @binding(0)\n            var sampled_texture_array: binding_array<texture_2d<f32>, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY\n            ),\n            ..\n        })\n    }\n\n    check_validation! {\n        r#\"\n            @group(0) @binding(0)\n            var sampler_array: binding_array<sampler, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY\n            ),\n            ..\n        })\n    }\n\n    // Binding arrays of external textures are not yet supported.\n    check_validation! {\n        r#\"\n            @group(0) @binding(0)\n            var external_texture_array: binding_array<texture_external, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::Type {\n            source: naga::valid::TypeError::BindingArrayBaseExternalTextures,\n            ..\n        }),\n        Capabilities::TEXTURE_EXTERNAL\n    }\n\n    // Binding arrays of acceleration structures require a capability.\n    check_validation! {\n        r#\"\n            enable wgpu_ray_query;\n            @group(0) @binding(0)\n            var acc_struct_array: binding_array<acceleration_structure, 10>;\n        \"#:\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY\n            ),\n            ..\n        }),\n        Capabilities::RAY_QUERY\n    }\n}\n\n#[test]\nfn cooperative_matrix_enable_extension() {\n    for ty in [\"coop_mat8x8\", \"coop_mat16x16\"] {\n        let carets = \"^\".repeat(ty.len());\n\n        check_extension_validation!(\n            // Used in type declaration\n            Capabilities::COOPERATIVE_MATRIX,\n            &format!(\n                r#\"fn foo() {{\n    var a: {ty}<f32, A>;\n}}\n\"#\n            ),\n            &format!(\n                r#\"error: the `wgpu_cooperative_matrix` enable extension is not enabled\n  ┌─ wgsl:2:12\n  │\n2 │     var a: {ty}<f32, A>;\n  │            {carets} the `wgpu_cooperative_matrix` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_cooperative_matrix;` at the top of the shader, before any other items.\n\n\"#,\n            ),\n            Err(naga::valid::ValidationError::Type {\n                source: naga::valid::TypeError::MissingCapability(Capabilities::COOPERATIVE_MATRIX),\n                ..\n            })\n        );\n\n        // Used as constructor\n        check_extension_validation!(\n            Capabilities::COOPERATIVE_MATRIX,\n            &format!(\n                r#\"fn foo() {{\n    let a = {ty}<f32, A>();\n}}\n\"#,\n            ),\n            &format!(\n                r#\"error: the `wgpu_cooperative_matrix` enable extension is not enabled\n  ┌─ wgsl:2:13\n  │\n2 │     let a = {ty}<f32, A>();\n  │             {carets}^^^^^^^^ the `wgpu_cooperative_matrix` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_cooperative_matrix;` at the top of the shader, before any other items.\n\n\"#,\n            ),\n            Err(naga::valid::ValidationError::Type {\n                source: naga::valid::TypeError::MissingCapability(Capabilities::COOPERATIVE_MATRIX),\n                ..\n            })\n        );\n    }\n}\n\n/// Tests for mesh shader extension validation via WGSL parsing.\n///\n/// Some mesh shader features can only be tested at parse-level in WGSL due to\n/// parse-order limitations (e.g., mesh builtins in structs fail before mesh-specific\n/// attributes are checked). For IR-level validation tests that directly test the\n/// validator capability checks, see `validation::mesh_shader_capability`.\n#[test]\nfn mesh_shader_enable_extension() {\n    // @task stage attribute\n    check_extension_validation!(\n        Capabilities::MESH_SHADER,\n        r#\"@task @workgroup_size(1)\n        fn main() -> @builtin(mesh_task_size) vec3<u32> {\n            return vec3(1u, 1u, 1u);\n        }\n        \"#,\n        r#\"error: the `wgpu_mesh_shader` enable extension is not enabled\n  ┌─ wgsl:1:2\n  │\n1 │ @task @workgroup_size(1)\n  │  ^^^^ the `wgpu_mesh_shader` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_mesh_shader;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::EntryPoint {\n            source: naga::valid::EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER),\n            ..\n        })\n    );\n\n    // @mesh stage attribute\n    check_extension_validation!(\n        Capabilities::MESH_SHADER,\n        r#\"struct MeshOutput { dummy: u32 }\n        var<workgroup> mesh_output: MeshOutput;\n        @mesh(mesh_output) @workgroup_size(1)\n        fn main() {}\n        \"#,\n        r#\"error: the `wgpu_mesh_shader` enable extension is not enabled\n  ┌─ wgsl:3:10\n  │\n3 │         @mesh(mesh_output) @workgroup_size(1)\n  │          ^^^^ the `wgpu_mesh_shader` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_mesh_shader;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::EntryPoint {\n            source: naga::valid::EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER),\n            ..\n        })\n    );\n\n    // @per_primitive attribute\n    check_extension_validation!(\n        Capabilities::MESH_SHADER,\n        r#\"struct FragInput {\n            @location(0) @per_primitive value: f32,\n        }\n        @fragment\n        fn main(input: FragInput) {}\n        \"#,\n        r#\"error: the `wgpu_mesh_shader` enable extension is not enabled\n  ┌─ wgsl:2:27\n  │\n2 │             @location(0) @per_primitive value: f32,\n  │                           ^^^^^^^^^^^^^ the `wgpu_mesh_shader` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_mesh_shader;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::EntryPoint {\n            source: naga::valid::EntryPointError::Argument(\n                _,\n                naga::valid::VaryingError::UnsupportedCapability(Capabilities::MESH_SHADER)\n            ),\n            ..\n        })\n    );\n\n    // `@payload`` attribute. It is not possible for this attribute to reach the validator\n    // without the extension enabled, because the attribute is only allowed on mesh and task\n    // stages, and those stages are rejected (with or without the `@payload` attribute) when\n    // the mesh shader extension is not enabled.\n    //\n    // There is a direct-to-validator test case for `@payload` in `validation.rs`.\n    check(\n        r#\"@compute @workgroup_size(1) @payload(foo)\n        fn main() {}\n        \"#,\n        r#\"error: the `wgpu_mesh_shader` enable extension is not enabled\n  ┌─ wgsl:1:30\n  │\n1 │ @compute @workgroup_size(1) @payload(foo)\n  │                              ^^^^^^^ the `wgpu_mesh_shader` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_mesh_shader;` at the top of the shader, before any other items.\n\n\"#,\n    );\n\n    // `task_payload` address space\n    check_extension_validation!(\n        Capabilities::MESH_SHADER,\n        r#\"struct Payload { dummy: u32 }\n        var<task_payload> taskPayload: Payload;\n        @compute @workgroup_size(1)\n        fn main() {\n            taskPayload.dummy = 1u;\n        }\n        \"#,\n        r#\"error: the `wgpu_mesh_shader` enable extension is not enabled\n  ┌─ wgsl:2:13\n  │\n2 │         var<task_payload> taskPayload: Payload;\n  │             ^^^^^^^^^^^^ the `wgpu_mesh_shader` \"Enable Extension\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_mesh_shader;` at the top of the shader, before any other items.\n\n\"#,\n        Err(naga::valid::ValidationError::GlobalVariable {\n            source: naga::valid::GlobalVariableError::UnsupportedCapability(\n                Capabilities::MESH_SHADER\n            ),\n            ..\n        })\n    );\n}\n\n/// Checks that every ray tracing pipeline binding in naga is invalid in other stages.\n#[test]\nfn check_ray_tracing_pipeline_bindings() {\n    for (builtin, ty) in [\n        (\"ray_invocation_id\", \"vec3<u32>\"),\n        (\"num_ray_invocations\", \"vec3<u32>\"),\n        (\"instance_custom_data\", \"u32\"),\n        (\"geometry_index\", \"u32\"),\n        (\"world_ray_origin\", \"vec3<f32>\"),\n        (\"world_ray_direction\", \"vec3<f32>\"),\n        (\"object_ray_origin\", \"vec3<f32>\"),\n        (\"object_ray_direction\", \"vec3<f32>\"),\n        (\"ray_t_min\", \"f32\"),\n        (\"ray_t_current_max\", \"f32\"),\n        (\"object_to_world\", \"mat4x3<f32>\"),\n        (\"world_to_object\", \"mat4x3<f32>\"),\n        (\"hit_kind\", \"u32\"),\n    ] {\n        for stage in [\"@compute @workgroup_size(1)\", \" @vertex\", \"@fragment\"] {\n            check_one_validation!(\n                &format!(\n                    \"{stage}\n            fn main(@builtin({builtin}) v: {ty}) {{}}\n            \"\n                ),\n                Err(naga::valid::ValidationError::EntryPoint {\n                    source: naga::valid::EntryPointError::Argument(\n                        0,\n                        naga::valid::VaryingError::InvalidBuiltInStage(_),\n                    ),\n                    ..\n                },)\n            );\n        }\n    }\n}\n\n/// Checks ray generation stage is invalid without enable extension (other stages require `@incoming_payload` which forces a ray payload which is checked in [`check_ray_tracing_pipeline_payload`])\n#[test]\nfn check_ray_tracing_pipeline_ray_generation() {\n    check_extension_validation!(\n            Capabilities::RAY_TRACING_PIPELINE,\n            \"@ray_generation\n                fn main() {{}}\",\n            \"error: the `wgpu_ray_tracing_pipeline` enable extension is not enabled\n  ┌─ wgsl:1:2\n  │\n1 │ @ray_generation\n  │  ^^^^^^^^^^^^^^ the `wgpu_ray_tracing_pipeline` \\\"Enable Extension\\\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_tracing_pipeline;` at the top of the shader, before any other items.\n\n\",\n            Err(naga::valid::ValidationError::EntryPoint {\n                source: naga::valid::EntryPointError::UnsupportedCapability(naga::valid::Capabilities::RAY_TRACING_PIPELINE),\n                ..\n            },)\n        );\n}\n\n#[test]\nfn check_ray_tracing_pipeline_payload() {\n    for space in [\"ray_payload\", \"incoming_ray_payload\"] {\n        // ascii is a byte per char so length is fine\n        let space_arrows = \"^\".to_string().repeat(space.len());\n        check_extension_validation!(\n            Capabilities::RAY_TRACING_PIPELINE,\n            &format!(\"var<{space}> payload: u32;\"),\n            &format!(\"error: the `wgpu_ray_tracing_pipeline` enable extension is not enabled\n  ┌─ wgsl:1:5\n  │\n1 │ var<{space}> payload: u32;\n  │     {space_arrows} the `wgpu_ray_tracing_pipeline` \\\"Enable Extension\\\" is needed for this functionality, but it is not currently enabled.\n  │\n  = note: You can enable this extension by adding `enable wgpu_ray_tracing_pipeline;` at the top of the shader, before any other items.\n\n\"),\n            Err(naga::valid::ValidationError::GlobalVariable {\n                source: naga::valid::GlobalVariableError::UnsupportedCapability(naga::valid::Capabilities::RAY_TRACING_PIPELINE),\n                ..\n            },)\n        );\n    }\n}\n\n#[test]\nfn check_ray_tracing_pipeline_incoming_payload_required() {\n    for stage in [\"any_hit\", \"closest_hit\", \"miss\"] {\n        // ascii is a byte per char so length is fine\n        let stage_arrows = \"^\".to_string().repeat(stage.len());\n        check(\n            &format!(\"enable wgpu_ray_tracing_pipeline; @{stage} fn main() {{}}\"),\n            &format!(\"error: incoming payload is missing on a `closest_hit`, `any_hit` or `miss` shader entry point\n  ┌─ wgsl:1:36\n  │\n1 │ enable wgpu_ray_tracing_pipeline; @{stage} fn main() {{}}\n  │                                    {stage_arrows} must be paired with a `@incoming_payload` attribute\n\n\"),\n        );\n    }\n}\n\n#[test]\nfn check_ray_tracing_pipeline_payload_disallowed() {\n    for (stage, output, stmt) in [\n        (\n            \"var<incoming_ray_payload> incoming: u32; @any_hit @incoming_payload(incoming)\",\n            \"\",\n            \"\",\n        ),\n        (\"@compute @workgroup_size(1)\", \"\", \"\"),\n        (\n            \"@vertex\",\n            \" -> @builtin(position) vec4<f32>\",\n            \"return vec4();\",\n        ),\n        (\"@fragment\", \"\", \"\"),\n    ] {\n        check_one_validation!(\n            &format!(\n                \"enable wgpu_ray_tracing_pipeline;\n            @group(0) @binding(0) var acc_struct: acceleration_structure;\n            var<ray_payload> payload: u32;\n\n            {stage} fn main() {output} {{_ = payload; {stmt}}}\"\n            ),\n            Err(naga::valid::ValidationError::EntryPoint {\n                source: naga::valid::EntryPointError::RayPayloadInInvalidStage(_),\n                ..\n            },),\n            Capabilities::RAY_TRACING_PIPELINE\n        );\n    }\n}\n\n#[track_caller]\nfn check_with_capabilities(input: &str, snapshot: &str, capabilities: Capabilities) {\n    let mut options = naga::front::wgsl::Options::new();\n    options.capabilities = capabilities;\n    let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);\n    let output = match frontend.parse(input) {\n        Ok(_) => panic!(\"expected parser error, but parsing succeeded!\"),\n        Err(err) => err.emit_to_string(input),\n    };\n    if output != snapshot {\n        for diff in diff::lines(snapshot, &output) {\n            match diff {\n                diff::Result::Left(l) => println!(\"-{l}\"),\n                diff::Result::Both(l, _) => println!(\" {l}\"),\n                diff::Result::Right(r) => println!(\"+{r}\"),\n            }\n        }\n        panic!(\"Error snapshot failed\");\n    }\n}\n\n#[test]\nfn enable_without_capability() {\n    for extension in ImplementedEnableExtension::all() {\n        let ident = EnableExtension::from(*extension).to_ident();\n        let carets = \"^\".repeat(ident.len());\n        check_with_capabilities(\n            &format!(\"enable {ident};\"),\n            &format!(\n                \"error: the `{ident}` extension is not supported in the current environment\n  ┌─ wgsl:1:8\n  │\n1 │ enable {ident};\n  │        {carets} unsupported enable-extension\n\n\"\n            ),\n            !extension.capability(),\n        );\n    }\n}\n\n#[test]\nfn bitwise_shift_errors() {\n    // 32-bit const by const >= bitwidth\n    check_error_matches(\n        \"const N: u32 = 1u >> 32;\",\n        \"RHS of shift operation is greater than or equal to 32\",\n    );\n    check_error_matches(\n        \"const N: i32 = 1i >> 32;\",\n        \"RHS of shift operation is greater than or equal to 32\",\n    );\n\n    // 32-bit const by const overflow\n    check_error_matches(\"const N: u32 = 0xFFFFFFFFu << 1;\", \"overflowed\");\n    check_error_matches(\"const N: i32 = 1i << 31;\", \"overflowed\");\n\n    // 32-bit const by const negative shift\n    check_error_matches(\"const N: u32 = 1u << -1;\", \"cannot represent\");\n    check_error_matches(\"const N: i32 = 1i << -1;\", \"cannot represent\");\n\n    // 32-bit runtime by const < bitwidth\n    check_success(\"fn foo() { var x: u32; var n = x << 31; }\");\n    check_success(\"fn foo() { var x: i32; var n = x << 31; }\");\n    check_success(\"fn foo() { var x: u32; var n = x >> 31; }\");\n    check_success(\"fn foo() { var x: i32; var n = x >> 31; }\");\n\n    // 32-bit runtime by const >= bitwidth\n    check_validation! {\n        \"fn foo() { var x: u32; var n = x >> 32; }\",\n        \"fn foo() { var x: i32; var n = x >> 32; }\",\n        \"fn foo() { var x: u32; var n = x << 32; }\",\n        \"fn foo() { var x: i32; var n = x << 32; }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::Expression {\n                source: naga::valid::ExpressionError::ShiftAmountTooLarge { .. },\n                ..\n            },\n            ..\n        })\n    }\n\n    // (CTS has more 32-bit test cases)\n\n    // Const evaluation of `i64` and `u64` is not implemented, https://github.com/gfx-rs/wgpu/issues/8972\n\n    // 64-bit runtime by const < bitwidth\n    check_success(\"fn foo() { var x: u64; var n = x << 63; }\");\n    check_success(\"fn foo() { var x: i64; var n = x << 63; }\");\n    check_success(\"fn foo() { var x: u64; var n = x >> 63; }\");\n    check_success(\"fn foo() { var x: i64; var n = x >> 63; }\");\n\n    // 64-bit runtime by const >= bitwidth\n    check_validation! {\n        \"fn foo() { var x: u64; var n = x << 64; }\",\n        \"fn foo() { var x: i64; var n = x << 64; }\",\n        \"fn foo() { var x: u64; var n = x >> 64; }\",\n        \"fn foo() { var x: i64; var n = x >> 64; }\":\n        Err(naga::valid::ValidationError::Function {\n            source: naga::valid::FunctionError::Expression {\n                source: naga::valid::ExpressionError::ShiftAmountTooLarge { .. },\n                ..\n            },\n            ..\n        }),\n        naga::valid::Capabilities::SHADER_INT64\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/analysis/spv-shadow.info.ron",
    "content": "(\n    type_flags: [\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | COPY | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | COPY | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n    ],\n    functions: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(1),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [\n                (\n                    image: 0,\n                    sampler: 1,\n                ),\n            ],\n            global_uses: [\n                (\"READ\"),\n                (\"READ\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Handle(13),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Handle(5),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 6,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(1),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [\n                (\n                    image: 0,\n                    sampler: 1,\n                ),\n            ],\n            global_uses: [\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"WRITE\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 7,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 12,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(5),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(6),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 4,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 8,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(20),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 11,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 7,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(20),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 9,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(9),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 11,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 10,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(20),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(22),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(20),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(20),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(1),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [\n                (\n                    image: 0,\n                    sampler: 1,\n                ),\n            ],\n            global_uses: [\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ | WRITE\"),\n                (\"READ | WRITE\"),\n                (\"READ | WRITE\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(5),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(6),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    const_expression_types: [\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Handle(0),\n        Handle(0),\n        Handle(0),\n        Handle(1),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n    ],\n)"
  },
  {
    "path": "naga/tests/out/analysis/wgsl-access.info.ron",
    "content": "(\n    type_flags: [\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | SIZED | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | COPY | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"SIZED | COPY | CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"SIZED | COPY | CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"SIZED | COPY | CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"SIZED | COPY | CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"SIZED | COPY | CREATION_RESOLVED | ARGUMENT\"),\n        (\"DATA | SIZED | COPY | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n    ],\n    functions: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"READ\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 14,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(15),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(15),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(16),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 7,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 16,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(15),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 15,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(49),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"READ\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 14,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(19),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(18),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Uniform,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(19),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(20),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 8,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 20,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(19),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(18),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 19,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 18,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: Some(Bi),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(53),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(21),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(5),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(23),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(22),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(5),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(26),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(28),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(27),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(27),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 27,\n                        space: Function,\n                    )),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(30),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(30),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(32),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(32),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 29,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 31,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(2),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(33),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(34),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 34,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 33,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(33),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(2),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(35),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 35,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Handle(37),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(36),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(36),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(1),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(37),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 37,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 36,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(36),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 36,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 36,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"READ\"),\n                (\"READ | QUERY\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 5,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(5),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 3,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 6,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(10),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(10),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 12,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(10),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(12),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 6,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(ValuePointer(\n                        size: Some(Tri),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 13,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(21),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(21),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 13,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(21),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(21),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 4,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(29),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 17,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(29),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(17),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(31),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(31),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 13,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(31),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 4,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(31),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(5),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(14),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(19),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(25),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 25,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(23),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(5),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Sint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(41),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(7),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(24),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"WRITE\"),\n                (\"\"),\n                (\"WRITE\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 6,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(ValuePointer(\n                        size: Some(Tri),\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(ValuePointer(\n                        size: None,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 6,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Tri,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(6),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(16),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(16),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 12,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(12),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(23),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 14,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(23),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 13,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(23),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 4,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(23),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Sint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(28),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Value(Pointer(\n                        base: 17,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(17),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(0),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(33),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(2),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    const_expression_types: [\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Handle(1),\n        Value(Scalar((\n            kind: Sint,\n            width: 4,\n        ))),\n        Handle(3),\n    ],\n)"
  },
  {
    "path": "naga/tests/out/analysis/wgsl-collatz.info.ron",
    "content": "(\n    type_flags: [\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | COPY | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | COPY | HOST_SHAREABLE | CREATION_RESOLVED\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n    ],\n    functions: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(3),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 7,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 3,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Bool,\n                        width: 1,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: Some(3),\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"READ | WRITE\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 2,\n                    assignable_global: None,\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(1),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 2,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Storage(\n                            access: (\"LOAD | STORE\"),\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(3),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    const_expression_types: [],\n)"
  },
  {
    "path": "naga/tests/out/analysis/wgsl-overrides.info.ron",
    "content": "(\n    type_flags: [\n        (\"DATA | SIZED | COPY | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n        (\"DATA | SIZED | COPY | IO_SHAREABLE | HOST_SHAREABLE | CREATION_RESOLVED | ARGUMENT | CONSTRUCTIBLE\"),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"READ\"),\n                (\"WRITE\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 0,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(6),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(10),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Function,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(11),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Value(Pointer(\n                        base: 1,\n                        space: Private,\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Handle(2),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    const_expression_types: [\n        Value(Scalar((\n            kind: Bool,\n            width: 1,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Handle(1),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Handle(1),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Uint,\n            width: 4,\n        ))),\n        Handle(1),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n        Value(Scalar((\n            kind: Float,\n            width: 4,\n        ))),\n    ],\n)"
  },
  {
    "path": "naga/tests/out/analysis/wgsl-storage-textures.info.ron",
    "content": "(\n    type_flags: [\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n        (\"CREATION_RESOLVED | ARGUMENT\"),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"READ\"),\n                (\"READ\"),\n                (\"READ\"),\n                (\"\"),\n                (\"\"),\n                (\"\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(0),\n                    ty: Handle(0),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(1),\n                    ty: Handle(1),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(4),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(8),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(2),\n                    ty: Handle(2),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(8),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 0,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            flags: (\"EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS\"),\n            available_stages: (\"VERTEX | FRAGMENT | COMPUTE | MESH | TASK | RAY_GENERATION | ANY_HIT | CLOSEST_HIT | MISS\"),\n            uniformity: (\n                non_uniform_result: None,\n                requirements: (\"\"),\n            ),\n            may_kill: false,\n            sampling_set: [],\n            global_uses: [\n                (\"\"),\n                (\"\"),\n                (\"\"),\n                (\"WRITE\"),\n                (\"WRITE\"),\n                (\"WRITE\"),\n            ],\n            expressions: [\n                (\n                    uniformity: (\n                        non_uniform_result: Some(0),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(3),\n                    ty: Handle(3),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(5),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(4),\n                    ty: Handle(4),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: Some(10),\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: Some(5),\n                    ty: Handle(5),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Uint,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Bi,\n                        scalar: (\n                            kind: Uint,\n                            width: 4,\n                        ),\n                    )),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Scalar((\n                        kind: Float,\n                        width: 4,\n                    ))),\n                ),\n                (\n                    uniformity: (\n                        non_uniform_result: None,\n                        requirements: (\"\"),\n                    ),\n                    ref_count: 1,\n                    assignable_global: None,\n                    ty: Value(Vector(\n                        size: Quad,\n                        scalar: (\n                            kind: Float,\n                            width: 4,\n                        ),\n                    )),\n                ),\n            ],\n            sampling: [],\n            dual_source_blending: false,\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    const_expression_types: [],\n)"
  },
  {
    "path": "naga/tests/out/dot/wgsl-quad.dot",
    "content": "digraph Module {\n\tsubgraph cluster_globals {\n\t\tlabel=\"Globals\"\n\t\tg0 [ shape=hexagon label=\"[0] Handle/'u_texture'\" ]\n\t\tg1 [ shape=hexagon label=\"[1] Handle/'u_sampler'\" ]\n\t}\n\tsubgraph cluster_ep0 {\n\t\tlabel=\"Vertex/'vert_main'\"\n\t\tnode [ style=filled ]\n\t\tep0_e0 [ color=\"#8dd3c7\" label=\"[0] Argument[0]\" ]\n\t\tep0_e1 [ color=\"#8dd3c7\" label=\"[1] Argument[1]\" ]\n\t\tep0_e2 [ fillcolor=\"#ffffb3\" label=\"[2] Constant\" ]\n\t\tep0_e3 [ color=\"#fdb462\" label=\"[3] Multiply\" ]\n\t\tep0_e0 -> ep0_e3 [ label=\"right\" ]\n\t\tep0_e2 -> ep0_e3 [ label=\"left\" ]\n\t\tep0_e4 [ fillcolor=\"#ffffb3\" label=\"[4] Literal\" ]\n\t\tep0_e5 [ fillcolor=\"#ffffb3\" label=\"[5] Literal\" ]\n\t\tep0_e6 [ color=\"#bebada\" label=\"[6] Compose\" ]\n\t\t{ ep0_e3 ep0_e4 ep0_e5 } -> ep0_e6\n\t\tep0_e7 [ color=\"#bebada\" label=\"[7] Compose\" ]\n\t\t{ ep0_e1 ep0_e6 } -> ep0_e7\n\t\tep0_s0 [ shape=square label=\"Root\" ]\n\t\tep0_s1 [ shape=square label=\"Emit\" ]\n\t\tep0_s2 [ shape=square label=\"Emit\" ]\n\t\tep0_s3 [ shape=square label=\"Return\" ]\n\t\tep0_s0 -> ep0_s1 [ arrowhead=tee label=\"\" ]\n\t\tep0_s1 -> ep0_s2 [ arrowhead=tee label=\"\" ]\n\t\tep0_s2 -> ep0_s3 [ arrowhead=tee label=\"\" ]\n\t\tep0_e7 -> ep0_s3 [ label=\"value\" ]\n\t\tep0_s1 -> ep0_e3 [ style=dotted ]\n\t\tep0_s2 -> ep0_e6 [ style=dotted ]\n\t\tep0_s2 -> ep0_e7 [ style=dotted ]\n\t}\n\tsubgraph cluster_ep1 {\n\t\tlabel=\"Fragment/'frag_main'\"\n\t\tnode [ style=filled ]\n\t\tep1_e0 [ color=\"#8dd3c7\" label=\"[0] Argument[0]\" ]\n\t\tep1_e1 [ color=\"#ffffb3\" label=\"[1] Global\" ]\n\t\tg0 -> ep1_e1 [fillcolor=gray]\n\t\tep1_e2 [ color=\"#ffffb3\" label=\"[2] Global\" ]\n\t\tg1 -> ep1_e2 [fillcolor=gray]\n\t\tep1_e3 [ color=\"#80b1d3\" label=\"[3] ImageSample\" ]\n\t\tep1_e2 -> ep1_e3 [ label=\"sampler\" ]\n\t\tep1_e1 -> ep1_e3 [ label=\"image\" ]\n\t\tep1_e0 -> ep1_e3 [ label=\"coordinate\" ]\n\t\tep1_e4 [ color=\"#8dd3c7\" label=\"[4] AccessIndex[3]\" ]\n\t\tep1_e3 -> ep1_e4 [ label=\"base\" ]\n\t\tep1_e5 [ fillcolor=\"#ffffb3\" label=\"[5] Literal\" ]\n\t\tep1_e6 [ color=\"#fdb462\" label=\"[6] Equal\" ]\n\t\tep1_e5 -> ep1_e6 [ label=\"right\" ]\n\t\tep1_e4 -> ep1_e6 [ label=\"left\" ]\n\t\tep1_e7 [ color=\"#8dd3c7\" label=\"[7] AccessIndex[3]\" ]\n\t\tep1_e3 -> ep1_e7 [ label=\"base\" ]\n\t\tep1_e8 [ color=\"#fdb462\" label=\"[8] Multiply\" ]\n\t\tep1_e3 -> ep1_e8 [ label=\"right\" ]\n\t\tep1_e7 -> ep1_e8 [ label=\"left\" ]\n\t\tep1_s0 [ shape=square label=\"Root\" ]\n\t\tep1_s1 [ shape=square label=\"Emit\" ]\n\t\tep1_s2 [ shape=square label=\"Emit\" ]\n\t\tep1_s3 [ shape=square label=\"Emit\" ]\n\t\tep1_s4 [ shape=square label=\"If\" ]\n\t\tep1_s5 [ shape=square label=\"Node\" ]\n\t\tep1_s6 [ shape=square label=\"Kill\" ]\n\t\tep1_s7 [ shape=square label=\"Node\" ]\n\t\tep1_s8 [ shape=square label=\"Merge\" ]\n\t\tep1_s9 [ shape=square label=\"Emit\" ]\n\t\tep1_s10 [ shape=square label=\"Return\" ]\n\t\tep1_s0 -> ep1_s1 [ arrowhead=tee label=\"\" ]\n\t\tep1_s1 -> ep1_s2 [ arrowhead=tee label=\"\" ]\n\t\tep1_s2 -> ep1_s3 [ arrowhead=tee label=\"\" ]\n\t\tep1_s3 -> ep1_s4 [ arrowhead=tee label=\"\" ]\n\t\tep1_s5 -> ep1_s6 [ arrowhead=tee label=\"\" ]\n\t\tep1_s4 -> ep1_s5 [ arrowhead=tee label=\"accept\" ]\n\t\tep1_s4 -> ep1_s7 [ arrowhead=tee label=\"reject\" ]\n\t\tep1_s6 -> ep1_s8 [ arrowhead=tee label=\"\" ]\n\t\tep1_s7 -> ep1_s8 [ arrowhead=tee label=\"\" ]\n\t\tep1_s8 -> ep1_s9 [ arrowhead=tee label=\"\" ]\n\t\tep1_s9 -> ep1_s10 [ arrowhead=tee label=\"\" ]\n\t\tep1_e6 -> ep1_s4 [ label=\"condition\" ]\n\t\tep1_e8 -> ep1_s10 [ label=\"value\" ]\n\t\tep1_s1 -> ep1_e3 [ style=dotted ]\n\t\tep1_s2 -> ep1_e4 [ style=dotted ]\n\t\tep1_s3 -> ep1_e6 [ style=dotted ]\n\t\tep1_s9 -> ep1_e7 [ style=dotted ]\n\t\tep1_s9 -> ep1_e8 [ style=dotted ]\n\t}\n\tsubgraph cluster_ep2 {\n\t\tlabel=\"Fragment/'fs_extra'\"\n\t\tnode [ style=filled ]\n\t\tep2_e0 [ fillcolor=\"#ffffb3\" label=\"[0] Literal\" ]\n\t\tep2_e1 [ fillcolor=\"#ffffb3\" label=\"[1] Literal\" ]\n\t\tep2_e2 [ fillcolor=\"#ffffb3\" label=\"[2] Literal\" ]\n\t\tep2_e3 [ fillcolor=\"#ffffb3\" label=\"[3] Literal\" ]\n\t\tep2_e4 [ fillcolor=\"#bebada\" label=\"[4] Compose\" ]\n\t\t{ ep2_e0 ep2_e1 ep2_e2 ep2_e3 } -> ep2_e4\n\t\tep2_s0 [ shape=square label=\"Root\" ]\n\t\tep2_s1 [ shape=square label=\"Emit\" ]\n\t\tep2_s2 [ shape=square label=\"Return\" ]\n\t\tep2_s0 -> ep2_s1 [ arrowhead=tee label=\"\" ]\n\t\tep2_s1 -> ep2_s2 [ arrowhead=tee label=\"\" ]\n\t\tep2_e4 -> ep2_s2 [ label=\"value\" ]\n\t\tep2_s1 -> ep2_e4 [ style=dotted ]\n\t}\n}\n"
  },
  {
    "path": "naga/tests/out/glsl/glsl-variations.frag.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nuniform highp samplerCube _group_0_binding_0_fs;\n\n\nvoid main_1() {\n    ivec2 sizeCube = ivec2(0);\n    float a = 1.0;\n    sizeCube = ivec2(uvec2(textureSize(_group_0_binding_0_fs, 0).xy));\n    return;\n}\n\nvoid main() {\n    main_1();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-barrier.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid function() {\n    memoryBarrierShared();\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierBuffer();\n    memoryBarrierImage();\n    memoryBarrierBuffer();\n    memoryBarrierImage();\n    barrier();\n    memoryBarrierBuffer();\n    memoryBarrierShared();\n    memoryBarrierImage();\n    memoryBarrierBuffer();\n    memoryBarrierShared();\n    memoryBarrierImage();\n    barrier();\n    return;\n}\n\nvoid main() {\n    function();\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-do-while.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\n\nvoid f_u0028_b1_u003b(inout bool cond) {\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            bool _e1 = cond;\n            if (!(_e1)) {\n                break;\n            }\n        }\n        loop_init = false;\n        continue;\n    }\n    return;\n}\n\nvoid main_1() {\n    bool param = false;\n    param = false;\n    f_u0028_b1_u003b(param);\n    return;\n}\n\nvoid main() {\n    main_1();\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-quad-vert.main.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct gen_gl_PerVertex {\n    vec4 gen_gl_Position;\n    float gen_gl_PointSize;\n    float gen_gl_ClipDistance[1];\n    float gen_gl_CullDistance[1];\n};\nstruct VertexOutput {\n    vec2 member;\n    vec4 gen_gl_Position;\n};\nvec2 v_uv = vec2(0.0);\n\nvec2 a_uv_1 = vec2(0.0);\n\ngen_gl_PerVertex unnamed = gen_gl_PerVertex(vec4(0.0, 0.0, 0.0, 1.0), 1.0, float[1](0.0), float[1](0.0));\n\nvec2 a_pos_1 = vec2(0.0);\n\nlayout(location = 1) in vec2 _p2vs_location1;\nlayout(location = 0) in vec2 _p2vs_location0;\nlayout(location = 0) smooth out vec2 _vs2fs_location0;\n\nvoid main_1() {\n    vec2 _e6 = a_uv_1;\n    v_uv = _e6;\n    vec2 _e7 = a_pos_1;\n    unnamed.gen_gl_Position = vec4(_e7.x, _e7.y, 0.0, 1.0);\n    return;\n}\n\nvoid main() {\n    vec2 a_uv = _p2vs_location1;\n    vec2 a_pos = _p2vs_location0;\n    a_uv_1 = a_uv;\n    a_pos_1 = a_pos;\n    main_1();\n    vec2 _e7 = v_uv;\n    vec4 _e8 = unnamed.gen_gl_Position;\n    VertexOutput _tmp_return = VertexOutput(_e7, _e8);\n    _vs2fs_location0 = _tmp_return.member;\n    gl_Position = _tmp_return.gen_gl_Position;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-spec-constants-issue-5598.fragment.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nvec4 global = vec4(0.0);\n\nvec4 global_1 = vec4(0.0);\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid function() {\n    vec2 phi_49_ = vec2(0.0);\n    vec4 _e7 = global;\n    if (false) {\n        phi_49_ = vec2((_e7.x * 0.5), (_e7.y * 0.5));\n    } else {\n        phi_49_ = vec2((_e7.x * 0.25), (_e7.y * 0.25));\n    }\n    vec2 _e20 = phi_49_;\n    global_1[0u] = _e20.x;\n    global_1[1u] = _e20.y;\n    return;\n}\n\nvoid main() {\n    vec4 param = gl_FragCoord;\n    global = param;\n    function();\n    vec4 _e3 = global_1;\n    _fs2p_location0 = _e3;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-spec-constants-issue-5598.vertex.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nuint global_2 = 0u;\n\nvec4 global_3 = vec4(0.0, 0.0, 0.0, 1.0);\n\ninvariant gl_Position;\n\nvoid function_1() {\n    vec4 local[6] = vec4[6](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0));\n    uint _e5 = global_2;\n    local = vec4[6](vec4(-1.0, -1.0, 0.0, 1.0), vec4(1.0, -1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), vec4(-1.0, 1.0, 0.0, 1.0), vec4(-1.0, -1.0, 0.0, 1.0));\n    if ((_e5 < 6u)) {\n        vec4 _e8 = local[_e5];\n        global_3 = _e8;\n    }\n    return;\n}\n\nvoid main() {\n    uint param_1 = uint(gl_VertexID);\n    global_2 = param_1;\n    function_1();\n    float _e4 = global_3.y;\n    global_3.y = -(_e4);\n    vec4 _e6 = global_3;\n    gl_Position = _e6;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-subgroup-barrier.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid function() {\n    subgroupMemoryBarrier();\n    subgroupMemoryBarrier();\n    barrier();\n    return;\n}\n\nvoid main() {\n    function();\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-subgroup-operations-s.main.Compute.glsl",
    "content": "#version 430 core\n#extension GL_ARB_compute_shader : require\n#extension GL_KHR_shader_subgroup_basic : require\n#extension GL_KHR_shader_subgroup_vote : require\n#extension GL_KHR_shader_subgroup_arithmetic : require\n#extension GL_KHR_shader_subgroup_ballot : require\n#extension GL_KHR_shader_subgroup_shuffle : require\n#extension GL_KHR_shader_subgroup_shuffle_relative : require\n#extension GL_KHR_shader_subgroup_quad : require\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nuint global = 0u;\n\nuint global_1 = 0u;\n\nuint global_2 = 0u;\n\nuint global_3 = 0u;\n\n\nvoid function() {\n    uint _e5 = global_2;\n    uint _e6 = global_3;\n    barrier();\n    uvec4 _e9 = subgroupBallot(((_e6 & 1u) == 1u));\n    uvec4 _e10 = subgroupBallot(true);\n    bool _e12 = subgroupAll((_e6 != 0u));\n    bool _e14 = subgroupAny((_e6 == 0u));\n    uint _e15 = subgroupAdd(_e6);\n    uint _e16 = subgroupMul(_e6);\n    uint _e17 = subgroupMin(_e6);\n    uint _e18 = subgroupMax(_e6);\n    uint _e19 = subgroupAnd(_e6);\n    uint _e20 = subgroupOr(_e6);\n    uint _e21 = subgroupXor(_e6);\n    uint _e22 = subgroupExclusiveAdd(_e6);\n    uint _e23 = subgroupExclusiveMul(_e6);\n    uint _e24 = subgroupInclusiveAdd(_e6);\n    uint _e25 = subgroupInclusiveMul(_e6);\n    uint _e26 = subgroupBroadcastFirst(_e6);\n    uint _e27 = subgroupBroadcast(_e6, 4u);\n    uint _e30 = subgroupShuffle(_e6, ((_e5 - 1u) - _e6));\n    uint _e31 = subgroupShuffleDown(_e6, 1u);\n    uint _e32 = subgroupShuffleUp(_e6, 1u);\n    uint _e34 = subgroupShuffleXor(_e6, (_e5 - 1u));\n    return;\n}\n\nvoid main() {\n    uint param = gl_NumSubgroups;\n    uint param_1 = gl_SubgroupID;\n    uint param_2 = gl_SubgroupSize;\n    uint param_3 = gl_SubgroupInvocationID;\n    global = param;\n    global_1 = param_1;\n    global_2 = param_2;\n    global_3 = param_3;\n    function();\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/spv-unnamed-gl-per-vertex.main.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct type_4 {\n    vec4 member;\n    float member_1;\n    float member_2[1];\n    float member_3[1];\n};\ntype_4 global = type_4(vec4(0.0, 0.0, 0.0, 1.0), 1.0, float[1](0.0), float[1](0.0));\n\nint global_1 = 0;\n\n\nvoid function() {\n    int _e9 = global_1;\n    global.member = vec4(((_e9 == 0) ? -4.0 : 1.0), ((_e9 == 2) ? 4.0 : -1.0), 0.0, 1.0);\n    return;\n}\n\nvoid main() {\n    uint param = uint(gl_VertexID);\n    global_1 = int(param);\n    function();\n    float _e6 = global.member.y;\n    global.member.y = -(_e6);\n    vec4 _e8 = global.member;\n    gl_Position = _e8;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-6438-conflicting-idents.fs.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct OurVertexShaderOutput {\n    vec4 position;\n    vec2 texcoord;\n};\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    _fs2p_location0 = vec4(1.0, 0.0, 0.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-6438-conflicting-idents.vs.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct OurVertexShaderOutput {\n    vec4 position;\n    vec2 texcoord;\n};\nlayout(location = 0) in vec2 _p2vs_location0;\nlayout(location = 0) smooth out vec2 _vs2fs_location0;\n\nvoid main() {\n    vec2 xy = _p2vs_location0;\n    OurVertexShaderOutput vsOutput = OurVertexShaderOutput(vec4(0.0), vec2(0.0));\n    vsOutput.position = vec4(xy, 0.0, 1.0);\n    OurVertexShaderOutput _e6 = vsOutput;\n    gl_Position = _e6.position;\n    _vs2fs_location0 = _e6.texcoord;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-6772-unpack-expr-accesses.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    int phony = ivec4(bitfieldExtract(int(12u), 0, 8), bitfieldExtract(int(12u), 8, 8), bitfieldExtract(int(12u), 16, 8), bitfieldExtract(int(12u), 24, 8))[2];\n    uint phony_1 = uvec4(bitfieldExtract(12u, 0, 8), bitfieldExtract(12u, 8, 8), bitfieldExtract(12u, 16, 8), bitfieldExtract(12u, 24, 8)).y;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-7995-unicode-idents.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nlayout(std430) readonly buffer type_block_0Compute { float _group_0_binding_0_cs; };\n\n\nfloat compute() {\n    float _e1 = _group_0_binding_0_cs;\n    float u03b8_2_ = (_e1 + 9001.0);\n    return u03b8_2_;\n}\n\nvoid main() {\n    float _e0 = compute();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-8820-multiple-local-invocation-index-id.compute1.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct Input {\n    uvec3 local_invocation_id;\n    uint local_invocation_index;\n};\nshared uint wg_var;\n\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        wg_var = 0u;\n    }\n    memoryBarrierShared();\n    barrier();\n    Input input_ = Input(gl_LocalInvocationID, gl_LocalInvocationIndex);\n    wg_var = (input_.local_invocation_index * 2u);\n    uint _e6 = wg_var;\n    wg_var = (_e6 + input_.local_invocation_id.x);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-builtins.f.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    int clamp_aiaiai = 1;\n    float clamp_aiaiaf = 1.0;\n    int clamp_aiaii = 1;\n    float clamp_aiaif = 1.0;\n    float clamp_aiafai = 1.0;\n    float clamp_aiafaf = 1.0;\n    float clamp_aiaff = 1.0;\n    int clamp_aiiai = 1;\n    int clamp_aiii = 1;\n    float clamp_aifai = 1.0;\n    float clamp_aifaf = 1.0;\n    float clamp_aiff = 1.0;\n    float clamp_afaiai = 1.0;\n    float clamp_afaiaf = 1.0;\n    float clamp_afaif = 1.0;\n    float clamp_afafai = 1.0;\n    float clamp_afafaf = 1.0;\n    float clamp_afaff = 1.0;\n    float clamp_affai = 1.0;\n    float clamp_affaf = 1.0;\n    float clamp_afff = 1.0;\n    int clamp_iaiai = 1;\n    int clamp_iaii = 1;\n    int clamp_iiai = 1;\n    int clamp_iii = 1;\n    float clamp_faiai = 1.0;\n    float clamp_faiaf = 1.0;\n    float clamp_faif = 1.0;\n    float clamp_fafai = 1.0;\n    float clamp_fafaf = 1.0;\n    float clamp_faff = 1.0;\n    float clamp_ffai = 1.0;\n    float clamp_ffaf = 1.0;\n    float clamp_fff = 1.0;\n    int min_aiai = 1;\n    float min_aiaf = 1.0;\n    int min_aii = 1;\n    float min_aif = 1.0;\n    float min_afai = 1.0;\n    float min_afaf = 1.0;\n    float min_aff = 1.0;\n    int min_iai = 1;\n    int min_ii = 1;\n    float min_fai = 1.0;\n    float min_faf = 1.0;\n    float min_ff = 1.0;\n    float pow_aiai = 1.0;\n    float pow_aiaf = 1.0;\n    float pow_aif = 1.0;\n    float pow_afai = 1.0;\n    float pow_afaf = 1.0;\n    float pow_aff = 1.0;\n    float pow_fai = 1.0;\n    float pow_faf = 1.0;\n    float pow_ff = 1.0;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-function-calls.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid func_f(float a) {\n    return;\n}\n\nvoid func_i(int a_1) {\n    return;\n}\n\nvoid func_u(uint a_2) {\n    return;\n}\n\nvoid func_vf(vec2 a_3) {\n    return;\n}\n\nvoid func_vi(ivec2 a_4) {\n    return;\n}\n\nvoid func_vu(uvec2 a_5) {\n    return;\n}\n\nvoid func_mf(mat2x2 a_6) {\n    return;\n}\n\nvoid func_af(float a_7[2]) {\n    return;\n}\n\nvoid func_ai(int a_8[2]) {\n    return;\n}\n\nvoid func_au(uint a_9[2]) {\n    return;\n}\n\nvoid func_f_i(float a_10, int b) {\n    return;\n}\n\nvoid main() {\n    func_f(0.0);\n    func_f(0.0);\n    func_i(0);\n    func_u(0u);\n    func_f(0.0);\n    func_f(0.0);\n    func_i(0);\n    func_u(0u);\n    func_vf(vec2(0.0));\n    func_vf(vec2(0.0));\n    func_vi(ivec2(0));\n    func_vu(uvec2(0u));\n    func_vf(vec2(0.0));\n    func_vf(vec2(0.0));\n    func_vi(ivec2(0));\n    func_vu(uvec2(0u));\n    func_mf(mat2x2(vec2(0.0), vec2(0.0)));\n    func_mf(mat2x2(vec2(0.0), vec2(0.0)));\n    func_mf(mat2x2(vec2(0.0), vec2(0.0)));\n    func_af(float[2](0.0, 0.0));\n    func_af(float[2](0.0, 0.0));\n    func_ai(int[2](0, 0));\n    func_au(uint[2](0u, 0u));\n    func_af(float[2](0.0, 0.0));\n    func_af(float[2](0.0, 0.0));\n    func_ai(int[2](0, 0));\n    func_au(uint[2](0u, 0u));\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-let.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid all_constant_arguments() {\n    ivec2 xvipaiai = ivec2(42, 43);\n    uvec2 xvupaiai = uvec2(44u, 45u);\n    vec2 xvfpaiai = vec2(46.0, 47.0);\n    vec2 xvfpafaf = vec2(48.0, 49.0);\n    vec2 xvfpaiaf = vec2(48.0, 49.0);\n    uvec2 xvupuai = uvec2(42u, 43u);\n    uvec2 xvupaiu = uvec2(42u, 43u);\n    uvec2 xvuuai = uvec2(42u, 43u);\n    uvec2 xvuaiu = uvec2(42u, 43u);\n    ivec2 xvip = ivec2(0, 0);\n    uvec2 xvup = uvec2(0u, 0u);\n    vec2 xvfp = vec2(0.0, 0.0);\n    mat2x2 xmfp = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    mat2x2 xmfpaiaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpafaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiafaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiafai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiaiaf = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfp_faiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpai_faiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiai_fai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiai_f = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    ivec2 xvispai = ivec2(1);\n    vec2 xvfspaf = vec2(1.0);\n    ivec2 xvis_ai = ivec2(1);\n    uvec2 xvus_ai = uvec2(1u);\n    vec2 xvfs_ai = vec2(1.0);\n    vec2 xvfs_af = vec2(1.0);\n    float xafafaf[2] = float[2](1.0, 2.0);\n    float xaf_faf[2] = float[2](1.0, 2.0);\n    float xafaf_f[2] = float[2](1.0, 2.0);\n    float xafaiai[2] = float[2](1.0, 2.0);\n    int xai_iai[2] = int[2](1, 2);\n    int xaiai_i[2] = int[2](1, 2);\n    int xaipaiai[2] = int[2](1, 2);\n    float xafpaiai[2] = float[2](1.0, 2.0);\n    float xafpaiaf[2] = float[2](1.0, 2.0);\n    float xafpafai[2] = float[2](1.0, 2.0);\n    float xafpafaf[2] = float[2](1.0, 2.0);\n    ivec3 xavipai[1] = ivec3[1](ivec3(1));\n    vec3 xavfpai[1] = vec3[1](vec3(1.0));\n    vec3 xavfpaf[1] = vec3[1](vec3(1.0));\n    ivec2 xvisai = ivec2(1);\n    uvec2 xvusai = uvec2(1u);\n    vec2 xvfsai = vec2(1.0);\n    vec2 xvfsaf = vec2(1.0);\n    int iaipaiai[2] = int[2](1, 2);\n    float iafpaiaf[2] = float[2](1.0, 2.0);\n    float iafpafai[2] = float[2](1.0, 2.0);\n    float iafpafaf[2] = float[2](1.0, 2.0);\n    return;\n}\n\nvoid mixed_constant_and_runtime_arguments() {\n    uint u = 0u;\n    int i = 0;\n    float f = 0.0;\n    uint _e3 = u;\n    uvec2 xvupuai_1 = uvec2(_e3, 43u);\n    uint _e6 = u;\n    uvec2 xvupaiu_1 = uvec2(42u, _e6);\n    float _e9 = f;\n    vec2 xvfpfai = vec2(_e9, 47.0);\n    float _e12 = f;\n    vec2 xvfpfaf = vec2(_e12, 49.0);\n    uint _e15 = u;\n    uvec2 xvuuai_1 = uvec2(_e15, 43u);\n    uint _e18 = u;\n    uvec2 xvuaiu_1 = uvec2(42u, _e18);\n    float _e21 = f;\n    mat2x2 xmfp_faiaiai_1 = mat2x2(vec2(_e21, 2.0), vec2(3.0, 4.0));\n    float _e28 = f;\n    mat2x2 xmfpai_faiai_1 = mat2x2(vec2(1.0, _e28), vec2(3.0, 4.0));\n    float _e35 = f;\n    mat2x2 xmfpaiai_fai_1 = mat2x2(vec2(1.0, 2.0), vec2(_e35, 4.0));\n    float _e42 = f;\n    mat2x2 xmfpaiaiai_f_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, _e42));\n    float _e49 = f;\n    float xaf_faf_1[2] = float[2](_e49, 2.0);\n    float _e52 = f;\n    float xafaf_f_1[2] = float[2](1.0, _e52);\n    float _e55 = f;\n    float xaf_fai[2] = float[2](_e55, 2.0);\n    float _e58 = f;\n    float xafai_f[2] = float[2](1.0, _e58);\n    int _e61 = i;\n    int xai_iai_1[2] = int[2](_e61, 2);\n    int _e64 = i;\n    int xaiai_i_1[2] = int[2](1, _e64);\n    float _e67 = f;\n    float xafp_faf[2] = float[2](_e67, 2.0);\n    float _e70 = f;\n    float xafpaf_f[2] = float[2](1.0, _e70);\n    float _e73 = f;\n    float xafp_fai[2] = float[2](_e73, 2.0);\n    float _e76 = f;\n    float xafpai_f[2] = float[2](1.0, _e76);\n    int _e79 = i;\n    int xaip_iai[2] = int[2](_e79, 2);\n    int _e82 = i;\n    int xaipai_i[2] = int[2](1, _e82);\n    int _e85 = i;\n    ivec2 xvisi = ivec2(_e85);\n    uint _e87 = u;\n    uvec2 xvusu = uvec2(_e87);\n    float _e89 = f;\n    vec2 xvfsf = vec2(_e89);\n    return;\n}\n\nvoid main() {\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-operators.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst float plus_fafaf_1 = 3.0;\nconst float plus_fafai_1 = 3.0;\nconst float plus_faf_f_1 = 3.0;\nconst float plus_faiaf_1 = 3.0;\nconst float plus_faiai_1 = 3.0;\nconst float plus_fai_f_1 = 3.0;\nconst float plus_f_faf_1 = 3.0;\nconst float plus_f_fai_1 = 3.0;\nconst float plus_f_f_f_1 = 3.0;\nconst int plus_iaiai_1 = 3;\nconst int plus_iai_i_1 = 3;\nconst int plus_i_iai_1 = 3;\nconst int plus_i_i_i_1 = 3;\nconst uint plus_uaiai_1 = 3u;\nconst uint plus_uai_u_1 = 3u;\nconst uint plus_u_uai_1 = 3u;\nconst uint plus_u_u_u_1 = 3u;\nconst uint bitflip_u_u = 0u;\nconst uint bitflip_uai = 0u;\nconst int least_i32_ = -2147483648;\nconst float least_f32_ = -3.4028235e38;\nconst int shl_iaiai = 4;\nconst int shl_iai_u_1 = 4;\nconst uint shl_uaiai = 4u;\nconst uint shl_uai_u = 4u;\nconst int shr_iaiai = 0;\nconst int shr_iai_u_1 = 0;\nconst uint shr_uaiai = 0u;\nconst uint shr_uai_u = 0u;\nconst int wgpu_4492_ = -2147483648;\n\nshared uint a[64];\n\n\nvoid runtime_values() {\n    float f = 42.0;\n    int i = 43;\n    uint u = 44u;\n    float plus_fafaf = 3.0;\n    float plus_fafai = 3.0;\n    float plus_faf_f = 0.0;\n    float plus_faiaf = 3.0;\n    float plus_faiai = 3.0;\n    float plus_fai_f = 0.0;\n    float plus_f_faf = 0.0;\n    float plus_f_fai = 0.0;\n    float plus_f_f_f = 0.0;\n    int plus_iaiai = 3;\n    int plus_iai_i = 0;\n    int plus_i_iai = 0;\n    int plus_i_i_i = 0;\n    uint plus_uaiai = 3u;\n    uint plus_uai_u = 0u;\n    uint plus_u_uai = 0u;\n    uint plus_u_u_u = 0u;\n    int shl_iai_u = 0;\n    int shr_iai_u = 0;\n    float _e8 = f;\n    plus_faf_f = (1.0 + _e8);\n    float _e14 = f;\n    plus_fai_f = (1.0 + _e14);\n    float _e18 = f;\n    plus_f_faf = (_e18 + 2.0);\n    float _e22 = f;\n    plus_f_fai = (_e22 + 2.0);\n    float _e26 = f;\n    float _e27 = f;\n    plus_f_f_f = (_e26 + _e27);\n    int _e31 = i;\n    plus_iai_i = (1 + _e31);\n    int _e35 = i;\n    plus_i_iai = (_e35 + 2);\n    int _e39 = i;\n    int _e40 = i;\n    plus_i_i_i = (_e39 + _e40);\n    uint _e44 = u;\n    plus_uai_u = (1u + _e44);\n    uint _e48 = u;\n    plus_u_uai = (_e48 + 2u);\n    uint _e52 = u;\n    uint _e53 = u;\n    plus_u_u_u = (_e52 + _e53);\n    uint _e56 = u;\n    shl_iai_u = (1 << _e56);\n    uint _e60 = u;\n    shr_iai_u = (1 << _e60);\n    return;\n}\n\nvoid wgpu_4445_() {\n    return;\n}\n\nvoid wgpu_4435_() {\n    uint y = a[(1 - 1)];\n    return;\n}\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        a = uint[64](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u);\n    }\n    memoryBarrierShared();\n    barrier();\n    runtime_values();\n    wgpu_4445_();\n    wgpu_4435_();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-return.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nint return_i32_ai() {\n    return 1;\n}\n\nuint return_u32_ai() {\n    return 1u;\n}\n\nfloat return_f32_ai() {\n    return 1.0;\n}\n\nfloat return_f32_af() {\n    return 1.0;\n}\n\nvec2 return_vec2f32_ai() {\n    return vec2(1.0);\n}\n\nfloat[4] return_arrf32_ai() {\n    return float[4](1.0, 1.0, 1.0, 1.0);\n}\n\nfloat return_const_f32_const_ai() {\n    return 1.0;\n}\n\nvec2 return_vec2f32_const_ai() {\n    return vec2(1.0);\n}\n\nvoid main() {\n    int _e0 = return_i32_ai();\n    uint _e1 = return_u32_ai();\n    float _e2 = return_f32_ai();\n    float _e3 = return_f32_af();\n    vec2 _e4 = return_vec2f32_ai();\n    float _e5[4] = return_arrf32_ai();\n    float _e6 = return_const_f32_const_ai();\n    vec2 _e7 = return_vec2f32_const_ai();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-abstract-types-var.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nivec2 xvipaiai_1 = ivec2(42, 43);\n\nuvec2 xvupaiai_1 = uvec2(44u, 45u);\n\nvec2 xvfpaiai_1 = vec2(46.0, 47.0);\n\nvec2 xvfpafaf_1 = vec2(48.0, 49.0);\n\nvec2 xvfpaiaf_1 = vec2(48.0, 49.0);\n\nuvec2 xvupuai_2 = uvec2(42u, 43u);\n\nuvec2 xvupaiu_2 = uvec2(42u, 43u);\n\nuvec2 xvuuai_2 = uvec2(42u, 43u);\n\nuvec2 xvuaiu_2 = uvec2(42u, 43u);\n\nivec2 xvip_1 = ivec2(0, 0);\n\nuvec2 xvup_1 = uvec2(0u, 0u);\n\nvec2 xvfp_1 = vec2(0.0, 0.0);\n\nmat2x2 xmfp_1 = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n\nmat2x2 xmfpaiaiaiai_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n\nmat2x2 xmfpafaiaiai_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n\nmat2x2 xmfpaiafaiai_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n\nmat2x2 xmfpaiaiafai_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n\nmat2x2 xmfpaiaiaiaf_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n\nivec2 xvispai_1 = ivec2(1);\n\nvec2 xvfspaf_1 = vec2(1.0);\n\nivec2 xvis_ai_1 = ivec2(1);\n\nuvec2 xvus_ai_1 = uvec2(1u);\n\nvec2 xvfs_ai_1 = vec2(1.0);\n\nvec2 xvfs_af_1 = vec2(1.0);\n\nfloat xafafaf_1[2] = float[2](1.0, 2.0);\n\nfloat xafaiai_1[2] = float[2](1.0, 2.0);\n\nint xaipaiai_1[2] = int[2](1, 2);\n\nuint xaupaiai[2] = uint[2](1u, 2u);\n\nfloat xafpaiaf_1[2] = float[2](1.0, 2.0);\n\nfloat xafpafai_1[2] = float[2](1.0, 2.0);\n\nfloat xafpafaf_1[2] = float[2](1.0, 2.0);\n\nivec3 xavipai_1[1] = ivec3[1](ivec3(1));\n\nvec3 xavfpai_1[1] = vec3[1](vec3(1.0));\n\nvec3 xavfpaf_1[1] = vec3[1](vec3(1.0));\n\nivec2 xvisai_1 = ivec2(1);\n\nuvec2 xvusai_1 = uvec2(1u);\n\nvec2 xvfsai_1 = vec2(1.0);\n\nvec2 xvfsaf_1 = vec2(1.0);\n\nivec2 ivispai = ivec2(1);\n\nvec2 ivfspaf = vec2(1.0);\n\nivec2 ivis_ai = ivec2(1);\n\nuvec2 ivus_ai = uvec2(1u);\n\nvec2 ivfs_ai = vec2(1.0);\n\nvec2 ivfs_af = vec2(1.0);\n\nfloat iafafaf[2] = float[2](1.0, 2.0);\n\nfloat iafaiai[2] = float[2](1.0, 2.0);\n\nint iaipaiai_1[2] = int[2](1, 2);\n\nfloat iafpafaf_1[2] = float[2](1.0, 2.0);\n\nfloat iafpaiaf_1[2] = float[2](1.0, 2.0);\n\nfloat iafpafai_1[2] = float[2](1.0, 2.0);\n\nivec3 iavipai[1] = ivec3[1](ivec3(1));\n\nivec3 iavfpai[1] = ivec3[1](ivec3(1));\n\nvec3 iavfpaf[1] = vec3[1](vec3(1.0));\n\n\nvoid globals() {\n    ivec2 phony = xvipaiai_1;\n    uvec2 phony_1 = xvupaiai_1;\n    vec2 phony_2 = xvfpaiai_1;\n    vec2 phony_3 = xvfpafaf_1;\n    vec2 phony_4 = xvfpaiaf_1;\n    uvec2 phony_5 = xvupuai_2;\n    uvec2 phony_6 = xvupaiu_2;\n    uvec2 phony_7 = xvuuai_2;\n    uvec2 phony_8 = xvuaiu_2;\n    ivec2 phony_9 = xvip_1;\n    uvec2 phony_10 = xvup_1;\n    vec2 phony_11 = xvfp_1;\n    mat2x2 phony_12 = xmfp_1;\n    mat2x2 phony_13 = xmfpaiaiaiai_1;\n    mat2x2 phony_14 = xmfpafaiaiai_1;\n    mat2x2 phony_15 = xmfpaiafaiai_1;\n    mat2x2 phony_16 = xmfpaiaiafai_1;\n    mat2x2 phony_17 = xmfpaiaiaiaf_1;\n    ivec2 phony_18 = xvispai_1;\n    vec2 phony_19 = xvfspaf_1;\n    ivec2 phony_20 = xvis_ai_1;\n    uvec2 phony_21 = xvus_ai_1;\n    vec2 phony_22 = xvfs_ai_1;\n    vec2 phony_23 = xvfs_af_1;\n    float phony_24[2] = xafafaf_1;\n    float phony_25[2] = xafaiai_1;\n    int phony_26[2] = xaipaiai_1;\n    uint phony_27[2] = xaupaiai;\n    float phony_28[2] = xafpaiaf_1;\n    float phony_29[2] = xafpafai_1;\n    float phony_30[2] = xafpafaf_1;\n    ivec3 phony_31[1] = xavipai_1;\n    vec3 phony_32[1] = xavfpai_1;\n    vec3 phony_33[1] = xavfpaf_1;\n    ivec2 phony_34 = xvisai_1;\n    uvec2 phony_35 = xvusai_1;\n    vec2 phony_36 = xvfsai_1;\n    vec2 phony_37 = xvfsaf_1;\n    ivec2 phony_38 = ivispai;\n    vec2 phony_39 = ivfspaf;\n    ivec2 phony_40 = ivis_ai;\n    uvec2 phony_41 = ivus_ai;\n    vec2 phony_42 = ivfs_ai;\n    vec2 phony_43 = ivfs_af;\n    float phony_44[2] = iafafaf;\n    float phony_45[2] = iafaiai;\n    int phony_46[2] = iaipaiai_1;\n    float phony_47[2] = iafpafaf_1;\n    float phony_48[2] = iafpaiaf_1;\n    float phony_49[2] = iafpafai_1;\n    ivec3 phony_50[1] = iavipai;\n    ivec3 phony_51[1] = iavfpai;\n    vec3 phony_52[1] = iavfpaf;\n    return;\n}\n\nvoid all_constant_arguments() {\n    ivec2 xvipaiai = ivec2(42, 43);\n    uvec2 xvupaiai = uvec2(44u, 45u);\n    vec2 xvfpaiai = vec2(46.0, 47.0);\n    vec2 xvfpafaf = vec2(48.0, 49.0);\n    vec2 xvfpaiaf = vec2(48.0, 49.0);\n    uvec2 xvupuai = uvec2(42u, 43u);\n    uvec2 xvupaiu = uvec2(42u, 43u);\n    uvec2 xvuuai = uvec2(42u, 43u);\n    uvec2 xvuaiu = uvec2(42u, 43u);\n    ivec2 xvip = ivec2(0, 0);\n    uvec2 xvup = uvec2(0u, 0u);\n    vec2 xvfp = vec2(0.0, 0.0);\n    mat2x2 xmfp = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    mat2x2 xmfpaiaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpafaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiafaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiafai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiaiaf = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfp_faiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpai_faiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiai_fai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    mat2x2 xmfpaiaiai_f = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    ivec2 xvispai = ivec2(1);\n    vec2 xvfspaf = vec2(1.0);\n    ivec2 xvis_ai = ivec2(1);\n    uvec2 xvus_ai = uvec2(1u);\n    vec2 xvfs_ai = vec2(1.0);\n    vec2 xvfs_af = vec2(1.0);\n    float xafafaf[2] = float[2](1.0, 2.0);\n    float xaf_faf[2] = float[2](1.0, 2.0);\n    float xafaf_f[2] = float[2](1.0, 2.0);\n    float xafaiai[2] = float[2](1.0, 2.0);\n    int xai_iai[2] = int[2](1, 2);\n    int xaiai_i[2] = int[2](1, 2);\n    int xaipaiai[2] = int[2](1, 2);\n    float xafpaiai[2] = float[2](1.0, 2.0);\n    float xafpaiaf[2] = float[2](1.0, 2.0);\n    float xafpafai[2] = float[2](1.0, 2.0);\n    float xafpafaf[2] = float[2](1.0, 2.0);\n    ivec3 xavipai[1] = ivec3[1](ivec3(1));\n    vec3 xavfpai[1] = vec3[1](vec3(1.0));\n    vec3 xavfpaf[1] = vec3[1](vec3(1.0));\n    ivec2 xvisai = ivec2(1);\n    uvec2 xvusai = uvec2(1u);\n    vec2 xvfsai = vec2(1.0);\n    vec2 xvfsaf = vec2(1.0);\n    int iaipaiai[2] = int[2](1, 2);\n    float iafpaiaf[2] = float[2](1.0, 2.0);\n    float iafpafai[2] = float[2](1.0, 2.0);\n    float iafpafaf[2] = float[2](1.0, 2.0);\n    xvipaiai = ivec2(42, 43);\n    xvupaiai = uvec2(44u, 45u);\n    xvfpaiai = vec2(46.0, 47.0);\n    xvfpafaf = vec2(48.0, 49.0);\n    xvfpaiaf = vec2(48.0, 49.0);\n    xvupuai = uvec2(42u, 43u);\n    xvupaiu = uvec2(42u, 43u);\n    xvuuai = uvec2(42u, 43u);\n    xvuaiu = uvec2(42u, 43u);\n    xvip = ivec2(0, 0);\n    xvup = uvec2(0u, 0u);\n    xvfp = vec2(0.0, 0.0);\n    xmfp = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    xmfpaiaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpafaiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpaiafaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpaiaiafai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpaiaiaiaf = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfp_faiaiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpai_faiai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpaiai_fai = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xmfpaiaiai_f = mat2x2(vec2(1.0, 2.0), vec2(3.0, 4.0));\n    xvispai = ivec2(1);\n    xvfspaf = vec2(1.0);\n    xvis_ai = ivec2(1);\n    xvus_ai = uvec2(1u);\n    xvfs_ai = vec2(1.0);\n    xvfs_af = vec2(1.0);\n    xafafaf = float[2](1.0, 2.0);\n    xaf_faf = float[2](1.0, 2.0);\n    xafaf_f = float[2](1.0, 2.0);\n    xafaiai = float[2](1.0, 2.0);\n    xai_iai = int[2](1, 2);\n    xaiai_i = int[2](1, 2);\n    xaipaiai = int[2](1, 2);\n    xafpaiai = float[2](1.0, 2.0);\n    xafpaiaf = float[2](1.0, 2.0);\n    xafpafai = float[2](1.0, 2.0);\n    xafpafaf = float[2](1.0, 2.0);\n    xavipai = ivec3[1](ivec3(1));\n    xavfpai = vec3[1](vec3(1.0));\n    xavfpaf = vec3[1](vec3(1.0));\n    xvisai = ivec2(1);\n    xvusai = uvec2(1u);\n    xvfsai = vec2(1.0);\n    xvfsaf = vec2(1.0);\n    iaipaiai = int[2](1, 2);\n    iafpaiaf = float[2](1.0, 2.0);\n    iafpafai = float[2](1.0, 2.0);\n    iafpafaf = float[2](1.0, 2.0);\n    return;\n}\n\nvoid mixed_constant_and_runtime_arguments() {\n    uint u = 0u;\n    int i = 0;\n    float f = 0.0;\n    uvec2 xvupuai_1 = uvec2(0u);\n    uvec2 xvupaiu_1 = uvec2(0u);\n    vec2 xvfpfai = vec2(0.0);\n    vec2 xvfpfaf = vec2(0.0);\n    uvec2 xvuuai_1 = uvec2(0u);\n    uvec2 xvuaiu_1 = uvec2(0u);\n    mat2x2 xmfp_faiaiai_1 = mat2x2(0.0);\n    mat2x2 xmfpai_faiai_1 = mat2x2(0.0);\n    mat2x2 xmfpaiai_fai_1 = mat2x2(0.0);\n    mat2x2 xmfpaiaiai_f_1 = mat2x2(0.0);\n    float xaf_faf_1[2] = float[2](0.0, 0.0);\n    float xafaf_f_1[2] = float[2](0.0, 0.0);\n    float xaf_fai[2] = float[2](0.0, 0.0);\n    float xafai_f[2] = float[2](0.0, 0.0);\n    int xai_iai_1[2] = int[2](0, 0);\n    int xaiai_i_1[2] = int[2](0, 0);\n    float xafp_faf[2] = float[2](0.0, 0.0);\n    float xafpaf_f[2] = float[2](0.0, 0.0);\n    float xafp_fai[2] = float[2](0.0, 0.0);\n    float xafpai_f[2] = float[2](0.0, 0.0);\n    int xaip_iai[2] = int[2](0, 0);\n    int xaipai_i[2] = int[2](0, 0);\n    ivec2 xvisi = ivec2(0);\n    uvec2 xvusu = uvec2(0u);\n    vec2 xvfsf = vec2(0.0);\n    uint _e3 = u;\n    xvupuai_1 = uvec2(_e3, 43u);\n    uint _e7 = u;\n    xvupaiu_1 = uvec2(42u, _e7);\n    float _e11 = f;\n    xvfpfai = vec2(_e11, 47.0);\n    float _e15 = f;\n    xvfpfaf = vec2(_e15, 49.0);\n    uint _e19 = u;\n    xvuuai_1 = uvec2(_e19, 43u);\n    uint _e23 = u;\n    xvuaiu_1 = uvec2(42u, _e23);\n    float _e27 = f;\n    xmfp_faiaiai_1 = mat2x2(vec2(_e27, 2.0), vec2(3.0, 4.0));\n    float _e35 = f;\n    xmfpai_faiai_1 = mat2x2(vec2(1.0, _e35), vec2(3.0, 4.0));\n    float _e43 = f;\n    xmfpaiai_fai_1 = mat2x2(vec2(1.0, 2.0), vec2(_e43, 4.0));\n    float _e51 = f;\n    xmfpaiaiai_f_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, _e51));\n    float _e59 = f;\n    xaf_faf_1 = float[2](_e59, 2.0);\n    float _e63 = f;\n    xafaf_f_1 = float[2](1.0, _e63);\n    float _e67 = f;\n    xaf_fai = float[2](_e67, 2.0);\n    float _e71 = f;\n    xafai_f = float[2](1.0, _e71);\n    int _e75 = i;\n    xai_iai_1 = int[2](_e75, 2);\n    int _e79 = i;\n    xaiai_i_1 = int[2](1, _e79);\n    float _e83 = f;\n    xafp_faf = float[2](_e83, 2.0);\n    float _e87 = f;\n    xafpaf_f = float[2](1.0, _e87);\n    float _e91 = f;\n    xafp_fai = float[2](_e91, 2.0);\n    float _e95 = f;\n    xafpai_f = float[2](1.0, _e95);\n    int _e99 = i;\n    xaip_iai = int[2](_e99, 2);\n    int _e103 = i;\n    xaipai_i = int[2](1, _e103);\n    int _e107 = i;\n    xvisi = ivec2(_e107);\n    uint _e110 = u;\n    xvusu = uvec2(_e110);\n    float _e113 = f;\n    xvfsf = vec2(_e113);\n    uint _e116 = u;\n    xvupuai_1 = uvec2(_e116, 43u);\n    uint _e119 = u;\n    xvupaiu_1 = uvec2(42u, _e119);\n    uint _e122 = u;\n    xvuuai_1 = uvec2(_e122, 43u);\n    uint _e125 = u;\n    xvuaiu_1 = uvec2(42u, _e125);\n    float _e128 = f;\n    xmfp_faiaiai_1 = mat2x2(vec2(_e128, 2.0), vec2(3.0, 4.0));\n    float _e135 = f;\n    xmfpai_faiai_1 = mat2x2(vec2(1.0, _e135), vec2(3.0, 4.0));\n    float _e142 = f;\n    xmfpaiai_fai_1 = mat2x2(vec2(1.0, 2.0), vec2(_e142, 4.0));\n    float _e149 = f;\n    xmfpaiaiai_f_1 = mat2x2(vec2(1.0, 2.0), vec2(3.0, _e149));\n    float _e156 = f;\n    xaf_faf_1 = float[2](_e156, 2.0);\n    float _e159 = f;\n    xafaf_f_1 = float[2](1.0, _e159);\n    float _e162 = f;\n    xaf_fai = float[2](_e162, 2.0);\n    float _e165 = f;\n    xafai_f = float[2](1.0, _e165);\n    int _e168 = i;\n    xai_iai_1 = int[2](_e168, 2);\n    int _e171 = i;\n    xaiai_i_1 = int[2](1, _e171);\n    float _e174 = f;\n    xafp_faf = float[2](_e174, 2.0);\n    float _e177 = f;\n    xafpaf_f = float[2](1.0, _e177);\n    float _e180 = f;\n    xafp_fai = float[2](_e180, 2.0);\n    float _e183 = f;\n    xafpai_f = float[2](1.0, _e183);\n    int _e186 = i;\n    xaip_iai = int[2](_e186, 2);\n    int _e189 = i;\n    xaipai_i = int[2](1, _e189);\n    int _e192 = i;\n    xvisi = ivec2(_e192);\n    uint _e194 = u;\n    xvusu = uvec2(_e194);\n    float _e196 = f;\n    xvfsf = vec2(_e196);\n    return;\n}\n\nvoid main() {\n    globals();\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-access.foo_compute.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct GlobalConst {\n    uint a;\n    uvec3 b;\n    int c;\n};\nstruct AlignedWrapper {\n    int value;\n};\nstruct Baz {\n    mat3x2 m;\n};\nstruct MatCx2InArray {\n    mat4x2 am[2];\n};\nstruct AssignToMember {\n    uint x;\n};\nstruct S {\n    int m;\n};\nstruct Inner {\n    int delicious;\n};\nstruct Outer {\n    Inner om_nom_nom;\n    uint thing;\n};\n\nfloat read_from_private(inout float foo_1) {\n    float _e1 = foo_1;\n    return _e1;\n}\n\nfloat test_arr_as_arg(float a[5][10]) {\n    return a[4][9];\n}\n\nvoid assign_through_ptr_fn(inout uint p) {\n    p = 42u;\n    return;\n}\n\nvoid assign_array_through_ptr_fn(inout vec4 foo_2[2]) {\n    foo_2 = vec4[2](vec4(1.0), vec4(2.0));\n    return;\n}\n\nvoid assign_through_ptr() {\n    uint val = 33u;\n    vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0));\n    assign_through_ptr_fn(val);\n    assign_array_through_ptr_fn(arr);\n    return;\n}\n\nuint fetch_arg_ptr_member(inout AssignToMember p_1) {\n    uint _e2 = p_1.x;\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_member(inout AssignToMember p_2) {\n    p_2.x = 10u;\n    return;\n}\n\nuint fetch_arg_ptr_array_element(inout uint p_3[4]) {\n    uint _e2 = p_3[1];\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_array_element(inout uint p_4[4]) {\n    p_4[1] = 10u;\n    return;\n}\n\nvoid assign_to_ptr_components() {\n    AssignToMember s1_ = AssignToMember(0u);\n    uint a1_[4] = uint[4](0u, 0u, 0u, 0u);\n    assign_to_arg_ptr_member(s1_);\n    uint _e1 = fetch_arg_ptr_member(s1_);\n    assign_to_arg_ptr_array_element(a1_);\n    uint _e3 = fetch_arg_ptr_array_element(a1_);\n    return;\n}\n\nbool index_ptr(bool value) {\n    bool a_1[1] = bool[1](false);\n    a_1 = bool[1](value);\n    bool _e4 = a_1[0];\n    return _e4;\n}\n\nint member_ptr() {\n    S s = S(42);\n    int _e4 = s.m;\n    return _e4;\n}\n\nint let_members_of_members() {\n    Inner inner_1 = Outer(Inner(0), 0u).om_nom_nom;\n    int delishus_1 = inner_1.delicious;\n    if ((Outer(Inner(0), 0u).thing != uint(delishus_1))) {\n    }\n    return Outer(Inner(0), 0u).om_nom_nom.delicious;\n}\n\nint var_members_of_members() {\n    Outer thing = Outer(Inner(0), 0u);\n    Inner inner = Inner(0);\n    int delishus = 0;\n    Inner _e3 = thing.om_nom_nom;\n    inner = _e3;\n    int _e6 = inner.delicious;\n    delishus = _e6;\n    uint _e9 = thing.thing;\n    int _e10 = delishus;\n    if ((_e9 != uint(_e10))) {\n    }\n    int _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\n\nvoid main() {\n    assign_through_ptr();\n    assign_to_ptr_components();\n    bool _e1 = index_ptr(true);\n    int _e2 = member_ptr();\n    int _e3 = let_members_of_members();\n    int _e4 = var_members_of_members();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-access.foo_frag.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct GlobalConst {\n    uint a;\n    uvec3 b;\n    int c;\n};\nstruct AlignedWrapper {\n    int value;\n};\nstruct Baz {\n    mat3x2 m;\n};\nstruct MatCx2InArray {\n    mat4x2 am[2];\n};\nstruct AssignToMember {\n    uint x;\n};\nstruct S {\n    int m;\n};\nstruct Inner {\n    int delicious;\n};\nstruct Outer {\n    Inner om_nom_nom;\n    uint thing;\n};\nlayout(std430) buffer Bar_block_0Fragment {\n    mat4x3 _matrix;\n    mat2x2 matrix_array[2];\n    int atom;\n    int atom_arr[10];\n    uvec2 arr[2];\n    AlignedWrapper data[];\n} _group_0_binding_0_fs;\n\nlayout(std430) buffer type_13_block_1Fragment { ivec2 _group_0_binding_2_fs; };\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nfloat read_from_private(inout float foo_1) {\n    float _e1 = foo_1;\n    return _e1;\n}\n\nfloat test_arr_as_arg(float a[5][10]) {\n    return a[4][9];\n}\n\nvoid assign_through_ptr_fn(inout uint p) {\n    p = 42u;\n    return;\n}\n\nvoid assign_array_through_ptr_fn(inout vec4 foo_2[2]) {\n    foo_2 = vec4[2](vec4(1.0), vec4(2.0));\n    return;\n}\n\nvoid assign_through_ptr() {\n    uint val = 33u;\n    vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0));\n    assign_through_ptr_fn(val);\n    assign_array_through_ptr_fn(arr);\n    return;\n}\n\nuint fetch_arg_ptr_member(inout AssignToMember p_1) {\n    uint _e2 = p_1.x;\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_member(inout AssignToMember p_2) {\n    p_2.x = 10u;\n    return;\n}\n\nuint fetch_arg_ptr_array_element(inout uint p_3[4]) {\n    uint _e2 = p_3[1];\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_array_element(inout uint p_4[4]) {\n    p_4[1] = 10u;\n    return;\n}\n\nvoid assign_to_ptr_components() {\n    AssignToMember s1_ = AssignToMember(0u);\n    uint a1_[4] = uint[4](0u, 0u, 0u, 0u);\n    assign_to_arg_ptr_member(s1_);\n    uint _e1 = fetch_arg_ptr_member(s1_);\n    assign_to_arg_ptr_array_element(a1_);\n    uint _e3 = fetch_arg_ptr_array_element(a1_);\n    return;\n}\n\nbool index_ptr(bool value) {\n    bool a_1[1] = bool[1](false);\n    a_1 = bool[1](value);\n    bool _e4 = a_1[0];\n    return _e4;\n}\n\nint member_ptr() {\n    S s = S(42);\n    int _e4 = s.m;\n    return _e4;\n}\n\nint let_members_of_members() {\n    Inner inner_1 = Outer(Inner(0), 0u).om_nom_nom;\n    int delishus_1 = inner_1.delicious;\n    if ((Outer(Inner(0), 0u).thing != uint(delishus_1))) {\n    }\n    return Outer(Inner(0), 0u).om_nom_nom.delicious;\n}\n\nint var_members_of_members() {\n    Outer thing = Outer(Inner(0), 0u);\n    Inner inner = Inner(0);\n    int delishus = 0;\n    Inner _e3 = thing.om_nom_nom;\n    inner = _e3;\n    int _e6 = inner.delicious;\n    delishus = _e6;\n    uint _e9 = thing.thing;\n    int _e10 = delishus;\n    if ((_e9 != uint(_e10))) {\n    }\n    int _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\n\nvoid main() {\n    _group_0_binding_0_fs._matrix[1][2] = 1.0;\n    _group_0_binding_0_fs._matrix = mat4x3(vec3(0.0), vec3(1.0), vec3(2.0), vec3(3.0));\n    _group_0_binding_0_fs.arr = uvec2[2](uvec2(0u), uvec2(1u));\n    _group_0_binding_0_fs.data[1].value = 1;\n    _group_0_binding_2_fs = ivec2(0);\n    _fs2p_location0 = vec4(0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-access.foo_vert.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct GlobalConst {\n    uint a;\n    uvec3 b;\n    int c;\n};\nstruct AlignedWrapper {\n    int value;\n};\nstruct Baz {\n    mat3x2 m;\n};\nstruct MatCx2InArray {\n    mat4x2 am[2];\n};\nstruct AssignToMember {\n    uint x;\n};\nstruct S {\n    int m;\n};\nstruct Inner {\n    int delicious;\n};\nstruct Outer {\n    Inner om_nom_nom;\n    uint thing;\n};\nGlobalConst msl_padding_global_const = GlobalConst(0u, uvec3(0u, 0u, 0u), 0);\n\nlayout(std430) buffer Bar_block_0Vertex {\n    mat4x3 _matrix;\n    mat2x2 matrix_array[2];\n    int atom;\n    int atom_arr[10];\n    uvec2 arr[2];\n    AlignedWrapper data[];\n} _group_0_binding_0_vs;\n\nlayout(std140) uniform Baz_block_1Vertex { Baz _group_0_binding_1_vs; };\n\nlayout(std430) buffer type_13_block_2Vertex { ivec2 _group_0_binding_2_vs; };\n\nlayout(std140) uniform MatCx2InArray_block_3Vertex { MatCx2InArray _group_0_binding_3_vs; };\n\n\nvoid test_matrix_within_struct_accesses() {\n    int idx = 1;\n    Baz t = Baz(mat3x2(vec2(1.0), vec2(2.0), vec2(3.0)));\n    int _e3 = idx;\n    idx = (_e3 - 1);\n    mat3x2 l0_ = _group_0_binding_1_vs.m;\n    vec2 l1_ = _group_0_binding_1_vs.m[0];\n    int _e14 = idx;\n    vec2 l2_ = _group_0_binding_1_vs.m[_e14];\n    float l3_ = _group_0_binding_1_vs.m[0][1];\n    int _e25 = idx;\n    float l4_ = _group_0_binding_1_vs.m[0][_e25];\n    int _e30 = idx;\n    float l5_ = _group_0_binding_1_vs.m[_e30][1];\n    int _e36 = idx;\n    int _e38 = idx;\n    float l6_ = _group_0_binding_1_vs.m[_e36][_e38];\n    int _e51 = idx;\n    idx = (_e51 + 1);\n    t.m = mat3x2(vec2(6.0), vec2(5.0), vec2(4.0));\n    t.m[0] = vec2(9.0);\n    int _e66 = idx;\n    t.m[_e66] = vec2(90.0);\n    t.m[0][1] = 10.0;\n    int _e76 = idx;\n    t.m[0][_e76] = 20.0;\n    int _e80 = idx;\n    t.m[_e80][1] = 30.0;\n    int _e85 = idx;\n    int _e87 = idx;\n    t.m[_e85][_e87] = 40.0;\n    return;\n}\n\nvoid test_matrix_within_array_within_struct_accesses() {\n    int idx_1 = 1;\n    MatCx2InArray t_1 = MatCx2InArray(mat4x2[2](mat4x2(0.0), mat4x2(0.0)));\n    int _e3 = idx_1;\n    idx_1 = (_e3 - 1);\n    mat4x2 l0_1[2] = _group_0_binding_3_vs.am;\n    mat4x2 l1_1 = _group_0_binding_3_vs.am[0];\n    vec2 l2_1 = _group_0_binding_3_vs.am[0][0];\n    int _e20 = idx_1;\n    vec2 l3_1 = _group_0_binding_3_vs.am[0][_e20];\n    float l4_1 = _group_0_binding_3_vs.am[0][0][1];\n    int _e33 = idx_1;\n    float l5_1 = _group_0_binding_3_vs.am[0][0][_e33];\n    int _e39 = idx_1;\n    float l6_1 = _group_0_binding_3_vs.am[0][_e39][1];\n    int _e46 = idx_1;\n    int _e48 = idx_1;\n    float l7_ = _group_0_binding_3_vs.am[0][_e46][_e48];\n    int _e55 = idx_1;\n    idx_1 = (_e55 + 1);\n    t_1.am = mat4x2[2](mat4x2(0.0), mat4x2(0.0));\n    t_1.am[0] = mat4x2(vec2(8.0), vec2(7.0), vec2(6.0), vec2(5.0));\n    t_1.am[0][0] = vec2(9.0);\n    int _e77 = idx_1;\n    t_1.am[0][_e77] = vec2(90.0);\n    t_1.am[0][0][1] = 10.0;\n    int _e89 = idx_1;\n    t_1.am[0][0][_e89] = 20.0;\n    int _e94 = idx_1;\n    t_1.am[0][_e94][1] = 30.0;\n    int _e100 = idx_1;\n    int _e102 = idx_1;\n    t_1.am[0][_e100][_e102] = 40.0;\n    return;\n}\n\nfloat read_from_private(inout float foo_1) {\n    float _e1 = foo_1;\n    return _e1;\n}\n\nfloat test_arr_as_arg(float a[5][10]) {\n    return a[4][9];\n}\n\nvoid assign_through_ptr_fn(inout uint p) {\n    p = 42u;\n    return;\n}\n\nvoid assign_array_through_ptr_fn(inout vec4 foo_2[2]) {\n    foo_2 = vec4[2](vec4(1.0), vec4(2.0));\n    return;\n}\n\nvoid assign_through_ptr() {\n    uint val = 33u;\n    vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0));\n    assign_through_ptr_fn(val);\n    assign_array_through_ptr_fn(arr);\n    return;\n}\n\nuint fetch_arg_ptr_member(inout AssignToMember p_1) {\n    uint _e2 = p_1.x;\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_member(inout AssignToMember p_2) {\n    p_2.x = 10u;\n    return;\n}\n\nuint fetch_arg_ptr_array_element(inout uint p_3[4]) {\n    uint _e2 = p_3[1];\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_array_element(inout uint p_4[4]) {\n    p_4[1] = 10u;\n    return;\n}\n\nvoid assign_to_ptr_components() {\n    AssignToMember s1_ = AssignToMember(0u);\n    uint a1_[4] = uint[4](0u, 0u, 0u, 0u);\n    assign_to_arg_ptr_member(s1_);\n    uint _e1 = fetch_arg_ptr_member(s1_);\n    assign_to_arg_ptr_array_element(a1_);\n    uint _e3 = fetch_arg_ptr_array_element(a1_);\n    return;\n}\n\nbool index_ptr(bool value) {\n    bool a_1[1] = bool[1](false);\n    a_1 = bool[1](value);\n    bool _e4 = a_1[0];\n    return _e4;\n}\n\nint member_ptr() {\n    S s = S(42);\n    int _e4 = s.m;\n    return _e4;\n}\n\nint let_members_of_members() {\n    Inner inner_1 = Outer(Inner(0), 0u).om_nom_nom;\n    int delishus_1 = inner_1.delicious;\n    if ((Outer(Inner(0), 0u).thing != uint(delishus_1))) {\n    }\n    return Outer(Inner(0), 0u).om_nom_nom.delicious;\n}\n\nint var_members_of_members() {\n    Outer thing = Outer(Inner(0), 0u);\n    Inner inner = Inner(0);\n    int delishus = 0;\n    Inner _e3 = thing.om_nom_nom;\n    inner = _e3;\n    int _e6 = inner.delicious;\n    delishus = _e6;\n    uint _e9 = thing.thing;\n    int _e10 = delishus;\n    if ((_e9 != uint(_e10))) {\n    }\n    int _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\n\nvoid main() {\n    uint vi = uint(gl_VertexID);\n    float foo = 0.0;\n    int c2_[5] = int[5](0, 0, 0, 0, 0);\n    float baz_1 = foo;\n    foo = 1.0;\n    GlobalConst phony = msl_padding_global_const;\n    test_matrix_within_struct_accesses();\n    test_matrix_within_array_within_struct_accesses();\n    mat4x3 _matrix = _group_0_binding_0_vs._matrix;\n    uvec2 arr_1[2] = _group_0_binding_0_vs.arr;\n    float b = _group_0_binding_0_vs._matrix[3u][0];\n    int a_2 = _group_0_binding_0_vs.data[(uint(_group_0_binding_0_vs.data.length()) - 2u)].value;\n    ivec2 c = _group_0_binding_2_vs;\n    float _e35 = read_from_private(foo);\n    c2_ = int[5](a_2, int(b), 3, 4, 5);\n    c2_[(vi + 1u)] = 42;\n    int value_1 = c2_[vi];\n    float _e49 = test_arr_as_arg(float[5][10](float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));\n    gl_Position = vec4((_matrix * vec4(ivec4(value_1))), 2.0);\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-array-in-ctor.cs_main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct Ah {\n    float inner[2];\n};\nlayout(std430) readonly buffer Ah_block_0Compute { Ah _group_0_binding_0_cs; };\n\n\nvoid main() {\n    Ah ah_1 = _group_0_binding_0_cs;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-array-in-function-return-type.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nfloat[2] ret_array() {\n    return float[2](1.0, 2.0);\n}\n\nfloat[3][2] ret_array_array() {\n    float _e0[2] = ret_array();\n    float _e1[2] = ret_array();\n    float _e2[2] = ret_array();\n    return float[3][2](_e0, _e1, _e2);\n}\n\nvoid main() {\n    float _e0[3][2] = ret_array_array();\n    _fs2p_location0 = vec4(_e0[0][0], _e0[0][1], 0.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_i32.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n};\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n};\nconst uint SIZE = 128u;\n\nlayout(std430) buffer type_3_block_0Compute { int _group_0_binding_0_cs[128]; };\n\n\nvoid main() {\n    uint i = 0u;\n    int old = 0;\n    bool exchanged = false;\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            uint _e27 = i;\n            i = (_e27 + 1u);\n        }\n        loop_init = false;\n        uint _e2 = i;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i;\n            int _e8 = _group_0_binding_0_cs[_e6];\n            old = _e8;\n            exchanged = false;\n            while(true) {\n                bool _e12 = exchanged;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    int _e14 = old;\n                    int new = floatBitsToInt((intBitsToFloat(_e14) + 1.0));\n                    uint _e20 = i;\n                    int _e22 = old;\n                    _atomic_compare_exchange_result_Sint_4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_0_cs[_e20], _e22, new);\n                    _e23.exchanged = (_e23.old_value == _e22);\n                    old = _e23.old_value;\n                    exchanged = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_u32.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n};\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n};\nconst uint SIZE = 128u;\n\nlayout(std430) buffer type_5_block_0Compute { uint _group_0_binding_1_cs[128]; };\n\n\nvoid main() {\n    uint i_1 = 0u;\n    uint old_1 = 0u;\n    bool exchanged_1 = false;\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            uint _e27 = i_1;\n            i_1 = (_e27 + 1u);\n        }\n        loop_init = false;\n        uint _e2 = i_1;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i_1;\n            uint _e8 = _group_0_binding_1_cs[_e6];\n            old_1 = _e8;\n            exchanged_1 = false;\n            while(true) {\n                bool _e12 = exchanged_1;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    uint _e14 = old_1;\n                    uint new = floatBitsToUint((uintBitsToFloat(_e14) + 1.0));\n                    uint _e20 = i_1;\n                    uint _e22 = old_1;\n                    _atomic_compare_exchange_result_Uint_4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_1_cs[_e20], _e22, new);\n                    _e23.exchanged = (_e23.old_value == _e22);\n                    old_1 = _e23.old_value;\n                    exchanged_1 = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-atomicOps.cs_main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in;\n\nstruct Struct {\n    uint atomic_scalar;\n    int atomic_arr[2];\n};\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n};\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n};\nlayout(std430) buffer type_1_block_0Compute { uint _group_0_binding_0_cs; };\n\nlayout(std430) buffer type_4_block_1Compute { int _group_0_binding_1_cs[2]; };\n\nlayout(std430) buffer Struct_block_2Compute { Struct _group_0_binding_2_cs; };\n\nshared uint workgroup_atomic_scalar;\n\nshared int workgroup_atomic_arr[2];\n\nshared Struct workgroup_struct;\n\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        workgroup_atomic_scalar = 0u;\n        workgroup_atomic_arr = int[2](0, 0);\n        workgroup_struct = Struct(0u, int[2](0, 0));\n    }\n    memoryBarrierShared();\n    barrier();\n    uvec3 id = gl_LocalInvocationID;\n    _group_0_binding_0_cs = 1u;\n    _group_0_binding_1_cs[1] = 1;\n    _group_0_binding_2_cs.atomic_scalar = 1u;\n    _group_0_binding_2_cs.atomic_arr[1] = 1;\n    workgroup_atomic_scalar = 1u;\n    workgroup_atomic_arr[1] = 1;\n    workgroup_struct.atomic_scalar = 1u;\n    workgroup_struct.atomic_arr[1] = 1;\n    memoryBarrierShared();\n    barrier();\n    uint l0_ = _group_0_binding_0_cs;\n    int l1_ = _group_0_binding_1_cs[1];\n    uint l2_ = _group_0_binding_2_cs.atomic_scalar;\n    int l3_ = _group_0_binding_2_cs.atomic_arr[1];\n    uint l4_ = workgroup_atomic_scalar;\n    int l5_ = workgroup_atomic_arr[1];\n    uint l6_ = workgroup_struct.atomic_scalar;\n    int l7_ = workgroup_struct.atomic_arr[1];\n    memoryBarrierShared();\n    barrier();\n    uint _e51 = atomicAdd(_group_0_binding_0_cs, 1u);\n    int _e55 = atomicAdd(_group_0_binding_1_cs[1], 1);\n    uint _e59 = atomicAdd(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e64 = atomicAdd(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e67 = atomicAdd(workgroup_atomic_scalar, 1u);\n    int _e71 = atomicAdd(workgroup_atomic_arr[1], 1);\n    uint _e75 = atomicAdd(workgroup_struct.atomic_scalar, 1u);\n    int _e80 = atomicAdd(workgroup_struct.atomic_arr[1], 1);\n    memoryBarrierShared();\n    barrier();\n    uint _e83 = atomicAdd(_group_0_binding_0_cs, -1u);\n    int _e87 = atomicAdd(_group_0_binding_1_cs[1], -1);\n    uint _e91 = atomicAdd(_group_0_binding_2_cs.atomic_scalar, -1u);\n    int _e96 = atomicAdd(_group_0_binding_2_cs.atomic_arr[1], -1);\n    uint _e99 = atomicAdd(workgroup_atomic_scalar, -1u);\n    int _e103 = atomicAdd(workgroup_atomic_arr[1], -1);\n    uint _e107 = atomicAdd(workgroup_struct.atomic_scalar, -1u);\n    int _e112 = atomicAdd(workgroup_struct.atomic_arr[1], -1);\n    memoryBarrierShared();\n    barrier();\n    uint _e115 = atomicMax(_group_0_binding_0_cs, 1u);\n    int _e119 = atomicMax(_group_0_binding_1_cs[1], 1);\n    uint _e123 = atomicMax(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e128 = atomicMax(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e131 = atomicMax(workgroup_atomic_scalar, 1u);\n    int _e135 = atomicMax(workgroup_atomic_arr[1], 1);\n    uint _e139 = atomicMax(workgroup_struct.atomic_scalar, 1u);\n    int _e144 = atomicMax(workgroup_struct.atomic_arr[1], 1);\n    memoryBarrierShared();\n    barrier();\n    uint _e147 = atomicMin(_group_0_binding_0_cs, 1u);\n    int _e151 = atomicMin(_group_0_binding_1_cs[1], 1);\n    uint _e155 = atomicMin(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e160 = atomicMin(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e163 = atomicMin(workgroup_atomic_scalar, 1u);\n    int _e167 = atomicMin(workgroup_atomic_arr[1], 1);\n    uint _e171 = atomicMin(workgroup_struct.atomic_scalar, 1u);\n    int _e176 = atomicMin(workgroup_struct.atomic_arr[1], 1);\n    memoryBarrierShared();\n    barrier();\n    uint _e179 = atomicAnd(_group_0_binding_0_cs, 1u);\n    int _e183 = atomicAnd(_group_0_binding_1_cs[1], 1);\n    uint _e187 = atomicAnd(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e192 = atomicAnd(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e195 = atomicAnd(workgroup_atomic_scalar, 1u);\n    int _e199 = atomicAnd(workgroup_atomic_arr[1], 1);\n    uint _e203 = atomicAnd(workgroup_struct.atomic_scalar, 1u);\n    int _e208 = atomicAnd(workgroup_struct.atomic_arr[1], 1);\n    memoryBarrierShared();\n    barrier();\n    uint _e211 = atomicOr(_group_0_binding_0_cs, 1u);\n    int _e215 = atomicOr(_group_0_binding_1_cs[1], 1);\n    uint _e219 = atomicOr(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e224 = atomicOr(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e227 = atomicOr(workgroup_atomic_scalar, 1u);\n    int _e231 = atomicOr(workgroup_atomic_arr[1], 1);\n    uint _e235 = atomicOr(workgroup_struct.atomic_scalar, 1u);\n    int _e240 = atomicOr(workgroup_struct.atomic_arr[1], 1);\n    memoryBarrierShared();\n    barrier();\n    uint _e243 = atomicXor(_group_0_binding_0_cs, 1u);\n    int _e247 = atomicXor(_group_0_binding_1_cs[1], 1);\n    uint _e251 = atomicXor(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e256 = atomicXor(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e259 = atomicXor(workgroup_atomic_scalar, 1u);\n    int _e263 = atomicXor(workgroup_atomic_arr[1], 1);\n    uint _e267 = atomicXor(workgroup_struct.atomic_scalar, 1u);\n    int _e272 = atomicXor(workgroup_struct.atomic_arr[1], 1);\n    uint _e275 = atomicExchange(_group_0_binding_0_cs, 1u);\n    int _e279 = atomicExchange(_group_0_binding_1_cs[1], 1);\n    uint _e283 = atomicExchange(_group_0_binding_2_cs.atomic_scalar, 1u);\n    int _e288 = atomicExchange(_group_0_binding_2_cs.atomic_arr[1], 1);\n    uint _e291 = atomicExchange(workgroup_atomic_scalar, 1u);\n    int _e295 = atomicExchange(workgroup_atomic_arr[1], 1);\n    uint _e299 = atomicExchange(workgroup_struct.atomic_scalar, 1u);\n    int _e304 = atomicExchange(workgroup_struct.atomic_arr[1], 1);\n    _atomic_compare_exchange_result_Uint_4_ _e308; _e308.old_value = atomicCompSwap(_group_0_binding_0_cs, 1u, 2u);\n    _e308.exchanged = (_e308.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e313; _e313.old_value = atomicCompSwap(_group_0_binding_1_cs[1], 1, 2);\n    _e313.exchanged = (_e313.old_value == 1);\n    _atomic_compare_exchange_result_Uint_4_ _e318; _e318.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_scalar, 1u, 2u);\n    _e318.exchanged = (_e318.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e324; _e324.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_arr[1], 1, 2);\n    _e324.exchanged = (_e324.old_value == 1);\n    _atomic_compare_exchange_result_Uint_4_ _e328; _e328.old_value = atomicCompSwap(workgroup_atomic_scalar, 1u, 2u);\n    _e328.exchanged = (_e328.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e333; _e333.old_value = atomicCompSwap(workgroup_atomic_arr[1], 1, 2);\n    _e333.exchanged = (_e333.old_value == 1);\n    _atomic_compare_exchange_result_Uint_4_ _e338; _e338.old_value = atomicCompSwap(workgroup_struct.atomic_scalar, 1u, 2u);\n    _e338.exchanged = (_e338.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e344; _e344.old_value = atomicCompSwap(workgroup_struct.atomic_arr[1], 1, 2);\n    _e344.exchanged = (_e344.old_value == 1);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-atomicTexture.cs_main.Compute.glsl",
    "content": "#version 420 core\n#extension GL_ARB_compute_shader : require\n#extension GL_OES_shader_image_atomic : require\nlayout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in;\n\nlayout(r32ui) uniform uimage2D _group_0_binding_0_cs;\n\nlayout(r32i) uniform iimage2D _group_0_binding_1_cs;\n\n\nvoid main() {\n    uvec3 id = gl_LocalInvocationID;\n    imageAtomicMax(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicMin(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicAdd(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicAnd(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicOr(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicXor(_group_0_binding_0_cs, ivec2(0, 0), 1u);\n    imageAtomicMax(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    imageAtomicMin(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    imageAtomicAdd(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    imageAtomicAnd(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    imageAtomicOr(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    imageAtomicXor(_group_0_binding_1_cs, ivec2(0, 0), 1);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-barycentrics.fs_main.Fragment.glsl",
    "content": "#version 450 core\n#extension GL_EXT_fragment_shader_barycentric : require\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec3 bary = gl_BaryCoordEXT;\n    _fs2p_location0 = vec4(bary, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-barycentrics.fs_main_no_perspective.Fragment.glsl",
    "content": "#version 450 core\n#extension GL_EXT_fragment_shader_barycentric : require\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec3 bary_1 = gl_BaryCoordNoPerspEXT;\n    _fs2p_location0 = vec4(bary_1, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bitcast.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    ivec2 i2_ = ivec2(0);\n    ivec3 i3_ = ivec3(0);\n    ivec4 i4_ = ivec4(0);\n    uvec2 u2_ = uvec2(0u);\n    uvec3 u3_ = uvec3(0u);\n    uvec4 u4_ = uvec4(0u);\n    vec2 f2_ = vec2(0.0);\n    vec3 f3_ = vec3(0.0);\n    vec4 f4_ = vec4(0.0);\n    ivec2 _e27 = i2_;\n    u2_ = uvec2(_e27);\n    ivec3 _e29 = i3_;\n    u3_ = uvec3(_e29);\n    ivec4 _e31 = i4_;\n    u4_ = uvec4(_e31);\n    uvec2 _e33 = u2_;\n    i2_ = ivec2(_e33);\n    uvec3 _e35 = u3_;\n    i3_ = ivec3(_e35);\n    uvec4 _e37 = u4_;\n    i4_ = ivec4(_e37);\n    ivec2 _e39 = i2_;\n    f2_ = intBitsToFloat(_e39);\n    ivec3 _e41 = i3_;\n    f3_ = intBitsToFloat(_e41);\n    ivec4 _e43 = i4_;\n    f4_ = intBitsToFloat(_e43);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bits.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    int i = 0;\n    ivec2 i2_ = ivec2(0);\n    ivec3 i3_ = ivec3(0);\n    ivec4 i4_ = ivec4(0);\n    uint u = 0u;\n    uvec2 u2_ = uvec2(0u);\n    uvec3 u3_ = uvec3(0u);\n    uvec4 u4_ = uvec4(0u);\n    vec2 f2_ = vec2(0.0);\n    vec4 f4_ = vec4(0.0);\n    vec4 _e28 = f4_;\n    u = packSnorm4x8(_e28);\n    vec4 _e30 = f4_;\n    u = packUnorm4x8(_e30);\n    vec2 _e32 = f2_;\n    u = packSnorm2x16(_e32);\n    vec2 _e34 = f2_;\n    u = packUnorm2x16(_e34);\n    vec2 _e36 = f2_;\n    u = packHalf2x16(_e36);\n    ivec4 _e38 = i4_;\n    u = uint((_e38[0] & 0xFF) | ((_e38[1] & 0xFF) << 8) | ((_e38[2] & 0xFF) << 16) | ((_e38[3] & 0xFF) << 24));\n    uvec4 _e40 = u4_;\n    u = (_e40[0] & 0xFFu) | ((_e40[1] & 0xFFu) << 8) | ((_e40[2] & 0xFFu) << 16) | ((_e40[3] & 0xFFu) << 24);\n    ivec4 _e42 = i4_;\n    u = uint((clamp(_e42, -128, 127)[0] & 0xFF) | ((clamp(_e42, -128, 127)[1] & 0xFF) << 8) | ((clamp(_e42, -128, 127)[2] & 0xFF) << 16) | ((clamp(_e42, -128, 127)[3] & 0xFF) << 24));\n    uvec4 _e44 = u4_;\n    u = (clamp(_e44, 0u, 255u)[0] & 0xFFu) | ((clamp(_e44, 0u, 255u)[1] & 0xFFu) << 8) | ((clamp(_e44, 0u, 255u)[2] & 0xFFu) << 16) | ((clamp(_e44, 0u, 255u)[3] & 0xFFu) << 24);\n    uint _e46 = u;\n    f4_ = unpackSnorm4x8(_e46);\n    uint _e48 = u;\n    f4_ = unpackUnorm4x8(_e48);\n    uint _e50 = u;\n    f2_ = unpackSnorm2x16(_e50);\n    uint _e52 = u;\n    f2_ = unpackUnorm2x16(_e52);\n    uint _e54 = u;\n    f2_ = unpackHalf2x16(_e54);\n    uint _e56 = u;\n    i4_ = ivec4(bitfieldExtract(int(_e56), 0, 8), bitfieldExtract(int(_e56), 8, 8), bitfieldExtract(int(_e56), 16, 8), bitfieldExtract(int(_e56), 24, 8));\n    uint _e58 = u;\n    u4_ = uvec4(bitfieldExtract(_e58, 0, 8), bitfieldExtract(_e58, 8, 8), bitfieldExtract(_e58, 16, 8), bitfieldExtract(_e58, 24, 8));\n    int _e60 = i;\n    int _e61 = i;\n    i = bitfieldInsert(_e60, _e61, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec2 _e65 = i2_;\n    ivec2 _e66 = i2_;\n    i2_ = bitfieldInsert(_e65, _e66, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec3 _e70 = i3_;\n    ivec3 _e71 = i3_;\n    i3_ = bitfieldInsert(_e70, _e71, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec4 _e75 = i4_;\n    ivec4 _e76 = i4_;\n    i4_ = bitfieldInsert(_e75, _e76, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uint _e80 = u;\n    uint _e81 = u;\n    u = bitfieldInsert(_e80, _e81, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec2 _e85 = u2_;\n    uvec2 _e86 = u2_;\n    u2_ = bitfieldInsert(_e85, _e86, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec3 _e90 = u3_;\n    uvec3 _e91 = u3_;\n    u3_ = bitfieldInsert(_e90, _e91, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec4 _e95 = u4_;\n    uvec4 _e96 = u4_;\n    u4_ = bitfieldInsert(_e95, _e96, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    int _e100 = i;\n    i = bitfieldExtract(_e100, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec2 _e104 = i2_;\n    i2_ = bitfieldExtract(_e104, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec3 _e108 = i3_;\n    i3_ = bitfieldExtract(_e108, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    ivec4 _e112 = i4_;\n    i4_ = bitfieldExtract(_e112, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uint _e116 = u;\n    u = bitfieldExtract(_e116, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec2 _e120 = u2_;\n    u2_ = bitfieldExtract(_e120, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec3 _e124 = u3_;\n    u3_ = bitfieldExtract(_e124, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    uvec4 _e128 = u4_;\n    u4_ = bitfieldExtract(_e128, int(min(5u, 32u)), int(min(10u, 32u - min(5u, 32u))));\n    int _e132 = i;\n    i = findLSB(_e132);\n    uvec2 _e134 = u2_;\n    u2_ = uvec2(findLSB(_e134));\n    ivec3 _e136 = i3_;\n    i3_ = findMSB(_e136);\n    uvec3 _e138 = u3_;\n    u3_ = uvec3(findMSB(_e138));\n    int _e140 = i;\n    i = findMSB(_e140);\n    uint _e142 = u;\n    u = uint(findMSB(_e142));\n    int _e144 = i;\n    i = bitCount(_e144);\n    ivec2 _e146 = i2_;\n    i2_ = bitCount(_e146);\n    ivec3 _e148 = i3_;\n    i3_ = bitCount(_e148);\n    ivec4 _e150 = i4_;\n    i4_ = bitCount(_e150);\n    uint _e152 = u;\n    u = uint(bitCount(_e152));\n    uvec2 _e154 = u2_;\n    u2_ = uvec2(bitCount(_e154));\n    uvec3 _e156 = u3_;\n    u3_ = uvec3(bitCount(_e156));\n    uvec4 _e158 = u4_;\n    u4_ = uvec4(bitCount(_e158));\n    int _e160 = i;\n    i = bitfieldReverse(_e160);\n    ivec2 _e162 = i2_;\n    i2_ = bitfieldReverse(_e162);\n    ivec3 _e164 = i3_;\n    i3_ = bitfieldReverse(_e164);\n    ivec4 _e166 = i4_;\n    i4_ = bitfieldReverse(_e166);\n    uint _e168 = u;\n    u = bitfieldReverse(_e168);\n    uvec2 _e170 = u2_;\n    u2_ = bitfieldReverse(_e170);\n    uvec3 _e172 = u3_;\n    u3_ = bitfieldReverse(_e172);\n    uvec4 _e174 = u4_;\n    u4_ = bitfieldReverse(_e174);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bits_downlevel.main.Fragment.glsl",
    "content": "#version 330 core\n\nvoid main() {\n    int i = 0;\n    ivec2 i2_ = ivec2(0);\n    ivec3 i3_ = ivec3(0);\n    ivec4 i4_ = ivec4(0);\n    uint u = 0u;\n    uvec2 u2_ = uvec2(0u);\n    uvec3 u3_ = uvec3(0u);\n    uvec4 u4_ = uvec4(0u);\n    vec2 f2_ = vec2(0.0);\n    vec4 f4_ = vec4(0.0);\n    ivec4 _e23 = i4_;\n    u = uint((_e23[0] & 0xFF) | ((_e23[1] & 0xFF) << 8) | ((_e23[2] & 0xFF) << 16) | ((_e23[3] & 0xFF) << 24));\n    uvec4 _e25 = u4_;\n    u = (_e25[0] & 0xFFu) | ((_e25[1] & 0xFFu) << 8) | ((_e25[2] & 0xFFu) << 16) | ((_e25[3] & 0xFFu) << 24);\n    uint _e27 = u;\n    f4_ = (vec4(ivec4(_e27 << 24, _e27 << 16, _e27 << 8, _e27) >> 24) / 127.0);\n    uint _e29 = u;\n    f4_ = (vec4(_e29 & 0xFFu, _e29 >> 8 & 0xFFu, _e29 >> 16 & 0xFFu, _e29 >> 24) / 255.0);\n    uint _e31 = u;\n    f2_ = (vec2(ivec2(_e31 << 16, _e31) >> 16) / 32767.0);\n    uint _e33 = u;\n    f2_ = (vec2(_e33 & 0xFFFFu, _e33 >> 16) / 65535.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bits_downlevel_webgl.main.Fragment.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\n\nvoid main() {\n    int i = 0;\n    ivec2 i2_ = ivec2(0);\n    ivec3 i3_ = ivec3(0);\n    ivec4 i4_ = ivec4(0);\n    uint u = 0u;\n    uvec2 u2_ = uvec2(0u);\n    uvec3 u3_ = uvec3(0u);\n    uvec4 u4_ = uvec4(0u);\n    vec2 f2_ = vec2(0.0);\n    vec4 f4_ = vec4(0.0);\n    ivec4 _e23 = i4_;\n    u = uint((_e23[0] & 0xFF) | ((_e23[1] & 0xFF) << 8) | ((_e23[2] & 0xFF) << 16) | ((_e23[3] & 0xFF) << 24));\n    uvec4 _e25 = u4_;\n    u = (_e25[0] & 0xFFu) | ((_e25[1] & 0xFFu) << 8) | ((_e25[2] & 0xFFu) << 16) | ((_e25[3] & 0xFFu) << 24);\n    uint _e27 = u;\n    f4_ = (vec4(ivec4(_e27 << 24, _e27 << 16, _e27 << 8, _e27) >> 24) / 127.0);\n    uint _e29 = u;\n    f4_ = (vec4(_e29 & 0xFFu, _e29 >> 8 & 0xFFu, _e29 >> 16 & 0xFFu, _e29 >> 24) / 255.0);\n    uint _e31 = u;\n    f2_ = unpackSnorm2x16(_e31);\n    uint _e33 = u;\n    f2_ = unpackUnorm2x16(_e33);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-boids.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n\nstruct Particle {\n    vec2 pos;\n    vec2 vel;\n};\nstruct SimParams {\n    float deltaT;\n    float rule1Distance;\n    float rule2Distance;\n    float rule3Distance;\n    float rule1Scale;\n    float rule2Scale;\n    float rule3Scale;\n};\nconst uint NUM_PARTICLES = 1500u;\n\nlayout(std140) uniform SimParams_block_0Compute { SimParams _group_0_binding_0_cs; };\n\nlayout(std430) readonly buffer Particles_block_1Compute {\n    Particle particles[];\n} _group_0_binding_1_cs;\n\nlayout(std430) buffer Particles_block_2Compute {\n    Particle particles[];\n} _group_0_binding_2_cs;\n\n\nvoid main() {\n    uvec3 global_invocation_id = gl_GlobalInvocationID;\n    vec2 vPos = vec2(0.0);\n    vec2 vVel = vec2(0.0);\n    vec2 cMass = vec2(0.0, 0.0);\n    vec2 cVel = vec2(0.0, 0.0);\n    vec2 colVel = vec2(0.0, 0.0);\n    int cMassCount = 0;\n    int cVelCount = 0;\n    vec2 pos = vec2(0.0);\n    vec2 vel = vec2(0.0);\n    uint i = 0u;\n    uint index = global_invocation_id.x;\n    if ((index >= NUM_PARTICLES)) {\n        return;\n    }\n    vec2 _e8 = _group_0_binding_1_cs.particles[index].pos;\n    vPos = _e8;\n    vec2 _e14 = _group_0_binding_1_cs.particles[index].vel;\n    vVel = _e14;\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            uint _e91 = i;\n            i = (_e91 + 1u);\n        }\n        loop_init = false;\n        uint _e36 = i;\n        if ((_e36 >= NUM_PARTICLES)) {\n            break;\n        }\n        uint _e39 = i;\n        if ((_e39 == index)) {\n            continue;\n        }\n        uint _e43 = i;\n        vec2 _e46 = _group_0_binding_1_cs.particles[_e43].pos;\n        pos = _e46;\n        uint _e49 = i;\n        vec2 _e52 = _group_0_binding_1_cs.particles[_e49].vel;\n        vel = _e52;\n        vec2 _e53 = pos;\n        vec2 _e54 = vPos;\n        float _e58 = _group_0_binding_0_cs.rule1Distance;\n        if ((distance(_e53, _e54) < _e58)) {\n            vec2 _e60 = cMass;\n            vec2 _e61 = pos;\n            cMass = (_e60 + _e61);\n            int _e63 = cMassCount;\n            cMassCount = (_e63 + 1);\n        }\n        vec2 _e66 = pos;\n        vec2 _e67 = vPos;\n        float _e71 = _group_0_binding_0_cs.rule2Distance;\n        if ((distance(_e66, _e67) < _e71)) {\n            vec2 _e73 = colVel;\n            vec2 _e74 = pos;\n            vec2 _e75 = vPos;\n            colVel = (_e73 - (_e74 - _e75));\n        }\n        vec2 _e78 = pos;\n        vec2 _e79 = vPos;\n        float _e83 = _group_0_binding_0_cs.rule3Distance;\n        if ((distance(_e78, _e79) < _e83)) {\n            vec2 _e85 = cVel;\n            vec2 _e86 = vel;\n            cVel = (_e85 + _e86);\n            int _e88 = cVelCount;\n            cVelCount = (_e88 + 1);\n        }\n    }\n    int _e94 = cMassCount;\n    if ((_e94 > 0)) {\n        vec2 _e97 = cMass;\n        int _e98 = cMassCount;\n        vec2 _e102 = vPos;\n        cMass = ((_e97 / vec2(float(_e98))) - _e102);\n    }\n    int _e104 = cVelCount;\n    if ((_e104 > 0)) {\n        vec2 _e107 = cVel;\n        int _e108 = cVelCount;\n        cVel = (_e107 / vec2(float(_e108)));\n    }\n    vec2 _e112 = vVel;\n    vec2 _e113 = cMass;\n    float _e116 = _group_0_binding_0_cs.rule1Scale;\n    vec2 _e119 = colVel;\n    float _e122 = _group_0_binding_0_cs.rule2Scale;\n    vec2 _e125 = cVel;\n    float _e128 = _group_0_binding_0_cs.rule3Scale;\n    vVel = (((_e112 + (_e113 * _e116)) + (_e119 * _e122)) + (_e125 * _e128));\n    vec2 _e131 = vVel;\n    vec2 _e133 = vVel;\n    vVel = (normalize(_e131) * clamp(length(_e133), 0.0, 0.1));\n    vec2 _e139 = vPos;\n    vec2 _e140 = vVel;\n    float _e143 = _group_0_binding_0_cs.deltaT;\n    vPos = (_e139 + (_e140 * _e143));\n    float _e147 = vPos.x;\n    if ((_e147 < -1.0)) {\n        vPos.x = 1.0;\n    }\n    float _e153 = vPos.x;\n    if ((_e153 > 1.0)) {\n        vPos.x = -1.0;\n    }\n    float _e159 = vPos.y;\n    if ((_e159 < -1.0)) {\n        vPos.y = 1.0;\n    }\n    float _e165 = vPos.y;\n    if ((_e165 > 1.0)) {\n        vPos.y = -1.0;\n    }\n    vec2 _e174 = vPos;\n    _group_0_binding_2_cs.particles[index].pos = _e174;\n    vec2 _e179 = vVel;\n    _group_0_binding_2_cs.particles[index].vel = _e179;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bounds-check-image-restrict.fragment_shader.Fragment.glsl",
    "content": "#version 430 core\n#extension GL_ARB_shader_texture_image_samples : require\nuniform sampler1D _group_0_binding_0_fs;\n\nuniform sampler2D _group_0_binding_1_fs;\n\nuniform sampler2DArray _group_0_binding_2_fs;\n\nuniform sampler3D _group_0_binding_3_fs;\n\nuniform sampler2DMS _group_0_binding_4_fs;\n\nlayout(rgba8) writeonly uniform image1D _group_0_binding_5_fs;\n\nlayout(rgba8) writeonly uniform image2D _group_0_binding_6_fs;\n\nlayout(rgba8) writeonly uniform image2DArray _group_0_binding_7_fs;\n\nlayout(rgba8) writeonly uniform image3D _group_0_binding_8_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvec4 test_textureLoad_1d(int coords, int level) {\n    int _e3_clamped_lod = clamp(level, 0, textureQueryLevels(_group_0_binding_0_fs) - 1);\n    vec4 _e3 = texelFetch(_group_0_binding_0_fs, clamp(coords, 0, textureSize(_group_0_binding_0_fs, _e3_clamped_lod) - 1), _e3_clamped_lod);\n    return _e3;\n}\n\nvec4 test_textureLoad_2d(ivec2 coords_1, int level_1) {\n    int _e3_clamped_lod = clamp(level_1, 0, textureQueryLevels(_group_0_binding_1_fs) - 1);\n    vec4 _e3 = texelFetch(_group_0_binding_1_fs, clamp(coords_1, ivec2(0), textureSize(_group_0_binding_1_fs, _e3_clamped_lod) - ivec2(1)), _e3_clamped_lod);\n    return _e3;\n}\n\nvec4 test_textureLoad_2d_array_u(ivec2 coords_2, uint index, int level_2) {\n    int _e4_clamped_lod = clamp(level_2, 0, textureQueryLevels(_group_0_binding_2_fs) - 1);\n    vec4 _e4 = texelFetch(_group_0_binding_2_fs, clamp(ivec3(coords_2, index), ivec3(0), textureSize(_group_0_binding_2_fs, _e4_clamped_lod) - ivec3(1)), _e4_clamped_lod);\n    return _e4;\n}\n\nvec4 test_textureLoad_2d_array_s(ivec2 coords_3, int index_1, int level_3) {\n    int _e4_clamped_lod = clamp(level_3, 0, textureQueryLevels(_group_0_binding_2_fs) - 1);\n    vec4 _e4 = texelFetch(_group_0_binding_2_fs, clamp(ivec3(coords_3, index_1), ivec3(0), textureSize(_group_0_binding_2_fs, _e4_clamped_lod) - ivec3(1)), _e4_clamped_lod);\n    return _e4;\n}\n\nvec4 test_textureLoad_3d(ivec3 coords_4, int level_4) {\n    int _e3_clamped_lod = clamp(level_4, 0, textureQueryLevels(_group_0_binding_3_fs) - 1);\n    vec4 _e3 = texelFetch(_group_0_binding_3_fs, clamp(coords_4, ivec3(0), textureSize(_group_0_binding_3_fs, _e3_clamped_lod) - ivec3(1)), _e3_clamped_lod);\n    return _e3;\n}\n\nvec4 test_textureLoad_multisampled_2d(ivec2 coords_5, int _sample) {\n    vec4 _e3 = texelFetch(_group_0_binding_4_fs, clamp(coords_5, ivec2(0), textureSize(_group_0_binding_4_fs) - ivec2(1)), clamp(_sample, 0, textureSamples(_group_0_binding_4_fs) - 1)\n);\n    return _e3;\n}\n\nvoid test_textureStore_1d(int coords_6, vec4 value) {\n    imageStore(_group_0_binding_5_fs, coords_6, value);\n    return;\n}\n\nvoid test_textureStore_2d(ivec2 coords_7, vec4 value_1) {\n    imageStore(_group_0_binding_6_fs, coords_7, value_1);\n    return;\n}\n\nvoid test_textureStore_2d_array_u(ivec2 coords_8, uint array_index, vec4 value_2) {\n    imageStore(_group_0_binding_7_fs, ivec3(coords_8, array_index), value_2);\n    return;\n}\n\nvoid test_textureStore_2d_array_s(ivec2 coords_9, int array_index_1, vec4 value_3) {\n    imageStore(_group_0_binding_7_fs, ivec3(coords_9, array_index_1), value_3);\n    return;\n}\n\nvoid test_textureStore_3d(ivec3 coords_10, vec4 value_4) {\n    imageStore(_group_0_binding_8_fs, coords_10, value_4);\n    return;\n}\n\nvoid main() {\n    vec4 _e2 = test_textureLoad_1d(0, 0);\n    vec4 _e5 = test_textureLoad_2d(ivec2(0), 0);\n    vec4 _e9 = test_textureLoad_2d_array_u(ivec2(0), 0u, 0);\n    vec4 _e13 = test_textureLoad_2d_array_s(ivec2(0), 0, 0);\n    vec4 _e16 = test_textureLoad_3d(ivec3(0), 0);\n    vec4 _e19 = test_textureLoad_multisampled_2d(ivec2(0), 0);\n    test_textureStore_1d(0, vec4(0.0));\n    test_textureStore_2d(ivec2(0), vec4(0.0));\n    test_textureStore_2d_array_u(ivec2(0), 0u, vec4(0.0));\n    test_textureStore_2d_array_s(ivec2(0), 0, vec4(0.0));\n    test_textureStore_3d(ivec3(0), vec4(0.0));\n    _fs2p_location0 = vec4(0.0, 0.0, 0.0, 0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-bounds-check-image-rzsw.fragment_shader.Fragment.glsl",
    "content": "#version 430 core\n#extension GL_ARB_shader_texture_image_samples : require\nuniform sampler1D _group_0_binding_0_fs;\n\nuniform sampler2D _group_0_binding_1_fs;\n\nuniform sampler2DArray _group_0_binding_2_fs;\n\nuniform sampler3D _group_0_binding_3_fs;\n\nuniform sampler2DMS _group_0_binding_4_fs;\n\nlayout(rgba8) writeonly uniform image1D _group_0_binding_5_fs;\n\nlayout(rgba8) writeonly uniform image2D _group_0_binding_6_fs;\n\nlayout(rgba8) writeonly uniform image2DArray _group_0_binding_7_fs;\n\nlayout(rgba8) writeonly uniform image3D _group_0_binding_8_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvec4 test_textureLoad_1d(int coords, int level) {\n    vec4 _e3 = (level < textureQueryLevels(_group_0_binding_0_fs) && coords < textureSize(_group_0_binding_0_fs, level) ? texelFetch(_group_0_binding_0_fs, coords, level) : vec4(0.0));\n    return _e3;\n}\n\nvec4 test_textureLoad_2d(ivec2 coords_1, int level_1) {\n    vec4 _e3 = (level_1 < textureQueryLevels(_group_0_binding_1_fs) && all(lessThan(coords_1, textureSize(_group_0_binding_1_fs, level_1))) ? texelFetch(_group_0_binding_1_fs, coords_1, level_1) : vec4(0.0));\n    return _e3;\n}\n\nvec4 test_textureLoad_2d_array_u(ivec2 coords_2, uint index, int level_2) {\n    vec4 _e4 = (level_2 < textureQueryLevels(_group_0_binding_2_fs) && all(lessThan(ivec3(coords_2, index), textureSize(_group_0_binding_2_fs, level_2))) ? texelFetch(_group_0_binding_2_fs, ivec3(coords_2, index), level_2) : vec4(0.0));\n    return _e4;\n}\n\nvec4 test_textureLoad_2d_array_s(ivec2 coords_3, int index_1, int level_3) {\n    vec4 _e4 = (level_3 < textureQueryLevels(_group_0_binding_2_fs) && all(lessThan(ivec3(coords_3, index_1), textureSize(_group_0_binding_2_fs, level_3))) ? texelFetch(_group_0_binding_2_fs, ivec3(coords_3, index_1), level_3) : vec4(0.0));\n    return _e4;\n}\n\nvec4 test_textureLoad_3d(ivec3 coords_4, int level_4) {\n    vec4 _e3 = (level_4 < textureQueryLevels(_group_0_binding_3_fs) && all(lessThan(coords_4, textureSize(_group_0_binding_3_fs, level_4))) ? texelFetch(_group_0_binding_3_fs, coords_4, level_4) : vec4(0.0));\n    return _e3;\n}\n\nvec4 test_textureLoad_multisampled_2d(ivec2 coords_5, int _sample) {\n    vec4 _e3 = (_sample < textureSamples(_group_0_binding_4_fs) && all(lessThan(coords_5, textureSize(_group_0_binding_4_fs))) ? texelFetch(_group_0_binding_4_fs, coords_5, _sample) : vec4(0.0));\n    return _e3;\n}\n\nvoid test_textureStore_1d(int coords_6, vec4 value) {\n    imageStore(_group_0_binding_5_fs, coords_6, value);\n    return;\n}\n\nvoid test_textureStore_2d(ivec2 coords_7, vec4 value_1) {\n    imageStore(_group_0_binding_6_fs, coords_7, value_1);\n    return;\n}\n\nvoid test_textureStore_2d_array_u(ivec2 coords_8, uint array_index, vec4 value_2) {\n    imageStore(_group_0_binding_7_fs, ivec3(coords_8, array_index), value_2);\n    return;\n}\n\nvoid test_textureStore_2d_array_s(ivec2 coords_9, int array_index_1, vec4 value_3) {\n    imageStore(_group_0_binding_7_fs, ivec3(coords_9, array_index_1), value_3);\n    return;\n}\n\nvoid test_textureStore_3d(ivec3 coords_10, vec4 value_4) {\n    imageStore(_group_0_binding_8_fs, coords_10, value_4);\n    return;\n}\n\nvoid main() {\n    vec4 _e2 = test_textureLoad_1d(0, 0);\n    vec4 _e5 = test_textureLoad_2d(ivec2(0), 0);\n    vec4 _e9 = test_textureLoad_2d_array_u(ivec2(0), 0u, 0);\n    vec4 _e13 = test_textureLoad_2d_array_s(ivec2(0), 0, 0);\n    vec4 _e16 = test_textureLoad_3d(ivec3(0), 0);\n    vec4 _e19 = test_textureLoad_multisampled_2d(ivec2(0), 0);\n    test_textureStore_1d(0, vec4(0.0));\n    test_textureStore_2d(ivec2(0), vec4(0.0));\n    test_textureStore_2d_array_u(ivec2(0), 0u, vec4(0.0));\n    test_textureStore_2d_array_s(ivec2(0), 0, vec4(0.0));\n    test_textureStore_3d(ivec3(0), vec4(0.0));\n    _fs2p_location0 = vec4(0.0, 0.0, 0.0, 0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-break-if.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid breakIfEmpty() {\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            if (true) {\n                break;\n            }\n        }\n        loop_init = false;\n    }\n    return;\n}\n\nvoid breakIfEmptyBody(bool a) {\n    bool b = false;\n    bool c = false;\n    bool loop_init_1 = true;\n    while(true) {\n        if (!loop_init_1) {\n            b = a;\n            bool _e2 = b;\n            c = (a != _e2);\n            bool _e5 = c;\n            if ((a == _e5)) {\n                break;\n            }\n        }\n        loop_init_1 = false;\n    }\n    return;\n}\n\nvoid breakIf(bool a_1) {\n    bool d = false;\n    bool e = false;\n    bool loop_init_2 = true;\n    while(true) {\n        if (!loop_init_2) {\n            bool _e5 = e;\n            if ((a_1 == _e5)) {\n                break;\n            }\n        }\n        loop_init_2 = false;\n        d = a_1;\n        bool _e2 = d;\n        e = (a_1 != _e2);\n    }\n    return;\n}\n\nvoid breakIfSeparateVariable() {\n    uint counter = 0u;\n    bool loop_init_3 = true;\n    while(true) {\n        if (!loop_init_3) {\n            uint _e5 = counter;\n            if ((_e5 == 5u)) {\n                break;\n            }\n        }\n        loop_init_3 = false;\n        uint _e2 = counter;\n        counter = (_e2 + 1u);\n    }\n    return;\n}\n\nvoid main() {\n    breakIfEmpty();\n    breakIfEmptyBody(false);\n    breakIf(false);\n    breakIfSeparateVariable();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-clip-distances.main.Vertex.glsl",
    "content": "#version 330 core\nstruct VertexOutput {\n    vec4 position;\n    float clip_distances[1];\n};\nout float gl_ClipDistance[1];\n\nvoid main() {\n    VertexOutput out_ = VertexOutput(vec4(0.0), float[1](0.0));\n    out_.clip_distances[0] = 0.5;\n    VertexOutput _e4 = out_;\n    gl_Position = _e4.position;\n    gl_ClipDistance = _e4.clip_distances;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-const-exprs.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 2, local_size_y = 3, local_size_z = 1) in;\n\nconst uint TWO = 2u;\nconst int THREE = 3;\nconst bool TRUE = true;\nconst bool FALSE = false;\nconst int FOUR = 4;\nconst int TEXTURE_KIND_REGULAR = 0;\nconst int TEXTURE_KIND_WARP = 1;\nconst int TEXTURE_KIND_SKY = 2;\nconst int FOUR_ALIAS = 4;\nconst int TEST_CONSTANT_ADDITION = 8;\nconst int TEST_CONSTANT_ALIAS_ADDITION = 8;\nconst float PI = 3.141;\nconst float phi_sun = 6.282;\nconst vec4 DIV = vec4(0.44444445, 0.0, 0.0, 0.0);\nconst vec2 add_vec = vec2(4.0, 5.0);\nconst bvec2 compare_vec = bvec2(true, false);\n\n\nvoid swizzle_of_compose() {\n    ivec4 out_ = ivec4(4, 3, 2, 1);\n    return;\n}\n\nvoid index_of_compose() {\n    int out_1 = 2;\n    return;\n}\n\nvoid compose_three_deep() {\n    int out_2 = 6;\n    return;\n}\n\nvoid non_constant_initializers() {\n    int w = 30;\n    int x = 0;\n    int y = 0;\n    int z = 70;\n    ivec4 out_3 = ivec4(0);\n    int _e2 = w;\n    x = _e2;\n    int _e4 = x;\n    y = _e4;\n    int _e8 = w;\n    int _e9 = x;\n    int _e10 = y;\n    int _e11 = z;\n    out_3 = ivec4(_e8, _e9, _e10, _e11);\n    return;\n}\n\nvoid splat_of_constant() {\n    ivec4 out_4 = ivec4(-4, -4, -4, -4);\n    return;\n}\n\nvoid compose_of_constant() {\n    ivec4 out_5 = ivec4(-4, -4, -4, -4);\n    return;\n}\n\nuint map_texture_kind(int texture_kind) {\n    switch(texture_kind) {\n        case 0: {\n            return 10u;\n        }\n        case 1: {\n            return 20u;\n        }\n        case 2: {\n            return 30u;\n        }\n        default: {\n            return 0u;\n        }\n    }\n}\n\nvoid compose_of_splat() {\n    vec4 x_1 = vec4(2.0, 1.0, 1.0, 1.0);\n    return;\n}\n\nvoid test_local_const() {\n    float arr[2] = float[2](0.0, 0.0);\n    return;\n}\n\nvoid compose_vector_zero_val_binop() {\n    ivec3 a = ivec3(1, 1, 1);\n    ivec3 b = ivec3(0, 1, 2);\n    ivec3 c = ivec3(1, 0, 2);\n    return;\n}\n\nvoid relational() {\n    bool scalar_any_false = false;\n    bool scalar_any_true = true;\n    bool scalar_all_false = false;\n    bool scalar_all_true = true;\n    bool vec_any_false = false;\n    bool vec_any_true = true;\n    bool vec_all_false = false;\n    bool vec_all_true = true;\n    return;\n}\n\nvoid packed_dot_product() {\n    int signed_four = 4;\n    uint unsigned_four = 4u;\n    int signed_twelve = 12;\n    uint unsigned_twelve = 12u;\n    int signed_seventy = 70;\n    uint unsigned_seventy = 70u;\n    int minus_four = -4;\n    return;\n}\n\nvoid abstract_access(uint i) {\n    float a_1 = 1.0;\n    uint b_1 = 1u;\n    int c_1 = 0;\n    int d = 0;\n    c_1 = int[9](1, 2, 3, 4, 5, 6, 7, 8, 9)[i];\n    d = ivec4(1, 2, 3, 4)[i];\n    return;\n}\n\nvoid main() {\n    swizzle_of_compose();\n    index_of_compose();\n    compose_three_deep();\n    non_constant_initializers();\n    splat_of_constant();\n    compose_of_constant();\n    uint _e1 = map_texture_kind(1);\n    compose_of_splat();\n    test_local_const();\n    compose_vector_zero_val_binop();\n    relational();\n    packed_dot_product();\n    test_local_const();\n    abstract_access(1u);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-constructors.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct Foo {\n    vec4 a;\n    int b;\n};\nconst vec3 const1_ = vec3(0.0);\nconst mat2x2 const3_ = mat2x2(vec2(0.0, 1.0), vec2(2.0, 3.0));\nconst mat2x2 const4_[1] = mat2x2[1](mat2x2(vec2(0.0, 1.0), vec2(2.0, 3.0)));\nconst bool cz0_ = false;\nconst int cz1_ = 0;\nconst uint cz2_ = 0u;\nconst float cz3_ = 0.0;\nconst uvec2 cz4_ = uvec2(0u);\nconst mat2x2 cz5_ = mat2x2(0.0);\nconst Foo cz6_[3] = Foo[3](Foo(vec4(0.0), 0), Foo(vec4(0.0), 0), Foo(vec4(0.0), 0));\nconst Foo cz7_ = Foo(vec4(0.0), 0);\nconst uvec2 cp1_ = uvec2(0u);\n\n\nvoid main() {\n    Foo foo = Foo(vec4(0.0), 0);\n    foo = Foo(vec4(1.0), 1);\n    mat2x2 m0_ = mat2x2(vec2(1.0, 0.0), vec2(0.0, 1.0));\n    mat4x4 m1_ = mat4x4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n    uvec2 zvc8_ = uvec2(0u, 0u);\n    vec2 zvc9_ = vec2(0.0, 0.0);\n    uvec2 cit0_ = uvec2(0u);\n    mat2x2 cit1_ = mat2x2(vec2(0.0), vec2(0.0));\n    int cit2_[4] = int[4](0, 1, 2, 3);\n    uvec2 ic4_ = uvec2(0u, 0u);\n    mat2x3 ic5_ = mat2x3(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-control-flow.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid control_flow() {\n    int pos = 0;\n    memoryBarrierBuffer();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierImage();\n    barrier();\n    do {\n        pos = 1;\n    } while(false);\n    int _e3 = pos;\n    switch(_e3) {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n            break;\n        }\n        case 3:\n        case 4: {\n            pos = 2;\n            break;\n        }\n        case 5: {\n            pos = 3;\n            break;\n        }\n        default:\n        case 6: {\n            pos = 4;\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    int _e10 = pos;\n    switch(_e10) {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n            break;\n        }\n        case 3: {\n            pos = 2;\n            break;\n        }\n        case 4: {\n            break;\n        }\n        default: {\n            pos = 3;\n            break;\n        }\n    }\n    int _e15 = pos;\n    switch(_e15) {\n        case 1: {\n            pos = 0;\n            return;\n        }\n        case 2: {\n            pos = 1;\n            return;\n        }\n        case 3:\n        case 4: {\n            pos = 2;\n            return;\n        }\n        case 5:\n        case 6: {\n            pos = 3;\n            return;\n        }\n        default: {\n            pos = 4;\n            return;\n        }\n    }\n}\n\nvoid switch_default_break(int i) {\n    do {\n        break;\n    } while(false);\n}\n\nvoid switch_case_break() {\n    switch(0) {\n        case 0: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    return;\n}\n\nvoid switch_selector_type_conversion() {\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid switch_const_expr_case_selectors() {\n    switch(0) {\n        case 0: {\n            return;\n        }\n        case 1: {\n            return;\n        }\n        case 2: {\n            return;\n        }\n        case 3: {\n            return;\n        }\n        case 4: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid loop_switch_continue(int x) {\n    while(true) {\n        switch(x) {\n            case 1: {\n                continue;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    return;\n}\n\nvoid loop_switch_continue_nesting(int x_1, int y, int z) {\n    while(true) {\n        switch(x_1) {\n            case 1: {\n                continue;\n            }\n            case 2: {\n                switch(y) {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        while(true) {\n                            switch(z) {\n                                case 1: {\n                                    continue;\n                                }\n                                default: {\n                                    break;\n                                }\n                            }\n                        }\n                        break;\n                    }\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n        bool should_continue = false;\n        do {\n            should_continue = true;\n            break;\n        } while(false);\n        if (should_continue) {\n            continue;\n        }\n    }\n    while(true) {\n        bool should_continue_1 = false;\n        do {\n            do {\n                should_continue_1 = true;\n                break;\n            } while(false);\n            if (should_continue_1) {\n                break;\n            }\n        } while(false);\n        if (should_continue_1) {\n            continue;\n        }\n    }\n    return;\n}\n\nvoid loop_switch_omit_continue_variable_checks(int x_2, int y_1, int z_1, int w) {\n    int pos_1 = 0;\n    while(true) {\n        switch(x_2) {\n            case 1: {\n                pos_1 = 1;\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    while(true) {\n        switch(x_2) {\n            case 1: {\n                break;\n            }\n            case 2: {\n                switch(y_1) {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        switch(z_1) {\n                            case 1: {\n                                pos_1 = 2;\n                                break;\n                            }\n                            default: {\n                                break;\n                            }\n                        }\n                        break;\n                    }\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    return;\n}\n\nvoid main() {\n    control_flow();\n    switch_default_break(1);\n    switch_case_break();\n    switch_selector_type_conversion();\n    switch_const_expr_case_selectors();\n    loop_switch_continue(1);\n    loop_switch_continue_nesting(1, 2, 3);\n    loop_switch_omit_continue_variable_checks(1, 2, 3, 4);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-conversions.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst vec2 ic0_ = vec2(2.0, 2.0);\n\n\nvoid main() {\n    vec2 vc0_ = vec2(2.0, 2.0);\n    vec2 lc0_ = vec2(2.0, 2.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-cross.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    vec3 a = vec3(0.0, 0.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-cubeArrayShadow.fragment.Fragment.glsl",
    "content": "#version 310 es\n#extension GL_EXT_texture_cube_map_array : require\n\nprecision highp float;\nprecision highp int;\n\nuniform highp samplerCubeArrayShadow _group_0_binding_4_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec3 frag_ls = vec3(1.0, 1.0, 2.0);\n    float a = texture(_group_0_binding_4_fs, vec4(frag_ls, 1), 1.0);\n    _fs2p_location0 = vec4(a, 1.0, 1.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-dualsource.main.Fragment.glsl",
    "content": "#version 310 es\n#extension GL_EXT_blend_func_extended : require\n\nprecision highp float;\nprecision highp int;\n\nstruct FragmentOutput {\n    vec4 output0_;\n    vec4 output1_;\n};\nlayout(location = 0, index = 0) out vec4 _fs2p_location0;\nlayout(location = 0, index = 1) out vec4 _fs2p_location1;\n\nvoid main() {\n    FragmentOutput _tmp_return = FragmentOutput(vec4(0.4, 0.3, 0.2, 0.1), vec4(0.9, 0.8, 0.7, 0.6));\n    _fs2p_location0 = _tmp_return.output0_;\n    _fs2p_location1 = _tmp_return.output1_;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-early-depth-test-conservative.main.Fragment.glsl",
    "content": "#version 420 core\nlayout (depth_less) out float gl_FragDepth;\n\nvoid main() {\n    vec4 pos = gl_FragCoord;\n    gl_FragDepth = (pos.z - 0.1);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-early-depth-test-force.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(early_fragment_tests) in;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    _fs2p_location0 = vec4(0.4, 0.3, 0.2, 0.1);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-empty-if.comp.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    uvec3 id = gl_GlobalInvocationID;\n    if ((id.x == 0u)) {\n    }\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-empty.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-f64.main.Compute.glsl",
    "content": "#version 420 core\n#extension GL_ARB_compute_shader : require\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst double k = 2.0LF;\n\ndouble v = 1.0LF;\n\n\ndouble f(double x) {\n    double z = 0.0;\n    double w = -1.0LF;\n    double phony = v;\n    double y = (30.0LF + 400.0LF);\n    z = (y + 5.0LF);\n    return (((x + y) + k) + 5.0LF);\n}\n\nvoid main() {\n    double _e1 = f(6.0LF);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-force_point_size_vertex_shader_webgl.fs_main.Fragment.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    _fs2p_location0 = vec4(1.0, 0.0, 0.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-force_point_size_vertex_shader_webgl.vs_main.Vertex.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\n\nvoid main() {\n    uint in_vertex_index = uint(gl_VertexID);\n    float x = float((int(in_vertex_index) - 1));\n    float y = float(((int((in_vertex_index & 1u)) * 2) - 1));\n    gl_Position = vec4(x, y, 0.0, 1.0);\n    gl_PointSize = 1.0;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-fragment-output.main_vec2scalar.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct FragmentOutputVec4Vec3_ {\n    vec4 vec4f;\n    ivec4 vec4i;\n    uvec4 vec4u;\n    vec3 vec3f;\n    ivec3 vec3i;\n    uvec3 vec3u;\n};\nstruct FragmentOutputVec2Scalar {\n    vec2 vec2f;\n    ivec2 vec2i;\n    uvec2 vec2u;\n    float scalarf;\n    int scalari;\n    uint scalaru;\n};\nlayout(location = 0) out vec2 _fs2p_location0;\nlayout(location = 1) out ivec2 _fs2p_location1;\nlayout(location = 2) out uvec2 _fs2p_location2;\nlayout(location = 3) out float _fs2p_location3;\nlayout(location = 4) out int _fs2p_location4;\nlayout(location = 5) out uint _fs2p_location5;\n\nvoid main() {\n    FragmentOutputVec2Scalar output_1 = FragmentOutputVec2Scalar(vec2(0.0), ivec2(0), uvec2(0u), 0.0, 0, 0u);\n    output_1.vec2f = vec2(0.0);\n    output_1.vec2i = ivec2(0);\n    output_1.vec2u = uvec2(0u);\n    output_1.scalarf = 0.0;\n    output_1.scalari = 0;\n    output_1.scalaru = 0u;\n    FragmentOutputVec2Scalar _e16 = output_1;\n    _fs2p_location0 = _e16.vec2f;\n    _fs2p_location1 = _e16.vec2i;\n    _fs2p_location2 = _e16.vec2u;\n    _fs2p_location3 = _e16.scalarf;\n    _fs2p_location4 = _e16.scalari;\n    _fs2p_location5 = _e16.scalaru;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-fragment-output.main_vec4vec3.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct FragmentOutputVec4Vec3_ {\n    vec4 vec4f;\n    ivec4 vec4i;\n    uvec4 vec4u;\n    vec3 vec3f;\n    ivec3 vec3i;\n    uvec3 vec3u;\n};\nstruct FragmentOutputVec2Scalar {\n    vec2 vec2f;\n    ivec2 vec2i;\n    uvec2 vec2u;\n    float scalarf;\n    int scalari;\n    uint scalaru;\n};\nlayout(location = 0) out vec4 _fs2p_location0;\nlayout(location = 1) out ivec4 _fs2p_location1;\nlayout(location = 2) out uvec4 _fs2p_location2;\nlayout(location = 3) out vec3 _fs2p_location3;\nlayout(location = 4) out ivec3 _fs2p_location4;\nlayout(location = 5) out uvec3 _fs2p_location5;\n\nvoid main() {\n    FragmentOutputVec4Vec3_ output_ = FragmentOutputVec4Vec3_(vec4(0.0), ivec4(0), uvec4(0u), vec3(0.0), ivec3(0), uvec3(0u));\n    output_.vec4f = vec4(0.0);\n    output_.vec4i = ivec4(0);\n    output_.vec4u = uvec4(0u);\n    output_.vec3f = vec3(0.0);\n    output_.vec3i = ivec3(0);\n    output_.vec3u = uvec3(0u);\n    FragmentOutputVec4Vec3_ _e19 = output_;\n    _fs2p_location0 = _e19.vec4f;\n    _fs2p_location1 = _e19.vec4i;\n    _fs2p_location2 = _e19.vec4u;\n    _fs2p_location3 = _e19.vec3f;\n    _fs2p_location4 = _e19.vec3i;\n    _fs2p_location5 = _e19.vec3u;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-functions-webgl.main.Fragment.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\n\nvec2 test_fma() {\n    vec2 a = vec2(2.0, 2.0);\n    vec2 b = vec2(0.5, 0.5);\n    vec2 c = vec2(0.5, 0.5);\n    return fma(a, b, c);\n}\n\nvoid main() {\n    vec2 _e0 = test_fma();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-functions.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvec2 test_fma() {\n    vec2 a = vec2(2.0, 2.0);\n    vec2 b = vec2(0.5, 0.5);\n    vec2 c = vec2(0.5, 0.5);\n    return (a * b + c);\n}\n\nint test_integer_dot_product() {\n    ivec2 a_2_ = ivec2(1);\n    ivec2 b_2_ = ivec2(1);\n    int c_2_ = ( + a_2_.x * b_2_.x + a_2_.y * b_2_.y);\n    uvec3 a_3_ = uvec3(1u);\n    uvec3 b_3_ = uvec3(1u);\n    uint c_3_ = ( + a_3_.x * b_3_.x + a_3_.y * b_3_.y + a_3_.z * b_3_.z);\n    return 32;\n}\n\nuint test_packed_integer_dot_product() {\n    int c_5_ = (bitfieldExtract(int(1u), 0, 8) * bitfieldExtract(int(2u), 0, 8) + bitfieldExtract(int(1u), 8, 8) * bitfieldExtract(int(2u), 8, 8) + bitfieldExtract(int(1u), 16, 8) * bitfieldExtract(int(2u), 16, 8) + bitfieldExtract(int(1u), 24, 8) * bitfieldExtract(int(2u), 24, 8));\n    uint c_6_ = (bitfieldExtract((3u), 0, 8) * bitfieldExtract((4u), 0, 8) + bitfieldExtract((3u), 8, 8) * bitfieldExtract((4u), 8, 8) + bitfieldExtract((3u), 16, 8) * bitfieldExtract((4u), 16, 8) + bitfieldExtract((3u), 24, 8) * bitfieldExtract((4u), 24, 8));\n    uint _e7 = (5u + c_6_);\n    uint _e9 = (6u + c_6_);\n    int c_7_ = (bitfieldExtract(int(_e7), 0, 8) * bitfieldExtract(int(_e9), 0, 8) + bitfieldExtract(int(_e7), 8, 8) * bitfieldExtract(int(_e9), 8, 8) + bitfieldExtract(int(_e7), 16, 8) * bitfieldExtract(int(_e9), 16, 8) + bitfieldExtract(int(_e7), 24, 8) * bitfieldExtract(int(_e9), 24, 8));\n    uint _e12 = (7u + c_6_);\n    uint _e14 = (8u + c_6_);\n    uint c_8_ = (bitfieldExtract((_e12), 0, 8) * bitfieldExtract((_e14), 0, 8) + bitfieldExtract((_e12), 8, 8) * bitfieldExtract((_e14), 8, 8) + bitfieldExtract((_e12), 16, 8) * bitfieldExtract((_e14), 16, 8) + bitfieldExtract((_e12), 24, 8) * bitfieldExtract((_e14), 24, 8));\n    return c_8_;\n}\n\nvoid main() {\n    vec2 _e0 = test_fma();\n    int _e1 = test_integer_dot_product();\n    uint _e2 = test_packed_integer_dot_product();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-globals.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct FooStruct {\n    vec3 v3_;\n    float v1_;\n};\nconst bool Foo_1 = true;\n\nshared float wg[10];\n\nshared uint at_1;\n\nlayout(std430) buffer FooStruct_block_0Compute { FooStruct _group_0_binding_1_cs; };\n\nlayout(std430) readonly buffer type_6_block_1Compute { vec2 _group_0_binding_2_cs[]; };\n\nlayout(std140) uniform type_8_block_2Compute { vec4 _group_0_binding_3_cs[20]; };\n\nlayout(std140) uniform type_4_block_3Compute { vec3 _group_0_binding_4_cs; };\n\nlayout(std140) uniform type_9_block_4Compute { mat3x2 _group_0_binding_5_cs; };\n\nlayout(std140) uniform type_12_block_5Compute { mat2x4 _group_0_binding_6_cs[2][2]; };\n\nlayout(std140) uniform type_15_block_6Compute { mat4x2 _group_0_binding_7_cs[2][2]; };\n\n\nvoid test_msl_packed_vec3_as_arg(vec3 arg) {\n    return;\n}\n\nvoid test_msl_packed_vec3_() {\n    int idx = 1;\n    _group_0_binding_1_cs.v3_ = vec3(1.0);\n    _group_0_binding_1_cs.v3_.x = 1.0;\n    _group_0_binding_1_cs.v3_.x = 2.0;\n    int _e16 = idx;\n    _group_0_binding_1_cs.v3_[_e16] = 3.0;\n    FooStruct data = _group_0_binding_1_cs;\n    vec3 l0_ = data.v3_;\n    vec2 l1_ = data.v3_.zx;\n    test_msl_packed_vec3_as_arg(data.v3_);\n    vec3 mvm0_ = (data.v3_ * mat3x3(0.0));\n    vec3 mvm1_ = (mat3x3(0.0) * data.v3_);\n    vec3 svm0_ = (data.v3_ * 2.0);\n    vec3 svm1_ = (2.0 * data.v3_);\n    return;\n}\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        wg = float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);\n        at_1 = 0u;\n    }\n    memoryBarrierShared();\n    barrier();\n    float Foo = 1.0;\n    bool at = true;\n    test_msl_packed_vec3_();\n    mat4x2 _e5 = _group_0_binding_7_cs[0][0];\n    vec4 _e10 = _group_0_binding_6_cs[0][0][0];\n    wg[7] = (_e5 * _e10).x;\n    mat3x2 _e16 = _group_0_binding_5_cs;\n    vec3 _e18 = _group_0_binding_4_cs;\n    wg[6] = (_e16 * _e18).x;\n    float _e26 = _group_0_binding_2_cs[1].y;\n    wg[5] = _e26;\n    float _e32 = _group_0_binding_3_cs[0].w;\n    wg[4] = _e32;\n    float _e37 = _group_0_binding_1_cs.v1_;\n    wg[3] = _e37;\n    float _e43 = _group_0_binding_1_cs.v3_.x;\n    wg[2] = _e43;\n    _group_0_binding_1_cs.v1_ = 4.0;\n    wg[1] = float(uint(_group_0_binding_2_cs.length()));\n    at_1 = 2u;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.gather.Fragment.glsl",
    "content": "#version 430 core\nuniform sampler2D _group_0_binding_1_fs;\n\nuniform usampler2D _group_0_binding_2_fs;\n\nuniform isampler2D _group_0_binding_3_fs;\n\nuniform sampler2DShadow _group_1_binding_2_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec2 tc = vec2(0.5);\n    vec4 s2d = textureGather(_group_0_binding_1_fs, vec2(tc), 1);\n    vec4 s2d_offset = textureGatherOffset(_group_0_binding_1_fs, vec2(tc), ivec2(3, 1), 3);\n    vec4 s2d_depth = textureGather(_group_1_binding_2_fs, vec2(tc), 0.5);\n    vec4 s2d_depth_offset = textureGatherOffset(_group_1_binding_2_fs, vec2(tc), 0.5, ivec2(3, 1));\n    uvec4 u = textureGather(_group_0_binding_2_fs, vec2(tc), 0);\n    ivec4 i = textureGather(_group_0_binding_3_fs, vec2(tc), 0);\n    vec4 f = (vec4(u) + vec4(i));\n    _fs2p_location0 = ((((s2d + s2d_offset) + s2d_depth) + s2d_depth_offset) + f);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.levels_queries.Vertex.glsl",
    "content": "#version 430 core\n#extension GL_ARB_shader_texture_image_samples : require\nuniform sampler2D _group_0_binding_1_vs;\n\nuniform sampler2DArray _group_0_binding_4_vs;\n\nuniform samplerCube _group_0_binding_5_vs;\n\nuniform samplerCubeArray _group_0_binding_6_vs;\n\nuniform sampler3D _group_0_binding_7_vs;\n\nuniform sampler2DMS _group_0_binding_8_vs;\n\n\nvoid main() {\n    uint num_levels_2d = uint(textureQueryLevels(_group_0_binding_1_vs));\n    uint num_layers_2d = uint(textureSize(_group_0_binding_4_vs, 0).z);\n    uint num_levels_2d_array = uint(textureQueryLevels(_group_0_binding_4_vs));\n    uint num_layers_2d_array = uint(textureSize(_group_0_binding_4_vs, 0).z);\n    uint num_levels_cube = uint(textureQueryLevels(_group_0_binding_5_vs));\n    uint num_levels_cube_array = uint(textureQueryLevels(_group_0_binding_6_vs));\n    uint num_layers_cube = uint(textureSize(_group_0_binding_6_vs, 0).z);\n    uint num_levels_3d = uint(textureQueryLevels(_group_0_binding_7_vs));\n    uint num_samples_aa = uint(textureSamples(_group_0_binding_8_vs));\n    uint sum = (((((((num_layers_2d + num_layers_cube) + num_samples_aa) + num_levels_2d) + num_levels_2d_array) + num_levels_3d) + num_levels_cube) + num_levels_cube_array);\n    gl_Position = vec4(float(sum));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.main.Compute.glsl",
    "content": "#version 430 core\n#extension GL_ARB_compute_shader : require\nlayout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;\n\nuniform usampler2D _group_0_binding_0_cs;\n\nuniform usampler2DMS _group_0_binding_3_cs;\n\nlayout(rgba8ui) readonly uniform uimage2D _group_0_binding_1_cs;\n\nuniform usampler2DArray _group_0_binding_5_cs;\n\nlayout(r32ui) readonly uniform uimage1D _group_0_binding_6_cs;\n\nuniform usampler1D _group_0_binding_7_cs;\n\nlayout(r32ui) writeonly uniform uimage1D _group_0_binding_2_cs;\n\n\nvoid main() {\n    uvec3 local_id = gl_LocalInvocationID;\n    uvec2 dim = uvec2(imageSize(_group_0_binding_1_cs).xy);\n    ivec2 itc = (ivec2((dim * local_id.xy)) % ivec2(10, 20));\n    uvec4 value1_ = texelFetch(_group_0_binding_0_cs, itc, int(local_id.z));\n    uvec4 value1_2_ = texelFetch(_group_0_binding_0_cs, itc, int(uint(local_id.z)));\n    uvec4 value2_ = texelFetch(_group_0_binding_3_cs, itc, int(local_id.z));\n    uvec4 value3_ = texelFetch(_group_0_binding_3_cs, itc, int(uint(local_id.z)));\n    uvec4 value4_ = imageLoad(_group_0_binding_1_cs, itc);\n    uvec4 value5_ = texelFetch(_group_0_binding_5_cs, ivec3(itc, local_id.z), (int(local_id.z) + 1));\n    uvec4 value6_ = texelFetch(_group_0_binding_5_cs, ivec3(itc, int(local_id.z)), (int(local_id.z) + 1));\n    uvec4 value7_ = texelFetch(_group_0_binding_7_cs, int(local_id.x), int(local_id.z));\n    uvec4 value8_ = imageLoad(_group_0_binding_6_cs, int(local_id.x));\n    uvec4 value1u = texelFetch(_group_0_binding_0_cs, ivec2(uvec2(itc)), int(local_id.z));\n    uvec4 value2u = texelFetch(_group_0_binding_3_cs, ivec2(uvec2(itc)), int(local_id.z));\n    uvec4 value3u = texelFetch(_group_0_binding_3_cs, ivec2(uvec2(itc)), int(uint(local_id.z)));\n    uvec4 value4u = imageLoad(_group_0_binding_1_cs, ivec2(uvec2(itc)));\n    uvec4 value5u = texelFetch(_group_0_binding_5_cs, ivec3(uvec2(itc), local_id.z), (int(local_id.z) + 1));\n    uvec4 value6u = texelFetch(_group_0_binding_5_cs, ivec3(uvec2(itc), int(local_id.z)), (int(local_id.z) + 1));\n    uvec4 value7u = texelFetch(_group_0_binding_7_cs, int(uint(local_id.x)), int(local_id.z));\n    imageStore(_group_0_binding_2_cs, itc.x, ((((value1_ + value2_) + value4_) + value5_) + value6_));\n    imageStore(_group_0_binding_2_cs, int(uint(itc.x)), ((((value1u + value2u) + value4u) + value5u) + value6u));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.queries.Vertex.glsl",
    "content": "#version 430 core\nuniform sampler1D _group_0_binding_0_vs;\n\nuniform sampler2D _group_0_binding_1_vs;\n\nuniform sampler2DArray _group_0_binding_4_vs;\n\nuniform samplerCube _group_0_binding_5_vs;\n\nuniform samplerCubeArray _group_0_binding_6_vs;\n\nuniform sampler3D _group_0_binding_7_vs;\n\nuniform sampler2DMS _group_0_binding_8_vs;\n\n\nvoid main() {\n    uint dim_1d = uint(textureSize(_group_0_binding_0_vs, 0));\n    uint dim_1d_lod = uint(textureSize(_group_0_binding_0_vs, int(dim_1d)));\n    uvec2 dim_2d = uvec2(textureSize(_group_0_binding_1_vs, 0).xy);\n    uvec2 dim_2d_lod = uvec2(textureSize(_group_0_binding_1_vs, 1).xy);\n    uvec2 dim_2d_array = uvec2(textureSize(_group_0_binding_4_vs, 0).xy);\n    uvec2 dim_2d_array_lod = uvec2(textureSize(_group_0_binding_4_vs, 1).xy);\n    uvec2 dim_cube = uvec2(textureSize(_group_0_binding_5_vs, 0).xy);\n    uvec2 dim_cube_lod = uvec2(textureSize(_group_0_binding_5_vs, 1).xy);\n    uvec2 dim_cube_array = uvec2(textureSize(_group_0_binding_6_vs, 0).xy);\n    uvec2 dim_cube_array_lod = uvec2(textureSize(_group_0_binding_6_vs, 1).xy);\n    uvec3 dim_3d = uvec3(textureSize(_group_0_binding_7_vs, 0).xyz);\n    uvec3 dim_3d_lod = uvec3(textureSize(_group_0_binding_7_vs, 1).xyz);\n    uvec2 dim_2s_ms = uvec2(textureSize(_group_0_binding_8_vs).xy);\n    uint sum = ((((((((((dim_1d + dim_2d.y) + dim_2d_lod.y) + dim_2d_array.y) + dim_2d_array_lod.y) + dim_cube.y) + dim_cube_lod.y) + dim_cube_array.y) + dim_cube_array_lod.y) + dim_3d.z) + dim_3d_lod.z);\n    gl_Position = vec4(float(sum));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.texture_sample.Fragment.glsl",
    "content": "#version 430 core\nuniform sampler1D _group_0_binding_0_fs;\n\nuniform sampler2D _group_0_binding_1_fs;\n\nuniform sampler2DArray _group_0_binding_4_fs;\n\nuniform samplerCubeArray _group_0_binding_6_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec4 a = vec4(0.0);\n    vec2 _e1 = vec2(0.5);\n    vec3 _e3 = vec3(0.5);\n    ivec2 _e6 = ivec2(3, 1);\n    vec4 _e9 = a;\n    vec4 _e12 = texture(_group_0_binding_0_fs, 0.5);\n    a = (_e9 + _e12);\n    vec4 _e14 = a;\n    vec4 _e17 = texture(_group_0_binding_1_fs, vec2(_e1));\n    a = (_e14 + _e17);\n    vec4 _e19 = a;\n    vec4 _e25 = textureOffset(_group_0_binding_1_fs, vec2(_e1), ivec2(3, 1));\n    a = (_e19 + _e25);\n    vec4 _e27 = a;\n    vec4 _e30 = textureLod(_group_0_binding_1_fs, vec2(_e1), 2.3);\n    a = (_e27 + _e30);\n    vec4 _e32 = a;\n    vec4 _e35 = textureLodOffset(_group_0_binding_1_fs, vec2(_e1), 2.3, ivec2(3, 1));\n    a = (_e32 + _e35);\n    vec4 _e37 = a;\n    vec4 _e41 = textureOffset(_group_0_binding_1_fs, vec2(_e1), ivec2(3, 1), 2.0);\n    a = (_e37 + _e41);\n    vec4 _e43 = a;\n    vec4 _e46 = textureLod(_group_0_binding_1_fs, vec2(_e1), 0.0);\n    a = (_e43 + _e46);\n    vec4 _e48 = a;\n    vec4 _e52 = texture(_group_0_binding_4_fs, vec3(_e1, 0u));\n    a = (_e48 + _e52);\n    vec4 _e54 = a;\n    vec4 _e58 = textureOffset(_group_0_binding_4_fs, vec3(_e1, 0u), ivec2(3, 1));\n    a = (_e54 + _e58);\n    vec4 _e60 = a;\n    vec4 _e64 = textureLod(_group_0_binding_4_fs, vec3(_e1, 0u), 2.3);\n    a = (_e60 + _e64);\n    vec4 _e66 = a;\n    vec4 _e70 = textureLodOffset(_group_0_binding_4_fs, vec3(_e1, 0u), 2.3, ivec2(3, 1));\n    a = (_e66 + _e70);\n    vec4 _e72 = a;\n    vec4 _e77 = textureOffset(_group_0_binding_4_fs, vec3(_e1, 0u), ivec2(3, 1), 2.0);\n    a = (_e72 + _e77);\n    vec4 _e79 = a;\n    vec4 _e83 = texture(_group_0_binding_4_fs, vec3(_e1, 0));\n    a = (_e79 + _e83);\n    vec4 _e85 = a;\n    vec4 _e89 = textureOffset(_group_0_binding_4_fs, vec3(_e1, 0), ivec2(3, 1));\n    a = (_e85 + _e89);\n    vec4 _e91 = a;\n    vec4 _e95 = textureLod(_group_0_binding_4_fs, vec3(_e1, 0), 2.3);\n    a = (_e91 + _e95);\n    vec4 _e97 = a;\n    vec4 _e101 = textureLodOffset(_group_0_binding_4_fs, vec3(_e1, 0), 2.3, ivec2(3, 1));\n    a = (_e97 + _e101);\n    vec4 _e103 = a;\n    vec4 _e108 = textureOffset(_group_0_binding_4_fs, vec3(_e1, 0), ivec2(3, 1), 2.0);\n    a = (_e103 + _e108);\n    vec4 _e110 = a;\n    vec4 _e114 = texture(_group_0_binding_6_fs, vec4(_e3, 0u));\n    a = (_e110 + _e114);\n    vec4 _e116 = a;\n    vec4 _e120 = textureLod(_group_0_binding_6_fs, vec4(_e3, 0u), 2.3);\n    a = (_e116 + _e120);\n    vec4 _e122 = a;\n    vec4 _e127 = texture(_group_0_binding_6_fs, vec4(_e3, 0u), 2.0);\n    a = (_e122 + _e127);\n    vec4 _e129 = a;\n    vec4 _e133 = texture(_group_0_binding_6_fs, vec4(_e3, 0));\n    a = (_e129 + _e133);\n    vec4 _e135 = a;\n    vec4 _e139 = textureLod(_group_0_binding_6_fs, vec4(_e3, 0), 2.3);\n    a = (_e135 + _e139);\n    vec4 _e141 = a;\n    vec4 _e146 = texture(_group_0_binding_6_fs, vec4(_e3, 0), 2.0);\n    a = (_e141 + _e146);\n    vec4 _e148 = a;\n    _fs2p_location0 = _e148;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-image.texture_sample_comparison.Fragment.glsl",
    "content": "#version 430 core\nuniform sampler2DShadow _group_1_binding_2_fs;\n\nuniform sampler2DArrayShadow _group_1_binding_3_fs;\n\nuniform samplerCubeShadow _group_1_binding_4_fs;\n\nlayout(location = 0) out float _fs2p_location0;\n\nvoid main() {\n    float a_1 = 0.0;\n    vec2 tc = vec2(0.5);\n    vec3 tc3_ = vec3(0.5);\n    float _e6 = a_1;\n    float _e9 = texture(_group_1_binding_2_fs, vec3(tc, 0.5));\n    a_1 = (_e6 + _e9);\n    float _e11 = a_1;\n    float _e15 = texture(_group_1_binding_3_fs, vec4(tc, 0u, 0.5));\n    a_1 = (_e11 + _e15);\n    float _e17 = a_1;\n    float _e21 = texture(_group_1_binding_3_fs, vec4(tc, 0, 0.5));\n    a_1 = (_e17 + _e21);\n    float _e23 = a_1;\n    float _e26 = texture(_group_1_binding_4_fs, vec4(tc3_, 0.5));\n    a_1 = (_e23 + _e26);\n    float _e28 = a_1;\n    float _e31 = textureLod(_group_1_binding_2_fs, vec3(tc, 0.5), 0.0);\n    a_1 = (_e28 + _e31);\n    float _e33 = a_1;\n    float _e37 = textureGrad(_group_1_binding_3_fs, vec4(tc, 0u, 0.5), vec2(0.0), vec2(0.0));\n    a_1 = (_e33 + _e37);\n    float _e39 = a_1;\n    float _e43 = textureGrad(_group_1_binding_3_fs, vec4(tc, 0, 0.5), vec2(0.0), vec2(0.0));\n    a_1 = (_e39 + _e43);\n    float _e45 = a_1;\n    float _e48 = textureGrad(_group_1_binding_4_fs, vec4(tc3_, 0.5), vec3(0.0), vec3(0.0));\n    a_1 = (_e45 + _e48);\n    float _e50 = a_1;\n    _fs2p_location0 = _e50;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-interpolate_compat.frag_main.Fragment.glsl",
    "content": "#version 400 core\nstruct FragmentInput {\n    vec4 position;\n    uint _flat;\n    uint flat_either;\n    float _linear;\n    vec2 linear_centroid;\n    vec3 linear_sample;\n    vec3 linear_center;\n    vec4 perspective;\n    float perspective_centroid;\n    float perspective_sample;\n    float perspective_center;\n};\nflat in uint _vs2fs_location0;\nflat in uint _vs2fs_location2;\nnoperspective in float _vs2fs_location3;\nnoperspective centroid in vec2 _vs2fs_location4;\nnoperspective sample in vec3 _vs2fs_location6;\nnoperspective in vec3 _vs2fs_location7;\nsmooth in vec4 _vs2fs_location8;\nsmooth centroid in float _vs2fs_location9;\nsmooth sample in float _vs2fs_location10;\nsmooth in float _vs2fs_location11;\n\nvoid main() {\n    FragmentInput val = FragmentInput(gl_FragCoord, _vs2fs_location0, _vs2fs_location2, _vs2fs_location3, _vs2fs_location4, _vs2fs_location6, _vs2fs_location7, _vs2fs_location8, _vs2fs_location9, _vs2fs_location10, _vs2fs_location11);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-interpolate_compat.vert_main.Vertex.glsl",
    "content": "#version 400 core\nstruct FragmentInput {\n    vec4 position;\n    uint _flat;\n    uint flat_either;\n    float _linear;\n    vec2 linear_centroid;\n    vec3 linear_sample;\n    vec3 linear_center;\n    vec4 perspective;\n    float perspective_centroid;\n    float perspective_sample;\n    float perspective_center;\n};\nflat out uint _vs2fs_location0;\nflat out uint _vs2fs_location2;\nnoperspective out float _vs2fs_location3;\nnoperspective centroid out vec2 _vs2fs_location4;\nnoperspective sample out vec3 _vs2fs_location6;\nnoperspective out vec3 _vs2fs_location7;\nsmooth out vec4 _vs2fs_location8;\nsmooth centroid out float _vs2fs_location9;\nsmooth sample out float _vs2fs_location10;\nsmooth out float _vs2fs_location11;\n\nvoid main() {\n    FragmentInput out_ = FragmentInput(vec4(0.0), 0u, 0u, 0.0, vec2(0.0), vec3(0.0), vec3(0.0), vec4(0.0), 0.0, 0.0, 0.0);\n    out_.position = vec4(2.0, 4.0, 5.0, 6.0);\n    out_._flat = 8u;\n    out_.flat_either = 10u;\n    out_._linear = 27.0;\n    out_.linear_centroid = vec2(64.0, 125.0);\n    out_.linear_sample = vec3(216.0, 343.0, 512.0);\n    out_.linear_center = vec3(255.0, 511.0, 1024.0);\n    out_.perspective = vec4(729.0, 1000.0, 1331.0, 1728.0);\n    out_.perspective_centroid = 2197.0;\n    out_.perspective_sample = 2744.0;\n    out_.perspective_center = 2812.0;\n    FragmentInput _e39 = out_;\n    gl_Position = _e39.position;\n    _vs2fs_location0 = _e39._flat;\n    _vs2fs_location2 = _e39.flat_either;\n    _vs2fs_location3 = _e39._linear;\n    _vs2fs_location4 = _e39.linear_centroid;\n    _vs2fs_location6 = _e39.linear_sample;\n    _vs2fs_location7 = _e39.linear_center;\n    _vs2fs_location8 = _e39.perspective;\n    _vs2fs_location9 = _e39.perspective_centroid;\n    _vs2fs_location10 = _e39.perspective_sample;\n    _vs2fs_location11 = _e39.perspective_center;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-invariant.fs.Fragment.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\n\nvoid main() {\n    vec4 position = gl_FragCoord;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-invariant.vs.Vertex.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\ninvariant gl_Position;\n\nvoid main() {\n    gl_Position = vec4(0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-math-functions.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct _modf_result_f32_ {\n    float fract_;\n    float whole;\n};\nstruct _modf_result_vec2_f32_ {\n    vec2 fract_;\n    vec2 whole;\n};\nstruct _modf_result_vec4_f32_ {\n    vec4 fract_;\n    vec4 whole;\n};\nstruct _frexp_result_f32_ {\n    float fract_;\n    int exp_;\n};\nstruct _frexp_result_vec4_f32_ {\n    vec4 fract_;\n    ivec4 exp_;\n};\n\n_modf_result_f32_ naga_modf(float arg) {\n    float other;\n    float fract = modf(arg, other);\n    return _modf_result_f32_(fract, other);\n}\n\n_modf_result_vec2_f32_ naga_modf(vec2 arg) {\n    vec2 other;\n    vec2 fract = modf(arg, other);\n    return _modf_result_vec2_f32_(fract, other);\n}\n\n_modf_result_vec4_f32_ naga_modf(vec4 arg) {\n    vec4 other;\n    vec4 fract = modf(arg, other);\n    return _modf_result_vec4_f32_(fract, other);\n}\n\n_frexp_result_f32_ naga_frexp(float arg) {\n    int other;\n    float fract = frexp(arg, other);\n    return _frexp_result_f32_(fract, other);\n}\n\n_frexp_result_vec4_f32_ naga_frexp(vec4 arg) {\n    ivec4 other;\n    vec4 fract = frexp(arg, other);\n    return _frexp_result_vec4_f32_(fract, other);\n}\n\nvoid main() {\n    vec4 v = vec4(0.0);\n    float a = degrees(1.0);\n    float b = radians(1.0);\n    vec4 c = degrees(v);\n    vec4 d = radians(v);\n    vec4 e = clamp(v, vec4(0.0), vec4(1.0));\n    vec4 g = refract(v, v, 1.0);\n    ivec4 sign_b = ivec4(-1, -1, -1, -1);\n    vec4 sign_d = vec4(-1.0, -1.0, -1.0, -1.0);\n    vec4 sign_e = vec4(0.0, 0.0, 0.0, 0.0);\n    ivec2 flb_b = ivec2(-1, -1);\n    uvec2 flb_c = uvec2(0u, 0u);\n    ivec2 ftb_c = ivec2(0, 0);\n    uvec2 ftb_d = uvec2(0u, 0u);\n    uvec2 ctz_e = uvec2(32u, 32u);\n    ivec2 ctz_f = ivec2(32, 32);\n    uvec2 ctz_g = uvec2(0u, 0u);\n    ivec2 ctz_h = ivec2(0, 0);\n    ivec2 clz_c = ivec2(0, 0);\n    uvec2 clz_d = uvec2(31u, 31u);\n    float lde_a = ldexp(1.0, 2);\n    vec2 lde_b = ldexp(vec2(1.0, 2.0), ivec2(3, 4));\n    _modf_result_f32_ modf_a = naga_modf(1.5);\n    float modf_b = naga_modf(1.5).fract_;\n    float modf_c = naga_modf(1.5).whole;\n    _modf_result_vec2_f32_ modf_d = naga_modf(vec2(1.5, 1.5));\n    float modf_e = naga_modf(vec4(1.5, 1.5, 1.5, 1.5)).whole.x;\n    float modf_f = naga_modf(vec2(1.5, 1.5)).fract_.y;\n    _frexp_result_f32_ frexp_a = naga_frexp(1.5);\n    float frexp_b = naga_frexp(1.5).fract_;\n    int frexp_c = naga_frexp(1.5).exp_;\n    int frexp_d = naga_frexp(vec4(1.5, 1.5, 1.5, 1.5)).exp_.x;\n    float quantizeToF16_a = unpackHalf2x16(packHalf2x16(vec2(1.0))).x;\n    vec2 _e123 = vec2(1.0, 1.0);\n    vec2 quantizeToF16_b = unpackHalf2x16(packHalf2x16(_e123));\n    vec3 _e128 = vec3(1.0, 1.0, 1.0);\n    vec3 quantizeToF16_c = vec3(unpackHalf2x16(packHalf2x16(_e128.xy)), unpackHalf2x16(packHalf2x16(_e128.zz)).x);\n    vec4 _e134 = vec4(1.0, 1.0, 1.0, 1.0);\n    vec4 quantizeToF16_d = vec4(unpackHalf2x16(packHalf2x16(_e134.xy)), unpackHalf2x16(packHalf2x16(_e134.zw)));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-memory-decorations-coherent.main.Compute.glsl",
    "content": "#version 430 core\n#extension GL_ARB_compute_shader : require\n#extension GL_ARB_shader_storage_buffer_object : require\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nlayout(std430) coherent buffer Data_block_0Compute {\n    uint values[];\n} _group_0_binding_0_cs;\n\nlayout(std430) buffer Data_block_1Compute {\n    uint values[];\n} _group_0_binding_1_cs;\n\n\nvoid main() {\n    uint _e6 = _group_0_binding_1_cs.values[0];\n    _group_0_binding_0_cs.values[0] = _e6;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-memory-decorations.main.Compute.glsl",
    "content": "#version 430 core\n#extension GL_ARB_compute_shader : require\n#extension GL_ARB_shader_storage_buffer_object : require\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nlayout(std430) coherent buffer Data_block_0Compute {\n    uint values[];\n} _group_0_binding_0_cs;\n\nlayout(std430) volatile buffer Data_block_1Compute {\n    uint values[];\n} _group_0_binding_1_cs;\n\nlayout(std430) coherent volatile buffer Data_block_2Compute {\n    uint values[];\n} _group_0_binding_2_cs;\n\nlayout(std430) buffer Data_block_3Compute {\n    uint values[];\n} _group_0_binding_3_cs;\n\n\nvoid main() {\n    uint _e6 = _group_0_binding_1_cs.values[0];\n    _group_0_binding_0_cs.values[0] = _e6;\n    uint _e13 = _group_0_binding_3_cs.values[0];\n    _group_0_binding_2_cs.values[0] = _e13;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl",
    "content": "#version 310 es\n#extension GL_EXT_multiview : require\n\nprecision highp float;\nprecision highp int;\n\n\nvoid main() {\n    uint view_index = uint(gl_ViewIndex);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl",
    "content": "#version 300 es\n#extension GL_OVR_multiview2 : require\n\nprecision highp float;\nprecision highp int;\n\n\nvoid main() {\n    uint view_index = gl_ViewID_OVR;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-operators.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst vec4 v_f32_one = vec4(1.0, 1.0, 1.0, 1.0);\nconst vec4 v_f32_zero = vec4(0.0, 0.0, 0.0, 0.0);\nconst vec4 v_f32_half = vec4(0.5, 0.5, 0.5, 0.5);\nconst ivec4 v_i32_one = ivec4(1, 1, 1, 1);\nconst bool b_false = false;\nconst bool b_true = true;\nconst bool short_circuit_1_invalid_rhs = false;\nconst bool short_circuit_2_invalid_rhs = false;\nconst bool short_circuit_3_ = true;\nconst bool short_circuit_4_ = true;\n\n\nvec4 builtins() {\n    int s1_ = (true ? 1 : 0);\n    vec4 s2_ = (true ? v_f32_one : v_f32_zero);\n    vec4 s3_ = vec4(1.0, 1.0, 1.0, 1.0);\n    vec4 m1_ = mix(v_f32_zero, v_f32_one, v_f32_half);\n    vec4 m2_ = mix(v_f32_zero, v_f32_one, 0.1);\n    float b1_ = intBitsToFloat(1);\n    vec4 b2_ = intBitsToFloat(v_i32_one);\n    ivec4 v_i32_zero = ivec4(0, 0, 0, 0);\n    return (((((vec4((ivec4(s1_) + v_i32_zero)) + s2_) + m1_) + m2_) + vec4(b1_)) + b2_);\n}\n\nvec4 splat(float m, int n) {\n    vec2 a_2 = (((vec2(2.0) + vec2(m)) - vec2(4.0)) / vec2(8.0));\n    ivec4 b = (ivec4(n) % ivec4(2));\n    return (a_2.xyxy + vec4(b));\n}\n\nvec2 splat_assignment() {\n    vec2 a = vec2(2.0);\n    vec2 _e3 = a;\n    a = (_e3 + vec2(1.0));\n    vec2 _e7 = a;\n    a = (_e7 - vec2(3.0));\n    vec2 _e11 = a;\n    a = (_e11 / vec2(4.0));\n    vec2 _e15 = a;\n    return _e15;\n}\n\nvec3 bool_cast(vec3 x) {\n    bvec3 y = bvec3(x);\n    return vec3(y);\n}\n\nbool p() {\n    return true;\n}\n\nbool q() {\n    return false;\n}\n\nbool r() {\n    return true;\n}\n\nbool s() {\n    return false;\n}\n\nvoid logical() {\n    bool local = false;\n    bool local_1 = false;\n    bool local_2 = false;\n    bool local_3 = false;\n    bool local_4 = false;\n    bool local_5 = false;\n    bool local_6 = false;\n    bool neg0_ = !(true);\n    bvec2 neg1_ = not(bvec2(true));\n    if (!(true)) {\n        local = false;\n    } else {\n        local = true;\n    }\n    bool or = local;\n    if (true) {\n        local_1 = false;\n    } else {\n        local_1 = false;\n    }\n    bool and = local_1;\n    bool bitwise_or0_ = (true || false);\n    bvec3 bitwise_or1_ = bvec3(bvec3(true).x || bvec3(false).x, bvec3(true).y || bvec3(false).y, bvec3(true).z || bvec3(false).z);\n    bool bitwise_and0_ = (true && false);\n    bvec4 bitwise_and1_ = bvec4(bvec4(true).x && bvec4(false).x, bvec4(true).y && bvec4(false).y, bvec4(true).z && bvec4(false).z, bvec4(true).w && bvec4(false).w);\n    if (!(false)) {\n        local_2 = false;\n    } else {\n        local_2 = true;\n    }\n    bool _e27 = local_2;\n    bool short_circuit_5_ = !(_e27);\n    bool _e29 = p();\n    if (!(_e29)) {\n        bool _e33 = q();\n        local_3 = _e33;\n    } else {\n        local_3 = true;\n    }\n    bool _e35 = local_3;\n    if (_e35) {\n        bool _e38 = r();\n        if (!(_e38)) {\n            bool _e42 = s();\n            local_5 = _e42;\n        } else {\n            local_5 = true;\n        }\n        bool _e44 = local_5;\n        local_4 = _e44;\n    } else {\n        local_4 = false;\n    }\n    bool short_circuit_6_ = local_4;\n    if (false) {\n        bool _e50 = q();\n        local_6 = _e50;\n    } else {\n        local_6 = true;\n    }\n    bool short_circuit_7_ = local_6;\n    return;\n}\n\nvoid arithmetic() {\n    int prevent_const_eval = 0;\n    int wgpu_7437_ = 0;\n    float neg0_1 = -(1.0);\n    ivec2 neg1_1 = -(ivec2(1));\n    vec2 neg2_ = -(vec2(1.0));\n    int add0_ = (2 + 1);\n    uint add1_ = (2u + 1u);\n    float add2_ = (2.0 + 1.0);\n    ivec2 add3_ = (ivec2(2) + ivec2(1));\n    uvec3 add4_ = (uvec3(2u) + uvec3(1u));\n    vec4 add5_ = (vec4(2.0) + vec4(1.0));\n    int sub0_ = (2 - 1);\n    uint sub1_ = (2u - 1u);\n    float sub2_ = (2.0 - 1.0);\n    ivec2 sub3_ = (ivec2(2) - ivec2(1));\n    uvec3 sub4_ = (uvec3(2u) - uvec3(1u));\n    vec4 sub5_ = (vec4(2.0) - vec4(1.0));\n    int mul0_ = (2 * 1);\n    uint mul1_ = (2u * 1u);\n    float mul2_ = (2.0 * 1.0);\n    ivec2 mul3_ = (ivec2(2) * ivec2(1));\n    uvec3 mul4_ = (uvec3(2u) * uvec3(1u));\n    vec4 mul5_ = (vec4(2.0) * vec4(1.0));\n    int div0_ = (2 / 1);\n    uint div1_ = (2u / 1u);\n    float div2_ = (2.0 / 1.0);\n    ivec2 div3_ = (ivec2(2) / ivec2(1));\n    uvec3 div4_ = (uvec3(2u) / uvec3(1u));\n    vec4 div5_ = (vec4(2.0) / vec4(1.0));\n    int rem0_ = (2 % 1);\n    uint rem1_ = (2u % 1u);\n    float rem2_ = (2.0 - 1.0 * trunc(2.0 / 1.0));\n    ivec2 rem3_ = (ivec2(2) % ivec2(1));\n    uvec3 rem4_ = (uvec3(2u) % uvec3(1u));\n    vec4 rem5_ = (vec4(2.0) - vec4(1.0) * trunc(vec4(2.0) / vec4(1.0)));\n    {\n        ivec2 add0_1 = (ivec2(2) + ivec2(1));\n        ivec2 add1_1 = (ivec2(2) + ivec2(1));\n        uvec2 add2_1 = (uvec2(2u) + uvec2(1u));\n        uvec2 add3_1 = (uvec2(2u) + uvec2(1u));\n        vec2 add4_1 = (vec2(2.0) + vec2(1.0));\n        vec2 add5_1 = (vec2(2.0) + vec2(1.0));\n        ivec2 sub0_1 = (ivec2(2) - ivec2(1));\n        ivec2 sub1_1 = (ivec2(2) - ivec2(1));\n        uvec2 sub2_1 = (uvec2(2u) - uvec2(1u));\n        uvec2 sub3_1 = (uvec2(2u) - uvec2(1u));\n        vec2 sub4_1 = (vec2(2.0) - vec2(1.0));\n        vec2 sub5_1 = (vec2(2.0) - vec2(1.0));\n        ivec2 mul0_1 = (ivec2(2) * 1);\n        ivec2 mul1_1 = (2 * ivec2(1));\n        uvec2 mul2_1 = (uvec2(2u) * 1u);\n        uvec2 mul3_1 = (2u * uvec2(1u));\n        vec2 mul4_1 = (vec2(2.0) * 1.0);\n        vec2 mul5_1 = (2.0 * vec2(1.0));\n        ivec2 div0_1 = (ivec2(2) / ivec2(1));\n        ivec2 div1_1 = (ivec2(2) / ivec2(1));\n        uvec2 div2_1 = (uvec2(2u) / uvec2(1u));\n        uvec2 div3_1 = (uvec2(2u) / uvec2(1u));\n        vec2 div4_1 = (vec2(2.0) / vec2(1.0));\n        vec2 div5_1 = (vec2(2.0) / vec2(1.0));\n        ivec2 rem0_1 = (ivec2(2) % ivec2(1));\n        ivec2 rem1_1 = (ivec2(2) % ivec2(1));\n        uvec2 rem2_1 = (uvec2(2u) % uvec2(1u));\n        uvec2 rem3_1 = (uvec2(2u) % uvec2(1u));\n        vec2 rem4_1 = (vec2(2.0) - vec2(1.0) * trunc(vec2(2.0) / vec2(1.0)));\n        vec2 rem5_1 = (vec2(2.0) - vec2(1.0) * trunc(vec2(2.0) / vec2(1.0)));\n    }\n    mat3x3 add = mat3x3(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0));\n    mat3x3 sub = mat3x3(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0));\n    mat3x3 mul_scalar0_ = (mat3x3(0.0) * 1.0);\n    mat3x3 mul_scalar1_ = (2.0 * mat3x3(0.0));\n    vec3 mul_vector0_ = (mat4x3(0.0) * vec4(1.0));\n    vec4 mul_vector1_ = (vec3(2.0) * mat4x3(0.0));\n    mat3x3 mul = mat3x3(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0));\n    int _e205 = prevent_const_eval;\n    wgpu_7437_ = (_e205 + -2147483648);\n    return;\n}\n\nvoid bit() {\n    int flip0_ = ~(1);\n    uint flip1_ = ~(1u);\n    ivec2 flip2_ = ~(ivec2(1));\n    uvec3 flip3_ = ~(uvec3(1u));\n    int or0_ = (2 | 1);\n    uint or1_ = (2u | 1u);\n    ivec2 or2_ = (ivec2(2) | ivec2(1));\n    uvec3 or3_ = (uvec3(2u) | uvec3(1u));\n    int and0_ = (2 & 1);\n    uint and1_ = (2u & 1u);\n    ivec2 and2_ = (ivec2(2) & ivec2(1));\n    uvec3 and3_ = (uvec3(2u) & uvec3(1u));\n    int xor0_ = (2 ^ 1);\n    uint xor1_ = (2u ^ 1u);\n    ivec2 xor2_ = (ivec2(2) ^ ivec2(1));\n    uvec3 xor3_ = (uvec3(2u) ^ uvec3(1u));\n    int shl0_ = (2 << 1u);\n    uint shl1_ = (2u << 1u);\n    ivec2 shl2_ = (ivec2(2) << uvec2(1u));\n    uvec3 shl3_ = (uvec3(2u) << uvec3(1u));\n    int shr0_ = (2 >> 1u);\n    uint shr1_ = (2u >> 1u);\n    ivec2 shr2_ = (ivec2(2) >> uvec2(1u));\n    uvec3 shr3_ = (uvec3(2u) >> uvec3(1u));\n    return;\n}\n\nvoid comparison() {\n    bool eq0_ = (2 == 1);\n    bool eq1_ = (2u == 1u);\n    bool eq2_ = (2.0 == 1.0);\n    bvec2 eq3_ = equal(ivec2(2), ivec2(1));\n    bvec3 eq4_ = equal(uvec3(2u), uvec3(1u));\n    bvec4 eq5_ = equal(vec4(2.0), vec4(1.0));\n    bool neq0_ = (2 != 1);\n    bool neq1_ = (2u != 1u);\n    bool neq2_ = (2.0 != 1.0);\n    bvec2 neq3_ = notEqual(ivec2(2), ivec2(1));\n    bvec3 neq4_ = notEqual(uvec3(2u), uvec3(1u));\n    bvec4 neq5_ = notEqual(vec4(2.0), vec4(1.0));\n    bool lt0_ = (2 < 1);\n    bool lt1_ = (2u < 1u);\n    bool lt2_ = (2.0 < 1.0);\n    bvec2 lt3_ = lessThan(ivec2(2), ivec2(1));\n    bvec3 lt4_ = lessThan(uvec3(2u), uvec3(1u));\n    bvec4 lt5_ = lessThan(vec4(2.0), vec4(1.0));\n    bool lte0_ = (2 <= 1);\n    bool lte1_ = (2u <= 1u);\n    bool lte2_ = (2.0 <= 1.0);\n    bvec2 lte3_ = lessThanEqual(ivec2(2), ivec2(1));\n    bvec3 lte4_ = lessThanEqual(uvec3(2u), uvec3(1u));\n    bvec4 lte5_ = lessThanEqual(vec4(2.0), vec4(1.0));\n    bool gt0_ = (2 > 1);\n    bool gt1_ = (2u > 1u);\n    bool gt2_ = (2.0 > 1.0);\n    bvec2 gt3_ = greaterThan(ivec2(2), ivec2(1));\n    bvec3 gt4_ = greaterThan(uvec3(2u), uvec3(1u));\n    bvec4 gt5_ = greaterThan(vec4(2.0), vec4(1.0));\n    bool gte0_ = (2 >= 1);\n    bool gte1_ = (2u >= 1u);\n    bool gte2_ = (2.0 >= 1.0);\n    bvec2 gte3_ = greaterThanEqual(ivec2(2), ivec2(1));\n    bvec3 gte4_ = greaterThanEqual(uvec3(2u), uvec3(1u));\n    bvec4 gte5_ = greaterThanEqual(vec4(2.0), vec4(1.0));\n    return;\n}\n\nvoid assignment() {\n    int a_1 = 0;\n    ivec3 vec0_ = ivec3(0);\n    a_1 = 1;\n    int _e5 = a_1;\n    a_1 = (_e5 + 1);\n    int _e7 = a_1;\n    a_1 = (_e7 - 1);\n    int _e9 = a_1;\n    int _e10 = a_1;\n    a_1 = (_e9 * _e10);\n    int _e12 = a_1;\n    int _e13 = a_1;\n    a_1 = (_e12 / _e13);\n    int _e15 = a_1;\n    a_1 = (_e15 % 1);\n    int _e17 = a_1;\n    a_1 = (_e17 & 0);\n    int _e19 = a_1;\n    a_1 = (_e19 | 0);\n    int _e21 = a_1;\n    a_1 = (_e21 ^ 0);\n    int _e23 = a_1;\n    a_1 = (_e23 << 2u);\n    int _e25 = a_1;\n    a_1 = (_e25 >> 1u);\n    int _e28 = a_1;\n    a_1 = (_e28 + 1);\n    int _e31 = a_1;\n    a_1 = (_e31 - 1);\n    int _e37 = vec0_[1];\n    vec0_[1] = (_e37 + 1);\n    int _e41 = vec0_[1];\n    vec0_[1] = (_e41 - 1);\n    return;\n}\n\nvoid negation_avoids_prefix_decrement() {\n    int i0_ = -(1);\n    int i1_ = -(-(1));\n    int i2_ = -(-(1));\n    int i3_ = -(-(1));\n    int i4_ = -(-(-(1)));\n    int i5_ = -(-(-(-(1))));\n    int i6_ = -(-(-(-(-(1)))));\n    int i7_ = -(-(-(-(-(1)))));\n    float f0_ = -(1.0);\n    float f1_ = -(-(1.0));\n    float f2_ = -(-(1.0));\n    float f3_ = -(-(1.0));\n    float f4_ = -(-(-(1.0)));\n    float f5_ = -(-(-(-(1.0))));\n    float f6_ = -(-(-(-(-(1.0)))));\n    float f7_ = -(-(-(-(-(1.0)))));\n    return;\n}\n\nvoid main() {\n    uvec3 id = gl_WorkGroupID;\n    vec4 _e1 = builtins();\n    vec4 _e6 = splat(float(id.x), int(id.y));\n    vec2 _e7 = splat_assignment();\n    vec3 _e12 = bool_cast(vec3(1.0, 1.0, 1.0));\n    logical();\n    arithmetic();\n    bit();\n    comparison();\n    assignment();\n    negation_avoids_prefix_decrement();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-overrides.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst bool has_point_light = false;\nconst float specular_param = 2.3;\nconst float gain = 1.1;\nconst float width = 0.0;\nconst float depth = 2.3;\nconst float height = 4.6;\nconst float inferred_f32_ = 2.718;\nconst uint auto_conversion = 0u;\n\nfloat gain_x_10_ = 11.0;\n\nfloat store_override = 0.0;\n\n\nvoid main() {\n    float t = 23.0;\n    bool x = false;\n    float gain_x_100_ = 0.0;\n    x = true;\n    float _e9 = gain_x_10_;\n    gain_x_100_ = (_e9 * 10.0);\n    store_override = gain;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-padding.vertex.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct S {\n    vec3 a;\n};\nstruct Test {\n    S a;\n    float b;\n};\nstruct Test2_ {\n    vec3 a[2];\n    float b;\n};\nstruct Test3_ {\n    mat4x3 a;\n    float b;\n};\nlayout(std140) uniform Test_block_0Vertex { Test _group_0_binding_0_vs; };\n\nlayout(std140) uniform Test2_block_1Vertex { Test2_ _group_0_binding_1_vs; };\n\nlayout(std140) uniform Test3_block_2Vertex { Test3_ _group_0_binding_2_vs; };\n\n\nvoid main() {\n    float _e4 = _group_0_binding_0_vs.b;\n    float _e8 = _group_0_binding_1_vs.b;\n    float _e12 = _group_0_binding_2_vs.b;\n    gl_Position = (((vec4(1.0) * _e4) * _e8) * _e12);\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-phony_assignment.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nlayout(std140) uniform type_block_0Compute { float _group_0_binding_0_cs; };\n\n\nint five() {\n    return 5;\n}\n\nvoid main() {\n    uvec3 id = gl_GlobalInvocationID;\n    float phony = _group_0_binding_0_cs;\n    float phony_1 = _group_0_binding_0_cs;\n    int _e6 = five();\n    int _e7 = five();\n    float phony_2 = _group_0_binding_0_cs;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-pointer-function-arg.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid takes_ptr(inout int p) {\n    return;\n}\n\nvoid takes_array_ptr(inout int p_1[4]) {\n    return;\n}\n\nvoid takes_vec_ptr(inout ivec2 p_2) {\n    return;\n}\n\nvoid takes_mat_ptr(inout mat2x2 p_3) {\n    return;\n}\n\nvoid local_var(uint i) {\n    int arr[4] = int[4](1, 2, 3, 4);\n    takes_ptr(arr[i]);\n    takes_array_ptr(arr);\n    return;\n}\n\nvoid mat_vec_ptrs(inout ivec2 pv[4], inout mat2x2 pm[4], uint i_1) {\n    takes_vec_ptr(pv[i_1]);\n    takes_mat_ptr(pm[i_1]);\n    return;\n}\n\nvoid argument(inout int v[4], uint i_2) {\n    takes_ptr(v[i_2]);\n    return;\n}\n\nvoid argument_nested_x2_(inout int v_1[4][4], uint i_3, uint j) {\n    takes_ptr(v_1[i_3][j]);\n    takes_ptr(v_1[i_3][0]);\n    takes_ptr(v_1[0][j]);\n    takes_array_ptr(v_1[i_3]);\n    return;\n}\n\nvoid argument_nested_x3_(inout int v_2[4][4][4], uint i_4, uint j_1) {\n    takes_ptr(v_2[i_4][0][j_1]);\n    takes_ptr(v_2[i_4][j_1][0]);\n    takes_ptr(v_2[0][i_4][j_1]);\n    return;\n}\n\nvoid index_from_self(inout int v_3[4], uint i_5) {\n    int _e3 = v_3[i_5];\n    takes_ptr(v_3[_e3]);\n    return;\n}\n\nvoid local_var_from_arg(int a[4], uint i_6) {\n    int b[4] = int[4](0, 0, 0, 0);\n    b = a;\n    takes_ptr(b[i_6]);\n    return;\n}\n\nvoid let_binding(inout int a_1[4], uint i_7) {\n    takes_ptr(a_1[i_7]);\n    takes_ptr(a_1[0]);\n    return;\n}\n\nvoid main() {\n    ivec2 vec[4] = ivec2[4](ivec2(0), ivec2(0), ivec2(0), ivec2(0));\n    mat2x2 mat[4] = mat2x2[4](mat2x2(0.0), mat2x2(0.0), mat2x2(0.0), mat2x2(0.0));\n    int arr1d[4] = int[4](0, 0, 0, 0);\n    int arr2d[4][4] = int[4][4](int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0));\n    int arr3d[4][4][4] = int[4][4][4](int[4][4](int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0)), int[4][4](int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0)), int[4][4](int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0)), int[4][4](int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0), int[4](0, 0, 0, 0)));\n    local_var(1u);\n    mat_vec_ptrs(vec, mat, 1u);\n    argument(arr1d, 1u);\n    argument_nested_x2_(arr2d, 1u, 2u);\n    argument_nested_x3_(arr3d, 1u, 2u);\n    index_from_self(arr1d, 1u);\n    local_var_from_arg(int[4](1, 2, 3, 4), 5u);\n    let_binding(arr1d, 1u);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-primitive-index-mesh.func.Fragment.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    uint index = uint(gl_PrimitiveID);\n    _fs2p_location0 = vec4(float(index), 1.0, 1.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-primitive-index.func.Fragment.glsl",
    "content": "#version 450 core\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    uint index = uint(gl_PrimitiveID);\n    _fs2p_location0 = vec4(float(index), 1.0, 1.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-push-constants.main.Fragment.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\nstruct ImmediateData {\n    float multiplier;\n};\nstruct FragmentIn {\n    vec4 color;\n};\nuniform ImmediateData _immediates_binding_fs;\n\nlayout(location = 0) smooth in vec4 _vs2fs_location0;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    FragmentIn in_ = FragmentIn(_vs2fs_location0);\n    float _e4 = _immediates_binding_fs.multiplier;\n    _fs2p_location0 = (in_.color * _e4);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-push-constants.vert_main.Vertex.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\nuniform uint naga_vs_first_instance;\n\nstruct ImmediateData {\n    float multiplier;\n};\nstruct FragmentIn {\n    vec4 color;\n};\nuniform ImmediateData _immediates_binding_vs;\n\nlayout(location = 0) in vec2 _p2vs_location0;\n\nvoid main() {\n    vec2 pos = _p2vs_location0;\n    uint ii = (uint(gl_InstanceID) + naga_vs_first_instance);\n    uint vi = uint(gl_VertexID);\n    float _e8 = _immediates_binding_vs.multiplier;\n    gl_Position = vec4((((float(ii) * float(vi)) * _e8) * pos), 0.0, 1.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-quad.frag_main.Fragment.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\nstruct VertexOutput {\n    vec2 uv;\n    vec4 position;\n};\nconst float c_scale = 1.2;\n\nuniform highp sampler2D _group_0_binding_0_fs;\n\nsmooth in vec2 _vs2fs_location0;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    vec2 uv_1 = _vs2fs_location0;\n    vec4 color = texture(_group_0_binding_0_fs, vec2(uv_1));\n    if ((color.w == 0.0)) {\n        discard;\n    }\n    vec4 premultiplied = (color.w * color);\n    _fs2p_location0 = premultiplied;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-quad.fs_extra.Fragment.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\nstruct VertexOutput {\n    vec2 uv;\n    vec4 position;\n};\nconst float c_scale = 1.2;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    _fs2p_location0 = vec4(0.0, 0.5, 0.0, 0.5);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-quad.vert_main.Vertex.glsl",
    "content": "#version 300 es\n\nprecision highp float;\nprecision highp int;\n\nstruct VertexOutput {\n    vec2 uv;\n    vec4 position;\n};\nconst float c_scale = 1.2;\n\nlayout(location = 0) in vec2 _p2vs_location0;\nlayout(location = 1) in vec2 _p2vs_location1;\nsmooth out vec2 _vs2fs_location0;\n\nvoid main() {\n    vec2 pos = _p2vs_location0;\n    vec2 uv = _p2vs_location1;\n    VertexOutput _tmp_return = VertexOutput(uv, vec4((c_scale * pos), 0.0, 1.0));\n    _vs2fs_location0 = _tmp_return.uv;\n    gl_Position = _tmp_return.position;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-sample-cube-array-depth-lod.main.Fragment.glsl",
    "content": "#version 320 es\n#extension GL_EXT_texture_cube_map_array : require\n#extension GL_EXT_texture_shadow_lod : require\n\nprecision highp float;\nprecision highp int;\n\nuniform highp samplerCubeArrayShadow _group_0_binding_0_fs;\n\nlayout(location = 0) out float _fs2p_location0;\n\nvoid main() {\n    vec3 pos = vec3(0.0);\n    float _e6 = textureLod(_group_0_binding_0_fs, vec4(pos, 0), 0.0, 0.0);\n    _fs2p_location0 = _e6;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-select.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid main() {\n    ivec2 x0_ = ivec2(1, 2);\n    vec2 i1_ = vec2(0.0);\n    int _e12 = x0_.x;\n    int _e14 = x0_.y;\n    i1_ = ((_e12 < _e14) ? vec2(0.0, 1.0) : vec2(1.0, 0.0));\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-separate-entry-points.compute.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\n\nvoid barriers() {\n    memoryBarrierBuffer();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierImage();\n    barrier();\n    return;\n}\n\nvoid main() {\n    barriers();\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-separate-entry-points.fragment.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid derivatives() {\n    float x = dFdx(0.0);\n    float y = dFdy(0.0);\n    float width = fwidth(0.0);\n    return;\n}\n\nvoid main() {\n    derivatives();\n    _fs2p_location0 = vec4(0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-shadow.fs_main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct Globals {\n    mat4x4 view_proj;\n    uvec4 num_lights;\n};\nstruct Entity {\n    mat4x4 world;\n    vec4 color;\n};\nstruct VertexOutput {\n    vec4 proj_position;\n    vec3 world_normal;\n    vec4 world_position;\n};\nstruct Light {\n    mat4x4 proj;\n    vec4 pos;\n    vec4 color;\n};\nconst vec3 c_ambient = vec3(0.05, 0.05, 0.05);\nconst uint c_max_lights = 10u;\n\nlayout(std140) uniform Globals_block_0Fragment { Globals _group_0_binding_0_fs; };\n\nlayout(std140) uniform Entity_block_1Fragment { Entity _group_1_binding_0_fs; };\n\nlayout(std430) readonly buffer type_8_block_2Fragment { Light _group_0_binding_1_fs[]; };\n\nuniform highp sampler2DArrayShadow _group_0_binding_2_fs;\n\nlayout(location = 0) smooth in vec3 _vs2fs_location0;\nlayout(location = 1) smooth in vec4 _vs2fs_location1;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nfloat fetch_shadow(uint light_id, vec4 homogeneous_coords) {\n    if ((homogeneous_coords.w <= 0.0)) {\n        return 1.0;\n    }\n    vec2 flip_correction = vec2(0.5, -0.5);\n    float proj_correction = (1.0 / homogeneous_coords.w);\n    vec2 light_local = (((homogeneous_coords.xy * flip_correction) * proj_correction) + vec2(0.5, 0.5));\n    float _e24 = textureGrad(_group_0_binding_2_fs, vec4(light_local, int(light_id), (homogeneous_coords.z * proj_correction)), vec2(0.0), vec2(0.0));\n    return _e24;\n}\n\nvoid main() {\n    VertexOutput in_ = VertexOutput(gl_FragCoord, _vs2fs_location0, _vs2fs_location1);\n    vec3 color = c_ambient;\n    uint i = 0u;\n    vec3 normal_1 = normalize(in_.world_normal);\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            uint _e40 = i;\n            i = (_e40 + 1u);\n        }\n        loop_init = false;\n        uint _e7 = i;\n        uint _e11 = _group_0_binding_0_fs.num_lights.x;\n        if ((_e7 < min(_e11, c_max_lights))) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i;\n            Light light = _group_0_binding_1_fs[_e16];\n            uint _e19 = i;\n            float _e23 = fetch_shadow(_e19, (light.proj * in_.world_position));\n            vec3 light_dir = normalize((light.pos.xyz - in_.world_position.xyz));\n            float diffuse = max(0.0, dot(normal_1, light_dir));\n            vec3 _e33 = color;\n            color = (_e33 + ((_e23 * diffuse) * light.color.xyz));\n        }\n    }\n    vec3 _e42 = color;\n    vec4 _e47 = _group_1_binding_0_fs.color;\n    _fs2p_location0 = (vec4(_e42, 1.0) * _e47);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-shadow.fs_main_without_storage.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct Globals {\n    mat4x4 view_proj;\n    uvec4 num_lights;\n};\nstruct Entity {\n    mat4x4 world;\n    vec4 color;\n};\nstruct VertexOutput {\n    vec4 proj_position;\n    vec3 world_normal;\n    vec4 world_position;\n};\nstruct Light {\n    mat4x4 proj;\n    vec4 pos;\n    vec4 color;\n};\nconst vec3 c_ambient = vec3(0.05, 0.05, 0.05);\nconst uint c_max_lights = 10u;\n\nlayout(std140) uniform Globals_block_0Fragment { Globals _group_0_binding_0_fs; };\n\nlayout(std140) uniform Entity_block_1Fragment { Entity _group_1_binding_0_fs; };\n\nlayout(std140) uniform type_9_block_2Fragment { Light _group_0_binding_1_fs[10]; };\n\nuniform highp sampler2DArrayShadow _group_0_binding_2_fs;\n\nlayout(location = 0) smooth in vec3 _vs2fs_location0;\nlayout(location = 1) smooth in vec4 _vs2fs_location1;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nfloat fetch_shadow(uint light_id, vec4 homogeneous_coords) {\n    if ((homogeneous_coords.w <= 0.0)) {\n        return 1.0;\n    }\n    vec2 flip_correction = vec2(0.5, -0.5);\n    float proj_correction = (1.0 / homogeneous_coords.w);\n    vec2 light_local = (((homogeneous_coords.xy * flip_correction) * proj_correction) + vec2(0.5, 0.5));\n    float _e24 = textureGrad(_group_0_binding_2_fs, vec4(light_local, int(light_id), (homogeneous_coords.z * proj_correction)), vec2(0.0), vec2(0.0));\n    return _e24;\n}\n\nvoid main() {\n    VertexOutput in_1 = VertexOutput(gl_FragCoord, _vs2fs_location0, _vs2fs_location1);\n    vec3 color_1 = c_ambient;\n    uint i_1 = 0u;\n    vec3 normal_1 = normalize(in_1.world_normal);\n    bool loop_init = true;\n    while(true) {\n        if (!loop_init) {\n            uint _e40 = i_1;\n            i_1 = (_e40 + 1u);\n        }\n        loop_init = false;\n        uint _e7 = i_1;\n        uint _e11 = _group_0_binding_0_fs.num_lights.x;\n        if ((_e7 < min(_e11, c_max_lights))) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i_1;\n            Light light = _group_0_binding_1_fs[_e16];\n            uint _e19 = i_1;\n            float _e23 = fetch_shadow(_e19, (light.proj * in_1.world_position));\n            vec3 light_dir = normalize((light.pos.xyz - in_1.world_position.xyz));\n            float diffuse = max(0.0, dot(normal_1, light_dir));\n            vec3 _e33 = color_1;\n            color_1 = (_e33 + ((_e23 * diffuse) * light.color.xyz));\n        }\n    }\n    vec3 _e42 = color_1;\n    vec4 _e47 = _group_1_binding_0_fs.color;\n    _fs2p_location0 = (vec4(_e42, 1.0) * _e47);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-shadow.vs_main.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct Globals {\n    mat4x4 view_proj;\n    uvec4 num_lights;\n};\nstruct Entity {\n    mat4x4 world;\n    vec4 color;\n};\nstruct VertexOutput {\n    vec4 proj_position;\n    vec3 world_normal;\n    vec4 world_position;\n};\nstruct Light {\n    mat4x4 proj;\n    vec4 pos;\n    vec4 color;\n};\nconst vec3 c_ambient = vec3(0.05, 0.05, 0.05);\nconst uint c_max_lights = 10u;\n\nlayout(std140) uniform Globals_block_0Vertex { Globals _group_0_binding_0_vs; };\n\nlayout(std140) uniform Entity_block_1Vertex { Entity _group_1_binding_0_vs; };\n\nlayout(location = 0) in ivec4 _p2vs_location0;\nlayout(location = 1) in ivec4 _p2vs_location1;\nlayout(location = 0) smooth out vec3 _vs2fs_location0;\nlayout(location = 1) smooth out vec4 _vs2fs_location1;\n\nvoid main() {\n    ivec4 position = _p2vs_location0;\n    ivec4 normal = _p2vs_location1;\n    VertexOutput out_ = VertexOutput(vec4(0.0), vec3(0.0), vec4(0.0));\n    mat4x4 w = _group_1_binding_0_vs.world;\n    mat4x4 _e7 = _group_1_binding_0_vs.world;\n    vec4 world_pos = (_e7 * vec4(position));\n    out_.world_normal = (mat3x3(w[0].xyz, w[1].xyz, w[2].xyz) * vec3(normal.xyz));\n    out_.world_position = world_pos;\n    mat4x4 _e26 = _group_0_binding_0_vs.view_proj;\n    out_.proj_position = (_e26 * world_pos);\n    VertexOutput _e28 = out_;\n    gl_Position = _e28.proj_position;\n    _vs2fs_location0 = _e28.world_normal;\n    _vs2fs_location1 = _e28.world_position;\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-skybox.fs_main.Fragment.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\nstruct VertexOutput {\n    vec4 position;\n    vec3 uv;\n};\nstruct Data {\n    mat4x4 proj_inv;\n    mat4x4 view;\n};\nlayout(binding = 0) uniform highp samplerCube _group_0_binding_1_fs;\n\nlayout(location = 0) smooth in vec3 _vs2fs_location0;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    VertexOutput in_ = VertexOutput(gl_FragCoord, _vs2fs_location0);\n    vec4 _e4 = texture(_group_0_binding_1_fs, vec3(in_.uv));\n    _fs2p_location0 = _e4;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-skybox.vs_main.Vertex.glsl",
    "content": "#version 320 es\n\nprecision highp float;\nprecision highp int;\n\nstruct VertexOutput {\n    vec4 position;\n    vec3 uv;\n};\nstruct Data {\n    mat4x4 proj_inv;\n    mat4x4 view;\n};\nlayout(std140, binding = 0) uniform Data_block_0Vertex { Data _group_0_binding_0_vs; };\n\nlayout(location = 0) smooth out vec3 _vs2fs_location0;\n\nvoid main() {\n    uint vertex_index = uint(gl_VertexID);\n    int tmp1_ = 0;\n    int tmp2_ = 0;\n    tmp1_ = (int(vertex_index) / 2);\n    tmp2_ = (int(vertex_index) & 1);\n    int _e9 = tmp1_;\n    int _e15 = tmp2_;\n    vec4 pos = vec4(((float(_e9) * 4.0) - 1.0), ((float(_e15) * 4.0) - 1.0), 0.0, 1.0);\n    vec4 _e27 = _group_0_binding_0_vs.view[0];\n    vec4 _e32 = _group_0_binding_0_vs.view[1];\n    vec4 _e37 = _group_0_binding_0_vs.view[2];\n    mat3x3 inv_model_view = transpose(mat3x3(_e27.xyz, _e32.xyz, _e37.xyz));\n    mat4x4 _e43 = _group_0_binding_0_vs.proj_inv;\n    vec4 unprojected = (_e43 * pos);\n    VertexOutput _tmp_return = VertexOutput(pos, (inv_model_view * unprojected.xyz));\n    gl_Position = _tmp_return.position;\n    _vs2fs_location0 = _tmp_return.uv;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-standard.derivatives.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nbool test_any_and_all_for_bool() {\n    return true;\n}\n\nvoid main() {\n    vec4 foo = gl_FragCoord;\n    vec4 x = vec4(0.0);\n    vec4 y = vec4(0.0);\n    vec4 z = vec4(0.0);\n    vec4 _e1 = dFdx(foo);\n    x = _e1;\n    vec4 _e3 = dFdy(foo);\n    y = _e3;\n    vec4 _e5 = fwidth(foo);\n    z = _e5;\n    vec4 _e7 = dFdx(foo);\n    x = _e7;\n    vec4 _e8 = dFdy(foo);\n    y = _e8;\n    vec4 _e9 = fwidth(foo);\n    z = _e9;\n    vec4 _e10 = dFdx(foo);\n    x = _e10;\n    vec4 _e11 = dFdy(foo);\n    y = _e11;\n    vec4 _e12 = fwidth(foo);\n    z = _e12;\n    bool _e13 = test_any_and_all_for_bool();\n    vec4 _e14 = x;\n    vec4 _e15 = y;\n    vec4 _e17 = z;\n    _fs2p_location0 = ((_e14 + _e15) * _e17);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.needs_padding_comp.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(std140) uniform NeedsPadding_block_0Compute { NeedsPadding _group_0_binding_2_cs; };\n\nlayout(std430) buffer NeedsPadding_block_1Compute { NeedsPadding _group_0_binding_3_cs; };\n\n\nvoid main() {\n    NeedsPadding x_1 = NeedsPadding(0.0, vec3(0.0), 0.0);\n    NeedsPadding _e2 = _group_0_binding_2_cs;\n    x_1 = _e2;\n    NeedsPadding _e4 = _group_0_binding_3_cs;\n    x_1 = _e4;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.needs_padding_frag.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(location = 0) smooth in float _vs2fs_location0;\nlayout(location = 1) smooth in vec3 _vs2fs_location1;\nlayout(location = 2) smooth in float _vs2fs_location2;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    NeedsPadding input_2 = NeedsPadding(_vs2fs_location0, _vs2fs_location1, _vs2fs_location2);\n    _fs2p_location0 = vec4(0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.needs_padding_vert.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(location = 0) in float _p2vs_location0;\nlayout(location = 1) in vec3 _p2vs_location1;\nlayout(location = 2) in float _p2vs_location2;\n\nvoid main() {\n    NeedsPadding input_3 = NeedsPadding(_p2vs_location0, _p2vs_location1, _p2vs_location2);\n    gl_Position = vec4(0.0);\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.no_padding_comp.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(std140) uniform NoPadding_block_0Compute { NoPadding _group_0_binding_0_cs; };\n\nlayout(std430) buffer NoPadding_block_1Compute { NoPadding _group_0_binding_1_cs; };\n\n\nvoid main() {\n    NoPadding x = NoPadding(vec3(0.0), 0.0);\n    NoPadding _e2 = _group_0_binding_0_cs;\n    x = _e2;\n    NoPadding _e4 = _group_0_binding_1_cs;\n    x = _e4;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.no_padding_frag.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(location = 0) smooth in vec3 _vs2fs_location0;\nlayout(location = 1) smooth in float _vs2fs_location1;\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvoid main() {\n    NoPadding input_ = NoPadding(_vs2fs_location0, _vs2fs_location1);\n    _fs2p_location0 = vec4(0.0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-struct-layout.no_padding_vert.Vertex.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nstruct NoPadding {\n    vec3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    vec3 v3_needs_padding;\n    float f3_;\n};\nlayout(location = 0) in vec3 _p2vs_location0;\nlayout(location = 1) in float _p2vs_location1;\n\nvoid main() {\n    NoPadding input_1 = NoPadding(_p2vs_location0, _p2vs_location1);\n    gl_Position = vec4(0.0);\n    gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-subgroup-operations.main.Compute.glsl",
    "content": "#version 430 core\n#extension GL_ARB_compute_shader : require\n#extension GL_KHR_shader_subgroup_basic : require\n#extension GL_KHR_shader_subgroup_vote : require\n#extension GL_KHR_shader_subgroup_arithmetic : require\n#extension GL_KHR_shader_subgroup_ballot : require\n#extension GL_KHR_shader_subgroup_shuffle : require\n#extension GL_KHR_shader_subgroup_shuffle_relative : require\n#extension GL_KHR_shader_subgroup_quad : require\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct Structure {\n    uint num_subgroups;\n    uint subgroup_size;\n};\n\nvoid main() {\n    Structure sizes = Structure(gl_NumSubgroups, gl_SubgroupSize);\n    uint subgroup_id = gl_SubgroupID;\n    uint subgroup_invocation_id = gl_SubgroupInvocationID;\n    uvec4 _e7 = subgroupBallot(((subgroup_invocation_id & 1u) == 1u));\n    uvec4 _e8 = subgroupBallot(true);\n    bool _e11 = subgroupAll((subgroup_invocation_id != 0u));\n    bool _e14 = subgroupAny((subgroup_invocation_id == 0u));\n    uint _e15 = subgroupAdd(subgroup_invocation_id);\n    uint _e16 = subgroupMul(subgroup_invocation_id);\n    uint _e17 = subgroupMin(subgroup_invocation_id);\n    uint _e18 = subgroupMax(subgroup_invocation_id);\n    uint _e19 = subgroupAnd(subgroup_invocation_id);\n    uint _e20 = subgroupOr(subgroup_invocation_id);\n    uint _e21 = subgroupXor(subgroup_invocation_id);\n    uint _e22 = subgroupExclusiveAdd(subgroup_invocation_id);\n    uint _e23 = subgroupExclusiveMul(subgroup_invocation_id);\n    uint _e24 = subgroupInclusiveAdd(subgroup_invocation_id);\n    uint _e25 = subgroupInclusiveMul(subgroup_invocation_id);\n    uint _e26 = subgroupBroadcastFirst(subgroup_invocation_id);\n    uint _e28 = subgroupBroadcast(subgroup_invocation_id, 4u);\n    uint _e33 = subgroupShuffle(subgroup_invocation_id, ((sizes.subgroup_size - 1u) - subgroup_invocation_id));\n    uint _e35 = subgroupShuffleDown(subgroup_invocation_id, 1u);\n    uint _e37 = subgroupShuffleUp(subgroup_invocation_id, 1u);\n    uint _e41 = subgroupShuffleXor(subgroup_invocation_id, (sizes.subgroup_size - 1u));\n    uint _e43 = subgroupQuadBroadcast(subgroup_invocation_id, 4u);\n    uint _e44 = subgroupQuadSwapHorizontal(subgroup_invocation_id);\n    uint _e45 = subgroupQuadSwapVertical(subgroup_invocation_id);\n    uint _e46 = subgroupQuadSwapDiagonal(subgroup_invocation_id);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-texture-arg.main.Fragment.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nuniform highp sampler2D _group_0_binding_0_fs;\n\nlayout(location = 0) out vec4 _fs2p_location0;\n\nvec4 test(highp sampler2D Passed_Texture) {\n    vec4 _e5 = texture(Passed_Texture, vec2(vec2(0.0, 0.0)));\n    return _e5;\n}\n\nvoid main() {\n    vec4 _e2 = test(_group_0_binding_0_fs);\n    _fs2p_location0 = _e2;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-type-inference.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nconst uint g1_ = 1u;\nconst float g3_ = 1.0;\nconst ivec4 g4_ = ivec4(0);\nconst ivec4 g5_ = ivec4(1);\nconst mat2x2 g6_ = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n\n\nvoid main() {\n    int g0x = 1;\n    float g2x = 1.0;\n    mat2x2 g7x = mat2x2(vec2(1.0, 1.0), vec2(1.0, 1.0));\n    int c0x = 1;\n    uint c1x = 1u;\n    float c2x = 1.0;\n    float c3x = 1.0;\n    ivec4 c4x = ivec4(0);\n    ivec4 c5x = ivec4(1);\n    mat2x2 c6x = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    mat2x2 c7x = mat2x2(vec2(1.0, 1.0), vec2(1.0, 1.0));\n    int l0x = 0;\n    uint l1x = 0u;\n    float l2x = 0.0;\n    float l3x = 0.0;\n    ivec4 l4x = ivec4(0);\n    int v0_ = 1;\n    uint v1_ = 1u;\n    float v2_ = 1.0;\n    float v3_ = 1.0;\n    ivec4 v4_ = ivec4(0);\n    ivec4 v5_ = ivec4(1);\n    mat2x2 v6_ = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    mat2x2 v7_ = mat2x2(vec2(1.0, 1.0), vec2(1.0, 1.0));\n    ivec4 l5_ = ivec4(1);\n    mat2x2 l6_ = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));\n    mat2x2 l7_ = mat2x2(vec2(1.0, 1.0), vec2(1.0, 1.0));\n    l0x = 1;\n    l1x = 1u;\n    l2x = 1.0;\n    l3x = 1.0;\n    l4x = ivec4(0);\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-use-gl-ext-over-grad-workaround-if-instructed.main.Fragment.glsl",
    "content": "#version 320 es\n#extension GL_EXT_texture_shadow_lod : require\n\nprecision highp float;\nprecision highp int;\n\nuniform highp sampler2DArrayShadow _group_0_binding_0_fs;\n\nlayout(location = 0) out float _fs2p_location0;\n\nvoid main() {\n    vec2 pos = vec2(0.0);\n    float _e6 = textureLod(_group_0_binding_0_fs, vec4(pos, 0, 0.0), 0.0);\n    _fs2p_location0 = _e6;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-workgroup-uniform-load-atomic.test_atomic_workgroup_uniform_load.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n\nstruct AtomicStruct {\n    uint atomic_scalar;\n    int atomic_arr[2];\n};\nshared uint wg_scalar;\n\nshared int wg_signed;\n\nshared AtomicStruct wg_struct;\n\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        wg_scalar = 0u;\n        wg_signed = 0;\n        wg_struct = AtomicStruct(0u, int[2](0, 0));\n    }\n    memoryBarrierShared();\n    barrier();\n    uvec3 workgroup_id = gl_WorkGroupID;\n    uvec3 local_id = gl_LocalInvocationID;\n    bool local = false;\n    bool local_1 = false;\n    bool local_2 = false;\n    uint active_tile_index = (workgroup_id.x + (workgroup_id.y * 32768u));\n    uint _e11 = atomicOr(wg_scalar, uint((active_tile_index >= 64u)));\n    int _e14 = atomicAdd(wg_signed, 1);\n    wg_struct.atomic_scalar = 1u;\n    int _e22 = atomicAdd(wg_struct.atomic_arr[0], 1);\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    uint _e24 = wg_scalar;\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    int _e26 = wg_signed;\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    uint _e29 = wg_struct.atomic_scalar;\n    memoryBarrierShared();\n    barrier();\n    memoryBarrierShared();\n    barrier();\n    int _e33 = wg_struct.atomic_arr[0];\n    memoryBarrierShared();\n    barrier();\n    if ((_e24 == 0u)) {\n        local = (_e26 > 0);\n    } else {\n        local = false;\n    }\n    bool _e41 = local;\n    if (_e41) {\n        local_1 = (_e29 > 0u);\n    } else {\n        local_1 = false;\n    }\n    bool _e47 = local_1;\n    if (_e47) {\n        local_2 = (_e33 > 0);\n    } else {\n        local_2 = false;\n    }\n    bool _e53 = local_2;\n    if (_e53) {\n        return;\n    } else {\n        return;\n    }\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-workgroup-uniform-load.test_workgroupUniformLoad.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in;\n\nconst uint SIZE = 128u;\n\nshared int arr_i32_[128];\n\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        arr_i32_ = int[128](0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n    }\n    memoryBarrierShared();\n    barrier();\n    uvec3 workgroup_id = gl_WorkGroupID;\n    memoryBarrierShared();\n    barrier();\n    int _e4 = arr_i32_[workgroup_id.x];\n    memoryBarrierShared();\n    barrier();\n    if ((_e4 > 10)) {\n        memoryBarrierShared();\n        barrier();\n        return;\n    } else {\n        return;\n    }\n}\n\n"
  },
  {
    "path": "naga/tests/out/glsl/wgsl-workgroup-var-init.main.Compute.glsl",
    "content": "#version 310 es\n\nprecision highp float;\nprecision highp int;\n\nlayout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\nstruct WStruct {\n    uint arr[512];\n    int atom;\n    int atom_arr[8][8];\n};\nshared WStruct w_mem;\n\nlayout(std430) buffer type_1_block_0Compute { uint _group_0_binding_0_cs[512]; };\n\n\nvoid main() {\n    if (gl_LocalInvocationID == uvec3(0u)) {\n        w_mem = WStruct(uint[512](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u), 0, int[8][8](int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0), int[8](0, 0, 0, 0, 0, 0, 0, 0)));\n    }\n    memoryBarrierShared();\n    barrier();\n    uint _e3[512] = w_mem.arr;\n    _group_0_binding_0_cs = _e3;\n    return;\n}\n\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-barrier.hlsl",
    "content": "void function()\n{\n    GroupMemoryBarrier();\n    GroupMemoryBarrierWithGroupSync();\n    DeviceMemoryBarrier();\n    DeviceMemoryBarrier();\n    DeviceMemoryBarrierWithGroupSync();\n    DeviceMemoryBarrierWithGroupSync();\n    DeviceMemoryBarrier();\n    GroupMemoryBarrier();\n    DeviceMemoryBarrier();\n    DeviceMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    DeviceMemoryBarrierWithGroupSync();\n    return;\n}\n\n[numthreads(64, 1, 1)]\nvoid main()\n{\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-barrier.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-do-while.hlsl",
    "content": "void f_u0028_b1_u003b(inout bool cond)\n{\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            bool _e1 = cond;\n            if (!(_e1)) {\n                break;\n            }\n        }\n        loop_init = false;\n        continue;\n    }\n    return;\n}\n\nvoid main_1()\n{\n    bool param = (bool)0;\n\n    param = false;\n    f_u0028_b1_u003b(param);\n    return;\n}\n\nvoid main()\n{\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-do-while.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-empty-global-name.hlsl",
    "content": "struct type_1 {\n    int member;\n};\n\nRWByteAddressBuffer unnamed : register(u0);\n\nvoid function()\n{\n    int _e3 = asint(unnamed.Load(0));\n    unnamed.Store(0, asuint(asint(asuint(_e3) + asuint(int(1)))));\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-empty-global-name.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-fetch_depth.hlsl",
    "content": "struct type_2 {\n    float member;\n};\n\nstruct type_4 {\n    uint2 member;\n};\n\nRWByteAddressBuffer global : register(u0);\nByteAddressBuffer global_1 : register(t1);\nTexture2D<float> global_2 : register(t2);\n\nvoid function()\n{\n    uint2 _e6 = asuint(global_1.Load2(0));\n    float _e7 = global_2.Load(int3(_e6, int(0))).x;\n    global.Store(0, asuint((_e7).xxxx.x));\n    return;\n}\n\n[numthreads(32, 1, 1)]\nvoid cull_fetch_depth()\n{\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-fetch_depth.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cull_fetch_depth\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-inv-hyperbolic-trig-functions.hlsl",
    "content": "static float a = (float)0;\n\nvoid main_1()\n{\n    float b = (float)0;\n    float c = (float)0;\n    float d = (float)0;\n\n    float _e4 = a;\n    b = log(_e4 + sqrt(_e4 * _e4 + 1.0));\n    float _e6 = a;\n    c = log(_e6 + sqrt(_e6 * _e6 - 1.0));\n    float _e8 = a;\n    d = 0.5 * log((1.0 + _e8) / (1.0 - _e8));\n    return;\n}\n\nvoid main()\n{\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-inv-hyperbolic-trig-functions.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-quad-vert.hlsl",
    "content": "struct gl_PerVertex {\n    float4 gl_Position : SV_Position;\n    float gl_PointSize;\n    float gl_ClipDistance[1];\n    float gl_CullDistance[1];\n    int _end_pad_0;\n};\n\nstruct VertexOutput {\n    float2 member : LOC0;\n    float4 gl_Position : SV_Position;\n};\n\ngl_PerVertex Constructgl_PerVertex(float4 arg0, float arg1, float arg2[1], float arg3[1]) {\n    gl_PerVertex ret = (gl_PerVertex)0;\n    ret.gl_Position = arg0;\n    ret.gl_PointSize = arg1;\n    ret.gl_ClipDistance = arg2;\n    ret.gl_CullDistance = arg3;\n    return ret;\n}\n\ntypedef float ret_ZeroValuearray1_float_[1];\nret_ZeroValuearray1_float_ ZeroValuearray1_float_() {\n    return (float[1])0;\n}\n\nstatic float2 v_uv = (float2)0;\nstatic float2 a_uv_1 = (float2)0;\nstatic gl_PerVertex unnamed = Constructgl_PerVertex(float4(0.0, 0.0, 0.0, 1.0), 1.0, ZeroValuearray1_float_(), ZeroValuearray1_float_());\nstatic float2 a_pos_1 = (float2)0;\n\nstruct VertexOutput_main {\n    float2 member : LOC0;\n    float4 gl_Position : SV_Position;\n};\n\nvoid main_1()\n{\n    float2 _e6 = a_uv_1;\n    v_uv = _e6;\n    float2 _e7 = a_pos_1;\n    unnamed.gl_Position = float4(_e7.x, _e7.y, 0.0, 1.0);\n    return;\n}\n\nVertexOutput ConstructVertexOutput(float2 arg0, float4 arg1) {\n    VertexOutput ret = (VertexOutput)0;\n    ret.member = arg0;\n    ret.gl_Position = arg1;\n    return ret;\n}\n\nVertexOutput_main main(float2 a_uv : LOC1, float2 a_pos : LOC0)\n{\n    a_uv_1 = a_uv;\n    a_pos_1 = a_pos;\n    main_1();\n    float2 _e7 = v_uv;\n    float4 _e8 = unnamed.gl_Position;\n    const VertexOutput vertexoutput = ConstructVertexOutput(_e7, _e8);\n    const VertexOutput_main vertexoutput_1 = { vertexoutput.member, vertexoutput.gl_Position };\n    return vertexoutput_1;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-quad-vert.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-subgroup-operations-s.hlsl",
    "content": "static uint global = (uint)0;\nstatic uint global_1 = (uint)0;\nstatic uint global_2 = (uint)0;\nstatic uint global_3 = (uint)0;\n\nstruct ComputeInput_main {\n    uint local_invocation_index : SV_GroupIndex;\n};\n\nvoid function()\n{\n    uint _e5 = global_2;\n    uint _e6 = global_3;\n    const uint4 _e9 = WaveActiveBallot(((_e6 & 1u) == 1u));\n    const uint4 _e10 = WaveActiveBallot(true);\n    const bool _e12 = WaveActiveAllTrue((_e6 != 0u));\n    const bool _e14 = WaveActiveAnyTrue((_e6 == 0u));\n    const uint _e15 = WaveActiveSum(_e6);\n    const uint _e16 = WaveActiveProduct(_e6);\n    const uint _e17 = WaveActiveMin(_e6);\n    const uint _e18 = WaveActiveMax(_e6);\n    const uint _e19 = WaveActiveBitAnd(_e6);\n    const uint _e20 = WaveActiveBitOr(_e6);\n    const uint _e21 = WaveActiveBitXor(_e6);\n    const uint _e22 = WavePrefixSum(_e6);\n    const uint _e23 = WavePrefixProduct(_e6);\n    const uint _e24 = _e6 + WavePrefixSum(_e6);\n    const uint _e25 = _e6 * WavePrefixProduct(_e6);\n    const uint _e26 = WaveReadLaneFirst(_e6);\n    const uint _e27 = WaveReadLaneAt(_e6, 4u);\n    const uint _e30 = WaveReadLaneAt(_e6, ((_e5 - 1u) - _e6));\n    const uint _e31 = WaveReadLaneAt(_e6, WaveGetLaneIndex() + 1u);\n    const uint _e32 = WaveReadLaneAt(_e6, WaveGetLaneIndex() - 1u);\n    const uint _e34 = WaveReadLaneAt(_e6, WaveGetLaneIndex() ^ (_e5 - 1u));\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main(ComputeInput_main computeinput_main)\n{\n    uint param = (1u + WaveGetLaneCount() - 1u) / WaveGetLaneCount();\n    uint param_1 = computeinput_main.local_invocation_index / WaveGetLaneCount();\n    uint param_2 = WaveGetLaneCount();\n    uint param_3 = WaveGetLaneIndex();\n    global = param;\n    global_1 = param_1;\n    global_2 = param_2;\n    global_3 = param_3;\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-subgroup-operations-s.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_0\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-unnamed-gl-per-vertex.hlsl",
    "content": "struct type_4 {\n    float4 member : SV_Position;\n    float member_1;\n    float member_2[1];\n    float member_3[1];\n    int _end_pad_0;\n};\n\ntype_4 Constructtype_4(float4 arg0, float arg1, float arg2[1], float arg3[1]) {\n    type_4 ret = (type_4)0;\n    ret.member = arg0;\n    ret.member_1 = arg1;\n    ret.member_2 = arg2;\n    ret.member_3 = arg3;\n    return ret;\n}\n\ntypedef float ret_ZeroValuearray1_float_[1];\nret_ZeroValuearray1_float_ ZeroValuearray1_float_() {\n    return (float[1])0;\n}\n\nstatic type_4 global = Constructtype_4(float4(0.0, 0.0, 0.0, 1.0), 1.0, ZeroValuearray1_float_(), ZeroValuearray1_float_());\nstatic int global_1 = (int)0;\n\nvoid function()\n{\n    int _e9 = global_1;\n    global.member = float4(((_e9 == int(0)) ? -4.0 : 1.0), ((_e9 == int(2)) ? 4.0 : -1.0), 0.0, 1.0);\n    return;\n}\n\nfloat4 main(uint param : SV_VertexID) : SV_Position\n{\n    global_1 = int(param);\n    function();\n    float _e6 = global.member.y;\n    global.member.y = -(_e6);\n    float4 _e8 = global.member;\n    return _e8;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/spv-unnamed-gl-per-vertex.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-6438-conflicting-idents.hlsl",
    "content": "struct OurVertexShaderOutput {\n    float4 position : SV_Position;\n    float2 texcoord : LOC0;\n};\n\nstruct VertexOutput_vs {\n    float2 texcoord : LOC0;\n    float4 position : SV_Position;\n};\n\nVertexOutput_vs vs(float2 xy : LOC0)\n{\n    OurVertexShaderOutput vsOutput = (OurVertexShaderOutput)0;\n\n    vsOutput.position = float4(xy, 0.0, 1.0);\n    OurVertexShaderOutput _e6 = vsOutput;\n    const OurVertexShaderOutput ourvertexshaderoutput = _e6;\n    const VertexOutput_vs ourvertexshaderoutput_1 = { ourvertexshaderoutput.texcoord, ourvertexshaderoutput.position };\n    return ourvertexshaderoutput_1;\n}\n\nfloat4 fs() : SV_Target0\n{\n    return float4(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-6438-conflicting-idents.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vs\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"fs\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-6772-unpack-expr-accesses.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid main()\n{\n    int phony = (int4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24)[int(2)];\n    uint phony_1 = (uint4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24).y;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-6772-unpack-expr-accesses.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-7995-unicode-idents.hlsl",
    "content": "ByteAddressBuffer asdf : register(t0);\n\nfloat compute()\n{\n    float _e1 = asfloat(asdf.Load(0));\n    float u03b8_2_ = (_e1 + 9001.0);\n    return u03b8_2_;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const float _e0 = compute();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-7995-unicode-idents.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-8820-multiple-local-invocation-index-id.hlsl",
    "content": "struct Input {\n    uint3 local_invocation_id : SV_GroupThreadID;\n    uint local_invocation_index : SV_GroupIndex;\n};\n\ngroupshared uint wg_var;\n\n[numthreads(1, 1, 1)]\nvoid compute1_(Input input, uint3 local_invocation_id : SV_GroupThreadID)\n{\n    if (all(local_invocation_id == uint3(0u, 0u, 0u))) {\n        wg_var = (uint)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    wg_var = (input.local_invocation_index * 2u);\n    uint _e6 = wg_var;\n    wg_var = (_e6 + input.local_invocation_id.x);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-8820-multiple-local-invocation-index-id.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"compute1_\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-9105-primitive-index-ordering.hlsl",
    "content": "struct FragmentInput_func {\n    float input_location_1 : LOC0;\n    uint index_1 : SV_PrimitiveID;\n    float4 arbitrary_position_1 : SV_Position;\n};\n\nfloat4 func(FragmentInput_func fragmentinput_func) : SV_Target0\n{\n    float input_location = fragmentinput_func.input_location_1;\n    float4 arbitrary_position = fragmentinput_func.arbitrary_position_1;\n    uint index = fragmentinput_func.index_1;\n    return float4(arbitrary_position.xy, input_location, float(index));\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-9105-primitive-index-ordering.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"func\",\n            target_profile:\"ps_5_0\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-abstract-types-return.hlsl",
    "content": "int return_i32_ai()\n{\n    return int(1);\n}\n\nuint return_u32_ai()\n{\n    return 1u;\n}\n\nfloat return_f32_ai()\n{\n    return 1.0;\n}\n\nfloat return_f32_af()\n{\n    return 1.0;\n}\n\nfloat2 return_vec2f32_ai()\n{\n    return (1.0).xx;\n}\n\ntypedef float ret_Constructarray4_float_[4];\nret_Constructarray4_float_ Constructarray4_float_(float arg0, float arg1, float arg2, float arg3) {\n    float ret[4] = { arg0, arg1, arg2, arg3 };\n    return ret;\n}\n\ntypedef float ret_return_arrf32_ai[4];\nret_return_arrf32_ai return_arrf32_ai()\n{\n    return Constructarray4_float_(1.0, 1.0, 1.0, 1.0);\n}\n\nfloat return_const_f32_const_ai()\n{\n    return 1.0;\n}\n\nfloat2 return_vec2f32_const_ai()\n{\n    return (1.0).xx;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const int _e0 = return_i32_ai();\n    const uint _e1 = return_u32_ai();\n    const float _e2 = return_f32_ai();\n    const float _e3 = return_f32_af();\n    const float2 _e4 = return_vec2f32_ai();\n    const float _e5[4] = return_arrf32_ai();\n    const float _e6 = return_const_f32_const_ai();\n    const float2 _e7 = return_vec2f32_const_ai();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-abstract-types-return.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-access.hlsl",
    "content": "typedef struct { float2 _0; float2 _1; } __mat2x2;\nfloat2 __get_col_of_mat2x2(__mat2x2 mat, uint idx) {\n    switch(idx) {\n    case 0: { return mat._0; }\n    case 1: { return mat._1; }\n    default: { return (float2)0; }\n    }\n}\nvoid __set_col_of_mat2x2(__mat2x2 mat, uint idx, float2 value) {\n    switch(idx) {\n    case 0: { mat._0 = value; break; }\n    case 1: { mat._1 = value; break; }\n    }\n}\nvoid __set_el_of_mat2x2(__mat2x2 mat, uint idx, uint vec_idx, float value) {\n    switch(idx) {\n    case 0: { mat._0[vec_idx] = value; break; }\n    case 1: { mat._1[vec_idx] = value; break; }\n    }\n}\n\ntypedef struct { float2 _0; float2 _1; float2 _2; float2 _3; } __mat4x2;\nfloat2 __get_col_of_mat4x2(__mat4x2 mat, uint idx) {\n    switch(idx) {\n    case 0: { return mat._0; }\n    case 1: { return mat._1; }\n    case 2: { return mat._2; }\n    case 3: { return mat._3; }\n    default: { return (float2)0; }\n    }\n}\nvoid __set_col_of_mat4x2(__mat4x2 mat, uint idx, float2 value) {\n    switch(idx) {\n    case 0: { mat._0 = value; break; }\n    case 1: { mat._1 = value; break; }\n    case 2: { mat._2 = value; break; }\n    case 3: { mat._3 = value; break; }\n    }\n}\nvoid __set_el_of_mat4x2(__mat4x2 mat, uint idx, uint vec_idx, float value) {\n    switch(idx) {\n    case 0: { mat._0[vec_idx] = value; break; }\n    case 1: { mat._1[vec_idx] = value; break; }\n    case 2: { mat._2[vec_idx] = value; break; }\n    case 3: { mat._3[vec_idx] = value; break; }\n    }\n}\n\nstruct GlobalConst {\n    uint a;\n    int _pad1_0;\n    int _pad1_1;\n    int _pad1_2;\n    uint3 b;\n    int c;\n};\n\nstruct AlignedWrapper {\n    int value;\n    int _end_pad_0;\n};\n\nstruct Baz {\n    float2 m_0; float2 m_1; float2 m_2;\n};\n\nstruct MatCx2InArray {\n    __mat4x2 am[2];\n};\n\nstruct AssignToMember {\n    uint x;\n};\n\nstruct S {\n    int m;\n};\n\nstruct Inner {\n    int delicious;\n};\n\nstruct Outer {\n    Inner om_nom_nom;\n    uint thing;\n};\n\nGlobalConst ConstructGlobalConst(uint arg0, uint3 arg1, int arg2) {\n    GlobalConst ret = (GlobalConst)0;\n    ret.a = arg0;\n    ret.b = arg1;\n    ret.c = arg2;\n    return ret;\n}\n\nstatic GlobalConst msl_padding_global_const = ConstructGlobalConst(0u, uint3(0u, 0u, 0u), int(0));\nRWByteAddressBuffer bar : register(u0);\ncbuffer baz : register(b1) { Baz baz; }\nRWByteAddressBuffer qux : register(u2);\ncbuffer nested_mat_cx2_ : register(b3) { MatCx2InArray nested_mat_cx2_; }\n\nBaz ConstructBaz(float3x2 arg0) {\n    Baz ret = (Baz)0;\n    ret.m_0 = arg0[0];\n    ret.m_1 = arg0[1];\n    ret.m_2 = arg0[2];\n    return ret;\n}\n\nfloat3x2 GetMatmOnBaz(Baz obj) {\n    return float3x2(obj.m_0, obj.m_1, obj.m_2);\n}\n\nvoid SetMatmOnBaz(Baz obj, float3x2 mat) {\n    obj.m_0 = mat[0];\n    obj.m_1 = mat[1];\n    obj.m_2 = mat[2];\n}\n\nvoid SetMatVecmOnBaz(Baz obj, float2 vec, uint mat_idx) {\n    switch(mat_idx) {\n    case 0: { obj.m_0 = vec; break; }\n    case 1: { obj.m_1 = vec; break; }\n    case 2: { obj.m_2 = vec; break; }\n    }\n}\n\nvoid SetMatScalarmOnBaz(Baz obj, float scalar, uint mat_idx, uint vec_idx) {\n    switch(mat_idx) {\n    case 0: { obj.m_0[vec_idx] = scalar; break; }\n    case 1: { obj.m_1[vec_idx] = scalar; break; }\n    case 2: { obj.m_2[vec_idx] = scalar; break; }\n    }\n}\n\nvoid test_matrix_within_struct_accesses()\n{\n    int idx = int(1);\n    Baz t = ConstructBaz(float3x2((1.0).xx, (2.0).xx, (3.0).xx));\n\n    int _e3 = idx;\n    idx = asint(asuint(_e3) - asuint(int(1)));\n    float3x2 l0_ = GetMatmOnBaz(baz);\n    float2 l1_ = GetMatmOnBaz(baz)[0];\n    int _e14 = idx;\n    float2 l2_ = GetMatmOnBaz(baz)[_e14];\n    float l3_ = GetMatmOnBaz(baz)[0].y;\n    int _e25 = idx;\n    float l4_ = GetMatmOnBaz(baz)[0][_e25];\n    int _e30 = idx;\n    float l5_ = GetMatmOnBaz(baz)[_e30].y;\n    int _e36 = idx;\n    int _e38 = idx;\n    float l6_ = GetMatmOnBaz(baz)[_e36][_e38];\n    int _e51 = idx;\n    idx = asint(asuint(_e51) + asuint(int(1)));\n    SetMatmOnBaz(t, float3x2((6.0).xx, (5.0).xx, (4.0).xx));\n    t.m_0 = (9.0).xx;\n    int _e66 = idx;\n    SetMatVecmOnBaz(t, (90.0).xx, _e66);\n    t.m_0[1] = 10.0;\n    int _e76 = idx;\n    t.m_0[_e76] = 20.0;\n    int _e80 = idx;\n    SetMatScalarmOnBaz(t, 30.0, _e80, 1);\n    int _e85 = idx;\n    int _e87 = idx;\n    SetMatScalarmOnBaz(t, 40.0, _e85, _e87);\n    return;\n}\n\nMatCx2InArray ConstructMatCx2InArray(float4x2 arg0[2]) {\n    MatCx2InArray ret = (MatCx2InArray)0;\n    ret.am = (__mat4x2[2])arg0;\n    return ret;\n}\n\ntypedef float4x2 ret_ZeroValuearray2_float4x2_[2];\nret_ZeroValuearray2_float4x2_ ZeroValuearray2_float4x2_() {\n    return (float4x2[2])0;\n}\n\nvoid test_matrix_within_array_within_struct_accesses()\n{\n    int idx_1 = int(1);\n    MatCx2InArray t_1 = ConstructMatCx2InArray(ZeroValuearray2_float4x2_());\n\n    int _e3 = idx_1;\n    idx_1 = asint(asuint(_e3) - asuint(int(1)));\n    float4x2 l0_1[2] = ((float4x2[2])nested_mat_cx2_.am);\n    float4x2 l1_1 = ((float4x2)nested_mat_cx2_.am[0]);\n    float2 l2_1 = nested_mat_cx2_.am[0]._0;\n    int _e20 = idx_1;\n    float2 l3_1 = __get_col_of_mat4x2(nested_mat_cx2_.am[0], _e20);\n    float l4_1 = nested_mat_cx2_.am[0]._0.y;\n    int _e33 = idx_1;\n    float l5_1 = nested_mat_cx2_.am[0]._0[_e33];\n    int _e39 = idx_1;\n    float l6_1 = __get_col_of_mat4x2(nested_mat_cx2_.am[0], _e39).y;\n    int _e46 = idx_1;\n    int _e48 = idx_1;\n    float l7_ = __get_col_of_mat4x2(nested_mat_cx2_.am[0], _e46)[_e48];\n    int _e55 = idx_1;\n    idx_1 = asint(asuint(_e55) + asuint(int(1)));\n    t_1.am = (__mat4x2[2])ZeroValuearray2_float4x2_();\n    t_1.am[0] = (__mat4x2)float4x2((8.0).xx, (7.0).xx, (6.0).xx, (5.0).xx);\n    t_1.am[0]._0 = (9.0).xx;\n    int _e77 = idx_1;\n    __set_col_of_mat4x2(t_1.am[0], _e77, (90.0).xx);\n    t_1.am[0]._0.y = 10.0;\n    int _e89 = idx_1;\n    t_1.am[0]._0[min(uint(_e89), 1u)] = 20.0;\n    int _e94 = idx_1;\n    __set_el_of_mat4x2(t_1.am[0], _e94, 1, 30.0);\n    int _e100 = idx_1;\n    int _e102 = idx_1;\n    __set_el_of_mat4x2(t_1.am[0], _e100, _e102, 40.0);\n    return;\n}\n\nfloat read_from_private(inout float foo_1)\n{\n    float _e1 = foo_1;\n    return _e1;\n}\n\nfloat test_arr_as_arg(float a[5][10])\n{\n    return a[4][9];\n}\n\nvoid assign_through_ptr_fn(inout uint p)\n{\n    p = 42u;\n    return;\n}\n\ntypedef float4 ret_Constructarray2_float4_[2];\nret_Constructarray2_float4_ Constructarray2_float4_(float4 arg0, float4 arg1) {\n    float4 ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nvoid assign_array_through_ptr_fn(inout float4 foo_2[2])\n{\n    foo_2 = Constructarray2_float4_((1.0).xxxx, (2.0).xxxx);\n    return;\n}\n\nvoid assign_through_ptr()\n{\n    uint val = 33u;\n    float4 arr[2] = Constructarray2_float4_((6.0).xxxx, (7.0).xxxx);\n\n    assign_through_ptr_fn(val);\n    assign_array_through_ptr_fn(arr);\n    return;\n}\n\nuint fetch_arg_ptr_member(inout AssignToMember p_1)\n{\n    uint _e2 = p_1.x;\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_member(inout AssignToMember p_2)\n{\n    p_2.x = 10u;\n    return;\n}\n\nuint fetch_arg_ptr_array_element(inout uint p_3[4])\n{\n    uint _e2 = p_3[1];\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_array_element(inout uint p_4[4])\n{\n    p_4[1] = 10u;\n    return;\n}\n\nvoid assign_to_ptr_components()\n{\n    AssignToMember s1_ = (AssignToMember)0;\n    uint a1_[4] = (uint[4])0;\n\n    assign_to_arg_ptr_member(s1_);\n    const uint _e1 = fetch_arg_ptr_member(s1_);\n    assign_to_arg_ptr_array_element(a1_);\n    const uint _e3 = fetch_arg_ptr_array_element(a1_);\n    return;\n}\n\ntypedef bool ret_Constructarray1_bool_[1];\nret_Constructarray1_bool_ Constructarray1_bool_(bool arg0) {\n    bool ret[1] = { arg0 };\n    return ret;\n}\n\nbool index_ptr(bool value)\n{\n    bool a_1[1] = (bool[1])0;\n\n    a_1 = Constructarray1_bool_(value);\n    bool _e4 = a_1[0];\n    return _e4;\n}\n\nS ConstructS(int arg0) {\n    S ret = (S)0;\n    ret.m = arg0;\n    return ret;\n}\n\nint member_ptr()\n{\n    S s = ConstructS(int(42));\n\n    int _e4 = s.m;\n    return _e4;\n}\n\nOuter ZeroValueOuter() {\n    return (Outer)0;\n}\n\nint let_members_of_members()\n{\n    Inner inner_1 = ZeroValueOuter().om_nom_nom;\n    int delishus_1 = inner_1.delicious;\n    if ((ZeroValueOuter().thing != uint(delishus_1))) {\n    }\n    return ZeroValueOuter().om_nom_nom.delicious;\n}\n\nint var_members_of_members()\n{\n    Outer thing = ZeroValueOuter();\n    Inner inner = (Inner)0;\n    int delishus = (int)0;\n\n    Inner _e3 = thing.om_nom_nom;\n    inner = _e3;\n    int _e6 = inner.delicious;\n    delishus = _e6;\n    uint _e9 = thing.thing;\n    int _e10 = delishus;\n    if ((_e9 != uint(_e10))) {\n    }\n    int _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\n\ntypedef int ret_Constructarray5_int_[5];\nret_Constructarray5_int_ Constructarray5_int_(int arg0, int arg1, int arg2, int arg3, int arg4) {\n    int ret[5] = { arg0, arg1, arg2, arg3, arg4 };\n    return ret;\n}\n\ntypedef float ret_ZeroValuearray5_array10_float__[5][10];\nret_ZeroValuearray5_array10_float__ ZeroValuearray5_array10_float__() {\n    return (float[5][10])0;\n}\n\nint naga_f2i32(float value) {\n    return int(clamp(value, -2147483600.0, 2147483500.0));\n}\n\ntypedef uint2 ret_Constructarray2_uint2_[2];\nret_Constructarray2_uint2_ Constructarray2_uint2_(uint2 arg0, uint2 arg1) {\n    uint2 ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nuint NagaBufferLengthRW(RWByteAddressBuffer buffer)\n{\n    uint ret;\n    buffer.GetDimensions(ret);\n    return ret;\n}\n\nfloat4 foo_vert(uint vi : SV_VertexID) : SV_Position\n{\n    float foo = 0.0;\n    int c2_[5] = (int[5])0;\n\n    float baz_1 = foo;\n    foo = 1.0;\n    GlobalConst phony = msl_padding_global_const;\n    test_matrix_within_struct_accesses();\n    test_matrix_within_array_within_struct_accesses();\n    float4x3 _matrix = float4x3(asfloat(bar.Load3(0+0)), asfloat(bar.Load3(0+16)), asfloat(bar.Load3(0+32)), asfloat(bar.Load3(0+48)));\n    uint2 arr_1[2] = Constructarray2_uint2_(asuint(bar.Load2(144+0)), asuint(bar.Load2(144+8)));\n    float b = asfloat(bar.Load(0+3u*16+0));\n    int a_2 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160));\n    int2 c = asint(qux.Load2(0));\n    const float _e35 = read_from_private(foo);\n    c2_ = Constructarray5_int_(a_2, naga_f2i32(b), int(3), int(4), int(5));\n    c2_[min(uint((vi + 1u)), 4u)] = int(42);\n    int value_1 = c2_[min(uint(vi), 4u)];\n    const float _e49 = test_arr_as_arg(ZeroValuearray5_array10_float__());\n    return float4(mul(float4((value_1).xxxx), _matrix), 2.0);\n}\n\nint2 ZeroValueint2() {\n    return (int2)0;\n}\n\nfloat4 foo_frag() : SV_Target0\n{\n    bar.Store(8+16+0, asuint(1.0));\n    {\n        float4x3 _value2 = float4x3((0.0).xxx, (1.0).xxx, (2.0).xxx, (3.0).xxx);\n        bar.Store3(0+0, asuint(_value2[0]));\n        bar.Store3(0+16, asuint(_value2[1]));\n        bar.Store3(0+32, asuint(_value2[2]));\n        bar.Store3(0+48, asuint(_value2[3]));\n    }\n    {\n        uint2 _value2[2] = Constructarray2_uint2_((0u).xx, (1u).xx);\n        bar.Store2(144+0, asuint(_value2[0]));\n        bar.Store2(144+8, asuint(_value2[1]));\n    }\n    bar.Store(0+8+160, asuint(int(1)));\n    qux.Store2(0, asuint(ZeroValueint2()));\n    return (0.0).xxxx;\n}\n\n[numthreads(1, 1, 1)]\nvoid foo_compute()\n{\n    assign_through_ptr();\n    assign_to_ptr_components();\n    const bool _e1 = index_ptr(true);\n    const int _e2 = member_ptr();\n    const int _e3 = let_members_of_members();\n    const int _e4 = var_members_of_members();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-access.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"foo_vert\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"foo_frag\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n        (\n            entry_point:\"foo_compute\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-aliased-ray-query.hlsl",
    "content": "struct RayDesc_ {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    float3 origin;\n    int _pad5_0;\n    float3 dir;\n    int _end_pad_0;\n};\n\nstruct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    float2 barycentrics;\n    bool front_face;\n    int _pad9_0;\n    int _pad9_1;\n    row_major float4x3 object_to_world;\n    int _pad10_0;\n    row_major float4x3 world_to_object;\n    int _end_pad_0;\n};\n\nRayDesc RayDescFromRayDesc_(RayDesc_ arg0) {\n    RayDesc ret = (RayDesc)0;\n    ret.Origin = arg0.origin;\n    ret.TMin = arg0.tmin;\n    ret.Direction = arg0.dir;\n    ret.TMax = arg0.tmax;\n    return ret;\n}\n\nRaytracingAccelerationStructure acc_struct : register(t0);\n\nRayDesc_ ConstructRayDesc_(uint arg0, uint arg1, float arg2, float arg3, float3 arg4, float3 arg5) {\n    RayDesc_ ret = (RayDesc_)0;\n    ret.flags = arg0;\n    ret.cull_mask = arg1;\n    ret.tmin = arg2;\n    ret.tmax = arg3;\n    ret.origin = arg4;\n    ret.dir = arg5;\n    return ret;\n}\n\nRayIntersection GetCandidateIntersection(RayQuery<RAY_FLAG_NONE> rq, uint rq_tracker) {\n    RayIntersection ret = (RayIntersection)0;\n    if (((rq_tracker & 2) == 2) && !((rq_tracker & 4) == 4)) {\n        CANDIDATE_TYPE kind = rq.CandidateType();\n        if (kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {\n            ret.kind = 1;\n            ret.t = rq.CandidateTriangleRayT();\n            ret.barycentrics = rq.CandidateTriangleBarycentrics();\n            ret.front_face = rq.CandidateTriangleFrontFace();\n        } else {\n            ret.kind = 3;\n        }\n        ret.instance_custom_data = rq.CandidateInstanceID();\n        ret.instance_index = rq.CandidateInstanceIndex();\n        ret.sbt_record_offset = rq.CandidateInstanceContributionToHitGroupIndex();\n        ret.geometry_index = rq.CandidateGeometryIndex();\n        ret.primitive_index = rq.CandidatePrimitiveIndex();\n        ret.object_to_world = rq.CandidateObjectToWorld4x3();\n        ret.world_to_object = rq.CandidateWorldToObject4x3();\n    }\n    return ret;\n}\n\n[numthreads(1, 1, 1)]\nvoid main_candidate()\n{\n    RayQuery<RAY_FLAG_NONE> rq_1;\n    uint naga_query_init_tracker_for_rq_1 = 0;\n\n    float3 pos = (0.0).xxx;\n    float3 dir = float3(0.0, 1.0, 0.0);\n    {\n        RayDesc_ naga_desc = ConstructRayDesc_(4u, 255u, 0.1, 100.0, pos, dir);\n        float naga_tmin = naga_desc.tmin;\n        float naga_tmax = naga_desc.tmax;\n        float3 naga_origin = naga_desc.origin;\n        float3 naga_dir = naga_desc.dir;\n        uint naga_flags = naga_desc.flags;\n        bool naga_tmin_valid = (naga_tmin >= 0.0) && (naga_tmin <= naga_tmax) && !(((asuint(naga_tmin) & 2139095040) == 2139095040) && ((asuint(naga_tmin) & 0x7fffff) != 0));\n        bool naga_tmax_valid = !(((asuint(naga_tmax) & 2139095040) == 2139095040) && ((asuint(naga_tmax) & 0x7fffff) != 0));\n        bool naga_origin_valid = !any((((asuint(naga_origin) & 2139095040) == 2139095040) && ((asuint(naga_origin) & 0x7fffff) != 0)));\n        bool naga_dir_valid = !any((((asuint(naga_dir) & 2139095040) == 2139095040) && ((asuint(naga_dir) & 0x7fffff) != 0)));\n        bool naga_contains_opaque = ((naga_flags & 1) == 1);\n        bool naga_contains_no_opaque = ((naga_flags & 2) == 2);\n        bool naga_contains_cull_opaque = ((naga_flags & 64) == 64);\n        bool naga_contains_cull_no_opaque = ((naga_flags & 128) == 128);\n        bool naga_contains_cull_front = ((naga_flags & 32) == 32);\n        bool naga_contains_cull_back = ((naga_flags & 16) == 16);\n        bool naga_contains_skip_triangles = ((naga_flags & 256) == 256);\n        bool naga_contains_skip_aabbs = ((naga_flags & 512) == 512);\n        bool naga_contains_skip_triangles_aabbs =  (naga_contains_skip_aabbs && naga_contains_skip_triangles) ;\n        bool naga_contains_skip_triangles_cull =  (naga_contains_cull_front && naga_contains_skip_triangles) || (naga_contains_cull_front && naga_contains_cull_back) || (naga_contains_cull_back && naga_contains_skip_triangles) ;\n        bool naga_contains_multiple_opaque =  (naga_contains_cull_no_opaque && naga_contains_opaque) || (naga_contains_cull_no_opaque && naga_contains_no_opaque) || (naga_contains_cull_no_opaque && naga_contains_cull_opaque) || (naga_contains_cull_opaque && naga_contains_opaque) || (naga_contains_cull_opaque && naga_contains_no_opaque) || (naga_contains_no_opaque && naga_contains_opaque) ;\n        if (naga_tmin_valid && naga_tmax_valid && naga_origin_valid && naga_dir_valid && !(naga_contains_skip_triangles_aabbs || naga_contains_skip_triangles_cull || naga_contains_multiple_opaque)) {\n            naga_query_init_tracker_for_rq_1 = naga_query_init_tracker_for_rq_1 | 1;\n            rq_1.TraceRayInline(acc_struct, naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\n        }\n    }\n    RayIntersection intersection = GetCandidateIntersection(rq_1, naga_query_init_tracker_for_rq_1);\n    if ((intersection.kind == 3u)) {\n        if (((naga_query_init_tracker_for_rq_1 & 2) == 2) && !((naga_query_init_tracker_for_rq_1 & 4) == 4)) {\n            CANDIDATE_TYPE naga_kind = rq_1.CandidateType();\n            float naga_tmin = rq_1.RayTMin();\n            float naga_tcurrentmax = rq_1.CommittedRayT();\n            if ((naga_kind == CANDIDATE_PROCEDURAL_PRIMITIVE) && (naga_tmin <=10.0) && (10.0 <= naga_tcurrentmax)) {\n                rq_1.CommitProceduralPrimitiveHit(10.0);\n        }}\n        return;\n    } else {\n        if ((intersection.kind == 1u)) {\n            if (((naga_query_init_tracker_for_rq_1 & 2) == 2) && !((naga_query_init_tracker_for_rq_1 & 4) == 4)) {\n                CANDIDATE_TYPE naga_kind = rq_1.CandidateType();\n                if (naga_kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {\n                    rq_1.CommitNonOpaqueTriangleHit();\n            }}\n            return;\n        } else {\n            if (((naga_query_init_tracker_for_rq_1 & 1) == 1)) {\n                rq_1.Abort();\n            }\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-aliased-ray-query.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main_candidate\",\n            target_profile:\"cs_6_5\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-array-in-ctor.hlsl",
    "content": "struct Ah {\n    float inner[2];\n};\n\nByteAddressBuffer ah : register(t0);\n\ntypedef float ret_Constructarray2_float_[2];\nret_Constructarray2_float_ Constructarray2_float_(float arg0, float arg1) {\n    float ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nAh ConstructAh(float arg0[2]) {\n    Ah ret = (Ah)0;\n    ret.inner = arg0;\n    return ret;\n}\n\n[numthreads(1, 1, 1)]\nvoid cs_main()\n{\n    Ah ah_1 = ConstructAh(Constructarray2_float_(asfloat(ah.Load(0+0)), asfloat(ah.Load(0+4))));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-array-in-ctor.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-array-in-function-return-type.hlsl",
    "content": "typedef float ret_Constructarray2_float_[2];\nret_Constructarray2_float_ Constructarray2_float_(float arg0, float arg1) {\n    float ret[2] = { arg0, arg1 };\n    return ret;\n}\n\ntypedef float ret_ret_array[2];\nret_ret_array ret_array()\n{\n    return Constructarray2_float_(1.0, 2.0);\n}\n\ntypedef float ret_Constructarray3_array2_float__[3][2];\nret_Constructarray3_array2_float__ Constructarray3_array2_float__(float arg0[2], float arg1[2], float arg2[2]) {\n    float ret[3][2] = { arg0, arg1, arg2 };\n    return ret;\n}\n\ntypedef float ret_ret_array_array[3][2];\nret_ret_array_array ret_array_array()\n{\n    const float _e0[2] = ret_array();\n    const float _e1[2] = ret_array();\n    const float _e2[2] = ret_array();\n    return Constructarray3_array2_float__(_e0, _e1, _e2);\n}\n\nfloat4 main() : SV_Target0\n{\n    const float _e0[3][2] = ret_array_array();\n    return float4(_e0[0][0], _e0[0][1], 0.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-array-in-function-return-type.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct _atomic_compare_exchange_result_Sint_8_ {\n    int64_t old_value;\n    bool exchanged;\n    int _end_pad_0;\n};\n\nstruct _atomic_compare_exchange_result_Uint_8_ {\n    uint64_t old_value;\n    bool exchanged;\n    int _end_pad_0;\n};\n\nstatic const uint SIZE = 128u;\n\nRWByteAddressBuffer arr_i64_ : register(u0);\nRWByteAddressBuffer arr_u64_ : register(u1);\n\n[numthreads(1, 1, 1)]\nvoid test_atomic_compare_exchange_i64_()\n{\n    uint i = 0u;\n    int64_t old = (int64_t)0;\n    bool exchanged = (bool)0;\n\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e26 = i;\n            i = (_e26 + 1u);\n        }\n        loop_init = false;\n        uint _e2 = i;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i;\n            int64_t _e8 = arr_i64_.Load<int64_t>(_e6*8);\n            old = _e8;\n            exchanged = false;\n            uint2 loop_bound_1 = uint2(4294967295u, 4294967295u);\n            while(true) {\n                if (all(loop_bound_1 == uint2(0u, 0u))) { break; }\n                loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n                bool _e12 = exchanged;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    int64_t _e14 = old;\n                    int64_t new_ = (_e14 + 10L);\n                    uint _e19 = i;\n                    int64_t _e21 = old;\n                    _atomic_compare_exchange_result_Sint_8_ _e22; arr_i64_.InterlockedCompareExchange64(_e19*8, _e21, new_, _e22.old_value);\n                    _e22.exchanged = (_e22.old_value == _e21);\n                    old = _e22.old_value;\n                    exchanged = _e22.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid test_atomic_compare_exchange_u64_()\n{\n    uint i_1 = 0u;\n    uint64_t old_1 = (uint64_t)0;\n    bool exchanged_1 = (bool)0;\n\n    uint2 loop_bound_2 = uint2(4294967295u, 4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (all(loop_bound_2 == uint2(0u, 0u))) { break; }\n        loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n        if (!loop_init_1) {\n            uint _e26 = i_1;\n            i_1 = (_e26 + 1u);\n        }\n        loop_init_1 = false;\n        uint _e2 = i_1;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i_1;\n            uint64_t _e8 = arr_u64_.Load<uint64_t>(_e6*8);\n            old_1 = _e8;\n            exchanged_1 = false;\n            uint2 loop_bound_3 = uint2(4294967295u, 4294967295u);\n            while(true) {\n                if (all(loop_bound_3 == uint2(0u, 0u))) { break; }\n                loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n                bool _e12 = exchanged_1;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    uint64_t _e14 = old_1;\n                    uint64_t new_1 = (_e14 + 10uL);\n                    uint _e19 = i_1;\n                    uint64_t _e21 = old_1;\n                    _atomic_compare_exchange_result_Uint_8_ _e22; arr_u64_.InterlockedCompareExchange64(_e19*8, _e21, new_1, _e22.old_value);\n                    _e22.exchanged = (_e22.old_value == _e21);\n                    old_1 = _e22.old_value;\n                    exchanged_1 = _e22.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"test_atomic_compare_exchange_i64_\",\n            target_profile:\"cs_6_6\",\n        ),\n        (\n            entry_point:\"test_atomic_compare_exchange_u64_\",\n            target_profile:\"cs_6_6\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicCompareExchange.hlsl",
    "content": "struct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n};\n\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n};\n\nstatic const uint SIZE = 128u;\n\nRWByteAddressBuffer arr_i32_ : register(u0);\nRWByteAddressBuffer arr_u32_ : register(u1);\n\n[numthreads(1, 1, 1)]\nvoid test_atomic_compare_exchange_i32_()\n{\n    uint i = 0u;\n    int old = (int)0;\n    bool exchanged = (bool)0;\n\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e27 = i;\n            i = (_e27 + 1u);\n        }\n        loop_init = false;\n        uint _e2 = i;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i;\n            int _e8 = asint(arr_i32_.Load(_e6*4));\n            old = _e8;\n            exchanged = false;\n            uint2 loop_bound_1 = uint2(4294967295u, 4294967295u);\n            while(true) {\n                if (all(loop_bound_1 == uint2(0u, 0u))) { break; }\n                loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n                bool _e12 = exchanged;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    int _e14 = old;\n                    int new_ = asint((asfloat(_e14) + 1.0));\n                    uint _e20 = i;\n                    int _e22 = old;\n                    _atomic_compare_exchange_result_Sint_4_ _e23; arr_i32_.InterlockedCompareExchange(_e20*4, _e22, new_, _e23.old_value);\n                    _e23.exchanged = (_e23.old_value == _e22);\n                    old = _e23.old_value;\n                    exchanged = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid test_atomic_compare_exchange_u32_()\n{\n    uint i_1 = 0u;\n    uint old_1 = (uint)0;\n    bool exchanged_1 = (bool)0;\n\n    uint2 loop_bound_2 = uint2(4294967295u, 4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (all(loop_bound_2 == uint2(0u, 0u))) { break; }\n        loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n        if (!loop_init_1) {\n            uint _e27 = i_1;\n            i_1 = (_e27 + 1u);\n        }\n        loop_init_1 = false;\n        uint _e2 = i_1;\n        if ((_e2 < SIZE)) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i_1;\n            uint _e8 = asuint(arr_u32_.Load(_e6*4));\n            old_1 = _e8;\n            exchanged_1 = false;\n            uint2 loop_bound_3 = uint2(4294967295u, 4294967295u);\n            while(true) {\n                if (all(loop_bound_3 == uint2(0u, 0u))) { break; }\n                loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n                bool _e12 = exchanged_1;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    uint _e14 = old_1;\n                    uint new_1 = asuint((asfloat(_e14) + 1.0));\n                    uint _e20 = i_1;\n                    uint _e22 = old_1;\n                    _atomic_compare_exchange_result_Uint_4_ _e23; arr_u32_.InterlockedCompareExchange(_e20*4, _e22, new_1, _e23.old_value);\n                    _e23.exchanged = (_e23.old_value == _e22);\n                    old_1 = _e23.old_value;\n                    exchanged_1 = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicCompareExchange.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"test_atomic_compare_exchange_i32_\",\n            target_profile:\"cs_5_1\",\n        ),\n        (\n            entry_point:\"test_atomic_compare_exchange_u32_\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps-int64-min-max.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct Struct {\n    uint64_t atomic_scalar;\n    uint64_t atomic_arr[2];\n};\n\nRWByteAddressBuffer storage_atomic_scalar : register(u0);\nRWByteAddressBuffer storage_atomic_arr : register(u1);\nRWByteAddressBuffer storage_struct : register(u2);\ncbuffer input : register(b3) { uint64_t input; }\n\n[numthreads(2, 1, 1)]\nvoid cs_main(uint3 id : SV_GroupThreadID)\n{\n    uint64_t _e3 = input;\n    storage_atomic_scalar.InterlockedMax64(0, _e3);\n    uint64_t _e7 = input;\n    storage_atomic_arr.InterlockedMax64(8, (1uL + _e7));\n    storage_struct.InterlockedMax64(0, 1uL);\n    storage_struct.InterlockedMax64(8+8, uint64_t(id.x));\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e20 = input;\n    storage_atomic_scalar.InterlockedMin64(0, _e20);\n    uint64_t _e24 = input;\n    storage_atomic_arr.InterlockedMin64(8, (1uL + _e24));\n    storage_struct.InterlockedMin64(0, 1uL);\n    storage_struct.InterlockedMin64(8+8, uint64_t(id.x));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps-int64-min-max.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_6_6\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps-int64.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct Struct {\n    uint64_t atomic_scalar;\n    int64_t atomic_arr[2];\n};\n\nstruct _atomic_compare_exchange_result_Uint_8_ {\n    uint64_t old_value;\n    bool exchanged;\n    int _end_pad_0;\n};\n\nstruct _atomic_compare_exchange_result_Sint_8_ {\n    int64_t old_value;\n    bool exchanged;\n    int _end_pad_0;\n};\n\nRWByteAddressBuffer storage_atomic_scalar : register(u0);\nRWByteAddressBuffer storage_atomic_arr : register(u1);\nRWByteAddressBuffer storage_struct : register(u2);\ngroupshared uint64_t workgroup_atomic_scalar;\ngroupshared int64_t workgroup_atomic_arr[2];\ngroupshared Struct workgroup_struct;\n\n[numthreads(2, 1, 1)]\nvoid cs_main(uint3 id : SV_GroupThreadID)\n{\n    if (all(id == uint3(0u, 0u, 0u))) {\n        workgroup_atomic_scalar = (uint64_t)0;\n        workgroup_atomic_arr = (int64_t[2])0;\n        workgroup_struct = (Struct)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    storage_atomic_scalar.Store(0, 1uL);\n    storage_atomic_arr.Store(8, 1L);\n    storage_struct.Store(0, 1uL);\n    storage_struct.Store(8+8, 1L);\n    workgroup_atomic_scalar = 1uL;\n    workgroup_atomic_arr[1] = 1L;\n    workgroup_struct.atomic_scalar = 1uL;\n    workgroup_struct.atomic_arr[1] = 1L;\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t l0_ = storage_atomic_scalar.Load<uint64_t>(0);\n    int64_t l1_ = storage_atomic_arr.Load<int64_t>(8);\n    uint64_t l2_ = storage_struct.Load<uint64_t>(0);\n    int64_t l3_ = storage_struct.Load<int64_t>(8+8);\n    uint64_t l4_ = workgroup_atomic_scalar;\n    int64_t l5_ = workgroup_atomic_arr[1];\n    uint64_t l6_ = workgroup_struct.atomic_scalar;\n    int64_t l7_ = workgroup_struct.atomic_arr[1];\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e51; storage_atomic_scalar.InterlockedAdd64(0, 1uL, _e51);\n    int64_t _e55; storage_atomic_arr.InterlockedAdd64(8, 1L, _e55);\n    uint64_t _e59; storage_struct.InterlockedAdd64(0, 1uL, _e59);\n    int64_t _e64; storage_struct.InterlockedAdd64(8+8, 1L, _e64);\n    uint64_t _e67; InterlockedAdd(workgroup_atomic_scalar, 1uL, _e67);\n    int64_t _e71; InterlockedAdd(workgroup_atomic_arr[1], 1L, _e71);\n    uint64_t _e75; InterlockedAdd(workgroup_struct.atomic_scalar, 1uL, _e75);\n    int64_t _e80; InterlockedAdd(workgroup_struct.atomic_arr[1], 1L, _e80);\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e83; storage_atomic_scalar.InterlockedAdd64(0, -1uL, _e83);\n    int64_t _e87; storage_atomic_arr.InterlockedAdd64(8, -1L, _e87);\n    uint64_t _e91; storage_struct.InterlockedAdd64(0, -1uL, _e91);\n    int64_t _e96; storage_struct.InterlockedAdd64(8+8, -1L, _e96);\n    uint64_t _e99; InterlockedAdd(workgroup_atomic_scalar, -1uL, _e99);\n    int64_t _e103; InterlockedAdd(workgroup_atomic_arr[1], -1L, _e103);\n    uint64_t _e107; InterlockedAdd(workgroup_struct.atomic_scalar, -1uL, _e107);\n    int64_t _e112; InterlockedAdd(workgroup_struct.atomic_arr[1], -1L, _e112);\n    GroupMemoryBarrierWithGroupSync();\n    storage_atomic_scalar.InterlockedMax64(0, 1uL);\n    storage_atomic_arr.InterlockedMax64(8, 1L);\n    storage_struct.InterlockedMax64(0, 1uL);\n    storage_struct.InterlockedMax64(8+8, 1L);\n    InterlockedMax(workgroup_atomic_scalar, 1uL);\n    InterlockedMax(workgroup_atomic_arr[1], 1L);\n    InterlockedMax(workgroup_struct.atomic_scalar, 1uL);\n    InterlockedMax(workgroup_struct.atomic_arr[1], 1L);\n    GroupMemoryBarrierWithGroupSync();\n    storage_atomic_scalar.InterlockedMin64(0, 1uL);\n    storage_atomic_arr.InterlockedMin64(8, 1L);\n    storage_struct.InterlockedMin64(0, 1uL);\n    storage_struct.InterlockedMin64(8+8, 1L);\n    InterlockedMin(workgroup_atomic_scalar, 1uL);\n    InterlockedMin(workgroup_atomic_arr[1], 1L);\n    InterlockedMin(workgroup_struct.atomic_scalar, 1uL);\n    InterlockedMin(workgroup_struct.atomic_arr[1], 1L);\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e163; storage_atomic_scalar.InterlockedAnd64(0, 1uL, _e163);\n    int64_t _e167; storage_atomic_arr.InterlockedAnd64(8, 1L, _e167);\n    uint64_t _e171; storage_struct.InterlockedAnd64(0, 1uL, _e171);\n    int64_t _e176; storage_struct.InterlockedAnd64(8+8, 1L, _e176);\n    uint64_t _e179; InterlockedAnd(workgroup_atomic_scalar, 1uL, _e179);\n    int64_t _e183; InterlockedAnd(workgroup_atomic_arr[1], 1L, _e183);\n    uint64_t _e187; InterlockedAnd(workgroup_struct.atomic_scalar, 1uL, _e187);\n    int64_t _e192; InterlockedAnd(workgroup_struct.atomic_arr[1], 1L, _e192);\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e195; storage_atomic_scalar.InterlockedOr64(0, 1uL, _e195);\n    int64_t _e199; storage_atomic_arr.InterlockedOr64(8, 1L, _e199);\n    uint64_t _e203; storage_struct.InterlockedOr64(0, 1uL, _e203);\n    int64_t _e208; storage_struct.InterlockedOr64(8+8, 1L, _e208);\n    uint64_t _e211; InterlockedOr(workgroup_atomic_scalar, 1uL, _e211);\n    int64_t _e215; InterlockedOr(workgroup_atomic_arr[1], 1L, _e215);\n    uint64_t _e219; InterlockedOr(workgroup_struct.atomic_scalar, 1uL, _e219);\n    int64_t _e224; InterlockedOr(workgroup_struct.atomic_arr[1], 1L, _e224);\n    GroupMemoryBarrierWithGroupSync();\n    uint64_t _e227; storage_atomic_scalar.InterlockedXor64(0, 1uL, _e227);\n    int64_t _e231; storage_atomic_arr.InterlockedXor64(8, 1L, _e231);\n    uint64_t _e235; storage_struct.InterlockedXor64(0, 1uL, _e235);\n    int64_t _e240; storage_struct.InterlockedXor64(8+8, 1L, _e240);\n    uint64_t _e243; InterlockedXor(workgroup_atomic_scalar, 1uL, _e243);\n    int64_t _e247; InterlockedXor(workgroup_atomic_arr[1], 1L, _e247);\n    uint64_t _e251; InterlockedXor(workgroup_struct.atomic_scalar, 1uL, _e251);\n    int64_t _e256; InterlockedXor(workgroup_struct.atomic_arr[1], 1L, _e256);\n    uint64_t _e259; storage_atomic_scalar.InterlockedExchange64(0, 1uL, _e259);\n    int64_t _e263; storage_atomic_arr.InterlockedExchange64(8, 1L, _e263);\n    uint64_t _e267; storage_struct.InterlockedExchange64(0, 1uL, _e267);\n    int64_t _e272; storage_struct.InterlockedExchange64(8+8, 1L, _e272);\n    uint64_t _e275; InterlockedExchange(workgroup_atomic_scalar, 1uL, _e275);\n    int64_t _e279; InterlockedExchange(workgroup_atomic_arr[1], 1L, _e279);\n    uint64_t _e283; InterlockedExchange(workgroup_struct.atomic_scalar, 1uL, _e283);\n    int64_t _e288; InterlockedExchange(workgroup_struct.atomic_arr[1], 1L, _e288);\n    _atomic_compare_exchange_result_Uint_8_ _e292; storage_atomic_scalar.InterlockedCompareExchange64(0, 1uL, 2uL, _e292.old_value);\n    _e292.exchanged = (_e292.old_value == 1uL);\n    _atomic_compare_exchange_result_Sint_8_ _e297; storage_atomic_arr.InterlockedCompareExchange64(8, 1L, 2L, _e297.old_value);\n    _e297.exchanged = (_e297.old_value == 1L);\n    _atomic_compare_exchange_result_Uint_8_ _e302; storage_struct.InterlockedCompareExchange64(0, 1uL, 2uL, _e302.old_value);\n    _e302.exchanged = (_e302.old_value == 1uL);\n    _atomic_compare_exchange_result_Sint_8_ _e308; storage_struct.InterlockedCompareExchange64(8+8, 1L, 2L, _e308.old_value);\n    _e308.exchanged = (_e308.old_value == 1L);\n    _atomic_compare_exchange_result_Uint_8_ _e312; InterlockedCompareExchange(workgroup_atomic_scalar, 1uL, 2uL, _e312.old_value);\n    _e312.exchanged = (_e312.old_value == 1uL);\n    _atomic_compare_exchange_result_Sint_8_ _e317; InterlockedCompareExchange(workgroup_atomic_arr[1], 1L, 2L, _e317.old_value);\n    _e317.exchanged = (_e317.old_value == 1L);\n    _atomic_compare_exchange_result_Uint_8_ _e322; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1uL, 2uL, _e322.old_value);\n    _e322.exchanged = (_e322.old_value == 1uL);\n    _atomic_compare_exchange_result_Sint_8_ _e328; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], 1L, 2L, _e328.old_value);\n    _e328.exchanged = (_e328.old_value == 1L);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps-int64.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_6_6\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps.hlsl",
    "content": "struct Struct {\n    uint atomic_scalar;\n    int atomic_arr[2];\n};\n\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n};\n\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n};\n\nRWByteAddressBuffer storage_atomic_scalar : register(u0);\nRWByteAddressBuffer storage_atomic_arr : register(u1);\nRWByteAddressBuffer storage_struct : register(u2);\ngroupshared uint workgroup_atomic_scalar;\ngroupshared int workgroup_atomic_arr[2];\ngroupshared Struct workgroup_struct;\n\n[numthreads(2, 1, 1)]\nvoid cs_main(uint3 id : SV_GroupThreadID)\n{\n    if (all(id == uint3(0u, 0u, 0u))) {\n        workgroup_atomic_scalar = (uint)0;\n        workgroup_atomic_arr = (int[2])0;\n        workgroup_struct = (Struct)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    storage_atomic_scalar.Store(0, asuint(1u));\n    storage_atomic_arr.Store(4, asuint(int(1)));\n    storage_struct.Store(0, asuint(1u));\n    storage_struct.Store(4+4, asuint(int(1)));\n    workgroup_atomic_scalar = 1u;\n    workgroup_atomic_arr[1] = int(1);\n    workgroup_struct.atomic_scalar = 1u;\n    workgroup_struct.atomic_arr[1] = int(1);\n    GroupMemoryBarrierWithGroupSync();\n    uint l0_ = asuint(storage_atomic_scalar.Load(0));\n    int l1_ = asint(storage_atomic_arr.Load(4));\n    uint l2_ = asuint(storage_struct.Load(0));\n    int l3_ = asint(storage_struct.Load(4+4));\n    uint l4_ = workgroup_atomic_scalar;\n    int l5_ = workgroup_atomic_arr[1];\n    uint l6_ = workgroup_struct.atomic_scalar;\n    int l7_ = workgroup_struct.atomic_arr[1];\n    GroupMemoryBarrierWithGroupSync();\n    uint _e51; storage_atomic_scalar.InterlockedAdd(0, 1u, _e51);\n    int _e55; storage_atomic_arr.InterlockedAdd(4, int(1), _e55);\n    uint _e59; storage_struct.InterlockedAdd(0, 1u, _e59);\n    int _e64; storage_struct.InterlockedAdd(4+4, int(1), _e64);\n    uint _e67; InterlockedAdd(workgroup_atomic_scalar, 1u, _e67);\n    int _e71; InterlockedAdd(workgroup_atomic_arr[1], int(1), _e71);\n    uint _e75; InterlockedAdd(workgroup_struct.atomic_scalar, 1u, _e75);\n    int _e80; InterlockedAdd(workgroup_struct.atomic_arr[1], int(1), _e80);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e83; storage_atomic_scalar.InterlockedAdd(0, -1u, _e83);\n    int _e87; storage_atomic_arr.InterlockedAdd(4, -int(1), _e87);\n    uint _e91; storage_struct.InterlockedAdd(0, -1u, _e91);\n    int _e96; storage_struct.InterlockedAdd(4+4, -int(1), _e96);\n    uint _e99; InterlockedAdd(workgroup_atomic_scalar, -1u, _e99);\n    int _e103; InterlockedAdd(workgroup_atomic_arr[1], -int(1), _e103);\n    uint _e107; InterlockedAdd(workgroup_struct.atomic_scalar, -1u, _e107);\n    int _e112; InterlockedAdd(workgroup_struct.atomic_arr[1], -int(1), _e112);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e115; storage_atomic_scalar.InterlockedMax(0, 1u, _e115);\n    int _e119; storage_atomic_arr.InterlockedMax(4, int(1), _e119);\n    uint _e123; storage_struct.InterlockedMax(0, 1u, _e123);\n    int _e128; storage_struct.InterlockedMax(4+4, int(1), _e128);\n    uint _e131; InterlockedMax(workgroup_atomic_scalar, 1u, _e131);\n    int _e135; InterlockedMax(workgroup_atomic_arr[1], int(1), _e135);\n    uint _e139; InterlockedMax(workgroup_struct.atomic_scalar, 1u, _e139);\n    int _e144; InterlockedMax(workgroup_struct.atomic_arr[1], int(1), _e144);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e147; storage_atomic_scalar.InterlockedMin(0, 1u, _e147);\n    int _e151; storage_atomic_arr.InterlockedMin(4, int(1), _e151);\n    uint _e155; storage_struct.InterlockedMin(0, 1u, _e155);\n    int _e160; storage_struct.InterlockedMin(4+4, int(1), _e160);\n    uint _e163; InterlockedMin(workgroup_atomic_scalar, 1u, _e163);\n    int _e167; InterlockedMin(workgroup_atomic_arr[1], int(1), _e167);\n    uint _e171; InterlockedMin(workgroup_struct.atomic_scalar, 1u, _e171);\n    int _e176; InterlockedMin(workgroup_struct.atomic_arr[1], int(1), _e176);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e179; storage_atomic_scalar.InterlockedAnd(0, 1u, _e179);\n    int _e183; storage_atomic_arr.InterlockedAnd(4, int(1), _e183);\n    uint _e187; storage_struct.InterlockedAnd(0, 1u, _e187);\n    int _e192; storage_struct.InterlockedAnd(4+4, int(1), _e192);\n    uint _e195; InterlockedAnd(workgroup_atomic_scalar, 1u, _e195);\n    int _e199; InterlockedAnd(workgroup_atomic_arr[1], int(1), _e199);\n    uint _e203; InterlockedAnd(workgroup_struct.atomic_scalar, 1u, _e203);\n    int _e208; InterlockedAnd(workgroup_struct.atomic_arr[1], int(1), _e208);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e211; storage_atomic_scalar.InterlockedOr(0, 1u, _e211);\n    int _e215; storage_atomic_arr.InterlockedOr(4, int(1), _e215);\n    uint _e219; storage_struct.InterlockedOr(0, 1u, _e219);\n    int _e224; storage_struct.InterlockedOr(4+4, int(1), _e224);\n    uint _e227; InterlockedOr(workgroup_atomic_scalar, 1u, _e227);\n    int _e231; InterlockedOr(workgroup_atomic_arr[1], int(1), _e231);\n    uint _e235; InterlockedOr(workgroup_struct.atomic_scalar, 1u, _e235);\n    int _e240; InterlockedOr(workgroup_struct.atomic_arr[1], int(1), _e240);\n    GroupMemoryBarrierWithGroupSync();\n    uint _e243; storage_atomic_scalar.InterlockedXor(0, 1u, _e243);\n    int _e247; storage_atomic_arr.InterlockedXor(4, int(1), _e247);\n    uint _e251; storage_struct.InterlockedXor(0, 1u, _e251);\n    int _e256; storage_struct.InterlockedXor(4+4, int(1), _e256);\n    uint _e259; InterlockedXor(workgroup_atomic_scalar, 1u, _e259);\n    int _e263; InterlockedXor(workgroup_atomic_arr[1], int(1), _e263);\n    uint _e267; InterlockedXor(workgroup_struct.atomic_scalar, 1u, _e267);\n    int _e272; InterlockedXor(workgroup_struct.atomic_arr[1], int(1), _e272);\n    uint _e275; storage_atomic_scalar.InterlockedExchange(0, 1u, _e275);\n    int _e279; storage_atomic_arr.InterlockedExchange(4, int(1), _e279);\n    uint _e283; storage_struct.InterlockedExchange(0, 1u, _e283);\n    int _e288; storage_struct.InterlockedExchange(4+4, int(1), _e288);\n    uint _e291; InterlockedExchange(workgroup_atomic_scalar, 1u, _e291);\n    int _e295; InterlockedExchange(workgroup_atomic_arr[1], int(1), _e295);\n    uint _e299; InterlockedExchange(workgroup_struct.atomic_scalar, 1u, _e299);\n    int _e304; InterlockedExchange(workgroup_struct.atomic_arr[1], int(1), _e304);\n    _atomic_compare_exchange_result_Uint_4_ _e308; storage_atomic_scalar.InterlockedCompareExchange(0, 1u, 2u, _e308.old_value);\n    _e308.exchanged = (_e308.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e313; storage_atomic_arr.InterlockedCompareExchange(4, int(1), int(2), _e313.old_value);\n    _e313.exchanged = (_e313.old_value == int(1));\n    _atomic_compare_exchange_result_Uint_4_ _e318; storage_struct.InterlockedCompareExchange(0, 1u, 2u, _e318.old_value);\n    _e318.exchanged = (_e318.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e324; storage_struct.InterlockedCompareExchange(4+4, int(1), int(2), _e324.old_value);\n    _e324.exchanged = (_e324.old_value == int(1));\n    _atomic_compare_exchange_result_Uint_4_ _e328; InterlockedCompareExchange(workgroup_atomic_scalar, 1u, 2u, _e328.old_value);\n    _e328.exchanged = (_e328.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e333; InterlockedCompareExchange(workgroup_atomic_arr[1], int(1), int(2), _e333.old_value);\n    _e333.exchanged = (_e333.old_value == int(1));\n    _atomic_compare_exchange_result_Uint_4_ _e338; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1u, 2u, _e338.old_value);\n    _e338.exchanged = (_e338.old_value == 1u);\n    _atomic_compare_exchange_result_Sint_4_ _e344; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], int(1), int(2), _e344.old_value);\n    _e344.exchanged = (_e344.old_value == int(1));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicOps.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicTexture-int64.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nRWTexture2D<uint64_t> image : register(u0);\n\n[numthreads(2, 1, 1)]\nvoid cs_main(uint3 id : SV_GroupThreadID)\n{\n    InterlockedMax(image[int2(int(0), int(0))],1uL);\n    GroupMemoryBarrierWithGroupSync();\n    InterlockedMin(image[int2(int(0), int(0))],1uL);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicTexture-int64.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_6_6\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicTexture.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nRWTexture2D<uint> image_u : register(u0);\nRWTexture2D<int> image_s : register(u1);\n\n[numthreads(2, 1, 1)]\nvoid cs_main(uint3 id : SV_GroupThreadID)\n{\n    InterlockedMax(image_u[int2(int(0), int(0))],1u);\n    InterlockedMin(image_u[int2(int(0), int(0))],1u);\n    InterlockedAdd(image_u[int2(int(0), int(0))],1u);\n    InterlockedAnd(image_u[int2(int(0), int(0))],1u);\n    InterlockedOr(image_u[int2(int(0), int(0))],1u);\n    InterlockedXor(image_u[int2(int(0), int(0))],1u);\n    InterlockedMax(image_s[int2(int(0), int(0))],int(1));\n    InterlockedMin(image_s[int2(int(0), int(0))],int(1));\n    InterlockedAdd(image_s[int2(int(0), int(0))],int(1));\n    InterlockedAnd(image_s[int2(int(0), int(0))],int(1));\n    InterlockedOr(image_s[int2(int(0), int(0))],int(1));\n    InterlockedXor(image_s[int2(int(0), int(0))],int(1));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-atomicTexture.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"cs_main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-barycentrics.hlsl",
    "content": "struct FragmentInput_fs_main {\n    float3 bary_2 : SV_Barycentrics;\n};\n\nstruct FragmentInput_fs_main_no_perspective {\n    noperspective float3 bary_3 : SV_Barycentrics;\n};\n\nfloat4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0\n{\n    float3 bary = fragmentinput_fs_main.bary_2;\n    return float4(bary, 1.0);\n}\n\nfloat4 fs_main_no_perspective(FragmentInput_fs_main_no_perspective fragmentinput_fs_main_no_perspective) : SV_Target0\n{\n    float3 bary_1 = fragmentinput_fs_main_no_perspective.bary_3;\n    return float4(bary_1, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-barycentrics.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"fs_main\",\n            target_profile:\"ps_6_1\",\n        ),\n        (\n            entry_point:\"fs_main_no_perspective\",\n            target_profile:\"ps_6_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-binding-arrays.hlsl",
    "content": "struct UniformIndex {\n    uint index;\n};\n\nstruct FragmentIn {\n    nointerpolation uint index : LOC0;\n};\n\nTexture2D<float4> texture_array_unbounded[10] : register(t0);\nTexture2D<float4> texture_array_bounded[5] : register(t0, space1);\nTexture2DArray<float4> texture_array_2darray[5] : register(t0, space2);\nTexture2DMS<float4> texture_array_multisampled[5] : register(t0, space3);\nTexture2D<float> texture_array_depth[5] : register(t0, space4);\nRWTexture2D<float4> texture_array_storage[5] : register(u0, space5);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space255);\nstatic const uint samp = 0;\nstatic const uint samp_comp = 0;\ncbuffer uni : register(b0, space8) { UniformIndex uni; }\n\nstruct FragmentInput_main {\n    nointerpolation uint index : LOC0;\n};\n\nuint2 NagaDimensions2D(Texture2D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nuint NagaNumLayers2DArray(Texture2DArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaNumLevels2D(Texture2D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.z;\n}\n\nuint NagaMSNumSamples2D(Texture2DMS<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(ret.x, ret.y, ret.z);\n    return ret.z;\n}\n\nfloat4 main(FragmentInput_main fragmentinput_main) : SV_Target0\n{\n    FragmentIn fragment_in = { fragmentinput_main.index };\n    uint u1_ = 0u;\n    uint2 u2_ = (0u).xx;\n    float v1_ = 0.0;\n    float4 v4_ = (0.0).xxxx;\n\n    uint uniform_index = uni.index;\n    uint non_uniform_index = fragment_in.index;\n    float2 uv = (0.0).xx;\n    int2 pix = (int(0)).xx;\n    uint2 _e19 = u2_;\n    u2_ = (_e19 + NagaDimensions2D(texture_array_unbounded[0]));\n    uint2 _e24 = u2_;\n    u2_ = (_e24 + NagaDimensions2D(texture_array_unbounded[uniform_index]));\n    uint2 _e29 = u2_;\n    u2_ = (_e29 + NagaDimensions2D(texture_array_unbounded[NonUniformResourceIndex(non_uniform_index)]));\n    float4 _e34 = v4_;\n    float4 _e39 = texture_array_bounded[0].Gather(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + 0]], uv);\n    v4_ = (_e34 + _e39);\n    float4 _e41 = v4_;\n    float4 _e46 = texture_array_bounded[uniform_index].Gather(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + uniform_index]], uv);\n    v4_ = (_e41 + _e46);\n    float4 _e48 = v4_;\n    float4 _e53 = texture_array_bounded[NonUniformResourceIndex(non_uniform_index)].Gather(nagaSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp + non_uniform_index])], uv);\n    v4_ = (_e48 + _e53);\n    float4 _e55 = v4_;\n    float4 _e61 = texture_array_depth[0].GatherCmp(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + 0]], uv, 0.0);\n    v4_ = (_e55 + _e61);\n    float4 _e63 = v4_;\n    float4 _e69 = texture_array_depth[uniform_index].GatherCmp(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + uniform_index]], uv, 0.0);\n    v4_ = (_e63 + _e69);\n    float4 _e71 = v4_;\n    float4 _e77 = texture_array_depth[NonUniformResourceIndex(non_uniform_index)].GatherCmp(nagaComparisonSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp_comp + non_uniform_index])], uv, 0.0);\n    v4_ = (_e71 + _e77);\n    float4 _e79 = v4_;\n    float4 _e83 = texture_array_unbounded[0].Load(int3(pix, int(0)));\n    v4_ = (_e79 + _e83);\n    float4 _e85 = v4_;\n    float4 _e89 = texture_array_unbounded[uniform_index].Load(int3(pix, int(0)));\n    v4_ = (_e85 + _e89);\n    float4 _e91 = v4_;\n    float4 _e95 = texture_array_unbounded[NonUniformResourceIndex(non_uniform_index)].Load(int3(pix, int(0)));\n    v4_ = (_e91 + _e95);\n    uint _e97 = u1_;\n    u1_ = (_e97 + NagaNumLayers2DArray(texture_array_2darray[0]));\n    uint _e102 = u1_;\n    u1_ = (_e102 + NagaNumLayers2DArray(texture_array_2darray[uniform_index]));\n    uint _e107 = u1_;\n    u1_ = (_e107 + NagaNumLayers2DArray(texture_array_2darray[NonUniformResourceIndex(non_uniform_index)]));\n    uint _e112 = u1_;\n    u1_ = (_e112 + NagaNumLevels2D(texture_array_bounded[0]));\n    uint _e117 = u1_;\n    u1_ = (_e117 + NagaNumLevels2D(texture_array_bounded[uniform_index]));\n    uint _e122 = u1_;\n    u1_ = (_e122 + NagaNumLevels2D(texture_array_bounded[NonUniformResourceIndex(non_uniform_index)]));\n    uint _e127 = u1_;\n    u1_ = (_e127 + NagaMSNumSamples2D(texture_array_multisampled[0]));\n    uint _e132 = u1_;\n    u1_ = (_e132 + NagaMSNumSamples2D(texture_array_multisampled[uniform_index]));\n    uint _e137 = u1_;\n    u1_ = (_e137 + NagaMSNumSamples2D(texture_array_multisampled[NonUniformResourceIndex(non_uniform_index)]));\n    float4 _e142 = v4_;\n    float4 _e147 = texture_array_bounded[0].Sample(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + 0]], uv);\n    v4_ = (_e142 + _e147);\n    float4 _e149 = v4_;\n    float4 _e154 = texture_array_bounded[uniform_index].Sample(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + uniform_index]], uv);\n    v4_ = (_e149 + _e154);\n    float4 _e156 = v4_;\n    float4 _e161 = texture_array_bounded[NonUniformResourceIndex(non_uniform_index)].Sample(nagaSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp + non_uniform_index])], uv);\n    v4_ = (_e156 + _e161);\n    float4 _e163 = v4_;\n    float4 _e169 = texture_array_bounded[0].SampleBias(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + 0]], uv, 0.0);\n    v4_ = (_e163 + _e169);\n    float4 _e171 = v4_;\n    float4 _e177 = texture_array_bounded[uniform_index].SampleBias(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + uniform_index]], uv, 0.0);\n    v4_ = (_e171 + _e177);\n    float4 _e179 = v4_;\n    float4 _e185 = texture_array_bounded[NonUniformResourceIndex(non_uniform_index)].SampleBias(nagaSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp + non_uniform_index])], uv, 0.0);\n    v4_ = (_e179 + _e185);\n    float _e187 = v1_;\n    float _e193 = texture_array_depth[0].SampleCmp(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + 0]], uv, 0.0);\n    v1_ = (_e187 + _e193);\n    float _e195 = v1_;\n    float _e201 = texture_array_depth[uniform_index].SampleCmp(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + uniform_index]], uv, 0.0);\n    v1_ = (_e195 + _e201);\n    float _e203 = v1_;\n    float _e209 = texture_array_depth[NonUniformResourceIndex(non_uniform_index)].SampleCmp(nagaComparisonSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp_comp + non_uniform_index])], uv, 0.0);\n    v1_ = (_e203 + _e209);\n    float _e211 = v1_;\n    float _e217 = texture_array_depth[0].SampleCmpLevelZero(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + 0]], uv, 0.0);\n    v1_ = (_e211 + _e217);\n    float _e219 = v1_;\n    float _e225 = texture_array_depth[uniform_index].SampleCmpLevelZero(nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[samp_comp + uniform_index]], uv, 0.0);\n    v1_ = (_e219 + _e225);\n    float _e227 = v1_;\n    float _e233 = texture_array_depth[NonUniformResourceIndex(non_uniform_index)].SampleCmpLevelZero(nagaComparisonSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp_comp + non_uniform_index])], uv, 0.0);\n    v1_ = (_e227 + _e233);\n    float4 _e235 = v4_;\n    float4 _e240 = texture_array_bounded[0].SampleGrad(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + 0]], uv, uv, uv);\n    v4_ = (_e235 + _e240);\n    float4 _e242 = v4_;\n    float4 _e247 = texture_array_bounded[uniform_index].SampleGrad(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + uniform_index]], uv, uv, uv);\n    v4_ = (_e242 + _e247);\n    float4 _e249 = v4_;\n    float4 _e254 = texture_array_bounded[NonUniformResourceIndex(non_uniform_index)].SampleGrad(nagaSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp + non_uniform_index])], uv, uv, uv);\n    v4_ = (_e249 + _e254);\n    float4 _e256 = v4_;\n    float4 _e262 = texture_array_bounded[0].SampleLevel(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + 0]], uv, 0.0);\n    v4_ = (_e256 + _e262);\n    float4 _e264 = v4_;\n    float4 _e270 = texture_array_bounded[uniform_index].SampleLevel(nagaSamplerHeap[nagaGroup0SamplerIndexArray[samp + uniform_index]], uv, 0.0);\n    v4_ = (_e264 + _e270);\n    float4 _e272 = v4_;\n    float4 _e278 = texture_array_bounded[NonUniformResourceIndex(non_uniform_index)].SampleLevel(nagaSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp + non_uniform_index])], uv, 0.0);\n    v4_ = (_e272 + _e278);\n    float4 _e282 = v4_;\n    texture_array_storage[0][pix] = _e282;\n    float4 _e285 = v4_;\n    texture_array_storage[uniform_index][pix] = _e285;\n    float4 _e288 = v4_;\n    texture_array_storage[NonUniformResourceIndex(non_uniform_index)][pix] = _e288;\n    uint2 _e289 = u2_;\n    uint _e290 = u1_;\n    float2 v2_ = float2((_e289 + (_e290).xx));\n    float4 _e294 = v4_;\n    float _e301 = v1_;\n    return ((_e294 + float4(v2_.x, v2_.y, v2_.x, v2_.y)) + (_e301).xxxx);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-binding-arrays.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bitcast.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid main()\n{\n    int2 i2_ = (int(0)).xx;\n    int3 i3_ = (int(0)).xxx;\n    int4 i4_ = (int(0)).xxxx;\n    uint2 u2_ = (0u).xx;\n    uint3 u3_ = (0u).xxx;\n    uint4 u4_ = (0u).xxxx;\n    float2 f2_ = (0.0).xx;\n    float3 f3_ = (0.0).xxx;\n    float4 f4_ = (0.0).xxxx;\n\n    int2 _e27 = i2_;\n    u2_ = asuint(_e27);\n    int3 _e29 = i3_;\n    u3_ = asuint(_e29);\n    int4 _e31 = i4_;\n    u4_ = asuint(_e31);\n    uint2 _e33 = u2_;\n    i2_ = asint(_e33);\n    uint3 _e35 = u3_;\n    i3_ = asint(_e35);\n    uint4 _e37 = u4_;\n    i4_ = asint(_e37);\n    int2 _e39 = i2_;\n    f2_ = asfloat(_e39);\n    int3 _e41 = i3_;\n    f3_ = asfloat(_e41);\n    int4 _e43 = i4_;\n    f4_ = asfloat(_e43);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bitcast.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bits.hlsl",
    "content": "int naga_insertBits(\n    int e,\n    int newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nint2 naga_insertBits(\n    int2 e,\n    int2 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nint3 naga_insertBits(\n    int3 e,\n    int3 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nint4 naga_insertBits(\n    int4 e,\n    int4 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nuint naga_insertBits(\n    uint e,\n    uint newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nuint2 naga_insertBits(\n    uint2 e,\n    uint2 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nuint3 naga_insertBits(\n    uint3 e,\n    uint3 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nuint4 naga_insertBits(\n    uint4 e,\n    uint4 newbits,\n    uint offset,\n    uint count\n) {\n    uint w = 32u;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    uint mask = ((4294967295u >> (32u - c)) << o);\n    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));\n}\nint naga_extractBits(\n    int e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nint2 naga_extractBits(\n    int2 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nint3 naga_extractBits(\n    int3 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nint4 naga_extractBits(\n    int4 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nuint naga_extractBits(\n    uint e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nuint2 naga_extractBits(\n    uint2 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nuint3 naga_extractBits(\n    uint3 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\nuint4 naga_extractBits(\n    uint4 e,\n    uint offset,\n    uint count\n) {\n    uint w = 32;\n    uint o = min(offset, w);\n    uint c = min(count, w - o);\n    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));\n}\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    int i = int(0);\n    int2 i2_ = (int(0)).xx;\n    int3 i3_ = (int(0)).xxx;\n    int4 i4_ = (int(0)).xxxx;\n    uint u = 0u;\n    uint2 u2_ = (0u).xx;\n    uint3 u3_ = (0u).xxx;\n    uint4 u4_ = (0u).xxxx;\n    float2 f2_ = (0.0).xx;\n    float4 f4_ = (0.0).xxxx;\n\n    float4 _e28 = f4_;\n    u = uint((int(round(clamp(_e28[0], -1.0, 1.0) * 127.0)) & 0xFF) | ((int(round(clamp(_e28[1], -1.0, 1.0) * 127.0)) & 0xFF) << 8) | ((int(round(clamp(_e28[2], -1.0, 1.0) * 127.0)) & 0xFF) << 16) | ((int(round(clamp(_e28[3], -1.0, 1.0) * 127.0)) & 0xFF) << 24));\n    float4 _e30 = f4_;\n    u = (uint(round(clamp(_e30[0], 0.0, 1.0) * 255.0)) | uint(round(clamp(_e30[1], 0.0, 1.0) * 255.0)) << 8 | uint(round(clamp(_e30[2], 0.0, 1.0) * 255.0)) << 16 | uint(round(clamp(_e30[3], 0.0, 1.0) * 255.0)) << 24);\n    float2 _e32 = f2_;\n    u = uint((int(round(clamp(_e32[0], -1.0, 1.0) * 32767.0)) & 0xFFFF) | ((int(round(clamp(_e32[1], -1.0, 1.0) * 32767.0)) & 0xFFFF) << 16));\n    float2 _e34 = f2_;\n    u = (uint(round(clamp(_e34[0], 0.0, 1.0) * 65535.0)) | uint(round(clamp(_e34[1], 0.0, 1.0) * 65535.0)) << 16);\n    float2 _e36 = f2_;\n    u = (f32tof16(_e36[0]) | f32tof16(_e36[1]) << 16);\n    int4 _e38 = i4_;\n    u = uint((_e38[0] & 0xFF) | ((_e38[1] & 0xFF) << 8) | ((_e38[2] & 0xFF) << 16) | ((_e38[3] & 0xFF) << 24));\n    uint4 _e40 = u4_;\n    u = (_e40[0] & 0xFF) | ((_e40[1] & 0xFF) << 8) | ((_e40[2] & 0xFF) << 16) | ((_e40[3] & 0xFF) << 24);\n    int4 _e42 = i4_;\n    u = uint((clamp(_e42, -128, 127)[0] & 0xFF) | ((clamp(_e42, -128, 127)[1] & 0xFF) << 8) | ((clamp(_e42, -128, 127)[2] & 0xFF) << 16) | ((clamp(_e42, -128, 127)[3] & 0xFF) << 24));\n    uint4 _e44 = u4_;\n    u = (clamp(_e44, 0, 255)[0] & 0xFF) | ((clamp(_e44, 0, 255)[1] & 0xFF) << 8) | ((clamp(_e44, 0, 255)[2] & 0xFF) << 16) | ((clamp(_e44, 0, 255)[3] & 0xFF) << 24);\n    uint _e46 = u;\n    f4_ = (float4(int4(_e46 << 24, _e46 << 16, _e46 << 8, _e46) >> 24) / 127.0);\n    uint _e48 = u;\n    f4_ = (float4(_e48 & 0xFF, _e48 >> 8 & 0xFF, _e48 >> 16 & 0xFF, _e48 >> 24) / 255.0);\n    uint _e50 = u;\n    f2_ = (float2(int2(_e50 << 16, _e50) >> 16) / 32767.0);\n    uint _e52 = u;\n    f2_ = (float2(_e52 & 0xFFFF, _e52 >> 16) / 65535.0);\n    uint _e54 = u;\n    f2_ = float2(f16tof32(_e54), f16tof32((_e54) >> 16));\n    uint _e56 = u;\n    i4_ = (int4(_e56, _e56 >> 8, _e56 >> 16, _e56 >> 24) << 24 >> 24);\n    uint _e58 = u;\n    u4_ = (uint4(_e58, _e58 >> 8, _e58 >> 16, _e58 >> 24) << 24 >> 24);\n    int _e60 = i;\n    int _e61 = i;\n    i = naga_insertBits(_e60, _e61, 5u, 10u);\n    int2 _e65 = i2_;\n    int2 _e66 = i2_;\n    i2_ = naga_insertBits(_e65, _e66, 5u, 10u);\n    int3 _e70 = i3_;\n    int3 _e71 = i3_;\n    i3_ = naga_insertBits(_e70, _e71, 5u, 10u);\n    int4 _e75 = i4_;\n    int4 _e76 = i4_;\n    i4_ = naga_insertBits(_e75, _e76, 5u, 10u);\n    uint _e80 = u;\n    uint _e81 = u;\n    u = naga_insertBits(_e80, _e81, 5u, 10u);\n    uint2 _e85 = u2_;\n    uint2 _e86 = u2_;\n    u2_ = naga_insertBits(_e85, _e86, 5u, 10u);\n    uint3 _e90 = u3_;\n    uint3 _e91 = u3_;\n    u3_ = naga_insertBits(_e90, _e91, 5u, 10u);\n    uint4 _e95 = u4_;\n    uint4 _e96 = u4_;\n    u4_ = naga_insertBits(_e95, _e96, 5u, 10u);\n    int _e100 = i;\n    i = naga_extractBits(_e100, 5u, 10u);\n    int2 _e104 = i2_;\n    i2_ = naga_extractBits(_e104, 5u, 10u);\n    int3 _e108 = i3_;\n    i3_ = naga_extractBits(_e108, 5u, 10u);\n    int4 _e112 = i4_;\n    i4_ = naga_extractBits(_e112, 5u, 10u);\n    uint _e116 = u;\n    u = naga_extractBits(_e116, 5u, 10u);\n    uint2 _e120 = u2_;\n    u2_ = naga_extractBits(_e120, 5u, 10u);\n    uint3 _e124 = u3_;\n    u3_ = naga_extractBits(_e124, 5u, 10u);\n    uint4 _e128 = u4_;\n    u4_ = naga_extractBits(_e128, 5u, 10u);\n    int _e132 = i;\n    i = asint(firstbitlow(_e132));\n    uint2 _e134 = u2_;\n    u2_ = firstbitlow(_e134);\n    int3 _e136 = i3_;\n    i3_ = asint(firstbithigh(_e136));\n    uint3 _e138 = u3_;\n    u3_ = firstbithigh(_e138);\n    int _e140 = i;\n    i = asint(firstbithigh(_e140));\n    uint _e142 = u;\n    u = firstbithigh(_e142);\n    int _e144 = i;\n    i = asint(countbits(asuint(_e144)));\n    int2 _e146 = i2_;\n    i2_ = asint(countbits(asuint(_e146)));\n    int3 _e148 = i3_;\n    i3_ = asint(countbits(asuint(_e148)));\n    int4 _e150 = i4_;\n    i4_ = asint(countbits(asuint(_e150)));\n    uint _e152 = u;\n    u = countbits(_e152);\n    uint2 _e154 = u2_;\n    u2_ = countbits(_e154);\n    uint3 _e156 = u3_;\n    u3_ = countbits(_e156);\n    uint4 _e158 = u4_;\n    u4_ = countbits(_e158);\n    int _e160 = i;\n    i = asint(reversebits(asuint(_e160)));\n    int2 _e162 = i2_;\n    i2_ = asint(reversebits(asuint(_e162)));\n    int3 _e164 = i3_;\n    i3_ = asint(reversebits(asuint(_e164)));\n    int4 _e166 = i4_;\n    i4_ = asint(reversebits(asuint(_e166)));\n    uint _e168 = u;\n    u = reversebits(_e168);\n    uint2 _e170 = u2_;\n    u2_ = reversebits(_e170);\n    uint3 _e172 = u3_;\n    u3_ = reversebits(_e172);\n    uint4 _e174 = u4_;\n    u4_ = reversebits(_e174);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bits.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-boids.hlsl",
    "content": "struct Particle {\n    float2 pos;\n    float2 vel;\n};\n\nstruct SimParams {\n    float deltaT;\n    float rule1Distance;\n    float rule2Distance;\n    float rule3Distance;\n    float rule1Scale;\n    float rule2Scale;\n    float rule3Scale;\n};\n\nstatic const uint NUM_PARTICLES = 1500u;\n\ncbuffer params : register(b0) { SimParams params; }\nByteAddressBuffer particlesSrc : register(t1);\nRWByteAddressBuffer particlesDst : register(u2);\n\n[numthreads(64, 1, 1)]\nvoid main(uint3 global_invocation_id : SV_DispatchThreadID)\n{\n    float2 vPos = (float2)0;\n    float2 vVel = (float2)0;\n    float2 cMass = float2(0.0, 0.0);\n    float2 cVel = float2(0.0, 0.0);\n    float2 colVel = float2(0.0, 0.0);\n    int cMassCount = int(0);\n    int cVelCount = int(0);\n    float2 pos = (float2)0;\n    float2 vel = (float2)0;\n    uint i = 0u;\n\n    uint index = global_invocation_id.x;\n    if ((index >= NUM_PARTICLES)) {\n        return;\n    }\n    float2 _e8 = asfloat(particlesSrc.Load2(0+index*16+0));\n    vPos = _e8;\n    float2 _e14 = asfloat(particlesSrc.Load2(8+index*16+0));\n    vVel = _e14;\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e91 = i;\n            i = (_e91 + 1u);\n        }\n        loop_init = false;\n        uint _e36 = i;\n        if ((_e36 >= NUM_PARTICLES)) {\n            break;\n        }\n        uint _e39 = i;\n        if ((_e39 == index)) {\n            continue;\n        }\n        uint _e43 = i;\n        float2 _e46 = asfloat(particlesSrc.Load2(0+_e43*16+0));\n        pos = _e46;\n        uint _e49 = i;\n        float2 _e52 = asfloat(particlesSrc.Load2(8+_e49*16+0));\n        vel = _e52;\n        float2 _e53 = pos;\n        float2 _e54 = vPos;\n        float _e58 = params.rule1Distance;\n        if ((distance(_e53, _e54) < _e58)) {\n            float2 _e60 = cMass;\n            float2 _e61 = pos;\n            cMass = (_e60 + _e61);\n            int _e63 = cMassCount;\n            cMassCount = asint(asuint(_e63) + asuint(int(1)));\n        }\n        float2 _e66 = pos;\n        float2 _e67 = vPos;\n        float _e71 = params.rule2Distance;\n        if ((distance(_e66, _e67) < _e71)) {\n            float2 _e73 = colVel;\n            float2 _e74 = pos;\n            float2 _e75 = vPos;\n            colVel = (_e73 - (_e74 - _e75));\n        }\n        float2 _e78 = pos;\n        float2 _e79 = vPos;\n        float _e83 = params.rule3Distance;\n        if ((distance(_e78, _e79) < _e83)) {\n            float2 _e85 = cVel;\n            float2 _e86 = vel;\n            cVel = (_e85 + _e86);\n            int _e88 = cVelCount;\n            cVelCount = asint(asuint(_e88) + asuint(int(1)));\n        }\n    }\n    int _e94 = cMassCount;\n    if ((_e94 > int(0))) {\n        float2 _e97 = cMass;\n        int _e98 = cMassCount;\n        float2 _e102 = vPos;\n        cMass = ((_e97 / (float(_e98)).xx) - _e102);\n    }\n    int _e104 = cVelCount;\n    if ((_e104 > int(0))) {\n        float2 _e107 = cVel;\n        int _e108 = cVelCount;\n        cVel = (_e107 / (float(_e108)).xx);\n    }\n    float2 _e112 = vVel;\n    float2 _e113 = cMass;\n    float _e116 = params.rule1Scale;\n    float2 _e119 = colVel;\n    float _e122 = params.rule2Scale;\n    float2 _e125 = cVel;\n    float _e128 = params.rule3Scale;\n    vVel = (((_e112 + (_e113 * _e116)) + (_e119 * _e122)) + (_e125 * _e128));\n    float2 _e131 = vVel;\n    float2 _e133 = vVel;\n    vVel = (normalize(_e131) * clamp(length(_e133), 0.0, 0.1));\n    float2 _e139 = vPos;\n    float2 _e140 = vVel;\n    float _e143 = params.deltaT;\n    vPos = (_e139 + (_e140 * _e143));\n    float _e147 = vPos.x;\n    if ((_e147 < -1.0)) {\n        vPos.x = 1.0;\n    }\n    float _e153 = vPos.x;\n    if ((_e153 > 1.0)) {\n        vPos.x = -1.0;\n    }\n    float _e159 = vPos.y;\n    if ((_e159 < -1.0)) {\n        vPos.y = 1.0;\n    }\n    float _e165 = vPos.y;\n    if ((_e165 > 1.0)) {\n        vPos.y = -1.0;\n    }\n    float2 _e174 = vPos;\n    particlesDst.Store2(0+index*16+0, asuint(_e174));\n    float2 _e179 = vVel;\n    particlesDst.Store2(8+index*16+0, asuint(_e179));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-boids.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bounds-check-dynamic-buffer.hlsl",
    "content": "struct __dynamic_buffer_offsetsTy0 {\n    uint _0;\n    uint _1;\n};\nConstantBuffer<__dynamic_buffer_offsetsTy0> __dynamic_buffer_offsets0: register(b1, space0);\n\nstruct __dynamic_buffer_offsetsTy1 {\n    uint _0;\n};\nConstantBuffer<__dynamic_buffer_offsetsTy1> __dynamic_buffer_offsets1: register(b2, space0);\n\nstruct T {\n    uint t;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n};\n\nRWByteAddressBuffer in_ : register(u0);\nRWByteAddressBuffer out_ : register(u1);\ncbuffer in_data_uniform : register(b0) { T in_data_uniform[1]; }\nRWByteAddressBuffer in_data_storage_g0_b3_ : register(u2);\nRWByteAddressBuffer in_data_storage_g0_b4_ : register(u3);\nRWByteAddressBuffer in_data_storage_g1_b0_ : register(u4);\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    uint i = asuint(in_.Load(0));\n    uint _e7 = in_data_uniform[min(uint(i), 0u)].t;\n    out_.Store(0, asuint(_e7));\n    uint _e13 = asuint(in_data_storage_g0_b3_.Load(0+i*16+__dynamic_buffer_offsets0._0));\n    out_.Store(4, asuint(_e13));\n    uint _e19 = asuint(in_data_storage_g0_b4_.Load(0+i*16+__dynamic_buffer_offsets0._1));\n    out_.Store(8, asuint(_e19));\n    uint _e25 = asuint(in_data_storage_g1_b0_.Load(0+i*16+__dynamic_buffer_offsets1._0));\n    out_.Store(12, asuint(_e25));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-bounds-check-dynamic-buffer.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-break-if.hlsl",
    "content": "void breakIfEmpty()\n{\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            if (true) {\n                break;\n            }\n        }\n        loop_init = false;\n    }\n    return;\n}\n\nvoid breakIfEmptyBody(bool a)\n{\n    bool b = (bool)0;\n    bool c = (bool)0;\n\n    uint2 loop_bound_1 = uint2(4294967295u, 4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (all(loop_bound_1 == uint2(0u, 0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        if (!loop_init_1) {\n            b = a;\n            bool _e2 = b;\n            c = (a != _e2);\n            bool _e5 = c;\n            if ((a == _e5)) {\n                break;\n            }\n        }\n        loop_init_1 = false;\n    }\n    return;\n}\n\nvoid breakIf(bool a_1)\n{\n    bool d = (bool)0;\n    bool e = (bool)0;\n\n    uint2 loop_bound_2 = uint2(4294967295u, 4294967295u);\n    bool loop_init_2 = true;\n    while(true) {\n        if (all(loop_bound_2 == uint2(0u, 0u))) { break; }\n        loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n        if (!loop_init_2) {\n            bool _e5 = e;\n            if ((a_1 == _e5)) {\n                break;\n            }\n        }\n        loop_init_2 = false;\n        d = a_1;\n        bool _e2 = d;\n        e = (a_1 != _e2);\n    }\n    return;\n}\n\nvoid breakIfSeparateVariable()\n{\n    uint counter = 0u;\n\n    uint2 loop_bound_3 = uint2(4294967295u, 4294967295u);\n    bool loop_init_3 = true;\n    while(true) {\n        if (all(loop_bound_3 == uint2(0u, 0u))) { break; }\n        loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n        if (!loop_init_3) {\n            uint _e5 = counter;\n            if ((_e5 == 5u)) {\n                break;\n            }\n        }\n        loop_init_3 = false;\n        uint _e2 = counter;\n        counter = (_e2 + 1u);\n    }\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    breakIfEmpty();\n    breakIfEmptyBody(false);\n    breakIf(false);\n    breakIfSeparateVariable();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-break-if.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-collatz.hlsl",
    "content": "RWByteAddressBuffer v_indices : register(u0);\n\nuint naga_mod(uint lhs, uint rhs) {\n    return lhs % (rhs == 0u ? 1u : rhs);\n}\n\nuint naga_div(uint lhs, uint rhs) {\n    return lhs / (rhs == 0u ? 1u : rhs);\n}\n\nuint collatz_iterations(uint n_base)\n{\n    uint n = (uint)0;\n    uint i = 0u;\n\n    n = n_base;\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        uint _e4 = n;\n        if ((_e4 > 1u)) {\n        } else {\n            break;\n        }\n        {\n            uint _e7 = n;\n            if ((naga_mod(_e7, 2u) == 0u)) {\n                uint _e12 = n;\n                n = naga_div(_e12, 2u);\n            } else {\n                uint _e16 = n;\n                n = ((3u * _e16) + 1u);\n            }\n            uint _e20 = i;\n            i = (_e20 + 1u);\n        }\n    }\n    uint _e23 = i;\n    return _e23;\n}\n\n[numthreads(1, 1, 1)]\nvoid main(uint3 global_id : SV_DispatchThreadID)\n{\n    uint _e9 = asuint(v_indices.Load(global_id.x*4+0));\n    const uint _e10 = collatz_iterations(_e9);\n    v_indices.Store(global_id.x*4+0, asuint(_e10));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-collatz.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-const-exprs.hlsl",
    "content": "static const uint TWO = 2u;\nstatic const int THREE = int(3);\nstatic const bool TRUE = true;\nstatic const bool FALSE = false;\nstatic const int FOUR = int(4);\nstatic const int TEXTURE_KIND_REGULAR = int(0);\nstatic const int TEXTURE_KIND_WARP = int(1);\nstatic const int TEXTURE_KIND_SKY = int(2);\nstatic const int FOUR_ALIAS = int(4);\nstatic const int TEST_CONSTANT_ADDITION = int(8);\nstatic const int TEST_CONSTANT_ALIAS_ADDITION = int(8);\nstatic const float PI = 3.141;\nstatic const float phi_sun = 6.282;\nstatic const float4 DIV = float4(0.44444445, 0.0, 0.0, 0.0);\nstatic const float2 add_vec = float2(4.0, 5.0);\nstatic const bool2 compare_vec = bool2(true, false);\n\nvoid swizzle_of_compose()\n{\n    int4 out_ = int4(int(4), int(3), int(2), int(1));\n\n    return;\n}\n\nvoid index_of_compose()\n{\n    int out_1 = int(2);\n\n    return;\n}\n\nvoid compose_three_deep()\n{\n    int out_2 = int(6);\n\n    return;\n}\n\nvoid non_constant_initializers()\n{\n    int w = int(30);\n    int x = (int)0;\n    int y = (int)0;\n    int z = int(70);\n    int4 out_3 = (int4)0;\n\n    int _e2 = w;\n    x = _e2;\n    int _e4 = x;\n    y = _e4;\n    int _e8 = w;\n    int _e9 = x;\n    int _e10 = y;\n    int _e11 = z;\n    out_3 = int4(_e8, _e9, _e10, _e11);\n    return;\n}\n\nvoid splat_of_constant()\n{\n    int4 out_4 = int4(int(-4), int(-4), int(-4), int(-4));\n\n    return;\n}\n\nvoid compose_of_constant()\n{\n    int4 out_5 = int4(int(-4), int(-4), int(-4), int(-4));\n\n    return;\n}\n\nuint map_texture_kind(int texture_kind)\n{\n    switch(texture_kind) {\n        case 0: {\n            return 10u;\n        }\n        case 1: {\n            return 20u;\n        }\n        case 2: {\n            return 30u;\n        }\n        default: {\n            return 0u;\n        }\n    }\n}\n\nvoid compose_of_splat()\n{\n    float4 x_1 = float4(2.0, 1.0, 1.0, 1.0);\n\n    return;\n}\n\nvoid test_local_const()\n{\n    float arr[2] = (float[2])0;\n\n    return;\n}\n\nvoid compose_vector_zero_val_binop()\n{\n    int3 a = int3(int(1), int(1), int(1));\n    int3 b = int3(int(0), int(1), int(2));\n    int3 c = int3(int(1), int(0), int(2));\n\n    return;\n}\n\nvoid relational()\n{\n    bool scalar_any_false = false;\n    bool scalar_any_true = true;\n    bool scalar_all_false = false;\n    bool scalar_all_true = true;\n    bool vec_any_false = false;\n    bool vec_any_true = true;\n    bool vec_all_false = false;\n    bool vec_all_true = true;\n\n    return;\n}\n\nvoid packed_dot_product()\n{\n    int signed_four = int(4);\n    uint unsigned_four = 4u;\n    int signed_twelve = int(12);\n    uint unsigned_twelve = 12u;\n    int signed_seventy = int(70);\n    uint unsigned_seventy = 70u;\n    int minus_four = int(-4);\n\n    return;\n}\n\ntypedef int ret_Constructarray9_int_[9];\nret_Constructarray9_int_ Constructarray9_int_(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8) {\n    int ret[9] = { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 };\n    return ret;\n}\n\nvoid abstract_access(uint i)\n{\n    float a_1 = 1.0;\n    uint b_1 = 1u;\n    int c_1 = (int)0;\n    int d = (int)0;\n\n    c_1 = Constructarray9_int_(int(1), int(2), int(3), int(4), int(5), int(6), int(7), int(8), int(9))[min(uint(i), 8u)];\n    d = int4(int(1), int(2), int(3), int(4))[min(uint(i), 3u)];\n    return;\n}\n\n[numthreads(2, 3, 1)]\nvoid main()\n{\n    swizzle_of_compose();\n    index_of_compose();\n    compose_three_deep();\n    non_constant_initializers();\n    splat_of_constant();\n    compose_of_constant();\n    const uint _e1 = map_texture_kind(int(1));\n    compose_of_splat();\n    test_local_const();\n    compose_vector_zero_val_binop();\n    relational();\n    packed_dot_product();\n    test_local_const();\n    abstract_access(1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-const-exprs.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-constructors.hlsl",
    "content": "struct Foo {\n    float4 a;\n    int b;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n};\n\ntypedef float2x2 ret_Constructarray1_float2x2_[1];\nret_Constructarray1_float2x2_ Constructarray1_float2x2_(float2x2 arg0) {\n    float2x2 ret[1] = { arg0 };\n    return ret;\n}\n\nbool ZeroValuebool() {\n    return (bool)0;\n}\n\nint ZeroValueint() {\n    return (int)0;\n}\n\nuint ZeroValueuint() {\n    return (uint)0;\n}\n\nfloat ZeroValuefloat() {\n    return (float)0;\n}\n\nuint2 ZeroValueuint2() {\n    return (uint2)0;\n}\n\nfloat2x2 ZeroValuefloat2x2() {\n    return (float2x2)0;\n}\n\ntypedef Foo ret_ZeroValuearray3_Foo_[3];\nret_ZeroValuearray3_Foo_ ZeroValuearray3_Foo_() {\n    return (Foo[3])0;\n}\n\nFoo ZeroValueFoo() {\n    return (Foo)0;\n}\n\nstatic const float3 const1_ = (0.0).xxx;\nstatic const float2x2 const3_ = float2x2(float2(0.0, 1.0), float2(2.0, 3.0));\nstatic const float2x2 const4_[1] = Constructarray1_float2x2_(float2x2(float2(0.0, 1.0), float2(2.0, 3.0)));\nstatic const bool cz0_ = ZeroValuebool();\nstatic const int cz1_ = ZeroValueint();\nstatic const uint cz2_ = ZeroValueuint();\nstatic const float cz3_ = ZeroValuefloat();\nstatic const uint2 cz4_ = ZeroValueuint2();\nstatic const float2x2 cz5_ = ZeroValuefloat2x2();\nstatic const Foo cz6_[3] = ZeroValuearray3_Foo_();\nstatic const Foo cz7_ = ZeroValueFoo();\nstatic const uint2 cp1_ = (0u).xx;\n\nFoo ConstructFoo(float4 arg0, int arg1) {\n    Foo ret = (Foo)0;\n    ret.a = arg0;\n    ret.b = arg1;\n    return ret;\n}\n\ntypedef int ret_Constructarray4_int_[4];\nret_Constructarray4_int_ Constructarray4_int_(int arg0, int arg1, int arg2, int arg3) {\n    int ret[4] = { arg0, arg1, arg2, arg3 };\n    return ret;\n}\n\nfloat2x3 ZeroValuefloat2x3() {\n    return (float2x3)0;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    Foo foo = (Foo)0;\n\n    foo = ConstructFoo((1.0).xxxx, int(1));\n    float2x2 m0_ = float2x2(float2(1.0, 0.0), float2(0.0, 1.0));\n    float4x4 m1_ = float4x4(float4(1.0, 0.0, 0.0, 0.0), float4(0.0, 1.0, 0.0, 0.0), float4(0.0, 0.0, 1.0, 0.0), float4(0.0, 0.0, 0.0, 1.0));\n    uint2 zvc8_ = uint2(0u, 0u);\n    float2 zvc9_ = float2(0.0, 0.0);\n    uint2 cit0_ = (0u).xx;\n    float2x2 cit1_ = float2x2((0.0).xx, (0.0).xx);\n    int cit2_[4] = Constructarray4_int_(int(0), int(1), int(2), int(3));\n    uint2 ic4_ = uint2(0u, 0u);\n    float2x3 ic5_ = float2x3(float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-constructors.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-control-flow.hlsl",
    "content": "void control_flow()\n{\n    int pos = (int)0;\n\n    DeviceMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    DeviceMemoryBarrierWithGroupSync();\n    do {\n        pos = int(1);\n    } while(false);\n    int _e3 = pos;\n    switch(_e3) {\n        case 1: {\n            pos = int(0);\n            break;\n        }\n        case 2: {\n            pos = int(1);\n            break;\n        }\n        case 3:\n        case 4: {\n            pos = int(2);\n            break;\n        }\n        case 5: {\n            pos = int(3);\n            break;\n        }\n        default:\n        case 6: {\n            pos = int(4);\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    int _e10 = pos;\n    switch(_e10) {\n        case 1: {\n            pos = int(0);\n            break;\n        }\n        case 2: {\n            pos = int(1);\n            break;\n        }\n        case 3: {\n            pos = int(2);\n            break;\n        }\n        case 4: {\n            break;\n        }\n        default: {\n            pos = int(3);\n            break;\n        }\n    }\n    int _e15 = pos;\n    switch(_e15) {\n        case 1: {\n            pos = int(0);\n            return;\n        }\n        case 2: {\n            pos = int(1);\n            return;\n        }\n        case 3:\n        case 4: {\n            pos = int(2);\n            return;\n        }\n        case 5:\n        case 6: {\n            pos = int(3);\n            return;\n        }\n        default: {\n            pos = int(4);\n            return;\n        }\n    }\n}\n\nvoid switch_default_break(int i)\n{\n    do {\n        break;\n    } while(false);\n}\n\nvoid switch_case_break()\n{\n    switch(int(0)) {\n        case 0: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    return;\n}\n\nvoid switch_selector_type_conversion()\n{\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid switch_const_expr_case_selectors()\n{\n    switch(int(0)) {\n        case 0: {\n            return;\n        }\n        case 1: {\n            return;\n        }\n        case 2: {\n            return;\n        }\n        case 3: {\n            return;\n        }\n        case 4: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid loop_switch_continue(int x)\n{\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool should_continue = false;\n        switch(x) {\n            case 1: {\n                should_continue = true;\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n        if (should_continue) {\n            continue;\n        }\n    }\n    return;\n}\n\nvoid loop_switch_continue_nesting(int x_1, int y, int z)\n{\n    uint2 loop_bound_1 = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound_1 == uint2(0u, 0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        bool should_continue_1 = false;\n        switch(x_1) {\n            case 1: {\n                should_continue_1 = true;\n                break;\n            }\n            case 2: {\n                switch(y) {\n                    case 1: {\n                        should_continue_1 = true;\n                        break;\n                    }\n                    default: {\n                        uint2 loop_bound_2 = uint2(4294967295u, 4294967295u);\n                        while(true) {\n                            if (all(loop_bound_2 == uint2(0u, 0u))) { break; }\n                            loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n                            bool should_continue_2 = false;\n                            switch(z) {\n                                case 1: {\n                                    should_continue_2 = true;\n                                    break;\n                                }\n                                default: {\n                                    break;\n                                }\n                            }\n                            if (should_continue_2) {\n                                continue;\n                            }\n                        }\n                        break;\n                    }\n                }\n                if (should_continue_1) {\n                    break;\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n        if (should_continue_1) {\n            continue;\n        }\n        bool should_continue_3 = false;\n        do {\n            should_continue_3 = true;\n            break;\n        } while(false);\n        if (should_continue_3) {\n            continue;\n        }\n    }\n    uint2 loop_bound_3 = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound_3 == uint2(0u, 0u))) { break; }\n        loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n        bool should_continue_4 = false;\n        do {\n            do {\n                should_continue_4 = true;\n                break;\n            } while(false);\n            if (should_continue_4) {\n                break;\n            }\n        } while(false);\n        if (should_continue_4) {\n            continue;\n        }\n    }\n    return;\n}\n\nvoid loop_switch_omit_continue_variable_checks(int x_2, int y_1, int z_1, int w)\n{\n    int pos_1 = int(0);\n\n    uint2 loop_bound_4 = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound_4 == uint2(0u, 0u))) { break; }\n        loop_bound_4 -= uint2(loop_bound_4.y == 0u, 1u);\n        bool should_continue_5 = false;\n        switch(x_2) {\n            case 1: {\n                pos_1 = int(1);\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    uint2 loop_bound_5 = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound_5 == uint2(0u, 0u))) { break; }\n        loop_bound_5 -= uint2(loop_bound_5.y == 0u, 1u);\n        bool should_continue_6 = false;\n        switch(x_2) {\n            case 1: {\n                break;\n            }\n            case 2: {\n                switch(y_1) {\n                    case 1: {\n                        should_continue_6 = true;\n                        break;\n                    }\n                    default: {\n                        switch(z_1) {\n                            case 1: {\n                                pos_1 = int(2);\n                                break;\n                            }\n                            default: {\n                                break;\n                            }\n                        }\n                        break;\n                    }\n                }\n                if (should_continue_6) {\n                    break;\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n        if (should_continue_6) {\n            continue;\n        }\n    }\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    control_flow();\n    switch_default_break(int(1));\n    switch_case_break();\n    switch_selector_type_conversion();\n    switch_const_expr_case_selectors();\n    loop_switch_continue(int(1));\n    loop_switch_continue_nesting(int(1), int(2), int(3));\n    loop_switch_omit_continue_variable_checks(int(1), int(2), int(3), int(4));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-control-flow.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-conversion-float-to-int.hlsl",
    "content": "static const half MIN_F16_ = -65504.0h;\nstatic const half MAX_F16_ = 65504.0h;\nstatic const float MIN_F32_ = -3.4028235e38;\nstatic const float MAX_F32_ = 3.4028235e38;\nstatic const double MIN_F64_ = -1.7976931348623157e308L;\nstatic const double MAX_F64_ = 1.7976931348623157e308L;\n\nvoid test_const_eval()\n{\n    int min_f16_to_i32_ = int(-65504);\n    int max_f16_to_i32_ = int(65504);\n    uint min_f16_to_u32_ = 0u;\n    uint max_f16_to_u32_ = 65504u;\n    int64_t min_f16_to_i64_ = -65504L;\n    int64_t max_f16_to_i64_ = 65504L;\n    uint64_t min_f16_to_u64_ = 0uL;\n    uint64_t max_f16_to_u64_ = 65504uL;\n    int min_f32_to_i32_ = int(-2147483647 - 1);\n    int max_f32_to_i32_ = int(2147483520);\n    uint min_f32_to_u32_ = 0u;\n    uint max_f32_to_u32_ = 4294967040u;\n    int64_t min_f32_to_i64_ = (-9223372036854775807L - 1L);\n    int64_t max_f32_to_i64_ = 9223371487098961920L;\n    uint64_t min_f32_to_u64_ = 0uL;\n    uint64_t max_f32_to_u64_ = 18446742974197923840uL;\n    int64_t min_f64_to_i64_ = (-9223372036854775807L - 1L);\n    int64_t max_f64_to_i64_ = 9223372036854774784L;\n    uint64_t min_f64_to_u64_ = 0uL;\n    uint64_t max_f64_to_u64_ = 18446744073709549568uL;\n    int min_abstract_float_to_i32_ = int(-2147483647 - 1);\n    int max_abstract_float_to_i32_ = int(2147483647);\n    uint min_abstract_float_to_u32_ = 0u;\n    uint max_abstract_float_to_u32_ = 4294967295u;\n    int64_t min_abstract_float_to_i64_ = (-9223372036854775807L - 1L);\n    int64_t max_abstract_float_to_i64_ = 9223372036854774784L;\n    uint64_t min_abstract_float_to_u64_ = 0uL;\n    uint64_t max_abstract_float_to_u64_ = 18446744073709549568uL;\n\n    return;\n}\n\nint naga_f2i32(half value) {\n    return int(clamp(value, -65504.0h, 65504.0h));\n}\n\nint test_f16_to_i32_(half f)\n{\n    return naga_f2i32(f);\n}\n\nuint naga_f2u32(half value) {\n    return uint(clamp(value, 0.0h, 65504.0h));\n}\n\nuint test_f16_to_u32_(half f_1)\n{\n    return naga_f2u32(f_1);\n}\n\nint64_t naga_f2i64(half value) {\n    return int64_t(clamp(value, -65504.0h, 65504.0h));\n}\n\nint64_t test_f16_to_i64_(half f_2)\n{\n    return naga_f2i64(f_2);\n}\n\nuint64_t naga_f2u64(half value) {\n    return uint64_t(clamp(value, 0.0h, 65504.0h));\n}\n\nuint64_t test_f16_to_u64_(half f_3)\n{\n    return naga_f2u64(f_3);\n}\n\nint naga_f2i32(float value) {\n    return int(clamp(value, -2147483600.0, 2147483500.0));\n}\n\nint test_f32_to_i32_(float f_4)\n{\n    return naga_f2i32(f_4);\n}\n\nuint naga_f2u32(float value) {\n    return uint(clamp(value, 0.0, 4294967000.0));\n}\n\nuint test_f32_to_u32_(float f_5)\n{\n    return naga_f2u32(f_5);\n}\n\nint64_t naga_f2i64(float value) {\n    return int64_t(clamp(value, -9.223372e18, 9.2233715e18));\n}\n\nint64_t test_f32_to_i64_(float f_6)\n{\n    return naga_f2i64(f_6);\n}\n\nuint64_t naga_f2u64(float value) {\n    return uint64_t(clamp(value, 0.0, 1.8446743e19));\n}\n\nuint64_t test_f32_to_u64_(float f_7)\n{\n    return naga_f2u64(f_7);\n}\n\nint naga_f2i32(double value) {\n    return int(clamp(value, -2147483648.0L, 2147483647.0L));\n}\n\nint test_f64_to_i32_(double f_8)\n{\n    return naga_f2i32(f_8);\n}\n\nuint naga_f2u32(double value) {\n    return uint(clamp(value, 0.0L, 4294967295.0L));\n}\n\nuint test_f64_to_u32_(double f_9)\n{\n    return naga_f2u32(f_9);\n}\n\nint64_t naga_f2i64(double value) {\n    return int64_t(clamp(value, -9.223372036854776e18L, 9.223372036854775e18L));\n}\n\nint64_t test_f64_to_i64_(double f_10)\n{\n    return naga_f2i64(f_10);\n}\n\nuint64_t naga_f2u64(double value) {\n    return uint64_t(clamp(value, 0.0L, 1.844674407370955e19L));\n}\n\nuint64_t test_f64_to_u64_(double f_11)\n{\n    return naga_f2u64(f_11);\n}\n\nint2 naga_f2i32(half2 value) {\n    return int2(clamp(value, -65504.0h, 65504.0h));\n}\n\nint2 test_f16_to_i32_vec(half2 f_12)\n{\n    return naga_f2i32(f_12);\n}\n\nuint2 naga_f2u32(half2 value) {\n    return uint2(clamp(value, 0.0h, 65504.0h));\n}\n\nuint2 test_f16_to_u32_vec(half2 f_13)\n{\n    return naga_f2u32(f_13);\n}\n\nint64_t2 naga_f2i64(half2 value) {\n    return int64_t2(clamp(value, -65504.0h, 65504.0h));\n}\n\nint64_t2 test_f16_to_i64_vec(half2 f_14)\n{\n    return naga_f2i64(f_14);\n}\n\nuint64_t2 naga_f2u64(half2 value) {\n    return uint64_t2(clamp(value, 0.0h, 65504.0h));\n}\n\nuint64_t2 test_f16_to_u64_vec(half2 f_15)\n{\n    return naga_f2u64(f_15);\n}\n\nint2 naga_f2i32(float2 value) {\n    return int2(clamp(value, -2147483600.0, 2147483500.0));\n}\n\nint2 test_f32_to_i32_vec(float2 f_16)\n{\n    return naga_f2i32(f_16);\n}\n\nuint2 naga_f2u32(float2 value) {\n    return uint2(clamp(value, 0.0, 4294967000.0));\n}\n\nuint2 test_f32_to_u32_vec(float2 f_17)\n{\n    return naga_f2u32(f_17);\n}\n\nint64_t2 naga_f2i64(float2 value) {\n    return int64_t2(clamp(value, -9.223372e18, 9.2233715e18));\n}\n\nint64_t2 test_f32_to_i64_vec(float2 f_18)\n{\n    return naga_f2i64(f_18);\n}\n\nuint64_t2 naga_f2u64(float2 value) {\n    return uint64_t2(clamp(value, 0.0, 1.8446743e19));\n}\n\nuint64_t2 test_f32_to_u64_vec(float2 f_19)\n{\n    return naga_f2u64(f_19);\n}\n\nint2 naga_f2i32(double2 value) {\n    return int2(clamp(value, -2147483648.0L, 2147483647.0L));\n}\n\nint2 test_f64_to_i32_vec(double2 f_20)\n{\n    return naga_f2i32(f_20);\n}\n\nuint2 naga_f2u32(double2 value) {\n    return uint2(clamp(value, 0.0L, 4294967295.0L));\n}\n\nuint2 test_f64_to_u32_vec(double2 f_21)\n{\n    return naga_f2u32(f_21);\n}\n\nint64_t2 naga_f2i64(double2 value) {\n    return int64_t2(clamp(value, -9.223372036854776e18L, 9.223372036854775e18L));\n}\n\nint64_t2 test_f64_to_i64_vec(double2 f_22)\n{\n    return naga_f2i64(f_22);\n}\n\nuint64_t2 naga_f2u64(double2 value) {\n    return uint64_t2(clamp(value, 0.0L, 1.844674407370955e19L));\n}\n\nuint64_t2 test_f64_to_u64_vec(double2 f_23)\n{\n    return naga_f2u64(f_23);\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    test_const_eval();\n    const int _e1 = test_f16_to_i32_(1.0h);\n    const uint _e3 = test_f16_to_u32_(1.0h);\n    const int64_t _e5 = test_f16_to_i64_(1.0h);\n    const uint64_t _e7 = test_f16_to_u64_(1.0h);\n    const int _e9 = test_f32_to_i32_(1.0);\n    const uint _e11 = test_f32_to_u32_(1.0);\n    const int64_t _e13 = test_f32_to_i64_(1.0);\n    const uint64_t _e15 = test_f32_to_u64_(1.0);\n    const int _e17 = test_f64_to_i32_(1.0L);\n    const uint _e19 = test_f64_to_u32_(1.0L);\n    const int64_t _e21 = test_f64_to_i64_(1.0L);\n    const uint64_t _e23 = test_f64_to_u64_(1.0L);\n    const int2 _e27 = test_f16_to_i32_vec(half2(1.0h, 2.0h));\n    const uint2 _e31 = test_f16_to_u32_vec(half2(1.0h, 2.0h));\n    const int64_t2 _e35 = test_f16_to_i64_vec(half2(1.0h, 2.0h));\n    const uint64_t2 _e39 = test_f16_to_u64_vec(half2(1.0h, 2.0h));\n    const int2 _e43 = test_f32_to_i32_vec(float2(1.0, 2.0));\n    const uint2 _e47 = test_f32_to_u32_vec(float2(1.0, 2.0));\n    const int64_t2 _e51 = test_f32_to_i64_vec(float2(1.0, 2.0));\n    const uint64_t2 _e55 = test_f32_to_u64_vec(float2(1.0, 2.0));\n    const int2 _e59 = test_f64_to_i32_vec(double2(1.0L, 2.0L));\n    const uint2 _e63 = test_f64_to_u32_vec(double2(1.0L, 2.0L));\n    const int64_t2 _e67 = test_f64_to_i64_vec(double2(1.0L, 2.0L));\n    const uint64_t2 _e71 = test_f64_to_u64_vec(double2(1.0L, 2.0L));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-conversion-float-to-int.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_0\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-conversions.hlsl",
    "content": "static const float2 ic0_ = float2(2.0, 2.0);\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    float2 vc0_ = float2(2.0, 2.0);\n\n    float2 lc0_ = float2(2.0, 2.0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-conversions.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-cross.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid main()\n{\n    float3 a = float3(0.0, 0.0, 1.0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-cross.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-dualsource.hlsl",
    "content": "struct FragmentOutput {\n    float4 output0_ : SV_Target0;\n    float4 output1_ : SV_Target1;\n};\n\nFragmentOutput ConstructFragmentOutput(float4 arg0, float4 arg1) {\n    FragmentOutput ret = (FragmentOutput)0;\n    ret.output0_ = arg0;\n    ret.output1_ = arg1;\n    return ret;\n}\n\nFragmentOutput main()\n{\n    const FragmentOutput fragmentoutput = ConstructFragmentOutput(float4(0.4, 0.3, 0.2, 0.1), float4(0.9, 0.8, 0.7, 0.6));\n    return fragmentoutput;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-dualsource.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-empty-if.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid comp(uint3 id : SV_DispatchThreadID)\n{\n    if ((id.x == 0u)) {\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-empty-if.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"comp\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-empty.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid main()\n{\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-empty.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-f16.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct UniformCompatible {\n    uint val_u32_;\n    int val_i32_;\n    float val_f32_;\n    half val_f16_;\n    half2 val_f16_2_;\n    int _pad5_0;\n    half3 val_f16_3_;\n    half4 val_f16_4_;\n    half final_value;\n    half2 val_mat2x2__0; half2 val_mat2x2__1;\n    int _pad9_0;\n    row_major half2x3 val_mat2x3_;\n    row_major half2x4 val_mat2x4_;\n    half2 val_mat3x2__0; half2 val_mat3x2__1; half2 val_mat3x2__2;\n    int _pad12_0;\n    row_major half3x3 val_mat3x3_;\n    row_major half3x4 val_mat3x4_;\n    half2 val_mat4x2__0; half2 val_mat4x2__1; half2 val_mat4x2__2; half2 val_mat4x2__3;\n    row_major half4x3 val_mat4x3_;\n    row_major half4x4 val_mat4x4_;\n};\n\nstruct StorageCompatible {\n    half val_f16_array_2_[2];\n};\n\nstruct LayoutTest {\n    half scalar1_;\n    half scalar2_;\n    int _pad2_0;\n    half3 v3_;\n    half tuck_in;\n    half scalar4_;\n    uint larger;\n};\n\nstatic const half constant_variable = 15.203125h;\n\nstatic half private_variable = 1.0h;\ncbuffer input_uniform : register(b0) { UniformCompatible input_uniform; }\nByteAddressBuffer input_storage : register(t1);\nByteAddressBuffer input_arrays : register(t2);\nRWByteAddressBuffer output : register(u3);\nRWByteAddressBuffer output_arrays : register(u4);\n\nhalf2x2 GetMatval_mat2x2_OnUniformCompatible(UniformCompatible obj) {\n    return half2x2(obj.val_mat2x2__0, obj.val_mat2x2__1);\n}\n\nvoid SetMatval_mat2x2_OnUniformCompatible(UniformCompatible obj, half2x2 mat) {\n    obj.val_mat2x2__0 = mat[0];\n    obj.val_mat2x2__1 = mat[1];\n}\n\nvoid SetMatVecval_mat2x2_OnUniformCompatible(UniformCompatible obj, half2 vec, uint mat_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat2x2__0 = vec; break; }\n    case 1: { obj.val_mat2x2__1 = vec; break; }\n    }\n}\n\nvoid SetMatScalarval_mat2x2_OnUniformCompatible(UniformCompatible obj, half scalar, uint mat_idx, uint vec_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat2x2__0[vec_idx] = scalar; break; }\n    case 1: { obj.val_mat2x2__1[vec_idx] = scalar; break; }\n    }\n}\n\nhalf3x2 GetMatval_mat3x2_OnUniformCompatible(UniformCompatible obj) {\n    return half3x2(obj.val_mat3x2__0, obj.val_mat3x2__1, obj.val_mat3x2__2);\n}\n\nvoid SetMatval_mat3x2_OnUniformCompatible(UniformCompatible obj, half3x2 mat) {\n    obj.val_mat3x2__0 = mat[0];\n    obj.val_mat3x2__1 = mat[1];\n    obj.val_mat3x2__2 = mat[2];\n}\n\nvoid SetMatVecval_mat3x2_OnUniformCompatible(UniformCompatible obj, half2 vec, uint mat_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat3x2__0 = vec; break; }\n    case 1: { obj.val_mat3x2__1 = vec; break; }\n    case 2: { obj.val_mat3x2__2 = vec; break; }\n    }\n}\n\nvoid SetMatScalarval_mat3x2_OnUniformCompatible(UniformCompatible obj, half scalar, uint mat_idx, uint vec_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat3x2__0[vec_idx] = scalar; break; }\n    case 1: { obj.val_mat3x2__1[vec_idx] = scalar; break; }\n    case 2: { obj.val_mat3x2__2[vec_idx] = scalar; break; }\n    }\n}\n\nhalf4x2 GetMatval_mat4x2_OnUniformCompatible(UniformCompatible obj) {\n    return half4x2(obj.val_mat4x2__0, obj.val_mat4x2__1, obj.val_mat4x2__2, obj.val_mat4x2__3);\n}\n\nvoid SetMatval_mat4x2_OnUniformCompatible(UniformCompatible obj, half4x2 mat) {\n    obj.val_mat4x2__0 = mat[0];\n    obj.val_mat4x2__1 = mat[1];\n    obj.val_mat4x2__2 = mat[2];\n    obj.val_mat4x2__3 = mat[3];\n}\n\nvoid SetMatVecval_mat4x2_OnUniformCompatible(UniformCompatible obj, half2 vec, uint mat_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat4x2__0 = vec; break; }\n    case 1: { obj.val_mat4x2__1 = vec; break; }\n    case 2: { obj.val_mat4x2__2 = vec; break; }\n    case 3: { obj.val_mat4x2__3 = vec; break; }\n    }\n}\n\nvoid SetMatScalarval_mat4x2_OnUniformCompatible(UniformCompatible obj, half scalar, uint mat_idx, uint vec_idx) {\n    switch(mat_idx) {\n    case 0: { obj.val_mat4x2__0[vec_idx] = scalar; break; }\n    case 1: { obj.val_mat4x2__1[vec_idx] = scalar; break; }\n    case 2: { obj.val_mat4x2__2[vec_idx] = scalar; break; }\n    case 3: { obj.val_mat4x2__3[vec_idx] = scalar; break; }\n    }\n}\n\ntypedef half ret_Constructarray2_half_[2];\nret_Constructarray2_half_ Constructarray2_half_(half arg0, half arg1) {\n    half ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nhalf f16_function(half x)\n{\n    LayoutTest l = (LayoutTest)0;\n    half val = 15.203125h;\n\n    half phony = private_variable;\n    half _e5 = val;\n    val = (_e5 + -33344.0h);\n    half _e8 = val;\n    half _e9 = val;\n    val = (_e8 + (_e9 + 5.0h));\n    half _e13 = val;\n    float _e16 = input_uniform.val_f32_;\n    half _e17 = val;\n    val = (_e13 + half((_e16 + float(_e17))));\n    half _e22 = val;\n    half _e25 = input_uniform.val_f16_;\n    val = (_e22 + (_e25).xxx.z);\n    output.Store(4, asuint(int(65504)));\n    output.Store(4, asuint(int(-65504)));\n    output.Store(0, asuint(65504u));\n    output.Store(0, asuint(0u));\n    output.Store(8, asuint(65504.0));\n    output.Store(8, asuint(-65504.0));\n    half _e51 = input_uniform.val_f16_;\n    half _e54 = input_storage.Load<half>(12);\n    output.Store(12, (_e51 + _e54));\n    half2 _e60 = input_uniform.val_f16_2_;\n    half2 _e63 = input_storage.Load<half2>(16);\n    output.Store(16, (_e60 + _e63));\n    half3 _e69 = input_uniform.val_f16_3_;\n    half3 _e72 = input_storage.Load<half3>(24);\n    output.Store(24, (_e69 + _e72));\n    half4 _e78 = input_uniform.val_f16_4_;\n    half4 _e81 = input_storage.Load<half4>(32);\n    output.Store(32, (_e78 + _e81));\n    half2x2 _e87 = GetMatval_mat2x2_OnUniformCompatible(input_uniform);\n    half2x2 _e90 = half2x2(input_storage.Load<half2>(44+0), input_storage.Load<half2>(44+4));\n    {\n        half2x2 _value2 = (_e87 + _e90);\n        output.Store(44+0, _value2[0]);\n        output.Store(44+4, _value2[1]);\n    }\n    half2x3 _e96 = input_uniform.val_mat2x3_;\n    half2x3 _e99 = half2x3(input_storage.Load<half3>(56+0), input_storage.Load<half3>(56+8));\n    {\n        half2x3 _value2 = (_e96 + _e99);\n        output.Store(56+0, _value2[0]);\n        output.Store(56+8, _value2[1]);\n    }\n    half2x4 _e105 = input_uniform.val_mat2x4_;\n    half2x4 _e108 = half2x4(input_storage.Load<half4>(72+0), input_storage.Load<half4>(72+8));\n    {\n        half2x4 _value2 = (_e105 + _e108);\n        output.Store(72+0, _value2[0]);\n        output.Store(72+8, _value2[1]);\n    }\n    half3x2 _e114 = GetMatval_mat3x2_OnUniformCompatible(input_uniform);\n    half3x2 _e117 = half3x2(input_storage.Load<half2>(88+0), input_storage.Load<half2>(88+4), input_storage.Load<half2>(88+8));\n    {\n        half3x2 _value2 = (_e114 + _e117);\n        output.Store(88+0, _value2[0]);\n        output.Store(88+4, _value2[1]);\n        output.Store(88+8, _value2[2]);\n    }\n    half3x3 _e123 = input_uniform.val_mat3x3_;\n    half3x3 _e126 = half3x3(input_storage.Load<half3>(104+0), input_storage.Load<half3>(104+8), input_storage.Load<half3>(104+16));\n    {\n        half3x3 _value2 = (_e123 + _e126);\n        output.Store(104+0, _value2[0]);\n        output.Store(104+8, _value2[1]);\n        output.Store(104+16, _value2[2]);\n    }\n    half3x4 _e132 = input_uniform.val_mat3x4_;\n    half3x4 _e135 = half3x4(input_storage.Load<half4>(128+0), input_storage.Load<half4>(128+8), input_storage.Load<half4>(128+16));\n    {\n        half3x4 _value2 = (_e132 + _e135);\n        output.Store(128+0, _value2[0]);\n        output.Store(128+8, _value2[1]);\n        output.Store(128+16, _value2[2]);\n    }\n    half4x2 _e141 = GetMatval_mat4x2_OnUniformCompatible(input_uniform);\n    half4x2 _e144 = half4x2(input_storage.Load<half2>(152+0), input_storage.Load<half2>(152+4), input_storage.Load<half2>(152+8), input_storage.Load<half2>(152+12));\n    {\n        half4x2 _value2 = (_e141 + _e144);\n        output.Store(152+0, _value2[0]);\n        output.Store(152+4, _value2[1]);\n        output.Store(152+8, _value2[2]);\n        output.Store(152+12, _value2[3]);\n    }\n    half4x3 _e150 = input_uniform.val_mat4x3_;\n    half4x3 _e153 = half4x3(input_storage.Load<half3>(168+0), input_storage.Load<half3>(168+8), input_storage.Load<half3>(168+16), input_storage.Load<half3>(168+24));\n    {\n        half4x3 _value2 = (_e150 + _e153);\n        output.Store(168+0, _value2[0]);\n        output.Store(168+8, _value2[1]);\n        output.Store(168+16, _value2[2]);\n        output.Store(168+24, _value2[3]);\n    }\n    half4x4 _e159 = input_uniform.val_mat4x4_;\n    half4x4 _e162 = half4x4(input_storage.Load<half4>(200+0), input_storage.Load<half4>(200+8), input_storage.Load<half4>(200+16), input_storage.Load<half4>(200+24));\n    {\n        half4x4 _value2 = (_e159 + _e162);\n        output.Store(200+0, _value2[0]);\n        output.Store(200+8, _value2[1]);\n        output.Store(200+16, _value2[2]);\n        output.Store(200+24, _value2[3]);\n    }\n    half _e168[2] = Constructarray2_half_(input_arrays.Load<half>(0+0), input_arrays.Load<half>(0+2));\n    {\n        half _value2[2] = _e168;\n        output_arrays.Store(0+0, _value2[0]);\n        output_arrays.Store(0+2, _value2[1]);\n    }\n    half _e169 = val;\n    half _e170 = val;\n    val = (_e169 + abs(_e170));\n    half _e173 = val;\n    half _e174 = val;\n    half _e175 = val;\n    half _e176 = val;\n    val = (_e173 + clamp(_e174, _e175, _e176));\n    half _e179 = val;\n    half _e180 = val;\n    half _e182 = val;\n    val = (_e179 + dot((_e180).xx, (_e182).xx));\n    half _e186 = val;\n    half _e187 = val;\n    half _e188 = val;\n    val = (_e186 + max(_e187, _e188));\n    half _e191 = val;\n    half _e192 = val;\n    half _e193 = val;\n    val = (_e191 + min(_e192, _e193));\n    half _e196 = val;\n    half _e197 = val;\n    val = (_e196 + sign(_e197));\n    half _e200 = val;\n    val = (_e200 + 1.0h);\n    half2 _e205 = input_uniform.val_f16_2_;\n    float2 float_vec2_ = float2(_e205);\n    output.Store(16, half2(float_vec2_));\n    half3 _e212 = input_uniform.val_f16_3_;\n    float3 float_vec3_ = float3(_e212);\n    output.Store(24, half3(float_vec3_));\n    half4 _e219 = input_uniform.val_f16_4_;\n    float4 float_vec4_ = float4(_e219);\n    output.Store(32, half4(float_vec4_));\n    half2x2 _e228 = GetMatval_mat2x2_OnUniformCompatible(input_uniform);\n    {\n        half2x2 _value2 = half2x2(float2x2(_e228));\n        output.Store(44+0, _value2[0]);\n        output.Store(44+4, _value2[1]);\n    }\n    half2x3 _e235 = input_uniform.val_mat2x3_;\n    {\n        half2x3 _value2 = half2x3(float2x3(_e235));\n        output.Store(56+0, _value2[0]);\n        output.Store(56+8, _value2[1]);\n    }\n    half2x4 _e242 = input_uniform.val_mat2x4_;\n    {\n        half2x4 _value2 = half2x4(float2x4(_e242));\n        output.Store(72+0, _value2[0]);\n        output.Store(72+8, _value2[1]);\n    }\n    half3x2 _e249 = GetMatval_mat3x2_OnUniformCompatible(input_uniform);\n    {\n        half3x2 _value2 = half3x2(float3x2(_e249));\n        output.Store(88+0, _value2[0]);\n        output.Store(88+4, _value2[1]);\n        output.Store(88+8, _value2[2]);\n    }\n    half3x3 _e256 = input_uniform.val_mat3x3_;\n    {\n        half3x3 _value2 = half3x3(float3x3(_e256));\n        output.Store(104+0, _value2[0]);\n        output.Store(104+8, _value2[1]);\n        output.Store(104+16, _value2[2]);\n    }\n    half3x4 _e263 = input_uniform.val_mat3x4_;\n    {\n        half3x4 _value2 = half3x4(float3x4(_e263));\n        output.Store(128+0, _value2[0]);\n        output.Store(128+8, _value2[1]);\n        output.Store(128+16, _value2[2]);\n    }\n    half4x2 _e270 = GetMatval_mat4x2_OnUniformCompatible(input_uniform);\n    {\n        half4x2 _value2 = half4x2(float4x2(_e270));\n        output.Store(152+0, _value2[0]);\n        output.Store(152+4, _value2[1]);\n        output.Store(152+8, _value2[2]);\n        output.Store(152+12, _value2[3]);\n    }\n    half4x3 _e277 = input_uniform.val_mat4x3_;\n    {\n        half4x3 _value2 = half4x3(float4x3(_e277));\n        output.Store(168+0, _value2[0]);\n        output.Store(168+8, _value2[1]);\n        output.Store(168+16, _value2[2]);\n        output.Store(168+24, _value2[3]);\n    }\n    half4x4 _e284 = input_uniform.val_mat4x4_;\n    {\n        half4x4 _value2 = half4x4(float4x4(_e284));\n        output.Store(200+0, _value2[0]);\n        output.Store(200+8, _value2[1]);\n        output.Store(200+16, _value2[2]);\n        output.Store(200+24, _value2[3]);\n    }\n    half _e287 = val;\n    return _e287;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const half _e3 = f16_function(2.0h);\n    output.Store(40, _e3);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-f16.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_2\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-f64.hlsl",
    "content": "static const double k = 2.0L;\n\nstatic double v = 1.0L;\n\ndouble f(double x)\n{\n    double z = (double)0;\n    double w = -1.0L;\n\n    double phony = v;\n    double y = (30.0L + 400.0L);\n    z = (y + 5.0L);\n    return (((x + y) + k) + 5.0L);\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const double _e1 = f(6.0L);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-f64.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-fragment-output.hlsl",
    "content": "struct FragmentOutputVec4Vec3_ {\n    float4 vec4f : SV_Target0;\n    nointerpolation int4 vec4i : SV_Target1;\n    nointerpolation uint4 vec4u : SV_Target2;\n    float3 vec3f : SV_Target3;\n    nointerpolation int3 vec3i : SV_Target4;\n    nointerpolation uint3 vec3u : SV_Target5;\n};\n\nstruct FragmentOutputVec2Scalar {\n    float2 vec2f : SV_Target0;\n    nointerpolation int2 vec2i : SV_Target1;\n    nointerpolation uint2 vec2u : SV_Target2;\n    float scalarf : SV_Target3;\n    nointerpolation int scalari : SV_Target4;\n    nointerpolation uint scalaru : SV_Target5;\n};\n\nFragmentOutputVec4Vec3_ main_vec4vec3_()\n{\n    FragmentOutputVec4Vec3_ output = (FragmentOutputVec4Vec3_)0;\n\n    output.vec4f = (0.0).xxxx;\n    output.vec4i = (int(0)).xxxx;\n    output.vec4u = (0u).xxxx;\n    output.vec3f = (0.0).xxx;\n    output.vec3i = (int(0)).xxx;\n    output.vec3u = (0u).xxx;\n    FragmentOutputVec4Vec3_ _e19 = output;\n    const FragmentOutputVec4Vec3_ fragmentoutputvec4vec3_ = _e19;\n    return fragmentoutputvec4vec3_;\n}\n\nFragmentOutputVec2Scalar main_vec2scalar()\n{\n    FragmentOutputVec2Scalar output_1 = (FragmentOutputVec2Scalar)0;\n\n    output_1.vec2f = (0.0).xx;\n    output_1.vec2i = (int(0)).xx;\n    output_1.vec2u = (0u).xx;\n    output_1.scalarf = 0.0;\n    output_1.scalari = int(0);\n    output_1.scalaru = 0u;\n    FragmentOutputVec2Scalar _e16 = output_1;\n    const FragmentOutputVec2Scalar fragmentoutputvec2scalar = _e16;\n    return fragmentoutputvec2scalar;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-fragment-output.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main_vec4vec3_\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"main_vec2scalar\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions-optimized-by-version.hlsl",
    "content": "uint test_packed_integer_dot_product()\n{\n    int c_5_ = dot4add_i8packed(1u, 2u, 0);\n    uint c_6_ = dot4add_u8packed(3u, 4u, 0);\n    uint _e7 = (5u + c_6_);\n    uint _e9 = (6u + c_6_);\n    int c_7_ = dot4add_i8packed(_e7, _e9, 0);\n    uint _e12 = (7u + c_6_);\n    uint _e14 = (8u + c_6_);\n    uint c_8_ = dot4add_u8packed(_e12, _e14, 0);\n    return c_8_;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const uint _e0 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions-optimized-by-version.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_4\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions-unoptimized.hlsl",
    "content": "uint test_packed_integer_dot_product()\n{\n    int c_5_ = dot(int4(1u, 1u >> 8, 1u >> 16, 1u >> 24) << 24 >> 24, int4(2u, 2u >> 8, 2u >> 16, 2u >> 24) << 24 >> 24);\n    uint c_6_ = dot(uint4(3u, 3u >> 8, 3u >> 16, 3u >> 24) << 24 >> 24, uint4(4u, 4u >> 8, 4u >> 16, 4u >> 24) << 24 >> 24);\n    uint _e7 = (5u + c_6_);\n    uint _e9 = (6u + c_6_);\n    int c_7_ = dot(int4(_e7, _e7 >> 8, _e7 >> 16, _e7 >> 24) << 24 >> 24, int4(_e9, _e9 >> 8, _e9 >> 16, _e9 >> 24) << 24 >> 24);\n    uint _e12 = (7u + c_6_);\n    uint _e14 = (8u + c_6_);\n    uint c_8_ = dot(uint4(_e12, _e12 >> 8, _e12 >> 16, _e12 >> 24) << 24 >> 24, uint4(_e14, _e14 >> 8, _e14 >> 16, _e14 >> 24) << 24 >> 24);\n    return c_8_;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const uint _e0 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions-unoptimized.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_3\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions.hlsl",
    "content": "float2 test_fma()\n{\n    float2 a = float2(2.0, 2.0);\n    float2 b = float2(0.5, 0.5);\n    float2 c = float2(0.5, 0.5);\n    return mad(a, b, c);\n}\n\nint test_integer_dot_product()\n{\n    int2 a_2_ = (int(1)).xx;\n    int2 b_2_ = (int(1)).xx;\n    int c_2_ = dot(a_2_, b_2_);\n    uint3 a_3_ = (1u).xxx;\n    uint3 b_3_ = (1u).xxx;\n    uint c_3_ = dot(a_3_, b_3_);\n    return int(32);\n}\n\nuint test_packed_integer_dot_product()\n{\n    int c_5_ = dot(int4(1u, 1u >> 8, 1u >> 16, 1u >> 24) << 24 >> 24, int4(2u, 2u >> 8, 2u >> 16, 2u >> 24) << 24 >> 24);\n    uint c_6_ = dot(uint4(3u, 3u >> 8, 3u >> 16, 3u >> 24) << 24 >> 24, uint4(4u, 4u >> 8, 4u >> 16, 4u >> 24) << 24 >> 24);\n    uint _e7 = (5u + c_6_);\n    uint _e9 = (6u + c_6_);\n    int c_7_ = dot(int4(_e7, _e7 >> 8, _e7 >> 16, _e7 >> 24) << 24 >> 24, int4(_e9, _e9 >> 8, _e9 >> 16, _e9 >> 24) << 24 >> 24);\n    uint _e12 = (7u + c_6_);\n    uint _e14 = (8u + c_6_);\n    uint c_8_ = dot(uint4(_e12, _e12 >> 8, _e12 >> 16, _e12 >> 24) << 24 >> 24, uint4(_e14, _e14 >> 8, _e14 >> 16, _e14 >> 24) << 24 >> 24);\n    return c_8_;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const float2 _e0 = test_fma();\n    const int _e1 = test_integer_dot_product();\n    const uint _e2 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-functions.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-globals.hlsl",
    "content": "typedef struct { float2 _0; float2 _1; float2 _2; } __mat3x2;\nfloat2 __get_col_of_mat3x2(__mat3x2 mat, uint idx) {\n    switch(idx) {\n    case 0: { return mat._0; }\n    case 1: { return mat._1; }\n    case 2: { return mat._2; }\n    default: { return (float2)0; }\n    }\n}\nvoid __set_col_of_mat3x2(__mat3x2 mat, uint idx, float2 value) {\n    switch(idx) {\n    case 0: { mat._0 = value; break; }\n    case 1: { mat._1 = value; break; }\n    case 2: { mat._2 = value; break; }\n    }\n}\nvoid __set_el_of_mat3x2(__mat3x2 mat, uint idx, uint vec_idx, float value) {\n    switch(idx) {\n    case 0: { mat._0[vec_idx] = value; break; }\n    case 1: { mat._1[vec_idx] = value; break; }\n    case 2: { mat._2[vec_idx] = value; break; }\n    }\n}\n\ntypedef struct { float2 _0; float2 _1; float2 _2; float2 _3; } __mat4x2;\nfloat2 __get_col_of_mat4x2(__mat4x2 mat, uint idx) {\n    switch(idx) {\n    case 0: { return mat._0; }\n    case 1: { return mat._1; }\n    case 2: { return mat._2; }\n    case 3: { return mat._3; }\n    default: { return (float2)0; }\n    }\n}\nvoid __set_col_of_mat4x2(__mat4x2 mat, uint idx, float2 value) {\n    switch(idx) {\n    case 0: { mat._0 = value; break; }\n    case 1: { mat._1 = value; break; }\n    case 2: { mat._2 = value; break; }\n    case 3: { mat._3 = value; break; }\n    }\n}\nvoid __set_el_of_mat4x2(__mat4x2 mat, uint idx, uint vec_idx, float value) {\n    switch(idx) {\n    case 0: { mat._0[vec_idx] = value; break; }\n    case 1: { mat._1[vec_idx] = value; break; }\n    case 2: { mat._2[vec_idx] = value; break; }\n    case 3: { mat._3[vec_idx] = value; break; }\n    }\n}\n\nstruct FooStruct {\n    float3 v3_;\n    float v1_;\n};\n\nstatic const bool Foo_1 = true;\n\ngroupshared float wg[10];\ngroupshared uint at_1;\nRWByteAddressBuffer alignment : register(u1);\nByteAddressBuffer dummy : register(t2);\ncbuffer float_vecs : register(b3) { float4 float_vecs[20]; }\ncbuffer global_vec : register(b4) { float3 global_vec; }\ncbuffer global_mat : register(b5) { __mat3x2 global_mat; }\ncbuffer global_nested_arrays_of_matrices_2x4_ : register(b6) { row_major float2x4 global_nested_arrays_of_matrices_2x4_[2][2]; }\ncbuffer global_nested_arrays_of_matrices_4x2_ : register(b7) { __mat4x2 global_nested_arrays_of_matrices_4x2_[2][2]; }\n\nvoid test_msl_packed_vec3_as_arg(float3 arg)\n{\n    return;\n}\n\nfloat3x3 ZeroValuefloat3x3() {\n    return (float3x3)0;\n}\n\nFooStruct ConstructFooStruct(float3 arg0, float arg1) {\n    FooStruct ret = (FooStruct)0;\n    ret.v3_ = arg0;\n    ret.v1_ = arg1;\n    return ret;\n}\n\nvoid test_msl_packed_vec3_()\n{\n    int idx = int(1);\n\n    alignment.Store3(0, asuint((1.0).xxx));\n    alignment.Store(0+0, asuint(1.0));\n    alignment.Store(0+0, asuint(2.0));\n    int _e16 = idx;\n    alignment.Store(_e16*4+0, asuint(3.0));\n    FooStruct data = ConstructFooStruct(asfloat(alignment.Load3(0)), asfloat(alignment.Load(12)));\n    float3 l0_ = data.v3_;\n    float2 l1_ = data.v3_.zx;\n    test_msl_packed_vec3_as_arg(data.v3_);\n    float3 mvm0_ = mul(ZeroValuefloat3x3(), data.v3_);\n    float3 mvm1_ = mul(data.v3_, ZeroValuefloat3x3());\n    float3 svm0_ = (data.v3_ * 2.0);\n    float3 svm1_ = (2.0 * data.v3_);\n    return;\n}\n\nuint NagaBufferLength(ByteAddressBuffer buffer)\n{\n    uint ret;\n    buffer.GetDimensions(ret);\n    return ret;\n}\n\n[numthreads(1, 1, 1)]\nvoid main(uint3 local_invocation_id : SV_GroupThreadID)\n{\n    if (all(local_invocation_id == uint3(0u, 0u, 0u))) {\n        wg = (float[10])0;\n        at_1 = (uint)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    float Foo = 1.0;\n    bool at = true;\n\n    test_msl_packed_vec3_();\n    float4x2 _e5 = ((float4x2)global_nested_arrays_of_matrices_4x2_[0][0]);\n    float4 _e10 = global_nested_arrays_of_matrices_2x4_[0][0][0];\n    wg[7] = mul(_e10, _e5).x;\n    float3x2 _e16 = ((float3x2)global_mat);\n    float3 _e18 = global_vec;\n    wg[6] = mul(_e18, _e16).x;\n    float _e26 = asfloat(dummy.Load(4+8));\n    wg[5] = _e26;\n    float _e32 = float_vecs[0].w;\n    wg[4] = _e32;\n    float _e37 = asfloat(alignment.Load(12));\n    wg[3] = _e37;\n    float _e43 = asfloat(alignment.Load(0+0));\n    wg[2] = _e43;\n    alignment.Store(12, asuint(4.0));\n    wg[1] = float(((NagaBufferLength(dummy) - 0) / 8));\n    at_1 = 2u;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-globals.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-hlsl-keyword.hlsl",
    "content": "float4 fs_main() : SV_Target0\n{\n    float4 Pass_ = float4(1.0, 1.0, 1.0, 1.0);\n\n    float4 _e6 = Pass_;\n    return _e6;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-hlsl-keyword.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"fs_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-image.hlsl",
    "content": "Texture2D<uint4> image_mipmapped_src : register(t0);\nTexture2DMS<uint4> image_multisampled_src : register(t3);\nTexture2DMS<float> image_depth_multisampled_src : register(t4);\nRWTexture2D<uint4> image_storage_src : register(u1);\nTexture2DArray<uint4> image_array_src : register(t5);\nRWTexture1D<uint> image_dup_src : register(u6);\nTexture1D<uint4> image_1d_src : register(t7);\nRWTexture1D<uint> image_dst : register(u2);\nTexture1D<float4> image_1d : register(t0);\nTexture2D<float4> image_2d : register(t1);\nTexture2D<uint4> image_2d_u32_ : register(t2);\nTexture2D<int4> image_2d_i32_ : register(t3);\nTexture2DArray<float4> image_2d_array : register(t4);\nTextureCube<float4> image_cube : register(t5);\nTextureCubeArray<float4> image_cube_array : register(t6);\nTexture3D<float4> image_3d : register(t7);\nTexture2DMS<float4> image_aa : register(t8);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup1SamplerIndexArray : register(t1, space255);\nstatic const SamplerState sampler_reg = nagaSamplerHeap[nagaGroup1SamplerIndexArray[0]];\nstatic const SamplerComparisonState sampler_cmp = nagaComparisonSamplerHeap[nagaGroup1SamplerIndexArray[1]];\nTexture2D<float> image_2d_depth : register(t2, space1);\nTexture2DArray<float> image_2d_array_depth : register(t3, space1);\nTextureCube<float> image_cube_depth : register(t4, space1);\n\nint2 naga_mod(int2 lhs, int2 rhs) {\n    int2 divisor = ((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs;\n    return lhs - (lhs / divisor) * divisor;\n}\n\nuint4 LoadedStorageValueFromuint(uint arg) {uint4 ret = uint4(arg, 0u, 0u, 1u);return ret;}\nuint2 NagaRWDimensions2D(RWTexture2D<uint4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(ret.x, ret.y);\n    return ret.xy;\n}\n\n[numthreads(16, 1, 1)]\nvoid main(uint3 local_id : SV_GroupThreadID)\n{\n    uint2 dim = NagaRWDimensions2D(image_storage_src);\n    int2 itc = naga_mod(int2((dim * local_id.xy)), int2(int(10), int(20)));\n    uint4 value1_ = image_mipmapped_src.Load(int3(itc, int(local_id.z)));\n    uint4 value1_2_ = image_mipmapped_src.Load(int3(itc, int(uint(local_id.z))));\n    uint4 value2_ = image_multisampled_src.Load(itc, int(local_id.z));\n    uint4 value3_ = image_multisampled_src.Load(itc, uint(local_id.z));\n    uint4 value4_ = image_storage_src.Load(itc);\n    uint4 value5_ = image_array_src.Load(int4(itc, local_id.z, asint(asuint(int(local_id.z)) + asuint(int(1)))));\n    uint4 value6_ = image_array_src.Load(int4(itc, int(local_id.z), asint(asuint(int(local_id.z)) + asuint(int(1)))));\n    uint4 value7_ = image_1d_src.Load(int2(int(local_id.x), int(local_id.z)));\n    uint4 value8_ = LoadedStorageValueFromuint(image_dup_src.Load(int(local_id.x)));\n    uint4 value1u = image_mipmapped_src.Load(int3(uint2(itc), int(local_id.z)));\n    uint4 value2u = image_multisampled_src.Load(uint2(itc), int(local_id.z));\n    uint4 value3u = image_multisampled_src.Load(uint2(itc), uint(local_id.z));\n    uint4 value4u = image_storage_src.Load(uint2(itc));\n    uint4 value5u = image_array_src.Load(int4(uint2(itc), local_id.z, asint(asuint(int(local_id.z)) + asuint(int(1)))));\n    uint4 value6u = image_array_src.Load(int4(uint2(itc), int(local_id.z), asint(asuint(int(local_id.z)) + asuint(int(1)))));\n    uint4 value7u = image_1d_src.Load(int2(uint(local_id.x), int(local_id.z)));\n    image_dst[itc.x] = ((((value1_ + value2_) + value4_) + value5_) + value6_);\n    image_dst[uint(itc.x)] = ((((value1u + value2u) + value4u) + value5u) + value6u);\n    return;\n}\n\nuint naga_f2u32(float value) {\n    return uint(clamp(value, 0.0, 4294967000.0));\n}\n\n[numthreads(16, 1, 1)]\nvoid depth_load(uint3 local_id_1 : SV_GroupThreadID)\n{\n    uint2 dim_1 = NagaRWDimensions2D(image_storage_src);\n    int2 itc_1 = naga_mod(int2((dim_1 * local_id_1.xy)), int2(int(10), int(20)));\n    float val = image_depth_multisampled_src.Load(itc_1, int(local_id_1.z)).x;\n    image_dst[itc_1.x] = (naga_f2u32(val)).xxxx;\n    return;\n}\n\nuint NagaDimensions1D(Texture1D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y);\n    return ret.x;\n}\n\nuint NagaMipDimensions1D(Texture1D<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y);\n    return ret.x;\n}\n\nuint2 NagaDimensions2D(Texture2D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nuint2 NagaMipDimensions2D(Texture2D<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nuint2 NagaDimensions2DArray(Texture2DArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.xy;\n}\n\nuint2 NagaMipDimensions2DArray(Texture2DArray<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y, ret.z, ret.w);\n    return ret.xy;\n}\n\nuint2 NagaDimensionsCube(TextureCube<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nuint2 NagaMipDimensionsCube(TextureCube<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nuint2 NagaDimensionsCubeArray(TextureCubeArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.xy;\n}\n\nuint2 NagaMipDimensionsCubeArray(TextureCubeArray<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y, ret.z, ret.w);\n    return ret.xy;\n}\n\nuint3 NagaDimensions3D(Texture3D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.xyz;\n}\n\nuint3 NagaMipDimensions3D(Texture3D<float4> tex, uint mip_level)\n{\n    uint4 ret;\n    tex.GetDimensions(mip_level, ret.x, ret.y, ret.z, ret.w);\n    return ret.xyz;\n}\n\nuint2 NagaMSDimensions2D(Texture2DMS<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(ret.x, ret.y, ret.z);\n    return ret.xy;\n}\n\nfloat4 queries() : SV_Position\n{\n    uint dim_1d = NagaDimensions1D(image_1d);\n    uint dim_1d_lod = NagaMipDimensions1D(image_1d, int(dim_1d));\n    uint2 dim_2d = NagaDimensions2D(image_2d);\n    uint2 dim_2d_lod = NagaMipDimensions2D(image_2d, int(1));\n    uint2 dim_2d_array = NagaDimensions2DArray(image_2d_array);\n    uint2 dim_2d_array_lod = NagaMipDimensions2DArray(image_2d_array, int(1));\n    uint2 dim_cube = NagaDimensionsCube(image_cube);\n    uint2 dim_cube_lod = NagaMipDimensionsCube(image_cube, int(1));\n    uint2 dim_cube_array = NagaDimensionsCubeArray(image_cube_array);\n    uint2 dim_cube_array_lod = NagaMipDimensionsCubeArray(image_cube_array, int(1));\n    uint3 dim_3d = NagaDimensions3D(image_3d);\n    uint3 dim_3d_lod = NagaMipDimensions3D(image_3d, int(1));\n    uint2 dim_2s_ms = NagaMSDimensions2D(image_aa);\n    uint sum = ((((((((((dim_1d + dim_2d.y) + dim_2d_lod.y) + dim_2d_array.y) + dim_2d_array_lod.y) + dim_cube.y) + dim_cube_lod.y) + dim_cube_array.y) + dim_cube_array_lod.y) + dim_3d.z) + dim_3d_lod.z);\n    return (float(sum)).xxxx;\n}\n\nuint NagaNumLevels2D(Texture2D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.z;\n}\n\nuint NagaNumLayers2DArray(Texture2DArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaNumLevels2DArray(Texture2DArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaNumLevelsCube(TextureCube<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z);\n    return ret.z;\n}\n\nuint NagaNumLevelsCubeArray(TextureCubeArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaNumLayersCubeArray(TextureCubeArray<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaNumLevels3D(Texture3D<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(0, ret.x, ret.y, ret.z, ret.w);\n    return ret.w;\n}\n\nuint NagaMSNumSamples2D(Texture2DMS<float4> tex)\n{\n    uint4 ret;\n    tex.GetDimensions(ret.x, ret.y, ret.z);\n    return ret.z;\n}\n\nfloat4 levels_queries() : SV_Position\n{\n    uint num_levels_2d = NagaNumLevels2D(image_2d);\n    uint num_layers_2d = NagaNumLayers2DArray(image_2d_array);\n    uint num_levels_2d_array = NagaNumLevels2DArray(image_2d_array);\n    uint num_layers_2d_array = NagaNumLayers2DArray(image_2d_array);\n    uint num_levels_cube = NagaNumLevelsCube(image_cube);\n    uint num_levels_cube_array = NagaNumLevelsCubeArray(image_cube_array);\n    uint num_layers_cube = NagaNumLayersCubeArray(image_cube_array);\n    uint num_levels_3d = NagaNumLevels3D(image_3d);\n    uint num_samples_aa = NagaMSNumSamples2D(image_aa);\n    uint sum_1 = (((((((num_layers_2d + num_layers_cube) + num_samples_aa) + num_levels_2d) + num_levels_2d_array) + num_levels_3d) + num_levels_cube) + num_levels_cube_array);\n    return (float(sum_1)).xxxx;\n}\n\nfloat4 nagaTextureSampleBaseClampToEdge(Texture2D<float4> tex, SamplerState samp, float2 coords) {\n    float2 size;\n    tex.GetDimensions(size.x, size.y);\n    float2 half_texel = float2(0.5, 0.5) / size;\n    return tex.SampleLevel(samp, clamp(coords, half_texel, 1.0 - half_texel), 0.0);\n}\n\nfloat4 texture_sample() : SV_Target0\n{\n    float4 a = (float4)0;\n\n    float2 _e1 = (0.5).xx;\n    float3 _e3 = (0.5).xxx;\n    int2 _e6 = int2(int(3), int(1));\n    float4 _e9 = a;\n    float4 _e12 = image_1d.Sample(sampler_reg, 0.5);\n    a = (_e9 + _e12);\n    float4 _e14 = a;\n    float4 _e17 = image_2d.Sample(sampler_reg, _e1);\n    a = (_e14 + _e17);\n    float4 _e19 = a;\n    float4 _e25 = image_2d.Sample(sampler_reg, _e1, int2(int2(int(3), int(1))));\n    a = (_e19 + _e25);\n    float4 _e27 = a;\n    float4 _e30 = image_2d.SampleLevel(sampler_reg, _e1, 2.3);\n    a = (_e27 + _e30);\n    float4 _e32 = a;\n    float4 _e35 = image_2d.SampleLevel(sampler_reg, _e1, 2.3, int2(int2(int(3), int(1))));\n    a = (_e32 + _e35);\n    float4 _e37 = a;\n    float4 _e41 = image_2d.SampleBias(sampler_reg, _e1, 2.0, int2(int2(int(3), int(1))));\n    a = (_e37 + _e41);\n    float4 _e43 = a;\n    float4 _e46 = nagaTextureSampleBaseClampToEdge(image_2d, sampler_reg, _e1);\n    a = (_e43 + _e46);\n    float4 _e48 = a;\n    float4 _e52 = image_2d_array.Sample(sampler_reg, float3(_e1, 0u));\n    a = (_e48 + _e52);\n    float4 _e54 = a;\n    float4 _e58 = image_2d_array.Sample(sampler_reg, float3(_e1, 0u), int2(int2(int(3), int(1))));\n    a = (_e54 + _e58);\n    float4 _e60 = a;\n    float4 _e64 = image_2d_array.SampleLevel(sampler_reg, float3(_e1, 0u), 2.3);\n    a = (_e60 + _e64);\n    float4 _e66 = a;\n    float4 _e70 = image_2d_array.SampleLevel(sampler_reg, float3(_e1, 0u), 2.3, int2(int2(int(3), int(1))));\n    a = (_e66 + _e70);\n    float4 _e72 = a;\n    float4 _e77 = image_2d_array.SampleBias(sampler_reg, float3(_e1, 0u), 2.0, int2(int2(int(3), int(1))));\n    a = (_e72 + _e77);\n    float4 _e79 = a;\n    float4 _e83 = image_2d_array.Sample(sampler_reg, float3(_e1, int(0)));\n    a = (_e79 + _e83);\n    float4 _e85 = a;\n    float4 _e89 = image_2d_array.Sample(sampler_reg, float3(_e1, int(0)), int2(int2(int(3), int(1))));\n    a = (_e85 + _e89);\n    float4 _e91 = a;\n    float4 _e95 = image_2d_array.SampleLevel(sampler_reg, float3(_e1, int(0)), 2.3);\n    a = (_e91 + _e95);\n    float4 _e97 = a;\n    float4 _e101 = image_2d_array.SampleLevel(sampler_reg, float3(_e1, int(0)), 2.3, int2(int2(int(3), int(1))));\n    a = (_e97 + _e101);\n    float4 _e103 = a;\n    float4 _e108 = image_2d_array.SampleBias(sampler_reg, float3(_e1, int(0)), 2.0, int2(int2(int(3), int(1))));\n    a = (_e103 + _e108);\n    float4 _e110 = a;\n    float4 _e114 = image_cube_array.Sample(sampler_reg, float4(_e3, 0u));\n    a = (_e110 + _e114);\n    float4 _e116 = a;\n    float4 _e120 = image_cube_array.SampleLevel(sampler_reg, float4(_e3, 0u), 2.3);\n    a = (_e116 + _e120);\n    float4 _e122 = a;\n    float4 _e127 = image_cube_array.SampleBias(sampler_reg, float4(_e3, 0u), 2.0);\n    a = (_e122 + _e127);\n    float4 _e129 = a;\n    float4 _e133 = image_cube_array.Sample(sampler_reg, float4(_e3, int(0)));\n    a = (_e129 + _e133);\n    float4 _e135 = a;\n    float4 _e139 = image_cube_array.SampleLevel(sampler_reg, float4(_e3, int(0)), 2.3);\n    a = (_e135 + _e139);\n    float4 _e141 = a;\n    float4 _e146 = image_cube_array.SampleBias(sampler_reg, float4(_e3, int(0)), 2.0);\n    a = (_e141 + _e146);\n    float4 _e148 = a;\n    return _e148;\n}\n\nfloat texture_sample_comparison() : SV_Target0\n{\n    float a_1 = (float)0;\n\n    float2 tc = (0.5).xx;\n    float3 tc3_ = (0.5).xxx;\n    float _e6 = a_1;\n    float _e9 = image_2d_depth.SampleCmp(sampler_cmp, tc, 0.5);\n    a_1 = (_e6 + _e9);\n    float _e11 = a_1;\n    float _e15 = image_2d_array_depth.SampleCmp(sampler_cmp, float3(tc, 0u), 0.5);\n    a_1 = (_e11 + _e15);\n    float _e17 = a_1;\n    float _e21 = image_2d_array_depth.SampleCmp(sampler_cmp, float3(tc, int(0)), 0.5);\n    a_1 = (_e17 + _e21);\n    float _e23 = a_1;\n    float _e26 = image_cube_depth.SampleCmp(sampler_cmp, tc3_, 0.5);\n    a_1 = (_e23 + _e26);\n    float _e28 = a_1;\n    float _e31 = image_2d_depth.SampleCmpLevelZero(sampler_cmp, tc, 0.5);\n    a_1 = (_e28 + _e31);\n    float _e33 = a_1;\n    float _e37 = image_2d_array_depth.SampleCmpLevelZero(sampler_cmp, float3(tc, 0u), 0.5);\n    a_1 = (_e33 + _e37);\n    float _e39 = a_1;\n    float _e43 = image_2d_array_depth.SampleCmpLevelZero(sampler_cmp, float3(tc, int(0)), 0.5);\n    a_1 = (_e39 + _e43);\n    float _e45 = a_1;\n    float _e48 = image_cube_depth.SampleCmpLevelZero(sampler_cmp, tc3_, 0.5);\n    a_1 = (_e45 + _e48);\n    float _e50 = a_1;\n    return _e50;\n}\n\nfloat4 gather() : SV_Target0\n{\n    float2 tc_1 = (0.5).xx;\n    float4 s2d = image_2d.GatherGreen(sampler_reg, tc_1);\n    float4 s2d_offset = image_2d.GatherAlpha(sampler_reg, tc_1, int2(int2(int(3), int(1))));\n    float4 s2d_depth = image_2d_depth.GatherCmp(sampler_cmp, tc_1, 0.5);\n    float4 s2d_depth_offset = image_2d_depth.GatherCmp(sampler_cmp, tc_1, 0.5, int2(int2(int(3), int(1))));\n    uint4 u = image_2d_u32_.Gather(sampler_reg, tc_1);\n    int4 i = image_2d_i32_.Gather(sampler_reg, tc_1);\n    float4 f = (float4(u) + float4(i));\n    return ((((s2d + s2d_offset) + s2d_depth) + s2d_depth_offset) + f);\n}\n\nfloat4 depth_no_comparison() : SV_Target0\n{\n    float2 tc_2 = (0.5).xx;\n    float s2d_1 = image_2d_depth.Sample(sampler_reg, tc_2);\n    float4 s2d_gather = image_2d_depth.Gather(sampler_reg, tc_2);\n    float s2d_level = image_2d_depth.SampleLevel(sampler_reg, tc_2, int(1));\n    return (((s2d_1).xxxx + s2d_gather) + (s2d_level).xxxx);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-image.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"queries\",\n            target_profile:\"vs_5_1\",\n        ),\n        (\n            entry_point:\"levels_queries\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"texture_sample\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"texture_sample_comparison\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"gather\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"depth_no_comparison\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n        (\n            entry_point:\"depth_load\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-int64.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct UniformCompatible {\n    uint val_u32_;\n    int val_i32_;\n    float val_f32_;\n    int _pad3_0;\n    uint64_t val_u64_;\n    int _pad4_0;\n    int _pad4_1;\n    uint64_t2 val_u64_2_;\n    int _pad5_0;\n    int _pad5_1;\n    int _pad5_2;\n    int _pad5_3;\n    uint64_t3 val_u64_3_;\n    int _pad6_0;\n    int _pad6_1;\n    uint64_t4 val_u64_4_;\n    int64_t val_i64_;\n    int _pad8_0;\n    int _pad8_1;\n    int64_t2 val_i64_2_;\n    int64_t3 val_i64_3_;\n    int _pad10_0;\n    int _pad10_1;\n    int64_t4 val_i64_4_;\n    uint64_t final_value;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n    int _end_pad_3;\n    int _end_pad_4;\n    int _end_pad_5;\n};\n\nstruct StorageCompatible {\n    uint64_t val_u64_array_2_[2];\n    int64_t val_i64_array_2_[2];\n};\n\nstatic const uint64_t constant_variable = 20uL;\n\nstatic int64_t private_variable = 1L;\ncbuffer input_uniform : register(b0) { UniformCompatible input_uniform; }\nByteAddressBuffer input_storage : register(t1);\nByteAddressBuffer input_arrays : register(t2);\nRWByteAddressBuffer output : register(u3);\nRWByteAddressBuffer output_arrays : register(u4);\n\nint64_t naga_f2i64(float value) {\n    return int64_t(clamp(value, -9.223372e18, 9.2233715e18));\n}\n\ntypedef int64_t ret_Constructarray2_int64_t_[2];\nret_Constructarray2_int64_t_ Constructarray2_int64_t_(int64_t arg0, int64_t arg1) {\n    int64_t ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nint64_t int64_function(int64_t x)\n{\n    int64_t val = 20L;\n\n    int64_t phony = private_variable;\n    int64_t _e5 = val;\n    val = (_e5 + ((31L - 1002003004005006L) + -9223372036854775807L));\n    int64_t _e12 = val;\n    int64_t _e13 = val;\n    val = (_e12 + (_e13 + 5L));\n    int64_t _e17 = val;\n    uint _e20 = input_uniform.val_u32_;\n    int64_t _e21 = val;\n    val = (_e17 + int64_t((_e20 + uint(_e21))));\n    int64_t _e26 = val;\n    int _e29 = input_uniform.val_i32_;\n    int64_t _e30 = val;\n    val = (_e26 + int64_t(asint(asuint(_e29) + asuint(int(_e30)))));\n    int64_t _e35 = val;\n    float _e38 = input_uniform.val_f32_;\n    int64_t _e39 = val;\n    val = (_e35 + naga_f2i64((_e38 + float(_e39))));\n    int64_t _e44 = val;\n    int64_t _e47 = input_uniform.val_i64_;\n    val = (_e44 + (_e47).xxx.z);\n    int64_t _e51 = val;\n    uint64_t _e54 = input_uniform.val_u64_;\n    val = (_e51 + _e54);\n    int64_t _e57 = val;\n    uint64_t2 _e60 = input_uniform.val_u64_2_;\n    val = (_e57 + _e60.y);\n    int64_t _e64 = val;\n    uint64_t3 _e67 = input_uniform.val_u64_3_;\n    val = (_e64 + _e67.z);\n    int64_t _e71 = val;\n    uint64_t4 _e74 = input_uniform.val_u64_4_;\n    val = (_e71 + _e74.w);\n    int64_t _e78 = val;\n    val = (_e78 + (-9223372036854775807L - 1L));\n    int64_t _e85 = input_uniform.val_i64_;\n    int64_t _e88 = input_storage.Load<int64_t>(128);\n    output.Store(128, (_e85 + _e88));\n    int64_t2 _e94 = input_uniform.val_i64_2_;\n    int64_t2 _e97 = input_storage.Load<int64_t2>(144);\n    output.Store(144, (_e94 + _e97));\n    int64_t3 _e103 = input_uniform.val_i64_3_;\n    int64_t3 _e106 = input_storage.Load<int64_t3>(160);\n    output.Store(160, (_e103 + _e106));\n    int64_t4 _e112 = input_uniform.val_i64_4_;\n    int64_t4 _e115 = input_storage.Load<int64_t4>(192);\n    output.Store(192, (_e112 + _e115));\n    int64_t _e121[2] = Constructarray2_int64_t_(input_arrays.Load<int64_t>(16+0), input_arrays.Load<int64_t>(16+8));\n    {\n        int64_t _value2[2] = _e121;\n        output_arrays.Store(16+0, _value2[0]);\n        output_arrays.Store(16+8, _value2[1]);\n    }\n    int64_t _e122 = val;\n    int64_t _e123 = val;\n    val = (_e122 + abs(_e123));\n    int64_t _e126 = val;\n    int64_t _e127 = val;\n    int64_t _e128 = val;\n    int64_t _e129 = val;\n    val = (_e126 + clamp(_e127, _e128, _e129));\n    int64_t _e132 = val;\n    int64_t _e133 = val;\n    int64_t _e135 = val;\n    val = (_e132 + dot((_e133).xx, (_e135).xx));\n    int64_t _e139 = val;\n    int64_t _e140 = val;\n    int64_t _e141 = val;\n    val = (_e139 + max(_e140, _e141));\n    int64_t _e144 = val;\n    int64_t _e145 = val;\n    int64_t _e146 = val;\n    val = (_e144 + min(_e145, _e146));\n    int64_t _e149 = val;\n    int64_t _e150 = val;\n    val = (_e149 + sign(_e150));\n    int64_t _e153 = val;\n    return _e153;\n}\n\nuint64_t naga_f2u64(float value) {\n    return uint64_t(clamp(value, 0.0, 1.8446743e19));\n}\n\ntypedef uint64_t ret_Constructarray2_uint64_t_[2];\nret_Constructarray2_uint64_t_ Constructarray2_uint64_t_(uint64_t arg0, uint64_t arg1) {\n    uint64_t ret[2] = { arg0, arg1 };\n    return ret;\n}\n\nuint64_t uint64_function(uint64_t x_1)\n{\n    uint64_t val_1 = 20uL;\n\n    uint64_t _e3 = val_1;\n    val_1 = (_e3 + ((31uL + 18446744073709551615uL) - 18446744073709551615uL));\n    uint64_t _e10 = val_1;\n    uint64_t _e11 = val_1;\n    val_1 = (_e10 + (_e11 + 5uL));\n    uint64_t _e15 = val_1;\n    uint _e18 = input_uniform.val_u32_;\n    uint64_t _e19 = val_1;\n    val_1 = (_e15 + uint64_t((_e18 + uint(_e19))));\n    uint64_t _e24 = val_1;\n    int _e27 = input_uniform.val_i32_;\n    uint64_t _e28 = val_1;\n    val_1 = (_e24 + uint64_t(asint(asuint(_e27) + asuint(int(_e28)))));\n    uint64_t _e33 = val_1;\n    float _e36 = input_uniform.val_f32_;\n    uint64_t _e37 = val_1;\n    val_1 = (_e33 + naga_f2u64((_e36 + float(_e37))));\n    uint64_t _e42 = val_1;\n    uint64_t _e45 = input_uniform.val_u64_;\n    val_1 = (_e42 + (_e45).xxx.z);\n    uint64_t _e49 = val_1;\n    int64_t _e52 = input_uniform.val_i64_;\n    val_1 = (_e49 + _e52);\n    uint64_t _e55 = val_1;\n    int64_t2 _e58 = input_uniform.val_i64_2_;\n    val_1 = (_e55 + _e58.y);\n    uint64_t _e62 = val_1;\n    int64_t3 _e65 = input_uniform.val_i64_3_;\n    val_1 = (_e62 + _e65.z);\n    uint64_t _e69 = val_1;\n    int64_t4 _e72 = input_uniform.val_i64_4_;\n    val_1 = (_e69 + _e72.w);\n    uint64_t _e80 = input_uniform.val_u64_;\n    uint64_t _e83 = input_storage.Load<uint64_t>(16);\n    output.Store(16, (_e80 + _e83));\n    uint64_t2 _e89 = input_uniform.val_u64_2_;\n    uint64_t2 _e92 = input_storage.Load<uint64_t2>(32);\n    output.Store(32, (_e89 + _e92));\n    uint64_t3 _e98 = input_uniform.val_u64_3_;\n    uint64_t3 _e101 = input_storage.Load<uint64_t3>(64);\n    output.Store(64, (_e98 + _e101));\n    uint64_t4 _e107 = input_uniform.val_u64_4_;\n    uint64_t4 _e110 = input_storage.Load<uint64_t4>(96);\n    output.Store(96, (_e107 + _e110));\n    uint64_t _e116[2] = Constructarray2_uint64_t_(input_arrays.Load<uint64_t>(0+0), input_arrays.Load<uint64_t>(0+8));\n    {\n        uint64_t _value2[2] = _e116;\n        output_arrays.Store(0+0, _value2[0]);\n        output_arrays.Store(0+8, _value2[1]);\n    }\n    uint64_t _e117 = val_1;\n    uint64_t _e118 = val_1;\n    val_1 = (_e117 + abs(_e118));\n    uint64_t _e121 = val_1;\n    uint64_t _e122 = val_1;\n    uint64_t _e123 = val_1;\n    uint64_t _e124 = val_1;\n    val_1 = (_e121 + clamp(_e122, _e123, _e124));\n    uint64_t _e127 = val_1;\n    uint64_t _e128 = val_1;\n    uint64_t _e130 = val_1;\n    val_1 = (_e127 + dot((_e128).xx, (_e130).xx));\n    uint64_t _e134 = val_1;\n    uint64_t _e135 = val_1;\n    uint64_t _e136 = val_1;\n    val_1 = (_e134 + max(_e135, _e136));\n    uint64_t _e139 = val_1;\n    uint64_t _e140 = val_1;\n    uint64_t _e141 = val_1;\n    val_1 = (_e139 + min(_e140, _e141));\n    uint64_t _e144 = val_1;\n    return _e144;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    const uint64_t _e3 = uint64_function(67uL);\n    const int64_t _e5 = int64_function(60L);\n    output.Store(224, (_e3 + _e5));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-int64.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_0\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interface.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct VertexOutput {\n    precise float4 position : SV_Position;\n    float _varying : LOC1;\n};\n\nstruct FragmentOutput {\n    float depth : SV_Depth;\n    uint sample_mask : SV_Coverage;\n    float color : SV_Target0;\n};\n\nstruct Input1_ {\n    uint index : SV_VertexID;\n};\n\nstruct Input2_ {\n    uint index : SV_InstanceID;\n};\n\ngroupshared uint output[1];\n\nstruct VertexOutput_vertex {\n    float _varying : LOC1;\n    precise float4 position : SV_Position;\n};\n\nstruct FragmentInput_fragment {\n    float _varying_1 : LOC1;\n    precise float4 position_1 : SV_Position;\n    bool front_facing_1 : SV_IsFrontFace;\n    uint sample_index_1 : SV_SampleIndex;\n    uint sample_mask_1 : SV_Coverage;\n};\n\nVertexOutput ConstructVertexOutput(float4 arg0, float arg1) {\n    VertexOutput ret = (VertexOutput)0;\n    ret.position = arg0;\n    ret._varying = arg1;\n    return ret;\n}\n\nVertexOutput_vertex vertex(uint vertex_index : SV_VertexID, uint instance_index : SV_InstanceID, uint color : LOC10)\n{\n    uint tmp = (((_NagaConstants.first_vertex + vertex_index) + (_NagaConstants.first_instance + instance_index)) + color);\n    const VertexOutput vertexoutput = ConstructVertexOutput((1.0).xxxx, float(tmp));\n    const VertexOutput_vertex vertexoutput_1 = { vertexoutput._varying, vertexoutput.position };\n    return vertexoutput_1;\n}\n\nFragmentOutput ConstructFragmentOutput(float arg0, uint arg1, float arg2) {\n    FragmentOutput ret = (FragmentOutput)0;\n    ret.depth = arg0;\n    ret.sample_mask = arg1;\n    ret.color = arg2;\n    return ret;\n}\n\nFragmentOutput fragment(FragmentInput_fragment fragmentinput_fragment)\n{\n    VertexOutput in_ = { fragmentinput_fragment.position_1, fragmentinput_fragment._varying_1 };\n    bool front_facing = fragmentinput_fragment.front_facing_1;\n    uint sample_index = fragmentinput_fragment.sample_index_1;\n    uint sample_mask = fragmentinput_fragment.sample_mask_1;\n    uint mask = (sample_mask & (1u << sample_index));\n    float color_1 = (front_facing ? 1.0 : 0.0);\n    const FragmentOutput fragmentoutput = ConstructFragmentOutput(in_._varying, mask, color_1);\n    return fragmentoutput;\n}\n\n[numthreads(1, 1, 1)]\nvoid compute(uint3 global_id : SV_DispatchThreadID, uint3 local_id : SV_GroupThreadID, uint local_index : SV_GroupIndex, uint3 wg_id : SV_GroupID, uint3 num_wgs : SV_GroupID)\n{\n    if (all(local_id == uint3(0u, 0u, 0u))) {\n        output = (uint[1])0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    output[0] = ((((global_id.x + local_id.x) + local_index) + wg_id.x) + uint3(_NagaConstants.first_vertex, _NagaConstants.first_instance, _NagaConstants.other).x);\n    return;\n}\n\nprecise float4 vertex_two_structs(Input1_ in1_, Input2_ in2_) : SV_Position\n{\n    uint index = 2u;\n\n    uint _e8 = index;\n    return float4(float((_NagaConstants.first_vertex + in1_.index)), float((_NagaConstants.first_instance + in2_.index)), float(_e8), 0.0);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interface.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vertex\",\n            target_profile:\"vs_5_1\",\n        ),\n        (\n            entry_point:\"vertex_two_structs\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"fragment\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n        (\n            entry_point:\"compute\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interpolate.hlsl",
    "content": "struct FragmentInput {\n    float4 position : SV_Position;\n    nointerpolation uint _flat : LOC0;\n    nointerpolation uint flat_first : LOC1;\n    nointerpolation uint flat_either : LOC2;\n    noperspective float _linear : LOC3;\n    noperspective centroid float2 linear_centroid : LOC4;\n    noperspective sample float3 linear_sample : LOC6;\n    noperspective float3 linear_center : LOC7;\n    float4 perspective : LOC8;\n    centroid float perspective_centroid : LOC9;\n    sample float perspective_sample : LOC10;\n    float perspective_center : LOC11;\n};\n\nstruct VertexOutput_vert_main {\n    nointerpolation uint _flat : LOC0;\n    nointerpolation uint flat_first : LOC1;\n    nointerpolation uint flat_either : LOC2;\n    noperspective float _linear : LOC3;\n    noperspective centroid float2 linear_centroid : LOC4;\n    noperspective sample float3 linear_sample : LOC6;\n    noperspective float3 linear_center : LOC7;\n    float4 perspective : LOC8;\n    centroid float perspective_centroid : LOC9;\n    sample float perspective_sample : LOC10;\n    float perspective_center : LOC11;\n    float4 position : SV_Position;\n};\n\nstruct FragmentInput_frag_main {\n    nointerpolation uint _flat_1 : LOC0;\n    nointerpolation uint flat_first_1 : LOC1;\n    nointerpolation uint flat_either_1 : LOC2;\n    noperspective float _linear_1 : LOC3;\n    noperspective centroid float2 linear_centroid_1 : LOC4;\n    noperspective sample float3 linear_sample_1 : LOC6;\n    noperspective float3 linear_center_1 : LOC7;\n    float4 perspective_1 : LOC8;\n    centroid float perspective_centroid_1 : LOC9;\n    sample float perspective_sample_1 : LOC10;\n    float perspective_center_1 : LOC11;\n    float4 position_1 : SV_Position;\n};\n\nVertexOutput_vert_main vert_main()\n{\n    FragmentInput out_ = (FragmentInput)0;\n\n    out_.position = float4(2.0, 4.0, 5.0, 6.0);\n    out_._flat = 8u;\n    out_.flat_first = 9u;\n    out_.flat_either = 10u;\n    out_._linear = 27.0;\n    out_.linear_centroid = float2(64.0, 125.0);\n    out_.linear_sample = float3(216.0, 343.0, 512.0);\n    out_.linear_center = float3(255.0, 511.0, 1024.0);\n    out_.perspective = float4(729.0, 1000.0, 1331.0, 1728.0);\n    out_.perspective_centroid = 2197.0;\n    out_.perspective_sample = 2744.0;\n    out_.perspective_center = 2812.0;\n    FragmentInput _e41 = out_;\n    const FragmentInput fragmentinput = _e41;\n    const VertexOutput_vert_main fragmentinput_1 = { fragmentinput._flat, fragmentinput.flat_first, fragmentinput.flat_either, fragmentinput._linear, fragmentinput.linear_centroid, fragmentinput.linear_sample, fragmentinput.linear_center, fragmentinput.perspective, fragmentinput.perspective_centroid, fragmentinput.perspective_sample, fragmentinput.perspective_center, fragmentinput.position };\n    return fragmentinput_1;\n}\n\nvoid frag_main(FragmentInput_frag_main fragmentinput_frag_main)\n{\n    FragmentInput val = { fragmentinput_frag_main.position_1, fragmentinput_frag_main._flat_1, fragmentinput_frag_main.flat_first_1, fragmentinput_frag_main.flat_either_1, fragmentinput_frag_main._linear_1, fragmentinput_frag_main.linear_centroid_1, fragmentinput_frag_main.linear_sample_1, fragmentinput_frag_main.linear_center_1, fragmentinput_frag_main.perspective_1, fragmentinput_frag_main.perspective_centroid_1, fragmentinput_frag_main.perspective_sample_1, fragmentinput_frag_main.perspective_center_1 };\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interpolate.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vert_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"frag_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interpolate_compat.hlsl",
    "content": "struct FragmentInput {\n    float4 position : SV_Position;\n    nointerpolation uint _flat : LOC0;\n    nointerpolation uint flat_either : LOC2;\n    noperspective float _linear : LOC3;\n    noperspective centroid float2 linear_centroid : LOC4;\n    noperspective sample float3 linear_sample : LOC6;\n    noperspective float3 linear_center : LOC7;\n    float4 perspective : LOC8;\n    centroid float perspective_centroid : LOC9;\n    sample float perspective_sample : LOC10;\n    float perspective_center : LOC11;\n};\n\nstruct VertexOutput_vert_main {\n    nointerpolation uint _flat : LOC0;\n    nointerpolation uint flat_either : LOC2;\n    noperspective float _linear : LOC3;\n    noperspective centroid float2 linear_centroid : LOC4;\n    noperspective sample float3 linear_sample : LOC6;\n    noperspective float3 linear_center : LOC7;\n    float4 perspective : LOC8;\n    centroid float perspective_centroid : LOC9;\n    sample float perspective_sample : LOC10;\n    float perspective_center : LOC11;\n    float4 position : SV_Position;\n};\n\nstruct FragmentInput_frag_main {\n    nointerpolation uint _flat_1 : LOC0;\n    nointerpolation uint flat_either_1 : LOC2;\n    noperspective float _linear_1 : LOC3;\n    noperspective centroid float2 linear_centroid_1 : LOC4;\n    noperspective sample float3 linear_sample_1 : LOC6;\n    noperspective float3 linear_center_1 : LOC7;\n    float4 perspective_1 : LOC8;\n    centroid float perspective_centroid_1 : LOC9;\n    sample float perspective_sample_1 : LOC10;\n    float perspective_center_1 : LOC11;\n    float4 position_1 : SV_Position;\n};\n\nVertexOutput_vert_main vert_main()\n{\n    FragmentInput out_ = (FragmentInput)0;\n\n    out_.position = float4(2.0, 4.0, 5.0, 6.0);\n    out_._flat = 8u;\n    out_.flat_either = 10u;\n    out_._linear = 27.0;\n    out_.linear_centroid = float2(64.0, 125.0);\n    out_.linear_sample = float3(216.0, 343.0, 512.0);\n    out_.linear_center = float3(255.0, 511.0, 1024.0);\n    out_.perspective = float4(729.0, 1000.0, 1331.0, 1728.0);\n    out_.perspective_centroid = 2197.0;\n    out_.perspective_sample = 2744.0;\n    out_.perspective_center = 2812.0;\n    FragmentInput _e39 = out_;\n    const FragmentInput fragmentinput = _e39;\n    const VertexOutput_vert_main fragmentinput_1 = { fragmentinput._flat, fragmentinput.flat_either, fragmentinput._linear, fragmentinput.linear_centroid, fragmentinput.linear_sample, fragmentinput.linear_center, fragmentinput.perspective, fragmentinput.perspective_centroid, fragmentinput.perspective_sample, fragmentinput.perspective_center, fragmentinput.position };\n    return fragmentinput_1;\n}\n\nvoid frag_main(FragmentInput_frag_main fragmentinput_frag_main)\n{\n    FragmentInput val = { fragmentinput_frag_main.position_1, fragmentinput_frag_main._flat_1, fragmentinput_frag_main.flat_either_1, fragmentinput_frag_main._linear_1, fragmentinput_frag_main.linear_centroid_1, fragmentinput_frag_main.linear_sample_1, fragmentinput_frag_main.linear_center_1, fragmentinput_frag_main.perspective_1, fragmentinput_frag_main.perspective_centroid_1, fragmentinput_frag_main.perspective_sample_1, fragmentinput_frag_main.perspective_center_1 };\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-interpolate_compat.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vert_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"frag_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-mat_cx2.hlsl",
    "content": "typedef struct { float2 _0; float2 _1; } __mat2x2;\nfloat2 __get_col_of_mat2x2(__mat2x2 mat, uint idx) {\n    switch(idx) {\n    case 0: { return mat._0; }\n    case 1: { return mat._1; }\n    default: { return (float2)0; }\n    }\n}\nvoid __set_col_of_mat2x2(__mat2x2 mat, uint idx, float2 value) {\n    switch(idx) {\n    case 0: { mat._0 = value; break; }\n    case 1: { mat._1 = value; break; }\n    }\n}\nvoid __set_el_of_mat2x2(__mat2x2 mat, uint idx, uint vec_idx, float value) {\n    switch(idx) {\n    case 0: { mat._0[vec_idx] = value; break; }\n    case 1: { mat._1[vec_idx] = value; break; }\n    }\n}\n\nstruct StructWithMat {\n    float2 m_0; float2 m_1;\n};\n\nstruct StructWithArrayOfStructOfMat {\n    StructWithMat a[4];\n};\n\nRWByteAddressBuffer s_m : register(u0);\ncbuffer u_m : register(b1) { __mat2x2 u_m; }\nRWByteAddressBuffer s_sm : register(u0, space1);\ncbuffer u_sm : register(b1, space1) { StructWithMat u_sm; }\nRWByteAddressBuffer s_sasm : register(u0, space2);\ncbuffer u_sasm : register(b1, space2) { StructWithArrayOfStructOfMat u_sasm; }\n\nvoid access_m()\n{\n    int idx = int(1);\n\n    int _e3 = idx;\n    idx = asint(asuint(_e3) - asuint(int(1)));\n    float2x2 l_s_m = float2x2(asfloat(s_m.Load2(0)), asfloat(s_m.Load2(8)));\n    float2 l_s_c_c = asfloat(s_m.Load2(0));\n    int _e11 = idx;\n    float2 l_s_c_v = asfloat(s_m.Load2(_e11*8));\n    float l_s_e_cc = asfloat(s_m.Load(0+0));\n    int _e20 = idx;\n    float l_s_e_cv = asfloat(s_m.Load(_e20*4+0));\n    int _e24 = idx;\n    float l_s_e_vc = asfloat(s_m.Load(0+_e24*8));\n    int _e29 = idx;\n    int _e31 = idx;\n    float l_s_e_vv = asfloat(s_m.Load(_e31*4+_e29*8));\n    float2x2 l_u_m = ((float2x2)u_m);\n    float2 l_u_c_c = u_m._0;\n    int _e40 = idx;\n    float2 l_u_c_v = __get_col_of_mat2x2(u_m, _e40);\n    float l_u_e_cc = u_m._0.x;\n    int _e49 = idx;\n    float l_u_e_cv = u_m._0[_e49];\n    int _e53 = idx;\n    float l_u_e_vc = __get_col_of_mat2x2(u_m, _e53).x;\n    int _e58 = idx;\n    int _e60 = idx;\n    float l_u_e_vv = __get_col_of_mat2x2(u_m, _e58)[_e60];\n    {\n        float2x2 _value2 = l_u_m;\n        s_m.Store2(0, asuint(_value2[0]));\n        s_m.Store2(8, asuint(_value2[1]));\n    }\n    s_m.Store2(0, asuint(l_u_c_c));\n    int _e67 = idx;\n    s_m.Store2(_e67*8, asuint(l_u_c_v));\n    s_m.Store(0+0, asuint(l_u_e_cc));\n    int _e74 = idx;\n    s_m.Store(_e74*4+0, asuint(l_u_e_cv));\n    int _e77 = idx;\n    s_m.Store(0+_e77*8, asuint(l_u_e_vc));\n    int _e81 = idx;\n    int _e83 = idx;\n    s_m.Store(_e83*4+_e81*8, asuint(l_u_e_vv));\n    return;\n}\n\nStructWithMat ConstructStructWithMat(float2x2 arg0) {\n    StructWithMat ret = (StructWithMat)0;\n    ret.m_0 = arg0[0];\n    ret.m_1 = arg0[1];\n    return ret;\n}\n\nfloat2x2 GetMatmOnStructWithMat(StructWithMat obj) {\n    return float2x2(obj.m_0, obj.m_1);\n}\n\nvoid SetMatmOnStructWithMat(StructWithMat obj, float2x2 mat) {\n    obj.m_0 = mat[0];\n    obj.m_1 = mat[1];\n}\n\nvoid SetMatVecmOnStructWithMat(StructWithMat obj, float2 vec, uint mat_idx) {\n    switch(mat_idx) {\n    case 0: { obj.m_0 = vec; break; }\n    case 1: { obj.m_1 = vec; break; }\n    }\n}\n\nvoid SetMatScalarmOnStructWithMat(StructWithMat obj, float scalar, uint mat_idx, uint vec_idx) {\n    switch(mat_idx) {\n    case 0: { obj.m_0[vec_idx] = scalar; break; }\n    case 1: { obj.m_1[vec_idx] = scalar; break; }\n    }\n}\n\nvoid access_sm()\n{\n    int idx_1 = int(1);\n\n    int _e3 = idx_1;\n    idx_1 = asint(asuint(_e3) - asuint(int(1)));\n    StructWithMat l_s_s = ConstructStructWithMat(float2x2(asfloat(s_sm.Load2(0+0)), asfloat(s_sm.Load2(0+8))));\n    float2x2 l_s_m_1 = float2x2(asfloat(s_sm.Load2(0+0)), asfloat(s_sm.Load2(0+8)));\n    float2 l_s_c_c_1 = asfloat(s_sm.Load2(0+0));\n    int _e16 = idx_1;\n    float2 l_s_c_v_1 = asfloat(s_sm.Load2(_e16*8+0));\n    float l_s_e_cc_1 = asfloat(s_sm.Load(0+0+0));\n    int _e27 = idx_1;\n    float l_s_e_cv_1 = asfloat(s_sm.Load(_e27*4+0+0));\n    int _e32 = idx_1;\n    float l_s_e_vc_1 = asfloat(s_sm.Load(0+_e32*8+0));\n    int _e38 = idx_1;\n    int _e40 = idx_1;\n    float l_s_e_vv_1 = asfloat(s_sm.Load(_e40*4+_e38*8+0));\n    StructWithMat l_u_s = u_sm;\n    float2x2 l_u_m_1 = GetMatmOnStructWithMat(u_sm);\n    float2 l_u_c_c_1 = GetMatmOnStructWithMat(u_sm)[0];\n    int _e54 = idx_1;\n    float2 l_u_c_v_1 = GetMatmOnStructWithMat(u_sm)[_e54];\n    float l_u_e_cc_1 = GetMatmOnStructWithMat(u_sm)[0].x;\n    int _e65 = idx_1;\n    float l_u_e_cv_1 = GetMatmOnStructWithMat(u_sm)[0][_e65];\n    int _e70 = idx_1;\n    float l_u_e_vc_1 = GetMatmOnStructWithMat(u_sm)[_e70].x;\n    int _e76 = idx_1;\n    int _e78 = idx_1;\n    float l_u_e_vv_1 = GetMatmOnStructWithMat(u_sm)[_e76][_e78];\n    {\n        StructWithMat _value2 = l_u_s;\n        {\n            s_sm.Store2(0+0, asuint(_value2.m_0));\n            s_sm.Store2(0+8, asuint(_value2.m_1));\n        }\n    }\n    {\n        float2x2 _value2 = l_u_m_1;\n        s_sm.Store2(0+0, asuint(_value2[0]));\n        s_sm.Store2(0+8, asuint(_value2[1]));\n    }\n    s_sm.Store2(0+0, asuint(l_u_c_c_1));\n    int _e89 = idx_1;\n    s_sm.Store2(_e89*8+0, asuint(l_u_c_v_1));\n    s_sm.Store(0+0+0, asuint(l_u_e_cc_1));\n    int _e98 = idx_1;\n    s_sm.Store(_e98*4+0+0, asuint(l_u_e_cv_1));\n    int _e102 = idx_1;\n    s_sm.Store(0+_e102*8+0, asuint(l_u_e_vc_1));\n    int _e107 = idx_1;\n    int _e109 = idx_1;\n    s_sm.Store(_e109*4+_e107*8+0, asuint(l_u_e_vv_1));\n    return;\n}\n\ntypedef StructWithMat ret_Constructarray4_StructWithMat_[4];\nret_Constructarray4_StructWithMat_ Constructarray4_StructWithMat_(StructWithMat arg0, StructWithMat arg1, StructWithMat arg2, StructWithMat arg3) {\n    StructWithMat ret[4] = { arg0, arg1, arg2, arg3 };\n    return ret;\n}\n\nStructWithArrayOfStructOfMat ConstructStructWithArrayOfStructOfMat(StructWithMat arg0[4]) {\n    StructWithArrayOfStructOfMat ret = (StructWithArrayOfStructOfMat)0;\n    ret.a = arg0;\n    return ret;\n}\n\nvoid access_sasm()\n{\n    int idx_2 = int(1);\n\n    int _e3 = idx_2;\n    idx_2 = asint(asuint(_e3) - asuint(int(1)));\n    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))))));\n    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)))));\n    float2x2 l_s_m_c = float2x2(asfloat(s_sasm.Load2(0+0+0+0)), asfloat(s_sasm.Load2(0+0+0+8)));\n    int _e17 = idx_2;\n    float2x2 l_s_m_v = float2x2(asfloat(s_sasm.Load2(0+_e17*16+0+0)), asfloat(s_sasm.Load2(0+_e17*16+0+8)));\n    float2 l_s_c_cc = asfloat(s_sasm.Load2(0+0+0+0));\n    int _e31 = idx_2;\n    float2 l_s_c_cv = asfloat(s_sasm.Load2(_e31*8+0+0+0));\n    int _e36 = idx_2;\n    float2 l_s_c_vc = asfloat(s_sasm.Load2(0+0+_e36*16+0));\n    int _e43 = idx_2;\n    int _e46 = idx_2;\n    float2 l_s_c_vv = asfloat(s_sasm.Load2(_e46*8+0+_e43*16+0));\n    float l_s_e_ccc = asfloat(s_sasm.Load(0+0+0+0+0));\n    int _e61 = idx_2;\n    float l_s_e_ccv = asfloat(s_sasm.Load(_e61*4+0+0+0+0));\n    int _e68 = idx_2;\n    float l_s_e_cvc = asfloat(s_sasm.Load(0+_e68*8+0+0+0));\n    int _e76 = idx_2;\n    int _e78 = idx_2;\n    float l_s_e_cvv = asfloat(s_sasm.Load(_e78*4+_e76*8+0+0+0));\n    int _e83 = idx_2;\n    float l_s_e_vcc = asfloat(s_sasm.Load(0+0+0+_e83*16+0));\n    int _e91 = idx_2;\n    int _e95 = idx_2;\n    float l_s_e_vcv = asfloat(s_sasm.Load(_e95*4+0+0+_e91*16+0));\n    int _e100 = idx_2;\n    int _e103 = idx_2;\n    float l_s_e_vvc = asfloat(s_sasm.Load(0+_e103*8+0+_e100*16+0));\n    int _e109 = idx_2;\n    int _e112 = idx_2;\n    int _e114 = idx_2;\n    float l_s_e_vvv = asfloat(s_sasm.Load(_e114*4+_e112*8+0+_e109*16+0));\n    StructWithArrayOfStructOfMat l_u_s_1 = u_sasm;\n    StructWithMat l_u_a[4] = u_sasm.a;\n    float2x2 l_u_m_c = GetMatmOnStructWithMat(u_sasm.a[0]);\n    int _e129 = idx_2;\n    float2x2 l_u_m_v = GetMatmOnStructWithMat(u_sasm.a[_e129]);\n    float2 l_u_c_cc = GetMatmOnStructWithMat(u_sasm.a[0])[0];\n    int _e143 = idx_2;\n    float2 l_u_c_cv = GetMatmOnStructWithMat(u_sasm.a[0])[_e143];\n    int _e148 = idx_2;\n    float2 l_u_c_vc = GetMatmOnStructWithMat(u_sasm.a[_e148])[0];\n    int _e155 = idx_2;\n    int _e158 = idx_2;\n    float2 l_u_c_vv = GetMatmOnStructWithMat(u_sasm.a[_e155])[_e158];\n    float l_u_e_ccc = GetMatmOnStructWithMat(u_sasm.a[0])[0].x;\n    int _e173 = idx_2;\n    float l_u_e_ccv = GetMatmOnStructWithMat(u_sasm.a[0])[0][_e173];\n    int _e180 = idx_2;\n    float l_u_e_cvc = GetMatmOnStructWithMat(u_sasm.a[0])[_e180].x;\n    int _e188 = idx_2;\n    int _e190 = idx_2;\n    float l_u_e_cvv = GetMatmOnStructWithMat(u_sasm.a[0])[_e188][_e190];\n    int _e195 = idx_2;\n    float l_u_e_vcc = GetMatmOnStructWithMat(u_sasm.a[_e195])[0].x;\n    int _e203 = idx_2;\n    int _e207 = idx_2;\n    float l_u_e_vcv = GetMatmOnStructWithMat(u_sasm.a[_e203])[0][_e207];\n    int _e212 = idx_2;\n    int _e215 = idx_2;\n    float l_u_e_vvc = GetMatmOnStructWithMat(u_sasm.a[_e212])[_e215].x;\n    int _e221 = idx_2;\n    int _e224 = idx_2;\n    int _e226 = idx_2;\n    float l_u_e_vvv = GetMatmOnStructWithMat(u_sasm.a[_e221])[_e224][_e226];\n    {\n        StructWithArrayOfStructOfMat _value2 = l_u_s_1;\n        {\n            StructWithMat _value3[4] = _value2.a;\n            {\n                StructWithMat _value4 = _value3[0];\n                {\n                    s_sasm.Store2(0+0+0+0, asuint(_value4.m_0));\n                    s_sasm.Store2(0+0+0+8, asuint(_value4.m_1));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[1];\n                {\n                    s_sasm.Store2(0+16+0+0, asuint(_value4.m_0));\n                    s_sasm.Store2(0+16+0+8, asuint(_value4.m_1));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[2];\n                {\n                    s_sasm.Store2(0+32+0+0, asuint(_value4.m_0));\n                    s_sasm.Store2(0+32+0+8, asuint(_value4.m_1));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[3];\n                {\n                    s_sasm.Store2(0+48+0+0, asuint(_value4.m_0));\n                    s_sasm.Store2(0+48+0+8, asuint(_value4.m_1));\n                }\n            }\n        }\n    }\n    {\n        StructWithMat _value2[4] = l_u_a;\n        {\n            StructWithMat _value3 = _value2[0];\n            {\n                s_sasm.Store2(0+0+0+0, asuint(_value3.m_0));\n                s_sasm.Store2(0+0+0+8, asuint(_value3.m_1));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[1];\n            {\n                s_sasm.Store2(0+16+0+0, asuint(_value3.m_0));\n                s_sasm.Store2(0+16+0+8, asuint(_value3.m_1));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[2];\n            {\n                s_sasm.Store2(0+32+0+0, asuint(_value3.m_0));\n                s_sasm.Store2(0+32+0+8, asuint(_value3.m_1));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[3];\n            {\n                s_sasm.Store2(0+48+0+0, asuint(_value3.m_0));\n                s_sasm.Store2(0+48+0+8, asuint(_value3.m_1));\n            }\n        }\n    }\n    {\n        float2x2 _value2 = l_u_m_c;\n        s_sasm.Store2(0+0+0+0, asuint(_value2[0]));\n        s_sasm.Store2(0+0+0+8, asuint(_value2[1]));\n    }\n    int _e238 = idx_2;\n    {\n        float2x2 _value2 = l_u_m_v;\n        s_sasm.Store2(0+_e238*16+0+0, asuint(_value2[0]));\n        s_sasm.Store2(0+_e238*16+0+8, asuint(_value2[1]));\n    }\n    s_sasm.Store2(0+0+0+0, asuint(l_u_c_cc));\n    int _e250 = idx_2;\n    s_sasm.Store2(_e250*8+0+0+0, asuint(l_u_c_cv));\n    int _e254 = idx_2;\n    s_sasm.Store2(0+0+_e254*16+0, asuint(l_u_c_vc));\n    int _e260 = idx_2;\n    int _e263 = idx_2;\n    s_sasm.Store2(_e263*8+0+_e260*16+0, asuint(l_u_c_vv));\n    s_sasm.Store(0+0+0+0+0, asuint(l_u_e_ccc));\n    int _e276 = idx_2;\n    s_sasm.Store(_e276*4+0+0+0+0, asuint(l_u_e_ccv));\n    int _e282 = idx_2;\n    s_sasm.Store(0+_e282*8+0+0+0, asuint(l_u_e_cvc));\n    int _e289 = idx_2;\n    int _e291 = idx_2;\n    s_sasm.Store(_e291*4+_e289*8+0+0+0, asuint(l_u_e_cvv));\n    int _e295 = idx_2;\n    s_sasm.Store(0+0+0+_e295*16+0, asuint(l_u_e_vcc));\n    int _e302 = idx_2;\n    int _e306 = idx_2;\n    s_sasm.Store(_e306*4+0+0+_e302*16+0, asuint(l_u_e_vcv));\n    int _e310 = idx_2;\n    int _e313 = idx_2;\n    s_sasm.Store(0+_e313*8+0+_e310*16+0, asuint(l_u_e_vvc));\n    int _e318 = idx_2;\n    int _e321 = idx_2;\n    int _e323 = idx_2;\n    s_sasm.Store(_e323*4+_e321*8+0+_e318*16+0, asuint(l_u_e_vvv));\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    access_m();\n    access_sm();\n    access_sasm();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-mat_cx2.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-mat_cx3.hlsl",
    "content": "struct StructWithMat {\n    row_major float3x3 m;\n    int _end_pad_0;\n};\n\nstruct StructWithArrayOfStructOfMat {\n    StructWithMat a[4];\n};\n\nRWByteAddressBuffer s_m : register(u0);\ncbuffer u_m : register(b1) { row_major float3x3 u_m; }\nRWByteAddressBuffer s_sm : register(u0, space1);\ncbuffer u_sm : register(b1, space1) { StructWithMat u_sm; }\nRWByteAddressBuffer s_sasm : register(u0, space2);\ncbuffer u_sasm : register(b1, space2) { StructWithArrayOfStructOfMat u_sasm; }\n\nvoid access_m()\n{\n    int idx = int(1);\n\n    int _e3 = idx;\n    idx = asint(asuint(_e3) - asuint(int(1)));\n    float3x3 l_s_m = float3x3(asfloat(s_m.Load3(0)), asfloat(s_m.Load3(16)), asfloat(s_m.Load3(32)));\n    float3 l_s_c_c = asfloat(s_m.Load3(0));\n    int _e11 = idx;\n    float3 l_s_c_v = asfloat(s_m.Load3(_e11*16));\n    float l_s_e_cc = asfloat(s_m.Load(0+0));\n    int _e20 = idx;\n    float l_s_e_cv = asfloat(s_m.Load(_e20*4+0));\n    int _e24 = idx;\n    float l_s_e_vc = asfloat(s_m.Load(0+_e24*16));\n    int _e29 = idx;\n    int _e31 = idx;\n    float l_s_e_vv = asfloat(s_m.Load(_e31*4+_e29*16));\n    float3x3 l_u_m = u_m;\n    float3 l_u_c_c = u_m[0];\n    int _e40 = idx;\n    float3 l_u_c_v = u_m[_e40];\n    float l_u_e_cc = u_m[0].x;\n    int _e49 = idx;\n    float l_u_e_cv = u_m[0][_e49];\n    int _e53 = idx;\n    float l_u_e_vc = u_m[_e53].x;\n    int _e58 = idx;\n    int _e60 = idx;\n    float l_u_e_vv = u_m[_e58][_e60];\n    {\n        float3x3 _value2 = l_u_m;\n        s_m.Store3(0, asuint(_value2[0]));\n        s_m.Store3(16, asuint(_value2[1]));\n        s_m.Store3(32, asuint(_value2[2]));\n    }\n    s_m.Store3(0, asuint(l_u_c_c));\n    int _e67 = idx;\n    s_m.Store3(_e67*16, asuint(l_u_c_v));\n    s_m.Store(0+0, asuint(l_u_e_cc));\n    int _e74 = idx;\n    s_m.Store(_e74*4+0, asuint(l_u_e_cv));\n    int _e77 = idx;\n    s_m.Store(0+_e77*16, asuint(l_u_e_vc));\n    int _e81 = idx;\n    int _e83 = idx;\n    s_m.Store(_e83*4+_e81*16, asuint(l_u_e_vv));\n    return;\n}\n\nStructWithMat ConstructStructWithMat(float3x3 arg0) {\n    StructWithMat ret = (StructWithMat)0;\n    ret.m = arg0;\n    return ret;\n}\n\nvoid access_sm()\n{\n    int idx_1 = int(1);\n\n    int _e3 = idx_1;\n    idx_1 = asint(asuint(_e3) - asuint(int(1)));\n    StructWithMat l_s_s = ConstructStructWithMat(float3x3(asfloat(s_sm.Load3(0+0)), asfloat(s_sm.Load3(0+16)), asfloat(s_sm.Load3(0+32))));\n    float3x3 l_s_m_1 = float3x3(asfloat(s_sm.Load3(0+0)), asfloat(s_sm.Load3(0+16)), asfloat(s_sm.Load3(0+32)));\n    float3 l_s_c_c_1 = asfloat(s_sm.Load3(0+0));\n    int _e16 = idx_1;\n    float3 l_s_c_v_1 = asfloat(s_sm.Load3(_e16*16+0));\n    float l_s_e_cc_1 = asfloat(s_sm.Load(0+0+0));\n    int _e27 = idx_1;\n    float l_s_e_cv_1 = asfloat(s_sm.Load(_e27*4+0+0));\n    int _e32 = idx_1;\n    float l_s_e_vc_1 = asfloat(s_sm.Load(0+_e32*16+0));\n    int _e38 = idx_1;\n    int _e40 = idx_1;\n    float l_s_e_vv_1 = asfloat(s_sm.Load(_e40*4+_e38*16+0));\n    StructWithMat l_u_s = u_sm;\n    float3x3 l_u_m_1 = u_sm.m;\n    float3 l_u_c_c_1 = u_sm.m[0];\n    int _e54 = idx_1;\n    float3 l_u_c_v_1 = u_sm.m[_e54];\n    float l_u_e_cc_1 = u_sm.m[0].x;\n    int _e65 = idx_1;\n    float l_u_e_cv_1 = u_sm.m[0][_e65];\n    int _e70 = idx_1;\n    float l_u_e_vc_1 = u_sm.m[_e70].x;\n    int _e76 = idx_1;\n    int _e78 = idx_1;\n    float l_u_e_vv_1 = u_sm.m[_e76][_e78];\n    {\n        StructWithMat _value2 = l_u_s;\n        {\n            float3x3 _value3 = _value2.m;\n            s_sm.Store3(0+0, asuint(_value3[0]));\n            s_sm.Store3(0+16, asuint(_value3[1]));\n            s_sm.Store3(0+32, asuint(_value3[2]));\n        }\n    }\n    {\n        float3x3 _value2 = l_u_m_1;\n        s_sm.Store3(0+0, asuint(_value2[0]));\n        s_sm.Store3(0+16, asuint(_value2[1]));\n        s_sm.Store3(0+32, asuint(_value2[2]));\n    }\n    s_sm.Store3(0+0, asuint(l_u_c_c_1));\n    int _e89 = idx_1;\n    s_sm.Store3(_e89*16+0, asuint(l_u_c_v_1));\n    s_sm.Store(0+0+0, asuint(l_u_e_cc_1));\n    int _e98 = idx_1;\n    s_sm.Store(_e98*4+0+0, asuint(l_u_e_cv_1));\n    int _e102 = idx_1;\n    s_sm.Store(0+_e102*16+0, asuint(l_u_e_vc_1));\n    int _e107 = idx_1;\n    int _e109 = idx_1;\n    s_sm.Store(_e109*4+_e107*16+0, asuint(l_u_e_vv_1));\n    return;\n}\n\ntypedef StructWithMat ret_Constructarray4_StructWithMat_[4];\nret_Constructarray4_StructWithMat_ Constructarray4_StructWithMat_(StructWithMat arg0, StructWithMat arg1, StructWithMat arg2, StructWithMat arg3) {\n    StructWithMat ret[4] = { arg0, arg1, arg2, arg3 };\n    return ret;\n}\n\nStructWithArrayOfStructOfMat ConstructStructWithArrayOfStructOfMat(StructWithMat arg0[4]) {\n    StructWithArrayOfStructOfMat ret = (StructWithArrayOfStructOfMat)0;\n    ret.a = arg0;\n    return ret;\n}\n\nvoid access_sasm()\n{\n    int idx_2 = int(1);\n\n    int _e3 = idx_2;\n    idx_2 = asint(asuint(_e3) - asuint(int(1)));\n    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))))));\n    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)))));\n    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)));\n    int _e17 = idx_2;\n    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)));\n    float3 l_s_c_cc = asfloat(s_sasm.Load3(0+0+0+0));\n    int _e31 = idx_2;\n    float3 l_s_c_cv = asfloat(s_sasm.Load3(_e31*16+0+0+0));\n    int _e36 = idx_2;\n    float3 l_s_c_vc = asfloat(s_sasm.Load3(0+0+_e36*48+0));\n    int _e43 = idx_2;\n    int _e46 = idx_2;\n    float3 l_s_c_vv = asfloat(s_sasm.Load3(_e46*16+0+_e43*48+0));\n    float l_s_e_ccc = asfloat(s_sasm.Load(0+0+0+0+0));\n    int _e61 = idx_2;\n    float l_s_e_ccv = asfloat(s_sasm.Load(_e61*4+0+0+0+0));\n    int _e68 = idx_2;\n    float l_s_e_cvc = asfloat(s_sasm.Load(0+_e68*16+0+0+0));\n    int _e76 = idx_2;\n    int _e78 = idx_2;\n    float l_s_e_cvv = asfloat(s_sasm.Load(_e78*4+_e76*16+0+0+0));\n    int _e83 = idx_2;\n    float l_s_e_vcc = asfloat(s_sasm.Load(0+0+0+_e83*48+0));\n    int _e91 = idx_2;\n    int _e95 = idx_2;\n    float l_s_e_vcv = asfloat(s_sasm.Load(_e95*4+0+0+_e91*48+0));\n    int _e100 = idx_2;\n    int _e103 = idx_2;\n    float l_s_e_vvc = asfloat(s_sasm.Load(0+_e103*16+0+_e100*48+0));\n    int _e109 = idx_2;\n    int _e112 = idx_2;\n    int _e114 = idx_2;\n    float l_s_e_vvv = asfloat(s_sasm.Load(_e114*4+_e112*16+0+_e109*48+0));\n    StructWithArrayOfStructOfMat l_u_s_1 = u_sasm;\n    StructWithMat l_u_a[4] = u_sasm.a;\n    float3x3 l_u_m_c = u_sasm.a[0].m;\n    int _e129 = idx_2;\n    float3x3 l_u_m_v = u_sasm.a[_e129].m;\n    float3 l_u_c_cc = u_sasm.a[0].m[0];\n    int _e143 = idx_2;\n    float3 l_u_c_cv = u_sasm.a[0].m[_e143];\n    int _e148 = idx_2;\n    float3 l_u_c_vc = u_sasm.a[_e148].m[0];\n    int _e155 = idx_2;\n    int _e158 = idx_2;\n    float3 l_u_c_vv = u_sasm.a[_e155].m[_e158];\n    float l_u_e_ccc = u_sasm.a[0].m[0].x;\n    int _e173 = idx_2;\n    float l_u_e_ccv = u_sasm.a[0].m[0][_e173];\n    int _e180 = idx_2;\n    float l_u_e_cvc = u_sasm.a[0].m[_e180].x;\n    int _e188 = idx_2;\n    int _e190 = idx_2;\n    float l_u_e_cvv = u_sasm.a[0].m[_e188][_e190];\n    int _e195 = idx_2;\n    float l_u_e_vcc = u_sasm.a[_e195].m[0].x;\n    int _e203 = idx_2;\n    int _e207 = idx_2;\n    float l_u_e_vcv = u_sasm.a[_e203].m[0][_e207];\n    int _e212 = idx_2;\n    int _e215 = idx_2;\n    float l_u_e_vvc = u_sasm.a[_e212].m[_e215].x;\n    int _e221 = idx_2;\n    int _e224 = idx_2;\n    int _e226 = idx_2;\n    float l_u_e_vvv = u_sasm.a[_e221].m[_e224][_e226];\n    {\n        StructWithArrayOfStructOfMat _value2 = l_u_s_1;\n        {\n            StructWithMat _value3[4] = _value2.a;\n            {\n                StructWithMat _value4 = _value3[0];\n                {\n                    float3x3 _value5 = _value4.m;\n                    s_sasm.Store3(0+0+0+0, asuint(_value5[0]));\n                    s_sasm.Store3(0+0+0+16, asuint(_value5[1]));\n                    s_sasm.Store3(0+0+0+32, asuint(_value5[2]));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[1];\n                {\n                    float3x3 _value5 = _value4.m;\n                    s_sasm.Store3(0+48+0+0, asuint(_value5[0]));\n                    s_sasm.Store3(0+48+0+16, asuint(_value5[1]));\n                    s_sasm.Store3(0+48+0+32, asuint(_value5[2]));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[2];\n                {\n                    float3x3 _value5 = _value4.m;\n                    s_sasm.Store3(0+96+0+0, asuint(_value5[0]));\n                    s_sasm.Store3(0+96+0+16, asuint(_value5[1]));\n                    s_sasm.Store3(0+96+0+32, asuint(_value5[2]));\n                }\n            }\n            {\n                StructWithMat _value4 = _value3[3];\n                {\n                    float3x3 _value5 = _value4.m;\n                    s_sasm.Store3(0+144+0+0, asuint(_value5[0]));\n                    s_sasm.Store3(0+144+0+16, asuint(_value5[1]));\n                    s_sasm.Store3(0+144+0+32, asuint(_value5[2]));\n                }\n            }\n        }\n    }\n    {\n        StructWithMat _value2[4] = l_u_a;\n        {\n            StructWithMat _value3 = _value2[0];\n            {\n                float3x3 _value4 = _value3.m;\n                s_sasm.Store3(0+0+0+0, asuint(_value4[0]));\n                s_sasm.Store3(0+0+0+16, asuint(_value4[1]));\n                s_sasm.Store3(0+0+0+32, asuint(_value4[2]));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[1];\n            {\n                float3x3 _value4 = _value3.m;\n                s_sasm.Store3(0+48+0+0, asuint(_value4[0]));\n                s_sasm.Store3(0+48+0+16, asuint(_value4[1]));\n                s_sasm.Store3(0+48+0+32, asuint(_value4[2]));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[2];\n            {\n                float3x3 _value4 = _value3.m;\n                s_sasm.Store3(0+96+0+0, asuint(_value4[0]));\n                s_sasm.Store3(0+96+0+16, asuint(_value4[1]));\n                s_sasm.Store3(0+96+0+32, asuint(_value4[2]));\n            }\n        }\n        {\n            StructWithMat _value3 = _value2[3];\n            {\n                float3x3 _value4 = _value3.m;\n                s_sasm.Store3(0+144+0+0, asuint(_value4[0]));\n                s_sasm.Store3(0+144+0+16, asuint(_value4[1]));\n                s_sasm.Store3(0+144+0+32, asuint(_value4[2]));\n            }\n        }\n    }\n    {\n        float3x3 _value2 = l_u_m_c;\n        s_sasm.Store3(0+0+0+0, asuint(_value2[0]));\n        s_sasm.Store3(0+0+0+16, asuint(_value2[1]));\n        s_sasm.Store3(0+0+0+32, asuint(_value2[2]));\n    }\n    int _e238 = idx_2;\n    {\n        float3x3 _value2 = l_u_m_v;\n        s_sasm.Store3(0+_e238*48+0+0, asuint(_value2[0]));\n        s_sasm.Store3(0+_e238*48+0+16, asuint(_value2[1]));\n        s_sasm.Store3(0+_e238*48+0+32, asuint(_value2[2]));\n    }\n    s_sasm.Store3(0+0+0+0, asuint(l_u_c_cc));\n    int _e250 = idx_2;\n    s_sasm.Store3(_e250*16+0+0+0, asuint(l_u_c_cv));\n    int _e254 = idx_2;\n    s_sasm.Store3(0+0+_e254*48+0, asuint(l_u_c_vc));\n    int _e260 = idx_2;\n    int _e263 = idx_2;\n    s_sasm.Store3(_e263*16+0+_e260*48+0, asuint(l_u_c_vv));\n    s_sasm.Store(0+0+0+0+0, asuint(l_u_e_ccc));\n    int _e276 = idx_2;\n    s_sasm.Store(_e276*4+0+0+0+0, asuint(l_u_e_ccv));\n    int _e282 = idx_2;\n    s_sasm.Store(0+_e282*16+0+0+0, asuint(l_u_e_cvc));\n    int _e289 = idx_2;\n    int _e291 = idx_2;\n    s_sasm.Store(_e291*4+_e289*16+0+0+0, asuint(l_u_e_cvv));\n    int _e295 = idx_2;\n    s_sasm.Store(0+0+0+_e295*48+0, asuint(l_u_e_vcc));\n    int _e302 = idx_2;\n    int _e306 = idx_2;\n    s_sasm.Store(_e306*4+0+0+_e302*48+0, asuint(l_u_e_vcv));\n    int _e310 = idx_2;\n    int _e313 = idx_2;\n    s_sasm.Store(0+_e313*16+0+_e310*48+0, asuint(l_u_e_vvc));\n    int _e318 = idx_2;\n    int _e321 = idx_2;\n    int _e323 = idx_2;\n    s_sasm.Store(_e323*4+_e321*16+0+_e318*48+0, asuint(l_u_e_vvv));\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    access_m();\n    access_sm();\n    access_sasm();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-mat_cx3.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-math-functions.hlsl",
    "content": "struct _modf_result_f32_ {\n    float fract;\n    float whole;\n};\n\nstruct _modf_result_vec2_f32_ {\n    float2 fract;\n    float2 whole;\n};\n\nstruct _modf_result_vec4_f32_ {\n    float4 fract;\n    float4 whole;\n};\n\nstruct _frexp_result_f32_ {\n    float fract;\n    int exp_;\n};\n\nstruct _frexp_result_vec4_f32_ {\n    float4 fract;\n    int4 exp_;\n};\n\n_modf_result_f32_ naga_modf(float arg) {\n    float other;\n    _modf_result_f32_ result;\n    result.fract = modf(arg, other);\n    result.whole = other;\n    return result;\n}\n\n_modf_result_vec2_f32_ naga_modf(float2 arg) {\n    float2 other;\n    _modf_result_vec2_f32_ result;\n    result.fract = modf(arg, other);\n    result.whole = other;\n    return result;\n}\n\n_modf_result_vec4_f32_ naga_modf(float4 arg) {\n    float4 other;\n    _modf_result_vec4_f32_ result;\n    result.fract = modf(arg, other);\n    result.whole = other;\n    return result;\n}\n\n_frexp_result_f32_ naga_frexp(float arg) {\n    float other;\n    _frexp_result_f32_ result;\n    result.fract = sign(arg) * frexp(arg, other);\n    result.exp_ = other;\n    return result;\n}\n\n_frexp_result_vec4_f32_ naga_frexp(float4 arg) {\n    float4 other;\n    _frexp_result_vec4_f32_ result;\n    result.fract = sign(arg) * frexp(arg, other);\n    result.exp_ = other;\n    return result;\n}\n\nvoid main()\n{\n    float4 v = (0.0).xxxx;\n    float a = degrees(1.0);\n    float b = radians(1.0);\n    float4 c = degrees(v);\n    float4 d = radians(v);\n    float4 e = saturate(v);\n    float4 g = refract(v, v, 1.0);\n    int4 sign_b = int4(int(-1), int(-1), int(-1), int(-1));\n    float4 sign_d = float4(-1.0, -1.0, -1.0, -1.0);\n    float4 sign_e = float4(0.0, 0.0, 0.0, 0.0);\n    int2 flb_b = int2(int(-1), int(-1));\n    uint2 flb_c = uint2(0u, 0u);\n    int2 ftb_c = int2(int(0), int(0));\n    uint2 ftb_d = uint2(0u, 0u);\n    uint2 ctz_e = uint2(32u, 32u);\n    int2 ctz_f = int2(int(32), int(32));\n    uint2 ctz_g = uint2(0u, 0u);\n    int2 ctz_h = int2(int(0), int(0));\n    int2 clz_c = int2(int(0), int(0));\n    uint2 clz_d = uint2(31u, 31u);\n    float lde_a = ldexp(1.0, int(2));\n    float2 lde_b = ldexp(float2(1.0, 2.0), int2(int(3), int(4)));\n    _modf_result_f32_ modf_a = naga_modf(1.5);\n    float modf_b = naga_modf(1.5).fract;\n    float modf_c = naga_modf(1.5).whole;\n    _modf_result_vec2_f32_ modf_d = naga_modf(float2(1.5, 1.5));\n    float modf_e = naga_modf(float4(1.5, 1.5, 1.5, 1.5)).whole.x;\n    float modf_f = naga_modf(float2(1.5, 1.5)).fract.y;\n    _frexp_result_f32_ frexp_a = naga_frexp(1.5);\n    float frexp_b = naga_frexp(1.5).fract;\n    int frexp_c = naga_frexp(1.5).exp_;\n    int frexp_d = naga_frexp(float4(1.5, 1.5, 1.5, 1.5)).exp_.x;\n    float quantizeToF16_a = f16tof32(f32tof16(1.0));\n    float2 quantizeToF16_b = f16tof32(f32tof16(float2(1.0, 1.0)));\n    float3 quantizeToF16_c = f16tof32(f32tof16(float3(1.0, 1.0, 1.0)));\n    float4 quantizeToF16_d = f16tof32(f32tof16(float4(1.0, 1.0, 1.0, 1.0)));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-math-functions.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-memory-decorations-coherent.hlsl",
    "content": "globallycoherent RWByteAddressBuffer coherent_buf : register(u0);\nRWByteAddressBuffer plain_buf : register(u1);\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    uint _e6 = asuint(plain_buf.Load(0+0));\n    coherent_buf.Store(0+0, asuint(_e6));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-memory-decorations-coherent.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-multiview.hlsl",
    "content": "struct FragmentInput_main {\n    uint view_index_1 : SV_ViewID;\n};\n\nvoid main(FragmentInput_main fragmentinput_main)\n{\n    uint view_index = fragmentinput_main.view_index_1;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-multiview.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_6_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-operators.hlsl",
    "content": "static const float4 v_f32_one = float4(1.0, 1.0, 1.0, 1.0);\nstatic const float4 v_f32_zero = float4(0.0, 0.0, 0.0, 0.0);\nstatic const float4 v_f32_half = float4(0.5, 0.5, 0.5, 0.5);\nstatic const int4 v_i32_one = int4(int(1), int(1), int(1), int(1));\nstatic const bool b_false = false;\nstatic const bool b_true = true;\nstatic const bool short_circuit_1_invalid_rhs = false;\nstatic const bool short_circuit_2_invalid_rhs = false;\nstatic const bool short_circuit_3_ = true;\nstatic const bool short_circuit_4_ = true;\n\nfloat4 builtins()\n{\n    int s1_ = (true ? int(1) : int(0));\n    float4 s2_ = (true ? v_f32_one : v_f32_zero);\n    float4 s3_ = float4(1.0, 1.0, 1.0, 1.0);\n    float4 m1_ = lerp(v_f32_zero, v_f32_one, v_f32_half);\n    float4 m2_ = lerp(v_f32_zero, v_f32_one, 0.1);\n    float b1_ = asfloat(int(1));\n    float4 b2_ = asfloat(v_i32_one);\n    int4 v_i32_zero = int4(int(0), int(0), int(0), int(0));\n    return (((((float4(asint(asuint((s1_).xxxx) + asuint(v_i32_zero))) + s2_) + m1_) + m2_) + (b1_).xxxx) + b2_);\n}\n\nint4 naga_mod(int4 lhs, int4 rhs) {\n    int4 divisor = ((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs;\n    return lhs - (lhs / divisor) * divisor;\n}\n\nfloat4 splat(float m, int n)\n{\n    float2 a_2 = ((((2.0).xx + (m).xx) - (4.0).xx) / (8.0).xx);\n    int4 b = naga_mod((n).xxxx, (int(2)).xxxx);\n    return (a_2.xyxy + float4(b));\n}\n\nfloat2 splat_assignment()\n{\n    float2 a = (2.0).xx;\n\n    float2 _e3 = a;\n    a = (_e3 + (1.0).xx);\n    float2 _e7 = a;\n    a = (_e7 - (3.0).xx);\n    float2 _e11 = a;\n    a = (_e11 / (4.0).xx);\n    float2 _e15 = a;\n    return _e15;\n}\n\nfloat3 bool_cast(float3 x)\n{\n    bool3 y = bool3(x);\n    return float3(y);\n}\n\nbool p()\n{\n    return true;\n}\n\nbool q()\n{\n    return false;\n}\n\nbool r()\n{\n    return true;\n}\n\nbool s()\n{\n    return false;\n}\n\nvoid logical()\n{\n    bool local = (bool)0;\n    bool local_1 = (bool)0;\n    bool local_2 = (bool)0;\n    bool local_3 = (bool)0;\n    bool local_4 = (bool)0;\n    bool local_5 = (bool)0;\n    bool local_6 = (bool)0;\n\n    bool neg0_ = !(true);\n    bool2 neg1_ = !((true).xx);\n    if (!(true)) {\n        local = false;\n    } else {\n        local = true;\n    }\n    bool or_ = local;\n    if (true) {\n        local_1 = false;\n    } else {\n        local_1 = false;\n    }\n    bool and_ = local_1;\n    bool bitwise_or0_ = (true | false);\n    bool3 bitwise_or1_ = ((true).xxx | (false).xxx);\n    bool bitwise_and0_ = (true & false);\n    bool4 bitwise_and1_ = ((true).xxxx & (false).xxxx);\n    if (!(false)) {\n        local_2 = false;\n    } else {\n        local_2 = true;\n    }\n    bool _e27 = local_2;\n    bool short_circuit_5_ = !(_e27);\n    const bool _e29 = p();\n    if (!(_e29)) {\n        const bool _e33 = q();\n        local_3 = _e33;\n    } else {\n        local_3 = true;\n    }\n    bool _e35 = local_3;\n    if (_e35) {\n        const bool _e38 = r();\n        if (!(_e38)) {\n            const bool _e42 = s();\n            local_5 = _e42;\n        } else {\n            local_5 = true;\n        }\n        bool _e44 = local_5;\n        local_4 = _e44;\n    } else {\n        local_4 = false;\n    }\n    bool short_circuit_6_ = local_4;\n    if (false) {\n        const bool _e50 = q();\n        local_6 = _e50;\n    } else {\n        local_6 = true;\n    }\n    bool short_circuit_7_ = local_6;\n    return;\n}\n\nint2 naga_neg(int2 val) {\n    return asint(-asuint(val));\n}\n\nint naga_div(int lhs, int rhs) {\n    return lhs / (((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs);\n}\n\nuint naga_div(uint lhs, uint rhs) {\n    return lhs / (rhs == 0u ? 1u : rhs);\n}\n\nint2 naga_div(int2 lhs, int2 rhs) {\n    return lhs / (((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs);\n}\n\nuint3 naga_div(uint3 lhs, uint3 rhs) {\n    return lhs / (rhs == 0u ? 1u : rhs);\n}\n\nint naga_mod(int lhs, int rhs) {\n    int divisor = ((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs;\n    return lhs - (lhs / divisor) * divisor;\n}\n\nuint naga_mod(uint lhs, uint rhs) {\n    return lhs % (rhs == 0u ? 1u : rhs);\n}\n\nfloat naga_mod(float lhs, float rhs) {\n    return lhs - rhs * trunc(lhs / rhs);\n}\n\nint2 naga_mod(int2 lhs, int2 rhs) {\n    int2 divisor = ((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs;\n    return lhs - (lhs / divisor) * divisor;\n}\n\nuint3 naga_mod(uint3 lhs, uint3 rhs) {\n    return lhs % (rhs == 0u ? 1u : rhs);\n}\n\nfloat4 naga_mod(float4 lhs, float4 rhs) {\n    return lhs - rhs * trunc(lhs / rhs);\n}\n\nuint2 naga_div(uint2 lhs, uint2 rhs) {\n    return lhs / (rhs == 0u ? 1u : rhs);\n}\n\nuint2 naga_mod(uint2 lhs, uint2 rhs) {\n    return lhs % (rhs == 0u ? 1u : rhs);\n}\n\nfloat2 naga_mod(float2 lhs, float2 rhs) {\n    return lhs - rhs * trunc(lhs / rhs);\n}\n\nfloat3x3 ZeroValuefloat3x3() {\n    return (float3x3)0;\n}\n\nfloat4x3 ZeroValuefloat4x3() {\n    return (float4x3)0;\n}\n\nvoid arithmetic()\n{\n    int prevent_const_eval = (int)0;\n    int wgpu_7437_ = (int)0;\n\n    float neg0_1 = -(1.0);\n    int2 neg1_1 = naga_neg((int(1)).xx);\n    float2 neg2_ = -((1.0).xx);\n    int add0_ = asint(asuint(int(2)) + asuint(int(1)));\n    uint add1_ = (2u + 1u);\n    float add2_ = (2.0 + 1.0);\n    int2 add3_ = asint(asuint((int(2)).xx) + asuint((int(1)).xx));\n    uint3 add4_ = ((2u).xxx + (1u).xxx);\n    float4 add5_ = ((2.0).xxxx + (1.0).xxxx);\n    int sub0_ = asint(asuint(int(2)) - asuint(int(1)));\n    uint sub1_ = (2u - 1u);\n    float sub2_ = (2.0 - 1.0);\n    int2 sub3_ = asint(asuint((int(2)).xx) - asuint((int(1)).xx));\n    uint3 sub4_ = ((2u).xxx - (1u).xxx);\n    float4 sub5_ = ((2.0).xxxx - (1.0).xxxx);\n    int mul0_ = asint(asuint(int(2)) * asuint(int(1)));\n    uint mul1_ = (2u * 1u);\n    float mul2_ = (2.0 * 1.0);\n    int2 mul3_ = asint(asuint((int(2)).xx) * asuint((int(1)).xx));\n    uint3 mul4_ = ((2u).xxx * (1u).xxx);\n    float4 mul5_ = ((2.0).xxxx * (1.0).xxxx);\n    int div0_ = naga_div(int(2), int(1));\n    uint div1_ = naga_div(2u, 1u);\n    float div2_ = (2.0 / 1.0);\n    int2 div3_ = naga_div((int(2)).xx, (int(1)).xx);\n    uint3 div4_ = naga_div((2u).xxx, (1u).xxx);\n    float4 div5_ = ((2.0).xxxx / (1.0).xxxx);\n    int rem0_ = naga_mod(int(2), int(1));\n    uint rem1_ = naga_mod(2u, 1u);\n    float rem2_ = naga_mod(2.0, 1.0);\n    int2 rem3_ = naga_mod((int(2)).xx, (int(1)).xx);\n    uint3 rem4_ = naga_mod((2u).xxx, (1u).xxx);\n    float4 rem5_ = naga_mod((2.0).xxxx, (1.0).xxxx);\n    {\n        int2 add0_1 = asint(asuint((int(2)).xx) + asuint((int(1)).xx));\n        int2 add1_1 = asint(asuint((int(2)).xx) + asuint((int(1)).xx));\n        uint2 add2_1 = ((2u).xx + (1u).xx);\n        uint2 add3_1 = ((2u).xx + (1u).xx);\n        float2 add4_1 = ((2.0).xx + (1.0).xx);\n        float2 add5_1 = ((2.0).xx + (1.0).xx);\n        int2 sub0_1 = asint(asuint((int(2)).xx) - asuint((int(1)).xx));\n        int2 sub1_1 = asint(asuint((int(2)).xx) - asuint((int(1)).xx));\n        uint2 sub2_1 = ((2u).xx - (1u).xx);\n        uint2 sub3_1 = ((2u).xx - (1u).xx);\n        float2 sub4_1 = ((2.0).xx - (1.0).xx);\n        float2 sub5_1 = ((2.0).xx - (1.0).xx);\n        int2 mul0_1 = asint(asuint((int(2)).xx) * asuint(int(1)));\n        int2 mul1_1 = asint(asuint(int(2)) * asuint((int(1)).xx));\n        uint2 mul2_1 = ((2u).xx * 1u);\n        uint2 mul3_1 = (2u * (1u).xx);\n        float2 mul4_1 = ((2.0).xx * 1.0);\n        float2 mul5_1 = (2.0 * (1.0).xx);\n        int2 div0_1 = naga_div((int(2)).xx, (int(1)).xx);\n        int2 div1_1 = naga_div((int(2)).xx, (int(1)).xx);\n        uint2 div2_1 = naga_div((2u).xx, (1u).xx);\n        uint2 div3_1 = naga_div((2u).xx, (1u).xx);\n        float2 div4_1 = ((2.0).xx / (1.0).xx);\n        float2 div5_1 = ((2.0).xx / (1.0).xx);\n        int2 rem0_1 = naga_mod((int(2)).xx, (int(1)).xx);\n        int2 rem1_1 = naga_mod((int(2)).xx, (int(1)).xx);\n        uint2 rem2_1 = naga_mod((2u).xx, (1u).xx);\n        uint2 rem3_1 = naga_mod((2u).xx, (1u).xx);\n        float2 rem4_1 = naga_mod((2.0).xx, (1.0).xx);\n        float2 rem5_1 = naga_mod((2.0).xx, (1.0).xx);\n    }\n    float3x3 add = float3x3(float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0));\n    float3x3 sub = float3x3(float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0));\n    float3x3 mul_scalar0_ = mul(1.0, ZeroValuefloat3x3());\n    float3x3 mul_scalar1_ = mul(ZeroValuefloat3x3(), 2.0);\n    float3 mul_vector0_ = mul((1.0).xxxx, ZeroValuefloat4x3());\n    float4 mul_vector1_ = mul(ZeroValuefloat4x3(), (2.0).xxx);\n    float3x3 mul_ = float3x3(float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0));\n    int _e205 = prevent_const_eval;\n    wgpu_7437_ = asint(asuint(_e205) + asuint(int(-2147483647 - 1)));\n    return;\n}\n\nvoid bit()\n{\n    int flip0_ = ~(int(1));\n    uint flip1_ = ~(1u);\n    int2 flip2_ = ~((int(1)).xx);\n    uint3 flip3_ = ~((1u).xxx);\n    int or0_ = (int(2) | int(1));\n    uint or1_ = (2u | 1u);\n    int2 or2_ = ((int(2)).xx | (int(1)).xx);\n    uint3 or3_ = ((2u).xxx | (1u).xxx);\n    int and0_ = (int(2) & int(1));\n    uint and1_ = (2u & 1u);\n    int2 and2_ = ((int(2)).xx & (int(1)).xx);\n    uint3 and3_ = ((2u).xxx & (1u).xxx);\n    int xor0_ = (int(2) ^ int(1));\n    uint xor1_ = (2u ^ 1u);\n    int2 xor2_ = ((int(2)).xx ^ (int(1)).xx);\n    uint3 xor3_ = ((2u).xxx ^ (1u).xxx);\n    int shl0_ = (int(2) << 1u);\n    uint shl1_ = (2u << 1u);\n    int2 shl2_ = ((int(2)).xx << (1u).xx);\n    uint3 shl3_ = ((2u).xxx << (1u).xxx);\n    int shr0_ = (int(2) >> 1u);\n    uint shr1_ = (2u >> 1u);\n    int2 shr2_ = ((int(2)).xx >> (1u).xx);\n    uint3 shr3_ = ((2u).xxx >> (1u).xxx);\n    return;\n}\n\nvoid comparison()\n{\n    bool eq0_ = (int(2) == int(1));\n    bool eq1_ = (2u == 1u);\n    bool eq2_ = (2.0 == 1.0);\n    bool2 eq3_ = ((int(2)).xx == (int(1)).xx);\n    bool3 eq4_ = ((2u).xxx == (1u).xxx);\n    bool4 eq5_ = ((2.0).xxxx == (1.0).xxxx);\n    bool neq0_ = (int(2) != int(1));\n    bool neq1_ = (2u != 1u);\n    bool neq2_ = (2.0 != 1.0);\n    bool2 neq3_ = ((int(2)).xx != (int(1)).xx);\n    bool3 neq4_ = ((2u).xxx != (1u).xxx);\n    bool4 neq5_ = ((2.0).xxxx != (1.0).xxxx);\n    bool lt0_ = (int(2) < int(1));\n    bool lt1_ = (2u < 1u);\n    bool lt2_ = (2.0 < 1.0);\n    bool2 lt3_ = ((int(2)).xx < (int(1)).xx);\n    bool3 lt4_ = ((2u).xxx < (1u).xxx);\n    bool4 lt5_ = ((2.0).xxxx < (1.0).xxxx);\n    bool lte0_ = (int(2) <= int(1));\n    bool lte1_ = (2u <= 1u);\n    bool lte2_ = (2.0 <= 1.0);\n    bool2 lte3_ = ((int(2)).xx <= (int(1)).xx);\n    bool3 lte4_ = ((2u).xxx <= (1u).xxx);\n    bool4 lte5_ = ((2.0).xxxx <= (1.0).xxxx);\n    bool gt0_ = (int(2) > int(1));\n    bool gt1_ = (2u > 1u);\n    bool gt2_ = (2.0 > 1.0);\n    bool2 gt3_ = ((int(2)).xx > (int(1)).xx);\n    bool3 gt4_ = ((2u).xxx > (1u).xxx);\n    bool4 gt5_ = ((2.0).xxxx > (1.0).xxxx);\n    bool gte0_ = (int(2) >= int(1));\n    bool gte1_ = (2u >= 1u);\n    bool gte2_ = (2.0 >= 1.0);\n    bool2 gte3_ = ((int(2)).xx >= (int(1)).xx);\n    bool3 gte4_ = ((2u).xxx >= (1u).xxx);\n    bool4 gte5_ = ((2.0).xxxx >= (1.0).xxxx);\n    return;\n}\n\nint3 ZeroValueint3() {\n    return (int3)0;\n}\n\nvoid assignment()\n{\n    int a_1 = (int)0;\n    int3 vec0_ = ZeroValueint3();\n\n    a_1 = int(1);\n    int _e5 = a_1;\n    a_1 = asint(asuint(_e5) + asuint(int(1)));\n    int _e7 = a_1;\n    a_1 = asint(asuint(_e7) - asuint(int(1)));\n    int _e9 = a_1;\n    int _e10 = a_1;\n    a_1 = asint(asuint(_e9) * asuint(_e10));\n    int _e12 = a_1;\n    int _e13 = a_1;\n    a_1 = naga_div(_e12, _e13);\n    int _e15 = a_1;\n    a_1 = naga_mod(_e15, int(1));\n    int _e17 = a_1;\n    a_1 = (_e17 & int(0));\n    int _e19 = a_1;\n    a_1 = (_e19 | int(0));\n    int _e21 = a_1;\n    a_1 = (_e21 ^ int(0));\n    int _e23 = a_1;\n    a_1 = (_e23 << 2u);\n    int _e25 = a_1;\n    a_1 = (_e25 >> 1u);\n    int _e28 = a_1;\n    a_1 = asint(asuint(_e28) + asuint(int(1)));\n    int _e31 = a_1;\n    a_1 = asint(asuint(_e31) - asuint(int(1)));\n    int _e37 = vec0_[int(1)];\n    vec0_[int(1)] = asint(asuint(_e37) + asuint(int(1)));\n    int _e41 = vec0_[int(1)];\n    vec0_[int(1)] = asint(asuint(_e41) - asuint(int(1)));\n    return;\n}\n\nint naga_neg(int val) {\n    return asint(-asuint(val));\n}\n\nvoid negation_avoids_prefix_decrement()\n{\n    int i0_ = naga_neg(int(1));\n    int i1_ = naga_neg(naga_neg(int(1)));\n    int i2_ = naga_neg(naga_neg(int(1)));\n    int i3_ = naga_neg(naga_neg(int(1)));\n    int i4_ = naga_neg(naga_neg(naga_neg(int(1))));\n    int i5_ = naga_neg(naga_neg(naga_neg(naga_neg(int(1)))));\n    int i6_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(int(1))))));\n    int i7_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(int(1))))));\n    float f0_ = -(1.0);\n    float f1_ = -(-(1.0));\n    float f2_ = -(-(1.0));\n    float f3_ = -(-(1.0));\n    float f4_ = -(-(-(1.0)));\n    float f5_ = -(-(-(-(1.0))));\n    float f6_ = -(-(-(-(-(1.0)))));\n    float f7_ = -(-(-(-(-(1.0)))));\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main(uint3 id : SV_GroupID)\n{\n    const float4 _e1 = builtins();\n    const float4 _e6 = splat(float(id.x), int(id.y));\n    const float2 _e7 = splat_assignment();\n    const float3 _e12 = bool_cast(float3(1.0, 1.0, 1.0));\n    logical();\n    arithmetic();\n    bit();\n    comparison();\n    assignment();\n    negation_avoids_prefix_decrement();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-operators.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-overrides.hlsl",
    "content": "static const bool has_point_light = false;\nstatic const float specular_param = 2.3;\nstatic const float gain = 1.1;\nstatic const float width = 0.0;\nstatic const float depth = 2.3;\nstatic const float height = 4.6;\nstatic const float inferred_f32_ = 2.718;\nstatic const uint auto_conversion = 0u;\n\nstatic float gain_x_10_ = 11.0;\nstatic float store_override = (float)0;\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    float t = 23.0;\n    bool x = (bool)0;\n    float gain_x_100_ = (float)0;\n\n    x = true;\n    float _e9 = gain_x_10_;\n    gain_x_100_ = (_e9 * 10.0);\n    store_override = gain;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-overrides.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-padding.hlsl",
    "content": "struct S {\n    float3 a;\n    int _end_pad_0;\n};\n\nstruct Test {\n    S a;\n    float b;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n};\n\nstruct Test2_ {\n    float3 a[2];\n    int _pad1_0;\n    float b;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n};\n\nstruct Test3_ {\n    row_major float4x3 a;\n    int _pad1_0;\n    float b;\n    int _end_pad_0;\n    int _end_pad_1;\n    int _end_pad_2;\n};\n\ncbuffer input1_ : register(b0) { Test input1_; }\ncbuffer input2_ : register(b1) { Test2_ input2_; }\ncbuffer input3_ : register(b2) { Test3_ input3_; }\n\nfloat4 vertex() : SV_Position\n{\n    float _e4 = input1_.b;\n    float _e8 = input2_.b;\n    float _e12 = input3_.b;\n    return ((((1.0).xxxx * _e4) * _e8) * _e12);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-padding.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vertex\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-phony_assignment.hlsl",
    "content": "cbuffer binding : register(b0) { float binding; }\n\nint five()\n{\n    return int(5);\n}\n\n[numthreads(1, 1, 1)]\nvoid main(uint3 id : SV_DispatchThreadID)\n{\n    float phony = binding;\n    float phony_1 = binding;\n    const int _e6 = five();\n    const int _e7 = five();\n    float phony_2 = binding;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-phony_assignment.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-pointer-function-arg.hlsl",
    "content": "void takes_ptr(inout int p)\n{\n    return;\n}\n\nvoid takes_array_ptr(inout int p_1[4])\n{\n    return;\n}\n\nvoid takes_vec_ptr(inout int2 p_2)\n{\n    return;\n}\n\nvoid takes_mat_ptr(inout float2x2 p_3)\n{\n    return;\n}\n\ntypedef int ret_Constructarray4_int_[4];\nret_Constructarray4_int_ Constructarray4_int_(int arg0, int arg1, int arg2, int arg3) {\n    int ret[4] = { arg0, arg1, arg2, arg3 };\n    return ret;\n}\n\nvoid local_var(uint i)\n{\n    int arr[4] = Constructarray4_int_(int(1), int(2), int(3), int(4));\n\n    takes_ptr(arr[min(uint(i), 3u)]);\n    takes_array_ptr(arr);\n    return;\n}\n\nvoid mat_vec_ptrs(inout int2 pv[4], inout float2x2 pm[4], uint i_1)\n{\n    takes_vec_ptr(pv[min(uint(i_1), 3u)]);\n    takes_mat_ptr(pm[min(uint(i_1), 3u)]);\n    return;\n}\n\nvoid argument(inout int v[4], uint i_2)\n{\n    takes_ptr(v[min(uint(i_2), 3u)]);\n    return;\n}\n\nvoid argument_nested_x2_(inout int v_1[4][4], uint i_3, uint j)\n{\n    takes_ptr(v_1[min(uint(i_3), 3u)][min(uint(j), 3u)]);\n    takes_ptr(v_1[min(uint(i_3), 3u)][0]);\n    takes_ptr(v_1[0][min(uint(j), 3u)]);\n    takes_array_ptr(v_1[min(uint(i_3), 3u)]);\n    return;\n}\n\nvoid argument_nested_x3_(inout int v_2[4][4][4], uint i_4, uint j_1)\n{\n    takes_ptr(v_2[min(uint(i_4), 3u)][0][min(uint(j_1), 3u)]);\n    takes_ptr(v_2[min(uint(i_4), 3u)][min(uint(j_1), 3u)][0]);\n    takes_ptr(v_2[0][min(uint(i_4), 3u)][min(uint(j_1), 3u)]);\n    return;\n}\n\nvoid index_from_self(inout int v_3[4], uint i_5)\n{\n    int _e3 = v_3[min(uint(i_5), 3u)];\n    takes_ptr(v_3[min(uint(_e3), 3u)]);\n    return;\n}\n\nvoid local_var_from_arg(int a[4], uint i_6)\n{\n    int b[4] = (int[4])0;\n\n    b = a;\n    takes_ptr(b[min(uint(i_6), 3u)]);\n    return;\n}\n\nvoid let_binding(inout int a_1[4], uint i_7)\n{\n    takes_ptr(a_1[min(uint(i_7), 3u)]);\n    takes_ptr(a_1[0]);\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    int2 vec[4] = (int2[4])0;\n    float2x2 mat[4] = (float2x2[4])0;\n    int arr1d[4] = (int[4])0;\n    int arr2d[4][4] = (int[4][4])0;\n    int arr3d[4][4][4] = (int[4][4][4])0;\n\n    local_var(1u);\n    mat_vec_ptrs(vec, mat, 1u);\n    argument(arr1d, 1u);\n    argument_nested_x2_(arr2d, 1u, 2u);\n    argument_nested_x3_(arr3d, 1u, 2u);\n    index_from_self(arr1d, 1u);\n    local_var_from_arg(Constructarray4_int_(int(1), int(2), int(3), int(4)), 5u);\n    let_binding(arr1d, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-pointer-function-arg.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-primitive-index.hlsl",
    "content": "struct FragmentInput_func {\n    uint index_1 : SV_PrimitiveID;\n};\n\nfloat4 func(FragmentInput_func fragmentinput_func) : SV_Target0\n{\n    uint index = fragmentinput_func.index_1;\n    return float4(float(index), 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-primitive-index.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"func\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-push-constants.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);\n\nstruct ImmediateData {\n    float multiplier;\n};\n\nstruct FragmentIn {\n    float4 color : LOC0;\n};\n\nConstantBuffer<ImmediateData> im: register(b0);\n\nstruct FragmentInput_main {\n    float4 color : LOC0;\n};\n\nfloat4 vert_main(float2 pos : LOC0, uint ii : SV_InstanceID, uint vi : SV_VertexID) : SV_Position\n{\n    float _e8 = im.multiplier;\n    return float4((((float((_NagaConstants.first_instance + ii)) * float((_NagaConstants.first_vertex + vi))) * _e8) * pos), 0.0, 1.0);\n}\n\nfloat4 main(FragmentInput_main fragmentinput_main) : SV_Target0\n{\n    FragmentIn in_ = { fragmentinput_main.color };\n    float _e4 = im.multiplier;\n    return (in_.color * _e4);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-push-constants.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vert_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-quad.hlsl",
    "content": "struct VertexOutput {\n    float2 uv : LOC0;\n    float4 position : SV_Position;\n};\n\nstatic const float c_scale = 1.2;\n\nTexture2D<float4> u_texture : register(t0);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space255);\nstatic const SamplerState u_sampler = nagaSamplerHeap[nagaGroup0SamplerIndexArray[1]];\n\nstruct VertexOutput_vert_main {\n    float2 uv_2 : LOC0;\n    float4 position : SV_Position;\n};\n\nstruct FragmentInput_frag_main {\n    float2 uv_3 : LOC0;\n};\n\nVertexOutput ConstructVertexOutput(float2 arg0, float4 arg1) {\n    VertexOutput ret = (VertexOutput)0;\n    ret.uv = arg0;\n    ret.position = arg1;\n    return ret;\n}\n\nVertexOutput_vert_main vert_main(float2 pos : LOC0, float2 uv : LOC1)\n{\n    const VertexOutput vertexoutput = ConstructVertexOutput(uv, float4((c_scale * pos), 0.0, 1.0));\n    const VertexOutput_vert_main vertexoutput_1 = { vertexoutput.uv, vertexoutput.position };\n    return vertexoutput_1;\n}\n\nfloat4 frag_main(FragmentInput_frag_main fragmentinput_frag_main) : SV_Target0\n{\n    float2 uv_1 = fragmentinput_frag_main.uv_3;\n    float4 color = u_texture.Sample(u_sampler, uv_1);\n    if ((color.w == 0.0)) {\n        discard;\n    }\n    float4 premultiplied = (color.w * color);\n    return premultiplied;\n}\n\nfloat4 fs_extra() : SV_Target0\n{\n    return float4(0.0, 0.5, 0.0, 0.5);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-quad.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vert_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"frag_main\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"fs_extra\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-ray-query-no-init-tracking.hlsl",
    "content": "struct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    float2 barycentrics;\n    bool front_face;\n    int _pad9_0;\n    int _pad9_1;\n    row_major float4x3 object_to_world;\n    int _pad10_0;\n    row_major float4x3 world_to_object;\n    int _end_pad_0;\n};\n\nstruct RayDesc_ {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    float3 origin;\n    int _pad5_0;\n    float3 dir;\n    int _end_pad_0;\n};\n\nstruct Output {\n    uint visible;\n    int _pad1_0;\n    int _pad1_1;\n    int _pad1_2;\n    float3 normal;\n    int _end_pad_0;\n};\n\nRayDesc RayDescFromRayDesc_(RayDesc_ arg0) {\n    RayDesc ret = (RayDesc)0;\n    ret.Origin = arg0.origin;\n    ret.TMin = arg0.tmin;\n    ret.Direction = arg0.dir;\n    ret.TMax = arg0.tmax;\n    return ret;\n}\n\nRaytracingAccelerationStructure acc_struct : register(t0);\nRWByteAddressBuffer output : register(u1);\n\nRayDesc_ ConstructRayDesc_(uint arg0, uint arg1, float arg2, float arg3, float3 arg4, float3 arg5) {\n    RayDesc_ ret = (RayDesc_)0;\n    ret.flags = arg0;\n    ret.cull_mask = arg1;\n    ret.tmin = arg2;\n    ret.tmax = arg3;\n    ret.origin = arg4;\n    ret.dir = arg5;\n    return ret;\n}\n\nRayIntersection GetCommittedIntersection(RayQuery<RAY_FLAG_NONE> rq, uint rq_tracker) {\n    RayIntersection ret = (RayIntersection)0;\n    ret.kind = rq.CommittedStatus();\n    if( rq.CommittedStatus() == COMMITTED_NOTHING) {} else {\n        ret.t = rq.CommittedRayT();\n        ret.instance_custom_data = rq.CommittedInstanceID();\n        ret.instance_index = rq.CommittedInstanceIndex();\n        ret.sbt_record_offset = rq.CommittedInstanceContributionToHitGroupIndex();\n        ret.geometry_index = rq.CommittedGeometryIndex();\n        ret.primitive_index = rq.CommittedPrimitiveIndex();\n        if( rq.CommittedStatus() == COMMITTED_TRIANGLE_HIT ) {\n            ret.barycentrics = rq.CommittedTriangleBarycentrics();\n            ret.front_face = rq.CommittedTriangleFrontFace();\n        }\n        ret.object_to_world = rq.CommittedObjectToWorld4x3();\n        ret.world_to_object = rq.CommittedWorldToObject4x3();\n    }\n    return ret;\n}\n\nRayIntersection query_loop(float3 pos, float3 dir, RaytracingAccelerationStructure acs)\n{\n    RayQuery<RAY_FLAG_NONE> rq_1;\n    uint naga_query_init_tracker_for_rq_1 = 0;\n\n    {\n        RayDesc_ naga_desc = ConstructRayDesc_(4u, 255u, 0.1, 100.0, pos, dir);\n        rq_1.TraceRayInline(acs, naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\n    }\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool _e9 = false;\n        _e9 = rq_1.Proceed();\n        if (_e9) {\n        } else {\n            break;\n        }\n        {\n        }\n    }\n    const RayIntersection rayintersection = GetCommittedIntersection(rq_1, naga_query_init_tracker_for_rq_1);\n    return rayintersection;\n}\n\nfloat3 get_torus_normal(float3 world_point, RayIntersection intersection)\n{\n    float3 local_point = mul(float4(world_point, 1.0), intersection.world_to_object);\n    float2 point_on_guiding_line = (normalize(local_point.xy) * 2.4);\n    float3 world_point_on_guiding_line = mul(float4(point_on_guiding_line, 0.0, 1.0), intersection.object_to_world);\n    return normalize((world_point - world_point_on_guiding_line));\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    float3 pos_1 = (0.0).xxx;\n    float3 dir_1 = float3(0.0, 1.0, 0.0);\n    const RayIntersection _e7 = query_loop(pos_1, dir_1, acc_struct);\n    output.Store(0, asuint(uint((_e7.kind == 0u))));\n    const float3 _e18 = get_torus_normal((dir_1 * _e7.t), _e7);\n    output.Store3(16, asuint(_e18));\n    return;\n}\n\nRayIntersection GetCandidateIntersection(RayQuery<RAY_FLAG_NONE> rq, uint rq_tracker) {\n    RayIntersection ret = (RayIntersection)0;\n    CANDIDATE_TYPE kind = rq.CandidateType();\n    if (kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {\n        ret.kind = 1;\n        ret.t = rq.CandidateTriangleRayT();\n        ret.barycentrics = rq.CandidateTriangleBarycentrics();\n        ret.front_face = rq.CandidateTriangleFrontFace();\n    } else {\n        ret.kind = 3;\n    }\n    ret.instance_custom_data = rq.CandidateInstanceID();\n    ret.instance_index = rq.CandidateInstanceIndex();\n    ret.sbt_record_offset = rq.CandidateInstanceContributionToHitGroupIndex();\n    ret.geometry_index = rq.CandidateGeometryIndex();\n    ret.primitive_index = rq.CandidatePrimitiveIndex();\n    ret.object_to_world = rq.CandidateObjectToWorld4x3();\n    ret.world_to_object = rq.CandidateWorldToObject4x3();\n    return ret;\n}\n\n[numthreads(1, 1, 1)]\nvoid main_candidate()\n{\n    RayQuery<RAY_FLAG_NONE> rq;\n    uint naga_query_init_tracker_for_rq = 0;\n\n    float3 pos_2 = (0.0).xxx;\n    float3 dir_2 = float3(0.0, 1.0, 0.0);\n    {\n        RayDesc_ naga_desc = ConstructRayDesc_(4u, 255u, 0.1, 100.0, pos_2, dir_2);\n        rq.TraceRayInline(acc_struct, naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\n    }\n    RayIntersection intersection_1 = GetCandidateIntersection(rq, naga_query_init_tracker_for_rq);\n    if ((intersection_1.kind == 3u)) {\n        rq.CommitProceduralPrimitiveHit(10.0);\n        return;\n    } else {\n        if ((intersection_1.kind == 1u)) {\n            rq.CommitNonOpaqueTriangleHit();\n            return;\n        } else {\n            rq.Abort();\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-ray-query-no-init-tracking.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_5\",\n        ),\n        (\n            entry_point:\"main_candidate\",\n            target_profile:\"cs_6_5\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-ray-query.hlsl",
    "content": "struct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    float2 barycentrics;\n    bool front_face;\n    int _pad9_0;\n    int _pad9_1;\n    row_major float4x3 object_to_world;\n    int _pad10_0;\n    row_major float4x3 world_to_object;\n    int _end_pad_0;\n};\n\nstruct RayDesc_ {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    float3 origin;\n    int _pad5_0;\n    float3 dir;\n    int _end_pad_0;\n};\n\nstruct Output {\n    uint visible;\n    int _pad1_0;\n    int _pad1_1;\n    int _pad1_2;\n    float3 normal;\n    int _end_pad_0;\n};\n\nRayDesc RayDescFromRayDesc_(RayDesc_ arg0) {\n    RayDesc ret = (RayDesc)0;\n    ret.Origin = arg0.origin;\n    ret.TMin = arg0.tmin;\n    ret.Direction = arg0.dir;\n    ret.TMax = arg0.tmax;\n    return ret;\n}\n\nRaytracingAccelerationStructure acc_struct : register(t0);\nRWByteAddressBuffer output : register(u1);\n\nRayDesc_ ConstructRayDesc_(uint arg0, uint arg1, float arg2, float arg3, float3 arg4, float3 arg5) {\n    RayDesc_ ret = (RayDesc_)0;\n    ret.flags = arg0;\n    ret.cull_mask = arg1;\n    ret.tmin = arg2;\n    ret.tmax = arg3;\n    ret.origin = arg4;\n    ret.dir = arg5;\n    return ret;\n}\n\nRayIntersection GetCommittedIntersection(RayQuery<RAY_FLAG_NONE> rq, uint rq_tracker) {\n    RayIntersection ret = (RayIntersection)0;\n    if (((rq_tracker & 4) == 4)) {\n        ret.kind = rq.CommittedStatus();\n        if( rq.CommittedStatus() == COMMITTED_NOTHING) {} else {\n            ret.t = rq.CommittedRayT();\n            ret.instance_custom_data = rq.CommittedInstanceID();\n            ret.instance_index = rq.CommittedInstanceIndex();\n            ret.sbt_record_offset = rq.CommittedInstanceContributionToHitGroupIndex();\n            ret.geometry_index = rq.CommittedGeometryIndex();\n            ret.primitive_index = rq.CommittedPrimitiveIndex();\n            if( rq.CommittedStatus() == COMMITTED_TRIANGLE_HIT ) {\n                ret.barycentrics = rq.CommittedTriangleBarycentrics();\n                ret.front_face = rq.CommittedTriangleFrontFace();\n            }\n            ret.object_to_world = rq.CommittedObjectToWorld4x3();\n            ret.world_to_object = rq.CommittedWorldToObject4x3();\n        }\n    }\n    return ret;\n}\n\nRayIntersection query_loop(float3 pos, float3 dir, RaytracingAccelerationStructure acs)\n{\n    RayQuery<RAY_FLAG_NONE> rq_1;\n    uint naga_query_init_tracker_for_rq_1 = 0;\n\n    {\n        RayDesc_ naga_desc = ConstructRayDesc_(4u, 255u, 0.1, 100.0, pos, dir);\n        float naga_tmin = naga_desc.tmin;\n        float naga_tmax = naga_desc.tmax;\n        float3 naga_origin = naga_desc.origin;\n        float3 naga_dir = naga_desc.dir;\n        uint naga_flags = naga_desc.flags;\n        bool naga_tmin_valid = (naga_tmin >= 0.0) && (naga_tmin <= naga_tmax) && !(((asuint(naga_tmin) & 2139095040) == 2139095040) && ((asuint(naga_tmin) & 0x7fffff) != 0));\n        bool naga_tmax_valid = !(((asuint(naga_tmax) & 2139095040) == 2139095040) && ((asuint(naga_tmax) & 0x7fffff) != 0));\n        bool naga_origin_valid = !any((((asuint(naga_origin) & 2139095040) == 2139095040) && ((asuint(naga_origin) & 0x7fffff) != 0)));\n        bool naga_dir_valid = !any((((asuint(naga_dir) & 2139095040) == 2139095040) && ((asuint(naga_dir) & 0x7fffff) != 0)));\n        bool naga_contains_opaque = ((naga_flags & 1) == 1);\n        bool naga_contains_no_opaque = ((naga_flags & 2) == 2);\n        bool naga_contains_cull_opaque = ((naga_flags & 64) == 64);\n        bool naga_contains_cull_no_opaque = ((naga_flags & 128) == 128);\n        bool naga_contains_cull_front = ((naga_flags & 32) == 32);\n        bool naga_contains_cull_back = ((naga_flags & 16) == 16);\n        bool naga_contains_skip_triangles = ((naga_flags & 256) == 256);\n        bool naga_contains_skip_aabbs = ((naga_flags & 512) == 512);\n        bool naga_contains_skip_triangles_aabbs =  (naga_contains_skip_aabbs && naga_contains_skip_triangles) ;\n        bool naga_contains_skip_triangles_cull =  (naga_contains_cull_front && naga_contains_skip_triangles) || (naga_contains_cull_front && naga_contains_cull_back) || (naga_contains_cull_back && naga_contains_skip_triangles) ;\n        bool naga_contains_multiple_opaque =  (naga_contains_cull_no_opaque && naga_contains_opaque) || (naga_contains_cull_no_opaque && naga_contains_no_opaque) || (naga_contains_cull_no_opaque && naga_contains_cull_opaque) || (naga_contains_cull_opaque && naga_contains_opaque) || (naga_contains_cull_opaque && naga_contains_no_opaque) || (naga_contains_no_opaque && naga_contains_opaque) ;\n        if (naga_tmin_valid && naga_tmax_valid && naga_origin_valid && naga_dir_valid && !(naga_contains_skip_triangles_aabbs || naga_contains_skip_triangles_cull || naga_contains_multiple_opaque)) {\n            naga_query_init_tracker_for_rq_1 = naga_query_init_tracker_for_rq_1 | 1;\n            rq_1.TraceRayInline(acs, naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\n        }\n    }\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool _e9 = false;\n        {\n            bool naga_has_initialized = ((naga_query_init_tracker_for_rq_1 & 1) == 1);\n            bool naga_has_finished = ((naga_query_init_tracker_for_rq_1 & 4) == 4);\n            if (naga_has_initialized && !naga_has_finished) {\n                _e9 = rq_1.Proceed();\n                naga_query_init_tracker_for_rq_1 = naga_query_init_tracker_for_rq_1 | 2;\n                if (!_e9) { naga_query_init_tracker_for_rq_1 = naga_query_init_tracker_for_rq_1 | 4; }\n        }}\n        if (_e9) {\n        } else {\n            break;\n        }\n        {\n        }\n    }\n    const RayIntersection rayintersection = GetCommittedIntersection(rq_1, naga_query_init_tracker_for_rq_1);\n    return rayintersection;\n}\n\nfloat3 get_torus_normal(float3 world_point, RayIntersection intersection)\n{\n    float3 local_point = mul(float4(world_point, 1.0), intersection.world_to_object);\n    float2 point_on_guiding_line = (normalize(local_point.xy) * 2.4);\n    float3 world_point_on_guiding_line = mul(float4(point_on_guiding_line, 0.0, 1.0), intersection.object_to_world);\n    return normalize((world_point - world_point_on_guiding_line));\n}\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    float3 pos_1 = (0.0).xxx;\n    float3 dir_1 = float3(0.0, 1.0, 0.0);\n    const RayIntersection _e7 = query_loop(pos_1, dir_1, acc_struct);\n    output.Store(0, asuint(uint((_e7.kind == 0u))));\n    const float3 _e18 = get_torus_normal((dir_1 * _e7.t), _e7);\n    output.Store3(16, asuint(_e18));\n    return;\n}\n\nRayIntersection GetCandidateIntersection(RayQuery<RAY_FLAG_NONE> rq, uint rq_tracker) {\n    RayIntersection ret = (RayIntersection)0;\n    if (((rq_tracker & 2) == 2) && !((rq_tracker & 4) == 4)) {\n        CANDIDATE_TYPE kind = rq.CandidateType();\n        if (kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {\n            ret.kind = 1;\n            ret.t = rq.CandidateTriangleRayT();\n            ret.barycentrics = rq.CandidateTriangleBarycentrics();\n            ret.front_face = rq.CandidateTriangleFrontFace();\n        } else {\n            ret.kind = 3;\n        }\n        ret.instance_custom_data = rq.CandidateInstanceID();\n        ret.instance_index = rq.CandidateInstanceIndex();\n        ret.sbt_record_offset = rq.CandidateInstanceContributionToHitGroupIndex();\n        ret.geometry_index = rq.CandidateGeometryIndex();\n        ret.primitive_index = rq.CandidatePrimitiveIndex();\n        ret.object_to_world = rq.CandidateObjectToWorld4x3();\n        ret.world_to_object = rq.CandidateWorldToObject4x3();\n    }\n    return ret;\n}\n\n[numthreads(1, 1, 1)]\nvoid main_candidate()\n{\n    RayQuery<RAY_FLAG_NONE> rq;\n    uint naga_query_init_tracker_for_rq = 0;\n\n    float3 pos_2 = (0.0).xxx;\n    float3 dir_2 = float3(0.0, 1.0, 0.0);\n    {\n        RayDesc_ naga_desc = ConstructRayDesc_(4u, 255u, 0.1, 100.0, pos_2, dir_2);\n        float naga_tmin = naga_desc.tmin;\n        float naga_tmax = naga_desc.tmax;\n        float3 naga_origin = naga_desc.origin;\n        float3 naga_dir = naga_desc.dir;\n        uint naga_flags = naga_desc.flags;\n        bool naga_tmin_valid = (naga_tmin >= 0.0) && (naga_tmin <= naga_tmax) && !(((asuint(naga_tmin) & 2139095040) == 2139095040) && ((asuint(naga_tmin) & 0x7fffff) != 0));\n        bool naga_tmax_valid = !(((asuint(naga_tmax) & 2139095040) == 2139095040) && ((asuint(naga_tmax) & 0x7fffff) != 0));\n        bool naga_origin_valid = !any((((asuint(naga_origin) & 2139095040) == 2139095040) && ((asuint(naga_origin) & 0x7fffff) != 0)));\n        bool naga_dir_valid = !any((((asuint(naga_dir) & 2139095040) == 2139095040) && ((asuint(naga_dir) & 0x7fffff) != 0)));\n        bool naga_contains_opaque = ((naga_flags & 1) == 1);\n        bool naga_contains_no_opaque = ((naga_flags & 2) == 2);\n        bool naga_contains_cull_opaque = ((naga_flags & 64) == 64);\n        bool naga_contains_cull_no_opaque = ((naga_flags & 128) == 128);\n        bool naga_contains_cull_front = ((naga_flags & 32) == 32);\n        bool naga_contains_cull_back = ((naga_flags & 16) == 16);\n        bool naga_contains_skip_triangles = ((naga_flags & 256) == 256);\n        bool naga_contains_skip_aabbs = ((naga_flags & 512) == 512);\n        bool naga_contains_skip_triangles_aabbs =  (naga_contains_skip_aabbs && naga_contains_skip_triangles) ;\n        bool naga_contains_skip_triangles_cull =  (naga_contains_cull_front && naga_contains_skip_triangles) || (naga_contains_cull_front && naga_contains_cull_back) || (naga_contains_cull_back && naga_contains_skip_triangles) ;\n        bool naga_contains_multiple_opaque =  (naga_contains_cull_no_opaque && naga_contains_opaque) || (naga_contains_cull_no_opaque && naga_contains_no_opaque) || (naga_contains_cull_no_opaque && naga_contains_cull_opaque) || (naga_contains_cull_opaque && naga_contains_opaque) || (naga_contains_cull_opaque && naga_contains_no_opaque) || (naga_contains_no_opaque && naga_contains_opaque) ;\n        if (naga_tmin_valid && naga_tmax_valid && naga_origin_valid && naga_dir_valid && !(naga_contains_skip_triangles_aabbs || naga_contains_skip_triangles_cull || naga_contains_multiple_opaque)) {\n            naga_query_init_tracker_for_rq = naga_query_init_tracker_for_rq | 1;\n            rq.TraceRayInline(acc_struct, naga_desc.flags, naga_desc.cull_mask, RayDescFromRayDesc_(naga_desc));\n        }\n    }\n    RayIntersection intersection_1 = GetCandidateIntersection(rq, naga_query_init_tracker_for_rq);\n    if ((intersection_1.kind == 3u)) {\n        if (((naga_query_init_tracker_for_rq & 2) == 2) && !((naga_query_init_tracker_for_rq & 4) == 4)) {\n            CANDIDATE_TYPE naga_kind = rq.CandidateType();\n            float naga_tmin = rq.RayTMin();\n            float naga_tcurrentmax = rq.CommittedRayT();\n            if ((naga_kind == CANDIDATE_PROCEDURAL_PRIMITIVE) && (naga_tmin <=10.0) && (10.0 <= naga_tcurrentmax)) {\n                rq.CommitProceduralPrimitiveHit(10.0);\n        }}\n        return;\n    } else {\n        if ((intersection_1.kind == 1u)) {\n            if (((naga_query_init_tracker_for_rq & 2) == 2) && !((naga_query_init_tracker_for_rq & 4) == 4)) {\n                CANDIDATE_TYPE naga_kind = rq.CandidateType();\n                if (naga_kind == CANDIDATE_NON_OPAQUE_TRIANGLE) {\n                    rq.CommitNonOpaqueTriangleHit();\n            }}\n            return;\n        } else {\n            if (((naga_query_init_tracker_for_rq & 1) == 1)) {\n                rq.Abort();\n            }\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-ray-query.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_5\",\n        ),\n        (\n            entry_point:\"main_candidate\",\n            target_profile:\"cs_6_5\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-select.hlsl",
    "content": "[numthreads(1, 1, 1)]\nvoid main()\n{\n    int2 x0_ = int2(int(1), int(2));\n    float2 i1_ = (float2)0;\n\n    int _e12 = x0_.x;\n    int _e14 = x0_.y;\n    i1_ = ((_e12 < _e14) ? float2(0.0, 1.0) : float2(1.0, 0.0));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-select.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-shadow.hlsl",
    "content": "struct Globals {\n    row_major float4x4 view_proj;\n    uint4 num_lights;\n};\n\nstruct Entity {\n    row_major float4x4 world;\n    float4 color;\n};\n\nstruct VertexOutput {\n    float4 proj_position : SV_Position;\n    float3 world_normal : LOC0;\n    float4 world_position : LOC1;\n};\n\nstruct Light {\n    row_major float4x4 proj;\n    float4 pos;\n    float4 color;\n};\n\nstatic const float3 c_ambient = float3(0.05, 0.05, 0.05);\nstatic const uint c_max_lights = 10u;\n\ncbuffer u_globals : register(b0) { Globals u_globals; }\ncbuffer u_entity : register(b0, space1) { Entity u_entity; }\nByteAddressBuffer s_lights : register(t1);\ncbuffer u_lights : register(b1) { Light u_lights[10]; }\nTexture2DArray<float> t_shadow : register(t2);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space255);\nstatic const SamplerComparisonState sampler_shadow = nagaComparisonSamplerHeap[nagaGroup0SamplerIndexArray[3]];\n\nstruct VertexOutput_vs_main {\n    float3 world_normal : LOC0;\n    float4 world_position : LOC1;\n    float4 proj_position : SV_Position;\n};\n\nstruct FragmentInput_fs_main {\n    float3 world_normal_1 : LOC0;\n    float4 world_position_1 : LOC1;\n    float4 proj_position_1 : SV_Position;\n};\n\nstruct FragmentInput_fs_main_without_storage {\n    float3 world_normal_2 : LOC0;\n    float4 world_position_2 : LOC1;\n    float4 proj_position_2 : SV_Position;\n};\n\nfloat fetch_shadow(uint light_id, float4 homogeneous_coords)\n{\n    if ((homogeneous_coords.w <= 0.0)) {\n        return 1.0;\n    }\n    float2 flip_correction = float2(0.5, -0.5);\n    float proj_correction = (1.0 / homogeneous_coords.w);\n    float2 light_local = (((homogeneous_coords.xy * flip_correction) * proj_correction) + float2(0.5, 0.5));\n    float _e24 = t_shadow.SampleCmpLevelZero(sampler_shadow, float3(light_local, int(light_id)), (homogeneous_coords.z * proj_correction));\n    return _e24;\n}\n\nVertexOutput_vs_main vs_main(int4 position : LOC0, int4 normal : LOC1)\n{\n    VertexOutput out_ = (VertexOutput)0;\n\n    float4x4 w = u_entity.world;\n    float4x4 _e7 = u_entity.world;\n    float4 world_pos = mul(float4(position), _e7);\n    out_.world_normal = mul(float3(normal.xyz), float3x3(w[0].xyz, w[1].xyz, w[2].xyz));\n    out_.world_position = world_pos;\n    float4x4 _e26 = u_globals.view_proj;\n    out_.proj_position = mul(world_pos, _e26);\n    VertexOutput _e28 = out_;\n    const VertexOutput vertexoutput = _e28;\n    const VertexOutput_vs_main vertexoutput_1 = { vertexoutput.world_normal, vertexoutput.world_position, vertexoutput.proj_position };\n    return vertexoutput_1;\n}\n\nLight ConstructLight(float4x4 arg0, float4 arg1, float4 arg2) {\n    Light ret = (Light)0;\n    ret.proj = arg0;\n    ret.pos = arg1;\n    ret.color = arg2;\n    return ret;\n}\n\nfloat4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0\n{\n    VertexOutput in_ = { fragmentinput_fs_main.proj_position_1, fragmentinput_fs_main.world_normal_1, fragmentinput_fs_main.world_position_1 };\n    float3 color = c_ambient;\n    uint i = 0u;\n\n    float3 normal_1 = normalize(in_.world_normal);\n    uint2 loop_bound = uint2(4294967295u, 4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (all(loop_bound == uint2(0u, 0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e40 = i;\n            i = (_e40 + 1u);\n        }\n        loop_init = false;\n        uint _e7 = i;\n        uint _e11 = u_globals.num_lights.x;\n        if ((_e7 < min(_e11, c_max_lights))) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i;\n            Light light = ConstructLight(float4x4(asfloat(s_lights.Load4(_e16*96+0+0)), asfloat(s_lights.Load4(_e16*96+0+16)), asfloat(s_lights.Load4(_e16*96+0+32)), asfloat(s_lights.Load4(_e16*96+0+48))), asfloat(s_lights.Load4(_e16*96+64)), asfloat(s_lights.Load4(_e16*96+80)));\n            uint _e19 = i;\n            const float _e23 = fetch_shadow(_e19, mul(in_.world_position, light.proj));\n            float3 light_dir = normalize((light.pos.xyz - in_.world_position.xyz));\n            float diffuse = max(0.0, dot(normal_1, light_dir));\n            float3 _e33 = color;\n            color = (_e33 + ((_e23 * diffuse) * light.color.xyz));\n        }\n    }\n    float3 _e42 = color;\n    float4 _e47 = u_entity.color;\n    return (float4(_e42, 1.0) * _e47);\n}\n\nfloat4 fs_main_without_storage(FragmentInput_fs_main_without_storage fragmentinput_fs_main_without_storage) : SV_Target0\n{\n    VertexOutput in_1 = { fragmentinput_fs_main_without_storage.proj_position_2, fragmentinput_fs_main_without_storage.world_normal_2, fragmentinput_fs_main_without_storage.world_position_2 };\n    float3 color_1 = c_ambient;\n    uint i_1 = 0u;\n\n    float3 normal_2 = normalize(in_1.world_normal);\n    uint2 loop_bound_1 = uint2(4294967295u, 4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (all(loop_bound_1 == uint2(0u, 0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        if (!loop_init_1) {\n            uint _e40 = i_1;\n            i_1 = (_e40 + 1u);\n        }\n        loop_init_1 = false;\n        uint _e7 = i_1;\n        uint _e11 = u_globals.num_lights.x;\n        if ((_e7 < min(_e11, c_max_lights))) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i_1;\n            Light light_1 = u_lights[_e16];\n            uint _e19 = i_1;\n            const float _e23 = fetch_shadow(_e19, mul(in_1.world_position, light_1.proj));\n            float3 light_dir_1 = normalize((light_1.pos.xyz - in_1.world_position.xyz));\n            float diffuse_1 = max(0.0, dot(normal_2, light_dir_1));\n            float3 _e33 = color_1;\n            color_1 = (_e33 + ((_e23 * diffuse_1) * light_1.color.xyz));\n        }\n    }\n    float3 _e42 = color_1;\n    float4 _e47 = u_entity.color;\n    return (float4(_e42, 1.0) * _e47);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-shadow.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vs_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"fs_main\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"fs_main_without_storage\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-skybox.hlsl",
    "content": "struct NagaConstants {\n    int first_vertex;\n    int first_instance;\n    uint other;\n};\nConstantBuffer<NagaConstants> _NagaConstants: register(b1);\n\nstruct VertexOutput {\n    float4 position : SV_Position;\n    float3 uv : LOC0;\n};\n\nstruct Data {\n    row_major float4x4 proj_inv;\n    row_major float4x4 view;\n};\n\ncbuffer r_data : register(b0) { Data r_data; }\nTextureCube<float4> r_texture : register(t0);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space2);\nstatic const SamplerState r_sampler = nagaSamplerHeap[nagaGroup0SamplerIndexArray[0]];\n\nstruct VertexOutput_vs_main {\n    float3 uv : LOC0;\n    float4 position : SV_Position;\n};\n\nstruct FragmentInput_fs_main {\n    float3 uv_1 : LOC0;\n    float4 position_1 : SV_Position;\n};\n\nint naga_div(int lhs, int rhs) {\n    return lhs / (((lhs == int(-2147483647 - 1) & rhs == -1) | (rhs == 0)) ? 1 : rhs);\n}\n\nVertexOutput ConstructVertexOutput(float4 arg0, float3 arg1) {\n    VertexOutput ret = (VertexOutput)0;\n    ret.position = arg0;\n    ret.uv = arg1;\n    return ret;\n}\n\nVertexOutput_vs_main vs_main(uint vertex_index : SV_VertexID)\n{\n    int tmp1_ = (int)0;\n    int tmp2_ = (int)0;\n\n    tmp1_ = naga_div(int((_NagaConstants.first_vertex + vertex_index)), int(2));\n    tmp2_ = (int((_NagaConstants.first_vertex + vertex_index)) & int(1));\n    int _e9 = tmp1_;\n    int _e15 = tmp2_;\n    float4 pos = float4(((float(_e9) * 4.0) - 1.0), ((float(_e15) * 4.0) - 1.0), 0.0, 1.0);\n    float4 _e27 = r_data.view[0];\n    float4 _e32 = r_data.view[1];\n    float4 _e37 = r_data.view[2];\n    float3x3 inv_model_view = transpose(float3x3(_e27.xyz, _e32.xyz, _e37.xyz));\n    float4x4 _e43 = r_data.proj_inv;\n    float4 unprojected = mul(pos, _e43);\n    const VertexOutput vertexoutput = ConstructVertexOutput(pos, mul(unprojected.xyz, inv_model_view));\n    const VertexOutput_vs_main vertexoutput_1 = { vertexoutput.uv, vertexoutput.position };\n    return vertexoutput_1;\n}\n\nfloat4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0\n{\n    VertexOutput in_ = { fragmentinput_fs_main.position_1, fragmentinput_fs_main.uv_1 };\n    float4 _e4 = r_texture.Sample(r_sampler, in_.uv);\n    return _e4;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-skybox.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vs_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"fs_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-standard.hlsl",
    "content": "struct FragmentInput_derivatives {\n    float4 foo_1 : SV_Position;\n};\n\nbool test_any_and_all_for_bool()\n{\n    return true;\n}\n\nfloat4 derivatives(FragmentInput_derivatives fragmentinput_derivatives) : SV_Target0\n{\n    float4 foo = fragmentinput_derivatives.foo_1;\n    float4 x = (float4)0;\n    float4 y = (float4)0;\n    float4 z = (float4)0;\n\n    float4 _e1 = ddx_coarse(foo);\n    x = _e1;\n    float4 _e3 = ddy_coarse(foo);\n    y = _e3;\n    float4 _e5 = abs(ddx_coarse(foo)) + abs(ddy_coarse(foo));\n    z = _e5;\n    float4 _e7 = ddx_fine(foo);\n    x = _e7;\n    float4 _e8 = ddy_fine(foo);\n    y = _e8;\n    float4 _e9 = abs(ddx_fine(foo)) + abs(ddy_fine(foo));\n    z = _e9;\n    float4 _e10 = ddx(foo);\n    x = _e10;\n    float4 _e11 = ddy(foo);\n    y = _e11;\n    float4 _e12 = fwidth(foo);\n    z = _e12;\n    const bool _e13 = test_any_and_all_for_bool();\n    float4 _e14 = x;\n    float4 _e15 = y;\n    float4 _e17 = z;\n    return ((_e14 + _e15) * _e17);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-standard.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"derivatives\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-storage-textures.hlsl",
    "content": "RWTexture2D<float> s_r_r : register(u0);\nRWTexture2D<float4> s_rg_r : register(u1);\nRWTexture2D<float4> s_rgba_r : register(u2);\nRWTexture2D<float> s_r_w : register(u0, space1);\nRWTexture2D<float4> s_rg_w : register(u1, space1);\nRWTexture2D<float4> s_rgba_w : register(u2, space1);\n\nfloat4 LoadedStorageValueFromfloat(float arg) {float4 ret = float4(arg, 0.0, 0.0, 1.0);return ret;}\n[numthreads(1, 1, 1)]\nvoid csLoad()\n{\n    float4 phony = LoadedStorageValueFromfloat(s_r_r.Load((0u).xx));\n    float4 phony_1 = s_rg_r.Load((0u).xx);\n    float4 phony_2 = s_rgba_r.Load((0u).xx);\n    return;\n}\n\n[numthreads(1, 1, 1)]\nvoid csStore()\n{\n    s_r_w[(0u).xx] = (0.0).xxxx;\n    s_rg_w[(0u).xx] = (0.0).xxxx;\n    s_rgba_w[(0u).xx] = (0.0).xxxx;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-storage-textures.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"csLoad\",\n            target_profile:\"cs_5_1\",\n        ),\n        (\n            entry_point:\"csStore\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-struct-layout.hlsl",
    "content": "struct NoPadding {\n    float3 v3_ : LOC0;\n    float f3_ : LOC1;\n};\n\nstruct NeedsPadding {\n    float f3_forces_padding : LOC0;\n    float3 v3_needs_padding : LOC1;\n    float f3_ : LOC2;\n};\n\ncbuffer no_padding_uniform : register(b0) { NoPadding no_padding_uniform; }\nRWByteAddressBuffer no_padding_storage : register(u1);\ncbuffer needs_padding_uniform : register(b2) { NeedsPadding needs_padding_uniform; }\nRWByteAddressBuffer needs_padding_storage : register(u3);\n\nstruct FragmentInput_no_padding_frag {\n    float3 v3_ : LOC0;\n    float f3_ : LOC1;\n};\n\nstruct FragmentInput_needs_padding_frag {\n    float f3_forces_padding : LOC0;\n    float3 v3_needs_padding : LOC1;\n    float f3_1 : LOC2;\n};\n\nfloat4 no_padding_frag(FragmentInput_no_padding_frag fragmentinput_no_padding_frag) : SV_Target0\n{\n    NoPadding input = { fragmentinput_no_padding_frag.v3_, fragmentinput_no_padding_frag.f3_ };\n    return (0.0).xxxx;\n}\n\nfloat4 no_padding_vert(NoPadding input_1) : SV_Position\n{\n    return (0.0).xxxx;\n}\n\nNoPadding ConstructNoPadding(float3 arg0, float arg1) {\n    NoPadding ret = (NoPadding)0;\n    ret.v3_ = arg0;\n    ret.f3_ = arg1;\n    return ret;\n}\n\n[numthreads(16, 1, 1)]\nvoid no_padding_comp()\n{\n    NoPadding x = (NoPadding)0;\n\n    NoPadding _e2 = no_padding_uniform;\n    x = _e2;\n    NoPadding _e4 = ConstructNoPadding(asfloat(no_padding_storage.Load3(0)), asfloat(no_padding_storage.Load(12)));\n    x = _e4;\n    return;\n}\n\nfloat4 needs_padding_frag(FragmentInput_needs_padding_frag fragmentinput_needs_padding_frag) : SV_Target0\n{\n    NeedsPadding input_2 = { fragmentinput_needs_padding_frag.f3_forces_padding, fragmentinput_needs_padding_frag.v3_needs_padding, fragmentinput_needs_padding_frag.f3_1 };\n    return (0.0).xxxx;\n}\n\nfloat4 needs_padding_vert(NeedsPadding input_3) : SV_Position\n{\n    return (0.0).xxxx;\n}\n\nNeedsPadding ConstructNeedsPadding(float arg0, float3 arg1, float arg2) {\n    NeedsPadding ret = (NeedsPadding)0;\n    ret.f3_forces_padding = arg0;\n    ret.v3_needs_padding = arg1;\n    ret.f3_ = arg2;\n    return ret;\n}\n\n[numthreads(16, 1, 1)]\nvoid needs_padding_comp()\n{\n    NeedsPadding x_1 = (NeedsPadding)0;\n\n    NeedsPadding _e2 = needs_padding_uniform;\n    x_1 = _e2;\n    NeedsPadding _e4 = ConstructNeedsPadding(asfloat(needs_padding_storage.Load(0)), asfloat(needs_padding_storage.Load3(16)), asfloat(needs_padding_storage.Load(28)));\n    x_1 = _e4;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-struct-layout.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"no_padding_vert\",\n            target_profile:\"vs_5_1\",\n        ),\n        (\n            entry_point:\"needs_padding_vert\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"no_padding_frag\",\n            target_profile:\"ps_5_1\",\n        ),\n        (\n            entry_point:\"needs_padding_frag\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n        (\n            entry_point:\"no_padding_comp\",\n            target_profile:\"cs_5_1\",\n        ),\n        (\n            entry_point:\"needs_padding_comp\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-subgroup-operations.hlsl",
    "content": "struct Structure {\n    uint num_subgroups;\n    uint subgroup_size;\n};\n\nstruct ComputeInput_main {\n    uint local_invocation_index : SV_GroupIndex;\n};\n\n[numthreads(1, 1, 1)]\nvoid main(ComputeInput_main computeinput_main)\n{\n    Structure sizes = { (1u + WaveGetLaneCount() - 1u) / WaveGetLaneCount(), WaveGetLaneCount() };\n    uint subgroup_id = computeinput_main.local_invocation_index / WaveGetLaneCount();\n    uint subgroup_invocation_id = WaveGetLaneIndex();\n    const uint4 _e7 = WaveActiveBallot(((subgroup_invocation_id & 1u) == 1u));\n    const uint4 _e8 = WaveActiveBallot(true);\n    const bool _e11 = WaveActiveAllTrue((subgroup_invocation_id != 0u));\n    const bool _e14 = WaveActiveAnyTrue((subgroup_invocation_id == 0u));\n    const uint _e15 = WaveActiveSum(subgroup_invocation_id);\n    const uint _e16 = WaveActiveProduct(subgroup_invocation_id);\n    const uint _e17 = WaveActiveMin(subgroup_invocation_id);\n    const uint _e18 = WaveActiveMax(subgroup_invocation_id);\n    const uint _e19 = WaveActiveBitAnd(subgroup_invocation_id);\n    const uint _e20 = WaveActiveBitOr(subgroup_invocation_id);\n    const uint _e21 = WaveActiveBitXor(subgroup_invocation_id);\n    const uint _e22 = WavePrefixSum(subgroup_invocation_id);\n    const uint _e23 = WavePrefixProduct(subgroup_invocation_id);\n    const uint _e24 = subgroup_invocation_id + WavePrefixSum(subgroup_invocation_id);\n    const uint _e25 = subgroup_invocation_id * WavePrefixProduct(subgroup_invocation_id);\n    const uint _e26 = WaveReadLaneFirst(subgroup_invocation_id);\n    const uint _e28 = WaveReadLaneAt(subgroup_invocation_id, 4u);\n    const uint _e33 = WaveReadLaneAt(subgroup_invocation_id, ((sizes.subgroup_size - 1u) - subgroup_invocation_id));\n    const uint _e35 = WaveReadLaneAt(subgroup_invocation_id, WaveGetLaneIndex() + 1u);\n    const uint _e37 = WaveReadLaneAt(subgroup_invocation_id, WaveGetLaneIndex() - 1u);\n    const uint _e41 = WaveReadLaneAt(subgroup_invocation_id, WaveGetLaneIndex() ^ (sizes.subgroup_size - 1u));\n    const uint _e43 = QuadReadLaneAt(subgroup_invocation_id, 4u);\n    const uint _e44 = QuadReadAcrossX(subgroup_invocation_id);\n    const uint _e45 = QuadReadAcrossY(subgroup_invocation_id);\n    const uint _e46 = QuadReadAcrossDiagonal(subgroup_invocation_id);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-subgroup-operations.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_6_0\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-texture-arg.hlsl",
    "content": "Texture2D<float4> Texture : register(t0);\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space255);\nstatic const SamplerState Sampler = nagaSamplerHeap[nagaGroup0SamplerIndexArray[1]];\n\nfloat4 test(Texture2D<float4> Passed_Texture, SamplerState Passed_Sampler)\n{\n    float4 _e5 = Passed_Texture.Sample(Passed_Sampler, float2(0.0, 0.0));\n    return _e5;\n}\n\nfloat4 main() : SV_Target0\n{\n    const float4 _e2 = test(Texture, Sampler);\n    return _e2;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-texture-arg.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-texture-external.hlsl",
    "content": "struct NagaExternalTextureTransferFn {\n    float a;\n    float b;\n    float g;\n    float k;\n};\n\nstruct NagaExternalTextureParams {\n    row_major float4x4 yuv_conversion_matrix;\n    row_major float3x3 gamut_conversion_matrix;\n    int _pad2_0;\n    NagaExternalTextureTransferFn src_tf;\n    NagaExternalTextureTransferFn dst_tf;\n    float2 sample_transform_0; float2 sample_transform_1; float2 sample_transform_2;\n    float2 load_transform_0; float2 load_transform_1; float2 load_transform_2;\n    uint2 size;\n    uint num_planes;\n    int _end_pad_0;\n};\n\nTexture2D<float4> tex_plane0_: register(t0);\nTexture2D<float4> tex_plane1_: register(t1);\nTexture2D<float4> tex_plane2_: register(t2);\ncbuffer tex_params: register(b3) { NagaExternalTextureParams tex_params; };\nSamplerState nagaSamplerHeap[2048]: register(s0, space0);\nSamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1);\nStructuredBuffer<uint> nagaGroup0SamplerIndexArray : register(t0, space255);\nstatic const SamplerState samp = nagaSamplerHeap[nagaGroup0SamplerIndexArray[0]];\n\nfloat4 nagaTextureSampleBaseClampToEdge(\n    Texture2D<float4> plane0,\n    Texture2D<float4> plane1,\n    Texture2D<float4> plane2,\n    NagaExternalTextureParams params,\n    SamplerState samp,\n    float2 coords)\n{\n    float2 plane0_size;\n    plane0.GetDimensions(plane0_size.x, plane0_size.y);\n    float3x2 sample_transform = float3x2(\n        params.sample_transform_0,\n        params.sample_transform_1,\n        params.sample_transform_2\n    );\n    coords = mul(float3(coords, 1.0), sample_transform);\n    float2 bounds_min = mul(float3(0.0, 0.0, 1.0), sample_transform);\n    float2 bounds_max = mul(float3(1.0, 1.0, 1.0), sample_transform);\n    float4 bounds = float4(min(bounds_min, bounds_max), max(bounds_min, bounds_max));\n    float2 plane0_half_texel = float2(0.5, 0.5) / plane0_size;\n    float2 plane0_coords = clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);\n    if (params.num_planes == 1u) {\n        return plane0.SampleLevel(samp, plane0_coords, 0.0f);\n    } else {\n        float2 plane1_size;\n        plane1.GetDimensions(plane1_size.x, plane1_size.y);\n        float2 plane1_half_texel = float2(0.5, 0.5) / plane1_size;\n        float2 plane1_coords = clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);\n        float y = plane0.SampleLevel(samp, plane0_coords, 0.0f).x;\n        float2 uv;\n        if (params.num_planes == 2u) {\n            uv = plane1.SampleLevel(samp, plane1_coords, 0.0f).xy;\n        } else {\n            float2 plane2_size;\n            plane2.GetDimensions(plane2_size.x, plane2_size.y);\n            float2 plane2_half_texel = float2(0.5, 0.5) / plane2_size;\n            float2 plane2_coords = clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane2_half_texel);\n            uv = float2(plane1.SampleLevel(samp, plane1_coords, 0.0f).x, plane2.SampleLevel(samp, plane2_coords, 0.0f).x);\n        }\n        float3 srcGammaRgb = mul(float4(y, uv, 1.0), params.yuv_conversion_matrix).rgb;\n        float3 srcLinearRgb = srcGammaRgb < params.src_tf.k * params.src_tf.b ?\n            srcGammaRgb / params.src_tf.k :\n            pow((srcGammaRgb + params.src_tf.a - 1.0) / params.src_tf.a, params.src_tf.g);\n        float3 dstLinearRgb = mul(srcLinearRgb, params.gamut_conversion_matrix);\n        float3 dstGammaRgb = dstLinearRgb < params.dst_tf.b ?\n            params.dst_tf.k * dstLinearRgb :\n            params.dst_tf.a * pow(dstLinearRgb, 1.0 / params.dst_tf.g) - (params.dst_tf.a - 1);\n        return float4(dstGammaRgb, 1.0);\n    }\n}\n\nfloat4 nagaTextureLoadExternal(\n    Texture2D<float4> plane0,\n    Texture2D<float4> plane1,\n    Texture2D<float4> plane2,\n    NagaExternalTextureParams params,\n    uint2 coords)\n{\n    uint2 plane0_size;\n    plane0.GetDimensions(plane0_size.x, plane0_size.y);\n    uint2 cropped_size = any(params.size) ? params.size : plane0_size;\n    coords = min(coords, cropped_size - 1);\n    float3x2 load_transform = float3x2(\n        params.load_transform_0,\n        params.load_transform_1,\n        params.load_transform_2\n    );\n    uint2 plane0_coords = uint2(round(mul(float3(coords, 1.0), load_transform)));\n    if (params.num_planes == 1u) {\n        return plane0.Load(uint3(plane0_coords, 0u));\n    } else {\n        uint2 plane1_size;\n        plane1.GetDimensions(plane1_size.x, plane1_size.y);\n        uint2 plane1_coords = uint2(floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));\n        float y = plane0.Load(uint3(plane0_coords, 0u)).x;\n        float2 uv;\n        if (params.num_planes == 2u) {\n            uv = plane1.Load(uint3(plane1_coords, 0u)).xy;\n        } else {\n            uint2 plane2_size;\n            plane2.GetDimensions(plane2_size.x, plane2_size.y);\n            uint2 plane2_coords = uint2(floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));\n            uv = float2(plane1.Load(uint3(plane1_coords, 0u)).x, plane2.Load(uint3(plane2_coords, 0u)).x);\n        }\n        float3 srcGammaRgb = mul(float4(y, uv, 1.0), params.yuv_conversion_matrix).rgb;\n        float3 srcLinearRgb = srcGammaRgb < params.src_tf.k * params.src_tf.b ?\n            srcGammaRgb / params.src_tf.k :\n            pow((srcGammaRgb + params.src_tf.a - 1.0) / params.src_tf.a, params.src_tf.g);\n        float3 dstLinearRgb = mul(srcLinearRgb, params.gamut_conversion_matrix);\n        float3 dstGammaRgb = dstLinearRgb < params.dst_tf.b ?\n            params.dst_tf.k * dstLinearRgb :\n            params.dst_tf.a * pow(dstLinearRgb, 1.0 / params.dst_tf.g) - (params.dst_tf.a - 1);\n        return float4(dstGammaRgb, 1.0);\n    }\n}\n\nuint2 NagaExternalDimensions2D(Texture2D<float4> plane0, Texture2D<float4> plane1, Texture2D<float4> plane2, NagaExternalTextureParams params) {\n    if (any(params.size)) {\n        return params.size;\n    } else {\n        uint2 ret;\n        plane0.GetDimensions(ret.x, ret.y);\n        return ret;\n    }\n}\n\nfloat4 test(Texture2D<float4> t_plane0_, Texture2D<float4> t_plane1_, Texture2D<float4> t_plane2_, NagaExternalTextureParams t_params)\n{\n    float4 a = (float4)0;\n    float4 b = (float4)0;\n    float4 c = (float4)0;\n    uint2 d = (uint2)0;\n\n    float4 _e4 = nagaTextureSampleBaseClampToEdge(t_plane0_, t_plane1_, t_plane2_, t_params, samp, (0.0).xx);\n    a = _e4;\n    float4 _e8 = nagaTextureLoadExternal(t_plane0_, t_plane1_, t_plane2_, t_params, (int(0)).xx);\n    b = _e8;\n    float4 _e12 = nagaTextureLoadExternal(t_plane0_, t_plane1_, t_plane2_, t_params, (0u).xx);\n    c = _e12;\n    d = NagaExternalDimensions2D(t_plane0_, t_plane1_, t_plane2_, t_params);\n    float4 _e16 = a;\n    float4 _e17 = b;\n    float4 _e19 = c;\n    uint2 _e21 = d;\n    return (((_e16 + _e17) + _e19) + float2(_e21).xyxy);\n}\n\nfloat4 fragment_main() : SV_Target0\n{\n    const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params);\n    return _e1;\n}\n\nfloat4 vertex_main() : SV_Position\n{\n    const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params);\n    return _e1;\n}\n\n[numthreads(1, 1, 1)]\nvoid compute_main()\n{\n    const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-texture-external.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vertex_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n        (\n            entry_point:\"fragment_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n        (\n            entry_point:\"compute_main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-type-inference.hlsl",
    "content": "int4 ZeroValueint4() {\n    return (int4)0;\n}\n\nstatic const uint g1_ = 1u;\nstatic const float g3_ = 1.0;\nstatic const int4 g4_ = ZeroValueint4();\nstatic const int4 g5_ = (int(1)).xxxx;\nstatic const float2x2 g6_ = float2x2(float2(0.0, 0.0), float2(0.0, 0.0));\n\n[numthreads(1, 1, 1)]\nvoid main()\n{\n    int g0x = int(1);\n    float g2x = 1.0;\n    float2x2 g7x = float2x2(float2(1.0, 1.0), float2(1.0, 1.0));\n    int c0x = int(1);\n    uint c1x = 1u;\n    float c2x = 1.0;\n    float c3x = 1.0;\n    int4 c4x = ZeroValueint4();\n    int4 c5x = (int(1)).xxxx;\n    float2x2 c6x = float2x2(float2(0.0, 0.0), float2(0.0, 0.0));\n    float2x2 c7x = float2x2(float2(1.0, 1.0), float2(1.0, 1.0));\n    int l0x = (int)0;\n    uint l1x = (uint)0;\n    float l2x = (float)0;\n    float l3x = (float)0;\n    int4 l4x = (int4)0;\n    int v0_ = int(1);\n    uint v1_ = 1u;\n    float v2_ = 1.0;\n    float v3_ = 1.0;\n    int4 v4_ = ZeroValueint4();\n    int4 v5_ = (int(1)).xxxx;\n    float2x2 v6_ = float2x2(float2(0.0, 0.0), float2(0.0, 0.0));\n    float2x2 v7_ = float2x2(float2(1.0, 1.0), float2(1.0, 1.0));\n\n    int4 l5_ = (int(1)).xxxx;\n    float2x2 l6_ = float2x2(float2(0.0, 0.0), float2(0.0, 0.0));\n    float2x2 l7_ = float2x2(float2(1.0, 1.0), float2(1.0, 1.0));\n    l0x = int(1);\n    l1x = 1u;\n    l2x = 1.0;\n    l3x = 1.0;\n    l4x = ZeroValueint4();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-type-inference.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-unconsumed_vertex_outputs_frag.hlsl",
    "content": "struct FragmentIn {\n    float value : LOC1;\n    float value2_ : LOC3;\n    float4 position : SV_Position;\n};\n\nstruct FragmentInput_fs_main {\n    float value : LOC1;\n    float value2_ : LOC3;\n    float4 position : SV_Position;\n};\n\nfloat4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0\n{\n    FragmentIn v_out = { fragmentinput_fs_main.value, fragmentinput_fs_main.value2_, fragmentinput_fs_main.position };\n    return float4(v_out.value, v_out.value, v_out.value2_, v_out.value2_);\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-unconsumed_vertex_outputs_frag.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n        (\n            entry_point:\"fs_main\",\n            target_profile:\"ps_5_1\",\n        ),\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-unconsumed_vertex_outputs_vert.hlsl",
    "content": "struct VertexOut {\n    float4 position : SV_Position;\n    float value : LOC1;\n    float4 unused_value2_ : LOC2;\n    float unused_value : LOC0;\n    float value2_ : LOC3;\n};\n\nstruct VertexOutput_vs_main {\n    float value : LOC1;\n    float value2_ : LOC3;\n    float4 position : SV_Position;\n};\n\nVertexOut ConstructVertexOut(float4 arg0, float arg1, float4 arg2, float arg3, float arg4) {\n    VertexOut ret = (VertexOut)0;\n    ret.position = arg0;\n    ret.value = arg1;\n    ret.unused_value2_ = arg2;\n    ret.unused_value = arg3;\n    ret.value2_ = arg4;\n    return ret;\n}\n\nVertexOutput_vs_main vs_main()\n{\n    const VertexOut vertexout = ConstructVertexOut((1.0).xxxx, 1.0, (2.0).xxxx, 1.0, 0.5);\n    const VertexOutput_vs_main vertexout_1 = { vertexout.value, vertexout.value2_, vertexout.position };\n    return vertexout_1;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-unconsumed_vertex_outputs_vert.ron",
    "content": "(\n    vertex:[\n        (\n            entry_point:\"vs_main\",\n            target_profile:\"vs_5_1\",\n        ),\n    ],\n    fragment:[\n    ],\n    compute:[\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-uniform-load-atomic.hlsl",
    "content": "struct AtomicStruct {\n    uint atomic_scalar;\n    int atomic_arr[2];\n};\n\ngroupshared uint wg_scalar;\ngroupshared int wg_signed;\ngroupshared AtomicStruct wg_struct;\n\n[numthreads(64, 1, 1)]\nvoid test_atomic_workgroup_uniform_load(uint3 workgroup_id : SV_GroupID, uint3 local_id : SV_GroupThreadID)\n{\n    if (all(local_id == uint3(0u, 0u, 0u))) {\n        wg_scalar = (uint)0;\n        wg_signed = (int)0;\n        wg_struct = (AtomicStruct)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    bool local = (bool)0;\n    bool local_1 = (bool)0;\n    bool local_2 = (bool)0;\n\n    uint active_tile_index = (workgroup_id.x + (workgroup_id.y * 32768u));\n    uint _e11; InterlockedOr(wg_scalar, uint((active_tile_index >= 64u)), _e11);\n    int _e14; InterlockedAdd(wg_signed, int(1), _e14);\n    wg_struct.atomic_scalar = 1u;\n    int _e22; InterlockedAdd(wg_struct.atomic_arr[0], int(1), _e22);\n    GroupMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    uint _e24 = wg_scalar;\n    GroupMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    int _e26 = wg_signed;\n    GroupMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    uint _e29 = wg_struct.atomic_scalar;\n    GroupMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    int _e33 = wg_struct.atomic_arr[0];\n    GroupMemoryBarrierWithGroupSync();\n    if ((_e24 == 0u)) {\n        local = (_e26 > int(0));\n    } else {\n        local = false;\n    }\n    bool _e41 = local;\n    if (_e41) {\n        local_1 = (_e29 > 0u);\n    } else {\n        local_1 = false;\n    }\n    bool _e47 = local_1;\n    if (_e47) {\n        local_2 = (_e33 > int(0));\n    } else {\n        local_2 = false;\n    }\n    bool _e53 = local_2;\n    if (_e53) {\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-uniform-load-atomic.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"test_atomic_workgroup_uniform_load\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-uniform-load.hlsl",
    "content": "static const uint SIZE = 128u;\n\ngroupshared int arr_i32_[128];\n\n[numthreads(4, 1, 1)]\nvoid test_workgroupUniformLoad(uint3 workgroup_id : SV_GroupID, uint3 local_invocation_id : SV_GroupThreadID)\n{\n    if (all(local_invocation_id == uint3(0u, 0u, 0u))) {\n        arr_i32_ = (int[128])0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    GroupMemoryBarrierWithGroupSync();\n    int _e4 = arr_i32_[min(uint(workgroup_id.x), 127u)];\n    GroupMemoryBarrierWithGroupSync();\n    if ((_e4 > int(10))) {\n        GroupMemoryBarrierWithGroupSync();\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-uniform-load.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"test_workgroupUniformLoad\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-var-init.hlsl",
    "content": "struct WStruct {\n    uint arr[512];\n    int atom;\n    int atom_arr[8][8];\n};\n\ngroupshared WStruct w_mem;\nRWByteAddressBuffer output : register(u0);\n\n[numthreads(1, 1, 1)]\nvoid main(uint3 local_invocation_id : SV_GroupThreadID)\n{\n    if (all(local_invocation_id == uint3(0u, 0u, 0u))) {\n        w_mem = (WStruct)0;\n    }\n    GroupMemoryBarrierWithGroupSync();\n    uint _e3[512] = w_mem.arr;\n    {\n        uint _value2[512] = _e3;\n        output.Store(0, asuint(_value2[0]));\n        output.Store(4, asuint(_value2[1]));\n        output.Store(8, asuint(_value2[2]));\n        output.Store(12, asuint(_value2[3]));\n        output.Store(16, asuint(_value2[4]));\n        output.Store(20, asuint(_value2[5]));\n        output.Store(24, asuint(_value2[6]));\n        output.Store(28, asuint(_value2[7]));\n        output.Store(32, asuint(_value2[8]));\n        output.Store(36, asuint(_value2[9]));\n        output.Store(40, asuint(_value2[10]));\n        output.Store(44, asuint(_value2[11]));\n        output.Store(48, asuint(_value2[12]));\n        output.Store(52, asuint(_value2[13]));\n        output.Store(56, asuint(_value2[14]));\n        output.Store(60, asuint(_value2[15]));\n        output.Store(64, asuint(_value2[16]));\n        output.Store(68, asuint(_value2[17]));\n        output.Store(72, asuint(_value2[18]));\n        output.Store(76, asuint(_value2[19]));\n        output.Store(80, asuint(_value2[20]));\n        output.Store(84, asuint(_value2[21]));\n        output.Store(88, asuint(_value2[22]));\n        output.Store(92, asuint(_value2[23]));\n        output.Store(96, asuint(_value2[24]));\n        output.Store(100, asuint(_value2[25]));\n        output.Store(104, asuint(_value2[26]));\n        output.Store(108, asuint(_value2[27]));\n        output.Store(112, asuint(_value2[28]));\n        output.Store(116, asuint(_value2[29]));\n        output.Store(120, asuint(_value2[30]));\n        output.Store(124, asuint(_value2[31]));\n        output.Store(128, asuint(_value2[32]));\n        output.Store(132, asuint(_value2[33]));\n        output.Store(136, asuint(_value2[34]));\n        output.Store(140, asuint(_value2[35]));\n        output.Store(144, asuint(_value2[36]));\n        output.Store(148, asuint(_value2[37]));\n        output.Store(152, asuint(_value2[38]));\n        output.Store(156, asuint(_value2[39]));\n        output.Store(160, asuint(_value2[40]));\n        output.Store(164, asuint(_value2[41]));\n        output.Store(168, asuint(_value2[42]));\n        output.Store(172, asuint(_value2[43]));\n        output.Store(176, asuint(_value2[44]));\n        output.Store(180, asuint(_value2[45]));\n        output.Store(184, asuint(_value2[46]));\n        output.Store(188, asuint(_value2[47]));\n        output.Store(192, asuint(_value2[48]));\n        output.Store(196, asuint(_value2[49]));\n        output.Store(200, asuint(_value2[50]));\n        output.Store(204, asuint(_value2[51]));\n        output.Store(208, asuint(_value2[52]));\n        output.Store(212, asuint(_value2[53]));\n        output.Store(216, asuint(_value2[54]));\n        output.Store(220, asuint(_value2[55]));\n        output.Store(224, asuint(_value2[56]));\n        output.Store(228, asuint(_value2[57]));\n        output.Store(232, asuint(_value2[58]));\n        output.Store(236, asuint(_value2[59]));\n        output.Store(240, asuint(_value2[60]));\n        output.Store(244, asuint(_value2[61]));\n        output.Store(248, asuint(_value2[62]));\n        output.Store(252, asuint(_value2[63]));\n        output.Store(256, asuint(_value2[64]));\n        output.Store(260, asuint(_value2[65]));\n        output.Store(264, asuint(_value2[66]));\n        output.Store(268, asuint(_value2[67]));\n        output.Store(272, asuint(_value2[68]));\n        output.Store(276, asuint(_value2[69]));\n        output.Store(280, asuint(_value2[70]));\n        output.Store(284, asuint(_value2[71]));\n        output.Store(288, asuint(_value2[72]));\n        output.Store(292, asuint(_value2[73]));\n        output.Store(296, asuint(_value2[74]));\n        output.Store(300, asuint(_value2[75]));\n        output.Store(304, asuint(_value2[76]));\n        output.Store(308, asuint(_value2[77]));\n        output.Store(312, asuint(_value2[78]));\n        output.Store(316, asuint(_value2[79]));\n        output.Store(320, asuint(_value2[80]));\n        output.Store(324, asuint(_value2[81]));\n        output.Store(328, asuint(_value2[82]));\n        output.Store(332, asuint(_value2[83]));\n        output.Store(336, asuint(_value2[84]));\n        output.Store(340, asuint(_value2[85]));\n        output.Store(344, asuint(_value2[86]));\n        output.Store(348, asuint(_value2[87]));\n        output.Store(352, asuint(_value2[88]));\n        output.Store(356, asuint(_value2[89]));\n        output.Store(360, asuint(_value2[90]));\n        output.Store(364, asuint(_value2[91]));\n        output.Store(368, asuint(_value2[92]));\n        output.Store(372, asuint(_value2[93]));\n        output.Store(376, asuint(_value2[94]));\n        output.Store(380, asuint(_value2[95]));\n        output.Store(384, asuint(_value2[96]));\n        output.Store(388, asuint(_value2[97]));\n        output.Store(392, asuint(_value2[98]));\n        output.Store(396, asuint(_value2[99]));\n        output.Store(400, asuint(_value2[100]));\n        output.Store(404, asuint(_value2[101]));\n        output.Store(408, asuint(_value2[102]));\n        output.Store(412, asuint(_value2[103]));\n        output.Store(416, asuint(_value2[104]));\n        output.Store(420, asuint(_value2[105]));\n        output.Store(424, asuint(_value2[106]));\n        output.Store(428, asuint(_value2[107]));\n        output.Store(432, asuint(_value2[108]));\n        output.Store(436, asuint(_value2[109]));\n        output.Store(440, asuint(_value2[110]));\n        output.Store(444, asuint(_value2[111]));\n        output.Store(448, asuint(_value2[112]));\n        output.Store(452, asuint(_value2[113]));\n        output.Store(456, asuint(_value2[114]));\n        output.Store(460, asuint(_value2[115]));\n        output.Store(464, asuint(_value2[116]));\n        output.Store(468, asuint(_value2[117]));\n        output.Store(472, asuint(_value2[118]));\n        output.Store(476, asuint(_value2[119]));\n        output.Store(480, asuint(_value2[120]));\n        output.Store(484, asuint(_value2[121]));\n        output.Store(488, asuint(_value2[122]));\n        output.Store(492, asuint(_value2[123]));\n        output.Store(496, asuint(_value2[124]));\n        output.Store(500, asuint(_value2[125]));\n        output.Store(504, asuint(_value2[126]));\n        output.Store(508, asuint(_value2[127]));\n        output.Store(512, asuint(_value2[128]));\n        output.Store(516, asuint(_value2[129]));\n        output.Store(520, asuint(_value2[130]));\n        output.Store(524, asuint(_value2[131]));\n        output.Store(528, asuint(_value2[132]));\n        output.Store(532, asuint(_value2[133]));\n        output.Store(536, asuint(_value2[134]));\n        output.Store(540, asuint(_value2[135]));\n        output.Store(544, asuint(_value2[136]));\n        output.Store(548, asuint(_value2[137]));\n        output.Store(552, asuint(_value2[138]));\n        output.Store(556, asuint(_value2[139]));\n        output.Store(560, asuint(_value2[140]));\n        output.Store(564, asuint(_value2[141]));\n        output.Store(568, asuint(_value2[142]));\n        output.Store(572, asuint(_value2[143]));\n        output.Store(576, asuint(_value2[144]));\n        output.Store(580, asuint(_value2[145]));\n        output.Store(584, asuint(_value2[146]));\n        output.Store(588, asuint(_value2[147]));\n        output.Store(592, asuint(_value2[148]));\n        output.Store(596, asuint(_value2[149]));\n        output.Store(600, asuint(_value2[150]));\n        output.Store(604, asuint(_value2[151]));\n        output.Store(608, asuint(_value2[152]));\n        output.Store(612, asuint(_value2[153]));\n        output.Store(616, asuint(_value2[154]));\n        output.Store(620, asuint(_value2[155]));\n        output.Store(624, asuint(_value2[156]));\n        output.Store(628, asuint(_value2[157]));\n        output.Store(632, asuint(_value2[158]));\n        output.Store(636, asuint(_value2[159]));\n        output.Store(640, asuint(_value2[160]));\n        output.Store(644, asuint(_value2[161]));\n        output.Store(648, asuint(_value2[162]));\n        output.Store(652, asuint(_value2[163]));\n        output.Store(656, asuint(_value2[164]));\n        output.Store(660, asuint(_value2[165]));\n        output.Store(664, asuint(_value2[166]));\n        output.Store(668, asuint(_value2[167]));\n        output.Store(672, asuint(_value2[168]));\n        output.Store(676, asuint(_value2[169]));\n        output.Store(680, asuint(_value2[170]));\n        output.Store(684, asuint(_value2[171]));\n        output.Store(688, asuint(_value2[172]));\n        output.Store(692, asuint(_value2[173]));\n        output.Store(696, asuint(_value2[174]));\n        output.Store(700, asuint(_value2[175]));\n        output.Store(704, asuint(_value2[176]));\n        output.Store(708, asuint(_value2[177]));\n        output.Store(712, asuint(_value2[178]));\n        output.Store(716, asuint(_value2[179]));\n        output.Store(720, asuint(_value2[180]));\n        output.Store(724, asuint(_value2[181]));\n        output.Store(728, asuint(_value2[182]));\n        output.Store(732, asuint(_value2[183]));\n        output.Store(736, asuint(_value2[184]));\n        output.Store(740, asuint(_value2[185]));\n        output.Store(744, asuint(_value2[186]));\n        output.Store(748, asuint(_value2[187]));\n        output.Store(752, asuint(_value2[188]));\n        output.Store(756, asuint(_value2[189]));\n        output.Store(760, asuint(_value2[190]));\n        output.Store(764, asuint(_value2[191]));\n        output.Store(768, asuint(_value2[192]));\n        output.Store(772, asuint(_value2[193]));\n        output.Store(776, asuint(_value2[194]));\n        output.Store(780, asuint(_value2[195]));\n        output.Store(784, asuint(_value2[196]));\n        output.Store(788, asuint(_value2[197]));\n        output.Store(792, asuint(_value2[198]));\n        output.Store(796, asuint(_value2[199]));\n        output.Store(800, asuint(_value2[200]));\n        output.Store(804, asuint(_value2[201]));\n        output.Store(808, asuint(_value2[202]));\n        output.Store(812, asuint(_value2[203]));\n        output.Store(816, asuint(_value2[204]));\n        output.Store(820, asuint(_value2[205]));\n        output.Store(824, asuint(_value2[206]));\n        output.Store(828, asuint(_value2[207]));\n        output.Store(832, asuint(_value2[208]));\n        output.Store(836, asuint(_value2[209]));\n        output.Store(840, asuint(_value2[210]));\n        output.Store(844, asuint(_value2[211]));\n        output.Store(848, asuint(_value2[212]));\n        output.Store(852, asuint(_value2[213]));\n        output.Store(856, asuint(_value2[214]));\n        output.Store(860, asuint(_value2[215]));\n        output.Store(864, asuint(_value2[216]));\n        output.Store(868, asuint(_value2[217]));\n        output.Store(872, asuint(_value2[218]));\n        output.Store(876, asuint(_value2[219]));\n        output.Store(880, asuint(_value2[220]));\n        output.Store(884, asuint(_value2[221]));\n        output.Store(888, asuint(_value2[222]));\n        output.Store(892, asuint(_value2[223]));\n        output.Store(896, asuint(_value2[224]));\n        output.Store(900, asuint(_value2[225]));\n        output.Store(904, asuint(_value2[226]));\n        output.Store(908, asuint(_value2[227]));\n        output.Store(912, asuint(_value2[228]));\n        output.Store(916, asuint(_value2[229]));\n        output.Store(920, asuint(_value2[230]));\n        output.Store(924, asuint(_value2[231]));\n        output.Store(928, asuint(_value2[232]));\n        output.Store(932, asuint(_value2[233]));\n        output.Store(936, asuint(_value2[234]));\n        output.Store(940, asuint(_value2[235]));\n        output.Store(944, asuint(_value2[236]));\n        output.Store(948, asuint(_value2[237]));\n        output.Store(952, asuint(_value2[238]));\n        output.Store(956, asuint(_value2[239]));\n        output.Store(960, asuint(_value2[240]));\n        output.Store(964, asuint(_value2[241]));\n        output.Store(968, asuint(_value2[242]));\n        output.Store(972, asuint(_value2[243]));\n        output.Store(976, asuint(_value2[244]));\n        output.Store(980, asuint(_value2[245]));\n        output.Store(984, asuint(_value2[246]));\n        output.Store(988, asuint(_value2[247]));\n        output.Store(992, asuint(_value2[248]));\n        output.Store(996, asuint(_value2[249]));\n        output.Store(1000, asuint(_value2[250]));\n        output.Store(1004, asuint(_value2[251]));\n        output.Store(1008, asuint(_value2[252]));\n        output.Store(1012, asuint(_value2[253]));\n        output.Store(1016, asuint(_value2[254]));\n        output.Store(1020, asuint(_value2[255]));\n        output.Store(1024, asuint(_value2[256]));\n        output.Store(1028, asuint(_value2[257]));\n        output.Store(1032, asuint(_value2[258]));\n        output.Store(1036, asuint(_value2[259]));\n        output.Store(1040, asuint(_value2[260]));\n        output.Store(1044, asuint(_value2[261]));\n        output.Store(1048, asuint(_value2[262]));\n        output.Store(1052, asuint(_value2[263]));\n        output.Store(1056, asuint(_value2[264]));\n        output.Store(1060, asuint(_value2[265]));\n        output.Store(1064, asuint(_value2[266]));\n        output.Store(1068, asuint(_value2[267]));\n        output.Store(1072, asuint(_value2[268]));\n        output.Store(1076, asuint(_value2[269]));\n        output.Store(1080, asuint(_value2[270]));\n        output.Store(1084, asuint(_value2[271]));\n        output.Store(1088, asuint(_value2[272]));\n        output.Store(1092, asuint(_value2[273]));\n        output.Store(1096, asuint(_value2[274]));\n        output.Store(1100, asuint(_value2[275]));\n        output.Store(1104, asuint(_value2[276]));\n        output.Store(1108, asuint(_value2[277]));\n        output.Store(1112, asuint(_value2[278]));\n        output.Store(1116, asuint(_value2[279]));\n        output.Store(1120, asuint(_value2[280]));\n        output.Store(1124, asuint(_value2[281]));\n        output.Store(1128, asuint(_value2[282]));\n        output.Store(1132, asuint(_value2[283]));\n        output.Store(1136, asuint(_value2[284]));\n        output.Store(1140, asuint(_value2[285]));\n        output.Store(1144, asuint(_value2[286]));\n        output.Store(1148, asuint(_value2[287]));\n        output.Store(1152, asuint(_value2[288]));\n        output.Store(1156, asuint(_value2[289]));\n        output.Store(1160, asuint(_value2[290]));\n        output.Store(1164, asuint(_value2[291]));\n        output.Store(1168, asuint(_value2[292]));\n        output.Store(1172, asuint(_value2[293]));\n        output.Store(1176, asuint(_value2[294]));\n        output.Store(1180, asuint(_value2[295]));\n        output.Store(1184, asuint(_value2[296]));\n        output.Store(1188, asuint(_value2[297]));\n        output.Store(1192, asuint(_value2[298]));\n        output.Store(1196, asuint(_value2[299]));\n        output.Store(1200, asuint(_value2[300]));\n        output.Store(1204, asuint(_value2[301]));\n        output.Store(1208, asuint(_value2[302]));\n        output.Store(1212, asuint(_value2[303]));\n        output.Store(1216, asuint(_value2[304]));\n        output.Store(1220, asuint(_value2[305]));\n        output.Store(1224, asuint(_value2[306]));\n        output.Store(1228, asuint(_value2[307]));\n        output.Store(1232, asuint(_value2[308]));\n        output.Store(1236, asuint(_value2[309]));\n        output.Store(1240, asuint(_value2[310]));\n        output.Store(1244, asuint(_value2[311]));\n        output.Store(1248, asuint(_value2[312]));\n        output.Store(1252, asuint(_value2[313]));\n        output.Store(1256, asuint(_value2[314]));\n        output.Store(1260, asuint(_value2[315]));\n        output.Store(1264, asuint(_value2[316]));\n        output.Store(1268, asuint(_value2[317]));\n        output.Store(1272, asuint(_value2[318]));\n        output.Store(1276, asuint(_value2[319]));\n        output.Store(1280, asuint(_value2[320]));\n        output.Store(1284, asuint(_value2[321]));\n        output.Store(1288, asuint(_value2[322]));\n        output.Store(1292, asuint(_value2[323]));\n        output.Store(1296, asuint(_value2[324]));\n        output.Store(1300, asuint(_value2[325]));\n        output.Store(1304, asuint(_value2[326]));\n        output.Store(1308, asuint(_value2[327]));\n        output.Store(1312, asuint(_value2[328]));\n        output.Store(1316, asuint(_value2[329]));\n        output.Store(1320, asuint(_value2[330]));\n        output.Store(1324, asuint(_value2[331]));\n        output.Store(1328, asuint(_value2[332]));\n        output.Store(1332, asuint(_value2[333]));\n        output.Store(1336, asuint(_value2[334]));\n        output.Store(1340, asuint(_value2[335]));\n        output.Store(1344, asuint(_value2[336]));\n        output.Store(1348, asuint(_value2[337]));\n        output.Store(1352, asuint(_value2[338]));\n        output.Store(1356, asuint(_value2[339]));\n        output.Store(1360, asuint(_value2[340]));\n        output.Store(1364, asuint(_value2[341]));\n        output.Store(1368, asuint(_value2[342]));\n        output.Store(1372, asuint(_value2[343]));\n        output.Store(1376, asuint(_value2[344]));\n        output.Store(1380, asuint(_value2[345]));\n        output.Store(1384, asuint(_value2[346]));\n        output.Store(1388, asuint(_value2[347]));\n        output.Store(1392, asuint(_value2[348]));\n        output.Store(1396, asuint(_value2[349]));\n        output.Store(1400, asuint(_value2[350]));\n        output.Store(1404, asuint(_value2[351]));\n        output.Store(1408, asuint(_value2[352]));\n        output.Store(1412, asuint(_value2[353]));\n        output.Store(1416, asuint(_value2[354]));\n        output.Store(1420, asuint(_value2[355]));\n        output.Store(1424, asuint(_value2[356]));\n        output.Store(1428, asuint(_value2[357]));\n        output.Store(1432, asuint(_value2[358]));\n        output.Store(1436, asuint(_value2[359]));\n        output.Store(1440, asuint(_value2[360]));\n        output.Store(1444, asuint(_value2[361]));\n        output.Store(1448, asuint(_value2[362]));\n        output.Store(1452, asuint(_value2[363]));\n        output.Store(1456, asuint(_value2[364]));\n        output.Store(1460, asuint(_value2[365]));\n        output.Store(1464, asuint(_value2[366]));\n        output.Store(1468, asuint(_value2[367]));\n        output.Store(1472, asuint(_value2[368]));\n        output.Store(1476, asuint(_value2[369]));\n        output.Store(1480, asuint(_value2[370]));\n        output.Store(1484, asuint(_value2[371]));\n        output.Store(1488, asuint(_value2[372]));\n        output.Store(1492, asuint(_value2[373]));\n        output.Store(1496, asuint(_value2[374]));\n        output.Store(1500, asuint(_value2[375]));\n        output.Store(1504, asuint(_value2[376]));\n        output.Store(1508, asuint(_value2[377]));\n        output.Store(1512, asuint(_value2[378]));\n        output.Store(1516, asuint(_value2[379]));\n        output.Store(1520, asuint(_value2[380]));\n        output.Store(1524, asuint(_value2[381]));\n        output.Store(1528, asuint(_value2[382]));\n        output.Store(1532, asuint(_value2[383]));\n        output.Store(1536, asuint(_value2[384]));\n        output.Store(1540, asuint(_value2[385]));\n        output.Store(1544, asuint(_value2[386]));\n        output.Store(1548, asuint(_value2[387]));\n        output.Store(1552, asuint(_value2[388]));\n        output.Store(1556, asuint(_value2[389]));\n        output.Store(1560, asuint(_value2[390]));\n        output.Store(1564, asuint(_value2[391]));\n        output.Store(1568, asuint(_value2[392]));\n        output.Store(1572, asuint(_value2[393]));\n        output.Store(1576, asuint(_value2[394]));\n        output.Store(1580, asuint(_value2[395]));\n        output.Store(1584, asuint(_value2[396]));\n        output.Store(1588, asuint(_value2[397]));\n        output.Store(1592, asuint(_value2[398]));\n        output.Store(1596, asuint(_value2[399]));\n        output.Store(1600, asuint(_value2[400]));\n        output.Store(1604, asuint(_value2[401]));\n        output.Store(1608, asuint(_value2[402]));\n        output.Store(1612, asuint(_value2[403]));\n        output.Store(1616, asuint(_value2[404]));\n        output.Store(1620, asuint(_value2[405]));\n        output.Store(1624, asuint(_value2[406]));\n        output.Store(1628, asuint(_value2[407]));\n        output.Store(1632, asuint(_value2[408]));\n        output.Store(1636, asuint(_value2[409]));\n        output.Store(1640, asuint(_value2[410]));\n        output.Store(1644, asuint(_value2[411]));\n        output.Store(1648, asuint(_value2[412]));\n        output.Store(1652, asuint(_value2[413]));\n        output.Store(1656, asuint(_value2[414]));\n        output.Store(1660, asuint(_value2[415]));\n        output.Store(1664, asuint(_value2[416]));\n        output.Store(1668, asuint(_value2[417]));\n        output.Store(1672, asuint(_value2[418]));\n        output.Store(1676, asuint(_value2[419]));\n        output.Store(1680, asuint(_value2[420]));\n        output.Store(1684, asuint(_value2[421]));\n        output.Store(1688, asuint(_value2[422]));\n        output.Store(1692, asuint(_value2[423]));\n        output.Store(1696, asuint(_value2[424]));\n        output.Store(1700, asuint(_value2[425]));\n        output.Store(1704, asuint(_value2[426]));\n        output.Store(1708, asuint(_value2[427]));\n        output.Store(1712, asuint(_value2[428]));\n        output.Store(1716, asuint(_value2[429]));\n        output.Store(1720, asuint(_value2[430]));\n        output.Store(1724, asuint(_value2[431]));\n        output.Store(1728, asuint(_value2[432]));\n        output.Store(1732, asuint(_value2[433]));\n        output.Store(1736, asuint(_value2[434]));\n        output.Store(1740, asuint(_value2[435]));\n        output.Store(1744, asuint(_value2[436]));\n        output.Store(1748, asuint(_value2[437]));\n        output.Store(1752, asuint(_value2[438]));\n        output.Store(1756, asuint(_value2[439]));\n        output.Store(1760, asuint(_value2[440]));\n        output.Store(1764, asuint(_value2[441]));\n        output.Store(1768, asuint(_value2[442]));\n        output.Store(1772, asuint(_value2[443]));\n        output.Store(1776, asuint(_value2[444]));\n        output.Store(1780, asuint(_value2[445]));\n        output.Store(1784, asuint(_value2[446]));\n        output.Store(1788, asuint(_value2[447]));\n        output.Store(1792, asuint(_value2[448]));\n        output.Store(1796, asuint(_value2[449]));\n        output.Store(1800, asuint(_value2[450]));\n        output.Store(1804, asuint(_value2[451]));\n        output.Store(1808, asuint(_value2[452]));\n        output.Store(1812, asuint(_value2[453]));\n        output.Store(1816, asuint(_value2[454]));\n        output.Store(1820, asuint(_value2[455]));\n        output.Store(1824, asuint(_value2[456]));\n        output.Store(1828, asuint(_value2[457]));\n        output.Store(1832, asuint(_value2[458]));\n        output.Store(1836, asuint(_value2[459]));\n        output.Store(1840, asuint(_value2[460]));\n        output.Store(1844, asuint(_value2[461]));\n        output.Store(1848, asuint(_value2[462]));\n        output.Store(1852, asuint(_value2[463]));\n        output.Store(1856, asuint(_value2[464]));\n        output.Store(1860, asuint(_value2[465]));\n        output.Store(1864, asuint(_value2[466]));\n        output.Store(1868, asuint(_value2[467]));\n        output.Store(1872, asuint(_value2[468]));\n        output.Store(1876, asuint(_value2[469]));\n        output.Store(1880, asuint(_value2[470]));\n        output.Store(1884, asuint(_value2[471]));\n        output.Store(1888, asuint(_value2[472]));\n        output.Store(1892, asuint(_value2[473]));\n        output.Store(1896, asuint(_value2[474]));\n        output.Store(1900, asuint(_value2[475]));\n        output.Store(1904, asuint(_value2[476]));\n        output.Store(1908, asuint(_value2[477]));\n        output.Store(1912, asuint(_value2[478]));\n        output.Store(1916, asuint(_value2[479]));\n        output.Store(1920, asuint(_value2[480]));\n        output.Store(1924, asuint(_value2[481]));\n        output.Store(1928, asuint(_value2[482]));\n        output.Store(1932, asuint(_value2[483]));\n        output.Store(1936, asuint(_value2[484]));\n        output.Store(1940, asuint(_value2[485]));\n        output.Store(1944, asuint(_value2[486]));\n        output.Store(1948, asuint(_value2[487]));\n        output.Store(1952, asuint(_value2[488]));\n        output.Store(1956, asuint(_value2[489]));\n        output.Store(1960, asuint(_value2[490]));\n        output.Store(1964, asuint(_value2[491]));\n        output.Store(1968, asuint(_value2[492]));\n        output.Store(1972, asuint(_value2[493]));\n        output.Store(1976, asuint(_value2[494]));\n        output.Store(1980, asuint(_value2[495]));\n        output.Store(1984, asuint(_value2[496]));\n        output.Store(1988, asuint(_value2[497]));\n        output.Store(1992, asuint(_value2[498]));\n        output.Store(1996, asuint(_value2[499]));\n        output.Store(2000, asuint(_value2[500]));\n        output.Store(2004, asuint(_value2[501]));\n        output.Store(2008, asuint(_value2[502]));\n        output.Store(2012, asuint(_value2[503]));\n        output.Store(2016, asuint(_value2[504]));\n        output.Store(2020, asuint(_value2[505]));\n        output.Store(2024, asuint(_value2[506]));\n        output.Store(2028, asuint(_value2[507]));\n        output.Store(2032, asuint(_value2[508]));\n        output.Store(2036, asuint(_value2[509]));\n        output.Store(2040, asuint(_value2[510]));\n        output.Store(2044, asuint(_value2[511]));\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/hlsl/wgsl-workgroup-var-init.ron",
    "content": "(\n    vertex:[\n    ],\n    fragment:[\n    ],\n    compute:[\n        (\n            entry_point:\"main\",\n            target_profile:\"cs_5_1\",\n        ),\n    ],\n)\n"
  },
  {
    "path": "naga/tests/out/ir/spv-fetch_depth.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 3,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Depth(\n                    multi: false,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 1,\n            init: 0,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: None,\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: None,\n            space: Storage(\n                access: (\"LOAD\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 4,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: None,\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(I32(0)),\n    ],\n    functions: [\n        (\n            name: None,\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [\n                GlobalVariable(2),\n                GlobalVariable(0),\n                GlobalVariable(1),\n                Constant(0),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 5,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 6,\n                    array_index: None,\n                    sample: None,\n                    level: Some(3),\n                ),\n                Splat(\n                    size: Quad,\n                    value: 7,\n                ),\n                AccessIndex(\n                    base: 8,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 4,\n                    end: 10,\n                )),\n                Store(\n                    pointer: 4,\n                    value: 9,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"cull::fetch_depth\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (32, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"cull::fetch_depth_wrap\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/spv-fetch_depth.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 4,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 6,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 7,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Depth(\n                    multi: false,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 1,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 6,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 0,\n            init: 0,\n        ),\n        (\n            name: None,\n            ty: 3,\n            init: 1,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: None,\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 4,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: None,\n            space: Storage(\n                access: (\"LOAD\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 7,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: None,\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 10,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(U32(0)),\n        Literal(I32(0)),\n    ],\n    functions: [\n        (\n            name: None,\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [\n                GlobalVariable(2),\n                GlobalVariable(0),\n                GlobalVariable(1),\n                Constant(0),\n                Constant(1),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 6,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 7,\n                    array_index: None,\n                    sample: None,\n                    level: Some(4),\n                ),\n                Splat(\n                    size: Quad,\n                    value: 8,\n                ),\n                AccessIndex(\n                    base: 9,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 5,\n                    end: 11,\n                )),\n                Store(\n                    pointer: 5,\n                    value: 10,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"cull::fetch_depth\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (32, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"cull::fetch_depth_wrap\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/spv-shadow.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: true,\n                class: Depth(\n                    multi: false,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Globals\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"num_lights\"),\n                        ty: 7,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Light\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"proj\"),\n                        ty: 9,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"pos\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"color\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 80,\n                    ),\n                ],\n                span: 96,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 10,\n                size: Dynamic,\n                stride: 96,\n            ),\n        ),\n        (\n            name: Some(\"Lights\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"data\"),\n                        ty: 11,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 96,\n            ),\n        ),\n        (\n            name: None,\n            inner: Sampler(\n                comparison: true,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 0,\n            init: 0,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 1,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 2,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 3,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 4,\n        ),\n        (\n            name: None,\n            ty: 1,\n            init: 8,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 9,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 10,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 11,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 12,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 13,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 14,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 15,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 16,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 17,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 18,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 19,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 20,\n        ),\n        (\n            name: None,\n            ty: 6,\n            init: 21,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"t_shadow\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"sampler_shadow\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 3,\n            )),\n            ty: 13,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"u_globals\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 8,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_lights\"),\n            space: Storage(\n                access: (\"LOAD\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 12,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"in_position_fs\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"in_normal_fs\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"out_color_fs\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Literal(F32(0.5)),\n        Literal(F32(-0.5)),\n        Literal(F32(0.05)),\n        Constant(4),\n        Constant(4),\n        Constant(4),\n        Compose(\n            ty: 1,\n            components: [\n                5,\n                6,\n                7,\n            ],\n        ),\n        Literal(U32(10)),\n        Literal(U32(0)),\n        Literal(U32(1)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(2)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(2)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(2)),\n    ],\n    functions: [\n        (\n            name: None,\n            arguments: [\n                (\n                    name: None,\n                    ty: 2,\n                    binding: None,\n                ),\n                (\n                    name: None,\n                    ty: 3,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                GlobalVariable(1),\n                GlobalVariable(0),\n                Constant(1),\n                Constant(2),\n                Constant(3),\n                Constant(0),\n                FunctionArgument(0),\n                FunctionArgument(1),\n                AccessIndex(\n                    base: 7,\n                    index: 3,\n                ),\n                Binary(\n                    op: LessEqual,\n                    left: 8,\n                    right: 5,\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 4,\n                    components: [\n                        10,\n                        11,\n                    ],\n                ),\n                Compose(\n                    ty: 4,\n                    components: [\n                        3,\n                        4,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 12,\n                    right: 13,\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 3,\n                ),\n                Binary(\n                    op: Divide,\n                    left: 2,\n                    right: 15,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 14,\n                    right: 16,\n                ),\n                Splat(\n                    size: Bi,\n                    value: 3,\n                ),\n                Binary(\n                    op: Add,\n                    left: 17,\n                    right: 18,\n                ),\n                AccessIndex(\n                    base: 19,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 19,\n                    index: 1,\n                ),\n                As(\n                    expr: 6,\n                    kind: Sint,\n                    convert: None,\n                ),\n                As(\n                    expr: 22,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        20,\n                        21,\n                        23,\n                    ],\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 2,\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 3,\n                ),\n                Binary(\n                    op: Divide,\n                    left: 2,\n                    right: 26,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 25,\n                    right: 27,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 4,\n                    components: [\n                        29,\n                        30,\n                    ],\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 2,\n                ),\n                As(\n                    expr: 32,\n                    kind: Sint,\n                    convert: Some(4),\n                ),\n                ImageSample(\n                    image: 1,\n                    sampler: 0,\n                    gather: None,\n                    coordinate: 31,\n                    array_index: Some(33),\n                    offset: None,\n                    level: Zero,\n                    depth_ref: Some(28),\n                    clamp_to_edge: false,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 8,\n                    end: 10,\n                )),\n                If(\n                    condition: 9,\n                    accept: [\n                        Return(\n                            value: Some(2),\n                        ),\n                    ],\n                    reject: [],\n                ),\n                Emit((\n                    start: 10,\n                    end: 35,\n                )),\n                Return(\n                    value: Some(34),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fs_main\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"color\"),\n                    ty: 1,\n                    init: Some(19),\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 2,\n                    init: Some(21),\n                ),\n            ],\n            expressions: [\n                GlobalVariable(3),\n                GlobalVariable(5),\n                GlobalVariable(6),\n                GlobalVariable(4),\n                GlobalVariable(2),\n                Constant(10),\n                Constant(17),\n                Constant(8),\n                Constant(1),\n                Constant(13),\n                Constant(11),\n                Constant(18),\n                Constant(6),\n                Constant(15),\n                Constant(12),\n                Constant(9),\n                Constant(0),\n                Constant(14),\n                Constant(16),\n                Constant(5),\n                LocalVariable(0),\n                Constant(7),\n                LocalVariable(1),\n                Load(\n                    pointer: 22,\n                ),\n                AccessIndex(\n                    base: 4,\n                    index: 0,\n                ),\n                Access(\n                    base: 24,\n                    index: 15,\n                ),\n                Load(\n                    pointer: 25,\n                ),\n                Math(\n                    fun: Min,\n                    arg: 26,\n                    arg1: Some(12),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Binary(\n                    op: GreaterEqual,\n                    left: 23,\n                    right: 27,\n                ),\n                Load(\n                    pointer: 20,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 31,\n                    index: 32,\n                ),\n                AccessIndex(\n                    base: 33,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 34,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 35,\n                    right: 36,\n                ),\n                CallResult(0),\n                Load(\n                    pointer: 1,\n                ),\n                Math(\n                    fun: Normalize,\n                    arg: 39,\n                    arg1: None,\n                    arg2: None,\n                    arg3: None,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 41,\n                    index: 42,\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 1,\n                ),\n                Access(\n                    base: 44,\n                    index: 5,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 47,\n                    index: 48,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 1,\n                ),\n                Access(\n                    base: 50,\n                    index: 10,\n                ),\n                Load(\n                    pointer: 51,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 53,\n                    index: 54,\n                ),\n                AccessIndex(\n                    base: 55,\n                    index: 1,\n                ),\n                Access(\n                    base: 56,\n                    index: 14,\n                ),\n                Load(\n                    pointer: 57,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        46,\n                        52,\n                        58,\n                    ],\n                ),\n                Access(\n                    base: 3,\n                    index: 9,\n                ),\n                Load(\n                    pointer: 60,\n                ),\n                Access(\n                    base: 3,\n                    index: 17,\n                ),\n                Load(\n                    pointer: 62,\n                ),\n                Access(\n                    base: 3,\n                    index: 13,\n                ),\n                Load(\n                    pointer: 64,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        61,\n                        63,\n                        65,\n                    ],\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 59,\n                    right: 66,\n                ),\n                Math(\n                    fun: Normalize,\n                    arg: 67,\n                    arg1: None,\n                    arg2: None,\n                    arg3: None,\n                ),\n                Math(\n                    fun: Dot,\n                    arg: 40,\n                    arg1: Some(68),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Math(\n                    fun: Max,\n                    arg: 16,\n                    arg1: Some(69),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 38,\n                    right: 70,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 72,\n                    index: 73,\n                ),\n                AccessIndex(\n                    base: 74,\n                    index: 2,\n                ),\n                Access(\n                    base: 75,\n                    index: 18,\n                ),\n                Load(\n                    pointer: 76,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 78,\n                    index: 79,\n                ),\n                AccessIndex(\n                    base: 80,\n                    index: 2,\n                ),\n                Access(\n                    base: 81,\n                    index: 6,\n                ),\n                Load(\n                    pointer: 82,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Access(\n                    base: 84,\n                    index: 85,\n                ),\n                AccessIndex(\n                    base: 86,\n                    index: 2,\n                ),\n                Access(\n                    base: 87,\n                    index: 11,\n                ),\n                Load(\n                    pointer: 88,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        77,\n                        83,\n                        89,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 90,\n                    right: 71,\n                ),\n                Binary(\n                    op: Add,\n                    left: 29,\n                    right: 91,\n                ),\n                Load(\n                    pointer: 22,\n                ),\n                Binary(\n                    op: Add,\n                    left: 93,\n                    right: 7,\n                ),\n                Load(\n                    pointer: 20,\n                ),\n                Compose(\n                    ty: 3,\n                    components: [\n                        95,\n                        8,\n                    ],\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Loop(\n                    body: [\n                        Emit((\n                            start: 23,\n                            end: 29,\n                        )),\n                        If(\n                            condition: 28,\n                            accept: [\n                                Break,\n                            ],\n                            reject: [],\n                        ),\n                        Emit((\n                            start: 29,\n                            end: 38,\n                        )),\n                        Call(\n                            function: 0,\n                            arguments: [\n                                30,\n                                37,\n                            ],\n                            result: Some(38),\n                        ),\n                        Emit((\n                            start: 39,\n                            end: 93,\n                        )),\n                        Store(\n                            pointer: 20,\n                            value: 92,\n                        ),\n                        Continue,\n                    ],\n                    continuing: [\n                        Emit((\n                            start: 93,\n                            end: 95,\n                        )),\n                        Store(\n                            pointer: 22,\n                            value: 94,\n                        ),\n                    ],\n                    break_if: None,\n                ),\n                Emit((\n                    start: 95,\n                    end: 97,\n                )),\n                Store(\n                    pointer: 2,\n                    value: 96,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"fs_main\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"fs_main_wrap\"),\n                arguments: [\n                    (\n                        name: Some(\"in_normal_fs\"),\n                        ty: 1,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"in_position_fs\"),\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 1,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                ],\n                result: Some((\n                    ty: 3,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: None,\n                        sampling: None,\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(5),\n                    FunctionArgument(1),\n                    GlobalVariable(4),\n                    GlobalVariable(6),\n                    Load(\n                        pointer: 4,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Store(\n                        pointer: 1,\n                        value: 0,\n                    ),\n                    Store(\n                        pointer: 3,\n                        value: 2,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 5,\n                        end: 6,\n                    )),\n                    Return(\n                        value: Some(5),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/spv-shadow.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: true,\n                class: Depth(\n                    multi: false,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Sampler(\n                comparison: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 1,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 2,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Globals\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"num_lights\"),\n                        ty: 11,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 12,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 11,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 2,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Light\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"proj\"),\n                        ty: 16,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"pos\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"color\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 80,\n                    ),\n                ],\n                span: 96,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 17,\n                size: Dynamic,\n                stride: 96,\n            ),\n        ),\n        (\n            name: Some(\"Lights\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"data\"),\n                        ty: 18,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 96,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 19,\n                space: Storage(\n                    access: (\"\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 18,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 17,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 16,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 3,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 1,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 3,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 0,\n                space: Storage(\n                    access: (\"LOAD | STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 0,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Sampler(\n                comparison: true,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 0,\n            init: 0,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 1,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 2,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 3,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 4,\n        ),\n        (\n            name: None,\n            ty: 1,\n            init: 8,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 9,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 10,\n        ),\n        (\n            name: None,\n            ty: 2,\n            init: 11,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 12,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 13,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 14,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 15,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 16,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 17,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 18,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 19,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 20,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 21,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 22,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 23,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 24,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 25,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 26,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 27,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 28,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 29,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 30,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 31,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 32,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 33,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 34,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 35,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 36,\n        ),\n        (\n            name: None,\n            ty: 8,\n            init: 37,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"t_shadow\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 6,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"sampler_shadow\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 3,\n            )),\n            ty: 29,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"u_globals\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 12,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_lights\"),\n            space: Storage(\n                access: (\"LOAD\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 19,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"in_position_fs\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"in_normal_fs\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"out_color_fs\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Literal(F32(0.5)),\n        Literal(F32(-0.5)),\n        Literal(F32(0.05)),\n        Constant(4),\n        Constant(4),\n        Constant(4),\n        Compose(\n            ty: 1,\n            components: [\n                5,\n                6,\n                7,\n            ],\n        ),\n        Literal(U32(10)),\n        Literal(U32(0)),\n        Literal(U32(1)),\n        Literal(F32(0.0)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(1)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(2)),\n        Literal(I32(0)),\n        Literal(I32(1)),\n        Literal(I32(2)),\n        Literal(I32(0)),\n        Literal(I32(2)),\n        Literal(I32(0)),\n        Literal(I32(0)),\n        Literal(I32(2)),\n        Literal(I32(1)),\n        Literal(I32(0)),\n        Literal(I32(2)),\n        Literal(I32(2)),\n    ],\n    functions: [\n        (\n            name: None,\n            arguments: [\n                (\n                    name: None,\n                    ty: 2,\n                    binding: None,\n                ),\n                (\n                    name: None,\n                    ty: 3,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                GlobalVariable(3),\n                GlobalVariable(5),\n                GlobalVariable(6),\n                GlobalVariable(1),\n                GlobalVariable(4),\n                GlobalVariable(0),\n                GlobalVariable(2),\n                Constant(16),\n                Constant(13),\n                Constant(12),\n                Constant(10),\n                Constant(31),\n                Constant(8),\n                Constant(27),\n                Constant(4),\n                Constant(1),\n                Constant(23),\n                Constant(26),\n                Constant(19),\n                Constant(15),\n                Constant(14),\n                Constant(34),\n                Constant(30),\n                Constant(29),\n                Constant(6),\n                Constant(2),\n                Constant(25),\n                Constant(22),\n                Constant(18),\n                Constant(17),\n                Constant(11),\n                Constant(9),\n                Constant(32),\n                Constant(33),\n                Constant(7),\n                Constant(5),\n                Constant(3),\n                Constant(0),\n                Constant(24),\n                Constant(21),\n                Constant(20),\n                Constant(28),\n                FunctionArgument(0),\n                FunctionArgument(1),\n                AccessIndex(\n                    base: 43,\n                    index: 3,\n                ),\n                Binary(\n                    op: LessEqual,\n                    left: 44,\n                    right: 37,\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        46,\n                        47,\n                    ],\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        25,\n                        36,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 48,\n                    right: 49,\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 3,\n                ),\n                Binary(\n                    op: Divide,\n                    left: 15,\n                    right: 51,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 50,\n                    right: 52,\n                ),\n                Splat(\n                    size: Bi,\n                    value: 25,\n                ),\n                Binary(\n                    op: Add,\n                    left: 53,\n                    right: 54,\n                ),\n                AccessIndex(\n                    base: 55,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 55,\n                    index: 1,\n                ),\n                As(\n                    expr: 42,\n                    kind: Sint,\n                    convert: None,\n                ),\n                As(\n                    expr: 58,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        56,\n                        57,\n                        59,\n                    ],\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 2,\n                ),\n                AccessIndex(\n                    base: 43,\n                    index: 3,\n                ),\n                Binary(\n                    op: Divide,\n                    left: 15,\n                    right: 62,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 61,\n                    right: 63,\n                ),\n                AccessIndex(\n                    base: 60,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 60,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        65,\n                        66,\n                    ],\n                ),\n                AccessIndex(\n                    base: 60,\n                    index: 2,\n                ),\n                As(\n                    expr: 68,\n                    kind: Sint,\n                    convert: Some(4),\n                ),\n                ImageSample(\n                    image: 5,\n                    sampler: 3,\n                    gather: None,\n                    coordinate: 67,\n                    array_index: Some(69),\n                    offset: None,\n                    level: Zero,\n                    depth_ref: Some(64),\n                    clamp_to_edge: false,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 44,\n                    end: 46,\n                )),\n                If(\n                    condition: 45,\n                    accept: [\n                        Return(\n                            value: Some(15),\n                        ),\n                    ],\n                    reject: [],\n                ),\n                Emit((\n                    start: 46,\n                    end: 71,\n                )),\n                Return(\n                    value: Some(70),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fs_main\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"color\"),\n                    ty: 1,\n                    init: Some(42),\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 2,\n                    init: Some(44),\n                ),\n            ],\n            expressions: [\n                GlobalVariable(3),\n                GlobalVariable(5),\n                GlobalVariable(6),\n                GlobalVariable(1),\n                GlobalVariable(4),\n                GlobalVariable(0),\n                GlobalVariable(2),\n                Constant(16),\n                Constant(13),\n                Constant(12),\n                Constant(10),\n                Constant(31),\n                Constant(8),\n                Constant(27),\n                Constant(4),\n                Constant(1),\n                Constant(23),\n                Constant(26),\n                Constant(19),\n                Constant(15),\n                Constant(14),\n                Constant(34),\n                Constant(30),\n                Constant(29),\n                Constant(6),\n                Constant(2),\n                Constant(25),\n                Constant(22),\n                Constant(18),\n                Constant(17),\n                Constant(11),\n                Constant(9),\n                Constant(32),\n                Constant(33),\n                Constant(7),\n                Constant(5),\n                Constant(3),\n                Constant(0),\n                Constant(24),\n                Constant(21),\n                Constant(20),\n                Constant(28),\n                Constant(5),\n                LocalVariable(0),\n                Constant(7),\n                LocalVariable(1),\n                Load(\n                    pointer: 45,\n                ),\n                AccessIndex(\n                    base: 6,\n                    index: 0,\n                ),\n                Access(\n                    base: 47,\n                    index: 30,\n                ),\n                Load(\n                    pointer: 48,\n                ),\n                Math(\n                    fun: Min,\n                    arg: 49,\n                    arg1: Some(24),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Binary(\n                    op: GreaterEqual,\n                    left: 46,\n                    right: 50,\n                ),\n                Load(\n                    pointer: 43,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 54,\n                    index: 55,\n                ),\n                AccessIndex(\n                    base: 56,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 57,\n                ),\n                Load(\n                    pointer: 4,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 58,\n                    right: 59,\n                ),\n                CallResult(0),\n                Load(\n                    pointer: 1,\n                ),\n                Math(\n                    fun: Normalize,\n                    arg: 62,\n                    arg1: None,\n                    arg2: None,\n                    arg3: None,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 64,\n                    index: 65,\n                ),\n                AccessIndex(\n                    base: 66,\n                    index: 1,\n                ),\n                Access(\n                    base: 67,\n                    index: 7,\n                ),\n                Load(\n                    pointer: 68,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 70,\n                    index: 71,\n                ),\n                AccessIndex(\n                    base: 72,\n                    index: 1,\n                ),\n                Access(\n                    base: 73,\n                    index: 18,\n                ),\n                Load(\n                    pointer: 74,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 76,\n                    index: 77,\n                ),\n                AccessIndex(\n                    base: 78,\n                    index: 1,\n                ),\n                Access(\n                    base: 79,\n                    index: 27,\n                ),\n                Load(\n                    pointer: 80,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        69,\n                        75,\n                        81,\n                    ],\n                ),\n                Access(\n                    base: 4,\n                    index: 16,\n                ),\n                Load(\n                    pointer: 83,\n                ),\n                Access(\n                    base: 4,\n                    index: 38,\n                ),\n                Load(\n                    pointer: 85,\n                ),\n                Access(\n                    base: 4,\n                    index: 26,\n                ),\n                Load(\n                    pointer: 87,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        84,\n                        86,\n                        88,\n                    ],\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 82,\n                    right: 89,\n                ),\n                Math(\n                    fun: Normalize,\n                    arg: 90,\n                    arg1: None,\n                    arg2: None,\n                    arg3: None,\n                ),\n                Math(\n                    fun: Dot,\n                    arg: 63,\n                    arg1: Some(91),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Math(\n                    fun: Max,\n                    arg: 37,\n                    arg1: Some(92),\n                    arg2: None,\n                    arg3: None,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 61,\n                    right: 93,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 95,\n                    index: 96,\n                ),\n                AccessIndex(\n                    base: 97,\n                    index: 2,\n                ),\n                Access(\n                    base: 98,\n                    index: 41,\n                ),\n                Load(\n                    pointer: 99,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 101,\n                    index: 102,\n                ),\n                AccessIndex(\n                    base: 103,\n                    index: 2,\n                ),\n                Access(\n                    base: 104,\n                    index: 11,\n                ),\n                Load(\n                    pointer: 105,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Access(\n                    base: 107,\n                    index: 108,\n                ),\n                AccessIndex(\n                    base: 109,\n                    index: 2,\n                ),\n                Access(\n                    base: 110,\n                    index: 21,\n                ),\n                Load(\n                    pointer: 111,\n                ),\n                Compose(\n                    ty: 1,\n                    components: [\n                        100,\n                        106,\n                        112,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 113,\n                    right: 94,\n                ),\n                Binary(\n                    op: Add,\n                    left: 52,\n                    right: 114,\n                ),\n                Load(\n                    pointer: 45,\n                ),\n                Binary(\n                    op: Add,\n                    left: 116,\n                    right: 12,\n                ),\n                Load(\n                    pointer: 43,\n                ),\n                Compose(\n                    ty: 3,\n                    components: [\n                        118,\n                        15,\n                    ],\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Loop(\n                    body: [\n                        Emit((\n                            start: 46,\n                            end: 52,\n                        )),\n                        If(\n                            condition: 51,\n                            accept: [\n                                Break,\n                            ],\n                            reject: [],\n                        ),\n                        Emit((\n                            start: 52,\n                            end: 61,\n                        )),\n                        Call(\n                            function: 0,\n                            arguments: [\n                                53,\n                                60,\n                            ],\n                            result: Some(61),\n                        ),\n                        Emit((\n                            start: 62,\n                            end: 116,\n                        )),\n                        Store(\n                            pointer: 43,\n                            value: 115,\n                        ),\n                        Continue,\n                    ],\n                    continuing: [\n                        Emit((\n                            start: 116,\n                            end: 118,\n                        )),\n                        Store(\n                            pointer: 45,\n                            value: 117,\n                        ),\n                    ],\n                    break_if: None,\n                ),\n                Emit((\n                    start: 118,\n                    end: 120,\n                )),\n                Store(\n                    pointer: 2,\n                    value: 119,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"fs_main\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"fs_main_wrap\"),\n                arguments: [\n                    (\n                        name: Some(\"in_normal_fs\"),\n                        ty: 1,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"in_position_fs\"),\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 1,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                ],\n                result: Some((\n                    ty: 3,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: None,\n                        sampling: None,\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(5),\n                    FunctionArgument(1),\n                    GlobalVariable(4),\n                    GlobalVariable(6),\n                    Load(\n                        pointer: 4,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Store(\n                        pointer: 1,\n                        value: 0,\n                    ),\n                    Store(\n                        pointer: 3,\n                        value: 2,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 5,\n                        end: 6,\n                    )),\n                    Return(\n                        value: Some(5),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/spv-spec-constants.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Sprite_size\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"size\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(1),\n                stride: 4,\n            ),\n        ),\n        (\n            name: Some(\"gl_PerVertex\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"gl_Position\"),\n                        ty: 5,\n                        binding: Some(BuiltIn(Position(\n                            invariant: false,\n                        ))),\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gl_PointSize\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"gl_ClipDistance\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 20,\n                    ),\n                    (\n                        name: Some(\"gl_CullDistance\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 24,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Camera\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"ViewProj\"),\n                        ty: 8,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: Some(\"Transform\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"Model\"),\n                        ty: 8,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 2,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gl_Position\"),\n                        ty: 5,\n                        binding: Some(BuiltIn(Position(\n                            invariant: false,\n                        ))),\n                        offset: 16,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 0,\n            init: 2,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 3,\n        ),\n    ],\n    overrides: [\n        (\n            name: Some(\"TEST_CONSTANT\"),\n            id: Some(0),\n            ty: 0,\n            init: Some(0),\n        ),\n        (\n            name: Some(\"TEST_CONSTANT_TRUE\"),\n            id: Some(1),\n            ty: 1,\n            init: Some(1),\n        ),\n        (\n            name: Some(\"TEST_CONSTANT_FALSE\"),\n            id: Some(2),\n            ty: 1,\n            init: Some(4),\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"v_Uv\"),\n            space: Private,\n            binding: None,\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Uv\"),\n            space: Private,\n            binding: None,\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Position\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 2,\n                binding: 1,\n            )),\n            ty: 4,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Private,\n            binding: None,\n            ty: 7,\n            init: Some(11),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 9,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 2,\n                binding: 0,\n            )),\n            ty: 10,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Normal\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(F32(64.0)),\n        Literal(Bool(true)),\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Literal(Bool(false)),\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Compose(\n            ty: 5,\n            components: [\n                5,\n                5,\n                5,\n                6,\n            ],\n        ),\n        Literal(F32(1.0)),\n        ZeroValue(6),\n        ZeroValue(6),\n        Compose(\n            ty: 7,\n            components: [\n                7,\n                8,\n                9,\n                10,\n            ],\n        ),\n    ],\n    functions: [\n        (\n            name: Some(\"main\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"test_constant\"),\n                    ty: 0,\n                    init: None,\n                ),\n                (\n                    name: Some(\"position\"),\n                    ty: 3,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                GlobalVariable(0),\n                GlobalVariable(1),\n                GlobalVariable(5),\n                GlobalVariable(3),\n                GlobalVariable(2),\n                GlobalVariable(4),\n                GlobalVariable(6),\n                Override(1),\n                Constant(1),\n                Override(0),\n                Constant(0),\n                Override(2),\n                LocalVariable(0),\n                LocalVariable(1),\n                Select(\n                    condition: 7,\n                    accept: 8,\n                    reject: 10,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 9,\n                    right: 14,\n                ),\n                Select(\n                    condition: 11,\n                    accept: 8,\n                    reject: 10,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 15,\n                    right: 16,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Load(\n                    pointer: 4,\n                ),\n                AccessIndex(\n                    base: 3,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 20,\n                ),\n                AccessIndex(\n                    base: 21,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 21,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 3,\n                    components: [\n                        22,\n                        23,\n                        8,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 19,\n                    right: 24,\n                ),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 26,\n                ),\n                AccessIndex(\n                    base: 6,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 28,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 27,\n                    right: 29,\n                ),\n                Load(\n                    pointer: 13,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 1,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 2,\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        32,\n                        33,\n                        34,\n                        8,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 30,\n                    right: 35,\n                ),\n                Load(\n                    pointer: 12,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 36,\n                    right: 37,\n                ),\n                AccessIndex(\n                    base: 5,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 14,\n                    end: 18,\n                )),\n                Store(\n                    pointer: 12,\n                    value: 17,\n                ),\n                Emit((\n                    start: 18,\n                    end: 19,\n                )),\n                Store(\n                    pointer: 0,\n                    value: 18,\n                ),\n                Emit((\n                    start: 19,\n                    end: 26,\n                )),\n                Store(\n                    pointer: 13,\n                    value: 25,\n                ),\n                Emit((\n                    start: 26,\n                    end: 40,\n                )),\n                Store(\n                    pointer: 39,\n                    value: 38,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main_wrap\"),\n                arguments: [\n                    (\n                        name: Some(\"Vertex_Uv\"),\n                        ty: 2,\n                        binding: Some(Location(\n                            location: 2,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"Vertex_Position\"),\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"Vertex_Normal\"),\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 1,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                ],\n                result: Some((\n                    ty: 11,\n                    binding: None,\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(1),\n                    FunctionArgument(1),\n                    GlobalVariable(2),\n                    FunctionArgument(2),\n                    GlobalVariable(7),\n                    GlobalVariable(0),\n                    GlobalVariable(4),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 8,\n                        index: 1,\n                    ),\n                    Load(\n                        pointer: 9,\n                    ),\n                    Unary(\n                        op: Negate,\n                        expr: 10,\n                    ),\n                    Load(\n                        pointer: 6,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    Compose(\n                        ty: 11,\n                        components: [\n                            12,\n                            13,\n                        ],\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Store(\n                        pointer: 1,\n                        value: 0,\n                    ),\n                    Store(\n                        pointer: 3,\n                        value: 2,\n                    ),\n                    Store(\n                        pointer: 5,\n                        value: 4,\n                    ),\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 9,\n                        end: 12,\n                    )),\n                    Store(\n                        pointer: 9,\n                        value: 11,\n                    ),\n                    Emit((\n                        start: 12,\n                        end: 15,\n                    )),\n                    Return(\n                        value: Some(14),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/spv-spec-constants.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 0,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 3,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 5,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 5,\n                space: Private,\n            ),\n        ),\n        (\n            name: Some(\"Sprite_size\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"size\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 8,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 3,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(1),\n                stride: 4,\n            ),\n        ),\n        (\n            name: Some(\"gl_PerVertex\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"gl_Position\"),\n                        ty: 12,\n                        binding: Some(BuiltIn(Position(\n                            invariant: false,\n                        ))),\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gl_PointSize\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"gl_ClipDistance\"),\n                        ty: 14,\n                        binding: None,\n                        offset: 20,\n                    ),\n                    (\n                        name: Some(\"gl_CullDistance\"),\n                        ty: 14,\n                        binding: None,\n                        offset: 24,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 15,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Camera\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"ViewProj\"),\n                        ty: 17,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 18,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 17,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: Some(\"Transform\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"Model\"),\n                        ty: 17,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 21,\n                space: Uniform,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 12,\n                space: Private,\n            ),\n        ),\n        (\n            name: None,\n            inner: Struct(\n                members: [\n                    (\n                        name: None,\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: Some(Perspective),\n                            sampling: Some(Center),\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gl_Position\"),\n                        ty: 12,\n                        binding: Some(BuiltIn(Position(\n                            invariant: false,\n                        ))),\n                        offset: 16,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: None,\n            ty: 0,\n            init: 2,\n        ),\n        (\n            name: None,\n            ty: 0,\n            init: 3,\n        ),\n        (\n            name: None,\n            ty: 10,\n            init: 5,\n        ),\n        (\n            name: None,\n            ty: 13,\n            init: 6,\n        ),\n    ],\n    overrides: [\n        (\n            name: Some(\"TEST_CONSTANT\"),\n            id: Some(0),\n            ty: 0,\n            init: Some(0),\n        ),\n        (\n            name: Some(\"TEST_CONSTANT_TRUE\"),\n            id: Some(1),\n            ty: 2,\n            init: Some(1),\n        ),\n        (\n            name: Some(\"TEST_CONSTANT_FALSE\"),\n            id: Some(2),\n            ty: 2,\n            init: Some(4),\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"v_Uv\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Uv\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Position\"),\n            space: Private,\n            binding: None,\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 2,\n                binding: 1,\n            )),\n            ty: 8,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Private,\n            binding: None,\n            ty: 15,\n            init: Some(13),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 18,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"\"),\n            space: Uniform,\n            binding: Some((\n                group: 2,\n                binding: 0,\n            )),\n            ty: 21,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"Vertex_Normal\"),\n            space: Private,\n            binding: None,\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(F32(64.0)),\n        Literal(Bool(true)),\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Literal(Bool(false)),\n        Literal(I32(0)),\n        Literal(U32(1)),\n        Literal(F32(0.0)),\n        Literal(F32(1.0)),\n        Compose(\n            ty: 12,\n            components: [\n                7,\n                7,\n                7,\n                8,\n            ],\n        ),\n        Literal(F32(1.0)),\n        ZeroValue(14),\n        ZeroValue(14),\n        Compose(\n            ty: 15,\n            components: [\n                9,\n                10,\n                11,\n                12,\n            ],\n        ),\n    ],\n    functions: [\n        (\n            name: Some(\"main\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"test_constant\"),\n                    ty: 0,\n                    init: None,\n                ),\n                (\n                    name: Some(\"position\"),\n                    ty: 5,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                GlobalVariable(0),\n                GlobalVariable(7),\n                GlobalVariable(1),\n                GlobalVariable(5),\n                GlobalVariable(3),\n                GlobalVariable(2),\n                GlobalVariable(4),\n                GlobalVariable(6),\n                Constant(2),\n                Override(1),\n                Constant(1),\n                Constant(3),\n                Override(0),\n                Constant(0),\n                Override(2),\n                LocalVariable(0),\n                LocalVariable(1),\n                Select(\n                    condition: 9,\n                    accept: 10,\n                    reject: 13,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 12,\n                    right: 17,\n                ),\n                Select(\n                    condition: 14,\n                    accept: 10,\n                    reject: 13,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 18,\n                    right: 19,\n                ),\n                Load(\n                    pointer: 2,\n                ),\n                Load(\n                    pointer: 5,\n                ),\n                AccessIndex(\n                    base: 4,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 23,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 1,\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        25,\n                        26,\n                        10,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 22,\n                    right: 27,\n                ),\n                AccessIndex(\n                    base: 3,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 29,\n                ),\n                AccessIndex(\n                    base: 7,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 31,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 30,\n                    right: 32,\n                ),\n                Load(\n                    pointer: 16,\n                ),\n                AccessIndex(\n                    base: 34,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 34,\n                    index: 1,\n                ),\n                AccessIndex(\n                    base: 34,\n                    index: 2,\n                ),\n                Compose(\n                    ty: 12,\n                    components: [\n                        35,\n                        36,\n                        37,\n                        10,\n                    ],\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 33,\n                    right: 38,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 39,\n                    right: 40,\n                ),\n                AccessIndex(\n                    base: 6,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 17,\n                    end: 21,\n                )),\n                Store(\n                    pointer: 15,\n                    value: 20,\n                ),\n                Emit((\n                    start: 21,\n                    end: 22,\n                )),\n                Store(\n                    pointer: 0,\n                    value: 21,\n                ),\n                Emit((\n                    start: 22,\n                    end: 29,\n                )),\n                Store(\n                    pointer: 16,\n                    value: 28,\n                ),\n                Emit((\n                    start: 29,\n                    end: 43,\n                )),\n                Store(\n                    pointer: 42,\n                    value: 41,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main_wrap\"),\n                arguments: [\n                    (\n                        name: Some(\"Vertex_Uv\"),\n                        ty: 3,\n                        binding: Some(Location(\n                            location: 2,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"Vertex_Position\"),\n                        ty: 5,\n                        binding: Some(Location(\n                            location: 0,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                    (\n                        name: Some(\"Vertex_Normal\"),\n                        ty: 5,\n                        binding: Some(Location(\n                            location: 1,\n                            interpolation: None,\n                            sampling: None,\n                            blend_src: None,\n                            per_primitive: false,\n                        )),\n                    ),\n                ],\n                result: Some((\n                    ty: 24,\n                    binding: None,\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(1),\n                    FunctionArgument(1),\n                    GlobalVariable(2),\n                    FunctionArgument(2),\n                    GlobalVariable(7),\n                    GlobalVariable(0),\n                    GlobalVariable(4),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 8,\n                        index: 1,\n                    ),\n                    Load(\n                        pointer: 9,\n                    ),\n                    Unary(\n                        op: Negate,\n                        expr: 10,\n                    ),\n                    Load(\n                        pointer: 6,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    Compose(\n                        ty: 24,\n                        components: [\n                            12,\n                            13,\n                        ],\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Store(\n                        pointer: 1,\n                        value: 0,\n                    ),\n                    Store(\n                        pointer: 3,\n                        value: 2,\n                    ),\n                    Store(\n                        pointer: 5,\n                        value: 4,\n                    ),\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 9,\n                        end: 12,\n                    )),\n                    Store(\n                        pointer: 9,\n                        value: 11,\n                    ),\n                    Emit((\n                        start: 12,\n                        end: 15,\n                    )),\n                    Return(\n                        value: Some(14),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-access.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: Some(\"GlobalConst\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"a\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"b\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"c\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 28,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n        (\n            name: Some(\"AlignedWrapper\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"value\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 7,\n                size: Constant(2),\n                stride: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Atomic((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 9,\n                size: Constant(10),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 11,\n                size: Constant(2),\n                stride: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 4,\n                size: Dynamic,\n                stride: 8,\n            ),\n        ),\n        (\n            name: Some(\"Bar\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"_matrix\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"matrix_array\"),\n                        ty: 8,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"atom\"),\n                        ty: 9,\n                        binding: None,\n                        offset: 96,\n                    ),\n                    (\n                        name: Some(\"atom_arr\"),\n                        ty: 10,\n                        binding: None,\n                        offset: 100,\n                    ),\n                    (\n                        name: Some(\"arr\"),\n                        ty: 12,\n                        binding: None,\n                        offset: 144,\n                    ),\n                    (\n                        name: Some(\"data\"),\n                        ty: 13,\n                        binding: None,\n                        offset: 160,\n                    ),\n                ],\n                span: 176,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Baz\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"m\"),\n                        ty: 15,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 24,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Sint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 18,\n                size: Constant(2),\n                stride: 32,\n            ),\n        ),\n        (\n            name: Some(\"MatCx2InArray\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"am\"),\n                        ty: 19,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 5,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 5,\n                size: Constant(10),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 22,\n                size: Constant(5),\n                stride: 40,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 2,\n                size: Constant(5),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 0,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 24,\n                size: Constant(2),\n                stride: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 27,\n                space: Function,\n            ),\n        ),\n        (\n            name: Some(\"AssignToMember\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"x\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 29,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(4),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 31,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 33,\n                size: Constant(1),\n                stride: 1,\n            ),\n        ),\n        (\n            name: Some(\"S\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"m\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: Some(\"Inner\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"delicious\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: Some(\"Outer\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"om_nom_nom\"),\n                        ty: 36,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"thing\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 4,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"msl_padding_global_const\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: Some(6),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"bar\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 14,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"baz\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 16,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"qux\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 17,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"nested_mat_cx2\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 3,\n            )),\n            ty: 20,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Compose(\n            ty: 1,\n            components: [\n                1,\n                2,\n                3,\n            ],\n        ),\n        Literal(I32(0)),\n        Compose(\n            ty: 3,\n            components: [\n                0,\n                4,\n                5,\n            ],\n        ),\n    ],\n    functions: [\n        (\n            name: Some(\"test_matrix_within_struct_accesses\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"idx\"),\n                    ty: 2,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"t\"),\n                    ty: 16,\n                    init: Some(48),\n                ),\n            ],\n            expressions: [\n                Literal(I32(1)),\n                LocalVariable(0),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 3,\n                    right: 2,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 5,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 6,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 8,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 9,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 10,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 12,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 13,\n                    index: 14,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 17,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 18,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 19,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 20,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 22,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 23,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 24,\n                    index: 25,\n                ),\n                Load(\n                    pointer: 26,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 28,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 29,\n                    index: 30,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 32,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 34,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 35,\n                    index: 36,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 37,\n                    index: 38,\n                ),\n                Load(\n                    pointer: 39,\n                ),\n                Literal(F32(1.0)),\n                Splat(\n                    size: Bi,\n                    value: 41,\n                ),\n                Literal(F32(2.0)),\n                Splat(\n                    size: Bi,\n                    value: 43,\n                ),\n                Literal(F32(3.0)),\n                Splat(\n                    size: Bi,\n                    value: 45,\n                ),\n                Compose(\n                    ty: 15,\n                    components: [\n                        42,\n                        44,\n                        46,\n                    ],\n                ),\n                Compose(\n                    ty: 16,\n                    components: [\n                        47,\n                    ],\n                ),\n                LocalVariable(1),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Add,\n                    left: 51,\n                    right: 50,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Bi,\n                    value: 54,\n                ),\n                Literal(F32(5.0)),\n                Splat(\n                    size: Bi,\n                    value: 56,\n                ),\n                Literal(F32(4.0)),\n                Splat(\n                    size: Bi,\n                    value: 58,\n                ),\n                Compose(\n                    ty: 15,\n                    components: [\n                        55,\n                        57,\n                        59,\n                    ],\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 61,\n                    index: 0,\n                ),\n                Literal(F32(9.0)),\n                Splat(\n                    size: Bi,\n                    value: 63,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 65,\n                    index: 66,\n                ),\n                Literal(F32(90.0)),\n                Splat(\n                    size: Bi,\n                    value: 68,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 70,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 71,\n                    index: 1,\n                ),\n                Literal(F32(10.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 74,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 75,\n                    index: 76,\n                ),\n                Literal(F32(20.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 79,\n                    index: 80,\n                ),\n                AccessIndex(\n                    base: 81,\n                    index: 1,\n                ),\n                Literal(F32(30.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 84,\n                    index: 85,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 86,\n                    index: 87,\n                ),\n                Literal(F32(40.0)),\n            ],\n            named_expressions: {\n                7: \"l0\",\n                11: \"l1\",\n                16: \"l2\",\n                21: \"l3\",\n                27: \"l4\",\n                33: \"l5\",\n                40: \"l6\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 4,\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Emit((\n                    start: 9,\n                    end: 10,\n                )),\n                Emit((\n                    start: 10,\n                    end: 12,\n                )),\n                Emit((\n                    start: 13,\n                    end: 17,\n                )),\n                Emit((\n                    start: 18,\n                    end: 19,\n                )),\n                Emit((\n                    start: 19,\n                    end: 20,\n                )),\n                Emit((\n                    start: 20,\n                    end: 22,\n                )),\n                Emit((\n                    start: 23,\n                    end: 24,\n                )),\n                Emit((\n                    start: 24,\n                    end: 28,\n                )),\n                Emit((\n                    start: 29,\n                    end: 32,\n                )),\n                Emit((\n                    start: 32,\n                    end: 34,\n                )),\n                Emit((\n                    start: 35,\n                    end: 41,\n                )),\n                Emit((\n                    start: 42,\n                    end: 43,\n                )),\n                Emit((\n                    start: 44,\n                    end: 45,\n                )),\n                Emit((\n                    start: 46,\n                    end: 49,\n                )),\n                Emit((\n                    start: 51,\n                    end: 53,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 52,\n                ),\n                Emit((\n                    start: 53,\n                    end: 54,\n                )),\n                Emit((\n                    start: 55,\n                    end: 56,\n                )),\n                Emit((\n                    start: 57,\n                    end: 58,\n                )),\n                Emit((\n                    start: 59,\n                    end: 61,\n                )),\n                Store(\n                    pointer: 53,\n                    value: 60,\n                ),\n                Emit((\n                    start: 61,\n                    end: 62,\n                )),\n                Emit((\n                    start: 62,\n                    end: 63,\n                )),\n                Emit((\n                    start: 64,\n                    end: 65,\n                )),\n                Store(\n                    pointer: 62,\n                    value: 64,\n                ),\n                Emit((\n                    start: 65,\n                    end: 68,\n                )),\n                Emit((\n                    start: 69,\n                    end: 70,\n                )),\n                Store(\n                    pointer: 67,\n                    value: 69,\n                ),\n                Emit((\n                    start: 70,\n                    end: 71,\n                )),\n                Emit((\n                    start: 71,\n                    end: 72,\n                )),\n                Emit((\n                    start: 72,\n                    end: 73,\n                )),\n                Store(\n                    pointer: 72,\n                    value: 73,\n                ),\n                Emit((\n                    start: 74,\n                    end: 75,\n                )),\n                Emit((\n                    start: 75,\n                    end: 78,\n                )),\n                Store(\n                    pointer: 77,\n                    value: 78,\n                ),\n                Emit((\n                    start: 79,\n                    end: 82,\n                )),\n                Emit((\n                    start: 82,\n                    end: 83,\n                )),\n                Store(\n                    pointer: 82,\n                    value: 83,\n                ),\n                Emit((\n                    start: 84,\n                    end: 89,\n                )),\n                Store(\n                    pointer: 88,\n                    value: 89,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"test_matrix_within_array_within_struct_accesses\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"idx\"),\n                    ty: 2,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"t\"),\n                    ty: 20,\n                    init: Some(52),\n                ),\n            ],\n            expressions: [\n                Literal(I32(1)),\n                LocalVariable(0),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 3,\n                    right: 2,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 5,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 6,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 8,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 9,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 10,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 12,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 13,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 14,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 17,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 18,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 19,\n                    index: 20,\n                ),\n                Load(\n                    pointer: 21,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 23,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 25,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 26,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 27,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 29,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 30,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 32,\n                    index: 33,\n                ),\n                Load(\n                    pointer: 34,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 36,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 37,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 38,\n                    index: 39,\n                ),\n                AccessIndex(\n                    base: 40,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 41,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 43,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 44,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 45,\n                    index: 46,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 47,\n                    index: 48,\n                ),\n                Load(\n                    pointer: 49,\n                ),\n                ZeroValue(19),\n                Compose(\n                    ty: 20,\n                    components: [\n                        51,\n                    ],\n                ),\n                LocalVariable(1),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Add,\n                    left: 55,\n                    right: 54,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                ZeroValue(19),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 59,\n                    index: 0,\n                ),\n                Literal(F32(8.0)),\n                Splat(\n                    size: Bi,\n                    value: 61,\n                ),\n                Literal(F32(7.0)),\n                Splat(\n                    size: Bi,\n                    value: 63,\n                ),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Bi,\n                    value: 65,\n                ),\n                Literal(F32(5.0)),\n                Splat(\n                    size: Bi,\n                    value: 67,\n                ),\n                Compose(\n                    ty: 18,\n                    components: [\n                        62,\n                        64,\n                        66,\n                        68,\n                    ],\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 70,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 71,\n                    index: 0,\n                ),\n                Literal(F32(9.0)),\n                Splat(\n                    size: Bi,\n                    value: 73,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 75,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 76,\n                    index: 77,\n                ),\n                Literal(F32(90.0)),\n                Splat(\n                    size: Bi,\n                    value: 79,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 81,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 82,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 83,\n                    index: 1,\n                ),\n                Literal(F32(10.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 86,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 87,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 88,\n                    index: 89,\n                ),\n                Literal(F32(20.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 92,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 93,\n                    index: 94,\n                ),\n                AccessIndex(\n                    base: 95,\n                    index: 1,\n                ),\n                Literal(F32(30.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 98,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 99,\n                    index: 100,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 101,\n                    index: 102,\n                ),\n                Literal(F32(40.0)),\n            ],\n            named_expressions: {\n                7: \"l0\",\n                11: \"l1\",\n                16: \"l2\",\n                22: \"l3\",\n                28: \"l4\",\n                35: \"l5\",\n                42: \"l6\",\n                50: \"l7\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 4,\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Emit((\n                    start: 9,\n                    end: 10,\n                )),\n                Emit((\n                    start: 10,\n                    end: 12,\n                )),\n                Emit((\n                    start: 13,\n                    end: 14,\n                )),\n                Emit((\n                    start: 14,\n                    end: 15,\n                )),\n                Emit((\n                    start: 15,\n                    end: 17,\n                )),\n                Emit((\n                    start: 18,\n                    end: 19,\n                )),\n                Emit((\n                    start: 19,\n                    end: 23,\n                )),\n                Emit((\n                    start: 24,\n                    end: 25,\n                )),\n                Emit((\n                    start: 25,\n                    end: 26,\n                )),\n                Emit((\n                    start: 26,\n                    end: 27,\n                )),\n                Emit((\n                    start: 27,\n                    end: 29,\n                )),\n                Emit((\n                    start: 30,\n                    end: 31,\n                )),\n                Emit((\n                    start: 31,\n                    end: 32,\n                )),\n                Emit((\n                    start: 32,\n                    end: 36,\n                )),\n                Emit((\n                    start: 37,\n                    end: 38,\n                )),\n                Emit((\n                    start: 38,\n                    end: 41,\n                )),\n                Emit((\n                    start: 41,\n                    end: 43,\n                )),\n                Emit((\n                    start: 44,\n                    end: 45,\n                )),\n                Emit((\n                    start: 45,\n                    end: 51,\n                )),\n                Emit((\n                    start: 52,\n                    end: 53,\n                )),\n                Emit((\n                    start: 55,\n                    end: 57,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 56,\n                ),\n                Emit((\n                    start: 57,\n                    end: 58,\n                )),\n                Store(\n                    pointer: 57,\n                    value: 58,\n                ),\n                Emit((\n                    start: 59,\n                    end: 60,\n                )),\n                Emit((\n                    start: 60,\n                    end: 61,\n                )),\n                Emit((\n                    start: 62,\n                    end: 63,\n                )),\n                Emit((\n                    start: 64,\n                    end: 65,\n                )),\n                Emit((\n                    start: 66,\n                    end: 67,\n                )),\n                Emit((\n                    start: 68,\n                    end: 70,\n                )),\n                Store(\n                    pointer: 60,\n                    value: 69,\n                ),\n                Emit((\n                    start: 70,\n                    end: 71,\n                )),\n                Emit((\n                    start: 71,\n                    end: 72,\n                )),\n                Emit((\n                    start: 72,\n                    end: 73,\n                )),\n                Emit((\n                    start: 74,\n                    end: 75,\n                )),\n                Store(\n                    pointer: 72,\n                    value: 74,\n                ),\n                Emit((\n                    start: 75,\n                    end: 76,\n                )),\n                Emit((\n                    start: 76,\n                    end: 79,\n                )),\n                Emit((\n                    start: 80,\n                    end: 81,\n                )),\n                Store(\n                    pointer: 78,\n                    value: 80,\n                ),\n                Emit((\n                    start: 81,\n                    end: 82,\n                )),\n                Emit((\n                    start: 82,\n                    end: 83,\n                )),\n                Emit((\n                    start: 83,\n                    end: 84,\n                )),\n                Emit((\n                    start: 84,\n                    end: 85,\n                )),\n                Store(\n                    pointer: 84,\n                    value: 85,\n                ),\n                Emit((\n                    start: 86,\n                    end: 87,\n                )),\n                Emit((\n                    start: 87,\n                    end: 88,\n                )),\n                Emit((\n                    start: 88,\n                    end: 91,\n                )),\n                Store(\n                    pointer: 90,\n                    value: 91,\n                ),\n                Emit((\n                    start: 92,\n                    end: 93,\n                )),\n                Emit((\n                    start: 93,\n                    end: 96,\n                )),\n                Emit((\n                    start: 96,\n                    end: 97,\n                )),\n                Store(\n                    pointer: 96,\n                    value: 97,\n                ),\n                Emit((\n                    start: 98,\n                    end: 99,\n                )),\n                Emit((\n                    start: 99,\n                    end: 104,\n                )),\n                Store(\n                    pointer: 103,\n                    value: 104,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"read_from_private\"),\n            arguments: [\n                (\n                    name: Some(\"foo\"),\n                    ty: 21,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 5,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Load(\n                    pointer: 0,\n                ),\n            ],\n            named_expressions: {\n                0: \"foo\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Return(\n                    value: Some(1),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"test_arr_as_arg\"),\n            arguments: [\n                (\n                    name: Some(\"a\"),\n                    ty: 23,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 5,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 4,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 9,\n                ),\n            ],\n            named_expressions: {\n                0: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_through_ptr_fn\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 26,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(U32(42)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Store(\n                    pointer: 0,\n                    value: 1,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_array_through_ptr_fn\"),\n            arguments: [\n                (\n                    name: Some(\"foo\"),\n                    ty: 28,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(F32(1.0)),\n                Splat(\n                    size: Quad,\n                    value: 1,\n                ),\n                Literal(F32(2.0)),\n                Splat(\n                    size: Quad,\n                    value: 3,\n                ),\n                Compose(\n                    ty: 27,\n                    components: [\n                        2,\n                        4,\n                    ],\n                ),\n            ],\n            named_expressions: {\n                0: \"foo\",\n            },\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Emit((\n                    start: 4,\n                    end: 6,\n                )),\n                Store(\n                    pointer: 0,\n                    value: 5,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_through_ptr\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"val\"),\n                    ty: 0,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"arr\"),\n                    ty: 27,\n                    init: Some(6),\n                ),\n            ],\n            expressions: [\n                Literal(U32(33)),\n                LocalVariable(0),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Quad,\n                    value: 2,\n                ),\n                Literal(F32(7.0)),\n                Splat(\n                    size: Quad,\n                    value: 4,\n                ),\n                Compose(\n                    ty: 27,\n                    components: [\n                        3,\n                        5,\n                    ],\n                ),\n                LocalVariable(1),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 4,\n                    arguments: [\n                        1,\n                    ],\n                    result: None,\n                ),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 3,\n                    end: 4,\n                )),\n                Emit((\n                    start: 5,\n                    end: 7,\n                )),\n                Call(\n                    function: 5,\n                    arguments: [\n                        7,\n                    ],\n                    result: None,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fetch_arg_ptr_member\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 30,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_arg_ptr_member\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 30,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Literal(U32(10)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 2,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fetch_arg_ptr_array_element\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 32,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_arg_ptr_array_element\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 32,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                Literal(U32(10)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 2,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_ptr_components\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"s1\"),\n                    ty: 29,\n                    init: None,\n                ),\n                (\n                    name: Some(\"a1\"),\n                    ty: 31,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                LocalVariable(0),\n                CallResult(7),\n                LocalVariable(1),\n                CallResult(9),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 8,\n                    arguments: [\n                        0,\n                    ],\n                    result: None,\n                ),\n                Call(\n                    function: 7,\n                    arguments: [\n                        0,\n                    ],\n                    result: Some(1),\n                ),\n                Call(\n                    function: 10,\n                    arguments: [\n                        2,\n                    ],\n                    result: None,\n                ),\n                Call(\n                    function: 9,\n                    arguments: [\n                        2,\n                    ],\n                    result: Some(3),\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_ptr\"),\n            arguments: [\n                (\n                    name: Some(\"value\"),\n                    ty: 33,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 33,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"a\"),\n                    ty: 34,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                Compose(\n                    ty: 34,\n                    components: [\n                        0,\n                    ],\n                ),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                0: \"value\",\n                2: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 2,\n                    value: 1,\n                ),\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Return(\n                    value: Some(4),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"member_ptr\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"s\"),\n                    ty: 35,\n                    init: Some(1),\n                ),\n            ],\n            expressions: [\n                Literal(I32(42)),\n                Compose(\n                    ty: 35,\n                    components: [\n                        0,\n                    ],\n                ),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                2: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Return(\n                    value: Some(4),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"let_members_of_members\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                ZeroValue(37),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                As(\n                    expr: 2,\n                    kind: Uint,\n                    convert: Some(4),\n                ),\n                Binary(\n                    op: NotEqual,\n                    left: 3,\n                    right: 4,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 6,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {\n                0: \"thing\",\n                1: \"inner\",\n                2: \"delishus\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Emit((\n                    start: 3,\n                    end: 6,\n                )),\n                If(\n                    condition: 5,\n                    accept: [],\n                    reject: [],\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Return(\n                    value: Some(7),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"var_members_of_members\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"thing\"),\n                    ty: 37,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"inner\"),\n                    ty: 36,\n                    init: None,\n                ),\n                (\n                    name: Some(\"delishus\"),\n                    ty: 2,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                ZeroValue(37),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 2,\n                ),\n                LocalVariable(1),\n                AccessIndex(\n                    base: 4,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 5,\n                ),\n                LocalVariable(2),\n                AccessIndex(\n                    base: 1,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 8,\n                ),\n                Load(\n                    pointer: 7,\n                ),\n                As(\n                    expr: 10,\n                    kind: Uint,\n                    convert: Some(4),\n                ),\n                Binary(\n                    op: NotEqual,\n                    left: 9,\n                    right: 11,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 13,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 14,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 2,\n                    end: 4,\n                )),\n                Store(\n                    pointer: 4,\n                    value: 3,\n                ),\n                Emit((\n                    start: 5,\n                    end: 7,\n                )),\n                Store(\n                    pointer: 7,\n                    value: 6,\n                ),\n                Emit((\n                    start: 8,\n                    end: 13,\n                )),\n                If(\n                    condition: 12,\n                    accept: [],\n                    reject: [],\n                ),\n                Emit((\n                    start: 13,\n                    end: 16,\n                )),\n                Return(\n                    value: Some(15),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"foo_vert\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_vert\"),\n                arguments: [\n                    (\n                        name: Some(\"vi\"),\n                        ty: 0,\n                        binding: Some(BuiltIn(VertexIndex)),\n                    ),\n                ],\n                result: Some((\n                    ty: 24,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [\n                    (\n                        name: Some(\"foo\"),\n                        ty: 5,\n                        init: Some(1),\n                    ),\n                    (\n                        name: Some(\"c2\"),\n                        ty: 25,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    FunctionArgument(0),\n                    Literal(F32(0.0)),\n                    LocalVariable(0),\n                    Load(\n                        pointer: 2,\n                    ),\n                    Literal(F32(1.0)),\n                    GlobalVariable(0),\n                    Load(\n                        pointer: 5,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 10,\n                        index: 4,\n                    ),\n                    Load(\n                        pointer: 11,\n                    ),\n                    Literal(U32(3)),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 14,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 15,\n                        index: 13,\n                    ),\n                    AccessIndex(\n                        base: 16,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 17,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 19,\n                        index: 5,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 21,\n                        index: 5,\n                    ),\n                    ArrayLength(22),\n                    Literal(U32(2)),\n                    Binary(\n                        op: Subtract,\n                        left: 23,\n                        right: 24,\n                    ),\n                    Access(\n                        base: 20,\n                        index: 25,\n                    ),\n                    AccessIndex(\n                        base: 26,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 27,\n                    ),\n                    GlobalVariable(3),\n                    Load(\n                        pointer: 29,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 31,\n                        index: 5,\n                    ),\n                    AccessIndex(\n                        base: 32,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 33,\n                        index: 0,\n                    ),\n                    CallResult(2),\n                    As(\n                        expr: 18,\n                        kind: Sint,\n                        convert: Some(4),\n                    ),\n                    Literal(I32(3)),\n                    Literal(I32(4)),\n                    Literal(I32(5)),\n                    Compose(\n                        ty: 25,\n                        components: [\n                            28,\n                            36,\n                            37,\n                            38,\n                            39,\n                        ],\n                    ),\n                    LocalVariable(1),\n                    Literal(U32(1)),\n                    Binary(\n                        op: Add,\n                        left: 0,\n                        right: 42,\n                    ),\n                    Access(\n                        base: 41,\n                        index: 43,\n                    ),\n                    Literal(I32(42)),\n                    Access(\n                        base: 41,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 46,\n                    ),\n                    ZeroValue(23),\n                    CallResult(3),\n                    Splat(\n                        size: Quad,\n                        value: 47,\n                    ),\n                    As(\n                        expr: 50,\n                        kind: Float,\n                        convert: Some(4),\n                    ),\n                    Binary(\n                        op: Multiply,\n                        left: 9,\n                        right: 51,\n                    ),\n                    Literal(F32(2.0)),\n                    Compose(\n                        ty: 24,\n                        components: [\n                            52,\n                            53,\n                        ],\n                    ),\n                ],\n                named_expressions: {\n                    0: \"vi\",\n                    3: \"baz\",\n                    6: \"phony\",\n                    9: \"_matrix\",\n                    12: \"arr\",\n                    13: \"index\",\n                    18: \"b\",\n                    28: \"a\",\n                    30: \"c\",\n                    34: \"data_pointer\",\n                    35: \"foo_value\",\n                    47: \"value\",\n                },\n                body: [\n                    Emit((\n                        start: 3,\n                        end: 4,\n                    )),\n                    Store(\n                        pointer: 2,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 8,\n                        end: 10,\n                    )),\n                    Emit((\n                        start: 11,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 15,\n                        end: 19,\n                    )),\n                    Emit((\n                        start: 20,\n                        end: 21,\n                    )),\n                    Emit((\n                        start: 22,\n                        end: 24,\n                    )),\n                    Emit((\n                        start: 25,\n                        end: 29,\n                    )),\n                    Emit((\n                        start: 30,\n                        end: 31,\n                    )),\n                    Emit((\n                        start: 32,\n                        end: 33,\n                    )),\n                    Emit((\n                        start: 33,\n                        end: 35,\n                    )),\n                    Call(\n                        function: 2,\n                        arguments: [\n                            2,\n                        ],\n                        result: Some(35),\n                    ),\n                    Emit((\n                        start: 36,\n                        end: 37,\n                    )),\n                    Emit((\n                        start: 40,\n                        end: 41,\n                    )),\n                    Store(\n                        pointer: 41,\n                        value: 40,\n                    ),\n                    Emit((\n                        start: 43,\n                        end: 45,\n                    )),\n                    Store(\n                        pointer: 44,\n                        value: 45,\n                    ),\n                    Emit((\n                        start: 46,\n                        end: 48,\n                    )),\n                    Call(\n                        function: 3,\n                        arguments: [\n                            48,\n                        ],\n                        result: Some(49),\n                    ),\n                    Emit((\n                        start: 50,\n                        end: 53,\n                    )),\n                    Emit((\n                        start: 54,\n                        end: 55,\n                    )),\n                    Return(\n                        value: Some(54),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"foo_frag\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_frag\"),\n                arguments: [],\n                result: Some((\n                    ty: 24,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: Some(Perspective),\n                        sampling: Some(Center),\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 1,\n                        index: 1,\n                    ),\n                    AccessIndex(\n                        base: 2,\n                        index: 2,\n                    ),\n                    Literal(F32(1.0)),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Tri,\n                        value: 7,\n                    ),\n                    Literal(F32(1.0)),\n                    Splat(\n                        size: Tri,\n                        value: 9,\n                    ),\n                    Literal(F32(2.0)),\n                    Splat(\n                        size: Tri,\n                        value: 11,\n                    ),\n                    Literal(F32(3.0)),\n                    Splat(\n                        size: Tri,\n                        value: 13,\n                    ),\n                    Compose(\n                        ty: 6,\n                        components: [\n                            8,\n                            10,\n                            12,\n                            14,\n                        ],\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 16,\n                        index: 4,\n                    ),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 18,\n                    ),\n                    Literal(U32(1)),\n                    Splat(\n                        size: Bi,\n                        value: 20,\n                    ),\n                    Compose(\n                        ty: 12,\n                        components: [\n                            19,\n                            21,\n                        ],\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 23,\n                        index: 5,\n                    ),\n                    AccessIndex(\n                        base: 24,\n                        index: 1,\n                    ),\n                    AccessIndex(\n                        base: 25,\n                        index: 0,\n                    ),\n                    Literal(I32(1)),\n                    GlobalVariable(3),\n                    ZeroValue(17),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 30,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Emit((\n                        start: 2,\n                        end: 4,\n                    )),\n                    Store(\n                        pointer: 3,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 9,\n                    )),\n                    Emit((\n                        start: 10,\n                        end: 11,\n                    )),\n                    Emit((\n                        start: 12,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 14,\n                        end: 16,\n                    )),\n                    Store(\n                        pointer: 6,\n                        value: 15,\n                    ),\n                    Emit((\n                        start: 17,\n                        end: 18,\n                    )),\n                    Emit((\n                        start: 19,\n                        end: 20,\n                    )),\n                    Emit((\n                        start: 21,\n                        end: 23,\n                    )),\n                    Store(\n                        pointer: 17,\n                        value: 22,\n                    ),\n                    Emit((\n                        start: 24,\n                        end: 25,\n                    )),\n                    Emit((\n                        start: 25,\n                        end: 27,\n                    )),\n                    Store(\n                        pointer: 26,\n                        value: 27,\n                    ),\n                    Store(\n                        pointer: 28,\n                        value: 29,\n                    ),\n                    Emit((\n                        start: 31,\n                        end: 32,\n                    )),\n                    Return(\n                        value: Some(31),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"foo_compute\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_compute\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    Literal(Bool(true)),\n                    CallResult(12),\n                    CallResult(13),\n                    CallResult(14),\n                    CallResult(15),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 6,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 11,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 12,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Call(\n                        function: 13,\n                        arguments: [],\n                        result: Some(2),\n                    ),\n                    Call(\n                        function: 14,\n                        arguments: [],\n                        result: Some(3),\n                    ),\n                    Call(\n                        function: 15,\n                        arguments: [],\n                        result: Some(4),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-access.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: Some(\"GlobalConst\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"a\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"b\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"c\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 28,\n                    ),\n                ],\n                span: 32,\n            ),\n        ),\n        (\n            name: Some(\"AlignedWrapper\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"value\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 7,\n                size: Constant(2),\n                stride: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Atomic((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 9,\n                size: Constant(10),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 11,\n                size: Constant(2),\n                stride: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 4,\n                size: Dynamic,\n                stride: 8,\n            ),\n        ),\n        (\n            name: Some(\"Bar\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"_matrix\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"matrix_array\"),\n                        ty: 8,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"atom\"),\n                        ty: 9,\n                        binding: None,\n                        offset: 96,\n                    ),\n                    (\n                        name: Some(\"atom_arr\"),\n                        ty: 10,\n                        binding: None,\n                        offset: 100,\n                    ),\n                    (\n                        name: Some(\"arr\"),\n                        ty: 12,\n                        binding: None,\n                        offset: 144,\n                    ),\n                    (\n                        name: Some(\"data\"),\n                        ty: 13,\n                        binding: None,\n                        offset: 160,\n                    ),\n                ],\n                span: 176,\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"Baz\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"m\"),\n                        ty: 15,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 24,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Sint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 18,\n                size: Constant(2),\n                stride: 32,\n            ),\n        ),\n        (\n            name: Some(\"MatCx2InArray\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"am\"),\n                        ty: 19,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 64,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 5,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 5,\n                size: Constant(10),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 22,\n                size: Constant(5),\n                stride: 40,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 2,\n                size: Constant(5),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 0,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 24,\n                size: Constant(2),\n                stride: 16,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 27,\n                space: Function,\n            ),\n        ),\n        (\n            name: Some(\"AssignToMember\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"x\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 29,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(4),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Pointer(\n                base: 31,\n                space: Function,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 33,\n                size: Constant(1),\n                stride: 1,\n            ),\n        ),\n        (\n            name: Some(\"S\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"m\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: Some(\"Inner\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"delicious\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: Some(\"Outer\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"om_nom_nom\"),\n                        ty: 36,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"thing\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 4,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"msl_padding_global_const\"),\n            space: Private,\n            binding: None,\n            ty: 3,\n            init: Some(6),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"bar\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 14,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"baz\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 16,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"qux\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 17,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"nested_mat_cx2\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 3,\n            )),\n            ty: 20,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Literal(U32(0)),\n        Compose(\n            ty: 1,\n            components: [\n                1,\n                2,\n                3,\n            ],\n        ),\n        Literal(I32(0)),\n        Compose(\n            ty: 3,\n            components: [\n                0,\n                4,\n                5,\n            ],\n        ),\n    ],\n    functions: [\n        (\n            name: Some(\"test_matrix_within_struct_accesses\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"idx\"),\n                    ty: 2,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"t\"),\n                    ty: 16,\n                    init: Some(48),\n                ),\n            ],\n            expressions: [\n                Literal(I32(1)),\n                LocalVariable(0),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 3,\n                    right: 2,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 5,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 6,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 8,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 9,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 10,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 12,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 13,\n                    index: 14,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 17,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 18,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 19,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 20,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 22,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 23,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 24,\n                    index: 25,\n                ),\n                Load(\n                    pointer: 26,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 28,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 29,\n                    index: 30,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 32,\n                ),\n                GlobalVariable(2),\n                AccessIndex(\n                    base: 34,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 35,\n                    index: 36,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 37,\n                    index: 38,\n                ),\n                Load(\n                    pointer: 39,\n                ),\n                Literal(F32(1.0)),\n                Splat(\n                    size: Bi,\n                    value: 41,\n                ),\n                Literal(F32(2.0)),\n                Splat(\n                    size: Bi,\n                    value: 43,\n                ),\n                Literal(F32(3.0)),\n                Splat(\n                    size: Bi,\n                    value: 45,\n                ),\n                Compose(\n                    ty: 15,\n                    components: [\n                        42,\n                        44,\n                        46,\n                    ],\n                ),\n                Compose(\n                    ty: 16,\n                    components: [\n                        47,\n                    ],\n                ),\n                LocalVariable(1),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Add,\n                    left: 51,\n                    right: 50,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Bi,\n                    value: 54,\n                ),\n                Literal(F32(5.0)),\n                Splat(\n                    size: Bi,\n                    value: 56,\n                ),\n                Literal(F32(4.0)),\n                Splat(\n                    size: Bi,\n                    value: 58,\n                ),\n                Compose(\n                    ty: 15,\n                    components: [\n                        55,\n                        57,\n                        59,\n                    ],\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 61,\n                    index: 0,\n                ),\n                Literal(F32(9.0)),\n                Splat(\n                    size: Bi,\n                    value: 63,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 65,\n                    index: 66,\n                ),\n                Literal(F32(90.0)),\n                Splat(\n                    size: Bi,\n                    value: 68,\n                ),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 70,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 71,\n                    index: 1,\n                ),\n                Literal(F32(10.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 74,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 75,\n                    index: 76,\n                ),\n                Literal(F32(20.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 79,\n                    index: 80,\n                ),\n                AccessIndex(\n                    base: 81,\n                    index: 1,\n                ),\n                Literal(F32(30.0)),\n                AccessIndex(\n                    base: 49,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 84,\n                    index: 85,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 86,\n                    index: 87,\n                ),\n                Literal(F32(40.0)),\n            ],\n            named_expressions: {\n                7: \"l0\",\n                11: \"l1\",\n                16: \"l2\",\n                21: \"l3\",\n                27: \"l4\",\n                33: \"l5\",\n                40: \"l6\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 4,\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Emit((\n                    start: 9,\n                    end: 10,\n                )),\n                Emit((\n                    start: 10,\n                    end: 12,\n                )),\n                Emit((\n                    start: 13,\n                    end: 17,\n                )),\n                Emit((\n                    start: 18,\n                    end: 19,\n                )),\n                Emit((\n                    start: 19,\n                    end: 20,\n                )),\n                Emit((\n                    start: 20,\n                    end: 22,\n                )),\n                Emit((\n                    start: 23,\n                    end: 24,\n                )),\n                Emit((\n                    start: 24,\n                    end: 28,\n                )),\n                Emit((\n                    start: 29,\n                    end: 32,\n                )),\n                Emit((\n                    start: 32,\n                    end: 34,\n                )),\n                Emit((\n                    start: 35,\n                    end: 41,\n                )),\n                Emit((\n                    start: 42,\n                    end: 43,\n                )),\n                Emit((\n                    start: 44,\n                    end: 45,\n                )),\n                Emit((\n                    start: 46,\n                    end: 49,\n                )),\n                Emit((\n                    start: 51,\n                    end: 53,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 52,\n                ),\n                Emit((\n                    start: 53,\n                    end: 54,\n                )),\n                Emit((\n                    start: 55,\n                    end: 56,\n                )),\n                Emit((\n                    start: 57,\n                    end: 58,\n                )),\n                Emit((\n                    start: 59,\n                    end: 61,\n                )),\n                Store(\n                    pointer: 53,\n                    value: 60,\n                ),\n                Emit((\n                    start: 61,\n                    end: 62,\n                )),\n                Emit((\n                    start: 62,\n                    end: 63,\n                )),\n                Emit((\n                    start: 64,\n                    end: 65,\n                )),\n                Store(\n                    pointer: 62,\n                    value: 64,\n                ),\n                Emit((\n                    start: 65,\n                    end: 68,\n                )),\n                Emit((\n                    start: 69,\n                    end: 70,\n                )),\n                Store(\n                    pointer: 67,\n                    value: 69,\n                ),\n                Emit((\n                    start: 70,\n                    end: 71,\n                )),\n                Emit((\n                    start: 71,\n                    end: 72,\n                )),\n                Emit((\n                    start: 72,\n                    end: 73,\n                )),\n                Store(\n                    pointer: 72,\n                    value: 73,\n                ),\n                Emit((\n                    start: 74,\n                    end: 75,\n                )),\n                Emit((\n                    start: 75,\n                    end: 78,\n                )),\n                Store(\n                    pointer: 77,\n                    value: 78,\n                ),\n                Emit((\n                    start: 79,\n                    end: 82,\n                )),\n                Emit((\n                    start: 82,\n                    end: 83,\n                )),\n                Store(\n                    pointer: 82,\n                    value: 83,\n                ),\n                Emit((\n                    start: 84,\n                    end: 89,\n                )),\n                Store(\n                    pointer: 88,\n                    value: 89,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"test_matrix_within_array_within_struct_accesses\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"idx\"),\n                    ty: 2,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"t\"),\n                    ty: 20,\n                    init: Some(52),\n                ),\n            ],\n            expressions: [\n                Literal(I32(1)),\n                LocalVariable(0),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Subtract,\n                    left: 3,\n                    right: 2,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 5,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 6,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 8,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 9,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 10,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 12,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 13,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 14,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 17,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 18,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 19,\n                    index: 20,\n                ),\n                Load(\n                    pointer: 21,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 23,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 24,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 25,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 26,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 27,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 29,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 30,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 31,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 32,\n                    index: 33,\n                ),\n                Load(\n                    pointer: 34,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 36,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 37,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 38,\n                    index: 39,\n                ),\n                AccessIndex(\n                    base: 40,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 41,\n                ),\n                GlobalVariable(4),\n                AccessIndex(\n                    base: 43,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 44,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 45,\n                    index: 46,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 47,\n                    index: 48,\n                ),\n                Load(\n                    pointer: 49,\n                ),\n                ZeroValue(19),\n                Compose(\n                    ty: 20,\n                    components: [\n                        51,\n                    ],\n                ),\n                LocalVariable(1),\n                Literal(I32(1)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Add,\n                    left: 55,\n                    right: 54,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                ZeroValue(19),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 59,\n                    index: 0,\n                ),\n                Literal(F32(8.0)),\n                Splat(\n                    size: Bi,\n                    value: 61,\n                ),\n                Literal(F32(7.0)),\n                Splat(\n                    size: Bi,\n                    value: 63,\n                ),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Bi,\n                    value: 65,\n                ),\n                Literal(F32(5.0)),\n                Splat(\n                    size: Bi,\n                    value: 67,\n                ),\n                Compose(\n                    ty: 18,\n                    components: [\n                        62,\n                        64,\n                        66,\n                        68,\n                    ],\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 70,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 71,\n                    index: 0,\n                ),\n                Literal(F32(9.0)),\n                Splat(\n                    size: Bi,\n                    value: 73,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 75,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 76,\n                    index: 77,\n                ),\n                Literal(F32(90.0)),\n                Splat(\n                    size: Bi,\n                    value: 79,\n                ),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 81,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 82,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 83,\n                    index: 1,\n                ),\n                Literal(F32(10.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 86,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 87,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 88,\n                    index: 89,\n                ),\n                Literal(F32(20.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 92,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 93,\n                    index: 94,\n                ),\n                AccessIndex(\n                    base: 95,\n                    index: 1,\n                ),\n                Literal(F32(30.0)),\n                AccessIndex(\n                    base: 53,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 98,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 99,\n                    index: 100,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Access(\n                    base: 101,\n                    index: 102,\n                ),\n                Literal(F32(40.0)),\n            ],\n            named_expressions: {\n                7: \"l0\",\n                11: \"l1\",\n                16: \"l2\",\n                22: \"l3\",\n                28: \"l4\",\n                35: \"l5\",\n                42: \"l6\",\n                50: \"l7\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 4,\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Emit((\n                    start: 9,\n                    end: 10,\n                )),\n                Emit((\n                    start: 10,\n                    end: 12,\n                )),\n                Emit((\n                    start: 13,\n                    end: 14,\n                )),\n                Emit((\n                    start: 14,\n                    end: 15,\n                )),\n                Emit((\n                    start: 15,\n                    end: 17,\n                )),\n                Emit((\n                    start: 18,\n                    end: 19,\n                )),\n                Emit((\n                    start: 19,\n                    end: 23,\n                )),\n                Emit((\n                    start: 24,\n                    end: 25,\n                )),\n                Emit((\n                    start: 25,\n                    end: 26,\n                )),\n                Emit((\n                    start: 26,\n                    end: 27,\n                )),\n                Emit((\n                    start: 27,\n                    end: 29,\n                )),\n                Emit((\n                    start: 30,\n                    end: 31,\n                )),\n                Emit((\n                    start: 31,\n                    end: 32,\n                )),\n                Emit((\n                    start: 32,\n                    end: 36,\n                )),\n                Emit((\n                    start: 37,\n                    end: 38,\n                )),\n                Emit((\n                    start: 38,\n                    end: 41,\n                )),\n                Emit((\n                    start: 41,\n                    end: 43,\n                )),\n                Emit((\n                    start: 44,\n                    end: 45,\n                )),\n                Emit((\n                    start: 45,\n                    end: 51,\n                )),\n                Emit((\n                    start: 52,\n                    end: 53,\n                )),\n                Emit((\n                    start: 55,\n                    end: 57,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 56,\n                ),\n                Emit((\n                    start: 57,\n                    end: 58,\n                )),\n                Store(\n                    pointer: 57,\n                    value: 58,\n                ),\n                Emit((\n                    start: 59,\n                    end: 60,\n                )),\n                Emit((\n                    start: 60,\n                    end: 61,\n                )),\n                Emit((\n                    start: 62,\n                    end: 63,\n                )),\n                Emit((\n                    start: 64,\n                    end: 65,\n                )),\n                Emit((\n                    start: 66,\n                    end: 67,\n                )),\n                Emit((\n                    start: 68,\n                    end: 70,\n                )),\n                Store(\n                    pointer: 60,\n                    value: 69,\n                ),\n                Emit((\n                    start: 70,\n                    end: 71,\n                )),\n                Emit((\n                    start: 71,\n                    end: 72,\n                )),\n                Emit((\n                    start: 72,\n                    end: 73,\n                )),\n                Emit((\n                    start: 74,\n                    end: 75,\n                )),\n                Store(\n                    pointer: 72,\n                    value: 74,\n                ),\n                Emit((\n                    start: 75,\n                    end: 76,\n                )),\n                Emit((\n                    start: 76,\n                    end: 79,\n                )),\n                Emit((\n                    start: 80,\n                    end: 81,\n                )),\n                Store(\n                    pointer: 78,\n                    value: 80,\n                ),\n                Emit((\n                    start: 81,\n                    end: 82,\n                )),\n                Emit((\n                    start: 82,\n                    end: 83,\n                )),\n                Emit((\n                    start: 83,\n                    end: 84,\n                )),\n                Emit((\n                    start: 84,\n                    end: 85,\n                )),\n                Store(\n                    pointer: 84,\n                    value: 85,\n                ),\n                Emit((\n                    start: 86,\n                    end: 87,\n                )),\n                Emit((\n                    start: 87,\n                    end: 88,\n                )),\n                Emit((\n                    start: 88,\n                    end: 91,\n                )),\n                Store(\n                    pointer: 90,\n                    value: 91,\n                ),\n                Emit((\n                    start: 92,\n                    end: 93,\n                )),\n                Emit((\n                    start: 93,\n                    end: 96,\n                )),\n                Emit((\n                    start: 96,\n                    end: 97,\n                )),\n                Store(\n                    pointer: 96,\n                    value: 97,\n                ),\n                Emit((\n                    start: 98,\n                    end: 99,\n                )),\n                Emit((\n                    start: 99,\n                    end: 104,\n                )),\n                Store(\n                    pointer: 103,\n                    value: 104,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"read_from_private\"),\n            arguments: [\n                (\n                    name: Some(\"foo\"),\n                    ty: 21,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 5,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Load(\n                    pointer: 0,\n                ),\n            ],\n            named_expressions: {\n                0: \"foo\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Return(\n                    value: Some(1),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"test_arr_as_arg\"),\n            arguments: [\n                (\n                    name: Some(\"a\"),\n                    ty: 23,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 5,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 4,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 9,\n                ),\n            ],\n            named_expressions: {\n                0: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_through_ptr_fn\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 26,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(U32(42)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Store(\n                    pointer: 0,\n                    value: 1,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_array_through_ptr_fn\"),\n            arguments: [\n                (\n                    name: Some(\"foo\"),\n                    ty: 28,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(F32(1.0)),\n                Splat(\n                    size: Quad,\n                    value: 1,\n                ),\n                Literal(F32(2.0)),\n                Splat(\n                    size: Quad,\n                    value: 3,\n                ),\n                Compose(\n                    ty: 27,\n                    components: [\n                        2,\n                        4,\n                    ],\n                ),\n            ],\n            named_expressions: {\n                0: \"foo\",\n            },\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Emit((\n                    start: 4,\n                    end: 6,\n                )),\n                Store(\n                    pointer: 0,\n                    value: 5,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_through_ptr\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"val\"),\n                    ty: 0,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"arr\"),\n                    ty: 27,\n                    init: Some(6),\n                ),\n            ],\n            expressions: [\n                Literal(U32(33)),\n                LocalVariable(0),\n                Literal(F32(6.0)),\n                Splat(\n                    size: Quad,\n                    value: 2,\n                ),\n                Literal(F32(7.0)),\n                Splat(\n                    size: Quad,\n                    value: 4,\n                ),\n                Compose(\n                    ty: 27,\n                    components: [\n                        3,\n                        5,\n                    ],\n                ),\n                LocalVariable(1),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 4,\n                    arguments: [\n                        1,\n                    ],\n                    result: None,\n                ),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 3,\n                    end: 4,\n                )),\n                Emit((\n                    start: 5,\n                    end: 7,\n                )),\n                Call(\n                    function: 5,\n                    arguments: [\n                        7,\n                    ],\n                    result: None,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fetch_arg_ptr_member\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 30,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_arg_ptr_member\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 30,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                Literal(U32(10)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 2,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"fetch_arg_ptr_array_element\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 32,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_arg_ptr_array_element\"),\n            arguments: [\n                (\n                    name: Some(\"p\"),\n                    ty: 32,\n                    binding: None,\n                ),\n            ],\n            result: None,\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                Literal(U32(10)),\n            ],\n            named_expressions: {\n                0: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 1,\n                    value: 2,\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"assign_to_ptr_components\"),\n            arguments: [],\n            result: None,\n            local_variables: [\n                (\n                    name: Some(\"s1\"),\n                    ty: 29,\n                    init: None,\n                ),\n                (\n                    name: Some(\"a1\"),\n                    ty: 31,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                LocalVariable(0),\n                CallResult(7),\n                LocalVariable(1),\n                CallResult(9),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 8,\n                    arguments: [\n                        0,\n                    ],\n                    result: None,\n                ),\n                Call(\n                    function: 7,\n                    arguments: [\n                        0,\n                    ],\n                    result: Some(1),\n                ),\n                Call(\n                    function: 10,\n                    arguments: [\n                        2,\n                    ],\n                    result: None,\n                ),\n                Call(\n                    function: 9,\n                    arguments: [\n                        2,\n                    ],\n                    result: Some(3),\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_ptr\"),\n            arguments: [\n                (\n                    name: Some(\"value\"),\n                    ty: 33,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 33,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"a\"),\n                    ty: 34,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                Compose(\n                    ty: 34,\n                    components: [\n                        0,\n                    ],\n                ),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                0: \"value\",\n                2: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Store(\n                    pointer: 2,\n                    value: 1,\n                ),\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Return(\n                    value: Some(4),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"member_ptr\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"s\"),\n                    ty: 35,\n                    init: Some(1),\n                ),\n            ],\n            expressions: [\n                Literal(I32(42)),\n                Compose(\n                    ty: 35,\n                    components: [\n                        0,\n                    ],\n                ),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 2,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                2: \"p\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Return(\n                    value: Some(4),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"let_members_of_members\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                ZeroValue(37),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 1,\n                ),\n                As(\n                    expr: 2,\n                    kind: Uint,\n                    convert: Some(4),\n                ),\n                Binary(\n                    op: NotEqual,\n                    left: 3,\n                    right: 4,\n                ),\n                AccessIndex(\n                    base: 0,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 6,\n                    index: 0,\n                ),\n            ],\n            named_expressions: {\n                0: \"thing\",\n                1: \"inner\",\n                2: \"delishus\",\n            },\n            body: [\n                Emit((\n                    start: 1,\n                    end: 2,\n                )),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Emit((\n                    start: 3,\n                    end: 6,\n                )),\n                If(\n                    condition: 5,\n                    accept: [],\n                    reject: [],\n                ),\n                Emit((\n                    start: 6,\n                    end: 8,\n                )),\n                Return(\n                    value: Some(7),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"var_members_of_members\"),\n            arguments: [],\n            result: Some((\n                ty: 2,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"thing\"),\n                    ty: 37,\n                    init: Some(0),\n                ),\n                (\n                    name: Some(\"inner\"),\n                    ty: 36,\n                    init: None,\n                ),\n                (\n                    name: Some(\"delishus\"),\n                    ty: 2,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                ZeroValue(37),\n                LocalVariable(0),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 2,\n                ),\n                LocalVariable(1),\n                AccessIndex(\n                    base: 4,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 5,\n                ),\n                LocalVariable(2),\n                AccessIndex(\n                    base: 1,\n                    index: 1,\n                ),\n                Load(\n                    pointer: 8,\n                ),\n                Load(\n                    pointer: 7,\n                ),\n                As(\n                    expr: 10,\n                    kind: Uint,\n                    convert: Some(4),\n                ),\n                Binary(\n                    op: NotEqual,\n                    left: 9,\n                    right: 11,\n                ),\n                AccessIndex(\n                    base: 1,\n                    index: 0,\n                ),\n                AccessIndex(\n                    base: 13,\n                    index: 0,\n                ),\n                Load(\n                    pointer: 14,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 2,\n                    end: 4,\n                )),\n                Store(\n                    pointer: 4,\n                    value: 3,\n                ),\n                Emit((\n                    start: 5,\n                    end: 7,\n                )),\n                Store(\n                    pointer: 7,\n                    value: 6,\n                ),\n                Emit((\n                    start: 8,\n                    end: 13,\n                )),\n                If(\n                    condition: 12,\n                    accept: [],\n                    reject: [],\n                ),\n                Emit((\n                    start: 13,\n                    end: 16,\n                )),\n                Return(\n                    value: Some(15),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"foo_vert\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_vert\"),\n                arguments: [\n                    (\n                        name: Some(\"vi\"),\n                        ty: 0,\n                        binding: Some(BuiltIn(VertexIndex)),\n                    ),\n                ],\n                result: Some((\n                    ty: 24,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [\n                    (\n                        name: Some(\"foo\"),\n                        ty: 5,\n                        init: Some(1),\n                    ),\n                    (\n                        name: Some(\"c2\"),\n                        ty: 25,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    FunctionArgument(0),\n                    Literal(F32(0.0)),\n                    LocalVariable(0),\n                    Load(\n                        pointer: 2,\n                    ),\n                    Literal(F32(1.0)),\n                    GlobalVariable(0),\n                    Load(\n                        pointer: 5,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 10,\n                        index: 4,\n                    ),\n                    Load(\n                        pointer: 11,\n                    ),\n                    Literal(U32(3)),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 14,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 15,\n                        index: 13,\n                    ),\n                    AccessIndex(\n                        base: 16,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 17,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 19,\n                        index: 5,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 21,\n                        index: 5,\n                    ),\n                    ArrayLength(22),\n                    Literal(U32(2)),\n                    Binary(\n                        op: Subtract,\n                        left: 23,\n                        right: 24,\n                    ),\n                    Access(\n                        base: 20,\n                        index: 25,\n                    ),\n                    AccessIndex(\n                        base: 26,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 27,\n                    ),\n                    GlobalVariable(3),\n                    Load(\n                        pointer: 29,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 31,\n                        index: 5,\n                    ),\n                    AccessIndex(\n                        base: 32,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 33,\n                        index: 0,\n                    ),\n                    CallResult(2),\n                    As(\n                        expr: 18,\n                        kind: Sint,\n                        convert: Some(4),\n                    ),\n                    Literal(I32(3)),\n                    Literal(I32(4)),\n                    Literal(I32(5)),\n                    Compose(\n                        ty: 25,\n                        components: [\n                            28,\n                            36,\n                            37,\n                            38,\n                            39,\n                        ],\n                    ),\n                    LocalVariable(1),\n                    Literal(U32(1)),\n                    Binary(\n                        op: Add,\n                        left: 0,\n                        right: 42,\n                    ),\n                    Access(\n                        base: 41,\n                        index: 43,\n                    ),\n                    Literal(I32(42)),\n                    Access(\n                        base: 41,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 46,\n                    ),\n                    ZeroValue(23),\n                    CallResult(3),\n                    Splat(\n                        size: Quad,\n                        value: 47,\n                    ),\n                    As(\n                        expr: 50,\n                        kind: Float,\n                        convert: Some(4),\n                    ),\n                    Binary(\n                        op: Multiply,\n                        left: 9,\n                        right: 51,\n                    ),\n                    Literal(F32(2.0)),\n                    Compose(\n                        ty: 24,\n                        components: [\n                            52,\n                            53,\n                        ],\n                    ),\n                ],\n                named_expressions: {\n                    0: \"vi\",\n                    3: \"baz\",\n                    6: \"phony\",\n                    9: \"_matrix\",\n                    12: \"arr\",\n                    13: \"index\",\n                    18: \"b\",\n                    28: \"a\",\n                    30: \"c\",\n                    34: \"data_pointer\",\n                    35: \"foo_value\",\n                    47: \"value\",\n                },\n                body: [\n                    Emit((\n                        start: 3,\n                        end: 4,\n                    )),\n                    Store(\n                        pointer: 2,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Emit((\n                        start: 8,\n                        end: 10,\n                    )),\n                    Emit((\n                        start: 11,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 15,\n                        end: 19,\n                    )),\n                    Emit((\n                        start: 20,\n                        end: 21,\n                    )),\n                    Emit((\n                        start: 22,\n                        end: 24,\n                    )),\n                    Emit((\n                        start: 25,\n                        end: 29,\n                    )),\n                    Emit((\n                        start: 30,\n                        end: 31,\n                    )),\n                    Emit((\n                        start: 32,\n                        end: 33,\n                    )),\n                    Emit((\n                        start: 33,\n                        end: 35,\n                    )),\n                    Call(\n                        function: 2,\n                        arguments: [\n                            2,\n                        ],\n                        result: Some(35),\n                    ),\n                    Emit((\n                        start: 36,\n                        end: 37,\n                    )),\n                    Emit((\n                        start: 40,\n                        end: 41,\n                    )),\n                    Store(\n                        pointer: 41,\n                        value: 40,\n                    ),\n                    Emit((\n                        start: 43,\n                        end: 45,\n                    )),\n                    Store(\n                        pointer: 44,\n                        value: 45,\n                    ),\n                    Emit((\n                        start: 46,\n                        end: 48,\n                    )),\n                    Call(\n                        function: 3,\n                        arguments: [\n                            48,\n                        ],\n                        result: Some(49),\n                    ),\n                    Emit((\n                        start: 50,\n                        end: 53,\n                    )),\n                    Emit((\n                        start: 54,\n                        end: 55,\n                    )),\n                    Return(\n                        value: Some(54),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"foo_frag\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_frag\"),\n                arguments: [],\n                result: Some((\n                    ty: 24,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: Some(Perspective),\n                        sampling: Some(Center),\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 1,\n                        index: 1,\n                    ),\n                    AccessIndex(\n                        base: 2,\n                        index: 2,\n                    ),\n                    Literal(F32(1.0)),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Tri,\n                        value: 7,\n                    ),\n                    Literal(F32(1.0)),\n                    Splat(\n                        size: Tri,\n                        value: 9,\n                    ),\n                    Literal(F32(2.0)),\n                    Splat(\n                        size: Tri,\n                        value: 11,\n                    ),\n                    Literal(F32(3.0)),\n                    Splat(\n                        size: Tri,\n                        value: 13,\n                    ),\n                    Compose(\n                        ty: 6,\n                        components: [\n                            8,\n                            10,\n                            12,\n                            14,\n                        ],\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 16,\n                        index: 4,\n                    ),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 18,\n                    ),\n                    Literal(U32(1)),\n                    Splat(\n                        size: Bi,\n                        value: 20,\n                    ),\n                    Compose(\n                        ty: 12,\n                        components: [\n                            19,\n                            21,\n                        ],\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 23,\n                        index: 5,\n                    ),\n                    AccessIndex(\n                        base: 24,\n                        index: 1,\n                    ),\n                    AccessIndex(\n                        base: 25,\n                        index: 0,\n                    ),\n                    Literal(I32(1)),\n                    GlobalVariable(3),\n                    ZeroValue(17),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 30,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Emit((\n                        start: 2,\n                        end: 4,\n                    )),\n                    Store(\n                        pointer: 3,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 9,\n                    )),\n                    Emit((\n                        start: 10,\n                        end: 11,\n                    )),\n                    Emit((\n                        start: 12,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 14,\n                        end: 16,\n                    )),\n                    Store(\n                        pointer: 6,\n                        value: 15,\n                    ),\n                    Emit((\n                        start: 17,\n                        end: 18,\n                    )),\n                    Emit((\n                        start: 19,\n                        end: 20,\n                    )),\n                    Emit((\n                        start: 21,\n                        end: 23,\n                    )),\n                    Store(\n                        pointer: 17,\n                        value: 22,\n                    ),\n                    Emit((\n                        start: 24,\n                        end: 25,\n                    )),\n                    Emit((\n                        start: 25,\n                        end: 27,\n                    )),\n                    Store(\n                        pointer: 26,\n                        value: 27,\n                    ),\n                    Store(\n                        pointer: 28,\n                        value: 29,\n                    ),\n                    Emit((\n                        start: 31,\n                        end: 32,\n                    )),\n                    Return(\n                        value: Some(31),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"foo_compute\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo_compute\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    Literal(Bool(true)),\n                    CallResult(12),\n                    CallResult(13),\n                    CallResult(14),\n                    CallResult(15),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 6,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 11,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 12,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Call(\n                        function: 13,\n                        arguments: [],\n                        result: Some(2),\n                    ),\n                    Call(\n                        function: 14,\n                        arguments: [],\n                        result: Some(3),\n                    ),\n                    Call(\n                        function: 15,\n                        arguments: [],\n                        result: Some(4),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-collatz.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Dynamic,\n                stride: 4,\n            ),\n        ),\n        (\n            name: Some(\"PrimeIndices\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"data\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"v_indices\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"collatz_iterations\"),\n            arguments: [\n                (\n                    name: Some(\"n_base\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"n\"),\n                    ty: 0,\n                    init: None,\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    init: Some(2),\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                LocalVariable(0),\n                Literal(U32(0)),\n                LocalVariable(1),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Greater,\n                    left: 4,\n                    right: 5,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(2)),\n                Binary(\n                    op: Modulo,\n                    left: 7,\n                    right: 8,\n                ),\n                Literal(U32(0)),\n                Binary(\n                    op: Equal,\n                    left: 9,\n                    right: 10,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(2)),\n                Binary(\n                    op: Divide,\n                    left: 12,\n                    right: 13,\n                ),\n                Literal(U32(3)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 15,\n                    right: 16,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Add,\n                    left: 17,\n                    right: 18,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Add,\n                    left: 20,\n                    right: 21,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                0: \"n_base\",\n            },\n            body: [\n                Store(\n                    pointer: 1,\n                    value: 0,\n                ),\n                Loop(\n                    body: [\n                        Emit((\n                            start: 4,\n                            end: 5,\n                        )),\n                        Emit((\n                            start: 6,\n                            end: 7,\n                        )),\n                        If(\n                            condition: 6,\n                            accept: [],\n                            reject: [\n                                Break,\n                            ],\n                        ),\n                        Block([\n                            Emit((\n                                start: 7,\n                                end: 8,\n                            )),\n                            Emit((\n                                start: 9,\n                                end: 10,\n                            )),\n                            Emit((\n                                start: 11,\n                                end: 12,\n                            )),\n                            If(\n                                condition: 11,\n                                accept: [\n                                    Emit((\n                                        start: 12,\n                                        end: 13,\n                                    )),\n                                    Emit((\n                                        start: 14,\n                                        end: 15,\n                                    )),\n                                    Store(\n                                        pointer: 1,\n                                        value: 14,\n                                    ),\n                                ],\n                                reject: [\n                                    Emit((\n                                        start: 16,\n                                        end: 18,\n                                    )),\n                                    Emit((\n                                        start: 19,\n                                        end: 20,\n                                    )),\n                                    Store(\n                                        pointer: 1,\n                                        value: 19,\n                                    ),\n                                ],\n                            ),\n                            Emit((\n                                start: 20,\n                                end: 21,\n                            )),\n                            Emit((\n                                start: 22,\n                                end: 23,\n                            )),\n                            Store(\n                                pointer: 3,\n                                value: 22,\n                            ),\n                        ]),\n                    ],\n                    continuing: [],\n                    break_if: None,\n                ),\n                Emit((\n                    start: 23,\n                    end: 24,\n                )),\n                Return(\n                    value: Some(23),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [\n                    (\n                        name: Some(\"global_id\"),\n                        ty: 3,\n                        binding: Some(BuiltIn(GlobalInvocationId)),\n                    ),\n                ],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 1,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 2,\n                        index: 3,\n                    ),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 6,\n                        index: 7,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    CallResult(0),\n                ],\n                named_expressions: {\n                    0: \"global_id\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 5,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 10,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [\n                            9,\n                        ],\n                        result: Some(10),\n                    ),\n                    Store(\n                        pointer: 4,\n                        value: 10,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-collatz.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Dynamic,\n                stride: 4,\n            ),\n        ),\n        (\n            name: Some(\"PrimeIndices\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"data\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"v_indices\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"collatz_iterations\"),\n            arguments: [\n                (\n                    name: Some(\"n_base\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"n\"),\n                    ty: 0,\n                    init: None,\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    init: Some(2),\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                LocalVariable(0),\n                Literal(U32(0)),\n                LocalVariable(1),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Greater,\n                    left: 4,\n                    right: 5,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(2)),\n                Binary(\n                    op: Modulo,\n                    left: 7,\n                    right: 8,\n                ),\n                Literal(U32(0)),\n                Binary(\n                    op: Equal,\n                    left: 9,\n                    right: 10,\n                ),\n                Load(\n                    pointer: 1,\n                ),\n                Literal(U32(2)),\n                Binary(\n                    op: Divide,\n                    left: 12,\n                    right: 13,\n                ),\n                Literal(U32(3)),\n                Load(\n                    pointer: 1,\n                ),\n                Binary(\n                    op: Multiply,\n                    left: 15,\n                    right: 16,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Add,\n                    left: 17,\n                    right: 18,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n                Literal(U32(1)),\n                Binary(\n                    op: Add,\n                    left: 20,\n                    right: 21,\n                ),\n                Load(\n                    pointer: 3,\n                ),\n            ],\n            named_expressions: {\n                0: \"n_base\",\n            },\n            body: [\n                Store(\n                    pointer: 1,\n                    value: 0,\n                ),\n                Loop(\n                    body: [\n                        Emit((\n                            start: 4,\n                            end: 5,\n                        )),\n                        Emit((\n                            start: 6,\n                            end: 7,\n                        )),\n                        If(\n                            condition: 6,\n                            accept: [],\n                            reject: [\n                                Break,\n                            ],\n                        ),\n                        Block([\n                            Emit((\n                                start: 7,\n                                end: 8,\n                            )),\n                            Emit((\n                                start: 9,\n                                end: 10,\n                            )),\n                            Emit((\n                                start: 11,\n                                end: 12,\n                            )),\n                            If(\n                                condition: 11,\n                                accept: [\n                                    Emit((\n                                        start: 12,\n                                        end: 13,\n                                    )),\n                                    Emit((\n                                        start: 14,\n                                        end: 15,\n                                    )),\n                                    Store(\n                                        pointer: 1,\n                                        value: 14,\n                                    ),\n                                ],\n                                reject: [\n                                    Emit((\n                                        start: 16,\n                                        end: 18,\n                                    )),\n                                    Emit((\n                                        start: 19,\n                                        end: 20,\n                                    )),\n                                    Store(\n                                        pointer: 1,\n                                        value: 19,\n                                    ),\n                                ],\n                            ),\n                            Emit((\n                                start: 20,\n                                end: 21,\n                            )),\n                            Emit((\n                                start: 22,\n                                end: 23,\n                            )),\n                            Store(\n                                pointer: 3,\n                                value: 22,\n                            ),\n                        ]),\n                    ],\n                    continuing: [],\n                    break_if: None,\n                ),\n                Emit((\n                    start: 23,\n                    end: 24,\n                )),\n                Return(\n                    value: Some(23),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [\n                    (\n                        name: Some(\"global_id\"),\n                        ty: 3,\n                        binding: Some(BuiltIn(GlobalInvocationId)),\n                    ),\n                ],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 1,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 2,\n                        index: 3,\n                    ),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    Access(\n                        base: 6,\n                        index: 7,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    CallResult(0),\n                ],\n                named_expressions: {\n                    0: \"global_id\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 5,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 10,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [\n                            9,\n                        ],\n                        result: Some(10),\n                    ),\n                    Store(\n                        pointer: 4,\n                        value: 10,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-const_assert.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"g_false\"),\n            ty: 0,\n            init: 0,\n        ),\n    ],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [\n        Literal(Bool(false)),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            name: \"foo\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-const_assert.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"g_false\"),\n            ty: 0,\n            init: 0,\n        ),\n    ],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [\n        Literal(Bool(false)),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            name: \"foo\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"foo\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-diagnostic-filter.compact.ron",
    "content": "(\n    types: [],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"thing\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: Some(0),\n        ),\n        (\n            name: Some(\"with_diagnostic\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: Some(1),\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: Some(0),\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [\n        (\n            inner: (\n                new_severity: Off,\n                triggering_rule: Standard(DerivativeUniformity),\n            ),\n            parent: None,\n        ),\n        (\n            inner: (\n                new_severity: Warning,\n                triggering_rule: Standard(DerivativeUniformity),\n            ),\n            parent: Some(0),\n        ),\n    ],\n    diagnostic_filter_leaf: Some(0),\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-diagnostic-filter.ron",
    "content": "(\n    types: [],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"thing\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: Some(0),\n        ),\n        (\n            name: Some(\"with_diagnostic\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: Some(1),\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: Some(0),\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [\n        (\n            inner: (\n                new_severity: Off,\n                triggering_rule: Standard(DerivativeUniformity),\n            ),\n            parent: None,\n        ),\n        (\n            inner: (\n                new_severity: Warning,\n                triggering_rule: Standard(DerivativeUniformity),\n            ),\n            parent: Some(0),\n        ),\n    ],\n    diagnostic_filter_leaf: Some(0),\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-index-by-value.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(5),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(2),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 2,\n                size: Constant(2),\n                stride: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"index_arg_array\"),\n            arguments: [\n                (\n                    name: Some(\"a\"),\n                    ty: 1,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Access(\n                    base: 0,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"a\",\n                1: \"i\",\n            },\n            body: [\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_array\"),\n            arguments: [\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"j\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Literal(I32(1)),\n                Literal(I32(2)),\n                Compose(\n                    ty: 2,\n                    components: [\n                        2,\n                        3,\n                    ],\n                ),\n                Literal(I32(3)),\n                Literal(I32(4)),\n                Compose(\n                    ty: 2,\n                    components: [\n                        5,\n                        6,\n                    ],\n                ),\n                Compose(\n                    ty: 3,\n                    components: [\n                        4,\n                        7,\n                    ],\n                ),\n                Access(\n                    base: 8,\n                    index: 0,\n                ),\n                Access(\n                    base: 9,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"i\",\n                1: \"j\",\n                8: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 4,\n                    end: 5,\n                )),\n                Emit((\n                    start: 7,\n                    end: 9,\n                )),\n                Emit((\n                    start: 9,\n                    end: 11,\n                )),\n                Return(\n                    value: Some(10),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_matrix\"),\n            arguments: [\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"j\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 4,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Literal(F32(1.0)),\n                Literal(F32(2.0)),\n                Literal(F32(3.0)),\n                Literal(F32(4.0)),\n                Compose(\n                    ty: 6,\n                    components: [\n                        2,\n                        3,\n                    ],\n                ),\n                Compose(\n                    ty: 6,\n                    components: [\n                        4,\n                        5,\n                    ],\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        6,\n                        7,\n                    ],\n                ),\n                Access(\n                    base: 8,\n                    index: 0,\n                ),\n                Access(\n                    base: 9,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"i\",\n                1: \"j\",\n                8: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 6,\n                    end: 9,\n                )),\n                Emit((\n                    start: 9,\n                    end: 11,\n                )),\n                Return(\n                    value: Some(10),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_array_1d\"),\n            arguments: [\n                (\n                    name: Some(\"vi\"),\n                    ty: 7,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 8,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(I32(1)),\n                Literal(I32(2)),\n                Literal(I32(3)),\n                Literal(I32(4)),\n                Literal(I32(5)),\n                Compose(\n                    ty: 1,\n                    components: [\n                        1,\n                        2,\n                        3,\n                        4,\n                        5,\n                    ],\n                ),\n                Access(\n                    base: 6,\n                    index: 0,\n                ),\n                Splat(\n                    size: Quad,\n                    value: 7,\n                ),\n                As(\n                    expr: 8,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n            ],\n            named_expressions: {\n                0: \"vi\",\n                6: \"arr\",\n                7: \"value\",\n            },\n            body: [\n                Emit((\n                    start: 6,\n                    end: 7,\n                )),\n                Emit((\n                    start: 7,\n                    end: 8,\n                )),\n                Emit((\n                    start: 8,\n                    end: 10,\n                )),\n                Return(\n                    value: Some(9),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [\n                    (\n                        name: Some(\"vi\"),\n                        ty: 7,\n                        binding: Some(BuiltIn(VertexIndex)),\n                    ),\n                ],\n                result: Some((\n                    ty: 8,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    Literal(I32(3)),\n                    Literal(I32(4)),\n                    Literal(I32(5)),\n                    Compose(\n                        ty: 1,\n                        components: [\n                            1,\n                            2,\n                            3,\n                            4,\n                            5,\n                        ],\n                    ),\n                    Literal(I32(6)),\n                    CallResult(0),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    CallResult(1),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    CallResult(2),\n                    CallResult(3),\n                ],\n                named_expressions: {\n                    0: \"vi\",\n                },\n                body: [\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [\n                            6,\n                            7,\n                        ],\n                        result: Some(8),\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [\n                            9,\n                            10,\n                        ],\n                        result: Some(11),\n                    ),\n                    Call(\n                        function: 2,\n                        arguments: [\n                            12,\n                            13,\n                        ],\n                        result: Some(14),\n                    ),\n                    Call(\n                        function: 3,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(15),\n                    ),\n                    Return(\n                        value: Some(15),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-index-by-value.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(5),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(2),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 2,\n                size: Constant(2),\n                stride: 8,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"index_arg_array\"),\n            arguments: [\n                (\n                    name: Some(\"a\"),\n                    ty: 1,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Access(\n                    base: 0,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"a\",\n                1: \"i\",\n            },\n            body: [\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_array\"),\n            arguments: [\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"j\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Literal(I32(1)),\n                Literal(I32(2)),\n                Compose(\n                    ty: 2,\n                    components: [\n                        2,\n                        3,\n                    ],\n                ),\n                Literal(I32(3)),\n                Literal(I32(4)),\n                Compose(\n                    ty: 2,\n                    components: [\n                        5,\n                        6,\n                    ],\n                ),\n                Compose(\n                    ty: 3,\n                    components: [\n                        4,\n                        7,\n                    ],\n                ),\n                Access(\n                    base: 8,\n                    index: 0,\n                ),\n                Access(\n                    base: 9,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"i\",\n                1: \"j\",\n                8: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Emit((\n                    start: 4,\n                    end: 5,\n                )),\n                Emit((\n                    start: 7,\n                    end: 9,\n                )),\n                Emit((\n                    start: 9,\n                    end: 11,\n                )),\n                Return(\n                    value: Some(10),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_matrix\"),\n            arguments: [\n                (\n                    name: Some(\"i\"),\n                    ty: 0,\n                    binding: None,\n                ),\n                (\n                    name: Some(\"j\"),\n                    ty: 0,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 4,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                FunctionArgument(1),\n                Literal(F32(1.0)),\n                Literal(F32(2.0)),\n                Literal(F32(3.0)),\n                Literal(F32(4.0)),\n                Compose(\n                    ty: 6,\n                    components: [\n                        2,\n                        3,\n                    ],\n                ),\n                Compose(\n                    ty: 6,\n                    components: [\n                        4,\n                        5,\n                    ],\n                ),\n                Compose(\n                    ty: 5,\n                    components: [\n                        6,\n                        7,\n                    ],\n                ),\n                Access(\n                    base: 8,\n                    index: 0,\n                ),\n                Access(\n                    base: 9,\n                    index: 1,\n                ),\n            ],\n            named_expressions: {\n                0: \"i\",\n                1: \"j\",\n                8: \"a\",\n            },\n            body: [\n                Emit((\n                    start: 6,\n                    end: 9,\n                )),\n                Emit((\n                    start: 9,\n                    end: 11,\n                )),\n                Return(\n                    value: Some(10),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"index_let_array_1d\"),\n            arguments: [\n                (\n                    name: Some(\"vi\"),\n                    ty: 7,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 8,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                FunctionArgument(0),\n                Literal(I32(1)),\n                Literal(I32(2)),\n                Literal(I32(3)),\n                Literal(I32(4)),\n                Literal(I32(5)),\n                Compose(\n                    ty: 1,\n                    components: [\n                        1,\n                        2,\n                        3,\n                        4,\n                        5,\n                    ],\n                ),\n                Access(\n                    base: 6,\n                    index: 0,\n                ),\n                Splat(\n                    size: Quad,\n                    value: 7,\n                ),\n                As(\n                    expr: 8,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n            ],\n            named_expressions: {\n                0: \"vi\",\n                6: \"arr\",\n                7: \"value\",\n            },\n            body: [\n                Emit((\n                    start: 6,\n                    end: 7,\n                )),\n                Emit((\n                    start: 7,\n                    end: 8,\n                )),\n                Emit((\n                    start: 8,\n                    end: 10,\n                )),\n                Return(\n                    value: Some(9),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [\n                    (\n                        name: Some(\"vi\"),\n                        ty: 7,\n                        binding: Some(BuiltIn(VertexIndex)),\n                    ),\n                ],\n                result: Some((\n                    ty: 8,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [],\n                expressions: [\n                    FunctionArgument(0),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    Literal(I32(3)),\n                    Literal(I32(4)),\n                    Literal(I32(5)),\n                    Compose(\n                        ty: 1,\n                        components: [\n                            1,\n                            2,\n                            3,\n                            4,\n                            5,\n                        ],\n                    ),\n                    Literal(I32(6)),\n                    CallResult(0),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    CallResult(1),\n                    Literal(I32(1)),\n                    Literal(I32(2)),\n                    CallResult(2),\n                    CallResult(3),\n                ],\n                named_expressions: {\n                    0: \"vi\",\n                },\n                body: [\n                    Emit((\n                        start: 0,\n                        end: 0,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [\n                            6,\n                            7,\n                        ],\n                        result: Some(8),\n                    ),\n                    Call(\n                        function: 1,\n                        arguments: [\n                            9,\n                            10,\n                        ],\n                        result: Some(11),\n                    ),\n                    Call(\n                        function: 2,\n                        arguments: [\n                            12,\n                            13,\n                        ],\n                        result: Some(14),\n                    ),\n                    Call(\n                        function: 3,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(15),\n                    ),\n                    Return(\n                        value: Some(15),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-local-const.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"gb\"),\n            ty: 0,\n            init: 0,\n        ),\n        (\n            name: Some(\"gc\"),\n            ty: 1,\n            init: 1,\n        ),\n        (\n            name: Some(\"gd\"),\n            ty: 2,\n            init: 2,\n        ),\n    ],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [\n        Literal(I32(4)),\n        Literal(U32(4)),\n        Literal(F32(4.0)),\n    ],\n    functions: [\n        (\n            name: Some(\"const_in_fn\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-local-const.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"gb\"),\n            ty: 0,\n            init: 0,\n        ),\n        (\n            name: Some(\"gc\"),\n            ty: 1,\n            init: 1,\n        ),\n        (\n            name: Some(\"gd\"),\n            ty: 2,\n            init: 2,\n        ),\n    ],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [\n        Literal(I32(4)),\n        Literal(U32(4)),\n        Literal(F32(4.0)),\n    ],\n    functions: [\n        (\n            name: Some(\"const_in_fn\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Emit((\n                    start: 0,\n                    end: 0,\n                )),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-must-use.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"use_me\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                Literal(I32(10)),\n            ],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_return\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_assign_var\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"q\"),\n                    ty: 0,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                CallResult(0),\n                LocalVariable(0),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Store(\n                    pointer: 1,\n                    value: 0,\n                ),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_assign_let\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {\n                0: \"q\",\n            },\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_phony_assign\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {\n                0: \"phony\",\n            },\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    CallResult(1),\n                    CallResult(2),\n                    CallResult(3),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: Some(0),\n                    ),\n                    Call(\n                        function: 2,\n                        arguments: [],\n                        result: Some(1),\n                    ),\n                    Call(\n                        function: 3,\n                        arguments: [],\n                        result: Some(2),\n                    ),\n                    Call(\n                        function: 4,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-must-use.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"use_me\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                Literal(I32(10)),\n            ],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_return\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_assign_var\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"q\"),\n                    ty: 0,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                CallResult(0),\n                LocalVariable(0),\n                Load(\n                    pointer: 1,\n                ),\n            ],\n            named_expressions: {},\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Store(\n                    pointer: 1,\n                    value: 0,\n                ),\n                Emit((\n                    start: 2,\n                    end: 3,\n                )),\n                Return(\n                    value: Some(2),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_assign_let\"),\n            arguments: [],\n            result: Some((\n                ty: 0,\n                binding: None,\n            )),\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {\n                0: \"q\",\n            },\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: Some(0),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"use_phony_assign\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [\n                CallResult(0),\n            ],\n            named_expressions: {\n                0: \"phony\",\n            },\n            body: [\n                Call(\n                    function: 0,\n                    arguments: [],\n                    result: Some(0),\n                ),\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    CallResult(1),\n                    CallResult(2),\n                    CallResult(3),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: Some(0),\n                    ),\n                    Call(\n                        function: 2,\n                        arguments: [],\n                        result: Some(1),\n                    ),\n                    Call(\n                        function: 3,\n                        arguments: [],\n                        result: Some(2),\n                    ),\n                    Call(\n                        function: 4,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Atomic((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: Some(\"__atomic_compare_exchange_result<Uint,4>\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"old_value\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"exchanged\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 4,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {\n            AtomicCompareExchangeWeakResult((\n                kind: Uint,\n                width: 4,\n            )): 4,\n        },\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"o\"),\n            id: None,\n            ty: 0,\n            init: None,\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"a\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"f\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"f\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    Override(0),\n                    As(\n                        expr: 1,\n                        kind: Uint,\n                        convert: Some(4),\n                    ),\n                    Literal(U32(1)),\n                    AtomicResult(\n                        ty: 4,\n                        comparison: true,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Atomic(\n                        pointer: 0,\n                        fun: Exchange(\n                            compare: Some(2),\n                        ),\n                        value: 3,\n                        result: Some(4),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Sint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Atomic((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: Some(\"__atomic_compare_exchange_result<Uint,4>\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"old_value\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"exchanged\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 4,\n                    ),\n                ],\n                span: 8,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {\n            AtomicCompareExchangeWeakResult((\n                kind: Uint,\n                width: 4,\n            )): 4,\n        },\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"o\"),\n            id: None,\n            ty: 0,\n            init: None,\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"a\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"f\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"f\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    Override(0),\n                    As(\n                        expr: 1,\n                        kind: Uint,\n                        convert: Some(4),\n                    ),\n                    Literal(U32(1)),\n                    AtomicResult(\n                        ty: 4,\n                        comparison: true,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Atomic(\n                        pointer: 0,\n                        fun: Exchange(\n                            compare: Some(2),\n                        ),\n                        value: 3,\n                        result: Some(4),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides-ray-query.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: AccelerationStructure(\n                vertex_return: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: RayQuery(\n                vertex_return: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"RayDesc\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"flags\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"cull_mask\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 4,\n                    ),\n                    (\n                        name: Some(\"tmin\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 8,\n                    ),\n                    (\n                        name: Some(\"tmax\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 12,\n                    ),\n                    (\n                        name: Some(\"origin\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"dir\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 32,\n                    ),\n                ],\n                span: 48,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: Some(5),\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"o\"),\n            id: None,\n            ty: 0,\n            init: None,\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"acc_struct\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [\n                    (\n                        name: Some(\"rq\"),\n                        ty: 2,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    LocalVariable(0),\n                    Literal(U32(4)),\n                    Literal(U32(255)),\n                    Override(0),\n                    Literal(F32(17.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 3,\n                        right: 4,\n                    ),\n                    Override(0),\n                    Literal(F32(19.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 6,\n                        right: 7,\n                    ),\n                    Override(0),\n                    Literal(F32(23.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 9,\n                        right: 10,\n                    ),\n                    Splat(\n                        size: Tri,\n                        value: 11,\n                    ),\n                    Override(0),\n                    Literal(F32(29.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 13,\n                        right: 14,\n                    ),\n                    Override(0),\n                    Literal(F32(31.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 16,\n                        right: 17,\n                    ),\n                    Override(0),\n                    Literal(F32(37.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 19,\n                        right: 20,\n                    ),\n                    Compose(\n                        ty: 4,\n                        components: [\n                            15,\n                            18,\n                            21,\n                        ],\n                    ),\n                    Compose(\n                        ty: 5,\n                        components: [\n                            1,\n                            2,\n                            5,\n                            8,\n                            12,\n                            22,\n                        ],\n                    ),\n                    GlobalVariable(0),\n                    RayQueryProceedResult,\n                ],\n                named_expressions: {\n                    23: \"desc\",\n                },\n                body: [\n                    Emit((\n                        start: 5,\n                        end: 6,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 9,\n                    )),\n                    Emit((\n                        start: 11,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 15,\n                        end: 16,\n                    )),\n                    Emit((\n                        start: 18,\n                        end: 19,\n                    )),\n                    Emit((\n                        start: 21,\n                        end: 24,\n                    )),\n                    RayQuery(\n                        query: 0,\n                        fun: Initialize(\n                            acceleration_structure: 24,\n                            descriptor: 23,\n                        ),\n                    ),\n                    Loop(\n                        body: [\n                            RayQuery(\n                                query: 0,\n                                fun: Proceed(\n                                    result: 25,\n                                ),\n                            ),\n                            If(\n                                condition: 25,\n                                accept: [],\n                                reject: [\n                                    Break,\n                                ],\n                            ),\n                            Block([]),\n                        ],\n                        continuing: [],\n                        break_if: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides-ray-query.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: AccelerationStructure(\n                vertex_return: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: RayQuery(\n                vertex_return: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"RayDesc\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"flags\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"cull_mask\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 4,\n                    ),\n                    (\n                        name: Some(\"tmin\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 8,\n                    ),\n                    (\n                        name: Some(\"tmax\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 12,\n                    ),\n                    (\n                        name: Some(\"origin\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 16,\n                    ),\n                    (\n                        name: Some(\"dir\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 32,\n                    ),\n                ],\n                span: 48,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: Some(5),\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"o\"),\n            id: None,\n            ty: 0,\n            init: None,\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"acc_struct\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [\n                    (\n                        name: Some(\"rq\"),\n                        ty: 2,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    LocalVariable(0),\n                    Literal(U32(4)),\n                    Literal(U32(255)),\n                    Override(0),\n                    Literal(F32(17.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 3,\n                        right: 4,\n                    ),\n                    Override(0),\n                    Literal(F32(19.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 6,\n                        right: 7,\n                    ),\n                    Override(0),\n                    Literal(F32(23.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 9,\n                        right: 10,\n                    ),\n                    Splat(\n                        size: Tri,\n                        value: 11,\n                    ),\n                    Override(0),\n                    Literal(F32(29.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 13,\n                        right: 14,\n                    ),\n                    Override(0),\n                    Literal(F32(31.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 16,\n                        right: 17,\n                    ),\n                    Override(0),\n                    Literal(F32(37.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 19,\n                        right: 20,\n                    ),\n                    Compose(\n                        ty: 4,\n                        components: [\n                            15,\n                            18,\n                            21,\n                        ],\n                    ),\n                    Compose(\n                        ty: 5,\n                        components: [\n                            1,\n                            2,\n                            5,\n                            8,\n                            12,\n                            22,\n                        ],\n                    ),\n                    GlobalVariable(0),\n                    RayQueryProceedResult,\n                ],\n                named_expressions: {\n                    23: \"desc\",\n                },\n                body: [\n                    Emit((\n                        start: 5,\n                        end: 6,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 9,\n                    )),\n                    Emit((\n                        start: 11,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 15,\n                        end: 16,\n                    )),\n                    Emit((\n                        start: 18,\n                        end: 19,\n                    )),\n                    Emit((\n                        start: 21,\n                        end: 24,\n                    )),\n                    RayQuery(\n                        query: 0,\n                        fun: Initialize(\n                            acceleration_structure: 24,\n                            descriptor: 23,\n                        ),\n                    ),\n                    Loop(\n                        body: [\n                            RayQuery(\n                                query: 0,\n                                fun: Proceed(\n                                    result: 25,\n                                ),\n                            ),\n                            If(\n                                condition: 25,\n                                accept: [],\n                                reject: [\n                                    Break,\n                                ],\n                            ),\n                            Block([]),\n                        ],\n                        continuing: [],\n                        break_if: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"has_point_light\"),\n            id: Some(0),\n            ty: 0,\n            init: Some(0),\n        ),\n        (\n            name: Some(\"specular_param\"),\n            id: Some(1200),\n            ty: 1,\n            init: Some(1),\n        ),\n        (\n            name: Some(\"gain\"),\n            id: Some(1300),\n            ty: 1,\n            init: None,\n        ),\n        (\n            name: Some(\"width\"),\n            id: None,\n            ty: 1,\n            init: Some(2),\n        ),\n        (\n            name: Some(\"depth\"),\n            id: None,\n            ty: 1,\n            init: None,\n        ),\n        (\n            name: Some(\"height\"),\n            id: None,\n            ty: 1,\n            init: Some(5),\n        ),\n        (\n            name: Some(\"inferred_f32\"),\n            id: None,\n            ty: 1,\n            init: Some(6),\n        ),\n        (\n            name: Some(\"auto_conversion\"),\n            id: None,\n            ty: 2,\n            init: Some(7),\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"gain_x_10\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: Some(10),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"store_override\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(Bool(true)),\n        Literal(F32(2.3)),\n        Literal(F32(0.0)),\n        Override(4),\n        Literal(F32(2.0)),\n        Binary(\n            op: Multiply,\n            left: 4,\n            right: 3,\n        ),\n        Literal(F32(2.718)),\n        Literal(U32(0)),\n        Override(2),\n        Literal(F32(10.0)),\n        Binary(\n            op: Multiply,\n            left: 8,\n            right: 9,\n        ),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [\n                    (\n                        name: Some(\"t\"),\n                        ty: 1,\n                        init: Some(2),\n                    ),\n                    (\n                        name: Some(\"x\"),\n                        ty: 0,\n                        init: None,\n                    ),\n                    (\n                        name: Some(\"gain_x_100\"),\n                        ty: 1,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    Override(5),\n                    Literal(F32(5.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 0,\n                        right: 1,\n                    ),\n                    Override(0),\n                    Unary(\n                        op: LogicalNot,\n                        expr: 3,\n                    ),\n                    LocalVariable(1),\n                    GlobalVariable(0),\n                    Load(\n                        pointer: 6,\n                    ),\n                    Literal(F32(10.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 7,\n                        right: 8,\n                    ),\n                    LocalVariable(2),\n                    GlobalVariable(1),\n                    Override(2),\n                    Override(1),\n                    Override(3),\n                    Override(6),\n                    Override(7),\n                ],\n                named_expressions: {\n                    4: \"a\",\n                    13: \"phony\",\n                    14: \"phony\",\n                    15: \"phony\",\n                    16: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Emit((\n                        start: 4,\n                        end: 5,\n                    )),\n                    Store(\n                        pointer: 5,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 7,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 9,\n                        end: 10,\n                    )),\n                    Store(\n                        pointer: 10,\n                        value: 9,\n                    ),\n                    Store(\n                        pointer: 11,\n                        value: 12,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-overrides.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Bool,\n                width: 1,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [\n        (\n            name: Some(\"has_point_light\"),\n            id: Some(0),\n            ty: 0,\n            init: Some(0),\n        ),\n        (\n            name: Some(\"specular_param\"),\n            id: Some(1200),\n            ty: 1,\n            init: Some(1),\n        ),\n        (\n            name: Some(\"gain\"),\n            id: Some(1300),\n            ty: 1,\n            init: None,\n        ),\n        (\n            name: Some(\"width\"),\n            id: None,\n            ty: 1,\n            init: Some(2),\n        ),\n        (\n            name: Some(\"depth\"),\n            id: None,\n            ty: 1,\n            init: None,\n        ),\n        (\n            name: Some(\"height\"),\n            id: None,\n            ty: 1,\n            init: Some(5),\n        ),\n        (\n            name: Some(\"inferred_f32\"),\n            id: None,\n            ty: 1,\n            init: Some(6),\n        ),\n        (\n            name: Some(\"auto_conversion\"),\n            id: None,\n            ty: 2,\n            init: Some(7),\n        ),\n    ],\n    global_variables: [\n        (\n            name: Some(\"gain_x_10\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: Some(10),\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"store_override\"),\n            space: Private,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(Bool(true)),\n        Literal(F32(2.3)),\n        Literal(F32(0.0)),\n        Override(4),\n        Literal(F32(2.0)),\n        Binary(\n            op: Multiply,\n            left: 4,\n            right: 3,\n        ),\n        Literal(F32(2.718)),\n        Literal(U32(0)),\n        Override(2),\n        Literal(F32(10.0)),\n        Binary(\n            op: Multiply,\n            left: 8,\n            right: 9,\n        ),\n    ],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [\n                    (\n                        name: Some(\"t\"),\n                        ty: 1,\n                        init: Some(2),\n                    ),\n                    (\n                        name: Some(\"x\"),\n                        ty: 0,\n                        init: None,\n                    ),\n                    (\n                        name: Some(\"gain_x_100\"),\n                        ty: 1,\n                        init: None,\n                    ),\n                ],\n                expressions: [\n                    Override(5),\n                    Literal(F32(5.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 0,\n                        right: 1,\n                    ),\n                    Override(0),\n                    Unary(\n                        op: LogicalNot,\n                        expr: 3,\n                    ),\n                    LocalVariable(1),\n                    GlobalVariable(0),\n                    Load(\n                        pointer: 6,\n                    ),\n                    Literal(F32(10.0)),\n                    Binary(\n                        op: Multiply,\n                        left: 7,\n                        right: 8,\n                    ),\n                    LocalVariable(2),\n                    GlobalVariable(1),\n                    Override(2),\n                    Override(1),\n                    Override(3),\n                    Override(6),\n                    Override(7),\n                ],\n                named_expressions: {\n                    4: \"a\",\n                    13: \"phony\",\n                    14: \"phony\",\n                    15: \"phony\",\n                    16: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Emit((\n                        start: 4,\n                        end: 5,\n                    )),\n                    Store(\n                        pointer: 5,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 7,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 9,\n                        end: 10,\n                    )),\n                    Store(\n                        pointer: 10,\n                        value: 9,\n                    ),\n                    Store(\n                        pointer: 11,\n                        value: 12,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-storage-textures.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: R32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rg32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rgba32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: R32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rg32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rgba32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"s_r_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 0,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rg_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rgba_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_r_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 0,\n            )),\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rg_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 1,\n            )),\n            ty: 4,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rgba_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 2,\n            )),\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"csLoad\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"csLoad\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 1,\n                    ),\n                    ImageLoad(\n                        image: 0,\n                        coordinate: 2,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                    GlobalVariable(1),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 5,\n                    ),\n                    ImageLoad(\n                        image: 4,\n                        coordinate: 6,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                    GlobalVariable(2),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 9,\n                    ),\n                    ImageLoad(\n                        image: 8,\n                        coordinate: 10,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                ],\n                named_expressions: {\n                    3: \"phony\",\n                    7: \"phony\",\n                    11: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 4,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 10,\n                        end: 12,\n                    )),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"csStore\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"csStore\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(3),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 1,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 3,\n                    ),\n                    GlobalVariable(4),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 6,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 8,\n                    ),\n                    GlobalVariable(5),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 11,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 13,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Emit((\n                        start: 4,\n                        end: 5,\n                    )),\n                    ImageStore(\n                        image: 0,\n                        coordinate: 2,\n                        array_index: None,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 7,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 9,\n                        end: 10,\n                    )),\n                    ImageStore(\n                        image: 5,\n                        coordinate: 7,\n                        array_index: None,\n                        value: 9,\n                    ),\n                    Emit((\n                        start: 12,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 14,\n                        end: 15,\n                    )),\n                    ImageStore(\n                        image: 10,\n                        coordinate: 12,\n                        array_index: None,\n                        value: 14,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-storage-textures.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: R32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rg32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rgba32Float,\n                    access: (\"LOAD\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: R32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rg32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: Storage(\n                    format: Rgba32Float,\n                    access: (\"STORE\"),\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"s_r_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 0,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rg_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rgba_r\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 2,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_r_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 0,\n            )),\n            ty: 3,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rg_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 1,\n            )),\n            ty: 4,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"s_rgba_w\"),\n            space: Handle,\n            binding: Some((\n                group: 1,\n                binding: 2,\n            )),\n            ty: 5,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"csLoad\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"csLoad\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 1,\n                    ),\n                    ImageLoad(\n                        image: 0,\n                        coordinate: 2,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                    GlobalVariable(1),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 5,\n                    ),\n                    ImageLoad(\n                        image: 4,\n                        coordinate: 6,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                    GlobalVariable(2),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 9,\n                    ),\n                    ImageLoad(\n                        image: 8,\n                        coordinate: 10,\n                        array_index: None,\n                        sample: None,\n                        level: None,\n                    ),\n                ],\n                named_expressions: {\n                    3: \"phony\",\n                    7: \"phony\",\n                    11: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 4,\n                    )),\n                    Emit((\n                        start: 6,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 10,\n                        end: 12,\n                    )),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"csStore\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"csStore\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(3),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 1,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 3,\n                    ),\n                    GlobalVariable(4),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 6,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 8,\n                    ),\n                    GlobalVariable(5),\n                    Literal(U32(0)),\n                    Splat(\n                        size: Bi,\n                        value: 11,\n                    ),\n                    Literal(F32(0.0)),\n                    Splat(\n                        size: Quad,\n                        value: 13,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 2,\n                        end: 3,\n                    )),\n                    Emit((\n                        start: 4,\n                        end: 5,\n                    )),\n                    ImageStore(\n                        image: 0,\n                        coordinate: 2,\n                        array_index: None,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 7,\n                        end: 8,\n                    )),\n                    Emit((\n                        start: 9,\n                        end: 10,\n                    )),\n                    ImageStore(\n                        image: 5,\n                        coordinate: 7,\n                        array_index: None,\n                        value: 9,\n                    ),\n                    Emit((\n                        start: 12,\n                        end: 13,\n                    )),\n                    Emit((\n                        start: 14,\n                        end: 15,\n                    )),\n                    ImageStore(\n                        image: 10,\n                        coordinate: 12,\n                        array_index: None,\n                        value: 14,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-template-list-trailing-comma.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(1),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Dynamic,\n                stride: 4,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"sized_comma\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"sized_no_comma\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"unsized_comma\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"unsized_no_comma\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    GlobalVariable(2),\n                    AccessIndex(\n                        base: 2,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 3,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    GlobalVariable(3),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    GlobalVariable(3),\n                    AccessIndex(\n                        base: 10,\n                        index: 0,\n                    ),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 12,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 13,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 15,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 16,\n                    ),\n                    Binary(\n                        op: Add,\n                        left: 14,\n                        right: 17,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Emit((\n                        start: 3,\n                        end: 5,\n                    )),\n                    Store(\n                        pointer: 1,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 10,\n                    )),\n                    Store(\n                        pointer: 6,\n                        value: 9,\n                    ),\n                    Emit((\n                        start: 11,\n                        end: 12,\n                    )),\n                    Emit((\n                        start: 13,\n                        end: 15,\n                    )),\n                    Emit((\n                        start: 16,\n                        end: 19,\n                    )),\n                    Store(\n                        pointer: 11,\n                        value: 18,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-template-list-trailing-comma.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Constant(1),\n                stride: 4,\n            ),\n        ),\n        (\n            name: None,\n            inner: Array(\n                base: 0,\n                size: Dynamic,\n                stride: 4,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"sized_comma\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"sized_no_comma\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"unsized_comma\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"unsized_no_comma\"),\n            space: Storage(\n                access: (\"LOAD | STORE\"),\n            ),\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 2,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [],\n    entry_points: [\n        (\n            name: \"main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 0,\n                        index: 0,\n                    ),\n                    GlobalVariable(2),\n                    AccessIndex(\n                        base: 2,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 3,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 5,\n                        index: 0,\n                    ),\n                    GlobalVariable(3),\n                    AccessIndex(\n                        base: 7,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 8,\n                    ),\n                    GlobalVariable(3),\n                    AccessIndex(\n                        base: 10,\n                        index: 0,\n                    ),\n                    GlobalVariable(0),\n                    AccessIndex(\n                        base: 12,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 13,\n                    ),\n                    GlobalVariable(1),\n                    AccessIndex(\n                        base: 15,\n                        index: 0,\n                    ),\n                    Load(\n                        pointer: 16,\n                    ),\n                    Binary(\n                        op: Add,\n                        left: 14,\n                        right: 17,\n                    ),\n                ],\n                named_expressions: {},\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Emit((\n                        start: 3,\n                        end: 5,\n                    )),\n                    Store(\n                        pointer: 1,\n                        value: 4,\n                    ),\n                    Emit((\n                        start: 6,\n                        end: 7,\n                    )),\n                    Emit((\n                        start: 8,\n                        end: 10,\n                    )),\n                    Store(\n                        pointer: 6,\n                        value: 9,\n                    ),\n                    Emit((\n                        start: 11,\n                        end: 12,\n                    )),\n                    Emit((\n                        start: 13,\n                        end: 15,\n                    )),\n                    Emit((\n                        start: 16,\n                        end: 19,\n                    )),\n                    Store(\n                        pointer: 11,\n                        value: 18,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-texture-external.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"NagaExternalTextureTransferFn\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"a\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"b\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 4,\n                    ),\n                    (\n                        name: Some(\"g\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 8,\n                    ),\n                    (\n                        name: Some(\"k\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 12,\n                    ),\n                ],\n                span: 16,\n            ),\n        ),\n        (\n            name: Some(\"NagaExternalTextureParams\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"yuv_conversion_matrix\"),\n                        ty: 5,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gamut_conversion_matrix\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"src_tf\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 112,\n                    ),\n                    (\n                        name: Some(\"dst_tf\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 128,\n                    ),\n                    (\n                        name: Some(\"sample_transform\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 144,\n                    ),\n                    (\n                        name: Some(\"load_transform\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 168,\n                    ),\n                    (\n                        name: Some(\"size\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 192,\n                    ),\n                    (\n                        name: Some(\"num_planes\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 200,\n                    ),\n                ],\n                span: 208,\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: External,\n            ),\n        ),\n        (\n            name: None,\n            inner: Sampler(\n                comparison: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: Some(7),\n        external_texture_transfer_function: Some(6),\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"tex\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 8,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"samp\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 9,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"test\"),\n            arguments: [\n                (\n                    name: Some(\"t\"),\n                    ty: 8,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 10,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"a\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"b\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"c\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"d\"),\n                    ty: 2,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                GlobalVariable(1),\n                Literal(F32(0.0)),\n                Splat(\n                    size: Bi,\n                    value: 2,\n                ),\n                ImageSample(\n                    image: 0,\n                    sampler: 1,\n                    gather: None,\n                    coordinate: 3,\n                    array_index: None,\n                    offset: None,\n                    level: Zero,\n                    depth_ref: None,\n                    clamp_to_edge: true,\n                ),\n                LocalVariable(0),\n                Literal(I32(0)),\n                Splat(\n                    size: Bi,\n                    value: 6,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 7,\n                    array_index: None,\n                    sample: None,\n                    level: None,\n                ),\n                LocalVariable(1),\n                Literal(U32(0)),\n                Splat(\n                    size: Bi,\n                    value: 10,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 11,\n                    array_index: None,\n                    sample: None,\n                    level: None,\n                ),\n                LocalVariable(2),\n                ImageQuery(\n                    image: 0,\n                    query: Size(\n                        level: None,\n                    ),\n                ),\n                LocalVariable(3),\n                Load(\n                    pointer: 5,\n                ),\n                Load(\n                    pointer: 9,\n                ),\n                Binary(\n                    op: Add,\n                    left: 16,\n                    right: 17,\n                ),\n                Load(\n                    pointer: 13,\n                ),\n                Binary(\n                    op: Add,\n                    left: 18,\n                    right: 19,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                As(\n                    expr: 21,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n                Swizzle(\n                    size: Quad,\n                    vector: 22,\n                    pattern: (X, Y, X, Y),\n                ),\n                Binary(\n                    op: Add,\n                    left: 20,\n                    right: 23,\n                ),\n            ],\n            named_expressions: {\n                0: \"t\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 5,\n                    value: 4,\n                ),\n                Emit((\n                    start: 7,\n                    end: 9,\n                )),\n                Store(\n                    pointer: 9,\n                    value: 8,\n                ),\n                Emit((\n                    start: 11,\n                    end: 13,\n                )),\n                Store(\n                    pointer: 13,\n                    value: 12,\n                ),\n                Emit((\n                    start: 14,\n                    end: 15,\n                )),\n                Store(\n                    pointer: 15,\n                    value: 14,\n                ),\n                Emit((\n                    start: 16,\n                    end: 25,\n                )),\n                Return(\n                    value: Some(24),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"fragment_main\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"fragment_main\"),\n                arguments: [],\n                result: Some((\n                    ty: 10,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: Some(Perspective),\n                        sampling: Some(Center),\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: Some(1),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"vertex_main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"vertex_main\"),\n                arguments: [],\n                result: Some((\n                    ty: 10,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: Some(1),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"compute_main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"compute_main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-texture-external.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Scalar((\n                kind: Float,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Bi,\n                scalar: (\n                    kind: Uint,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Tri,\n                rows: Tri,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: Some(\"NagaExternalTextureTransferFn\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"a\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"b\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 4,\n                    ),\n                    (\n                        name: Some(\"g\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 8,\n                    ),\n                    (\n                        name: Some(\"k\"),\n                        ty: 0,\n                        binding: None,\n                        offset: 12,\n                    ),\n                ],\n                span: 16,\n            ),\n        ),\n        (\n            name: Some(\"NagaExternalTextureParams\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"yuv_conversion_matrix\"),\n                        ty: 5,\n                        binding: None,\n                        offset: 0,\n                    ),\n                    (\n                        name: Some(\"gamut_conversion_matrix\"),\n                        ty: 4,\n                        binding: None,\n                        offset: 64,\n                    ),\n                    (\n                        name: Some(\"src_tf\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 112,\n                    ),\n                    (\n                        name: Some(\"dst_tf\"),\n                        ty: 6,\n                        binding: None,\n                        offset: 128,\n                    ),\n                    (\n                        name: Some(\"sample_transform\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 144,\n                    ),\n                    (\n                        name: Some(\"load_transform\"),\n                        ty: 3,\n                        binding: None,\n                        offset: 168,\n                    ),\n                    (\n                        name: Some(\"size\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 192,\n                    ),\n                    (\n                        name: Some(\"num_planes\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 200,\n                    ),\n                ],\n                span: 208,\n            ),\n        ),\n        (\n            name: None,\n            inner: Image(\n                dim: D2,\n                arrayed: false,\n                class: External,\n            ),\n        ),\n        (\n            name: None,\n            inner: Sampler(\n                comparison: false,\n            ),\n        ),\n        (\n            name: None,\n            inner: Vector(\n                size: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: Some(7),\n        external_texture_transfer_function: Some(6),\n        predeclared_types: {},\n    ),\n    constants: [],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"tex\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 8,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"samp\"),\n            space: Handle,\n            binding: Some((\n                group: 0,\n                binding: 1,\n            )),\n            ty: 9,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [],\n    functions: [\n        (\n            name: Some(\"test\"),\n            arguments: [\n                (\n                    name: Some(\"t\"),\n                    ty: 8,\n                    binding: None,\n                ),\n            ],\n            result: Some((\n                ty: 10,\n                binding: None,\n            )),\n            local_variables: [\n                (\n                    name: Some(\"a\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"b\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"c\"),\n                    ty: 10,\n                    init: None,\n                ),\n                (\n                    name: Some(\"d\"),\n                    ty: 2,\n                    init: None,\n                ),\n            ],\n            expressions: [\n                FunctionArgument(0),\n                GlobalVariable(1),\n                Literal(F32(0.0)),\n                Splat(\n                    size: Bi,\n                    value: 2,\n                ),\n                ImageSample(\n                    image: 0,\n                    sampler: 1,\n                    gather: None,\n                    coordinate: 3,\n                    array_index: None,\n                    offset: None,\n                    level: Zero,\n                    depth_ref: None,\n                    clamp_to_edge: true,\n                ),\n                LocalVariable(0),\n                Literal(I32(0)),\n                Splat(\n                    size: Bi,\n                    value: 6,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 7,\n                    array_index: None,\n                    sample: None,\n                    level: None,\n                ),\n                LocalVariable(1),\n                Literal(U32(0)),\n                Splat(\n                    size: Bi,\n                    value: 10,\n                ),\n                ImageLoad(\n                    image: 0,\n                    coordinate: 11,\n                    array_index: None,\n                    sample: None,\n                    level: None,\n                ),\n                LocalVariable(2),\n                ImageQuery(\n                    image: 0,\n                    query: Size(\n                        level: None,\n                    ),\n                ),\n                LocalVariable(3),\n                Load(\n                    pointer: 5,\n                ),\n                Load(\n                    pointer: 9,\n                ),\n                Binary(\n                    op: Add,\n                    left: 16,\n                    right: 17,\n                ),\n                Load(\n                    pointer: 13,\n                ),\n                Binary(\n                    op: Add,\n                    left: 18,\n                    right: 19,\n                ),\n                Load(\n                    pointer: 15,\n                ),\n                As(\n                    expr: 21,\n                    kind: Float,\n                    convert: Some(4),\n                ),\n                Swizzle(\n                    size: Quad,\n                    vector: 22,\n                    pattern: (X, Y, X, Y),\n                ),\n                Binary(\n                    op: Add,\n                    left: 20,\n                    right: 23,\n                ),\n            ],\n            named_expressions: {\n                0: \"t\",\n            },\n            body: [\n                Emit((\n                    start: 3,\n                    end: 5,\n                )),\n                Store(\n                    pointer: 5,\n                    value: 4,\n                ),\n                Emit((\n                    start: 7,\n                    end: 9,\n                )),\n                Store(\n                    pointer: 9,\n                    value: 8,\n                ),\n                Emit((\n                    start: 11,\n                    end: 13,\n                )),\n                Store(\n                    pointer: 13,\n                    value: 12,\n                ),\n                Emit((\n                    start: 14,\n                    end: 15,\n                )),\n                Store(\n                    pointer: 15,\n                    value: 14,\n                ),\n                Emit((\n                    start: 16,\n                    end: 25,\n                )),\n                Return(\n                    value: Some(24),\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"fragment_main\",\n            stage: Fragment,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"fragment_main\"),\n                arguments: [],\n                result: Some((\n                    ty: 10,\n                    binding: Some(Location(\n                        location: 0,\n                        interpolation: Some(Perspective),\n                        sampling: Some(Center),\n                        blend_src: None,\n                        per_primitive: false,\n                    )),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: Some(1),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"vertex_main\",\n            stage: Vertex,\n            early_depth_test: None,\n            workgroup_size: (0, 0, 0),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"vertex_main\"),\n                arguments: [],\n                result: Some((\n                    ty: 10,\n                    binding: Some(BuiltIn(Position(\n                        invariant: false,\n                    ))),\n                )),\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: Some(1),\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n        (\n            name: \"compute_main\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"compute_main\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    CallResult(0),\n                ],\n                named_expressions: {},\n                body: [\n                    Call(\n                        function: 0,\n                        arguments: [\n                            0,\n                        ],\n                        result: Some(1),\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: None,\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-types_with_comments.compact.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: Some(\"TestS\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"test_m\"),\n                        ty: 1,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"test_c\"),\n            ty: 1,\n            init: 0,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"w_mem2\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 0,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(U32(1)),\n    ],\n    functions: [\n        (\n            name: Some(\"test_g\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"test_ep\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"test_ep\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(0),\n                    Load(\n                        pointer: 0,\n                    ),\n                    ZeroValue(2),\n                ],\n                named_expressions: {\n                    1: \"phony\",\n                    2: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Call(\n                        function: 0,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: Some((\n        types: {\n            2: [\n                \"/// struct S doc comment\",\n            ],\n        },\n        struct_members: {\n            (2, 0): [\n                \"/// member doc comment\",\n            ],\n        },\n        entry_points: {\n            0: [\n                \"/// entry point doc comment\",\n            ],\n        },\n        functions: {\n            0: [\n                \"/// function g doc comment\",\n            ],\n        },\n        constants: {\n            0: [\n                \"/// constant doc comment\",\n            ],\n        },\n        global_variables: {\n            0: [\n                \"/// workgroup var 2 doc comment\",\n            ],\n        },\n        module: [\n            \"//! Module doc comment.\",\n            \"//! 2nd line of module doc comment.\",\n        ],\n    )),\n)"
  },
  {
    "path": "naga/tests/out/ir/wgsl-types_with_comments.ron",
    "content": "(\n    types: [\n        (\n            name: None,\n            inner: Matrix(\n                columns: Quad,\n                rows: Quad,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Matrix(\n                columns: Bi,\n                rows: Bi,\n                scalar: (\n                    kind: Float,\n                    width: 4,\n                ),\n            ),\n        ),\n        (\n            name: None,\n            inner: Scalar((\n                kind: Uint,\n                width: 4,\n            )),\n        ),\n        (\n            name: Some(\"TestR\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"test_m\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n        (\n            name: Some(\"TestS\"),\n            inner: Struct(\n                members: [\n                    (\n                        name: Some(\"test_m\"),\n                        ty: 2,\n                        binding: None,\n                        offset: 0,\n                    ),\n                ],\n                span: 4,\n            ),\n        ),\n    ],\n    special_types: (\n        ray_desc: None,\n        ray_intersection: None,\n        ray_vertex_return: None,\n        external_texture_params: None,\n        external_texture_transfer_function: None,\n        predeclared_types: {},\n    ),\n    constants: [\n        (\n            name: Some(\"test_c\"),\n            ty: 2,\n            init: 0,\n        ),\n    ],\n    overrides: [],\n    global_variables: [\n        (\n            name: Some(\"mvp_matrix\"),\n            space: Uniform,\n            binding: Some((\n                group: 0,\n                binding: 0,\n            )),\n            ty: 0,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"w_mem\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n        (\n            name: Some(\"w_mem2\"),\n            space: WorkGroup,\n            binding: None,\n            ty: 1,\n            init: None,\n            memory_decorations: (\"\"),\n        ),\n    ],\n    global_expressions: [\n        Literal(U32(1)),\n    ],\n    functions: [\n        (\n            name: Some(\"test_f\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n        (\n            name: Some(\"test_g\"),\n            arguments: [],\n            result: None,\n            local_variables: [],\n            expressions: [],\n            named_expressions: {},\n            body: [\n                Return(\n                    value: None,\n                ),\n            ],\n            diagnostic_filter_leaf: None,\n        ),\n    ],\n    entry_points: [\n        (\n            name: \"test_ep\",\n            stage: Compute,\n            early_depth_test: None,\n            workgroup_size: (1, 1, 1),\n            workgroup_size_overrides: None,\n            function: (\n                name: Some(\"test_ep\"),\n                arguments: [],\n                result: None,\n                local_variables: [],\n                expressions: [\n                    GlobalVariable(2),\n                    Load(\n                        pointer: 0,\n                    ),\n                    ZeroValue(4),\n                ],\n                named_expressions: {\n                    1: \"phony\",\n                    2: \"phony\",\n                },\n                body: [\n                    Emit((\n                        start: 1,\n                        end: 2,\n                    )),\n                    Call(\n                        function: 1,\n                        arguments: [],\n                        result: None,\n                    ),\n                    Return(\n                        value: None,\n                    ),\n                ],\n                diagnostic_filter_leaf: None,\n            ),\n            mesh_info: None,\n            task_payload: None,\n            incoming_ray_payload: None,\n        ),\n    ],\n    diagnostic_filters: [],\n    diagnostic_filter_leaf: None,\n    doc_comments: Some((\n        types: {\n            3: [\n                \"/// struct R doc comment\",\n            ],\n            4: [\n                \"/// struct S doc comment\",\n            ],\n        },\n        struct_members: {\n            (3, 0): [\n                \"/// member doc comment\",\n            ],\n            (4, 0): [\n                \"/// member doc comment\",\n            ],\n        },\n        entry_points: {\n            0: [\n                \"/// entry point doc comment\",\n            ],\n        },\n        functions: {\n            0: [\n                \"/// function f doc comment\",\n            ],\n            1: [\n                \"/// function g doc comment\",\n            ],\n        },\n        constants: {\n            0: [\n                \"/// constant doc comment\",\n            ],\n        },\n        global_variables: {\n            0: [\n                \"/**\\n 🍽\\u{fe0f} /* nested comment */\\n */\",\n            ],\n            1: [\n                \"/// workgroup var 1 doc comment\",\n                \"/// 2nd line of workgroup var doc comment\",\n            ],\n            2: [\n                \"/// workgroup var 2 doc comment\",\n            ],\n        },\n        module: [\n            \"//! Module doc comment.\",\n            \"//! 2nd line of module doc comment.\",\n        ],\n    )),\n)"
  },
  {
    "path": "naga/tests/out/msl/spv-barrier.metal",
    "content": "// language: metal2.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid function(\n) {\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_device);\n    metal::threadgroup_barrier(metal::mem_flags::mem_texture);\n    metal::threadgroup_barrier(metal::mem_flags::mem_device);\n    metal::threadgroup_barrier(metal::mem_flags::mem_texture);\n    metal::threadgroup_barrier(metal::mem_flags::mem_device);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_texture);\n    metal::threadgroup_barrier(metal::mem_flags::mem_device);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_texture);\n    return;\n}\n\nkernel void main_(\n) {\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-do-while.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid f_u0028_b1_u003b(\n    thread bool& cond\n) {\n    uint2 loop_bound = uint2(4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            bool _e1 = cond;\n            if (!(cond)) {\n                break;\n            }\n        }\n        loop_init = false;\n        continue;\n    }\n    return;\n}\n\nvoid main_1(\n) {\n    bool param = {};\n    param = false;\n    f_u0028_b1_u003b(param);\n    return;\n}\n\nfragment void main_(\n) {\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-empty-global-name.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_1 {\n    int member;\n};\n\nvoid function(\n    device type_1& unnamed\n) {\n    int _e3 = unnamed.member;\n    unnamed.member = as_type<int>(as_type<uint>(_e3) + as_type<uint>(1));\n    return;\n}\n\nkernel void main_(\n  device type_1& unnamed [[user(fake0)]]\n) {\n    function(unnamed);\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-fetch_depth.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    float member;\n};\nstruct type_4 {\n    metal::uint2 member;\n};\n\nvoid function(\n    device type_2& global,\n    device type_4 const& global_1,\n    metal::depth2d<float, metal::access::sample> global_2\n) {\n    metal::uint2 _e6 = global_1.member;\n    float _e7 = global_2.read(metal::uint2(_e6), 0);\n    global.member = metal::float4(_e7).x;\n    return;\n}\n\nkernel void cull_fetch_depth(\n  device type_2& global [[user(fake0)]]\n, device type_4 const& global_1 [[user(fake0)]]\n, metal::depth2d<float, metal::access::sample> global_2 [[user(fake0)]]\n) {\n    function(global, global_1, global_2);\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-quad-vert.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_3 {\n    float inner[1];\n};\nstruct gl_PerVertex {\n    metal::float4 gl_Position;\n    float gl_PointSize;\n    type_3 gl_ClipDistance;\n    type_3 gl_CullDistance;\n    char _pad4[4];\n};\nstruct VertexOutput {\n    metal::float2 member;\n    char _pad1[8];\n    metal::float4 gl_Position;\n};\n\nvoid main_1(\n    thread metal::float2& v_uv,\n    thread metal::float2& a_uv_1,\n    thread gl_PerVertex& unnamed,\n    thread metal::float2& a_pos_1\n) {\n    metal::float2 _e6 = a_uv_1;\n    v_uv = _e6;\n    metal::float2 _e7 = a_pos_1;\n    unnamed.gl_Position = metal::float4(_e7.x, _e7.y, 0.0, 1.0);\n    return;\n}\n\nstruct main_Input {\n    metal::float2 a_uv [[attribute(1)]];\n    metal::float2 a_pos [[attribute(0)]];\n};\nstruct main_Output {\n    metal::float2 member [[user(loc0), center_perspective]];\n    metal::float4 gl_Position [[position]];\n};\nvertex main_Output main_(\n  main_Input varyings [[stage_in]]\n) {\n    metal::float2 v_uv = {};\n    metal::float2 a_uv_1 = {};\n    gl_PerVertex unnamed = gl_PerVertex {metal::float4(0.0, 0.0, 0.0, 1.0), 1.0, type_3 {}, type_3 {}};\n    metal::float2 a_pos_1 = {};\n    const auto a_uv = varyings.a_uv;\n    const auto a_pos = varyings.a_pos;\n    a_uv_1 = a_uv;\n    a_pos_1 = a_pos;\n    main_1(v_uv, a_uv_1, unnamed, a_pos_1);\n    metal::float2 _e7 = v_uv;\n    metal::float4 _e8 = unnamed.gl_Position;\n    const auto _tmp = VertexOutput {_e7, {}, _e8};\n    return main_Output { _tmp.member, _tmp.gl_Position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-subgroup-barrier.metal",
    "content": "// language: metal2.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid function(\n) {\n    metal::simdgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::simdgroup_barrier(metal::mem_flags::mem_threadgroup);\n    return;\n}\n\nkernel void main_(\n) {\n    function();\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-subgroup-operations-s.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid function(\n    thread uint& global_2,\n    thread uint& global_3\n) {\n    uint _e5 = global_2;\n    uint _e6 = global_3;\n    metal::threadgroup_barrier(metal::mem_flags::mem_none);\n    metal::uint4 unnamed = metal::uint4((uint64_t)metal::simd_ballot((_e6 & 1u) == 1u), 0, 0, 0);\n    metal::uint4 unnamed_1 = metal::uint4((uint64_t)metal::simd_ballot(true), 0, 0, 0);\n    bool unnamed_2 = metal::simd_all(_e6 != 0u);\n    bool unnamed_3 = metal::simd_any(_e6 == 0u);\n    uint unnamed_4 = metal::simd_sum(_e6);\n    uint unnamed_5 = metal::simd_product(_e6);\n    uint unnamed_6 = metal::simd_min(_e6);\n    uint unnamed_7 = metal::simd_max(_e6);\n    uint unnamed_8 = metal::simd_and(_e6);\n    uint unnamed_9 = metal::simd_or(_e6);\n    uint unnamed_10 = metal::simd_xor(_e6);\n    uint unnamed_11 = metal::simd_prefix_exclusive_sum(_e6);\n    uint unnamed_12 = metal::simd_prefix_exclusive_product(_e6);\n    uint unnamed_13 = metal::simd_prefix_inclusive_sum(_e6);\n    uint unnamed_14 = metal::simd_prefix_inclusive_product(_e6);\n    uint unnamed_15 = metal::simd_broadcast_first(_e6);\n    uint unnamed_16 = metal::simd_broadcast(_e6, 4u);\n    uint unnamed_17 = metal::simd_shuffle(_e6, (_e5 - 1u) - _e6);\n    uint unnamed_18 = metal::simd_shuffle_down(_e6, 1u);\n    uint unnamed_19 = metal::simd_shuffle_up(_e6, 1u);\n    uint unnamed_20 = metal::simd_shuffle_xor(_e6, _e5 - 1u);\n    return;\n}\n\nstruct main_Input {\n};\nkernel void main_(\n  uint param [[simdgroups_per_threadgroup]]\n, uint param_1 [[simdgroup_index_in_threadgroup]]\n, uint param_2 [[threads_per_simdgroup]]\n, uint param_3 [[thread_index_in_simdgroup]]\n) {\n    uint global = {};\n    uint global_1 = {};\n    uint global_2 = {};\n    uint global_3 = {};\n    global = param;\n    global_1 = param_1;\n    global_2 = param_2;\n    global_3 = param_3;\n    function(global_2, global_3);\n}\n"
  },
  {
    "path": "naga/tests/out/msl/spv-unnamed-gl-per-vertex.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_3 {\n    float inner[1];\n};\nstruct type_4 {\n    metal::float4 member;\n    float member_1;\n    type_3 member_2;\n    type_3 member_3;\n    char _pad4[4];\n};\n\nvoid function(\n    thread type_4& global,\n    thread int& global_1\n) {\n    int _e9 = global_1;\n    global.member = metal::float4((_e9 == 0) ? -4.0 : 1.0, (_e9 == 2) ? 4.0 : -1.0, 0.0, 1.0);\n    return;\n}\n\nstruct main_Input {\n};\nstruct main_Output {\n    metal::float4 member [[position]];\n};\nvertex main_Output main_(\n  uint param [[vertex_id]]\n) {\n    type_4 global = type_4 {metal::float4(0.0, 0.0, 0.0, 1.0), 1.0, type_3 {}, type_3 {}};\n    int global_1 = {};\n    global_1 = static_cast<int>(param);\n    function(global, global_1);\n    float _e6 = global.member.y;\n    global.member.y = -(_e6);\n    metal::float4 _e8 = global.member;\n    return main_Output { _e8 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-6438-conflicting-idents.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct OurVertexShaderOutput {\n    metal::float4 position;\n    metal::float2 texcoord;\n    char _pad2[8];\n};\n\nstruct vsInput {\n    metal::float2 xy [[attribute(0)]];\n};\nstruct vsOutput_1 {\n    metal::float4 position [[position]];\n    metal::float2 texcoord [[user(loc0), center_perspective]];\n};\nvertex vsOutput_1 vs(\n  vsInput varyings [[stage_in]]\n) {\n    const auto xy = varyings.xy;\n    OurVertexShaderOutput vsOutput = {};\n    vsOutput.position = metal::float4(xy, 0.0, 1.0);\n    OurVertexShaderOutput _e6 = vsOutput;\n    const auto _tmp = _e6;\n    return vsOutput_1 { _tmp.position, _tmp.texcoord };\n}\n\n\nstruct fsOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment fsOutput fs(\n) {\n    return fsOutput { metal::float4(1.0, 0.0, 0.0, 1.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-6772-unpack-expr-accesses.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    int phony = (int4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24)[2];\n    uint phony_1 = (uint4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24).y;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-7995-unicode-idents.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nfloat compute(\n    device float const& asdf\n) {\n    float _e1 = asdf;\n    float u03b8_2_ = _e1 + 9001.0;\n    return u03b8_2_;\n}\n\nkernel void main_(\n  device float const& asdf [[user(fake0)]]\n) {\n    float _e0 = compute(asdf);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-8820-multiple-local-invocation-index-id.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct Input {\n    metal::packed_uint3 local_invocation_id;\n    uint local_invocation_index;\n};\n\nstruct compute1_Input {\n};\nkernel void compute1_(\n  metal::uint3 local_invocation_id [[thread_position_in_threadgroup]]\n, uint local_invocation_index [[thread_index_in_threadgroup]]\n, threadgroup uint& wg_var\n) {\n    if (metal::all(local_invocation_id == metal::uint3(0u))) {\n        wg_var = {};\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    const Input input = { local_invocation_id, local_invocation_index };\n    wg_var = input.local_invocation_index * 2u;\n    uint _e6 = wg_var;\n    wg_var = _e6 + input.local_invocation_id[0];\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-builtins.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void f(\n) {\n    int clamp_aiaiai = 1;\n    float clamp_aiaiaf = 1.0;\n    int clamp_aiaii = 1;\n    float clamp_aiaif = 1.0;\n    float clamp_aiafai = 1.0;\n    float clamp_aiafaf = 1.0;\n    float clamp_aiaff = 1.0;\n    int clamp_aiiai = 1;\n    int clamp_aiii = 1;\n    float clamp_aifai = 1.0;\n    float clamp_aifaf = 1.0;\n    float clamp_aiff = 1.0;\n    float clamp_afaiai = 1.0;\n    float clamp_afaiaf = 1.0;\n    float clamp_afaif = 1.0;\n    float clamp_afafai = 1.0;\n    float clamp_afafaf = 1.0;\n    float clamp_afaff = 1.0;\n    float clamp_affai = 1.0;\n    float clamp_affaf = 1.0;\n    float clamp_afff = 1.0;\n    int clamp_iaiai = 1;\n    int clamp_iaii = 1;\n    int clamp_iiai = 1;\n    int clamp_iii = 1;\n    float clamp_faiai = 1.0;\n    float clamp_faiaf = 1.0;\n    float clamp_faif = 1.0;\n    float clamp_fafai = 1.0;\n    float clamp_fafaf = 1.0;\n    float clamp_faff = 1.0;\n    float clamp_ffai = 1.0;\n    float clamp_ffaf = 1.0;\n    float clamp_fff = 1.0;\n    int min_aiai = 1;\n    float min_aiaf = 1.0;\n    int min_aii = 1;\n    float min_aif = 1.0;\n    float min_afai = 1.0;\n    float min_afaf = 1.0;\n    float min_aff = 1.0;\n    int min_iai = 1;\n    int min_ii = 1;\n    float min_fai = 1.0;\n    float min_faf = 1.0;\n    float min_ff = 1.0;\n    float pow_aiai = 1.0;\n    float pow_aiaf = 1.0;\n    float pow_aif = 1.0;\n    float pow_afai = 1.0;\n    float pow_afaf = 1.0;\n    float pow_aff = 1.0;\n    float pow_fai = 1.0;\n    float pow_faf = 1.0;\n    float pow_ff = 1.0;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-const.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_7 {\n    float inner[2];\n};\nstruct type_8 {\n    int inner[2];\n};\nstruct type_9 {\n    uint inner[2];\n};\nstruct S {\n    float f;\n    int i;\n    uint u;\n};\nconstant metal::int2 xvipaiai = metal::int2(42, 43);\nconstant metal::uint2 xvupaiai = metal::uint2(44u, 45u);\nconstant metal::float2 xvfpaiai = metal::float2(46.0, 47.0);\nconstant metal::float2 xvfpafaf = metal::float2(48.0, 49.0);\nconstant metal::float2 xvfpaiaf = metal::float2(48.0, 49.0);\nconstant metal::uint2 xvupuai = metal::uint2(42u, 43u);\nconstant metal::uint2 xvupaiu = metal::uint2(42u, 43u);\nconstant metal::uint2 xvuuai = metal::uint2(42u, 43u);\nconstant metal::uint2 xvuaiu = metal::uint2(42u, 43u);\nconstant metal::int2 xvip = metal::int2(0, 0);\nconstant metal::uint2 xvup = metal::uint2(0u, 0u);\nconstant metal::float2 xvfp = metal::float2(0.0, 0.0);\nconstant metal::float2x2 xmfp = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\nconstant metal::float2x2 xmfpaiaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\nconstant metal::float2x2 xmfpafaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\nconstant metal::float2x2 xmfpaiafaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\nconstant metal::float2x2 xmfpaiaiafai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\nconstant metal::float2x2 xmfpaiaiaiaf = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\nconstant metal::int2 ivis_ai = metal::int2(1);\nconstant metal::uint2 ivus_ai = metal::uint2(1u);\nconstant metal::float2 ivfs_ai = metal::float2(1.0);\nconstant metal::float2 ivfs_af = metal::float2(1.0);\nconstant type_7 iafafaf = type_7 {{1.0, 2.0}};\nconstant type_7 iafaiai = type_7 {{1.0, 2.0}};\nconstant type_8 xaipaiai = type_8 {{1, 2}};\nconstant type_9 xaupaiai = type_9 {{1u, 2u}};\nconstant type_7 xafpaiaf = type_7 {{1.0, 2.0}};\nconstant type_7 xafpafai = type_7 {{1.0, 2.0}};\nconstant type_7 xafpafaf = type_7 {{1.0, 2.0}};\nconstant S s_f_i_u = S {1.0, 1, 1u};\nconstant S s_f_iai = S {1.0, 1, 1u};\nconstant S s_fai_u = S {1.0, 1, 1u};\nconstant S s_faiai = S {1.0, 1, 1u};\nconstant S saf_i_u = S {1.0, 1, 1u};\nconstant S saf_iai = S {1.0, 1, 1u};\nconstant S safai_u = S {1.0, 1, 1u};\nconstant S safaiai = S {1.0, 1, 1u};\nconstant metal::int2 xvisai = metal::int2(1);\nconstant metal::uint2 xvusai = metal::uint2(1u);\nconstant metal::float2 xvfsai = metal::float2(1.0);\nconstant metal::float2 xvfsaf = metal::float2(1.0);\nconstant metal::float3 ivfr_f_f = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivfr_f_af = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivfraf_f = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivfraf_af = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivf_fr_f = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivf_fraf = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivf_afr_f = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivf_afraf = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivfr_f_ai = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivfrai_f = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivfrai_ai = metal::float3(metal::float2(1.0, 2.0), 3.0);\nconstant metal::float3 ivf_frai = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivf_air_f = metal::float3(1.0, metal::float2(2.0, 3.0));\nconstant metal::float3 ivf_airai = metal::float3(1.0, metal::float2(2.0, 3.0));\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-function-calls.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_7 {\n    float inner[2];\n};\nstruct type_8 {\n    int inner[2];\n};\nstruct type_9 {\n    uint inner[2];\n};\n\nvoid func_f(\n    float a\n) {\n    return;\n}\n\nvoid func_i(\n    int a_1\n) {\n    return;\n}\n\nvoid func_u(\n    uint a_2\n) {\n    return;\n}\n\nvoid func_vf(\n    metal::float2 a_3\n) {\n    return;\n}\n\nvoid func_vi(\n    metal::int2 a_4\n) {\n    return;\n}\n\nvoid func_vu(\n    metal::uint2 a_5\n) {\n    return;\n}\n\nvoid func_mf(\n    metal::float2x2 a_6\n) {\n    return;\n}\n\nvoid func_af(\n    type_7 a_7\n) {\n    return;\n}\n\nvoid func_ai(\n    type_8 a_8\n) {\n    return;\n}\n\nvoid func_au(\n    type_9 a_9\n) {\n    return;\n}\n\nvoid func_f_i(\n    float a_10,\n    int b\n) {\n    return;\n}\n\nkernel void main_(\n) {\n    func_f(0.0);\n    func_f(0.0);\n    func_i(0);\n    func_u(0u);\n    func_f(0.0);\n    func_f(0.0);\n    func_i(0);\n    func_u(0u);\n    func_vf(metal::float2(0.0));\n    func_vf(metal::float2(0.0));\n    func_vi(metal::int2(0));\n    func_vu(metal::uint2(0u));\n    func_vf(metal::float2(0.0));\n    func_vf(metal::float2(0.0));\n    func_vi(metal::int2(0));\n    func_vu(metal::uint2(0u));\n    func_mf(metal::float2x2(metal::float2(0.0), metal::float2(0.0)));\n    func_mf(metal::float2x2(metal::float2(0.0), metal::float2(0.0)));\n    func_mf(metal::float2x2(metal::float2(0.0), metal::float2(0.0)));\n    func_af(type_7 {{0.0, 0.0}});\n    func_af(type_7 {{0.0, 0.0}});\n    func_ai(type_8 {{0, 0}});\n    func_au(type_9 {{0u, 0u}});\n    func_af(type_7 {{0.0, 0.0}});\n    func_af(type_7 {{0.0, 0.0}});\n    func_ai(type_8 {{0, 0}});\n    func_au(type_9 {{0u, 0u}});\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    func_f_i(0.0, 0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-let.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_7 {\n    float inner[2];\n};\nstruct type_8 {\n    int inner[2];\n};\nstruct type_10 {\n    metal::int3 inner[1];\n};\nstruct type_12 {\n    metal::float3 inner[1];\n};\n\nvoid all_constant_arguments(\n) {\n    metal::int2 xvipaiai = metal::int2(42, 43);\n    metal::uint2 xvupaiai = metal::uint2(44u, 45u);\n    metal::float2 xvfpaiai = metal::float2(46.0, 47.0);\n    metal::float2 xvfpafaf = metal::float2(48.0, 49.0);\n    metal::float2 xvfpaiaf = metal::float2(48.0, 49.0);\n    metal::uint2 xvupuai = metal::uint2(42u, 43u);\n    metal::uint2 xvupaiu = metal::uint2(42u, 43u);\n    metal::uint2 xvuuai = metal::uint2(42u, 43u);\n    metal::uint2 xvuaiu = metal::uint2(42u, 43u);\n    metal::int2 xvip = metal::int2(0, 0);\n    metal::uint2 xvup = metal::uint2(0u, 0u);\n    metal::float2 xvfp = metal::float2(0.0, 0.0);\n    metal::float2x2 xmfp = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 xmfpaiaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpafaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiafaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiafai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiaiaf = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfp_faiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpai_faiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiai_fai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiai_f = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::int2 xvispai = metal::int2(1);\n    metal::float2 xvfspaf = metal::float2(1.0);\n    metal::int2 xvis_ai = metal::int2(1);\n    metal::uint2 xvus_ai = metal::uint2(1u);\n    metal::float2 xvfs_ai = metal::float2(1.0);\n    metal::float2 xvfs_af = metal::float2(1.0);\n    type_7 xafafaf = type_7 {{1.0, 2.0}};\n    type_7 xaf_faf = type_7 {{1.0, 2.0}};\n    type_7 xafaf_f = type_7 {{1.0, 2.0}};\n    type_7 xafaiai = type_7 {{1.0, 2.0}};\n    type_8 xai_iai = type_8 {{1, 2}};\n    type_8 xaiai_i = type_8 {{1, 2}};\n    type_8 xaipaiai = type_8 {{1, 2}};\n    type_7 xafpaiai = type_7 {{1.0, 2.0}};\n    type_7 xafpaiaf = type_7 {{1.0, 2.0}};\n    type_7 xafpafai = type_7 {{1.0, 2.0}};\n    type_7 xafpafaf = type_7 {{1.0, 2.0}};\n    type_10 xavipai = type_10 {{metal::int3(1)}};\n    type_12 xavfpai = type_12 {{metal::float3(1.0)}};\n    type_12 xavfpaf = type_12 {{metal::float3(1.0)}};\n    metal::int2 xvisai = metal::int2(1);\n    metal::uint2 xvusai = metal::uint2(1u);\n    metal::float2 xvfsai = metal::float2(1.0);\n    metal::float2 xvfsaf = metal::float2(1.0);\n    type_8 iaipaiai = type_8 {{1, 2}};\n    type_7 iafpaiaf = type_7 {{1.0, 2.0}};\n    type_7 iafpafai = type_7 {{1.0, 2.0}};\n    type_7 iafpafaf = type_7 {{1.0, 2.0}};\n    return;\n}\n\nvoid mixed_constant_and_runtime_arguments(\n) {\n    uint u = {};\n    int i = {};\n    float f = {};\n    uint _e3 = u;\n    metal::uint2 xvupuai_1 = metal::uint2(_e3, 43u);\n    uint _e6 = u;\n    metal::uint2 xvupaiu_1 = metal::uint2(42u, _e6);\n    float _e9 = f;\n    metal::float2 xvfpfai = metal::float2(_e9, 47.0);\n    float _e12 = f;\n    metal::float2 xvfpfaf = metal::float2(_e12, 49.0);\n    uint _e15 = u;\n    metal::uint2 xvuuai_1 = metal::uint2(_e15, 43u);\n    uint _e18 = u;\n    metal::uint2 xvuaiu_1 = metal::uint2(42u, _e18);\n    float _e21 = f;\n    metal::float2x2 xmfp_faiaiai_1 = metal::float2x2(metal::float2(_e21, 2.0), metal::float2(3.0, 4.0));\n    float _e28 = f;\n    metal::float2x2 xmfpai_faiai_1 = metal::float2x2(metal::float2(1.0, _e28), metal::float2(3.0, 4.0));\n    float _e35 = f;\n    metal::float2x2 xmfpaiai_fai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(_e35, 4.0));\n    float _e42 = f;\n    metal::float2x2 xmfpaiaiai_f_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, _e42));\n    float _e49 = f;\n    type_7 xaf_faf_1 = type_7 {{_e49, 2.0}};\n    float _e52 = f;\n    type_7 xafaf_f_1 = type_7 {{1.0, _e52}};\n    float _e55 = f;\n    type_7 xaf_fai = type_7 {{_e55, 2.0}};\n    float _e58 = f;\n    type_7 xafai_f = type_7 {{1.0, _e58}};\n    int _e61 = i;\n    type_8 xai_iai_1 = type_8 {{_e61, 2}};\n    int _e64 = i;\n    type_8 xaiai_i_1 = type_8 {{1, _e64}};\n    float _e67 = f;\n    type_7 xafp_faf = type_7 {{_e67, 2.0}};\n    float _e70 = f;\n    type_7 xafpaf_f = type_7 {{1.0, _e70}};\n    float _e73 = f;\n    type_7 xafp_fai = type_7 {{_e73, 2.0}};\n    float _e76 = f;\n    type_7 xafpai_f = type_7 {{1.0, _e76}};\n    int _e79 = i;\n    type_8 xaip_iai = type_8 {{_e79, 2}};\n    int _e82 = i;\n    type_8 xaipai_i = type_8 {{1, _e82}};\n    int _e85 = i;\n    metal::int2 xvisi = metal::int2(_e85);\n    uint _e87 = u;\n    metal::uint2 xvusu = metal::uint2(_e87);\n    float _e89 = f;\n    metal::float2 xvfsf = metal::float2(_e89);\n    return;\n}\n\nkernel void main_(\n) {\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-operators.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_3 {\n    uint inner[64];\n};\nconstant float plus_fafaf_1 = 3.0;\nconstant float plus_fafai_1 = 3.0;\nconstant float plus_faf_f_1 = 3.0;\nconstant float plus_faiaf_1 = 3.0;\nconstant float plus_faiai_1 = 3.0;\nconstant float plus_fai_f_1 = 3.0;\nconstant float plus_f_faf_1 = 3.0;\nconstant float plus_f_fai_1 = 3.0;\nconstant float plus_f_f_f_1 = 3.0;\nconstant int plus_iaiai_1 = 3;\nconstant int plus_iai_i_1 = 3;\nconstant int plus_i_iai_1 = 3;\nconstant int plus_i_i_i_1 = 3;\nconstant uint plus_uaiai_1 = 3u;\nconstant uint plus_uai_u_1 = 3u;\nconstant uint plus_u_uai_1 = 3u;\nconstant uint plus_u_u_u_1 = 3u;\nconstant uint bitflip_u_u = 0u;\nconstant uint bitflip_uai = 0u;\nconstant int least_i32_ = (-2147483647 - 1);\nconstant float least_f32_ = -340282350000000000000000000000000000000.0;\nconstant int shl_iaiai = 4;\nconstant int shl_iai_u_1 = 4;\nconstant uint shl_uaiai = 4u;\nconstant uint shl_uai_u = 4u;\nconstant int shr_iaiai = 0;\nconstant int shr_iai_u_1 = 0;\nconstant uint shr_uaiai = 0u;\nconstant uint shr_uai_u = 0u;\nconstant int wgpu_4492_ = (-2147483647 - 1);\n\nvoid runtime_values(\n) {\n    float f = 42.0;\n    int i = 43;\n    uint u = 44u;\n    float plus_fafaf = 3.0;\n    float plus_fafai = 3.0;\n    float plus_faf_f = {};\n    float plus_faiaf = 3.0;\n    float plus_faiai = 3.0;\n    float plus_fai_f = {};\n    float plus_f_faf = {};\n    float plus_f_fai = {};\n    float plus_f_f_f = {};\n    int plus_iaiai = 3;\n    int plus_iai_i = {};\n    int plus_i_iai = {};\n    int plus_i_i_i = {};\n    uint plus_uaiai = 3u;\n    uint plus_uai_u = {};\n    uint plus_u_uai = {};\n    uint plus_u_u_u = {};\n    int shl_iai_u = {};\n    int shr_iai_u = {};\n    float _e8 = f;\n    plus_faf_f = 1.0 + _e8;\n    float _e14 = f;\n    plus_fai_f = 1.0 + _e14;\n    float _e18 = f;\n    plus_f_faf = _e18 + 2.0;\n    float _e22 = f;\n    plus_f_fai = _e22 + 2.0;\n    float _e26 = f;\n    float _e27 = f;\n    plus_f_f_f = _e26 + _e27;\n    int _e31 = i;\n    plus_iai_i = as_type<int>(as_type<uint>(1) + as_type<uint>(_e31));\n    int _e35 = i;\n    plus_i_iai = as_type<int>(as_type<uint>(_e35) + as_type<uint>(2));\n    int _e39 = i;\n    int _e40 = i;\n    plus_i_i_i = as_type<int>(as_type<uint>(_e39) + as_type<uint>(_e40));\n    uint _e44 = u;\n    plus_uai_u = 1u + _e44;\n    uint _e48 = u;\n    plus_u_uai = _e48 + 2u;\n    uint _e52 = u;\n    uint _e53 = u;\n    plus_u_u_u = _e52 + _e53;\n    uint _e56 = u;\n    shl_iai_u = 1 << _e56;\n    uint _e60 = u;\n    shr_iai_u = 1 << _e60;\n    return;\n}\n\nvoid wgpu_4445_(\n) {\n    return;\n}\n\nvoid wgpu_4435_(\n    threadgroup type_3& a\n) {\n    uint y = a.inner[as_type<int>(as_type<uint>(1) - as_type<uint>(1))];\n    return;\n}\n\nkernel void main_(\n  metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, threadgroup type_3& a\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        a = {};\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    runtime_values();\n    wgpu_4445_();\n    wgpu_4435_(a);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-return.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_4 {\n    float inner[4];\n};\n\nint return_i32_ai(\n) {\n    return 1;\n}\n\nuint return_u32_ai(\n) {\n    return 1u;\n}\n\nfloat return_f32_ai(\n) {\n    return 1.0;\n}\n\nfloat return_f32_af(\n) {\n    return 1.0;\n}\n\nmetal::float2 return_vec2f32_ai(\n) {\n    return metal::float2(1.0);\n}\n\ntype_4 return_arrf32_ai(\n) {\n    return type_4 {{1.0, 1.0, 1.0, 1.0}};\n}\n\nfloat return_const_f32_const_ai(\n) {\n    return 1.0;\n}\n\nmetal::float2 return_vec2f32_const_ai(\n) {\n    return metal::float2(1.0);\n}\n\nkernel void main_(\n) {\n    int _e0 = return_i32_ai();\n    uint _e1 = return_u32_ai();\n    float _e2 = return_f32_ai();\n    float _e3 = return_f32_af();\n    metal::float2 _e4 = return_vec2f32_ai();\n    type_4 _e5 = return_arrf32_ai();\n    float _e6 = return_const_f32_const_ai();\n    metal::float2 _e7 = return_vec2f32_const_ai();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-texture.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid color(\n    metal::texture2d<float, metal::access::sample> t,\n    metal::sampler s\n) {\n    metal::float4 phony = t.sample(s, metal::float2(1.0, 2.0));\n    metal::float4 phony_1 = t.sample(s, metal::float2(1.0, 2.0), metal::int2(3, 4));\n    metal::float4 phony_2 = t.sample(s, metal::float2(1.0, 2.0), metal::level(0.0));\n    metal::float4 phony_3 = t.sample(s, metal::float2(1.0, 2.0), metal::level(0.0));\n    metal::float4 phony_4 = t.sample(s, metal::float2(1.0, 2.0), metal::gradient2d(metal::float2(3.0, 4.0), metal::float2(5.0, 6.0)));\n    metal::float4 phony_5 = t.sample(s, metal::float2(1.0, 2.0), metal::bias(1.0));\n    return;\n}\n\nvoid depth(\n    metal::sampler s,\n    metal::depth2d<float, metal::access::sample> d,\n    metal::sampler c\n) {\n    float phony_6 = d.sample(s, metal::float2(1.0, 2.0), metal::level(1));\n    float phony_7 = d.sample_compare(c, metal::float2(1.0, 2.0), 0.0);\n    metal::float4 phony_8 = d.gather_compare(c, metal::float2(1.0, 2.0), 0.0);\n    return;\n}\n\nvoid storage(\n    metal::texture2d<float, metal::access::read_write> st\n) {\n    st.write(metal::float4(2.0, 3.0, 4.0, 5.0), metal::uint2(metal::int2(0, 1)));\n    return;\n}\n\nfragment void main_(\n  metal::texture2d<float, metal::access::sample> t [[user(fake0)]]\n, metal::sampler s [[user(fake0)]]\n, metal::depth2d<float, metal::access::sample> d [[user(fake0)]]\n, metal::sampler c [[user(fake0)]]\n, metal::texture2d<float, metal::access::read_write> st [[user(fake0)]]\n) {\n    color(t, s);\n    depth(s, d, c);\n    storage(st);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-abstract-types-var.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_7 {\n    float inner[2];\n};\nstruct type_8 {\n    int inner[2];\n};\nstruct type_9 {\n    uint inner[2];\n};\nstruct type_11 {\n    metal::int3 inner[1];\n};\nstruct type_13 {\n    metal::float3 inner[1];\n};\n\nvoid globals(\n    thread metal::int2& xvipaiai_1,\n    thread metal::uint2& xvupaiai_1,\n    thread metal::float2& xvfpaiai_1,\n    thread metal::float2& xvfpafaf_1,\n    thread metal::float2& xvfpaiaf_1,\n    thread metal::uint2& xvupuai_2,\n    thread metal::uint2& xvupaiu_2,\n    thread metal::uint2& xvuuai_2,\n    thread metal::uint2& xvuaiu_2,\n    thread metal::int2& xvip_1,\n    thread metal::uint2& xvup_1,\n    thread metal::float2& xvfp_1,\n    thread metal::float2x2& xmfp_1,\n    thread metal::float2x2& xmfpaiaiaiai_1,\n    thread metal::float2x2& xmfpafaiaiai_1,\n    thread metal::float2x2& xmfpaiafaiai_1,\n    thread metal::float2x2& xmfpaiaiafai_1,\n    thread metal::float2x2& xmfpaiaiaiaf_1,\n    thread metal::int2& xvispai_1,\n    thread metal::float2& xvfspaf_1,\n    thread metal::int2& xvis_ai_1,\n    thread metal::uint2& xvus_ai_1,\n    thread metal::float2& xvfs_ai_1,\n    thread metal::float2& xvfs_af_1,\n    thread type_7& xafafaf_1,\n    thread type_7& xafaiai_1,\n    thread type_8& xaipaiai_1,\n    thread type_9& xaupaiai,\n    thread type_7& xafpaiaf_1,\n    thread type_7& xafpafai_1,\n    thread type_7& xafpafaf_1,\n    thread type_11& xavipai_1,\n    thread type_13& xavfpai_1,\n    thread type_13& xavfpaf_1,\n    thread metal::int2& xvisai_1,\n    thread metal::uint2& xvusai_1,\n    thread metal::float2& xvfsai_1,\n    thread metal::float2& xvfsaf_1,\n    thread metal::int2& ivispai,\n    thread metal::float2& ivfspaf,\n    thread metal::int2& ivis_ai,\n    thread metal::uint2& ivus_ai,\n    thread metal::float2& ivfs_ai,\n    thread metal::float2& ivfs_af,\n    thread type_7& iafafaf,\n    thread type_7& iafaiai,\n    thread type_8& iaipaiai_1,\n    thread type_7& iafpafaf_1,\n    thread type_7& iafpaiaf_1,\n    thread type_7& iafpafai_1,\n    thread type_11& iavipai,\n    thread type_11& iavfpai,\n    thread type_13& iavfpaf\n) {\n    metal::int2 phony = xvipaiai_1;\n    metal::uint2 phony_1 = xvupaiai_1;\n    metal::float2 phony_2 = xvfpaiai_1;\n    metal::float2 phony_3 = xvfpafaf_1;\n    metal::float2 phony_4 = xvfpaiaf_1;\n    metal::uint2 phony_5 = xvupuai_2;\n    metal::uint2 phony_6 = xvupaiu_2;\n    metal::uint2 phony_7 = xvuuai_2;\n    metal::uint2 phony_8 = xvuaiu_2;\n    metal::int2 phony_9 = xvip_1;\n    metal::uint2 phony_10 = xvup_1;\n    metal::float2 phony_11 = xvfp_1;\n    metal::float2x2 phony_12 = xmfp_1;\n    metal::float2x2 phony_13 = xmfpaiaiaiai_1;\n    metal::float2x2 phony_14 = xmfpafaiaiai_1;\n    metal::float2x2 phony_15 = xmfpaiafaiai_1;\n    metal::float2x2 phony_16 = xmfpaiaiafai_1;\n    metal::float2x2 phony_17 = xmfpaiaiaiaf_1;\n    metal::int2 phony_18 = xvispai_1;\n    metal::float2 phony_19 = xvfspaf_1;\n    metal::int2 phony_20 = xvis_ai_1;\n    metal::uint2 phony_21 = xvus_ai_1;\n    metal::float2 phony_22 = xvfs_ai_1;\n    metal::float2 phony_23 = xvfs_af_1;\n    type_7 phony_24 = xafafaf_1;\n    type_7 phony_25 = xafaiai_1;\n    type_8 phony_26 = xaipaiai_1;\n    type_9 phony_27 = xaupaiai;\n    type_7 phony_28 = xafpaiaf_1;\n    type_7 phony_29 = xafpafai_1;\n    type_7 phony_30 = xafpafaf_1;\n    type_11 phony_31 = xavipai_1;\n    type_13 phony_32 = xavfpai_1;\n    type_13 phony_33 = xavfpaf_1;\n    metal::int2 phony_34 = xvisai_1;\n    metal::uint2 phony_35 = xvusai_1;\n    metal::float2 phony_36 = xvfsai_1;\n    metal::float2 phony_37 = xvfsaf_1;\n    metal::int2 phony_38 = ivispai;\n    metal::float2 phony_39 = ivfspaf;\n    metal::int2 phony_40 = ivis_ai;\n    metal::uint2 phony_41 = ivus_ai;\n    metal::float2 phony_42 = ivfs_ai;\n    metal::float2 phony_43 = ivfs_af;\n    type_7 phony_44 = iafafaf;\n    type_7 phony_45 = iafaiai;\n    type_8 phony_46 = iaipaiai_1;\n    type_7 phony_47 = iafpafaf_1;\n    type_7 phony_48 = iafpaiaf_1;\n    type_7 phony_49 = iafpafai_1;\n    type_11 phony_50 = iavipai;\n    type_11 phony_51 = iavfpai;\n    type_13 phony_52 = iavfpaf;\n    return;\n}\n\nvoid all_constant_arguments(\n) {\n    metal::int2 xvipaiai = metal::int2(42, 43);\n    metal::uint2 xvupaiai = metal::uint2(44u, 45u);\n    metal::float2 xvfpaiai = metal::float2(46.0, 47.0);\n    metal::float2 xvfpafaf = metal::float2(48.0, 49.0);\n    metal::float2 xvfpaiaf = metal::float2(48.0, 49.0);\n    metal::uint2 xvupuai = metal::uint2(42u, 43u);\n    metal::uint2 xvupaiu = metal::uint2(42u, 43u);\n    metal::uint2 xvuuai = metal::uint2(42u, 43u);\n    metal::uint2 xvuaiu = metal::uint2(42u, 43u);\n    metal::int2 xvip = metal::int2(0, 0);\n    metal::uint2 xvup = metal::uint2(0u, 0u);\n    metal::float2 xvfp = metal::float2(0.0, 0.0);\n    metal::float2x2 xmfp = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 xmfpaiaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpafaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiafaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiafai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiaiaf = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfp_faiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpai_faiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiai_fai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiai_f = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::int2 xvispai = metal::int2(1);\n    metal::float2 xvfspaf = metal::float2(1.0);\n    metal::int2 xvis_ai = metal::int2(1);\n    metal::uint2 xvus_ai = metal::uint2(1u);\n    metal::float2 xvfs_ai = metal::float2(1.0);\n    metal::float2 xvfs_af = metal::float2(1.0);\n    type_7 xafafaf = type_7 {{1.0, 2.0}};\n    type_7 xaf_faf = type_7 {{1.0, 2.0}};\n    type_7 xafaf_f = type_7 {{1.0, 2.0}};\n    type_7 xafaiai = type_7 {{1.0, 2.0}};\n    type_8 xai_iai = type_8 {{1, 2}};\n    type_8 xaiai_i = type_8 {{1, 2}};\n    type_8 xaipaiai = type_8 {{1, 2}};\n    type_7 xafpaiai = type_7 {{1.0, 2.0}};\n    type_7 xafpaiaf = type_7 {{1.0, 2.0}};\n    type_7 xafpafai = type_7 {{1.0, 2.0}};\n    type_7 xafpafaf = type_7 {{1.0, 2.0}};\n    type_11 xavipai = type_11 {{metal::int3(1)}};\n    type_13 xavfpai = type_13 {{metal::float3(1.0)}};\n    type_13 xavfpaf = type_13 {{metal::float3(1.0)}};\n    metal::int2 xvisai = metal::int2(1);\n    metal::uint2 xvusai = metal::uint2(1u);\n    metal::float2 xvfsai = metal::float2(1.0);\n    metal::float2 xvfsaf = metal::float2(1.0);\n    type_8 iaipaiai = type_8 {{1, 2}};\n    type_7 iafpaiaf = type_7 {{1.0, 2.0}};\n    type_7 iafpafai = type_7 {{1.0, 2.0}};\n    type_7 iafpafaf = type_7 {{1.0, 2.0}};\n    xvipaiai = metal::int2(42, 43);\n    xvupaiai = metal::uint2(44u, 45u);\n    xvfpaiai = metal::float2(46.0, 47.0);\n    xvfpafaf = metal::float2(48.0, 49.0);\n    xvfpaiaf = metal::float2(48.0, 49.0);\n    xvupuai = metal::uint2(42u, 43u);\n    xvupaiu = metal::uint2(42u, 43u);\n    xvuuai = metal::uint2(42u, 43u);\n    xvuaiu = metal::uint2(42u, 43u);\n    xvip = metal::int2(0, 0);\n    xvup = metal::uint2(0u, 0u);\n    xvfp = metal::float2(0.0, 0.0);\n    xmfp = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    xmfpaiaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpafaiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpaiafaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpaiaiafai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpaiaiaiaf = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfp_faiaiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpai_faiai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpaiai_fai = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xmfpaiaiai_f = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    xvispai = metal::int2(1);\n    xvfspaf = metal::float2(1.0);\n    xvis_ai = metal::int2(1);\n    xvus_ai = metal::uint2(1u);\n    xvfs_ai = metal::float2(1.0);\n    xvfs_af = metal::float2(1.0);\n    xafafaf = type_7 {{1.0, 2.0}};\n    xaf_faf = type_7 {{1.0, 2.0}};\n    xafaf_f = type_7 {{1.0, 2.0}};\n    xafaiai = type_7 {{1.0, 2.0}};\n    xai_iai = type_8 {{1, 2}};\n    xaiai_i = type_8 {{1, 2}};\n    xaipaiai = type_8 {{1, 2}};\n    xafpaiai = type_7 {{1.0, 2.0}};\n    xafpaiaf = type_7 {{1.0, 2.0}};\n    xafpafai = type_7 {{1.0, 2.0}};\n    xafpafaf = type_7 {{1.0, 2.0}};\n    xavipai = type_11 {{metal::int3(1)}};\n    xavfpai = type_13 {{metal::float3(1.0)}};\n    xavfpaf = type_13 {{metal::float3(1.0)}};\n    xvisai = metal::int2(1);\n    xvusai = metal::uint2(1u);\n    xvfsai = metal::float2(1.0);\n    xvfsaf = metal::float2(1.0);\n    iaipaiai = type_8 {{1, 2}};\n    iafpaiaf = type_7 {{1.0, 2.0}};\n    iafpafai = type_7 {{1.0, 2.0}};\n    iafpafaf = type_7 {{1.0, 2.0}};\n    return;\n}\n\nvoid mixed_constant_and_runtime_arguments(\n) {\n    uint u = {};\n    int i = {};\n    float f = {};\n    metal::uint2 xvupuai_1 = {};\n    metal::uint2 xvupaiu_1 = {};\n    metal::float2 xvfpfai = {};\n    metal::float2 xvfpfaf = {};\n    metal::uint2 xvuuai_1 = {};\n    metal::uint2 xvuaiu_1 = {};\n    metal::float2x2 xmfp_faiaiai_1 = {};\n    metal::float2x2 xmfpai_faiai_1 = {};\n    metal::float2x2 xmfpaiai_fai_1 = {};\n    metal::float2x2 xmfpaiaiai_f_1 = {};\n    type_7 xaf_faf_1 = {};\n    type_7 xafaf_f_1 = {};\n    type_7 xaf_fai = {};\n    type_7 xafai_f = {};\n    type_8 xai_iai_1 = {};\n    type_8 xaiai_i_1 = {};\n    type_7 xafp_faf = {};\n    type_7 xafpaf_f = {};\n    type_7 xafp_fai = {};\n    type_7 xafpai_f = {};\n    type_8 xaip_iai = {};\n    type_8 xaipai_i = {};\n    metal::int2 xvisi = {};\n    metal::uint2 xvusu = {};\n    metal::float2 xvfsf = {};\n    uint _e3 = u;\n    xvupuai_1 = metal::uint2(_e3, 43u);\n    uint _e7 = u;\n    xvupaiu_1 = metal::uint2(42u, _e7);\n    float _e11 = f;\n    xvfpfai = metal::float2(_e11, 47.0);\n    float _e15 = f;\n    xvfpfaf = metal::float2(_e15, 49.0);\n    uint _e19 = u;\n    xvuuai_1 = metal::uint2(_e19, 43u);\n    uint _e23 = u;\n    xvuaiu_1 = metal::uint2(42u, _e23);\n    float _e27 = f;\n    xmfp_faiaiai_1 = metal::float2x2(metal::float2(_e27, 2.0), metal::float2(3.0, 4.0));\n    float _e35 = f;\n    xmfpai_faiai_1 = metal::float2x2(metal::float2(1.0, _e35), metal::float2(3.0, 4.0));\n    float _e43 = f;\n    xmfpaiai_fai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(_e43, 4.0));\n    float _e51 = f;\n    xmfpaiaiai_f_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, _e51));\n    float _e59 = f;\n    xaf_faf_1 = type_7 {{_e59, 2.0}};\n    float _e63 = f;\n    xafaf_f_1 = type_7 {{1.0, _e63}};\n    float _e67 = f;\n    xaf_fai = type_7 {{_e67, 2.0}};\n    float _e71 = f;\n    xafai_f = type_7 {{1.0, _e71}};\n    int _e75 = i;\n    xai_iai_1 = type_8 {{_e75, 2}};\n    int _e79 = i;\n    xaiai_i_1 = type_8 {{1, _e79}};\n    float _e83 = f;\n    xafp_faf = type_7 {{_e83, 2.0}};\n    float _e87 = f;\n    xafpaf_f = type_7 {{1.0, _e87}};\n    float _e91 = f;\n    xafp_fai = type_7 {{_e91, 2.0}};\n    float _e95 = f;\n    xafpai_f = type_7 {{1.0, _e95}};\n    int _e99 = i;\n    xaip_iai = type_8 {{_e99, 2}};\n    int _e103 = i;\n    xaipai_i = type_8 {{1, _e103}};\n    int _e107 = i;\n    xvisi = metal::int2(_e107);\n    uint _e110 = u;\n    xvusu = metal::uint2(_e110);\n    float _e113 = f;\n    xvfsf = metal::float2(_e113);\n    uint _e116 = u;\n    xvupuai_1 = metal::uint2(_e116, 43u);\n    uint _e119 = u;\n    xvupaiu_1 = metal::uint2(42u, _e119);\n    uint _e122 = u;\n    xvuuai_1 = metal::uint2(_e122, 43u);\n    uint _e125 = u;\n    xvuaiu_1 = metal::uint2(42u, _e125);\n    float _e128 = f;\n    xmfp_faiaiai_1 = metal::float2x2(metal::float2(_e128, 2.0), metal::float2(3.0, 4.0));\n    float _e135 = f;\n    xmfpai_faiai_1 = metal::float2x2(metal::float2(1.0, _e135), metal::float2(3.0, 4.0));\n    float _e142 = f;\n    xmfpaiai_fai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(_e142, 4.0));\n    float _e149 = f;\n    xmfpaiaiai_f_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, _e149));\n    float _e156 = f;\n    xaf_faf_1 = type_7 {{_e156, 2.0}};\n    float _e159 = f;\n    xafaf_f_1 = type_7 {{1.0, _e159}};\n    float _e162 = f;\n    xaf_fai = type_7 {{_e162, 2.0}};\n    float _e165 = f;\n    xafai_f = type_7 {{1.0, _e165}};\n    int _e168 = i;\n    xai_iai_1 = type_8 {{_e168, 2}};\n    int _e171 = i;\n    xaiai_i_1 = type_8 {{1, _e171}};\n    float _e174 = f;\n    xafp_faf = type_7 {{_e174, 2.0}};\n    float _e177 = f;\n    xafpaf_f = type_7 {{1.0, _e177}};\n    float _e180 = f;\n    xafp_fai = type_7 {{_e180, 2.0}};\n    float _e183 = f;\n    xafpai_f = type_7 {{1.0, _e183}};\n    int _e186 = i;\n    xaip_iai = type_8 {{_e186, 2}};\n    int _e189 = i;\n    xaipai_i = type_8 {{1, _e189}};\n    int _e192 = i;\n    xvisi = metal::int2(_e192);\n    uint _e194 = u;\n    xvusu = metal::uint2(_e194);\n    float _e196 = f;\n    xvfsf = metal::float2(_e196);\n    return;\n}\n\nkernel void main_(\n) {\n    metal::int2 xvipaiai_1 = metal::int2(42, 43);\n    metal::uint2 xvupaiai_1 = metal::uint2(44u, 45u);\n    metal::float2 xvfpaiai_1 = metal::float2(46.0, 47.0);\n    metal::float2 xvfpafaf_1 = metal::float2(48.0, 49.0);\n    metal::float2 xvfpaiaf_1 = metal::float2(48.0, 49.0);\n    metal::uint2 xvupuai_2 = metal::uint2(42u, 43u);\n    metal::uint2 xvupaiu_2 = metal::uint2(42u, 43u);\n    metal::uint2 xvuuai_2 = metal::uint2(42u, 43u);\n    metal::uint2 xvuaiu_2 = metal::uint2(42u, 43u);\n    metal::int2 xvip_1 = metal::int2(0, 0);\n    metal::uint2 xvup_1 = metal::uint2(0u, 0u);\n    metal::float2 xvfp_1 = metal::float2(0.0, 0.0);\n    metal::float2x2 xmfp_1 = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 xmfpaiaiaiai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpafaiaiai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiafaiai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiafai_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::float2x2 xmfpaiaiaiaf_1 = metal::float2x2(metal::float2(1.0, 2.0), metal::float2(3.0, 4.0));\n    metal::int2 xvispai_1 = metal::int2(1);\n    metal::float2 xvfspaf_1 = metal::float2(1.0);\n    metal::int2 xvis_ai_1 = metal::int2(1);\n    metal::uint2 xvus_ai_1 = metal::uint2(1u);\n    metal::float2 xvfs_ai_1 = metal::float2(1.0);\n    metal::float2 xvfs_af_1 = metal::float2(1.0);\n    type_7 xafafaf_1 = type_7 {{1.0, 2.0}};\n    type_7 xafaiai_1 = type_7 {{1.0, 2.0}};\n    type_8 xaipaiai_1 = type_8 {{1, 2}};\n    type_9 xaupaiai = type_9 {{1u, 2u}};\n    type_7 xafpaiaf_1 = type_7 {{1.0, 2.0}};\n    type_7 xafpafai_1 = type_7 {{1.0, 2.0}};\n    type_7 xafpafaf_1 = type_7 {{1.0, 2.0}};\n    type_11 xavipai_1 = type_11 {{metal::int3(1)}};\n    type_13 xavfpai_1 = type_13 {{metal::float3(1.0)}};\n    type_13 xavfpaf_1 = type_13 {{metal::float3(1.0)}};\n    metal::int2 xvisai_1 = metal::int2(1);\n    metal::uint2 xvusai_1 = metal::uint2(1u);\n    metal::float2 xvfsai_1 = metal::float2(1.0);\n    metal::float2 xvfsaf_1 = metal::float2(1.0);\n    metal::int2 ivispai = metal::int2(1);\n    metal::float2 ivfspaf = metal::float2(1.0);\n    metal::int2 ivis_ai = metal::int2(1);\n    metal::uint2 ivus_ai = metal::uint2(1u);\n    metal::float2 ivfs_ai = metal::float2(1.0);\n    metal::float2 ivfs_af = metal::float2(1.0);\n    type_7 iafafaf = type_7 {{1.0, 2.0}};\n    type_7 iafaiai = type_7 {{1.0, 2.0}};\n    type_8 iaipaiai_1 = type_8 {{1, 2}};\n    type_7 iafpafaf_1 = type_7 {{1.0, 2.0}};\n    type_7 iafpaiaf_1 = type_7 {{1.0, 2.0}};\n    type_7 iafpafai_1 = type_7 {{1.0, 2.0}};\n    type_11 iavipai = type_11 {{metal::int3(1)}};\n    type_11 iavfpai = type_11 {{metal::int3(1)}};\n    type_13 iavfpaf = type_13 {{metal::float3(1.0)}};\n    globals(xvipaiai_1, xvupaiai_1, xvfpaiai_1, xvfpafaf_1, xvfpaiaf_1, xvupuai_2, xvupaiu_2, xvuuai_2, xvuaiu_2, xvip_1, xvup_1, xvfp_1, xmfp_1, xmfpaiaiaiai_1, xmfpafaiaiai_1, xmfpaiafaiai_1, xmfpaiaiafai_1, xmfpaiaiaiaf_1, xvispai_1, xvfspaf_1, xvis_ai_1, xvus_ai_1, xvfs_ai_1, xvfs_af_1, xafafaf_1, xafaiai_1, xaipaiai_1, xaupaiai, xafpaiaf_1, xafpafai_1, xafpafaf_1, xavipai_1, xavfpai_1, xavfpaf_1, xvisai_1, xvusai_1, xvfsai_1, xvfsaf_1, ivispai, ivfspaf, ivis_ai, ivus_ai, ivfs_ai, ivfs_af, iafafaf, iafaiai, iaipaiai_1, iafpafaf_1, iafpaiaf_1, iafpafai_1, iavipai, iavfpai, iavfpaf);\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-access.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size1;\n};\n\nstruct GlobalConst {\n    uint a;\n    char _pad1[12];\n    metal::packed_uint3 b;\n    int c;\n};\nstruct AlignedWrapper {\n    int value;\n    char _pad1[4];\n};\nstruct type_6 {\n    metal::float2x2 inner[2];\n};\nstruct type_8 {\n    metal::atomic_int inner[10];\n};\nstruct type_10 {\n    metal::uint2 inner[2];\n};\ntypedef AlignedWrapper type_11[1];\nstruct Bar {\n    metal::float4x3 _matrix;\n    type_6 matrix_array;\n    metal::atomic_int atom;\n    type_8 atom_arr;\n    char _pad4[4];\n    type_10 arr;\n    type_11 data;\n    char _pad6[8];\n};\nstruct Baz {\n    metal::float3x2 m;\n};\nstruct type_15 {\n    metal::float4x2 inner[2];\n};\nstruct MatCx2InArray {\n    type_15 am;\n};\nstruct type_17 {\n    float inner[10];\n};\nstruct type_18 {\n    type_17 inner[5];\n};\nstruct type_20 {\n    int inner[5];\n};\nstruct type_22 {\n    metal::float4 inner[2];\n};\nstruct AssignToMember {\n    uint x;\n};\nstruct type_25 {\n    uint inner[4];\n};\nstruct type_28 {\n    bool inner[1];\n};\nstruct S {\n    int m;\n};\nstruct Inner {\n    int delicious;\n};\nstruct Outer {\n    Inner om_nom_nom;\n    uint thing;\n};\n\nvoid test_matrix_within_struct_accesses(\n    constant Baz& baz\n) {\n    int idx = 1;\n    Baz t = Baz {metal::float3x2(metal::float2(1.0), metal::float2(2.0), metal::float2(3.0))};\n    int _e3 = idx;\n    idx = as_type<int>(as_type<uint>(_e3) - as_type<uint>(1));\n    metal::float3x2 l0_ = baz.m;\n    metal::float2 l1_ = baz.m[0];\n    int _e14 = idx;\n    metal::float2 l2_ = baz.m[_e14];\n    float l3_ = baz.m[0].y;\n    int _e25 = idx;\n    float l4_ = baz.m[0][_e25];\n    int _e30 = idx;\n    float l5_ = baz.m[_e30].y;\n    int _e36 = idx;\n    int _e38 = idx;\n    float l6_ = baz.m[_e36][_e38];\n    int _e51 = idx;\n    idx = as_type<int>(as_type<uint>(_e51) + as_type<uint>(1));\n    t.m = metal::float3x2(metal::float2(6.0), metal::float2(5.0), metal::float2(4.0));\n    t.m[0] = metal::float2(9.0);\n    int _e66 = idx;\n    t.m[_e66] = metal::float2(90.0);\n    t.m[0].y = 10.0;\n    int _e76 = idx;\n    t.m[0][_e76] = 20.0;\n    int _e80 = idx;\n    t.m[_e80].y = 30.0;\n    int _e85 = idx;\n    int _e87 = idx;\n    t.m[_e85][_e87] = 40.0;\n    return;\n}\n\nvoid test_matrix_within_array_within_struct_accesses(\n    constant MatCx2InArray& nested_mat_cx2_\n) {\n    int idx_1 = 1;\n    MatCx2InArray t_1 = MatCx2InArray {type_15 {}};\n    int _e3 = idx_1;\n    idx_1 = as_type<int>(as_type<uint>(_e3) - as_type<uint>(1));\n    type_15 l0_1 = nested_mat_cx2_.am;\n    metal::float4x2 l1_1 = nested_mat_cx2_.am.inner[0];\n    metal::float2 l2_1 = nested_mat_cx2_.am.inner[0][0];\n    int _e20 = idx_1;\n    metal::float2 l3_1 = nested_mat_cx2_.am.inner[0][_e20];\n    float l4_1 = nested_mat_cx2_.am.inner[0][0].y;\n    int _e33 = idx_1;\n    float l5_1 = nested_mat_cx2_.am.inner[0][0][_e33];\n    int _e39 = idx_1;\n    float l6_1 = nested_mat_cx2_.am.inner[0][_e39].y;\n    int _e46 = idx_1;\n    int _e48 = idx_1;\n    float l7_ = nested_mat_cx2_.am.inner[0][_e46][_e48];\n    int _e55 = idx_1;\n    idx_1 = as_type<int>(as_type<uint>(_e55) + as_type<uint>(1));\n    t_1.am = type_15 {};\n    t_1.am.inner[0] = metal::float4x2(metal::float2(8.0), metal::float2(7.0), metal::float2(6.0), metal::float2(5.0));\n    t_1.am.inner[0][0] = metal::float2(9.0);\n    int _e77 = idx_1;\n    t_1.am.inner[0][_e77] = metal::float2(90.0);\n    t_1.am.inner[0][0].y = 10.0;\n    int _e89 = idx_1;\n    t_1.am.inner[0][0][_e89] = 20.0;\n    int _e94 = idx_1;\n    t_1.am.inner[0][_e94].y = 30.0;\n    int _e100 = idx_1;\n    int _e102 = idx_1;\n    t_1.am.inner[0][_e100][_e102] = 40.0;\n    return;\n}\n\nfloat read_from_private(\n    thread float& foo_1\n) {\n    float _e1 = foo_1;\n    return _e1;\n}\n\nfloat test_arr_as_arg(\n    type_18 a\n) {\n    return a.inner[4].inner[9];\n}\n\nvoid assign_through_ptr_fn(\n    thread uint& p\n) {\n    p = 42u;\n    return;\n}\n\nvoid assign_array_through_ptr_fn(\n    thread type_22& foo_2\n) {\n    foo_2 = type_22 {{metal::float4(1.0), metal::float4(2.0)}};\n    return;\n}\n\nvoid assign_through_ptr(\n) {\n    uint val = 33u;\n    type_22 arr = type_22 {{metal::float4(6.0), metal::float4(7.0)}};\n    assign_through_ptr_fn(val);\n    assign_array_through_ptr_fn(arr);\n    return;\n}\n\nuint fetch_arg_ptr_member(\n    thread AssignToMember& p_1\n) {\n    uint _e2 = p_1.x;\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_member(\n    thread AssignToMember& p_2\n) {\n    p_2.x = 10u;\n    return;\n}\n\nuint fetch_arg_ptr_array_element(\n    thread type_25& p_3\n) {\n    uint _e2 = p_3.inner[1];\n    return _e2;\n}\n\nvoid assign_to_arg_ptr_array_element(\n    thread type_25& p_4\n) {\n    p_4.inner[1] = 10u;\n    return;\n}\n\nvoid assign_to_ptr_components(\n) {\n    AssignToMember s1_ = {};\n    type_25 a1_ = {};\n    assign_to_arg_ptr_member(s1_);\n    uint _e1 = fetch_arg_ptr_member(s1_);\n    assign_to_arg_ptr_array_element(a1_);\n    uint _e3 = fetch_arg_ptr_array_element(a1_);\n    return;\n}\n\nbool index_ptr(\n    bool value\n) {\n    type_28 a_1 = {};\n    a_1 = type_28 {{value}};\n    bool _e4 = a_1.inner[0];\n    return _e4;\n}\n\nint member_ptr(\n) {\n    S s = S {42};\n    int _e4 = s.m;\n    return _e4;\n}\n\nint let_members_of_members(\n) {\n    Inner inner_1 = Outer {}.om_nom_nom;\n    int delishus_1 = Outer {}.om_nom_nom.delicious;\n    if (Outer {}.thing != static_cast<uint>(delishus_1)) {\n    }\n    return Outer {}.om_nom_nom.delicious;\n}\n\nint var_members_of_members(\n) {\n    Outer thing = Outer {};\n    Inner inner = {};\n    int delishus = {};\n    Inner _e3 = thing.om_nom_nom;\n    inner = _e3;\n    int _e6 = inner.delicious;\n    delishus = _e6;\n    uint _e9 = thing.thing;\n    int _e10 = delishus;\n    if (_e9 != static_cast<uint>(_e10)) {\n    }\n    int _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\nint naga_f2i32(float value) {\n    return static_cast<int>(metal::clamp(value, -2147483600.0, 2147483500.0));\n}\n\n\nstruct foo_vertInput {\n};\nstruct foo_vertOutput {\n    metal::float4 member [[position]];\n};\nvertex foo_vertOutput foo_vert(\n  uint vi [[vertex_id]]\n, device Bar const& bar [[buffer(0)]]\n, constant Baz& baz [[buffer(1)]]\n, device metal::int2 const& qux [[buffer(2)]]\n, constant MatCx2InArray& nested_mat_cx2_ [[buffer(3)]]\n, constant _mslBufferSizes& _buffer_sizes [[buffer(24)]]\n) {\n    GlobalConst msl_padding_global_const = GlobalConst {0u, {}, metal::uint3(0u, 0u, 0u), 0};\n    float foo = 0.0;\n    type_20 c2_ = {};\n    float baz_1 = foo;\n    foo = 1.0;\n    GlobalConst phony = msl_padding_global_const;\n    test_matrix_within_struct_accesses(baz);\n    test_matrix_within_array_within_struct_accesses(nested_mat_cx2_);\n    metal::float4x3 _matrix = bar._matrix;\n    type_10 arr_1 = bar.arr;\n    float b = bar._matrix[3u].x;\n    int a_2 = bar.data[(1 + (_buffer_sizes.size1 - 160 - 8) / 8) - 2u].value;\n    metal::int2 c = qux;\n    float _e35 = read_from_private(foo);\n    c2_ = type_20 {{a_2, naga_f2i32(b), 3, 4, 5}};\n    c2_.inner[vi + 1u] = 42;\n    int value_1 = c2_.inner[vi];\n    float _e49 = test_arr_as_arg(type_18 {});\n    return foo_vertOutput { metal::float4(_matrix * static_cast<metal::float4>(metal::int4(value_1)), 2.0) };\n}\n\n\nstruct foo_fragOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment foo_fragOutput foo_frag(\n  device Bar& bar [[buffer(0)]]\n, device metal::int2& qux [[buffer(2)]]\n, constant _mslBufferSizes& _buffer_sizes [[buffer(24)]]\n) {\n    bar._matrix[1].z = 1.0;\n    bar._matrix = metal::float4x3(metal::float3(0.0), metal::float3(1.0), metal::float3(2.0), metal::float3(3.0));\n    bar.arr = type_10 {{metal::uint2(0u), metal::uint2(1u)}};\n    bar.data[1].value = 1;\n    qux = metal::int2 {};\n    return foo_fragOutput { metal::float4(0.0) };\n}\n\n\nkernel void foo_compute(\n) {\n    assign_through_ptr();\n    assign_to_ptr_components();\n    bool _e1 = index_ptr(true);\n    int _e2 = member_ptr();\n    int _e3 = let_members_of_members();\n    int _e4 = var_members_of_members();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-aliased-ray-query.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct _RayQuery {\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data> intersector;\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data>::result_type intersection;\n    bool ready = false;\n};\nconstexpr metal::uint _map_intersection_type(const metal::raytracing::intersection_type ty) {\n    return ty==metal::raytracing::intersection_type::triangle ? 1 : \n        ty==metal::raytracing::intersection_type::bounding_box ? 4 : 0;\n}\n\nstruct RayDesc {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    metal::float3 origin;\n    metal::float3 dir;\n};\nstruct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    metal::float2 barycentrics;\n    bool front_face;\n    char _pad9[11];\n    metal::float4x3 object_to_world;\n    metal::float4x3 world_to_object;\n};\n\nkernel void main_candidate(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n) {\n    _RayQuery rq_1 = {};\n    metal::float3 pos = metal::float3(0.0);\n    metal::float3 dir = metal::float3(0.0, 1.0, 0.0);\n    RayDesc _e12 = RayDesc {4u, 255u, 0.1, 100.0, pos, dir};\n    rq_1.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq_1.intersector.set_opacity_cull_mode((_e12.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (_e12.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq_1.intersector.force_opacity((_e12.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e12.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq_1.intersector.accept_any_intersection((_e12.flags & 4) != 0);\n    rq_1.intersection = rq_1.intersector.intersect(metal::raytracing::ray(_e12.origin, _e12.dir, _e12.tmin, _e12.tmax), acc_struct, _e12.cull_mask);    rq_1.ready = true;\n    RayIntersection intersection = RayIntersection {_map_intersection_type(rq_1.intersection.type), rq_1.intersection.distance, rq_1.intersection.user_instance_id, rq_1.intersection.instance_id, {}, rq_1.intersection.geometry_id, rq_1.intersection.primitive_id, rq_1.intersection.triangle_barycentric_coord, rq_1.intersection.triangle_front_facing, {}, rq_1.intersection.object_to_world_transform, rq_1.intersection.world_to_object_transform};\n    if (intersection.kind == 3u) {\n        return;\n    } else {\n        if (intersection.kind == 1u) {\n            return;\n        } else {\n            rq_1.ready = false;\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-array-in-ctor.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_1 {\n    float inner[2];\n};\nstruct Ah {\n    type_1 inner;\n};\n\nkernel void cs_main(\n  device Ah const& ah [[user(fake0)]]\n) {\n    Ah ah_1 = ah;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-array-in-function-return-type.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_1 {\n    float inner[2];\n};\nstruct type_2 {\n    type_1 inner[3];\n};\n\ntype_1 ret_array(\n) {\n    return type_1 {{1.0, 2.0}};\n}\n\ntype_2 ret_array_array(\n) {\n    type_1 _e0 = ret_array();\n    type_1 _e1 = ret_array();\n    type_1 _e2 = ret_array();\n    return type_2 {{_e0, _e1, _e2}};\n}\n\nstruct main_Output {\n    metal::float4 member [[color(0)]];\n};\nfragment main_Output main_(\n) {\n    type_2 _e0 = ret_array_array();\n    return main_Output { metal::float4(_e0.inner[0].inner[0], _e0.inner[0].inner[1], 0.0, 1.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicCompareExchange.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_3 {\n    metal::atomic_int inner[128];\n};\nstruct type_5 {\n    metal::atomic_uint inner[128];\n};\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n    char _pad2[3];\n};\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n    char _pad2[3];\n};\n\ntemplate <typename A>\n_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit(\n    device A *atomic_ptr,\n    int cmp,\n    int v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Sint_4_{cmp, swapped};\n}\ntemplate <typename A>\n_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit(\n    threadgroup A *atomic_ptr,\n    int cmp,\n    int v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Sint_4_{cmp, swapped};\n}\n\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    device A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    threadgroup A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\nconstant uint SIZE = 128u;\n\nkernel void test_atomic_compare_exchange_i32_(\n  device type_3& arr_i32_ [[user(fake0)]]\n) {\n    uint i = 0u;\n    int old = {};\n    bool exchanged = {};\n    uint2 loop_bound = uint2(4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e27 = i;\n            i = _e27 + 1u;\n        }\n        loop_init = false;\n        uint _e2 = i;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i;\n            int _e8 = metal::atomic_load_explicit(&arr_i32_.inner[_e6], metal::memory_order_relaxed);\n            old = _e8;\n            exchanged = false;\n            uint2 loop_bound_1 = uint2(4294967295u);\n            while(true) {\n                if (metal::all(loop_bound_1 == uint2(0u))) { break; }\n                loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n                bool _e12 = exchanged;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    int _e14 = old;\n                    int new_ = as_type<int>(as_type<float>(_e14) + 1.0);\n                    uint _e20 = i;\n                    int _e22 = old;\n                    _atomic_compare_exchange_result_Sint_4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_i32_.inner[_e20], _e22, new_);\n                    old = _e23.old_value;\n                    exchanged = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n\n\nkernel void test_atomic_compare_exchange_u32_(\n  device type_5& arr_u32_ [[user(fake0)]]\n) {\n    uint i_1 = 0u;\n    uint old_1 = {};\n    bool exchanged_1 = {};\n    uint2 loop_bound_2 = uint2(4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (metal::all(loop_bound_2 == uint2(0u))) { break; }\n        loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n        if (!loop_init_1) {\n            uint _e27 = i_1;\n            i_1 = _e27 + 1u;\n        }\n        loop_init_1 = false;\n        uint _e2 = i_1;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            uint _e6 = i_1;\n            uint _e8 = metal::atomic_load_explicit(&arr_u32_.inner[_e6], metal::memory_order_relaxed);\n            old_1 = _e8;\n            exchanged_1 = false;\n            uint2 loop_bound_3 = uint2(4294967295u);\n            while(true) {\n                if (metal::all(loop_bound_3 == uint2(0u))) { break; }\n                loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n                bool _e12 = exchanged_1;\n                if (!(_e12)) {\n                } else {\n                    break;\n                }\n                {\n                    uint _e14 = old_1;\n                    uint new_1 = as_type<uint>(as_type<float>(_e14) + 1.0);\n                    uint _e20 = i_1;\n                    uint _e22 = old_1;\n                    _atomic_compare_exchange_result_Uint_4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_u32_.inner[_e20], _e22, new_1);\n                    old_1 = _e23.old_value;\n                    exchanged_1 = _e23.exchanged;\n                }\n            }\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicOps-float32.metal",
    "content": "// language: metal3.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    metal::atomic_float inner[2];\n};\nstruct Struct {\n    metal::atomic_float atomic_scalar;\n    type_2 atomic_arr;\n};\n\nstruct cs_mainInput {\n};\nkernel void cs_main(\n  metal::uint3 id [[thread_position_in_threadgroup]]\n, device metal::atomic_float& storage_atomic_scalar [[user(fake0)]]\n, device type_2& storage_atomic_arr [[user(fake0)]]\n, device Struct& storage_struct [[user(fake0)]]\n) {\n    metal::atomic_store_explicit(&storage_atomic_scalar, 1.5, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_struct.atomic_scalar, 1.5, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_struct.atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    float l0_ = metal::atomic_load_explicit(&storage_atomic_scalar, metal::memory_order_relaxed);\n    float l1_ = metal::atomic_load_explicit(&storage_atomic_arr.inner[1], metal::memory_order_relaxed);\n    float l2_ = metal::atomic_load_explicit(&storage_struct.atomic_scalar, metal::memory_order_relaxed);\n    float l3_ = metal::atomic_load_explicit(&storage_struct.atomic_arr.inner[1], metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    float _e27 = metal::atomic_fetch_add_explicit(&storage_atomic_scalar, 1.5, metal::memory_order_relaxed);\n    float _e31 = metal::atomic_fetch_add_explicit(&storage_atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    float _e35 = metal::atomic_fetch_add_explicit(&storage_struct.atomic_scalar, 1.5, metal::memory_order_relaxed);\n    float _e40 = metal::atomic_fetch_add_explicit(&storage_struct.atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    float _e43 = metal::atomic_exchange_explicit(&storage_atomic_scalar, 1.5, metal::memory_order_relaxed);\n    float _e47 = metal::atomic_exchange_explicit(&storage_atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    float _e51 = metal::atomic_exchange_explicit(&storage_struct.atomic_scalar, 1.5, metal::memory_order_relaxed);\n    float _e56 = metal::atomic_exchange_explicit(&storage_struct.atomic_arr.inner[1], 1.5, metal::memory_order_relaxed);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicOps-int64-min-max.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    metal::atomic_ulong inner[2];\n};\nstruct Struct {\n    metal::atomic_ulong atomic_scalar;\n    type_2 atomic_arr;\n};\n\nstruct cs_mainInput {\n};\nkernel void cs_main(\n  metal::uint3 id [[thread_position_in_threadgroup]]\n, device metal::atomic_ulong& storage_atomic_scalar [[user(fake0)]]\n, device type_2& storage_atomic_arr [[user(fake0)]]\n, device Struct& storage_struct [[user(fake0)]]\n, constant ulong& input [[user(fake0)]]\n) {\n    ulong _e3 = input;\n    metal::atomic_max_explicit(&storage_atomic_scalar, _e3, metal::memory_order_relaxed);\n    ulong _e7 = input;\n    metal::atomic_max_explicit(&storage_atomic_arr.inner[1], 1uL + _e7, metal::memory_order_relaxed);\n    metal::atomic_max_explicit(&storage_struct.atomic_scalar, 1uL, metal::memory_order_relaxed);\n    metal::atomic_max_explicit(&storage_struct.atomic_arr.inner[1], static_cast<ulong>(id.x), metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    ulong _e20 = input;\n    metal::atomic_min_explicit(&storage_atomic_scalar, _e20, metal::memory_order_relaxed);\n    ulong _e24 = input;\n    metal::atomic_min_explicit(&storage_atomic_arr.inner[1], 1uL + _e24, metal::memory_order_relaxed);\n    metal::atomic_min_explicit(&storage_struct.atomic_scalar, 1uL, metal::memory_order_relaxed);\n    metal::atomic_min_explicit(&storage_struct.atomic_arr.inner[1], static_cast<ulong>(id.x), metal::memory_order_relaxed);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicOps.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_4 {\n    metal::atomic_int inner[2];\n};\nstruct Struct {\n    metal::atomic_uint atomic_scalar;\n    type_4 atomic_arr;\n};\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n    char _pad2[3];\n};\nstruct _atomic_compare_exchange_result_Sint_4_ {\n    int old_value;\n    bool exchanged;\n    char _pad2[3];\n};\n\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    device A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    threadgroup A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\n\ntemplate <typename A>\n_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit(\n    device A *atomic_ptr,\n    int cmp,\n    int v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Sint_4_{cmp, swapped};\n}\ntemplate <typename A>\n_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit(\n    threadgroup A *atomic_ptr,\n    int cmp,\n    int v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Sint_4_{cmp, swapped};\n}\n\nstruct cs_mainInput {\n};\nkernel void cs_main(\n  metal::uint3 id [[thread_position_in_threadgroup]]\n, device metal::atomic_uint& storage_atomic_scalar [[user(fake0)]]\n, device type_4& storage_atomic_arr [[user(fake0)]]\n, device Struct& storage_struct [[user(fake0)]]\n, threadgroup metal::atomic_uint& workgroup_atomic_scalar\n, threadgroup type_4& workgroup_atomic_arr\n, threadgroup Struct& workgroup_struct\n) {\n    if (metal::all(id == metal::uint3(0u))) {\n        metal::atomic_store_explicit(&workgroup_atomic_scalar, 0, metal::memory_order_relaxed);\n        for (int __i0 = 0; __i0 < 2; __i0++) {\n            metal::atomic_store_explicit(&workgroup_atomic_arr.inner[__i0], 0, metal::memory_order_relaxed);\n        }\n        metal::atomic_store_explicit(&workgroup_struct.atomic_scalar, 0, metal::memory_order_relaxed);\n        for (int __i0 = 0; __i0 < 2; __i0++) {\n            metal::atomic_store_explicit(&workgroup_struct.atomic_arr.inner[__i0], 0, metal::memory_order_relaxed);\n        }\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::atomic_store_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint l0_ = metal::atomic_load_explicit(&storage_atomic_scalar, metal::memory_order_relaxed);\n    int l1_ = metal::atomic_load_explicit(&storage_atomic_arr.inner[1], metal::memory_order_relaxed);\n    uint l2_ = metal::atomic_load_explicit(&storage_struct.atomic_scalar, metal::memory_order_relaxed);\n    int l3_ = metal::atomic_load_explicit(&storage_struct.atomic_arr.inner[1], metal::memory_order_relaxed);\n    uint l4_ = metal::atomic_load_explicit(&workgroup_atomic_scalar, metal::memory_order_relaxed);\n    int l5_ = metal::atomic_load_explicit(&workgroup_atomic_arr.inner[1], metal::memory_order_relaxed);\n    uint l6_ = metal::atomic_load_explicit(&workgroup_struct.atomic_scalar, metal::memory_order_relaxed);\n    int l7_ = metal::atomic_load_explicit(&workgroup_struct.atomic_arr.inner[1], metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e51 = metal::atomic_fetch_add_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e55 = metal::atomic_fetch_add_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e59 = metal::atomic_fetch_add_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e64 = metal::atomic_fetch_add_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e67 = metal::atomic_fetch_add_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e71 = metal::atomic_fetch_add_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e75 = metal::atomic_fetch_add_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e80 = metal::atomic_fetch_add_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e83 = metal::atomic_fetch_sub_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e87 = metal::atomic_fetch_sub_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e91 = metal::atomic_fetch_sub_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e96 = metal::atomic_fetch_sub_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e99 = metal::atomic_fetch_sub_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e103 = metal::atomic_fetch_sub_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e107 = metal::atomic_fetch_sub_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e112 = metal::atomic_fetch_sub_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e115 = metal::atomic_fetch_max_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e119 = metal::atomic_fetch_max_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e123 = metal::atomic_fetch_max_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e128 = metal::atomic_fetch_max_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e131 = metal::atomic_fetch_max_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e135 = metal::atomic_fetch_max_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e139 = metal::atomic_fetch_max_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e144 = metal::atomic_fetch_max_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e147 = metal::atomic_fetch_min_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e151 = metal::atomic_fetch_min_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e155 = metal::atomic_fetch_min_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e160 = metal::atomic_fetch_min_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e163 = metal::atomic_fetch_min_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e167 = metal::atomic_fetch_min_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e171 = metal::atomic_fetch_min_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e176 = metal::atomic_fetch_min_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e179 = metal::atomic_fetch_and_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e183 = metal::atomic_fetch_and_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e187 = metal::atomic_fetch_and_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e192 = metal::atomic_fetch_and_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e195 = metal::atomic_fetch_and_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e199 = metal::atomic_fetch_and_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e203 = metal::atomic_fetch_and_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e208 = metal::atomic_fetch_and_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e211 = metal::atomic_fetch_or_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e215 = metal::atomic_fetch_or_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e219 = metal::atomic_fetch_or_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e224 = metal::atomic_fetch_or_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e227 = metal::atomic_fetch_or_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e231 = metal::atomic_fetch_or_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e235 = metal::atomic_fetch_or_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e240 = metal::atomic_fetch_or_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint _e243 = metal::atomic_fetch_xor_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e247 = metal::atomic_fetch_xor_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e251 = metal::atomic_fetch_xor_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e256 = metal::atomic_fetch_xor_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e259 = metal::atomic_fetch_xor_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e263 = metal::atomic_fetch_xor_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e267 = metal::atomic_fetch_xor_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e272 = metal::atomic_fetch_xor_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e275 = metal::atomic_exchange_explicit(&storage_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e279 = metal::atomic_exchange_explicit(&storage_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e283 = metal::atomic_exchange_explicit(&storage_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e288 = metal::atomic_exchange_explicit(&storage_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e291 = metal::atomic_exchange_explicit(&workgroup_atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e295 = metal::atomic_exchange_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    uint _e299 = metal::atomic_exchange_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e304 = metal::atomic_exchange_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed);\n    _atomic_compare_exchange_result_Uint_4_ _e308 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_scalar, 1u, 2u);\n    _atomic_compare_exchange_result_Sint_4_ _e313 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_arr.inner[1], 1, 2);\n    _atomic_compare_exchange_result_Uint_4_ _e318 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_scalar, 1u, 2u);\n    _atomic_compare_exchange_result_Sint_4_ _e324 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_arr.inner[1], 1, 2);\n    _atomic_compare_exchange_result_Uint_4_ _e328 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_scalar, 1u, 2u);\n    _atomic_compare_exchange_result_Sint_4_ _e333 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_arr.inner[1], 1, 2);\n    _atomic_compare_exchange_result_Uint_4_ _e338 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_scalar, 1u, 2u);\n    _atomic_compare_exchange_result_Sint_4_ _e344 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_arr.inner[1], 1, 2);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicTexture-int64.metal",
    "content": "// language: metal3.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct cs_mainInput {\n};\nkernel void cs_main(\n  metal::uint3 id [[thread_position_in_threadgroup]]\n, metal::texture2d<ulong, metal::access::read_write> image [[user(fake0)]]\n) {\n    image.atomic_max(metal::uint2(metal::int2(0, 0)), 1uL);\n    if (metal::int2(0, 0).x == -99999) { image.write(ulong4(0uL), metal::uint2(metal::int2(0, 0))); }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    image.atomic_min(metal::uint2(metal::int2(0, 0)), 1uL);\n    if (metal::int2(0, 0).x == -99999) { image.write(ulong4(0uL), metal::uint2(metal::int2(0, 0))); }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-atomicTexture.metal",
    "content": "// language: metal3.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct cs_mainInput {\n};\nkernel void cs_main(\n  metal::uint3 id [[thread_position_in_threadgroup]]\n, metal::texture2d<uint, metal::access::read_write> image_u [[user(fake0)]]\n, metal::texture2d<int, metal::access::read_write> image_s [[user(fake0)]]\n) {\n    image_u.atomic_fetch_max(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_u.atomic_fetch_min(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_u.atomic_fetch_add(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_u.atomic_fetch_and(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_u.atomic_fetch_or(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_u.atomic_fetch_xor(metal::uint2(metal::int2(0, 0)), 1u);\n    if (metal::int2(0, 0).x == -99999) { image_u.write(uint4(0u), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_max(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_min(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_add(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_and(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_or(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    image_s.atomic_fetch_xor(metal::uint2(metal::int2(0, 0)), 1);\n    if (metal::int2(0, 0).x == -99999) { image_s.write(int4(0), metal::uint2(metal::int2(0, 0))); }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-barycentrics.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct fs_mainInput {\n    metal::float3 bary [[barycentric_coord]];\n};\nstruct fs_mainOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fs_mainOutput fs_main(\n  fs_mainInput varyings [[stage_in]]\n) {\n    const auto bary = varyings.bary;\n    return fs_mainOutput { metal::float4(bary, 1.0) };\n}\n\n\nstruct fs_main_no_perspectiveInput {\n    metal::float3 bary_1 [[barycentric_coord, center_no_perspective]];\n};\nstruct fs_main_no_perspectiveOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment fs_main_no_perspectiveOutput fs_main_no_perspective(\n  fs_main_no_perspectiveInput varyings_1 [[stage_in]]\n) {\n    const auto bary_1 = varyings_1.bary_1;\n    return fs_main_no_perspectiveOutput { metal::float4(bary_1, 1.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-binding-arrays.metal",
    "content": "// language: metal3.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\nstruct UniformIndex {\n    uint index;\n};\ntemplate <typename T>\nstruct NagaArgumentBufferWrapper {\n    T inner;\n};\nstruct FragmentIn {\n    uint index;\n};\n\nstruct main_Input {\n    uint index [[user(loc0), flat]];\n};\nstruct main_Output {\n    metal::float4 member [[color(0)]];\n};\nfragment main_Output main_(\n  main_Input varyings [[stage_in]]\n, constant NagaArgumentBufferWrapper<metal::texture2d<float, metal::access::sample>>* texture_array_unbounded [[buffer(0)]]\n, constant NagaArgumentBufferWrapper<metal::texture2d<float, metal::access::sample>>* texture_array_bounded [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::texture2d_array<float, metal::access::sample>>* texture_array_2darray [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::texture2d_ms<float, metal::access::read>>* texture_array_multisampled [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::depth2d<float, metal::access::sample>>* texture_array_depth [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::texture2d<float, metal::access::write>>* texture_array_storage [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::sampler>* samp [[user(fake0)]]\n, constant NagaArgumentBufferWrapper<metal::sampler>* samp_comp [[user(fake0)]]\n, constant UniformIndex& uni [[user(fake0)]]\n) {\n    const FragmentIn fragment_in = { varyings.index };\n    uint u1_ = 0u;\n    metal::uint2 u2_ = metal::uint2(0u);\n    float v1_ = 0.0;\n    metal::float4 v4_ = metal::float4(0.0);\n    uint uniform_index = uni.index;\n    uint non_uniform_index = fragment_in.index;\n    metal::float2 uv = metal::float2(0.0);\n    metal::int2 pix = metal::int2(0);\n    metal::uint2 _e19 = u2_;\n    u2_ = _e19 + metal::uint2(texture_array_unbounded[0].inner.get_width(), texture_array_unbounded[0].inner.get_height());\n    metal::uint2 _e24 = u2_;\n    u2_ = _e24 + metal::uint2(texture_array_unbounded[uniform_index].inner.get_width(), texture_array_unbounded[uniform_index].inner.get_height());\n    metal::uint2 _e29 = u2_;\n    u2_ = _e29 + metal::uint2(texture_array_unbounded[non_uniform_index].inner.get_width(), texture_array_unbounded[non_uniform_index].inner.get_height());\n    metal::float4 _e34 = v4_;\n    metal::float4 _e39 = texture_array_bounded[0].inner.gather(samp[0].inner, uv);\n    v4_ = _e34 + _e39;\n    metal::float4 _e41 = v4_;\n    metal::float4 _e46 = texture_array_bounded[uniform_index].inner.gather(samp[uniform_index].inner, uv);\n    v4_ = _e41 + _e46;\n    metal::float4 _e48 = v4_;\n    metal::float4 _e53 = texture_array_bounded[non_uniform_index].inner.gather(samp[non_uniform_index].inner, uv);\n    v4_ = _e48 + _e53;\n    metal::float4 _e55 = v4_;\n    metal::float4 _e61 = texture_array_depth[0].inner.gather_compare(samp_comp[0].inner, uv, 0.0);\n    v4_ = _e55 + _e61;\n    metal::float4 _e63 = v4_;\n    metal::float4 _e69 = texture_array_depth[uniform_index].inner.gather_compare(samp_comp[uniform_index].inner, uv, 0.0);\n    v4_ = _e63 + _e69;\n    metal::float4 _e71 = v4_;\n    metal::float4 _e77 = texture_array_depth[non_uniform_index].inner.gather_compare(samp_comp[non_uniform_index].inner, uv, 0.0);\n    v4_ = _e71 + _e77;\n    metal::float4 _e79 = v4_;\n    metal::float4 _e83 = (uint(0) < texture_array_unbounded[0].inner.get_num_mip_levels() && metal::all(metal::uint2(pix) < metal::uint2(texture_array_unbounded[0].inner.get_width(0), texture_array_unbounded[0].inner.get_height(0))) ? texture_array_unbounded[0].inner.read(metal::uint2(pix), 0): DefaultConstructible());\n    v4_ = _e79 + _e83;\n    metal::float4 _e85 = v4_;\n    metal::float4 _e89 = (uint(0) < texture_array_unbounded[uniform_index].inner.get_num_mip_levels() && metal::all(metal::uint2(pix) < metal::uint2(texture_array_unbounded[uniform_index].inner.get_width(0), texture_array_unbounded[uniform_index].inner.get_height(0))) ? texture_array_unbounded[uniform_index].inner.read(metal::uint2(pix), 0): DefaultConstructible());\n    v4_ = _e85 + _e89;\n    metal::float4 _e91 = v4_;\n    metal::float4 _e95 = (uint(0) < texture_array_unbounded[non_uniform_index].inner.get_num_mip_levels() && metal::all(metal::uint2(pix) < metal::uint2(texture_array_unbounded[non_uniform_index].inner.get_width(0), texture_array_unbounded[non_uniform_index].inner.get_height(0))) ? texture_array_unbounded[non_uniform_index].inner.read(metal::uint2(pix), 0): DefaultConstructible());\n    v4_ = _e91 + _e95;\n    uint _e97 = u1_;\n    u1_ = _e97 + texture_array_2darray[0].inner.get_array_size();\n    uint _e102 = u1_;\n    u1_ = _e102 + texture_array_2darray[uniform_index].inner.get_array_size();\n    uint _e107 = u1_;\n    u1_ = _e107 + texture_array_2darray[non_uniform_index].inner.get_array_size();\n    uint _e112 = u1_;\n    u1_ = _e112 + texture_array_bounded[0].inner.get_num_mip_levels();\n    uint _e117 = u1_;\n    u1_ = _e117 + texture_array_bounded[uniform_index].inner.get_num_mip_levels();\n    uint _e122 = u1_;\n    u1_ = _e122 + texture_array_bounded[non_uniform_index].inner.get_num_mip_levels();\n    uint _e127 = u1_;\n    u1_ = _e127 + texture_array_multisampled[0].inner.get_num_samples();\n    uint _e132 = u1_;\n    u1_ = _e132 + texture_array_multisampled[uniform_index].inner.get_num_samples();\n    uint _e137 = u1_;\n    u1_ = _e137 + texture_array_multisampled[non_uniform_index].inner.get_num_samples();\n    metal::float4 _e142 = v4_;\n    metal::float4 _e147 = texture_array_bounded[0].inner.sample(samp[0].inner, uv);\n    v4_ = _e142 + _e147;\n    metal::float4 _e149 = v4_;\n    metal::float4 _e154 = texture_array_bounded[uniform_index].inner.sample(samp[uniform_index].inner, uv);\n    v4_ = _e149 + _e154;\n    metal::float4 _e156 = v4_;\n    metal::float4 _e161 = texture_array_bounded[non_uniform_index].inner.sample(samp[non_uniform_index].inner, uv);\n    v4_ = _e156 + _e161;\n    metal::float4 _e163 = v4_;\n    metal::float4 _e169 = texture_array_bounded[0].inner.sample(samp[0].inner, uv, metal::bias(0.0));\n    v4_ = _e163 + _e169;\n    metal::float4 _e171 = v4_;\n    metal::float4 _e177 = texture_array_bounded[uniform_index].inner.sample(samp[uniform_index].inner, uv, metal::bias(0.0));\n    v4_ = _e171 + _e177;\n    metal::float4 _e179 = v4_;\n    metal::float4 _e185 = texture_array_bounded[non_uniform_index].inner.sample(samp[non_uniform_index].inner, uv, metal::bias(0.0));\n    v4_ = _e179 + _e185;\n    float _e187 = v1_;\n    float _e193 = texture_array_depth[0].inner.sample_compare(samp_comp[0].inner, uv, 0.0);\n    v1_ = _e187 + _e193;\n    float _e195 = v1_;\n    float _e201 = texture_array_depth[uniform_index].inner.sample_compare(samp_comp[uniform_index].inner, uv, 0.0);\n    v1_ = _e195 + _e201;\n    float _e203 = v1_;\n    float _e209 = texture_array_depth[non_uniform_index].inner.sample_compare(samp_comp[non_uniform_index].inner, uv, 0.0);\n    v1_ = _e203 + _e209;\n    float _e211 = v1_;\n    float _e217 = texture_array_depth[0].inner.sample_compare(samp_comp[0].inner, uv, 0.0);\n    v1_ = _e211 + _e217;\n    float _e219 = v1_;\n    float _e225 = texture_array_depth[uniform_index].inner.sample_compare(samp_comp[uniform_index].inner, uv, 0.0);\n    v1_ = _e219 + _e225;\n    float _e227 = v1_;\n    float _e233 = texture_array_depth[non_uniform_index].inner.sample_compare(samp_comp[non_uniform_index].inner, uv, 0.0);\n    v1_ = _e227 + _e233;\n    metal::float4 _e235 = v4_;\n    metal::float4 _e240 = texture_array_bounded[0].inner.sample(samp[0].inner, uv, metal::gradient2d(uv, uv));\n    v4_ = _e235 + _e240;\n    metal::float4 _e242 = v4_;\n    metal::float4 _e247 = texture_array_bounded[uniform_index].inner.sample(samp[uniform_index].inner, uv, metal::gradient2d(uv, uv));\n    v4_ = _e242 + _e247;\n    metal::float4 _e249 = v4_;\n    metal::float4 _e254 = texture_array_bounded[non_uniform_index].inner.sample(samp[non_uniform_index].inner, uv, metal::gradient2d(uv, uv));\n    v4_ = _e249 + _e254;\n    metal::float4 _e256 = v4_;\n    metal::float4 _e262 = texture_array_bounded[0].inner.sample(samp[0].inner, uv, metal::level(0.0));\n    v4_ = _e256 + _e262;\n    metal::float4 _e264 = v4_;\n    metal::float4 _e270 = texture_array_bounded[uniform_index].inner.sample(samp[uniform_index].inner, uv, metal::level(0.0));\n    v4_ = _e264 + _e270;\n    metal::float4 _e272 = v4_;\n    metal::float4 _e278 = texture_array_bounded[non_uniform_index].inner.sample(samp[non_uniform_index].inner, uv, metal::level(0.0));\n    v4_ = _e272 + _e278;\n    metal::float4 _e282 = v4_;\n    texture_array_storage[0].inner.write(_e282, metal::uint2(pix));\n    metal::float4 _e285 = v4_;\n    texture_array_storage[uniform_index].inner.write(_e285, metal::uint2(pix));\n    metal::float4 _e288 = v4_;\n    texture_array_storage[non_uniform_index].inner.write(_e288, metal::uint2(pix));\n    metal::uint2 _e289 = u2_;\n    uint _e290 = u1_;\n    metal::float2 v2_ = static_cast<metal::float2>(_e289 + metal::uint2(_e290));\n    metal::float4 _e294 = v4_;\n    float _e301 = v1_;\n    return main_Output { (_e294 + metal::float4(v2_.x, v2_.y, v2_.x, v2_.y)) + metal::float4(_e301) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bitcast.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    metal::int2 i2_ = metal::int2(0);\n    metal::int3 i3_ = metal::int3(0);\n    metal::int4 i4_ = metal::int4(0);\n    metal::uint2 u2_ = metal::uint2(0u);\n    metal::uint3 u3_ = metal::uint3(0u);\n    metal::uint4 u4_ = metal::uint4(0u);\n    metal::float2 f2_ = metal::float2(0.0);\n    metal::float3 f3_ = metal::float3(0.0);\n    metal::float4 f4_ = metal::float4(0.0);\n    metal::int2 _e27 = i2_;\n    u2_ = as_type<metal::uint2>(_e27);\n    metal::int3 _e29 = i3_;\n    u3_ = as_type<metal::uint3>(_e29);\n    metal::int4 _e31 = i4_;\n    u4_ = as_type<metal::uint4>(_e31);\n    metal::uint2 _e33 = u2_;\n    i2_ = as_type<metal::int2>(_e33);\n    metal::uint3 _e35 = u3_;\n    i3_ = as_type<metal::int3>(_e35);\n    metal::uint4 _e37 = u4_;\n    i4_ = as_type<metal::int4>(_e37);\n    metal::int2 _e39 = i2_;\n    f2_ = as_type<metal::float2>(_e39);\n    metal::int3 _e41 = i3_;\n    f3_ = as_type<metal::float3>(_e41);\n    metal::int4 _e43 = i4_;\n    f4_ = as_type<metal::float4>(_e43);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bits-optimized-msl.metal",
    "content": "// language: metal2.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    int i = 0;\n    metal::int2 i2_ = metal::int2(0);\n    metal::int3 i3_ = metal::int3(0);\n    metal::int4 i4_ = metal::int4(0);\n    uint u = 0u;\n    metal::uint2 u2_ = metal::uint2(0u);\n    metal::uint3 u3_ = metal::uint3(0u);\n    metal::uint4 u4_ = metal::uint4(0u);\n    metal::float2 f2_ = metal::float2(0.0);\n    metal::float4 f4_ = metal::float4(0.0);\n    metal::float4 _e28 = f4_;\n    u = metal::pack_float_to_snorm4x8(_e28);\n    metal::float4 _e30 = f4_;\n    u = metal::pack_float_to_unorm4x8(_e30);\n    metal::float2 _e32 = f2_;\n    u = metal::pack_float_to_snorm2x16(_e32);\n    metal::float2 _e34 = f2_;\n    u = metal::pack_float_to_unorm2x16(_e34);\n    metal::float2 _e36 = f2_;\n    u = as_type<uint>(half2(_e36));\n    metal::int4 _e38 = i4_;\n    u = as_type<uint>(packed_char4(_e38));\n    metal::uint4 _e40 = u4_;\n    u = as_type<uint>(packed_uchar4(_e40));\n    metal::int4 _e42 = i4_;\n    u = as_type<uint>(packed_char4(metal::clamp(_e42, -128, 127)));\n    metal::uint4 _e44 = u4_;\n    u = as_type<uint>(packed_uchar4(metal::clamp(_e44, 0, 255)));\n    uint _e46 = u;\n    f4_ = metal::unpack_snorm4x8_to_float(_e46);\n    uint _e48 = u;\n    f4_ = metal::unpack_unorm4x8_to_float(_e48);\n    uint _e50 = u;\n    f2_ = metal::unpack_snorm2x16_to_float(_e50);\n    uint _e52 = u;\n    f2_ = metal::unpack_unorm2x16_to_float(_e52);\n    uint _e54 = u;\n    f2_ = float2(as_type<half2>(_e54));\n    uint _e56 = u;\n    i4_ = int4(as_type<packed_char4>(_e56));\n    uint _e58 = u;\n    u4_ = uint4(as_type<packed_uchar4>(_e58));\n    int _e60 = i;\n    int _e61 = i;\n    i = metal::insert_bits(_e60, _e61, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int2 _e65 = i2_;\n    metal::int2 _e66 = i2_;\n    i2_ = metal::insert_bits(_e65, _e66, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int3 _e70 = i3_;\n    metal::int3 _e71 = i3_;\n    i3_ = metal::insert_bits(_e70, _e71, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int4 _e75 = i4_;\n    metal::int4 _e76 = i4_;\n    i4_ = metal::insert_bits(_e75, _e76, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    uint _e80 = u;\n    uint _e81 = u;\n    u = metal::insert_bits(_e80, _e81, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint2 _e85 = u2_;\n    metal::uint2 _e86 = u2_;\n    u2_ = metal::insert_bits(_e85, _e86, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint3 _e90 = u3_;\n    metal::uint3 _e91 = u3_;\n    u3_ = metal::insert_bits(_e90, _e91, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint4 _e95 = u4_;\n    metal::uint4 _e96 = u4_;\n    u4_ = metal::insert_bits(_e95, _e96, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    int _e100 = i;\n    i = metal::extract_bits(_e100, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int2 _e104 = i2_;\n    i2_ = metal::extract_bits(_e104, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int3 _e108 = i3_;\n    i3_ = metal::extract_bits(_e108, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int4 _e112 = i4_;\n    i4_ = metal::extract_bits(_e112, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    uint _e116 = u;\n    u = metal::extract_bits(_e116, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint2 _e120 = u2_;\n    u2_ = metal::extract_bits(_e120, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint3 _e124 = u3_;\n    u3_ = metal::extract_bits(_e124, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint4 _e128 = u4_;\n    u4_ = metal::extract_bits(_e128, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    int _e132 = i;\n    i = (((metal::ctz(_e132) + 1) % 33) - 1);\n    metal::uint2 _e134 = u2_;\n    u2_ = (((metal::ctz(_e134) + 1) % 33) - 1);\n    metal::int3 _e136 = i3_;\n    i3_ = metal::select(31 - metal::clz(metal::select(_e136, ~_e136, _e136 < 0)), int3(-1), _e136 == 0 || _e136 == -1);\n    metal::uint3 _e138 = u3_;\n    u3_ = metal::select(31 - metal::clz(_e138), uint3(-1), _e138 == 0 || _e138 == -1);\n    int _e140 = i;\n    i = metal::select(31 - metal::clz(metal::select(_e140, ~_e140, _e140 < 0)), int(-1), _e140 == 0 || _e140 == -1);\n    uint _e142 = u;\n    u = metal::select(31 - metal::clz(_e142), uint(-1), _e142 == 0 || _e142 == -1);\n    int _e144 = i;\n    i = metal::popcount(_e144);\n    metal::int2 _e146 = i2_;\n    i2_ = metal::popcount(_e146);\n    metal::int3 _e148 = i3_;\n    i3_ = metal::popcount(_e148);\n    metal::int4 _e150 = i4_;\n    i4_ = metal::popcount(_e150);\n    uint _e152 = u;\n    u = metal::popcount(_e152);\n    metal::uint2 _e154 = u2_;\n    u2_ = metal::popcount(_e154);\n    metal::uint3 _e156 = u3_;\n    u3_ = metal::popcount(_e156);\n    metal::uint4 _e158 = u4_;\n    u4_ = metal::popcount(_e158);\n    int _e160 = i;\n    i = metal::reverse_bits(_e160);\n    metal::int2 _e162 = i2_;\n    i2_ = metal::reverse_bits(_e162);\n    metal::int3 _e164 = i3_;\n    i3_ = metal::reverse_bits(_e164);\n    metal::int4 _e166 = i4_;\n    i4_ = metal::reverse_bits(_e166);\n    uint _e168 = u;\n    u = metal::reverse_bits(_e168);\n    metal::uint2 _e170 = u2_;\n    u2_ = metal::reverse_bits(_e170);\n    metal::uint3 _e172 = u3_;\n    u3_ = metal::reverse_bits(_e172);\n    metal::uint4 _e174 = u4_;\n    u4_ = metal::reverse_bits(_e174);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bits.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    int i = 0;\n    metal::int2 i2_ = metal::int2(0);\n    metal::int3 i3_ = metal::int3(0);\n    metal::int4 i4_ = metal::int4(0);\n    uint u = 0u;\n    metal::uint2 u2_ = metal::uint2(0u);\n    metal::uint3 u3_ = metal::uint3(0u);\n    metal::uint4 u4_ = metal::uint4(0u);\n    metal::float2 f2_ = metal::float2(0.0);\n    metal::float4 f4_ = metal::float4(0.0);\n    metal::float4 _e28 = f4_;\n    u = metal::pack_float_to_snorm4x8(_e28);\n    metal::float4 _e30 = f4_;\n    u = metal::pack_float_to_unorm4x8(_e30);\n    metal::float2 _e32 = f2_;\n    u = metal::pack_float_to_snorm2x16(_e32);\n    metal::float2 _e34 = f2_;\n    u = metal::pack_float_to_unorm2x16(_e34);\n    metal::float2 _e36 = f2_;\n    u = as_type<uint>(half2(_e36));\n    metal::int4 _e38 = i4_;\n    u = uint((_e38[0] & 0xFF) | ((_e38[1] & 0xFF) << 8) | ((_e38[2] & 0xFF) << 16) | ((_e38[3] & 0xFF) << 24));\n    metal::uint4 _e40 = u4_;\n    u = (_e40[0] & 0xFF) | ((_e40[1] & 0xFF) << 8) | ((_e40[2] & 0xFF) << 16) | ((_e40[3] & 0xFF) << 24);\n    metal::int4 _e42 = i4_;\n    u = uint((metal::clamp(_e42, -128, 127)[0] & 0xFF) | ((metal::clamp(_e42, -128, 127)[1] & 0xFF) << 8) | ((metal::clamp(_e42, -128, 127)[2] & 0xFF) << 16) | ((metal::clamp(_e42, -128, 127)[3] & 0xFF) << 24));\n    metal::uint4 _e44 = u4_;\n    u = (metal::clamp(_e44, 0, 255)[0] & 0xFF) | ((metal::clamp(_e44, 0, 255)[1] & 0xFF) << 8) | ((metal::clamp(_e44, 0, 255)[2] & 0xFF) << 16) | ((metal::clamp(_e44, 0, 255)[3] & 0xFF) << 24);\n    uint _e46 = u;\n    f4_ = metal::unpack_snorm4x8_to_float(_e46);\n    uint _e48 = u;\n    f4_ = metal::unpack_unorm4x8_to_float(_e48);\n    uint _e50 = u;\n    f2_ = metal::unpack_snorm2x16_to_float(_e50);\n    uint _e52 = u;\n    f2_ = metal::unpack_unorm2x16_to_float(_e52);\n    uint _e54 = u;\n    f2_ = float2(as_type<half2>(_e54));\n    uint _e56 = u;\n    i4_ = (int4(_e56, _e56 >> 8, _e56 >> 16, _e56 >> 24) << 24 >> 24);\n    uint _e58 = u;\n    u4_ = (uint4(_e58, _e58 >> 8, _e58 >> 16, _e58 >> 24) << 24 >> 24);\n    int _e60 = i;\n    int _e61 = i;\n    i = metal::insert_bits(_e60, _e61, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int2 _e65 = i2_;\n    metal::int2 _e66 = i2_;\n    i2_ = metal::insert_bits(_e65, _e66, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int3 _e70 = i3_;\n    metal::int3 _e71 = i3_;\n    i3_ = metal::insert_bits(_e70, _e71, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int4 _e75 = i4_;\n    metal::int4 _e76 = i4_;\n    i4_ = metal::insert_bits(_e75, _e76, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    uint _e80 = u;\n    uint _e81 = u;\n    u = metal::insert_bits(_e80, _e81, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint2 _e85 = u2_;\n    metal::uint2 _e86 = u2_;\n    u2_ = metal::insert_bits(_e85, _e86, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint3 _e90 = u3_;\n    metal::uint3 _e91 = u3_;\n    u3_ = metal::insert_bits(_e90, _e91, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint4 _e95 = u4_;\n    metal::uint4 _e96 = u4_;\n    u4_ = metal::insert_bits(_e95, _e96, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    int _e100 = i;\n    i = metal::extract_bits(_e100, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int2 _e104 = i2_;\n    i2_ = metal::extract_bits(_e104, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int3 _e108 = i3_;\n    i3_ = metal::extract_bits(_e108, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::int4 _e112 = i4_;\n    i4_ = metal::extract_bits(_e112, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    uint _e116 = u;\n    u = metal::extract_bits(_e116, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint2 _e120 = u2_;\n    u2_ = metal::extract_bits(_e120, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint3 _e124 = u3_;\n    u3_ = metal::extract_bits(_e124, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    metal::uint4 _e128 = u4_;\n    u4_ = metal::extract_bits(_e128, metal::min(5u, 32u), metal::min(10u, 32u - metal::min(5u, 32u)));\n    int _e132 = i;\n    i = (((metal::ctz(_e132) + 1) % 33) - 1);\n    metal::uint2 _e134 = u2_;\n    u2_ = (((metal::ctz(_e134) + 1) % 33) - 1);\n    metal::int3 _e136 = i3_;\n    i3_ = metal::select(31 - metal::clz(metal::select(_e136, ~_e136, _e136 < 0)), int3(-1), _e136 == 0 || _e136 == -1);\n    metal::uint3 _e138 = u3_;\n    u3_ = metal::select(31 - metal::clz(_e138), uint3(-1), _e138 == 0 || _e138 == -1);\n    int _e140 = i;\n    i = metal::select(31 - metal::clz(metal::select(_e140, ~_e140, _e140 < 0)), int(-1), _e140 == 0 || _e140 == -1);\n    uint _e142 = u;\n    u = metal::select(31 - metal::clz(_e142), uint(-1), _e142 == 0 || _e142 == -1);\n    int _e144 = i;\n    i = metal::popcount(_e144);\n    metal::int2 _e146 = i2_;\n    i2_ = metal::popcount(_e146);\n    metal::int3 _e148 = i3_;\n    i3_ = metal::popcount(_e148);\n    metal::int4 _e150 = i4_;\n    i4_ = metal::popcount(_e150);\n    uint _e152 = u;\n    u = metal::popcount(_e152);\n    metal::uint2 _e154 = u2_;\n    u2_ = metal::popcount(_e154);\n    metal::uint3 _e156 = u3_;\n    u3_ = metal::popcount(_e156);\n    metal::uint4 _e158 = u4_;\n    u4_ = metal::popcount(_e158);\n    int _e160 = i;\n    i = metal::reverse_bits(_e160);\n    metal::int2 _e162 = i2_;\n    i2_ = metal::reverse_bits(_e162);\n    metal::int3 _e164 = i3_;\n    i3_ = metal::reverse_bits(_e164);\n    metal::int4 _e166 = i4_;\n    i4_ = metal::reverse_bits(_e166);\n    uint _e168 = u;\n    u = metal::reverse_bits(_e168);\n    metal::uint2 _e170 = u2_;\n    u2_ = metal::reverse_bits(_e170);\n    metal::uint3 _e172 = u3_;\n    u3_ = metal::reverse_bits(_e172);\n    metal::uint4 _e174 = u4_;\n    u4_ = metal::reverse_bits(_e174);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-boids.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size1;\n    uint size2;\n};\n\nstruct Particle {\n    metal::float2 pos;\n    metal::float2 vel;\n};\nstruct SimParams {\n    float deltaT;\n    float rule1Distance;\n    float rule2Distance;\n    float rule3Distance;\n    float rule1Scale;\n    float rule2Scale;\n    float rule3Scale;\n};\ntypedef Particle type_3[1];\nstruct Particles {\n    type_3 particles;\n};\nconstant uint NUM_PARTICLES = 1500u;\n\nstruct main_Input {\n};\nkernel void main_(\n  metal::uint3 global_invocation_id [[thread_position_in_grid]]\n, constant SimParams& params [[buffer(0)]]\n, device Particles const& particlesSrc [[buffer(1)]]\n, device Particles& particlesDst [[buffer(2)]]\n, constant _mslBufferSizes& _buffer_sizes [[buffer(3)]]\n) {\n    metal::float2 vPos = {};\n    metal::float2 vVel = {};\n    metal::float2 cMass = metal::float2(0.0, 0.0);\n    metal::float2 cVel = metal::float2(0.0, 0.0);\n    metal::float2 colVel = metal::float2(0.0, 0.0);\n    int cMassCount = 0;\n    int cVelCount = 0;\n    metal::float2 pos = {};\n    metal::float2 vel = {};\n    uint i = 0u;\n    uint index = global_invocation_id.x;\n    if (index >= NUM_PARTICLES) {\n        return;\n    }\n    metal::float2 _e8 = particlesSrc.particles[index].pos;\n    vPos = _e8;\n    metal::float2 _e14 = particlesSrc.particles[index].vel;\n    vVel = _e14;\n    uint2 loop_bound = uint2(4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e91 = i;\n            i = _e91 + 1u;\n        }\n        loop_init = false;\n        uint _e36 = i;\n        if (_e36 >= NUM_PARTICLES) {\n            break;\n        }\n        uint _e39 = i;\n        if (_e39 == index) {\n            continue;\n        }\n        uint _e43 = i;\n        metal::float2 _e46 = particlesSrc.particles[_e43].pos;\n        pos = _e46;\n        uint _e49 = i;\n        metal::float2 _e52 = particlesSrc.particles[_e49].vel;\n        vel = _e52;\n        metal::float2 _e53 = pos;\n        metal::float2 _e54 = vPos;\n        float _e58 = params.rule1Distance;\n        if (metal::distance(_e53, _e54) < _e58) {\n            metal::float2 _e60 = cMass;\n            metal::float2 _e61 = pos;\n            cMass = _e60 + _e61;\n            int _e63 = cMassCount;\n            cMassCount = as_type<int>(as_type<uint>(_e63) + as_type<uint>(1));\n        }\n        metal::float2 _e66 = pos;\n        metal::float2 _e67 = vPos;\n        float _e71 = params.rule2Distance;\n        if (metal::distance(_e66, _e67) < _e71) {\n            metal::float2 _e73 = colVel;\n            metal::float2 _e74 = pos;\n            metal::float2 _e75 = vPos;\n            colVel = _e73 - (_e74 - _e75);\n        }\n        metal::float2 _e78 = pos;\n        metal::float2 _e79 = vPos;\n        float _e83 = params.rule3Distance;\n        if (metal::distance(_e78, _e79) < _e83) {\n            metal::float2 _e85 = cVel;\n            metal::float2 _e86 = vel;\n            cVel = _e85 + _e86;\n            int _e88 = cVelCount;\n            cVelCount = as_type<int>(as_type<uint>(_e88) + as_type<uint>(1));\n        }\n    }\n    int _e94 = cMassCount;\n    if (_e94 > 0) {\n        metal::float2 _e97 = cMass;\n        int _e98 = cMassCount;\n        metal::float2 _e102 = vPos;\n        cMass = (_e97 / metal::float2(static_cast<float>(_e98))) - _e102;\n    }\n    int _e104 = cVelCount;\n    if (_e104 > 0) {\n        metal::float2 _e107 = cVel;\n        int _e108 = cVelCount;\n        cVel = _e107 / metal::float2(static_cast<float>(_e108));\n    }\n    metal::float2 _e112 = vVel;\n    metal::float2 _e113 = cMass;\n    float _e116 = params.rule1Scale;\n    metal::float2 _e119 = colVel;\n    float _e122 = params.rule2Scale;\n    metal::float2 _e125 = cVel;\n    float _e128 = params.rule3Scale;\n    vVel = ((_e112 + (_e113 * _e116)) + (_e119 * _e122)) + (_e125 * _e128);\n    metal::float2 _e131 = vVel;\n    metal::float2 _e133 = vVel;\n    vVel = metal::normalize(_e131) * metal::clamp(metal::length(_e133), 0.0, 0.1);\n    metal::float2 _e139 = vPos;\n    metal::float2 _e140 = vVel;\n    float _e143 = params.deltaT;\n    vPos = _e139 + (_e140 * _e143);\n    float _e147 = vPos.x;\n    if (_e147 < -1.0) {\n        vPos.x = 1.0;\n    }\n    float _e153 = vPos.x;\n    if (_e153 > 1.0) {\n        vPos.x = -1.0;\n    }\n    float _e159 = vPos.y;\n    if (_e159 < -1.0) {\n        vPos.y = 1.0;\n    }\n    float _e165 = vPos.y;\n    if (_e165 > 1.0) {\n        vPos.y = -1.0;\n    }\n    metal::float2 _e174 = vPos;\n    particlesDst.particles[index].pos = _e174;\n    metal::float2 _e179 = vVel;\n    particlesDst.particles[index].vel = _e179;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-image-restrict-depth.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nfloat test_textureLoad_depth_2d(\n    metal::int2 coords,\n    int level,\n    metal::depth2d<float, metal::access::sample> image_depth_2d\n) {\n    uint clamped_lod_e3 = metal::min(uint(level), image_depth_2d.get_num_mip_levels() - 1);\n    float _e3 = image_depth_2d.read(metal::min(metal::uint2(coords), metal::uint2(image_depth_2d.get_width(clamped_lod_e3), image_depth_2d.get_height(clamped_lod_e3)) - 1), clamped_lod_e3);\n    return _e3;\n}\n\nfloat test_textureLoad_depth_2d_array_u(\n    metal::int2 coords_1,\n    uint index,\n    int level_1,\n    metal::depth2d_array<float, metal::access::sample> image_depth_2d_array\n) {\n    uint clamped_lod_e4 = metal::min(uint(level_1), image_depth_2d_array.get_num_mip_levels() - 1);\n    float _e4 = image_depth_2d_array.read(metal::min(metal::uint2(coords_1), metal::uint2(image_depth_2d_array.get_width(clamped_lod_e4), image_depth_2d_array.get_height(clamped_lod_e4)) - 1), metal::min(uint(index), image_depth_2d_array.get_array_size() - 1), clamped_lod_e4);\n    return _e4;\n}\n\nfloat test_textureLoad_depth_2d_array_s(\n    metal::int2 coords_2,\n    int index_1,\n    int level_2,\n    metal::depth2d_array<float, metal::access::sample> image_depth_2d_array\n) {\n    uint clamped_lod_e4 = metal::min(uint(level_2), image_depth_2d_array.get_num_mip_levels() - 1);\n    float _e4 = image_depth_2d_array.read(metal::min(metal::uint2(coords_2), metal::uint2(image_depth_2d_array.get_width(clamped_lod_e4), image_depth_2d_array.get_height(clamped_lod_e4)) - 1), metal::min(uint(index_1), image_depth_2d_array.get_array_size() - 1), clamped_lod_e4);\n    return _e4;\n}\n\nfloat test_textureLoad_depth_multisampled_2d(\n    metal::int2 coords_3,\n    int _sample,\n    metal::depth2d_ms<float, metal::access::read> image_depth_multisampled_2d\n) {\n    float _e3 = image_depth_multisampled_2d.read(metal::min(metal::uint2(coords_3), metal::uint2(image_depth_multisampled_2d.get_width(), image_depth_multisampled_2d.get_height()) - 1), metal::min(uint(_sample), image_depth_multisampled_2d.get_num_samples() - 1));\n    return _e3;\n}\n\nstruct fragment_shaderOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fragment_shaderOutput fragment_shader(\n  metal::depth2d<float, metal::access::sample> image_depth_2d [[user(fake0)]]\n, metal::depth2d_array<float, metal::access::sample> image_depth_2d_array [[user(fake0)]]\n, metal::depth2d_ms<float, metal::access::read> image_depth_multisampled_2d [[user(fake0)]]\n) {\n    float _e2 = test_textureLoad_depth_2d(metal::int2 {}, 0, image_depth_2d);\n    float _e6 = test_textureLoad_depth_2d_array_u(metal::int2 {}, 0u, 0, image_depth_2d_array);\n    float _e10 = test_textureLoad_depth_2d_array_s(metal::int2 {}, 0, 0, image_depth_2d_array);\n    float _e13 = test_textureLoad_depth_multisampled_2d(metal::int2 {}, 0, image_depth_multisampled_2d);\n    return fragment_shaderOutput { metal::float4(0.0, 0.0, 0.0, 0.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-image-restrict.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nmetal::float4 test_textureLoad_1d(\n    int coords,\n    int level,\n    metal::texture1d<float, metal::access::sample> image_1d\n) {\n    metal::float4 _e3 = image_1d.read(metal::min(uint(coords), image_1d.get_width() - 1));\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_2d(\n    metal::int2 coords_1,\n    int level_1,\n    metal::texture2d<float, metal::access::sample> image_2d\n) {\n    uint clamped_lod_e3 = metal::min(uint(level_1), image_2d.get_num_mip_levels() - 1);\n    metal::float4 _e3 = image_2d.read(metal::min(metal::uint2(coords_1), metal::uint2(image_2d.get_width(clamped_lod_e3), image_2d.get_height(clamped_lod_e3)) - 1), clamped_lod_e3);\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_2d_array_u(\n    metal::int2 coords_2,\n    uint index,\n    int level_2,\n    metal::texture2d_array<float, metal::access::sample> image_2d_array\n) {\n    uint clamped_lod_e4 = metal::min(uint(level_2), image_2d_array.get_num_mip_levels() - 1);\n    metal::float4 _e4 = image_2d_array.read(metal::min(metal::uint2(coords_2), metal::uint2(image_2d_array.get_width(clamped_lod_e4), image_2d_array.get_height(clamped_lod_e4)) - 1), metal::min(uint(index), image_2d_array.get_array_size() - 1), clamped_lod_e4);\n    return _e4;\n}\n\nmetal::float4 test_textureLoad_2d_array_s(\n    metal::int2 coords_3,\n    int index_1,\n    int level_3,\n    metal::texture2d_array<float, metal::access::sample> image_2d_array\n) {\n    uint clamped_lod_e4 = metal::min(uint(level_3), image_2d_array.get_num_mip_levels() - 1);\n    metal::float4 _e4 = image_2d_array.read(metal::min(metal::uint2(coords_3), metal::uint2(image_2d_array.get_width(clamped_lod_e4), image_2d_array.get_height(clamped_lod_e4)) - 1), metal::min(uint(index_1), image_2d_array.get_array_size() - 1), clamped_lod_e4);\n    return _e4;\n}\n\nmetal::float4 test_textureLoad_3d(\n    metal::int3 coords_4,\n    int level_4,\n    metal::texture3d<float, metal::access::sample> image_3d\n) {\n    uint clamped_lod_e3 = metal::min(uint(level_4), image_3d.get_num_mip_levels() - 1);\n    metal::float4 _e3 = image_3d.read(metal::min(metal::uint3(coords_4), metal::uint3(image_3d.get_width(clamped_lod_e3), image_3d.get_height(clamped_lod_e3), image_3d.get_depth(clamped_lod_e3)) - 1), clamped_lod_e3);\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_multisampled_2d(\n    metal::int2 coords_5,\n    int _sample,\n    metal::texture2d_ms<float, metal::access::read> image_multisampled_2d\n) {\n    metal::float4 _e3 = image_multisampled_2d.read(metal::min(metal::uint2(coords_5), metal::uint2(image_multisampled_2d.get_width(), image_multisampled_2d.get_height()) - 1), metal::min(uint(_sample), image_multisampled_2d.get_num_samples() - 1));\n    return _e3;\n}\n\nvoid test_textureStore_1d(\n    int coords_6,\n    metal::float4 value,\n    metal::texture1d<float, metal::access::write> image_storage_1d\n) {\n    image_storage_1d.write(value, uint(coords_6));\n    return;\n}\n\nvoid test_textureStore_2d(\n    metal::int2 coords_7,\n    metal::float4 value_1,\n    metal::texture2d<float, metal::access::write> image_storage_2d\n) {\n    image_storage_2d.write(value_1, metal::uint2(coords_7));\n    return;\n}\n\nvoid test_textureStore_2d_array_u(\n    metal::int2 coords_8,\n    uint array_index,\n    metal::float4 value_2,\n    metal::texture2d_array<float, metal::access::write> image_storage_2d_array\n) {\n    image_storage_2d_array.write(value_2, metal::uint2(coords_8), array_index);\n    return;\n}\n\nvoid test_textureStore_2d_array_s(\n    metal::int2 coords_9,\n    int array_index_1,\n    metal::float4 value_3,\n    metal::texture2d_array<float, metal::access::write> image_storage_2d_array\n) {\n    image_storage_2d_array.write(value_3, metal::uint2(coords_9), array_index_1);\n    return;\n}\n\nvoid test_textureStore_3d(\n    metal::int3 coords_10,\n    metal::float4 value_4,\n    metal::texture3d<float, metal::access::write> image_storage_3d\n) {\n    image_storage_3d.write(value_4, metal::uint3(coords_10));\n    return;\n}\n\nstruct fragment_shaderOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fragment_shaderOutput fragment_shader(\n  metal::texture1d<float, metal::access::sample> image_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::sample> image_3d [[user(fake0)]]\n, metal::texture2d_ms<float, metal::access::read> image_multisampled_2d [[user(fake0)]]\n, metal::texture1d<float, metal::access::write> image_storage_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::write> image_storage_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::write> image_storage_2d_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::write> image_storage_3d [[user(fake0)]]\n) {\n    metal::float4 _e2 = test_textureLoad_1d(0, 0, image_1d);\n    metal::float4 _e5 = test_textureLoad_2d(metal::int2 {}, 0, image_2d);\n    metal::float4 _e9 = test_textureLoad_2d_array_u(metal::int2 {}, 0u, 0, image_2d_array);\n    metal::float4 _e13 = test_textureLoad_2d_array_s(metal::int2 {}, 0, 0, image_2d_array);\n    metal::float4 _e16 = test_textureLoad_3d(metal::int3 {}, 0, image_3d);\n    metal::float4 _e19 = test_textureLoad_multisampled_2d(metal::int2 {}, 0, image_multisampled_2d);\n    test_textureStore_1d(0, metal::float4 {}, image_storage_1d);\n    test_textureStore_2d(metal::int2 {}, metal::float4 {}, image_storage_2d);\n    test_textureStore_2d_array_u(metal::int2 {}, 0u, metal::float4 {}, image_storage_2d_array);\n    test_textureStore_2d_array_s(metal::int2 {}, 0, metal::float4 {}, image_storage_2d_array);\n    test_textureStore_3d(metal::int3 {}, metal::float4 {}, image_storage_3d);\n    return fragment_shaderOutput { metal::float4(0.0, 0.0, 0.0, 0.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-image-rzsw-depth.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\n\nfloat test_textureLoad_depth_2d(\n    metal::int2 coords,\n    int level,\n    metal::depth2d<float, metal::access::sample> image_depth_2d\n) {\n    float _e3 = (uint(level) < image_depth_2d.get_num_mip_levels() && metal::all(metal::uint2(coords) < metal::uint2(image_depth_2d.get_width(level), image_depth_2d.get_height(level))) ? image_depth_2d.read(metal::uint2(coords), level): DefaultConstructible());\n    return _e3;\n}\n\nfloat test_textureLoad_depth_2d_array_u(\n    metal::int2 coords_1,\n    uint index,\n    int level_1,\n    metal::depth2d_array<float, metal::access::sample> image_depth_2d_array\n) {\n    float _e4 = (uint(level_1) < image_depth_2d_array.get_num_mip_levels() && uint(index) < image_depth_2d_array.get_array_size() && metal::all(metal::uint2(coords_1) < metal::uint2(image_depth_2d_array.get_width(level_1), image_depth_2d_array.get_height(level_1))) ? image_depth_2d_array.read(metal::uint2(coords_1), index, level_1): DefaultConstructible());\n    return _e4;\n}\n\nfloat test_textureLoad_depth_2d_array_s(\n    metal::int2 coords_2,\n    int index_1,\n    int level_2,\n    metal::depth2d_array<float, metal::access::sample> image_depth_2d_array\n) {\n    float _e4 = (uint(level_2) < image_depth_2d_array.get_num_mip_levels() && uint(index_1) < image_depth_2d_array.get_array_size() && metal::all(metal::uint2(coords_2) < metal::uint2(image_depth_2d_array.get_width(level_2), image_depth_2d_array.get_height(level_2))) ? image_depth_2d_array.read(metal::uint2(coords_2), index_1, level_2): DefaultConstructible());\n    return _e4;\n}\n\nfloat test_textureLoad_depth_multisampled_2d(\n    metal::int2 coords_3,\n    int _sample,\n    metal::depth2d_ms<float, metal::access::read> image_depth_multisampled_2d\n) {\n    float _e3 = (uint(_sample) < image_depth_multisampled_2d.get_num_samples() && metal::all(metal::uint2(coords_3) < metal::uint2(image_depth_multisampled_2d.get_width(), image_depth_multisampled_2d.get_height())) ? image_depth_multisampled_2d.read(metal::uint2(coords_3), _sample): DefaultConstructible());\n    return _e3;\n}\n\nstruct fragment_shaderOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fragment_shaderOutput fragment_shader(\n  metal::depth2d<float, metal::access::sample> image_depth_2d [[user(fake0)]]\n, metal::depth2d_array<float, metal::access::sample> image_depth_2d_array [[user(fake0)]]\n, metal::depth2d_ms<float, metal::access::read> image_depth_multisampled_2d [[user(fake0)]]\n) {\n    float _e2 = test_textureLoad_depth_2d(metal::int2 {}, 0, image_depth_2d);\n    float _e6 = test_textureLoad_depth_2d_array_u(metal::int2 {}, 0u, 0, image_depth_2d_array);\n    float _e10 = test_textureLoad_depth_2d_array_s(metal::int2 {}, 0, 0, image_depth_2d_array);\n    float _e13 = test_textureLoad_depth_multisampled_2d(metal::int2 {}, 0, image_depth_multisampled_2d);\n    return fragment_shaderOutput { metal::float4(0.0, 0.0, 0.0, 0.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-image-rzsw.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\n\nmetal::float4 test_textureLoad_1d(\n    int coords,\n    int level,\n    metal::texture1d<float, metal::access::sample> image_1d\n) {\n    metal::float4 _e3 = (uint(level) < image_1d.get_num_mip_levels() && uint(coords) < image_1d.get_width() ? image_1d.read(uint(coords)): DefaultConstructible());\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_2d(\n    metal::int2 coords_1,\n    int level_1,\n    metal::texture2d<float, metal::access::sample> image_2d\n) {\n    metal::float4 _e3 = (uint(level_1) < image_2d.get_num_mip_levels() && metal::all(metal::uint2(coords_1) < metal::uint2(image_2d.get_width(level_1), image_2d.get_height(level_1))) ? image_2d.read(metal::uint2(coords_1), level_1): DefaultConstructible());\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_2d_array_u(\n    metal::int2 coords_2,\n    uint index,\n    int level_2,\n    metal::texture2d_array<float, metal::access::sample> image_2d_array\n) {\n    metal::float4 _e4 = (uint(level_2) < image_2d_array.get_num_mip_levels() && uint(index) < image_2d_array.get_array_size() && metal::all(metal::uint2(coords_2) < metal::uint2(image_2d_array.get_width(level_2), image_2d_array.get_height(level_2))) ? image_2d_array.read(metal::uint2(coords_2), index, level_2): DefaultConstructible());\n    return _e4;\n}\n\nmetal::float4 test_textureLoad_2d_array_s(\n    metal::int2 coords_3,\n    int index_1,\n    int level_3,\n    metal::texture2d_array<float, metal::access::sample> image_2d_array\n) {\n    metal::float4 _e4 = (uint(level_3) < image_2d_array.get_num_mip_levels() && uint(index_1) < image_2d_array.get_array_size() && metal::all(metal::uint2(coords_3) < metal::uint2(image_2d_array.get_width(level_3), image_2d_array.get_height(level_3))) ? image_2d_array.read(metal::uint2(coords_3), index_1, level_3): DefaultConstructible());\n    return _e4;\n}\n\nmetal::float4 test_textureLoad_3d(\n    metal::int3 coords_4,\n    int level_4,\n    metal::texture3d<float, metal::access::sample> image_3d\n) {\n    metal::float4 _e3 = (uint(level_4) < image_3d.get_num_mip_levels() && metal::all(metal::uint3(coords_4) < metal::uint3(image_3d.get_width(level_4), image_3d.get_height(level_4), image_3d.get_depth(level_4))) ? image_3d.read(metal::uint3(coords_4), level_4): DefaultConstructible());\n    return _e3;\n}\n\nmetal::float4 test_textureLoad_multisampled_2d(\n    metal::int2 coords_5,\n    int _sample,\n    metal::texture2d_ms<float, metal::access::read> image_multisampled_2d\n) {\n    metal::float4 _e3 = (uint(_sample) < image_multisampled_2d.get_num_samples() && metal::all(metal::uint2(coords_5) < metal::uint2(image_multisampled_2d.get_width(), image_multisampled_2d.get_height())) ? image_multisampled_2d.read(metal::uint2(coords_5), _sample): DefaultConstructible());\n    return _e3;\n}\n\nvoid test_textureStore_1d(\n    int coords_6,\n    metal::float4 value,\n    metal::texture1d<float, metal::access::write> image_storage_1d\n) {\n    image_storage_1d.write(value, uint(coords_6));\n    return;\n}\n\nvoid test_textureStore_2d(\n    metal::int2 coords_7,\n    metal::float4 value_1,\n    metal::texture2d<float, metal::access::write> image_storage_2d\n) {\n    image_storage_2d.write(value_1, metal::uint2(coords_7));\n    return;\n}\n\nvoid test_textureStore_2d_array_u(\n    metal::int2 coords_8,\n    uint array_index,\n    metal::float4 value_2,\n    metal::texture2d_array<float, metal::access::write> image_storage_2d_array\n) {\n    image_storage_2d_array.write(value_2, metal::uint2(coords_8), array_index);\n    return;\n}\n\nvoid test_textureStore_2d_array_s(\n    metal::int2 coords_9,\n    int array_index_1,\n    metal::float4 value_3,\n    metal::texture2d_array<float, metal::access::write> image_storage_2d_array\n) {\n    image_storage_2d_array.write(value_3, metal::uint2(coords_9), array_index_1);\n    return;\n}\n\nvoid test_textureStore_3d(\n    metal::int3 coords_10,\n    metal::float4 value_4,\n    metal::texture3d<float, metal::access::write> image_storage_3d\n) {\n    image_storage_3d.write(value_4, metal::uint3(coords_10));\n    return;\n}\n\nstruct fragment_shaderOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fragment_shaderOutput fragment_shader(\n  metal::texture1d<float, metal::access::sample> image_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::sample> image_3d [[user(fake0)]]\n, metal::texture2d_ms<float, metal::access::read> image_multisampled_2d [[user(fake0)]]\n, metal::texture1d<float, metal::access::write> image_storage_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::write> image_storage_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::write> image_storage_2d_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::write> image_storage_3d [[user(fake0)]]\n) {\n    metal::float4 _e2 = test_textureLoad_1d(0, 0, image_1d);\n    metal::float4 _e5 = test_textureLoad_2d(metal::int2 {}, 0, image_2d);\n    metal::float4 _e9 = test_textureLoad_2d_array_u(metal::int2 {}, 0u, 0, image_2d_array);\n    metal::float4 _e13 = test_textureLoad_2d_array_s(metal::int2 {}, 0, 0, image_2d_array);\n    metal::float4 _e16 = test_textureLoad_3d(metal::int3 {}, 0, image_3d);\n    metal::float4 _e19 = test_textureLoad_multisampled_2d(metal::int2 {}, 0, image_multisampled_2d);\n    test_textureStore_1d(0, metal::float4 {}, image_storage_1d);\n    test_textureStore_2d(metal::int2 {}, metal::float4 {}, image_storage_2d);\n    test_textureStore_2d_array_u(metal::int2 {}, 0u, metal::float4 {}, image_storage_2d_array);\n    test_textureStore_2d_array_s(metal::int2 {}, 0, metal::float4 {}, image_storage_2d_array);\n    test_textureStore_3d(metal::int3 {}, metal::float4 {}, image_storage_3d);\n    return fragment_shaderOutput { metal::float4(0.0, 0.0, 0.0, 0.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-restrict.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size0;\n};\n\nstruct type_1 {\n    float inner[10];\n};\ntypedef float type_4[1];\nstruct Globals {\n    type_1 a;\n    char _pad1[8];\n    metal::float4 v;\n    metal::float3x4 m;\n    type_4 d;\n    char _pad4[12];\n};\n\nfloat index_array(\n    int i,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = globals.a.inner[metal::min(unsigned(i), 9u)];\n    return _e4;\n}\n\nfloat index_dynamic_array(\n    int i_1,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = globals.d[metal::min(unsigned(i_1), (_buffer_sizes.size0 - 112 - 4) / 4)];\n    return _e4;\n}\n\nfloat index_vector(\n    int i_2,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = globals.v[metal::min(unsigned(i_2), 3u)];\n    return _e4;\n}\n\nfloat index_vector_by_value(\n    metal::float4 v,\n    int i_3\n) {\n    return v[metal::min(unsigned(i_3), 3u)];\n}\n\nmetal::float4 index_matrix(\n    int i_4,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    metal::float4 _e4 = globals.m[metal::min(unsigned(i_4), 2u)];\n    return _e4;\n}\n\nfloat index_twice(\n    int i_5,\n    int j,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e6 = globals.m[metal::min(unsigned(i_5), 2u)][metal::min(unsigned(j), 3u)];\n    return _e6;\n}\n\nint naga_f2i32(float value) {\n    return static_cast<int>(metal::clamp(value, -2147483600.0, 2147483500.0));\n}\n\nfloat index_expensive(\n    int i_6,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e11 = globals.a.inner[metal::min(unsigned(naga_f2i32(metal::sin(static_cast<float>(i_6) / 100.0) * 100.0)), 9u)];\n    return _e11;\n}\n\nfloat index_in_bounds(\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e3 = globals.a.inner[9];\n    float _e7 = globals.v.w;\n    float _e13 = globals.m[2].w;\n    return (_e3 + _e7) + _e13;\n}\n\nvoid set_array(\n    int i_7,\n    float v_1,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.a.inner[metal::min(unsigned(i_7), 9u)] = v_1;\n    return;\n}\n\nvoid set_dynamic_array(\n    int i_8,\n    float v_2,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.d[metal::min(unsigned(i_8), (_buffer_sizes.size0 - 112 - 4) / 4)] = v_2;\n    return;\n}\n\nvoid set_vector(\n    int i_9,\n    float v_3,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.v[metal::min(unsigned(i_9), 3u)] = v_3;\n    return;\n}\n\nvoid set_matrix(\n    int i_10,\n    metal::float4 v_4,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.m[metal::min(unsigned(i_10), 2u)] = v_4;\n    return;\n}\n\nvoid set_index_twice(\n    int i_11,\n    int j_1,\n    float v_5,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.m[metal::min(unsigned(i_11), 2u)][metal::min(unsigned(j_1), 3u)] = v_5;\n    return;\n}\n\nvoid set_expensive(\n    int i_12,\n    float v_6,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.a.inner[metal::min(unsigned(naga_f2i32(metal::sin(static_cast<float>(i_12) / 100.0) * 100.0)), 9u)] = v_6;\n    return;\n}\n\nvoid set_in_bounds(\n    float v_7,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.a.inner[9] = v_7;\n    globals.v.w = v_7;\n    globals.m[2].w = v_7;\n    return;\n}\n\nfloat index_dynamic_array_constant_index(\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e3 = globals.d[metal::min(unsigned(1000), (_buffer_sizes.size0 - 112 - 4) / 4)];\n    return _e3;\n}\n\nvoid set_dynamic_array_constant_index(\n    float v_8,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.d[metal::min(unsigned(1000), (_buffer_sizes.size0 - 112 - 4) / 4)] = v_8;\n    return;\n}\n\nkernel void main_(\n  device Globals& globals [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    float _e1 = index_array(1, globals, _buffer_sizes);\n    float _e3 = index_dynamic_array(1, globals, _buffer_sizes);\n    float _e5 = index_vector(1, globals, _buffer_sizes);\n    float _e12 = index_vector_by_value(metal::float4(2.0, 3.0, 4.0, 5.0), 6);\n    metal::float4 _e14 = index_matrix(1, globals, _buffer_sizes);\n    float _e17 = index_twice(1, 2, globals, _buffer_sizes);\n    float _e19 = index_expensive(1, globals, _buffer_sizes);\n    float _e20 = index_in_bounds(globals, _buffer_sizes);\n    set_array(1, 2.0, globals, _buffer_sizes);\n    set_dynamic_array(1, 2.0, globals, _buffer_sizes);\n    set_vector(1, 2.0, globals, _buffer_sizes);\n    set_matrix(1, metal::float4(2.0, 3.0, 4.0, 5.0), globals, _buffer_sizes);\n    set_index_twice(1, 2, 1.0, globals, _buffer_sizes);\n    set_expensive(1, 1.0, globals, _buffer_sizes);\n    set_in_bounds(1.0, globals, _buffer_sizes);\n    float _e39 = index_dynamic_array_constant_index(globals, _buffer_sizes);\n    set_dynamic_array_constant_index(1.0, globals, _buffer_sizes);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-zero-atomic.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\nstruct _mslBufferSizes {\n    uint size0;\n};\n\nstruct type_2 {\n    metal::atomic_uint inner[10];\n};\ntypedef metal::atomic_uint type_3[1];\nstruct Globals {\n    metal::atomic_uint a;\n    type_2 b;\n    type_3 c;\n};\n\nuint fetch_add_atomic(\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e3 = metal::atomic_fetch_add_explicit(&globals.a, 1u, metal::memory_order_relaxed);\n    return _e3;\n}\n\nuint fetch_add_atomic_static_sized_array(\n    int i,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e5 = uint(i) < 10 ? metal::atomic_fetch_add_explicit(&globals.b.inner[i], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e5;\n}\n\nuint fetch_add_atomic_dynamic_sized_array(\n    int i_1,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e5 = uint(i_1) < 1 + (_buffer_sizes.size0 - 44 - 4) / 4 ? metal::atomic_fetch_add_explicit(&globals.c[i_1], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e5;\n}\n\nuint exchange_atomic(\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e3 = metal::atomic_exchange_explicit(&globals.a, 1u, metal::memory_order_relaxed);\n    return _e3;\n}\n\nuint exchange_atomic_static_sized_array(\n    int i_2,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e5 = uint(i_2) < 10 ? metal::atomic_exchange_explicit(&globals.b.inner[i_2], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e5;\n}\n\nuint exchange_atomic_dynamic_sized_array(\n    int i_3,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e5 = uint(i_3) < 1 + (_buffer_sizes.size0 - 44 - 4) / 4 ? metal::atomic_exchange_explicit(&globals.c[i_3], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e5;\n}\n\nuint fetch_add_atomic_dynamic_sized_array_static_index(\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e4 = uint(1000) < 1 + (_buffer_sizes.size0 - 44 - 4) / 4 ? metal::atomic_fetch_add_explicit(&globals.c[1000], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e4;\n}\n\nuint exchange_atomic_dynamic_sized_array_static_index(\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    uint _e4 = uint(1000) < 1 + (_buffer_sizes.size0 - 44 - 4) / 4 ? metal::atomic_exchange_explicit(&globals.c[1000], 1u, metal::memory_order_relaxed) : DefaultConstructible();\n    return _e4;\n}\n\nkernel void main_(\n  device Globals& globals [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    uint _e0 = fetch_add_atomic(globals, _buffer_sizes);\n    uint _e2 = fetch_add_atomic_static_sized_array(1, globals, _buffer_sizes);\n    uint _e4 = fetch_add_atomic_dynamic_sized_array(1, globals, _buffer_sizes);\n    uint _e5 = exchange_atomic(globals, _buffer_sizes);\n    uint _e7 = exchange_atomic_static_sized_array(1, globals, _buffer_sizes);\n    uint _e9 = exchange_atomic_dynamic_sized_array(1, globals, _buffer_sizes);\n    uint _e10 = fetch_add_atomic_dynamic_sized_array_static_index(globals, _buffer_sizes);\n    uint _e11 = exchange_atomic_dynamic_sized_array_static_index(globals, _buffer_sizes);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-bounds-check-zero.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\nstruct _mslBufferSizes {\n    uint size0;\n};\n\nstruct type_1 {\n    float inner[10];\n};\ntypedef float type_4[1];\nstruct Globals {\n    type_1 a;\n    char _pad1[8];\n    metal::float4 v;\n    metal::float3x4 m;\n    type_4 d;\n    char _pad4[12];\n};\n\nfloat index_array(\n    int i,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = uint(i) < 10 ? globals.a.inner[i] : DefaultConstructible();\n    return _e4;\n}\n\nfloat index_dynamic_array(\n    int i_1,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = uint(i_1) < 1 + (_buffer_sizes.size0 - 112 - 4) / 4 ? globals.d[i_1] : DefaultConstructible();\n    return _e4;\n}\n\nfloat index_vector(\n    int i_2,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e4 = uint(i_2) < 4 ? globals.v[i_2] : DefaultConstructible();\n    return _e4;\n}\n\nfloat index_vector_by_value(\n    metal::float4 v,\n    int i_3\n) {\n    return uint(i_3) < 4 ? v[i_3] : DefaultConstructible();\n}\n\nmetal::float4 index_matrix(\n    int i_4,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    metal::float4 _e4 = uint(i_4) < 3 ? globals.m[i_4] : DefaultConstructible();\n    return _e4;\n}\n\nfloat index_twice(\n    int i_5,\n    int j,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e6 = uint(j) < 4 && uint(i_5) < 3 ? globals.m[i_5][j] : DefaultConstructible();\n    return _e6;\n}\n\nint naga_f2i32(float value) {\n    return static_cast<int>(metal::clamp(value, -2147483600.0, 2147483500.0));\n}\n\nfloat index_expensive(\n    int i_6,\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    int _e9 = naga_f2i32(metal::sin(static_cast<float>(i_6) / 100.0) * 100.0);\n    float _e11 = uint(_e9) < 10 ? globals.a.inner[_e9] : DefaultConstructible();\n    return _e11;\n}\n\nfloat index_in_bounds(\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e3 = globals.a.inner[9];\n    float _e7 = globals.v.w;\n    float _e13 = globals.m[2].w;\n    return (_e3 + _e7) + _e13;\n}\n\nvoid set_array(\n    int i_7,\n    float v_1,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(i_7) < 10) {\n        globals.a.inner[i_7] = v_1;\n    }\n    return;\n}\n\nvoid set_dynamic_array(\n    int i_8,\n    float v_2,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(i_8) < 1 + (_buffer_sizes.size0 - 112 - 4) / 4) {\n        globals.d[i_8] = v_2;\n    }\n    return;\n}\n\nvoid set_vector(\n    int i_9,\n    float v_3,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(i_9) < 4) {\n        globals.v[i_9] = v_3;\n    }\n    return;\n}\n\nvoid set_matrix(\n    int i_10,\n    metal::float4 v_4,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(i_10) < 3) {\n        globals.m[i_10] = v_4;\n    }\n    return;\n}\n\nvoid set_index_twice(\n    int i_11,\n    int j_1,\n    float v_5,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(j_1) < 4 && uint(i_11) < 3) {\n        globals.m[i_11][j_1] = v_5;\n    }\n    return;\n}\n\nvoid set_expensive(\n    int i_12,\n    float v_6,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    int _e10 = naga_f2i32(metal::sin(static_cast<float>(i_12) / 100.0) * 100.0);\n    if (uint(_e10) < 10) {\n        globals.a.inner[_e10] = v_6;\n    }\n    return;\n}\n\nvoid set_in_bounds(\n    float v_7,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    globals.a.inner[9] = v_7;\n    globals.v.w = v_7;\n    globals.m[2].w = v_7;\n    return;\n}\n\nfloat index_dynamic_array_constant_index(\n    device Globals const& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    float _e3 = uint(1000) < 1 + (_buffer_sizes.size0 - 112 - 4) / 4 ? globals.d[1000] : DefaultConstructible();\n    return _e3;\n}\n\nvoid set_dynamic_array_constant_index(\n    float v_8,\n    device Globals& globals,\n    constant _mslBufferSizes& _buffer_sizes\n) {\n    if (uint(1000) < 1 + (_buffer_sizes.size0 - 112 - 4) / 4) {\n        globals.d[1000] = v_8;\n    }\n    return;\n}\n\nkernel void main_(\n  device Globals& globals [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    float _e1 = index_array(1, globals, _buffer_sizes);\n    float _e3 = index_dynamic_array(1, globals, _buffer_sizes);\n    float _e5 = index_vector(1, globals, _buffer_sizes);\n    float _e12 = index_vector_by_value(metal::float4(2.0, 3.0, 4.0, 5.0), 6);\n    metal::float4 _e14 = index_matrix(1, globals, _buffer_sizes);\n    float _e17 = index_twice(1, 2, globals, _buffer_sizes);\n    float _e19 = index_expensive(1, globals, _buffer_sizes);\n    float _e20 = index_in_bounds(globals, _buffer_sizes);\n    set_array(1, 2.0, globals, _buffer_sizes);\n    set_dynamic_array(1, 2.0, globals, _buffer_sizes);\n    set_vector(1, 2.0, globals, _buffer_sizes);\n    set_matrix(1, metal::float4(2.0, 3.0, 4.0, 5.0), globals, _buffer_sizes);\n    set_index_twice(1, 2, 1.0, globals, _buffer_sizes);\n    set_expensive(1, 1.0, globals, _buffer_sizes);\n    set_in_bounds(1.0, globals, _buffer_sizes);\n    float _e39 = index_dynamic_array_constant_index(globals, _buffer_sizes);\n    set_dynamic_array_constant_index(1.0, globals, _buffer_sizes);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-break-if.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid breakIfEmpty(\n) {\n    uint2 loop_bound = uint2(4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            if (true) {\n                break;\n            }\n        }\n        loop_init = false;\n    }\n    return;\n}\n\nvoid breakIfEmptyBody(\n    bool a\n) {\n    bool b = {};\n    bool c = {};\n    uint2 loop_bound_1 = uint2(4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (metal::all(loop_bound_1 == uint2(0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        if (!loop_init_1) {\n            b = a;\n            bool _e2 = b;\n            c = a != _e2;\n            bool _e5 = c;\n            if (a == c) {\n                break;\n            }\n        }\n        loop_init_1 = false;\n    }\n    return;\n}\n\nvoid breakIf(\n    bool a_1\n) {\n    bool d = {};\n    bool e = {};\n    uint2 loop_bound_2 = uint2(4294967295u);\n    bool loop_init_2 = true;\n    while(true) {\n        if (metal::all(loop_bound_2 == uint2(0u))) { break; }\n        loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n        if (!loop_init_2) {\n            bool _e5 = e;\n            if (a_1 == e) {\n                break;\n            }\n        }\n        loop_init_2 = false;\n        d = a_1;\n        bool _e2 = d;\n        e = a_1 != _e2;\n    }\n    return;\n}\n\nvoid breakIfSeparateVariable(\n) {\n    uint counter = 0u;\n    uint2 loop_bound_3 = uint2(4294967295u);\n    bool loop_init_3 = true;\n    while(true) {\n        if (metal::all(loop_bound_3 == uint2(0u))) { break; }\n        loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n        if (!loop_init_3) {\n            uint _e5 = counter;\n            if (counter == 5u) {\n                break;\n            }\n        }\n        loop_init_3 = false;\n        uint _e2 = counter;\n        counter = _e2 + 1u;\n    }\n    return;\n}\n\nkernel void main_(\n) {\n    breakIfEmpty();\n    breakIfEmptyBody(false);\n    breakIf(false);\n    breakIfSeparateVariable();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-collatz.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size0;\n};\n\ntypedef uint type_1[1];\nstruct PrimeIndices {\n    type_1 data;\n};\n\nuint naga_mod(uint lhs, uint rhs) {\n    return lhs % metal::select(rhs, 1u, rhs == 0u);\n}\n\nuint naga_div(uint lhs, uint rhs) {\n    return lhs / metal::select(rhs, 1u, rhs == 0u);\n}\n\nuint collatz_iterations(\n    uint n_base\n) {\n    uint n = {};\n    uint i = 0u;\n    n = n_base;\n    uint2 loop_bound = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        uint _e4 = n;\n        if (_e4 > 1u) {\n        } else {\n            break;\n        }\n        {\n            uint _e7 = n;\n            if (naga_mod(_e7, 2u) == 0u) {\n                uint _e12 = n;\n                n = naga_div(_e12, 2u);\n            } else {\n                uint _e16 = n;\n                n = (3u * _e16) + 1u;\n            }\n            uint _e20 = i;\n            i = _e20 + 1u;\n        }\n    }\n    uint _e23 = i;\n    return _e23;\n}\n\nstruct main_Input {\n};\nkernel void main_(\n  metal::uint3 global_id [[thread_position_in_grid]]\n, device PrimeIndices& v_indices [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    uint _e9 = v_indices.data[global_id.x];\n    uint _e10 = collatz_iterations(_e9);\n    v_indices.data[global_id.x] = _e10;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-const-exprs.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_6 {\n    float inner[2];\n};\nstruct type_9 {\n    int inner[9];\n};\nconstant uint TWO = 2u;\nconstant int THREE = 3;\nconstant bool TRUE = true;\nconstant bool FALSE = false;\nconstant int FOUR = 4;\nconstant int TEXTURE_KIND_REGULAR = 0;\nconstant int TEXTURE_KIND_WARP = 1;\nconstant int TEXTURE_KIND_SKY = 2;\nconstant int FOUR_ALIAS = 4;\nconstant int TEST_CONSTANT_ADDITION = 8;\nconstant int TEST_CONSTANT_ALIAS_ADDITION = 8;\nconstant float PI = 3.141;\nconstant float phi_sun = 6.282;\nconstant metal::float4 DIV = metal::float4(0.44444445, 0.0, 0.0, 0.0);\nconstant metal::float2 add_vec = metal::float2(4.0, 5.0);\nconstant metal::bool2 compare_vec = metal::bool2(true, false);\n\nvoid swizzle_of_compose(\n) {\n    metal::int4 out = metal::int4(4, 3, 2, 1);\n    return;\n}\n\nvoid index_of_compose(\n) {\n    int out_1 = 2;\n    return;\n}\n\nvoid compose_three_deep(\n) {\n    int out_2 = 6;\n    return;\n}\n\nvoid non_constant_initializers(\n) {\n    int w = 30;\n    int x = {};\n    int y = {};\n    int z = 70;\n    metal::int4 out_3 = {};\n    int _e2 = w;\n    x = _e2;\n    int _e4 = x;\n    y = _e4;\n    int _e8 = w;\n    int _e9 = x;\n    int _e10 = y;\n    int _e11 = z;\n    out_3 = metal::int4(_e8, _e9, _e10, _e11);\n    return;\n}\n\nvoid splat_of_constant(\n) {\n    metal::int4 out_4 = metal::int4(-4, -4, -4, -4);\n    return;\n}\n\nvoid compose_of_constant(\n) {\n    metal::int4 out_5 = metal::int4(-4, -4, -4, -4);\n    return;\n}\n\nuint map_texture_kind(\n    int texture_kind\n) {\n    switch(texture_kind) {\n        case 0: {\n            return 10u;\n        }\n        case 1: {\n            return 20u;\n        }\n        case 2: {\n            return 30u;\n        }\n        default: {\n            return 0u;\n        }\n    }\n}\n\nvoid compose_of_splat(\n) {\n    metal::float4 x_1 = metal::float4(2.0, 1.0, 1.0, 1.0);\n    return;\n}\n\nvoid test_local_const(\n) {\n    type_6 arr = {};\n    return;\n}\n\nvoid compose_vector_zero_val_binop(\n) {\n    metal::int3 a = metal::int3(1, 1, 1);\n    metal::int3 b = metal::int3(0, 1, 2);\n    metal::int3 c = metal::int3(1, 0, 2);\n    return;\n}\n\nvoid relational(\n) {\n    bool scalar_any_false = false;\n    bool scalar_any_true = true;\n    bool scalar_all_false = false;\n    bool scalar_all_true = true;\n    bool vec_any_false = false;\n    bool vec_any_true = true;\n    bool vec_all_false = false;\n    bool vec_all_true = true;\n    return;\n}\n\nvoid packed_dot_product(\n) {\n    int signed_four = 4;\n    uint unsigned_four = 4u;\n    int signed_twelve = 12;\n    uint unsigned_twelve = 12u;\n    int signed_seventy = 70;\n    uint unsigned_seventy = 70u;\n    int minus_four = -4;\n    return;\n}\n\nvoid abstract_access(\n    uint i\n) {\n    float a_1 = 1.0;\n    uint b_1 = 1u;\n    int c_1 = {};\n    int d = {};\n    c_1 = type_9 {{1, 2, 3, 4, 5, 6, 7, 8, 9}}.inner[i];\n    d = metal::int4(1, 2, 3, 4)[i];\n    return;\n}\n\nkernel void main_(\n) {\n    swizzle_of_compose();\n    index_of_compose();\n    compose_three_deep();\n    non_constant_initializers();\n    splat_of_constant();\n    compose_of_constant();\n    uint _e1 = map_texture_kind(1);\n    compose_of_splat();\n    test_local_const();\n    compose_vector_zero_val_binop();\n    relational();\n    packed_dot_product();\n    test_local_const();\n    abstract_access(1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-constructors.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct Foo {\n    metal::float4 a;\n    int b;\n    char _pad2[12];\n};\nstruct type_6 {\n    metal::float2x2 inner[1];\n};\nstruct type_10 {\n    Foo inner[3];\n};\nstruct type_12 {\n    int inner[4];\n};\nconstant metal::float3 const1_ = metal::float3(0.0);\nconstant metal::float2x2 const3_ = metal::float2x2(metal::float2(0.0, 1.0), metal::float2(2.0, 3.0));\nconstant type_6 const4_ = type_6 {{metal::float2x2(metal::float2(0.0, 1.0), metal::float2(2.0, 3.0))}};\nconstant bool cz0_ = bool {};\nconstant int cz1_ = int {};\nconstant uint cz2_ = uint {};\nconstant float cz3_ = float {};\nconstant metal::uint2 cz4_ = metal::uint2 {};\nconstant metal::float2x2 cz5_ = metal::float2x2 {};\nconstant type_10 cz6_ = type_10 {};\nconstant Foo cz7_ = Foo {};\nconstant metal::uint2 cp1_ = metal::uint2(0u);\n\nkernel void main_(\n) {\n    Foo foo = {};\n    foo = Foo {metal::float4(1.0), 1};\n    metal::float2x2 m0_ = metal::float2x2(metal::float2(1.0, 0.0), metal::float2(0.0, 1.0));\n    metal::float4x4 m1_ = metal::float4x4(metal::float4(1.0, 0.0, 0.0, 0.0), metal::float4(0.0, 1.0, 0.0, 0.0), metal::float4(0.0, 0.0, 1.0, 0.0), metal::float4(0.0, 0.0, 0.0, 1.0));\n    metal::uint2 zvc8_ = metal::uint2(0u, 0u);\n    metal::float2 zvc9_ = metal::float2(0.0, 0.0);\n    metal::uint2 cit0_ = metal::uint2(0u);\n    metal::float2x2 cit1_ = metal::float2x2(metal::float2(0.0), metal::float2(0.0));\n    type_12 cit2_ = type_12 {{0, 1, 2, 3}};\n    metal::uint2 ic4_ = metal::uint2(0u, 0u);\n    metal::float2x3 ic5_ = metal::float2x3(metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-control-flow.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nvoid control_flow(\n) {\n    int pos = {};\n    metal::threadgroup_barrier(metal::mem_flags::mem_device);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_texture);\n    switch(1) {\n        default: {\n            pos = 1;\n            break;\n        }\n    }\n    int _e3 = pos;\n    switch(_e3) {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n            break;\n        }\n        case 3:\n        case 4: {\n            pos = 2;\n            break;\n        }\n        case 5: {\n            pos = 3;\n            break;\n        }\n        default:\n        case 6: {\n            pos = 4;\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    int _e10 = pos;\n    switch(_e10) {\n        case 1: {\n            pos = 0;\n            break;\n        }\n        case 2: {\n            pos = 1;\n            break;\n        }\n        case 3: {\n            pos = 2;\n            break;\n        }\n        case 4: {\n            break;\n        }\n        default: {\n            pos = 3;\n            break;\n        }\n    }\n    int _e15 = pos;\n    switch(_e15) {\n        case 1: {\n            pos = 0;\n            return;\n        }\n        case 2: {\n            pos = 1;\n            return;\n        }\n        case 3:\n        case 4: {\n            pos = 2;\n            return;\n        }\n        case 5:\n        case 6: {\n            pos = 3;\n            return;\n        }\n        default: {\n            pos = 4;\n            return;\n        }\n    }\n}\n\nvoid switch_default_break(\n    int i\n) {\n    switch(i) {\n        default: {\n            break;\n        }\n    }\n}\n\nvoid switch_case_break(\n) {\n    switch(0) {\n        case 0: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    return;\n}\n\nvoid switch_selector_type_conversion(\n) {\n    switch(0u) {\n        case 0u: {\n            break;\n        }\n        default: {\n            break;\n        }\n    }\n    switch(0u) {\n        case 0u: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid switch_const_expr_case_selectors(\n) {\n    switch(0) {\n        case 0: {\n            return;\n        }\n        case 1: {\n            return;\n        }\n        case 2: {\n            return;\n        }\n        case 3: {\n            return;\n        }\n        case 4: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nvoid loop_switch_continue(\n    int x\n) {\n    uint2 loop_bound = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        switch(x) {\n            case 1: {\n                continue;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    return;\n}\n\nvoid loop_switch_continue_nesting(\n    int x_1,\n    int y,\n    int z\n) {\n    uint2 loop_bound_1 = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound_1 == uint2(0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        switch(x_1) {\n            case 1: {\n                continue;\n            }\n            case 2: {\n                switch(y) {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        uint2 loop_bound_2 = uint2(4294967295u);\n                        while(true) {\n                            if (metal::all(loop_bound_2 == uint2(0u))) { break; }\n                            loop_bound_2 -= uint2(loop_bound_2.y == 0u, 1u);\n                            switch(z) {\n                                case 1: {\n                                    continue;\n                                }\n                                default: {\n                                    break;\n                                }\n                            }\n                        }\n                        break;\n                    }\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n        switch(y) {\n            default: {\n                continue;\n            }\n        }\n    }\n    uint2 loop_bound_3 = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound_3 == uint2(0u))) { break; }\n        loop_bound_3 -= uint2(loop_bound_3.y == 0u, 1u);\n        switch(y) {\n            case 1:\n            default: {\n                switch(z) {\n                    default: {\n                        continue;\n                    }\n                }\n                break;\n            }\n        }\n    }\n    return;\n}\n\nvoid loop_switch_omit_continue_variable_checks(\n    int x_2,\n    int y_1,\n    int z_1,\n    int w\n) {\n    int pos_1 = 0;\n    uint2 loop_bound_4 = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound_4 == uint2(0u))) { break; }\n        loop_bound_4 -= uint2(loop_bound_4.y == 0u, 1u);\n        switch(x_2) {\n            case 1: {\n                pos_1 = 1;\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    uint2 loop_bound_5 = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound_5 == uint2(0u))) { break; }\n        loop_bound_5 -= uint2(loop_bound_5.y == 0u, 1u);\n        switch(x_2) {\n            case 1: {\n                break;\n            }\n            case 2: {\n                switch(y_1) {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        switch(z_1) {\n                            case 1: {\n                                pos_1 = 2;\n                                break;\n                            }\n                            default: {\n                                break;\n                            }\n                        }\n                        break;\n                    }\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n    return;\n}\n\nkernel void main_(\n) {\n    control_flow();\n    switch_default_break(1);\n    switch_case_break();\n    switch_selector_type_conversion();\n    switch_const_expr_case_selectors();\n    loop_switch_continue(1);\n    loop_switch_continue_nesting(1, 2, 3);\n    loop_switch_omit_continue_variable_checks(1, 2, 3, 4);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-conversion-float-to-int-no-f64.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nconstant half MIN_F16_ = -65504.0h;\nconstant half MAX_F16_ = 65504.0h;\nconstant float MIN_F32_ = -340282350000000000000000000000000000000.0;\nconstant float MAX_F32_ = 340282350000000000000000000000000000000.0;\n\nvoid test_const_eval(\n) {\n    int min_f16_to_i32_ = -65504;\n    int max_f16_to_i32_ = 65504;\n    uint min_f16_to_u32_ = 0u;\n    uint max_f16_to_u32_ = 65504u;\n    long min_f16_to_i64_ = -65504L;\n    long max_f16_to_i64_ = 65504L;\n    ulong min_f16_to_u64_ = 0uL;\n    ulong max_f16_to_u64_ = 65504uL;\n    int min_f32_to_i32_ = (-2147483647 - 1);\n    int max_f32_to_i32_ = 2147483520;\n    uint min_f32_to_u32_ = 0u;\n    uint max_f32_to_u32_ = 4294967040u;\n    long min_f32_to_i64_ = (-9223372036854775807L - 1L);\n    long max_f32_to_i64_ = 9223371487098961920L;\n    ulong min_f32_to_u64_ = 0uL;\n    ulong max_f32_to_u64_ = 18446742974197923840uL;\n    int min_abstract_float_to_i32_ = (-2147483647 - 1);\n    int max_abstract_float_to_i32_ = 2147483647;\n    uint min_abstract_float_to_u32_ = 0u;\n    uint max_abstract_float_to_u32_ = 4294967295u;\n    long min_abstract_float_to_i64_ = (-9223372036854775807L - 1L);\n    long max_abstract_float_to_i64_ = 9223372036854774784L;\n    ulong min_abstract_float_to_u64_ = 0uL;\n    ulong max_abstract_float_to_u64_ = 18446744073709549568uL;\n    return;\n}\n\nint naga_f2i32(half value) {\n    return static_cast<int>(metal::clamp(value, -65504.0h, 65504.0h));\n}\n\nint test_f16_to_i32_(\n    half f\n) {\n    return naga_f2i32(f);\n}\n\nuint naga_f2u32(half value) {\n    return static_cast<uint>(metal::clamp(value, 0.0h, 65504.0h));\n}\n\nuint test_f16_to_u32_(\n    half f_1\n) {\n    return naga_f2u32(f_1);\n}\n\nlong naga_f2i64(half value) {\n    return static_cast<long>(metal::clamp(value, -65504.0h, 65504.0h));\n}\n\nlong test_f16_to_i64_(\n    half f_2\n) {\n    return naga_f2i64(f_2);\n}\n\nulong naga_f2u64(half value) {\n    return static_cast<ulong>(metal::clamp(value, 0.0h, 65504.0h));\n}\n\nulong test_f16_to_u64_(\n    half f_3\n) {\n    return naga_f2u64(f_3);\n}\n\nint naga_f2i32(float value) {\n    return static_cast<int>(metal::clamp(value, -2147483600.0, 2147483500.0));\n}\n\nint test_f32_to_i32_(\n    float f_4\n) {\n    return naga_f2i32(f_4);\n}\n\nuint naga_f2u32(float value) {\n    return static_cast<uint>(metal::clamp(value, 0.0, 4294967000.0));\n}\n\nuint test_f32_to_u32_(\n    float f_5\n) {\n    return naga_f2u32(f_5);\n}\n\nlong naga_f2i64(float value) {\n    return static_cast<long>(metal::clamp(value, -9223372000000000000.0, 9223371500000000000.0));\n}\n\nlong test_f32_to_i64_(\n    float f_6\n) {\n    return naga_f2i64(f_6);\n}\n\nulong naga_f2u64(float value) {\n    return static_cast<ulong>(metal::clamp(value, 0.0, 18446743000000000000.0));\n}\n\nulong test_f32_to_u64_(\n    float f_7\n) {\n    return naga_f2u64(f_7);\n}\n\nmetal::int2 naga_f2i32(metal::half2 value) {\n    return static_cast<metal::int2>(metal::clamp(value, -65504.0h, 65504.0h));\n}\n\nmetal::int2 test_f16_to_i32_vec(\n    metal::half2 f_8\n) {\n    return naga_f2i32(f_8);\n}\n\nmetal::uint2 naga_f2u32(metal::half2 value) {\n    return static_cast<metal::uint2>(metal::clamp(value, 0.0h, 65504.0h));\n}\n\nmetal::uint2 test_f16_to_u32_vec(\n    metal::half2 f_9\n) {\n    return naga_f2u32(f_9);\n}\n\nmetal::long2 naga_f2i64(metal::half2 value) {\n    return static_cast<metal::long2>(metal::clamp(value, -65504.0h, 65504.0h));\n}\n\nmetal::long2 test_f16_to_i64_vec(\n    metal::half2 f_10\n) {\n    return naga_f2i64(f_10);\n}\n\nmetal::ulong2 naga_f2u64(metal::half2 value) {\n    return static_cast<metal::ulong2>(metal::clamp(value, 0.0h, 65504.0h));\n}\n\nmetal::ulong2 test_f16_to_u64_vec(\n    metal::half2 f_11\n) {\n    return naga_f2u64(f_11);\n}\n\nmetal::int2 naga_f2i32(metal::float2 value) {\n    return static_cast<metal::int2>(metal::clamp(value, -2147483600.0, 2147483500.0));\n}\n\nmetal::int2 test_f32_to_i32_vec(\n    metal::float2 f_12\n) {\n    return naga_f2i32(f_12);\n}\n\nmetal::uint2 naga_f2u32(metal::float2 value) {\n    return static_cast<metal::uint2>(metal::clamp(value, 0.0, 4294967000.0));\n}\n\nmetal::uint2 test_f32_to_u32_vec(\n    metal::float2 f_13\n) {\n    return naga_f2u32(f_13);\n}\n\nmetal::long2 naga_f2i64(metal::float2 value) {\n    return static_cast<metal::long2>(metal::clamp(value, -9223372000000000000.0, 9223371500000000000.0));\n}\n\nmetal::long2 test_f32_to_i64_vec(\n    metal::float2 f_14\n) {\n    return naga_f2i64(f_14);\n}\n\nmetal::ulong2 naga_f2u64(metal::float2 value) {\n    return static_cast<metal::ulong2>(metal::clamp(value, 0.0, 18446743000000000000.0));\n}\n\nmetal::ulong2 test_f32_to_u64_vec(\n    metal::float2 f_15\n) {\n    return naga_f2u64(f_15);\n}\n\nkernel void main_(\n) {\n    test_const_eval();\n    int _e1 = test_f16_to_i32_(1.0h);\n    uint _e3 = test_f16_to_u32_(1.0h);\n    long _e5 = test_f16_to_i64_(1.0h);\n    ulong _e7 = test_f16_to_u64_(1.0h);\n    int _e9 = test_f32_to_i32_(1.0);\n    uint _e11 = test_f32_to_u32_(1.0);\n    long _e13 = test_f32_to_i64_(1.0);\n    ulong _e15 = test_f32_to_u64_(1.0);\n    metal::int2 _e19 = test_f16_to_i32_vec(metal::half2(1.0h, 2.0h));\n    metal::uint2 _e23 = test_f16_to_u32_vec(metal::half2(1.0h, 2.0h));\n    metal::long2 _e27 = test_f16_to_i64_vec(metal::half2(1.0h, 2.0h));\n    metal::ulong2 _e31 = test_f16_to_u64_vec(metal::half2(1.0h, 2.0h));\n    metal::int2 _e35 = test_f32_to_i32_vec(metal::float2(1.0, 2.0));\n    metal::uint2 _e39 = test_f32_to_u32_vec(metal::float2(1.0, 2.0));\n    metal::long2 _e43 = test_f32_to_i64_vec(metal::float2(1.0, 2.0));\n    metal::ulong2 _e47 = test_f32_to_u64_vec(metal::float2(1.0, 2.0));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-conversions.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nconstant metal::float2 ic0_ = metal::float2(2.0, 2.0);\n\nkernel void main_(\n) {\n    metal::float2 vc0_ = metal::float2(2.0, 2.0);\n    metal::float2 lc0_ = metal::float2(2.0, 2.0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-cooperative-matrix.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size2;\n};\n\ntypedef float type_3[1];\nmetal::simdgroup_float8x8 NagaCooperativeLoad(const device float* ptr, int stride, bool is_row_major) {\n    metal::simdgroup_float8x8 m;\n    simdgroup_load(m, ptr, stride, 0, is_row_major);\n    return m;\n}\n\nmetal::simdgroup_float8x8 NagaCooperativeMultiplyAdd(const thread metal::simdgroup_float8x8& a, const thread metal::simdgroup_float8x8& b, const thread metal::simdgroup_float8x8& c) {\n    metal::simdgroup_float8x8 d;\n    simdgroup_multiply_accumulate(d,a,b,c);\n    return d;\n}\n\n\nkernel void main_(\n  device type_3& ext [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    metal::simdgroup_float8x8 a = {};\n    metal::simdgroup_float8x8 b = {};\n    metal::simdgroup_float8x8 c = {};\n    metal::simdgroup_float8x8 d = {};\n    c = NagaCooperativeLoad(&ext[4], 8u, false);\n    metal::simdgroup_float8x8 _e6 = a;\n    metal::simdgroup_float8x8 _e8 = b;\n    metal::simdgroup_float8x8 _e9 = c;\n    d = NagaCooperativeMultiplyAdd(_e6, _e8, _e9);\n    metal::simdgroup_float8x8 _e12 = d;\n    simdgroup_store(_e12, &ext[0], 8u);\n    metal::simdgroup_float8x8 _e16 = d;\n    c = _e16;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-cross.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    metal::float3 a = metal::float3(0.0, 0.0, 1.0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-dualsource.metal",
    "content": "// language: metal1.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct FragmentOutput {\n    metal::float4 output0_;\n    metal::float4 output1_;\n};\n\nstruct main_Output {\n    metal::float4 output0_ [[color(0) index(0)]];\n    metal::float4 output1_ [[color(0) index(1)]];\n};\nfragment main_Output main_(\n) {\n    const auto _tmp = FragmentOutput {metal::float4(0.4, 0.3, 0.2, 0.1), metal::float4(0.9, 0.8, 0.7, 0.6)};\n    return main_Output { _tmp.output0_, _tmp.output1_ };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-empty-if.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct compInput {\n};\nkernel void comp(\n  metal::uint3 id [[thread_position_in_grid]]\n) {\n    if (id.x == 0u) {\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-empty.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-extra.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct ImmediateData {\n    uint index;\n    char _pad1[4];\n    metal::float2 double_;\n};\nstruct FragmentIn {\n    metal::float4 color;\n    uint primitive_index;\n    char _pad2[12];\n};\n\nstruct main_Input {\n    metal::float4 color [[user(loc0), center_perspective]];\n};\nstruct main_Output {\n    metal::float4 member [[color(0)]];\n};\nfragment main_Output main_(\n  main_Input varyings [[stage_in]]\n, uint primitive_index [[primitive_id]]\n, constant ImmediateData& im [[buffer(1)]]\n) {\n    const FragmentIn in = { varyings.color, primitive_index };\n    uint _e4 = im.index;\n    if (in.primitive_index == _e4) {\n        return main_Output { in.color };\n    } else {\n        return main_Output { metal::float4(metal::float3(1.0) - in.color.xyz, in.color.w) };\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-f16.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct UniformCompatible {\n    uint val_u32_;\n    int val_i32_;\n    float val_f32_;\n    half val_f16_;\n    char _pad4[2];\n    metal::half2 val_f16_2_;\n    char _pad5[4];\n    metal::half3 val_f16_3_;\n    metal::half4 val_f16_4_;\n    half final_value;\n    char _pad8[2];\n    metal::half2x2 val_mat2x2_;\n    char _pad9[4];\n    metal::half2x3 val_mat2x3_;\n    metal::half2x4 val_mat2x4_;\n    metal::half3x2 val_mat3x2_;\n    char _pad12[4];\n    metal::half3x3 val_mat3x3_;\n    metal::half3x4 val_mat3x4_;\n    metal::half4x2 val_mat4x2_;\n    metal::half4x3 val_mat4x3_;\n    metal::half4x4 val_mat4x4_;\n};\nstruct type_16 {\n    half inner[2];\n};\nstruct StorageCompatible {\n    type_16 val_f16_array_2_;\n};\nstruct LayoutTest {\n    half scalar1_;\n    half scalar2_;\n    char _pad2[4];\n    metal::packed_half3 v3_;\n    half tuck_in;\n    half scalar4_;\n    char _pad5[2];\n    uint larger;\n};\nconstant half constant_variable = 15.203125h;\n\nhalf f16_function(\n    half x,\n    thread half& private_variable,\n    constant UniformCompatible& input_uniform,\n    device UniformCompatible const& input_storage,\n    device StorageCompatible const& input_arrays,\n    device UniformCompatible& output,\n    device StorageCompatible& output_arrays\n) {\n    LayoutTest l = {};\n    half val = 15.203125h;\n    half phony = private_variable;\n    half _e5 = val;\n    val = _e5 + -33344.0h;\n    half _e8 = val;\n    half _e9 = val;\n    val = _e8 + (_e9 + 5.0h);\n    half _e13 = val;\n    float _e16 = input_uniform.val_f32_;\n    half _e17 = val;\n    val = _e13 + static_cast<half>(_e16 + static_cast<float>(_e17));\n    half _e22 = val;\n    half _e25 = input_uniform.val_f16_;\n    val = _e22 + metal::half3(_e25).z;\n    output.val_i32_ = 65504;\n    output.val_i32_ = -65504;\n    output.val_u32_ = 65504u;\n    output.val_u32_ = 0u;\n    output.val_f32_ = 65504.0;\n    output.val_f32_ = -65504.0;\n    half _e51 = input_uniform.val_f16_;\n    half _e54 = input_storage.val_f16_;\n    output.val_f16_ = _e51 + _e54;\n    metal::half2 _e60 = input_uniform.val_f16_2_;\n    metal::half2 _e63 = input_storage.val_f16_2_;\n    output.val_f16_2_ = _e60 + _e63;\n    metal::half3 _e69 = input_uniform.val_f16_3_;\n    metal::half3 _e72 = input_storage.val_f16_3_;\n    output.val_f16_3_ = _e69 + _e72;\n    metal::half4 _e78 = input_uniform.val_f16_4_;\n    metal::half4 _e81 = input_storage.val_f16_4_;\n    output.val_f16_4_ = _e78 + _e81;\n    metal::half2x2 _e87 = input_uniform.val_mat2x2_;\n    metal::half2x2 _e90 = input_storage.val_mat2x2_;\n    output.val_mat2x2_ = _e87 + _e90;\n    metal::half2x3 _e96 = input_uniform.val_mat2x3_;\n    metal::half2x3 _e99 = input_storage.val_mat2x3_;\n    output.val_mat2x3_ = _e96 + _e99;\n    metal::half2x4 _e105 = input_uniform.val_mat2x4_;\n    metal::half2x4 _e108 = input_storage.val_mat2x4_;\n    output.val_mat2x4_ = _e105 + _e108;\n    metal::half3x2 _e114 = input_uniform.val_mat3x2_;\n    metal::half3x2 _e117 = input_storage.val_mat3x2_;\n    output.val_mat3x2_ = _e114 + _e117;\n    metal::half3x3 _e123 = input_uniform.val_mat3x3_;\n    metal::half3x3 _e126 = input_storage.val_mat3x3_;\n    output.val_mat3x3_ = _e123 + _e126;\n    metal::half3x4 _e132 = input_uniform.val_mat3x4_;\n    metal::half3x4 _e135 = input_storage.val_mat3x4_;\n    output.val_mat3x4_ = _e132 + _e135;\n    metal::half4x2 _e141 = input_uniform.val_mat4x2_;\n    metal::half4x2 _e144 = input_storage.val_mat4x2_;\n    output.val_mat4x2_ = _e141 + _e144;\n    metal::half4x3 _e150 = input_uniform.val_mat4x3_;\n    metal::half4x3 _e153 = input_storage.val_mat4x3_;\n    output.val_mat4x3_ = _e150 + _e153;\n    metal::half4x4 _e159 = input_uniform.val_mat4x4_;\n    metal::half4x4 _e162 = input_storage.val_mat4x4_;\n    output.val_mat4x4_ = _e159 + _e162;\n    type_16 _e168 = input_arrays.val_f16_array_2_;\n    output_arrays.val_f16_array_2_ = _e168;\n    half _e169 = val;\n    half _e170 = val;\n    val = _e169 + metal::abs(_e170);\n    half _e173 = val;\n    half _e174 = val;\n    half _e175 = val;\n    half _e176 = val;\n    val = _e173 + metal::clamp(_e174, _e175, _e176);\n    half _e179 = val;\n    half _e180 = val;\n    half _e182 = val;\n    val = _e179 + metal::dot(metal::half2(_e180), metal::half2(_e182));\n    half _e186 = val;\n    half _e187 = val;\n    half _e188 = val;\n    val = _e186 + metal::max(_e187, _e188);\n    half _e191 = val;\n    half _e192 = val;\n    half _e193 = val;\n    val = _e191 + metal::min(_e192, _e193);\n    half _e196 = val;\n    half _e197 = val;\n    val = _e196 + metal::sign(_e197);\n    half _e200 = val;\n    val = _e200 + 1.0h;\n    metal::half2 _e205 = input_uniform.val_f16_2_;\n    metal::float2 float_vec2_ = static_cast<metal::float2>(_e205);\n    output.val_f16_2_ = static_cast<metal::half2>(float_vec2_);\n    metal::half3 _e212 = input_uniform.val_f16_3_;\n    metal::float3 float_vec3_ = static_cast<metal::float3>(_e212);\n    output.val_f16_3_ = static_cast<metal::half3>(float_vec3_);\n    metal::half4 _e219 = input_uniform.val_f16_4_;\n    metal::float4 float_vec4_ = static_cast<metal::float4>(_e219);\n    output.val_f16_4_ = static_cast<metal::half4>(float_vec4_);\n    metal::half2x2 _e228 = input_uniform.val_mat2x2_;\n    output.val_mat2x2_ = metal::half2x2(metal::float2x2(_e228));\n    metal::half2x3 _e235 = input_uniform.val_mat2x3_;\n    output.val_mat2x3_ = metal::half2x3(metal::float2x3(_e235));\n    metal::half2x4 _e242 = input_uniform.val_mat2x4_;\n    output.val_mat2x4_ = metal::half2x4(metal::float2x4(_e242));\n    metal::half3x2 _e249 = input_uniform.val_mat3x2_;\n    output.val_mat3x2_ = metal::half3x2(metal::float3x2(_e249));\n    metal::half3x3 _e256 = input_uniform.val_mat3x3_;\n    output.val_mat3x3_ = metal::half3x3(metal::float3x3(_e256));\n    metal::half3x4 _e263 = input_uniform.val_mat3x4_;\n    output.val_mat3x4_ = metal::half3x4(metal::float3x4(_e263));\n    metal::half4x2 _e270 = input_uniform.val_mat4x2_;\n    output.val_mat4x2_ = metal::half4x2(metal::float4x2(_e270));\n    metal::half4x3 _e277 = input_uniform.val_mat4x3_;\n    output.val_mat4x3_ = metal::half4x3(metal::float4x3(_e277));\n    metal::half4x4 _e284 = input_uniform.val_mat4x4_;\n    output.val_mat4x4_ = metal::half4x4(metal::float4x4(_e284));\n    half _e287 = val;\n    return _e287;\n}\n\nkernel void main_(\n  constant UniformCompatible& input_uniform [[user(fake0)]]\n, device UniformCompatible const& input_storage [[user(fake0)]]\n, device StorageCompatible const& input_arrays [[user(fake0)]]\n, device UniformCompatible& output [[user(fake0)]]\n, device StorageCompatible& output_arrays [[user(fake0)]]\n) {\n    half private_variable = 1.0h;\n    half _e3 = f16_function(2.0h, private_variable, input_uniform, input_storage, input_arrays, output, output_arrays);\n    output.final_value = _e3;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-fragment-output.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct FragmentOutputVec4Vec3_ {\n    metal::float4 vec4f;\n    metal::int4 vec4i;\n    metal::uint4 vec4u;\n    metal::float3 vec3f;\n    metal::int3 vec3i;\n    metal::uint3 vec3u;\n};\nstruct FragmentOutputVec2Scalar {\n    metal::float2 vec2f;\n    metal::int2 vec2i;\n    metal::uint2 vec2u;\n    float scalarf;\n    int scalari;\n    uint scalaru;\n    char _pad6[4];\n};\n\nstruct main_vec4vec3_Output {\n    metal::float4 vec4f [[color(0)]];\n    metal::int4 vec4i [[color(1)]];\n    metal::uint4 vec4u [[color(2)]];\n    metal::float3 vec3f [[color(3)]];\n    metal::int3 vec3i [[color(4)]];\n    metal::uint3 vec3u [[color(5)]];\n};\nfragment main_vec4vec3_Output main_vec4vec3_(\n) {\n    FragmentOutputVec4Vec3_ output = {};\n    output.vec4f = metal::float4(0.0);\n    output.vec4i = metal::int4(0);\n    output.vec4u = metal::uint4(0u);\n    output.vec3f = metal::float3(0.0);\n    output.vec3i = metal::int3(0);\n    output.vec3u = metal::uint3(0u);\n    FragmentOutputVec4Vec3_ _e19 = output;\n    const auto _tmp = _e19;\n    return main_vec4vec3_Output { _tmp.vec4f, _tmp.vec4i, _tmp.vec4u, _tmp.vec3f, _tmp.vec3i, _tmp.vec3u };\n}\n\n\nstruct main_vec2scalarOutput {\n    metal::float2 vec2f [[color(0)]];\n    metal::int2 vec2i [[color(1)]];\n    metal::uint2 vec2u [[color(2)]];\n    float scalarf [[color(3)]];\n    int scalari [[color(4)]];\n    uint scalaru [[color(5)]];\n};\nfragment main_vec2scalarOutput main_vec2scalar(\n) {\n    FragmentOutputVec2Scalar output_1 = {};\n    output_1.vec2f = metal::float2(0.0);\n    output_1.vec2i = metal::int2(0);\n    output_1.vec2u = metal::uint2(0u);\n    output_1.scalarf = 0.0;\n    output_1.scalari = 0;\n    output_1.scalaru = 0u;\n    FragmentOutputVec2Scalar _e16 = output_1;\n    const auto _tmp = _e16;\n    return main_vec2scalarOutput { _tmp.vec2f, _tmp.vec2i, _tmp.vec2u, _tmp.scalarf, _tmp.scalari, _tmp.scalaru };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-functions-optimized-by-version.metal",
    "content": "// language: metal2.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nuint test_packed_integer_dot_product(\n) {\n    packed_char4 reinterpreted_packed_char4_e0 = as_type<packed_char4>(1u);\n    packed_char4 reinterpreted_packed_char4_e1 = as_type<packed_char4>(2u);\n    int c_5_ = ( + reinterpreted_packed_char4_e0[0] * reinterpreted_packed_char4_e1[0] + reinterpreted_packed_char4_e0[1] * reinterpreted_packed_char4_e1[1] + reinterpreted_packed_char4_e0[2] * reinterpreted_packed_char4_e1[2] + reinterpreted_packed_char4_e0[3] * reinterpreted_packed_char4_e1[3]);\n    packed_uchar4 reinterpreted_packed_uchar4_e3 = as_type<packed_uchar4>(3u);\n    packed_uchar4 reinterpreted_packed_uchar4_e4 = as_type<packed_uchar4>(4u);\n    uint c_6_ = ( + reinterpreted_packed_uchar4_e3[0] * reinterpreted_packed_uchar4_e4[0] + reinterpreted_packed_uchar4_e3[1] * reinterpreted_packed_uchar4_e4[1] + reinterpreted_packed_uchar4_e3[2] * reinterpreted_packed_uchar4_e4[2] + reinterpreted_packed_uchar4_e3[3] * reinterpreted_packed_uchar4_e4[3]);\n    uint _e7 = 5u + c_6_;\n    uint _e9 = 6u + c_6_;\n    packed_char4 reinterpreted_packed_char4_e7 = as_type<packed_char4>(_e7);\n    packed_char4 reinterpreted_packed_char4_e9 = as_type<packed_char4>(_e9);\n    int c_7_ = ( + reinterpreted_packed_char4_e7[0] * reinterpreted_packed_char4_e9[0] + reinterpreted_packed_char4_e7[1] * reinterpreted_packed_char4_e9[1] + reinterpreted_packed_char4_e7[2] * reinterpreted_packed_char4_e9[2] + reinterpreted_packed_char4_e7[3] * reinterpreted_packed_char4_e9[3]);\n    uint _e12 = 7u + c_6_;\n    uint _e14 = 8u + c_6_;\n    packed_uchar4 reinterpreted_packed_uchar4_e12 = as_type<packed_uchar4>(_e12);\n    packed_uchar4 reinterpreted_packed_uchar4_e14 = as_type<packed_uchar4>(_e14);\n    uint c_8_ = ( + reinterpreted_packed_uchar4_e12[0] * reinterpreted_packed_uchar4_e14[0] + reinterpreted_packed_uchar4_e12[1] * reinterpreted_packed_uchar4_e14[1] + reinterpreted_packed_uchar4_e12[2] * reinterpreted_packed_uchar4_e14[2] + reinterpreted_packed_uchar4_e12[3] * reinterpreted_packed_uchar4_e14[3]);\n    return c_8_;\n}\n\nkernel void main_(\n) {\n    uint _e0 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-functions-unoptimized.metal",
    "content": "// language: metal2.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nuint test_packed_integer_dot_product(\n) {\n    int c_5_ = ( + (int(1u) << 24 >> 24) * (int(2u) << 24 >> 24) + (int(1u) << 16 >> 24) * (int(2u) << 16 >> 24) + (int(1u) << 8 >> 24) * (int(2u) << 8 >> 24) + (int(1u) >> 24) * (int(2u) >> 24));\n    uint c_6_ = ( + ((3u) << 24 >> 24) * ((4u) << 24 >> 24) + ((3u) << 16 >> 24) * ((4u) << 16 >> 24) + ((3u) << 8 >> 24) * ((4u) << 8 >> 24) + ((3u) >> 24) * ((4u) >> 24));\n    uint _e7 = 5u + c_6_;\n    uint _e9 = 6u + c_6_;\n    int c_7_ = ( + (int(_e7) << 24 >> 24) * (int(_e9) << 24 >> 24) + (int(_e7) << 16 >> 24) * (int(_e9) << 16 >> 24) + (int(_e7) << 8 >> 24) * (int(_e9) << 8 >> 24) + (int(_e7) >> 24) * (int(_e9) >> 24));\n    uint _e12 = 7u + c_6_;\n    uint _e14 = 8u + c_6_;\n    uint c_8_ = ( + ((_e12) << 24 >> 24) * ((_e14) << 24 >> 24) + ((_e12) << 16 >> 24) * ((_e14) << 16 >> 24) + ((_e12) << 8 >> 24) * ((_e14) << 8 >> 24) + ((_e12) >> 24) * ((_e14) >> 24));\n    return c_8_;\n}\n\nkernel void main_(\n) {\n    uint _e0 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-functions.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nmetal::float2 test_fma(\n) {\n    metal::float2 a = metal::float2(2.0, 2.0);\n    metal::float2 b = metal::float2(0.5, 0.5);\n    metal::float2 c = metal::float2(0.5, 0.5);\n    return metal::fma(a, b, c);\n}\n\nint naga_dot_int2(metal::int2 a, metal::int2 b) {\n    return ( + a.x * b.x + a.y * b.y);\n}\n\nuint naga_dot_uint3(metal::uint3 a, metal::uint3 b) {\n    return ( + a.x * b.x + a.y * b.y + a.z * b.z);\n}\n\nint test_integer_dot_product(\n) {\n    metal::int2 a_2_ = metal::int2(1);\n    metal::int2 b_2_ = metal::int2(1);\n    int c_2_ = naga_dot_int2(a_2_, b_2_);\n    metal::uint3 a_3_ = metal::uint3(1u);\n    metal::uint3 b_3_ = metal::uint3(1u);\n    uint c_3_ = naga_dot_uint3(a_3_, b_3_);\n    return 32;\n}\n\nuint test_packed_integer_dot_product(\n) {\n    int c_5_ = ( + (int(1u) << 24 >> 24) * (int(2u) << 24 >> 24) + (int(1u) << 16 >> 24) * (int(2u) << 16 >> 24) + (int(1u) << 8 >> 24) * (int(2u) << 8 >> 24) + (int(1u) >> 24) * (int(2u) >> 24));\n    uint c_6_ = ( + ((3u) << 24 >> 24) * ((4u) << 24 >> 24) + ((3u) << 16 >> 24) * ((4u) << 16 >> 24) + ((3u) << 8 >> 24) * ((4u) << 8 >> 24) + ((3u) >> 24) * ((4u) >> 24));\n    uint _e7 = 5u + c_6_;\n    uint _e9 = 6u + c_6_;\n    int c_7_ = ( + (int(_e7) << 24 >> 24) * (int(_e9) << 24 >> 24) + (int(_e7) << 16 >> 24) * (int(_e9) << 16 >> 24) + (int(_e7) << 8 >> 24) * (int(_e9) << 8 >> 24) + (int(_e7) >> 24) * (int(_e9) >> 24));\n    uint _e12 = 7u + c_6_;\n    uint _e14 = 8u + c_6_;\n    uint c_8_ = ( + ((_e12) << 24 >> 24) * ((_e14) << 24 >> 24) + ((_e12) << 16 >> 24) * ((_e14) << 16 >> 24) + ((_e12) << 8 >> 24) * ((_e14) << 8 >> 24) + ((_e12) >> 24) * ((_e14) >> 24));\n    return c_8_;\n}\n\nkernel void main_(\n) {\n    metal::float2 _e0 = test_fma();\n    int _e1 = test_integer_dot_product();\n    uint _e2 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-globals.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size3;\n};\n\nstruct type_2 {\n    float inner[10];\n};\nstruct FooStruct {\n    metal::packed_float3 v3_;\n    float v1_;\n};\ntypedef metal::float2 type_6[1];\nstruct type_8 {\n    metal::float4 inner[20];\n};\nstruct type_11 {\n    metal::float2x4 inner[2];\n};\nstruct type_12 {\n    type_11 inner[2];\n};\nstruct type_14 {\n    metal::float4x2 inner[2];\n};\nstruct type_15 {\n    type_14 inner[2];\n};\nconstant bool Foo_1 = true;\n\nvoid test_msl_packed_vec3_as_arg(\n    metal::float3 arg\n) {\n    return;\n}\n\nvoid test_msl_packed_vec3_(\n    device FooStruct& alignment\n) {\n    int idx = 1;\n    alignment.v3_ = metal::float3(1.0);\n    alignment.v3_[0] = 1.0;\n    alignment.v3_[0] = 2.0;\n    int _e16 = idx;\n    alignment.v3_[_e16] = 3.0;\n    FooStruct data = alignment;\n    metal::float3 l0_ = data.v3_;\n    metal::float2 l1_ = metal::float3(data.v3_).zx;\n    test_msl_packed_vec3_as_arg(data.v3_);\n    metal::float3 mvm0_ = metal::float3(data.v3_) * metal::float3x3 {};\n    metal::float3 mvm1_ = metal::float3x3 {} * metal::float3(data.v3_);\n    metal::float3 svm0_ = data.v3_ * 2.0;\n    metal::float3 svm1_ = 2.0 * data.v3_;\n    return;\n}\n\nkernel void main_(\n  metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, threadgroup type_2& wg\n, threadgroup metal::atomic_uint& at_1\n, device FooStruct& alignment [[user(fake0)]]\n, device type_6 const& dummy [[user(fake0)]]\n, constant type_8& float_vecs [[user(fake0)]]\n, constant metal::float3& global_vec [[user(fake0)]]\n, constant metal::float3x2& global_mat [[user(fake0)]]\n, constant type_12& global_nested_arrays_of_matrices_2x4_ [[user(fake0)]]\n, constant type_15& global_nested_arrays_of_matrices_4x2_ [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        wg = {};\n        metal::atomic_store_explicit(&at_1, 0, metal::memory_order_relaxed);\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    float Foo = 1.0;\n    bool at = true;\n    test_msl_packed_vec3_(alignment);\n    metal::float4x2 _e5 = global_nested_arrays_of_matrices_4x2_.inner[0].inner[0];\n    metal::float4 _e10 = global_nested_arrays_of_matrices_2x4_.inner[0].inner[0][0];\n    wg.inner[7] = (_e5 * _e10).x;\n    metal::float3x2 _e16 = global_mat;\n    metal::float3 _e18 = global_vec;\n    wg.inner[6] = (_e16 * _e18).x;\n    float _e26 = dummy[1].y;\n    wg.inner[5] = _e26;\n    float _e32 = float_vecs.inner[0].w;\n    wg.inner[4] = _e32;\n    float _e37 = alignment.v1_;\n    wg.inner[3] = _e37;\n    float _e43 = alignment.v3_[0];\n    wg.inner[2] = _e43;\n    alignment.v1_ = 4.0;\n    wg.inner[1] = static_cast<float>(1 + (_buffer_sizes.size3 - 0 - 8) / 8);\n    metal::atomic_store_explicit(&at_1, 2u, metal::memory_order_relaxed);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-image.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nmetal::int2 naga_mod(metal::int2 lhs, metal::int2 rhs) {\n    metal::int2 divisor = metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n    return lhs - (lhs / divisor) * divisor;\n}\n\n\nstruct main_Input {\n};\nkernel void main_(\n  metal::uint3 local_id [[thread_position_in_threadgroup]]\n, metal::texture2d<uint, metal::access::sample> image_mipmapped_src [[user(fake0)]]\n, metal::texture2d_ms<uint, metal::access::read> image_multisampled_src [[user(fake0)]]\n, metal::texture2d<uint, metal::access::read> image_storage_src [[user(fake0)]]\n, metal::texture2d_array<uint, metal::access::sample> image_array_src [[user(fake0)]]\n, metal::texture1d<uint, metal::access::read> image_dup_src [[user(fake0)]]\n, metal::texture1d<uint, metal::access::sample> image_1d_src [[user(fake0)]]\n, metal::texture1d<uint, metal::access::write> image_dst [[user(fake0)]]\n) {\n    metal::uint2 dim = metal::uint2(image_storage_src.get_width(), image_storage_src.get_height());\n    metal::int2 itc = naga_mod(static_cast<metal::int2>(dim * local_id.xy), metal::int2(10, 20));\n    metal::uint4 value1_ = image_mipmapped_src.read(metal::uint2(itc), static_cast<int>(local_id.z));\n    metal::uint4 value1_2_ = image_mipmapped_src.read(metal::uint2(itc), static_cast<uint>(local_id.z));\n    metal::uint4 value2_ = image_multisampled_src.read(metal::uint2(itc), static_cast<int>(local_id.z));\n    metal::uint4 value3_ = image_multisampled_src.read(metal::uint2(itc), static_cast<uint>(local_id.z));\n    metal::uint4 value4_ = image_storage_src.read(metal::uint2(itc));\n    metal::uint4 value5_ = image_array_src.read(metal::uint2(itc), local_id.z, as_type<int>(as_type<uint>(static_cast<int>(local_id.z)) + as_type<uint>(1)));\n    metal::uint4 value6_ = image_array_src.read(metal::uint2(itc), static_cast<int>(local_id.z), as_type<int>(as_type<uint>(static_cast<int>(local_id.z)) + as_type<uint>(1)));\n    metal::uint4 value7_ = image_1d_src.read(uint(static_cast<int>(local_id.x)));\n    metal::uint4 value8_ = image_dup_src.read(uint(static_cast<int>(local_id.x)));\n    metal::uint4 value1u = image_mipmapped_src.read(metal::uint2(static_cast<metal::uint2>(itc)), static_cast<int>(local_id.z));\n    metal::uint4 value2u = image_multisampled_src.read(metal::uint2(static_cast<metal::uint2>(itc)), static_cast<int>(local_id.z));\n    metal::uint4 value3u = image_multisampled_src.read(metal::uint2(static_cast<metal::uint2>(itc)), static_cast<uint>(local_id.z));\n    metal::uint4 value4u = image_storage_src.read(metal::uint2(static_cast<metal::uint2>(itc)));\n    metal::uint4 value5u = image_array_src.read(metal::uint2(static_cast<metal::uint2>(itc)), local_id.z, as_type<int>(as_type<uint>(static_cast<int>(local_id.z)) + as_type<uint>(1)));\n    metal::uint4 value6u = image_array_src.read(metal::uint2(static_cast<metal::uint2>(itc)), static_cast<int>(local_id.z), as_type<int>(as_type<uint>(static_cast<int>(local_id.z)) + as_type<uint>(1)));\n    metal::uint4 value7u = image_1d_src.read(uint(static_cast<uint>(local_id.x)));\n    image_dst.write((((value1_ + value2_) + value4_) + value5_) + value6_, uint(itc.x));\n    image_dst.write((((value1u + value2u) + value4u) + value5u) + value6u, uint(static_cast<uint>(itc.x)));\n    return;\n}\n\nuint naga_f2u32(float value) {\n    return static_cast<uint>(metal::clamp(value, 0.0, 4294967000.0));\n}\n\n\nstruct depth_loadInput {\n};\nkernel void depth_load(\n  metal::uint3 local_id_1 [[thread_position_in_threadgroup]]\n, metal::depth2d_ms<float, metal::access::read> image_depth_multisampled_src [[user(fake0)]]\n, metal::texture2d<uint, metal::access::read> image_storage_src [[user(fake0)]]\n, metal::texture1d<uint, metal::access::write> image_dst [[user(fake0)]]\n) {\n    metal::uint2 dim_1 = metal::uint2(image_storage_src.get_width(), image_storage_src.get_height());\n    metal::int2 itc_1 = naga_mod(static_cast<metal::int2>(dim_1 * local_id_1.xy), metal::int2(10, 20));\n    float val = image_depth_multisampled_src.read(metal::uint2(itc_1), static_cast<int>(local_id_1.z));\n    image_dst.write(metal::uint4(naga_f2u32(val)), uint(itc_1.x));\n    return;\n}\n\n\nstruct queriesOutput {\n    metal::float4 member_2 [[position]];\n};\nvertex queriesOutput queries(\n  metal::texture1d<float, metal::access::sample> image_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, metal::texturecube<float, metal::access::sample> image_cube [[user(fake0)]]\n, metal::texturecube_array<float, metal::access::sample> image_cube_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::sample> image_3d [[user(fake0)]]\n, metal::texture2d_ms<float, metal::access::read> image_aa [[user(fake0)]]\n) {\n    uint dim_1d = image_1d.get_width();\n    uint dim_1d_lod = image_1d.get_width();\n    metal::uint2 dim_2d = metal::uint2(image_2d.get_width(), image_2d.get_height());\n    metal::uint2 dim_2d_lod = metal::uint2(image_2d.get_width(1), image_2d.get_height(1));\n    metal::uint2 dim_2d_array = metal::uint2(image_2d_array.get_width(), image_2d_array.get_height());\n    metal::uint2 dim_2d_array_lod = metal::uint2(image_2d_array.get_width(1), image_2d_array.get_height(1));\n    metal::uint2 dim_cube = metal::uint2(image_cube.get_width());\n    metal::uint2 dim_cube_lod = metal::uint2(image_cube.get_width(1));\n    metal::uint2 dim_cube_array = metal::uint2(image_cube_array.get_width());\n    metal::uint2 dim_cube_array_lod = metal::uint2(image_cube_array.get_width(1));\n    metal::uint3 dim_3d = metal::uint3(image_3d.get_width(), image_3d.get_height(), image_3d.get_depth());\n    metal::uint3 dim_3d_lod = metal::uint3(image_3d.get_width(1), image_3d.get_height(1), image_3d.get_depth(1));\n    metal::uint2 dim_2s_ms = metal::uint2(image_aa.get_width(), image_aa.get_height());\n    uint sum = (((((((((dim_1d + dim_2d.y) + dim_2d_lod.y) + dim_2d_array.y) + dim_2d_array_lod.y) + dim_cube.y) + dim_cube_lod.y) + dim_cube_array.y) + dim_cube_array_lod.y) + dim_3d.z) + dim_3d_lod.z;\n    return queriesOutput { metal::float4(static_cast<float>(sum)) };\n}\n\n\nstruct levels_queriesOutput {\n    metal::float4 member_3 [[position]];\n};\nvertex levels_queriesOutput levels_queries(\n  metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, metal::texturecube<float, metal::access::sample> image_cube [[user(fake0)]]\n, metal::texturecube_array<float, metal::access::sample> image_cube_array [[user(fake0)]]\n, metal::texture3d<float, metal::access::sample> image_3d [[user(fake0)]]\n, metal::texture2d_ms<float, metal::access::read> image_aa [[user(fake0)]]\n) {\n    uint num_levels_2d = image_2d.get_num_mip_levels();\n    uint num_layers_2d = image_2d_array.get_array_size();\n    uint num_levels_2d_array = image_2d_array.get_num_mip_levels();\n    uint num_layers_2d_array = image_2d_array.get_array_size();\n    uint num_levels_cube = image_cube.get_num_mip_levels();\n    uint num_levels_cube_array = image_cube_array.get_num_mip_levels();\n    uint num_layers_cube = image_cube_array.get_array_size();\n    uint num_levels_3d = image_3d.get_num_mip_levels();\n    uint num_samples_aa = image_aa.get_num_samples();\n    uint sum_1 = ((((((num_layers_2d + num_layers_cube) + num_samples_aa) + num_levels_2d) + num_levels_2d_array) + num_levels_3d) + num_levels_cube) + num_levels_cube_array;\n    return levels_queriesOutput { metal::float4(static_cast<float>(sum_1)) };\n}\n\nmetal::float4 nagaTextureSampleBaseClampToEdge(metal::texture2d<float, metal::access::sample> tex, metal::sampler samp, metal::float2 coords) {\n    metal::float2 half_texel = 0.5 / metal::float2(tex.get_width(0u), tex.get_height(0u));\n    return tex.sample(samp, metal::clamp(coords, half_texel, 1.0 - half_texel), metal::level(0.0));\n}\n\n\nstruct texture_sampleOutput {\n    metal::float4 member_4 [[color(0)]];\n};\nfragment texture_sampleOutput texture_sample(\n  metal::texture1d<float, metal::access::sample> image_1d [[user(fake0)]]\n, metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, metal::texturecube_array<float, metal::access::sample> image_cube_array [[user(fake0)]]\n, metal::sampler sampler_reg [[user(fake0)]]\n) {\n    metal::float4 a = {};\n    metal::float2 _e1 = metal::float2(0.5);\n    metal::float3 _e3 = metal::float3(0.5);\n    metal::int2 _e6 = metal::int2(3, 1);\n    metal::float4 _e9 = a;\n    metal::float4 _e12 = image_1d.sample(sampler_reg, 0.5);\n    a = _e9 + _e12;\n    metal::float4 _e14 = a;\n    metal::float4 _e17 = image_2d.sample(sampler_reg, _e1);\n    a = _e14 + _e17;\n    metal::float4 _e19 = a;\n    metal::float4 _e25 = image_2d.sample(sampler_reg, _e1, metal::int2(3, 1));\n    a = _e19 + _e25;\n    metal::float4 _e27 = a;\n    metal::float4 _e30 = image_2d.sample(sampler_reg, _e1, metal::level(2.3));\n    a = _e27 + _e30;\n    metal::float4 _e32 = a;\n    metal::float4 _e35 = image_2d.sample(sampler_reg, _e1, metal::level(2.3), _e6);\n    a = _e32 + _e35;\n    metal::float4 _e37 = a;\n    metal::float4 _e41 = image_2d.sample(sampler_reg, _e1, metal::bias(2.0), _e6);\n    a = _e37 + _e41;\n    metal::float4 _e43 = a;\n    metal::float4 _e46 = nagaTextureSampleBaseClampToEdge(image_2d, sampler_reg, _e1);\n    a = _e43 + _e46;\n    metal::float4 _e48 = a;\n    metal::float4 _e52 = image_2d_array.sample(sampler_reg, _e1, 0u);\n    a = _e48 + _e52;\n    metal::float4 _e54 = a;\n    metal::float4 _e58 = image_2d_array.sample(sampler_reg, _e1, 0u, _e6);\n    a = _e54 + _e58;\n    metal::float4 _e60 = a;\n    metal::float4 _e64 = image_2d_array.sample(sampler_reg, _e1, 0u, metal::level(2.3));\n    a = _e60 + _e64;\n    metal::float4 _e66 = a;\n    metal::float4 _e70 = image_2d_array.sample(sampler_reg, _e1, 0u, metal::level(2.3), _e6);\n    a = _e66 + _e70;\n    metal::float4 _e72 = a;\n    metal::float4 _e77 = image_2d_array.sample(sampler_reg, _e1, 0u, metal::bias(2.0), _e6);\n    a = _e72 + _e77;\n    metal::float4 _e79 = a;\n    metal::float4 _e83 = image_2d_array.sample(sampler_reg, _e1, 0);\n    a = _e79 + _e83;\n    metal::float4 _e85 = a;\n    metal::float4 _e89 = image_2d_array.sample(sampler_reg, _e1, 0, _e6);\n    a = _e85 + _e89;\n    metal::float4 _e91 = a;\n    metal::float4 _e95 = image_2d_array.sample(sampler_reg, _e1, 0, metal::level(2.3));\n    a = _e91 + _e95;\n    metal::float4 _e97 = a;\n    metal::float4 _e101 = image_2d_array.sample(sampler_reg, _e1, 0, metal::level(2.3), _e6);\n    a = _e97 + _e101;\n    metal::float4 _e103 = a;\n    metal::float4 _e108 = image_2d_array.sample(sampler_reg, _e1, 0, metal::bias(2.0), _e6);\n    a = _e103 + _e108;\n    metal::float4 _e110 = a;\n    metal::float4 _e114 = image_cube_array.sample(sampler_reg, _e3, 0u);\n    a = _e110 + _e114;\n    metal::float4 _e116 = a;\n    metal::float4 _e120 = image_cube_array.sample(sampler_reg, _e3, 0u, metal::level(2.3));\n    a = _e116 + _e120;\n    metal::float4 _e122 = a;\n    metal::float4 _e127 = image_cube_array.sample(sampler_reg, _e3, 0u, metal::bias(2.0));\n    a = _e122 + _e127;\n    metal::float4 _e129 = a;\n    metal::float4 _e133 = image_cube_array.sample(sampler_reg, _e3, 0);\n    a = _e129 + _e133;\n    metal::float4 _e135 = a;\n    metal::float4 _e139 = image_cube_array.sample(sampler_reg, _e3, 0, metal::level(2.3));\n    a = _e135 + _e139;\n    metal::float4 _e141 = a;\n    metal::float4 _e146 = image_cube_array.sample(sampler_reg, _e3, 0, metal::bias(2.0));\n    a = _e141 + _e146;\n    metal::float4 _e148 = a;\n    return texture_sampleOutput { _e148 };\n}\n\n\nstruct texture_sample_comparisonOutput {\n    float member_5 [[color(0)]];\n};\nfragment texture_sample_comparisonOutput texture_sample_comparison(\n  metal::sampler sampler_cmp [[user(fake0)]]\n, metal::depth2d<float, metal::access::sample> image_2d_depth [[user(fake0)]]\n, metal::depth2d_array<float, metal::access::sample> image_2d_array_depth [[user(fake0)]]\n, metal::depthcube<float, metal::access::sample> image_cube_depth [[user(fake0)]]\n) {\n    float a_1 = {};\n    metal::float2 tc = metal::float2(0.5);\n    metal::float3 tc3_ = metal::float3(0.5);\n    float _e6 = a_1;\n    float _e9 = image_2d_depth.sample_compare(sampler_cmp, tc, 0.5);\n    a_1 = _e6 + _e9;\n    float _e11 = a_1;\n    float _e15 = image_2d_array_depth.sample_compare(sampler_cmp, tc, 0u, 0.5);\n    a_1 = _e11 + _e15;\n    float _e17 = a_1;\n    float _e21 = image_2d_array_depth.sample_compare(sampler_cmp, tc, 0, 0.5);\n    a_1 = _e17 + _e21;\n    float _e23 = a_1;\n    float _e26 = image_cube_depth.sample_compare(sampler_cmp, tc3_, 0.5);\n    a_1 = _e23 + _e26;\n    float _e28 = a_1;\n    float _e31 = image_2d_depth.sample_compare(sampler_cmp, tc, 0.5);\n    a_1 = _e28 + _e31;\n    float _e33 = a_1;\n    float _e37 = image_2d_array_depth.sample_compare(sampler_cmp, tc, 0u, 0.5);\n    a_1 = _e33 + _e37;\n    float _e39 = a_1;\n    float _e43 = image_2d_array_depth.sample_compare(sampler_cmp, tc, 0, 0.5);\n    a_1 = _e39 + _e43;\n    float _e45 = a_1;\n    float _e48 = image_cube_depth.sample_compare(sampler_cmp, tc3_, 0.5);\n    a_1 = _e45 + _e48;\n    float _e50 = a_1;\n    return texture_sample_comparisonOutput { _e50 };\n}\n\n\nstruct gatherOutput {\n    metal::float4 member_6 [[color(0)]];\n};\nfragment gatherOutput gather(\n  metal::texture2d<float, metal::access::sample> image_2d [[user(fake0)]]\n, metal::texture2d<uint, metal::access::sample> image_2d_u32_ [[user(fake0)]]\n, metal::texture2d<int, metal::access::sample> image_2d_i32_ [[user(fake0)]]\n, metal::sampler sampler_reg [[user(fake0)]]\n, metal::sampler sampler_cmp [[user(fake0)]]\n, metal::depth2d<float, metal::access::sample> image_2d_depth [[user(fake0)]]\n) {\n    metal::float2 tc_1 = metal::float2(0.5);\n    metal::float4 s2d = image_2d.gather(sampler_reg, tc_1, metal::int2(0), metal::component::y);\n    metal::float4 s2d_offset = image_2d.gather(sampler_reg, tc_1, metal::int2(3, 1), metal::component::w);\n    metal::float4 s2d_depth = image_2d_depth.gather_compare(sampler_cmp, tc_1, 0.5);\n    metal::float4 s2d_depth_offset = image_2d_depth.gather_compare(sampler_cmp, tc_1, 0.5, metal::int2(3, 1));\n    metal::uint4 u = image_2d_u32_.gather(sampler_reg, tc_1);\n    metal::int4 i = image_2d_i32_.gather(sampler_reg, tc_1);\n    metal::float4 f = static_cast<metal::float4>(u) + static_cast<metal::float4>(i);\n    return gatherOutput { (((s2d + s2d_offset) + s2d_depth) + s2d_depth_offset) + f };\n}\n\n\nstruct depth_no_comparisonOutput {\n    metal::float4 member_7 [[color(0)]];\n};\nfragment depth_no_comparisonOutput depth_no_comparison(\n  metal::sampler sampler_reg [[user(fake0)]]\n, metal::depth2d<float, metal::access::sample> image_2d_depth [[user(fake0)]]\n) {\n    metal::float2 tc_2 = metal::float2(0.5);\n    float s2d_1 = image_2d_depth.sample(sampler_reg, tc_2);\n    metal::float4 s2d_gather = image_2d_depth.gather(sampler_reg, tc_2);\n    float s2d_level = image_2d_depth.sample(sampler_reg, tc_2, metal::level(1));\n    return depth_no_comparisonOutput { (metal::float4(s2d_1) + s2d_gather) + metal::float4(s2d_level) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-int64.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct UniformCompatible {\n    uint val_u32_;\n    int val_i32_;\n    float val_f32_;\n    char _pad3[4];\n    ulong val_u64_;\n    char _pad4[8];\n    metal::ulong2 val_u64_2_;\n    char _pad5[16];\n    metal::ulong3 val_u64_3_;\n    metal::ulong4 val_u64_4_;\n    long val_i64_;\n    char _pad8[8];\n    metal::long2 val_i64_2_;\n    metal::long3 val_i64_3_;\n    metal::long4 val_i64_4_;\n    ulong final_value;\n    char _pad12[24];\n};\nstruct type_11 {\n    ulong inner[2];\n};\nstruct type_12 {\n    long inner[2];\n};\nstruct StorageCompatible {\n    type_11 val_u64_array_2_;\n    type_12 val_i64_array_2_;\n};\nconstant ulong constant_variable = 20uL;\n\nlong naga_f2i64(float value) {\n    return static_cast<long>(metal::clamp(value, -9223372000000000000.0, 9223371500000000000.0));\n}\n\nlong naga_abs(long val) {\n    return metal::select(as_type<long>(-as_type<ulong>(val)), val, val >= 0);\n}\n\nlong naga_dot_long2(metal::long2 a, metal::long2 b) {\n    return ( + a.x * b.x + a.y * b.y);\n}\n\nlong int64_function(\n    long x,\n    thread long& private_variable,\n    constant UniformCompatible& input_uniform,\n    device UniformCompatible const& input_storage,\n    device StorageCompatible const& input_arrays,\n    device UniformCompatible& output,\n    device StorageCompatible& output_arrays\n) {\n    long val = 20L;\n    long phony = private_variable;\n    long _e5 = val;\n    val = as_type<long>(as_type<ulong>(_e5) + as_type<ulong>(as_type<long>(as_type<ulong>(as_type<long>(as_type<ulong>(31L) - as_type<ulong>(1002003004005006L))) + as_type<ulong>(-9223372036854775807L))));\n    long _e12 = val;\n    long _e13 = val;\n    val = as_type<long>(as_type<ulong>(_e12) + as_type<ulong>(as_type<long>(as_type<ulong>(_e13) + as_type<ulong>(5L))));\n    long _e17 = val;\n    uint _e20 = input_uniform.val_u32_;\n    long _e21 = val;\n    val = as_type<long>(as_type<ulong>(_e17) + as_type<ulong>(static_cast<long>(_e20 + static_cast<uint>(_e21))));\n    long _e26 = val;\n    int _e29 = input_uniform.val_i32_;\n    long _e30 = val;\n    val = as_type<long>(as_type<ulong>(_e26) + as_type<ulong>(static_cast<long>(as_type<int>(as_type<uint>(_e29) + as_type<uint>(static_cast<int>(_e30))))));\n    long _e35 = val;\n    float _e38 = input_uniform.val_f32_;\n    long _e39 = val;\n    val = as_type<long>(as_type<ulong>(_e35) + as_type<ulong>(naga_f2i64(_e38 + static_cast<float>(_e39))));\n    long _e44 = val;\n    long _e47 = input_uniform.val_i64_;\n    val = as_type<long>(as_type<ulong>(_e44) + as_type<ulong>(metal::long3(_e47).z));\n    long _e51 = val;\n    ulong _e54 = input_uniform.val_u64_;\n    val = as_type<long>(as_type<ulong>(_e51) + as_type<ulong>(as_type<long>(_e54)));\n    long _e57 = val;\n    metal::ulong2 _e60 = input_uniform.val_u64_2_;\n    val = as_type<long>(as_type<ulong>(_e57) + as_type<ulong>(as_type<metal::long2>(_e60).y));\n    long _e64 = val;\n    metal::ulong3 _e67 = input_uniform.val_u64_3_;\n    val = as_type<long>(as_type<ulong>(_e64) + as_type<ulong>(as_type<metal::long3>(_e67).z));\n    long _e71 = val;\n    metal::ulong4 _e74 = input_uniform.val_u64_4_;\n    val = as_type<long>(as_type<ulong>(_e71) + as_type<ulong>(as_type<metal::long4>(_e74).w));\n    long _e78 = val;\n    val = as_type<long>(as_type<ulong>(_e78) + as_type<ulong>((-9223372036854775807L - 1L)));\n    long _e85 = input_uniform.val_i64_;\n    long _e88 = input_storage.val_i64_;\n    output.val_i64_ = as_type<long>(as_type<ulong>(_e85) + as_type<ulong>(_e88));\n    metal::long2 _e94 = input_uniform.val_i64_2_;\n    metal::long2 _e97 = input_storage.val_i64_2_;\n    output.val_i64_2_ = as_type<metal::long2>(as_type<metal::ulong2>(_e94) + as_type<metal::ulong2>(_e97));\n    metal::long3 _e103 = input_uniform.val_i64_3_;\n    metal::long3 _e106 = input_storage.val_i64_3_;\n    output.val_i64_3_ = as_type<metal::long3>(as_type<metal::ulong3>(_e103) + as_type<metal::ulong3>(_e106));\n    metal::long4 _e112 = input_uniform.val_i64_4_;\n    metal::long4 _e115 = input_storage.val_i64_4_;\n    output.val_i64_4_ = as_type<metal::long4>(as_type<metal::ulong4>(_e112) + as_type<metal::ulong4>(_e115));\n    type_12 _e121 = input_arrays.val_i64_array_2_;\n    output_arrays.val_i64_array_2_ = _e121;\n    long _e122 = val;\n    long _e123 = val;\n    val = as_type<long>(as_type<ulong>(_e122) + as_type<ulong>(naga_abs(_e123)));\n    long _e126 = val;\n    long _e127 = val;\n    long _e128 = val;\n    long _e129 = val;\n    val = as_type<long>(as_type<ulong>(_e126) + as_type<ulong>(metal::clamp(_e127, _e128, _e129)));\n    long _e132 = val;\n    long _e133 = val;\n    long _e135 = val;\n    val = as_type<long>(as_type<ulong>(_e132) + as_type<ulong>(naga_dot_long2(metal::long2(_e133), metal::long2(_e135))));\n    long _e139 = val;\n    long _e140 = val;\n    long _e141 = val;\n    val = as_type<long>(as_type<ulong>(_e139) + as_type<ulong>(metal::max(_e140, _e141)));\n    long _e144 = val;\n    long _e145 = val;\n    long _e146 = val;\n    val = as_type<long>(as_type<ulong>(_e144) + as_type<ulong>(metal::min(_e145, _e146)));\n    long _e149 = val;\n    long _e150 = val;\n    val = as_type<long>(as_type<ulong>(_e149) + as_type<ulong>(metal::select(metal::select(long(-1), long(1), (_e150 > 0)), long(0), (_e150 == 0))));\n    long _e153 = val;\n    return _e153;\n}\n\nulong naga_f2u64(float value) {\n    return static_cast<ulong>(metal::clamp(value, 0.0, 18446743000000000000.0));\n}\n\nulong naga_dot_ulong2(metal::ulong2 a, metal::ulong2 b) {\n    return ( + a.x * b.x + a.y * b.y);\n}\n\nulong uint64_function(\n    ulong x_1,\n    constant UniformCompatible& input_uniform,\n    device UniformCompatible const& input_storage,\n    device StorageCompatible const& input_arrays,\n    device UniformCompatible& output,\n    device StorageCompatible& output_arrays\n) {\n    ulong val_1 = 20uL;\n    ulong _e3 = val_1;\n    val_1 = _e3 + ((31uL + 18446744073709551615uL) - 18446744073709551615uL);\n    ulong _e10 = val_1;\n    ulong _e11 = val_1;\n    val_1 = _e10 + (_e11 + 5uL);\n    ulong _e15 = val_1;\n    uint _e18 = input_uniform.val_u32_;\n    ulong _e19 = val_1;\n    val_1 = _e15 + static_cast<ulong>(_e18 + static_cast<uint>(_e19));\n    ulong _e24 = val_1;\n    int _e27 = input_uniform.val_i32_;\n    ulong _e28 = val_1;\n    val_1 = _e24 + static_cast<ulong>(as_type<int>(as_type<uint>(_e27) + as_type<uint>(static_cast<int>(_e28))));\n    ulong _e33 = val_1;\n    float _e36 = input_uniform.val_f32_;\n    ulong _e37 = val_1;\n    val_1 = _e33 + naga_f2u64(_e36 + static_cast<float>(_e37));\n    ulong _e42 = val_1;\n    ulong _e45 = input_uniform.val_u64_;\n    val_1 = _e42 + metal::ulong3(_e45).z;\n    ulong _e49 = val_1;\n    long _e52 = input_uniform.val_i64_;\n    val_1 = _e49 + as_type<ulong>(_e52);\n    ulong _e55 = val_1;\n    metal::long2 _e58 = input_uniform.val_i64_2_;\n    val_1 = _e55 + as_type<metal::ulong2>(_e58).y;\n    ulong _e62 = val_1;\n    metal::long3 _e65 = input_uniform.val_i64_3_;\n    val_1 = _e62 + as_type<metal::ulong3>(_e65).z;\n    ulong _e69 = val_1;\n    metal::long4 _e72 = input_uniform.val_i64_4_;\n    val_1 = _e69 + as_type<metal::ulong4>(_e72).w;\n    ulong _e80 = input_uniform.val_u64_;\n    ulong _e83 = input_storage.val_u64_;\n    output.val_u64_ = _e80 + _e83;\n    metal::ulong2 _e89 = input_uniform.val_u64_2_;\n    metal::ulong2 _e92 = input_storage.val_u64_2_;\n    output.val_u64_2_ = _e89 + _e92;\n    metal::ulong3 _e98 = input_uniform.val_u64_3_;\n    metal::ulong3 _e101 = input_storage.val_u64_3_;\n    output.val_u64_3_ = _e98 + _e101;\n    metal::ulong4 _e107 = input_uniform.val_u64_4_;\n    metal::ulong4 _e110 = input_storage.val_u64_4_;\n    output.val_u64_4_ = _e107 + _e110;\n    type_11 _e116 = input_arrays.val_u64_array_2_;\n    output_arrays.val_u64_array_2_ = _e116;\n    ulong _e117 = val_1;\n    ulong _e118 = val_1;\n    val_1 = _e117 + metal::abs(_e118);\n    ulong _e121 = val_1;\n    ulong _e122 = val_1;\n    ulong _e123 = val_1;\n    ulong _e124 = val_1;\n    val_1 = _e121 + metal::clamp(_e122, _e123, _e124);\n    ulong _e127 = val_1;\n    ulong _e128 = val_1;\n    ulong _e130 = val_1;\n    val_1 = _e127 + naga_dot_ulong2(metal::ulong2(_e128), metal::ulong2(_e130));\n    ulong _e134 = val_1;\n    ulong _e135 = val_1;\n    ulong _e136 = val_1;\n    val_1 = _e134 + metal::max(_e135, _e136);\n    ulong _e139 = val_1;\n    ulong _e140 = val_1;\n    ulong _e141 = val_1;\n    val_1 = _e139 + metal::min(_e140, _e141);\n    ulong _e144 = val_1;\n    return _e144;\n}\n\nkernel void main_(\n  constant UniformCompatible& input_uniform [[user(fake0)]]\n, device UniformCompatible const& input_storage [[user(fake0)]]\n, device StorageCompatible const& input_arrays [[user(fake0)]]\n, device UniformCompatible& output [[user(fake0)]]\n, device StorageCompatible& output_arrays [[user(fake0)]]\n) {\n    long private_variable = 1L;\n    ulong _e3 = uint64_function(67uL, input_uniform, input_storage, input_arrays, output, output_arrays);\n    long _e5 = int64_function(60L, private_variable, input_uniform, input_storage, input_arrays, output, output_arrays);\n    output.final_value = _e3 + as_type<ulong>(_e5);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-interface.metal",
    "content": "// language: metal2.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct VertexOutput {\n    metal::float4 position;\n    float _varying;\n    char _pad2[12];\n};\nstruct FragmentOutput {\n    float depth;\n    uint sample_mask;\n    float color;\n};\nstruct type_4 {\n    uint inner[1];\n};\nstruct Input1_ {\n    uint index;\n};\nstruct Input2_ {\n    uint index;\n};\n\nstruct vertex_Input {\n    uint color [[attribute(10)]];\n};\nstruct vertex_Output {\n    metal::float4 position [[position, invariant]];\n    float _varying [[user(loc1), center_perspective]];\n    float _point_size [[point_size]];\n};\nvertex vertex_Output vertex_(\n  vertex_Input varyings [[stage_in]]\n, uint vertex_index [[vertex_id]]\n, uint instance_index [[instance_id]]\n) {\n    const auto color = varyings.color;\n    uint tmp = (vertex_index + instance_index) + color;\n    const auto _tmp = VertexOutput {metal::float4(1.0), static_cast<float>(tmp)};\n    return vertex_Output { _tmp.position, _tmp._varying, 1.0 };\n}\n\n\nstruct fragment_Input {\n    float _varying [[user(loc1), center_perspective]];\n};\nstruct fragment_Output {\n    float depth [[depth(any)]];\n    uint sample_mask [[sample_mask]];\n    float color [[color(0)]];\n};\nfragment fragment_Output fragment_(\n  fragment_Input varyings_1 [[stage_in]]\n, metal::float4 position [[position]]\n, bool front_facing [[front_facing]]\n, uint sample_index [[sample_id]]\n, uint sample_mask [[sample_mask]]\n) {\n    const VertexOutput in = { position, varyings_1._varying };\n    uint mask = sample_mask & (1u << sample_index);\n    float color_1 = front_facing ? 1.0 : 0.0;\n    const auto _tmp = FragmentOutput {in._varying, mask, color_1};\n    return fragment_Output { _tmp.depth, _tmp.sample_mask, _tmp.color };\n}\n\n\nstruct computeInput {\n};\nkernel void compute(\n  metal::uint3 global_id [[thread_position_in_grid]]\n, metal::uint3 local_id [[thread_position_in_threadgroup]]\n, uint local_index [[thread_index_in_threadgroup]]\n, metal::uint3 wg_id [[threadgroup_position_in_grid]]\n, metal::uint3 num_wgs [[threadgroups_per_grid]]\n, threadgroup type_4& output\n) {\n    if (metal::all(local_id == metal::uint3(0u))) {\n        output = {};\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    output.inner[0] = (((global_id.x + local_id.x) + local_index) + wg_id.x) + num_wgs.x;\n    return;\n}\n\n\nstruct vertex_two_structsInput {\n};\nstruct vertex_two_structsOutput {\n    metal::float4 member_3 [[position, invariant]];\n    float _point_size [[point_size]];\n};\nvertex vertex_two_structsOutput vertex_two_structs(\n  uint index_1 [[vertex_id]]\n, uint index_2 [[instance_id]]\n) {\n    const Input1_ in1_ = { index_1 };\n    const Input2_ in2_ = { index_2 };\n    uint index = 2u;\n    uint _e8 = index;\n    return vertex_two_structsOutput { metal::float4(static_cast<float>(in1_.index), static_cast<float>(in2_.index), static_cast<float>(_e8), 0.0), 1.0 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-interpolate.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct FragmentInput {\n    metal::float4 position;\n    uint _flat;\n    uint flat_first;\n    uint flat_either;\n    float _linear;\n    metal::float2 linear_centroid;\n    char _pad6[8];\n    metal::float3 linear_sample;\n    metal::float3 linear_center;\n    metal::float4 perspective;\n    float perspective_centroid;\n    float perspective_sample;\n    float perspective_center;\n    char _pad12[4];\n};\n\nstruct vert_mainOutput {\n    metal::float4 position [[position]];\n    uint _flat [[user(loc0), flat]];\n    uint flat_first [[user(loc1), flat]];\n    uint flat_either [[user(loc2), flat]];\n    float _linear [[user(loc3), center_no_perspective]];\n    metal::float2 linear_centroid [[user(loc4), centroid_no_perspective]];\n    metal::float3 linear_sample [[user(loc6), sample_no_perspective]];\n    metal::float3 linear_center [[user(loc7), center_no_perspective]];\n    metal::float4 perspective [[user(loc8), center_perspective]];\n    float perspective_centroid [[user(loc9), centroid_perspective]];\n    float perspective_sample [[user(loc10), sample_perspective]];\n    float perspective_center [[user(loc11), center_perspective]];\n};\nvertex vert_mainOutput vert_main(\n) {\n    FragmentInput out = {};\n    out.position = metal::float4(2.0, 4.0, 5.0, 6.0);\n    out._flat = 8u;\n    out.flat_first = 9u;\n    out.flat_either = 10u;\n    out._linear = 27.0;\n    out.linear_centroid = metal::float2(64.0, 125.0);\n    out.linear_sample = metal::float3(216.0, 343.0, 512.0);\n    out.linear_center = metal::float3(255.0, 511.0, 1024.0);\n    out.perspective = metal::float4(729.0, 1000.0, 1331.0, 1728.0);\n    out.perspective_centroid = 2197.0;\n    out.perspective_sample = 2744.0;\n    out.perspective_center = 2812.0;\n    FragmentInput _e41 = out;\n    const auto _tmp = _e41;\n    return vert_mainOutput { _tmp.position, _tmp._flat, _tmp.flat_first, _tmp.flat_either, _tmp._linear, _tmp.linear_centroid, _tmp.linear_sample, _tmp.linear_center, _tmp.perspective, _tmp.perspective_centroid, _tmp.perspective_sample, _tmp.perspective_center };\n}\n\n\nstruct frag_mainInput {\n    uint _flat [[user(loc0), flat]];\n    uint flat_first [[user(loc1), flat]];\n    uint flat_either [[user(loc2), flat]];\n    float _linear [[user(loc3), center_no_perspective]];\n    metal::float2 linear_centroid [[user(loc4), centroid_no_perspective]];\n    metal::float3 linear_sample [[user(loc6), sample_no_perspective]];\n    metal::float3 linear_center [[user(loc7), center_no_perspective]];\n    metal::float4 perspective [[user(loc8), center_perspective]];\n    float perspective_centroid [[user(loc9), centroid_perspective]];\n    float perspective_sample [[user(loc10), sample_perspective]];\n    float perspective_center [[user(loc11), center_perspective]];\n};\nfragment void frag_main(\n  frag_mainInput varyings_1 [[stage_in]]\n, metal::float4 position [[position]]\n) {\n    const FragmentInput val = { position, varyings_1._flat, varyings_1.flat_first, varyings_1.flat_either, varyings_1._linear, varyings_1.linear_centroid, {}, varyings_1.linear_sample, varyings_1.linear_center, varyings_1.perspective, varyings_1.perspective_centroid, varyings_1.perspective_sample, varyings_1.perspective_center };\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-interpolate_compat.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct FragmentInput {\n    metal::float4 position;\n    uint _flat;\n    uint flat_either;\n    float _linear;\n    char _pad4[4];\n    metal::float2 linear_centroid;\n    char _pad5[8];\n    metal::float3 linear_sample;\n    metal::float3 linear_center;\n    metal::float4 perspective;\n    float perspective_centroid;\n    float perspective_sample;\n    float perspective_center;\n    char _pad11[4];\n};\n\nstruct vert_mainOutput {\n    metal::float4 position [[position]];\n    uint _flat [[user(loc0), flat]];\n    uint flat_either [[user(loc2), flat]];\n    float _linear [[user(loc3), center_no_perspective]];\n    metal::float2 linear_centroid [[user(loc4), centroid_no_perspective]];\n    metal::float3 linear_sample [[user(loc6), sample_no_perspective]];\n    metal::float3 linear_center [[user(loc7), center_no_perspective]];\n    metal::float4 perspective [[user(loc8), center_perspective]];\n    float perspective_centroid [[user(loc9), centroid_perspective]];\n    float perspective_sample [[user(loc10), sample_perspective]];\n    float perspective_center [[user(loc11), center_perspective]];\n};\nvertex vert_mainOutput vert_main(\n) {\n    FragmentInput out = {};\n    out.position = metal::float4(2.0, 4.0, 5.0, 6.0);\n    out._flat = 8u;\n    out.flat_either = 10u;\n    out._linear = 27.0;\n    out.linear_centroid = metal::float2(64.0, 125.0);\n    out.linear_sample = metal::float3(216.0, 343.0, 512.0);\n    out.linear_center = metal::float3(255.0, 511.0, 1024.0);\n    out.perspective = metal::float4(729.0, 1000.0, 1331.0, 1728.0);\n    out.perspective_centroid = 2197.0;\n    out.perspective_sample = 2744.0;\n    out.perspective_center = 2812.0;\n    FragmentInput _e39 = out;\n    const auto _tmp = _e39;\n    return vert_mainOutput { _tmp.position, _tmp._flat, _tmp.flat_either, _tmp._linear, _tmp.linear_centroid, _tmp.linear_sample, _tmp.linear_center, _tmp.perspective, _tmp.perspective_centroid, _tmp.perspective_sample, _tmp.perspective_center };\n}\n\n\nstruct frag_mainInput {\n    uint _flat [[user(loc0), flat]];\n    uint flat_either [[user(loc2), flat]];\n    float _linear [[user(loc3), center_no_perspective]];\n    metal::float2 linear_centroid [[user(loc4), centroid_no_perspective]];\n    metal::float3 linear_sample [[user(loc6), sample_no_perspective]];\n    metal::float3 linear_center [[user(loc7), center_no_perspective]];\n    metal::float4 perspective [[user(loc8), center_perspective]];\n    float perspective_centroid [[user(loc9), centroid_perspective]];\n    float perspective_sample [[user(loc10), sample_perspective]];\n    float perspective_center [[user(loc11), center_perspective]];\n};\nfragment void frag_main(\n  frag_mainInput varyings_1 [[stage_in]]\n, metal::float4 position [[position]]\n) {\n    const FragmentInput val = { position, varyings_1._flat, varyings_1.flat_either, varyings_1._linear, {}, varyings_1.linear_centroid, {}, varyings_1.linear_sample, varyings_1.linear_center, varyings_1.perspective, varyings_1.perspective_centroid, varyings_1.perspective_sample, varyings_1.perspective_center };\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-math-functions.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _modf_result_f32_ {\n    float fract;\n    float whole;\n};\nstruct _modf_result_vec2_f32_ {\n    metal::float2 fract;\n    metal::float2 whole;\n};\nstruct _modf_result_vec4_f32_ {\n    metal::float4 fract;\n    metal::float4 whole;\n};\nstruct _frexp_result_f32_ {\n    float fract;\n    int exp;\n};\nstruct _frexp_result_vec4_f32_ {\n    metal::float4 fract;\n    metal::int4 exp;\n};\n\n_modf_result_f32_ naga_modf(float arg) {\n    float other;\n    float fract = metal::modf(arg, other);\n    return _modf_result_f32_{ fract, other };\n}\n\n_modf_result_vec2_f32_ naga_modf(metal::float2 arg) {\n    metal::float2 other;\n    metal::float2 fract = metal::modf(arg, other);\n    return _modf_result_vec2_f32_{ fract, other };\n}\n\n_modf_result_vec4_f32_ naga_modf(metal::float4 arg) {\n    metal::float4 other;\n    metal::float4 fract = metal::modf(arg, other);\n    return _modf_result_vec4_f32_{ fract, other };\n}\n\n_frexp_result_f32_ naga_frexp(float arg) {\n    int other;\n    float fract = metal::frexp(arg, other);\n    return _frexp_result_f32_{ fract, other };\n}\n\n_frexp_result_vec4_f32_ naga_frexp(metal::float4 arg) {\n    int4 other;\n    metal::float4 fract = metal::frexp(arg, other);\n    return _frexp_result_vec4_f32_{ fract, other };\n}\n\nfragment void main_(\n) {\n    metal::float4 v = metal::float4(0.0);\n    float a = ((1.0) * 57.295779513082322865);\n    float b = ((1.0) * 0.017453292519943295474);\n    metal::float4 c = ((v) * 57.295779513082322865);\n    metal::float4 d = ((v) * 0.017453292519943295474);\n    metal::float4 e = metal::saturate(v);\n    metal::float4 g = metal::refract(v, v, 1.0);\n    metal::int4 sign_b = metal::int4(-1, -1, -1, -1);\n    metal::float4 sign_d = metal::float4(-1.0, -1.0, -1.0, -1.0);\n    metal::float4 sign_e = metal::float4(0.0, 0.0, 0.0, 0.0);\n    metal::int2 flb_b = metal::int2(-1, -1);\n    metal::uint2 flb_c = metal::uint2(0u, 0u);\n    metal::int2 ftb_c = metal::int2(0, 0);\n    metal::uint2 ftb_d = metal::uint2(0u, 0u);\n    metal::uint2 ctz_e = metal::uint2(32u, 32u);\n    metal::int2 ctz_f = metal::int2(32, 32);\n    metal::uint2 ctz_g = metal::uint2(0u, 0u);\n    metal::int2 ctz_h = metal::int2(0, 0);\n    metal::int2 clz_c = metal::int2(0, 0);\n    metal::uint2 clz_d = metal::uint2(31u, 31u);\n    float lde_a = metal::ldexp(1.0, 2);\n    metal::float2 lde_b = metal::ldexp(metal::float2(1.0, 2.0), metal::int2(3, 4));\n    _modf_result_f32_ modf_a = naga_modf(1.5);\n    float modf_b = naga_modf(1.5).fract;\n    float modf_c = naga_modf(1.5).whole;\n    _modf_result_vec2_f32_ modf_d = naga_modf(metal::float2(1.5, 1.5));\n    float modf_e = naga_modf(metal::float4(1.5, 1.5, 1.5, 1.5)).whole.x;\n    float modf_f = naga_modf(metal::float2(1.5, 1.5)).fract.y;\n    _frexp_result_f32_ frexp_a = naga_frexp(1.5);\n    float frexp_b = naga_frexp(1.5).fract;\n    int frexp_c = naga_frexp(1.5).exp;\n    int frexp_d = naga_frexp(metal::float4(1.5, 1.5, 1.5, 1.5)).exp.x;\n    float quantizeToF16_a = float(half(1.0));\n    metal::float2 quantizeToF16_b = metal::float2(metal::half2(metal::float2(1.0, 1.0)));\n    metal::float3 quantizeToF16_c = metal::float3(metal::half3(metal::float3(1.0, 1.0, 1.0)));\n    metal::float4 quantizeToF16_d = metal::float4(metal::half4(metal::float4(1.0, 1.0, 1.0, 1.0)));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-memory-decorations-coherent.metal",
    "content": "// language: metal3.2\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size0;\n    uint size1;\n};\n\ntypedef uint type_1[1];\nstruct Data {\n    type_1 values;\n};\n\nkernel void main_(\n  coherent device Data& coherent_buf [[user(fake0)]]\n, device Data const& plain_buf [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    uint _e6 = plain_buf.values[0];\n    coherent_buf.values[0] = _e6;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-varyings.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct Vertex {\n    metal::float2 position;\n};\nstruct NoteInstance {\n    metal::float2 position;\n};\nstruct VertexOutput {\n    metal::float4 position;\n};\n\nstruct vs_mainInput {\n    metal::float2 position [[attribute(0)]];\n    metal::float2 position_1 [[attribute(1)]];\n};\nstruct vs_mainOutput {\n    metal::float4 position [[position]];\n};\nvertex vs_mainOutput vs_main(\n  vs_mainInput varyings [[stage_in]]\n) {\n    const Vertex vertex_ = { varyings.position };\n    const NoteInstance note = { varyings.position_1 };\n    VertexOutput out = {};\n    VertexOutput _e3 = out;\n    const auto _tmp = _e3;\n    return vs_mainOutput { _tmp.position };\n}\n\n\nstruct fs_mainInput {\n    metal::float2 position [[user(loc1), center_perspective]];\n};\nstruct fs_mainOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment fs_mainOutput fs_main(\n  fs_mainInput varyings_1 [[stage_in]]\n, metal::float4 position [[position]]\n) {\n    const VertexOutput in = { position };\n    const NoteInstance note_1 = { varyings_1.position };\n    metal::float3 position_1 = metal::float3(1.0);\n    return fs_mainOutput { in.position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-vpt-formats-x1.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint buffer_size1;\n};\n\nstruct VertexOutput {\n    metal::float4 position;\n};\nstruct VertexInput {\n    uint v_uint8_;\n    uint v_uint8x2_;\n    uint v_uint8x4_;\n    int v_sint8_;\n    int v_sint8x2_;\n    int v_sint8x4_;\n    float v_unorm8_;\n    float v_unorm8x2_;\n    float v_unorm8x4_;\n    float v_snorm8_;\n    float v_snorm8x2_;\n    float v_snorm8x4_;\n    uint v_uint16_;\n    uint v_uint16x2_;\n    uint v_uint16x4_;\n    int v_sint16_;\n    int v_sint16x2_;\n    int v_sint16x4_;\n    float v_unorm16_;\n    float v_unorm16x2_;\n    float v_unorm16x4_;\n    float v_snorm16_;\n    float v_snorm16x2_;\n    float v_snorm16x4_;\n    float v_float16_;\n    float v_float16x2_;\n    float v_float16x4_;\n    float v_float32_;\n    float v_float32x2_;\n    float v_float32x3_;\n    float v_float32x4_;\n    uint v_uint32_;\n    uint v_uint32x2_;\n    uint v_uint32x3_;\n    uint v_uint32x4_;\n    int v_sint32_;\n    int v_sint32x2_;\n    int v_sint32x3_;\n    int v_sint32x4_;\n    float v_unorm10_10_10_2_;\n    float v_unorm8x4_bgra;\n    half v_float16_as_f16_;\n    half v_float16x2_as_f16_;\n    half v_float16x4_as_f16_;\n    char _pad44[2];\n};\nuint unpackUint8_(metal::uchar b0) {\n    return uint(b0);\n}\nmetal::uint2 unpackUint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::uint2(b0, b1);\n}\nmetal::uint4 unpackUint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::uint4(b0, b1, b2, b3);\n}\nint unpackSint8_(metal::uchar b0) {\n    return int(as_type<char>(b0));\n}\nmetal::int2 unpackSint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::int2(as_type<char>(b0), as_type<char>(b1));\n}\nmetal::int4 unpackSint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::int4(as_type<char>(b0), as_type<char>(b1), as_type<char>(b2), as_type<char>(b3));\n}\nfloat unpackUnorm8_(metal::uchar b0) {\n    return float(float(b0) / 255.0f);\n}\nmetal::float2 unpackUnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(float(b0) / 255.0f, float(b1) / 255.0f);\n}\nmetal::float4 unpackUnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b0) / 255.0f, float(b1) / 255.0f, float(b2) / 255.0f, float(b3) / 255.0f);\n}\nfloat unpackSnorm8_(metal::uchar b0) {\n    return float(metal::max(-1.0f, as_type<char>(b0) / 127.0f));\n}\nmetal::float2 unpackSnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f));\n}\nmetal::float4 unpackSnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f), metal::max(-1.0f, as_type<char>(b2) / 127.0f), metal::max(-1.0f, as_type<char>(b3) / 127.0f));\n}\nmetal::uint unpackUint16_(metal::uint b0, metal::uint b1) {\n    return metal::uint(b1 << 8 | b0);\n}\nmetal::uint2 unpackUint16x2_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3) {\n    return metal::uint2(b1 << 8 | b0, b3 << 8 | b2);\n}\nmetal::uint4 unpackUint16x4_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3, metal::uint b4, metal::uint b5, metal::uint b6, metal::uint b7) {\n    return metal::uint4(b1 << 8 | b0, b3 << 8 | b2, b5 << 8 | b4, b7 << 8 | b6);\n}\nint unpackSint16_(metal::ushort b0, metal::ushort b1) {\n    return int(as_type<short>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::int2 unpackSint16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::int4 unpackSint16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)), as_type<short>(metal::ushort(b5 << 8 | b4)), as_type<short>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackUnorm16_(metal::ushort b0, metal::ushort b1) {\n    return float(float(b1 << 8 | b0) / 65535.0f);\n}\nmetal::float2 unpackUnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f);\n}\nmetal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f, float(b5 << 8 | b4) / 65535.0f, float(b7 << 8 | b6) / 65535.0f);\n}\nfloat unpackSnorm16_(metal::ushort b0, metal::ushort b1) {\n    return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x;\n}\nmetal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    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));\n}\nfloat unpackFloat16_(metal::ushort b0, metal::ushort b1) {\n    return float(as_type<half>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::float2 unpackFloat16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::float4 unpackFloat16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)), as_type<half>(metal::ushort(b5 << 8 | b4)), as_type<half>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackFloat32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float2 unpackFloat32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::float3 unpackFloat32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::float3(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::float4 unpackFloat32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nuint unpackUint32_(uint b0, uint b1, uint b2, uint b3) {\n    return (b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nuint2 unpackUint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return uint2((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nuint3 unpackUint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return uint3((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::uint4 unpackUint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::uint4((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8), (b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nint unpackSint32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::int2 unpackSint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::int2(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::int3 unpackSint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::int3(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::int4 unpackSint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::int4(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<int>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nmetal::float4 unpackUnorm10_10_10_2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackUnorm8x4Bgra(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b2) / 255.0f, float(b1) / 255.0f, float(b0) / 255.0f, float(b3) / 255.0f);\n}\n\nstruct render_vertexOutput {\n    metal::float4 position [[position]];\n};\nstruct vb_1_type { metal::uchar data[704]; };\nvertex render_vertexOutput render_vertex(\n  uint v_id [[vertex_id]]\n, const device vb_1_type* vb_1_in [[buffer(1)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    uint v_uint8_ = {};\n    uint v_uint8x2_ = {};\n    uint v_uint8x4_ = {};\n    int v_sint8_ = {};\n    int v_sint8x2_ = {};\n    int v_sint8x4_ = {};\n    float v_unorm8_ = {};\n    float v_unorm8x2_ = {};\n    float v_unorm8x4_ = {};\n    float v_snorm8_ = {};\n    float v_snorm8x2_ = {};\n    float v_snorm8x4_ = {};\n    uint v_uint16_ = {};\n    uint v_uint16x2_ = {};\n    uint v_uint16x4_ = {};\n    int v_sint16_ = {};\n    int v_sint16x2_ = {};\n    int v_sint16x4_ = {};\n    float v_unorm16_ = {};\n    float v_unorm16x2_ = {};\n    float v_unorm16x4_ = {};\n    float v_snorm16_ = {};\n    float v_snorm16x2_ = {};\n    float v_snorm16x4_ = {};\n    float v_float16_ = {};\n    float v_float16x2_ = {};\n    float v_float16x4_ = {};\n    float v_float32_ = {};\n    float v_float32x2_ = {};\n    float v_float32x3_ = {};\n    float v_float32x4_ = {};\n    uint v_uint32_ = {};\n    uint v_uint32x2_ = {};\n    uint v_uint32x3_ = {};\n    uint v_uint32x4_ = {};\n    int v_sint32_ = {};\n    int v_sint32x2_ = {};\n    int v_sint32x3_ = {};\n    int v_sint32x4_ = {};\n    float v_unorm10_10_10_2_ = {};\n    float v_unorm8x4_bgra = {};\n    half v_float16_as_f16_ = {};\n    half v_float16x2_as_f16_ = {};\n    half v_float16x4_as_f16_ = {};\n    if (v_id < (_buffer_sizes.buffer_size1 / 704)) {\n        const vb_1_type vb_1_elem = vb_1_in[v_id];\n        v_uint8_ = unpackUint8_(vb_1_elem.data[0]);\n        // uint <- Uint8x2\n        v_uint8x2_ = unpackUint8x2_(vb_1_elem.data[16], vb_1_elem.data[17]).x;\n        // uint <- Uint8x4\n        v_uint8x4_ = unpackUint8x4_(vb_1_elem.data[32], vb_1_elem.data[33], vb_1_elem.data[34], vb_1_elem.data[35]).x;\n        v_sint8_ = unpackSint8_(vb_1_elem.data[48]);\n        // int <- Sint8x2\n        v_sint8x2_ = unpackSint8x2_(vb_1_elem.data[64], vb_1_elem.data[65]).x;\n        // int <- Sint8x4\n        v_sint8x4_ = unpackSint8x4_(vb_1_elem.data[80], vb_1_elem.data[81], vb_1_elem.data[82], vb_1_elem.data[83]).x;\n        v_unorm8_ = unpackUnorm8_(vb_1_elem.data[96]);\n        // float <- Unorm8x2\n        v_unorm8x2_ = unpackUnorm8x2_(vb_1_elem.data[112], vb_1_elem.data[113]).x;\n        // float <- Unorm8x4\n        v_unorm8x4_ = unpackUnorm8x4_(vb_1_elem.data[128], vb_1_elem.data[129], vb_1_elem.data[130], vb_1_elem.data[131]).x;\n        v_snorm8_ = unpackSnorm8_(vb_1_elem.data[144]);\n        // float <- Snorm8x2\n        v_snorm8x2_ = unpackSnorm8x2_(vb_1_elem.data[160], vb_1_elem.data[161]).x;\n        // float <- Snorm8x4\n        v_snorm8x4_ = unpackSnorm8x4_(vb_1_elem.data[176], vb_1_elem.data[177], vb_1_elem.data[178], vb_1_elem.data[179]).x;\n        v_uint16_ = unpackUint16_(vb_1_elem.data[192], vb_1_elem.data[193]);\n        // uint <- Uint16x2\n        v_uint16x2_ = unpackUint16x2_(vb_1_elem.data[208], vb_1_elem.data[209], vb_1_elem.data[210], vb_1_elem.data[211]).x;\n        // uint <- Uint16x4\n        v_uint16x4_ = unpackUint16x4_(vb_1_elem.data[224], vb_1_elem.data[225], vb_1_elem.data[226], vb_1_elem.data[227], vb_1_elem.data[228], vb_1_elem.data[229], vb_1_elem.data[230], vb_1_elem.data[231]).x;\n        v_sint16_ = unpackSint16_(vb_1_elem.data[240], vb_1_elem.data[241]);\n        // int <- Sint16x2\n        v_sint16x2_ = unpackSint16x2_(vb_1_elem.data[256], vb_1_elem.data[257], vb_1_elem.data[258], vb_1_elem.data[259]).x;\n        // int <- Sint16x4\n        v_sint16x4_ = unpackSint16x4_(vb_1_elem.data[272], vb_1_elem.data[273], vb_1_elem.data[274], vb_1_elem.data[275], vb_1_elem.data[276], vb_1_elem.data[277], vb_1_elem.data[278], vb_1_elem.data[279]).x;\n        v_unorm16_ = unpackUnorm16_(vb_1_elem.data[288], vb_1_elem.data[289]);\n        // float <- Unorm16x2\n        v_unorm16x2_ = unpackUnorm16x2_(vb_1_elem.data[304], vb_1_elem.data[305], vb_1_elem.data[306], vb_1_elem.data[307]).x;\n        // float <- Unorm16x4\n        v_unorm16x4_ = unpackUnorm16x4_(vb_1_elem.data[320], vb_1_elem.data[321], vb_1_elem.data[322], vb_1_elem.data[323], vb_1_elem.data[324], vb_1_elem.data[325], vb_1_elem.data[326], vb_1_elem.data[327]).x;\n        v_snorm16_ = unpackSnorm16_(vb_1_elem.data[336], vb_1_elem.data[337]);\n        // float <- Snorm16x2\n        v_snorm16x2_ = unpackSnorm16x2_(vb_1_elem.data[352], vb_1_elem.data[353], vb_1_elem.data[354], vb_1_elem.data[355]).x;\n        // float <- Snorm16x4\n        v_snorm16x4_ = unpackSnorm16x4_(vb_1_elem.data[368], vb_1_elem.data[369], vb_1_elem.data[370], vb_1_elem.data[371], vb_1_elem.data[372], vb_1_elem.data[373], vb_1_elem.data[374], vb_1_elem.data[375]).x;\n        v_float16_ = unpackFloat16_(vb_1_elem.data[384], vb_1_elem.data[385]);\n        // float <- Float16x2\n        v_float16x2_ = unpackFloat16x2_(vb_1_elem.data[400], vb_1_elem.data[401], vb_1_elem.data[402], vb_1_elem.data[403]).x;\n        // float <- Float16x4\n        v_float16x4_ = unpackFloat16x4_(vb_1_elem.data[416], vb_1_elem.data[417], vb_1_elem.data[418], vb_1_elem.data[419], vb_1_elem.data[420], vb_1_elem.data[421], vb_1_elem.data[422], vb_1_elem.data[423]).x;\n        v_float32_ = unpackFloat32_(vb_1_elem.data[432], vb_1_elem.data[433], vb_1_elem.data[434], vb_1_elem.data[435]);\n        // float <- Float32x2\n        v_float32x2_ = unpackFloat32x2_(vb_1_elem.data[448], vb_1_elem.data[449], vb_1_elem.data[450], vb_1_elem.data[451], vb_1_elem.data[452], vb_1_elem.data[453], vb_1_elem.data[454], vb_1_elem.data[455]).x;\n        // float <- Float32x3\n        v_float32x3_ = unpackFloat32x3_(vb_1_elem.data[464], vb_1_elem.data[465], vb_1_elem.data[466], vb_1_elem.data[467], vb_1_elem.data[468], vb_1_elem.data[469], vb_1_elem.data[470], vb_1_elem.data[471], vb_1_elem.data[472], vb_1_elem.data[473], vb_1_elem.data[474], vb_1_elem.data[475]).x;\n        // float <- Float32x4\n        v_float32x4_ = unpackFloat32x4_(vb_1_elem.data[480], vb_1_elem.data[481], vb_1_elem.data[482], vb_1_elem.data[483], vb_1_elem.data[484], vb_1_elem.data[485], vb_1_elem.data[486], vb_1_elem.data[487], vb_1_elem.data[488], vb_1_elem.data[489], vb_1_elem.data[490], vb_1_elem.data[491], vb_1_elem.data[492], vb_1_elem.data[493], vb_1_elem.data[494], vb_1_elem.data[495]).x;\n        v_uint32_ = unpackUint32_(vb_1_elem.data[496], vb_1_elem.data[497], vb_1_elem.data[498], vb_1_elem.data[499]);\n        // uint <- Uint32x2\n        v_uint32x2_ = unpackUint32x2_(vb_1_elem.data[512], vb_1_elem.data[513], vb_1_elem.data[514], vb_1_elem.data[515], vb_1_elem.data[516], vb_1_elem.data[517], vb_1_elem.data[518], vb_1_elem.data[519]).x;\n        // uint <- Uint32x3\n        v_uint32x3_ = unpackUint32x3_(vb_1_elem.data[528], vb_1_elem.data[529], vb_1_elem.data[530], vb_1_elem.data[531], vb_1_elem.data[532], vb_1_elem.data[533], vb_1_elem.data[534], vb_1_elem.data[535], vb_1_elem.data[536], vb_1_elem.data[537], vb_1_elem.data[538], vb_1_elem.data[539]).x;\n        // uint <- Uint32x4\n        v_uint32x4_ = unpackUint32x4_(vb_1_elem.data[544], vb_1_elem.data[545], vb_1_elem.data[546], vb_1_elem.data[547], vb_1_elem.data[548], vb_1_elem.data[549], vb_1_elem.data[550], vb_1_elem.data[551], vb_1_elem.data[552], vb_1_elem.data[553], vb_1_elem.data[554], vb_1_elem.data[555], vb_1_elem.data[556], vb_1_elem.data[557], vb_1_elem.data[558], vb_1_elem.data[559]).x;\n        v_sint32_ = unpackSint32_(vb_1_elem.data[560], vb_1_elem.data[561], vb_1_elem.data[562], vb_1_elem.data[563]);\n        // int <- Sint32x2\n        v_sint32x2_ = unpackSint32x2_(vb_1_elem.data[576], vb_1_elem.data[577], vb_1_elem.data[578], vb_1_elem.data[579], vb_1_elem.data[580], vb_1_elem.data[581], vb_1_elem.data[582], vb_1_elem.data[583]).x;\n        // int <- Sint32x3\n        v_sint32x3_ = unpackSint32x3_(vb_1_elem.data[592], vb_1_elem.data[593], vb_1_elem.data[594], vb_1_elem.data[595], vb_1_elem.data[596], vb_1_elem.data[597], vb_1_elem.data[598], vb_1_elem.data[599], vb_1_elem.data[600], vb_1_elem.data[601], vb_1_elem.data[602], vb_1_elem.data[603]).x;\n        // int <- Sint32x4\n        v_sint32x4_ = unpackSint32x4_(vb_1_elem.data[608], vb_1_elem.data[609], vb_1_elem.data[610], vb_1_elem.data[611], vb_1_elem.data[612], vb_1_elem.data[613], vb_1_elem.data[614], vb_1_elem.data[615], vb_1_elem.data[616], vb_1_elem.data[617], vb_1_elem.data[618], vb_1_elem.data[619], vb_1_elem.data[620], vb_1_elem.data[621], vb_1_elem.data[622], vb_1_elem.data[623]).x;\n        // float <- Unorm10_10_10_2\n        v_unorm10_10_10_2_ = unpackUnorm10_10_10_2_(vb_1_elem.data[624], vb_1_elem.data[625], vb_1_elem.data[626], vb_1_elem.data[627]).x;\n        // float <- Unorm8x4Bgra\n        v_unorm8x4_bgra = unpackUnorm8x4Bgra(vb_1_elem.data[640], vb_1_elem.data[641], vb_1_elem.data[642], vb_1_elem.data[643]).x;\n        v_float16_as_f16_ = half(unpackFloat16_(vb_1_elem.data[656], vb_1_elem.data[657]));\n        // half <- Float16x2\n        v_float16x2_as_f16_ = metal::half2(unpackFloat16x2_(vb_1_elem.data[672], vb_1_elem.data[673], vb_1_elem.data[674], vb_1_elem.data[675])).x;\n        // half <- Float16x4\n        v_float16x4_as_f16_ = metal::half4(unpackFloat16x4_(vb_1_elem.data[688], vb_1_elem.data[689], vb_1_elem.data[690], vb_1_elem.data[691], vb_1_elem.data[692], vb_1_elem.data[693], vb_1_elem.data[694], vb_1_elem.data[695])).x;\n    }\n    const VertexInput v_in = { v_uint8_, v_uint8x2_, v_uint8x4_, v_sint8_, v_sint8x2_, v_sint8x4_, v_unorm8_, v_unorm8x2_, v_unorm8x4_, v_snorm8_, v_snorm8x2_, v_snorm8x4_, v_uint16_, v_uint16x2_, v_uint16x4_, v_sint16_, v_sint16x2_, v_sint16x4_, v_unorm16_, v_unorm16x2_, v_unorm16x4_, v_snorm16_, v_snorm16x2_, v_snorm16x4_, v_float16_, v_float16x2_, v_float16x4_, v_float32_, v_float32x2_, v_float32x3_, v_float32x4_, v_uint32_, v_uint32x2_, v_uint32x3_, v_uint32x4_, v_sint32_, v_sint32x2_, v_sint32x3_, v_sint32x4_, v_unorm10_10_10_2_, v_unorm8x4_bgra, v_float16_as_f16_, v_float16x2_as_f16_, v_float16x4_as_f16_ };\n    const auto _tmp = VertexOutput {metal::float4(v_in.v_float32_)};\n    return render_vertexOutput { _tmp.position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-vpt-formats-x2.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint buffer_size1;\n};\n\nstruct VertexOutput {\n    metal::float4 position;\n};\nstruct VertexInput {\n    metal::uint2 v_uint8_;\n    metal::uint2 v_uint8x2_;\n    metal::uint2 v_uint8x4_;\n    metal::int2 v_sint8_;\n    metal::int2 v_sint8x2_;\n    metal::int2 v_sint8x4_;\n    metal::float2 v_unorm8_;\n    metal::float2 v_unorm8x2_;\n    metal::float2 v_unorm8x4_;\n    metal::float2 v_snorm8_;\n    metal::float2 v_snorm8x2_;\n    metal::float2 v_snorm8x4_;\n    metal::uint2 v_uint16_;\n    metal::uint2 v_uint16x2_;\n    metal::uint2 v_uint16x4_;\n    metal::int2 v_sint16_;\n    metal::int2 v_sint16x2_;\n    metal::int2 v_sint16x4_;\n    metal::float2 v_unorm16_;\n    metal::float2 v_unorm16x2_;\n    metal::float2 v_unorm16x4_;\n    metal::float2 v_snorm16_;\n    metal::float2 v_snorm16x2_;\n    metal::float2 v_snorm16x4_;\n    metal::float2 v_float16_;\n    metal::float2 v_float16x2_;\n    metal::float2 v_float16x4_;\n    metal::float2 v_float32_;\n    metal::float2 v_float32x2_;\n    metal::float2 v_float32x3_;\n    metal::float2 v_float32x4_;\n    metal::uint2 v_uint32_;\n    metal::uint2 v_uint32x2_;\n    metal::uint2 v_uint32x3_;\n    metal::uint2 v_uint32x4_;\n    metal::int2 v_sint32_;\n    metal::int2 v_sint32x2_;\n    metal::int2 v_sint32x3_;\n    metal::int2 v_sint32x4_;\n    metal::float2 v_unorm10_10_10_2_;\n    metal::float2 v_unorm8x4_bgra;\n    metal::half2 v_float16_as_f16_;\n    metal::half2 v_float16x2_as_f16_;\n    metal::half2 v_float16x4_as_f16_;\n    char _pad44[4];\n};\nuint unpackUint8_(metal::uchar b0) {\n    return uint(b0);\n}\nmetal::uint2 unpackUint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::uint2(b0, b1);\n}\nmetal::uint4 unpackUint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::uint4(b0, b1, b2, b3);\n}\nint unpackSint8_(metal::uchar b0) {\n    return int(as_type<char>(b0));\n}\nmetal::int2 unpackSint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::int2(as_type<char>(b0), as_type<char>(b1));\n}\nmetal::int4 unpackSint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::int4(as_type<char>(b0), as_type<char>(b1), as_type<char>(b2), as_type<char>(b3));\n}\nfloat unpackUnorm8_(metal::uchar b0) {\n    return float(float(b0) / 255.0f);\n}\nmetal::float2 unpackUnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(float(b0) / 255.0f, float(b1) / 255.0f);\n}\nmetal::float4 unpackUnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b0) / 255.0f, float(b1) / 255.0f, float(b2) / 255.0f, float(b3) / 255.0f);\n}\nfloat unpackSnorm8_(metal::uchar b0) {\n    return float(metal::max(-1.0f, as_type<char>(b0) / 127.0f));\n}\nmetal::float2 unpackSnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f));\n}\nmetal::float4 unpackSnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f), metal::max(-1.0f, as_type<char>(b2) / 127.0f), metal::max(-1.0f, as_type<char>(b3) / 127.0f));\n}\nmetal::uint unpackUint16_(metal::uint b0, metal::uint b1) {\n    return metal::uint(b1 << 8 | b0);\n}\nmetal::uint2 unpackUint16x2_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3) {\n    return metal::uint2(b1 << 8 | b0, b3 << 8 | b2);\n}\nmetal::uint4 unpackUint16x4_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3, metal::uint b4, metal::uint b5, metal::uint b6, metal::uint b7) {\n    return metal::uint4(b1 << 8 | b0, b3 << 8 | b2, b5 << 8 | b4, b7 << 8 | b6);\n}\nint unpackSint16_(metal::ushort b0, metal::ushort b1) {\n    return int(as_type<short>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::int2 unpackSint16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::int4 unpackSint16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)), as_type<short>(metal::ushort(b5 << 8 | b4)), as_type<short>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackUnorm16_(metal::ushort b0, metal::ushort b1) {\n    return float(float(b1 << 8 | b0) / 65535.0f);\n}\nmetal::float2 unpackUnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f);\n}\nmetal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f, float(b5 << 8 | b4) / 65535.0f, float(b7 << 8 | b6) / 65535.0f);\n}\nfloat unpackSnorm16_(metal::ushort b0, metal::ushort b1) {\n    return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x;\n}\nmetal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    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));\n}\nfloat unpackFloat16_(metal::ushort b0, metal::ushort b1) {\n    return float(as_type<half>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::float2 unpackFloat16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::float4 unpackFloat16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)), as_type<half>(metal::ushort(b5 << 8 | b4)), as_type<half>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackFloat32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float2 unpackFloat32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::float3 unpackFloat32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::float3(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::float4 unpackFloat32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nuint unpackUint32_(uint b0, uint b1, uint b2, uint b3) {\n    return (b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nuint2 unpackUint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return uint2((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nuint3 unpackUint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return uint3((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::uint4 unpackUint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::uint4((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8), (b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nint unpackSint32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::int2 unpackSint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::int2(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::int3 unpackSint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::int3(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::int4 unpackSint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::int4(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<int>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nmetal::float4 unpackUnorm10_10_10_2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackUnorm8x4Bgra(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b2) / 255.0f, float(b1) / 255.0f, float(b0) / 255.0f, float(b3) / 255.0f);\n}\n\nstruct render_vertexOutput {\n    metal::float4 position [[position]];\n};\nstruct vb_1_type { metal::uchar data[704]; };\nvertex render_vertexOutput render_vertex(\n  uint v_id [[vertex_id]]\n, const device vb_1_type* vb_1_in [[buffer(1)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    metal::uint2 v_uint8_ = {};\n    metal::uint2 v_uint8x2_ = {};\n    metal::uint2 v_uint8x4_ = {};\n    metal::int2 v_sint8_ = {};\n    metal::int2 v_sint8x2_ = {};\n    metal::int2 v_sint8x4_ = {};\n    metal::float2 v_unorm8_ = {};\n    metal::float2 v_unorm8x2_ = {};\n    metal::float2 v_unorm8x4_ = {};\n    metal::float2 v_snorm8_ = {};\n    metal::float2 v_snorm8x2_ = {};\n    metal::float2 v_snorm8x4_ = {};\n    metal::uint2 v_uint16_ = {};\n    metal::uint2 v_uint16x2_ = {};\n    metal::uint2 v_uint16x4_ = {};\n    metal::int2 v_sint16_ = {};\n    metal::int2 v_sint16x2_ = {};\n    metal::int2 v_sint16x4_ = {};\n    metal::float2 v_unorm16_ = {};\n    metal::float2 v_unorm16x2_ = {};\n    metal::float2 v_unorm16x4_ = {};\n    metal::float2 v_snorm16_ = {};\n    metal::float2 v_snorm16x2_ = {};\n    metal::float2 v_snorm16x4_ = {};\n    metal::float2 v_float16_ = {};\n    metal::float2 v_float16x2_ = {};\n    metal::float2 v_float16x4_ = {};\n    metal::float2 v_float32_ = {};\n    metal::float2 v_float32x2_ = {};\n    metal::float2 v_float32x3_ = {};\n    metal::float2 v_float32x4_ = {};\n    metal::uint2 v_uint32_ = {};\n    metal::uint2 v_uint32x2_ = {};\n    metal::uint2 v_uint32x3_ = {};\n    metal::uint2 v_uint32x4_ = {};\n    metal::int2 v_sint32_ = {};\n    metal::int2 v_sint32x2_ = {};\n    metal::int2 v_sint32x3_ = {};\n    metal::int2 v_sint32x4_ = {};\n    metal::float2 v_unorm10_10_10_2_ = {};\n    metal::float2 v_unorm8x4_bgra = {};\n    metal::half2 v_float16_as_f16_ = {};\n    metal::half2 v_float16x2_as_f16_ = {};\n    metal::half2 v_float16x4_as_f16_ = {};\n    if (v_id < (_buffer_sizes.buffer_size1 / 704)) {\n        const vb_1_type vb_1_elem = vb_1_in[v_id];\n        // metal::uint2 <- Uint8\n        v_uint8_ = metal::uint2(unpackUint8_(vb_1_elem.data[0]), 0);\n        v_uint8x2_ = unpackUint8x2_(vb_1_elem.data[16], vb_1_elem.data[17]);\n        // metal::uint2 <- Uint8x4\n        v_uint8x4_ = unpackUint8x4_(vb_1_elem.data[32], vb_1_elem.data[33], vb_1_elem.data[34], vb_1_elem.data[35]).xy;\n        // metal::int2 <- Sint8\n        v_sint8_ = metal::int2(unpackSint8_(vb_1_elem.data[48]), 0);\n        v_sint8x2_ = unpackSint8x2_(vb_1_elem.data[64], vb_1_elem.data[65]);\n        // metal::int2 <- Sint8x4\n        v_sint8x4_ = unpackSint8x4_(vb_1_elem.data[80], vb_1_elem.data[81], vb_1_elem.data[82], vb_1_elem.data[83]).xy;\n        // metal::float2 <- Unorm8\n        v_unorm8_ = metal::float2(unpackUnorm8_(vb_1_elem.data[96]), 0.0);\n        v_unorm8x2_ = unpackUnorm8x2_(vb_1_elem.data[112], vb_1_elem.data[113]);\n        // metal::float2 <- Unorm8x4\n        v_unorm8x4_ = unpackUnorm8x4_(vb_1_elem.data[128], vb_1_elem.data[129], vb_1_elem.data[130], vb_1_elem.data[131]).xy;\n        // metal::float2 <- Snorm8\n        v_snorm8_ = metal::float2(unpackSnorm8_(vb_1_elem.data[144]), 0.0);\n        v_snorm8x2_ = unpackSnorm8x2_(vb_1_elem.data[160], vb_1_elem.data[161]);\n        // metal::float2 <- Snorm8x4\n        v_snorm8x4_ = unpackSnorm8x4_(vb_1_elem.data[176], vb_1_elem.data[177], vb_1_elem.data[178], vb_1_elem.data[179]).xy;\n        // metal::uint2 <- Uint16\n        v_uint16_ = metal::uint2(unpackUint16_(vb_1_elem.data[192], vb_1_elem.data[193]), 0);\n        v_uint16x2_ = unpackUint16x2_(vb_1_elem.data[208], vb_1_elem.data[209], vb_1_elem.data[210], vb_1_elem.data[211]);\n        // metal::uint2 <- Uint16x4\n        v_uint16x4_ = unpackUint16x4_(vb_1_elem.data[224], vb_1_elem.data[225], vb_1_elem.data[226], vb_1_elem.data[227], vb_1_elem.data[228], vb_1_elem.data[229], vb_1_elem.data[230], vb_1_elem.data[231]).xy;\n        // metal::int2 <- Sint16\n        v_sint16_ = metal::int2(unpackSint16_(vb_1_elem.data[240], vb_1_elem.data[241]), 0);\n        v_sint16x2_ = unpackSint16x2_(vb_1_elem.data[256], vb_1_elem.data[257], vb_1_elem.data[258], vb_1_elem.data[259]);\n        // metal::int2 <- Sint16x4\n        v_sint16x4_ = unpackSint16x4_(vb_1_elem.data[272], vb_1_elem.data[273], vb_1_elem.data[274], vb_1_elem.data[275], vb_1_elem.data[276], vb_1_elem.data[277], vb_1_elem.data[278], vb_1_elem.data[279]).xy;\n        // metal::float2 <- Unorm16\n        v_unorm16_ = metal::float2(unpackUnorm16_(vb_1_elem.data[288], vb_1_elem.data[289]), 0.0);\n        v_unorm16x2_ = unpackUnorm16x2_(vb_1_elem.data[304], vb_1_elem.data[305], vb_1_elem.data[306], vb_1_elem.data[307]);\n        // metal::float2 <- Unorm16x4\n        v_unorm16x4_ = unpackUnorm16x4_(vb_1_elem.data[320], vb_1_elem.data[321], vb_1_elem.data[322], vb_1_elem.data[323], vb_1_elem.data[324], vb_1_elem.data[325], vb_1_elem.data[326], vb_1_elem.data[327]).xy;\n        // metal::float2 <- Snorm16\n        v_snorm16_ = metal::float2(unpackSnorm16_(vb_1_elem.data[336], vb_1_elem.data[337]), 0.0);\n        v_snorm16x2_ = unpackSnorm16x2_(vb_1_elem.data[352], vb_1_elem.data[353], vb_1_elem.data[354], vb_1_elem.data[355]);\n        // metal::float2 <- Snorm16x4\n        v_snorm16x4_ = unpackSnorm16x4_(vb_1_elem.data[368], vb_1_elem.data[369], vb_1_elem.data[370], vb_1_elem.data[371], vb_1_elem.data[372], vb_1_elem.data[373], vb_1_elem.data[374], vb_1_elem.data[375]).xy;\n        // metal::float2 <- Float16\n        v_float16_ = metal::float2(unpackFloat16_(vb_1_elem.data[384], vb_1_elem.data[385]), 0.0);\n        v_float16x2_ = unpackFloat16x2_(vb_1_elem.data[400], vb_1_elem.data[401], vb_1_elem.data[402], vb_1_elem.data[403]);\n        // metal::float2 <- Float16x4\n        v_float16x4_ = unpackFloat16x4_(vb_1_elem.data[416], vb_1_elem.data[417], vb_1_elem.data[418], vb_1_elem.data[419], vb_1_elem.data[420], vb_1_elem.data[421], vb_1_elem.data[422], vb_1_elem.data[423]).xy;\n        // metal::float2 <- Float32\n        v_float32_ = metal::float2(unpackFloat32_(vb_1_elem.data[432], vb_1_elem.data[433], vb_1_elem.data[434], vb_1_elem.data[435]), 0.0);\n        v_float32x2_ = unpackFloat32x2_(vb_1_elem.data[448], vb_1_elem.data[449], vb_1_elem.data[450], vb_1_elem.data[451], vb_1_elem.data[452], vb_1_elem.data[453], vb_1_elem.data[454], vb_1_elem.data[455]);\n        // metal::float2 <- Float32x3\n        v_float32x3_ = unpackFloat32x3_(vb_1_elem.data[464], vb_1_elem.data[465], vb_1_elem.data[466], vb_1_elem.data[467], vb_1_elem.data[468], vb_1_elem.data[469], vb_1_elem.data[470], vb_1_elem.data[471], vb_1_elem.data[472], vb_1_elem.data[473], vb_1_elem.data[474], vb_1_elem.data[475]).xy;\n        // metal::float2 <- Float32x4\n        v_float32x4_ = unpackFloat32x4_(vb_1_elem.data[480], vb_1_elem.data[481], vb_1_elem.data[482], vb_1_elem.data[483], vb_1_elem.data[484], vb_1_elem.data[485], vb_1_elem.data[486], vb_1_elem.data[487], vb_1_elem.data[488], vb_1_elem.data[489], vb_1_elem.data[490], vb_1_elem.data[491], vb_1_elem.data[492], vb_1_elem.data[493], vb_1_elem.data[494], vb_1_elem.data[495]).xy;\n        // metal::uint2 <- Uint32\n        v_uint32_ = metal::uint2(unpackUint32_(vb_1_elem.data[496], vb_1_elem.data[497], vb_1_elem.data[498], vb_1_elem.data[499]), 0);\n        v_uint32x2_ = unpackUint32x2_(vb_1_elem.data[512], vb_1_elem.data[513], vb_1_elem.data[514], vb_1_elem.data[515], vb_1_elem.data[516], vb_1_elem.data[517], vb_1_elem.data[518], vb_1_elem.data[519]);\n        // metal::uint2 <- Uint32x3\n        v_uint32x3_ = unpackUint32x3_(vb_1_elem.data[528], vb_1_elem.data[529], vb_1_elem.data[530], vb_1_elem.data[531], vb_1_elem.data[532], vb_1_elem.data[533], vb_1_elem.data[534], vb_1_elem.data[535], vb_1_elem.data[536], vb_1_elem.data[537], vb_1_elem.data[538], vb_1_elem.data[539]).xy;\n        // metal::uint2 <- Uint32x4\n        v_uint32x4_ = unpackUint32x4_(vb_1_elem.data[544], vb_1_elem.data[545], vb_1_elem.data[546], vb_1_elem.data[547], vb_1_elem.data[548], vb_1_elem.data[549], vb_1_elem.data[550], vb_1_elem.data[551], vb_1_elem.data[552], vb_1_elem.data[553], vb_1_elem.data[554], vb_1_elem.data[555], vb_1_elem.data[556], vb_1_elem.data[557], vb_1_elem.data[558], vb_1_elem.data[559]).xy;\n        // metal::int2 <- Sint32\n        v_sint32_ = metal::int2(unpackSint32_(vb_1_elem.data[560], vb_1_elem.data[561], vb_1_elem.data[562], vb_1_elem.data[563]), 0);\n        v_sint32x2_ = unpackSint32x2_(vb_1_elem.data[576], vb_1_elem.data[577], vb_1_elem.data[578], vb_1_elem.data[579], vb_1_elem.data[580], vb_1_elem.data[581], vb_1_elem.data[582], vb_1_elem.data[583]);\n        // metal::int2 <- Sint32x3\n        v_sint32x3_ = unpackSint32x3_(vb_1_elem.data[592], vb_1_elem.data[593], vb_1_elem.data[594], vb_1_elem.data[595], vb_1_elem.data[596], vb_1_elem.data[597], vb_1_elem.data[598], vb_1_elem.data[599], vb_1_elem.data[600], vb_1_elem.data[601], vb_1_elem.data[602], vb_1_elem.data[603]).xy;\n        // metal::int2 <- Sint32x4\n        v_sint32x4_ = unpackSint32x4_(vb_1_elem.data[608], vb_1_elem.data[609], vb_1_elem.data[610], vb_1_elem.data[611], vb_1_elem.data[612], vb_1_elem.data[613], vb_1_elem.data[614], vb_1_elem.data[615], vb_1_elem.data[616], vb_1_elem.data[617], vb_1_elem.data[618], vb_1_elem.data[619], vb_1_elem.data[620], vb_1_elem.data[621], vb_1_elem.data[622], vb_1_elem.data[623]).xy;\n        // metal::float2 <- Unorm10_10_10_2\n        v_unorm10_10_10_2_ = unpackUnorm10_10_10_2_(vb_1_elem.data[624], vb_1_elem.data[625], vb_1_elem.data[626], vb_1_elem.data[627]).xy;\n        // metal::float2 <- Unorm8x4Bgra\n        v_unorm8x4_bgra = unpackUnorm8x4Bgra(vb_1_elem.data[640], vb_1_elem.data[641], vb_1_elem.data[642], vb_1_elem.data[643]).xy;\n        // metal::half2 <- Float16\n        v_float16_as_f16_ = metal::half2(half(unpackFloat16_(vb_1_elem.data[656], vb_1_elem.data[657])), 0.0);\n        v_float16x2_as_f16_ = metal::half2(unpackFloat16x2_(vb_1_elem.data[672], vb_1_elem.data[673], vb_1_elem.data[674], vb_1_elem.data[675]));\n        // metal::half2 <- Float16x4\n        v_float16x4_as_f16_ = metal::half4(unpackFloat16x4_(vb_1_elem.data[688], vb_1_elem.data[689], vb_1_elem.data[690], vb_1_elem.data[691], vb_1_elem.data[692], vb_1_elem.data[693], vb_1_elem.data[694], vb_1_elem.data[695])).xy;\n    }\n    const VertexInput v_in = { v_uint8_, v_uint8x2_, v_uint8x4_, v_sint8_, v_sint8x2_, v_sint8x4_, v_unorm8_, v_unorm8x2_, v_unorm8x4_, v_snorm8_, v_snorm8x2_, v_snorm8x4_, v_uint16_, v_uint16x2_, v_uint16x4_, v_sint16_, v_sint16x2_, v_sint16x4_, v_unorm16_, v_unorm16x2_, v_unorm16x4_, v_snorm16_, v_snorm16x2_, v_snorm16x4_, v_float16_, v_float16x2_, v_float16x4_, v_float32_, v_float32x2_, v_float32x3_, v_float32x4_, v_uint32_, v_uint32x2_, v_uint32x3_, v_uint32x4_, v_sint32_, v_sint32x2_, v_sint32x3_, v_sint32x4_, v_unorm10_10_10_2_, v_unorm8x4_bgra, v_float16_as_f16_, v_float16x2_as_f16_, v_float16x4_as_f16_ };\n    const auto _tmp = VertexOutput {metal::float4(v_in.v_float32_.x)};\n    return render_vertexOutput { _tmp.position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-vpt-formats-x3.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint buffer_size1;\n};\n\nstruct VertexOutput {\n    metal::float4 position;\n};\nstruct VertexInput {\n    metal::uint3 v_uint8_;\n    metal::uint3 v_uint8x2_;\n    metal::uint3 v_uint8x4_;\n    metal::int3 v_sint8_;\n    metal::int3 v_sint8x2_;\n    metal::int3 v_sint8x4_;\n    metal::float3 v_unorm8_;\n    metal::float3 v_unorm8x2_;\n    metal::float3 v_unorm8x4_;\n    metal::float3 v_snorm8_;\n    metal::float3 v_snorm8x2_;\n    metal::float3 v_snorm8x4_;\n    metal::uint3 v_uint16_;\n    metal::uint3 v_uint16x2_;\n    metal::uint3 v_uint16x4_;\n    metal::int3 v_sint16_;\n    metal::int3 v_sint16x2_;\n    metal::int3 v_sint16x4_;\n    metal::float3 v_unorm16_;\n    metal::float3 v_unorm16x2_;\n    metal::float3 v_unorm16x4_;\n    metal::float3 v_snorm16_;\n    metal::float3 v_snorm16x2_;\n    metal::float3 v_snorm16x4_;\n    metal::float3 v_float16_;\n    metal::float3 v_float16x2_;\n    metal::float3 v_float16x4_;\n    metal::float3 v_float32_;\n    metal::float3 v_float32x2_;\n    metal::float3 v_float32x3_;\n    metal::float3 v_float32x4_;\n    metal::uint3 v_uint32_;\n    metal::uint3 v_uint32x2_;\n    metal::uint3 v_uint32x3_;\n    metal::uint3 v_uint32x4_;\n    metal::int3 v_sint32_;\n    metal::int3 v_sint32x2_;\n    metal::int3 v_sint32x3_;\n    metal::int3 v_sint32x4_;\n    metal::float3 v_unorm10_10_10_2_;\n    metal::float3 v_unorm8x4_bgra;\n    metal::half3 v_float16_as_f16_;\n    metal::half3 v_float16x2_as_f16_;\n    metal::half3 v_float16x4_as_f16_;\n    char _pad44[8];\n};\nuint unpackUint8_(metal::uchar b0) {\n    return uint(b0);\n}\nmetal::uint2 unpackUint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::uint2(b0, b1);\n}\nmetal::uint4 unpackUint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::uint4(b0, b1, b2, b3);\n}\nint unpackSint8_(metal::uchar b0) {\n    return int(as_type<char>(b0));\n}\nmetal::int2 unpackSint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::int2(as_type<char>(b0), as_type<char>(b1));\n}\nmetal::int4 unpackSint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::int4(as_type<char>(b0), as_type<char>(b1), as_type<char>(b2), as_type<char>(b3));\n}\nfloat unpackUnorm8_(metal::uchar b0) {\n    return float(float(b0) / 255.0f);\n}\nmetal::float2 unpackUnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(float(b0) / 255.0f, float(b1) / 255.0f);\n}\nmetal::float4 unpackUnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b0) / 255.0f, float(b1) / 255.0f, float(b2) / 255.0f, float(b3) / 255.0f);\n}\nfloat unpackSnorm8_(metal::uchar b0) {\n    return float(metal::max(-1.0f, as_type<char>(b0) / 127.0f));\n}\nmetal::float2 unpackSnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f));\n}\nmetal::float4 unpackSnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f), metal::max(-1.0f, as_type<char>(b2) / 127.0f), metal::max(-1.0f, as_type<char>(b3) / 127.0f));\n}\nmetal::uint unpackUint16_(metal::uint b0, metal::uint b1) {\n    return metal::uint(b1 << 8 | b0);\n}\nmetal::uint2 unpackUint16x2_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3) {\n    return metal::uint2(b1 << 8 | b0, b3 << 8 | b2);\n}\nmetal::uint4 unpackUint16x4_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3, metal::uint b4, metal::uint b5, metal::uint b6, metal::uint b7) {\n    return metal::uint4(b1 << 8 | b0, b3 << 8 | b2, b5 << 8 | b4, b7 << 8 | b6);\n}\nint unpackSint16_(metal::ushort b0, metal::ushort b1) {\n    return int(as_type<short>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::int2 unpackSint16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::int4 unpackSint16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)), as_type<short>(metal::ushort(b5 << 8 | b4)), as_type<short>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackUnorm16_(metal::ushort b0, metal::ushort b1) {\n    return float(float(b1 << 8 | b0) / 65535.0f);\n}\nmetal::float2 unpackUnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f);\n}\nmetal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f, float(b5 << 8 | b4) / 65535.0f, float(b7 << 8 | b6) / 65535.0f);\n}\nfloat unpackSnorm16_(metal::ushort b0, metal::ushort b1) {\n    return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x;\n}\nmetal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    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));\n}\nfloat unpackFloat16_(metal::ushort b0, metal::ushort b1) {\n    return float(as_type<half>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::float2 unpackFloat16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::float4 unpackFloat16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)), as_type<half>(metal::ushort(b5 << 8 | b4)), as_type<half>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackFloat32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float2 unpackFloat32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::float3 unpackFloat32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::float3(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::float4 unpackFloat32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nuint unpackUint32_(uint b0, uint b1, uint b2, uint b3) {\n    return (b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nuint2 unpackUint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return uint2((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nuint3 unpackUint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return uint3((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::uint4 unpackUint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::uint4((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8), (b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nint unpackSint32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::int2 unpackSint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::int2(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::int3 unpackSint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::int3(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::int4 unpackSint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::int4(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<int>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nmetal::float4 unpackUnorm10_10_10_2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackUnorm8x4Bgra(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b2) / 255.0f, float(b1) / 255.0f, float(b0) / 255.0f, float(b3) / 255.0f);\n}\n\nstruct render_vertexOutput {\n    metal::float4 position [[position]];\n};\nstruct vb_1_type { metal::uchar data[704]; };\nvertex render_vertexOutput render_vertex(\n  uint v_id [[vertex_id]]\n, const device vb_1_type* vb_1_in [[buffer(1)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    metal::uint3 v_uint8_ = {};\n    metal::uint3 v_uint8x2_ = {};\n    metal::uint3 v_uint8x4_ = {};\n    metal::int3 v_sint8_ = {};\n    metal::int3 v_sint8x2_ = {};\n    metal::int3 v_sint8x4_ = {};\n    metal::float3 v_unorm8_ = {};\n    metal::float3 v_unorm8x2_ = {};\n    metal::float3 v_unorm8x4_ = {};\n    metal::float3 v_snorm8_ = {};\n    metal::float3 v_snorm8x2_ = {};\n    metal::float3 v_snorm8x4_ = {};\n    metal::uint3 v_uint16_ = {};\n    metal::uint3 v_uint16x2_ = {};\n    metal::uint3 v_uint16x4_ = {};\n    metal::int3 v_sint16_ = {};\n    metal::int3 v_sint16x2_ = {};\n    metal::int3 v_sint16x4_ = {};\n    metal::float3 v_unorm16_ = {};\n    metal::float3 v_unorm16x2_ = {};\n    metal::float3 v_unorm16x4_ = {};\n    metal::float3 v_snorm16_ = {};\n    metal::float3 v_snorm16x2_ = {};\n    metal::float3 v_snorm16x4_ = {};\n    metal::float3 v_float16_ = {};\n    metal::float3 v_float16x2_ = {};\n    metal::float3 v_float16x4_ = {};\n    metal::float3 v_float32_ = {};\n    metal::float3 v_float32x2_ = {};\n    metal::float3 v_float32x3_ = {};\n    metal::float3 v_float32x4_ = {};\n    metal::uint3 v_uint32_ = {};\n    metal::uint3 v_uint32x2_ = {};\n    metal::uint3 v_uint32x3_ = {};\n    metal::uint3 v_uint32x4_ = {};\n    metal::int3 v_sint32_ = {};\n    metal::int3 v_sint32x2_ = {};\n    metal::int3 v_sint32x3_ = {};\n    metal::int3 v_sint32x4_ = {};\n    metal::float3 v_unorm10_10_10_2_ = {};\n    metal::float3 v_unorm8x4_bgra = {};\n    metal::half3 v_float16_as_f16_ = {};\n    metal::half3 v_float16x2_as_f16_ = {};\n    metal::half3 v_float16x4_as_f16_ = {};\n    if (v_id < (_buffer_sizes.buffer_size1 / 704)) {\n        const vb_1_type vb_1_elem = vb_1_in[v_id];\n        // metal::uint3 <- Uint8\n        v_uint8_ = metal::uint3(unpackUint8_(vb_1_elem.data[0]), 0, 0);\n        // metal::uint3 <- Uint8x2\n        v_uint8x2_ = metal::uint3(unpackUint8x2_(vb_1_elem.data[16], vb_1_elem.data[17]), 0);\n        // metal::uint3 <- Uint8x4\n        v_uint8x4_ = unpackUint8x4_(vb_1_elem.data[32], vb_1_elem.data[33], vb_1_elem.data[34], vb_1_elem.data[35]).xyz;\n        // metal::int3 <- Sint8\n        v_sint8_ = metal::int3(unpackSint8_(vb_1_elem.data[48]), 0, 0);\n        // metal::int3 <- Sint8x2\n        v_sint8x2_ = metal::int3(unpackSint8x2_(vb_1_elem.data[64], vb_1_elem.data[65]), 0);\n        // metal::int3 <- Sint8x4\n        v_sint8x4_ = unpackSint8x4_(vb_1_elem.data[80], vb_1_elem.data[81], vb_1_elem.data[82], vb_1_elem.data[83]).xyz;\n        // metal::float3 <- Unorm8\n        v_unorm8_ = metal::float3(unpackUnorm8_(vb_1_elem.data[96]), 0.0, 0.0);\n        // metal::float3 <- Unorm8x2\n        v_unorm8x2_ = metal::float3(unpackUnorm8x2_(vb_1_elem.data[112], vb_1_elem.data[113]), 0.0);\n        // metal::float3 <- Unorm8x4\n        v_unorm8x4_ = unpackUnorm8x4_(vb_1_elem.data[128], vb_1_elem.data[129], vb_1_elem.data[130], vb_1_elem.data[131]).xyz;\n        // metal::float3 <- Snorm8\n        v_snorm8_ = metal::float3(unpackSnorm8_(vb_1_elem.data[144]), 0.0, 0.0);\n        // metal::float3 <- Snorm8x2\n        v_snorm8x2_ = metal::float3(unpackSnorm8x2_(vb_1_elem.data[160], vb_1_elem.data[161]), 0.0);\n        // metal::float3 <- Snorm8x4\n        v_snorm8x4_ = unpackSnorm8x4_(vb_1_elem.data[176], vb_1_elem.data[177], vb_1_elem.data[178], vb_1_elem.data[179]).xyz;\n        // metal::uint3 <- Uint16\n        v_uint16_ = metal::uint3(unpackUint16_(vb_1_elem.data[192], vb_1_elem.data[193]), 0, 0);\n        // metal::uint3 <- Uint16x2\n        v_uint16x2_ = metal::uint3(unpackUint16x2_(vb_1_elem.data[208], vb_1_elem.data[209], vb_1_elem.data[210], vb_1_elem.data[211]), 0);\n        // metal::uint3 <- Uint16x4\n        v_uint16x4_ = unpackUint16x4_(vb_1_elem.data[224], vb_1_elem.data[225], vb_1_elem.data[226], vb_1_elem.data[227], vb_1_elem.data[228], vb_1_elem.data[229], vb_1_elem.data[230], vb_1_elem.data[231]).xyz;\n        // metal::int3 <- Sint16\n        v_sint16_ = metal::int3(unpackSint16_(vb_1_elem.data[240], vb_1_elem.data[241]), 0, 0);\n        // metal::int3 <- Sint16x2\n        v_sint16x2_ = metal::int3(unpackSint16x2_(vb_1_elem.data[256], vb_1_elem.data[257], vb_1_elem.data[258], vb_1_elem.data[259]), 0);\n        // metal::int3 <- Sint16x4\n        v_sint16x4_ = unpackSint16x4_(vb_1_elem.data[272], vb_1_elem.data[273], vb_1_elem.data[274], vb_1_elem.data[275], vb_1_elem.data[276], vb_1_elem.data[277], vb_1_elem.data[278], vb_1_elem.data[279]).xyz;\n        // metal::float3 <- Unorm16\n        v_unorm16_ = metal::float3(unpackUnorm16_(vb_1_elem.data[288], vb_1_elem.data[289]), 0.0, 0.0);\n        // metal::float3 <- Unorm16x2\n        v_unorm16x2_ = metal::float3(unpackUnorm16x2_(vb_1_elem.data[304], vb_1_elem.data[305], vb_1_elem.data[306], vb_1_elem.data[307]), 0.0);\n        // metal::float3 <- Unorm16x4\n        v_unorm16x4_ = unpackUnorm16x4_(vb_1_elem.data[320], vb_1_elem.data[321], vb_1_elem.data[322], vb_1_elem.data[323], vb_1_elem.data[324], vb_1_elem.data[325], vb_1_elem.data[326], vb_1_elem.data[327]).xyz;\n        // metal::float3 <- Snorm16\n        v_snorm16_ = metal::float3(unpackSnorm16_(vb_1_elem.data[336], vb_1_elem.data[337]), 0.0, 0.0);\n        // metal::float3 <- Snorm16x2\n        v_snorm16x2_ = metal::float3(unpackSnorm16x2_(vb_1_elem.data[352], vb_1_elem.data[353], vb_1_elem.data[354], vb_1_elem.data[355]), 0.0);\n        // metal::float3 <- Snorm16x4\n        v_snorm16x4_ = unpackSnorm16x4_(vb_1_elem.data[368], vb_1_elem.data[369], vb_1_elem.data[370], vb_1_elem.data[371], vb_1_elem.data[372], vb_1_elem.data[373], vb_1_elem.data[374], vb_1_elem.data[375]).xyz;\n        // metal::float3 <- Float16\n        v_float16_ = metal::float3(unpackFloat16_(vb_1_elem.data[384], vb_1_elem.data[385]), 0.0, 0.0);\n        // metal::float3 <- Float16x2\n        v_float16x2_ = metal::float3(unpackFloat16x2_(vb_1_elem.data[400], vb_1_elem.data[401], vb_1_elem.data[402], vb_1_elem.data[403]), 0.0);\n        // metal::float3 <- Float16x4\n        v_float16x4_ = unpackFloat16x4_(vb_1_elem.data[416], vb_1_elem.data[417], vb_1_elem.data[418], vb_1_elem.data[419], vb_1_elem.data[420], vb_1_elem.data[421], vb_1_elem.data[422], vb_1_elem.data[423]).xyz;\n        // metal::float3 <- Float32\n        v_float32_ = metal::float3(unpackFloat32_(vb_1_elem.data[432], vb_1_elem.data[433], vb_1_elem.data[434], vb_1_elem.data[435]), 0.0, 0.0);\n        // metal::float3 <- Float32x2\n        v_float32x2_ = metal::float3(unpackFloat32x2_(vb_1_elem.data[448], vb_1_elem.data[449], vb_1_elem.data[450], vb_1_elem.data[451], vb_1_elem.data[452], vb_1_elem.data[453], vb_1_elem.data[454], vb_1_elem.data[455]), 0.0);\n        v_float32x3_ = unpackFloat32x3_(vb_1_elem.data[464], vb_1_elem.data[465], vb_1_elem.data[466], vb_1_elem.data[467], vb_1_elem.data[468], vb_1_elem.data[469], vb_1_elem.data[470], vb_1_elem.data[471], vb_1_elem.data[472], vb_1_elem.data[473], vb_1_elem.data[474], vb_1_elem.data[475]);\n        // metal::float3 <- Float32x4\n        v_float32x4_ = unpackFloat32x4_(vb_1_elem.data[480], vb_1_elem.data[481], vb_1_elem.data[482], vb_1_elem.data[483], vb_1_elem.data[484], vb_1_elem.data[485], vb_1_elem.data[486], vb_1_elem.data[487], vb_1_elem.data[488], vb_1_elem.data[489], vb_1_elem.data[490], vb_1_elem.data[491], vb_1_elem.data[492], vb_1_elem.data[493], vb_1_elem.data[494], vb_1_elem.data[495]).xyz;\n        // metal::uint3 <- Uint32\n        v_uint32_ = metal::uint3(unpackUint32_(vb_1_elem.data[496], vb_1_elem.data[497], vb_1_elem.data[498], vb_1_elem.data[499]), 0, 0);\n        // metal::uint3 <- Uint32x2\n        v_uint32x2_ = metal::uint3(unpackUint32x2_(vb_1_elem.data[512], vb_1_elem.data[513], vb_1_elem.data[514], vb_1_elem.data[515], vb_1_elem.data[516], vb_1_elem.data[517], vb_1_elem.data[518], vb_1_elem.data[519]), 0);\n        v_uint32x3_ = unpackUint32x3_(vb_1_elem.data[528], vb_1_elem.data[529], vb_1_elem.data[530], vb_1_elem.data[531], vb_1_elem.data[532], vb_1_elem.data[533], vb_1_elem.data[534], vb_1_elem.data[535], vb_1_elem.data[536], vb_1_elem.data[537], vb_1_elem.data[538], vb_1_elem.data[539]);\n        // metal::uint3 <- Uint32x4\n        v_uint32x4_ = unpackUint32x4_(vb_1_elem.data[544], vb_1_elem.data[545], vb_1_elem.data[546], vb_1_elem.data[547], vb_1_elem.data[548], vb_1_elem.data[549], vb_1_elem.data[550], vb_1_elem.data[551], vb_1_elem.data[552], vb_1_elem.data[553], vb_1_elem.data[554], vb_1_elem.data[555], vb_1_elem.data[556], vb_1_elem.data[557], vb_1_elem.data[558], vb_1_elem.data[559]).xyz;\n        // metal::int3 <- Sint32\n        v_sint32_ = metal::int3(unpackSint32_(vb_1_elem.data[560], vb_1_elem.data[561], vb_1_elem.data[562], vb_1_elem.data[563]), 0, 0);\n        // metal::int3 <- Sint32x2\n        v_sint32x2_ = metal::int3(unpackSint32x2_(vb_1_elem.data[576], vb_1_elem.data[577], vb_1_elem.data[578], vb_1_elem.data[579], vb_1_elem.data[580], vb_1_elem.data[581], vb_1_elem.data[582], vb_1_elem.data[583]), 0);\n        v_sint32x3_ = unpackSint32x3_(vb_1_elem.data[592], vb_1_elem.data[593], vb_1_elem.data[594], vb_1_elem.data[595], vb_1_elem.data[596], vb_1_elem.data[597], vb_1_elem.data[598], vb_1_elem.data[599], vb_1_elem.data[600], vb_1_elem.data[601], vb_1_elem.data[602], vb_1_elem.data[603]);\n        // metal::int3 <- Sint32x4\n        v_sint32x4_ = unpackSint32x4_(vb_1_elem.data[608], vb_1_elem.data[609], vb_1_elem.data[610], vb_1_elem.data[611], vb_1_elem.data[612], vb_1_elem.data[613], vb_1_elem.data[614], vb_1_elem.data[615], vb_1_elem.data[616], vb_1_elem.data[617], vb_1_elem.data[618], vb_1_elem.data[619], vb_1_elem.data[620], vb_1_elem.data[621], vb_1_elem.data[622], vb_1_elem.data[623]).xyz;\n        // metal::float3 <- Unorm10_10_10_2\n        v_unorm10_10_10_2_ = unpackUnorm10_10_10_2_(vb_1_elem.data[624], vb_1_elem.data[625], vb_1_elem.data[626], vb_1_elem.data[627]).xyz;\n        // metal::float3 <- Unorm8x4Bgra\n        v_unorm8x4_bgra = unpackUnorm8x4Bgra(vb_1_elem.data[640], vb_1_elem.data[641], vb_1_elem.data[642], vb_1_elem.data[643]).xyz;\n        // metal::half3 <- Float16\n        v_float16_as_f16_ = metal::half3(half(unpackFloat16_(vb_1_elem.data[656], vb_1_elem.data[657])), 0.0, 0.0);\n        // metal::half3 <- Float16x2\n        v_float16x2_as_f16_ = metal::half3(metal::half2(unpackFloat16x2_(vb_1_elem.data[672], vb_1_elem.data[673], vb_1_elem.data[674], vb_1_elem.data[675])), 0.0);\n        // metal::half3 <- Float16x4\n        v_float16x4_as_f16_ = metal::half4(unpackFloat16x4_(vb_1_elem.data[688], vb_1_elem.data[689], vb_1_elem.data[690], vb_1_elem.data[691], vb_1_elem.data[692], vb_1_elem.data[693], vb_1_elem.data[694], vb_1_elem.data[695])).xyz;\n    }\n    const VertexInput v_in = { v_uint8_, v_uint8x2_, v_uint8x4_, v_sint8_, v_sint8x2_, v_sint8x4_, v_unorm8_, v_unorm8x2_, v_unorm8x4_, v_snorm8_, v_snorm8x2_, v_snorm8x4_, v_uint16_, v_uint16x2_, v_uint16x4_, v_sint16_, v_sint16x2_, v_sint16x4_, v_unorm16_, v_unorm16x2_, v_unorm16x4_, v_snorm16_, v_snorm16x2_, v_snorm16x4_, v_float16_, v_float16x2_, v_float16x4_, v_float32_, v_float32x2_, v_float32x3_, v_float32x4_, v_uint32_, v_uint32x2_, v_uint32x3_, v_uint32x4_, v_sint32_, v_sint32x2_, v_sint32x3_, v_sint32x4_, v_unorm10_10_10_2_, v_unorm8x4_bgra, v_float16_as_f16_, v_float16x2_as_f16_, v_float16x4_as_f16_ };\n    const auto _tmp = VertexOutput {metal::float4(v_in.v_float32_.x)};\n    return render_vertexOutput { _tmp.position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-vpt-formats-x4.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint buffer_size1;\n};\n\nstruct VertexOutput {\n    metal::float4 position;\n};\nstruct VertexInput {\n    metal::uint4 v_uint8_;\n    metal::uint4 v_uint8x2_;\n    metal::uint4 v_uint8x4_;\n    metal::int4 v_sint8_;\n    metal::int4 v_sint8x2_;\n    metal::int4 v_sint8x4_;\n    metal::float4 v_unorm8_;\n    metal::float4 v_unorm8x2_;\n    metal::float4 v_unorm8x4_;\n    metal::float4 v_snorm8_;\n    metal::float4 v_snorm8x2_;\n    metal::float4 v_snorm8x4_;\n    metal::uint4 v_uint16_;\n    metal::uint4 v_uint16x2_;\n    metal::uint4 v_uint16x4_;\n    metal::int4 v_sint16_;\n    metal::int4 v_sint16x2_;\n    metal::int4 v_sint16x4_;\n    metal::float4 v_unorm16_;\n    metal::float4 v_unorm16x2_;\n    metal::float4 v_unorm16x4_;\n    metal::float4 v_snorm16_;\n    metal::float4 v_snorm16x2_;\n    metal::float4 v_snorm16x4_;\n    metal::float4 v_float16_;\n    metal::float4 v_float16x2_;\n    metal::float4 v_float16x4_;\n    metal::float4 v_float32_;\n    metal::float4 v_float32x2_;\n    metal::float4 v_float32x3_;\n    metal::float4 v_float32x4_;\n    metal::uint4 v_uint32_;\n    metal::uint4 v_uint32x2_;\n    metal::uint4 v_uint32x3_;\n    metal::uint4 v_uint32x4_;\n    metal::int4 v_sint32_;\n    metal::int4 v_sint32x2_;\n    metal::int4 v_sint32x3_;\n    metal::int4 v_sint32x4_;\n    metal::float4 v_unorm10_10_10_2_;\n    metal::float4 v_unorm8x4_bgra;\n    metal::half4 v_float16_as_f16_;\n    metal::half4 v_float16x2_as_f16_;\n    metal::half4 v_float16x4_as_f16_;\n    char _pad44[8];\n};\nuint unpackUint8_(metal::uchar b0) {\n    return uint(b0);\n}\nmetal::uint2 unpackUint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::uint2(b0, b1);\n}\nmetal::uint4 unpackUint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::uint4(b0, b1, b2, b3);\n}\nint unpackSint8_(metal::uchar b0) {\n    return int(as_type<char>(b0));\n}\nmetal::int2 unpackSint8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::int2(as_type<char>(b0), as_type<char>(b1));\n}\nmetal::int4 unpackSint8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::int4(as_type<char>(b0), as_type<char>(b1), as_type<char>(b2), as_type<char>(b3));\n}\nfloat unpackUnorm8_(metal::uchar b0) {\n    return float(float(b0) / 255.0f);\n}\nmetal::float2 unpackUnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(float(b0) / 255.0f, float(b1) / 255.0f);\n}\nmetal::float4 unpackUnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b0) / 255.0f, float(b1) / 255.0f, float(b2) / 255.0f, float(b3) / 255.0f);\n}\nfloat unpackSnorm8_(metal::uchar b0) {\n    return float(metal::max(-1.0f, as_type<char>(b0) / 127.0f));\n}\nmetal::float2 unpackSnorm8x2_(metal::uchar b0, metal::uchar b1) {\n    return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f));\n}\nmetal::float4 unpackSnorm8x4_(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), metal::max(-1.0f, as_type<char>(b1) / 127.0f), metal::max(-1.0f, as_type<char>(b2) / 127.0f), metal::max(-1.0f, as_type<char>(b3) / 127.0f));\n}\nmetal::uint unpackUint16_(metal::uint b0, metal::uint b1) {\n    return metal::uint(b1 << 8 | b0);\n}\nmetal::uint2 unpackUint16x2_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3) {\n    return metal::uint2(b1 << 8 | b0, b3 << 8 | b2);\n}\nmetal::uint4 unpackUint16x4_(metal::uint b0, metal::uint b1, metal::uint b2, metal::uint b3, metal::uint b4, metal::uint b5, metal::uint b6, metal::uint b7) {\n    return metal::uint4(b1 << 8 | b0, b3 << 8 | b2, b5 << 8 | b4, b7 << 8 | b6);\n}\nint unpackSint16_(metal::ushort b0, metal::ushort b1) {\n    return int(as_type<short>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::int2 unpackSint16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::int4 unpackSint16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), as_type<short>(metal::ushort(b3 << 8 | b2)), as_type<short>(metal::ushort(b5 << 8 | b4)), as_type<short>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackUnorm16_(metal::ushort b0, metal::ushort b1) {\n    return float(float(b1 << 8 | b0) / 65535.0f);\n}\nmetal::float2 unpackUnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f);\n}\nmetal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(float(b1 << 8 | b0) / 65535.0f, float(b3 << 8 | b2) / 65535.0f, float(b5 << 8 | b4) / 65535.0f, float(b7 << 8 | b6) / 65535.0f);\n}\nfloat unpackSnorm16_(metal::ushort b0, metal::ushort b1) {\n    return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x;\n}\nmetal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    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));\n}\nfloat unpackFloat16_(metal::ushort b0, metal::ushort b1) {\n    return float(as_type<half>(metal::ushort(b1 << 8 | b0)));\n}\nmetal::float2 unpackFloat16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) {\n    return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)));\n}\nmetal::float4 unpackFloat16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) {\n    return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), as_type<half>(metal::ushort(b3 << 8 | b2)), as_type<half>(metal::ushort(b5 << 8 | b4)), as_type<half>(metal::ushort(b7 << 8 | b6)));\n}\nfloat unpackFloat32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float2 unpackFloat32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::float3 unpackFloat32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::float3(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::float4 unpackFloat32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nuint unpackUint32_(uint b0, uint b1, uint b2, uint b3) {\n    return (b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nuint2 unpackUint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return uint2((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nuint3 unpackUint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return uint3((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::uint4 unpackUint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::uint4((b3 << 24 | b2 << 16 | b1 << 8 | b0), (b7 << 24 | b6 << 16 | b5 << 8 | b4), (b11 << 24 | b10 << 16 | b9 << 8 | b8), (b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nint unpackSint32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::int2 unpackSint32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::int2(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\nmetal::int3 unpackSint32x3_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11) {\n    return metal::int3(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8));\n}\nmetal::int4 unpackSint32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::int4(as_type<int>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<int>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<int>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<int>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nmetal::float4 unpackUnorm10_10_10_2_(uint b0, uint b1, uint b2, uint b3) {\n    return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackUnorm8x4Bgra(metal::uchar b0, metal::uchar b1, metal::uchar b2, metal::uchar b3) {\n    return metal::float4(float(b2) / 255.0f, float(b1) / 255.0f, float(b0) / 255.0f, float(b3) / 255.0f);\n}\n\nstruct render_vertexOutput {\n    metal::float4 position [[position]];\n};\nstruct vb_1_type { metal::uchar data[704]; };\nvertex render_vertexOutput render_vertex(\n  uint v_id [[vertex_id]]\n, const device vb_1_type* vb_1_in [[buffer(1)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    metal::uint4 v_uint8_ = {};\n    metal::uint4 v_uint8x2_ = {};\n    metal::uint4 v_uint8x4_ = {};\n    metal::int4 v_sint8_ = {};\n    metal::int4 v_sint8x2_ = {};\n    metal::int4 v_sint8x4_ = {};\n    metal::float4 v_unorm8_ = {};\n    metal::float4 v_unorm8x2_ = {};\n    metal::float4 v_unorm8x4_ = {};\n    metal::float4 v_snorm8_ = {};\n    metal::float4 v_snorm8x2_ = {};\n    metal::float4 v_snorm8x4_ = {};\n    metal::uint4 v_uint16_ = {};\n    metal::uint4 v_uint16x2_ = {};\n    metal::uint4 v_uint16x4_ = {};\n    metal::int4 v_sint16_ = {};\n    metal::int4 v_sint16x2_ = {};\n    metal::int4 v_sint16x4_ = {};\n    metal::float4 v_unorm16_ = {};\n    metal::float4 v_unorm16x2_ = {};\n    metal::float4 v_unorm16x4_ = {};\n    metal::float4 v_snorm16_ = {};\n    metal::float4 v_snorm16x2_ = {};\n    metal::float4 v_snorm16x4_ = {};\n    metal::float4 v_float16_ = {};\n    metal::float4 v_float16x2_ = {};\n    metal::float4 v_float16x4_ = {};\n    metal::float4 v_float32_ = {};\n    metal::float4 v_float32x2_ = {};\n    metal::float4 v_float32x3_ = {};\n    metal::float4 v_float32x4_ = {};\n    metal::uint4 v_uint32_ = {};\n    metal::uint4 v_uint32x2_ = {};\n    metal::uint4 v_uint32x3_ = {};\n    metal::uint4 v_uint32x4_ = {};\n    metal::int4 v_sint32_ = {};\n    metal::int4 v_sint32x2_ = {};\n    metal::int4 v_sint32x3_ = {};\n    metal::int4 v_sint32x4_ = {};\n    metal::float4 v_unorm10_10_10_2_ = {};\n    metal::float4 v_unorm8x4_bgra = {};\n    metal::half4 v_float16_as_f16_ = {};\n    metal::half4 v_float16x2_as_f16_ = {};\n    metal::half4 v_float16x4_as_f16_ = {};\n    if (v_id < (_buffer_sizes.buffer_size1 / 704)) {\n        const vb_1_type vb_1_elem = vb_1_in[v_id];\n        // metal::uint4 <- Uint8\n        v_uint8_ = metal::uint4(unpackUint8_(vb_1_elem.data[0]), 0, 0, 1);\n        // metal::uint4 <- Uint8x2\n        v_uint8x2_ = metal::uint4(unpackUint8x2_(vb_1_elem.data[16], vb_1_elem.data[17]), 0, 1);\n        v_uint8x4_ = unpackUint8x4_(vb_1_elem.data[32], vb_1_elem.data[33], vb_1_elem.data[34], vb_1_elem.data[35]);\n        // metal::int4 <- Sint8\n        v_sint8_ = metal::int4(unpackSint8_(vb_1_elem.data[48]), 0, 0, 1);\n        // metal::int4 <- Sint8x2\n        v_sint8x2_ = metal::int4(unpackSint8x2_(vb_1_elem.data[64], vb_1_elem.data[65]), 0, 1);\n        v_sint8x4_ = unpackSint8x4_(vb_1_elem.data[80], vb_1_elem.data[81], vb_1_elem.data[82], vb_1_elem.data[83]);\n        // metal::float4 <- Unorm8\n        v_unorm8_ = metal::float4(unpackUnorm8_(vb_1_elem.data[96]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Unorm8x2\n        v_unorm8x2_ = metal::float4(unpackUnorm8x2_(vb_1_elem.data[112], vb_1_elem.data[113]), 0.0, 1.0);\n        v_unorm8x4_ = unpackUnorm8x4_(vb_1_elem.data[128], vb_1_elem.data[129], vb_1_elem.data[130], vb_1_elem.data[131]);\n        // metal::float4 <- Snorm8\n        v_snorm8_ = metal::float4(unpackSnorm8_(vb_1_elem.data[144]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Snorm8x2\n        v_snorm8x2_ = metal::float4(unpackSnorm8x2_(vb_1_elem.data[160], vb_1_elem.data[161]), 0.0, 1.0);\n        v_snorm8x4_ = unpackSnorm8x4_(vb_1_elem.data[176], vb_1_elem.data[177], vb_1_elem.data[178], vb_1_elem.data[179]);\n        // metal::uint4 <- Uint16\n        v_uint16_ = metal::uint4(unpackUint16_(vb_1_elem.data[192], vb_1_elem.data[193]), 0, 0, 1);\n        // metal::uint4 <- Uint16x2\n        v_uint16x2_ = metal::uint4(unpackUint16x2_(vb_1_elem.data[208], vb_1_elem.data[209], vb_1_elem.data[210], vb_1_elem.data[211]), 0, 1);\n        v_uint16x4_ = unpackUint16x4_(vb_1_elem.data[224], vb_1_elem.data[225], vb_1_elem.data[226], vb_1_elem.data[227], vb_1_elem.data[228], vb_1_elem.data[229], vb_1_elem.data[230], vb_1_elem.data[231]);\n        // metal::int4 <- Sint16\n        v_sint16_ = metal::int4(unpackSint16_(vb_1_elem.data[240], vb_1_elem.data[241]), 0, 0, 1);\n        // metal::int4 <- Sint16x2\n        v_sint16x2_ = metal::int4(unpackSint16x2_(vb_1_elem.data[256], vb_1_elem.data[257], vb_1_elem.data[258], vb_1_elem.data[259]), 0, 1);\n        v_sint16x4_ = unpackSint16x4_(vb_1_elem.data[272], vb_1_elem.data[273], vb_1_elem.data[274], vb_1_elem.data[275], vb_1_elem.data[276], vb_1_elem.data[277], vb_1_elem.data[278], vb_1_elem.data[279]);\n        // metal::float4 <- Unorm16\n        v_unorm16_ = metal::float4(unpackUnorm16_(vb_1_elem.data[288], vb_1_elem.data[289]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Unorm16x2\n        v_unorm16x2_ = metal::float4(unpackUnorm16x2_(vb_1_elem.data[304], vb_1_elem.data[305], vb_1_elem.data[306], vb_1_elem.data[307]), 0.0, 1.0);\n        v_unorm16x4_ = unpackUnorm16x4_(vb_1_elem.data[320], vb_1_elem.data[321], vb_1_elem.data[322], vb_1_elem.data[323], vb_1_elem.data[324], vb_1_elem.data[325], vb_1_elem.data[326], vb_1_elem.data[327]);\n        // metal::float4 <- Snorm16\n        v_snorm16_ = metal::float4(unpackSnorm16_(vb_1_elem.data[336], vb_1_elem.data[337]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Snorm16x2\n        v_snorm16x2_ = metal::float4(unpackSnorm16x2_(vb_1_elem.data[352], vb_1_elem.data[353], vb_1_elem.data[354], vb_1_elem.data[355]), 0.0, 1.0);\n        v_snorm16x4_ = unpackSnorm16x4_(vb_1_elem.data[368], vb_1_elem.data[369], vb_1_elem.data[370], vb_1_elem.data[371], vb_1_elem.data[372], vb_1_elem.data[373], vb_1_elem.data[374], vb_1_elem.data[375]);\n        // metal::float4 <- Float16\n        v_float16_ = metal::float4(unpackFloat16_(vb_1_elem.data[384], vb_1_elem.data[385]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Float16x2\n        v_float16x2_ = metal::float4(unpackFloat16x2_(vb_1_elem.data[400], vb_1_elem.data[401], vb_1_elem.data[402], vb_1_elem.data[403]), 0.0, 1.0);\n        v_float16x4_ = unpackFloat16x4_(vb_1_elem.data[416], vb_1_elem.data[417], vb_1_elem.data[418], vb_1_elem.data[419], vb_1_elem.data[420], vb_1_elem.data[421], vb_1_elem.data[422], vb_1_elem.data[423]);\n        // metal::float4 <- Float32\n        v_float32_ = metal::float4(unpackFloat32_(vb_1_elem.data[432], vb_1_elem.data[433], vb_1_elem.data[434], vb_1_elem.data[435]), 0.0, 0.0, 1.0);\n        // metal::float4 <- Float32x2\n        v_float32x2_ = metal::float4(unpackFloat32x2_(vb_1_elem.data[448], vb_1_elem.data[449], vb_1_elem.data[450], vb_1_elem.data[451], vb_1_elem.data[452], vb_1_elem.data[453], vb_1_elem.data[454], vb_1_elem.data[455]), 0.0, 1.0);\n        // metal::float4 <- Float32x3\n        v_float32x3_ = metal::float4(unpackFloat32x3_(vb_1_elem.data[464], vb_1_elem.data[465], vb_1_elem.data[466], vb_1_elem.data[467], vb_1_elem.data[468], vb_1_elem.data[469], vb_1_elem.data[470], vb_1_elem.data[471], vb_1_elem.data[472], vb_1_elem.data[473], vb_1_elem.data[474], vb_1_elem.data[475]), 1.0);\n        v_float32x4_ = unpackFloat32x4_(vb_1_elem.data[480], vb_1_elem.data[481], vb_1_elem.data[482], vb_1_elem.data[483], vb_1_elem.data[484], vb_1_elem.data[485], vb_1_elem.data[486], vb_1_elem.data[487], vb_1_elem.data[488], vb_1_elem.data[489], vb_1_elem.data[490], vb_1_elem.data[491], vb_1_elem.data[492], vb_1_elem.data[493], vb_1_elem.data[494], vb_1_elem.data[495]);\n        // metal::uint4 <- Uint32\n        v_uint32_ = metal::uint4(unpackUint32_(vb_1_elem.data[496], vb_1_elem.data[497], vb_1_elem.data[498], vb_1_elem.data[499]), 0, 0, 1);\n        // metal::uint4 <- Uint32x2\n        v_uint32x2_ = metal::uint4(unpackUint32x2_(vb_1_elem.data[512], vb_1_elem.data[513], vb_1_elem.data[514], vb_1_elem.data[515], vb_1_elem.data[516], vb_1_elem.data[517], vb_1_elem.data[518], vb_1_elem.data[519]), 0, 1);\n        // metal::uint4 <- Uint32x3\n        v_uint32x3_ = metal::uint4(unpackUint32x3_(vb_1_elem.data[528], vb_1_elem.data[529], vb_1_elem.data[530], vb_1_elem.data[531], vb_1_elem.data[532], vb_1_elem.data[533], vb_1_elem.data[534], vb_1_elem.data[535], vb_1_elem.data[536], vb_1_elem.data[537], vb_1_elem.data[538], vb_1_elem.data[539]), 1);\n        v_uint32x4_ = unpackUint32x4_(vb_1_elem.data[544], vb_1_elem.data[545], vb_1_elem.data[546], vb_1_elem.data[547], vb_1_elem.data[548], vb_1_elem.data[549], vb_1_elem.data[550], vb_1_elem.data[551], vb_1_elem.data[552], vb_1_elem.data[553], vb_1_elem.data[554], vb_1_elem.data[555], vb_1_elem.data[556], vb_1_elem.data[557], vb_1_elem.data[558], vb_1_elem.data[559]);\n        // metal::int4 <- Sint32\n        v_sint32_ = metal::int4(unpackSint32_(vb_1_elem.data[560], vb_1_elem.data[561], vb_1_elem.data[562], vb_1_elem.data[563]), 0, 0, 1);\n        // metal::int4 <- Sint32x2\n        v_sint32x2_ = metal::int4(unpackSint32x2_(vb_1_elem.data[576], vb_1_elem.data[577], vb_1_elem.data[578], vb_1_elem.data[579], vb_1_elem.data[580], vb_1_elem.data[581], vb_1_elem.data[582], vb_1_elem.data[583]), 0, 1);\n        // metal::int4 <- Sint32x3\n        v_sint32x3_ = metal::int4(unpackSint32x3_(vb_1_elem.data[592], vb_1_elem.data[593], vb_1_elem.data[594], vb_1_elem.data[595], vb_1_elem.data[596], vb_1_elem.data[597], vb_1_elem.data[598], vb_1_elem.data[599], vb_1_elem.data[600], vb_1_elem.data[601], vb_1_elem.data[602], vb_1_elem.data[603]), 1);\n        v_sint32x4_ = unpackSint32x4_(vb_1_elem.data[608], vb_1_elem.data[609], vb_1_elem.data[610], vb_1_elem.data[611], vb_1_elem.data[612], vb_1_elem.data[613], vb_1_elem.data[614], vb_1_elem.data[615], vb_1_elem.data[616], vb_1_elem.data[617], vb_1_elem.data[618], vb_1_elem.data[619], vb_1_elem.data[620], vb_1_elem.data[621], vb_1_elem.data[622], vb_1_elem.data[623]);\n        v_unorm10_10_10_2_ = unpackUnorm10_10_10_2_(vb_1_elem.data[624], vb_1_elem.data[625], vb_1_elem.data[626], vb_1_elem.data[627]);\n        v_unorm8x4_bgra = unpackUnorm8x4Bgra(vb_1_elem.data[640], vb_1_elem.data[641], vb_1_elem.data[642], vb_1_elem.data[643]);\n        // metal::half4 <- Float16\n        v_float16_as_f16_ = metal::half4(half(unpackFloat16_(vb_1_elem.data[656], vb_1_elem.data[657])), 0.0, 0.0, 1.0);\n        // metal::half4 <- Float16x2\n        v_float16x2_as_f16_ = metal::half4(metal::half2(unpackFloat16x2_(vb_1_elem.data[672], vb_1_elem.data[673], vb_1_elem.data[674], vb_1_elem.data[675])), 0.0, 1.0);\n        v_float16x4_as_f16_ = metal::half4(unpackFloat16x4_(vb_1_elem.data[688], vb_1_elem.data[689], vb_1_elem.data[690], vb_1_elem.data[691], vb_1_elem.data[692], vb_1_elem.data[693], vb_1_elem.data[694], vb_1_elem.data[695]));\n    }\n    const VertexInput v_in = { v_uint8_, v_uint8x2_, v_uint8x4_, v_sint8_, v_sint8x2_, v_sint8x4_, v_unorm8_, v_unorm8x2_, v_unorm8x4_, v_snorm8_, v_snorm8x2_, v_snorm8x4_, v_uint16_, v_uint16x2_, v_uint16x4_, v_sint16_, v_sint16x2_, v_sint16x4_, v_unorm16_, v_unorm16x2_, v_unorm16x4_, v_snorm16_, v_snorm16x2_, v_snorm16x4_, v_float16_, v_float16x2_, v_float16x4_, v_float32_, v_float32x2_, v_float32x3_, v_float32x4_, v_uint32_, v_uint32x2_, v_uint32x3_, v_uint32x4_, v_sint32_, v_sint32x2_, v_sint32x3_, v_sint32x4_, v_unorm10_10_10_2_, v_unorm8x4_bgra, v_float16_as_f16_, v_float16x2_as_f16_, v_float16x4_as_f16_ };\n    const auto _tmp = VertexOutput {metal::float4(v_in.v_float32_.x)};\n    return render_vertexOutput { _tmp.position };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-msl-vpt.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint buffer_size1;\n    uint buffer_size2;\n};\n\nstruct VertexOutput {\n    metal::float4 position;\n    metal::float4 color;\n    metal::float2 texcoord;\n    char _pad3[8];\n};\nstruct VertexInput {\n    metal::float4 position;\n    metal::float3 normal;\n    metal::float2 texcoord;\n    char _pad3[8];\n};\nfloat unpackFloat32_(uint b0, uint b1, uint b2, uint b3) {\n    return as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0);\n}\nmetal::float4 unpackFloat32x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7, uint b8, uint b9, uint b10, uint b11, uint b12, uint b13, uint b14, uint b15) {\n    return metal::float4(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4), as_type<float>(b11 << 24 | b10 << 16 | b9 << 8 | b8), as_type<float>(b15 << 24 | b14 << 16 | b13 << 8 | b12));\n}\nmetal::float2 unpackFloat32x2_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) {\n    return metal::float2(as_type<float>(b3 << 24 | b2 << 16 | b1 << 8 | b0), as_type<float>(b7 << 24 | b6 << 16 | b5 << 8 | b4));\n}\n\nmetal::float4 do_lighting(\n    metal::float4 position,\n    metal::float3 normal\n) {\n    return metal::float4(0.0);\n}\n\nstruct render_vertexOutput {\n    metal::float4 position [[position]];\n    metal::float4 color [[user(loc0), center_perspective]];\n    metal::float2 texcoord [[user(loc1), center_perspective]];\n};\nstruct vb_1_type { metal::uchar data[20]; };\nstruct vb_2_type { metal::uchar data[16]; };\nvertex render_vertexOutput render_vertex(\n  uint v_existing_id [[vertex_id]]\n, constant metal::float4x4& mvp_matrix [[user(fake0)]]\n, uint i_id [[instance_id]]\n, const device vb_1_type* vb_1_in [[buffer(1)]]\n, const device vb_2_type* vb_2_in [[buffer(2)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    metal::float4 position_1 = {};\n    metal::float3 normal_1 = {};\n    if (v_existing_id < (_buffer_sizes.buffer_size1 / 20)) {\n        const vb_1_type vb_1_elem = vb_1_in[v_existing_id];\n        // metal::float4 <- Float32\n        position_1 = metal::float4(unpackFloat32_(vb_1_elem.data[0], vb_1_elem.data[1], vb_1_elem.data[2], vb_1_elem.data[3]), 0.0, 0.0, 1.0);\n        // metal::float3 <- Float32x4\n        normal_1 = unpackFloat32x4_(vb_1_elem.data[4], vb_1_elem.data[5], vb_1_elem.data[6], vb_1_elem.data[7], vb_1_elem.data[8], vb_1_elem.data[9], vb_1_elem.data[10], vb_1_elem.data[11], vb_1_elem.data[12], vb_1_elem.data[13], vb_1_elem.data[14], vb_1_elem.data[15], vb_1_elem.data[16], vb_1_elem.data[17], vb_1_elem.data[18], vb_1_elem.data[19]).xyz;\n    }\n    metal::float2 texcoord = {};\n    if (i_id < (_buffer_sizes.buffer_size2 / 16)) {\n        const vb_2_type vb_2_elem = vb_2_in[i_id];\n        texcoord = unpackFloat32x2_(vb_2_elem.data[0], vb_2_elem.data[1], vb_2_elem.data[2], vb_2_elem.data[3], vb_2_elem.data[4], vb_2_elem.data[5], vb_2_elem.data[6], vb_2_elem.data[7]);\n    }\n    const VertexInput v_in = { position_1, normal_1, texcoord };\n    VertexOutput v_out = {};\n    metal::float4x4 _e6 = mvp_matrix;\n    v_out.position = v_in.position * _e6;\n    metal::float4 _e11 = do_lighting(v_in.position, v_in.normal);\n    v_out.color = _e11;\n    v_out.texcoord = v_in.texcoord;\n    VertexOutput _e14 = v_out;\n    const auto _tmp = _e14;\n    return render_vertexOutput { _tmp.position, _tmp.color, _tmp.texcoord };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-multiview.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct main_Input {\n};\nfragment void main_(\n  uint view_index [[amplification_id]]\n) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-operators.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nconstant metal::float4 v_f32_one = metal::float4(1.0, 1.0, 1.0, 1.0);\nconstant metal::float4 v_f32_zero = metal::float4(0.0, 0.0, 0.0, 0.0);\nconstant metal::float4 v_f32_half = metal::float4(0.5, 0.5, 0.5, 0.5);\nconstant metal::int4 v_i32_one = metal::int4(1, 1, 1, 1);\nconstant bool b_false = false;\nconstant bool b_true = true;\nconstant bool short_circuit_1_invalid_rhs = false;\nconstant bool short_circuit_2_invalid_rhs = false;\nconstant bool short_circuit_3_ = true;\nconstant bool short_circuit_4_ = true;\n\nmetal::float4 builtins(\n) {\n    int s1_ = true ? 1 : 0;\n    metal::float4 s2_ = true ? v_f32_one : v_f32_zero;\n    metal::float4 s3_ = metal::float4(1.0, 1.0, 1.0, 1.0);\n    metal::float4 m1_ = metal::mix(v_f32_zero, v_f32_one, v_f32_half);\n    metal::float4 m2_ = metal::mix(v_f32_zero, v_f32_one, 0.1);\n    float b1_ = as_type<float>(1);\n    metal::float4 b2_ = as_type<metal::float4>(v_i32_one);\n    metal::int4 v_i32_zero = metal::int4(0, 0, 0, 0);\n    return ((((static_cast<metal::float4>(as_type<metal::int4>(as_type<metal::uint4>(metal::int4(s1_)) + as_type<metal::uint4>(v_i32_zero))) + s2_) + m1_) + m2_) + metal::float4(b1_)) + b2_;\n}\n\nmetal::int4 naga_mod(metal::int4 lhs, metal::int4 rhs) {\n    metal::int4 divisor = metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n    return lhs - (lhs / divisor) * divisor;\n}\n\nmetal::float4 splat(\n    float m,\n    int n\n) {\n    metal::float2 a_2 = ((metal::float2(2.0) + metal::float2(m)) - metal::float2(4.0)) / metal::float2(8.0);\n    metal::int4 b = naga_mod(metal::int4(n), metal::int4(2));\n    return a_2.xyxy + static_cast<metal::float4>(b);\n}\n\nmetal::float2 splat_assignment(\n) {\n    metal::float2 a = metal::float2(2.0);\n    metal::float2 _e3 = a;\n    a = _e3 + metal::float2(1.0);\n    metal::float2 _e7 = a;\n    a = _e7 - metal::float2(3.0);\n    metal::float2 _e11 = a;\n    a = _e11 / metal::float2(4.0);\n    metal::float2 _e15 = a;\n    return _e15;\n}\n\nmetal::float3 bool_cast(\n    metal::float3 x\n) {\n    metal::bool3 y = static_cast<metal::bool3>(x);\n    return static_cast<metal::float3>(y);\n}\n\nbool p(\n) {\n    return true;\n}\n\nbool q(\n) {\n    return false;\n}\n\nbool r(\n) {\n    return true;\n}\n\nbool s(\n) {\n    return false;\n}\n\nvoid logical(\n) {\n    bool local = {};\n    bool local_1 = {};\n    bool local_2 = {};\n    bool local_3 = {};\n    bool local_4 = {};\n    bool local_5 = {};\n    bool local_6 = {};\n    bool neg0_ = !(true);\n    metal::bool2 neg1_ = !(metal::bool2(true));\n    if (!(true)) {\n        local = false;\n    } else {\n        local = true;\n    }\n    bool or_ = local;\n    if (true) {\n        local_1 = false;\n    } else {\n        local_1 = false;\n    }\n    bool and_ = local_1;\n    bool bitwise_or0_ = true | false;\n    metal::bool3 bitwise_or1_ = metal::bool3(true) | metal::bool3(false);\n    bool bitwise_and0_ = true & false;\n    metal::bool4 bitwise_and1_ = metal::bool4(true) & metal::bool4(false);\n    if (!(false)) {\n        local_2 = false;\n    } else {\n        local_2 = true;\n    }\n    bool _e27 = local_2;\n    bool short_circuit_5_ = !(_e27);\n    bool _e29 = p();\n    if (!(_e29)) {\n        bool _e33 = q();\n        local_3 = _e33;\n    } else {\n        local_3 = true;\n    }\n    bool _e35 = local_3;\n    if (_e35) {\n        bool _e38 = r();\n        if (!(_e38)) {\n            bool _e42 = s();\n            local_5 = _e42;\n        } else {\n            local_5 = true;\n        }\n        bool _e44 = local_5;\n        local_4 = _e44;\n    } else {\n        local_4 = false;\n    }\n    bool short_circuit_6_ = local_4;\n    if (false) {\n        bool _e50 = q();\n        local_6 = _e50;\n    } else {\n        local_6 = true;\n    }\n    bool short_circuit_7_ = local_6;\n    return;\n}\n\nmetal::int2 naga_neg(metal::int2 val) {\n    return as_type<metal::int2>(-as_type<metal::uint2>(val));\n}\n\nint naga_div(int lhs, int rhs) {\n    return lhs / metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n}\n\nuint naga_div(uint lhs, uint rhs) {\n    return lhs / metal::select(rhs, 1u, rhs == 0u);\n}\n\nmetal::int2 naga_div(metal::int2 lhs, metal::int2 rhs) {\n    return lhs / metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n}\n\nmetal::uint3 naga_div(metal::uint3 lhs, metal::uint3 rhs) {\n    return lhs / metal::select(rhs, 1u, rhs == 0u);\n}\n\nint naga_mod(int lhs, int rhs) {\n    int divisor = metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n    return lhs - (lhs / divisor) * divisor;\n}\n\nuint naga_mod(uint lhs, uint rhs) {\n    return lhs % metal::select(rhs, 1u, rhs == 0u);\n}\n\nmetal::int2 naga_mod(metal::int2 lhs, metal::int2 rhs) {\n    metal::int2 divisor = metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n    return lhs - (lhs / divisor) * divisor;\n}\n\nmetal::uint3 naga_mod(metal::uint3 lhs, metal::uint3 rhs) {\n    return lhs % metal::select(rhs, 1u, rhs == 0u);\n}\n\nmetal::uint2 naga_div(metal::uint2 lhs, metal::uint2 rhs) {\n    return lhs / metal::select(rhs, 1u, rhs == 0u);\n}\n\nmetal::uint2 naga_mod(metal::uint2 lhs, metal::uint2 rhs) {\n    return lhs % metal::select(rhs, 1u, rhs == 0u);\n}\n\nvoid arithmetic(\n) {\n    int prevent_const_eval = {};\n    int wgpu_7437_ = {};\n    float neg0_1 = -(1.0);\n    metal::int2 neg1_1 = naga_neg(metal::int2(1));\n    metal::float2 neg2_ = -(metal::float2(1.0));\n    int add0_ = as_type<int>(as_type<uint>(2) + as_type<uint>(1));\n    uint add1_ = 2u + 1u;\n    float add2_ = 2.0 + 1.0;\n    metal::int2 add3_ = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) + as_type<metal::uint2>(metal::int2(1)));\n    metal::uint3 add4_ = metal::uint3(2u) + metal::uint3(1u);\n    metal::float4 add5_ = metal::float4(2.0) + metal::float4(1.0);\n    int sub0_ = as_type<int>(as_type<uint>(2) - as_type<uint>(1));\n    uint sub1_ = 2u - 1u;\n    float sub2_ = 2.0 - 1.0;\n    metal::int2 sub3_ = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) - as_type<metal::uint2>(metal::int2(1)));\n    metal::uint3 sub4_ = metal::uint3(2u) - metal::uint3(1u);\n    metal::float4 sub5_ = metal::float4(2.0) - metal::float4(1.0);\n    int mul0_ = as_type<int>(as_type<uint>(2) * as_type<uint>(1));\n    uint mul1_ = 2u * 1u;\n    float mul2_ = 2.0 * 1.0;\n    metal::int2 mul3_ = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) * as_type<metal::uint2>(metal::int2(1)));\n    metal::uint3 mul4_ = metal::uint3(2u) * metal::uint3(1u);\n    metal::float4 mul5_ = metal::float4(2.0) * metal::float4(1.0);\n    int div0_ = naga_div(2, 1);\n    uint div1_ = naga_div(2u, 1u);\n    float div2_ = 2.0 / 1.0;\n    metal::int2 div3_ = naga_div(metal::int2(2), metal::int2(1));\n    metal::uint3 div4_ = naga_div(metal::uint3(2u), metal::uint3(1u));\n    metal::float4 div5_ = metal::float4(2.0) / metal::float4(1.0);\n    int rem0_ = naga_mod(2, 1);\n    uint rem1_ = naga_mod(2u, 1u);\n    float rem2_ = metal::fmod(2.0, 1.0);\n    metal::int2 rem3_ = naga_mod(metal::int2(2), metal::int2(1));\n    metal::uint3 rem4_ = naga_mod(metal::uint3(2u), metal::uint3(1u));\n    metal::float4 rem5_ = metal::fmod(metal::float4(2.0), metal::float4(1.0));\n    {\n        metal::int2 add0_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) + as_type<metal::uint2>(metal::int2(1)));\n        metal::int2 add1_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) + as_type<metal::uint2>(metal::int2(1)));\n        metal::uint2 add2_1 = metal::uint2(2u) + metal::uint2(1u);\n        metal::uint2 add3_1 = metal::uint2(2u) + metal::uint2(1u);\n        metal::float2 add4_1 = metal::float2(2.0) + metal::float2(1.0);\n        metal::float2 add5_1 = metal::float2(2.0) + metal::float2(1.0);\n        metal::int2 sub0_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) - as_type<metal::uint2>(metal::int2(1)));\n        metal::int2 sub1_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) - as_type<metal::uint2>(metal::int2(1)));\n        metal::uint2 sub2_1 = metal::uint2(2u) - metal::uint2(1u);\n        metal::uint2 sub3_1 = metal::uint2(2u) - metal::uint2(1u);\n        metal::float2 sub4_1 = metal::float2(2.0) - metal::float2(1.0);\n        metal::float2 sub5_1 = metal::float2(2.0) - metal::float2(1.0);\n        metal::int2 mul0_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) * as_type<uint>(1));\n        metal::int2 mul1_1 = as_type<metal::int2>(as_type<uint>(2) * as_type<metal::uint2>(metal::int2(1)));\n        metal::uint2 mul2_1 = metal::uint2(2u) * 1u;\n        metal::uint2 mul3_1 = 2u * metal::uint2(1u);\n        metal::float2 mul4_1 = metal::float2(2.0) * 1.0;\n        metal::float2 mul5_1 = 2.0 * metal::float2(1.0);\n        metal::int2 div0_1 = naga_div(metal::int2(2), metal::int2(1));\n        metal::int2 div1_1 = naga_div(metal::int2(2), metal::int2(1));\n        metal::uint2 div2_1 = naga_div(metal::uint2(2u), metal::uint2(1u));\n        metal::uint2 div3_1 = naga_div(metal::uint2(2u), metal::uint2(1u));\n        metal::float2 div4_1 = metal::float2(2.0) / metal::float2(1.0);\n        metal::float2 div5_1 = metal::float2(2.0) / metal::float2(1.0);\n        metal::int2 rem0_1 = naga_mod(metal::int2(2), metal::int2(1));\n        metal::int2 rem1_1 = naga_mod(metal::int2(2), metal::int2(1));\n        metal::uint2 rem2_1 = naga_mod(metal::uint2(2u), metal::uint2(1u));\n        metal::uint2 rem3_1 = naga_mod(metal::uint2(2u), metal::uint2(1u));\n        metal::float2 rem4_1 = metal::fmod(metal::float2(2.0), metal::float2(1.0));\n        metal::float2 rem5_1 = metal::fmod(metal::float2(2.0), metal::float2(1.0));\n    }\n    metal::float3x3 add = metal::float3x3(metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0));\n    metal::float3x3 sub = metal::float3x3(metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0));\n    metal::float3x3 mul_scalar0_ = metal::float3x3 {} * 1.0;\n    metal::float3x3 mul_scalar1_ = 2.0 * metal::float3x3 {};\n    metal::float3 mul_vector0_ = metal::float4x3 {} * metal::float4(1.0);\n    metal::float4 mul_vector1_ = metal::float3(2.0) * metal::float4x3 {};\n    metal::float3x3 mul = metal::float3x3(metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0), metal::float3(0.0, 0.0, 0.0));\n    int _e205 = prevent_const_eval;\n    wgpu_7437_ = as_type<int>(as_type<uint>(_e205) + as_type<uint>((-2147483647 - 1)));\n    return;\n}\n\nvoid bit(\n) {\n    int flip0_ = ~(1);\n    uint flip1_ = ~(1u);\n    metal::int2 flip2_ = ~(metal::int2(1));\n    metal::uint3 flip3_ = ~(metal::uint3(1u));\n    int or0_ = 2 | 1;\n    uint or1_ = 2u | 1u;\n    metal::int2 or2_ = metal::int2(2) | metal::int2(1);\n    metal::uint3 or3_ = metal::uint3(2u) | metal::uint3(1u);\n    int and0_ = 2 & 1;\n    uint and1_ = 2u & 1u;\n    metal::int2 and2_ = metal::int2(2) & metal::int2(1);\n    metal::uint3 and3_ = metal::uint3(2u) & metal::uint3(1u);\n    int xor0_ = 2 ^ 1;\n    uint xor1_ = 2u ^ 1u;\n    metal::int2 xor2_ = metal::int2(2) ^ metal::int2(1);\n    metal::uint3 xor3_ = metal::uint3(2u) ^ metal::uint3(1u);\n    int shl0_ = 2 << 1u;\n    uint shl1_ = 2u << 1u;\n    metal::int2 shl2_ = metal::int2(2) << metal::uint2(1u);\n    metal::uint3 shl3_ = metal::uint3(2u) << metal::uint3(1u);\n    int shr0_ = 2 >> 1u;\n    uint shr1_ = 2u >> 1u;\n    metal::int2 shr2_ = metal::int2(2) >> metal::uint2(1u);\n    metal::uint3 shr3_ = metal::uint3(2u) >> metal::uint3(1u);\n    return;\n}\n\nvoid comparison(\n) {\n    bool eq0_ = 2 == 1;\n    bool eq1_ = 2u == 1u;\n    bool eq2_ = 2.0 == 1.0;\n    metal::bool2 eq3_ = metal::int2(2) == metal::int2(1);\n    metal::bool3 eq4_ = metal::uint3(2u) == metal::uint3(1u);\n    metal::bool4 eq5_ = metal::float4(2.0) == metal::float4(1.0);\n    bool neq0_ = 2 != 1;\n    bool neq1_ = 2u != 1u;\n    bool neq2_ = 2.0 != 1.0;\n    metal::bool2 neq3_ = metal::int2(2) != metal::int2(1);\n    metal::bool3 neq4_ = metal::uint3(2u) != metal::uint3(1u);\n    metal::bool4 neq5_ = metal::float4(2.0) != metal::float4(1.0);\n    bool lt0_ = 2 < 1;\n    bool lt1_ = 2u < 1u;\n    bool lt2_ = 2.0 < 1.0;\n    metal::bool2 lt3_ = metal::int2(2) < metal::int2(1);\n    metal::bool3 lt4_ = metal::uint3(2u) < metal::uint3(1u);\n    metal::bool4 lt5_ = metal::float4(2.0) < metal::float4(1.0);\n    bool lte0_ = 2 <= 1;\n    bool lte1_ = 2u <= 1u;\n    bool lte2_ = 2.0 <= 1.0;\n    metal::bool2 lte3_ = metal::int2(2) <= metal::int2(1);\n    metal::bool3 lte4_ = metal::uint3(2u) <= metal::uint3(1u);\n    metal::bool4 lte5_ = metal::float4(2.0) <= metal::float4(1.0);\n    bool gt0_ = 2 > 1;\n    bool gt1_ = 2u > 1u;\n    bool gt2_ = 2.0 > 1.0;\n    metal::bool2 gt3_ = metal::int2(2) > metal::int2(1);\n    metal::bool3 gt4_ = metal::uint3(2u) > metal::uint3(1u);\n    metal::bool4 gt5_ = metal::float4(2.0) > metal::float4(1.0);\n    bool gte0_ = 2 >= 1;\n    bool gte1_ = 2u >= 1u;\n    bool gte2_ = 2.0 >= 1.0;\n    metal::bool2 gte3_ = metal::int2(2) >= metal::int2(1);\n    metal::bool3 gte4_ = metal::uint3(2u) >= metal::uint3(1u);\n    metal::bool4 gte5_ = metal::float4(2.0) >= metal::float4(1.0);\n    return;\n}\n\nvoid assignment(\n) {\n    int a_1 = {};\n    metal::int3 vec0_ = metal::int3 {};\n    a_1 = 1;\n    int _e5 = a_1;\n    a_1 = as_type<int>(as_type<uint>(_e5) + as_type<uint>(1));\n    int _e7 = a_1;\n    a_1 = as_type<int>(as_type<uint>(_e7) - as_type<uint>(1));\n    int _e9 = a_1;\n    int _e10 = a_1;\n    a_1 = as_type<int>(as_type<uint>(_e9) * as_type<uint>(_e10));\n    int _e12 = a_1;\n    int _e13 = a_1;\n    a_1 = naga_div(_e12, _e13);\n    int _e15 = a_1;\n    a_1 = naga_mod(_e15, 1);\n    int _e17 = a_1;\n    a_1 = _e17 & 0;\n    int _e19 = a_1;\n    a_1 = _e19 | 0;\n    int _e21 = a_1;\n    a_1 = _e21 ^ 0;\n    int _e23 = a_1;\n    a_1 = _e23 << 2u;\n    int _e25 = a_1;\n    a_1 = _e25 >> 1u;\n    int _e28 = a_1;\n    a_1 = as_type<int>(as_type<uint>(_e28) + as_type<uint>(1));\n    int _e31 = a_1;\n    a_1 = as_type<int>(as_type<uint>(_e31) - as_type<uint>(1));\n    int _e37 = vec0_[1];\n    vec0_[1] = as_type<int>(as_type<uint>(_e37) + as_type<uint>(1));\n    int _e41 = vec0_[1];\n    vec0_[1] = as_type<int>(as_type<uint>(_e41) - as_type<uint>(1));\n    return;\n}\n\nint naga_neg(int val) {\n    return as_type<int>(-as_type<uint>(val));\n}\n\nvoid negation_avoids_prefix_decrement(\n) {\n    int i0_ = naga_neg(1);\n    int i1_ = naga_neg(naga_neg(1));\n    int i2_ = naga_neg(naga_neg(1));\n    int i3_ = naga_neg(naga_neg(1));\n    int i4_ = naga_neg(naga_neg(naga_neg(1)));\n    int i5_ = naga_neg(naga_neg(naga_neg(naga_neg(1))));\n    int i6_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(1)))));\n    int i7_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(1)))));\n    float f0_ = -(1.0);\n    float f1_ = -(-(1.0));\n    float f2_ = -(-(1.0));\n    float f3_ = -(-(1.0));\n    float f4_ = -(-(-(1.0)));\n    float f5_ = -(-(-(-(1.0))));\n    float f6_ = -(-(-(-(-(1.0)))));\n    float f7_ = -(-(-(-(-(1.0)))));\n    return;\n}\n\nstruct main_Input {\n};\nkernel void main_(\n  metal::uint3 id [[threadgroup_position_in_grid]]\n) {\n    metal::float4 _e1 = builtins();\n    metal::float4 _e6 = splat(static_cast<float>(id.x), static_cast<int>(id.y));\n    metal::float2 _e7 = splat_assignment();\n    metal::float3 _e12 = bool_cast(metal::float3(1.0, 1.0, 1.0));\n    logical();\n    arithmetic();\n    bit();\n    comparison();\n    assignment();\n    negation_avoids_prefix_decrement();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-overrides-atomicCompareExchangeWeak.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _atomic_compare_exchange_result_Uint_4_ {\n    uint old_value;\n    bool exchanged;\n    char _pad2[3];\n};\n\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    device A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\ntemplate <typename A>\n_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit(\n    threadgroup A *atomic_ptr,\n    uint cmp,\n    uint v\n) {\n    bool swapped = metal::atomic_compare_exchange_weak_explicit(\n        atomic_ptr, &cmp, v,\n        metal::memory_order_relaxed, metal::memory_order_relaxed\n    );\n    return _atomic_compare_exchange_result_Uint_4_{cmp, swapped};\n}\nconstant int o = 2;\n\nkernel void f(\n  metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, threadgroup metal::atomic_uint& a\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        metal::atomic_store_explicit(&a, 0, metal::memory_order_relaxed);\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    _atomic_compare_exchange_result_Uint_4_ _e5 = naga_atomic_compare_exchange_weak_explicit(&a, 2u, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-overrides-ray-query.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct _RayQuery {\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data> intersector;\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data>::result_type intersection;\n    bool ready = false;\n};\nconstexpr metal::uint _map_intersection_type(const metal::raytracing::intersection_type ty) {\n    return ty==metal::raytracing::intersection_type::triangle ? 1 : \n        ty==metal::raytracing::intersection_type::bounding_box ? 4 : 0;\n}\n\nstruct RayDesc {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    metal::float3 origin;\n    metal::float3 dir;\n};\nconstant float o = 2.0;\n\nkernel void main_(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n) {\n    _RayQuery rq = {};\n    RayDesc desc = RayDesc {4u, 255u, 34.0, 38.0, metal::float3(46.0), metal::float3(58.0, 62.0, 74.0)};\n    rq.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq.intersector.set_opacity_cull_mode((desc.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (desc.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq.intersector.force_opacity((desc.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (desc.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq.intersector.accept_any_intersection((desc.flags & 4) != 0);\n    rq.intersection = rq.intersector.intersect(metal::raytracing::ray(desc.origin, desc.dir, desc.tmin, desc.tmax), acc_struct, desc.cull_mask);    rq.ready = true;\n    uint2 loop_bound = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool _e31 = rq.ready;\n        if (_e31) {\n        } else {\n            break;\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-overrides.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nconstant bool has_point_light = false;\nconstant float specular_param = 2.3;\nconstant float gain = 1.1;\nconstant float width = 0.0;\nconstant float depth = 2.3;\nconstant float height = 4.6;\nconstant float inferred_f32_ = 2.718;\nconstant uint auto_conversion = 0u;\n\nkernel void main_(\n) {\n    float gain_x_10_ = 11.0;\n    float store_override = {};\n    float t = 23.0;\n    bool x = {};\n    float gain_x_100_ = {};\n    x = true;\n    float _e9 = gain_x_10_;\n    gain_x_100_ = _e9 * 10.0;\n    store_override = gain;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-padding.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct S {\n    metal::float3 a;\n};\nstruct Test {\n    S a;\n    float b;\n    char _pad2[12];\n};\nstruct type_2 {\n    metal::float3 inner[2];\n};\nstruct Test2_ {\n    type_2 a;\n    float b;\n    char _pad2[12];\n};\nstruct Test3_ {\n    metal::float4x3 a;\n    float b;\n    char _pad2[12];\n};\n\nstruct vertex_Output {\n    metal::float4 member [[position]];\n};\nvertex vertex_Output vertex_(\n  constant Test& input1_ [[buffer(0)]]\n, constant Test2_& input2_ [[buffer(1)]]\n, constant Test3_& input3_ [[buffer(2)]]\n) {\n    float _e4 = input1_.b;\n    float _e8 = input2_.b;\n    float _e12 = input3_.b;\n    return vertex_Output { ((metal::float4(1.0) * _e4) * _e8) * _e12 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-phony_assignment.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nint five(\n) {\n    return 5;\n}\n\nstruct main_Input {\n};\nkernel void main_(\n  metal::uint3 id [[thread_position_in_grid]]\n, constant float& binding [[user(fake0)]]\n) {\n    float phony = binding;\n    float phony_1 = binding;\n    int _e6 = five();\n    int _e7 = five();\n    float phony_2 = binding;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-pointer-function-arg-restrict.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    int inner[4];\n};\nstruct type_9 {\n    metal::int2 inner[4];\n};\nstruct type_11 {\n    metal::float2x2 inner[4];\n};\nstruct type_13 {\n    type_2 inner[4];\n};\nstruct type_15 {\n    type_13 inner[4];\n};\n\nvoid takes_ptr(\n    thread int& p\n) {\n    return;\n}\n\nvoid takes_array_ptr(\n    thread type_2& p_1\n) {\n    return;\n}\n\nvoid takes_vec_ptr(\n    thread metal::int2& p_2\n) {\n    return;\n}\n\nvoid takes_mat_ptr(\n    thread metal::float2x2& p_3\n) {\n    return;\n}\n\nvoid local_var(\n    uint i\n) {\n    type_2 arr = type_2 {{1, 2, 3, 4}};\n    takes_ptr(arr.inner[metal::min(unsigned(i), 3u)]);\n    takes_array_ptr(arr);\n    return;\n}\n\nvoid mat_vec_ptrs(\n    thread type_9& pv,\n    thread type_11& pm,\n    uint i_1\n) {\n    takes_vec_ptr(pv.inner[metal::min(unsigned(i_1), 3u)]);\n    takes_mat_ptr(pm.inner[metal::min(unsigned(i_1), 3u)]);\n    return;\n}\n\nvoid argument(\n    thread type_2& v,\n    uint i_2\n) {\n    takes_ptr(v.inner[metal::min(unsigned(i_2), 3u)]);\n    return;\n}\n\nvoid argument_nested_x2_(\n    thread type_13& v_1,\n    uint i_3,\n    uint j\n) {\n    takes_ptr(v_1.inner[metal::min(unsigned(i_3), 3u)].inner[metal::min(unsigned(j), 3u)]);\n    takes_ptr(v_1.inner[metal::min(unsigned(i_3), 3u)].inner[0]);\n    takes_ptr(v_1.inner[0].inner[metal::min(unsigned(j), 3u)]);\n    takes_array_ptr(v_1.inner[metal::min(unsigned(i_3), 3u)]);\n    return;\n}\n\nvoid argument_nested_x3_(\n    thread type_15& v_2,\n    uint i_4,\n    uint j_1\n) {\n    takes_ptr(v_2.inner[metal::min(unsigned(i_4), 3u)].inner[0].inner[metal::min(unsigned(j_1), 3u)]);\n    takes_ptr(v_2.inner[metal::min(unsigned(i_4), 3u)].inner[metal::min(unsigned(j_1), 3u)].inner[0]);\n    takes_ptr(v_2.inner[0].inner[metal::min(unsigned(i_4), 3u)].inner[metal::min(unsigned(j_1), 3u)]);\n    return;\n}\n\nvoid index_from_self(\n    thread type_2& v_3,\n    uint i_5\n) {\n    int _e3 = v_3.inner[metal::min(unsigned(i_5), 3u)];\n    takes_ptr(v_3.inner[metal::min(unsigned(_e3), 3u)]);\n    return;\n}\n\nvoid local_var_from_arg(\n    type_2 a,\n    uint i_6\n) {\n    type_2 b = {};\n    b = a;\n    takes_ptr(b.inner[metal::min(unsigned(i_6), 3u)]);\n    return;\n}\n\nvoid let_binding(\n    thread type_2& a_1,\n    uint i_7\n) {\n    takes_ptr(a_1.inner[metal::min(unsigned(i_7), 3u)]);\n    takes_ptr(a_1.inner[0]);\n    return;\n}\n\nkernel void main_(\n) {\n    type_9 vec_ = {};\n    type_11 mat = {};\n    type_2 arr1d = {};\n    type_13 arr2d = {};\n    type_15 arr3d = {};\n    local_var(1u);\n    mat_vec_ptrs(vec_, mat, 1u);\n    argument(arr1d, 1u);\n    argument_nested_x2_(arr2d, 1u, 2u);\n    argument_nested_x3_(arr3d, 1u, 2u);\n    index_from_self(arr1d, 1u);\n    local_var_from_arg(type_2 {{1, 2, 3, 4}}, 5u);\n    let_binding(arr1d, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-pointer-function-arg-rzsw.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\nstruct type_2 {\n    int inner[4];\n};\nstruct type_9 {\n    metal::int2 inner[4];\n};\nstruct type_11 {\n    metal::float2x2 inner[4];\n};\nstruct type_13 {\n    type_2 inner[4];\n};\nstruct type_15 {\n    type_13 inner[4];\n};\n\nvoid takes_ptr(\n    thread int& p\n) {\n    return;\n}\n\nvoid takes_array_ptr(\n    thread type_2& p_1\n) {\n    return;\n}\n\nvoid takes_vec_ptr(\n    thread metal::int2& p_2\n) {\n    return;\n}\n\nvoid takes_mat_ptr(\n    thread metal::float2x2& p_3\n) {\n    return;\n}\n\nvoid local_var(\n    uint i\n) {\n    type_2 arr = type_2 {{1, 2, 3, 4}};\n    int oob = {};\n    takes_ptr(uint(i) < 4 ? arr.inner[i] : oob);\n    takes_array_ptr(arr);\n    return;\n}\n\nvoid mat_vec_ptrs(\n    thread type_9& pv,\n    thread type_11& pm,\n    uint i_1\n) {\n    metal::int2 oob_1 = {};\n    metal::float2x2 oob_2 = {};\n    takes_vec_ptr(uint(i_1) < 4 ? pv.inner[i_1] : oob_1);\n    takes_mat_ptr(uint(i_1) < 4 ? pm.inner[i_1] : oob_2);\n    return;\n}\n\nvoid argument(\n    thread type_2& v,\n    uint i_2\n) {\n    int oob_3 = {};\n    takes_ptr(uint(i_2) < 4 ? v.inner[i_2] : oob_3);\n    return;\n}\n\nvoid argument_nested_x2_(\n    thread type_13& v_1,\n    uint i_3,\n    uint j\n) {\n    int oob_4 = {};\n    type_2 oob_5 = {};\n    takes_ptr(uint(j) < 4 && uint(i_3) < 4 ? v_1.inner[i_3].inner[j] : oob_4);\n    takes_ptr(uint(i_3) < 4 ? v_1.inner[i_3].inner[0] : oob_4);\n    takes_ptr(uint(j) < 4 ? v_1.inner[0].inner[j] : oob_4);\n    takes_array_ptr(uint(i_3) < 4 ? v_1.inner[i_3] : oob_5);\n    return;\n}\n\nvoid argument_nested_x3_(\n    thread type_15& v_2,\n    uint i_4,\n    uint j_1\n) {\n    int oob_6 = {};\n    takes_ptr(uint(j_1) < 4 && uint(i_4) < 4 ? v_2.inner[i_4].inner[0].inner[j_1] : oob_6);\n    takes_ptr(uint(j_1) < 4 && uint(i_4) < 4 ? v_2.inner[i_4].inner[j_1].inner[0] : oob_6);\n    takes_ptr(uint(j_1) < 4 && uint(i_4) < 4 ? v_2.inner[0].inner[i_4].inner[j_1] : oob_6);\n    return;\n}\n\nvoid index_from_self(\n    thread type_2& v_3,\n    uint i_5\n) {\n    int oob_7 = {};\n    int _e3 = uint(i_5) < 4 ? v_3.inner[i_5] : DefaultConstructible();\n    takes_ptr(uint(_e3) < 4 ? v_3.inner[_e3] : oob_7);\n    return;\n}\n\nvoid local_var_from_arg(\n    type_2 a,\n    uint i_6\n) {\n    type_2 b = {};\n    int oob_8 = {};\n    b = a;\n    takes_ptr(uint(i_6) < 4 ? b.inner[i_6] : oob_8);\n    return;\n}\n\nvoid let_binding(\n    thread type_2& a_1,\n    uint i_7\n) {\n    int oob_9 = {};\n    takes_ptr(uint(i_7) < 4 ? a_1.inner[i_7] : oob_9);\n    takes_ptr(a_1.inner[0]);\n    return;\n}\n\nkernel void main_(\n) {\n    type_9 vec_ = {};\n    type_11 mat = {};\n    type_2 arr1d = {};\n    type_13 arr2d = {};\n    type_15 arr3d = {};\n    local_var(1u);\n    mat_vec_ptrs(vec_, mat, 1u);\n    argument(arr1d, 1u);\n    argument_nested_x2_(arr2d, 1u, 2u);\n    argument_nested_x3_(arr3d, 1u, 2u);\n    index_from_self(arr1d, 1u);\n    local_var_from_arg(type_2 {{1, 2, 3, 4}}, 5u);\n    let_binding(arr1d, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-pointer-function-arg.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    int inner[4];\n};\nstruct type_9 {\n    metal::int2 inner[4];\n};\nstruct type_11 {\n    metal::float2x2 inner[4];\n};\nstruct type_13 {\n    type_2 inner[4];\n};\nstruct type_15 {\n    type_13 inner[4];\n};\n\nvoid takes_ptr(\n    thread int& p\n) {\n    return;\n}\n\nvoid takes_array_ptr(\n    thread type_2& p_1\n) {\n    return;\n}\n\nvoid takes_vec_ptr(\n    thread metal::int2& p_2\n) {\n    return;\n}\n\nvoid takes_mat_ptr(\n    thread metal::float2x2& p_3\n) {\n    return;\n}\n\nvoid local_var(\n    uint i\n) {\n    type_2 arr = type_2 {{1, 2, 3, 4}};\n    takes_ptr(arr.inner[i]);\n    takes_array_ptr(arr);\n    return;\n}\n\nvoid mat_vec_ptrs(\n    thread type_9& pv,\n    thread type_11& pm,\n    uint i_1\n) {\n    takes_vec_ptr(pv.inner[i_1]);\n    takes_mat_ptr(pm.inner[i_1]);\n    return;\n}\n\nvoid argument(\n    thread type_2& v,\n    uint i_2\n) {\n    takes_ptr(v.inner[i_2]);\n    return;\n}\n\nvoid argument_nested_x2_(\n    thread type_13& v_1,\n    uint i_3,\n    uint j\n) {\n    takes_ptr(v_1.inner[i_3].inner[j]);\n    takes_ptr(v_1.inner[i_3].inner[0]);\n    takes_ptr(v_1.inner[0].inner[j]);\n    takes_array_ptr(v_1.inner[i_3]);\n    return;\n}\n\nvoid argument_nested_x3_(\n    thread type_15& v_2,\n    uint i_4,\n    uint j_1\n) {\n    takes_ptr(v_2.inner[i_4].inner[0].inner[j_1]);\n    takes_ptr(v_2.inner[i_4].inner[j_1].inner[0]);\n    takes_ptr(v_2.inner[0].inner[i_4].inner[j_1]);\n    return;\n}\n\nvoid index_from_self(\n    thread type_2& v_3,\n    uint i_5\n) {\n    int _e3 = v_3.inner[i_5];\n    takes_ptr(v_3.inner[_e3]);\n    return;\n}\n\nvoid local_var_from_arg(\n    type_2 a,\n    uint i_6\n) {\n    type_2 b = {};\n    b = a;\n    takes_ptr(b.inner[i_6]);\n    return;\n}\n\nvoid let_binding(\n    thread type_2& a_1,\n    uint i_7\n) {\n    takes_ptr(a_1.inner[i_7]);\n    takes_ptr(a_1.inner[0]);\n    return;\n}\n\nkernel void main_(\n) {\n    type_9 vec_ = {};\n    type_11 mat = {};\n    type_2 arr1d = {};\n    type_13 arr2d = {};\n    type_15 arr3d = {};\n    local_var(1u);\n    mat_vec_ptrs(vec_, mat, 1u);\n    argument(arr1d, 1u);\n    argument_nested_x2_(arr2d, 1u, 2u);\n    argument_nested_x3_(arr3d, 1u, 2u);\n    index_from_self(arr1d, 1u);\n    local_var_from_arg(type_2 {{1, 2, 3, 4}}, 5u);\n    let_binding(arr1d, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-policy-mix.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\nstruct type_2 {\n    metal::float4 inner[10];\n};\nstruct InStorage {\n    type_2 a;\n};\nstruct type_3 {\n    metal::float4 inner[20];\n};\nstruct InUniform {\n    type_3 a;\n};\nstruct type_5 {\n    float inner[30];\n};\nstruct type_6 {\n    float inner[40];\n};\nstruct type_9 {\n    metal::float4 inner[2];\n};\n\nmetal::float4 mock_function(\n    metal::int2 c,\n    int i,\n    int l,\n    device InStorage const& in_storage,\n    constant InUniform& in_uniform,\n    metal::texture2d_array<float, metal::access::sample> image_2d_array,\n    threadgroup type_5& in_workgroup,\n    thread type_6& in_private\n) {\n    type_9 in_function = type_9 {{metal::float4(0.707, 0.0, 0.0, 1.0), metal::float4(0.0, 0.707, 0.0, 1.0)}};\n    metal::float4 _e18 = in_storage.a.inner[i];\n    metal::float4 _e22 = in_uniform.a.inner[i];\n    metal::float4 _e25 = (uint(l) < image_2d_array.get_num_mip_levels() && uint(i) < image_2d_array.get_array_size() && metal::all(metal::uint2(c) < metal::uint2(image_2d_array.get_width(l), image_2d_array.get_height(l))) ? image_2d_array.read(metal::uint2(c), i, l): DefaultConstructible());\n    float _e29 = in_workgroup.inner[metal::min(unsigned(i), 29u)];\n    float _e34 = in_private.inner[metal::min(unsigned(i), 39u)];\n    metal::float4 _e38 = in_function.inner[metal::min(unsigned(i), 1u)];\n    return ((((_e18 + _e22) + _e25) + metal::float4(_e29)) + metal::float4(_e34)) + _e38;\n}\n\nkernel void main_(\n  metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, device InStorage const& in_storage [[user(fake0)]]\n, constant InUniform& in_uniform [[user(fake0)]]\n, metal::texture2d_array<float, metal::access::sample> image_2d_array [[user(fake0)]]\n, threadgroup type_5& in_workgroup\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        in_workgroup = {};\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    type_6 in_private = {};\n    metal::float4 _e5 = mock_function(metal::int2(1, 2), 3, 4, in_storage, in_uniform, image_2d_array, in_workgroup, in_private);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-primitive-index.metal",
    "content": "// language: metal2.3\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nstruct funcInput {\n};\nstruct funcOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment funcOutput func(\n  uint index [[primitive_id]]\n) {\n    return funcOutput { metal::float4(static_cast<float>(index), 1.0, 1.0, 1.0) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-quad.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct VertexOutput {\n    metal::float2 uv;\n    char _pad1[8];\n    metal::float4 position;\n};\nconstant float c_scale = 1.2;\n\nstruct vert_mainInput {\n    metal::float2 pos [[attribute(0)]];\n    metal::float2 uv [[attribute(1)]];\n};\nstruct vert_mainOutput {\n    metal::float2 uv [[user(loc0), center_perspective]];\n    metal::float4 position [[position]];\n};\nvertex vert_mainOutput vert_main(\n  vert_mainInput varyings [[stage_in]]\n) {\n    const auto pos = varyings.pos;\n    const auto uv = varyings.uv;\n    const auto _tmp = VertexOutput {uv, {}, metal::float4(c_scale * pos, 0.0, 1.0)};\n    return vert_mainOutput { _tmp.uv, _tmp.position };\n}\n\n\nstruct frag_mainInput {\n    metal::float2 uv_1 [[user(loc0), center_perspective]];\n};\nstruct frag_mainOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment frag_mainOutput frag_main(\n  frag_mainInput varyings_1 [[stage_in]]\n, metal::texture2d<float, metal::access::sample> u_texture [[user(fake0)]]\n, metal::sampler u_sampler [[user(fake0)]]\n) {\n    const auto uv_1 = varyings_1.uv_1;\n    metal::float4 color = u_texture.sample(u_sampler, uv_1);\n    if (color.w == 0.0) {\n        metal::discard_fragment();\n    }\n    metal::float4 premultiplied = color.w * color;\n    return frag_mainOutput { premultiplied };\n}\n\n\nstruct fs_extraOutput {\n    metal::float4 member_2 [[color(0)]];\n};\nfragment fs_extraOutput fs_extra(\n) {\n    return fs_extraOutput { metal::float4(0.0, 0.5, 0.0, 0.5) };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-ray-query-no-init-tracking.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct _RayQuery {\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data> intersector;\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data>::result_type intersection;\n    bool ready = false;\n};\nconstexpr metal::uint _map_intersection_type(const metal::raytracing::intersection_type ty) {\n    return ty==metal::raytracing::intersection_type::triangle ? 1 : \n        ty==metal::raytracing::intersection_type::bounding_box ? 4 : 0;\n}\n\nstruct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    metal::float2 barycentrics;\n    bool front_face;\n    char _pad9[11];\n    metal::float4x3 object_to_world;\n    metal::float4x3 world_to_object;\n};\nstruct RayDesc {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    metal::float3 origin;\n    metal::float3 dir;\n};\nstruct Output {\n    uint visible;\n    char _pad1[12];\n    metal::float3 normal;\n};\n\nRayIntersection query_loop(\n    metal::float3 pos,\n    metal::float3 dir,\n    metal::raytracing::instance_acceleration_structure acs\n) {\n    _RayQuery rq_1 = {};\n    RayDesc _e8 = RayDesc {4u, 255u, 0.1, 100.0, pos, dir};\n    rq_1.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq_1.intersector.set_opacity_cull_mode((_e8.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (_e8.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq_1.intersector.force_opacity((_e8.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e8.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq_1.intersector.accept_any_intersection((_e8.flags & 4) != 0);\n    rq_1.intersection = rq_1.intersector.intersect(metal::raytracing::ray(_e8.origin, _e8.dir, _e8.tmin, _e8.tmax), acs, _e8.cull_mask);    rq_1.ready = true;\n    uint2 loop_bound = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool _e9 = rq_1.ready;\n        if (_e9) {\n        } else {\n            break;\n        }\n    }\n    return RayIntersection {_map_intersection_type(rq_1.intersection.type), rq_1.intersection.distance, rq_1.intersection.user_instance_id, rq_1.intersection.instance_id, {}, rq_1.intersection.geometry_id, rq_1.intersection.primitive_id, rq_1.intersection.triangle_barycentric_coord, rq_1.intersection.triangle_front_facing, {}, rq_1.intersection.object_to_world_transform, rq_1.intersection.world_to_object_transform};\n}\n\nmetal::float3 get_torus_normal(\n    metal::float3 world_point,\n    RayIntersection intersection\n) {\n    metal::float3 local_point = intersection.world_to_object * metal::float4(world_point, 1.0);\n    metal::float2 point_on_guiding_line = metal::normalize(local_point.xy) * 2.4;\n    metal::float3 world_point_on_guiding_line = intersection.object_to_world * metal::float4(point_on_guiding_line, 0.0, 1.0);\n    return metal::normalize(world_point - world_point_on_guiding_line);\n}\n\nkernel void main_(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n, device Output& output [[user(fake0)]]\n) {\n    metal::float3 pos_1 = metal::float3(0.0);\n    metal::float3 dir_1 = metal::float3(0.0, 1.0, 0.0);\n    RayIntersection _e7 = query_loop(pos_1, dir_1, acc_struct);\n    output.visible = static_cast<uint>(_e7.kind == 0u);\n    metal::float3 _e18 = get_torus_normal(dir_1 * _e7.t, _e7);\n    output.normal = _e18;\n    return;\n}\n\n\nkernel void main_candidate(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n) {\n    _RayQuery rq = {};\n    metal::float3 pos_2 = metal::float3(0.0);\n    metal::float3 dir_2 = metal::float3(0.0, 1.0, 0.0);\n    RayDesc _e12 = RayDesc {4u, 255u, 0.1, 100.0, pos_2, dir_2};\n    rq.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq.intersector.set_opacity_cull_mode((_e12.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (_e12.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq.intersector.force_opacity((_e12.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e12.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq.intersector.accept_any_intersection((_e12.flags & 4) != 0);\n    rq.intersection = rq.intersector.intersect(metal::raytracing::ray(_e12.origin, _e12.dir, _e12.tmin, _e12.tmax), acc_struct, _e12.cull_mask);    rq.ready = true;\n    RayIntersection intersection_1 = RayIntersection {_map_intersection_type(rq.intersection.type), rq.intersection.distance, rq.intersection.user_instance_id, rq.intersection.instance_id, {}, rq.intersection.geometry_id, rq.intersection.primitive_id, rq.intersection.triangle_barycentric_coord, rq.intersection.triangle_front_facing, {}, rq.intersection.object_to_world_transform, rq.intersection.world_to_object_transform};\n    if (intersection_1.kind == 3u) {\n        return;\n    } else {\n        if (intersection_1.kind == 1u) {\n            return;\n        } else {\n            rq.ready = false;\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-ray-query.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct _RayQuery {\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data> intersector;\n    metal::raytracing::intersector<metal::raytracing::instancing, metal::raytracing::triangle_data, metal::raytracing::world_space_data>::result_type intersection;\n    bool ready = false;\n};\nconstexpr metal::uint _map_intersection_type(const metal::raytracing::intersection_type ty) {\n    return ty==metal::raytracing::intersection_type::triangle ? 1 : \n        ty==metal::raytracing::intersection_type::bounding_box ? 4 : 0;\n}\n\nstruct RayIntersection {\n    uint kind;\n    float t;\n    uint instance_custom_data;\n    uint instance_index;\n    uint sbt_record_offset;\n    uint geometry_index;\n    uint primitive_index;\n    metal::float2 barycentrics;\n    bool front_face;\n    char _pad9[11];\n    metal::float4x3 object_to_world;\n    metal::float4x3 world_to_object;\n};\nstruct RayDesc {\n    uint flags;\n    uint cull_mask;\n    float tmin;\n    float tmax;\n    metal::float3 origin;\n    metal::float3 dir;\n};\nstruct Output {\n    uint visible;\n    char _pad1[12];\n    metal::float3 normal;\n};\n\nRayIntersection query_loop(\n    metal::float3 pos,\n    metal::float3 dir,\n    metal::raytracing::instance_acceleration_structure acs\n) {\n    _RayQuery rq_1 = {};\n    RayDesc _e8 = RayDesc {4u, 255u, 0.1, 100.0, pos, dir};\n    rq_1.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq_1.intersector.set_opacity_cull_mode((_e8.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (_e8.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq_1.intersector.force_opacity((_e8.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e8.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq_1.intersector.accept_any_intersection((_e8.flags & 4) != 0);\n    rq_1.intersection = rq_1.intersector.intersect(metal::raytracing::ray(_e8.origin, _e8.dir, _e8.tmin, _e8.tmax), acs, _e8.cull_mask);    rq_1.ready = true;\n    uint2 loop_bound = uint2(4294967295u);\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        bool _e9 = rq_1.ready;\n        if (_e9) {\n        } else {\n            break;\n        }\n    }\n    return RayIntersection {_map_intersection_type(rq_1.intersection.type), rq_1.intersection.distance, rq_1.intersection.user_instance_id, rq_1.intersection.instance_id, {}, rq_1.intersection.geometry_id, rq_1.intersection.primitive_id, rq_1.intersection.triangle_barycentric_coord, rq_1.intersection.triangle_front_facing, {}, rq_1.intersection.object_to_world_transform, rq_1.intersection.world_to_object_transform};\n}\n\nmetal::float3 get_torus_normal(\n    metal::float3 world_point,\n    RayIntersection intersection\n) {\n    metal::float3 local_point = intersection.world_to_object * metal::float4(world_point, 1.0);\n    metal::float2 point_on_guiding_line = metal::normalize(local_point.xy) * 2.4;\n    metal::float3 world_point_on_guiding_line = intersection.object_to_world * metal::float4(point_on_guiding_line, 0.0, 1.0);\n    return metal::normalize(world_point - world_point_on_guiding_line);\n}\n\nkernel void main_(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n, device Output& output [[user(fake0)]]\n) {\n    metal::float3 pos_1 = metal::float3(0.0);\n    metal::float3 dir_1 = metal::float3(0.0, 1.0, 0.0);\n    RayIntersection _e7 = query_loop(pos_1, dir_1, acc_struct);\n    output.visible = static_cast<uint>(_e7.kind == 0u);\n    metal::float3 _e18 = get_torus_normal(dir_1 * _e7.t, _e7);\n    output.normal = _e18;\n    return;\n}\n\n\nkernel void main_candidate(\n  metal::raytracing::instance_acceleration_structure acc_struct [[user(fake0)]]\n) {\n    _RayQuery rq = {};\n    metal::float3 pos_2 = metal::float3(0.0);\n    metal::float3 dir_2 = metal::float3(0.0, 1.0, 0.0);\n    RayDesc _e12 = RayDesc {4u, 255u, 0.1, 100.0, pos_2, dir_2};\n    rq.intersector.assume_geometry_type(metal::raytracing::geometry_type::triangle);\n    rq.intersector.set_opacity_cull_mode((_e12.flags & 64) != 0 ? metal::raytracing::opacity_cull_mode::opaque : (_e12.flags & 128) != 0 ? metal::raytracing::opacity_cull_mode::non_opaque : metal::raytracing::opacity_cull_mode::none);\n    rq.intersector.force_opacity((_e12.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e12.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none);\n    rq.intersector.accept_any_intersection((_e12.flags & 4) != 0);\n    rq.intersection = rq.intersector.intersect(metal::raytracing::ray(_e12.origin, _e12.dir, _e12.tmin, _e12.tmax), acc_struct, _e12.cull_mask);    rq.ready = true;\n    RayIntersection intersection_1 = RayIntersection {_map_intersection_type(rq.intersection.type), rq.intersection.distance, rq.intersection.user_instance_id, rq.intersection.instance_id, {}, rq.intersection.geometry_id, rq.intersection.primitive_id, rq.intersection.triangle_barycentric_coord, rq.intersection.triangle_front_facing, {}, rq.intersection.object_to_world_transform, rq.intersection.world_to_object_transform};\n    if (intersection_1.kind == 3u) {\n        return;\n    } else {\n        if (intersection_1.kind == 1u) {\n            return;\n        } else {\n            rq.ready = false;\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-resource-binding-map.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\nstruct DefaultConstructible {\n    template<typename T>\n    operator T() && {\n        return T {};\n    }\n};\n\n\nstruct entry_point_oneInput {\n};\nstruct entry_point_oneOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment entry_point_oneOutput entry_point_one(\n  metal::float4 pos [[position]]\n, metal::texture2d<float, metal::access::sample> t1_ [[texture(0)]]\n) {\n    constexpr metal::sampler s1_(\n        metal::s_address::clamp_to_edge,\n        metal::t_address::clamp_to_edge,\n        metal::r_address::clamp_to_edge,\n        metal::mag_filter::linear,\n        metal::min_filter::linear,\n        metal::coord::normalized\n    );\n    metal::float4 _e4 = t1_.sample(s1_, pos.xy);\n    return entry_point_oneOutput { _e4 };\n}\n\n\nstruct entry_point_twoOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment entry_point_twoOutput entry_point_two(\n  metal::texture2d<float, metal::access::sample> t1_ [[texture(0)]]\n, metal::sampler s1_ [[sampler(0)]]\n, constant metal::float2& uniformOne [[buffer(0)]]\n) {\n    metal::float2 _e3 = uniformOne;\n    metal::float4 _e4 = t1_.sample(s1_, _e3);\n    return entry_point_twoOutput { _e4 };\n}\n\n\nstruct entry_point_threeOutput {\n    metal::float4 member_2 [[color(0)]];\n};\nfragment entry_point_threeOutput entry_point_three(\n  metal::texture2d<float, metal::access::sample> t1_ [[texture(0)]]\n, metal::texture2d<float, metal::access::sample> t2_ [[texture(1)]]\n, metal::sampler s2_ [[sampler(1)]]\n, constant metal::float2& uniformOne [[buffer(0)]]\n, constant metal::float2& uniformTwo [[buffer(1)]]\n) {\n    constexpr metal::sampler s1_(\n        metal::s_address::clamp_to_edge,\n        metal::t_address::clamp_to_edge,\n        metal::r_address::clamp_to_edge,\n        metal::mag_filter::linear,\n        metal::min_filter::linear,\n        metal::coord::normalized\n    );\n    metal::float2 _e3 = uniformTwo;\n    metal::float2 _e5 = uniformOne;\n    metal::float4 _e7 = t1_.sample(s1_, _e3 + _e5);\n    metal::float2 _e11 = uniformOne;\n    metal::float4 _e12 = t2_.sample(s2_, _e11);\n    return entry_point_threeOutput { _e7 + _e12 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-select.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    metal::int2 x0_ = metal::int2(1, 2);\n    metal::float2 i1_ = {};\n    int _e12 = x0_.x;\n    int _e14 = x0_.y;\n    i1_ = (_e12 < _e14) ? metal::float2(0.0, 1.0) : metal::float2(1.0, 0.0);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-shadow.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct _mslBufferSizes {\n    uint size2;\n};\n\nstruct Globals {\n    metal::float4x4 view_proj;\n    metal::uint4 num_lights;\n};\nstruct Entity {\n    metal::float4x4 world;\n    metal::float4 color;\n};\nstruct VertexOutput {\n    metal::float4 proj_position;\n    metal::float3 world_normal;\n    metal::float4 world_position;\n};\nstruct Light {\n    metal::float4x4 proj;\n    metal::float4 pos;\n    metal::float4 color;\n};\ntypedef Light type_8[1];\nstruct type_9 {\n    Light inner[10];\n};\nconstant metal::float3 c_ambient = metal::float3(0.05, 0.05, 0.05);\nconstant uint c_max_lights = 10u;\n\nfloat fetch_shadow(\n    uint light_id,\n    metal::float4 homogeneous_coords,\n    metal::depth2d_array<float, metal::access::sample> t_shadow,\n    metal::sampler sampler_shadow\n) {\n    if (homogeneous_coords.w <= 0.0) {\n        return 1.0;\n    }\n    metal::float2 flip_correction = metal::float2(0.5, -0.5);\n    float proj_correction = 1.0 / homogeneous_coords.w;\n    metal::float2 light_local = ((homogeneous_coords.xy * flip_correction) * proj_correction) + metal::float2(0.5, 0.5);\n    float _e24 = t_shadow.sample_compare(sampler_shadow, light_local, static_cast<int>(light_id), homogeneous_coords.z * proj_correction);\n    return _e24;\n}\n\nstruct vs_mainInput {\n    metal::int4 position [[attribute(0)]];\n    metal::int4 normal [[attribute(1)]];\n};\nstruct vs_mainOutput {\n    metal::float4 proj_position [[position]];\n    metal::float3 world_normal [[user(loc0), center_perspective]];\n    metal::float4 world_position [[user(loc1), center_perspective]];\n};\nvertex vs_mainOutput vs_main(\n  vs_mainInput varyings [[stage_in]]\n, constant Globals& u_globals [[user(fake0)]]\n, constant Entity& u_entity [[user(fake0)]]\n) {\n    const auto position = varyings.position;\n    const auto normal = varyings.normal;\n    VertexOutput out = {};\n    metal::float4x4 w = u_entity.world;\n    metal::float4x4 _e7 = u_entity.world;\n    metal::float4 world_pos = _e7 * static_cast<metal::float4>(position);\n    out.world_normal = metal::float3x3(w[0].xyz, w[1].xyz, w[2].xyz) * static_cast<metal::float3>(normal.xyz);\n    out.world_position = world_pos;\n    metal::float4x4 _e26 = u_globals.view_proj;\n    out.proj_position = _e26 * world_pos;\n    VertexOutput _e28 = out;\n    const auto _tmp = _e28;\n    return vs_mainOutput { _tmp.proj_position, _tmp.world_normal, _tmp.world_position };\n}\n\n\nstruct fs_mainInput {\n    metal::float3 world_normal [[user(loc0), center_perspective]];\n    metal::float4 world_position [[user(loc1), center_perspective]];\n};\nstruct fs_mainOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment fs_mainOutput fs_main(\n  fs_mainInput varyings_1 [[stage_in]]\n, metal::float4 proj_position [[position]]\n, constant Globals& u_globals [[user(fake0)]]\n, constant Entity& u_entity [[user(fake0)]]\n, device type_8 const& s_lights [[user(fake0)]]\n, metal::depth2d_array<float, metal::access::sample> t_shadow [[user(fake0)]]\n, metal::sampler sampler_shadow [[user(fake0)]]\n, constant _mslBufferSizes& _buffer_sizes [[user(fake0)]]\n) {\n    const VertexOutput in = { proj_position, varyings_1.world_normal, varyings_1.world_position };\n    metal::float3 color = c_ambient;\n    uint i = 0u;\n    metal::float3 normal_1 = metal::normalize(in.world_normal);\n    uint2 loop_bound = uint2(4294967295u);\n    bool loop_init = true;\n    while(true) {\n        if (metal::all(loop_bound == uint2(0u))) { break; }\n        loop_bound -= uint2(loop_bound.y == 0u, 1u);\n        if (!loop_init) {\n            uint _e40 = i;\n            i = _e40 + 1u;\n        }\n        loop_init = false;\n        uint _e7 = i;\n        uint _e11 = u_globals.num_lights.x;\n        if (_e7 < metal::min(_e11, c_max_lights)) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i;\n            Light light = s_lights[_e16];\n            uint _e19 = i;\n            float _e23 = fetch_shadow(_e19, light.proj * in.world_position, t_shadow, sampler_shadow);\n            metal::float3 light_dir = metal::normalize(light.pos.xyz - in.world_position.xyz);\n            float diffuse = metal::max(0.0, metal::dot(normal_1, light_dir));\n            metal::float3 _e33 = color;\n            color = _e33 + ((_e23 * diffuse) * light.color.xyz);\n        }\n    }\n    metal::float3 _e42 = color;\n    metal::float4 _e47 = u_entity.color;\n    return fs_mainOutput { metal::float4(_e42, 1.0) * _e47 };\n}\n\n\nstruct fs_main_without_storageInput {\n    metal::float3 world_normal [[user(loc0), center_perspective]];\n    metal::float4 world_position [[user(loc1), center_perspective]];\n};\nstruct fs_main_without_storageOutput {\n    metal::float4 member_2 [[color(0)]];\n};\nfragment fs_main_without_storageOutput fs_main_without_storage(\n  fs_main_without_storageInput varyings_2 [[stage_in]]\n, metal::float4 proj_position_1 [[position]]\n, constant Globals& u_globals [[user(fake0)]]\n, constant Entity& u_entity [[user(fake0)]]\n, constant type_9& u_lights [[user(fake0)]]\n, metal::depth2d_array<float, metal::access::sample> t_shadow [[user(fake0)]]\n, metal::sampler sampler_shadow [[user(fake0)]]\n) {\n    const VertexOutput in_1 = { proj_position_1, varyings_2.world_normal, varyings_2.world_position };\n    metal::float3 color_1 = c_ambient;\n    uint i_1 = 0u;\n    metal::float3 normal_2 = metal::normalize(in_1.world_normal);\n    uint2 loop_bound_1 = uint2(4294967295u);\n    bool loop_init_1 = true;\n    while(true) {\n        if (metal::all(loop_bound_1 == uint2(0u))) { break; }\n        loop_bound_1 -= uint2(loop_bound_1.y == 0u, 1u);\n        if (!loop_init_1) {\n            uint _e40 = i_1;\n            i_1 = _e40 + 1u;\n        }\n        loop_init_1 = false;\n        uint _e7 = i_1;\n        uint _e11 = u_globals.num_lights.x;\n        if (_e7 < metal::min(_e11, c_max_lights)) {\n        } else {\n            break;\n        }\n        {\n            uint _e16 = i_1;\n            Light light_1 = u_lights.inner[_e16];\n            uint _e19 = i_1;\n            float _e23 = fetch_shadow(_e19, light_1.proj * in_1.world_position, t_shadow, sampler_shadow);\n            metal::float3 light_dir_1 = metal::normalize(light_1.pos.xyz - in_1.world_position.xyz);\n            float diffuse_1 = metal::max(0.0, metal::dot(normal_2, light_dir_1));\n            metal::float3 _e33 = color_1;\n            color_1 = _e33 + ((_e23 * diffuse_1) * light_1.color.xyz);\n        }\n    }\n    metal::float3 _e42 = color_1;\n    metal::float4 _e47 = u_entity.color;\n    return fs_main_without_storageOutput { metal::float4(_e42, 1.0) * _e47 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-skybox.metal",
    "content": "// language: metal2.1\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct VertexOutput {\n    metal::float4 position;\n    metal::float3 uv;\n};\nstruct Data {\n    metal::float4x4 proj_inv;\n    metal::float4x4 view;\n};\nint naga_div(int lhs, int rhs) {\n    return lhs / metal::select(rhs, 1, (lhs == (-2147483647 - 1) & rhs == -1) | (rhs == 0));\n}\n\n\nstruct vs_mainInput {\n};\nstruct vs_mainOutput {\n    metal::float4 position [[position]];\n    metal::float3 uv [[user(loc0), center_perspective]];\n};\nvertex vs_mainOutput vs_main(\n  uint vertex_index [[vertex_id]]\n, constant Data& r_data [[buffer(0)]]\n) {\n    int tmp1_ = {};\n    int tmp2_ = {};\n    tmp1_ = naga_div(static_cast<int>(vertex_index), 2);\n    tmp2_ = static_cast<int>(vertex_index) & 1;\n    int _e9 = tmp1_;\n    int _e15 = tmp2_;\n    metal::float4 pos = metal::float4((static_cast<float>(_e9) * 4.0) - 1.0, (static_cast<float>(_e15) * 4.0) - 1.0, 0.0, 1.0);\n    metal::float4 _e27 = r_data.view[0];\n    metal::float4 _e32 = r_data.view[1];\n    metal::float4 _e37 = r_data.view[2];\n    metal::float3x3 inv_model_view = metal::transpose(metal::float3x3(_e27.xyz, _e32.xyz, _e37.xyz));\n    metal::float4x4 _e43 = r_data.proj_inv;\n    metal::float4 unprojected = _e43 * pos;\n    const auto _tmp = VertexOutput {pos, inv_model_view * unprojected.xyz};\n    return vs_mainOutput { _tmp.position, _tmp.uv };\n}\n\n\nstruct fs_mainInput {\n    metal::float3 uv [[user(loc0), center_perspective]];\n};\nstruct fs_mainOutput {\n    metal::float4 member_1 [[color(0)]];\n};\nfragment fs_mainOutput fs_main(\n  fs_mainInput varyings_1 [[stage_in]]\n, metal::float4 position [[position]]\n, metal::texturecube<float, metal::access::sample> r_texture [[texture(0)]]\n) {\n    constexpr metal::sampler r_sampler(\n        metal::s_address::clamp_to_edge,\n        metal::t_address::clamp_to_edge,\n        metal::r_address::clamp_to_edge,\n        metal::mag_filter::linear,\n        metal::min_filter::linear,\n        metal::coord::normalized\n    );\n    const VertexOutput in = { position, varyings_1.uv };\n    metal::float4 _e4 = r_texture.sample(r_sampler, in.uv);\n    return fs_mainOutput { _e4 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-standard.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nbool test_any_and_all_for_bool(\n) {\n    return true;\n}\n\nstruct derivativesInput {\n};\nstruct derivativesOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment derivativesOutput derivatives(\n  metal::float4 foo [[position]]\n) {\n    metal::float4 x = {};\n    metal::float4 y = {};\n    metal::float4 z = {};\n    metal::float4 _e1 = metal::dfdx(foo);\n    x = _e1;\n    metal::float4 _e3 = metal::dfdy(foo);\n    y = _e3;\n    metal::float4 _e5 = metal::fwidth(foo);\n    z = _e5;\n    metal::float4 _e7 = metal::dfdx(foo);\n    x = _e7;\n    metal::float4 _e8 = metal::dfdy(foo);\n    y = _e8;\n    metal::float4 _e9 = metal::fwidth(foo);\n    z = _e9;\n    metal::float4 _e10 = metal::dfdx(foo);\n    x = _e10;\n    metal::float4 _e11 = metal::dfdy(foo);\n    y = _e11;\n    metal::float4 _e12 = metal::fwidth(foo);\n    z = _e12;\n    bool _e13 = test_any_and_all_for_bool();\n    metal::float4 _e14 = x;\n    metal::float4 _e15 = y;\n    metal::float4 _e17 = z;\n    return derivativesOutput { (_e14 + _e15) * _e17 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-storage-textures.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void csLoad(\n  metal::texture2d<float, metal::access::read> s_r_r [[user(fake0)]]\n, metal::texture2d<float, metal::access::read> s_rg_r [[user(fake0)]]\n, metal::texture2d<float, metal::access::read> s_rgba_r [[user(fake0)]]\n) {\n    metal::float4 phony = s_r_r.read(metal::uint2(metal::uint2(0u)));\n    metal::float4 phony_1 = s_rg_r.read(metal::uint2(metal::uint2(0u)));\n    metal::float4 phony_2 = s_rgba_r.read(metal::uint2(metal::uint2(0u)));\n    return;\n}\n\n\nkernel void csStore(\n  metal::texture2d<float, metal::access::write> s_r_w [[user(fake0)]]\n, metal::texture2d<float, metal::access::write> s_rg_w [[user(fake0)]]\n, metal::texture2d<float, metal::access::write> s_rgba_w [[user(fake0)]]\n) {\n    s_r_w.write(metal::float4(0.0), metal::uint2(metal::uint2(0u)));\n    s_rg_w.write(metal::float4(0.0), metal::uint2(metal::uint2(0u)));\n    s_rgba_w.write(metal::float4(0.0), metal::uint2(metal::uint2(0u)));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-struct-layout.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct NoPadding {\n    metal::packed_float3 v3_;\n    float f3_;\n};\nstruct NeedsPadding {\n    float f3_forces_padding;\n    char _pad1[12];\n    metal::packed_float3 v3_needs_padding;\n    float f3_;\n};\n\nstruct no_padding_fragInput {\n    metal::float3 v3_ [[user(loc0), center_perspective]];\n    float f3_ [[user(loc1), center_perspective]];\n};\nstruct no_padding_fragOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment no_padding_fragOutput no_padding_frag(\n  no_padding_fragInput varyings [[stage_in]]\n) {\n    const NoPadding input = { varyings.v3_, varyings.f3_ };\n    return no_padding_fragOutput { metal::float4(0.0) };\n}\n\n\nstruct no_padding_vertInput {\n    metal::float3 v3_ [[attribute(0)]];\n    float f3_ [[attribute(1)]];\n};\nstruct no_padding_vertOutput {\n    metal::float4 member_1 [[position]];\n};\nvertex no_padding_vertOutput no_padding_vert(\n  no_padding_vertInput varyings_1 [[stage_in]]\n) {\n    const NoPadding input_1 = { varyings_1.v3_, varyings_1.f3_ };\n    return no_padding_vertOutput { metal::float4(0.0) };\n}\n\n\nkernel void no_padding_comp(\n  constant NoPadding& no_padding_uniform [[user(fake0)]]\n, device NoPadding const& no_padding_storage [[user(fake0)]]\n) {\n    NoPadding x = {};\n    NoPadding _e2 = no_padding_uniform;\n    x = _e2;\n    NoPadding _e4 = no_padding_storage;\n    x = _e4;\n    return;\n}\n\n\nstruct needs_padding_fragInput {\n    float f3_forces_padding [[user(loc0), center_perspective]];\n    metal::float3 v3_needs_padding [[user(loc1), center_perspective]];\n    float f3_ [[user(loc2), center_perspective]];\n};\nstruct needs_padding_fragOutput {\n    metal::float4 member_3 [[color(0)]];\n};\nfragment needs_padding_fragOutput needs_padding_frag(\n  needs_padding_fragInput varyings_3 [[stage_in]]\n) {\n    const NeedsPadding input_2 = { varyings_3.f3_forces_padding, {}, varyings_3.v3_needs_padding, varyings_3.f3_ };\n    return needs_padding_fragOutput { metal::float4(0.0) };\n}\n\n\nstruct needs_padding_vertInput {\n    float f3_forces_padding [[attribute(0)]];\n    metal::float3 v3_needs_padding [[attribute(1)]];\n    float f3_ [[attribute(2)]];\n};\nstruct needs_padding_vertOutput {\n    metal::float4 member_4 [[position]];\n};\nvertex needs_padding_vertOutput needs_padding_vert(\n  needs_padding_vertInput varyings_4 [[stage_in]]\n) {\n    const NeedsPadding input_3 = { varyings_4.f3_forces_padding, {}, varyings_4.v3_needs_padding, varyings_4.f3_ };\n    return needs_padding_vertOutput { metal::float4(0.0) };\n}\n\n\nkernel void needs_padding_comp(\n  constant NeedsPadding& needs_padding_uniform [[user(fake0)]]\n, device NeedsPadding const& needs_padding_storage [[user(fake0)]]\n) {\n    NeedsPadding x_1 = {};\n    NeedsPadding _e2 = needs_padding_uniform;\n    x_1 = _e2;\n    NeedsPadding _e4 = needs_padding_storage;\n    x_1 = _e4;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-subgroup-barrier.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nkernel void main_(\n) {\n    metal::simdgroup_barrier(metal::mem_flags::mem_threadgroup);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-subgroup-operations.metal",
    "content": "// language: metal2.4\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct Structure {\n    uint num_subgroups;\n    uint subgroup_size;\n};\n\nstruct main_Input {\n};\nkernel void main_(\n  uint num_subgroups [[simdgroups_per_threadgroup]]\n, uint subgroup_size [[threads_per_simdgroup]]\n, uint subgroup_id [[simdgroup_index_in_threadgroup]]\n, uint subgroup_invocation_id [[thread_index_in_simdgroup]]\n) {\n    const Structure sizes = { num_subgroups, subgroup_size };\n    metal::uint4 unnamed = metal::uint4((uint64_t)metal::simd_ballot((subgroup_invocation_id & 1u) == 1u), 0, 0, 0);\n    metal::uint4 unnamed_1 = metal::uint4((uint64_t)metal::simd_ballot(true), 0, 0, 0);\n    bool unnamed_2 = metal::simd_all(subgroup_invocation_id != 0u);\n    bool unnamed_3 = metal::simd_any(subgroup_invocation_id == 0u);\n    uint unnamed_4 = metal::simd_sum(subgroup_invocation_id);\n    uint unnamed_5 = metal::simd_product(subgroup_invocation_id);\n    uint unnamed_6 = metal::simd_min(subgroup_invocation_id);\n    uint unnamed_7 = metal::simd_max(subgroup_invocation_id);\n    uint unnamed_8 = metal::simd_and(subgroup_invocation_id);\n    uint unnamed_9 = metal::simd_or(subgroup_invocation_id);\n    uint unnamed_10 = metal::simd_xor(subgroup_invocation_id);\n    uint unnamed_11 = metal::simd_prefix_exclusive_sum(subgroup_invocation_id);\n    uint unnamed_12 = metal::simd_prefix_exclusive_product(subgroup_invocation_id);\n    uint unnamed_13 = metal::simd_prefix_inclusive_sum(subgroup_invocation_id);\n    uint unnamed_14 = metal::simd_prefix_inclusive_product(subgroup_invocation_id);\n    uint unnamed_15 = metal::simd_broadcast_first(subgroup_invocation_id);\n    uint unnamed_16 = metal::simd_broadcast(subgroup_invocation_id, 4u);\n    uint unnamed_17 = metal::simd_shuffle(subgroup_invocation_id, (sizes.subgroup_size - 1u) - subgroup_invocation_id);\n    uint unnamed_18 = metal::simd_shuffle_down(subgroup_invocation_id, 1u);\n    uint unnamed_19 = metal::simd_shuffle_up(subgroup_invocation_id, 1u);\n    uint unnamed_20 = metal::simd_shuffle_xor(subgroup_invocation_id, sizes.subgroup_size - 1u);\n    uint unnamed_21 = metal::quad_broadcast(subgroup_invocation_id, 4u);\n    uint unnamed_22 = metal::quad_shuffle_xor(subgroup_invocation_id, 1u);\n    uint unnamed_23 = metal::quad_shuffle_xor(subgroup_invocation_id, 2u);\n    uint unnamed_24 = metal::quad_shuffle_xor(subgroup_invocation_id, 3u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-texture-arg.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\n\nmetal::float4 test(\n    metal::texture2d<float, metal::access::sample> Passed_Texture,\n    metal::sampler Passed_Sampler\n) {\n    metal::float4 _e5 = Passed_Texture.sample(Passed_Sampler, metal::float2(0.0, 0.0));\n    return _e5;\n}\n\nstruct main_Output {\n    metal::float4 member [[color(0)]];\n};\nfragment main_Output main_(\n  metal::texture2d<float, metal::access::sample> Texture [[user(fake0)]]\n, metal::sampler Sampler [[user(fake0)]]\n) {\n    metal::float4 _e2 = test(Texture, Sampler);\n    return main_Output { _e2 };\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-texture-external.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct NagaExternalTextureTransferFn {\n    float a;\n    float b;\n    float g;\n    float k;\n};\nstruct NagaExternalTextureParams {\n    metal::float4x4 yuv_conversion_matrix;\n    metal::float3x3 gamut_conversion_matrix;\n    NagaExternalTextureTransferFn src_tf;\n    NagaExternalTextureTransferFn dst_tf;\n    metal::float3x2 sample_transform;\n    metal::float3x2 load_transform;\n    metal::uint2 size;\n    uint num_planes;\n    char _pad8[4];\n};\nstruct NagaExternalTextureWrapper {\n    metal::texture2d<float, metal::access::sample> plane0;\n    metal::texture2d<float, metal::access::sample> plane1;\n    metal::texture2d<float, metal::access::sample> plane2;\n    NagaExternalTextureParams params;\n};\n\nfloat4 nagaTextureSampleBaseClampToEdge(NagaExternalTextureWrapper tex, metal::sampler samp, float2 coords) {\n    uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());\n    coords = tex.params.sample_transform * float3(coords, 1.0);\n    float2 bounds_min = tex.params.sample_transform * float3(0.0, 0.0, 1.0);\n    float2 bounds_max = tex.params.sample_transform * float3(1.0, 1.0, 1.0);\n    float4 bounds = float4(metal::min(bounds_min, bounds_max), metal::max(bounds_min, bounds_max));\n    float2 plane0_half_texel = float2(0.5, 0.5) / float2(plane0_size);\n    float2 plane0_coords = metal::clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);\n    if (tex.params.num_planes == 1u) {\n        return tex.plane0.sample(samp, plane0_coords, metal::level(0.0f));\n    } else {\n        uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());\n        float2 plane1_half_texel = float2(0.5, 0.5) / float2(plane1_size);\n        float2 plane1_coords = metal::clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);\n        float y = tex.plane0.sample(samp, plane0_coords, metal::level(0.0f)).r;\n        float2 uv = float2(0.0, 0.0);\n        if (tex.params.num_planes == 2u) {\n            uv = tex.plane1.sample(samp, plane1_coords, metal::level(0.0f)).xy;\n        } else {\n            uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());\n            float2 plane2_half_texel = float2(0.5, 0.5) / float2(plane2_size);\n            float2 plane2_coords = metal::clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane1_half_texel);\n            uv.x = tex.plane1.sample(samp, plane1_coords, metal::level(0.0f)).x;\n            uv.y = tex.plane2.sample(samp, plane2_coords, metal::level(0.0f)).x;\n        }\n        float3 srcGammaRgb = (tex.params.yuv_conversion_matrix * float4(y, uv, 1.0)).rgb;\n        float3 srcLinearRgb = metal::select(\n            metal::pow((srcGammaRgb + tex.params.src_tf.a - 1.0) / tex.params.src_tf.a, tex.params.src_tf.g),\n            srcGammaRgb / tex.params.src_tf.k,\n            srcGammaRgb < tex.params.src_tf.k * tex.params.src_tf.b);\n        float3 dstLinearRgb = tex.params.gamut_conversion_matrix * srcLinearRgb;\n        float3 dstGammaRgb = metal::select(\n            tex.params.dst_tf.a * metal::pow(dstLinearRgb, 1.0 / tex.params.dst_tf.g) - (tex.params.dst_tf.a - 1),\n            tex.params.dst_tf.k * dstLinearRgb,\n            dstLinearRgb < tex.params.dst_tf.b);\n        return float4(dstGammaRgb, 1.0);\n    }\n}\n\nfloat4 nagaTextureLoadExternal(NagaExternalTextureWrapper tex, uint2 coords) {\n    uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());\n    uint2 cropped_size = metal::any(tex.params.size != 0) ? tex.params.size : plane0_size;\n    coords = metal::min(coords, cropped_size - 1);\n    uint2 plane0_coords = uint2(metal::round(tex.params.load_transform * float3(float2(coords), 1.0)));\n    if (tex.params.num_planes == 1u) {\n        return tex.plane0.read(plane0_coords);\n    } else {\n        uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());\n        uint2 plane1_coords = uint2(metal::floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));\n        float y = tex.plane0.read(plane0_coords).x;\n        float2 uv;\n        if (tex.params.num_planes == 2u) {\n            uv = tex.plane1.read(plane1_coords).xy;\n        } else {\n        uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());\n        uint2 plane2_coords = uint2(metal::floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));\n            uv = float2(tex.plane1.read(plane1_coords).x, tex.plane2.read(plane2_coords).x);\n        }\n        float3 srcGammaRgb = (tex.params.yuv_conversion_matrix * float4(y, uv, 1.0)).rgb;\n        float3 srcLinearRgb = metal::select(\n            metal::pow((srcGammaRgb + tex.params.src_tf.a - 1.0) / tex.params.src_tf.a, tex.params.src_tf.g),\n            srcGammaRgb / tex.params.src_tf.k,\n            srcGammaRgb < tex.params.src_tf.k * tex.params.src_tf.b);\n        float3 dstLinearRgb = tex.params.gamut_conversion_matrix * srcLinearRgb;\n        float3 dstGammaRgb = metal::select(\n            tex.params.dst_tf.a * metal::pow(dstLinearRgb, 1.0 / tex.params.dst_tf.g) - (tex.params.dst_tf.a - 1),\n            tex.params.dst_tf.k * dstLinearRgb,\n            dstLinearRgb < tex.params.dst_tf.b);\n        return float4(dstGammaRgb, 1.0);\n    }\n}\n\nuint2 nagaTextureDimensionsExternal(NagaExternalTextureWrapper tex) {\n    if (metal::any(tex.params.size != uint2(0u))) {\n        return tex.params.size;\n    } else {\n        return uint2(tex.plane0.get_width(), tex.plane0.get_height());\n    }\n}\n\nmetal::float4 test(\n    NagaExternalTextureWrapper t,\n    metal::sampler samp\n) {\n    metal::float4 a = {};\n    metal::float4 b = {};\n    metal::float4 c = {};\n    metal::uint2 d = {};\n    metal::float4 _e4 = nagaTextureSampleBaseClampToEdge(t, samp, metal::float2(0.0));\n    a = _e4;\n    metal::float4 _e8 = nagaTextureLoadExternal(t, metal::uint2(metal::int2(0)));\n    b = _e8;\n    metal::float4 _e12 = nagaTextureLoadExternal(t, metal::uint2(metal::uint2(0u)));\n    c = _e12;\n    d = nagaTextureDimensionsExternal(t);\n    metal::float4 _e16 = a;\n    metal::float4 _e17 = b;\n    metal::float4 _e19 = c;\n    metal::uint2 _e21 = d;\n    return ((_e16 + _e17) + _e19) + static_cast<metal::float2>(_e21).xyxy;\n}\n\nstruct fragment_mainOutput {\n    metal::float4 member [[color(0)]];\n};\nfragment fragment_mainOutput fragment_main(\n  metal::texture2d<float, metal::access::sample> tex_plane0_ [[texture(0)]]\n, metal::texture2d<float, metal::access::sample> tex_plane1_ [[texture(1)]]\n, metal::texture2d<float, metal::access::sample> tex_plane2_ [[texture(2)]]\n, constant NagaExternalTextureParams& tex_params [[buffer(0)]]\n, metal::sampler samp [[sampler(0)]]\n) {\n    const NagaExternalTextureWrapper tex {\n        .plane0 = tex_plane0_,\n        .plane1 = tex_plane1_,\n        .plane2 = tex_plane2_,\n        .params = tex_params,\n    };\n    metal::float4 _e1 = test(tex, samp);\n    return fragment_mainOutput { _e1 };\n}\n\n\nstruct vertex_mainOutput {\n    metal::float4 member_1 [[position]];\n};\nvertex vertex_mainOutput vertex_main(\n  metal::texture2d<float, metal::access::sample> tex_plane0_ [[texture(0)]]\n, metal::texture2d<float, metal::access::sample> tex_plane1_ [[texture(1)]]\n, metal::texture2d<float, metal::access::sample> tex_plane2_ [[texture(2)]]\n, constant NagaExternalTextureParams& tex_params [[buffer(0)]]\n, metal::sampler samp [[sampler(0)]]\n) {\n    const NagaExternalTextureWrapper tex {\n        .plane0 = tex_plane0_,\n        .plane1 = tex_plane1_,\n        .plane2 = tex_plane2_,\n        .params = tex_params,\n    };\n    metal::float4 _e1 = test(tex, samp);\n    return vertex_mainOutput { _e1 };\n}\n\n\nkernel void compute_main(\n  metal::texture2d<float, metal::access::sample> tex_plane0_ [[texture(0)]]\n, metal::texture2d<float, metal::access::sample> tex_plane1_ [[texture(1)]]\n, metal::texture2d<float, metal::access::sample> tex_plane2_ [[texture(2)]]\n, constant NagaExternalTextureParams& tex_params [[buffer(0)]]\n, metal::sampler samp [[sampler(0)]]\n) {\n    const NagaExternalTextureWrapper tex {\n        .plane0 = tex_plane0_,\n        .plane1 = tex_plane1_,\n        .plane2 = tex_plane2_,\n        .params = tex_params,\n    };\n    metal::float4 _e1 = test(tex, samp);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-type-inference.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nconstant uint g1_ = 1u;\nconstant float g3_ = 1.0;\nconstant metal::int4 g4_ = metal::int4 {};\nconstant metal::int4 g5_ = metal::int4(1);\nconstant metal::float2x2 g6_ = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n\nkernel void main_(\n) {\n    int g0x = 1;\n    float g2x = 1.0;\n    metal::float2x2 g7x = metal::float2x2(metal::float2(1.0, 1.0), metal::float2(1.0, 1.0));\n    int c0x = 1;\n    uint c1x = 1u;\n    float c2x = 1.0;\n    float c3x = 1.0;\n    metal::int4 c4x = metal::int4 {};\n    metal::int4 c5x = metal::int4(1);\n    metal::float2x2 c6x = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 c7x = metal::float2x2(metal::float2(1.0, 1.0), metal::float2(1.0, 1.0));\n    int l0x = {};\n    uint l1x = {};\n    float l2x = {};\n    float l3x = {};\n    metal::int4 l4x = {};\n    int v0_ = 1;\n    uint v1_ = 1u;\n    float v2_ = 1.0;\n    float v3_ = 1.0;\n    metal::int4 v4_ = metal::int4 {};\n    metal::int4 v5_ = metal::int4(1);\n    metal::float2x2 v6_ = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 v7_ = metal::float2x2(metal::float2(1.0, 1.0), metal::float2(1.0, 1.0));\n    metal::int4 l5_ = metal::int4(1);\n    metal::float2x2 l6_ = metal::float2x2(metal::float2(0.0, 0.0), metal::float2(0.0, 0.0));\n    metal::float2x2 l7_ = metal::float2x2(metal::float2(1.0, 1.0), metal::float2(1.0, 1.0));\n    l0x = 1;\n    l1x = 1u;\n    l2x = 1.0;\n    l3x = 1.0;\n    l4x = metal::int4 {};\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-workgroup-uniform-load-atomic.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_4 {\n    metal::atomic_int inner[2];\n};\nstruct AtomicStruct {\n    metal::atomic_uint atomic_scalar;\n    type_4 atomic_arr;\n};\n\nstruct test_atomic_workgroup_uniform_loadInput {\n};\nkernel void test_atomic_workgroup_uniform_load(\n  metal::uint3 workgroup_id [[threadgroup_position_in_grid]]\n, metal::uint3 local_id [[thread_position_in_threadgroup]]\n, threadgroup metal::atomic_uint& wg_scalar\n, threadgroup metal::atomic_int& wg_signed\n, threadgroup AtomicStruct& wg_struct\n) {\n    if (metal::all(local_id == metal::uint3(0u))) {\n        metal::atomic_store_explicit(&wg_scalar, 0, metal::memory_order_relaxed);\n        metal::atomic_store_explicit(&wg_signed, 0, metal::memory_order_relaxed);\n        metal::atomic_store_explicit(&wg_struct.atomic_scalar, 0, metal::memory_order_relaxed);\n        for (int __i0 = 0; __i0 < 2; __i0++) {\n            metal::atomic_store_explicit(&wg_struct.atomic_arr.inner[__i0], 0, metal::memory_order_relaxed);\n        }\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    bool local = {};\n    bool local_1 = {};\n    bool local_2 = {};\n    uint active_tile_index = workgroup_id.x + (workgroup_id.y * 32768u);\n    uint _e11 = metal::atomic_fetch_or_explicit(&wg_scalar, static_cast<uint>(active_tile_index >= 64u), metal::memory_order_relaxed);\n    int _e14 = metal::atomic_fetch_add_explicit(&wg_signed, 1, metal::memory_order_relaxed);\n    metal::atomic_store_explicit(&wg_struct.atomic_scalar, 1u, metal::memory_order_relaxed);\n    int _e22 = metal::atomic_fetch_add_explicit(&wg_struct.atomic_arr.inner[0], 1, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint unnamed = metal::atomic_load_explicit(&wg_scalar, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    int unnamed_1 = metal::atomic_load_explicit(&wg_signed, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    uint unnamed_2 = metal::atomic_load_explicit(&wg_struct.atomic_scalar, metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    int unnamed_3 = metal::atomic_load_explicit(&wg_struct.atomic_arr.inner[0], metal::memory_order_relaxed);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    if (unnamed == 0u) {\n        local = unnamed_1 > 0;\n    } else {\n        local = false;\n    }\n    bool _e41 = local;\n    if (_e41) {\n        local_1 = unnamed_2 > 0u;\n    } else {\n        local_1 = false;\n    }\n    bool _e47 = local_1;\n    if (_e47) {\n        local_2 = unnamed_3 > 0;\n    } else {\n        local_2 = false;\n    }\n    bool _e53 = local_2;\n    if (_e53) {\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-workgroup-uniform-load.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_2 {\n    int inner[128];\n};\nconstant uint SIZE = 128u;\n\nstruct test_workgroupUniformLoadInput {\n};\nkernel void test_workgroupUniformLoad(\n  metal::uint3 workgroup_id [[threadgroup_position_in_grid]]\n, metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, threadgroup type_2& arr_i32_\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        arr_i32_ = {};\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    int unnamed = arr_i32_.inner[workgroup_id.x];\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    if (unnamed > 10) {\n        metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/msl/wgsl-workgroup-var-init.metal",
    "content": "// language: metal1.0\n#include <metal_stdlib>\n#include <simd/simd.h>\n\nusing metal::uint;\n\nstruct type_1 {\n    uint inner[512];\n};\nstruct type_3 {\n    metal::atomic_int inner[8];\n};\nstruct type_4 {\n    type_3 inner[8];\n};\nstruct WStruct {\n    type_1 arr;\n    metal::atomic_int atom;\n    type_4 atom_arr;\n};\n\nkernel void main_(\n  metal::uint3 __local_invocation_id [[thread_position_in_threadgroup]]\n, threadgroup WStruct& w_mem\n, device type_1& output [[buffer(0)]]\n) {\n    if (metal::all(__local_invocation_id == metal::uint3(0u))) {\n        w_mem.arr = {};\n        metal::atomic_store_explicit(&w_mem.atom, 0, metal::memory_order_relaxed);\n        for (int __i0 = 0; __i0 < 8; __i0++) {\n            for (int __i1 = 0; __i1 < 8; __i1++) {\n                metal::atomic_store_explicit(&w_mem.atom_arr.inner[__i0].inner[__i1], 0, metal::memory_order_relaxed);\n            }\n        }\n    }\n    metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);\n    type_1 _e3 = w_mem.arr;\n    output = _e3;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/spv/spv-barrier.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 17\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %14 \"main\"\nOpExecutionMode %14 LocalSize 64 1 1\n%2 = OpTypeVoid\n%5 = OpTypeFunction %2\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  2\n%9 = OpConstant  %8  264\n%10 = OpConstant  %8  1\n%11 = OpConstant  %8  2120\n%12 = OpConstant  %8  2376\n%4 = OpFunction  %2  None %5\n%3 = OpLabel\nOpBranch %6\n%6 = OpLabel\nOpMemoryBarrier %7 %9\nOpControlBarrier %7 %7 %9\nOpMemoryBarrier %10 %11\nOpControlBarrier %7 %10 %11\nOpMemoryBarrier %10 %12\nOpControlBarrier %7 %10 %12\nOpReturn\nOpFunctionEnd\n%14 = OpFunction  %2  None %5\n%13 = OpLabel\nOpBranch %15\n%15 = OpLabel\n%16 = OpFunctionCall  %2  %4\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/spv-fetch_depth.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 46\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %40 \"cull::fetch_depth\"\nOpExecutionMode %40 LocalSize 32 1 1\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %8 0 Offset 0\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 0\nOpDecorate %12 Block\nOpMemberDecorate %12 0 Offset 0\nOpDecorate %14 NonWritable\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 1\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 2\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeInt 32 1\n%5 = OpTypeStruct %3\n%7 = OpTypeInt 32 0\n%6 = OpTypeVector %7 2\n%8 = OpTypeStruct %6\n%9 = OpTypeImage %3 2D 1 0 0 1 Unknown\n%10 = OpConstant  %4  0\n%12 = OpTypeStruct %5\n%13 = OpTypePointer StorageBuffer %12\n%11 = OpVariable  %13  StorageBuffer\n%15 = OpTypeStruct %8\n%16 = OpTypePointer StorageBuffer %15\n%14 = OpVariable  %16  StorageBuffer\n%18 = OpTypePointer UniformConstant %9\n%17 = OpVariable  %18  UniformConstant\n%21 = OpTypeFunction %2\n%22 = OpTypePointer StorageBuffer %5\n%23 = OpConstant  %7  0\n%25 = OpTypePointer StorageBuffer %8\n%29 = OpTypePointer StorageBuffer %3\n%30 = OpTypePointer StorageBuffer %6\n%33 = OpTypeVector %3 4\n%20 = OpFunction  %2  None %21\n%19 = OpLabel\n%24 = OpAccessChain  %22  %11 %23\n%26 = OpAccessChain  %25  %14 %23\n%27 = OpLoad  %9  %17\nOpBranch %28\n%28 = OpLabel\n%31 = OpAccessChain  %30  %26 %23\n%32 = OpLoad  %6  %31\n%34 = OpImageFetch  %33  %27 %32 Lod %10\n%35 = OpCompositeExtract  %3  %34 0\n%36 = OpCompositeConstruct  %33  %35 %35 %35 %35\n%37 = OpCompositeExtract  %3  %36 0\n%38 = OpAccessChain  %29  %24 %23\nOpStore %38 %37\nOpReturn\nOpFunctionEnd\n%40 = OpFunction  %2  None %21\n%39 = OpLabel\n%41 = OpAccessChain  %22  %11 %23\n%42 = OpAccessChain  %25  %14 %23\n%43 = OpLoad  %9  %17\nOpBranch %44\n%44 = OpLabel\n%45 = OpFunctionCall  %2  %20\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/spv-per-vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 34\nOpCapability Shader\nOpCapability FragmentBarycentricKHR\nOpExtension \"SPV_KHR_fragment_shader_barycentric\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %30 \"fs_main\" %25 %28\nOpExecutionMode %30 OriginUpperLeft\nOpDecorate %4 ArrayStride 4\nOpDecorate %25 Location 0\nOpDecorate %25 PerVertexKHR\nOpDecorate %28 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  3\n%4 = OpTypeArray %3 %5\n%7 = OpTypeVector %3 4\n%8 = OpConstant  %3  1\n%10 = OpTypePointer Private %4\n%11 = OpConstantNull  %4\n%9 = OpVariable  %10  Private %11\n%13 = OpTypePointer Private %7\n%14 = OpConstantNull  %7\n%12 = OpVariable  %13  Private %14\n%17 = OpTypeFunction %2\n%26 = OpTypePointer Input %4\n%25 = OpVariable  %26  Input\n%29 = OpTypePointer Output %7\n%28 = OpVariable  %29  Output\n%16 = OpFunction  %2  None %17\n%15 = OpLabel\nOpBranch %18\n%18 = OpLabel\n%19 = OpLoad  %4  %9\n%20 = OpCompositeExtract  %3  %19 0\n%21 = OpCompositeExtract  %3  %19 1\n%22 = OpCompositeExtract  %3  %19 2\n%23 = OpCompositeConstruct  %7  %20 %21 %22 %8\nOpStore %12 %23\nOpReturn\nOpFunctionEnd\n%30 = OpFunction  %2  None %17\n%24 = OpLabel\n%27 = OpLoad  %4  %25\nOpBranch %31\n%31 = OpLabel\nOpStore %9 %27\n%32 = OpFunctionCall  %2  %16\n%33 = OpLoad  %7  %12\nOpStore %28 %33\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/spv-subgroup-barrier.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 14\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %11 \"main\"\nOpExecutionMode %11 LocalSize 64 1 1\n%2 = OpTypeVoid\n%5 = OpTypeFunction %2\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  3\n%9 = OpConstant  %8  136\n%4 = OpFunction  %2  None %5\n%3 = OpLabel\nOpBranch %6\n%6 = OpLabel\nOpMemoryBarrier %7 %9\nOpControlBarrier %7 %7 %9\nOpReturn\nOpFunctionEnd\n%11 = OpFunction  %2  None %5\n%10 = OpLabel\nOpBranch %12\n%12 = OpLabel\n%13 = OpFunctionCall  %2  %4\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-6220-break-from-loop.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 50\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %47 \"main\"\nOpExecutionMode %47 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%6 = OpTypeFunction %2\n%7 = OpConstant  %3  0\n%8 = OpConstant  %3  4\n%9 = OpConstant  %3  1\n%11 = OpTypePointer Function %3\n%17 = OpTypeInt 32 0\n%18 = OpTypeVector %17 2\n%19 = OpTypePointer Function %18\n%20 = OpTypeBool\n%21 = OpTypeVector %20 2\n%22 = OpConstant  %17  0\n%23 = OpConstantComposite  %18  %22 %22\n%24 = OpConstant  %17  1\n%25 = OpConstant  %17  4294967295\n%26 = OpConstantComposite  %18  %25 %25\n%5 = OpFunction  %2  None %6\n%4 = OpLabel\n%10 = OpVariable  %11  Function %7\n%27 = OpVariable  %19  Function %26\nOpBranch %12\n%12 = OpLabel\nOpBranch %13\n%13 = OpLabel\nOpLoopMerge %14 %16 None\nOpBranch %28\n%28 = OpLabel\n%29 = OpLoad  %18  %27\n%30 = OpIEqual  %21  %23 %29\n%31 = OpAll  %20  %30\nOpSelectionMerge %32 None\nOpBranchConditional %31 %14 %32\n%32 = OpLabel\n%33 = OpCompositeExtract  %17  %29 1\n%34 = OpIEqual  %20  %33 %22\n%35 = OpSelect  %17  %34 %24 %22\n%36 = OpCompositeConstruct  %18  %35 %24\n%37 = OpISub  %18  %29 %36\nOpStore %27 %37\nOpBranch %15\n%15 = OpLabel\n%38 = OpLoad  %3  %10\n%39 = OpSLessThan  %20  %38 %8\nOpSelectionMerge %40 None\nOpBranchConditional %39 %40 %41\n%41 = OpLabel\nOpBranch %14\n%40 = OpLabel\nOpBranch %42\n%42 = OpLabel\nOpBranch %14\n%16 = OpLabel\n%44 = OpLoad  %3  %10\n%45 = OpIAdd  %3  %44 %9\nOpStore %10 %45\nOpBranch %13\n%14 = OpLabel\nOpReturn\nOpFunctionEnd\n%47 = OpFunction  %2  None %6\n%46 = OpLabel\nOpBranch %48\n%48 = OpLabel\n%49 = OpFunctionCall  %2  %5\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 36\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %15 \"vs\" %8 %11 %13\nOpEntryPoint Fragment %33 \"fs\" %32\nOpExecutionMode %33 OriginUpperLeft\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 16\nOpDecorate %8 Location 0\nOpDecorate %11 BuiltIn Position\nOpDecorate %13 Location 0\nOpDecorate %32 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%5 = OpTypeVector %4 2\n%6 = OpTypeStruct %3 %5\n%9 = OpTypePointer Input %5\n%8 = OpVariable  %9  Input\n%12 = OpTypePointer Output %3\n%11 = OpVariable  %12  Output\n%14 = OpTypePointer Output %5\n%13 = OpVariable  %14  Output\n%16 = OpTypeFunction %2\n%17 = OpConstant  %4  0\n%18 = OpConstant  %4  1\n%20 = OpTypePointer Function %6\n%21 = OpConstantNull  %6\n%23 = OpTypePointer Function %3\n%26 = OpTypeInt 32 0\n%25 = OpConstant  %26  0\n%32 = OpVariable  %12  Output\n%34 = OpConstantComposite  %3  %18 %17 %17 %18\n%15 = OpFunction  %2  None %16\n%7 = OpLabel\n%19 = OpVariable  %20  Function %21\n%10 = OpLoad  %5  %8\nOpBranch %22\n%22 = OpLabel\n%24 = OpCompositeConstruct  %3  %10 %17 %18\n%27 = OpAccessChain  %23  %19 %25\nOpStore %27 %24\n%28 = OpLoad  %6  %19\n%29 = OpCompositeExtract  %3  %28 0\nOpStore %11 %29\n%30 = OpCompositeExtract  %5  %28 1\nOpStore %13 %30\nOpReturn\nOpFunctionEnd\n%33 = OpFunction  %2  None %16\n%31 = OpLabel\nOpBranch %35\n%35 = OpLabel\nOpStore %32 %34\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-6772-unpack-expr-accesses.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 23\nOpCapability Shader\nOpCapability Int8\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %4 \"main\"\nOpExecutionMode %4 LocalSize 1 1 1\n%2 = OpTypeVoid\n%5 = OpTypeFunction %2\n%6 = OpTypeInt 32 1\n%7 = OpConstant  %6  2\n%8 = OpTypeInt 32 0\n%9 = OpConstant  %8  12\n%11 = OpTypeVector %6 4\n%14 = OpTypeInt 8 1\n%13 = OpTypeVector %14 4\n%17 = OpTypeVector %8 4\n%20 = OpTypeInt 8 0\n%19 = OpTypeVector %20 4\n%4 = OpFunction  %2  None %5\n%3 = OpLabel\nOpBranch %10\n%10 = OpLabel\n%15 = OpBitcast  %13  %9\n%12 = OpSConvert  %11  %15\n%16 = OpCompositeExtract  %6  %12 2\n%21 = OpBitcast  %19  %9\n%18 = OpUConvert  %17  %21\n%22 = OpCompositeExtract  %8  %18 1\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 39\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %11 \"f\"\nOpExecutionMode %11 LocalSize 1 1 1\nOpDecorate %5 ArrayStride 16\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 3\n%7 = OpTypeInt 32 0\n%6 = OpConstant  %7  2\n%5 = OpTypeArray %3 %6\n%8 = OpTypeVector %4 4\n%9 = OpTypeInt 32 1\n%12 = OpTypeFunction %2\n%13 = OpConstantNull  %5\n%14 = OpConstant  %4  0\n%15 = OpConstantComposite  %8  %14 %14 %14 %14\n%16 = OpConstant  %9  0\n%18 = OpTypePointer Function %8\n%20 = OpTypePointer Function %9\n%23 = OpTypePointer Function %4\n%24 = OpConstant  %7  0\n%28 = OpTypePointer Function %5\n%30 = OpConstant  %7  1\n%11 = OpFunction  %2  None %12\n%10 = OpLabel\n%17 = OpVariable  %18  Function %15\n%19 = OpVariable  %20  Function %16\n%21 = OpVariable  %20  Function %16\n%29 = OpVariable  %28  Function\nOpBranch %22\n%22 = OpLabel\n%25 = OpAccessChain  %23  %17 %24\n%26 = OpLoad  %4  %25\n%27 = OpLoad  %9  %21\nOpStore %29 %13\n%31 = OpAccessChain  %23  %29 %27 %30\n%32 = OpLoad  %4  %31\n%33 = OpLoad  %9  %19\nOpStore %29 %13\n%34 = OpAccessChain  %23  %29 %33 %6\n%35 = OpLoad  %4  %34\n%36 = OpFMul  %4  %32 %35\n%37 = OpFAdd  %4  %26 %36\n%38 = OpAccessChain  %23  %17 %24\nOpStore %38 %37\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-7048-multiple-dynamic-2.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 33\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %13 \"fs_main\" %11\nOpExecutionMode %13 OriginUpperLeft\nOpDecorate %6 ArrayStride 8\nOpDecorate %11 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%5 = OpTypeVector %4 2\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  2\n%6 = OpTypeArray %5 %7\n%9 = OpTypeInt 32 1\n%12 = OpTypePointer Output %3\n%11 = OpVariable  %12  Output\n%14 = OpTypeFunction %2\n%15 = OpConstant  %4  0\n%16 = OpConstantComposite  %5  %15 %15\n%17 = OpConstantComposite  %6  %16 %16\n%18 = OpConstant  %9  0\n%20 = OpTypePointer Function %9\n%23 = OpTypePointer Function %6\n%25 = OpTypePointer Function %5\n%13 = OpFunction  %2  None %14\n%10 = OpLabel\n%19 = OpVariable  %20  Function %18\n%24 = OpVariable  %23  Function\nOpBranch %21\n%21 = OpLabel\n%22 = OpLoad  %9  %19\nOpStore %24 %17\n%26 = OpAccessChain  %25  %24 %22\n%27 = OpLoad  %5  %26\n%28 = OpLoad  %9  %19\nOpStore %24 %17\n%29 = OpAccessChain  %25  %24 %28\n%30 = OpLoad  %5  %29\n%31 = OpFMul  %5  %27 %30\n%32 = OpVectorShuffle  %3  %31 %31 0 0 1 1\nOpStore %11 %32\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-7048-multiple-dynamic-3.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 97\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %90 \"main\"\nOpExecutionMode %90 OriginUpperLeft\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpDecorate %6 ArrayStride 16\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 3\n%5 = OpTypeStruct %3 %4\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  12\n%6 = OpTypeArray %4 %7\n%13 = OpTypeFunction %5 %6 %8\n%14 = OpConstant  %8  0\n%15 = OpConstant  %8  1\n%16 = OpConstant  %3  0\n%17 = OpConstantComposite  %4  %16 %16 %16\n%18 = OpConstantComposite  %5  %16 %17\n%20 = OpTypePointer Function %8\n%22 = OpTypePointer Function %4\n%23 = OpConstantNull  %4\n%26 = OpConstantNull  %4\n%32 = OpTypeVector %8 2\n%33 = OpTypePointer Function %32\n%34 = OpTypeBool\n%35 = OpTypeVector %34 2\n%36 = OpConstantComposite  %32  %14 %14\n%37 = OpConstant  %8  4294967295\n%38 = OpConstantComposite  %32  %37 %37\n%57 = OpTypePointer Function %6\n%91 = OpTypeFunction %2\n%93 = OpConstantNull  %6\n%12 = OpFunction  %5  None %13\n%10 = OpFunctionParameter  %6\n%11 = OpFunctionParameter  %8\n%9 = OpLabel\n%21 = OpVariable  %22  Function %23\n%25 = OpVariable  %22  Function %26\n%19 = OpVariable  %20  Function %14\n%24 = OpVariable  %20  Function %14\n%39 = OpVariable  %33  Function %38\n%67 = OpVariable  %33  Function %38\n%58 = OpVariable  %57  Function\nOpBranch %27\n%27 = OpLabel\nOpBranch %28\n%28 = OpLabel\nOpLoopMerge %29 %31 None\nOpBranch %40\n%40 = OpLabel\n%41 = OpLoad  %32  %39\n%42 = OpIEqual  %35  %36 %41\n%43 = OpAll  %34  %42\nOpSelectionMerge %44 None\nOpBranchConditional %43 %29 %44\n%44 = OpLabel\n%45 = OpCompositeExtract  %8  %41 1\n%46 = OpIEqual  %34  %45 %14\n%47 = OpSelect  %8  %46 %15 %14\n%48 = OpCompositeConstruct  %32  %47 %15\n%49 = OpISub  %32  %41 %48\nOpStore %39 %49\nOpBranch %30\n%30 = OpLabel\n%50 = OpLoad  %8  %19\n%51 = OpULessThan  %34  %50 %11\nOpSelectionMerge %52 None\nOpBranchConditional %51 %52 %53\n%53 = OpLabel\nOpBranch %29\n%52 = OpLabel\nOpBranch %54\n%54 = OpLabel\n%56 = OpLoad  %8  %19\nOpStore %58 %10\n%59 = OpAccessChain  %22  %58 %56\n%60 = OpLoad  %4  %59\nOpStore %21 %60\nOpBranch %55\n%55 = OpLabel\nOpBranch %31\n%31 = OpLabel\n%61 = OpLoad  %8  %19\n%62 = OpIAdd  %8  %61 %15\nOpStore %19 %62\nOpBranch %28\n%29 = OpLabel\nOpBranch %63\n%63 = OpLabel\nOpLoopMerge %64 %66 None\nOpBranch %68\n%68 = OpLabel\n%69 = OpLoad  %32  %67\n%70 = OpIEqual  %35  %36 %69\n%71 = OpAll  %34  %70\nOpSelectionMerge %72 None\nOpBranchConditional %71 %64 %72\n%72 = OpLabel\n%73 = OpCompositeExtract  %8  %69 1\n%74 = OpIEqual  %34  %73 %14\n%75 = OpSelect  %8  %74 %15 %14\n%76 = OpCompositeConstruct  %32  %75 %15\n%77 = OpISub  %32  %69 %76\nOpStore %67 %77\nOpBranch %65\n%65 = OpLabel\n%78 = OpLoad  %8  %24\n%79 = OpULessThan  %34  %78 %11\nOpSelectionMerge %80 None\nOpBranchConditional %79 %80 %81\n%81 = OpLabel\nOpBranch %64\n%80 = OpLabel\nOpBranch %82\n%82 = OpLabel\n%84 = OpLoad  %8  %24\nOpStore %58 %10\n%85 = OpAccessChain  %22  %58 %84\n%86 = OpLoad  %4  %85\nOpStore %25 %86\nOpBranch %83\n%83 = OpLabel\nOpBranch %66\n%66 = OpLabel\n%87 = OpLoad  %8  %24\n%88 = OpIAdd  %8  %87 %15\nOpStore %24 %88\nOpBranch %63\n%64 = OpLabel\nOpReturnValue %18\nOpFunctionEnd\n%90 = OpFunction  %2  None %91\n%89 = OpLabel\n%92 = OpVariable  %57  Function %93\nOpBranch %94\n%94 = OpLabel\n%95 = OpLoad  %6  %92\n%96 = OpFunctionCall  %5  %12 %95 %15\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-7995-unicode-idents.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 24\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %19 \"main\"\nOpExecutionMode %19 LocalSize 1 1 1\nOpDecorate %4 NonWritable\nOpDecorate %4 DescriptorSet 0\nOpDecorate %4 Binding 0\nOpDecorate %5 Block\nOpMemberDecorate %5 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%5 = OpTypeStruct %3\n%6 = OpTypePointer StorageBuffer %5\n%4 = OpVariable  %6  StorageBuffer\n%9 = OpTypeFunction %3\n%10 = OpTypePointer StorageBuffer %3\n%12 = OpTypeInt 32 0\n%11 = OpConstant  %12  0\n%14 = OpConstant  %3  9001\n%20 = OpTypeFunction %2\n%8 = OpFunction  %3  None %9\n%7 = OpLabel\n%13 = OpAccessChain  %10  %4 %11\nOpBranch %15\n%15 = OpLabel\n%16 = OpLoad  %3  %13\n%17 = OpFAdd  %3  %16 %14\nOpReturnValue %17\nOpFunctionEnd\n%19 = OpFunction  %2  None %20\n%18 = OpLabel\n%21 = OpAccessChain  %10  %4 %11\nOpBranch %22\n%22 = OpLabel\n%23 = OpFunctionCall  %3  %8\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-8820-multiple-local-invocation-index-id.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 34\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %16 \"compute1\" %10 %13\nOpExecutionMode %16 LocalSize 1 1 1\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 12\nOpDecorate %10 BuiltIn LocalInvocationId\nOpDecorate %13 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeVector %3 3\n%5 = OpTypeStruct %4 %3\n%7 = OpTypePointer Workgroup %3\n%6 = OpVariable  %7  Workgroup\n%11 = OpTypePointer Input %4\n%10 = OpVariable  %11  Input\n%14 = OpTypePointer Input %3\n%13 = OpVariable  %14  Input\n%17 = OpTypeFunction %2\n%18 = OpConstant  %3  2\n%20 = OpConstantNull  %3\n%21 = OpConstant  %3  0\n%23 = OpTypeBool\n%26 = OpConstant  %3  264\n%16 = OpFunction  %2  None %17\n%8 = OpLabel\n%12 = OpLoad  %4  %10\n%15 = OpLoad  %3  %13\n%9 = OpCompositeConstruct  %5  %12 %15\nOpBranch %19\n%19 = OpLabel\n%22 = OpIEqual  %23  %15 %21\nOpSelectionMerge %24 None\nOpBranchConditional %22 %25 %24\n%25 = OpLabel\nOpStore %6 %20\nOpBranch %24\n%24 = OpLabel\nOpControlBarrier %18 %18 %26\nOpBranch %27\n%27 = OpLabel\n%28 = OpCompositeExtract  %3  %9 1\n%29 = OpIMul  %3  %28 %18\nOpStore %6 %29\n%30 = OpLoad  %3  %6\n%31 = OpCompositeExtract  %4  %9 0\n%32 = OpCompositeExtract  %3  %31 0\n%33 = OpIAdd  %3  %30 %32\nOpStore %6 %33\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-builtins.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 68\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %6 \"f\"\nOpExecutionMode %6 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeFloat 32\n%7 = OpTypeFunction %2\n%8 = OpConstant  %3  1\n%9 = OpConstant  %4  1\n%11 = OpTypePointer Function %3\n%13 = OpTypePointer Function %4\n%6 = OpFunction  %2  None %7\n%5 = OpLabel\n%66 = OpVariable  %13  Function %9\n%63 = OpVariable  %13  Function %9\n%60 = OpVariable  %13  Function %9\n%57 = OpVariable  %13  Function %9\n%54 = OpVariable  %11  Function %8\n%51 = OpVariable  %13  Function %9\n%48 = OpVariable  %11  Function %8\n%45 = OpVariable  %13  Function %9\n%42 = OpVariable  %13  Function %9\n%39 = OpVariable  %13  Function %9\n%36 = OpVariable  %11  Function %8\n%33 = OpVariable  %11  Function %8\n%30 = OpVariable  %13  Function %9\n%27 = OpVariable  %13  Function %9\n%24 = OpVariable  %13  Function %9\n%21 = OpVariable  %13  Function %9\n%18 = OpVariable  %13  Function %9\n%15 = OpVariable  %13  Function %9\n%10 = OpVariable  %11  Function %8\n%64 = OpVariable  %13  Function %9\n%61 = OpVariable  %13  Function %9\n%58 = OpVariable  %13  Function %9\n%55 = OpVariable  %13  Function %9\n%52 = OpVariable  %13  Function %9\n%49 = OpVariable  %13  Function %9\n%46 = OpVariable  %11  Function %8\n%43 = OpVariable  %13  Function %9\n%40 = OpVariable  %13  Function %9\n%37 = OpVariable  %13  Function %9\n%34 = OpVariable  %11  Function %8\n%31 = OpVariable  %13  Function %9\n%28 = OpVariable  %13  Function %9\n%25 = OpVariable  %13  Function %9\n%22 = OpVariable  %13  Function %9\n%19 = OpVariable  %11  Function %8\n%16 = OpVariable  %13  Function %9\n%12 = OpVariable  %13  Function %9\n%65 = OpVariable  %13  Function %9\n%62 = OpVariable  %13  Function %9\n%59 = OpVariable  %13  Function %9\n%56 = OpVariable  %13  Function %9\n%53 = OpVariable  %11  Function %8\n%50 = OpVariable  %13  Function %9\n%47 = OpVariable  %13  Function %9\n%44 = OpVariable  %13  Function %9\n%41 = OpVariable  %13  Function %9\n%38 = OpVariable  %13  Function %9\n%35 = OpVariable  %11  Function %8\n%32 = OpVariable  %13  Function %9\n%29 = OpVariable  %13  Function %9\n%26 = OpVariable  %13  Function %9\n%23 = OpVariable  %13  Function %9\n%20 = OpVariable  %11  Function %8\n%17 = OpVariable  %13  Function %9\n%14 = OpVariable  %11  Function %8\nOpBranch %67\n%67 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-const.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 57\nOpCapability Shader\nOpCapability Linkage\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpDecorate %10 ArrayStride 4\nOpDecorate %12 ArrayStride 4\nOpDecorate %13 ArrayStride 4\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 4\nOpMemberDecorate %14 2 Offset 8\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeVector %3 2\n%5 = OpTypeInt 32 0\n%6 = OpTypeVector %5 2\n%7 = OpTypeFloat 32\n%8 = OpTypeVector %7 2\n%9 = OpTypeMatrix %8 2\n%11 = OpConstant  %5  2\n%10 = OpTypeArray %7 %11\n%12 = OpTypeArray %3 %11\n%13 = OpTypeArray %5 %11\n%14 = OpTypeStruct %7 %3 %5\n%15 = OpTypeVector %7 3\n%16 = OpConstant  %3  42\n%17 = OpConstant  %3  43\n%18 = OpConstantComposite  %4  %16 %17\n%19 = OpConstant  %5  44\n%20 = OpConstant  %5  45\n%21 = OpConstantComposite  %6  %19 %20\n%22 = OpConstant  %7  46\n%23 = OpConstant  %7  47\n%24 = OpConstantComposite  %8  %22 %23\n%25 = OpConstant  %7  48\n%26 = OpConstant  %7  49\n%27 = OpConstantComposite  %8  %25 %26\n%28 = OpConstant  %5  42\n%29 = OpConstant  %5  43\n%30 = OpConstantComposite  %6  %28 %29\n%31 = OpConstant  %3  0\n%32 = OpConstantComposite  %4  %31 %31\n%33 = OpConstant  %5  0\n%34 = OpConstantComposite  %6  %33 %33\n%35 = OpConstant  %7  0\n%36 = OpConstantComposite  %8  %35 %35\n%37 = OpConstantComposite  %9  %36 %36\n%38 = OpConstant  %7  1\n%39 = OpConstant  %7  2\n%40 = OpConstantComposite  %8  %38 %39\n%41 = OpConstant  %7  3\n%42 = OpConstant  %7  4\n%43 = OpConstantComposite  %8  %41 %42\n%44 = OpConstantComposite  %9  %40 %43\n%45 = OpConstant  %3  1\n%46 = OpConstantComposite  %4  %45 %45\n%47 = OpConstant  %5  1\n%48 = OpConstantComposite  %6  %47 %47\n%49 = OpConstantComposite  %8  %38 %38\n%50 = OpConstantComposite  %10  %38 %39\n%51 = OpConstant  %3  2\n%52 = OpConstantComposite  %12  %45 %51\n%53 = OpConstantComposite  %13  %47 %11\n%54 = OpConstantComposite  %14  %38 %45 %47\n%55 = OpConstantComposite  %15  %38 %39 %41\n%56 = OpConstantComposite  %8  %39 %41"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-function-calls.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 115\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %71 \"main\"\nOpExecutionMode %71 LocalSize 1 1 1\nOpDecorate %10 ArrayStride 4\nOpDecorate %12 ArrayStride 4\nOpDecorate %13 ArrayStride 4\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeInt 32 1\n%5 = OpTypeInt 32 0\n%6 = OpTypeVector %3 2\n%7 = OpTypeVector %4 2\n%8 = OpTypeVector %5 2\n%9 = OpTypeMatrix %6 2\n%11 = OpConstant  %5  2\n%10 = OpTypeArray %3 %11\n%12 = OpTypeArray %4 %11\n%13 = OpTypeArray %5 %11\n%17 = OpTypeFunction %2 %3\n%22 = OpTypeFunction %2 %4\n%27 = OpTypeFunction %2 %5\n%32 = OpTypeFunction %2 %6\n%37 = OpTypeFunction %2 %7\n%42 = OpTypeFunction %2 %8\n%47 = OpTypeFunction %2 %9\n%52 = OpTypeFunction %2 %10\n%57 = OpTypeFunction %2 %12\n%62 = OpTypeFunction %2 %13\n%68 = OpTypeFunction %2 %3 %4\n%72 = OpTypeFunction %2\n%73 = OpConstant  %3  0\n%74 = OpConstant  %4  0\n%75 = OpConstant  %5  0\n%76 = OpConstantComposite  %6  %73 %73\n%77 = OpConstantComposite  %7  %74 %74\n%78 = OpConstantComposite  %8  %75 %75\n%79 = OpConstantComposite  %9  %76 %76\n%80 = OpConstantComposite  %10  %73 %73\n%81 = OpConstantComposite  %12  %74 %74\n%82 = OpConstantComposite  %13  %75 %75\n%16 = OpFunction  %2  None %17\n%15 = OpFunctionParameter  %3\n%14 = OpLabel\nOpBranch %18\n%18 = OpLabel\nOpReturn\nOpFunctionEnd\n%21 = OpFunction  %2  None %22\n%20 = OpFunctionParameter  %4\n%19 = OpLabel\nOpBranch %23\n%23 = OpLabel\nOpReturn\nOpFunctionEnd\n%26 = OpFunction  %2  None %27\n%25 = OpFunctionParameter  %5\n%24 = OpLabel\nOpBranch %28\n%28 = OpLabel\nOpReturn\nOpFunctionEnd\n%31 = OpFunction  %2  None %32\n%30 = OpFunctionParameter  %6\n%29 = OpLabel\nOpBranch %33\n%33 = OpLabel\nOpReturn\nOpFunctionEnd\n%36 = OpFunction  %2  None %37\n%35 = OpFunctionParameter  %7\n%34 = OpLabel\nOpBranch %38\n%38 = OpLabel\nOpReturn\nOpFunctionEnd\n%41 = OpFunction  %2  None %42\n%40 = OpFunctionParameter  %8\n%39 = OpLabel\nOpBranch %43\n%43 = OpLabel\nOpReturn\nOpFunctionEnd\n%46 = OpFunction  %2  None %47\n%45 = OpFunctionParameter  %9\n%44 = OpLabel\nOpBranch %48\n%48 = OpLabel\nOpReturn\nOpFunctionEnd\n%51 = OpFunction  %2  None %52\n%50 = OpFunctionParameter  %10\n%49 = OpLabel\nOpBranch %53\n%53 = OpLabel\nOpReturn\nOpFunctionEnd\n%56 = OpFunction  %2  None %57\n%55 = OpFunctionParameter  %12\n%54 = OpLabel\nOpBranch %58\n%58 = OpLabel\nOpReturn\nOpFunctionEnd\n%61 = OpFunction  %2  None %62\n%60 = OpFunctionParameter  %13\n%59 = OpLabel\nOpBranch %63\n%63 = OpLabel\nOpReturn\nOpFunctionEnd\n%67 = OpFunction  %2  None %68\n%65 = OpFunctionParameter  %3\n%66 = OpFunctionParameter  %4\n%64 = OpLabel\nOpBranch %69\n%69 = OpLabel\nOpReturn\nOpFunctionEnd\n%71 = OpFunction  %2  None %72\n%70 = OpLabel\nOpBranch %83\n%83 = OpLabel\n%84 = OpFunctionCall  %2  %16 %73\n%85 = OpFunctionCall  %2  %16 %73\n%86 = OpFunctionCall  %2  %21 %74\n%87 = OpFunctionCall  %2  %26 %75\n%88 = OpFunctionCall  %2  %16 %73\n%89 = OpFunctionCall  %2  %16 %73\n%90 = OpFunctionCall  %2  %21 %74\n%91 = OpFunctionCall  %2  %26 %75\n%92 = OpFunctionCall  %2  %31 %76\n%93 = OpFunctionCall  %2  %31 %76\n%94 = OpFunctionCall  %2  %36 %77\n%95 = OpFunctionCall  %2  %41 %78\n%96 = OpFunctionCall  %2  %31 %76\n%97 = OpFunctionCall  %2  %31 %76\n%98 = OpFunctionCall  %2  %36 %77\n%99 = OpFunctionCall  %2  %41 %78\n%100 = OpFunctionCall  %2  %46 %79\n%101 = OpFunctionCall  %2  %46 %79\n%102 = OpFunctionCall  %2  %46 %79\n%103 = OpFunctionCall  %2  %51 %80\n%104 = OpFunctionCall  %2  %51 %80\n%105 = OpFunctionCall  %2  %56 %81\n%106 = OpFunctionCall  %2  %61 %82\n%107 = OpFunctionCall  %2  %51 %80\n%108 = OpFunctionCall  %2  %51 %80\n%109 = OpFunctionCall  %2  %56 %81\n%110 = OpFunctionCall  %2  %61 %82\n%111 = OpFunctionCall  %2  %67 %73 %74\n%112 = OpFunctionCall  %2  %67 %73 %74\n%113 = OpFunctionCall  %2  %67 %73 %74\n%114 = OpFunctionCall  %2  %67 %73 %74\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-let.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 133\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %129 \"main\"\nOpExecutionMode %129 LocalSize 1 1 1\nOpDecorate %10 ArrayStride 4\nOpDecorate %12 ArrayStride 4\nOpDecorate %14 ArrayStride 16\nOpDecorate %17 ArrayStride 16\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeVector %3 2\n%5 = OpTypeInt 32 0\n%6 = OpTypeVector %5 2\n%7 = OpTypeFloat 32\n%8 = OpTypeVector %7 2\n%9 = OpTypeMatrix %8 2\n%11 = OpConstant  %5  2\n%10 = OpTypeArray %7 %11\n%12 = OpTypeArray %3 %11\n%13 = OpTypeVector %3 3\n%15 = OpConstant  %5  1\n%14 = OpTypeArray %13 %15\n%16 = OpTypeVector %7 3\n%17 = OpTypeArray %16 %15\n%20 = OpTypeFunction %2\n%21 = OpConstant  %3  42\n%22 = OpConstant  %3  43\n%23 = OpConstantComposite  %4  %21 %22\n%24 = OpConstant  %5  44\n%25 = OpConstant  %5  45\n%26 = OpConstantComposite  %6  %24 %25\n%27 = OpConstant  %7  46\n%28 = OpConstant  %7  47\n%29 = OpConstantComposite  %8  %27 %28\n%30 = OpConstant  %7  48\n%31 = OpConstant  %7  49\n%32 = OpConstantComposite  %8  %30 %31\n%33 = OpConstant  %5  42\n%34 = OpConstant  %5  43\n%35 = OpConstantComposite  %6  %33 %34\n%36 = OpConstant  %3  0\n%37 = OpConstantComposite  %4  %36 %36\n%38 = OpConstant  %5  0\n%39 = OpConstantComposite  %6  %38 %38\n%40 = OpConstant  %7  0\n%41 = OpConstantComposite  %8  %40 %40\n%42 = OpConstantComposite  %9  %41 %41\n%43 = OpConstant  %7  1\n%44 = OpConstant  %7  2\n%45 = OpConstantComposite  %8  %43 %44\n%46 = OpConstant  %7  3\n%47 = OpConstant  %7  4\n%48 = OpConstantComposite  %8  %46 %47\n%49 = OpConstantComposite  %9  %45 %48\n%50 = OpConstant  %3  1\n%51 = OpConstantComposite  %4  %50 %50\n%52 = OpConstantComposite  %8  %43 %43\n%53 = OpConstantComposite  %6  %15 %15\n%54 = OpConstantComposite  %10  %43 %44\n%55 = OpConstant  %3  2\n%56 = OpConstantComposite  %12  %50 %55\n%57 = OpConstantComposite  %13  %50 %50 %50\n%58 = OpConstantComposite  %14  %57\n%59 = OpConstantComposite  %16  %43 %43 %43\n%60 = OpConstantComposite  %17  %59\n%65 = OpTypePointer Function %5\n%66 = OpConstantNull  %5\n%68 = OpTypePointer Function %3\n%69 = OpConstantNull  %3\n%71 = OpTypePointer Function %7\n%72 = OpConstantNull  %7\n%19 = OpFunction  %2  None %20\n%18 = OpLabel\nOpBranch %61\n%61 = OpLabel\nOpReturn\nOpFunctionEnd\n%63 = OpFunction  %2  None %20\n%62 = OpLabel\n%64 = OpVariable  %65  Function %66\n%67 = OpVariable  %68  Function %69\n%70 = OpVariable  %71  Function %72\nOpBranch %73\n%73 = OpLabel\n%74 = OpLoad  %5  %64\n%75 = OpCompositeConstruct  %6  %74 %34\n%76 = OpLoad  %5  %64\n%77 = OpCompositeConstruct  %6  %33 %76\n%78 = OpLoad  %7  %70\n%79 = OpCompositeConstruct  %8  %78 %28\n%80 = OpLoad  %7  %70\n%81 = OpCompositeConstruct  %8  %80 %31\n%82 = OpLoad  %5  %64\n%83 = OpCompositeConstruct  %6  %82 %34\n%84 = OpLoad  %5  %64\n%85 = OpCompositeConstruct  %6  %33 %84\n%86 = OpLoad  %7  %70\n%87 = OpCompositeConstruct  %8  %86 %44\n%88 = OpCompositeConstruct  %9  %87 %48\n%89 = OpLoad  %7  %70\n%90 = OpCompositeConstruct  %8  %43 %89\n%91 = OpCompositeConstruct  %9  %90 %48\n%92 = OpLoad  %7  %70\n%93 = OpCompositeConstruct  %8  %92 %47\n%94 = OpCompositeConstruct  %9  %45 %93\n%95 = OpLoad  %7  %70\n%96 = OpCompositeConstruct  %8  %46 %95\n%97 = OpCompositeConstruct  %9  %45 %96\n%98 = OpLoad  %7  %70\n%99 = OpCompositeConstruct  %10  %98 %44\n%100 = OpLoad  %7  %70\n%101 = OpCompositeConstruct  %10  %43 %100\n%102 = OpLoad  %7  %70\n%103 = OpCompositeConstruct  %10  %102 %44\n%104 = OpLoad  %7  %70\n%105 = OpCompositeConstruct  %10  %43 %104\n%106 = OpLoad  %3  %67\n%107 = OpCompositeConstruct  %12  %106 %55\n%108 = OpLoad  %3  %67\n%109 = OpCompositeConstruct  %12  %50 %108\n%110 = OpLoad  %7  %70\n%111 = OpCompositeConstruct  %10  %110 %44\n%112 = OpLoad  %7  %70\n%113 = OpCompositeConstruct  %10  %43 %112\n%114 = OpLoad  %7  %70\n%115 = OpCompositeConstruct  %10  %114 %44\n%116 = OpLoad  %7  %70\n%117 = OpCompositeConstruct  %10  %43 %116\n%118 = OpLoad  %3  %67\n%119 = OpCompositeConstruct  %12  %118 %55\n%120 = OpLoad  %3  %67\n%121 = OpCompositeConstruct  %12  %50 %120\n%122 = OpLoad  %3  %67\n%123 = OpCompositeConstruct  %4  %122 %122\n%124 = OpLoad  %5  %64\n%125 = OpCompositeConstruct  %6  %124 %124\n%126 = OpLoad  %7  %70\n%127 = OpCompositeConstruct  %8  %126 %126\nOpReturn\nOpFunctionEnd\n%129 = OpFunction  %2  None %20\n%128 = OpLabel\nOpBranch %130\n%130 = OpLabel\n%131 = OpFunctionCall  %2  %19\n%132 = OpFunctionCall  %2  %63\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-operators.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 127\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %112 \"main\" %115\nOpExecutionMode %112 LocalSize 1 1 1\nOpDecorate %6 ArrayStride 4\nOpDecorate %115 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeInt 32 1\n%5 = OpTypeInt 32 0\n%7 = OpConstant  %5  64\n%6 = OpTypeArray %5 %7\n%8 = OpConstant  %3  3\n%9 = OpConstant  %4  3\n%10 = OpConstant  %5  3\n%11 = OpConstant  %5  0\n%12 = OpConstant  %4  -2147483648\n%13 = OpConstant  %3  -340282350000000000000000000000000000000\n%14 = OpConstant  %4  4\n%15 = OpConstant  %5  4\n%16 = OpConstant  %4  0\n%18 = OpTypePointer Workgroup %6\n%17 = OpVariable  %18  Workgroup\n%21 = OpTypeFunction %2\n%22 = OpConstant  %3  42\n%23 = OpConstant  %4  43\n%24 = OpConstant  %5  44\n%25 = OpConstant  %3  1\n%26 = OpConstant  %3  2\n%27 = OpConstant  %4  1\n%28 = OpConstant  %4  2\n%29 = OpConstant  %5  1\n%30 = OpConstant  %5  2\n%32 = OpTypePointer Function %3\n%34 = OpTypePointer Function %4\n%36 = OpTypePointer Function %5\n%40 = OpConstantNull  %3\n%44 = OpConstantNull  %3\n%46 = OpConstantNull  %3\n%48 = OpConstantNull  %3\n%50 = OpConstantNull  %3\n%53 = OpConstantNull  %4\n%55 = OpConstantNull  %4\n%57 = OpConstantNull  %4\n%60 = OpConstantNull  %5\n%62 = OpConstantNull  %5\n%64 = OpConstantNull  %5\n%66 = OpConstantNull  %4\n%68 = OpConstantNull  %4\n%101 = OpConstant  %3  5\n%102 = OpConstant  %3  7\n%108 = OpTypePointer Workgroup %5\n%114 = OpConstantNull  %6\n%116 = OpTypePointer Input %5\n%115 = OpVariable  %116  Input\n%119 = OpTypeBool\n%122 = OpConstant  %5  264\n%20 = OpFunction  %2  None %21\n%19 = OpLabel\n%63 = OpVariable  %36  Function %64\n%58 = OpVariable  %36  Function %10\n%52 = OpVariable  %34  Function %53\n%47 = OpVariable  %32  Function %48\n%42 = OpVariable  %32  Function %8\n%38 = OpVariable  %32  Function %8\n%33 = OpVariable  %34  Function %23\n%67 = OpVariable  %34  Function %68\n%61 = OpVariable  %36  Function %62\n%56 = OpVariable  %34  Function %57\n%51 = OpVariable  %34  Function %9\n%45 = OpVariable  %32  Function %46\n%41 = OpVariable  %32  Function %8\n%37 = OpVariable  %32  Function %8\n%31 = OpVariable  %32  Function %22\n%65 = OpVariable  %34  Function %66\n%59 = OpVariable  %36  Function %60\n%54 = OpVariable  %34  Function %55\n%49 = OpVariable  %32  Function %50\n%43 = OpVariable  %32  Function %44\n%39 = OpVariable  %32  Function %40\n%35 = OpVariable  %36  Function %24\nOpBranch %69\n%69 = OpLabel\n%70 = OpLoad  %3  %31\n%71 = OpFAdd  %3  %25 %70\nOpStore %39 %71\n%72 = OpLoad  %3  %31\n%73 = OpFAdd  %3  %25 %72\nOpStore %43 %73\n%74 = OpLoad  %3  %31\n%75 = OpFAdd  %3  %74 %26\nOpStore %45 %75\n%76 = OpLoad  %3  %31\n%77 = OpFAdd  %3  %76 %26\nOpStore %47 %77\n%78 = OpLoad  %3  %31\n%79 = OpLoad  %3  %31\n%80 = OpFAdd  %3  %78 %79\nOpStore %49 %80\n%81 = OpLoad  %4  %33\n%82 = OpIAdd  %4  %27 %81\nOpStore %52 %82\n%83 = OpLoad  %4  %33\n%84 = OpIAdd  %4  %83 %28\nOpStore %54 %84\n%85 = OpLoad  %4  %33\n%86 = OpLoad  %4  %33\n%87 = OpIAdd  %4  %85 %86\nOpStore %56 %87\n%88 = OpLoad  %5  %35\n%89 = OpIAdd  %5  %29 %88\nOpStore %59 %89\n%90 = OpLoad  %5  %35\n%91 = OpIAdd  %5  %90 %30\nOpStore %61 %91\n%92 = OpLoad  %5  %35\n%93 = OpLoad  %5  %35\n%94 = OpIAdd  %5  %92 %93\nOpStore %63 %94\n%95 = OpLoad  %5  %35\n%96 = OpShiftLeftLogical  %4  %27 %95\nOpStore %65 %96\n%97 = OpLoad  %5  %35\n%98 = OpShiftLeftLogical  %4  %27 %97\nOpStore %67 %98\nOpReturn\nOpFunctionEnd\n%100 = OpFunction  %2  None %21\n%99 = OpLabel\nOpBranch %103\n%103 = OpLabel\nOpReturn\nOpFunctionEnd\n%105 = OpFunction  %2  None %21\n%104 = OpLabel\nOpBranch %106\n%106 = OpLabel\n%107 = OpISub  %4  %27 %27\n%109 = OpAccessChain  %108  %17 %107\n%110 = OpLoad  %5  %109\nOpReturn\nOpFunctionEnd\n%112 = OpFunction  %2  None %21\n%111 = OpLabel\nOpBranch %113\n%113 = OpLabel\n%117 = OpLoad  %5  %115\n%118 = OpIEqual  %119  %117 %11\nOpSelectionMerge %120 None\nOpBranchConditional %118 %121 %120\n%121 = OpLabel\nOpStore %17 %114\nOpBranch %120\n%120 = OpLabel\nOpControlBarrier %30 %30 %122\nOpBranch %123\n%123 = OpLabel\n%124 = OpFunctionCall  %2  %20\n%125 = OpFunctionCall  %2  %100\n%126 = OpFunctionCall  %2  %105\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-return.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 55\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %44 \"main\"\nOpExecutionMode %44 LocalSize 1 1 1\nOpDecorate %7 ArrayStride 4\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeInt 32 0\n%5 = OpTypeFloat 32\n%6 = OpTypeVector %5 2\n%8 = OpConstant  %4  4\n%7 = OpTypeArray %5 %8\n%11 = OpTypeFunction %3\n%12 = OpConstant  %3  1\n%16 = OpTypeFunction %4\n%17 = OpConstant  %4  1\n%21 = OpTypeFunction %5\n%22 = OpConstant  %5  1\n%29 = OpTypeFunction %6\n%30 = OpConstantComposite  %6  %22 %22\n%34 = OpTypeFunction %7\n%35 = OpConstantComposite  %7  %22 %22 %22 %22\n%45 = OpTypeFunction %2\n%10 = OpFunction  %3  None %11\n%9 = OpLabel\nOpBranch %13\n%13 = OpLabel\nOpReturnValue %12\nOpFunctionEnd\n%15 = OpFunction  %4  None %16\n%14 = OpLabel\nOpBranch %18\n%18 = OpLabel\nOpReturnValue %17\nOpFunctionEnd\n%20 = OpFunction  %5  None %21\n%19 = OpLabel\nOpBranch %23\n%23 = OpLabel\nOpReturnValue %22\nOpFunctionEnd\n%25 = OpFunction  %5  None %21\n%24 = OpLabel\nOpBranch %26\n%26 = OpLabel\nOpReturnValue %22\nOpFunctionEnd\n%28 = OpFunction  %6  None %29\n%27 = OpLabel\nOpBranch %31\n%31 = OpLabel\nOpReturnValue %30\nOpFunctionEnd\n%33 = OpFunction  %7  None %34\n%32 = OpLabel\nOpBranch %36\n%36 = OpLabel\nOpReturnValue %35\nOpFunctionEnd\n%38 = OpFunction  %5  None %21\n%37 = OpLabel\nOpBranch %39\n%39 = OpLabel\nOpReturnValue %22\nOpFunctionEnd\n%41 = OpFunction  %6  None %29\n%40 = OpLabel\nOpBranch %42\n%42 = OpLabel\nOpReturnValue %30\nOpFunctionEnd\n%44 = OpFunction  %2  None %45\n%43 = OpLabel\nOpBranch %46\n%46 = OpLabel\n%47 = OpFunctionCall  %3  %10\n%48 = OpFunctionCall  %4  %15\n%49 = OpFunctionCall  %5  %20\n%50 = OpFunctionCall  %5  %25\n%51 = OpFunctionCall  %6  %28\n%52 = OpFunctionCall  %7  %33\n%53 = OpFunctionCall  %5  %38\n%54 = OpFunctionCall  %6  %41\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-abstract-types-var.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 412\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %407 \"main\"\nOpExecutionMode %407 LocalSize 1 1 1\nOpDecorate %10 ArrayStride 4\nOpDecorate %12 ArrayStride 4\nOpDecorate %13 ArrayStride 4\nOpDecorate %15 ArrayStride 16\nOpDecorate %18 ArrayStride 16\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeVector %3 2\n%5 = OpTypeInt 32 0\n%6 = OpTypeVector %5 2\n%7 = OpTypeFloat 32\n%8 = OpTypeVector %7 2\n%9 = OpTypeMatrix %8 2\n%11 = OpConstant  %5  2\n%10 = OpTypeArray %7 %11\n%12 = OpTypeArray %3 %11\n%13 = OpTypeArray %5 %11\n%14 = OpTypeVector %3 3\n%16 = OpConstant  %5  1\n%15 = OpTypeArray %14 %16\n%17 = OpTypeVector %7 3\n%18 = OpTypeArray %17 %16\n%19 = OpConstant  %3  42\n%20 = OpConstant  %3  43\n%21 = OpConstantComposite  %4  %19 %20\n%22 = OpConstant  %5  44\n%23 = OpConstant  %5  45\n%24 = OpConstantComposite  %6  %22 %23\n%25 = OpConstant  %7  46\n%26 = OpConstant  %7  47\n%27 = OpConstantComposite  %8  %25 %26\n%28 = OpConstant  %7  48\n%29 = OpConstant  %7  49\n%30 = OpConstantComposite  %8  %28 %29\n%31 = OpConstant  %5  42\n%32 = OpConstant  %5  43\n%33 = OpConstantComposite  %6  %31 %32\n%34 = OpConstant  %3  0\n%35 = OpConstantComposite  %4  %34 %34\n%36 = OpConstant  %5  0\n%37 = OpConstantComposite  %6  %36 %36\n%38 = OpConstant  %7  0\n%39 = OpConstantComposite  %8  %38 %38\n%40 = OpConstantComposite  %9  %39 %39\n%41 = OpConstant  %7  1\n%42 = OpConstant  %7  2\n%43 = OpConstantComposite  %8  %41 %42\n%44 = OpConstant  %7  3\n%45 = OpConstant  %7  4\n%46 = OpConstantComposite  %8  %44 %45\n%47 = OpConstantComposite  %9  %43 %46\n%48 = OpConstant  %3  1\n%49 = OpConstantComposite  %4  %48 %48\n%50 = OpConstantComposite  %8  %41 %41\n%51 = OpConstantComposite  %6  %16 %16\n%52 = OpConstantComposite  %10  %41 %42\n%53 = OpConstant  %3  2\n%54 = OpConstantComposite  %12  %48 %53\n%55 = OpConstantComposite  %13  %16 %11\n%56 = OpConstantComposite  %14  %48 %48 %48\n%57 = OpConstantComposite  %15  %56\n%58 = OpConstantComposite  %17  %41 %41 %41\n%59 = OpConstantComposite  %18  %58\n%61 = OpTypePointer Private %4\n%60 = OpVariable  %61  Private %21\n%63 = OpTypePointer Private %6\n%62 = OpVariable  %63  Private %24\n%65 = OpTypePointer Private %8\n%64 = OpVariable  %65  Private %27\n%66 = OpVariable  %65  Private %30\n%67 = OpVariable  %65  Private %30\n%68 = OpVariable  %63  Private %33\n%69 = OpVariable  %63  Private %33\n%70 = OpVariable  %63  Private %33\n%71 = OpVariable  %63  Private %33\n%72 = OpVariable  %61  Private %35\n%73 = OpVariable  %63  Private %37\n%74 = OpVariable  %65  Private %39\n%76 = OpTypePointer Private %9\n%75 = OpVariable  %76  Private %40\n%77 = OpVariable  %76  Private %47\n%78 = OpVariable  %76  Private %47\n%79 = OpVariable  %76  Private %47\n%80 = OpVariable  %76  Private %47\n%81 = OpVariable  %76  Private %47\n%82 = OpVariable  %61  Private %49\n%83 = OpVariable  %65  Private %50\n%84 = OpVariable  %61  Private %49\n%85 = OpVariable  %63  Private %51\n%86 = OpVariable  %65  Private %50\n%87 = OpVariable  %65  Private %50\n%89 = OpTypePointer Private %10\n%88 = OpVariable  %89  Private %52\n%90 = OpVariable  %89  Private %52\n%92 = OpTypePointer Private %12\n%91 = OpVariable  %92  Private %54\n%94 = OpTypePointer Private %13\n%93 = OpVariable  %94  Private %55\n%95 = OpVariable  %89  Private %52\n%96 = OpVariable  %89  Private %52\n%97 = OpVariable  %89  Private %52\n%99 = OpTypePointer Private %15\n%98 = OpVariable  %99  Private %57\n%101 = OpTypePointer Private %18\n%100 = OpVariable  %101  Private %59\n%102 = OpVariable  %101  Private %59\n%103 = OpVariable  %61  Private %49\n%104 = OpVariable  %63  Private %51\n%105 = OpVariable  %65  Private %50\n%106 = OpVariable  %65  Private %50\n%107 = OpVariable  %61  Private %49\n%108 = OpVariable  %65  Private %50\n%109 = OpVariable  %61  Private %49\n%110 = OpVariable  %63  Private %51\n%111 = OpVariable  %65  Private %50\n%112 = OpVariable  %65  Private %50\n%113 = OpVariable  %89  Private %52\n%114 = OpVariable  %89  Private %52\n%115 = OpVariable  %92  Private %54\n%116 = OpVariable  %89  Private %52\n%117 = OpVariable  %89  Private %52\n%118 = OpVariable  %89  Private %52\n%119 = OpVariable  %99  Private %57\n%120 = OpVariable  %99  Private %57\n%121 = OpVariable  %101  Private %59\n%124 = OpTypeFunction %2\n%182 = OpTypePointer Function %4\n%184 = OpTypePointer Function %6\n%186 = OpTypePointer Function %8\n%197 = OpTypePointer Function %9\n%214 = OpTypePointer Function %10\n%219 = OpTypePointer Function %12\n%227 = OpTypePointer Function %15\n%229 = OpTypePointer Function %18\n%243 = OpTypePointer Function %5\n%244 = OpConstantNull  %5\n%246 = OpTypePointer Function %3\n%247 = OpConstantNull  %3\n%249 = OpTypePointer Function %7\n%250 = OpConstantNull  %7\n%252 = OpConstantNull  %6\n%254 = OpConstantNull  %6\n%256 = OpConstantNull  %8\n%258 = OpConstantNull  %8\n%260 = OpConstantNull  %6\n%262 = OpConstantNull  %6\n%264 = OpConstantNull  %9\n%266 = OpConstantNull  %9\n%268 = OpConstantNull  %9\n%270 = OpConstantNull  %9\n%272 = OpConstantNull  %10\n%274 = OpConstantNull  %10\n%276 = OpConstantNull  %10\n%278 = OpConstantNull  %10\n%280 = OpConstantNull  %12\n%282 = OpConstantNull  %12\n%284 = OpConstantNull  %10\n%286 = OpConstantNull  %10\n%288 = OpConstantNull  %10\n%290 = OpConstantNull  %10\n%292 = OpConstantNull  %12\n%294 = OpConstantNull  %12\n%296 = OpConstantNull  %4\n%298 = OpConstantNull  %6\n%300 = OpConstantNull  %8\n%123 = OpFunction  %2  None %124\n%122 = OpLabel\nOpBranch %125\n%125 = OpLabel\n%126 = OpLoad  %4  %60\n%127 = OpLoad  %6  %62\n%128 = OpLoad  %8  %64\n%129 = OpLoad  %8  %66\n%130 = OpLoad  %8  %67\n%131 = OpLoad  %6  %68\n%132 = OpLoad  %6  %69\n%133 = OpLoad  %6  %70\n%134 = OpLoad  %6  %71\n%135 = OpLoad  %4  %72\n%136 = OpLoad  %6  %73\n%137 = OpLoad  %8  %74\n%138 = OpLoad  %9  %75\n%139 = OpLoad  %9  %77\n%140 = OpLoad  %9  %78\n%141 = OpLoad  %9  %79\n%142 = OpLoad  %9  %80\n%143 = OpLoad  %9  %81\n%144 = OpLoad  %4  %82\n%145 = OpLoad  %8  %83\n%146 = OpLoad  %4  %84\n%147 = OpLoad  %6  %85\n%148 = OpLoad  %8  %86\n%149 = OpLoad  %8  %87\n%150 = OpLoad  %10  %88\n%151 = OpLoad  %10  %90\n%152 = OpLoad  %12  %91\n%153 = OpLoad  %13  %93\n%154 = OpLoad  %10  %95\n%155 = OpLoad  %10  %96\n%156 = OpLoad  %10  %97\n%157 = OpLoad  %15  %98\n%158 = OpLoad  %18  %100\n%159 = OpLoad  %18  %102\n%160 = OpLoad  %4  %103\n%161 = OpLoad  %6  %104\n%162 = OpLoad  %8  %105\n%163 = OpLoad  %8  %106\n%164 = OpLoad  %4  %107\n%165 = OpLoad  %8  %108\n%166 = OpLoad  %4  %109\n%167 = OpLoad  %6  %110\n%168 = OpLoad  %8  %111\n%169 = OpLoad  %8  %112\n%170 = OpLoad  %10  %113\n%171 = OpLoad  %10  %114\n%172 = OpLoad  %12  %115\n%173 = OpLoad  %10  %116\n%174 = OpLoad  %10  %117\n%175 = OpLoad  %10  %118\n%176 = OpLoad  %15  %119\n%177 = OpLoad  %15  %120\n%178 = OpLoad  %18  %121\nOpReturn\nOpFunctionEnd\n%180 = OpFunction  %2  None %124\n%179 = OpLabel\n%237 = OpVariable  %214  Function %52\n%234 = OpVariable  %186  Function %50\n%231 = OpVariable  %182  Function %49\n%226 = OpVariable  %227  Function %57\n%223 = OpVariable  %214  Function %52\n%220 = OpVariable  %219  Function %54\n%216 = OpVariable  %214  Function %52\n%212 = OpVariable  %186  Function %50\n%209 = OpVariable  %182  Function %49\n%206 = OpVariable  %197  Function %47\n%203 = OpVariable  %197  Function %47\n%200 = OpVariable  %197  Function %47\n%196 = OpVariable  %197  Function %40\n%193 = OpVariable  %182  Function %35\n%190 = OpVariable  %184  Function %33\n%187 = OpVariable  %186  Function %30\n%181 = OpVariable  %182  Function %21\n%238 = OpVariable  %214  Function %52\n%235 = OpVariable  %219  Function %54\n%232 = OpVariable  %184  Function %51\n%228 = OpVariable  %229  Function %59\n%224 = OpVariable  %214  Function %52\n%221 = OpVariable  %219  Function %54\n%217 = OpVariable  %214  Function %52\n%213 = OpVariable  %214  Function %52\n%210 = OpVariable  %184  Function %51\n%207 = OpVariable  %182  Function %49\n%204 = OpVariable  %197  Function %47\n%201 = OpVariable  %197  Function %47\n%198 = OpVariable  %197  Function %47\n%194 = OpVariable  %184  Function %37\n%191 = OpVariable  %184  Function %33\n%188 = OpVariable  %186  Function %30\n%183 = OpVariable  %184  Function %24\n%236 = OpVariable  %214  Function %52\n%233 = OpVariable  %186  Function %50\n%230 = OpVariable  %229  Function %59\n%225 = OpVariable  %214  Function %52\n%222 = OpVariable  %214  Function %52\n%218 = OpVariable  %219  Function %54\n%215 = OpVariable  %214  Function %52\n%211 = OpVariable  %186  Function %50\n%208 = OpVariable  %186  Function %50\n%205 = OpVariable  %197  Function %47\n%202 = OpVariable  %197  Function %47\n%199 = OpVariable  %197  Function %47\n%195 = OpVariable  %186  Function %39\n%192 = OpVariable  %184  Function %33\n%189 = OpVariable  %184  Function %33\n%185 = OpVariable  %186  Function %27\nOpBranch %239\n%239 = OpLabel\nOpStore %181 %21\nOpStore %183 %24\nOpStore %185 %27\nOpStore %187 %30\nOpStore %188 %30\nOpStore %189 %33\nOpStore %190 %33\nOpStore %191 %33\nOpStore %192 %33\nOpStore %193 %35\nOpStore %194 %37\nOpStore %195 %39\nOpStore %196 %40\nOpStore %198 %47\nOpStore %199 %47\nOpStore %200 %47\nOpStore %201 %47\nOpStore %202 %47\nOpStore %203 %47\nOpStore %204 %47\nOpStore %205 %47\nOpStore %206 %47\nOpStore %207 %49\nOpStore %208 %50\nOpStore %209 %49\nOpStore %210 %51\nOpStore %211 %50\nOpStore %212 %50\nOpStore %213 %52\nOpStore %215 %52\nOpStore %216 %52\nOpStore %217 %52\nOpStore %218 %54\nOpStore %220 %54\nOpStore %221 %54\nOpStore %222 %52\nOpStore %223 %52\nOpStore %224 %52\nOpStore %225 %52\nOpStore %226 %57\nOpStore %228 %59\nOpStore %230 %59\nOpStore %231 %49\nOpStore %232 %51\nOpStore %233 %50\nOpStore %234 %50\nOpStore %235 %54\nOpStore %236 %52\nOpStore %237 %52\nOpStore %238 %52\nOpReturn\nOpFunctionEnd\n%241 = OpFunction  %2  None %124\n%240 = OpLabel\n%295 = OpVariable  %182  Function %296\n%289 = OpVariable  %214  Function %290\n%283 = OpVariable  %214  Function %284\n%277 = OpVariable  %214  Function %278\n%271 = OpVariable  %214  Function %272\n%265 = OpVariable  %197  Function %266\n%259 = OpVariable  %184  Function %260\n%253 = OpVariable  %184  Function %254\n%245 = OpVariable  %246  Function %247\n%299 = OpVariable  %186  Function %300\n%293 = OpVariable  %219  Function %294\n%287 = OpVariable  %214  Function %288\n%281 = OpVariable  %219  Function %282\n%275 = OpVariable  %214  Function %276\n%269 = OpVariable  %197  Function %270\n%263 = OpVariable  %197  Function %264\n%257 = OpVariable  %186  Function %258\n%251 = OpVariable  %184  Function %252\n%242 = OpVariable  %243  Function %244\n%297 = OpVariable  %184  Function %298\n%291 = OpVariable  %219  Function %292\n%285 = OpVariable  %214  Function %286\n%279 = OpVariable  %219  Function %280\n%273 = OpVariable  %214  Function %274\n%267 = OpVariable  %197  Function %268\n%261 = OpVariable  %184  Function %262\n%255 = OpVariable  %186  Function %256\n%248 = OpVariable  %249  Function %250\nOpBranch %301\n%301 = OpLabel\n%302 = OpLoad  %5  %242\n%303 = OpCompositeConstruct  %6  %302 %32\nOpStore %251 %303\n%304 = OpLoad  %5  %242\n%305 = OpCompositeConstruct  %6  %31 %304\nOpStore %253 %305\n%306 = OpLoad  %7  %248\n%307 = OpCompositeConstruct  %8  %306 %26\nOpStore %255 %307\n%308 = OpLoad  %7  %248\n%309 = OpCompositeConstruct  %8  %308 %29\nOpStore %257 %309\n%310 = OpLoad  %5  %242\n%311 = OpCompositeConstruct  %6  %310 %32\nOpStore %259 %311\n%312 = OpLoad  %5  %242\n%313 = OpCompositeConstruct  %6  %31 %312\nOpStore %261 %313\n%314 = OpLoad  %7  %248\n%315 = OpCompositeConstruct  %8  %314 %42\n%316 = OpCompositeConstruct  %9  %315 %46\nOpStore %263 %316\n%317 = OpLoad  %7  %248\n%318 = OpCompositeConstruct  %8  %41 %317\n%319 = OpCompositeConstruct  %9  %318 %46\nOpStore %265 %319\n%320 = OpLoad  %7  %248\n%321 = OpCompositeConstruct  %8  %320 %45\n%322 = OpCompositeConstruct  %9  %43 %321\nOpStore %267 %322\n%323 = OpLoad  %7  %248\n%324 = OpCompositeConstruct  %8  %44 %323\n%325 = OpCompositeConstruct  %9  %43 %324\nOpStore %269 %325\n%326 = OpLoad  %7  %248\n%327 = OpCompositeConstruct  %10  %326 %42\nOpStore %271 %327\n%328 = OpLoad  %7  %248\n%329 = OpCompositeConstruct  %10  %41 %328\nOpStore %273 %329\n%330 = OpLoad  %7  %248\n%331 = OpCompositeConstruct  %10  %330 %42\nOpStore %275 %331\n%332 = OpLoad  %7  %248\n%333 = OpCompositeConstruct  %10  %41 %332\nOpStore %277 %333\n%334 = OpLoad  %3  %245\n%335 = OpCompositeConstruct  %12  %334 %53\nOpStore %279 %335\n%336 = OpLoad  %3  %245\n%337 = OpCompositeConstruct  %12  %48 %336\nOpStore %281 %337\n%338 = OpLoad  %7  %248\n%339 = OpCompositeConstruct  %10  %338 %42\nOpStore %283 %339\n%340 = OpLoad  %7  %248\n%341 = OpCompositeConstruct  %10  %41 %340\nOpStore %285 %341\n%342 = OpLoad  %7  %248\n%343 = OpCompositeConstruct  %10  %342 %42\nOpStore %287 %343\n%344 = OpLoad  %7  %248\n%345 = OpCompositeConstruct  %10  %41 %344\nOpStore %289 %345\n%346 = OpLoad  %3  %245\n%347 = OpCompositeConstruct  %12  %346 %53\nOpStore %291 %347\n%348 = OpLoad  %3  %245\n%349 = OpCompositeConstruct  %12  %48 %348\nOpStore %293 %349\n%350 = OpLoad  %3  %245\n%351 = OpCompositeConstruct  %4  %350 %350\nOpStore %295 %351\n%352 = OpLoad  %5  %242\n%353 = OpCompositeConstruct  %6  %352 %352\nOpStore %297 %353\n%354 = OpLoad  %7  %248\n%355 = OpCompositeConstruct  %8  %354 %354\nOpStore %299 %355\n%356 = OpLoad  %5  %242\n%357 = OpCompositeConstruct  %6  %356 %32\nOpStore %251 %357\n%358 = OpLoad  %5  %242\n%359 = OpCompositeConstruct  %6  %31 %358\nOpStore %253 %359\n%360 = OpLoad  %5  %242\n%361 = OpCompositeConstruct  %6  %360 %32\nOpStore %259 %361\n%362 = OpLoad  %5  %242\n%363 = OpCompositeConstruct  %6  %31 %362\nOpStore %261 %363\n%364 = OpLoad  %7  %248\n%365 = OpCompositeConstruct  %8  %364 %42\n%366 = OpCompositeConstruct  %9  %365 %46\nOpStore %263 %366\n%367 = OpLoad  %7  %248\n%368 = OpCompositeConstruct  %8  %41 %367\n%369 = OpCompositeConstruct  %9  %368 %46\nOpStore %265 %369\n%370 = OpLoad  %7  %248\n%371 = OpCompositeConstruct  %8  %370 %45\n%372 = OpCompositeConstruct  %9  %43 %371\nOpStore %267 %372\n%373 = OpLoad  %7  %248\n%374 = OpCompositeConstruct  %8  %44 %373\n%375 = OpCompositeConstruct  %9  %43 %374\nOpStore %269 %375\n%376 = OpLoad  %7  %248\n%377 = OpCompositeConstruct  %10  %376 %42\nOpStore %271 %377\n%378 = OpLoad  %7  %248\n%379 = OpCompositeConstruct  %10  %41 %378\nOpStore %273 %379\n%380 = OpLoad  %7  %248\n%381 = OpCompositeConstruct  %10  %380 %42\nOpStore %275 %381\n%382 = OpLoad  %7  %248\n%383 = OpCompositeConstruct  %10  %41 %382\nOpStore %277 %383\n%384 = OpLoad  %3  %245\n%385 = OpCompositeConstruct  %12  %384 %53\nOpStore %279 %385\n%386 = OpLoad  %3  %245\n%387 = OpCompositeConstruct  %12  %48 %386\nOpStore %281 %387\n%388 = OpLoad  %7  %248\n%389 = OpCompositeConstruct  %10  %388 %42\nOpStore %283 %389\n%390 = OpLoad  %7  %248\n%391 = OpCompositeConstruct  %10  %41 %390\nOpStore %285 %391\n%392 = OpLoad  %7  %248\n%393 = OpCompositeConstruct  %10  %392 %42\nOpStore %287 %393\n%394 = OpLoad  %7  %248\n%395 = OpCompositeConstruct  %10  %41 %394\nOpStore %289 %395\n%396 = OpLoad  %3  %245\n%397 = OpCompositeConstruct  %12  %396 %53\nOpStore %291 %397\n%398 = OpLoad  %3  %245\n%399 = OpCompositeConstruct  %12  %48 %398\nOpStore %293 %399\n%400 = OpLoad  %3  %245\n%401 = OpCompositeConstruct  %4  %400 %400\nOpStore %295 %401\n%402 = OpLoad  %5  %242\n%403 = OpCompositeConstruct  %6  %402 %402\nOpStore %297 %403\n%404 = OpLoad  %7  %248\n%405 = OpCompositeConstruct  %8  %404 %404\nOpStore %299 %405\nOpReturn\nOpFunctionEnd\n%407 = OpFunction  %2  None %124\n%406 = OpLabel\nOpBranch %408\n%408 = OpLabel\n%409 = OpFunctionCall  %2  %123\n%410 = OpFunctionCall  %2  %180\n%411 = OpFunctionCall  %2  %241\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-access.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 511\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %428 \"foo_vert\" %423 %426\nOpEntryPoint Fragment %484 \"foo_frag\" %483\nOpEntryPoint GLCompute %502 \"foo_compute\"\nOpExecutionMode %484 OriginUpperLeft\nOpExecutionMode %502 LocalSize 1 1 1\n%3 = OpString \"access.wgsl\"\nOpSource Unknown 0 %3 \"// This snapshot tests accessing various containers, dereferencing pointers.\n\nstruct GlobalConst {\n    a: u32,\n    b: vec3<u32>,\n    c: i32,\n}\n// tests msl padding insertion for global constants\nvar<private> msl_padding_global_const: GlobalConst = GlobalConst(0u, vec3<u32>(0u, 0u, 0u), 0);\n\nstruct AlignedWrapper {\n    @align(8) value: i32\n}\n\nstruct Bar {\n    _matrix: mat4x3<f32>,\n    matrix_array: array<mat2x2<f32>, 2>,\n    atom: atomic<i32>,\n    atom_arr: array<atomic<i32>, 10>,\n    arr: array<vec2<u32>, 2>,\n    data: array<AlignedWrapper>,\n}\n\n@group(0) @binding(0)\nvar<storage,read_write> bar: Bar;\n\nstruct Baz {\n    m: mat3x2<f32>,\n}\n\n@group(0) @binding(1)\nvar<uniform> baz: Baz;\n\n@group(0) @binding(2)\nvar<storage,read_write> qux: vec2<i32>;\n\nfn test_matrix_within_struct_accesses() {\n    // Test accesses to Cx2 matrices. There are additional tests in\n    // `mat_cx2.wgsl`.\n\n    var idx = 1;\n\n    idx--;\n\n    // loads\n    let l0 = baz.m;\n    let l1 = baz.m[0];\n    let l2 = baz.m[idx];\n    let l3 = baz.m[0][1];\n    let l4 = baz.m[0][idx];\n    let l5 = baz.m[idx][1];\n    let l6 = baz.m[idx][idx];\n\n    var t = Baz(mat3x2<f32>(vec2<f32>(1.0), vec2<f32>(2.0), vec2<f32>(3.0)));\n\n    idx++;\n\n    // stores\n    t.m = mat3x2<f32>(vec2<f32>(6.0), vec2<f32>(5.0), vec2<f32>(4.0));\n    t.m[0] = vec2<f32>(9.0);\n    t.m[idx] = vec2<f32>(90.0);\n    t.m[0][1] = 10.0;\n    t.m[0][idx] = 20.0;\n    t.m[idx][1] = 30.0;\n    t.m[idx][idx] = 40.0;\n}\n\nstruct MatCx2InArray {\n    am: array<mat4x2<f32>, 2>,\n}\n\n@group(0) @binding(3)\nvar<uniform> nested_mat_cx2: MatCx2InArray;\n\nfn test_matrix_within_array_within_struct_accesses() {\n    var idx = 1;\n\n    idx--;\n\n    // loads\n    let l0 = nested_mat_cx2.am;\n    let l1 = nested_mat_cx2.am[0];\n    let l2 = nested_mat_cx2.am[0][0];\n    let l3 = nested_mat_cx2.am[0][idx];\n    let l4 = nested_mat_cx2.am[0][0][1];\n    let l5 = nested_mat_cx2.am[0][0][idx];\n    let l6 = nested_mat_cx2.am[0][idx][1];\n    let l7 = nested_mat_cx2.am[0][idx][idx];\n\n    var t = MatCx2InArray(array<mat4x2<f32>, 2>());\n\n    idx++;\n\n    // stores\n    t.am = array<mat4x2<f32>, 2>();\n    t.am[0] = mat4x2<f32>(vec2<f32>(8.0), vec2<f32>(7.0), vec2<f32>(6.0), vec2<f32>(5.0));\n    t.am[0][0] = vec2<f32>(9.0);\n    t.am[0][idx] = vec2<f32>(90.0);\n    t.am[0][0][1] = 10.0;\n    t.am[0][0][idx] = 20.0;\n    t.am[0][idx][1] = 30.0;\n    t.am[0][idx][idx] = 40.0;\n}\n\nfn read_from_private(foo: ptr<function, f32>) -> f32 {\n    return *foo;\n}\n\nfn test_arr_as_arg(a: array<array<f32, 10>, 5>) -> f32 {\n    return a[4][9];\n}\n\n@vertex\nfn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n    var foo: f32 = 0.0;\n    // We should check that backed doesn't skip this expression\n    let baz: f32 = foo;\n    foo = 1.0;\n\n    _ = msl_padding_global_const;\n    test_matrix_within_struct_accesses();\n    test_matrix_within_array_within_struct_accesses();\n\n    // test storage loads\n    let _matrix = bar._matrix;\n    let arr = bar.arr;\n    let index = 3u;\n    let b = bar._matrix[index].x;\n    let a = bar.data[arrayLength(&bar.data) - 2u].value;\n    let c = qux;\n\n    // test pointer types\n    let data_pointer: ptr<storage, i32, read_write> = &bar.data[0].value;\n    let foo_value = read_from_private(&foo);\n\n    // test array indexing\n    var c2 = array<i32, 5>(a, i32(b), 3, 4, 5);\n    c2[vi + 1u] = 42;\n    let value = c2[vi];\n\n    test_arr_as_arg(array<array<f32, 10>, 5>());\n\n    return vec4<f32>(_matrix * vec4<f32>(vec4<i32>(value)), 2.0);\n}\n\n@fragment\nfn foo_frag() -> @location(0) vec4<f32> {\n    // test storage stores\n    bar._matrix[1].z = 1.0;\n    bar._matrix = mat4x3<f32>(vec3<f32>(0.0), vec3<f32>(1.0), vec3<f32>(2.0), vec3<f32>(3.0));\n    bar.arr = array<vec2<u32>, 2>(vec2<u32>(0u), vec2<u32>(1u));\n    bar.data[1].value = 1;\n    qux = vec2<i32>();\n\n    return vec4<f32>(0.0);\n}\n\nfn assign_through_ptr_fn(p: ptr<function, u32>) {\n    *p = 42u;\n}\n\nfn assign_array_through_ptr_fn(foo: ptr<function, array<vec4<f32>, 2>>) {\n    *foo = array<vec4<f32>, 2>(vec4(1.0), vec4(2.0));\n}\n\nfn assign_through_ptr() {\n    var val = 33u;\n    assign_through_ptr_fn(&val);\n\n    var arr = array<vec4<f32>, 2>(vec4(6.0), vec4(7.0));\n    assign_array_through_ptr_fn(&arr);\n}\n\nstruct AssignToMember {\n  x: u32,\n}\n\nfn fetch_arg_ptr_member(p: ptr<function, AssignToMember>) -> u32 {\n  return (*p).x;\n}\n\nfn assign_to_arg_ptr_member(p: ptr<function, AssignToMember>) {\n  (*p).x = 10u;\n}\n\nfn fetch_arg_ptr_array_element(p: ptr<function, array<u32, 4>>) -> u32 {\n  return (*p)[1];\n}\n\nfn assign_to_arg_ptr_array_element(p: ptr<function, array<u32, 4>>) {\n  (*p)[1] = 10u;\n}\n\nfn assign_to_ptr_components() {\n   var s1: AssignToMember;\n   assign_to_arg_ptr_member(&s1);\n   fetch_arg_ptr_member(&s1);\n\n   var a1: array<u32, 4>;\n   assign_to_arg_ptr_array_element(&a1);\n   fetch_arg_ptr_array_element(&a1);\n}\n\nfn index_ptr(value: bool) -> bool {\n    var a = array<bool, 1>(value);\n    let p = &a;\n    return p[0];\n}\n\nstruct S { m: i32 };\n\nfn member_ptr() -> i32 {\n    var s: S = S(42);\n    let p = &s;\n    return p.m;\n}\n\nstruct Inner { delicious: i32 }\n\nstruct Outer { om_nom_nom: Inner, thing: u32 }\n\nfn let_members_of_members() -> i32 {\n    let thing = Outer();\n\n    let inner = thing.om_nom_nom;\n    let delishus = inner.delicious;\n\n    if (thing.thing != u32(delishus)) {\n        // LOL\n    }\n\n    return thing.om_nom_nom.delicious;\n}\n\nfn var_members_of_members() -> i32 {\n    var thing = Outer();\n\n    var inner = thing.om_nom_nom;\n    var delishus = inner.delicious;\n\n    if (thing.thing != u32(delishus)) {\n        // LOL\n    }\n\n    return thing.om_nom_nom.delicious;\n}\n\n@compute @workgroup_size(1)\nfn foo_compute() {\n    assign_through_ptr();\n    assign_to_ptr_components();\n    index_ptr(true);\n    member_ptr();\n    let_members_of_members();\n    var_members_of_members();\n}\n\"\nOpMemberName %7 0 \"a\"\nOpMemberName %7 1 \"b\"\nOpMemberName %7 2 \"c\"\nOpName %7 \"GlobalConst\"\nOpMemberName %8 0 \"value\"\nOpName %8 \"AlignedWrapper\"\nOpMemberName %21 0 \"_matrix\"\nOpMemberName %21 1 \"matrix_array\"\nOpMemberName %21 2 \"atom\"\nOpMemberName %21 3 \"atom_arr\"\nOpMemberName %21 4 \"arr\"\nOpMemberName %21 5 \"data\"\nOpName %21 \"Bar\"\nOpMemberName %23 0 \"m\"\nOpName %23 \"Baz\"\nOpMemberName %27 0 \"am\"\nOpName %27 \"MatCx2InArray\"\nOpMemberName %37 0 \"x\"\nOpName %37 \"AssignToMember\"\nOpMemberName %45 0 \"m\"\nOpName %45 \"S\"\nOpMemberName %46 0 \"delicious\"\nOpName %46 \"Inner\"\nOpMemberName %47 0 \"om_nom_nom\"\nOpMemberName %47 1 \"thing\"\nOpName %47 \"Outer\"\nOpMemberName %48 0 \"m_col0\"\nOpMemberName %48 1 \"m_col1\"\nOpMemberName %48 2 \"m_col2\"\nOpName %48 \"std140_Baz\"\nOpMemberName %49 0 \"col0\"\nOpMemberName %49 1 \"col1\"\nOpMemberName %49 2 \"col2\"\nOpMemberName %49 3 \"col3\"\nOpName %49 \"std140_mat4x2<f32>\"\nOpName %50 \"std140_array<mat4x2<f32>, 2>\"\nOpMemberName %51 0 \"am\"\nOpName %51 \"std140_MatCx2InArray\"\nOpName %56 \"msl_padding_global_const\"\nOpName %58 \"bar\"\nOpName %60 \"baz\"\nOpName %63 \"qux\"\nOpName %66 \"nested_mat_cx2\"\nOpName %69 \"mat3x2<f32>_get_column\"\nOpName %84 \"test_matrix_within_struct_accesses\"\nOpName %113 \"idx\"\nOpName %115 \"t\"\nOpName %186 \"array<mat4x2<f32>, 2>_from_std140\"\nOpName %190 \"mat4x2<f32>_from_std140\"\nOpName %204 \"mat4x2<f32>_get_column\"\nOpName %221 \"test_matrix_within_array_within_struct_accesses\"\nOpName %232 \"idx\"\nOpName %233 \"t\"\nOpName %294 \"foo\"\nOpName %295 \"read_from_private\"\nOpName %300 \"a\"\nOpName %301 \"test_arr_as_arg\"\nOpName %307 \"p\"\nOpName %308 \"assign_through_ptr_fn\"\nOpName %313 \"foo\"\nOpName %314 \"assign_array_through_ptr_fn\"\nOpName %321 \"assign_through_ptr\"\nOpName %326 \"val\"\nOpName %327 \"arr\"\nOpName %332 \"p\"\nOpName %333 \"fetch_arg_ptr_member\"\nOpName %339 \"p\"\nOpName %340 \"assign_to_arg_ptr_member\"\nOpName %345 \"p\"\nOpName %346 \"fetch_arg_ptr_array_element\"\nOpName %352 \"p\"\nOpName %353 \"assign_to_arg_ptr_array_element\"\nOpName %358 \"assign_to_ptr_components\"\nOpName %359 \"s1\"\nOpName %361 \"a1\"\nOpName %369 \"value\"\nOpName %370 \"index_ptr\"\nOpName %372 \"a\"\nOpName %381 \"member_ptr\"\nOpName %385 \"s\"\nOpName %391 \"let_members_of_members\"\nOpName %402 \"var_members_of_members\"\nOpName %403 \"thing\"\nOpName %405 \"inner\"\nOpName %408 \"delishus\"\nOpName %423 \"vi\"\nOpName %428 \"foo_vert\"\nOpName %439 \"foo\"\nOpName %440 \"c2\"\nOpName %484 \"foo_frag\"\nOpName %502 \"foo_compute\"\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 16\nOpMemberDecorate %7 2 Offset 28\nOpMemberDecorate %8 0 Offset 0\nOpDecorate %14 ArrayStride 16\nOpDecorate %16 ArrayStride 4\nOpDecorate %19 ArrayStride 8\nOpDecorate %20 ArrayStride 8\nOpMemberDecorate %21 0 Offset 0\nOpMemberDecorate %21 0 ColMajor\nOpMemberDecorate %21 0 MatrixStride 16\nOpMemberDecorate %21 1 Offset 64\nOpMemberDecorate %21 1 ColMajor\nOpMemberDecorate %21 1 MatrixStride 8\nOpMemberDecorate %21 2 Offset 96\nOpMemberDecorate %21 3 Offset 100\nOpMemberDecorate %21 4 Offset 144\nOpMemberDecorate %21 5 Offset 160\nOpDecorate %21 Block\nOpMemberDecorate %23 0 Offset 0\nOpMemberDecorate %23 0 ColMajor\nOpMemberDecorate %23 0 MatrixStride 8\nOpDecorate %26 ArrayStride 32\nOpMemberDecorate %27 0 Offset 0\nOpMemberDecorate %27 0 ColMajor\nOpMemberDecorate %27 0 MatrixStride 8\nOpDecorate %29 ArrayStride 4\nOpDecorate %30 ArrayStride 40\nOpDecorate %33 ArrayStride 4\nOpDecorate %35 ArrayStride 16\nOpMemberDecorate %37 0 Offset 0\nOpDecorate %39 ArrayStride 4\nOpDecorate %43 ArrayStride 1\nOpMemberDecorate %45 0 Offset 0\nOpMemberDecorate %46 0 Offset 0\nOpMemberDecorate %47 0 Offset 0\nOpMemberDecorate %47 1 Offset 4\nOpMemberDecorate %48 0 Offset 0\nOpMemberDecorate %48 1 Offset 8\nOpMemberDecorate %48 2 Offset 16\nOpMemberDecorate %49 0 Offset 0\nOpMemberDecorate %49 1 Offset 8\nOpMemberDecorate %49 2 Offset 16\nOpMemberDecorate %49 3 Offset 24\nOpDecorate %50 ArrayStride 32\nOpMemberDecorate %51 0 Offset 0\nOpDecorate %58 DescriptorSet 0\nOpDecorate %58 Binding 0\nOpDecorate %60 DescriptorSet 0\nOpDecorate %60 Binding 1\nOpDecorate %61 Block\nOpMemberDecorate %61 0 Offset 0\nOpDecorate %63 DescriptorSet 0\nOpDecorate %63 Binding 2\nOpDecorate %64 Block\nOpMemberDecorate %64 0 Offset 0\nOpDecorate %66 DescriptorSet 0\nOpDecorate %66 Binding 3\nOpDecorate %67 Block\nOpMemberDecorate %67 0 Offset 0\nOpDecorate %423 BuiltIn VertexIndex\nOpDecorate %426 BuiltIn Position\nOpDecorate %483 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%5 = OpTypeVector %4 3\n%6 = OpTypeInt 32 1\n%7 = OpTypeStruct %4 %5 %6\n%8 = OpTypeStruct %6\n%9 = OpTypeFloat 32\n%11 = OpTypeVector %9 3\n%10 = OpTypeMatrix %11 4\n%13 = OpTypeVector %9 2\n%12 = OpTypeMatrix %13 2\n%15 = OpConstant  %4  2\n%14 = OpTypeArray %12 %15\n%17 = OpConstant  %4  10\n%16 = OpTypeArray %6 %17\n%18 = OpTypeVector %4 2\n%19 = OpTypeArray %18 %15\n%20 = OpTypeRuntimeArray %8\n%21 = OpTypeStruct %10 %14 %6 %16 %19 %20\n%22 = OpTypeMatrix %13 3\n%23 = OpTypeStruct %22\n%24 = OpTypeVector %6 2\n%25 = OpTypeMatrix %13 4\n%26 = OpTypeArray %25 %15\n%27 = OpTypeStruct %26\n%28 = OpTypePointer Function %9\n%29 = OpTypeArray %9 %17\n%31 = OpConstant  %4  5\n%30 = OpTypeArray %29 %31\n%32 = OpTypeVector %9 4\n%33 = OpTypeArray %6 %31\n%34 = OpTypePointer Function %4\n%35 = OpTypeArray %32 %15\n%36 = OpTypePointer Function %35\n%37 = OpTypeStruct %4\n%38 = OpTypePointer Function %37\n%40 = OpConstant  %4  4\n%39 = OpTypeArray %4 %40\n%41 = OpTypePointer Function %39\n%42 = OpTypeBool\n%44 = OpConstant  %4  1\n%43 = OpTypeArray %42 %44\n%45 = OpTypeStruct %6\n%46 = OpTypeStruct %6\n%47 = OpTypeStruct %46 %4\n%48 = OpTypeStruct %13 %13 %13\n%49 = OpTypeStruct %13 %13 %13 %13\n%50 = OpTypeArray %49 %15\n%51 = OpTypeStruct %50\n%52 = OpConstant  %4  0\n%53 = OpConstantComposite  %5  %52 %52 %52\n%54 = OpConstant  %6  0\n%55 = OpConstantComposite  %7  %52 %53 %54\n%57 = OpTypePointer Private %7\n%56 = OpVariable  %57  Private %55\n%59 = OpTypePointer StorageBuffer %21\n%58 = OpVariable  %59  StorageBuffer\n%61 = OpTypeStruct %48\n%62 = OpTypePointer Uniform %61\n%60 = OpVariable  %62  Uniform\n%64 = OpTypeStruct %24\n%65 = OpTypePointer StorageBuffer %64\n%63 = OpVariable  %65  StorageBuffer\n%67 = OpTypeStruct %51\n%68 = OpTypePointer Uniform %67\n%66 = OpVariable  %68  Uniform\n%72 = OpTypeFunction %13 %22 %4\n%85 = OpTypeFunction %2\n%86 = OpTypePointer Uniform %48\n%88 = OpConstant  %6  1\n%89 = OpTypePointer Uniform %23\n%90 = OpConstant  %9  1\n%91 = OpConstantComposite  %13  %90 %90\n%92 = OpConstant  %9  2\n%93 = OpConstantComposite  %13  %92 %92\n%94 = OpConstant  %9  3\n%95 = OpConstantComposite  %13  %94 %94\n%96 = OpConstantComposite  %22  %91 %93 %95\n%97 = OpConstantComposite  %23  %96\n%98 = OpConstant  %9  6\n%99 = OpConstantComposite  %13  %98 %98\n%100 = OpConstant  %9  5\n%101 = OpConstantComposite  %13  %100 %100\n%102 = OpConstant  %9  4\n%103 = OpConstantComposite  %13  %102 %102\n%104 = OpConstantComposite  %22  %99 %101 %103\n%105 = OpConstant  %9  9\n%106 = OpConstantComposite  %13  %105 %105\n%107 = OpConstant  %9  90\n%108 = OpConstantComposite  %13  %107 %107\n%109 = OpConstant  %9  10\n%110 = OpConstant  %9  20\n%111 = OpConstant  %9  30\n%112 = OpConstant  %9  40\n%114 = OpTypePointer Function %6\n%116 = OpTypePointer Function %23\n%120 = OpTypePointer Uniform %22\n%121 = OpTypePointer Uniform %13\n%141 = OpTypePointer Uniform %9\n%172 = OpTypePointer Function %22\n%174 = OpTypePointer Function %13\n%187 = OpTypeFunction %26 %50\n%191 = OpTypeFunction %25 %49\n%207 = OpTypeFunction %13 %25 %4\n%222 = OpTypePointer Uniform %51\n%224 = OpTypePointer Uniform %27\n%225 = OpConstantNull  %26\n%226 = OpConstantComposite  %27  %225\n%227 = OpConstant  %9  8\n%228 = OpConstantComposite  %13  %227 %227\n%229 = OpConstant  %9  7\n%230 = OpConstantComposite  %13  %229 %229\n%231 = OpConstantComposite  %25  %228 %230 %99 %101\n%234 = OpTypePointer Function %27\n%238 = OpTypePointer Uniform %26\n%239 = OpTypePointer Uniform %50\n%243 = OpTypePointer Uniform %25\n%244 = OpTypePointer Uniform %49\n%278 = OpTypePointer Function %26\n%280 = OpTypePointer Function %25\n%296 = OpTypeFunction %9 %28\n%302 = OpTypeFunction %9 %30\n%309 = OpTypeFunction %2 %34\n%310 = OpConstant  %4  42\n%315 = OpTypeFunction %2 %36\n%316 = OpConstantComposite  %32  %90 %90 %90 %90\n%317 = OpConstantComposite  %32  %92 %92 %92 %92\n%318 = OpConstantComposite  %35  %316 %317\n%322 = OpConstant  %4  33\n%323 = OpConstantComposite  %32  %98 %98 %98 %98\n%324 = OpConstantComposite  %32  %229 %229 %229 %229\n%325 = OpConstantComposite  %35  %323 %324\n%334 = OpTypeFunction %4 %38\n%341 = OpTypeFunction %2 %38\n%347 = OpTypeFunction %4 %41\n%354 = OpTypeFunction %2 %41\n%360 = OpConstantNull  %37\n%362 = OpConstantNull  %39\n%371 = OpTypeFunction %42 %42\n%373 = OpTypePointer Function %43\n%374 = OpConstantNull  %43\n%377 = OpTypePointer Function %42\n%382 = OpTypeFunction %6\n%383 = OpConstant  %6  42\n%384 = OpConstantComposite  %45  %383\n%386 = OpTypePointer Function %45\n%392 = OpConstantNull  %47\n%404 = OpTypePointer Function %47\n%406 = OpTypePointer Function %46\n%407 = OpConstantNull  %46\n%409 = OpConstantNull  %6\n%424 = OpTypePointer Input %4\n%423 = OpVariable  %424  Input\n%427 = OpTypePointer Output %32\n%426 = OpVariable  %427  Output\n%430 = OpTypePointer StorageBuffer %24\n%433 = OpConstant  %9  0\n%434 = OpConstant  %4  3\n%435 = OpConstant  %6  3\n%436 = OpConstant  %6  4\n%437 = OpConstant  %6  5\n%438 = OpConstantNull  %30\n%441 = OpTypePointer Function %33\n%442 = OpConstantNull  %33\n%448 = OpTypePointer StorageBuffer %10\n%451 = OpTypePointer StorageBuffer %19\n%454 = OpTypePointer StorageBuffer %11\n%455 = OpTypePointer StorageBuffer %9\n%458 = OpTypePointer StorageBuffer %20\n%461 = OpTypePointer StorageBuffer %8\n%462 = OpTypePointer StorageBuffer %6\n%467 = OpConstant  %9  -2147483600\n%468 = OpConstant  %9  2147483500\n%477 = OpTypeVector %6 4\n%483 = OpVariable  %427  Output\n%486 = OpConstantComposite  %11  %433 %433 %433\n%487 = OpConstantComposite  %11  %90 %90 %90\n%488 = OpConstantComposite  %11  %92 %92 %92\n%489 = OpConstantComposite  %11  %94 %94 %94\n%490 = OpConstantComposite  %10  %486 %487 %488 %489\n%491 = OpConstantComposite  %18  %52 %52\n%492 = OpConstantComposite  %18  %44 %44\n%493 = OpConstantComposite  %19  %491 %492\n%494 = OpConstantNull  %24\n%495 = OpConstantComposite  %32  %433 %433 %433 %433\n%503 = OpConstantTrue  %42\n%69 = OpFunction  %13  None %72\n%70 = OpFunctionParameter  %22\n%71 = OpFunctionParameter  %4\n%73 = OpLabel\nOpSelectionMerge %74 None\nOpSwitch %71 %78 0 %75 1 %76 2 %77\n%75 = OpLabel\n%79 = OpCompositeExtract  %13  %70 0\nOpBranch %74\n%76 = OpLabel\n%80 = OpCompositeExtract  %13  %70 1\nOpBranch %74\n%77 = OpLabel\n%81 = OpCompositeExtract  %13  %70 2\nOpBranch %74\n%78 = OpLabel\nOpUnreachable\n%74 = OpLabel\n%82 = OpPhi  %13  %79 %75 %80 %76 %81 %77\nOpReturnValue %82\nOpFunctionEnd\n%84 = OpFunction  %2  None %85\n%83 = OpLabel\n%113 = OpVariable  %114  Function %88\n%115 = OpVariable  %116  Function %97\n%87 = OpAccessChain  %86  %60 %52\nOpBranch %117\n%117 = OpLabel\nOpLine %3 43 5\n%118 = OpLoad  %6  %113\n%119 = OpISub  %6  %118 %88\nOpLine %3 43 5\nOpStore %113 %119\nOpLine %3 46 14\n%122 = OpAccessChain  %121  %87 %52\n%123 = OpLoad  %13  %122\n%124 = OpAccessChain  %121  %87 %44\n%125 = OpLoad  %13  %124\n%126 = OpAccessChain  %121  %87 %15\n%127 = OpLoad  %13  %126\n%128 = OpCompositeConstruct  %22  %123 %125 %127\nOpLine %3 47 14\nOpLine %3 47 14\n%129 = OpAccessChain  %121  %87 %52\n%130 = OpLoad  %13  %129\nOpLine %3 48 14\n%131 = OpLoad  %6  %113\n%132 = OpAccessChain  %121  %87 %52\n%133 = OpLoad  %13  %132\n%134 = OpAccessChain  %121  %87 %44\n%135 = OpLoad  %13  %134\n%136 = OpAccessChain  %121  %87 %15\n%137 = OpLoad  %13  %136\n%138 = OpCompositeConstruct  %22  %133 %135 %137\n%139 = OpBitcast  %4  %131\n%140 = OpFunctionCall  %13  %69 %138 %139\nOpLine %3 49 14\nOpLine %3 49 14\nOpLine %3 49 14\n%142 = OpAccessChain  %141  %87 %52 %44\n%143 = OpLoad  %9  %142\nOpLine %3 50 14\nOpLine %3 50 14\n%144 = OpLoad  %6  %113\n%145 = OpAccessChain  %141  %87 %52 %144\n%146 = OpLoad  %9  %145\nOpLine %3 51 14\n%147 = OpLoad  %6  %113\nOpLine %3 51 14\n%148 = OpAccessChain  %121  %87 %52\n%149 = OpLoad  %13  %148\n%150 = OpAccessChain  %121  %87 %44\n%151 = OpLoad  %13  %150\n%152 = OpAccessChain  %121  %87 %15\n%153 = OpLoad  %13  %152\n%154 = OpCompositeConstruct  %22  %149 %151 %153\n%155 = OpBitcast  %4  %147\n%156 = OpFunctionCall  %13  %69 %154 %155\n%157 = OpCompositeExtract  %9  %156 1\nOpLine %3 52 14\n%158 = OpLoad  %6  %113\n%159 = OpLoad  %6  %113\n%160 = OpAccessChain  %121  %87 %52\n%161 = OpLoad  %13  %160\n%162 = OpAccessChain  %121  %87 %44\n%163 = OpLoad  %13  %162\n%164 = OpAccessChain  %121  %87 %15\n%165 = OpLoad  %13  %164\n%166 = OpCompositeConstruct  %22  %161 %163 %165\n%167 = OpBitcast  %4  %158\n%168 = OpFunctionCall  %13  %69 %166 %167\n%169 = OpVectorExtractDynamic  %9  %168 %159\nOpLine %3 54 29\nOpLine %3 54 45\nOpLine %3 54 13\nOpLine %3 56 5\n%170 = OpLoad  %6  %113\n%171 = OpIAdd  %6  %170 %88\nOpLine %3 56 5\nOpStore %113 %171\nOpLine %3 59 5\nOpLine %3 59 23\nOpLine %3 59 39\nOpLine %3 59 11\nOpLine %3 59 5\n%173 = OpAccessChain  %172  %115 %52\nOpStore %173 %104\nOpLine %3 60 5\nOpLine %3 60 5\nOpLine %3 60 14\nOpLine %3 60 5\n%175 = OpAccessChain  %174  %115 %52 %52\nOpStore %175 %106\nOpLine %3 61 5\n%176 = OpLoad  %6  %113\nOpLine %3 61 16\nOpLine %3 61 5\n%177 = OpAccessChain  %174  %115 %52 %176\nOpStore %177 %108\nOpLine %3 62 5\nOpLine %3 62 5\nOpLine %3 62 5\nOpLine %3 62 5\n%178 = OpAccessChain  %28  %115 %52 %52 %44\nOpStore %178 %109\nOpLine %3 63 5\nOpLine %3 63 5\n%179 = OpLoad  %6  %113\nOpLine %3 63 5\n%180 = OpAccessChain  %28  %115 %52 %52 %179\nOpStore %180 %110\nOpLine %3 64 5\n%181 = OpLoad  %6  %113\nOpLine %3 64 5\nOpLine %3 64 5\n%182 = OpAccessChain  %28  %115 %52 %181 %44\nOpStore %182 %111\nOpLine %3 65 5\n%183 = OpLoad  %6  %113\n%184 = OpLoad  %6  %113\nOpLine %3 65 5\n%185 = OpAccessChain  %28  %115 %52 %183 %184\nOpStore %185 %112\nOpReturn\nOpFunctionEnd\n%190 = OpFunction  %25  None %191\n%192 = OpFunctionParameter  %49\n%193 = OpLabel\n%194 = OpCompositeExtract  %13  %192 0\n%195 = OpCompositeExtract  %13  %192 1\n%196 = OpCompositeExtract  %13  %192 2\n%197 = OpCompositeExtract  %13  %192 3\n%198 = OpCompositeConstruct  %25  %194 %195 %196 %197\nOpReturnValue %198\nOpFunctionEnd\n%186 = OpFunction  %26  None %187\n%188 = OpFunctionParameter  %50\n%189 = OpLabel\n%199 = OpCompositeExtract  %49  %188 0\n%200 = OpFunctionCall  %25  %190 %199\n%201 = OpCompositeExtract  %49  %188 1\n%202 = OpFunctionCall  %25  %190 %201\n%203 = OpCompositeConstruct  %26  %200 %202\nOpReturnValue %203\nOpFunctionEnd\n%204 = OpFunction  %13  None %207\n%205 = OpFunctionParameter  %25\n%206 = OpFunctionParameter  %4\n%208 = OpLabel\nOpSelectionMerge %209 None\nOpSwitch %206 %214 0 %210 1 %211 2 %212 3 %213\n%210 = OpLabel\n%215 = OpCompositeExtract  %13  %205 0\nOpBranch %209\n%211 = OpLabel\n%216 = OpCompositeExtract  %13  %205 1\nOpBranch %209\n%212 = OpLabel\n%217 = OpCompositeExtract  %13  %205 2\nOpBranch %209\n%213 = OpLabel\n%218 = OpCompositeExtract  %13  %205 3\nOpBranch %209\n%214 = OpLabel\nOpUnreachable\n%209 = OpLabel\n%219 = OpPhi  %13  %215 %210 %216 %211 %217 %212 %218 %213\nOpReturnValue %219\nOpFunctionEnd\n%221 = OpFunction  %2  None %85\n%220 = OpLabel\n%232 = OpVariable  %114  Function %88\n%233 = OpVariable  %234  Function %226\n%223 = OpAccessChain  %222  %66 %52\nOpBranch %235\n%235 = OpLabel\nOpLine %3 78 5\n%236 = OpLoad  %6  %232\n%237 = OpISub  %6  %236 %88\nOpLine %3 78 5\nOpStore %232 %237\nOpLine %3 81 14\n%240 = OpAccessChain  %239  %223 %52\n%241 = OpLoad  %50  %240\n%242 = OpFunctionCall  %26  %186 %241\nOpLine %3 82 14\nOpLine %3 82 14\n%245 = OpAccessChain  %244  %223 %52 %52\n%246 = OpLoad  %49  %245\n%247 = OpFunctionCall  %25  %190 %246\nOpLine %3 83 14\nOpLine %3 83 14\nOpLine %3 83 14\n%248 = OpAccessChain  %121  %223 %52 %52 %52\n%249 = OpLoad  %13  %248\nOpLine %3 84 14\nOpLine %3 84 14\n%250 = OpLoad  %6  %232\n%251 = OpAccessChain  %244  %223 %52 %52\n%252 = OpLoad  %49  %251\n%253 = OpFunctionCall  %25  %190 %252\n%254 = OpBitcast  %4  %250\n%255 = OpFunctionCall  %13  %204 %253 %254\nOpLine %3 85 14\nOpLine %3 85 14\nOpLine %3 85 14\nOpLine %3 85 14\n%256 = OpAccessChain  %141  %223 %52 %52 %52 %44\n%257 = OpLoad  %9  %256\nOpLine %3 86 14\nOpLine %3 86 14\nOpLine %3 86 14\n%258 = OpLoad  %6  %232\n%259 = OpAccessChain  %141  %223 %52 %52 %52 %258\n%260 = OpLoad  %9  %259\nOpLine %3 87 14\nOpLine %3 87 14\n%261 = OpLoad  %6  %232\nOpLine %3 87 14\n%262 = OpAccessChain  %244  %223 %52 %52\n%263 = OpLoad  %49  %262\n%264 = OpFunctionCall  %25  %190 %263\n%265 = OpBitcast  %4  %261\n%266 = OpFunctionCall  %13  %204 %264 %265\n%267 = OpCompositeExtract  %9  %266 1\nOpLine %3 88 14\nOpLine %3 88 14\n%268 = OpLoad  %6  %232\n%269 = OpLoad  %6  %232\n%270 = OpAccessChain  %244  %223 %52 %52\n%271 = OpLoad  %49  %270\n%272 = OpFunctionCall  %25  %190 %271\n%273 = OpBitcast  %4  %268\n%274 = OpFunctionCall  %13  %204 %272 %273\n%275 = OpVectorExtractDynamic  %9  %274 %269\nOpLine %3 90 13\nOpLine %3 92 5\n%276 = OpLoad  %6  %232\n%277 = OpIAdd  %6  %276 %88\nOpLine %3 92 5\nOpStore %232 %277\nOpLine %3 95 5\nOpLine %3 95 5\n%279 = OpAccessChain  %278  %233 %52\nOpStore %279 %225\nOpLine %3 96 5\nOpLine %3 96 5\nOpLine %3 96 27\nOpLine %3 96 43\nOpLine %3 96 59\nOpLine %3 96 15\nOpLine %3 96 5\n%281 = OpAccessChain  %280  %233 %52 %52\nOpStore %281 %231\nOpLine %3 97 5\nOpLine %3 97 5\nOpLine %3 97 5\nOpLine %3 97 18\nOpLine %3 97 5\n%282 = OpAccessChain  %174  %233 %52 %52 %52\nOpStore %282 %106\nOpLine %3 98 5\nOpLine %3 98 5\n%283 = OpLoad  %6  %232\nOpLine %3 98 20\nOpLine %3 98 5\n%284 = OpAccessChain  %174  %233 %52 %52 %283\nOpStore %284 %108\nOpLine %3 99 5\nOpLine %3 99 5\nOpLine %3 99 5\nOpLine %3 99 5\nOpLine %3 99 5\n%285 = OpAccessChain  %28  %233 %52 %52 %52 %44\nOpStore %285 %109\nOpLine %3 100 5\nOpLine %3 100 5\nOpLine %3 100 5\n%286 = OpLoad  %6  %232\nOpLine %3 100 5\n%287 = OpAccessChain  %28  %233 %52 %52 %52 %286\nOpStore %287 %110\nOpLine %3 101 5\nOpLine %3 101 5\n%288 = OpLoad  %6  %232\nOpLine %3 101 5\nOpLine %3 101 5\n%289 = OpAccessChain  %28  %233 %52 %52 %288 %44\nOpStore %289 %111\nOpLine %3 102 5\nOpLine %3 102 5\n%290 = OpLoad  %6  %232\n%291 = OpLoad  %6  %232\nOpLine %3 102 5\n%292 = OpAccessChain  %28  %233 %52 %52 %290 %291\nOpStore %292 %112\nOpReturn\nOpFunctionEnd\n%295 = OpFunction  %9  None %296\n%294 = OpFunctionParameter  %28\n%293 = OpLabel\nOpBranch %297\n%297 = OpLabel\nOpLine %3 105 22\n%298 = OpLoad  %9  %294\nOpReturnValue %298\nOpFunctionEnd\n%301 = OpFunction  %9  None %302\n%300 = OpFunctionParameter  %30\n%299 = OpLabel\nOpBranch %303\n%303 = OpLabel\nOpLine %3 110 12\n%304 = OpCompositeExtract  %29  %300 4\nOpLine %3 110 12\n%305 = OpCompositeExtract  %9  %304 9\nOpReturnValue %305\nOpFunctionEnd\n%308 = OpFunction  %2  None %309\n%307 = OpFunctionParameter  %34\n%306 = OpLabel\nOpBranch %311\n%311 = OpLabel\nOpLine %3 159 5\nOpStore %307 %310\nOpReturn\nOpFunctionEnd\n%314 = OpFunction  %2  None %315\n%313 = OpFunctionParameter  %36\n%312 = OpLabel\nOpBranch %319\n%319 = OpLabel\nOpLine %3 163 32\nOpLine %3 163 43\nOpLine %3 163 32\nOpLine %3 163 12\nOpLine %3 163 5\nOpStore %313 %318\nOpReturn\nOpFunctionEnd\n%321 = OpFunction  %2  None %85\n%320 = OpLabel\n%326 = OpVariable  %34  Function %322\n%327 = OpVariable  %36  Function %325\nOpBranch %328\n%328 = OpLabel\nOpLine %3 168 5\n%329 = OpFunctionCall  %2  %308 %326\nOpLine %3 170 35\nOpLine %3 170 46\nOpLine %3 170 35\nOpLine %3 170 15\nOpLine %3 171 5\n%330 = OpFunctionCall  %2  %314 %327\nOpReturn\nOpFunctionEnd\n%333 = OpFunction  %4  None %334\n%332 = OpFunctionParameter  %38\n%331 = OpLabel\nOpBranch %335\n%335 = OpLabel\nOpLine %3 179 10\n%336 = OpAccessChain  %34  %332 %52\n%337 = OpLoad  %4  %336\nOpReturnValue %337\nOpFunctionEnd\n%340 = OpFunction  %2  None %341\n%339 = OpFunctionParameter  %38\n%338 = OpLabel\nOpBranch %342\n%342 = OpLabel\nOpLine %3 183 3\nOpLine %3 183 3\n%343 = OpAccessChain  %34  %339 %52\nOpStore %343 %17\nOpReturn\nOpFunctionEnd\n%346 = OpFunction  %4  None %347\n%345 = OpFunctionParameter  %41\n%344 = OpLabel\nOpBranch %348\n%348 = OpLabel\nOpLine %3 187 10\n%349 = OpAccessChain  %34  %345 %44\n%350 = OpLoad  %4  %349\nOpReturnValue %350\nOpFunctionEnd\n%353 = OpFunction  %2  None %354\n%352 = OpFunctionParameter  %41\n%351 = OpLabel\nOpBranch %355\n%355 = OpLabel\nOpLine %3 191 3\nOpLine %3 191 3\n%356 = OpAccessChain  %34  %352 %44\nOpStore %356 %17\nOpReturn\nOpFunctionEnd\n%358 = OpFunction  %2  None %85\n%357 = OpLabel\n%359 = OpVariable  %38  Function %360\n%361 = OpVariable  %41  Function %362\nOpBranch %363\n%363 = OpLabel\nOpLine %3 196 4\n%364 = OpFunctionCall  %2  %340 %359\nOpLine %3 197 4\n%365 = OpFunctionCall  %4  %333 %359\nOpLine %3 200 4\n%366 = OpFunctionCall  %2  %353 %361\nOpLine %3 201 4\n%367 = OpFunctionCall  %4  %346 %361\nOpReturn\nOpFunctionEnd\n%370 = OpFunction  %42  None %371\n%369 = OpFunctionParameter  %42\n%368 = OpLabel\n%372 = OpVariable  %373  Function %374\nOpBranch %375\n%375 = OpLabel\nOpLine %3 205 13\n%376 = OpCompositeConstruct  %43  %369\nOpLine %3 205 5\nOpStore %372 %376\nOpLine %3 207 12\n%378 = OpAccessChain  %377  %372 %52\n%379 = OpLoad  %42  %378\nOpReturnValue %379\nOpFunctionEnd\n%381 = OpFunction  %6  None %382\n%380 = OpLabel\n%385 = OpVariable  %386  Function %384\nOpBranch %387\n%387 = OpLabel\nOpLine %3 213 16\nOpLine %3 215 12\n%388 = OpAccessChain  %114  %385 %52\n%389 = OpLoad  %6  %388\nOpReturnValue %389\nOpFunctionEnd\n%391 = OpFunction  %6  None %382\n%390 = OpLabel\nOpBranch %393\n%393 = OpLabel\nOpLine %3 225 17\n%394 = OpCompositeExtract  %46  %392 0\nOpLine %3 226 20\n%395 = OpCompositeExtract  %6  %394 0\nOpLine %3 228 9\n%396 = OpCompositeExtract  %4  %392 1\n%397 = OpBitcast  %4  %395\n%398 = OpINotEqual  %42  %396 %397\nOpLine %3 228 5\nOpLine %3 232 12\n%399 = OpCompositeExtract  %46  %392 0\n%400 = OpCompositeExtract  %6  %399 0\nOpReturnValue %400\nOpFunctionEnd\n%402 = OpFunction  %6  None %382\n%401 = OpLabel\n%403 = OpVariable  %404  Function %392\n%405 = OpVariable  %406  Function %407\n%408 = OpVariable  %114  Function %409\nOpBranch %410\n%410 = OpLabel\nOpLine %3 238 17\n%411 = OpAccessChain  %406  %403 %52\n%412 = OpLoad  %46  %411\nOpLine %3 238 5\nOpStore %405 %412\nOpLine %3 239 20\n%413 = OpAccessChain  %114  %405 %52\n%414 = OpLoad  %6  %413\nOpLine %3 239 5\nOpStore %408 %414\nOpLine %3 241 9\n%415 = OpAccessChain  %34  %403 %44\n%416 = OpLoad  %4  %415\n%417 = OpLoad  %6  %408\n%418 = OpBitcast  %4  %417\n%419 = OpINotEqual  %42  %416 %418\nOpLine %3 241 5\nOpLine %3 245 12\n%420 = OpAccessChain  %114  %403 %52 %52\n%421 = OpLoad  %6  %420\nOpReturnValue %421\nOpFunctionEnd\n%428 = OpFunction  %2  None %85\n%422 = OpLabel\n%439 = OpVariable  %28  Function %433\n%440 = OpVariable  %441  Function %442\n%425 = OpLoad  %4  %423\n%429 = OpAccessChain  %86  %60 %52\n%431 = OpAccessChain  %430  %63 %52\n%432 = OpAccessChain  %222  %66 %52\nOpBranch %443\n%443 = OpLabel\nOpLine %3 1 1\n%444 = OpLoad  %9  %439\nOpLine %3 118 5\nOpStore %439 %90\nOpLine %3 120 9\n%445 = OpLoad  %7  %56\nOpLine %3 121 5\n%446 = OpFunctionCall  %2  %84\nOpLine %3 122 5\n%447 = OpFunctionCall  %2  %221\nOpLine %3 125 19\n%449 = OpAccessChain  %448  %58 %52\n%450 = OpLoad  %10  %449\nOpLine %3 126 15\n%452 = OpAccessChain  %451  %58 %40\n%453 = OpLoad  %19  %452\nOpLine %3 128 13\n%456 = OpAccessChain  %455  %58 %52 %434 %52\n%457 = OpLoad  %9  %456\nOpLine %3 129 13\nOpLine %3 129 22\n%459 = OpArrayLength  %4  %58 5\nOpLine %3 129 13\n%460 = OpISub  %4  %459 %15\n%463 = OpAccessChain  %462  %58 %31 %460 %52\n%464 = OpLoad  %6  %463\nOpLine %3 130 13\n%465 = OpLoad  %24  %431\nOpLine %3 133 56\nOpLine %3 133 56\nOpLine %3 134 21\n%466 = OpFunctionCall  %9  %295 %439\nOpLine %3 137 31\n%469 = OpExtInst  %9  %1 FClamp %457 %467 %468\n%470 = OpConvertFToS  %6  %469\nOpLine %3 137 14\n%471 = OpCompositeConstruct  %33  %464 %470 %435 %436 %437\nOpLine %3 137 5\nOpStore %440 %471\nOpLine %3 138 5\n%472 = OpIAdd  %4  %425 %44\nOpLine %3 138 5\n%473 = OpAccessChain  %114  %440 %472\nOpStore %473 %383\nOpLine %3 139 17\n%474 = OpAccessChain  %114  %440 %425\n%475 = OpLoad  %6  %474\nOpLine %3 141 5\n%476 = OpFunctionCall  %9  %301 %438\nOpLine %3 143 22\n%478 = OpCompositeConstruct  %477  %475 %475 %475 %475\n%479 = OpConvertSToF  %32  %478\n%480 = OpMatrixTimesVector  %11  %450 %479\nOpLine %3 143 12\n%481 = OpCompositeConstruct  %32  %480 %92\nOpStore %426 %481\nOpReturn\nOpFunctionEnd\n%484 = OpFunction  %2  None %85\n%482 = OpLabel\n%485 = OpAccessChain  %430  %63 %52\nOpBranch %496\n%496 = OpLabel\nOpLine %3 149 5\nOpLine %3 149 5\nOpLine %3 149 5\n%497 = OpAccessChain  %455  %58 %52 %44 %15\nOpStore %497 %90\nOpLine %3 150 5\nOpLine %3 150 31\nOpLine %3 150 47\nOpLine %3 150 63\nOpLine %3 150 19\nOpLine %3 150 5\n%498 = OpAccessChain  %448  %58 %52\nOpStore %498 %490\nOpLine %3 151 5\nOpLine %3 151 35\nOpLine %3 151 15\nOpLine %3 151 5\n%499 = OpAccessChain  %451  %58 %40\nOpStore %499 %493\nOpLine %3 152 5\nOpLine %3 152 5\nOpLine %3 152 5\n%500 = OpAccessChain  %462  %58 %31 %44 %52\nOpStore %500 %88\nOpLine %3 153 5\nOpStore %485 %494\nOpLine %3 155 12\nOpStore %483 %495\nOpReturn\nOpFunctionEnd\n%502 = OpFunction  %2  None %85\n%501 = OpLabel\nOpBranch %504\n%504 = OpLabel\nOpLine %3 250 5\n%505 = OpFunctionCall  %2  %321\nOpLine %3 251 5\n%506 = OpFunctionCall  %2  %358\nOpLine %3 252 5\n%507 = OpFunctionCall  %42  %370 %503\nOpLine %3 253 5\n%508 = OpFunctionCall  %6  %381\nOpLine %3 254 5\n%509 = OpFunctionCall  %6  %391\nOpLine %3 255 5\n%510 = OpFunctionCall  %6  %402\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-aliased-ray-query.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 258\nOpCapability Shader\nOpCapability RayQueryKHR\nOpExtension \"SPV_KHR_ray_query\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %16 \"main_candidate\" %13\nOpExecutionMode %16 LocalSize 1 1 1\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 4\nOpMemberDecorate %8 2 Offset 8\nOpMemberDecorate %8 3 Offset 12\nOpMemberDecorate %8 4 Offset 16\nOpMemberDecorate %8 5 Offset 32\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 4\nOpMemberDecorate %12 2 Offset 8\nOpMemberDecorate %12 3 Offset 12\nOpMemberDecorate %12 4 Offset 16\nOpMemberDecorate %12 5 Offset 20\nOpMemberDecorate %12 6 Offset 24\nOpMemberDecorate %12 7 Offset 28\nOpMemberDecorate %12 8 Offset 36\nOpMemberDecorate %12 9 Offset 48\nOpMemberDecorate %12 9 ColMajor\nOpMemberDecorate %12 9 MatrixStride 16\nOpMemberDecorate %12 10 Offset 112\nOpMemberDecorate %12 10 ColMajor\nOpMemberDecorate %12 10 MatrixStride 16\nOpDecorate %13 DescriptorSet 0\nOpDecorate %13 Binding 0\n%2 = OpTypeVoid\n%3 = OpTypeRayQueryKHR\n%4 = OpTypeAccelerationStructureKHR\n%5 = OpTypeFloat 32\n%6 = OpTypeVector %5 3\n%7 = OpTypeInt 32 0\n%8 = OpTypeStruct %7 %7 %5 %5 %6 %6\n%9 = OpTypeVector %5 2\n%10 = OpTypeBool\n%11 = OpTypeMatrix %6 4\n%12 = OpTypeStruct %7 %5 %7 %7 %7 %7 %7 %9 %10 %11 %11\n%14 = OpTypePointer UniformConstant %4\n%13 = OpVariable  %14  UniformConstant\n%17 = OpTypeFunction %2\n%19 = OpConstant  %5  0\n%20 = OpConstantComposite  %6  %19 %19 %19\n%21 = OpConstant  %5  1\n%22 = OpConstantComposite  %6  %19 %21 %19\n%23 = OpConstant  %7  4\n%24 = OpConstant  %7  255\n%25 = OpConstant  %5  0.1\n%26 = OpConstant  %5  100\n%27 = OpConstantComposite  %8  %23 %24 %25 %26 %20 %22\n%28 = OpConstant  %7  3\n%29 = OpConstant  %5  10\n%30 = OpConstant  %7  1\n%32 = OpTypePointer Function %3\n%33 = OpTypePointer Function %7\n%35 = OpConstant  %7  0\n%36 = OpTypePointer Function %5\n%39 = OpTypeVector %10 3\n%40 = OpTypeFunction %2 %32 %4 %8 %33 %36\n%68 = OpConstant  %7  256\n%71 = OpConstant  %7  512\n%76 = OpConstant  %7  16\n%79 = OpConstant  %7  32\n%90 = OpConstant  %7  2\n%93 = OpConstant  %7  64\n%96 = OpConstant  %7  128\n%121 = OpTypePointer Function %12\n%122 = OpTypePointer Function %11\n%123 = OpTypePointer Function %9\n%124 = OpTypePointer Function %10\n%125 = OpTypeFunction %12 %32 %33\n%130 = OpConstantNull  %12\n%158 = OpConstant  %7  5\n%160 = OpConstant  %7  6\n%162 = OpConstant  %7  9\n%164 = OpConstant  %7  10\n%173 = OpConstant  %7  7\n%175 = OpConstant  %7  8\n%184 = OpTypeFunction %2 %32 %33 %5 %36\n%225 = OpTypeFunction %2 %32 %33\n%41 = OpFunction  %2  None %40\n%42 = OpFunctionParameter  %32\n%43 = OpFunctionParameter  %4\n%44 = OpFunctionParameter  %8\n%45 = OpFunctionParameter  %33\n%46 = OpFunctionParameter  %36\n%47 = OpLabel\n%48 = OpCompositeExtract  %7  %44 0\n%49 = OpCompositeExtract  %7  %44 1\n%50 = OpCompositeExtract  %5  %44 2\n%51 = OpCompositeExtract  %5  %44 3\nOpStore %46 %51\n%52 = OpCompositeExtract  %6  %44 4\n%53 = OpCompositeExtract  %6  %44 5\n%54 = OpFOrdLessThanEqual  %10  %50 %51\n%55 = OpFOrdGreaterThanEqual  %10  %50 %19\n%56 = OpIsInf  %39  %52\n%57 = OpAny  %10  %56\n%58 = OpIsNan  %39  %52\n%59 = OpAny  %10  %58\n%60 = OpLogicalOr  %10  %59 %57\n%61 = OpLogicalNot  %10  %60\n%62 = OpIsInf  %39  %53\n%63 = OpAny  %10  %62\n%64 = OpIsNan  %39  %53\n%65 = OpAny  %10  %64\n%66 = OpLogicalOr  %10  %65 %63\n%67 = OpLogicalNot  %10  %66\n%69 = OpBitwiseAnd  %7  %48 %68\n%70 = OpINotEqual  %10  %69 %35\n%72 = OpBitwiseAnd  %7  %48 %71\n%73 = OpINotEqual  %10  %72 %35\n%74 = OpLogicalAnd  %10  %73 %70\n%75 = OpLogicalNot  %10  %74\n%77 = OpBitwiseAnd  %7  %48 %76\n%78 = OpINotEqual  %10  %77 %35\n%80 = OpBitwiseAnd  %7  %48 %79\n%81 = OpINotEqual  %10  %80 %35\n%82 = OpLogicalAnd  %10  %81 %70\n%83 = OpLogicalAnd  %10  %81 %78\n%84 = OpLogicalAnd  %10  %78 %70\n%85 = OpLogicalOr  %10  %84 %82\n%86 = OpLogicalOr  %10  %85 %83\n%87 = OpLogicalNot  %10  %86\n%88 = OpBitwiseAnd  %7  %48 %30\n%89 = OpINotEqual  %10  %88 %35\n%91 = OpBitwiseAnd  %7  %48 %90\n%92 = OpINotEqual  %10  %91 %35\n%94 = OpBitwiseAnd  %7  %48 %93\n%95 = OpINotEqual  %10  %94 %35\n%97 = OpBitwiseAnd  %7  %48 %96\n%98 = OpINotEqual  %10  %97 %35\n%99 = OpLogicalAnd  %10  %98 %89\n%100 = OpLogicalAnd  %10  %98 %92\n%101 = OpLogicalAnd  %10  %98 %95\n%102 = OpLogicalAnd  %10  %95 %89\n%103 = OpLogicalAnd  %10  %95 %92\n%104 = OpLogicalAnd  %10  %92 %89\n%105 = OpLogicalOr  %10  %104 %99\n%106 = OpLogicalOr  %10  %105 %100\n%107 = OpLogicalOr  %10  %106 %101\n%108 = OpLogicalOr  %10  %107 %102\n%109 = OpLogicalOr  %10  %108 %103\n%110 = OpLogicalNot  %10  %109\n%111 = OpLogicalAnd  %10  %110 %54\n%112 = OpLogicalAnd  %10  %111 %55\n%113 = OpLogicalAnd  %10  %112 %61\n%114 = OpLogicalAnd  %10  %113 %67\n%115 = OpLogicalAnd  %10  %114 %75\n%116 = OpLogicalAnd  %10  %115 %87\nOpSelectionMerge %117 None\nOpBranchConditional %116 %119 %118\n%119 = OpLabel\nOpRayQueryInitializeKHR %42 %43 %48 %49 %52 %50 %53 %51\nOpStore %45 %30\nOpBranch %117\n%118 = OpLabel\nOpBranch %117\n%117 = OpLabel\nOpReturn\nOpFunctionEnd\n%126 = OpFunction  %12  None %125\n%127 = OpFunctionParameter  %32\n%128 = OpFunctionParameter  %33\n%129 = OpLabel\n%131 = OpVariable  %121  Function %130\n%132 = OpLoad  %7  %128\n%133 = OpBitwiseAnd  %7  %132 %90\n%134 = OpINotEqual  %10  %133 %35\n%135 = OpBitwiseAnd  %7  %132 %23\n%136 = OpINotEqual  %10  %135 %35\n%137 = OpLogicalNot  %10  %136\n%138 = OpLogicalAnd  %10  %137 %134\nOpSelectionMerge %140 None\nOpBranchConditional %138 %139 %140\n%139 = OpLabel\n%141 = OpRayQueryGetIntersectionTypeKHR  %7  %127 %35\n%142 = OpIEqual  %10  %141 %35\n%143 = OpSelect  %7  %142 %30 %28\n%144 = OpAccessChain  %33  %131 %35\nOpStore %144 %143\n%145 = OpINotEqual  %10  %143 %35\nOpSelectionMerge %147 None\nOpBranchConditional %145 %146 %147\n%146 = OpLabel\n%148 = OpRayQueryGetIntersectionInstanceCustomIndexKHR  %7  %127 %35\n%149 = OpRayQueryGetIntersectionInstanceIdKHR  %7  %127 %35\n%150 = OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR  %7  %127 %35\n%151 = OpRayQueryGetIntersectionGeometryIndexKHR  %7  %127 %35\n%152 = OpRayQueryGetIntersectionPrimitiveIndexKHR  %7  %127 %35\n%153 = OpRayQueryGetIntersectionObjectToWorldKHR  %11  %127 %35\n%154 = OpRayQueryGetIntersectionWorldToObjectKHR  %11  %127 %35\n%155 = OpAccessChain  %33  %131 %90\nOpStore %155 %148\n%156 = OpAccessChain  %33  %131 %28\nOpStore %156 %149\n%157 = OpAccessChain  %33  %131 %23\nOpStore %157 %150\n%159 = OpAccessChain  %33  %131 %158\nOpStore %159 %151\n%161 = OpAccessChain  %33  %131 %160\nOpStore %161 %152\n%163 = OpAccessChain  %122  %131 %162\nOpStore %163 %153\n%165 = OpAccessChain  %122  %131 %164\nOpStore %165 %154\n%166 = OpIEqual  %10  %143 %30\nOpSelectionMerge %168 None\nOpBranchConditional %145 %167 %168\n%167 = OpLabel\n%169 = OpRayQueryGetIntersectionTKHR  %5  %127 %35\n%170 = OpAccessChain  %36  %131 %30\nOpStore %170 %169\n%171 = OpRayQueryGetIntersectionBarycentricsKHR  %9  %127 %35\n%172 = OpRayQueryGetIntersectionFrontFaceKHR  %10  %127 %35\n%174 = OpAccessChain  %123  %131 %173\nOpStore %174 %171\n%176 = OpAccessChain  %124  %131 %175\nOpStore %176 %172\nOpBranch %168\n%168 = OpLabel\nOpBranch %147\n%147 = OpLabel\nOpBranch %140\n%140 = OpLabel\n%177 = OpLoad  %12  %131\nOpReturnValue %177\nOpFunctionEnd\n%185 = OpFunction  %2  None %184\n%186 = OpFunctionParameter  %32\n%187 = OpFunctionParameter  %33\n%188 = OpFunctionParameter  %5\n%189 = OpFunctionParameter  %36\n%190 = OpLabel\n%191 = OpVariable  %36  Function\n%192 = OpVariable  %36  Function\n%195 = OpLoad  %7  %187\n%196 = OpBitwiseAnd  %7  %195 %90\n%197 = OpINotEqual  %10  %196 %35\n%198 = OpBitwiseAnd  %7  %195 %23\n%199 = OpINotEqual  %10  %198 %35\n%200 = OpLogicalNot  %10  %199\n%201 = OpLogicalAnd  %10  %200 %197\nOpSelectionMerge %194 None\nOpBranchConditional %201 %193 %194\n%193 = OpLabel\n%202 = OpRayQueryGetIntersectionTypeKHR  %7  %186 %35\n%203 = OpIEqual  %10  %202 %30\n%204 = OpRayQueryGetRayTMinKHR  %5  %186\n%205 = OpRayQueryGetIntersectionTypeKHR  %7  %186 %30\n%206 = OpIEqual  %10  %205 %35\nOpSelectionMerge %207 None\nOpBranchConditional %206 %208 %209\n%208 = OpLabel\n%210 = OpLoad  %5  %189\nOpStore %192 %210\nOpBranch %207\n%209 = OpLabel\n%211 = OpRayQueryGetIntersectionTKHR  %5  %186 %35\nOpStore %192 %211\nOpBranch %207\n%207 = OpLabel\n%212 = OpFOrdGreaterThanEqual  %10  %188 %204\n%213 = OpLoad  %5  %192\n%214 = OpFOrdLessThanEqual  %10  %188 %213\n%215 = OpLogicalAnd  %10  %212 %214\n%216 = OpLogicalAnd  %10  %215 %203\nOpSelectionMerge %218 None\nOpBranchConditional %216 %217 %218\n%217 = OpLabel\nOpRayQueryGenerateIntersectionKHR %186 %188\nOpBranch %218\n%218 = OpLabel\nOpBranch %194\n%194 = OpLabel\nOpReturn\nOpFunctionEnd\n%226 = OpFunction  %2  None %225\n%227 = OpFunctionParameter  %32\n%228 = OpFunctionParameter  %33\n%229 = OpLabel\n%232 = OpLoad  %7  %228\n%233 = OpBitwiseAnd  %7  %232 %90\n%234 = OpINotEqual  %10  %233 %35\n%235 = OpBitwiseAnd  %7  %232 %23\n%236 = OpINotEqual  %10  %235 %35\n%237 = OpLogicalNot  %10  %236\n%238 = OpLogicalAnd  %10  %237 %234\nOpSelectionMerge %231 None\nOpBranchConditional %238 %230 %231\n%230 = OpLabel\n%239 = OpRayQueryGetIntersectionTypeKHR  %7  %227 %35\n%240 = OpIEqual  %10  %239 %35\nOpSelectionMerge %242 None\nOpBranchConditional %240 %241 %242\n%241 = OpLabel\nOpRayQueryConfirmIntersectionKHR %227\nOpBranch %242\n%242 = OpLabel\nOpBranch %231\n%231 = OpLabel\nOpReturn\nOpFunctionEnd\n%245 = OpFunction  %2  None %225\n%246 = OpFunctionParameter  %32\n%247 = OpFunctionParameter  %33\n%248 = OpLabel\n%249 = OpLoad  %7  %247\n%252 = OpBitwiseAnd  %7  %249 %90\n%253 = OpINotEqual  %10  %252 %35\n%254 = OpBitwiseAnd  %7  %249 %23\n%255 = OpINotEqual  %10  %254 %35\n%256 = OpLogicalNot  %10  %255\n%257 = OpLogicalAnd  %10  %256 %253\nOpSelectionMerge %250 None\nOpBranchConditional %257 %251 %250\n%251 = OpLabel\nOpRayQueryTerminateKHR %246\nOpBranch %250\n%250 = OpLabel\nOpReturn\nOpFunctionEnd\n%16 = OpFunction  %2  None %17\n%15 = OpLabel\n%31 = OpVariable  %32  Function\n%34 = OpVariable  %33  Function %35\n%37 = OpVariable  %36  Function %19\n%18 = OpLoad  %4  %13\nOpBranch %38\n%38 = OpLabel\n%120 = OpFunctionCall  %2  %41 %31 %18 %27 %34 %37\n%178 = OpFunctionCall  %12  %126 %31 %34\n%179 = OpCompositeExtract  %7  %178 0\n%180 = OpIEqual  %10  %179 %28\nOpSelectionMerge %181 None\nOpBranchConditional %180 %182 %183\n%182 = OpLabel\n%219 = OpFunctionCall  %2  %185 %31 %34 %29 %37\nOpReturn\n%183 = OpLabel\n%220 = OpCompositeExtract  %7  %178 0\n%221 = OpIEqual  %10  %220 %30\nOpSelectionMerge %222 None\nOpBranchConditional %221 %223 %224\n%223 = OpLabel\n%243 = OpFunctionCall  %2  %226 %31 %34\nOpReturn\n%224 = OpLabel\n%244 = OpFunctionCall  %2  %245 %31 %34\nOpReturn\n%222 = OpLabel\nOpBranch %181\n%181 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-array-in-ctor.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 19\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %12 \"cs_main\"\nOpExecutionMode %12 LocalSize 1 1 1\nOpDecorate %4 ArrayStride 4\nOpMemberDecorate %7 0 Offset 0\nOpDecorate %8 NonWritable\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 0\nOpDecorate %9 Block\nOpMemberDecorate %9 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  2\n%4 = OpTypeArray %3 %5\n%7 = OpTypeStruct %4\n%9 = OpTypeStruct %7\n%10 = OpTypePointer StorageBuffer %9\n%8 = OpVariable  %10  StorageBuffer\n%13 = OpTypeFunction %2\n%14 = OpTypePointer StorageBuffer %7\n%15 = OpConstant  %6  0\n%12 = OpFunction  %2  None %13\n%11 = OpLabel\n%16 = OpAccessChain  %14  %8 %15\nOpBranch %17\n%17 = OpLabel\n%18 = OpLoad  %7  %16\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-array-in-function-return-type.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 38\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %28 \"main\" %26\nOpExecutionMode %28 OriginUpperLeft\nOpDecorate %4 ArrayStride 4\nOpDecorate %7 ArrayStride 8\nOpDecorate %26 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  2\n%4 = OpTypeArray %3 %5\n%8 = OpConstant  %6  3\n%7 = OpTypeArray %4 %8\n%9 = OpTypeVector %3 4\n%12 = OpTypeFunction %4\n%13 = OpConstant  %3  1\n%14 = OpConstant  %3  2\n%15 = OpConstantComposite  %4  %13 %14\n%19 = OpTypeFunction %7\n%27 = OpTypePointer Output %9\n%26 = OpVariable  %27  Output\n%29 = OpTypeFunction %2\n%30 = OpConstant  %3  0\n%11 = OpFunction  %4  None %12\n%10 = OpLabel\nOpBranch %16\n%16 = OpLabel\nOpReturnValue %15\nOpFunctionEnd\n%18 = OpFunction  %7  None %19\n%17 = OpLabel\nOpBranch %20\n%20 = OpLabel\n%21 = OpFunctionCall  %4  %11\n%22 = OpFunctionCall  %4  %11\n%23 = OpFunctionCall  %4  %11\n%24 = OpCompositeConstruct  %7  %21 %22 %23\nOpReturnValue %24\nOpFunctionEnd\n%28 = OpFunction  %2  None %29\n%25 = OpLabel\nOpBranch %31\n%31 = OpLabel\n%32 = OpFunctionCall  %7  %18\n%33 = OpCompositeExtract  %4  %32 0\n%34 = OpCompositeExtract  %3  %33 0\n%35 = OpCompositeExtract  %4  %32 0\n%36 = OpCompositeExtract  %3  %35 1\n%37 = OpCompositeConstruct  %9  %34 %36 %30 %13\nOpStore %26 %37\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicCompareExchange-int64.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 172\nOpCapability Shader\nOpCapability Int64\nOpCapability Int64Atomics\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %19 \"test_atomic_compare_exchange_i64\"\nOpEntryPoint GLCompute %103 \"test_atomic_compare_exchange_u64\"\nOpExecutionMode %19 LocalSize 1 1 1\nOpExecutionMode %103 LocalSize 1 1 1\nOpDecorate %5 ArrayStride 8\nOpDecorate %8 ArrayStride 8\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 8\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 8\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\nOpDecorate %13 Block\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 1\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 64 1\n%6 = OpConstant  %3  128\n%5 = OpTypeArray %4 %6\n%7 = OpTypeInt 64 0\n%8 = OpTypeArray %7 %6\n%9 = OpTypeBool\n%10 = OpTypeStruct %4 %9\n%11 = OpTypeStruct %7 %9\n%13 = OpTypeStruct %5\n%14 = OpTypePointer StorageBuffer %13\n%12 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %8\n%17 = OpTypePointer StorageBuffer %16\n%15 = OpVariable  %17  StorageBuffer\n%20 = OpTypeFunction %2\n%21 = OpTypePointer StorageBuffer %5\n%22 = OpConstant  %3  0\n%24 = OpConstantFalse  %9\n%25 = OpConstant  %4  10\n%26 = OpConstant  %3  1\n%28 = OpTypePointer Function %3\n%30 = OpTypePointer Function %4\n%31 = OpConstantNull  %4\n%33 = OpTypePointer Function %9\n%34 = OpConstantNull  %9\n%40 = OpTypeVector %3 2\n%41 = OpTypePointer Function %40\n%42 = OpTypeVector %9 2\n%43 = OpConstantComposite  %40  %22 %22\n%44 = OpConstant  %3  4294967295\n%45 = OpConstantComposite  %40  %44 %44\n%64 = OpTypePointer StorageBuffer %4\n%68 = OpTypeInt 32 1\n%67 = OpConstant  %68  1\n%104 = OpTypePointer StorageBuffer %8\n%106 = OpConstant  %7  10\n%109 = OpTypePointer Function %7\n%110 = OpConstantNull  %7\n%112 = OpConstantNull  %9\n%136 = OpTypePointer StorageBuffer %7\n%19 = OpFunction  %2  None %20\n%18 = OpLabel\n%27 = OpVariable  %28  Function %22\n%29 = OpVariable  %30  Function %31\n%32 = OpVariable  %33  Function %34\n%46 = OpVariable  %41  Function %45\n%73 = OpVariable  %41  Function %45\n%23 = OpAccessChain  %21  %12 %22\nOpBranch %35\n%35 = OpLabel\nOpBranch %36\n%36 = OpLabel\nOpLoopMerge %37 %39 None\nOpBranch %47\n%47 = OpLabel\n%48 = OpLoad  %40  %46\n%49 = OpIEqual  %42  %43 %48\n%50 = OpAll  %9  %49\nOpSelectionMerge %51 None\nOpBranchConditional %50 %37 %51\n%51 = OpLabel\n%52 = OpCompositeExtract  %3  %48 1\n%53 = OpIEqual  %9  %52 %22\n%54 = OpSelect  %3  %53 %26 %22\n%55 = OpCompositeConstruct  %40  %54 %26\n%56 = OpISub  %40  %48 %55\nOpStore %46 %56\nOpBranch %38\n%38 = OpLabel\n%57 = OpLoad  %3  %27\n%58 = OpULessThan  %9  %57 %6\nOpSelectionMerge %59 None\nOpBranchConditional %58 %59 %60\n%60 = OpLabel\nOpBranch %37\n%59 = OpLabel\nOpBranch %61\n%61 = OpLabel\n%63 = OpLoad  %3  %27\n%65 = OpAccessChain  %64  %23 %63\n%66 = OpAtomicLoad  %4  %65 %67 %22\nOpStore %29 %66\nOpStore %32 %24\nOpBranch %69\n%69 = OpLabel\nOpLoopMerge %70 %72 None\nOpBranch %74\n%74 = OpLabel\n%75 = OpLoad  %40  %73\n%76 = OpIEqual  %42  %43 %75\n%77 = OpAll  %9  %76\nOpSelectionMerge %78 None\nOpBranchConditional %77 %70 %78\n%78 = OpLabel\n%79 = OpCompositeExtract  %3  %75 1\n%80 = OpIEqual  %9  %79 %22\n%81 = OpSelect  %3  %80 %26 %22\n%82 = OpCompositeConstruct  %40  %81 %26\n%83 = OpISub  %40  %75 %82\nOpStore %73 %83\nOpBranch %71\n%71 = OpLabel\n%84 = OpLoad  %9  %32\n%85 = OpLogicalNot  %9  %84\nOpSelectionMerge %86 None\nOpBranchConditional %85 %86 %87\n%87 = OpLabel\nOpBranch %70\n%86 = OpLabel\nOpBranch %88\n%88 = OpLabel\n%90 = OpLoad  %4  %29\n%91 = OpIAdd  %4  %90 %25\n%92 = OpLoad  %3  %27\n%93 = OpLoad  %4  %29\n%95 = OpAccessChain  %64  %23 %92\n%96 = OpAtomicCompareExchange  %4  %95 %67 %22 %22 %91 %93\n%97 = OpIEqual  %9  %96 %93\n%94 = OpCompositeConstruct  %10  %96 %97\n%98 = OpCompositeExtract  %4  %94 0\nOpStore %29 %98\n%99 = OpCompositeExtract  %9  %94 1\nOpStore %32 %99\nOpBranch %89\n%89 = OpLabel\nOpBranch %72\n%72 = OpLabel\nOpBranch %69\n%70 = OpLabel\nOpBranch %62\n%62 = OpLabel\nOpBranch %39\n%39 = OpLabel\n%100 = OpLoad  %3  %27\n%101 = OpIAdd  %3  %100 %26\nOpStore %27 %101\nOpBranch %36\n%37 = OpLabel\nOpReturn\nOpFunctionEnd\n%103 = OpFunction  %2  None %20\n%102 = OpLabel\n%107 = OpVariable  %28  Function %22\n%108 = OpVariable  %109  Function %110\n%111 = OpVariable  %33  Function %112\n%118 = OpVariable  %41  Function %45\n%143 = OpVariable  %41  Function %45\n%105 = OpAccessChain  %104  %15 %22\nOpBranch %113\n%113 = OpLabel\nOpBranch %114\n%114 = OpLabel\nOpLoopMerge %115 %117 None\nOpBranch %119\n%119 = OpLabel\n%120 = OpLoad  %40  %118\n%121 = OpIEqual  %42  %43 %120\n%122 = OpAll  %9  %121\nOpSelectionMerge %123 None\nOpBranchConditional %122 %115 %123\n%123 = OpLabel\n%124 = OpCompositeExtract  %3  %120 1\n%125 = OpIEqual  %9  %124 %22\n%126 = OpSelect  %3  %125 %26 %22\n%127 = OpCompositeConstruct  %40  %126 %26\n%128 = OpISub  %40  %120 %127\nOpStore %118 %128\nOpBranch %116\n%116 = OpLabel\n%129 = OpLoad  %3  %107\n%130 = OpULessThan  %9  %129 %6\nOpSelectionMerge %131 None\nOpBranchConditional %130 %131 %132\n%132 = OpLabel\nOpBranch %115\n%131 = OpLabel\nOpBranch %133\n%133 = OpLabel\n%135 = OpLoad  %3  %107\n%137 = OpAccessChain  %136  %105 %135\n%138 = OpAtomicLoad  %7  %137 %67 %22\nOpStore %108 %138\nOpStore %111 %24\nOpBranch %139\n%139 = OpLabel\nOpLoopMerge %140 %142 None\nOpBranch %144\n%144 = OpLabel\n%145 = OpLoad  %40  %143\n%146 = OpIEqual  %42  %43 %145\n%147 = OpAll  %9  %146\nOpSelectionMerge %148 None\nOpBranchConditional %147 %140 %148\n%148 = OpLabel\n%149 = OpCompositeExtract  %3  %145 1\n%150 = OpIEqual  %9  %149 %22\n%151 = OpSelect  %3  %150 %26 %22\n%152 = OpCompositeConstruct  %40  %151 %26\n%153 = OpISub  %40  %145 %152\nOpStore %143 %153\nOpBranch %141\n%141 = OpLabel\n%154 = OpLoad  %9  %111\n%155 = OpLogicalNot  %9  %154\nOpSelectionMerge %156 None\nOpBranchConditional %155 %156 %157\n%157 = OpLabel\nOpBranch %140\n%156 = OpLabel\nOpBranch %158\n%158 = OpLabel\n%160 = OpLoad  %7  %108\n%161 = OpIAdd  %7  %160 %106\n%162 = OpLoad  %3  %107\n%163 = OpLoad  %7  %108\n%165 = OpAccessChain  %136  %105 %162\n%166 = OpAtomicCompareExchange  %7  %165 %67 %22 %22 %161 %163\n%167 = OpIEqual  %9  %166 %163\n%164 = OpCompositeConstruct  %11  %166 %167\n%168 = OpCompositeExtract  %7  %164 0\nOpStore %108 %168\n%169 = OpCompositeExtract  %9  %164 1\nOpStore %111 %169\nOpBranch %159\n%159 = OpLabel\nOpBranch %142\n%142 = OpLabel\nOpBranch %139\n%140 = OpLabel\nOpBranch %134\n%134 = OpLabel\nOpBranch %117\n%117 = OpLabel\n%170 = OpLoad  %3  %107\n%171 = OpIAdd  %3  %170 %26\nOpStore %107 %171\nOpBranch %114\n%115 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicCompareExchange.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 173\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %18 \"test_atomic_compare_exchange_i32\"\nOpEntryPoint GLCompute %104 \"test_atomic_compare_exchange_u32\"\nOpExecutionMode %18 LocalSize 1 1 1\nOpExecutionMode %104 LocalSize 1 1 1\nOpDecorate %5 ArrayStride 4\nOpDecorate %7 ArrayStride 4\nOpMemberDecorate %9 0 Offset 0\nOpMemberDecorate %9 1 Offset 4\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 4\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 0\nOpDecorate %12 Block\nOpMemberDecorate %12 0 Offset 0\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 1\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 32 1\n%6 = OpConstant  %3  128\n%5 = OpTypeArray %4 %6\n%7 = OpTypeArray %3 %6\n%8 = OpTypeBool\n%9 = OpTypeStruct %4 %8\n%10 = OpTypeStruct %3 %8\n%12 = OpTypeStruct %5\n%13 = OpTypePointer StorageBuffer %12\n%11 = OpVariable  %13  StorageBuffer\n%15 = OpTypeStruct %7\n%16 = OpTypePointer StorageBuffer %15\n%14 = OpVariable  %16  StorageBuffer\n%19 = OpTypeFunction %2\n%20 = OpTypePointer StorageBuffer %5\n%21 = OpConstant  %3  0\n%23 = OpConstantFalse  %8\n%24 = OpTypeFloat 32\n%25 = OpConstant  %24  1\n%26 = OpConstant  %3  1\n%28 = OpTypePointer Function %3\n%30 = OpTypePointer Function %4\n%31 = OpConstantNull  %4\n%33 = OpTypePointer Function %8\n%34 = OpConstantNull  %8\n%40 = OpTypeVector %3 2\n%41 = OpTypePointer Function %40\n%42 = OpTypeVector %8 2\n%43 = OpConstantComposite  %40  %21 %21\n%44 = OpConstant  %3  4294967295\n%45 = OpConstantComposite  %40  %44 %44\n%64 = OpTypePointer StorageBuffer %4\n%67 = OpConstant  %4  1\n%105 = OpTypePointer StorageBuffer %7\n%109 = OpConstantNull  %3\n%111 = OpConstantNull  %8\n%135 = OpTypePointer StorageBuffer %3\n%18 = OpFunction  %2  None %19\n%17 = OpLabel\n%27 = OpVariable  %28  Function %21\n%29 = OpVariable  %30  Function %31\n%32 = OpVariable  %33  Function %34\n%46 = OpVariable  %41  Function %45\n%72 = OpVariable  %41  Function %45\n%22 = OpAccessChain  %20  %11 %21\nOpBranch %35\n%35 = OpLabel\nOpBranch %36\n%36 = OpLabel\nOpLoopMerge %37 %39 None\nOpBranch %47\n%47 = OpLabel\n%48 = OpLoad  %40  %46\n%49 = OpIEqual  %42  %43 %48\n%50 = OpAll  %8  %49\nOpSelectionMerge %51 None\nOpBranchConditional %50 %37 %51\n%51 = OpLabel\n%52 = OpCompositeExtract  %3  %48 1\n%53 = OpIEqual  %8  %52 %21\n%54 = OpSelect  %3  %53 %26 %21\n%55 = OpCompositeConstruct  %40  %54 %26\n%56 = OpISub  %40  %48 %55\nOpStore %46 %56\nOpBranch %38\n%38 = OpLabel\n%57 = OpLoad  %3  %27\n%58 = OpULessThan  %8  %57 %6\nOpSelectionMerge %59 None\nOpBranchConditional %58 %59 %60\n%60 = OpLabel\nOpBranch %37\n%59 = OpLabel\nOpBranch %61\n%61 = OpLabel\n%63 = OpLoad  %3  %27\n%65 = OpAccessChain  %64  %22 %63\n%66 = OpAtomicLoad  %4  %65 %67 %21\nOpStore %29 %66\nOpStore %32 %23\nOpBranch %68\n%68 = OpLabel\nOpLoopMerge %69 %71 None\nOpBranch %73\n%73 = OpLabel\n%74 = OpLoad  %40  %72\n%75 = OpIEqual  %42  %43 %74\n%76 = OpAll  %8  %75\nOpSelectionMerge %77 None\nOpBranchConditional %76 %69 %77\n%77 = OpLabel\n%78 = OpCompositeExtract  %3  %74 1\n%79 = OpIEqual  %8  %78 %21\n%80 = OpSelect  %3  %79 %26 %21\n%81 = OpCompositeConstruct  %40  %80 %26\n%82 = OpISub  %40  %74 %81\nOpStore %72 %82\nOpBranch %70\n%70 = OpLabel\n%83 = OpLoad  %8  %32\n%84 = OpLogicalNot  %8  %83\nOpSelectionMerge %85 None\nOpBranchConditional %84 %85 %86\n%86 = OpLabel\nOpBranch %69\n%85 = OpLabel\nOpBranch %87\n%87 = OpLabel\n%89 = OpLoad  %4  %29\n%90 = OpBitcast  %24  %89\n%91 = OpFAdd  %24  %90 %25\n%92 = OpBitcast  %4  %91\n%93 = OpLoad  %3  %27\n%94 = OpLoad  %4  %29\n%96 = OpAccessChain  %64  %22 %93\n%97 = OpAtomicCompareExchange  %4  %96 %67 %21 %21 %92 %94\n%98 = OpIEqual  %8  %97 %94\n%95 = OpCompositeConstruct  %9  %97 %98\n%99 = OpCompositeExtract  %4  %95 0\nOpStore %29 %99\n%100 = OpCompositeExtract  %8  %95 1\nOpStore %32 %100\nOpBranch %88\n%88 = OpLabel\nOpBranch %71\n%71 = OpLabel\nOpBranch %68\n%69 = OpLabel\nOpBranch %62\n%62 = OpLabel\nOpBranch %39\n%39 = OpLabel\n%101 = OpLoad  %3  %27\n%102 = OpIAdd  %3  %101 %26\nOpStore %27 %102\nOpBranch %36\n%37 = OpLabel\nOpReturn\nOpFunctionEnd\n%104 = OpFunction  %2  None %19\n%103 = OpLabel\n%107 = OpVariable  %28  Function %21\n%108 = OpVariable  %28  Function %109\n%110 = OpVariable  %33  Function %111\n%117 = OpVariable  %41  Function %45\n%142 = OpVariable  %41  Function %45\n%106 = OpAccessChain  %105  %14 %21\nOpBranch %112\n%112 = OpLabel\nOpBranch %113\n%113 = OpLabel\nOpLoopMerge %114 %116 None\nOpBranch %118\n%118 = OpLabel\n%119 = OpLoad  %40  %117\n%120 = OpIEqual  %42  %43 %119\n%121 = OpAll  %8  %120\nOpSelectionMerge %122 None\nOpBranchConditional %121 %114 %122\n%122 = OpLabel\n%123 = OpCompositeExtract  %3  %119 1\n%124 = OpIEqual  %8  %123 %21\n%125 = OpSelect  %3  %124 %26 %21\n%126 = OpCompositeConstruct  %40  %125 %26\n%127 = OpISub  %40  %119 %126\nOpStore %117 %127\nOpBranch %115\n%115 = OpLabel\n%128 = OpLoad  %3  %107\n%129 = OpULessThan  %8  %128 %6\nOpSelectionMerge %130 None\nOpBranchConditional %129 %130 %131\n%131 = OpLabel\nOpBranch %114\n%130 = OpLabel\nOpBranch %132\n%132 = OpLabel\n%134 = OpLoad  %3  %107\n%136 = OpAccessChain  %135  %106 %134\n%137 = OpAtomicLoad  %3  %136 %67 %21\nOpStore %108 %137\nOpStore %110 %23\nOpBranch %138\n%138 = OpLabel\nOpLoopMerge %139 %141 None\nOpBranch %143\n%143 = OpLabel\n%144 = OpLoad  %40  %142\n%145 = OpIEqual  %42  %43 %144\n%146 = OpAll  %8  %145\nOpSelectionMerge %147 None\nOpBranchConditional %146 %139 %147\n%147 = OpLabel\n%148 = OpCompositeExtract  %3  %144 1\n%149 = OpIEqual  %8  %148 %21\n%150 = OpSelect  %3  %149 %26 %21\n%151 = OpCompositeConstruct  %40  %150 %26\n%152 = OpISub  %40  %144 %151\nOpStore %142 %152\nOpBranch %140\n%140 = OpLabel\n%153 = OpLoad  %8  %110\n%154 = OpLogicalNot  %8  %153\nOpSelectionMerge %155 None\nOpBranchConditional %154 %155 %156\n%156 = OpLabel\nOpBranch %139\n%155 = OpLabel\nOpBranch %157\n%157 = OpLabel\n%159 = OpLoad  %3  %108\n%160 = OpBitcast  %24  %159\n%161 = OpFAdd  %24  %160 %25\n%162 = OpBitcast  %3  %161\n%163 = OpLoad  %3  %107\n%164 = OpLoad  %3  %108\n%166 = OpAccessChain  %135  %106 %163\n%167 = OpAtomicCompareExchange  %3  %166 %67 %21 %21 %162 %164\n%168 = OpIEqual  %8  %167 %164\n%165 = OpCompositeConstruct  %10  %167 %168\n%169 = OpCompositeExtract  %3  %165 0\nOpStore %108 %169\n%170 = OpCompositeExtract  %8  %165 1\nOpStore %110 %170\nOpBranch %158\n%158 = OpLabel\nOpBranch %141\n%141 = OpLabel\nOpBranch %138\n%139 = OpLabel\nOpBranch %133\n%133 = OpLabel\nOpBranch %116\n%116 = OpLabel\n%171 = OpLoad  %3  %107\n%172 = OpIAdd  %3  %171 %26\nOpStore %107 %172\nOpBranch %113\n%114 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicOps-float32.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 61\nOpCapability Shader\nOpCapability AtomicFloat32AddEXT\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\nOpExtension \"SPV_EXT_shader_atomic_float_add\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %22 \"cs_main\" %19\nOpExecutionMode %22 LocalSize 2 1 1\nOpDecorate %4 ArrayStride 4\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 0\nOpDecorate %10 Block\nOpMemberDecorate %10 0 Offset 0\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 1\nOpDecorate %13 Block\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 2\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %19 BuiltIn LocalInvocationId\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  2\n%4 = OpTypeArray %3 %5\n%7 = OpTypeStruct %3 %4\n%8 = OpTypeVector %6 3\n%10 = OpTypeStruct %3\n%11 = OpTypePointer StorageBuffer %10\n%9 = OpVariable  %11  StorageBuffer\n%13 = OpTypeStruct %4\n%14 = OpTypePointer StorageBuffer %13\n%12 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %7\n%17 = OpTypePointer StorageBuffer %16\n%15 = OpVariable  %17  StorageBuffer\n%20 = OpTypePointer Input %8\n%19 = OpVariable  %20  Input\n%23 = OpTypeFunction %2\n%24 = OpTypePointer StorageBuffer %3\n%25 = OpConstant  %6  0\n%27 = OpTypePointer StorageBuffer %4\n%29 = OpTypePointer StorageBuffer %7\n%31 = OpConstant  %3  1.5\n%34 = OpTypeInt 32 1\n%33 = OpConstant  %34  1\n%35 = OpConstant  %6  1\n%39 = OpConstant  %6  264\n%22 = OpFunction  %2  None %23\n%18 = OpLabel\n%21 = OpLoad  %8  %19\n%26 = OpAccessChain  %24  %9 %25\n%28 = OpAccessChain  %27  %12 %25\n%30 = OpAccessChain  %29  %15 %25\nOpBranch %32\n%32 = OpLabel\nOpAtomicStore %26 %33 %25 %31\n%36 = OpAccessChain  %24  %28 %35\nOpAtomicStore %36 %33 %25 %31\n%37 = OpAccessChain  %24  %30 %25\nOpAtomicStore %37 %33 %25 %31\n%38 = OpAccessChain  %24  %30 %35 %35\nOpAtomicStore %38 %33 %25 %31\nOpControlBarrier %5 %5 %39\n%40 = OpAtomicLoad  %3  %26 %33 %25\n%41 = OpAccessChain  %24  %28 %35\n%42 = OpAtomicLoad  %3  %41 %33 %25\n%43 = OpAccessChain  %24  %30 %25\n%44 = OpAtomicLoad  %3  %43 %33 %25\n%45 = OpAccessChain  %24  %30 %35 %35\n%46 = OpAtomicLoad  %3  %45 %33 %25\nOpControlBarrier %5 %5 %39\n%47 = OpAtomicFAddEXT  %3  %26 %33 %25 %31\n%49 = OpAccessChain  %24  %28 %35\n%48 = OpAtomicFAddEXT  %3  %49 %33 %25 %31\n%51 = OpAccessChain  %24  %30 %25\n%50 = OpAtomicFAddEXT  %3  %51 %33 %25 %31\n%53 = OpAccessChain  %24  %30 %35 %35\n%52 = OpAtomicFAddEXT  %3  %53 %33 %25 %31\nOpControlBarrier %5 %5 %39\n%54 = OpAtomicExchange  %3  %26 %33 %25 %31\n%56 = OpAccessChain  %24  %28 %35\n%55 = OpAtomicExchange  %3  %56 %33 %25 %31\n%58 = OpAccessChain  %24  %30 %25\n%57 = OpAtomicExchange  %3  %58 %33 %25 %31\n%60 = OpAccessChain  %24  %30 %35 %35\n%59 = OpAtomicExchange  %3  %60 %33 %25 %31\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicOps-int64-min-max.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 66\nOpCapability Shader\nOpCapability Int64\nOpCapability Int64Atomics\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %25 \"cs_main\" %22\nOpExecutionMode %25 LocalSize 2 1 1\nOpDecorate %4 ArrayStride 8\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 8\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 0\nOpDecorate %10 Block\nOpMemberDecorate %10 0 Offset 0\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 1\nOpDecorate %13 Block\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 2\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %18 DescriptorSet 0\nOpDecorate %18 Binding 3\nOpDecorate %19 Block\nOpMemberDecorate %19 0 Offset 0\nOpDecorate %22 BuiltIn LocalInvocationId\n%2 = OpTypeVoid\n%3 = OpTypeInt 64 0\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  2\n%4 = OpTypeArray %3 %5\n%7 = OpTypeStruct %3 %4\n%8 = OpTypeVector %6 3\n%10 = OpTypeStruct %3\n%11 = OpTypePointer StorageBuffer %10\n%9 = OpVariable  %11  StorageBuffer\n%13 = OpTypeStruct %4\n%14 = OpTypePointer StorageBuffer %13\n%12 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %7\n%17 = OpTypePointer StorageBuffer %16\n%15 = OpVariable  %17  StorageBuffer\n%19 = OpTypeStruct %3\n%20 = OpTypePointer Uniform %19\n%18 = OpVariable  %20  Uniform\n%23 = OpTypePointer Input %8\n%22 = OpVariable  %23  Input\n%26 = OpTypeFunction %2\n%27 = OpTypePointer StorageBuffer %3\n%28 = OpConstant  %6  0\n%30 = OpTypePointer StorageBuffer %4\n%32 = OpTypePointer StorageBuffer %7\n%34 = OpTypePointer Uniform %3\n%36 = OpConstant  %3  1\n%41 = OpTypeInt 32 1\n%40 = OpConstant  %41  1\n%45 = OpConstant  %6  1\n%53 = OpConstant  %6  264\n%25 = OpFunction  %2  None %26\n%21 = OpLabel\n%24 = OpLoad  %8  %22\n%29 = OpAccessChain  %27  %9 %28\n%31 = OpAccessChain  %30  %12 %28\n%33 = OpAccessChain  %32  %15 %28\n%35 = OpAccessChain  %34  %18 %28\nOpBranch %37\n%37 = OpLabel\n%38 = OpLoad  %3  %35\n%39 = OpAtomicUMax  %3  %29 %40 %28 %38\n%42 = OpLoad  %3  %35\n%43 = OpIAdd  %3  %36 %42\n%46 = OpAccessChain  %27  %31 %45\n%44 = OpAtomicUMax  %3  %46 %40 %28 %43\n%48 = OpAccessChain  %27  %33 %28\n%47 = OpAtomicUMax  %3  %48 %40 %28 %36\n%49 = OpCompositeExtract  %6  %24 0\n%50 = OpUConvert  %3  %49\n%52 = OpAccessChain  %27  %33 %45 %45\n%51 = OpAtomicUMax  %3  %52 %40 %28 %50\nOpControlBarrier %5 %5 %53\n%54 = OpLoad  %3  %35\n%55 = OpAtomicUMin  %3  %29 %40 %28 %54\n%56 = OpLoad  %3  %35\n%57 = OpIAdd  %3  %36 %56\n%59 = OpAccessChain  %27  %31 %45\n%58 = OpAtomicUMin  %3  %59 %40 %28 %57\n%61 = OpAccessChain  %27  %33 %28\n%60 = OpAtomicUMin  %3  %61 %40 %28 %36\n%62 = OpCompositeExtract  %6  %24 0\n%63 = OpUConvert  %3  %62\n%65 = OpAccessChain  %27  %33 %45 %45\n%64 = OpAtomicUMin  %3  %65 %40 %28 %63\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicOps-int64.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 225\nOpCapability Shader\nOpCapability Int64\nOpCapability Int64Atomics\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %32 \"cs_main\" %29 %49\nOpExecutionMode %32 LocalSize 2 1 1\nOpDecorate %5 ArrayStride 8\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 8\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 8\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 8\nOpDecorate %13 DescriptorSet 0\nOpDecorate %13 Binding 0\nOpDecorate %14 Block\nOpMemberDecorate %14 0 Offset 0\nOpDecorate %16 DescriptorSet 0\nOpDecorate %16 Binding 1\nOpDecorate %17 Block\nOpMemberDecorate %17 0 Offset 0\nOpDecorate %19 DescriptorSet 0\nOpDecorate %19 Binding 2\nOpDecorate %20 Block\nOpMemberDecorate %20 0 Offset 0\nOpDecorate %29 BuiltIn LocalInvocationId\nOpDecorate %49 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 64 0\n%4 = OpTypeInt 64 1\n%7 = OpTypeInt 32 0\n%6 = OpConstant  %7  2\n%5 = OpTypeArray %4 %6\n%8 = OpTypeStruct %3 %5\n%9 = OpTypeVector %7 3\n%10 = OpTypeBool\n%11 = OpTypeStruct %3 %10\n%12 = OpTypeStruct %4 %10\n%14 = OpTypeStruct %3\n%15 = OpTypePointer StorageBuffer %14\n%13 = OpVariable  %15  StorageBuffer\n%17 = OpTypeStruct %5\n%18 = OpTypePointer StorageBuffer %17\n%16 = OpVariable  %18  StorageBuffer\n%20 = OpTypeStruct %8\n%21 = OpTypePointer StorageBuffer %20\n%19 = OpVariable  %21  StorageBuffer\n%23 = OpTypePointer Workgroup %3\n%22 = OpVariable  %23  Workgroup\n%25 = OpTypePointer Workgroup %5\n%24 = OpVariable  %25  Workgroup\n%27 = OpTypePointer Workgroup %8\n%26 = OpVariable  %27  Workgroup\n%30 = OpTypePointer Input %9\n%29 = OpVariable  %30  Input\n%33 = OpTypeFunction %2\n%34 = OpTypePointer StorageBuffer %3\n%35 = OpConstant  %7  0\n%37 = OpTypePointer StorageBuffer %5\n%39 = OpTypePointer StorageBuffer %8\n%41 = OpConstant  %3  1\n%42 = OpConstant  %4  1\n%43 = OpConstant  %3  2\n%44 = OpConstant  %4  2\n%46 = OpConstantNull  %3\n%47 = OpConstantNull  %5\n%48 = OpConstantNull  %8\n%50 = OpTypePointer Input %7\n%49 = OpVariable  %50  Input\n%55 = OpConstant  %7  264\n%58 = OpTypeInt 32 1\n%57 = OpConstant  %58  1\n%59 = OpTypePointer StorageBuffer %4\n%60 = OpConstant  %7  1\n%64 = OpConstant  %58  2\n%65 = OpTypePointer Workgroup %4\n%32 = OpFunction  %2  None %33\n%28 = OpLabel\n%31 = OpLoad  %9  %29\n%36 = OpAccessChain  %34  %13 %35\n%38 = OpAccessChain  %37  %16 %35\n%40 = OpAccessChain  %39  %19 %35\nOpBranch %45\n%45 = OpLabel\n%51 = OpLoad  %7  %49\n%52 = OpIEqual  %10  %51 %35\nOpSelectionMerge %53 None\nOpBranchConditional %52 %54 %53\n%54 = OpLabel\nOpStore %22 %46\nOpStore %24 %47\nOpStore %26 %48\nOpBranch %53\n%53 = OpLabel\nOpControlBarrier %6 %6 %55\nOpBranch %56\n%56 = OpLabel\nOpAtomicStore %36 %57 %35 %41\n%61 = OpAccessChain  %59  %38 %60\nOpAtomicStore %61 %57 %35 %42\n%62 = OpAccessChain  %34  %40 %35\nOpAtomicStore %62 %57 %35 %41\n%63 = OpAccessChain  %59  %40 %60 %60\nOpAtomicStore %63 %57 %35 %42\nOpAtomicStore %22 %64 %35 %41\n%66 = OpAccessChain  %65  %24 %60\nOpAtomicStore %66 %64 %35 %42\n%67 = OpAccessChain  %23  %26 %35\nOpAtomicStore %67 %64 %35 %41\n%68 = OpAccessChain  %65  %26 %60 %60\nOpAtomicStore %68 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%69 = OpAtomicLoad  %3  %36 %57 %35\n%70 = OpAccessChain  %59  %38 %60\n%71 = OpAtomicLoad  %4  %70 %57 %35\n%72 = OpAccessChain  %34  %40 %35\n%73 = OpAtomicLoad  %3  %72 %57 %35\n%74 = OpAccessChain  %59  %40 %60 %60\n%75 = OpAtomicLoad  %4  %74 %57 %35\n%76 = OpAtomicLoad  %3  %22 %64 %35\n%77 = OpAccessChain  %65  %24 %60\n%78 = OpAtomicLoad  %4  %77 %64 %35\n%79 = OpAccessChain  %23  %26 %35\n%80 = OpAtomicLoad  %3  %79 %64 %35\n%81 = OpAccessChain  %65  %26 %60 %60\n%82 = OpAtomicLoad  %4  %81 %64 %35\nOpControlBarrier %6 %6 %55\n%83 = OpAtomicIAdd  %3  %36 %57 %35 %41\n%85 = OpAccessChain  %59  %38 %60\n%84 = OpAtomicIAdd  %4  %85 %57 %35 %42\n%87 = OpAccessChain  %34  %40 %35\n%86 = OpAtomicIAdd  %3  %87 %57 %35 %41\n%89 = OpAccessChain  %59  %40 %60 %60\n%88 = OpAtomicIAdd  %4  %89 %57 %35 %42\n%90 = OpAtomicIAdd  %3  %22 %64 %35 %41\n%92 = OpAccessChain  %65  %24 %60\n%91 = OpAtomicIAdd  %4  %92 %64 %35 %42\n%94 = OpAccessChain  %23  %26 %35\n%93 = OpAtomicIAdd  %3  %94 %64 %35 %41\n%96 = OpAccessChain  %65  %26 %60 %60\n%95 = OpAtomicIAdd  %4  %96 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%97 = OpAtomicISub  %3  %36 %57 %35 %41\n%99 = OpAccessChain  %59  %38 %60\n%98 = OpAtomicISub  %4  %99 %57 %35 %42\n%101 = OpAccessChain  %34  %40 %35\n%100 = OpAtomicISub  %3  %101 %57 %35 %41\n%103 = OpAccessChain  %59  %40 %60 %60\n%102 = OpAtomicISub  %4  %103 %57 %35 %42\n%104 = OpAtomicISub  %3  %22 %64 %35 %41\n%106 = OpAccessChain  %65  %24 %60\n%105 = OpAtomicISub  %4  %106 %64 %35 %42\n%108 = OpAccessChain  %23  %26 %35\n%107 = OpAtomicISub  %3  %108 %64 %35 %41\n%110 = OpAccessChain  %65  %26 %60 %60\n%109 = OpAtomicISub  %4  %110 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%111 = OpAtomicUMax  %3  %36 %57 %35 %41\n%113 = OpAccessChain  %59  %38 %60\n%112 = OpAtomicSMax  %4  %113 %57 %35 %42\n%115 = OpAccessChain  %34  %40 %35\n%114 = OpAtomicUMax  %3  %115 %57 %35 %41\n%117 = OpAccessChain  %59  %40 %60 %60\n%116 = OpAtomicSMax  %4  %117 %57 %35 %42\n%118 = OpAtomicUMax  %3  %22 %64 %35 %41\n%120 = OpAccessChain  %65  %24 %60\n%119 = OpAtomicSMax  %4  %120 %64 %35 %42\n%122 = OpAccessChain  %23  %26 %35\n%121 = OpAtomicUMax  %3  %122 %64 %35 %41\n%124 = OpAccessChain  %65  %26 %60 %60\n%123 = OpAtomicSMax  %4  %124 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%125 = OpAtomicUMin  %3  %36 %57 %35 %41\n%127 = OpAccessChain  %59  %38 %60\n%126 = OpAtomicSMin  %4  %127 %57 %35 %42\n%129 = OpAccessChain  %34  %40 %35\n%128 = OpAtomicUMin  %3  %129 %57 %35 %41\n%131 = OpAccessChain  %59  %40 %60 %60\n%130 = OpAtomicSMin  %4  %131 %57 %35 %42\n%132 = OpAtomicUMin  %3  %22 %64 %35 %41\n%134 = OpAccessChain  %65  %24 %60\n%133 = OpAtomicSMin  %4  %134 %64 %35 %42\n%136 = OpAccessChain  %23  %26 %35\n%135 = OpAtomicUMin  %3  %136 %64 %35 %41\n%138 = OpAccessChain  %65  %26 %60 %60\n%137 = OpAtomicSMin  %4  %138 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%139 = OpAtomicAnd  %3  %36 %57 %35 %41\n%141 = OpAccessChain  %59  %38 %60\n%140 = OpAtomicAnd  %4  %141 %57 %35 %42\n%143 = OpAccessChain  %34  %40 %35\n%142 = OpAtomicAnd  %3  %143 %57 %35 %41\n%145 = OpAccessChain  %59  %40 %60 %60\n%144 = OpAtomicAnd  %4  %145 %57 %35 %42\n%146 = OpAtomicAnd  %3  %22 %64 %35 %41\n%148 = OpAccessChain  %65  %24 %60\n%147 = OpAtomicAnd  %4  %148 %64 %35 %42\n%150 = OpAccessChain  %23  %26 %35\n%149 = OpAtomicAnd  %3  %150 %64 %35 %41\n%152 = OpAccessChain  %65  %26 %60 %60\n%151 = OpAtomicAnd  %4  %152 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%153 = OpAtomicOr  %3  %36 %57 %35 %41\n%155 = OpAccessChain  %59  %38 %60\n%154 = OpAtomicOr  %4  %155 %57 %35 %42\n%157 = OpAccessChain  %34  %40 %35\n%156 = OpAtomicOr  %3  %157 %57 %35 %41\n%159 = OpAccessChain  %59  %40 %60 %60\n%158 = OpAtomicOr  %4  %159 %57 %35 %42\n%160 = OpAtomicOr  %3  %22 %64 %35 %41\n%162 = OpAccessChain  %65  %24 %60\n%161 = OpAtomicOr  %4  %162 %64 %35 %42\n%164 = OpAccessChain  %23  %26 %35\n%163 = OpAtomicOr  %3  %164 %64 %35 %41\n%166 = OpAccessChain  %65  %26 %60 %60\n%165 = OpAtomicOr  %4  %166 %64 %35 %42\nOpControlBarrier %6 %6 %55\n%167 = OpAtomicXor  %3  %36 %57 %35 %41\n%169 = OpAccessChain  %59  %38 %60\n%168 = OpAtomicXor  %4  %169 %57 %35 %42\n%171 = OpAccessChain  %34  %40 %35\n%170 = OpAtomicXor  %3  %171 %57 %35 %41\n%173 = OpAccessChain  %59  %40 %60 %60\n%172 = OpAtomicXor  %4  %173 %57 %35 %42\n%174 = OpAtomicXor  %3  %22 %64 %35 %41\n%176 = OpAccessChain  %65  %24 %60\n%175 = OpAtomicXor  %4  %176 %64 %35 %42\n%178 = OpAccessChain  %23  %26 %35\n%177 = OpAtomicXor  %3  %178 %64 %35 %41\n%180 = OpAccessChain  %65  %26 %60 %60\n%179 = OpAtomicXor  %4  %180 %64 %35 %42\n%181 = OpAtomicExchange  %3  %36 %57 %35 %41\n%183 = OpAccessChain  %59  %38 %60\n%182 = OpAtomicExchange  %4  %183 %57 %35 %42\n%185 = OpAccessChain  %34  %40 %35\n%184 = OpAtomicExchange  %3  %185 %57 %35 %41\n%187 = OpAccessChain  %59  %40 %60 %60\n%186 = OpAtomicExchange  %4  %187 %57 %35 %42\n%188 = OpAtomicExchange  %3  %22 %64 %35 %41\n%190 = OpAccessChain  %65  %24 %60\n%189 = OpAtomicExchange  %4  %190 %64 %35 %42\n%192 = OpAccessChain  %23  %26 %35\n%191 = OpAtomicExchange  %3  %192 %64 %35 %41\n%194 = OpAccessChain  %65  %26 %60 %60\n%193 = OpAtomicExchange  %4  %194 %64 %35 %42\n%196 = OpAtomicCompareExchange  %3  %36 %57 %35 %35 %43 %41\n%197 = OpIEqual  %10  %196 %41\n%195 = OpCompositeConstruct  %11  %196 %197\n%199 = OpAccessChain  %59  %38 %60\n%200 = OpAtomicCompareExchange  %4  %199 %57 %35 %35 %44 %42\n%201 = OpIEqual  %10  %200 %42\n%198 = OpCompositeConstruct  %12  %200 %201\n%203 = OpAccessChain  %34  %40 %35\n%204 = OpAtomicCompareExchange  %3  %203 %57 %35 %35 %43 %41\n%205 = OpIEqual  %10  %204 %41\n%202 = OpCompositeConstruct  %11  %204 %205\n%207 = OpAccessChain  %59  %40 %60 %60\n%208 = OpAtomicCompareExchange  %4  %207 %57 %35 %35 %44 %42\n%209 = OpIEqual  %10  %208 %42\n%206 = OpCompositeConstruct  %12  %208 %209\n%211 = OpAtomicCompareExchange  %3  %22 %64 %35 %35 %43 %41\n%212 = OpIEqual  %10  %211 %41\n%210 = OpCompositeConstruct  %11  %211 %212\n%214 = OpAccessChain  %65  %24 %60\n%215 = OpAtomicCompareExchange  %4  %214 %64 %35 %35 %44 %42\n%216 = OpIEqual  %10  %215 %42\n%213 = OpCompositeConstruct  %12  %215 %216\n%218 = OpAccessChain  %23  %26 %35\n%219 = OpAtomicCompareExchange  %3  %218 %64 %35 %35 %43 %41\n%220 = OpIEqual  %10  %219 %41\n%217 = OpCompositeConstruct  %11  %219 %220\n%222 = OpAccessChain  %65  %26 %60 %60\n%223 = OpAtomicCompareExchange  %4  %222 %64 %35 %35 %44 %42\n%224 = OpIEqual  %10  %223 %42\n%221 = OpCompositeConstruct  %12  %223 %224\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicOps.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 219\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %31 \"cs_main\" %28 %47\nOpExecutionMode %31 LocalSize 2 1 1\nOpDecorate %5 ArrayStride 4\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 4\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 4\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\nOpDecorate %13 Block\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 1\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %18 DescriptorSet 0\nOpDecorate %18 Binding 2\nOpDecorate %19 Block\nOpMemberDecorate %19 0 Offset 0\nOpDecorate %28 BuiltIn LocalInvocationId\nOpDecorate %47 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 32 1\n%6 = OpConstant  %3  2\n%5 = OpTypeArray %4 %6\n%7 = OpTypeStruct %3 %5\n%8 = OpTypeVector %3 3\n%9 = OpTypeBool\n%10 = OpTypeStruct %3 %9\n%11 = OpTypeStruct %4 %9\n%13 = OpTypeStruct %3\n%14 = OpTypePointer StorageBuffer %13\n%12 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %5\n%17 = OpTypePointer StorageBuffer %16\n%15 = OpVariable  %17  StorageBuffer\n%19 = OpTypeStruct %7\n%20 = OpTypePointer StorageBuffer %19\n%18 = OpVariable  %20  StorageBuffer\n%22 = OpTypePointer Workgroup %3\n%21 = OpVariable  %22  Workgroup\n%24 = OpTypePointer Workgroup %5\n%23 = OpVariable  %24  Workgroup\n%26 = OpTypePointer Workgroup %7\n%25 = OpVariable  %26  Workgroup\n%29 = OpTypePointer Input %8\n%28 = OpVariable  %29  Input\n%32 = OpTypeFunction %2\n%33 = OpTypePointer StorageBuffer %3\n%34 = OpConstant  %3  0\n%36 = OpTypePointer StorageBuffer %5\n%38 = OpTypePointer StorageBuffer %7\n%40 = OpConstant  %3  1\n%41 = OpConstant  %4  1\n%42 = OpConstant  %4  2\n%44 = OpConstantNull  %3\n%45 = OpConstantNull  %5\n%46 = OpConstantNull  %7\n%48 = OpTypePointer Input %3\n%47 = OpVariable  %48  Input\n%53 = OpConstant  %3  264\n%55 = OpTypePointer StorageBuffer %4\n%59 = OpTypePointer Workgroup %4\n%31 = OpFunction  %2  None %32\n%27 = OpLabel\n%30 = OpLoad  %8  %28\n%35 = OpAccessChain  %33  %12 %34\n%37 = OpAccessChain  %36  %15 %34\n%39 = OpAccessChain  %38  %18 %34\nOpBranch %43\n%43 = OpLabel\n%49 = OpLoad  %3  %47\n%50 = OpIEqual  %9  %49 %34\nOpSelectionMerge %51 None\nOpBranchConditional %50 %52 %51\n%52 = OpLabel\nOpStore %21 %44\nOpStore %23 %45\nOpStore %25 %46\nOpBranch %51\n%51 = OpLabel\nOpControlBarrier %6 %6 %53\nOpBranch %54\n%54 = OpLabel\nOpAtomicStore %35 %41 %34 %40\n%56 = OpAccessChain  %55  %37 %40\nOpAtomicStore %56 %41 %34 %41\n%57 = OpAccessChain  %33  %39 %34\nOpAtomicStore %57 %41 %34 %40\n%58 = OpAccessChain  %55  %39 %40 %40\nOpAtomicStore %58 %41 %34 %41\nOpAtomicStore %21 %42 %34 %40\n%60 = OpAccessChain  %59  %23 %40\nOpAtomicStore %60 %42 %34 %41\n%61 = OpAccessChain  %22  %25 %34\nOpAtomicStore %61 %42 %34 %40\n%62 = OpAccessChain  %59  %25 %40 %40\nOpAtomicStore %62 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%63 = OpAtomicLoad  %3  %35 %41 %34\n%64 = OpAccessChain  %55  %37 %40\n%65 = OpAtomicLoad  %4  %64 %41 %34\n%66 = OpAccessChain  %33  %39 %34\n%67 = OpAtomicLoad  %3  %66 %41 %34\n%68 = OpAccessChain  %55  %39 %40 %40\n%69 = OpAtomicLoad  %4  %68 %41 %34\n%70 = OpAtomicLoad  %3  %21 %42 %34\n%71 = OpAccessChain  %59  %23 %40\n%72 = OpAtomicLoad  %4  %71 %42 %34\n%73 = OpAccessChain  %22  %25 %34\n%74 = OpAtomicLoad  %3  %73 %42 %34\n%75 = OpAccessChain  %59  %25 %40 %40\n%76 = OpAtomicLoad  %4  %75 %42 %34\nOpControlBarrier %6 %6 %53\n%77 = OpAtomicIAdd  %3  %35 %41 %34 %40\n%79 = OpAccessChain  %55  %37 %40\n%78 = OpAtomicIAdd  %4  %79 %41 %34 %41\n%81 = OpAccessChain  %33  %39 %34\n%80 = OpAtomicIAdd  %3  %81 %41 %34 %40\n%83 = OpAccessChain  %55  %39 %40 %40\n%82 = OpAtomicIAdd  %4  %83 %41 %34 %41\n%84 = OpAtomicIAdd  %3  %21 %42 %34 %40\n%86 = OpAccessChain  %59  %23 %40\n%85 = OpAtomicIAdd  %4  %86 %42 %34 %41\n%88 = OpAccessChain  %22  %25 %34\n%87 = OpAtomicIAdd  %3  %88 %42 %34 %40\n%90 = OpAccessChain  %59  %25 %40 %40\n%89 = OpAtomicIAdd  %4  %90 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%91 = OpAtomicISub  %3  %35 %41 %34 %40\n%93 = OpAccessChain  %55  %37 %40\n%92 = OpAtomicISub  %4  %93 %41 %34 %41\n%95 = OpAccessChain  %33  %39 %34\n%94 = OpAtomicISub  %3  %95 %41 %34 %40\n%97 = OpAccessChain  %55  %39 %40 %40\n%96 = OpAtomicISub  %4  %97 %41 %34 %41\n%98 = OpAtomicISub  %3  %21 %42 %34 %40\n%100 = OpAccessChain  %59  %23 %40\n%99 = OpAtomicISub  %4  %100 %42 %34 %41\n%102 = OpAccessChain  %22  %25 %34\n%101 = OpAtomicISub  %3  %102 %42 %34 %40\n%104 = OpAccessChain  %59  %25 %40 %40\n%103 = OpAtomicISub  %4  %104 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%105 = OpAtomicUMax  %3  %35 %41 %34 %40\n%107 = OpAccessChain  %55  %37 %40\n%106 = OpAtomicSMax  %4  %107 %41 %34 %41\n%109 = OpAccessChain  %33  %39 %34\n%108 = OpAtomicUMax  %3  %109 %41 %34 %40\n%111 = OpAccessChain  %55  %39 %40 %40\n%110 = OpAtomicSMax  %4  %111 %41 %34 %41\n%112 = OpAtomicUMax  %3  %21 %42 %34 %40\n%114 = OpAccessChain  %59  %23 %40\n%113 = OpAtomicSMax  %4  %114 %42 %34 %41\n%116 = OpAccessChain  %22  %25 %34\n%115 = OpAtomicUMax  %3  %116 %42 %34 %40\n%118 = OpAccessChain  %59  %25 %40 %40\n%117 = OpAtomicSMax  %4  %118 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%119 = OpAtomicUMin  %3  %35 %41 %34 %40\n%121 = OpAccessChain  %55  %37 %40\n%120 = OpAtomicSMin  %4  %121 %41 %34 %41\n%123 = OpAccessChain  %33  %39 %34\n%122 = OpAtomicUMin  %3  %123 %41 %34 %40\n%125 = OpAccessChain  %55  %39 %40 %40\n%124 = OpAtomicSMin  %4  %125 %41 %34 %41\n%126 = OpAtomicUMin  %3  %21 %42 %34 %40\n%128 = OpAccessChain  %59  %23 %40\n%127 = OpAtomicSMin  %4  %128 %42 %34 %41\n%130 = OpAccessChain  %22  %25 %34\n%129 = OpAtomicUMin  %3  %130 %42 %34 %40\n%132 = OpAccessChain  %59  %25 %40 %40\n%131 = OpAtomicSMin  %4  %132 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%133 = OpAtomicAnd  %3  %35 %41 %34 %40\n%135 = OpAccessChain  %55  %37 %40\n%134 = OpAtomicAnd  %4  %135 %41 %34 %41\n%137 = OpAccessChain  %33  %39 %34\n%136 = OpAtomicAnd  %3  %137 %41 %34 %40\n%139 = OpAccessChain  %55  %39 %40 %40\n%138 = OpAtomicAnd  %4  %139 %41 %34 %41\n%140 = OpAtomicAnd  %3  %21 %42 %34 %40\n%142 = OpAccessChain  %59  %23 %40\n%141 = OpAtomicAnd  %4  %142 %42 %34 %41\n%144 = OpAccessChain  %22  %25 %34\n%143 = OpAtomicAnd  %3  %144 %42 %34 %40\n%146 = OpAccessChain  %59  %25 %40 %40\n%145 = OpAtomicAnd  %4  %146 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%147 = OpAtomicOr  %3  %35 %41 %34 %40\n%149 = OpAccessChain  %55  %37 %40\n%148 = OpAtomicOr  %4  %149 %41 %34 %41\n%151 = OpAccessChain  %33  %39 %34\n%150 = OpAtomicOr  %3  %151 %41 %34 %40\n%153 = OpAccessChain  %55  %39 %40 %40\n%152 = OpAtomicOr  %4  %153 %41 %34 %41\n%154 = OpAtomicOr  %3  %21 %42 %34 %40\n%156 = OpAccessChain  %59  %23 %40\n%155 = OpAtomicOr  %4  %156 %42 %34 %41\n%158 = OpAccessChain  %22  %25 %34\n%157 = OpAtomicOr  %3  %158 %42 %34 %40\n%160 = OpAccessChain  %59  %25 %40 %40\n%159 = OpAtomicOr  %4  %160 %42 %34 %41\nOpControlBarrier %6 %6 %53\n%161 = OpAtomicXor  %3  %35 %41 %34 %40\n%163 = OpAccessChain  %55  %37 %40\n%162 = OpAtomicXor  %4  %163 %41 %34 %41\n%165 = OpAccessChain  %33  %39 %34\n%164 = OpAtomicXor  %3  %165 %41 %34 %40\n%167 = OpAccessChain  %55  %39 %40 %40\n%166 = OpAtomicXor  %4  %167 %41 %34 %41\n%168 = OpAtomicXor  %3  %21 %42 %34 %40\n%170 = OpAccessChain  %59  %23 %40\n%169 = OpAtomicXor  %4  %170 %42 %34 %41\n%172 = OpAccessChain  %22  %25 %34\n%171 = OpAtomicXor  %3  %172 %42 %34 %40\n%174 = OpAccessChain  %59  %25 %40 %40\n%173 = OpAtomicXor  %4  %174 %42 %34 %41\n%175 = OpAtomicExchange  %3  %35 %41 %34 %40\n%177 = OpAccessChain  %55  %37 %40\n%176 = OpAtomicExchange  %4  %177 %41 %34 %41\n%179 = OpAccessChain  %33  %39 %34\n%178 = OpAtomicExchange  %3  %179 %41 %34 %40\n%181 = OpAccessChain  %55  %39 %40 %40\n%180 = OpAtomicExchange  %4  %181 %41 %34 %41\n%182 = OpAtomicExchange  %3  %21 %42 %34 %40\n%184 = OpAccessChain  %59  %23 %40\n%183 = OpAtomicExchange  %4  %184 %42 %34 %41\n%186 = OpAccessChain  %22  %25 %34\n%185 = OpAtomicExchange  %3  %186 %42 %34 %40\n%188 = OpAccessChain  %59  %25 %40 %40\n%187 = OpAtomicExchange  %4  %188 %42 %34 %41\n%190 = OpAtomicCompareExchange  %3  %35 %41 %34 %34 %6 %40\n%191 = OpIEqual  %9  %190 %40\n%189 = OpCompositeConstruct  %10  %190 %191\n%193 = OpAccessChain  %55  %37 %40\n%194 = OpAtomicCompareExchange  %4  %193 %41 %34 %34 %42 %41\n%195 = OpIEqual  %9  %194 %41\n%192 = OpCompositeConstruct  %11  %194 %195\n%197 = OpAccessChain  %33  %39 %34\n%198 = OpAtomicCompareExchange  %3  %197 %41 %34 %34 %6 %40\n%199 = OpIEqual  %9  %198 %40\n%196 = OpCompositeConstruct  %10  %198 %199\n%201 = OpAccessChain  %55  %39 %40 %40\n%202 = OpAtomicCompareExchange  %4  %201 %41 %34 %34 %42 %41\n%203 = OpIEqual  %9  %202 %41\n%200 = OpCompositeConstruct  %11  %202 %203\n%205 = OpAtomicCompareExchange  %3  %21 %42 %34 %34 %6 %40\n%206 = OpIEqual  %9  %205 %40\n%204 = OpCompositeConstruct  %10  %205 %206\n%208 = OpAccessChain  %59  %23 %40\n%209 = OpAtomicCompareExchange  %4  %208 %42 %34 %34 %42 %41\n%210 = OpIEqual  %9  %209 %41\n%207 = OpCompositeConstruct  %11  %209 %210\n%212 = OpAccessChain  %22  %25 %34\n%213 = OpAtomicCompareExchange  %3  %212 %42 %34 %34 %6 %40\n%214 = OpIEqual  %9  %213 %40\n%211 = OpCompositeConstruct  %10  %213 %214\n%216 = OpAccessChain  %59  %25 %40 %40\n%217 = OpAtomicCompareExchange  %4  %216 %42 %34 %34 %42 %41\n%218 = OpIEqual  %9  %217 %41\n%215 = OpCompositeConstruct  %11  %217 %218\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicTexture-int64.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 31\nOpCapability Shader\nOpCapability Int64ImageEXT\nOpCapability Int64\nOpCapability Int64Atomics\nOpExtension \"SPV_EXT_shader_image_int64\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %15 \"cs_main\" %12\nOpExecutionMode %15 LocalSize 2 1 1\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 0\nOpDecorate %12 BuiltIn LocalInvocationId\n%2 = OpTypeVoid\n%4 = OpTypeInt 64 0\n%3 = OpTypeImage %4 2D 0 0 0 2 R64ui\n%6 = OpTypeInt 32 0\n%5 = OpTypeVector %6 3\n%8 = OpTypeInt 32 1\n%7 = OpTypeVector %8 2\n%10 = OpTypePointer UniformConstant %3\n%9 = OpVariable  %10  UniformConstant\n%13 = OpTypePointer Input %5\n%12 = OpVariable  %13  Input\n%16 = OpTypeFunction %2\n%18 = OpConstant  %8  0\n%19 = OpConstantComposite  %7  %18 %18\n%20 = OpConstant  %4  1\n%22 = OpTypePointer Image %4\n%24 = OpConstant  %6  0\n%26 = OpConstant  %8  1\n%27 = OpConstant  %6  2\n%28 = OpConstant  %6  264\n%15 = OpFunction  %2  None %16\n%11 = OpLabel\n%14 = OpLoad  %5  %12\n%17 = OpLoad  %3  %9\nOpBranch %21\n%21 = OpLabel\n%23 = OpImageTexelPointer  %22  %9 %19 %24\n%25 = OpAtomicUMax  %4  %23 %26 %24 %20\nOpControlBarrier %27 %27 %28\n%29 = OpImageTexelPointer  %22  %9 %19 %24\n%30 = OpAtomicUMin  %4  %29 %26 %24 %20\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-atomicTexture.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 53\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %17 \"cs_main\" %14\nOpExecutionMode %17 LocalSize 2 1 1\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 0\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 1\nOpDecorate %14 BuiltIn LocalInvocationId\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%3 = OpTypeImage %4 2D 0 0 0 2 R32ui\n%6 = OpTypeInt 32 1\n%5 = OpTypeImage %6 2D 0 0 0 2 R32i\n%7 = OpTypeVector %4 3\n%8 = OpTypeVector %6 2\n%10 = OpTypePointer UniformConstant %3\n%9 = OpVariable  %10  UniformConstant\n%12 = OpTypePointer UniformConstant %5\n%11 = OpVariable  %12  UniformConstant\n%15 = OpTypePointer Input %7\n%14 = OpVariable  %15  Input\n%18 = OpTypeFunction %2\n%21 = OpConstant  %6  0\n%22 = OpConstantComposite  %8  %21 %21\n%23 = OpConstant  %4  1\n%24 = OpConstant  %6  1\n%26 = OpTypePointer Image %4\n%28 = OpConstant  %4  0\n%40 = OpTypePointer Image %6\n%17 = OpFunction  %2  None %18\n%13 = OpLabel\n%16 = OpLoad  %7  %14\n%19 = OpLoad  %3  %9\n%20 = OpLoad  %5  %11\nOpBranch %25\n%25 = OpLabel\n%27 = OpImageTexelPointer  %26  %9 %22 %28\n%29 = OpAtomicUMax  %4  %27 %24 %28 %23\n%30 = OpImageTexelPointer  %26  %9 %22 %28\n%31 = OpAtomicUMin  %4  %30 %24 %28 %23\n%32 = OpImageTexelPointer  %26  %9 %22 %28\n%33 = OpAtomicIAdd  %4  %32 %24 %28 %23\n%34 = OpImageTexelPointer  %26  %9 %22 %28\n%35 = OpAtomicAnd  %4  %34 %24 %28 %23\n%36 = OpImageTexelPointer  %26  %9 %22 %28\n%37 = OpAtomicOr  %4  %36 %24 %28 %23\n%38 = OpImageTexelPointer  %26  %9 %22 %28\n%39 = OpAtomicXor  %4  %38 %24 %28 %23\n%41 = OpImageTexelPointer  %40  %11 %22 %28\n%42 = OpAtomicSMax  %6  %41 %24 %28 %24\n%43 = OpImageTexelPointer  %40  %11 %22 %28\n%44 = OpAtomicSMin  %6  %43 %24 %28 %24\n%45 = OpImageTexelPointer  %40  %11 %22 %28\n%46 = OpAtomicIAdd  %6  %45 %24 %28 %24\n%47 = OpImageTexelPointer  %40  %11 %22 %28\n%48 = OpAtomicAnd  %6  %47 %24 %28 %24\n%49 = OpImageTexelPointer  %40  %11 %22 %28\n%50 = OpAtomicOr  %6  %49 %24 %28 %24\n%51 = OpImageTexelPointer  %40  %11 %22 %28\n%52 = OpAtomicXor  %6  %51 %24 %28 %24\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-barycentrics.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 24\nOpCapability Shader\nOpCapability FragmentBarycentricKHR\nOpExtension \"SPV_KHR_fragment_shader_barycentric\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %12 \"fs_main\" %7 %10\nOpEntryPoint Fragment %21 \"fs_main_no_perspective\" %18 %20\nOpExecutionMode %12 OriginUpperLeft\nOpExecutionMode %21 OriginUpperLeft\nOpDecorate %7 BuiltIn BaryCoordKHR\nOpDecorate %10 Location 0\nOpDecorate %18 BuiltIn BaryCoordNoPerspKHR\nOpDecorate %20 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 3\n%5 = OpTypeVector %4 4\n%8 = OpTypePointer Input %3\n%7 = OpVariable  %8  Input\n%11 = OpTypePointer Output %5\n%10 = OpVariable  %11  Output\n%13 = OpTypeFunction %2\n%14 = OpConstant  %4  1\n%18 = OpVariable  %8  Input\n%20 = OpVariable  %11  Output\n%12 = OpFunction  %2  None %13\n%6 = OpLabel\n%9 = OpLoad  %3  %7\nOpBranch %15\n%15 = OpLabel\n%16 = OpCompositeConstruct  %5  %9 %14\nOpStore %10 %16\nOpReturn\nOpFunctionEnd\n%21 = OpFunction  %2  None %13\n%17 = OpLabel\n%19 = OpLoad  %3  %18\nOpBranch %22\n%22 = OpLabel\n%23 = OpCompositeConstruct  %5  %19 %14\nOpStore %20 %23\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-binding-arrays.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 412\nOpCapability Shader\nOpCapability ImageQuery\nOpCapability ShaderNonUniform\nOpExtension \"SPV_EXT_descriptor_indexing\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %52 \"main\" %47 %50\nOpExecutionMode %52 OriginUpperLeft\nOpMemberDecorate %4 0 Offset 0\nOpMemberDecorate %21 0 Offset 0\nOpDecorate %24 DescriptorSet 0\nOpDecorate %24 Binding 0\nOpDecorate %28 DescriptorSet 0\nOpDecorate %28 Binding 1\nOpDecorate %30 DescriptorSet 0\nOpDecorate %30 Binding 2\nOpDecorate %32 DescriptorSet 0\nOpDecorate %32 Binding 3\nOpDecorate %34 DescriptorSet 0\nOpDecorate %34 Binding 4\nOpDecorate %36 DescriptorSet 0\nOpDecorate %36 Binding 5\nOpDecorate %38 DescriptorSet 0\nOpDecorate %38 Binding 6\nOpDecorate %40 DescriptorSet 0\nOpDecorate %40 Binding 7\nOpDecorate %42 DescriptorSet 0\nOpDecorate %42 Binding 8\nOpDecorate %43 Block\nOpMemberDecorate %43 0 Offset 0\nOpDecorate %47 Location 0\nOpDecorate %47 Flat\nOpDecorate %50 Location 0\nOpDecorate %91 NonUniform\nOpDecorate %92 NonUniform\nOpDecorate %114 NonUniform\nOpDecorate %115 NonUniform\nOpDecorate %116 NonUniform\nOpDecorate %117 NonUniform\nOpDecorate %140 NonUniform\nOpDecorate %141 NonUniform\nOpDecorate %142 NonUniform\nOpDecorate %143 NonUniform\nOpDecorate %179 NonUniform\nOpDecorate %180 NonUniform\nOpDecorate %207 NonUniform\nOpDecorate %208 NonUniform\nOpDecorate %223 NonUniform\nOpDecorate %224 NonUniform\nOpDecorate %239 NonUniform\nOpDecorate %240 NonUniform\nOpDecorate %260 NonUniform\nOpDecorate %261 NonUniform\nOpDecorate %262 NonUniform\nOpDecorate %263 NonUniform\nOpDecorate %284 NonUniform\nOpDecorate %285 NonUniform\nOpDecorate %286 NonUniform\nOpDecorate %287 NonUniform\nOpDecorate %308 NonUniform\nOpDecorate %309 NonUniform\nOpDecorate %310 NonUniform\nOpDecorate %311 NonUniform\nOpDecorate %332 NonUniform\nOpDecorate %333 NonUniform\nOpDecorate %334 NonUniform\nOpDecorate %335 NonUniform\nOpDecorate %356 NonUniform\nOpDecorate %357 NonUniform\nOpDecorate %358 NonUniform\nOpDecorate %359 NonUniform\nOpDecorate %380 NonUniform\nOpDecorate %381 NonUniform\nOpDecorate %382 NonUniform\nOpDecorate %383 NonUniform\nOpDecorate %394 NonUniform\nOpDecorate %395 NonUniform\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%5 = OpTypeFloat 32\n%6 = OpTypeImage %5 2D 0 0 0 1 Unknown\n%7 = OpTypeRuntimeArray %6\n%9 = OpConstant  %3  5\n%8 = OpTypeArray %6 %9\n%10 = OpTypeImage %5 2D 0 1 0 1 Unknown\n%11 = OpTypeArray %10 %9\n%12 = OpTypeImage %5 2D 0 0 1 1 Unknown\n%13 = OpTypeArray %12 %9\n%14 = OpTypeImage %5 2D 1 0 0 1 Unknown\n%15 = OpTypeArray %14 %9\n%16 = OpTypeImage %5 2D 0 0 0 2 Rgba32f\n%17 = OpTypeArray %16 %9\n%18 = OpTypeSampler\n%19 = OpTypeArray %18 %9\n%20 = OpTypeArray %18 %9\n%21 = OpTypeStruct %3\n%22 = OpTypeVector %5 4\n%23 = OpTypeVector %3 2\n%26 = OpConstant  %3  10\n%25 = OpTypeArray %6 %26\n%27 = OpTypePointer UniformConstant %25\n%24 = OpVariable  %27  UniformConstant\n%29 = OpTypePointer UniformConstant %8\n%28 = OpVariable  %29  UniformConstant\n%31 = OpTypePointer UniformConstant %11\n%30 = OpVariable  %31  UniformConstant\n%33 = OpTypePointer UniformConstant %13\n%32 = OpVariable  %33  UniformConstant\n%35 = OpTypePointer UniformConstant %15\n%34 = OpVariable  %35  UniformConstant\n%37 = OpTypePointer UniformConstant %17\n%36 = OpVariable  %37  UniformConstant\n%39 = OpTypePointer UniformConstant %19\n%38 = OpVariable  %39  UniformConstant\n%41 = OpTypePointer UniformConstant %20\n%40 = OpVariable  %41  UniformConstant\n%43 = OpTypeStruct %4\n%44 = OpTypePointer Uniform %43\n%42 = OpVariable  %44  Uniform\n%48 = OpTypePointer Input %3\n%47 = OpVariable  %48  Input\n%51 = OpTypePointer Output %22\n%50 = OpVariable  %51  Output\n%53 = OpTypeFunction %2\n%54 = OpTypePointer Uniform %4\n%55 = OpConstant  %3  0\n%57 = OpConstantComposite  %23  %55 %55\n%58 = OpConstant  %5  0\n%59 = OpConstantComposite  %22  %58 %58 %58 %58\n%60 = OpTypeVector %5 2\n%61 = OpConstantComposite  %60  %58 %58\n%62 = OpTypeInt 32 1\n%63 = OpConstant  %62  0\n%64 = OpTypeVector %62 2\n%65 = OpConstantComposite  %64  %63 %63\n%67 = OpTypePointer Function %3\n%69 = OpTypePointer Function %23\n%71 = OpTypePointer Function %5\n%73 = OpTypePointer Function %22\n%75 = OpTypePointer Uniform %3\n%80 = OpTypePointer UniformConstant %6\n%98 = OpTypePointer UniformConstant %18\n%101 = OpTypeSampledImage %6\n%122 = OpTypePointer UniformConstant %14\n%127 = OpTypeSampledImage %14\n%150 = OpTypeBool\n%151 = OpConstantNull  %22\n%157 = OpTypeVector %150 2\n%193 = OpTypePointer UniformConstant %10\n%196 = OpTypeVector %3 3\n%228 = OpTypePointer UniformConstant %12\n%387 = OpTypePointer UniformConstant %16\n%52 = OpFunction  %2  None %53\n%45 = OpLabel\n%68 = OpVariable  %69  Function %57\n%72 = OpVariable  %73  Function %59\n%66 = OpVariable  %67  Function %55\n%70 = OpVariable  %71  Function %58\n%49 = OpLoad  %3  %47\n%46 = OpCompositeConstruct  %21  %49\n%56 = OpAccessChain  %54  %42 %55\nOpBranch %74\n%74 = OpLabel\n%76 = OpAccessChain  %75  %56 %55\n%77 = OpLoad  %3  %76\n%78 = OpCompositeExtract  %3  %46 0\n%79 = OpLoad  %23  %68\n%81 = OpAccessChain  %80  %24 %55\n%82 = OpLoad  %6  %81\n%83 = OpImageQuerySizeLod  %23  %82 %55\n%84 = OpIAdd  %23  %79 %83\nOpStore %68 %84\n%85 = OpLoad  %23  %68\n%86 = OpAccessChain  %80  %24 %77\n%87 = OpLoad  %6  %86\n%88 = OpImageQuerySizeLod  %23  %87 %55\n%89 = OpIAdd  %23  %85 %88\nOpStore %68 %89\n%90 = OpLoad  %23  %68\n%91 = OpAccessChain  %80  %24 %78\n%92 = OpLoad  %6  %91\n%93 = OpImageQuerySizeLod  %23  %92 %55\n%94 = OpIAdd  %23  %90 %93\nOpStore %68 %94\n%95 = OpLoad  %22  %72\n%96 = OpAccessChain  %80  %28 %55\n%97 = OpLoad  %6  %96\n%99 = OpAccessChain  %98  %38 %55\n%100 = OpLoad  %18  %99\n%102 = OpSampledImage  %101  %97 %100\n%103 = OpImageGather  %22  %102 %61 %55\n%104 = OpFAdd  %22  %95 %103\nOpStore %72 %104\n%105 = OpLoad  %22  %72\n%106 = OpAccessChain  %80  %28 %77\n%107 = OpLoad  %6  %106\n%108 = OpAccessChain  %98  %38 %77\n%109 = OpLoad  %18  %108\n%110 = OpSampledImage  %101  %107 %109\n%111 = OpImageGather  %22  %110 %61 %55\n%112 = OpFAdd  %22  %105 %111\nOpStore %72 %112\n%113 = OpLoad  %22  %72\n%114 = OpAccessChain  %80  %28 %78\n%115 = OpLoad  %6  %114\n%116 = OpAccessChain  %98  %38 %78\n%117 = OpLoad  %18  %116\n%118 = OpSampledImage  %101  %115 %117\n%119 = OpImageGather  %22  %118 %61 %55\n%120 = OpFAdd  %22  %113 %119\nOpStore %72 %120\n%121 = OpLoad  %22  %72\n%123 = OpAccessChain  %122  %34 %55\n%124 = OpLoad  %14  %123\n%125 = OpAccessChain  %98  %40 %55\n%126 = OpLoad  %18  %125\n%128 = OpSampledImage  %127  %124 %126\n%129 = OpImageDrefGather  %22  %128 %61 %58\n%130 = OpFAdd  %22  %121 %129\nOpStore %72 %130\n%131 = OpLoad  %22  %72\n%132 = OpAccessChain  %122  %34 %77\n%133 = OpLoad  %14  %132\n%134 = OpAccessChain  %98  %40 %77\n%135 = OpLoad  %18  %134\n%136 = OpSampledImage  %127  %133 %135\n%137 = OpImageDrefGather  %22  %136 %61 %58\n%138 = OpFAdd  %22  %131 %137\nOpStore %72 %138\n%139 = OpLoad  %22  %72\n%140 = OpAccessChain  %122  %34 %78\n%141 = OpLoad  %14  %140\n%142 = OpAccessChain  %98  %40 %78\n%143 = OpLoad  %18  %142\n%144 = OpSampledImage  %127  %141 %143\n%145 = OpImageDrefGather  %22  %144 %61 %58\n%146 = OpFAdd  %22  %139 %145\nOpStore %72 %146\n%147 = OpLoad  %22  %72\n%148 = OpAccessChain  %80  %24 %55\n%149 = OpLoad  %6  %148\n%152 = OpImageQueryLevels  %62  %149\n%153 = OpULessThan  %150  %63 %152\nOpSelectionMerge %154 None\nOpBranchConditional %153 %155 %154\n%155 = OpLabel\n%156 = OpImageQuerySizeLod  %64  %149 %63\n%158 = OpULessThan  %157  %65 %156\n%159 = OpAll  %150  %158\nOpBranchConditional %159 %160 %154\n%160 = OpLabel\n%161 = OpImageFetch  %22  %149 %65 Lod %63\nOpBranch %154\n%154 = OpLabel\n%162 = OpPhi  %22  %151 %74 %151 %155 %161 %160\n%163 = OpFAdd  %22  %147 %162\nOpStore %72 %163\n%164 = OpLoad  %22  %72\n%165 = OpAccessChain  %80  %24 %77\n%166 = OpLoad  %6  %165\n%167 = OpImageQueryLevels  %62  %166\n%168 = OpULessThan  %150  %63 %167\nOpSelectionMerge %169 None\nOpBranchConditional %168 %170 %169\n%170 = OpLabel\n%171 = OpImageQuerySizeLod  %64  %166 %63\n%172 = OpULessThan  %157  %65 %171\n%173 = OpAll  %150  %172\nOpBranchConditional %173 %174 %169\n%174 = OpLabel\n%175 = OpImageFetch  %22  %166 %65 Lod %63\nOpBranch %169\n%169 = OpLabel\n%176 = OpPhi  %22  %151 %154 %151 %170 %175 %174\n%177 = OpFAdd  %22  %164 %176\nOpStore %72 %177\n%178 = OpLoad  %22  %72\n%179 = OpAccessChain  %80  %24 %78\n%180 = OpLoad  %6  %179\n%181 = OpImageQueryLevels  %62  %180\n%182 = OpULessThan  %150  %63 %181\nOpSelectionMerge %183 None\nOpBranchConditional %182 %184 %183\n%184 = OpLabel\n%185 = OpImageQuerySizeLod  %64  %180 %63\n%186 = OpULessThan  %157  %65 %185\n%187 = OpAll  %150  %186\nOpBranchConditional %187 %188 %183\n%188 = OpLabel\n%189 = OpImageFetch  %22  %180 %65 Lod %63\nOpBranch %183\n%183 = OpLabel\n%190 = OpPhi  %22  %151 %169 %151 %184 %189 %188\n%191 = OpFAdd  %22  %178 %190\nOpStore %72 %191\n%192 = OpLoad  %3  %66\n%194 = OpAccessChain  %193  %30 %55\n%195 = OpLoad  %10  %194\n%197 = OpImageQuerySizeLod  %196  %195 %55\n%198 = OpCompositeExtract  %3  %197 2\n%199 = OpIAdd  %3  %192 %198\nOpStore %66 %199\n%200 = OpLoad  %3  %66\n%201 = OpAccessChain  %193  %30 %77\n%202 = OpLoad  %10  %201\n%203 = OpImageQuerySizeLod  %196  %202 %55\n%204 = OpCompositeExtract  %3  %203 2\n%205 = OpIAdd  %3  %200 %204\nOpStore %66 %205\n%206 = OpLoad  %3  %66\n%207 = OpAccessChain  %193  %30 %78\n%208 = OpLoad  %10  %207\n%209 = OpImageQuerySizeLod  %196  %208 %55\n%210 = OpCompositeExtract  %3  %209 2\n%211 = OpIAdd  %3  %206 %210\nOpStore %66 %211\n%212 = OpLoad  %3  %66\n%213 = OpAccessChain  %80  %28 %55\n%214 = OpLoad  %6  %213\n%215 = OpImageQueryLevels  %3  %214\n%216 = OpIAdd  %3  %212 %215\nOpStore %66 %216\n%217 = OpLoad  %3  %66\n%218 = OpAccessChain  %80  %28 %77\n%219 = OpLoad  %6  %218\n%220 = OpImageQueryLevels  %3  %219\n%221 = OpIAdd  %3  %217 %220\nOpStore %66 %221\n%222 = OpLoad  %3  %66\n%223 = OpAccessChain  %80  %28 %78\n%224 = OpLoad  %6  %223\n%225 = OpImageQueryLevels  %3  %224\n%226 = OpIAdd  %3  %222 %225\nOpStore %66 %226\n%227 = OpLoad  %3  %66\n%229 = OpAccessChain  %228  %32 %55\n%230 = OpLoad  %12  %229\n%231 = OpImageQuerySamples  %3  %230\n%232 = OpIAdd  %3  %227 %231\nOpStore %66 %232\n%233 = OpLoad  %3  %66\n%234 = OpAccessChain  %228  %32 %77\n%235 = OpLoad  %12  %234\n%236 = OpImageQuerySamples  %3  %235\n%237 = OpIAdd  %3  %233 %236\nOpStore %66 %237\n%238 = OpLoad  %3  %66\n%239 = OpAccessChain  %228  %32 %78\n%240 = OpLoad  %12  %239\n%241 = OpImageQuerySamples  %3  %240\n%242 = OpIAdd  %3  %238 %241\nOpStore %66 %242\n%243 = OpLoad  %22  %72\n%244 = OpAccessChain  %80  %28 %55\n%245 = OpLoad  %6  %244\n%246 = OpAccessChain  %98  %38 %55\n%247 = OpLoad  %18  %246\n%248 = OpSampledImage  %101  %245 %247\n%249 = OpImageSampleImplicitLod  %22  %248 %61\n%250 = OpFAdd  %22  %243 %249\nOpStore %72 %250\n%251 = OpLoad  %22  %72\n%252 = OpAccessChain  %80  %28 %77\n%253 = OpLoad  %6  %252\n%254 = OpAccessChain  %98  %38 %77\n%255 = OpLoad  %18  %254\n%256 = OpSampledImage  %101  %253 %255\n%257 = OpImageSampleImplicitLod  %22  %256 %61\n%258 = OpFAdd  %22  %251 %257\nOpStore %72 %258\n%259 = OpLoad  %22  %72\n%260 = OpAccessChain  %80  %28 %78\n%261 = OpLoad  %6  %260\n%262 = OpAccessChain  %98  %38 %78\n%263 = OpLoad  %18  %262\n%264 = OpSampledImage  %101  %261 %263\n%265 = OpImageSampleImplicitLod  %22  %264 %61\n%266 = OpFAdd  %22  %259 %265\nOpStore %72 %266\n%267 = OpLoad  %22  %72\n%268 = OpAccessChain  %80  %28 %55\n%269 = OpLoad  %6  %268\n%270 = OpAccessChain  %98  %38 %55\n%271 = OpLoad  %18  %270\n%272 = OpSampledImage  %101  %269 %271\n%273 = OpImageSampleImplicitLod  %22  %272 %61 Bias %58\n%274 = OpFAdd  %22  %267 %273\nOpStore %72 %274\n%275 = OpLoad  %22  %72\n%276 = OpAccessChain  %80  %28 %77\n%277 = OpLoad  %6  %276\n%278 = OpAccessChain  %98  %38 %77\n%279 = OpLoad  %18  %278\n%280 = OpSampledImage  %101  %277 %279\n%281 = OpImageSampleImplicitLod  %22  %280 %61 Bias %58\n%282 = OpFAdd  %22  %275 %281\nOpStore %72 %282\n%283 = OpLoad  %22  %72\n%284 = OpAccessChain  %80  %28 %78\n%285 = OpLoad  %6  %284\n%286 = OpAccessChain  %98  %38 %78\n%287 = OpLoad  %18  %286\n%288 = OpSampledImage  %101  %285 %287\n%289 = OpImageSampleImplicitLod  %22  %288 %61 Bias %58\n%290 = OpFAdd  %22  %283 %289\nOpStore %72 %290\n%291 = OpLoad  %5  %70\n%292 = OpAccessChain  %122  %34 %55\n%293 = OpLoad  %14  %292\n%294 = OpAccessChain  %98  %40 %55\n%295 = OpLoad  %18  %294\n%296 = OpSampledImage  %127  %293 %295\n%297 = OpImageSampleDrefImplicitLod  %5  %296 %61 %58\n%298 = OpFAdd  %5  %291 %297\nOpStore %70 %298\n%299 = OpLoad  %5  %70\n%300 = OpAccessChain  %122  %34 %77\n%301 = OpLoad  %14  %300\n%302 = OpAccessChain  %98  %40 %77\n%303 = OpLoad  %18  %302\n%304 = OpSampledImage  %127  %301 %303\n%305 = OpImageSampleDrefImplicitLod  %5  %304 %61 %58\n%306 = OpFAdd  %5  %299 %305\nOpStore %70 %306\n%307 = OpLoad  %5  %70\n%308 = OpAccessChain  %122  %34 %78\n%309 = OpLoad  %14  %308\n%310 = OpAccessChain  %98  %40 %78\n%311 = OpLoad  %18  %310\n%312 = OpSampledImage  %127  %309 %311\n%313 = OpImageSampleDrefImplicitLod  %5  %312 %61 %58\n%314 = OpFAdd  %5  %307 %313\nOpStore %70 %314\n%315 = OpLoad  %5  %70\n%316 = OpAccessChain  %122  %34 %55\n%317 = OpLoad  %14  %316\n%318 = OpAccessChain  %98  %40 %55\n%319 = OpLoad  %18  %318\n%320 = OpSampledImage  %127  %317 %319\n%321 = OpImageSampleDrefExplicitLod  %5  %320 %61 %58 Lod %58\n%322 = OpFAdd  %5  %315 %321\nOpStore %70 %322\n%323 = OpLoad  %5  %70\n%324 = OpAccessChain  %122  %34 %77\n%325 = OpLoad  %14  %324\n%326 = OpAccessChain  %98  %40 %77\n%327 = OpLoad  %18  %326\n%328 = OpSampledImage  %127  %325 %327\n%329 = OpImageSampleDrefExplicitLod  %5  %328 %61 %58 Lod %58\n%330 = OpFAdd  %5  %323 %329\nOpStore %70 %330\n%331 = OpLoad  %5  %70\n%332 = OpAccessChain  %122  %34 %78\n%333 = OpLoad  %14  %332\n%334 = OpAccessChain  %98  %40 %78\n%335 = OpLoad  %18  %334\n%336 = OpSampledImage  %127  %333 %335\n%337 = OpImageSampleDrefExplicitLod  %5  %336 %61 %58 Lod %58\n%338 = OpFAdd  %5  %331 %337\nOpStore %70 %338\n%339 = OpLoad  %22  %72\n%340 = OpAccessChain  %80  %28 %55\n%341 = OpLoad  %6  %340\n%342 = OpAccessChain  %98  %38 %55\n%343 = OpLoad  %18  %342\n%344 = OpSampledImage  %101  %341 %343\n%345 = OpImageSampleExplicitLod  %22  %344 %61 Grad %61 %61\n%346 = OpFAdd  %22  %339 %345\nOpStore %72 %346\n%347 = OpLoad  %22  %72\n%348 = OpAccessChain  %80  %28 %77\n%349 = OpLoad  %6  %348\n%350 = OpAccessChain  %98  %38 %77\n%351 = OpLoad  %18  %350\n%352 = OpSampledImage  %101  %349 %351\n%353 = OpImageSampleExplicitLod  %22  %352 %61 Grad %61 %61\n%354 = OpFAdd  %22  %347 %353\nOpStore %72 %354\n%355 = OpLoad  %22  %72\n%356 = OpAccessChain  %80  %28 %78\n%357 = OpLoad  %6  %356\n%358 = OpAccessChain  %98  %38 %78\n%359 = OpLoad  %18  %358\n%360 = OpSampledImage  %101  %357 %359\n%361 = OpImageSampleExplicitLod  %22  %360 %61 Grad %61 %61\n%362 = OpFAdd  %22  %355 %361\nOpStore %72 %362\n%363 = OpLoad  %22  %72\n%364 = OpAccessChain  %80  %28 %55\n%365 = OpLoad  %6  %364\n%366 = OpAccessChain  %98  %38 %55\n%367 = OpLoad  %18  %366\n%368 = OpSampledImage  %101  %365 %367\n%369 = OpImageSampleExplicitLod  %22  %368 %61 Lod %58\n%370 = OpFAdd  %22  %363 %369\nOpStore %72 %370\n%371 = OpLoad  %22  %72\n%372 = OpAccessChain  %80  %28 %77\n%373 = OpLoad  %6  %372\n%374 = OpAccessChain  %98  %38 %77\n%375 = OpLoad  %18  %374\n%376 = OpSampledImage  %101  %373 %375\n%377 = OpImageSampleExplicitLod  %22  %376 %61 Lod %58\n%378 = OpFAdd  %22  %371 %377\nOpStore %72 %378\n%379 = OpLoad  %22  %72\n%380 = OpAccessChain  %80  %28 %78\n%381 = OpLoad  %6  %380\n%382 = OpAccessChain  %98  %38 %78\n%383 = OpLoad  %18  %382\n%384 = OpSampledImage  %101  %381 %383\n%385 = OpImageSampleExplicitLod  %22  %384 %61 Lod %58\n%386 = OpFAdd  %22  %379 %385\nOpStore %72 %386\n%388 = OpAccessChain  %387  %36 %55\n%389 = OpLoad  %16  %388\n%390 = OpLoad  %22  %72\nOpImageWrite %389 %65 %390\n%391 = OpAccessChain  %387  %36 %77\n%392 = OpLoad  %16  %391\n%393 = OpLoad  %22  %72\nOpImageWrite %392 %65 %393\n%394 = OpAccessChain  %387  %36 %78\n%395 = OpLoad  %16  %394\n%396 = OpLoad  %22  %72\nOpImageWrite %395 %65 %396\n%397 = OpLoad  %23  %68\n%398 = OpLoad  %3  %66\n%399 = OpCompositeConstruct  %23  %398 %398\n%400 = OpIAdd  %23  %397 %399\n%401 = OpConvertUToF  %60  %400\n%402 = OpLoad  %22  %72\n%403 = OpCompositeExtract  %5  %401 0\n%404 = OpCompositeExtract  %5  %401 1\n%405 = OpCompositeExtract  %5  %401 0\n%406 = OpCompositeExtract  %5  %401 1\n%407 = OpCompositeConstruct  %22  %403 %404 %405 %406\n%408 = OpFAdd  %22  %402 %407\n%409 = OpLoad  %5  %70\n%410 = OpCompositeConstruct  %22  %409 %409 %409 %409\n%411 = OpFAdd  %22  %408 %410\nOpStore %50 %411\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-binding-buffer-arrays.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 76\nOpCapability Shader\nOpCapability ShaderNonUniform\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\nOpExtension \"SPV_EXT_descriptor_indexing\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %25 \"main\" %20 %23\nOpExecutionMode %25 OriginUpperLeft\nOpMemberDecorate %4 0 Offset 0\nOpDecorate %6 ArrayStride 4\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpDecorate %7 Block\nOpMemberDecorate %10 0 Offset 0\nOpDecorate %11 NonWritable\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 0\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 10\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %20 Location 0\nOpDecorate %20 Flat\nOpDecorate %23 Location 0\nOpDecorate %56 NonUniform\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%5 = OpTypeInt 32 1\n%6 = OpTypeRuntimeArray %5\n%7 = OpTypeStruct %3 %6\n%9 = OpConstant  %3  1\n%8 = OpTypeArray %7 %9\n%10 = OpTypeStruct %3\n%13 = OpConstant  %3  10\n%12 = OpTypeArray %7 %13\n%14 = OpTypePointer StorageBuffer %12\n%11 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %4\n%17 = OpTypePointer Uniform %16\n%15 = OpVariable  %17  Uniform\n%21 = OpTypePointer Input %3\n%20 = OpVariable  %21  Input\n%24 = OpTypePointer Output %3\n%23 = OpVariable  %24  Output\n%26 = OpTypeFunction %2\n%27 = OpTypePointer Uniform %4\n%28 = OpConstant  %3  0\n%30 = OpTypePointer StorageBuffer %8\n%32 = OpTypePointer Function %3\n%34 = OpTypePointer Uniform %3\n%39 = OpTypePointer StorageBuffer %7\n%40 = OpTypePointer StorageBuffer %3\n%46 = OpTypeBool\n%48 = OpConstantNull  %3\n%63 = OpTypePointer StorageBuffer %6\n%25 = OpFunction  %2  None %26\n%18 = OpLabel\n%31 = OpVariable  %32  Function %28\n%22 = OpLoad  %3  %20\n%19 = OpCompositeConstruct  %10  %22\n%29 = OpAccessChain  %27  %15 %28\nOpBranch %33\n%33 = OpLabel\n%35 = OpAccessChain  %34  %29 %28\n%36 = OpLoad  %3  %35\n%37 = OpCompositeExtract  %3  %19 0\n%38 = OpLoad  %3  %31\n%41 = OpAccessChain  %40  %11 %28 %28\n%42 = OpLoad  %3  %41\n%43 = OpIAdd  %3  %38 %42\nOpStore %31 %43\n%44 = OpLoad  %3  %31\n%45 = OpULessThan  %46  %36 %9\nOpSelectionMerge %49 None\nOpBranchConditional %45 %50 %49\n%50 = OpLabel\n%47 = OpAccessChain  %40  %11 %36 %28\n%51 = OpLoad  %3  %47\nOpBranch %49\n%49 = OpLabel\n%52 = OpPhi  %3  %48 %33 %51 %50\n%53 = OpIAdd  %3  %44 %52\nOpStore %31 %53\n%54 = OpLoad  %3  %31\n%55 = OpULessThan  %46  %37 %9\nOpSelectionMerge %57 None\nOpBranchConditional %55 %58 %57\n%58 = OpLabel\n%56 = OpAccessChain  %40  %11 %37 %28\n%59 = OpLoad  %3  %56\nOpBranch %57\n%57 = OpLabel\n%60 = OpPhi  %3  %48 %49 %59 %58\n%61 = OpIAdd  %3  %54 %60\nOpStore %31 %61\n%62 = OpLoad  %3  %31\n%64 = OpAccessChain  %39  %11 %28\n%65 = OpArrayLength  %3  %64 1\n%66 = OpIAdd  %3  %62 %65\nOpStore %31 %66\n%67 = OpLoad  %3  %31\n%68 = OpAccessChain  %39  %11 %36\n%69 = OpArrayLength  %3  %68 1\n%70 = OpIAdd  %3  %67 %69\nOpStore %31 %70\n%71 = OpLoad  %3  %31\n%72 = OpAccessChain  %39  %11 %37\n%73 = OpArrayLength  %3  %72 1\n%74 = OpIAdd  %3  %71 %73\nOpStore %31 %74\n%75 = OpLoad  %3  %31\nOpStore %23 %75\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bitcast.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 67\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %16 \"main\"\nOpExecutionMode %16 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 1\n%3 = OpTypeVector %4 2\n%5 = OpTypeVector %4 3\n%6 = OpTypeVector %4 4\n%8 = OpTypeInt 32 0\n%7 = OpTypeVector %8 2\n%9 = OpTypeVector %8 3\n%10 = OpTypeVector %8 4\n%12 = OpTypeFloat 32\n%11 = OpTypeVector %12 2\n%13 = OpTypeVector %12 3\n%14 = OpTypeVector %12 4\n%17 = OpTypeFunction %2\n%18 = OpConstant  %4  0\n%19 = OpConstantComposite  %3  %18 %18\n%20 = OpConstantComposite  %5  %18 %18 %18\n%21 = OpConstantComposite  %6  %18 %18 %18 %18\n%22 = OpConstant  %8  0\n%23 = OpConstantComposite  %7  %22 %22\n%24 = OpConstantComposite  %9  %22 %22 %22\n%25 = OpConstantComposite  %10  %22 %22 %22 %22\n%26 = OpConstant  %12  0\n%27 = OpConstantComposite  %11  %26 %26\n%28 = OpConstantComposite  %13  %26 %26 %26\n%29 = OpConstantComposite  %14  %26 %26 %26 %26\n%31 = OpTypePointer Function %3\n%33 = OpTypePointer Function %5\n%35 = OpTypePointer Function %6\n%37 = OpTypePointer Function %7\n%39 = OpTypePointer Function %9\n%41 = OpTypePointer Function %10\n%43 = OpTypePointer Function %11\n%45 = OpTypePointer Function %13\n%47 = OpTypePointer Function %14\n%16 = OpFunction  %2  None %17\n%15 = OpLabel\n%42 = OpVariable  %43  Function %27\n%36 = OpVariable  %37  Function %23\n%30 = OpVariable  %31  Function %19\n%44 = OpVariable  %45  Function %28\n%38 = OpVariable  %39  Function %24\n%32 = OpVariable  %33  Function %20\n%46 = OpVariable  %47  Function %29\n%40 = OpVariable  %41  Function %25\n%34 = OpVariable  %35  Function %21\nOpBranch %48\n%48 = OpLabel\n%49 = OpLoad  %3  %30\n%50 = OpBitcast  %7  %49\nOpStore %36 %50\n%51 = OpLoad  %5  %32\n%52 = OpBitcast  %9  %51\nOpStore %38 %52\n%53 = OpLoad  %6  %34\n%54 = OpBitcast  %10  %53\nOpStore %40 %54\n%55 = OpLoad  %7  %36\n%56 = OpBitcast  %3  %55\nOpStore %30 %56\n%57 = OpLoad  %9  %38\n%58 = OpBitcast  %5  %57\nOpStore %32 %58\n%59 = OpLoad  %10  %40\n%60 = OpBitcast  %6  %59\nOpStore %34 %60\n%61 = OpLoad  %3  %30\n%62 = OpBitcast  %11  %61\nOpStore %42 %62\n%63 = OpLoad  %5  %32\n%64 = OpBitcast  %13  %63\nOpStore %44 %64\n%65 = OpLoad  %6  %34\n%66 = OpBitcast  %14  %65\nOpStore %46 %66\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bits.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 234\nOpCapability Shader\nOpCapability Int8\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %15 \"main\"\nOpExecutionMode %15 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeVector %3 2\n%5 = OpTypeVector %3 3\n%6 = OpTypeVector %3 4\n%7 = OpTypeInt 32 0\n%8 = OpTypeVector %7 2\n%9 = OpTypeVector %7 3\n%10 = OpTypeVector %7 4\n%12 = OpTypeFloat 32\n%11 = OpTypeVector %12 2\n%13 = OpTypeVector %12 4\n%16 = OpTypeFunction %2\n%17 = OpConstant  %3  0\n%18 = OpConstantComposite  %4  %17 %17\n%19 = OpConstantComposite  %5  %17 %17 %17\n%20 = OpConstantComposite  %6  %17 %17 %17 %17\n%21 = OpConstant  %7  0\n%22 = OpConstantComposite  %8  %21 %21\n%23 = OpConstantComposite  %9  %21 %21 %21\n%24 = OpConstantComposite  %10  %21 %21 %21 %21\n%25 = OpConstant  %12  0\n%26 = OpConstantComposite  %11  %25 %25\n%27 = OpConstantComposite  %13  %25 %25 %25 %25\n%28 = OpConstant  %7  5\n%29 = OpConstant  %7  10\n%31 = OpTypePointer Function %3\n%33 = OpTypePointer Function %4\n%35 = OpTypePointer Function %5\n%37 = OpTypePointer Function %6\n%39 = OpTypePointer Function %7\n%41 = OpTypePointer Function %8\n%43 = OpTypePointer Function %9\n%45 = OpTypePointer Function %10\n%47 = OpTypePointer Function %11\n%49 = OpTypePointer Function %13\n%64 = OpTypeInt 8 0\n%63 = OpTypeVector %64 4\n%71 = OpConstant  %3  -128\n%72 = OpConstantComposite  %6  %71 %71 %71 %71\n%73 = OpConstant  %3  127\n%74 = OpConstantComposite  %6  %73 %73 %73 %73\n%79 = OpConstant  %7  255\n%80 = OpConstantComposite  %10  %79 %79 %79 %79\n%96 = OpTypeInt 8 1\n%95 = OpTypeVector %96 4\n%104 = OpConstant  %7  32\n%15 = OpFunction  %2  None %16\n%14 = OpLabel\n%48 = OpVariable  %49  Function %27\n%42 = OpVariable  %43  Function %23\n%36 = OpVariable  %37  Function %20\n%30 = OpVariable  %31  Function %17\n%44 = OpVariable  %45  Function %24\n%38 = OpVariable  %39  Function %21\n%32 = OpVariable  %33  Function %18\n%46 = OpVariable  %47  Function %26\n%40 = OpVariable  %41  Function %22\n%34 = OpVariable  %35  Function %19\nOpBranch %50\n%50 = OpLabel\n%51 = OpLoad  %13  %48\n%52 = OpExtInst  %7  %1 PackSnorm4x8 %51\nOpStore %38 %52\n%53 = OpLoad  %13  %48\n%54 = OpExtInst  %7  %1 PackUnorm4x8 %53\nOpStore %38 %54\n%55 = OpLoad  %11  %46\n%56 = OpExtInst  %7  %1 PackSnorm2x16 %55\nOpStore %38 %56\n%57 = OpLoad  %11  %46\n%58 = OpExtInst  %7  %1 PackUnorm2x16 %57\nOpStore %38 %58\n%59 = OpLoad  %11  %46\n%60 = OpExtInst  %7  %1 PackHalf2x16 %59\nOpStore %38 %60\n%61 = OpLoad  %6  %36\n%65 = OpUConvert  %63  %61\n%62 = OpBitcast  %7  %65\nOpStore %38 %62\n%66 = OpLoad  %10  %44\n%68 = OpUConvert  %63  %66\n%67 = OpBitcast  %7  %68\nOpStore %38 %67\n%69 = OpLoad  %6  %36\n%75 = OpExtInst  %6  %1 SClamp %69 %72 %74\n%76 = OpUConvert  %63  %75\n%70 = OpBitcast  %7  %76\nOpStore %38 %70\n%77 = OpLoad  %10  %44\n%81 = OpExtInst  %10  %1 UClamp %77 %24 %80\n%82 = OpUConvert  %63  %81\n%78 = OpBitcast  %7  %82\nOpStore %38 %78\n%83 = OpLoad  %7  %38\n%84 = OpExtInst  %13  %1 UnpackSnorm4x8 %83\nOpStore %48 %84\n%85 = OpLoad  %7  %38\n%86 = OpExtInst  %13  %1 UnpackUnorm4x8 %85\nOpStore %48 %86\n%87 = OpLoad  %7  %38\n%88 = OpExtInst  %11  %1 UnpackSnorm2x16 %87\nOpStore %46 %88\n%89 = OpLoad  %7  %38\n%90 = OpExtInst  %11  %1 UnpackUnorm2x16 %89\nOpStore %46 %90\n%91 = OpLoad  %7  %38\n%92 = OpExtInst  %11  %1 UnpackHalf2x16 %91\nOpStore %46 %92\n%93 = OpLoad  %7  %38\n%97 = OpBitcast  %95  %93\n%94 = OpSConvert  %6  %97\nOpStore %36 %94\n%98 = OpLoad  %7  %38\n%100 = OpBitcast  %63  %98\n%99 = OpUConvert  %10  %100\nOpStore %44 %99\n%101 = OpLoad  %3  %30\n%102 = OpLoad  %3  %30\n%105 = OpExtInst  %7  %1 UMin %28 %104\n%106 = OpISub  %7  %104 %105\n%107 = OpExtInst  %7  %1 UMin %29 %106\n%103 = OpBitFieldInsert  %3  %101 %102 %105 %107\nOpStore %30 %103\n%108 = OpLoad  %4  %32\n%109 = OpLoad  %4  %32\n%111 = OpExtInst  %7  %1 UMin %28 %104\n%112 = OpISub  %7  %104 %111\n%113 = OpExtInst  %7  %1 UMin %29 %112\n%110 = OpBitFieldInsert  %4  %108 %109 %111 %113\nOpStore %32 %110\n%114 = OpLoad  %5  %34\n%115 = OpLoad  %5  %34\n%117 = OpExtInst  %7  %1 UMin %28 %104\n%118 = OpISub  %7  %104 %117\n%119 = OpExtInst  %7  %1 UMin %29 %118\n%116 = OpBitFieldInsert  %5  %114 %115 %117 %119\nOpStore %34 %116\n%120 = OpLoad  %6  %36\n%121 = OpLoad  %6  %36\n%123 = OpExtInst  %7  %1 UMin %28 %104\n%124 = OpISub  %7  %104 %123\n%125 = OpExtInst  %7  %1 UMin %29 %124\n%122 = OpBitFieldInsert  %6  %120 %121 %123 %125\nOpStore %36 %122\n%126 = OpLoad  %7  %38\n%127 = OpLoad  %7  %38\n%129 = OpExtInst  %7  %1 UMin %28 %104\n%130 = OpISub  %7  %104 %129\n%131 = OpExtInst  %7  %1 UMin %29 %130\n%128 = OpBitFieldInsert  %7  %126 %127 %129 %131\nOpStore %38 %128\n%132 = OpLoad  %8  %40\n%133 = OpLoad  %8  %40\n%135 = OpExtInst  %7  %1 UMin %28 %104\n%136 = OpISub  %7  %104 %135\n%137 = OpExtInst  %7  %1 UMin %29 %136\n%134 = OpBitFieldInsert  %8  %132 %133 %135 %137\nOpStore %40 %134\n%138 = OpLoad  %9  %42\n%139 = OpLoad  %9  %42\n%141 = OpExtInst  %7  %1 UMin %28 %104\n%142 = OpISub  %7  %104 %141\n%143 = OpExtInst  %7  %1 UMin %29 %142\n%140 = OpBitFieldInsert  %9  %138 %139 %141 %143\nOpStore %42 %140\n%144 = OpLoad  %10  %44\n%145 = OpLoad  %10  %44\n%147 = OpExtInst  %7  %1 UMin %28 %104\n%148 = OpISub  %7  %104 %147\n%149 = OpExtInst  %7  %1 UMin %29 %148\n%146 = OpBitFieldInsert  %10  %144 %145 %147 %149\nOpStore %44 %146\n%150 = OpLoad  %3  %30\n%152 = OpExtInst  %7  %1 UMin %28 %104\n%153 = OpISub  %7  %104 %152\n%154 = OpExtInst  %7  %1 UMin %29 %153\n%151 = OpBitFieldSExtract  %3  %150 %152 %154\nOpStore %30 %151\n%155 = OpLoad  %4  %32\n%157 = OpExtInst  %7  %1 UMin %28 %104\n%158 = OpISub  %7  %104 %157\n%159 = OpExtInst  %7  %1 UMin %29 %158\n%156 = OpBitFieldSExtract  %4  %155 %157 %159\nOpStore %32 %156\n%160 = OpLoad  %5  %34\n%162 = OpExtInst  %7  %1 UMin %28 %104\n%163 = OpISub  %7  %104 %162\n%164 = OpExtInst  %7  %1 UMin %29 %163\n%161 = OpBitFieldSExtract  %5  %160 %162 %164\nOpStore %34 %161\n%165 = OpLoad  %6  %36\n%167 = OpExtInst  %7  %1 UMin %28 %104\n%168 = OpISub  %7  %104 %167\n%169 = OpExtInst  %7  %1 UMin %29 %168\n%166 = OpBitFieldSExtract  %6  %165 %167 %169\nOpStore %36 %166\n%170 = OpLoad  %7  %38\n%172 = OpExtInst  %7  %1 UMin %28 %104\n%173 = OpISub  %7  %104 %172\n%174 = OpExtInst  %7  %1 UMin %29 %173\n%171 = OpBitFieldUExtract  %7  %170 %172 %174\nOpStore %38 %171\n%175 = OpLoad  %8  %40\n%177 = OpExtInst  %7  %1 UMin %28 %104\n%178 = OpISub  %7  %104 %177\n%179 = OpExtInst  %7  %1 UMin %29 %178\n%176 = OpBitFieldUExtract  %8  %175 %177 %179\nOpStore %40 %176\n%180 = OpLoad  %9  %42\n%182 = OpExtInst  %7  %1 UMin %28 %104\n%183 = OpISub  %7  %104 %182\n%184 = OpExtInst  %7  %1 UMin %29 %183\n%181 = OpBitFieldUExtract  %9  %180 %182 %184\nOpStore %42 %181\n%185 = OpLoad  %10  %44\n%187 = OpExtInst  %7  %1 UMin %28 %104\n%188 = OpISub  %7  %104 %187\n%189 = OpExtInst  %7  %1 UMin %29 %188\n%186 = OpBitFieldUExtract  %10  %185 %187 %189\nOpStore %44 %186\n%190 = OpLoad  %3  %30\n%191 = OpExtInst  %3  %1 FindILsb %190\nOpStore %30 %191\n%192 = OpLoad  %8  %40\n%193 = OpExtInst  %8  %1 FindILsb %192\nOpStore %40 %193\n%194 = OpLoad  %5  %34\n%195 = OpExtInst  %5  %1 FindSMsb %194\nOpStore %34 %195\n%196 = OpLoad  %9  %42\n%197 = OpExtInst  %9  %1 FindUMsb %196\nOpStore %42 %197\n%198 = OpLoad  %3  %30\n%199 = OpExtInst  %3  %1 FindSMsb %198\nOpStore %30 %199\n%200 = OpLoad  %7  %38\n%201 = OpExtInst  %7  %1 FindUMsb %200\nOpStore %38 %201\n%202 = OpLoad  %3  %30\n%203 = OpBitCount  %3  %202\nOpStore %30 %203\n%204 = OpLoad  %4  %32\n%205 = OpBitCount  %4  %204\nOpStore %32 %205\n%206 = OpLoad  %5  %34\n%207 = OpBitCount  %5  %206\nOpStore %34 %207\n%208 = OpLoad  %6  %36\n%209 = OpBitCount  %6  %208\nOpStore %36 %209\n%210 = OpLoad  %7  %38\n%211 = OpBitCount  %7  %210\nOpStore %38 %211\n%212 = OpLoad  %8  %40\n%213 = OpBitCount  %8  %212\nOpStore %40 %213\n%214 = OpLoad  %9  %42\n%215 = OpBitCount  %9  %214\nOpStore %42 %215\n%216 = OpLoad  %10  %44\n%217 = OpBitCount  %10  %216\nOpStore %44 %217\n%218 = OpLoad  %3  %30\n%219 = OpBitReverse  %3  %218\nOpStore %30 %219\n%220 = OpLoad  %4  %32\n%221 = OpBitReverse  %4  %220\nOpStore %32 %221\n%222 = OpLoad  %5  %34\n%223 = OpBitReverse  %5  %222\nOpStore %34 %223\n%224 = OpLoad  %6  %36\n%225 = OpBitReverse  %6  %224\nOpStore %36 %225\n%226 = OpLoad  %7  %38\n%227 = OpBitReverse  %7  %226\nOpStore %38 %227\n%228 = OpLoad  %8  %40\n%229 = OpBitReverse  %8  %228\nOpStore %40 %229\n%230 = OpLoad  %9  %42\n%231 = OpBitReverse  %9  %230\nOpStore %42 %231\n%232 = OpLoad  %10  %44\n%233 = OpBitReverse  %10  %232\nOpStore %44 %233\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-boids.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 226\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %24 \"main\" %21\nOpExecutionMode %24 LocalSize 64 1 1\n%3 = OpString \"boids.wgsl\"\nOpSource Unknown 0 %3 \"const NUM_PARTICLES: u32 = 1500u;\n\nstruct Particle {\n  pos : vec2<f32>,\n  vel : vec2<f32>,\n}\n\nstruct SimParams {\n  deltaT : f32,\n  rule1Distance : f32,\n  rule2Distance : f32,\n  rule3Distance : f32,\n  rule1Scale : f32,\n  rule2Scale : f32,\n  rule3Scale : f32,\n}\n\nstruct Particles {\n  particles : array<Particle>\n}\n\n@group(0) @binding(0) var<uniform> params : SimParams;\n@group(0) @binding(1) var<storage> particlesSrc : Particles;\n@group(0) @binding(2) var<storage,read_write> particlesDst : Particles;\n\n// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) global_invocation_id : vec3<u32>) {\n  let index : u32 = global_invocation_id.x;\n  if index >= NUM_PARTICLES {\n    return;\n  }\n\n  var vPos = particlesSrc.particles[index].pos;\n  var vVel = particlesSrc.particles[index].vel;\n\n  var cMass = vec2<f32>(0.0, 0.0);\n  var cVel = vec2<f32>(0.0, 0.0);\n  var colVel = vec2<f32>(0.0, 0.0);\n  var cMassCount : i32 = 0;\n  var cVelCount : i32 = 0;\n\n  var pos : vec2<f32>;\n  var vel : vec2<f32>;\n  var i : u32 = 0u;\n  loop {\n    if i >= NUM_PARTICLES {\n      break;\n    }\n    if i == index {\n      continue;\n    }\n\n    pos = particlesSrc.particles[i].pos;\n    vel = particlesSrc.particles[i].vel;\n\n    if distance(pos, vPos) < params.rule1Distance {\n      cMass = cMass + pos;\n      cMassCount = cMassCount + 1;\n    }\n    if distance(pos, vPos) < params.rule2Distance {\n      colVel = colVel - (pos - vPos);\n    }\n    if distance(pos, vPos) < params.rule3Distance {\n      cVel = cVel + vel;\n      cVelCount = cVelCount + 1;\n    }\n\n    continuing {\n      i = i + 1u;\n    }\n  }\n  if cMassCount > 0 {\n    cMass = cMass / f32(cMassCount) - vPos;\n  }\n  if cVelCount > 0 {\n    cVel = cVel / f32(cVelCount);\n  }\n\n  vVel = vVel + (cMass * params.rule1Scale) +\n      (colVel * params.rule2Scale) +\n      (cVel * params.rule3Scale);\n\n  // clamp velocity for a more pleasing simulation\n  vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);\n\n  // kinematic update\n  vPos = vPos + (vVel * params.deltaT);\n\n  // Wrap around boundary\n  if vPos.x < -1.0 {\n    vPos.x = 1.0;\n  }\n  if vPos.x > 1.0 {\n    vPos.x = -1.0;\n  }\n  if vPos.y < -1.0 {\n    vPos.y = 1.0;\n  }\n  if vPos.y > 1.0 {\n    vPos.y = -1.0;\n  }\n\n  // Write back\n  particlesDst.particles[index].pos = vPos;\n  particlesDst.particles[index].vel = vVel;\n}\n\"\nOpMemberName %7 0 \"pos\"\nOpMemberName %7 1 \"vel\"\nOpName %7 \"Particle\"\nOpMemberName %8 0 \"deltaT\"\nOpMemberName %8 1 \"rule1Distance\"\nOpMemberName %8 2 \"rule2Distance\"\nOpMemberName %8 3 \"rule3Distance\"\nOpMemberName %8 4 \"rule1Scale\"\nOpMemberName %8 5 \"rule2Scale\"\nOpMemberName %8 6 \"rule3Scale\"\nOpName %8 \"SimParams\"\nOpMemberName %10 0 \"particles\"\nOpName %10 \"Particles\"\nOpName %13 \"NUM_PARTICLES\"\nOpName %14 \"params\"\nOpName %17 \"particlesSrc\"\nOpName %19 \"particlesDst\"\nOpName %21 \"global_invocation_id\"\nOpName %24 \"main\"\nOpName %37 \"vPos\"\nOpName %40 \"vVel\"\nOpName %42 \"cMass\"\nOpName %43 \"cVel\"\nOpName %44 \"colVel\"\nOpName %45 \"cMassCount\"\nOpName %47 \"cVelCount\"\nOpName %48 \"pos\"\nOpName %50 \"vel\"\nOpName %52 \"i\"\nOpName %77 \"loop_bound\"\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 8\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 4\nOpMemberDecorate %8 2 Offset 8\nOpMemberDecorate %8 3 Offset 12\nOpMemberDecorate %8 4 Offset 16\nOpMemberDecorate %8 5 Offset 20\nOpMemberDecorate %8 6 Offset 24\nOpDecorate %9 ArrayStride 16\nOpMemberDecorate %10 0 Offset 0\nOpDecorate %10 Block\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 0\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\nOpDecorate %17 NonWritable\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 1\nOpDecorate %19 DescriptorSet 0\nOpDecorate %19 Binding 2\nOpDecorate %21 BuiltIn GlobalInvocationId\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%5 = OpTypeFloat 32\n%6 = OpTypeVector %5 2\n%7 = OpTypeStruct %6 %6\n%8 = OpTypeStruct %5 %5 %5 %5 %5 %5 %5\n%9 = OpTypeRuntimeArray %7\n%10 = OpTypeStruct %9\n%11 = OpTypeVector %4 3\n%12 = OpTypeInt 32 1\n%13 = OpConstant  %4  1500\n%15 = OpTypeStruct %8\n%16 = OpTypePointer Uniform %15\n%14 = OpVariable  %16  Uniform\n%18 = OpTypePointer StorageBuffer %10\n%17 = OpVariable  %18  StorageBuffer\n%19 = OpVariable  %18  StorageBuffer\n%22 = OpTypePointer Input %11\n%21 = OpVariable  %22  Input\n%25 = OpTypeFunction %2\n%26 = OpTypePointer Uniform %8\n%27 = OpConstant  %4  0\n%29 = OpConstant  %5  0\n%30 = OpConstantComposite  %6  %29 %29\n%31 = OpConstant  %12  0\n%32 = OpConstant  %12  1\n%33 = OpConstant  %4  1\n%34 = OpConstant  %5  0.1\n%35 = OpConstant  %5  -1\n%36 = OpConstant  %5  1\n%38 = OpTypePointer Function %6\n%39 = OpConstantNull  %6\n%41 = OpConstantNull  %6\n%46 = OpTypePointer Function %12\n%49 = OpConstantNull  %6\n%51 = OpConstantNull  %6\n%53 = OpTypePointer Function %4\n%56 = OpTypeBool\n%60 = OpTypePointer StorageBuffer %9\n%61 = OpTypePointer StorageBuffer %7\n%62 = OpTypePointer StorageBuffer %6\n%71 = OpTypeVector %4 2\n%72 = OpTypePointer Function %71\n%73 = OpTypeVector %56 2\n%74 = OpConstantComposite  %71  %27 %27\n%75 = OpConstant  %4  4294967295\n%76 = OpConstantComposite  %71  %75 %75\n%105 = OpTypePointer Uniform %5\n%119 = OpConstant  %4  2\n%133 = OpConstant  %4  3\n%168 = OpConstant  %4  4\n%174 = OpConstant  %4  5\n%180 = OpConstant  %4  6\n%197 = OpTypePointer Function %5\n%24 = OpFunction  %2  None %25\n%20 = OpLabel\n%52 = OpVariable  %53  Function %27\n%47 = OpVariable  %46  Function %31\n%43 = OpVariable  %38  Function %30\n%37 = OpVariable  %38  Function %39\n%48 = OpVariable  %38  Function %49\n%44 = OpVariable  %38  Function %30\n%40 = OpVariable  %38  Function %41\n%50 = OpVariable  %38  Function %51\n%45 = OpVariable  %46  Function %31\n%42 = OpVariable  %38  Function %30\n%77 = OpVariable  %72  Function %76\n%23 = OpLoad  %11  %21\n%28 = OpAccessChain  %26  %14 %27\nOpBranch %54\n%54 = OpLabel\nOpLine %3 29 21\n%55 = OpCompositeExtract  %4  %23 0\nOpLine %3 30 6\n%57 = OpUGreaterThanEqual  %56  %55 %13\nOpLine %3 30 3\nOpSelectionMerge %58 None\nOpBranchConditional %57 %59 %58\n%59 = OpLabel\nOpReturn\n%58 = OpLabel\nOpLine %3 34 14\n%63 = OpAccessChain  %62  %17 %27 %55 %27\n%64 = OpLoad  %6  %63\nOpLine %3 34 3\nOpStore %37 %64\nOpLine %3 35 14\n%65 = OpAccessChain  %62  %17 %27 %55 %33\n%66 = OpLoad  %6  %65\nOpLine %3 35 3\nOpStore %40 %66\nOpLine %3 37 15\nOpLine %3 38 14\nOpLine %3 39 16\nOpBranch %67\n%67 = OpLabel\nOpLine %3 46 3\nOpLoopMerge %68 %70 None\nOpBranch %78\n%78 = OpLabel\n%79 = OpLoad  %71  %77\n%80 = OpIEqual  %73  %74 %79\n%81 = OpAll  %56  %80\nOpSelectionMerge %82 None\nOpBranchConditional %81 %68 %82\n%82 = OpLabel\n%83 = OpCompositeExtract  %4  %79 1\n%84 = OpIEqual  %56  %83 %27\n%85 = OpSelect  %4  %84 %33 %27\n%86 = OpCompositeConstruct  %71  %85 %33\n%87 = OpISub  %71  %79 %86\nOpStore %77 %87\nOpBranch %69\n%69 = OpLabel\nOpLine %3 1 1\n%88 = OpLoad  %4  %52\nOpLine %3 47 8\n%89 = OpUGreaterThanEqual  %56  %88 %13\nOpLine %3 47 5\nOpSelectionMerge %90 None\nOpBranchConditional %89 %91 %90\n%91 = OpLabel\nOpBranch %68\n%90 = OpLabel\nOpLine %3 50 8\n%92 = OpLoad  %4  %52\n%93 = OpIEqual  %56  %92 %55\nOpLine %3 50 5\nOpSelectionMerge %94 None\nOpBranchConditional %93 %95 %94\n%95 = OpLabel\nOpBranch %70\n%94 = OpLabel\nOpLine %3 54 11\n%96 = OpLoad  %4  %52\n%97 = OpAccessChain  %62  %17 %27 %96 %27\n%98 = OpLoad  %6  %97\nOpLine %3 54 5\nOpStore %48 %98\nOpLine %3 55 11\n%99 = OpLoad  %4  %52\n%100 = OpAccessChain  %62  %17 %27 %99 %33\n%101 = OpLoad  %6  %100\nOpLine %3 55 5\nOpStore %50 %101\nOpLine %3 57 8\n%102 = OpLoad  %6  %48\n%103 = OpLoad  %6  %37\n%104 = OpExtInst  %5  %1 Distance %102 %103\nOpLine %3 57 8\n%106 = OpAccessChain  %105  %28 %33\n%107 = OpLoad  %5  %106\n%108 = OpFOrdLessThan  %56  %104 %107\nOpLine %3 57 5\nOpSelectionMerge %109 None\nOpBranchConditional %108 %110 %109\n%110 = OpLabel\nOpLine %3 58 15\n%111 = OpLoad  %6  %42\n%112 = OpLoad  %6  %48\n%113 = OpFAdd  %6  %111 %112\nOpLine %3 58 7\nOpStore %42 %113\nOpLine %3 1 1\n%114 = OpLoad  %12  %45\nOpLine %3 59 20\n%115 = OpIAdd  %12  %114 %32\nOpLine %3 59 7\nOpStore %45 %115\nOpBranch %109\n%109 = OpLabel\nOpLine %3 61 8\n%116 = OpLoad  %6  %48\n%117 = OpLoad  %6  %37\n%118 = OpExtInst  %5  %1 Distance %116 %117\nOpLine %3 61 8\n%120 = OpAccessChain  %105  %28 %119\n%121 = OpLoad  %5  %120\n%122 = OpFOrdLessThan  %56  %118 %121\nOpLine %3 61 5\nOpSelectionMerge %123 None\nOpBranchConditional %122 %124 %123\n%124 = OpLabel\nOpLine %3 62 16\n%125 = OpLoad  %6  %44\n%126 = OpLoad  %6  %48\n%127 = OpLoad  %6  %37\n%128 = OpFSub  %6  %126 %127\n%129 = OpFSub  %6  %125 %128\nOpLine %3 62 7\nOpStore %44 %129\nOpBranch %123\n%123 = OpLabel\nOpLine %3 64 8\n%130 = OpLoad  %6  %48\n%131 = OpLoad  %6  %37\n%132 = OpExtInst  %5  %1 Distance %130 %131\nOpLine %3 64 8\n%134 = OpAccessChain  %105  %28 %133\n%135 = OpLoad  %5  %134\n%136 = OpFOrdLessThan  %56  %132 %135\nOpLine %3 64 5\nOpSelectionMerge %137 None\nOpBranchConditional %136 %138 %137\n%138 = OpLabel\nOpLine %3 65 14\n%139 = OpLoad  %6  %43\n%140 = OpLoad  %6  %50\n%141 = OpFAdd  %6  %139 %140\nOpLine %3 65 7\nOpStore %43 %141\nOpLine %3 1 1\n%142 = OpLoad  %12  %47\nOpLine %3 66 19\n%143 = OpIAdd  %12  %142 %32\nOpLine %3 66 7\nOpStore %47 %143\nOpBranch %137\n%137 = OpLabel\nOpBranch %70\n%70 = OpLabel\nOpLine %3 1 1\n%144 = OpLoad  %4  %52\nOpLine %3 70 11\n%145 = OpIAdd  %4  %144 %33\nOpLine %3 70 7\nOpStore %52 %145\nOpBranch %67\n%68 = OpLabel\nOpLine %3 1 1\n%146 = OpLoad  %12  %45\nOpLine %3 73 6\n%147 = OpSGreaterThan  %56  %146 %31\nOpLine %3 73 3\nOpSelectionMerge %148 None\nOpBranchConditional %147 %149 %148\n%149 = OpLabel\nOpLine %3 74 13\n%150 = OpLoad  %6  %42\n%151 = OpLoad  %12  %45\n%152 = OpConvertSToF  %5  %151\n%153 = OpCompositeConstruct  %6  %152 %152\n%154 = OpFDiv  %6  %150 %153\n%155 = OpLoad  %6  %37\n%156 = OpFSub  %6  %154 %155\nOpLine %3 74 5\nOpStore %42 %156\nOpBranch %148\n%148 = OpLabel\nOpLine %3 1 1\n%157 = OpLoad  %12  %47\nOpLine %3 76 6\n%158 = OpSGreaterThan  %56  %157 %31\nOpLine %3 76 3\nOpSelectionMerge %159 None\nOpBranchConditional %158 %160 %159\n%160 = OpLabel\nOpLine %3 77 12\n%161 = OpLoad  %6  %43\n%162 = OpLoad  %12  %47\n%163 = OpConvertSToF  %5  %162\n%164 = OpCompositeConstruct  %6  %163 %163\n%165 = OpFDiv  %6  %161 %164\nOpLine %3 77 5\nOpStore %43 %165\nOpBranch %159\n%159 = OpLabel\nOpLine %3 1 1\n%166 = OpLoad  %6  %40\n%167 = OpLoad  %6  %42\nOpLine %3 80 10\n%169 = OpAccessChain  %105  %28 %168\n%170 = OpLoad  %5  %169\n%171 = OpVectorTimesScalar  %6  %167 %170\n%172 = OpFAdd  %6  %166 %171\n%173 = OpLoad  %6  %44\nOpLine %3 80 10\n%175 = OpAccessChain  %105  %28 %174\n%176 = OpLoad  %5  %175\n%177 = OpVectorTimesScalar  %6  %173 %176\n%178 = OpFAdd  %6  %172 %177\n%179 = OpLoad  %6  %43\nOpLine %3 80 10\n%181 = OpAccessChain  %105  %28 %180\n%182 = OpLoad  %5  %181\n%183 = OpVectorTimesScalar  %6  %179 %182\n%184 = OpFAdd  %6  %178 %183\nOpLine %3 80 3\nOpStore %40 %184\nOpLine %3 85 10\n%185 = OpLoad  %6  %40\n%186 = OpExtInst  %6  %1 Normalize %185\n%187 = OpLoad  %6  %40\n%188 = OpExtInst  %5  %1 Length %187\nOpLine %3 85 10\n%189 = OpExtInst  %5  %1 FClamp %188 %29 %34\n%190 = OpVectorTimesScalar  %6  %186 %189\nOpLine %3 85 3\nOpStore %40 %190\nOpLine %3 1 1\n%191 = OpLoad  %6  %37\n%192 = OpLoad  %6  %40\nOpLine %3 88 10\n%193 = OpAccessChain  %105  %28 %27\n%194 = OpLoad  %5  %193\n%195 = OpVectorTimesScalar  %6  %192 %194\n%196 = OpFAdd  %6  %191 %195\nOpLine %3 88 3\nOpStore %37 %196\nOpLine %3 91 6\n%198 = OpAccessChain  %197  %37 %27\n%199 = OpLoad  %5  %198\nOpLine %3 91 6\n%200 = OpFOrdLessThan  %56  %199 %35\nOpLine %3 91 3\nOpSelectionMerge %201 None\nOpBranchConditional %200 %202 %201\n%202 = OpLabel\nOpLine %3 92 5\nOpLine %3 92 5\n%203 = OpAccessChain  %197  %37 %27\nOpStore %203 %36\nOpBranch %201\n%201 = OpLabel\nOpLine %3 94 6\n%204 = OpAccessChain  %197  %37 %27\n%205 = OpLoad  %5  %204\nOpLine %3 94 6\n%206 = OpFOrdGreaterThan  %56  %205 %36\nOpLine %3 94 3\nOpSelectionMerge %207 None\nOpBranchConditional %206 %208 %207\n%208 = OpLabel\nOpLine %3 95 5\nOpLine %3 95 5\n%209 = OpAccessChain  %197  %37 %27\nOpStore %209 %35\nOpBranch %207\n%207 = OpLabel\nOpLine %3 97 6\n%210 = OpAccessChain  %197  %37 %33\n%211 = OpLoad  %5  %210\nOpLine %3 97 6\n%212 = OpFOrdLessThan  %56  %211 %35\nOpLine %3 97 3\nOpSelectionMerge %213 None\nOpBranchConditional %212 %214 %213\n%214 = OpLabel\nOpLine %3 98 5\nOpLine %3 98 5\n%215 = OpAccessChain  %197  %37 %33\nOpStore %215 %36\nOpBranch %213\n%213 = OpLabel\nOpLine %3 100 6\n%216 = OpAccessChain  %197  %37 %33\n%217 = OpLoad  %5  %216\nOpLine %3 100 6\n%218 = OpFOrdGreaterThan  %56  %217 %36\nOpLine %3 100 3\nOpSelectionMerge %219 None\nOpBranchConditional %218 %220 %219\n%220 = OpLabel\nOpLine %3 101 5\nOpLine %3 101 5\n%221 = OpAccessChain  %197  %37 %33\nOpStore %221 %35\nOpBranch %219\n%219 = OpLabel\nOpLine %3 105 3\n%222 = OpLoad  %6  %37\nOpLine %3 105 3\n%223 = OpAccessChain  %62  %19 %27 %55 %27\nOpStore %223 %222\nOpLine %3 106 3\n%224 = OpLoad  %6  %40\nOpLine %3 106 3\n%225 = OpAccessChain  %62  %19 %27 %55 %33\nOpStore %225 %224\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-image-restrict-depth.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 106\nOpCapability Shader\nOpCapability ImageQuery\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %91 \"fragment_shader\" %89\nOpExecutionMode %91 OriginUpperLeft\n%3 = OpString \"bounds-check-image-restrict-depth.wgsl\"\nOpSource Unknown 0 %3 \"// Cases from bounds-check-image-restrict that GLSL does not yet support.\n\n@group(0) @binding(0)\nvar image_depth_2d: texture_depth_2d;\n\nfn test_textureLoad_depth_2d(coords: vec2<i32>, level: i32) -> f32 {\n   return textureLoad(image_depth_2d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_depth_2d_array: texture_depth_2d_array;\n\nfn test_textureLoad_depth_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_depth_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\n@group(0) @binding(2)\nvar image_depth_multisampled_2d: texture_depth_multisampled_2d;\n\nfn test_textureLoad_depth_multisampled_2d(coords: vec2<i32>, _sample: i32) -> f32 {\n   return textureLoad(image_depth_multisampled_2d, coords, _sample);\n}\n\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_depth_2d(vec2<i32>(), 0);\n    test_textureLoad_depth_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_depth_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_depth_multisampled_2d(vec2<i32>(), 0);\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n\"\nOpName %12 \"image_depth_2d\"\nOpName %14 \"image_depth_2d_array\"\nOpName %16 \"image_depth_multisampled_2d\"\nOpName %19 \"coords\"\nOpName %20 \"level\"\nOpName %21 \"test_textureLoad_depth_2d\"\nOpName %36 \"coords\"\nOpName %37 \"index\"\nOpName %38 \"level\"\nOpName %39 \"test_textureLoad_depth_2d_array_u\"\nOpName %56 \"coords\"\nOpName %57 \"index\"\nOpName %58 \"level\"\nOpName %59 \"test_textureLoad_depth_2d_array_s\"\nOpName %74 \"coords\"\nOpName %75 \"_sample\"\nOpName %76 \"test_textureLoad_depth_multisampled_2d\"\nOpName %91 \"fragment_shader\"\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 1\nOpDecorate %16 DescriptorSet 0\nOpDecorate %16 Binding 2\nOpDecorate %89 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeImage %5 2D 1 0 0 1 Unknown\n%6 = OpTypeInt 32 1\n%7 = OpTypeVector %6 2\n%8 = OpTypeImage %5 2D 1 1 0 1 Unknown\n%9 = OpTypeInt 32 0\n%10 = OpTypeImage %5 2D 1 0 1 1 Unknown\n%11 = OpTypeVector %5 4\n%13 = OpTypePointer UniformConstant %4\n%12 = OpVariable  %13  UniformConstant\n%15 = OpTypePointer UniformConstant %8\n%14 = OpVariable  %15  UniformConstant\n%17 = OpTypePointer UniformConstant %10\n%16 = OpVariable  %17  UniformConstant\n%22 = OpTypeFunction %5 %7 %6\n%26 = OpConstant  %6  1\n%30 = OpConstantComposite  %7  %26 %26\n%40 = OpTypeFunction %5 %7 %9 %6\n%44 = OpTypeVector %6 3\n%50 = OpConstantComposite  %44  %26 %26 %26\n%60 = OpTypeFunction %5 %7 %6 %6\n%68 = OpConstantComposite  %44  %26 %26 %26\n%83 = OpConstantComposite  %7  %26 %26\n%90 = OpTypePointer Output %11\n%89 = OpVariable  %90  Output\n%92 = OpTypeFunction %2\n%96 = OpConstantNull  %7\n%97 = OpConstant  %6  0\n%98 = OpConstant  %9  0\n%99 = OpConstant  %5  0\n%100 = OpConstantComposite  %11  %99 %99 %99 %99\n%21 = OpFunction  %5  None %22\n%19 = OpFunctionParameter  %7\n%20 = OpFunctionParameter  %6\n%18 = OpLabel\n%23 = OpLoad  %4  %12\nOpBranch %24\n%24 = OpLabel\nOpLine %3 7 11\n%25 = OpImageQueryLevels  %6  %23\n%27 = OpISub  %6  %25 %26\n%28 = OpExtInst  %6  %1 UMin %20 %27\n%29 = OpImageQuerySizeLod  %7  %23 %28\n%31 = OpISub  %7  %29 %30\n%32 = OpExtInst  %7  %1 UMin %19 %31\n%33 = OpImageFetch  %11  %23 %32 Lod %28\n%34 = OpCompositeExtract  %5  %33 0\nOpReturnValue %34\nOpFunctionEnd\n%39 = OpFunction  %5  None %40\n%36 = OpFunctionParameter  %7\n%37 = OpFunctionParameter  %9\n%38 = OpFunctionParameter  %6\n%35 = OpLabel\n%41 = OpLoad  %8  %14\nOpBranch %42\n%42 = OpLabel\nOpLine %3 14 11\n%43 = OpBitcast  %6  %37\n%45 = OpCompositeConstruct  %44  %36 %43\n%46 = OpImageQueryLevels  %6  %41\n%47 = OpISub  %6  %46 %26\n%48 = OpExtInst  %6  %1 UMin %38 %47\n%49 = OpImageQuerySizeLod  %44  %41 %48\n%51 = OpISub  %44  %49 %50\n%52 = OpExtInst  %44  %1 UMin %45 %51\n%53 = OpImageFetch  %11  %41 %52 Lod %48\n%54 = OpCompositeExtract  %5  %53 0\nOpReturnValue %54\nOpFunctionEnd\n%59 = OpFunction  %5  None %60\n%56 = OpFunctionParameter  %7\n%57 = OpFunctionParameter  %6\n%58 = OpFunctionParameter  %6\n%55 = OpLabel\n%61 = OpLoad  %8  %14\nOpBranch %62\n%62 = OpLabel\nOpLine %3 18 11\n%63 = OpCompositeConstruct  %44  %56 %57\n%64 = OpImageQueryLevels  %6  %61\n%65 = OpISub  %6  %64 %26\n%66 = OpExtInst  %6  %1 UMin %58 %65\n%67 = OpImageQuerySizeLod  %44  %61 %66\n%69 = OpISub  %44  %67 %68\n%70 = OpExtInst  %44  %1 UMin %63 %69\n%71 = OpImageFetch  %11  %61 %70 Lod %66\n%72 = OpCompositeExtract  %5  %71 0\nOpReturnValue %72\nOpFunctionEnd\n%76 = OpFunction  %5  None %22\n%74 = OpFunctionParameter  %7\n%75 = OpFunctionParameter  %6\n%73 = OpLabel\n%77 = OpLoad  %10  %16\nOpBranch %78\n%78 = OpLabel\nOpLine %3 25 11\n%79 = OpImageQuerySamples  %6  %77\n%80 = OpISub  %6  %79 %26\n%81 = OpExtInst  %6  %1 UMin %75 %80\n%82 = OpImageQuerySize  %7  %77\n%84 = OpISub  %7  %82 %83\n%85 = OpExtInst  %7  %1 UMin %74 %84\n%86 = OpImageFetch  %11  %77 %85 Sample %81\n%87 = OpCompositeExtract  %5  %86 0\nOpReturnValue %87\nOpFunctionEnd\n%91 = OpFunction  %2  None %92\n%88 = OpLabel\n%93 = OpLoad  %4  %12\n%94 = OpLoad  %8  %14\n%95 = OpLoad  %10  %16\nOpBranch %101\n%101 = OpLabel\nOpLine %3 30 5\n%102 = OpFunctionCall  %5  %21 %96 %97\nOpLine %3 31 5\n%103 = OpFunctionCall  %5  %39 %96 %98 %97\nOpLine %3 32 5\n%104 = OpFunctionCall  %5  %59 %96 %97 %97\nOpLine %3 33 5\n%105 = OpFunctionCall  %5  %76 %96 %97\nOpLine %3 35 12\nOpStore %89 %100\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-image-restrict.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 204\nOpCapability Shader\nOpCapability Sampled1D\nOpCapability Image1D\nOpCapability ImageQuery\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %174 \"fragment_shader\" %172\nOpExecutionMode %174 OriginUpperLeft\n%3 = OpString \"bounds-check-image-restrict.wgsl\"\nOpSource Unknown 0 %3 \"@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n\nfn test_textureLoad_1d(coords: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_1d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n\nfn test_textureLoad_2d(coords: vec2<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d, coords, level);\n}\n\n@group(0) @binding(2)\nvar image_2d_array: texture_2d_array<f32>;\n\nfn test_textureLoad_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\n@group(0) @binding(3)\nvar image_3d: texture_3d<f32>;\n\nfn test_textureLoad_3d(coords: vec3<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_3d, coords, level);\n}\n\n@group(0) @binding(4)\nvar image_multisampled_2d: texture_multisampled_2d<f32>;\n\nfn test_textureLoad_multisampled_2d(coords: vec2<i32>, _sample: i32) -> vec4<f32> {\n   return textureLoad(image_multisampled_2d, coords, _sample);\n}\n\n@group(0) @binding(5)\nvar image_storage_1d: texture_storage_1d<rgba8unorm, write>;\n\nfn test_textureStore_1d(coords: i32, value: vec4<f32>) {\n    textureStore(image_storage_1d, coords, value);\n}\n\n@group(0) @binding(6)\nvar image_storage_2d: texture_storage_2d<rgba8unorm, write>;\n\nfn test_textureStore_2d(coords: vec2<i32>, value: vec4<f32>) {\n    textureStore(image_storage_2d, coords, value);\n}\n\n@group(0) @binding(7)\nvar image_storage_2d_array: texture_storage_2d_array<rgba8unorm, write>;\n\nfn test_textureStore_2d_array_u(coords: vec2<i32>, array_index: u32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\nfn test_textureStore_2d_array_s(coords: vec2<i32>, array_index: i32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\n@group(0) @binding(8)\nvar image_storage_3d: texture_storage_3d<rgba8unorm, write>;\n\nfn test_textureStore_3d(coords: vec3<i32>, value: vec4<f32>) {\n    textureStore(image_storage_3d, coords, value);\n}\n\n// GLSL output requires that we identify an entry point, so\n// that it can tell what \\\"in\\\" and \\\"out\\\" globals to write.\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_1d(0, 0);\n    test_textureLoad_2d(vec2<i32>(), 0);\n    test_textureLoad_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_3d(vec3<i32>(), 0);\n    test_textureLoad_multisampled_2d(vec2<i32>(), 0);\n    test_textureStore_1d(0, vec4<f32>());\n    test_textureStore_2d(vec2<i32>(), vec4<f32>());\n    test_textureStore_2d_array_u(vec2<i32>(), 0u, vec4<f32>());\n    test_textureStore_2d_array_s(vec2<i32>(), 0, vec4<f32>());\n    test_textureStore_3d(vec3<i32>(), vec4<f32>());\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n\"\nOpName %19 \"image_1d\"\nOpName %21 \"image_2d\"\nOpName %23 \"image_2d_array\"\nOpName %25 \"image_3d\"\nOpName %27 \"image_multisampled_2d\"\nOpName %29 \"image_storage_1d\"\nOpName %31 \"image_storage_2d\"\nOpName %33 \"image_storage_2d_array\"\nOpName %35 \"image_storage_3d\"\nOpName %38 \"coords\"\nOpName %39 \"level\"\nOpName %40 \"test_textureLoad_1d\"\nOpName %53 \"coords\"\nOpName %54 \"level\"\nOpName %55 \"test_textureLoad_2d\"\nOpName %68 \"coords\"\nOpName %69 \"index\"\nOpName %70 \"level\"\nOpName %71 \"test_textureLoad_2d_array_u\"\nOpName %86 \"coords\"\nOpName %87 \"index\"\nOpName %88 \"level\"\nOpName %89 \"test_textureLoad_2d_array_s\"\nOpName %103 \"coords\"\nOpName %104 \"level\"\nOpName %105 \"test_textureLoad_3d\"\nOpName %118 \"coords\"\nOpName %119 \"_sample\"\nOpName %120 \"test_textureLoad_multisampled_2d\"\nOpName %132 \"coords\"\nOpName %133 \"value\"\nOpName %134 \"test_textureStore_1d\"\nOpName %139 \"coords\"\nOpName %140 \"value\"\nOpName %141 \"test_textureStore_2d\"\nOpName %146 \"coords\"\nOpName %147 \"array_index\"\nOpName %148 \"value\"\nOpName %149 \"test_textureStore_2d_array_u\"\nOpName %156 \"coords\"\nOpName %157 \"array_index\"\nOpName %158 \"value\"\nOpName %159 \"test_textureStore_2d_array_s\"\nOpName %165 \"coords\"\nOpName %166 \"value\"\nOpName %167 \"test_textureStore_3d\"\nOpName %174 \"fragment_shader\"\nOpDecorate %19 DescriptorSet 0\nOpDecorate %19 Binding 0\nOpDecorate %21 DescriptorSet 0\nOpDecorate %21 Binding 1\nOpDecorate %23 DescriptorSet 0\nOpDecorate %23 Binding 2\nOpDecorate %25 DescriptorSet 0\nOpDecorate %25 Binding 3\nOpDecorate %27 DescriptorSet 0\nOpDecorate %27 Binding 4\nOpDecorate %29 NonReadable\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 5\nOpDecorate %31 NonReadable\nOpDecorate %31 DescriptorSet 0\nOpDecorate %31 Binding 6\nOpDecorate %33 NonReadable\nOpDecorate %33 DescriptorSet 0\nOpDecorate %33 Binding 7\nOpDecorate %35 NonReadable\nOpDecorate %35 DescriptorSet 0\nOpDecorate %35 Binding 8\nOpDecorate %172 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeImage %5 1D 0 0 0 1 Unknown\n%6 = OpTypeInt 32 1\n%7 = OpTypeVector %5 4\n%8 = OpTypeImage %5 2D 0 0 0 1 Unknown\n%9 = OpTypeVector %6 2\n%10 = OpTypeImage %5 2D 0 1 0 1 Unknown\n%11 = OpTypeInt 32 0\n%12 = OpTypeImage %5 3D 0 0 0 1 Unknown\n%13 = OpTypeVector %6 3\n%14 = OpTypeImage %5 2D 0 0 1 1 Unknown\n%15 = OpTypeImage %5 1D 0 0 0 2 Rgba8\n%16 = OpTypeImage %5 2D 0 0 0 2 Rgba8\n%17 = OpTypeImage %5 2D 0 1 0 2 Rgba8\n%18 = OpTypeImage %5 3D 0 0 0 2 Rgba8\n%20 = OpTypePointer UniformConstant %4\n%19 = OpVariable  %20  UniformConstant\n%22 = OpTypePointer UniformConstant %8\n%21 = OpVariable  %22  UniformConstant\n%24 = OpTypePointer UniformConstant %10\n%23 = OpVariable  %24  UniformConstant\n%26 = OpTypePointer UniformConstant %12\n%25 = OpVariable  %26  UniformConstant\n%28 = OpTypePointer UniformConstant %14\n%27 = OpVariable  %28  UniformConstant\n%30 = OpTypePointer UniformConstant %15\n%29 = OpVariable  %30  UniformConstant\n%32 = OpTypePointer UniformConstant %16\n%31 = OpVariable  %32  UniformConstant\n%34 = OpTypePointer UniformConstant %17\n%33 = OpVariable  %34  UniformConstant\n%36 = OpTypePointer UniformConstant %18\n%35 = OpVariable  %36  UniformConstant\n%41 = OpTypeFunction %7 %6 %6\n%45 = OpConstant  %6  1\n%56 = OpTypeFunction %7 %9 %6\n%63 = OpConstantComposite  %9  %45 %45\n%72 = OpTypeFunction %7 %9 %11 %6\n%81 = OpConstantComposite  %13  %45 %45 %45\n%90 = OpTypeFunction %7 %9 %6 %6\n%98 = OpConstantComposite  %13  %45 %45 %45\n%106 = OpTypeFunction %7 %13 %6\n%113 = OpConstantComposite  %13  %45 %45 %45\n%127 = OpConstantComposite  %9  %45 %45\n%135 = OpTypeFunction %2 %6 %7\n%142 = OpTypeFunction %2 %9 %7\n%150 = OpTypeFunction %2 %9 %11 %7\n%160 = OpTypeFunction %2 %9 %6 %7\n%168 = OpTypeFunction %2 %13 %7\n%173 = OpTypePointer Output %7\n%172 = OpVariable  %173  Output\n%175 = OpTypeFunction %2\n%185 = OpConstant  %6  0\n%186 = OpConstantNull  %9\n%187 = OpConstant  %11  0\n%188 = OpConstantNull  %13\n%189 = OpConstantNull  %7\n%190 = OpConstant  %5  0\n%191 = OpConstantComposite  %7  %190 %190 %190 %190\n%40 = OpFunction  %7  None %41\n%38 = OpFunctionParameter  %6\n%39 = OpFunctionParameter  %6\n%37 = OpLabel\n%42 = OpLoad  %4  %19\nOpBranch %43\n%43 = OpLabel\nOpLine %3 5 11\n%44 = OpImageQueryLevels  %6  %42\n%46 = OpISub  %6  %44 %45\n%47 = OpExtInst  %6  %1 UMin %39 %46\n%48 = OpImageQuerySizeLod  %6  %42 %47\n%49 = OpISub  %6  %48 %45\n%50 = OpExtInst  %6  %1 UMin %38 %49\n%51 = OpImageFetch  %7  %42 %50 Lod %47\nOpReturnValue %51\nOpFunctionEnd\n%55 = OpFunction  %7  None %56\n%53 = OpFunctionParameter  %9\n%54 = OpFunctionParameter  %6\n%52 = OpLabel\n%57 = OpLoad  %8  %21\nOpBranch %58\n%58 = OpLabel\nOpLine %3 12 11\n%59 = OpImageQueryLevels  %6  %57\n%60 = OpISub  %6  %59 %45\n%61 = OpExtInst  %6  %1 UMin %54 %60\n%62 = OpImageQuerySizeLod  %9  %57 %61\n%64 = OpISub  %9  %62 %63\n%65 = OpExtInst  %9  %1 UMin %53 %64\n%66 = OpImageFetch  %7  %57 %65 Lod %61\nOpReturnValue %66\nOpFunctionEnd\n%71 = OpFunction  %7  None %72\n%68 = OpFunctionParameter  %9\n%69 = OpFunctionParameter  %11\n%70 = OpFunctionParameter  %6\n%67 = OpLabel\n%73 = OpLoad  %10  %23\nOpBranch %74\n%74 = OpLabel\nOpLine %3 19 11\n%75 = OpBitcast  %6  %69\n%76 = OpCompositeConstruct  %13  %68 %75\n%77 = OpImageQueryLevels  %6  %73\n%78 = OpISub  %6  %77 %45\n%79 = OpExtInst  %6  %1 UMin %70 %78\n%80 = OpImageQuerySizeLod  %13  %73 %79\n%82 = OpISub  %13  %80 %81\n%83 = OpExtInst  %13  %1 UMin %76 %82\n%84 = OpImageFetch  %7  %73 %83 Lod %79\nOpReturnValue %84\nOpFunctionEnd\n%89 = OpFunction  %7  None %90\n%86 = OpFunctionParameter  %9\n%87 = OpFunctionParameter  %6\n%88 = OpFunctionParameter  %6\n%85 = OpLabel\n%91 = OpLoad  %10  %23\nOpBranch %92\n%92 = OpLabel\nOpLine %3 23 11\n%93 = OpCompositeConstruct  %13  %86 %87\n%94 = OpImageQueryLevels  %6  %91\n%95 = OpISub  %6  %94 %45\n%96 = OpExtInst  %6  %1 UMin %88 %95\n%97 = OpImageQuerySizeLod  %13  %91 %96\n%99 = OpISub  %13  %97 %98\n%100 = OpExtInst  %13  %1 UMin %93 %99\n%101 = OpImageFetch  %7  %91 %100 Lod %96\nOpReturnValue %101\nOpFunctionEnd\n%105 = OpFunction  %7  None %106\n%103 = OpFunctionParameter  %13\n%104 = OpFunctionParameter  %6\n%102 = OpLabel\n%107 = OpLoad  %12  %25\nOpBranch %108\n%108 = OpLabel\nOpLine %3 30 11\n%109 = OpImageQueryLevels  %6  %107\n%110 = OpISub  %6  %109 %45\n%111 = OpExtInst  %6  %1 UMin %104 %110\n%112 = OpImageQuerySizeLod  %13  %107 %111\n%114 = OpISub  %13  %112 %113\n%115 = OpExtInst  %13  %1 UMin %103 %114\n%116 = OpImageFetch  %7  %107 %115 Lod %111\nOpReturnValue %116\nOpFunctionEnd\n%120 = OpFunction  %7  None %56\n%118 = OpFunctionParameter  %9\n%119 = OpFunctionParameter  %6\n%117 = OpLabel\n%121 = OpLoad  %14  %27\nOpBranch %122\n%122 = OpLabel\nOpLine %3 37 11\n%123 = OpImageQuerySamples  %6  %121\n%124 = OpISub  %6  %123 %45\n%125 = OpExtInst  %6  %1 UMin %119 %124\n%126 = OpImageQuerySize  %9  %121\n%128 = OpISub  %9  %126 %127\n%129 = OpExtInst  %9  %1 UMin %118 %128\n%130 = OpImageFetch  %7  %121 %129 Sample %125\nOpReturnValue %130\nOpFunctionEnd\n%134 = OpFunction  %2  None %135\n%132 = OpFunctionParameter  %6\n%133 = OpFunctionParameter  %7\n%131 = OpLabel\n%136 = OpLoad  %15  %29\nOpBranch %137\n%137 = OpLabel\nOpLine %3 44 5\nOpImageWrite %136 %132 %133\nOpReturn\nOpFunctionEnd\n%141 = OpFunction  %2  None %142\n%139 = OpFunctionParameter  %9\n%140 = OpFunctionParameter  %7\n%138 = OpLabel\n%143 = OpLoad  %16  %31\nOpBranch %144\n%144 = OpLabel\nOpLine %3 51 5\nOpImageWrite %143 %139 %140\nOpReturn\nOpFunctionEnd\n%149 = OpFunction  %2  None %150\n%146 = OpFunctionParameter  %9\n%147 = OpFunctionParameter  %11\n%148 = OpFunctionParameter  %7\n%145 = OpLabel\n%151 = OpLoad  %17  %33\nOpBranch %152\n%152 = OpLabel\nOpLine %3 58 2\n%153 = OpBitcast  %6  %147\n%154 = OpCompositeConstruct  %13  %146 %153\nOpImageWrite %151 %154 %148\nOpReturn\nOpFunctionEnd\n%159 = OpFunction  %2  None %160\n%156 = OpFunctionParameter  %9\n%157 = OpFunctionParameter  %6\n%158 = OpFunctionParameter  %7\n%155 = OpLabel\n%161 = OpLoad  %17  %33\nOpBranch %162\n%162 = OpLabel\nOpLine %3 62 2\n%163 = OpCompositeConstruct  %13  %156 %157\nOpImageWrite %161 %163 %158\nOpReturn\nOpFunctionEnd\n%167 = OpFunction  %2  None %168\n%165 = OpFunctionParameter  %13\n%166 = OpFunctionParameter  %7\n%164 = OpLabel\n%169 = OpLoad  %18  %35\nOpBranch %170\n%170 = OpLabel\nOpLine %3 69 5\nOpImageWrite %169 %165 %166\nOpReturn\nOpFunctionEnd\n%174 = OpFunction  %2  None %175\n%171 = OpLabel\n%176 = OpLoad  %4  %19\n%177 = OpLoad  %8  %21\n%178 = OpLoad  %10  %23\n%179 = OpLoad  %12  %25\n%180 = OpLoad  %14  %27\n%181 = OpLoad  %15  %29\n%182 = OpLoad  %16  %31\n%183 = OpLoad  %17  %33\n%184 = OpLoad  %18  %35\nOpBranch %192\n%192 = OpLabel\nOpLine %3 76 5\n%193 = OpFunctionCall  %7  %40 %185 %185\nOpLine %3 77 5\n%194 = OpFunctionCall  %7  %55 %186 %185\nOpLine %3 78 5\n%195 = OpFunctionCall  %7  %71 %186 %187 %185\nOpLine %3 79 5\n%196 = OpFunctionCall  %7  %89 %186 %185 %185\nOpLine %3 80 5\n%197 = OpFunctionCall  %7  %105 %188 %185\nOpLine %3 81 5\n%198 = OpFunctionCall  %7  %120 %186 %185\nOpLine %3 82 5\n%199 = OpFunctionCall  %2  %134 %185 %189\nOpLine %3 83 5\n%200 = OpFunctionCall  %2  %141 %186 %189\nOpLine %3 84 5\n%201 = OpFunctionCall  %2  %149 %186 %187 %189\nOpLine %3 85 5\n%202 = OpFunctionCall  %2  %159 %186 %185 %189\nOpLine %3 86 5\n%203 = OpFunctionCall  %2  %167 %188 %189\nOpLine %3 88 12\nOpStore %172 %191\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-image-rzsw-depth.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 117\nOpCapability Shader\nOpCapability ImageQuery\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %102 \"fragment_shader\" %100\nOpExecutionMode %102 OriginUpperLeft\n%3 = OpString \"bounds-check-image-rzsw-depth.wgsl\"\nOpSource Unknown 0 %3 \"// Cases from bounds-check-image-restrict that GLSL does not yet support.\n\n@group(0) @binding(0)\nvar image_depth_2d: texture_depth_2d;\n\nfn test_textureLoad_depth_2d(coords: vec2<i32>, level: i32) -> f32 {\n   return textureLoad(image_depth_2d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_depth_2d_array: texture_depth_2d_array;\n\nfn test_textureLoad_depth_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_depth_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> f32 {\n   return textureLoad(image_depth_2d_array, coords, index, level);\n}\n\n@group(0) @binding(2)\nvar image_depth_multisampled_2d: texture_depth_multisampled_2d;\n\nfn test_textureLoad_depth_multisampled_2d(coords: vec2<i32>, _sample: i32) -> f32 {\n   return textureLoad(image_depth_multisampled_2d, coords, _sample);\n}\n\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_depth_2d(vec2<i32>(), 0);\n    test_textureLoad_depth_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_depth_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_depth_multisampled_2d(vec2<i32>(), 0);\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n\"\nOpName %12 \"image_depth_2d\"\nOpName %14 \"image_depth_2d_array\"\nOpName %16 \"image_depth_multisampled_2d\"\nOpName %19 \"coords\"\nOpName %20 \"level\"\nOpName %21 \"test_textureLoad_depth_2d\"\nOpName %40 \"coords\"\nOpName %41 \"index\"\nOpName %42 \"level\"\nOpName %43 \"test_textureLoad_depth_2d_array_u\"\nOpName %63 \"coords\"\nOpName %64 \"index\"\nOpName %65 \"level\"\nOpName %66 \"test_textureLoad_depth_2d_array_s\"\nOpName %83 \"coords\"\nOpName %84 \"_sample\"\nOpName %85 \"test_textureLoad_depth_multisampled_2d\"\nOpName %102 \"fragment_shader\"\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 1\nOpDecorate %16 DescriptorSet 0\nOpDecorate %16 Binding 2\nOpDecorate %100 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeImage %5 2D 1 0 0 1 Unknown\n%6 = OpTypeInt 32 1\n%7 = OpTypeVector %6 2\n%8 = OpTypeImage %5 2D 1 1 0 1 Unknown\n%9 = OpTypeInt 32 0\n%10 = OpTypeImage %5 2D 1 0 1 1 Unknown\n%11 = OpTypeVector %5 4\n%13 = OpTypePointer UniformConstant %4\n%12 = OpVariable  %13  UniformConstant\n%15 = OpTypePointer UniformConstant %8\n%14 = OpVariable  %15  UniformConstant\n%17 = OpTypePointer UniformConstant %10\n%16 = OpVariable  %17  UniformConstant\n%22 = OpTypeFunction %5 %7 %6\n%25 = OpTypeBool\n%26 = OpConstantNull  %11\n%32 = OpTypeVector %25 2\n%44 = OpTypeFunction %5 %7 %9 %6\n%48 = OpTypeVector %6 3\n%55 = OpTypeVector %25 3\n%67 = OpTypeFunction %5 %7 %6 %6\n%101 = OpTypePointer Output %11\n%100 = OpVariable  %101  Output\n%103 = OpTypeFunction %2\n%107 = OpConstantNull  %7\n%108 = OpConstant  %6  0\n%109 = OpConstant  %9  0\n%110 = OpConstant  %5  0\n%111 = OpConstantComposite  %11  %110 %110 %110 %110\n%21 = OpFunction  %5  None %22\n%19 = OpFunctionParameter  %7\n%20 = OpFunctionParameter  %6\n%18 = OpLabel\n%23 = OpLoad  %4  %12\nOpBranch %24\n%24 = OpLabel\nOpLine %3 7 11\n%27 = OpImageQueryLevels  %6  %23\n%28 = OpULessThan  %25  %20 %27\nOpSelectionMerge %29 None\nOpBranchConditional %28 %30 %29\n%30 = OpLabel\n%31 = OpImageQuerySizeLod  %7  %23 %20\n%33 = OpULessThan  %32  %19 %31\n%34 = OpAll  %25  %33\nOpBranchConditional %34 %35 %29\n%35 = OpLabel\n%36 = OpImageFetch  %11  %23 %19 Lod %20\nOpBranch %29\n%29 = OpLabel\n%37 = OpPhi  %11  %26 %24 %26 %30 %36 %35\n%38 = OpCompositeExtract  %5  %37 0\nOpReturnValue %38\nOpFunctionEnd\n%43 = OpFunction  %5  None %44\n%40 = OpFunctionParameter  %7\n%41 = OpFunctionParameter  %9\n%42 = OpFunctionParameter  %6\n%39 = OpLabel\n%45 = OpLoad  %8  %14\nOpBranch %46\n%46 = OpLabel\nOpLine %3 14 11\n%47 = OpBitcast  %6  %41\n%49 = OpCompositeConstruct  %48  %40 %47\n%50 = OpImageQueryLevels  %6  %45\n%51 = OpULessThan  %25  %42 %50\nOpSelectionMerge %52 None\nOpBranchConditional %51 %53 %52\n%53 = OpLabel\n%54 = OpImageQuerySizeLod  %48  %45 %42\n%56 = OpULessThan  %55  %49 %54\n%57 = OpAll  %25  %56\nOpBranchConditional %57 %58 %52\n%58 = OpLabel\n%59 = OpImageFetch  %11  %45 %49 Lod %42\nOpBranch %52\n%52 = OpLabel\n%60 = OpPhi  %11  %26 %46 %26 %53 %59 %58\n%61 = OpCompositeExtract  %5  %60 0\nOpReturnValue %61\nOpFunctionEnd\n%66 = OpFunction  %5  None %67\n%63 = OpFunctionParameter  %7\n%64 = OpFunctionParameter  %6\n%65 = OpFunctionParameter  %6\n%62 = OpLabel\n%68 = OpLoad  %8  %14\nOpBranch %69\n%69 = OpLabel\nOpLine %3 18 11\n%70 = OpCompositeConstruct  %48  %63 %64\n%71 = OpImageQueryLevels  %6  %68\n%72 = OpULessThan  %25  %65 %71\nOpSelectionMerge %73 None\nOpBranchConditional %72 %74 %73\n%74 = OpLabel\n%75 = OpImageQuerySizeLod  %48  %68 %65\n%76 = OpULessThan  %55  %70 %75\n%77 = OpAll  %25  %76\nOpBranchConditional %77 %78 %73\n%78 = OpLabel\n%79 = OpImageFetch  %11  %68 %70 Lod %65\nOpBranch %73\n%73 = OpLabel\n%80 = OpPhi  %11  %26 %69 %26 %74 %79 %78\n%81 = OpCompositeExtract  %5  %80 0\nOpReturnValue %81\nOpFunctionEnd\n%85 = OpFunction  %5  None %22\n%83 = OpFunctionParameter  %7\n%84 = OpFunctionParameter  %6\n%82 = OpLabel\n%86 = OpLoad  %10  %16\nOpBranch %87\n%87 = OpLabel\nOpLine %3 25 11\n%88 = OpImageQuerySamples  %6  %86\n%89 = OpULessThan  %25  %84 %88\nOpSelectionMerge %90 None\nOpBranchConditional %89 %91 %90\n%91 = OpLabel\n%92 = OpImageQuerySize  %7  %86\n%93 = OpULessThan  %32  %83 %92\n%94 = OpAll  %25  %93\nOpBranchConditional %94 %95 %90\n%95 = OpLabel\n%96 = OpImageFetch  %11  %86 %83 Sample %84\nOpBranch %90\n%90 = OpLabel\n%97 = OpPhi  %11  %26 %87 %26 %91 %96 %95\n%98 = OpCompositeExtract  %5  %97 0\nOpReturnValue %98\nOpFunctionEnd\n%102 = OpFunction  %2  None %103\n%99 = OpLabel\n%104 = OpLoad  %4  %12\n%105 = OpLoad  %8  %14\n%106 = OpLoad  %10  %16\nOpBranch %112\n%112 = OpLabel\nOpLine %3 30 5\n%113 = OpFunctionCall  %5  %21 %107 %108\nOpLine %3 31 5\n%114 = OpFunctionCall  %5  %43 %107 %109 %108\nOpLine %3 32 5\n%115 = OpFunctionCall  %5  %66 %107 %108 %108\nOpLine %3 33 5\n%116 = OpFunctionCall  %5  %85 %107 %108\nOpLine %3 35 12\nOpStore %100 %111\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-image-rzsw.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 218\nOpCapability Shader\nOpCapability Sampled1D\nOpCapability Image1D\nOpCapability ImageQuery\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %189 \"fragment_shader\" %187\nOpExecutionMode %189 OriginUpperLeft\n%3 = OpString \"bounds-check-image-rzsw.wgsl\"\nOpSource Unknown 0 %3 \"@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n\nfn test_textureLoad_1d(coords: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_1d, coords, level);\n}\n\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n\nfn test_textureLoad_2d(coords: vec2<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d, coords, level);\n}\n\n@group(0) @binding(2)\nvar image_2d_array: texture_2d_array<f32>;\n\nfn test_textureLoad_2d_array_u(coords: vec2<i32>, index: u32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\nfn test_textureLoad_2d_array_s(coords: vec2<i32>, index: i32, level: i32) -> vec4<f32> {\n   return textureLoad(image_2d_array, coords, index, level);\n}\n\n@group(0) @binding(3)\nvar image_3d: texture_3d<f32>;\n\nfn test_textureLoad_3d(coords: vec3<i32>, level: i32) -> vec4<f32> {\n   return textureLoad(image_3d, coords, level);\n}\n\n@group(0) @binding(4)\nvar image_multisampled_2d: texture_multisampled_2d<f32>;\n\nfn test_textureLoad_multisampled_2d(coords: vec2<i32>, _sample: i32) -> vec4<f32> {\n   return textureLoad(image_multisampled_2d, coords, _sample);\n}\n\n@group(0) @binding(5)\nvar image_storage_1d: texture_storage_1d<rgba8unorm, write>;\n\nfn test_textureStore_1d(coords: i32, value: vec4<f32>) {\n    textureStore(image_storage_1d, coords, value);\n}\n\n@group(0) @binding(6)\nvar image_storage_2d: texture_storage_2d<rgba8unorm, write>;\n\nfn test_textureStore_2d(coords: vec2<i32>, value: vec4<f32>) {\n    textureStore(image_storage_2d, coords, value);\n}\n\n@group(0) @binding(7)\nvar image_storage_2d_array: texture_storage_2d_array<rgba8unorm, write>;\n\nfn test_textureStore_2d_array_u(coords: vec2<i32>, array_index: u32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\nfn test_textureStore_2d_array_s(coords: vec2<i32>, array_index: i32, value: vec4<f32>) {\n textureStore(image_storage_2d_array, coords, array_index, value);\n}\n\n@group(0) @binding(8)\nvar image_storage_3d: texture_storage_3d<rgba8unorm, write>;\n\nfn test_textureStore_3d(coords: vec3<i32>, value: vec4<f32>) {\n    textureStore(image_storage_3d, coords, value);\n}\n\n// GLSL output requires that we identify an entry point, so\n// that it can tell what \\\"in\\\" and \\\"out\\\" globals to write.\n@fragment\nfn fragment_shader() -> @location(0) vec4<f32> {\n    test_textureLoad_1d(0, 0);\n    test_textureLoad_2d(vec2<i32>(), 0);\n    test_textureLoad_2d_array_u(vec2<i32>(), 0u, 0);\n    test_textureLoad_2d_array_s(vec2<i32>(), 0, 0);\n    test_textureLoad_3d(vec3<i32>(), 0);\n    test_textureLoad_multisampled_2d(vec2<i32>(), 0);\n    test_textureStore_1d(0, vec4<f32>());\n    test_textureStore_2d(vec2<i32>(), vec4<f32>());\n    test_textureStore_2d_array_u(vec2<i32>(), 0u, vec4<f32>());\n    test_textureStore_2d_array_s(vec2<i32>(), 0, vec4<f32>());\n    test_textureStore_3d(vec3<i32>(), vec4<f32>());\n\n    return vec4<f32>(0.,0.,0.,0.);\n}\n\"\nOpName %19 \"image_1d\"\nOpName %21 \"image_2d\"\nOpName %23 \"image_2d_array\"\nOpName %25 \"image_3d\"\nOpName %27 \"image_multisampled_2d\"\nOpName %29 \"image_storage_1d\"\nOpName %31 \"image_storage_2d\"\nOpName %33 \"image_storage_2d_array\"\nOpName %35 \"image_storage_3d\"\nOpName %38 \"coords\"\nOpName %39 \"level\"\nOpName %40 \"test_textureLoad_1d\"\nOpName %56 \"coords\"\nOpName %57 \"level\"\nOpName %58 \"test_textureLoad_2d\"\nOpName %74 \"coords\"\nOpName %75 \"index\"\nOpName %76 \"level\"\nOpName %77 \"test_textureLoad_2d_array_u\"\nOpName %95 \"coords\"\nOpName %96 \"index\"\nOpName %97 \"level\"\nOpName %98 \"test_textureLoad_2d_array_s\"\nOpName %114 \"coords\"\nOpName %115 \"level\"\nOpName %116 \"test_textureLoad_3d\"\nOpName %131 \"coords\"\nOpName %132 \"_sample\"\nOpName %133 \"test_textureLoad_multisampled_2d\"\nOpName %147 \"coords\"\nOpName %148 \"value\"\nOpName %149 \"test_textureStore_1d\"\nOpName %154 \"coords\"\nOpName %155 \"value\"\nOpName %156 \"test_textureStore_2d\"\nOpName %161 \"coords\"\nOpName %162 \"array_index\"\nOpName %163 \"value\"\nOpName %164 \"test_textureStore_2d_array_u\"\nOpName %171 \"coords\"\nOpName %172 \"array_index\"\nOpName %173 \"value\"\nOpName %174 \"test_textureStore_2d_array_s\"\nOpName %180 \"coords\"\nOpName %181 \"value\"\nOpName %182 \"test_textureStore_3d\"\nOpName %189 \"fragment_shader\"\nOpDecorate %19 DescriptorSet 0\nOpDecorate %19 Binding 0\nOpDecorate %21 DescriptorSet 0\nOpDecorate %21 Binding 1\nOpDecorate %23 DescriptorSet 0\nOpDecorate %23 Binding 2\nOpDecorate %25 DescriptorSet 0\nOpDecorate %25 Binding 3\nOpDecorate %27 DescriptorSet 0\nOpDecorate %27 Binding 4\nOpDecorate %29 NonReadable\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 5\nOpDecorate %31 NonReadable\nOpDecorate %31 DescriptorSet 0\nOpDecorate %31 Binding 6\nOpDecorate %33 NonReadable\nOpDecorate %33 DescriptorSet 0\nOpDecorate %33 Binding 7\nOpDecorate %35 NonReadable\nOpDecorate %35 DescriptorSet 0\nOpDecorate %35 Binding 8\nOpDecorate %187 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeImage %5 1D 0 0 0 1 Unknown\n%6 = OpTypeInt 32 1\n%7 = OpTypeVector %5 4\n%8 = OpTypeImage %5 2D 0 0 0 1 Unknown\n%9 = OpTypeVector %6 2\n%10 = OpTypeImage %5 2D 0 1 0 1 Unknown\n%11 = OpTypeInt 32 0\n%12 = OpTypeImage %5 3D 0 0 0 1 Unknown\n%13 = OpTypeVector %6 3\n%14 = OpTypeImage %5 2D 0 0 1 1 Unknown\n%15 = OpTypeImage %5 1D 0 0 0 2 Rgba8\n%16 = OpTypeImage %5 2D 0 0 0 2 Rgba8\n%17 = OpTypeImage %5 2D 0 1 0 2 Rgba8\n%18 = OpTypeImage %5 3D 0 0 0 2 Rgba8\n%20 = OpTypePointer UniformConstant %4\n%19 = OpVariable  %20  UniformConstant\n%22 = OpTypePointer UniformConstant %8\n%21 = OpVariable  %22  UniformConstant\n%24 = OpTypePointer UniformConstant %10\n%23 = OpVariable  %24  UniformConstant\n%26 = OpTypePointer UniformConstant %12\n%25 = OpVariable  %26  UniformConstant\n%28 = OpTypePointer UniformConstant %14\n%27 = OpVariable  %28  UniformConstant\n%30 = OpTypePointer UniformConstant %15\n%29 = OpVariable  %30  UniformConstant\n%32 = OpTypePointer UniformConstant %16\n%31 = OpVariable  %32  UniformConstant\n%34 = OpTypePointer UniformConstant %17\n%33 = OpVariable  %34  UniformConstant\n%36 = OpTypePointer UniformConstant %18\n%35 = OpVariable  %36  UniformConstant\n%41 = OpTypeFunction %7 %6 %6\n%44 = OpTypeBool\n%45 = OpConstantNull  %7\n%59 = OpTypeFunction %7 %9 %6\n%67 = OpTypeVector %44 2\n%78 = OpTypeFunction %7 %9 %11 %6\n%88 = OpTypeVector %44 3\n%99 = OpTypeFunction %7 %9 %6 %6\n%117 = OpTypeFunction %7 %13 %6\n%150 = OpTypeFunction %2 %6 %7\n%157 = OpTypeFunction %2 %9 %7\n%165 = OpTypeFunction %2 %9 %11 %7\n%175 = OpTypeFunction %2 %9 %6 %7\n%183 = OpTypeFunction %2 %13 %7\n%188 = OpTypePointer Output %7\n%187 = OpVariable  %188  Output\n%190 = OpTypeFunction %2\n%200 = OpConstant  %6  0\n%201 = OpConstantNull  %9\n%202 = OpConstant  %11  0\n%203 = OpConstantNull  %13\n%204 = OpConstant  %5  0\n%205 = OpConstantComposite  %7  %204 %204 %204 %204\n%40 = OpFunction  %7  None %41\n%38 = OpFunctionParameter  %6\n%39 = OpFunctionParameter  %6\n%37 = OpLabel\n%42 = OpLoad  %4  %19\nOpBranch %43\n%43 = OpLabel\nOpLine %3 5 11\n%46 = OpImageQueryLevels  %6  %42\n%47 = OpULessThan  %44  %39 %46\nOpSelectionMerge %48 None\nOpBranchConditional %47 %49 %48\n%49 = OpLabel\n%50 = OpImageQuerySizeLod  %6  %42 %39\n%51 = OpULessThan  %44  %38 %50\nOpBranchConditional %51 %52 %48\n%52 = OpLabel\n%53 = OpImageFetch  %7  %42 %38 Lod %39\nOpBranch %48\n%48 = OpLabel\n%54 = OpPhi  %7  %45 %43 %45 %49 %53 %52\nOpReturnValue %54\nOpFunctionEnd\n%58 = OpFunction  %7  None %59\n%56 = OpFunctionParameter  %9\n%57 = OpFunctionParameter  %6\n%55 = OpLabel\n%60 = OpLoad  %8  %21\nOpBranch %61\n%61 = OpLabel\nOpLine %3 12 11\n%62 = OpImageQueryLevels  %6  %60\n%63 = OpULessThan  %44  %57 %62\nOpSelectionMerge %64 None\nOpBranchConditional %63 %65 %64\n%65 = OpLabel\n%66 = OpImageQuerySizeLod  %9  %60 %57\n%68 = OpULessThan  %67  %56 %66\n%69 = OpAll  %44  %68\nOpBranchConditional %69 %70 %64\n%70 = OpLabel\n%71 = OpImageFetch  %7  %60 %56 Lod %57\nOpBranch %64\n%64 = OpLabel\n%72 = OpPhi  %7  %45 %61 %45 %65 %71 %70\nOpReturnValue %72\nOpFunctionEnd\n%77 = OpFunction  %7  None %78\n%74 = OpFunctionParameter  %9\n%75 = OpFunctionParameter  %11\n%76 = OpFunctionParameter  %6\n%73 = OpLabel\n%79 = OpLoad  %10  %23\nOpBranch %80\n%80 = OpLabel\nOpLine %3 19 11\n%81 = OpBitcast  %6  %75\n%82 = OpCompositeConstruct  %13  %74 %81\n%83 = OpImageQueryLevels  %6  %79\n%84 = OpULessThan  %44  %76 %83\nOpSelectionMerge %85 None\nOpBranchConditional %84 %86 %85\n%86 = OpLabel\n%87 = OpImageQuerySizeLod  %13  %79 %76\n%89 = OpULessThan  %88  %82 %87\n%90 = OpAll  %44  %89\nOpBranchConditional %90 %91 %85\n%91 = OpLabel\n%92 = OpImageFetch  %7  %79 %82 Lod %76\nOpBranch %85\n%85 = OpLabel\n%93 = OpPhi  %7  %45 %80 %45 %86 %92 %91\nOpReturnValue %93\nOpFunctionEnd\n%98 = OpFunction  %7  None %99\n%95 = OpFunctionParameter  %9\n%96 = OpFunctionParameter  %6\n%97 = OpFunctionParameter  %6\n%94 = OpLabel\n%100 = OpLoad  %10  %23\nOpBranch %101\n%101 = OpLabel\nOpLine %3 23 11\n%102 = OpCompositeConstruct  %13  %95 %96\n%103 = OpImageQueryLevels  %6  %100\n%104 = OpULessThan  %44  %97 %103\nOpSelectionMerge %105 None\nOpBranchConditional %104 %106 %105\n%106 = OpLabel\n%107 = OpImageQuerySizeLod  %13  %100 %97\n%108 = OpULessThan  %88  %102 %107\n%109 = OpAll  %44  %108\nOpBranchConditional %109 %110 %105\n%110 = OpLabel\n%111 = OpImageFetch  %7  %100 %102 Lod %97\nOpBranch %105\n%105 = OpLabel\n%112 = OpPhi  %7  %45 %101 %45 %106 %111 %110\nOpReturnValue %112\nOpFunctionEnd\n%116 = OpFunction  %7  None %117\n%114 = OpFunctionParameter  %13\n%115 = OpFunctionParameter  %6\n%113 = OpLabel\n%118 = OpLoad  %12  %25\nOpBranch %119\n%119 = OpLabel\nOpLine %3 30 11\n%120 = OpImageQueryLevels  %6  %118\n%121 = OpULessThan  %44  %115 %120\nOpSelectionMerge %122 None\nOpBranchConditional %121 %123 %122\n%123 = OpLabel\n%124 = OpImageQuerySizeLod  %13  %118 %115\n%125 = OpULessThan  %88  %114 %124\n%126 = OpAll  %44  %125\nOpBranchConditional %126 %127 %122\n%127 = OpLabel\n%128 = OpImageFetch  %7  %118 %114 Lod %115\nOpBranch %122\n%122 = OpLabel\n%129 = OpPhi  %7  %45 %119 %45 %123 %128 %127\nOpReturnValue %129\nOpFunctionEnd\n%133 = OpFunction  %7  None %59\n%131 = OpFunctionParameter  %9\n%132 = OpFunctionParameter  %6\n%130 = OpLabel\n%134 = OpLoad  %14  %27\nOpBranch %135\n%135 = OpLabel\nOpLine %3 37 11\n%136 = OpImageQuerySamples  %6  %134\n%137 = OpULessThan  %44  %132 %136\nOpSelectionMerge %138 None\nOpBranchConditional %137 %139 %138\n%139 = OpLabel\n%140 = OpImageQuerySize  %9  %134\n%141 = OpULessThan  %67  %131 %140\n%142 = OpAll  %44  %141\nOpBranchConditional %142 %143 %138\n%143 = OpLabel\n%144 = OpImageFetch  %7  %134 %131 Sample %132\nOpBranch %138\n%138 = OpLabel\n%145 = OpPhi  %7  %45 %135 %45 %139 %144 %143\nOpReturnValue %145\nOpFunctionEnd\n%149 = OpFunction  %2  None %150\n%147 = OpFunctionParameter  %6\n%148 = OpFunctionParameter  %7\n%146 = OpLabel\n%151 = OpLoad  %15  %29\nOpBranch %152\n%152 = OpLabel\nOpLine %3 44 5\nOpImageWrite %151 %147 %148\nOpReturn\nOpFunctionEnd\n%156 = OpFunction  %2  None %157\n%154 = OpFunctionParameter  %9\n%155 = OpFunctionParameter  %7\n%153 = OpLabel\n%158 = OpLoad  %16  %31\nOpBranch %159\n%159 = OpLabel\nOpLine %3 51 5\nOpImageWrite %158 %154 %155\nOpReturn\nOpFunctionEnd\n%164 = OpFunction  %2  None %165\n%161 = OpFunctionParameter  %9\n%162 = OpFunctionParameter  %11\n%163 = OpFunctionParameter  %7\n%160 = OpLabel\n%166 = OpLoad  %17  %33\nOpBranch %167\n%167 = OpLabel\nOpLine %3 58 2\n%168 = OpBitcast  %6  %162\n%169 = OpCompositeConstruct  %13  %161 %168\nOpImageWrite %166 %169 %163\nOpReturn\nOpFunctionEnd\n%174 = OpFunction  %2  None %175\n%171 = OpFunctionParameter  %9\n%172 = OpFunctionParameter  %6\n%173 = OpFunctionParameter  %7\n%170 = OpLabel\n%176 = OpLoad  %17  %33\nOpBranch %177\n%177 = OpLabel\nOpLine %3 62 2\n%178 = OpCompositeConstruct  %13  %171 %172\nOpImageWrite %176 %178 %173\nOpReturn\nOpFunctionEnd\n%182 = OpFunction  %2  None %183\n%180 = OpFunctionParameter  %13\n%181 = OpFunctionParameter  %7\n%179 = OpLabel\n%184 = OpLoad  %18  %35\nOpBranch %185\n%185 = OpLabel\nOpLine %3 69 5\nOpImageWrite %184 %180 %181\nOpReturn\nOpFunctionEnd\n%189 = OpFunction  %2  None %190\n%186 = OpLabel\n%191 = OpLoad  %4  %19\n%192 = OpLoad  %8  %21\n%193 = OpLoad  %10  %23\n%194 = OpLoad  %12  %25\n%195 = OpLoad  %14  %27\n%196 = OpLoad  %15  %29\n%197 = OpLoad  %16  %31\n%198 = OpLoad  %17  %33\n%199 = OpLoad  %18  %35\nOpBranch %206\n%206 = OpLabel\nOpLine %3 76 5\n%207 = OpFunctionCall  %7  %40 %200 %200\nOpLine %3 77 5\n%208 = OpFunctionCall  %7  %58 %201 %200\nOpLine %3 78 5\n%209 = OpFunctionCall  %7  %77 %201 %202 %200\nOpLine %3 79 5\n%210 = OpFunctionCall  %7  %98 %201 %200 %200\nOpLine %3 80 5\n%211 = OpFunctionCall  %7  %116 %203 %200\nOpLine %3 81 5\n%212 = OpFunctionCall  %7  %133 %201 %200\nOpLine %3 82 5\n%213 = OpFunctionCall  %2  %149 %200 %45\nOpLine %3 83 5\n%214 = OpFunctionCall  %2  %156 %201 %45\nOpLine %3 84 5\n%215 = OpFunctionCall  %2  %164 %201 %202 %45\nOpLine %3 85 5\n%216 = OpFunctionCall  %2  %174 %201 %200 %45\nOpLine %3 86 5\n%217 = OpFunctionCall  %2  %182 %203 %45\nOpLine %3 88 12\nOpStore %187 %205\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-restrict.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 212\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %183 \"main\"\nOpExecutionMode %183 LocalSize 1 1 1\nOpDecorate %4 ArrayStride 4\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 48\nOpMemberDecorate %10 2 Offset 64\nOpMemberDecorate %10 2 ColMajor\nOpMemberDecorate %10 2 MatrixStride 16\nOpMemberDecorate %10 3 Offset 112\nOpDecorate %10 Block\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  10\n%4 = OpTypeArray %3 %5\n%7 = OpTypeVector %3 4\n%8 = OpTypeMatrix %7 3\n%9 = OpTypeRuntimeArray %3\n%10 = OpTypeStruct %4 %7 %8 %9\n%11 = OpTypeInt 32 1\n%13 = OpTypePointer StorageBuffer %10\n%12 = OpVariable  %13  StorageBuffer\n%17 = OpTypeFunction %3 %11\n%19 = OpTypePointer StorageBuffer %4\n%20 = OpTypePointer StorageBuffer %3\n%21 = OpConstant  %6  9\n%23 = OpConstant  %6  0\n%30 = OpTypePointer StorageBuffer %9\n%32 = OpConstant  %6  1\n%35 = OpConstant  %6  3\n%42 = OpTypePointer StorageBuffer %7\n%50 = OpTypeFunction %3 %7 %11\n%57 = OpTypeFunction %7 %11\n%59 = OpTypePointer StorageBuffer %8\n%60 = OpConstant  %6  2\n%68 = OpTypeFunction %3 %11 %11\n%77 = OpConstant  %3  100\n%83 = OpConstant  %3  -2147483600\n%84 = OpConstant  %3  2147483500\n%92 = OpTypeFunction %3\n%106 = OpTypeFunction %2 %11 %3\n%130 = OpTypeFunction %2 %11 %7\n%139 = OpTypeFunction %2 %11 %11 %3\n%160 = OpTypeFunction %2 %3\n%170 = OpConstant  %6  1000\n%184 = OpTypeFunction %2\n%185 = OpConstant  %11  1\n%186 = OpConstant  %3  2\n%187 = OpConstant  %3  3\n%188 = OpConstant  %3  4\n%189 = OpConstant  %3  5\n%190 = OpConstantComposite  %7  %186 %187 %188 %189\n%191 = OpConstant  %11  6\n%192 = OpConstant  %11  2\n%193 = OpConstant  %3  1\n%16 = OpFunction  %3  None %17\n%15 = OpFunctionParameter  %11\n%14 = OpLabel\nOpBranch %18\n%18 = OpLabel\n%22 = OpExtInst  %6  %1 UMin %15 %21\n%24 = OpAccessChain  %20  %12 %23 %22\n%25 = OpLoad  %3  %24\nOpReturnValue %25\nOpFunctionEnd\n%28 = OpFunction  %3  None %17\n%27 = OpFunctionParameter  %11\n%26 = OpLabel\nOpBranch %29\n%29 = OpLabel\n%31 = OpArrayLength  %6  %12 3\n%33 = OpISub  %6  %31 %32\n%34 = OpExtInst  %6  %1 UMin %27 %33\n%36 = OpAccessChain  %20  %12 %35 %34\n%37 = OpLoad  %3  %36\nOpReturnValue %37\nOpFunctionEnd\n%40 = OpFunction  %3  None %17\n%39 = OpFunctionParameter  %11\n%38 = OpLabel\nOpBranch %41\n%41 = OpLabel\n%43 = OpExtInst  %6  %1 UMin %39 %35\n%44 = OpAccessChain  %20  %12 %32 %43\n%45 = OpLoad  %3  %44\nOpReturnValue %45\nOpFunctionEnd\n%49 = OpFunction  %3  None %50\n%47 = OpFunctionParameter  %7\n%48 = OpFunctionParameter  %11\n%46 = OpLabel\nOpBranch %51\n%51 = OpLabel\n%52 = OpExtInst  %6  %1 UMin %48 %35\n%53 = OpVectorExtractDynamic  %3  %47 %52\nOpReturnValue %53\nOpFunctionEnd\n%56 = OpFunction  %7  None %57\n%55 = OpFunctionParameter  %11\n%54 = OpLabel\nOpBranch %58\n%58 = OpLabel\n%61 = OpExtInst  %6  %1 UMin %55 %60\n%62 = OpAccessChain  %42  %12 %60 %61\n%63 = OpLoad  %7  %62\nOpReturnValue %63\nOpFunctionEnd\n%67 = OpFunction  %3  None %68\n%65 = OpFunctionParameter  %11\n%66 = OpFunctionParameter  %11\n%64 = OpLabel\nOpBranch %69\n%69 = OpLabel\n%70 = OpExtInst  %6  %1 UMin %66 %35\n%71 = OpExtInst  %6  %1 UMin %65 %60\n%72 = OpAccessChain  %20  %12 %60 %71 %70\n%73 = OpLoad  %3  %72\nOpReturnValue %73\nOpFunctionEnd\n%76 = OpFunction  %3  None %17\n%75 = OpFunctionParameter  %11\n%74 = OpLabel\nOpBranch %78\n%78 = OpLabel\n%79 = OpConvertSToF  %3  %75\n%80 = OpFDiv  %3  %79 %77\n%81 = OpExtInst  %3  %1 Sin %80\n%82 = OpFMul  %3  %81 %77\n%85 = OpExtInst  %3  %1 FClamp %82 %83 %84\n%86 = OpConvertFToS  %11  %85\n%87 = OpExtInst  %6  %1 UMin %86 %21\n%88 = OpAccessChain  %20  %12 %23 %87\n%89 = OpLoad  %3  %88\nOpReturnValue %89\nOpFunctionEnd\n%91 = OpFunction  %3  None %92\n%90 = OpLabel\nOpBranch %93\n%93 = OpLabel\n%94 = OpAccessChain  %20  %12 %23 %21\n%95 = OpLoad  %3  %94\n%96 = OpAccessChain  %20  %12 %32 %35\n%97 = OpLoad  %3  %96\n%98 = OpFAdd  %3  %95 %97\n%99 = OpAccessChain  %20  %12 %60 %60 %35\n%100 = OpLoad  %3  %99\n%101 = OpFAdd  %3  %98 %100\nOpReturnValue %101\nOpFunctionEnd\n%105 = OpFunction  %2  None %106\n%103 = OpFunctionParameter  %11\n%104 = OpFunctionParameter  %3\n%102 = OpLabel\nOpBranch %107\n%107 = OpLabel\n%108 = OpExtInst  %6  %1 UMin %103 %21\n%109 = OpAccessChain  %20  %12 %23 %108\nOpStore %109 %104\nOpReturn\nOpFunctionEnd\n%113 = OpFunction  %2  None %106\n%111 = OpFunctionParameter  %11\n%112 = OpFunctionParameter  %3\n%110 = OpLabel\nOpBranch %114\n%114 = OpLabel\n%115 = OpArrayLength  %6  %12 3\n%116 = OpISub  %6  %115 %32\n%117 = OpExtInst  %6  %1 UMin %111 %116\n%118 = OpAccessChain  %20  %12 %35 %117\nOpStore %118 %112\nOpReturn\nOpFunctionEnd\n%122 = OpFunction  %2  None %106\n%120 = OpFunctionParameter  %11\n%121 = OpFunctionParameter  %3\n%119 = OpLabel\nOpBranch %123\n%123 = OpLabel\n%124 = OpExtInst  %6  %1 UMin %120 %35\n%125 = OpAccessChain  %20  %12 %32 %124\nOpStore %125 %121\nOpReturn\nOpFunctionEnd\n%129 = OpFunction  %2  None %130\n%127 = OpFunctionParameter  %11\n%128 = OpFunctionParameter  %7\n%126 = OpLabel\nOpBranch %131\n%131 = OpLabel\n%132 = OpExtInst  %6  %1 UMin %127 %60\n%133 = OpAccessChain  %42  %12 %60 %132\nOpStore %133 %128\nOpReturn\nOpFunctionEnd\n%138 = OpFunction  %2  None %139\n%135 = OpFunctionParameter  %11\n%136 = OpFunctionParameter  %11\n%137 = OpFunctionParameter  %3\n%134 = OpLabel\nOpBranch %140\n%140 = OpLabel\n%141 = OpExtInst  %6  %1 UMin %136 %35\n%142 = OpExtInst  %6  %1 UMin %135 %60\n%143 = OpAccessChain  %20  %12 %60 %142 %141\nOpStore %143 %137\nOpReturn\nOpFunctionEnd\n%147 = OpFunction  %2  None %106\n%145 = OpFunctionParameter  %11\n%146 = OpFunctionParameter  %3\n%144 = OpLabel\nOpBranch %148\n%148 = OpLabel\n%149 = OpConvertSToF  %3  %145\n%150 = OpFDiv  %3  %149 %77\n%151 = OpExtInst  %3  %1 Sin %150\n%152 = OpFMul  %3  %151 %77\n%153 = OpExtInst  %3  %1 FClamp %152 %83 %84\n%154 = OpConvertFToS  %11  %153\n%155 = OpExtInst  %6  %1 UMin %154 %21\n%156 = OpAccessChain  %20  %12 %23 %155\nOpStore %156 %146\nOpReturn\nOpFunctionEnd\n%159 = OpFunction  %2  None %160\n%158 = OpFunctionParameter  %3\n%157 = OpLabel\nOpBranch %161\n%161 = OpLabel\n%162 = OpAccessChain  %20  %12 %23 %21\nOpStore %162 %158\n%163 = OpAccessChain  %20  %12 %32 %35\nOpStore %163 %158\n%164 = OpAccessChain  %20  %12 %60 %60 %35\nOpStore %164 %158\nOpReturn\nOpFunctionEnd\n%166 = OpFunction  %3  None %92\n%165 = OpLabel\nOpBranch %167\n%167 = OpLabel\n%168 = OpArrayLength  %6  %12 3\n%169 = OpISub  %6  %168 %32\n%171 = OpExtInst  %6  %1 UMin %170 %169\n%172 = OpAccessChain  %20  %12 %35 %171\n%173 = OpLoad  %3  %172\nOpReturnValue %173\nOpFunctionEnd\n%176 = OpFunction  %2  None %160\n%175 = OpFunctionParameter  %3\n%174 = OpLabel\nOpBranch %177\n%177 = OpLabel\n%178 = OpArrayLength  %6  %12 3\n%179 = OpISub  %6  %178 %32\n%180 = OpExtInst  %6  %1 UMin %170 %179\n%181 = OpAccessChain  %20  %12 %35 %180\nOpStore %181 %175\nOpReturn\nOpFunctionEnd\n%183 = OpFunction  %2  None %184\n%182 = OpLabel\nOpBranch %194\n%194 = OpLabel\n%195 = OpFunctionCall  %3  %16 %185\n%196 = OpFunctionCall  %3  %28 %185\n%197 = OpFunctionCall  %3  %40 %185\n%198 = OpFunctionCall  %3  %49 %190 %191\n%199 = OpFunctionCall  %7  %56 %185\n%200 = OpFunctionCall  %3  %67 %185 %192\n%201 = OpFunctionCall  %3  %76 %185\n%202 = OpFunctionCall  %3  %91\n%203 = OpFunctionCall  %2  %105 %185 %186\n%204 = OpFunctionCall  %2  %113 %185 %186\n%205 = OpFunctionCall  %2  %122 %185 %186\n%206 = OpFunctionCall  %2  %129 %185 %190\n%207 = OpFunctionCall  %2  %138 %185 %192 %193\n%208 = OpFunctionCall  %2  %147 %185 %193\n%209 = OpFunctionCall  %2  %159 %193\n%210 = OpFunctionCall  %3  %166\n%211 = OpFunctionCall  %2  %176 %193\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-bounds-check-zero.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 252\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %223 \"main\"\nOpExecutionMode %223 LocalSize 1 1 1\nOpDecorate %4 ArrayStride 4\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 48\nOpMemberDecorate %10 2 Offset 64\nOpMemberDecorate %10 2 ColMajor\nOpMemberDecorate %10 2 MatrixStride 16\nOpMemberDecorate %10 3 Offset 112\nOpDecorate %10 Block\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  10\n%4 = OpTypeArray %3 %5\n%7 = OpTypeVector %3 4\n%8 = OpTypeMatrix %7 3\n%9 = OpTypeRuntimeArray %3\n%10 = OpTypeStruct %4 %7 %8 %9\n%11 = OpTypeInt 32 1\n%13 = OpTypePointer StorageBuffer %10\n%12 = OpVariable  %13  StorageBuffer\n%17 = OpTypeFunction %3 %11\n%19 = OpTypePointer StorageBuffer %4\n%20 = OpTypePointer StorageBuffer %3\n%22 = OpTypeBool\n%23 = OpConstant  %6  0\n%25 = OpConstantNull  %3\n%34 = OpTypePointer StorageBuffer %9\n%37 = OpConstant  %6  3\n%47 = OpTypePointer StorageBuffer %7\n%48 = OpConstant  %6  4\n%50 = OpConstant  %6  1\n%60 = OpTypeFunction %3 %7 %11\n%70 = OpTypeFunction %7 %11\n%72 = OpTypePointer StorageBuffer %8\n%74 = OpConstant  %6  2\n%76 = OpConstantNull  %7\n%85 = OpTypeFunction %3 %11 %11\n%98 = OpConstant  %3  100\n%104 = OpConstant  %3  -2147483600\n%105 = OpConstant  %3  2147483500\n%116 = OpTypeFunction %3\n%118 = OpConstant  %6  9\n%131 = OpTypeFunction %2 %11 %3\n%160 = OpTypeFunction %2 %11 %7\n%171 = OpTypeFunction %2 %11 %11 %3\n%197 = OpTypeFunction %2 %3\n%206 = OpConstant  %6  1000\n%224 = OpTypeFunction %2\n%225 = OpConstant  %11  1\n%226 = OpConstant  %3  2\n%227 = OpConstant  %3  3\n%228 = OpConstant  %3  4\n%229 = OpConstant  %3  5\n%230 = OpConstantComposite  %7  %226 %227 %228 %229\n%231 = OpConstant  %11  6\n%232 = OpConstant  %11  2\n%233 = OpConstant  %3  1\n%16 = OpFunction  %3  None %17\n%15 = OpFunctionParameter  %11\n%14 = OpLabel\nOpBranch %18\n%18 = OpLabel\n%21 = OpULessThan  %22  %15 %5\nOpSelectionMerge %26 None\nOpBranchConditional %21 %27 %26\n%27 = OpLabel\n%24 = OpAccessChain  %20  %12 %23 %15\n%28 = OpLoad  %3  %24\nOpBranch %26\n%26 = OpLabel\n%29 = OpPhi  %3  %25 %18 %28 %27\nOpReturnValue %29\nOpFunctionEnd\n%32 = OpFunction  %3  None %17\n%31 = OpFunctionParameter  %11\n%30 = OpLabel\nOpBranch %33\n%33 = OpLabel\n%35 = OpArrayLength  %6  %12 3\n%36 = OpULessThan  %22  %31 %35\nOpSelectionMerge %39 None\nOpBranchConditional %36 %40 %39\n%40 = OpLabel\n%38 = OpAccessChain  %20  %12 %37 %31\n%41 = OpLoad  %3  %38\nOpBranch %39\n%39 = OpLabel\n%42 = OpPhi  %3  %25 %33 %41 %40\nOpReturnValue %42\nOpFunctionEnd\n%45 = OpFunction  %3  None %17\n%44 = OpFunctionParameter  %11\n%43 = OpLabel\nOpBranch %46\n%46 = OpLabel\n%49 = OpULessThan  %22  %44 %48\nOpSelectionMerge %52 None\nOpBranchConditional %49 %53 %52\n%53 = OpLabel\n%51 = OpAccessChain  %20  %12 %50 %44\n%54 = OpLoad  %3  %51\nOpBranch %52\n%52 = OpLabel\n%55 = OpPhi  %3  %25 %46 %54 %53\nOpReturnValue %55\nOpFunctionEnd\n%59 = OpFunction  %3  None %60\n%57 = OpFunctionParameter  %7\n%58 = OpFunctionParameter  %11\n%56 = OpLabel\nOpBranch %61\n%61 = OpLabel\n%62 = OpULessThan  %22  %58 %48\nOpSelectionMerge %63 None\nOpBranchConditional %62 %64 %63\n%64 = OpLabel\n%65 = OpVectorExtractDynamic  %3  %57 %58\nOpBranch %63\n%63 = OpLabel\n%66 = OpPhi  %3  %25 %61 %65 %64\nOpReturnValue %66\nOpFunctionEnd\n%69 = OpFunction  %7  None %70\n%68 = OpFunctionParameter  %11\n%67 = OpLabel\nOpBranch %71\n%71 = OpLabel\n%73 = OpULessThan  %22  %68 %37\nOpSelectionMerge %77 None\nOpBranchConditional %73 %78 %77\n%78 = OpLabel\n%75 = OpAccessChain  %47  %12 %74 %68\n%79 = OpLoad  %7  %75\nOpBranch %77\n%77 = OpLabel\n%80 = OpPhi  %7  %76 %71 %79 %78\nOpReturnValue %80\nOpFunctionEnd\n%84 = OpFunction  %3  None %85\n%82 = OpFunctionParameter  %11\n%83 = OpFunctionParameter  %11\n%81 = OpLabel\nOpBranch %86\n%86 = OpLabel\n%87 = OpULessThan  %22  %83 %48\n%88 = OpULessThan  %22  %82 %37\n%89 = OpLogicalAnd  %22  %87 %88\nOpSelectionMerge %91 None\nOpBranchConditional %89 %92 %91\n%92 = OpLabel\n%90 = OpAccessChain  %20  %12 %74 %82 %83\n%93 = OpLoad  %3  %90\nOpBranch %91\n%91 = OpLabel\n%94 = OpPhi  %3  %25 %86 %93 %92\nOpReturnValue %94\nOpFunctionEnd\n%97 = OpFunction  %3  None %17\n%96 = OpFunctionParameter  %11\n%95 = OpLabel\nOpBranch %99\n%99 = OpLabel\n%100 = OpConvertSToF  %3  %96\n%101 = OpFDiv  %3  %100 %98\n%102 = OpExtInst  %3  %1 Sin %101\n%103 = OpFMul  %3  %102 %98\n%106 = OpExtInst  %3  %1 FClamp %103 %104 %105\n%107 = OpConvertFToS  %11  %106\n%108 = OpULessThan  %22  %107 %5\nOpSelectionMerge %110 None\nOpBranchConditional %108 %111 %110\n%111 = OpLabel\n%109 = OpAccessChain  %20  %12 %23 %107\n%112 = OpLoad  %3  %109\nOpBranch %110\n%110 = OpLabel\n%113 = OpPhi  %3  %25 %99 %112 %111\nOpReturnValue %113\nOpFunctionEnd\n%115 = OpFunction  %3  None %116\n%114 = OpLabel\nOpBranch %117\n%117 = OpLabel\n%119 = OpAccessChain  %20  %12 %23 %118\n%120 = OpLoad  %3  %119\n%121 = OpAccessChain  %20  %12 %50 %37\n%122 = OpLoad  %3  %121\n%123 = OpFAdd  %3  %120 %122\n%124 = OpAccessChain  %20  %12 %74 %74 %37\n%125 = OpLoad  %3  %124\n%126 = OpFAdd  %3  %123 %125\nOpReturnValue %126\nOpFunctionEnd\n%130 = OpFunction  %2  None %131\n%128 = OpFunctionParameter  %11\n%129 = OpFunctionParameter  %3\n%127 = OpLabel\nOpBranch %132\n%132 = OpLabel\n%133 = OpULessThan  %22  %128 %5\nOpSelectionMerge %135 None\nOpBranchConditional %133 %136 %135\n%136 = OpLabel\n%134 = OpAccessChain  %20  %12 %23 %128\nOpStore %134 %129\nOpBranch %135\n%135 = OpLabel\nOpReturn\nOpFunctionEnd\n%140 = OpFunction  %2  None %131\n%138 = OpFunctionParameter  %11\n%139 = OpFunctionParameter  %3\n%137 = OpLabel\nOpBranch %141\n%141 = OpLabel\n%142 = OpArrayLength  %6  %12 3\n%143 = OpULessThan  %22  %138 %142\nOpSelectionMerge %145 None\nOpBranchConditional %143 %146 %145\n%146 = OpLabel\n%144 = OpAccessChain  %20  %12 %37 %138\nOpStore %144 %139\nOpBranch %145\n%145 = OpLabel\nOpReturn\nOpFunctionEnd\n%150 = OpFunction  %2  None %131\n%148 = OpFunctionParameter  %11\n%149 = OpFunctionParameter  %3\n%147 = OpLabel\nOpBranch %151\n%151 = OpLabel\n%152 = OpULessThan  %22  %148 %48\nOpSelectionMerge %154 None\nOpBranchConditional %152 %155 %154\n%155 = OpLabel\n%153 = OpAccessChain  %20  %12 %50 %148\nOpStore %153 %149\nOpBranch %154\n%154 = OpLabel\nOpReturn\nOpFunctionEnd\n%159 = OpFunction  %2  None %160\n%157 = OpFunctionParameter  %11\n%158 = OpFunctionParameter  %7\n%156 = OpLabel\nOpBranch %161\n%161 = OpLabel\n%162 = OpULessThan  %22  %157 %37\nOpSelectionMerge %164 None\nOpBranchConditional %162 %165 %164\n%165 = OpLabel\n%163 = OpAccessChain  %47  %12 %74 %157\nOpStore %163 %158\nOpBranch %164\n%164 = OpLabel\nOpReturn\nOpFunctionEnd\n%170 = OpFunction  %2  None %171\n%167 = OpFunctionParameter  %11\n%168 = OpFunctionParameter  %11\n%169 = OpFunctionParameter  %3\n%166 = OpLabel\nOpBranch %172\n%172 = OpLabel\n%173 = OpULessThan  %22  %168 %48\n%174 = OpULessThan  %22  %167 %37\n%175 = OpLogicalAnd  %22  %173 %174\nOpSelectionMerge %177 None\nOpBranchConditional %175 %178 %177\n%178 = OpLabel\n%176 = OpAccessChain  %20  %12 %74 %167 %168\nOpStore %176 %169\nOpBranch %177\n%177 = OpLabel\nOpReturn\nOpFunctionEnd\n%182 = OpFunction  %2  None %131\n%180 = OpFunctionParameter  %11\n%181 = OpFunctionParameter  %3\n%179 = OpLabel\nOpBranch %183\n%183 = OpLabel\n%184 = OpConvertSToF  %3  %180\n%185 = OpFDiv  %3  %184 %98\n%186 = OpExtInst  %3  %1 Sin %185\n%187 = OpFMul  %3  %186 %98\n%188 = OpExtInst  %3  %1 FClamp %187 %104 %105\n%189 = OpConvertFToS  %11  %188\n%190 = OpULessThan  %22  %189 %5\nOpSelectionMerge %192 None\nOpBranchConditional %190 %193 %192\n%193 = OpLabel\n%191 = OpAccessChain  %20  %12 %23 %189\nOpStore %191 %181\nOpBranch %192\n%192 = OpLabel\nOpReturn\nOpFunctionEnd\n%196 = OpFunction  %2  None %197\n%195 = OpFunctionParameter  %3\n%194 = OpLabel\nOpBranch %198\n%198 = OpLabel\n%199 = OpAccessChain  %20  %12 %23 %118\nOpStore %199 %195\n%200 = OpAccessChain  %20  %12 %50 %37\nOpStore %200 %195\n%201 = OpAccessChain  %20  %12 %74 %74 %37\nOpStore %201 %195\nOpReturn\nOpFunctionEnd\n%203 = OpFunction  %3  None %116\n%202 = OpLabel\nOpBranch %204\n%204 = OpLabel\n%205 = OpArrayLength  %6  %12 3\n%207 = OpULessThan  %22  %206 %205\nOpSelectionMerge %209 None\nOpBranchConditional %207 %210 %209\n%210 = OpLabel\n%208 = OpAccessChain  %20  %12 %37 %206\n%211 = OpLoad  %3  %208\nOpBranch %209\n%209 = OpLabel\n%212 = OpPhi  %3  %25 %204 %211 %210\nOpReturnValue %212\nOpFunctionEnd\n%215 = OpFunction  %2  None %197\n%214 = OpFunctionParameter  %3\n%213 = OpLabel\nOpBranch %216\n%216 = OpLabel\n%217 = OpArrayLength  %6  %12 3\n%218 = OpULessThan  %22  %206 %217\nOpSelectionMerge %220 None\nOpBranchConditional %218 %221 %220\n%221 = OpLabel\n%219 = OpAccessChain  %20  %12 %37 %206\nOpStore %219 %214\nOpBranch %220\n%220 = OpLabel\nOpReturn\nOpFunctionEnd\n%223 = OpFunction  %2  None %224\n%222 = OpLabel\nOpBranch %234\n%234 = OpLabel\n%235 = OpFunctionCall  %3  %16 %225\n%236 = OpFunctionCall  %3  %32 %225\n%237 = OpFunctionCall  %3  %45 %225\n%238 = OpFunctionCall  %3  %59 %230 %231\n%239 = OpFunctionCall  %7  %69 %225\n%240 = OpFunctionCall  %3  %84 %225 %232\n%241 = OpFunctionCall  %3  %97 %225\n%242 = OpFunctionCall  %3  %115\n%243 = OpFunctionCall  %2  %130 %225 %226\n%244 = OpFunctionCall  %2  %140 %225 %226\n%245 = OpFunctionCall  %2  %150 %225 %226\n%246 = OpFunctionCall  %2  %159 %225 %230\n%247 = OpFunctionCall  %2  %170 %225 %232 %233\n%248 = OpFunctionCall  %2  %182 %225 %233\n%249 = OpFunctionCall  %2  %196 %233\n%250 = OpFunctionCall  %3  %203\n%251 = OpFunctionCall  %2  %215 %233\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-break-if.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 122\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %115 \"main\"\nOpExecutionMode %115 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeBool\n%4 = OpTypeInt 32 0\n%7 = OpTypeFunction %2\n%8 = OpConstantTrue  %3\n%14 = OpTypeVector %4 2\n%15 = OpTypePointer Function %14\n%16 = OpTypeVector %3 2\n%17 = OpConstant  %4  0\n%18 = OpConstantComposite  %14  %17 %17\n%19 = OpConstant  %4  1\n%20 = OpConstant  %4  4294967295\n%21 = OpConstantComposite  %14  %20 %20\n%36 = OpTypeFunction %2 %3\n%38 = OpTypePointer Function %3\n%39 = OpConstantNull  %3\n%41 = OpConstantNull  %3\n%66 = OpConstantNull  %3\n%68 = OpConstantNull  %3\n%91 = OpConstant  %4  5\n%93 = OpTypePointer Function %4\n%116 = OpConstantFalse  %3\n%6 = OpFunction  %2  None %7\n%5 = OpLabel\n%22 = OpVariable  %15  Function %21\nOpBranch %9\n%9 = OpLabel\nOpBranch %10\n%10 = OpLabel\nOpLoopMerge %11 %13 None\nOpBranch %23\n%23 = OpLabel\n%24 = OpLoad  %14  %22\n%25 = OpIEqual  %16  %18 %24\n%26 = OpAll  %3  %25\nOpSelectionMerge %27 None\nOpBranchConditional %26 %11 %27\n%27 = OpLabel\n%28 = OpCompositeExtract  %4  %24 1\n%29 = OpIEqual  %3  %28 %17\n%30 = OpSelect  %4  %29 %19 %17\n%31 = OpCompositeConstruct  %14  %30 %19\n%32 = OpISub  %14  %24 %31\nOpStore %22 %32\nOpBranch %12\n%12 = OpLabel\nOpBranch %13\n%13 = OpLabel\nOpBranchConditional %8 %11 %10\n%11 = OpLabel\nOpReturn\nOpFunctionEnd\n%35 = OpFunction  %2  None %36\n%34 = OpFunctionParameter  %3\n%33 = OpLabel\n%37 = OpVariable  %38  Function %39\n%40 = OpVariable  %38  Function %41\n%47 = OpVariable  %15  Function %21\nOpBranch %42\n%42 = OpLabel\nOpBranch %43\n%43 = OpLabel\nOpLoopMerge %44 %46 None\nOpBranch %48\n%48 = OpLabel\n%49 = OpLoad  %14  %47\n%50 = OpIEqual  %16  %18 %49\n%51 = OpAll  %3  %50\nOpSelectionMerge %52 None\nOpBranchConditional %51 %44 %52\n%52 = OpLabel\n%53 = OpCompositeExtract  %4  %49 1\n%54 = OpIEqual  %3  %53 %17\n%55 = OpSelect  %4  %54 %19 %17\n%56 = OpCompositeConstruct  %14  %55 %19\n%57 = OpISub  %14  %49 %56\nOpStore %47 %57\nOpBranch %45\n%45 = OpLabel\nOpBranch %46\n%46 = OpLabel\nOpStore %37 %34\n%58 = OpLoad  %3  %37\n%59 = OpLogicalNotEqual  %3  %34 %58\nOpStore %40 %59\n%60 = OpLoad  %3  %40\n%61 = OpLogicalEqual  %3  %34 %60\nOpBranchConditional %61 %44 %43\n%44 = OpLabel\nOpReturn\nOpFunctionEnd\n%64 = OpFunction  %2  None %36\n%63 = OpFunctionParameter  %3\n%62 = OpLabel\n%65 = OpVariable  %38  Function %66\n%67 = OpVariable  %38  Function %68\n%74 = OpVariable  %15  Function %21\nOpBranch %69\n%69 = OpLabel\nOpBranch %70\n%70 = OpLabel\nOpLoopMerge %71 %73 None\nOpBranch %75\n%75 = OpLabel\n%76 = OpLoad  %14  %74\n%77 = OpIEqual  %16  %18 %76\n%78 = OpAll  %3  %77\nOpSelectionMerge %79 None\nOpBranchConditional %78 %71 %79\n%79 = OpLabel\n%80 = OpCompositeExtract  %4  %76 1\n%81 = OpIEqual  %3  %80 %17\n%82 = OpSelect  %4  %81 %19 %17\n%83 = OpCompositeConstruct  %14  %82 %19\n%84 = OpISub  %14  %76 %83\nOpStore %74 %84\nOpBranch %72\n%72 = OpLabel\nOpStore %65 %63\n%85 = OpLoad  %3  %65\n%86 = OpLogicalNotEqual  %3  %63 %85\nOpStore %67 %86\nOpBranch %73\n%73 = OpLabel\n%87 = OpLoad  %3  %67\n%88 = OpLogicalEqual  %3  %63 %87\nOpBranchConditional %88 %71 %70\n%71 = OpLabel\nOpReturn\nOpFunctionEnd\n%90 = OpFunction  %2  None %7\n%89 = OpLabel\n%92 = OpVariable  %93  Function %17\n%99 = OpVariable  %15  Function %21\nOpBranch %94\n%94 = OpLabel\nOpBranch %95\n%95 = OpLabel\nOpLoopMerge %96 %98 None\nOpBranch %100\n%100 = OpLabel\n%101 = OpLoad  %14  %99\n%102 = OpIEqual  %16  %18 %101\n%103 = OpAll  %3  %102\nOpSelectionMerge %104 None\nOpBranchConditional %103 %96 %104\n%104 = OpLabel\n%105 = OpCompositeExtract  %4  %101 1\n%106 = OpIEqual  %3  %105 %17\n%107 = OpSelect  %4  %106 %19 %17\n%108 = OpCompositeConstruct  %14  %107 %19\n%109 = OpISub  %14  %101 %108\nOpStore %99 %109\nOpBranch %97\n%97 = OpLabel\n%110 = OpLoad  %4  %92\n%111 = OpIAdd  %4  %110 %19\nOpStore %92 %111\nOpBranch %98\n%98 = OpLabel\n%112 = OpLoad  %4  %92\n%113 = OpIEqual  %3  %112 %91\nOpBranchConditional %113 %96 %95\n%96 = OpLabel\nOpReturn\nOpFunctionEnd\n%115 = OpFunction  %2  None %7\n%114 = OpLabel\nOpBranch %117\n%117 = OpLabel\n%118 = OpFunctionCall  %2  %6\n%119 = OpFunctionCall  %2  %35 %116\n%120 = OpFunctionCall  %2  %64 %116\n%121 = OpFunctionCall  %2  %90\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-clip-distances.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 28\nOpCapability Shader\nOpCapability ClipDistance\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %14 \"main\" %10 %12\nOpDecorate %5 ArrayStride 4\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 16\nOpDecorate %10 BuiltIn Position\nOpDecorate %12 BuiltIn ClipDistance\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%7 = OpTypeInt 32 0\n%6 = OpConstant  %7  1\n%5 = OpTypeArray %3 %6\n%8 = OpTypeStruct %4 %5\n%11 = OpTypePointer Output %4\n%10 = OpVariable  %11  Output\n%13 = OpTypePointer Output %5\n%12 = OpVariable  %13  Output\n%15 = OpTypeFunction %2\n%16 = OpConstant  %3  0.5\n%18 = OpTypePointer Function %8\n%19 = OpConstantNull  %8\n%21 = OpTypePointer Function %5\n%22 = OpTypePointer Function %3\n%23 = OpConstant  %7  0\n%14 = OpFunction  %2  None %15\n%9 = OpLabel\n%17 = OpVariable  %18  Function %19\nOpBranch %20\n%20 = OpLabel\n%24 = OpAccessChain  %22  %17 %6 %23\nOpStore %24 %16\n%25 = OpLoad  %8  %17\n%26 = OpCompositeExtract  %4  %25 0\nOpStore %10 %26\n%27 = OpCompositeExtract  %5  %25 1\nOpStore %12 %27\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-collatz.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 95\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %84 \"main\" %81\nOpExecutionMode %84 LocalSize 1 1 1\n%3 = OpString \"collatz.wgsl\"\nOpSource Unknown 0 %3 \"struct PrimeIndices {\n    data: array<u32>\n} // this is used as both input and output for convenience\n\n@group(0) @binding(0)\nvar<storage,read_write> v_indices: PrimeIndices;\n\n// The Collatz Conjecture states that for any integer n:\n// If n is even, n = n/2\n// If n is odd, n = 3n+1\n// And repeat this process for each new n, you will always eventually reach 1.\n// Though the conjecture has not been proven, no counterexample has ever been found.\n// This function returns how many times this recurrence needs to be applied to reach 1.\nfn collatz_iterations(n_base: u32) -> u32 {\n    var n = n_base;\n    var i: u32 = 0u;\n    while n > 1u {\n        if n % 2u == 0u {\n            n = n / 2u;\n        }\n        else {\n            n = 3u * n + 1u;\n        }\n        i = i + 1u;\n    }\n    return i;\n}\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    v_indices.data[global_id.x] = collatz_iterations(v_indices.data[global_id.x]);\n}\n\"\nOpMemberName %6 0 \"data\"\nOpName %6 \"PrimeIndices\"\nOpName %8 \"v_indices\"\nOpName %10 \"naga_mod\"\nOpName %12 \"lhs\"\nOpName %13 \"rhs\"\nOpName %21 \"naga_div\"\nOpName %22 \"lhs\"\nOpName %23 \"rhs\"\nOpName %29 \"n_base\"\nOpName %30 \"collatz_iterations\"\nOpName %34 \"n\"\nOpName %37 \"i\"\nOpName %49 \"loop_bound\"\nOpName %81 \"global_id\"\nOpName %84 \"main\"\nOpDecorate %5 ArrayStride 4\nOpMemberDecorate %6 0 Offset 0\nOpDecorate %6 Block\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 0\nOpDecorate %81 BuiltIn GlobalInvocationId\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%5 = OpTypeRuntimeArray %4\n%6 = OpTypeStruct %5\n%7 = OpTypeVector %4 3\n%9 = OpTypePointer StorageBuffer %6\n%8 = OpVariable  %9  StorageBuffer\n%11 = OpTypeFunction %4 %4 %4\n%15 = OpTypeBool\n%16 = OpConstant  %4  0\n%18 = OpConstant  %4  1\n%31 = OpTypeFunction %4 %4\n%32 = OpConstant  %4  2\n%33 = OpConstant  %4  3\n%35 = OpTypePointer Function %4\n%36 = OpConstantNull  %4\n%43 = OpTypeVector %4 2\n%44 = OpTypePointer Function %43\n%45 = OpTypeVector %15 2\n%46 = OpConstantComposite  %43  %16 %16\n%47 = OpConstant  %4  4294967295\n%48 = OpConstantComposite  %43  %47 %47\n%82 = OpTypePointer Input %7\n%81 = OpVariable  %82  Input\n%85 = OpTypeFunction %2\n%87 = OpTypePointer StorageBuffer %5\n%89 = OpTypePointer StorageBuffer %4\n%10 = OpFunction  %4  None %11\n%12 = OpFunctionParameter  %4\n%13 = OpFunctionParameter  %4\n%14 = OpLabel\n%17 = OpIEqual  %15  %13 %16\n%19 = OpSelect  %4  %17 %18 %13\n%20 = OpUMod  %4  %12 %19\nOpReturnValue %20\nOpFunctionEnd\n%21 = OpFunction  %4  None %11\n%22 = OpFunctionParameter  %4\n%23 = OpFunctionParameter  %4\n%24 = OpLabel\n%25 = OpIEqual  %15  %23 %16\n%26 = OpSelect  %4  %25 %18 %23\n%27 = OpUDiv  %4  %22 %26\nOpReturnValue %27\nOpFunctionEnd\n%30 = OpFunction  %4  None %31\n%29 = OpFunctionParameter  %4\n%28 = OpLabel\n%34 = OpVariable  %35  Function %36\n%37 = OpVariable  %35  Function %16\n%49 = OpVariable  %44  Function %48\nOpBranch %38\n%38 = OpLabel\nOpLine %3 15 5\nOpStore %34 %29\nOpBranch %39\n%39 = OpLabel\nOpLine %3 17 5\nOpLoopMerge %40 %42 None\nOpBranch %50\n%50 = OpLabel\n%51 = OpLoad  %43  %49\n%52 = OpIEqual  %45  %46 %51\n%53 = OpAll  %15  %52\nOpSelectionMerge %54 None\nOpBranchConditional %53 %40 %54\n%54 = OpLabel\n%55 = OpCompositeExtract  %4  %51 1\n%56 = OpIEqual  %15  %55 %16\n%57 = OpSelect  %4  %56 %18 %16\n%58 = OpCompositeConstruct  %43  %57 %18\n%59 = OpISub  %43  %51 %58\nOpStore %49 %59\nOpBranch %41\n%41 = OpLabel\nOpLine %3 1 1\n%60 = OpLoad  %4  %34\nOpLine %3 17 11\n%61 = OpUGreaterThan  %15  %60 %18\nOpLine %3 17 10\nOpSelectionMerge %62 None\nOpBranchConditional %61 %62 %63\n%63 = OpLabel\nOpBranch %40\n%62 = OpLabel\nOpBranch %64\n%64 = OpLabel\nOpLine %3 1 1\n%66 = OpLoad  %4  %34\nOpLine %3 18 12\n%67 = OpFunctionCall  %4  %10 %66 %32\nOpLine %3 18 12\n%68 = OpIEqual  %15  %67 %16\nOpLine %3 18 9\nOpSelectionMerge %69 None\nOpBranchConditional %68 %70 %71\n%70 = OpLabel\nOpLine %3 1 1\n%72 = OpLoad  %4  %34\nOpLine %3 19 17\n%73 = OpFunctionCall  %4  %21 %72 %32\nOpLine %3 19 13\nOpStore %34 %73\nOpBranch %69\n%71 = OpLabel\nOpLine %3 22 17\n%74 = OpLoad  %4  %34\n%75 = OpIMul  %4  %33 %74\nOpLine %3 22 17\n%76 = OpIAdd  %4  %75 %18\nOpLine %3 22 13\nOpStore %34 %76\nOpBranch %69\n%69 = OpLabel\nOpLine %3 1 1\n%77 = OpLoad  %4  %37\nOpLine %3 24 13\n%78 = OpIAdd  %4  %77 %18\nOpLine %3 24 9\nOpStore %37 %78\nOpBranch %65\n%65 = OpLabel\nOpBranch %42\n%42 = OpLabel\nOpBranch %39\n%40 = OpLabel\nOpLine %3 1 1\n%79 = OpLoad  %4  %37\nOpReturnValue %79\nOpFunctionEnd\n%84 = OpFunction  %2  None %85\n%80 = OpLabel\n%83 = OpLoad  %7  %81\nOpBranch %86\n%86 = OpLabel\nOpLine %3 31 5\n%88 = OpCompositeExtract  %4  %83 0\nOpLine %3 31 54\n%90 = OpCompositeExtract  %4  %83 0\n%91 = OpAccessChain  %89  %8 %16 %90\n%92 = OpLoad  %4  %91\nOpLine %3 31 35\n%93 = OpFunctionCall  %4  %30 %92\nOpLine %3 31 5\n%94 = OpAccessChain  %89  %8 %16 %88\nOpStore %94 %93\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-const-exprs.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 186\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %170 \"main\"\nOpExecutionMode %170 LocalSize 2 3 1\nOpDecorate %9 ArrayStride 4\nOpDecorate %13 ArrayStride 4\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 32 1\n%5 = OpTypeBool\n%6 = OpTypeVector %4 4\n%8 = OpTypeFloat 32\n%7 = OpTypeVector %8 4\n%10 = OpConstant  %3  2\n%9 = OpTypeArray %8 %10\n%11 = OpTypeVector %4 3\n%12 = OpTypeVector %5 2\n%14 = OpConstant  %3  9\n%13 = OpTypeArray %4 %14\n%15 = OpTypeVector %8 2\n%16 = OpConstant  %4  3\n%17 = OpConstantTrue  %5\n%18 = OpConstantFalse  %5\n%19 = OpConstant  %4  4\n%20 = OpConstant  %4  0\n%21 = OpConstant  %4  1\n%22 = OpConstant  %4  2\n%23 = OpConstant  %4  8\n%24 = OpConstant  %8  3.141\n%25 = OpConstant  %8  6.282\n%26 = OpConstant  %8  0.44444445\n%27 = OpConstant  %8  0\n%28 = OpConstantComposite  %7  %26 %27 %27 %27\n%29 = OpConstant  %8  4\n%30 = OpConstant  %8  5\n%31 = OpConstantComposite  %15  %29 %30\n%32 = OpConstantComposite  %12  %17 %18\n%35 = OpTypeFunction %2\n%36 = OpConstantComposite  %6  %19 %16 %22 %21\n%38 = OpTypePointer Function %6\n%43 = OpTypePointer Function %4\n%47 = OpConstant  %4  6\n%52 = OpConstant  %4  30\n%53 = OpConstant  %4  70\n%56 = OpConstantNull  %4\n%58 = OpConstantNull  %4\n%61 = OpConstantNull  %6\n%72 = OpConstant  %4  -4\n%73 = OpConstantComposite  %6  %72 %72 %72 %72\n%83 = OpTypeFunction %3 %4\n%84 = OpConstant  %3  10\n%85 = OpConstant  %3  20\n%86 = OpConstant  %3  30\n%87 = OpConstant  %3  0\n%94 = OpConstantNull  %3\n%97 = OpConstant  %8  1\n%98 = OpConstant  %8  2\n%99 = OpConstantComposite  %7  %98 %97 %97 %97\n%101 = OpTypePointer Function %7\n%106 = OpTypePointer Function %9\n%107 = OpConstantNull  %9\n%111 = OpConstantComposite  %11  %21 %21 %21\n%112 = OpConstantComposite  %11  %20 %21 %22\n%113 = OpConstantComposite  %11  %21 %20 %22\n%115 = OpTypePointer Function %11\n%122 = OpTypePointer Function %5\n%133 = OpConstant  %3  4\n%134 = OpConstant  %4  12\n%135 = OpConstant  %3  12\n%136 = OpConstant  %3  70\n%139 = OpTypePointer Function %3\n%149 = OpTypeFunction %2 %3\n%150 = OpConstant  %3  1\n%151 = OpConstant  %4  5\n%152 = OpConstant  %4  7\n%153 = OpConstant  %4  9\n%154 = OpConstantComposite  %13  %21 %22 %16 %19 %151 %47 %152 %23 %153\n%155 = OpConstantComposite  %6  %21 %22 %16 %19\n%157 = OpTypePointer Function %8\n%160 = OpConstantNull  %4\n%162 = OpConstantNull  %4\n%164 = OpTypePointer Function %13\n%34 = OpFunction  %2  None %35\n%33 = OpLabel\n%37 = OpVariable  %38  Function %36\nOpBranch %39\n%39 = OpLabel\nOpReturn\nOpFunctionEnd\n%41 = OpFunction  %2  None %35\n%40 = OpLabel\n%42 = OpVariable  %43  Function %22\nOpBranch %44\n%44 = OpLabel\nOpReturn\nOpFunctionEnd\n%46 = OpFunction  %2  None %35\n%45 = OpLabel\n%48 = OpVariable  %43  Function %47\nOpBranch %49\n%49 = OpLabel\nOpReturn\nOpFunctionEnd\n%51 = OpFunction  %2  None %35\n%50 = OpLabel\n%60 = OpVariable  %38  Function %61\n%55 = OpVariable  %43  Function %56\n%59 = OpVariable  %43  Function %53\n%54 = OpVariable  %43  Function %52\n%57 = OpVariable  %43  Function %58\nOpBranch %62\n%62 = OpLabel\n%63 = OpLoad  %4  %54\nOpStore %55 %63\n%64 = OpLoad  %4  %55\nOpStore %57 %64\n%65 = OpLoad  %4  %54\n%66 = OpLoad  %4  %55\n%67 = OpLoad  %4  %57\n%68 = OpLoad  %4  %59\n%69 = OpCompositeConstruct  %6  %65 %66 %67 %68\nOpStore %60 %69\nOpReturn\nOpFunctionEnd\n%71 = OpFunction  %2  None %35\n%70 = OpLabel\n%74 = OpVariable  %38  Function %73\nOpBranch %75\n%75 = OpLabel\nOpReturn\nOpFunctionEnd\n%77 = OpFunction  %2  None %35\n%76 = OpLabel\n%78 = OpVariable  %38  Function %73\nOpBranch %79\n%79 = OpLabel\nOpReturn\nOpFunctionEnd\n%82 = OpFunction  %3  None %83\n%81 = OpFunctionParameter  %4\n%80 = OpLabel\nOpBranch %88\n%88 = OpLabel\nOpSelectionMerge %89 None\nOpSwitch %81 %93 0 %90 1 %91 2 %92\n%90 = OpLabel\nOpReturnValue %84\n%91 = OpLabel\nOpReturnValue %85\n%92 = OpLabel\nOpReturnValue %86\n%93 = OpLabel\nOpReturnValue %87\n%89 = OpLabel\nOpReturnValue %94\nOpFunctionEnd\n%96 = OpFunction  %2  None %35\n%95 = OpLabel\n%100 = OpVariable  %101  Function %99\nOpBranch %102\n%102 = OpLabel\nOpReturn\nOpFunctionEnd\n%104 = OpFunction  %2  None %35\n%103 = OpLabel\n%105 = OpVariable  %106  Function %107\nOpBranch %108\n%108 = OpLabel\nOpReturn\nOpFunctionEnd\n%110 = OpFunction  %2  None %35\n%109 = OpLabel\n%114 = OpVariable  %115  Function %111\n%116 = OpVariable  %115  Function %112\n%117 = OpVariable  %115  Function %113\nOpBranch %118\n%118 = OpLabel\nOpReturn\nOpFunctionEnd\n%120 = OpFunction  %2  None %35\n%119 = OpLabel\n%128 = OpVariable  %122  Function %18\n%125 = OpVariable  %122  Function %17\n%121 = OpVariable  %122  Function %18\n%129 = OpVariable  %122  Function %17\n%126 = OpVariable  %122  Function %18\n%123 = OpVariable  %122  Function %17\n%127 = OpVariable  %122  Function %17\n%124 = OpVariable  %122  Function %18\nOpBranch %130\n%130 = OpLabel\nOpReturn\nOpFunctionEnd\n%132 = OpFunction  %2  None %35\n%131 = OpLabel\n%142 = OpVariable  %43  Function %53\n%138 = OpVariable  %139  Function %133\n%144 = OpVariable  %43  Function %72\n%141 = OpVariable  %139  Function %135\n%137 = OpVariable  %43  Function %19\n%143 = OpVariable  %139  Function %136\n%140 = OpVariable  %43  Function %134\nOpBranch %145\n%145 = OpLabel\nOpReturn\nOpFunctionEnd\n%148 = OpFunction  %2  None %149\n%147 = OpFunctionParameter  %3\n%146 = OpLabel\n%158 = OpVariable  %139  Function %150\n%161 = OpVariable  %43  Function %162\n%156 = OpVariable  %157  Function %97\n%159 = OpVariable  %43  Function %160\n%165 = OpVariable  %164  Function\nOpBranch %163\n%163 = OpLabel\nOpStore %165 %154\n%166 = OpAccessChain  %43  %165 %147\n%167 = OpLoad  %4  %166\nOpStore %159 %167\n%168 = OpVectorExtractDynamic  %4  %155 %147\nOpStore %161 %168\nOpReturn\nOpFunctionEnd\n%170 = OpFunction  %2  None %35\n%169 = OpLabel\nOpBranch %171\n%171 = OpLabel\n%172 = OpFunctionCall  %2  %34\n%173 = OpFunctionCall  %2  %41\n%174 = OpFunctionCall  %2  %46\n%175 = OpFunctionCall  %2  %51\n%176 = OpFunctionCall  %2  %71\n%177 = OpFunctionCall  %2  %77\n%178 = OpFunctionCall  %3  %82 %21\n%179 = OpFunctionCall  %2  %96\n%180 = OpFunctionCall  %2  %104\n%181 = OpFunctionCall  %2  %110\n%182 = OpFunctionCall  %2  %120\n%183 = OpFunctionCall  %2  %132\n%184 = OpFunctionCall  %2  %104\n%185 = OpFunctionCall  %2  %148 %150\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-constructors.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 70\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %41 \"main\"\nOpExecutionMode %41 LocalSize 1 1 1\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 16\nOpDecorate %10 ArrayStride 16\nOpDecorate %15 ArrayStride 32\nOpDecorate %18 ArrayStride 4\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeInt 32 1\n%6 = OpTypeStruct %4 %5\n%7 = OpTypeVector %3 3\n%9 = OpTypeVector %3 2\n%8 = OpTypeMatrix %9 2\n%12 = OpTypeInt 32 0\n%11 = OpConstant  %12  1\n%10 = OpTypeArray %8 %11\n%13 = OpTypeBool\n%14 = OpTypeVector %12 2\n%16 = OpConstant  %12  3\n%15 = OpTypeArray %6 %16\n%17 = OpTypeMatrix %4 4\n%19 = OpConstant  %12  4\n%18 = OpTypeArray %5 %19\n%20 = OpTypeMatrix %7 2\n%21 = OpConstant  %3  0\n%22 = OpConstantComposite  %7  %21 %21 %21\n%23 = OpConstant  %3  1\n%24 = OpConstant  %3  2\n%25 = OpConstant  %3  3\n%26 = OpConstantComposite  %9  %21 %23\n%27 = OpConstantComposite  %9  %24 %25\n%28 = OpConstantComposite  %8  %26 %27\n%29 = OpConstantComposite  %10  %28\n%30 = OpConstantNull  %13\n%31 = OpConstantNull  %5\n%32 = OpConstantNull  %12\n%33 = OpConstantNull  %3\n%34 = OpConstantNull  %14\n%35 = OpConstantNull  %8\n%36 = OpConstantNull  %15\n%37 = OpConstantNull  %6\n%38 = OpConstant  %12  0\n%39 = OpConstantComposite  %14  %38 %38\n%42 = OpTypeFunction %2\n%43 = OpConstantComposite  %4  %23 %23 %23 %23\n%44 = OpConstant  %5  1\n%45 = OpConstantComposite  %6  %43 %44\n%46 = OpConstantComposite  %9  %23 %21\n%47 = OpConstantComposite  %8  %46 %26\n%48 = OpConstantComposite  %4  %23 %21 %21 %21\n%49 = OpConstantComposite  %4  %21 %23 %21 %21\n%50 = OpConstantComposite  %4  %21 %21 %23 %21\n%51 = OpConstantComposite  %4  %21 %21 %21 %23\n%52 = OpConstantComposite  %17  %48 %49 %50 %51\n%53 = OpConstantComposite  %14  %38 %38\n%54 = OpConstantComposite  %9  %21 %21\n%55 = OpConstantComposite  %9  %21 %21\n%56 = OpConstantComposite  %8  %55 %55\n%57 = OpConstant  %5  0\n%58 = OpConstant  %5  2\n%59 = OpConstant  %5  3\n%60 = OpConstantComposite  %18  %57 %44 %58 %59\n%61 = OpConstantFalse  %13\n%62 = OpConstantComposite  %7  %21 %21 %21\n%63 = OpConstantComposite  %20  %62 %62\n%64 = OpConstantNull  %20\n%65 = OpConstantTrue  %13\n%67 = OpTypePointer Function %6\n%68 = OpConstantNull  %6\n%41 = OpFunction  %2  None %42\n%40 = OpLabel\n%66 = OpVariable  %67  Function %68\nOpBranch %69\n%69 = OpLabel\nOpStore %66 %45\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-control-flow.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 241\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %231 \"main\"\nOpExecutionMode %231 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%6 = OpTypeFunction %2\n%7 = OpConstant  %3  1\n%8 = OpConstant  %3  0\n%9 = OpConstant  %3  2\n%10 = OpConstant  %3  3\n%11 = OpConstant  %3  4\n%12 = OpTypeInt 32 0\n%13 = OpConstant  %12  0\n%15 = OpTypePointer Function %3\n%16 = OpConstantNull  %3\n%18 = OpConstant  %12  2\n%19 = OpConstant  %12  1\n%20 = OpConstant  %12  72\n%21 = OpConstant  %12  264\n%22 = OpConstant  %12  2056\n%52 = OpTypeFunction %2 %3\n%89 = OpTypeVector %12 2\n%90 = OpTypePointer Function %89\n%91 = OpTypeBool\n%92 = OpTypeVector %91 2\n%93 = OpConstantComposite  %89  %13 %13\n%94 = OpConstant  %12  4294967295\n%95 = OpConstantComposite  %89  %94 %94\n%115 = OpTypeFunction %2 %3 %3 %3\n%184 = OpTypeFunction %2 %3 %3 %3 %3\n%5 = OpFunction  %2  None %6\n%4 = OpLabel\n%14 = OpVariable  %15  Function %16\nOpBranch %17\n%17 = OpLabel\nOpControlBarrier %18 %19 %20\nOpControlBarrier %18 %18 %21\nOpControlBarrier %18 %18 %22\nOpSelectionMerge %23 None\nOpSwitch %7 %24\n%24 = OpLabel\nOpStore %14 %7\nOpBranch %23\n%23 = OpLabel\n%25 = OpLoad  %3  %14\nOpSelectionMerge %26 None\nOpSwitch %25 %31 1 %27 2 %28 3 %29 4 %29 5 %30 6 %31\n%27 = OpLabel\nOpStore %14 %8\nOpBranch %26\n%28 = OpLabel\nOpStore %14 %7\nOpBranch %26\n%29 = OpLabel\nOpStore %14 %9\nOpBranch %26\n%30 = OpLabel\nOpStore %14 %10\nOpBranch %26\n%31 = OpLabel\nOpStore %14 %11\nOpBranch %26\n%26 = OpLabel\nOpSelectionMerge %32 None\nOpSwitch %13 %34 0 %33\n%33 = OpLabel\nOpBranch %32\n%34 = OpLabel\nOpBranch %32\n%32 = OpLabel\n%35 = OpLoad  %3  %14\nOpSelectionMerge %36 None\nOpSwitch %35 %41 1 %37 2 %38 3 %39 4 %40\n%37 = OpLabel\nOpStore %14 %8\nOpBranch %36\n%38 = OpLabel\nOpStore %14 %7\nOpBranch %36\n%39 = OpLabel\nOpStore %14 %9\nOpBranch %36\n%40 = OpLabel\nOpBranch %36\n%41 = OpLabel\nOpStore %14 %10\nOpBranch %36\n%36 = OpLabel\n%42 = OpLoad  %3  %14\nOpSelectionMerge %43 None\nOpSwitch %42 %48 1 %44 2 %45 3 %46 4 %46 5 %47 6 %47\n%44 = OpLabel\nOpStore %14 %8\nOpReturn\n%45 = OpLabel\nOpStore %14 %7\nOpReturn\n%46 = OpLabel\nOpStore %14 %9\nOpReturn\n%47 = OpLabel\nOpStore %14 %10\nOpReturn\n%48 = OpLabel\nOpStore %14 %11\nOpReturn\n%43 = OpLabel\nOpReturn\nOpFunctionEnd\n%51 = OpFunction  %2  None %52\n%50 = OpFunctionParameter  %3\n%49 = OpLabel\nOpBranch %53\n%53 = OpLabel\nOpSelectionMerge %54 None\nOpSwitch %50 %55\n%55 = OpLabel\nOpBranch %54\n%54 = OpLabel\nOpReturn\nOpFunctionEnd\n%57 = OpFunction  %2  None %6\n%56 = OpLabel\nOpBranch %58\n%58 = OpLabel\nOpSelectionMerge %59 None\nOpSwitch %8 %61 0 %60\n%60 = OpLabel\nOpBranch %59\n%61 = OpLabel\nOpBranch %59\n%59 = OpLabel\nOpReturn\nOpFunctionEnd\n%63 = OpFunction  %2  None %6\n%62 = OpLabel\nOpBranch %64\n%64 = OpLabel\nOpSelectionMerge %65 None\nOpSwitch %13 %67 0 %66\n%66 = OpLabel\nOpBranch %65\n%67 = OpLabel\nOpBranch %65\n%65 = OpLabel\nOpSelectionMerge %68 None\nOpSwitch %13 %70 0 %69\n%69 = OpLabel\nOpReturn\n%70 = OpLabel\nOpReturn\n%68 = OpLabel\nOpReturn\nOpFunctionEnd\n%72 = OpFunction  %2  None %6\n%71 = OpLabel\nOpBranch %73\n%73 = OpLabel\nOpSelectionMerge %74 None\nOpSwitch %8 %80 0 %75 1 %76 2 %77 3 %78 4 %79\n%75 = OpLabel\nOpReturn\n%76 = OpLabel\nOpReturn\n%77 = OpLabel\nOpReturn\n%78 = OpLabel\nOpReturn\n%79 = OpLabel\nOpReturn\n%80 = OpLabel\nOpReturn\n%74 = OpLabel\nOpReturn\nOpFunctionEnd\n%83 = OpFunction  %2  None %52\n%82 = OpFunctionParameter  %3\n%81 = OpLabel\n%96 = OpVariable  %90  Function %95\nOpBranch %84\n%84 = OpLabel\nOpBranch %85\n%85 = OpLabel\nOpLoopMerge %86 %88 None\nOpBranch %97\n%97 = OpLabel\n%98 = OpLoad  %89  %96\n%99 = OpIEqual  %92  %93 %98\n%100 = OpAll  %91  %99\nOpSelectionMerge %101 None\nOpBranchConditional %100 %86 %101\n%101 = OpLabel\n%102 = OpCompositeExtract  %12  %98 1\n%103 = OpIEqual  %91  %102 %13\n%104 = OpSelect  %12  %103 %19 %13\n%105 = OpCompositeConstruct  %89  %104 %19\n%106 = OpISub  %89  %98 %105\nOpStore %96 %106\nOpBranch %87\n%87 = OpLabel\nOpSelectionMerge %107 None\nOpSwitch %82 %109 1 %108\n%108 = OpLabel\nOpBranch %88\n%109 = OpLabel\nOpBranch %107\n%107 = OpLabel\nOpBranch %88\n%88 = OpLabel\nOpBranch %85\n%86 = OpLabel\nOpReturn\nOpFunctionEnd\n%114 = OpFunction  %2  None %115\n%111 = OpFunctionParameter  %3\n%112 = OpFunctionParameter  %3\n%113 = OpFunctionParameter  %3\n%110 = OpLabel\n%121 = OpVariable  %90  Function %95\n%143 = OpVariable  %90  Function %95\n%163 = OpVariable  %90  Function %95\nOpBranch %116\n%116 = OpLabel\nOpBranch %117\n%117 = OpLabel\nOpLoopMerge %118 %120 None\nOpBranch %122\n%122 = OpLabel\n%123 = OpLoad  %89  %121\n%124 = OpIEqual  %92  %93 %123\n%125 = OpAll  %91  %124\nOpSelectionMerge %126 None\nOpBranchConditional %125 %118 %126\n%126 = OpLabel\n%127 = OpCompositeExtract  %12  %123 1\n%128 = OpIEqual  %91  %127 %13\n%129 = OpSelect  %12  %128 %19 %13\n%130 = OpCompositeConstruct  %89  %129 %19\n%131 = OpISub  %89  %123 %130\nOpStore %121 %131\nOpBranch %119\n%119 = OpLabel\nOpSelectionMerge %132 None\nOpSwitch %111 %135 1 %133 2 %134\n%133 = OpLabel\nOpBranch %120\n%134 = OpLabel\nOpSelectionMerge %136 None\nOpSwitch %112 %138 1 %137\n%137 = OpLabel\nOpBranch %120\n%138 = OpLabel\nOpBranch %139\n%139 = OpLabel\nOpLoopMerge %140 %142 None\nOpBranch %144\n%144 = OpLabel\n%145 = OpLoad  %89  %143\n%146 = OpIEqual  %92  %93 %145\n%147 = OpAll  %91  %146\nOpSelectionMerge %148 None\nOpBranchConditional %147 %140 %148\n%148 = OpLabel\n%149 = OpCompositeExtract  %12  %145 1\n%150 = OpIEqual  %91  %149 %13\n%151 = OpSelect  %12  %150 %19 %13\n%152 = OpCompositeConstruct  %89  %151 %19\n%153 = OpISub  %89  %145 %152\nOpStore %143 %153\nOpBranch %141\n%141 = OpLabel\nOpSelectionMerge %154 None\nOpSwitch %113 %156 1 %155\n%155 = OpLabel\nOpBranch %142\n%156 = OpLabel\nOpBranch %154\n%154 = OpLabel\nOpBranch %142\n%142 = OpLabel\nOpBranch %139\n%140 = OpLabel\nOpBranch %136\n%136 = OpLabel\nOpBranch %132\n%135 = OpLabel\nOpBranch %132\n%132 = OpLabel\nOpSelectionMerge %157 None\nOpSwitch %112 %158\n%158 = OpLabel\nOpBranch %120\n%157 = OpLabel\nOpBranch %120\n%120 = OpLabel\nOpBranch %117\n%118 = OpLabel\nOpBranch %159\n%159 = OpLabel\nOpLoopMerge %160 %162 None\nOpBranch %164\n%164 = OpLabel\n%165 = OpLoad  %89  %163\n%166 = OpIEqual  %92  %93 %165\n%167 = OpAll  %91  %166\nOpSelectionMerge %168 None\nOpBranchConditional %167 %160 %168\n%168 = OpLabel\n%169 = OpCompositeExtract  %12  %165 1\n%170 = OpIEqual  %91  %169 %13\n%171 = OpSelect  %12  %170 %19 %13\n%172 = OpCompositeConstruct  %89  %171 %19\n%173 = OpISub  %89  %165 %172\nOpStore %163 %173\nOpBranch %161\n%161 = OpLabel\nOpSelectionMerge %174 None\nOpSwitch %112 %175 1 %175\n%175 = OpLabel\nOpSelectionMerge %176 None\nOpSwitch %113 %177\n%177 = OpLabel\nOpBranch %162\n%176 = OpLabel\nOpBranch %174\n%174 = OpLabel\nOpBranch %162\n%162 = OpLabel\nOpBranch %159\n%160 = OpLabel\nOpReturn\nOpFunctionEnd\n%183 = OpFunction  %2  None %184\n%179 = OpFunctionParameter  %3\n%180 = OpFunctionParameter  %3\n%181 = OpFunctionParameter  %3\n%182 = OpFunctionParameter  %3\n%178 = OpLabel\n%185 = OpVariable  %15  Function %8\n%191 = OpVariable  %90  Function %95\n%209 = OpVariable  %90  Function %95\nOpBranch %186\n%186 = OpLabel\nOpBranch %187\n%187 = OpLabel\nOpLoopMerge %188 %190 None\nOpBranch %192\n%192 = OpLabel\n%193 = OpLoad  %89  %191\n%194 = OpIEqual  %92  %93 %193\n%195 = OpAll  %91  %194\nOpSelectionMerge %196 None\nOpBranchConditional %195 %188 %196\n%196 = OpLabel\n%197 = OpCompositeExtract  %12  %193 1\n%198 = OpIEqual  %91  %197 %13\n%199 = OpSelect  %12  %198 %19 %13\n%200 = OpCompositeConstruct  %89  %199 %19\n%201 = OpISub  %89  %193 %200\nOpStore %191 %201\nOpBranch %189\n%189 = OpLabel\nOpSelectionMerge %202 None\nOpSwitch %179 %204 1 %203\n%203 = OpLabel\nOpStore %185 %7\nOpBranch %202\n%204 = OpLabel\nOpBranch %202\n%202 = OpLabel\nOpBranch %190\n%190 = OpLabel\nOpBranch %187\n%188 = OpLabel\nOpBranch %205\n%205 = OpLabel\nOpLoopMerge %206 %208 None\nOpBranch %210\n%210 = OpLabel\n%211 = OpLoad  %89  %209\n%212 = OpIEqual  %92  %93 %211\n%213 = OpAll  %91  %212\nOpSelectionMerge %214 None\nOpBranchConditional %213 %206 %214\n%214 = OpLabel\n%215 = OpCompositeExtract  %12  %211 1\n%216 = OpIEqual  %91  %215 %13\n%217 = OpSelect  %12  %216 %19 %13\n%218 = OpCompositeConstruct  %89  %217 %19\n%219 = OpISub  %89  %211 %218\nOpStore %209 %219\nOpBranch %207\n%207 = OpLabel\nOpSelectionMerge %220 None\nOpSwitch %179 %223 1 %221 2 %222\n%221 = OpLabel\nOpBranch %220\n%222 = OpLabel\nOpSelectionMerge %224 None\nOpSwitch %180 %226 1 %225\n%225 = OpLabel\nOpBranch %208\n%226 = OpLabel\nOpSelectionMerge %227 None\nOpSwitch %181 %229 1 %228\n%228 = OpLabel\nOpStore %185 %9\nOpBranch %227\n%229 = OpLabel\nOpBranch %227\n%227 = OpLabel\nOpBranch %224\n%224 = OpLabel\nOpBranch %220\n%223 = OpLabel\nOpBranch %220\n%220 = OpLabel\nOpBranch %208\n%208 = OpLabel\nOpBranch %205\n%206 = OpLabel\nOpReturn\nOpFunctionEnd\n%231 = OpFunction  %2  None %6\n%230 = OpLabel\nOpBranch %232\n%232 = OpLabel\n%233 = OpFunctionCall  %2  %5\n%234 = OpFunctionCall  %2  %51 %7\n%235 = OpFunctionCall  %2  %57\n%236 = OpFunctionCall  %2  %63\n%237 = OpFunctionCall  %2  %72\n%238 = OpFunctionCall  %2  %83 %7\n%239 = OpFunctionCall  %2  %114 %7 %9 %10\n%240 = OpFunctionCall  %2  %183 %7 %9 %10 %11\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-conversion-float-to-int.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 314\nOpCapability Shader\nOpCapability Float16\nOpCapability StorageBuffer16BitAccess\nOpCapability UniformAndStorageBuffer16BitAccess\nOpCapability StorageInputOutput16\nOpCapability Float64\nOpCapability Int64\nOpExtension \"SPV_KHR_16bit_storage\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %278 \"main\"\nOpExecutionMode %278 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeFloat 16\n%4 = OpTypeFloat 32\n%5 = OpTypeFloat 64\n%6 = OpTypeInt 32 1\n%7 = OpTypeInt 32 0\n%8 = OpTypeInt 64 1\n%9 = OpTypeInt 64 0\n%10 = OpTypeVector %3 2\n%11 = OpTypeVector %6 2\n%12 = OpTypeVector %7 2\n%13 = OpTypeVector %8 2\n%14 = OpTypeVector %9 2\n%15 = OpTypeVector %4 2\n%16 = OpTypeVector %5 2\n%17 = OpConstant  %3  0.000000000000000000000000000000000000000090399\n%18 = OpConstant  %3  0.000000000000000000000000000000000000000044481\n%19 = OpConstant  %4  -340282350000000000000000000000000000000\n%20 = OpConstant  %4  340282350000000000000000000000000000000\n%21 = OpConstant  %5  -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n%22 = OpConstant  %5  179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n%25 = OpTypeFunction %2\n%26 = OpConstant  %6  -65504\n%27 = OpConstant  %6  65504\n%28 = OpConstant  %7  0\n%29 = OpConstant  %7  65504\n%30 = OpConstant  %8  -65504\n%31 = OpConstant  %8  65504\n%32 = OpConstant  %9  0\n%33 = OpConstant  %9  65504\n%34 = OpConstant  %6  -2147483648\n%35 = OpConstant  %6  2147483520\n%36 = OpConstant  %7  4294967040\n%37 = OpConstant  %8  -9223372036854775808\n%38 = OpConstant  %8  9223371487098961920\n%39 = OpConstant  %9  18446742974197923840\n%40 = OpConstant  %8  9223372036854774784\n%41 = OpConstant  %9  18446744073709549568\n%42 = OpConstant  %6  2147483647\n%43 = OpConstant  %7  4294967295\n%45 = OpTypePointer Function %6\n%48 = OpTypePointer Function %7\n%51 = OpTypePointer Function %8\n%54 = OpTypePointer Function %9\n%80 = OpTypeFunction %6 %3\n%87 = OpTypeFunction %7 %3\n%89 = OpConstant  %3  0\n%95 = OpTypeFunction %8 %3\n%102 = OpTypeFunction %9 %3\n%109 = OpTypeFunction %6 %4\n%111 = OpConstant  %4  -2147483600\n%112 = OpConstant  %4  2147483500\n%118 = OpTypeFunction %7 %4\n%120 = OpConstant  %4  0\n%121 = OpConstant  %4  4294967000\n%127 = OpTypeFunction %8 %4\n%129 = OpConstant  %4  -9223372000000000000\n%130 = OpConstant  %4  9223371500000000000\n%136 = OpTypeFunction %9 %4\n%138 = OpConstant  %4  18446743000000000000\n%144 = OpTypeFunction %6 %5\n%146 = OpConstant  %5  -2147483648\n%147 = OpConstant  %5  2147483647\n%153 = OpTypeFunction %7 %5\n%155 = OpConstant  %5  0\n%156 = OpConstant  %5  4294967295\n%162 = OpTypeFunction %8 %5\n%164 = OpConstant  %5  -9223372036854776000\n%165 = OpConstant  %5  9223372036854775000\n%171 = OpTypeFunction %9 %5\n%173 = OpConstant  %5  18446744073709550000\n%179 = OpTypeFunction %11 %10\n%181 = OpConstantComposite  %10  %17 %17\n%182 = OpConstantComposite  %10  %18 %18\n%188 = OpTypeFunction %12 %10\n%190 = OpConstantComposite  %10  %89 %89\n%196 = OpTypeFunction %13 %10\n%203 = OpTypeFunction %14 %10\n%210 = OpTypeFunction %11 %15\n%212 = OpConstantComposite  %15  %111 %111\n%213 = OpConstantComposite  %15  %112 %112\n%219 = OpTypeFunction %12 %15\n%221 = OpConstantComposite  %15  %120 %120\n%222 = OpConstantComposite  %15  %121 %121\n%228 = OpTypeFunction %13 %15\n%230 = OpConstantComposite  %15  %129 %129\n%231 = OpConstantComposite  %15  %130 %130\n%237 = OpTypeFunction %14 %15\n%239 = OpConstantComposite  %15  %138 %138\n%245 = OpTypeFunction %11 %16\n%247 = OpConstantComposite  %16  %146 %146\n%248 = OpConstantComposite  %16  %147 %147\n%254 = OpTypeFunction %12 %16\n%256 = OpConstantComposite  %16  %155 %155\n%257 = OpConstantComposite  %16  %156 %156\n%263 = OpTypeFunction %13 %16\n%265 = OpConstantComposite  %16  %164 %164\n%266 = OpConstantComposite  %16  %165 %165\n%272 = OpTypeFunction %14 %16\n%274 = OpConstantComposite  %16  %173 %173\n%279 = OpConstant  %3  0.000000000000000000000000000000000000000021524\n%280 = OpConstant  %4  1\n%281 = OpConstant  %5  1\n%282 = OpConstant  %3  0.000000000000000000000000000000000000000022959\n%283 = OpConstantComposite  %10  %279 %282\n%284 = OpConstant  %4  2\n%285 = OpConstantComposite  %15  %280 %284\n%286 = OpConstant  %5  2\n%287 = OpConstantComposite  %16  %281 %286\n%24 = OpFunction  %2  None %25\n%23 = OpLabel\n%73 = OpVariable  %51  Function %40\n%70 = OpVariable  %48  Function %28\n%67 = OpVariable  %54  Function %41\n%64 = OpVariable  %51  Function %37\n%61 = OpVariable  %51  Function %38\n%58 = OpVariable  %48  Function %28\n%55 = OpVariable  %54  Function %33\n%50 = OpVariable  %51  Function %30\n%46 = OpVariable  %45  Function %27\n%75 = OpVariable  %54  Function %41\n%72 = OpVariable  %51  Function %37\n%69 = OpVariable  %45  Function %42\n%66 = OpVariable  %54  Function %32\n%63 = OpVariable  %54  Function %39\n%60 = OpVariable  %51  Function %37\n%57 = OpVariable  %45  Function %35\n%53 = OpVariable  %54  Function %32\n%49 = OpVariable  %48  Function %29\n%44 = OpVariable  %45  Function %26\n%74 = OpVariable  %54  Function %32\n%71 = OpVariable  %48  Function %43\n%68 = OpVariable  %45  Function %34\n%65 = OpVariable  %51  Function %40\n%62 = OpVariable  %54  Function %32\n%59 = OpVariable  %48  Function %36\n%56 = OpVariable  %45  Function %34\n%52 = OpVariable  %51  Function %31\n%47 = OpVariable  %48  Function %28\nOpBranch %76\n%76 = OpLabel\nOpReturn\nOpFunctionEnd\n%79 = OpFunction  %6  None %80\n%78 = OpFunctionParameter  %3\n%77 = OpLabel\nOpBranch %81\n%81 = OpLabel\n%82 = OpExtInst  %3  %1 FClamp %78 %17 %18\n%83 = OpConvertFToS  %6  %82\nOpReturnValue %83\nOpFunctionEnd\n%86 = OpFunction  %7  None %87\n%85 = OpFunctionParameter  %3\n%84 = OpLabel\nOpBranch %88\n%88 = OpLabel\n%90 = OpExtInst  %3  %1 FClamp %85 %89 %18\n%91 = OpConvertFToU  %7  %90\nOpReturnValue %91\nOpFunctionEnd\n%94 = OpFunction  %8  None %95\n%93 = OpFunctionParameter  %3\n%92 = OpLabel\nOpBranch %96\n%96 = OpLabel\n%97 = OpExtInst  %3  %1 FClamp %93 %17 %18\n%98 = OpConvertFToS  %8  %97\nOpReturnValue %98\nOpFunctionEnd\n%101 = OpFunction  %9  None %102\n%100 = OpFunctionParameter  %3\n%99 = OpLabel\nOpBranch %103\n%103 = OpLabel\n%104 = OpExtInst  %3  %1 FClamp %100 %89 %18\n%105 = OpConvertFToU  %9  %104\nOpReturnValue %105\nOpFunctionEnd\n%108 = OpFunction  %6  None %109\n%107 = OpFunctionParameter  %4\n%106 = OpLabel\nOpBranch %110\n%110 = OpLabel\n%113 = OpExtInst  %4  %1 FClamp %107 %111 %112\n%114 = OpConvertFToS  %6  %113\nOpReturnValue %114\nOpFunctionEnd\n%117 = OpFunction  %7  None %118\n%116 = OpFunctionParameter  %4\n%115 = OpLabel\nOpBranch %119\n%119 = OpLabel\n%122 = OpExtInst  %4  %1 FClamp %116 %120 %121\n%123 = OpConvertFToU  %7  %122\nOpReturnValue %123\nOpFunctionEnd\n%126 = OpFunction  %8  None %127\n%125 = OpFunctionParameter  %4\n%124 = OpLabel\nOpBranch %128\n%128 = OpLabel\n%131 = OpExtInst  %4  %1 FClamp %125 %129 %130\n%132 = OpConvertFToS  %8  %131\nOpReturnValue %132\nOpFunctionEnd\n%135 = OpFunction  %9  None %136\n%134 = OpFunctionParameter  %4\n%133 = OpLabel\nOpBranch %137\n%137 = OpLabel\n%139 = OpExtInst  %4  %1 FClamp %134 %120 %138\n%140 = OpConvertFToU  %9  %139\nOpReturnValue %140\nOpFunctionEnd\n%143 = OpFunction  %6  None %144\n%142 = OpFunctionParameter  %5\n%141 = OpLabel\nOpBranch %145\n%145 = OpLabel\n%148 = OpExtInst  %5  %1 FClamp %142 %146 %147\n%149 = OpConvertFToS  %6  %148\nOpReturnValue %149\nOpFunctionEnd\n%152 = OpFunction  %7  None %153\n%151 = OpFunctionParameter  %5\n%150 = OpLabel\nOpBranch %154\n%154 = OpLabel\n%157 = OpExtInst  %5  %1 FClamp %151 %155 %156\n%158 = OpConvertFToU  %7  %157\nOpReturnValue %158\nOpFunctionEnd\n%161 = OpFunction  %8  None %162\n%160 = OpFunctionParameter  %5\n%159 = OpLabel\nOpBranch %163\n%163 = OpLabel\n%166 = OpExtInst  %5  %1 FClamp %160 %164 %165\n%167 = OpConvertFToS  %8  %166\nOpReturnValue %167\nOpFunctionEnd\n%170 = OpFunction  %9  None %171\n%169 = OpFunctionParameter  %5\n%168 = OpLabel\nOpBranch %172\n%172 = OpLabel\n%174 = OpExtInst  %5  %1 FClamp %169 %155 %173\n%175 = OpConvertFToU  %9  %174\nOpReturnValue %175\nOpFunctionEnd\n%178 = OpFunction  %11  None %179\n%177 = OpFunctionParameter  %10\n%176 = OpLabel\nOpBranch %180\n%180 = OpLabel\n%183 = OpExtInst  %10  %1 FClamp %177 %181 %182\n%184 = OpConvertFToS  %11  %183\nOpReturnValue %184\nOpFunctionEnd\n%187 = OpFunction  %12  None %188\n%186 = OpFunctionParameter  %10\n%185 = OpLabel\nOpBranch %189\n%189 = OpLabel\n%191 = OpExtInst  %10  %1 FClamp %186 %190 %182\n%192 = OpConvertFToU  %12  %191\nOpReturnValue %192\nOpFunctionEnd\n%195 = OpFunction  %13  None %196\n%194 = OpFunctionParameter  %10\n%193 = OpLabel\nOpBranch %197\n%197 = OpLabel\n%198 = OpExtInst  %10  %1 FClamp %194 %181 %182\n%199 = OpConvertFToS  %13  %198\nOpReturnValue %199\nOpFunctionEnd\n%202 = OpFunction  %14  None %203\n%201 = OpFunctionParameter  %10\n%200 = OpLabel\nOpBranch %204\n%204 = OpLabel\n%205 = OpExtInst  %10  %1 FClamp %201 %190 %182\n%206 = OpConvertFToU  %14  %205\nOpReturnValue %206\nOpFunctionEnd\n%209 = OpFunction  %11  None %210\n%208 = OpFunctionParameter  %15\n%207 = OpLabel\nOpBranch %211\n%211 = OpLabel\n%214 = OpExtInst  %15  %1 FClamp %208 %212 %213\n%215 = OpConvertFToS  %11  %214\nOpReturnValue %215\nOpFunctionEnd\n%218 = OpFunction  %12  None %219\n%217 = OpFunctionParameter  %15\n%216 = OpLabel\nOpBranch %220\n%220 = OpLabel\n%223 = OpExtInst  %15  %1 FClamp %217 %221 %222\n%224 = OpConvertFToU  %12  %223\nOpReturnValue %224\nOpFunctionEnd\n%227 = OpFunction  %13  None %228\n%226 = OpFunctionParameter  %15\n%225 = OpLabel\nOpBranch %229\n%229 = OpLabel\n%232 = OpExtInst  %15  %1 FClamp %226 %230 %231\n%233 = OpConvertFToS  %13  %232\nOpReturnValue %233\nOpFunctionEnd\n%236 = OpFunction  %14  None %237\n%235 = OpFunctionParameter  %15\n%234 = OpLabel\nOpBranch %238\n%238 = OpLabel\n%240 = OpExtInst  %15  %1 FClamp %235 %221 %239\n%241 = OpConvertFToU  %14  %240\nOpReturnValue %241\nOpFunctionEnd\n%244 = OpFunction  %11  None %245\n%243 = OpFunctionParameter  %16\n%242 = OpLabel\nOpBranch %246\n%246 = OpLabel\n%249 = OpExtInst  %16  %1 FClamp %243 %247 %248\n%250 = OpConvertFToS  %11  %249\nOpReturnValue %250\nOpFunctionEnd\n%253 = OpFunction  %12  None %254\n%252 = OpFunctionParameter  %16\n%251 = OpLabel\nOpBranch %255\n%255 = OpLabel\n%258 = OpExtInst  %16  %1 FClamp %252 %256 %257\n%259 = OpConvertFToU  %12  %258\nOpReturnValue %259\nOpFunctionEnd\n%262 = OpFunction  %13  None %263\n%261 = OpFunctionParameter  %16\n%260 = OpLabel\nOpBranch %264\n%264 = OpLabel\n%267 = OpExtInst  %16  %1 FClamp %261 %265 %266\n%268 = OpConvertFToS  %13  %267\nOpReturnValue %268\nOpFunctionEnd\n%271 = OpFunction  %14  None %272\n%270 = OpFunctionParameter  %16\n%269 = OpLabel\nOpBranch %273\n%273 = OpLabel\n%275 = OpExtInst  %16  %1 FClamp %270 %256 %274\n%276 = OpConvertFToU  %14  %275\nOpReturnValue %276\nOpFunctionEnd\n%278 = OpFunction  %2  None %25\n%277 = OpLabel\nOpBranch %288\n%288 = OpLabel\n%289 = OpFunctionCall  %2  %24\n%290 = OpFunctionCall  %6  %79 %279\n%291 = OpFunctionCall  %7  %86 %279\n%292 = OpFunctionCall  %8  %94 %279\n%293 = OpFunctionCall  %9  %101 %279\n%294 = OpFunctionCall  %6  %108 %280\n%295 = OpFunctionCall  %7  %117 %280\n%296 = OpFunctionCall  %8  %126 %280\n%297 = OpFunctionCall  %9  %135 %280\n%298 = OpFunctionCall  %6  %143 %281\n%299 = OpFunctionCall  %7  %152 %281\n%300 = OpFunctionCall  %8  %161 %281\n%301 = OpFunctionCall  %9  %170 %281\n%302 = OpFunctionCall  %11  %178 %283\n%303 = OpFunctionCall  %12  %187 %283\n%304 = OpFunctionCall  %13  %195 %283\n%305 = OpFunctionCall  %14  %202 %283\n%306 = OpFunctionCall  %11  %209 %285\n%307 = OpFunctionCall  %12  %218 %285\n%308 = OpFunctionCall  %13  %227 %285\n%309 = OpFunctionCall  %14  %236 %285\n%310 = OpFunctionCall  %11  %244 %287\n%311 = OpFunctionCall  %12  %253 %287\n%312 = OpFunctionCall  %13  %262 %287\n%313 = OpFunctionCall  %14  %271 %287\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-conversions.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 13\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %8 \"main\"\nOpExecutionMode %8 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 2\n%5 = OpConstant  %4  2\n%6 = OpConstantComposite  %3  %5 %5\n%9 = OpTypeFunction %2\n%11 = OpTypePointer Function %3\n%8 = OpFunction  %2  None %9\n%7 = OpLabel\n%10 = OpVariable  %11  Function %6\nOpBranch %12\n%12 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-cooperative-matrix.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 45\nOpCapability Shader\nOpCapability CooperativeMatrixKHR\nOpCapability VulkanMemoryModel\nOpExtension \"SPV_KHR_cooperative_matrix\"\nOpExtension \"SPV_KHR_vulkan_memory_model\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical Vulkan\nOpEntryPoint GLCompute %24 \"main\" %14 %17 %20\nOpExecutionMode %24 LocalSize 8 8 1\nOpDecorate %11 ArrayStride 4\nOpDecorate %20 DescriptorSet 0\nOpDecorate %20 Binding 0\nOpDecorate %21 Block\nOpMemberDecorate %21 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  3\n%7 = OpConstant  %6  8\n%8 = OpConstant  %6  0\n%4 = OpTypeCooperativeMatrixKHR %3 %5 %7 %7 %8\n%10 = OpConstant  %6  1\n%9 = OpTypeCooperativeMatrixKHR %3 %5 %7 %7 %10\n%11 = OpTypeRuntimeArray %3\n%13 = OpConstant  %6  2\n%12 = OpTypeCooperativeMatrixKHR %3 %5 %7 %7 %13\n%15 = OpTypePointer Private %4\n%16 = OpConstantNull  %4\n%14 = OpVariable  %15  Private %16\n%18 = OpTypePointer Private %9\n%19 = OpConstantNull  %9\n%17 = OpVariable  %18  Private %19\n%21 = OpTypeStruct %11\n%22 = OpTypePointer StorageBuffer %21\n%20 = OpVariable  %22  StorageBuffer\n%25 = OpTypeFunction %2\n%26 = OpTypePointer StorageBuffer %11\n%29 = OpTypePointer Function %12\n%30 = OpConstantNull  %12\n%32 = OpConstantNull  %12\n%34 = OpTypePointer StorageBuffer %3\n%35 = OpConstant  %6  4\n%24 = OpFunction  %2  None %25\n%23 = OpLabel\n%28 = OpVariable  %29  Function %30\n%31 = OpVariable  %29  Function %32\n%27 = OpAccessChain  %26  %20 %8\nOpBranch %33\n%33 = OpLabel\n%36 = OpAccessChain  %34  %27 %35\n%37 = OpCooperativeMatrixLoadKHR  %12  %36 %10 %7\nOpStore %28 %37\n%38 = OpLoad  %4  %14\n%39 = OpLoad  %9  %17\n%40 = OpLoad  %12  %28\n%41 = OpCooperativeMatrixMulAddKHR  %12  %38 %39 %40\nOpStore %31 %41\n%42 = OpLoad  %12  %31\n%43 = OpAccessChain  %34  %27 %8\nOpCooperativeMatrixStoreKHR %43 %42 %10 %7\n%44 = OpLoad  %12  %31\nOpStore %28 %44\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-cross.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 12\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %6 \"main\"\nOpExecutionMode %6 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 3\n%7 = OpTypeFunction %2\n%8 = OpConstant  %4  0\n%9 = OpConstant  %4  1\n%10 = OpConstantComposite  %3  %8 %8 %9\n%6 = OpFunction  %2  None %7\n%5 = OpLabel\nOpBranch %11\n%11 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-debug-symbol-large-source.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 682\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %367 \"gen_terrain_compute\" %364\nOpEntryPoint Vertex %444 \"gen_terrain_vertex\" %435 %438 %440 %442\nOpEntryPoint Fragment %495 \"gen_terrain_fragment\" %485 %487 %490 %493 %494\nOpEntryPoint Vertex %590 \"vs_main\" %581 %584 %586 %587 %589\nOpEntryPoint Fragment %615 \"fs_main\" %608 %610 %612 %614\nOpExecutionMode %367 LocalSize 64 1 1\nOpExecutionMode %495 OriginUpperLeft\nOpExecutionMode %615 OriginUpperLeft\n%3 = OpString \"debug-symbol-large-source.wgsl\"\nOpSource Unknown 0 %3 \"//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 T\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテス\"\nOpSourceContinued \"トだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是\"\nOpSourceContinued \"测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n/\"\nOpSourceContinued \"/This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテスト\"\nOpSourceContinued \"だ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测\"\nOpSourceContinued \"试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This\"\nOpSourceContinued \" is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ \"\nOpSourceContinued \"테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 \"\nOpSourceContinued \"これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This i\"\nOpSourceContinued \"s a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테\"\nOpSourceContinued \"스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 こ\"\nOpSourceContinued \"れはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a\"\nOpSourceContinued \" test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스\"\nOpSourceContinued \"트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これ\"\nOpSourceContinued \"はテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test\"\nOpSourceContinued \" 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트\"\nOpSourceContinued \"입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これは\"\nOpSourceContinued \"テストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test \"\nOpSourceContinued \"这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n\n\n// Taken from https://github.com/sotrh/learn-wgpu/blob/11820796f5e1dbce42fb1119f04ddeb4b167d2a0/code/intermediate/tutorial13-terrain/src/terrain.wgsl\n// ============================\n// Terrain Generation\n// ============================\n\n// https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39\n//  MIT License. © Ian McEwan, Stefan Gustavson, Munrocket\n// - Less condensed glsl implementation with comments can be found at https://weber.itn.liu.se/~stegu/jgt2012/article.pdf\n\nfn permute3(x: vec3<f32>) -> vec3<f32> { return (((x * 34.) + 1.) * x) % vec3<f32>(289.); }\n\nfn snoise2(v: vec2<f32>) -> f32 {\n    let C = vec4<f32>(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);\n    var i: vec2<f32> = floor(v + dot(v, C.yy));\n    let x0 = v - i + dot(i, C.xx);\n    // I flipped the condition here from > to < as it fixed some artifacting I was observing\n    var i1: vec2<f32> = select(vec2<f32>(1., 0.), vec2<f32>(0., 1.), (x0.x < x0.y));\n    var x12: vec4<f32> = x0.xyxy + C.xxzz - vec4<f32>(i1, 0., 0.);\n    i = i % vec2<f32>(289.);\n    let p = permute3(permute3(i.y + vec3<f32>(0., i1.y, 1.)) + i.x + vec3<f32>(0., i1.x, 1.));\n    var m: vec3<f32> = max(0.5 - vec3<f32>(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3<f32>(0.));\n    m = m * m;\n    m = m * m;\n    let x = 2. * fract(p * C.www) - 1.;\n    let h = abs(x) - 0.5;\n    //This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 T\"\nOpSourceContinued \"his is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ \"\nOpSourceContinued \"테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 \"\nOpSourceContinued \"これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This i\"\nOpSourceContinued \"s a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다\n    let ox = floor(x + 0.5);\n    let a0 = x - ox;\n    m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));\n    let g = vec3<f32>(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);\n    return 130. * dot(m, g);\n}\n\n\nfn fbm(p: vec2<f32>) -> f32 {\n    let NUM_OCTAVES: u32 = 5u;\n    var x = p * 0.01;\n    var v = 0.0;\n    var a = 0.5;\n    let shift = vec2<f32>(100.0);\n    let cs = vec2<f32>(cos(0.5), sin(0.5));\n    let rot = mat2x2<f32>(cs.x, cs.y, -cs.y, cs.x);\n\n    for (var i = 0u; i < NUM_OCTAVES; i = i + 1u) {\n        v = v + a * snoise2(x);\n        x = rot * x * 2.0 + shift;\n        a = a * 0.5;\n    }\n\n    return v;\n}\n\nstruct ChunkData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n}\n\nstruct Vertex {\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n}\n\nstruct VertexBuffer {\n    data: array<Vertex>, // stride: 32\n}\n\nstruct IndexBuffer {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) var<uniform> chunk_data: ChunkData;\n@group(0) @binding(1) var<storage, read_write> vertices: VertexBuffer;\n@group(0) @binding(2) var<storage, read_write> indices: IndexBuffer;\n\nfn terrain_point(p: vec2<f32>, min_max_height: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        p.x,\n        mix(min_max_height.x, min_max_height.y, fbm(p)),\n        p.y,\n    );\n}\n\nfn terrain_vertex(p: vec2<f32>, min_max_height: vec2<f32>) -> Vertex {\n    let v = terrain_point(p, min_max_height);\n\n    let tpx = terrain_point(p + vec2<f32>(0.1, 0.0), min_max_height) - v;\n    let tpz = terrain_point(p + vec2<f32>(0.0, 0.1), min_max_height) - v;\n    let tnx = terrain_point(p + vec2<f32>(-0.1, 0.0), min_max_height) - v;\n    let tnz = terrain_point(p + vec2<f32>(0.0, -0.1), min_max_height) - v;\n\n    let pn = normalize(cross(tpz, tpx));\n    let nn = normalize(cross(tnz, tnx));\n\n    let n = (pn + nn) * 0.5;\n\n    return Vertex(v, n);\n}\n\nfn index_to_p(vert_index: u32, chunk_size: vec2<u32>, chunk_corner: vec2<i32>) -> vec2<f32> {\n    return vec2(\n        f32(vert_index) % f32(chunk_size.x + 1u),\n        f32(vert_index / (chunk_size.x + 1u)),\n    ) + vec2<f32>(chunk_corner);\n}\n\n@compute @workgroup_size(64)\nfn gen_terrain_compute(\n    @builtin(global_invocation_id) gid: vec3<u32>\n) {\n    // Create vert_component\n    let vert_index = gid.x;\n\n    let p = index_to_p(vert_index, chunk_data.chunk_size, chunk_data.chunk_corner);\n\n    vertices.data[vert_index] = terrain_vertex(p, chunk_data.min_max_height);\n\n    // Create indices\n    let start_index = gid.x * 6u; // using TriangleList\n\n    if start_index >= (chunk_data.chunk_size.x * chunk_data.chunk_size.y * 6u) { return; }\n\n    let v00 = vert_index + gid.x / chunk_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + chunk_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    indices.data[start_index] = v00;\n    indices.data[start_index + 1u] = v01;\n    indices.data[start_index + 2u] = v11;\n    indices.data[start_index + 3u] = v00;\n    indices.data[start_index + 4u] = v11;\n    indices.data[start_index + 5u] = v10;\n}\n\n// ============================\n// Terrain Gen (Fragment Shader)\n// ============================\n\nstruct GenData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n    texture_size: u32,\n    start_index: u32,\n}\n@group(0)\n@binding(0)\nvar<uniform> gen_data: GenData;\n\nstruct GenVertexOutput {\n    @location(0)\n    index: u32,\n    @builtin(position)\n    position: vec4<f32>,\n    @location(1)\n    uv: vec2<f32>,\n};\n\n@vertex\nfn gen_terrain_vertex(@builtin(vertex_index) vindex: u32) -> GenVertexOutput {\n    let u = f32(((vindex + 2u) / 3u) % 2u);\n    let v = f32(((vindex + 1u) / 3u) % 2u);\n    let uv = vec2<f32>(u, v);\n\n    let position = vec4<f32>(-1.0 + uv * 2.0, 0.0, 1.0);\n\n    // TODO: maybe replace this with u32(dot(uv, vec2(f32(gen_data.texture_dim.x))))\n    let index = u32(uv.x * f32(gen_data.texture_size) + uv.y * f32(gen_data.texture_size)) + gen_data.start_index;\n\n    return GenVertexOutput(index, position, uv);\n}\n\n\nstruct GenFragmentOutput {\n    @location(0) vert_component: u32,\n    @location(1) index: u32,\n}\n\n@fragment\nfn gen_terrain_fragment(in: GenVertexOutput) -> GenFragmentOutput {\n    let i = u32(in.uv.x * f32(gen_data.texture_size) + in.uv.y * f32(gen_data.texture_size * gen_data.texture_size)) + gen_data.start_index;\n    let vert_index = u32(floor(f32(i) / 6.));\n    let comp_index = i % 6u;\n\n    let p = index_to_p(vert_index, gen_data.chunk_size, gen_data.chunk_corner);\n    let v = terrain_vertex(p, gen_data.min_max_height);\n\n    var vert_component: f32 = 0.;\n\n    switch comp_index {\n        case 0u: { vert_component = v.position.x; }\n        case 1u: { vert_component = v.position.y; }\n        case 2u: { vert_component = v.position.z; }\n        case 3u: { vert_component = v.normal.x; }\n        case 4u: { vert_component = v.normal.y; }\n        case 5u: { vert_component = v.normal.z; }\n        default: {}\n    }\n\n    let v00 = vert_index + vert_index / gen_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + gen_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    var index = 0u;\n    switch comp_index {\n        case 0u, 3u: { index = v00; }\n        case 2u, 4u: { index = v11; }\n        case 1u: { index = v01; }\n        case 5u: { index = v10; }\n        default: {}\n    }\n    index = in.index;\n    // index = gen_data.start_index;\n    // indices.data[start_index] = v00;\n    // indices.data[start_index + 1u] = v01;\n    // indices.data[start_index + 2u] = v11;\n    // indices.data[start_index + 3u] = v00;\n    // indices.data[start_index + 4u] = v11;\n    // indices.data[start_index + 5u] = v10;\n\n    let ivert_component = bitcast<u32>(vert_component);\n    return GenFragmentOutput(ivert_component, index);\n}\n\n// ============================\n// Terrain Rendering\n// ============================\n\nstruct Camera {\n    view_pos: vec4<f32>,\n    view_proj: mat4x4<f32>,\n}\n@group(0) @binding(0)\nvar<uniform> camera: Camera;\n\nstruct Light {\n    position: vec3<f32>,\n    color: vec3<f32>,\n}\n@group(1) @binding(0)\nvar<uniform> light: Light;\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) normal: vec3<f32>,\n    @location(1) world_pos: vec3<f32>,\n}\n\n@vertex\nfn vs_main(\n    vertex: Vertex,\n) -> VertexOutput {\n    let clip_position = camera.view_proj * vec4<f32>(vertex.position, 1.);\n    let normal = vertex.normal;\n    return VertexOutput(clip_position, normal, vertex.position);\n}\n\n@group(2) @binding(0)\nvar t_diffuse: texture_2d<f32>;\n@group(2) @binding(1)\nvar s_diffuse: sampler;\n@group(2) @binding(2)\nvar t_normal: texture_2d<f32>;\n@group(2) @binding(3)\nvar s_normal: sampler;\n\nfn color23(p: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        snoise2(p) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(23., 32.)) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(-43., 3.)) * 0.5 + 0.5,\n    );\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    _ = t_diffuse;\n    _ = s_diffuse;\n    _ = t_normal;\n    _ = s_normal;\n\n    color23(vec2(1, 2));\n\n    var color = smoothstep(vec3<f32>(0.0), vec3<f32>(0.1), fract(in.world_pos));\n    color = mix(vec3<f32>(0.5, 0.1, 0.7), vec3<f32>(0.2, 0.2, 0.2), vec3<f32>(color.x * color.y * color.z));\n\n    let ambient_strength = 0.1;\n    let ambient_color = light.color * ambient_strength;\n\n    let light_dir = normalize(light.position - in.world_pos);\n    let view_dir = normalize(camera.view_pos.xyz - in.world_pos);\n    let half_dir = normalize(view_dir + light_dir);\n\n    let diffuse_strength = max(dot(in.normal, light_dir), 0.0);\n    let diffuse_color = diffuse_strength * light.color;\n\n    let specular_strength = pow(max(dot(in.normal, half_dir), 0.0), 32.0);\n    let specular_color = specular_strength * light.color;\n\n    let result = (ambient_color + diffuse_color + specular_color) * color;\n\n    return vec4<f32>(result, 1.0);\n}\n\"\nOpMemberName %13 0 \"chunk_size\"\nOpMemberName %13 1 \"chunk_corner\"\nOpMemberName %13 2 \"min_max_height\"\nOpName %13 \"ChunkData\"\nOpMemberName %14 0 \"position\"\nOpMemberName %14 1 \"normal\"\nOpName %14 \"Vertex\"\nOpMemberName %16 0 \"data\"\nOpName %16 \"VertexBuffer\"\nOpMemberName %18 0 \"data\"\nOpName %18 \"IndexBuffer\"\nOpMemberName %20 0 \"chunk_size\"\nOpMemberName %20 1 \"chunk_corner\"\nOpMemberName %20 2 \"min_max_height\"\nOpMemberName %20 3 \"texture_size\"\nOpMemberName %20 4 \"start_index\"\nOpName %20 \"GenData\"\nOpMemberName %21 0 \"index\"\nOpMemberName %21 1 \"position\"\nOpMemberName %21 2 \"uv\"\nOpName %21 \"GenVertexOutput\"\nOpMemberName %22 0 \"vert_component\"\nOpMemberName %22 1 \"index\"\nOpName %22 \"GenFragmentOutput\"\nOpMemberName %24 0 \"view_pos\"\nOpMemberName %24 1 \"view_proj\"\nOpName %24 \"Camera\"\nOpMemberName %25 0 \"position\"\nOpMemberName %25 1 \"color\"\nOpName %25 \"Light\"\nOpMemberName %26 0 \"clip_position\"\nOpMemberName %26 1 \"normal\"\nOpMemberName %26 2 \"world_pos\"\nOpName %26 \"VertexOutput\"\nOpName %29 \"chunk_data\"\nOpName %32 \"vertices\"\nOpName %34 \"indices\"\nOpName %36 \"gen_data\"\nOpName %39 \"camera\"\nOpName %42 \"light\"\nOpName %45 \"t_diffuse\"\nOpName %47 \"s_diffuse\"\nOpName %49 \"t_normal\"\nOpName %50 \"s_normal\"\nOpName %52 \"x\"\nOpName %53 \"permute3\"\nOpName %66 \"v\"\nOpName %67 \"snoise2\"\nOpName %86 \"i\"\nOpName %89 \"i1\"\nOpName %91 \"x12\"\nOpName %94 \"m\"\nOpName %203 \"p\"\nOpName %204 \"fbm\"\nOpName %212 \"x\"\nOpName %214 \"v\"\nOpName %215 \"a\"\nOpName %216 \"i\"\nOpName %236 \"loop_bound\"\nOpName %269 \"p\"\nOpName %270 \"min_max_height\"\nOpName %271 \"terrain_point\"\nOpName %282 \"p\"\nOpName %283 \"min_max_height\"\nOpName %284 \"terrain_vertex\"\nOpName %313 \"naga_div\"\nOpName %315 \"lhs\"\nOpName %316 \"rhs\"\nOpName %322 \"vert_index\"\nOpName %323 \"chunk_size\"\nOpName %324 \"chunk_corner\"\nOpName %325 \"index_to_p\"\nOpName %341 \"p\"\nOpName %342 \"color23\"\nOpName %364 \"gid\"\nOpName %367 \"gen_terrain_compute\"\nOpName %427 \"naga_mod\"\nOpName %428 \"lhs\"\nOpName %429 \"rhs\"\nOpName %435 \"vindex\"\nOpName %438 \"index\"\nOpName %440 \"position\"\nOpName %442 \"uv\"\nOpName %444 \"gen_terrain_vertex\"\nOpName %485 \"index\"\nOpName %487 \"position\"\nOpName %490 \"uv\"\nOpName %493 \"vert_component\"\nOpName %494 \"index\"\nOpName %495 \"gen_terrain_fragment\"\nOpName %498 \"vert_component\"\nOpName %499 \"index\"\nOpName %581 \"position\"\nOpName %584 \"normal\"\nOpName %586 \"clip_position\"\nOpName %587 \"normal\"\nOpName %589 \"world_pos\"\nOpName %590 \"vs_main\"\nOpName %608 \"clip_position\"\nOpName %610 \"normal\"\nOpName %612 \"world_pos\"\nOpName %615 \"fs_main\"\nOpName %629 \"color\"\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 8\nOpMemberDecorate %13 2 Offset 16\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 16\nOpDecorate %15 ArrayStride 32\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %16 Block\nOpDecorate %17 ArrayStride 4\nOpMemberDecorate %18 0 Offset 0\nOpDecorate %18 Block\nOpMemberDecorate %20 0 Offset 0\nOpMemberDecorate %20 1 Offset 8\nOpMemberDecorate %20 2 Offset 16\nOpMemberDecorate %20 3 Offset 24\nOpMemberDecorate %20 4 Offset 28\nOpMemberDecorate %21 0 Offset 0\nOpMemberDecorate %21 1 Offset 16\nOpMemberDecorate %21 2 Offset 32\nOpMemberDecorate %22 0 Offset 0\nOpMemberDecorate %22 1 Offset 4\nOpMemberDecorate %24 0 Offset 0\nOpMemberDecorate %24 1 Offset 16\nOpMemberDecorate %24 1 ColMajor\nOpMemberDecorate %24 1 MatrixStride 16\nOpMemberDecorate %25 0 Offset 0\nOpMemberDecorate %25 1 Offset 16\nOpMemberDecorate %26 0 Offset 0\nOpMemberDecorate %26 1 Offset 16\nOpMemberDecorate %26 2 Offset 32\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 0\nOpDecorate %30 Block\nOpMemberDecorate %30 0 Offset 0\nOpDecorate %32 DescriptorSet 0\nOpDecorate %32 Binding 1\nOpDecorate %34 DescriptorSet 0\nOpDecorate %34 Binding 2\nOpDecorate %36 DescriptorSet 0\nOpDecorate %36 Binding 0\nOpDecorate %37 Block\nOpMemberDecorate %37 0 Offset 0\nOpDecorate %39 DescriptorSet 0\nOpDecorate %39 Binding 0\nOpDecorate %40 Block\nOpMemberDecorate %40 0 Offset 0\nOpDecorate %42 DescriptorSet 1\nOpDecorate %42 Binding 0\nOpDecorate %43 Block\nOpMemberDecorate %43 0 Offset 0\nOpDecorate %45 DescriptorSet 2\nOpDecorate %45 Binding 0\nOpDecorate %47 DescriptorSet 2\nOpDecorate %47 Binding 1\nOpDecorate %49 DescriptorSet 2\nOpDecorate %49 Binding 2\nOpDecorate %50 DescriptorSet 2\nOpDecorate %50 Binding 3\nOpDecorate %364 BuiltIn GlobalInvocationId\nOpDecorate %435 BuiltIn VertexIndex\nOpDecorate %438 Location 0\nOpDecorate %438 Flat\nOpDecorate %440 BuiltIn Position\nOpDecorate %442 Location 1\nOpDecorate %485 Location 0\nOpDecorate %485 Flat\nOpDecorate %487 BuiltIn FragCoord\nOpDecorate %490 Location 1\nOpDecorate %493 Location 0\nOpDecorate %494 Location 1\nOpDecorate %581 Location 0\nOpDecorate %584 Location 1\nOpDecorate %586 BuiltIn Position\nOpDecorate %587 Location 0\nOpDecorate %589 Location 1\nOpDecorate %608 BuiltIn FragCoord\nOpDecorate %610 Location 0\nOpDecorate %612 Location 1\nOpDecorate %614 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 3\n%6 = OpTypeVector %4 2\n%7 = OpTypeVector %4 4\n%8 = OpTypeInt 32 0\n%9 = OpTypeMatrix %6 2\n%10 = OpTypeVector %8 2\n%12 = OpTypeInt 32 1\n%11 = OpTypeVector %12 2\n%13 = OpTypeStruct %10 %11 %6\n%14 = OpTypeStruct %5 %5\n%15 = OpTypeRuntimeArray %14\n%16 = OpTypeStruct %15\n%17 = OpTypeRuntimeArray %8\n%18 = OpTypeStruct %17\n%19 = OpTypeVector %8 3\n%20 = OpTypeStruct %10 %11 %6 %8 %8\n%21 = OpTypeStruct %8 %7 %6\n%22 = OpTypeStruct %8 %8\n%23 = OpTypeMatrix %7 4\n%24 = OpTypeStruct %7 %23\n%25 = OpTypeStruct %5 %5\n%26 = OpTypeStruct %7 %5 %5\n%27 = OpTypeImage %4 2D 0 0 0 1 Unknown\n%28 = OpTypeSampler\n%30 = OpTypeStruct %13\n%31 = OpTypePointer Uniform %30\n%29 = OpVariable  %31  Uniform\n%33 = OpTypePointer StorageBuffer %16\n%32 = OpVariable  %33  StorageBuffer\n%35 = OpTypePointer StorageBuffer %18\n%34 = OpVariable  %35  StorageBuffer\n%37 = OpTypeStruct %20\n%38 = OpTypePointer Uniform %37\n%36 = OpVariable  %38  Uniform\n%40 = OpTypeStruct %24\n%41 = OpTypePointer Uniform %40\n%39 = OpVariable  %41  Uniform\n%43 = OpTypeStruct %25\n%44 = OpTypePointer Uniform %43\n%42 = OpVariable  %44  Uniform\n%46 = OpTypePointer UniformConstant %27\n%45 = OpVariable  %46  UniformConstant\n%48 = OpTypePointer UniformConstant %28\n%47 = OpVariable  %48  UniformConstant\n%49 = OpVariable  %46  UniformConstant\n%50 = OpVariable  %48  UniformConstant\n%54 = OpTypeFunction %5 %5\n%55 = OpConstant  %4  34\n%56 = OpConstant  %4  1\n%57 = OpConstantComposite  %5  %56 %56 %56\n%58 = OpConstant  %4  289\n%59 = OpConstantComposite  %5  %58 %58 %58\n%68 = OpTypeFunction %4 %6\n%69 = OpConstant  %4  0.21132487\n%70 = OpConstant  %4  0.36602542\n%71 = OpConstant  %4  -0.57735026\n%72 = OpConstant  %4  0.024390243\n%73 = OpConstantComposite  %7  %69 %70 %71 %72\n%74 = OpConstant  %4  0\n%75 = OpConstantComposite  %6  %56 %74\n%76 = OpConstantComposite  %6  %74 %56\n%77 = OpConstantComposite  %6  %58 %58\n%78 = OpConstant  %4  0.5\n%79 = OpConstantComposite  %5  %78 %78 %78\n%80 = OpConstantComposite  %5  %74 %74 %74\n%81 = OpConstant  %4  2\n%82 = OpConstant  %4  0.85373473\n%83 = OpConstant  %4  1.7928429\n%84 = OpConstantComposite  %5  %83 %83 %83\n%85 = OpConstant  %4  130\n%87 = OpTypePointer Function %6\n%88 = OpConstantNull  %6\n%90 = OpConstantNull  %6\n%92 = OpTypePointer Function %7\n%93 = OpConstantNull  %7\n%95 = OpTypePointer Function %5\n%96 = OpConstantNull  %5\n%112 = OpTypeBool\n%115 = OpTypeVector %112 2\n%125 = OpTypePointer Function %4\n%126 = OpConstant  %8  1\n%135 = OpConstant  %8  0\n%205 = OpConstant  %8  5\n%206 = OpConstant  %4  0.01\n%207 = OpConstant  %4  100\n%208 = OpConstantComposite  %6  %207 %207\n%209 = OpConstant  %4  0.87758255\n%210 = OpConstant  %4  0.47942555\n%211 = OpConstantComposite  %6  %209 %210\n%213 = OpConstantNull  %6\n%217 = OpTypePointer Function %8\n%232 = OpTypePointer Function %10\n%233 = OpConstantComposite  %10  %135 %135\n%234 = OpConstant  %8  4294967295\n%235 = OpConstantComposite  %10  %234 %234\n%272 = OpTypeFunction %5 %6 %6\n%285 = OpTypeFunction %14 %6 %6\n%286 = OpConstant  %4  0.1\n%287 = OpConstantComposite  %6  %286 %74\n%288 = OpConstantComposite  %6  %74 %286\n%289 = OpConstant  %4  -0.1\n%290 = OpConstantComposite  %6  %289 %74\n%291 = OpConstantComposite  %6  %74 %289\n%314 = OpTypeFunction %8 %8 %8\n%326 = OpTypeFunction %6 %8 %10 %11\n%343 = OpTypeFunction %5 %6\n%344 = OpConstant  %4  23\n%345 = OpConstant  %4  32\n%346 = OpConstantComposite  %6  %344 %345\n%347 = OpConstant  %4  -43\n%348 = OpConstant  %4  3\n%349 = OpConstantComposite  %6  %347 %348\n%365 = OpTypePointer Input %19\n%364 = OpVariable  %365  Input\n%368 = OpTypeFunction %2\n%369 = OpTypePointer Uniform %13\n%371 = OpConstant  %8  6\n%372 = OpConstant  %8  2\n%373 = OpConstant  %8  3\n%374 = OpConstant  %8  4\n%377 = OpTypePointer Uniform %10\n%380 = OpTypePointer Uniform %11\n%384 = OpTypePointer StorageBuffer %15\n%385 = OpTypePointer StorageBuffer %14\n%386 = OpTypePointer Uniform %6\n%393 = OpTypePointer Uniform %8\n%414 = OpTypePointer StorageBuffer %17\n%415 = OpTypePointer StorageBuffer %8\n%436 = OpTypePointer Input %8\n%435 = OpVariable  %436  Input\n%439 = OpTypePointer Output %8\n%438 = OpVariable  %439  Output\n%441 = OpTypePointer Output %7\n%440 = OpVariable  %441  Output\n%443 = OpTypePointer Output %6\n%442 = OpVariable  %443  Output\n%445 = OpTypePointer Uniform %20\n%447 = OpConstant  %4  -1\n%448 = OpConstantComposite  %6  %447 %447\n%473 = OpConstant  %4  4294967000\n%485 = OpVariable  %436  Input\n%488 = OpTypePointer Input %7\n%487 = OpVariable  %488  Input\n%491 = OpTypePointer Input %6\n%490 = OpVariable  %491  Input\n%493 = OpVariable  %439  Output\n%494 = OpVariable  %439  Output\n%497 = OpConstant  %4  6\n%582 = OpTypePointer Input %5\n%581 = OpVariable  %582  Input\n%584 = OpVariable  %582  Input\n%586 = OpVariable  %441  Output\n%588 = OpTypePointer Output %5\n%587 = OpVariable  %588  Output\n%589 = OpVariable  %588  Output\n%591 = OpTypePointer Uniform %24\n%594 = OpTypePointer Uniform %23\n%608 = OpVariable  %488  Input\n%610 = OpVariable  %582  Input\n%612 = OpVariable  %582  Input\n%614 = OpVariable  %441  Output\n%617 = OpTypePointer Uniform %25\n%623 = OpConstantComposite  %6  %56 %81\n%624 = OpConstantComposite  %5  %286 %286 %286\n%625 = OpConstant  %4  0.7\n%626 = OpConstantComposite  %5  %78 %286 %625\n%627 = OpConstant  %4  0.2\n%628 = OpConstantComposite  %5  %627 %627 %627\n%630 = OpConstantNull  %5\n%646 = OpTypePointer Uniform %5\n%655 = OpTypePointer Uniform %7\n%53 = OpFunction  %5  None %54\n%52 = OpFunctionParameter  %5\n%51 = OpLabel\nOpBranch %60\n%60 = OpLabel\nOpLine %3 5734 52\n%61 = OpVectorTimesScalar  %5  %52 %55\nOpLine %3 5734 63\nOpLine %3 5734 50\n%62 = OpFAdd  %5  %61 %57\n%63 = OpFMul  %5  %62 %52\nOpLine %3 5734 49\n%64 = OpFRem  %5  %63 %59\nOpReturnValue %64\nOpFunctionEnd\n%67 = OpFunction  %4  None %68\n%66 = OpFunctionParameter  %6\n%65 = OpLabel\n%89 = OpVariable  %87  Function %90\n%94 = OpVariable  %95  Function %96\n%86 = OpVariable  %87  Function %88\n%91 = OpVariable  %92  Function %93\nOpBranch %97\n%97 = OpLabel\nOpLine %3 5737 13\nOpLine %3 5738 24\n%98 = OpVectorShuffle  %6  %73 %73 1 1\n%99 = OpDot  %4  %66 %98\n%100 = OpCompositeConstruct  %6  %99 %99\n%101 = OpFAdd  %6  %66 %100\n%102 = OpExtInst  %6  %1 Floor %101\nOpLine %3 5738 5\nOpStore %86 %102\nOpLine %3 5739 14\n%103 = OpLoad  %6  %86\n%104 = OpFSub  %6  %66 %103\n%105 = OpLoad  %6  %86\n%106 = OpVectorShuffle  %6  %73 %73 0 0\n%107 = OpDot  %4  %105 %106\n%108 = OpCompositeConstruct  %6  %107 %107\n%109 = OpFAdd  %6  %104 %108\nOpLine %3 5741 32\nOpLine %3 5741 25\n%110 = OpCompositeExtract  %4  %109 0\n%111 = OpCompositeExtract  %4  %109 1\n%113 = OpFOrdLessThan  %112  %110 %111\n%116 = OpCompositeConstruct  %115  %113 %113\n%114 = OpSelect  %6  %116 %76 %75\nOpLine %3 5741 5\nOpStore %89 %114\nOpLine %3 5742 26\n%117 = OpVectorShuffle  %7  %109 %109 0 1 0 1\n%118 = OpVectorShuffle  %7  %73 %73 0 0 2 2\n%119 = OpFAdd  %7  %117 %118\n%120 = OpLoad  %6  %89\nOpLine %3 5742 26\n%121 = OpCompositeConstruct  %7  %120 %74 %74\n%122 = OpFSub  %7  %119 %121\nOpLine %3 5742 5\nOpStore %91 %122\nOpLine %3 1 1\n%123 = OpLoad  %6  %86\nOpLine %3 5743 9\n%124 = OpFRem  %6  %123 %77\nOpLine %3 5743 5\nOpStore %86 %124\nOpLine %3 5744 31\n%127 = OpAccessChain  %125  %86 %126\n%128 = OpLoad  %4  %127\nOpLine %3 5744 51\n%129 = OpAccessChain  %125  %89 %126\n%130 = OpLoad  %4  %129\nOpLine %3 5744 31\n%131 = OpCompositeConstruct  %5  %74 %130 %56\n%132 = OpCompositeConstruct  %5  %128 %128 %128\n%133 = OpFAdd  %5  %132 %131\nOpLine %3 5744 22\n%134 = OpFunctionCall  %5  %53 %133\nOpLine %3 5744 22\n%136 = OpAccessChain  %125  %86 %135\n%137 = OpLoad  %4  %136\n%138 = OpCompositeConstruct  %5  %137 %137 %137\n%139 = OpFAdd  %5  %134 %138\nOpLine %3 5744 84\n%140 = OpAccessChain  %125  %89 %135\n%141 = OpLoad  %4  %140\nOpLine %3 5744 22\n%142 = OpCompositeConstruct  %5  %74 %141 %56\n%143 = OpFAdd  %5  %139 %142\nOpLine %3 5744 13\n%144 = OpFunctionCall  %5  %53 %143\nOpLine %3 5745 28\n%145 = OpDot  %4  %109 %109\n%146 = OpLoad  %7  %91\n%147 = OpVectorShuffle  %6  %146 %146 0 1\n%148 = OpLoad  %7  %91\n%149 = OpVectorShuffle  %6  %148 %148 0 1\n%150 = OpDot  %4  %147 %149\n%151 = OpLoad  %7  %91\n%152 = OpVectorShuffle  %6  %151 %151 2 3\n%153 = OpLoad  %7  %91\n%154 = OpVectorShuffle  %6  %153 %153 2 3\n%155 = OpDot  %4  %152 %154\n%156 = OpCompositeConstruct  %5  %145 %150 %155\nOpLine %3 5745 28\n%157 = OpFSub  %5  %79 %156\nOpLine %3 5745 24\n%158 = OpExtInst  %5  %1 FMax %157 %80\nOpLine %3 5745 5\nOpStore %94 %158\nOpLine %3 5746 9\n%159 = OpLoad  %5  %94\n%160 = OpLoad  %5  %94\n%161 = OpFMul  %5  %159 %160\nOpLine %3 5746 5\nOpStore %94 %161\nOpLine %3 5747 9\n%162 = OpLoad  %5  %94\n%163 = OpLoad  %5  %94\n%164 = OpFMul  %5  %162 %163\nOpLine %3 5747 5\nOpStore %94 %164\nOpLine %3 5748 18\n%165 = OpVectorShuffle  %5  %73 %73 3 3 3\n%166 = OpFMul  %5  %144 %165\n%167 = OpExtInst  %5  %1 Fract %166\nOpLine %3 5748 13\n%168 = OpVectorTimesScalar  %5  %167 %81\nOpLine %3 5748 37\nOpLine %3 5748 13\n%169 = OpFSub  %5  %168 %57\nOpLine %3 5749 13\n%170 = OpExtInst  %5  %1 FAbs %169\nOpLine %3 5749 22\nOpLine %3 5749 13\n%171 = OpFSub  %5  %170 %79\nOpLine %3 7169 24\nOpLine %3 7169 14\n%172 = OpFAdd  %5  %169 %79\n%173 = OpExtInst  %5  %1 Floor %172\nOpLine %3 7170 14\n%174 = OpFSub  %5  %169 %173\nOpLine %3 1 1\n%175 = OpLoad  %5  %94\nOpLine %3 7171 53\n%176 = OpFMul  %5  %174 %174\n%177 = OpFMul  %5  %171 %171\n%178 = OpFAdd  %5  %176 %177\nOpLine %3 7171 14\n%179 = OpVectorTimesScalar  %5  %178 %82\nOpLine %3 7171 9\n%180 = OpFSub  %5  %84 %179\n%181 = OpFMul  %5  %175 %180\nOpLine %3 7171 5\nOpStore %94 %181\nOpLine %3 7172 13\n%182 = OpCompositeExtract  %4  %174 0\n%183 = OpCompositeExtract  %4  %109 0\n%184 = OpFMul  %4  %182 %183\n%185 = OpCompositeExtract  %4  %171 0\n%186 = OpCompositeExtract  %4  %109 1\n%187 = OpFMul  %4  %185 %186\n%188 = OpFAdd  %4  %184 %187\n%189 = OpVectorShuffle  %6  %174 %174 1 2\n%190 = OpLoad  %7  %91\n%191 = OpVectorShuffle  %6  %190 %190 0 2\n%192 = OpFMul  %6  %189 %191\n%193 = OpVectorShuffle  %6  %171 %171 1 2\n%194 = OpLoad  %7  %91\n%195 = OpVectorShuffle  %6  %194 %194 1 3\n%196 = OpFMul  %6  %193 %195\n%197 = OpFAdd  %6  %192 %196\n%198 = OpCompositeConstruct  %5  %188 %197\nOpLine %3 7173 19\n%199 = OpLoad  %5  %94\n%200 = OpDot  %4  %199 %198\nOpLine %3 7173 12\n%201 = OpFMul  %4  %85 %200\nOpReturnValue %201\nOpFunctionEnd\n%204 = OpFunction  %4  None %68\n%203 = OpFunctionParameter  %6\n%202 = OpLabel\n%214 = OpVariable  %125  Function %74\n%216 = OpVariable  %217  Function %135\n%212 = OpVariable  %87  Function %213\n%215 = OpVariable  %125  Function %78\n%236 = OpVariable  %232  Function %235\nOpBranch %218\n%218 = OpLabel\nOpLine %3 7179 13\n%219 = OpVectorTimesScalar  %6  %203 %206\nOpLine %3 7179 5\nOpStore %212 %219\nOpLine %3 7182 17\nOpLine %3 7183 14\nOpLine %3 7184 15\n%220 = OpCompositeExtract  %4  %211 0\n%221 = OpCompositeExtract  %4  %211 1\n%222 = OpCompositeExtract  %4  %211 1\n%223 = OpFNegate  %4  %222\n%224 = OpCompositeExtract  %4  %211 0\n%225 = OpCompositeConstruct  %6  %220 %221\n%226 = OpCompositeConstruct  %6  %223 %224\n%227 = OpCompositeConstruct  %9  %225 %226\nOpBranch %228\n%228 = OpLabel\nOpLine %3 7186 5\nOpLoopMerge %229 %231 None\nOpBranch %237\n%237 = OpLabel\n%238 = OpLoad  %10  %236\n%239 = OpIEqual  %115  %233 %238\n%240 = OpAll  %112  %239\nOpSelectionMerge %241 None\nOpBranchConditional %240 %229 %241\n%241 = OpLabel\n%242 = OpCompositeExtract  %8  %238 1\n%243 = OpIEqual  %112  %242 %135\n%244 = OpSelect  %8  %243 %126 %135\n%245 = OpCompositeConstruct  %10  %244 %126\n%246 = OpISub  %10  %238 %245\nOpStore %236 %246\nOpBranch %230\n%230 = OpLabel\nOpLine %3 7186 22\n%247 = OpLoad  %8  %216\n%248 = OpULessThan  %112  %247 %205\nOpLine %3 7186 21\nOpSelectionMerge %249 None\nOpBranchConditional %248 %249 %250\n%250 = OpLabel\nOpBranch %229\n%249 = OpLabel\nOpBranch %251\n%251 = OpLabel\nOpLine %3 1 1\n%253 = OpLoad  %4  %214\n%254 = OpLoad  %4  %215\n%255 = OpLoad  %6  %212\nOpLine %3 7187 21\n%256 = OpFunctionCall  %4  %67 %255\nOpLine %3 7187 13\n%257 = OpFMul  %4  %254 %256\n%258 = OpFAdd  %4  %253 %257\nOpLine %3 7187 9\nOpStore %214 %258\nOpLine %3 7188 13\n%259 = OpLoad  %6  %212\n%260 = OpMatrixTimesVector  %6  %227 %259\nOpLine %3 7188 13\n%261 = OpVectorTimesScalar  %6  %260 %81\n%262 = OpFAdd  %6  %261 %208\nOpLine %3 7188 9\nOpStore %212 %262\nOpLine %3 1 1\n%263 = OpLoad  %4  %215\nOpLine %3 7189 13\n%264 = OpFMul  %4  %263 %78\nOpLine %3 7189 9\nOpStore %215 %264\nOpBranch %252\n%252 = OpLabel\nOpBranch %231\n%231 = OpLabel\nOpLine %3 1 1\n%265 = OpLoad  %8  %216\nOpLine %3 7186 43\n%266 = OpIAdd  %8  %265 %126\nOpLine %3 7186 39\nOpStore %216 %266\nOpBranch %228\n%229 = OpLabel\nOpLine %3 1 1\n%267 = OpLoad  %4  %214\nOpReturnValue %267\nOpFunctionEnd\n%271 = OpFunction  %5  None %272\n%269 = OpFunctionParameter  %6\n%270 = OpFunctionParameter  %6\n%268 = OpLabel\nOpBranch %273\n%273 = OpLabel\nOpLine %3 7220 9\n%274 = OpCompositeExtract  %4  %269 0\n%275 = OpCompositeExtract  %4  %270 0\n%276 = OpCompositeExtract  %4  %270 1\nOpLine %3 7221 49\n%277 = OpFunctionCall  %4  %204 %269\nOpLine %3 7219 12\n%278 = OpExtInst  %4  %1 FMix %275 %276 %277\n%279 = OpCompositeExtract  %4  %269 1\n%280 = OpCompositeConstruct  %5  %274 %278 %279\nOpReturnValue %280\nOpFunctionEnd\n%284 = OpFunction  %14  None %285\n%282 = OpFunctionParameter  %6\n%283 = OpFunctionParameter  %6\n%281 = OpLabel\nOpBranch %292\n%292 = OpLabel\nOpLine %3 7227 13\n%293 = OpFunctionCall  %5  %271 %282 %283\nOpLine %3 7229 29\n%294 = OpFAdd  %6  %282 %287\nOpLine %3 7229 15\n%295 = OpFunctionCall  %5  %271 %294 %283\nOpLine %3 7229 15\n%296 = OpFSub  %5  %295 %293\nOpLine %3 7230 29\n%297 = OpFAdd  %6  %282 %288\nOpLine %3 7230 15\n%298 = OpFunctionCall  %5  %271 %297 %283\nOpLine %3 7230 15\n%299 = OpFSub  %5  %298 %293\nOpLine %3 7231 29\n%300 = OpFAdd  %6  %282 %290\nOpLine %3 7231 15\n%301 = OpFunctionCall  %5  %271 %300 %283\nOpLine %3 7231 15\n%302 = OpFSub  %5  %301 %293\nOpLine %3 7232 29\n%303 = OpFAdd  %6  %282 %291\nOpLine %3 7232 15\n%304 = OpFunctionCall  %5  %271 %303 %283\nOpLine %3 7232 15\n%305 = OpFSub  %5  %304 %293\nOpLine %3 7234 14\n%306 = OpExtInst  %5  %1 Cross %299 %296\n%307 = OpExtInst  %5  %1 Normalize %306\nOpLine %3 7235 14\n%308 = OpExtInst  %5  %1 Cross %305 %302\n%309 = OpExtInst  %5  %1 Normalize %308\nOpLine %3 7237 14\n%310 = OpFAdd  %5  %307 %309\nOpLine %3 7237 13\n%311 = OpVectorTimesScalar  %5  %310 %78\nOpLine %3 7239 12\n%312 = OpCompositeConstruct  %14  %293 %311\nOpReturnValue %312\nOpFunctionEnd\n%313 = OpFunction  %8  None %314\n%315 = OpFunctionParameter  %8\n%316 = OpFunctionParameter  %8\n%317 = OpLabel\n%318 = OpIEqual  %112  %316 %135\n%319 = OpSelect  %8  %318 %126 %316\n%320 = OpUDiv  %8  %315 %319\nOpReturnValue %320\nOpFunctionEnd\n%325 = OpFunction  %6  None %326\n%322 = OpFunctionParameter  %8\n%323 = OpFunctionParameter  %10\n%324 = OpFunctionParameter  %11\n%321 = OpLabel\nOpBranch %327\n%327 = OpLabel\nOpLine %3 7244 9\n%328 = OpConvertUToF  %4  %322\n%329 = OpCompositeExtract  %8  %323 0\nOpLine %3 7244 9\n%330 = OpIAdd  %8  %329 %126\n%331 = OpConvertUToF  %4  %330\n%332 = OpFRem  %4  %328 %331\n%333 = OpCompositeExtract  %8  %323 0\nOpLine %3 7243 12\n%334 = OpIAdd  %8  %333 %126\n%335 = OpFunctionCall  %8  %313 %322 %334\n%336 = OpConvertUToF  %4  %335\n%337 = OpCompositeConstruct  %6  %332 %336\n%338 = OpConvertSToF  %6  %324\n%339 = OpFAdd  %6  %337 %338\nOpReturnValue %339\nOpFunctionEnd\n%342 = OpFunction  %5  None %343\n%341 = OpFunctionParameter  %6\n%340 = OpLabel\nOpBranch %350\n%350 = OpLabel\nOpLine %3 7413 9\n%351 = OpFunctionCall  %4  %67 %341\nOpLine %3 7413 9\n%352 = OpFMul  %4  %351 %78\nOpLine %3 7413 9\n%353 = OpFAdd  %4  %352 %78\nOpLine %3 7414 17\n%354 = OpFAdd  %6  %341 %346\nOpLine %3 7414 9\n%355 = OpFunctionCall  %4  %67 %354\nOpLine %3 7414 9\n%356 = OpFMul  %4  %355 %78\nOpLine %3 7414 9\n%357 = OpFAdd  %4  %356 %78\nOpLine %3 7415 17\n%358 = OpFAdd  %6  %341 %349\nOpLine %3 7415 9\n%359 = OpFunctionCall  %4  %67 %358\nOpLine %3 7415 9\n%360 = OpFMul  %4  %359 %78\nOpLine %3 7412 12\n%361 = OpFAdd  %4  %360 %78\n%362 = OpCompositeConstruct  %5  %353 %357 %361\nOpReturnValue %362\nOpFunctionEnd\n%367 = OpFunction  %2  None %368\n%363 = OpLabel\n%366 = OpLoad  %19  %364\n%370 = OpAccessChain  %369  %29 %135\nOpBranch %375\n%375 = OpLabel\nOpLine %3 7254 22\n%376 = OpCompositeExtract  %8  %366 0\nOpLine %3 7256 36\n%378 = OpAccessChain  %377  %370 %135\n%379 = OpLoad  %10  %378\nOpLine %3 7256 59\n%381 = OpAccessChain  %380  %370 %126\n%382 = OpLoad  %11  %381\nOpLine %3 7256 13\n%383 = OpFunctionCall  %6  %325 %376 %379 %382\nOpLine %3 7258 5\nOpLine %3 7258 51\n%387 = OpAccessChain  %386  %370 %372\n%388 = OpLoad  %6  %387\nOpLine %3 7258 33\n%389 = OpFunctionCall  %14  %284 %383 %388\nOpLine %3 7258 5\n%390 = OpAccessChain  %385  %32 %135 %376\nOpStore %390 %389\nOpLine %3 7261 23\n%391 = OpCompositeExtract  %8  %366 0\nOpLine %3 7261 23\n%392 = OpIMul  %8  %391 %371\nOpLine %3 7263 24\n%394 = OpAccessChain  %393  %370 %135 %135\n%395 = OpLoad  %8  %394\nOpLine %3 7263 24\n%396 = OpAccessChain  %393  %370 %135 %126\n%397 = OpLoad  %8  %396\n%398 = OpIMul  %8  %395 %397\nOpLine %3 7263 8\n%399 = OpIMul  %8  %398 %371\n%400 = OpUGreaterThanEqual  %112  %392 %399\nOpLine %3 7263 5\nOpSelectionMerge %401 None\nOpBranchConditional %400 %402 %401\n%402 = OpLabel\nOpReturn\n%401 = OpLabel\nOpLine %3 7265 28\n%403 = OpCompositeExtract  %8  %366 0\nOpLine %3 7265 15\n%404 = OpAccessChain  %393  %370 %135 %135\n%405 = OpLoad  %8  %404\n%406 = OpFunctionCall  %8  %313 %403 %405\n%407 = OpIAdd  %8  %376 %406\nOpLine %3 7266 15\n%408 = OpIAdd  %8  %407 %126\nOpLine %3 7267 15\n%409 = OpAccessChain  %393  %370 %135 %135\n%410 = OpLoad  %8  %409\n%411 = OpIAdd  %8  %407 %410\nOpLine %3 7267 15\n%412 = OpIAdd  %8  %411 %126\nOpLine %3 7268 15\n%413 = OpIAdd  %8  %412 %126\nOpLine %3 7270 5\nOpLine %3 7270 5\n%416 = OpAccessChain  %415  %34 %135 %392\nOpStore %416 %407\nOpLine %3 7271 5\nOpLine %3 7271 5\n%417 = OpIAdd  %8  %392 %126\nOpLine %3 7271 5\n%418 = OpAccessChain  %415  %34 %135 %417\nOpStore %418 %412\nOpLine %3 7272 5\nOpLine %3 7272 5\n%419 = OpIAdd  %8  %392 %372\nOpLine %3 7272 5\n%420 = OpAccessChain  %415  %34 %135 %419\nOpStore %420 %413\nOpLine %3 7273 5\nOpLine %3 7273 5\n%421 = OpIAdd  %8  %392 %373\nOpLine %3 7273 5\n%422 = OpAccessChain  %415  %34 %135 %421\nOpStore %422 %407\nOpLine %3 7274 5\nOpLine %3 7274 5\n%423 = OpIAdd  %8  %392 %374\nOpLine %3 7274 5\n%424 = OpAccessChain  %415  %34 %135 %423\nOpStore %424 %413\nOpLine %3 7275 5\nOpLine %3 7275 5\n%425 = OpIAdd  %8  %392 %205\nOpLine %3 7275 5\n%426 = OpAccessChain  %415  %34 %135 %425\nOpStore %426 %408\nOpReturn\nOpFunctionEnd\n%427 = OpFunction  %8  None %314\n%428 = OpFunctionParameter  %8\n%429 = OpFunctionParameter  %8\n%430 = OpLabel\n%431 = OpIEqual  %112  %429 %135\n%432 = OpSelect  %8  %431 %126 %429\n%433 = OpUMod  %8  %428 %432\nOpReturnValue %433\nOpFunctionEnd\n%444 = OpFunction  %2  None %368\n%434 = OpLabel\n%437 = OpLoad  %8  %435\n%446 = OpAccessChain  %445  %36 %135\nOpBranch %449\n%449 = OpLabel\nOpLine %3 7304 19\n%450 = OpIAdd  %8  %437 %372\nOpLine %3 7304 18\n%451 = OpFunctionCall  %8  %313 %450 %373\nOpLine %3 7304 13\n%452 = OpFunctionCall  %8  %427 %451 %372\n%453 = OpConvertUToF  %4  %452\nOpLine %3 7305 19\n%454 = OpIAdd  %8  %437 %126\nOpLine %3 7305 18\n%455 = OpFunctionCall  %8  %313 %454 %373\nOpLine %3 7305 13\n%456 = OpFunctionCall  %8  %427 %455 %372\n%457 = OpConvertUToF  %4  %456\nOpLine %3 7306 14\n%458 = OpCompositeConstruct  %6  %453 %457\nOpLine %3 7308 30\n%459 = OpVectorTimesScalar  %6  %458 %81\nOpLine %3 7308 30\n%460 = OpFAdd  %6  %448 %459\nOpLine %3 7308 20\n%461 = OpCompositeConstruct  %7  %460 %74 %56\nOpLine %3 7311 21\n%462 = OpCompositeExtract  %4  %458 0\nOpLine %3 7311 21\n%463 = OpAccessChain  %393  %446 %373\n%464 = OpLoad  %8  %463\n%465 = OpConvertUToF  %4  %464\n%466 = OpFMul  %4  %462 %465\n%467 = OpCompositeExtract  %4  %458 1\nOpLine %3 7311 17\n%468 = OpAccessChain  %393  %446 %373\n%469 = OpLoad  %8  %468\n%470 = OpConvertUToF  %4  %469\n%471 = OpFMul  %4  %467 %470\n%472 = OpFAdd  %4  %466 %471\n%474 = OpExtInst  %4  %1 FClamp %472 %74 %473\n%475 = OpConvertFToU  %8  %474\nOpLine %3 7311 17\n%476 = OpAccessChain  %393  %446 %374\n%477 = OpLoad  %8  %476\n%478 = OpIAdd  %8  %475 %477\nOpLine %3 7313 12\n%479 = OpCompositeConstruct  %21  %478 %461 %458\n%480 = OpCompositeExtract  %8  %479 0\nOpStore %438 %480\n%481 = OpCompositeExtract  %7  %479 1\nOpStore %440 %481\n%482 = OpCompositeExtract  %6  %479 2\nOpStore %442 %482\nOpReturn\nOpFunctionEnd\n%495 = OpFunction  %2  None %368\n%483 = OpLabel\n%498 = OpVariable  %125  Function %74\n%499 = OpVariable  %217  Function %135\n%486 = OpLoad  %8  %485\n%489 = OpLoad  %7  %487\n%492 = OpLoad  %6  %490\n%484 = OpCompositeConstruct  %21  %486 %489 %492\n%496 = OpAccessChain  %445  %36 %135\nOpBranch %500\n%500 = OpLabel\nOpLine %3 7324 17\n%501 = OpCompositeExtract  %6  %484 2\n%502 = OpCompositeExtract  %4  %501 0\nOpLine %3 7324 17\n%503 = OpAccessChain  %393  %496 %373\n%504 = OpLoad  %8  %503\n%505 = OpConvertUToF  %4  %504\n%506 = OpFMul  %4  %502 %505\n%507 = OpCompositeExtract  %6  %484 2\n%508 = OpCompositeExtract  %4  %507 1\nOpLine %3 7324 70\n%509 = OpAccessChain  %393  %496 %373\n%510 = OpLoad  %8  %509\nOpLine %3 7324 13\n%511 = OpAccessChain  %393  %496 %373\n%512 = OpLoad  %8  %511\n%513 = OpIMul  %8  %510 %512\n%514 = OpConvertUToF  %4  %513\n%515 = OpFMul  %4  %508 %514\n%516 = OpFAdd  %4  %506 %515\n%517 = OpExtInst  %4  %1 FClamp %516 %74 %473\n%518 = OpConvertFToU  %8  %517\nOpLine %3 7324 13\n%519 = OpAccessChain  %393  %496 %374\n%520 = OpLoad  %8  %519\n%521 = OpIAdd  %8  %518 %520\nOpLine %3 7325 32\n%522 = OpConvertUToF  %4  %521\nOpLine %3 7325 22\n%523 = OpFDiv  %4  %522 %497\n%524 = OpExtInst  %4  %1 Floor %523\n%525 = OpExtInst  %4  %1 FClamp %524 %74 %473\n%526 = OpConvertFToU  %8  %525\nOpLine %3 7326 22\n%527 = OpFunctionCall  %8  %427 %521 %371\nOpLine %3 7328 36\n%528 = OpAccessChain  %377  %496 %135\n%529 = OpLoad  %10  %528\nOpLine %3 7328 57\n%530 = OpAccessChain  %380  %496 %126\n%531 = OpLoad  %11  %530\nOpLine %3 7328 13\n%532 = OpFunctionCall  %6  %325 %526 %529 %531\nOpLine %3 7329 31\n%533 = OpAccessChain  %386  %496 %372\n%534 = OpLoad  %6  %533\nOpLine %3 7329 13\n%535 = OpFunctionCall  %14  %284 %532 %534\nOpLine %3 7333 5\nOpSelectionMerge %536 None\nOpSwitch %527 %543 0 %537 1 %538 2 %539 3 %540 4 %541 5 %542\n%537 = OpLabel\nOpLine %3 7334 37\n%544 = OpCompositeExtract  %5  %535 0\n%545 = OpCompositeExtract  %4  %544 0\nOpLine %3 7334 20\nOpStore %498 %545\nOpBranch %536\n%538 = OpLabel\nOpLine %3 7335 37\n%546 = OpCompositeExtract  %5  %535 0\n%547 = OpCompositeExtract  %4  %546 1\nOpLine %3 7335 20\nOpStore %498 %547\nOpBranch %536\n%539 = OpLabel\nOpLine %3 7336 37\n%548 = OpCompositeExtract  %5  %535 0\n%549 = OpCompositeExtract  %4  %548 2\nOpLine %3 7336 20\nOpStore %498 %549\nOpBranch %536\n%540 = OpLabel\nOpLine %3 7337 37\n%550 = OpCompositeExtract  %5  %535 1\n%551 = OpCompositeExtract  %4  %550 0\nOpLine %3 7337 20\nOpStore %498 %551\nOpBranch %536\n%541 = OpLabel\nOpLine %3 7338 37\n%552 = OpCompositeExtract  %5  %535 1\n%553 = OpCompositeExtract  %4  %552 1\nOpLine %3 7338 20\nOpStore %498 %553\nOpBranch %536\n%542 = OpLabel\nOpLine %3 7339 37\n%554 = OpCompositeExtract  %5  %535 1\n%555 = OpCompositeExtract  %4  %554 2\nOpLine %3 7339 20\nOpStore %498 %555\nOpBranch %536\n%543 = OpLabel\nOpBranch %536\n%536 = OpLabel\nOpLine %3 7343 15\n%556 = OpAccessChain  %393  %496 %135 %135\n%557 = OpLoad  %8  %556\n%558 = OpFunctionCall  %8  %313 %526 %557\n%559 = OpIAdd  %8  %526 %558\nOpLine %3 7344 15\n%560 = OpIAdd  %8  %559 %126\nOpLine %3 7345 15\n%561 = OpAccessChain  %393  %496 %135 %135\n%562 = OpLoad  %8  %561\n%563 = OpIAdd  %8  %559 %562\nOpLine %3 7345 15\n%564 = OpIAdd  %8  %563 %126\nOpLine %3 7346 15\n%565 = OpIAdd  %8  %564 %126\nOpLine %3 7349 5\nOpSelectionMerge %566 None\nOpSwitch %527 %571 0 %567 3 %567 2 %568 4 %568 1 %569 5 %570\n%567 = OpLabel\nOpLine %3 7350 24\nOpStore %499 %559\nOpBranch %566\n%568 = OpLabel\nOpLine %3 7351 24\nOpStore %499 %565\nOpBranch %566\n%569 = OpLabel\nOpLine %3 7352 20\nOpStore %499 %564\nOpBranch %566\n%570 = OpLabel\nOpLine %3 7353 20\nOpStore %499 %560\nOpBranch %566\n%571 = OpLabel\nOpBranch %566\n%566 = OpLabel\nOpLine %3 7356 13\n%572 = OpCompositeExtract  %8  %484 0\nOpLine %3 7356 5\nOpStore %499 %572\nOpLine %3 7365 27\n%573 = OpLoad  %4  %498\n%574 = OpBitcast  %8  %573\nOpLine %3 7366 12\n%575 = OpLoad  %8  %499\n%576 = OpCompositeConstruct  %22  %574 %575\n%577 = OpCompositeExtract  %8  %576 0\nOpStore %493 %577\n%578 = OpCompositeExtract  %8  %576 1\nOpStore %494 %578\nOpReturn\nOpFunctionEnd\n%590 = OpFunction  %2  None %368\n%579 = OpLabel\n%583 = OpLoad  %5  %581\n%585 = OpLoad  %5  %584\n%580 = OpCompositeConstruct  %14  %583 %585\n%592 = OpAccessChain  %591  %39 %135\nOpBranch %593\n%593 = OpLabel\nOpLine %3 7397 25\n%595 = OpAccessChain  %594  %592 %126\n%596 = OpLoad  %23  %595\n%597 = OpCompositeExtract  %5  %580 0\nOpLine %3 7397 25\n%598 = OpCompositeConstruct  %7  %597 %56\n%599 = OpMatrixTimesVector  %7  %596 %598\nOpLine %3 7398 18\n%600 = OpCompositeExtract  %5  %580 1\nOpLine %3 7399 12\n%601 = OpCompositeExtract  %5  %580 0\n%602 = OpCompositeConstruct  %26  %599 %600 %601\n%603 = OpCompositeExtract  %7  %602 0\nOpStore %586 %603\n%604 = OpCompositeExtract  %5  %602 1\nOpStore %587 %604\n%605 = OpCompositeExtract  %5  %602 2\nOpStore %589 %605\nOpReturn\nOpFunctionEnd\n%615 = OpFunction  %2  None %368\n%606 = OpLabel\n%629 = OpVariable  %95  Function %630\n%609 = OpLoad  %7  %608\n%611 = OpLoad  %5  %610\n%613 = OpLoad  %5  %612\n%607 = OpCompositeConstruct  %26  %609 %611 %613\n%616 = OpAccessChain  %591  %39 %135\n%618 = OpAccessChain  %617  %42 %135\n%619 = OpLoad  %27  %45\n%620 = OpLoad  %28  %47\n%621 = OpLoad  %27  %49\n%622 = OpLoad  %28  %50\nOpBranch %631\n%631 = OpLabel\nOpLine %3 7426 13\nOpLine %3 7426 13\nOpLine %3 7426 5\n%632 = OpFunctionCall  %5  %342 %623\nOpLine %3 7428 28\nOpLine %3 7428 17\n%633 = OpCompositeExtract  %5  %607 2\n%634 = OpExtInst  %5  %1 Fract %633\n%635 = OpExtInst  %5  %1 SmoothStep %80 %624 %634\nOpLine %3 7428 5\nOpStore %629 %635\nOpLine %3 7429 17\nOpLine %3 7429 13\n%636 = OpAccessChain  %125  %629 %135\n%637 = OpLoad  %4  %636\n%638 = OpAccessChain  %125  %629 %126\n%639 = OpLoad  %4  %638\n%640 = OpFMul  %4  %637 %639\n%641 = OpAccessChain  %125  %629 %372\n%642 = OpLoad  %4  %641\n%643 = OpFMul  %4  %640 %642\n%644 = OpCompositeConstruct  %5  %643 %643 %643\n%645 = OpExtInst  %5  %1 FMix %626 %628 %644\nOpLine %3 7429 5\nOpStore %629 %645\nOpLine %3 7432 25\n%647 = OpAccessChain  %646  %618 %126\n%648 = OpLoad  %5  %647\n%649 = OpVectorTimesScalar  %5  %648 %286\nOpLine %3 7434 21\n%650 = OpAccessChain  %646  %618 %135\n%651 = OpLoad  %5  %650\n%652 = OpCompositeExtract  %5  %607 2\n%653 = OpFSub  %5  %651 %652\n%654 = OpExtInst  %5  %1 Normalize %653\nOpLine %3 7435 20\n%656 = OpAccessChain  %655  %616 %135\n%657 = OpLoad  %7  %656\n%658 = OpVectorShuffle  %5  %657 %657 0 1 2\n%659 = OpCompositeExtract  %5  %607 2\n%660 = OpFSub  %5  %658 %659\n%661 = OpExtInst  %5  %1 Normalize %660\nOpLine %3 7436 20\n%662 = OpFAdd  %5  %661 %654\n%663 = OpExtInst  %5  %1 Normalize %662\nOpLine %3 7438 32\n%664 = OpCompositeExtract  %5  %607 1\n%665 = OpDot  %4  %664 %654\nOpLine %3 7438 28\n%666 = OpExtInst  %4  %1 FMax %665 %74\nOpLine %3 7439 25\n%667 = OpAccessChain  %646  %618 %126\n%668 = OpLoad  %5  %667\n%669 = OpVectorTimesScalar  %5  %668 %666\nOpLine %3 7441 37\n%670 = OpCompositeExtract  %5  %607 1\n%671 = OpDot  %4  %670 %663\nOpLine %3 7441 33\n%672 = OpExtInst  %4  %1 FMax %671 %74\nOpLine %3 7441 29\n%673 = OpExtInst  %4  %1 Pow %672 %345\nOpLine %3 7442 26\n%674 = OpAccessChain  %646  %618 %126\n%675 = OpLoad  %5  %674\n%676 = OpVectorTimesScalar  %5  %675 %673\nOpLine %3 7444 18\n%677 = OpFAdd  %5  %649 %669\n%678 = OpFAdd  %5  %677 %676\n%679 = OpLoad  %5  %629\n%680 = OpFMul  %5  %678 %679\nOpLine %3 7446 12\n%681 = OpCompositeConstruct  %7  %680 %56\nOpStore %614 %681\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-debug-symbol-simple.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 110\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %21 \"vs_main\" %12 %15 %17 %19\nOpEntryPoint Fragment %49 \"fs_main\" %43 %46 %48\nOpExecutionMode %49 OriginUpperLeft\n%3 = OpString \"debug-symbol-simple.wgsl\"\nOpSource Unknown 0 %3 \"struct VertexInput {\n    @location(0) position: vec3<f32>,\n    @location(1) color: vec3<f32>,\n};\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) color: vec3<f32>,\n};\n\n@vertex\nfn vs_main(\n    model: VertexInput,\n) -> VertexOutput {\n    var out: VertexOutput;\n    out.color = model.color;\n    out.clip_position = vec4<f32>(model.position, 1.0);\n    return out;\n}\n\n// Fragment shader\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    var color = in.color;\n    for (var i = 0; i < 10; i += 1) {\n        var ii = f32(i);\n        color.x += ii*0.001;\n        color.y += ii*0.002;\n    }\n\n    return vec4<f32>(color, 1.0);\n}\"\nOpMemberName %6 0 \"position\"\nOpMemberName %6 1 \"color\"\nOpName %6 \"VertexInput\"\nOpMemberName %8 0 \"clip_position\"\nOpMemberName %8 1 \"color\"\nOpName %8 \"VertexOutput\"\nOpName %12 \"position\"\nOpName %15 \"color\"\nOpName %17 \"clip_position\"\nOpName %19 \"color\"\nOpName %21 \"vs_main\"\nOpName %24 \"out\"\nOpName %43 \"clip_position\"\nOpName %46 \"color\"\nOpName %49 \"fs_main\"\nOpName %55 \"color\"\nOpName %57 \"i\"\nOpName %59 \"ii\"\nOpName %75 \"loop_bound\"\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 16\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 16\nOpDecorate %12 Location 0\nOpDecorate %15 Location 1\nOpDecorate %17 BuiltIn Position\nOpDecorate %19 Location 0\nOpDecorate %43 BuiltIn FragCoord\nOpDecorate %46 Location 0\nOpDecorate %48 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 3\n%6 = OpTypeStruct %5 %5\n%7 = OpTypeVector %4 4\n%8 = OpTypeStruct %7 %5\n%9 = OpTypeInt 32 1\n%13 = OpTypePointer Input %5\n%12 = OpVariable  %13  Input\n%15 = OpVariable  %13  Input\n%18 = OpTypePointer Output %7\n%17 = OpVariable  %18  Output\n%20 = OpTypePointer Output %5\n%19 = OpVariable  %20  Output\n%22 = OpTypeFunction %2\n%23 = OpConstant  %4  1\n%25 = OpTypePointer Function %8\n%26 = OpConstantNull  %8\n%28 = OpTypePointer Function %5\n%31 = OpTypeInt 32 0\n%30 = OpConstant  %31  1\n%33 = OpTypePointer Function %7\n%36 = OpConstant  %31  0\n%44 = OpTypePointer Input %7\n%43 = OpVariable  %44  Input\n%46 = OpVariable  %13  Input\n%48 = OpVariable  %18  Output\n%50 = OpConstant  %9  0\n%51 = OpConstant  %9  10\n%52 = OpConstant  %4  0.001\n%53 = OpConstant  %4  0.002\n%54 = OpConstant  %9  1\n%56 = OpConstantNull  %5\n%58 = OpTypePointer Function %9\n%60 = OpTypePointer Function %4\n%61 = OpConstantNull  %4\n%68 = OpTypeVector %31 2\n%69 = OpTypePointer Function %68\n%70 = OpTypeBool\n%71 = OpTypeVector %70 2\n%72 = OpConstantComposite  %68  %36 %36\n%73 = OpConstant  %31  4294967295\n%74 = OpConstantComposite  %68  %73 %73\n%21 = OpFunction  %2  None %22\n%10 = OpLabel\n%24 = OpVariable  %25  Function %26\n%14 = OpLoad  %5  %12\n%16 = OpLoad  %5  %15\n%11 = OpCompositeConstruct  %6  %14 %16\nOpBranch %27\n%27 = OpLabel\nOpLine %3 16 5\n%29 = OpCompositeExtract  %5  %11 1\nOpLine %3 16 5\n%32 = OpAccessChain  %28  %24 %30\nOpStore %32 %29\nOpLine %3 17 5\n%34 = OpCompositeExtract  %5  %11 0\nOpLine %3 17 25\n%35 = OpCompositeConstruct  %7  %34 %23\nOpLine %3 17 5\n%37 = OpAccessChain  %33  %24 %36\nOpStore %37 %35\nOpLine %3 1 1\n%38 = OpLoad  %8  %24\n%39 = OpCompositeExtract  %7  %38 0\nOpStore %17 %39\n%40 = OpCompositeExtract  %5  %38 1\nOpStore %19 %40\nOpReturn\nOpFunctionEnd\n%49 = OpFunction  %2  None %22\n%41 = OpLabel\n%55 = OpVariable  %28  Function %56\n%57 = OpVariable  %58  Function %50\n%59 = OpVariable  %60  Function %61\n%75 = OpVariable  %69  Function %74\n%45 = OpLoad  %7  %43\n%47 = OpLoad  %5  %46\n%42 = OpCompositeConstruct  %8  %45 %47\nOpBranch %62\n%62 = OpLabel\nOpLine %3 25 17\n%63 = OpCompositeExtract  %5  %42 1\nOpLine %3 25 5\nOpStore %55 %63\nOpBranch %64\n%64 = OpLabel\nOpLine %3 26 5\nOpLoopMerge %65 %67 None\nOpBranch %76\n%76 = OpLabel\n%77 = OpLoad  %68  %75\n%78 = OpIEqual  %71  %72 %77\n%79 = OpAll  %70  %78\nOpSelectionMerge %80 None\nOpBranchConditional %79 %65 %80\n%80 = OpLabel\n%81 = OpCompositeExtract  %31  %77 1\n%82 = OpIEqual  %70  %81 %36\n%83 = OpSelect  %31  %82 %30 %36\n%84 = OpCompositeConstruct  %68  %83 %30\n%85 = OpISub  %68  %77 %84\nOpStore %75 %85\nOpBranch %66\n%66 = OpLabel\nOpLine %3 1 1\n%86 = OpLoad  %9  %57\nOpLine %3 26 21\n%87 = OpSLessThan  %70  %86 %51\nOpLine %3 26 20\nOpSelectionMerge %88 None\nOpBranchConditional %87 %88 %89\n%89 = OpLabel\nOpBranch %65\n%88 = OpLabel\nOpBranch %90\n%90 = OpLabel\nOpLine %3 27 18\n%92 = OpLoad  %9  %57\n%93 = OpConvertSToF  %4  %92\nOpLine %3 27 9\nOpStore %59 %93\nOpLine %3 28 9\n%94 = OpAccessChain  %60  %55 %36\n%95 = OpLoad  %4  %94\n%96 = OpLoad  %4  %59\nOpLine %3 28 9\n%97 = OpFMul  %4  %96 %52\n%98 = OpFAdd  %4  %95 %97\nOpLine %3 28 9\n%99 = OpAccessChain  %60  %55 %36\nOpStore %99 %98\nOpLine %3 29 9\n%100 = OpAccessChain  %60  %55 %30\n%101 = OpLoad  %4  %100\n%102 = OpLoad  %4  %59\nOpLine %3 29 9\n%103 = OpFMul  %4  %102 %53\n%104 = OpFAdd  %4  %101 %103\nOpLine %3 29 9\n%105 = OpAccessChain  %60  %55 %30\nOpStore %105 %104\nOpBranch %91\n%91 = OpLabel\nOpBranch %67\n%67 = OpLabel\nOpLine %3 1 1\n%106 = OpLoad  %9  %57\nOpLine %3 26 29\n%107 = OpIAdd  %9  %106 %54\nOpLine %3 26 29\nOpStore %57 %107\nOpBranch %64\n%65 = OpLabel\nOpLine %3 1 1\n%108 = OpLoad  %5  %55\nOpLine %3 32 12\n%109 = OpCompositeConstruct  %7  %108 %23\nOpStore %48 %109\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-debug-symbol-terrain.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 682\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %367 \"gen_terrain_compute\" %364\nOpEntryPoint Vertex %444 \"gen_terrain_vertex\" %435 %438 %440 %442\nOpEntryPoint Fragment %495 \"gen_terrain_fragment\" %485 %487 %490 %493 %494\nOpEntryPoint Vertex %590 \"vs_main\" %581 %584 %586 %587 %589\nOpEntryPoint Fragment %615 \"fs_main\" %608 %610 %612 %614\nOpExecutionMode %367 LocalSize 64 1 1\nOpExecutionMode %495 OriginUpperLeft\nOpExecutionMode %615 OriginUpperLeft\n%3 = OpString \"debug-symbol-terrain.wgsl\"\nOpSource Unknown 0 %3 \"// Taken from https://github.com/sotrh/learn-wgpu/blob/11820796f5e1dbce42fb1119f04ddeb4b167d2a0/code/intermediate/tutorial13-terrain/src/terrain.wgsl\n// ============================\n// Terrain Generation\n// ============================\n\n// https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39\n//  MIT License. © Ian McEwan, Stefan Gustavson, Munrocket\n// - Less condensed glsl implementation with comments can be found at https://weber.itn.liu.se/~stegu/jgt2012/article.pdf\n\nfn permute3(x: vec3<f32>) -> vec3<f32> { return (((x * 34.) + 1.) * x) % vec3<f32>(289.); }\n\nfn snoise2(v: vec2<f32>) -> f32 {\n    let C = vec4<f32>(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);\n    var i: vec2<f32> = floor(v + dot(v, C.yy));\n    let x0 = v - i + dot(i, C.xx);\n    // I flipped the condition here from > to < as it fixed some artifacting I was observing\n    var i1: vec2<f32> = select(vec2<f32>(1., 0.), vec2<f32>(0., 1.), (x0.x < x0.y));\n    var x12: vec4<f32> = x0.xyxy + C.xxzz - vec4<f32>(i1, 0., 0.);\n    i = i % vec2<f32>(289.);\n    let p = permute3(permute3(i.y + vec3<f32>(0., i1.y, 1.)) + i.x + vec3<f32>(0., i1.x, 1.));\n    var m: vec3<f32> = max(0.5 - vec3<f32>(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3<f32>(0.));\n    m = m * m;\n    m = m * m;\n    let x = 2. * fract(p * C.www) - 1.;\n    let h = abs(x) - 0.5;\n    let ox = floor(x + 0.5);\n    let a0 = x - ox;\n    m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));\n    let g = vec3<f32>(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);\n    return 130. * dot(m, g);\n}\n\n\nfn fbm(p: vec2<f32>) -> f32 {\n    let NUM_OCTAVES: u32 = 5u;\n    var x = p * 0.01;\n    var v = 0.0;\n    var a = 0.5;\n    let shift = vec2<f32>(100.0);\n    let cs = vec2<f32>(cos(0.5), sin(0.5));\n    let rot = mat2x2<f32>(cs.x, cs.y, -cs.y, cs.x);\n\n    for (var i = 0u; i < NUM_OCTAVES; i = i + 1u) {\n        v = v + a * snoise2(x);\n        x = rot * x * 2.0 + shift;\n        a = a * 0.5;\n    }\n\n    return v;\n}\n\nstruct ChunkData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n}\n\nstruct Vertex {\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n}\n\nstruct VertexBuffer {\n    data: array<Vertex>, // stride: 32\n}\n\nstruct IndexBuffer {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) var<uniform> chunk_data: ChunkData;\n@group(0) @binding(1) var<storage, read_write> vertices: VertexBuffer;\n@group(0) @binding(2) var<storage, read_write> indices: IndexBuffer;\n\nfn terrain_point(p: vec2<f32>, min_max_height: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        p.x,\n        mix(min_max_height.x, min_max_height.y, fbm(p)),\n        p.y,\n    );\n}\n\nfn terrain_vertex(p: vec2<f32>, min_max_height: vec2<f32>) -> Vertex {\n    let v = terrain_point(p, min_max_height);\n\n    let tpx = terrain_point(p + vec2<f32>(0.1, 0.0), min_max_height) - v;\n    let tpz = terrain_point(p + vec2<f32>(0.0, 0.1), min_max_height) - v;\n    let tnx = terrain_point(p + vec2<f32>(-0.1, 0.0), min_max_height) - v;\n    let tnz = terrain_point(p + vec2<f32>(0.0, -0.1), min_max_height) - v;\n\n    let pn = normalize(cross(tpz, tpx));\n    let nn = normalize(cross(tnz, tnx));\n\n    let n = (pn + nn) * 0.5;\n\n    return Vertex(v, n);\n}\n\nfn index_to_p(vert_index: u32, chunk_size: vec2<u32>, chunk_corner: vec2<i32>) -> vec2<f32> {\n    return vec2(\n        f32(vert_index) % f32(chunk_size.x + 1u),\n        f32(vert_index / (chunk_size.x + 1u)),\n    ) + vec2<f32>(chunk_corner);\n}\n\n@compute @workgroup_size(64)\nfn gen_terrain_compute(\n    @builtin(global_invocation_id) gid: vec3<u32>\n) {\n    // Create vert_component\n    let vert_index = gid.x;\n\n    let p = index_to_p(vert_index, chunk_data.chunk_size, chunk_data.chunk_corner);\n\n    vertices.data[vert_index] = terrain_vertex(p, chunk_data.min_max_height);\n\n    // Create indices\n    let start_index = gid.x * 6u; // using TriangleList\n\n    if (start_index >= (chunk_data.chunk_size.x * chunk_data.chunk_size.y * 6u)) { return; }\n\n    let v00 = vert_index + gid.x / chunk_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + chunk_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    indices.data[start_index] = v00;\n    indices.data[start_index + 1u] = v01;\n    indices.data[start_index + 2u] = v11;\n    indices.data[start_index + 3u] = v00;\n    indices.data[start_index + 4u] = v11;\n    indices.data[start_index + 5u] = v10;\n}\n\n// ============================\n// Terrain Gen (Fragment Shader)\n// ============================\n\nstruct GenData {\n    chunk_size: vec2<u32>,\n    chunk_corner: vec2<i32>,\n    min_max_height: vec2<f32>,\n    texture_size: u32,\n    start_index: u32,\n}\n@group(0)\n@binding(0)\nvar<uniform> gen_data: GenData;\n\nstruct GenVertexOutput {\n    @location(0)\n    index: u32,\n    @builtin(position)\n    position: vec4<f32>,\n    @location(1)\n    uv: vec2<f32>,\n};\n\n@vertex\nfn gen_terrain_vertex(@builtin(vertex_index) vindex: u32) -> GenVertexOutput {\n    let u = f32(((vindex + 2u) / 3u) % 2u);\n    let v = f32(((vindex + 1u) / 3u) % 2u);\n    let uv = vec2<f32>(u, v);\n\n    let position = vec4<f32>(-1.0 + uv * 2.0, 0.0, 1.0);\n\n    // TODO: maybe replace this with u32(dot(uv, vec2(f32(gen_data.texture_dim.x))))\n    let index = u32(uv.x * f32(gen_data.texture_size) + uv.y * f32(gen_data.texture_size)) + gen_data.start_index;\n\n    return GenVertexOutput(index, position, uv);\n}\n\n\nstruct GenFragmentOutput {\n    @location(0) vert_component: u32,\n    @location(1) index: u32,\n}\n\n@fragment\nfn gen_terrain_fragment(in: GenVertexOutput) -> GenFragmentOutput {\n    let i = u32(in.uv.x * f32(gen_data.texture_size) + in.uv.y * f32(gen_data.texture_size * gen_data.texture_size)) + gen_data.start_index;\n    let vert_index = u32(floor(f32(i) / 6.));\n    let comp_index = i % 6u;\n\n    let p = index_to_p(vert_index, gen_data.chunk_size, gen_data.chunk_corner);\n    let v = terrain_vertex(p, gen_data.min_max_height);\n\n    var vert_component: f32 = 0.;\n\n    switch comp_index {\n        case 0u: { vert_component = v.position.x; }\n        case 1u: { vert_component = v.position.y; }\n        case 2u: { vert_component = v.position.z; }\n        case 3u: { vert_component = v.normal.x; }\n        case 4u: { vert_component = v.normal.y; }\n        case 5u: { vert_component = v.normal.z; }\n        default: {}\n    }\n\n    let v00 = vert_index + vert_index / gen_data.chunk_size.x;\n    let v10 = v00 + 1u;\n    let v01 = v00 + gen_data.chunk_size.x + 1u;\n    let v11 = v01 + 1u;\n\n    var index = 0u;\n    switch comp_index {\n        case 0u, 3u: { index = v00; }\n        case 2u, 4u: { index = v11; }\n        case 1u: { index = v01; }\n        case 5u: { index = v10; }\n        default: {}\n    }\n    index = in.index;\n    // index = gen_data.start_index;\n    // indices.data[start_index] = v00;\n    // indices.data[start_index + 1u] = v01;\n    // indices.data[start_index + 2u] = v11;\n    // indices.data[start_index + 3u] = v00;\n    // indices.data[start_index + 4u] = v11;\n    // indices.data[start_index + 5u] = v10;\n\n    let ivert_component = bitcast<u32>(vert_component);\n    return GenFragmentOutput(ivert_component, index);\n}\n\n// ============================\n// Terrain Rendering\n// ============================\n\nstruct Camera {\n    view_pos: vec4<f32>,\n    view_proj: mat4x4<f32>,\n}\n@group(0) @binding(0)\nvar<uniform> camera: Camera;\n\nstruct Light {\n    position: vec3<f32>,\n    color: vec3<f32>,\n}\n@group(1) @binding(0)\nvar<uniform> light: Light;\n\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) normal: vec3<f32>,\n    @location(1) world_pos: vec3<f32>,\n}\n\n@vertex\nfn vs_main(\n    vertex: Vertex,\n) -> VertexOutput {\n    let clip_position = camera.view_proj * vec4<f32>(vertex.position, 1.);\n    let normal = vertex.normal;\n    return VertexOutput(clip_position, normal, vertex.position);\n}\n\n@group(2) @binding(0)\nvar t_diffuse: texture_2d<f32>;\n@group(2) @binding(1)\nvar s_diffuse: sampler;\n@group(2) @binding(2)\nvar t_normal: texture_2d<f32>;\n@group(2) @binding(3)\nvar s_normal: sampler;\n\nfn color23(p: vec2<f32>) -> vec3<f32> {\n    return vec3<f32>(\n        snoise2(p) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(23., 32.)) * 0.5 + 0.5,\n        snoise2(p + vec2<f32>(-43., 3.)) * 0.5 + 0.5,\n    );\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    _ = t_diffuse;\n    _ = s_diffuse;\n    _ = t_normal;\n    _ = s_normal;\n\n    color23(vec2(1, 2));\n\n    var color = smoothstep(vec3<f32>(0.0), vec3<f32>(0.1), fract(in.world_pos));\n    color = mix(vec3<f32>(0.5, 0.1, 0.7), vec3<f32>(0.2, 0.2, 0.2), vec3<f32>(color.x * color.y * color.z));\n\n    let ambient_strength = 0.1;\n    let ambient_color = light.color * ambient_strength;\n\n    let light_dir = normalize(light.position - in.world_pos);\n    let view_dir = normalize(camera.view_pos.xyz - in.world_pos);\n    let half_dir = normalize(view_dir + light_dir);\n\n    let diffuse_strength = max(dot(in.normal, light_dir), 0.0);\n    let diffuse_color = diffuse_strength * light.color;\n\n    let specular_strength = pow(max(dot(in.normal, half_dir), 0.0), 32.0);\n    let specular_color = specular_strength * light.color;\n\n    let result = (ambient_color + diffuse_color + specular_color) * color;\n\n    return vec4<f32>(result, 1.0);\n}\n\"\nOpMemberName %13 0 \"chunk_size\"\nOpMemberName %13 1 \"chunk_corner\"\nOpMemberName %13 2 \"min_max_height\"\nOpName %13 \"ChunkData\"\nOpMemberName %14 0 \"position\"\nOpMemberName %14 1 \"normal\"\nOpName %14 \"Vertex\"\nOpMemberName %16 0 \"data\"\nOpName %16 \"VertexBuffer\"\nOpMemberName %18 0 \"data\"\nOpName %18 \"IndexBuffer\"\nOpMemberName %20 0 \"chunk_size\"\nOpMemberName %20 1 \"chunk_corner\"\nOpMemberName %20 2 \"min_max_height\"\nOpMemberName %20 3 \"texture_size\"\nOpMemberName %20 4 \"start_index\"\nOpName %20 \"GenData\"\nOpMemberName %21 0 \"index\"\nOpMemberName %21 1 \"position\"\nOpMemberName %21 2 \"uv\"\nOpName %21 \"GenVertexOutput\"\nOpMemberName %22 0 \"vert_component\"\nOpMemberName %22 1 \"index\"\nOpName %22 \"GenFragmentOutput\"\nOpMemberName %24 0 \"view_pos\"\nOpMemberName %24 1 \"view_proj\"\nOpName %24 \"Camera\"\nOpMemberName %25 0 \"position\"\nOpMemberName %25 1 \"color\"\nOpName %25 \"Light\"\nOpMemberName %26 0 \"clip_position\"\nOpMemberName %26 1 \"normal\"\nOpMemberName %26 2 \"world_pos\"\nOpName %26 \"VertexOutput\"\nOpName %29 \"chunk_data\"\nOpName %32 \"vertices\"\nOpName %34 \"indices\"\nOpName %36 \"gen_data\"\nOpName %39 \"camera\"\nOpName %42 \"light\"\nOpName %45 \"t_diffuse\"\nOpName %47 \"s_diffuse\"\nOpName %49 \"t_normal\"\nOpName %50 \"s_normal\"\nOpName %52 \"x\"\nOpName %53 \"permute3\"\nOpName %66 \"v\"\nOpName %67 \"snoise2\"\nOpName %86 \"i\"\nOpName %89 \"i1\"\nOpName %91 \"x12\"\nOpName %94 \"m\"\nOpName %203 \"p\"\nOpName %204 \"fbm\"\nOpName %212 \"x\"\nOpName %214 \"v\"\nOpName %215 \"a\"\nOpName %216 \"i\"\nOpName %236 \"loop_bound\"\nOpName %269 \"p\"\nOpName %270 \"min_max_height\"\nOpName %271 \"terrain_point\"\nOpName %282 \"p\"\nOpName %283 \"min_max_height\"\nOpName %284 \"terrain_vertex\"\nOpName %313 \"naga_div\"\nOpName %315 \"lhs\"\nOpName %316 \"rhs\"\nOpName %322 \"vert_index\"\nOpName %323 \"chunk_size\"\nOpName %324 \"chunk_corner\"\nOpName %325 \"index_to_p\"\nOpName %341 \"p\"\nOpName %342 \"color23\"\nOpName %364 \"gid\"\nOpName %367 \"gen_terrain_compute\"\nOpName %427 \"naga_mod\"\nOpName %428 \"lhs\"\nOpName %429 \"rhs\"\nOpName %435 \"vindex\"\nOpName %438 \"index\"\nOpName %440 \"position\"\nOpName %442 \"uv\"\nOpName %444 \"gen_terrain_vertex\"\nOpName %485 \"index\"\nOpName %487 \"position\"\nOpName %490 \"uv\"\nOpName %493 \"vert_component\"\nOpName %494 \"index\"\nOpName %495 \"gen_terrain_fragment\"\nOpName %498 \"vert_component\"\nOpName %499 \"index\"\nOpName %581 \"position\"\nOpName %584 \"normal\"\nOpName %586 \"clip_position\"\nOpName %587 \"normal\"\nOpName %589 \"world_pos\"\nOpName %590 \"vs_main\"\nOpName %608 \"clip_position\"\nOpName %610 \"normal\"\nOpName %612 \"world_pos\"\nOpName %615 \"fs_main\"\nOpName %629 \"color\"\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 8\nOpMemberDecorate %13 2 Offset 16\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 16\nOpDecorate %15 ArrayStride 32\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %16 Block\nOpDecorate %17 ArrayStride 4\nOpMemberDecorate %18 0 Offset 0\nOpDecorate %18 Block\nOpMemberDecorate %20 0 Offset 0\nOpMemberDecorate %20 1 Offset 8\nOpMemberDecorate %20 2 Offset 16\nOpMemberDecorate %20 3 Offset 24\nOpMemberDecorate %20 4 Offset 28\nOpMemberDecorate %21 0 Offset 0\nOpMemberDecorate %21 1 Offset 16\nOpMemberDecorate %21 2 Offset 32\nOpMemberDecorate %22 0 Offset 0\nOpMemberDecorate %22 1 Offset 4\nOpMemberDecorate %24 0 Offset 0\nOpMemberDecorate %24 1 Offset 16\nOpMemberDecorate %24 1 ColMajor\nOpMemberDecorate %24 1 MatrixStride 16\nOpMemberDecorate %25 0 Offset 0\nOpMemberDecorate %25 1 Offset 16\nOpMemberDecorate %26 0 Offset 0\nOpMemberDecorate %26 1 Offset 16\nOpMemberDecorate %26 2 Offset 32\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 0\nOpDecorate %30 Block\nOpMemberDecorate %30 0 Offset 0\nOpDecorate %32 DescriptorSet 0\nOpDecorate %32 Binding 1\nOpDecorate %34 DescriptorSet 0\nOpDecorate %34 Binding 2\nOpDecorate %36 DescriptorSet 0\nOpDecorate %36 Binding 0\nOpDecorate %37 Block\nOpMemberDecorate %37 0 Offset 0\nOpDecorate %39 DescriptorSet 0\nOpDecorate %39 Binding 0\nOpDecorate %40 Block\nOpMemberDecorate %40 0 Offset 0\nOpDecorate %42 DescriptorSet 1\nOpDecorate %42 Binding 0\nOpDecorate %43 Block\nOpMemberDecorate %43 0 Offset 0\nOpDecorate %45 DescriptorSet 2\nOpDecorate %45 Binding 0\nOpDecorate %47 DescriptorSet 2\nOpDecorate %47 Binding 1\nOpDecorate %49 DescriptorSet 2\nOpDecorate %49 Binding 2\nOpDecorate %50 DescriptorSet 2\nOpDecorate %50 Binding 3\nOpDecorate %364 BuiltIn GlobalInvocationId\nOpDecorate %435 BuiltIn VertexIndex\nOpDecorate %438 Location 0\nOpDecorate %438 Flat\nOpDecorate %440 BuiltIn Position\nOpDecorate %442 Location 1\nOpDecorate %485 Location 0\nOpDecorate %485 Flat\nOpDecorate %487 BuiltIn FragCoord\nOpDecorate %490 Location 1\nOpDecorate %493 Location 0\nOpDecorate %494 Location 1\nOpDecorate %581 Location 0\nOpDecorate %584 Location 1\nOpDecorate %586 BuiltIn Position\nOpDecorate %587 Location 0\nOpDecorate %589 Location 1\nOpDecorate %608 BuiltIn FragCoord\nOpDecorate %610 Location 0\nOpDecorate %612 Location 1\nOpDecorate %614 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 3\n%6 = OpTypeVector %4 2\n%7 = OpTypeVector %4 4\n%8 = OpTypeInt 32 0\n%9 = OpTypeMatrix %6 2\n%10 = OpTypeVector %8 2\n%12 = OpTypeInt 32 1\n%11 = OpTypeVector %12 2\n%13 = OpTypeStruct %10 %11 %6\n%14 = OpTypeStruct %5 %5\n%15 = OpTypeRuntimeArray %14\n%16 = OpTypeStruct %15\n%17 = OpTypeRuntimeArray %8\n%18 = OpTypeStruct %17\n%19 = OpTypeVector %8 3\n%20 = OpTypeStruct %10 %11 %6 %8 %8\n%21 = OpTypeStruct %8 %7 %6\n%22 = OpTypeStruct %8 %8\n%23 = OpTypeMatrix %7 4\n%24 = OpTypeStruct %7 %23\n%25 = OpTypeStruct %5 %5\n%26 = OpTypeStruct %7 %5 %5\n%27 = OpTypeImage %4 2D 0 0 0 1 Unknown\n%28 = OpTypeSampler\n%30 = OpTypeStruct %13\n%31 = OpTypePointer Uniform %30\n%29 = OpVariable  %31  Uniform\n%33 = OpTypePointer StorageBuffer %16\n%32 = OpVariable  %33  StorageBuffer\n%35 = OpTypePointer StorageBuffer %18\n%34 = OpVariable  %35  StorageBuffer\n%37 = OpTypeStruct %20\n%38 = OpTypePointer Uniform %37\n%36 = OpVariable  %38  Uniform\n%40 = OpTypeStruct %24\n%41 = OpTypePointer Uniform %40\n%39 = OpVariable  %41  Uniform\n%43 = OpTypeStruct %25\n%44 = OpTypePointer Uniform %43\n%42 = OpVariable  %44  Uniform\n%46 = OpTypePointer UniformConstant %27\n%45 = OpVariable  %46  UniformConstant\n%48 = OpTypePointer UniformConstant %28\n%47 = OpVariable  %48  UniformConstant\n%49 = OpVariable  %46  UniformConstant\n%50 = OpVariable  %48  UniformConstant\n%54 = OpTypeFunction %5 %5\n%55 = OpConstant  %4  34\n%56 = OpConstant  %4  1\n%57 = OpConstantComposite  %5  %56 %56 %56\n%58 = OpConstant  %4  289\n%59 = OpConstantComposite  %5  %58 %58 %58\n%68 = OpTypeFunction %4 %6\n%69 = OpConstant  %4  0.21132487\n%70 = OpConstant  %4  0.36602542\n%71 = OpConstant  %4  -0.57735026\n%72 = OpConstant  %4  0.024390243\n%73 = OpConstantComposite  %7  %69 %70 %71 %72\n%74 = OpConstant  %4  0\n%75 = OpConstantComposite  %6  %56 %74\n%76 = OpConstantComposite  %6  %74 %56\n%77 = OpConstantComposite  %6  %58 %58\n%78 = OpConstant  %4  0.5\n%79 = OpConstantComposite  %5  %78 %78 %78\n%80 = OpConstantComposite  %5  %74 %74 %74\n%81 = OpConstant  %4  2\n%82 = OpConstant  %4  0.85373473\n%83 = OpConstant  %4  1.7928429\n%84 = OpConstantComposite  %5  %83 %83 %83\n%85 = OpConstant  %4  130\n%87 = OpTypePointer Function %6\n%88 = OpConstantNull  %6\n%90 = OpConstantNull  %6\n%92 = OpTypePointer Function %7\n%93 = OpConstantNull  %7\n%95 = OpTypePointer Function %5\n%96 = OpConstantNull  %5\n%112 = OpTypeBool\n%115 = OpTypeVector %112 2\n%125 = OpTypePointer Function %4\n%126 = OpConstant  %8  1\n%135 = OpConstant  %8  0\n%205 = OpConstant  %8  5\n%206 = OpConstant  %4  0.01\n%207 = OpConstant  %4  100\n%208 = OpConstantComposite  %6  %207 %207\n%209 = OpConstant  %4  0.87758255\n%210 = OpConstant  %4  0.47942555\n%211 = OpConstantComposite  %6  %209 %210\n%213 = OpConstantNull  %6\n%217 = OpTypePointer Function %8\n%232 = OpTypePointer Function %10\n%233 = OpConstantComposite  %10  %135 %135\n%234 = OpConstant  %8  4294967295\n%235 = OpConstantComposite  %10  %234 %234\n%272 = OpTypeFunction %5 %6 %6\n%285 = OpTypeFunction %14 %6 %6\n%286 = OpConstant  %4  0.1\n%287 = OpConstantComposite  %6  %286 %74\n%288 = OpConstantComposite  %6  %74 %286\n%289 = OpConstant  %4  -0.1\n%290 = OpConstantComposite  %6  %289 %74\n%291 = OpConstantComposite  %6  %74 %289\n%314 = OpTypeFunction %8 %8 %8\n%326 = OpTypeFunction %6 %8 %10 %11\n%343 = OpTypeFunction %5 %6\n%344 = OpConstant  %4  23\n%345 = OpConstant  %4  32\n%346 = OpConstantComposite  %6  %344 %345\n%347 = OpConstant  %4  -43\n%348 = OpConstant  %4  3\n%349 = OpConstantComposite  %6  %347 %348\n%365 = OpTypePointer Input %19\n%364 = OpVariable  %365  Input\n%368 = OpTypeFunction %2\n%369 = OpTypePointer Uniform %13\n%371 = OpConstant  %8  6\n%372 = OpConstant  %8  2\n%373 = OpConstant  %8  3\n%374 = OpConstant  %8  4\n%377 = OpTypePointer Uniform %10\n%380 = OpTypePointer Uniform %11\n%384 = OpTypePointer StorageBuffer %15\n%385 = OpTypePointer StorageBuffer %14\n%386 = OpTypePointer Uniform %6\n%393 = OpTypePointer Uniform %8\n%414 = OpTypePointer StorageBuffer %17\n%415 = OpTypePointer StorageBuffer %8\n%436 = OpTypePointer Input %8\n%435 = OpVariable  %436  Input\n%439 = OpTypePointer Output %8\n%438 = OpVariable  %439  Output\n%441 = OpTypePointer Output %7\n%440 = OpVariable  %441  Output\n%443 = OpTypePointer Output %6\n%442 = OpVariable  %443  Output\n%445 = OpTypePointer Uniform %20\n%447 = OpConstant  %4  -1\n%448 = OpConstantComposite  %6  %447 %447\n%473 = OpConstant  %4  4294967000\n%485 = OpVariable  %436  Input\n%488 = OpTypePointer Input %7\n%487 = OpVariable  %488  Input\n%491 = OpTypePointer Input %6\n%490 = OpVariable  %491  Input\n%493 = OpVariable  %439  Output\n%494 = OpVariable  %439  Output\n%497 = OpConstant  %4  6\n%582 = OpTypePointer Input %5\n%581 = OpVariable  %582  Input\n%584 = OpVariable  %582  Input\n%586 = OpVariable  %441  Output\n%588 = OpTypePointer Output %5\n%587 = OpVariable  %588  Output\n%589 = OpVariable  %588  Output\n%591 = OpTypePointer Uniform %24\n%594 = OpTypePointer Uniform %23\n%608 = OpVariable  %488  Input\n%610 = OpVariable  %582  Input\n%612 = OpVariable  %582  Input\n%614 = OpVariable  %441  Output\n%617 = OpTypePointer Uniform %25\n%623 = OpConstantComposite  %6  %56 %81\n%624 = OpConstantComposite  %5  %286 %286 %286\n%625 = OpConstant  %4  0.7\n%626 = OpConstantComposite  %5  %78 %286 %625\n%627 = OpConstant  %4  0.2\n%628 = OpConstantComposite  %5  %627 %627 %627\n%630 = OpConstantNull  %5\n%646 = OpTypePointer Uniform %5\n%655 = OpTypePointer Uniform %7\n%53 = OpFunction  %5  None %54\n%52 = OpFunctionParameter  %5\n%51 = OpLabel\nOpBranch %60\n%60 = OpLabel\nOpLine %3 10 52\n%61 = OpVectorTimesScalar  %5  %52 %55\nOpLine %3 10 63\nOpLine %3 10 50\n%62 = OpFAdd  %5  %61 %57\n%63 = OpFMul  %5  %62 %52\nOpLine %3 10 49\n%64 = OpFRem  %5  %63 %59\nOpReturnValue %64\nOpFunctionEnd\n%67 = OpFunction  %4  None %68\n%66 = OpFunctionParameter  %6\n%65 = OpLabel\n%89 = OpVariable  %87  Function %90\n%94 = OpVariable  %95  Function %96\n%86 = OpVariable  %87  Function %88\n%91 = OpVariable  %92  Function %93\nOpBranch %97\n%97 = OpLabel\nOpLine %3 13 13\nOpLine %3 14 24\n%98 = OpVectorShuffle  %6  %73 %73 1 1\n%99 = OpDot  %4  %66 %98\n%100 = OpCompositeConstruct  %6  %99 %99\n%101 = OpFAdd  %6  %66 %100\n%102 = OpExtInst  %6  %1 Floor %101\nOpLine %3 14 5\nOpStore %86 %102\nOpLine %3 15 14\n%103 = OpLoad  %6  %86\n%104 = OpFSub  %6  %66 %103\n%105 = OpLoad  %6  %86\n%106 = OpVectorShuffle  %6  %73 %73 0 0\n%107 = OpDot  %4  %105 %106\n%108 = OpCompositeConstruct  %6  %107 %107\n%109 = OpFAdd  %6  %104 %108\nOpLine %3 17 32\nOpLine %3 17 25\n%110 = OpCompositeExtract  %4  %109 0\n%111 = OpCompositeExtract  %4  %109 1\n%113 = OpFOrdLessThan  %112  %110 %111\n%116 = OpCompositeConstruct  %115  %113 %113\n%114 = OpSelect  %6  %116 %76 %75\nOpLine %3 17 5\nOpStore %89 %114\nOpLine %3 18 26\n%117 = OpVectorShuffle  %7  %109 %109 0 1 0 1\n%118 = OpVectorShuffle  %7  %73 %73 0 0 2 2\n%119 = OpFAdd  %7  %117 %118\n%120 = OpLoad  %6  %89\nOpLine %3 18 26\n%121 = OpCompositeConstruct  %7  %120 %74 %74\n%122 = OpFSub  %7  %119 %121\nOpLine %3 18 5\nOpStore %91 %122\nOpLine %3 1 1\n%123 = OpLoad  %6  %86\nOpLine %3 19 9\n%124 = OpFRem  %6  %123 %77\nOpLine %3 19 5\nOpStore %86 %124\nOpLine %3 20 31\n%127 = OpAccessChain  %125  %86 %126\n%128 = OpLoad  %4  %127\nOpLine %3 20 51\n%129 = OpAccessChain  %125  %89 %126\n%130 = OpLoad  %4  %129\nOpLine %3 20 31\n%131 = OpCompositeConstruct  %5  %74 %130 %56\n%132 = OpCompositeConstruct  %5  %128 %128 %128\n%133 = OpFAdd  %5  %132 %131\nOpLine %3 20 22\n%134 = OpFunctionCall  %5  %53 %133\nOpLine %3 20 22\n%136 = OpAccessChain  %125  %86 %135\n%137 = OpLoad  %4  %136\n%138 = OpCompositeConstruct  %5  %137 %137 %137\n%139 = OpFAdd  %5  %134 %138\nOpLine %3 20 84\n%140 = OpAccessChain  %125  %89 %135\n%141 = OpLoad  %4  %140\nOpLine %3 20 22\n%142 = OpCompositeConstruct  %5  %74 %141 %56\n%143 = OpFAdd  %5  %139 %142\nOpLine %3 20 13\n%144 = OpFunctionCall  %5  %53 %143\nOpLine %3 21 28\n%145 = OpDot  %4  %109 %109\n%146 = OpLoad  %7  %91\n%147 = OpVectorShuffle  %6  %146 %146 0 1\n%148 = OpLoad  %7  %91\n%149 = OpVectorShuffle  %6  %148 %148 0 1\n%150 = OpDot  %4  %147 %149\n%151 = OpLoad  %7  %91\n%152 = OpVectorShuffle  %6  %151 %151 2 3\n%153 = OpLoad  %7  %91\n%154 = OpVectorShuffle  %6  %153 %153 2 3\n%155 = OpDot  %4  %152 %154\n%156 = OpCompositeConstruct  %5  %145 %150 %155\nOpLine %3 21 28\n%157 = OpFSub  %5  %79 %156\nOpLine %3 21 24\n%158 = OpExtInst  %5  %1 FMax %157 %80\nOpLine %3 21 5\nOpStore %94 %158\nOpLine %3 22 9\n%159 = OpLoad  %5  %94\n%160 = OpLoad  %5  %94\n%161 = OpFMul  %5  %159 %160\nOpLine %3 22 5\nOpStore %94 %161\nOpLine %3 23 9\n%162 = OpLoad  %5  %94\n%163 = OpLoad  %5  %94\n%164 = OpFMul  %5  %162 %163\nOpLine %3 23 5\nOpStore %94 %164\nOpLine %3 24 18\n%165 = OpVectorShuffle  %5  %73 %73 3 3 3\n%166 = OpFMul  %5  %144 %165\n%167 = OpExtInst  %5  %1 Fract %166\nOpLine %3 24 13\n%168 = OpVectorTimesScalar  %5  %167 %81\nOpLine %3 24 37\nOpLine %3 24 13\n%169 = OpFSub  %5  %168 %57\nOpLine %3 25 13\n%170 = OpExtInst  %5  %1 FAbs %169\nOpLine %3 25 22\nOpLine %3 25 13\n%171 = OpFSub  %5  %170 %79\nOpLine %3 26 24\nOpLine %3 26 14\n%172 = OpFAdd  %5  %169 %79\n%173 = OpExtInst  %5  %1 Floor %172\nOpLine %3 27 14\n%174 = OpFSub  %5  %169 %173\nOpLine %3 1 1\n%175 = OpLoad  %5  %94\nOpLine %3 28 53\n%176 = OpFMul  %5  %174 %174\n%177 = OpFMul  %5  %171 %171\n%178 = OpFAdd  %5  %176 %177\nOpLine %3 28 14\n%179 = OpVectorTimesScalar  %5  %178 %82\nOpLine %3 28 9\n%180 = OpFSub  %5  %84 %179\n%181 = OpFMul  %5  %175 %180\nOpLine %3 28 5\nOpStore %94 %181\nOpLine %3 29 13\n%182 = OpCompositeExtract  %4  %174 0\n%183 = OpCompositeExtract  %4  %109 0\n%184 = OpFMul  %4  %182 %183\n%185 = OpCompositeExtract  %4  %171 0\n%186 = OpCompositeExtract  %4  %109 1\n%187 = OpFMul  %4  %185 %186\n%188 = OpFAdd  %4  %184 %187\n%189 = OpVectorShuffle  %6  %174 %174 1 2\n%190 = OpLoad  %7  %91\n%191 = OpVectorShuffle  %6  %190 %190 0 2\n%192 = OpFMul  %6  %189 %191\n%193 = OpVectorShuffle  %6  %171 %171 1 2\n%194 = OpLoad  %7  %91\n%195 = OpVectorShuffle  %6  %194 %194 1 3\n%196 = OpFMul  %6  %193 %195\n%197 = OpFAdd  %6  %192 %196\n%198 = OpCompositeConstruct  %5  %188 %197\nOpLine %3 30 19\n%199 = OpLoad  %5  %94\n%200 = OpDot  %4  %199 %198\nOpLine %3 30 12\n%201 = OpFMul  %4  %85 %200\nOpReturnValue %201\nOpFunctionEnd\n%204 = OpFunction  %4  None %68\n%203 = OpFunctionParameter  %6\n%202 = OpLabel\n%214 = OpVariable  %125  Function %74\n%216 = OpVariable  %217  Function %135\n%212 = OpVariable  %87  Function %213\n%215 = OpVariable  %125  Function %78\n%236 = OpVariable  %232  Function %235\nOpBranch %218\n%218 = OpLabel\nOpLine %3 36 13\n%219 = OpVectorTimesScalar  %6  %203 %206\nOpLine %3 36 5\nOpStore %212 %219\nOpLine %3 39 17\nOpLine %3 40 14\nOpLine %3 41 15\n%220 = OpCompositeExtract  %4  %211 0\n%221 = OpCompositeExtract  %4  %211 1\n%222 = OpCompositeExtract  %4  %211 1\n%223 = OpFNegate  %4  %222\n%224 = OpCompositeExtract  %4  %211 0\n%225 = OpCompositeConstruct  %6  %220 %221\n%226 = OpCompositeConstruct  %6  %223 %224\n%227 = OpCompositeConstruct  %9  %225 %226\nOpBranch %228\n%228 = OpLabel\nOpLine %3 43 5\nOpLoopMerge %229 %231 None\nOpBranch %237\n%237 = OpLabel\n%238 = OpLoad  %10  %236\n%239 = OpIEqual  %115  %233 %238\n%240 = OpAll  %112  %239\nOpSelectionMerge %241 None\nOpBranchConditional %240 %229 %241\n%241 = OpLabel\n%242 = OpCompositeExtract  %8  %238 1\n%243 = OpIEqual  %112  %242 %135\n%244 = OpSelect  %8  %243 %126 %135\n%245 = OpCompositeConstruct  %10  %244 %126\n%246 = OpISub  %10  %238 %245\nOpStore %236 %246\nOpBranch %230\n%230 = OpLabel\nOpLine %3 43 22\n%247 = OpLoad  %8  %216\n%248 = OpULessThan  %112  %247 %205\nOpLine %3 43 21\nOpSelectionMerge %249 None\nOpBranchConditional %248 %249 %250\n%250 = OpLabel\nOpBranch %229\n%249 = OpLabel\nOpBranch %251\n%251 = OpLabel\nOpLine %3 1 1\n%253 = OpLoad  %4  %214\n%254 = OpLoad  %4  %215\n%255 = OpLoad  %6  %212\nOpLine %3 44 21\n%256 = OpFunctionCall  %4  %67 %255\nOpLine %3 44 13\n%257 = OpFMul  %4  %254 %256\n%258 = OpFAdd  %4  %253 %257\nOpLine %3 44 9\nOpStore %214 %258\nOpLine %3 45 13\n%259 = OpLoad  %6  %212\n%260 = OpMatrixTimesVector  %6  %227 %259\nOpLine %3 45 13\n%261 = OpVectorTimesScalar  %6  %260 %81\n%262 = OpFAdd  %6  %261 %208\nOpLine %3 45 9\nOpStore %212 %262\nOpLine %3 1 1\n%263 = OpLoad  %4  %215\nOpLine %3 46 13\n%264 = OpFMul  %4  %263 %78\nOpLine %3 46 9\nOpStore %215 %264\nOpBranch %252\n%252 = OpLabel\nOpBranch %231\n%231 = OpLabel\nOpLine %3 1 1\n%265 = OpLoad  %8  %216\nOpLine %3 43 43\n%266 = OpIAdd  %8  %265 %126\nOpLine %3 43 39\nOpStore %216 %266\nOpBranch %228\n%229 = OpLabel\nOpLine %3 1 1\n%267 = OpLoad  %4  %214\nOpReturnValue %267\nOpFunctionEnd\n%271 = OpFunction  %5  None %272\n%269 = OpFunctionParameter  %6\n%270 = OpFunctionParameter  %6\n%268 = OpLabel\nOpBranch %273\n%273 = OpLabel\nOpLine %3 77 9\n%274 = OpCompositeExtract  %4  %269 0\n%275 = OpCompositeExtract  %4  %270 0\n%276 = OpCompositeExtract  %4  %270 1\nOpLine %3 78 49\n%277 = OpFunctionCall  %4  %204 %269\nOpLine %3 76 12\n%278 = OpExtInst  %4  %1 FMix %275 %276 %277\n%279 = OpCompositeExtract  %4  %269 1\n%280 = OpCompositeConstruct  %5  %274 %278 %279\nOpReturnValue %280\nOpFunctionEnd\n%284 = OpFunction  %14  None %285\n%282 = OpFunctionParameter  %6\n%283 = OpFunctionParameter  %6\n%281 = OpLabel\nOpBranch %292\n%292 = OpLabel\nOpLine %3 84 13\n%293 = OpFunctionCall  %5  %271 %282 %283\nOpLine %3 86 29\n%294 = OpFAdd  %6  %282 %287\nOpLine %3 86 15\n%295 = OpFunctionCall  %5  %271 %294 %283\nOpLine %3 86 15\n%296 = OpFSub  %5  %295 %293\nOpLine %3 87 29\n%297 = OpFAdd  %6  %282 %288\nOpLine %3 87 15\n%298 = OpFunctionCall  %5  %271 %297 %283\nOpLine %3 87 15\n%299 = OpFSub  %5  %298 %293\nOpLine %3 88 29\n%300 = OpFAdd  %6  %282 %290\nOpLine %3 88 15\n%301 = OpFunctionCall  %5  %271 %300 %283\nOpLine %3 88 15\n%302 = OpFSub  %5  %301 %293\nOpLine %3 89 29\n%303 = OpFAdd  %6  %282 %291\nOpLine %3 89 15\n%304 = OpFunctionCall  %5  %271 %303 %283\nOpLine %3 89 15\n%305 = OpFSub  %5  %304 %293\nOpLine %3 91 14\n%306 = OpExtInst  %5  %1 Cross %299 %296\n%307 = OpExtInst  %5  %1 Normalize %306\nOpLine %3 92 14\n%308 = OpExtInst  %5  %1 Cross %305 %302\n%309 = OpExtInst  %5  %1 Normalize %308\nOpLine %3 94 14\n%310 = OpFAdd  %5  %307 %309\nOpLine %3 94 13\n%311 = OpVectorTimesScalar  %5  %310 %78\nOpLine %3 96 12\n%312 = OpCompositeConstruct  %14  %293 %311\nOpReturnValue %312\nOpFunctionEnd\n%313 = OpFunction  %8  None %314\n%315 = OpFunctionParameter  %8\n%316 = OpFunctionParameter  %8\n%317 = OpLabel\n%318 = OpIEqual  %112  %316 %135\n%319 = OpSelect  %8  %318 %126 %316\n%320 = OpUDiv  %8  %315 %319\nOpReturnValue %320\nOpFunctionEnd\n%325 = OpFunction  %6  None %326\n%322 = OpFunctionParameter  %8\n%323 = OpFunctionParameter  %10\n%324 = OpFunctionParameter  %11\n%321 = OpLabel\nOpBranch %327\n%327 = OpLabel\nOpLine %3 101 9\n%328 = OpConvertUToF  %4  %322\n%329 = OpCompositeExtract  %8  %323 0\nOpLine %3 101 9\n%330 = OpIAdd  %8  %329 %126\n%331 = OpConvertUToF  %4  %330\n%332 = OpFRem  %4  %328 %331\n%333 = OpCompositeExtract  %8  %323 0\nOpLine %3 100 12\n%334 = OpIAdd  %8  %333 %126\n%335 = OpFunctionCall  %8  %313 %322 %334\n%336 = OpConvertUToF  %4  %335\n%337 = OpCompositeConstruct  %6  %332 %336\n%338 = OpConvertSToF  %6  %324\n%339 = OpFAdd  %6  %337 %338\nOpReturnValue %339\nOpFunctionEnd\n%342 = OpFunction  %5  None %343\n%341 = OpFunctionParameter  %6\n%340 = OpLabel\nOpBranch %350\n%350 = OpLabel\nOpLine %3 270 9\n%351 = OpFunctionCall  %4  %67 %341\nOpLine %3 270 9\n%352 = OpFMul  %4  %351 %78\nOpLine %3 270 9\n%353 = OpFAdd  %4  %352 %78\nOpLine %3 271 17\n%354 = OpFAdd  %6  %341 %346\nOpLine %3 271 9\n%355 = OpFunctionCall  %4  %67 %354\nOpLine %3 271 9\n%356 = OpFMul  %4  %355 %78\nOpLine %3 271 9\n%357 = OpFAdd  %4  %356 %78\nOpLine %3 272 17\n%358 = OpFAdd  %6  %341 %349\nOpLine %3 272 9\n%359 = OpFunctionCall  %4  %67 %358\nOpLine %3 272 9\n%360 = OpFMul  %4  %359 %78\nOpLine %3 269 12\n%361 = OpFAdd  %4  %360 %78\n%362 = OpCompositeConstruct  %5  %353 %357 %361\nOpReturnValue %362\nOpFunctionEnd\n%367 = OpFunction  %2  None %368\n%363 = OpLabel\n%366 = OpLoad  %19  %364\n%370 = OpAccessChain  %369  %29 %135\nOpBranch %375\n%375 = OpLabel\nOpLine %3 111 22\n%376 = OpCompositeExtract  %8  %366 0\nOpLine %3 113 36\n%378 = OpAccessChain  %377  %370 %135\n%379 = OpLoad  %10  %378\nOpLine %3 113 59\n%381 = OpAccessChain  %380  %370 %126\n%382 = OpLoad  %11  %381\nOpLine %3 113 13\n%383 = OpFunctionCall  %6  %325 %376 %379 %382\nOpLine %3 115 5\nOpLine %3 115 51\n%387 = OpAccessChain  %386  %370 %372\n%388 = OpLoad  %6  %387\nOpLine %3 115 33\n%389 = OpFunctionCall  %14  %284 %383 %388\nOpLine %3 115 5\n%390 = OpAccessChain  %385  %32 %135 %376\nOpStore %390 %389\nOpLine %3 118 23\n%391 = OpCompositeExtract  %8  %366 0\nOpLine %3 118 23\n%392 = OpIMul  %8  %391 %371\nOpLine %3 120 25\n%394 = OpAccessChain  %393  %370 %135 %135\n%395 = OpLoad  %8  %394\nOpLine %3 120 25\n%396 = OpAccessChain  %393  %370 %135 %126\n%397 = OpLoad  %8  %396\n%398 = OpIMul  %8  %395 %397\nOpLine %3 120 9\n%399 = OpIMul  %8  %398 %371\n%400 = OpUGreaterThanEqual  %112  %392 %399\nOpLine %3 120 5\nOpSelectionMerge %401 None\nOpBranchConditional %400 %402 %401\n%402 = OpLabel\nOpReturn\n%401 = OpLabel\nOpLine %3 122 28\n%403 = OpCompositeExtract  %8  %366 0\nOpLine %3 122 15\n%404 = OpAccessChain  %393  %370 %135 %135\n%405 = OpLoad  %8  %404\n%406 = OpFunctionCall  %8  %313 %403 %405\n%407 = OpIAdd  %8  %376 %406\nOpLine %3 123 15\n%408 = OpIAdd  %8  %407 %126\nOpLine %3 124 15\n%409 = OpAccessChain  %393  %370 %135 %135\n%410 = OpLoad  %8  %409\n%411 = OpIAdd  %8  %407 %410\nOpLine %3 124 15\n%412 = OpIAdd  %8  %411 %126\nOpLine %3 125 15\n%413 = OpIAdd  %8  %412 %126\nOpLine %3 127 5\nOpLine %3 127 5\n%416 = OpAccessChain  %415  %34 %135 %392\nOpStore %416 %407\nOpLine %3 128 5\nOpLine %3 128 5\n%417 = OpIAdd  %8  %392 %126\nOpLine %3 128 5\n%418 = OpAccessChain  %415  %34 %135 %417\nOpStore %418 %412\nOpLine %3 129 5\nOpLine %3 129 5\n%419 = OpIAdd  %8  %392 %372\nOpLine %3 129 5\n%420 = OpAccessChain  %415  %34 %135 %419\nOpStore %420 %413\nOpLine %3 130 5\nOpLine %3 130 5\n%421 = OpIAdd  %8  %392 %373\nOpLine %3 130 5\n%422 = OpAccessChain  %415  %34 %135 %421\nOpStore %422 %407\nOpLine %3 131 5\nOpLine %3 131 5\n%423 = OpIAdd  %8  %392 %374\nOpLine %3 131 5\n%424 = OpAccessChain  %415  %34 %135 %423\nOpStore %424 %413\nOpLine %3 132 5\nOpLine %3 132 5\n%425 = OpIAdd  %8  %392 %205\nOpLine %3 132 5\n%426 = OpAccessChain  %415  %34 %135 %425\nOpStore %426 %408\nOpReturn\nOpFunctionEnd\n%427 = OpFunction  %8  None %314\n%428 = OpFunctionParameter  %8\n%429 = OpFunctionParameter  %8\n%430 = OpLabel\n%431 = OpIEqual  %112  %429 %135\n%432 = OpSelect  %8  %431 %126 %429\n%433 = OpUMod  %8  %428 %432\nOpReturnValue %433\nOpFunctionEnd\n%444 = OpFunction  %2  None %368\n%434 = OpLabel\n%437 = OpLoad  %8  %435\n%446 = OpAccessChain  %445  %36 %135\nOpBranch %449\n%449 = OpLabel\nOpLine %3 161 19\n%450 = OpIAdd  %8  %437 %372\nOpLine %3 161 18\n%451 = OpFunctionCall  %8  %313 %450 %373\nOpLine %3 161 13\n%452 = OpFunctionCall  %8  %427 %451 %372\n%453 = OpConvertUToF  %4  %452\nOpLine %3 162 19\n%454 = OpIAdd  %8  %437 %126\nOpLine %3 162 18\n%455 = OpFunctionCall  %8  %313 %454 %373\nOpLine %3 162 13\n%456 = OpFunctionCall  %8  %427 %455 %372\n%457 = OpConvertUToF  %4  %456\nOpLine %3 163 14\n%458 = OpCompositeConstruct  %6  %453 %457\nOpLine %3 165 30\n%459 = OpVectorTimesScalar  %6  %458 %81\nOpLine %3 165 30\n%460 = OpFAdd  %6  %448 %459\nOpLine %3 165 20\n%461 = OpCompositeConstruct  %7  %460 %74 %56\nOpLine %3 168 21\n%462 = OpCompositeExtract  %4  %458 0\nOpLine %3 168 21\n%463 = OpAccessChain  %393  %446 %373\n%464 = OpLoad  %8  %463\n%465 = OpConvertUToF  %4  %464\n%466 = OpFMul  %4  %462 %465\n%467 = OpCompositeExtract  %4  %458 1\nOpLine %3 168 17\n%468 = OpAccessChain  %393  %446 %373\n%469 = OpLoad  %8  %468\n%470 = OpConvertUToF  %4  %469\n%471 = OpFMul  %4  %467 %470\n%472 = OpFAdd  %4  %466 %471\n%474 = OpExtInst  %4  %1 FClamp %472 %74 %473\n%475 = OpConvertFToU  %8  %474\nOpLine %3 168 17\n%476 = OpAccessChain  %393  %446 %374\n%477 = OpLoad  %8  %476\n%478 = OpIAdd  %8  %475 %477\nOpLine %3 170 12\n%479 = OpCompositeConstruct  %21  %478 %461 %458\n%480 = OpCompositeExtract  %8  %479 0\nOpStore %438 %480\n%481 = OpCompositeExtract  %7  %479 1\nOpStore %440 %481\n%482 = OpCompositeExtract  %6  %479 2\nOpStore %442 %482\nOpReturn\nOpFunctionEnd\n%495 = OpFunction  %2  None %368\n%483 = OpLabel\n%498 = OpVariable  %125  Function %74\n%499 = OpVariable  %217  Function %135\n%486 = OpLoad  %8  %485\n%489 = OpLoad  %7  %487\n%492 = OpLoad  %6  %490\n%484 = OpCompositeConstruct  %21  %486 %489 %492\n%496 = OpAccessChain  %445  %36 %135\nOpBranch %500\n%500 = OpLabel\nOpLine %3 181 17\n%501 = OpCompositeExtract  %6  %484 2\n%502 = OpCompositeExtract  %4  %501 0\nOpLine %3 181 17\n%503 = OpAccessChain  %393  %496 %373\n%504 = OpLoad  %8  %503\n%505 = OpConvertUToF  %4  %504\n%506 = OpFMul  %4  %502 %505\n%507 = OpCompositeExtract  %6  %484 2\n%508 = OpCompositeExtract  %4  %507 1\nOpLine %3 181 70\n%509 = OpAccessChain  %393  %496 %373\n%510 = OpLoad  %8  %509\nOpLine %3 181 13\n%511 = OpAccessChain  %393  %496 %373\n%512 = OpLoad  %8  %511\n%513 = OpIMul  %8  %510 %512\n%514 = OpConvertUToF  %4  %513\n%515 = OpFMul  %4  %508 %514\n%516 = OpFAdd  %4  %506 %515\n%517 = OpExtInst  %4  %1 FClamp %516 %74 %473\n%518 = OpConvertFToU  %8  %517\nOpLine %3 181 13\n%519 = OpAccessChain  %393  %496 %374\n%520 = OpLoad  %8  %519\n%521 = OpIAdd  %8  %518 %520\nOpLine %3 182 32\n%522 = OpConvertUToF  %4  %521\nOpLine %3 182 22\n%523 = OpFDiv  %4  %522 %497\n%524 = OpExtInst  %4  %1 Floor %523\n%525 = OpExtInst  %4  %1 FClamp %524 %74 %473\n%526 = OpConvertFToU  %8  %525\nOpLine %3 183 22\n%527 = OpFunctionCall  %8  %427 %521 %371\nOpLine %3 185 36\n%528 = OpAccessChain  %377  %496 %135\n%529 = OpLoad  %10  %528\nOpLine %3 185 57\n%530 = OpAccessChain  %380  %496 %126\n%531 = OpLoad  %11  %530\nOpLine %3 185 13\n%532 = OpFunctionCall  %6  %325 %526 %529 %531\nOpLine %3 186 31\n%533 = OpAccessChain  %386  %496 %372\n%534 = OpLoad  %6  %533\nOpLine %3 186 13\n%535 = OpFunctionCall  %14  %284 %532 %534\nOpLine %3 190 5\nOpSelectionMerge %536 None\nOpSwitch %527 %543 0 %537 1 %538 2 %539 3 %540 4 %541 5 %542\n%537 = OpLabel\nOpLine %3 191 37\n%544 = OpCompositeExtract  %5  %535 0\n%545 = OpCompositeExtract  %4  %544 0\nOpLine %3 191 20\nOpStore %498 %545\nOpBranch %536\n%538 = OpLabel\nOpLine %3 192 37\n%546 = OpCompositeExtract  %5  %535 0\n%547 = OpCompositeExtract  %4  %546 1\nOpLine %3 192 20\nOpStore %498 %547\nOpBranch %536\n%539 = OpLabel\nOpLine %3 193 37\n%548 = OpCompositeExtract  %5  %535 0\n%549 = OpCompositeExtract  %4  %548 2\nOpLine %3 193 20\nOpStore %498 %549\nOpBranch %536\n%540 = OpLabel\nOpLine %3 194 37\n%550 = OpCompositeExtract  %5  %535 1\n%551 = OpCompositeExtract  %4  %550 0\nOpLine %3 194 20\nOpStore %498 %551\nOpBranch %536\n%541 = OpLabel\nOpLine %3 195 37\n%552 = OpCompositeExtract  %5  %535 1\n%553 = OpCompositeExtract  %4  %552 1\nOpLine %3 195 20\nOpStore %498 %553\nOpBranch %536\n%542 = OpLabel\nOpLine %3 196 37\n%554 = OpCompositeExtract  %5  %535 1\n%555 = OpCompositeExtract  %4  %554 2\nOpLine %3 196 20\nOpStore %498 %555\nOpBranch %536\n%543 = OpLabel\nOpBranch %536\n%536 = OpLabel\nOpLine %3 200 15\n%556 = OpAccessChain  %393  %496 %135 %135\n%557 = OpLoad  %8  %556\n%558 = OpFunctionCall  %8  %313 %526 %557\n%559 = OpIAdd  %8  %526 %558\nOpLine %3 201 15\n%560 = OpIAdd  %8  %559 %126\nOpLine %3 202 15\n%561 = OpAccessChain  %393  %496 %135 %135\n%562 = OpLoad  %8  %561\n%563 = OpIAdd  %8  %559 %562\nOpLine %3 202 15\n%564 = OpIAdd  %8  %563 %126\nOpLine %3 203 15\n%565 = OpIAdd  %8  %564 %126\nOpLine %3 206 5\nOpSelectionMerge %566 None\nOpSwitch %527 %571 0 %567 3 %567 2 %568 4 %568 1 %569 5 %570\n%567 = OpLabel\nOpLine %3 207 24\nOpStore %499 %559\nOpBranch %566\n%568 = OpLabel\nOpLine %3 208 24\nOpStore %499 %565\nOpBranch %566\n%569 = OpLabel\nOpLine %3 209 20\nOpStore %499 %564\nOpBranch %566\n%570 = OpLabel\nOpLine %3 210 20\nOpStore %499 %560\nOpBranch %566\n%571 = OpLabel\nOpBranch %566\n%566 = OpLabel\nOpLine %3 213 13\n%572 = OpCompositeExtract  %8  %484 0\nOpLine %3 213 5\nOpStore %499 %572\nOpLine %3 222 27\n%573 = OpLoad  %4  %498\n%574 = OpBitcast  %8  %573\nOpLine %3 223 12\n%575 = OpLoad  %8  %499\n%576 = OpCompositeConstruct  %22  %574 %575\n%577 = OpCompositeExtract  %8  %576 0\nOpStore %493 %577\n%578 = OpCompositeExtract  %8  %576 1\nOpStore %494 %578\nOpReturn\nOpFunctionEnd\n%590 = OpFunction  %2  None %368\n%579 = OpLabel\n%583 = OpLoad  %5  %581\n%585 = OpLoad  %5  %584\n%580 = OpCompositeConstruct  %14  %583 %585\n%592 = OpAccessChain  %591  %39 %135\nOpBranch %593\n%593 = OpLabel\nOpLine %3 254 25\n%595 = OpAccessChain  %594  %592 %126\n%596 = OpLoad  %23  %595\n%597 = OpCompositeExtract  %5  %580 0\nOpLine %3 254 25\n%598 = OpCompositeConstruct  %7  %597 %56\n%599 = OpMatrixTimesVector  %7  %596 %598\nOpLine %3 255 18\n%600 = OpCompositeExtract  %5  %580 1\nOpLine %3 256 12\n%601 = OpCompositeExtract  %5  %580 0\n%602 = OpCompositeConstruct  %26  %599 %600 %601\n%603 = OpCompositeExtract  %7  %602 0\nOpStore %586 %603\n%604 = OpCompositeExtract  %5  %602 1\nOpStore %587 %604\n%605 = OpCompositeExtract  %5  %602 2\nOpStore %589 %605\nOpReturn\nOpFunctionEnd\n%615 = OpFunction  %2  None %368\n%606 = OpLabel\n%629 = OpVariable  %95  Function %630\n%609 = OpLoad  %7  %608\n%611 = OpLoad  %5  %610\n%613 = OpLoad  %5  %612\n%607 = OpCompositeConstruct  %26  %609 %611 %613\n%616 = OpAccessChain  %591  %39 %135\n%618 = OpAccessChain  %617  %42 %135\n%619 = OpLoad  %27  %45\n%620 = OpLoad  %28  %47\n%621 = OpLoad  %27  %49\n%622 = OpLoad  %28  %50\nOpBranch %631\n%631 = OpLabel\nOpLine %3 283 13\nOpLine %3 283 13\nOpLine %3 283 5\n%632 = OpFunctionCall  %5  %342 %623\nOpLine %3 285 28\nOpLine %3 285 17\n%633 = OpCompositeExtract  %5  %607 2\n%634 = OpExtInst  %5  %1 Fract %633\n%635 = OpExtInst  %5  %1 SmoothStep %80 %624 %634\nOpLine %3 285 5\nOpStore %629 %635\nOpLine %3 286 17\nOpLine %3 286 13\n%636 = OpAccessChain  %125  %629 %135\n%637 = OpLoad  %4  %636\n%638 = OpAccessChain  %125  %629 %126\n%639 = OpLoad  %4  %638\n%640 = OpFMul  %4  %637 %639\n%641 = OpAccessChain  %125  %629 %372\n%642 = OpLoad  %4  %641\n%643 = OpFMul  %4  %640 %642\n%644 = OpCompositeConstruct  %5  %643 %643 %643\n%645 = OpExtInst  %5  %1 FMix %626 %628 %644\nOpLine %3 286 5\nOpStore %629 %645\nOpLine %3 289 25\n%647 = OpAccessChain  %646  %618 %126\n%648 = OpLoad  %5  %647\n%649 = OpVectorTimesScalar  %5  %648 %286\nOpLine %3 291 21\n%650 = OpAccessChain  %646  %618 %135\n%651 = OpLoad  %5  %650\n%652 = OpCompositeExtract  %5  %607 2\n%653 = OpFSub  %5  %651 %652\n%654 = OpExtInst  %5  %1 Normalize %653\nOpLine %3 292 20\n%656 = OpAccessChain  %655  %616 %135\n%657 = OpLoad  %7  %656\n%658 = OpVectorShuffle  %5  %657 %657 0 1 2\n%659 = OpCompositeExtract  %5  %607 2\n%660 = OpFSub  %5  %658 %659\n%661 = OpExtInst  %5  %1 Normalize %660\nOpLine %3 293 20\n%662 = OpFAdd  %5  %661 %654\n%663 = OpExtInst  %5  %1 Normalize %662\nOpLine %3 295 32\n%664 = OpCompositeExtract  %5  %607 1\n%665 = OpDot  %4  %664 %654\nOpLine %3 295 28\n%666 = OpExtInst  %4  %1 FMax %665 %74\nOpLine %3 296 25\n%667 = OpAccessChain  %646  %618 %126\n%668 = OpLoad  %5  %667\n%669 = OpVectorTimesScalar  %5  %668 %666\nOpLine %3 298 37\n%670 = OpCompositeExtract  %5  %607 1\n%671 = OpDot  %4  %670 %663\nOpLine %3 298 33\n%672 = OpExtInst  %4  %1 FMax %671 %74\nOpLine %3 298 29\n%673 = OpExtInst  %4  %1 Pow %672 %345\nOpLine %3 299 26\n%674 = OpAccessChain  %646  %618 %126\n%675 = OpLoad  %5  %674\n%676 = OpVectorTimesScalar  %5  %675 %673\nOpLine %3 301 18\n%677 = OpFAdd  %5  %649 %669\n%678 = OpFAdd  %5  %677 %676\n%679 = OpLoad  %5  %629\n%680 = OpFMul  %5  %678 %679\nOpLine %3 303 12\n%681 = OpCompositeConstruct  %7  %680 %56\nOpStore %614 %681\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-draw-index.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 21\nOpCapability Shader\nOpCapability DrawParameters\nOpExtension \"SPV_KHR_shader_draw_parameters\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %14 \"vertex\" %9 %12\nOpMemberDecorate %4 0 Offset 0\nOpDecorate %9 BuiltIn DrawIndex\nOpDecorate %12 BuiltIn Position\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 4\n%10 = OpTypePointer Input %3\n%9 = OpVariable  %10  Input\n%13 = OpTypePointer Output %5\n%12 = OpVariable  %13  Output\n%15 = OpTypeFunction %2\n%16 = OpConstant  %6  1\n%14 = OpFunction  %2  None %15\n%7 = OpLabel\n%11 = OpLoad  %3  %9\n%8 = OpCompositeConstruct  %4  %11\nOpBranch %17\n%17 = OpLabel\n%18 = OpCompositeExtract  %3  %8 0\n%19 = OpConvertUToF  %6  %18\n%20 = OpCompositeConstruct  %5  %19 %16 %16 %16\nOpStore %12 %20\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-dualsource.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 26\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %10 \"main\" %7 %9\nOpExecutionMode %10 OriginUpperLeft\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpDecorate %7 Location 0\nOpDecorate %7 Index 0\nOpDecorate %9 Location 0\nOpDecorate %9 Index 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%5 = OpTypeStruct %3 %3\n%8 = OpTypePointer Output %3\n%7 = OpVariable  %8  Output\n%9 = OpVariable  %8  Output\n%11 = OpTypeFunction %2\n%12 = OpConstant  %4  0.4\n%13 = OpConstant  %4  0.3\n%14 = OpConstant  %4  0.2\n%15 = OpConstant  %4  0.1\n%16 = OpConstantComposite  %3  %12 %13 %14 %15\n%17 = OpConstant  %4  0.9\n%18 = OpConstant  %4  0.8\n%19 = OpConstant  %4  0.7\n%20 = OpConstant  %4  0.6\n%21 = OpConstantComposite  %3  %17 %18 %19 %20\n%22 = OpConstantComposite  %5  %16 %21\n%10 = OpFunction  %2  None %11\n%6 = OpLabel\nOpBranch %23\n%23 = OpLabel\n%24 = OpCompositeExtract  %3  %22 0\nOpStore %7 %24\n%25 = OpCompositeExtract  %3  %22 1\nOpStore %9 %25\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-early-depth-test-conservative.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 17\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %11 \"main\" %6 %9\nOpExecutionMode %11 OriginUpperLeft\nOpExecutionMode %11 DepthLess\nOpExecutionMode %11 DepthReplacing\nOpDecorate %6 BuiltIn FragCoord\nOpDecorate %9 BuiltIn FragDepth\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%7 = OpTypePointer Input %4\n%6 = OpVariable  %7  Input\n%10 = OpTypePointer Output %3\n%9 = OpVariable  %10  Output\n%12 = OpTypeFunction %2\n%13 = OpConstant  %3  0.1\n%11 = OpFunction  %2  None %12\n%5 = OpLabel\n%8 = OpLoad  %4  %6\nOpBranch %14\n%14 = OpLabel\n%15 = OpCompositeExtract  %3  %8 2\n%16 = OpFSub  %3  %15 %13\nOpStore %9 %16\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-early-depth-test-force.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 16\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %8 \"main\" %6\nOpExecutionMode %8 OriginUpperLeft\nOpExecutionMode %8 EarlyFragmentTests\nOpDecorate %6 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%7 = OpTypePointer Output %3\n%6 = OpVariable  %7  Output\n%9 = OpTypeFunction %2\n%10 = OpConstant  %4  0.4\n%11 = OpConstant  %4  0.3\n%12 = OpConstant  %4  0.2\n%13 = OpConstant  %4  0.1\n%14 = OpConstantComposite  %3  %10 %11 %12 %13\n%8 = OpFunction  %2  None %9\n%5 = OpLabel\nOpBranch %15\n%15 = OpLabel\nOpStore %6 %14\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-empty-if.spvasm",
    "content": "; SPIR-V\n; Version: 1.6\n; Generator: rspirv\n; Bound: 18\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %9 \"comp\" %6\nOpExecutionMode %9 LocalSize 1 1 1\nOpDecorate %6 BuiltIn GlobalInvocationId\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%3 = OpTypeVector %4 3\n%7 = OpTypePointer Input %3\n%6 = OpVariable  %7  Input\n%10 = OpTypeFunction %2\n%11 = OpConstant  %4  0\n%12 = OpTypeInt 32 1\n%13 = OpConstant  %12  2\n%16 = OpTypeBool\n%9 = OpFunction  %2  None %10\n%5 = OpLabel\n%8 = OpLoad  %3  %6\nOpBranch %14\n%14 = OpLabel\n%15 = OpCompositeExtract  %4  %8 0\n%17 = OpIEqual  %16  %15 %11\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-empty.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 7\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %4 \"main\"\nOpExecutionMode %4 LocalSize 1 1 1\n%2 = OpTypeVoid\n%5 = OpTypeFunction %2\n%4 = OpFunction  %2  None %5\n%3 = OpLabel\nOpBranch %6\n%6 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-extra.spvasm",
    "content": "; SPIR-V\n; Version: 1.2\n; Generator: rspirv\n; Bound: 47\nOpCapability Shader\nOpCapability Geometry\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %22 \"main\" %14 %17 %20\nOpExecutionMode %22 OriginUpperLeft\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 8\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 16\nOpDecorate %10 Block\nOpMemberDecorate %10 0 Offset 0\nOpDecorate %14 Location 0\nOpDecorate %17 BuiltIn PrimitiveId\nOpDecorate %17 Flat\nOpDecorate %20 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%5 = OpTypeFloat 32\n%4 = OpTypeVector %5 2\n%6 = OpTypeStruct %3 %4\n%7 = OpTypeVector %5 4\n%8 = OpTypeStruct %7 %3\n%10 = OpTypeStruct %6\n%11 = OpTypePointer PushConstant %10\n%9 = OpVariable  %11  PushConstant\n%15 = OpTypePointer Input %7\n%14 = OpVariable  %15  Input\n%18 = OpTypePointer Input %3\n%17 = OpVariable  %18  Input\n%21 = OpTypePointer Output %7\n%20 = OpVariable  %21  Output\n%23 = OpTypeFunction %2\n%24 = OpTypePointer PushConstant %6\n%25 = OpConstant  %3  0\n%27 = OpConstant  %5  1\n%28 = OpTypeVector %5 3\n%29 = OpConstantComposite  %28  %27 %27 %27\n%32 = OpTypePointer PushConstant %3\n%35 = OpTypeBool\n%22 = OpFunction  %2  None %23\n%12 = OpLabel\n%16 = OpLoad  %7  %14\n%19 = OpLoad  %3  %17\n%13 = OpCompositeConstruct  %8  %16 %19\n%26 = OpAccessChain  %24  %9 %25\nOpBranch %30\n%30 = OpLabel\n%31 = OpCompositeExtract  %3  %13 1\n%33 = OpAccessChain  %32  %26 %25\n%34 = OpLoad  %3  %33\n%36 = OpIEqual  %35  %31 %34\nOpSelectionMerge %37 None\nOpBranchConditional %36 %38 %39\n%38 = OpLabel\n%40 = OpCompositeExtract  %7  %13 0\nOpStore %20 %40\nOpReturn\n%39 = OpLabel\n%41 = OpCompositeExtract  %7  %13 0\n%42 = OpVectorShuffle  %28  %41 %41 0 1 2\n%43 = OpFSub  %28  %29 %42\n%44 = OpCompositeExtract  %7  %13 0\n%45 = OpCompositeExtract  %5  %44 3\n%46 = OpCompositeConstruct  %7  %43 %45\nOpStore %20 %46\nOpReturn\n%37 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-f16-native.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 318\nOpCapability Shader\nOpCapability Float16\nOpCapability StorageBuffer16BitAccess\nOpCapability UniformAndStorageBuffer16BitAccess\nOpCapability StorageInputOutput16\nOpExtension \"SPV_KHR_16bit_storage\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %54 \"test_direct\" %14 %17 %20 %23 %26 %29 %32 %35 %38 %40 %42 %44 %46 %48 %50 %52\nOpEntryPoint Fragment %136 \"test_struct\" %112 %114 %116 %118 %120 %122 %124 %126 %128 %129 %130 %131 %132 %133 %134 %135\nOpEntryPoint Fragment %199 \"test_copy_input\" %175 %177 %179 %181 %183 %185 %187 %189 %191 %192 %193 %194 %195 %196 %197 %198\nOpEntryPoint Fragment %265 \"test_return_partial\" %248 %250 %252 %254 %256 %258 %260 %262 %264\nOpEntryPoint Fragment %299 \"test_component_access\" %275 %277 %279 %281 %283 %285 %287 %289 %291 %292 %293 %294 %295 %296 %297 %298\nOpExecutionMode %54 OriginUpperLeft\nOpExecutionMode %136 OriginUpperLeft\nOpExecutionMode %199 OriginUpperLeft\nOpExecutionMode %265 OriginUpperLeft\nOpExecutionMode %299 OriginUpperLeft\n%3 = OpString \"f16-native.wgsl\"\nOpSource Unknown 0 %3 \"enable f16;\n\n@fragment\nfn test_direct(\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = scalar_f16 + 1.0h;\n    output.scalar_f32 = scalar_f32 + 1.0;\n    output.vec2_f16 = vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = vec2_f32 + vec2(1.0);\n    output.vec3_f16 = vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = vec3_f32 + vec3(1.0);\n    output.vec4_f16 = vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = vec4_f32 + vec4(1.0);\n    return output;\n}\n\nstruct F16IO {\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n}\n\n@fragment\nfn test_struct(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_copy_input(input_original: F16IO) -> F16IO {\n    var input = input_original;\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_return_partial(input_original: F16IO) -> @location(0) f16 {\n    var input = input_original;\n    input.scalar_f16 = 0.0h;\n    return input.scalar_f16;\n}\n\n@fragment\nfn test_component_access(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.vec2_f16.x = input.vec2_f16.y;\n    output.vec2_f16.y = input.vec2_f16.x;\n    return output;\n}\"\nOpMemberName %12 0 \"scalar_f16\"\nOpMemberName %12 1 \"scalar_f32\"\nOpMemberName %12 2 \"vec2_f16\"\nOpMemberName %12 3 \"vec2_f32\"\nOpMemberName %12 4 \"vec3_f16\"\nOpMemberName %12 5 \"vec3_f32\"\nOpMemberName %12 6 \"vec4_f16\"\nOpMemberName %12 7 \"vec4_f32\"\nOpName %12 \"F16IO\"\nOpName %14 \"scalar_f16\"\nOpName %17 \"scalar_f32\"\nOpName %20 \"vec2_f16\"\nOpName %23 \"vec2_f32\"\nOpName %26 \"vec3_f16\"\nOpName %29 \"vec3_f32\"\nOpName %32 \"vec4_f16\"\nOpName %35 \"vec4_f32\"\nOpName %38 \"scalar_f16\"\nOpName %40 \"scalar_f32\"\nOpName %42 \"vec2_f16\"\nOpName %44 \"vec2_f32\"\nOpName %46 \"vec3_f16\"\nOpName %48 \"vec3_f32\"\nOpName %50 \"vec4_f16\"\nOpName %52 \"vec4_f32\"\nOpName %54 \"test_direct\"\nOpName %64 \"output\"\nOpName %112 \"scalar_f16\"\nOpName %114 \"scalar_f32\"\nOpName %116 \"vec2_f16\"\nOpName %118 \"vec2_f32\"\nOpName %120 \"vec3_f16\"\nOpName %122 \"vec3_f32\"\nOpName %124 \"vec4_f16\"\nOpName %126 \"vec4_f32\"\nOpName %128 \"scalar_f16\"\nOpName %129 \"scalar_f32\"\nOpName %130 \"vec2_f16\"\nOpName %131 \"vec2_f32\"\nOpName %132 \"vec3_f16\"\nOpName %133 \"vec3_f32\"\nOpName %134 \"vec4_f16\"\nOpName %135 \"vec4_f32\"\nOpName %136 \"test_struct\"\nOpName %137 \"output\"\nOpName %175 \"scalar_f16\"\nOpName %177 \"scalar_f32\"\nOpName %179 \"vec2_f16\"\nOpName %181 \"vec2_f32\"\nOpName %183 \"vec3_f16\"\nOpName %185 \"vec3_f32\"\nOpName %187 \"vec4_f16\"\nOpName %189 \"vec4_f32\"\nOpName %191 \"scalar_f16\"\nOpName %192 \"scalar_f32\"\nOpName %193 \"vec2_f16\"\nOpName %194 \"vec2_f32\"\nOpName %195 \"vec3_f16\"\nOpName %196 \"vec3_f32\"\nOpName %197 \"vec4_f16\"\nOpName %198 \"vec4_f32\"\nOpName %199 \"test_copy_input\"\nOpName %200 \"input\"\nOpName %202 \"output\"\nOpName %248 \"scalar_f16\"\nOpName %250 \"scalar_f32\"\nOpName %252 \"vec2_f16\"\nOpName %254 \"vec2_f32\"\nOpName %256 \"vec3_f16\"\nOpName %258 \"vec3_f32\"\nOpName %260 \"vec4_f16\"\nOpName %262 \"vec4_f32\"\nOpName %265 \"test_return_partial\"\nOpName %267 \"input\"\nOpName %275 \"scalar_f16\"\nOpName %277 \"scalar_f32\"\nOpName %279 \"vec2_f16\"\nOpName %281 \"vec2_f32\"\nOpName %283 \"vec3_f16\"\nOpName %285 \"vec3_f32\"\nOpName %287 \"vec4_f16\"\nOpName %289 \"vec4_f32\"\nOpName %291 \"scalar_f16\"\nOpName %292 \"scalar_f32\"\nOpName %293 \"vec2_f16\"\nOpName %294 \"vec2_f32\"\nOpName %295 \"vec3_f16\"\nOpName %296 \"vec3_f32\"\nOpName %297 \"vec4_f16\"\nOpName %298 \"vec4_f32\"\nOpName %299 \"test_component_access\"\nOpName %300 \"output\"\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 4\nOpMemberDecorate %12 2 Offset 8\nOpMemberDecorate %12 3 Offset 16\nOpMemberDecorate %12 4 Offset 24\nOpMemberDecorate %12 5 Offset 32\nOpMemberDecorate %12 6 Offset 48\nOpMemberDecorate %12 7 Offset 64\nOpDecorate %14 Location 0\nOpDecorate %17 Location 1\nOpDecorate %20 Location 2\nOpDecorate %23 Location 3\nOpDecorate %26 Location 4\nOpDecorate %29 Location 5\nOpDecorate %32 Location 6\nOpDecorate %35 Location 7\nOpDecorate %38 Location 0\nOpDecorate %40 Location 1\nOpDecorate %42 Location 2\nOpDecorate %44 Location 3\nOpDecorate %46 Location 4\nOpDecorate %48 Location 5\nOpDecorate %50 Location 6\nOpDecorate %52 Location 7\nOpDecorate %112 Location 0\nOpDecorate %114 Location 1\nOpDecorate %116 Location 2\nOpDecorate %118 Location 3\nOpDecorate %120 Location 4\nOpDecorate %122 Location 5\nOpDecorate %124 Location 6\nOpDecorate %126 Location 7\nOpDecorate %128 Location 0\nOpDecorate %129 Location 1\nOpDecorate %130 Location 2\nOpDecorate %131 Location 3\nOpDecorate %132 Location 4\nOpDecorate %133 Location 5\nOpDecorate %134 Location 6\nOpDecorate %135 Location 7\nOpDecorate %175 Location 0\nOpDecorate %177 Location 1\nOpDecorate %179 Location 2\nOpDecorate %181 Location 3\nOpDecorate %183 Location 4\nOpDecorate %185 Location 5\nOpDecorate %187 Location 6\nOpDecorate %189 Location 7\nOpDecorate %191 Location 0\nOpDecorate %192 Location 1\nOpDecorate %193 Location 2\nOpDecorate %194 Location 3\nOpDecorate %195 Location 4\nOpDecorate %196 Location 5\nOpDecorate %197 Location 6\nOpDecorate %198 Location 7\nOpDecorate %248 Location 0\nOpDecorate %250 Location 1\nOpDecorate %252 Location 2\nOpDecorate %254 Location 3\nOpDecorate %256 Location 4\nOpDecorate %258 Location 5\nOpDecorate %260 Location 6\nOpDecorate %262 Location 7\nOpDecorate %264 Location 0\nOpDecorate %275 Location 0\nOpDecorate %277 Location 1\nOpDecorate %279 Location 2\nOpDecorate %281 Location 3\nOpDecorate %283 Location 4\nOpDecorate %285 Location 5\nOpDecorate %287 Location 6\nOpDecorate %289 Location 7\nOpDecorate %291 Location 0\nOpDecorate %292 Location 1\nOpDecorate %293 Location 2\nOpDecorate %294 Location 3\nOpDecorate %295 Location 4\nOpDecorate %296 Location 5\nOpDecorate %297 Location 6\nOpDecorate %298 Location 7\n%2 = OpTypeVoid\n%4 = OpTypeFloat 16\n%5 = OpTypeFloat 32\n%6 = OpTypeVector %4 2\n%7 = OpTypeVector %5 2\n%8 = OpTypeVector %4 3\n%9 = OpTypeVector %5 3\n%10 = OpTypeVector %4 4\n%11 = OpTypeVector %5 4\n%12 = OpTypeStruct %4 %5 %6 %7 %8 %9 %10 %11\n%15 = OpTypePointer Input %4\n%14 = OpVariable  %15  Input\n%18 = OpTypePointer Input %5\n%17 = OpVariable  %18  Input\n%21 = OpTypePointer Input %6\n%20 = OpVariable  %21  Input\n%24 = OpTypePointer Input %7\n%23 = OpVariable  %24  Input\n%27 = OpTypePointer Input %8\n%26 = OpVariable  %27  Input\n%30 = OpTypePointer Input %9\n%29 = OpVariable  %30  Input\n%33 = OpTypePointer Input %10\n%32 = OpVariable  %33  Input\n%36 = OpTypePointer Input %11\n%35 = OpVariable  %36  Input\n%39 = OpTypePointer Output %4\n%38 = OpVariable  %39  Output\n%41 = OpTypePointer Output %5\n%40 = OpVariable  %41  Output\n%43 = OpTypePointer Output %6\n%42 = OpVariable  %43  Output\n%45 = OpTypePointer Output %7\n%44 = OpVariable  %45  Output\n%47 = OpTypePointer Output %8\n%46 = OpVariable  %47  Output\n%49 = OpTypePointer Output %9\n%48 = OpVariable  %49  Output\n%51 = OpTypePointer Output %10\n%50 = OpVariable  %51  Output\n%53 = OpTypePointer Output %11\n%52 = OpVariable  %53  Output\n%55 = OpTypeFunction %2\n%56 = OpConstant  %4  0.000000000000000000000000000000000000000021524\n%57 = OpConstant  %5  1\n%58 = OpConstantComposite  %6  %56 %56\n%59 = OpConstantComposite  %7  %57 %57\n%60 = OpConstantComposite  %8  %56 %56 %56\n%61 = OpConstantComposite  %9  %57 %57 %57\n%62 = OpConstantComposite  %10  %56 %56 %56 %56\n%63 = OpConstantComposite  %11  %57 %57 %57 %57\n%65 = OpTypePointer Function %12\n%66 = OpConstantNull  %12\n%68 = OpTypePointer Function %4\n%71 = OpTypeInt 32 0\n%70 = OpConstant  %71  0\n%73 = OpTypePointer Function %5\n%75 = OpConstant  %71  1\n%77 = OpTypePointer Function %6\n%79 = OpConstant  %71  2\n%81 = OpTypePointer Function %7\n%83 = OpConstant  %71  3\n%85 = OpTypePointer Function %8\n%87 = OpConstant  %71  4\n%89 = OpTypePointer Function %9\n%91 = OpConstant  %71  5\n%93 = OpTypePointer Function %10\n%95 = OpConstant  %71  6\n%97 = OpTypePointer Function %11\n%99 = OpConstant  %71  7\n%112 = OpVariable  %15  Input\n%114 = OpVariable  %18  Input\n%116 = OpVariable  %21  Input\n%118 = OpVariable  %24  Input\n%120 = OpVariable  %27  Input\n%122 = OpVariable  %30  Input\n%124 = OpVariable  %33  Input\n%126 = OpVariable  %36  Input\n%128 = OpVariable  %39  Output\n%129 = OpVariable  %41  Output\n%130 = OpVariable  %43  Output\n%131 = OpVariable  %45  Output\n%132 = OpVariable  %47  Output\n%133 = OpVariable  %49  Output\n%134 = OpVariable  %51  Output\n%135 = OpVariable  %53  Output\n%138 = OpConstantNull  %12\n%175 = OpVariable  %15  Input\n%177 = OpVariable  %18  Input\n%179 = OpVariable  %21  Input\n%181 = OpVariable  %24  Input\n%183 = OpVariable  %27  Input\n%185 = OpVariable  %30  Input\n%187 = OpVariable  %33  Input\n%189 = OpVariable  %36  Input\n%191 = OpVariable  %39  Output\n%192 = OpVariable  %41  Output\n%193 = OpVariable  %43  Output\n%194 = OpVariable  %45  Output\n%195 = OpVariable  %47  Output\n%196 = OpVariable  %49  Output\n%197 = OpVariable  %51  Output\n%198 = OpVariable  %53  Output\n%201 = OpConstantNull  %12\n%203 = OpConstantNull  %12\n%248 = OpVariable  %15  Input\n%250 = OpVariable  %18  Input\n%252 = OpVariable  %21  Input\n%254 = OpVariable  %24  Input\n%256 = OpVariable  %27  Input\n%258 = OpVariable  %30  Input\n%260 = OpVariable  %33  Input\n%262 = OpVariable  %36  Input\n%264 = OpVariable  %39  Output\n%266 = OpConstant  %4  0\n%268 = OpConstantNull  %12\n%275 = OpVariable  %15  Input\n%277 = OpVariable  %18  Input\n%279 = OpVariable  %21  Input\n%281 = OpVariable  %24  Input\n%283 = OpVariable  %27  Input\n%285 = OpVariable  %30  Input\n%287 = OpVariable  %33  Input\n%289 = OpVariable  %36  Input\n%291 = OpVariable  %39  Output\n%292 = OpVariable  %41  Output\n%293 = OpVariable  %43  Output\n%294 = OpVariable  %45  Output\n%295 = OpVariable  %47  Output\n%296 = OpVariable  %49  Output\n%297 = OpVariable  %51  Output\n%298 = OpVariable  %53  Output\n%301 = OpConstantNull  %12\n%54 = OpFunction  %2  None %55\n%13 = OpLabel\n%64 = OpVariable  %65  Function %66\n%16 = OpLoad  %4  %14\n%19 = OpLoad  %5  %17\n%22 = OpLoad  %6  %20\n%25 = OpLoad  %7  %23\n%28 = OpLoad  %8  %26\n%31 = OpLoad  %9  %29\n%34 = OpLoad  %10  %32\n%37 = OpLoad  %11  %35\nOpBranch %67\n%67 = OpLabel\nOpLine %3 15 5\nOpLine %3 15 25\n%69 = OpFAdd  %4  %16 %56\nOpLine %3 15 5\n%72 = OpAccessChain  %68  %64 %70\nOpStore %72 %69\nOpLine %3 16 5\nOpLine %3 16 25\n%74 = OpFAdd  %5  %19 %57\nOpLine %3 16 5\n%76 = OpAccessChain  %73  %64 %75\nOpStore %76 %74\nOpLine %3 17 5\nOpLine %3 17 23\n%78 = OpFAdd  %6  %22 %58\nOpLine %3 17 5\n%80 = OpAccessChain  %77  %64 %79\nOpStore %80 %78\nOpLine %3 18 5\nOpLine %3 18 34\nOpLine %3 18 23\n%82 = OpFAdd  %7  %25 %59\nOpLine %3 18 5\n%84 = OpAccessChain  %81  %64 %83\nOpStore %84 %82\nOpLine %3 19 5\nOpLine %3 19 23\n%86 = OpFAdd  %8  %28 %60\nOpLine %3 19 5\n%88 = OpAccessChain  %85  %64 %87\nOpStore %88 %86\nOpLine %3 20 5\nOpLine %3 20 34\nOpLine %3 20 23\n%90 = OpFAdd  %9  %31 %61\nOpLine %3 20 5\n%92 = OpAccessChain  %89  %64 %91\nOpStore %92 %90\nOpLine %3 21 5\nOpLine %3 21 23\n%94 = OpFAdd  %10  %34 %62\nOpLine %3 21 5\n%96 = OpAccessChain  %93  %64 %95\nOpStore %96 %94\nOpLine %3 22 5\nOpLine %3 22 34\nOpLine %3 22 23\n%98 = OpFAdd  %11  %37 %63\nOpLine %3 22 5\n%100 = OpAccessChain  %97  %64 %99\nOpStore %100 %98\nOpLine %3 1 1\n%101 = OpLoad  %12  %64\n%102 = OpCompositeExtract  %4  %101 0\nOpStore %38 %102\n%103 = OpCompositeExtract  %5  %101 1\nOpStore %40 %103\n%104 = OpCompositeExtract  %6  %101 2\nOpStore %42 %104\n%105 = OpCompositeExtract  %7  %101 3\nOpStore %44 %105\n%106 = OpCompositeExtract  %8  %101 4\nOpStore %46 %106\n%107 = OpCompositeExtract  %9  %101 5\nOpStore %48 %107\n%108 = OpCompositeExtract  %10  %101 6\nOpStore %50 %108\n%109 = OpCompositeExtract  %11  %101 7\nOpStore %52 %109\nOpReturn\nOpFunctionEnd\n%136 = OpFunction  %2  None %55\n%110 = OpLabel\n%137 = OpVariable  %65  Function %138\n%113 = OpLoad  %4  %112\n%115 = OpLoad  %5  %114\n%117 = OpLoad  %6  %116\n%119 = OpLoad  %7  %118\n%121 = OpLoad  %8  %120\n%123 = OpLoad  %9  %122\n%125 = OpLoad  %10  %124\n%127 = OpLoad  %11  %126\n%111 = OpCompositeConstruct  %12  %113 %115 %117 %119 %121 %123 %125 %127\nOpBranch %139\n%139 = OpLabel\nOpLine %3 40 5\n%140 = OpCompositeExtract  %4  %111 0\nOpLine %3 40 25\n%141 = OpFAdd  %4  %140 %56\nOpLine %3 40 5\n%142 = OpAccessChain  %68  %137 %70\nOpStore %142 %141\nOpLine %3 41 5\n%143 = OpCompositeExtract  %5  %111 1\nOpLine %3 41 25\n%144 = OpFAdd  %5  %143 %57\nOpLine %3 41 5\n%145 = OpAccessChain  %73  %137 %75\nOpStore %145 %144\nOpLine %3 42 5\n%146 = OpCompositeExtract  %6  %111 2\nOpLine %3 42 23\n%147 = OpFAdd  %6  %146 %58\nOpLine %3 42 5\n%148 = OpAccessChain  %77  %137 %79\nOpStore %148 %147\nOpLine %3 43 5\n%149 = OpCompositeExtract  %7  %111 3\nOpLine %3 43 40\nOpLine %3 43 23\n%150 = OpFAdd  %7  %149 %59\nOpLine %3 43 5\n%151 = OpAccessChain  %81  %137 %83\nOpStore %151 %150\nOpLine %3 44 5\n%152 = OpCompositeExtract  %8  %111 4\nOpLine %3 44 23\n%153 = OpFAdd  %8  %152 %60\nOpLine %3 44 5\n%154 = OpAccessChain  %85  %137 %87\nOpStore %154 %153\nOpLine %3 45 5\n%155 = OpCompositeExtract  %9  %111 5\nOpLine %3 45 40\nOpLine %3 45 23\n%156 = OpFAdd  %9  %155 %61\nOpLine %3 45 5\n%157 = OpAccessChain  %89  %137 %91\nOpStore %157 %156\nOpLine %3 46 5\n%158 = OpCompositeExtract  %10  %111 6\nOpLine %3 46 23\n%159 = OpFAdd  %10  %158 %62\nOpLine %3 46 5\n%160 = OpAccessChain  %93  %137 %95\nOpStore %160 %159\nOpLine %3 47 5\n%161 = OpCompositeExtract  %11  %111 7\nOpLine %3 47 40\nOpLine %3 47 23\n%162 = OpFAdd  %11  %161 %63\nOpLine %3 47 5\n%163 = OpAccessChain  %97  %137 %99\nOpStore %163 %162\nOpLine %3 1 1\n%164 = OpLoad  %12  %137\n%165 = OpCompositeExtract  %4  %164 0\nOpStore %128 %165\n%166 = OpCompositeExtract  %5  %164 1\nOpStore %129 %166\n%167 = OpCompositeExtract  %6  %164 2\nOpStore %130 %167\n%168 = OpCompositeExtract  %7  %164 3\nOpStore %131 %168\n%169 = OpCompositeExtract  %8  %164 4\nOpStore %132 %169\n%170 = OpCompositeExtract  %9  %164 5\nOpStore %133 %170\n%171 = OpCompositeExtract  %10  %164 6\nOpStore %134 %171\n%172 = OpCompositeExtract  %11  %164 7\nOpStore %135 %172\nOpReturn\nOpFunctionEnd\n%199 = OpFunction  %2  None %55\n%173 = OpLabel\n%200 = OpVariable  %65  Function %201\n%202 = OpVariable  %65  Function %203\n%176 = OpLoad  %4  %175\n%178 = OpLoad  %5  %177\n%180 = OpLoad  %6  %179\n%182 = OpLoad  %7  %181\n%184 = OpLoad  %8  %183\n%186 = OpLoad  %9  %185\n%188 = OpLoad  %10  %187\n%190 = OpLoad  %11  %189\n%174 = OpCompositeConstruct  %12  %176 %178 %180 %182 %184 %186 %188 %190\nOpBranch %204\n%204 = OpLabel\nOpLine %3 53 5\nOpStore %200 %174\nOpLine %3 55 5\n%205 = OpAccessChain  %68  %200 %70\n%206 = OpLoad  %4  %205\nOpLine %3 55 25\n%207 = OpFAdd  %4  %206 %56\nOpLine %3 55 5\n%208 = OpAccessChain  %68  %202 %70\nOpStore %208 %207\nOpLine %3 56 5\n%209 = OpAccessChain  %73  %200 %75\n%210 = OpLoad  %5  %209\nOpLine %3 56 25\n%211 = OpFAdd  %5  %210 %57\nOpLine %3 56 5\n%212 = OpAccessChain  %73  %202 %75\nOpStore %212 %211\nOpLine %3 57 5\n%213 = OpAccessChain  %77  %200 %79\n%214 = OpLoad  %6  %213\nOpLine %3 57 23\n%215 = OpFAdd  %6  %214 %58\nOpLine %3 57 5\n%216 = OpAccessChain  %77  %202 %79\nOpStore %216 %215\nOpLine %3 58 5\n%217 = OpAccessChain  %81  %200 %83\n%218 = OpLoad  %7  %217\nOpLine %3 58 40\nOpLine %3 58 23\n%219 = OpFAdd  %7  %218 %59\nOpLine %3 58 5\n%220 = OpAccessChain  %81  %202 %83\nOpStore %220 %219\nOpLine %3 59 5\n%221 = OpAccessChain  %85  %200 %87\n%222 = OpLoad  %8  %221\nOpLine %3 59 23\n%223 = OpFAdd  %8  %222 %60\nOpLine %3 59 5\n%224 = OpAccessChain  %85  %202 %87\nOpStore %224 %223\nOpLine %3 60 5\n%225 = OpAccessChain  %89  %200 %91\n%226 = OpLoad  %9  %225\nOpLine %3 60 40\nOpLine %3 60 23\n%227 = OpFAdd  %9  %226 %61\nOpLine %3 60 5\n%228 = OpAccessChain  %89  %202 %91\nOpStore %228 %227\nOpLine %3 61 5\n%229 = OpAccessChain  %93  %200 %95\n%230 = OpLoad  %10  %229\nOpLine %3 61 23\n%231 = OpFAdd  %10  %230 %62\nOpLine %3 61 5\n%232 = OpAccessChain  %93  %202 %95\nOpStore %232 %231\nOpLine %3 62 5\n%233 = OpAccessChain  %97  %200 %99\n%234 = OpLoad  %11  %233\nOpLine %3 62 40\nOpLine %3 62 23\n%235 = OpFAdd  %11  %234 %63\nOpLine %3 62 5\n%236 = OpAccessChain  %97  %202 %99\nOpStore %236 %235\nOpLine %3 1 1\n%237 = OpLoad  %12  %202\n%238 = OpCompositeExtract  %4  %237 0\nOpStore %191 %238\n%239 = OpCompositeExtract  %5  %237 1\nOpStore %192 %239\n%240 = OpCompositeExtract  %6  %237 2\nOpStore %193 %240\n%241 = OpCompositeExtract  %7  %237 3\nOpStore %194 %241\n%242 = OpCompositeExtract  %8  %237 4\nOpStore %195 %242\n%243 = OpCompositeExtract  %9  %237 5\nOpStore %196 %243\n%244 = OpCompositeExtract  %10  %237 6\nOpStore %197 %244\n%245 = OpCompositeExtract  %11  %237 7\nOpStore %198 %245\nOpReturn\nOpFunctionEnd\n%265 = OpFunction  %2  None %55\n%246 = OpLabel\n%267 = OpVariable  %65  Function %268\n%249 = OpLoad  %4  %248\n%251 = OpLoad  %5  %250\n%253 = OpLoad  %6  %252\n%255 = OpLoad  %7  %254\n%257 = OpLoad  %8  %256\n%259 = OpLoad  %9  %258\n%261 = OpLoad  %10  %260\n%263 = OpLoad  %11  %262\n%247 = OpCompositeConstruct  %12  %249 %251 %253 %255 %257 %259 %261 %263\nOpBranch %269\n%269 = OpLabel\nOpLine %3 68 5\nOpStore %267 %247\nOpLine %3 69 5\nOpLine %3 69 5\n%270 = OpAccessChain  %68  %267 %70\nOpStore %270 %266\nOpLine %3 70 12\n%271 = OpAccessChain  %68  %267 %70\n%272 = OpLoad  %4  %271\nOpStore %264 %272\nOpReturn\nOpFunctionEnd\n%299 = OpFunction  %2  None %55\n%273 = OpLabel\n%300 = OpVariable  %65  Function %301\n%276 = OpLoad  %4  %275\n%278 = OpLoad  %5  %277\n%280 = OpLoad  %6  %279\n%282 = OpLoad  %7  %281\n%284 = OpLoad  %8  %283\n%286 = OpLoad  %9  %285\n%288 = OpLoad  %10  %287\n%290 = OpLoad  %11  %289\n%274 = OpCompositeConstruct  %12  %276 %278 %280 %282 %284 %286 %288 %290\nOpBranch %302\n%302 = OpLabel\nOpLine %3 76 5\n%303 = OpCompositeExtract  %6  %274 2\n%304 = OpCompositeExtract  %4  %303 1\nOpLine %3 76 5\n%305 = OpAccessChain  %68  %300 %79 %70\nOpStore %305 %304\nOpLine %3 77 5\n%306 = OpCompositeExtract  %6  %274 2\n%307 = OpCompositeExtract  %4  %306 0\nOpLine %3 77 5\n%308 = OpAccessChain  %68  %300 %79 %75\nOpStore %308 %307\nOpLine %3 1 1\n%309 = OpLoad  %12  %300\n%310 = OpCompositeExtract  %4  %309 0\nOpStore %291 %310\n%311 = OpCompositeExtract  %5  %309 1\nOpStore %292 %311\n%312 = OpCompositeExtract  %6  %309 2\nOpStore %293 %312\n%313 = OpCompositeExtract  %7  %309 3\nOpStore %294 %313\n%314 = OpCompositeExtract  %8  %309 4\nOpStore %295 %314\n%315 = OpCompositeExtract  %9  %309 5\nOpStore %296 %315\n%316 = OpCompositeExtract  %10  %309 6\nOpStore %297 %316\n%317 = OpCompositeExtract  %11  %309 7\nOpStore %298 %317\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-f16-polyfill.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 347\nOpCapability Shader\nOpCapability Float16\nOpCapability StorageBuffer16BitAccess\nOpCapability UniformAndStorageBuffer16BitAccess\nOpExtension \"SPV_KHR_16bit_storage\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %50 \"test_direct\" %14 %18 %20 %24 %26 %30 %32 %36 %38 %40 %41 %43 %44 %46 %47 %49\nOpEntryPoint Fragment %140 \"test_struct\" %112 %115 %117 %120 %122 %125 %127 %130 %132 %133 %134 %135 %136 %137 %138 %139\nOpEntryPoint Fragment %211 \"test_copy_input\" %183 %186 %188 %191 %193 %196 %198 %201 %203 %204 %205 %206 %207 %208 %209 %210\nOpEntryPoint Fragment %285 \"test_return_partial\" %264 %267 %269 %272 %274 %277 %279 %282 %284\nOpEntryPoint Fragment %324 \"test_component_access\" %296 %299 %301 %304 %306 %309 %311 %314 %316 %317 %318 %319 %320 %321 %322 %323\nOpExecutionMode %50 OriginUpperLeft\nOpExecutionMode %140 OriginUpperLeft\nOpExecutionMode %211 OriginUpperLeft\nOpExecutionMode %285 OriginUpperLeft\nOpExecutionMode %324 OriginUpperLeft\n%3 = OpString \"f16-polyfill.wgsl\"\nOpSource Unknown 0 %3 \"enable f16;\n\n@fragment\nfn test_direct(\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = scalar_f16 + 1.0h;\n    output.scalar_f32 = scalar_f32 + 1.0;\n    output.vec2_f16 = vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = vec2_f32 + vec2(1.0);\n    output.vec3_f16 = vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = vec3_f32 + vec3(1.0);\n    output.vec4_f16 = vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = vec4_f32 + vec4(1.0);\n    return output;\n}\n\nstruct F16IO {\n    @location(0) scalar_f16: f16,\n    @location(1) scalar_f32: f32,\n    @location(2) vec2_f16: vec2<f16>,\n    @location(3) vec2_f32: vec2<f32>,\n    @location(4) vec3_f16: vec3<f16>,\n    @location(5) vec3_f32: vec3<f32>,\n    @location(6) vec4_f16: vec4<f16>,\n    @location(7) vec4_f32: vec4<f32>,\n}\n\n@fragment\nfn test_struct(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_copy_input(input_original: F16IO) -> F16IO {\n    var input = input_original;\n    var output: F16IO;\n    output.scalar_f16 = input.scalar_f16 + 1.0h;\n    output.scalar_f32 = input.scalar_f32 + 1.0;\n    output.vec2_f16 = input.vec2_f16 + vec2(1.0h);\n    output.vec2_f32 = input.vec2_f32 + vec2(1.0);\n    output.vec3_f16 = input.vec3_f16 + vec3(1.0h);\n    output.vec3_f32 = input.vec3_f32 + vec3(1.0);\n    output.vec4_f16 = input.vec4_f16 + vec4(1.0h);\n    output.vec4_f32 = input.vec4_f32 + vec4(1.0);\n    return output;\n}\n\n@fragment\nfn test_return_partial(input_original: F16IO) -> @location(0) f16 {\n    var input = input_original;\n    input.scalar_f16 = 0.0h;\n    return input.scalar_f16;\n}\n\n@fragment\nfn test_component_access(input: F16IO) -> F16IO {\n    var output: F16IO;\n    output.vec2_f16.x = input.vec2_f16.y;\n    output.vec2_f16.y = input.vec2_f16.x;\n    return output;\n}\"\nOpMemberName %12 0 \"scalar_f16\"\nOpMemberName %12 1 \"scalar_f32\"\nOpMemberName %12 2 \"vec2_f16\"\nOpMemberName %12 3 \"vec2_f32\"\nOpMemberName %12 4 \"vec3_f16\"\nOpMemberName %12 5 \"vec3_f32\"\nOpMemberName %12 6 \"vec4_f16\"\nOpMemberName %12 7 \"vec4_f32\"\nOpName %12 \"F16IO\"\nOpName %14 \"scalar_f16\"\nOpName %18 \"scalar_f32\"\nOpName %20 \"vec2_f16\"\nOpName %24 \"vec2_f32\"\nOpName %26 \"vec3_f16\"\nOpName %30 \"vec3_f32\"\nOpName %32 \"vec4_f16\"\nOpName %36 \"vec4_f32\"\nOpName %38 \"scalar_f16\"\nOpName %40 \"scalar_f32\"\nOpName %41 \"vec2_f16\"\nOpName %43 \"vec2_f32\"\nOpName %44 \"vec3_f16\"\nOpName %46 \"vec3_f32\"\nOpName %47 \"vec4_f16\"\nOpName %49 \"vec4_f32\"\nOpName %50 \"test_direct\"\nOpName %60 \"output\"\nOpName %112 \"scalar_f16\"\nOpName %115 \"scalar_f32\"\nOpName %117 \"vec2_f16\"\nOpName %120 \"vec2_f32\"\nOpName %122 \"vec3_f16\"\nOpName %125 \"vec3_f32\"\nOpName %127 \"vec4_f16\"\nOpName %130 \"vec4_f32\"\nOpName %132 \"scalar_f16\"\nOpName %133 \"scalar_f32\"\nOpName %134 \"vec2_f16\"\nOpName %135 \"vec2_f32\"\nOpName %136 \"vec3_f16\"\nOpName %137 \"vec3_f32\"\nOpName %138 \"vec4_f16\"\nOpName %139 \"vec4_f32\"\nOpName %140 \"test_struct\"\nOpName %141 \"output\"\nOpName %183 \"scalar_f16\"\nOpName %186 \"scalar_f32\"\nOpName %188 \"vec2_f16\"\nOpName %191 \"vec2_f32\"\nOpName %193 \"vec3_f16\"\nOpName %196 \"vec3_f32\"\nOpName %198 \"vec4_f16\"\nOpName %201 \"vec4_f32\"\nOpName %203 \"scalar_f16\"\nOpName %204 \"scalar_f32\"\nOpName %205 \"vec2_f16\"\nOpName %206 \"vec2_f32\"\nOpName %207 \"vec3_f16\"\nOpName %208 \"vec3_f32\"\nOpName %209 \"vec4_f16\"\nOpName %210 \"vec4_f32\"\nOpName %211 \"test_copy_input\"\nOpName %212 \"input\"\nOpName %214 \"output\"\nOpName %264 \"scalar_f16\"\nOpName %267 \"scalar_f32\"\nOpName %269 \"vec2_f16\"\nOpName %272 \"vec2_f32\"\nOpName %274 \"vec3_f16\"\nOpName %277 \"vec3_f32\"\nOpName %279 \"vec4_f16\"\nOpName %282 \"vec4_f32\"\nOpName %285 \"test_return_partial\"\nOpName %287 \"input\"\nOpName %296 \"scalar_f16\"\nOpName %299 \"scalar_f32\"\nOpName %301 \"vec2_f16\"\nOpName %304 \"vec2_f32\"\nOpName %306 \"vec3_f16\"\nOpName %309 \"vec3_f32\"\nOpName %311 \"vec4_f16\"\nOpName %314 \"vec4_f32\"\nOpName %316 \"scalar_f16\"\nOpName %317 \"scalar_f32\"\nOpName %318 \"vec2_f16\"\nOpName %319 \"vec2_f32\"\nOpName %320 \"vec3_f16\"\nOpName %321 \"vec3_f32\"\nOpName %322 \"vec4_f16\"\nOpName %323 \"vec4_f32\"\nOpName %324 \"test_component_access\"\nOpName %325 \"output\"\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 4\nOpMemberDecorate %12 2 Offset 8\nOpMemberDecorate %12 3 Offset 16\nOpMemberDecorate %12 4 Offset 24\nOpMemberDecorate %12 5 Offset 32\nOpMemberDecorate %12 6 Offset 48\nOpMemberDecorate %12 7 Offset 64\nOpDecorate %14 Location 0\nOpDecorate %18 Location 1\nOpDecorate %20 Location 2\nOpDecorate %24 Location 3\nOpDecorate %26 Location 4\nOpDecorate %30 Location 5\nOpDecorate %32 Location 6\nOpDecorate %36 Location 7\nOpDecorate %38 Location 0\nOpDecorate %40 Location 1\nOpDecorate %41 Location 2\nOpDecorate %43 Location 3\nOpDecorate %44 Location 4\nOpDecorate %46 Location 5\nOpDecorate %47 Location 6\nOpDecorate %49 Location 7\nOpDecorate %112 Location 0\nOpDecorate %115 Location 1\nOpDecorate %117 Location 2\nOpDecorate %120 Location 3\nOpDecorate %122 Location 4\nOpDecorate %125 Location 5\nOpDecorate %127 Location 6\nOpDecorate %130 Location 7\nOpDecorate %132 Location 0\nOpDecorate %133 Location 1\nOpDecorate %134 Location 2\nOpDecorate %135 Location 3\nOpDecorate %136 Location 4\nOpDecorate %137 Location 5\nOpDecorate %138 Location 6\nOpDecorate %139 Location 7\nOpDecorate %183 Location 0\nOpDecorate %186 Location 1\nOpDecorate %188 Location 2\nOpDecorate %191 Location 3\nOpDecorate %193 Location 4\nOpDecorate %196 Location 5\nOpDecorate %198 Location 6\nOpDecorate %201 Location 7\nOpDecorate %203 Location 0\nOpDecorate %204 Location 1\nOpDecorate %205 Location 2\nOpDecorate %206 Location 3\nOpDecorate %207 Location 4\nOpDecorate %208 Location 5\nOpDecorate %209 Location 6\nOpDecorate %210 Location 7\nOpDecorate %264 Location 0\nOpDecorate %267 Location 1\nOpDecorate %269 Location 2\nOpDecorate %272 Location 3\nOpDecorate %274 Location 4\nOpDecorate %277 Location 5\nOpDecorate %279 Location 6\nOpDecorate %282 Location 7\nOpDecorate %284 Location 0\nOpDecorate %296 Location 0\nOpDecorate %299 Location 1\nOpDecorate %301 Location 2\nOpDecorate %304 Location 3\nOpDecorate %306 Location 4\nOpDecorate %309 Location 5\nOpDecorate %311 Location 6\nOpDecorate %314 Location 7\nOpDecorate %316 Location 0\nOpDecorate %317 Location 1\nOpDecorate %318 Location 2\nOpDecorate %319 Location 3\nOpDecorate %320 Location 4\nOpDecorate %321 Location 5\nOpDecorate %322 Location 6\nOpDecorate %323 Location 7\n%2 = OpTypeVoid\n%4 = OpTypeFloat 16\n%5 = OpTypeFloat 32\n%6 = OpTypeVector %4 2\n%7 = OpTypeVector %5 2\n%8 = OpTypeVector %4 3\n%9 = OpTypeVector %5 3\n%10 = OpTypeVector %4 4\n%11 = OpTypeVector %5 4\n%12 = OpTypeStruct %4 %5 %6 %7 %8 %9 %10 %11\n%15 = OpTypePointer Input %5\n%14 = OpVariable  %15  Input\n%18 = OpVariable  %15  Input\n%21 = OpTypePointer Input %7\n%20 = OpVariable  %21  Input\n%24 = OpVariable  %21  Input\n%27 = OpTypePointer Input %9\n%26 = OpVariable  %27  Input\n%30 = OpVariable  %27  Input\n%33 = OpTypePointer Input %11\n%32 = OpVariable  %33  Input\n%36 = OpVariable  %33  Input\n%39 = OpTypePointer Output %5\n%38 = OpVariable  %39  Output\n%40 = OpVariable  %39  Output\n%42 = OpTypePointer Output %7\n%41 = OpVariable  %42  Output\n%43 = OpVariable  %42  Output\n%45 = OpTypePointer Output %9\n%44 = OpVariable  %45  Output\n%46 = OpVariable  %45  Output\n%48 = OpTypePointer Output %11\n%47 = OpVariable  %48  Output\n%49 = OpVariable  %48  Output\n%51 = OpTypeFunction %2\n%52 = OpConstant  %4  0.000000000000000000000000000000000000000021524\n%53 = OpConstant  %5  1\n%54 = OpConstantComposite  %6  %52 %52\n%55 = OpConstantComposite  %7  %53 %53\n%56 = OpConstantComposite  %8  %52 %52 %52\n%57 = OpConstantComposite  %9  %53 %53 %53\n%58 = OpConstantComposite  %10  %52 %52 %52 %52\n%59 = OpConstantComposite  %11  %53 %53 %53 %53\n%61 = OpTypePointer Function %12\n%62 = OpConstantNull  %12\n%64 = OpTypePointer Function %4\n%67 = OpTypeInt 32 0\n%66 = OpConstant  %67  0\n%69 = OpTypePointer Function %5\n%71 = OpConstant  %67  1\n%73 = OpTypePointer Function %6\n%75 = OpConstant  %67  2\n%77 = OpTypePointer Function %7\n%79 = OpConstant  %67  3\n%81 = OpTypePointer Function %8\n%83 = OpConstant  %67  4\n%85 = OpTypePointer Function %9\n%87 = OpConstant  %67  5\n%89 = OpTypePointer Function %10\n%91 = OpConstant  %67  6\n%93 = OpTypePointer Function %11\n%95 = OpConstant  %67  7\n%112 = OpVariable  %15  Input\n%115 = OpVariable  %15  Input\n%117 = OpVariable  %21  Input\n%120 = OpVariable  %21  Input\n%122 = OpVariable  %27  Input\n%125 = OpVariable  %27  Input\n%127 = OpVariable  %33  Input\n%130 = OpVariable  %33  Input\n%132 = OpVariable  %39  Output\n%133 = OpVariable  %39  Output\n%134 = OpVariable  %42  Output\n%135 = OpVariable  %42  Output\n%136 = OpVariable  %45  Output\n%137 = OpVariable  %45  Output\n%138 = OpVariable  %48  Output\n%139 = OpVariable  %48  Output\n%142 = OpConstantNull  %12\n%183 = OpVariable  %15  Input\n%186 = OpVariable  %15  Input\n%188 = OpVariable  %21  Input\n%191 = OpVariable  %21  Input\n%193 = OpVariable  %27  Input\n%196 = OpVariable  %27  Input\n%198 = OpVariable  %33  Input\n%201 = OpVariable  %33  Input\n%203 = OpVariable  %39  Output\n%204 = OpVariable  %39  Output\n%205 = OpVariable  %42  Output\n%206 = OpVariable  %42  Output\n%207 = OpVariable  %45  Output\n%208 = OpVariable  %45  Output\n%209 = OpVariable  %48  Output\n%210 = OpVariable  %48  Output\n%213 = OpConstantNull  %12\n%215 = OpConstantNull  %12\n%264 = OpVariable  %15  Input\n%267 = OpVariable  %15  Input\n%269 = OpVariable  %21  Input\n%272 = OpVariable  %21  Input\n%274 = OpVariable  %27  Input\n%277 = OpVariable  %27  Input\n%279 = OpVariable  %33  Input\n%282 = OpVariable  %33  Input\n%284 = OpVariable  %39  Output\n%286 = OpConstant  %4  0\n%288 = OpConstantNull  %12\n%296 = OpVariable  %15  Input\n%299 = OpVariable  %15  Input\n%301 = OpVariable  %21  Input\n%304 = OpVariable  %21  Input\n%306 = OpVariable  %27  Input\n%309 = OpVariable  %27  Input\n%311 = OpVariable  %33  Input\n%314 = OpVariable  %33  Input\n%316 = OpVariable  %39  Output\n%317 = OpVariable  %39  Output\n%318 = OpVariable  %42  Output\n%319 = OpVariable  %42  Output\n%320 = OpVariable  %45  Output\n%321 = OpVariable  %45  Output\n%322 = OpVariable  %48  Output\n%323 = OpVariable  %48  Output\n%326 = OpConstantNull  %12\n%50 = OpFunction  %2  None %51\n%13 = OpLabel\n%60 = OpVariable  %61  Function %62\n%16 = OpLoad  %5  %14\n%17 = OpFConvert  %4  %16\n%19 = OpLoad  %5  %18\n%22 = OpLoad  %7  %20\n%23 = OpFConvert  %6  %22\n%25 = OpLoad  %7  %24\n%28 = OpLoad  %9  %26\n%29 = OpFConvert  %8  %28\n%31 = OpLoad  %9  %30\n%34 = OpLoad  %11  %32\n%35 = OpFConvert  %10  %34\n%37 = OpLoad  %11  %36\nOpBranch %63\n%63 = OpLabel\nOpLine %3 15 5\nOpLine %3 15 25\n%65 = OpFAdd  %4  %17 %52\nOpLine %3 15 5\n%68 = OpAccessChain  %64  %60 %66\nOpStore %68 %65\nOpLine %3 16 5\nOpLine %3 16 25\n%70 = OpFAdd  %5  %19 %53\nOpLine %3 16 5\n%72 = OpAccessChain  %69  %60 %71\nOpStore %72 %70\nOpLine %3 17 5\nOpLine %3 17 23\n%74 = OpFAdd  %6  %23 %54\nOpLine %3 17 5\n%76 = OpAccessChain  %73  %60 %75\nOpStore %76 %74\nOpLine %3 18 5\nOpLine %3 18 34\nOpLine %3 18 23\n%78 = OpFAdd  %7  %25 %55\nOpLine %3 18 5\n%80 = OpAccessChain  %77  %60 %79\nOpStore %80 %78\nOpLine %3 19 5\nOpLine %3 19 23\n%82 = OpFAdd  %8  %29 %56\nOpLine %3 19 5\n%84 = OpAccessChain  %81  %60 %83\nOpStore %84 %82\nOpLine %3 20 5\nOpLine %3 20 34\nOpLine %3 20 23\n%86 = OpFAdd  %9  %31 %57\nOpLine %3 20 5\n%88 = OpAccessChain  %85  %60 %87\nOpStore %88 %86\nOpLine %3 21 5\nOpLine %3 21 23\n%90 = OpFAdd  %10  %35 %58\nOpLine %3 21 5\n%92 = OpAccessChain  %89  %60 %91\nOpStore %92 %90\nOpLine %3 22 5\nOpLine %3 22 34\nOpLine %3 22 23\n%94 = OpFAdd  %11  %37 %59\nOpLine %3 22 5\n%96 = OpAccessChain  %93  %60 %95\nOpStore %96 %94\nOpLine %3 1 1\n%97 = OpLoad  %12  %60\n%98 = OpCompositeExtract  %4  %97 0\n%99 = OpFConvert  %5  %98\nOpStore %38 %99\n%100 = OpCompositeExtract  %5  %97 1\nOpStore %40 %100\n%101 = OpCompositeExtract  %6  %97 2\n%102 = OpFConvert  %7  %101\nOpStore %41 %102\n%103 = OpCompositeExtract  %7  %97 3\nOpStore %43 %103\n%104 = OpCompositeExtract  %8  %97 4\n%105 = OpFConvert  %9  %104\nOpStore %44 %105\n%106 = OpCompositeExtract  %9  %97 5\nOpStore %46 %106\n%107 = OpCompositeExtract  %10  %97 6\n%108 = OpFConvert  %11  %107\nOpStore %47 %108\n%109 = OpCompositeExtract  %11  %97 7\nOpStore %49 %109\nOpReturn\nOpFunctionEnd\n%140 = OpFunction  %2  None %51\n%110 = OpLabel\n%141 = OpVariable  %61  Function %142\n%113 = OpLoad  %5  %112\n%114 = OpFConvert  %4  %113\n%116 = OpLoad  %5  %115\n%118 = OpLoad  %7  %117\n%119 = OpFConvert  %6  %118\n%121 = OpLoad  %7  %120\n%123 = OpLoad  %9  %122\n%124 = OpFConvert  %8  %123\n%126 = OpLoad  %9  %125\n%128 = OpLoad  %11  %127\n%129 = OpFConvert  %10  %128\n%131 = OpLoad  %11  %130\n%111 = OpCompositeConstruct  %12  %114 %116 %119 %121 %124 %126 %129 %131\nOpBranch %143\n%143 = OpLabel\nOpLine %3 40 5\n%144 = OpCompositeExtract  %4  %111 0\nOpLine %3 40 25\n%145 = OpFAdd  %4  %144 %52\nOpLine %3 40 5\n%146 = OpAccessChain  %64  %141 %66\nOpStore %146 %145\nOpLine %3 41 5\n%147 = OpCompositeExtract  %5  %111 1\nOpLine %3 41 25\n%148 = OpFAdd  %5  %147 %53\nOpLine %3 41 5\n%149 = OpAccessChain  %69  %141 %71\nOpStore %149 %148\nOpLine %3 42 5\n%150 = OpCompositeExtract  %6  %111 2\nOpLine %3 42 23\n%151 = OpFAdd  %6  %150 %54\nOpLine %3 42 5\n%152 = OpAccessChain  %73  %141 %75\nOpStore %152 %151\nOpLine %3 43 5\n%153 = OpCompositeExtract  %7  %111 3\nOpLine %3 43 40\nOpLine %3 43 23\n%154 = OpFAdd  %7  %153 %55\nOpLine %3 43 5\n%155 = OpAccessChain  %77  %141 %79\nOpStore %155 %154\nOpLine %3 44 5\n%156 = OpCompositeExtract  %8  %111 4\nOpLine %3 44 23\n%157 = OpFAdd  %8  %156 %56\nOpLine %3 44 5\n%158 = OpAccessChain  %81  %141 %83\nOpStore %158 %157\nOpLine %3 45 5\n%159 = OpCompositeExtract  %9  %111 5\nOpLine %3 45 40\nOpLine %3 45 23\n%160 = OpFAdd  %9  %159 %57\nOpLine %3 45 5\n%161 = OpAccessChain  %85  %141 %87\nOpStore %161 %160\nOpLine %3 46 5\n%162 = OpCompositeExtract  %10  %111 6\nOpLine %3 46 23\n%163 = OpFAdd  %10  %162 %58\nOpLine %3 46 5\n%164 = OpAccessChain  %89  %141 %91\nOpStore %164 %163\nOpLine %3 47 5\n%165 = OpCompositeExtract  %11  %111 7\nOpLine %3 47 40\nOpLine %3 47 23\n%166 = OpFAdd  %11  %165 %59\nOpLine %3 47 5\n%167 = OpAccessChain  %93  %141 %95\nOpStore %167 %166\nOpLine %3 1 1\n%168 = OpLoad  %12  %141\n%169 = OpCompositeExtract  %4  %168 0\n%170 = OpFConvert  %5  %169\nOpStore %132 %170\n%171 = OpCompositeExtract  %5  %168 1\nOpStore %133 %171\n%172 = OpCompositeExtract  %6  %168 2\n%173 = OpFConvert  %7  %172\nOpStore %134 %173\n%174 = OpCompositeExtract  %7  %168 3\nOpStore %135 %174\n%175 = OpCompositeExtract  %8  %168 4\n%176 = OpFConvert  %9  %175\nOpStore %136 %176\n%177 = OpCompositeExtract  %9  %168 5\nOpStore %137 %177\n%178 = OpCompositeExtract  %10  %168 6\n%179 = OpFConvert  %11  %178\nOpStore %138 %179\n%180 = OpCompositeExtract  %11  %168 7\nOpStore %139 %180\nOpReturn\nOpFunctionEnd\n%211 = OpFunction  %2  None %51\n%181 = OpLabel\n%212 = OpVariable  %61  Function %213\n%214 = OpVariable  %61  Function %215\n%184 = OpLoad  %5  %183\n%185 = OpFConvert  %4  %184\n%187 = OpLoad  %5  %186\n%189 = OpLoad  %7  %188\n%190 = OpFConvert  %6  %189\n%192 = OpLoad  %7  %191\n%194 = OpLoad  %9  %193\n%195 = OpFConvert  %8  %194\n%197 = OpLoad  %9  %196\n%199 = OpLoad  %11  %198\n%200 = OpFConvert  %10  %199\n%202 = OpLoad  %11  %201\n%182 = OpCompositeConstruct  %12  %185 %187 %190 %192 %195 %197 %200 %202\nOpBranch %216\n%216 = OpLabel\nOpLine %3 53 5\nOpStore %212 %182\nOpLine %3 55 5\n%217 = OpAccessChain  %64  %212 %66\n%218 = OpLoad  %4  %217\nOpLine %3 55 25\n%219 = OpFAdd  %4  %218 %52\nOpLine %3 55 5\n%220 = OpAccessChain  %64  %214 %66\nOpStore %220 %219\nOpLine %3 56 5\n%221 = OpAccessChain  %69  %212 %71\n%222 = OpLoad  %5  %221\nOpLine %3 56 25\n%223 = OpFAdd  %5  %222 %53\nOpLine %3 56 5\n%224 = OpAccessChain  %69  %214 %71\nOpStore %224 %223\nOpLine %3 57 5\n%225 = OpAccessChain  %73  %212 %75\n%226 = OpLoad  %6  %225\nOpLine %3 57 23\n%227 = OpFAdd  %6  %226 %54\nOpLine %3 57 5\n%228 = OpAccessChain  %73  %214 %75\nOpStore %228 %227\nOpLine %3 58 5\n%229 = OpAccessChain  %77  %212 %79\n%230 = OpLoad  %7  %229\nOpLine %3 58 40\nOpLine %3 58 23\n%231 = OpFAdd  %7  %230 %55\nOpLine %3 58 5\n%232 = OpAccessChain  %77  %214 %79\nOpStore %232 %231\nOpLine %3 59 5\n%233 = OpAccessChain  %81  %212 %83\n%234 = OpLoad  %8  %233\nOpLine %3 59 23\n%235 = OpFAdd  %8  %234 %56\nOpLine %3 59 5\n%236 = OpAccessChain  %81  %214 %83\nOpStore %236 %235\nOpLine %3 60 5\n%237 = OpAccessChain  %85  %212 %87\n%238 = OpLoad  %9  %237\nOpLine %3 60 40\nOpLine %3 60 23\n%239 = OpFAdd  %9  %238 %57\nOpLine %3 60 5\n%240 = OpAccessChain  %85  %214 %87\nOpStore %240 %239\nOpLine %3 61 5\n%241 = OpAccessChain  %89  %212 %91\n%242 = OpLoad  %10  %241\nOpLine %3 61 23\n%243 = OpFAdd  %10  %242 %58\nOpLine %3 61 5\n%244 = OpAccessChain  %89  %214 %91\nOpStore %244 %243\nOpLine %3 62 5\n%245 = OpAccessChain  %93  %212 %95\n%246 = OpLoad  %11  %245\nOpLine %3 62 40\nOpLine %3 62 23\n%247 = OpFAdd  %11  %246 %59\nOpLine %3 62 5\n%248 = OpAccessChain  %93  %214 %95\nOpStore %248 %247\nOpLine %3 1 1\n%249 = OpLoad  %12  %214\n%250 = OpCompositeExtract  %4  %249 0\n%251 = OpFConvert  %5  %250\nOpStore %203 %251\n%252 = OpCompositeExtract  %5  %249 1\nOpStore %204 %252\n%253 = OpCompositeExtract  %6  %249 2\n%254 = OpFConvert  %7  %253\nOpStore %205 %254\n%255 = OpCompositeExtract  %7  %249 3\nOpStore %206 %255\n%256 = OpCompositeExtract  %8  %249 4\n%257 = OpFConvert  %9  %256\nOpStore %207 %257\n%258 = OpCompositeExtract  %9  %249 5\nOpStore %208 %258\n%259 = OpCompositeExtract  %10  %249 6\n%260 = OpFConvert  %11  %259\nOpStore %209 %260\n%261 = OpCompositeExtract  %11  %249 7\nOpStore %210 %261\nOpReturn\nOpFunctionEnd\n%285 = OpFunction  %2  None %51\n%262 = OpLabel\n%287 = OpVariable  %61  Function %288\n%265 = OpLoad  %5  %264\n%266 = OpFConvert  %4  %265\n%268 = OpLoad  %5  %267\n%270 = OpLoad  %7  %269\n%271 = OpFConvert  %6  %270\n%273 = OpLoad  %7  %272\n%275 = OpLoad  %9  %274\n%276 = OpFConvert  %8  %275\n%278 = OpLoad  %9  %277\n%280 = OpLoad  %11  %279\n%281 = OpFConvert  %10  %280\n%283 = OpLoad  %11  %282\n%263 = OpCompositeConstruct  %12  %266 %268 %271 %273 %276 %278 %281 %283\nOpBranch %289\n%289 = OpLabel\nOpLine %3 68 5\nOpStore %287 %263\nOpLine %3 69 5\nOpLine %3 69 5\n%290 = OpAccessChain  %64  %287 %66\nOpStore %290 %286\nOpLine %3 70 12\n%291 = OpAccessChain  %64  %287 %66\n%292 = OpLoad  %4  %291\n%293 = OpFConvert  %5  %292\nOpStore %284 %293\nOpReturn\nOpFunctionEnd\n%324 = OpFunction  %2  None %51\n%294 = OpLabel\n%325 = OpVariable  %61  Function %326\n%297 = OpLoad  %5  %296\n%298 = OpFConvert  %4  %297\n%300 = OpLoad  %5  %299\n%302 = OpLoad  %7  %301\n%303 = OpFConvert  %6  %302\n%305 = OpLoad  %7  %304\n%307 = OpLoad  %9  %306\n%308 = OpFConvert  %8  %307\n%310 = OpLoad  %9  %309\n%312 = OpLoad  %11  %311\n%313 = OpFConvert  %10  %312\n%315 = OpLoad  %11  %314\n%295 = OpCompositeConstruct  %12  %298 %300 %303 %305 %308 %310 %313 %315\nOpBranch %327\n%327 = OpLabel\nOpLine %3 76 5\n%328 = OpCompositeExtract  %6  %295 2\n%329 = OpCompositeExtract  %4  %328 1\nOpLine %3 76 5\n%330 = OpAccessChain  %64  %325 %75 %66\nOpStore %330 %329\nOpLine %3 77 5\n%331 = OpCompositeExtract  %6  %295 2\n%332 = OpCompositeExtract  %4  %331 0\nOpLine %3 77 5\n%333 = OpAccessChain  %64  %325 %75 %71\nOpStore %333 %332\nOpLine %3 1 1\n%334 = OpLoad  %12  %325\n%335 = OpCompositeExtract  %4  %334 0\n%336 = OpFConvert  %5  %335\nOpStore %316 %336\n%337 = OpCompositeExtract  %5  %334 1\nOpStore %317 %337\n%338 = OpCompositeExtract  %6  %334 2\n%339 = OpFConvert  %7  %338\nOpStore %318 %339\n%340 = OpCompositeExtract  %7  %334 3\nOpStore %319 %340\n%341 = OpCompositeExtract  %8  %334 4\n%342 = OpFConvert  %9  %341\nOpStore %320 %342\n%343 = OpCompositeExtract  %9  %334 5\nOpStore %321 %343\n%344 = OpCompositeExtract  %10  %334 6\n%345 = OpFConvert  %11  %344\nOpStore %322 %345\n%346 = OpCompositeExtract  %11  %334 7\nOpStore %323 %346\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-f16.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 568\nOpCapability Shader\nOpCapability Float16\nOpCapability StorageBuffer16BitAccess\nOpCapability UniformAndStorageBuffer16BitAccess\nOpCapability StorageInputOutput16\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\nOpExtension \"SPV_KHR_16bit_storage\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %556 \"main\"\nOpExecutionMode %556 LocalSize 1 1 1\nOpMemberDecorate %19 0 Offset 0\nOpMemberDecorate %19 1 Offset 4\nOpMemberDecorate %19 2 Offset 8\nOpMemberDecorate %19 3 Offset 12\nOpMemberDecorate %19 4 Offset 16\nOpMemberDecorate %19 5 Offset 24\nOpMemberDecorate %19 6 Offset 32\nOpMemberDecorate %19 7 Offset 40\nOpMemberDecorate %19 8 Offset 44\nOpMemberDecorate %19 8 ColMajor\nOpMemberDecorate %19 8 MatrixStride 4\nOpMemberDecorate %19 9 Offset 56\nOpMemberDecorate %19 9 ColMajor\nOpMemberDecorate %19 9 MatrixStride 8\nOpMemberDecorate %19 10 Offset 72\nOpMemberDecorate %19 10 ColMajor\nOpMemberDecorate %19 10 MatrixStride 8\nOpMemberDecorate %19 11 Offset 88\nOpMemberDecorate %19 11 ColMajor\nOpMemberDecorate %19 11 MatrixStride 4\nOpMemberDecorate %19 12 Offset 104\nOpMemberDecorate %19 12 ColMajor\nOpMemberDecorate %19 12 MatrixStride 8\nOpMemberDecorate %19 13 Offset 128\nOpMemberDecorate %19 13 ColMajor\nOpMemberDecorate %19 13 MatrixStride 8\nOpMemberDecorate %19 14 Offset 152\nOpMemberDecorate %19 14 ColMajor\nOpMemberDecorate %19 14 MatrixStride 4\nOpMemberDecorate %19 15 Offset 168\nOpMemberDecorate %19 15 ColMajor\nOpMemberDecorate %19 15 MatrixStride 8\nOpMemberDecorate %19 16 Offset 200\nOpMemberDecorate %19 16 ColMajor\nOpMemberDecorate %19 16 MatrixStride 8\nOpDecorate %20 ArrayStride 2\nOpMemberDecorate %22 0 Offset 0\nOpMemberDecorate %23 0 Offset 0\nOpMemberDecorate %23 1 Offset 2\nOpMemberDecorate %23 2 Offset 8\nOpMemberDecorate %23 3 Offset 14\nOpMemberDecorate %23 4 Offset 16\nOpMemberDecorate %23 5 Offset 20\nOpMemberDecorate %24 0 Offset 0\nOpMemberDecorate %24 1 Offset 4\nOpMemberDecorate %24 2 Offset 8\nOpMemberDecorate %24 3 Offset 12\nOpMemberDecorate %24 4 Offset 16\nOpMemberDecorate %24 5 Offset 24\nOpMemberDecorate %24 6 Offset 32\nOpMemberDecorate %24 7 Offset 40\nOpMemberDecorate %24 8 Offset 44\nOpMemberDecorate %24 9 Offset 48\nOpMemberDecorate %24 10 Offset 56\nOpMemberDecorate %24 10 ColMajor\nOpMemberDecorate %24 10 MatrixStride 8\nOpMemberDecorate %24 11 Offset 72\nOpMemberDecorate %24 11 ColMajor\nOpMemberDecorate %24 11 MatrixStride 8\nOpMemberDecorate %24 12 Offset 88\nOpMemberDecorate %24 13 Offset 92\nOpMemberDecorate %24 14 Offset 96\nOpMemberDecorate %24 15 Offset 104\nOpMemberDecorate %24 15 ColMajor\nOpMemberDecorate %24 15 MatrixStride 8\nOpMemberDecorate %24 16 Offset 128\nOpMemberDecorate %24 16 ColMajor\nOpMemberDecorate %24 16 MatrixStride 8\nOpMemberDecorate %24 17 Offset 152\nOpMemberDecorate %24 18 Offset 156\nOpMemberDecorate %24 19 Offset 160\nOpMemberDecorate %24 20 Offset 164\nOpMemberDecorate %24 21 Offset 168\nOpMemberDecorate %24 21 ColMajor\nOpMemberDecorate %24 21 MatrixStride 8\nOpMemberDecorate %24 22 Offset 200\nOpMemberDecorate %24 22 ColMajor\nOpMemberDecorate %24 22 MatrixStride 8\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 0\nOpDecorate %30 Block\nOpMemberDecorate %30 0 Offset 0\nOpDecorate %32 NonWritable\nOpDecorate %32 DescriptorSet 0\nOpDecorate %32 Binding 1\nOpDecorate %33 Block\nOpMemberDecorate %33 0 Offset 0\nOpDecorate %35 NonWritable\nOpDecorate %35 DescriptorSet 0\nOpDecorate %35 Binding 2\nOpDecorate %36 Block\nOpMemberDecorate %36 0 Offset 0\nOpDecorate %38 DescriptorSet 0\nOpDecorate %38 Binding 3\nOpDecorate %39 Block\nOpMemberDecorate %39 0 Offset 0\nOpDecorate %41 DescriptorSet 0\nOpDecorate %41 Binding 4\nOpDecorate %42 Block\nOpMemberDecorate %42 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 16\n%4 = OpTypeInt 32 0\n%5 = OpTypeInt 32 1\n%6 = OpTypeFloat 32\n%7 = OpTypeVector %3 2\n%8 = OpTypeVector %3 3\n%9 = OpTypeVector %3 4\n%10 = OpTypeMatrix %7 2\n%11 = OpTypeMatrix %8 2\n%12 = OpTypeMatrix %9 2\n%13 = OpTypeMatrix %7 3\n%14 = OpTypeMatrix %8 3\n%15 = OpTypeMatrix %9 3\n%16 = OpTypeMatrix %7 4\n%17 = OpTypeMatrix %8 4\n%18 = OpTypeMatrix %9 4\n%19 = OpTypeStruct %4 %5 %6 %3 %7 %8 %9 %3 %10 %11 %12 %13 %14 %15 %16 %17 %18\n%21 = OpConstant  %4  2\n%20 = OpTypeArray %3 %21\n%22 = OpTypeStruct %20\n%23 = OpTypeStruct %3 %3 %8 %3 %3 %4\n%24 = OpTypeStruct %4 %5 %6 %3 %7 %8 %9 %3 %7 %7 %11 %12 %7 %7 %7 %14 %15 %7 %7 %7 %7 %17 %18\n%25 = OpConstant  %3  0.000000000000000000000000000000000000000021524\n%26 = OpConstant  %3  0.000000000000000000000000000000000000000027121\n%28 = OpTypePointer Private %3\n%27 = OpVariable  %28  Private %25\n%30 = OpTypeStruct %24\n%31 = OpTypePointer Uniform %30\n%29 = OpVariable  %31  Uniform\n%33 = OpTypeStruct %19\n%34 = OpTypePointer StorageBuffer %33\n%32 = OpVariable  %34  StorageBuffer\n%36 = OpTypeStruct %22\n%37 = OpTypePointer StorageBuffer %36\n%35 = OpVariable  %37  StorageBuffer\n%39 = OpTypeStruct %19\n%40 = OpTypePointer StorageBuffer %39\n%38 = OpVariable  %40  StorageBuffer\n%42 = OpTypeStruct %22\n%43 = OpTypePointer StorageBuffer %42\n%41 = OpVariable  %43  StorageBuffer\n%47 = OpTypeFunction %3 %3\n%48 = OpTypePointer Uniform %24\n%49 = OpConstant  %4  0\n%51 = OpTypePointer StorageBuffer %19\n%53 = OpTypePointer StorageBuffer %22\n%57 = OpConstant  %3  0.000000000000000000000000000000000000000088991\n%58 = OpConstant  %3  0.000000000000000000000000000000000000000024753\n%59 = OpTypePointer Uniform %19\n%60 = OpConstant  %5  65504\n%61 = OpConstant  %5  -65504\n%62 = OpConstant  %4  65504\n%63 = OpConstant  %6  65504\n%64 = OpConstant  %6  -65504\n%66 = OpTypePointer Function %23\n%67 = OpConstantNull  %23\n%69 = OpTypePointer Function %3\n%79 = OpTypePointer Uniform %6\n%88 = OpTypePointer Uniform %3\n%89 = OpConstant  %4  3\n%95 = OpTypePointer StorageBuffer %5\n%96 = OpConstant  %4  1\n%99 = OpTypePointer StorageBuffer %4\n%102 = OpTypePointer StorageBuffer %6\n%105 = OpTypePointer StorageBuffer %3\n%112 = OpTypePointer StorageBuffer %7\n%113 = OpTypePointer Uniform %7\n%114 = OpConstant  %4  4\n%121 = OpTypePointer StorageBuffer %8\n%122 = OpTypePointer Uniform %8\n%123 = OpConstant  %4  5\n%130 = OpTypePointer StorageBuffer %9\n%131 = OpTypePointer Uniform %9\n%132 = OpConstant  %4  6\n%139 = OpTypePointer StorageBuffer %10\n%140 = OpTypePointer Uniform %10\n%141 = OpConstant  %4  8\n%142 = OpConstant  %4  9\n%158 = OpTypePointer StorageBuffer %11\n%159 = OpTypePointer Uniform %11\n%160 = OpConstant  %4  10\n%173 = OpTypePointer StorageBuffer %12\n%174 = OpTypePointer Uniform %12\n%175 = OpConstant  %4  11\n%188 = OpTypePointer StorageBuffer %13\n%189 = OpTypePointer Uniform %13\n%190 = OpConstant  %4  12\n%191 = OpConstant  %4  13\n%192 = OpConstant  %4  14\n%213 = OpTypePointer StorageBuffer %14\n%214 = OpTypePointer Uniform %14\n%215 = OpConstant  %4  15\n%231 = OpTypePointer StorageBuffer %15\n%232 = OpTypePointer Uniform %15\n%233 = OpConstant  %4  16\n%249 = OpTypePointer StorageBuffer %16\n%250 = OpTypePointer Uniform %16\n%251 = OpConstant  %4  17\n%252 = OpConstant  %4  18\n%253 = OpConstant  %4  19\n%254 = OpConstant  %4  20\n%280 = OpTypePointer StorageBuffer %17\n%281 = OpTypePointer Uniform %17\n%282 = OpConstant  %4  21\n%301 = OpTypePointer StorageBuffer %18\n%302 = OpTypePointer Uniform %18\n%303 = OpConstant  %4  22\n%322 = OpTypePointer StorageBuffer %20\n%361 = OpTypeVector %6 2\n%367 = OpTypeVector %6 3\n%373 = OpTypeVector %6 4\n%382 = OpTypeMatrix %361 2\n%396 = OpTypeMatrix %367 2\n%410 = OpTypeMatrix %373 2\n%429 = OpTypeMatrix %361 3\n%447 = OpTypeMatrix %367 3\n%465 = OpTypeMatrix %373 3\n%490 = OpTypeMatrix %361 4\n%512 = OpTypeMatrix %367 4\n%534 = OpTypeMatrix %373 4\n%557 = OpTypeFunction %2\n%563 = OpConstant  %3  0.000000000000000000000000000000000000000022959\n%566 = OpConstant  %4  7\n%46 = OpFunction  %3  None %47\n%45 = OpFunctionParameter  %3\n%44 = OpLabel\n%65 = OpVariable  %66  Function %67\n%68 = OpVariable  %69  Function %26\n%50 = OpAccessChain  %48  %29 %49\n%52 = OpAccessChain  %51  %32 %49\n%54 = OpAccessChain  %53  %35 %49\n%55 = OpAccessChain  %51  %38 %49\n%56 = OpAccessChain  %53  %41 %49\nOpBranch %70\n%70 = OpLabel\n%71 = OpLoad  %3  %27\n%72 = OpLoad  %3  %68\n%73 = OpFAdd  %3  %72 %57\nOpStore %68 %73\n%74 = OpLoad  %3  %68\n%75 = OpLoad  %3  %68\n%76 = OpFAdd  %3  %75 %58\n%77 = OpFAdd  %3  %74 %76\nOpStore %68 %77\n%78 = OpLoad  %3  %68\n%80 = OpAccessChain  %79  %50 %21\n%81 = OpLoad  %6  %80\n%82 = OpLoad  %3  %68\n%83 = OpFConvert  %6  %82\n%84 = OpFAdd  %6  %81 %83\n%85 = OpFConvert  %3  %84\n%86 = OpFAdd  %3  %78 %85\nOpStore %68 %86\n%87 = OpLoad  %3  %68\n%90 = OpAccessChain  %88  %50 %89\n%91 = OpLoad  %3  %90\n%92 = OpCompositeConstruct  %8  %91 %91 %91\n%93 = OpCompositeExtract  %3  %92 2\n%94 = OpFAdd  %3  %87 %93\nOpStore %68 %94\n%97 = OpAccessChain  %95  %55 %96\nOpStore %97 %60\n%98 = OpAccessChain  %95  %55 %96\nOpStore %98 %61\n%100 = OpAccessChain  %99  %55 %49\nOpStore %100 %62\n%101 = OpAccessChain  %99  %55 %49\nOpStore %101 %49\n%103 = OpAccessChain  %102  %55 %21\nOpStore %103 %63\n%104 = OpAccessChain  %102  %55 %21\nOpStore %104 %64\n%106 = OpAccessChain  %88  %50 %89\n%107 = OpLoad  %3  %106\n%108 = OpAccessChain  %105  %52 %89\n%109 = OpLoad  %3  %108\n%110 = OpFAdd  %3  %107 %109\n%111 = OpAccessChain  %105  %55 %89\nOpStore %111 %110\n%115 = OpAccessChain  %113  %50 %114\n%116 = OpLoad  %7  %115\n%117 = OpAccessChain  %112  %52 %114\n%118 = OpLoad  %7  %117\n%119 = OpFAdd  %7  %116 %118\n%120 = OpAccessChain  %112  %55 %114\nOpStore %120 %119\n%124 = OpAccessChain  %122  %50 %123\n%125 = OpLoad  %8  %124\n%126 = OpAccessChain  %121  %52 %123\n%127 = OpLoad  %8  %126\n%128 = OpFAdd  %8  %125 %127\n%129 = OpAccessChain  %121  %55 %123\nOpStore %129 %128\n%133 = OpAccessChain  %131  %50 %132\n%134 = OpLoad  %9  %133\n%135 = OpAccessChain  %130  %52 %132\n%136 = OpLoad  %9  %135\n%137 = OpFAdd  %9  %134 %136\n%138 = OpAccessChain  %130  %55 %132\nOpStore %138 %137\n%143 = OpAccessChain  %113  %50 %141\n%144 = OpLoad  %7  %143\n%145 = OpAccessChain  %113  %50 %142\n%146 = OpLoad  %7  %145\n%147 = OpCompositeConstruct  %10  %144 %146\n%148 = OpAccessChain  %139  %52 %141\n%149 = OpLoad  %10  %148\n%151 = OpCompositeExtract  %7  %147 0\n%152 = OpCompositeExtract  %7  %149 0\n%153 = OpFAdd  %7  %151 %152\n%154 = OpCompositeExtract  %7  %147 1\n%155 = OpCompositeExtract  %7  %149 1\n%156 = OpFAdd  %7  %154 %155\n%150 = OpCompositeConstruct  %10  %153 %156\n%157 = OpAccessChain  %139  %55 %141\nOpStore %157 %150\n%161 = OpAccessChain  %159  %50 %160\n%162 = OpLoad  %11  %161\n%163 = OpAccessChain  %158  %52 %142\n%164 = OpLoad  %11  %163\n%166 = OpCompositeExtract  %8  %162 0\n%167 = OpCompositeExtract  %8  %164 0\n%168 = OpFAdd  %8  %166 %167\n%169 = OpCompositeExtract  %8  %162 1\n%170 = OpCompositeExtract  %8  %164 1\n%171 = OpFAdd  %8  %169 %170\n%165 = OpCompositeConstruct  %11  %168 %171\n%172 = OpAccessChain  %158  %55 %142\nOpStore %172 %165\n%176 = OpAccessChain  %174  %50 %175\n%177 = OpLoad  %12  %176\n%178 = OpAccessChain  %173  %52 %160\n%179 = OpLoad  %12  %178\n%181 = OpCompositeExtract  %9  %177 0\n%182 = OpCompositeExtract  %9  %179 0\n%183 = OpFAdd  %9  %181 %182\n%184 = OpCompositeExtract  %9  %177 1\n%185 = OpCompositeExtract  %9  %179 1\n%186 = OpFAdd  %9  %184 %185\n%180 = OpCompositeConstruct  %12  %183 %186\n%187 = OpAccessChain  %173  %55 %160\nOpStore %187 %180\n%193 = OpAccessChain  %113  %50 %190\n%194 = OpLoad  %7  %193\n%195 = OpAccessChain  %113  %50 %191\n%196 = OpLoad  %7  %195\n%197 = OpAccessChain  %113  %50 %192\n%198 = OpLoad  %7  %197\n%199 = OpCompositeConstruct  %13  %194 %196 %198\n%200 = OpAccessChain  %188  %52 %175\n%201 = OpLoad  %13  %200\n%203 = OpCompositeExtract  %7  %199 0\n%204 = OpCompositeExtract  %7  %201 0\n%205 = OpFAdd  %7  %203 %204\n%206 = OpCompositeExtract  %7  %199 1\n%207 = OpCompositeExtract  %7  %201 1\n%208 = OpFAdd  %7  %206 %207\n%209 = OpCompositeExtract  %7  %199 2\n%210 = OpCompositeExtract  %7  %201 2\n%211 = OpFAdd  %7  %209 %210\n%202 = OpCompositeConstruct  %13  %205 %208 %211\n%212 = OpAccessChain  %188  %55 %175\nOpStore %212 %202\n%216 = OpAccessChain  %214  %50 %215\n%217 = OpLoad  %14  %216\n%218 = OpAccessChain  %213  %52 %190\n%219 = OpLoad  %14  %218\n%221 = OpCompositeExtract  %8  %217 0\n%222 = OpCompositeExtract  %8  %219 0\n%223 = OpFAdd  %8  %221 %222\n%224 = OpCompositeExtract  %8  %217 1\n%225 = OpCompositeExtract  %8  %219 1\n%226 = OpFAdd  %8  %224 %225\n%227 = OpCompositeExtract  %8  %217 2\n%228 = OpCompositeExtract  %8  %219 2\n%229 = OpFAdd  %8  %227 %228\n%220 = OpCompositeConstruct  %14  %223 %226 %229\n%230 = OpAccessChain  %213  %55 %190\nOpStore %230 %220\n%234 = OpAccessChain  %232  %50 %233\n%235 = OpLoad  %15  %234\n%236 = OpAccessChain  %231  %52 %191\n%237 = OpLoad  %15  %236\n%239 = OpCompositeExtract  %9  %235 0\n%240 = OpCompositeExtract  %9  %237 0\n%241 = OpFAdd  %9  %239 %240\n%242 = OpCompositeExtract  %9  %235 1\n%243 = OpCompositeExtract  %9  %237 1\n%244 = OpFAdd  %9  %242 %243\n%245 = OpCompositeExtract  %9  %235 2\n%246 = OpCompositeExtract  %9  %237 2\n%247 = OpFAdd  %9  %245 %246\n%238 = OpCompositeConstruct  %15  %241 %244 %247\n%248 = OpAccessChain  %231  %55 %191\nOpStore %248 %238\n%255 = OpAccessChain  %113  %50 %251\n%256 = OpLoad  %7  %255\n%257 = OpAccessChain  %113  %50 %252\n%258 = OpLoad  %7  %257\n%259 = OpAccessChain  %113  %50 %253\n%260 = OpLoad  %7  %259\n%261 = OpAccessChain  %113  %50 %254\n%262 = OpLoad  %7  %261\n%263 = OpCompositeConstruct  %16  %256 %258 %260 %262\n%264 = OpAccessChain  %249  %52 %192\n%265 = OpLoad  %16  %264\n%267 = OpCompositeExtract  %7  %263 0\n%268 = OpCompositeExtract  %7  %265 0\n%269 = OpFAdd  %7  %267 %268\n%270 = OpCompositeExtract  %7  %263 1\n%271 = OpCompositeExtract  %7  %265 1\n%272 = OpFAdd  %7  %270 %271\n%273 = OpCompositeExtract  %7  %263 2\n%274 = OpCompositeExtract  %7  %265 2\n%275 = OpFAdd  %7  %273 %274\n%276 = OpCompositeExtract  %7  %263 3\n%277 = OpCompositeExtract  %7  %265 3\n%278 = OpFAdd  %7  %276 %277\n%266 = OpCompositeConstruct  %16  %269 %272 %275 %278\n%279 = OpAccessChain  %249  %55 %192\nOpStore %279 %266\n%283 = OpAccessChain  %281  %50 %282\n%284 = OpLoad  %17  %283\n%285 = OpAccessChain  %280  %52 %215\n%286 = OpLoad  %17  %285\n%288 = OpCompositeExtract  %8  %284 0\n%289 = OpCompositeExtract  %8  %286 0\n%290 = OpFAdd  %8  %288 %289\n%291 = OpCompositeExtract  %8  %284 1\n%292 = OpCompositeExtract  %8  %286 1\n%293 = OpFAdd  %8  %291 %292\n%294 = OpCompositeExtract  %8  %284 2\n%295 = OpCompositeExtract  %8  %286 2\n%296 = OpFAdd  %8  %294 %295\n%297 = OpCompositeExtract  %8  %284 3\n%298 = OpCompositeExtract  %8  %286 3\n%299 = OpFAdd  %8  %297 %298\n%287 = OpCompositeConstruct  %17  %290 %293 %296 %299\n%300 = OpAccessChain  %280  %55 %215\nOpStore %300 %287\n%304 = OpAccessChain  %302  %50 %303\n%305 = OpLoad  %18  %304\n%306 = OpAccessChain  %301  %52 %233\n%307 = OpLoad  %18  %306\n%309 = OpCompositeExtract  %9  %305 0\n%310 = OpCompositeExtract  %9  %307 0\n%311 = OpFAdd  %9  %309 %310\n%312 = OpCompositeExtract  %9  %305 1\n%313 = OpCompositeExtract  %9  %307 1\n%314 = OpFAdd  %9  %312 %313\n%315 = OpCompositeExtract  %9  %305 2\n%316 = OpCompositeExtract  %9  %307 2\n%317 = OpFAdd  %9  %315 %316\n%318 = OpCompositeExtract  %9  %305 3\n%319 = OpCompositeExtract  %9  %307 3\n%320 = OpFAdd  %9  %318 %319\n%308 = OpCompositeConstruct  %18  %311 %314 %317 %320\n%321 = OpAccessChain  %301  %55 %233\nOpStore %321 %308\n%323 = OpAccessChain  %322  %54 %49\n%324 = OpLoad  %20  %323\n%325 = OpAccessChain  %322  %56 %49\nOpStore %325 %324\n%326 = OpLoad  %3  %68\n%327 = OpLoad  %3  %68\n%328 = OpExtInst  %3  %1 FAbs %327\n%329 = OpFAdd  %3  %326 %328\nOpStore %68 %329\n%330 = OpLoad  %3  %68\n%331 = OpLoad  %3  %68\n%332 = OpLoad  %3  %68\n%333 = OpLoad  %3  %68\n%334 = OpExtInst  %3  %1 FClamp %331 %332 %333\n%335 = OpFAdd  %3  %330 %334\nOpStore %68 %335\n%336 = OpLoad  %3  %68\n%337 = OpLoad  %3  %68\n%338 = OpCompositeConstruct  %7  %337 %337\n%339 = OpLoad  %3  %68\n%340 = OpCompositeConstruct  %7  %339 %339\n%341 = OpDot  %3  %338 %340\n%342 = OpFAdd  %3  %336 %341\nOpStore %68 %342\n%343 = OpLoad  %3  %68\n%344 = OpLoad  %3  %68\n%345 = OpLoad  %3  %68\n%346 = OpExtInst  %3  %1 FMax %344 %345\n%347 = OpFAdd  %3  %343 %346\nOpStore %68 %347\n%348 = OpLoad  %3  %68\n%349 = OpLoad  %3  %68\n%350 = OpLoad  %3  %68\n%351 = OpExtInst  %3  %1 FMin %349 %350\n%352 = OpFAdd  %3  %348 %351\nOpStore %68 %352\n%353 = OpLoad  %3  %68\n%354 = OpLoad  %3  %68\n%355 = OpExtInst  %3  %1 FSign %354\n%356 = OpFAdd  %3  %353 %355\nOpStore %68 %356\n%357 = OpLoad  %3  %68\n%358 = OpFAdd  %3  %357 %25\nOpStore %68 %358\n%359 = OpAccessChain  %113  %50 %114\n%360 = OpLoad  %7  %359\n%362 = OpFConvert  %361  %360\n%363 = OpFConvert  %7  %362\n%364 = OpAccessChain  %112  %55 %114\nOpStore %364 %363\n%365 = OpAccessChain  %122  %50 %123\n%366 = OpLoad  %8  %365\n%368 = OpFConvert  %367  %366\n%369 = OpFConvert  %8  %368\n%370 = OpAccessChain  %121  %55 %123\nOpStore %370 %369\n%371 = OpAccessChain  %131  %50 %132\n%372 = OpLoad  %9  %371\n%374 = OpFConvert  %373  %372\n%375 = OpFConvert  %9  %374\n%376 = OpAccessChain  %130  %55 %132\nOpStore %376 %375\n%377 = OpAccessChain  %113  %50 %141\n%378 = OpLoad  %7  %377\n%379 = OpAccessChain  %113  %50 %142\n%380 = OpLoad  %7  %379\n%381 = OpCompositeConstruct  %10  %378 %380\n%383 = OpCompositeExtract  %7  %381 0\n%384 = OpFConvert  %361  %383\n%385 = OpCompositeExtract  %7  %381 1\n%386 = OpFConvert  %361  %385\n%387 = OpCompositeConstruct  %382  %384 %386\n%388 = OpCompositeExtract  %361  %387 0\n%389 = OpFConvert  %7  %388\n%390 = OpCompositeExtract  %361  %387 1\n%391 = OpFConvert  %7  %390\n%392 = OpCompositeConstruct  %10  %389 %391\n%393 = OpAccessChain  %139  %55 %141\nOpStore %393 %392\n%394 = OpAccessChain  %159  %50 %160\n%395 = OpLoad  %11  %394\n%397 = OpCompositeExtract  %8  %395 0\n%398 = OpFConvert  %367  %397\n%399 = OpCompositeExtract  %8  %395 1\n%400 = OpFConvert  %367  %399\n%401 = OpCompositeConstruct  %396  %398 %400\n%402 = OpCompositeExtract  %367  %401 0\n%403 = OpFConvert  %8  %402\n%404 = OpCompositeExtract  %367  %401 1\n%405 = OpFConvert  %8  %404\n%406 = OpCompositeConstruct  %11  %403 %405\n%407 = OpAccessChain  %158  %55 %142\nOpStore %407 %406\n%408 = OpAccessChain  %174  %50 %175\n%409 = OpLoad  %12  %408\n%411 = OpCompositeExtract  %9  %409 0\n%412 = OpFConvert  %373  %411\n%413 = OpCompositeExtract  %9  %409 1\n%414 = OpFConvert  %373  %413\n%415 = OpCompositeConstruct  %410  %412 %414\n%416 = OpCompositeExtract  %373  %415 0\n%417 = OpFConvert  %9  %416\n%418 = OpCompositeExtract  %373  %415 1\n%419 = OpFConvert  %9  %418\n%420 = OpCompositeConstruct  %12  %417 %419\n%421 = OpAccessChain  %173  %55 %160\nOpStore %421 %420\n%422 = OpAccessChain  %113  %50 %190\n%423 = OpLoad  %7  %422\n%424 = OpAccessChain  %113  %50 %191\n%425 = OpLoad  %7  %424\n%426 = OpAccessChain  %113  %50 %192\n%427 = OpLoad  %7  %426\n%428 = OpCompositeConstruct  %13  %423 %425 %427\n%430 = OpCompositeExtract  %7  %428 0\n%431 = OpFConvert  %361  %430\n%432 = OpCompositeExtract  %7  %428 1\n%433 = OpFConvert  %361  %432\n%434 = OpCompositeExtract  %7  %428 2\n%435 = OpFConvert  %361  %434\n%436 = OpCompositeConstruct  %429  %431 %433 %435\n%437 = OpCompositeExtract  %361  %436 0\n%438 = OpFConvert  %7  %437\n%439 = OpCompositeExtract  %361  %436 1\n%440 = OpFConvert  %7  %439\n%441 = OpCompositeExtract  %361  %436 2\n%442 = OpFConvert  %7  %441\n%443 = OpCompositeConstruct  %13  %438 %440 %442\n%444 = OpAccessChain  %188  %55 %175\nOpStore %444 %443\n%445 = OpAccessChain  %214  %50 %215\n%446 = OpLoad  %14  %445\n%448 = OpCompositeExtract  %8  %446 0\n%449 = OpFConvert  %367  %448\n%450 = OpCompositeExtract  %8  %446 1\n%451 = OpFConvert  %367  %450\n%452 = OpCompositeExtract  %8  %446 2\n%453 = OpFConvert  %367  %452\n%454 = OpCompositeConstruct  %447  %449 %451 %453\n%455 = OpCompositeExtract  %367  %454 0\n%456 = OpFConvert  %8  %455\n%457 = OpCompositeExtract  %367  %454 1\n%458 = OpFConvert  %8  %457\n%459 = OpCompositeExtract  %367  %454 2\n%460 = OpFConvert  %8  %459\n%461 = OpCompositeConstruct  %14  %456 %458 %460\n%462 = OpAccessChain  %213  %55 %190\nOpStore %462 %461\n%463 = OpAccessChain  %232  %50 %233\n%464 = OpLoad  %15  %463\n%466 = OpCompositeExtract  %9  %464 0\n%467 = OpFConvert  %373  %466\n%468 = OpCompositeExtract  %9  %464 1\n%469 = OpFConvert  %373  %468\n%470 = OpCompositeExtract  %9  %464 2\n%471 = OpFConvert  %373  %470\n%472 = OpCompositeConstruct  %465  %467 %469 %471\n%473 = OpCompositeExtract  %373  %472 0\n%474 = OpFConvert  %9  %473\n%475 = OpCompositeExtract  %373  %472 1\n%476 = OpFConvert  %9  %475\n%477 = OpCompositeExtract  %373  %472 2\n%478 = OpFConvert  %9  %477\n%479 = OpCompositeConstruct  %15  %474 %476 %478\n%480 = OpAccessChain  %231  %55 %191\nOpStore %480 %479\n%481 = OpAccessChain  %113  %50 %251\n%482 = OpLoad  %7  %481\n%483 = OpAccessChain  %113  %50 %252\n%484 = OpLoad  %7  %483\n%485 = OpAccessChain  %113  %50 %253\n%486 = OpLoad  %7  %485\n%487 = OpAccessChain  %113  %50 %254\n%488 = OpLoad  %7  %487\n%489 = OpCompositeConstruct  %16  %482 %484 %486 %488\n%491 = OpCompositeExtract  %7  %489 0\n%492 = OpFConvert  %361  %491\n%493 = OpCompositeExtract  %7  %489 1\n%494 = OpFConvert  %361  %493\n%495 = OpCompositeExtract  %7  %489 2\n%496 = OpFConvert  %361  %495\n%497 = OpCompositeExtract  %7  %489 3\n%498 = OpFConvert  %361  %497\n%499 = OpCompositeConstruct  %490  %492 %494 %496 %498\n%500 = OpCompositeExtract  %361  %499 0\n%501 = OpFConvert  %7  %500\n%502 = OpCompositeExtract  %361  %499 1\n%503 = OpFConvert  %7  %502\n%504 = OpCompositeExtract  %361  %499 2\n%505 = OpFConvert  %7  %504\n%506 = OpCompositeExtract  %361  %499 3\n%507 = OpFConvert  %7  %506\n%508 = OpCompositeConstruct  %16  %501 %503 %505 %507\n%509 = OpAccessChain  %249  %55 %192\nOpStore %509 %508\n%510 = OpAccessChain  %281  %50 %282\n%511 = OpLoad  %17  %510\n%513 = OpCompositeExtract  %8  %511 0\n%514 = OpFConvert  %367  %513\n%515 = OpCompositeExtract  %8  %511 1\n%516 = OpFConvert  %367  %515\n%517 = OpCompositeExtract  %8  %511 2\n%518 = OpFConvert  %367  %517\n%519 = OpCompositeExtract  %8  %511 3\n%520 = OpFConvert  %367  %519\n%521 = OpCompositeConstruct  %512  %514 %516 %518 %520\n%522 = OpCompositeExtract  %367  %521 0\n%523 = OpFConvert  %8  %522\n%524 = OpCompositeExtract  %367  %521 1\n%525 = OpFConvert  %8  %524\n%526 = OpCompositeExtract  %367  %521 2\n%527 = OpFConvert  %8  %526\n%528 = OpCompositeExtract  %367  %521 3\n%529 = OpFConvert  %8  %528\n%530 = OpCompositeConstruct  %17  %523 %525 %527 %529\n%531 = OpAccessChain  %280  %55 %215\nOpStore %531 %530\n%532 = OpAccessChain  %302  %50 %303\n%533 = OpLoad  %18  %532\n%535 = OpCompositeExtract  %9  %533 0\n%536 = OpFConvert  %373  %535\n%537 = OpCompositeExtract  %9  %533 1\n%538 = OpFConvert  %373  %537\n%539 = OpCompositeExtract  %9  %533 2\n%540 = OpFConvert  %373  %539\n%541 = OpCompositeExtract  %9  %533 3\n%542 = OpFConvert  %373  %541\n%543 = OpCompositeConstruct  %534  %536 %538 %540 %542\n%544 = OpCompositeExtract  %373  %543 0\n%545 = OpFConvert  %9  %544\n%546 = OpCompositeExtract  %373  %543 1\n%547 = OpFConvert  %9  %546\n%548 = OpCompositeExtract  %373  %543 2\n%549 = OpFConvert  %9  %548\n%550 = OpCompositeExtract  %373  %543 3\n%551 = OpFConvert  %9  %550\n%552 = OpCompositeConstruct  %18  %545 %547 %549 %551\n%553 = OpAccessChain  %301  %55 %233\nOpStore %553 %552\n%554 = OpLoad  %3  %68\nOpReturnValue %554\nOpFunctionEnd\n%556 = OpFunction  %2  None %557\n%555 = OpLabel\n%558 = OpAccessChain  %48  %29 %49\n%559 = OpAccessChain  %51  %32 %49\n%560 = OpAccessChain  %53  %35 %49\n%561 = OpAccessChain  %51  %38 %49\n%562 = OpAccessChain  %53  %41 %49\nOpBranch %564\n%564 = OpLabel\n%565 = OpFunctionCall  %3  %46 %563\n%567 = OpAccessChain  %105  %561 %566\nOpStore %567 %565\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-f64.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 33\nOpCapability Shader\nOpCapability Float64\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %28 \"main\"\nOpExecutionMode %28 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeFloat 64\n%4 = OpConstant  %3  1\n%5 = OpConstant  %3  2\n%7 = OpTypePointer Private %3\n%6 = OpVariable  %7  Private %4\n%11 = OpTypeFunction %3 %3\n%12 = OpConstant  %3  30\n%13 = OpConstant  %3  400\n%14 = OpConstant  %3  5\n%15 = OpConstant  %3  -1\n%17 = OpTypePointer Function %3\n%18 = OpConstantNull  %3\n%29 = OpTypeFunction %2\n%30 = OpConstant  %3  6\n%10 = OpFunction  %3  None %11\n%9 = OpFunctionParameter  %3\n%8 = OpLabel\n%16 = OpVariable  %17  Function %18\n%19 = OpVariable  %17  Function %15\nOpBranch %20\n%20 = OpLabel\n%21 = OpLoad  %3  %6\n%22 = OpFAdd  %3  %12 %13\n%23 = OpFAdd  %3  %22 %14\nOpStore %16 %23\n%24 = OpFAdd  %3  %9 %22\n%25 = OpFAdd  %3  %24 %5\n%26 = OpFAdd  %3  %25 %14\nOpReturnValue %26\nOpFunctionEnd\n%28 = OpFunction  %2  None %29\n%27 = OpLabel\nOpBranch %31\n%31 = OpLabel\n%32 = OpFunctionCall  %3  %10 %30\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-fragment-output.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 109\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %30 \"main_vec4vec3\" %18 %20 %22 %24 %26 %28\nOpEntryPoint Fragment %82 \"main_vec2scalar\" %70 %72 %74 %76 %78 %80\nOpExecutionMode %30 OriginUpperLeft\nOpExecutionMode %82 OriginUpperLeft\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 16\nOpMemberDecorate %12 2 Offset 32\nOpMemberDecorate %12 3 Offset 48\nOpMemberDecorate %12 4 Offset 64\nOpMemberDecorate %12 5 Offset 80\nOpMemberDecorate %16 0 Offset 0\nOpMemberDecorate %16 1 Offset 8\nOpMemberDecorate %16 2 Offset 16\nOpMemberDecorate %16 3 Offset 24\nOpMemberDecorate %16 4 Offset 28\nOpMemberDecorate %16 5 Offset 32\nOpDecorate %18 Location 0\nOpDecorate %20 Location 1\nOpDecorate %22 Location 2\nOpDecorate %24 Location 3\nOpDecorate %26 Location 4\nOpDecorate %28 Location 5\nOpDecorate %70 Location 0\nOpDecorate %72 Location 1\nOpDecorate %74 Location 2\nOpDecorate %76 Location 3\nOpDecorate %78 Location 4\nOpDecorate %80 Location 5\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeInt 32 1\n%6 = OpTypeVector %5 4\n%7 = OpTypeInt 32 0\n%8 = OpTypeVector %7 4\n%9 = OpTypeVector %3 3\n%10 = OpTypeVector %5 3\n%11 = OpTypeVector %7 3\n%12 = OpTypeStruct %4 %6 %8 %9 %10 %11\n%13 = OpTypeVector %3 2\n%14 = OpTypeVector %5 2\n%15 = OpTypeVector %7 2\n%16 = OpTypeStruct %13 %14 %15 %3 %5 %7\n%19 = OpTypePointer Output %4\n%18 = OpVariable  %19  Output\n%21 = OpTypePointer Output %6\n%20 = OpVariable  %21  Output\n%23 = OpTypePointer Output %8\n%22 = OpVariable  %23  Output\n%25 = OpTypePointer Output %9\n%24 = OpVariable  %25  Output\n%27 = OpTypePointer Output %10\n%26 = OpVariable  %27  Output\n%29 = OpTypePointer Output %11\n%28 = OpVariable  %29  Output\n%31 = OpTypeFunction %2\n%32 = OpConstant  %3  0\n%33 = OpConstantComposite  %4  %32 %32 %32 %32\n%34 = OpConstant  %5  0\n%35 = OpConstantComposite  %6  %34 %34 %34 %34\n%36 = OpConstant  %7  0\n%37 = OpConstantComposite  %8  %36 %36 %36 %36\n%38 = OpConstantComposite  %9  %32 %32 %32\n%39 = OpConstantComposite  %10  %34 %34 %34\n%40 = OpConstantComposite  %11  %36 %36 %36\n%42 = OpTypePointer Function %12\n%43 = OpConstantNull  %12\n%45 = OpTypePointer Function %4\n%47 = OpTypePointer Function %6\n%48 = OpConstant  %7  1\n%50 = OpTypePointer Function %8\n%51 = OpConstant  %7  2\n%53 = OpTypePointer Function %9\n%54 = OpConstant  %7  3\n%56 = OpTypePointer Function %10\n%57 = OpConstant  %7  4\n%59 = OpTypePointer Function %11\n%60 = OpConstant  %7  5\n%71 = OpTypePointer Output %13\n%70 = OpVariable  %71  Output\n%73 = OpTypePointer Output %14\n%72 = OpVariable  %73  Output\n%75 = OpTypePointer Output %15\n%74 = OpVariable  %75  Output\n%77 = OpTypePointer Output %3\n%76 = OpVariable  %77  Output\n%79 = OpTypePointer Output %5\n%78 = OpVariable  %79  Output\n%81 = OpTypePointer Output %7\n%80 = OpVariable  %81  Output\n%83 = OpConstantComposite  %13  %32 %32\n%84 = OpConstantComposite  %14  %34 %34\n%85 = OpConstantComposite  %15  %36 %36\n%87 = OpTypePointer Function %16\n%88 = OpConstantNull  %16\n%90 = OpTypePointer Function %13\n%92 = OpTypePointer Function %14\n%94 = OpTypePointer Function %15\n%96 = OpTypePointer Function %3\n%98 = OpTypePointer Function %5\n%100 = OpTypePointer Function %7\n%30 = OpFunction  %2  None %31\n%17 = OpLabel\n%41 = OpVariable  %42  Function %43\nOpBranch %44\n%44 = OpLabel\n%46 = OpAccessChain  %45  %41 %36\nOpStore %46 %33\n%49 = OpAccessChain  %47  %41 %48\nOpStore %49 %35\n%52 = OpAccessChain  %50  %41 %51\nOpStore %52 %37\n%55 = OpAccessChain  %53  %41 %54\nOpStore %55 %38\n%58 = OpAccessChain  %56  %41 %57\nOpStore %58 %39\n%61 = OpAccessChain  %59  %41 %60\nOpStore %61 %40\n%62 = OpLoad  %12  %41\n%63 = OpCompositeExtract  %4  %62 0\nOpStore %18 %63\n%64 = OpCompositeExtract  %6  %62 1\nOpStore %20 %64\n%65 = OpCompositeExtract  %8  %62 2\nOpStore %22 %65\n%66 = OpCompositeExtract  %9  %62 3\nOpStore %24 %66\n%67 = OpCompositeExtract  %10  %62 4\nOpStore %26 %67\n%68 = OpCompositeExtract  %11  %62 5\nOpStore %28 %68\nOpReturn\nOpFunctionEnd\n%82 = OpFunction  %2  None %31\n%69 = OpLabel\n%86 = OpVariable  %87  Function %88\nOpBranch %89\n%89 = OpLabel\n%91 = OpAccessChain  %90  %86 %36\nOpStore %91 %83\n%93 = OpAccessChain  %92  %86 %48\nOpStore %93 %84\n%95 = OpAccessChain  %94  %86 %51\nOpStore %95 %85\n%97 = OpAccessChain  %96  %86 %54\nOpStore %97 %32\n%99 = OpAccessChain  %98  %86 %57\nOpStore %99 %34\n%101 = OpAccessChain  %100  %86 %60\nOpStore %101 %36\n%102 = OpLoad  %16  %86\n%103 = OpCompositeExtract  %13  %102 0\nOpStore %70 %103\n%104 = OpCompositeExtract  %14  %102 1\nOpStore %72 %104\n%105 = OpCompositeExtract  %15  %102 2\nOpStore %74 %105\n%106 = OpCompositeExtract  %3  %102 3\nOpStore %76 %106\n%107 = OpCompositeExtract  %5  %102 4\nOpStore %78 %107\n%108 = OpCompositeExtract  %7  %102 5\nOpStore %80 %108\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-functions-optimized-by-capability.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 30\nOpCapability Shader\nOpCapability DotProduct\nOpCapability DotProductInput4x8BitPacked\nOpExtension \"SPV_KHR_integer_dot_product\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %26 \"main\"\nOpExecutionMode %26 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%6 = OpTypeFunction %3\n%7 = OpConstant  %3  1\n%8 = OpConstant  %3  2\n%9 = OpConstant  %3  3\n%10 = OpConstant  %3  4\n%11 = OpConstant  %3  5\n%12 = OpConstant  %3  6\n%13 = OpConstant  %3  7\n%14 = OpConstant  %3  8\n%16 = OpTypeInt 32 1\n%27 = OpTypeFunction %2\n%5 = OpFunction  %3  None %6\n%4 = OpLabel\nOpBranch %15\n%15 = OpLabel\n%17 = OpSDot  %16  %7 %8 PackedVectorFormat4x8Bit\n%18 = OpUDot  %3  %9 %10 PackedVectorFormat4x8Bit\n%19 = OpIAdd  %3  %11 %18\n%20 = OpIAdd  %3  %12 %18\n%21 = OpSDot  %16  %19 %20 PackedVectorFormat4x8Bit\n%22 = OpIAdd  %3  %13 %18\n%23 = OpIAdd  %3  %14 %18\n%24 = OpUDot  %3  %22 %23 PackedVectorFormat4x8Bit\nOpReturnValue %24\nOpFunctionEnd\n%26 = OpFunction  %2  None %27\n%25 = OpLabel\nOpBranch %28\n%28 = OpLabel\n%29 = OpFunctionCall  %3  %5\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-functions-optimized-by-version.spvasm",
    "content": "; SPIR-V\n; Version: 1.6\n; Generator: rspirv\n; Bound: 30\nOpCapability Shader\nOpCapability DotProduct\nOpCapability DotProductInput4x8BitPacked\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %26 \"main\"\nOpExecutionMode %26 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%6 = OpTypeFunction %3\n%7 = OpConstant  %3  1\n%8 = OpConstant  %3  2\n%9 = OpConstant  %3  3\n%10 = OpConstant  %3  4\n%11 = OpConstant  %3  5\n%12 = OpConstant  %3  6\n%13 = OpConstant  %3  7\n%14 = OpConstant  %3  8\n%16 = OpTypeInt 32 1\n%27 = OpTypeFunction %2\n%5 = OpFunction  %3  None %6\n%4 = OpLabel\nOpBranch %15\n%15 = OpLabel\n%17 = OpSDot  %16  %7 %8 PackedVectorFormat4x8Bit\n%18 = OpUDot  %3  %9 %10 PackedVectorFormat4x8Bit\n%19 = OpIAdd  %3  %11 %18\n%20 = OpIAdd  %3  %12 %18\n%21 = OpSDot  %16  %19 %20 PackedVectorFormat4x8Bit\n%22 = OpIAdd  %3  %13 %18\n%23 = OpIAdd  %3  %14 %18\n%24 = OpUDot  %3  %22 %23 PackedVectorFormat4x8Bit\nOpReturnValue %24\nOpFunctionEnd\n%26 = OpFunction  %2  None %27\n%25 = OpLabel\nOpBranch %28\n%28 = OpLabel\n%29 = OpFunctionCall  %3  %5\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-functions-unoptimized.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 99\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %95 \"main\"\nOpExecutionMode %95 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%6 = OpTypeFunction %3\n%7 = OpConstant  %3  1\n%8 = OpConstant  %3  2\n%9 = OpConstant  %3  3\n%10 = OpConstant  %3  4\n%11 = OpConstant  %3  5\n%12 = OpConstant  %3  6\n%13 = OpConstant  %3  7\n%14 = OpConstant  %3  8\n%16 = OpTypeInt 32 1\n%20 = OpConstant  %3  0\n%21 = OpConstant  %3  16\n%22 = OpConstant  %3  24\n%23 = OpConstantNull  %16\n%40 = OpConstantNull  %3\n%96 = OpTypeFunction %2\n%5 = OpFunction  %3  None %6\n%4 = OpLabel\nOpBranch %15\n%15 = OpLabel\n%18 = OpBitcast  %16  %7\n%19 = OpBitcast  %16  %8\n%24 = OpBitFieldSExtract  %16  %18 %20 %14\n%25 = OpBitFieldSExtract  %16  %19 %20 %14\n%26 = OpIMul  %16  %24 %25\n%27 = OpIAdd  %16  %23 %26\n%28 = OpBitFieldSExtract  %16  %18 %14 %14\n%29 = OpBitFieldSExtract  %16  %19 %14 %14\n%30 = OpIMul  %16  %28 %29\n%31 = OpIAdd  %16  %27 %30\n%32 = OpBitFieldSExtract  %16  %18 %21 %14\n%33 = OpBitFieldSExtract  %16  %19 %21 %14\n%34 = OpIMul  %16  %32 %33\n%35 = OpIAdd  %16  %31 %34\n%36 = OpBitFieldSExtract  %16  %18 %22 %14\n%37 = OpBitFieldSExtract  %16  %19 %22 %14\n%38 = OpIMul  %16  %36 %37\n%17 = OpIAdd  %16  %35 %38\n%41 = OpBitFieldUExtract  %3  %9 %20 %14\n%42 = OpBitFieldUExtract  %3  %10 %20 %14\n%43 = OpIMul  %3  %41 %42\n%44 = OpIAdd  %3  %40 %43\n%45 = OpBitFieldUExtract  %3  %9 %14 %14\n%46 = OpBitFieldUExtract  %3  %10 %14 %14\n%47 = OpIMul  %3  %45 %46\n%48 = OpIAdd  %3  %44 %47\n%49 = OpBitFieldUExtract  %3  %9 %21 %14\n%50 = OpBitFieldUExtract  %3  %10 %21 %14\n%51 = OpIMul  %3  %49 %50\n%52 = OpIAdd  %3  %48 %51\n%53 = OpBitFieldUExtract  %3  %9 %22 %14\n%54 = OpBitFieldUExtract  %3  %10 %22 %14\n%55 = OpIMul  %3  %53 %54\n%39 = OpIAdd  %3  %52 %55\n%56 = OpIAdd  %3  %11 %39\n%57 = OpIAdd  %3  %12 %39\n%59 = OpBitcast  %16  %56\n%60 = OpBitcast  %16  %57\n%61 = OpBitFieldSExtract  %16  %59 %20 %14\n%62 = OpBitFieldSExtract  %16  %60 %20 %14\n%63 = OpIMul  %16  %61 %62\n%64 = OpIAdd  %16  %23 %63\n%65 = OpBitFieldSExtract  %16  %59 %14 %14\n%66 = OpBitFieldSExtract  %16  %60 %14 %14\n%67 = OpIMul  %16  %65 %66\n%68 = OpIAdd  %16  %64 %67\n%69 = OpBitFieldSExtract  %16  %59 %21 %14\n%70 = OpBitFieldSExtract  %16  %60 %21 %14\n%71 = OpIMul  %16  %69 %70\n%72 = OpIAdd  %16  %68 %71\n%73 = OpBitFieldSExtract  %16  %59 %22 %14\n%74 = OpBitFieldSExtract  %16  %60 %22 %14\n%75 = OpIMul  %16  %73 %74\n%58 = OpIAdd  %16  %72 %75\n%76 = OpIAdd  %3  %13 %39\n%77 = OpIAdd  %3  %14 %39\n%79 = OpBitFieldUExtract  %3  %76 %20 %14\n%80 = OpBitFieldUExtract  %3  %77 %20 %14\n%81 = OpIMul  %3  %79 %80\n%82 = OpIAdd  %3  %40 %81\n%83 = OpBitFieldUExtract  %3  %76 %14 %14\n%84 = OpBitFieldUExtract  %3  %77 %14 %14\n%85 = OpIMul  %3  %83 %84\n%86 = OpIAdd  %3  %82 %85\n%87 = OpBitFieldUExtract  %3  %76 %21 %14\n%88 = OpBitFieldUExtract  %3  %77 %21 %14\n%89 = OpIMul  %3  %87 %88\n%90 = OpIAdd  %3  %86 %89\n%91 = OpBitFieldUExtract  %3  %76 %22 %14\n%92 = OpBitFieldUExtract  %3  %77 %22 %14\n%93 = OpIMul  %3  %91 %92\n%78 = OpIAdd  %3  %90 %93\nOpReturnValue %78\nOpFunctionEnd\n%95 = OpFunction  %2  None %96\n%94 = OpLabel\nOpBranch %97\n%97 = OpLabel\n%98 = OpFunctionCall  %3  %5\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-functions.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 75\nOpCapability Shader\nOpCapability DotProduct\nOpCapability DotProductInput4x8BitPacked\nOpExtension \"SPV_KHR_integer_dot_product\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %69 \"main\"\nOpExecutionMode %69 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 2\n%5 = OpTypeInt 32 1\n%6 = OpTypeInt 32 0\n%9 = OpTypeFunction %3\n%10 = OpConstant  %4  2\n%11 = OpConstantComposite  %3  %10 %10\n%12 = OpConstant  %4  0.5\n%13 = OpConstantComposite  %3  %12 %12\n%18 = OpTypeFunction %5\n%19 = OpConstant  %5  1\n%20 = OpTypeVector %5 2\n%21 = OpConstantComposite  %20  %19 %19\n%22 = OpConstant  %6  1\n%23 = OpTypeVector %6 3\n%24 = OpConstantComposite  %23  %22 %22 %22\n%25 = OpConstant  %5  32\n%28 = OpConstantNull  %5\n%37 = OpConstantNull  %6\n%51 = OpTypeFunction %6\n%52 = OpConstant  %6  2\n%53 = OpConstant  %6  3\n%54 = OpConstant  %6  4\n%55 = OpConstant  %6  5\n%56 = OpConstant  %6  6\n%57 = OpConstant  %6  7\n%58 = OpConstant  %6  8\n%70 = OpTypeFunction %2\n%8 = OpFunction  %3  None %9\n%7 = OpLabel\nOpBranch %14\n%14 = OpLabel\n%15 = OpExtInst  %3  %1 Fma %11 %13 %13\nOpReturnValue %15\nOpFunctionEnd\n%17 = OpFunction  %5  None %18\n%16 = OpLabel\nOpBranch %26\n%26 = OpLabel\n%29 = OpCompositeExtract  %5  %21 0\n%30 = OpCompositeExtract  %5  %21 0\n%31 = OpIMul  %5  %29 %30\n%32 = OpIAdd  %5  %28 %31\n%33 = OpCompositeExtract  %5  %21 1\n%34 = OpCompositeExtract  %5  %21 1\n%35 = OpIMul  %5  %33 %34\n%27 = OpIAdd  %5  %32 %35\n%38 = OpCompositeExtract  %6  %24 0\n%39 = OpCompositeExtract  %6  %24 0\n%40 = OpIMul  %6  %38 %39\n%41 = OpIAdd  %6  %37 %40\n%42 = OpCompositeExtract  %6  %24 1\n%43 = OpCompositeExtract  %6  %24 1\n%44 = OpIMul  %6  %42 %43\n%45 = OpIAdd  %6  %41 %44\n%46 = OpCompositeExtract  %6  %24 2\n%47 = OpCompositeExtract  %6  %24 2\n%48 = OpIMul  %6  %46 %47\n%36 = OpIAdd  %6  %45 %48\nOpReturnValue %25\nOpFunctionEnd\n%50 = OpFunction  %6  None %51\n%49 = OpLabel\nOpBranch %59\n%59 = OpLabel\n%60 = OpSDot  %5  %22 %52 PackedVectorFormat4x8Bit\n%61 = OpUDot  %6  %53 %54 PackedVectorFormat4x8Bit\n%62 = OpIAdd  %6  %55 %61\n%63 = OpIAdd  %6  %56 %61\n%64 = OpSDot  %5  %62 %63 PackedVectorFormat4x8Bit\n%65 = OpIAdd  %6  %57 %61\n%66 = OpIAdd  %6  %58 %61\n%67 = OpUDot  %6  %65 %66 PackedVectorFormat4x8Bit\nOpReturnValue %67\nOpFunctionEnd\n%69 = OpFunction  %2  None %70\n%68 = OpLabel\nOpBranch %71\n%71 = OpLabel\n%72 = OpFunctionCall  %3  %8\n%73 = OpFunctionCall  %5  %17\n%74 = OpFunctionCall  %6  %50\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-globals.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 193\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %114 \"main\" %138\nOpExecutionMode %114 LocalSize 1 1 1\nOpDecorate %5 ArrayStride 4\nOpMemberDecorate %9 0 Offset 0\nOpMemberDecorate %9 1 Offset 12\nOpDecorate %11 ArrayStride 8\nOpDecorate %13 ArrayStride 16\nOpDecorate %17 ArrayStride 32\nOpDecorate %19 ArrayStride 64\nOpDecorate %21 ArrayStride 32\nOpDecorate %22 ArrayStride 64\nOpMemberDecorate %25 0 Offset 0\nOpMemberDecorate %25 1 Offset 8\nOpMemberDecorate %25 2 Offset 16\nOpMemberDecorate %26 0 Offset 0\nOpMemberDecorate %26 1 Offset 8\nOpMemberDecorate %26 2 Offset 16\nOpMemberDecorate %26 3 Offset 24\nOpDecorate %27 ArrayStride 32\nOpDecorate %28 ArrayStride 64\nOpDecorate %34 DescriptorSet 0\nOpDecorate %34 Binding 1\nOpDecorate %35 Block\nOpMemberDecorate %35 0 Offset 0\nOpDecorate %37 NonWritable\nOpDecorate %37 DescriptorSet 0\nOpDecorate %37 Binding 2\nOpDecorate %38 Block\nOpMemberDecorate %38 0 Offset 0\nOpDecorate %40 DescriptorSet 0\nOpDecorate %40 Binding 3\nOpDecorate %41 Block\nOpMemberDecorate %41 0 Offset 0\nOpDecorate %43 DescriptorSet 0\nOpDecorate %43 Binding 4\nOpDecorate %44 Block\nOpMemberDecorate %44 0 Offset 0\nOpDecorate %46 DescriptorSet 0\nOpDecorate %46 Binding 5\nOpDecorate %47 Block\nOpMemberDecorate %47 0 Offset 0\nOpDecorate %49 DescriptorSet 0\nOpDecorate %49 Binding 6\nOpDecorate %50 Block\nOpMemberDecorate %50 0 Offset 0\nOpMemberDecorate %50 0 ColMajor\nOpMemberDecorate %50 0 MatrixStride 16\nOpDecorate %52 DescriptorSet 0\nOpDecorate %52 Binding 7\nOpDecorate %53 Block\nOpMemberDecorate %53 0 Offset 0\nOpDecorate %138 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeBool\n%4 = OpTypeFloat 32\n%7 = OpTypeInt 32 0\n%6 = OpConstant  %7  10\n%5 = OpTypeArray %4 %6\n%8 = OpTypeVector %4 3\n%9 = OpTypeStruct %8 %4\n%10 = OpTypeVector %4 2\n%11 = OpTypeRuntimeArray %10\n%12 = OpTypeVector %4 4\n%14 = OpConstant  %7  20\n%13 = OpTypeArray %12 %14\n%15 = OpTypeMatrix %10 3\n%16 = OpTypeMatrix %12 2\n%18 = OpConstant  %7  2\n%17 = OpTypeArray %16 %18\n%19 = OpTypeArray %17 %18\n%20 = OpTypeMatrix %10 4\n%21 = OpTypeArray %20 %18\n%22 = OpTypeArray %21 %18\n%23 = OpTypeInt 32 1\n%24 = OpTypeMatrix %8 3\n%25 = OpTypeStruct %10 %10 %10\n%26 = OpTypeStruct %10 %10 %10 %10\n%27 = OpTypeArray %26 %18\n%28 = OpTypeArray %27 %18\n%29 = OpConstantTrue  %3\n%31 = OpTypePointer Workgroup %5\n%30 = OpVariable  %31  Workgroup\n%33 = OpTypePointer Workgroup %7\n%32 = OpVariable  %33  Workgroup\n%35 = OpTypeStruct %9\n%36 = OpTypePointer StorageBuffer %35\n%34 = OpVariable  %36  StorageBuffer\n%38 = OpTypeStruct %11\n%39 = OpTypePointer StorageBuffer %38\n%37 = OpVariable  %39  StorageBuffer\n%41 = OpTypeStruct %13\n%42 = OpTypePointer Uniform %41\n%40 = OpVariable  %42  Uniform\n%44 = OpTypeStruct %8\n%45 = OpTypePointer Uniform %44\n%43 = OpVariable  %45  Uniform\n%47 = OpTypeStruct %25\n%48 = OpTypePointer Uniform %47\n%46 = OpVariable  %48  Uniform\n%50 = OpTypeStruct %19\n%51 = OpTypePointer Uniform %50\n%49 = OpVariable  %51  Uniform\n%53 = OpTypeStruct %28\n%54 = OpTypePointer Uniform %53\n%52 = OpVariable  %54  Uniform\n%58 = OpTypeFunction %2 %8\n%62 = OpTypeFunction %2\n%63 = OpTypePointer StorageBuffer %9\n%64 = OpConstant  %7  0\n%66 = OpConstant  %4  1\n%67 = OpConstantComposite  %8  %66 %66 %66\n%68 = OpConstant  %23  1\n%69 = OpConstant  %4  2\n%70 = OpConstant  %4  3\n%71 = OpConstantNull  %24\n%73 = OpTypePointer Function %23\n%75 = OpTypePointer StorageBuffer %8\n%77 = OpTypePointer StorageBuffer %4\n%97 = OpTypeFunction %20 %26\n%106 = OpTypeFunction %15 %25\n%116 = OpTypePointer StorageBuffer %11\n%118 = OpTypePointer Uniform %13\n%120 = OpTypePointer Uniform %8\n%122 = OpTypePointer Uniform %25\n%124 = OpTypePointer Uniform %19\n%126 = OpTypePointer Uniform %28\n%128 = OpTypePointer Uniform %22\n%129 = OpTypePointer Uniform %15\n%130 = OpConstant  %4  4\n%132 = OpTypePointer Function %4\n%134 = OpTypePointer Function %3\n%136 = OpConstantNull  %5\n%137 = OpConstantNull  %7\n%139 = OpTypePointer Input %7\n%138 = OpVariable  %139  Input\n%144 = OpConstant  %7  264\n%147 = OpTypePointer Workgroup %4\n%148 = OpTypePointer Uniform %21\n%149 = OpTypePointer Uniform %20\n%150 = OpTypePointer Uniform %26\n%154 = OpTypePointer Uniform %17\n%155 = OpTypePointer Uniform %16\n%156 = OpTypePointer Uniform %12\n%161 = OpConstant  %7  7\n%168 = OpConstant  %7  6\n%170 = OpTypePointer StorageBuffer %10\n%171 = OpConstant  %7  1\n%174 = OpConstant  %7  5\n%176 = OpTypePointer Uniform %4\n%177 = OpConstant  %7  3\n%180 = OpConstant  %7  4\n%192 = OpConstant  %23  2\n%57 = OpFunction  %2  None %58\n%56 = OpFunctionParameter  %8\n%55 = OpLabel\nOpBranch %59\n%59 = OpLabel\nOpReturn\nOpFunctionEnd\n%61 = OpFunction  %2  None %62\n%60 = OpLabel\n%72 = OpVariable  %73  Function %68\n%65 = OpAccessChain  %63  %34 %64\nOpBranch %74\n%74 = OpLabel\n%76 = OpAccessChain  %75  %65 %64\nOpStore %76 %67\n%78 = OpAccessChain  %77  %65 %64 %64\nOpStore %78 %66\n%79 = OpAccessChain  %77  %65 %64 %64\nOpStore %79 %69\n%80 = OpLoad  %23  %72\n%81 = OpAccessChain  %77  %65 %64 %80\nOpStore %81 %70\n%82 = OpLoad  %9  %65\n%83 = OpCompositeExtract  %8  %82 0\n%84 = OpCompositeExtract  %8  %82 0\n%85 = OpVectorShuffle  %10  %84 %84 2 0\n%86 = OpCompositeExtract  %8  %82 0\n%87 = OpFunctionCall  %2  %57 %86\n%88 = OpCompositeExtract  %8  %82 0\n%89 = OpVectorTimesMatrix  %8  %88 %71\n%90 = OpCompositeExtract  %8  %82 0\n%91 = OpMatrixTimesVector  %8  %71 %90\n%92 = OpCompositeExtract  %8  %82 0\n%93 = OpVectorTimesScalar  %8  %92 %69\n%94 = OpCompositeExtract  %8  %82 0\n%95 = OpVectorTimesScalar  %8  %94 %69\nOpReturn\nOpFunctionEnd\n%96 = OpFunction  %20  None %97\n%98 = OpFunctionParameter  %26\n%99 = OpLabel\n%100 = OpCompositeExtract  %10  %98 0\n%101 = OpCompositeExtract  %10  %98 1\n%102 = OpCompositeExtract  %10  %98 2\n%103 = OpCompositeExtract  %10  %98 3\n%104 = OpCompositeConstruct  %20  %100 %101 %102 %103\nOpReturnValue %104\nOpFunctionEnd\n%105 = OpFunction  %15  None %106\n%107 = OpFunctionParameter  %25\n%108 = OpLabel\n%109 = OpCompositeExtract  %10  %107 0\n%110 = OpCompositeExtract  %10  %107 1\n%111 = OpCompositeExtract  %10  %107 2\n%112 = OpCompositeConstruct  %15  %109 %110 %111\nOpReturnValue %112\nOpFunctionEnd\n%114 = OpFunction  %2  None %62\n%113 = OpLabel\n%131 = OpVariable  %132  Function %66\n%133 = OpVariable  %134  Function %29\n%115 = OpAccessChain  %63  %34 %64\n%117 = OpAccessChain  %116  %37 %64\n%119 = OpAccessChain  %118  %40 %64\n%121 = OpAccessChain  %120  %43 %64\n%123 = OpAccessChain  %122  %46 %64\n%125 = OpAccessChain  %124  %49 %64\n%127 = OpAccessChain  %126  %52 %64\nOpBranch %135\n%135 = OpLabel\n%140 = OpLoad  %7  %138\n%141 = OpIEqual  %3  %140 %64\nOpSelectionMerge %142 None\nOpBranchConditional %141 %143 %142\n%143 = OpLabel\nOpStore %30 %136\nOpStore %32 %137\nOpBranch %142\n%142 = OpLabel\nOpControlBarrier %18 %18 %144\nOpBranch %145\n%145 = OpLabel\n%146 = OpFunctionCall  %2  %61\n%151 = OpAccessChain  %150  %127 %64 %64\n%152 = OpLoad  %26  %151\n%153 = OpFunctionCall  %20  %96 %152\n%157 = OpAccessChain  %156  %125 %64 %64 %64\n%158 = OpLoad  %12  %157\n%159 = OpMatrixTimesVector  %10  %153 %158\n%160 = OpCompositeExtract  %4  %159 0\n%162 = OpAccessChain  %147  %30 %161\nOpStore %162 %160\n%163 = OpLoad  %25  %123\n%164 = OpFunctionCall  %15  %105 %163\n%165 = OpLoad  %8  %121\n%166 = OpMatrixTimesVector  %10  %164 %165\n%167 = OpCompositeExtract  %4  %166 0\n%169 = OpAccessChain  %147  %30 %168\nOpStore %169 %167\n%172 = OpAccessChain  %77  %117 %171 %171\n%173 = OpLoad  %4  %172\n%175 = OpAccessChain  %147  %30 %174\nOpStore %175 %173\n%178 = OpAccessChain  %176  %119 %64 %177\n%179 = OpLoad  %4  %178\n%181 = OpAccessChain  %147  %30 %180\nOpStore %181 %179\n%182 = OpAccessChain  %77  %115 %171\n%183 = OpLoad  %4  %182\n%184 = OpAccessChain  %147  %30 %177\nOpStore %184 %183\n%185 = OpAccessChain  %77  %115 %64 %64\n%186 = OpLoad  %4  %185\n%187 = OpAccessChain  %147  %30 %18\nOpStore %187 %186\n%188 = OpAccessChain  %77  %115 %171\nOpStore %188 %130\n%189 = OpArrayLength  %7  %37 0\n%190 = OpConvertUToF  %4  %189\n%191 = OpAccessChain  %147  %30 %171\nOpStore %191 %190\nOpAtomicStore %32 %192 %64 %18\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-image.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 567\nOpCapability Shader\nOpCapability Image1D\nOpCapability Sampled1D\nOpCapability SampledCubeArray\nOpCapability ImageQuery\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %96 \"main\" %93\nOpEntryPoint GLCompute %197 \"depth_load\" %195\nOpEntryPoint Vertex %220 \"queries\" %218\nOpEntryPoint Vertex %272 \"levels_queries\" %271\nOpEntryPoint Fragment %303 \"texture_sample\" %302\nOpEntryPoint Fragment %461 \"texture_sample_comparison\" %459\nOpEntryPoint Fragment %516 \"gather\" %515\nOpEntryPoint Fragment %550 \"depth_no_comparison\" %549\nOpExecutionMode %96 LocalSize 16 1 1\nOpExecutionMode %197 LocalSize 16 1 1\nOpExecutionMode %303 OriginUpperLeft\nOpExecutionMode %461 OriginUpperLeft\nOpExecutionMode %516 OriginUpperLeft\nOpExecutionMode %550 OriginUpperLeft\n%3 = OpString \"image.wgsl\"\nOpSource Unknown 0 %3 \"@group(0) @binding(0)\nvar image_mipmapped_src: texture_2d<u32>;\n@group(0) @binding(3)\nvar image_multisampled_src: texture_multisampled_2d<u32>;\n@group(0) @binding(4)\nvar image_depth_multisampled_src: texture_depth_multisampled_2d;\n@group(0) @binding(1)\nvar image_storage_src: texture_storage_2d<rgba8uint, read>;\n@group(0) @binding(5)\nvar image_array_src: texture_2d_array<u32>;\n@group(0) @binding(6)\nvar image_dup_src: texture_storage_1d<r32uint,read>; // for #1307\n@group(0) @binding(7)\nvar image_1d_src: texture_1d<u32>;\n@group(0) @binding(2)\nvar image_dst: texture_storage_1d<r32uint,write>;\n\n@compute @workgroup_size(16)\nfn main(@builtin(local_invocation_id) local_id: vec3<u32>) {\n    let dim = textureDimensions(image_storage_src);\n    let itc = vec2<i32>(dim * local_id.xy) % vec2<i32>(10, 20);\n    // loads with ivec2 coords.\n    let value1 = textureLoad(image_mipmapped_src, itc, i32(local_id.z));\n    // doing the same thing as the line above, but with u32, as textureLoad must also support unsigned integers.\n    let value1_2 = textureLoad(image_mipmapped_src, itc, u32(local_id.z));\n    let value2 = textureLoad(image_multisampled_src, itc, i32(local_id.z));\n    let value3 = textureLoad(image_multisampled_src, itc, u32(local_id.z));\n    let value4 = textureLoad(image_storage_src, itc);\n    let value5 = textureLoad(image_array_src, itc, local_id.z, i32(local_id.z) + 1);\n    let value6 = textureLoad(image_array_src, itc, i32(local_id.z), i32(local_id.z) + 1);\n    let value7 = textureLoad(image_1d_src, i32(local_id.x), i32(local_id.z));\n    let value8 = textureLoad(image_dup_src, i32(local_id.x));\n    // loads with uvec2 coords.\n    let value1u = textureLoad(image_mipmapped_src, vec2<u32>(itc), i32(local_id.z));\n    let value2u = textureLoad(image_multisampled_src, vec2<u32>(itc), i32(local_id.z));\n    let value3u = textureLoad(image_multisampled_src, vec2<u32>(itc), u32(local_id.z));\n    let value4u = textureLoad(image_storage_src, vec2<u32>(itc));\n    let value5u = textureLoad(image_array_src, vec2<u32>(itc), local_id.z, i32(local_id.z) + 1);\n    let value6u = textureLoad(image_array_src, vec2<u32>(itc), i32(local_id.z), i32(local_id.z) + 1);\n    let value7u = textureLoad(image_1d_src, u32(local_id.x), i32(local_id.z));\n    // store with ivec2 coords.\n    textureStore(image_dst, itc.x, value1 + value2 + value4 + value5 + value6);\n    // store with uvec2 coords.\n    textureStore(image_dst, u32(itc.x), value1u + value2u + value4u + value5u + value6u);\n}\n\n@compute @workgroup_size(16, 1, 1)\nfn depth_load(@builtin(local_invocation_id) local_id: vec3<u32>) {\n    let dim: vec2<u32> = textureDimensions(image_storage_src);\n    let itc: vec2<i32> = (vec2<i32>(dim * local_id.xy) % vec2<i32>(10, 20));\n    let val: f32 = textureLoad(image_depth_multisampled_src, itc, i32(local_id.z));\n    textureStore(image_dst, itc.x, vec4<u32>(u32(val)));\n    return;\n}\n\n@group(0) @binding(0)\nvar image_1d: texture_1d<f32>;\n@group(0) @binding(1)\nvar image_2d: texture_2d<f32>;\n@group(0) @binding(2)\nvar image_2d_u32: texture_2d<u32>;\n@group(0) @binding(3)\nvar image_2d_i32: texture_2d<i32>;\n@group(0) @binding(4)\nvar image_2d_array: texture_2d_array<f32>;\n@group(0) @binding(5)\nvar image_cube: texture_cube<f32>;\n@group(0) @binding(6)\nvar image_cube_array: texture_cube_array<f32>;\n@group(0) @binding(7)\nvar image_3d: texture_3d<f32>;\n@group(0) @binding(8)\nvar image_aa: texture_multisampled_2d<f32>;\n\n@vertex\nfn queries() -> @builtin(position) vec4<f32> {\n    let dim_1d = textureDimensions(image_1d);\n    let dim_1d_lod = textureDimensions(image_1d, i32(dim_1d));\n    let dim_2d = textureDimensions(image_2d);\n    let dim_2d_lod = textureDimensions(image_2d, 1);\n    let dim_2d_array = textureDimensions(image_2d_array);\n    let dim_2d_array_lod = textureDimensions(image_2d_array, 1);\n    let dim_cube = textureDimensions(image_cube);\n    let dim_cube_lod = textureDimensions(image_cube, 1);\n    let dim_cube_array = textureDimensions(image_cube_array);\n    let dim_cube_array_lod = textureDimensions(image_cube_array, 1);\n    let dim_3d = textureDimensions(image_3d);\n    let dim_3d_lod = textureDimensions(image_3d, 1);\n    let dim_2s_ms = textureDimensions(image_aa);\n\n    let sum = dim_1d + dim_2d.y + dim_2d_lod.y + dim_2d_array.y + dim_2d_array_lod.y + \n        dim_cube.y + dim_cube_lod.y + dim_cube_array.y + dim_cube_array_lod.y +\n        dim_3d.z + dim_3d_lod.z;\n    return vec4<f32>(f32(sum));\n}\n\n@vertex\nfn levels_queries() -> @builtin(position) vec4<f32> {\n    let num_levels_2d = textureNumLevels(image_2d);\n    let num_layers_2d = textureNumLayers(image_2d_array);\n    let num_levels_2d_array = textureNumLevels(image_2d_array);\n    let num_layers_2d_array = textureNumLayers(image_2d_array);\n    let num_levels_cube = textureNumLevels(image_cube);\n    let num_levels_cube_array = textureNumLevels(image_cube_array);\n    let num_layers_cube = textureNumLayers(image_cube_array);\n    let num_levels_3d = textureNumLevels(image_3d);\n    let num_samples_aa = textureNumSamples(image_aa);\n\n    let sum = num_layers_2d + num_layers_cube + num_samples_aa +\n        num_levels_2d + num_levels_2d_array + num_levels_3d + num_levels_cube + num_levels_cube_array;\n    return vec4<f32>(f32(sum));\n}\n\n@group(1) @binding(0)\nvar sampler_reg: sampler;\n\n@fragment\nfn texture_sample() -> @location(0) vec4<f32> {\n    const tc = vec2<f32>(0.5);\n    const tc3 = vec3<f32>(0.5);\n    const offset = vec2<i32>(3, 1);\n    let level = 2.3;\n    var a: vec4<f32>;\n    a += textureSample(image_1d, sampler_reg, tc.x);\n    a += textureSample(image_2d, sampler_reg, tc);\n    a += textureSample(image_2d, sampler_reg, tc, vec2<i32>(3, 1));\n    a += textureSampleLevel(image_2d, sampler_reg, tc, level);\n    a += textureSampleLevel(image_2d, sampler_reg, tc, level, offset);\n    a += textureSampleBias(image_2d, sampler_reg, tc, 2.0, offset);\n    a += textureSampleBaseClampToEdge(image_2d, sampler_reg, tc);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0u);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0u, offset);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0u, level);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0u, level, offset);\n    a += textureSampleBias(image_2d_array, sampler_reg, tc, 0u, 2.0, offset);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0);\n    a += textureSample(image_2d_array, sampler_reg, tc, 0, offset);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0, level);\n    a += textureSampleLevel(image_2d_array, sampler_reg, tc, 0, level, offset);\n    a += textureSampleBias(image_2d_array, sampler_reg, tc, 0, 2.0, offset);\n    a += textureSample(image_cube_array, sampler_reg, tc3, 0u);\n    a += textureSampleLevel(image_cube_array, sampler_reg, tc3, 0u, level);\n    a += textureSampleBias(image_cube_array, sampler_reg, tc3, 0u, 2.0);\n    a += textureSample(image_cube_array, sampler_reg, tc3, 0);\n    a += textureSampleLevel(image_cube_array, sampler_reg, tc3, 0, level);\n    a += textureSampleBias(image_cube_array, sampler_reg, tc3, 0, 2.0);\n    return a;\n}\n\n@group(1) @binding(1)\nvar sampler_cmp: sampler_comparison;\n@group(1) @binding(2)\nvar image_2d_depth: texture_depth_2d;\n@group(1) @binding(3)\nvar image_2d_array_depth: texture_depth_2d_array;\n@group(1) @binding(4)\nvar image_cube_depth: texture_depth_cube;\n\n@fragment\nfn texture_sample_comparison() -> @location(0) f32 {\n    let tc = vec2<f32>(0.5);\n    let tc3 = vec3<f32>(0.5);\n    let dref = 0.5;\n    var a: f32;\n    a += textureSampleCompare(image_2d_depth, sampler_cmp, tc, dref);\n    a += textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0u, dref);\n    a += textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0, dref);\n    a += textureSampleCompare(image_cube_depth, sampler_cmp, tc3, dref);\n    a += textureSampleCompareLevel(image_2d_depth, sampler_cmp, tc, dref);\n    a += textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0u, dref);\n    a += textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0, dref);\n    a += textureSampleCompareLevel(image_cube_depth, sampler_cmp, tc3, dref);\n    return a;\n}\n\n@fragment\nfn gather() -> @location(0) vec4<f32> {\n    let tc = vec2<f32>(0.5);\n    let dref = 0.5;\n    let s2d = textureGather(1, image_2d, sampler_reg, tc);\n    let s2d_offset = textureGather(3, image_2d, sampler_reg, tc, vec2<i32>(3, 1));\n    let s2d_depth = textureGatherCompare(image_2d_depth, sampler_cmp, tc, dref);\n    let s2d_depth_offset = textureGatherCompare(image_2d_depth, sampler_cmp, tc, dref, vec2<i32>(3, 1));\n\n    let u = textureGather(0, image_2d_u32, sampler_reg, tc);\n    let i = textureGather(0, image_2d_i32, sampler_reg, tc);\n    let f = vec4<f32>(u) + vec4<f32>(i);\n\n    return s2d + s2d_offset + s2d_depth + s2d_depth_offset + f;\n}\n\n@fragment\nfn depth_no_comparison() -> @location(0) vec4<f32> {\n    let tc = vec2<f32>(0.5);\n    let level = 1;\n    let s2d = textureSample(image_2d_depth, sampler_reg, tc);\n    let s2d_gather = textureGather(image_2d_depth, sampler_reg, tc);\n    let s2d_level = textureSampleLevel(image_2d_depth, sampler_reg, tc, level);\n    return s2d + s2d_gather + s2d_level;\n}\n\"\nOpName %29 \"image_mipmapped_src\"\nOpName %31 \"image_multisampled_src\"\nOpName %33 \"image_depth_multisampled_src\"\nOpName %35 \"image_storage_src\"\nOpName %37 \"image_array_src\"\nOpName %39 \"image_dup_src\"\nOpName %41 \"image_1d_src\"\nOpName %43 \"image_dst\"\nOpName %44 \"image_1d\"\nOpName %46 \"image_2d\"\nOpName %48 \"image_2d_u32\"\nOpName %49 \"image_2d_i32\"\nOpName %51 \"image_2d_array\"\nOpName %53 \"image_cube\"\nOpName %55 \"image_cube_array\"\nOpName %57 \"image_3d\"\nOpName %59 \"image_aa\"\nOpName %61 \"sampler_reg\"\nOpName %63 \"sampler_cmp\"\nOpName %64 \"image_2d_depth\"\nOpName %66 \"image_2d_array_depth\"\nOpName %68 \"image_cube_depth\"\nOpName %70 \"naga_mod\"\nOpName %72 \"lhs\"\nOpName %73 \"rhs\"\nOpName %93 \"local_id\"\nOpName %96 \"main\"\nOpName %195 \"local_id\"\nOpName %197 \"depth_load\"\nOpName %220 \"queries\"\nOpName %272 \"levels_queries\"\nOpName %303 \"texture_sample\"\nOpName %318 \"a\"\nOpName %461 \"texture_sample_comparison\"\nOpName %466 \"a\"\nOpName %516 \"gather\"\nOpName %550 \"depth_no_comparison\"\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 0\nOpDecorate %31 DescriptorSet 0\nOpDecorate %31 Binding 3\nOpDecorate %33 DescriptorSet 0\nOpDecorate %33 Binding 4\nOpDecorate %35 NonWritable\nOpDecorate %35 DescriptorSet 0\nOpDecorate %35 Binding 1\nOpDecorate %37 DescriptorSet 0\nOpDecorate %37 Binding 5\nOpDecorate %39 NonWritable\nOpDecorate %39 DescriptorSet 0\nOpDecorate %39 Binding 6\nOpDecorate %41 DescriptorSet 0\nOpDecorate %41 Binding 7\nOpDecorate %43 NonReadable\nOpDecorate %43 DescriptorSet 0\nOpDecorate %43 Binding 2\nOpDecorate %44 DescriptorSet 0\nOpDecorate %44 Binding 0\nOpDecorate %46 DescriptorSet 0\nOpDecorate %46 Binding 1\nOpDecorate %48 DescriptorSet 0\nOpDecorate %48 Binding 2\nOpDecorate %49 DescriptorSet 0\nOpDecorate %49 Binding 3\nOpDecorate %51 DescriptorSet 0\nOpDecorate %51 Binding 4\nOpDecorate %53 DescriptorSet 0\nOpDecorate %53 Binding 5\nOpDecorate %55 DescriptorSet 0\nOpDecorate %55 Binding 6\nOpDecorate %57 DescriptorSet 0\nOpDecorate %57 Binding 7\nOpDecorate %59 DescriptorSet 0\nOpDecorate %59 Binding 8\nOpDecorate %61 DescriptorSet 1\nOpDecorate %61 Binding 0\nOpDecorate %63 DescriptorSet 1\nOpDecorate %63 Binding 1\nOpDecorate %64 DescriptorSet 1\nOpDecorate %64 Binding 2\nOpDecorate %66 DescriptorSet 1\nOpDecorate %66 Binding 3\nOpDecorate %68 DescriptorSet 1\nOpDecorate %68 Binding 4\nOpDecorate %93 BuiltIn LocalInvocationId\nOpDecorate %195 BuiltIn LocalInvocationId\nOpDecorate %218 BuiltIn Position\nOpDecorate %271 BuiltIn Position\nOpDecorate %302 Location 0\nOpDecorate %459 Location 0\nOpDecorate %515 Location 0\nOpDecorate %549 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeInt 32 0\n%4 = OpTypeImage %5 2D 0 0 0 1 Unknown\n%6 = OpTypeImage %5 2D 0 0 1 1 Unknown\n%8 = OpTypeFloat 32\n%7 = OpTypeImage %8 2D 1 0 1 1 Unknown\n%9 = OpTypeImage %5 2D 0 0 0 2 Rgba8ui\n%10 = OpTypeImage %5 2D 0 1 0 1 Unknown\n%11 = OpTypeImage %5 1D 0 0 0 2 R32ui\n%12 = OpTypeImage %5 1D 0 0 0 1 Unknown\n%13 = OpTypeVector %5 3\n%15 = OpTypeInt 32 1\n%14 = OpTypeVector %15 2\n%16 = OpTypeImage %8 1D 0 0 0 1 Unknown\n%17 = OpTypeImage %8 2D 0 0 0 1 Unknown\n%18 = OpTypeImage %15 2D 0 0 0 1 Unknown\n%19 = OpTypeImage %8 2D 0 1 0 1 Unknown\n%20 = OpTypeImage %8 Cube 0 0 0 1 Unknown\n%21 = OpTypeImage %8 Cube 0 1 0 1 Unknown\n%22 = OpTypeImage %8 3D 0 0 0 1 Unknown\n%23 = OpTypeImage %8 2D 0 0 1 1 Unknown\n%24 = OpTypeVector %8 4\n%25 = OpTypeSampler\n%26 = OpTypeImage %8 2D 1 0 0 1 Unknown\n%27 = OpTypeImage %8 2D 1 1 0 1 Unknown\n%28 = OpTypeImage %8 Cube 1 0 0 1 Unknown\n%30 = OpTypePointer UniformConstant %4\n%29 = OpVariable  %30  UniformConstant\n%32 = OpTypePointer UniformConstant %6\n%31 = OpVariable  %32  UniformConstant\n%34 = OpTypePointer UniformConstant %7\n%33 = OpVariable  %34  UniformConstant\n%36 = OpTypePointer UniformConstant %9\n%35 = OpVariable  %36  UniformConstant\n%38 = OpTypePointer UniformConstant %10\n%37 = OpVariable  %38  UniformConstant\n%40 = OpTypePointer UniformConstant %11\n%39 = OpVariable  %40  UniformConstant\n%42 = OpTypePointer UniformConstant %12\n%41 = OpVariable  %42  UniformConstant\n%43 = OpVariable  %40  UniformConstant\n%45 = OpTypePointer UniformConstant %16\n%44 = OpVariable  %45  UniformConstant\n%47 = OpTypePointer UniformConstant %17\n%46 = OpVariable  %47  UniformConstant\n%48 = OpVariable  %30  UniformConstant\n%50 = OpTypePointer UniformConstant %18\n%49 = OpVariable  %50  UniformConstant\n%52 = OpTypePointer UniformConstant %19\n%51 = OpVariable  %52  UniformConstant\n%54 = OpTypePointer UniformConstant %20\n%53 = OpVariable  %54  UniformConstant\n%56 = OpTypePointer UniformConstant %21\n%55 = OpVariable  %56  UniformConstant\n%58 = OpTypePointer UniformConstant %22\n%57 = OpVariable  %58  UniformConstant\n%60 = OpTypePointer UniformConstant %23\n%59 = OpVariable  %60  UniformConstant\n%62 = OpTypePointer UniformConstant %25\n%61 = OpVariable  %62  UniformConstant\n%63 = OpVariable  %62  UniformConstant\n%65 = OpTypePointer UniformConstant %26\n%64 = OpVariable  %65  UniformConstant\n%67 = OpTypePointer UniformConstant %27\n%66 = OpVariable  %67  UniformConstant\n%69 = OpTypePointer UniformConstant %28\n%68 = OpVariable  %69  UniformConstant\n%71 = OpTypeFunction %14 %14 %14\n%76 = OpTypeBool\n%75 = OpTypeVector %76 2\n%77 = OpConstant  %15  0\n%78 = OpConstantComposite  %14  %77 %77\n%80 = OpConstant  %15  -2147483648\n%81 = OpConstant  %15  -1\n%82 = OpConstantComposite  %14  %80 %80\n%83 = OpConstantComposite  %14  %81 %81\n%88 = OpConstant  %15  1\n%89 = OpConstantComposite  %14  %88 %88\n%94 = OpTypePointer Input %13\n%93 = OpVariable  %94  Input\n%97 = OpTypeFunction %2\n%105 = OpConstant  %15  10\n%106 = OpConstant  %15  20\n%107 = OpConstantComposite  %14  %105 %106\n%109 = OpTypeVector %5 2\n%117 = OpTypeVector %5 4\n%132 = OpTypeVector %15 3\n%195 = OpVariable  %94  Input\n%212 = OpConstant  %8  0\n%213 = OpConstant  %8  4294967000\n%219 = OpTypePointer Output %24\n%218 = OpVariable  %219  Output\n%229 = OpConstant  %5  0\n%271 = OpVariable  %219  Output\n%302 = OpVariable  %219  Output\n%309 = OpConstant  %8  0.5\n%310 = OpTypeVector %8 2\n%311 = OpConstantComposite  %310  %309 %309\n%312 = OpTypeVector %8 3\n%313 = OpConstantComposite  %312  %309 %309 %309\n%314 = OpConstant  %15  3\n%315 = OpConstantComposite  %14  %314 %88\n%316 = OpConstant  %8  2.3\n%317 = OpConstant  %8  2\n%319 = OpTypePointer Function %24\n%320 = OpConstantNull  %24\n%323 = OpTypeSampledImage %16\n%328 = OpTypeSampledImage %17\n%352 = OpConstant  %8  1\n%353 = OpConstantComposite  %310  %352 %352\n%360 = OpTypeSampledImage %19\n%421 = OpTypeSampledImage %21\n%460 = OpTypePointer Output %8\n%459 = OpVariable  %460  Output\n%467 = OpTypePointer Function %8\n%468 = OpConstantNull  %8\n%471 = OpTypeSampledImage %26\n%476 = OpTypeSampledImage %27\n%489 = OpTypeSampledImage %28\n%515 = OpVariable  %219  Output\n%526 = OpConstant  %5  1\n%529 = OpConstant  %5  3\n%534 = OpTypeSampledImage %4\n%537 = OpTypeVector %15 4\n%538 = OpTypeSampledImage %18\n%549 = OpVariable  %219  Output\n%70 = OpFunction  %14  None %71\n%72 = OpFunctionParameter  %14\n%73 = OpFunctionParameter  %14\n%74 = OpLabel\n%79 = OpIEqual  %75  %73 %78\n%84 = OpIEqual  %75  %72 %82\n%85 = OpIEqual  %75  %73 %83\n%86 = OpLogicalAnd  %75  %84 %85\n%87 = OpLogicalOr  %75  %79 %86\n%90 = OpSelect  %14  %87 %89 %73\n%91 = OpSRem  %14  %72 %90\nOpReturnValue %91\nOpFunctionEnd\n%96 = OpFunction  %2  None %97\n%92 = OpLabel\n%95 = OpLoad  %13  %93\n%98 = OpLoad  %4  %29\n%99 = OpLoad  %6  %31\n%100 = OpLoad  %9  %35\n%101 = OpLoad  %10  %37\n%102 = OpLoad  %11  %39\n%103 = OpLoad  %12  %41\n%104 = OpLoad  %11  %43\nOpBranch %108\n%108 = OpLabel\nOpLine %3 20 15\n%110 = OpImageQuerySize  %109  %100\nOpLine %3 21 15\n%111 = OpVectorShuffle  %109  %95 %95 0 1\n%112 = OpIMul  %109  %110 %111\n%113 = OpBitcast  %14  %112\nOpLine %3 21 15\n%114 = OpFunctionCall  %14  %70 %113 %107\nOpLine %3 23 18\n%115 = OpCompositeExtract  %5  %95 2\n%116 = OpBitcast  %15  %115\n%118 = OpImageFetch  %117  %98 %114 Lod %116\nOpLine %3 25 20\n%119 = OpCompositeExtract  %5  %95 2\n%120 = OpImageFetch  %117  %98 %114 Lod %119\nOpLine %3 26 18\n%121 = OpCompositeExtract  %5  %95 2\n%122 = OpBitcast  %15  %121\n%123 = OpImageFetch  %117  %99 %114 Sample %122\nOpLine %3 27 18\n%124 = OpCompositeExtract  %5  %95 2\n%125 = OpImageFetch  %117  %99 %114 Sample %124\nOpLine %3 28 18\n%126 = OpImageRead  %117  %100 %114\nOpLine %3 29 52\n%127 = OpCompositeExtract  %5  %95 2\n%128 = OpCompositeExtract  %5  %95 2\n%129 = OpBitcast  %15  %128\nOpLine %3 29 18\n%130 = OpIAdd  %15  %129 %88\n%131 = OpBitcast  %15  %127\n%133 = OpCompositeConstruct  %132  %114 %131\n%134 = OpImageFetch  %117  %101 %133 Lod %130\nOpLine %3 30 52\n%135 = OpCompositeExtract  %5  %95 2\n%136 = OpBitcast  %15  %135\n%137 = OpCompositeExtract  %5  %95 2\n%138 = OpBitcast  %15  %137\nOpLine %3 30 18\n%139 = OpIAdd  %15  %138 %88\n%140 = OpCompositeConstruct  %132  %114 %136\n%141 = OpImageFetch  %117  %101 %140 Lod %139\nOpLine %3 31 18\n%142 = OpCompositeExtract  %5  %95 0\n%143 = OpBitcast  %15  %142\n%144 = OpCompositeExtract  %5  %95 2\n%145 = OpBitcast  %15  %144\n%146 = OpImageFetch  %117  %103 %143 Lod %145\nOpLine %3 32 18\n%147 = OpCompositeExtract  %5  %95 0\n%148 = OpBitcast  %15  %147\n%149 = OpImageRead  %117  %102 %148\nOpLine %3 34 19\n%150 = OpBitcast  %109  %114\n%151 = OpCompositeExtract  %5  %95 2\n%152 = OpBitcast  %15  %151\n%153 = OpImageFetch  %117  %98 %150 Lod %152\nOpLine %3 35 19\n%154 = OpBitcast  %109  %114\n%155 = OpCompositeExtract  %5  %95 2\n%156 = OpBitcast  %15  %155\n%157 = OpImageFetch  %117  %99 %154 Sample %156\nOpLine %3 36 19\n%158 = OpBitcast  %109  %114\n%159 = OpCompositeExtract  %5  %95 2\n%160 = OpImageFetch  %117  %99 %158 Sample %159\nOpLine %3 37 19\n%161 = OpBitcast  %109  %114\n%162 = OpImageRead  %117  %100 %161\nOpLine %3 38 48\n%163 = OpBitcast  %109  %114\n%164 = OpCompositeExtract  %5  %95 2\n%165 = OpCompositeExtract  %5  %95 2\n%166 = OpBitcast  %15  %165\nOpLine %3 38 19\n%167 = OpIAdd  %15  %166 %88\n%168 = OpCompositeConstruct  %13  %163 %164\n%169 = OpImageFetch  %117  %101 %168 Lod %167\nOpLine %3 39 48\n%170 = OpBitcast  %109  %114\n%171 = OpCompositeExtract  %5  %95 2\n%172 = OpBitcast  %15  %171\n%173 = OpCompositeExtract  %5  %95 2\n%174 = OpBitcast  %15  %173\nOpLine %3 39 19\n%175 = OpIAdd  %15  %174 %88\n%176 = OpBitcast  %5  %172\n%177 = OpCompositeConstruct  %13  %170 %176\n%178 = OpImageFetch  %117  %101 %177 Lod %175\nOpLine %3 40 19\n%179 = OpCompositeExtract  %5  %95 0\n%180 = OpCompositeExtract  %5  %95 2\n%181 = OpBitcast  %15  %180\n%182 = OpImageFetch  %117  %103 %179 Lod %181\nOpLine %3 42 29\n%183 = OpCompositeExtract  %15  %114 0\n%184 = OpIAdd  %117  %118 %123\n%185 = OpIAdd  %117  %184 %126\n%186 = OpIAdd  %117  %185 %134\n%187 = OpIAdd  %117  %186 %141\nOpLine %3 42 5\nOpImageWrite %104 %183 %187\nOpLine %3 44 29\n%188 = OpCompositeExtract  %15  %114 0\n%189 = OpBitcast  %5  %188\n%190 = OpIAdd  %117  %153 %157\n%191 = OpIAdd  %117  %190 %162\n%192 = OpIAdd  %117  %191 %169\n%193 = OpIAdd  %117  %192 %178\nOpLine %3 44 5\nOpImageWrite %104 %189 %193\nOpReturn\nOpFunctionEnd\n%197 = OpFunction  %2  None %97\n%194 = OpLabel\n%196 = OpLoad  %13  %195\n%198 = OpLoad  %7  %33\n%199 = OpLoad  %9  %35\n%200 = OpLoad  %11  %43\nOpBranch %201\n%201 = OpLabel\nOpLine %3 49 26\n%202 = OpImageQuerySize  %109  %199\nOpLine %3 50 27\n%203 = OpVectorShuffle  %109  %196 %196 0 1\n%204 = OpIMul  %109  %202 %203\n%205 = OpBitcast  %14  %204\nOpLine %3 50 27\n%206 = OpFunctionCall  %14  %70 %205 %107\nOpLine %3 51 20\n%207 = OpCompositeExtract  %5  %196 2\n%208 = OpBitcast  %15  %207\n%209 = OpImageFetch  %24  %198 %206 Sample %208\n%210 = OpCompositeExtract  %8  %209 0\nOpLine %3 52 29\n%211 = OpCompositeExtract  %15  %206 0\n%214 = OpExtInst  %8  %1 FClamp %210 %212 %213\n%215 = OpConvertFToU  %5  %214\n%216 = OpCompositeConstruct  %117  %215 %215 %215 %215\nOpLine %3 52 5\nOpImageWrite %200 %211 %216\nOpReturn\nOpFunctionEnd\n%220 = OpFunction  %2  None %97\n%217 = OpLabel\n%221 = OpLoad  %16  %44\n%222 = OpLoad  %17  %46\n%223 = OpLoad  %19  %51\n%224 = OpLoad  %20  %53\n%225 = OpLoad  %21  %55\n%226 = OpLoad  %22  %57\n%227 = OpLoad  %23  %59\nOpBranch %228\n%228 = OpLabel\nOpLine %3 77 18\n%230 = OpImageQuerySizeLod  %5  %221 %229\nOpLine %3 78 22\n%231 = OpBitcast  %15  %230\n%232 = OpImageQuerySizeLod  %5  %221 %231\nOpLine %3 79 18\n%233 = OpImageQuerySizeLod  %109  %222 %229\nOpLine %3 80 22\n%234 = OpImageQuerySizeLod  %109  %222 %88\nOpLine %3 81 24\n%235 = OpImageQuerySizeLod  %13  %223 %229\n%236 = OpVectorShuffle  %109  %235 %235 0 1\nOpLine %3 82 28\n%237 = OpImageQuerySizeLod  %13  %223 %88\n%238 = OpVectorShuffle  %109  %237 %237 0 1\nOpLine %3 83 20\n%239 = OpImageQuerySizeLod  %109  %224 %229\nOpLine %3 84 24\n%240 = OpImageQuerySizeLod  %109  %224 %88\nOpLine %3 85 26\n%241 = OpImageQuerySizeLod  %13  %225 %229\n%242 = OpVectorShuffle  %109  %241 %241 0 0\nOpLine %3 86 30\n%243 = OpImageQuerySizeLod  %13  %225 %88\n%244 = OpVectorShuffle  %109  %243 %243 0 0\nOpLine %3 87 18\n%245 = OpImageQuerySizeLod  %13  %226 %229\nOpLine %3 88 22\n%246 = OpImageQuerySizeLod  %13  %226 %88\nOpLine %3 89 21\n%247 = OpImageQuerySize  %109  %227\nOpLine %3 91 15\n%248 = OpCompositeExtract  %5  %233 1\n%249 = OpIAdd  %5  %230 %248\n%250 = OpCompositeExtract  %5  %234 1\n%251 = OpIAdd  %5  %249 %250\n%252 = OpCompositeExtract  %5  %236 1\n%253 = OpIAdd  %5  %251 %252\n%254 = OpCompositeExtract  %5  %238 1\n%255 = OpIAdd  %5  %253 %254\n%256 = OpCompositeExtract  %5  %239 1\n%257 = OpIAdd  %5  %255 %256\n%258 = OpCompositeExtract  %5  %240 1\n%259 = OpIAdd  %5  %257 %258\n%260 = OpCompositeExtract  %5  %242 1\n%261 = OpIAdd  %5  %259 %260\n%262 = OpCompositeExtract  %5  %244 1\n%263 = OpIAdd  %5  %261 %262\n%264 = OpCompositeExtract  %5  %245 2\n%265 = OpIAdd  %5  %263 %264\n%266 = OpCompositeExtract  %5  %246 2\n%267 = OpIAdd  %5  %265 %266\nOpLine %3 94 12\n%268 = OpConvertUToF  %8  %267\n%269 = OpCompositeConstruct  %24  %268 %268 %268 %268\nOpStore %218 %269\nOpReturn\nOpFunctionEnd\n%272 = OpFunction  %2  None %97\n%270 = OpLabel\n%273 = OpLoad  %17  %46\n%274 = OpLoad  %19  %51\n%275 = OpLoad  %20  %53\n%276 = OpLoad  %21  %55\n%277 = OpLoad  %22  %57\n%278 = OpLoad  %23  %59\nOpBranch %279\n%279 = OpLabel\nOpLine %3 99 25\n%280 = OpImageQueryLevels  %5  %273\nOpLine %3 100 25\n%281 = OpImageQuerySizeLod  %13  %274 %229\n%282 = OpCompositeExtract  %5  %281 2\nOpLine %3 101 31\n%283 = OpImageQueryLevels  %5  %274\nOpLine %3 102 31\n%284 = OpImageQuerySizeLod  %13  %274 %229\n%285 = OpCompositeExtract  %5  %284 2\nOpLine %3 103 27\n%286 = OpImageQueryLevels  %5  %275\nOpLine %3 104 33\n%287 = OpImageQueryLevels  %5  %276\nOpLine %3 105 27\n%288 = OpImageQuerySizeLod  %13  %276 %229\n%289 = OpCompositeExtract  %5  %288 2\nOpLine %3 106 25\n%290 = OpImageQueryLevels  %5  %277\nOpLine %3 107 26\n%291 = OpImageQuerySamples  %5  %278\nOpLine %3 109 15\n%292 = OpIAdd  %5  %282 %289\n%293 = OpIAdd  %5  %292 %291\n%294 = OpIAdd  %5  %293 %280\n%295 = OpIAdd  %5  %294 %283\n%296 = OpIAdd  %5  %295 %290\n%297 = OpIAdd  %5  %296 %286\n%298 = OpIAdd  %5  %297 %287\nOpLine %3 111 12\n%299 = OpConvertUToF  %8  %298\n%300 = OpCompositeConstruct  %24  %299 %299 %299 %299\nOpStore %271 %300\nOpReturn\nOpFunctionEnd\n%303 = OpFunction  %2  None %97\n%301 = OpLabel\n%318 = OpVariable  %319  Function %320\n%304 = OpLoad  %16  %44\n%305 = OpLoad  %17  %46\n%306 = OpLoad  %19  %51\n%307 = OpLoad  %21  %55\n%308 = OpLoad  %25  %61\nOpBranch %321\n%321 = OpLabel\nOpLine %3 119 16\nOpLine %3 120 17\nOpLine %3 121 20\nOpLine %3 1 1\n%322 = OpLoad  %24  %318\nOpLine %3 124 5\n%324 = OpSampledImage  %323  %304 %308\n%325 = OpImageSampleImplicitLod  %24  %324 %309\n%326 = OpFAdd  %24  %322 %325\nOpLine %3 124 5\nOpStore %318 %326\nOpLine %3 1 1\n%327 = OpLoad  %24  %318\nOpLine %3 125 5\n%329 = OpSampledImage  %328  %305 %308\n%330 = OpImageSampleImplicitLod  %24  %329 %311\n%331 = OpFAdd  %24  %327 %330\nOpLine %3 125 5\nOpStore %318 %331\nOpLine %3 1 1\n%332 = OpLoad  %24  %318\nOpLine %3 126 5\n%333 = OpSampledImage  %328  %305 %308\n%334 = OpImageSampleImplicitLod  %24  %333 %311 ConstOffset %315\n%335 = OpFAdd  %24  %332 %334\nOpLine %3 126 5\nOpStore %318 %335\nOpLine %3 1 1\n%336 = OpLoad  %24  %318\nOpLine %3 127 5\n%337 = OpSampledImage  %328  %305 %308\n%338 = OpImageSampleExplicitLod  %24  %337 %311 Lod %316\n%339 = OpFAdd  %24  %336 %338\nOpLine %3 127 5\nOpStore %318 %339\nOpLine %3 1 1\n%340 = OpLoad  %24  %318\nOpLine %3 128 5\n%341 = OpSampledImage  %328  %305 %308\n%342 = OpImageSampleExplicitLod  %24  %341 %311 Lod|ConstOffset %316 %315\n%343 = OpFAdd  %24  %340 %342\nOpLine %3 128 5\nOpStore %318 %343\nOpLine %3 1 1\n%344 = OpLoad  %24  %318\nOpLine %3 129 5\n%345 = OpSampledImage  %328  %305 %308\n%346 = OpImageSampleImplicitLod  %24  %345 %311 Bias|ConstOffset %317 %315\n%347 = OpFAdd  %24  %344 %346\nOpLine %3 129 5\nOpStore %318 %347\nOpLine %3 1 1\n%348 = OpLoad  %24  %318\nOpLine %3 130 5\n%349 = OpImageQuerySizeLod  %109  %305 %229\n%350 = OpConvertUToF  %310  %349\n%351 = OpFDiv  %310  %311 %350\n%354 = OpFSub  %310  %353 %351\n%355 = OpExtInst  %310  %1 NClamp %311 %351 %354\n%356 = OpSampledImage  %328  %305 %308\n%357 = OpImageSampleExplicitLod  %24  %356 %355 Lod %212\n%358 = OpFAdd  %24  %348 %357\nOpLine %3 130 5\nOpStore %318 %358\nOpLine %3 1 1\n%359 = OpLoad  %24  %318\nOpLine %3 131 5\n%361 = OpConvertUToF  %8  %229\n%362 = OpCompositeConstruct  %312  %311 %361\n%363 = OpSampledImage  %360  %306 %308\n%364 = OpImageSampleImplicitLod  %24  %363 %362\n%365 = OpFAdd  %24  %359 %364\nOpLine %3 131 5\nOpStore %318 %365\nOpLine %3 1 1\n%366 = OpLoad  %24  %318\nOpLine %3 132 5\n%367 = OpConvertUToF  %8  %229\n%368 = OpCompositeConstruct  %312  %311 %367\n%369 = OpSampledImage  %360  %306 %308\n%370 = OpImageSampleImplicitLod  %24  %369 %368 ConstOffset %315\n%371 = OpFAdd  %24  %366 %370\nOpLine %3 132 5\nOpStore %318 %371\nOpLine %3 1 1\n%372 = OpLoad  %24  %318\nOpLine %3 133 5\n%373 = OpConvertUToF  %8  %229\n%374 = OpCompositeConstruct  %312  %311 %373\n%375 = OpSampledImage  %360  %306 %308\n%376 = OpImageSampleExplicitLod  %24  %375 %374 Lod %316\n%377 = OpFAdd  %24  %372 %376\nOpLine %3 133 5\nOpStore %318 %377\nOpLine %3 1 1\n%378 = OpLoad  %24  %318\nOpLine %3 134 5\n%379 = OpConvertUToF  %8  %229\n%380 = OpCompositeConstruct  %312  %311 %379\n%381 = OpSampledImage  %360  %306 %308\n%382 = OpImageSampleExplicitLod  %24  %381 %380 Lod|ConstOffset %316 %315\n%383 = OpFAdd  %24  %378 %382\nOpLine %3 134 5\nOpStore %318 %383\nOpLine %3 1 1\n%384 = OpLoad  %24  %318\nOpLine %3 135 5\n%385 = OpConvertUToF  %8  %229\n%386 = OpCompositeConstruct  %312  %311 %385\n%387 = OpSampledImage  %360  %306 %308\n%388 = OpImageSampleImplicitLod  %24  %387 %386 Bias|ConstOffset %317 %315\n%389 = OpFAdd  %24  %384 %388\nOpLine %3 135 5\nOpStore %318 %389\nOpLine %3 1 1\n%390 = OpLoad  %24  %318\nOpLine %3 136 5\n%391 = OpConvertSToF  %8  %77\n%392 = OpCompositeConstruct  %312  %311 %391\n%393 = OpSampledImage  %360  %306 %308\n%394 = OpImageSampleImplicitLod  %24  %393 %392\n%395 = OpFAdd  %24  %390 %394\nOpLine %3 136 5\nOpStore %318 %395\nOpLine %3 1 1\n%396 = OpLoad  %24  %318\nOpLine %3 137 5\n%397 = OpConvertSToF  %8  %77\n%398 = OpCompositeConstruct  %312  %311 %397\n%399 = OpSampledImage  %360  %306 %308\n%400 = OpImageSampleImplicitLod  %24  %399 %398 ConstOffset %315\n%401 = OpFAdd  %24  %396 %400\nOpLine %3 137 5\nOpStore %318 %401\nOpLine %3 1 1\n%402 = OpLoad  %24  %318\nOpLine %3 138 5\n%403 = OpConvertSToF  %8  %77\n%404 = OpCompositeConstruct  %312  %311 %403\n%405 = OpSampledImage  %360  %306 %308\n%406 = OpImageSampleExplicitLod  %24  %405 %404 Lod %316\n%407 = OpFAdd  %24  %402 %406\nOpLine %3 138 5\nOpStore %318 %407\nOpLine %3 1 1\n%408 = OpLoad  %24  %318\nOpLine %3 139 5\n%409 = OpConvertSToF  %8  %77\n%410 = OpCompositeConstruct  %312  %311 %409\n%411 = OpSampledImage  %360  %306 %308\n%412 = OpImageSampleExplicitLod  %24  %411 %410 Lod|ConstOffset %316 %315\n%413 = OpFAdd  %24  %408 %412\nOpLine %3 139 5\nOpStore %318 %413\nOpLine %3 1 1\n%414 = OpLoad  %24  %318\nOpLine %3 140 5\n%415 = OpConvertSToF  %8  %77\n%416 = OpCompositeConstruct  %312  %311 %415\n%417 = OpSampledImage  %360  %306 %308\n%418 = OpImageSampleImplicitLod  %24  %417 %416 Bias|ConstOffset %317 %315\n%419 = OpFAdd  %24  %414 %418\nOpLine %3 140 5\nOpStore %318 %419\nOpLine %3 1 1\n%420 = OpLoad  %24  %318\nOpLine %3 141 5\n%422 = OpConvertUToF  %8  %229\n%423 = OpCompositeConstruct  %24  %313 %422\n%424 = OpSampledImage  %421  %307 %308\n%425 = OpImageSampleImplicitLod  %24  %424 %423\n%426 = OpFAdd  %24  %420 %425\nOpLine %3 141 5\nOpStore %318 %426\nOpLine %3 1 1\n%427 = OpLoad  %24  %318\nOpLine %3 142 5\n%428 = OpConvertUToF  %8  %229\n%429 = OpCompositeConstruct  %24  %313 %428\n%430 = OpSampledImage  %421  %307 %308\n%431 = OpImageSampleExplicitLod  %24  %430 %429 Lod %316\n%432 = OpFAdd  %24  %427 %431\nOpLine %3 142 5\nOpStore %318 %432\nOpLine %3 1 1\n%433 = OpLoad  %24  %318\nOpLine %3 143 5\n%434 = OpConvertUToF  %8  %229\n%435 = OpCompositeConstruct  %24  %313 %434\n%436 = OpSampledImage  %421  %307 %308\n%437 = OpImageSampleImplicitLod  %24  %436 %435 Bias %317\n%438 = OpFAdd  %24  %433 %437\nOpLine %3 143 5\nOpStore %318 %438\nOpLine %3 1 1\n%439 = OpLoad  %24  %318\nOpLine %3 144 5\n%440 = OpConvertSToF  %8  %77\n%441 = OpCompositeConstruct  %24  %313 %440\n%442 = OpSampledImage  %421  %307 %308\n%443 = OpImageSampleImplicitLod  %24  %442 %441\n%444 = OpFAdd  %24  %439 %443\nOpLine %3 144 5\nOpStore %318 %444\nOpLine %3 1 1\n%445 = OpLoad  %24  %318\nOpLine %3 145 5\n%446 = OpConvertSToF  %8  %77\n%447 = OpCompositeConstruct  %24  %313 %446\n%448 = OpSampledImage  %421  %307 %308\n%449 = OpImageSampleExplicitLod  %24  %448 %447 Lod %316\n%450 = OpFAdd  %24  %445 %449\nOpLine %3 145 5\nOpStore %318 %450\nOpLine %3 1 1\n%451 = OpLoad  %24  %318\nOpLine %3 146 5\n%452 = OpConvertSToF  %8  %77\n%453 = OpCompositeConstruct  %24  %313 %452\n%454 = OpSampledImage  %421  %307 %308\n%455 = OpImageSampleImplicitLod  %24  %454 %453 Bias %317\n%456 = OpFAdd  %24  %451 %455\nOpLine %3 146 5\nOpStore %318 %456\nOpLine %3 1 1\n%457 = OpLoad  %24  %318\nOpStore %302 %457\nOpReturn\nOpFunctionEnd\n%461 = OpFunction  %2  None %97\n%458 = OpLabel\n%466 = OpVariable  %467  Function %468\n%462 = OpLoad  %25  %63\n%463 = OpLoad  %26  %64\n%464 = OpLoad  %27  %66\n%465 = OpLoad  %28  %68\nOpBranch %469\n%469 = OpLabel\nOpLine %3 161 14\nOpLine %3 162 15\nOpLine %3 1 1\n%470 = OpLoad  %8  %466\nOpLine %3 165 5\n%472 = OpSampledImage  %471  %463 %462\n%473 = OpImageSampleDrefImplicitLod  %8  %472 %311 %309\n%474 = OpFAdd  %8  %470 %473\nOpLine %3 165 5\nOpStore %466 %474\nOpLine %3 1 1\n%475 = OpLoad  %8  %466\nOpLine %3 166 5\n%477 = OpConvertUToF  %8  %229\n%478 = OpCompositeConstruct  %312  %311 %477\n%479 = OpSampledImage  %476  %464 %462\n%480 = OpImageSampleDrefImplicitLod  %8  %479 %478 %309\n%481 = OpFAdd  %8  %475 %480\nOpLine %3 166 5\nOpStore %466 %481\nOpLine %3 1 1\n%482 = OpLoad  %8  %466\nOpLine %3 167 5\n%483 = OpConvertSToF  %8  %77\n%484 = OpCompositeConstruct  %312  %311 %483\n%485 = OpSampledImage  %476  %464 %462\n%486 = OpImageSampleDrefImplicitLod  %8  %485 %484 %309\n%487 = OpFAdd  %8  %482 %486\nOpLine %3 167 5\nOpStore %466 %487\nOpLine %3 1 1\n%488 = OpLoad  %8  %466\nOpLine %3 168 5\n%490 = OpSampledImage  %489  %465 %462\n%491 = OpImageSampleDrefImplicitLod  %8  %490 %313 %309\n%492 = OpFAdd  %8  %488 %491\nOpLine %3 168 5\nOpStore %466 %492\nOpLine %3 1 1\n%493 = OpLoad  %8  %466\nOpLine %3 169 5\n%494 = OpSampledImage  %471  %463 %462\n%495 = OpImageSampleDrefExplicitLod  %8  %494 %311 %309 Lod %212\n%496 = OpFAdd  %8  %493 %495\nOpLine %3 169 5\nOpStore %466 %496\nOpLine %3 1 1\n%497 = OpLoad  %8  %466\nOpLine %3 170 5\n%498 = OpConvertUToF  %8  %229\n%499 = OpCompositeConstruct  %312  %311 %498\n%500 = OpSampledImage  %476  %464 %462\n%501 = OpImageSampleDrefExplicitLod  %8  %500 %499 %309 Lod %212\n%502 = OpFAdd  %8  %497 %501\nOpLine %3 170 5\nOpStore %466 %502\nOpLine %3 1 1\n%503 = OpLoad  %8  %466\nOpLine %3 171 5\n%504 = OpConvertSToF  %8  %77\n%505 = OpCompositeConstruct  %312  %311 %504\n%506 = OpSampledImage  %476  %464 %462\n%507 = OpImageSampleDrefExplicitLod  %8  %506 %505 %309 Lod %212\n%508 = OpFAdd  %8  %503 %507\nOpLine %3 171 5\nOpStore %466 %508\nOpLine %3 1 1\n%509 = OpLoad  %8  %466\nOpLine %3 172 5\n%510 = OpSampledImage  %489  %465 %462\n%511 = OpImageSampleDrefExplicitLod  %8  %510 %313 %309 Lod %212\n%512 = OpFAdd  %8  %509 %511\nOpLine %3 172 5\nOpStore %466 %512\nOpLine %3 1 1\n%513 = OpLoad  %8  %466\nOpStore %459 %513\nOpReturn\nOpFunctionEnd\n%516 = OpFunction  %2  None %97\n%514 = OpLabel\n%517 = OpLoad  %17  %46\n%518 = OpLoad  %4  %48\n%519 = OpLoad  %18  %49\n%520 = OpLoad  %25  %61\n%521 = OpLoad  %25  %63\n%522 = OpLoad  %26  %64\nOpBranch %523\n%523 = OpLabel\nOpLine %3 178 14\nOpLine %3 180 15\n%524 = OpSampledImage  %328  %517 %520\n%525 = OpImageGather  %24  %524 %311 %526\nOpLine %3 181 22\n%527 = OpSampledImage  %328  %517 %520\n%528 = OpImageGather  %24  %527 %311 %529 ConstOffset %315\nOpLine %3 182 21\n%530 = OpSampledImage  %471  %522 %521\n%531 = OpImageDrefGather  %24  %530 %311 %309\nOpLine %3 183 28\n%532 = OpSampledImage  %471  %522 %521\n%533 = OpImageDrefGather  %24  %532 %311 %309 ConstOffset %315\nOpLine %3 185 13\n%535 = OpSampledImage  %534  %518 %520\n%536 = OpImageGather  %117  %535 %311 %229\nOpLine %3 186 13\n%539 = OpSampledImage  %538  %519 %520\n%540 = OpImageGather  %537  %539 %311 %229\nOpLine %3 187 13\n%541 = OpConvertUToF  %24  %536\n%542 = OpConvertSToF  %24  %540\n%543 = OpFAdd  %24  %541 %542\nOpLine %3 189 12\n%544 = OpFAdd  %24  %525 %528\n%545 = OpFAdd  %24  %544 %531\n%546 = OpFAdd  %24  %545 %533\n%547 = OpFAdd  %24  %546 %543\nOpStore %515 %547\nOpReturn\nOpFunctionEnd\n%550 = OpFunction  %2  None %97\n%548 = OpLabel\n%551 = OpLoad  %25  %61\n%552 = OpLoad  %26  %64\nOpBranch %553\n%553 = OpLabel\nOpLine %3 194 14\nOpLine %3 196 15\n%554 = OpSampledImage  %471  %552 %551\n%555 = OpImageSampleImplicitLod  %24  %554 %311\n%556 = OpCompositeExtract  %8  %555 0\nOpLine %3 197 22\n%557 = OpSampledImage  %471  %552 %551\n%558 = OpImageGather  %24  %557 %311 %229\nOpLine %3 198 21\n%559 = OpSampledImage  %471  %552 %551\n%561 = OpConvertSToF  %8  %88\n%560 = OpImageSampleExplicitLod  %24  %559 %311 Lod %561\n%562 = OpCompositeExtract  %8  %560 0\nOpLine %3 196 15\n%563 = OpCompositeConstruct  %24  %556 %556 %556 %556\n%564 = OpFAdd  %24  %563 %558\n%565 = OpCompositeConstruct  %24  %562 %562 %562 %562\n%566 = OpFAdd  %24  %564 %565\nOpStore %549 %566\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-index-by-value.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 87\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %79 \"main\" %74 %77\nOpDecorate %4 ArrayStride 4\nOpDecorate %7 ArrayStride 4\nOpDecorate %9 ArrayStride 8\nOpDecorate %74 BuiltIn VertexIndex\nOpDecorate %77 BuiltIn Position\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  5\n%4 = OpTypeArray %3 %5\n%8 = OpConstant  %6  2\n%7 = OpTypeArray %3 %8\n%9 = OpTypeArray %7 %8\n%10 = OpTypeFloat 32\n%12 = OpTypeVector %10 2\n%11 = OpTypeMatrix %12 2\n%13 = OpTypeVector %10 4\n%18 = OpTypeFunction %3 %4 %3\n%20 = OpTypePointer Function %4\n%22 = OpTypePointer Function %3\n%29 = OpTypeFunction %3 %3 %3\n%30 = OpConstant  %3  1\n%31 = OpConstant  %3  2\n%32 = OpConstantComposite  %7  %30 %31\n%33 = OpConstant  %3  3\n%34 = OpConstant  %3  4\n%35 = OpConstantComposite  %7  %33 %34\n%36 = OpConstantComposite  %9  %32 %35\n%38 = OpTypePointer Function %9\n%46 = OpTypeFunction %10 %3 %3\n%47 = OpConstant  %10  1\n%48 = OpConstant  %10  2\n%49 = OpConstant  %10  3\n%50 = OpConstant  %10  4\n%51 = OpConstantComposite  %12  %47 %48\n%52 = OpConstantComposite  %12  %49 %50\n%53 = OpConstantComposite  %11  %51 %52\n%55 = OpTypePointer Function %11\n%57 = OpTypePointer Function %10\n%63 = OpTypeFunction %13 %6\n%64 = OpConstant  %3  5\n%65 = OpConstantComposite  %4  %30 %31 %33 %34 %64\n%70 = OpTypeVector %3 4\n%75 = OpTypePointer Input %6\n%74 = OpVariable  %75  Input\n%78 = OpTypePointer Output %13\n%77 = OpVariable  %78  Output\n%80 = OpTypeFunction %2\n%81 = OpConstant  %3  6\n%17 = OpFunction  %3  None %18\n%15 = OpFunctionParameter  %4\n%16 = OpFunctionParameter  %3\n%14 = OpLabel\n%21 = OpVariable  %20  Function\nOpBranch %19\n%19 = OpLabel\nOpStore %21 %15\n%23 = OpAccessChain  %22  %21 %16\n%24 = OpLoad  %3  %23\nOpReturnValue %24\nOpFunctionEnd\n%28 = OpFunction  %3  None %29\n%26 = OpFunctionParameter  %3\n%27 = OpFunctionParameter  %3\n%25 = OpLabel\n%39 = OpVariable  %38  Function\nOpBranch %37\n%37 = OpLabel\nOpStore %39 %36\n%40 = OpAccessChain  %22  %39 %26 %27\n%41 = OpLoad  %3  %40\nOpReturnValue %41\nOpFunctionEnd\n%45 = OpFunction  %10  None %46\n%43 = OpFunctionParameter  %3\n%44 = OpFunctionParameter  %3\n%42 = OpLabel\n%56 = OpVariable  %55  Function\nOpBranch %54\n%54 = OpLabel\nOpStore %56 %53\n%58 = OpAccessChain  %57  %56 %43 %44\n%59 = OpLoad  %10  %58\nOpReturnValue %59\nOpFunctionEnd\n%62 = OpFunction  %13  None %63\n%61 = OpFunctionParameter  %6\n%60 = OpLabel\n%67 = OpVariable  %20  Function\nOpBranch %66\n%66 = OpLabel\nOpStore %67 %65\n%68 = OpAccessChain  %22  %67 %61\n%69 = OpLoad  %3  %68\n%71 = OpCompositeConstruct  %70  %69 %69 %69 %69\n%72 = OpConvertSToF  %13  %71\nOpReturnValue %72\nOpFunctionEnd\n%79 = OpFunction  %2  None %80\n%73 = OpLabel\n%76 = OpLoad  %6  %74\nOpBranch %82\n%82 = OpLabel\n%83 = OpFunctionCall  %3  %17 %65 %81\n%84 = OpFunctionCall  %3  %28 %30 %31\n%85 = OpFunctionCall  %10  %45 %30 %31\n%86 = OpFunctionCall  %13  %62 %76\nOpStore %77 %86\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-int64.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 385\nOpCapability Shader\nOpCapability Int64\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %369 \"main\"\nOpExecutionMode %369 LocalSize 1 1 1\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 4\nOpMemberDecorate %14 2 Offset 8\nOpMemberDecorate %14 3 Offset 16\nOpMemberDecorate %14 4 Offset 32\nOpMemberDecorate %14 5 Offset 64\nOpMemberDecorate %14 6 Offset 96\nOpMemberDecorate %14 7 Offset 128\nOpMemberDecorate %14 8 Offset 144\nOpMemberDecorate %14 9 Offset 160\nOpMemberDecorate %14 10 Offset 192\nOpMemberDecorate %14 11 Offset 224\nOpDecorate %15 ArrayStride 8\nOpDecorate %17 ArrayStride 8\nOpMemberDecorate %18 0 Offset 0\nOpMemberDecorate %18 1 Offset 16\nOpDecorate %23 DescriptorSet 0\nOpDecorate %23 Binding 0\nOpDecorate %24 Block\nOpMemberDecorate %24 0 Offset 0\nOpDecorate %26 NonWritable\nOpDecorate %26 DescriptorSet 0\nOpDecorate %26 Binding 1\nOpDecorate %27 Block\nOpMemberDecorate %27 0 Offset 0\nOpDecorate %29 NonWritable\nOpDecorate %29 DescriptorSet 0\nOpDecorate %29 Binding 2\nOpDecorate %30 Block\nOpMemberDecorate %30 0 Offset 0\nOpDecorate %32 DescriptorSet 0\nOpDecorate %32 Binding 3\nOpDecorate %33 Block\nOpMemberDecorate %33 0 Offset 0\nOpDecorate %35 DescriptorSet 0\nOpDecorate %35 Binding 4\nOpDecorate %36 Block\nOpMemberDecorate %36 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeInt 64 1\n%4 = OpTypeInt 64 0\n%5 = OpTypeInt 32 0\n%6 = OpTypeInt 32 1\n%7 = OpTypeFloat 32\n%8 = OpTypeVector %4 2\n%9 = OpTypeVector %4 3\n%10 = OpTypeVector %4 4\n%11 = OpTypeVector %3 2\n%12 = OpTypeVector %3 3\n%13 = OpTypeVector %3 4\n%14 = OpTypeStruct %5 %6 %7 %4 %8 %9 %10 %3 %11 %12 %13 %4\n%16 = OpConstant  %5  2\n%15 = OpTypeArray %4 %16\n%17 = OpTypeArray %3 %16\n%18 = OpTypeStruct %15 %17\n%19 = OpConstant  %3  1\n%20 = OpConstant  %4  20\n%22 = OpTypePointer Private %3\n%21 = OpVariable  %22  Private %19\n%24 = OpTypeStruct %14\n%25 = OpTypePointer Uniform %24\n%23 = OpVariable  %25  Uniform\n%27 = OpTypeStruct %14\n%28 = OpTypePointer StorageBuffer %27\n%26 = OpVariable  %28  StorageBuffer\n%30 = OpTypeStruct %18\n%31 = OpTypePointer StorageBuffer %30\n%29 = OpVariable  %31  StorageBuffer\n%33 = OpTypeStruct %14\n%34 = OpTypePointer StorageBuffer %33\n%32 = OpVariable  %34  StorageBuffer\n%36 = OpTypeStruct %18\n%37 = OpTypePointer StorageBuffer %36\n%35 = OpVariable  %37  StorageBuffer\n%41 = OpTypeFunction %3 %3\n%42 = OpTypePointer Uniform %14\n%43 = OpConstant  %5  0\n%45 = OpTypePointer StorageBuffer %14\n%47 = OpTypePointer StorageBuffer %18\n%51 = OpConstant  %3  20\n%52 = OpConstant  %3  31\n%53 = OpConstant  %3  1002003004005006\n%54 = OpConstant  %3  -9223372036854775807\n%55 = OpConstant  %3  5\n%56 = OpConstant  %3  -9223372036854775808\n%58 = OpTypePointer Function %3\n%70 = OpTypePointer Uniform %5\n%79 = OpTypePointer Uniform %6\n%80 = OpConstant  %5  1\n%89 = OpTypePointer Uniform %7\n%95 = OpConstant  %7  -9223372000000000000\n%96 = OpConstant  %7  9223371500000000000\n%101 = OpTypePointer Uniform %3\n%102 = OpConstant  %5  7\n%109 = OpTypePointer Uniform %4\n%110 = OpConstant  %5  3\n%116 = OpTypePointer Uniform %8\n%117 = OpConstant  %5  4\n%124 = OpTypePointer Uniform %9\n%125 = OpConstant  %5  5\n%132 = OpTypePointer Uniform %10\n%133 = OpConstant  %5  6\n%141 = OpTypePointer StorageBuffer %3\n%148 = OpTypePointer StorageBuffer %11\n%149 = OpTypePointer Uniform %11\n%150 = OpConstant  %5  8\n%157 = OpTypePointer StorageBuffer %12\n%158 = OpTypePointer Uniform %12\n%159 = OpConstant  %5  9\n%166 = OpTypePointer StorageBuffer %13\n%167 = OpTypePointer Uniform %13\n%168 = OpConstant  %5  10\n%175 = OpTypePointer StorageBuffer %17\n%196 = OpConstantNull  %3\n%223 = OpTypeFunction %4 %4\n%229 = OpConstant  %4  31\n%230 = OpConstant  %4  18446744073709551615\n%231 = OpConstant  %4  5\n%233 = OpTypePointer Function %4\n%265 = OpConstant  %7  0\n%266 = OpConstant  %7  18446743000000000000\n%299 = OpTypePointer StorageBuffer %4\n%306 = OpTypePointer StorageBuffer %8\n%313 = OpTypePointer StorageBuffer %9\n%320 = OpTypePointer StorageBuffer %10\n%327 = OpTypePointer StorageBuffer %15\n%348 = OpConstantNull  %4\n%370 = OpTypeFunction %2\n%376 = OpConstant  %4  67\n%377 = OpConstant  %3  60\n%383 = OpConstant  %5  11\n%40 = OpFunction  %3  None %41\n%39 = OpFunctionParameter  %3\n%38 = OpLabel\n%57 = OpVariable  %58  Function %51\n%44 = OpAccessChain  %42  %23 %43\n%46 = OpAccessChain  %45  %26 %43\n%48 = OpAccessChain  %47  %29 %43\n%49 = OpAccessChain  %45  %32 %43\n%50 = OpAccessChain  %47  %35 %43\nOpBranch %59\n%59 = OpLabel\n%60 = OpLoad  %3  %21\n%61 = OpLoad  %3  %57\n%62 = OpISub  %3  %52 %53\n%63 = OpIAdd  %3  %62 %54\n%64 = OpIAdd  %3  %61 %63\nOpStore %57 %64\n%65 = OpLoad  %3  %57\n%66 = OpLoad  %3  %57\n%67 = OpIAdd  %3  %66 %55\n%68 = OpIAdd  %3  %65 %67\nOpStore %57 %68\n%69 = OpLoad  %3  %57\n%71 = OpAccessChain  %70  %44 %43\n%72 = OpLoad  %5  %71\n%73 = OpLoad  %3  %57\n%74 = OpUConvert  %5  %73\n%75 = OpIAdd  %5  %72 %74\n%76 = OpSConvert  %3  %75\n%77 = OpIAdd  %3  %69 %76\nOpStore %57 %77\n%78 = OpLoad  %3  %57\n%81 = OpAccessChain  %79  %44 %80\n%82 = OpLoad  %6  %81\n%83 = OpLoad  %3  %57\n%84 = OpSConvert  %6  %83\n%85 = OpIAdd  %6  %82 %84\n%86 = OpSConvert  %3  %85\n%87 = OpIAdd  %3  %78 %86\nOpStore %57 %87\n%88 = OpLoad  %3  %57\n%90 = OpAccessChain  %89  %44 %16\n%91 = OpLoad  %7  %90\n%92 = OpLoad  %3  %57\n%93 = OpConvertSToF  %7  %92\n%94 = OpFAdd  %7  %91 %93\n%97 = OpExtInst  %7  %1 FClamp %94 %95 %96\n%98 = OpConvertFToS  %3  %97\n%99 = OpIAdd  %3  %88 %98\nOpStore %57 %99\n%100 = OpLoad  %3  %57\n%103 = OpAccessChain  %101  %44 %102\n%104 = OpLoad  %3  %103\n%105 = OpCompositeConstruct  %12  %104 %104 %104\n%106 = OpCompositeExtract  %3  %105 2\n%107 = OpIAdd  %3  %100 %106\nOpStore %57 %107\n%108 = OpLoad  %3  %57\n%111 = OpAccessChain  %109  %44 %110\n%112 = OpLoad  %4  %111\n%113 = OpBitcast  %3  %112\n%114 = OpIAdd  %3  %108 %113\nOpStore %57 %114\n%115 = OpLoad  %3  %57\n%118 = OpAccessChain  %116  %44 %117\n%119 = OpLoad  %8  %118\n%120 = OpBitcast  %11  %119\n%121 = OpCompositeExtract  %3  %120 1\n%122 = OpIAdd  %3  %115 %121\nOpStore %57 %122\n%123 = OpLoad  %3  %57\n%126 = OpAccessChain  %124  %44 %125\n%127 = OpLoad  %9  %126\n%128 = OpBitcast  %12  %127\n%129 = OpCompositeExtract  %3  %128 2\n%130 = OpIAdd  %3  %123 %129\nOpStore %57 %130\n%131 = OpLoad  %3  %57\n%134 = OpAccessChain  %132  %44 %133\n%135 = OpLoad  %10  %134\n%136 = OpBitcast  %13  %135\n%137 = OpCompositeExtract  %3  %136 3\n%138 = OpIAdd  %3  %131 %137\nOpStore %57 %138\n%139 = OpLoad  %3  %57\n%140 = OpIAdd  %3  %139 %56\nOpStore %57 %140\n%142 = OpAccessChain  %101  %44 %102\n%143 = OpLoad  %3  %142\n%144 = OpAccessChain  %141  %46 %102\n%145 = OpLoad  %3  %144\n%146 = OpIAdd  %3  %143 %145\n%147 = OpAccessChain  %141  %49 %102\nOpStore %147 %146\n%151 = OpAccessChain  %149  %44 %150\n%152 = OpLoad  %11  %151\n%153 = OpAccessChain  %148  %46 %150\n%154 = OpLoad  %11  %153\n%155 = OpIAdd  %11  %152 %154\n%156 = OpAccessChain  %148  %49 %150\nOpStore %156 %155\n%160 = OpAccessChain  %158  %44 %159\n%161 = OpLoad  %12  %160\n%162 = OpAccessChain  %157  %46 %159\n%163 = OpLoad  %12  %162\n%164 = OpIAdd  %12  %161 %163\n%165 = OpAccessChain  %157  %49 %159\nOpStore %165 %164\n%169 = OpAccessChain  %167  %44 %168\n%170 = OpLoad  %13  %169\n%171 = OpAccessChain  %166  %46 %168\n%172 = OpLoad  %13  %171\n%173 = OpIAdd  %13  %170 %172\n%174 = OpAccessChain  %166  %49 %168\nOpStore %174 %173\n%176 = OpAccessChain  %175  %48 %80\n%177 = OpLoad  %17  %176\n%178 = OpAccessChain  %175  %50 %80\nOpStore %178 %177\n%179 = OpLoad  %3  %57\n%180 = OpLoad  %3  %57\n%181 = OpExtInst  %3  %1 SAbs %180\n%182 = OpIAdd  %3  %179 %181\nOpStore %57 %182\n%183 = OpLoad  %3  %57\n%184 = OpLoad  %3  %57\n%185 = OpLoad  %3  %57\n%186 = OpLoad  %3  %57\n%188 = OpExtInst  %3  %1 SMax %184 %185\n%187 = OpExtInst  %3  %1 SMin %188 %186\n%189 = OpIAdd  %3  %183 %187\nOpStore %57 %189\n%190 = OpLoad  %3  %57\n%191 = OpLoad  %3  %57\n%192 = OpCompositeConstruct  %11  %191 %191\n%193 = OpLoad  %3  %57\n%194 = OpCompositeConstruct  %11  %193 %193\n%197 = OpCompositeExtract  %3  %192 0\n%198 = OpCompositeExtract  %3  %194 0\n%199 = OpIMul  %3  %197 %198\n%200 = OpIAdd  %3  %196 %199\n%201 = OpCompositeExtract  %3  %192 1\n%202 = OpCompositeExtract  %3  %194 1\n%203 = OpIMul  %3  %201 %202\n%195 = OpIAdd  %3  %200 %203\n%204 = OpIAdd  %3  %190 %195\nOpStore %57 %204\n%205 = OpLoad  %3  %57\n%206 = OpLoad  %3  %57\n%207 = OpLoad  %3  %57\n%208 = OpExtInst  %3  %1 SMax %206 %207\n%209 = OpIAdd  %3  %205 %208\nOpStore %57 %209\n%210 = OpLoad  %3  %57\n%211 = OpLoad  %3  %57\n%212 = OpLoad  %3  %57\n%213 = OpExtInst  %3  %1 SMin %211 %212\n%214 = OpIAdd  %3  %210 %213\nOpStore %57 %214\n%215 = OpLoad  %3  %57\n%216 = OpLoad  %3  %57\n%217 = OpExtInst  %3  %1 SSign %216\n%218 = OpIAdd  %3  %215 %217\nOpStore %57 %218\n%219 = OpLoad  %3  %57\nOpReturnValue %219\nOpFunctionEnd\n%222 = OpFunction  %4  None %223\n%221 = OpFunctionParameter  %4\n%220 = OpLabel\n%232 = OpVariable  %233  Function %20\n%224 = OpAccessChain  %42  %23 %43\n%225 = OpAccessChain  %45  %26 %43\n%226 = OpAccessChain  %47  %29 %43\n%227 = OpAccessChain  %45  %32 %43\n%228 = OpAccessChain  %47  %35 %43\nOpBranch %234\n%234 = OpLabel\n%235 = OpLoad  %4  %232\n%236 = OpIAdd  %4  %229 %230\n%237 = OpISub  %4  %236 %230\n%238 = OpIAdd  %4  %235 %237\nOpStore %232 %238\n%239 = OpLoad  %4  %232\n%240 = OpLoad  %4  %232\n%241 = OpIAdd  %4  %240 %231\n%242 = OpIAdd  %4  %239 %241\nOpStore %232 %242\n%243 = OpLoad  %4  %232\n%244 = OpAccessChain  %70  %224 %43\n%245 = OpLoad  %5  %244\n%246 = OpLoad  %4  %232\n%247 = OpUConvert  %5  %246\n%248 = OpIAdd  %5  %245 %247\n%249 = OpUConvert  %4  %248\n%250 = OpIAdd  %4  %243 %249\nOpStore %232 %250\n%251 = OpLoad  %4  %232\n%252 = OpAccessChain  %79  %224 %80\n%253 = OpLoad  %6  %252\n%254 = OpLoad  %4  %232\n%255 = OpSConvert  %6  %254\n%256 = OpIAdd  %6  %253 %255\n%257 = OpUConvert  %4  %256\n%258 = OpIAdd  %4  %251 %257\nOpStore %232 %258\n%259 = OpLoad  %4  %232\n%260 = OpAccessChain  %89  %224 %16\n%261 = OpLoad  %7  %260\n%262 = OpLoad  %4  %232\n%263 = OpConvertUToF  %7  %262\n%264 = OpFAdd  %7  %261 %263\n%267 = OpExtInst  %7  %1 FClamp %264 %265 %266\n%268 = OpConvertFToU  %4  %267\n%269 = OpIAdd  %4  %259 %268\nOpStore %232 %269\n%270 = OpLoad  %4  %232\n%271 = OpAccessChain  %109  %224 %110\n%272 = OpLoad  %4  %271\n%273 = OpCompositeConstruct  %9  %272 %272 %272\n%274 = OpCompositeExtract  %4  %273 2\n%275 = OpIAdd  %4  %270 %274\nOpStore %232 %275\n%276 = OpLoad  %4  %232\n%277 = OpAccessChain  %101  %224 %102\n%278 = OpLoad  %3  %277\n%279 = OpBitcast  %4  %278\n%280 = OpIAdd  %4  %276 %279\nOpStore %232 %280\n%281 = OpLoad  %4  %232\n%282 = OpAccessChain  %149  %224 %150\n%283 = OpLoad  %11  %282\n%284 = OpBitcast  %8  %283\n%285 = OpCompositeExtract  %4  %284 1\n%286 = OpIAdd  %4  %281 %285\nOpStore %232 %286\n%287 = OpLoad  %4  %232\n%288 = OpAccessChain  %158  %224 %159\n%289 = OpLoad  %12  %288\n%290 = OpBitcast  %9  %289\n%291 = OpCompositeExtract  %4  %290 2\n%292 = OpIAdd  %4  %287 %291\nOpStore %232 %292\n%293 = OpLoad  %4  %232\n%294 = OpAccessChain  %167  %224 %168\n%295 = OpLoad  %13  %294\n%296 = OpBitcast  %10  %295\n%297 = OpCompositeExtract  %4  %296 3\n%298 = OpIAdd  %4  %293 %297\nOpStore %232 %298\n%300 = OpAccessChain  %109  %224 %110\n%301 = OpLoad  %4  %300\n%302 = OpAccessChain  %299  %225 %110\n%303 = OpLoad  %4  %302\n%304 = OpIAdd  %4  %301 %303\n%305 = OpAccessChain  %299  %227 %110\nOpStore %305 %304\n%307 = OpAccessChain  %116  %224 %117\n%308 = OpLoad  %8  %307\n%309 = OpAccessChain  %306  %225 %117\n%310 = OpLoad  %8  %309\n%311 = OpIAdd  %8  %308 %310\n%312 = OpAccessChain  %306  %227 %117\nOpStore %312 %311\n%314 = OpAccessChain  %124  %224 %125\n%315 = OpLoad  %9  %314\n%316 = OpAccessChain  %313  %225 %125\n%317 = OpLoad  %9  %316\n%318 = OpIAdd  %9  %315 %317\n%319 = OpAccessChain  %313  %227 %125\nOpStore %319 %318\n%321 = OpAccessChain  %132  %224 %133\n%322 = OpLoad  %10  %321\n%323 = OpAccessChain  %320  %225 %133\n%324 = OpLoad  %10  %323\n%325 = OpIAdd  %10  %322 %324\n%326 = OpAccessChain  %320  %227 %133\nOpStore %326 %325\n%328 = OpAccessChain  %327  %226 %43\n%329 = OpLoad  %15  %328\n%330 = OpAccessChain  %327  %228 %43\nOpStore %330 %329\n%331 = OpLoad  %4  %232\n%332 = OpLoad  %4  %232\n%333 = OpCopyObject  %4  %332\n%334 = OpIAdd  %4  %331 %333\nOpStore %232 %334\n%335 = OpLoad  %4  %232\n%336 = OpLoad  %4  %232\n%337 = OpLoad  %4  %232\n%338 = OpLoad  %4  %232\n%340 = OpExtInst  %4  %1 UMax %336 %337\n%339 = OpExtInst  %4  %1 UMin %340 %338\n%341 = OpIAdd  %4  %335 %339\nOpStore %232 %341\n%342 = OpLoad  %4  %232\n%343 = OpLoad  %4  %232\n%344 = OpCompositeConstruct  %8  %343 %343\n%345 = OpLoad  %4  %232\n%346 = OpCompositeConstruct  %8  %345 %345\n%349 = OpCompositeExtract  %4  %344 0\n%350 = OpCompositeExtract  %4  %346 0\n%351 = OpIMul  %4  %349 %350\n%352 = OpIAdd  %4  %348 %351\n%353 = OpCompositeExtract  %4  %344 1\n%354 = OpCompositeExtract  %4  %346 1\n%355 = OpIMul  %4  %353 %354\n%347 = OpIAdd  %4  %352 %355\n%356 = OpIAdd  %4  %342 %347\nOpStore %232 %356\n%357 = OpLoad  %4  %232\n%358 = OpLoad  %4  %232\n%359 = OpLoad  %4  %232\n%360 = OpExtInst  %4  %1 UMax %358 %359\n%361 = OpIAdd  %4  %357 %360\nOpStore %232 %361\n%362 = OpLoad  %4  %232\n%363 = OpLoad  %4  %232\n%364 = OpLoad  %4  %232\n%365 = OpExtInst  %4  %1 UMin %363 %364\n%366 = OpIAdd  %4  %362 %365\nOpStore %232 %366\n%367 = OpLoad  %4  %232\nOpReturnValue %367\nOpFunctionEnd\n%369 = OpFunction  %2  None %370\n%368 = OpLabel\n%371 = OpAccessChain  %42  %23 %43\n%372 = OpAccessChain  %45  %26 %43\n%373 = OpAccessChain  %47  %29 %43\n%374 = OpAccessChain  %45  %32 %43\n%375 = OpAccessChain  %47  %35 %43\nOpBranch %378\n%378 = OpLabel\n%379 = OpFunctionCall  %4  %222 %376\n%380 = OpFunctionCall  %3  %40 %377\n%381 = OpBitcast  %4  %380\n%382 = OpIAdd  %4  %379 %381\n%384 = OpAccessChain  %299  %374 %383\nOpStore %384 %382\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interface.compute.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 50\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %29 \"compute\" %17 %20 %22 %25 %27\nOpExecutionMode %29 LocalSize 1 1 1\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpMemberDecorate %7 2 Offset 8\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %17 BuiltIn GlobalInvocationId\nOpDecorate %20 BuiltIn LocalInvocationId\nOpDecorate %22 BuiltIn LocalInvocationIndex\nOpDecorate %25 BuiltIn WorkgroupId\nOpDecorate %27 BuiltIn NumWorkgroups\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeStruct %4 %3\n%6 = OpTypeInt 32 0\n%7 = OpTypeStruct %3 %6 %3\n%8 = OpTypeBool\n%10 = OpConstant  %6  1\n%9 = OpTypeArray %6 %10\n%11 = OpTypeVector %6 3\n%12 = OpTypeStruct %6\n%13 = OpTypeStruct %6\n%15 = OpTypePointer Workgroup %9\n%14 = OpVariable  %15  Workgroup\n%18 = OpTypePointer Input %11\n%17 = OpVariable  %18  Input\n%20 = OpVariable  %18  Input\n%23 = OpTypePointer Input %6\n%22 = OpVariable  %23  Input\n%25 = OpVariable  %18  Input\n%27 = OpVariable  %18  Input\n%30 = OpTypeFunction %2\n%32 = OpConstantNull  %9\n%33 = OpConstant  %6  0\n%37 = OpConstant  %6  2\n%38 = OpConstant  %6  264\n%40 = OpTypePointer Workgroup %6\n%29 = OpFunction  %2  None %30\n%16 = OpLabel\n%19 = OpLoad  %11  %17\n%21 = OpLoad  %11  %20\n%24 = OpLoad  %6  %22\n%26 = OpLoad  %11  %25\n%28 = OpLoad  %11  %27\nOpBranch %31\n%31 = OpLabel\n%34 = OpIEqual  %8  %24 %33\nOpSelectionMerge %35 None\nOpBranchConditional %34 %36 %35\n%36 = OpLabel\nOpStore %14 %32\nOpBranch %35\n%35 = OpLabel\nOpControlBarrier %37 %37 %38\nOpBranch %39\n%39 = OpLabel\n%41 = OpCompositeExtract  %6  %19 0\n%42 = OpCompositeExtract  %6  %21 0\n%43 = OpIAdd  %6  %41 %42\n%44 = OpIAdd  %6  %43 %24\n%45 = OpCompositeExtract  %6  %26 0\n%46 = OpIAdd  %6  %44 %45\n%47 = OpCompositeExtract  %6  %28 0\n%48 = OpIAdd  %6  %46 %47\n%49 = OpAccessChain  %40  %14 %33\nOpStore %49 %48\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interface.fragment.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 50\nOpCapability Shader\nOpCapability SampleRateShading\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %35 \"fragment\" %16 %19 %22 %25 %28 %30 %32 %34\nOpExecutionMode %35 OriginUpperLeft\nOpExecutionMode %35 DepthReplacing\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpMemberDecorate %7 2 Offset 8\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %16 BuiltIn FragCoord\nOpDecorate %16 Invariant\nOpDecorate %19 Location 1\nOpDecorate %22 BuiltIn FrontFacing\nOpDecorate %22 Flat\nOpDecorate %25 BuiltIn SampleId\nOpDecorate %25 Flat\nOpDecorate %28 BuiltIn SampleMask\nOpDecorate %28 Flat\nOpDecorate %30 BuiltIn FragDepth\nOpDecorate %32 BuiltIn SampleMask\nOpDecorate %34 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeStruct %4 %3\n%6 = OpTypeInt 32 0\n%7 = OpTypeStruct %3 %6 %3\n%8 = OpTypeBool\n%10 = OpConstant  %6  1\n%9 = OpTypeArray %6 %10\n%11 = OpTypeVector %6 3\n%12 = OpTypeStruct %6\n%13 = OpTypeStruct %6\n%17 = OpTypePointer Input %4\n%16 = OpVariable  %17  Input\n%20 = OpTypePointer Input %3\n%19 = OpVariable  %20  Input\n%23 = OpTypePointer Input %8\n%22 = OpVariable  %23  Input\n%26 = OpTypePointer Input %6\n%25 = OpVariable  %26  Input\n%28 = OpVariable  %26  Input\n%31 = OpTypePointer Output %3\n%30 = OpVariable  %31  Output\n%33 = OpTypePointer Output %6\n%32 = OpVariable  %33  Output\n%34 = OpVariable  %31  Output\n%36 = OpTypeFunction %2\n%37 = OpConstant  %3  0\n%38 = OpConstant  %3  1\n%35 = OpFunction  %2  None %36\n%14 = OpLabel\n%18 = OpLoad  %4  %16\n%21 = OpLoad  %3  %19\n%15 = OpCompositeConstruct  %5  %18 %21\n%24 = OpLoad  %8  %22\n%27 = OpLoad  %6  %25\n%29 = OpLoad  %6  %28\nOpBranch %39\n%39 = OpLabel\n%40 = OpShiftLeftLogical  %6  %10 %27\n%41 = OpBitwiseAnd  %6  %29 %40\n%42 = OpSelect  %3  %24 %38 %37\n%43 = OpCompositeExtract  %3  %15 1\n%44 = OpCompositeConstruct  %7  %43 %41 %42\n%45 = OpCompositeExtract  %3  %44 0\nOpStore %30 %45\n%46 = OpLoad  %3  %30\n%47 = OpExtInst  %3  %1 FClamp %46 %37 %38\nOpStore %30 %47\n%48 = OpCompositeExtract  %6  %44 1\nOpStore %32 %48\n%49 = OpCompositeExtract  %3  %44 2\nOpStore %34 %49\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interface.vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 38\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %28 \"vertex\" %15 %18 %20 %22 %24 %26\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpMemberDecorate %7 2 Offset 8\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %15 BuiltIn VertexIndex\nOpDecorate %18 BuiltIn InstanceIndex\nOpDecorate %20 Location 10\nOpDecorate %22 BuiltIn Position\nOpDecorate %22 Invariant\nOpDecorate %24 Location 1\nOpDecorate %26 BuiltIn PointSize\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeStruct %4 %3\n%6 = OpTypeInt 32 0\n%7 = OpTypeStruct %3 %6 %3\n%8 = OpTypeBool\n%10 = OpConstant  %6  1\n%9 = OpTypeArray %6 %10\n%11 = OpTypeVector %6 3\n%12 = OpTypeStruct %6\n%13 = OpTypeStruct %6\n%16 = OpTypePointer Input %6\n%15 = OpVariable  %16  Input\n%18 = OpVariable  %16  Input\n%20 = OpVariable  %16  Input\n%23 = OpTypePointer Output %4\n%22 = OpVariable  %23  Output\n%25 = OpTypePointer Output %3\n%24 = OpVariable  %25  Output\n%26 = OpVariable  %25  Output\n%27 = OpConstant  %3  1\n%29 = OpTypeFunction %2\n%30 = OpConstantComposite  %4  %27 %27 %27 %27\n%28 = OpFunction  %2  None %29\n%14 = OpLabel\n%17 = OpLoad  %6  %15\n%19 = OpLoad  %6  %18\n%21 = OpLoad  %6  %20\nOpStore %26 %27\nOpBranch %31\n%31 = OpLabel\n%32 = OpIAdd  %6  %17 %19\n%33 = OpIAdd  %6  %32 %21\n%34 = OpConvertUToF  %3  %33\n%35 = OpCompositeConstruct  %5  %30 %34\n%36 = OpCompositeExtract  %4  %35 0\nOpStore %22 %36\n%37 = OpCompositeExtract  %3  %35 1\nOpStore %24 %37\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interface.vertex_two_structs.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 41\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %27 \"vertex_two_structs\" %16 %20 %22 %24\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 16\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpMemberDecorate %7 2 Offset 8\nOpDecorate %9 ArrayStride 4\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %13 0 Offset 0\nOpDecorate %16 BuiltIn VertexIndex\nOpDecorate %20 BuiltIn InstanceIndex\nOpDecorate %22 BuiltIn Position\nOpDecorate %22 Invariant\nOpDecorate %24 BuiltIn PointSize\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeStruct %4 %3\n%6 = OpTypeInt 32 0\n%7 = OpTypeStruct %3 %6 %3\n%8 = OpTypeBool\n%10 = OpConstant  %6  1\n%9 = OpTypeArray %6 %10\n%11 = OpTypeVector %6 3\n%12 = OpTypeStruct %6\n%13 = OpTypeStruct %6\n%17 = OpTypePointer Input %6\n%16 = OpVariable  %17  Input\n%20 = OpVariable  %17  Input\n%23 = OpTypePointer Output %4\n%22 = OpVariable  %23  Output\n%25 = OpTypePointer Output %3\n%24 = OpVariable  %25  Output\n%26 = OpConstant  %3  1\n%28 = OpTypeFunction %2\n%29 = OpConstant  %6  2\n%30 = OpConstant  %3  0\n%32 = OpTypePointer Function %6\n%27 = OpFunction  %2  None %28\n%14 = OpLabel\n%31 = OpVariable  %32  Function %29\n%18 = OpLoad  %6  %16\n%15 = OpCompositeConstruct  %12  %18\n%21 = OpLoad  %6  %20\n%19 = OpCompositeConstruct  %13  %21\nOpStore %24 %26\nOpBranch %33\n%33 = OpLabel\n%34 = OpCompositeExtract  %6  %15 0\n%35 = OpConvertUToF  %3  %34\n%36 = OpCompositeExtract  %6  %19 0\n%37 = OpConvertUToF  %3  %36\n%38 = OpLoad  %6  %31\n%39 = OpConvertUToF  %3  %38\n%40 = OpCompositeConstruct  %4  %35 %37 %39 %30\nOpStore %22 %40\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interpolate.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 139\nOpCapability Shader\nOpCapability SampleRateShading\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %30 \"vert_main\" %11 %13 %15 %16 %17 %19 %21 %23 %24 %25 %26 %27 %28\nOpEntryPoint Fragment %137 \"frag_main\" %108 %111 %114 %116 %118 %121 %124 %127 %129 %131 %133 %135\nOpExecutionMode %137 OriginUpperLeft\n%3 = OpString \"interpolate.wgsl\"\nOpSource Unknown 0 %3 \"//TODO: merge with \\\"interface\\\"?\n\n// NOTE: invalid combinations are tested in the\n// `validation::incompatible_interpolation_and_sampling_types` test.\nstruct FragmentInput {\n  @builtin(position) position: vec4<f32>,\n  @location(0) @interpolate(flat) _flat : u32,\n  @location(1) @interpolate(flat, first) flat_first : u32,\n  @location(2) @interpolate(flat, either) flat_either : u32,\n  @location(3) @interpolate(linear) _linear : f32,\n  @location(4) @interpolate(linear, centroid) linear_centroid : vec2<f32>,\n  @location(6) @interpolate(linear, sample) linear_sample : vec3<f32>,\n  @location(7) @interpolate(linear, center) linear_center : vec3<f32>,\n  @location(8) @interpolate(perspective) perspective : vec4<f32>,\n  @location(9) @interpolate(perspective, centroid) perspective_centroid : f32,\n  @location(10) @interpolate(perspective, sample) perspective_sample : f32,\n  @location(11) @interpolate(perspective, center) perspective_center : f32,\n}\n\n@vertex\nfn vert_main() -> FragmentInput {\n   var out: FragmentInput;\n\n   out.position = vec4<f32>(2.0, 4.0, 5.0, 6.0);\n   out._flat = 8u;\n   out.flat_first = 9u;\n   out.flat_either = 10u;\n   out._linear = 27.0;\n   out.linear_centroid = vec2<f32>(64.0, 125.0);\n   out.linear_sample = vec3<f32>(216.0, 343.0, 512.0);\n   out.linear_center = vec3<f32>(255.0, 511.0, 1024.0);\n   out.perspective = vec4<f32>(729.0, 1000.0, 1331.0, 1728.0);\n   out.perspective_centroid = 2197.0;\n   out.perspective_sample = 2744.0;\n   out.perspective_center = 2812.0;\n\n   return out;\n}\n\n@fragment\nfn frag_main(val : FragmentInput) { }\n\"\nOpMemberName %9 0 \"position\"\nOpMemberName %9 1 \"_flat\"\nOpMemberName %9 2 \"flat_first\"\nOpMemberName %9 3 \"flat_either\"\nOpMemberName %9 4 \"_linear\"\nOpMemberName %9 5 \"linear_centroid\"\nOpMemberName %9 6 \"linear_sample\"\nOpMemberName %9 7 \"linear_center\"\nOpMemberName %9 8 \"perspective\"\nOpMemberName %9 9 \"perspective_centroid\"\nOpMemberName %9 10 \"perspective_sample\"\nOpMemberName %9 11 \"perspective_center\"\nOpName %9 \"FragmentInput\"\nOpName %11 \"position\"\nOpName %13 \"_flat\"\nOpName %15 \"flat_first\"\nOpName %16 \"flat_either\"\nOpName %17 \"_linear\"\nOpName %19 \"linear_centroid\"\nOpName %21 \"linear_sample\"\nOpName %23 \"linear_center\"\nOpName %24 \"perspective\"\nOpName %25 \"perspective_centroid\"\nOpName %26 \"perspective_sample\"\nOpName %27 \"perspective_center\"\nOpName %30 \"vert_main\"\nOpName %60 \"out\"\nOpName %108 \"position\"\nOpName %111 \"_flat\"\nOpName %114 \"flat_first\"\nOpName %116 \"flat_either\"\nOpName %118 \"_linear\"\nOpName %121 \"linear_centroid\"\nOpName %124 \"linear_sample\"\nOpName %127 \"linear_center\"\nOpName %129 \"perspective\"\nOpName %131 \"perspective_centroid\"\nOpName %133 \"perspective_sample\"\nOpName %135 \"perspective_center\"\nOpName %137 \"frag_main\"\nOpMemberDecorate %9 0 Offset 0\nOpMemberDecorate %9 1 Offset 16\nOpMemberDecorate %9 2 Offset 20\nOpMemberDecorate %9 3 Offset 24\nOpMemberDecorate %9 4 Offset 28\nOpMemberDecorate %9 5 Offset 32\nOpMemberDecorate %9 6 Offset 48\nOpMemberDecorate %9 7 Offset 64\nOpMemberDecorate %9 8 Offset 80\nOpMemberDecorate %9 9 Offset 96\nOpMemberDecorate %9 10 Offset 100\nOpMemberDecorate %9 11 Offset 104\nOpDecorate %11 BuiltIn Position\nOpDecorate %13 Location 0\nOpDecorate %13 Flat\nOpDecorate %15 Location 1\nOpDecorate %15 Flat\nOpDecorate %16 Location 2\nOpDecorate %16 Flat\nOpDecorate %17 Location 3\nOpDecorate %17 NoPerspective\nOpDecorate %19 Location 4\nOpDecorate %19 NoPerspective\nOpDecorate %19 Centroid\nOpDecorate %21 Location 6\nOpDecorate %21 NoPerspective\nOpDecorate %21 Sample\nOpDecorate %23 Location 7\nOpDecorate %23 NoPerspective\nOpDecorate %24 Location 8\nOpDecorate %25 Location 9\nOpDecorate %25 Centroid\nOpDecorate %26 Location 10\nOpDecorate %26 Sample\nOpDecorate %27 Location 11\nOpDecorate %28 BuiltIn PointSize\nOpDecorate %108 BuiltIn FragCoord\nOpDecorate %111 Location 0\nOpDecorate %111 Flat\nOpDecorate %114 Location 1\nOpDecorate %114 Flat\nOpDecorate %116 Location 2\nOpDecorate %116 Flat\nOpDecorate %118 Location 3\nOpDecorate %118 NoPerspective\nOpDecorate %121 Location 4\nOpDecorate %121 NoPerspective\nOpDecorate %121 Centroid\nOpDecorate %124 Location 6\nOpDecorate %124 NoPerspective\nOpDecorate %124 Sample\nOpDecorate %127 Location 7\nOpDecorate %127 NoPerspective\nOpDecorate %129 Location 8\nOpDecorate %131 Location 9\nOpDecorate %131 Centroid\nOpDecorate %133 Location 10\nOpDecorate %133 Sample\nOpDecorate %135 Location 11\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 4\n%6 = OpTypeInt 32 0\n%7 = OpTypeVector %4 2\n%8 = OpTypeVector %4 3\n%9 = OpTypeStruct %5 %6 %6 %6 %4 %7 %8 %8 %5 %4 %4 %4\n%12 = OpTypePointer Output %5\n%11 = OpVariable  %12  Output\n%14 = OpTypePointer Output %6\n%13 = OpVariable  %14  Output\n%15 = OpVariable  %14  Output\n%16 = OpVariable  %14  Output\n%18 = OpTypePointer Output %4\n%17 = OpVariable  %18  Output\n%20 = OpTypePointer Output %7\n%19 = OpVariable  %20  Output\n%22 = OpTypePointer Output %8\n%21 = OpVariable  %22  Output\n%23 = OpVariable  %22  Output\n%24 = OpVariable  %12  Output\n%25 = OpVariable  %18  Output\n%26 = OpVariable  %18  Output\n%27 = OpVariable  %18  Output\n%28 = OpVariable  %18  Output\n%29 = OpConstant  %4  1\n%31 = OpTypeFunction %2\n%32 = OpConstant  %4  2\n%33 = OpConstant  %4  4\n%34 = OpConstant  %4  5\n%35 = OpConstant  %4  6\n%36 = OpConstantComposite  %5  %32 %33 %34 %35\n%37 = OpConstant  %6  8\n%38 = OpConstant  %6  9\n%39 = OpConstant  %6  10\n%40 = OpConstant  %4  27\n%41 = OpConstant  %4  64\n%42 = OpConstant  %4  125\n%43 = OpConstantComposite  %7  %41 %42\n%44 = OpConstant  %4  216\n%45 = OpConstant  %4  343\n%46 = OpConstant  %4  512\n%47 = OpConstantComposite  %8  %44 %45 %46\n%48 = OpConstant  %4  255\n%49 = OpConstant  %4  511\n%50 = OpConstant  %4  1024\n%51 = OpConstantComposite  %8  %48 %49 %50\n%52 = OpConstant  %4  729\n%53 = OpConstant  %4  1000\n%54 = OpConstant  %4  1331\n%55 = OpConstant  %4  1728\n%56 = OpConstantComposite  %5  %52 %53 %54 %55\n%57 = OpConstant  %4  2197\n%58 = OpConstant  %4  2744\n%59 = OpConstant  %4  2812\n%61 = OpTypePointer Function %9\n%62 = OpConstantNull  %9\n%64 = OpTypePointer Function %5\n%65 = OpConstant  %6  0\n%67 = OpTypePointer Function %6\n%68 = OpConstant  %6  1\n%70 = OpConstant  %6  2\n%72 = OpConstant  %6  3\n%74 = OpTypePointer Function %4\n%75 = OpConstant  %6  4\n%77 = OpTypePointer Function %7\n%78 = OpConstant  %6  5\n%80 = OpTypePointer Function %8\n%81 = OpConstant  %6  6\n%83 = OpConstant  %6  7\n%88 = OpConstant  %6  11\n%109 = OpTypePointer Input %5\n%108 = OpVariable  %109  Input\n%112 = OpTypePointer Input %6\n%111 = OpVariable  %112  Input\n%114 = OpVariable  %112  Input\n%116 = OpVariable  %112  Input\n%119 = OpTypePointer Input %4\n%118 = OpVariable  %119  Input\n%122 = OpTypePointer Input %7\n%121 = OpVariable  %122  Input\n%125 = OpTypePointer Input %8\n%124 = OpVariable  %125  Input\n%127 = OpVariable  %125  Input\n%129 = OpVariable  %109  Input\n%131 = OpVariable  %119  Input\n%133 = OpVariable  %119  Input\n%135 = OpVariable  %119  Input\n%30 = OpFunction  %2  None %31\n%10 = OpLabel\n%60 = OpVariable  %61  Function %62\nOpStore %28 %29\nOpBranch %63\n%63 = OpLabel\nOpLine %3 24 4\nOpLine %3 24 19\nOpLine %3 24 4\n%66 = OpAccessChain  %64  %60 %65\nOpStore %66 %36\nOpLine %3 25 4\nOpLine %3 25 4\n%69 = OpAccessChain  %67  %60 %68\nOpStore %69 %37\nOpLine %3 26 4\nOpLine %3 26 4\n%71 = OpAccessChain  %67  %60 %70\nOpStore %71 %38\nOpLine %3 27 4\nOpLine %3 27 4\n%73 = OpAccessChain  %67  %60 %72\nOpStore %73 %39\nOpLine %3 28 4\nOpLine %3 28 4\n%76 = OpAccessChain  %74  %60 %75\nOpStore %76 %40\nOpLine %3 29 4\nOpLine %3 29 26\nOpLine %3 29 4\n%79 = OpAccessChain  %77  %60 %78\nOpStore %79 %43\nOpLine %3 30 4\nOpLine %3 30 24\nOpLine %3 30 4\n%82 = OpAccessChain  %80  %60 %81\nOpStore %82 %47\nOpLine %3 31 4\nOpLine %3 31 24\nOpLine %3 31 4\n%84 = OpAccessChain  %80  %60 %83\nOpStore %84 %51\nOpLine %3 32 4\nOpLine %3 32 22\nOpLine %3 32 4\n%85 = OpAccessChain  %64  %60 %37\nOpStore %85 %56\nOpLine %3 33 4\nOpLine %3 33 4\n%86 = OpAccessChain  %74  %60 %38\nOpStore %86 %57\nOpLine %3 34 4\nOpLine %3 34 4\n%87 = OpAccessChain  %74  %60 %39\nOpStore %87 %58\nOpLine %3 35 4\nOpLine %3 35 4\n%89 = OpAccessChain  %74  %60 %88\nOpStore %89 %59\nOpLine %3 1 1\n%90 = OpLoad  %9  %60\n%91 = OpCompositeExtract  %5  %90 0\nOpStore %11 %91\n%92 = OpAccessChain  %18  %11 %68\n%93 = OpLoad  %4  %92\n%94 = OpFNegate  %4  %93\nOpStore %92 %94\n%95 = OpCompositeExtract  %6  %90 1\nOpStore %13 %95\n%96 = OpCompositeExtract  %6  %90 2\nOpStore %15 %96\n%97 = OpCompositeExtract  %6  %90 3\nOpStore %16 %97\n%98 = OpCompositeExtract  %4  %90 4\nOpStore %17 %98\n%99 = OpCompositeExtract  %7  %90 5\nOpStore %19 %99\n%100 = OpCompositeExtract  %8  %90 6\nOpStore %21 %100\n%101 = OpCompositeExtract  %8  %90 7\nOpStore %23 %101\n%102 = OpCompositeExtract  %5  %90 8\nOpStore %24 %102\n%103 = OpCompositeExtract  %4  %90 9\nOpStore %25 %103\n%104 = OpCompositeExtract  %4  %90 10\nOpStore %26 %104\n%105 = OpCompositeExtract  %4  %90 11\nOpStore %27 %105\nOpReturn\nOpFunctionEnd\n%137 = OpFunction  %2  None %31\n%106 = OpLabel\n%110 = OpLoad  %5  %108\n%113 = OpLoad  %6  %111\n%115 = OpLoad  %6  %114\n%117 = OpLoad  %6  %116\n%120 = OpLoad  %4  %118\n%123 = OpLoad  %7  %121\n%126 = OpLoad  %8  %124\n%128 = OpLoad  %8  %127\n%130 = OpLoad  %5  %129\n%132 = OpLoad  %4  %131\n%134 = OpLoad  %4  %133\n%136 = OpLoad  %4  %135\n%107 = OpCompositeConstruct  %9  %110 %113 %115 %117 %120 %123 %126 %128 %130 %132 %134 %136\nOpBranch %138\n%138 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-interpolate_compat.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 133\nOpCapability Shader\nOpCapability SampleRateShading\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %29 \"vert_main\" %11 %13 %15 %16 %18 %20 %22 %23 %24 %25 %26 %27\nOpEntryPoint Fragment %131 \"frag_main\" %104 %107 %110 %112 %115 %118 %121 %123 %125 %127 %129\nOpExecutionMode %131 OriginUpperLeft\n%3 = OpString \"interpolate_compat.wgsl\"\nOpSource Unknown 0 %3 \"// NOTE: This is basically the same as `interpolate.wgsl`, except for the removal of\n// `@interpolate(flat, first)`, which is unsupported in GLSL and `compat`.\n\n// NOTE: invalid combinations are tested in the\n// `validation::incompatible_interpolation_and_sampling_types` test.\nstruct FragmentInput {\n  @builtin(position) position: vec4<f32>,\n  @location(0) @interpolate(flat) _flat : u32,\n  // NOTE: not supported in `compat` or GLSL\n  // // @location(1) @interpolate(flat, first) flat_first : u32,\n  @location(2) @interpolate(flat, either) flat_either : u32,\n  @location(3) @interpolate(linear) _linear : f32,\n  @location(4) @interpolate(linear, centroid) linear_centroid : vec2<f32>,\n  @location(6) @interpolate(linear, sample) linear_sample : vec3<f32>,\n  @location(7) @interpolate(linear, center) linear_center : vec3<f32>,\n  @location(8) @interpolate(perspective) perspective : vec4<f32>,\n  @location(9) @interpolate(perspective, centroid) perspective_centroid : f32,\n  @location(10) @interpolate(perspective, sample) perspective_sample : f32,\n  @location(11) @interpolate(perspective, center) perspective_center : f32,\n}\n\n@vertex\nfn vert_main() -> FragmentInput {\n   var out: FragmentInput;\n\n   out.position = vec4<f32>(2.0, 4.0, 5.0, 6.0);\n   out._flat = 8u;\n   // out.flat_first = 9u;\n   out.flat_either = 10u;\n   out._linear = 27.0;\n   out.linear_centroid = vec2<f32>(64.0, 125.0);\n   out.linear_sample = vec3<f32>(216.0, 343.0, 512.0);\n   out.linear_center = vec3<f32>(255.0, 511.0, 1024.0);\n   out.perspective = vec4<f32>(729.0, 1000.0, 1331.0, 1728.0);\n   out.perspective_centroid = 2197.0;\n   out.perspective_sample = 2744.0;\n   out.perspective_center = 2812.0;\n\n   return out;\n}\n\n@fragment\nfn frag_main(val : FragmentInput) { }\n\"\nOpMemberName %9 0 \"position\"\nOpMemberName %9 1 \"_flat\"\nOpMemberName %9 2 \"flat_either\"\nOpMemberName %9 3 \"_linear\"\nOpMemberName %9 4 \"linear_centroid\"\nOpMemberName %9 5 \"linear_sample\"\nOpMemberName %9 6 \"linear_center\"\nOpMemberName %9 7 \"perspective\"\nOpMemberName %9 8 \"perspective_centroid\"\nOpMemberName %9 9 \"perspective_sample\"\nOpMemberName %9 10 \"perspective_center\"\nOpName %9 \"FragmentInput\"\nOpName %11 \"position\"\nOpName %13 \"_flat\"\nOpName %15 \"flat_either\"\nOpName %16 \"_linear\"\nOpName %18 \"linear_centroid\"\nOpName %20 \"linear_sample\"\nOpName %22 \"linear_center\"\nOpName %23 \"perspective\"\nOpName %24 \"perspective_centroid\"\nOpName %25 \"perspective_sample\"\nOpName %26 \"perspective_center\"\nOpName %29 \"vert_main\"\nOpName %58 \"out\"\nOpName %104 \"position\"\nOpName %107 \"_flat\"\nOpName %110 \"flat_either\"\nOpName %112 \"_linear\"\nOpName %115 \"linear_centroid\"\nOpName %118 \"linear_sample\"\nOpName %121 \"linear_center\"\nOpName %123 \"perspective\"\nOpName %125 \"perspective_centroid\"\nOpName %127 \"perspective_sample\"\nOpName %129 \"perspective_center\"\nOpName %131 \"frag_main\"\nOpMemberDecorate %9 0 Offset 0\nOpMemberDecorate %9 1 Offset 16\nOpMemberDecorate %9 2 Offset 20\nOpMemberDecorate %9 3 Offset 24\nOpMemberDecorate %9 4 Offset 32\nOpMemberDecorate %9 5 Offset 48\nOpMemberDecorate %9 6 Offset 64\nOpMemberDecorate %9 7 Offset 80\nOpMemberDecorate %9 8 Offset 96\nOpMemberDecorate %9 9 Offset 100\nOpMemberDecorate %9 10 Offset 104\nOpDecorate %11 BuiltIn Position\nOpDecorate %13 Location 0\nOpDecorate %13 Flat\nOpDecorate %15 Location 2\nOpDecorate %15 Flat\nOpDecorate %16 Location 3\nOpDecorate %16 NoPerspective\nOpDecorate %18 Location 4\nOpDecorate %18 NoPerspective\nOpDecorate %18 Centroid\nOpDecorate %20 Location 6\nOpDecorate %20 NoPerspective\nOpDecorate %20 Sample\nOpDecorate %22 Location 7\nOpDecorate %22 NoPerspective\nOpDecorate %23 Location 8\nOpDecorate %24 Location 9\nOpDecorate %24 Centroid\nOpDecorate %25 Location 10\nOpDecorate %25 Sample\nOpDecorate %26 Location 11\nOpDecorate %27 BuiltIn PointSize\nOpDecorate %104 BuiltIn FragCoord\nOpDecorate %107 Location 0\nOpDecorate %107 Flat\nOpDecorate %110 Location 2\nOpDecorate %110 Flat\nOpDecorate %112 Location 3\nOpDecorate %112 NoPerspective\nOpDecorate %115 Location 4\nOpDecorate %115 NoPerspective\nOpDecorate %115 Centroid\nOpDecorate %118 Location 6\nOpDecorate %118 NoPerspective\nOpDecorate %118 Sample\nOpDecorate %121 Location 7\nOpDecorate %121 NoPerspective\nOpDecorate %123 Location 8\nOpDecorate %125 Location 9\nOpDecorate %125 Centroid\nOpDecorate %127 Location 10\nOpDecorate %127 Sample\nOpDecorate %129 Location 11\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 4\n%6 = OpTypeInt 32 0\n%7 = OpTypeVector %4 2\n%8 = OpTypeVector %4 3\n%9 = OpTypeStruct %5 %6 %6 %4 %7 %8 %8 %5 %4 %4 %4\n%12 = OpTypePointer Output %5\n%11 = OpVariable  %12  Output\n%14 = OpTypePointer Output %6\n%13 = OpVariable  %14  Output\n%15 = OpVariable  %14  Output\n%17 = OpTypePointer Output %4\n%16 = OpVariable  %17  Output\n%19 = OpTypePointer Output %7\n%18 = OpVariable  %19  Output\n%21 = OpTypePointer Output %8\n%20 = OpVariable  %21  Output\n%22 = OpVariable  %21  Output\n%23 = OpVariable  %12  Output\n%24 = OpVariable  %17  Output\n%25 = OpVariable  %17  Output\n%26 = OpVariable  %17  Output\n%27 = OpVariable  %17  Output\n%28 = OpConstant  %4  1\n%30 = OpTypeFunction %2\n%31 = OpConstant  %4  2\n%32 = OpConstant  %4  4\n%33 = OpConstant  %4  5\n%34 = OpConstant  %4  6\n%35 = OpConstantComposite  %5  %31 %32 %33 %34\n%36 = OpConstant  %6  8\n%37 = OpConstant  %6  10\n%38 = OpConstant  %4  27\n%39 = OpConstant  %4  64\n%40 = OpConstant  %4  125\n%41 = OpConstantComposite  %7  %39 %40\n%42 = OpConstant  %4  216\n%43 = OpConstant  %4  343\n%44 = OpConstant  %4  512\n%45 = OpConstantComposite  %8  %42 %43 %44\n%46 = OpConstant  %4  255\n%47 = OpConstant  %4  511\n%48 = OpConstant  %4  1024\n%49 = OpConstantComposite  %8  %46 %47 %48\n%50 = OpConstant  %4  729\n%51 = OpConstant  %4  1000\n%52 = OpConstant  %4  1331\n%53 = OpConstant  %4  1728\n%54 = OpConstantComposite  %5  %50 %51 %52 %53\n%55 = OpConstant  %4  2197\n%56 = OpConstant  %4  2744\n%57 = OpConstant  %4  2812\n%59 = OpTypePointer Function %9\n%60 = OpConstantNull  %9\n%62 = OpTypePointer Function %5\n%63 = OpConstant  %6  0\n%65 = OpTypePointer Function %6\n%66 = OpConstant  %6  1\n%68 = OpConstant  %6  2\n%70 = OpTypePointer Function %4\n%71 = OpConstant  %6  3\n%73 = OpTypePointer Function %7\n%74 = OpConstant  %6  4\n%76 = OpTypePointer Function %8\n%77 = OpConstant  %6  5\n%79 = OpConstant  %6  6\n%81 = OpConstant  %6  7\n%84 = OpConstant  %6  9\n%105 = OpTypePointer Input %5\n%104 = OpVariable  %105  Input\n%108 = OpTypePointer Input %6\n%107 = OpVariable  %108  Input\n%110 = OpVariable  %108  Input\n%113 = OpTypePointer Input %4\n%112 = OpVariable  %113  Input\n%116 = OpTypePointer Input %7\n%115 = OpVariable  %116  Input\n%119 = OpTypePointer Input %8\n%118 = OpVariable  %119  Input\n%121 = OpVariable  %119  Input\n%123 = OpVariable  %105  Input\n%125 = OpVariable  %113  Input\n%127 = OpVariable  %113  Input\n%129 = OpVariable  %113  Input\n%29 = OpFunction  %2  None %30\n%10 = OpLabel\n%58 = OpVariable  %59  Function %60\nOpStore %27 %28\nOpBranch %61\n%61 = OpLabel\nOpLine %3 26 4\nOpLine %3 26 19\nOpLine %3 26 4\n%64 = OpAccessChain  %62  %58 %63\nOpStore %64 %35\nOpLine %3 27 4\nOpLine %3 27 4\n%67 = OpAccessChain  %65  %58 %66\nOpStore %67 %36\nOpLine %3 29 4\nOpLine %3 29 4\n%69 = OpAccessChain  %65  %58 %68\nOpStore %69 %37\nOpLine %3 30 4\nOpLine %3 30 4\n%72 = OpAccessChain  %70  %58 %71\nOpStore %72 %38\nOpLine %3 31 4\nOpLine %3 31 26\nOpLine %3 31 4\n%75 = OpAccessChain  %73  %58 %74\nOpStore %75 %41\nOpLine %3 32 4\nOpLine %3 32 24\nOpLine %3 32 4\n%78 = OpAccessChain  %76  %58 %77\nOpStore %78 %45\nOpLine %3 33 4\nOpLine %3 33 24\nOpLine %3 33 4\n%80 = OpAccessChain  %76  %58 %79\nOpStore %80 %49\nOpLine %3 34 4\nOpLine %3 34 22\nOpLine %3 34 4\n%82 = OpAccessChain  %62  %58 %81\nOpStore %82 %54\nOpLine %3 35 4\nOpLine %3 35 4\n%83 = OpAccessChain  %70  %58 %36\nOpStore %83 %55\nOpLine %3 36 4\nOpLine %3 36 4\n%85 = OpAccessChain  %70  %58 %84\nOpStore %85 %56\nOpLine %3 37 4\nOpLine %3 37 4\n%86 = OpAccessChain  %70  %58 %37\nOpStore %86 %57\nOpLine %3 1 1\n%87 = OpLoad  %9  %58\n%88 = OpCompositeExtract  %5  %87 0\nOpStore %11 %88\n%89 = OpAccessChain  %17  %11 %66\n%90 = OpLoad  %4  %89\n%91 = OpFNegate  %4  %90\nOpStore %89 %91\n%92 = OpCompositeExtract  %6  %87 1\nOpStore %13 %92\n%93 = OpCompositeExtract  %6  %87 2\nOpStore %15 %93\n%94 = OpCompositeExtract  %4  %87 3\nOpStore %16 %94\n%95 = OpCompositeExtract  %7  %87 4\nOpStore %18 %95\n%96 = OpCompositeExtract  %8  %87 5\nOpStore %20 %96\n%97 = OpCompositeExtract  %8  %87 6\nOpStore %22 %97\n%98 = OpCompositeExtract  %5  %87 7\nOpStore %23 %98\n%99 = OpCompositeExtract  %4  %87 8\nOpStore %24 %99\n%100 = OpCompositeExtract  %4  %87 9\nOpStore %25 %100\n%101 = OpCompositeExtract  %4  %87 10\nOpStore %26 %101\nOpReturn\nOpFunctionEnd\n%131 = OpFunction  %2  None %30\n%102 = OpLabel\n%106 = OpLoad  %5  %104\n%109 = OpLoad  %6  %107\n%111 = OpLoad  %6  %110\n%114 = OpLoad  %4  %112\n%117 = OpLoad  %7  %115\n%120 = OpLoad  %8  %118\n%122 = OpLoad  %8  %121\n%124 = OpLoad  %5  %123\n%126 = OpLoad  %4  %125\n%128 = OpLoad  %4  %127\n%130 = OpLoad  %4  %129\n%103 = OpCompositeConstruct  %9  %106 %109 %111 %114 %117 %120 %122 %124 %126 %128 %130\nOpBranch %132\n%132 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mat_cx2.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 446\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %435 \"main\"\nOpExecutionMode %435 LocalSize 1 1 1\n%3 = OpString \"mat_cx2.wgsl\"\nOpSource Unknown 0 %3 \"// Test handling of N-by-2 matrices.\n// See the doc comments on `naga::back::hlsl` and `naga::back::spv` for details.\n//\n// There are additional tests in `access.wgsl`.\n//\n// Tests that we don't apply this handling to other sizes are in mat_cx3.wgsl.\n\n// Access type (3rd item in variable names)\n// S = Struct\n// M = Matrix\n// C = Column\n// E = Element\n\n// Index type (4th item in variable names)\n// C = Constant\n// V = Variable\n\nalias Mat = mat2x2<f32>;\n\n@group(0) @binding(0)\nvar<storage, read_write> s_m: Mat;\n\n@group(0) @binding(1)\nvar<uniform> u_m: Mat;\n\nfn access_m() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_m = s_m;\n    let l_s_c_c = s_m[0];\n    let l_s_c_v = s_m[idx];\n    let l_s_e_cc = s_m[0][0];\n    let l_s_e_cv = s_m[0][idx];\n    let l_s_e_vc = s_m[idx][0];\n    let l_s_e_vv = s_m[idx][idx];\n\n    // loads from uniform\n    let l_u_m = u_m;\n    let l_u_c_c = u_m[0];\n    let l_u_c_v = u_m[idx];\n    let l_u_e_cc = u_m[0][0];\n    let l_u_e_cv = u_m[0][idx];\n    let l_u_e_vc = u_m[idx][0];\n    let l_u_e_vv = u_m[idx][idx];\n\n    // stores to storage\n    s_m = l_u_m;\n    s_m[0] = l_u_c_c;\n    s_m[idx] = l_u_c_v;\n    s_m[0][0] = l_u_e_cc;\n    s_m[0][idx] = l_u_e_cv;\n    s_m[idx][0] = l_u_e_vc;\n    s_m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithMat {\n    m: Mat,\n}\n\n@group(1) @binding(0)\nvar<storage, read_write> s_sm: StructWithMat;\n\n@group(1) @binding(1)\nvar<uniform> u_sm: StructWithMat;\n\nfn access_sm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sm;\n    let l_s_m = s_sm.m;\n    let l_s_c_c = s_sm.m[0];\n    let l_s_c_v = s_sm.m[idx];\n    let l_s_e_cc = s_sm.m[0][0];\n    let l_s_e_cv = s_sm.m[0][idx];\n    let l_s_e_vc = s_sm.m[idx][0];\n    let l_s_e_vv = s_sm.m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sm;\n    let l_u_m = u_sm.m;\n    let l_u_c_c = u_sm.m[0];\n    let l_u_c_v = u_sm.m[idx];\n    let l_u_e_cc = u_sm.m[0][0];\n    let l_u_e_cv = u_sm.m[0][idx];\n    let l_u_e_vc = u_sm.m[idx][0];\n    let l_u_e_vv = u_sm.m[idx][idx];\n\n    // stores to storage\n    s_sm = l_u_s;\n    s_sm.m = l_u_m;\n    s_sm.m[0] = l_u_c_c;\n    s_sm.m[idx] = l_u_c_v;\n    s_sm.m[0][0] = l_u_e_cc;\n    s_sm.m[0][idx] = l_u_e_cv;\n    s_sm.m[idx][0] = l_u_e_vc;\n    s_sm.m[idx][idx] = l_u_e_vv;\n}\n\nstruct StructWithArrayOfStructOfMat {\n    a: array<StructWithMat, 4>,\n}\n\n@group(2) @binding(0)\nvar<storage, read_write> s_sasm: StructWithArrayOfStructOfMat;\n\n@group(2) @binding(1)\nvar<uniform> u_sasm: StructWithArrayOfStructOfMat;\n\nfn access_sasm() {\n    var idx = 1;\n    idx--;\n\n    // loads from storage\n    let l_s_s = s_sasm;\n    let l_s_a = s_sasm.a;\n    let l_s_m_c = s_sasm.a[0].m;\n    let l_s_m_v = s_sasm.a[idx].m;\n    let l_s_c_cc = s_sasm.a[0].m[0];\n    let l_s_c_cv = s_sasm.a[0].m[idx];\n    let l_s_c_vc = s_sasm.a[idx].m[0];\n    let l_s_c_vv = s_sasm.a[idx].m[idx];\n    let l_s_e_ccc = s_sasm.a[0].m[0][0];\n    let l_s_e_ccv = s_sasm.a[0].m[0][idx];\n    let l_s_e_cvc = s_sasm.a[0].m[idx][0];\n    let l_s_e_cvv = s_sasm.a[0].m[idx][idx];\n    let l_s_e_vcc = s_sasm.a[idx].m[0][0];\n    let l_s_e_vcv = s_sasm.a[idx].m[0][idx];\n    let l_s_e_vvc = s_sasm.a[idx].m[idx][0];\n    let l_s_e_vvv = s_sasm.a[idx].m[idx][idx];\n\n    // loads from uniform\n    let l_u_s = u_sasm;\n    let l_u_a = u_sasm.a;\n    let l_u_m_c = u_sasm.a[0].m;\n    let l_u_m_v = u_sasm.a[idx].m;\n    let l_u_c_cc = u_sasm.a[0].m[0];\n    let l_u_c_cv = u_sasm.a[0].m[idx];\n    let l_u_c_vc = u_sasm.a[idx].m[0];\n    let l_u_c_vv = u_sasm.a[idx].m[idx];\n    let l_u_e_ccc = u_sasm.a[0].m[0][0];\n    let l_u_e_ccv = u_sasm.a[0].m[0][idx];\n    let l_u_e_cvc = u_sasm.a[0].m[idx][0];\n    let l_u_e_cvv = u_sasm.a[0].m[idx][idx];\n    let l_u_e_vcc = u_sasm.a[idx].m[0][0];\n    let l_u_e_vcv = u_sasm.a[idx].m[0][idx];\n    let l_u_e_vvc = u_sasm.a[idx].m[idx][0];\n    let l_u_e_vvv = u_sasm.a[idx].m[idx][idx];\n\n    // stores to storage\n    s_sasm = l_u_s;\n    s_sasm.a = l_u_a;\n    s_sasm.a[0].m = l_u_m_c;\n    s_sasm.a[idx].m = l_u_m_v;\n    s_sasm.a[0].m[0] = l_u_c_cc;\n    s_sasm.a[0].m[idx] = l_u_c_cv;\n    s_sasm.a[idx].m[0] = l_u_c_vc;\n    s_sasm.a[idx].m[idx] = l_u_c_vv;\n    s_sasm.a[0].m[0][0] = l_u_e_ccc;\n    s_sasm.a[0].m[0][idx] = l_u_e_ccv;\n    s_sasm.a[0].m[idx][0] = l_u_e_cvc;\n    s_sasm.a[0].m[idx][idx] = l_u_e_cvv;\n    s_sasm.a[idx].m[0][0] = l_u_e_vcc;\n    s_sasm.a[idx].m[0][idx] = l_u_e_vcv;\n    s_sasm.a[idx].m[idx][0] = l_u_e_vvc;\n    s_sasm.a[idx].m[idx][idx] = l_u_e_vvv;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    access_m();\n    access_sm();\n    access_sasm();\n}\n\"\nOpName %4 \"Mat\"\nOpMemberName %8 0 \"m\"\nOpName %8 \"StructWithMat\"\nOpMemberName %12 0 \"a\"\nOpName %12 \"StructWithArrayOfStructOfMat\"\nOpMemberName %13 0 \"col0\"\nOpMemberName %13 1 \"col1\"\nOpName %13 \"std140_mat2x2<f32>\"\nOpMemberName %14 0 \"m_col0\"\nOpMemberName %14 1 \"m_col1\"\nOpName %14 \"std140_StructWithMat\"\nOpName %15 \"std140_array<StructWithMat, 4>\"\nOpMemberName %16 0 \"a\"\nOpName %16 \"std140_StructWithArrayOfStructOfMat\"\nOpName %17 \"s_m\"\nOpName %20 \"u_m\"\nOpName %23 \"s_sm\"\nOpName %26 \"u_sm\"\nOpName %29 \"s_sasm\"\nOpName %32 \"u_sasm\"\nOpName %35 \"mat2x2<f32>_from_std140\"\nOpName %42 \"mat2x2<f32>_get_column\"\nOpName %55 \"access_m\"\nOpName %64 \"idx\"\nOpName %129 \"StructWithMat_from_std140\"\nOpName %138 \"access_sm\"\nOpName %144 \"idx\"\nOpName %222 \"StructWithArrayOfStructOfMat_from_std140\"\nOpName %227 \"array<StructWithMat, 4>_from_std140\"\nOpName %243 \"access_sasm\"\nOpName %249 \"idx\"\nOpName %435 \"main\"\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 0 ColMajor\nOpMemberDecorate %8 0 MatrixStride 8\nOpDecorate %9 ArrayStride 16\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 8\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 8\nOpDecorate %15 ArrayStride 16\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 0\nOpDecorate %18 Block\nOpMemberDecorate %18 0 Offset 0\nOpMemberDecorate %18 0 ColMajor\nOpMemberDecorate %18 0 MatrixStride 8\nOpDecorate %20 DescriptorSet 0\nOpDecorate %20 Binding 1\nOpDecorate %21 Block\nOpMemberDecorate %21 0 Offset 0\nOpDecorate %23 DescriptorSet 1\nOpDecorate %23 Binding 0\nOpDecorate %24 Block\nOpMemberDecorate %24 0 Offset 0\nOpDecorate %26 DescriptorSet 1\nOpDecorate %26 Binding 1\nOpDecorate %27 Block\nOpMemberDecorate %27 0 Offset 0\nOpDecorate %29 DescriptorSet 2\nOpDecorate %29 Binding 0\nOpDecorate %30 Block\nOpMemberDecorate %30 0 Offset 0\nOpDecorate %32 DescriptorSet 2\nOpDecorate %32 Binding 1\nOpDecorate %33 Block\nOpMemberDecorate %33 0 Offset 0\n%2 = OpTypeVoid\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 2\n%4 = OpTypeMatrix %5 2\n%7 = OpTypeInt 32 1\n%8 = OpTypeStruct %4\n%11 = OpTypeInt 32 0\n%10 = OpConstant  %11  4\n%9 = OpTypeArray %8 %10\n%12 = OpTypeStruct %9\n%13 = OpTypeStruct %5 %5\n%14 = OpTypeStruct %5 %5\n%15 = OpTypeArray %14 %10\n%16 = OpTypeStruct %15\n%18 = OpTypeStruct %4\n%19 = OpTypePointer StorageBuffer %18\n%17 = OpVariable  %19  StorageBuffer\n%21 = OpTypeStruct %13\n%22 = OpTypePointer Uniform %21\n%20 = OpVariable  %22  Uniform\n%24 = OpTypeStruct %8\n%25 = OpTypePointer StorageBuffer %24\n%23 = OpVariable  %25  StorageBuffer\n%27 = OpTypeStruct %14\n%28 = OpTypePointer Uniform %27\n%26 = OpVariable  %28  Uniform\n%30 = OpTypeStruct %12\n%31 = OpTypePointer StorageBuffer %30\n%29 = OpVariable  %31  StorageBuffer\n%33 = OpTypeStruct %16\n%34 = OpTypePointer Uniform %33\n%32 = OpVariable  %34  Uniform\n%36 = OpTypeFunction %4 %13\n%45 = OpTypeFunction %5 %4 %11\n%56 = OpTypeFunction %2\n%57 = OpTypePointer StorageBuffer %4\n%58 = OpConstant  %11  0\n%60 = OpTypePointer Uniform %13\n%62 = OpConstant  %7  1\n%63 = OpTypePointer Uniform %4\n%65 = OpTypePointer Function %7\n%70 = OpTypePointer StorageBuffer %5\n%76 = OpTypePointer StorageBuffer %6\n%91 = OpTypePointer Uniform %5\n%99 = OpTypePointer Uniform %6\n%130 = OpTypeFunction %8 %14\n%139 = OpTypePointer StorageBuffer %8\n%141 = OpTypePointer Uniform %14\n%143 = OpTypePointer Uniform %8\n%170 = OpConstant  %11  1\n%223 = OpTypeFunction %12 %16\n%228 = OpTypeFunction %9 %15\n%244 = OpTypePointer StorageBuffer %12\n%246 = OpTypePointer Uniform %16\n%248 = OpTypePointer Uniform %12\n%254 = OpTypePointer StorageBuffer %9\n%304 = OpTypePointer Uniform %9\n%305 = OpTypePointer Uniform %15\n%35 = OpFunction  %4  None %36\n%37 = OpFunctionParameter  %13\n%38 = OpLabel\n%39 = OpCompositeExtract  %5  %37 0\n%40 = OpCompositeExtract  %5  %37 1\n%41 = OpCompositeConstruct  %4  %39 %40\nOpReturnValue %41\nOpFunctionEnd\n%42 = OpFunction  %5  None %45\n%43 = OpFunctionParameter  %4\n%44 = OpFunctionParameter  %11\n%46 = OpLabel\nOpSelectionMerge %47 None\nOpSwitch %44 %50 0 %48 1 %49\n%48 = OpLabel\n%51 = OpCompositeExtract  %5  %43 0\nOpBranch %47\n%49 = OpLabel\n%52 = OpCompositeExtract  %5  %43 1\nOpBranch %47\n%50 = OpLabel\nOpUnreachable\n%47 = OpLabel\n%53 = OpPhi  %5  %51 %48 %52 %49\nOpReturnValue %53\nOpFunctionEnd\n%55 = OpFunction  %2  None %56\n%54 = OpLabel\n%64 = OpVariable  %65  Function %62\n%59 = OpAccessChain  %57  %17 %58\n%61 = OpAccessChain  %60  %20 %58\nOpBranch %66\n%66 = OpLabel\nOpLine %3 28 5\n%67 = OpLoad  %7  %64\n%68 = OpISub  %7  %67 %62\nOpLine %3 28 5\nOpStore %64 %68\nOpLine %3 31 17\n%69 = OpLoad  %4  %59\nOpLine %3 32 19\n%71 = OpAccessChain  %70  %59 %58\n%72 = OpLoad  %5  %71\nOpLine %3 33 19\n%73 = OpLoad  %7  %64\n%74 = OpAccessChain  %70  %59 %73\n%75 = OpLoad  %5  %74\nOpLine %3 34 20\nOpLine %3 34 20\n%77 = OpAccessChain  %76  %59 %58 %58\n%78 = OpLoad  %6  %77\nOpLine %3 35 20\n%79 = OpLoad  %7  %64\n%80 = OpAccessChain  %76  %59 %58 %79\n%81 = OpLoad  %6  %80\nOpLine %3 36 20\n%82 = OpLoad  %7  %64\nOpLine %3 36 20\n%83 = OpAccessChain  %76  %59 %82 %58\n%84 = OpLoad  %6  %83\nOpLine %3 37 20\n%85 = OpLoad  %7  %64\n%86 = OpLoad  %7  %64\n%87 = OpAccessChain  %76  %59 %85 %86\n%88 = OpLoad  %6  %87\nOpLine %3 40 17\n%89 = OpLoad  %13  %61\n%90 = OpFunctionCall  %4  %35 %89\nOpLine %3 41 19\n%92 = OpAccessChain  %91  %61 %58\n%93 = OpLoad  %5  %92\nOpLine %3 42 19\n%94 = OpLoad  %7  %64\n%95 = OpLoad  %13  %61\n%96 = OpFunctionCall  %4  %35 %95\n%97 = OpBitcast  %11  %94\n%98 = OpFunctionCall  %5  %42 %96 %97\nOpLine %3 43 20\nOpLine %3 43 20\n%100 = OpAccessChain  %99  %61 %58 %58\n%101 = OpLoad  %6  %100\nOpLine %3 44 20\n%102 = OpLoad  %7  %64\n%103 = OpAccessChain  %99  %61 %58 %102\n%104 = OpLoad  %6  %103\nOpLine %3 45 20\n%105 = OpLoad  %7  %64\nOpLine %3 45 20\n%106 = OpLoad  %13  %61\n%107 = OpFunctionCall  %4  %35 %106\n%108 = OpBitcast  %11  %105\n%109 = OpFunctionCall  %5  %42 %107 %108\n%110 = OpCompositeExtract  %6  %109 0\nOpLine %3 46 20\n%111 = OpLoad  %7  %64\n%112 = OpLoad  %7  %64\n%113 = OpLoad  %13  %61\n%114 = OpFunctionCall  %4  %35 %113\n%115 = OpBitcast  %11  %111\n%116 = OpFunctionCall  %5  %42 %114 %115\n%117 = OpVectorExtractDynamic  %6  %116 %112\nOpLine %3 49 5\nOpStore %59 %90\nOpLine %3 50 5\nOpLine %3 50 5\n%118 = OpAccessChain  %70  %59 %58\nOpStore %118 %93\nOpLine %3 51 5\n%119 = OpLoad  %7  %64\nOpLine %3 51 5\n%120 = OpAccessChain  %70  %59 %119\nOpStore %120 %98\nOpLine %3 52 5\nOpLine %3 52 5\nOpLine %3 52 5\n%121 = OpAccessChain  %76  %59 %58 %58\nOpStore %121 %101\nOpLine %3 53 5\n%122 = OpLoad  %7  %64\nOpLine %3 53 5\n%123 = OpAccessChain  %76  %59 %58 %122\nOpStore %123 %104\nOpLine %3 54 5\n%124 = OpLoad  %7  %64\nOpLine %3 54 5\nOpLine %3 54 5\n%125 = OpAccessChain  %76  %59 %124 %58\nOpStore %125 %110\nOpLine %3 55 5\n%126 = OpLoad  %7  %64\n%127 = OpLoad  %7  %64\nOpLine %3 55 5\n%128 = OpAccessChain  %76  %59 %126 %127\nOpStore %128 %117\nOpReturn\nOpFunctionEnd\n%129 = OpFunction  %8  None %130\n%131 = OpFunctionParameter  %14\n%132 = OpLabel\n%134 = OpCompositeExtract  %5  %131 0\n%135 = OpCompositeExtract  %5  %131 1\n%133 = OpCompositeConstruct  %4  %134 %135\n%136 = OpCompositeConstruct  %8  %133\nOpReturnValue %136\nOpFunctionEnd\n%138 = OpFunction  %2  None %56\n%137 = OpLabel\n%144 = OpVariable  %65  Function %62\n%140 = OpAccessChain  %139  %23 %58\n%142 = OpAccessChain  %141  %26 %58\nOpBranch %145\n%145 = OpLabel\nOpLine %3 70 5\n%146 = OpLoad  %7  %144\n%147 = OpISub  %7  %146 %62\nOpLine %3 70 5\nOpStore %144 %147\nOpLine %3 73 17\n%148 = OpLoad  %8  %140\nOpLine %3 74 17\n%149 = OpAccessChain  %57  %140 %58\n%150 = OpLoad  %4  %149\nOpLine %3 75 19\nOpLine %3 75 19\n%151 = OpAccessChain  %70  %140 %58 %58\n%152 = OpLoad  %5  %151\nOpLine %3 76 19\n%153 = OpLoad  %7  %144\n%154 = OpAccessChain  %70  %140 %58 %153\n%155 = OpLoad  %5  %154\nOpLine %3 77 20\nOpLine %3 77 20\nOpLine %3 77 20\n%156 = OpAccessChain  %76  %140 %58 %58 %58\n%157 = OpLoad  %6  %156\nOpLine %3 78 20\nOpLine %3 78 20\n%158 = OpLoad  %7  %144\n%159 = OpAccessChain  %76  %140 %58 %58 %158\n%160 = OpLoad  %6  %159\nOpLine %3 79 20\n%161 = OpLoad  %7  %144\nOpLine %3 79 20\n%162 = OpAccessChain  %76  %140 %58 %161 %58\n%163 = OpLoad  %6  %162\nOpLine %3 80 20\n%164 = OpLoad  %7  %144\n%165 = OpLoad  %7  %144\n%166 = OpAccessChain  %76  %140 %58 %164 %165\n%167 = OpLoad  %6  %166\nOpLine %3 83 17\n%168 = OpLoad  %14  %142\n%169 = OpFunctionCall  %8  %129 %168\nOpLine %3 84 17\n%171 = OpAccessChain  %91  %142 %58\n%172 = OpLoad  %5  %171\n%173 = OpAccessChain  %91  %142 %170\n%174 = OpLoad  %5  %173\n%175 = OpCompositeConstruct  %4  %172 %174\nOpLine %3 85 19\nOpLine %3 85 19\n%176 = OpAccessChain  %91  %142 %58\n%177 = OpLoad  %5  %176\nOpLine %3 86 19\n%178 = OpLoad  %7  %144\n%179 = OpAccessChain  %91  %142 %58\n%180 = OpLoad  %5  %179\n%181 = OpAccessChain  %91  %142 %170\n%182 = OpLoad  %5  %181\n%183 = OpCompositeConstruct  %4  %180 %182\n%184 = OpBitcast  %11  %178\n%185 = OpFunctionCall  %5  %42 %183 %184\nOpLine %3 87 20\nOpLine %3 87 20\nOpLine %3 87 20\n%186 = OpAccessChain  %99  %142 %58 %58\n%187 = OpLoad  %6  %186\nOpLine %3 88 20\nOpLine %3 88 20\n%188 = OpLoad  %7  %144\n%189 = OpAccessChain  %99  %142 %58 %188\n%190 = OpLoad  %6  %189\nOpLine %3 89 20\n%191 = OpLoad  %7  %144\nOpLine %3 89 20\n%192 = OpAccessChain  %91  %142 %58\n%193 = OpLoad  %5  %192\n%194 = OpAccessChain  %91  %142 %170\n%195 = OpLoad  %5  %194\n%196 = OpCompositeConstruct  %4  %193 %195\n%197 = OpBitcast  %11  %191\n%198 = OpFunctionCall  %5  %42 %196 %197\n%199 = OpCompositeExtract  %6  %198 0\nOpLine %3 90 20\n%200 = OpLoad  %7  %144\n%201 = OpLoad  %7  %144\n%202 = OpAccessChain  %91  %142 %58\n%203 = OpLoad  %5  %202\n%204 = OpAccessChain  %91  %142 %170\n%205 = OpLoad  %5  %204\n%206 = OpCompositeConstruct  %4  %203 %205\n%207 = OpBitcast  %11  %200\n%208 = OpFunctionCall  %5  %42 %206 %207\n%209 = OpVectorExtractDynamic  %6  %208 %201\nOpLine %3 93 5\nOpStore %140 %169\nOpLine %3 94 5\nOpLine %3 94 5\n%210 = OpAccessChain  %57  %140 %58\nOpStore %210 %175\nOpLine %3 95 5\nOpLine %3 95 5\nOpLine %3 95 5\n%211 = OpAccessChain  %70  %140 %58 %58\nOpStore %211 %177\nOpLine %3 96 5\n%212 = OpLoad  %7  %144\nOpLine %3 96 5\n%213 = OpAccessChain  %70  %140 %58 %212\nOpStore %213 %185\nOpLine %3 97 5\nOpLine %3 97 5\nOpLine %3 97 5\nOpLine %3 97 5\n%214 = OpAccessChain  %76  %140 %58 %58 %58\nOpStore %214 %187\nOpLine %3 98 5\nOpLine %3 98 5\n%215 = OpLoad  %7  %144\nOpLine %3 98 5\n%216 = OpAccessChain  %76  %140 %58 %58 %215\nOpStore %216 %190\nOpLine %3 99 5\n%217 = OpLoad  %7  %144\nOpLine %3 99 5\nOpLine %3 99 5\n%218 = OpAccessChain  %76  %140 %58 %217 %58\nOpStore %218 %199\nOpLine %3 100 5\n%219 = OpLoad  %7  %144\n%220 = OpLoad  %7  %144\nOpLine %3 100 5\n%221 = OpAccessChain  %76  %140 %58 %219 %220\nOpStore %221 %209\nOpReturn\nOpFunctionEnd\n%227 = OpFunction  %9  None %228\n%229 = OpFunctionParameter  %15\n%230 = OpLabel\n%231 = OpCompositeExtract  %14  %229 0\n%232 = OpFunctionCall  %8  %129 %231\n%233 = OpCompositeExtract  %14  %229 1\n%234 = OpFunctionCall  %8  %129 %233\n%235 = OpCompositeExtract  %14  %229 2\n%236 = OpFunctionCall  %8  %129 %235\n%237 = OpCompositeExtract  %14  %229 3\n%238 = OpFunctionCall  %8  %129 %237\n%239 = OpCompositeConstruct  %9  %232 %234 %236 %238\nOpReturnValue %239\nOpFunctionEnd\n%222 = OpFunction  %12  None %223\n%224 = OpFunctionParameter  %16\n%225 = OpLabel\n%240 = OpCompositeExtract  %15  %224 0\n%226 = OpFunctionCall  %9  %227 %240\n%241 = OpCompositeConstruct  %12  %226\nOpReturnValue %241\nOpFunctionEnd\n%243 = OpFunction  %2  None %56\n%242 = OpLabel\n%249 = OpVariable  %65  Function %62\n%245 = OpAccessChain  %244  %29 %58\n%247 = OpAccessChain  %246  %32 %58\nOpBranch %250\n%250 = OpLabel\nOpLine %3 115 5\n%251 = OpLoad  %7  %249\n%252 = OpISub  %7  %251 %62\nOpLine %3 115 5\nOpStore %249 %252\nOpLine %3 118 17\n%253 = OpLoad  %12  %245\nOpLine %3 119 17\n%255 = OpAccessChain  %254  %245 %58\n%256 = OpLoad  %9  %255\nOpLine %3 120 19\nOpLine %3 120 19\n%257 = OpAccessChain  %57  %245 %58 %58 %58\n%258 = OpLoad  %4  %257\nOpLine %3 121 19\n%259 = OpLoad  %7  %249\n%260 = OpAccessChain  %57  %245 %58 %259 %58\n%261 = OpLoad  %4  %260\nOpLine %3 122 20\nOpLine %3 122 20\nOpLine %3 122 20\n%262 = OpAccessChain  %70  %245 %58 %58 %58 %58\n%263 = OpLoad  %5  %262\nOpLine %3 123 20\nOpLine %3 123 20\n%264 = OpLoad  %7  %249\n%265 = OpAccessChain  %70  %245 %58 %58 %58 %264\n%266 = OpLoad  %5  %265\nOpLine %3 124 20\n%267 = OpLoad  %7  %249\nOpLine %3 124 20\n%268 = OpAccessChain  %70  %245 %58 %267 %58 %58\n%269 = OpLoad  %5  %268\nOpLine %3 125 20\n%270 = OpLoad  %7  %249\n%271 = OpLoad  %7  %249\n%272 = OpAccessChain  %70  %245 %58 %270 %58 %271\n%273 = OpLoad  %5  %272\nOpLine %3 126 21\nOpLine %3 126 21\nOpLine %3 126 21\nOpLine %3 126 21\n%274 = OpAccessChain  %76  %245 %58 %58 %58 %58 %58\n%275 = OpLoad  %6  %274\nOpLine %3 127 21\nOpLine %3 127 21\nOpLine %3 127 21\n%276 = OpLoad  %7  %249\n%277 = OpAccessChain  %76  %245 %58 %58 %58 %58 %276\n%278 = OpLoad  %6  %277\nOpLine %3 128 21\nOpLine %3 128 21\n%279 = OpLoad  %7  %249\nOpLine %3 128 21\n%280 = OpAccessChain  %76  %245 %58 %58 %58 %279 %58\n%281 = OpLoad  %6  %280\nOpLine %3 129 21\nOpLine %3 129 21\n%282 = OpLoad  %7  %249\n%283 = OpLoad  %7  %249\n%284 = OpAccessChain  %76  %245 %58 %58 %58 %282 %283\n%285 = OpLoad  %6  %284\nOpLine %3 130 21\n%286 = OpLoad  %7  %249\nOpLine %3 130 21\nOpLine %3 130 21\n%287 = OpAccessChain  %76  %245 %58 %286 %58 %58 %58\n%288 = OpLoad  %6  %287\nOpLine %3 131 21\n%289 = OpLoad  %7  %249\nOpLine %3 131 21\n%290 = OpLoad  %7  %249\n%291 = OpAccessChain  %76  %245 %58 %289 %58 %58 %290\n%292 = OpLoad  %6  %291\nOpLine %3 132 21\n%293 = OpLoad  %7  %249\n%294 = OpLoad  %7  %249\nOpLine %3 132 21\n%295 = OpAccessChain  %76  %245 %58 %293 %58 %294 %58\n%296 = OpLoad  %6  %295\nOpLine %3 133 21\n%297 = OpLoad  %7  %249\n%298 = OpLoad  %7  %249\n%299 = OpLoad  %7  %249\n%300 = OpAccessChain  %76  %245 %58 %297 %58 %298 %299\n%301 = OpLoad  %6  %300\nOpLine %3 136 17\n%302 = OpLoad  %16  %247\n%303 = OpFunctionCall  %12  %222 %302\nOpLine %3 137 17\n%306 = OpAccessChain  %305  %247 %58\n%307 = OpLoad  %15  %306\n%308 = OpFunctionCall  %9  %227 %307\nOpLine %3 138 19\nOpLine %3 138 19\n%309 = OpAccessChain  %141  %247 %58 %58\n%310 = OpAccessChain  %91  %309 %58\n%311 = OpLoad  %5  %310\n%312 = OpAccessChain  %91  %309 %170\n%313 = OpLoad  %5  %312\n%314 = OpCompositeConstruct  %4  %311 %313\nOpLine %3 139 19\n%315 = OpLoad  %7  %249\n%316 = OpAccessChain  %141  %247 %58 %315\n%317 = OpAccessChain  %91  %316 %58\n%318 = OpLoad  %5  %317\n%319 = OpAccessChain  %91  %316 %170\n%320 = OpLoad  %5  %319\n%321 = OpCompositeConstruct  %4  %318 %320\nOpLine %3 140 20\nOpLine %3 140 20\nOpLine %3 140 20\n%322 = OpAccessChain  %91  %247 %58 %58 %58\n%323 = OpLoad  %5  %322\nOpLine %3 141 20\nOpLine %3 141 20\n%324 = OpLoad  %7  %249\n%325 = OpAccessChain  %141  %247 %58 %58\n%326 = OpAccessChain  %91  %325 %58\n%327 = OpLoad  %5  %326\n%328 = OpAccessChain  %91  %325 %170\n%329 = OpLoad  %5  %328\n%330 = OpCompositeConstruct  %4  %327 %329\n%331 = OpBitcast  %11  %324\n%332 = OpFunctionCall  %5  %42 %330 %331\nOpLine %3 142 20\n%333 = OpLoad  %7  %249\nOpLine %3 142 20\n%334 = OpAccessChain  %91  %247 %58 %333 %58\n%335 = OpLoad  %5  %334\nOpLine %3 143 20\n%336 = OpLoad  %7  %249\n%337 = OpLoad  %7  %249\n%338 = OpAccessChain  %141  %247 %58 %336\n%339 = OpAccessChain  %91  %338 %58\n%340 = OpLoad  %5  %339\n%341 = OpAccessChain  %91  %338 %170\n%342 = OpLoad  %5  %341\n%343 = OpCompositeConstruct  %4  %340 %342\n%344 = OpBitcast  %11  %337\n%345 = OpFunctionCall  %5  %42 %343 %344\nOpLine %3 144 21\nOpLine %3 144 21\nOpLine %3 144 21\nOpLine %3 144 21\n%346 = OpAccessChain  %99  %247 %58 %58 %58 %58\n%347 = OpLoad  %6  %346\nOpLine %3 145 21\nOpLine %3 145 21\nOpLine %3 145 21\n%348 = OpLoad  %7  %249\n%349 = OpAccessChain  %99  %247 %58 %58 %58 %348\n%350 = OpLoad  %6  %349\nOpLine %3 146 21\nOpLine %3 146 21\n%351 = OpLoad  %7  %249\nOpLine %3 146 21\n%352 = OpAccessChain  %141  %247 %58 %58\n%353 = OpAccessChain  %91  %352 %58\n%354 = OpLoad  %5  %353\n%355 = OpAccessChain  %91  %352 %170\n%356 = OpLoad  %5  %355\n%357 = OpCompositeConstruct  %4  %354 %356\n%358 = OpBitcast  %11  %351\n%359 = OpFunctionCall  %5  %42 %357 %358\n%360 = OpCompositeExtract  %6  %359 0\nOpLine %3 147 21\nOpLine %3 147 21\n%361 = OpLoad  %7  %249\n%362 = OpLoad  %7  %249\n%363 = OpAccessChain  %141  %247 %58 %58\n%364 = OpAccessChain  %91  %363 %58\n%365 = OpLoad  %5  %364\n%366 = OpAccessChain  %91  %363 %170\n%367 = OpLoad  %5  %366\n%368 = OpCompositeConstruct  %4  %365 %367\n%369 = OpBitcast  %11  %361\n%370 = OpFunctionCall  %5  %42 %368 %369\n%371 = OpVectorExtractDynamic  %6  %370 %362\nOpLine %3 148 21\n%372 = OpLoad  %7  %249\nOpLine %3 148 21\nOpLine %3 148 21\n%373 = OpAccessChain  %99  %247 %58 %372 %58 %58\n%374 = OpLoad  %6  %373\nOpLine %3 149 21\n%375 = OpLoad  %7  %249\nOpLine %3 149 21\n%376 = OpLoad  %7  %249\n%377 = OpAccessChain  %99  %247 %58 %375 %58 %376\n%378 = OpLoad  %6  %377\nOpLine %3 150 21\n%379 = OpLoad  %7  %249\n%380 = OpLoad  %7  %249\nOpLine %3 150 21\n%381 = OpAccessChain  %141  %247 %58 %379\n%382 = OpAccessChain  %91  %381 %58\n%383 = OpLoad  %5  %382\n%384 = OpAccessChain  %91  %381 %170\n%385 = OpLoad  %5  %384\n%386 = OpCompositeConstruct  %4  %383 %385\n%387 = OpBitcast  %11  %380\n%388 = OpFunctionCall  %5  %42 %386 %387\n%389 = OpCompositeExtract  %6  %388 0\nOpLine %3 151 21\n%390 = OpLoad  %7  %249\n%391 = OpLoad  %7  %249\n%392 = OpLoad  %7  %249\n%393 = OpAccessChain  %141  %247 %58 %390\n%394 = OpAccessChain  %91  %393 %58\n%395 = OpLoad  %5  %394\n%396 = OpAccessChain  %91  %393 %170\n%397 = OpLoad  %5  %396\n%398 = OpCompositeConstruct  %4  %395 %397\n%399 = OpBitcast  %11  %391\n%400 = OpFunctionCall  %5  %42 %398 %399\n%401 = OpVectorExtractDynamic  %6  %400 %392\nOpLine %3 154 5\nOpStore %245 %303\nOpLine %3 155 5\nOpLine %3 155 5\n%402 = OpAccessChain  %254  %245 %58\nOpStore %402 %308\nOpLine %3 156 5\nOpLine %3 156 5\nOpLine %3 156 5\n%403 = OpAccessChain  %57  %245 %58 %58 %58\nOpStore %403 %314\nOpLine %3 157 5\n%404 = OpLoad  %7  %249\nOpLine %3 157 5\n%405 = OpAccessChain  %57  %245 %58 %404 %58\nOpStore %405 %321\nOpLine %3 158 5\nOpLine %3 158 5\nOpLine %3 158 5\nOpLine %3 158 5\n%406 = OpAccessChain  %70  %245 %58 %58 %58 %58\nOpStore %406 %323\nOpLine %3 159 5\nOpLine %3 159 5\n%407 = OpLoad  %7  %249\nOpLine %3 159 5\n%408 = OpAccessChain  %70  %245 %58 %58 %58 %407\nOpStore %408 %332\nOpLine %3 160 5\n%409 = OpLoad  %7  %249\nOpLine %3 160 5\nOpLine %3 160 5\n%410 = OpAccessChain  %70  %245 %58 %409 %58 %58\nOpStore %410 %335\nOpLine %3 161 5\n%411 = OpLoad  %7  %249\n%412 = OpLoad  %7  %249\nOpLine %3 161 5\n%413 = OpAccessChain  %70  %245 %58 %411 %58 %412\nOpStore %413 %345\nOpLine %3 162 5\nOpLine %3 162 5\nOpLine %3 162 5\nOpLine %3 162 5\nOpLine %3 162 5\n%414 = OpAccessChain  %76  %245 %58 %58 %58 %58 %58\nOpStore %414 %347\nOpLine %3 163 5\nOpLine %3 163 5\nOpLine %3 163 5\n%415 = OpLoad  %7  %249\nOpLine %3 163 5\n%416 = OpAccessChain  %76  %245 %58 %58 %58 %58 %415\nOpStore %416 %350\nOpLine %3 164 5\nOpLine %3 164 5\n%417 = OpLoad  %7  %249\nOpLine %3 164 5\nOpLine %3 164 5\n%418 = OpAccessChain  %76  %245 %58 %58 %58 %417 %58\nOpStore %418 %360\nOpLine %3 165 5\nOpLine %3 165 5\n%419 = OpLoad  %7  %249\n%420 = OpLoad  %7  %249\nOpLine %3 165 5\n%421 = OpAccessChain  %76  %245 %58 %58 %58 %419 %420\nOpStore %421 %371\nOpLine %3 166 5\n%422 = OpLoad  %7  %249\nOpLine %3 166 5\nOpLine %3 166 5\nOpLine %3 166 5\n%423 = OpAccessChain  %76  %245 %58 %422 %58 %58 %58\nOpStore %423 %374\nOpLine %3 167 5\n%424 = OpLoad  %7  %249\nOpLine %3 167 5\n%425 = OpLoad  %7  %249\nOpLine %3 167 5\n%426 = OpAccessChain  %76  %245 %58 %424 %58 %58 %425\nOpStore %426 %378\nOpLine %3 168 5\n%427 = OpLoad  %7  %249\n%428 = OpLoad  %7  %249\nOpLine %3 168 5\nOpLine %3 168 5\n%429 = OpAccessChain  %76  %245 %58 %427 %58 %428 %58\nOpStore %429 %389\nOpLine %3 169 5\n%430 = OpLoad  %7  %249\n%431 = OpLoad  %7  %249\n%432 = OpLoad  %7  %249\nOpLine %3 169 5\n%433 = OpAccessChain  %76  %245 %58 %430 %58 %431 %432\nOpStore %433 %401\nOpReturn\nOpFunctionEnd\n%435 = OpFunction  %2  None %56\n%434 = OpLabel\n%436 = OpAccessChain  %57  %17 %58\n%437 = OpAccessChain  %60  %20 %58\n%438 = OpAccessChain  %139  %23 %58\n%439 = OpAccessChain  %141  %26 %58\n%440 = OpAccessChain  %244  %29 %58\n%441 = OpAccessChain  %246  %32 %58\nOpBranch %442\n%442 = OpLabel\nOpLine %3 174 5\n%443 = OpFunctionCall  %2  %55\nOpLine %3 175 5\n%444 = OpFunctionCall  %2  %138\nOpLine %3 176 5\n%445 = OpFunctionCall  %2  %243\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mat_cx3.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 309\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %298 \"main\"\nOpExecutionMode %298 LocalSize 1 1 1\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 0 ColMajor\nOpMemberDecorate %7 0 MatrixStride 16\nOpDecorate %8 ArrayStride 48\nOpMemberDecorate %11 0 Offset 0\nOpDecorate %12 DescriptorSet 0\nOpDecorate %12 Binding 0\nOpDecorate %13 Block\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 0 ColMajor\nOpMemberDecorate %13 0 MatrixStride 16\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 1\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpMemberDecorate %16 0 ColMajor\nOpMemberDecorate %16 0 MatrixStride 16\nOpDecorate %18 DescriptorSet 1\nOpDecorate %18 Binding 0\nOpDecorate %19 Block\nOpMemberDecorate %19 0 Offset 0\nOpDecorate %21 DescriptorSet 1\nOpDecorate %21 Binding 1\nOpDecorate %22 Block\nOpMemberDecorate %22 0 Offset 0\nOpDecorate %24 DescriptorSet 2\nOpDecorate %24 Binding 0\nOpDecorate %25 Block\nOpMemberDecorate %25 0 Offset 0\nOpDecorate %27 DescriptorSet 2\nOpDecorate %27 Binding 1\nOpDecorate %28 Block\nOpMemberDecorate %28 0 Offset 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeVector %5 3\n%3 = OpTypeMatrix %4 3\n%6 = OpTypeInt 32 1\n%7 = OpTypeStruct %3\n%10 = OpTypeInt 32 0\n%9 = OpConstant  %10  4\n%8 = OpTypeArray %7 %9\n%11 = OpTypeStruct %8\n%13 = OpTypeStruct %3\n%14 = OpTypePointer StorageBuffer %13\n%12 = OpVariable  %14  StorageBuffer\n%16 = OpTypeStruct %3\n%17 = OpTypePointer Uniform %16\n%15 = OpVariable  %17  Uniform\n%19 = OpTypeStruct %7\n%20 = OpTypePointer StorageBuffer %19\n%18 = OpVariable  %20  StorageBuffer\n%22 = OpTypeStruct %7\n%23 = OpTypePointer Uniform %22\n%21 = OpVariable  %23  Uniform\n%25 = OpTypeStruct %11\n%26 = OpTypePointer StorageBuffer %25\n%24 = OpVariable  %26  StorageBuffer\n%28 = OpTypeStruct %11\n%29 = OpTypePointer Uniform %28\n%27 = OpVariable  %29  Uniform\n%32 = OpTypeFunction %2\n%33 = OpTypePointer StorageBuffer %3\n%34 = OpConstant  %10  0\n%36 = OpTypePointer Uniform %3\n%38 = OpConstant  %6  1\n%40 = OpTypePointer Function %6\n%45 = OpTypePointer StorageBuffer %4\n%51 = OpTypePointer StorageBuffer %5\n%65 = OpTypePointer Uniform %4\n%71 = OpTypePointer Uniform %5\n%97 = OpTypePointer StorageBuffer %7\n%99 = OpTypePointer Uniform %7\n%159 = OpTypePointer StorageBuffer %11\n%161 = OpTypePointer Uniform %11\n%168 = OpTypePointer StorageBuffer %8\n%217 = OpTypePointer Uniform %8\n%31 = OpFunction  %2  None %32\n%30 = OpLabel\n%39 = OpVariable  %40  Function %38\n%35 = OpAccessChain  %33  %12 %34\n%37 = OpAccessChain  %36  %15 %34\nOpBranch %41\n%41 = OpLabel\n%42 = OpLoad  %6  %39\n%43 = OpISub  %6  %42 %38\nOpStore %39 %43\n%44 = OpLoad  %3  %35\n%46 = OpAccessChain  %45  %35 %34\n%47 = OpLoad  %4  %46\n%48 = OpLoad  %6  %39\n%49 = OpAccessChain  %45  %35 %48\n%50 = OpLoad  %4  %49\n%52 = OpAccessChain  %51  %35 %34 %34\n%53 = OpLoad  %5  %52\n%54 = OpLoad  %6  %39\n%55 = OpAccessChain  %51  %35 %34 %54\n%56 = OpLoad  %5  %55\n%57 = OpLoad  %6  %39\n%58 = OpAccessChain  %51  %35 %57 %34\n%59 = OpLoad  %5  %58\n%60 = OpLoad  %6  %39\n%61 = OpLoad  %6  %39\n%62 = OpAccessChain  %51  %35 %60 %61\n%63 = OpLoad  %5  %62\n%64 = OpLoad  %3  %37\n%66 = OpAccessChain  %65  %37 %34\n%67 = OpLoad  %4  %66\n%68 = OpLoad  %6  %39\n%69 = OpAccessChain  %65  %37 %68\n%70 = OpLoad  %4  %69\n%72 = OpAccessChain  %71  %37 %34 %34\n%73 = OpLoad  %5  %72\n%74 = OpLoad  %6  %39\n%75 = OpAccessChain  %71  %37 %34 %74\n%76 = OpLoad  %5  %75\n%77 = OpLoad  %6  %39\n%78 = OpAccessChain  %71  %37 %77 %34\n%79 = OpLoad  %5  %78\n%80 = OpLoad  %6  %39\n%81 = OpLoad  %6  %39\n%82 = OpAccessChain  %71  %37 %80 %81\n%83 = OpLoad  %5  %82\nOpStore %35 %64\n%84 = OpAccessChain  %45  %35 %34\nOpStore %84 %67\n%85 = OpLoad  %6  %39\n%86 = OpAccessChain  %45  %35 %85\nOpStore %86 %70\n%87 = OpAccessChain  %51  %35 %34 %34\nOpStore %87 %73\n%88 = OpLoad  %6  %39\n%89 = OpAccessChain  %51  %35 %34 %88\nOpStore %89 %76\n%90 = OpLoad  %6  %39\n%91 = OpAccessChain  %51  %35 %90 %34\nOpStore %91 %79\n%92 = OpLoad  %6  %39\n%93 = OpLoad  %6  %39\n%94 = OpAccessChain  %51  %35 %92 %93\nOpStore %94 %83\nOpReturn\nOpFunctionEnd\n%96 = OpFunction  %2  None %32\n%95 = OpLabel\n%101 = OpVariable  %40  Function %38\n%98 = OpAccessChain  %97  %18 %34\n%100 = OpAccessChain  %99  %21 %34\nOpBranch %102\n%102 = OpLabel\n%103 = OpLoad  %6  %101\n%104 = OpISub  %6  %103 %38\nOpStore %101 %104\n%105 = OpLoad  %7  %98\n%106 = OpAccessChain  %33  %98 %34\n%107 = OpLoad  %3  %106\n%108 = OpAccessChain  %45  %98 %34 %34\n%109 = OpLoad  %4  %108\n%110 = OpLoad  %6  %101\n%111 = OpAccessChain  %45  %98 %34 %110\n%112 = OpLoad  %4  %111\n%113 = OpAccessChain  %51  %98 %34 %34 %34\n%114 = OpLoad  %5  %113\n%115 = OpLoad  %6  %101\n%116 = OpAccessChain  %51  %98 %34 %34 %115\n%117 = OpLoad  %5  %116\n%118 = OpLoad  %6  %101\n%119 = OpAccessChain  %51  %98 %34 %118 %34\n%120 = OpLoad  %5  %119\n%121 = OpLoad  %6  %101\n%122 = OpLoad  %6  %101\n%123 = OpAccessChain  %51  %98 %34 %121 %122\n%124 = OpLoad  %5  %123\n%125 = OpLoad  %7  %100\n%126 = OpAccessChain  %36  %100 %34\n%127 = OpLoad  %3  %126\n%128 = OpAccessChain  %65  %100 %34 %34\n%129 = OpLoad  %4  %128\n%130 = OpLoad  %6  %101\n%131 = OpAccessChain  %65  %100 %34 %130\n%132 = OpLoad  %4  %131\n%133 = OpAccessChain  %71  %100 %34 %34 %34\n%134 = OpLoad  %5  %133\n%135 = OpLoad  %6  %101\n%136 = OpAccessChain  %71  %100 %34 %34 %135\n%137 = OpLoad  %5  %136\n%138 = OpLoad  %6  %101\n%139 = OpAccessChain  %71  %100 %34 %138 %34\n%140 = OpLoad  %5  %139\n%141 = OpLoad  %6  %101\n%142 = OpLoad  %6  %101\n%143 = OpAccessChain  %71  %100 %34 %141 %142\n%144 = OpLoad  %5  %143\nOpStore %98 %125\n%145 = OpAccessChain  %33  %98 %34\nOpStore %145 %127\n%146 = OpAccessChain  %45  %98 %34 %34\nOpStore %146 %129\n%147 = OpLoad  %6  %101\n%148 = OpAccessChain  %45  %98 %34 %147\nOpStore %148 %132\n%149 = OpAccessChain  %51  %98 %34 %34 %34\nOpStore %149 %134\n%150 = OpLoad  %6  %101\n%151 = OpAccessChain  %51  %98 %34 %34 %150\nOpStore %151 %137\n%152 = OpLoad  %6  %101\n%153 = OpAccessChain  %51  %98 %34 %152 %34\nOpStore %153 %140\n%154 = OpLoad  %6  %101\n%155 = OpLoad  %6  %101\n%156 = OpAccessChain  %51  %98 %34 %154 %155\nOpStore %156 %144\nOpReturn\nOpFunctionEnd\n%158 = OpFunction  %2  None %32\n%157 = OpLabel\n%163 = OpVariable  %40  Function %38\n%160 = OpAccessChain  %159  %24 %34\n%162 = OpAccessChain  %161  %27 %34\nOpBranch %164\n%164 = OpLabel\n%165 = OpLoad  %6  %163\n%166 = OpISub  %6  %165 %38\nOpStore %163 %166\n%167 = OpLoad  %11  %160\n%169 = OpAccessChain  %168  %160 %34\n%170 = OpLoad  %8  %169\n%171 = OpAccessChain  %33  %160 %34 %34 %34\n%172 = OpLoad  %3  %171\n%173 = OpLoad  %6  %163\n%174 = OpAccessChain  %33  %160 %34 %173 %34\n%175 = OpLoad  %3  %174\n%176 = OpAccessChain  %45  %160 %34 %34 %34 %34\n%177 = OpLoad  %4  %176\n%178 = OpLoad  %6  %163\n%179 = OpAccessChain  %45  %160 %34 %34 %34 %178\n%180 = OpLoad  %4  %179\n%181 = OpLoad  %6  %163\n%182 = OpAccessChain  %45  %160 %34 %181 %34 %34\n%183 = OpLoad  %4  %182\n%184 = OpLoad  %6  %163\n%185 = OpLoad  %6  %163\n%186 = OpAccessChain  %45  %160 %34 %184 %34 %185\n%187 = OpLoad  %4  %186\n%188 = OpAccessChain  %51  %160 %34 %34 %34 %34 %34\n%189 = OpLoad  %5  %188\n%190 = OpLoad  %6  %163\n%191 = OpAccessChain  %51  %160 %34 %34 %34 %34 %190\n%192 = OpLoad  %5  %191\n%193 = OpLoad  %6  %163\n%194 = OpAccessChain  %51  %160 %34 %34 %34 %193 %34\n%195 = OpLoad  %5  %194\n%196 = OpLoad  %6  %163\n%197 = OpLoad  %6  %163\n%198 = OpAccessChain  %51  %160 %34 %34 %34 %196 %197\n%199 = OpLoad  %5  %198\n%200 = OpLoad  %6  %163\n%201 = OpAccessChain  %51  %160 %34 %200 %34 %34 %34\n%202 = OpLoad  %5  %201\n%203 = OpLoad  %6  %163\n%204 = OpLoad  %6  %163\n%205 = OpAccessChain  %51  %160 %34 %203 %34 %34 %204\n%206 = OpLoad  %5  %205\n%207 = OpLoad  %6  %163\n%208 = OpLoad  %6  %163\n%209 = OpAccessChain  %51  %160 %34 %207 %34 %208 %34\n%210 = OpLoad  %5  %209\n%211 = OpLoad  %6  %163\n%212 = OpLoad  %6  %163\n%213 = OpLoad  %6  %163\n%214 = OpAccessChain  %51  %160 %34 %211 %34 %212 %213\n%215 = OpLoad  %5  %214\n%216 = OpLoad  %11  %162\n%218 = OpAccessChain  %217  %162 %34\n%219 = OpLoad  %8  %218\n%220 = OpAccessChain  %36  %162 %34 %34 %34\n%221 = OpLoad  %3  %220\n%222 = OpLoad  %6  %163\n%223 = OpAccessChain  %36  %162 %34 %222 %34\n%224 = OpLoad  %3  %223\n%225 = OpAccessChain  %65  %162 %34 %34 %34 %34\n%226 = OpLoad  %4  %225\n%227 = OpLoad  %6  %163\n%228 = OpAccessChain  %65  %162 %34 %34 %34 %227\n%229 = OpLoad  %4  %228\n%230 = OpLoad  %6  %163\n%231 = OpAccessChain  %65  %162 %34 %230 %34 %34\n%232 = OpLoad  %4  %231\n%233 = OpLoad  %6  %163\n%234 = OpLoad  %6  %163\n%235 = OpAccessChain  %65  %162 %34 %233 %34 %234\n%236 = OpLoad  %4  %235\n%237 = OpAccessChain  %71  %162 %34 %34 %34 %34 %34\n%238 = OpLoad  %5  %237\n%239 = OpLoad  %6  %163\n%240 = OpAccessChain  %71  %162 %34 %34 %34 %34 %239\n%241 = OpLoad  %5  %240\n%242 = OpLoad  %6  %163\n%243 = OpAccessChain  %71  %162 %34 %34 %34 %242 %34\n%244 = OpLoad  %5  %243\n%245 = OpLoad  %6  %163\n%246 = OpLoad  %6  %163\n%247 = OpAccessChain  %71  %162 %34 %34 %34 %245 %246\n%248 = OpLoad  %5  %247\n%249 = OpLoad  %6  %163\n%250 = OpAccessChain  %71  %162 %34 %249 %34 %34 %34\n%251 = OpLoad  %5  %250\n%252 = OpLoad  %6  %163\n%253 = OpLoad  %6  %163\n%254 = OpAccessChain  %71  %162 %34 %252 %34 %34 %253\n%255 = OpLoad  %5  %254\n%256 = OpLoad  %6  %163\n%257 = OpLoad  %6  %163\n%258 = OpAccessChain  %71  %162 %34 %256 %34 %257 %34\n%259 = OpLoad  %5  %258\n%260 = OpLoad  %6  %163\n%261 = OpLoad  %6  %163\n%262 = OpLoad  %6  %163\n%263 = OpAccessChain  %71  %162 %34 %260 %34 %261 %262\n%264 = OpLoad  %5  %263\nOpStore %160 %216\n%265 = OpAccessChain  %168  %160 %34\nOpStore %265 %219\n%266 = OpAccessChain  %33  %160 %34 %34 %34\nOpStore %266 %221\n%267 = OpLoad  %6  %163\n%268 = OpAccessChain  %33  %160 %34 %267 %34\nOpStore %268 %224\n%269 = OpAccessChain  %45  %160 %34 %34 %34 %34\nOpStore %269 %226\n%270 = OpLoad  %6  %163\n%271 = OpAccessChain  %45  %160 %34 %34 %34 %270\nOpStore %271 %229\n%272 = OpLoad  %6  %163\n%273 = OpAccessChain  %45  %160 %34 %272 %34 %34\nOpStore %273 %232\n%274 = OpLoad  %6  %163\n%275 = OpLoad  %6  %163\n%276 = OpAccessChain  %45  %160 %34 %274 %34 %275\nOpStore %276 %236\n%277 = OpAccessChain  %51  %160 %34 %34 %34 %34 %34\nOpStore %277 %238\n%278 = OpLoad  %6  %163\n%279 = OpAccessChain  %51  %160 %34 %34 %34 %34 %278\nOpStore %279 %241\n%280 = OpLoad  %6  %163\n%281 = OpAccessChain  %51  %160 %34 %34 %34 %280 %34\nOpStore %281 %244\n%282 = OpLoad  %6  %163\n%283 = OpLoad  %6  %163\n%284 = OpAccessChain  %51  %160 %34 %34 %34 %282 %283\nOpStore %284 %248\n%285 = OpLoad  %6  %163\n%286 = OpAccessChain  %51  %160 %34 %285 %34 %34 %34\nOpStore %286 %251\n%287 = OpLoad  %6  %163\n%288 = OpLoad  %6  %163\n%289 = OpAccessChain  %51  %160 %34 %287 %34 %34 %288\nOpStore %289 %255\n%290 = OpLoad  %6  %163\n%291 = OpLoad  %6  %163\n%292 = OpAccessChain  %51  %160 %34 %290 %34 %291 %34\nOpStore %292 %259\n%293 = OpLoad  %6  %163\n%294 = OpLoad  %6  %163\n%295 = OpLoad  %6  %163\n%296 = OpAccessChain  %51  %160 %34 %293 %34 %294 %295\nOpStore %296 %264\nOpReturn\nOpFunctionEnd\n%298 = OpFunction  %2  None %32\n%297 = OpLabel\n%299 = OpAccessChain  %33  %12 %34\n%300 = OpAccessChain  %36  %15 %34\n%301 = OpAccessChain  %97  %18 %34\n%302 = OpAccessChain  %99  %21 %34\n%303 = OpAccessChain  %159  %24 %34\n%304 = OpAccessChain  %161  %27 %34\nOpBranch %305\n%305 = OpLabel\n%306 = OpFunctionCall  %2  %31\n%307 = OpFunctionCall  %2  %96\n%308 = OpFunctionCall  %2  %158\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-math-functions.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 86\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %18 \"main\"\nOpExecutionMode %18 OriginUpperLeft\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 4\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 8\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 16\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 4\nOpMemberDecorate %15 0 Offset 0\nOpMemberDecorate %15 1 Offset 16\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeInt 32 1\n%6 = OpTypeVector %5 4\n%7 = OpTypeVector %5 2\n%9 = OpTypeInt 32 0\n%8 = OpTypeVector %9 2\n%10 = OpTypeVector %3 2\n%11 = OpTypeStruct %3 %3\n%12 = OpTypeStruct %10 %10\n%13 = OpTypeStruct %4 %4\n%14 = OpTypeStruct %3 %5\n%15 = OpTypeStruct %4 %6\n%16 = OpTypeVector %3 3\n%19 = OpTypeFunction %2\n%20 = OpConstant  %3  1\n%21 = OpConstant  %3  0\n%22 = OpConstantComposite  %4  %21 %21 %21 %21\n%23 = OpConstant  %5  -1\n%24 = OpConstantComposite  %6  %23 %23 %23 %23\n%25 = OpConstant  %3  -1\n%26 = OpConstantComposite  %4  %25 %25 %25 %25\n%27 = OpConstantComposite  %4  %21 %21 %21 %21\n%28 = OpConstant  %5  0\n%29 = OpConstant  %9  4294967295\n%30 = OpConstantComposite  %7  %23 %23\n%31 = OpConstant  %9  0\n%32 = OpConstantComposite  %8  %31 %31\n%33 = OpConstantComposite  %7  %28 %28\n%34 = OpConstant  %9  32\n%35 = OpConstant  %5  32\n%36 = OpConstantComposite  %8  %34 %34\n%37 = OpConstantComposite  %7  %35 %35\n%38 = OpConstant  %9  31\n%39 = OpConstantComposite  %8  %38 %38\n%40 = OpConstant  %5  2\n%41 = OpConstant  %3  2\n%42 = OpConstantComposite  %10  %20 %41\n%43 = OpConstant  %5  3\n%44 = OpConstant  %5  4\n%45 = OpConstantComposite  %7  %43 %44\n%46 = OpConstant  %3  1.5\n%47 = OpConstantComposite  %10  %46 %46\n%48 = OpConstantComposite  %4  %46 %46 %46 %46\n%49 = OpConstantComposite  %10  %20 %20\n%50 = OpConstantComposite  %16  %20 %20 %20\n%51 = OpConstantComposite  %4  %20 %20 %20 %20\n%58 = OpConstantComposite  %4  %20 %20 %20 %20\n%18 = OpFunction  %2  None %19\n%17 = OpLabel\nOpBranch %52\n%52 = OpLabel\n%53 = OpExtInst  %3  %1 Degrees %20\n%54 = OpExtInst  %3  %1 Radians %20\n%55 = OpExtInst  %4  %1 Degrees %22\n%56 = OpExtInst  %4  %1 Radians %22\n%57 = OpExtInst  %4  %1 FClamp %22 %22 %58\n%59 = OpExtInst  %4  %1 Refract %22 %22 %20\n%60 = OpExtInst  %3  %1 Ldexp %20 %40\n%61 = OpExtInst  %10  %1 Ldexp %42 %45\n%62 = OpExtInst  %11  %1 ModfStruct %46\n%63 = OpExtInst  %11  %1 ModfStruct %46\n%64 = OpCompositeExtract  %3  %63 0\n%65 = OpExtInst  %11  %1 ModfStruct %46\n%66 = OpCompositeExtract  %3  %65 1\n%67 = OpExtInst  %12  %1 ModfStruct %47\n%68 = OpExtInst  %13  %1 ModfStruct %48\n%69 = OpCompositeExtract  %4  %68 1\n%70 = OpCompositeExtract  %3  %69 0\n%71 = OpExtInst  %12  %1 ModfStruct %47\n%72 = OpCompositeExtract  %10  %71 0\n%73 = OpCompositeExtract  %3  %72 1\n%74 = OpExtInst  %14  %1 FrexpStruct %46\n%75 = OpExtInst  %14  %1 FrexpStruct %46\n%76 = OpCompositeExtract  %3  %75 0\n%77 = OpExtInst  %14  %1 FrexpStruct %46\n%78 = OpCompositeExtract  %5  %77 1\n%79 = OpExtInst  %15  %1 FrexpStruct %48\n%80 = OpCompositeExtract  %6  %79 1\n%81 = OpCompositeExtract  %5  %80 0\n%82 = OpQuantizeToF16  %3  %20\n%83 = OpQuantizeToF16  %10  %49\n%84 = OpQuantizeToF16  %16  %50\n%85 = OpQuantizeToF16  %4  %51\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-memory-decorations-coherent.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 19\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %10 \"main\"\nOpExecutionMode %10 LocalSize 1 1 1\nOpDecorate %4 ArrayStride 4\nOpMemberDecorate %5 0 Offset 0\nOpDecorate %5 Block\nOpDecorate %6 Coherent\nOpDecorate %6 DescriptorSet 0\nOpDecorate %6 Binding 0\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeRuntimeArray %3\n%5 = OpTypeStruct %4\n%7 = OpTypePointer StorageBuffer %5\n%6 = OpVariable  %7  StorageBuffer\n%8 = OpVariable  %7  StorageBuffer\n%11 = OpTypeFunction %2\n%13 = OpTypePointer StorageBuffer %4\n%14 = OpTypePointer StorageBuffer %3\n%15 = OpConstant  %3  0\n%10 = OpFunction  %2  None %11\n%9 = OpLabel\nOpBranch %12\n%12 = OpLabel\n%16 = OpAccessChain  %14  %8 %15 %15\n%17 = OpLoad  %3  %16\n%18 = OpAccessChain  %14  %6 %15 %15\nOpStore %18 %17\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-memory-decorations.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 24\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %12 \"main\"\nOpExecutionMode %12 LocalSize 1 1 1\nOpDecorate %4 ArrayStride 4\nOpMemberDecorate %5 0 Offset 0\nOpDecorate %5 Block\nOpDecorate %6 Coherent\nOpDecorate %6 DescriptorSet 0\nOpDecorate %6 Binding 0\nOpDecorate %8 Volatile\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 1\nOpDecorate %9 Coherent\nOpDecorate %9 Volatile\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 2\nOpDecorate %10 DescriptorSet 0\nOpDecorate %10 Binding 3\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeRuntimeArray %3\n%5 = OpTypeStruct %4\n%7 = OpTypePointer StorageBuffer %5\n%6 = OpVariable  %7  StorageBuffer\n%8 = OpVariable  %7  StorageBuffer\n%9 = OpVariable  %7  StorageBuffer\n%10 = OpVariable  %7  StorageBuffer\n%13 = OpTypeFunction %2\n%15 = OpTypePointer StorageBuffer %4\n%16 = OpTypePointer StorageBuffer %3\n%17 = OpConstant  %3  0\n%12 = OpFunction  %2  None %13\n%11 = OpLabel\nOpBranch %14\n%14 = OpLabel\n%18 = OpAccessChain  %16  %8 %17 %17\n%19 = OpLoad  %3  %18\n%20 = OpAccessChain  %16  %6 %17 %17\nOpStore %20 %19\n%21 = OpAccessChain  %16  %10 %17 %17\n%22 = OpLoad  %3  %21\n%23 = OpAccessChain  %16  %9 %17 %17\nOpStore %23 %22\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mesh-shader-empty.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 135\nOpCapability Shader\nOpCapability MeshShadingEXT\nOpExtension \"SPV_EXT_mesh_shader\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint TaskEXT %36 \"ts_main\" %15 %25\nOpEntryPoint MeshEXT %87 \"ms_main\" %15 %69 %74 %77 %17 %81\nOpExecutionMode %36 LocalSize 64 1 1\nOpExecutionMode %87 LocalSize 64 1 1\nOpExecutionMode %87 OutputTrianglesEXT\nOpExecutionMode %87 OutputVertices 3\nOpExecutionMode %87 OutputPrimitivesEXT 1\nOpDecorate %69 BuiltIn LocalInvocationIndex\nOpMemberDecorate %71 0 BuiltIn Position\nOpDecorate %71 Block\nOpDecorate %77 BuiltIn PrimitiveTriangleIndicesEXT\nOpMemberDecorate %4 0 Offset 0\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %9 0 Offset 0\nOpDecorate %10 ArrayStride 16\nOpDecorate %12 ArrayStride 16\nOpMemberDecorate %14 0 Offset 0\nOpMemberDecorate %14 1 Offset 48\nOpMemberDecorate %14 2 Offset 64\nOpMemberDecorate %14 3 Offset 68\nOpDecorate %25 BuiltIn LocalInvocationIndex\nOpDecorate %81 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 4\n%7 = OpTypeStruct %5\n%8 = OpTypeVector %3 3\n%9 = OpTypeStruct %8\n%11 = OpConstant  %3  3\n%10 = OpTypeArray %7 %11\n%13 = OpConstant  %3  1\n%12 = OpTypeArray %9 %13\n%14 = OpTypeStruct %10 %12 %3 %3\n%16 = OpTypePointer TaskPayloadWorkgroupEXT %4\n%15 = OpVariable  %16  TaskPayloadWorkgroupEXT\n%18 = OpTypePointer Workgroup %14\n%17 = OpVariable  %18  Workgroup\n%21 = OpTypeFunction %8\n%22 = OpConstantComposite  %8  %13 %13 %13\n%24 = OpConstantNull  %4\n%26 = OpTypePointer Input %3\n%25 = OpVariable  %26  Input\n%28 = OpConstant  %3  0\n%30 = OpTypeBool\n%33 = OpConstant  %3  2\n%34 = OpConstant  %3  264\n%37 = OpTypeFunction %2\n%40 = OpConstant  %3  256\n%41 = OpConstant  %3  1024\n%42 = OpTypeStruct %3 %3\n%69 = OpVariable  %26  Input\n%70 = OpConstant  %3  64\n%71 = OpTypeStruct %5\n%72 = OpTypeArray %71 %11\n%73 = OpTypePointer Output %72\n%74 = OpVariable  %73  Output\n%75 = OpTypeArray %8 %13\n%76 = OpTypePointer Output %75\n%77 = OpVariable  %76  Output\n%80 = OpConstantNull  %14\n%81 = OpVariable  %26  Input\n%91 = OpTypePointer Function %3\n%95 = OpTypePointer Workgroup %3\n%102 = OpTypePointer Workgroup %10\n%104 = OpTypePointer Workgroup %12\n%111 = OpTypePointer Workgroup %5\n%114 = OpTypePointer Output %5\n%124 = OpTypePointer Workgroup %8\n%127 = OpTypePointer Output %8\n%20 = OpFunction  %8  None %21\n%19 = OpLabel\nOpBranch %23\n%23 = OpLabel\n%27 = OpLoad  %3  %25\n%29 = OpIEqual  %30  %27 %28\nOpSelectionMerge %31 None\nOpBranchConditional %29 %32 %31\n%32 = OpLabel\nOpStore %15 %24\nOpBranch %31\n%31 = OpLabel\nOpControlBarrier %33 %33 %34\nOpBranch %35\n%35 = OpLabel\nOpReturnValue %22\nOpFunctionEnd\n%36 = OpFunction  %2  None %37\n%38 = OpLabel\n%39 = OpFunctionCall  %8  %20\nOpControlBarrier %33 %33 %34\n%43 = OpCompositeExtract  %3  %39 0\n%44 = OpCompositeExtract  %3  %39 1\n%45 = OpCompositeExtract  %3  %39 2\n%49 = OpUMulExtended  %42  %43 %44\n%46 = OpCompositeExtract  %3  %49 0\n%47 = OpCompositeExtract  %3  %49 1\n%51 = OpUMulExtended  %42  %46 %45\n%50 = OpCompositeExtract  %3  %51 0\n%48 = OpCompositeExtract  %3  %51 1\n%52 = OpUGreaterThan  %30  %50 %41\n%53 = OpUGreaterThan  %30  %43 %40\n%54 = OpUGreaterThan  %30  %44 %40\n%55 = OpUGreaterThan  %30  %45 %40\n%56 = OpINotEqual  %30  %47 %28\n%57 = OpINotEqual  %30  %48 %28\n%58 = OpLogicalOr  %30  %52 %53\n%59 = OpLogicalOr  %30  %58 %54\n%60 = OpLogicalOr  %30  %59 %55\n%61 = OpLogicalOr  %30  %60 %56\n%62 = OpLogicalOr  %30  %61 %57\n%63 = OpCompositeConstruct  %8  %28 %28 %28\n%64 = OpSelect  %8  %62 %63 %39\n%65 = OpCompositeExtract  %3  %64 0\n%66 = OpCompositeExtract  %3  %64 1\n%67 = OpCompositeExtract  %3  %64 2\nOpEmitMeshTasksEXT %65 %66 %67 %15\nOpFunctionEnd\n%78 = OpFunction  %2  None %37\n%68 = OpLabel\nOpBranch %79\n%79 = OpLabel\n%82 = OpLoad  %3  %81\n%83 = OpIEqual  %30  %82 %28\nOpSelectionMerge %84 None\nOpBranchConditional %83 %85 %84\n%85 = OpLabel\nOpStore %17 %80\nOpBranch %84\n%84 = OpLabel\nOpControlBarrier %33 %33 %34\nOpBranch %86\n%86 = OpLabel\nOpReturn\nOpFunctionEnd\n%87 = OpFunction  %2  None %37\n%88 = OpLabel\n%89 = OpVariable  %91  Function\n%90 = OpVariable  %91  Function\n%92 = OpLoad  %3  %69\n%93 = OpFunctionCall  %2  %78\nOpControlBarrier %33 %33 %34\n%94 = OpAccessChain  %95  %17 %33\n%96 = OpLoad  %3  %94\n%97 = OpExtInst  %3  %1 UMin %96 %11\n%98 = OpAccessChain  %95  %17 %11\n%99 = OpLoad  %3  %98\n%100 = OpExtInst  %3  %1 UMin %99 %13\n%101 = OpAccessChain  %102  %17 %28\n%103 = OpAccessChain  %104  %17 %13\nOpSetMeshOutputsEXT %97 %100\nOpStore %89 %92\nOpBranch %105\n%105 = OpLabel\nOpLoopMerge %107 %116 None\nOpBranch %115\n%115 = OpLabel\n%118 = OpLoad  %3  %89\n%119 = OpULessThan  %30  %118 %97\nOpBranchConditional %119 %117 %107\n%117 = OpLabel\n%109 = OpLoad  %3  %89\n%110 = OpAccessChain  %111  %101 %109 %28\n%112 = OpLoad  %5  %110\n%113 = OpAccessChain  %114  %74 %109 %28\nOpStore %113 %112\nOpBranch %116\n%116 = OpLabel\n%120 = OpLoad  %3  %89\n%121 = OpIAdd  %3  %120 %70\nOpStore %89 %121\nOpBranch %105\n%107 = OpLabel\nOpStore %90 %92\nOpBranch %106\n%106 = OpLabel\nOpLoopMerge %108 %129 None\nOpBranch %128\n%128 = OpLabel\n%131 = OpLoad  %3  %90\n%132 = OpULessThan  %30  %131 %100\nOpBranchConditional %132 %130 %108\n%130 = OpLabel\n%122 = OpLoad  %3  %90\n%123 = OpAccessChain  %124  %103 %122 %28\n%125 = OpLoad  %8  %123\n%126 = OpAccessChain  %127  %77 %122\nOpStore %126 %125\nOpBranch %129\n%129 = OpLabel\n%133 = OpLoad  %3  %90\n%134 = OpIAdd  %3  %133 %70\nOpStore %90 %134\nOpBranch %106\n%108 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mesh-shader-lines.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 136\nOpCapability Shader\nOpCapability MeshShadingEXT\nOpExtension \"SPV_EXT_mesh_shader\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint TaskEXT %36 \"ts_main\" %16 %26\nOpEntryPoint MeshEXT %87 \"ms_main\" %16 %69 %74 %77 %18 %81\nOpExecutionMode %36 LocalSize 64 1 1\nOpExecutionMode %87 LocalSize 64 1 1\nOpExecutionMode %87 OutputLinesEXT\nOpExecutionMode %87 OutputVertices 2\nOpExecutionMode %87 OutputPrimitivesEXT 1\nOpDecorate %69 BuiltIn LocalInvocationIndex\nOpMemberDecorate %71 0 BuiltIn Position\nOpDecorate %71 Block\nOpDecorate %77 BuiltIn PrimitiveLineIndicesEXT\nOpMemberDecorate %4 0 Offset 0\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %9 0 Offset 0\nOpDecorate %11 ArrayStride 16\nOpDecorate %13 ArrayStride 8\nOpMemberDecorate %15 0 Offset 0\nOpMemberDecorate %15 1 Offset 32\nOpMemberDecorate %15 2 Offset 40\nOpMemberDecorate %15 3 Offset 44\nOpDecorate %26 BuiltIn LocalInvocationIndex\nOpDecorate %81 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 4\n%7 = OpTypeStruct %5\n%8 = OpTypeVector %3 2\n%9 = OpTypeStruct %8\n%10 = OpTypeVector %3 3\n%12 = OpConstant  %3  2\n%11 = OpTypeArray %7 %12\n%14 = OpConstant  %3  1\n%13 = OpTypeArray %9 %14\n%15 = OpTypeStruct %11 %13 %3 %3\n%17 = OpTypePointer TaskPayloadWorkgroupEXT %4\n%16 = OpVariable  %17  TaskPayloadWorkgroupEXT\n%19 = OpTypePointer Workgroup %15\n%18 = OpVariable  %19  Workgroup\n%22 = OpTypeFunction %10\n%23 = OpConstantComposite  %10  %14 %14 %14\n%25 = OpConstantNull  %4\n%27 = OpTypePointer Input %3\n%26 = OpVariable  %27  Input\n%29 = OpConstant  %3  0\n%31 = OpTypeBool\n%34 = OpConstant  %3  264\n%37 = OpTypeFunction %2\n%40 = OpConstant  %3  256\n%41 = OpConstant  %3  1024\n%42 = OpTypeStruct %3 %3\n%69 = OpVariable  %27  Input\n%70 = OpConstant  %3  64\n%71 = OpTypeStruct %5\n%72 = OpTypeArray %71 %12\n%73 = OpTypePointer Output %72\n%74 = OpVariable  %73  Output\n%75 = OpTypeArray %8 %14\n%76 = OpTypePointer Output %75\n%77 = OpVariable  %76  Output\n%80 = OpConstantNull  %15\n%81 = OpVariable  %27  Input\n%91 = OpTypePointer Function %3\n%95 = OpTypePointer Workgroup %3\n%99 = OpConstant  %3  3\n%103 = OpTypePointer Workgroup %11\n%105 = OpTypePointer Workgroup %13\n%112 = OpTypePointer Workgroup %5\n%115 = OpTypePointer Output %5\n%125 = OpTypePointer Workgroup %8\n%128 = OpTypePointer Output %8\n%21 = OpFunction  %10  None %22\n%20 = OpLabel\nOpBranch %24\n%24 = OpLabel\n%28 = OpLoad  %3  %26\n%30 = OpIEqual  %31  %28 %29\nOpSelectionMerge %32 None\nOpBranchConditional %30 %33 %32\n%33 = OpLabel\nOpStore %16 %25\nOpBranch %32\n%32 = OpLabel\nOpControlBarrier %12 %12 %34\nOpBranch %35\n%35 = OpLabel\nOpReturnValue %23\nOpFunctionEnd\n%36 = OpFunction  %2  None %37\n%38 = OpLabel\n%39 = OpFunctionCall  %10  %21\nOpControlBarrier %12 %12 %34\n%43 = OpCompositeExtract  %3  %39 0\n%44 = OpCompositeExtract  %3  %39 1\n%45 = OpCompositeExtract  %3  %39 2\n%49 = OpUMulExtended  %42  %43 %44\n%46 = OpCompositeExtract  %3  %49 0\n%47 = OpCompositeExtract  %3  %49 1\n%51 = OpUMulExtended  %42  %46 %45\n%50 = OpCompositeExtract  %3  %51 0\n%48 = OpCompositeExtract  %3  %51 1\n%52 = OpUGreaterThan  %31  %50 %41\n%53 = OpUGreaterThan  %31  %43 %40\n%54 = OpUGreaterThan  %31  %44 %40\n%55 = OpUGreaterThan  %31  %45 %40\n%56 = OpINotEqual  %31  %47 %29\n%57 = OpINotEqual  %31  %48 %29\n%58 = OpLogicalOr  %31  %52 %53\n%59 = OpLogicalOr  %31  %58 %54\n%60 = OpLogicalOr  %31  %59 %55\n%61 = OpLogicalOr  %31  %60 %56\n%62 = OpLogicalOr  %31  %61 %57\n%63 = OpCompositeConstruct  %10  %29 %29 %29\n%64 = OpSelect  %10  %62 %63 %39\n%65 = OpCompositeExtract  %3  %64 0\n%66 = OpCompositeExtract  %3  %64 1\n%67 = OpCompositeExtract  %3  %64 2\nOpEmitMeshTasksEXT %65 %66 %67 %16\nOpFunctionEnd\n%78 = OpFunction  %2  None %37\n%68 = OpLabel\nOpBranch %79\n%79 = OpLabel\n%82 = OpLoad  %3  %81\n%83 = OpIEqual  %31  %82 %29\nOpSelectionMerge %84 None\nOpBranchConditional %83 %85 %84\n%85 = OpLabel\nOpStore %18 %80\nOpBranch %84\n%84 = OpLabel\nOpControlBarrier %12 %12 %34\nOpBranch %86\n%86 = OpLabel\nOpReturn\nOpFunctionEnd\n%87 = OpFunction  %2  None %37\n%88 = OpLabel\n%89 = OpVariable  %91  Function\n%90 = OpVariable  %91  Function\n%92 = OpLoad  %3  %69\n%93 = OpFunctionCall  %2  %78\nOpControlBarrier %12 %12 %34\n%94 = OpAccessChain  %95  %18 %12\n%96 = OpLoad  %3  %94\n%97 = OpExtInst  %3  %1 UMin %96 %12\n%98 = OpAccessChain  %95  %18 %99\n%100 = OpLoad  %3  %98\n%101 = OpExtInst  %3  %1 UMin %100 %14\n%102 = OpAccessChain  %103  %18 %29\n%104 = OpAccessChain  %105  %18 %14\nOpSetMeshOutputsEXT %97 %101\nOpStore %89 %92\nOpBranch %106\n%106 = OpLabel\nOpLoopMerge %108 %117 None\nOpBranch %116\n%116 = OpLabel\n%119 = OpLoad  %3  %89\n%120 = OpULessThan  %31  %119 %97\nOpBranchConditional %120 %118 %108\n%118 = OpLabel\n%110 = OpLoad  %3  %89\n%111 = OpAccessChain  %112  %102 %110 %29\n%113 = OpLoad  %5  %111\n%114 = OpAccessChain  %115  %74 %110 %29\nOpStore %114 %113\nOpBranch %117\n%117 = OpLabel\n%121 = OpLoad  %3  %89\n%122 = OpIAdd  %3  %121 %70\nOpStore %89 %122\nOpBranch %106\n%108 = OpLabel\nOpStore %90 %92\nOpBranch %107\n%107 = OpLabel\nOpLoopMerge %109 %130 None\nOpBranch %129\n%129 = OpLabel\n%132 = OpLoad  %3  %90\n%133 = OpULessThan  %31  %132 %101\nOpBranchConditional %133 %131 %109\n%131 = OpLabel\n%123 = OpLoad  %3  %90\n%124 = OpAccessChain  %125  %104 %123 %29\n%126 = OpLoad  %8  %124\n%127 = OpAccessChain  %128  %77 %123\nOpStore %127 %126\nOpBranch %130\n%130 = OpLabel\n%134 = OpLoad  %3  %90\n%135 = OpIAdd  %3  %134 %70\nOpStore %90 %135\nOpBranch %107\n%109 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mesh-shader-points.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 134\nOpCapability Shader\nOpCapability MeshShadingEXT\nOpExtension \"SPV_EXT_mesh_shader\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint TaskEXT %35 \"ts_main\" %14 %24\nOpEntryPoint MeshEXT %86 \"ms_main\" %14 %68 %73 %76 %16 %80\nOpExecutionMode %35 LocalSize 64 1 1\nOpExecutionMode %86 LocalSize 64 1 1\nOpExecutionMode %86 OutputPoints\nOpExecutionMode %86 OutputVertices 1\nOpExecutionMode %86 OutputPrimitivesEXT 1\nOpDecorate %68 BuiltIn LocalInvocationIndex\nOpMemberDecorate %70 0 BuiltIn Position\nOpDecorate %70 Block\nOpDecorate %76 BuiltIn PrimitivePointIndicesEXT\nOpMemberDecorate %4 0 Offset 0\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %8 0 Offset 0\nOpDecorate %10 ArrayStride 16\nOpDecorate %12 ArrayStride 4\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 16\nOpMemberDecorate %13 2 Offset 20\nOpMemberDecorate %13 3 Offset 24\nOpDecorate %24 BuiltIn LocalInvocationIndex\nOpDecorate %80 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 4\n%7 = OpTypeStruct %5\n%8 = OpTypeStruct %3\n%9 = OpTypeVector %3 3\n%11 = OpConstant  %3  1\n%10 = OpTypeArray %7 %11\n%12 = OpTypeArray %8 %11\n%13 = OpTypeStruct %10 %12 %3 %3\n%15 = OpTypePointer TaskPayloadWorkgroupEXT %4\n%14 = OpVariable  %15  TaskPayloadWorkgroupEXT\n%17 = OpTypePointer Workgroup %13\n%16 = OpVariable  %17  Workgroup\n%20 = OpTypeFunction %9\n%21 = OpConstantComposite  %9  %11 %11 %11\n%23 = OpConstantNull  %4\n%25 = OpTypePointer Input %3\n%24 = OpVariable  %25  Input\n%27 = OpConstant  %3  0\n%29 = OpTypeBool\n%32 = OpConstant  %3  2\n%33 = OpConstant  %3  264\n%36 = OpTypeFunction %2\n%39 = OpConstant  %3  256\n%40 = OpConstant  %3  1024\n%41 = OpTypeStruct %3 %3\n%68 = OpVariable  %25  Input\n%69 = OpConstant  %3  64\n%70 = OpTypeStruct %5\n%71 = OpTypeArray %70 %11\n%72 = OpTypePointer Output %71\n%73 = OpVariable  %72  Output\n%74 = OpTypeArray %3 %11\n%75 = OpTypePointer Output %74\n%76 = OpVariable  %75  Output\n%79 = OpConstantNull  %13\n%80 = OpVariable  %25  Input\n%90 = OpTypePointer Function %3\n%94 = OpTypePointer Workgroup %3\n%98 = OpConstant  %3  3\n%102 = OpTypePointer Workgroup %10\n%104 = OpTypePointer Workgroup %12\n%111 = OpTypePointer Workgroup %5\n%114 = OpTypePointer Output %5\n%126 = OpTypePointer Output %3\n%19 = OpFunction  %9  None %20\n%18 = OpLabel\nOpBranch %22\n%22 = OpLabel\n%26 = OpLoad  %3  %24\n%28 = OpIEqual  %29  %26 %27\nOpSelectionMerge %30 None\nOpBranchConditional %28 %31 %30\n%31 = OpLabel\nOpStore %14 %23\nOpBranch %30\n%30 = OpLabel\nOpControlBarrier %32 %32 %33\nOpBranch %34\n%34 = OpLabel\nOpReturnValue %21\nOpFunctionEnd\n%35 = OpFunction  %2  None %36\n%37 = OpLabel\n%38 = OpFunctionCall  %9  %19\nOpControlBarrier %32 %32 %33\n%42 = OpCompositeExtract  %3  %38 0\n%43 = OpCompositeExtract  %3  %38 1\n%44 = OpCompositeExtract  %3  %38 2\n%48 = OpUMulExtended  %41  %42 %43\n%45 = OpCompositeExtract  %3  %48 0\n%46 = OpCompositeExtract  %3  %48 1\n%50 = OpUMulExtended  %41  %45 %44\n%49 = OpCompositeExtract  %3  %50 0\n%47 = OpCompositeExtract  %3  %50 1\n%51 = OpUGreaterThan  %29  %49 %40\n%52 = OpUGreaterThan  %29  %42 %39\n%53 = OpUGreaterThan  %29  %43 %39\n%54 = OpUGreaterThan  %29  %44 %39\n%55 = OpINotEqual  %29  %46 %27\n%56 = OpINotEqual  %29  %47 %27\n%57 = OpLogicalOr  %29  %51 %52\n%58 = OpLogicalOr  %29  %57 %53\n%59 = OpLogicalOr  %29  %58 %54\n%60 = OpLogicalOr  %29  %59 %55\n%61 = OpLogicalOr  %29  %60 %56\n%62 = OpCompositeConstruct  %9  %27 %27 %27\n%63 = OpSelect  %9  %61 %62 %38\n%64 = OpCompositeExtract  %3  %63 0\n%65 = OpCompositeExtract  %3  %63 1\n%66 = OpCompositeExtract  %3  %63 2\nOpEmitMeshTasksEXT %64 %65 %66 %14\nOpFunctionEnd\n%77 = OpFunction  %2  None %36\n%67 = OpLabel\nOpBranch %78\n%78 = OpLabel\n%81 = OpLoad  %3  %80\n%82 = OpIEqual  %29  %81 %27\nOpSelectionMerge %83 None\nOpBranchConditional %82 %84 %83\n%84 = OpLabel\nOpStore %16 %79\nOpBranch %83\n%83 = OpLabel\nOpControlBarrier %32 %32 %33\nOpBranch %85\n%85 = OpLabel\nOpReturn\nOpFunctionEnd\n%86 = OpFunction  %2  None %36\n%87 = OpLabel\n%88 = OpVariable  %90  Function\n%89 = OpVariable  %90  Function\n%91 = OpLoad  %3  %68\n%92 = OpFunctionCall  %2  %77\nOpControlBarrier %32 %32 %33\n%93 = OpAccessChain  %94  %16 %32\n%95 = OpLoad  %3  %93\n%96 = OpExtInst  %3  %1 UMin %95 %11\n%97 = OpAccessChain  %94  %16 %98\n%99 = OpLoad  %3  %97\n%100 = OpExtInst  %3  %1 UMin %99 %11\n%101 = OpAccessChain  %102  %16 %27\n%103 = OpAccessChain  %104  %16 %11\nOpSetMeshOutputsEXT %96 %100\nOpStore %88 %91\nOpBranch %105\n%105 = OpLabel\nOpLoopMerge %107 %116 None\nOpBranch %115\n%115 = OpLabel\n%118 = OpLoad  %3  %88\n%119 = OpULessThan  %29  %118 %96\nOpBranchConditional %119 %117 %107\n%117 = OpLabel\n%109 = OpLoad  %3  %88\n%110 = OpAccessChain  %111  %101 %109 %27\n%112 = OpLoad  %5  %110\n%113 = OpAccessChain  %114  %73 %109 %27\nOpStore %113 %112\nOpBranch %116\n%116 = OpLabel\n%120 = OpLoad  %3  %88\n%121 = OpIAdd  %3  %120 %69\nOpStore %88 %121\nOpBranch %105\n%107 = OpLabel\nOpStore %89 %91\nOpBranch %106\n%106 = OpLabel\nOpLoopMerge %108 %128 None\nOpBranch %127\n%127 = OpLabel\n%130 = OpLoad  %3  %89\n%131 = OpULessThan  %29  %130 %100\nOpBranchConditional %131 %129 %108\n%129 = OpLabel\n%122 = OpLoad  %3  %89\n%123 = OpAccessChain  %94  %103 %122 %27\n%124 = OpLoad  %3  %123\n%125 = OpAccessChain  %126  %76 %122\nOpStore %125 %124\nOpBranch %128\n%128 = OpLabel\n%132 = OpLoad  %3  %89\n%133 = OpIAdd  %3  %132 %69\nOpStore %89 %133\nOpBranch %106\n%108 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-mesh-shader.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 459\nOpCapability Shader\nOpCapability MeshShadingEXT\nOpExtension \"SPV_EXT_mesh_shader\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint TaskEXT %62 \"ts_main\" %17 %19 %47\nOpEntryPoint TaskEXT %113 \"ts_divergent\" %95 %17 %101\nOpEntryPoint MeshEXT %208 \"ms_main\" %17 %142 %146 %150 %153 %156 %159 %19 %21 %172\nOpEntryPoint MeshEXT %300 \"ms_no_ts\" %262 %266 %270 %273 %276 %279 %19 %21 %283\nOpEntryPoint MeshEXT %394 \"ms_divergent\" %350 %352 %356 %360 %363 %366 %369 %19 %21 %372\nOpEntryPoint Fragment %454 \"fs_main\" %445 %448 %451 %453\nOpExecutionMode %62 LocalSize 1 1 1\nOpExecutionMode %113 LocalSize 2 1 1\nOpExecutionMode %208 LocalSize 1 1 1\nOpExecutionMode %208 OutputTrianglesEXT\nOpExecutionMode %208 OutputVertices 3\nOpExecutionMode %208 OutputPrimitivesEXT 1\nOpExecutionMode %300 LocalSize 1 1 1\nOpExecutionMode %300 OutputTrianglesEXT\nOpExecutionMode %300 OutputVertices 3\nOpExecutionMode %300 OutputPrimitivesEXT 1\nOpExecutionMode %394 LocalSize 2 1 1\nOpExecutionMode %394 OutputTrianglesEXT\nOpExecutionMode %394 OutputVertices 3\nOpExecutionMode %394 OutputPrimitivesEXT 1\nOpExecutionMode %454 OriginUpperLeft\nOpDecorate %142 BuiltIn LocalInvocationIndex\nOpMemberDecorate %143 0 BuiltIn Position\nOpDecorate %143 Block\nOpMemberDecorate %147 0 BuiltIn CullPrimitiveEXT\nOpMemberDecorate %147 0 PerPrimitiveEXT\nOpDecorate %147 Block\nOpDecorate %150 PerPrimitiveEXT\nOpDecorate %153 Location 0\nOpDecorate %156 BuiltIn PrimitiveTriangleIndicesEXT\nOpDecorate %159 Location 1\nOpDecorate %159 PerPrimitiveEXT\nOpDecorate %262 BuiltIn LocalInvocationIndex\nOpMemberDecorate %263 0 BuiltIn Position\nOpDecorate %263 Block\nOpMemberDecorate %267 0 BuiltIn CullPrimitiveEXT\nOpMemberDecorate %267 0 PerPrimitiveEXT\nOpDecorate %267 Block\nOpDecorate %270 PerPrimitiveEXT\nOpDecorate %273 Location 0\nOpDecorate %276 BuiltIn PrimitiveTriangleIndicesEXT\nOpDecorate %279 Location 1\nOpDecorate %279 PerPrimitiveEXT\nOpDecorate %352 BuiltIn LocalInvocationIndex\nOpMemberDecorate %353 0 BuiltIn Position\nOpDecorate %353 Block\nOpMemberDecorate %357 0 BuiltIn CullPrimitiveEXT\nOpMemberDecorate %357 0 PerPrimitiveEXT\nOpDecorate %357 Block\nOpDecorate %360 PerPrimitiveEXT\nOpDecorate %363 Location 0\nOpDecorate %366 BuiltIn PrimitiveTriangleIndicesEXT\nOpDecorate %369 Location 1\nOpDecorate %369 PerPrimitiveEXT\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 16\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 16\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 12\nOpMemberDecorate %10 2 Offset 16\nOpMemberDecorate %11 0 Offset 0\nOpDecorate %12 ArrayStride 32\nOpDecorate %14 ArrayStride 32\nOpMemberDecorate %16 0 Offset 0\nOpMemberDecorate %16 1 Offset 96\nOpMemberDecorate %16 2 Offset 128\nOpMemberDecorate %16 3 Offset 132\nOpDecorate %47 BuiltIn LocalInvocationIndex\nOpDecorate %95 BuiltIn LocalInvocationId\nOpDecorate %101 BuiltIn LocalInvocationIndex\nOpDecorate %172 BuiltIn LocalInvocationIndex\nOpDecorate %283 BuiltIn LocalInvocationIndex\nOpDecorate %350 BuiltIn LocalInvocationId\nOpDecorate %372 BuiltIn LocalInvocationIndex\nOpDecorate %445 BuiltIn FragCoord\nOpDecorate %448 Location 0\nOpDecorate %451 Location 1\nOpDecorate %451 PerPrimitiveEXT\nOpDecorate %453 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeBool\n%6 = OpTypeStruct %4 %5\n%7 = OpTypeStruct %4 %4\n%8 = OpTypeInt 32 0\n%9 = OpTypeVector %8 3\n%10 = OpTypeStruct %9 %5 %4\n%11 = OpTypeStruct %4\n%13 = OpConstant  %8  3\n%12 = OpTypeArray %7 %13\n%15 = OpConstant  %8  1\n%14 = OpTypeArray %10 %15\n%16 = OpTypeStruct %12 %14 %8 %8\n%18 = OpTypePointer TaskPayloadWorkgroupEXT %6\n%17 = OpVariable  %18  TaskPayloadWorkgroupEXT\n%20 = OpTypePointer Workgroup %3\n%19 = OpVariable  %20  Workgroup\n%22 = OpTypePointer Workgroup %16\n%21 = OpVariable  %22  Workgroup\n%25 = OpTypeFunction %5\n%27 = OpTypePointer TaskPayloadWorkgroupEXT %5\n%33 = OpTypeFunction %2 %5\n%38 = OpTypeFunction %9\n%39 = OpConstant  %3  1\n%40 = OpConstant  %3  0\n%41 = OpConstantComposite  %4  %39 %39 %40 %39\n%42 = OpConstantTrue  %5\n%43 = OpConstantComposite  %9  %15 %15 %15\n%45 = OpConstantNull  %6\n%46 = OpConstantNull  %3\n%48 = OpTypePointer Input %8\n%47 = OpVariable  %48  Input\n%50 = OpConstant  %8  0\n%54 = OpConstant  %8  2\n%55 = OpConstant  %8  264\n%57 = OpTypePointer TaskPayloadWorkgroupEXT %4\n%63 = OpTypeFunction %2\n%66 = OpConstant  %8  256\n%67 = OpConstant  %8  1024\n%68 = OpTypeStruct %8 %8\n%96 = OpTypePointer Input %9\n%95 = OpVariable  %96  Input\n%99 = OpConstantComposite  %9  %54 %54 %54\n%101 = OpVariable  %48  Input\n%142 = OpVariable  %48  Input\n%143 = OpTypeStruct %4\n%144 = OpTypeArray %143 %13\n%145 = OpTypePointer Output %144\n%146 = OpVariable  %145  Output\n%147 = OpTypeStruct %5\n%148 = OpTypeArray %147 %15\n%149 = OpTypePointer Output %148\n%150 = OpVariable  %149  Output\n%151 = OpTypeArray %4 %13\n%152 = OpTypePointer Output %151\n%153 = OpVariable  %152  Output\n%154 = OpTypeArray %9 %15\n%155 = OpTypePointer Output %154\n%156 = OpVariable  %155  Output\n%157 = OpTypeArray %4 %15\n%158 = OpTypePointer Output %157\n%159 = OpVariable  %158  Output\n%161 = OpConstant  %3  2\n%162 = OpConstantComposite  %4  %40 %39 %40 %39\n%163 = OpConstant  %3  -1\n%164 = OpConstantComposite  %4  %163 %163 %40 %39\n%165 = OpConstantComposite  %4  %40 %40 %39 %39\n%166 = OpConstantComposite  %4  %39 %163 %40 %39\n%167 = OpConstantComposite  %4  %39 %40 %40 %39\n%168 = OpConstantComposite  %9  %50 %15 %54\n%169 = OpConstantComposite  %4  %39 %40 %39 %39\n%171 = OpConstantNull  %16\n%172 = OpVariable  %48  Input\n%178 = OpTypePointer Workgroup %8\n%181 = OpTypePointer Workgroup %12\n%182 = OpTypePointer Workgroup %7\n%183 = OpTypePointer Workgroup %4\n%199 = OpTypePointer Workgroup %14\n%200 = OpTypePointer Workgroup %10\n%201 = OpTypePointer Workgroup %9\n%203 = OpTypePointer Workgroup %5\n%212 = OpTypePointer Function %8\n%231 = OpTypePointer Output %4\n%246 = OpTypePointer Output %9\n%250 = OpTypePointer Output %5\n%262 = OpVariable  %48  Input\n%263 = OpTypeStruct %4\n%264 = OpTypeArray %263 %13\n%265 = OpTypePointer Output %264\n%266 = OpVariable  %265  Output\n%267 = OpTypeStruct %5\n%268 = OpTypeArray %267 %15\n%269 = OpTypePointer Output %268\n%270 = OpVariable  %269  Output\n%271 = OpTypeArray %4 %13\n%272 = OpTypePointer Output %271\n%273 = OpVariable  %272  Output\n%274 = OpTypeArray %9 %15\n%275 = OpTypePointer Output %274\n%276 = OpVariable  %275  Output\n%277 = OpTypeArray %4 %15\n%278 = OpTypePointer Output %277\n%279 = OpVariable  %278  Output\n%281 = OpConstantFalse  %5\n%283 = OpVariable  %48  Input\n%350 = OpVariable  %96  Input\n%352 = OpVariable  %48  Input\n%353 = OpTypeStruct %4\n%354 = OpTypeArray %353 %13\n%355 = OpTypePointer Output %354\n%356 = OpVariable  %355  Output\n%357 = OpTypeStruct %5\n%358 = OpTypeArray %357 %15\n%359 = OpTypePointer Output %358\n%360 = OpVariable  %359  Output\n%361 = OpTypeArray %4 %13\n%362 = OpTypePointer Output %361\n%363 = OpVariable  %362  Output\n%364 = OpTypeArray %9 %15\n%365 = OpTypePointer Output %364\n%366 = OpVariable  %365  Output\n%367 = OpTypeArray %4 %15\n%368 = OpTypePointer Output %367\n%369 = OpVariable  %368  Output\n%372 = OpVariable  %48  Input\n%446 = OpTypePointer Input %4\n%445 = OpVariable  %446  Input\n%448 = OpVariable  %446  Input\n%451 = OpVariable  %446  Input\n%453 = OpVariable  %231  Output\n%24 = OpFunction  %5  None %25\n%23 = OpLabel\nOpBranch %26\n%26 = OpLabel\n%28 = OpAccessChain  %27  %17 %15\n%29 = OpLoad  %5  %28\nOpReturnValue %29\nOpFunctionEnd\n%32 = OpFunction  %2  None %33\n%31 = OpFunctionParameter  %5\n%30 = OpLabel\nOpBranch %34\n%34 = OpLabel\n%35 = OpAccessChain  %27  %17 %15\nOpStore %35 %31\nOpReturn\nOpFunctionEnd\n%37 = OpFunction  %9  None %38\n%36 = OpLabel\nOpBranch %44\n%44 = OpLabel\n%49 = OpLoad  %8  %47\n%51 = OpIEqual  %5  %49 %50\nOpSelectionMerge %52 None\nOpBranchConditional %51 %53 %52\n%53 = OpLabel\nOpStore %17 %45\nOpStore %19 %46\nOpBranch %52\n%52 = OpLabel\nOpControlBarrier %54 %54 %55\nOpBranch %56\n%56 = OpLabel\nOpStore %19 %39\n%58 = OpAccessChain  %57  %17 %50\nOpStore %58 %41\n%59 = OpFunctionCall  %2  %32 %42\n%60 = OpFunctionCall  %5  %24\n%61 = OpAccessChain  %27  %17 %15\nOpStore %61 %60\nOpReturnValue %43\nOpFunctionEnd\n%62 = OpFunction  %2  None %63\n%64 = OpLabel\n%65 = OpFunctionCall  %9  %37\nOpControlBarrier %54 %54 %55\n%69 = OpCompositeExtract  %8  %65 0\n%70 = OpCompositeExtract  %8  %65 1\n%71 = OpCompositeExtract  %8  %65 2\n%75 = OpUMulExtended  %68  %69 %70\n%72 = OpCompositeExtract  %8  %75 0\n%73 = OpCompositeExtract  %8  %75 1\n%77 = OpUMulExtended  %68  %72 %71\n%76 = OpCompositeExtract  %8  %77 0\n%74 = OpCompositeExtract  %8  %77 1\n%78 = OpUGreaterThan  %5  %76 %67\n%79 = OpUGreaterThan  %5  %69 %66\n%80 = OpUGreaterThan  %5  %70 %66\n%81 = OpUGreaterThan  %5  %71 %66\n%82 = OpINotEqual  %5  %73 %50\n%83 = OpINotEqual  %5  %74 %50\n%84 = OpLogicalOr  %5  %78 %79\n%85 = OpLogicalOr  %5  %84 %80\n%86 = OpLogicalOr  %5  %85 %81\n%87 = OpLogicalOr  %5  %86 %82\n%88 = OpLogicalOr  %5  %87 %83\n%89 = OpCompositeConstruct  %9  %50 %50 %50\n%90 = OpSelect  %9  %88 %89 %65\n%91 = OpCompositeExtract  %8  %90 0\n%92 = OpCompositeExtract  %8  %90 1\n%93 = OpCompositeExtract  %8  %90 2\nOpEmitMeshTasksEXT %91 %92 %93 %17\nOpFunctionEnd\n%98 = OpFunction  %9  None %38\n%94 = OpLabel\n%97 = OpLoad  %9  %95\nOpBranch %100\n%100 = OpLabel\n%102 = OpLoad  %8  %101\n%103 = OpIEqual  %5  %102 %50\nOpSelectionMerge %104 None\nOpBranchConditional %103 %105 %104\n%105 = OpLabel\nOpStore %17 %45\nOpBranch %104\n%104 = OpLabel\nOpControlBarrier %54 %54 %55\nOpBranch %106\n%106 = OpLabel\n%107 = OpCompositeExtract  %8  %97 0\n%108 = OpIEqual  %5  %107 %50\nOpSelectionMerge %109 None\nOpBranchConditional %108 %110 %109\n%110 = OpLabel\n%111 = OpAccessChain  %57  %17 %50\nOpStore %111 %41\n%112 = OpAccessChain  %27  %17 %15\nOpStore %112 %42\nOpReturnValue %43\n%109 = OpLabel\nOpReturnValue %99\nOpFunctionEnd\n%113 = OpFunction  %2  None %63\n%114 = OpLabel\n%115 = OpFunctionCall  %9  %98\nOpControlBarrier %54 %54 %55\n%116 = OpCompositeExtract  %8  %115 0\n%117 = OpCompositeExtract  %8  %115 1\n%118 = OpCompositeExtract  %8  %115 2\n%122 = OpUMulExtended  %68  %116 %117\n%119 = OpCompositeExtract  %8  %122 0\n%120 = OpCompositeExtract  %8  %122 1\n%124 = OpUMulExtended  %68  %119 %118\n%123 = OpCompositeExtract  %8  %124 0\n%121 = OpCompositeExtract  %8  %124 1\n%125 = OpUGreaterThan  %5  %123 %67\n%126 = OpUGreaterThan  %5  %116 %66\n%127 = OpUGreaterThan  %5  %117 %66\n%128 = OpUGreaterThan  %5  %118 %66\n%129 = OpINotEqual  %5  %120 %50\n%130 = OpINotEqual  %5  %121 %50\n%131 = OpLogicalOr  %5  %125 %126\n%132 = OpLogicalOr  %5  %131 %127\n%133 = OpLogicalOr  %5  %132 %128\n%134 = OpLogicalOr  %5  %133 %129\n%135 = OpLogicalOr  %5  %134 %130\n%136 = OpCompositeConstruct  %9  %50 %50 %50\n%137 = OpSelect  %9  %135 %136 %115\n%138 = OpCompositeExtract  %8  %137 0\n%139 = OpCompositeExtract  %8  %137 1\n%140 = OpCompositeExtract  %8  %137 2\nOpEmitMeshTasksEXT %138 %139 %140 %17\nOpFunctionEnd\n%160 = OpFunction  %2  None %63\n%141 = OpLabel\nOpBranch %170\n%170 = OpLabel\n%173 = OpLoad  %8  %172\n%174 = OpIEqual  %5  %173 %50\nOpSelectionMerge %175 None\nOpBranchConditional %174 %176 %175\n%176 = OpLabel\nOpStore %19 %46\nOpStore %21 %171\nOpBranch %175\n%175 = OpLabel\nOpControlBarrier %54 %54 %55\nOpBranch %177\n%177 = OpLabel\n%179 = OpAccessChain  %178  %21 %54\nOpStore %179 %13\n%180 = OpAccessChain  %178  %21 %13\nOpStore %180 %15\nOpStore %19 %161\n%184 = OpAccessChain  %183  %21 %50 %50 %50\nOpStore %184 %162\n%185 = OpAccessChain  %57  %17 %50\n%186 = OpLoad  %4  %185\n%187 = OpFMul  %4  %162 %186\n%188 = OpAccessChain  %183  %21 %50 %50 %15\nOpStore %188 %187\n%189 = OpAccessChain  %183  %21 %50 %15 %50\nOpStore %189 %164\n%190 = OpAccessChain  %57  %17 %50\n%191 = OpLoad  %4  %190\n%192 = OpFMul  %4  %165 %191\n%193 = OpAccessChain  %183  %21 %50 %15 %15\nOpStore %193 %192\n%194 = OpAccessChain  %183  %21 %50 %54 %50\nOpStore %194 %166\n%195 = OpAccessChain  %57  %17 %50\n%196 = OpLoad  %4  %195\n%197 = OpFMul  %4  %167 %196\n%198 = OpAccessChain  %183  %21 %50 %54 %15\nOpStore %198 %197\n%202 = OpAccessChain  %201  %21 %15 %50 %50\nOpStore %202 %168\n%204 = OpFunctionCall  %5  %24\n%205 = OpLogicalNot  %5  %204\n%206 = OpAccessChain  %203  %21 %15 %50 %15\nOpStore %206 %205\n%207 = OpAccessChain  %183  %21 %15 %50 %54\nOpStore %207 %169\nOpReturn\nOpFunctionEnd\n%208 = OpFunction  %2  None %63\n%209 = OpLabel\n%210 = OpVariable  %212  Function\n%211 = OpVariable  %212  Function\n%213 = OpLoad  %8  %142\n%214 = OpFunctionCall  %2  %160\nOpControlBarrier %54 %54 %55\n%215 = OpAccessChain  %178  %21 %54\n%216 = OpLoad  %8  %215\n%217 = OpExtInst  %8  %1 UMin %216 %13\n%218 = OpAccessChain  %178  %21 %13\n%219 = OpLoad  %8  %218\n%220 = OpExtInst  %8  %1 UMin %219 %15\n%221 = OpAccessChain  %181  %21 %50\n%222 = OpAccessChain  %199  %21 %15\nOpSetMeshOutputsEXT %217 %220\nOpStore %210 %213\nOpBranch %223\n%223 = OpLabel\nOpLoopMerge %225 %236 None\nOpBranch %235\n%235 = OpLabel\n%238 = OpLoad  %8  %210\n%239 = OpULessThan  %5  %238 %217\nOpBranchConditional %239 %237 %225\n%237 = OpLabel\n%227 = OpLoad  %8  %210\n%228 = OpAccessChain  %183  %221 %227 %50\n%229 = OpLoad  %4  %228\n%230 = OpAccessChain  %231  %146 %227 %50\nOpStore %230 %229\n%232 = OpAccessChain  %183  %221 %227 %15\n%233 = OpLoad  %4  %232\n%234 = OpAccessChain  %231  %153 %227\nOpStore %234 %233\nOpBranch %236\n%236 = OpLabel\n%240 = OpLoad  %8  %210\n%241 = OpIAdd  %8  %240 %15\nOpStore %210 %241\nOpBranch %223\n%225 = OpLabel\nOpStore %211 %213\nOpBranch %224\n%224 = OpLabel\nOpLoopMerge %226 %255 None\nOpBranch %254\n%254 = OpLabel\n%257 = OpLoad  %8  %211\n%258 = OpULessThan  %5  %257 %220\nOpBranchConditional %258 %256 %226\n%256 = OpLabel\n%242 = OpLoad  %8  %211\n%243 = OpAccessChain  %201  %222 %242 %50\n%244 = OpLoad  %9  %243\n%245 = OpAccessChain  %246  %156 %242\nOpStore %245 %244\n%247 = OpAccessChain  %203  %222 %242 %15\n%248 = OpLoad  %5  %247\n%249 = OpAccessChain  %250  %150 %242 %50\nOpStore %249 %248\n%251 = OpAccessChain  %183  %222 %242 %54\n%252 = OpLoad  %4  %251\n%253 = OpAccessChain  %231  %159 %242\nOpStore %253 %252\nOpBranch %255\n%255 = OpLabel\n%259 = OpLoad  %8  %211\n%260 = OpIAdd  %8  %259 %15\nOpStore %211 %260\nOpBranch %224\n%226 = OpLabel\nOpReturn\nOpFunctionEnd\n%280 = OpFunction  %2  None %63\n%261 = OpLabel\nOpBranch %282\n%282 = OpLabel\n%284 = OpLoad  %8  %283\n%285 = OpIEqual  %5  %284 %50\nOpSelectionMerge %286 None\nOpBranchConditional %285 %287 %286\n%287 = OpLabel\nOpStore %19 %46\nOpStore %21 %171\nOpBranch %286\n%286 = OpLabel\nOpControlBarrier %54 %54 %55\nOpBranch %288\n%288 = OpLabel\n%289 = OpAccessChain  %178  %21 %54\nOpStore %289 %13\n%290 = OpAccessChain  %178  %21 %13\nOpStore %290 %15\nOpStore %19 %161\n%291 = OpAccessChain  %183  %21 %50 %50 %50\nOpStore %291 %162\n%292 = OpAccessChain  %183  %21 %50 %50 %15\nOpStore %292 %162\n%293 = OpAccessChain  %183  %21 %50 %15 %50\nOpStore %293 %164\n%294 = OpAccessChain  %183  %21 %50 %15 %15\nOpStore %294 %165\n%295 = OpAccessChain  %183  %21 %50 %54 %50\nOpStore %295 %166\n%296 = OpAccessChain  %183  %21 %50 %54 %15\nOpStore %296 %167\n%297 = OpAccessChain  %201  %21 %15 %50 %50\nOpStore %297 %168\n%298 = OpAccessChain  %203  %21 %15 %50 %15\nOpStore %298 %281\n%299 = OpAccessChain  %183  %21 %15 %50 %54\nOpStore %299 %169\nOpReturn\nOpFunctionEnd\n%300 = OpFunction  %2  None %63\n%301 = OpLabel\n%302 = OpVariable  %212  Function\n%303 = OpVariable  %212  Function\n%304 = OpLoad  %8  %262\n%305 = OpFunctionCall  %2  %280\nOpControlBarrier %54 %54 %55\n%306 = OpAccessChain  %178  %21 %54\n%307 = OpLoad  %8  %306\n%308 = OpExtInst  %8  %1 UMin %307 %13\n%309 = OpAccessChain  %178  %21 %13\n%310 = OpLoad  %8  %309\n%311 = OpExtInst  %8  %1 UMin %310 %15\n%312 = OpAccessChain  %181  %21 %50\n%313 = OpAccessChain  %199  %21 %15\nOpSetMeshOutputsEXT %308 %311\nOpStore %302 %304\nOpBranch %314\n%314 = OpLabel\nOpLoopMerge %316 %326 None\nOpBranch %325\n%325 = OpLabel\n%328 = OpLoad  %8  %302\n%329 = OpULessThan  %5  %328 %308\nOpBranchConditional %329 %327 %316\n%327 = OpLabel\n%318 = OpLoad  %8  %302\n%319 = OpAccessChain  %183  %312 %318 %50\n%320 = OpLoad  %4  %319\n%321 = OpAccessChain  %231  %266 %318 %50\nOpStore %321 %320\n%322 = OpAccessChain  %183  %312 %318 %15\n%323 = OpLoad  %4  %322\n%324 = OpAccessChain  %231  %273 %318\nOpStore %324 %323\nOpBranch %326\n%326 = OpLabel\n%330 = OpLoad  %8  %302\n%331 = OpIAdd  %8  %330 %15\nOpStore %302 %331\nOpBranch %314\n%316 = OpLabel\nOpStore %303 %304\nOpBranch %315\n%315 = OpLabel\nOpLoopMerge %317 %343 None\nOpBranch %342\n%342 = OpLabel\n%345 = OpLoad  %8  %303\n%346 = OpULessThan  %5  %345 %311\nOpBranchConditional %346 %344 %317\n%344 = OpLabel\n%332 = OpLoad  %8  %303\n%333 = OpAccessChain  %201  %313 %332 %50\n%334 = OpLoad  %9  %333\n%335 = OpAccessChain  %246  %276 %332\nOpStore %335 %334\n%336 = OpAccessChain  %203  %313 %332 %15\n%337 = OpLoad  %5  %336\n%338 = OpAccessChain  %250  %270 %332 %50\nOpStore %338 %337\n%339 = OpAccessChain  %183  %313 %332 %54\n%340 = OpLoad  %4  %339\n%341 = OpAccessChain  %231  %279 %332\nOpStore %341 %340\nOpBranch %343\n%343 = OpLabel\n%347 = OpLoad  %8  %303\n%348 = OpIAdd  %8  %347 %15\nOpStore %303 %348\nOpBranch %315\n%317 = OpLabel\nOpReturn\nOpFunctionEnd\n%370 = OpFunction  %2  None %63\n%349 = OpLabel\n%351 = OpLoad  %9  %350\nOpBranch %371\n%371 = OpLabel\n%373 = OpLoad  %8  %372\n%374 = OpIEqual  %5  %373 %50\nOpSelectionMerge %375 None\nOpBranchConditional %374 %376 %375\n%376 = OpLabel\nOpStore %19 %46\nOpStore %21 %171\nOpBranch %375\n%375 = OpLabel\nOpControlBarrier %54 %54 %55\nOpBranch %377\n%377 = OpLabel\n%378 = OpCompositeExtract  %8  %351 0\n%379 = OpIEqual  %5  %378 %50\nOpSelectionMerge %380 None\nOpBranchConditional %379 %381 %382\n%381 = OpLabel\n%383 = OpAccessChain  %178  %21 %54\nOpStore %383 %13\n%384 = OpAccessChain  %178  %21 %13\nOpStore %384 %15\nOpStore %19 %161\n%385 = OpAccessChain  %183  %21 %50 %50 %50\nOpStore %385 %162\n%386 = OpAccessChain  %183  %21 %50 %50 %15\nOpStore %386 %162\n%387 = OpAccessChain  %183  %21 %50 %15 %50\nOpStore %387 %164\n%388 = OpAccessChain  %183  %21 %50 %15 %15\nOpStore %388 %165\n%389 = OpAccessChain  %183  %21 %50 %54 %50\nOpStore %389 %166\n%390 = OpAccessChain  %183  %21 %50 %54 %15\nOpStore %390 %167\n%391 = OpAccessChain  %201  %21 %15 %50 %50\nOpStore %391 %168\n%392 = OpAccessChain  %203  %21 %15 %50 %15\nOpStore %392 %281\n%393 = OpAccessChain  %183  %21 %15 %50 %54\nOpStore %393 %169\nOpReturn\n%382 = OpLabel\nOpReturn\n%380 = OpLabel\nOpReturn\nOpFunctionEnd\n%394 = OpFunction  %2  None %63\n%395 = OpLabel\n%396 = OpVariable  %212  Function\n%397 = OpVariable  %212  Function\n%398 = OpLoad  %8  %352\n%399 = OpFunctionCall  %2  %370\nOpControlBarrier %54 %54 %55\n%400 = OpAccessChain  %178  %21 %54\n%401 = OpLoad  %8  %400\n%402 = OpExtInst  %8  %1 UMin %401 %13\n%403 = OpAccessChain  %178  %21 %13\n%404 = OpLoad  %8  %403\n%405 = OpExtInst  %8  %1 UMin %404 %15\n%406 = OpAccessChain  %181  %21 %50\n%407 = OpAccessChain  %199  %21 %15\nOpSetMeshOutputsEXT %402 %405\nOpStore %396 %398\nOpBranch %408\n%408 = OpLabel\nOpLoopMerge %410 %420 None\nOpBranch %419\n%419 = OpLabel\n%422 = OpLoad  %8  %396\n%423 = OpULessThan  %5  %422 %402\nOpBranchConditional %423 %421 %410\n%421 = OpLabel\n%412 = OpLoad  %8  %396\n%413 = OpAccessChain  %183  %406 %412 %50\n%414 = OpLoad  %4  %413\n%415 = OpAccessChain  %231  %356 %412 %50\nOpStore %415 %414\n%416 = OpAccessChain  %183  %406 %412 %15\n%417 = OpLoad  %4  %416\n%418 = OpAccessChain  %231  %363 %412\nOpStore %418 %417\nOpBranch %420\n%420 = OpLabel\n%424 = OpLoad  %8  %396\n%425 = OpIAdd  %8  %424 %54\nOpStore %396 %425\nOpBranch %408\n%410 = OpLabel\nOpStore %397 %398\nOpBranch %409\n%409 = OpLabel\nOpLoopMerge %411 %437 None\nOpBranch %436\n%436 = OpLabel\n%439 = OpLoad  %8  %397\n%440 = OpULessThan  %5  %439 %405\nOpBranchConditional %440 %438 %411\n%438 = OpLabel\n%426 = OpLoad  %8  %397\n%427 = OpAccessChain  %201  %407 %426 %50\n%428 = OpLoad  %9  %427\n%429 = OpAccessChain  %246  %366 %426\nOpStore %429 %428\n%430 = OpAccessChain  %203  %407 %426 %15\n%431 = OpLoad  %5  %430\n%432 = OpAccessChain  %250  %360 %426 %50\nOpStore %432 %431\n%433 = OpAccessChain  %183  %407 %426 %54\n%434 = OpLoad  %4  %433\n%435 = OpAccessChain  %231  %369 %426\nOpStore %435 %434\nOpBranch %437\n%437 = OpLabel\n%441 = OpLoad  %8  %397\n%442 = OpIAdd  %8  %441 %54\nOpStore %397 %442\nOpBranch %409\n%411 = OpLabel\nOpReturn\nOpFunctionEnd\n%454 = OpFunction  %2  None %63\n%443 = OpLabel\n%447 = OpLoad  %4  %445\n%449 = OpLoad  %4  %448\n%444 = OpCompositeConstruct  %7  %447 %449\n%452 = OpLoad  %4  %451\n%450 = OpCompositeConstruct  %11  %452\nOpBranch %455\n%455 = OpLabel\n%456 = OpCompositeExtract  %4  %444 1\n%457 = OpCompositeExtract  %4  %450 0\n%458 = OpFMul  %4  %456 %457\nOpStore %453 %458\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-multiview.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 11\nOpCapability Shader\nOpCapability MultiView\nOpExtension \"SPV_KHR_multiview\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %8 \"main\" %5\nOpExecutionMode %8 OriginUpperLeft\nOpDecorate %5 BuiltIn ViewIndex\nOpDecorate %5 Flat\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%6 = OpTypePointer Input %3\n%5 = OpVariable  %6  Input\n%9 = OpTypeFunction %2\n%8 = OpFunction  %2  None %9\n%4 = OpLabel\n%7 = OpLoad  %3  %5\nOpBranch %10\n%10 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-operators.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 576\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %559 \"main\" %556\nOpExecutionMode %559 LocalSize 1 1 1\nOpDecorate %556 BuiltIn WorkgroupId\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 4\n%5 = OpTypeInt 32 1\n%6 = OpTypeVector %5 4\n%7 = OpTypeBool\n%8 = OpTypeVector %3 2\n%9 = OpTypeVector %3 3\n%11 = OpTypeInt 32 0\n%10 = OpTypeVector %11 3\n%12 = OpTypeMatrix %9 3\n%13 = OpTypeMatrix %9 4\n%14 = OpTypeVector %5 3\n%15 = OpConstant  %3  1\n%16 = OpConstantComposite  %4  %15 %15 %15 %15\n%17 = OpConstant  %3  0\n%18 = OpConstantComposite  %4  %17 %17 %17 %17\n%19 = OpConstant  %3  0.5\n%20 = OpConstantComposite  %4  %19 %19 %19 %19\n%21 = OpConstant  %5  1\n%22 = OpConstantComposite  %6  %21 %21 %21 %21\n%23 = OpConstantFalse  %7\n%24 = OpConstantTrue  %7\n%27 = OpTypeFunction %4\n%28 = OpConstant  %5  0\n%29 = OpConstant  %3  0.1\n%30 = OpConstantComposite  %6  %28 %28 %28 %28\n%34 = OpTypeVector %7 4\n%51 = OpTypeFunction %6 %6 %6\n%55 = OpConstantComposite  %6  %28 %28 %28 %28\n%57 = OpConstant  %5  -2147483648\n%58 = OpConstant  %5  -1\n%59 = OpConstantComposite  %6  %57 %57 %57 %57\n%60 = OpConstantComposite  %6  %58 %58 %58 %58\n%65 = OpConstantComposite  %6  %21 %21 %21 %21\n%72 = OpTypeFunction %4 %3 %5\n%73 = OpConstant  %3  2\n%74 = OpConstantComposite  %8  %73 %73\n%75 = OpConstant  %3  4\n%76 = OpConstantComposite  %8  %75 %75\n%77 = OpConstant  %3  8\n%78 = OpConstantComposite  %8  %77 %77\n%79 = OpConstant  %5  2\n%80 = OpConstantComposite  %6  %79 %79 %79 %79\n%93 = OpTypeFunction %8\n%94 = OpConstantComposite  %8  %15 %15\n%95 = OpConstant  %3  3\n%96 = OpConstantComposite  %8  %95 %95\n%98 = OpTypePointer Function %8\n%110 = OpTypeFunction %9 %9\n%112 = OpTypeVector %7 3\n%113 = OpConstantComposite  %9  %17 %17 %17\n%115 = OpConstantComposite  %9  %15 %15 %15\n%119 = OpTypeFunction %7\n%132 = OpTypeFunction %2\n%133 = OpTypeVector %7 2\n%134 = OpConstantComposite  %133  %24 %24\n%135 = OpConstantComposite  %112  %24 %24 %24\n%136 = OpConstantComposite  %112  %23 %23 %23\n%137 = OpConstantComposite  %34  %24 %24 %24 %24\n%138 = OpConstantComposite  %34  %23 %23 %23 %23\n%140 = OpTypePointer Function %7\n%141 = OpConstantNull  %7\n%143 = OpConstantNull  %7\n%145 = OpConstantNull  %7\n%147 = OpConstantNull  %7\n%149 = OpConstantNull  %7\n%151 = OpConstantNull  %7\n%153 = OpConstantNull  %7\n%200 = OpTypeFunction %5 %5 %5\n%212 = OpTypeFunction %11 %11 %11\n%216 = OpConstant  %11  0\n%218 = OpConstant  %11  1\n%221 = OpTypeVector %5 2\n%223 = OpTypeFunction %221 %221 %221\n%227 = OpConstantComposite  %221  %28 %28\n%229 = OpConstantComposite  %221  %57 %57\n%230 = OpConstantComposite  %221  %58 %58\n%235 = OpConstantComposite  %221  %21 %21\n%239 = OpTypeFunction %10 %10 %10\n%243 = OpConstantComposite  %10  %216 %216 %216\n%245 = OpConstantComposite  %10  %218 %218 %218\n%284 = OpTypeVector %11 2\n%286 = OpTypeFunction %284 %284 %284\n%290 = OpConstantComposite  %284  %216 %216\n%292 = OpConstantComposite  %284  %218 %218\n%304 = OpConstant  %11  2\n%305 = OpConstantComposite  %221  %79 %79\n%306 = OpConstantComposite  %10  %304 %304 %304\n%307 = OpConstantComposite  %4  %73 %73 %73 %73\n%308 = OpConstantComposite  %4  %15 %15 %15 %15\n%309 = OpConstantComposite  %284  %304 %304\n%310 = OpConstantComposite  %9  %17 %17 %17\n%311 = OpConstantComposite  %12  %310 %310 %310\n%312 = OpConstantNull  %12\n%313 = OpConstantNull  %13\n%314 = OpConstantComposite  %9  %73 %73 %73\n%316 = OpTypePointer Function %5\n%317 = OpConstantNull  %5\n%319 = OpConstantNull  %5\n%464 = OpConstantNull  %14\n%466 = OpConstantNull  %5\n%468 = OpTypePointer Function %14\n%557 = OpTypePointer Input %10\n%556 = OpVariable  %557  Input\n%560 = OpConstantComposite  %9  %15 %15 %15\n%26 = OpFunction  %4  None %27\n%25 = OpLabel\nOpBranch %31\n%31 = OpLabel\n%32 = OpSelect  %5  %24 %21 %28\n%35 = OpCompositeConstruct  %34  %24 %24 %24 %24\n%33 = OpSelect  %4  %35 %16 %18\n%36 = OpExtInst  %4  %1 FMix %18 %16 %20\n%38 = OpCompositeConstruct  %4  %29 %29 %29 %29\n%37 = OpExtInst  %4  %1 FMix %18 %16 %38\n%39 = OpBitcast  %3  %21\n%40 = OpBitcast  %4  %22\n%41 = OpCompositeConstruct  %6  %32 %32 %32 %32\n%42 = OpIAdd  %6  %41 %30\n%43 = OpConvertSToF  %4  %42\n%44 = OpFAdd  %4  %43 %33\n%45 = OpFAdd  %4  %44 %36\n%46 = OpFAdd  %4  %45 %37\n%47 = OpCompositeConstruct  %4  %39 %39 %39 %39\n%48 = OpFAdd  %4  %46 %47\n%49 = OpFAdd  %4  %48 %40\nOpReturnValue %49\nOpFunctionEnd\n%50 = OpFunction  %6  None %51\n%52 = OpFunctionParameter  %6\n%53 = OpFunctionParameter  %6\n%54 = OpLabel\n%56 = OpIEqual  %34  %53 %55\n%61 = OpIEqual  %34  %52 %59\n%62 = OpIEqual  %34  %53 %60\n%63 = OpLogicalAnd  %34  %61 %62\n%64 = OpLogicalOr  %34  %56 %63\n%66 = OpSelect  %6  %64 %65 %53\n%67 = OpSRem  %6  %52 %66\nOpReturnValue %67\nOpFunctionEnd\n%71 = OpFunction  %4  None %72\n%69 = OpFunctionParameter  %3\n%70 = OpFunctionParameter  %5\n%68 = OpLabel\nOpBranch %81\n%81 = OpLabel\n%82 = OpCompositeConstruct  %8  %69 %69\n%83 = OpFAdd  %8  %74 %82\n%84 = OpFSub  %8  %83 %76\n%85 = OpFDiv  %8  %84 %78\n%86 = OpCompositeConstruct  %6  %70 %70 %70 %70\n%87 = OpFunctionCall  %6  %50 %86 %80\n%88 = OpVectorShuffle  %4  %85 %85 0 1 0 1\n%89 = OpConvertSToF  %4  %87\n%90 = OpFAdd  %4  %88 %89\nOpReturnValue %90\nOpFunctionEnd\n%92 = OpFunction  %8  None %93\n%91 = OpLabel\n%97 = OpVariable  %98  Function %74\nOpBranch %99\n%99 = OpLabel\n%100 = OpLoad  %8  %97\n%101 = OpFAdd  %8  %100 %94\nOpStore %97 %101\n%102 = OpLoad  %8  %97\n%103 = OpFSub  %8  %102 %96\nOpStore %97 %103\n%104 = OpLoad  %8  %97\n%105 = OpFDiv  %8  %104 %76\nOpStore %97 %105\n%106 = OpLoad  %8  %97\nOpReturnValue %106\nOpFunctionEnd\n%109 = OpFunction  %9  None %110\n%108 = OpFunctionParameter  %9\n%107 = OpLabel\nOpBranch %111\n%111 = OpLabel\n%114 = OpFUnordNotEqual  %112  %108 %113\n%116 = OpSelect  %9  %114 %115 %113\nOpReturnValue %116\nOpFunctionEnd\n%118 = OpFunction  %7  None %119\n%117 = OpLabel\nOpBranch %120\n%120 = OpLabel\nOpReturnValue %24\nOpFunctionEnd\n%122 = OpFunction  %7  None %119\n%121 = OpLabel\nOpBranch %123\n%123 = OpLabel\nOpReturnValue %23\nOpFunctionEnd\n%125 = OpFunction  %7  None %119\n%124 = OpLabel\nOpBranch %126\n%126 = OpLabel\nOpReturnValue %24\nOpFunctionEnd\n%128 = OpFunction  %7  None %119\n%127 = OpLabel\nOpBranch %129\n%129 = OpLabel\nOpReturnValue %23\nOpFunctionEnd\n%131 = OpFunction  %2  None %132\n%130 = OpLabel\n%148 = OpVariable  %140  Function %149\n%142 = OpVariable  %140  Function %143\n%152 = OpVariable  %140  Function %153\n%146 = OpVariable  %140  Function %147\n%139 = OpVariable  %140  Function %141\n%150 = OpVariable  %140  Function %151\n%144 = OpVariable  %140  Function %145\nOpBranch %154\n%154 = OpLabel\n%155 = OpLogicalNot  %7  %24\n%156 = OpLogicalNot  %133  %134\n%157 = OpLogicalNot  %7  %24\nOpSelectionMerge %158 None\nOpBranchConditional %157 %159 %160\n%159 = OpLabel\nOpStore %139 %23\nOpBranch %158\n%160 = OpLabel\nOpStore %139 %24\nOpBranch %158\n%158 = OpLabel\n%161 = OpLoad  %7  %139\nOpSelectionMerge %162 None\nOpBranchConditional %24 %163 %164\n%163 = OpLabel\nOpStore %142 %23\nOpBranch %162\n%164 = OpLabel\nOpStore %142 %23\nOpBranch %162\n%162 = OpLabel\n%165 = OpLoad  %7  %142\n%166 = OpLogicalOr  %7  %24 %23\n%167 = OpLogicalOr  %112  %135 %136\n%168 = OpLogicalAnd  %7  %24 %23\n%169 = OpLogicalAnd  %34  %137 %138\n%170 = OpLogicalNot  %7  %23\nOpSelectionMerge %171 None\nOpBranchConditional %170 %172 %173\n%172 = OpLabel\nOpStore %144 %23\nOpBranch %171\n%173 = OpLabel\nOpStore %144 %24\nOpBranch %171\n%171 = OpLabel\n%174 = OpLoad  %7  %144\n%175 = OpLogicalNot  %7  %174\n%176 = OpFunctionCall  %7  %118\n%177 = OpLogicalNot  %7  %176\nOpSelectionMerge %178 None\nOpBranchConditional %177 %179 %180\n%179 = OpLabel\n%181 = OpFunctionCall  %7  %122\nOpStore %146 %181\nOpBranch %178\n%180 = OpLabel\nOpStore %146 %24\nOpBranch %178\n%178 = OpLabel\n%182 = OpLoad  %7  %146\nOpSelectionMerge %183 None\nOpBranchConditional %182 %184 %185\n%184 = OpLabel\n%186 = OpFunctionCall  %7  %125\n%187 = OpLogicalNot  %7  %186\nOpSelectionMerge %188 None\nOpBranchConditional %187 %189 %190\n%189 = OpLabel\n%191 = OpFunctionCall  %7  %128\nOpStore %150 %191\nOpBranch %188\n%190 = OpLabel\nOpStore %150 %24\nOpBranch %188\n%188 = OpLabel\n%192 = OpLoad  %7  %150\nOpStore %148 %192\nOpBranch %183\n%185 = OpLabel\nOpStore %148 %23\nOpBranch %183\n%183 = OpLabel\n%193 = OpLoad  %7  %148\nOpSelectionMerge %194 None\nOpBranchConditional %23 %195 %196\n%195 = OpLabel\n%197 = OpFunctionCall  %7  %122\nOpStore %152 %197\nOpBranch %194\n%196 = OpLabel\nOpStore %152 %24\nOpBranch %194\n%194 = OpLabel\n%198 = OpLoad  %7  %152\nOpReturn\nOpFunctionEnd\n%199 = OpFunction  %5  None %200\n%201 = OpFunctionParameter  %5\n%202 = OpFunctionParameter  %5\n%203 = OpLabel\n%204 = OpIEqual  %7  %202 %28\n%205 = OpIEqual  %7  %201 %57\n%206 = OpIEqual  %7  %202 %58\n%207 = OpLogicalAnd  %7  %205 %206\n%208 = OpLogicalOr  %7  %204 %207\n%209 = OpSelect  %5  %208 %21 %202\n%210 = OpSDiv  %5  %201 %209\nOpReturnValue %210\nOpFunctionEnd\n%211 = OpFunction  %11  None %212\n%213 = OpFunctionParameter  %11\n%214 = OpFunctionParameter  %11\n%215 = OpLabel\n%217 = OpIEqual  %7  %214 %216\n%219 = OpSelect  %11  %217 %218 %214\n%220 = OpUDiv  %11  %213 %219\nOpReturnValue %220\nOpFunctionEnd\n%222 = OpFunction  %221  None %223\n%224 = OpFunctionParameter  %221\n%225 = OpFunctionParameter  %221\n%226 = OpLabel\n%228 = OpIEqual  %133  %225 %227\n%231 = OpIEqual  %133  %224 %229\n%232 = OpIEqual  %133  %225 %230\n%233 = OpLogicalAnd  %133  %231 %232\n%234 = OpLogicalOr  %133  %228 %233\n%236 = OpSelect  %221  %234 %235 %225\n%237 = OpSDiv  %221  %224 %236\nOpReturnValue %237\nOpFunctionEnd\n%238 = OpFunction  %10  None %239\n%240 = OpFunctionParameter  %10\n%241 = OpFunctionParameter  %10\n%242 = OpLabel\n%244 = OpIEqual  %112  %241 %243\n%246 = OpSelect  %10  %244 %245 %241\n%247 = OpUDiv  %10  %240 %246\nOpReturnValue %247\nOpFunctionEnd\n%248 = OpFunction  %5  None %200\n%249 = OpFunctionParameter  %5\n%250 = OpFunctionParameter  %5\n%251 = OpLabel\n%252 = OpIEqual  %7  %250 %28\n%253 = OpIEqual  %7  %249 %57\n%254 = OpIEqual  %7  %250 %58\n%255 = OpLogicalAnd  %7  %253 %254\n%256 = OpLogicalOr  %7  %252 %255\n%257 = OpSelect  %5  %256 %21 %250\n%258 = OpSRem  %5  %249 %257\nOpReturnValue %258\nOpFunctionEnd\n%259 = OpFunction  %11  None %212\n%260 = OpFunctionParameter  %11\n%261 = OpFunctionParameter  %11\n%262 = OpLabel\n%263 = OpIEqual  %7  %261 %216\n%264 = OpSelect  %11  %263 %218 %261\n%265 = OpUMod  %11  %260 %264\nOpReturnValue %265\nOpFunctionEnd\n%266 = OpFunction  %221  None %223\n%267 = OpFunctionParameter  %221\n%268 = OpFunctionParameter  %221\n%269 = OpLabel\n%270 = OpIEqual  %133  %268 %227\n%271 = OpIEqual  %133  %267 %229\n%272 = OpIEqual  %133  %268 %230\n%273 = OpLogicalAnd  %133  %271 %272\n%274 = OpLogicalOr  %133  %270 %273\n%275 = OpSelect  %221  %274 %235 %268\n%276 = OpSRem  %221  %267 %275\nOpReturnValue %276\nOpFunctionEnd\n%277 = OpFunction  %10  None %239\n%278 = OpFunctionParameter  %10\n%279 = OpFunctionParameter  %10\n%280 = OpLabel\n%281 = OpIEqual  %112  %279 %243\n%282 = OpSelect  %10  %281 %245 %279\n%283 = OpUMod  %10  %278 %282\nOpReturnValue %283\nOpFunctionEnd\n%285 = OpFunction  %284  None %286\n%287 = OpFunctionParameter  %284\n%288 = OpFunctionParameter  %284\n%289 = OpLabel\n%291 = OpIEqual  %133  %288 %290\n%293 = OpSelect  %284  %291 %292 %288\n%294 = OpUDiv  %284  %287 %293\nOpReturnValue %294\nOpFunctionEnd\n%295 = OpFunction  %284  None %286\n%296 = OpFunctionParameter  %284\n%297 = OpFunctionParameter  %284\n%298 = OpLabel\n%299 = OpIEqual  %133  %297 %290\n%300 = OpSelect  %284  %299 %292 %297\n%301 = OpUMod  %284  %296 %300\nOpReturnValue %301\nOpFunctionEnd\n%303 = OpFunction  %2  None %132\n%302 = OpLabel\n%315 = OpVariable  %316  Function %317\n%318 = OpVariable  %316  Function %319\nOpBranch %320\n%320 = OpLabel\n%321 = OpFNegate  %3  %15\n%322 = OpSNegate  %221  %235\n%323 = OpFNegate  %8  %94\n%324 = OpIAdd  %5  %79 %21\n%325 = OpIAdd  %11  %304 %218\n%326 = OpFAdd  %3  %73 %15\n%327 = OpIAdd  %221  %305 %235\n%328 = OpIAdd  %10  %306 %245\n%329 = OpFAdd  %4  %307 %308\n%330 = OpISub  %5  %79 %21\n%331 = OpISub  %11  %304 %218\n%332 = OpFSub  %3  %73 %15\n%333 = OpISub  %221  %305 %235\n%334 = OpISub  %10  %306 %245\n%335 = OpFSub  %4  %307 %308\n%336 = OpIMul  %5  %79 %21\n%337 = OpIMul  %11  %304 %218\n%338 = OpFMul  %3  %73 %15\n%339 = OpIMul  %221  %305 %235\n%340 = OpIMul  %10  %306 %245\n%341 = OpFMul  %4  %307 %308\n%342 = OpFunctionCall  %5  %199 %79 %21\n%343 = OpFunctionCall  %11  %211 %304 %218\n%344 = OpFDiv  %3  %73 %15\n%345 = OpFunctionCall  %221  %222 %305 %235\n%346 = OpFunctionCall  %10  %238 %306 %245\n%347 = OpFDiv  %4  %307 %308\n%348 = OpFunctionCall  %5  %248 %79 %21\n%349 = OpFunctionCall  %11  %259 %304 %218\n%350 = OpFRem  %3  %73 %15\n%351 = OpFunctionCall  %221  %266 %305 %235\n%352 = OpFunctionCall  %10  %277 %306 %245\n%353 = OpFRem  %4  %307 %308\nOpBranch %354\n%354 = OpLabel\n%356 = OpIAdd  %221  %305 %235\n%357 = OpIAdd  %221  %305 %235\n%358 = OpIAdd  %284  %309 %292\n%359 = OpIAdd  %284  %309 %292\n%360 = OpFAdd  %8  %74 %94\n%361 = OpFAdd  %8  %74 %94\n%362 = OpISub  %221  %305 %235\n%363 = OpISub  %221  %305 %235\n%364 = OpISub  %284  %309 %292\n%365 = OpISub  %284  %309 %292\n%366 = OpFSub  %8  %74 %94\n%367 = OpFSub  %8  %74 %94\n%369 = OpCompositeConstruct  %221  %21 %21\n%368 = OpIMul  %221  %305 %369\n%371 = OpCompositeConstruct  %221  %79 %79\n%370 = OpIMul  %221  %235 %371\n%373 = OpCompositeConstruct  %284  %218 %218\n%372 = OpIMul  %284  %309 %373\n%375 = OpCompositeConstruct  %284  %304 %304\n%374 = OpIMul  %284  %292 %375\n%376 = OpVectorTimesScalar  %8  %74 %15\n%377 = OpVectorTimesScalar  %8  %94 %73\n%378 = OpFunctionCall  %221  %222 %305 %235\n%379 = OpFunctionCall  %221  %222 %305 %235\n%380 = OpFunctionCall  %284  %285 %309 %292\n%381 = OpFunctionCall  %284  %285 %309 %292\n%382 = OpFDiv  %8  %74 %94\n%383 = OpFDiv  %8  %74 %94\n%384 = OpFunctionCall  %221  %266 %305 %235\n%385 = OpFunctionCall  %221  %266 %305 %235\n%386 = OpFunctionCall  %284  %295 %309 %292\n%387 = OpFunctionCall  %284  %295 %309 %292\n%388 = OpFRem  %8  %74 %94\n%389 = OpFRem  %8  %74 %94\nOpBranch %355\n%355 = OpLabel\n%390 = OpMatrixTimesScalar  %12  %312 %15\n%391 = OpMatrixTimesScalar  %12  %312 %73\n%392 = OpMatrixTimesVector  %9  %313 %308\n%393 = OpVectorTimesMatrix  %4  %314 %313\n%394 = OpLoad  %5  %315\n%395 = OpIAdd  %5  %394 %57\nOpStore %318 %395\nOpReturn\nOpFunctionEnd\n%397 = OpFunction  %2  None %132\n%396 = OpLabel\nOpBranch %398\n%398 = OpLabel\n%399 = OpNot  %5  %21\n%400 = OpNot  %11  %218\n%401 = OpNot  %221  %235\n%402 = OpNot  %10  %245\n%403 = OpBitwiseOr  %5  %79 %21\n%404 = OpBitwiseOr  %11  %304 %218\n%405 = OpBitwiseOr  %221  %305 %235\n%406 = OpBitwiseOr  %10  %306 %245\n%407 = OpBitwiseAnd  %5  %79 %21\n%408 = OpBitwiseAnd  %11  %304 %218\n%409 = OpBitwiseAnd  %221  %305 %235\n%410 = OpBitwiseAnd  %10  %306 %245\n%411 = OpBitwiseXor  %5  %79 %21\n%412 = OpBitwiseXor  %11  %304 %218\n%413 = OpBitwiseXor  %221  %305 %235\n%414 = OpBitwiseXor  %10  %306 %245\n%415 = OpShiftLeftLogical  %5  %79 %218\n%416 = OpShiftLeftLogical  %11  %304 %218\n%417 = OpShiftLeftLogical  %221  %305 %292\n%418 = OpShiftLeftLogical  %10  %306 %245\n%419 = OpShiftRightArithmetic  %5  %79 %218\n%420 = OpShiftRightLogical  %11  %304 %218\n%421 = OpShiftRightArithmetic  %221  %305 %292\n%422 = OpShiftRightLogical  %10  %306 %245\nOpReturn\nOpFunctionEnd\n%424 = OpFunction  %2  None %132\n%423 = OpLabel\nOpBranch %425\n%425 = OpLabel\n%426 = OpIEqual  %7  %79 %21\n%427 = OpIEqual  %7  %304 %218\n%428 = OpFOrdEqual  %7  %73 %15\n%429 = OpIEqual  %133  %305 %235\n%430 = OpIEqual  %112  %306 %245\n%431 = OpFOrdEqual  %34  %307 %308\n%432 = OpINotEqual  %7  %79 %21\n%433 = OpINotEqual  %7  %304 %218\n%434 = OpFOrdNotEqual  %7  %73 %15\n%435 = OpINotEqual  %133  %305 %235\n%436 = OpINotEqual  %112  %306 %245\n%437 = OpFOrdNotEqual  %34  %307 %308\n%438 = OpSLessThan  %7  %79 %21\n%439 = OpULessThan  %7  %304 %218\n%440 = OpFOrdLessThan  %7  %73 %15\n%441 = OpSLessThan  %133  %305 %235\n%442 = OpULessThan  %112  %306 %245\n%443 = OpFOrdLessThan  %34  %307 %308\n%444 = OpSLessThanEqual  %7  %79 %21\n%445 = OpULessThanEqual  %7  %304 %218\n%446 = OpFOrdLessThanEqual  %7  %73 %15\n%447 = OpSLessThanEqual  %133  %305 %235\n%448 = OpULessThanEqual  %112  %306 %245\n%449 = OpFOrdLessThanEqual  %34  %307 %308\n%450 = OpSGreaterThan  %7  %79 %21\n%451 = OpUGreaterThan  %7  %304 %218\n%452 = OpFOrdGreaterThan  %7  %73 %15\n%453 = OpSGreaterThan  %133  %305 %235\n%454 = OpUGreaterThan  %112  %306 %245\n%455 = OpFOrdGreaterThan  %34  %307 %308\n%456 = OpSGreaterThanEqual  %7  %79 %21\n%457 = OpUGreaterThanEqual  %7  %304 %218\n%458 = OpFOrdGreaterThanEqual  %7  %73 %15\n%459 = OpSGreaterThanEqual  %133  %305 %235\n%460 = OpUGreaterThanEqual  %112  %306 %245\n%461 = OpFOrdGreaterThanEqual  %34  %307 %308\nOpReturn\nOpFunctionEnd\n%463 = OpFunction  %2  None %132\n%462 = OpLabel\n%465 = OpVariable  %316  Function %466\n%467 = OpVariable  %468  Function %464\nOpBranch %469\n%469 = OpLabel\nOpStore %465 %21\n%470 = OpLoad  %5  %465\n%471 = OpIAdd  %5  %470 %21\nOpStore %465 %471\n%472 = OpLoad  %5  %465\n%473 = OpISub  %5  %472 %21\nOpStore %465 %473\n%474 = OpLoad  %5  %465\n%475 = OpLoad  %5  %465\n%476 = OpIMul  %5  %474 %475\nOpStore %465 %476\n%477 = OpLoad  %5  %465\n%478 = OpLoad  %5  %465\n%479 = OpFunctionCall  %5  %199 %477 %478\nOpStore %465 %479\n%480 = OpLoad  %5  %465\n%481 = OpFunctionCall  %5  %248 %480 %21\nOpStore %465 %481\n%482 = OpLoad  %5  %465\n%483 = OpBitwiseAnd  %5  %482 %28\nOpStore %465 %483\n%484 = OpLoad  %5  %465\n%485 = OpBitwiseOr  %5  %484 %28\nOpStore %465 %485\n%486 = OpLoad  %5  %465\n%487 = OpBitwiseXor  %5  %486 %28\nOpStore %465 %487\n%488 = OpLoad  %5  %465\n%489 = OpShiftLeftLogical  %5  %488 %304\nOpStore %465 %489\n%490 = OpLoad  %5  %465\n%491 = OpShiftRightArithmetic  %5  %490 %218\nOpStore %465 %491\n%492 = OpLoad  %5  %465\n%493 = OpIAdd  %5  %492 %21\nOpStore %465 %493\n%494 = OpLoad  %5  %465\n%495 = OpISub  %5  %494 %21\nOpStore %465 %495\n%496 = OpAccessChain  %316  %467 %218\n%497 = OpLoad  %5  %496\n%498 = OpIAdd  %5  %497 %21\n%499 = OpAccessChain  %316  %467 %218\nOpStore %499 %498\n%500 = OpAccessChain  %316  %467 %218\n%501 = OpLoad  %5  %500\n%502 = OpISub  %5  %501 %21\n%503 = OpAccessChain  %316  %467 %218\nOpStore %503 %502\nOpReturn\nOpFunctionEnd\n%505 = OpFunction  %2  None %132\n%504 = OpLabel\nOpBranch %506\n%506 = OpLabel\n%507 = OpSNegate  %5  %21\n%508 = OpSNegate  %5  %21\n%509 = OpSNegate  %5  %508\n%510 = OpSNegate  %5  %21\n%511 = OpSNegate  %5  %510\n%512 = OpSNegate  %5  %21\n%513 = OpSNegate  %5  %512\n%514 = OpSNegate  %5  %21\n%515 = OpSNegate  %5  %514\n%516 = OpSNegate  %5  %515\n%517 = OpSNegate  %5  %21\n%518 = OpSNegate  %5  %517\n%519 = OpSNegate  %5  %518\n%520 = OpSNegate  %5  %519\n%521 = OpSNegate  %5  %21\n%522 = OpSNegate  %5  %521\n%523 = OpSNegate  %5  %522\n%524 = OpSNegate  %5  %523\n%525 = OpSNegate  %5  %524\n%526 = OpSNegate  %5  %21\n%527 = OpSNegate  %5  %526\n%528 = OpSNegate  %5  %527\n%529 = OpSNegate  %5  %528\n%530 = OpSNegate  %5  %529\n%531 = OpFNegate  %3  %15\n%532 = OpFNegate  %3  %15\n%533 = OpFNegate  %3  %532\n%534 = OpFNegate  %3  %15\n%535 = OpFNegate  %3  %534\n%536 = OpFNegate  %3  %15\n%537 = OpFNegate  %3  %536\n%538 = OpFNegate  %3  %15\n%539 = OpFNegate  %3  %538\n%540 = OpFNegate  %3  %539\n%541 = OpFNegate  %3  %15\n%542 = OpFNegate  %3  %541\n%543 = OpFNegate  %3  %542\n%544 = OpFNegate  %3  %543\n%545 = OpFNegate  %3  %15\n%546 = OpFNegate  %3  %545\n%547 = OpFNegate  %3  %546\n%548 = OpFNegate  %3  %547\n%549 = OpFNegate  %3  %548\n%550 = OpFNegate  %3  %15\n%551 = OpFNegate  %3  %550\n%552 = OpFNegate  %3  %551\n%553 = OpFNegate  %3  %552\n%554 = OpFNegate  %3  %553\nOpReturn\nOpFunctionEnd\n%559 = OpFunction  %2  None %132\n%555 = OpLabel\n%558 = OpLoad  %10  %556\nOpBranch %561\n%561 = OpLabel\n%562 = OpFunctionCall  %4  %26\n%563 = OpCompositeExtract  %11  %558 0\n%564 = OpConvertUToF  %3  %563\n%565 = OpCompositeExtract  %11  %558 1\n%566 = OpBitcast  %5  %565\n%567 = OpFunctionCall  %4  %71 %564 %566\n%568 = OpFunctionCall  %8  %92\n%569 = OpFunctionCall  %9  %109 %560\n%570 = OpFunctionCall  %2  %131\n%571 = OpFunctionCall  %2  %303\n%572 = OpFunctionCall  %2  %397\n%573 = OpFunctionCall  %2  %424\n%574 = OpFunctionCall  %2  %463\n%575 = OpFunctionCall  %2  %505\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-overrides-atomicCompareExchangeWeak.f.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 29\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %11 \"f\" %17\nOpExecutionMode %11 LocalSize 1 1 1\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 4\nOpDecorate %17 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 1\n%4 = OpTypeInt 32 0\n%5 = OpTypeBool\n%6 = OpTypeStruct %4 %5\n%7 = OpConstant  %3  2\n%9 = OpTypePointer Workgroup %4\n%8 = OpVariable  %9  Workgroup\n%12 = OpTypeFunction %2\n%13 = OpConstant  %4  2\n%14 = OpConstant  %4  1\n%16 = OpConstantNull  %4\n%18 = OpTypePointer Input %4\n%17 = OpVariable  %18  Input\n%20 = OpConstant  %4  0\n%24 = OpConstant  %4  264\n%11 = OpFunction  %2  None %12\n%10 = OpLabel\nOpBranch %15\n%15 = OpLabel\n%19 = OpLoad  %4  %17\n%21 = OpIEqual  %5  %19 %20\nOpSelectionMerge %22 None\nOpBranchConditional %21 %23 %22\n%23 = OpLabel\nOpStore %8 %16\nOpBranch %22\n%22 = OpLabel\nOpControlBarrier %13 %13 %24\nOpBranch %25\n%25 = OpLabel\n%27 = OpAtomicCompareExchange  %4  %8 %7 %20 %20 %14 %13\n%28 = OpIEqual  %5  %27 %13\n%26 = OpCompositeConstruct  %6  %27 %28\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-overrides-ray-query.main.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 164\nOpCapability Shader\nOpCapability RayQueryKHR\nOpExtension \"SPV_KHR_ray_query\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %13 \"main\" %10\nOpExecutionMode %13 LocalSize 1 1 1\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 1 Offset 4\nOpMemberDecorate %8 2 Offset 8\nOpMemberDecorate %8 3 Offset 12\nOpMemberDecorate %8 4 Offset 16\nOpMemberDecorate %8 5 Offset 32\nOpDecorate %10 DescriptorSet 0\nOpDecorate %10 Binding 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeAccelerationStructureKHR\n%5 = OpTypeRayQueryKHR\n%6 = OpTypeInt 32 0\n%7 = OpTypeVector %3 3\n%8 = OpTypeStruct %6 %6 %3 %3 %7 %7\n%9 = OpConstant  %3  2\n%11 = OpTypePointer UniformConstant %4\n%10 = OpVariable  %11  UniformConstant\n%14 = OpTypeFunction %2\n%16 = OpConstant  %6  4\n%17 = OpConstant  %6  255\n%18 = OpConstant  %3  34\n%19 = OpConstant  %3  38\n%20 = OpConstant  %3  46\n%21 = OpConstantComposite  %7  %20 %20 %20\n%22 = OpConstant  %3  58\n%23 = OpConstant  %3  62\n%24 = OpConstant  %3  74\n%25 = OpConstantComposite  %7  %22 %23 %24\n%26 = OpConstantComposite  %8  %16 %17 %18 %19 %21 %25\n%28 = OpTypePointer Function %5\n%29 = OpTypePointer Function %6\n%31 = OpConstant  %6  0\n%32 = OpTypePointer Function %3\n%34 = OpConstant  %3  0\n%36 = OpTypeBool\n%37 = OpTypeVector %36 3\n%38 = OpTypeFunction %2 %28 %4 %8 %29 %32\n%66 = OpConstant  %6  256\n%69 = OpConstant  %6  512\n%74 = OpConstant  %6  16\n%77 = OpConstant  %6  32\n%86 = OpConstant  %6  1\n%89 = OpConstant  %6  2\n%92 = OpConstant  %6  64\n%95 = OpConstant  %6  128\n%124 = OpTypeVector %6 2\n%125 = OpTypePointer Function %124\n%126 = OpTypeVector %36 2\n%127 = OpConstantComposite  %124  %31 %31\n%128 = OpConstant  %6  4294967295\n%129 = OpConstantComposite  %124  %128 %128\n%142 = OpTypePointer Function %36\n%143 = OpTypeFunction %36 %28 %29\n%149 = OpConstantFalse  %36\n%156 = OpConstant  %6  6\n%39 = OpFunction  %2  None %38\n%40 = OpFunctionParameter  %28\n%41 = OpFunctionParameter  %4\n%42 = OpFunctionParameter  %8\n%43 = OpFunctionParameter  %29\n%44 = OpFunctionParameter  %32\n%45 = OpLabel\n%46 = OpCompositeExtract  %6  %42 0\n%47 = OpCompositeExtract  %6  %42 1\n%48 = OpCompositeExtract  %3  %42 2\n%49 = OpCompositeExtract  %3  %42 3\nOpStore %44 %49\n%50 = OpCompositeExtract  %7  %42 4\n%51 = OpCompositeExtract  %7  %42 5\n%52 = OpFOrdLessThanEqual  %36  %48 %49\n%53 = OpFOrdGreaterThanEqual  %36  %48 %34\n%54 = OpIsInf  %37  %50\n%55 = OpAny  %36  %54\n%56 = OpIsNan  %37  %50\n%57 = OpAny  %36  %56\n%58 = OpLogicalOr  %36  %57 %55\n%59 = OpLogicalNot  %36  %58\n%60 = OpIsInf  %37  %51\n%61 = OpAny  %36  %60\n%62 = OpIsNan  %37  %51\n%63 = OpAny  %36  %62\n%64 = OpLogicalOr  %36  %63 %61\n%65 = OpLogicalNot  %36  %64\n%67 = OpBitwiseAnd  %6  %46 %66\n%68 = OpINotEqual  %36  %67 %31\n%70 = OpBitwiseAnd  %6  %46 %69\n%71 = OpINotEqual  %36  %70 %31\n%72 = OpLogicalAnd  %36  %71 %68\n%73 = OpLogicalNot  %36  %72\n%75 = OpBitwiseAnd  %6  %46 %74\n%76 = OpINotEqual  %36  %75 %31\n%78 = OpBitwiseAnd  %6  %46 %77\n%79 = OpINotEqual  %36  %78 %31\n%80 = OpLogicalAnd  %36  %79 %68\n%81 = OpLogicalAnd  %36  %79 %76\n%82 = OpLogicalAnd  %36  %76 %68\n%83 = OpLogicalOr  %36  %82 %80\n%84 = OpLogicalOr  %36  %83 %81\n%85 = OpLogicalNot  %36  %84\n%87 = OpBitwiseAnd  %6  %46 %86\n%88 = OpINotEqual  %36  %87 %31\n%90 = OpBitwiseAnd  %6  %46 %89\n%91 = OpINotEqual  %36  %90 %31\n%93 = OpBitwiseAnd  %6  %46 %92\n%94 = OpINotEqual  %36  %93 %31\n%96 = OpBitwiseAnd  %6  %46 %95\n%97 = OpINotEqual  %36  %96 %31\n%98 = OpLogicalAnd  %36  %97 %88\n%99 = OpLogicalAnd  %36  %97 %91\n%100 = OpLogicalAnd  %36  %97 %94\n%101 = OpLogicalAnd  %36  %94 %88\n%102 = OpLogicalAnd  %36  %94 %91\n%103 = OpLogicalAnd  %36  %91 %88\n%104 = OpLogicalOr  %36  %103 %98\n%105 = OpLogicalOr  %36  %104 %99\n%106 = OpLogicalOr  %36  %105 %100\n%107 = OpLogicalOr  %36  %106 %101\n%108 = OpLogicalOr  %36  %107 %102\n%109 = OpLogicalNot  %36  %108\n%110 = OpLogicalAnd  %36  %109 %52\n%111 = OpLogicalAnd  %36  %110 %53\n%112 = OpLogicalAnd  %36  %111 %59\n%113 = OpLogicalAnd  %36  %112 %65\n%114 = OpLogicalAnd  %36  %113 %73\n%115 = OpLogicalAnd  %36  %114 %85\nOpSelectionMerge %116 None\nOpBranchConditional %115 %118 %117\n%118 = OpLabel\nOpRayQueryInitializeKHR %40 %41 %46 %47 %50 %48 %51 %49\nOpStore %43 %86\nOpBranch %116\n%117 = OpLabel\nOpBranch %116\n%116 = OpLabel\nOpReturn\nOpFunctionEnd\n%144 = OpFunction  %36  None %143\n%145 = OpFunctionParameter  %28\n%146 = OpFunctionParameter  %29\n%147 = OpLabel\n%148 = OpVariable  %142  Function %149\n%150 = OpLoad  %6  %146\n%153 = OpBitwiseAnd  %6  %150 %86\n%154 = OpINotEqual  %36  %153 %31\nOpSelectionMerge %151 None\nOpBranchConditional %154 %152 %151\n%152 = OpLabel\n%155 = OpRayQueryProceedKHR  %36  %145\nOpStore %148 %155\n%157 = OpSelect  %6  %155 %89 %156\n%158 = OpBitwiseOr  %6  %150 %157\nOpStore %146 %158\nOpBranch %151\n%151 = OpLabel\n%159 = OpLoad  %36  %148\nOpReturnValue %159\nOpFunctionEnd\n%13 = OpFunction  %2  None %14\n%12 = OpLabel\n%27 = OpVariable  %28  Function\n%30 = OpVariable  %29  Function %31\n%33 = OpVariable  %32  Function %34\n%130 = OpVariable  %125  Function %129\n%15 = OpLoad  %4  %10\nOpBranch %35\n%35 = OpLabel\n%119 = OpFunctionCall  %2  %39 %27 %15 %26 %30 %33\nOpBranch %120\n%120 = OpLabel\nOpLoopMerge %121 %123 None\nOpBranch %131\n%131 = OpLabel\n%132 = OpLoad  %124  %130\n%133 = OpIEqual  %126  %127 %132\n%134 = OpAll  %36  %133\nOpSelectionMerge %135 None\nOpBranchConditional %134 %121 %135\n%135 = OpLabel\n%136 = OpCompositeExtract  %6  %132 1\n%137 = OpIEqual  %36  %136 %31\n%138 = OpSelect  %6  %137 %86 %31\n%139 = OpCompositeConstruct  %124  %138 %86\n%140 = OpISub  %124  %132 %139\nOpStore %130 %140\nOpBranch %122\n%122 = OpLabel\n%141 = OpFunctionCall  %36  %144 %27 %30\nOpSelectionMerge %160 None\nOpBranchConditional %141 %160 %161\n%161 = OpLabel\nOpBranch %121\n%160 = OpLabel\nOpBranch %162\n%162 = OpLabel\nOpBranch %163\n%163 = OpLabel\nOpBranch %123\n%123 = OpLabel\nOpBranch %120\n%121 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-overrides.main.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 35\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %22 \"main\"\nOpExecutionMode %22 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeBool\n%4 = OpTypeFloat 32\n%5 = OpTypeInt 32 0\n%6 = OpConstantTrue  %3\n%7 = OpConstant  %4  2.3\n%8 = OpConstant  %4  0\n%9 = OpConstantFalse  %3\n%10 = OpConstant  %4  1.1\n%11 = OpConstant  %4  2\n%12 = OpConstant  %4  4.6\n%13 = OpConstant  %4  2.718\n%14 = OpConstant  %5  0\n%15 = OpConstant  %4  10\n%16 = OpConstant  %4  11\n%18 = OpTypePointer Private %4\n%17 = OpVariable  %18  Private %16\n%20 = OpConstantNull  %4\n%19 = OpVariable  %18  Private %20\n%23 = OpTypeFunction %2\n%24 = OpConstant  %4  23\n%26 = OpTypePointer Function %4\n%28 = OpTypePointer Function %3\n%29 = OpConstantNull  %3\n%31 = OpConstantNull  %4\n%22 = OpFunction  %2  None %23\n%21 = OpLabel\n%25 = OpVariable  %26  Function %24\n%27 = OpVariable  %28  Function %29\n%30 = OpVariable  %26  Function %31\nOpBranch %32\n%32 = OpLabel\nOpStore %27 %6\n%33 = OpLoad  %4  %17\n%34 = OpFMul  %4  %33 %15\nOpStore %30 %34\nOpStore %19 %10\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-padding.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 50\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %27 \"vertex\" %25\n%3 = OpString \"padding.wgsl\"\nOpSource Unknown 0 %3 \"struct S {\n    a: vec3<f32>,\n}\n\nstruct Test {\n    a: S,\n    b: f32, // offset: 16\n}\n\nstruct Test2 {\n    a: array<vec3<f32>, 2>,\n    b: f32, // offset: 32\n}\n\nstruct Test3 {\n    a: mat4x3<f32>,\n    b: f32, // offset: 64\n}\n\n@group(0) @binding(0)\nvar<uniform> input1: Test;\n\n@group(0) @binding(1)\nvar<uniform> input2: Test2;\n\n@group(0) @binding(2)\nvar<uniform> input3: Test3;\n\n\n@vertex\nfn vertex() -> @builtin(position) vec4<f32> {\n    return vec4<f32>(1.0) * input1.b * input2.b * input3.b;\n}\n\"\nOpMemberName %6 0 \"a\"\nOpName %6 \"S\"\nOpMemberName %7 0 \"a\"\nOpMemberName %7 1 \"b\"\nOpName %7 \"Test\"\nOpMemberName %11 0 \"a\"\nOpMemberName %11 1 \"b\"\nOpName %11 \"Test2\"\nOpMemberName %13 0 \"a\"\nOpMemberName %13 1 \"b\"\nOpName %13 \"Test3\"\nOpName %15 \"input1\"\nOpName %18 \"input2\"\nOpName %21 \"input3\"\nOpName %27 \"vertex\"\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 16\nOpDecorate %8 ArrayStride 16\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 32\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 0 ColMajor\nOpMemberDecorate %13 0 MatrixStride 16\nOpMemberDecorate %13 1 Offset 64\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 0\nOpDecorate %16 Block\nOpMemberDecorate %16 0 Offset 0\nOpDecorate %18 DescriptorSet 0\nOpDecorate %18 Binding 1\nOpDecorate %19 Block\nOpMemberDecorate %19 0 Offset 0\nOpDecorate %21 DescriptorSet 0\nOpDecorate %21 Binding 2\nOpDecorate %22 Block\nOpMemberDecorate %22 0 Offset 0\nOpDecorate %25 BuiltIn Position\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 3\n%6 = OpTypeStruct %5\n%7 = OpTypeStruct %6 %4\n%10 = OpTypeInt 32 0\n%9 = OpConstant  %10  2\n%8 = OpTypeArray %5 %9\n%11 = OpTypeStruct %8 %4\n%12 = OpTypeMatrix %5 4\n%13 = OpTypeStruct %12 %4\n%14 = OpTypeVector %4 4\n%16 = OpTypeStruct %7\n%17 = OpTypePointer Uniform %16\n%15 = OpVariable  %17  Uniform\n%19 = OpTypeStruct %11\n%20 = OpTypePointer Uniform %19\n%18 = OpVariable  %20  Uniform\n%22 = OpTypeStruct %13\n%23 = OpTypePointer Uniform %22\n%21 = OpVariable  %23  Uniform\n%26 = OpTypePointer Output %14\n%25 = OpVariable  %26  Output\n%28 = OpTypeFunction %2\n%29 = OpTypePointer Uniform %7\n%30 = OpConstant  %10  0\n%32 = OpTypePointer Uniform %11\n%34 = OpTypePointer Uniform %13\n%36 = OpConstant  %4  1\n%37 = OpConstantComposite  %14  %36 %36 %36 %36\n%39 = OpTypePointer Uniform %4\n%40 = OpConstant  %10  1\n%27 = OpFunction  %2  None %28\n%24 = OpLabel\n%31 = OpAccessChain  %29  %15 %30\n%33 = OpAccessChain  %32  %18 %30\n%35 = OpAccessChain  %34  %21 %30\nOpBranch %38\n%38 = OpLabel\nOpLine %3 32 12\nOpLine %3 32 12\n%41 = OpAccessChain  %39  %31 %40\n%42 = OpLoad  %4  %41\n%43 = OpVectorTimesScalar  %14  %37 %42\nOpLine %3 32 12\n%44 = OpAccessChain  %39  %33 %40\n%45 = OpLoad  %4  %44\n%46 = OpVectorTimesScalar  %14  %43 %45\nOpLine %3 32 12\n%47 = OpAccessChain  %39  %35 %40\n%48 = OpLoad  %4  %47\n%49 = OpVectorTimesScalar  %14  %46 %48\nOpStore %25 %49\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-per-vertex.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 22\nOpCapability Shader\nOpCapability FragmentBarycentricKHR\nOpExtension \"SPV_KHR_fragment_shader_barycentric\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %14 \"fs_main\" %9 %12\nOpExecutionMode %14 OriginUpperLeft\nOpDecorate %4 ArrayStride 4\nOpDecorate %9 Location 0\nOpDecorate %9 PerVertexKHR\nOpDecorate %12 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%6 = OpTypeInt 32 0\n%5 = OpConstant  %6  3\n%4 = OpTypeArray %3 %5\n%7 = OpTypeVector %3 4\n%10 = OpTypePointer Input %4\n%9 = OpVariable  %10  Input\n%13 = OpTypePointer Output %7\n%12 = OpVariable  %13  Output\n%15 = OpTypeFunction %2\n%16 = OpConstant  %3  1\n%14 = OpFunction  %2  None %15\n%8 = OpLabel\n%11 = OpLoad  %4  %9\nOpBranch %17\n%17 = OpLabel\n%18 = OpCompositeExtract  %3  %11 0\n%19 = OpCompositeExtract  %3  %11 1\n%20 = OpCompositeExtract  %3  %11 2\n%21 = OpCompositeConstruct  %7  %18 %19 %20 %16\nOpStore %12 %21\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-phony_assignment.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 30\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %19 \"main\" %16\nOpExecutionMode %19 LocalSize 1 1 1\nOpDecorate %7 DescriptorSet 0\nOpDecorate %7 Binding 0\nOpDecorate %8 Block\nOpMemberDecorate %8 0 Offset 0\nOpDecorate %16 BuiltIn GlobalInvocationId\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeInt 32 1\n%6 = OpTypeInt 32 0\n%5 = OpTypeVector %6 3\n%8 = OpTypeStruct %3\n%9 = OpTypePointer Uniform %8\n%7 = OpVariable  %9  Uniform\n%12 = OpTypeFunction %4\n%13 = OpConstant  %4  5\n%17 = OpTypePointer Input %5\n%16 = OpVariable  %17  Input\n%20 = OpTypeFunction %2\n%21 = OpTypePointer Uniform %3\n%22 = OpConstant  %6  0\n%11 = OpFunction  %4  None %12\n%10 = OpLabel\nOpBranch %14\n%14 = OpLabel\nOpReturnValue %13\nOpFunctionEnd\n%19 = OpFunction  %2  None %20\n%15 = OpLabel\n%18 = OpLoad  %5  %16\n%23 = OpAccessChain  %21  %7 %22\nOpBranch %24\n%24 = OpLabel\n%25 = OpLoad  %3  %23\n%26 = OpLoad  %3  %23\n%27 = OpFunctionCall  %4  %11\n%28 = OpFunctionCall  %4  %11\n%29 = OpLoad  %3  %23\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-pointers.spvasm",
    "content": "; SPIR-V\n; Version: 1.2\n; Generator: rspirv\n; Bound: 54\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %47 \"main\"\nOpExecutionMode %47 LocalSize 1 1 1\n%3 = OpString \"pointers.wgsl\"\nOpSource Unknown 0 %3 \"fn f() {\n   var v: mat2x2<f32>;\n   let px = &v[0];\n   *px = vec2<f32>(10.0);\n}\n\nstruct DynamicArray {\n    arr: array<u32>\n}\n\n@group(0) @binding(0)\nvar<storage, read_write> dynamic_array: DynamicArray;\n\nfn index_unsized(i: i32, v: u32) {\n   let p: ptr<storage, DynamicArray, read_write> = &dynamic_array;\n\n   let val = (*p).arr[i];\n   (*p).arr[i] = val + v;\n}\n\nfn index_dynamic_array(i: i32, v: u32) {\n   let p: ptr<storage, array<u32>, read_write> = &dynamic_array.arr;\n\n   let val = (*p)[i];\n   (*p)[i] = val + v;\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    f();\n    index_unsized(1, 1);\n    index_dynamic_array(1, 1);\n}\n\"\nOpMemberName %9 0 \"arr\"\nOpName %9 \"DynamicArray\"\nOpName %11 \"dynamic_array\"\nOpName %14 \"f\"\nOpName %18 \"v\"\nOpName %26 \"i\"\nOpName %27 \"v\"\nOpName %28 \"index_unsized\"\nOpName %38 \"i\"\nOpName %39 \"v\"\nOpName %40 \"index_dynamic_array\"\nOpName %47 \"main\"\nOpDecorate %8 ArrayStride 4\nOpMemberDecorate %9 0 Offset 0\nOpDecorate %9 Block\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 0\n%2 = OpTypeVoid\n%6 = OpTypeFloat 32\n%5 = OpTypeVector %6 2\n%4 = OpTypeMatrix %5 2\n%7 = OpTypeInt 32 0\n%8 = OpTypeRuntimeArray %7\n%9 = OpTypeStruct %8\n%10 = OpTypeInt 32 1\n%12 = OpTypePointer StorageBuffer %9\n%11 = OpVariable  %12  StorageBuffer\n%15 = OpTypeFunction %2\n%16 = OpConstant  %6  10\n%17 = OpConstantComposite  %5  %16 %16\n%19 = OpTypePointer Function %4\n%20 = OpConstantNull  %4\n%22 = OpTypePointer Function %5\n%23 = OpConstant  %7  0\n%29 = OpTypeFunction %2 %10 %7\n%31 = OpTypePointer StorageBuffer %8\n%32 = OpTypePointer StorageBuffer %7\n%48 = OpConstant  %10  1\n%49 = OpConstant  %7  1\n%14 = OpFunction  %2  None %15\n%13 = OpLabel\n%18 = OpVariable  %19  Function %20\nOpBranch %21\n%21 = OpLabel\nOpLine %3 3 14\nOpLine %3 4 10\nOpLine %3 4 4\n%24 = OpAccessChain  %22  %18 %23\nOpStore %24 %17\nOpReturn\nOpFunctionEnd\n%28 = OpFunction  %2  None %29\n%26 = OpFunctionParameter  %10\n%27 = OpFunctionParameter  %7\n%25 = OpLabel\nOpBranch %30\n%30 = OpLabel\nOpLine %3 17 14\n%33 = OpAccessChain  %32  %11 %23 %26\n%34 = OpLoad  %7  %33\nOpLine %3 18 4\n%35 = OpIAdd  %7  %34 %27\nOpLine %3 18 4\n%36 = OpAccessChain  %32  %11 %23 %26\nOpStore %36 %35\nOpReturn\nOpFunctionEnd\n%40 = OpFunction  %2  None %29\n%38 = OpFunctionParameter  %10\n%39 = OpFunctionParameter  %7\n%37 = OpLabel\nOpBranch %41\n%41 = OpLabel\nOpLine %3 22 51\nOpLine %3 24 14\n%42 = OpAccessChain  %32  %11 %23 %38\n%43 = OpLoad  %7  %42\nOpLine %3 25 4\n%44 = OpIAdd  %7  %43 %39\nOpLine %3 25 4\n%45 = OpAccessChain  %32  %11 %23 %38\nOpStore %45 %44\nOpReturn\nOpFunctionEnd\n%47 = OpFunction  %2  None %15\n%46 = OpLabel\nOpBranch %50\n%50 = OpLabel\nOpLine %3 30 5\n%51 = OpFunctionCall  %2  %14\nOpLine %3 31 5\n%52 = OpFunctionCall  %2  %28 %48 %49\nOpLine %3 32 5\n%53 = OpFunctionCall  %2  %40 %48 %49\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-policy-mix.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 123\nOpCapability Shader\nOpCapability ImageQuery\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %102 \"main\" %114\nOpExecutionMode %102 LocalSize 1 1 1\n%3 = OpString \"policy-mix.wgsl\"\nOpSource Unknown 0 %3 \"// Tests that the index, buffer, and texture bounds checks policies are\n// implemented separately.\n\n// Storage and Uniform storage classes\nstruct InStorage {\n  a: array<vec4<f32>, 10>\n}\n@group(0) @binding(0) var<storage> in_storage: InStorage;\n\nstruct InUniform {\n  a: array<vec4<f32>, 20>\n}\n@group(0) @binding(1) var<uniform> in_uniform: InUniform;\n\n// Textures automatically land in the `handle` storage class.\n@group(0) @binding(2) var image_2d_array: texture_2d_array<f32>;\n\n// None of the above.\nvar<workgroup> in_workgroup: array<f32, 30>;\nvar<private> in_private: array<f32, 40>;\n\nfn mock_function(c: vec2<i32>, i: i32, l: i32) -> vec4<f32> {\n  var in_function: array<vec4<f32>, 2> =\n    array<vec4<f32>, 2>(vec4<f32>(0.707, 0.0, 0.0, 1.0),\n                        vec4<f32>(0.0, 0.707, 0.0, 1.0));\n\n  return (in_storage.a[i] +\n          in_uniform.a[i] +\n          textureLoad(image_2d_array, c, i, l) +\n          in_workgroup[i] +\n          in_private[i] +\n          in_function[i]);\n}\n\n@compute @workgroup_size(1)\nfn main() {\n    mock_function(vec2(1, 2), 3, 4);\n}\n\"\nOpMemberName %9 0 \"a\"\nOpName %9 \"InStorage\"\nOpMemberName %12 0 \"a\"\nOpName %12 \"InUniform\"\nOpName %22 \"in_storage\"\nOpName %25 \"in_uniform\"\nOpName %28 \"image_2d_array\"\nOpName %30 \"in_workgroup\"\nOpName %32 \"in_private\"\nOpName %36 \"c\"\nOpName %37 \"i\"\nOpName %38 \"l\"\nOpName %39 \"mock_function\"\nOpName %53 \"in_function\"\nOpName %102 \"main\"\nOpDecorate %6 ArrayStride 16\nOpMemberDecorate %9 0 Offset 0\nOpDecorate %10 ArrayStride 16\nOpMemberDecorate %12 0 Offset 0\nOpDecorate %14 ArrayStride 4\nOpDecorate %16 ArrayStride 4\nOpDecorate %20 ArrayStride 16\nOpDecorate %22 NonWritable\nOpDecorate %22 DescriptorSet 0\nOpDecorate %22 Binding 0\nOpDecorate %23 Block\nOpMemberDecorate %23 0 Offset 0\nOpDecorate %25 DescriptorSet 0\nOpDecorate %25 Binding 1\nOpDecorate %26 Block\nOpMemberDecorate %26 0 Offset 0\nOpDecorate %28 DescriptorSet 0\nOpDecorate %28 Binding 2\nOpDecorate %114 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 4\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  10\n%6 = OpTypeArray %5 %7\n%9 = OpTypeStruct %6\n%11 = OpConstant  %8  20\n%10 = OpTypeArray %5 %11\n%12 = OpTypeStruct %10\n%13 = OpTypeImage %4 2D 0 1 0 1 Unknown\n%15 = OpConstant  %8  30\n%14 = OpTypeArray %4 %15\n%17 = OpConstant  %8  40\n%16 = OpTypeArray %4 %17\n%18 = OpTypeInt 32 1\n%19 = OpTypeVector %18 2\n%21 = OpConstant  %8  2\n%20 = OpTypeArray %5 %21\n%23 = OpTypeStruct %9\n%24 = OpTypePointer StorageBuffer %23\n%22 = OpVariable  %24  StorageBuffer\n%26 = OpTypeStruct %12\n%27 = OpTypePointer Uniform %26\n%25 = OpVariable  %27  Uniform\n%29 = OpTypePointer UniformConstant %13\n%28 = OpVariable  %29  UniformConstant\n%31 = OpTypePointer Workgroup %14\n%30 = OpVariable  %31  Workgroup\n%33 = OpTypePointer Private %16\n%34 = OpConstantNull  %16\n%32 = OpVariable  %33  Private %34\n%40 = OpTypeFunction %5 %19 %18 %18\n%41 = OpTypePointer StorageBuffer %9\n%42 = OpConstant  %8  0\n%44 = OpTypePointer Uniform %12\n%47 = OpConstant  %4  0.707\n%48 = OpConstant  %4  0\n%49 = OpConstant  %4  1\n%50 = OpConstantComposite  %5  %47 %48 %48 %49\n%51 = OpConstantComposite  %5  %48 %47 %48 %49\n%52 = OpConstantComposite  %20  %50 %51\n%54 = OpTypePointer Function %20\n%56 = OpTypePointer StorageBuffer %6\n%57 = OpTypePointer StorageBuffer %5\n%60 = OpTypePointer Uniform %10\n%61 = OpTypePointer Uniform %5\n%65 = OpTypeVector %18 3\n%67 = OpTypeBool\n%68 = OpConstantNull  %5\n%74 = OpTypeVector %67 3\n%81 = OpTypePointer Workgroup %4\n%82 = OpConstant  %8  29\n%88 = OpTypePointer Private %4\n%89 = OpConstant  %8  39\n%95 = OpTypePointer Function %5\n%96 = OpConstant  %8  1\n%103 = OpTypeFunction %2\n%107 = OpConstant  %18  1\n%108 = OpConstant  %18  2\n%109 = OpConstantComposite  %19  %107 %108\n%110 = OpConstant  %18  3\n%111 = OpConstant  %18  4\n%113 = OpConstantNull  %14\n%115 = OpTypePointer Input %8\n%114 = OpVariable  %115  Input\n%120 = OpConstant  %8  264\n%39 = OpFunction  %5  None %40\n%36 = OpFunctionParameter  %19\n%37 = OpFunctionParameter  %18\n%38 = OpFunctionParameter  %18\n%35 = OpLabel\n%53 = OpVariable  %54  Function %52\n%43 = OpAccessChain  %41  %22 %42\n%45 = OpAccessChain  %44  %25 %42\n%46 = OpLoad  %13  %28\nOpBranch %55\n%55 = OpLabel\nOpLine %3 24 25\nOpLine %3 24 5\nOpLine %3 27 11\n%58 = OpAccessChain  %57  %43 %42 %37\n%59 = OpLoad  %5  %58\nOpLine %3 27 11\n%62 = OpAccessChain  %61  %45 %42 %37\n%63 = OpLoad  %5  %62\n%64 = OpFAdd  %5  %59 %63\nOpLine %3 27 11\n%66 = OpCompositeConstruct  %65  %36 %37\n%69 = OpImageQueryLevels  %18  %46\n%70 = OpULessThan  %67  %38 %69\nOpSelectionMerge %71 None\nOpBranchConditional %70 %72 %71\n%72 = OpLabel\n%73 = OpImageQuerySizeLod  %65  %46 %38\n%75 = OpULessThan  %74  %66 %73\n%76 = OpAll  %67  %75\nOpBranchConditional %76 %77 %71\n%77 = OpLabel\n%78 = OpImageFetch  %5  %46 %66 Lod %38\nOpBranch %71\n%71 = OpLabel\n%79 = OpPhi  %5  %68 %55 %68 %72 %78 %77\n%80 = OpFAdd  %5  %64 %79\nOpLine %3 27 11\n%83 = OpExtInst  %8  %1 UMin %37 %82\n%84 = OpAccessChain  %81  %30 %83\n%85 = OpLoad  %4  %84\n%86 = OpCompositeConstruct  %5  %85 %85 %85 %85\n%87 = OpFAdd  %5  %80 %86\nOpLine %3 27 11\n%90 = OpExtInst  %8  %1 UMin %37 %89\n%91 = OpAccessChain  %88  %32 %90\n%92 = OpLoad  %4  %91\n%93 = OpCompositeConstruct  %5  %92 %92 %92 %92\n%94 = OpFAdd  %5  %87 %93\n%97 = OpExtInst  %8  %1 UMin %37 %96\n%98 = OpAccessChain  %95  %53 %97\n%99 = OpLoad  %5  %98\n%100 = OpFAdd  %5  %94 %99\nOpReturnValue %100\nOpFunctionEnd\n%102 = OpFunction  %2  None %103\n%101 = OpLabel\n%104 = OpAccessChain  %41  %22 %42\n%105 = OpAccessChain  %44  %25 %42\n%106 = OpLoad  %13  %28\nOpBranch %112\n%112 = OpLabel\n%116 = OpLoad  %8  %114\n%117 = OpIEqual  %67  %116 %42\nOpSelectionMerge %118 None\nOpBranchConditional %117 %119 %118\n%119 = OpLabel\nOpStore %30 %113\nOpBranch %118\n%118 = OpLabel\nOpControlBarrier %21 %21 %120\nOpBranch %121\n%121 = OpLabel\nOpLine %3 37 19\nOpLine %3 37 19\nOpLine %3 37 5\n%122 = OpFunctionCall  %5  %39 %109 %110 %111\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-primitive-index.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 18\nOpCapability Shader\nOpCapability Geometry\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %12 \"func\" %7 %10\nOpExecutionMode %12 OriginUpperLeft\nOpDecorate %7 BuiltIn PrimitiveId\nOpDecorate %7 Flat\nOpDecorate %10 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%5 = OpTypeFloat 32\n%4 = OpTypeVector %5 4\n%8 = OpTypePointer Input %3\n%7 = OpVariable  %8  Input\n%11 = OpTypePointer Output %4\n%10 = OpVariable  %11  Output\n%13 = OpTypeFunction %2\n%14 = OpConstant  %5  1\n%12 = OpFunction  %2  None %13\n%6 = OpLabel\n%9 = OpLoad  %3  %7\nOpBranch %15\n%15 = OpLabel\n%16 = OpConvertUToF  %5  %9\n%17 = OpCompositeConstruct  %4  %16 %14 %14 %14\nOpStore %10 %17\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-quad.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 65\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %25 \"vert_main\" %16 %19 %21 %23\nOpEntryPoint Fragment %45 \"frag_main\" %42 %44\nOpEntryPoint Fragment %61 \"fs_extra\" %60\nOpExecutionMode %45 OriginUpperLeft\nOpExecutionMode %61 OriginUpperLeft\n%3 = OpString \"quad.wgsl\"\nOpSource Unknown 0 %3 \"// vertex\nconst c_scale: f32 = 1.2;\n\nstruct VertexOutput {\n  @location(0) uv : vec2<f32>,\n  @builtin(position) position : vec4<f32>,\n}\n\n@vertex\nfn vert_main(\n  @location(0) pos : vec2<f32>,\n  @location(1) uv : vec2<f32>,\n) -> VertexOutput {\n  return VertexOutput(uv, vec4<f32>(c_scale * pos, 0.0, 1.0));\n}\n\n// fragment\n@group(0) @binding(0) var u_texture : texture_2d<f32>;\n@group(0) @binding(1) var u_sampler : sampler;\n\n@fragment\nfn frag_main(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {\n  let color = textureSample(u_texture, u_sampler, uv);\n  if color.a == 0.0 {\n    discard;\n  }\n  // forcing the expression here to be emitted in order to check the\n  // uniformity of the control flow a bit more strongly.\n  let premultiplied = color.a * color;\n  return premultiplied;\n}\n\n\n// We need to make sure that backends are successfully handling multiple entry points for the same shader stage.\n@fragment\nfn fs_extra() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0, 0.5, 0.0, 0.5);\n}\n\"\nOpMemberName %7 0 \"uv\"\nOpMemberName %7 1 \"position\"\nOpName %7 \"VertexOutput\"\nOpName %10 \"c_scale\"\nOpName %11 \"u_texture\"\nOpName %13 \"u_sampler\"\nOpName %16 \"pos\"\nOpName %19 \"uv\"\nOpName %21 \"uv\"\nOpName %23 \"position\"\nOpName %25 \"vert_main\"\nOpName %42 \"uv\"\nOpName %45 \"frag_main\"\nOpName %61 \"fs_extra\"\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 16\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 0\nOpDecorate %13 DescriptorSet 0\nOpDecorate %13 Binding 1\nOpDecorate %16 Location 0\nOpDecorate %19 Location 1\nOpDecorate %21 Location 0\nOpDecorate %23 BuiltIn Position\nOpDecorate %42 Location 0\nOpDecorate %44 Location 0\nOpDecorate %60 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%5 = OpTypeVector %4 2\n%6 = OpTypeVector %4 4\n%7 = OpTypeStruct %5 %6\n%8 = OpTypeImage %4 2D 0 0 0 1 Unknown\n%9 = OpTypeSampler\n%10 = OpConstant  %4  1.2\n%12 = OpTypePointer UniformConstant %8\n%11 = OpVariable  %12  UniformConstant\n%14 = OpTypePointer UniformConstant %9\n%13 = OpVariable  %14  UniformConstant\n%17 = OpTypePointer Input %5\n%16 = OpVariable  %17  Input\n%19 = OpVariable  %17  Input\n%22 = OpTypePointer Output %5\n%21 = OpVariable  %22  Output\n%24 = OpTypePointer Output %6\n%23 = OpVariable  %24  Output\n%26 = OpTypeFunction %2\n%27 = OpConstant  %4  0\n%28 = OpConstant  %4  1\n%35 = OpTypePointer Output %4\n%37 = OpTypeInt 32 0\n%36 = OpConstant  %37  1\n%42 = OpVariable  %17  Input\n%44 = OpVariable  %24  Output\n%49 = OpTypeSampledImage %8\n%53 = OpTypeBool\n%60 = OpVariable  %24  Output\n%62 = OpConstant  %4  0.5\n%63 = OpConstantComposite  %6  %27 %62 %27 %62\n%25 = OpFunction  %2  None %26\n%15 = OpLabel\n%18 = OpLoad  %5  %16\n%20 = OpLoad  %5  %19\nOpBranch %29\n%29 = OpLabel\nOpLine %3 14 37\n%30 = OpVectorTimesScalar  %5  %18 %10\nOpLine %3 14 10\n%31 = OpCompositeConstruct  %6  %30 %27 %28\n%32 = OpCompositeConstruct  %7  %20 %31\n%33 = OpCompositeExtract  %5  %32 0\nOpStore %21 %33\n%34 = OpCompositeExtract  %6  %32 1\nOpStore %23 %34\n%38 = OpAccessChain  %35  %23 %36\n%39 = OpLoad  %4  %38\n%40 = OpFNegate  %4  %39\nOpStore %38 %40\nOpReturn\nOpFunctionEnd\n%45 = OpFunction  %2  None %26\n%41 = OpLabel\n%43 = OpLoad  %5  %42\n%46 = OpLoad  %8  %11\n%47 = OpLoad  %9  %13\nOpBranch %48\n%48 = OpLabel\nOpLine %3 23 15\n%50 = OpSampledImage  %49  %46 %47\n%51 = OpImageSampleImplicitLod  %6  %50 %43\nOpLine %3 24 6\n%52 = OpCompositeExtract  %4  %51 3\nOpLine %3 24 6\n%54 = OpFOrdEqual  %53  %52 %27\nOpLine %3 24 3\nOpSelectionMerge %55 None\nOpBranchConditional %54 %56 %55\n%56 = OpLabel\nOpKill\n%55 = OpLabel\nOpLine %3 29 23\n%57 = OpCompositeExtract  %4  %51 3\n%58 = OpVectorTimesScalar  %6  %51 %57\nOpStore %44 %58\nOpReturn\nOpFunctionEnd\n%61 = OpFunction  %2  None %26\n%59 = OpLabel\nOpBranch %64\n%64 = OpLabel\nOpLine %3 37 12\nOpStore %60 %63\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-ray-query-no-init-tracking.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 396\nOpCapability Shader\nOpCapability RayQueryKHR\nOpExtension \"SPV_KHR_ray_query\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %242 \"main\" %15 %17\nOpEntryPoint GLCompute %262 \"main_candidate\" %15\nOpExecutionMode %242 LocalSize 1 1 1\nOpExecutionMode %262 LocalSize 1 1 1\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 4\nOpMemberDecorate %10 2 Offset 8\nOpMemberDecorate %10 3 Offset 12\nOpMemberDecorate %10 4 Offset 16\nOpMemberDecorate %10 5 Offset 20\nOpMemberDecorate %10 6 Offset 24\nOpMemberDecorate %10 7 Offset 28\nOpMemberDecorate %10 8 Offset 36\nOpMemberDecorate %10 9 Offset 48\nOpMemberDecorate %10 9 ColMajor\nOpMemberDecorate %10 9 MatrixStride 16\nOpMemberDecorate %10 10 Offset 112\nOpMemberDecorate %10 10 ColMajor\nOpMemberDecorate %10 10 MatrixStride 16\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 4\nOpMemberDecorate %12 2 Offset 8\nOpMemberDecorate %12 3 Offset 12\nOpMemberDecorate %12 4 Offset 16\nOpMemberDecorate %12 5 Offset 32\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 16\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 1\nOpDecorate %18 Block\nOpMemberDecorate %18 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 3\n%5 = OpTypeAccelerationStructureKHR\n%6 = OpTypeInt 32 0\n%7 = OpTypeVector %3 2\n%8 = OpTypeBool\n%9 = OpTypeMatrix %4 4\n%10 = OpTypeStruct %6 %3 %6 %6 %6 %6 %6 %7 %8 %9 %9\n%11 = OpTypeRayQueryKHR\n%12 = OpTypeStruct %6 %6 %3 %3 %4 %4\n%13 = OpTypeStruct %6 %4\n%14 = OpTypeVector %3 4\n%16 = OpTypePointer UniformConstant %5\n%15 = OpVariable  %16  UniformConstant\n%18 = OpTypeStruct %13\n%19 = OpTypePointer StorageBuffer %18\n%17 = OpVariable  %19  StorageBuffer\n%26 = OpTypeFunction %10 %4 %4 %16\n%27 = OpConstant  %6  4\n%28 = OpConstant  %6  255\n%29 = OpConstant  %3  0.1\n%30 = OpConstant  %3  100\n%32 = OpTypePointer Function %11\n%33 = OpTypePointer Function %6\n%35 = OpConstant  %6  0\n%36 = OpTypePointer Function %3\n%38 = OpConstant  %3  0\n%41 = OpTypeVector %8 3\n%42 = OpTypeFunction %2 %32 %5 %12 %33 %36\n%70 = OpConstant  %6  256\n%73 = OpConstant  %6  512\n%78 = OpConstant  %6  16\n%81 = OpConstant  %6  32\n%90 = OpConstant  %6  1\n%93 = OpConstant  %6  2\n%96 = OpConstant  %6  64\n%99 = OpConstant  %6  128\n%128 = OpTypeVector %6 2\n%129 = OpTypePointer Function %128\n%130 = OpTypeVector %8 2\n%131 = OpConstantComposite  %128  %35 %35\n%132 = OpConstant  %6  4294967295\n%133 = OpConstantComposite  %128  %132 %132\n%146 = OpTypePointer Function %8\n%147 = OpTypeFunction %8 %32 %33\n%153 = OpConstantFalse  %8\n%160 = OpConstant  %6  6\n%168 = OpTypePointer Function %10\n%169 = OpTypePointer Function %9\n%170 = OpTypePointer Function %7\n%171 = OpTypeFunction %10 %32 %33\n%176 = OpConstantNull  %10\n%199 = OpConstant  %6  3\n%202 = OpConstant  %6  5\n%205 = OpConstant  %6  9\n%207 = OpConstant  %6  10\n%216 = OpConstant  %6  7\n%218 = OpConstant  %6  8\n%226 = OpTypeFunction %4 %4 %10\n%227 = OpConstant  %3  1\n%228 = OpConstant  %3  2.4\n%243 = OpTypeFunction %2\n%245 = OpTypePointer StorageBuffer %13\n%247 = OpConstantComposite  %4  %38 %38 %38\n%248 = OpConstantComposite  %4  %38 %227 %38\n%251 = OpTypePointer StorageBuffer %6\n%256 = OpTypePointer StorageBuffer %4\n%264 = OpConstantComposite  %12  %27 %28 %29 %30 %247 %248\n%265 = OpConstant  %3  10\n%322 = OpTypeFunction %2 %32 %33 %3 %36\n%363 = OpTypeFunction %2 %32 %33\n%43 = OpFunction  %2  None %42\n%44 = OpFunctionParameter  %32\n%45 = OpFunctionParameter  %5\n%46 = OpFunctionParameter  %12\n%47 = OpFunctionParameter  %33\n%48 = OpFunctionParameter  %36\n%49 = OpLabel\n%50 = OpCompositeExtract  %6  %46 0\n%51 = OpCompositeExtract  %6  %46 1\n%52 = OpCompositeExtract  %3  %46 2\n%53 = OpCompositeExtract  %3  %46 3\nOpStore %48 %53\n%54 = OpCompositeExtract  %4  %46 4\n%55 = OpCompositeExtract  %4  %46 5\n%56 = OpFOrdLessThanEqual  %8  %52 %53\n%57 = OpFOrdGreaterThanEqual  %8  %52 %38\n%58 = OpIsInf  %41  %54\n%59 = OpAny  %8  %58\n%60 = OpIsNan  %41  %54\n%61 = OpAny  %8  %60\n%62 = OpLogicalOr  %8  %61 %59\n%63 = OpLogicalNot  %8  %62\n%64 = OpIsInf  %41  %55\n%65 = OpAny  %8  %64\n%66 = OpIsNan  %41  %55\n%67 = OpAny  %8  %66\n%68 = OpLogicalOr  %8  %67 %65\n%69 = OpLogicalNot  %8  %68\n%71 = OpBitwiseAnd  %6  %50 %70\n%72 = OpINotEqual  %8  %71 %35\n%74 = OpBitwiseAnd  %6  %50 %73\n%75 = OpINotEqual  %8  %74 %35\n%76 = OpLogicalAnd  %8  %75 %72\n%77 = OpLogicalNot  %8  %76\n%79 = OpBitwiseAnd  %6  %50 %78\n%80 = OpINotEqual  %8  %79 %35\n%82 = OpBitwiseAnd  %6  %50 %81\n%83 = OpINotEqual  %8  %82 %35\n%84 = OpLogicalAnd  %8  %83 %72\n%85 = OpLogicalAnd  %8  %83 %80\n%86 = OpLogicalAnd  %8  %80 %72\n%87 = OpLogicalOr  %8  %86 %84\n%88 = OpLogicalOr  %8  %87 %85\n%89 = OpLogicalNot  %8  %88\n%91 = OpBitwiseAnd  %6  %50 %90\n%92 = OpINotEqual  %8  %91 %35\n%94 = OpBitwiseAnd  %6  %50 %93\n%95 = OpINotEqual  %8  %94 %35\n%97 = OpBitwiseAnd  %6  %50 %96\n%98 = OpINotEqual  %8  %97 %35\n%100 = OpBitwiseAnd  %6  %50 %99\n%101 = OpINotEqual  %8  %100 %35\n%102 = OpLogicalAnd  %8  %101 %92\n%103 = OpLogicalAnd  %8  %101 %95\n%104 = OpLogicalAnd  %8  %101 %98\n%105 = OpLogicalAnd  %8  %98 %92\n%106 = OpLogicalAnd  %8  %98 %95\n%107 = OpLogicalAnd  %8  %95 %92\n%108 = OpLogicalOr  %8  %107 %102\n%109 = OpLogicalOr  %8  %108 %103\n%110 = OpLogicalOr  %8  %109 %104\n%111 = OpLogicalOr  %8  %110 %105\n%112 = OpLogicalOr  %8  %111 %106\n%113 = OpLogicalNot  %8  %112\n%114 = OpLogicalAnd  %8  %113 %56\n%115 = OpLogicalAnd  %8  %114 %57\n%116 = OpLogicalAnd  %8  %115 %63\n%117 = OpLogicalAnd  %8  %116 %69\n%118 = OpLogicalAnd  %8  %117 %77\n%119 = OpLogicalAnd  %8  %118 %89\nOpSelectionMerge %120 None\nOpBranchConditional %119 %122 %121\n%122 = OpLabel\nOpRayQueryInitializeKHR %44 %45 %50 %51 %54 %52 %55 %53\nOpStore %47 %90\nOpBranch %120\n%121 = OpLabel\nOpBranch %120\n%120 = OpLabel\nOpReturn\nOpFunctionEnd\n%148 = OpFunction  %8  None %147\n%149 = OpFunctionParameter  %32\n%150 = OpFunctionParameter  %33\n%151 = OpLabel\n%152 = OpVariable  %146  Function %153\n%154 = OpLoad  %6  %150\n%157 = OpBitwiseAnd  %6  %154 %90\n%158 = OpINotEqual  %8  %157 %35\nOpSelectionMerge %155 None\nOpBranchConditional %158 %156 %155\n%156 = OpLabel\n%159 = OpRayQueryProceedKHR  %8  %149\nOpStore %152 %159\n%161 = OpSelect  %6  %159 %93 %160\n%162 = OpBitwiseOr  %6  %154 %161\nOpStore %150 %162\nOpBranch %155\n%155 = OpLabel\n%163 = OpLoad  %8  %152\nOpReturnValue %163\nOpFunctionEnd\n%172 = OpFunction  %10  None %171\n%173 = OpFunctionParameter  %32\n%174 = OpFunctionParameter  %33\n%175 = OpLabel\n%177 = OpVariable  %168  Function %176\n%178 = OpLoad  %6  %174\n%179 = OpBitwiseAnd  %6  %178 %93\n%180 = OpINotEqual  %8  %179 %35\n%181 = OpBitwiseAnd  %6  %178 %27\n%182 = OpINotEqual  %8  %181 %35\n%183 = OpLogicalAnd  %8  %182 %180\nOpSelectionMerge %185 None\nOpBranchConditional %183 %184 %185\n%184 = OpLabel\n%186 = OpRayQueryGetIntersectionTypeKHR  %6  %173 %90\n%187 = OpAccessChain  %33  %177 %35\nOpStore %187 %186\n%188 = OpINotEqual  %8  %186 %35\nOpSelectionMerge %190 None\nOpBranchConditional %188 %189 %190\n%189 = OpLabel\n%191 = OpRayQueryGetIntersectionInstanceCustomIndexKHR  %6  %173 %90\n%192 = OpRayQueryGetIntersectionInstanceIdKHR  %6  %173 %90\n%193 = OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR  %6  %173 %90\n%194 = OpRayQueryGetIntersectionGeometryIndexKHR  %6  %173 %90\n%195 = OpRayQueryGetIntersectionPrimitiveIndexKHR  %6  %173 %90\n%196 = OpRayQueryGetIntersectionObjectToWorldKHR  %9  %173 %90\n%197 = OpRayQueryGetIntersectionWorldToObjectKHR  %9  %173 %90\n%198 = OpAccessChain  %33  %177 %93\nOpStore %198 %191\n%200 = OpAccessChain  %33  %177 %199\nOpStore %200 %192\n%201 = OpAccessChain  %33  %177 %27\nOpStore %201 %193\n%203 = OpAccessChain  %33  %177 %202\nOpStore %203 %194\n%204 = OpAccessChain  %33  %177 %160\nOpStore %204 %195\n%206 = OpAccessChain  %169  %177 %205\nOpStore %206 %196\n%208 = OpAccessChain  %169  %177 %207\nOpStore %208 %197\n%209 = OpIEqual  %8  %186 %90\n%212 = OpRayQueryGetIntersectionTKHR  %3  %173 %90\n%213 = OpAccessChain  %36  %177 %90\nOpStore %213 %212\nOpSelectionMerge %211 None\nOpBranchConditional %188 %210 %211\n%210 = OpLabel\n%214 = OpRayQueryGetIntersectionBarycentricsKHR  %7  %173 %90\n%215 = OpRayQueryGetIntersectionFrontFaceKHR  %8  %173 %90\n%217 = OpAccessChain  %170  %177 %216\nOpStore %217 %214\n%219 = OpAccessChain  %146  %177 %218\nOpStore %219 %215\nOpBranch %211\n%211 = OpLabel\nOpBranch %190\n%190 = OpLabel\nOpBranch %185\n%185 = OpLabel\n%220 = OpLoad  %10  %177\nOpReturnValue %220\nOpFunctionEnd\n%25 = OpFunction  %10  None %26\n%21 = OpFunctionParameter  %4\n%22 = OpFunctionParameter  %4\n%23 = OpFunctionParameter  %16\n%20 = OpLabel\n%31 = OpVariable  %32  Function\n%34 = OpVariable  %33  Function %35\n%37 = OpVariable  %36  Function %38\n%134 = OpVariable  %129  Function %133\n%24 = OpLoad  %5  %23\nOpBranch %39\n%39 = OpLabel\n%40 = OpCompositeConstruct  %12  %27 %28 %29 %30 %21 %22\n%123 = OpFunctionCall  %2  %43 %31 %24 %40 %34 %37\nOpBranch %124\n%124 = OpLabel\nOpLoopMerge %125 %127 None\nOpBranch %135\n%135 = OpLabel\n%136 = OpLoad  %128  %134\n%137 = OpIEqual  %130  %131 %136\n%138 = OpAll  %8  %137\nOpSelectionMerge %139 None\nOpBranchConditional %138 %125 %139\n%139 = OpLabel\n%140 = OpCompositeExtract  %6  %136 1\n%141 = OpIEqual  %8  %140 %35\n%142 = OpSelect  %6  %141 %90 %35\n%143 = OpCompositeConstruct  %128  %142 %90\n%144 = OpISub  %128  %136 %143\nOpStore %134 %144\nOpBranch %126\n%126 = OpLabel\n%145 = OpFunctionCall  %8  %148 %31 %34\nOpSelectionMerge %164 None\nOpBranchConditional %145 %164 %165\n%165 = OpLabel\nOpBranch %125\n%164 = OpLabel\nOpBranch %166\n%166 = OpLabel\nOpBranch %167\n%167 = OpLabel\nOpBranch %127\n%127 = OpLabel\nOpBranch %124\n%125 = OpLabel\n%221 = OpFunctionCall  %10  %172 %31 %34\nOpReturnValue %221\nOpFunctionEnd\n%225 = OpFunction  %4  None %226\n%223 = OpFunctionParameter  %4\n%224 = OpFunctionParameter  %10\n%222 = OpLabel\nOpBranch %229\n%229 = OpLabel\n%230 = OpCompositeExtract  %9  %224 10\n%231 = OpCompositeConstruct  %14  %223 %227\n%232 = OpMatrixTimesVector  %4  %230 %231\n%233 = OpVectorShuffle  %7  %232 %232 0 1\n%234 = OpExtInst  %7  %1 Normalize %233\n%235 = OpVectorTimesScalar  %7  %234 %228\n%236 = OpCompositeExtract  %9  %224 9\n%237 = OpCompositeConstruct  %14  %235 %38 %227\n%238 = OpMatrixTimesVector  %4  %236 %237\n%239 = OpFSub  %4  %223 %238\n%240 = OpExtInst  %4  %1 Normalize %239\nOpReturnValue %240\nOpFunctionEnd\n%242 = OpFunction  %2  None %243\n%241 = OpLabel\n%244 = OpLoad  %5  %15\n%246 = OpAccessChain  %245  %17 %35\nOpBranch %249\n%249 = OpLabel\n%250 = OpFunctionCall  %10  %25 %247 %248 %15\n%252 = OpCompositeExtract  %6  %250 0\n%253 = OpIEqual  %8  %252 %35\n%254 = OpSelect  %6  %253 %90 %35\n%255 = OpAccessChain  %251  %246 %35\nOpStore %255 %254\n%257 = OpCompositeExtract  %3  %250 1\n%258 = OpVectorTimesScalar  %4  %248 %257\n%259 = OpFunctionCall  %4  %225 %258 %250\n%260 = OpAccessChain  %256  %246 %90\nOpStore %260 %259\nOpReturn\nOpFunctionEnd\n%271 = OpFunction  %10  None %171\n%272 = OpFunctionParameter  %32\n%273 = OpFunctionParameter  %33\n%274 = OpLabel\n%275 = OpVariable  %168  Function %176\n%276 = OpLoad  %6  %273\n%277 = OpBitwiseAnd  %6  %276 %93\n%278 = OpINotEqual  %8  %277 %35\n%279 = OpBitwiseAnd  %6  %276 %27\n%280 = OpINotEqual  %8  %279 %35\n%281 = OpLogicalNot  %8  %280\n%282 = OpLogicalAnd  %8  %281 %278\nOpSelectionMerge %284 None\nOpBranchConditional %282 %283 %284\n%283 = OpLabel\n%285 = OpRayQueryGetIntersectionTypeKHR  %6  %272 %35\n%286 = OpIEqual  %8  %285 %35\n%287 = OpSelect  %6  %286 %90 %199\n%288 = OpAccessChain  %33  %275 %35\nOpStore %288 %287\n%289 = OpINotEqual  %8  %287 %35\nOpSelectionMerge %291 None\nOpBranchConditional %289 %290 %291\n%290 = OpLabel\n%292 = OpRayQueryGetIntersectionInstanceCustomIndexKHR  %6  %272 %35\n%293 = OpRayQueryGetIntersectionInstanceIdKHR  %6  %272 %35\n%294 = OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR  %6  %272 %35\n%295 = OpRayQueryGetIntersectionGeometryIndexKHR  %6  %272 %35\n%296 = OpRayQueryGetIntersectionPrimitiveIndexKHR  %6  %272 %35\n%297 = OpRayQueryGetIntersectionObjectToWorldKHR  %9  %272 %35\n%298 = OpRayQueryGetIntersectionWorldToObjectKHR  %9  %272 %35\n%299 = OpAccessChain  %33  %275 %93\nOpStore %299 %292\n%300 = OpAccessChain  %33  %275 %199\nOpStore %300 %293\n%301 = OpAccessChain  %33  %275 %27\nOpStore %301 %294\n%302 = OpAccessChain  %33  %275 %202\nOpStore %302 %295\n%303 = OpAccessChain  %33  %275 %160\nOpStore %303 %296\n%304 = OpAccessChain  %169  %275 %205\nOpStore %304 %297\n%305 = OpAccessChain  %169  %275 %207\nOpStore %305 %298\n%306 = OpIEqual  %8  %287 %90\nOpSelectionMerge %308 None\nOpBranchConditional %289 %307 %308\n%307 = OpLabel\n%309 = OpRayQueryGetIntersectionTKHR  %3  %272 %35\n%310 = OpAccessChain  %36  %275 %90\nOpStore %310 %309\n%311 = OpRayQueryGetIntersectionBarycentricsKHR  %7  %272 %35\n%312 = OpRayQueryGetIntersectionFrontFaceKHR  %8  %272 %35\n%313 = OpAccessChain  %170  %275 %216\nOpStore %313 %311\n%314 = OpAccessChain  %146  %275 %218\nOpStore %314 %312\nOpBranch %308\n%308 = OpLabel\nOpBranch %291\n%291 = OpLabel\nOpBranch %284\n%284 = OpLabel\n%315 = OpLoad  %10  %275\nOpReturnValue %315\nOpFunctionEnd\n%323 = OpFunction  %2  None %322\n%324 = OpFunctionParameter  %32\n%325 = OpFunctionParameter  %33\n%326 = OpFunctionParameter  %3\n%327 = OpFunctionParameter  %36\n%328 = OpLabel\n%329 = OpVariable  %36  Function\n%330 = OpVariable  %36  Function\n%333 = OpLoad  %6  %325\n%334 = OpBitwiseAnd  %6  %333 %93\n%335 = OpINotEqual  %8  %334 %35\n%336 = OpBitwiseAnd  %6  %333 %27\n%337 = OpINotEqual  %8  %336 %35\n%338 = OpLogicalNot  %8  %337\n%339 = OpLogicalAnd  %8  %338 %335\nOpSelectionMerge %332 None\nOpBranchConditional %339 %331 %332\n%331 = OpLabel\n%340 = OpRayQueryGetIntersectionTypeKHR  %6  %324 %35\n%341 = OpIEqual  %8  %340 %90\n%342 = OpRayQueryGetRayTMinKHR  %3  %324\n%343 = OpRayQueryGetIntersectionTypeKHR  %6  %324 %90\n%344 = OpIEqual  %8  %343 %35\nOpSelectionMerge %345 None\nOpBranchConditional %344 %346 %347\n%346 = OpLabel\n%348 = OpLoad  %3  %327\nOpStore %330 %348\nOpBranch %345\n%347 = OpLabel\n%349 = OpRayQueryGetIntersectionTKHR  %3  %324 %35\nOpStore %330 %349\nOpBranch %345\n%345 = OpLabel\n%350 = OpFOrdGreaterThanEqual  %8  %326 %342\n%351 = OpLoad  %3  %330\n%352 = OpFOrdLessThanEqual  %8  %326 %351\n%353 = OpLogicalAnd  %8  %350 %352\n%354 = OpLogicalAnd  %8  %353 %341\nOpSelectionMerge %356 None\nOpBranchConditional %354 %355 %356\n%355 = OpLabel\nOpRayQueryGenerateIntersectionKHR %324 %326\nOpBranch %356\n%356 = OpLabel\nOpBranch %332\n%332 = OpLabel\nOpReturn\nOpFunctionEnd\n%364 = OpFunction  %2  None %363\n%365 = OpFunctionParameter  %32\n%366 = OpFunctionParameter  %33\n%367 = OpLabel\n%370 = OpLoad  %6  %366\n%371 = OpBitwiseAnd  %6  %370 %93\n%372 = OpINotEqual  %8  %371 %35\n%373 = OpBitwiseAnd  %6  %370 %27\n%374 = OpINotEqual  %8  %373 %35\n%375 = OpLogicalNot  %8  %374\n%376 = OpLogicalAnd  %8  %375 %372\nOpSelectionMerge %369 None\nOpBranchConditional %376 %368 %369\n%368 = OpLabel\n%377 = OpRayQueryGetIntersectionTypeKHR  %6  %365 %35\n%378 = OpIEqual  %8  %377 %35\nOpSelectionMerge %380 None\nOpBranchConditional %378 %379 %380\n%379 = OpLabel\nOpRayQueryConfirmIntersectionKHR %365\nOpBranch %380\n%380 = OpLabel\nOpBranch %369\n%369 = OpLabel\nOpReturn\nOpFunctionEnd\n%383 = OpFunction  %2  None %363\n%384 = OpFunctionParameter  %32\n%385 = OpFunctionParameter  %33\n%386 = OpLabel\n%387 = OpLoad  %6  %385\n%390 = OpBitwiseAnd  %6  %387 %93\n%391 = OpINotEqual  %8  %390 %35\n%392 = OpBitwiseAnd  %6  %387 %27\n%393 = OpINotEqual  %8  %392 %35\n%394 = OpLogicalNot  %8  %393\n%395 = OpLogicalAnd  %8  %394 %391\nOpSelectionMerge %388 None\nOpBranchConditional %395 %389 %388\n%389 = OpLabel\nOpRayQueryTerminateKHR %384\nOpBranch %388\n%388 = OpLabel\nOpReturn\nOpFunctionEnd\n%262 = OpFunction  %2  None %243\n%261 = OpLabel\n%266 = OpVariable  %32  Function\n%267 = OpVariable  %33  Function %35\n%268 = OpVariable  %36  Function %38\n%263 = OpLoad  %5  %15\nOpBranch %269\n%269 = OpLabel\n%270 = OpFunctionCall  %2  %43 %266 %263 %264 %267 %268\n%316 = OpFunctionCall  %10  %271 %266 %267\n%317 = OpCompositeExtract  %6  %316 0\n%318 = OpIEqual  %8  %317 %199\nOpSelectionMerge %319 None\nOpBranchConditional %318 %320 %321\n%320 = OpLabel\n%357 = OpFunctionCall  %2  %323 %266 %267 %265 %268\nOpReturn\n%321 = OpLabel\n%358 = OpCompositeExtract  %6  %316 0\n%359 = OpIEqual  %8  %358 %90\nOpSelectionMerge %360 None\nOpBranchConditional %359 %361 %362\n%361 = OpLabel\n%381 = OpFunctionCall  %2  %364 %266 %267\nOpReturn\n%362 = OpLabel\n%382 = OpFunctionCall  %2  %383 %266 %267\nOpReturn\n%360 = OpLabel\nOpBranch %319\n%319 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-ray-query.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 396\nOpCapability Shader\nOpCapability RayQueryKHR\nOpExtension \"SPV_KHR_ray_query\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %242 \"main\" %15 %17\nOpEntryPoint GLCompute %262 \"main_candidate\" %15\nOpExecutionMode %242 LocalSize 1 1 1\nOpExecutionMode %262 LocalSize 1 1 1\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 1 Offset 4\nOpMemberDecorate %10 2 Offset 8\nOpMemberDecorate %10 3 Offset 12\nOpMemberDecorate %10 4 Offset 16\nOpMemberDecorate %10 5 Offset 20\nOpMemberDecorate %10 6 Offset 24\nOpMemberDecorate %10 7 Offset 28\nOpMemberDecorate %10 8 Offset 36\nOpMemberDecorate %10 9 Offset 48\nOpMemberDecorate %10 9 ColMajor\nOpMemberDecorate %10 9 MatrixStride 16\nOpMemberDecorate %10 10 Offset 112\nOpMemberDecorate %10 10 ColMajor\nOpMemberDecorate %10 10 MatrixStride 16\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 4\nOpMemberDecorate %12 2 Offset 8\nOpMemberDecorate %12 3 Offset 12\nOpMemberDecorate %12 4 Offset 16\nOpMemberDecorate %12 5 Offset 32\nOpMemberDecorate %13 0 Offset 0\nOpMemberDecorate %13 1 Offset 16\nOpDecorate %15 DescriptorSet 0\nOpDecorate %15 Binding 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 1\nOpDecorate %18 Block\nOpMemberDecorate %18 0 Offset 0\n%2 = OpTypeVoid\n%3 = OpTypeFloat 32\n%4 = OpTypeVector %3 3\n%5 = OpTypeAccelerationStructureKHR\n%6 = OpTypeInt 32 0\n%7 = OpTypeVector %3 2\n%8 = OpTypeBool\n%9 = OpTypeMatrix %4 4\n%10 = OpTypeStruct %6 %3 %6 %6 %6 %6 %6 %7 %8 %9 %9\n%11 = OpTypeRayQueryKHR\n%12 = OpTypeStruct %6 %6 %3 %3 %4 %4\n%13 = OpTypeStruct %6 %4\n%14 = OpTypeVector %3 4\n%16 = OpTypePointer UniformConstant %5\n%15 = OpVariable  %16  UniformConstant\n%18 = OpTypeStruct %13\n%19 = OpTypePointer StorageBuffer %18\n%17 = OpVariable  %19  StorageBuffer\n%26 = OpTypeFunction %10 %4 %4 %16\n%27 = OpConstant  %6  4\n%28 = OpConstant  %6  255\n%29 = OpConstant  %3  0.1\n%30 = OpConstant  %3  100\n%32 = OpTypePointer Function %11\n%33 = OpTypePointer Function %6\n%35 = OpConstant  %6  0\n%36 = OpTypePointer Function %3\n%38 = OpConstant  %3  0\n%41 = OpTypeVector %8 3\n%42 = OpTypeFunction %2 %32 %5 %12 %33 %36\n%70 = OpConstant  %6  256\n%73 = OpConstant  %6  512\n%78 = OpConstant  %6  16\n%81 = OpConstant  %6  32\n%90 = OpConstant  %6  1\n%93 = OpConstant  %6  2\n%96 = OpConstant  %6  64\n%99 = OpConstant  %6  128\n%128 = OpTypeVector %6 2\n%129 = OpTypePointer Function %128\n%130 = OpTypeVector %8 2\n%131 = OpConstantComposite  %128  %35 %35\n%132 = OpConstant  %6  4294967295\n%133 = OpConstantComposite  %128  %132 %132\n%146 = OpTypePointer Function %8\n%147 = OpTypeFunction %8 %32 %33\n%153 = OpConstantFalse  %8\n%160 = OpConstant  %6  6\n%168 = OpTypePointer Function %10\n%169 = OpTypePointer Function %9\n%170 = OpTypePointer Function %7\n%171 = OpTypeFunction %10 %32 %33\n%176 = OpConstantNull  %10\n%199 = OpConstant  %6  3\n%202 = OpConstant  %6  5\n%205 = OpConstant  %6  9\n%207 = OpConstant  %6  10\n%216 = OpConstant  %6  7\n%218 = OpConstant  %6  8\n%226 = OpTypeFunction %4 %4 %10\n%227 = OpConstant  %3  1\n%228 = OpConstant  %3  2.4\n%243 = OpTypeFunction %2\n%245 = OpTypePointer StorageBuffer %13\n%247 = OpConstantComposite  %4  %38 %38 %38\n%248 = OpConstantComposite  %4  %38 %227 %38\n%251 = OpTypePointer StorageBuffer %6\n%256 = OpTypePointer StorageBuffer %4\n%264 = OpConstantComposite  %12  %27 %28 %29 %30 %247 %248\n%265 = OpConstant  %3  10\n%322 = OpTypeFunction %2 %32 %33 %3 %36\n%363 = OpTypeFunction %2 %32 %33\n%43 = OpFunction  %2  None %42\n%44 = OpFunctionParameter  %32\n%45 = OpFunctionParameter  %5\n%46 = OpFunctionParameter  %12\n%47 = OpFunctionParameter  %33\n%48 = OpFunctionParameter  %36\n%49 = OpLabel\n%50 = OpCompositeExtract  %6  %46 0\n%51 = OpCompositeExtract  %6  %46 1\n%52 = OpCompositeExtract  %3  %46 2\n%53 = OpCompositeExtract  %3  %46 3\nOpStore %48 %53\n%54 = OpCompositeExtract  %4  %46 4\n%55 = OpCompositeExtract  %4  %46 5\n%56 = OpFOrdLessThanEqual  %8  %52 %53\n%57 = OpFOrdGreaterThanEqual  %8  %52 %38\n%58 = OpIsInf  %41  %54\n%59 = OpAny  %8  %58\n%60 = OpIsNan  %41  %54\n%61 = OpAny  %8  %60\n%62 = OpLogicalOr  %8  %61 %59\n%63 = OpLogicalNot  %8  %62\n%64 = OpIsInf  %41  %55\n%65 = OpAny  %8  %64\n%66 = OpIsNan  %41  %55\n%67 = OpAny  %8  %66\n%68 = OpLogicalOr  %8  %67 %65\n%69 = OpLogicalNot  %8  %68\n%71 = OpBitwiseAnd  %6  %50 %70\n%72 = OpINotEqual  %8  %71 %35\n%74 = OpBitwiseAnd  %6  %50 %73\n%75 = OpINotEqual  %8  %74 %35\n%76 = OpLogicalAnd  %8  %75 %72\n%77 = OpLogicalNot  %8  %76\n%79 = OpBitwiseAnd  %6  %50 %78\n%80 = OpINotEqual  %8  %79 %35\n%82 = OpBitwiseAnd  %6  %50 %81\n%83 = OpINotEqual  %8  %82 %35\n%84 = OpLogicalAnd  %8  %83 %72\n%85 = OpLogicalAnd  %8  %83 %80\n%86 = OpLogicalAnd  %8  %80 %72\n%87 = OpLogicalOr  %8  %86 %84\n%88 = OpLogicalOr  %8  %87 %85\n%89 = OpLogicalNot  %8  %88\n%91 = OpBitwiseAnd  %6  %50 %90\n%92 = OpINotEqual  %8  %91 %35\n%94 = OpBitwiseAnd  %6  %50 %93\n%95 = OpINotEqual  %8  %94 %35\n%97 = OpBitwiseAnd  %6  %50 %96\n%98 = OpINotEqual  %8  %97 %35\n%100 = OpBitwiseAnd  %6  %50 %99\n%101 = OpINotEqual  %8  %100 %35\n%102 = OpLogicalAnd  %8  %101 %92\n%103 = OpLogicalAnd  %8  %101 %95\n%104 = OpLogicalAnd  %8  %101 %98\n%105 = OpLogicalAnd  %8  %98 %92\n%106 = OpLogicalAnd  %8  %98 %95\n%107 = OpLogicalAnd  %8  %95 %92\n%108 = OpLogicalOr  %8  %107 %102\n%109 = OpLogicalOr  %8  %108 %103\n%110 = OpLogicalOr  %8  %109 %104\n%111 = OpLogicalOr  %8  %110 %105\n%112 = OpLogicalOr  %8  %111 %106\n%113 = OpLogicalNot  %8  %112\n%114 = OpLogicalAnd  %8  %113 %56\n%115 = OpLogicalAnd  %8  %114 %57\n%116 = OpLogicalAnd  %8  %115 %63\n%117 = OpLogicalAnd  %8  %116 %69\n%118 = OpLogicalAnd  %8  %117 %77\n%119 = OpLogicalAnd  %8  %118 %89\nOpSelectionMerge %120 None\nOpBranchConditional %119 %122 %121\n%122 = OpLabel\nOpRayQueryInitializeKHR %44 %45 %50 %51 %54 %52 %55 %53\nOpStore %47 %90\nOpBranch %120\n%121 = OpLabel\nOpBranch %120\n%120 = OpLabel\nOpReturn\nOpFunctionEnd\n%148 = OpFunction  %8  None %147\n%149 = OpFunctionParameter  %32\n%150 = OpFunctionParameter  %33\n%151 = OpLabel\n%152 = OpVariable  %146  Function %153\n%154 = OpLoad  %6  %150\n%157 = OpBitwiseAnd  %6  %154 %90\n%158 = OpINotEqual  %8  %157 %35\nOpSelectionMerge %155 None\nOpBranchConditional %158 %156 %155\n%156 = OpLabel\n%159 = OpRayQueryProceedKHR  %8  %149\nOpStore %152 %159\n%161 = OpSelect  %6  %159 %93 %160\n%162 = OpBitwiseOr  %6  %154 %161\nOpStore %150 %162\nOpBranch %155\n%155 = OpLabel\n%163 = OpLoad  %8  %152\nOpReturnValue %163\nOpFunctionEnd\n%172 = OpFunction  %10  None %171\n%173 = OpFunctionParameter  %32\n%174 = OpFunctionParameter  %33\n%175 = OpLabel\n%177 = OpVariable  %168  Function %176\n%178 = OpLoad  %6  %174\n%179 = OpBitwiseAnd  %6  %178 %93\n%180 = OpINotEqual  %8  %179 %35\n%181 = OpBitwiseAnd  %6  %178 %27\n%182 = OpINotEqual  %8  %181 %35\n%183 = OpLogicalAnd  %8  %182 %180\nOpSelectionMerge %185 None\nOpBranchConditional %183 %184 %185\n%184 = OpLabel\n%186 = OpRayQueryGetIntersectionTypeKHR  %6  %173 %90\n%187 = OpAccessChain  %33  %177 %35\nOpStore %187 %186\n%188 = OpINotEqual  %8  %186 %35\nOpSelectionMerge %190 None\nOpBranchConditional %188 %189 %190\n%189 = OpLabel\n%191 = OpRayQueryGetIntersectionInstanceCustomIndexKHR  %6  %173 %90\n%192 = OpRayQueryGetIntersectionInstanceIdKHR  %6  %173 %90\n%193 = OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR  %6  %173 %90\n%194 = OpRayQueryGetIntersectionGeometryIndexKHR  %6  %173 %90\n%195 = OpRayQueryGetIntersectionPrimitiveIndexKHR  %6  %173 %90\n%196 = OpRayQueryGetIntersectionObjectToWorldKHR  %9  %173 %90\n%197 = OpRayQueryGetIntersectionWorldToObjectKHR  %9  %173 %90\n%198 = OpAccessChain  %33  %177 %93\nOpStore %198 %191\n%200 = OpAccessChain  %33  %177 %199\nOpStore %200 %192\n%201 = OpAccessChain  %33  %177 %27\nOpStore %201 %193\n%203 = OpAccessChain  %33  %177 %202\nOpStore %203 %194\n%204 = OpAccessChain  %33  %177 %160\nOpStore %204 %195\n%206 = OpAccessChain  %169  %177 %205\nOpStore %206 %196\n%208 = OpAccessChain  %169  %177 %207\nOpStore %208 %197\n%209 = OpIEqual  %8  %186 %90\n%212 = OpRayQueryGetIntersectionTKHR  %3  %173 %90\n%213 = OpAccessChain  %36  %177 %90\nOpStore %213 %212\nOpSelectionMerge %211 None\nOpBranchConditional %188 %210 %211\n%210 = OpLabel\n%214 = OpRayQueryGetIntersectionBarycentricsKHR  %7  %173 %90\n%215 = OpRayQueryGetIntersectionFrontFaceKHR  %8  %173 %90\n%217 = OpAccessChain  %170  %177 %216\nOpStore %217 %214\n%219 = OpAccessChain  %146  %177 %218\nOpStore %219 %215\nOpBranch %211\n%211 = OpLabel\nOpBranch %190\n%190 = OpLabel\nOpBranch %185\n%185 = OpLabel\n%220 = OpLoad  %10  %177\nOpReturnValue %220\nOpFunctionEnd\n%25 = OpFunction  %10  None %26\n%21 = OpFunctionParameter  %4\n%22 = OpFunctionParameter  %4\n%23 = OpFunctionParameter  %16\n%20 = OpLabel\n%31 = OpVariable  %32  Function\n%34 = OpVariable  %33  Function %35\n%37 = OpVariable  %36  Function %38\n%134 = OpVariable  %129  Function %133\n%24 = OpLoad  %5  %23\nOpBranch %39\n%39 = OpLabel\n%40 = OpCompositeConstruct  %12  %27 %28 %29 %30 %21 %22\n%123 = OpFunctionCall  %2  %43 %31 %24 %40 %34 %37\nOpBranch %124\n%124 = OpLabel\nOpLoopMerge %125 %127 None\nOpBranch %135\n%135 = OpLabel\n%136 = OpLoad  %128  %134\n%137 = OpIEqual  %130  %131 %136\n%138 = OpAll  %8  %137\nOpSelectionMerge %139 None\nOpBranchConditional %138 %125 %139\n%139 = OpLabel\n%140 = OpCompositeExtract  %6  %136 1\n%141 = OpIEqual  %8  %140 %35\n%142 = OpSelect  %6  %141 %90 %35\n%143 = OpCompositeConstruct  %128  %142 %90\n%144 = OpISub  %128  %136 %143\nOpStore %134 %144\nOpBranch %126\n%126 = OpLabel\n%145 = OpFunctionCall  %8  %148 %31 %34\nOpSelectionMerge %164 None\nOpBranchConditional %145 %164 %165\n%165 = OpLabel\nOpBranch %125\n%164 = OpLabel\nOpBranch %166\n%166 = OpLabel\nOpBranch %167\n%167 = OpLabel\nOpBranch %127\n%127 = OpLabel\nOpBranch %124\n%125 = OpLabel\n%221 = OpFunctionCall  %10  %172 %31 %34\nOpReturnValue %221\nOpFunctionEnd\n%225 = OpFunction  %4  None %226\n%223 = OpFunctionParameter  %4\n%224 = OpFunctionParameter  %10\n%222 = OpLabel\nOpBranch %229\n%229 = OpLabel\n%230 = OpCompositeExtract  %9  %224 10\n%231 = OpCompositeConstruct  %14  %223 %227\n%232 = OpMatrixTimesVector  %4  %230 %231\n%233 = OpVectorShuffle  %7  %232 %232 0 1\n%234 = OpExtInst  %7  %1 Normalize %233\n%235 = OpVectorTimesScalar  %7  %234 %228\n%236 = OpCompositeExtract  %9  %224 9\n%237 = OpCompositeConstruct  %14  %235 %38 %227\n%238 = OpMatrixTimesVector  %4  %236 %237\n%239 = OpFSub  %4  %223 %238\n%240 = OpExtInst  %4  %1 Normalize %239\nOpReturnValue %240\nOpFunctionEnd\n%242 = OpFunction  %2  None %243\n%241 = OpLabel\n%244 = OpLoad  %5  %15\n%246 = OpAccessChain  %245  %17 %35\nOpBranch %249\n%249 = OpLabel\n%250 = OpFunctionCall  %10  %25 %247 %248 %15\n%252 = OpCompositeExtract  %6  %250 0\n%253 = OpIEqual  %8  %252 %35\n%254 = OpSelect  %6  %253 %90 %35\n%255 = OpAccessChain  %251  %246 %35\nOpStore %255 %254\n%257 = OpCompositeExtract  %3  %250 1\n%258 = OpVectorTimesScalar  %4  %248 %257\n%259 = OpFunctionCall  %4  %225 %258 %250\n%260 = OpAccessChain  %256  %246 %90\nOpStore %260 %259\nOpReturn\nOpFunctionEnd\n%271 = OpFunction  %10  None %171\n%272 = OpFunctionParameter  %32\n%273 = OpFunctionParameter  %33\n%274 = OpLabel\n%275 = OpVariable  %168  Function %176\n%276 = OpLoad  %6  %273\n%277 = OpBitwiseAnd  %6  %276 %93\n%278 = OpINotEqual  %8  %277 %35\n%279 = OpBitwiseAnd  %6  %276 %27\n%280 = OpINotEqual  %8  %279 %35\n%281 = OpLogicalNot  %8  %280\n%282 = OpLogicalAnd  %8  %281 %278\nOpSelectionMerge %284 None\nOpBranchConditional %282 %283 %284\n%283 = OpLabel\n%285 = OpRayQueryGetIntersectionTypeKHR  %6  %272 %35\n%286 = OpIEqual  %8  %285 %35\n%287 = OpSelect  %6  %286 %90 %199\n%288 = OpAccessChain  %33  %275 %35\nOpStore %288 %287\n%289 = OpINotEqual  %8  %287 %35\nOpSelectionMerge %291 None\nOpBranchConditional %289 %290 %291\n%290 = OpLabel\n%292 = OpRayQueryGetIntersectionInstanceCustomIndexKHR  %6  %272 %35\n%293 = OpRayQueryGetIntersectionInstanceIdKHR  %6  %272 %35\n%294 = OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR  %6  %272 %35\n%295 = OpRayQueryGetIntersectionGeometryIndexKHR  %6  %272 %35\n%296 = OpRayQueryGetIntersectionPrimitiveIndexKHR  %6  %272 %35\n%297 = OpRayQueryGetIntersectionObjectToWorldKHR  %9  %272 %35\n%298 = OpRayQueryGetIntersectionWorldToObjectKHR  %9  %272 %35\n%299 = OpAccessChain  %33  %275 %93\nOpStore %299 %292\n%300 = OpAccessChain  %33  %275 %199\nOpStore %300 %293\n%301 = OpAccessChain  %33  %275 %27\nOpStore %301 %294\n%302 = OpAccessChain  %33  %275 %202\nOpStore %302 %295\n%303 = OpAccessChain  %33  %275 %160\nOpStore %303 %296\n%304 = OpAccessChain  %169  %275 %205\nOpStore %304 %297\n%305 = OpAccessChain  %169  %275 %207\nOpStore %305 %298\n%306 = OpIEqual  %8  %287 %90\nOpSelectionMerge %308 None\nOpBranchConditional %289 %307 %308\n%307 = OpLabel\n%309 = OpRayQueryGetIntersectionTKHR  %3  %272 %35\n%310 = OpAccessChain  %36  %275 %90\nOpStore %310 %309\n%311 = OpRayQueryGetIntersectionBarycentricsKHR  %7  %272 %35\n%312 = OpRayQueryGetIntersectionFrontFaceKHR  %8  %272 %35\n%313 = OpAccessChain  %170  %275 %216\nOpStore %313 %311\n%314 = OpAccessChain  %146  %275 %218\nOpStore %314 %312\nOpBranch %308\n%308 = OpLabel\nOpBranch %291\n%291 = OpLabel\nOpBranch %284\n%284 = OpLabel\n%315 = OpLoad  %10  %275\nOpReturnValue %315\nOpFunctionEnd\n%323 = OpFunction  %2  None %322\n%324 = OpFunctionParameter  %32\n%325 = OpFunctionParameter  %33\n%326 = OpFunctionParameter  %3\n%327 = OpFunctionParameter  %36\n%328 = OpLabel\n%329 = OpVariable  %36  Function\n%330 = OpVariable  %36  Function\n%333 = OpLoad  %6  %325\n%334 = OpBitwiseAnd  %6  %333 %93\n%335 = OpINotEqual  %8  %334 %35\n%336 = OpBitwiseAnd  %6  %333 %27\n%337 = OpINotEqual  %8  %336 %35\n%338 = OpLogicalNot  %8  %337\n%339 = OpLogicalAnd  %8  %338 %335\nOpSelectionMerge %332 None\nOpBranchConditional %339 %331 %332\n%331 = OpLabel\n%340 = OpRayQueryGetIntersectionTypeKHR  %6  %324 %35\n%341 = OpIEqual  %8  %340 %90\n%342 = OpRayQueryGetRayTMinKHR  %3  %324\n%343 = OpRayQueryGetIntersectionTypeKHR  %6  %324 %90\n%344 = OpIEqual  %8  %343 %35\nOpSelectionMerge %345 None\nOpBranchConditional %344 %346 %347\n%346 = OpLabel\n%348 = OpLoad  %3  %327\nOpStore %330 %348\nOpBranch %345\n%347 = OpLabel\n%349 = OpRayQueryGetIntersectionTKHR  %3  %324 %35\nOpStore %330 %349\nOpBranch %345\n%345 = OpLabel\n%350 = OpFOrdGreaterThanEqual  %8  %326 %342\n%351 = OpLoad  %3  %330\n%352 = OpFOrdLessThanEqual  %8  %326 %351\n%353 = OpLogicalAnd  %8  %350 %352\n%354 = OpLogicalAnd  %8  %353 %341\nOpSelectionMerge %356 None\nOpBranchConditional %354 %355 %356\n%355 = OpLabel\nOpRayQueryGenerateIntersectionKHR %324 %326\nOpBranch %356\n%356 = OpLabel\nOpBranch %332\n%332 = OpLabel\nOpReturn\nOpFunctionEnd\n%364 = OpFunction  %2  None %363\n%365 = OpFunctionParameter  %32\n%366 = OpFunctionParameter  %33\n%367 = OpLabel\n%370 = OpLoad  %6  %366\n%371 = OpBitwiseAnd  %6  %370 %93\n%372 = OpINotEqual  %8  %371 %35\n%373 = OpBitwiseAnd  %6  %370 %27\n%374 = OpINotEqual  %8  %373 %35\n%375 = OpLogicalNot  %8  %374\n%376 = OpLogicalAnd  %8  %375 %372\nOpSelectionMerge %369 None\nOpBranchConditional %376 %368 %369\n%368 = OpLabel\n%377 = OpRayQueryGetIntersectionTypeKHR  %6  %365 %35\n%378 = OpIEqual  %8  %377 %35\nOpSelectionMerge %380 None\nOpBranchConditional %378 %379 %380\n%379 = OpLabel\nOpRayQueryConfirmIntersectionKHR %365\nOpBranch %380\n%380 = OpLabel\nOpBranch %369\n%369 = OpLabel\nOpReturn\nOpFunctionEnd\n%383 = OpFunction  %2  None %363\n%384 = OpFunctionParameter  %32\n%385 = OpFunctionParameter  %33\n%386 = OpLabel\n%387 = OpLoad  %6  %385\n%390 = OpBitwiseAnd  %6  %387 %93\n%391 = OpINotEqual  %8  %390 %35\n%392 = OpBitwiseAnd  %6  %387 %27\n%393 = OpINotEqual  %8  %392 %35\n%394 = OpLogicalNot  %8  %393\n%395 = OpLogicalAnd  %8  %394 %391\nOpSelectionMerge %388 None\nOpBranchConditional %395 %389 %388\n%389 = OpLabel\nOpRayQueryTerminateKHR %384\nOpBranch %388\n%388 = OpLabel\nOpReturn\nOpFunctionEnd\n%262 = OpFunction  %2  None %243\n%261 = OpLabel\n%266 = OpVariable  %32  Function\n%267 = OpVariable  %33  Function %35\n%268 = OpVariable  %36  Function %38\n%263 = OpLoad  %5  %15\nOpBranch %269\n%269 = OpLabel\n%270 = OpFunctionCall  %2  %43 %266 %263 %264 %267 %268\n%316 = OpFunctionCall  %10  %271 %266 %267\n%317 = OpCompositeExtract  %6  %316 0\n%318 = OpIEqual  %8  %317 %199\nOpSelectionMerge %319 None\nOpBranchConditional %318 %320 %321\n%320 = OpLabel\n%357 = OpFunctionCall  %2  %323 %266 %267 %265 %268\nOpReturn\n%321 = OpLabel\n%358 = OpCompositeExtract  %6  %316 0\n%359 = OpIEqual  %8  %358 %90\nOpSelectionMerge %360 None\nOpBranchConditional %359 %361 %362\n%361 = OpLabel\n%381 = OpFunctionCall  %2  %364 %266 %267\nOpReturn\n%362 = OpLabel\n%382 = OpFunctionCall  %2  %383 %266 %267\nOpReturn\n%360 = OpLabel\nOpBranch %319\n%319 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-select.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 36\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %8 \"main\"\nOpExecutionMode %8 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 2\n%6 = OpTypeInt 32 1\n%5 = OpTypeVector %6 2\n%9 = OpTypeFunction %2\n%10 = OpConstant  %4  1\n%11 = OpConstant  %6  1\n%12 = OpConstant  %6  2\n%13 = OpConstantComposite  %5  %11 %12\n%14 = OpConstant  %4  0\n%15 = OpConstantComposite  %3  %10 %14\n%16 = OpConstantComposite  %3  %14 %10\n%18 = OpTypePointer Function %5\n%20 = OpTypePointer Function %3\n%21 = OpConstantNull  %3\n%23 = OpTypePointer Function %6\n%25 = OpTypeInt 32 0\n%24 = OpConstant  %25  0\n%28 = OpConstant  %25  1\n%31 = OpTypeBool\n%34 = OpTypeVector %31 2\n%8 = OpFunction  %2  None %9\n%7 = OpLabel\n%17 = OpVariable  %18  Function %13\n%19 = OpVariable  %20  Function %21\nOpBranch %22\n%22 = OpLabel\n%26 = OpAccessChain  %23  %17 %24\n%27 = OpLoad  %6  %26\n%29 = OpAccessChain  %23  %17 %28\n%30 = OpLoad  %6  %29\n%32 = OpSLessThan  %31  %27 %30\n%35 = OpCompositeConstruct  %34  %32 %32\n%33 = OpSelect  %3  %35 %16 %15\nOpStore %19 %33\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-separate-entry-points.compute.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 19\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %16 \"compute\"\nOpExecutionMode %16 LocalSize 1 1 1\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%7 = OpTypeFunction %2\n%10 = OpTypeInt 32 0\n%9 = OpConstant  %10  2\n%11 = OpConstant  %10  1\n%12 = OpConstant  %10  72\n%13 = OpConstant  %10  264\n%14 = OpConstant  %10  2056\n%6 = OpFunction  %2  None %7\n%5 = OpLabel\nOpBranch %8\n%8 = OpLabel\nOpControlBarrier %9 %11 %12\nOpControlBarrier %9 %9 %13\nOpControlBarrier %9 %9 %14\nOpReturn\nOpFunctionEnd\n%16 = OpFunction  %2  None %7\n%15 = OpLabel\nOpBranch %17\n%17 = OpLabel\n%18 = OpFunctionCall  %2  %6\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-separate-entry-points.fragment.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 20\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %16 \"fragment\" %14\nOpExecutionMode %16 OriginUpperLeft\nOpDecorate %14 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%7 = OpTypeFunction %2\n%8 = OpConstant  %4  0\n%15 = OpTypePointer Output %3\n%14 = OpVariable  %15  Output\n%17 = OpConstantNull  %3\n%6 = OpFunction  %2  None %7\n%5 = OpLabel\nOpBranch %9\n%9 = OpLabel\n%10 = OpDPdx  %4  %8\n%11 = OpDPdy  %4  %8\n%12 = OpFwidth  %4  %8\nOpReturn\nOpFunctionEnd\n%16 = OpFunction  %2  None %7\n%13 = OpLabel\nOpBranch %18\n%18 = OpLabel\n%19 = OpFunctionCall  %2  %6\nOpStore %14 %17\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-shadow.spvasm",
    "content": "; SPIR-V\n; Version: 1.2\n; Generator: rspirv\n; Bound: 294\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %85 \"vs_main\" %75 %78 %80 %82 %84\nOpEntryPoint Fragment %143 \"fs_main\" %134 %137 %140 %142\nOpEntryPoint Fragment %228 \"fs_main_without_storage\" %221 %223 %225 %227\nOpExecutionMode %143 OriginUpperLeft\nOpExecutionMode %228 OriginUpperLeft\n%3 = OpString \"shadow.wgsl\"\nOpSource Unknown 0 %3 \"struct Globals {\n    view_proj: mat4x4<f32>,\n    num_lights: vec4<u32>,\n}\n\n@group(0)\n@binding(0)\nvar<uniform> u_globals: Globals;\n\nstruct Entity {\n    world: mat4x4<f32>,\n    color: vec4<f32>,\n}\n\n@group(1)\n@binding(0)\nvar<uniform> u_entity: Entity;\n\n/* Not useful for testing\n@vertex\nfn vs_bake(@location(0) position: vec4<i32>) -> @builtin(position) vec4<f32> {\n    return u_globals.view_proj * u_entity.world * vec4<f32>(position);\n}\n*/\n\nstruct VertexOutput {\n    @builtin(position) proj_position: vec4<f32>,\n    @location(0) world_normal: vec3<f32>,\n    @location(1) world_position: vec4<f32>,\n}\n\n@vertex\nfn vs_main(\n    @location(0) position: vec4<i32>,\n    @location(1) normal: vec4<i32>,\n) -> VertexOutput {\n    let w = u_entity.world;\n    let world_pos = u_entity.world * vec4<f32>(position);\n    var out: VertexOutput;\n    out.world_normal = mat3x3<f32>(w[0].xyz, w[1].xyz, w[2].xyz) * vec3<f32>(normal.xyz);\n    out.world_position = world_pos;\n    out.proj_position = u_globals.view_proj * world_pos;\n    return out;\n}\n\n// fragment shader\n\nstruct Light {\n    proj: mat4x4<f32>,\n    pos: vec4<f32>,\n    color: vec4<f32>,\n}\n\n@group(0)\n@binding(1)\nvar<storage, read> s_lights: array<Light>;\n@group(0)\n@binding(1)\nvar<uniform> u_lights: array<Light, 10>; // Used when storage types are not supported\n@group(0)\n@binding(2)\nvar t_shadow: texture_depth_2d_array;\n@group(0)\n@binding(3)\nvar sampler_shadow: sampler_comparison;\n\nfn fetch_shadow(light_id: u32, homogeneous_coords: vec4<f32>) -> f32 {\n    if (homogeneous_coords.w <= 0.0) {\n        return 1.0;\n    }\n    // compensate for the Y-flip difference between the NDC and texture coordinates\n    let flip_correction = vec2<f32>(0.5, -0.5);\n    // compute texture coordinates for shadow lookup\n    let proj_correction = 1.0 / homogeneous_coords.w;\n    let light_local = homogeneous_coords.xy * flip_correction * proj_correction + vec2<f32>(0.5, 0.5);\n    // do the lookup, using HW PCF and comparison\n    return textureSampleCompareLevel(t_shadow, sampler_shadow, light_local, i32(light_id), homogeneous_coords.z * proj_correction);\n}\n\nconst c_ambient: vec3<f32> = vec3<f32>(0.05, 0.05, 0.05);\nconst c_max_lights: u32 = 10u;\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(in.world_normal);\n    // accumulate color\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i++) {\n        let light = s_lights[i];\n        // project into the light space\n        let shadow = fetch_shadow(i, light.proj * in.world_position);\n        // compute Lambertian diffuse term\n        let light_dir = normalize(light.pos.xyz - in.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        // add light contribution\n        color += shadow * diffuse * light.color.xyz;\n    }\n    // multiply the light by material color\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n\n// The fragment entrypoint used when storage buffers are not available for the lights\n@fragment\nfn fs_main_without_storage(in: VertexOutput) -> @location(0) vec4<f32> {\n    let normal = normalize(in.world_normal);\n    var color: vec3<f32> = c_ambient;\n    for(var i = 0u; i < min(u_globals.num_lights.x, c_max_lights); i++) {\n        // This line is the only difference from the entrypoint above. It uses the lights\n        // uniform instead of the lights storage buffer\n        let light = u_lights[i];\n        let shadow = fetch_shadow(i, light.proj * in.world_position);\n        let light_dir = normalize(light.pos.xyz - in.world_position.xyz);\n        let diffuse = max(0.0, dot(normal, light_dir));\n        color += shadow * diffuse * light.color.xyz;\n    }\n    return vec4<f32>(color, 1.0) * u_entity.color;\n}\n\"\nOpMemberName %9 0 \"view_proj\"\nOpMemberName %9 1 \"num_lights\"\nOpName %9 \"Globals\"\nOpMemberName %10 0 \"world\"\nOpMemberName %10 1 \"color\"\nOpName %10 \"Entity\"\nOpMemberName %12 0 \"proj_position\"\nOpMemberName %12 1 \"world_normal\"\nOpMemberName %12 2 \"world_position\"\nOpName %12 \"VertexOutput\"\nOpMemberName %16 0 \"proj\"\nOpMemberName %16 1 \"pos\"\nOpMemberName %16 2 \"color\"\nOpName %16 \"Light\"\nOpName %24 \"c_ambient\"\nOpName %19 \"c_max_lights\"\nOpName %25 \"u_globals\"\nOpName %28 \"u_entity\"\nOpName %31 \"s_lights\"\nOpName %34 \"u_lights\"\nOpName %37 \"t_shadow\"\nOpName %39 \"sampler_shadow\"\nOpName %42 \"light_id\"\nOpName %43 \"homogeneous_coords\"\nOpName %44 \"fetch_shadow\"\nOpName %75 \"position\"\nOpName %78 \"normal\"\nOpName %80 \"proj_position\"\nOpName %82 \"world_normal\"\nOpName %84 \"world_position\"\nOpName %85 \"vs_main\"\nOpName %92 \"out\"\nOpName %134 \"proj_position\"\nOpName %137 \"world_normal\"\nOpName %140 \"world_position\"\nOpName %143 \"fs_main\"\nOpName %150 \"color\"\nOpName %151 \"i\"\nOpName %166 \"loop_bound\"\nOpName %221 \"proj_position\"\nOpName %223 \"world_normal\"\nOpName %225 \"world_position\"\nOpName %228 \"fs_main_without_storage\"\nOpName %235 \"color\"\nOpName %236 \"i\"\nOpName %244 \"loop_bound\"\nOpMemberDecorate %9 0 Offset 0\nOpMemberDecorate %9 0 ColMajor\nOpMemberDecorate %9 0 MatrixStride 16\nOpMemberDecorate %9 1 Offset 64\nOpMemberDecorate %10 0 Offset 0\nOpMemberDecorate %10 0 ColMajor\nOpMemberDecorate %10 0 MatrixStride 16\nOpMemberDecorate %10 1 Offset 64\nOpMemberDecorate %12 0 Offset 0\nOpMemberDecorate %12 1 Offset 16\nOpMemberDecorate %12 2 Offset 32\nOpMemberDecorate %16 0 Offset 0\nOpMemberDecorate %16 0 ColMajor\nOpMemberDecorate %16 0 MatrixStride 16\nOpMemberDecorate %16 1 Offset 64\nOpMemberDecorate %16 2 Offset 80\nOpDecorate %17 ArrayStride 96\nOpDecorate %18 ArrayStride 96\nOpDecorate %25 DescriptorSet 0\nOpDecorate %25 Binding 0\nOpDecorate %26 Block\nOpMemberDecorate %26 0 Offset 0\nOpDecorate %28 DescriptorSet 1\nOpDecorate %28 Binding 0\nOpDecorate %29 Block\nOpMemberDecorate %29 0 Offset 0\nOpDecorate %31 NonWritable\nOpDecorate %31 DescriptorSet 0\nOpDecorate %31 Binding 1\nOpDecorate %32 Block\nOpMemberDecorate %32 0 Offset 0\nOpDecorate %34 DescriptorSet 0\nOpDecorate %34 Binding 1\nOpDecorate %35 Block\nOpMemberDecorate %35 0 Offset 0\nOpDecorate %37 DescriptorSet 0\nOpDecorate %37 Binding 2\nOpDecorate %39 DescriptorSet 0\nOpDecorate %39 Binding 3\nOpDecorate %75 Location 0\nOpDecorate %78 Location 1\nOpDecorate %80 BuiltIn Position\nOpDecorate %82 Location 0\nOpDecorate %84 Location 1\nOpDecorate %134 BuiltIn FragCoord\nOpDecorate %137 Location 0\nOpDecorate %140 Location 1\nOpDecorate %142 Location 0\nOpDecorate %221 BuiltIn FragCoord\nOpDecorate %223 Location 0\nOpDecorate %225 Location 1\nOpDecorate %227 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%6 = OpTypeVector %4 4\n%5 = OpTypeMatrix %6 4\n%7 = OpTypeInt 32 0\n%8 = OpTypeVector %7 4\n%9 = OpTypeStruct %5 %8\n%10 = OpTypeStruct %5 %6\n%11 = OpTypeVector %4 3\n%12 = OpTypeStruct %6 %11 %6\n%14 = OpTypeInt 32 1\n%13 = OpTypeVector %14 4\n%15 = OpTypeMatrix %11 3\n%16 = OpTypeStruct %5 %6 %6\n%17 = OpTypeRuntimeArray %16\n%19 = OpConstant  %7  10\n%18 = OpTypeArray %16 %19\n%20 = OpTypeImage %4 2D 1 1 0 1 Unknown\n%21 = OpTypeSampler\n%22 = OpTypeVector %4 2\n%23 = OpConstant  %4  0.05\n%24 = OpConstantComposite  %11  %23 %23 %23\n%26 = OpTypeStruct %9\n%27 = OpTypePointer Uniform %26\n%25 = OpVariable  %27  Uniform\n%29 = OpTypeStruct %10\n%30 = OpTypePointer Uniform %29\n%28 = OpVariable  %30  Uniform\n%32 = OpTypeStruct %17\n%33 = OpTypePointer StorageBuffer %32\n%31 = OpVariable  %33  StorageBuffer\n%35 = OpTypeStruct %18\n%36 = OpTypePointer Uniform %35\n%34 = OpVariable  %36  Uniform\n%38 = OpTypePointer UniformConstant %20\n%37 = OpVariable  %38  UniformConstant\n%40 = OpTypePointer UniformConstant %21\n%39 = OpVariable  %40  UniformConstant\n%45 = OpTypeFunction %4 %7 %6\n%48 = OpConstant  %4  0\n%49 = OpConstant  %4  1\n%50 = OpConstant  %4  0.5\n%51 = OpConstant  %4  -0.5\n%52 = OpConstantComposite  %22  %50 %51\n%53 = OpConstantComposite  %22  %50 %50\n%56 = OpTypeBool\n%69 = OpTypeSampledImage %20\n%76 = OpTypePointer Input %13\n%75 = OpVariable  %76  Input\n%78 = OpVariable  %76  Input\n%81 = OpTypePointer Output %6\n%80 = OpVariable  %81  Output\n%83 = OpTypePointer Output %11\n%82 = OpVariable  %83  Output\n%84 = OpVariable  %81  Output\n%86 = OpTypeFunction %2\n%87 = OpTypePointer Uniform %9\n%88 = OpConstant  %7  0\n%90 = OpTypePointer Uniform %10\n%93 = OpTypePointer Function %12\n%94 = OpConstantNull  %12\n%96 = OpTypePointer Uniform %5\n%103 = OpTypePointer Function %11\n%111 = OpTypeVector %14 3\n%115 = OpConstant  %7  1\n%117 = OpTypePointer Function %6\n%118 = OpConstant  %7  2\n%126 = OpTypePointer Output %4\n%135 = OpTypePointer Input %6\n%134 = OpVariable  %135  Input\n%138 = OpTypePointer Input %11\n%137 = OpVariable  %138  Input\n%140 = OpVariable  %135  Input\n%142 = OpVariable  %81  Output\n%146 = OpTypePointer StorageBuffer %17\n%152 = OpTypePointer Function %7\n%160 = OpTypeVector %7 2\n%161 = OpTypePointer Function %160\n%162 = OpTypeVector %56 2\n%163 = OpConstantComposite  %160  %88 %88\n%164 = OpConstant  %7  4294967295\n%165 = OpConstantComposite  %160  %164 %164\n%178 = OpTypePointer Uniform %8\n%179 = OpTypePointer Uniform %7\n%189 = OpTypePointer StorageBuffer %16\n%215 = OpTypePointer Uniform %6\n%221 = OpVariable  %135  Input\n%223 = OpVariable  %138  Input\n%225 = OpVariable  %135  Input\n%227 = OpVariable  %81  Output\n%231 = OpTypePointer Uniform %18\n%265 = OpTypePointer Uniform %16\n%44 = OpFunction  %4  None %45\n%42 = OpFunctionParameter  %7\n%43 = OpFunctionParameter  %6\n%41 = OpLabel\n%46 = OpLoad  %20  %37\n%47 = OpLoad  %21  %39\nOpBranch %54\n%54 = OpLabel\nOpLine %3 68 9\n%55 = OpCompositeExtract  %4  %43 3\nOpLine %3 68 9\n%57 = OpFOrdLessThanEqual  %56  %55 %48\nOpLine %3 68 5\nOpSelectionMerge %58 None\nOpBranchConditional %57 %59 %58\n%59 = OpLabel\nOpReturnValue %49\n%58 = OpLabel\nOpLine %3 72 27\nOpLine %3 74 33\n%60 = OpCompositeExtract  %4  %43 3\nOpLine %3 74 27\n%61 = OpFDiv  %4  %49 %60\nOpLine %3 75 23\n%62 = OpVectorShuffle  %22  %43 %43 0 1\n%63 = OpFMul  %22  %62 %52\n%64 = OpVectorTimesScalar  %22  %63 %61\nOpLine %3 75 23\n%65 = OpFAdd  %22  %64 %53\nOpLine %3 77 12\n%66 = OpBitcast  %14  %42\n%67 = OpCompositeExtract  %4  %43 2\n%68 = OpFMul  %4  %67 %61\n%70 = OpConvertSToF  %4  %66\n%71 = OpCompositeConstruct  %11  %65 %70\n%72 = OpSampledImage  %69  %46 %47\n%73 = OpImageSampleDrefExplicitLod  %4  %72 %71 %68 Lod %48\nOpReturnValue %73\nOpFunctionEnd\n%85 = OpFunction  %2  None %86\n%74 = OpLabel\n%92 = OpVariable  %93  Function %94\n%77 = OpLoad  %13  %75\n%79 = OpLoad  %13  %78\n%89 = OpAccessChain  %87  %25 %88\n%91 = OpAccessChain  %90  %28 %88\nOpBranch %95\n%95 = OpLabel\nOpLine %3 37 13\n%97 = OpAccessChain  %96  %91 %88\n%98 = OpLoad  %5  %97\nOpLine %3 38 21\n%99 = OpAccessChain  %96  %91 %88\n%100 = OpLoad  %5  %99\n%101 = OpConvertSToF  %6  %77\n%102 = OpMatrixTimesVector  %6  %100 %101\nOpLine %3 40 5\nOpLine %3 40 36\n%104 = OpCompositeExtract  %6  %98 0\n%105 = OpVectorShuffle  %11  %104 %104 0 1 2\nOpLine %3 40 46\n%106 = OpCompositeExtract  %6  %98 1\n%107 = OpVectorShuffle  %11  %106 %106 0 1 2\nOpLine %3 40 24\n%108 = OpCompositeExtract  %6  %98 2\n%109 = OpVectorShuffle  %11  %108 %108 0 1 2\n%110 = OpCompositeConstruct  %15  %105 %107 %109\n%112 = OpVectorShuffle  %111  %79 %79 0 1 2\n%113 = OpConvertSToF  %11  %112\n%114 = OpMatrixTimesVector  %11  %110 %113\nOpLine %3 40 5\n%116 = OpAccessChain  %103  %92 %115\nOpStore %116 %114\nOpLine %3 41 5\nOpLine %3 41 5\n%119 = OpAccessChain  %117  %92 %118\nOpStore %119 %102\nOpLine %3 42 5\nOpLine %3 42 25\n%120 = OpAccessChain  %96  %89 %88\n%121 = OpLoad  %5  %120\n%122 = OpMatrixTimesVector  %6  %121 %102\nOpLine %3 42 5\n%123 = OpAccessChain  %117  %92 %88\nOpStore %123 %122\nOpLine %3 1 1\n%124 = OpLoad  %12  %92\n%125 = OpCompositeExtract  %6  %124 0\nOpStore %80 %125\n%127 = OpAccessChain  %126  %80 %115\n%128 = OpLoad  %4  %127\n%129 = OpFNegate  %4  %128\nOpStore %127 %129\n%130 = OpCompositeExtract  %11  %124 1\nOpStore %82 %130\n%131 = OpCompositeExtract  %6  %124 2\nOpStore %84 %131\nOpReturn\nOpFunctionEnd\n%143 = OpFunction  %2  None %86\n%132 = OpLabel\n%150 = OpVariable  %103  Function %24\n%151 = OpVariable  %152  Function %88\n%166 = OpVariable  %161  Function %165\n%136 = OpLoad  %6  %134\n%139 = OpLoad  %11  %137\n%141 = OpLoad  %6  %140\n%133 = OpCompositeConstruct  %12  %136 %139 %141\n%144 = OpAccessChain  %87  %25 %88\n%145 = OpAccessChain  %90  %28 %88\n%147 = OpAccessChain  %146  %31 %88\n%148 = OpLoad  %20  %37\n%149 = OpLoad  %21  %39\nOpBranch %153\n%153 = OpLabel\nOpLine %3 85 18\n%154 = OpCompositeExtract  %11  %133 1\n%155 = OpExtInst  %11  %1 Normalize %154\nOpBranch %156\n%156 = OpLabel\nOpLine %3 88 5\nOpLoopMerge %157 %159 None\nOpBranch %167\n%167 = OpLabel\n%168 = OpLoad  %160  %166\n%169 = OpIEqual  %162  %163 %168\n%170 = OpAll  %56  %169\nOpSelectionMerge %171 None\nOpBranchConditional %170 %157 %171\n%171 = OpLabel\n%172 = OpCompositeExtract  %7  %168 1\n%173 = OpIEqual  %56  %172 %88\n%174 = OpSelect  %7  %173 %115 %88\n%175 = OpCompositeConstruct  %160  %174 %115\n%176 = OpISub  %160  %168 %175\nOpStore %166 %176\nOpBranch %158\n%158 = OpLabel\nOpLine %3 1 1\n%177 = OpLoad  %7  %151\nOpLine %3 88 29\n%180 = OpAccessChain  %179  %144 %115 %88\n%181 = OpLoad  %7  %180\nOpLine %3 88 21\n%182 = OpExtInst  %7  %1 UMin %181 %19\n%183 = OpULessThan  %56  %177 %182\nOpLine %3 88 20\nOpSelectionMerge %184 None\nOpBranchConditional %183 %184 %185\n%185 = OpLabel\nOpBranch %157\n%184 = OpLabel\nOpBranch %186\n%186 = OpLabel\nOpLine %3 89 21\n%188 = OpLoad  %7  %151\n%190 = OpAccessChain  %189  %147 %188\n%191 = OpLoad  %16  %190\nOpLine %3 91 38\n%192 = OpLoad  %7  %151\n%193 = OpCompositeExtract  %5  %191 0\n%194 = OpCompositeExtract  %6  %133 2\n%195 = OpMatrixTimesVector  %6  %193 %194\nOpLine %3 91 22\n%196 = OpFunctionCall  %4  %44 %192 %195\nOpLine %3 93 25\n%197 = OpCompositeExtract  %6  %191 1\n%198 = OpVectorShuffle  %11  %197 %197 0 1 2\n%199 = OpCompositeExtract  %6  %133 2\n%200 = OpVectorShuffle  %11  %199 %199 0 1 2\n%201 = OpFSub  %11  %198 %200\n%202 = OpExtInst  %11  %1 Normalize %201\nOpLine %3 94 32\n%203 = OpDot  %4  %155 %202\nOpLine %3 94 23\n%204 = OpExtInst  %4  %1 FMax %48 %203\nOpLine %3 96 9\n%205 = OpLoad  %11  %150\n%206 = OpFMul  %4  %196 %204\n%207 = OpCompositeExtract  %6  %191 2\n%208 = OpVectorShuffle  %11  %207 %207 0 1 2\n%209 = OpVectorTimesScalar  %11  %208 %206\n%210 = OpFAdd  %11  %205 %209\nOpLine %3 96 9\nOpStore %150 %210\nOpBranch %187\n%187 = OpLabel\nOpBranch %159\n%159 = OpLabel\nOpLine %3 88 68\n%211 = OpLoad  %7  %151\n%212 = OpIAdd  %7  %211 %115\nOpLine %3 88 68\nOpStore %151 %212\nOpBranch %156\n%157 = OpLabel\nOpLine %3 1 1\n%213 = OpLoad  %11  %150\nOpLine %3 99 12\n%214 = OpCompositeConstruct  %6  %213 %49\nOpLine %3 99 12\n%216 = OpAccessChain  %215  %145 %115\n%217 = OpLoad  %6  %216\n%218 = OpFMul  %6  %214 %217\nOpStore %142 %218\nOpReturn\nOpFunctionEnd\n%228 = OpFunction  %2  None %86\n%219 = OpLabel\n%235 = OpVariable  %103  Function %24\n%236 = OpVariable  %152  Function %88\n%244 = OpVariable  %161  Function %165\n%222 = OpLoad  %6  %221\n%224 = OpLoad  %11  %223\n%226 = OpLoad  %6  %225\n%220 = OpCompositeConstruct  %12  %222 %224 %226\n%229 = OpAccessChain  %87  %25 %88\n%230 = OpAccessChain  %90  %28 %88\n%232 = OpAccessChain  %231  %34 %88\n%233 = OpLoad  %20  %37\n%234 = OpLoad  %21  %39\nOpBranch %237\n%237 = OpLabel\nOpLine %3 105 18\n%238 = OpCompositeExtract  %11  %220 1\n%239 = OpExtInst  %11  %1 Normalize %238\nOpBranch %240\n%240 = OpLabel\nOpLine %3 107 5\nOpLoopMerge %241 %243 None\nOpBranch %245\n%245 = OpLabel\n%246 = OpLoad  %160  %244\n%247 = OpIEqual  %162  %163 %246\n%248 = OpAll  %56  %247\nOpSelectionMerge %249 None\nOpBranchConditional %248 %241 %249\n%249 = OpLabel\n%250 = OpCompositeExtract  %7  %246 1\n%251 = OpIEqual  %56  %250 %88\n%252 = OpSelect  %7  %251 %115 %88\n%253 = OpCompositeConstruct  %160  %252 %115\n%254 = OpISub  %160  %246 %253\nOpStore %244 %254\nOpBranch %242\n%242 = OpLabel\nOpLine %3 1 1\n%255 = OpLoad  %7  %236\nOpLine %3 107 29\n%256 = OpAccessChain  %179  %229 %115 %88\n%257 = OpLoad  %7  %256\nOpLine %3 107 21\n%258 = OpExtInst  %7  %1 UMin %257 %19\n%259 = OpULessThan  %56  %255 %258\nOpLine %3 107 20\nOpSelectionMerge %260 None\nOpBranchConditional %259 %260 %261\n%261 = OpLabel\nOpBranch %241\n%260 = OpLabel\nOpBranch %262\n%262 = OpLabel\nOpLine %3 110 21\n%264 = OpLoad  %7  %236\n%266 = OpAccessChain  %265  %232 %264\n%267 = OpLoad  %16  %266\nOpLine %3 111 38\n%268 = OpLoad  %7  %236\n%269 = OpCompositeExtract  %5  %267 0\n%270 = OpCompositeExtract  %6  %220 2\n%271 = OpMatrixTimesVector  %6  %269 %270\nOpLine %3 111 22\n%272 = OpFunctionCall  %4  %44 %268 %271\nOpLine %3 112 25\n%273 = OpCompositeExtract  %6  %267 1\n%274 = OpVectorShuffle  %11  %273 %273 0 1 2\n%275 = OpCompositeExtract  %6  %220 2\n%276 = OpVectorShuffle  %11  %275 %275 0 1 2\n%277 = OpFSub  %11  %274 %276\n%278 = OpExtInst  %11  %1 Normalize %277\nOpLine %3 113 32\n%279 = OpDot  %4  %239 %278\nOpLine %3 113 23\n%280 = OpExtInst  %4  %1 FMax %48 %279\nOpLine %3 114 9\n%281 = OpLoad  %11  %235\n%282 = OpFMul  %4  %272 %280\n%283 = OpCompositeExtract  %6  %267 2\n%284 = OpVectorShuffle  %11  %283 %283 0 1 2\n%285 = OpVectorTimesScalar  %11  %284 %282\n%286 = OpFAdd  %11  %281 %285\nOpLine %3 114 9\nOpStore %235 %286\nOpBranch %263\n%263 = OpLabel\nOpBranch %243\n%243 = OpLabel\nOpLine %3 107 68\n%287 = OpLoad  %7  %236\n%288 = OpIAdd  %7  %287 %115\nOpLine %3 107 68\nOpStore %236 %288\nOpBranch %240\n%241 = OpLabel\nOpLine %3 1 1\n%289 = OpLoad  %11  %235\nOpLine %3 116 12\n%290 = OpCompositeConstruct  %6  %289 %49\nOpLine %3 116 12\n%291 = OpAccessChain  %215  %230 %115\n%292 = OpLoad  %6  %291\n%293 = OpFMul  %6  %290 %292\nOpStore %227 %293\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-skybox.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 114\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Vertex %46 \"vs_main\" %39 %42 %44\nOpEntryPoint Fragment %106 \"fs_main\" %99 %102 %105\nOpExecutionMode %106 OriginUpperLeft\nOpMemberDecorate %6 0 Offset 0\nOpMemberDecorate %6 1 Offset 16\nOpMemberDecorate %8 0 Offset 0\nOpMemberDecorate %8 0 ColMajor\nOpMemberDecorate %8 0 MatrixStride 16\nOpMemberDecorate %8 1 Offset 64\nOpMemberDecorate %8 1 ColMajor\nOpMemberDecorate %8 1 MatrixStride 16\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 0\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 1\nOpDecorate %19 DescriptorSet 0\nOpDecorate %19 Binding 2\nOpDecorate %39 BuiltIn VertexIndex\nOpDecorate %42 BuiltIn Position\nOpDecorate %44 Location 0\nOpDecorate %99 BuiltIn FragCoord\nOpDecorate %102 Location 0\nOpDecorate %105 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 4\n%5 = OpTypeVector %4 3\n%6 = OpTypeStruct %3 %5\n%7 = OpTypeMatrix %3 4\n%8 = OpTypeStruct %7 %7\n%9 = OpTypeInt 32 0\n%10 = OpTypeInt 32 1\n%11 = OpTypeMatrix %5 3\n%12 = OpTypeImage %4 Cube 0 0 0 1 Unknown\n%13 = OpTypeSampler\n%15 = OpTypeStruct %8\n%16 = OpTypePointer Uniform %15\n%14 = OpVariable  %16  Uniform\n%18 = OpTypePointer UniformConstant %12\n%17 = OpVariable  %18  UniformConstant\n%20 = OpTypePointer UniformConstant %13\n%19 = OpVariable  %20  UniformConstant\n%22 = OpTypeFunction %10 %10 %10\n%26 = OpTypeBool\n%27 = OpConstant  %10  0\n%29 = OpConstant  %10  -2147483648\n%30 = OpConstant  %10  -1\n%35 = OpConstant  %10  1\n%40 = OpTypePointer Input %9\n%39 = OpVariable  %40  Input\n%43 = OpTypePointer Output %3\n%42 = OpVariable  %43  Output\n%45 = OpTypePointer Output %5\n%44 = OpVariable  %45  Output\n%47 = OpTypeFunction %2\n%48 = OpTypePointer Uniform %8\n%49 = OpConstant  %9  0\n%51 = OpConstant  %10  2\n%52 = OpConstant  %4  4\n%53 = OpConstant  %4  1\n%54 = OpConstant  %4  0\n%56 = OpTypePointer Function %10\n%57 = OpConstantNull  %10\n%59 = OpConstantNull  %10\n%74 = OpTypePointer Uniform %7\n%75 = OpTypePointer Uniform %3\n%76 = OpConstant  %9  1\n%83 = OpConstant  %9  2\n%100 = OpTypePointer Input %3\n%99 = OpVariable  %100  Input\n%103 = OpTypePointer Input %5\n%102 = OpVariable  %103  Input\n%105 = OpVariable  %43  Output\n%111 = OpTypeSampledImage %12\n%21 = OpFunction  %10  None %22\n%23 = OpFunctionParameter  %10\n%24 = OpFunctionParameter  %10\n%25 = OpLabel\n%28 = OpIEqual  %26  %24 %27\n%31 = OpIEqual  %26  %23 %29\n%32 = OpIEqual  %26  %24 %30\n%33 = OpLogicalAnd  %26  %31 %32\n%34 = OpLogicalOr  %26  %28 %33\n%36 = OpSelect  %10  %34 %35 %24\n%37 = OpSDiv  %10  %23 %36\nOpReturnValue %37\nOpFunctionEnd\n%46 = OpFunction  %2  None %47\n%38 = OpLabel\n%55 = OpVariable  %56  Function %57\n%58 = OpVariable  %56  Function %59\n%41 = OpLoad  %9  %39\n%50 = OpAccessChain  %48  %14 %49\nOpBranch %60\n%60 = OpLabel\n%61 = OpBitcast  %10  %41\n%62 = OpFunctionCall  %10  %21 %61 %51\nOpStore %55 %62\n%63 = OpBitcast  %10  %41\n%64 = OpBitwiseAnd  %10  %63 %35\nOpStore %58 %64\n%65 = OpLoad  %10  %55\n%66 = OpConvertSToF  %4  %65\n%67 = OpFMul  %4  %66 %52\n%68 = OpFSub  %4  %67 %53\n%69 = OpLoad  %10  %58\n%70 = OpConvertSToF  %4  %69\n%71 = OpFMul  %4  %70 %52\n%72 = OpFSub  %4  %71 %53\n%73 = OpCompositeConstruct  %3  %68 %72 %54 %53\n%77 = OpAccessChain  %75  %50 %76 %49\n%78 = OpLoad  %3  %77\n%79 = OpVectorShuffle  %5  %78 %78 0 1 2\n%80 = OpAccessChain  %75  %50 %76 %76\n%81 = OpLoad  %3  %80\n%82 = OpVectorShuffle  %5  %81 %81 0 1 2\n%84 = OpAccessChain  %75  %50 %76 %83\n%85 = OpLoad  %3  %84\n%86 = OpVectorShuffle  %5  %85 %85 0 1 2\n%87 = OpCompositeConstruct  %11  %79 %82 %86\n%88 = OpTranspose  %11  %87\n%89 = OpAccessChain  %74  %50 %49\n%90 = OpLoad  %7  %89\n%91 = OpMatrixTimesVector  %3  %90 %73\n%92 = OpVectorShuffle  %5  %91 %91 0 1 2\n%93 = OpMatrixTimesVector  %5  %88 %92\n%94 = OpCompositeConstruct  %6  %73 %93\n%95 = OpCompositeExtract  %3  %94 0\nOpStore %42 %95\n%96 = OpCompositeExtract  %5  %94 1\nOpStore %44 %96\nOpReturn\nOpFunctionEnd\n%106 = OpFunction  %2  None %47\n%97 = OpLabel\n%101 = OpLoad  %3  %99\n%104 = OpLoad  %5  %102\n%98 = OpCompositeConstruct  %6  %101 %104\n%107 = OpLoad  %12  %17\n%108 = OpLoad  %13  %19\nOpBranch %109\n%109 = OpLabel\n%110 = OpCompositeExtract  %5  %98 1\n%112 = OpSampledImage  %111  %107 %108\n%113 = OpImageSampleImplicitLod  %3  %112 %110\nOpStore %105 %113\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-sprite.spvasm",
    "content": "; SPIR-V\n; Version: 1.4\n; Generator: rspirv\n; Bound: 26\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %18 \"main\" %13 %16 %8 %10\nOpExecutionMode %18 OriginUpperLeft\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 0\nOpDecorate %10 DescriptorSet 0\nOpDecorate %10 Binding 1\nOpDecorate %13 Location 0\nOpDecorate %16 Location 0\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeImage %4 2D 0 0 0 1 Unknown\n%5 = OpTypeSampler\n%6 = OpTypeVector %4 2\n%7 = OpTypeVector %4 4\n%9 = OpTypePointer UniformConstant %3\n%8 = OpVariable  %9  UniformConstant\n%11 = OpTypePointer UniformConstant %5\n%10 = OpVariable  %11  UniformConstant\n%14 = OpTypePointer Input %6\n%13 = OpVariable  %14  Input\n%17 = OpTypePointer Output %7\n%16 = OpVariable  %17  Output\n%19 = OpTypeFunction %2\n%23 = OpTypeSampledImage %3\n%18 = OpFunction  %2  None %19\n%12 = OpLabel\n%15 = OpLoad  %6  %13\n%20 = OpLoad  %3  %8\n%21 = OpLoad  %5  %10\nOpBranch %22\n%22 = OpLabel\n%24 = OpSampledImage  %23  %20 %21\n%25 = OpImageSampleImplicitLod  %7  %24 %15\nOpStore %16 %25\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-standard.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 42\nOpCapability Shader\nOpCapability DerivativeControl\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %17 \"derivatives\" %12 %15\nOpExecutionMode %17 OriginUpperLeft\nOpDecorate %12 BuiltIn FragCoord\nOpDecorate %15 Location 0\n%2 = OpTypeVoid\n%3 = OpTypeBool\n%5 = OpTypeFloat 32\n%4 = OpTypeVector %5 4\n%8 = OpTypeFunction %3\n%9 = OpConstantTrue  %3\n%13 = OpTypePointer Input %4\n%12 = OpVariable  %13  Input\n%16 = OpTypePointer Output %4\n%15 = OpVariable  %16  Output\n%18 = OpTypeFunction %2\n%20 = OpTypePointer Function %4\n%21 = OpConstantNull  %4\n%23 = OpConstantNull  %4\n%25 = OpConstantNull  %4\n%7 = OpFunction  %3  None %8\n%6 = OpLabel\nOpBranch %10\n%10 = OpLabel\nOpReturnValue %9\nOpFunctionEnd\n%17 = OpFunction  %2  None %18\n%11 = OpLabel\n%19 = OpVariable  %20  Function %21\n%22 = OpVariable  %20  Function %23\n%24 = OpVariable  %20  Function %25\n%14 = OpLoad  %4  %12\nOpBranch %26\n%26 = OpLabel\n%27 = OpDPdxCoarse  %4  %14\nOpStore %19 %27\n%28 = OpDPdyCoarse  %4  %14\nOpStore %22 %28\n%29 = OpFwidthCoarse  %4  %14\nOpStore %24 %29\n%30 = OpDPdxFine  %4  %14\nOpStore %19 %30\n%31 = OpDPdyFine  %4  %14\nOpStore %22 %31\n%32 = OpFwidthFine  %4  %14\nOpStore %24 %32\n%33 = OpDPdx  %4  %14\nOpStore %19 %33\n%34 = OpDPdy  %4  %14\nOpStore %22 %34\n%35 = OpFwidth  %4  %14\nOpStore %24 %35\n%36 = OpFunctionCall  %3  %7\n%37 = OpLoad  %4  %19\n%38 = OpLoad  %4  %22\n%39 = OpFAdd  %4  %37 %38\n%40 = OpLoad  %4  %24\n%41 = OpFMul  %4  %39 %40\nOpStore %15 %41\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-storage-textures.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 39\nOpCapability Shader\nOpCapability StorageImageExtendedFormats\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %17 \"csLoad\"\nOpEntryPoint GLCompute %32 \"csStore\"\nOpExecutionMode %17 LocalSize 1 1 1\nOpExecutionMode %32 LocalSize 1 1 1\nOpDecorate %7 NonWritable\nOpDecorate %7 DescriptorSet 0\nOpDecorate %7 Binding 0\nOpDecorate %9 NonWritable\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 1\nOpDecorate %11 NonWritable\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 2\nOpDecorate %13 NonReadable\nOpDecorate %13 DescriptorSet 1\nOpDecorate %13 Binding 0\nOpDecorate %14 NonReadable\nOpDecorate %14 DescriptorSet 1\nOpDecorate %14 Binding 1\nOpDecorate %15 NonReadable\nOpDecorate %15 DescriptorSet 1\nOpDecorate %15 Binding 2\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeImage %4 2D 0 0 0 2 R32f\n%5 = OpTypeImage %4 2D 0 0 0 2 Rg32f\n%6 = OpTypeImage %4 2D 0 0 0 2 Rgba32f\n%8 = OpTypePointer UniformConstant %3\n%7 = OpVariable  %8  UniformConstant\n%10 = OpTypePointer UniformConstant %5\n%9 = OpVariable  %10  UniformConstant\n%12 = OpTypePointer UniformConstant %6\n%11 = OpVariable  %12  UniformConstant\n%13 = OpVariable  %8  UniformConstant\n%14 = OpVariable  %10  UniformConstant\n%15 = OpVariable  %12  UniformConstant\n%18 = OpTypeFunction %2\n%22 = OpTypeInt 32 0\n%23 = OpConstant  %22  0\n%24 = OpTypeVector %22 2\n%25 = OpConstantComposite  %24  %23 %23\n%27 = OpTypeVector %4 4\n%36 = OpConstant  %4  0\n%37 = OpConstantComposite  %27  %36 %36 %36 %36\n%17 = OpFunction  %2  None %18\n%16 = OpLabel\n%19 = OpLoad  %3  %7\n%20 = OpLoad  %5  %9\n%21 = OpLoad  %6  %11\nOpBranch %26\n%26 = OpLabel\n%28 = OpImageRead  %27  %19 %25\n%29 = OpImageRead  %27  %20 %25\n%30 = OpImageRead  %27  %21 %25\nOpReturn\nOpFunctionEnd\n%32 = OpFunction  %2  None %18\n%31 = OpLabel\n%33 = OpLoad  %3  %13\n%34 = OpLoad  %5  %14\n%35 = OpLoad  %6  %15\nOpBranch %38\n%38 = OpLabel\nOpImageWrite %33 %25 %37\nOpImageWrite %34 %25 %37\nOpImageWrite %35 %25 %37\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-struct-layout.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 92\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %30 \"no_padding_frag\" %22 %25 %28\nOpEntryPoint Vertex %42 \"no_padding_vert\" %37 %39 %41\nOpEntryPoint GLCompute %45 \"no_padding_comp\"\nOpEntryPoint Fragment %67 \"needs_padding_frag\" %60 %62 %64 %66\nOpEntryPoint Vertex %78 \"needs_padding_vert\" %71 %73 %75 %77\nOpEntryPoint GLCompute %81 \"needs_padding_comp\"\nOpExecutionMode %30 OriginUpperLeft\nOpExecutionMode %45 LocalSize 16 1 1\nOpExecutionMode %67 OriginUpperLeft\nOpExecutionMode %81 LocalSize 16 1 1\nOpMemberDecorate %5 0 Offset 0\nOpMemberDecorate %5 1 Offset 12\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 16\nOpMemberDecorate %7 2 Offset 28\nOpDecorate %8 DescriptorSet 0\nOpDecorate %8 Binding 0\nOpDecorate %9 Block\nOpMemberDecorate %9 0 Offset 0\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 1\nOpDecorate %12 Block\nOpMemberDecorate %12 0 Offset 0\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 2\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\nOpDecorate %17 DescriptorSet 0\nOpDecorate %17 Binding 3\nOpDecorate %18 Block\nOpMemberDecorate %18 0 Offset 0\nOpDecorate %22 Location 0\nOpDecorate %25 Location 1\nOpDecorate %28 Location 0\nOpDecorate %37 Location 0\nOpDecorate %39 Location 1\nOpDecorate %41 BuiltIn Position\nOpDecorate %60 Location 0\nOpDecorate %62 Location 1\nOpDecorate %64 Location 2\nOpDecorate %66 Location 0\nOpDecorate %71 Location 0\nOpDecorate %73 Location 1\nOpDecorate %75 Location 2\nOpDecorate %77 BuiltIn Position\n%2 = OpTypeVoid\n%4 = OpTypeFloat 32\n%3 = OpTypeVector %4 3\n%5 = OpTypeStruct %3 %4\n%6 = OpTypeVector %4 4\n%7 = OpTypeStruct %4 %3 %4\n%9 = OpTypeStruct %5\n%10 = OpTypePointer Uniform %9\n%8 = OpVariable  %10  Uniform\n%12 = OpTypeStruct %5\n%13 = OpTypePointer StorageBuffer %12\n%11 = OpVariable  %13  StorageBuffer\n%15 = OpTypeStruct %7\n%16 = OpTypePointer Uniform %15\n%14 = OpVariable  %16  Uniform\n%18 = OpTypeStruct %7\n%19 = OpTypePointer StorageBuffer %18\n%17 = OpVariable  %19  StorageBuffer\n%23 = OpTypePointer Input %3\n%22 = OpVariable  %23  Input\n%26 = OpTypePointer Input %4\n%25 = OpVariable  %26  Input\n%29 = OpTypePointer Output %6\n%28 = OpVariable  %29  Output\n%31 = OpTypeFunction %2\n%32 = OpConstant  %4  0\n%33 = OpConstantComposite  %6  %32 %32 %32 %32\n%37 = OpVariable  %23  Input\n%39 = OpVariable  %26  Input\n%41 = OpVariable  %29  Output\n%46 = OpTypePointer Uniform %5\n%48 = OpTypeInt 32 0\n%47 = OpConstant  %48  0\n%50 = OpTypePointer StorageBuffer %5\n%53 = OpTypePointer Function %5\n%54 = OpConstantNull  %5\n%60 = OpVariable  %26  Input\n%62 = OpVariable  %23  Input\n%64 = OpVariable  %26  Input\n%66 = OpVariable  %29  Output\n%71 = OpVariable  %26  Input\n%73 = OpVariable  %23  Input\n%75 = OpVariable  %26  Input\n%77 = OpVariable  %29  Output\n%82 = OpTypePointer Uniform %7\n%84 = OpTypePointer StorageBuffer %7\n%87 = OpTypePointer Function %7\n%88 = OpConstantNull  %7\n%30 = OpFunction  %2  None %31\n%20 = OpLabel\n%24 = OpLoad  %3  %22\n%27 = OpLoad  %4  %25\n%21 = OpCompositeConstruct  %5  %24 %27\nOpBranch %34\n%34 = OpLabel\nOpStore %28 %33\nOpReturn\nOpFunctionEnd\n%42 = OpFunction  %2  None %31\n%35 = OpLabel\n%38 = OpLoad  %3  %37\n%40 = OpLoad  %4  %39\n%36 = OpCompositeConstruct  %5  %38 %40\nOpBranch %43\n%43 = OpLabel\nOpStore %41 %33\nOpReturn\nOpFunctionEnd\n%45 = OpFunction  %2  None %31\n%44 = OpLabel\n%52 = OpVariable  %53  Function %54\n%49 = OpAccessChain  %46  %8 %47\n%51 = OpAccessChain  %50  %11 %47\nOpBranch %55\n%55 = OpLabel\n%56 = OpLoad  %5  %49\nOpStore %52 %56\n%57 = OpLoad  %5  %51\nOpStore %52 %57\nOpReturn\nOpFunctionEnd\n%67 = OpFunction  %2  None %31\n%58 = OpLabel\n%61 = OpLoad  %4  %60\n%63 = OpLoad  %3  %62\n%65 = OpLoad  %4  %64\n%59 = OpCompositeConstruct  %7  %61 %63 %65\nOpBranch %68\n%68 = OpLabel\nOpStore %66 %33\nOpReturn\nOpFunctionEnd\n%78 = OpFunction  %2  None %31\n%69 = OpLabel\n%72 = OpLoad  %4  %71\n%74 = OpLoad  %3  %73\n%76 = OpLoad  %4  %75\n%70 = OpCompositeConstruct  %7  %72 %74 %76\nOpBranch %79\n%79 = OpLabel\nOpStore %77 %33\nOpReturn\nOpFunctionEnd\n%81 = OpFunction  %2  None %31\n%80 = OpLabel\n%86 = OpVariable  %87  Function %88\n%83 = OpAccessChain  %82  %14 %47\n%85 = OpAccessChain  %84  %17 %47\nOpBranch %89\n%89 = OpLabel\n%90 = OpLoad  %7  %83\nOpStore %86 %90\n%91 = OpLoad  %7  %85\nOpStore %86 %91\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-subgroup-barrier.spvasm",
    "content": "; SPIR-V\n; Version: 1.3\n; Generator: rspirv\n; Bound: 10\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %4 \"main\"\nOpExecutionMode %4 LocalSize 1 1 1\n%2 = OpTypeVoid\n%5 = OpTypeFunction %2\n%8 = OpTypeInt 32 0\n%7 = OpConstant  %8  3\n%9 = OpConstant  %8  136\n%4 = OpFunction  %2  None %5\n%3 = OpLabel\nOpBranch %6\n%6 = OpLabel\nOpControlBarrier %7 %7 %9\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-subgroup-operations.spvasm",
    "content": "; SPIR-V\n; Version: 1.3\n; Generator: rspirv\n; Bound: 61\nOpCapability Shader\nOpCapability GroupNonUniform\nOpCapability GroupNonUniformBallot\nOpCapability GroupNonUniformVote\nOpCapability GroupNonUniformArithmetic\nOpCapability GroupNonUniformShuffle\nOpCapability GroupNonUniformShuffleRelative\nOpCapability GroupNonUniformQuad\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %17 \"main\" %8 %11 %13 %15\nOpExecutionMode %17 LocalSize 1 1 1\nOpMemberDecorate %4 0 Offset 0\nOpMemberDecorate %4 1 Offset 4\nOpDecorate %8 BuiltIn NumSubgroups\nOpDecorate %11 BuiltIn SubgroupSize\nOpDecorate %13 BuiltIn SubgroupId\nOpDecorate %15 BuiltIn SubgroupLocalInvocationId\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeStruct %3 %3\n%5 = OpTypeBool\n%9 = OpTypePointer Input %3\n%8 = OpVariable  %9  Input\n%11 = OpVariable  %9  Input\n%13 = OpVariable  %9  Input\n%15 = OpVariable  %9  Input\n%18 = OpTypeFunction %2\n%19 = OpConstant  %3  1\n%20 = OpConstant  %3  0\n%21 = OpConstant  %3  4\n%25 = OpTypeVector %3 4\n%26 = OpConstant  %3  3\n%28 = OpConstantTrue  %5\n%60 = OpConstant  %3  2\n%17 = OpFunction  %2  None %18\n%6 = OpLabel\n%10 = OpLoad  %3  %8\n%12 = OpLoad  %3  %11\n%7 = OpCompositeConstruct  %4  %10 %12\n%14 = OpLoad  %3  %13\n%16 = OpLoad  %3  %15\nOpBranch %22\n%22 = OpLabel\n%23 = OpBitwiseAnd  %3  %16 %19\n%24 = OpIEqual  %5  %23 %19\n%27 = OpGroupNonUniformBallot  %25  %26 %24\n%29 = OpGroupNonUniformBallot  %25  %26 %28\n%30 = OpINotEqual  %5  %16 %20\n%31 = OpGroupNonUniformAll  %5  %26 %30\n%32 = OpIEqual  %5  %16 %20\n%33 = OpGroupNonUniformAny  %5  %26 %32\n%34 = OpGroupNonUniformIAdd  %3  %26 Reduce %16\n%35 = OpGroupNonUniformIMul  %3  %26 Reduce %16\n%36 = OpGroupNonUniformUMin  %3  %26 Reduce %16\n%37 = OpGroupNonUniformUMax  %3  %26 Reduce %16\n%38 = OpGroupNonUniformBitwiseAnd  %3  %26 Reduce %16\n%39 = OpGroupNonUniformBitwiseOr  %3  %26 Reduce %16\n%40 = OpGroupNonUniformBitwiseXor  %3  %26 Reduce %16\n%41 = OpGroupNonUniformIAdd  %3  %26 ExclusiveScan %16\n%42 = OpGroupNonUniformIMul  %3  %26 ExclusiveScan %16\n%43 = OpGroupNonUniformIAdd  %3  %26 InclusiveScan %16\n%44 = OpGroupNonUniformIMul  %3  %26 InclusiveScan %16\n%45 = OpGroupNonUniformBroadcastFirst  %3  %26 %16\n%46 = OpGroupNonUniformShuffle  %3  %26 %16 %21\n%47 = OpCompositeExtract  %3  %7 1\n%48 = OpISub  %3  %47 %19\n%49 = OpISub  %3  %48 %16\n%50 = OpGroupNonUniformShuffle  %3  %26 %16 %49\n%51 = OpGroupNonUniformShuffleDown  %3  %26 %16 %19\n%52 = OpGroupNonUniformShuffleUp  %3  %26 %16 %19\n%53 = OpCompositeExtract  %3  %7 1\n%54 = OpISub  %3  %53 %19\n%55 = OpGroupNonUniformShuffleXor  %3  %26 %16 %54\n%56 = OpGroupNonUniformQuadBroadcast  %3  %26 %16 %21\n%57 = OpGroupNonUniformQuadSwap  %3  %26 %16 %20\n%58 = OpGroupNonUniformQuadSwap  %3  %26 %16 %19\n%59 = OpGroupNonUniformQuadSwap  %3  %26 %16 %60\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-texture-arg.spvasm",
    "content": "; SPIR-V\n; Version: 1.0\n; Generator: rspirv\n; Bound: 35\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint Fragment %29 \"main\" %27\nOpExecutionMode %29 OriginUpperLeft\n%3 = OpString \"texture-arg.wgsl\"\nOpSource Unknown 0 %3 \"@group(0) @binding(0)\nvar Texture: texture_2d<f32>;\n@group(0) @binding(1)\nvar Sampler: sampler;\n\nfn test(Passed_Texture: texture_2d<f32>, Passed_Sampler: sampler) -> vec4<f32> {\n    return textureSample(Passed_Texture, Passed_Sampler, vec2<f32>(0.0, 0.0));\n}\n\n@fragment\nfn main() -> @location(0) vec4<f32> {\n    return test(Texture, Sampler);\n}\n\"\nOpName %9 \"Texture\"\nOpName %11 \"Sampler\"\nOpName %14 \"Passed_Texture\"\nOpName %16 \"Passed_Sampler\"\nOpName %18 \"test\"\nOpName %29 \"main\"\nOpDecorate %9 DescriptorSet 0\nOpDecorate %9 Binding 0\nOpDecorate %11 DescriptorSet 0\nOpDecorate %11 Binding 1\nOpDecorate %27 Location 0\n%2 = OpTypeVoid\n%5 = OpTypeFloat 32\n%4 = OpTypeImage %5 2D 0 0 0 1 Unknown\n%6 = OpTypeSampler\n%7 = OpTypeVector %5 4\n%8 = OpTypeVector %5 2\n%10 = OpTypePointer UniformConstant %4\n%9 = OpVariable  %10  UniformConstant\n%12 = OpTypePointer UniformConstant %6\n%11 = OpVariable  %12  UniformConstant\n%19 = OpTypeFunction %7 %10 %12\n%20 = OpConstant  %5  0\n%21 = OpConstantComposite  %8  %20 %20\n%23 = OpTypeSampledImage %4\n%28 = OpTypePointer Output %7\n%27 = OpVariable  %28  Output\n%30 = OpTypeFunction %2\n%18 = OpFunction  %7  None %19\n%14 = OpFunctionParameter  %10\n%16 = OpFunctionParameter  %12\n%13 = OpLabel\n%15 = OpLoad  %4  %14\n%17 = OpLoad  %6  %16\nOpBranch %22\n%22 = OpLabel\nOpLine %3 7 12\n%24 = OpSampledImage  %23  %15 %17\n%25 = OpImageSampleImplicitLod  %7  %24 %21\nOpReturnValue %25\nOpFunctionEnd\n%29 = OpFunction  %2  None %30\n%26 = OpLabel\n%31 = OpLoad  %4  %9\n%32 = OpLoad  %6  %11\nOpBranch %33\n%33 = OpLabel\nOpLine %3 12 12\n%34 = OpFunctionCall  %7  %18 %9 %11\nOpStore %27 %34\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-type-inference.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 57\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %18 \"main\"\nOpExecutionMode %18 LocalSize 1 1 1\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeFloat 32\n%5 = OpTypeInt 32 1\n%6 = OpTypeVector %5 4\n%8 = OpTypeVector %4 2\n%7 = OpTypeMatrix %8 2\n%9 = OpConstant  %3  1\n%10 = OpConstant  %4  1\n%11 = OpConstantNull  %6\n%12 = OpConstant  %5  1\n%13 = OpConstantComposite  %6  %12 %12 %12 %12\n%14 = OpConstant  %4  0\n%15 = OpConstantComposite  %8  %14 %14\n%16 = OpConstantComposite  %7  %15 %15\n%19 = OpTypeFunction %2\n%20 = OpConstantComposite  %8  %10 %10\n%21 = OpConstantComposite  %7  %20 %20\n%23 = OpTypePointer Function %5\n%25 = OpTypePointer Function %4\n%27 = OpTypePointer Function %7\n%30 = OpTypePointer Function %3\n%34 = OpTypePointer Function %6\n%39 = OpConstantNull  %5\n%41 = OpConstantNull  %3\n%43 = OpConstantNull  %4\n%45 = OpConstantNull  %4\n%47 = OpConstantNull  %6\n%18 = OpFunction  %2  None %19\n%17 = OpLabel\n%54 = OpVariable  %27  Function %16\n%51 = OpVariable  %25  Function %10\n%48 = OpVariable  %23  Function %12\n%42 = OpVariable  %25  Function %43\n%37 = OpVariable  %27  Function %21\n%33 = OpVariable  %34  Function %11\n%29 = OpVariable  %30  Function %9\n%24 = OpVariable  %25  Function %10\n%53 = OpVariable  %34  Function %13\n%50 = OpVariable  %25  Function %10\n%46 = OpVariable  %34  Function %47\n%40 = OpVariable  %30  Function %41\n%36 = OpVariable  %27  Function %16\n%32 = OpVariable  %25  Function %10\n%28 = OpVariable  %23  Function %12\n%22 = OpVariable  %23  Function %12\n%55 = OpVariable  %27  Function %21\n%52 = OpVariable  %34  Function %11\n%49 = OpVariable  %30  Function %9\n%44 = OpVariable  %25  Function %45\n%38 = OpVariable  %23  Function %39\n%35 = OpVariable  %34  Function %13\n%31 = OpVariable  %25  Function %10\n%26 = OpVariable  %27  Function %21\nOpBranch %56\n%56 = OpLabel\nOpStore %38 %12\nOpStore %40 %9\nOpStore %42 %10\nOpStore %44 %10\nOpStore %46 %11\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-workgroup-uniform-load-atomic.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 88\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %22 \"test_atomic_workgroup_uniform_load\" %17 %20 %42\nOpExecutionMode %22 LocalSize 64 1 1\nOpDecorate %5 ArrayStride 4\nOpMemberDecorate %7 0 Offset 0\nOpMemberDecorate %7 1 Offset 4\nOpDecorate %17 BuiltIn WorkgroupId\nOpDecorate %20 BuiltIn LocalInvocationId\nOpDecorate %42 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 32 1\n%6 = OpConstant  %3  2\n%5 = OpTypeArray %4 %6\n%7 = OpTypeStruct %3 %5\n%8 = OpTypeVector %3 3\n%9 = OpTypeBool\n%11 = OpTypePointer Workgroup %3\n%10 = OpVariable  %11  Workgroup\n%13 = OpTypePointer Workgroup %4\n%12 = OpVariable  %13  Workgroup\n%15 = OpTypePointer Workgroup %7\n%14 = OpVariable  %15  Workgroup\n%18 = OpTypePointer Input %8\n%17 = OpVariable  %18  Input\n%20 = OpVariable  %18  Input\n%23 = OpTypeFunction %2\n%24 = OpConstant  %3  32768\n%25 = OpConstant  %3  64\n%26 = OpConstant  %4  1\n%27 = OpConstant  %3  1\n%28 = OpConstant  %3  0\n%29 = OpConstantFalse  %9\n%30 = OpConstant  %4  0\n%32 = OpTypePointer Function %9\n%33 = OpConstantNull  %9\n%35 = OpConstantNull  %9\n%37 = OpConstantNull  %9\n%39 = OpConstantNull  %3\n%40 = OpConstantNull  %4\n%41 = OpConstantNull  %7\n%43 = OpTypePointer Input %3\n%42 = OpVariable  %43  Input\n%48 = OpConstant  %3  264\n%57 = OpConstant  %4  2\n%60 = OpTypePointer Workgroup %5\n%22 = OpFunction  %2  None %23\n%16 = OpLabel\n%31 = OpVariable  %32  Function %33\n%34 = OpVariable  %32  Function %35\n%36 = OpVariable  %32  Function %37\n%19 = OpLoad  %8  %17\n%21 = OpLoad  %8  %20\nOpBranch %38\n%38 = OpLabel\n%44 = OpLoad  %3  %42\n%45 = OpIEqual  %9  %44 %28\nOpSelectionMerge %46 None\nOpBranchConditional %45 %47 %46\n%47 = OpLabel\nOpStore %10 %39\nOpStore %12 %40\nOpStore %14 %41\nOpBranch %46\n%46 = OpLabel\nOpControlBarrier %6 %6 %48\nOpBranch %49\n%49 = OpLabel\n%50 = OpCompositeExtract  %3  %19 0\n%51 = OpCompositeExtract  %3  %19 1\n%52 = OpIMul  %3  %51 %24\n%53 = OpIAdd  %3  %50 %52\n%54 = OpUGreaterThanEqual  %9  %53 %25\n%55 = OpSelect  %3  %54 %27 %28\n%56 = OpAtomicOr  %3  %10 %57 %28 %55\n%58 = OpAtomicIAdd  %4  %12 %57 %28 %26\n%59 = OpAccessChain  %11  %14 %28\nOpAtomicStore %59 %57 %28 %27\n%62 = OpAccessChain  %13  %14 %27 %28\n%61 = OpAtomicIAdd  %4  %62 %57 %28 %26\nOpControlBarrier %6 %6 %48\nOpControlBarrier %6 %6 %48\n%63 = OpAtomicLoad  %3  %10 %57 %28\nOpControlBarrier %6 %6 %48\nOpControlBarrier %6 %6 %48\n%64 = OpAtomicLoad  %4  %12 %57 %28\nOpControlBarrier %6 %6 %48\nOpControlBarrier %6 %6 %48\n%65 = OpAccessChain  %11  %14 %28\n%66 = OpAtomicLoad  %3  %65 %57 %28\nOpControlBarrier %6 %6 %48\nOpControlBarrier %6 %6 %48\n%67 = OpAccessChain  %13  %14 %27 %28\n%68 = OpAtomicLoad  %4  %67 %57 %28\nOpControlBarrier %6 %6 %48\n%69 = OpIEqual  %9  %63 %28\nOpSelectionMerge %70 None\nOpBranchConditional %69 %71 %72\n%71 = OpLabel\n%73 = OpSGreaterThan  %9  %64 %30\nOpStore %31 %73\nOpBranch %70\n%72 = OpLabel\nOpStore %31 %29\nOpBranch %70\n%70 = OpLabel\n%74 = OpLoad  %9  %31\nOpSelectionMerge %75 None\nOpBranchConditional %74 %76 %77\n%76 = OpLabel\n%78 = OpUGreaterThan  %9  %66 %28\nOpStore %34 %78\nOpBranch %75\n%77 = OpLabel\nOpStore %34 %29\nOpBranch %75\n%75 = OpLabel\n%79 = OpLoad  %9  %34\nOpSelectionMerge %80 None\nOpBranchConditional %79 %81 %82\n%81 = OpLabel\n%83 = OpSGreaterThan  %9  %68 %30\nOpStore %36 %83\nOpBranch %80\n%82 = OpLabel\nOpStore %36 %29\nOpBranch %80\n%80 = OpLabel\n%84 = OpLoad  %9  %36\nOpSelectionMerge %85 None\nOpBranchConditional %84 %86 %87\n%86 = OpLabel\nOpReturn\n%87 = OpLabel\nOpReturn\n%85 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-workgroup-uniform-load.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 38\nOpCapability Shader\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %14 \"test_workgroupUniformLoad\" %11 %19\nOpExecutionMode %14 LocalSize 4 1 1\nOpDecorate %5 ArrayStride 4\nOpDecorate %11 BuiltIn WorkgroupId\nOpDecorate %19 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%3 = OpTypeInt 32 0\n%4 = OpTypeInt 32 1\n%6 = OpConstant  %3  128\n%5 = OpTypeArray %4 %6\n%7 = OpTypeVector %3 3\n%9 = OpTypePointer Workgroup %5\n%8 = OpVariable  %9  Workgroup\n%12 = OpTypePointer Input %7\n%11 = OpVariable  %12  Input\n%15 = OpTypeFunction %2\n%16 = OpConstant  %4  10\n%18 = OpConstantNull  %5\n%20 = OpTypePointer Input %3\n%19 = OpVariable  %20  Input\n%22 = OpConstant  %3  0\n%24 = OpTypeBool\n%27 = OpConstant  %3  2\n%28 = OpConstant  %3  264\n%31 = OpTypePointer Workgroup %4\n%14 = OpFunction  %2  None %15\n%10 = OpLabel\n%13 = OpLoad  %7  %11\nOpBranch %17\n%17 = OpLabel\n%21 = OpLoad  %3  %19\n%23 = OpIEqual  %24  %21 %22\nOpSelectionMerge %25 None\nOpBranchConditional %23 %26 %25\n%26 = OpLabel\nOpStore %8 %18\nOpBranch %25\n%25 = OpLabel\nOpControlBarrier %27 %27 %28\nOpBranch %29\n%29 = OpLabel\n%30 = OpCompositeExtract  %3  %13 0\nOpControlBarrier %27 %27 %28\n%32 = OpAccessChain  %31  %8 %30\n%33 = OpLoad  %4  %32\nOpControlBarrier %27 %27 %28\n%34 = OpSGreaterThan  %24  %33 %16\nOpSelectionMerge %35 None\nOpBranchConditional %34 %36 %37\n%36 = OpLabel\nOpControlBarrier %27 %27 %28\nOpReturn\n%37 = OpLabel\nOpReturn\n%35 = OpLabel\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/spv/wgsl-workgroup-var-init.spvasm",
    "content": "; SPIR-V\n; Version: 1.1\n; Generator: rspirv\n; Bound: 38\nOpCapability Shader\nOpExtension \"SPV_KHR_storage_buffer_storage_class\"\n%1 = OpExtInstImport \"GLSL.std.450\"\nOpMemoryModel Logical GLSL450\nOpEntryPoint GLCompute %18 \"main\" %25\nOpExecutionMode %18 LocalSize 1 1 1\n%3 = OpString \"workgroup-var-init.wgsl\"\nOpSource Unknown 0 %3 \"struct WStruct {\n    arr: array<u32, 512>,\n    atom: atomic<i32>,\n    atom_arr: array<array<atomic<i32>, 8>, 8>,\n}\n\nvar<workgroup> w_mem: WStruct;\n\n@group(0) @binding(0)\nvar<storage, read_write> output: array<u32, 512>;\n\n@compute @workgroup_size(1)\nfn main() {\n    output = w_mem.arr;\n}\"\nOpMemberName %11 0 \"arr\"\nOpMemberName %11 1 \"atom\"\nOpMemberName %11 2 \"atom_arr\"\nOpName %11 \"WStruct\"\nOpName %12 \"w_mem\"\nOpName %14 \"output\"\nOpName %18 \"main\"\nOpDecorate %5 ArrayStride 4\nOpDecorate %8 ArrayStride 4\nOpDecorate %10 ArrayStride 32\nOpMemberDecorate %11 0 Offset 0\nOpMemberDecorate %11 1 Offset 2048\nOpMemberDecorate %11 2 Offset 2052\nOpDecorate %14 DescriptorSet 0\nOpDecorate %14 Binding 0\nOpDecorate %15 Block\nOpMemberDecorate %15 0 Offset 0\nOpDecorate %25 BuiltIn LocalInvocationIndex\n%2 = OpTypeVoid\n%4 = OpTypeInt 32 0\n%6 = OpConstant  %4  512\n%5 = OpTypeArray %4 %6\n%7 = OpTypeInt 32 1\n%9 = OpConstant  %4  8\n%8 = OpTypeArray %7 %9\n%10 = OpTypeArray %8 %9\n%11 = OpTypeStruct %5 %7 %10\n%13 = OpTypePointer Workgroup %11\n%12 = OpVariable  %13  Workgroup\n%15 = OpTypeStruct %5\n%16 = OpTypePointer StorageBuffer %15\n%14 = OpVariable  %16  StorageBuffer\n%19 = OpTypeFunction %2\n%20 = OpTypePointer StorageBuffer %5\n%21 = OpConstant  %4  0\n%24 = OpConstantNull  %11\n%26 = OpTypePointer Input %4\n%25 = OpVariable  %26  Input\n%29 = OpTypeBool\n%32 = OpConstant  %4  2\n%33 = OpConstant  %4  264\n%35 = OpTypePointer Workgroup %5\n%18 = OpFunction  %2  None %19\n%17 = OpLabel\n%22 = OpAccessChain  %20  %14 %21\nOpBranch %23\n%23 = OpLabel\n%27 = OpLoad  %4  %25\n%28 = OpIEqual  %29  %27 %21\nOpSelectionMerge %30 None\nOpBranchConditional %28 %31 %30\n%31 = OpLabel\nOpStore %12 %24\nOpBranch %30\n%30 = OpLabel\nOpControlBarrier %32 %32 %33\nOpBranch %34\n%34 = OpLabel\nOpLine %3 14 14\n%36 = OpAccessChain  %35  %12 %21\n%37 = OpLoad  %5  %36\nOpLine %3 14 5\nOpStore %22 %37\nOpReturn\nOpFunctionEnd"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-210-bevy-2d-shader.frag.wgsl",
    "content": "struct ColorMaterial_color {\n    Color: vec4<f32>,\n}\n\nstruct FragmentOutput {\n    @location(0) o_Target: vec4<f32>,\n}\n\nvar<private> v_Uv_1: vec2<f32>;\nvar<private> o_Target: vec4<f32>;\n@group(1) @binding(0) \nvar<uniform> global: ColorMaterial_color;\n\nfn main_1() {\n    var color: vec4<f32>;\n\n    let _e3 = global.Color;\n    color = _e3;\n    let _e5 = color;\n    o_Target = _e5;\n    return;\n}\n\n@fragment \nfn main(@location(0) v_Uv: vec2<f32>) -> FragmentOutput {\n    v_Uv_1 = v_Uv;\n    main_1();\n    let _e3 = o_Target;\n    return FragmentOutput(_e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-210-bevy-2d-shader.vert.wgsl",
    "content": "struct Camera {\n    ViewProj: mat4x4<f32>,\n}\n\nstruct Transform {\n    Model: mat4x4<f32>,\n}\n\nstruct Sprite_size {\n    size: vec2<f32>,\n}\n\nstruct VertexOutput {\n    @location(0) v_Uv: vec2<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nvar<private> Vertex_Position_1: vec3<f32>;\nvar<private> Vertex_Normal_1: vec3<f32>;\nvar<private> Vertex_Uv_1: vec2<f32>;\nvar<private> v_Uv: vec2<f32>;\n@group(0) @binding(0) \nvar<uniform> global: Camera;\n@group(2) @binding(0) \nvar<uniform> global_1: Transform;\n@group(2) @binding(1) \nvar<uniform> global_2: Sprite_size;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    var position: vec3<f32>;\n\n    let _e9 = Vertex_Uv_1;\n    v_Uv = _e9;\n    let _e10 = Vertex_Position_1;\n    let _e11 = global_2.size;\n    position = (_e10 * vec3<f32>(_e11.x, _e11.y, 1f));\n    let _e19 = global.ViewProj;\n    let _e20 = global_1.Model;\n    let _e22 = position;\n    gl_Position = ((_e19 * _e20) * vec4<f32>(_e22.x, _e22.y, _e22.z, 1f));\n    return;\n}\n\n@vertex \nfn main(@location(0) Vertex_Position: vec3<f32>, @location(1) Vertex_Normal: vec3<f32>, @location(2) Vertex_Uv: vec2<f32>) -> VertexOutput {\n    Vertex_Position_1 = Vertex_Position;\n    Vertex_Normal_1 = Vertex_Normal;\n    Vertex_Uv_1 = Vertex_Uv;\n    main_1();\n    let _e7 = v_Uv;\n    let _e9 = gl_Position;\n    return VertexOutput(_e7, _e9);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-210-bevy-shader.vert.wgsl",
    "content": "struct Camera {\n    ViewProj: mat4x4<f32>,\n}\n\nstruct Transform {\n    Model: mat4x4<f32>,\n}\n\nstruct VertexOutput {\n    @location(0) v_Position: vec3<f32>,\n    @location(1) v_Normal: vec3<f32>,\n    @location(2) v_Uv: vec2<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nvar<private> Vertex_Position_1: vec3<f32>;\nvar<private> Vertex_Normal_1: vec3<f32>;\nvar<private> Vertex_Uv_1: vec2<f32>;\nvar<private> v_Position: vec3<f32>;\nvar<private> v_Normal: vec3<f32>;\nvar<private> v_Uv: vec2<f32>;\n@group(0) @binding(0) \nvar<uniform> global: Camera;\n@group(2) @binding(0) \nvar<uniform> global_1: Transform;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    let _e10 = global_1.Model;\n    let _e11 = Vertex_Normal_1;\n    v_Normal = (_e10 * vec4<f32>(_e11.x, _e11.y, _e11.z, 1f)).xyz;\n    let _e19 = global_1.Model;\n    let _e27 = Vertex_Normal_1;\n    v_Normal = (mat3x3<f32>(_e19[0].xyz, _e19[1].xyz, _e19[2].xyz) * _e27);\n    let _e29 = global_1.Model;\n    let _e30 = Vertex_Position_1;\n    v_Position = (_e29 * vec4<f32>(_e30.x, _e30.y, _e30.z, 1f)).xyz;\n    let _e38 = Vertex_Uv_1;\n    v_Uv = _e38;\n    let _e40 = global.ViewProj;\n    let _e41 = v_Position;\n    gl_Position = (_e40 * vec4<f32>(_e41.x, _e41.y, _e41.z, 1f));\n    return;\n}\n\n@vertex \nfn main(@location(0) Vertex_Position: vec3<f32>, @location(1) Vertex_Normal: vec3<f32>, @location(2) Vertex_Uv: vec2<f32>) -> VertexOutput {\n    Vertex_Position_1 = Vertex_Position;\n    Vertex_Normal_1 = Vertex_Normal;\n    Vertex_Uv_1 = Vertex_Uv;\n    main_1();\n    let _e7 = v_Position;\n    let _e9 = v_Normal;\n    let _e11 = v_Uv;\n    let _e13 = gl_Position;\n    return VertexOutput(_e7, _e9, _e11, _e13);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-246-collatz.comp.wgsl",
    "content": "struct PrimeIndices {\n    indices: array<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: PrimeIndices;\nvar<private> gl_GlobalInvocationID_1: vec3<u32>;\n\nfn collatz_iterations(n: u32) -> u32 {\n    var n_1: u32;\n    var i: u32 = 0u;\n\n    n_1 = n;\n    loop {\n        let _e4 = n_1;\n        if !((_e4 != 1u)) {\n            break;\n        }\n        {\n            let _e8 = n_1;\n            let _e9 = f32(_e8);\n            if ((_e9 - (floor((_e9 / 2f)) * 2f)) == 0f) {\n                {\n                    let _e17 = n_1;\n                    n_1 = (_e17 / 2u);\n                }\n            } else {\n                {\n                    let _e20 = n_1;\n                    n_1 = ((3u * _e20) + 1u);\n                }\n            }\n            let _e25 = i;\n            i = (_e25 + 1u);\n        }\n    }\n    let _e28 = i;\n    return _e28;\n}\n\nfn main_1() {\n    var index: u32;\n\n    let _e3 = gl_GlobalInvocationID_1;\n    index = _e3.x;\n    let _e6 = index;\n    let _e8 = index;\n    let _e10 = global.indices[_e8];\n    let _e11 = collatz_iterations(_e10);\n    global.indices[_e6] = _e11;\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(@builtin(global_invocation_id) gl_GlobalInvocationID: vec3<u32>) {\n    gl_GlobalInvocationID_1 = gl_GlobalInvocationID;\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-277-casting.frag.wgsl",
    "content": "fn main_1() {\n    var a: f32 = 1f;\n\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-280-matrix-cast.frag.wgsl",
    "content": "fn main_1() {\n    var a: mat4x4<f32> = mat4x4<f32>(vec4<f32>(1f, 0f, 0f, 0f), vec4<f32>(0f, 1f, 0f, 0f), vec4<f32>(0f, 0f, 1f, 0f), vec4<f32>(0f, 0f, 0f, 1f));\n\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-484-preprocessor-if.frag.wgsl",
    "content": "fn main_1() {\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-5246-dual-iteration.frag.wgsl",
    "content": "fn main_1() {\n    var x: i32 = 0i;\n    var y: i32;\n    var z: i32;\n\n    loop {\n        let _e2 = x;\n        if !((_e2 < 10i)) {\n            break;\n        }\n        {\n            y = 0i;\n            loop {\n                let _e11 = y;\n                if !((_e11 < 10i)) {\n                    break;\n                }\n                {\n                    z = 0i;\n                    loop {\n                        let _e20 = z;\n                        if !((_e20 < 10i)) {\n                            break;\n                        }\n                        {\n                        }\n                        continuing {\n                            let _e24 = z;\n                            z = (_e24 + 1i);\n                        }\n                    }\n                }\n                continuing {\n                    let _e15 = y;\n                    y = (_e15 + 1i);\n                }\n            }\n        }\n        continuing {\n            let _e6 = x;\n            x = (_e6 + 1i);\n        }\n    }\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-800-out-of-bounds-panic.vert.wgsl",
    "content": "struct Globals {\n    view_matrix: mat4x4<f32>,\n}\n\nstruct VertexPushConstants {\n    world_matrix: mat4x4<f32>,\n}\n\nstruct VertexOutput {\n    @location(0) frag_color: vec4<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\n@group(0) @binding(0) \nvar<uniform> global: Globals;\nvar<immediate> global_1: VertexPushConstants;\nvar<private> position_1: vec2<f32>;\nvar<private> color_1: vec4<f32>;\nvar<private> frag_color: vec4<f32>;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    let _e7 = color_1;\n    frag_color = _e7;\n    let _e9 = global.view_matrix;\n    let _e10 = global_1.world_matrix;\n    let _e12 = position_1;\n    gl_Position = ((_e9 * _e10) * vec4<f32>(_e12.x, _e12.y, 0f, 1f));\n    let _e20 = gl_Position;\n    let _e22 = gl_Position;\n    gl_Position.z = ((_e20.z + _e22.w) / 2f);\n    return;\n}\n\n@vertex \nfn main(@location(0) position: vec2<f32>, @location(1) color: vec4<f32>) -> VertexOutput {\n    position_1 = position;\n    color_1 = color;\n    main_1();\n    let _e5 = frag_color;\n    let _e7 = gl_Position;\n    return VertexOutput(_e5, _e7);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-896-push-constant.frag.wgsl",
    "content": "fn main_1() {\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-900-implicit-conversions.frag.wgsl",
    "content": "fn exact(a: i32) {\n    var a_1: i32;\n\n    a_1 = a;\n    return;\n}\n\nfn implicit(a_2: f32) {\n    var a_3: f32;\n\n    a_3 = a_2;\n    return;\n}\n\nfn implicit_dims(v: vec3<f32>) {\n    var v_1: vec3<f32>;\n\n    v_1 = v;\n    return;\n}\n\nfn main_1() {\n    exact(1i);\n    implicit(1f);\n    implicit_dims(vec3(1f));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-901-lhs-field-select.frag.wgsl",
    "content": "fn main_1() {\n    var a: vec4<f32> = vec4(1f);\n\n    a.x = 2f;\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-931-constant-emitting.frag.wgsl",
    "content": "const constant: i32 = 10i;\n\nfn function_() -> f32 {\n    return 0f;\n}\n\nfn main_1() {\n    let _e0 = function_();\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-932-for-loop-if.frag.wgsl",
    "content": "fn main_1() {\n    var i: i32 = 0i;\n\n    loop {\n        let _e2 = i;\n        if !((_e2 < 1i)) {\n            break;\n        }\n        {\n        }\n        continuing {\n            let _e6 = i;\n            i = (_e6 + 1i);\n        }\n    }\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-anonymous-entry-point-type.frag.wgsl",
    "content": "struct FragmentOutput {\n    @location(0) o_Target: vec4<f32>,\n}\n\nvar<private> o_Target: vec4<f32>;\n\nfn main_1() {\n    o_Target = vec4(0f);\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = o_Target;\n    return FragmentOutput(_e1);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-bevy-pbr.frag.wgsl",
    "content": "struct PointLight {\n    pos: vec4<f32>,\n    color: vec4<f32>,\n    lightParams: vec4<f32>,\n}\n\nstruct DirectionalLight {\n    direction: vec4<f32>,\n    color: vec4<f32>,\n}\n\nstruct CameraPosition {\n    CameraPos: vec4<f32>,\n}\n\nstruct Lights {\n    AmbientColor: vec4<f32>,\n    NumLights: vec4<u32>,\n    PointLights: array<PointLight, 10>,\n    DirectionalLights: array<DirectionalLight, 1>,\n}\n\nstruct StandardMaterial_base_color {\n    base_color: vec4<f32>,\n}\n\nstruct StandardMaterial_roughness {\n    perceptual_roughness: f32,\n}\n\nstruct StandardMaterial_metallic {\n    metallic: f32,\n}\n\nstruct StandardMaterial_reflectance {\n    reflectance: f32,\n}\n\nstruct StandardMaterial_emissive {\n    emissive: vec4<f32>,\n}\n\nstruct FragmentOutput {\n    @location(0) o_Target: vec4<f32>,\n}\n\nconst MAX_POINT_LIGHTS: i32 = 10i;\nconst MAX_DIRECTIONAL_LIGHTS: i32 = 1i;\nconst PI: f32 = 3.1415927f;\n\nvar<private> v_WorldPosition_1: vec3<f32>;\nvar<private> v_WorldNormal_1: vec3<f32>;\nvar<private> v_Uv_1: vec2<f32>;\nvar<private> v_WorldTangent_1: vec4<f32>;\nvar<private> o_Target: vec4<f32>;\n@group(0) @binding(1) \nvar<uniform> global: CameraPosition;\n@group(1) @binding(0) \nvar<uniform> global_1: Lights;\n@group(3) @binding(0) \nvar<uniform> global_2: StandardMaterial_base_color;\n@group(3) @binding(1) \nvar StandardMaterial_base_color_texture: texture_2d<f32>;\n@group(3) @binding(2) \nvar StandardMaterial_base_color_texture_sampler: sampler;\n@group(3) @binding(3) \nvar<uniform> global_3: StandardMaterial_roughness;\n@group(3) @binding(4) \nvar<uniform> global_4: StandardMaterial_metallic;\n@group(3) @binding(5) \nvar StandardMaterial_metallic_roughness_texture: texture_2d<f32>;\n@group(3) @binding(6) \nvar StandardMaterial_metallic_roughness_texture_sampler: sampler;\n@group(3) @binding(7) \nvar<uniform> global_5: StandardMaterial_reflectance;\n@group(3) @binding(8) \nvar StandardMaterial_normal_map: texture_2d<f32>;\n@group(3) @binding(9) \nvar StandardMaterial_normal_map_sampler: sampler;\n@group(3) @binding(10) \nvar StandardMaterial_occlusion_texture: texture_2d<f32>;\n@group(3) @binding(11) \nvar StandardMaterial_occlusion_texture_sampler: sampler;\n@group(3) @binding(12) \nvar<uniform> global_6: StandardMaterial_emissive;\n@group(3) @binding(13) \nvar StandardMaterial_emissive_texture: texture_2d<f32>;\n@group(3) @binding(14) \nvar StandardMaterial_emissive_texture_sampler: sampler;\nvar<private> gl_FrontFacing_1: bool;\n\nfn pow5_(x: f32) -> f32 {\n    var x_1: f32;\n    var x2_: f32;\n\n    x_1 = x;\n    let _e2 = x_1;\n    let _e3 = x_1;\n    x2_ = (_e2 * _e3);\n    let _e6 = x2_;\n    let _e7 = x2_;\n    let _e9 = x_1;\n    return ((_e6 * _e7) * _e9);\n}\n\nfn getDistanceAttenuation(distanceSquare: f32, inverseRangeSquared: f32) -> f32 {\n    var distanceSquare_1: f32;\n    var inverseRangeSquared_1: f32;\n    var factor: f32;\n    var smoothFactor: f32;\n    var attenuation: f32;\n\n    distanceSquare_1 = distanceSquare;\n    inverseRangeSquared_1 = inverseRangeSquared;\n    let _e4 = distanceSquare_1;\n    let _e5 = inverseRangeSquared_1;\n    factor = (_e4 * _e5);\n    let _e9 = factor;\n    let _e10 = factor;\n    smoothFactor = clamp((1f - (_e9 * _e10)), 0f, 1f);\n    let _e17 = smoothFactor;\n    let _e18 = smoothFactor;\n    attenuation = (_e17 * _e18);\n    let _e21 = attenuation;\n    let _e24 = distanceSquare_1;\n    return ((_e21 * 1f) / max(_e24, 0.001f));\n}\n\nfn D_GGX(roughness: f32, NoH: f32, h: vec3<f32>) -> f32 {\n    var roughness_1: f32;\n    var NoH_1: f32;\n    var oneMinusNoHSquared: f32;\n    var a: f32;\n    var k: f32;\n    var d: f32;\n\n    roughness_1 = roughness;\n    NoH_1 = NoH;\n    let _e5 = NoH_1;\n    let _e6 = NoH_1;\n    oneMinusNoHSquared = (1f - (_e5 * _e6));\n    let _e10 = NoH_1;\n    let _e11 = roughness_1;\n    a = (_e10 * _e11);\n    let _e14 = roughness_1;\n    let _e15 = oneMinusNoHSquared;\n    let _e16 = a;\n    let _e17 = a;\n    k = (_e14 / (_e15 + (_e16 * _e17)));\n    let _e22 = k;\n    let _e23 = k;\n    d = ((_e22 * _e23) * 0.31830987f);\n    let _e28 = d;\n    return _e28;\n}\n\nfn V_SmithGGXCorrelated(roughness_2: f32, NoV: f32, NoL: f32) -> f32 {\n    var roughness_3: f32;\n    var NoV_1: f32;\n    var NoL_1: f32;\n    var a2_: f32;\n    var lambdaV: f32;\n    var lambdaL: f32;\n    var v: f32;\n\n    roughness_3 = roughness_2;\n    NoV_1 = NoV;\n    NoL_1 = NoL;\n    let _e6 = roughness_3;\n    let _e7 = roughness_3;\n    a2_ = (_e6 * _e7);\n    let _e10 = NoL_1;\n    let _e11 = NoV_1;\n    let _e12 = a2_;\n    let _e13 = NoV_1;\n    let _e16 = NoV_1;\n    let _e18 = a2_;\n    lambdaV = (_e10 * sqrt((((_e11 - (_e12 * _e13)) * _e16) + _e18)));\n    let _e23 = NoV_1;\n    let _e24 = NoL_1;\n    let _e25 = a2_;\n    let _e26 = NoL_1;\n    let _e29 = NoL_1;\n    let _e31 = a2_;\n    lambdaL = (_e23 * sqrt((((_e24 - (_e25 * _e26)) * _e29) + _e31)));\n    let _e37 = lambdaV;\n    let _e38 = lambdaL;\n    v = (0.5f / (_e37 + _e38));\n    let _e42 = v;\n    return _e42;\n}\n\nfn F_Schlick(f0_: vec3<f32>, f90_: f32, VoH: f32) -> vec3<f32> {\n    var f90_1: f32;\n    var VoH_1: f32;\n\n    f90_1 = f90_;\n    VoH_1 = VoH;\n    let _e5 = f90_1;\n    let _e9 = VoH_1;\n    let _e11 = pow5_((1f - _e9));\n    return (f0_ + ((vec3(_e5) - f0_) * _e11));\n}\n\nfn F_Schlick_1(f0_1: f32, f90_2: f32, VoH_2: f32) -> f32 {\n    var f0_2: f32;\n    var f90_3: f32;\n    var VoH_3: f32;\n\n    f0_2 = f0_1;\n    f90_3 = f90_2;\n    VoH_3 = VoH_2;\n    let _e6 = f0_2;\n    let _e7 = f90_3;\n    let _e8 = f0_2;\n    let _e11 = VoH_3;\n    let _e13 = pow5_((1f - _e11));\n    return (_e6 + ((_e7 - _e8) * _e13));\n}\n\nfn fresnel(f0_3: vec3<f32>, LoH: f32) -> vec3<f32> {\n    var f0_4: vec3<f32>;\n    var LoH_1: f32;\n    var f90_4: f32;\n\n    f0_4 = f0_3;\n    LoH_1 = LoH;\n    let _e4 = f0_4;\n    f90_4 = clamp(dot(_e4, vec3(16.5f)), 0f, 1f);\n    let _e12 = f0_4;\n    let _e13 = f90_4;\n    let _e14 = LoH_1;\n    let _e15 = F_Schlick(_e12, _e13, _e14);\n    return _e15;\n}\n\nfn specular(f0_5: vec3<f32>, roughness_4: f32, h_1: vec3<f32>, NoV_2: f32, NoL_2: f32, NoH_2: f32, LoH_2: f32, specularIntensity: f32) -> vec3<f32> {\n    var f0_6: vec3<f32>;\n    var roughness_5: f32;\n    var NoV_3: f32;\n    var NoL_3: f32;\n    var NoH_3: f32;\n    var LoH_3: f32;\n    var specularIntensity_1: f32;\n    var D: f32;\n    var V: f32;\n    var F: vec3<f32>;\n\n    f0_6 = f0_5;\n    roughness_5 = roughness_4;\n    NoV_3 = NoV_2;\n    NoL_3 = NoL_2;\n    NoH_3 = NoH_2;\n    LoH_3 = LoH_2;\n    specularIntensity_1 = specularIntensity;\n    let _e15 = roughness_5;\n    let _e16 = NoH_3;\n    let _e17 = D_GGX(_e15, _e16, h_1);\n    D = _e17;\n    let _e19 = roughness_5;\n    let _e20 = NoV_3;\n    let _e21 = NoL_3;\n    let _e22 = V_SmithGGXCorrelated(_e19, _e20, _e21);\n    V = _e22;\n    let _e24 = f0_6;\n    let _e25 = LoH_3;\n    let _e26 = fresnel(_e24, _e25);\n    F = _e26;\n    let _e28 = specularIntensity_1;\n    let _e29 = D;\n    let _e31 = V;\n    let _e33 = F;\n    return (((_e28 * _e29) * _e31) * _e33);\n}\n\nfn Fd_Burley(roughness_6: f32, NoV_4: f32, NoL_4: f32, LoH_4: f32) -> f32 {\n    var roughness_7: f32;\n    var NoV_5: f32;\n    var NoL_5: f32;\n    var LoH_5: f32;\n    var f90_5: f32;\n    var lightScatter: f32;\n    var viewScatter: f32;\n\n    roughness_7 = roughness_6;\n    NoV_5 = NoV_4;\n    NoL_5 = NoL_4;\n    LoH_5 = LoH_4;\n    let _e10 = roughness_7;\n    let _e12 = LoH_5;\n    let _e14 = LoH_5;\n    f90_5 = (0.5f + (((2f * _e10) * _e12) * _e14));\n    let _e19 = f90_5;\n    let _e20 = NoL_5;\n    let _e21 = F_Schlick_1(1f, _e19, _e20);\n    lightScatter = _e21;\n    let _e24 = f90_5;\n    let _e25 = NoV_5;\n    let _e26 = F_Schlick_1(1f, _e24, _e25);\n    viewScatter = _e26;\n    let _e28 = lightScatter;\n    let _e29 = viewScatter;\n    return ((_e28 * _e29) * 0.31830987f);\n}\n\nfn EnvBRDFApprox(f0_7: vec3<f32>, perceptual_roughness: f32, NoV_6: f32) -> vec3<f32> {\n    var f0_8: vec3<f32>;\n    var perceptual_roughness_1: f32;\n    var NoV_7: f32;\n    var c0_: vec4<f32> = vec4<f32>(-1f, -0.0275f, -0.572f, 0.022f);\n    var c1_: vec4<f32> = vec4<f32>(1f, 0.0425f, 1.04f, -0.04f);\n    var r: vec4<f32>;\n    var a004_: f32;\n    var AB: vec2<f32>;\n\n    f0_8 = f0_7;\n    perceptual_roughness_1 = perceptual_roughness;\n    NoV_7 = NoV_6;\n    let _e18 = perceptual_roughness_1;\n    let _e20 = c0_;\n    let _e22 = c1_;\n    r = ((vec4(_e18) * _e20) + _e22);\n    let _e25 = r;\n    let _e27 = r;\n    let _e31 = NoV_7;\n    let _e35 = r;\n    let _e38 = r;\n    a004_ = ((min((_e25.x * _e27.x), exp2((-9.28f * _e31))) * _e35.x) + _e38.y);\n    let _e45 = a004_;\n    let _e48 = r;\n    AB = ((vec2<f32>(-1.04f, 1.04f) * vec2(_e45)) + _e48.zw);\n    let _e52 = f0_8;\n    let _e53 = AB;\n    let _e57 = AB;\n    return ((_e52 * vec3(_e53.x)) + vec3(_e57.y));\n}\n\nfn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32 {\n    var perceptualRoughness_1: f32;\n    var clampedPerceptualRoughness: f32;\n\n    perceptualRoughness_1 = perceptualRoughness;\n    let _e2 = perceptualRoughness_1;\n    clampedPerceptualRoughness = clamp(_e2, 0.089f, 1f);\n    let _e7 = clampedPerceptualRoughness;\n    let _e8 = clampedPerceptualRoughness;\n    return (_e7 * _e8);\n}\n\nfn luminance(v_1: vec3<f32>) -> f32 {\n    var v_2: vec3<f32>;\n\n    v_2 = v_1;\n    let _e2 = v_2;\n    return dot(_e2, vec3<f32>(0.2126f, 0.7152f, 0.0722f));\n}\n\nfn change_luminance(c_in: vec3<f32>, l_out: f32) -> vec3<f32> {\n    var c_in_1: vec3<f32>;\n    var l_out_1: f32;\n    var l_in: f32;\n\n    c_in_1 = c_in;\n    l_out_1 = l_out;\n    let _e4 = c_in_1;\n    let _e5 = luminance(_e4);\n    l_in = _e5;\n    let _e7 = c_in_1;\n    let _e8 = l_out_1;\n    let _e9 = l_in;\n    return (_e7 * (_e8 / _e9));\n}\n\nfn reinhard_luminance(color: vec3<f32>) -> vec3<f32> {\n    var color_1: vec3<f32>;\n    var l_old: f32;\n    var l_new: f32;\n\n    color_1 = color;\n    let _e2 = color_1;\n    let _e3 = luminance(_e2);\n    l_old = _e3;\n    let _e5 = l_old;\n    let _e7 = l_old;\n    l_new = (_e5 / (1f + _e7));\n    let _e11 = color_1;\n    let _e12 = l_new;\n    let _e13 = change_luminance(_e11, _e12);\n    return _e13;\n}\n\nfn point_light(light: PointLight, roughness_8: f32, NdotV: f32, N: vec3<f32>, V_1: vec3<f32>, R: vec3<f32>, F0_: vec3<f32>, diffuseColor: vec3<f32>) -> vec3<f32> {\n    var light_1: PointLight;\n    var roughness_9: f32;\n    var NdotV_1: f32;\n    var N_1: vec3<f32>;\n    var V_2: vec3<f32>;\n    var R_1: vec3<f32>;\n    var F0_1: vec3<f32>;\n    var diffuseColor_1: vec3<f32>;\n    var light_to_frag: vec3<f32>;\n    var distance_square: f32;\n    var rangeAttenuation: f32;\n    var a_1: f32;\n    var radius: f32;\n    var centerToRay: vec3<f32>;\n    var closestPoint: vec3<f32>;\n    var LspecLengthInverse: f32;\n    var normalizationFactor: f32;\n    var specularIntensity_2: f32;\n    var L: vec3<f32>;\n    var H: vec3<f32>;\n    var NoL_6: f32;\n    var NoH_4: f32;\n    var LoH_6: f32;\n    var specular_1: vec3<f32>;\n    var diffuse: vec3<f32>;\n\n    light_1 = light;\n    roughness_9 = roughness_8;\n    NdotV_1 = NdotV;\n    N_1 = N;\n    V_2 = V_1;\n    R_1 = R;\n    F0_1 = F0_;\n    diffuseColor_1 = diffuseColor;\n    let _e17 = light_1;\n    let _e20 = v_WorldPosition_1;\n    light_to_frag = (_e17.pos.xyz - _e20.xyz);\n    let _e24 = light_to_frag;\n    let _e25 = light_to_frag;\n    distance_square = dot(_e24, _e25);\n    let _e28 = distance_square;\n    let _e29 = light_1;\n    let _e32 = getDistanceAttenuation(_e28, _e29.lightParams.x);\n    rangeAttenuation = _e32;\n    let _e34 = roughness_9;\n    a_1 = _e34;\n    let _e36 = light_1;\n    radius = _e36.lightParams.y;\n    let _e40 = light_to_frag;\n    let _e41 = R_1;\n    let _e43 = R_1;\n    let _e45 = light_to_frag;\n    centerToRay = ((dot(_e40, _e41) * _e43) - _e45);\n    let _e48 = light_to_frag;\n    let _e49 = centerToRay;\n    let _e50 = radius;\n    let _e51 = centerToRay;\n    let _e52 = centerToRay;\n    closestPoint = (_e48 + (_e49 * clamp((_e50 * inverseSqrt(dot(_e51, _e52))), 0f, 1f)));\n    let _e62 = closestPoint;\n    let _e63 = closestPoint;\n    LspecLengthInverse = inverseSqrt(dot(_e62, _e63));\n    let _e67 = a_1;\n    let _e68 = a_1;\n    let _e69 = radius;\n    let _e72 = LspecLengthInverse;\n    normalizationFactor = (_e67 / clamp((_e68 + ((_e69 * 0.5f) * _e72)), 0f, 1f));\n    let _e80 = normalizationFactor;\n    let _e81 = normalizationFactor;\n    specularIntensity_2 = (_e80 * _e81);\n    let _e84 = closestPoint;\n    let _e85 = LspecLengthInverse;\n    L = (_e84 * _e85);\n    let _e88 = L;\n    let _e89 = V_2;\n    H = normalize((_e88 + _e89));\n    let _e93 = N_1;\n    let _e94 = L;\n    NoL_6 = clamp(dot(_e93, _e94), 0f, 1f);\n    let _e100 = N_1;\n    let _e101 = H;\n    NoH_4 = clamp(dot(_e100, _e101), 0f, 1f);\n    let _e107 = L;\n    let _e108 = H;\n    LoH_6 = clamp(dot(_e107, _e108), 0f, 1f);\n    let _e114 = F0_1;\n    let _e115 = roughness_9;\n    let _e116 = H;\n    let _e117 = NdotV_1;\n    let _e118 = NoL_6;\n    let _e119 = NoH_4;\n    let _e120 = LoH_6;\n    let _e121 = specularIntensity_2;\n    let _e122 = specular(_e114, _e115, _e116, _e117, _e118, _e119, _e120, _e121);\n    specular_1 = _e122;\n    let _e124 = light_to_frag;\n    L = normalize(_e124);\n    let _e126 = L;\n    let _e127 = V_2;\n    H = normalize((_e126 + _e127));\n    let _e130 = N_1;\n    let _e131 = L;\n    NoL_6 = clamp(dot(_e130, _e131), 0f, 1f);\n    let _e136 = N_1;\n    let _e137 = H;\n    NoH_4 = clamp(dot(_e136, _e137), 0f, 1f);\n    let _e142 = L;\n    let _e143 = H;\n    LoH_6 = clamp(dot(_e142, _e143), 0f, 1f);\n    let _e148 = diffuseColor_1;\n    let _e149 = roughness_9;\n    let _e150 = NdotV_1;\n    let _e151 = NoL_6;\n    let _e152 = LoH_6;\n    let _e153 = Fd_Burley(_e149, _e150, _e151, _e152);\n    diffuse = (_e148 * _e153);\n    let _e156 = diffuse;\n    let _e157 = specular_1;\n    let _e159 = light_1;\n    let _e163 = rangeAttenuation;\n    let _e164 = NoL_6;\n    return (((_e156 + _e157) * _e159.color.xyz) * (_e163 * _e164));\n}\n\nfn dir_light(light_2: DirectionalLight, roughness_10: f32, NdotV_2: f32, normal: vec3<f32>, view: vec3<f32>, R_2: vec3<f32>, F0_2: vec3<f32>, diffuseColor_2: vec3<f32>) -> vec3<f32> {\n    var light_3: DirectionalLight;\n    var roughness_11: f32;\n    var NdotV_3: f32;\n    var normal_1: vec3<f32>;\n    var view_1: vec3<f32>;\n    var R_3: vec3<f32>;\n    var F0_3: vec3<f32>;\n    var diffuseColor_3: vec3<f32>;\n    var incident_light: vec3<f32>;\n    var half_vector: vec3<f32>;\n    var NoL_7: f32;\n    var NoH_5: f32;\n    var LoH_7: f32;\n    var diffuse_1: vec3<f32>;\n    var specularIntensity_3: f32 = 1f;\n    var specular_2: vec3<f32>;\n\n    light_3 = light_2;\n    roughness_11 = roughness_10;\n    NdotV_3 = NdotV_2;\n    normal_1 = normal;\n    view_1 = view;\n    R_3 = R_2;\n    F0_3 = F0_2;\n    diffuseColor_3 = diffuseColor_2;\n    let _e16 = light_3;\n    incident_light = _e16.direction.xyz;\n    let _e20 = incident_light;\n    let _e21 = view_1;\n    half_vector = normalize((_e20 + _e21));\n    let _e25 = normal_1;\n    let _e26 = incident_light;\n    NoL_7 = clamp(dot(_e25, _e26), 0f, 1f);\n    let _e32 = normal_1;\n    let _e33 = half_vector;\n    NoH_5 = clamp(dot(_e32, _e33), 0f, 1f);\n    let _e39 = incident_light;\n    let _e40 = half_vector;\n    LoH_7 = clamp(dot(_e39, _e40), 0f, 1f);\n    let _e46 = diffuseColor_3;\n    let _e47 = roughness_11;\n    let _e48 = NdotV_3;\n    let _e49 = NoL_7;\n    let _e50 = LoH_7;\n    let _e51 = Fd_Burley(_e47, _e48, _e49, _e50);\n    diffuse_1 = (_e46 * _e51);\n    let _e56 = F0_3;\n    let _e57 = roughness_11;\n    let _e58 = half_vector;\n    let _e59 = NdotV_3;\n    let _e60 = NoL_7;\n    let _e61 = NoH_5;\n    let _e62 = LoH_7;\n    let _e63 = specularIntensity_3;\n    let _e64 = specular(_e56, _e57, _e58, _e59, _e60, _e61, _e62, _e63);\n    specular_2 = _e64;\n    let _e66 = specular_2;\n    let _e67 = diffuse_1;\n    let _e69 = light_3;\n    let _e73 = NoL_7;\n    return (((_e66 + _e67) * _e69.color.xyz) * _e73);\n}\n\nfn main_1() {\n    var output_color: vec4<f32>;\n    var metallic_roughness: vec4<f32>;\n    var metallic: f32;\n    var perceptual_roughness_2: f32;\n    var roughness_12: f32;\n    var N_2: vec3<f32>;\n    var T: vec3<f32>;\n    var B: vec3<f32>;\n    var local: vec3<f32>;\n    var local_1: vec3<f32>;\n    var local_2: vec3<f32>;\n    var TBN: mat3x3<f32>;\n    var occlusion: f32;\n    var emissive: vec4<f32>;\n    var V_3: vec3<f32>;\n    var NdotV_4: f32;\n    var F0_4: vec3<f32>;\n    var diffuseColor_4: vec3<f32>;\n    var R_4: vec3<f32>;\n    var light_accum: vec3<f32> = vec3(0f);\n    var i: i32 = 0i;\n    var i_1: i32 = 0i;\n    var diffuse_ambient: vec3<f32>;\n    var specular_ambient: vec3<f32>;\n\n    let _e37 = global_2.base_color;\n    output_color = _e37;\n    let _e39 = output_color;\n    let _e40 = v_Uv_1;\n    let _e41 = textureSample(StandardMaterial_base_color_texture, StandardMaterial_base_color_texture_sampler, _e40);\n    output_color = (_e39 * _e41);\n    let _e43 = v_Uv_1;\n    let _e44 = textureSample(StandardMaterial_metallic_roughness_texture, StandardMaterial_metallic_roughness_texture_sampler, _e43);\n    metallic_roughness = _e44;\n    let _e46 = global_4.metallic;\n    let _e47 = metallic_roughness;\n    metallic = (_e46 * _e47.z);\n    let _e51 = global_3.perceptual_roughness;\n    let _e52 = metallic_roughness;\n    perceptual_roughness_2 = (_e51 * _e52.y);\n    let _e56 = perceptual_roughness_2;\n    let _e57 = perceptualRoughnessToRoughness(_e56);\n    roughness_12 = _e57;\n    let _e59 = v_WorldNormal_1;\n    N_2 = normalize(_e59);\n    let _e62 = v_WorldTangent_1;\n    T = normalize(_e62.xyz);\n    let _e66 = N_2;\n    let _e67 = T;\n    let _e69 = v_WorldTangent_1;\n    B = (cross(_e66, _e67) * _e69.w);\n    let _e74 = gl_FrontFacing_1;\n    if _e74 {\n        let _e75 = N_2;\n        local = _e75;\n    } else {\n        let _e76 = N_2;\n        local = -(_e76);\n    }\n    let _e79 = local;\n    N_2 = _e79;\n    let _e80 = gl_FrontFacing_1;\n    if _e80 {\n        let _e81 = T;\n        local_1 = _e81;\n    } else {\n        let _e82 = T;\n        local_1 = -(_e82);\n    }\n    let _e85 = local_1;\n    T = _e85;\n    let _e86 = gl_FrontFacing_1;\n    if _e86 {\n        let _e87 = B;\n        local_2 = _e87;\n    } else {\n        let _e88 = B;\n        local_2 = -(_e88);\n    }\n    let _e91 = local_2;\n    B = _e91;\n    let _e92 = T;\n    let _e93 = B;\n    let _e94 = N_2;\n    TBN = mat3x3<f32>(vec3<f32>(_e92.x, _e92.y, _e92.z), vec3<f32>(_e93.x, _e93.y, _e93.z), vec3<f32>(_e94.x, _e94.y, _e94.z));\n    let _e109 = TBN;\n    let _e110 = v_Uv_1;\n    let _e111 = textureSample(StandardMaterial_normal_map, StandardMaterial_normal_map_sampler, _e110);\n    N_2 = (_e109 * normalize(((_e111.xyz * 2f) - vec3(1f))));\n    let _e120 = v_Uv_1;\n    let _e121 = textureSample(StandardMaterial_occlusion_texture, StandardMaterial_occlusion_texture_sampler, _e120);\n    occlusion = _e121.x;\n    let _e124 = global_6.emissive;\n    emissive = _e124;\n    let _e126 = emissive;\n    let _e128 = v_Uv_1;\n    let _e129 = textureSample(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler, _e128);\n    let _e131 = (_e126.xyz * _e129.xyz);\n    emissive.x = _e131.x;\n    emissive.y = _e131.y;\n    emissive.z = _e131.z;\n    let _e138 = global.CameraPos;\n    let _e140 = v_WorldPosition_1;\n    V_3 = normalize((_e138.xyz - _e140.xyz));\n    let _e145 = N_2;\n    let _e146 = V_3;\n    NdotV_4 = max(dot(_e145, _e146), 0.001f);\n    let _e152 = global_5.reflectance;\n    let _e154 = global_5.reflectance;\n    let _e157 = metallic;\n    let _e161 = output_color;\n    let _e163 = metallic;\n    F0_4 = (vec3((((0.16f * _e152) * _e154) * (1f - _e157))) + (_e161.xyz * vec3(_e163)));\n    let _e168 = output_color;\n    let _e171 = metallic;\n    diffuseColor_4 = (_e168.xyz * vec3((1f - _e171)));\n    let _e176 = V_3;\n    let _e178 = N_2;\n    R_4 = reflect(-(_e176), _e178);\n    loop {\n        let _e186 = i;\n        let _e187 = global_1.NumLights;\n        let _e191 = i;\n        if !(((_e186 < i32(_e187.x)) && (_e191 < MAX_POINT_LIGHTS))) {\n            break;\n        }\n        {\n            let _e198 = light_accum;\n            let _e199 = i;\n            let _e201 = global_1.PointLights[_e199];\n            let _e202 = roughness_12;\n            let _e203 = NdotV_4;\n            let _e204 = N_2;\n            let _e205 = V_3;\n            let _e206 = R_4;\n            let _e207 = F0_4;\n            let _e208 = diffuseColor_4;\n            let _e209 = point_light(_e201, _e202, _e203, _e204, _e205, _e206, _e207, _e208);\n            light_accum = (_e198 + _e209);\n        }\n        continuing {\n            let _e195 = i;\n            i = (_e195 + 1i);\n        }\n    }\n    loop {\n        let _e213 = i_1;\n        let _e214 = global_1.NumLights;\n        let _e218 = i_1;\n        if !(((_e213 < i32(_e214.y)) && (_e218 < MAX_DIRECTIONAL_LIGHTS))) {\n            break;\n        }\n        {\n            let _e225 = light_accum;\n            let _e226 = i_1;\n            let _e228 = global_1.DirectionalLights[_e226];\n            let _e229 = roughness_12;\n            let _e230 = NdotV_4;\n            let _e231 = N_2;\n            let _e232 = V_3;\n            let _e233 = R_4;\n            let _e234 = F0_4;\n            let _e235 = diffuseColor_4;\n            let _e236 = dir_light(_e228, _e229, _e230, _e231, _e232, _e233, _e234, _e235);\n            light_accum = (_e225 + _e236);\n        }\n        continuing {\n            let _e222 = i_1;\n            i_1 = (_e222 + 1i);\n        }\n    }\n    let _e238 = diffuseColor_4;\n    let _e240 = NdotV_4;\n    let _e241 = EnvBRDFApprox(_e238, 1f, _e240);\n    diffuse_ambient = _e241;\n    let _e243 = F0_4;\n    let _e244 = perceptual_roughness_2;\n    let _e245 = NdotV_4;\n    let _e246 = EnvBRDFApprox(_e243, _e244, _e245);\n    specular_ambient = _e246;\n    let _e248 = light_accum;\n    output_color.x = _e248.x;\n    output_color.y = _e248.y;\n    output_color.z = _e248.z;\n    let _e255 = output_color;\n    let _e257 = diffuse_ambient;\n    let _e258 = specular_ambient;\n    let _e260 = global_1.AmbientColor;\n    let _e263 = occlusion;\n    let _e265 = (_e255.xyz + (((_e257 + _e258) * _e260.xyz) * _e263));\n    output_color.x = _e265.x;\n    output_color.y = _e265.y;\n    output_color.z = _e265.z;\n    let _e272 = output_color;\n    let _e274 = emissive;\n    let _e276 = output_color;\n    let _e279 = (_e272.xyz + (_e274.xyz * _e276.w));\n    output_color.x = _e279.x;\n    output_color.y = _e279.y;\n    output_color.z = _e279.z;\n    let _e286 = output_color;\n    let _e288 = reinhard_luminance(_e286.xyz);\n    output_color.x = _e288.x;\n    output_color.y = _e288.y;\n    output_color.z = _e288.z;\n    let _e295 = output_color;\n    o_Target = _e295;\n    return;\n}\n\n@fragment \nfn main(@location(0) v_WorldPosition: vec3<f32>, @location(1) v_WorldNormal: vec3<f32>, @location(2) v_Uv: vec2<f32>, @location(3) v_WorldTangent: vec4<f32>, @builtin(front_facing) gl_FrontFacing: bool) -> FragmentOutput {\n    v_WorldPosition_1 = v_WorldPosition;\n    v_WorldNormal_1 = v_WorldNormal;\n    v_Uv_1 = v_Uv;\n    v_WorldTangent_1 = v_WorldTangent;\n    gl_FrontFacing_1 = gl_FrontFacing;\n    main_1();\n    let _e11 = o_Target;\n    return FragmentOutput(_e11);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-bevy-pbr.vert.wgsl",
    "content": "struct CameraViewProj {\n    ViewProj: mat4x4<f32>,\n}\n\nstruct Transform {\n    Model: mat4x4<f32>,\n}\n\nstruct VertexOutput {\n    @location(0) v_WorldPosition: vec3<f32>,\n    @location(1) v_WorldNormal: vec3<f32>,\n    @location(2) v_Uv: vec2<f32>,\n    @location(3) v_WorldTangent: vec4<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nvar<private> Vertex_Position_1: vec3<f32>;\nvar<private> Vertex_Normal_1: vec3<f32>;\nvar<private> Vertex_Uv_1: vec2<f32>;\nvar<private> Vertex_Tangent_1: vec4<f32>;\nvar<private> v_WorldPosition: vec3<f32>;\nvar<private> v_WorldNormal: vec3<f32>;\nvar<private> v_Uv: vec2<f32>;\n@group(0) @binding(0) \nvar<uniform> global: CameraViewProj;\nvar<private> v_WorldTangent: vec4<f32>;\n@group(2) @binding(0) \nvar<uniform> global_1: Transform;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    var world_position: vec4<f32>;\n\n    let _e12 = global_1.Model;\n    let _e13 = Vertex_Position_1;\n    world_position = (_e12 * vec4<f32>(_e13.x, _e13.y, _e13.z, 1f));\n    let _e21 = world_position;\n    v_WorldPosition = _e21.xyz;\n    let _e23 = global_1.Model;\n    let _e31 = Vertex_Normal_1;\n    v_WorldNormal = (mat3x3<f32>(_e23[0].xyz, _e23[1].xyz, _e23[2].xyz) * _e31);\n    let _e33 = Vertex_Uv_1;\n    v_Uv = _e33;\n    let _e34 = global_1.Model;\n    let _e42 = Vertex_Tangent_1;\n    let _e44 = (mat3x3<f32>(_e34[0].xyz, _e34[1].xyz, _e34[2].xyz) * _e42.xyz);\n    let _e45 = Vertex_Tangent_1;\n    v_WorldTangent = vec4<f32>(_e44.x, _e44.y, _e44.z, _e45.w);\n    let _e52 = global.ViewProj;\n    let _e53 = world_position;\n    gl_Position = (_e52 * _e53);\n    return;\n}\n\n@vertex \nfn main(@location(0) Vertex_Position: vec3<f32>, @location(1) Vertex_Normal: vec3<f32>, @location(2) Vertex_Uv: vec2<f32>, @location(3) Vertex_Tangent: vec4<f32>) -> VertexOutput {\n    Vertex_Position_1 = Vertex_Position;\n    Vertex_Normal_1 = Vertex_Normal;\n    Vertex_Uv_1 = Vertex_Uv;\n    Vertex_Tangent_1 = Vertex_Tangent;\n    main_1();\n    let _e9 = v_WorldPosition;\n    let _e11 = v_WorldNormal;\n    let _e13 = v_Uv;\n    let _e15 = v_WorldTangent;\n    let _e17 = gl_Position;\n    return VertexOutput(_e9, _e11, _e13, _e15, _e17);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-bits.frag.wgsl",
    "content": "fn main_1() {\n    var i: i32 = 0i;\n    var i2_: vec2<i32> = vec2(0i);\n    var i3_: vec3<i32> = vec3(0i);\n    var i4_: vec4<i32> = vec4(0i);\n    var u: u32 = 0u;\n    var u2_: vec2<u32> = vec2(0u);\n    var u3_: vec3<u32> = vec3(0u);\n    var u4_: vec4<u32> = vec4(0u);\n    var f2_: vec2<f32> = vec2(0f);\n    var f4_: vec4<f32> = vec4(0f);\n\n    let _e28 = f4_;\n    u = pack4x8snorm(_e28);\n    let _e30 = f4_;\n    u = pack4x8unorm(_e30);\n    let _e32 = f2_;\n    u = pack2x16unorm(_e32);\n    let _e34 = f2_;\n    u = pack2x16snorm(_e34);\n    let _e36 = f2_;\n    u = pack2x16float(_e36);\n    let _e38 = u;\n    f4_ = unpack4x8snorm(_e38);\n    let _e40 = u;\n    f4_ = unpack4x8unorm(_e40);\n    let _e42 = u;\n    f2_ = unpack2x16snorm(_e42);\n    let _e44 = u;\n    f2_ = unpack2x16unorm(_e44);\n    let _e46 = u;\n    f2_ = unpack2x16float(_e46);\n    let _e48 = i;\n    let _e49 = i;\n    i = insertBits(_e48, _e49, 5u, 10u);\n    let _e53 = i2_;\n    let _e54 = i2_;\n    i2_ = insertBits(_e53, _e54, 5u, 10u);\n    let _e58 = i3_;\n    let _e59 = i3_;\n    i3_ = insertBits(_e58, _e59, 5u, 10u);\n    let _e63 = i4_;\n    let _e64 = i4_;\n    i4_ = insertBits(_e63, _e64, 5u, 10u);\n    let _e68 = u;\n    let _e69 = u;\n    u = insertBits(_e68, _e69, 5u, 10u);\n    let _e73 = u2_;\n    let _e74 = u2_;\n    u2_ = insertBits(_e73, _e74, 5u, 10u);\n    let _e78 = u3_;\n    let _e79 = u3_;\n    u3_ = insertBits(_e78, _e79, 5u, 10u);\n    let _e83 = u4_;\n    let _e84 = u4_;\n    u4_ = insertBits(_e83, _e84, 5u, 10u);\n    let _e88 = i;\n    i = extractBits(_e88, 5u, 10u);\n    let _e92 = i2_;\n    i2_ = extractBits(_e92, 5u, 10u);\n    let _e96 = i3_;\n    i3_ = extractBits(_e96, 5u, 10u);\n    let _e100 = i4_;\n    i4_ = extractBits(_e100, 5u, 10u);\n    let _e104 = u;\n    u = extractBits(_e104, 5u, 10u);\n    let _e108 = u2_;\n    u2_ = extractBits(_e108, 5u, 10u);\n    let _e112 = u3_;\n    u3_ = extractBits(_e112, 5u, 10u);\n    let _e116 = u4_;\n    u4_ = extractBits(_e116, 5u, 10u);\n    let _e120 = i;\n    i = firstTrailingBit(_e120);\n    let _e122 = i2_;\n    i2_ = firstTrailingBit(_e122);\n    let _e124 = i3_;\n    i3_ = firstTrailingBit(_e124);\n    let _e126 = i4_;\n    i4_ = firstTrailingBit(_e126);\n    let _e128 = u;\n    i = i32(firstTrailingBit(_e128));\n    let _e131 = u2_;\n    i2_ = vec2<i32>(firstTrailingBit(_e131));\n    let _e134 = u3_;\n    i3_ = vec3<i32>(firstTrailingBit(_e134));\n    let _e137 = u4_;\n    i4_ = vec4<i32>(firstTrailingBit(_e137));\n    let _e140 = i;\n    i = firstLeadingBit(_e140);\n    let _e142 = i2_;\n    i2_ = firstLeadingBit(_e142);\n    let _e144 = i3_;\n    i3_ = firstLeadingBit(_e144);\n    let _e146 = i4_;\n    i4_ = firstLeadingBit(_e146);\n    let _e148 = u;\n    i = i32(firstLeadingBit(_e148));\n    let _e151 = u2_;\n    i2_ = vec2<i32>(firstLeadingBit(_e151));\n    let _e154 = u3_;\n    i3_ = vec3<i32>(firstLeadingBit(_e154));\n    let _e157 = u4_;\n    i4_ = vec4<i32>(firstLeadingBit(_e157));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-bool-select.frag.wgsl",
    "content": "struct FragmentOutput {\n    @location(0) o_color: vec4<f32>,\n}\n\nvar<private> o_color: vec4<f32>;\n\nfn TevPerCompGT(a: f32, b: f32) -> f32 {\n    var a_1: f32;\n    var b_1: f32;\n\n    a_1 = a;\n    b_1 = b;\n    let _e4 = a_1;\n    let _e5 = b_1;\n    return select(0f, 1f, (_e4 > _e5));\n}\n\nfn TevPerCompGT_1(a_2: vec3<f32>, b_2: vec3<f32>) -> vec3<f32> {\n    var a_3: vec3<f32>;\n    var b_3: vec3<f32>;\n\n    a_3 = a_2;\n    b_3 = b_2;\n    let _e4 = a_3;\n    let _e5 = b_3;\n    return select(vec3(0f), vec3(1f), (_e4 > _e5));\n}\n\nfn main_1() {\n    let _e5 = TevPerCompGT_1(vec3(3f), vec3(5f));\n    o_color.x = _e5.x;\n    o_color.y = _e5.y;\n    o_color.z = _e5.z;\n    let _e15 = TevPerCompGT(3f, 5f);\n    o_color.w = _e15;\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = o_color;\n    return FragmentOutput(_e1);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-buffer.frag.wgsl",
    "content": "struct testBufferBlock {\n    data: array<u32>,\n}\n\nstruct testBufferReadOnlyBlock {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> testBuffer: testBufferBlock;\n@group(0) @binding(2) \nvar<storage> testBufferReadOnly: testBufferReadOnlyBlock;\n\nfn main_1() {\n    var a: u32;\n    var b: u32;\n\n    let _e4 = testBuffer.data[0];\n    a = _e4;\n    testBuffer.data[1i] = 2u;\n    let _e12 = testBufferReadOnly.data[0];\n    b = _e12;\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-clamp-splat.vert.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nvar<private> a_pos_1: vec2<f32>;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    let _e2 = a_pos_1;\n    let _e7 = clamp(_e2, vec2(0f), vec2(1f));\n    gl_Position = vec4<f32>(_e7.x, _e7.y, 0f, 1f);\n    return;\n}\n\n@vertex \nfn main(@location(0) a_pos: vec2<f32>) -> VertexOutput {\n    a_pos_1 = a_pos;\n    main_1();\n    let _e3 = gl_Position;\n    return VertexOutput(_e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-const-global-swizzle.frag.wgsl",
    "content": "struct FragmentOutput {\n    @location(0) o_Target: vec4<f32>,\n}\n\nconst blank: vec2<f32> = vec2<f32>(0f, 1f);\n\nvar<private> v_Uv_1: vec2<f32>;\nvar<private> o_Target: vec4<f32>;\n\nfn main_1() {\n    var col: vec2<f32>;\n\n    let _e3 = v_Uv_1;\n    col = (_e3.xy * blank);\n    let _e7 = col;\n    o_Target = vec4<f32>(_e7.x, _e7.y, 0f, 1f);\n    return;\n}\n\n@fragment \nfn main(@location(0) v_Uv: vec2<f32>) -> FragmentOutput {\n    v_Uv_1 = v_Uv;\n    main_1();\n    let _e3 = o_Target;\n    return FragmentOutput(_e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-constant-array-size.frag.wgsl",
    "content": "struct Data {\n    vecs: array<vec4<f32>, 42>,\n}\n\nconst NUM_VECS: i32 = 42i;\n\n@group(1) @binding(0) \nvar<uniform> global: Data;\n\nfn function_() -> vec4<f32> {\n    var sum: vec4<f32> = vec4(0f);\n    var i: i32 = 0i;\n\n    loop {\n        let _e8 = i;\n        if !((_e8 < NUM_VECS)) {\n            break;\n        }\n        {\n            let _e14 = sum;\n            let _e15 = i;\n            let _e17 = global.vecs[_e15];\n            sum = (_e14 + _e17);\n        }\n        continuing {\n            let _e11 = i;\n            i = (_e11 + 1i);\n        }\n    }\n    let _e19 = sum;\n    return _e19;\n}\n\nfn main_1() {\n    let _e0 = function_();\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-declarations.frag.wgsl",
    "content": "struct VertexData {\n    position: vec2<f32>,\n    a: vec2<f32>,\n}\n\nstruct FragmentData {\n    position: vec2<f32>,\n    a: vec2<f32>,\n}\n\nstruct TestStruct {\n    a: f32,\n    b: f32,\n}\n\nstruct LightScatteringParams {\n    BetaRay: f32,\n    BetaMie: array<f32, 3>,\n    HGg: f32,\n    DistanceMul: array<f32, 4>,\n    BlendCoeff: f32,\n    SunDirection: vec3<f32>,\n    SunColor: vec3<f32>,\n}\n\nstruct FragmentOutput {\n    @location(0) position: vec2<f32>,\n    @location(1) a: vec2<f32>,\n    @location(2) out_array: vec4<f32>,\n    @location(3) out_array_1: vec4<f32>,\n}\n\nvar<private> vert: VertexData;\nvar<private> frag: FragmentData;\nvar<private> in_array_2: array<vec4<f32>, 2>;\nvar<private> out_array: array<vec4<f32>, 2>;\nvar<private> array_2d: array<array<f32, 2>, 2>;\nvar<private> array_toomanyd: array<array<array<array<array<array<array<f32, 2>, 2>, 2>, 2>, 2>, 2>, 2>;\n\nfn main_1() {\n    var positions: array<vec3<f32>, 2> = array<vec3<f32>, 2>(vec3<f32>(-1f, 1f, 0f), vec3<f32>(-1f, -1f, 0f));\n    var strct: TestStruct = TestStruct(1f, 2f);\n    var from_input_array: vec4<f32>;\n    var a_1: f32;\n    var b: f32;\n    var light_scattering_params: LightScatteringParams;\n\n    let _e17 = in_array_2[1];\n    from_input_array = _e17;\n    let _e21 = array_2d[0][0];\n    a_1 = _e21;\n    let _e30 = array_toomanyd[0][0][0][0][0][0][0];\n    b = _e30;\n    out_array[0i] = vec4(2f);\n    return;\n}\n\n@fragment \nfn main(@location(0) position: vec2<f32>, @location(1) a: vec2<f32>, @location(2) in_array: vec4<f32>, @location(3) in_array_1: vec4<f32>) -> FragmentOutput {\n    vert.position = position;\n    vert.a = a;\n    in_array_2[0] = in_array;\n    in_array_2[1] = in_array_1;\n    main_1();\n    let _e12 = frag.position;\n    let _e14 = frag.a;\n    let _e17 = out_array[0];\n    let _e19 = out_array[1];\n    return FragmentOutput(_e12, _e14, _e17, _e19);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-double-math-functions.frag.wgsl",
    "content": "fn main_1() {\n    var a: vec4<f64> = vec4(1.0lf);\n    var b: vec4<f64> = vec4(2.0lf);\n    var m: mat4x4<f64>;\n    var i: i32 = 5i;\n    var ceilOut: vec4<f64>;\n    var roundOut: vec4<f64>;\n    var floorOut: vec4<f64>;\n    var fractOut: vec4<f64>;\n    var truncOut: vec4<f64>;\n    var absOut: vec4<f64>;\n    var sqrtOut: vec4<f64>;\n    var inversesqrtOut: vec4<f64>;\n    var signOut: vec4<f64>;\n    var transposeOut: mat4x4<f64>;\n    var normalizeOut: vec4<f64>;\n    var lengthOut: f64;\n    var determinantOut: f64;\n    var modOut: f64;\n    var dotOut: f64;\n    var maxOut: vec4<f64>;\n    var minOut: vec4<f64>;\n    var reflectOut: vec4<f64>;\n    var crossOut: vec3<f64>;\n    var distanceOut: f64;\n    var stepOut: vec4<f64>;\n    var ldexpOut: f64;\n    var smoothStepScalar: f64;\n    var smoothStepVector: vec4<f64>;\n    var smoothStepMixed: vec4<f64>;\n\n    let _e6 = a;\n    let _e7 = b;\n    let _e8 = a;\n    let _e9 = b;\n    m = mat4x4<f64>(vec4<f64>(_e6.x, _e6.y, _e6.z, _e6.w), vec4<f64>(_e7.x, _e7.y, _e7.z, _e7.w), vec4<f64>(_e8.x, _e8.y, _e8.z, _e8.w), vec4<f64>(_e9.x, _e9.y, _e9.z, _e9.w));\n    let _e34 = a;\n    ceilOut = ceil(_e34);\n    let _e37 = a;\n    roundOut = round(_e37);\n    let _e40 = a;\n    floorOut = floor(_e40);\n    let _e43 = a;\n    fractOut = fract(_e43);\n    let _e46 = a;\n    truncOut = trunc(_e46);\n    let _e49 = a;\n    absOut = abs(_e49);\n    let _e52 = a;\n    sqrtOut = sqrt(_e52);\n    let _e55 = a;\n    inversesqrtOut = inverseSqrt(_e55);\n    let _e58 = a;\n    signOut = sign(_e58);\n    let _e61 = m;\n    transposeOut = transpose(_e61);\n    let _e64 = a;\n    normalizeOut = normalize(_e64);\n    let _e67 = a;\n    lengthOut = length(_e67);\n    let _e70 = m;\n    determinantOut = determinant(_e70);\n    let _e73 = a;\n    let _e75 = b;\n    modOut = (_e73.x - (floor((_e73.x / _e75.x)) * _e75.x));\n    let _e82 = a;\n    let _e83 = b;\n    dotOut = dot(_e82, _e83);\n    let _e86 = a;\n    let _e87 = b;\n    maxOut = max(_e86, _e87);\n    let _e90 = a;\n    let _e91 = b;\n    minOut = min(_e90, _e91);\n    let _e94 = a;\n    let _e95 = b;\n    reflectOut = reflect(_e94, _e95);\n    let _e98 = a;\n    let _e100 = b;\n    crossOut = cross(_e98.xyz, _e100.xyz);\n    let _e104 = a;\n    let _e105 = b;\n    distanceOut = distance(_e104, _e105);\n    let _e108 = a;\n    let _e109 = b;\n    stepOut = step(_e108, _e109);\n    let _e112 = a;\n    let _e114 = i;\n    ldexpOut = ldexp(_e112.x, _e114);\n    smoothStepScalar = f64(smoothstep(0f, 1f, 0.5f));\n    smoothStepVector = smoothstep(vec4(0.0lf), vec4(1.0lf), vec4(0.5lf));\n    smoothStepMixed = smoothstep(vec4(0.0lf), vec4(1.0lf), vec4(0.5lf));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-dual-source-blending.frag.wgsl",
    "content": "enable dual_source_blending;\n\nstruct FragmentOutput {\n    @location(0) @blend_src(0) output0_: vec4<f32>,\n    @location(0) @blend_src(1) output1_: vec4<f32>,\n}\n\nvar<private> output0_: vec4<f32>;\nvar<private> output1_: vec4<f32>;\n\nfn main_1() {\n    output0_ = vec4<f32>(1f, 0f, 1f, 0f);\n    output1_ = vec4<f32>(0f, 1f, 0f, 1f);\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = output0_;\n    let _e3 = output1_;\n    return FragmentOutput(_e1, _e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-expressions.frag.wgsl",
    "content": "struct BST {\n    data: i32,\n}\n\nstruct a_buf {\n    a: array<f32>,\n}\n\nstruct TestStruct {\n    array: array<vec4<u32>, 2>,\n}\n\nstruct FragmentOutput {\n    @location(0) o_color: vec4<f32>,\n}\n\nconst strct: TestStruct = TestStruct(array<vec4<u32>, 2>(vec4(0u), vec4(1u)));\n\nvar<private> global: f32;\n@group(0) @binding(0) \nvar<storage, read_write> global_1: a_buf;\nvar<private> o_color: vec4<f32>;\n\nfn testBinOpVecFloat(a: vec4<f32>, b: f32) {\n    var a_1: vec4<f32>;\n    var b_1: f32;\n    var v: vec4<f32>;\n\n    a_1 = a;\n    b_1 = b;\n    let _e5 = a_1;\n    v = (_e5 * 2f);\n    let _e8 = a_1;\n    v = (_e8 / vec4(2f));\n    let _e12 = a_1;\n    v = (_e12 + vec4(2f));\n    let _e16 = a_1;\n    v = (_e16 - vec4(2f));\n    return;\n}\n\nfn testBinOpFloatVec(a_2: vec4<f32>, b_2: f32) {\n    var a_3: vec4<f32>;\n    var b_3: f32;\n    var v_1: vec4<f32>;\n\n    a_3 = a_2;\n    b_3 = b_2;\n    let _e5 = a_3;\n    let _e6 = b_3;\n    v_1 = (_e5 * _e6);\n    let _e8 = a_3;\n    let _e9 = b_3;\n    v_1 = (_e8 / vec4(_e9));\n    let _e12 = a_3;\n    let _e13 = b_3;\n    v_1 = (_e12 + vec4(_e13));\n    let _e16 = a_3;\n    let _e17 = b_3;\n    v_1 = (_e16 - vec4(_e17));\n    return;\n}\n\nfn testBinOpIVecInt(a_4: vec4<i32>, b_4: i32) {\n    var a_5: vec4<i32>;\n    var b_5: i32;\n    var v_2: vec4<i32>;\n\n    a_5 = a_4;\n    b_5 = b_4;\n    let _e5 = a_5;\n    let _e6 = b_5;\n    v_2 = (_e5 * _e6);\n    let _e8 = a_5;\n    let _e9 = b_5;\n    v_2 = (_e8 / vec4(_e9));\n    let _e12 = a_5;\n    let _e13 = b_5;\n    v_2 = (_e12 + vec4(_e13));\n    let _e16 = a_5;\n    let _e17 = b_5;\n    v_2 = (_e16 - vec4(_e17));\n    let _e20 = a_5;\n    let _e21 = b_5;\n    v_2 = (_e20 & vec4(_e21));\n    let _e24 = a_5;\n    let _e25 = b_5;\n    v_2 = (_e24 | vec4(_e25));\n    let _e28 = a_5;\n    let _e29 = b_5;\n    v_2 = (_e28 ^ vec4(_e29));\n    let _e32 = a_5;\n    let _e33 = b_5;\n    v_2 = (_e32 >> vec4(u32(_e33)));\n    let _e37 = a_5;\n    let _e38 = b_5;\n    v_2 = (_e37 << vec4(u32(_e38)));\n    return;\n}\n\nfn testBinOpIntIVec(a_6: i32, b_6: vec4<i32>) {\n    var a_7: i32;\n    var b_7: vec4<i32>;\n    var v_3: vec4<i32>;\n\n    a_7 = a_6;\n    b_7 = b_6;\n    let _e5 = a_7;\n    let _e6 = b_7;\n    v_3 = (_e5 * _e6);\n    let _e8 = a_7;\n    let _e9 = b_7;\n    v_3 = (vec4(_e8) + _e9);\n    let _e12 = a_7;\n    let _e13 = b_7;\n    v_3 = (vec4(_e12) - _e13);\n    let _e16 = a_7;\n    let _e17 = b_7;\n    v_3 = (vec4(_e16) & _e17);\n    let _e20 = a_7;\n    let _e21 = b_7;\n    v_3 = (vec4(_e20) | _e21);\n    let _e24 = a_7;\n    let _e25 = b_7;\n    v_3 = (vec4(_e24) ^ _e25);\n    return;\n}\n\nfn testBinOpUVecUint(a_8: vec4<u32>, b_8: u32) {\n    var a_9: vec4<u32>;\n    var b_9: u32;\n    var v_4: vec4<u32>;\n\n    a_9 = a_8;\n    b_9 = b_8;\n    let _e5 = a_9;\n    let _e6 = b_9;\n    v_4 = (_e5 * _e6);\n    let _e8 = a_9;\n    let _e9 = b_9;\n    v_4 = (_e8 / vec4(_e9));\n    let _e12 = a_9;\n    let _e13 = b_9;\n    v_4 = (_e12 + vec4(_e13));\n    let _e16 = a_9;\n    let _e17 = b_9;\n    v_4 = (_e16 - vec4(_e17));\n    let _e20 = a_9;\n    let _e21 = b_9;\n    v_4 = (_e20 & vec4(_e21));\n    let _e24 = a_9;\n    let _e25 = b_9;\n    v_4 = (_e24 | vec4(_e25));\n    let _e28 = a_9;\n    let _e29 = b_9;\n    v_4 = (_e28 ^ vec4(_e29));\n    let _e32 = a_9;\n    let _e33 = b_9;\n    v_4 = (_e32 >> vec4(_e33));\n    let _e36 = a_9;\n    let _e37 = b_9;\n    v_4 = (_e36 << vec4(_e37));\n    return;\n}\n\nfn testBinOpUintUVec(a_10: u32, b_10: vec4<u32>) {\n    var a_11: u32;\n    var b_11: vec4<u32>;\n    var v_5: vec4<u32>;\n\n    a_11 = a_10;\n    b_11 = b_10;\n    let _e5 = a_11;\n    let _e6 = b_11;\n    v_5 = (_e5 * _e6);\n    let _e8 = a_11;\n    let _e9 = b_11;\n    v_5 = (vec4(_e8) + _e9);\n    let _e12 = a_11;\n    let _e13 = b_11;\n    v_5 = (vec4(_e12) - _e13);\n    let _e16 = a_11;\n    let _e17 = b_11;\n    v_5 = (vec4(_e16) & _e17);\n    let _e20 = a_11;\n    let _e21 = b_11;\n    v_5 = (vec4(_e20) | _e21);\n    let _e24 = a_11;\n    let _e25 = b_11;\n    v_5 = (vec4(_e24) ^ _e25);\n    return;\n}\n\nfn testBinOpMatMat(a_12: mat3x3<f32>, b_12: mat3x3<f32>) {\n    var a_13: mat3x3<f32>;\n    var b_13: mat3x3<f32>;\n    var v_6: mat3x3<f32>;\n    var c: bool;\n\n    a_13 = a_12;\n    b_13 = b_12;\n    let _e6 = a_13;\n    let _e7 = b_13;\n    v_6 = mat3x3<f32>((_e6[0] / _e7[0]), (_e6[1] / _e7[1]), (_e6[2] / _e7[2]));\n    let _e18 = a_13;\n    let _e19 = b_13;\n    v_6 = (_e18 * _e19);\n    let _e21 = a_13;\n    let _e22 = b_13;\n    v_6 = (_e21 + _e22);\n    let _e24 = a_13;\n    let _e25 = b_13;\n    v_6 = (_e24 - _e25);\n    let _e27 = a_13;\n    let _e28 = b_13;\n    c = (all((_e27[2] == _e28[2])) && (all((_e27[1] == _e28[1])) && all((_e27[0] == _e28[0]))));\n    let _e43 = a_13;\n    let _e44 = b_13;\n    c = (any((_e43[2] != _e44[2])) || (any((_e43[1] != _e44[1])) || any((_e43[0] != _e44[0]))));\n    return;\n}\n\nfn testBinOpMatFloat(a_14: f32, b_14: mat3x3<f32>) {\n    var a_15: f32;\n    var b_15: mat3x3<f32>;\n    var v_7: mat3x3<f32>;\n\n    a_15 = a_14;\n    b_15 = b_14;\n    let _e5 = a_15;\n    let _e6 = b_15;\n    let _e7 = vec3(_e5);\n    v_7 = mat3x3<f32>((_e7 / _e6[0]), (_e7 / _e6[1]), (_e7 / _e6[2]));\n    let _e15 = a_15;\n    let _e16 = b_15;\n    v_7 = (_e15 * _e16);\n    let _e18 = a_15;\n    let _e19 = b_15;\n    let _e20 = vec3(_e18);\n    v_7 = mat3x3<f32>((_e20 + _e19[0]), (_e20 + _e19[1]), (_e20 + _e19[2]));\n    let _e28 = a_15;\n    let _e29 = b_15;\n    let _e30 = vec3(_e28);\n    v_7 = mat3x3<f32>((_e30 - _e29[0]), (_e30 - _e29[1]), (_e30 - _e29[2]));\n    let _e38 = b_15;\n    let _e39 = a_15;\n    let _e40 = vec3(_e39);\n    v_7 = mat3x3<f32>((_e38[0] / _e40), (_e38[1] / _e40), (_e38[2] / _e40));\n    let _e48 = b_15;\n    let _e49 = a_15;\n    v_7 = (_e48 * _e49);\n    let _e51 = b_15;\n    let _e52 = a_15;\n    let _e53 = vec3(_e52);\n    v_7 = mat3x3<f32>((_e51[0] + _e53), (_e51[1] + _e53), (_e51[2] + _e53));\n    let _e61 = b_15;\n    let _e62 = a_15;\n    let _e63 = vec3(_e62);\n    v_7 = mat3x3<f32>((_e61[0] - _e63), (_e61[1] - _e63), (_e61[2] - _e63));\n    return;\n}\n\nfn testUnaryOpMat(a_16: mat3x3<f32>) {\n    var a_17: mat3x3<f32>;\n    var v_8: mat3x3<f32>;\n\n    a_17 = a_16;\n    let _e3 = a_17;\n    v_8 = (-1f * _e3);\n    let _e6 = a_17;\n    let _e8 = vec3(1f);\n    let _e10 = (_e6 - mat3x3<f32>(_e8, _e8, _e8));\n    a_17 = _e10;\n    v_8 = _e10;\n    let _e11 = a_17;\n    let _e13 = vec3(1f);\n    a_17 = (_e11 - mat3x3<f32>(_e13, _e13, _e13));\n    v_8 = _e11;\n    return;\n}\n\nfn testStructConstructor() {\n    var tree: BST = BST(1i);\n\n    return;\n}\n\nfn testNonScalarToScalarConstructor() {\n    var f: f32 = 1f;\n\n    return;\n}\n\nfn testArrayConstructor() {\n    var tree_1: array<f32, 1> = array<f32, 1>(0f);\n\n    return;\n}\n\nfn testFreestandingConstructor() {\n    return;\n}\n\nfn testNonImplicitCastVectorCast() {\n    var a_18: u32 = 1u;\n    var b_16: vec4<i32>;\n\n    let _e2 = a_18;\n    b_16 = vec4(i32(_e2));\n    return;\n}\n\nfn privatePointer(a_19: ptr<function, f32>) {\n    return;\n}\n\nfn ternary(a_20: bool) {\n    var a_21: bool;\n    var local: u32;\n    var b_17: u32;\n    var local_1: u32;\n    var c_1: u32;\n    var local_2: u32;\n    var local_3: u32;\n    var local_4: u32;\n    var nested: u32;\n\n    a_21 = a_20;\n    let _e2 = a_21;\n    if _e2 {\n        local = 0u;\n    } else {\n        local = 1u;\n    }\n    let _e6 = local;\n    b_17 = _e6;\n    let _e8 = a_21;\n    if _e8 {\n        local_1 = 0u;\n    } else {\n        local_1 = 1u;\n    }\n    let _e12 = local_1;\n    c_1 = _e12;\n    let _e14 = a_21;\n    if _e14 {\n        let _e15 = a_21;\n        if _e15 {\n            let _e16 = a_21;\n            if _e16 {\n                local_2 = 2u;\n            } else {\n                local_2 = 3u;\n            }\n            let _e20 = local_2;\n            local_3 = _e20;\n        } else {\n            local_3 = 4u;\n        }\n        let _e23 = local_3;\n        local_4 = _e23;\n    } else {\n        local_4 = 5u;\n    }\n    let _e26 = local_4;\n    nested = _e26;\n    return;\n}\n\nfn testMatrixMultiplication(a_22: mat4x3<f32>, b_18: mat4x4<f32>) {\n    var a_23: mat4x3<f32>;\n    var b_19: mat4x4<f32>;\n    var c_2: mat4x3<f32>;\n\n    a_23 = a_22;\n    b_19 = b_18;\n    let _e4 = a_23;\n    let _e5 = b_19;\n    c_2 = (_e4 * _e5);\n    return;\n}\n\nfn testLength() {\n    var len: i32;\n\n    len = i32(arrayLength((&global_1.a)));\n    return;\n}\n\nfn testConstantLength(a_24: array<f32, 4>) {\n    var a_25: array<f32, 4>;\n    var len_1: i32 = 4i;\n\n    a_25 = a_24;\n    return;\n}\n\nfn indexConstantNonConstantIndex(i: i32) {\n    var i_1: i32;\n    var local_5: TestStruct = strct;\n    var a_26: vec4<u32>;\n\n    i_1 = i;\n    let _e2 = i_1;\n    let _e7 = local_5.array[_e2];\n    a_26 = _e7;\n    return;\n}\n\nfn testSwizzleWrites(a_27: vec3<f32>) {\n    var a_28: vec3<f32>;\n\n    a_28 = a_27;\n    a_28.z = 3f;\n    a_28.x = 4f;\n    let _e6 = a_28;\n    let _e9 = (_e6.xy * 5f);\n    a_28.x = _e9.x;\n    a_28.y = _e9.y;\n    let _e14 = a_28;\n    let _e18 = (_e14.zy + vec2(1f));\n    a_28.z = _e18.x;\n    a_28.y = _e18.y;\n    return;\n}\n\nfn main_1() {\n    var local_6: f32;\n\n    testBinOpVecFloat(vec4(0f), 1f);\n    testBinOpFloatVec(vec4(0f), 1f);\n    testBinOpIVecInt(vec4(0i), 1i);\n    testBinOpIntIVec(1i, vec4(0i));\n    testBinOpUVecUint(vec4(0u), 1u);\n    testBinOpUintUVec(1u, vec4(0u));\n    testBinOpMatMat(mat3x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f)), mat3x3<f32>(vec3<f32>(1f, 0f, 0f), vec3<f32>(0f, 1f, 0f), vec3<f32>(0f, 0f, 1f)));\n    testBinOpMatFloat(1f, mat3x3<f32>(vec3<f32>(1f, 0f, 0f), vec3<f32>(0f, 1f, 0f), vec3<f32>(0f, 0f, 1f)));\n    testUnaryOpMat(mat3x3<f32>(vec3<f32>(1f, 0f, 0f), vec3<f32>(0f, 1f, 0f), vec3<f32>(0f, 0f, 1f)));\n    testStructConstructor();\n    testNonScalarToScalarConstructor();\n    testArrayConstructor();\n    testFreestandingConstructor();\n    testNonImplicitCastVectorCast();\n    let _e45 = global;\n    local_6 = _e45;\n    privatePointer((&local_6));\n    let _e47 = local_6;\n    global = _e47;\n    ternary(false);\n    testMatrixMultiplication(mat4x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f)), mat4x4<f32>(vec4<f32>(1f, 0f, 0f, 0f), vec4<f32>(0f, 1f, 0f, 0f), vec4<f32>(0f, 0f, 1f, 0f), vec4<f32>(0f, 0f, 0f, 1f)));\n    testLength();\n    testConstantLength(array<f32, 4>(0f, 1f, 2f, 3f));\n    indexConstantNonConstantIndex(1i);\n    testSwizzleWrites(vec3(0f));\n    o_color.x = 1f;\n    o_color.y = 1f;\n    o_color.z = 1f;\n    o_color.w = 1f;\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = o_color;\n    return FragmentOutput(_e1);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-f16-glsl.comp.wgsl",
    "content": "enable f16;\n\nstruct A {\n    a_1_: f16,\n    a_vec2_: vec2<f16>,\n    a_vec3_: vec3<f16>,\n    a_vec4_: vec4<f16>,\n}\n\nstruct B {\n    b_1_: f16,\n    b_vec2_: vec2<f16>,\n    b_vec3_: vec3<f16>,\n    b_vec4_: vec4<f16>,\n    b_mat2_: mat2x2<f16>,\n    b_mat2x3_: mat2x3<f16>,\n    b_mat2x4_: mat2x4<f16>,\n    b_mat3x2_: mat3x2<f16>,\n    b_mat3_: mat3x3<f16>,\n    b_mat3x4_: mat3x4<f16>,\n    b_mat4x2_: mat4x2<f16>,\n    b_mat4x3_: mat4x3<f16>,\n    b_mat4_: mat4x4<f16>,\n}\n\n@group(0) @binding(0) \nvar<uniform> global: A;\n@group(0) @binding(1) \nvar<storage, read_write> global_1: B;\n\nfn main_1() {\n    let _e16 = global.a_1_;\n    global_1.b_1_ = _e16;\n    let _e17 = global.a_vec2_;\n    global_1.b_vec2_ = _e17;\n    let _e18 = global.a_vec3_;\n    global_1.b_vec3_ = _e18;\n    let _e19 = global.a_vec4_;\n    global_1.b_vec4_ = _e19;\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-fma.frag.wgsl",
    "content": "struct Mat4x3_ {\n    mx: vec4<f32>,\n    my: vec4<f32>,\n    mz: vec4<f32>,\n}\n\nstruct FragmentOutput {\n    @location(0) o_color: vec4<f32>,\n}\n\nvar<private> o_color: vec4<f32>;\n\nfn Fma(d: ptr<function, Mat4x3_>, m: Mat4x3_, s: f32) {\n    var m_1: Mat4x3_;\n    var s_1: f32;\n\n    m_1 = m;\n    s_1 = s;\n    let _e6 = (*d);\n    let _e8 = m_1;\n    let _e10 = s_1;\n    (*d).mx = (_e6.mx + (_e8.mx * _e10));\n    let _e14 = (*d);\n    let _e16 = m_1;\n    let _e18 = s_1;\n    (*d).my = (_e14.my + (_e16.my * _e18));\n    let _e22 = (*d);\n    let _e24 = m_1;\n    let _e26 = s_1;\n    (*d).mz = (_e22.mz + (_e24.mz * _e26));\n    return;\n}\n\nfn main_1() {\n    var m1_: Mat4x3_ = Mat4x3_(vec4(0f), vec4(1f), vec4(2f));\n    var m2_: Mat4x3_ = Mat4x3_(vec4(0f), vec4(1f), vec4(2f));\n\n    let _e17 = m2_;\n    Fma((&m1_), _e17, 2f);\n    o_color.x = 1f;\n    o_color.y = 1f;\n    o_color.z = 1f;\n    o_color.w = 1f;\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = o_color;\n    return FragmentOutput(_e1);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-functions_call.frag.wgsl",
    "content": "fn swizzleCallee(a: ptr<function, vec2<f32>>) {\n    return;\n}\n\nfn swizzleCaller(a_1: vec3<f32>) {\n    var a_2: vec3<f32>;\n    var local: vec2<f32>;\n\n    a_2 = a_1;\n    let _e2 = a_2;\n    local = _e2.xz;\n    swizzleCallee((&local));\n    let _e9 = local.x;\n    a_2.x = _e9;\n    let _e10 = local.y;\n    a_2.z = _e10;\n    return;\n}\n\nfn outImplicitCastCallee(a_3: ptr<function, u32>) {\n    return;\n}\n\nfn outImplicitCastCaller(a_4: f32) {\n    var a_5: f32;\n    var local_1: u32;\n\n    a_5 = a_4;\n    outImplicitCastCallee((&local_1));\n    let _e3 = local_1;\n    a_5 = f32(_e3);\n    return;\n}\n\nfn swizzleImplicitCastCallee(a_6: ptr<function, vec2<u32>>) {\n    return;\n}\n\nfn swizzleImplicitCastCaller(a_7: vec3<f32>) {\n    var a_8: vec3<f32>;\n    var local_2: vec2<u32>;\n\n    a_8 = a_7;\n    swizzleImplicitCastCallee((&local_2));\n    let _e7 = local_2.x;\n    a_8.x = f32(_e7);\n    let _e9 = local_2.y;\n    a_8.z = f32(_e9);\n    return;\n}\n\nfn main_1() {\n    var a_9: u32;\n    var b: vec2<u32>;\n\n    swizzleCaller(vec3(0f));\n    outImplicitCastCallee((&a_9));\n    outImplicitCastCaller(1f);\n    swizzleImplicitCastCallee((&b));\n    swizzleImplicitCastCaller(vec3(0f));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-global-constant-array.frag.wgsl",
    "content": "const array_: array<f32, 2> = array<f32, 2>(1f, 2f);\n\nfn main_1() {\n    var local: array<f32, 2> = array_;\n\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-images.frag.wgsl",
    "content": "@group(0) @binding(0) \nvar img1D: texture_storage_1d<rgba8unorm,read_write>;\n@group(0) @binding(1) \nvar img2D: texture_storage_2d<rgba8unorm,read_write>;\n@group(0) @binding(2) \nvar img3D: texture_storage_3d<rgba8unorm,read_write>;\n@group(0) @binding(5) \nvar img2DArray: texture_storage_2d_array<rgba8unorm,read_write>;\n@group(0) @binding(7) \nvar imgReadOnly: texture_storage_2d<rgba8unorm,read>;\n@group(0) @binding(8) \nvar imgWriteOnly: texture_storage_2d<rgba8unorm,write>;\n@group(0) @binding(9) \nvar imgWriteReadOnly: texture_storage_2d<rgba8unorm,write>;\n\nfn testImg1D(coord: i32) {\n    var coord_1: i32;\n    var size: i32;\n    var c: vec4<f32>;\n\n    coord_1 = coord;\n    let _e3 = textureDimensions(img1D);\n    size = i32(_e3);\n    let _e6 = coord_1;\n    textureStore(img1D, _e6, vec4(2f));\n    let _e9 = coord_1;\n    let _e10 = textureLoad(img1D, _e9);\n    c = _e10;\n    return;\n}\n\nfn testImg2D(coord_2: vec2<i32>) {\n    var coord_3: vec2<i32>;\n    var size_1: vec2<f32>;\n    var c_1: vec4<f32>;\n\n    coord_3 = coord_2;\n    let _e3 = textureDimensions(img2D);\n    size_1 = vec2<f32>(vec2<i32>(_e3));\n    let _e7 = coord_3;\n    let _e8 = textureLoad(img2D, _e7);\n    c_1 = _e8;\n    let _e10 = coord_3;\n    textureStore(img2D, _e10, vec4(2f));\n    return;\n}\n\nfn testImg2DArray(coord_4: vec3<i32>) {\n    var coord_5: vec3<i32>;\n    var size_2: vec3<f32>;\n    var c_2: vec4<f32>;\n\n    coord_5 = coord_4;\n    let _e3 = textureDimensions(img2DArray);\n    let _e6 = textureNumLayers(img2DArray);\n    size_2 = vec3<f32>(vec3<i32>(vec3<u32>(_e3.x, _e3.y, _e6)));\n    let _e11 = coord_5;\n    let _e14 = textureLoad(img2DArray, _e11.xy, _e11.z);\n    c_2 = _e14;\n    let _e16 = coord_5;\n    textureStore(img2DArray, _e16.xy, _e16.z, vec4(2f));\n    return;\n}\n\nfn testImg3D(coord_6: vec3<i32>) {\n    var coord_7: vec3<i32>;\n    var size_3: vec3<f32>;\n    var c_3: vec4<f32>;\n\n    coord_7 = coord_6;\n    let _e3 = textureDimensions(img3D);\n    size_3 = vec3<f32>(vec3<i32>(_e3));\n    let _e7 = coord_7;\n    let _e8 = textureLoad(img3D, _e7);\n    c_3 = _e8;\n    let _e10 = coord_7;\n    textureStore(img3D, _e10, vec4(2f));\n    return;\n}\n\nfn testImgReadOnly(coord_8: vec2<i32>) {\n    var coord_9: vec2<i32>;\n    var size_4: vec2<f32>;\n    var c_4: vec4<f32>;\n\n    coord_9 = coord_8;\n    let _e4 = textureDimensions(img2D);\n    size_4 = vec2<f32>(vec2<i32>(_e4));\n    let _e8 = coord_9;\n    let _e9 = textureLoad(imgReadOnly, _e8);\n    c_4 = _e9;\n    return;\n}\n\nfn testImgWriteOnly(coord_10: vec2<i32>) {\n    var coord_11: vec2<i32>;\n    var size_5: vec2<f32>;\n\n    coord_11 = coord_10;\n    let _e4 = textureDimensions(img2D);\n    size_5 = vec2<f32>(vec2<i32>(_e4));\n    let _e8 = coord_11;\n    textureStore(imgWriteOnly, _e8, vec4(2f));\n    return;\n}\n\nfn testImgWriteReadOnly(coord_12: vec2<i32>) {\n    var coord_13: vec2<i32>;\n    var size_6: vec2<f32>;\n\n    coord_13 = coord_12;\n    let _e3 = textureDimensions(imgWriteReadOnly);\n    size_6 = vec2<f32>(vec2<i32>(_e3));\n    return;\n}\n\nfn main_1() {\n    testImg1D(1i);\n    testImg2D(vec2(0i));\n    testImg2DArray(vec3(0i));\n    testImg3D(vec3(0i));\n    testImgReadOnly(vec2(0i));\n    testImgWriteOnly(vec2(0i));\n    testImgWriteReadOnly(vec2(0i));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-inverse-polyfill.frag.wgsl",
    "content": "fn main_1() {\n    var a4_: vec4<f32> = vec4(1f);\n    var b4_: vec4<f32> = vec4(2f);\n    var m4_: mat4x4<f32>;\n    var a3_: vec3<f32> = vec3(1f);\n    var b3_: vec3<f32> = vec3(2f);\n    var m3_: mat3x3<f32>;\n    var m2_: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var m4_inverse: mat4x4<f32>;\n    var m3_inverse: mat3x3<f32>;\n    var m2_inverse: mat2x2<f32>;\n\n    let _e6 = a4_;\n    let _e7 = b4_;\n    let _e8 = a4_;\n    let _e9 = b4_;\n    m4_ = mat4x4<f32>(vec4<f32>(_e6.x, _e6.y, _e6.z, _e6.w), vec4<f32>(_e7.x, _e7.y, _e7.z, _e7.w), vec4<f32>(_e8.x, _e8.y, _e8.z, _e8.w), vec4<f32>(_e9.x, _e9.y, _e9.z, _e9.w));\n    let _e38 = a3_;\n    let _e39 = b3_;\n    let _e40 = a3_;\n    m3_ = mat3x3<f32>(vec3<f32>(_e38.x, _e38.y, _e38.z), vec3<f32>(_e39.x, _e39.y, _e39.z), vec3<f32>(_e40.x, _e40.y, _e40.z));\n    let _e63 = m4_;\n    m4_inverse = _naga_inverse_4x4_f32(_e63);\n    let _e66 = m3_;\n    m3_inverse = _naga_inverse_3x3_f32(_e66);\n    let _e69 = m2_;\n    m2_inverse = _naga_inverse_2x2_f32(_e69);\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n\nfn _naga_inverse_4x4_f32(m: mat4x4<f32>) -> mat4x4<f32> {\n   let sub_factor00: f32 = m[2][2] * m[3][3] - m[3][2] * m[2][3];\n   let sub_factor01: f32 = m[2][1] * m[3][3] - m[3][1] * m[2][3];\n   let sub_factor02: f32 = m[2][1] * m[3][2] - m[3][1] * m[2][2];\n   let sub_factor03: f32 = m[2][0] * m[3][3] - m[3][0] * m[2][3];\n   let sub_factor04: f32 = m[2][0] * m[3][2] - m[3][0] * m[2][2];\n   let sub_factor05: f32 = m[2][0] * m[3][1] - m[3][0] * m[2][1];\n   let sub_factor06: f32 = m[1][2] * m[3][3] - m[3][2] * m[1][3];\n   let sub_factor07: f32 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor08: f32 = m[1][1] * m[3][2] - m[3][1] * m[1][2];\n   let sub_factor09: f32 = m[1][0] * m[3][3] - m[3][0] * m[1][3];\n   let sub_factor10: f32 = m[1][0] * m[3][2] - m[3][0] * m[1][2];\n   let sub_factor11: f32 = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n   let sub_factor12: f32 = m[1][0] * m[3][1] - m[3][0] * m[1][1];\n   let sub_factor13: f32 = m[1][2] * m[2][3] - m[2][2] * m[1][3];\n   let sub_factor14: f32 = m[1][1] * m[2][3] - m[2][1] * m[1][3];\n   let sub_factor15: f32 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n   let sub_factor16: f32 = m[1][0] * m[2][3] - m[2][0] * m[1][3];\n   let sub_factor17: f32 = m[1][0] * m[2][2] - m[2][0] * m[1][2];\n   let sub_factor18: f32 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n\n   var adj: mat4x4<f32>;\n   adj[0][0] =   (m[1][1] * sub_factor00 - m[1][2] * sub_factor01 + m[1][3] * sub_factor02);\n   adj[1][0] = - (m[1][0] * sub_factor00 - m[1][2] * sub_factor03 + m[1][3] * sub_factor04);\n   adj[2][0] =   (m[1][0] * sub_factor01 - m[1][1] * sub_factor03 + m[1][3] * sub_factor05);\n   adj[3][0] = - (m[1][0] * sub_factor02 - m[1][1] * sub_factor04 + m[1][2] * sub_factor05);\n   adj[0][1] = - (m[0][1] * sub_factor00 - m[0][2] * sub_factor01 + m[0][3] * sub_factor02);\n   adj[1][1] =   (m[0][0] * sub_factor00 - m[0][2] * sub_factor03 + m[0][3] * sub_factor04);\n   adj[2][1] = - (m[0][0] * sub_factor01 - m[0][1] * sub_factor03 + m[0][3] * sub_factor05);\n   adj[3][1] =   (m[0][0] * sub_factor02 - m[0][1] * sub_factor04 + m[0][2] * sub_factor05);\n   adj[0][2] =   (m[0][1] * sub_factor06 - m[0][2] * sub_factor07 + m[0][3] * sub_factor08);\n   adj[1][2] = - (m[0][0] * sub_factor06 - m[0][2] * sub_factor09 + m[0][3] * sub_factor10);\n   adj[2][2] =   (m[0][0] * sub_factor11 - m[0][1] * sub_factor09 + m[0][3] * sub_factor12);\n   adj[3][2] = - (m[0][0] * sub_factor08 - m[0][1] * sub_factor10 + m[0][2] * sub_factor12);\n   adj[0][3] = - (m[0][1] * sub_factor13 - m[0][2] * sub_factor14 + m[0][3] * sub_factor15);\n   adj[1][3] =   (m[0][0] * sub_factor13 - m[0][2] * sub_factor16 + m[0][3] * sub_factor17);\n   adj[2][3] = - (m[0][0] * sub_factor14 - m[0][1] * sub_factor16 + m[0][3] * sub_factor18);\n   adj[3][3] =   (m[0][0] * sub_factor15 - m[0][1] * sub_factor17 + m[0][2] * sub_factor18);\n\n   let det = (m[0][0] * adj[0][0] + m[0][1] * adj[1][0] + m[0][2] * adj[2][0] + m[0][3] * adj[3][0]);\n\n   return adj * (1 / det);\n}\n\nfn _naga_inverse_3x3_f32(m: mat3x3<f32>) -> mat3x3<f32> {\n    var adj: mat3x3<f32>;\n\n    adj[0][0] =   (m[1][1] * m[2][2] - m[2][1] * m[1][2]);\n    adj[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n    adj[2][0] =   (m[1][0] * m[2][1] - m[2][0] * m[1][1]);\n    adj[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n    adj[1][1] =   (m[0][0] * m[2][2] - m[2][0] * m[0][2]);\n    adj[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n    adj[0][2] =   (m[0][1] * m[1][2] - m[1][1] * m[0][2]);\n    adj[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n    adj[2][2] =   (m[0][0] * m[1][1] - m[1][0] * m[0][1]);\n\n    let det: f32 = (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])\n    \t\t- m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])\n    \t\t+ m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]));\n\n    return adj * (1 / det);\n}\n\nfn _naga_inverse_2x2_f32(m: mat2x2<f32>) -> mat2x2<f32> {\n    var adj: mat2x2<f32>;\n    adj[0][0] = m[1][1];\n    adj[0][1] = -m[0][1];\n    adj[1][0] = -m[1][0];\n    adj[1][1] = m[0][0];\n\n    let det: f32 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n    return adj * (1 / det);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-local-var-init-in-loop.comp.wgsl",
    "content": "fn main_1() {\n    var sum: vec4<f32> = vec4(0f);\n    var i: i32 = 0i;\n    var a: vec4<f32>;\n\n    loop {\n        let _e5 = i;\n        if !((_e5 < 4i)) {\n            break;\n        }\n        {\n            a = vec4(1f);\n            let _e15 = sum;\n            let _e16 = a;\n            sum = (_e15 + _e16);\n        }\n        continuing {\n            let _e9 = i;\n            i = (_e9 + 1i);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-long-form-matrix.frag.wgsl",
    "content": "fn main_1() {\n    var splat: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 0f), vec2<f32>(0f, 1f));\n    var normal: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 1f), vec2<f32>(2f, 2f));\n    var from_matrix: mat2x4<f32> = mat2x4<f32>(vec4<f32>(1f, 0f, 0f, 0f), vec4<f32>(0f, 1f, 0f, 0f));\n    var a: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var b: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var c: mat3x3<f32> = mat3x3<f32>(vec3<f32>(1f, 2f, 3f), vec3<f32>(1f, 1f, 1f), vec3<f32>(1f, 1f, 1f));\n    var d: mat3x3<f32> = mat3x3<f32>(vec3<f32>(2f, 2f, 1f), vec3<f32>(1f, 1f, 1f), vec3<f32>(1f, 1f, 1f));\n    var e: mat4x4<f32> = mat4x4<f32>(vec4<f32>(2f, 2f, 1f, 1f), vec4<f32>(1f, 1f, 2f, 2f), vec4<f32>(1f, 1f, 1f, 1f), vec4<f32>(1f, 1f, 1f, 1f));\n\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-math-functions.frag.wgsl",
    "content": "fn main_1() {\n    var a: vec4<f32> = vec4(1f);\n    var b: vec4<f32> = vec4(2f);\n    var m: mat4x4<f32>;\n    var i: i32 = 5i;\n    var ceilOut: vec4<f32>;\n    var roundOut: vec4<f32>;\n    var floorOut: vec4<f32>;\n    var fractOut: vec4<f32>;\n    var truncOut: vec4<f32>;\n    var sinOut: vec4<f32>;\n    var absOut: vec4<f32>;\n    var sqrtOut: vec4<f32>;\n    var inversesqrtOut: vec4<f32>;\n    var expOut: vec4<f32>;\n    var exp2Out: vec4<f32>;\n    var signOut: vec4<f32>;\n    var transposeOut: mat4x4<f32>;\n    var normalizeOut: vec4<f32>;\n    var sinhOut: vec4<f32>;\n    var cosOut: vec4<f32>;\n    var coshOut: vec4<f32>;\n    var tanOut: vec4<f32>;\n    var tanhOut: vec4<f32>;\n    var acosOut: vec4<f32>;\n    var asinOut: vec4<f32>;\n    var logOut: vec4<f32>;\n    var log2Out: vec4<f32>;\n    var lengthOut: f32;\n    var determinantOut: f32;\n    var bitCountOut: i32;\n    var bitfieldReverseOut: i32;\n    var atanOut: f32;\n    var atan2Out: f32;\n    var modOut: f32;\n    var powOut: vec4<f32>;\n    var dotOut: f32;\n    var maxOut: vec4<f32>;\n    var minOut: vec4<f32>;\n    var reflectOut: vec4<f32>;\n    var crossOut: vec3<f32>;\n    var distanceOut: f32;\n    var stepOut: vec4<f32>;\n    var ldexpOut: f32;\n    var rad: vec4<f32>;\n    var deg: f32;\n    var smoothStepScalar: f32;\n    var smoothStepVector: vec4<f32>;\n    var smoothStepMixed: vec4<f32>;\n\n    let _e6 = a;\n    let _e7 = b;\n    let _e8 = a;\n    let _e9 = b;\n    m = mat4x4<f32>(vec4<f32>(_e6.x, _e6.y, _e6.z, _e6.w), vec4<f32>(_e7.x, _e7.y, _e7.z, _e7.w), vec4<f32>(_e8.x, _e8.y, _e8.z, _e8.w), vec4<f32>(_e9.x, _e9.y, _e9.z, _e9.w));\n    let _e34 = a;\n    ceilOut = ceil(_e34);\n    let _e37 = a;\n    roundOut = round(_e37);\n    let _e40 = a;\n    floorOut = floor(_e40);\n    let _e43 = a;\n    fractOut = fract(_e43);\n    let _e46 = a;\n    truncOut = trunc(_e46);\n    let _e49 = a;\n    sinOut = sin(_e49);\n    let _e52 = a;\n    absOut = abs(_e52);\n    let _e55 = a;\n    sqrtOut = sqrt(_e55);\n    let _e58 = a;\n    inversesqrtOut = inverseSqrt(_e58);\n    let _e61 = a;\n    expOut = exp(_e61);\n    let _e64 = a;\n    exp2Out = exp2(_e64);\n    let _e67 = a;\n    signOut = sign(_e67);\n    let _e70 = m;\n    transposeOut = transpose(_e70);\n    let _e73 = a;\n    normalizeOut = normalize(_e73);\n    let _e76 = a;\n    sinhOut = sinh(_e76);\n    let _e79 = a;\n    cosOut = cos(_e79);\n    let _e82 = a;\n    coshOut = cosh(_e82);\n    let _e85 = a;\n    tanOut = tan(_e85);\n    let _e88 = a;\n    tanhOut = tanh(_e88);\n    let _e91 = a;\n    acosOut = acos(_e91);\n    let _e94 = a;\n    asinOut = asin(_e94);\n    let _e97 = a;\n    logOut = log(_e97);\n    let _e100 = a;\n    log2Out = log2(_e100);\n    let _e103 = a;\n    lengthOut = length(_e103);\n    let _e106 = m;\n    determinantOut = determinant(_e106);\n    let _e109 = i;\n    bitCountOut = countOneBits(_e109);\n    let _e112 = i;\n    bitfieldReverseOut = reverseBits(_e112);\n    let _e115 = a;\n    atanOut = atan(_e115.x);\n    let _e119 = a;\n    let _e121 = a;\n    atan2Out = atan2(_e119.x, _e121.y);\n    let _e125 = a;\n    let _e127 = b;\n    modOut = (_e125.x - (floor((_e125.x / _e127.x)) * _e127.x));\n    let _e134 = a;\n    let _e135 = b;\n    powOut = pow(_e134, _e135);\n    let _e138 = a;\n    let _e139 = b;\n    dotOut = dot(_e138, _e139);\n    let _e142 = a;\n    let _e143 = b;\n    maxOut = max(_e142, _e143);\n    let _e146 = a;\n    let _e147 = b;\n    minOut = min(_e146, _e147);\n    let _e150 = a;\n    let _e151 = b;\n    reflectOut = reflect(_e150, _e151);\n    let _e154 = a;\n    let _e156 = b;\n    crossOut = cross(_e154.xyz, _e156.xyz);\n    let _e160 = a;\n    let _e161 = b;\n    distanceOut = distance(_e160, _e161);\n    let _e164 = a;\n    let _e165 = b;\n    stepOut = step(_e164, _e165);\n    let _e168 = a;\n    let _e170 = i;\n    ldexpOut = ldexp(_e168.x, _e170);\n    let _e173 = a;\n    rad = radians(_e173);\n    let _e176 = a;\n    deg = degrees(_e176.x);\n    smoothStepScalar = smoothstep(0f, 1f, 0.5f);\n    smoothStepVector = smoothstep(vec4(0f), vec4(1f), vec4(0.5f));\n    smoothStepMixed = smoothstep(vec4(0f), vec4(1f), vec4(0.5f));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-multipart-for-loop.frag.wgsl",
    "content": "fn main_1() {\n    var a: f32 = 1f;\n    var b: f32 = 0.25f;\n    var c: f32 = 1.5f;\n    var i: i32 = 20i;\n\n    i = 0i;\n    let _e9 = c;\n    c = (_e9 - 1f);\n    loop {\n        let _e12 = i;\n        if !((_e12 < 25i)) {\n            break;\n        }\n        {\n            let _e22 = a;\n            a = (_e22 - 0.02f);\n        }\n        continuing {\n            let _e16 = i;\n            i = (_e16 + 1i);\n            let _e19 = b;\n            b = (_e19 + 0.01f);\n        }\n    }\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-prepostfix.frag.wgsl",
    "content": "fn main_1() {\n    var scalar_target: i32;\n    var scalar: i32 = 1i;\n    var vec_target: vec2<u32>;\n    var vec: vec2<u32> = vec2(1u);\n    var mat_target: mat4x3<f32>;\n    var mat: mat4x3<f32> = mat4x3<f32>(vec3<f32>(1f, 0f, 0f), vec3<f32>(0f, 1f, 0f), vec3<f32>(0f, 0f, 1f), vec3<f32>(0f, 0f, 0f));\n\n    let _e3 = scalar;\n    scalar = (_e3 + 1i);\n    scalar_target = _e3;\n    let _e6 = scalar;\n    let _e8 = (_e6 - 1i);\n    scalar = _e8;\n    scalar_target = _e8;\n    let _e13 = vec;\n    vec = (_e13 - vec2(1u));\n    vec_target = _e13;\n    let _e17 = vec;\n    let _e20 = (_e17 + vec2(1u));\n    vec = _e20;\n    vec_target = _e20;\n    let _e30 = mat;\n    let _e32 = vec3(1f);\n    mat = (_e30 + mat4x3<f32>(_e32, _e32, _e32, _e32));\n    mat_target = _e30;\n    let _e35 = mat;\n    let _e37 = vec3(1f);\n    let _e39 = (_e35 - mat4x3<f32>(_e37, _e37, _e37, _e37));\n    mat = _e39;\n    mat_target = _e39;\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-quad_glsl.frag.wgsl",
    "content": "struct FragmentOutput {\n    @location(0) o_color: vec4<f32>,\n}\n\nvar<private> v_uv_1: vec2<f32>;\nvar<private> o_color: vec4<f32>;\n\nfn main_1() {\n    o_color = vec4<f32>(1f, 1f, 1f, 1f);\n    return;\n}\n\n@fragment \nfn main(@location(0) v_uv: vec2<f32>) -> FragmentOutput {\n    v_uv_1 = v_uv;\n    main_1();\n    let _e3 = o_color;\n    return FragmentOutput(_e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-quad_glsl.vert.wgsl",
    "content": "struct VertexOutput {\n    @location(0) v_uv: vec2<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nconst c_scale: f32 = 1.2f;\n\nvar<private> a_pos_1: vec2<f32>;\nvar<private> a_uv_1: vec2<f32>;\nvar<private> v_uv: vec2<f32>;\nvar<private> gl_Position: vec4<f32>;\n\nfn main_1() {\n    let _e4 = a_uv_1;\n    v_uv = _e4;\n    let _e6 = a_pos_1;\n    let _e7 = (c_scale * _e6);\n    gl_Position = vec4<f32>(_e7.x, _e7.y, 0f, 1f);\n    return;\n}\n\n@vertex \nfn main(@location(0) a_pos: vec2<f32>, @location(1) a_uv: vec2<f32>) -> VertexOutput {\n    a_pos_1 = a_pos;\n    a_uv_1 = a_uv;\n    main_1();\n    let _e5 = v_uv;\n    let _e7 = gl_Position;\n    return VertexOutput(_e5, _e7);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-sampler-functions.frag.wgsl",
    "content": "@group(1) @binding(0) \nvar tex2D: texture_depth_2d;\n@group(1) @binding(1) \nvar sampShadow: sampler_comparison;\n\nfn CalcShadowPCF1_(T_P_t_TextureDepth: texture_depth_2d, S_P_t_TextureDepth: sampler_comparison, t_ProjCoord: vec3<f32>) -> f32 {\n    var t_ProjCoord_1: vec3<f32>;\n    var t_Res: f32 = 0f;\n\n    t_ProjCoord_1 = t_ProjCoord;\n    let _e6 = t_Res;\n    let _e7 = t_ProjCoord_1;\n    let _e8 = _e7.xyz;\n    let _e11 = textureSampleCompare(T_P_t_TextureDepth, S_P_t_TextureDepth, _e8.xy, _e8.z);\n    t_Res = (_e6 + (_e11 * 0.2f));\n    let _e15 = t_Res;\n    return _e15;\n}\n\nfn CalcShadowPCF(T_P_t_TextureDepth_1: texture_depth_2d, S_P_t_TextureDepth_1: sampler_comparison, t_ProjCoord_2: vec3<f32>, t_Bias: f32) -> f32 {\n    var t_ProjCoord_3: vec3<f32>;\n    var t_Bias_1: f32;\n\n    t_ProjCoord_3 = t_ProjCoord_2;\n    t_Bias_1 = t_Bias;\n    let _e7 = t_ProjCoord_3;\n    let _e9 = t_Bias_1;\n    t_ProjCoord_3.z = (_e7.z + _e9);\n    let _e11 = t_ProjCoord_3;\n    let _e13 = CalcShadowPCF1_(T_P_t_TextureDepth_1, S_P_t_TextureDepth_1, _e11.xyz);\n    return _e13;\n}\n\nfn main_1() {\n    let _e4 = CalcShadowPCF1_(tex2D, sampShadow, vec3(0f));\n    let _e8 = CalcShadowPCF(tex2D, sampShadow, vec3(0f), 1f);\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-samplers.frag.wgsl",
    "content": "@group(1) @binding(0) \nvar tex1D: texture_1d<f32>;\n@group(1) @binding(2) \nvar tex2D: texture_2d<f32>;\n@group(1) @binding(3) \nvar tex2DArray: texture_2d_array<f32>;\n@group(1) @binding(4) \nvar texCube: texture_cube<f32>;\n@group(1) @binding(5) \nvar texCubeArray: texture_cube_array<f32>;\n@group(1) @binding(6) \nvar tex3D: texture_3d<f32>;\n@group(1) @binding(7) \nvar utex2D: texture_2d<u32>;\n@group(1) @binding(8) \nvar itex2D: texture_2d<i32>;\n@group(2) @binding(0) \nvar samp: sampler;\n@group(1) @binding(12) \nvar tex2DShadow: texture_depth_2d;\n@group(1) @binding(13) \nvar tex2DArrayShadow: texture_depth_2d_array;\n@group(1) @binding(14) \nvar texCubeShadow: texture_depth_cube;\n@group(1) @binding(15) \nvar texCubeArrayShadow: texture_depth_cube_array;\n@group(1) @binding(17) \nvar sampShadow: sampler_comparison;\n@group(0) @binding(18) \nvar tex2DMS: texture_multisampled_2d<f32>;\n\nfn testTex1D(coord: f32) {\n    var coord_1: f32;\n    var size1D: i32;\n    var levels: i32;\n    var c: vec4<f32>;\n\n    coord_1 = coord;\n    let _e5 = textureDimensions(tex1D, 0i);\n    size1D = i32(_e5);\n    let _e8 = textureNumLevels(tex1D);\n    levels = i32(_e8);\n    let _e12 = coord_1;\n    let _e13 = textureSample(tex1D, samp, _e12);\n    c = _e13;\n    let _e14 = coord_1;\n    let _e17 = textureSampleGrad(tex1D, samp, _e14, 4f, 4f);\n    c = _e17;\n    let _e18 = coord_1;\n    let _e22 = textureSampleGrad(tex1D, samp, _e18, 4f, 4f, 5i);\n    c = _e22;\n    let _e23 = coord_1;\n    let _e25 = textureSampleLevel(tex1D, samp, _e23, 3f);\n    c = _e25;\n    let _e26 = coord_1;\n    let _e29 = textureSampleLevel(tex1D, samp, _e26, 3f, 5i);\n    c = _e29;\n    let _e30 = coord_1;\n    let _e32 = textureSample(tex1D, samp, _e30, 5i);\n    c = _e32;\n    let _e33 = coord_1;\n    let _e35 = vec2<f32>(_e33, 6f);\n    let _e39 = textureSample(tex1D, samp, (_e35.x / _e35.y));\n    c = _e39;\n    let _e40 = coord_1;\n    let _e44 = vec4<f32>(_e40, 0f, 0f, 6f);\n    let _e50 = textureSample(tex1D, samp, (_e44.xyz / vec3(_e44.w)).x);\n    c = _e50;\n    let _e51 = coord_1;\n    let _e53 = vec2<f32>(_e51, 6f);\n    let _e59 = textureSampleGrad(tex1D, samp, (_e53.x / _e53.y), 4f, 4f);\n    c = _e59;\n    let _e60 = coord_1;\n    let _e64 = vec4<f32>(_e60, 0f, 0f, 6f);\n    let _e72 = textureSampleGrad(tex1D, samp, (_e64.xyz / vec3(_e64.w)).x, 4f, 4f);\n    c = _e72;\n    let _e73 = coord_1;\n    let _e75 = vec2<f32>(_e73, 6f);\n    let _e82 = textureSampleGrad(tex1D, samp, (_e75.x / _e75.y), 4f, 4f, 5i);\n    c = _e82;\n    let _e83 = coord_1;\n    let _e87 = vec4<f32>(_e83, 0f, 0f, 6f);\n    let _e96 = textureSampleGrad(tex1D, samp, (_e87.xyz / vec3(_e87.w)).x, 4f, 4f, 5i);\n    c = _e96;\n    let _e97 = coord_1;\n    let _e99 = vec2<f32>(_e97, 6f);\n    let _e104 = textureSampleLevel(tex1D, samp, (_e99.x / _e99.y), 3f);\n    c = _e104;\n    let _e105 = coord_1;\n    let _e109 = vec4<f32>(_e105, 0f, 0f, 6f);\n    let _e116 = textureSampleLevel(tex1D, samp, (_e109.xyz / vec3(_e109.w)).x, 3f);\n    c = _e116;\n    let _e117 = coord_1;\n    let _e119 = vec2<f32>(_e117, 6f);\n    let _e125 = textureSampleLevel(tex1D, samp, (_e119.x / _e119.y), 3f, 5i);\n    c = _e125;\n    let _e126 = coord_1;\n    let _e130 = vec4<f32>(_e126, 0f, 0f, 6f);\n    let _e138 = textureSampleLevel(tex1D, samp, (_e130.xyz / vec3(_e130.w)).x, 3f, 5i);\n    c = _e138;\n    let _e139 = coord_1;\n    let _e141 = vec2<f32>(_e139, 6f);\n    let _e146 = textureSample(tex1D, samp, (_e141.x / _e141.y), 5i);\n    c = _e146;\n    let _e147 = coord_1;\n    let _e151 = vec4<f32>(_e147, 0f, 0f, 6f);\n    let _e158 = textureSample(tex1D, samp, (_e151.xyz / vec3(_e151.w)).x, 5i);\n    c = _e158;\n    let _e159 = coord_1;\n    let _e162 = textureLoad(tex1D, i32(_e159), 3i);\n    c = _e162;\n    let _e163 = coord_1;\n    let _e166 = textureLoad(tex1D, i32(_e163), 3i);\n    c = _e166;\n    return;\n}\n\nfn testTex2D(coord_2: vec2<f32>) {\n    var coord_3: vec2<f32>;\n    var size2D: vec2<i32>;\n    var levels_1: i32;\n    var c_1: vec4<f32>;\n\n    coord_3 = coord_2;\n    let _e7 = textureDimensions(tex2D, 0i);\n    size2D = vec2<i32>(_e7);\n    let _e10 = textureNumLevels(tex2D);\n    levels_1 = i32(_e10);\n    let _e14 = coord_3;\n    let _e15 = textureSample(tex2D, samp, _e14);\n    c_1 = _e15;\n    let _e16 = coord_3;\n    let _e18 = textureSampleBias(tex2D, samp, _e16, 2f);\n    c_1 = _e18;\n    let _e19 = coord_3;\n    let _e24 = textureSampleGrad(tex2D, samp, _e19, vec2(4f), vec2(4f));\n    c_1 = _e24;\n    let _e25 = coord_3;\n    let _e32 = textureSampleGrad(tex2D, samp, _e25, vec2(4f), vec2(4f), vec2(5i));\n    c_1 = _e32;\n    let _e33 = coord_3;\n    let _e35 = textureSampleLevel(tex2D, samp, _e33, 3f);\n    c_1 = _e35;\n    let _e36 = coord_3;\n    let _e40 = textureSampleLevel(tex2D, samp, _e36, 3f, vec2(5i));\n    c_1 = _e40;\n    let _e41 = coord_3;\n    let _e44 = textureSample(tex2D, samp, _e41, vec2(5i));\n    c_1 = _e44;\n    let _e45 = coord_3;\n    let _e49 = textureSampleBias(tex2D, samp, _e45, 2f, vec2(5i));\n    c_1 = _e49;\n    let _e50 = coord_3;\n    let _e54 = vec3<f32>(_e50.x, _e50.y, 6f);\n    let _e59 = textureSample(tex2D, samp, (_e54.xy / vec2(_e54.z)));\n    c_1 = _e59;\n    let _e60 = coord_3;\n    let _e65 = vec4<f32>(_e60.x, _e60.y, 0f, 6f);\n    let _e71 = textureSample(tex2D, samp, (_e65.xyz / vec3(_e65.w)).xy);\n    c_1 = _e71;\n    let _e72 = coord_3;\n    let _e76 = vec3<f32>(_e72.x, _e72.y, 6f);\n    let _e82 = textureSampleBias(tex2D, samp, (_e76.xy / vec2(_e76.z)), 2f);\n    c_1 = _e82;\n    let _e83 = coord_3;\n    let _e88 = vec4<f32>(_e83.x, _e83.y, 0f, 6f);\n    let _e95 = textureSampleBias(tex2D, samp, (_e88.xyz / vec3(_e88.w)).xy, 2f);\n    c_1 = _e95;\n    let _e96 = coord_3;\n    let _e100 = vec3<f32>(_e96.x, _e96.y, 6f);\n    let _e109 = textureSampleGrad(tex2D, samp, (_e100.xy / vec2(_e100.z)), vec2(4f), vec2(4f));\n    c_1 = _e109;\n    let _e110 = coord_3;\n    let _e115 = vec4<f32>(_e110.x, _e110.y, 0f, 6f);\n    let _e125 = textureSampleGrad(tex2D, samp, (_e115.xyz / vec3(_e115.w)).xy, vec2(4f), vec2(4f));\n    c_1 = _e125;\n    let _e126 = coord_3;\n    let _e130 = vec3<f32>(_e126.x, _e126.y, 6f);\n    let _e141 = textureSampleGrad(tex2D, samp, (_e130.xy / vec2(_e130.z)), vec2(4f), vec2(4f), vec2(5i));\n    c_1 = _e141;\n    let _e142 = coord_3;\n    let _e147 = vec4<f32>(_e142.x, _e142.y, 0f, 6f);\n    let _e159 = textureSampleGrad(tex2D, samp, (_e147.xyz / vec3(_e147.w)).xy, vec2(4f), vec2(4f), vec2(5i));\n    c_1 = _e159;\n    let _e160 = coord_3;\n    let _e164 = vec3<f32>(_e160.x, _e160.y, 6f);\n    let _e170 = textureSampleLevel(tex2D, samp, (_e164.xy / vec2(_e164.z)), 3f);\n    c_1 = _e170;\n    let _e171 = coord_3;\n    let _e176 = vec4<f32>(_e171.x, _e171.y, 0f, 6f);\n    let _e183 = textureSampleLevel(tex2D, samp, (_e176.xyz / vec3(_e176.w)).xy, 3f);\n    c_1 = _e183;\n    let _e184 = coord_3;\n    let _e188 = vec3<f32>(_e184.x, _e184.y, 6f);\n    let _e196 = textureSampleLevel(tex2D, samp, (_e188.xy / vec2(_e188.z)), 3f, vec2(5i));\n    c_1 = _e196;\n    let _e197 = coord_3;\n    let _e202 = vec4<f32>(_e197.x, _e197.y, 0f, 6f);\n    let _e211 = textureSampleLevel(tex2D, samp, (_e202.xyz / vec3(_e202.w)).xy, 3f, vec2(5i));\n    c_1 = _e211;\n    let _e212 = coord_3;\n    let _e216 = vec3<f32>(_e212.x, _e212.y, 6f);\n    let _e223 = textureSample(tex2D, samp, (_e216.xy / vec2(_e216.z)), vec2(5i));\n    c_1 = _e223;\n    let _e224 = coord_3;\n    let _e229 = vec4<f32>(_e224.x, _e224.y, 0f, 6f);\n    let _e237 = textureSample(tex2D, samp, (_e229.xyz / vec3(_e229.w)).xy, vec2(5i));\n    c_1 = _e237;\n    let _e238 = coord_3;\n    let _e242 = vec3<f32>(_e238.x, _e238.y, 6f);\n    let _e250 = textureSampleBias(tex2D, samp, (_e242.xy / vec2(_e242.z)), 2f, vec2(5i));\n    c_1 = _e250;\n    let _e251 = coord_3;\n    let _e256 = vec4<f32>(_e251.x, _e251.y, 0f, 6f);\n    let _e265 = textureSampleBias(tex2D, samp, (_e256.xyz / vec3(_e256.w)).xy, 2f, vec2(5i));\n    c_1 = _e265;\n    let _e266 = coord_3;\n    let _e269 = textureLoad(tex2D, vec2<i32>(_e266), 3i);\n    c_1 = _e269;\n    let _e270 = coord_3;\n    let _e273 = textureLoad(utex2D, vec2<i32>(_e270), 3i);\n    c_1 = vec4<f32>(_e273);\n    let _e275 = coord_3;\n    let _e278 = textureLoad(itex2D, vec2<i32>(_e275), 3i);\n    c_1 = vec4<f32>(_e278);\n    let _e280 = coord_3;\n    let _e283 = textureLoad(tex2D, vec2<i32>(_e280), 3i);\n    c_1 = _e283;\n    let _e284 = coord_3;\n    let _e287 = textureLoad(utex2D, vec2<i32>(_e284), 3i);\n    c_1 = vec4<f32>(_e287);\n    let _e289 = coord_3;\n    let _e292 = textureLoad(itex2D, vec2<i32>(_e289), 3i);\n    c_1 = vec4<f32>(_e292);\n    return;\n}\n\nfn testTex2DShadow(coord_4: vec2<f32>) {\n    var coord_5: vec2<f32>;\n    var size2DShadow: vec2<i32>;\n    var levels_2: i32;\n    var d: f32;\n\n    coord_5 = coord_4;\n    let _e5 = textureDimensions(tex2DShadow, 0i);\n    size2DShadow = vec2<i32>(_e5);\n    let _e8 = textureNumLevels(tex2DShadow);\n    levels_2 = i32(_e8);\n    let _e12 = coord_5;\n    let _e16 = vec3<f32>(_e12.x, _e12.y, 1f);\n    let _e19 = textureSampleCompare(tex2DShadow, sampShadow, _e16.xy, _e16.z);\n    d = _e19;\n    let _e20 = coord_5;\n    let _e24 = vec3<f32>(_e20.x, _e20.y, 1f);\n    let _e27 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e24.xy, _e24.z);\n    d = _e27;\n    let _e28 = coord_5;\n    let _e32 = vec3<f32>(_e28.x, _e28.y, 1f);\n    let _e37 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e32.xy, _e32.z, vec2(5i));\n    d = _e37;\n    let _e38 = coord_5;\n    let _e42 = vec3<f32>(_e38.x, _e38.y, 1f);\n    let _e45 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e42.xy, _e42.z);\n    d = _e45;\n    let _e46 = coord_5;\n    let _e50 = vec3<f32>(_e46.x, _e46.y, 1f);\n    let _e55 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e50.xy, _e50.z, vec2(5i));\n    d = _e55;\n    let _e56 = coord_5;\n    let _e60 = vec3<f32>(_e56.x, _e56.y, 1f);\n    let _e65 = textureSampleCompare(tex2DShadow, sampShadow, _e60.xy, _e60.z, vec2(5i));\n    d = _e65;\n    let _e66 = coord_5;\n    let _e71 = vec4<f32>(_e66.x, _e66.y, 1f, 6f);\n    let _e75 = (_e71.xyz / vec3(_e71.w));\n    let _e78 = textureSampleCompare(tex2DShadow, sampShadow, _e75.xy, _e75.z);\n    d = _e78;\n    let _e79 = coord_5;\n    let _e84 = vec4<f32>(_e79.x, _e79.y, 1f, 6f);\n    let _e88 = (_e84.xyz / vec3(_e84.w));\n    let _e91 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e88.xy, _e88.z);\n    d = _e91;\n    let _e92 = coord_5;\n    let _e97 = vec4<f32>(_e92.x, _e92.y, 1f, 6f);\n    let _e103 = (_e97.xyz / vec3(_e97.w));\n    let _e106 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e103.xy, _e103.z, vec2(5i));\n    d = _e106;\n    let _e107 = coord_5;\n    let _e112 = vec4<f32>(_e107.x, _e107.y, 1f, 6f);\n    let _e116 = (_e112.xyz / vec3(_e112.w));\n    let _e119 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e116.xy, _e116.z);\n    d = _e119;\n    let _e120 = coord_5;\n    let _e125 = vec4<f32>(_e120.x, _e120.y, 1f, 6f);\n    let _e131 = (_e125.xyz / vec3(_e125.w));\n    let _e134 = textureSampleCompareLevel(tex2DShadow, sampShadow, _e131.xy, _e131.z, vec2(5i));\n    d = _e134;\n    let _e135 = coord_5;\n    let _e140 = vec4<f32>(_e135.x, _e135.y, 1f, 6f);\n    let _e146 = (_e140.xyz / vec3(_e140.w));\n    let _e149 = textureSampleCompare(tex2DShadow, sampShadow, _e146.xy, _e146.z, vec2(5i));\n    d = _e149;\n    return;\n}\n\nfn testTex2DArray(coord_6: vec3<f32>) {\n    var coord_7: vec3<f32>;\n    var size2DArray: vec3<i32>;\n    var levels_3: i32;\n    var c_2: vec4<f32>;\n\n    coord_7 = coord_6;\n    let _e5 = textureDimensions(tex2DArray, 0i);\n    let _e8 = textureNumLayers(tex2DArray);\n    size2DArray = vec3<i32>(vec3<u32>(_e5.x, _e5.y, _e8));\n    let _e12 = textureNumLevels(tex2DArray);\n    levels_3 = i32(_e12);\n    let _e16 = coord_7;\n    let _e20 = textureSample(tex2DArray, samp, _e16.xy, i32(_e16.z));\n    c_2 = _e20;\n    let _e21 = coord_7;\n    let _e26 = textureSampleBias(tex2DArray, samp, _e21.xy, i32(_e21.z), 2f);\n    c_2 = _e26;\n    let _e27 = coord_7;\n    let _e35 = textureSampleGrad(tex2DArray, samp, _e27.xy, i32(_e27.z), vec2(4f), vec2(4f));\n    c_2 = _e35;\n    let _e36 = coord_7;\n    let _e46 = textureSampleGrad(tex2DArray, samp, _e36.xy, i32(_e36.z), vec2(4f), vec2(4f), vec2(5i));\n    c_2 = _e46;\n    let _e47 = coord_7;\n    let _e52 = textureSampleLevel(tex2DArray, samp, _e47.xy, i32(_e47.z), 3f);\n    c_2 = _e52;\n    let _e53 = coord_7;\n    let _e60 = textureSampleLevel(tex2DArray, samp, _e53.xy, i32(_e53.z), 3f, vec2(5i));\n    c_2 = _e60;\n    let _e61 = coord_7;\n    let _e67 = textureSample(tex2DArray, samp, _e61.xy, i32(_e61.z), vec2(5i));\n    c_2 = _e67;\n    let _e68 = coord_7;\n    let _e75 = textureSampleBias(tex2DArray, samp, _e68.xy, i32(_e68.z), 2f, vec2(5i));\n    c_2 = _e75;\n    let _e76 = coord_7;\n    let _e77 = vec3<i32>(_e76);\n    let _e81 = textureLoad(tex2DArray, _e77.xy, _e77.z, 3i);\n    c_2 = _e81;\n    let _e82 = coord_7;\n    let _e83 = vec3<i32>(_e82);\n    let _e87 = textureLoad(tex2DArray, _e83.xy, _e83.z, 3i);\n    c_2 = _e87;\n    return;\n}\n\nfn testTex2DArrayShadow(coord_8: vec3<f32>) {\n    var coord_9: vec3<f32>;\n    var size2DArrayShadow: vec3<i32>;\n    var levels_4: i32;\n    var d_1: f32;\n\n    coord_9 = coord_8;\n    let _e5 = textureDimensions(tex2DArrayShadow, 0i);\n    let _e8 = textureNumLayers(tex2DArrayShadow);\n    size2DArrayShadow = vec3<i32>(vec3<u32>(_e5.x, _e5.y, _e8));\n    let _e12 = textureNumLevels(tex2DArrayShadow);\n    levels_4 = i32(_e12);\n    let _e16 = coord_9;\n    let _e21 = vec4<f32>(_e16.x, _e16.y, _e16.z, 1f);\n    let _e26 = textureSampleCompare(tex2DArrayShadow, sampShadow, _e21.xy, i32(_e21.z), _e21.w);\n    d_1 = _e26;\n    let _e27 = coord_9;\n    let _e32 = vec4<f32>(_e27.x, _e27.y, _e27.z, 1f);\n    let _e37 = textureSampleCompareLevel(tex2DArrayShadow, sampShadow, _e32.xy, i32(_e32.z), _e32.w);\n    d_1 = _e37;\n    let _e38 = coord_9;\n    let _e43 = vec4<f32>(_e38.x, _e38.y, _e38.z, 1f);\n    let _e50 = textureSampleCompareLevel(tex2DArrayShadow, sampShadow, _e43.xy, i32(_e43.z), _e43.w, vec2(5i));\n    d_1 = _e50;\n    let _e51 = coord_9;\n    let _e56 = vec4<f32>(_e51.x, _e51.y, _e51.z, 1f);\n    let _e63 = textureSampleCompare(tex2DArrayShadow, sampShadow, _e56.xy, i32(_e56.z), _e56.w, vec2(5i));\n    d_1 = _e63;\n    return;\n}\n\nfn testTexCube(coord_10: vec3<f32>) {\n    var coord_11: vec3<f32>;\n    var sizeCube: vec2<i32>;\n    var levels_5: i32;\n    var c_3: vec4<f32>;\n\n    coord_11 = coord_10;\n    let _e5 = textureDimensions(texCube, 0i);\n    sizeCube = vec2<i32>(_e5);\n    let _e8 = textureNumLevels(texCube);\n    levels_5 = i32(_e8);\n    let _e12 = coord_11;\n    let _e13 = textureSample(texCube, samp, _e12);\n    c_3 = _e13;\n    let _e14 = coord_11;\n    let _e16 = textureSampleBias(texCube, samp, _e14, 2f);\n    c_3 = _e16;\n    let _e17 = coord_11;\n    let _e22 = textureSampleGrad(texCube, samp, _e17, vec3(4f), vec3(4f));\n    c_3 = _e22;\n    let _e23 = coord_11;\n    let _e25 = textureSampleLevel(texCube, samp, _e23, 3f);\n    c_3 = _e25;\n    return;\n}\n\nfn testTexCubeShadow(coord_12: vec3<f32>) {\n    var coord_13: vec3<f32>;\n    var sizeCubeShadow: vec2<i32>;\n    var levels_6: i32;\n    var d_2: f32;\n\n    coord_13 = coord_12;\n    let _e5 = textureDimensions(texCubeShadow, 0i);\n    sizeCubeShadow = vec2<i32>(_e5);\n    let _e8 = textureNumLevels(texCubeShadow);\n    levels_6 = i32(_e8);\n    let _e12 = coord_13;\n    let _e17 = vec4<f32>(_e12.x, _e12.y, _e12.z, 1f);\n    let _e20 = textureSampleCompare(texCubeShadow, sampShadow, _e17.xyz, _e17.w);\n    d_2 = _e20;\n    let _e21 = coord_13;\n    let _e26 = vec4<f32>(_e21.x, _e21.y, _e21.z, 1f);\n    let _e29 = textureSampleCompareLevel(texCubeShadow, sampShadow, _e26.xyz, _e26.w);\n    d_2 = _e29;\n    return;\n}\n\nfn testTexCubeArray(coord_14: vec4<f32>) {\n    var coord_15: vec4<f32>;\n    var sizeCubeArray: vec3<i32>;\n    var levels_7: i32;\n    var c_4: vec4<f32>;\n\n    coord_15 = coord_14;\n    let _e5 = textureDimensions(texCubeArray, 0i);\n    let _e8 = textureNumLayers(texCubeArray);\n    sizeCubeArray = vec3<i32>(vec3<u32>(_e5.x, _e5.y, _e8));\n    let _e12 = textureNumLevels(texCubeArray);\n    levels_7 = i32(_e12);\n    let _e16 = coord_15;\n    let _e20 = textureSample(texCubeArray, samp, _e16.xyz, i32(_e16.w));\n    c_4 = _e20;\n    let _e21 = coord_15;\n    let _e26 = textureSampleBias(texCubeArray, samp, _e21.xyz, i32(_e21.w), 2f);\n    c_4 = _e26;\n    let _e27 = coord_15;\n    let _e35 = textureSampleGrad(texCubeArray, samp, _e27.xyz, i32(_e27.w), vec3(4f), vec3(4f));\n    c_4 = _e35;\n    let _e36 = coord_15;\n    let _e41 = textureSampleLevel(texCubeArray, samp, _e36.xyz, i32(_e36.w), 3f);\n    c_4 = _e41;\n    return;\n}\n\nfn testTexCubeArrayShadow(coord_16: vec4<f32>) {\n    var coord_17: vec4<f32>;\n    var sizeCubeArrayShadow: vec3<i32>;\n    var levels_8: i32;\n    var d_3: f32;\n\n    coord_17 = coord_16;\n    let _e5 = textureDimensions(texCubeArrayShadow, 0i);\n    let _e8 = textureNumLayers(texCubeArrayShadow);\n    sizeCubeArrayShadow = vec3<i32>(vec3<u32>(_e5.x, _e5.y, _e8));\n    let _e12 = textureNumLevels(texCubeArrayShadow);\n    levels_8 = i32(_e12);\n    let _e16 = coord_17;\n    let _e21 = textureSampleCompare(texCubeArrayShadow, sampShadow, _e16.xyz, i32(_e16.w), 1f);\n    d_3 = _e21;\n    return;\n}\n\nfn testTex3D(coord_18: vec3<f32>) {\n    var coord_19: vec3<f32>;\n    var size3D: vec3<i32>;\n    var levels_9: i32;\n    var c_5: vec4<f32>;\n\n    coord_19 = coord_18;\n    let _e5 = textureDimensions(tex3D, 0i);\n    size3D = vec3<i32>(_e5);\n    let _e8 = textureNumLevels(tex3D);\n    levels_9 = i32(_e8);\n    let _e12 = coord_19;\n    let _e13 = textureSample(tex3D, samp, _e12);\n    c_5 = _e13;\n    let _e14 = coord_19;\n    let _e16 = textureSampleBias(tex3D, samp, _e14, 2f);\n    c_5 = _e16;\n    let _e17 = coord_19;\n    let _e22 = vec4<f32>(_e17.x, _e17.y, _e17.z, 6f);\n    let _e27 = textureSample(tex3D, samp, (_e22.xyz / vec3(_e22.w)));\n    c_5 = _e27;\n    let _e28 = coord_19;\n    let _e33 = vec4<f32>(_e28.x, _e28.y, _e28.z, 6f);\n    let _e39 = textureSampleBias(tex3D, samp, (_e33.xyz / vec3(_e33.w)), 2f);\n    c_5 = _e39;\n    let _e40 = coord_19;\n    let _e45 = vec4<f32>(_e40.x, _e40.y, _e40.z, 6f);\n    let _e52 = textureSample(tex3D, samp, (_e45.xyz / vec3(_e45.w)), vec3(5i));\n    c_5 = _e52;\n    let _e53 = coord_19;\n    let _e58 = vec4<f32>(_e53.x, _e53.y, _e53.z, 6f);\n    let _e66 = textureSampleBias(tex3D, samp, (_e58.xyz / vec3(_e58.w)), 2f, vec3(5i));\n    c_5 = _e66;\n    let _e67 = coord_19;\n    let _e72 = vec4<f32>(_e67.x, _e67.y, _e67.z, 6f);\n    let _e78 = textureSampleLevel(tex3D, samp, (_e72.xyz / vec3(_e72.w)), 3f);\n    c_5 = _e78;\n    let _e79 = coord_19;\n    let _e84 = vec4<f32>(_e79.x, _e79.y, _e79.z, 6f);\n    let _e92 = textureSampleLevel(tex3D, samp, (_e84.xyz / vec3(_e84.w)), 3f, vec3(5i));\n    c_5 = _e92;\n    let _e93 = coord_19;\n    let _e98 = vec4<f32>(_e93.x, _e93.y, _e93.z, 6f);\n    let _e107 = textureSampleGrad(tex3D, samp, (_e98.xyz / vec3(_e98.w)), vec3(4f), vec3(4f));\n    c_5 = _e107;\n    let _e108 = coord_19;\n    let _e113 = vec4<f32>(_e108.x, _e108.y, _e108.z, 6f);\n    let _e124 = textureSampleGrad(tex3D, samp, (_e113.xyz / vec3(_e113.w)), vec3(4f), vec3(4f), vec3(5i));\n    c_5 = _e124;\n    let _e125 = coord_19;\n    let _e130 = textureSampleGrad(tex3D, samp, _e125, vec3(4f), vec3(4f));\n    c_5 = _e130;\n    let _e131 = coord_19;\n    let _e138 = textureSampleGrad(tex3D, samp, _e131, vec3(4f), vec3(4f), vec3(5i));\n    c_5 = _e138;\n    let _e139 = coord_19;\n    let _e141 = textureSampleLevel(tex3D, samp, _e139, 3f);\n    c_5 = _e141;\n    let _e142 = coord_19;\n    let _e146 = textureSampleLevel(tex3D, samp, _e142, 3f, vec3(5i));\n    c_5 = _e146;\n    let _e147 = coord_19;\n    let _e150 = textureSample(tex3D, samp, _e147, vec3(5i));\n    c_5 = _e150;\n    let _e151 = coord_19;\n    let _e155 = textureSampleBias(tex3D, samp, _e151, 2f, vec3(5i));\n    c_5 = _e155;\n    let _e156 = coord_19;\n    let _e159 = textureLoad(tex3D, vec3<i32>(_e156), 3i);\n    c_5 = _e159;\n    let _e160 = coord_19;\n    let _e163 = textureLoad(tex3D, vec3<i32>(_e160), 3i);\n    c_5 = _e163;\n    return;\n}\n\nfn testTex2DMS(coord_20: vec2<f32>) {\n    var coord_21: vec2<f32>;\n    var size2DMS: vec2<i32>;\n    var c_6: vec4<f32>;\n\n    coord_21 = coord_20;\n    let _e3 = textureDimensions(tex2DMS);\n    size2DMS = vec2<i32>(_e3);\n    let _e7 = coord_21;\n    let _e10 = textureLoad(tex2DMS, vec2<i32>(_e7), 3i);\n    c_6 = _e10;\n    return;\n}\n\nfn main_1() {\n    testTex1D(1f);\n    testTex2D(vec2(1f));\n    testTex2DShadow(vec2(1f));\n    testTex2DArray(vec3(1f));\n    testTex2DArrayShadow(vec3(1f));\n    testTexCube(vec3(1f));\n    testTexCubeShadow(vec3(1f));\n    testTexCubeArray(vec4(1f));\n    testTexCubeArrayShadow(vec4(1f));\n    testTex3D(vec3(1f));\n    testTex2DMS(vec2(1f));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-spec-constant.frag.wgsl",
    "content": "struct FragmentOutput {\n    @location(0) o_color: vec4<f32>,\n}\n\n@id(0) override SPEC_CONST_BOOL: bool = true;\n@id(1) override SPEC_CONST_INT: i32 = 42i;\n@id(2) override SPEC_CONST_UINT: u32 = 10u;\n@id(3) override SPEC_CONST_FLOAT: f32 = 3.14f;\n\nvar<private> o_color: vec4<f32>;\n\nfn main_1() {\n    var result: f32 = 0f;\n\n    if SPEC_CONST_BOOL {\n        {\n            let _e7 = result;\n            result = (_e7 + f32(SPEC_CONST_INT));\n        }\n    }\n    let _e10 = result;\n    result = (_e10 + (f32(SPEC_CONST_UINT) * SPEC_CONST_FLOAT));\n    let _e14 = result;\n    o_color = vec4<f32>(_e14, 0f, 0f, 1f);\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e1 = o_color;\n    return FragmentOutput(_e1);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-statements.frag.wgsl",
    "content": "fn switchEmpty(a: i32) {\n    var a_1: i32;\n\n    a_1 = a;\n    let _e2 = a_1;\n    switch _e2 {\n        default: {\n        }\n    }\n    return;\n}\n\nfn switchNoDefault(a_2: i32) {\n    var a_3: i32;\n\n    a_3 = a_2;\n    let _e2 = a_3;\n    switch _e2 {\n        case 0: {\n        }\n        default: {\n        }\n    }\n    return;\n}\n\nfn switchCaseImplConv(a_4: u32) {\n    var a_5: u32;\n\n    a_5 = a_4;\n    let _e2 = a_5;\n    switch _e2 {\n        case 0u: {\n        }\n        default: {\n        }\n    }\n    return;\n}\n\nfn switchNoLastBreak(a_6: i32) {\n    var a_7: i32;\n    var b: i32;\n\n    a_7 = a_6;\n    let _e2 = a_7;\n    switch _e2 {\n        default: {\n            let _e3 = a_7;\n            b = _e3;\n        }\n    }\n    return;\n}\n\nfn main_1() {\n    switchEmpty(1i);\n    switchNoDefault(2i);\n    switchCaseImplConv(3u);\n    switchNoLastBreak(4i);\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/glsl-vector-functions.frag.wgsl",
    "content": "fn ftest(a: vec4<f32>, b: vec4<f32>) {\n    var a_1: vec4<f32>;\n    var b_1: vec4<f32>;\n    var c: vec4<bool>;\n    var d: vec4<bool>;\n    var e: vec4<bool>;\n    var f: vec4<bool>;\n    var g: vec4<bool>;\n    var h: vec4<bool>;\n\n    a_1 = a;\n    b_1 = b;\n    let _e4 = a_1;\n    let _e5 = b_1;\n    c = (_e4 < _e5);\n    let _e8 = a_1;\n    let _e9 = b_1;\n    d = (_e8 <= _e9);\n    let _e12 = a_1;\n    let _e13 = b_1;\n    e = (_e12 > _e13);\n    let _e16 = a_1;\n    let _e17 = b_1;\n    f = (_e16 >= _e17);\n    let _e20 = a_1;\n    let _e21 = b_1;\n    g = (_e20 == _e21);\n    let _e24 = a_1;\n    let _e25 = b_1;\n    h = (_e24 != _e25);\n    return;\n}\n\nfn dtest(a_2: vec4<f64>, b_2: vec4<f64>) {\n    var a_3: vec4<f64>;\n    var b_3: vec4<f64>;\n    var c_1: vec4<bool>;\n    var d_1: vec4<bool>;\n    var e_1: vec4<bool>;\n    var f_1: vec4<bool>;\n    var g_1: vec4<bool>;\n    var h_1: vec4<bool>;\n\n    a_3 = a_2;\n    b_3 = b_2;\n    let _e4 = a_3;\n    let _e5 = b_3;\n    c_1 = (_e4 < _e5);\n    let _e8 = a_3;\n    let _e9 = b_3;\n    d_1 = (_e8 <= _e9);\n    let _e12 = a_3;\n    let _e13 = b_3;\n    e_1 = (_e12 > _e13);\n    let _e16 = a_3;\n    let _e17 = b_3;\n    f_1 = (_e16 >= _e17);\n    let _e20 = a_3;\n    let _e21 = b_3;\n    g_1 = (_e20 == _e21);\n    let _e24 = a_3;\n    let _e25 = b_3;\n    h_1 = (_e24 != _e25);\n    return;\n}\n\nfn itest(a_4: vec4<i32>, b_4: vec4<i32>) {\n    var a_5: vec4<i32>;\n    var b_5: vec4<i32>;\n    var c_2: vec4<bool>;\n    var d_2: vec4<bool>;\n    var e_2: vec4<bool>;\n    var f_2: vec4<bool>;\n    var g_2: vec4<bool>;\n    var h_2: vec4<bool>;\n\n    a_5 = a_4;\n    b_5 = b_4;\n    let _e4 = a_5;\n    let _e5 = b_5;\n    c_2 = (_e4 < _e5);\n    let _e8 = a_5;\n    let _e9 = b_5;\n    d_2 = (_e8 <= _e9);\n    let _e12 = a_5;\n    let _e13 = b_5;\n    e_2 = (_e12 > _e13);\n    let _e16 = a_5;\n    let _e17 = b_5;\n    f_2 = (_e16 >= _e17);\n    let _e20 = a_5;\n    let _e21 = b_5;\n    g_2 = (_e20 == _e21);\n    let _e24 = a_5;\n    let _e25 = b_5;\n    h_2 = (_e24 != _e25);\n    return;\n}\n\nfn utest(a_6: vec4<u32>, b_6: vec4<u32>) {\n    var a_7: vec4<u32>;\n    var b_7: vec4<u32>;\n    var c_3: vec4<bool>;\n    var d_3: vec4<bool>;\n    var e_3: vec4<bool>;\n    var f_3: vec4<bool>;\n    var g_3: vec4<bool>;\n    var h_3: vec4<bool>;\n\n    a_7 = a_6;\n    b_7 = b_6;\n    let _e4 = a_7;\n    let _e5 = b_7;\n    c_3 = (_e4 < _e5);\n    let _e8 = a_7;\n    let _e9 = b_7;\n    d_3 = (_e8 <= _e9);\n    let _e12 = a_7;\n    let _e13 = b_7;\n    e_3 = (_e12 > _e13);\n    let _e16 = a_7;\n    let _e17 = b_7;\n    f_3 = (_e16 >= _e17);\n    let _e20 = a_7;\n    let _e21 = b_7;\n    g_3 = (_e20 == _e21);\n    let _e24 = a_7;\n    let _e25 = b_7;\n    h_3 = (_e24 != _e25);\n    return;\n}\n\nfn btest(a_8: vec4<bool>, b_8: vec4<bool>) {\n    var a_9: vec4<bool>;\n    var b_9: vec4<bool>;\n    var c_4: vec4<bool>;\n    var d_4: vec4<bool>;\n    var e_4: bool;\n    var f_4: bool;\n    var g_4: vec4<bool>;\n\n    a_9 = a_8;\n    b_9 = b_8;\n    let _e4 = a_9;\n    let _e5 = b_9;\n    c_4 = (_e4 == _e5);\n    let _e8 = a_9;\n    let _e9 = b_9;\n    d_4 = (_e8 != _e9);\n    let _e12 = a_9;\n    e_4 = any(_e12);\n    let _e15 = a_9;\n    f_4 = all(_e15);\n    let _e18 = a_9;\n    g_4 = !(_e18);\n    return;\n}\n\nfn main_1() {\n    ftest(vec4(0f), vec4(0f));\n    dtest(vec4(0.0lf), vec4(0.0lf));\n    itest(vec4(0i), vec4(0i));\n    utest(vec4(0u), vec4(0u));\n    btest(vec4(false), vec4(false));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-8151-barrier-reorder.wgsl",
    "content": "struct type_3 {\n    member: array<u32>,\n}\n\nvar<private> global: vec3<u32>;\n@group(0) @binding(0) \nvar<storage, read_write> global_1: type_3;\nvar<workgroup> global_2: u32;\n\nfn function_() {\n    let _e6 = global;\n    let _e8 = (_e6.x == 0u);\n    if _e8 {\n        global_2 = 1u;\n    }\n    workgroupBarrier();\n    let _e9 = global_2;\n    workgroupBarrier();\n    global_1.member[_e6.x] = _e9;\n    if _e8 {\n        global_2 = 2u;\n    }\n    return;\n}\n\n@compute @workgroup_size(2, 1, 1) \nfn barrier_reorder_bug(@builtin(local_invocation_id) param: vec3<u32>) {\n    global = param;\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_compare_exchange.wgsl",
    "content": "struct type_2 {\n    member: u32,\n    member_1: u32,\n}\n\nstruct type_3 {\n    member: u32,\n}\n\nstruct type_5 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_5;\n@group(0) @binding(1) \nvar<storage> global_1: type_3;\n\nfn function_() {\n    var phi_28_: type_2;\n    var phi_29_: type_2;\n    var phi_43_: type_2;\n    var phi_54_: bool;\n\n    let _e11 = global_1.member;\n    phi_28_ = type_2(0u, _e11);\n    loop {\n        let _e14 = phi_28_;\n        if (_e14.member < _e14.member_1) {\n            phi_29_ = type_2((_e14.member + 1u), _e14.member_1);\n            phi_43_ = type_2(1u, _e14.member);\n        } else {\n            phi_29_ = _e14;\n            phi_43_ = type_2(0u, type_2().member_1);\n        }\n        let _e25 = phi_29_;\n        let _e27 = phi_43_;\n        switch bitcast<i32>(_e27.member) {\n            case 0: {\n                phi_54_ = false;\n                break;\n            }\n            case 1: {\n                let _e31 = atomicCompareExchangeWeak((&global.member), 3u, _e27.member_1);\n                phi_54_ = select(true, false, (_e31.old_value == 3u));\n                break;\n            }\n            default: {\n                phi_54_ = bool();\n                break;\n            }\n        }\n        let _e36 = phi_54_;\n        continue;\n        continuing {\n            phi_28_ = _e25;\n            break if !(_e36);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_compare_exchange() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_exchange.wgsl",
    "content": "struct type_2 {\n    member: u32,\n    member_1: u32,\n}\n\nstruct type_3 {\n    member: u32,\n}\n\nstruct type_5 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_5;\n@group(0) @binding(1) \nvar<storage> global_1: type_3;\n\nfn function_() {\n    var phi_26_: type_2;\n    var phi_29_: u32;\n    var phi_43_: type_2;\n    var phi_44_: type_2;\n    var phi_53_: bool;\n    var phi_27_: type_2;\n    var phi_30_: u32;\n\n    let _e10 = global_1.member;\n    phi_26_ = type_2(0u, _e10);\n    phi_29_ = 0u;\n    loop {\n        let _e13 = phi_26_;\n        let _e15 = phi_29_;\n        if (_e13.member < _e13.member_1) {\n            phi_43_ = type_2((_e13.member + 1u), _e13.member_1);\n            phi_44_ = type_2(1u, _e13.member);\n        } else {\n            phi_43_ = _e13;\n            phi_44_ = type_2(0u, type_2().member_1);\n        }\n        let _e26 = phi_43_;\n        let _e28 = phi_44_;\n        switch bitcast<i32>(_e28.member) {\n            case 0: {\n                phi_53_ = false;\n                phi_27_ = type_2();\n                phi_30_ = u32();\n                break;\n            }\n            case 1: {\n                let _e31 = atomicExchange((&global.member), _e15);\n                phi_53_ = true;\n                phi_27_ = _e26;\n                phi_30_ = (_e15 + _e31);\n                break;\n            }\n            default: {\n                phi_53_ = false;\n                phi_27_ = type_2();\n                phi_30_ = u32();\n                break;\n            }\n        }\n        let _e34 = phi_53_;\n        let _e36 = phi_27_;\n        let _e38 = phi_30_;\n        continue;\n        continuing {\n            phi_26_ = _e36;\n            phi_29_ = _e38;\n            break if !(_e34);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_exchange() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_global_struct_field_vertex.wgsl",
    "content": "struct type_5 {\n    member: u32,\n    member_1: vec2<f32>,\n    member_2: atomic<u32>,\n}\n\nstruct type_6 {\n    member: type_5,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_6;\nvar<private> global_1: vec4<f32> = vec4<f32>(0f, 0f, 0f, 1f);\n\nfn function_() {\n    let _e7 = global.member.member;\n    let _e8 = atomicAdd((&global.member.member_2), _e7);\n    let _e9 = f32(_e8);\n    let _e12 = global.member.member_1;\n    global_1 = vec4<f32>((_e9 * _e12.x), (_e9 * _e12.y), 0f, _e9);\n    return;\n}\n\n@vertex \nfn global_field_vertex() -> @builtin(position) vec4<f32> {\n    function_();\n    let _e1 = global_1;\n    return _e1;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_i_add_sub.wgsl",
    "content": "struct type_2 {\n    member: array<u32>,\n}\n\nstruct type_4 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_4;\n@group(0) @binding(1) \nvar<storage, read_write> global_1: type_2;\n\nfn function_() {\n    let _e6 = atomicAdd((&global.member), 2u);\n    let _e7 = atomicSub((&global.member), _e6);\n    if (_e6 < arrayLength((&global_1.member))) {\n        global_1.member[_e6] = _e7;\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_i_add_sub() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_i_decrement.wgsl",
    "content": "struct type_3 {\n    member: array<u32>,\n}\n\nstruct type_5 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_5;\n@group(0) @binding(1) \nvar<storage, read_write> global_1: type_3;\n\nfn function_() {\n    var phi_33_: bool;\n\n    loop {\n        let _e8 = atomicSub((&global.member), 1u);\n        if (_e8 < arrayLength((&global_1.member))) {\n            global_1.member[_e8] = _e8;\n            phi_33_ = select(true, false, (_e8 == 0u));\n        } else {\n            phi_33_ = false;\n        }\n        let _e16 = phi_33_;\n        continue;\n        continuing {\n            break if !(_e16);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_i_decrement() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_i_increment.wgsl",
    "content": "struct type_2 {\n    member: u32,\n}\n\nstruct type_4 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_4;\n@group(0) @binding(1) \nvar<storage> global_1: type_2;\n\nfn function_() {\n    var phi_21_: u32;\n    var phi_22_: u32;\n\n    phi_21_ = 0u;\n    loop {\n        let _e10 = phi_21_;\n        let _e11 = global_1.member;\n        let _e12 = (_e10 >= _e11);\n        if _e12 {\n            phi_22_ = u32();\n        } else {\n            let _e13 = atomicAdd((&global.member), 1u);\n            phi_22_ = (_e10 + 1u);\n        }\n        let _e17 = phi_22_;\n        continue;\n        continuing {\n            phi_21_ = _e17;\n            break if !(select(true, false, _e12));\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_i_increment() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-atomic_load_and_store.wgsl",
    "content": "struct type_2 {\n    member: u32,\n    member_1: u32,\n}\n\nstruct type_3 {\n    member: u32,\n}\n\nstruct type_5 {\n    member: atomic<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_5;\n@group(0) @binding(1) \nvar<storage> global_1: type_3;\n\nfn function_() {\n    var phi_25_: type_2;\n    var phi_40_: type_2;\n    var phi_41_: type_2;\n    var phi_50_: bool;\n    var phi_26_: type_2;\n\n    let _e10 = global_1.member;\n    phi_25_ = type_2(0u, _e10);\n    loop {\n        let _e13 = phi_25_;\n        if (_e13.member < _e13.member_1) {\n            phi_40_ = type_2((_e13.member + 1u), _e13.member_1);\n            phi_41_ = type_2(1u, _e13.member);\n        } else {\n            phi_40_ = _e13;\n            phi_41_ = type_2(0u, type_2().member_1);\n        }\n        let _e24 = phi_40_;\n        let _e26 = phi_41_;\n        switch bitcast<i32>(_e26.member) {\n            case 0: {\n                phi_50_ = false;\n                phi_26_ = type_2();\n                break;\n            }\n            case 1: {\n                let _e29 = atomicLoad((&global.member));\n                atomicStore((&global.member), (_e29 + 2u));\n                phi_50_ = true;\n                phi_26_ = _e24;\n                break;\n            }\n            default: {\n                phi_50_ = false;\n                phi_26_ = type_2();\n                break;\n            }\n        }\n        let _e32 = phi_50_;\n        let _e34 = phi_26_;\n        continue;\n        continuing {\n            phi_25_ = _e34;\n            break if !(_e32);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn stage_test_atomic_load_and_store() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-barrier.wgsl",
    "content": "fn function_() {\n    workgroupBarrier();\n    workgroupBarrier();\n    storageBarrier();\n    textureBarrier();\n    storageBarrier();\n    textureBarrier();\n    storageBarrier();\n    workgroupBarrier();\n    textureBarrier();\n    storageBarrier();\n    workgroupBarrier();\n    textureBarrier();\n    return;\n}\n\n@compute @workgroup_size(64, 1, 1) \nfn main() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-binding-arrays.dynamic.wgsl",
    "content": "@group(0) @binding(0) \nvar global: binding_array<texture_2d<f32>>;\n@group(0) @binding(1) \nvar global_1: binding_array<sampler>;\nvar<private> global_2: vec4<f32>;\n\nfn function_() {\n    let _e8 = textureSampleLevel(global[1i], global_1[1i], vec2<f32>(0.5f, 0.5f), 0f);\n    global_2 = _e8;\n    return;\n}\n\n@fragment \nfn main() -> @location(0) vec4<f32> {\n    function_();\n    let _e1 = global_2;\n    return _e1;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-binding-arrays.runtime.wgsl",
    "content": "var<private> input_u002e_texture_coordinates_1: vec2<f32>;\nvar<private> input_u002e_texture_index_1: u32;\n@group(0) @binding(0) \nvar textures: binding_array<texture_2d<f32>>;\n@group(0) @binding(1) \nvar linear_sampler: sampler;\nvar<private> entryPointParam_main: vec4<f32>;\n\nfn main_1() {\n    let _e5 = input_u002e_texture_coordinates_1;\n    let _e6 = input_u002e_texture_index_1;\n    let _e8 = textureSample(textures[_e6], linear_sampler, _e5);\n    entryPointParam_main = _e8;\n    return;\n}\n\n@fragment \nfn main(@location(0) input_u002e_texture_coordinates: vec2<f32>, @location(1) @interpolate(flat) input_u002e_texture_index: u32) -> @location(0) vec4<f32> {\n    input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates;\n    input_u002e_texture_index_1 = input_u002e_texture_index;\n    main_1();\n    let _e5 = entryPointParam_main;\n    return _e5;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-binding-arrays.static.wgsl",
    "content": "@group(0) @binding(0) \nvar global: binding_array<texture_2d<f32>, 256>;\n@group(0) @binding(1) \nvar global_1: binding_array<sampler, 256>;\nvar<private> global_2: vec4<f32>;\n\nfn function_() {\n    let _e8 = textureSampleLevel(global[1i], global_1[1i], vec2<f32>(0.5f, 0.5f), 0f);\n    global_2 = _e8;\n    return;\n}\n\n@fragment \nfn main() -> @location(0) vec4<f32> {\n    function_();\n    let _e1 = global_2;\n    return _e1;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-builtin-accessed-outside-entrypoint.wgsl",
    "content": "struct gl_PerVertex {\n    @builtin(position) gl_Position: vec4<f32>,\n    gl_PointSize: f32,\n    gl_ClipDistance: array<f32, 1>,\n    gl_CullDistance: array<f32, 1>,\n}\n\nvar<private> unnamed: gl_PerVertex = gl_PerVertex(vec4<f32>(0f, 0f, 0f, 1f), 1f, array<f32, 1>(), array<f32, 1>());\nvar<private> gl_VertexIndex_1: i32;\n\nfn builtin_usage_u0028_() {\n    let _e9 = gl_VertexIndex_1;\n    let _e12 = gl_VertexIndex_1;\n    unnamed.gl_Position = vec4<f32>(select(1f, -4f, (_e9 == 0i)), select(-1f, 4f, (_e12 == 2i)), 0f, 1f);\n    return;\n}\n\nfn main_1() {\n    builtin_usage_u0028_();\n    return;\n}\n\n@vertex \nfn main(@builtin(vertex_index) gl_VertexIndex: u32) -> @builtin(position) vec4<f32> {\n    gl_VertexIndex_1 = i32(gl_VertexIndex);\n    main_1();\n    let _e6 = unnamed.gl_Position.y;\n    unnamed.gl_Position.y = -(_e6);\n    let _e8 = unnamed.gl_Position;\n    return _e8;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-do-while.wgsl",
    "content": "fn f_u0028_b1_u003b(cond: ptr<function, bool>) {\n    loop {\n        continue;\n        continuing {\n            let _e1 = (*cond);\n            break if !(_e1);\n        }\n    }\n    return;\n}\n\nfn main_1() {\n    var param: bool;\n\n    param = false;\n    f_u0028_b1_u003b((&param));\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-dual-source-blending.wgsl",
    "content": "enable dual_source_blending;\n\nstruct FragmentOutput {\n    @location(0) @blend_src(0) member: vec4<f32>,\n    @location(0) @blend_src(1) member_1: vec4<f32>,\n}\n\nvar<private> output0_: vec4<f32>;\nvar<private> output1_: vec4<f32>;\n\nfn main_1() {\n    output0_ = vec4<f32>(1f, 0f, 1f, 0f);\n    output1_ = vec4<f32>(0f, 1f, 0f, 1f);\n    return;\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    main_1();\n    let _e2 = output0_;\n    let _e3 = output1_;\n    return FragmentOutput(_e2, _e3);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-empty-global-name.wgsl",
    "content": "struct type_1 {\n    member: i32,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> unnamed: type_1;\n\nfn function_() {\n    let _e3 = unnamed.member;\n    unnamed.member = (_e3 + 1i);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-f16-spv.wgsl",
    "content": "enable f16;\n\nstruct B {\n    b_1_: f16,\n    b_vec2_: vec2<f16>,\n    b_vec3_: vec3<f16>,\n    b_vec4_: vec4<f16>,\n    b_mat2_: mat2x2<f16>,\n    b_mat2x3_: mat2x3<f16>,\n    b_mat2x4_: mat2x4<f16>,\n    b_mat3x2_: mat3x2<f16>,\n    b_mat3_: mat3x3<f16>,\n    b_mat3x4_: mat3x4<f16>,\n    b_mat4x2_: mat4x2<f16>,\n    b_mat4x3_: mat4x3<f16>,\n    b_mat4_: mat4x4<f16>,\n}\n\nstruct A {\n    a_1_: f16,\n    a_vec2_: vec2<f16>,\n    a_vec3_: vec3<f16>,\n    a_vec4_: vec4<f16>,\n}\n\n@group(0) @binding(1) \nvar<storage, read_write> unnamed: B;\n@group(0) @binding(0) \nvar<uniform> unnamed_1: A;\n\nfn main_1() {\n    let _e3 = unnamed_1.a_1_;\n    unnamed.b_1_ = _e3;\n    let _e6 = unnamed_1.a_vec2_;\n    unnamed.b_vec2_ = _e6;\n    let _e9 = unnamed_1.a_vec3_;\n    unnamed.b_vec3_ = _e9;\n    let _e12 = unnamed_1.a_vec4_;\n    unnamed.b_vec4_ = _e12;\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-fetch_depth.wgsl",
    "content": "struct type_2 {\n    member: f32,\n}\n\nstruct type_4 {\n    member: vec2<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> global: type_2;\n@group(0) @binding(1) \nvar<storage> global_1: type_4;\n@group(0) @binding(2) \nvar global_2: texture_depth_2d;\n\nfn function_() {\n    let _e6 = global_1.member;\n    let _e7 = textureLoad(global_2, _e6, 0i);\n    global.member = vec4(_e7).x;\n    return;\n}\n\n@compute @workgroup_size(32, 1, 1) \nfn cull_fetch_depth() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-gather-cmp.wgsl",
    "content": "var<private> input_u002e_texture_coordinates_1: vec2<f32>;\n@group(0) @binding(0) \nvar texture: texture_depth_2d;\n@group(0) @binding(1) \nvar depth_sampler: sampler_comparison;\nvar<private> entryPointParam_main: vec4<f32>;\n\nfn main_1() {\n    let _e5 = input_u002e_texture_coordinates_1;\n    let _e6 = textureGatherCompare(texture, depth_sampler, _e5, 0.5f);\n    entryPointParam_main = _e6;\n    return;\n}\n\n@fragment \nfn main(@location(0) input_u002e_texture_coordinates: vec2<f32>) -> @location(0) vec4<f32> {\n    input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates;\n    main_1();\n    let _e3 = entryPointParam_main;\n    return _e3;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-gather.wgsl",
    "content": "var<private> input_u002e_texture_coordinates_1: vec2<f32>;\n@group(0) @binding(0) \nvar texture: texture_2d<f32>;\n@group(0) @binding(1) \nvar linear_sampler: sampler;\nvar<private> entryPointParam_main: vec4<f32>;\n\nfn main_1() {\n    let _e4 = input_u002e_texture_coordinates_1;\n    let _e5 = textureGather(1, texture, linear_sampler, _e4);\n    entryPointParam_main = _e5;\n    return;\n}\n\n@fragment \nfn main(@location(0) input_u002e_texture_coordinates: vec2<f32>) -> @location(0) vec4<f32> {\n    input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates;\n    main_1();\n    let _e3 = entryPointParam_main;\n    return _e3;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-inv-hyperbolic-trig-functions.wgsl",
    "content": "var<private> a: f32;\n\nfn main_1() {\n    var b: f32;\n    var c: f32;\n    var d: f32;\n\n    let _e4 = a;\n    b = asinh(_e4);\n    let _e6 = a;\n    c = acosh(_e6);\n    let _e8 = a;\n    d = atanh(_e8);\n    return;\n}\n\n@fragment \nfn main() {\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-load-ms-texture.wgsl",
    "content": "var<private> global: vec4<f32>;\nvar<private> entryPointParam_fs_main: vec4<f32>;\n@group(0) @binding(0) \nvar texture: texture_multisampled_2d<f32>;\n\nfn fs_main() {\n    var index: i32;\n    var color: vec4<f32>;\n\n    let _e9 = global;\n    index = 0i;\n    loop {\n        let _e12 = index;\n        if (_e12 < 8i) {\n        } else {\n            break;\n        }\n        let _e14 = index;\n        let _e15 = textureLoad(texture, vec2<i32>(_e9.xy), _e14);\n        let _e16 = color;\n        let _e18 = index;\n        index = (_e18 + 1i);\n        color = (_e16 + _e15);\n        continue;\n    }\n    let _e20 = color;\n    entryPointParam_fs_main = (_e20 * vec4<f32>(0.125f, 0.125f, 0.125f, 0.125f));\n    return;\n}\n\n@fragment \nfn main(@builtin(position) param: vec4<f32>) -> @location(0) vec4<f32> {\n    global = param;\n    fs_main();\n    let _e3 = entryPointParam_fs_main;\n    return _e3;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-non-semantic-debug.wgsl",
    "content": "fn main_1() {\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    main_1();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-per-vertex.wgsl",
    "content": "var<private> global: array<f32, 3>;\nvar<private> global_1: vec4<f32>;\n\nfn function_() {\n    let _e3 = global;\n    global_1 = vec4<f32>(_e3[0], _e3[1], _e3[2], 1f);\n    return;\n}\n\n@fragment \nfn fs_main(@location(0) @interpolate(per_vertex) param: array<f32, 3>) -> @location(0) vec4<f32> {\n    global = param;\n    function_();\n    let _e3 = global_1;\n    return _e3;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-quad-vert.wgsl",
    "content": "struct gl_PerVertex {\n    @builtin(position) gl_Position: vec4<f32>,\n    gl_PointSize: f32,\n    gl_ClipDistance: array<f32, 1>,\n    gl_CullDistance: array<f32, 1>,\n}\n\nstruct VertexOutput {\n    @location(0) member: vec2<f32>,\n    @builtin(position) gl_Position: vec4<f32>,\n}\n\nvar<private> v_uv: vec2<f32>;\nvar<private> a_uv_1: vec2<f32>;\nvar<private> unnamed: gl_PerVertex = gl_PerVertex(vec4<f32>(0f, 0f, 0f, 1f), 1f, array<f32, 1>(), array<f32, 1>());\nvar<private> a_pos_1: vec2<f32>;\n\nfn main_1() {\n    let _e6 = a_uv_1;\n    v_uv = _e6;\n    let _e7 = a_pos_1;\n    unnamed.gl_Position = vec4<f32>(_e7.x, _e7.y, 0f, 1f);\n    return;\n}\n\n@vertex \nfn main(@location(1) a_uv: vec2<f32>, @location(0) a_pos: vec2<f32>) -> VertexOutput {\n    a_uv_1 = a_uv;\n    a_pos_1 = a_pos;\n    main_1();\n    let _e7 = v_uv;\n    let _e8 = unnamed.gl_Position;\n    return VertexOutput(_e7, _e8);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-subgroup-barrier.wgsl",
    "content": "fn function_() {\n    subgroupBarrier();\n    subgroupBarrier();\n    return;\n}\n\n@compute @workgroup_size(64, 1, 1) \nfn main() {\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-subgroup-operations-s.wgsl",
    "content": "var<private> global: u32;\nvar<private> global_1: u32;\nvar<private> global_2: u32;\nvar<private> global_3: u32;\n\nfn function_() {\n    let _e5 = global_2;\n    let _e6 = global_3;\n    let _e9 = subgroupBallot(((_e6 & 1u) == 1u));\n    let _e10 = subgroupBallot();\n    let _e12 = subgroupAll((_e6 != 0u));\n    let _e14 = subgroupAny((_e6 == 0u));\n    let _e15 = subgroupAdd(_e6);\n    let _e16 = subgroupMul(_e6);\n    let _e17 = subgroupMin(_e6);\n    let _e18 = subgroupMax(_e6);\n    let _e19 = subgroupAnd(_e6);\n    let _e20 = subgroupOr(_e6);\n    let _e21 = subgroupXor(_e6);\n    let _e22 = subgroupExclusiveAdd(_e6);\n    let _e23 = subgroupExclusiveMul(_e6);\n    let _e24 = subgroupInclusiveAdd(_e6);\n    let _e25 = subgroupInclusiveMul(_e6);\n    let _e26 = subgroupBroadcastFirst(_e6);\n    let _e27 = subgroupBroadcast(_e6, 4u);\n    let _e30 = subgroupShuffle(_e6, ((_e5 - 1u) - _e6));\n    let _e31 = subgroupShuffleDown(_e6, 1u);\n    let _e32 = subgroupShuffleUp(_e6, 1u);\n    let _e34 = subgroupShuffleXor(_e6, (_e5 - 1u));\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(@builtin(num_subgroups) param: u32, @builtin(subgroup_id) param_1: u32, @builtin(subgroup_size) param_2: u32, @builtin(subgroup_invocation_id) param_3: u32) {\n    global = param;\n    global_1 = param_1;\n    global_2 = param_2;\n    global_3 = param_3;\n    function_();\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/spv-unnamed-gl-per-vertex.wgsl",
    "content": "struct type_4 {\n    @builtin(position) member: vec4<f32>,\n    member_1: f32,\n    member_2: array<f32, 1>,\n    member_3: array<f32, 1>,\n}\n\nvar<private> global: type_4 = type_4(vec4<f32>(0f, 0f, 0f, 1f), 1f, array<f32, 1>(), array<f32, 1>());\nvar<private> global_1: i32;\n\nfn function_() {\n    let _e9 = global_1;\n    global.member = vec4<f32>(select(1f, -4f, (_e9 == 0i)), select(-1f, 4f, (_e9 == 2i)), 0f, 1f);\n    return;\n}\n\n@vertex \nfn main(@builtin(vertex_index) param: u32) -> @builtin(position) vec4<f32> {\n    global_1 = i32(param);\n    function_();\n    let _e6 = global.member.y;\n    global.member.y = -(_e6);\n    let _e8 = global.member;\n    return _e8;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-6438-conflicting-idents.wgsl",
    "content": "struct OurVertexShaderOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) texcoord: vec2<f32>,\n}\n\n@vertex \nfn vs(@location(0) xy: vec2<f32>) -> OurVertexShaderOutput {\n    var vsOutput: OurVertexShaderOutput;\n\n    vsOutput.position = vec4<f32>(xy, 0f, 1f);\n    let _e6 = vsOutput;\n    return _e6;\n}\n\n@fragment \nfn fs() -> @location(0) vec4<f32> {\n    return vec4<f32>(1f, 0f, 0f, 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-6772-unpack-expr-accesses.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let phony = unpack4xI8(12u)[2i];\n    let phony_1 = unpack4xU8(12u).y;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-7995-unicode-idents.wgsl",
    "content": "@group(0) @binding(0) \nvar<storage> asdf: f32;\n\nfn compute() -> f32 {\n    let _e1 = asdf;\n    let u03b8_2_ = (_e1 + 9001f);\n    return u03b8_2_;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e0 = compute();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-8820-multiple-local-invocation-index-id.wgsl",
    "content": "struct Input {\n    @builtin(local_invocation_id) local_invocation_id: vec3<u32>,\n    @builtin(local_invocation_index) local_invocation_index: u32,\n}\n\nvar<workgroup> wg_var: u32;\n\n@compute @workgroup_size(1, 1, 1) \nfn compute1_(input: Input) {\n    wg_var = (input.local_invocation_index * 2u);\n    let _e6 = wg_var;\n    wg_var = (_e6 + input.local_invocation_id.x);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-atomic.wgsl",
    "content": "@group(0) @binding(0) \nvar<storage, read_write> atomic_i32_: atomic<i32>;\n@group(0) @binding(1) \nvar<storage, read_write> atomic_u32_: atomic<u32>;\n\nfn test_atomic_i32_() {\n    atomicStore((&atomic_i32_), 1i);\n    let _e5 = atomicCompareExchangeWeak((&atomic_i32_), 1i, 1i);\n    let _e9 = atomicCompareExchangeWeak((&atomic_i32_), 1i, 1i);\n    let _e12 = atomicAdd((&atomic_i32_), 1i);\n    let _e15 = atomicSub((&atomic_i32_), 1i);\n    let _e18 = atomicAnd((&atomic_i32_), 1i);\n    let _e21 = atomicXor((&atomic_i32_), 1i);\n    let _e24 = atomicOr((&atomic_i32_), 1i);\n    let _e27 = atomicMin((&atomic_i32_), 1i);\n    let _e30 = atomicMax((&atomic_i32_), 1i);\n    let _e33 = atomicExchange((&atomic_i32_), 1i);\n    return;\n}\n\nfn test_atomic_u32_() {\n    atomicStore((&atomic_u32_), 1u);\n    let _e5 = atomicCompareExchangeWeak((&atomic_u32_), 1u, 1u);\n    let _e9 = atomicCompareExchangeWeak((&atomic_u32_), 1u, 1u);\n    let _e12 = atomicAdd((&atomic_u32_), 1u);\n    let _e15 = atomicSub((&atomic_u32_), 1u);\n    let _e18 = atomicAnd((&atomic_u32_), 1u);\n    let _e21 = atomicXor((&atomic_u32_), 1u);\n    let _e24 = atomicOr((&atomic_u32_), 1u);\n    let _e27 = atomicMin((&atomic_u32_), 1u);\n    let _e30 = atomicMax((&atomic_u32_), 1u);\n    let _e33 = atomicExchange((&atomic_u32_), 1u);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    test_atomic_i32_();\n    test_atomic_u32_();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-builtins.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn f() {\n    var clamp_aiaiai: i32 = 1i;\n    var clamp_aiaiaf: f32 = 1f;\n    var clamp_aiaii: i32 = 1i;\n    var clamp_aiaif: f32 = 1f;\n    var clamp_aiafai: f32 = 1f;\n    var clamp_aiafaf: f32 = 1f;\n    var clamp_aiaff: f32 = 1f;\n    var clamp_aiiai: i32 = 1i;\n    var clamp_aiii: i32 = 1i;\n    var clamp_aifai: f32 = 1f;\n    var clamp_aifaf: f32 = 1f;\n    var clamp_aiff: f32 = 1f;\n    var clamp_afaiai: f32 = 1f;\n    var clamp_afaiaf: f32 = 1f;\n    var clamp_afaif: f32 = 1f;\n    var clamp_afafai: f32 = 1f;\n    var clamp_afafaf: f32 = 1f;\n    var clamp_afaff: f32 = 1f;\n    var clamp_affai: f32 = 1f;\n    var clamp_affaf: f32 = 1f;\n    var clamp_afff: f32 = 1f;\n    var clamp_iaiai: i32 = 1i;\n    var clamp_iaii: i32 = 1i;\n    var clamp_iiai: i32 = 1i;\n    var clamp_iii: i32 = 1i;\n    var clamp_faiai: f32 = 1f;\n    var clamp_faiaf: f32 = 1f;\n    var clamp_faif: f32 = 1f;\n    var clamp_fafai: f32 = 1f;\n    var clamp_fafaf: f32 = 1f;\n    var clamp_faff: f32 = 1f;\n    var clamp_ffai: f32 = 1f;\n    var clamp_ffaf: f32 = 1f;\n    var clamp_fff: f32 = 1f;\n    var min_aiai: i32 = 1i;\n    var min_aiaf: f32 = 1f;\n    var min_aii: i32 = 1i;\n    var min_aif: f32 = 1f;\n    var min_afai: f32 = 1f;\n    var min_afaf: f32 = 1f;\n    var min_aff: f32 = 1f;\n    var min_iai: i32 = 1i;\n    var min_ii: i32 = 1i;\n    var min_fai: f32 = 1f;\n    var min_faf: f32 = 1f;\n    var min_ff: f32 = 1f;\n    var pow_aiai: f32 = 1f;\n    var pow_aiaf: f32 = 1f;\n    var pow_aif: f32 = 1f;\n    var pow_afai: f32 = 1f;\n    var pow_afaf: f32 = 1f;\n    var pow_aff: f32 = 1f;\n    var pow_fai: f32 = 1f;\n    var pow_faf: f32 = 1f;\n    var pow_ff: f32 = 1f;\n\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-const.wgsl",
    "content": "struct S {\n    f: f32,\n    i: i32,\n    u: u32,\n}\n\nconst xvipaiai: vec2<i32> = vec2<i32>(42i, 43i);\nconst xvupaiai: vec2<u32> = vec2<u32>(44u, 45u);\nconst xvfpaiai: vec2<f32> = vec2<f32>(46f, 47f);\nconst xvfpafaf: vec2<f32> = vec2<f32>(48f, 49f);\nconst xvfpaiaf: vec2<f32> = vec2<f32>(48f, 49f);\nconst xvupuai: vec2<u32> = vec2<u32>(42u, 43u);\nconst xvupaiu: vec2<u32> = vec2<u32>(42u, 43u);\nconst xvuuai: vec2<u32> = vec2<u32>(42u, 43u);\nconst xvuaiu: vec2<u32> = vec2<u32>(42u, 43u);\nconst xvip: vec2<i32> = vec2<i32>(0i, 0i);\nconst xvup: vec2<u32> = vec2<u32>(0u, 0u);\nconst xvfp: vec2<f32> = vec2<f32>(0f, 0f);\nconst xmfp: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\nconst xmfpaiaiaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nconst xmfpafaiaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nconst xmfpaiafaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nconst xmfpaiaiafai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nconst xmfpaiaiaiaf: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nconst ivis_ai: vec2<i32> = vec2(1i);\nconst ivus_ai: vec2<u32> = vec2(1u);\nconst ivfs_ai: vec2<f32> = vec2(1f);\nconst ivfs_af: vec2<f32> = vec2(1f);\nconst iafafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\nconst iafaiai: array<f32, 2> = array<f32, 2>(1f, 2f);\nconst xaipaiai: array<i32, 2> = array<i32, 2>(1i, 2i);\nconst xaupaiai: array<u32, 2> = array<u32, 2>(1u, 2u);\nconst xafpaiaf: array<f32, 2> = array<f32, 2>(1f, 2f);\nconst xafpafai: array<f32, 2> = array<f32, 2>(1f, 2f);\nconst xafpafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\nconst s_f_i_u: S = S(1f, 1i, 1u);\nconst s_f_iai: S = S(1f, 1i, 1u);\nconst s_fai_u: S = S(1f, 1i, 1u);\nconst s_faiai: S = S(1f, 1i, 1u);\nconst saf_i_u: S = S(1f, 1i, 1u);\nconst saf_iai: S = S(1f, 1i, 1u);\nconst safai_u: S = S(1f, 1i, 1u);\nconst safaiai: S = S(1f, 1i, 1u);\nconst xvisai: vec2<i32> = vec2(1i);\nconst xvusai: vec2<u32> = vec2(1u);\nconst xvfsai: vec2<f32> = vec2(1f);\nconst xvfsaf: vec2<f32> = vec2(1f);\nconst ivfr_f_f: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivfr_f_af: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivfraf_f: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivfraf_af: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivf_fr_f: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivf_fraf: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivf_afr_f: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivf_afraf: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivfr_f_ai: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivfrai_f: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivfrai_ai: vec3<f32> = vec3<f32>(vec2<f32>(1f, 2f), 3f);\nconst ivf_frai: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivf_air_f: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\nconst ivf_airai: vec3<f32> = vec3<f32>(1f, vec2<f32>(2f, 3f));\n\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-function-calls.wgsl",
    "content": "fn func_f(a: f32) {\n    return;\n}\n\nfn func_i(a_1: i32) {\n    return;\n}\n\nfn func_u(a_2: u32) {\n    return;\n}\n\nfn func_vf(a_3: vec2<f32>) {\n    return;\n}\n\nfn func_vi(a_4: vec2<i32>) {\n    return;\n}\n\nfn func_vu(a_5: vec2<u32>) {\n    return;\n}\n\nfn func_mf(a_6: mat2x2<f32>) {\n    return;\n}\n\nfn func_af(a_7: array<f32, 2>) {\n    return;\n}\n\nfn func_ai(a_8: array<i32, 2>) {\n    return;\n}\n\nfn func_au(a_9: array<u32, 2>) {\n    return;\n}\n\nfn func_f_i(a_10: f32, b: i32) {\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    func_f(0f);\n    func_f(0f);\n    func_i(0i);\n    func_u(0u);\n    func_f(0f);\n    func_f(0f);\n    func_i(0i);\n    func_u(0u);\n    func_vf(vec2(0f));\n    func_vf(vec2(0f));\n    func_vi(vec2(0i));\n    func_vu(vec2(0u));\n    func_vf(vec2(0f));\n    func_vf(vec2(0f));\n    func_vi(vec2(0i));\n    func_vu(vec2(0u));\n    func_mf(mat2x2<f32>(vec2(0f), vec2(0f)));\n    func_mf(mat2x2<f32>(vec2(0f), vec2(0f)));\n    func_mf(mat2x2<f32>(vec2(0f), vec2(0f)));\n    func_af(array<f32, 2>(0f, 0f));\n    func_af(array<f32, 2>(0f, 0f));\n    func_ai(array<i32, 2>(0i, 0i));\n    func_au(array<u32, 2>(0u, 0u));\n    func_af(array<f32, 2>(0f, 0f));\n    func_af(array<f32, 2>(0f, 0f));\n    func_ai(array<i32, 2>(0i, 0i));\n    func_au(array<u32, 2>(0u, 0u));\n    func_f_i(0f, 0i);\n    func_f_i(0f, 0i);\n    func_f_i(0f, 0i);\n    func_f_i(0f, 0i);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-let.wgsl",
    "content": "fn all_constant_arguments() {\n    let xvipaiai = vec2<i32>(42i, 43i);\n    let xvupaiai = vec2<u32>(44u, 45u);\n    let xvfpaiai = vec2<f32>(46f, 47f);\n    let xvfpafaf = vec2<f32>(48f, 49f);\n    let xvfpaiaf = vec2<f32>(48f, 49f);\n    let xvupuai = vec2<u32>(42u, 43u);\n    let xvupaiu = vec2<u32>(42u, 43u);\n    let xvuuai = vec2<u32>(42u, 43u);\n    let xvuaiu = vec2<u32>(42u, 43u);\n    let xvip = vec2<i32>(0i, 0i);\n    let xvup = vec2<u32>(0u, 0u);\n    let xvfp = vec2<f32>(0f, 0f);\n    let xmfp = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    let xmfpaiaiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpafaiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpaiafaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpaiaiafai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpaiaiaiaf = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfp_faiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpai_faiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpaiai_fai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xmfpaiaiai_f = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let xvispai = vec2(1i);\n    let xvfspaf = vec2(1f);\n    let xvis_ai = vec2(1i);\n    let xvus_ai = vec2(1u);\n    let xvfs_ai = vec2(1f);\n    let xvfs_af = vec2(1f);\n    let xafafaf = array<f32, 2>(1f, 2f);\n    let xaf_faf = array<f32, 2>(1f, 2f);\n    let xafaf_f = array<f32, 2>(1f, 2f);\n    let xafaiai = array<f32, 2>(1f, 2f);\n    let xai_iai = array<i32, 2>(1i, 2i);\n    let xaiai_i = array<i32, 2>(1i, 2i);\n    let xaipaiai = array<i32, 2>(1i, 2i);\n    let xafpaiai = array<f32, 2>(1f, 2f);\n    let xafpaiaf = array<f32, 2>(1f, 2f);\n    let xafpafai = array<f32, 2>(1f, 2f);\n    let xafpafaf = array<f32, 2>(1f, 2f);\n    let xavipai = array<vec3<i32>, 1>(vec3(1i));\n    let xavfpai = array<vec3<f32>, 1>(vec3(1f));\n    let xavfpaf = array<vec3<f32>, 1>(vec3(1f));\n    let xvisai = vec2(1i);\n    let xvusai = vec2(1u);\n    let xvfsai = vec2(1f);\n    let xvfsaf = vec2(1f);\n    let iaipaiai = array<i32, 2>(1i, 2i);\n    let iafpaiaf = array<f32, 2>(1f, 2f);\n    let iafpafai = array<f32, 2>(1f, 2f);\n    let iafpafaf = array<f32, 2>(1f, 2f);\n    return;\n}\n\nfn mixed_constant_and_runtime_arguments() {\n    var u: u32;\n    var i: i32;\n    var f: f32;\n\n    let _e3 = u;\n    let xvupuai_1 = vec2<u32>(_e3, 43u);\n    let _e6 = u;\n    let xvupaiu_1 = vec2<u32>(42u, _e6);\n    let _e9 = f;\n    let xvfpfai = vec2<f32>(_e9, 47f);\n    let _e12 = f;\n    let xvfpfaf = vec2<f32>(_e12, 49f);\n    let _e15 = u;\n    let xvuuai_1 = vec2<u32>(_e15, 43u);\n    let _e18 = u;\n    let xvuaiu_1 = vec2<u32>(42u, _e18);\n    let _e21 = f;\n    let xmfp_faiaiai_1 = mat2x2<f32>(vec2<f32>(_e21, 2f), vec2<f32>(3f, 4f));\n    let _e28 = f;\n    let xmfpai_faiai_1 = mat2x2<f32>(vec2<f32>(1f, _e28), vec2<f32>(3f, 4f));\n    let _e35 = f;\n    let xmfpaiai_fai_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(_e35, 4f));\n    let _e42 = f;\n    let xmfpaiaiai_f_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, _e42));\n    let _e49 = f;\n    let xaf_faf_1 = array<f32, 2>(_e49, 2f);\n    let _e52 = f;\n    let xafaf_f_1 = array<f32, 2>(1f, _e52);\n    let _e55 = f;\n    let xaf_fai = array<f32, 2>(_e55, 2f);\n    let _e58 = f;\n    let xafai_f = array<f32, 2>(1f, _e58);\n    let _e61 = i;\n    let xai_iai_1 = array<i32, 2>(_e61, 2i);\n    let _e64 = i;\n    let xaiai_i_1 = array<i32, 2>(1i, _e64);\n    let _e67 = f;\n    let xafp_faf = array<f32, 2>(_e67, 2f);\n    let _e70 = f;\n    let xafpaf_f = array<f32, 2>(1f, _e70);\n    let _e73 = f;\n    let xafp_fai = array<f32, 2>(_e73, 2f);\n    let _e76 = f;\n    let xafpai_f = array<f32, 2>(1f, _e76);\n    let _e79 = i;\n    let xaip_iai = array<i32, 2>(_e79, 2i);\n    let _e82 = i;\n    let xaipai_i = array<i32, 2>(1i, _e82);\n    let _e85 = i;\n    let xvisi = vec2(_e85);\n    let _e87 = u;\n    let xvusu = vec2(_e87);\n    let _e89 = f;\n    let xvfsf = vec2(_e89);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-operators.wgsl",
    "content": "const plus_fafaf_1: f32 = 3f;\nconst plus_fafai_1: f32 = 3f;\nconst plus_faf_f_1: f32 = 3f;\nconst plus_faiaf_1: f32 = 3f;\nconst plus_faiai_1: f32 = 3f;\nconst plus_fai_f_1: f32 = 3f;\nconst plus_f_faf_1: f32 = 3f;\nconst plus_f_fai_1: f32 = 3f;\nconst plus_f_f_f_1: f32 = 3f;\nconst plus_iaiai_1: i32 = 3i;\nconst plus_iai_i_1: i32 = 3i;\nconst plus_i_iai_1: i32 = 3i;\nconst plus_i_i_i_1: i32 = 3i;\nconst plus_uaiai_1: u32 = 3u;\nconst plus_uai_u_1: u32 = 3u;\nconst plus_u_uai_1: u32 = 3u;\nconst plus_u_u_u_1: u32 = 3u;\nconst bitflip_u_u: u32 = 0u;\nconst bitflip_uai: u32 = 0u;\nconst least_i32_: i32 = i32(-2147483648);\nconst least_f32_: f32 = -340282350000000000000000000000000000000f;\nconst shl_iaiai: i32 = 4i;\nconst shl_iai_u_1: i32 = 4i;\nconst shl_uaiai: u32 = 4u;\nconst shl_uai_u: u32 = 4u;\nconst shr_iaiai: i32 = 0i;\nconst shr_iai_u_1: i32 = 0i;\nconst shr_uaiai: u32 = 0u;\nconst shr_uai_u: u32 = 0u;\nconst wgpu_4492_: i32 = i32(-2147483648);\n\nvar<workgroup> a: array<u32, 64>;\n\nfn runtime_values() {\n    var f: f32 = 42f;\n    var i: i32 = 43i;\n    var u: u32 = 44u;\n    var plus_fafaf: f32 = 3f;\n    var plus_fafai: f32 = 3f;\n    var plus_faf_f: f32;\n    var plus_faiaf: f32 = 3f;\n    var plus_faiai: f32 = 3f;\n    var plus_fai_f: f32;\n    var plus_f_faf: f32;\n    var plus_f_fai: f32;\n    var plus_f_f_f: f32;\n    var plus_iaiai: i32 = 3i;\n    var plus_iai_i: i32;\n    var plus_i_iai: i32;\n    var plus_i_i_i: i32;\n    var plus_uaiai: u32 = 3u;\n    var plus_uai_u: u32;\n    var plus_u_uai: u32;\n    var plus_u_u_u: u32;\n    var shl_iai_u: i32;\n    var shr_iai_u: i32;\n\n    let _e8 = f;\n    plus_faf_f = (1f + _e8);\n    let _e14 = f;\n    plus_fai_f = (1f + _e14);\n    let _e18 = f;\n    plus_f_faf = (_e18 + 2f);\n    let _e22 = f;\n    plus_f_fai = (_e22 + 2f);\n    let _e26 = f;\n    let _e27 = f;\n    plus_f_f_f = (_e26 + _e27);\n    let _e31 = i;\n    plus_iai_i = (1i + _e31);\n    let _e35 = i;\n    plus_i_iai = (_e35 + 2i);\n    let _e39 = i;\n    let _e40 = i;\n    plus_i_i_i = (_e39 + _e40);\n    let _e44 = u;\n    plus_uai_u = (1u + _e44);\n    let _e48 = u;\n    plus_u_uai = (_e48 + 2u);\n    let _e52 = u;\n    let _e53 = u;\n    plus_u_u_u = (_e52 + _e53);\n    let _e56 = u;\n    shl_iai_u = (1i << _e56);\n    let _e60 = u;\n    shr_iai_u = (1i << _e60);\n    return;\n}\n\nfn wgpu_4445_() {\n    return;\n}\n\nfn wgpu_4435_() {\n    let y = a[(1i - 1i)];\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    runtime_values();\n    wgpu_4445_();\n    wgpu_4435_();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-return.wgsl",
    "content": "fn return_i32_ai() -> i32 {\n    return 1i;\n}\n\nfn return_u32_ai() -> u32 {\n    return 1u;\n}\n\nfn return_f32_ai() -> f32 {\n    return 1f;\n}\n\nfn return_f32_af() -> f32 {\n    return 1f;\n}\n\nfn return_vec2f32_ai() -> vec2<f32> {\n    return vec2(1f);\n}\n\nfn return_arrf32_ai() -> array<f32, 4> {\n    return array<f32, 4>(1f, 1f, 1f, 1f);\n}\n\nfn return_const_f32_const_ai() -> f32 {\n    return 1f;\n}\n\nfn return_vec2f32_const_ai() -> vec2<f32> {\n    return vec2(1f);\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e0 = return_i32_ai();\n    let _e1 = return_u32_ai();\n    let _e2 = return_f32_ai();\n    let _e3 = return_f32_af();\n    let _e4 = return_vec2f32_ai();\n    let _e5 = return_arrf32_ai();\n    let _e6 = return_const_f32_const_ai();\n    let _e7 = return_vec2f32_const_ai();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-abstract-types-var.wgsl",
    "content": "var<private> xvipaiai_1: vec2<i32> = vec2<i32>(42i, 43i);\nvar<private> xvupaiai_1: vec2<u32> = vec2<u32>(44u, 45u);\nvar<private> xvfpaiai_1: vec2<f32> = vec2<f32>(46f, 47f);\nvar<private> xvfpafaf_1: vec2<f32> = vec2<f32>(48f, 49f);\nvar<private> xvfpaiaf_1: vec2<f32> = vec2<f32>(48f, 49f);\nvar<private> xvupuai_2: vec2<u32> = vec2<u32>(42u, 43u);\nvar<private> xvupaiu_2: vec2<u32> = vec2<u32>(42u, 43u);\nvar<private> xvuuai_2: vec2<u32> = vec2<u32>(42u, 43u);\nvar<private> xvuaiu_2: vec2<u32> = vec2<u32>(42u, 43u);\nvar<private> xvip_1: vec2<i32> = vec2<i32>(0i, 0i);\nvar<private> xvup_1: vec2<u32> = vec2<u32>(0u, 0u);\nvar<private> xvfp_1: vec2<f32> = vec2<f32>(0f, 0f);\nvar<private> xmfp_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\nvar<private> xmfpaiaiaiai_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nvar<private> xmfpafaiaiai_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nvar<private> xmfpaiafaiai_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nvar<private> xmfpaiaiafai_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nvar<private> xmfpaiaiaiaf_1: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\nvar<private> xvispai_1: vec2<i32> = vec2(1i);\nvar<private> xvfspaf_1: vec2<f32> = vec2(1f);\nvar<private> xvis_ai_1: vec2<i32> = vec2(1i);\nvar<private> xvus_ai_1: vec2<u32> = vec2(1u);\nvar<private> xvfs_ai_1: vec2<f32> = vec2(1f);\nvar<private> xvfs_af_1: vec2<f32> = vec2(1f);\nvar<private> xafafaf_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> xafaiai_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> xaipaiai_1: array<i32, 2> = array<i32, 2>(1i, 2i);\nvar<private> xaupaiai: array<u32, 2> = array<u32, 2>(1u, 2u);\nvar<private> xafpaiaf_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> xafpafai_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> xafpafaf_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> xavipai_1: array<vec3<i32>, 1> = array<vec3<i32>, 1>(vec3(1i));\nvar<private> xavfpai_1: array<vec3<f32>, 1> = array<vec3<f32>, 1>(vec3(1f));\nvar<private> xavfpaf_1: array<vec3<f32>, 1> = array<vec3<f32>, 1>(vec3(1f));\nvar<private> xvisai_1: vec2<i32> = vec2(1i);\nvar<private> xvusai_1: vec2<u32> = vec2(1u);\nvar<private> xvfsai_1: vec2<f32> = vec2(1f);\nvar<private> xvfsaf_1: vec2<f32> = vec2(1f);\nvar<private> ivispai: vec2<i32> = vec2(1i);\nvar<private> ivfspaf: vec2<f32> = vec2(1f);\nvar<private> ivis_ai: vec2<i32> = vec2(1i);\nvar<private> ivus_ai: vec2<u32> = vec2(1u);\nvar<private> ivfs_ai: vec2<f32> = vec2(1f);\nvar<private> ivfs_af: vec2<f32> = vec2(1f);\nvar<private> iafafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> iafaiai: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> iaipaiai_1: array<i32, 2> = array<i32, 2>(1i, 2i);\nvar<private> iafpafaf_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> iafpaiaf_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> iafpafai_1: array<f32, 2> = array<f32, 2>(1f, 2f);\nvar<private> iavipai: array<vec3<i32>, 1> = array<vec3<i32>, 1>(vec3(1i));\nvar<private> iavfpai: array<vec3<i32>, 1> = array<vec3<i32>, 1>(vec3(1i));\nvar<private> iavfpaf: array<vec3<f32>, 1> = array<vec3<f32>, 1>(vec3(1f));\n\nfn globals() {\n    let phony = xvipaiai_1;\n    let phony_1 = xvupaiai_1;\n    let phony_2 = xvfpaiai_1;\n    let phony_3 = xvfpafaf_1;\n    let phony_4 = xvfpaiaf_1;\n    let phony_5 = xvupuai_2;\n    let phony_6 = xvupaiu_2;\n    let phony_7 = xvuuai_2;\n    let phony_8 = xvuaiu_2;\n    let phony_9 = xvip_1;\n    let phony_10 = xvup_1;\n    let phony_11 = xvfp_1;\n    let phony_12 = xmfp_1;\n    let phony_13 = xmfpaiaiaiai_1;\n    let phony_14 = xmfpafaiaiai_1;\n    let phony_15 = xmfpaiafaiai_1;\n    let phony_16 = xmfpaiaiafai_1;\n    let phony_17 = xmfpaiaiaiaf_1;\n    let phony_18 = xvispai_1;\n    let phony_19 = xvfspaf_1;\n    let phony_20 = xvis_ai_1;\n    let phony_21 = xvus_ai_1;\n    let phony_22 = xvfs_ai_1;\n    let phony_23 = xvfs_af_1;\n    let phony_24 = xafafaf_1;\n    let phony_25 = xafaiai_1;\n    let phony_26 = xaipaiai_1;\n    let phony_27 = xaupaiai;\n    let phony_28 = xafpaiaf_1;\n    let phony_29 = xafpafai_1;\n    let phony_30 = xafpafaf_1;\n    let phony_31 = xavipai_1;\n    let phony_32 = xavfpai_1;\n    let phony_33 = xavfpaf_1;\n    let phony_34 = xvisai_1;\n    let phony_35 = xvusai_1;\n    let phony_36 = xvfsai_1;\n    let phony_37 = xvfsaf_1;\n    let phony_38 = ivispai;\n    let phony_39 = ivfspaf;\n    let phony_40 = ivis_ai;\n    let phony_41 = ivus_ai;\n    let phony_42 = ivfs_ai;\n    let phony_43 = ivfs_af;\n    let phony_44 = iafafaf;\n    let phony_45 = iafaiai;\n    let phony_46 = iaipaiai_1;\n    let phony_47 = iafpafaf_1;\n    let phony_48 = iafpaiaf_1;\n    let phony_49 = iafpafai_1;\n    let phony_50 = iavipai;\n    let phony_51 = iavfpai;\n    let phony_52 = iavfpaf;\n    return;\n}\n\nfn all_constant_arguments() {\n    var xvipaiai: vec2<i32> = vec2<i32>(42i, 43i);\n    var xvupaiai: vec2<u32> = vec2<u32>(44u, 45u);\n    var xvfpaiai: vec2<f32> = vec2<f32>(46f, 47f);\n    var xvfpafaf: vec2<f32> = vec2<f32>(48f, 49f);\n    var xvfpaiaf: vec2<f32> = vec2<f32>(48f, 49f);\n    var xvupuai: vec2<u32> = vec2<u32>(42u, 43u);\n    var xvupaiu: vec2<u32> = vec2<u32>(42u, 43u);\n    var xvuuai: vec2<u32> = vec2<u32>(42u, 43u);\n    var xvuaiu: vec2<u32> = vec2<u32>(42u, 43u);\n    var xvip: vec2<i32> = vec2<i32>(0i, 0i);\n    var xvup: vec2<u32> = vec2<u32>(0u, 0u);\n    var xvfp: vec2<f32> = vec2<f32>(0f, 0f);\n    var xmfp: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    var xmfpaiaiaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpafaiaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpaiafaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpaiaiafai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpaiaiaiaf: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfp_faiaiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpai_faiai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpaiai_fai: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xmfpaiaiai_f: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    var xvispai: vec2<i32> = vec2(1i);\n    var xvfspaf: vec2<f32> = vec2(1f);\n    var xvis_ai: vec2<i32> = vec2(1i);\n    var xvus_ai: vec2<u32> = vec2(1u);\n    var xvfs_ai: vec2<f32> = vec2(1f);\n    var xvfs_af: vec2<f32> = vec2(1f);\n    var xafafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xaf_faf: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xafaf_f: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xafaiai: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xai_iai: array<i32, 2> = array<i32, 2>(1i, 2i);\n    var xaiai_i: array<i32, 2> = array<i32, 2>(1i, 2i);\n    var xaipaiai: array<i32, 2> = array<i32, 2>(1i, 2i);\n    var xafpaiai: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xafpaiaf: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xafpafai: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xafpafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var xavipai: array<vec3<i32>, 1> = array<vec3<i32>, 1>(vec3(1i));\n    var xavfpai: array<vec3<f32>, 1> = array<vec3<f32>, 1>(vec3(1f));\n    var xavfpaf: array<vec3<f32>, 1> = array<vec3<f32>, 1>(vec3(1f));\n    var xvisai: vec2<i32> = vec2(1i);\n    var xvusai: vec2<u32> = vec2(1u);\n    var xvfsai: vec2<f32> = vec2(1f);\n    var xvfsaf: vec2<f32> = vec2(1f);\n    var iaipaiai: array<i32, 2> = array<i32, 2>(1i, 2i);\n    var iafpaiaf: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var iafpafai: array<f32, 2> = array<f32, 2>(1f, 2f);\n    var iafpafaf: array<f32, 2> = array<f32, 2>(1f, 2f);\n\n    xvipaiai = vec2<i32>(42i, 43i);\n    xvupaiai = vec2<u32>(44u, 45u);\n    xvfpaiai = vec2<f32>(46f, 47f);\n    xvfpafaf = vec2<f32>(48f, 49f);\n    xvfpaiaf = vec2<f32>(48f, 49f);\n    xvupuai = vec2<u32>(42u, 43u);\n    xvupaiu = vec2<u32>(42u, 43u);\n    xvuuai = vec2<u32>(42u, 43u);\n    xvuaiu = vec2<u32>(42u, 43u);\n    xvip = vec2<i32>(0i, 0i);\n    xvup = vec2<u32>(0u, 0u);\n    xvfp = vec2<f32>(0f, 0f);\n    xmfp = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    xmfpaiaiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpafaiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpaiafaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpaiaiafai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpaiaiaiaf = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfp_faiaiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpai_faiai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpaiai_fai = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xmfpaiaiai_f = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    xvispai = vec2(1i);\n    xvfspaf = vec2(1f);\n    xvis_ai = vec2(1i);\n    xvus_ai = vec2(1u);\n    xvfs_ai = vec2(1f);\n    xvfs_af = vec2(1f);\n    xafafaf = array<f32, 2>(1f, 2f);\n    xaf_faf = array<f32, 2>(1f, 2f);\n    xafaf_f = array<f32, 2>(1f, 2f);\n    xafaiai = array<f32, 2>(1f, 2f);\n    xai_iai = array<i32, 2>(1i, 2i);\n    xaiai_i = array<i32, 2>(1i, 2i);\n    xaipaiai = array<i32, 2>(1i, 2i);\n    xafpaiai = array<f32, 2>(1f, 2f);\n    xafpaiaf = array<f32, 2>(1f, 2f);\n    xafpafai = array<f32, 2>(1f, 2f);\n    xafpafaf = array<f32, 2>(1f, 2f);\n    xavipai = array<vec3<i32>, 1>(vec3(1i));\n    xavfpai = array<vec3<f32>, 1>(vec3(1f));\n    xavfpaf = array<vec3<f32>, 1>(vec3(1f));\n    xvisai = vec2(1i);\n    xvusai = vec2(1u);\n    xvfsai = vec2(1f);\n    xvfsaf = vec2(1f);\n    iaipaiai = array<i32, 2>(1i, 2i);\n    iafpaiaf = array<f32, 2>(1f, 2f);\n    iafpafai = array<f32, 2>(1f, 2f);\n    iafpafaf = array<f32, 2>(1f, 2f);\n    return;\n}\n\nfn mixed_constant_and_runtime_arguments() {\n    var u: u32;\n    var i: i32;\n    var f: f32;\n    var xvupuai_1: vec2<u32>;\n    var xvupaiu_1: vec2<u32>;\n    var xvfpfai: vec2<f32>;\n    var xvfpfaf: vec2<f32>;\n    var xvuuai_1: vec2<u32>;\n    var xvuaiu_1: vec2<u32>;\n    var xmfp_faiaiai_1: mat2x2<f32>;\n    var xmfpai_faiai_1: mat2x2<f32>;\n    var xmfpaiai_fai_1: mat2x2<f32>;\n    var xmfpaiaiai_f_1: mat2x2<f32>;\n    var xaf_faf_1: array<f32, 2>;\n    var xafaf_f_1: array<f32, 2>;\n    var xaf_fai: array<f32, 2>;\n    var xafai_f: array<f32, 2>;\n    var xai_iai_1: array<i32, 2>;\n    var xaiai_i_1: array<i32, 2>;\n    var xafp_faf: array<f32, 2>;\n    var xafpaf_f: array<f32, 2>;\n    var xafp_fai: array<f32, 2>;\n    var xafpai_f: array<f32, 2>;\n    var xaip_iai: array<i32, 2>;\n    var xaipai_i: array<i32, 2>;\n    var xvisi: vec2<i32>;\n    var xvusu: vec2<u32>;\n    var xvfsf: vec2<f32>;\n\n    let _e3 = u;\n    xvupuai_1 = vec2<u32>(_e3, 43u);\n    let _e7 = u;\n    xvupaiu_1 = vec2<u32>(42u, _e7);\n    let _e11 = f;\n    xvfpfai = vec2<f32>(_e11, 47f);\n    let _e15 = f;\n    xvfpfaf = vec2<f32>(_e15, 49f);\n    let _e19 = u;\n    xvuuai_1 = vec2<u32>(_e19, 43u);\n    let _e23 = u;\n    xvuaiu_1 = vec2<u32>(42u, _e23);\n    let _e27 = f;\n    xmfp_faiaiai_1 = mat2x2<f32>(vec2<f32>(_e27, 2f), vec2<f32>(3f, 4f));\n    let _e35 = f;\n    xmfpai_faiai_1 = mat2x2<f32>(vec2<f32>(1f, _e35), vec2<f32>(3f, 4f));\n    let _e43 = f;\n    xmfpaiai_fai_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(_e43, 4f));\n    let _e51 = f;\n    xmfpaiaiai_f_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, _e51));\n    let _e59 = f;\n    xaf_faf_1 = array<f32, 2>(_e59, 2f);\n    let _e63 = f;\n    xafaf_f_1 = array<f32, 2>(1f, _e63);\n    let _e67 = f;\n    xaf_fai = array<f32, 2>(_e67, 2f);\n    let _e71 = f;\n    xafai_f = array<f32, 2>(1f, _e71);\n    let _e75 = i;\n    xai_iai_1 = array<i32, 2>(_e75, 2i);\n    let _e79 = i;\n    xaiai_i_1 = array<i32, 2>(1i, _e79);\n    let _e83 = f;\n    xafp_faf = array<f32, 2>(_e83, 2f);\n    let _e87 = f;\n    xafpaf_f = array<f32, 2>(1f, _e87);\n    let _e91 = f;\n    xafp_fai = array<f32, 2>(_e91, 2f);\n    let _e95 = f;\n    xafpai_f = array<f32, 2>(1f, _e95);\n    let _e99 = i;\n    xaip_iai = array<i32, 2>(_e99, 2i);\n    let _e103 = i;\n    xaipai_i = array<i32, 2>(1i, _e103);\n    let _e107 = i;\n    xvisi = vec2(_e107);\n    let _e110 = u;\n    xvusu = vec2(_e110);\n    let _e113 = f;\n    xvfsf = vec2(_e113);\n    let _e116 = u;\n    xvupuai_1 = vec2<u32>(_e116, 43u);\n    let _e119 = u;\n    xvupaiu_1 = vec2<u32>(42u, _e119);\n    let _e122 = u;\n    xvuuai_1 = vec2<u32>(_e122, 43u);\n    let _e125 = u;\n    xvuaiu_1 = vec2<u32>(42u, _e125);\n    let _e128 = f;\n    xmfp_faiaiai_1 = mat2x2<f32>(vec2<f32>(_e128, 2f), vec2<f32>(3f, 4f));\n    let _e135 = f;\n    xmfpai_faiai_1 = mat2x2<f32>(vec2<f32>(1f, _e135), vec2<f32>(3f, 4f));\n    let _e142 = f;\n    xmfpaiai_fai_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(_e142, 4f));\n    let _e149 = f;\n    xmfpaiaiai_f_1 = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, _e149));\n    let _e156 = f;\n    xaf_faf_1 = array<f32, 2>(_e156, 2f);\n    let _e159 = f;\n    xafaf_f_1 = array<f32, 2>(1f, _e159);\n    let _e162 = f;\n    xaf_fai = array<f32, 2>(_e162, 2f);\n    let _e165 = f;\n    xafai_f = array<f32, 2>(1f, _e165);\n    let _e168 = i;\n    xai_iai_1 = array<i32, 2>(_e168, 2i);\n    let _e171 = i;\n    xaiai_i_1 = array<i32, 2>(1i, _e171);\n    let _e174 = f;\n    xafp_faf = array<f32, 2>(_e174, 2f);\n    let _e177 = f;\n    xafpaf_f = array<f32, 2>(1f, _e177);\n    let _e180 = f;\n    xafp_fai = array<f32, 2>(_e180, 2f);\n    let _e183 = f;\n    xafpai_f = array<f32, 2>(1f, _e183);\n    let _e186 = i;\n    xaip_iai = array<i32, 2>(_e186, 2i);\n    let _e189 = i;\n    xaipai_i = array<i32, 2>(1i, _e189);\n    let _e192 = i;\n    xvisi = vec2(_e192);\n    let _e194 = u;\n    xvusu = vec2(_e194);\n    let _e196 = f;\n    xvfsf = vec2(_e196);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    globals();\n    all_constant_arguments();\n    mixed_constant_and_runtime_arguments();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-access.wgsl",
    "content": "struct GlobalConst {\n    a: u32,\n    b: vec3<u32>,\n    c: i32,\n}\n\nstruct AlignedWrapper {\n    value: i32,\n}\n\nstruct Bar {\n    _matrix: mat4x3<f32>,\n    matrix_array: array<mat2x2<f32>, 2>,\n    atom: atomic<i32>,\n    atom_arr: array<atomic<i32>, 10>,\n    arr: array<vec2<u32>, 2>,\n    data: array<AlignedWrapper>,\n}\n\nstruct Baz {\n    m: mat3x2<f32>,\n}\n\nstruct MatCx2InArray {\n    am: array<mat4x2<f32>, 2>,\n}\n\nstruct AssignToMember {\n    x: u32,\n}\n\nstruct S {\n    m: i32,\n}\n\nstruct Inner {\n    delicious: i32,\n}\n\nstruct Outer {\n    om_nom_nom: Inner,\n    thing: u32,\n}\n\nvar<private> msl_padding_global_const: GlobalConst = GlobalConst(0u, vec3<u32>(0u, 0u, 0u), 0i);\n@group(0) @binding(0) \nvar<storage, read_write> bar: Bar;\n@group(0) @binding(1) \nvar<uniform> baz: Baz;\n@group(0) @binding(2) \nvar<storage, read_write> qux: vec2<i32>;\n@group(0) @binding(3) \nvar<uniform> nested_mat_cx2_: MatCx2InArray;\n\nfn test_matrix_within_struct_accesses() {\n    var idx: i32 = 1i;\n    var t: Baz = Baz(mat3x2<f32>(vec2(1f), vec2(2f), vec2(3f)));\n\n    let _e3 = idx;\n    idx = (_e3 - 1i);\n    let l0_ = baz.m;\n    let l1_ = baz.m[0];\n    let _e14 = idx;\n    let l2_ = baz.m[_e14];\n    let l3_ = baz.m[0][1];\n    let _e25 = idx;\n    let l4_ = baz.m[0][_e25];\n    let _e30 = idx;\n    let l5_ = baz.m[_e30][1];\n    let _e36 = idx;\n    let _e38 = idx;\n    let l6_ = baz.m[_e36][_e38];\n    let _e51 = idx;\n    idx = (_e51 + 1i);\n    t.m = mat3x2<f32>(vec2(6f), vec2(5f), vec2(4f));\n    t.m[0] = vec2(9f);\n    let _e66 = idx;\n    t.m[_e66] = vec2(90f);\n    t.m[0][1] = 10f;\n    let _e76 = idx;\n    t.m[0][_e76] = 20f;\n    let _e80 = idx;\n    t.m[_e80][1] = 30f;\n    let _e85 = idx;\n    let _e87 = idx;\n    t.m[_e85][_e87] = 40f;\n    return;\n}\n\nfn test_matrix_within_array_within_struct_accesses() {\n    var idx_1: i32 = 1i;\n    var t_1: MatCx2InArray = MatCx2InArray(array<mat4x2<f32>, 2>());\n\n    let _e3 = idx_1;\n    idx_1 = (_e3 - 1i);\n    let l0_1 = nested_mat_cx2_.am;\n    let l1_1 = nested_mat_cx2_.am[0];\n    let l2_1 = nested_mat_cx2_.am[0][0];\n    let _e20 = idx_1;\n    let l3_1 = nested_mat_cx2_.am[0][_e20];\n    let l4_1 = nested_mat_cx2_.am[0][0][1];\n    let _e33 = idx_1;\n    let l5_1 = nested_mat_cx2_.am[0][0][_e33];\n    let _e39 = idx_1;\n    let l6_1 = nested_mat_cx2_.am[0][_e39][1];\n    let _e46 = idx_1;\n    let _e48 = idx_1;\n    let l7_ = nested_mat_cx2_.am[0][_e46][_e48];\n    let _e55 = idx_1;\n    idx_1 = (_e55 + 1i);\n    t_1.am = array<mat4x2<f32>, 2>();\n    t_1.am[0] = mat4x2<f32>(vec2(8f), vec2(7f), vec2(6f), vec2(5f));\n    t_1.am[0][0] = vec2(9f);\n    let _e77 = idx_1;\n    t_1.am[0][_e77] = vec2(90f);\n    t_1.am[0][0][1] = 10f;\n    let _e89 = idx_1;\n    t_1.am[0][0][_e89] = 20f;\n    let _e94 = idx_1;\n    t_1.am[0][_e94][1] = 30f;\n    let _e100 = idx_1;\n    let _e102 = idx_1;\n    t_1.am[0][_e100][_e102] = 40f;\n    return;\n}\n\nfn read_from_private(foo_1: ptr<function, f32>) -> f32 {\n    let _e1 = (*foo_1);\n    return _e1;\n}\n\nfn test_arr_as_arg(a: array<array<f32, 10>, 5>) -> f32 {\n    return a[4][9];\n}\n\nfn assign_through_ptr_fn(p: ptr<function, u32>) {\n    (*p) = 42u;\n    return;\n}\n\nfn assign_array_through_ptr_fn(foo_2: ptr<function, array<vec4<f32>, 2>>) {\n    (*foo_2) = array<vec4<f32>, 2>(vec4(1f), vec4(2f));\n    return;\n}\n\nfn assign_through_ptr() {\n    var val: u32 = 33u;\n    var arr: array<vec4<f32>, 2> = array<vec4<f32>, 2>(vec4(6f), vec4(7f));\n\n    assign_through_ptr_fn((&val));\n    assign_array_through_ptr_fn((&arr));\n    return;\n}\n\nfn fetch_arg_ptr_member(p_1: ptr<function, AssignToMember>) -> u32 {\n    let _e2 = (*p_1).x;\n    return _e2;\n}\n\nfn assign_to_arg_ptr_member(p_2: ptr<function, AssignToMember>) {\n    (*p_2).x = 10u;\n    return;\n}\n\nfn fetch_arg_ptr_array_element(p_3: ptr<function, array<u32, 4>>) -> u32 {\n    let _e2 = (*p_3)[1];\n    return _e2;\n}\n\nfn assign_to_arg_ptr_array_element(p_4: ptr<function, array<u32, 4>>) {\n    (*p_4)[1] = 10u;\n    return;\n}\n\nfn assign_to_ptr_components() {\n    var s1_: AssignToMember;\n    var a1_: array<u32, 4>;\n\n    assign_to_arg_ptr_member((&s1_));\n    let _e1 = fetch_arg_ptr_member((&s1_));\n    assign_to_arg_ptr_array_element((&a1_));\n    let _e3 = fetch_arg_ptr_array_element((&a1_));\n    return;\n}\n\nfn index_ptr(value: bool) -> bool {\n    var a_1: array<bool, 1>;\n\n    a_1 = array<bool, 1>(value);\n    let _e4 = a_1[0];\n    return _e4;\n}\n\nfn member_ptr() -> i32 {\n    var s: S = S(42i);\n\n    let _e4 = s.m;\n    return _e4;\n}\n\nfn let_members_of_members() -> i32 {\n    let inner_1 = Outer().om_nom_nom;\n    let delishus_1 = inner_1.delicious;\n    if (Outer().thing != u32(delishus_1)) {\n    }\n    return Outer().om_nom_nom.delicious;\n}\n\nfn var_members_of_members() -> i32 {\n    var thing: Outer = Outer();\n    var inner: Inner;\n    var delishus: i32;\n\n    let _e3 = thing.om_nom_nom;\n    inner = _e3;\n    let _e6 = inner.delicious;\n    delishus = _e6;\n    let _e9 = thing.thing;\n    let _e10 = delishus;\n    if (_e9 != u32(_e10)) {\n    }\n    let _e15 = thing.om_nom_nom.delicious;\n    return _e15;\n}\n\n@vertex \nfn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {\n    var foo: f32 = 0f;\n    var c2_: array<i32, 5>;\n\n    let baz_1 = foo;\n    foo = 1f;\n    let phony = msl_padding_global_const;\n    test_matrix_within_struct_accesses();\n    test_matrix_within_array_within_struct_accesses();\n    let _matrix = bar._matrix;\n    let arr_1 = bar.arr;\n    let b = bar._matrix[3u][0];\n    let a_2 = bar.data[(arrayLength((&bar.data)) - 2u)].value;\n    let c = qux;\n    let data_pointer = (&bar.data[0].value);\n    let _e35 = read_from_private((&foo));\n    c2_ = array<i32, 5>(a_2, i32(b), 3i, 4i, 5i);\n    c2_[(vi + 1u)] = 42i;\n    let value_1 = c2_[vi];\n    let _e49 = test_arr_as_arg(array<array<f32, 10>, 5>());\n    return vec4<f32>((_matrix * vec4<f32>(vec4(value_1))), 2f);\n}\n\n@fragment \nfn foo_frag() -> @location(0) vec4<f32> {\n    bar._matrix[1][2] = 1f;\n    bar._matrix = mat4x3<f32>(vec3(0f), vec3(1f), vec3(2f), vec3(3f));\n    bar.arr = array<vec2<u32>, 2>(vec2(0u), vec2(1u));\n    bar.data[1].value = 1i;\n    qux = vec2<i32>();\n    return vec4(0f);\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn foo_compute() {\n    assign_through_ptr();\n    assign_to_ptr_components();\n    let _e1 = index_ptr(true);\n    let _e2 = member_ptr();\n    let _e3 = let_members_of_members();\n    let _e4 = var_members_of_members();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-array-in-ctor.wgsl",
    "content": "struct Ah {\n    inner: array<f32, 2>,\n}\n\n@group(0) @binding(0) \nvar<storage> ah: Ah;\n\n@compute @workgroup_size(1, 1, 1) \nfn cs_main() {\n    let ah_1 = ah;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-array-in-function-return-type.wgsl",
    "content": "fn ret_array() -> array<f32, 2> {\n    return array<f32, 2>(1f, 2f);\n}\n\nfn ret_array_array() -> array<array<f32, 2>, 3> {\n    let _e0 = ret_array();\n    let _e1 = ret_array();\n    let _e2 = ret_array();\n    return array<array<f32, 2>, 3>(_e0, _e1, _e2);\n}\n\n@fragment \nfn main() -> @location(0) vec4<f32> {\n    let _e0 = ret_array_array();\n    return vec4<f32>(_e0[0][0], _e0[0][1], 0f, 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicCompareExchange-int64.wgsl",
    "content": "const SIZE: u32 = 128u;\n\n@group(0) @binding(0) \nvar<storage, read_write> arr_i64_: array<atomic<i64>, 128>;\n@group(0) @binding(1) \nvar<storage, read_write> arr_u64_: array<atomic<u64>, 128>;\n\n@compute @workgroup_size(1, 1, 1) \nfn test_atomic_compare_exchange_i64_() {\n    var i: u32 = 0u;\n    var old: i64;\n    var exchanged: bool;\n\n    loop {\n        let _e2 = i;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            let _e6 = i;\n            let _e8 = atomicLoad((&arr_i64_[_e6]));\n            old = _e8;\n            exchanged = false;\n            loop {\n                let _e12 = exchanged;\n                if !(_e12) {\n                } else {\n                    break;\n                }\n                {\n                    let _e14 = old;\n                    let new_ = bitcast<i64>((_e14 + 10li));\n                    let _e19 = i;\n                    let _e21 = old;\n                    let _e22 = atomicCompareExchangeWeak((&arr_i64_[_e19]), _e21, new_);\n                    old = _e22.old_value;\n                    exchanged = _e22.exchanged;\n                }\n            }\n        }\n        continuing {\n            let _e26 = i;\n            i = (_e26 + 1u);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn test_atomic_compare_exchange_u64_() {\n    var i_1: u32 = 0u;\n    var old_1: u64;\n    var exchanged_1: bool;\n\n    loop {\n        let _e2 = i_1;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            let _e6 = i_1;\n            let _e8 = atomicLoad((&arr_u64_[_e6]));\n            old_1 = _e8;\n            exchanged_1 = false;\n            loop {\n                let _e12 = exchanged_1;\n                if !(_e12) {\n                } else {\n                    break;\n                }\n                {\n                    let _e14 = old_1;\n                    let new_1 = bitcast<u64>((_e14 + 10lu));\n                    let _e19 = i_1;\n                    let _e21 = old_1;\n                    let _e22 = atomicCompareExchangeWeak((&arr_u64_[_e19]), _e21, new_1);\n                    old_1 = _e22.old_value;\n                    exchanged_1 = _e22.exchanged;\n                }\n            }\n        }\n        continuing {\n            let _e26 = i_1;\n            i_1 = (_e26 + 1u);\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicCompareExchange.wgsl",
    "content": "const SIZE: u32 = 128u;\n\n@group(0) @binding(0) \nvar<storage, read_write> arr_i32_: array<atomic<i32>, 128>;\n@group(0) @binding(1) \nvar<storage, read_write> arr_u32_: array<atomic<u32>, 128>;\n\n@compute @workgroup_size(1, 1, 1) \nfn test_atomic_compare_exchange_i32_() {\n    var i: u32 = 0u;\n    var old: i32;\n    var exchanged: bool;\n\n    loop {\n        let _e2 = i;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            let _e6 = i;\n            let _e8 = atomicLoad((&arr_i32_[_e6]));\n            old = _e8;\n            exchanged = false;\n            loop {\n                let _e12 = exchanged;\n                if !(_e12) {\n                } else {\n                    break;\n                }\n                {\n                    let _e14 = old;\n                    let new_ = bitcast<i32>((bitcast<f32>(_e14) + 1f));\n                    let _e20 = i;\n                    let _e22 = old;\n                    let _e23 = atomicCompareExchangeWeak((&arr_i32_[_e20]), _e22, new_);\n                    old = _e23.old_value;\n                    exchanged = _e23.exchanged;\n                }\n            }\n        }\n        continuing {\n            let _e27 = i;\n            i = (_e27 + 1u);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn test_atomic_compare_exchange_u32_() {\n    var i_1: u32 = 0u;\n    var old_1: u32;\n    var exchanged_1: bool;\n\n    loop {\n        let _e2 = i_1;\n        if (_e2 < SIZE) {\n        } else {\n            break;\n        }\n        {\n            let _e6 = i_1;\n            let _e8 = atomicLoad((&arr_u32_[_e6]));\n            old_1 = _e8;\n            exchanged_1 = false;\n            loop {\n                let _e12 = exchanged_1;\n                if !(_e12) {\n                } else {\n                    break;\n                }\n                {\n                    let _e14 = old_1;\n                    let new_1 = bitcast<u32>((bitcast<f32>(_e14) + 1f));\n                    let _e20 = i_1;\n                    let _e22 = old_1;\n                    let _e23 = atomicCompareExchangeWeak((&arr_u32_[_e20]), _e22, new_1);\n                    old_1 = _e23.old_value;\n                    exchanged_1 = _e23.exchanged;\n                }\n            }\n        }\n        continuing {\n            let _e27 = i_1;\n            i_1 = (_e27 + 1u);\n        }\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicOps-float32.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<f32>,\n    atomic_arr: array<atomic<f32>, 2>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> storage_atomic_scalar: atomic<f32>;\n@group(0) @binding(1) \nvar<storage, read_write> storage_atomic_arr: array<atomic<f32>, 2>;\n@group(0) @binding(2) \nvar<storage, read_write> storage_struct: Struct;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore((&storage_atomic_scalar), 1.5f);\n    atomicStore((&storage_atomic_arr[1]), 1.5f);\n    atomicStore((&storage_struct.atomic_scalar), 1.5f);\n    atomicStore((&storage_struct.atomic_arr[1]), 1.5f);\n    workgroupBarrier();\n    let l0_ = atomicLoad((&storage_atomic_scalar));\n    let l1_ = atomicLoad((&storage_atomic_arr[1]));\n    let l2_ = atomicLoad((&storage_struct.atomic_scalar));\n    let l3_ = atomicLoad((&storage_struct.atomic_arr[1]));\n    workgroupBarrier();\n    let _e27 = atomicAdd((&storage_atomic_scalar), 1.5f);\n    let _e31 = atomicAdd((&storage_atomic_arr[1]), 1.5f);\n    let _e35 = atomicAdd((&storage_struct.atomic_scalar), 1.5f);\n    let _e40 = atomicAdd((&storage_struct.atomic_arr[1]), 1.5f);\n    workgroupBarrier();\n    let _e43 = atomicExchange((&storage_atomic_scalar), 1.5f);\n    let _e47 = atomicExchange((&storage_atomic_arr[1]), 1.5f);\n    let _e51 = atomicExchange((&storage_struct.atomic_scalar), 1.5f);\n    let _e56 = atomicExchange((&storage_struct.atomic_arr[1]), 1.5f);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicOps-int64-min-max.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<u64>,\n    atomic_arr: array<atomic<u64>, 2>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> storage_atomic_scalar: atomic<u64>;\n@group(0) @binding(1) \nvar<storage, read_write> storage_atomic_arr: array<atomic<u64>, 2>;\n@group(0) @binding(2) \nvar<storage, read_write> storage_struct: Struct;\n@group(0) @binding(3) \nvar<uniform> input: u64;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    let _e3 = input;\n    atomicMax((&storage_atomic_scalar), _e3);\n    let _e7 = input;\n    atomicMax((&storage_atomic_arr[1]), (1lu + _e7));\n    atomicMax((&storage_struct.atomic_scalar), 1lu);\n    atomicMax((&storage_struct.atomic_arr[1]), u64(id.x));\n    workgroupBarrier();\n    let _e20 = input;\n    atomicMin((&storage_atomic_scalar), _e20);\n    let _e24 = input;\n    atomicMin((&storage_atomic_arr[1]), (1lu + _e24));\n    atomicMin((&storage_struct.atomic_scalar), 1lu);\n    atomicMin((&storage_struct.atomic_arr[1]), u64(id.x));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicOps-int64.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<u64>,\n    atomic_arr: array<atomic<i64>, 2>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> storage_atomic_scalar: atomic<u64>;\n@group(0) @binding(1) \nvar<storage, read_write> storage_atomic_arr: array<atomic<i64>, 2>;\n@group(0) @binding(2) \nvar<storage, read_write> storage_struct: Struct;\nvar<workgroup> workgroup_atomic_scalar: atomic<u64>;\nvar<workgroup> workgroup_atomic_arr: array<atomic<i64>, 2>;\nvar<workgroup> workgroup_struct: Struct;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore((&storage_atomic_scalar), 1lu);\n    atomicStore((&storage_atomic_arr[1]), 1li);\n    atomicStore((&storage_struct.atomic_scalar), 1lu);\n    atomicStore((&storage_struct.atomic_arr[1]), 1li);\n    atomicStore((&workgroup_atomic_scalar), 1lu);\n    atomicStore((&workgroup_atomic_arr[1]), 1li);\n    atomicStore((&workgroup_struct.atomic_scalar), 1lu);\n    atomicStore((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    let l0_ = atomicLoad((&storage_atomic_scalar));\n    let l1_ = atomicLoad((&storage_atomic_arr[1]));\n    let l2_ = atomicLoad((&storage_struct.atomic_scalar));\n    let l3_ = atomicLoad((&storage_struct.atomic_arr[1]));\n    let l4_ = atomicLoad((&workgroup_atomic_scalar));\n    let l5_ = atomicLoad((&workgroup_atomic_arr[1]));\n    let l6_ = atomicLoad((&workgroup_struct.atomic_scalar));\n    let l7_ = atomicLoad((&workgroup_struct.atomic_arr[1]));\n    workgroupBarrier();\n    let _e51 = atomicAdd((&storage_atomic_scalar), 1lu);\n    let _e55 = atomicAdd((&storage_atomic_arr[1]), 1li);\n    let _e59 = atomicAdd((&storage_struct.atomic_scalar), 1lu);\n    let _e64 = atomicAdd((&storage_struct.atomic_arr[1]), 1li);\n    let _e67 = atomicAdd((&workgroup_atomic_scalar), 1lu);\n    let _e71 = atomicAdd((&workgroup_atomic_arr[1]), 1li);\n    let _e75 = atomicAdd((&workgroup_struct.atomic_scalar), 1lu);\n    let _e80 = atomicAdd((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    let _e83 = atomicSub((&storage_atomic_scalar), 1lu);\n    let _e87 = atomicSub((&storage_atomic_arr[1]), 1li);\n    let _e91 = atomicSub((&storage_struct.atomic_scalar), 1lu);\n    let _e96 = atomicSub((&storage_struct.atomic_arr[1]), 1li);\n    let _e99 = atomicSub((&workgroup_atomic_scalar), 1lu);\n    let _e103 = atomicSub((&workgroup_atomic_arr[1]), 1li);\n    let _e107 = atomicSub((&workgroup_struct.atomic_scalar), 1lu);\n    let _e112 = atomicSub((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    atomicMax((&storage_atomic_scalar), 1lu);\n    atomicMax((&storage_atomic_arr[1]), 1li);\n    atomicMax((&storage_struct.atomic_scalar), 1lu);\n    atomicMax((&storage_struct.atomic_arr[1]), 1li);\n    atomicMax((&workgroup_atomic_scalar), 1lu);\n    atomicMax((&workgroup_atomic_arr[1]), 1li);\n    atomicMax((&workgroup_struct.atomic_scalar), 1lu);\n    atomicMax((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    atomicMin((&storage_atomic_scalar), 1lu);\n    atomicMin((&storage_atomic_arr[1]), 1li);\n    atomicMin((&storage_struct.atomic_scalar), 1lu);\n    atomicMin((&storage_struct.atomic_arr[1]), 1li);\n    atomicMin((&workgroup_atomic_scalar), 1lu);\n    atomicMin((&workgroup_atomic_arr[1]), 1li);\n    atomicMin((&workgroup_struct.atomic_scalar), 1lu);\n    atomicMin((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    let _e163 = atomicAnd((&storage_atomic_scalar), 1lu);\n    let _e167 = atomicAnd((&storage_atomic_arr[1]), 1li);\n    let _e171 = atomicAnd((&storage_struct.atomic_scalar), 1lu);\n    let _e176 = atomicAnd((&storage_struct.atomic_arr[1]), 1li);\n    let _e179 = atomicAnd((&workgroup_atomic_scalar), 1lu);\n    let _e183 = atomicAnd((&workgroup_atomic_arr[1]), 1li);\n    let _e187 = atomicAnd((&workgroup_struct.atomic_scalar), 1lu);\n    let _e192 = atomicAnd((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    let _e195 = atomicOr((&storage_atomic_scalar), 1lu);\n    let _e199 = atomicOr((&storage_atomic_arr[1]), 1li);\n    let _e203 = atomicOr((&storage_struct.atomic_scalar), 1lu);\n    let _e208 = atomicOr((&storage_struct.atomic_arr[1]), 1li);\n    let _e211 = atomicOr((&workgroup_atomic_scalar), 1lu);\n    let _e215 = atomicOr((&workgroup_atomic_arr[1]), 1li);\n    let _e219 = atomicOr((&workgroup_struct.atomic_scalar), 1lu);\n    let _e224 = atomicOr((&workgroup_struct.atomic_arr[1]), 1li);\n    workgroupBarrier();\n    let _e227 = atomicXor((&storage_atomic_scalar), 1lu);\n    let _e231 = atomicXor((&storage_atomic_arr[1]), 1li);\n    let _e235 = atomicXor((&storage_struct.atomic_scalar), 1lu);\n    let _e240 = atomicXor((&storage_struct.atomic_arr[1]), 1li);\n    let _e243 = atomicXor((&workgroup_atomic_scalar), 1lu);\n    let _e247 = atomicXor((&workgroup_atomic_arr[1]), 1li);\n    let _e251 = atomicXor((&workgroup_struct.atomic_scalar), 1lu);\n    let _e256 = atomicXor((&workgroup_struct.atomic_arr[1]), 1li);\n    let _e259 = atomicExchange((&storage_atomic_scalar), 1lu);\n    let _e263 = atomicExchange((&storage_atomic_arr[1]), 1li);\n    let _e267 = atomicExchange((&storage_struct.atomic_scalar), 1lu);\n    let _e272 = atomicExchange((&storage_struct.atomic_arr[1]), 1li);\n    let _e275 = atomicExchange((&workgroup_atomic_scalar), 1lu);\n    let _e279 = atomicExchange((&workgroup_atomic_arr[1]), 1li);\n    let _e283 = atomicExchange((&workgroup_struct.atomic_scalar), 1lu);\n    let _e288 = atomicExchange((&workgroup_struct.atomic_arr[1]), 1li);\n    let _e292 = atomicCompareExchangeWeak((&storage_atomic_scalar), 1lu, 2lu);\n    let _e297 = atomicCompareExchangeWeak((&storage_atomic_arr[1]), 1li, 2li);\n    let _e302 = atomicCompareExchangeWeak((&storage_struct.atomic_scalar), 1lu, 2lu);\n    let _e308 = atomicCompareExchangeWeak((&storage_struct.atomic_arr[1]), 1li, 2li);\n    let _e312 = atomicCompareExchangeWeak((&workgroup_atomic_scalar), 1lu, 2lu);\n    let _e317 = atomicCompareExchangeWeak((&workgroup_atomic_arr[1]), 1li, 2li);\n    let _e322 = atomicCompareExchangeWeak((&workgroup_struct.atomic_scalar), 1lu, 2lu);\n    let _e328 = atomicCompareExchangeWeak((&workgroup_struct.atomic_arr[1]), 1li, 2li);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicOps.wgsl",
    "content": "struct Struct {\n    atomic_scalar: atomic<u32>,\n    atomic_arr: array<atomic<i32>, 2>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> storage_atomic_scalar: atomic<u32>;\n@group(0) @binding(1) \nvar<storage, read_write> storage_atomic_arr: array<atomic<i32>, 2>;\n@group(0) @binding(2) \nvar<storage, read_write> storage_struct: Struct;\nvar<workgroup> workgroup_atomic_scalar: atomic<u32>;\nvar<workgroup> workgroup_atomic_arr: array<atomic<i32>, 2>;\nvar<workgroup> workgroup_struct: Struct;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    atomicStore((&storage_atomic_scalar), 1u);\n    atomicStore((&storage_atomic_arr[1]), 1i);\n    atomicStore((&storage_struct.atomic_scalar), 1u);\n    atomicStore((&storage_struct.atomic_arr[1]), 1i);\n    atomicStore((&workgroup_atomic_scalar), 1u);\n    atomicStore((&workgroup_atomic_arr[1]), 1i);\n    atomicStore((&workgroup_struct.atomic_scalar), 1u);\n    atomicStore((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let l0_ = atomicLoad((&storage_atomic_scalar));\n    let l1_ = atomicLoad((&storage_atomic_arr[1]));\n    let l2_ = atomicLoad((&storage_struct.atomic_scalar));\n    let l3_ = atomicLoad((&storage_struct.atomic_arr[1]));\n    let l4_ = atomicLoad((&workgroup_atomic_scalar));\n    let l5_ = atomicLoad((&workgroup_atomic_arr[1]));\n    let l6_ = atomicLoad((&workgroup_struct.atomic_scalar));\n    let l7_ = atomicLoad((&workgroup_struct.atomic_arr[1]));\n    workgroupBarrier();\n    let _e51 = atomicAdd((&storage_atomic_scalar), 1u);\n    let _e55 = atomicAdd((&storage_atomic_arr[1]), 1i);\n    let _e59 = atomicAdd((&storage_struct.atomic_scalar), 1u);\n    let _e64 = atomicAdd((&storage_struct.atomic_arr[1]), 1i);\n    let _e67 = atomicAdd((&workgroup_atomic_scalar), 1u);\n    let _e71 = atomicAdd((&workgroup_atomic_arr[1]), 1i);\n    let _e75 = atomicAdd((&workgroup_struct.atomic_scalar), 1u);\n    let _e80 = atomicAdd((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e83 = atomicSub((&storage_atomic_scalar), 1u);\n    let _e87 = atomicSub((&storage_atomic_arr[1]), 1i);\n    let _e91 = atomicSub((&storage_struct.atomic_scalar), 1u);\n    let _e96 = atomicSub((&storage_struct.atomic_arr[1]), 1i);\n    let _e99 = atomicSub((&workgroup_atomic_scalar), 1u);\n    let _e103 = atomicSub((&workgroup_atomic_arr[1]), 1i);\n    let _e107 = atomicSub((&workgroup_struct.atomic_scalar), 1u);\n    let _e112 = atomicSub((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e115 = atomicMax((&storage_atomic_scalar), 1u);\n    let _e119 = atomicMax((&storage_atomic_arr[1]), 1i);\n    let _e123 = atomicMax((&storage_struct.atomic_scalar), 1u);\n    let _e128 = atomicMax((&storage_struct.atomic_arr[1]), 1i);\n    let _e131 = atomicMax((&workgroup_atomic_scalar), 1u);\n    let _e135 = atomicMax((&workgroup_atomic_arr[1]), 1i);\n    let _e139 = atomicMax((&workgroup_struct.atomic_scalar), 1u);\n    let _e144 = atomicMax((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e147 = atomicMin((&storage_atomic_scalar), 1u);\n    let _e151 = atomicMin((&storage_atomic_arr[1]), 1i);\n    let _e155 = atomicMin((&storage_struct.atomic_scalar), 1u);\n    let _e160 = atomicMin((&storage_struct.atomic_arr[1]), 1i);\n    let _e163 = atomicMin((&workgroup_atomic_scalar), 1u);\n    let _e167 = atomicMin((&workgroup_atomic_arr[1]), 1i);\n    let _e171 = atomicMin((&workgroup_struct.atomic_scalar), 1u);\n    let _e176 = atomicMin((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e179 = atomicAnd((&storage_atomic_scalar), 1u);\n    let _e183 = atomicAnd((&storage_atomic_arr[1]), 1i);\n    let _e187 = atomicAnd((&storage_struct.atomic_scalar), 1u);\n    let _e192 = atomicAnd((&storage_struct.atomic_arr[1]), 1i);\n    let _e195 = atomicAnd((&workgroup_atomic_scalar), 1u);\n    let _e199 = atomicAnd((&workgroup_atomic_arr[1]), 1i);\n    let _e203 = atomicAnd((&workgroup_struct.atomic_scalar), 1u);\n    let _e208 = atomicAnd((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e211 = atomicOr((&storage_atomic_scalar), 1u);\n    let _e215 = atomicOr((&storage_atomic_arr[1]), 1i);\n    let _e219 = atomicOr((&storage_struct.atomic_scalar), 1u);\n    let _e224 = atomicOr((&storage_struct.atomic_arr[1]), 1i);\n    let _e227 = atomicOr((&workgroup_atomic_scalar), 1u);\n    let _e231 = atomicOr((&workgroup_atomic_arr[1]), 1i);\n    let _e235 = atomicOr((&workgroup_struct.atomic_scalar), 1u);\n    let _e240 = atomicOr((&workgroup_struct.atomic_arr[1]), 1i);\n    workgroupBarrier();\n    let _e243 = atomicXor((&storage_atomic_scalar), 1u);\n    let _e247 = atomicXor((&storage_atomic_arr[1]), 1i);\n    let _e251 = atomicXor((&storage_struct.atomic_scalar), 1u);\n    let _e256 = atomicXor((&storage_struct.atomic_arr[1]), 1i);\n    let _e259 = atomicXor((&workgroup_atomic_scalar), 1u);\n    let _e263 = atomicXor((&workgroup_atomic_arr[1]), 1i);\n    let _e267 = atomicXor((&workgroup_struct.atomic_scalar), 1u);\n    let _e272 = atomicXor((&workgroup_struct.atomic_arr[1]), 1i);\n    let _e275 = atomicExchange((&storage_atomic_scalar), 1u);\n    let _e279 = atomicExchange((&storage_atomic_arr[1]), 1i);\n    let _e283 = atomicExchange((&storage_struct.atomic_scalar), 1u);\n    let _e288 = atomicExchange((&storage_struct.atomic_arr[1]), 1i);\n    let _e291 = atomicExchange((&workgroup_atomic_scalar), 1u);\n    let _e295 = atomicExchange((&workgroup_atomic_arr[1]), 1i);\n    let _e299 = atomicExchange((&workgroup_struct.atomic_scalar), 1u);\n    let _e304 = atomicExchange((&workgroup_struct.atomic_arr[1]), 1i);\n    let _e308 = atomicCompareExchangeWeak((&storage_atomic_scalar), 1u, 2u);\n    let _e313 = atomicCompareExchangeWeak((&storage_atomic_arr[1]), 1i, 2i);\n    let _e318 = atomicCompareExchangeWeak((&storage_struct.atomic_scalar), 1u, 2u);\n    let _e324 = atomicCompareExchangeWeak((&storage_struct.atomic_arr[1]), 1i, 2i);\n    let _e328 = atomicCompareExchangeWeak((&workgroup_atomic_scalar), 1u, 2u);\n    let _e333 = atomicCompareExchangeWeak((&workgroup_atomic_arr[1]), 1i, 2i);\n    let _e338 = atomicCompareExchangeWeak((&workgroup_struct.atomic_scalar), 1u, 2u);\n    let _e344 = atomicCompareExchangeWeak((&workgroup_struct.atomic_arr[1]), 1i, 2i);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicTexture-int64.wgsl",
    "content": "@group(0) @binding(0) \nvar image: texture_storage_2d<r64uint,atomic>;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    textureAtomicMax(image, vec2<i32>(0i, 0i), 1lu);\n    workgroupBarrier();\n    textureAtomicMin(image, vec2<i32>(0i, 0i), 1lu);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-atomicTexture.wgsl",
    "content": "@group(0) @binding(0) \nvar image_u: texture_storage_2d<r32uint,atomic>;\n@group(0) @binding(1) \nvar image_s: texture_storage_2d<r32sint,atomic>;\n\n@compute @workgroup_size(2, 1, 1) \nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>) {\n    textureAtomicMax(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicMin(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicAdd(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicAnd(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicOr(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicXor(image_u, vec2<i32>(0i, 0i), 1u);\n    textureAtomicMax(image_s, vec2<i32>(0i, 0i), 1i);\n    textureAtomicMin(image_s, vec2<i32>(0i, 0i), 1i);\n    textureAtomicAdd(image_s, vec2<i32>(0i, 0i), 1i);\n    textureAtomicAnd(image_s, vec2<i32>(0i, 0i), 1i);\n    textureAtomicOr(image_s, vec2<i32>(0i, 0i), 1i);\n    textureAtomicXor(image_s, vec2<i32>(0i, 0i), 1i);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-barycentrics.wgsl",
    "content": "@fragment \nfn fs_main(@builtin(barycentric) bary: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4<f32>(bary, 1f);\n}\n\n@fragment \nfn fs_main_no_perspective(@builtin(barycentric_no_perspective) bary_1: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4<f32>(bary_1, 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-binding-arrays.wgsl",
    "content": "struct UniformIndex {\n    index: u32,\n}\n\nstruct FragmentIn {\n    @location(0) @interpolate(flat) index: u32,\n}\n\n@group(0) @binding(0) \nvar texture_array_unbounded: binding_array<texture_2d<f32>>;\n@group(0) @binding(1) \nvar texture_array_bounded: binding_array<texture_2d<f32>, 5>;\n@group(0) @binding(2) \nvar texture_array_2darray: binding_array<texture_2d_array<f32>, 5>;\n@group(0) @binding(3) \nvar texture_array_multisampled: binding_array<texture_multisampled_2d<f32>, 5>;\n@group(0) @binding(4) \nvar texture_array_depth: binding_array<texture_depth_2d, 5>;\n@group(0) @binding(5) \nvar texture_array_storage: binding_array<texture_storage_2d<rgba32float,write>, 5>;\n@group(0) @binding(6) \nvar samp: binding_array<sampler, 5>;\n@group(0) @binding(7) \nvar samp_comp: binding_array<sampler_comparison, 5>;\n@group(0) @binding(8) \nvar<uniform> uni: UniformIndex;\n\n@fragment \nfn main(fragment_in: FragmentIn) -> @location(0) vec4<f32> {\n    var u1_: u32 = 0u;\n    var u2_: vec2<u32> = vec2(0u);\n    var v1_: f32 = 0f;\n    var v4_: vec4<f32> = vec4(0f);\n\n    let uniform_index = uni.index;\n    let non_uniform_index = fragment_in.index;\n    let uv = vec2(0f);\n    let pix = vec2(0i);\n    let _e19 = u2_;\n    let _e22 = textureDimensions(texture_array_unbounded[0]);\n    u2_ = (_e19 + _e22);\n    let _e24 = u2_;\n    let _e27 = textureDimensions(texture_array_unbounded[uniform_index]);\n    u2_ = (_e24 + _e27);\n    let _e29 = u2_;\n    let _e32 = textureDimensions(texture_array_unbounded[non_uniform_index]);\n    u2_ = (_e29 + _e32);\n    let _e34 = v4_;\n    let _e39 = textureGather(0, texture_array_bounded[0], samp[0], uv);\n    v4_ = (_e34 + _e39);\n    let _e41 = v4_;\n    let _e46 = textureGather(0, texture_array_bounded[uniform_index], samp[uniform_index], uv);\n    v4_ = (_e41 + _e46);\n    let _e48 = v4_;\n    let _e53 = textureGather(0, texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv);\n    v4_ = (_e48 + _e53);\n    let _e55 = v4_;\n    let _e61 = textureGatherCompare(texture_array_depth[0], samp_comp[0], uv, 0f);\n    v4_ = (_e55 + _e61);\n    let _e63 = v4_;\n    let _e69 = textureGatherCompare(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0f);\n    v4_ = (_e63 + _e69);\n    let _e71 = v4_;\n    let _e77 = textureGatherCompare(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0f);\n    v4_ = (_e71 + _e77);\n    let _e79 = v4_;\n    let _e83 = textureLoad(texture_array_unbounded[0], pix, 0i);\n    v4_ = (_e79 + _e83);\n    let _e85 = v4_;\n    let _e89 = textureLoad(texture_array_unbounded[uniform_index], pix, 0i);\n    v4_ = (_e85 + _e89);\n    let _e91 = v4_;\n    let _e95 = textureLoad(texture_array_unbounded[non_uniform_index], pix, 0i);\n    v4_ = (_e91 + _e95);\n    let _e97 = u1_;\n    let _e100 = textureNumLayers(texture_array_2darray[0]);\n    u1_ = (_e97 + _e100);\n    let _e102 = u1_;\n    let _e105 = textureNumLayers(texture_array_2darray[uniform_index]);\n    u1_ = (_e102 + _e105);\n    let _e107 = u1_;\n    let _e110 = textureNumLayers(texture_array_2darray[non_uniform_index]);\n    u1_ = (_e107 + _e110);\n    let _e112 = u1_;\n    let _e115 = textureNumLevels(texture_array_bounded[0]);\n    u1_ = (_e112 + _e115);\n    let _e117 = u1_;\n    let _e120 = textureNumLevels(texture_array_bounded[uniform_index]);\n    u1_ = (_e117 + _e120);\n    let _e122 = u1_;\n    let _e125 = textureNumLevels(texture_array_bounded[non_uniform_index]);\n    u1_ = (_e122 + _e125);\n    let _e127 = u1_;\n    let _e130 = textureNumSamples(texture_array_multisampled[0]);\n    u1_ = (_e127 + _e130);\n    let _e132 = u1_;\n    let _e135 = textureNumSamples(texture_array_multisampled[uniform_index]);\n    u1_ = (_e132 + _e135);\n    let _e137 = u1_;\n    let _e140 = textureNumSamples(texture_array_multisampled[non_uniform_index]);\n    u1_ = (_e137 + _e140);\n    let _e142 = v4_;\n    let _e147 = textureSample(texture_array_bounded[0], samp[0], uv);\n    v4_ = (_e142 + _e147);\n    let _e149 = v4_;\n    let _e154 = textureSample(texture_array_bounded[uniform_index], samp[uniform_index], uv);\n    v4_ = (_e149 + _e154);\n    let _e156 = v4_;\n    let _e161 = textureSample(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv);\n    v4_ = (_e156 + _e161);\n    let _e163 = v4_;\n    let _e169 = textureSampleBias(texture_array_bounded[0], samp[0], uv, 0f);\n    v4_ = (_e163 + _e169);\n    let _e171 = v4_;\n    let _e177 = textureSampleBias(texture_array_bounded[uniform_index], samp[uniform_index], uv, 0f);\n    v4_ = (_e171 + _e177);\n    let _e179 = v4_;\n    let _e185 = textureSampleBias(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, 0f);\n    v4_ = (_e179 + _e185);\n    let _e187 = v1_;\n    let _e193 = textureSampleCompare(texture_array_depth[0], samp_comp[0], uv, 0f);\n    v1_ = (_e187 + _e193);\n    let _e195 = v1_;\n    let _e201 = textureSampleCompare(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0f);\n    v1_ = (_e195 + _e201);\n    let _e203 = v1_;\n    let _e209 = textureSampleCompare(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0f);\n    v1_ = (_e203 + _e209);\n    let _e211 = v1_;\n    let _e217 = textureSampleCompareLevel(texture_array_depth[0], samp_comp[0], uv, 0f);\n    v1_ = (_e211 + _e217);\n    let _e219 = v1_;\n    let _e225 = textureSampleCompareLevel(texture_array_depth[uniform_index], samp_comp[uniform_index], uv, 0f);\n    v1_ = (_e219 + _e225);\n    let _e227 = v1_;\n    let _e233 = textureSampleCompareLevel(texture_array_depth[non_uniform_index], samp_comp[non_uniform_index], uv, 0f);\n    v1_ = (_e227 + _e233);\n    let _e235 = v4_;\n    let _e240 = textureSampleGrad(texture_array_bounded[0], samp[0], uv, uv, uv);\n    v4_ = (_e235 + _e240);\n    let _e242 = v4_;\n    let _e247 = textureSampleGrad(texture_array_bounded[uniform_index], samp[uniform_index], uv, uv, uv);\n    v4_ = (_e242 + _e247);\n    let _e249 = v4_;\n    let _e254 = textureSampleGrad(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, uv, uv);\n    v4_ = (_e249 + _e254);\n    let _e256 = v4_;\n    let _e262 = textureSampleLevel(texture_array_bounded[0], samp[0], uv, 0f);\n    v4_ = (_e256 + _e262);\n    let _e264 = v4_;\n    let _e270 = textureSampleLevel(texture_array_bounded[uniform_index], samp[uniform_index], uv, 0f);\n    v4_ = (_e264 + _e270);\n    let _e272 = v4_;\n    let _e278 = textureSampleLevel(texture_array_bounded[non_uniform_index], samp[non_uniform_index], uv, 0f);\n    v4_ = (_e272 + _e278);\n    let _e282 = v4_;\n    textureStore(texture_array_storage[0], pix, _e282);\n    let _e285 = v4_;\n    textureStore(texture_array_storage[uniform_index], pix, _e285);\n    let _e288 = v4_;\n    textureStore(texture_array_storage[non_uniform_index], pix, _e288);\n    let _e289 = u2_;\n    let _e290 = u1_;\n    let v2_ = vec2<f32>((_e289 + vec2(_e290)));\n    let _e294 = v4_;\n    let _e301 = v1_;\n    return ((_e294 + vec4<f32>(v2_.x, v2_.y, v2_.x, v2_.y)) + vec4(_e301));\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-binding-buffer-arrays.wgsl",
    "content": "struct UniformIndex {\n    index: u32,\n}\n\nstruct Foo {\n    x: u32,\n    far: array<i32>,\n}\n\nstruct FragmentIn {\n    @location(0) @interpolate(flat) index: u32,\n}\n\n@group(0) @binding(0) \nvar<storage> storage_array: binding_array<Foo, 1>;\n@group(0) @binding(10) \nvar<uniform> uni: UniformIndex;\n\n@fragment \nfn main(fragment_in: FragmentIn) -> @location(0) @interpolate(flat) u32 {\n    var u1_: u32 = 0u;\n\n    let uniform_index = uni.index;\n    let non_uniform_index = fragment_in.index;\n    let _e7 = u1_;\n    let _e11 = storage_array[0].x;\n    u1_ = (_e7 + _e11);\n    let _e13 = u1_;\n    let _e17 = storage_array[uniform_index].x;\n    u1_ = (_e13 + _e17);\n    let _e19 = u1_;\n    let _e23 = storage_array[non_uniform_index].x;\n    u1_ = (_e19 + _e23);\n    let _e25 = u1_;\n    u1_ = (_e25 + arrayLength((&storage_array[0].far)));\n    let _e31 = u1_;\n    u1_ = (_e31 + arrayLength((&storage_array[uniform_index].far)));\n    let _e37 = u1_;\n    u1_ = (_e37 + arrayLength((&storage_array[non_uniform_index].far)));\n    let _e43 = u1_;\n    return _e43;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-bitcast.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var i2_: vec2<i32> = vec2(0i);\n    var i3_: vec3<i32> = vec3(0i);\n    var i4_: vec4<i32> = vec4(0i);\n    var u2_: vec2<u32> = vec2(0u);\n    var u3_: vec3<u32> = vec3(0u);\n    var u4_: vec4<u32> = vec4(0u);\n    var f2_: vec2<f32> = vec2(0f);\n    var f3_: vec3<f32> = vec3(0f);\n    var f4_: vec4<f32> = vec4(0f);\n\n    let _e27 = i2_;\n    u2_ = bitcast<vec2<u32>>(_e27);\n    let _e29 = i3_;\n    u3_ = bitcast<vec3<u32>>(_e29);\n    let _e31 = i4_;\n    u4_ = bitcast<vec4<u32>>(_e31);\n    let _e33 = u2_;\n    i2_ = bitcast<vec2<i32>>(_e33);\n    let _e35 = u3_;\n    i3_ = bitcast<vec3<i32>>(_e35);\n    let _e37 = u4_;\n    i4_ = bitcast<vec4<i32>>(_e37);\n    let _e39 = i2_;\n    f2_ = bitcast<vec2<f32>>(_e39);\n    let _e41 = i3_;\n    f3_ = bitcast<vec3<f32>>(_e41);\n    let _e43 = i4_;\n    f4_ = bitcast<vec4<f32>>(_e43);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-bits.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var i: i32 = 0i;\n    var i2_: vec2<i32> = vec2(0i);\n    var i3_: vec3<i32> = vec3(0i);\n    var i4_: vec4<i32> = vec4(0i);\n    var u: u32 = 0u;\n    var u2_: vec2<u32> = vec2(0u);\n    var u3_: vec3<u32> = vec3(0u);\n    var u4_: vec4<u32> = vec4(0u);\n    var f2_: vec2<f32> = vec2(0f);\n    var f4_: vec4<f32> = vec4(0f);\n\n    let _e28 = f4_;\n    u = pack4x8snorm(_e28);\n    let _e30 = f4_;\n    u = pack4x8unorm(_e30);\n    let _e32 = f2_;\n    u = pack2x16snorm(_e32);\n    let _e34 = f2_;\n    u = pack2x16unorm(_e34);\n    let _e36 = f2_;\n    u = pack2x16float(_e36);\n    let _e38 = i4_;\n    u = pack4xI8(_e38);\n    let _e40 = u4_;\n    u = pack4xU8(_e40);\n    let _e42 = i4_;\n    u = pack4xI8Clamp(_e42);\n    let _e44 = u4_;\n    u = pack4xU8Clamp(_e44);\n    let _e46 = u;\n    f4_ = unpack4x8snorm(_e46);\n    let _e48 = u;\n    f4_ = unpack4x8unorm(_e48);\n    let _e50 = u;\n    f2_ = unpack2x16snorm(_e50);\n    let _e52 = u;\n    f2_ = unpack2x16unorm(_e52);\n    let _e54 = u;\n    f2_ = unpack2x16float(_e54);\n    let _e56 = u;\n    i4_ = unpack4xI8(_e56);\n    let _e58 = u;\n    u4_ = unpack4xU8(_e58);\n    let _e60 = i;\n    let _e61 = i;\n    i = insertBits(_e60, _e61, 5u, 10u);\n    let _e65 = i2_;\n    let _e66 = i2_;\n    i2_ = insertBits(_e65, _e66, 5u, 10u);\n    let _e70 = i3_;\n    let _e71 = i3_;\n    i3_ = insertBits(_e70, _e71, 5u, 10u);\n    let _e75 = i4_;\n    let _e76 = i4_;\n    i4_ = insertBits(_e75, _e76, 5u, 10u);\n    let _e80 = u;\n    let _e81 = u;\n    u = insertBits(_e80, _e81, 5u, 10u);\n    let _e85 = u2_;\n    let _e86 = u2_;\n    u2_ = insertBits(_e85, _e86, 5u, 10u);\n    let _e90 = u3_;\n    let _e91 = u3_;\n    u3_ = insertBits(_e90, _e91, 5u, 10u);\n    let _e95 = u4_;\n    let _e96 = u4_;\n    u4_ = insertBits(_e95, _e96, 5u, 10u);\n    let _e100 = i;\n    i = extractBits(_e100, 5u, 10u);\n    let _e104 = i2_;\n    i2_ = extractBits(_e104, 5u, 10u);\n    let _e108 = i3_;\n    i3_ = extractBits(_e108, 5u, 10u);\n    let _e112 = i4_;\n    i4_ = extractBits(_e112, 5u, 10u);\n    let _e116 = u;\n    u = extractBits(_e116, 5u, 10u);\n    let _e120 = u2_;\n    u2_ = extractBits(_e120, 5u, 10u);\n    let _e124 = u3_;\n    u3_ = extractBits(_e124, 5u, 10u);\n    let _e128 = u4_;\n    u4_ = extractBits(_e128, 5u, 10u);\n    let _e132 = i;\n    i = firstTrailingBit(_e132);\n    let _e134 = u2_;\n    u2_ = firstTrailingBit(_e134);\n    let _e136 = i3_;\n    i3_ = firstLeadingBit(_e136);\n    let _e138 = u3_;\n    u3_ = firstLeadingBit(_e138);\n    let _e140 = i;\n    i = firstLeadingBit(_e140);\n    let _e142 = u;\n    u = firstLeadingBit(_e142);\n    let _e144 = i;\n    i = countOneBits(_e144);\n    let _e146 = i2_;\n    i2_ = countOneBits(_e146);\n    let _e148 = i3_;\n    i3_ = countOneBits(_e148);\n    let _e150 = i4_;\n    i4_ = countOneBits(_e150);\n    let _e152 = u;\n    u = countOneBits(_e152);\n    let _e154 = u2_;\n    u2_ = countOneBits(_e154);\n    let _e156 = u3_;\n    u3_ = countOneBits(_e156);\n    let _e158 = u4_;\n    u4_ = countOneBits(_e158);\n    let _e160 = i;\n    i = reverseBits(_e160);\n    let _e162 = i2_;\n    i2_ = reverseBits(_e162);\n    let _e164 = i3_;\n    i3_ = reverseBits(_e164);\n    let _e166 = i4_;\n    i4_ = reverseBits(_e166);\n    let _e168 = u;\n    u = reverseBits(_e168);\n    let _e170 = u2_;\n    u2_ = reverseBits(_e170);\n    let _e172 = u3_;\n    u3_ = reverseBits(_e172);\n    let _e174 = u4_;\n    u4_ = reverseBits(_e174);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-boids.wgsl",
    "content": "struct Particle {\n    pos: vec2<f32>,\n    vel: vec2<f32>,\n}\n\nstruct SimParams {\n    deltaT: f32,\n    rule1Distance: f32,\n    rule2Distance: f32,\n    rule3Distance: f32,\n    rule1Scale: f32,\n    rule2Scale: f32,\n    rule3Scale: f32,\n}\n\nstruct Particles {\n    particles: array<Particle>,\n}\n\nconst NUM_PARTICLES: u32 = 1500u;\n\n@group(0) @binding(0) \nvar<uniform> params: SimParams;\n@group(0) @binding(1) \nvar<storage> particlesSrc: Particles;\n@group(0) @binding(2) \nvar<storage, read_write> particlesDst: Particles;\n\n@compute @workgroup_size(64, 1, 1) \nfn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {\n    var vPos: vec2<f32>;\n    var vVel: vec2<f32>;\n    var cMass: vec2<f32> = vec2<f32>(0f, 0f);\n    var cVel: vec2<f32> = vec2<f32>(0f, 0f);\n    var colVel: vec2<f32> = vec2<f32>(0f, 0f);\n    var cMassCount: i32 = 0i;\n    var cVelCount: i32 = 0i;\n    var pos: vec2<f32>;\n    var vel: vec2<f32>;\n    var i: u32 = 0u;\n\n    let index = global_invocation_id.x;\n    if (index >= NUM_PARTICLES) {\n        return;\n    }\n    let _e8 = particlesSrc.particles[index].pos;\n    vPos = _e8;\n    let _e14 = particlesSrc.particles[index].vel;\n    vVel = _e14;\n    loop {\n        let _e36 = i;\n        if (_e36 >= NUM_PARTICLES) {\n            break;\n        }\n        let _e39 = i;\n        if (_e39 == index) {\n            continue;\n        }\n        let _e43 = i;\n        let _e46 = particlesSrc.particles[_e43].pos;\n        pos = _e46;\n        let _e49 = i;\n        let _e52 = particlesSrc.particles[_e49].vel;\n        vel = _e52;\n        let _e53 = pos;\n        let _e54 = vPos;\n        let _e58 = params.rule1Distance;\n        if (distance(_e53, _e54) < _e58) {\n            let _e60 = cMass;\n            let _e61 = pos;\n            cMass = (_e60 + _e61);\n            let _e63 = cMassCount;\n            cMassCount = (_e63 + 1i);\n        }\n        let _e66 = pos;\n        let _e67 = vPos;\n        let _e71 = params.rule2Distance;\n        if (distance(_e66, _e67) < _e71) {\n            let _e73 = colVel;\n            let _e74 = pos;\n            let _e75 = vPos;\n            colVel = (_e73 - (_e74 - _e75));\n        }\n        let _e78 = pos;\n        let _e79 = vPos;\n        let _e83 = params.rule3Distance;\n        if (distance(_e78, _e79) < _e83) {\n            let _e85 = cVel;\n            let _e86 = vel;\n            cVel = (_e85 + _e86);\n            let _e88 = cVelCount;\n            cVelCount = (_e88 + 1i);\n        }\n        continuing {\n            let _e91 = i;\n            i = (_e91 + 1u);\n        }\n    }\n    let _e94 = cMassCount;\n    if (_e94 > 0i) {\n        let _e97 = cMass;\n        let _e98 = cMassCount;\n        let _e102 = vPos;\n        cMass = ((_e97 / vec2(f32(_e98))) - _e102);\n    }\n    let _e104 = cVelCount;\n    if (_e104 > 0i) {\n        let _e107 = cVel;\n        let _e108 = cVelCount;\n        cVel = (_e107 / vec2(f32(_e108)));\n    }\n    let _e112 = vVel;\n    let _e113 = cMass;\n    let _e116 = params.rule1Scale;\n    let _e119 = colVel;\n    let _e122 = params.rule2Scale;\n    let _e125 = cVel;\n    let _e128 = params.rule3Scale;\n    vVel = (((_e112 + (_e113 * _e116)) + (_e119 * _e122)) + (_e125 * _e128));\n    let _e131 = vVel;\n    let _e133 = vVel;\n    vVel = (normalize(_e131) * clamp(length(_e133), 0f, 0.1f));\n    let _e139 = vPos;\n    let _e140 = vVel;\n    let _e143 = params.deltaT;\n    vPos = (_e139 + (_e140 * _e143));\n    let _e147 = vPos.x;\n    if (_e147 < -1f) {\n        vPos.x = 1f;\n    }\n    let _e153 = vPos.x;\n    if (_e153 > 1f) {\n        vPos.x = -1f;\n    }\n    let _e159 = vPos.y;\n    if (_e159 < -1f) {\n        vPos.y = 1f;\n    }\n    let _e165 = vPos.y;\n    if (_e165 > 1f) {\n        vPos.y = -1f;\n    }\n    let _e174 = vPos;\n    particlesDst.particles[index].pos = _e174;\n    let _e179 = vVel;\n    particlesDst.particles[index].vel = _e179;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-break-if.wgsl",
    "content": "fn breakIfEmpty() {\n    loop {\n        continuing {\n            break if true;\n        }\n    }\n    return;\n}\n\nfn breakIfEmptyBody(a: bool) {\n    var b: bool;\n    var c: bool;\n\n    loop {\n        continuing {\n            b = a;\n            let _e2 = b;\n            c = (a != _e2);\n            let _e5 = c;\n            break if (a == _e5);\n        }\n    }\n    return;\n}\n\nfn breakIf(a_1: bool) {\n    var d: bool;\n    var e: bool;\n\n    loop {\n        d = a_1;\n        let _e2 = d;\n        e = (a_1 != _e2);\n        continuing {\n            let _e5 = e;\n            break if (a_1 == _e5);\n        }\n    }\n    return;\n}\n\nfn breakIfSeparateVariable() {\n    var counter: u32 = 0u;\n\n    loop {\n        let _e2 = counter;\n        counter = (_e2 + 1u);\n        continuing {\n            let _e5 = counter;\n            break if (_e5 == 5u);\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    breakIfEmpty();\n    breakIfEmptyBody(false);\n    breakIf(false);\n    breakIfSeparateVariable();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-clip-distances.wgsl",
    "content": "enable clip_distances;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @builtin(clip_distances) clip_distances: array<f32, 1>,\n}\n\n@vertex \nfn main() -> VertexOutput {\n    var out: VertexOutput;\n\n    out.clip_distances[0] = 0.5f;\n    let _e4 = out;\n    return _e4;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-collatz.wgsl",
    "content": "struct PrimeIndices {\n    data: array<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> v_indices: PrimeIndices;\n\nfn collatz_iterations(n_base: u32) -> u32 {\n    var n: u32;\n    var i: u32 = 0u;\n\n    n = n_base;\n    loop {\n        let _e4 = n;\n        if (_e4 > 1u) {\n        } else {\n            break;\n        }\n        {\n            let _e7 = n;\n            if ((_e7 % 2u) == 0u) {\n                let _e12 = n;\n                n = (_e12 / 2u);\n            } else {\n                let _e16 = n;\n                n = ((3u * _e16) + 1u);\n            }\n            let _e20 = i;\n            i = (_e20 + 1u);\n        }\n    }\n    let _e23 = i;\n    return _e23;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    let _e9 = v_indices.data[global_id.x];\n    let _e10 = collatz_iterations(_e9);\n    v_indices.data[global_id.x] = _e10;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-const-exprs.wgsl",
    "content": "const TWO: u32 = 2u;\nconst THREE: i32 = 3i;\nconst TRUE: bool = true;\nconst FALSE: bool = false;\nconst FOUR: i32 = 4i;\nconst TEXTURE_KIND_REGULAR: i32 = 0i;\nconst TEXTURE_KIND_WARP: i32 = 1i;\nconst TEXTURE_KIND_SKY: i32 = 2i;\nconst FOUR_ALIAS: i32 = 4i;\nconst TEST_CONSTANT_ADDITION: i32 = 8i;\nconst TEST_CONSTANT_ALIAS_ADDITION: i32 = 8i;\nconst PI: f32 = 3.141f;\nconst phi_sun: f32 = 6.282f;\nconst DIV: vec4<f32> = vec4<f32>(0.44444445f, 0f, 0f, 0f);\nconst add_vec: vec2<f32> = vec2<f32>(4f, 5f);\nconst compare_vec: vec2<bool> = vec2<bool>(true, false);\n\nfn swizzle_of_compose() {\n    var out: vec4<i32> = vec4<i32>(4i, 3i, 2i, 1i);\n\n    return;\n}\n\nfn index_of_compose() {\n    var out_1: i32 = 2i;\n\n    return;\n}\n\nfn compose_three_deep() {\n    var out_2: i32 = 6i;\n\n    return;\n}\n\nfn non_constant_initializers() {\n    var w: i32 = 30i;\n    var x: i32;\n    var y: i32;\n    var z: i32 = 70i;\n    var out_3: vec4<i32>;\n\n    let _e2 = w;\n    x = _e2;\n    let _e4 = x;\n    y = _e4;\n    let _e8 = w;\n    let _e9 = x;\n    let _e10 = y;\n    let _e11 = z;\n    out_3 = vec4<i32>(_e8, _e9, _e10, _e11);\n    return;\n}\n\nfn splat_of_constant() {\n    var out_4: vec4<i32> = vec4<i32>(-4i, -4i, -4i, -4i);\n\n    return;\n}\n\nfn compose_of_constant() {\n    var out_5: vec4<i32> = vec4<i32>(-4i, -4i, -4i, -4i);\n\n    return;\n}\n\nfn map_texture_kind(texture_kind: i32) -> u32 {\n    switch texture_kind {\n        case 0: {\n            return 10u;\n        }\n        case 1: {\n            return 20u;\n        }\n        case 2: {\n            return 30u;\n        }\n        default: {\n            return 0u;\n        }\n    }\n}\n\nfn compose_of_splat() {\n    var x_1: vec4<f32> = vec4<f32>(2f, 1f, 1f, 1f);\n\n    return;\n}\n\nfn test_local_const() {\n    var arr: array<f32, 2>;\n\n    return;\n}\n\nfn compose_vector_zero_val_binop() {\n    var a: vec3<i32> = vec3<i32>(1i, 1i, 1i);\n    var b: vec3<i32> = vec3<i32>(0i, 1i, 2i);\n    var c: vec3<i32> = vec3<i32>(1i, 0i, 2i);\n\n    return;\n}\n\nfn relational() {\n    var scalar_any_false: bool = false;\n    var scalar_any_true: bool = true;\n    var scalar_all_false: bool = false;\n    var scalar_all_true: bool = true;\n    var vec_any_false: bool = false;\n    var vec_any_true: bool = true;\n    var vec_all_false: bool = false;\n    var vec_all_true: bool = true;\n\n    return;\n}\n\nfn packed_dot_product() {\n    var signed_four: i32 = 4i;\n    var unsigned_four: u32 = 4u;\n    var signed_twelve: i32 = 12i;\n    var unsigned_twelve: u32 = 12u;\n    var signed_seventy: i32 = 70i;\n    var unsigned_seventy: u32 = 70u;\n    var minus_four: i32 = -4i;\n\n    return;\n}\n\nfn abstract_access(i: u32) {\n    var a_1: f32 = 1f;\n    var b_1: u32 = 1u;\n    var c_1: i32;\n    var d: i32;\n\n    c_1 = array<i32, 9>(1i, 2i, 3i, 4i, 5i, 6i, 7i, 8i, 9i)[i];\n    d = vec4<i32>(1i, 2i, 3i, 4i)[i];\n    return;\n}\n\n@compute @workgroup_size(2, 3, 1) \nfn main() {\n    swizzle_of_compose();\n    index_of_compose();\n    compose_three_deep();\n    non_constant_initializers();\n    splat_of_constant();\n    compose_of_constant();\n    let _e1 = map_texture_kind(1i);\n    compose_of_splat();\n    test_local_const();\n    compose_vector_zero_val_binop();\n    relational();\n    packed_dot_product();\n    test_local_const();\n    abstract_access(1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-const_assert.wgsl",
    "content": "const g_false: bool = false;\n\n@compute @workgroup_size(1, 1, 1) \nfn foo() {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-constructors.wgsl",
    "content": "struct Foo {\n    a: vec4<f32>,\n    b: i32,\n}\n\nconst const1_: vec3<f32> = vec3(0f);\nconst const3_: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 1f), vec2<f32>(2f, 3f));\nconst const4_: array<mat2x2<f32>, 1> = array<mat2x2<f32>, 1>(mat2x2<f32>(vec2<f32>(0f, 1f), vec2<f32>(2f, 3f)));\nconst cz0_: bool = bool();\nconst cz1_: i32 = i32();\nconst cz2_: u32 = u32();\nconst cz3_: f32 = f32();\nconst cz4_: vec2<u32> = vec2<u32>();\nconst cz5_: mat2x2<f32> = mat2x2<f32>();\nconst cz6_: array<Foo, 3> = array<Foo, 3>();\nconst cz7_: Foo = Foo();\nconst cp1_: vec2<u32> = vec2(0u);\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var foo: Foo;\n\n    foo = Foo(vec4(1f), 1i);\n    let m0_ = mat2x2<f32>(vec2<f32>(1f, 0f), vec2<f32>(0f, 1f));\n    let m1_ = mat4x4<f32>(vec4<f32>(1f, 0f, 0f, 0f), vec4<f32>(0f, 1f, 0f, 0f), vec4<f32>(0f, 0f, 1f, 0f), vec4<f32>(0f, 0f, 0f, 1f));\n    let zvc8_ = vec2<u32>(0u, 0u);\n    let zvc9_ = vec2<f32>(0f, 0f);\n    let cit0_ = vec2(0u);\n    let cit1_ = mat2x2<f32>(vec2(0f), vec2(0f));\n    let cit2_ = array<i32, 4>(0i, 1i, 2i, 3i);\n    let ic4_ = vec2<u32>(0u, 0u);\n    let ic5_ = mat2x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-control-flow.wgsl",
    "content": "fn control_flow() {\n    var pos: i32;\n\n    storageBarrier();\n    workgroupBarrier();\n    textureBarrier();\n    switch 1i {\n        default: {\n            pos = 1i;\n        }\n    }\n    let _e3 = pos;\n    switch _e3 {\n        case 1: {\n            pos = 0i;\n            break;\n        }\n        case 2: {\n            pos = 1i;\n        }\n        case 3, 4: {\n            pos = 2i;\n        }\n        case 5: {\n            pos = 3i;\n        }\n        case default, 6: {\n            pos = 4i;\n        }\n    }\n    switch 0u {\n        case 0u: {\n        }\n        default: {\n        }\n    }\n    let _e10 = pos;\n    switch _e10 {\n        case 1: {\n            pos = 0i;\n            break;\n        }\n        case 2: {\n            pos = 1i;\n        }\n        case 3: {\n            pos = 2i;\n        }\n        case 4: {\n        }\n        default: {\n            pos = 3i;\n        }\n    }\n    let _e15 = pos;\n    switch _e15 {\n        case 1: {\n            pos = 0i;\n            return;\n        }\n        case 2: {\n            pos = 1i;\n            return;\n        }\n        case 3, 4: {\n            pos = 2i;\n            return;\n        }\n        case 5, 6: {\n            pos = 3i;\n            return;\n        }\n        default: {\n            pos = 4i;\n            return;\n        }\n    }\n}\n\nfn switch_default_break(i: i32) {\n    switch i {\n        default: {\n            break;\n        }\n    }\n}\n\nfn switch_case_break() {\n    switch 0i {\n        case 0: {\n            break;\n        }\n        default: {\n        }\n    }\n    return;\n}\n\nfn switch_selector_type_conversion() {\n    switch 0u {\n        case 0u: {\n        }\n        default: {\n        }\n    }\n    switch 0u {\n        case 0u: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nfn switch_const_expr_case_selectors() {\n    switch 0i {\n        case 0: {\n            return;\n        }\n        case 1: {\n            return;\n        }\n        case 2: {\n            return;\n        }\n        case 3: {\n            return;\n        }\n        case 4: {\n            return;\n        }\n        default: {\n            return;\n        }\n    }\n}\n\nfn loop_switch_continue(x: i32) {\n    loop {\n        switch x {\n            case 1: {\n                continue;\n            }\n            default: {\n            }\n        }\n    }\n    return;\n}\n\nfn loop_switch_continue_nesting(x_1: i32, y: i32, z: i32) {\n    loop {\n        switch x_1 {\n            case 1: {\n                continue;\n            }\n            case 2: {\n                switch y {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        loop {\n                            switch z {\n                                case 1: {\n                                    continue;\n                                }\n                                default: {\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            default: {\n            }\n        }\n        switch y {\n            default: {\n                continue;\n            }\n        }\n    }\n    loop {\n        switch y {\n            case 1, default: {\n                switch z {\n                    default: {\n                        continue;\n                    }\n                }\n            }\n        }\n    }\n    return;\n}\n\nfn loop_switch_omit_continue_variable_checks(x_2: i32, y_1: i32, z_1: i32, w: i32) {\n    var pos_1: i32 = 0i;\n\n    loop {\n        switch x_2 {\n            case 1: {\n                pos_1 = 1i;\n            }\n            default: {\n            }\n        }\n    }\n    loop {\n        switch x_2 {\n            case 1: {\n            }\n            case 2: {\n                switch y_1 {\n                    case 1: {\n                        continue;\n                    }\n                    default: {\n                        switch z_1 {\n                            case 1: {\n                                pos_1 = 2i;\n                            }\n                            default: {\n                            }\n                        }\n                    }\n                }\n            }\n            default: {\n            }\n        }\n    }\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    control_flow();\n    switch_default_break(1i);\n    switch_case_break();\n    switch_selector_type_conversion();\n    switch_const_expr_case_selectors();\n    loop_switch_continue(1i);\n    loop_switch_continue_nesting(1i, 2i, 3i);\n    loop_switch_omit_continue_variable_checks(1i, 2i, 3i, 4i);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-conversion-float-to-int.wgsl",
    "content": "enable f16;\n\nconst MIN_F16_: f16 = -65504h;\nconst MAX_F16_: f16 = 65504h;\nconst MIN_F32_: f32 = -340282350000000000000000000000000000000f;\nconst MAX_F32_: f32 = 340282350000000000000000000000000000000f;\nconst MIN_F64_: f64 = -1.7976931348623157e308lf;\nconst MAX_F64_: f64 = 1.7976931348623157e308lf;\n\nfn test_const_eval() {\n    var min_f16_to_i32_: i32 = -65504i;\n    var max_f16_to_i32_: i32 = 65504i;\n    var min_f16_to_u32_: u32 = 0u;\n    var max_f16_to_u32_: u32 = 65504u;\n    var min_f16_to_i64_: i64 = -65504li;\n    var max_f16_to_i64_: i64 = 65504li;\n    var min_f16_to_u64_: u64 = 0lu;\n    var max_f16_to_u64_: u64 = 65504lu;\n    var min_f32_to_i32_: i32 = i32(-2147483648);\n    var max_f32_to_i32_: i32 = 2147483520i;\n    var min_f32_to_u32_: u32 = 0u;\n    var max_f32_to_u32_: u32 = 4294967040u;\n    var min_f32_to_i64_: i64 = i64(-9223372036854775807 - 1);\n    var max_f32_to_i64_: i64 = 9223371487098961920li;\n    var min_f32_to_u64_: u64 = 0lu;\n    var max_f32_to_u64_: u64 = 18446742974197923840lu;\n    var min_f64_to_i64_: i64 = i64(-9223372036854775807 - 1);\n    var max_f64_to_i64_: i64 = 9223372036854774784li;\n    var min_f64_to_u64_: u64 = 0lu;\n    var max_f64_to_u64_: u64 = 18446744073709549568lu;\n    var min_abstract_float_to_i32_: i32 = i32(-2147483648);\n    var max_abstract_float_to_i32_: i32 = 2147483647i;\n    var min_abstract_float_to_u32_: u32 = 0u;\n    var max_abstract_float_to_u32_: u32 = 4294967295u;\n    var min_abstract_float_to_i64_: i64 = i64(-9223372036854775807 - 1);\n    var max_abstract_float_to_i64_: i64 = 9223372036854774784li;\n    var min_abstract_float_to_u64_: u64 = 0lu;\n    var max_abstract_float_to_u64_: u64 = 18446744073709549568lu;\n\n    return;\n}\n\nfn test_f16_to_i32_(f: f16) -> i32 {\n    return i32(f);\n}\n\nfn test_f16_to_u32_(f_1: f16) -> u32 {\n    return u32(f_1);\n}\n\nfn test_f16_to_i64_(f_2: f16) -> i64 {\n    return i64(f_2);\n}\n\nfn test_f16_to_u64_(f_3: f16) -> u64 {\n    return u64(f_3);\n}\n\nfn test_f32_to_i32_(f_4: f32) -> i32 {\n    return i32(f_4);\n}\n\nfn test_f32_to_u32_(f_5: f32) -> u32 {\n    return u32(f_5);\n}\n\nfn test_f32_to_i64_(f_6: f32) -> i64 {\n    return i64(f_6);\n}\n\nfn test_f32_to_u64_(f_7: f32) -> u64 {\n    return u64(f_7);\n}\n\nfn test_f64_to_i32_(f_8: f64) -> i32 {\n    return i32(f_8);\n}\n\nfn test_f64_to_u32_(f_9: f64) -> u32 {\n    return u32(f_9);\n}\n\nfn test_f64_to_i64_(f_10: f64) -> i64 {\n    return i64(f_10);\n}\n\nfn test_f64_to_u64_(f_11: f64) -> u64 {\n    return u64(f_11);\n}\n\nfn test_f16_to_i32_vec(f_12: vec2<f16>) -> vec2<i32> {\n    return vec2<i32>(f_12);\n}\n\nfn test_f16_to_u32_vec(f_13: vec2<f16>) -> vec2<u32> {\n    return vec2<u32>(f_13);\n}\n\nfn test_f16_to_i64_vec(f_14: vec2<f16>) -> vec2<i64> {\n    return vec2<i64>(f_14);\n}\n\nfn test_f16_to_u64_vec(f_15: vec2<f16>) -> vec2<u64> {\n    return vec2<u64>(f_15);\n}\n\nfn test_f32_to_i32_vec(f_16: vec2<f32>) -> vec2<i32> {\n    return vec2<i32>(f_16);\n}\n\nfn test_f32_to_u32_vec(f_17: vec2<f32>) -> vec2<u32> {\n    return vec2<u32>(f_17);\n}\n\nfn test_f32_to_i64_vec(f_18: vec2<f32>) -> vec2<i64> {\n    return vec2<i64>(f_18);\n}\n\nfn test_f32_to_u64_vec(f_19: vec2<f32>) -> vec2<u64> {\n    return vec2<u64>(f_19);\n}\n\nfn test_f64_to_i32_vec(f_20: vec2<f64>) -> vec2<i32> {\n    return vec2<i32>(f_20);\n}\n\nfn test_f64_to_u32_vec(f_21: vec2<f64>) -> vec2<u32> {\n    return vec2<u32>(f_21);\n}\n\nfn test_f64_to_i64_vec(f_22: vec2<f64>) -> vec2<i64> {\n    return vec2<i64>(f_22);\n}\n\nfn test_f64_to_u64_vec(f_23: vec2<f64>) -> vec2<u64> {\n    return vec2<u64>(f_23);\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    test_const_eval();\n    let _e1 = test_f16_to_i32_(1h);\n    let _e3 = test_f16_to_u32_(1h);\n    let _e5 = test_f16_to_i64_(1h);\n    let _e7 = test_f16_to_u64_(1h);\n    let _e9 = test_f32_to_i32_(1f);\n    let _e11 = test_f32_to_u32_(1f);\n    let _e13 = test_f32_to_i64_(1f);\n    let _e15 = test_f32_to_u64_(1f);\n    let _e17 = test_f64_to_i32_(1.0lf);\n    let _e19 = test_f64_to_u32_(1.0lf);\n    let _e21 = test_f64_to_i64_(1.0lf);\n    let _e23 = test_f64_to_u64_(1.0lf);\n    let _e27 = test_f16_to_i32_vec(vec2<f16>(1h, 2h));\n    let _e31 = test_f16_to_u32_vec(vec2<f16>(1h, 2h));\n    let _e35 = test_f16_to_i64_vec(vec2<f16>(1h, 2h));\n    let _e39 = test_f16_to_u64_vec(vec2<f16>(1h, 2h));\n    let _e43 = test_f32_to_i32_vec(vec2<f32>(1f, 2f));\n    let _e47 = test_f32_to_u32_vec(vec2<f32>(1f, 2f));\n    let _e51 = test_f32_to_i64_vec(vec2<f32>(1f, 2f));\n    let _e55 = test_f32_to_u64_vec(vec2<f32>(1f, 2f));\n    let _e59 = test_f64_to_i32_vec(vec2<f64>(1.0lf, 2.0lf));\n    let _e63 = test_f64_to_u32_vec(vec2<f64>(1.0lf, 2.0lf));\n    let _e67 = test_f64_to_i64_vec(vec2<f64>(1.0lf, 2.0lf));\n    let _e71 = test_f64_to_u64_vec(vec2<f64>(1.0lf, 2.0lf));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-conversions.wgsl",
    "content": "const ic0_: vec2<f32> = vec2<f32>(2f, 2f);\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var vc0_: vec2<f32> = vec2<f32>(2f, 2f);\n\n    let lc0_ = vec2<f32>(2f, 2f);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-cooperative-matrix.wgsl",
    "content": "enable wgpu_cooperative_matrix;\n\nvar<private> a: coop_mat8x8<f32,A>;\nvar<private> b: coop_mat8x8<f32,B>;\n@group(0) @binding(0) \nvar<storage, read_write> ext: array<f32>;\n\n@compute @workgroup_size(8, 8, 1) \nfn main() {\n    var c: coop_mat8x8<f32,C>;\n    var d: coop_mat8x8<f32,C>;\n\n    c = coopLoad<coop_mat8x8<f32,C>>((&ext[4]), 8u);\n    let _e6 = a;\n    let _e8 = b;\n    let _e9 = c;\n    d = coopMultiplyAdd(_e6, _e8, _e9);\n    let _e12 = d;\n    coopStore(_e12, (&ext[0]), 8u);\n    let _e16 = d;\n    c = _e16;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-cross.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let a = vec3<f32>(0f, 0f, 1f);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-draw-index.wgsl",
    "content": "enable draw_index;\n\nstruct Input {\n    @builtin(draw_index) draw_index: u32,\n}\n\n@vertex \nfn vertex(input: Input) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(f32(input.draw_index), 1f, 1f, 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-dualsource.wgsl",
    "content": "enable dual_source_blending;\n\nstruct FragmentOutput {\n    @location(0) @blend_src(0) output0_: vec4<f32>,\n    @location(0) @blend_src(1) output1_: vec4<f32>,\n}\n\n@fragment \nfn main() -> FragmentOutput {\n    return FragmentOutput(vec4<f32>(0.4f, 0.3f, 0.2f, 0.1f), vec4<f32>(0.9f, 0.8f, 0.7f, 0.6f));\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-empty-if.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn comp(@builtin(global_invocation_id) id: vec3<u32>) {\n    if (id.x == 0u) {\n    }\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-empty.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-extra.wgsl",
    "content": "enable primitive_index;\n\nstruct ImmediateData {\n    index: u32,\n    double: vec2<f32>,\n}\n\nstruct FragmentIn {\n    @location(0) color: vec4<f32>,\n    @builtin(primitive_index) primitive_index: u32,\n}\n\nvar<immediate> im: ImmediateData;\n\n@fragment \nfn main(in: FragmentIn) -> @location(0) vec4<f32> {\n    let _e4 = im.index;\n    if (in.primitive_index == _e4) {\n        return in.color;\n    } else {\n        return vec4<f32>((vec3(1f) - in.color.xyz), in.color.w);\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-f16.wgsl",
    "content": "enable f16;\n\nstruct UniformCompatible {\n    val_u32_: u32,\n    val_i32_: i32,\n    val_f32_: f32,\n    val_f16_: f16,\n    val_f16_2_: vec2<f16>,\n    val_f16_3_: vec3<f16>,\n    val_f16_4_: vec4<f16>,\n    final_value: f16,\n    val_mat2x2_: mat2x2<f16>,\n    val_mat2x3_: mat2x3<f16>,\n    val_mat2x4_: mat2x4<f16>,\n    val_mat3x2_: mat3x2<f16>,\n    val_mat3x3_: mat3x3<f16>,\n    val_mat3x4_: mat3x4<f16>,\n    val_mat4x2_: mat4x2<f16>,\n    val_mat4x3_: mat4x3<f16>,\n    val_mat4x4_: mat4x4<f16>,\n}\n\nstruct StorageCompatible {\n    val_f16_array_2_: array<f16, 2>,\n}\n\nstruct LayoutTest {\n    scalar1_: f16,\n    scalar2_: f16,\n    v3_: vec3<f16>,\n    tuck_in: f16,\n    scalar4_: f16,\n    larger: u32,\n}\n\nconst constant_variable: f16 = 15.203125h;\n\nvar<private> private_variable: f16 = 1h;\n@group(0) @binding(0) \nvar<uniform> input_uniform: UniformCompatible;\n@group(0) @binding(1) \nvar<storage> input_storage: UniformCompatible;\n@group(0) @binding(2) \nvar<storage> input_arrays: StorageCompatible;\n@group(0) @binding(3) \nvar<storage, read_write> output: UniformCompatible;\n@group(0) @binding(4) \nvar<storage, read_write> output_arrays: StorageCompatible;\n\nfn f16_function(x: f16) -> f16 {\n    var l: LayoutTest;\n    var val: f16 = 15.203125h;\n\n    let phony = private_variable;\n    let _e5 = val;\n    val = (_e5 + -33344h);\n    let _e8 = val;\n    let _e9 = val;\n    val = (_e8 + (_e9 + 5h));\n    let _e13 = val;\n    let _e16 = input_uniform.val_f32_;\n    let _e17 = val;\n    val = (_e13 + f16((_e16 + f32(_e17))));\n    let _e22 = val;\n    let _e25 = input_uniform.val_f16_;\n    val = (_e22 + vec3(_e25).z);\n    output.val_i32_ = 65504i;\n    output.val_i32_ = -65504i;\n    output.val_u32_ = 65504u;\n    output.val_u32_ = 0u;\n    output.val_f32_ = 65504f;\n    output.val_f32_ = -65504f;\n    let _e51 = input_uniform.val_f16_;\n    let _e54 = input_storage.val_f16_;\n    output.val_f16_ = (_e51 + _e54);\n    let _e60 = input_uniform.val_f16_2_;\n    let _e63 = input_storage.val_f16_2_;\n    output.val_f16_2_ = (_e60 + _e63);\n    let _e69 = input_uniform.val_f16_3_;\n    let _e72 = input_storage.val_f16_3_;\n    output.val_f16_3_ = (_e69 + _e72);\n    let _e78 = input_uniform.val_f16_4_;\n    let _e81 = input_storage.val_f16_4_;\n    output.val_f16_4_ = (_e78 + _e81);\n    let _e87 = input_uniform.val_mat2x2_;\n    let _e90 = input_storage.val_mat2x2_;\n    output.val_mat2x2_ = (_e87 + _e90);\n    let _e96 = input_uniform.val_mat2x3_;\n    let _e99 = input_storage.val_mat2x3_;\n    output.val_mat2x3_ = (_e96 + _e99);\n    let _e105 = input_uniform.val_mat2x4_;\n    let _e108 = input_storage.val_mat2x4_;\n    output.val_mat2x4_ = (_e105 + _e108);\n    let _e114 = input_uniform.val_mat3x2_;\n    let _e117 = input_storage.val_mat3x2_;\n    output.val_mat3x2_ = (_e114 + _e117);\n    let _e123 = input_uniform.val_mat3x3_;\n    let _e126 = input_storage.val_mat3x3_;\n    output.val_mat3x3_ = (_e123 + _e126);\n    let _e132 = input_uniform.val_mat3x4_;\n    let _e135 = input_storage.val_mat3x4_;\n    output.val_mat3x4_ = (_e132 + _e135);\n    let _e141 = input_uniform.val_mat4x2_;\n    let _e144 = input_storage.val_mat4x2_;\n    output.val_mat4x2_ = (_e141 + _e144);\n    let _e150 = input_uniform.val_mat4x3_;\n    let _e153 = input_storage.val_mat4x3_;\n    output.val_mat4x3_ = (_e150 + _e153);\n    let _e159 = input_uniform.val_mat4x4_;\n    let _e162 = input_storage.val_mat4x4_;\n    output.val_mat4x4_ = (_e159 + _e162);\n    let _e168 = input_arrays.val_f16_array_2_;\n    output_arrays.val_f16_array_2_ = _e168;\n    let _e169 = val;\n    let _e170 = val;\n    val = (_e169 + abs(_e170));\n    let _e173 = val;\n    let _e174 = val;\n    let _e175 = val;\n    let _e176 = val;\n    val = (_e173 + clamp(_e174, _e175, _e176));\n    let _e179 = val;\n    let _e180 = val;\n    let _e182 = val;\n    val = (_e179 + dot(vec2(_e180), vec2(_e182)));\n    let _e186 = val;\n    let _e187 = val;\n    let _e188 = val;\n    val = (_e186 + max(_e187, _e188));\n    let _e191 = val;\n    let _e192 = val;\n    let _e193 = val;\n    val = (_e191 + min(_e192, _e193));\n    let _e196 = val;\n    let _e197 = val;\n    val = (_e196 + sign(_e197));\n    let _e200 = val;\n    val = (_e200 + 1h);\n    let _e205 = input_uniform.val_f16_2_;\n    let float_vec2_ = vec2<f32>(_e205);\n    output.val_f16_2_ = vec2<f16>(float_vec2_);\n    let _e212 = input_uniform.val_f16_3_;\n    let float_vec3_ = vec3<f32>(_e212);\n    output.val_f16_3_ = vec3<f16>(float_vec3_);\n    let _e219 = input_uniform.val_f16_4_;\n    let float_vec4_ = vec4<f32>(_e219);\n    output.val_f16_4_ = vec4<f16>(float_vec4_);\n    let _e228 = input_uniform.val_mat2x2_;\n    output.val_mat2x2_ = mat2x2<f16>(mat2x2<f32>(_e228));\n    let _e235 = input_uniform.val_mat2x3_;\n    output.val_mat2x3_ = mat2x3<f16>(mat2x3<f32>(_e235));\n    let _e242 = input_uniform.val_mat2x4_;\n    output.val_mat2x4_ = mat2x4<f16>(mat2x4<f32>(_e242));\n    let _e249 = input_uniform.val_mat3x2_;\n    output.val_mat3x2_ = mat3x2<f16>(mat3x2<f32>(_e249));\n    let _e256 = input_uniform.val_mat3x3_;\n    output.val_mat3x3_ = mat3x3<f16>(mat3x3<f32>(_e256));\n    let _e263 = input_uniform.val_mat3x4_;\n    output.val_mat3x4_ = mat3x4<f16>(mat3x4<f32>(_e263));\n    let _e270 = input_uniform.val_mat4x2_;\n    output.val_mat4x2_ = mat4x2<f16>(mat4x2<f32>(_e270));\n    let _e277 = input_uniform.val_mat4x3_;\n    output.val_mat4x3_ = mat4x3<f16>(mat4x3<f32>(_e277));\n    let _e284 = input_uniform.val_mat4x4_;\n    output.val_mat4x4_ = mat4x4<f16>(mat4x4<f32>(_e284));\n    let _e287 = val;\n    return _e287;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e3 = f16_function(2h);\n    output.final_value = _e3;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-f64.wgsl",
    "content": "const k: f64 = 2.0lf;\n\nvar<private> v: f64 = 1.0lf;\n\nfn f(x: f64) -> f64 {\n    var z: f64;\n    var w: f64 = -1.0lf;\n\n    let phony = v;\n    let y = (30.0lf + 400.0lf);\n    z = (y + 5.0lf);\n    return (((x + y) + k) + 5.0lf);\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e1 = f(6.0lf);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-fragment-output.wgsl",
    "content": "struct FragmentOutputVec4Vec3_ {\n    @location(0) vec4f: vec4<f32>,\n    @location(1) @interpolate(flat) vec4i: vec4<i32>,\n    @location(2) @interpolate(flat) vec4u: vec4<u32>,\n    @location(3) vec3f: vec3<f32>,\n    @location(4) @interpolate(flat) vec3i: vec3<i32>,\n    @location(5) @interpolate(flat) vec3u: vec3<u32>,\n}\n\nstruct FragmentOutputVec2Scalar {\n    @location(0) vec2f: vec2<f32>,\n    @location(1) @interpolate(flat) vec2i: vec2<i32>,\n    @location(2) @interpolate(flat) vec2u: vec2<u32>,\n    @location(3) scalarf: f32,\n    @location(4) @interpolate(flat) scalari: i32,\n    @location(5) @interpolate(flat) scalaru: u32,\n}\n\n@fragment \nfn main_vec4vec3_() -> FragmentOutputVec4Vec3_ {\n    var output: FragmentOutputVec4Vec3_;\n\n    output.vec4f = vec4(0f);\n    output.vec4i = vec4(0i);\n    output.vec4u = vec4(0u);\n    output.vec3f = vec3(0f);\n    output.vec3i = vec3(0i);\n    output.vec3u = vec3(0u);\n    let _e19 = output;\n    return _e19;\n}\n\n@fragment \nfn main_vec2scalar() -> FragmentOutputVec2Scalar {\n    var output_1: FragmentOutputVec2Scalar;\n\n    output_1.vec2f = vec2(0f);\n    output_1.vec2i = vec2(0i);\n    output_1.vec2u = vec2(0u);\n    output_1.scalarf = 0f;\n    output_1.scalari = 0i;\n    output_1.scalaru = 0u;\n    let _e16 = output_1;\n    return _e16;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-functions.wgsl",
    "content": "fn test_fma() -> vec2<f32> {\n    let a = vec2<f32>(2f, 2f);\n    let b = vec2<f32>(0.5f, 0.5f);\n    let c = vec2<f32>(0.5f, 0.5f);\n    return fma(a, b, c);\n}\n\nfn test_integer_dot_product() -> i32 {\n    let a_2_ = vec2(1i);\n    let b_2_ = vec2(1i);\n    let c_2_ = dot(a_2_, b_2_);\n    let a_3_ = vec3(1u);\n    let b_3_ = vec3(1u);\n    let c_3_ = dot(a_3_, b_3_);\n    return 32i;\n}\n\nfn test_packed_integer_dot_product() -> u32 {\n    let c_5_ = dot4I8Packed(1u, 2u);\n    let c_6_ = dot4U8Packed(3u, 4u);\n    let c_7_ = dot4I8Packed((5u + c_6_), (6u + c_6_));\n    let c_8_ = dot4U8Packed((7u + c_6_), (8u + c_6_));\n    return c_8_;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e0 = test_fma();\n    let _e1 = test_integer_dot_product();\n    let _e2 = test_packed_integer_dot_product();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-globals.wgsl",
    "content": "struct FooStruct {\n    v3_: vec3<f32>,\n    v1_: f32,\n}\n\nconst Foo_1: bool = true;\n\nvar<workgroup> wg: array<f32, 10>;\nvar<workgroup> at_1: atomic<u32>;\n@group(0) @binding(1) \nvar<storage, read_write> alignment: FooStruct;\n@group(0) @binding(2) \nvar<storage> dummy: array<vec2<f32>>;\n@group(0) @binding(3) \nvar<uniform> float_vecs: array<vec4<f32>, 20>;\n@group(0) @binding(4) \nvar<uniform> global_vec: vec3<f32>;\n@group(0) @binding(5) \nvar<uniform> global_mat: mat3x2<f32>;\n@group(0) @binding(6) \nvar<uniform> global_nested_arrays_of_matrices_2x4_: array<array<mat2x4<f32>, 2>, 2>;\n@group(0) @binding(7) \nvar<uniform> global_nested_arrays_of_matrices_4x2_: array<array<mat4x2<f32>, 2>, 2>;\n\nfn test_msl_packed_vec3_as_arg(arg: vec3<f32>) {\n    return;\n}\n\nfn test_msl_packed_vec3_() {\n    var idx: i32 = 1i;\n\n    alignment.v3_ = vec3(1f);\n    alignment.v3_.x = 1f;\n    alignment.v3_.x = 2f;\n    let _e16 = idx;\n    alignment.v3_[_e16] = 3f;\n    let data = alignment;\n    let l0_ = data.v3_;\n    let l1_ = data.v3_.zx;\n    test_msl_packed_vec3_as_arg(data.v3_);\n    let mvm0_ = (data.v3_ * mat3x3<f32>());\n    let mvm1_ = (mat3x3<f32>() * data.v3_);\n    let svm0_ = (data.v3_ * 2f);\n    let svm1_ = (2f * data.v3_);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var Foo: f32 = 1f;\n    var at: bool = true;\n\n    test_msl_packed_vec3_();\n    let _e5 = global_nested_arrays_of_matrices_4x2_[0][0];\n    let _e10 = global_nested_arrays_of_matrices_2x4_[0][0][0];\n    wg[7] = (_e5 * _e10).x;\n    let _e16 = global_mat;\n    let _e18 = global_vec;\n    wg[6] = (_e16 * _e18).x;\n    let _e26 = dummy[1].y;\n    wg[5] = _e26;\n    let _e32 = float_vecs[0].w;\n    wg[4] = _e32;\n    let _e37 = alignment.v1_;\n    wg[3] = _e37;\n    let _e43 = alignment.v3_.x;\n    wg[2] = _e43;\n    alignment.v1_ = 4f;\n    wg[1] = f32(arrayLength((&dummy)));\n    atomicStore((&at_1), 2u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-image.wgsl",
    "content": "@group(0) @binding(0) \nvar image_mipmapped_src: texture_2d<u32>;\n@group(0) @binding(3) \nvar image_multisampled_src: texture_multisampled_2d<u32>;\n@group(0) @binding(4) \nvar image_depth_multisampled_src: texture_depth_multisampled_2d;\n@group(0) @binding(1) \nvar image_storage_src: texture_storage_2d<rgba8uint,read>;\n@group(0) @binding(5) \nvar image_array_src: texture_2d_array<u32>;\n@group(0) @binding(6) \nvar image_dup_src: texture_storage_1d<r32uint,read>;\n@group(0) @binding(7) \nvar image_1d_src: texture_1d<u32>;\n@group(0) @binding(2) \nvar image_dst: texture_storage_1d<r32uint,write>;\n@group(0) @binding(0) \nvar image_1d: texture_1d<f32>;\n@group(0) @binding(1) \nvar image_2d: texture_2d<f32>;\n@group(0) @binding(2) \nvar image_2d_u32_: texture_2d<u32>;\n@group(0) @binding(3) \nvar image_2d_i32_: texture_2d<i32>;\n@group(0) @binding(4) \nvar image_2d_array: texture_2d_array<f32>;\n@group(0) @binding(5) \nvar image_cube: texture_cube<f32>;\n@group(0) @binding(6) \nvar image_cube_array: texture_cube_array<f32>;\n@group(0) @binding(7) \nvar image_3d: texture_3d<f32>;\n@group(0) @binding(8) \nvar image_aa: texture_multisampled_2d<f32>;\n@group(1) @binding(0) \nvar sampler_reg: sampler;\n@group(1) @binding(1) \nvar sampler_cmp: sampler_comparison;\n@group(1) @binding(2) \nvar image_2d_depth: texture_depth_2d;\n@group(1) @binding(3) \nvar image_2d_array_depth: texture_depth_2d_array;\n@group(1) @binding(4) \nvar image_cube_depth: texture_depth_cube;\n\n@compute @workgroup_size(16, 1, 1) \nfn main(@builtin(local_invocation_id) local_id: vec3<u32>) {\n    let dim = textureDimensions(image_storage_src);\n    let itc = (vec2<i32>((dim * local_id.xy)) % vec2<i32>(10i, 20i));\n    let value1_ = textureLoad(image_mipmapped_src, itc, i32(local_id.z));\n    let value1_2_ = textureLoad(image_mipmapped_src, itc, u32(local_id.z));\n    let value2_ = textureLoad(image_multisampled_src, itc, i32(local_id.z));\n    let value3_ = textureLoad(image_multisampled_src, itc, u32(local_id.z));\n    let value4_ = textureLoad(image_storage_src, itc);\n    let value5_ = textureLoad(image_array_src, itc, local_id.z, (i32(local_id.z) + 1i));\n    let value6_ = textureLoad(image_array_src, itc, i32(local_id.z), (i32(local_id.z) + 1i));\n    let value7_ = textureLoad(image_1d_src, i32(local_id.x), i32(local_id.z));\n    let value8_ = textureLoad(image_dup_src, i32(local_id.x));\n    let value1u = textureLoad(image_mipmapped_src, vec2<u32>(itc), i32(local_id.z));\n    let value2u = textureLoad(image_multisampled_src, vec2<u32>(itc), i32(local_id.z));\n    let value3u = textureLoad(image_multisampled_src, vec2<u32>(itc), u32(local_id.z));\n    let value4u = textureLoad(image_storage_src, vec2<u32>(itc));\n    let value5u = textureLoad(image_array_src, vec2<u32>(itc), local_id.z, (i32(local_id.z) + 1i));\n    let value6u = textureLoad(image_array_src, vec2<u32>(itc), i32(local_id.z), (i32(local_id.z) + 1i));\n    let value7u = textureLoad(image_1d_src, u32(local_id.x), i32(local_id.z));\n    textureStore(image_dst, itc.x, ((((value1_ + value2_) + value4_) + value5_) + value6_));\n    textureStore(image_dst, u32(itc.x), ((((value1u + value2u) + value4u) + value5u) + value6u));\n    return;\n}\n\n@compute @workgroup_size(16, 1, 1) \nfn depth_load(@builtin(local_invocation_id) local_id_1: vec3<u32>) {\n    let dim_1 = textureDimensions(image_storage_src);\n    let itc_1 = (vec2<i32>((dim_1 * local_id_1.xy)) % vec2<i32>(10i, 20i));\n    let val = textureLoad(image_depth_multisampled_src, itc_1, i32(local_id_1.z));\n    textureStore(image_dst, itc_1.x, vec4(u32(val)));\n    return;\n}\n\n@vertex \nfn queries() -> @builtin(position) vec4<f32> {\n    let dim_1d = textureDimensions(image_1d);\n    let dim_1d_lod = textureDimensions(image_1d, i32(dim_1d));\n    let dim_2d = textureDimensions(image_2d);\n    let dim_2d_lod = textureDimensions(image_2d, 1i);\n    let dim_2d_array = textureDimensions(image_2d_array);\n    let dim_2d_array_lod = textureDimensions(image_2d_array, 1i);\n    let dim_cube = textureDimensions(image_cube);\n    let dim_cube_lod = textureDimensions(image_cube, 1i);\n    let dim_cube_array = textureDimensions(image_cube_array);\n    let dim_cube_array_lod = textureDimensions(image_cube_array, 1i);\n    let dim_3d = textureDimensions(image_3d);\n    let dim_3d_lod = textureDimensions(image_3d, 1i);\n    let dim_2s_ms = textureDimensions(image_aa);\n    let sum = ((((((((((dim_1d + dim_2d.y) + dim_2d_lod.y) + dim_2d_array.y) + dim_2d_array_lod.y) + dim_cube.y) + dim_cube_lod.y) + dim_cube_array.y) + dim_cube_array_lod.y) + dim_3d.z) + dim_3d_lod.z);\n    return vec4(f32(sum));\n}\n\n@vertex \nfn levels_queries() -> @builtin(position) vec4<f32> {\n    let num_levels_2d = textureNumLevels(image_2d);\n    let num_layers_2d = textureNumLayers(image_2d_array);\n    let num_levels_2d_array = textureNumLevels(image_2d_array);\n    let num_layers_2d_array = textureNumLayers(image_2d_array);\n    let num_levels_cube = textureNumLevels(image_cube);\n    let num_levels_cube_array = textureNumLevels(image_cube_array);\n    let num_layers_cube = textureNumLayers(image_cube_array);\n    let num_levels_3d = textureNumLevels(image_3d);\n    let num_samples_aa = textureNumSamples(image_aa);\n    let sum_1 = (((((((num_layers_2d + num_layers_cube) + num_samples_aa) + num_levels_2d) + num_levels_2d_array) + num_levels_3d) + num_levels_cube) + num_levels_cube_array);\n    return vec4(f32(sum_1));\n}\n\n@fragment \nfn texture_sample() -> @location(0) vec4<f32> {\n    var a: vec4<f32>;\n\n    let _e1 = vec2(0.5f);\n    let _e3 = vec3(0.5f);\n    let _e6 = vec2<i32>(3i, 1i);\n    let _e9 = a;\n    let _e12 = textureSample(image_1d, sampler_reg, 0.5f);\n    a = (_e9 + _e12);\n    let _e14 = a;\n    let _e17 = textureSample(image_2d, sampler_reg, _e1);\n    a = (_e14 + _e17);\n    let _e19 = a;\n    let _e25 = textureSample(image_2d, sampler_reg, _e1, vec2<i32>(3i, 1i));\n    a = (_e19 + _e25);\n    let _e27 = a;\n    let _e30 = textureSampleLevel(image_2d, sampler_reg, _e1, 2.3f);\n    a = (_e27 + _e30);\n    let _e32 = a;\n    let _e35 = textureSampleLevel(image_2d, sampler_reg, _e1, 2.3f, vec2<i32>(3i, 1i));\n    a = (_e32 + _e35);\n    let _e37 = a;\n    let _e41 = textureSampleBias(image_2d, sampler_reg, _e1, 2f, vec2<i32>(3i, 1i));\n    a = (_e37 + _e41);\n    let _e43 = a;\n    let _e46 = textureSampleBaseClampToEdge(image_2d, sampler_reg, _e1);\n    a = (_e43 + _e46);\n    let _e48 = a;\n    let _e52 = textureSample(image_2d_array, sampler_reg, _e1, 0u);\n    a = (_e48 + _e52);\n    let _e54 = a;\n    let _e58 = textureSample(image_2d_array, sampler_reg, _e1, 0u, vec2<i32>(3i, 1i));\n    a = (_e54 + _e58);\n    let _e60 = a;\n    let _e64 = textureSampleLevel(image_2d_array, sampler_reg, _e1, 0u, 2.3f);\n    a = (_e60 + _e64);\n    let _e66 = a;\n    let _e70 = textureSampleLevel(image_2d_array, sampler_reg, _e1, 0u, 2.3f, vec2<i32>(3i, 1i));\n    a = (_e66 + _e70);\n    let _e72 = a;\n    let _e77 = textureSampleBias(image_2d_array, sampler_reg, _e1, 0u, 2f, vec2<i32>(3i, 1i));\n    a = (_e72 + _e77);\n    let _e79 = a;\n    let _e83 = textureSample(image_2d_array, sampler_reg, _e1, 0i);\n    a = (_e79 + _e83);\n    let _e85 = a;\n    let _e89 = textureSample(image_2d_array, sampler_reg, _e1, 0i, vec2<i32>(3i, 1i));\n    a = (_e85 + _e89);\n    let _e91 = a;\n    let _e95 = textureSampleLevel(image_2d_array, sampler_reg, _e1, 0i, 2.3f);\n    a = (_e91 + _e95);\n    let _e97 = a;\n    let _e101 = textureSampleLevel(image_2d_array, sampler_reg, _e1, 0i, 2.3f, vec2<i32>(3i, 1i));\n    a = (_e97 + _e101);\n    let _e103 = a;\n    let _e108 = textureSampleBias(image_2d_array, sampler_reg, _e1, 0i, 2f, vec2<i32>(3i, 1i));\n    a = (_e103 + _e108);\n    let _e110 = a;\n    let _e114 = textureSample(image_cube_array, sampler_reg, _e3, 0u);\n    a = (_e110 + _e114);\n    let _e116 = a;\n    let _e120 = textureSampleLevel(image_cube_array, sampler_reg, _e3, 0u, 2.3f);\n    a = (_e116 + _e120);\n    let _e122 = a;\n    let _e127 = textureSampleBias(image_cube_array, sampler_reg, _e3, 0u, 2f);\n    a = (_e122 + _e127);\n    let _e129 = a;\n    let _e133 = textureSample(image_cube_array, sampler_reg, _e3, 0i);\n    a = (_e129 + _e133);\n    let _e135 = a;\n    let _e139 = textureSampleLevel(image_cube_array, sampler_reg, _e3, 0i, 2.3f);\n    a = (_e135 + _e139);\n    let _e141 = a;\n    let _e146 = textureSampleBias(image_cube_array, sampler_reg, _e3, 0i, 2f);\n    a = (_e141 + _e146);\n    let _e148 = a;\n    return _e148;\n}\n\n@fragment \nfn texture_sample_comparison() -> @location(0) f32 {\n    var a_1: f32;\n\n    let tc = vec2(0.5f);\n    let tc3_ = vec3(0.5f);\n    let _e6 = a_1;\n    let _e9 = textureSampleCompare(image_2d_depth, sampler_cmp, tc, 0.5f);\n    a_1 = (_e6 + _e9);\n    let _e11 = a_1;\n    let _e15 = textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0u, 0.5f);\n    a_1 = (_e11 + _e15);\n    let _e17 = a_1;\n    let _e21 = textureSampleCompare(image_2d_array_depth, sampler_cmp, tc, 0i, 0.5f);\n    a_1 = (_e17 + _e21);\n    let _e23 = a_1;\n    let _e26 = textureSampleCompare(image_cube_depth, sampler_cmp, tc3_, 0.5f);\n    a_1 = (_e23 + _e26);\n    let _e28 = a_1;\n    let _e31 = textureSampleCompareLevel(image_2d_depth, sampler_cmp, tc, 0.5f);\n    a_1 = (_e28 + _e31);\n    let _e33 = a_1;\n    let _e37 = textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0u, 0.5f);\n    a_1 = (_e33 + _e37);\n    let _e39 = a_1;\n    let _e43 = textureSampleCompareLevel(image_2d_array_depth, sampler_cmp, tc, 0i, 0.5f);\n    a_1 = (_e39 + _e43);\n    let _e45 = a_1;\n    let _e48 = textureSampleCompareLevel(image_cube_depth, sampler_cmp, tc3_, 0.5f);\n    a_1 = (_e45 + _e48);\n    let _e50 = a_1;\n    return _e50;\n}\n\n@fragment \nfn gather() -> @location(0) vec4<f32> {\n    let tc_1 = vec2(0.5f);\n    let s2d = textureGather(1, image_2d, sampler_reg, tc_1);\n    let s2d_offset = textureGather(3, image_2d, sampler_reg, tc_1, vec2<i32>(3i, 1i));\n    let s2d_depth = textureGatherCompare(image_2d_depth, sampler_cmp, tc_1, 0.5f);\n    let s2d_depth_offset = textureGatherCompare(image_2d_depth, sampler_cmp, tc_1, 0.5f, vec2<i32>(3i, 1i));\n    let u = textureGather(0, image_2d_u32_, sampler_reg, tc_1);\n    let i = textureGather(0, image_2d_i32_, sampler_reg, tc_1);\n    let f = (vec4<f32>(u) + vec4<f32>(i));\n    return ((((s2d + s2d_offset) + s2d_depth) + s2d_depth_offset) + f);\n}\n\n@fragment \nfn depth_no_comparison() -> @location(0) vec4<f32> {\n    let tc_2 = vec2(0.5f);\n    let s2d_1 = textureSample(image_2d_depth, sampler_reg, tc_2);\n    let s2d_gather = textureGather(image_2d_depth, sampler_reg, tc_2);\n    let s2d_level = textureSampleLevel(image_2d_depth, sampler_reg, tc_2, 1i);\n    return ((vec4(s2d_1) + s2d_gather) + vec4(s2d_level));\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-int64.wgsl",
    "content": "struct UniformCompatible {\n    val_u32_: u32,\n    val_i32_: i32,\n    val_f32_: f32,\n    val_u64_: u64,\n    val_u64_2_: vec2<u64>,\n    val_u64_3_: vec3<u64>,\n    val_u64_4_: vec4<u64>,\n    val_i64_: i64,\n    val_i64_2_: vec2<i64>,\n    val_i64_3_: vec3<i64>,\n    val_i64_4_: vec4<i64>,\n    final_value: u64,\n}\n\nstruct StorageCompatible {\n    val_u64_array_2_: array<u64, 2>,\n    val_i64_array_2_: array<i64, 2>,\n}\n\nconst constant_variable: u64 = 20lu;\n\nvar<private> private_variable: i64 = 1li;\n@group(0) @binding(0) \nvar<uniform> input_uniform: UniformCompatible;\n@group(0) @binding(1) \nvar<storage> input_storage: UniformCompatible;\n@group(0) @binding(2) \nvar<storage> input_arrays: StorageCompatible;\n@group(0) @binding(3) \nvar<storage, read_write> output: UniformCompatible;\n@group(0) @binding(4) \nvar<storage, read_write> output_arrays: StorageCompatible;\n\nfn int64_function(x: i64) -> i64 {\n    var val: i64 = 20li;\n\n    let phony = private_variable;\n    let _e5 = val;\n    val = (_e5 + ((31li - 1002003004005006li) + -9223372036854775807li));\n    let _e12 = val;\n    let _e13 = val;\n    val = (_e12 + (_e13 + 5li));\n    let _e17 = val;\n    let _e20 = input_uniform.val_u32_;\n    let _e21 = val;\n    val = (_e17 + i64((_e20 + u32(_e21))));\n    let _e26 = val;\n    let _e29 = input_uniform.val_i32_;\n    let _e30 = val;\n    val = (_e26 + i64((_e29 + i32(_e30))));\n    let _e35 = val;\n    let _e38 = input_uniform.val_f32_;\n    let _e39 = val;\n    val = (_e35 + i64((_e38 + f32(_e39))));\n    let _e44 = val;\n    let _e47 = input_uniform.val_i64_;\n    val = (_e44 + vec3(_e47).z);\n    let _e51 = val;\n    let _e54 = input_uniform.val_u64_;\n    val = (_e51 + bitcast<i64>(_e54));\n    let _e57 = val;\n    let _e60 = input_uniform.val_u64_2_;\n    val = (_e57 + bitcast<vec2<i64>>(_e60).y);\n    let _e64 = val;\n    let _e67 = input_uniform.val_u64_3_;\n    val = (_e64 + bitcast<vec3<i64>>(_e67).z);\n    let _e71 = val;\n    let _e74 = input_uniform.val_u64_4_;\n    val = (_e71 + bitcast<vec4<i64>>(_e74).w);\n    let _e78 = val;\n    val = (_e78 + i64(-9223372036854775807 - 1));\n    let _e85 = input_uniform.val_i64_;\n    let _e88 = input_storage.val_i64_;\n    output.val_i64_ = (_e85 + _e88);\n    let _e94 = input_uniform.val_i64_2_;\n    let _e97 = input_storage.val_i64_2_;\n    output.val_i64_2_ = (_e94 + _e97);\n    let _e103 = input_uniform.val_i64_3_;\n    let _e106 = input_storage.val_i64_3_;\n    output.val_i64_3_ = (_e103 + _e106);\n    let _e112 = input_uniform.val_i64_4_;\n    let _e115 = input_storage.val_i64_4_;\n    output.val_i64_4_ = (_e112 + _e115);\n    let _e121 = input_arrays.val_i64_array_2_;\n    output_arrays.val_i64_array_2_ = _e121;\n    let _e122 = val;\n    let _e123 = val;\n    val = (_e122 + abs(_e123));\n    let _e126 = val;\n    let _e127 = val;\n    let _e128 = val;\n    let _e129 = val;\n    val = (_e126 + clamp(_e127, _e128, _e129));\n    let _e132 = val;\n    let _e133 = val;\n    let _e135 = val;\n    val = (_e132 + dot(vec2(_e133), vec2(_e135)));\n    let _e139 = val;\n    let _e140 = val;\n    let _e141 = val;\n    val = (_e139 + max(_e140, _e141));\n    let _e144 = val;\n    let _e145 = val;\n    let _e146 = val;\n    val = (_e144 + min(_e145, _e146));\n    let _e149 = val;\n    let _e150 = val;\n    val = (_e149 + sign(_e150));\n    let _e153 = val;\n    return _e153;\n}\n\nfn uint64_function(x_1: u64) -> u64 {\n    var val_1: u64 = 20lu;\n\n    let _e3 = val_1;\n    val_1 = (_e3 + ((31lu + 18446744073709551615lu) - 18446744073709551615lu));\n    let _e10 = val_1;\n    let _e11 = val_1;\n    val_1 = (_e10 + (_e11 + 5lu));\n    let _e15 = val_1;\n    let _e18 = input_uniform.val_u32_;\n    let _e19 = val_1;\n    val_1 = (_e15 + u64((_e18 + u32(_e19))));\n    let _e24 = val_1;\n    let _e27 = input_uniform.val_i32_;\n    let _e28 = val_1;\n    val_1 = (_e24 + u64((_e27 + i32(_e28))));\n    let _e33 = val_1;\n    let _e36 = input_uniform.val_f32_;\n    let _e37 = val_1;\n    val_1 = (_e33 + u64((_e36 + f32(_e37))));\n    let _e42 = val_1;\n    let _e45 = input_uniform.val_u64_;\n    val_1 = (_e42 + vec3(_e45).z);\n    let _e49 = val_1;\n    let _e52 = input_uniform.val_i64_;\n    val_1 = (_e49 + bitcast<u64>(_e52));\n    let _e55 = val_1;\n    let _e58 = input_uniform.val_i64_2_;\n    val_1 = (_e55 + bitcast<vec2<u64>>(_e58).y);\n    let _e62 = val_1;\n    let _e65 = input_uniform.val_i64_3_;\n    val_1 = (_e62 + bitcast<vec3<u64>>(_e65).z);\n    let _e69 = val_1;\n    let _e72 = input_uniform.val_i64_4_;\n    val_1 = (_e69 + bitcast<vec4<u64>>(_e72).w);\n    let _e80 = input_uniform.val_u64_;\n    let _e83 = input_storage.val_u64_;\n    output.val_u64_ = (_e80 + _e83);\n    let _e89 = input_uniform.val_u64_2_;\n    let _e92 = input_storage.val_u64_2_;\n    output.val_u64_2_ = (_e89 + _e92);\n    let _e98 = input_uniform.val_u64_3_;\n    let _e101 = input_storage.val_u64_3_;\n    output.val_u64_3_ = (_e98 + _e101);\n    let _e107 = input_uniform.val_u64_4_;\n    let _e110 = input_storage.val_u64_4_;\n    output.val_u64_4_ = (_e107 + _e110);\n    let _e116 = input_arrays.val_u64_array_2_;\n    output_arrays.val_u64_array_2_ = _e116;\n    let _e117 = val_1;\n    let _e118 = val_1;\n    val_1 = (_e117 + abs(_e118));\n    let _e121 = val_1;\n    let _e122 = val_1;\n    let _e123 = val_1;\n    let _e124 = val_1;\n    val_1 = (_e121 + clamp(_e122, _e123, _e124));\n    let _e127 = val_1;\n    let _e128 = val_1;\n    let _e130 = val_1;\n    val_1 = (_e127 + dot(vec2(_e128), vec2(_e130)));\n    let _e134 = val_1;\n    let _e135 = val_1;\n    let _e136 = val_1;\n    val_1 = (_e134 + max(_e135, _e136));\n    let _e139 = val_1;\n    let _e140 = val_1;\n    let _e141 = val_1;\n    val_1 = (_e139 + min(_e140, _e141));\n    let _e144 = val_1;\n    return _e144;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e3 = uint64_function(67lu);\n    let _e5 = int64_function(60li);\n    output.final_value = (_e3 + bitcast<u64>(_e5));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-interface.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) @invariant position: vec4<f32>,\n    @location(1) _varying: f32,\n}\n\nstruct FragmentOutput {\n    @builtin(frag_depth) depth: f32,\n    @builtin(sample_mask) sample_mask: u32,\n    @location(0) color: f32,\n}\n\nstruct Input1_ {\n    @builtin(vertex_index) index: u32,\n}\n\nstruct Input2_ {\n    @builtin(instance_index) index: u32,\n}\n\nvar<workgroup> output: array<u32, 1>;\n\n@vertex \nfn vertex(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32, @location(10) @interpolate(flat) color: u32) -> VertexOutput {\n    let tmp: u32 = ((vertex_index + instance_index) + color);\n    return VertexOutput(vec4(1f), f32(tmp));\n}\n\n@fragment \nfn fragment(in: VertexOutput, @builtin(front_facing) front_facing: bool, @builtin(sample_index) sample_index: u32, @builtin(sample_mask) sample_mask: u32) -> FragmentOutput {\n    let mask: u32 = (sample_mask & (1u << sample_index));\n    let color_1: f32 = select(0f, 1f, front_facing);\n    return FragmentOutput(in._varying, mask, color_1);\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn compute(@builtin(global_invocation_id) global_id: vec3<u32>, @builtin(local_invocation_id) local_id: vec3<u32>, @builtin(local_invocation_index) local_index: u32, @builtin(workgroup_id) wg_id: vec3<u32>, @builtin(num_workgroups) num_wgs: vec3<u32>) {\n    output[0] = ((((global_id.x + local_id.x) + local_index) + wg_id.x) + num_wgs.x);\n    return;\n}\n\n@vertex \nfn vertex_two_structs(in1_: Input1_, in2_: Input2_) -> @builtin(position) @invariant vec4<f32> {\n    var index: u32 = 2u;\n\n    let _e8: u32 = index;\n    return vec4<f32>(f32(in1_.index), f32(in2_.index), f32(_e8), 0f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-interpolate.wgsl",
    "content": "struct FragmentInput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) @interpolate(flat) _flat: u32,\n    @location(1) @interpolate(flat, first) flat_first: u32,\n    @location(2) @interpolate(flat, either) flat_either: u32,\n    @location(3) @interpolate(linear) _linear: f32,\n    @location(4) @interpolate(linear, centroid) linear_centroid: vec2<f32>,\n    @location(6) @interpolate(linear, sample) linear_sample: vec3<f32>,\n    @location(7) @interpolate(linear) linear_center: vec3<f32>,\n    @location(8) perspective: vec4<f32>,\n    @location(9) @interpolate(perspective, centroid) perspective_centroid: f32,\n    @location(10) @interpolate(perspective, sample) perspective_sample: f32,\n    @location(11) perspective_center: f32,\n}\n\n@vertex \nfn vert_main() -> FragmentInput {\n    var out: FragmentInput;\n\n    out.position = vec4<f32>(2f, 4f, 5f, 6f);\n    out._flat = 8u;\n    out.flat_first = 9u;\n    out.flat_either = 10u;\n    out._linear = 27f;\n    out.linear_centroid = vec2<f32>(64f, 125f);\n    out.linear_sample = vec3<f32>(216f, 343f, 512f);\n    out.linear_center = vec3<f32>(255f, 511f, 1024f);\n    out.perspective = vec4<f32>(729f, 1000f, 1331f, 1728f);\n    out.perspective_centroid = 2197f;\n    out.perspective_sample = 2744f;\n    out.perspective_center = 2812f;\n    let _e41 = out;\n    return _e41;\n}\n\n@fragment \nfn frag_main(val: FragmentInput) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-interpolate_compat.wgsl",
    "content": "struct FragmentInput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) @interpolate(flat) _flat: u32,\n    @location(2) @interpolate(flat, either) flat_either: u32,\n    @location(3) @interpolate(linear) _linear: f32,\n    @location(4) @interpolate(linear, centroid) linear_centroid: vec2<f32>,\n    @location(6) @interpolate(linear, sample) linear_sample: vec3<f32>,\n    @location(7) @interpolate(linear) linear_center: vec3<f32>,\n    @location(8) perspective: vec4<f32>,\n    @location(9) @interpolate(perspective, centroid) perspective_centroid: f32,\n    @location(10) @interpolate(perspective, sample) perspective_sample: f32,\n    @location(11) perspective_center: f32,\n}\n\n@vertex \nfn vert_main() -> FragmentInput {\n    var out: FragmentInput;\n\n    out.position = vec4<f32>(2f, 4f, 5f, 6f);\n    out._flat = 8u;\n    out.flat_either = 10u;\n    out._linear = 27f;\n    out.linear_centroid = vec2<f32>(64f, 125f);\n    out.linear_sample = vec3<f32>(216f, 343f, 512f);\n    out.linear_center = vec3<f32>(255f, 511f, 1024f);\n    out.perspective = vec4<f32>(729f, 1000f, 1331f, 1728f);\n    out.perspective_centroid = 2197f;\n    out.perspective_sample = 2744f;\n    out.perspective_center = 2812f;\n    let _e39 = out;\n    return _e39;\n}\n\n@fragment \nfn frag_main(val: FragmentInput) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-lexical-scopes.wgsl",
    "content": "fn blockLexicalScope(a: bool) {\n    {\n        {\n            return;\n        }\n    }\n}\n\nfn ifLexicalScope(a_1: bool) {\n    if a_1 {\n        return;\n    } else {\n        return;\n    }\n}\n\nfn loopLexicalScope(a_2: bool) {\n    loop {\n    }\n    return;\n}\n\nfn forLexicalScope(a_3: f32) {\n    var a_4: i32 = 0i;\n\n    loop {\n        let _e3 = a_4;\n        if (_e3 < 1i) {\n        } else {\n            break;\n        }\n        {\n        }\n        continuing {\n            let _e8 = a_4;\n            a_4 = (_e8 + 1i);\n        }\n    }\n    return;\n}\n\nfn whileLexicalScope(a_5: i32) {\n    loop {\n        if (a_5 > 2i) {\n        } else {\n            break;\n        }\n        {\n        }\n    }\n    return;\n}\n\nfn switchLexicalScope(a_6: i32) {\n    switch a_6 {\n        case 0: {\n        }\n        case 1: {\n        }\n        default: {\n        }\n    }\n    let test = (a_6 == 2i);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    blockLexicalScope(false);\n    ifLexicalScope(true);\n    loopLexicalScope(false);\n    forLexicalScope(1f);\n    whileLexicalScope(1i);\n    switchLexicalScope(1i);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-local-const.wgsl",
    "content": "const gb: i32 = 4i;\nconst gc: u32 = 4u;\nconst gd: f32 = 4f;\n\nfn const_in_fn() {\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    const_in_fn();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-math-functions.wgsl",
    "content": "@fragment \nfn main() {\n    let v = vec4(0f);\n    let a = degrees(1f);\n    let b = radians(1f);\n    let c = degrees(v);\n    let d = radians(v);\n    let e = saturate(v);\n    let g = refract(v, v, 1f);\n    let sign_b = vec4<i32>(-1i, -1i, -1i, -1i);\n    let sign_d = vec4<f32>(-1f, -1f, -1f, -1f);\n    let sign_e = vec4<f32>(0f, 0f, 0f, 0f);\n    let flb_b = vec2<i32>(-1i, -1i);\n    let flb_c = vec2<u32>(0u, 0u);\n    let ftb_c = vec2<i32>(0i, 0i);\n    let ftb_d = vec2<u32>(0u, 0u);\n    let ctz_e = vec2<u32>(32u, 32u);\n    let ctz_f = vec2<i32>(32i, 32i);\n    let ctz_g = vec2<u32>(0u, 0u);\n    let ctz_h = vec2<i32>(0i, 0i);\n    let clz_c = vec2<i32>(0i, 0i);\n    let clz_d = vec2<u32>(31u, 31u);\n    let lde_a = ldexp(1f, 2i);\n    let lde_b = ldexp(vec2<f32>(1f, 2f), vec2<i32>(3i, 4i));\n    let modf_a = modf(1.5f);\n    let modf_b = modf(1.5f).fract;\n    let modf_c = modf(1.5f).whole;\n    let modf_d = modf(vec2<f32>(1.5f, 1.5f));\n    let modf_e = modf(vec4<f32>(1.5f, 1.5f, 1.5f, 1.5f)).whole.x;\n    let modf_f = modf(vec2<f32>(1.5f, 1.5f)).fract.y;\n    let frexp_a = frexp(1.5f);\n    let frexp_b = frexp(1.5f).fract;\n    let frexp_c = frexp(1.5f).exp;\n    let frexp_d = frexp(vec4<f32>(1.5f, 1.5f, 1.5f, 1.5f)).exp.x;\n    let quantizeToF16_a = quantizeToF16(1f);\n    let quantizeToF16_b = quantizeToF16(vec2<f32>(1f, 1f));\n    let quantizeToF16_c = quantizeToF16(vec3<f32>(1f, 1f, 1f));\n    let quantizeToF16_d = quantizeToF16(vec4<f32>(1f, 1f, 1f, 1f));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-memory-decorations-coherent.wgsl",
    "content": "struct Data {\n    values: array<u32>,\n}\n\n@group(0) @binding(0) \n@coherent var<storage, read_write> coherent_buf: Data;\n@group(0) @binding(1) \nvar<storage, read_write> plain_buf: Data;\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e6 = plain_buf.values[0];\n    coherent_buf.values[0] = _e6;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-memory-decorations.wgsl",
    "content": "struct Data {\n    values: array<u32>,\n}\n\n@group(0) @binding(0) \n@coherent var<storage, read_write> coherent_buf: Data;\n@group(0) @binding(1) \n@volatile var<storage, read_write> volatile_buf: Data;\n@group(0) @binding(2) \n@coherent @volatile var<storage, read_write> both_buf: Data;\n@group(0) @binding(3) \nvar<storage, read_write> plain_buf: Data;\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e6 = volatile_buf.values[0];\n    coherent_buf.values[0] = _e6;\n    let _e13 = plain_buf.values[0];\n    both_buf.values[0] = _e13;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-mesh-shader-empty.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\n\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> mesh_output: MeshOutput;\n\n@task @payload(taskPayload) @workgroup_size(64, 1, 1) \nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3<u32>(1u, 1u, 1u);\n}\n\n@mesh(mesh_output) @workgroup_size(64, 1, 1) @payload(taskPayload) \nfn ms_main() {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-mesh-shader-lines.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\n\nstruct PrimitiveOutput {\n    @builtin(line_indices) indices: vec2<u32>,\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 2>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> mesh_output: MeshOutput;\n\n@task @payload(taskPayload) @workgroup_size(64, 1, 1) \nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3<u32>(1u, 1u, 1u);\n}\n\n@mesh(mesh_output) @workgroup_size(64, 1, 1) @payload(taskPayload) \nfn ms_main() {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-mesh-shader-points.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    dummy: u32,\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\n\nstruct PrimitiveOutput {\n    @builtin(point_index) indices: u32,\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 1>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> mesh_output: MeshOutput;\n\n@task @payload(taskPayload) @workgroup_size(64, 1, 1) \nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3<u32>(1u, 1u, 1u);\n}\n\n@mesh(mesh_output) @workgroup_size(64, 1, 1) @payload(taskPayload) \nfn ms_main() {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-mesh-shader.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nstruct TaskPayload {\n    colorMask: vec4<f32>,\n    visible: bool,\n}\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) color: vec4<f32>,\n}\n\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n    @builtin(cull_primitive) cull: bool,\n    @location(1) @per_primitive colorMask: vec4<f32>,\n}\n\nstruct PrimitiveInput {\n    @location(1) @per_primitive colorMask: vec4<f32>,\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> workgroupData: f32;\nvar<workgroup> mesh_output: MeshOutput;\n\nfn helper_reader() -> bool {\n    let _e2 = taskPayload.visible;\n    return _e2;\n}\n\nfn helper_writer(value: bool) {\n    taskPayload.visible = value;\n    return;\n}\n\n@task @payload(taskPayload) @workgroup_size(1, 1, 1) \nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    workgroupData = 1f;\n    taskPayload.colorMask = vec4<f32>(1f, 1f, 0f, 1f);\n    helper_writer(true);\n    let _e12 = helper_reader();\n    taskPayload.visible = _e12;\n    return vec3<u32>(1u, 1u, 1u);\n}\n\n@task @payload(taskPayload) @workgroup_size(2, 1, 1) \nfn ts_divergent(@builtin(local_invocation_id) thread_id: vec3<u32>) -> @builtin(mesh_task_size) vec3<u32> {\n    if (thread_id.x == 0u) {\n        taskPayload.colorMask = vec4<f32>(1f, 1f, 0f, 1f);\n        taskPayload.visible = true;\n        return vec3<u32>(1u, 1u, 1u);\n    }\n    return vec3<u32>(2u, 2u, 2u);\n}\n\n@mesh(mesh_output) @workgroup_size(1, 1, 1) @payload(taskPayload) \nfn ms_main() {\n    mesh_output.vertex_count = 3u;\n    mesh_output.primitive_count = 1u;\n    workgroupData = 2f;\n    mesh_output.vertices[0].position = vec4<f32>(0f, 1f, 0f, 1f);\n    let _e23 = taskPayload.colorMask;\n    mesh_output.vertices[0].color = (vec4<f32>(0f, 1f, 0f, 1f) * _e23);\n    mesh_output.vertices[1].position = vec4<f32>(-1f, -1f, 0f, 1f);\n    let _e45 = taskPayload.colorMask;\n    mesh_output.vertices[1].color = (vec4<f32>(0f, 0f, 1f, 1f) * _e45);\n    mesh_output.vertices[2].position = vec4<f32>(1f, -1f, 0f, 1f);\n    let _e67 = taskPayload.colorMask;\n    mesh_output.vertices[2].color = (vec4<f32>(1f, 0f, 0f, 1f) * _e67);\n    mesh_output.primitives[0].indices = vec3<u32>(0u, 1u, 2u);\n    let _e86 = helper_reader();\n    mesh_output.primitives[0].cull = !(_e86);\n    mesh_output.primitives[0].colorMask = vec4<f32>(1f, 0f, 1f, 1f);\n    return;\n}\n\n@mesh(mesh_output) @workgroup_size(1, 1, 1) \nfn ms_no_ts() {\n    mesh_output.vertex_count = 3u;\n    mesh_output.primitive_count = 1u;\n    workgroupData = 2f;\n    mesh_output.vertices[0].position = vec4<f32>(0f, 1f, 0f, 1f);\n    mesh_output.vertices[0].color = vec4<f32>(0f, 1f, 0f, 1f);\n    mesh_output.vertices[1].position = vec4<f32>(-1f, -1f, 0f, 1f);\n    mesh_output.vertices[1].color = vec4<f32>(0f, 0f, 1f, 1f);\n    mesh_output.vertices[2].position = vec4<f32>(1f, -1f, 0f, 1f);\n    mesh_output.vertices[2].color = vec4<f32>(1f, 0f, 0f, 1f);\n    mesh_output.primitives[0].indices = vec3<u32>(0u, 1u, 2u);\n    mesh_output.primitives[0].cull = false;\n    mesh_output.primitives[0].colorMask = vec4<f32>(1f, 0f, 1f, 1f);\n    return;\n}\n\n@mesh(mesh_output) @workgroup_size(2, 1, 1) \nfn ms_divergent(@builtin(local_invocation_id) thread_id_1: vec3<u32>) {\n    if (thread_id_1.x == 0u) {\n        mesh_output.vertex_count = 3u;\n        mesh_output.primitive_count = 1u;\n        workgroupData = 2f;\n        mesh_output.vertices[0].position = vec4<f32>(0f, 1f, 0f, 1f);\n        mesh_output.vertices[0].color = vec4<f32>(0f, 1f, 0f, 1f);\n        mesh_output.vertices[1].position = vec4<f32>(-1f, -1f, 0f, 1f);\n        mesh_output.vertices[1].color = vec4<f32>(0f, 0f, 1f, 1f);\n        mesh_output.vertices[2].position = vec4<f32>(1f, -1f, 0f, 1f);\n        mesh_output.vertices[2].color = vec4<f32>(1f, 0f, 0f, 1f);\n        mesh_output.primitives[0].indices = vec3<u32>(0u, 1u, 2u);\n        mesh_output.primitives[0].cull = false;\n        mesh_output.primitives[0].colorMask = vec4<f32>(1f, 0f, 1f, 1f);\n        return;\n    } else {\n        return;\n    }\n}\n\n@fragment \nfn fs_main(vertex: VertexOutput, primitive: PrimitiveInput) -> @location(0) vec4<f32> {\n    return (vertex.color * primitive.colorMask);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-module-scope.wgsl",
    "content": "struct S {\n    x: i32,\n}\n\nconst Value: i32 = 1i;\n\n@group(0) @binding(0) \nvar Texture: texture_2d<f32>;\n@group(0) @binding(1) \nvar Sampler: sampler;\n\nfn statement() {\n    return;\n}\n\nfn returns() -> S {\n    return S(1i);\n}\n\nfn call() {\n    statement();\n    let _e0 = returns();\n    let s = textureSample(Texture, Sampler, vec2(1f));\n    return;\n}\n\n@fragment \nfn main() {\n    call();\n    statement();\n    let _e0 = returns();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-multiview.wgsl",
    "content": "@fragment \nfn main(@builtin(view_index) view_index: u32) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-operators.wgsl",
    "content": "const v_f32_one: vec4<f32> = vec4<f32>(1f, 1f, 1f, 1f);\nconst v_f32_zero: vec4<f32> = vec4<f32>(0f, 0f, 0f, 0f);\nconst v_f32_half: vec4<f32> = vec4<f32>(0.5f, 0.5f, 0.5f, 0.5f);\nconst v_i32_one: vec4<i32> = vec4<i32>(1i, 1i, 1i, 1i);\nconst b_false: bool = false;\nconst b_true: bool = true;\nconst short_circuit_1_invalid_rhs: bool = false;\nconst short_circuit_2_invalid_rhs: bool = false;\nconst short_circuit_3_: bool = true;\nconst short_circuit_4_: bool = true;\n\nfn builtins() -> vec4<f32> {\n    let s1_ = select(0i, 1i, true);\n    let s2_ = select(v_f32_zero, v_f32_one, true);\n    let s3_ = vec4<f32>(1f, 1f, 1f, 1f);\n    let m1_ = mix(v_f32_zero, v_f32_one, v_f32_half);\n    let m2_ = mix(v_f32_zero, v_f32_one, 0.1f);\n    let b1_ = bitcast<f32>(1i);\n    let b2_ = bitcast<vec4<f32>>(v_i32_one);\n    let v_i32_zero = vec4<i32>(0i, 0i, 0i, 0i);\n    return (((((vec4<f32>((vec4(s1_) + v_i32_zero)) + s2_) + m1_) + m2_) + vec4(b1_)) + b2_);\n}\n\nfn splat(m: f32, n: i32) -> vec4<f32> {\n    let a_2 = (((vec2(2f) + vec2(m)) - vec2(4f)) / vec2(8f));\n    let b = (vec4(n) % vec4(2i));\n    return (a_2.xyxy + vec4<f32>(b));\n}\n\nfn splat_assignment() -> vec2<f32> {\n    var a: vec2<f32> = vec2(2f);\n\n    let _e3 = a;\n    a = (_e3 + vec2(1f));\n    let _e7 = a;\n    a = (_e7 - vec2(3f));\n    let _e11 = a;\n    a = (_e11 / vec2(4f));\n    let _e15 = a;\n    return _e15;\n}\n\nfn bool_cast(x: vec3<f32>) -> vec3<f32> {\n    let y = vec3<bool>(x);\n    return vec3<f32>(y);\n}\n\nfn p() -> bool {\n    return true;\n}\n\nfn q() -> bool {\n    return false;\n}\n\nfn r() -> bool {\n    return true;\n}\n\nfn s() -> bool {\n    return false;\n}\n\nfn logical() {\n    var local: bool;\n    var local_1: bool;\n    var local_2: bool;\n    var local_3: bool;\n    var local_4: bool;\n    var local_5: bool;\n    var local_6: bool;\n\n    let neg0_ = !(true);\n    let neg1_ = !(vec2(true));\n    if !(true) {\n        local = false;\n    } else {\n        local = true;\n    }\n    let or = local;\n    if true {\n        local_1 = false;\n    } else {\n        local_1 = false;\n    }\n    let and = local_1;\n    let bitwise_or0_ = (true | false);\n    let bitwise_or1_ = (vec3(true) | vec3(false));\n    let bitwise_and0_ = (true & false);\n    let bitwise_and1_ = (vec4(true) & vec4(false));\n    if !(false) {\n        local_2 = false;\n    } else {\n        local_2 = true;\n    }\n    let _e27 = local_2;\n    let short_circuit_5_ = !(_e27);\n    let _e29 = p();\n    if !(_e29) {\n        let _e33 = q();\n        local_3 = _e33;\n    } else {\n        local_3 = true;\n    }\n    let _e35 = local_3;\n    if _e35 {\n        let _e38 = r();\n        if !(_e38) {\n            let _e42 = s();\n            local_5 = _e42;\n        } else {\n            local_5 = true;\n        }\n        let _e44 = local_5;\n        local_4 = _e44;\n    } else {\n        local_4 = false;\n    }\n    let short_circuit_6_ = local_4;\n    if false {\n        let _e50 = q();\n        local_6 = _e50;\n    } else {\n        local_6 = true;\n    }\n    let short_circuit_7_ = local_6;\n    return;\n}\n\nfn arithmetic() {\n    var prevent_const_eval: i32;\n    var wgpu_7437_: i32;\n\n    let neg0_1 = -(1f);\n    let neg1_1 = -(vec2(1i));\n    let neg2_ = -(vec2(1f));\n    let add0_ = (2i + 1i);\n    let add1_ = (2u + 1u);\n    let add2_ = (2f + 1f);\n    let add3_ = (vec2(2i) + vec2(1i));\n    let add4_ = (vec3(2u) + vec3(1u));\n    let add5_ = (vec4(2f) + vec4(1f));\n    let sub0_ = (2i - 1i);\n    let sub1_ = (2u - 1u);\n    let sub2_ = (2f - 1f);\n    let sub3_ = (vec2(2i) - vec2(1i));\n    let sub4_ = (vec3(2u) - vec3(1u));\n    let sub5_ = (vec4(2f) - vec4(1f));\n    let mul0_ = (2i * 1i);\n    let mul1_ = (2u * 1u);\n    let mul2_ = (2f * 1f);\n    let mul3_ = (vec2(2i) * vec2(1i));\n    let mul4_ = (vec3(2u) * vec3(1u));\n    let mul5_ = (vec4(2f) * vec4(1f));\n    let div0_ = (2i / 1i);\n    let div1_ = (2u / 1u);\n    let div2_ = (2f / 1f);\n    let div3_ = (vec2(2i) / vec2(1i));\n    let div4_ = (vec3(2u) / vec3(1u));\n    let div5_ = (vec4(2f) / vec4(1f));\n    let rem0_ = (2i % 1i);\n    let rem1_ = (2u % 1u);\n    let rem2_ = (2f % 1f);\n    let rem3_ = (vec2(2i) % vec2(1i));\n    let rem4_ = (vec3(2u) % vec3(1u));\n    let rem5_ = (vec4(2f) % vec4(1f));\n    {\n        let add0_1 = (vec2(2i) + vec2(1i));\n        let add1_1 = (vec2(2i) + vec2(1i));\n        let add2_1 = (vec2(2u) + vec2(1u));\n        let add3_1 = (vec2(2u) + vec2(1u));\n        let add4_1 = (vec2(2f) + vec2(1f));\n        let add5_1 = (vec2(2f) + vec2(1f));\n        let sub0_1 = (vec2(2i) - vec2(1i));\n        let sub1_1 = (vec2(2i) - vec2(1i));\n        let sub2_1 = (vec2(2u) - vec2(1u));\n        let sub3_1 = (vec2(2u) - vec2(1u));\n        let sub4_1 = (vec2(2f) - vec2(1f));\n        let sub5_1 = (vec2(2f) - vec2(1f));\n        let mul0_1 = (vec2(2i) * 1i);\n        let mul1_1 = (2i * vec2(1i));\n        let mul2_1 = (vec2(2u) * 1u);\n        let mul3_1 = (2u * vec2(1u));\n        let mul4_1 = (vec2(2f) * 1f);\n        let mul5_1 = (2f * vec2(1f));\n        let div0_1 = (vec2(2i) / vec2(1i));\n        let div1_1 = (vec2(2i) / vec2(1i));\n        let div2_1 = (vec2(2u) / vec2(1u));\n        let div3_1 = (vec2(2u) / vec2(1u));\n        let div4_1 = (vec2(2f) / vec2(1f));\n        let div5_1 = (vec2(2f) / vec2(1f));\n        let rem0_1 = (vec2(2i) % vec2(1i));\n        let rem1_1 = (vec2(2i) % vec2(1i));\n        let rem2_1 = (vec2(2u) % vec2(1u));\n        let rem3_1 = (vec2(2u) % vec2(1u));\n        let rem4_1 = (vec2(2f) % vec2(1f));\n        let rem5_1 = (vec2(2f) % vec2(1f));\n    }\n    let add = mat3x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f));\n    let sub = mat3x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f));\n    let mul_scalar0_ = (mat3x3<f32>() * 1f);\n    let mul_scalar1_ = (2f * mat3x3<f32>());\n    let mul_vector0_ = (mat4x3<f32>() * vec4(1f));\n    let mul_vector1_ = (vec3(2f) * mat4x3<f32>());\n    let mul = mat3x3<f32>(vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f), vec3<f32>(0f, 0f, 0f));\n    let _e205 = prevent_const_eval;\n    wgpu_7437_ = (_e205 + i32(-2147483648));\n    return;\n}\n\nfn bit() {\n    let flip0_ = ~(1i);\n    let flip1_ = ~(1u);\n    let flip2_ = ~(vec2(1i));\n    let flip3_ = ~(vec3(1u));\n    let or0_ = (2i | 1i);\n    let or1_ = (2u | 1u);\n    let or2_ = (vec2(2i) | vec2(1i));\n    let or3_ = (vec3(2u) | vec3(1u));\n    let and0_ = (2i & 1i);\n    let and1_ = (2u & 1u);\n    let and2_ = (vec2(2i) & vec2(1i));\n    let and3_ = (vec3(2u) & vec3(1u));\n    let xor0_ = (2i ^ 1i);\n    let xor1_ = (2u ^ 1u);\n    let xor2_ = (vec2(2i) ^ vec2(1i));\n    let xor3_ = (vec3(2u) ^ vec3(1u));\n    let shl0_ = (2i << 1u);\n    let shl1_ = (2u << 1u);\n    let shl2_ = (vec2(2i) << vec2(1u));\n    let shl3_ = (vec3(2u) << vec3(1u));\n    let shr0_ = (2i >> 1u);\n    let shr1_ = (2u >> 1u);\n    let shr2_ = (vec2(2i) >> vec2(1u));\n    let shr3_ = (vec3(2u) >> vec3(1u));\n    return;\n}\n\nfn comparison() {\n    let eq0_ = (2i == 1i);\n    let eq1_ = (2u == 1u);\n    let eq2_ = (2f == 1f);\n    let eq3_ = (vec2(2i) == vec2(1i));\n    let eq4_ = (vec3(2u) == vec3(1u));\n    let eq5_ = (vec4(2f) == vec4(1f));\n    let neq0_ = (2i != 1i);\n    let neq1_ = (2u != 1u);\n    let neq2_ = (2f != 1f);\n    let neq3_ = (vec2(2i) != vec2(1i));\n    let neq4_ = (vec3(2u) != vec3(1u));\n    let neq5_ = (vec4(2f) != vec4(1f));\n    let lt0_ = (2i < 1i);\n    let lt1_ = (2u < 1u);\n    let lt2_ = (2f < 1f);\n    let lt3_ = (vec2(2i) < vec2(1i));\n    let lt4_ = (vec3(2u) < vec3(1u));\n    let lt5_ = (vec4(2f) < vec4(1f));\n    let lte0_ = (2i <= 1i);\n    let lte1_ = (2u <= 1u);\n    let lte2_ = (2f <= 1f);\n    let lte3_ = (vec2(2i) <= vec2(1i));\n    let lte4_ = (vec3(2u) <= vec3(1u));\n    let lte5_ = (vec4(2f) <= vec4(1f));\n    let gt0_ = (2i > 1i);\n    let gt1_ = (2u > 1u);\n    let gt2_ = (2f > 1f);\n    let gt3_ = (vec2(2i) > vec2(1i));\n    let gt4_ = (vec3(2u) > vec3(1u));\n    let gt5_ = (vec4(2f) > vec4(1f));\n    let gte0_ = (2i >= 1i);\n    let gte1_ = (2u >= 1u);\n    let gte2_ = (2f >= 1f);\n    let gte3_ = (vec2(2i) >= vec2(1i));\n    let gte4_ = (vec3(2u) >= vec3(1u));\n    let gte5_ = (vec4(2f) >= vec4(1f));\n    return;\n}\n\nfn assignment() {\n    var a_1: i32;\n    var vec0_: vec3<i32> = vec3<i32>();\n\n    a_1 = 1i;\n    let _e5 = a_1;\n    a_1 = (_e5 + 1i);\n    let _e7 = a_1;\n    a_1 = (_e7 - 1i);\n    let _e9 = a_1;\n    let _e10 = a_1;\n    a_1 = (_e9 * _e10);\n    let _e12 = a_1;\n    let _e13 = a_1;\n    a_1 = (_e12 / _e13);\n    let _e15 = a_1;\n    a_1 = (_e15 % 1i);\n    let _e17 = a_1;\n    a_1 = (_e17 & 0i);\n    let _e19 = a_1;\n    a_1 = (_e19 | 0i);\n    let _e21 = a_1;\n    a_1 = (_e21 ^ 0i);\n    let _e23 = a_1;\n    a_1 = (_e23 << 2u);\n    let _e25 = a_1;\n    a_1 = (_e25 >> 1u);\n    let _e28 = a_1;\n    a_1 = (_e28 + 1i);\n    let _e31 = a_1;\n    a_1 = (_e31 - 1i);\n    let _e37 = vec0_[1i];\n    vec0_[1i] = (_e37 + 1i);\n    let _e41 = vec0_[1i];\n    vec0_[1i] = (_e41 - 1i);\n    return;\n}\n\nfn negation_avoids_prefix_decrement() {\n    let i0_ = -(1i);\n    let i1_ = -(-(1i));\n    let i2_ = -(-(1i));\n    let i3_ = -(-(1i));\n    let i4_ = -(-(-(1i)));\n    let i5_ = -(-(-(-(1i))));\n    let i6_ = -(-(-(-(-(1i)))));\n    let i7_ = -(-(-(-(-(1i)))));\n    let f0_ = -(1f);\n    let f1_ = -(-(1f));\n    let f2_ = -(-(1f));\n    let f3_ = -(-(1f));\n    let f4_ = -(-(-(1f)));\n    let f5_ = -(-(-(-(1f))));\n    let f6_ = -(-(-(-(-(1f)))));\n    let f7_ = -(-(-(-(-(1f)))));\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(@builtin(workgroup_id) id: vec3<u32>) {\n    let _e1 = builtins();\n    let _e6 = splat(f32(id.x), i32(id.y));\n    let _e7 = splat_assignment();\n    let _e12 = bool_cast(vec3<f32>(1f, 1f, 1f));\n    logical();\n    arithmetic();\n    bit();\n    comparison();\n    assignment();\n    negation_avoids_prefix_decrement();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-padding.wgsl",
    "content": "struct S {\n    a: vec3<f32>,\n}\n\nstruct Test {\n    a: S,\n    b: f32,\n}\n\nstruct Test2_ {\n    a: array<vec3<f32>, 2>,\n    b: f32,\n}\n\nstruct Test3_ {\n    a: mat4x3<f32>,\n    b: f32,\n}\n\n@group(0) @binding(0) \nvar<uniform> input1_: Test;\n@group(0) @binding(1) \nvar<uniform> input2_: Test2_;\n@group(0) @binding(2) \nvar<uniform> input3_: Test3_;\n\n@vertex \nfn vertex() -> @builtin(position) vec4<f32> {\n    let _e4 = input1_.b;\n    let _e8 = input2_.b;\n    let _e12 = input3_.b;\n    return (((vec4(1f) * _e4) * _e8) * _e12);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-per-vertex.wgsl",
    "content": "@fragment \nfn fs_main(@location(0) @interpolate(per_vertex) v: array<f32, 3>) -> @location(0) vec4<f32> {\n    return vec4<f32>(v[0], v[1], v[2], 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-phony_assignment.wgsl",
    "content": "@group(0) @binding(0) \nvar<uniform> binding: f32;\n\nfn five() -> i32 {\n    return 5i;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n    let phony = binding;\n    let phony_1 = binding;\n    let _e6 = five();\n    let _e7 = five();\n    let phony_2 = binding;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-pointer-function-arg.wgsl",
    "content": "fn takes_ptr(p: ptr<function, i32>) {\n    return;\n}\n\nfn takes_array_ptr(p_1: ptr<function, array<i32, 4>>) {\n    return;\n}\n\nfn takes_vec_ptr(p_2: ptr<function, vec2<i32>>) {\n    return;\n}\n\nfn takes_mat_ptr(p_3: ptr<function, mat2x2<f32>>) {\n    return;\n}\n\nfn local_var(i: u32) {\n    var arr: array<i32, 4> = array<i32, 4>(1i, 2i, 3i, 4i);\n\n    takes_ptr((&arr[i]));\n    takes_array_ptr((&arr));\n    return;\n}\n\nfn mat_vec_ptrs(pv: ptr<function, array<vec2<i32>, 4>>, pm: ptr<function, array<mat2x2<f32>, 4>>, i_1: u32) {\n    takes_vec_ptr((&(*pv)[i_1]));\n    takes_mat_ptr((&(*pm)[i_1]));\n    return;\n}\n\nfn argument(v: ptr<function, array<i32, 4>>, i_2: u32) {\n    takes_ptr((&(*v)[i_2]));\n    return;\n}\n\nfn argument_nested_x2_(v_1: ptr<function, array<array<i32, 4>, 4>>, i_3: u32, j: u32) {\n    takes_ptr((&(*v_1)[i_3][j]));\n    takes_ptr((&(*v_1)[i_3][0]));\n    takes_ptr((&(*v_1)[0][j]));\n    takes_array_ptr((&(*v_1)[i_3]));\n    return;\n}\n\nfn argument_nested_x3_(v_2: ptr<function, array<array<array<i32, 4>, 4>, 4>>, i_4: u32, j_1: u32) {\n    takes_ptr((&(*v_2)[i_4][0][j_1]));\n    takes_ptr((&(*v_2)[i_4][j_1][0]));\n    takes_ptr((&(*v_2)[0][i_4][j_1]));\n    return;\n}\n\nfn index_from_self(v_3: ptr<function, array<i32, 4>>, i_5: u32) {\n    let _e3 = (*v_3)[i_5];\n    takes_ptr((&(*v_3)[_e3]));\n    return;\n}\n\nfn local_var_from_arg(a: array<i32, 4>, i_6: u32) {\n    var b: array<i32, 4>;\n\n    b = a;\n    takes_ptr((&b[i_6]));\n    return;\n}\n\nfn let_binding(a_1: ptr<function, array<i32, 4>>, i_7: u32) {\n    let p0_ = (&(*a_1)[i_7]);\n    takes_ptr(p0_);\n    let p1_ = (&(*a_1)[0]);\n    takes_ptr(p1_);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var vec: array<vec2<i32>, 4>;\n    var mat: array<mat2x2<f32>, 4>;\n    var arr1d: array<i32, 4>;\n    var arr2d: array<array<i32, 4>, 4>;\n    var arr3d: array<array<array<i32, 4>, 4>, 4>;\n\n    local_var(1u);\n    mat_vec_ptrs((&vec), (&mat), 1u);\n    argument((&arr1d), 1u);\n    argument_nested_x2_((&arr2d), 1u, 2u);\n    argument_nested_x3_((&arr3d), 1u, 2u);\n    index_from_self((&arr1d), 1u);\n    local_var_from_arg(array<i32, 4>(1i, 2i, 3i, 4i), 5u);\n    let_binding((&arr1d), 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-pointers.wgsl",
    "content": "struct DynamicArray {\n    arr: array<u32>,\n}\n\n@group(0) @binding(0) \nvar<storage, read_write> dynamic_array: DynamicArray;\n\nfn f() {\n    var v: mat2x2<f32>;\n\n    let px = (&v[0]);\n    (*px) = vec2(10f);\n    return;\n}\n\nfn index_unsized(i: i32, v_1: u32) {\n    let val = dynamic_array.arr[i];\n    dynamic_array.arr[i] = (val + v_1);\n    return;\n}\n\nfn index_dynamic_array(i_1: i32, v_2: u32) {\n    let p = (&dynamic_array.arr);\n    let val_1 = (*p)[i_1];\n    (*p)[i_1] = (val_1 + v_2);\n    return;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    f();\n    index_unsized(1i, 1u);\n    index_dynamic_array(1i, 1u);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-primitive-index.wgsl",
    "content": "enable primitive_index;\n\n@fragment \nfn func(@builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    return vec4<f32>(f32(index), 1f, 1f, 1f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-quad.wgsl",
    "content": "struct VertexOutput {\n    @location(0) uv: vec2<f32>,\n    @builtin(position) position: vec4<f32>,\n}\n\nconst c_scale: f32 = 1.2f;\n\n@group(0) @binding(0) \nvar u_texture: texture_2d<f32>;\n@group(0) @binding(1) \nvar u_sampler: sampler;\n\n@vertex \nfn vert_main(@location(0) pos: vec2<f32>, @location(1) uv: vec2<f32>) -> VertexOutput {\n    return VertexOutput(uv, vec4<f32>((c_scale * pos), 0f, 1f));\n}\n\n@fragment \nfn frag_main(@location(0) uv_1: vec2<f32>) -> @location(0) vec4<f32> {\n    let color = textureSample(u_texture, u_sampler, uv_1);\n    if (color.w == 0f) {\n        discard;\n    }\n    let premultiplied = (color.w * color);\n    return premultiplied;\n}\n\n@fragment \nfn fs_extra() -> @location(0) vec4<f32> {\n    return vec4<f32>(0f, 0.5f, 0f, 0.5f);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-ray-tracing-pipeline.wgsl",
    "content": "enable wgpu_ray_tracing_pipeline;\n\nstruct HitCounters {\n    hit_num: u32,\n    selected_hit: u32,\n}\n\nstruct RayDesc {\n    flags: u32,\n    cull_mask: u32,\n    tmin: f32,\n    tmax: f32,\n    origin: vec3<f32>,\n    dir: vec3<f32>,\n}\n\nvar<ray_payload> hit_num: HitCounters;\n@group(0) @binding(0) \nvar acc_struct: acceleration_structure;\nvar<incoming_ray_payload> incoming_hit_num: HitCounters;\n\n@ray_generation \nfn ray_gen_main(@builtin(ray_invocation_id) id: vec3<u32>, @builtin(num_ray_invocations) num_invocations: vec3<u32>) {\n    hit_num = HitCounters();\n    let shift = (vec3<f32>(id) / vec3<f32>(num_invocations));\n    let ray_shift = ((vec3<f32>(shift.x, 0f, shift.y) * 2f) - vec3(1f));\n    traceRay(acc_struct, RayDesc(0u, 255u, 0.01f, 100f, vec3(0f), (vec3<f32>(0f, 1f, 0f) + ray_shift)), (&hit_num));\n    return;\n}\n\n@miss @incoming_payload(incoming_hit_num) \nfn miss(@builtin(world_ray_origin) origin: vec3<f32>, @builtin(world_ray_direction) dir: vec3<f32>, @builtin(ray_t_min) t_min: f32) {\n    return;\n}\n\n@any_hit @incoming_payload(incoming_hit_num) \nfn any_hit_main(@builtin(instance_custom_data) data: u32, @builtin(geometry_index) geo_idx: u32, @builtin(ray_t_current_max) max_: f32, @builtin(hit_kind) kind: u32) {\n    let _e7 = incoming_hit_num.hit_num;\n    incoming_hit_num.hit_num = (_e7 + 1u);\n    incoming_hit_num.selected_hit = data;\n    return;\n}\n\n@closest_hit @incoming_payload(incoming_hit_num) \nfn closest_hit_main(@builtin(object_ray_origin) origin_1: vec3<f32>, @builtin(object_ray_direction) dir_1: vec3<f32>, @builtin(object_to_world) obj_to_world: mat4x3<f32>, @builtin(world_to_object) world_to_obj: mat4x3<f32>) {\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-select.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var x0_: vec2<i32> = vec2<i32>(1i, 2i);\n    var i1_: vec2<f32>;\n\n    let _e12 = x0_.x;\n    let _e14 = x0_.y;\n    i1_ = select(vec2<f32>(1f, 0f), vec2<f32>(0f, 1f), (_e12 < _e14));\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-shadow.wgsl",
    "content": "struct Globals {\n    view_proj: mat4x4<f32>,\n    num_lights: vec4<u32>,\n}\n\nstruct Entity {\n    world: mat4x4<f32>,\n    color: vec4<f32>,\n}\n\nstruct VertexOutput {\n    @builtin(position) proj_position: vec4<f32>,\n    @location(0) world_normal: vec3<f32>,\n    @location(1) world_position: vec4<f32>,\n}\n\nstruct Light {\n    proj: mat4x4<f32>,\n    pos: vec4<f32>,\n    color: vec4<f32>,\n}\n\nconst c_ambient: vec3<f32> = vec3<f32>(0.05f, 0.05f, 0.05f);\nconst c_max_lights: u32 = 10u;\n\n@group(0) @binding(0) \nvar<uniform> u_globals: Globals;\n@group(1) @binding(0) \nvar<uniform> u_entity: Entity;\n@group(0) @binding(1) \nvar<storage> s_lights: array<Light>;\n@group(0) @binding(1) \nvar<uniform> u_lights: array<Light, 10>;\n@group(0) @binding(2) \nvar t_shadow: texture_depth_2d_array;\n@group(0) @binding(3) \nvar sampler_shadow: sampler_comparison;\n\nfn fetch_shadow(light_id: u32, homogeneous_coords: vec4<f32>) -> f32 {\n    if (homogeneous_coords.w <= 0f) {\n        return 1f;\n    }\n    let flip_correction = vec2<f32>(0.5f, -0.5f);\n    let proj_correction = (1f / homogeneous_coords.w);\n    let light_local = (((homogeneous_coords.xy * flip_correction) * proj_correction) + vec2<f32>(0.5f, 0.5f));\n    let _e24 = textureSampleCompareLevel(t_shadow, sampler_shadow, light_local, i32(light_id), (homogeneous_coords.z * proj_correction));\n    return _e24;\n}\n\n@vertex \nfn vs_main(@location(0) @interpolate(flat) position: vec4<i32>, @location(1) @interpolate(flat) normal: vec4<i32>) -> VertexOutput {\n    var out: VertexOutput;\n\n    let w = u_entity.world;\n    let _e7 = u_entity.world;\n    let world_pos = (_e7 * vec4<f32>(position));\n    out.world_normal = (mat3x3<f32>(w[0].xyz, w[1].xyz, w[2].xyz) * vec3<f32>(normal.xyz));\n    out.world_position = world_pos;\n    let _e26 = u_globals.view_proj;\n    out.proj_position = (_e26 * world_pos);\n    let _e28 = out;\n    return _e28;\n}\n\n@fragment \nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    var color: vec3<f32> = c_ambient;\n    var i: u32 = 0u;\n\n    let normal_1 = normalize(in.world_normal);\n    loop {\n        let _e7 = i;\n        let _e11 = u_globals.num_lights.x;\n        if (_e7 < min(_e11, c_max_lights)) {\n        } else {\n            break;\n        }\n        {\n            let _e16 = i;\n            let light = s_lights[_e16];\n            let _e19 = i;\n            let _e23 = fetch_shadow(_e19, (light.proj * in.world_position));\n            let light_dir = normalize((light.pos.xyz - in.world_position.xyz));\n            let diffuse = max(0f, dot(normal_1, light_dir));\n            let _e33 = color;\n            color = (_e33 + ((_e23 * diffuse) * light.color.xyz));\n        }\n        continuing {\n            let _e40 = i;\n            i = (_e40 + 1u);\n        }\n    }\n    let _e42 = color;\n    let _e47 = u_entity.color;\n    return (vec4<f32>(_e42, 1f) * _e47);\n}\n\n@fragment \nfn fs_main_without_storage(in_1: VertexOutput) -> @location(0) vec4<f32> {\n    var color_1: vec3<f32> = c_ambient;\n    var i_1: u32 = 0u;\n\n    let normal_2 = normalize(in_1.world_normal);\n    loop {\n        let _e7 = i_1;\n        let _e11 = u_globals.num_lights.x;\n        if (_e7 < min(_e11, c_max_lights)) {\n        } else {\n            break;\n        }\n        {\n            let _e16 = i_1;\n            let light_1 = u_lights[_e16];\n            let _e19 = i_1;\n            let _e23 = fetch_shadow(_e19, (light_1.proj * in_1.world_position));\n            let light_dir_1 = normalize((light_1.pos.xyz - in_1.world_position.xyz));\n            let diffuse_1 = max(0f, dot(normal_2, light_dir_1));\n            let _e33 = color_1;\n            color_1 = (_e33 + ((_e23 * diffuse_1) * light_1.color.xyz));\n        }\n        continuing {\n            let _e40 = i_1;\n            i_1 = (_e40 + 1u);\n        }\n    }\n    let _e42 = color_1;\n    let _e47 = u_entity.color;\n    return (vec4<f32>(_e42, 1f) * _e47);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-skybox.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) uv: vec3<f32>,\n}\n\nstruct Data {\n    proj_inv: mat4x4<f32>,\n    view: mat4x4<f32>,\n}\n\n@group(0) @binding(0) \nvar<uniform> r_data: Data;\n@group(0) @binding(1) \nvar r_texture: texture_cube<f32>;\n@group(0) @binding(2) \nvar r_sampler: sampler;\n\n@vertex \nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var tmp1_: i32;\n    var tmp2_: i32;\n\n    tmp1_ = (i32(vertex_index) / 2i);\n    tmp2_ = (i32(vertex_index) & 1i);\n    let _e9 = tmp1_;\n    let _e15 = tmp2_;\n    let pos = vec4<f32>(((f32(_e9) * 4f) - 1f), ((f32(_e15) * 4f) - 1f), 0f, 1f);\n    let _e27 = r_data.view[0];\n    let _e32 = r_data.view[1];\n    let _e37 = r_data.view[2];\n    let inv_model_view = transpose(mat3x3<f32>(_e27.xyz, _e32.xyz, _e37.xyz));\n    let _e43 = r_data.proj_inv;\n    let unprojected = (_e43 * pos);\n    return VertexOutput(pos, (inv_model_view * unprojected.xyz));\n}\n\n@fragment \nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    let _e4 = textureSample(r_texture, r_sampler, in.uv);\n    return _e4;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-standard.wgsl",
    "content": "fn test_any_and_all_for_bool() -> bool {\n    return true;\n}\n\n@fragment \nfn derivatives(@builtin(position) foo: vec4<f32>) -> @location(0) vec4<f32> {\n    var x: vec4<f32>;\n    var y: vec4<f32>;\n    var z: vec4<f32>;\n\n    let _e1 = dpdxCoarse(foo);\n    x = _e1;\n    let _e3 = dpdyCoarse(foo);\n    y = _e3;\n    let _e5 = fwidthCoarse(foo);\n    z = _e5;\n    let _e7 = dpdxFine(foo);\n    x = _e7;\n    let _e8 = dpdyFine(foo);\n    y = _e8;\n    let _e9 = fwidthFine(foo);\n    z = _e9;\n    let _e10 = dpdx(foo);\n    x = _e10;\n    let _e11 = dpdy(foo);\n    y = _e11;\n    let _e12 = fwidth(foo);\n    z = _e12;\n    let _e13 = test_any_and_all_for_bool();\n    let _e14 = x;\n    let _e15 = y;\n    let _e17 = z;\n    return ((_e14 + _e15) * _e17);\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-struct-layout.wgsl",
    "content": "struct NoPadding {\n    @location(0) v3_: vec3<f32>,\n    @location(1) f3_: f32,\n}\n\nstruct NeedsPadding {\n    @location(0) f3_forces_padding: f32,\n    @location(1) v3_needs_padding: vec3<f32>,\n    @location(2) f3_: f32,\n}\n\n@group(0) @binding(0) \nvar<uniform> no_padding_uniform: NoPadding;\n@group(0) @binding(1) \nvar<storage, read_write> no_padding_storage: NoPadding;\n@group(0) @binding(2) \nvar<uniform> needs_padding_uniform: NeedsPadding;\n@group(0) @binding(3) \nvar<storage, read_write> needs_padding_storage: NeedsPadding;\n\n@fragment \nfn no_padding_frag(input: NoPadding) -> @location(0) vec4<f32> {\n    return vec4(0f);\n}\n\n@vertex \nfn no_padding_vert(input_1: NoPadding) -> @builtin(position) vec4<f32> {\n    return vec4(0f);\n}\n\n@compute @workgroup_size(16, 1, 1) \nfn no_padding_comp() {\n    var x: NoPadding;\n\n    let _e2 = no_padding_uniform;\n    x = _e2;\n    let _e4 = no_padding_storage;\n    x = _e4;\n    return;\n}\n\n@fragment \nfn needs_padding_frag(input_2: NeedsPadding) -> @location(0) vec4<f32> {\n    return vec4(0f);\n}\n\n@vertex \nfn needs_padding_vert(input_3: NeedsPadding) -> @builtin(position) vec4<f32> {\n    return vec4(0f);\n}\n\n@compute @workgroup_size(16, 1, 1) \nfn needs_padding_comp() {\n    var x_1: NeedsPadding;\n\n    let _e2 = needs_padding_uniform;\n    x_1 = _e2;\n    let _e4 = needs_padding_storage;\n    x_1 = _e4;\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-subgroup-barrier.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    subgroupBarrier();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-subgroup-operations.wgsl",
    "content": "struct Structure {\n    @builtin(num_subgroups) num_subgroups: u32,\n    @builtin(subgroup_size) subgroup_size: u32,\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn main(sizes: Structure, @builtin(subgroup_id) subgroup_id: u32, @builtin(subgroup_invocation_id) subgroup_invocation_id: u32) {\n    let _e7 = subgroupBallot(((subgroup_invocation_id & 1u) == 1u));\n    let _e8 = subgroupBallot();\n    let _e11 = subgroupAll((subgroup_invocation_id != 0u));\n    let _e14 = subgroupAny((subgroup_invocation_id == 0u));\n    let _e15 = subgroupAdd(subgroup_invocation_id);\n    let _e16 = subgroupMul(subgroup_invocation_id);\n    let _e17 = subgroupMin(subgroup_invocation_id);\n    let _e18 = subgroupMax(subgroup_invocation_id);\n    let _e19 = subgroupAnd(subgroup_invocation_id);\n    let _e20 = subgroupOr(subgroup_invocation_id);\n    let _e21 = subgroupXor(subgroup_invocation_id);\n    let _e22 = subgroupExclusiveAdd(subgroup_invocation_id);\n    let _e23 = subgroupExclusiveMul(subgroup_invocation_id);\n    let _e24 = subgroupInclusiveAdd(subgroup_invocation_id);\n    let _e25 = subgroupInclusiveMul(subgroup_invocation_id);\n    let _e26 = subgroupBroadcastFirst(subgroup_invocation_id);\n    let _e28 = subgroupBroadcast(subgroup_invocation_id, 4u);\n    let _e33 = subgroupShuffle(subgroup_invocation_id, ((sizes.subgroup_size - 1u) - subgroup_invocation_id));\n    let _e35 = subgroupShuffleDown(subgroup_invocation_id, 1u);\n    let _e37 = subgroupShuffleUp(subgroup_invocation_id, 1u);\n    let _e41 = subgroupShuffleXor(subgroup_invocation_id, (sizes.subgroup_size - 1u));\n    let _e43 = quadBroadcast(subgroup_invocation_id, 4u);\n    let _e44 = quadSwapX(subgroup_invocation_id);\n    let _e45 = quadSwapY(subgroup_invocation_id);\n    let _e46 = quadSwapDiagonal(subgroup_invocation_id);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-texture-arg.wgsl",
    "content": "@group(0) @binding(0) \nvar Texture: texture_2d<f32>;\n@group(0) @binding(1) \nvar Sampler: sampler;\n\nfn test(Passed_Texture: texture_2d<f32>, Passed_Sampler: sampler) -> vec4<f32> {\n    let _e5 = textureSample(Passed_Texture, Passed_Sampler, vec2<f32>(0f, 0f));\n    return _e5;\n}\n\n@fragment \nfn main() -> @location(0) vec4<f32> {\n    let _e2 = test(Texture, Sampler);\n    return _e2;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-texture-external.wgsl",
    "content": "@group(0) @binding(0) \nvar tex: texture_external;\n@group(0) @binding(1) \nvar samp: sampler;\n\nfn test(t: texture_external) -> vec4<f32> {\n    var a: vec4<f32>;\n    var b: vec4<f32>;\n    var c: vec4<f32>;\n    var d: vec2<u32>;\n\n    let _e4 = textureSampleBaseClampToEdge(t, samp, vec2(0f));\n    a = _e4;\n    let _e8 = textureLoad(t, vec2(0i));\n    b = _e8;\n    let _e12 = textureLoad(t, vec2(0u));\n    c = _e12;\n    let _e14 = textureDimensions(t);\n    d = _e14;\n    let _e16 = a;\n    let _e17 = b;\n    let _e19 = c;\n    let _e21 = d;\n    return (((_e16 + _e17) + _e19) + vec2<f32>(_e21).xyxy);\n}\n\n@fragment \nfn fragment_main() -> @location(0) vec4<f32> {\n    let _e1 = test(tex);\n    return _e1;\n}\n\n@vertex \nfn vertex_main() -> @builtin(position) vec4<f32> {\n    let _e1 = test(tex);\n    return _e1;\n}\n\n@compute @workgroup_size(1, 1, 1) \nfn compute_main() {\n    let _e1 = test(tex);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-type-alias.wgsl",
    "content": "@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let a = vec3<f32>(0f, 0f, 0f);\n    let c = vec3(0f);\n    let b = vec3<f32>(vec2(0f), 0f);\n    let d = vec3<f32>(vec2(0f), 0f);\n    let e = vec3<i32>(d);\n    let f = mat2x2<f32>(vec2<f32>(1f, 2f), vec2<f32>(3f, 4f));\n    let g = mat3x3<f32>(a, a, a);\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-type-inference.wgsl",
    "content": "const g1_: u32 = 1u;\nconst g3_: f32 = 1f;\nconst g4_: vec4<i32> = vec4<i32>();\nconst g5_: vec4<i32> = vec4(1i);\nconst g6_: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    var g0x: i32 = 1i;\n    var g2x: f32 = 1f;\n    var g7x: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 1f), vec2<f32>(1f, 1f));\n    var c0x: i32 = 1i;\n    var c1x: u32 = 1u;\n    var c2x: f32 = 1f;\n    var c3x: f32 = 1f;\n    var c4x: vec4<i32> = vec4<i32>();\n    var c5x: vec4<i32> = vec4(1i);\n    var c6x: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    var c7x: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 1f), vec2<f32>(1f, 1f));\n    var l0x: i32;\n    var l1x: u32;\n    var l2x: f32;\n    var l3x: f32;\n    var l4x: vec4<i32>;\n    var v0_: i32 = 1i;\n    var v1_: u32 = 1u;\n    var v2_: f32 = 1f;\n    var v3_: f32 = 1f;\n    var v4_: vec4<i32> = vec4<i32>();\n    var v5_: vec4<i32> = vec4(1i);\n    var v6_: mat2x2<f32> = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    var v7_: mat2x2<f32> = mat2x2<f32>(vec2<f32>(1f, 1f), vec2<f32>(1f, 1f));\n\n    let l5_ = vec4(1i);\n    let l6_ = mat2x2<f32>(vec2<f32>(0f, 0f), vec2<f32>(0f, 0f));\n    let l7_ = mat2x2<f32>(vec2<f32>(1f, 1f), vec2<f32>(1f, 1f));\n    l0x = 1i;\n    l1x = 1u;\n    l2x = 1f;\n    l3x = 1f;\n    l4x = vec4<i32>();\n    return;\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-workgroup-uniform-load-atomic.wgsl",
    "content": "struct AtomicStruct {\n    atomic_scalar: atomic<u32>,\n    atomic_arr: array<atomic<i32>, 2>,\n}\n\nvar<workgroup> wg_scalar: atomic<u32>;\nvar<workgroup> wg_signed: atomic<i32>;\nvar<workgroup> wg_struct: AtomicStruct;\n\n@compute @workgroup_size(64, 1, 1) \nfn test_atomic_workgroup_uniform_load(@builtin(workgroup_id) workgroup_id: vec3<u32>, @builtin(local_invocation_id) local_id: vec3<u32>) {\n    var local: bool;\n    var local_1: bool;\n    var local_2: bool;\n\n    let active_tile_index = (workgroup_id.x + (workgroup_id.y * 32768u));\n    let _e11 = atomicOr((&wg_scalar), u32((active_tile_index >= 64u)));\n    let _e14 = atomicAdd((&wg_signed), 1i);\n    atomicStore((&wg_struct.atomic_scalar), 1u);\n    let _e22 = atomicAdd((&wg_struct.atomic_arr[0]), 1i);\n    workgroupBarrier();\n    let _e24 = workgroupUniformLoad((&wg_scalar));\n    let _e26 = workgroupUniformLoad((&wg_signed));\n    let _e29 = workgroupUniformLoad((&wg_struct.atomic_scalar));\n    let _e33 = workgroupUniformLoad((&wg_struct.atomic_arr[0]));\n    if (_e24 == 0u) {\n        local = (_e26 > 0i);\n    } else {\n        local = false;\n    }\n    let _e41 = local;\n    if _e41 {\n        local_1 = (_e29 > 0u);\n    } else {\n        local_1 = false;\n    }\n    let _e47 = local_1;\n    if _e47 {\n        local_2 = (_e33 > 0i);\n    } else {\n        local_2 = false;\n    }\n    let _e53 = local_2;\n    if _e53 {\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-workgroup-uniform-load.wgsl",
    "content": "const SIZE: u32 = 128u;\n\nvar<workgroup> arr_i32_: array<i32, 128>;\n\n@compute @workgroup_size(4, 1, 1) \nfn test_workgroupUniformLoad(@builtin(workgroup_id) workgroup_id: vec3<u32>) {\n    let x = (&arr_i32_[workgroup_id.x]);\n    let _e4 = workgroupUniformLoad(x);\n    if (_e4 > 10i) {\n        workgroupBarrier();\n        return;\n    } else {\n        return;\n    }\n}\n"
  },
  {
    "path": "naga/tests/out/wgsl/wgsl-workgroup-var-init.wgsl",
    "content": "struct WStruct {\n    arr: array<u32, 512>,\n    atom: atomic<i32>,\n    atom_arr: array<array<atomic<i32>, 8>, 8>,\n}\n\nvar<workgroup> w_mem: WStruct;\n@group(0) @binding(0) \nvar<storage, read_write> output: array<u32, 512>;\n\n@compute @workgroup_size(1, 1, 1) \nfn main() {\n    let _e3 = w_mem.arr;\n    output = _e3;\n    return;\n}\n"
  },
  {
    "path": "naga/xtask/.gitignore",
    "content": "!Cargo.lock\ntarget/\n"
  },
  {
    "path": "naga/xtask/Cargo.toml",
    "content": "[package]\nname = \"naga-xtask\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\nrust-version.workspace = true\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nanyhow.workspace = true\nenv_logger = { workspace = true, default-features = false }\nglob.workspace = true\nhlsl-snapshots.workspace = true\nindicatif.workspace = true\njobserver.workspace = true\nlog.workspace = true\nnum_cpus.workspace = true\npico-args.workspace = true\nshell-words.workspace = true\nwhich.workspace = true\n"
  },
  {
    "path": "naga/xtask/src/cli.rs",
    "content": "use std::process::exit;\n\nuse anyhow::{anyhow, bail, ensure, Context};\nuse pico_args::Arguments;\n\nconst HELP: &str = \"\\\nUsage: xtask <COMMAND>\n\nCommands:\n  all\n  validate\n    dot\n    glsl\n    hlsl\n      dxc\n      fxc\n    msl\n    spv\n    wgsl\n\nOptions:\n  -h, --help  Print help\n\";\n\n#[derive(Debug)]\npub(crate) struct Args {\n    pub subcommand: Subcommand,\n}\n\nimpl Args {\n    pub fn parse() -> Self {\n        let mut args = Arguments::from_env();\n        log::debug!(\"parsing args: {args:?}\");\n        if args.contains(\"--help\") {\n            eprint!(\"{HELP}\");\n            exit(101);\n        }\n        match Subcommand::parse(args).map(|subcommand| Self { subcommand }) {\n            Ok(this) => this,\n            Err(e) => {\n                eprintln!(\"{:?}\", anyhow!(e));\n                exit(1)\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum Subcommand {\n    All,\n    Validate(ValidateSubcommand),\n}\n\nimpl Subcommand {\n    fn parse(mut args: Arguments) -> anyhow::Result<Subcommand> {\n        let subcmd = args\n            .subcommand()\n            .context(\"failed to parse subcommand\")?\n            .context(\"no subcommand specified; see `--help` for more details\")?;\n        match &*subcmd {\n            \"all\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::All)\n            }\n            \"validate\" => Ok(Self::Validate(ValidateSubcommand::parse(args)?)),\n            other => {\n                bail!(\"unrecognized subcommand {other:?}; see `--help` for more details\")\n            }\n        }\n    }\n}\n\n// If you add a new validation subcommand, be sure to update the code\n// that processes `All`.\n#[derive(Debug)]\npub(crate) enum ValidateSubcommand {\n    Spirv,\n    Metal,\n    Glsl,\n    Dot,\n    Wgsl,\n    Hlsl(ValidateHlslCommand),\n    All,\n}\n\nimpl ValidateSubcommand {\n    fn parse(mut args: Arguments) -> Result<Self, anyhow::Error> {\n        let subcmd = args\n            .subcommand()\n            .context(\"failed to parse `validate` subcommand\")?\n            .context(\"no `validate` subcommand specified; see `--help` for more details\")?;\n        match &*subcmd {\n            \"spv\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Spirv)\n            }\n            \"msl\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Metal)\n            }\n            \"glsl\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Glsl)\n            }\n            \"dot\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Dot)\n            }\n            \"wgsl\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Wgsl)\n            }\n            \"hlsl\" => Ok(Self::Hlsl(ValidateHlslCommand::parse(args)?)),\n            \"all\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::All)\n            }\n            other => {\n                bail!(\"unrecognized `validate` subcommand {other:?}; see `--help` for more details\")\n            }\n        }\n    }\n\n    pub(crate) fn all() -> impl Iterator<Item = Self> {\n        [\n            Self::Spirv,\n            Self::Metal,\n            Self::Glsl,\n            Self::Dot,\n            Self::Wgsl,\n            Self::Hlsl(ValidateHlslCommand::Dxc),\n            Self::Hlsl(ValidateHlslCommand::Fxc),\n        ]\n        .into_iter()\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum ValidateHlslCommand {\n    Dxc,\n    Fxc,\n}\n\nimpl ValidateHlslCommand {\n    fn parse(mut args: Arguments) -> anyhow::Result<Self> {\n        let subcmd = args\n            .subcommand()\n            .context(\"failed to parse `hlsl` subcommand\")?\n            .context(\"no `hlsl` subcommand specified; see `--help` for more details\")?;\n        match &*subcmd {\n            \"dxc\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Dxc)\n            }\n            \"fxc\" => {\n                ensure_remaining_args_empty(args)?;\n                Ok(Self::Fxc)\n            }\n            other => {\n                bail!(\"unrecognized `hlsl` subcommand {other:?}; see `--help` for more details\")\n            }\n        }\n    }\n}\n\nfn ensure_remaining_args_empty(args: Arguments) -> anyhow::Result<()> {\n    let remaining_args = args.finish();\n    ensure!(\n        remaining_args.is_empty(),\n        \"not all arguments were parsed (remaining: {remaining_args:?}); fix your invocation, \\\n        please!\"\n    );\n    Ok(())\n}\n"
  },
  {
    "path": "naga/xtask/src/fs.rs",
    "content": "use std::{fs::File, path::Path};\n\nuse anyhow::Context;\n\npub(crate) fn open_file(path: impl AsRef<Path>) -> anyhow::Result<File> {\n    let path = path.as_ref();\n    File::open(path).with_context(|| format!(\"failed to open {path:?}\"))\n}\n"
  },
  {
    "path": "naga/xtask/src/glob.rs",
    "content": "use std::path::{Path, PathBuf};\n\nuse anyhow::Context;\nuse glob::glob;\n\n/// Apply `f` to each file matching `pattern` in `top_dir`.\n///\n/// Pass files as `anyhow::Result` values, to carry errors from\n/// directory iteration and metadata checking.\npub(crate) fn for_each_file(\n    top_dir: impl AsRef<Path>,\n    pattern: impl AsRef<Path>,\n    mut f: impl FnMut(anyhow::Result<PathBuf>),\n) {\n    fn filter_files(glob: &str, result: glob::GlobResult) -> anyhow::Result<Option<PathBuf>> {\n        let path = result.with_context(|| format!(\"error while iterating over glob {glob:?}\"))?;\n        let metadata = path\n            .metadata()\n            .with_context(|| format!(\"failed to fetch metadata for {path:?}\"))?;\n        Ok(metadata.is_file().then_some(path))\n    }\n\n    let pattern_in_dir = top_dir.as_ref().join(pattern.as_ref());\n    let pattern_in_dir = pattern_in_dir.to_str().unwrap();\n\n    glob(pattern_in_dir)\n        .context(\"glob pattern {path:?} is invalid\")\n        .unwrap()\n        .for_each(|result| {\n            if let Some(result) = filter_files(pattern_in_dir, result).transpose() {\n                f(result);\n            }\n        });\n}\n"
  },
  {
    "path": "naga/xtask/src/jobserver.rs",
    "content": "//! Running jobs in parallel, with a controlled degree of concurrency.\n\nuse std::sync::OnceLock;\n\nuse jobserver::Client;\n\nstatic JOB_SERVER: OnceLock<Client> = OnceLock::new();\n\npub fn init() {\n    JOB_SERVER.get_or_init(|| {\n        // Try to connect to a jobserver inherited from our parent.\n        if let Some(client) = unsafe { Client::from_env() } {\n            log::debug!(\"connected to inherited jobserver client\");\n            client\n        } else {\n            // Otherwise, start our own jobserver.\n            log::debug!(\"no inherited jobserver client; creating a new jobserver\");\n            Client::new(num_cpus::get()).expect(\"failed to create jobserver\")\n        }\n    });\n}\n\n/// Wait until it is okay to start a new job, and then spawn a thread running `body`.\npub fn start_job_thread<F>(body: F) -> anyhow::Result<()>\nwhere\n    F: FnOnce() + Send + 'static,\n{\n    let acquired = JOB_SERVER.get().unwrap().acquire()?;\n    std::thread::spawn(move || {\n        body();\n        drop(acquired);\n    });\n    Ok(())\n}\n"
  },
  {
    "path": "naga/xtask/src/main.rs",
    "content": "#![cfg_attr(target_arch = \"wasm32\", no_main)]\n#![cfg(not(target_arch = \"wasm32\"))]\n\nuse std::process::ExitCode;\n\nuse cli::Args;\n\nuse crate::{\n    cli::Subcommand,\n    process::{which, EasyCommand},\n};\n\nmod cli;\nmod fs;\nmod glob;\nmod jobserver;\nmod path;\nmod process;\nmod validate;\n\nfn main() -> ExitCode {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Info)\n        .parse_default_env()\n        .format_indent(Some(0))\n        .init();\n\n    jobserver::init();\n\n    let args = Args::parse();\n\n    match run(args) {\n        Ok(()) => ExitCode::SUCCESS,\n        Err(e) => {\n            log::error!(\"{e:?}\");\n            ExitCode::FAILURE\n        }\n    }\n}\n\nfn run(args: Args) -> anyhow::Result<()> {\n    let Args { subcommand } = args;\n\n    assert!(which(\"cargo\").is_ok());\n\n    match subcommand {\n        Subcommand::All => {\n            EasyCommand::simple(\"cargo\", [\"fmt\"]).success()?;\n            EasyCommand::simple(\"cargo\", [\"test\", \"--all-features\", \"--workspace\"]).success()?;\n            EasyCommand::simple(\n                \"cargo\",\n                [\n                    \"clippy\",\n                    \"--all-features\",\n                    \"--workspace\",\n                    \"--\",\n                    \"-D\",\n                    \"warnings\",\n                ],\n            )\n            .success()?;\n            Ok(())\n        }\n        Subcommand::Validate(cmd) => validate::validate(cmd),\n    }\n}\n"
  },
  {
    "path": "naga/xtask/src/path.rs",
    "content": "use std::path::{Path, PathBuf};\n\npub(crate) fn join_path<P, I>(iter: I) -> PathBuf\nwhere\n    P: AsRef<Path>,\n    I: IntoIterator<Item = P>,\n{\n    let mut path = PathBuf::new();\n    path.extend(iter);\n    path\n}\n"
  },
  {
    "path": "naga/xtask/src/process.rs",
    "content": "use std::{\n    ffi::{OsStr, OsString},\n    fmt::{self, Display},\n    iter::once,\n    ops::{Deref, DerefMut},\n    process::Command,\n};\n\nuse anyhow::{ensure, Context};\n\n#[derive(Debug)]\npub(crate) struct EasyCommand {\n    inner: Command,\n}\n\nimpl EasyCommand {\n    pub fn new<C>(cmd: C, config: impl FnOnce(&mut Command) -> &mut Command) -> Self\n    where\n        C: AsRef<OsStr>,\n    {\n        let mut inner = Command::new(cmd);\n        config(&mut inner);\n        Self { inner }\n    }\n\n    pub fn simple<C, A, I>(cmd: C, args: I) -> Self\n    where\n        C: AsRef<OsStr>,\n        A: AsRef<OsStr>,\n        I: IntoIterator<Item = A>,\n    {\n        Self::new(cmd, |cmd| cmd.args(args))\n    }\n\n    pub fn success(&mut self) -> anyhow::Result<()> {\n        let Self { inner } = self;\n        log::debug!(\"running {inner:?}\");\n        let output = inner\n            .output()\n            .with_context(|| format!(\"failed to run {self}\"))?;\n        ensure!(\n            output.status.success(),\n            \"{self} failed to run; exit code: {:?}\\nstdout:\\n{}\\nstderr:\\n{}\",\n            output.status.code(),\n            String::from_utf8_lossy(&output.stdout),\n            String::from_utf8_lossy(&output.stderr),\n        );\n        Ok(())\n    }\n}\n\nimpl Deref for EasyCommand {\n    type Target = Command;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl DerefMut for EasyCommand {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.inner\n    }\n}\n\npub(crate) fn which(binary_name: &str) -> anyhow::Result<OsString> {\n    ::which::which(binary_name)\n        .with_context(|| format!(\"unable to find `{binary_name}` binary\"))\n        .map(|buf| buf.file_name().unwrap().to_owned())\n}\n\nimpl Display for EasyCommand {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let Self { inner } = self;\n        let prog = inner.get_program().to_string_lossy();\n        let args = inner.get_args().map(|a| a.to_string_lossy());\n        let shell_words = shell_words::join(once(prog).chain(args));\n        write!(f, \"`{shell_words}`\")\n    }\n}\n"
  },
  {
    "path": "naga/xtask/src/validate.rs",
    "content": "use std::{\n    io::{BufRead, BufReader, Write},\n    path::Path,\n    process::Stdio,\n};\n\nuse anyhow::{bail, Context};\n\nuse crate::{\n    cli::{ValidateHlslCommand, ValidateSubcommand},\n    fs::open_file,\n    path::join_path,\n    process::{which, EasyCommand},\n};\n\ntype Job = Box<dyn FnOnce() -> anyhow::Result<()> + Send + std::panic::UnwindSafe + 'static>;\n\npub(crate) fn validate(cmd: ValidateSubcommand) -> anyhow::Result<()> {\n    let mut jobs = vec![];\n    collect_validation_jobs(&mut jobs, cmd)?;\n\n    let progress_bar = indicatif::ProgressBar::new(jobs.len() as u64);\n\n    let (tx_results, rx_results) = std::sync::mpsc::channel();\n    let enqueuing_thread = std::thread::spawn(move || -> anyhow::Result<()> {\n        for job in jobs {\n            let tx_results = tx_results.clone();\n            crate::jobserver::start_job_thread(move || {\n                let result = match std::panic::catch_unwind(job) {\n                    Ok(result) => result,\n                    Err(payload) => Err(match payload.downcast_ref::<&str>() {\n                        Some(message) => {\n                            anyhow::anyhow!(\"Validation job thread panicked: {message}\")\n                        }\n                        None => anyhow::anyhow!(\"Validation job thread panicked\"),\n                    }),\n                };\n                tx_results.send(result).unwrap();\n            })?;\n        }\n        Ok(())\n    });\n\n    let mut all_good = true;\n    for result in rx_results {\n        if let Err(error) = result {\n            all_good = false;\n            progress_bar.suspend(|| {\n                log::error!(\"{error:#}\");\n            });\n        }\n        progress_bar.inc(1);\n    }\n\n    progress_bar.finish_and_clear();\n\n    anyhow::ensure!(\n        all_good,\n        \"failed to validate one or more files, see above output for more details\"\n    );\n\n    if let Err(error) = enqueuing_thread.join().unwrap() {\n        bail!(\"Error enqueuing jobs:\\n{error:#}\");\n    }\n\n    Ok(())\n}\n\nfn collect_validation_jobs(jobs: &mut Vec<Job>, cmd: ValidateSubcommand) -> anyhow::Result<()> {\n    let snapshots_base_out = join_path([\"tests\", \"out\"]);\n    match cmd {\n        ValidateSubcommand::Spirv => {\n            let spirv_as = \"spirv-as\";\n            which(spirv_as)?;\n\n            let spirv_val = \"spirv-val\";\n            which(spirv_val)?;\n\n            push_job_for_each_file(snapshots_base_out, \"spv/*.spvasm\", jobs, |path| {\n                validate_spirv(&path, spirv_as, spirv_val)\n            });\n        }\n        ValidateSubcommand::Metal => {\n            let xcrun = \"xcrun\";\n            which(xcrun)?;\n            push_job_for_each_file(snapshots_base_out, \"msl/*.metal\", jobs, |path| {\n                validate_metal(&path, xcrun)\n            });\n        }\n        ValidateSubcommand::Glsl => {\n            let glslang_validator = \"glslangValidator\";\n            which(glslang_validator)?;\n            for (glob, type_arg) in [\n                (\"glsl/*.Vertex.glsl\", \"vert\"),\n                (\"glsl/*.Fragment.glsl\", \"frag\"),\n                (\"glsl/*.Compute.glsl\", \"comp\"),\n            ] {\n                push_job_for_each_file(&snapshots_base_out, glob, jobs, |path| {\n                    validate_glsl(&path, type_arg, glslang_validator)\n                });\n            }\n        }\n        ValidateSubcommand::Dot => {\n            let dot = \"dot\";\n            which(dot)?;\n            push_job_for_each_file(snapshots_base_out, \"dot/*.dot\", jobs, |path| {\n                validate_dot(&path, dot)\n            });\n        }\n        ValidateSubcommand::Wgsl => {\n            let mut paths = vec![];\n            crate::glob::for_each_file(snapshots_base_out, \"wgsl/*.wgsl\", |path_result| {\n                try_push_job(jobs, |_| {\n                    paths.push(path_result?.to_owned());\n                    Ok(())\n                })\n            });\n            if !paths.is_empty() {\n                jobs.push(Box::new(move || validate_wgsl(&paths)));\n            }\n        }\n        ValidateSubcommand::Hlsl(cmd) => {\n            let bin;\n            let validator: fn(&Path, hlsl_snapshots::ConfigItem, &str) -> anyhow::Result<()>;\n            match cmd {\n                ValidateHlslCommand::Dxc => {\n                    bin = \"dxc\";\n                    which(bin)?;\n                    validator = validate_hlsl_with_dxc;\n                }\n                ValidateHlslCommand::Fxc => {\n                    bin = \"fxc\";\n                    which(bin)?;\n                    validator = validate_hlsl_with_fxc;\n                }\n            }\n\n            crate::glob::for_each_file(snapshots_base_out, \"hlsl/*.hlsl\", |path_result| {\n                try_push_job(jobs, |jobs| {\n                    let path = path_result?;\n                    push_job_for_each_hlsl_config_item(&path, bin, jobs, validator)?;\n                    Ok(())\n                })\n            });\n        }\n        ValidateSubcommand::All => {\n            for subcmd in ValidateSubcommand::all() {\n                let should_validate = match &subcmd {\n                    ValidateSubcommand::Wgsl\n                    | ValidateSubcommand::Spirv\n                    | ValidateSubcommand::Glsl\n                    | ValidateSubcommand::Dot => true,\n                    ValidateSubcommand::Metal => cfg!(target_vendor = \"apple\"),\n                    // The FXC compiler is only available on Windows.\n                    //\n                    // The DXC compiler can be built and run on any platform,\n                    // but they don't make Linux releases and it's not clear\n                    // what Git commit actually works on Linux, so restrict\n                    // that to Windows as well.\n                    ValidateSubcommand::Hlsl(\n                        ValidateHlslCommand::Dxc | ValidateHlslCommand::Fxc,\n                    ) => cfg!(target_os = \"windows\"),\n                    ValidateSubcommand::All => continue,\n                };\n                if should_validate {\n                    collect_validation_jobs(jobs, subcmd)?;\n                }\n            }\n        }\n    };\n\n    Ok(())\n}\n\nfn push_job_for_each_file(\n    top_dir: impl AsRef<Path>,\n    pattern: impl AsRef<Path>,\n    jobs: &mut Vec<Job>,\n    f: impl FnOnce(std::path::PathBuf) -> anyhow::Result<()>\n        + Clone\n        + Send\n        + std::panic::UnwindSafe\n        + 'static,\n) {\n    crate::glob::for_each_file(top_dir, pattern, move |path_result| {\n        // Let each job closure stand on its own.\n        let f = f.clone();\n        jobs.push(Box::new(|| f(path_result?)));\n    });\n}\n\n/// Call `f` to extend `jobs`, but if `f` itself fails, push a job that reports that.\nfn try_push_job(jobs: &mut Vec<Job>, f: impl FnOnce(&mut Vec<Job>) -> anyhow::Result<()>) {\n    if let Err(error) = f(jobs) {\n        jobs.push(Box::new(|| Err(error)));\n    }\n}\n\nfn validate_spirv(path: &Path, spirv_as: &str, spirv_val: &str) -> anyhow::Result<()> {\n    let second_line = {\n        let mut file = BufReader::new(open_file(path)?);\n        let mut buf = String::new();\n        file.read_line(&mut buf)\n            .with_context(|| format!(\"failed to read first line from {path:?}\"))?;\n        buf.clear();\n        file.read_line(&mut buf)\n            .with_context(|| format!(\"failed to read second line from {path:?}\"))?;\n        buf\n    };\n    let expected_header_prefix = \"; Version: \";\n    let Some(version) = second_line\n        .strip_prefix(expected_header_prefix)\n        .map(str::trim)\n    else {\n        bail!(\"no {expected_header_prefix:?} header found in {path:?}\");\n    };\n    let mut spirv_as_cmd = EasyCommand::new(spirv_as, |cmd| {\n        cmd.stdout(Stdio::piped())\n            .arg(\"--target-env\")\n            .arg(format!(\"spv{version}\"))\n            .args([path.to_str().unwrap(), \"-o\", \"-\"])\n    });\n    let assembled_spirv = spirv_as_cmd\n        .output()\n        .with_context(|| format!(\"Failed to run {spirv_as_cmd}\"))?;\n\n    if !assembled_spirv.status.success() {\n        bail!(\n            \"Failed to assemble {path:?} with {spirv_as_cmd}:\\n{}\",\n            String::from_utf8_lossy(&assembled_spirv.stderr)\n        );\n    }\n\n    let error_message = || {\n        format!(\n            \"Failed to validate {path:?}.\nNote: Labels and line numbers will not match the input file.\n      Use this command to view the corresponding spvasm:\n      '{spirv_as} --target-env spv{version} {} -o - | spirv-dis'\\n\",\n            path.display(),\n        )\n    };\n    let mut spirv_val_command = EasyCommand::new(spirv_val, |cmd| cmd.stdin(Stdio::piped()));\n    let mut spirv_val_process = spirv_val_command\n        .spawn()\n        .with_context(|| format!(\"Failed to run {spirv_val_command}\"))?;\n\n    spirv_val_process\n        .stdin\n        .as_mut()\n        .unwrap()\n        .write_all(&assembled_spirv.stdout)?;\n\n    let spirv_val_output = spirv_val_process\n        .wait()\n        .with_context(|| format!(\"Failed to wait for {spirv_val_command}\"))?;\n\n    if !spirv_val_output.success() {\n        bail!(\"{}\", error_message());\n    }\n\n    Ok(())\n}\n\nfn validate_metal(path: &Path, xcrun: &str) -> anyhow::Result<()> {\n    let first_line = {\n        let mut file = BufReader::new(open_file(path)?);\n        let mut buf = String::new();\n        file.read_line(&mut buf)\n            .with_context(|| format!(\"failed to read header from {path:?}\"))?;\n        buf\n    };\n    let expected_header_prefix = \"// language: \";\n    let Some(language) = first_line.strip_prefix(expected_header_prefix) else {\n        bail!(\"no {expected_header_prefix:?} header found in {path:?}\");\n    };\n    let language = language.strip_suffix('\\n').unwrap_or(language);\n    let std_arg = if language.starts_with(\"metal1\") || language.starts_with(\"metal2\") {\n        format!(\"-std=macos-{language}\")\n    } else {\n        format!(\"-std={language}\")\n    };\n    let warnings_as_errors = [\"-Werror=constant-conversion\"];\n    EasyCommand::new(xcrun, |cmd| {\n        cmd.args([\"-sdk\", \"macosx\", \"metal\", \"-mmacosx-version-min=10.11\"])\n            .arg(std_arg)\n            .args(warnings_as_errors)\n            .args([\"-x\", \"metal\", &*path.to_string_lossy(), \"-o\", \"/dev/null\"])\n    })\n    .success()\n}\n\nfn validate_glsl(path: &Path, type_arg: &str, glslang_validator: &str) -> anyhow::Result<()> {\n    EasyCommand::new(glslang_validator, |cmd| {\n        cmd.args([&*path.to_string_lossy(), \"-S\"]).arg(type_arg)\n    })\n    .success()\n}\n\nfn validate_dot(path: &Path, dot: &str) -> anyhow::Result<()> {\n    let file = open_file(path)?;\n    EasyCommand::new(dot, |cmd| {\n        cmd.stdin(Stdio::from(file)).stdout(Stdio::null())\n    })\n    .success()\n}\n\nfn validate_wgsl(paths: &[std::path::PathBuf]) -> anyhow::Result<()> {\n    EasyCommand::new(\"cargo\", |cmd| {\n        cmd.args([\"run\", \"-p\", \"naga-cli\", \"--\", \"--bulk-validate\"])\n            .args(paths)\n    })\n    .success()\n}\n\nfn push_job_for_each_hlsl_config_item(\n    path: &Path,\n    bin: &str,\n    jobs: &mut Vec<Job>,\n    validator: impl FnMut(&Path, hlsl_snapshots::ConfigItem, &str) -> anyhow::Result<()>\n        + Clone\n        + Send\n        + std::panic::UnwindSafe\n        + 'static,\n) -> anyhow::Result<()> {\n    let hlsl_snapshots::Config {\n        vertex,\n        fragment,\n        compute,\n    } = hlsl_snapshots::Config::from_path(path.with_extension(\"ron\"))?;\n    for shader in [vertex, fragment, compute].into_iter().flatten() {\n        // Let each job closure stand on its own.\n        let mut validator = validator.clone();\n        let path = path.to_owned();\n        let bin = bin.to_owned();\n        jobs.push(Box::new(move || validator(&path, shader, &bin)));\n    }\n    Ok(())\n}\n\nfn validate_hlsl_with_dxc(\n    file: &Path,\n    config_item: hlsl_snapshots::ConfigItem,\n    dxc: &str,\n) -> anyhow::Result<()> {\n    // Reference:\n    // <https://github.com/microsoft/DirectXShaderCompiler/blob/6ee4074a4b43fa23bf5ad27e4f6cafc6b835e437/tools/clang/docs/UsingDxc.rst>.\n    validate_hlsl(\n        file,\n        dxc,\n        config_item,\n        &[\n            \"-Wno-parentheses-equality\",\n            \"-Zi\",\n            \"-Qembed_debug\",\n            \"-Od\",\n            \"-HV\",\n            \"2018\",\n        ],\n    )\n}\n\nfn validate_hlsl_with_fxc(\n    file: &Path,\n    config_item: hlsl_snapshots::ConfigItem,\n    fxc: &str,\n) -> anyhow::Result<()> {\n    let Some(Ok(shader_model_major_version)) = config_item\n        .target_profile\n        .split('_')\n        .nth(1)\n        .map(|segment| segment.parse::<u8>())\n    else {\n        bail!(\n            \"expected target profile of the form \\\n                 `{{model}}_{{major}}_{{minor}}`, found invalid target \\\n                 profile {:?} in file {}\",\n            config_item.target_profile,\n            file.display()\n        )\n    };\n    // NOTE: This isn't implemented by `fxc.exe`; see\n    // <https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-syntax#profiles>.\n    if shader_model_major_version < 6 {\n        // Reference:\n        // <https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-syntax>.\n        validate_hlsl(file, fxc, config_item, &[\"-Zi\", \"-Od\"])\n    } else {\n        log::debug!(\n            \"skipping config. item {config_item:?} because the \\\n             shader model major version is > 6\"\n        );\n        Ok(())\n    }\n}\n\nfn validate_hlsl(\n    file: &Path,\n    bin: &str,\n    config_item: hlsl_snapshots::ConfigItem,\n    params: &[&str],\n) -> anyhow::Result<()> {\n    let hlsl_snapshots::ConfigItem {\n        entry_point,\n        target_profile,\n    } = config_item;\n    EasyCommand::new(bin, |cmd| {\n        cmd.arg(file)\n            .arg(\"-T\")\n            .arg(&target_profile)\n            .arg(\"-E\")\n            .arg(&entry_point)\n            .args(params)\n            .stdout(Stdio::null())\n    })\n    .success()\n    .with_context(|| {\n        format!(\n            \"failed to validate entry point {entry_point:?} with profile \\\n                 {target_profile:?}\"\n        )\n    })\n}\n"
  },
  {
    "path": "naga-cli/Cargo.toml",
    "content": "[package]\nname = \"naga-cli\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"CLI for the naga shader translator and validator. Part of the wgpu project\"\nrepository.workspace = true\nkeywords = [\"shader\", \"SPIR-V\", \"GLSL\", \"MSL\"]\nlicense.workspace = true\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.76\"\n\n[[bin]]\nname = \"naga\"\npath = \"src/bin/naga.rs\"\n# This _must_ be false, as this conflicts with `naga`'s docs.\n#\n# See https://github.com/gfx-rs/wgpu/issues/4997\ndoc = false\ntest = false\n\n[dependencies]\nnaga = { workspace = true, features = [\n    \"wgsl-in\",\n    \"wgsl-out\",\n    \"glsl-in\",\n    \"glsl-out\",\n    \"spv-in\",\n    \"spv-out\",\n    \"msl-out\",\n    \"hlsl-out\",\n    \"dot-out\",\n    \"serialize\",\n    \"deserialize\",\n    \"termcolor\",\n    \"stderr\",\n    \"fs\",\n] }\n\nbincode = { workspace = true, features = [\"serde\"] }\ncodespan-reporting = { workspace = true, default-features = false, features = [\n    \"std\",\n    \"termcolor\",\n] }\nenv_logger.workspace = true\nargh.workspace = true\nanyhow = { workspace = true, features = [\"std\"] }\nlog.workspace = true\n"
  },
  {
    "path": "naga-cli/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "naga-cli/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "naga-cli/src/bin/naga.rs",
    "content": "use anyhow::{anyhow, Context as _};\nuse std::fs;\nuse std::{error::Error, fmt, io::Read, path::Path, str::FromStr};\n\n/// Translate shaders to different formats.\n#[derive(argh::FromArgs, Debug, Clone)]\nstruct Args {\n    /// bitmask of the ValidationFlags to be used, use 0 to disable validation\n    #[argh(option)]\n    validate: Option<u8>,\n\n    /// what policy to use for index bounds checking for arrays, vectors, and\n    /// matrices.\n    ///\n    /// May be `Restrict` (force all indices in-bounds), `ReadZeroSkipWrite`\n    /// (out-of-bounds indices read zeros, and don't write at all), or\n    /// `Unchecked` (generate the simplest code, and whatever happens, happens)\n    ///\n    /// `Unchecked` is the default.\n    #[argh(option)]\n    index_bounds_check_policy: Option<BoundsCheckPolicyArg>,\n\n    /// what policy to use for index bounds checking for arrays, vectors, and\n    /// matrices, when they are stored in globals in the `storage` or `uniform`\n    /// storage classes.\n    ///\n    /// Possible values are the same as for `index-bounds-check-policy`. If\n    /// omitted, defaults to the index bounds check policy.\n    #[argh(option)]\n    buffer_bounds_check_policy: Option<BoundsCheckPolicyArg>,\n\n    /// what policy to use for texture loads bounds checking.\n    ///\n    /// Possible values are the same as for `index-bounds-check-policy`. If\n    /// omitted, defaults to the index bounds check policy.\n    #[argh(option)]\n    image_load_bounds_check_policy: Option<BoundsCheckPolicyArg>,\n\n    /// directory to dump the SPIR-V block context dump to\n    #[argh(option)]\n    block_ctx_dir: Option<String>,\n\n    /// the shader entrypoint.\n    ///\n    /// When specified along with the `--compact` option, anything not reachable\n    /// from the selected entry point will not appear in the output module.\n    ///\n    /// When specified without the `--compact` option, alternate entry points\n    /// will not appear in the output module, and other declarations referenced\n    /// by alternate entrypoints may or may not appear, depending on whether\n    /// the module contains overrides.\n    #[argh(option)]\n    entry_point: Option<String>,\n\n    /// the shader profile to use, for example `es`, `core`, `es330`, if translating to GLSL\n    #[argh(option)]\n    profile: Option<GlslProfileArg>,\n\n    /// the shader model to use if targeting HLSL\n    ///\n    /// May be `50`, `51`, or `60`\n    #[argh(option)]\n    shader_model: Option<ShaderModelArg>,\n\n    /// the SPIR-V version to use if targeting SPIR-V\n    ///\n    /// For example, 1.0, 1.4, etc\n    #[argh(option)]\n    spirv_version: Option<SpirvVersionArg>,\n\n    /// the shader stage, for example 'frag', 'vert', or 'compute'.\n    /// if the shader stage is unspecified it will be derived from\n    /// the file extension.\n    #[argh(option)]\n    shader_stage: Option<ShaderStage>,\n\n    /// the kind of input, e.g. 'glsl', 'wgsl', 'spv', or 'bin'.\n    #[argh(option)]\n    input_kind: Option<InputKind>,\n\n    /// the metal version to use, for example, 1.0, 1.1, 1.2, etc.\n    #[argh(option)]\n    metal_version: Option<MslVersionArg>,\n\n    /// if the selected frontends/backends support coordinate space conversions,\n    /// disable them\n    #[argh(switch)]\n    keep_coordinate_space: bool,\n\n    /// in dot output, include only the control flow graph\n    #[argh(switch)]\n    dot_cfg_only: bool,\n\n    /// specify file path to process STDIN as\n    #[argh(option)]\n    stdin_file_path: Option<String>,\n\n    /// generate debug symbols, only works for spv-out for now\n    #[argh(switch, short = 'g')]\n    generate_debug_symbols: bool,\n\n    /// compact the module's IR and revalidate.\n    ///\n    /// Output files will reflect the compacted IR. If you want to see the IR as\n    /// it was before compaction, use the `--before-compaction` option.\n    ///\n    /// Even when this option is not active, compaction may still occur as part\n    /// of override processing.\n    #[argh(switch)]\n    compact: bool,\n\n    /// write the module's IR before compaction to the given file.\n    ///\n    /// This implies `--compact`. Like any other output file, the filename\n    /// extension determines the form in which the module is written.\n    #[argh(option)]\n    before_compaction: Option<String>,\n\n    /// bulk validation mode: all filenames are inputs to read and validate.\n    #[argh(switch)]\n    bulk_validate: bool,\n\n    /// show version\n    #[argh(switch)]\n    version: bool,\n\n    /// override value, of the form \"foo=N,bar=M\", repeatable\n    #[argh(option, long = \"override\")]\n    overrides: Vec<Overrides>,\n\n    /// the input and output files.\n    ///\n    /// First positional argument is the input file. If not specified, the\n    /// input will be read from stdin. In the case, --stdin-file-path must also\n    /// be specified.\n    ///\n    /// The rest arguments are the output files. If not specified, only\n    /// validation will be performed.\n    ///\n    /// In bulk validation mode, these are all input files to be validated.\n    #[argh(positional)]\n    files: Vec<String>,\n\n    /// defines to be passed to the parser (only glsl is supported)\n    #[argh(option, short = 'D')]\n    defines: Vec<Defines>,\n\n    /// capabilities for parsing and validation.\n    ///\n    /// Can be a comma-separated list of capability names (e.g.,\n    /// \"shader_float16,dual_source_blending\"), a numeric bitflags value (e.g.,\n    /// \"67108864\"), the string \"none\", or the string \"all\".\n    #[argh(option, default = \"CapabilitiesArg(naga::valid::Capabilities::all())\")]\n    capabilities: CapabilitiesArg,\n\n    /// the limits on the task shader dispatch size\n    #[argh(option, default = \"TaskDispatchLimitsArg(None)\")]\n    task_limits: TaskDispatchLimitsArg,\n\n    /// whether or not the mesh shader output should be validated.\n    #[argh(option, default = \"true\")]\n    validate_mesh_output: bool,\n}\n\n/// Newtype so we can implement [`FromStr`] for `Option<TaskDispatchLimits>`.\n#[derive(Debug, Clone, Copy)]\nstruct TaskDispatchLimitsArg(Option<naga::back::TaskDispatchLimits>);\n\nimpl FromStr for TaskDispatchLimitsArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let values = s\n            .split_once(\",\")\n            .ok_or_else(|| format!(\"No comma present for --task-limits value: {s}\"))?;\n        let x = values.0.parse::<u32>().map_err(|e| e.to_string())?;\n        let y = values.1.parse::<u32>().map_err(|e| e.to_string())?;\n        Ok(Self(Some(naga::back::TaskDispatchLimits {\n            max_mesh_workgroups_per_dim: x,\n            max_mesh_workgroups_total: y,\n        })))\n    }\n}\n\n/// Newtype so we can implement [`FromStr`] for `BoundsCheckPolicy`.\n#[derive(Debug, Clone, Copy)]\nstruct BoundsCheckPolicyArg(naga::proc::BoundsCheckPolicy);\n\nimpl FromStr for BoundsCheckPolicyArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use naga::proc::BoundsCheckPolicy;\n        Ok(Self(match s.to_lowercase().as_str() {\n            \"restrict\" => BoundsCheckPolicy::Restrict,\n            \"readzeroskipwrite\" => BoundsCheckPolicy::ReadZeroSkipWrite,\n            \"unchecked\" => BoundsCheckPolicy::Unchecked,\n            _ => {\n                return Err(format!(\n                    \"Invalid value for --index-bounds-check-policy: {s}\"\n                ))\n            }\n        }))\n    }\n}\n\n/// Newtype so we can implement [`FromStr`] for `ShaderModel`.\n#[derive(Debug, Clone)]\nstruct ShaderModelArg(naga::back::hlsl::ShaderModel);\n\nimpl FromStr for ShaderModelArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use naga::back::hlsl::ShaderModel;\n        Ok(Self(match s.to_lowercase().as_str() {\n            \"50\" => ShaderModel::V5_0,\n            \"51\" => ShaderModel::V5_1,\n            \"60\" => ShaderModel::V6_0,\n            \"61\" => ShaderModel::V6_1,\n            \"62\" => ShaderModel::V6_2,\n            \"63\" => ShaderModel::V6_3,\n            \"64\" => ShaderModel::V6_4,\n            \"65\" => ShaderModel::V6_5,\n            \"66\" => ShaderModel::V6_6,\n            \"67\" => ShaderModel::V6_7,\n            _ => return Err(format!(\"Invalid value for --shader-model: {s}\")),\n        }))\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct SpirvVersionArg(u8, u8);\n\nimpl FromStr for SpirvVersionArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let dot = s\n            .find(\".\")\n            .ok_or_else(|| \"Missing dot separator\".to_owned())?;\n        let major = s[..dot].parse::<u8>().map_err(|e| e.to_string())?;\n        let minor = s[dot + 1..].parse::<u8>().map_err(|e| e.to_string())?;\n        Ok(Self(major, minor))\n    }\n}\n\n/// Newtype so we can implement [`FromStr`] for `ShaderSource`.\n#[derive(Debug, Clone, Copy)]\nstruct ShaderStage(naga::ShaderStage);\n\nimpl FromStr for ShaderStage {\n    type Err = anyhow::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use naga::ShaderStage;\n        Ok(Self(match s.to_lowercase().as_str() {\n            \"frag\" | \"fragment\" => ShaderStage::Fragment,\n            \"comp\" | \"compute\" => ShaderStage::Compute,\n            \"vert\" | \"vertex\" => ShaderStage::Vertex,\n            _ => return Err(anyhow!(\"Invalid shader stage: {s}\")),\n        }))\n    }\n}\n\n/// Input kind/file extension mapping\n#[derive(Debug, Clone, Copy)]\nenum InputKind {\n    Bincode,\n    Glsl,\n    SpirV,\n    Wgsl,\n}\nimpl FromStr for InputKind {\n    type Err = anyhow::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(match s.to_lowercase().as_str() {\n            \"bin\" => InputKind::Bincode,\n            \"glsl\" => InputKind::Glsl,\n            \"spv\" => InputKind::SpirV,\n            \"wgsl\" => InputKind::Wgsl,\n            _ => return Err(anyhow!(\"Invalid value for --input-kind: {s}\")),\n        })\n    }\n}\n\n/// Newtype so we can implement [`FromStr`] for [`naga::back::glsl::Version`].\n#[derive(Clone, Debug)]\nstruct GlslProfileArg(naga::back::glsl::Version);\n\nimpl FromStr for GlslProfileArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use naga::back::glsl::Version;\n        Ok(Self(if let Some(s) = s.strip_prefix(\"core\") {\n            Version::Desktop(s.parse().unwrap_or(330))\n        } else if let Some(s) = s.strip_prefix(\"es\") {\n            Version::new_gles(s.parse().unwrap_or(310))\n        } else {\n            return Err(format!(\"Unknown profile: {s}\"));\n        }))\n    }\n}\n\n/// Newtype so we can implement [`FromStr`] for a Metal Language Version.\n#[derive(Clone, Debug)]\nstruct MslVersionArg((u8, u8));\n\nimpl FromStr for MslVersionArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut iter = s.split('.');\n\n        let check_value = |iter: &mut core::str::Split<_>| {\n            iter.next()\n                .ok_or_else(|| format!(\"Invalid value for --metal-version: {s}\"))?\n                .parse::<u8>()\n                .map_err(|err| format!(\"Invalid value for --metal-version: '{s}': {err}\"))\n        };\n\n        let major = check_value(&mut iter)?;\n        let minor = check_value(&mut iter)?;\n\n        Ok(Self((major, minor)))\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct Overrides {\n    pairs: Vec<(String, f64)>,\n}\n\nimpl FromStr for Overrides {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut pairs = vec![];\n        for pair in s.split(',') {\n            let Some((name, value)) = pair.split_once('=') else {\n                return Err(format!(\"value needs a `=`: {pair:?}\"));\n            };\n            let value = f64::from_str(value.trim()).map_err(|err| format!(\"{err}: {value:?}\"))?;\n            pairs.push((name.trim().to_string(), value));\n        }\n        Ok(Overrides { pairs })\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct Defines {\n    pairs: Vec<(String, String)>,\n}\n\nimpl FromStr for Defines {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut pairs = vec![];\n        for pair in s.split(',') {\n            let (name, value) = match pair.split_once('=') {\n                Some((name, value)) => (name, value),\n                None => (pair, \"\"), // Default to an empty string if no '=' is found\n            };\n            pairs.push((name.trim().to_string(), value.trim().to_string()));\n        }\n        Ok(Defines { pairs })\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct CapabilitiesArg(naga::valid::Capabilities);\n\nimpl FromStr for CapabilitiesArg {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use naga::valid::Capabilities;\n\n        let s = s.to_uppercase();\n\n        if s == \"NONE\" {\n            Ok(Self(Capabilities::empty()))\n        } else if s == \"ALL\" {\n            Ok(Self(Capabilities::all()))\n        } else if let Ok(bits) = s.parse::<u64>() {\n            Capabilities::from_bits(bits)\n                .map(Self)\n                .ok_or_else(|| format!(\"Invalid capabilities bitflags value: {bits}\"))\n        } else {\n            s.split(',')\n                .try_fold(Capabilities::empty(), |acc, s| {\n                    Capabilities::from_name(s.trim())\n                        .map(|cap| acc | cap)\n                        .ok_or(format!(\"Unknown capability {}\", s.trim()))\n                })\n                .map(Self)\n        }\n    }\n}\n\n#[derive(Default)]\nstruct Parameters<'a> {\n    validation_flags: naga::valid::ValidationFlags,\n    bounds_check_policies: naga::proc::BoundsCheckPolicies,\n    entry_point: Option<String>,\n    keep_coordinate_space: bool,\n    overrides: naga::back::PipelineConstants,\n    spv_in: naga::front::spv::Options,\n    spv_out: naga::back::spv::Options<'a>,\n    dot: naga::back::dot::Options,\n    msl: naga::back::msl::Options,\n    glsl: naga::back::glsl::Options,\n    hlsl: naga::back::hlsl::Options,\n    input_kind: Option<InputKind>,\n    shader_stage: Option<ShaderStage>,\n    defines: FastHashMap<String, String>,\n    capabilities: naga::valid::Capabilities,\n\n    /// We use this copy of `args.compact` to know whether we should pass the\n    /// entrypoint to `process_overrides`, which will result in removal from\n    /// the module of anything not reachable from that entry point.\n    ///\n    /// When we don't know an entrypoint, we still compact the module as a whole\n    /// if `args.compact` is set, but we don't use this copy for anything.\n    compact: bool,\n}\n\ntrait PrettyResult {\n    type Target;\n    fn unwrap_pretty(self) -> Self::Target;\n}\n\n#[cold]\n#[inline(never)]\nfn print_err(error: &dyn Error) {\n    eprint!(\"{error}\");\n\n    let mut e = error.source();\n    if e.is_some() {\n        eprintln!(\": \");\n    } else {\n        eprintln!();\n    }\n\n    while let Some(source) = e {\n        eprintln!(\"\\t{source}\");\n        e = source.source();\n    }\n}\n\nimpl<T, E: Error> PrettyResult for Result<T, E> {\n    type Target = T;\n    fn unwrap_pretty(self) -> T {\n        match self {\n            Result::Ok(value) => value,\n            Result::Err(error) => {\n                print_err(&error);\n                std::process::exit(1);\n            }\n        }\n    }\n}\n\nfn main() {\n    if let Err(e) = run() {\n        print_err(e.as_ref());\n        std::process::exit(1);\n    }\n}\n\n/// Error type for the CLI\n#[derive(Debug, Clone)]\nstruct CliError(&'static str);\nimpl fmt::Display for CliError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\nimpl std::error::Error for CliError {}\n\nfn run() -> anyhow::Result<()> {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Info)\n        .parse_default_env()\n        .init();\n\n    // Parse commandline arguments\n    let args = {\n        let mut args: Args = argh::from_env();\n\n        if args.before_compaction.is_some() {\n            args.compact = true;\n        }\n\n        args\n    };\n\n    if args.version {\n        println!(\"{}\", env!(\"CARGO_PKG_VERSION\"));\n        return Ok(());\n    }\n\n    // Initialize default parameters\n    //TODO: read the parameters from RON?\n    let mut params = Parameters::default();\n\n    // Update parameters from commandline arguments\n    if let Some(bits) = args.validate {\n        params.validation_flags = naga::valid::ValidationFlags::from_bits(bits)\n            .ok_or(CliError(\"Invalid validation flags\"))?;\n    }\n    if let Some(policy) = args.index_bounds_check_policy {\n        params.bounds_check_policies.index = policy.0;\n    }\n    params.bounds_check_policies.buffer = match args.buffer_bounds_check_policy {\n        Some(arg) => arg.0,\n        None => params.bounds_check_policies.index,\n    };\n    params.bounds_check_policies.image_load = match args.image_load_bounds_check_policy {\n        Some(arg) => arg.0,\n        None => params.bounds_check_policies.index,\n    };\n    params.overrides = args\n        .overrides\n        .iter()\n        .flat_map(|o| &o.pairs)\n        .cloned()\n        .collect();\n\n    params.defines = args\n        .defines\n        .iter()\n        .flat_map(|o| &o.pairs)\n        .cloned()\n        .collect();\n\n    params.spv_in = naga::front::spv::Options {\n        adjust_coordinate_space: !args.keep_coordinate_space,\n        strict_capabilities: false,\n        block_ctx_dump_prefix: args.block_ctx_dir.clone(),\n    };\n\n    params.entry_point.clone_from(&args.entry_point);\n    if let Some(ref version) = args.profile {\n        params.glsl.version = version.0;\n    }\n    if let Some(ref model) = args.shader_model {\n        params.hlsl.shader_model = model.0;\n    }\n    if let Some(ref version) = args.metal_version {\n        params.msl.lang_version = version.0;\n    }\n    if let Some(ref version) = args.spirv_version {\n        params.spv_out.lang_version = (version.0, version.1);\n    }\n    params.keep_coordinate_space = args.keep_coordinate_space;\n\n    params.dot.cfg_only = args.dot_cfg_only;\n\n    params.spv_out.bounds_check_policies = params.bounds_check_policies;\n    params.spv_out.flags.set(\n        naga::back::spv::WriterFlags::ADJUST_COORDINATE_SPACE,\n        !params.keep_coordinate_space,\n    );\n    params.glsl.writer_flags.set(\n        naga::back::glsl::WriterFlags::ADJUST_COORDINATE_SPACE,\n        !params.keep_coordinate_space,\n    );\n\n    params.compact = args.compact;\n    params.capabilities = args.capabilities.0;\n\n    params.spv_out.mesh_shader_primitive_indices_clamp = args.validate_mesh_output;\n    params.spv_out.task_dispatch_limits = args.task_limits.0;\n\n    if args.bulk_validate {\n        return bulk_validate(&args, &params);\n    }\n\n    let mut files = args.files.iter();\n\n    let (input_path, input) = if let Some(path) = args.stdin_file_path.as_ref() {\n        let mut input = vec![];\n        std::io::stdin().lock().read_to_end(&mut input)?;\n        (Path::new(path), input)\n    } else if let Some(path) = files.next() {\n        let path = Path::new(path);\n        (path, fs::read(path)?)\n    } else {\n        return Err(CliError(\"Input file path is not specified\").into());\n    };\n\n    let file_name = input_path.to_string_lossy();\n\n    params.input_kind = args.input_kind;\n    params.shader_stage = args.shader_stage;\n\n    let Parsed {\n        mut module,\n        input_text,\n        language,\n    } = parse_input(input_path, input, &params)?;\n\n    // Include debugging information if requested.\n    if args.generate_debug_symbols {\n        if let Some(ref input_text) = input_text {\n            params\n                .spv_out\n                .flags\n                .set(naga::back::spv::WriterFlags::DEBUG, true);\n            params.spv_out.debug_info = Some(naga::back::spv::DebugInfo {\n                source_code: input_text,\n                file_name: &file_name,\n                language,\n            })\n        } else {\n            eprintln!(\n                \"warning: `--generate-debug-symbols` was passed, \\\n                       but input is not human-readable: {}\",\n                input_path.display()\n            );\n        }\n    }\n\n    let output_paths = files;\n\n    // Decide which capabilities our output formats can support.\n    let validation_caps = output_paths\n        .clone()\n        .fold(params.capabilities, |caps, path| {\n            use naga::valid::Capabilities as C;\n            let allowed = match Path::new(path).extension().and_then(|ex| ex.to_str()) {\n                Some(\"wgsl\") => naga::back::wgsl::supported_capabilities(),\n                Some(\"metal\") => naga::back::msl::supported_capabilities(),\n                Some(\"hlsl\") => naga::back::hlsl::supported_capabilities(),\n                Some(\"spv\") | Some(\"spirv\") => naga::back::spv::supported_capabilities(),\n                Some(\"glsl\") | Some(\"frag\") | Some(\"vert\") | Some(\"comp\") | Some(\"task\")\n                | Some(\"mesh\") => naga::back::glsl::supported_capabilities(),\n                _ => C::all() - C::TEXTURE_EXTERNAL,\n            };\n            caps & allowed\n        });\n\n    // Validate the IR before compaction.\n    let info = match naga::valid::Validator::new(params.validation_flags, validation_caps)\n        .subgroup_stages(naga::valid::ShaderStages::all())\n        .subgroup_operations(naga::valid::SubgroupOperationSet::all())\n        .validate(&module)\n    {\n        Ok(info) => Some(info),\n        Err(error) => {\n            // Validation failure is not fatal. Just report the error.\n            if let Some(input) = &input_text {\n                let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str);\n                error.emit_to_stderr_with_path(input, filename.unwrap_or(\"input\"));\n            } else {\n                print_err(&error);\n            }\n            None\n        }\n    };\n\n    // Compact the module, if requested.\n    //\n    // Note that when output is to a non-WGSL shader language, we will call\n    // `process_overrides`, which does its own compaction even if it is not\n    // explicitly requested on the command line.\n    let info = if args.compact {\n        // Compact only if validation succeeded. Otherwise, compaction may panic.\n        if info.is_some() {\n            // Write out the module state before compaction, if requested.\n            if let Some(ref before_compaction) = args.before_compaction {\n                write_output(&module, &info, &params, before_compaction)?;\n            }\n\n            naga::compact::compact(&mut module, KeepUnused::No);\n\n            // Re-validate the IR after compaction.\n            match naga::valid::Validator::new(params.validation_flags, validation_caps)\n                .validate(&module)\n            {\n                Ok(info) => Some(info),\n                Err(error) => {\n                    // Validation failure is not fatal. Just report the error.\n                    eprintln!(\"Error validating compacted module:\");\n                    if let Some(input) = &input_text {\n                        let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str);\n                        error.emit_to_stderr_with_path(input, filename.unwrap_or(\"input\"));\n                    } else {\n                        print_err(&error);\n                    }\n                    None\n                }\n            }\n        } else {\n            eprintln!(\"Skipping compaction due to validation failure.\");\n            None\n        }\n    } else {\n        info\n    };\n\n    // If no output was requested, then report validation results and stop here.\n    //\n    // If the user asked for output, don't stop: some output formats (\".txt\",\n    // \".dot\", \".bin\") can be generated even without a `ModuleInfo`.\n    if output_paths.clone().next().is_none() {\n        if info.is_some() {\n            println!(\"Validation successful\");\n            return Ok(());\n        } else {\n            std::process::exit(-1);\n        }\n    }\n\n    for output_path in output_paths {\n        write_output(&module, &info, &params, output_path)?;\n    }\n\n    Ok(())\n}\n\nstruct Parsed {\n    module: naga::Module,\n    input_text: Option<String>,\n    language: naga::back::spv::SourceLanguage,\n}\n\nfn parse_input(input_path: &Path, input: Vec<u8>, params: &Parameters) -> anyhow::Result<Parsed> {\n    let input_kind = match params.input_kind {\n        Some(kind) => kind,\n        None => input_path\n            .extension()\n            .context(\"Input filename has no extension\")?\n            .to_str()\n            .context(\"Input filename not valid unicode\")?\n            .parse()\n            .context(\"Unable to determine --input-kind from filename\")?,\n    };\n\n    Ok(match input_kind {\n        InputKind::Bincode => Parsed {\n            module: bincode::serde::decode_from_slice(&input, bincode::config::standard())?.0,\n            input_text: None,\n            language: naga::back::spv::SourceLanguage::Unknown,\n        },\n        InputKind::SpirV => Parsed {\n            module: naga::front::spv::parse_u8_slice(&input, &params.spv_in)?,\n            input_text: None,\n            language: naga::back::spv::SourceLanguage::Unknown,\n        },\n        InputKind::Wgsl => {\n            let input = String::from_utf8(input)?;\n            let options = naga::front::wgsl::Options {\n                parse_doc_comments: false,\n                capabilities: params.capabilities,\n            };\n            let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);\n            let result = frontend.parse(&input);\n            match result {\n                Ok(v) => Parsed {\n                    module: v,\n                    input_text: Some(input),\n                    language: naga::back::spv::SourceLanguage::WGSL,\n                },\n                Err(ref e) => {\n                    let message = anyhow!(\n                        \"Could not parse WGSL:\\n{}\",\n                        e.emit_to_string_with_path(&input, input_path)\n                    );\n                    return Err(message);\n                }\n            }\n        }\n        InputKind::Glsl => {\n            let shader_stage = match params.shader_stage {\n                Some(shader_stage) => shader_stage,\n                None => {\n                    // filename.shader_stage.glsl -> filename.shader_stage\n                    let file_stem = input_path\n                        .file_stem()\n                        .context(\"Unable to determine file stem from input filename.\")?;\n                    // filename.shader_stage -> shader_stage\n                    let inner_ext = Path::new(file_stem)\n                        .extension()\n                        .context(\"Unable to determine inner extension from input filename.\")?\n                        .to_str()\n                        .context(\"Input filename not valid unicode\")?;\n                    inner_ext.parse().context(\"from input filename\")?\n                }\n            };\n            let input = String::from_utf8(input)?;\n            let mut parser = naga::front::glsl::Frontend::default();\n            Parsed {\n                module: parser\n                    .parse(\n                        &naga::front::glsl::Options {\n                            stage: shader_stage.0,\n                            defines: params.defines.clone(),\n                        },\n                        &input,\n                    )\n                    .unwrap_or_else(|error| {\n                        let filename = input_path\n                            .file_name()\n                            .and_then(std::ffi::OsStr::to_str)\n                            .unwrap_or(\"glsl\");\n                        let mut writer = StandardStream::stderr(ColorChoice::Auto);\n                        error.emit_to_writer_with_path(&mut writer, &input, filename);\n                        std::process::exit(1);\n                    }),\n                input_text: Some(input),\n                language: naga::back::spv::SourceLanguage::GLSL,\n            }\n        }\n    })\n}\n\nfn write_output(\n    module: &naga::Module,\n    info: &Option<naga::valid::ModuleInfo>,\n    params: &Parameters,\n    output_path: &str,\n) -> anyhow::Result<()> {\n    let entry_point = params.entry_point.as_deref().map(|name| {\n        let ep_index = module\n            .entry_points\n            .iter()\n            .position(|ep| ep.name == *name)\n            .expect(\"Unable to find the entry point\");\n\n        (module.entry_points[ep_index].stage, name)\n    });\n\n    match Path::new(&output_path)\n        .extension()\n        .ok_or(CliError(\"Output filename has no extension\"))?\n        .to_str()\n        .ok_or(CliError(\"Output filename not valid unicode\"))?\n    {\n        \"txt\" => {\n            use std::io::Write;\n\n            let mut file = fs::File::create(output_path)?;\n            writeln!(file, \"{module:#?}\")?;\n            if let Some(ref info) = *info {\n                writeln!(file)?;\n                writeln!(file, \"{info:#?}\")?;\n            }\n        }\n        \"bin\" => {\n            let mut file = fs::File::create(output_path)?;\n            bincode::serde::encode_into_std_write(module, &mut file, bincode::config::standard())?;\n        }\n        \"metal\" => {\n            use naga::back::msl;\n\n            let mut options = params.msl.clone();\n            options.bounds_check_policies = params.bounds_check_policies;\n\n            let info = info.as_ref().ok_or(CliError(\n                \"Generating metal output requires validation to \\\n                 succeed, and it failed in a previous step\",\n            ))?;\n\n            let (module, info) = naga::back::pipeline_constants::process_overrides(\n                module,\n                info,\n                entry_point.filter(|_| params.compact),\n                &params.overrides,\n            )\n            .unwrap_pretty();\n\n            let pipeline_options = msl::PipelineOptions::default();\n            let (msl, _) =\n                msl::write_string(&module, &info, &options, &pipeline_options).unwrap_pretty();\n            fs::write(output_path, msl)?;\n        }\n        \"spv\" => {\n            use naga::back::spv;\n\n            let pipeline_options = entry_point.map(|(shader_stage, name)| spv::PipelineOptions {\n                entry_point: name.to_owned(),\n                shader_stage,\n            });\n\n            let info = info.as_ref().ok_or(CliError(\n                \"Generating SPIR-V output requires validation to \\\n                 succeed, and it failed in a previous step\",\n            ))?;\n\n            let (module, info) = naga::back::pipeline_constants::process_overrides(\n                module,\n                info,\n                entry_point.filter(|_| params.compact),\n                &params.overrides,\n            )\n            .unwrap_pretty();\n\n            let spv = spv::write_vec(&module, &info, &params.spv_out, pipeline_options.as_ref())\n                .unwrap_pretty();\n            let bytes = spv\n                .iter()\n                .fold(Vec::with_capacity(spv.len() * 4), |mut v, w| {\n                    v.extend_from_slice(&w.to_le_bytes());\n                    v\n                });\n\n            fs::write(output_path, bytes.as_slice())?;\n        }\n        stage @ (\"vert\" | \"frag\" | \"comp\") => {\n            use naga::back::glsl;\n\n            let file_ext_stage = match stage {\n                \"vert\" => naga::ShaderStage::Vertex,\n                \"frag\" => naga::ShaderStage::Fragment,\n                \"comp\" => naga::ShaderStage::Compute,\n                _ => unreachable!(),\n            };\n\n            let (ep_stage, ep_name) = match entry_point {\n                Some((stage, name)) => {\n                    if stage != file_ext_stage {\n                        eprintln!(\n                            \"warning: the shader stage `{stage:?}` of the selected entry point \\\n                                `{name}` in the input file does not match the shader stage \\\n                                implied by the file name\",\n                        );\n                    }\n                    (stage, name.to_string())\n                }\n                _ => (file_ext_stage, \"main\".to_string()),\n            };\n\n            let pipeline_options = glsl::PipelineOptions {\n                entry_point: ep_name,\n                shader_stage: ep_stage,\n                multiview: None,\n            };\n\n            let info = info.as_ref().ok_or(CliError(\n                \"Generating glsl output requires validation to \\\n                 succeed, and it failed in a previous step\",\n            ))?;\n\n            let (module, info) = naga::back::pipeline_constants::process_overrides(\n                module,\n                info,\n                entry_point.filter(|_| params.compact),\n                &params.overrides,\n            )\n            .unwrap_pretty();\n\n            let mut buffer = String::new();\n            let mut writer = glsl::Writer::new(\n                &mut buffer,\n                &module,\n                &info,\n                &params.glsl,\n                &pipeline_options,\n                params.bounds_check_policies,\n            )\n            .unwrap_pretty();\n            writer.write()?;\n            fs::write(output_path, buffer)?;\n        }\n        \"dot\" => {\n            use naga::back::dot;\n\n            let output = dot::write(module, info.as_ref(), params.dot.clone())?;\n            fs::write(output_path, output)?;\n        }\n        \"hlsl\" => {\n            use naga::back::hlsl;\n\n            let info = info.as_ref().ok_or(CliError(\n                \"Generating hlsl output requires validation to \\\n                 succeed, and it failed in a previous step\",\n            ))?;\n\n            let (module, info) = naga::back::pipeline_constants::process_overrides(\n                module,\n                info,\n                entry_point.filter(|_| params.compact),\n                &params.overrides,\n            )\n            .unwrap_pretty();\n\n            let mut buffer = String::new();\n            let pipeline_options = Default::default();\n            let mut writer = hlsl::Writer::new(&mut buffer, &params.hlsl, &pipeline_options);\n            writer.write(&module, &info, None).unwrap_pretty();\n            fs::write(output_path, buffer)?;\n        }\n        \"wgsl\" => {\n            use naga::back::wgsl;\n\n            let wgsl = wgsl::write_string(\n                module,\n                info.as_ref().ok_or(CliError(\n                    \"Generating wgsl output requires validation to \\\n                     succeed, and it failed in a previous step\",\n                ))?,\n                wgsl::WriterFlags::empty(),\n            )\n            .unwrap_pretty();\n            fs::write(output_path, wgsl)?;\n        }\n        other => {\n            println!(\"Unknown output extension: {other}\");\n        }\n    }\n\n    Ok(())\n}\n\nfn bulk_validate(args: &Args, params: &Parameters) -> anyhow::Result<()> {\n    let mut invalid = vec![];\n    for input_path in &args.files {\n        let path = Path::new(&input_path);\n        let input = fs::read(path)?;\n\n        let Parsed {\n            module,\n            input_text,\n            language: _,\n        } = match parse_input(path, input, params) {\n            Ok(parsed) => parsed,\n            Err(error) => {\n                invalid.push(input_path.clone());\n                eprintln!(\"Error validating {input_path}:\");\n                eprintln!(\"{error}\");\n                continue;\n            }\n        };\n\n        let mut validator =\n            naga::valid::Validator::new(params.validation_flags, params.capabilities);\n        validator.subgroup_stages(naga::valid::ShaderStages::all());\n        validator.subgroup_operations(naga::valid::SubgroupOperationSet::all());\n\n        if let Err(error) = validator.validate(&module) {\n            invalid.push(input_path.clone());\n            eprintln!(\"Error validating {input_path}:\");\n            if let Some(input) = &input_text {\n                let filename = path.file_name().and_then(std::ffi::OsStr::to_str);\n                error.emit_to_stderr_with_path(input, filename.unwrap_or(\"input\"));\n            } else {\n                print_err(&error);\n            }\n        }\n    }\n\n    if !invalid.is_empty() {\n        use std::fmt::Write;\n        let mut formatted = String::new();\n        writeln!(\n            &mut formatted,\n            \"Validation failed for the following inputs:\"\n        )\n        .unwrap();\n        for path in invalid {\n            writeln!(&mut formatted, \"  {path}\").unwrap();\n        }\n        return Err(anyhow!(formatted));\n    }\n\n    Ok(())\n}\n\nuse codespan_reporting::term::termcolor::{ColorChoice, StandardStream};\nuse naga::{compact::KeepUnused, FastHashMap};\n"
  },
  {
    "path": "naga-test/Cargo.toml",
    "content": "[package]\nname = \"naga-test\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"common code for naga tests\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\npublish = false\n\n\n[features]\n\n[dependencies]\nnaga = { workspace = true, features = [\n    \"serialize\",\n    \"deserialize\",\n    \"glsl-in\",\n    \"glsl-out\",\n    \"spv-in\",\n    \"spv-out\",\n    \"wgsl-in\",\n    \"wgsl-out\",\n    \"msl-out\",\n    \"dot-out\",\n    \"hlsl-out\",\n] }\nspirv = { workspace = true, features = [\"deserialize\"] }\nron.workspace = true\ntoml.workspace = true\nbitflags.workspace = true\nserde_json.workspace = true\nserde.workspace = true\n"
  },
  {
    "path": "naga-test/src/lib.rs",
    "content": "// A lot of the code can be unused based on configuration flags,\n// the corresponding warnings aren't helpful.\n#![allow(dead_code, unused_imports)]\n\nuse core::fmt::Write;\n\nuse std::{\n    fs,\n    path::{Path, PathBuf},\n};\n\nuse naga::compact::KeepUnused;\nuse ron::de;\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, serde::Deserialize)]\n    #[serde(transparent)]\n    #[derive(Debug, Eq, PartialEq)]\n    pub struct Targets: u32 {\n        /// A serialization of the `naga::Module`, in RON format.\n        const IR = 1;\n\n        /// A serialization of the `naga::valid::ModuleInfo`, in RON format.\n        const ANALYSIS = 1 << 1;\n\n        const SPIRV = 1 << 2;\n        const METAL = 1 << 3;\n        const GLSL = 1 << 4;\n        const DOT = 1 << 5;\n        const HLSL = 1 << 6;\n        const WGSL = 1 << 7;\n        const NO_VALIDATION = 1 << 8;\n    }\n}\n\nimpl Targets {\n    /// Defaults for `spv` and `glsl` snapshots.\n    pub fn non_wgsl_default() -> Self {\n        Targets::WGSL\n    }\n\n    /// Defaults for `wgsl` snapshots.\n    pub fn wgsl_default() -> Self {\n        Targets::HLSL | Targets::SPIRV | Targets::GLSL | Targets::METAL | Targets::WGSL\n    }\n}\n\n#[derive(serde::Deserialize)]\npub struct SpvOutVersion(pub u8, pub u8);\nimpl Default for SpvOutVersion {\n    fn default() -> Self {\n        SpvOutVersion(1, 1)\n    }\n}\n\n#[derive(serde::Deserialize)]\npub struct BindingMapSerialization {\n    pub resource_binding: naga::ResourceBinding,\n    pub bind_target: naga::back::spv::BindingInfo,\n}\n\npub fn deserialize_binding_map<'de, D>(\n    deserializer: D,\n) -> Result<naga::back::spv::BindingMap, D::Error>\nwhere\n    D: serde::Deserializer<'de>,\n{\n    use serde::Deserialize;\n\n    let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;\n    let mut map = naga::back::spv::BindingMap::default();\n    for item in vec {\n        map.insert(item.resource_binding, item.bind_target);\n    }\n    Ok(map)\n}\n\n#[derive(Default, serde::Deserialize)]\n#[serde(default)]\npub struct WriterSharedOptions {\n    pub mesh_output_validation: bool,\n    pub task_limits: Option<naga::back::TaskDispatchLimits>,\n    pub bounds_checks_policies: naga::proc::BoundsCheckPolicies,\n}\n\n#[derive(Default, serde::Deserialize)]\n#[serde(default)]\npub struct WgslInParameters {\n    pub parse_doc_comments: bool,\n}\nimpl From<&WgslInParameters> for naga::front::wgsl::Options {\n    fn from(value: &WgslInParameters) -> Self {\n        Self {\n            parse_doc_comments: value.parse_doc_comments,\n            capabilities: naga::valid::Capabilities::all(),\n        }\n    }\n}\n\n#[derive(Default, serde::Deserialize)]\n#[serde(default)]\npub struct SpirvInParameters {\n    pub adjust_coordinate_space: bool,\n}\nimpl From<&SpirvInParameters> for naga::front::spv::Options {\n    fn from(value: &SpirvInParameters) -> Self {\n        Self {\n            adjust_coordinate_space: value.adjust_coordinate_space,\n            ..Default::default()\n        }\n    }\n}\n\n#[derive(serde::Deserialize)]\n#[serde(default)]\npub struct SpirvOutParameters {\n    pub version: SpvOutVersion,\n    pub capabilities: naga::FastHashSet<spirv::Capability>,\n    pub debug: bool,\n    pub adjust_coordinate_space: bool,\n    pub force_point_size: bool,\n    pub clamp_frag_depth: bool,\n    pub separate_entry_points: bool,\n    #[serde(deserialize_with = \"deserialize_binding_map\")]\n    pub binding_map: naga::back::spv::BindingMap,\n    pub ray_query_initialization_tracking: bool,\n    pub use_storage_input_output_16: bool,\n}\nimpl Default for SpirvOutParameters {\n    fn default() -> Self {\n        Self {\n            version: SpvOutVersion::default(),\n            capabilities: naga::FastHashSet::default(),\n            debug: false,\n            adjust_coordinate_space: false,\n            force_point_size: false,\n            clamp_frag_depth: false,\n            separate_entry_points: false,\n            ray_query_initialization_tracking: true,\n            use_storage_input_output_16: true,\n            binding_map: naga::back::spv::BindingMap::default(),\n        }\n    }\n}\nimpl SpirvOutParameters {\n    pub fn to_options<'a>(\n        &'a self,\n        shared_info: &WriterSharedOptions,\n        debug_info: Option<naga::back::spv::DebugInfo<'a>>,\n    ) -> naga::back::spv::Options<'a> {\n        use naga::back::spv;\n        let mut flags = spv::WriterFlags::LABEL_VARYINGS;\n        flags.set(spv::WriterFlags::DEBUG, self.debug);\n        flags.set(\n            spv::WriterFlags::ADJUST_COORDINATE_SPACE,\n            self.adjust_coordinate_space,\n        );\n        flags.set(spv::WriterFlags::FORCE_POINT_SIZE, self.force_point_size);\n        flags.set(spv::WriterFlags::CLAMP_FRAG_DEPTH, self.clamp_frag_depth);\n        naga::back::spv::Options {\n            lang_version: (self.version.0, self.version.1),\n            flags,\n            capabilities: if self.capabilities.is_empty() {\n                None\n            } else {\n                Some(self.capabilities.clone())\n            },\n            bounds_check_policies: shared_info.bounds_checks_policies,\n            fake_missing_bindings: true,\n            binding_map: self.binding_map.clone(),\n            zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill,\n            force_loop_bounding: true,\n            ray_query_initialization_tracking: true,\n            debug_info,\n            use_storage_input_output_16: self.use_storage_input_output_16,\n            task_dispatch_limits: shared_info.task_limits,\n            mesh_shader_primitive_indices_clamp: shared_info.mesh_output_validation,\n        }\n    }\n}\n\n#[derive(Default, serde::Deserialize)]\n#[serde(default)]\npub struct WgslOutParameters {\n    pub explicit_types: bool,\n}\nimpl From<&WgslOutParameters> for naga::back::wgsl::WriterFlags {\n    fn from(value: &WgslOutParameters) -> Self {\n        let mut flags = Self::empty();\n        flags.set(Self::EXPLICIT_TYPES, value.explicit_types);\n        flags\n    }\n}\n\n#[derive(Default, serde::Deserialize)]\npub struct FragmentModule {\n    pub path: String,\n    pub entry_point: String,\n}\n\n#[derive(Default, serde::Deserialize)]\n#[serde(default)]\npub struct Parameters {\n    // -- validation options --\n    //\n    // Capabilities to enable. Defaults to `Capabilities::default()`.\n    pub capabilities: Option<naga::valid::Capabilities>,\n\n    // -- wgsl-in options --\n    #[serde(rename = \"wgsl-in\")]\n    pub wgsl_in: WgslInParameters,\n\n    // -- spirv-in options --\n    #[serde(rename = \"spv-in\")]\n    pub spv_in: SpirvInParameters,\n\n    // -- SPIR-V options --\n    pub spv: SpirvOutParameters,\n\n    /// Defaults to [`Targets::non_wgsl_default()`] for `spv` and `glsl` snapshots,\n    /// and [`Targets::wgsl_default()`] for `wgsl` snapshots.\n    pub targets: Option<Targets>,\n\n    // -- MSL options --\n    pub msl: naga::back::msl::Options,\n    #[serde(default)]\n    pub msl_pipeline: naga::back::msl::PipelineOptions,\n\n    // -- GLSL options --\n    pub glsl: naga::back::glsl::Options,\n    pub glsl_exclude_list: naga::FastHashSet<String>,\n    pub glsl_multiview: Option<core::num::NonZeroU32>,\n\n    // -- HLSL options --\n    pub hlsl: naga::back::hlsl::Options,\n\n    // -- WGSL options --\n    pub wgsl: WgslOutParameters,\n\n    // -- General options --\n\n    // Allow backends to be aware of the fragment module.\n    // Is the name of a WGSL file in the same directory as the test file.\n    pub fragment_module: Option<FragmentModule>,\n\n    pub bounds_check_policies: naga::proc::BoundsCheckPolicies,\n    pub pipeline_constants: naga::back::PipelineConstants,\n\n    pub mesh_output_validation: bool,\n    #[serde(default = \"default_task_limits\")]\n    pub task_limits: Option<naga::back::TaskDispatchLimits>,\n}\n\nfn default_task_limits() -> Option<naga::back::TaskDispatchLimits> {\n    Some(naga::back::TaskDispatchLimits {\n        max_mesh_workgroups_per_dim: 256,\n        max_mesh_workgroups_total: 1024,\n    })\n}\n\n/// Information about a shader input file.\n#[derive(Debug)]\npub struct Input {\n    /// The subdirectory of `tests/in` to which this input belongs, if any.\n    ///\n    /// If the subdirectory is omitted, we assume that the output goes\n    /// to \"wgsl\".\n    pub subdirectory: PathBuf,\n\n    /// The input filename name, without a directory.\n    pub file_name: PathBuf,\n\n    /// True if output filenames should add the output extension on top of\n    /// `file_name`'s existing extension, rather than replacing it.\n    ///\n    /// This is used by `convert_snapshots_glsl`, which wants to take input files\n    /// like `210-bevy-2d-shader.frag` and just add `.wgsl` to it, producing\n    /// `210-bevy-2d-shader.frag.wgsl`.\n    pub keep_input_extension: bool,\n}\n\nimpl Input {\n    /// Read an input file and its corresponding parameters file.\n    ///\n    /// Given `input`, the relative path of a shader input file, return\n    /// a `Source` value containing its path, code, and parameters.\n    ///\n    /// The `input` path is interpreted relative to the `BASE_DIR_IN`\n    /// subdirectory of the directory given by the `CARGO_MANIFEST_DIR`\n    /// environment variable.\n    pub fn new(subdirectory: &str, name: &str, extension: &str) -> Input {\n        Input {\n            subdirectory: PathBuf::from(subdirectory),\n            // Don't wipe out any extensions on `name`, as\n            // `with_extension` would do.\n            file_name: PathBuf::from(format!(\"{name}.{extension}\")),\n            keep_input_extension: false,\n        }\n    }\n\n    /// Return an iterator that produces an `Input` for each entry in `subdirectory`.\n    pub fn files_in_dir<'a>(\n        subdirectory: &'a str,\n        file_extensions: &'a [&'a str],\n        dir_in: &str,\n    ) -> impl Iterator<Item = Input> + 'a {\n        let input_directory = Path::new(dir_in).join(subdirectory);\n\n        let entries = match std::fs::read_dir(&input_directory) {\n            Ok(entries) => entries,\n            Err(err) => panic!(\n                \"Error opening directory '{}': {}\",\n                input_directory.display(),\n                err\n            ),\n        };\n\n        entries.filter_map(move |result| {\n            let entry = result.expect(\"error reading directory\");\n            if !entry.file_type().unwrap().is_file() {\n                return None;\n            }\n\n            let file_name = PathBuf::from(entry.file_name());\n            let extension = file_name\n                .extension()\n                .expect(\"all files in snapshot input directory should have extensions\");\n\n            if !file_extensions.contains(&extension.to_str().unwrap()) {\n                return None;\n            }\n\n            if let Ok(pat) = std::env::var(\"NAGA_SNAPSHOT\") {\n                if !file_name.to_string_lossy().contains(&pat) {\n                    return None;\n                }\n            }\n\n            let input = Input::new(\n                subdirectory,\n                file_name.file_stem().unwrap().to_str().unwrap(),\n                extension.to_str().unwrap(),\n            );\n            Some(input)\n        })\n    }\n\n    /// Return the path to the input directory.\n    pub fn input_directory(&self, dir_in: &str) -> PathBuf {\n        Path::new(dir_in).join(&self.subdirectory)\n    }\n\n    /// Return the path to the output directory.\n    pub fn output_directory(subdirectory: &str, dir_out: &str) -> PathBuf {\n        Path::new(dir_out).join(subdirectory)\n    }\n\n    /// Return the path to the input file.\n    pub fn input_path(&self, dir_in: &str) -> PathBuf {\n        let mut input = self.input_directory(dir_in);\n        input.push(&self.file_name);\n        input\n    }\n\n    pub fn output_path(&self, subdirectory: &str, extension: &str, dir_out: &str) -> PathBuf {\n        let mut output = Self::output_directory(subdirectory, dir_out);\n        if self.keep_input_extension {\n            let file_name = format!(\n                \"{}-{}.{}\",\n                self.subdirectory.display(),\n                self.file_name.display(),\n                extension\n            );\n\n            output.push(&file_name);\n        } else {\n            let file_name = format!(\n                \"{}-{}\",\n                self.subdirectory.display(),\n                self.file_name.display()\n            );\n\n            output.push(&file_name);\n            output.set_extension(extension);\n        }\n        output\n    }\n\n    /// Return the contents of the input file as a string.\n    pub fn read_source(&self, dir_in: &str, print: bool) -> String {\n        if print {\n            println!(\"Processing '{}'\", self.file_name.display());\n        }\n        let input_path = self.input_path(dir_in);\n        match fs::read_to_string(&input_path) {\n            Ok(source) => source,\n            Err(err) => {\n                panic!(\n                    \"Couldn't read shader input file `{}`: {}\",\n                    input_path.display(),\n                    err\n                );\n            }\n        }\n    }\n\n    /// Return the contents of the input file as a vector of bytes.\n    pub fn read_bytes(&self, dir_in: &str, print: bool) -> Vec<u8> {\n        if print {\n            println!(\"Processing '{}'\", self.file_name.display());\n        }\n        let input_path = self.input_path(dir_in);\n        match fs::read(&input_path) {\n            Ok(bytes) => bytes,\n            Err(err) => {\n                panic!(\n                    \"Couldn't read shader input file `{}`: {}\",\n                    input_path.display(),\n                    err\n                );\n            }\n        }\n    }\n\n    pub fn bytes(&self, dir_in: &str) -> u64 {\n        let input_path = self.input_path(dir_in);\n        std::fs::metadata(input_path).unwrap().len()\n    }\n\n    /// Return this input's parameter file, parsed.\n    pub fn read_parameters(&self, dir_in: &str) -> Parameters {\n        let mut param_path = self.input_path(dir_in);\n        param_path.set_extension(\"toml\");\n        let mut params = match fs::read_to_string(&param_path) {\n            Ok(string) => match toml::de::from_str(&string) {\n                Ok(params) => params,\n                Err(e) => panic!(\n                    \"Couldn't parse param file: {} due to: {e}\",\n                    param_path.display()\n                ),\n            },\n            Err(_) => Parameters::default(),\n        };\n\n        if params.targets.is_none() {\n            match self\n                .input_path(dir_in)\n                .extension()\n                .unwrap()\n                .to_str()\n                .unwrap()\n            {\n                \"wgsl\" => params.targets = Some(Targets::wgsl_default()),\n                \"spvasm\" => params.targets = Some(Targets::non_wgsl_default()),\n                \"vert\" | \"frag\" | \"comp\" => params.targets = Some(Targets::non_wgsl_default()),\n                e => {\n                    panic!(\"Unknown extension: {e}\");\n                }\n            }\n        }\n\n        params\n    }\n\n    /// Write `data` to a file corresponding to this input file in\n    /// `subdirectory`, with `extension`.\n    pub fn write_output_file(\n        &self,\n        subdirectory: &str,\n        extension: &str,\n        data: impl AsRef<[u8]>,\n        dir_out: &str,\n    ) {\n        let output_path = self.output_path(subdirectory, extension, dir_out);\n        fs::create_dir_all(output_path.parent().unwrap()).unwrap();\n        if let Err(err) = fs::write(&output_path, data) {\n            panic!(\"Error writing {}: {}\", output_path.display(), err);\n        }\n    }\n}\n"
  },
  {
    "path": "player/Cargo.toml",
    "content": "[package]\nname = \"player\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"WebGPU trace player\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\npublish = false\n\n[lib]\ntest = false\n\n[[bin]]\nname = \"play\"\ntest = false\n\n[dependencies]\nwgpu-types = { workspace = true, features = [\"serde\", \"std\"] }\n\nenv_logger.workspace = true\nhashbrown.workspace = true\nlog.workspace = true\nraw-window-handle.workspace = true\nron.workspace = true\nwinit = { workspace = true, optional = true }\nbytemuck.workspace = true\n\n# Non-Webassembly\n#\n# We are a non-wasm only crate, and this allows us to compile.\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies.wgpu-core]\nworkspace = true\nfeatures = [\n    \"replay\",\n    \"strict_asserts\",\n    \"wgsl\",\n    \"metal\",\n    \"dx12\",\n    \"vulkan\",\n    \"gles\",\n]\n\n[dev-dependencies]\nserde.workspace = true\n"
  },
  {
    "path": "player/README.md",
    "content": "# wgpu player\n\nThis is an application that allows replaying the `wgpu` workloads recorded elsewhere. It requires the player to be built from\nthe same revision as an application was linking to, or otherwise, the data may fail to load.\n\nLaunch as:\n```rust\nplay <trace-dir>\n```\n\nWhen built with \"winit\" feature, it's able to replay the workloads that operate on a swapchain. It renders each frame sequentially and then waits for the user to close the window. When built without \"winit\", it launches in console mode and can replay any trace that doesn't use swapchains.\n\nNote: replaying is currently restricted to the same backend as one used for recording a trace. It is straightforward, however, to just replace the backend in RON since it's serialized as plain text. Valid values are: Vulkan, Metal, and Dx12.\n"
  },
  {
    "path": "player/src/bin/play.rs",
    "content": "//! This is a player for WebGPU traces.\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn main() {\n    extern crate wgpu_core as wgc;\n    extern crate wgpu_types as wgt;\n\n    use player::Player;\n    use wgc::device::trace;\n    use wgpu_core::command::PointerReferences;\n\n    use std::{\n        fs,\n        path::{Path, PathBuf},\n        process::exit,\n        sync::Arc,\n    };\n\n    #[cfg(feature = \"winit\")]\n    use raw_window_handle::HasWindowHandle;\n    #[cfg(feature = \"winit\")]\n    use winit::{\n        application::ApplicationHandler,\n        event::{ElementState, KeyEvent, WindowEvent},\n        event_loop::{ActiveEventLoop, ControlFlow, EventLoop},\n        keyboard::{Key, NamedKey},\n        window::Window,\n    };\n\n    env_logger::init();\n\n    //TODO: setting for the backend bits\n    //TODO: setting for the target frame, or controls\n\n    const HELP: &str = \"\\\n    Usage: play <trace directory> | <trace file>\\n\\\n    \\n\\\n    Play a wgpu trace from the specified file or directory. If the trace contains\\n\\\n    buffers, textures, or shaders, the directory form must be used.\\n\";\n\n    let (dir, trace) = match std::env::args().nth(1) {\n        Some(arg) if Path::new(&arg).is_dir() => (\n            PathBuf::from(arg.clone()),\n            PathBuf::from(arg).join(trace::FILE_NAME),\n        ),\n        Some(arg) if Path::new(&arg).is_file() => {\n            (PathBuf::from(\"/nonexistent\"), PathBuf::from(arg))\n        }\n        _ => {\n            eprintln!(\"{HELP}\");\n            exit(1);\n        }\n    };\n\n    log::info!(\"Loading trace '{trace:?}'\");\n    let file = fs::File::open(trace).unwrap();\n    let mut actions: Vec<trace::Action<PointerReferences>> = ron::de::from_reader(file).unwrap();\n    actions.reverse(); // allows us to pop from the top\n    log::info!(\"Found {} actions\", actions.len());\n\n    #[cfg(feature = \"winit\")]\n    let event_loop = {\n        log::info!(\"Creating a window\");\n        EventLoop::new().unwrap()\n    };\n\n    let instance_desc = wgt::InstanceDescriptor::new_without_display_handle_from_env();\n    #[cfg(feature = \"winit\")]\n    let instance_desc =\n        instance_desc.with_display_handle(Box::new(event_loop.owned_display_handle()));\n    let instance_flags = instance_desc.flags;\n    let instance = wgc::instance::Instance::new(\"player\", instance_desc, None);\n\n    let (backends, device_desc) =\n        match actions.pop_if(|action| matches!(action, trace::Action::Init { .. })) {\n            Some(trace::Action::Init { desc, backend }) => {\n                log::info!(\"Initializing the device for backend: {backend:?}\");\n                (wgt::Backends::from(backend), desc)\n            }\n            Some(_) => unreachable!(),\n            None => (wgt::Backends::all(), wgt::DeviceDescriptor::default()),\n        };\n\n    let adapter = Arc::new(\n        instance\n            .request_adapter(\n                &wgt::RequestAdapterOptions {\n                    compatible_surface: None,\n                    ..Default::default()\n                },\n                backends,\n            )\n            .expect(\"Unable to obtain an adapter\"),\n    );\n\n    let info = adapter.get_info();\n    log::info!(\"Using '{}'\", info.name);\n\n    let (device, queue) = adapter\n        .create_device_and_queue(&device_desc, instance_flags)\n        .unwrap();\n\n    let mut player = Player::default();\n\n    log::info!(\"Executing actions\");\n    #[cfg(not(feature = \"winit\"))]\n    {\n        unsafe { device.start_graphics_debugger_capture() };\n\n        while let Some(action) = actions.pop() {\n            player.process(&device, &queue, action, trace::DiskTraceLoader::new(&dir));\n        }\n\n        unsafe { device.stop_graphics_debugger_capture() };\n        device.poll(wgt::PollType::wait_indefinitely()).unwrap();\n    }\n    #[cfg(feature = \"winit\")]\n    {\n        struct App<'a> {\n            window: Option<Arc<Window>>,\n            surface: Option<wgc::instance::Surface>,\n            configured_surface_id: Option<wgc::id::PointerId<wgc::id::markers::Surface>>,\n            instance: &'a wgc::instance::Instance,\n            device: &'a Arc<wgc::device::Device>,\n            queue: &'a Arc<wgc::device::queue::Queue>,\n            player: &'a mut Player,\n            actions: &'a mut Vec<trace::Action<'a, PointerReferences>>,\n            dir: &'a Path,\n            resize_config: Option<wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>>,\n            frame_count: usize,\n            done: bool,\n        }\n\n        impl ApplicationHandler for App<'_> {\n            fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n                if self.window.is_some() {\n                    return;\n                }\n                let window = Arc::new(\n                    event_loop\n                        .create_window(\n                            Window::default_attributes()\n                                .with_title(\"wgpu player\")\n                                .with_resizable(true),\n                        )\n                        .unwrap(),\n                );\n                let surface = unsafe {\n                    self.instance\n                        .create_surface(None, window.window_handle().unwrap().into())\n                }\n                .unwrap();\n                self.window = Some(window);\n                self.surface = Some(surface);\n            }\n\n            fn exiting(&mut self, _event_loop: &ActiveEventLoop) {\n                log::info!(\"Closing\");\n                self.device\n                    .poll(wgt::PollType::wait_indefinitely())\n                    .unwrap();\n            }\n\n            fn window_event(\n                &mut self,\n                event_loop: &ActiveEventLoop,\n                _window_id: winit::window::WindowId,\n                event: WindowEvent,\n            ) {\n                event_loop.set_control_flow(ControlFlow::Poll);\n\n                let window = self.window.as_ref().unwrap();\n                let surface = self.surface.as_ref().unwrap();\n\n                match event {\n                    WindowEvent::RedrawRequested if self.resize_config.is_none() => loop {\n                        match self.actions.pop() {\n                            Some(trace::Action::ConfigureSurface(surface_id, config)) => {\n                                log::info!(\"Configuring the surface\");\n                                let current_size: (u32, u32) = window.inner_size().into();\n                                let size = (config.width, config.height);\n                                if current_size != size {\n                                    let _ = window.request_inner_size(\n                                        winit::dpi::PhysicalSize::new(config.width, config.height),\n                                    );\n                                    self.resize_config = Some(config);\n                                    break;\n                                } else {\n                                    let error = self.device.configure_surface(surface, &config);\n                                    self.configured_surface_id = Some(surface_id);\n                                    if let Some(e) = error {\n                                        panic!(\"{e:?}\");\n                                    }\n                                }\n                            }\n                            Some(trace::Action::GetSurfaceTexture { id, parent }) => {\n                                log::debug!(\"Get surface texture for frame {}\", self.frame_count);\n                                assert!(\n                                    self.configured_surface_id == Some(parent),\n                                    \"rendering to an unexpected surface\"\n                                );\n                                self.player.get_surface_texture(id, surface);\n                            }\n                            Some(trace::Action::Present(_id)) => {\n                                self.frame_count += 1;\n                                log::debug!(\"Presenting frame {}\", self.frame_count);\n                                surface.present().unwrap();\n                                break;\n                            }\n                            Some(trace::Action::DiscardSurfaceTexture(_id)) => {\n                                log::debug!(\"Discarding frame {}\", self.frame_count);\n                                surface.discard().unwrap();\n                                break;\n                            }\n                            Some(action) => {\n                                self.player.process(\n                                    self.device,\n                                    self.queue,\n                                    action,\n                                    trace::DiskTraceLoader::new(self.dir),\n                                );\n                            }\n                            None => {\n                                if !self.done {\n                                    println!(\"Finished the end at frame {}\", self.frame_count);\n                                    self.done = true;\n                                }\n                                break;\n                            }\n                        }\n                    },\n                    WindowEvent::Resized(_) => {\n                        if let Some(config) = self.resize_config.take() {\n                            let error = self.device.configure_surface(surface, &config);\n                            if let Some(e) = error {\n                                panic!(\"{e:?}\");\n                            }\n                        }\n                    }\n                    WindowEvent::KeyboardInput {\n                        event:\n                            KeyEvent {\n                                logical_key: Key::Named(NamedKey::Escape),\n                                state: ElementState::Pressed,\n                                ..\n                            },\n                        ..\n                    }\n                    | WindowEvent::CloseRequested => event_loop.exit(),\n                    _ => {}\n                }\n            }\n        }\n\n        let mut app = App {\n            window: None,\n            surface: None,\n            configured_surface_id: None,\n            instance: &instance,\n            device: &device,\n            queue: &queue,\n            player: &mut player,\n            actions: &mut actions,\n            dir: &dir,\n            resize_config: None,\n            frame_count: 0,\n            done: false,\n        };\n        event_loop.run_app(&mut app).unwrap();\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn main() {}\n"
  },
  {
    "path": "player/src/lib.rs",
    "content": "//! This is a player library for WebGPU traces.\n\n#![cfg(not(target_arch = \"wasm32\"))]\n#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]\n\nextern crate wgpu_core as wgc;\nextern crate wgpu_types as wgt;\n\nuse std::{borrow::Cow, convert::Infallible, sync::Arc};\n\nuse hashbrown::HashMap;\n\nuse wgc::{\n    binding_model::BindingResource,\n    command::{ArcCommand, ArcReferences, BasePass, Command, PointerReferences},\n    device::trace::{self, DataKind, DataLoader},\n    id::{Marker, PointerId},\n};\n\npub struct Player {\n    pipeline_layouts: HashMap<\n        wgc::id::PointerId<wgc::id::markers::PipelineLayout>,\n        Arc<wgc::binding_model::PipelineLayout>,\n    >,\n    shader_modules: HashMap<\n        wgc::id::PointerId<wgc::id::markers::ShaderModule>,\n        Arc<wgc::pipeline::ShaderModule>,\n    >,\n    bind_group_layouts: HashMap<\n        wgc::id::PointerId<wgc::id::markers::BindGroupLayout>,\n        Arc<wgc::binding_model::BindGroupLayout>,\n    >,\n    bind_groups: HashMap<\n        wgc::id::PointerId<wgc::id::markers::BindGroup>,\n        Arc<wgc::binding_model::BindGroup>,\n    >,\n    render_bundles: HashMap<\n        wgc::id::PointerId<wgc::id::markers::RenderBundle>,\n        Arc<wgc::command::RenderBundle>,\n    >,\n    render_pipelines: HashMap<\n        wgc::id::PointerId<wgc::id::markers::RenderPipeline>,\n        Arc<wgc::pipeline::RenderPipeline>,\n    >,\n    compute_pipelines: HashMap<\n        wgc::id::PointerId<wgc::id::markers::ComputePipeline>,\n        Arc<wgc::pipeline::ComputePipeline>,\n    >,\n    pipeline_caches: HashMap<\n        wgc::id::PointerId<wgc::id::markers::PipelineCache>,\n        Arc<wgc::pipeline::PipelineCache>,\n    >,\n    query_sets:\n        HashMap<wgc::id::PointerId<wgc::id::markers::QuerySet>, Arc<wgc::resource::QuerySet>>,\n    buffers: HashMap<wgc::id::PointerId<wgc::id::markers::Buffer>, Arc<wgc::resource::Buffer>>,\n    textures: HashMap<wgc::id::PointerId<wgc::id::markers::Texture>, Arc<wgc::resource::Texture>>,\n    texture_views:\n        HashMap<wgc::id::PointerId<wgc::id::markers::TextureView>, Arc<wgc::resource::TextureView>>,\n    external_textures: HashMap<\n        wgc::id::PointerId<wgc::id::markers::ExternalTexture>,\n        Arc<wgc::resource::ExternalTexture>,\n    >,\n    samplers: HashMap<wgc::id::PointerId<wgc::id::markers::Sampler>, Arc<wgc::resource::Sampler>>,\n    blas_s: HashMap<wgc::id::PointerId<wgc::id::markers::Blas>, Arc<wgc::resource::Blas>>,\n    tlas_s: HashMap<wgc::id::PointerId<wgc::id::markers::Tlas>, Arc<wgc::resource::Tlas>>,\n}\n\nimpl Default for Player {\n    fn default() -> Self {\n        Self {\n            pipeline_layouts: HashMap::new(),\n            shader_modules: HashMap::new(),\n            bind_group_layouts: HashMap::new(),\n            bind_groups: HashMap::new(),\n            render_bundles: HashMap::new(),\n            render_pipelines: HashMap::new(),\n            compute_pipelines: HashMap::new(),\n            pipeline_caches: HashMap::new(),\n            query_sets: HashMap::new(),\n            buffers: HashMap::new(),\n            textures: HashMap::new(),\n            texture_views: HashMap::new(),\n            external_textures: HashMap::new(),\n            samplers: HashMap::new(),\n            blas_s: HashMap::new(),\n            tlas_s: HashMap::new(),\n        }\n    }\n}\n\nfn process_result<T: Marker, U>(\n    op: &str,\n    map: &mut HashMap<PointerId<T>, U>,\n    id: Option<PointerId<T>>,\n    value: Result<U, impl std::error::Error>,\n) {\n    match (id, value) {\n        (Some(id), Ok(value)) => {\n            map.insert(id, value);\n        }\n        (Some(_), Err(err)) => {\n            panic!(\"{op} succeeded when recording, but failed on playback: {err}\");\n        }\n        (None, Ok(_)) => {\n            panic!(\"{op} failed when recording, but succeeded on playback\");\n        }\n        (None, Err(err)) => {\n            panic!(\"{op} failed when recording, and failed on playback: {err}\");\n        }\n    }\n}\n\nimpl Player {\n    pub fn process(\n        &mut self,\n        device: &Arc<wgc::device::Device>,\n        queue: &Arc<wgc::device::queue::Queue>,\n        action: trace::Action<PointerReferences>,\n        loader: impl DataLoader,\n    ) {\n        use wgc::device::trace::Action;\n        log::debug!(\"action {action:?}\");\n        match action {\n            Action::Init { .. } => {\n                panic!(\"Unexpected Action::Init: has to be the first action only\")\n            }\n            Action::ConfigureSurface { .. }\n            | Action::Present(_)\n            | Action::DiscardSurfaceTexture(_) => {\n                panic!(\"Unexpected Surface action: winit feature is not enabled\")\n            }\n            Action::CreateBuffer(id, desc) => {\n                let buffer = device.create_buffer(&desc).expect(\"create_buffer error\");\n                self.buffers.insert(id, buffer);\n            }\n            Action::FreeBuffer(id) => {\n                // Note: buffer remains in the HashMap. \"Free\" and \"Destroy\"\n                // mean the opposite from WebGPU.\n                let buffer = self.buffers.get(&id).expect(\"invalid buffer\");\n                buffer.destroy();\n            }\n            Action::DestroyBuffer(id) => {\n                let buffer = self.buffers.remove(&id).expect(\"invalid buffer\");\n                let _ = buffer.unmap();\n            }\n            Action::CreateTexture(id, desc) => {\n                let texture = device.create_texture(&desc).expect(\"create_texture error\");\n                self.textures.insert(id, texture);\n            }\n            Action::FreeTexture(id) => {\n                // Note: texture remains in the HashMap. \"Free\" and \"Destroy\"\n                // mean the opposite from WebGPU.\n                let texture = self.textures.get(&id).expect(\"invalid texture\");\n                texture.destroy();\n            }\n            Action::DestroyTexture(id) => {\n                self.textures.remove(&id).expect(\"invalid texture\");\n            }\n            Action::CreateTextureView { id, parent, desc } => {\n                let parent_texture = self.resolve_texture_id(parent);\n                let texture_view = device\n                    .create_texture_view(&parent_texture, &desc)\n                    .expect(\"create_texture_view error\");\n                self.texture_views.insert(id, texture_view);\n            }\n            Action::DestroyTextureView(id) => {\n                self.texture_views\n                    .remove(&id)\n                    .expect(\"invalid texture view\");\n            }\n            Action::CreateExternalTexture { id, desc, planes } => {\n                let planes = planes\n                    .iter()\n                    .map(|&id| self.resolve_texture_view_id(id))\n                    .collect::<Vec<_>>();\n                let external_texture = device\n                    .create_external_texture(&desc, &planes)\n                    .expect(\"create_external_texture error\");\n                self.external_textures.insert(id, external_texture);\n            }\n            Action::FreeExternalTexture(id) => {\n                // Note: external texture remains in the HashMap. \"Free\" and \"Destroy\"\n                // mean the opposite from WebGPU.\n                let external_texture = self\n                    .external_textures\n                    .get(&id)\n                    .expect(\"invalid external texture\");\n                external_texture.destroy();\n            }\n            Action::DestroyExternalTexture(id) => {\n                self.external_textures\n                    .remove(&id)\n                    .expect(\"invalid external texture\");\n            }\n            Action::CreateSampler(id, desc) => {\n                let sampler = device.create_sampler(&desc).expect(\"create_sampler error\");\n                self.samplers.insert(id, sampler);\n            }\n            Action::DestroySampler(id) => {\n                self.samplers.remove(&id).expect(\"invalid sampler\");\n            }\n            Action::GetSurfaceTexture { .. } => {\n                unimplemented!()\n            }\n            Action::CreateBindGroupLayout(id, desc) => {\n                let bind_group_layout = device\n                    .create_bind_group_layout(&desc)\n                    .expect(\"create_bind_group_layout error\");\n                self.bind_group_layouts.insert(id, bind_group_layout);\n            }\n            Action::GetRenderPipelineBindGroupLayout {\n                id,\n                pipeline,\n                index,\n            } => {\n                let pipeline = self.resolve_render_pipeline_id(pipeline);\n                let bgl = pipeline\n                    .get_bind_group_layout(index)\n                    .expect(\"invalid render pipeline\");\n                self.bind_group_layouts.insert(id, bgl);\n            }\n            Action::GetComputePipelineBindGroupLayout {\n                id,\n                pipeline,\n                index,\n            } => {\n                let pipeline = self.resolve_compute_pipeline_id(pipeline);\n                let bgl = pipeline\n                    .get_bind_group_layout(index)\n                    .expect(\"invalid compute pipeline\");\n                self.bind_group_layouts.insert(id, bgl);\n            }\n            Action::DestroyBindGroupLayout(id) => {\n                self.bind_group_layouts\n                    .remove(&id)\n                    .expect(\"invalid bind group layout\");\n            }\n            Action::CreatePipelineLayout(id, desc) => {\n                let bind_group_layouts: Vec<_> = desc\n                    .bind_group_layouts\n                    .to_vec()\n                    .into_iter()\n                    .map(|bgl_id| bgl_id.map(|bgl_id| self.resolve_bind_group_layout_id(bgl_id)))\n                    .collect();\n\n                let resolved_desc = wgc::binding_model::ResolvedPipelineLayoutDescriptor {\n                    label: desc.label.clone(),\n                    bind_group_layouts: Cow::from(&bind_group_layouts),\n                    immediate_size: desc.immediate_size,\n                };\n\n                let pipeline_layout = device\n                    .create_pipeline_layout(&resolved_desc)\n                    .expect(\"create_pipeline_layout error\");\n                self.pipeline_layouts.insert(id, pipeline_layout);\n            }\n            Action::DestroyPipelineLayout(id) => {\n                self.pipeline_layouts\n                    .remove(&id)\n                    .expect(\"invalid pipeline layout\");\n            }\n            Action::CreateBindGroup(id, desc) => {\n                let resolved_desc = self.resolve_bind_group_descriptor(desc);\n                let bind_group = device\n                    .create_bind_group(resolved_desc)\n                    .expect(\"create_bind_group error\");\n                self.bind_groups.insert(id, bind_group);\n            }\n            Action::DestroyBindGroup(id) => {\n                let _bind_group = self.bind_groups.remove(&id).expect(\"invalid bind group\");\n            }\n            Action::CreateShaderModule { id, desc, data } => {\n                let code = loader.load_utf8(&data);\n                let source = if data.kind() == DataKind::Wgsl {\n                    wgc::pipeline::ShaderModuleSource::Wgsl(code.clone())\n                } else if data.kind() == DataKind::Ron {\n                    let module = ron::de::from_str(&code).unwrap();\n                    wgc::pipeline::ShaderModuleSource::Naga(module)\n                } else {\n                    panic!(\n                        \"Unknown data kind for CreateShaderModule: {:?}\",\n                        data.kind()\n                    );\n                };\n                match device.create_shader_module(&desc, source) {\n                    Ok(module) => self.shader_modules.insert(id, module),\n                    Err(e) => panic!(\"shader compilation error:\\n---{code}\\n---\\n{e}\"),\n                };\n            }\n            Action::CreateShaderModulePassthrough {\n                id,\n                data,\n                label,\n                num_workgroups,\n            } => {\n                let spirv = data.iter().find_map(|a| {\n                    if a.kind() == DataKind::Spv {\n                        let data = loader.load(a);\n                        assert!(data.len().is_multiple_of(4));\n\n                        Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data)))\n                    } else {\n                        None\n                    }\n                });\n                let dxil = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::Dxil).then(|| loader.load(a)));\n                let hlsl = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::Hlsl).then(|| loader.load_utf8(a)));\n                let metallib = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::MetalLib).then(|| loader.load(a)));\n                let msl = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::Msl).then(|| loader.load_utf8(a)));\n                let glsl = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::Glsl).then(|| loader.load_utf8(a)));\n                let wgsl = data\n                    .iter()\n                    .find_map(|a| (a.kind() == DataKind::Wgsl).then(|| loader.load_utf8(a)));\n                let desc = wgt::CreateShaderModuleDescriptorPassthrough {\n                    label,\n                    num_workgroups,\n\n                    spirv,\n                    dxil,\n                    hlsl,\n                    metallib,\n                    msl,\n                    glsl,\n                    wgsl,\n                };\n                match unsafe { device.create_shader_module_passthrough(&desc) } {\n                    Ok(module) => self.shader_modules.insert(id, module),\n                    Err(e) => panic!(\"shader compilation error:\\n{e}\"),\n                };\n            }\n            Action::DestroyShaderModule(id) => {\n                self.shader_modules\n                    .remove(&id)\n                    .expect(\"invalid shader module\");\n            }\n            Action::CreateComputePipeline { id, desc } => {\n                let resolved_desc = self.resolve_compute_pipeline_descriptor(desc);\n                let pipeline = device.create_compute_pipeline(resolved_desc);\n                process_result(\n                    \"create_compute_pipeline\",\n                    &mut self.compute_pipelines,\n                    id,\n                    pipeline,\n                );\n            }\n            Action::DestroyComputePipeline(id) => {\n                self.compute_pipelines\n                    .remove(&id)\n                    .expect(\"invalid compute pipeline\");\n            }\n            Action::CreateGeneralRenderPipeline { id, desc } => {\n                // Note that this is the `General` version of the render\n                // pipeline descriptor that can represent either a conventional\n                // pipeline or a mesh shading pipeline.\n                let resolved_desc = self.resolve_render_pipeline_descriptor(desc);\n                let pipeline = device.create_render_pipeline(resolved_desc);\n                process_result(\n                    \"create_render_pipeline\",\n                    &mut self.render_pipelines,\n                    id,\n                    pipeline,\n                );\n            }\n            Action::DestroyRenderPipeline(id) => {\n                self.render_pipelines\n                    .remove(&id)\n                    .expect(\"invalid render pipeline\");\n            }\n            Action::CreatePipelineCache { id, desc } => {\n                let cache = unsafe { device.create_pipeline_cache(&desc) }.unwrap();\n                self.pipeline_caches.insert(id, cache);\n            }\n            Action::DestroyPipelineCache(id) => {\n                self.pipeline_caches\n                    .remove(&id)\n                    .expect(\"invalid pipeline cache\");\n            }\n            Action::CreateRenderBundle { .. } => {\n                unimplemented!(\"traced render bundles are not supported\");\n            }\n            Action::DestroyRenderBundle(id) => {\n                self.render_bundles\n                    .remove(&id)\n                    .expect(\"invalid render bundle\");\n            }\n            Action::CreateQuerySet { id, desc } => {\n                let query_set = device\n                    .create_query_set(&desc)\n                    .expect(\"create_query_set error\");\n                self.query_sets.insert(id, query_set);\n            }\n            Action::DestroyQuerySet(id) => {\n                self.query_sets.remove(&id).expect(\"invalid query set\");\n            }\n            Action::WriteBuffer {\n                id,\n                data,\n                offset,\n                size,\n                queued,\n            } => {\n                let buffer = self.resolve_buffer_id(id);\n                let bin = loader.load(&data);\n                if queued {\n                    queue\n                        .write_buffer(buffer, offset, &bin[..size.try_into().unwrap()])\n                        .expect(\"Queue::write_buffer error\");\n                } else {\n                    device\n                        .set_buffer_data(&buffer, offset, &bin[..size.try_into().unwrap()])\n                        .expect(\"Device::set_buffer_data error\");\n                }\n            }\n            Action::WriteTexture {\n                to,\n                data,\n                layout,\n                size,\n            } => {\n                let to = self.resolve_texel_copy_texture_info(to);\n                let bin = loader.load(&data);\n                queue\n                    .write_texture(to, &bin, &layout, &size)\n                    .expect(\"Queue::write_texture error\");\n            }\n            Action::Submit(_index, ref commands) if commands.is_empty() => {\n                queue.submit(&[]).unwrap();\n            }\n            Action::Submit(_index, commands) => {\n                let resolved_commands: Vec<_> = commands\n                    .into_iter()\n                    .map(|cmd| self.resolve_command(cmd))\n                    .collect();\n                let buffer = wgc::command::CommandBuffer::from_trace(device, resolved_commands);\n                queue.submit(&[buffer]).unwrap();\n            }\n            Action::FailedCommands {\n                commands,\n                failed_at_submit,\n                error,\n            } => {\n                let action = if failed_at_submit.is_some() {\n                    \"submitting\"\n                } else {\n                    \"encoding\"\n                };\n                if let Some(commands) = commands {\n                    log::trace!(\n                        \"Trace recorded an error {action} the following commands: {commands:#?}\"\n                    );\n                }\n                panic!(\"Error recorded in trace: {error}\");\n            }\n            Action::CreateBlas { id, desc, sizes } => {\n                let blas = device.create_blas(&desc, sizes).expect(\"create_blas error\");\n                self.blas_s.insert(id, blas);\n            }\n            Action::DestroyBlas(id) => {\n                self.blas_s.remove(&id).expect(\"invalid blas\");\n            }\n            Action::CreateTlas { id, desc } => {\n                let tlas = device.create_tlas(&desc).expect(\"create_tlas error\");\n                self.tlas_s.insert(id, tlas);\n            }\n            Action::DestroyTlas(id) => {\n                self.tlas_s.remove(&id).expect(\"invalid tlas\");\n            }\n        }\n    }\n\n    // This one is a little strange because the surface is held by the\n    // `player` application but we want to insert the texture into our\n    // map so we can find it for rendering.\n    pub fn get_surface_texture(\n        &mut self,\n        id: wgc::id::PointerId<wgc::id::markers::Texture>,\n        surface: &wgc::instance::Surface,\n    ) {\n        let frame = surface\n            .get_current_texture()\n            .expect(\"get_current_texture error\");\n        let texture = frame.texture.expect(\"did not obtain a surface texture\");\n        self.textures.insert(id, texture);\n    }\n\n    pub fn resolve_buffer_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::Buffer>,\n    ) -> Arc<wgc::resource::Buffer> {\n        self.buffers.get(&id).expect(\"invalid buffer\").clone()\n    }\n\n    fn resolve_texture_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::Texture>,\n    ) -> Arc<wgc::resource::Texture> {\n        self.textures.get(&id).expect(\"invalid texture\").clone()\n    }\n\n    fn resolve_texture_view_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::TextureView>,\n    ) -> Arc<wgc::resource::TextureView> {\n        self.texture_views\n            .get(&id)\n            .expect(\"invalid texture view\")\n            .clone()\n    }\n\n    fn resolve_external_texture_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::ExternalTexture>,\n    ) -> Arc<wgc::resource::ExternalTexture> {\n        self.external_textures\n            .get(&id)\n            .expect(\"invalid external texture\")\n            .clone()\n    }\n\n    fn resolve_sampler_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::Sampler>,\n    ) -> Arc<wgc::resource::Sampler> {\n        self.samplers.get(&id).expect(\"invalid sampler\").clone()\n    }\n\n    fn resolve_bind_group_layout_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::BindGroupLayout>,\n    ) -> Arc<wgc::binding_model::BindGroupLayout> {\n        self.bind_group_layouts\n            .get(&id)\n            .expect(\"invalid bind group layout\")\n            .clone()\n    }\n\n    fn resolve_bind_group_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::BindGroup>,\n    ) -> Arc<wgc::binding_model::BindGroup> {\n        self.bind_groups\n            .get(&id)\n            .expect(\"invalid bind group\")\n            .clone()\n    }\n\n    fn resolve_pipeline_layout_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::PipelineLayout>,\n    ) -> Arc<wgc::binding_model::PipelineLayout> {\n        self.pipeline_layouts\n            .get(&id)\n            .expect(\"invalid pipeline layout\")\n            .clone()\n    }\n\n    fn resolve_shader_module_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::ShaderModule>,\n    ) -> Arc<wgc::pipeline::ShaderModule> {\n        self.shader_modules\n            .get(&id)\n            .expect(\"invalid shader module\")\n            .clone()\n    }\n\n    fn resolve_render_pipeline_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::RenderPipeline>,\n    ) -> Arc<wgc::pipeline::RenderPipeline> {\n        self.render_pipelines\n            .get(&id)\n            .expect(\"invalid render pipeline\")\n            .clone()\n    }\n\n    fn resolve_compute_pipeline_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::ComputePipeline>,\n    ) -> Arc<wgc::pipeline::ComputePipeline> {\n        self.compute_pipelines\n            .get(&id)\n            .expect(\"invalid compute pipeline\")\n            .clone()\n    }\n\n    fn resolve_pipeline_cache_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::PipelineCache>,\n    ) -> Arc<wgc::pipeline::PipelineCache> {\n        self.pipeline_caches\n            .get(&id)\n            .expect(\"invalid pipeline cache\")\n            .clone()\n    }\n\n    fn resolve_render_bundle_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::RenderBundle>,\n    ) -> Arc<wgc::command::RenderBundle> {\n        self.render_bundles\n            .get(&id)\n            .expect(\"invalid render bundle\")\n            .clone()\n    }\n\n    fn resolve_query_set_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::QuerySet>,\n    ) -> Arc<wgc::resource::QuerySet> {\n        self.query_sets.get(&id).expect(\"invalid query set\").clone()\n    }\n\n    fn resolve_blas_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::Blas>,\n    ) -> Arc<wgc::resource::Blas> {\n        self.blas_s.get(&id).expect(\"invalid blas\").clone()\n    }\n\n    fn resolve_tlas_id(\n        &self,\n        id: wgc::id::PointerId<wgc::id::markers::Tlas>,\n    ) -> Arc<wgc::resource::Tlas> {\n        self.tlas_s.get(&id).expect(\"invalid tlas\").clone()\n    }\n\n    fn resolve_texel_copy_texture_info(\n        &self,\n        info: wgt::TexelCopyTextureInfo<wgc::id::PointerId<wgc::id::markers::Texture>>,\n    ) -> wgt::TexelCopyTextureInfo<Arc<wgc::resource::Texture>> {\n        wgt::TexelCopyTextureInfo {\n            texture: self.resolve_texture_id(info.texture),\n            mip_level: info.mip_level,\n            origin: info.origin,\n            aspect: info.aspect,\n        }\n    }\n\n    fn resolve_compute_pipeline_descriptor<'a>(\n        &self,\n        desc: wgc::device::trace::TraceComputePipelineDescriptor<'a>,\n    ) -> wgc::pipeline::ResolvedComputePipelineDescriptor<'a> {\n        wgc::pipeline::ResolvedComputePipelineDescriptor {\n            label: desc.label,\n            layout: desc.layout.map(|id| self.resolve_pipeline_layout_id(id)),\n            stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {\n                module: self.resolve_shader_module_id(desc.stage.module),\n                entry_point: desc.stage.entry_point,\n                constants: desc.stage.constants,\n                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,\n            },\n            cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)),\n        }\n    }\n\n    fn resolve_render_pipeline_descriptor<'a>(\n        &self,\n        desc: wgc::device::trace::TraceGeneralRenderPipelineDescriptor<'a>,\n    ) -> wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {\n        let layout = desc.layout.map(|id| self.resolve_pipeline_layout_id(id));\n\n        let vertex = match desc.vertex {\n            wgc::pipeline::RenderPipelineVertexProcessor::Vertex(vertex_state) => {\n                wgc::pipeline::RenderPipelineVertexProcessor::Vertex(\n                    wgc::pipeline::ResolvedVertexState {\n                        stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {\n                            module: self.resolve_shader_module_id(vertex_state.stage.module),\n                            entry_point: vertex_state.stage.entry_point,\n                            constants: vertex_state.stage.constants,\n                            zero_initialize_workgroup_memory: vertex_state\n                                .stage\n                                .zero_initialize_workgroup_memory,\n                        },\n                        buffers: vertex_state.buffers,\n                    },\n                )\n            }\n            wgc::pipeline::RenderPipelineVertexProcessor::Mesh(task_state, mesh_state) => {\n                let resolved_task = task_state.map(|task| wgc::pipeline::ResolvedTaskState {\n                    stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {\n                        module: self.resolve_shader_module_id(task.stage.module),\n                        entry_point: task.stage.entry_point,\n                        constants: task.stage.constants,\n                        zero_initialize_workgroup_memory: task\n                            .stage\n                            .zero_initialize_workgroup_memory,\n                    },\n                });\n                let resolved_mesh = wgc::pipeline::ResolvedMeshState {\n                    stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {\n                        module: self.resolve_shader_module_id(mesh_state.stage.module),\n                        entry_point: mesh_state.stage.entry_point,\n                        constants: mesh_state.stage.constants,\n                        zero_initialize_workgroup_memory: mesh_state\n                            .stage\n                            .zero_initialize_workgroup_memory,\n                    },\n                };\n                wgc::pipeline::RenderPipelineVertexProcessor::Mesh(resolved_task, resolved_mesh)\n            }\n        };\n\n        let fragment = desc\n            .fragment\n            .map(|fragment_state| wgc::pipeline::ResolvedFragmentState {\n                stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {\n                    module: self.resolve_shader_module_id(fragment_state.stage.module),\n                    entry_point: fragment_state.stage.entry_point,\n                    constants: fragment_state.stage.constants,\n                    zero_initialize_workgroup_memory: fragment_state\n                        .stage\n                        .zero_initialize_workgroup_memory,\n                },\n                targets: fragment_state.targets,\n            });\n\n        wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor {\n            label: desc.label,\n            layout,\n            vertex,\n            primitive: desc.primitive,\n            depth_stencil: desc.depth_stencil,\n            multisample: desc.multisample,\n            fragment,\n            multiview_mask: desc.multiview_mask,\n            cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)),\n        }\n    }\n\n    fn resolve_bind_group_descriptor<'a>(\n        &self,\n        desc: wgc::device::trace::TraceBindGroupDescriptor<'a>,\n    ) -> wgc::binding_model::ResolvedBindGroupDescriptor<'a> {\n        let layout = self.resolve_bind_group_layout_id(desc.layout);\n\n        let entries: Vec<wgc::binding_model::ResolvedBindGroupEntry> = desc\n            .entries\n            .to_vec()\n            .into_iter()\n            .map(|entry| {\n                let resource = match entry.resource {\n                    BindingResource::Buffer(buffer_binding) => {\n                        let buffer = self.resolve_buffer_id(buffer_binding.buffer);\n                        wgc::binding_model::ResolvedBindingResource::Buffer(\n                            wgc::binding_model::ResolvedBufferBinding {\n                                buffer,\n                                offset: buffer_binding.offset,\n                                size: buffer_binding.size,\n                            },\n                        )\n                    }\n                    BindingResource::BufferArray(buffer_bindings) => {\n                        let resolved_buffers: Vec<_> = buffer_bindings\n                            .to_vec()\n                            .into_iter()\n                            .map(|bb| {\n                                let buffer = self.resolve_buffer_id(bb.buffer);\n                                wgc::binding_model::ResolvedBufferBinding {\n                                    buffer,\n                                    offset: bb.offset,\n                                    size: bb.size,\n                                }\n                            })\n                            .collect();\n                        wgc::binding_model::ResolvedBindingResource::BufferArray(Cow::Owned(\n                            resolved_buffers,\n                        ))\n                    }\n                    BindingResource::Sampler(sampler_id) => {\n                        let sampler = self.resolve_sampler_id(sampler_id);\n                        wgc::binding_model::ResolvedBindingResource::Sampler(sampler)\n                    }\n                    BindingResource::SamplerArray(sampler_ids) => {\n                        let resolved_samplers: Vec<_> = sampler_ids\n                            .to_vec()\n                            .into_iter()\n                            .map(|id| self.resolve_sampler_id(id))\n                            .collect();\n                        wgc::binding_model::ResolvedBindingResource::SamplerArray(Cow::Owned(\n                            resolved_samplers,\n                        ))\n                    }\n                    BindingResource::TextureView(texture_view_id) => {\n                        let texture_view = self.resolve_texture_view_id(texture_view_id);\n                        wgc::binding_model::ResolvedBindingResource::TextureView(texture_view)\n                    }\n                    BindingResource::TextureViewArray(texture_view_ids) => {\n                        let resolved_views: Vec<_> = texture_view_ids\n                            .to_vec()\n                            .into_iter()\n                            .map(|id| self.resolve_texture_view_id(id))\n                            .collect();\n                        wgc::binding_model::ResolvedBindingResource::TextureViewArray(Cow::Owned(\n                            resolved_views,\n                        ))\n                    }\n                    BindingResource::AccelerationStructure(tlas_id) => {\n                        let tlas = self.resolve_tlas_id(tlas_id);\n                        wgc::binding_model::ResolvedBindingResource::AccelerationStructure(tlas)\n                    }\n                    BindingResource::AccelerationStructureArray(tlas_ids) => {\n                        let resolved_tlas: Vec<_> = tlas_ids\n                            .to_vec()\n                            .into_iter()\n                            .map(|id| self.resolve_tlas_id(id))\n                            .collect();\n                        wgc::binding_model::ResolvedBindingResource::AccelerationStructureArray(\n                            Cow::Owned(resolved_tlas),\n                        )\n                    }\n                    BindingResource::ExternalTexture(external_texture_id) => {\n                        let external_texture =\n                            self.resolve_external_texture_id(external_texture_id);\n                        wgc::binding_model::ResolvedBindingResource::ExternalTexture(\n                            external_texture,\n                        )\n                    }\n                };\n\n                wgc::binding_model::ResolvedBindGroupEntry {\n                    binding: entry.binding,\n                    resource,\n                }\n            })\n            .collect();\n\n        wgc::binding_model::ResolvedBindGroupDescriptor {\n            label: desc.label.clone(),\n            layout,\n            entries: entries.into(),\n        }\n    }\n\n    fn resolve_command(&self, command: Command<PointerReferences>) -> ArcCommand {\n        match command {\n            Command::CopyBufferToBuffer {\n                src,\n                src_offset,\n                dst,\n                dst_offset,\n                size,\n            } => Command::CopyBufferToBuffer {\n                src: self.resolve_buffer_id(src),\n                src_offset,\n                dst: self.resolve_buffer_id(dst),\n                dst_offset,\n                size,\n            },\n            Command::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture {\n                src: self.resolve_texel_copy_buffer_info(src),\n                dst: self.resolve_texel_copy_texture_info(dst),\n                size,\n            },\n            Command::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer {\n                src: self.resolve_texel_copy_texture_info(src),\n                dst: self.resolve_texel_copy_buffer_info(dst),\n                size,\n            },\n            Command::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture {\n                src: self.resolve_texel_copy_texture_info(src),\n                dst: self.resolve_texel_copy_texture_info(dst),\n                size,\n            },\n            Command::ClearBuffer { dst, offset, size } => Command::ClearBuffer {\n                dst: self.resolve_buffer_id(dst),\n                offset,\n                size,\n            },\n            Command::ClearTexture {\n                dst,\n                subresource_range,\n            } => Command::ClearTexture {\n                dst: self.resolve_texture_id(dst),\n                subresource_range,\n            },\n            Command::WriteTimestamp {\n                query_set,\n                query_index,\n            } => Command::WriteTimestamp {\n                query_set: self.resolve_query_set_id(query_set),\n                query_index,\n            },\n            Command::ResolveQuerySet {\n                query_set,\n                start_query,\n                query_count,\n                destination,\n                destination_offset,\n            } => Command::ResolveQuerySet {\n                query_set: self.resolve_query_set_id(query_set),\n                start_query,\n                query_count,\n                destination: self.resolve_buffer_id(destination),\n                destination_offset,\n            },\n            Command::PushDebugGroup(label) => Command::PushDebugGroup(label.clone()),\n            Command::PopDebugGroup => Command::PopDebugGroup,\n            Command::InsertDebugMarker(label) => Command::InsertDebugMarker(label.clone()),\n            Command::RunComputePass {\n                pass,\n                timestamp_writes,\n            } => Command::RunComputePass {\n                pass: self.resolve_compute_pass(pass),\n                timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)),\n            },\n            Command::RunRenderPass {\n                pass,\n                color_attachments,\n                depth_stencil_attachment,\n                timestamp_writes,\n                occlusion_query_set,\n                multiview_mask,\n            } => Command::RunRenderPass {\n                pass: self.resolve_render_pass(pass),\n                color_attachments: self.resolve_color_attachments(color_attachments),\n                depth_stencil_attachment: depth_stencil_attachment\n                    .map(|att| self.resolve_depth_stencil_attachment(att)),\n                timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)),\n                occlusion_query_set: occlusion_query_set.map(|qs| self.resolve_query_set_id(qs)),\n                multiview_mask,\n            },\n            Command::BuildAccelerationStructures { blas, tlas } => {\n                Command::BuildAccelerationStructures {\n                    blas: blas\n                        .into_iter()\n                        .map(|entry| self.resolve_blas_build_entry(entry))\n                        .collect(),\n                    tlas: tlas\n                        .into_iter()\n                        .map(|package| self.resolve_tlas_package(package))\n                        .collect(),\n                }\n            }\n            Command::TransitionResources {\n                buffer_transitions,\n                texture_transitions,\n            } => Command::TransitionResources {\n                buffer_transitions: buffer_transitions\n                    .into_iter()\n                    .map(|trans| self.resolve_buffer_transition(trans))\n                    .collect(),\n                texture_transitions: texture_transitions\n                    .into_iter()\n                    .map(|trans| self.resolve_texture_transition(trans))\n                    .collect(),\n            },\n        }\n    }\n\n    // Helper methods for command resolution\n    fn resolve_texel_copy_buffer_info(\n        &self,\n        info: wgt::TexelCopyBufferInfo<PointerId<wgc::id::markers::Buffer>>,\n    ) -> wgt::TexelCopyBufferInfo<Arc<wgc::resource::Buffer>> {\n        wgt::TexelCopyBufferInfo {\n            buffer: self\n                .buffers\n                .get(&info.buffer)\n                .cloned()\n                .expect(\"invalid buffer\"),\n            layout: info.layout,\n        }\n    }\n\n    fn resolve_compute_pass(\n        &self,\n        pass: BasePass<wgc::command::ComputeCommand<PointerReferences>, Infallible>,\n    ) -> BasePass<wgc::command::ComputeCommand<ArcReferences>, Infallible> {\n        let BasePass {\n            label,\n            error,\n            commands,\n            dynamic_offsets,\n            immediates_data,\n            string_data,\n        } = pass;\n\n        BasePass {\n            label,\n            error,\n            commands: commands\n                .into_iter()\n                .map(|cmd| self.resolve_compute_command(cmd))\n                .collect(),\n            dynamic_offsets,\n            immediates_data,\n            string_data,\n        }\n    }\n\n    fn resolve_render_pass(\n        &self,\n        pass: BasePass<wgc::command::RenderCommand<PointerReferences>, Infallible>,\n    ) -> BasePass<wgc::command::RenderCommand<ArcReferences>, Infallible> {\n        let BasePass {\n            label,\n            error,\n            commands,\n            dynamic_offsets,\n            immediates_data,\n            string_data,\n        } = pass;\n\n        BasePass {\n            label,\n            error,\n            commands: commands\n                .into_iter()\n                .map(|cmd| self.resolve_render_command(cmd))\n                .collect(),\n            dynamic_offsets,\n            immediates_data,\n            string_data,\n        }\n    }\n\n    fn resolve_compute_command(\n        &self,\n        command: wgc::command::ComputeCommand<PointerReferences>,\n    ) -> wgc::command::ComputeCommand<ArcReferences> {\n        use wgc::command::ComputeCommand as C;\n        match command {\n            C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group,\n            } => C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)),\n            },\n            C::SetPipeline(id) => C::SetPipeline(self.resolve_compute_pipeline_id(id)),\n            C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            } => C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            },\n            C::Dispatch(groups) => C::Dispatch(groups),\n            C::DispatchIndirect { buffer, offset } => C::DispatchIndirect {\n                buffer: self.resolve_buffer_id(buffer),\n                offset,\n            },\n            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },\n            C::PopDebugGroup => C::PopDebugGroup,\n            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },\n            C::WriteTimestamp {\n                query_set,\n                query_index,\n            } => C::WriteTimestamp {\n                query_set: self.resolve_query_set_id(query_set),\n                query_index,\n            },\n            C::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            } => C::BeginPipelineStatisticsQuery {\n                query_set: self.resolve_query_set_id(query_set),\n                query_index,\n            },\n            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,\n        }\n    }\n\n    fn resolve_render_command(\n        &self,\n        command: wgc::command::RenderCommand<PointerReferences>,\n    ) -> wgc::command::RenderCommand<ArcReferences> {\n        use wgc::command::RenderCommand as C;\n        match command {\n            C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group,\n            } => C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)),\n            },\n            C::SetPipeline(id) => C::SetPipeline(self.resolve_render_pipeline_id(id)),\n            C::SetIndexBuffer {\n                buffer,\n                index_format,\n                offset,\n                size,\n            } => C::SetIndexBuffer {\n                buffer: self.resolve_buffer_id(buffer),\n                index_format,\n                offset,\n                size,\n            },\n            C::SetVertexBuffer {\n                slot,\n                buffer,\n                offset,\n                size,\n            } => C::SetVertexBuffer {\n                slot,\n                buffer: self.resolve_buffer_id(buffer),\n                offset,\n                size,\n            },\n            C::SetBlendConstant(color) => C::SetBlendConstant(color),\n            C::SetStencilReference(val) => C::SetStencilReference(val),\n            C::SetViewport {\n                rect,\n                depth_min,\n                depth_max,\n            } => C::SetViewport {\n                rect,\n                depth_min,\n                depth_max,\n            },\n            C::SetScissor(rect) => C::SetScissor(rect),\n            C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            } => C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            },\n            C::Draw {\n                vertex_count,\n                instance_count,\n                first_vertex,\n                first_instance,\n            } => C::Draw {\n                vertex_count,\n                instance_count,\n                first_vertex,\n                first_instance,\n            },\n            C::DrawIndexed {\n                index_count,\n                instance_count,\n                first_index,\n                base_vertex,\n                first_instance,\n            } => C::DrawIndexed {\n                index_count,\n                instance_count,\n                first_index,\n                base_vertex,\n                first_instance,\n            },\n            C::DrawMeshTasks {\n                group_count_x,\n                group_count_y,\n                group_count_z,\n            } => C::DrawMeshTasks {\n                group_count_x,\n                group_count_y,\n                group_count_z,\n            },\n            C::DrawIndirect {\n                buffer,\n                offset,\n                count,\n                family,\n                vertex_or_index_limit,\n                instance_limit,\n            } => C::DrawIndirect {\n                buffer: self.resolve_buffer_id(buffer),\n                offset,\n                count,\n                family,\n                vertex_or_index_limit,\n                instance_limit,\n            },\n            C::MultiDrawIndirectCount {\n                buffer,\n                offset,\n                count_buffer,\n                count_buffer_offset,\n                max_count,\n                family,\n            } => C::MultiDrawIndirectCount {\n                buffer: self.resolve_buffer_id(buffer),\n                offset,\n                count_buffer: self.resolve_buffer_id(count_buffer),\n                count_buffer_offset,\n                max_count,\n                family,\n            },\n            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },\n            C::PopDebugGroup => C::PopDebugGroup,\n            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },\n            C::WriteTimestamp {\n                query_set,\n                query_index,\n            } => C::WriteTimestamp {\n                query_set: self.resolve_query_set_id(query_set),\n                query_index,\n            },\n            C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index },\n            C::EndOcclusionQuery => C::EndOcclusionQuery,\n            C::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            } => C::BeginPipelineStatisticsQuery {\n                query_set: self.resolve_query_set_id(query_set),\n                query_index,\n            },\n            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,\n            C::ExecuteBundle(bundle) => C::ExecuteBundle(self.resolve_render_bundle_id(bundle)),\n        }\n    }\n\n    fn resolve_pass_timestamp_writes(\n        &self,\n        writes: wgc::command::PassTimestampWrites<PointerId<wgc::id::markers::QuerySet>>,\n    ) -> wgc::command::PassTimestampWrites<Arc<wgc::resource::QuerySet>> {\n        wgc::command::PassTimestampWrites {\n            query_set: self.resolve_query_set_id(writes.query_set),\n            beginning_of_pass_write_index: writes.beginning_of_pass_write_index,\n            end_of_pass_write_index: writes.end_of_pass_write_index,\n        }\n    }\n\n    fn resolve_color_attachments(\n        &self,\n        attachments: wgc::command::ColorAttachments<PointerId<wgc::id::markers::TextureView>>,\n    ) -> wgc::command::ColorAttachments<Arc<wgc::resource::TextureView>> {\n        attachments\n            .into_iter()\n            .map(|opt| {\n                opt.map(|att| wgc::command::RenderPassColorAttachment {\n                    view: self.resolve_texture_view_id(att.view),\n                    depth_slice: att.depth_slice,\n                    resolve_target: att\n                        .resolve_target\n                        .map(|rt| self.resolve_texture_view_id(rt)),\n                    load_op: att.load_op,\n                    store_op: att.store_op,\n                })\n            })\n            .collect()\n    }\n\n    fn resolve_depth_stencil_attachment(\n        &self,\n        attachment: wgc::command::ResolvedRenderPassDepthStencilAttachment<\n            PointerId<wgc::id::markers::TextureView>,\n        >,\n    ) -> wgc::command::ResolvedRenderPassDepthStencilAttachment<Arc<wgc::resource::TextureView>>\n    {\n        wgc::command::ResolvedRenderPassDepthStencilAttachment {\n            view: self.resolve_texture_view_id(attachment.view),\n            depth: attachment.depth,\n            stencil: attachment.stencil,\n        }\n    }\n\n    fn resolve_blas_build_entry(\n        &self,\n        entry: wgc::ray_tracing::OwnedBlasBuildEntry<PointerReferences>,\n    ) -> wgc::ray_tracing::OwnedBlasBuildEntry<ArcReferences> {\n        wgc::ray_tracing::OwnedBlasBuildEntry {\n            blas: self.resolve_blas_id(entry.blas),\n            geometries: self.resolve_blas_geometries(entry.geometries),\n        }\n    }\n\n    fn resolve_tlas_package(\n        &self,\n        package: wgc::ray_tracing::OwnedTlasPackage<PointerReferences>,\n    ) -> wgc::ray_tracing::OwnedTlasPackage<ArcReferences> {\n        wgc::ray_tracing::OwnedTlasPackage {\n            tlas: self.resolve_tlas_id(package.tlas),\n            instances: package\n                .instances\n                .into_iter()\n                .map(|opt| opt.map(|inst| self.resolve_tlas_instance(inst)))\n                .collect(),\n            lowest_unmodified: package.lowest_unmodified,\n        }\n    }\n\n    // Helper functions for ray tracing structures\n    fn resolve_blas_geometries(\n        &self,\n        geometries: wgc::ray_tracing::OwnedBlasGeometries<PointerReferences>,\n    ) -> wgc::ray_tracing::OwnedBlasGeometries<ArcReferences> {\n        match geometries {\n            wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => {\n                wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries(\n                    geos.into_iter()\n                        .map(|geo| self.resolve_blas_triangle_geometry(geo))\n                        .collect(),\n                )\n            }\n        }\n    }\n\n    fn resolve_blas_triangle_geometry(\n        &self,\n        geometry: wgc::ray_tracing::OwnedBlasTriangleGeometry<PointerReferences>,\n    ) -> wgc::ray_tracing::OwnedBlasTriangleGeometry<ArcReferences> {\n        wgc::ray_tracing::OwnedBlasTriangleGeometry {\n            size: geometry.size,\n            vertex_buffer: self.resolve_buffer_id(geometry.vertex_buffer),\n            index_buffer: geometry.index_buffer.map(|buf| self.resolve_buffer_id(buf)),\n            transform_buffer: geometry\n                .transform_buffer\n                .map(|buf| self.resolve_buffer_id(buf)),\n            first_vertex: geometry.first_vertex,\n            vertex_stride: geometry.vertex_stride,\n            first_index: geometry.first_index,\n            transform_buffer_offset: geometry.transform_buffer_offset,\n        }\n    }\n\n    fn resolve_tlas_instance(\n        &self,\n        instance: wgc::ray_tracing::OwnedTlasInstance<PointerReferences>,\n    ) -> wgc::ray_tracing::OwnedTlasInstance<ArcReferences> {\n        wgc::ray_tracing::OwnedTlasInstance {\n            blas: self.resolve_blas_id(instance.blas),\n            transform: instance.transform,\n            custom_data: instance.custom_data,\n            mask: instance.mask,\n        }\n    }\n\n    fn resolve_buffer_transition(\n        &self,\n        trans: wgt::BufferTransition<PointerId<wgc::id::markers::Buffer>>,\n    ) -> wgt::BufferTransition<Arc<wgc::resource::Buffer>> {\n        wgt::BufferTransition {\n            buffer: self\n                .buffers\n                .get(&trans.buffer)\n                .cloned()\n                .expect(\"invalid buffer\"),\n            state: trans.state,\n        }\n    }\n\n    fn resolve_texture_transition(\n        &self,\n        trans: wgt::TextureTransition<PointerId<wgc::id::markers::Texture>>,\n    ) -> wgt::TextureTransition<Arc<wgc::resource::Texture>> {\n        wgt::TextureTransition {\n            texture: self\n                .textures\n                .get(&trans.texture)\n                .cloned()\n                .expect(\"invalid texture\"),\n            selector: trans.selector.clone(),\n            state: trans.state,\n        }\n    }\n}\n"
  },
  {
    "path": "player/tests/player/data/all.ron",
    "content": "(\n\tbackends: \"VULKAN | GL | METAL | DX12 | BROWSER_WEBGPU\",\n\ttests: [\n\t\t\"bind-group.ron\",\n\t\t\"buffer-copy.ron\",\n\t\t\"clear-buffer-texture.ron\",\n\t\t\"pipeline-statistics-query.ron\",\n\t\t\"quad.ron\",\n\t\t\"zero-init-buffer.ron\",\n\t\t\"zero-init-texture-binding.ron\",\n\t\t\"zero-init-texture-copytobuffer.ron\",\n\t\t\"zero-init-texture-rendertarget.ron\",\n\t],\n)"
  },
  {
    "path": "player/tests/player/data/bind-group.ron",
    "content": "(\n    features: \"\",\n    expectations: [], //not crash!\n    actions: [\n        CreateBuffer(PointerId(0x10), (\n            label: None,\n            size: 16,\n            usage: \"UNIFORM\",\n            mapped_at_creation: false,\n        )),\n        CreateBindGroupLayout(PointerId(0x10), (\n            label: None,\n            entries: [\n                (\n                    binding: 0,\n                    visibility: \"VERTEX | FRAGMENT\",\n                    ty: Buffer(\n                        ty: Uniform,\n                    ),\n                ),\n            ],\n        )),\n        CreateBindGroup(PointerId(0x10), (\n            label: None,\n            layout: PointerId(0x10),\n            entries: [\n                (\n                    binding: 0,\n                    resource: Buffer((\n                        buffer: PointerId(0x10),\n                        offset: 0,\n                        size: None,\n                    )),\n                )\n            ],\n        )),\n        CreatePipelineLayout(PointerId(0x10), (\n            label: Some(\"empty\"),\n            bind_group_layouts: [\n                Some(PointerId(0x10)),\n            ],\n            immediate_size: 0,\n        )),\n        CreateShaderModule(\n            id: PointerId(0x10),\n            desc: (\n                label: None,\n                flags: (bits: 3),\n            ),\n            data: File(\"empty.wgsl\"),\n        ),\n        CreateComputePipeline(\n            id: Some(PointerId(0x10)),\n            desc: (\n                label: None,\n                layout: Some(PointerId(0x10)),\n                stage: (\n                    module: PointerId(0x10),\n                    entry_point: None,\n                    constants: {},\n                    zero_initialize_workgroup_memory: true,\n                    vertex_pulling_transform: false,\n                ),\n            ),\n        ),\n        Submit(1, [\n            RunComputePass(\n                pass: (\n                    commands: [\n                        SetBindGroup(\n                            index: 0,\n                            num_dynamic_offsets: 0,\n                            bind_group: Some(PointerId(0x10)),\n                        ),\n                        SetPipeline(PointerId(0x10)),\n                    ],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n            ),\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/buffer-copy.ron",
    "content": "(\n    features: \"MAPPABLE_PRIMARY_BUFFERS\",\n    expectations: [\n        (\n            name: \"basic\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: Raw([0x00, 0x00, 0x80, 0xBF]),\n        )\n    ],\n    actions: [\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"dummy\"),\n                size: 16,\n                usage: \"MAP_READ | COPY_DST | VERTEX\",\n                mapped_at_creation: false,\n            ),\n        ),\n        WriteBuffer(\n            id: PointerId(0x10),\n            data: File(\"data1.bin\"),\n            offset: 0,\n            size: 16,\n            queued: true,\n        ),\n        Submit(1, []),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/clear-buffer-texture.ron",
    "content": "(\n    features: \"MAPPABLE_PRIMARY_BUFFERS | CLEAR_TEXTURE\",\n    expectations: [\n        (\n            name: \"Quad\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: File(\"clear-texture.bin\", 16384),\n        ),\n        (\n            name: \"buffer clear\",\n            buffer: PointerId(0x110),\n            offset: 0,\n            data: Raw([\n                0x00, 0x00, 0x80, 0xBF,\n                0x00, 0x00, 0x00, 0x00,\n                0x00, 0x00, 0x00, 0x00,\n                0x00, 0x00, 0x80, 0x3F,\n            ]),\n        )\n    ],\n    actions: [\n        CreateTexture(PointerId(0x10), (\n            label: Some(\"Output Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"COPY_SRC | COPY_DST | STORAGE_BINDING | RENDER_ATTACHMENT\",\n            view_formats: [],\n        )),\n        // First fill the texture to ensure it wasn't just zero initialized or \"happened\" to be zero.\n        WriteTexture(\n            to: (\n                texture: PointerId(0x10),\n                mip_level: 0,\n                array_layer: 0,\n            ),\n            data: File(\"quad.bin\"),\n            layout: (\n                offset: 0,\n                bytes_per_row: Some(256),\n                rows_per_image: None,\n            ),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n        ),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Output Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n\n        CreateBuffer(\n            PointerId(0x110),\n            (\n                label: Some(\"Buffer to be cleared\"),\n                size: 16,\n                usage: \"MAP_READ | COPY_DST | VERTEX\",\n                mapped_at_creation: false,\n            ),\n        ),\n        // Make sure there is something in the buffer, otherwise it might be just zero init!\n        WriteBuffer(\n            id: PointerId(0x110),\n            data: File(\"data1.bin\"),\n            offset: 0,\n            size: 16,\n            queued: true,\n        ),\n        Submit(1, [\n            ClearTexture(\n                dst: PointerId(0x10),\n                subresource_range: ImageSubresourceRange(\n                    aspect: all,\n                    baseMipLevel: 0,\n                    mipLevelCount: None,\n                    baseArrayLayer: 0,\n                    arrayLayerCount: None,\n                ),\n            ),\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x10),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x10),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: None,\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n            // Partial clear to prove\n            ClearBuffer(\n                dst: PointerId(0x110),\n                offset: 4,\n                size: Some(8),\n            )\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/empty.wgsl",
    "content": "@compute\n@workgroup_size(1)\nfn main() {\n}\n"
  },
  {
    "path": "player/tests/player/data/pipeline-statistics-query.ron",
    "content": "(\n    features: \"MAPPABLE_PRIMARY_BUFFERS | PIPELINE_STATISTICS_QUERY\",\n    expectations: [\n        (\n            name: \"Queried number of compute invocations is correct\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: U64([0x0, 0x2A]),\n        ),\n    ],\n    actions: [\n        CreatePipelineLayout(PointerId(0x10), (\n            label: Some(\"empty\"),\n            bind_group_layouts: [],\n            immediate_size: 0,\n        )),\n        CreateShaderModule(\n            id: PointerId(0x10),\n            desc: (\n                label: None,\n                flags: (bits: 3),\n            ),\n            data: File(\"empty.wgsl\"),\n        ),\n        CreateComputePipeline(\n            id: Some(PointerId(0x10)),\n            desc: (\n                label: None,\n                layout: Some(PointerId(0x10)),\n                stage: (\n                    module: PointerId(0x10),\n                    entry_point: None,\n                    constants: {},\n                    zero_initialize_workgroup_memory: true,\n                    vertex_pulling_transform: false,\n                ),\n            ),\n        ),\n        CreateQuerySet(\n            id: PointerId(0x10),\n            desc: (\n                label: Some(\"Compute Invocation QuerySet\"),\n                count: 2,\n                ty: PipelineStatistics(\"FRAGMENT_SHADER_INVOCATIONS | COMPUTE_SHADER_INVOCATIONS\"),\n            ),\n        ),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Compute Invocation Result Buffer\"),\n                size: 16,\n                usage: \"COPY_DST | MAP_READ | QUERY_RESOLVE\",\n                mapped_at_creation: false,\n            ),\n        ),\n        Submit(1, [\n            RunComputePass(\n                pass: (\n                    commands: [\n                        SetPipeline(PointerId(0x10)),\n                        BeginPipelineStatisticsQuery(\n                            query_set: PointerId(0x10),\n                            query_index: 0,\n                        ),\n                        Dispatch((2, 3, 7,)),\n                        EndPipelineStatisticsQuery,\n                    ],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n            ),\n            ResolveQuerySet(\n                query_set: PointerId(0x10),\n                start_query: 0,\n                query_count: 1,\n                destination: PointerId(0x10),\n                destination_offset: 0,\n            )\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/quad.ron",
    "content": "(\n    features: \"\",\n    expectations: [\n        (\n            name: \"Quad\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: File(\"quad.bin\", 16384),\n        )\n    ],\n    actions: [\n        CreateShaderModule(\n            id: PointerId(0x10),\n            desc: (\n                label: None,\n                flags: (bits: 3),\n            ),\n            data: File(\"quad.wgsl\"),\n        ),\n        CreateTexture(PointerId(0x10), (\n            label: Some(\"Output Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"COPY_SRC | COPY_DST | STORAGE_BINDING | RENDER_ATTACHMENT\",\n            view_formats: [],\n        )),\n        CreateTextureView(\n            id: PointerId(0x10),\n            parent: PointerId(0x10),\n            desc: (),\n        ),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Output Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n        CreatePipelineLayout(PointerId(0x10), (\n            label: None,\n            bind_group_layouts: [],\n            immediate_size: 0,\n        )),\n        CreateGeneralRenderPipeline(\n            id: Some(PointerId(0x10)),\n            desc: (\n                label: None,\n                layout: Some(PointerId(0x10)),\n                vertex: Vertex(\n                    VertexState(\n                        stage: (\n                            module: PointerId(0x10),\n                            entry_point: None,\n                            constants: {},\n                            zero_initialize_workgroup_memory: true,\n                            vertex_pulling_transform: false,\n                        ),\n                        buffers: [],\n                    ),\n                ),\n                fragment: Some((\n                    stage: (\n                        module: PointerId(0x10),\n                        entry_point: None,\n                        constants: {},\n                        zero_initialize_workgroup_memory: true,\n                        vertex_pulling_transform: false,\n                    ),\n                    targets: [\n                        Some((\n                            format: \"rgba8unorm\",\n                        )),\n                    ],\n                )),\n            ),\n        ),\n        Submit(1, [\n            RunRenderPass(\n                pass: (\n                    commands: [\n                        SetPipeline(PointerId(0x10)),\n                        Draw(\n                            vertex_count: 3,\n                            instance_count: 1,\n                            first_vertex: 0,\n                            first_instance: 0,\n                        ),\n                    ],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n                color_attachments: [\n                    Some((\n                        view: PointerId(0x10),\n                        resolve_target: None,\n                        load_op: clear(Color(\n                            r: 0,\n                            g: 0,\n                            b: 0,\n                            a: 1,\n                        )),\n                        store_op: store,\n                    )),\n                ],\n                depth_stencil_attachment: None,\n            ),\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x10),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x10),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: Some(64),\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/quad.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {\n    // hacky way to draw a large triangle\n    let tmp1 = i32(vertex_index) / 2;\n    let tmp2 = i32(vertex_index) & 1;\n    let pos = vec2<f32>(\n        f32(tmp1) * 4.0 - 1.0,\n        f32(tmp2) * 4.0 - 1.0\n    );\n    return vec4<f32>(pos, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "player/tests/player/data/zero-init-buffer-for-binding.wgsl",
    "content": "@group(0)\n@binding(0)\nvar<storage, read_write> buf: array<u32>;\n\n@compute\n@workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    buf[global_id.x] = buf[global_id.x] + global_id.x;\n}\n"
  },
  {
    "path": "player/tests/player/data/zero-init-buffer.ron",
    "content": "(\n    features: \"MAPPABLE_PRIMARY_BUFFERS\",\n    expectations: [\n        // Ensuring that mapping zero-inits buffers.\n        (\n            name: \"mapped_at_creation: false, with MAP_WRITE\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: Raw([0x00, 0x00, 0x00, 0x00]),\n        ),\n        (\n            name: \"mapped_at_creation: false, without MAP_WRITE\",\n            buffer: PointerId(0x110),\n            offset: 0,\n            data: Raw([0x00, 0x00, 0x00, 0x00]),\n        ),\n        (\n            name: \"partially written buffer\",\n            buffer: PointerId(0x120),\n            offset: 0,\n            data: Raw([0x00, 0x00, 0x00, 0x00,\n                       0x00, 0x00, 0x80, 0xBF,\n                       0x00, 0x00, 0x80, 0xBF,\n                       0x00, 0x00, 0x80, 0x3F,\n                       0x00, 0x00, 0x80, 0x3F,\n                       0x00, 0x00, 0x00, 0x00]),\n        ),\n        // Ensuring that binding zero-inits buffers\n        // (by observing correct side effects of compute shader reading & writing values)\n        (\n            name: \"buffer has correct values\",\n            buffer: PointerId(0x130),\n            offset: 0,\n            data: Raw([0x00, 0x00, 0x00, 0x00,\n                       0x01, 0x00, 0x00, 0x00,\n                       0x02, 0x00, 0x00, 0x00,\n                       0x03, 0x00, 0x00, 0x00]),\n        )\n    ],\n    actions: [\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"mapped_at_creation: false, with MAP_WRITE\"),\n                size: 16,\n                usage: \"STORAGE | MAP_READ | MAP_WRITE\",\n                mapped_at_creation: false,\n            ),\n        ),\n        CreateBuffer(\n            PointerId(0x110),\n            (\n                label: Some(\"mapped_at_creation: false, without MAP_WRITE\"),\n                size: 16,\n                usage: \"STORAGE | MAP_READ\",\n                mapped_at_creation: false,\n            ),\n        ),\n        CreateBuffer(\n            PointerId(0x120),\n            (\n                label: Some(\"partially written\"),\n                size: 24,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n        WriteBuffer(\n            id: PointerId(0x120),\n            data: File(\"data1.bin\"),\n            offset: 4,\n            size: 16,\n            queued: true,\n        ),\n        CreateShaderModule(\n            id: PointerId(0x10),\n            desc: (\n                label: None,\n                flags: (bits: 3),\n            ),\n            data: File(\"zero-init-buffer-for-binding.wgsl\"),\n        ),\n        CreateBuffer(PointerId(0x130), (\n            label: Some(\"used in binding\"),\n            size: 16,\n            usage: \"STORAGE | MAP_READ\",\n            mapped_at_creation: false,\n        )),\n        CreateBindGroupLayout(PointerId(0x10), (\n            label: None,\n            entries: [\n                (\n                    binding: 0,\n                    visibility: \"COMPUTE\",\n                    ty: Buffer(\n                        ty: Storage(\n                            read_only: false,\n                        ),\n                        has_dynamic_offset: false,\n                        min_binding_size: Some(16),\n                    ),\n                    count: None,\n                ),\n            ],\n        )),\n        CreateBindGroup(PointerId(0x10), (\n            label: None,\n            layout: PointerId(0x10),\n            entries: [\n                (\n                    binding: 0,\n                    resource: Buffer((\n                        buffer: PointerId(0x130),\n                        offset: 0,\n                        size: Some(16),\n                    )),\n                ),\n            ],\n        )),\n        CreatePipelineLayout(PointerId(0x10), (\n            label: None,\n            bind_group_layouts: [\n                Some(PointerId(0x10)),\n            ],\n            immediate_size: 0,\n        )),\n        CreateComputePipeline(\n            id: Some(PointerId(0x10)),\n            desc: (\n                label: None,\n                layout: Some(PointerId(0x10)),\n                stage: (\n                    module: PointerId(0x10),\n                    entry_point: None,\n                    constants: {},\n                    zero_initialize_workgroup_memory: true,\n                    vertex_pulling_transform: false,\n                ),\n            ),\n        ),\n        Submit(1, [\n            RunComputePass(\n                pass: (\n                    label: None,\n                    commands: [\n                        SetPipeline(PointerId(0x10)),\n                        SetBindGroup(\n                            index: 0,\n                            num_dynamic_offsets: 0,\n                            bind_group: Some(PointerId(0x10)),\n                        ),\n                        Dispatch((4, 1, 1)),\n                    ],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n            )\n        ]),\n    ]\n)\n"
  },
  {
    "path": "player/tests/player/data/zero-init-texture-binding.ron",
    "content": "(\n    features: \"\",\n    expectations: [\n        (\n            name: \"Sampled Texture\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: File(\"zero-16k.bin\", 16384),\n        ),\n        (\n            name: \"Storage Texture\",\n            buffer: PointerId(0x110),\n            offset: 0,\n            data: File(\"zero-16k.bin\", 16384),\n        ),\n        // MISSING: Texture binding arrays\n        // MISSING: Partial views\n    ],\n    actions: [\n        CreateTexture(PointerId(0x10), (\n            label: Some(\"Sampled Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"TEXTURE_BINDING | COPY_SRC\",\n            view_formats: [],\n        )),\n        CreateTextureView(\n            id: PointerId(0x10),\n            parent: PointerId(0x10),\n            desc: (),\n        ),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Sampled Texture Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n        CreateTexture(PointerId(0x110), (\n            label: Some(\"Storage Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"STORAGE_BINDING | COPY_SRC\",\n            view_formats: [],\n        )),\n        CreateTextureView(\n            id: PointerId(0x110),\n            parent: PointerId(0x110),\n            desc: (),\n        ),\n        CreateBuffer(\n            PointerId(0x110),\n            (\n                label: Some(\"Storage Texture Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n\n\n        CreateBindGroupLayout(PointerId(0x10), (\n            label: None,\n            entries: [\n                (\n                    binding: 0,\n                    visibility: \"COMPUTE\",\n                    ty: Texture (\n                        sample_type: Float(filterable: true),\n                        view_dimension: r#2d,\n                        multisampled: false,\n                    ),\n                    count: None,\n                ),\n                (\n                    binding: 1,\n                    visibility: \"COMPUTE\",\n                    ty: StorageTexture (\n                        access: r#write-only,\n                        format: \"rgba8unorm\",\n                        view_dimension: r#2d,\n                    ),\n                    count: None,\n                ),\n            ],\n        )),\n        CreateBindGroup(PointerId(0x10), (\n            label: None,\n            layout: PointerId(0x10),\n            entries: [\n                (\n                    binding: 0,\n                    resource: TextureView(PointerId(0x10)),\n                ),\n                (\n                    binding: 1,\n                    resource: TextureView(PointerId(0x110)),\n                ),\n            ],\n        )),\n        CreatePipelineLayout(PointerId(0x10), (\n            label: None,\n            bind_group_layouts: [\n                Some(PointerId(0x10)),\n            ],\n            immediate_size: 0,\n        )),\n        CreateShaderModule(\n            id: PointerId(0x10),\n            desc: (\n                label: None,\n                flags: (bits: 3),\n            ),\n            data: File(\"zero-init-texture-binding.wgsl\"),\n        ),\n        CreateComputePipeline(\n            id: Some(PointerId(0x10)),\n            desc: (\n                label: None,\n                layout: Some(PointerId(0x10)),\n                stage: (\n                    module: PointerId(0x10),\n                    entry_point: None,\n                    constants: {},\n                    zero_initialize_workgroup_memory: true,\n                    vertex_pulling_transform: false,\n                ),\n            ),\n        ),\n\n        Submit(1, [\n            RunComputePass(\n                pass: (\n                    commands: [\n                        SetPipeline(PointerId(0x10)),\n                        SetBindGroup(\n                            index: 0,\n                            num_dynamic_offsets: 0,\n                            bind_group: Some(PointerId(0x10)),\n                        ),\n                        Dispatch((4, 1, 1)),\n                    ],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n            ),\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x10),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x10),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: Some(64),\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x110),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x110),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: Some(64),\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/zero-init-texture-binding.wgsl",
    "content": "@group(0) @binding(0) var tex: texture_2d<f32>;\n@group(0) @binding(1) var tex_storage: texture_storage_2d<rgba8uint, write>;\n\n@compute\n@workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n}\n"
  },
  {
    "path": "player/tests/player/data/zero-init-texture-copytobuffer.ron",
    "content": "(\n    features: \"\",\n    expectations: [\n        (\n            name: \"Copy to Buffer\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: File(\"zero-16k.bin\", 16384),\n        ),\n        // MISSING: Partial copies\n    ],\n    actions: [\n        CreateTexture(PointerId(0x10), (\n            label: Some(\"Copy To Buffer Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"COPY_SRC\",\n            view_formats: [],\n        )),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Copy to Buffer Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n        Submit(1, [\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x10),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x10),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: Some(64),\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/data/zero-init-texture-rendertarget.ron",
    "content": "(\n    features: \"\",\n    expectations: [\n        (\n            name: \"Render Target\",\n            buffer: PointerId(0x10),\n            offset: 0,\n            data: File(\"zero-16k.bin\", 16384),\n        ),\n        // MISSING: Partial view.\n    ],\n    actions: [\n        CreateTexture(PointerId(0x10), (\n            label: Some(\"Render Target Texture\"),\n            size: (\n                width: 64,\n                height: 64,\n            ),\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: r#2d,\n            format: \"rgba8unorm\",\n            usage: \"RENDER_ATTACHMENT | COPY_SRC\",\n            view_formats: [],\n        )),\n        CreateTextureView(\n            id: PointerId(0x10),\n            parent: PointerId(0x10),\n            desc: (),\n        ),\n        CreateBuffer(\n            PointerId(0x10),\n            (\n                label: Some(\"Render Target Buffer\"),\n                size: 16384,\n                usage: \"MAP_READ | COPY_DST\",\n                mapped_at_creation: false,\n            ),\n        ),\n\n        Submit(1, [\n            RunRenderPass(\n                pass: (\n                    commands: [],\n                    dynamic_offsets: [],\n                    string_data: [],\n                    immediates_data: [],\n                ),\n                color_attachments: [\n                    Some((\n                        view: PointerId(0x10),\n                        resolve_target: None,\n                        load_op: load,\n                        store_op: store,\n                        clear_value: (\n                            r: 1, g: 1, b: 1, a: 1,\n                        ),\n                    )),\n                ],\n                depth_stencil_attachment: None,\n            ),\n            CopyTextureToBuffer(\n                src: (\n                    texture: PointerId(0x10),\n                    mip_level: 0,\n                    array_layer: 0,\n                ),\n                dst:  (\n                    buffer: PointerId(0x10),\n                    layout: (\n                        offset: 0,\n                        bytes_per_row: Some(256),\n                        rows_per_image: Some(64),\n                    ),\n                ),\n                size: (\n                    width: 64,\n                    height: 64,\n                ),\n            ),\n        ]),\n    ],\n)\n"
  },
  {
    "path": "player/tests/player/main.rs",
    "content": "//! Tester for WebGPU\n//!  It enumerates the available backends on the system,\n//!  and run the tests through them.\n//!\n//!  Test requirements:\n//!    - all IDs have the backend `Noop`\n//!    - all expected buffers have `MAP_READ` usage\n//!    - last action is `Submit`\n//!    - no swapchain use\n\n#![cfg(not(target_arch = \"wasm32\"))]\n\nextern crate wgpu_core as wgc;\nextern crate wgpu_types as wgt;\n\nuse player::Player;\nuse std::{\n    fs::{read_to_string, File},\n    io::{Read, Seek, SeekFrom},\n    path::{Path, PathBuf},\n    slice,\n    sync::Arc,\n};\nuse wgc::{command::PointerReferences, device::trace::DiskTraceLoader};\n\n#[derive(serde::Deserialize)]\nenum ExpectedData {\n    Raw(Vec<u8>),\n    U64(Vec<u64>),\n    File(String, usize),\n}\n\nimpl ExpectedData {\n    fn len(&self) -> usize {\n        match self {\n            ExpectedData::Raw(vec) => vec.len(),\n            ExpectedData::U64(vec) => vec.len() * size_of::<u64>(),\n            ExpectedData::File(_, size) => *size,\n        }\n    }\n}\n\n#[derive(serde::Deserialize)]\nstruct Expectation {\n    name: String,\n    buffer: wgc::id::PointerId<wgc::id::markers::Buffer>,\n    offset: wgt::BufferAddress,\n    data: ExpectedData,\n}\n\n#[derive(serde::Deserialize)]\nstruct Test<'a> {\n    features: wgt::Features,\n    expectations: Vec<Expectation>,\n    actions: Vec<wgc::device::trace::Action<'a, PointerReferences>>,\n}\n\nfn map_callback(status: Result<(), wgc::resource::BufferAccessError>) {\n    if let Err(e) = status {\n        panic!(\"Buffer map error: {e}\");\n    }\n}\n\nimpl Test<'_> {\n    fn load(path: PathBuf, backend: wgt::Backend) -> Self {\n        let backend_name = match backend {\n            wgt::Backend::Vulkan => \"Vulkan\",\n            wgt::Backend::Metal => \"Metal\",\n            wgt::Backend::Dx12 => \"Dx12\",\n            wgt::Backend::Gl => \"Gl\",\n            _ => unreachable!(),\n        };\n        let string = read_to_string(&path).unwrap().replace(\"Noop\", backend_name);\n        ron::de::from_str(&string).unwrap_or_else(|e| panic!(\"{path:?}:{} {}\", e.span, e.code))\n    }\n\n    fn run(\n        self,\n        dir: &Path,\n        instance_flags: wgt::InstanceFlags,\n        adapter: Arc<wgc::instance::Adapter>,\n    ) {\n        let (device, queue) = adapter\n            .create_device_and_queue(\n                &wgt::DeviceDescriptor {\n                    label: None,\n                    required_features: self.features,\n                    required_limits: wgt::Limits::default(),\n                    experimental_features: unsafe { wgt::ExperimentalFeatures::enabled() },\n                    memory_hints: wgt::MemoryHints::default(),\n                    trace: wgt::Trace::Off,\n                },\n                instance_flags,\n            )\n            .unwrap();\n\n        let mut player = Player::default();\n\n        println!(\"\\t\\t\\tRunning...\");\n        for action in self.actions {\n            player.process(&device, &queue, action, DiskTraceLoader::new(dir));\n        }\n        println!(\"\\t\\t\\tMapping...\");\n        for expect in &self.expectations {\n            player\n                .resolve_buffer_id(expect.buffer)\n                .map_async(\n                    expect.offset,\n                    Some(expect.data.len() as u64),\n                    wgc::resource::BufferMapOperation {\n                        host: wgc::device::HostMap::Read,\n                        callback: Some(Box::new(map_callback)),\n                    },\n                )\n                .unwrap();\n        }\n\n        println!(\"\\t\\t\\tWaiting...\");\n        device\n            .poll(wgt::PollType::Wait {\n                submission_index: None,\n                timeout: Some(std::time::Duration::from_secs(1)), // Tests really shouldn't need longer than that!\n            })\n            .unwrap();\n\n        for expect in self.expectations {\n            println!(\"\\t\\t\\tChecking {}\", expect.name);\n            let (ptr, size) = player\n                .resolve_buffer_id(expect.buffer)\n                .get_mapped_range(expect.offset, Some(expect.data.len() as wgt::BufferAddress))\n                .unwrap();\n            let contents = unsafe { slice::from_raw_parts(ptr.as_ptr(), size as usize) };\n            let expected_data = match expect.data {\n                ExpectedData::Raw(vec) => vec,\n                ExpectedData::File(name, size) => {\n                    let mut bin = vec![0; size];\n                    let mut file = File::open(dir.join(name)).unwrap();\n                    file.seek(SeekFrom::Start(expect.offset)).unwrap();\n                    file.read_exact(&mut bin[..]).unwrap();\n\n                    bin\n                }\n                ExpectedData::U64(vec) => vec\n                    .into_iter()\n                    .flat_map(|u| u.to_ne_bytes().to_vec())\n                    .collect::<Vec<u8>>(),\n            };\n\n            if &expected_data[..] != contents {\n                panic!(\n                    \"Test expectation is not met!\\nBuffer content was:\\n{contents:?}\\nbut expected:\\n{expected_data:?}\"\n                );\n            }\n        }\n    }\n}\n\n#[derive(serde::Deserialize)]\nstruct Corpus {\n    backends: wgt::Backends,\n    tests: Vec<String>,\n}\n\nconst BACKENDS: &[wgt::Backend] = &[\n    wgt::Backend::Vulkan,\n    wgt::Backend::Metal,\n    wgt::Backend::Dx12,\n    wgt::Backend::Gl,\n];\n\nimpl Corpus {\n    fn run_from(path: PathBuf) {\n        println!(\"Corpus {path:?}\");\n        let dir = path.parent().unwrap();\n        let corpus: Corpus = ron::de::from_reader(File::open(&path).unwrap()).unwrap();\n\n        for &backend in BACKENDS {\n            if !corpus.backends.contains(backend.into()) {\n                continue;\n            }\n            for test_path in &corpus.tests {\n                println!(\"\\t\\tTest '{test_path:?}'\");\n\n                let instance_desc = wgt::InstanceDescriptor::new_without_display_handle_from_env();\n                let instance_flags = instance_desc.flags;\n                let instance = wgc::instance::Instance::new(\"test\", instance_desc, None);\n                let adapter = match instance.request_adapter(\n                    &wgt::RequestAdapterOptions {\n                        power_preference: wgt::PowerPreference::None,\n                        force_fallback_adapter: false,\n                        compatible_surface: None,\n                    },\n                    wgt::Backends::from(backend),\n                ) {\n                    Ok(adapter) => Arc::new(adapter),\n                    Err(_) => continue,\n                };\n\n                println!(\"\\tBackend {backend:?}\");\n                let supported_features = adapter.features();\n                let downlevel_caps = adapter.downlevel_capabilities();\n\n                let test = Test::load(dir.join(test_path), backend);\n                if !supported_features.contains(test.features) {\n                    println!(\n                        \"\\t\\tSkipped due to missing features {:?}\",\n                        test.features - supported_features\n                    );\n                    continue;\n                }\n                if !downlevel_caps\n                    .flags\n                    .contains(wgt::DownlevelFlags::COMPUTE_SHADERS)\n                {\n                    println!(\"\\t\\tSkipped due to missing compute shader capability\");\n                    continue;\n                }\n                test.run(dir, instance_flags, adapter);\n            }\n        }\n    }\n}\n\n#[cfg_attr(miri, ignore)]\n#[test]\nfn test_api() {\n    env_logger::init();\n\n    Corpus::run_from(PathBuf::from(env!(\"CARGO_MANIFEST_DIR\")).join(\"tests/player/data/all.ron\"))\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:recommended\",\n    \"schedule:weekly\"\n  ],\n  \"dependencyDashboard\": true,\n  \"prConcurrentLimit\": 20,\n  \"prHourlyLimit\": 200,\n  \"labels\": [\n    \"dependencies\"\n  ],\n  \"lockFileMaintenance\": {\n    \"enabled\": true,\n    \"recreateWhen\": \"always\",\n    \"rebaseWhen\": \"behind-base-branch\",\n    \"branchTopic\": \"Cargo.lock update\",\n    \"commitMessageAction\": \"Update Cargo.lock\",\n    \"schedule\": [\n      \"before 4am on monday\"\n    ],\n    \"prBodyDefinitions\": {\n      \"Change\": \"All locks refreshed\"\n    }\n  },\n  \"packageRules\": [\n    {\n      \"matchUpdateTypes\": [\n        \"patch\"\n      ],\n      \"matchCurrentVersion\": \"<1.0.0\",\n      \"enabled\": false,\n      \"description\": \"Patch updates to 0.x.y crates are compatible and handled by lockFileMaintenance\"\n    },\n    {\n      \"matchUpdateTypes\": [\n        \"minor\",\n        \"patch\"\n      ],\n      \"matchCurrentVersion\": \">=1.0.0\",\n      \"enabled\": false,\n      \"description\": \"Minor and patch updates to x.y.z crates are compatible and handled by lockFileMaintenance\"\n    },\n    {\n      \"description\": \"IGNORE: Windows 0.59 is pending on https://github.com/gfx-rs/wgpu/pull/6876\",\n      \"matchPackageNames\": [\n        \"windows\",\n        \"windows-core\"\n      ],\n      \"matchCurrentVersion\": \"<0.59.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"IGNORE: Winit 0.30 is pending a major refactor. https://github.com/gfx-rs/wgpu/pull/5709\",\n      \"matchPackageNames\": [\n        \"winit\"\n      ],\n      \"matchCurrentVersion\": \"<0.30.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"IGNORE: glutin 0.32 depends on winit 0.30\",\n      \"matchPackageNames\": [\n        \"glutin\"\n      ],\n      \"matchCurrentVersion\": \"<0.32.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"IGNORE: glutin-winit 0.5 depends on winit 0.30\",\n      \"matchPackageNames\": [\n        \"glutin-winit\"\n      ],\n      \"matchCurrentVersion\": \"<0.5.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"IGNORE: glutin 0.32 depends on winit 0.30, and previous version depend on rwh 0.5\",\n      \"matchPackageNames\": [\n        \"raw-window-handle\"\n      ],\n      \"matchCurrentVersion\": \"<0.6.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"IGNORE: libfuzzer-sys 0.4.8 has a broken build on Windows.\",\n      \"matchPackageNames\": [\n        \"libfuzzer-sys\"\n      ],\n      \"matchCurrentVersion\": \"<0.5.0\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"PERMA IGNORE: rustc-hash 2.0 is a different algorithm.\",\n      \"matchPackageNames\": [\n        \"rustc-hash\"\n      ],\n      \"matchCurrentVersion\": \"<2\",\n      \"enabled\": false\n    },\n    {\n      \"description\": \"PERMA IGNORE: Deno packages are rolled up explicitly by the Deno team, in lockstep with deno_webgpu.\",\n      \"matchPackageNames\": [\n        \"deno_*\"\n      ],\n      \"enabled\": false\n    },\n    {\n      \"description\": \"CTS revision is pinned to a Git SHA.\",\n      \"matchPackageNames\": [\n        \"https://github.com/gpuweb/cts\"\n      ],\n      \"versioning\": \"git\"\n    }\n  ],\n  \"customManagers\": [\n    {\n      \"customType\": \"regex\",\n      \"managerFilePatterns\": [\"/^cts_runner\\\\/revision\\\\.txt$/\"],\n      \"matchStrings\": [\n        \"(?<currentDigest>.{40})\"\n      ],\n      \"currentValueTemplate\": \"gh-pages\",\n      \"depNameTemplate\": \"cts\",\n      \"packageNameTemplate\": \"https://github.com/gpuweb/cts\",\n      \"datasourceTemplate\": \"git-refs\"\n    }\n  ]\n}\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"1.93\"\ncomponents = [\"cargo\", \"rustfmt\", \"clippy\"]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "\n"
  },
  {
    "path": "taplo.toml",
    "content": "[formatting]\nindent_string = \"    \"\n"
  },
  {
    "path": "tests/Cargo.toml",
    "content": "[package]\nname = \"wgpu-test\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"common code for wgpu tests\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\npublish = false\n\n[[test]]\nname = \"wgpu-compile\"\nharness = true\n\n[[test]]\nname = \"wgpu-dependency\"\nharness = true\n\n[[test]]\nname = \"wgpu-gpu\"\nharness = false\n\n[[test]]\nname = \"wgpu_trace\"\nharness = true\n\n[[test]]\nname = \"wgpu-validation\"\nharness = true\n\n[features]\nwebgl = [\"wgpu/webgl\"]\n# This feature is not actually used by this package, but it being present somewhere in the workspace\n# allows us to force the build to have profiling code enabled so we can test that configuration.\ntest-build-with-profiling = [\"profiling/type-check\"]\n\n# Note that even though this is a package of tests and a library of tests, there is a split of\n# [depenencies] and [dev-dependencies]. This is because keeping as many deps as possible as\n# dev-dependencies will make the dependency graph just a bit shallower, and makes\n# `cargo clippy -- -Wunused-crate-dependencies` give fewer false positives.\n[dependencies]\nwgpu = { workspace = true, features = [\"noop\"] }\nwgpu-core = { workspace = true, features = [\"trace\"] }\nwgpu-hal = { workspace = true, features = [\"validation_canary\"] }\nwgpu-macros.workspace = true\nwgpu-types.workspace = true\nnaga = { workspace = true, features = [\n    \"spv-out\",\n    \"wgsl-out\",\n    \"hlsl-out\",\n    \"msl-out\",\n    \"glsl-out\",\n] }\n\nanyhow.workspace = true\narrayvec.workspace = true\nbitflags.workspace = true\nbytemuck.workspace = true\ncfg-if.workspace = true\nctor.workspace = true\nfutures-lite.workspace = true\nlibtest-mimic.workspace = true\nlog.workspace = true\npng.workspace = true\npollster.workspace = true\nprofiling.workspace = true\nraw-window-handle.workspace = true\nserde.workspace = true\nserde_json.workspace = true\n\n[dev-dependencies]\napprox.workspace = true\nglam.workspace = true\nhalf = { workspace = true, features = [\"bytemuck\", \"std\"] }\nitertools.workspace = true\nimage.workspace = true\nnanorand.workspace = true\nparking_lot.workspace = true\nstrum = { workspace = true, features = [\"derive\"] }\ntrybuild.workspace = true\n\n# Non-Webassembly\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\n# Cargo-metadata doesn't compile on wasm due to old cargo-util-schemas dependency.\ncargo_metadata.workspace = true\nenv_logger.workspace = true\nparking_lot = { workspace = true, features = [\"deadlock_detection\"] }\n\n[target.'cfg(not(any(target_arch = \"wasm32\", miri)))'.dependencies]\nnv-flip.workspace = true\n\n# Webassembly\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\nconsole_log.workspace = true\nwasm-bindgen.workspace = true\nweb-sys = { workspace = true }\n\n# Webassembly Dev Dependencies\n[target.'cfg(target_arch = \"wasm32\")'.dev-dependencies]\nimage.workspace = true\njs-sys.workspace = true\nwasm-bindgen-futures.workspace = true\nwasm-bindgen-test.workspace = true\nwasm-bindgen.workspace = true\nweb-sys = { workspace = true, features = [\"CanvasRenderingContext2d\", \"Blob\"] }\n\n[lints.clippy]\nbool_assert_comparison = \"allow\"\ndisallowed_types = \"allow\"\n"
  },
  {
    "path": "tests/src/config.rs",
    "content": "use std::{future::Future, panic::Location, pin::Pin, sync::Arc};\n\nuse crate::{TestParameters, TestingContext};\n\ncfg_if::cfg_if! {\n    if #[cfg(target_arch = \"wasm32\")] {\n        pub type RunTestAsync = Arc<dyn Fn(TestingContext) -> Pin<Box<dyn Future<Output = ()>>>>;\n\n        // We can't use WasmNonSend and WasmNonSync here, as we need these to not require Send/Sync\n        // even with the `fragile-send-sync-non-atomic-wasm` enabled.\n        pub trait RunTestSendSync {}\n        impl<T> RunTestSendSync for T {}\n        pub trait RunTestSend {}\n        impl<T> RunTestSend for T {}\n    } else {\n        pub type RunTestAsync = Arc<dyn Fn(TestingContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;\n\n        pub trait RunTestSend: Send {}\n        impl<T> RunTestSend for T where T: Send {}\n        pub trait RunTestSendSync: Send + Sync {}\n        impl<T> RunTestSendSync for T where T: Send + Sync {}\n    }\n}\n\n/// Configuration for a GPU test.\n#[derive(Clone)]\npub struct GpuTestConfiguration {\n    pub(crate) name: String,\n    pub(crate) location: &'static Location<'static>,\n    pub(crate) params: TestParameters,\n    pub(crate) test: Option<RunTestAsync>,\n}\n\nimpl GpuTestConfiguration {\n    #[track_caller]\n    pub fn new() -> Self {\n        Self {\n            name: String::new(),\n            location: Location::caller(),\n            params: TestParameters::default(),\n            test: None,\n        }\n    }\n\n    /// Set the name of the test. Must be unique across all tests in the binary.\n    pub fn name(self, name: &str) -> Self {\n        Self {\n            name: String::from(name),\n            ..self\n        }\n    }\n\n    #[doc(hidden)]\n    /// Derives the name from a `struct S` in the function initializing the test.\n    ///\n    /// Does not overwrite a given name if a name has already been set\n    pub fn name_from_init_function_typename<S>(self, name: &'static str) -> Self {\n        if !self.name.is_empty() {\n            return self;\n        }\n        let type_name = std::any::type_name::<S>();\n\n        // We end up with a string like:\n        //\n        // module::path::we::want::test_name_initializer::S\n        //\n        // So we reverse search for the 4th colon from the end, and take everything before that.\n        let mut colons = 0;\n        let mut colon_4_index = type_name.len();\n        for i in (0..type_name.len()).rev() {\n            if type_name.as_bytes()[i] == b':' {\n                colons += 1;\n            }\n            if colons == 4 {\n                colon_4_index = i;\n                break;\n            }\n        }\n\n        let full = format!(\"{}::{}\", &type_name[..colon_4_index], name);\n        Self { name: full, ..self }\n    }\n\n    /// Set the parameters that the test needs to succeed.\n    pub fn parameters(self, parameters: TestParameters) -> Self {\n        Self {\n            params: parameters,\n            ..self\n        }\n    }\n\n    /// Make the test function an synchronous function.\n    pub fn run_sync(\n        self,\n        test: impl Fn(TestingContext) + Copy + RunTestSendSync + 'static,\n    ) -> Self {\n        Self {\n            test: Some(Arc::new(move |ctx| Box::pin(async move { test(ctx) }))),\n            ..self\n        }\n    }\n\n    /// Make the test function an asynchronous function/future.\n    pub fn run_async<F, R>(self, test: F) -> Self\n    where\n        F: Fn(TestingContext) -> R + RunTestSendSync + 'static,\n        R: Future<Output = ()> + RunTestSend + 'static,\n    {\n        Self {\n            test: Some(Arc::new(move |ctx| Box::pin(test(ctx)))),\n            ..self\n        }\n    }\n}\n\nimpl Default for GpuTestConfiguration {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub type GpuTestInitializer = fn() -> GpuTestConfiguration;\n"
  },
  {
    "path": "tests/src/copy_texture_to_buffer.wgsl",
    "content": "@group(0) @binding(0)\nvar texture: texture_2d_array<{{type}}>;\n\n@group(0) @binding(1)\nvar<storage, read_write> output: array<{{type}}>;\n\n@compute @workgroup_size(1)\nfn copy_texture_to_buffer() {\n    let layers = i32(textureNumLayers(texture));\n    let dim = textureDimensions(texture);\n    for (var l = 0; l < layers; l++) {\n        for (var y = 0u; y < dim.y; y++) {\n            for (var x = 0u; x < dim.x; x++) {\n                output[x + y * dim.x] = textureLoad(texture, vec2(x, y), l, 0).x;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/src/expectations.rs",
    "content": "use core::fmt;\n\n/// Conditions under which a test should fail or be skipped.\n///\n/// By passing a `FailureCase` to [`TestParameters::expect_fail`][expect_fail], you can\n/// mark a test as expected to fail under the indicated conditions. By\n/// passing it to [`TestParameters::skip`][skip], you can request that the\n/// test be skipped altogether.\n///\n/// If a field is `None`, then that field does not restrict matches. For\n/// example:\n///\n/// ```\n/// # use wgpu_test::*;\n/// FailureCase {\n///     backends: Some(wgpu::Backends::DX12),\n///     vendor: None,\n///     adapter: Some(\"RTX\"),\n///     driver: None,\n///     reasons: vec![FailureReason::validation_error().with_message(\"Some error substring\")],\n///     behavior: FailureBehavior::AssertFailure,\n/// }\n/// # ;\n/// ```\n///\n/// This applies to all cards with `\"RTX'` in their name on either\n/// Direct3D backend, no matter the vendor ID or driver name.\n///\n/// The strings given here need only appear as a substring in the\n/// corresponding [`AdapterInfo`] fields. The comparison is\n/// case-insensitive.\n///\n/// The default value of `FailureCase` applies to any test case. That\n/// is, there are no criteria to constrain the match.\n///\n/// [skip]: super::TestParameters::skip\n/// [expect_fail]: super::TestParameters::expect_fail\n/// [`AdapterInfo`]: wgpu::AdapterInfo\n#[derive(Default, Clone, PartialEq)]\npub struct FailureCase {\n    /// Backends expected to fail, or `None` for any backend.\n    ///\n    /// If this is `None`, or if the test is using one of the backends\n    /// in `backends`, then this `FailureCase` applies.\n    pub backends: Option<wgpu::Backends>,\n\n    /// Vendor expected to fail, or `None` for any vendor.\n    ///\n    /// If `Some`, this must match [`AdapterInfo::device`], which is\n    /// usually the PCI device id. Otherwise, this `FailureCase`\n    /// applies regardless of vendor.\n    ///\n    /// [`AdapterInfo::device`]: wgpu::AdapterInfo::device\n    pub vendor: Option<u32>,\n\n    /// Name of adapter expected to fail, or `None` for any adapter name.\n    ///\n    /// If this is `Some(s)` and `s` is a substring of\n    /// [`AdapterInfo::name`], then this `FailureCase` applies. If\n    /// this is `None`, the adapter name isn't considered.\n    ///\n    /// [`AdapterInfo::name`]: wgpu::AdapterInfo::name\n    pub adapter: Option<&'static str>,\n\n    /// Name of driver expected to fail, or `None` for any driver name.\n    ///\n    /// If this is `Some(s)` and `s` is a substring of\n    /// [`AdapterInfo::driver`], then this `FailureCase` applies. If\n    /// this is `None`, the driver name isn't considered.\n    ///\n    /// [`AdapterInfo::driver`]: wgpu::AdapterInfo::driver\n    pub driver: Option<&'static str>,\n\n    /// Reason why the test is expected to fail.\n    ///\n    /// If this does not match, the failure will not match this case.\n    ///\n    /// If no reasons are pushed, will match any failure.\n    pub reasons: Vec<FailureReason>,\n\n    /// Behavior after this case matches a failure.\n    pub behavior: FailureBehavior,\n}\n\nimpl FailureCase {\n    /// Create a new failure case.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// This case applies to all tests.\n    pub fn always() -> Self {\n        FailureCase::default()\n    }\n\n    /// This case applies to no tests.\n    pub fn never() -> Self {\n        FailureCase {\n            backends: Some(wgpu::Backends::empty()),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running on any of the given backends.\n    pub fn backend(backends: wgpu::Backends) -> Self {\n        FailureCase {\n            backends: Some(backends),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running on `adapter`.\n    ///\n    /// For this case to apply, the `adapter` string must appear as a substring\n    /// of the adapter's [`AdapterInfo::name`]. The comparison is\n    /// case-insensitive.\n    ///\n    /// [`AdapterInfo::name`]: wgpu::AdapterInfo::name\n    pub fn adapter(adapter: &'static str) -> Self {\n        FailureCase {\n            adapter: Some(adapter),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running on `backend` and `adapter`.\n    ///\n    /// For this case to apply, the test must be using an adapter for one of the\n    /// given `backend` bits, and `adapter` string must appear as a substring of\n    /// the adapter's [`AdapterInfo::name`]. The string comparison is\n    /// case-insensitive.\n    ///\n    /// [`AdapterInfo::name`]: wgpu::AdapterInfo::name\n    pub fn backend_adapter(backends: wgpu::Backends, adapter: &'static str) -> Self {\n        FailureCase {\n            backends: Some(backends),\n            adapter: Some(adapter),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running under WebGL.\n    pub fn webgl2() -> Self {\n        #[cfg(target_arch = \"wasm32\")]\n        let case = FailureCase::backend(wgpu::Backends::GL);\n        #[cfg(not(target_arch = \"wasm32\"))]\n        let case = FailureCase::never();\n        case\n    }\n\n    /// Tests running on the MoltenVK Vulkan driver on macOS.\n    pub fn molten_vk() -> Self {\n        FailureCase {\n            backends: Some(wgpu::Backends::VULKAN),\n            driver: Some(\"MoltenVK\"),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running on the KosmicKrisp Vulkan driver on macOS.\n    pub fn kosmic_krisp() -> Self {\n        FailureCase {\n            backends: Some(wgpu::Backends::VULKAN),\n            driver: Some(\"KosmicKrisp\"),\n            ..FailureCase::default()\n        }\n    }\n\n    /// Tests running on either Vulkan driver on macOS.\n    pub fn mac_vulkan(f: impl Fn(FailureCase) -> FailureCase) -> Vec<Self> {\n        vec![f(FailureCase::molten_vk()), f(FailureCase::kosmic_krisp())]\n    }\n\n    /// Return the reasons why this case should fail.\n    pub fn reasons(&self) -> &[FailureReason] {\n        if self.reasons.is_empty() {\n            std::array::from_ref(&FailureReason::ANY)\n        } else {\n            &self.reasons\n        }\n    }\n\n    /// Matches this failure case against the given validation error substring.\n    ///\n    /// Substrings are matched case-insensitively.\n    ///\n    /// If multiple reasons are pushed, will match any of them.\n    pub fn validation_error(mut self, msg: &'static str) -> Self {\n        self.reasons\n            .push(FailureReason::validation_error().with_message(msg));\n        self\n    }\n\n    /// Matches this failure case against the given panic substring.\n    ///\n    /// Substrings are matched case-insensitively.\n    ///\n    /// If multiple reasons are pushed, will match any of them.\n    pub fn panic(mut self, msg: &'static str) -> Self {\n        self.reasons.push(FailureReason::panic().with_message(msg));\n        self\n    }\n\n    /// Test is flaky with the given configuration. Do not assert failure.\n    ///\n    /// Use this _very_ sparyingly, and match as tightly as you can, including giving a specific failure message.\n    pub fn flaky(self) -> Self {\n        FailureCase {\n            behavior: FailureBehavior::Ignore,\n            ..self\n        }\n    }\n\n    /// Test whether `self` applies to `info`.\n    ///\n    /// If it does, return a `FailureReasons` whose set bits indicate\n    /// why. If it doesn't, return `None`.\n    ///\n    /// The caller is responsible for converting the string-valued\n    /// fields of `info` to lower case, to ensure case-insensitive\n    /// matching.\n    pub(crate) fn applies_to_adapter(\n        &self,\n        info: &wgpu::AdapterInfo,\n    ) -> Option<FailureApplicationReasons> {\n        let mut reasons = FailureApplicationReasons::empty();\n\n        if let Some(backends) = self.backends {\n            if !backends.contains(wgpu::Backends::from(info.backend)) {\n                return None;\n            }\n            reasons.set(FailureApplicationReasons::BACKEND, true);\n        }\n        if let Some(vendor) = self.vendor {\n            if vendor != info.vendor {\n                return None;\n            }\n            reasons.set(FailureApplicationReasons::VENDOR, true);\n        }\n        if let Some(adapter) = self.adapter {\n            let adapter = adapter.to_lowercase();\n            if !info.name.contains(&adapter) {\n                return None;\n            }\n            reasons.set(FailureApplicationReasons::ADAPTER, true);\n        }\n        if let Some(driver) = self.driver {\n            let driver = driver.to_lowercase();\n            if !info.driver.contains(&driver) {\n                return None;\n            }\n            reasons.set(FailureApplicationReasons::DRIVER, true);\n        }\n\n        // If we got this far but no specific reasons were triggered, then this\n        // must be a wildcard.\n        if reasons.is_empty() {\n            Some(FailureApplicationReasons::ALWAYS)\n        } else {\n            Some(reasons)\n        }\n    }\n\n    /// Returns true if the given failure \"satisfies\" this failure case.\n    pub(crate) fn matches_failure(&self, failure: &FailureResult) -> bool {\n        for reason in self.reasons() {\n            let kind_matched = reason.kind.is_none_or(|kind| kind == failure.kind);\n\n            let message_matched =\n                reason\n                    .message\n                    .is_none_or(|message| matches!(&failure.message, Some(actual) if actual.to_lowercase().contains(&message.to_lowercase())));\n\n            if kind_matched && message_matched {\n                let message = failure.message.as_deref().unwrap_or(\"*no message*\");\n                log::error!(\"Matched {} {message}\", failure.kind);\n                return true;\n            }\n        }\n\n        false\n    }\n}\n\nbitflags::bitflags! {\n    /// Reason why a test matches a given failure case.\n    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n    pub struct FailureApplicationReasons: u8 {\n        const BACKEND = 1 << 0;\n        const VENDOR = 1 << 1;\n        const ADAPTER = 1 << 2;\n        const DRIVER = 1 << 3;\n        const ALWAYS = 1 << 4;\n    }\n}\n\n/// Reason why a test is expected to fail.\n///\n/// If the test fails for a different reason, the given FailureCase will be ignored.\n#[derive(Default, Debug, Clone, PartialEq)]\npub struct FailureReason {\n    /// Match a particular kind of failure result.\n    ///\n    /// If `None`, match any result kind.\n    kind: Option<FailureResultKind>,\n    /// Match a particular message of a failure result.\n    ///\n    /// If `None`, matches any message. If `Some`, a case-insensitive sub-string\n    /// test is performed. Allowing `\"error occurred\"` to match a message like\n    /// `\"An unexpected Error occurred!\"`.\n    message: Option<&'static str>,\n}\n\nimpl FailureReason {\n    /// Match any failure reason.\n    const ANY: Self = Self {\n        kind: None,\n        message: None,\n    };\n\n    /// Match a validation error.\n    #[allow(dead_code)] // Not constructed on wasm\n    pub fn validation_error() -> Self {\n        Self {\n            kind: Some(FailureResultKind::ValidationError),\n            message: None,\n        }\n    }\n\n    /// Match a panic.\n    pub fn panic() -> Self {\n        Self {\n            kind: Some(FailureResultKind::Panic),\n            message: None,\n        }\n    }\n\n    /// Match an error with a message.\n    ///\n    /// If specified, a case-insensitive sub-string test is performed. Allowing\n    /// `\"error occurred\"` to match a message like `\"An unexpected Error\n    /// occurred!\"`.\n    pub fn with_message(self, message: &'static str) -> Self {\n        Self {\n            message: Some(message),\n            ..self\n        }\n    }\n}\n\n#[derive(Default, Clone, PartialEq)]\npub enum FailureBehavior {\n    /// Assert that the test fails for the given reason.\n    ///\n    /// If the test passes, the test harness will panic.\n    #[default]\n    AssertFailure,\n    /// Ignore the matching failure.\n    ///\n    /// This is useful for tests that flake in a very specific way,\n    /// but sometimes succeed, so we can't assert that they always fail.\n    Ignore,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum FailureResultKind {\n    #[allow(dead_code)] // Not constructed on wasm\n    ValidationError,\n    Panic,\n}\n\nimpl fmt::Display for FailureResultKind {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            FailureResultKind::ValidationError => write!(f, \"Validation Error\"),\n            FailureResultKind::Panic => write!(f, \"Panic\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct FailureResult {\n    kind: FailureResultKind,\n    message: Option<String>,\n}\n\nimpl FailureResult {\n    /// Failure result is a panic.\n    pub(super) fn panic() -> Self {\n        Self {\n            kind: FailureResultKind::Panic,\n            message: None,\n        }\n    }\n\n    /// Failure result is a validation error.\n    #[allow(dead_code)] // Not constructed on wasm\n    pub(super) fn validation_error() -> Self {\n        Self {\n            kind: FailureResultKind::ValidationError,\n            message: None,\n        }\n    }\n\n    /// Message associated with a failure result.\n    pub(super) fn with_message(self, message: impl fmt::Display) -> Self {\n        Self {\n            kind: self.kind,\n            message: Some(message.to_string()),\n        }\n    }\n}\n\n#[derive(PartialEq, Clone, Copy, Debug)]\npub(crate) enum ExpectationMatchResult {\n    Panic,\n    Complete,\n}\n\n/// Compares if the actual failures match the expected failures.\npub(crate) fn expectations_match_failures(\n    expectations: &[FailureCase],\n    mut actual: Vec<FailureResult>,\n) -> ExpectationMatchResult {\n    // Start with the assumption that we will pass.\n    let mut result = ExpectationMatchResult::Complete;\n\n    // Run through all expected failures.\n    for expected_failure in expectations {\n        // If any of the failures match.\n        let mut matched = false;\n\n        // Iterate through the failures.\n        //\n        // In reverse, to be able to use swap_remove.\n        actual.retain(|failure| {\n            // If the failure matches, remove it from the list of failures, as we expected it.\n            let matches = expected_failure.matches_failure(failure);\n\n            if matches {\n                matched = true;\n            }\n\n            // Retain removes on false, so flip the bool so we remove on failure.\n            !matches\n        });\n\n        // If we didn't match our expected failure against any of the actual failures,\n        // and this failure is not flaky, then we need to panic, as we got an unexpected success.\n        if !matched && matches!(expected_failure.behavior, FailureBehavior::AssertFailure) {\n            result = ExpectationMatchResult::Panic;\n            log::error!(\n                \"Expected to fail due to {:?}, but did not fail\",\n                expected_failure.reasons()\n            );\n        }\n    }\n\n    // If we have any failures left, then we got an unexpected failure\n    // and we need to panic.\n    if !actual.is_empty() {\n        result = ExpectationMatchResult::Panic;\n        for failure in actual {\n            let message = failure.message.as_deref().unwrap_or(\"*no message*\");\n            log::error!(\"{}: {message}\", failure.kind);\n        }\n    }\n\n    result\n}\n\n#[cfg(test)]\nmod test {\n    use crate::{\n        expectations::{ExpectationMatchResult, FailureResult},\n        init::init_logger,\n        FailureCase,\n    };\n\n    fn validation_err(msg: &'static str) -> FailureResult {\n        FailureResult::validation_error().with_message(msg)\n    }\n\n    fn panic(msg: &'static str) -> FailureResult {\n        FailureResult::panic().with_message(msg)\n    }\n\n    #[test]\n    fn simple_match() {\n        init_logger();\n\n        // -- Unexpected failure --\n\n        let expectation = vec![];\n        let actual = vec![FailureResult::validation_error()];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Panic\n        );\n\n        // -- Missing expected failure --\n\n        let expectation = vec![FailureCase::always()];\n        let actual = vec![];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Panic\n        );\n\n        // -- Expected failure (validation) --\n\n        let expectation = vec![FailureCase::always()];\n        let actual = vec![FailureResult::validation_error()];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n\n        // -- Expected failure (panic) --\n\n        let expectation = vec![FailureCase::always()];\n        let actual = vec![FailureResult::panic()];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n    }\n\n    #[test]\n    fn substring_match() {\n        init_logger();\n\n        // -- Matching Substring --\n\n        let expectation: Vec<FailureCase> =\n            vec![FailureCase::always().validation_error(\"Some StrIng\")];\n        let actual = vec![FailureResult::validation_error().with_message(\n            \"a very long string that contains sOmE sTrInG of different capitalization\",\n        )];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n\n        // -- Non-Matching Substring --\n\n        let expectation = vec![FailureCase::always().validation_error(\"Some String\")];\n        let actual = vec![validation_err(\"a very long string that doesn't contain it\")];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Panic\n        );\n    }\n\n    #[test]\n    fn ignore_flaky() {\n        init_logger();\n\n        let expectation = vec![FailureCase::always().validation_error(\"blah\").flaky()];\n        let actual = vec![validation_err(\"some blah\")];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n\n        let expectation = vec![FailureCase::always().validation_error(\"blah\").flaky()];\n        let actual = vec![];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n    }\n\n    #[test]\n    fn matches_multiple_errors() {\n        init_logger();\n\n        // -- matches all matching errors --\n\n        let expectation = vec![FailureCase::always().validation_error(\"blah\")];\n        let actual = vec![\n            validation_err(\"some blah\"),\n            validation_err(\"some other blah\"),\n        ];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n\n        // -- but not all errors --\n\n        let expectation = vec![FailureCase::always().validation_error(\"blah\")];\n        let actual = vec![\n            validation_err(\"some blah\"),\n            validation_err(\"some other blah\"),\n            validation_err(\"something else\"),\n        ];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Panic\n        );\n    }\n\n    #[test]\n    fn multi_reason_error() {\n        init_logger();\n\n        let expectation = vec![FailureCase::default()\n            .validation_error(\"blah\")\n            .panic(\"panik\")];\n        let actual = vec![\n            validation_err(\"my blah blah validation error\"),\n            panic(\"my panik\"),\n        ];\n\n        assert_eq!(\n            super::expectations_match_failures(&expectation, actual),\n            ExpectationMatchResult::Complete\n        );\n    }\n}\n"
  },
  {
    "path": "tests/src/image.rs",
    "content": "//! Image comparison utilities\n\nuse std::{borrow::Cow, ffi::OsStr, path::Path};\n\nuse wgpu::util::{align_to, DeviceExt};\nuse wgpu::*;\n\nuse crate::TestingContext;\n\n#[cfg(not(any(target_arch = \"wasm32\", miri)))]\nasync fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>> {\n    let data = match std::fs::read(&path) {\n        Ok(f) => f,\n        Err(e) => {\n            log::warn!(\n                \"image comparison invalid: file io error when comparing {}: {}\",\n                path.as_ref().display(),\n                e\n            );\n            return None;\n        }\n    };\n    let decoder = png::Decoder::new(std::io::Cursor::new(data));\n    let mut reader = decoder.read_info().ok()?;\n\n    let buffer_len = reader\n        .output_buffer_size()\n        .expect(\"output buffer would not fit in memory\");\n    let mut buffer = vec![0; buffer_len];\n    let info = reader.next_frame(&mut buffer).ok()?;\n    if info.width != width {\n        log::warn!(\"image comparison invalid: size mismatch\");\n        return None;\n    }\n    if info.height != height {\n        log::warn!(\"image comparison invalid: size mismatch\");\n        return None;\n    }\n    if info.color_type != png::ColorType::Rgba {\n        log::warn!(\"image comparison invalid: color type mismatch\");\n        return None;\n    }\n    if info.bit_depth != png::BitDepth::Eight {\n        log::warn!(\"image comparison invalid: bit depth mismatch\");\n        return None;\n    }\n\n    Some(buffer)\n}\n\n#[cfg(not(any(target_arch = \"wasm32\", miri)))]\nasync fn write_png(\n    path: impl AsRef<Path>,\n    width: u32,\n    height: u32,\n    data: &[u8],\n    compression: png::Compression,\n) {\n    let file = std::io::BufWriter::new(std::fs::File::create(path).unwrap());\n\n    let mut encoder = png::Encoder::new(file, width, height);\n    encoder.set_color(png::ColorType::Rgba);\n    encoder.set_depth(png::BitDepth::Eight);\n    encoder.set_compression(compression);\n    let mut writer = encoder.write_header().unwrap();\n\n    writer.write_image_data(data).unwrap();\n}\n\n#[cfg_attr(any(target_arch = \"wasm32\", miri), allow(unused))]\nfn add_alpha(input: &[u8]) -> Vec<u8> {\n    input\n        .chunks_exact(3)\n        .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255])\n        .collect()\n}\n\n#[cfg_attr(any(target_arch = \"wasm32\", miri), allow(unused))]\nfn remove_alpha(input: &[u8]) -> Vec<u8> {\n    input\n        .chunks_exact(4)\n        .flat_map(|chunk| &chunk[0..3])\n        .copied()\n        .collect()\n}\n\n#[cfg(not(any(target_arch = \"wasm32\", miri)))]\nfn print_flip(pool: &mut nv_flip::FlipPool) {\n    println!(\"\\tMean: {:.6}\", pool.mean());\n    println!(\"\\tMin Value: {:.6}\", pool.min_value());\n    for percentile in [25, 50, 75, 95, 99] {\n        println!(\n            \"\\t      {percentile}%: {:.6}\",\n            pool.get_percentile(percentile as f32 / 100.0, true)\n        );\n    }\n    println!(\"\\tMax Value: {:.6}\", pool.max_value());\n}\n\n/// The FLIP library generates a per-pixel error map where 0.0 represents \"no error\"\n/// and 1.0 represents \"maximum error\" between the images. This is then put into\n/// a weighted-histogram, which we query to determine if the errors between\n/// the test and reference image is high enough to count as \"different\".\n///\n/// Error thresholds will be different for every test, but good initial values\n/// to look at are in the [0.01, 0.1] range. The larger the area that might have\n/// inherent variance, the larger this base value is. Using a high percentile comparison\n/// (e.g. 95% or 99%) is good for images that are likely to have a lot of error\n/// in a small area when they fail.\n#[derive(Debug, Clone, Copy)]\npub enum ComparisonType {\n    /// If the mean error is greater than the given value, the test will fail.\n    Mean(f32),\n    /// If the given percentile is greater than the given value, the test will fail.\n    ///\n    /// The percentile is given in the range [0, 1].\n    Percentile { percentile: f32, threshold: f32 },\n}\n\nimpl ComparisonType {\n    #[cfg(not(any(target_arch = \"wasm32\", miri)))]\n    fn check(&self, pool: &mut nv_flip::FlipPool) -> bool {\n        match *self {\n            ComparisonType::Mean(v) => {\n                let mean = pool.mean();\n                let within = mean <= v;\n                println!(\n                    \"\\tExpected Mean ({:.6}) to be under expected maximum ({}): {}\",\n                    mean,\n                    v,\n                    if within { \"PASS\" } else { \"FAIL\" }\n                );\n                within\n            }\n            ComparisonType::Percentile {\n                percentile: p,\n                threshold: v,\n            } => {\n                let percentile = pool.get_percentile(p, true);\n                let within = percentile <= v;\n                println!(\n                    \"\\tExpected {}% ({:.6}) to be under expected maximum ({}): {}\",\n                    p * 100.0,\n                    percentile,\n                    v,\n                    if within { \"PASS\" } else { \"FAIL\" }\n                );\n                within\n            }\n        }\n    }\n}\n\n#[cfg(not(any(target_arch = \"wasm32\", miri)))]\npub async fn compare_image_output(\n    path: impl AsRef<Path> + AsRef<OsStr>,\n    adapter_info: &wgpu::AdapterInfo,\n    width: u32,\n    height: u32,\n    test_with_alpha: &[u8],\n    checks: &[ComparisonType],\n) {\n    use std::{ffi::OsString, str::FromStr};\n\n    let reference_path = Path::new(&path);\n    let reference_with_alpha = read_png(&path, width, height).await;\n\n    let reference = match reference_with_alpha {\n        Some(v) => remove_alpha(&v),\n        None => {\n            write_png(\n                &path,\n                width,\n                height,\n                test_with_alpha,\n                png::Compression::High,\n            )\n            .await;\n            return;\n        }\n    };\n    let test = remove_alpha(test_with_alpha);\n\n    assert_eq!(reference.len(), test.len());\n\n    let file_stem = reference_path.file_stem().unwrap().to_string_lossy();\n    let renderer = format!(\n        \"{}-{}-{}\",\n        adapter_info.backend,\n        sanitize_for_path(&adapter_info.name),\n        sanitize_for_path(&adapter_info.driver)\n    );\n    // Determine the paths to write out the various intermediate files\n    let actual_path = Path::new(&path)\n        .with_file_name(OsString::from_str(&format!(\"{file_stem}-{renderer}-actual.png\")).unwrap());\n    let difference_path = Path::new(&path).with_file_name(\n        OsString::from_str(&format!(\"{file_stem}-{renderer}-difference.png\",)).unwrap(),\n    );\n\n    let mut all_passed;\n    let magma_image_with_alpha;\n    {\n        let reference_flip = nv_flip::FlipImageRgb8::with_data(width, height, &reference);\n        let test_flip = nv_flip::FlipImageRgb8::with_data(width, height, &test);\n\n        let error_map_flip = nv_flip::flip(\n            reference_flip,\n            test_flip,\n            nv_flip::DEFAULT_PIXELS_PER_DEGREE,\n        );\n        let mut pool = nv_flip::FlipPool::from_image(&error_map_flip);\n\n        println!(\n            \"Starting image comparison test with reference image \\\"{}\\\"\",\n            reference_path.display()\n        );\n\n        print_flip(&mut pool);\n\n        // If there are no checks, we want to fail the test.\n        all_passed = !checks.is_empty();\n        // We always iterate all of these, as the call to check prints\n        for check in checks {\n            all_passed &= check.check(&mut pool);\n        }\n\n        // Convert the error values to a false color representation\n        let magma_image = error_map_flip\n            .apply_color_lut(&nv_flip::magma_lut())\n            .to_vec();\n        magma_image_with_alpha = add_alpha(&magma_image);\n    }\n\n    write_png(\n        actual_path,\n        width,\n        height,\n        test_with_alpha,\n        png::Compression::Fast,\n    )\n    .await;\n    write_png(\n        &difference_path,\n        width,\n        height,\n        &magma_image_with_alpha,\n        png::Compression::Fast,\n    )\n    .await;\n\n    if !all_passed {\n        panic!(\"Image data mismatch: {}\", difference_path.display())\n    }\n}\n\n#[cfg(any(target_arch = \"wasm32\", miri))]\npub async fn compare_image_output(\n    path: impl AsRef<Path> + AsRef<OsStr>,\n    adapter_info: &wgpu::AdapterInfo,\n    width: u32,\n    height: u32,\n    test_with_alpha: &[u8],\n    checks: &[ComparisonType],\n) {\n    #[cfg(any(target_arch = \"wasm32\", miri))]\n    {\n        let _ = (path, adapter_info, width, height, test_with_alpha, checks);\n    }\n}\n\n#[cfg_attr(any(target_arch = \"wasm32\", miri), allow(unused))]\nfn sanitize_for_path(s: &str) -> String {\n    s.chars()\n        .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' })\n        .collect()\n}\n\nfn copy_via_compute(\n    device: &Device,\n    encoder: &mut CommandEncoder,\n    texture: &Texture,\n    buffer: &Buffer,\n    aspect: TextureAspect,\n) {\n    let bgl = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[\n            BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::COMPUTE,\n                ty: BindingType::Texture {\n                    sample_type: match aspect {\n                        TextureAspect::DepthOnly => TextureSampleType::Float { filterable: false },\n                        TextureAspect::StencilOnly => TextureSampleType::Uint,\n                        _ => unreachable!(),\n                    },\n                    view_dimension: TextureViewDimension::D2Array,\n                    multisampled: false,\n                },\n                count: None,\n            },\n            BindGroupLayoutEntry {\n                binding: 1,\n                visibility: ShaderStages::COMPUTE,\n                ty: BindingType::Buffer {\n                    ty: BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            },\n        ],\n    });\n\n    let view = texture.create_view(&TextureViewDescriptor {\n        aspect,\n        dimension: Some(TextureViewDimension::D2Array),\n        ..Default::default()\n    });\n\n    let output_buffer = device.create_buffer(&BufferDescriptor {\n        label: Some(\"output buffer\"),\n        size: buffer.size(),\n        usage: BufferUsages::COPY_SRC | BufferUsages::STORAGE,\n        mapped_at_creation: false,\n    });\n\n    let bg = device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::TextureView(&view),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: BindingResource::Buffer(BufferBinding {\n                    buffer: &output_buffer,\n                    offset: 0,\n                    size: None,\n                }),\n            },\n        ],\n    });\n\n    let pll = device.create_pipeline_layout(&PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[Some(&bgl)],\n        immediate_size: 0,\n    });\n\n    let source = String::from(include_str!(\"copy_texture_to_buffer.wgsl\"));\n\n    let processed_source = source.replace(\n        \"{{type}}\",\n        match aspect {\n            TextureAspect::DepthOnly => \"f32\",\n            TextureAspect::StencilOnly => \"u32\",\n            _ => unreachable!(),\n        },\n    );\n\n    let sm = device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"shader copy_texture_to_buffer.wgsl\"),\n        source: ShaderSource::Wgsl(Cow::Borrowed(&processed_source)),\n    });\n\n    let pipeline_copy = device.create_compute_pipeline(&ComputePipelineDescriptor {\n        label: Some(\"pipeline read\"),\n        layout: Some(&pll),\n        module: &sm,\n        entry_point: Some(\"copy_texture_to_buffer\"),\n        compilation_options: Default::default(),\n        cache: None,\n    });\n\n    {\n        let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor::default());\n\n        pass.set_pipeline(&pipeline_copy);\n        pass.set_bind_group(0, &bg, &[]);\n        pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, buffer, 0, buffer.size());\n}\n\nfn copy_texture_to_buffer_with_aspect(\n    encoder: &mut CommandEncoder,\n    texture: &Texture,\n    buffer: &Buffer,\n    buffer_stencil: &Option<Buffer>,\n    aspect: TextureAspect,\n) {\n    let (block_width, block_height) = texture.format().block_dimensions();\n    let block_size = texture.format().block_copy_size(Some(aspect)).unwrap();\n    let bytes_per_row = align_to(\n        (texture.width() / block_width) * block_size,\n        COPY_BYTES_PER_ROW_ALIGNMENT,\n    );\n    let mip_level = 0;\n    encoder.copy_texture_to_buffer(\n        TexelCopyTextureInfo {\n            texture,\n            mip_level,\n            origin: Origin3d::ZERO,\n            aspect,\n        },\n        TexelCopyBufferInfo {\n            buffer: match aspect {\n                TextureAspect::StencilOnly => buffer_stencil.as_ref().unwrap(),\n                _ => buffer,\n            },\n            layout: TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(bytes_per_row),\n                rows_per_image: Some(texture.height() / block_height),\n            },\n        },\n        texture\n            .size()\n            .mip_level_size(mip_level, texture.dimension()),\n    );\n}\n\nfn copy_texture_to_buffer(\n    device: &Device,\n    encoder: &mut CommandEncoder,\n    texture: &Texture,\n    buffer: &Buffer,\n    buffer_stencil: &Option<Buffer>,\n) {\n    match texture.format() {\n        TextureFormat::Depth24Plus => {\n            copy_via_compute(device, encoder, texture, buffer, TextureAspect::DepthOnly);\n        }\n        TextureFormat::Depth24PlusStencil8 => {\n            copy_via_compute(device, encoder, texture, buffer, TextureAspect::DepthOnly);\n            copy_texture_to_buffer_with_aspect(\n                encoder,\n                texture,\n                buffer,\n                buffer_stencil,\n                TextureAspect::StencilOnly,\n            );\n        }\n        TextureFormat::Depth32FloatStencil8 => {\n            copy_texture_to_buffer_with_aspect(\n                encoder,\n                texture,\n                buffer,\n                buffer_stencil,\n                TextureAspect::DepthOnly,\n            );\n            copy_texture_to_buffer_with_aspect(\n                encoder,\n                texture,\n                buffer,\n                buffer_stencil,\n                TextureAspect::StencilOnly,\n            );\n        }\n        _ => {\n            copy_texture_to_buffer_with_aspect(\n                encoder,\n                texture,\n                buffer,\n                buffer_stencil,\n                TextureAspect::All,\n            );\n        }\n    }\n}\n\npub struct ReadbackBuffers {\n    /// texture format\n    texture_format: TextureFormat,\n    /// texture width\n    texture_width: u32,\n    /// texture height\n    texture_height: u32,\n    /// texture depth or array layer count\n    texture_depth_or_array_layers: u32,\n    /// buffer for color or depth aspects\n    buffer: Buffer,\n    /// buffer for stencil aspect\n    buffer_stencil: Option<Buffer>,\n}\n\nimpl ReadbackBuffers {\n    pub fn new(device: &Device, texture: &Texture) -> Self {\n        let (block_width, block_height) = texture.format().block_dimensions();\n        const SKIP_ALIGNMENT_FORMATS: [TextureFormat; 2] = [\n            TextureFormat::Depth24Plus,\n            TextureFormat::Depth24PlusStencil8,\n        ];\n        let should_align_buffer_size = !SKIP_ALIGNMENT_FORMATS.contains(&texture.format());\n        if texture.format().is_combined_depth_stencil_format() {\n            let mut buffer_depth_bytes_per_row = (texture.width() / block_width)\n                * texture\n                    .format()\n                    .block_copy_size(Some(TextureAspect::DepthOnly))\n                    .unwrap_or(4);\n            if should_align_buffer_size {\n                buffer_depth_bytes_per_row =\n                    align_to(buffer_depth_bytes_per_row, COPY_BYTES_PER_ROW_ALIGNMENT);\n            }\n            let buffer_size = buffer_depth_bytes_per_row\n                * (texture.height() / block_height)\n                * texture.depth_or_array_layers();\n\n            let buffer_stencil_bytes_per_row = align_to(\n                (texture.width() / block_width)\n                    * texture\n                        .format()\n                        .block_copy_size(Some(TextureAspect::StencilOnly))\n                        .unwrap_or(4),\n                COPY_BYTES_PER_ROW_ALIGNMENT,\n            );\n            let buffer_stencil_size = buffer_stencil_bytes_per_row\n                * (texture.height() / block_height)\n                * texture.depth_or_array_layers();\n\n            let buffer = device.create_buffer_init(&util::BufferInitDescriptor {\n                label: Some(\"Texture Readback\"),\n                usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n                contents: &vec![255; buffer_size as usize],\n            });\n            let buffer_stencil = device.create_buffer_init(&util::BufferInitDescriptor {\n                label: Some(\"Texture Stencil-Aspect Readback\"),\n                usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n                contents: &vec![255; buffer_stencil_size as usize],\n            });\n            ReadbackBuffers {\n                texture_format: texture.format(),\n                texture_width: texture.width(),\n                texture_height: texture.height(),\n                texture_depth_or_array_layers: texture.depth_or_array_layers(),\n                buffer,\n                buffer_stencil: Some(buffer_stencil),\n            }\n        } else {\n            let mut bytes_per_row = (texture.width() / block_width)\n                * texture.format().block_copy_size(None).unwrap_or(4);\n            if should_align_buffer_size {\n                bytes_per_row = align_to(bytes_per_row, COPY_BYTES_PER_ROW_ALIGNMENT);\n            }\n            let buffer_size =\n                bytes_per_row * (texture.height() / block_height) * texture.depth_or_array_layers();\n            let buffer = device.create_buffer_init(&util::BufferInitDescriptor {\n                label: Some(\"Texture Readback\"),\n                usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n                contents: &vec![255; buffer_size as usize],\n            });\n            ReadbackBuffers {\n                texture_format: texture.format(),\n                texture_width: texture.width(),\n                texture_height: texture.height(),\n                texture_depth_or_array_layers: texture.depth_or_array_layers(),\n                buffer,\n                buffer_stencil: None,\n            }\n        }\n    }\n\n    // TODO: also copy and check mips\n    pub fn copy_from(&self, device: &Device, encoder: &mut CommandEncoder, texture: &Texture) {\n        copy_texture_to_buffer(device, encoder, texture, &self.buffer, &self.buffer_stencil);\n    }\n\n    async fn retrieve_buffer(\n        &self,\n        ctx: &TestingContext,\n        buffer: &Buffer,\n        aspect: Option<TextureAspect>,\n    ) -> Vec<u8> {\n        let buffer_slice = buffer.slice(..);\n        buffer_slice.map_async(MapMode::Read, |_| ());\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n        let (block_width, block_height) = self.texture_format.block_dimensions();\n        let expected_bytes_per_row = (self.texture_width / block_width)\n            * self.texture_format.block_copy_size(aspect).unwrap_or(4);\n        let expected_buffer_size = expected_bytes_per_row\n            * (self.texture_height / block_height)\n            * self.texture_depth_or_array_layers;\n        let data: BufferView = buffer_slice.get_mapped_range();\n        if expected_buffer_size as usize == data.len() {\n            data.to_vec()\n        } else {\n            bytemuck::cast_slice(&data)\n                .chunks_exact(\n                    align_to(expected_bytes_per_row, COPY_BYTES_PER_ROW_ALIGNMENT) as usize,\n                )\n                .flat_map(|x| x.iter().take(expected_bytes_per_row as usize))\n                .copied()\n                .collect()\n        }\n    }\n\n    fn buffer_aspect(&self) -> Option<TextureAspect> {\n        if self.texture_format.is_combined_depth_stencil_format() {\n            Some(TextureAspect::DepthOnly)\n        } else {\n            None\n        }\n    }\n\n    async fn is_zero(\n        &self,\n        ctx: &TestingContext,\n        buffer: &Buffer,\n        aspect: Option<TextureAspect>,\n    ) -> bool {\n        let is_zero = self\n            .retrieve_buffer(ctx, buffer, aspect)\n            .await\n            .iter()\n            .all(|b| *b == 0);\n        buffer.unmap();\n        is_zero\n    }\n\n    pub async fn are_zero(&self, ctx: &TestingContext) -> bool {\n        let buffer_zero = self.is_zero(ctx, &self.buffer, self.buffer_aspect()).await;\n        let mut stencil_buffer_zero = true;\n        if let Some(buffer) = &self.buffer_stencil {\n            stencil_buffer_zero = self\n                .is_zero(ctx, buffer, Some(TextureAspect::StencilOnly))\n                .await;\n        };\n        buffer_zero && stencil_buffer_zero\n    }\n\n    pub async fn assert_buffer_contents(&self, ctx: &TestingContext, expected_data: &[u8]) {\n        let result_buffer = self\n            .retrieve_buffer(ctx, &self.buffer, self.buffer_aspect())\n            .await;\n        assert!(\n            result_buffer.len() >= expected_data.len(),\n            \"Result buffer ({}) smaller than expected buffer ({})\",\n            result_buffer.len(),\n            expected_data.len()\n        );\n        let result_buffer = &result_buffer[..expected_data.len()];\n        assert_eq!(result_buffer, expected_data);\n        self.buffer.unmap();\n    }\n}\n"
  },
  {
    "path": "tests/src/init.rs",
    "content": "use wgpu::{Adapter, Backends, Device, Features, Instance, Limits, Queue};\n\nuse crate::{report::AdapterReport, TestParameters};\n\n/// Initialize the logger for the test runner.\npub fn init_logger() {\n    // We don't actually care if it fails\n    #[cfg(not(target_arch = \"wasm32\"))]\n    let _ = env_logger::try_init();\n    #[cfg(target_arch = \"wasm32\")]\n    let _ = console_log::init_with_level(log::Level::Info);\n}\n\n/// Initialize a wgpu instance with the options from the environment.\npub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> Instance {\n    // We ignore `WGPU_BACKEND` for now, merely using test filtering to only run a single backend's tests.\n    //\n    // We can potentially work support back into the test runner in the future, but as the adapters are matched up\n    // based on adapter index, removing some backends messes up the indexes in annoying ways.\n    //\n    // WORKAROUND for https://github.com/rust-lang/cargo/issues/7160:\n    // `--no-default-features` is not passed through correctly to the test runner.\n    // We use it whenever we want to explicitly run with webgl instead of webgpu.\n    // To \"disable\" webgpu regardless, we do this by removing the webgpu backend whenever we see\n    // the webgl feature.\n    let backends = if cfg!(feature = \"webgl\") {\n        backends - wgpu::Backends::BROWSER_WEBGPU\n    } else {\n        backends\n    };\n    // Some tests need to be able to force demote to FXC, to specifically test workarounds for FXC\n    // behavior.\n    let dx12_shader_compiler = if params.force_fxc {\n        wgpu::Dx12Compiler::Fxc\n    } else {\n        wgpu::Dx12Compiler::from_env().unwrap_or(wgpu::Dx12Compiler::StaticDxc)\n    };\n    // The defaults for debugging, overridden by the environment, overridden by the test parameters.\n    let flags = wgpu::InstanceFlags::debugging()\n        .with_env()\n        .union(params.required_instance_flags);\n\n    Instance::new(wgpu::InstanceDescriptor {\n        backends,\n        flags,\n        memory_budget_thresholds: wgpu::MemoryBudgetThresholds {\n            for_resource_creation: Some(99),\n            for_device_loss: None,\n        },\n        backend_options: wgpu::BackendOptions {\n            dx12: wgpu::Dx12BackendOptions {\n                shader_compiler: dx12_shader_compiler,\n                ..Default::default()\n            },\n            gl: wgpu::GlBackendOptions {\n                fence_behavior: if cfg!(target_family = \"wasm\") {\n                    // On WebGL, you cannot call Poll(Wait) with any timeout. This is because the\n                    // browser does not things to block. However all of our tests are written to\n                    // expect this behavior. This is the workaround to allow this to work.\n                    //\n                    // However on native you can wait, so we want to ensure that behavior as well.\n                    wgpu::GlFenceBehavior::AutoFinish\n                } else {\n                    wgpu::GlFenceBehavior::Normal\n                },\n                ..Default::default()\n            },\n            // Allow the noop backend to be used in tests. This will not be used unless\n            // WGPU_GPU_TESTS_USE_NOOP_BACKEND env var is set, because wgpu-info will not\n            // enumerate the noop backend.\n            //\n            // However, we use wasm_bindgen_test to run tests on wasm, and wgpu\n            // will chose the noop on wasm32 for some reason.\n            noop: wgpu::NoopBackendOptions {\n                enable: !cfg!(target_arch = \"wasm32\"),\n            },\n        }\n        .with_env(),\n        #[cfg(not(all(\n            target_arch = \"wasm32\",\n            any(target_os = \"emscripten\", feature = \"webgl\")\n        )))]\n        display: None,\n        // Wasm requires a canvas surface below, and create_surface() requires\n        // the `display` to be set even if it's \"empty\" on Web:\n        #[cfg(all(\n            target_arch = \"wasm32\",\n            any(target_os = \"emscripten\", feature = \"webgl\")\n        ))]\n        display: Some(Box::new(WebDisplayHandle)),\n    })\n}\n\n/// Initialize a wgpu adapter, using the given adapter report to match the adapter.\npub async fn initialize_adapter(\n    adapter_report: Option<&AdapterReport>,\n    params: &TestParameters,\n) -> (Instance, Adapter, Option<SurfaceGuard>) {\n    let backends = adapter_report\n        .map(|report| Backends::from(report.info.backend))\n        .unwrap_or_default();\n\n    let instance = initialize_instance(backends, params);\n    #[allow(unused_variables)]\n    let surface: Option<wgpu::Surface>;\n    let surface_guard: Option<SurfaceGuard>;\n\n    #[allow(unused_assignments)]\n    // Create a canvas if we need a WebGL2RenderingContext to have a working device.\n    #[cfg(not(all(\n        target_arch = \"wasm32\",\n        any(target_os = \"emscripten\", feature = \"webgl\")\n    )))]\n    {\n        surface = None;\n        surface_guard = None;\n    }\n    #[cfg(all(\n        target_arch = \"wasm32\",\n        any(target_os = \"emscripten\", feature = \"webgl\")\n    ))]\n    {\n        // On wasm, append a canvas to the document body for initializing the adapter\n        let canvas = initialize_html_canvas();\n\n        surface = Some(\n            instance\n                .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))\n                .expect(\"could not create surface from canvas\"),\n        );\n\n        surface_guard = Some(SurfaceGuard { canvas });\n    }\n\n    cfg_if::cfg_if! {\n        if #[cfg(not(target_arch = \"wasm32\"))] {\n            let adapter_iter = instance.enumerate_adapters(backends).await;\n            let adapter = adapter_iter.into_iter()\n                // If we have a report, we only want to match the adapter with the same info.\n                //\n                // If we don't have a report, we just take the first adapter.\n                .find(|adapter| if let Some(adapter_report) = adapter_report {\n                    adapter.get_info() == adapter_report.info\n                } else {\n                    true\n                });\n            let Some(adapter) = adapter else {\n                panic!(\n                    \"Could not find adapter with info {:#?} in {:#?}\",\n                    adapter_report.map(|r| &r.info),\n                    instance.enumerate_adapters(backends).await.into_iter().map(|a| a.get_info()).collect::<Vec<_>>(),\n                );\n            };\n        } else {\n            let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {\n                compatible_surface: surface.as_ref(),\n                ..Default::default()\n            }).await.unwrap();\n        }\n    }\n\n    log::info!(\"Testing using adapter: {:#?}\", adapter.get_info());\n\n    (instance, adapter, surface_guard)\n}\n\n/// Initialize a wgpu device from a given adapter.\npub async fn initialize_device(\n    adapter: &Adapter,\n    features: Features,\n    limits: Limits,\n) -> (Device, Queue) {\n    let bundle = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            label: None,\n            required_features: features,\n            required_limits: limits,\n            experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },\n            memory_hints: wgpu::MemoryHints::MemoryUsage,\n            trace: wgpu::Trace::Off,\n        })\n        .await;\n\n    match bundle {\n        Ok(b) => b,\n        Err(e) => panic!(\"Failed to initialize device: {e}\"),\n    }\n}\n\n/// Create a canvas for testing.\n#[cfg(target_arch = \"wasm32\")]\npub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement {\n    use wasm_bindgen::JsCast;\n\n    web_sys::window()\n        .and_then(|win| win.document())\n        .and_then(|doc| {\n            let canvas = doc.create_element(\"Canvas\").unwrap();\n            canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()\n        })\n        .expect(\"couldn't create canvas\")\n}\n\npub struct SurfaceGuard {\n    #[cfg(target_arch = \"wasm32\")]\n    #[allow(unused)]\n    canvas: web_sys::HtmlCanvasElement,\n}\n\nimpl SurfaceGuard {\n    #[cfg(all(\n        target_arch = \"wasm32\",\n        any(target_os = \"emscripten\", feature = \"webgl\")\n    ))]\n    pub(crate) fn check_for_unreported_errors(&self) -> bool {\n        use wasm_bindgen::JsCast;\n\n        self.canvas\n            .get_context(\"webgl2\")\n            .unwrap()\n            .unwrap()\n            .dyn_into::<web_sys::WebGl2RenderingContext>()\n            .unwrap()\n            .get_error()\n            != web_sys::WebGl2RenderingContext::NO_ERROR\n    }\n}\n\n/// [`raw_window_handle::HasDisplayHandle`] implementation for Web that's [`Send`]+[`Sync`]\n/// because it doesn't own any pointers\n#[cfg(all(\n    target_arch = \"wasm32\",\n    any(target_os = \"emscripten\", feature = \"webgl\")\n))]\n#[derive(Debug)]\nstruct WebDisplayHandle;\n\n#[cfg(all(\n    target_arch = \"wasm32\",\n    any(target_os = \"emscripten\", feature = \"webgl\")\n))]\nimpl raw_window_handle::HasDisplayHandle for WebDisplayHandle {\n    fn display_handle(\n        &self,\n    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {\n        Ok(raw_window_handle::DisplayHandle::web())\n    }\n}\n"
  },
  {
    "path": "tests/src/isolation.rs",
    "content": "use std::sync::atomic::{AtomicBool, Ordering};\n\n/// True if a test is in progress somewhere in the process, false otherwise.\nstatic TEST_ACTIVE_IN_PROCESS: AtomicBool = AtomicBool::new(false);\n\nconst OTHER_TEST_IN_PROGRESS_ERROR: &str = \"TEST ISOLATION ERROR:\n\nwgpu's test harness requires that no more than one test is running per process.\n\nThe best way to facilitate this is by using cargo-nextest which runs each test in its own process\nand has a very good testing UI:\n\ncargo install cargo-nextest\ncargo nextest run\n\nAlternatively, you can run tests in single threaded mode (much slower).\n\ncargo test -- --test-threads=1\n\nCalling std::process::abort()...\n\";\n\n/// When this guard is active, enforces that there is only a single test running in the process\n/// at any one time. If there are multiple processes, creating the guard hard terminates the process.\npub struct OneTestPerProcessGuard(());\n\nimpl OneTestPerProcessGuard {\n    pub fn new() -> Self {\n        let other_tests_in_flight = TEST_ACTIVE_IN_PROCESS.swap(true, Ordering::SeqCst);\n\n        // We never abort if we're on wasm. Wasm tests are inherently single threaded, and panics cannot\n        // unwind the stack and trigger all the guards, so we don't actually need to check.\n        if other_tests_in_flight && !cfg!(target_arch = \"wasm32\") {\n            log::error!(\"{OTHER_TEST_IN_PROGRESS_ERROR}\");\n            // Hard exit to call attention to the error\n            std::process::abort();\n        }\n        OneTestPerProcessGuard(())\n    }\n}\n\nimpl Drop for OneTestPerProcessGuard {\n    fn drop(&mut self) {\n        TEST_ACTIVE_IN_PROCESS.store(false, Ordering::SeqCst);\n    }\n}\n"
  },
  {
    "path": "tests/src/lib.rs",
    "content": "//! Test utilities for the wgpu repository.\n\n#![allow(clippy::arc_with_non_send_sync)] // False positive on wasm\n\nmod config;\nmod expectations;\npub mod image;\nmod init;\nmod isolation;\npub mod native;\nmod params;\nmod poll;\nmod report;\nmod run;\n\n#[cfg(target_arch = \"wasm32\")]\npub use init::initialize_html_canvas;\n\npub use self::image::ComparisonType;\npub use config::{GpuTestConfiguration, GpuTestInitializer};\n#[doc(hidden)]\npub use ctor;\npub use expectations::{FailureApplicationReasons, FailureBehavior, FailureCase, FailureReason};\npub use init::{initialize_adapter, initialize_device, initialize_instance};\npub use params::TestParameters;\npub use run::{execute_test, TestingContext};\npub use wgpu_macros::gpu_test;\n\n/// Run some code in an error scope and assert that validation fails.\n///\n/// Note that errors related to commands for the GPU (i.e. raised by methods on\n/// GPUCommandEncoder, GPURenderPassEncoder, GPUComputePassEncoder,\n/// GPURenderBundleEncoder) are usually not raised immediately. They are raised\n/// only when `finish()` is called on the command encoder. Tests of such error\n/// cases should call `fail` with a closure that calls `finish()`, not with a\n/// closure that encodes the actual command.\npub fn fail<T>(\n    device: &wgpu::Device,\n    callback: impl FnOnce() -> T,\n    expected_msg_substring: Option<&str>,\n) -> T {\n    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    let result = callback();\n    let validation_error = pollster::block_on(scope.pop())\n        .expect(\"expected validation error in callback, but no validation error was emitted\");\n    if let Some(expected_msg_substring) = expected_msg_substring {\n        let lowered_expected = expected_msg_substring.to_lowercase();\n        let lowered_actual = validation_error.to_string().to_lowercase();\n        assert!(\n            lowered_actual.contains(&lowered_expected),\n            concat!(\n                \"expected validation error case-insensitively containing {}, \",\n                \"but it was not present in actual error message:\\n{}\"\n            ),\n            expected_msg_substring,\n            validation_error\n        );\n    }\n\n    result\n}\n\n/// Run some code in an error scope and assert that validation succeeds.\n#[track_caller]\npub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {\n    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    let result = callback();\n    if let Some(error) = pollster::block_on(scope.pop()) {\n        panic!(\n            \"`valid` block at {} encountered wgpu error:\\n{error}\",\n            std::panic::Location::caller()\n        );\n    }\n\n    result\n}\n\n/// Run some code in an error scope and assert that validation succeeds or fails depending on the\n/// provided `should_fail` boolean.\npub fn fail_if<T>(\n    device: &wgpu::Device,\n    should_fail: bool,\n    callback: impl FnOnce() -> T,\n    expected_msg_substring: Option<&'static str>,\n) -> T {\n    if should_fail {\n        fail(device, callback, expected_msg_substring)\n    } else {\n        valid(device, callback)\n    }\n}\n\nfn did_fill_error_scope<T>(\n    device: &wgpu::Device,\n    callback: impl FnOnce() -> T,\n    filter: wgpu::ErrorFilter,\n) -> (bool, T) {\n    let scope = device.push_error_scope(filter);\n    let result = callback();\n    let validation_error = pollster::block_on(scope.pop());\n    let failed = validation_error.is_some();\n\n    (failed, result)\n}\n\n/// Returns true if the provided callback fails validation.\npub fn did_fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> (bool, T) {\n    did_fill_error_scope(device, callback, wgpu::ErrorFilter::Validation)\n}\n\n/// Returns true if the provided callback encounters an out-of-memory error.\npub fn did_oom<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> (bool, T) {\n    did_fill_error_scope(device, callback, wgpu::ErrorFilter::OutOfMemory)\n}\n\n/// Adds the necessary main function for our gpu test harness.\n///\n/// Takes a single argument which is an expression that evaluates to `Vec<wgpu_test::GpuTestInitializer>`.\n#[macro_export]\nmacro_rules! gpu_test_main {\n    ($tests: expr) => {\n        #[cfg(target_arch = \"wasm32\")]\n        wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);\n        #[cfg(target_arch = \"wasm32\")]\n        fn main() {\n            // Ensure that value is used so that warnings don't happen.\n            let _ = $tests;\n        }\n\n        #[cfg(not(target_arch = \"wasm32\"))]\n        fn main() -> $crate::native::MainResult {\n            $crate::native::main($tests)\n        }\n    };\n}\n"
  },
  {
    "path": "tests/src/native.rs",
    "content": "#![cfg(not(target_arch = \"wasm32\"))]\n//! Infrastructure for the native, `cargo-nextest` based harness.\n//!\n//! This is largly used by [`gpu_test_main`](crate::gpu_test_main) and [`gpu_test`](crate::gpu_test).\n\nuse std::{future::Future, pin::Pin};\n\nuse parking_lot::Mutex;\n\nuse crate::{\n    config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test,\n    GpuTestInitializer,\n};\n\ntype NativeTestFuture = Pin<Box<dyn Future<Output = ()> + Send>>;\n\nstruct NativeTest {\n    name: String,\n    future: NativeTestFuture,\n}\n\nimpl NativeTest {\n    /// Adapter index is only used for naming the test, the adapters are matched based on the adapter info.\n    fn from_configuration(\n        config: GpuTestConfiguration,\n        adapter_report: AdapterReport,\n        adapter_index: usize,\n    ) -> Self {\n        let device_name = &adapter_report.info.name;\n        let backend = adapter_report.info.backend;\n        let driver = &adapter_report.info.driver;\n        let report_driver_as_backend = [\n            (wgpu::Backend::Vulkan, \"MoltenVK\"),\n            (wgpu::Backend::Vulkan, \"KosmicKrisp\"),\n        ];\n        let backend_str = if report_driver_as_backend.contains(&(backend, driver.as_ref())) {\n            driver.clone()\n        } else {\n            format!(\"{backend:?}\")\n        };\n\n        let test_info = TestInfo::from_configuration(&config, &adapter_report);\n\n        let full_name = format!(\n            \"[{running_msg}] [{backend_str}/{device_name}/{adapter_index}] {base_name}\",\n            running_msg = test_info.running_msg,\n            base_name = config.name,\n        );\n        Self {\n            name: full_name,\n            future: Box::pin(async move {\n                // Enable metal validation layers if we're running on metal.\n                //\n                // This is a process-wide setting as it's via environment variable, but all\n                // tests are run in separate processes.\n                //\n                // We don't do this in the instance initializer as we don't want to enable\n                // validation layers for the entire process, or other instances.\n                //\n                // We do not enable metal validation when running on moltenvk.\n                let metal_validation = backend == wgpu::Backend::Metal;\n\n                let env_value = if metal_validation { \"1\" } else { \"0\" };\n                std::env::set_var(\"MTL_DEBUG_LAYER\", env_value);\n                if std::env::var(\"GITHUB_ACTIONS\").as_deref() != Ok(\"true\") {\n                    // Metal Shader Validation is entirely broken in the paravirtualized CI environment.\n                    std::env::set_var(\"MTL_SHADER_VALIDATION\", env_value);\n                }\n\n                execute_test(Some(&adapter_report), config, Some(test_info)).await;\n            }),\n        }\n    }\n\n    pub fn into_trial(self) -> libtest_mimic::Trial {\n        libtest_mimic::Trial::test(self.name, || {\n            pollster::block_on(self.future);\n            Ok(())\n        })\n    }\n}\n\n#[doc(hidden)]\npub static TEST_LIST: Mutex<Vec<crate::GpuTestConfiguration>> = Mutex::new(Vec::new());\n\n/// Return value for the main function.\npub type MainResult = anyhow::Result<()>;\n\n/// Main function that runs every gpu function once for every adapter on the system.\npub fn main(tests: Vec<GpuTestInitializer>) -> MainResult {\n    use anyhow::Context;\n\n    use crate::report::GpuReport;\n\n    // If this environment variable is set, we will only enumerate the noop backend. The\n    // main use case is running tests with miri, where we can't even enumerate adapters,\n    // as we cannot load DLLs or make any external calls.\n    let use_noop = std::env::var(\"WGPU_GPU_TESTS_USE_NOOP_BACKEND\").as_deref() == Ok(\"1\");\n\n    let report = if use_noop {\n        GpuReport::noop_only()\n    } else {\n        let config_text = {\n            profiling::scope!(\"Reading .gpuconfig\");\n            &std::fs::read_to_string(format!(\"{}/../.gpuconfig\", env!(\"CARGO_MANIFEST_DIR\")))\n                .context(\n                    \"Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?\",\n                )?\n        };\n        let mut report =\n            GpuReport::from_json(config_text).context(\"Could not parse .gpuconfig JSON\")?;\n\n        // Filter out the adapters that are not part of WGPU_BACKEND.\n        let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default();\n        report\n            .devices\n            .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend)));\n\n        report\n    };\n\n    // Iterate through all the tests. Creating a test per adapter.\n    execute_native(tests.into_iter().flat_map(|initializer| {\n        let test = initializer();\n        report\n            .devices\n            .iter()\n            .enumerate()\n            .map(move |(adapter_index, adapter_report)| {\n                NativeTest::from_configuration(test.clone(), adapter_report.clone(), adapter_index)\n            })\n    }));\n\n    Ok(())\n}\n\nfn execute_native(tests: impl IntoIterator<Item = NativeTest>) {\n    let args = libtest_mimic::Arguments::from_args();\n    let trials = {\n        profiling::scope!(\"collecting tests\");\n        tests.into_iter().map(NativeTest::into_trial).collect()\n    };\n\n    libtest_mimic::run(&args, trials).exit_if_failed();\n}\n"
  },
  {
    "path": "tests/src/params.rs",
    "content": "use arrayvec::ArrayVec;\nuse wgpu::{DownlevelCapabilities, DownlevelFlags, Features, InstanceFlags, Limits};\n\nuse crate::{\n    report::AdapterReport, FailureApplicationReasons, FailureBehavior, FailureCase,\n    GpuTestConfiguration,\n};\n\nconst LOWEST_DOWNLEVEL_PROPERTIES: wgpu::DownlevelCapabilities = DownlevelCapabilities {\n    flags: wgpu::DownlevelFlags::empty(),\n    limits: wgpu::DownlevelLimits {},\n    shader_model: wgpu::ShaderModel::Sm2,\n};\n\n/// This information determines if a test should run.\n#[derive(Clone)]\npub struct TestParameters {\n    pub required_features: Features,\n    pub required_downlevel_caps: DownlevelCapabilities,\n    pub required_limits: Limits,\n\n    pub required_instance_flags: InstanceFlags,\n\n    /// On Dx12, specifically test against the Fxc compiler.\n    ///\n    /// For testing workarounds to Fxc bugs.\n    pub force_fxc: bool,\n\n    /// Conditions under which this test should be skipped.\n    pub skips: Vec<FailureCase>,\n\n    /// Conditions under which this test should be run, but is expected to fail.\n    pub failures: Vec<FailureCase>,\n}\n\nimpl Default for TestParameters {\n    fn default() -> Self {\n        Self {\n            required_features: Features::empty(),\n            required_downlevel_caps: LOWEST_DOWNLEVEL_PROPERTIES,\n            required_limits: Limits::downlevel_webgl2_defaults(),\n            required_instance_flags: InstanceFlags::empty(),\n            force_fxc: false,\n            // By default we skip the noop backend, and enable it if the test\n            // parameters ask us to remove it.\n            skips: vec![FailureCase::backend(wgpu::Backends::NOOP)],\n            failures: Vec::new(),\n        }\n    }\n}\n\n// Builder pattern to make it easier\nimpl TestParameters {\n    /// Set of common features that most internal tests require for compute and readback.\n    pub fn test_features_limits(self) -> Self {\n        self.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults())\n    }\n\n    /// Set the list of features this test requires.\n    pub fn features(mut self, features: Features) -> Self {\n        self.required_features |= features;\n        self\n    }\n\n    pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self {\n        self.required_downlevel_caps.flags |= downlevel_flags;\n        self\n    }\n\n    /// Set the limits needed for the test.\n    pub fn limits(mut self, limits: Limits) -> Self {\n        self.required_limits = limits;\n        self\n    }\n\n    /// Sets the instance flags that the test requires.\n    pub fn instance_flags(mut self, instance_flags: InstanceFlags) -> Self {\n        self.required_instance_flags |= instance_flags;\n        self\n    }\n\n    pub fn force_fxc(mut self, force_fxc: bool) -> Self {\n        self.force_fxc = force_fxc;\n        self\n    }\n\n    /// Mark the test as always failing, but not to be skipped.\n    pub fn expect_fail(mut self, when: FailureCase) -> Self {\n        self.failures.push(when);\n        self\n    }\n\n    /// Mark the test as always failing, and needing to be skipped.\n    pub fn skip(mut self, when: FailureCase) -> Self {\n        self.skips.push(when);\n        self\n    }\n\n    /// Enable testing against the noop backend and miri.\n    ///\n    /// The noop backend does not execute any operations, but allows us to test\n    /// validation and memory safety.\n    pub fn enable_noop(mut self) -> Self {\n        self.skips\n            .retain(|case| *case != FailureCase::backend(wgpu::Backends::NOOP));\n        self\n    }\n}\n\n/// Information about a test, including if if it should be skipped.\npub struct TestInfo {\n    pub skip: bool,\n    pub failure_application_reasons: FailureApplicationReasons,\n    pub failures: Vec<FailureCase>,\n    pub running_msg: String,\n}\n\nimpl TestInfo {\n    pub(crate) fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self {\n        // Figure out if a test is unsupported, and why.\n        let mut unsupported_reasons: ArrayVec<_, 4> = ArrayVec::new();\n        let missing_features = test.params.required_features - adapter.features;\n        if !missing_features.is_empty() {\n            unsupported_reasons.push(\"Features\");\n        }\n\n        if !test.params.required_limits.check_limits(&adapter.limits) {\n            unsupported_reasons.push(\"Limits\");\n        }\n\n        let missing_downlevel_flags =\n            test.params.required_downlevel_caps.flags - adapter.downlevel_caps.flags;\n        if !missing_downlevel_flags.is_empty() {\n            unsupported_reasons.push(\"Downlevel Flags\");\n        }\n\n        if test.params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model {\n            unsupported_reasons.push(\"Shader Model\");\n        }\n\n        // Produce a lower-case version of the adapter info, for comparison against\n        // `parameters.skips` and `parameters.failures`.\n        let adapter_lowercase_info = wgpu::AdapterInfo {\n            name: adapter.info.name.to_lowercase(),\n            driver: adapter.info.driver.to_lowercase(),\n            ..adapter.info.clone()\n        };\n\n        // Check if we should skip the test altogether.\n        let skip_application_reason = test\n            .params\n            .skips\n            .iter()\n            .find_map(|case| case.applies_to_adapter(&adapter_lowercase_info));\n\n        let mut applicable_cases = Vec::with_capacity(test.params.failures.len());\n        let mut failure_application_reasons = FailureApplicationReasons::empty();\n        let mut flaky = false;\n        for failure in &test.params.failures {\n            if let Some(reasons) = failure.applies_to_adapter(&adapter_lowercase_info) {\n                failure_application_reasons.insert(reasons);\n                applicable_cases.push(failure.clone());\n                flaky |= matches!(failure.behavior, FailureBehavior::Ignore);\n            }\n        }\n\n        let mut skip = false;\n        let running_msg = if let Some(reasons) = skip_application_reason {\n            skip = true;\n\n            let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect();\n            let names_text = names.join(\" | \");\n\n            format!(\"Skipped Failure: {names_text}\")\n        } else if !unsupported_reasons.is_empty() {\n            skip = true;\n            format!(\"Unsupported: {}\", unsupported_reasons.join(\" | \"))\n        } else if !failure_application_reasons.is_empty() {\n            if cfg!(target_arch = \"wasm32\") {\n                skip = true;\n            }\n\n            let names: ArrayVec<_, 4> = failure_application_reasons\n                .iter_names()\n                .map(|(name, _)| name)\n                .collect();\n            let names_text = names.join(\" & \");\n            let flaky_text = if flaky { \" Flaky \" } else { \" \" };\n\n            format!(\"Executed{flaky_text}Failure: {names_text}\")\n        } else {\n            String::from(\"Executed\")\n        };\n\n        Self {\n            skip,\n            failure_application_reasons,\n            failures: applicable_cases,\n            running_msg,\n        }\n    }\n}\n"
  },
  {
    "path": "tests/src/poll.rs",
    "content": "use crate::TestingContext;\n\nimpl TestingContext {\n    /// Utility to allow future asynchronous polling.\n    pub async fn async_poll(\n        &self,\n        poll_type: wgpu::PollType,\n    ) -> Result<wgpu::PollStatus, wgpu::PollError> {\n        self.device.poll(poll_type)\n    }\n}\n"
  },
  {
    "path": "tests/src/report.rs",
    "content": "use std::collections::HashMap;\n\nuse serde::Deserialize;\nuse wgpu::{\n    AdapterInfo, DownlevelCapabilities, Features, Limits, TextureFormat, TextureFormatFeatures,\n};\n\n/// Report specifying the capabilities of the GPUs on the system.\n///\n/// Must be synchronized with the definition on wgpu-info/src/report.rs.\n#[derive(Deserialize)]\npub(crate) struct GpuReport {\n    #[cfg_attr(target_arch = \"wasm32\", allow(unused))]\n    pub devices: Vec<AdapterReport>,\n}\n\nimpl GpuReport {\n    #[cfg(not(target_arch = \"wasm32\"))]\n    /// Creates a new GpuReport with a single noop adapter.\n    pub(crate) fn noop_only() -> Self {\n        GpuReport {\n            devices: vec![AdapterReport {\n                info: wgpu::hal::noop::adapter_info(),\n                features: Features::all(),\n                limits: wgpu::hal::noop::CAPABILITIES.limits,\n                downlevel_caps: wgpu::hal::noop::CAPABILITIES.downlevel,\n                texture_format_features: HashMap::new(), // todo\n            }],\n        }\n    }\n\n    #[cfg_attr(target_arch = \"wasm32\", allow(unused))]\n    pub(crate) fn from_json(file: &str) -> serde_json::Result<Self> {\n        profiling::scope!(\"Parsing .gpuconfig\");\n        serde_json::from_str(file)\n    }\n}\n\n/// A single report of the capabilities of an Adapter.\n///\n/// Must be synchronized with the definition on wgpu-info/src/report.rs.\n#[derive(Deserialize, Clone)]\npub struct AdapterReport {\n    pub info: AdapterInfo,\n    pub features: Features,\n    pub limits: Limits,\n    pub downlevel_caps: DownlevelCapabilities,\n    pub texture_format_features: HashMap<TextureFormat, TextureFormatFeatures>,\n}\n\nimpl AdapterReport {\n    pub(crate) fn from_adapter(adapter: &wgpu::Adapter) -> Self {\n        let info = adapter.get_info();\n        let features = adapter.features();\n        let limits = adapter.limits();\n        let downlevel_caps = adapter.get_downlevel_capabilities();\n\n        Self {\n            info,\n            features,\n            limits,\n            downlevel_caps,\n            texture_format_features: HashMap::new(), // todo\n        }\n    }\n}\n"
  },
  {
    "path": "tests/src/run.rs",
    "content": "use std::panic::AssertUnwindSafe;\n\nuse futures_lite::FutureExt;\nuse wgpu::{Adapter, Device, Instance, Queue};\n\nuse crate::{\n    expectations::{expectations_match_failures, ExpectationMatchResult, FailureResult},\n    init::{init_logger, initialize_adapter, initialize_device},\n    isolation,\n    params::TestInfo,\n    report::AdapterReport,\n    GpuTestConfiguration,\n};\n\n#[derive(Hash)]\n/// Parameters and resources handed to the test function.\npub struct TestingContext {\n    pub instance: Instance,\n    pub adapter: Adapter,\n    pub adapter_info: wgpu::AdapterInfo,\n    pub adapter_downlevel_capabilities: wgpu::DownlevelCapabilities,\n    pub device: Device,\n    pub device_features: wgpu::Features,\n    pub device_limits: wgpu::Limits,\n    pub queue: Queue,\n}\n\n/// Execute the given test configuration with the given adapter report.\n///\n/// If test_info is specified, will use the information whether to skip the test.\n/// If it is not, we'll create the test info from the adapter itself.\npub async fn execute_test(\n    adapter_report: Option<&AdapterReport>,\n    config: GpuTestConfiguration,\n    test_info: Option<TestInfo>,\n) {\n    // If we get information externally, skip based on that information before we do anything.\n    if let Some(TestInfo { skip: true, .. }) = test_info {\n        return;\n    }\n\n    init_logger();\n\n    let _test_guard = isolation::OneTestPerProcessGuard::new();\n\n    let (instance, adapter, _surface_guard) =\n        initialize_adapter(adapter_report, &config.params).await;\n\n    let adapter_info = adapter.get_info();\n    let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities();\n\n    let test_info = test_info.unwrap_or_else(|| {\n        let adapter_report = AdapterReport::from_adapter(&adapter);\n        TestInfo::from_configuration(&config, &adapter_report)\n    });\n\n    // We are now guaranteed to have information about this test, so skip if we need to.\n    if test_info.skip {\n        log::info!(\"TEST RESULT: SKIPPED\");\n        return;\n    }\n\n    // Print the name of the test.\n    log::info!(\"TEST: {}\", config.name);\n\n    let (device, queue) = pollster::block_on(initialize_device(\n        &adapter,\n        config.params.required_features,\n        config.params.required_limits.clone(),\n    ));\n\n    let context = TestingContext {\n        instance,\n        adapter,\n        adapter_info,\n        adapter_downlevel_capabilities,\n        device,\n        device_features: config.params.required_features,\n        device_limits: config.params.required_limits.clone(),\n        queue,\n    };\n\n    let mut failures = Vec::new();\n\n    // Run the test, and catch panics (possibly due to failed assertions).\n    let panic_res = AssertUnwindSafe((config.test.as_ref().unwrap())(context))\n        .catch_unwind()\n        .await;\n\n    if let Err(panic) = panic_res {\n        let message = panic\n            .downcast_ref::<&str>()\n            .copied()\n            .or_else(|| panic.downcast_ref::<String>().map(String::as_str));\n\n        let result = FailureResult::panic();\n\n        let result = if let Some(panic_str) = message {\n            result.with_message(panic_str)\n        } else {\n            result\n        };\n\n        failures.push(result)\n    }\n\n    // Check whether any validation errors were reported during the test run.\n    cfg_if::cfg_if!(\n        if #[cfg(any(not(target_arch = \"wasm32\"), target_os = \"emscripten\"))] {\n            failures.extend(wgpu::hal::VALIDATION_CANARY.get_and_reset().into_iter().map(|msg| FailureResult::validation_error().with_message(msg)));\n        } else if #[cfg(all(target_arch = \"wasm32\", feature = \"webgl\"))] {\n            if _surface_guard.unwrap().check_for_unreported_errors() {\n                failures.push(FailureResult::validation_error());\n            }\n        } else {\n        }\n    );\n\n    // The call to matches_failure will log.\n    if expectations_match_failures(&test_info.failures, failures) == ExpectationMatchResult::Panic {\n        panic!(\n            \"{}: test {:?} did not behave as expected\",\n            config.location, config.name\n        );\n    }\n    // Print the name of the test.\n    log::info!(\"TEST FINISHED: {}\", config.name);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-compile/fail/cpass_lifetime.rs",
    "content": "// Test to ensure that ComputePass without forget_lifetime does not compile\n// when the ComputePass is dropped before the CommandBuffer is finished.\n//\n// See #6145 for more info.\n\nfn main() {\n    let instance = wgpu::Instance::default();\n    let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();\n    let (device, queue) =\n        pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap();\n\n    let mut encoder = device.create_command_encoder(&Default::default());\n    let _compute_pass = encoder.begin_compute_pass(&Default::default());\n    // set up the compute pass...\n\n    let cmd_buffer = encoder.finish();\n    queue.submit([cmd_buffer]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-compile/fail/cpass_lifetime.stderr",
    "content": "error[E0505]: cannot move out of `encoder` because it is borrowed\n  --> compile_tests/fail/cpass_lifetime.rs:16:22\n   |\n12 |     let mut encoder = device.create_command_encoder(&Default::default());\n   |         ----------- binding `encoder` declared here\n13 |     let _compute_pass = encoder.begin_compute_pass(&Default::default());\n   |                         ------- borrow of `encoder` occurs here\n...\n16 |     let cmd_buffer = encoder.finish();\n   |                      ^^^^^^^ move out of `encoder` occurs here\n17 |     queue.submit([cmd_buffer]);\n18 | }\n   | - borrow might be used here, when `_compute_pass` is dropped and runs the destructor for type `wgpu::ComputePass<'_>`\n"
  },
  {
    "path": "tests/tests/wgpu-compile/fail/rpass_lifetime.rs",
    "content": "// Test to ensure that ComputePass without forget_lifetime does not compile\n// when the ComputePass is dropped before the CommandBuffer is finished.\n//\n// See #6145 for more info.\n\nfn main() {\n    let instance = wgpu::Instance::default();\n    let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();\n    let (device, queue) =\n        pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap();\n\n    let mut encoder = device.create_command_encoder(&Default::default());\n    let _render_pass = encoder.begin_render_pass(&Default::default());\n    // set up the render pass...\n\n    let cmd_buffer = encoder.finish();\n    queue.submit([cmd_buffer]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-compile/fail/rpass_lifetime.stderr",
    "content": "error[E0505]: cannot move out of `encoder` because it is borrowed\n  --> compile_tests/fail/rpass_lifetime.rs:16:22\n   |\n12 |     let mut encoder = device.create_command_encoder(&Default::default());\n   |         ----------- binding `encoder` declared here\n13 |     let _render_pass = encoder.begin_render_pass(&Default::default());\n   |                        ------- borrow of `encoder` occurs here\n...\n16 |     let cmd_buffer = encoder.finish();\n   |                      ^^^^^^^ move out of `encoder` occurs here\n17 |     queue.submit([cmd_buffer]);\n18 | }\n   | - borrow might be used here, when `_render_pass` is dropped and runs the destructor for type `wgpu::RenderPass<'_>`\n"
  },
  {
    "path": "tests/tests/wgpu-compile/main.rs",
    "content": "#![cfg(not(miri))]\n// Tests that ensure that various constructs that should not compile do not compile.\n\n#[cfg_attr(miri, ignore)]\n#[test]\nfn compile_fail() {\n    let t = trybuild::TestCases::new();\n    t.compile_fail(\"compile_tests/fail/*.rs\");\n}\n"
  },
  {
    "path": "tests/tests/wgpu-dependency/main.rs",
    "content": "// Cargo-metadata doesn't compile on wasm due to old cargo-util-schemas dependency.\n// Since this test isn't dependent on the current architecture, we can just skip it on wasm without any issues.\n#![cfg(not(any(target_arch = \"wasm32\", miri)))]\n\nuse std::process::Command;\n\n#[derive(Debug)]\nenum Search<'a> {\n    Positive(&'a str),\n    Negative(&'a str),\n}\n\n#[derive(Debug)]\nstruct Requirement<'a> {\n    human_readable_name: &'a str,\n    target: &'a str,\n    packages: &'a [&'a str],\n    features: &'a [&'a str],\n    default_features: bool,\n    search_terms: &'a [Search<'a>],\n}\n\nfn check_feature_dependency(requirement: Requirement) {\n    println!(\"Checking: {}\", requirement.human_readable_name);\n\n    let mut args = Vec::new();\n    args.extend([\"tree\", \"--target\", requirement.target]);\n\n    for package in requirement.packages {\n        args.push(\"--package\");\n        args.push(package);\n    }\n\n    if !requirement.default_features {\n        args.push(\"--no-default-features\");\n    }\n\n    let features = requirement.features.join(\",\");\n    if !requirement.features.is_empty() {\n        args.push(\"--features\");\n        args.push(&features);\n    }\n\n    println!(\"$ cargo {}\", args.join(\" \"));\n\n    let output = Command::new(\"cargo\")\n        .args(&args)\n        .output()\n        .expect(\"Failed to run cargo tree\")\n        .stdout;\n    let output = String::from_utf8(output).expect(\"Output is not valid UTF-8\");\n\n    let mut any_failed = false;\n    println!(\"{output}\");\n\n    for (i, search_term) in requirement.search_terms.iter().enumerate() {\n        // Add a space and after to make sure we're getting a full match\n        let found = match search_term {\n            Search::Positive(search_term) => output.contains(&format!(\" {search_term} \")),\n            Search::Negative(search_term) => !output.contains(&format!(\" {search_term} \")),\n        };\n\n        if found {\n            println!(\n                \"✅ Passed! ({} of {})\",\n                i + 1,\n                requirement.search_terms.len()\n            );\n        } else {\n            println!(\n                \"❌ Failed! ({} of {})\",\n                i + 1,\n                requirement.search_terms.len()\n            );\n            any_failed = true;\n        }\n    }\n\n    assert!(!any_failed);\n}\n\nfn get_all_wgpu_features() -> Vec<String> {\n    let metadata = cargo_metadata::MetadataCommand::new()\n        .no_deps()\n        .exec()\n        .unwrap();\n\n    metadata\n        .packages\n        .iter()\n        .find(|p| p.name.as_str() == \"wgpu\")\n        .unwrap()\n        .features\n        .keys()\n        .cloned()\n        .collect()\n}\n\n#[test]\nfn wasm32_without_webgl_or_noop_does_not_depend_on_wgpu_core() {\n    let all_features = get_all_wgpu_features();\n\n    let removed_features = [\"webgl\", \"noop\", \"wgpu-core\"];\n\n    let features_no_webgl: Vec<&str> = all_features\n        .iter()\n        .map(String::as_str)\n        .filter(|&feature| !removed_features.contains(&feature))\n        .collect();\n\n    check_feature_dependency(Requirement {\n        human_readable_name:\n            \"wasm32 without `webgl` or `noop` feature does not depend on `wgpu-core`\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &features_no_webgl,\n        default_features: false,\n        search_terms: &[Search::Negative(\"wgpu-core\")],\n    });\n}\n\n#[test]\nfn wasm32_with_webgpu_and_wgsl_does_not_depend_on_naga() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"wasm32 with `webgpu` and `wgsl` feature does not depend on `naga`\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgpu\", \"wgsl\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"naga\")],\n    });\n}\n\n#[test]\nfn wasm32_with_webgl_depends_on_glow() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"wasm32 with `webgl` feature depends on `glow`\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgl\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"glow\")],\n    });\n}\n\n#[test]\nfn wasm32_with_only_custom_backend_does_not_depend_on_web_specifics() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"wasm32 with only the `custom` backend does not depend on web-specific bindings [`wasm-bindgen`, `js-sys`, `web-sys`]\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &[\"custom\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"wasm-bindgen\"), Search::Negative(\"js-sys\"), Search::Negative(\"web-sys\")],\n    });\n}\n\n#[test]\nfn wasm32_with_webgpu_backend_does_depend_on_web_specifics() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"wasm32 with the `webgpu` backend depends on web-specific bindings [`wasm-bindgen`, `js-sys`, `web-sys`]\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgpu\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"wasm-bindgen\"), Search::Positive(\"js-sys\"), Search::Positive(\"web-sys\")],\n    });\n}\n\n#[test]\nfn wasm32_with_webgl_backend_does_depend_on_web_specifics() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"wasm32 with the `webgl` backend depends on web-specific bindings [`wasm-bindgen`, `js-sys`, `web-sys`]\",\n        target: \"wasm32-unknown-unknown\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgl\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"wasm-bindgen\"), Search::Positive(\"js-sys\"), Search::Positive(\"web-sys\")],\n    });\n}\n\n#[test]\nfn windows_with_webgpu_webgl_backend_does_not_depend_on_web_specifics() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"windows with the `webgpu` and `webgl` backends enabled does not depend on web-specific bindings [`wasm-bindgen`, `js-sys`, `web-sys`]\",\n        target: \"x86_64-pc-windows-msvc\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgpu\", \"webgl\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"wasm-bindgen\"), Search::Negative(\"js-sys\"), Search::Negative(\"web-sys\")],\n    });\n}\n\n#[test]\nfn windows_with_webgl_does_not_depend_on_glow() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"windows with `webgl` does not depend on `glow`\",\n        target: \"x86_64-pc-windows-msvc\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgl\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"glow\")],\n    });\n}\n\n#[test]\nfn apple_with_vulkan_does_not_depend_on_ash() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"apple with `vulkan` feature does not depend on `ash`\",\n        target: \"aarch64-apple-darwin\",\n        packages: &[\"wgpu\"],\n        features: &[\"vulkan\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"ash\")],\n    });\n}\n\n#[test]\nfn apple_with_vulkan_portability_depends_on_ash_and_renderdoc_sys() {\n    check_feature_dependency(Requirement {\n        human_readable_name:\n            \"apple with `vulkan-portability` feature depends on `ash` and `renderdoc-sys`\",\n        target: \"aarch64-apple-darwin\",\n        packages: &[\"wgpu\"],\n        features: &[\"vulkan-portability\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"ash\"), Search::Positive(\"renderdoc-sys\")],\n    });\n}\n\n#[test]\nfn apple_with_gles_does_not_depend_on_glow() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"apple with 'gles' feature does not depend on 'glow'\",\n        target: \"aarch64-apple-darwin\",\n        packages: &[\"wgpu\"],\n        features: &[\"gles\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"glow\")],\n    });\n}\n\n#[test]\nfn apple_with_angle_depends_on_glow_and_renderdoc_sys() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"apple with 'angle' feature depends on 'glow' and `renderdoc-sys`\",\n        target: \"aarch64-apple-darwin\",\n        packages: &[\"wgpu\"],\n        features: &[\"angle\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"glow\"), Search::Positive(\"renderdoc-sys\")],\n    });\n}\n\n#[test]\nfn apple_with_no_features_does_not_depend_on_renderdoc_sys() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"apple with no features does not depend on 'renderdoc-sys'\",\n        target: \"aarch64-apple-darwin\",\n        packages: &[\"wgpu\"],\n        features: &[],\n        default_features: false,\n        search_terms: &[Search::Negative(\"renderdoc-sys\")],\n    });\n}\n\n#[test]\nfn windows_with_no_features_does_not_depend_on_glow_windows_or_ash() {\n    check_feature_dependency(Requirement {\n        human_readable_name:\n            \"windows with no features does not depend on 'glow', `windows`, or `ash`\",\n        target: \"x86_64-pc-windows-msvc\",\n        packages: &[\"wgpu\"],\n        features: &[],\n        default_features: false,\n        search_terms: &[\n            Search::Negative(\"glow\"),\n            Search::Negative(\"windows\"),\n            Search::Negative(\"ash\"),\n        ],\n    });\n}\n\n#[test]\nfn windows_with_no_features_depends_on_renderdoc_sys() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"windows with no features depends on renderdoc-sys\",\n        target: \"x86_64-pc-windows-msvc\",\n        packages: &[\"wgpu\"],\n        features: &[],\n        default_features: false,\n        search_terms: &[Search::Positive(\"renderdoc-sys\")],\n    });\n}\n\n#[test]\nfn emscripten_with_webgl_does_not_depend_on_glow() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"emscripten with webgl feature does not depend on glow\",\n        target: \"wasm32-unknown-emscripten\",\n        packages: &[\"wgpu\"],\n        features: &[\"webgl\"],\n        default_features: false,\n        search_terms: &[Search::Negative(\"glow\")],\n    });\n}\n\n#[test]\nfn emscripten_with_gles_depends_on_glow() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"emscripten with gles feature depends on glow\",\n        target: \"wasm32-unknown-emscripten\",\n        packages: &[\"wgpu\"],\n        features: &[\"gles\"],\n        default_features: false,\n        search_terms: &[Search::Positive(\"glow\")],\n    });\n}\n\n#[test]\nfn x86_64_does_not_depend_on_portable_atomic() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"x86-64 does not depend on portable-atomic\",\n        target: \"x86_64-unknown-linux-gnu\",\n        packages: &[\"wgpu\"],\n        features: &[],\n        default_features: false,\n        search_terms: &[Search::Negative(\"portable-atomic\")],\n    });\n}\n\n#[test]\nfn ppc32_does_depend_on_portable_atomic() {\n    check_feature_dependency(Requirement {\n        human_readable_name: \"ppc32 does depend on portable-atomic\",\n        target: \"powerpc-unknown-linux-gnu\",\n        packages: &[\"wgpu\"],\n        features: &[],\n        default_features: false,\n        search_terms: &[Search::Positive(\"portable-atomic\")],\n    });\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/bgra8unorm_storage.rs",
    "content": "//! Tests for BGRA8UNORM_STORAGE feature\n\nuse std::borrow::Cow;\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(BGRA8_UNORM_STORAGE);\n}\n\nconst SHADER_SRC: &str = \"\n@group(0) @binding(0) var tex: texture_storage_2d<bgra8unorm, write>;\n@compute @workgroup_size(256)\nfn main(@builtin(workgroup_id) wgid: vec3<u32>) {\n    var texel = vec4f(0.0, 0.0, 1.0, 1.0);\n    textureStore(tex, wgid.xy, texel);\n}\n\";\n\n#[gpu_test]\nstatic BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits {\n                max_storage_textures_per_shader_stage: 1,\n                ..Default::default()\n            })\n            .features(wgpu::Features::BGRA8UNORM_STORAGE),\n    )\n    .run_async(|ctx| async move {\n        let device = &ctx.device;\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 256,\n                height: 256,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Bgra8Unorm,\n            usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::COPY_SRC,\n            view_formats: &[],\n        });\n\n        let view = texture.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            format: None,\n            dimension: None,\n            usage: None,\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            base_array_layer: 0,\n            mip_level_count: Some(1),\n            array_layer_count: Some(1),\n        });\n\n        let readback_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256 * 256 * 4,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::StorageTexture {\n                    access: wgpu::StorageTextureAccess::WriteOnly,\n                    format: wgpu::TextureFormat::Bgra8Unorm,\n                    view_dimension: wgpu::TextureViewDimension::D2,\n                },\n                count: None,\n            }],\n        });\n\n        let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::TextureView(&view),\n            }],\n        });\n\n        let pl = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n        let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(SHADER_SRC)),\n        });\n\n        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&pl),\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            module: &module,\n            cache: None,\n        });\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        {\n            let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n\n            pass.set_bind_group(0, &bg, &[]);\n            pass.set_pipeline(&pipeline);\n            pass.dispatch_workgroups(256, 256, 1);\n        }\n\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &texture,\n                mip_level: 0,\n                origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &readback_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(256 * 4),\n                    rows_per_image: Some(256),\n                },\n            },\n            wgpu::Extent3d {\n                width: 256,\n                height: 256,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let buffer_slice = readback_buffer.slice(..);\n        buffer_slice.map_async(wgpu::MapMode::Read, Result::unwrap);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        {\n            let texels = buffer_slice.get_mapped_range();\n            assert_eq!(texels.len(), 256 * 256 * 4);\n            for texel in texels.chunks(4) {\n                assert_eq!(texel[0], 255); // b\n                assert_eq!(texel[1], 0); // g\n                assert_eq!(texel[2], 0); // r\n                assert_eq!(texel[3], 255); // a\n            }\n        }\n\n        readback_buffer.unmap();\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/bind_group_layout_dedup.rs",
    "content": "use std::num::NonZeroU64;\n\nuse wgpu_test::{\n    fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        BIND_GROUP_LAYOUT_DEDUPLICATION,\n        BIND_GROUP_LAYOUT_DEDUPLICATION_WITH_DROPPED_USER_HANDLE,\n        GET_DERIVED_BGL,\n        SEPARATE_PIPELINES_HAVE_INCOMPATIBLE_DERIVED_BGLS,\n        DERIVED_BGLS_INCOMPATIBLE_WITH_REGULAR_BGLS,\n        BIND_GROUP_LAYOUT_DEDUPLICATION_DERIVED,\n    ]);\n}\n\nconst SHADER_SRC: &str = \"\n@group(0) @binding(0)\nvar<uniform> buffer : f32;\n\n@compute @workgroup_size(1, 1, 1) fn no_resources() {}\n@compute @workgroup_size(1, 1, 1) fn resources() {\n    // Just need a static use.\n    let _value = buffer;\n}\n\";\n\nconst ENTRY: wgpu::BindGroupLayoutEntry = wgpu::BindGroupLayoutEntry {\n    binding: 0,\n    visibility: wgpu::ShaderStages::COMPUTE,\n    ty: wgpu::BindingType::Buffer {\n        ty: wgpu::BufferBindingType::Uniform,\n        has_dynamic_offset: false,\n        // Should be Some(.unwrap()) but unwrap is not const.\n        min_binding_size: NonZeroU64::new(4),\n    },\n    count: None,\n};\n\n#[gpu_test]\nstatic BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .enable_noop(),\n    )\n    .run_async(bgl_dedupe);\n\nasync fn bgl_dedupe(ctx: TestingContext) {\n    let entries = &[];\n\n    let bgl_1a = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries,\n        });\n\n    let bgl_1b = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries,\n        });\n\n    let bg_1a = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl_1a,\n        entries: &[],\n    });\n\n    let bg_1b = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl_1b,\n        entries: &[],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl_1b)],\n            immediate_size: 0,\n        });\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let desc = wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: Some(&pipeline_layout),\n        module: &module,\n        entry_point: Some(\"no_resources\"),\n        compilation_options: Default::default(),\n        cache: None,\n    };\n\n    let pipeline = ctx.device.create_compute_pipeline(&desc);\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    pass.set_bind_group(0, &bg_1b, &[]);\n    pass.set_pipeline(&pipeline);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    pass.set_bind_group(0, &bg_1a, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    ctx.queue.submit(Some(encoder.finish()));\n}\n\n#[gpu_test]\nstatic BIND_GROUP_LAYOUT_DEDUPLICATION_WITH_DROPPED_USER_HANDLE: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .enable_noop(),\n        )\n        .run_sync(bgl_dedupe_with_dropped_user_handle);\n\n// https://github.com/gfx-rs/wgpu/issues/4824\nfn bgl_dedupe_with_dropped_user_handle(ctx: TestingContext) {\n    let bgl_1 = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[ENTRY],\n        });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl_1)],\n            immediate_size: 0,\n        });\n\n    // We drop bgl_1 here. As bgl_1 is still alive, referenced by the pipeline layout,\n    // the deduplication should work as expected. Previously this did not work.\n    drop(bgl_1);\n\n    let bgl_2 = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[ENTRY],\n        });\n\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4,\n        usage: wgpu::BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl_2,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"no_resources\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    pass.set_bind_group(0, &bg, &[]);\n    pass.set_pipeline(&pipeline);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    ctx.queue.submit(Some(encoder.finish()));\n}\n\n#[gpu_test]\nstatic GET_DERIVED_BGL: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .enable_noop(),\n    )\n    .run_sync(get_derived_bgl);\n\nfn get_derived_bgl(ctx: TestingContext) {\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4,\n        usage: wgpu::BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: Some(\"resources\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    // We create two bind groups, pulling the bind_group_layout from the pipeline each time.\n    //\n    // This ensures a derived BGLs are properly deduplicated despite multiple external\n    // references.\n    let bg1 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let bg2 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    pass.set_pipeline(&pipeline);\n\n    pass.set_bind_group(0, &bg1, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    pass.set_bind_group(0, &bg2, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    ctx.queue.submit(Some(encoder.finish()));\n}\n\n#[gpu_test]\nstatic SEPARATE_PIPELINES_HAVE_INCOMPATIBLE_DERIVED_BGLS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .enable_noop(),\n        )\n        .run_sync(separate_pipelines_have_incompatible_derived_bgls);\n\nfn separate_pipelines_have_incompatible_derived_bgls(ctx: TestingContext) {\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4,\n        usage: wgpu::BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let desc = wgpu::ComputePipelineDescriptor {\n        label: None,\n        layout: None,\n        module: &module,\n        entry_point: Some(\"resources\"),\n        compilation_options: Default::default(),\n        cache: None,\n    };\n    // Create two pipelines, creating a BG from the second.\n    let pipeline1 = ctx.device.create_compute_pipeline(&desc);\n    let pipeline2 = ctx.device.create_compute_pipeline(&desc);\n\n    let bg2 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline2.get_bind_group_layout(0),\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    pass.set_pipeline(&pipeline1);\n\n    // We use the wrong bind group for this pipeline here. This should fail.\n    pass.set_bind_group(0, &bg2, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    fail(\n        &ctx.device,\n        || encoder.finish(),\n        Some(\"label at index 0 is not compatible with the corresponding bindgrouplayout\"),\n    );\n}\n\n#[gpu_test]\nstatic DERIVED_BGLS_INCOMPATIBLE_WITH_REGULAR_BGLS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .enable_noop(),\n        )\n        .run_sync(derived_bgls_incompatible_with_regular_bgls);\n\nfn derived_bgls_incompatible_with_regular_bgls(ctx: TestingContext) {\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4,\n        usage: wgpu::BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    // Create a pipeline.\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: Some(\"resources\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    // Create a matching BGL\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[ENTRY],\n        });\n\n    // Create a bind group from the explicit BGL. This should be incompatible with the derived BGL used by the pipeline.\n    let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n\n    pass.set_pipeline(&pipeline);\n\n    pass.set_bind_group(0, &bg, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    fail(\n        &ctx.device,\n        || encoder.finish(),\n        Some(\"label at index 0 is not compatible with the corresponding bindgrouplayout\"),\n    );\n}\n\n#[gpu_test]\nstatic BIND_GROUP_LAYOUT_DEDUPLICATION_DERIVED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .enable_noop(),\n    )\n    .run_sync(bgl_dedupe_derived);\n\nfn bgl_dedupe_derived(ctx: TestingContext) {\n    let src = \"\n        @group(0) @binding(0) var<uniform> u1: vec4f;\n        @group(1) @binding(0) var<uniform> u2: vec4f;\n\n        @compute @workgroup_size(1, 1, 1)\n        fn main() {\n            // Just need a static use.\n            let _u1 = u1;\n            let _u2 = u2;\n        }\n    \";\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(src.into()),\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let bind_group_layout_0 = pipeline.get_bind_group_layout(0);\n    let bind_group_layout_1 = pipeline.get_bind_group_layout(1);\n\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 16,\n        usage: wgpu::BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_0 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout_1,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                buffer: &buffer,\n                offset: 0,\n                size: None,\n            }),\n        }],\n    });\n    let bind_group_1 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout_0,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                buffer: &buffer,\n                offset: 0,\n                size: None,\n            }),\n        }],\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n    pass.set_pipeline(&pipeline);\n    pass.set_bind_group(0, &bind_group_0, &[]);\n    pass.set_bind_group(1, &bind_group_1, &[]);\n    pass.dispatch_workgroups(1, 1, 1);\n\n    drop(pass);\n\n    ctx.queue.submit(Some(encoder.finish()));\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/bind_groups.rs",
    "content": "use std::num::NonZeroU64;\n\nuse wgpu::{util::DeviceExt, BufferUsages, PollType};\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        MULTIPLE_BINDINGS_WITH_DIFFERENT_SIZES,\n        BIND_GROUP_NONFILTERING_LAYOUT_NONFILTERING_SAMPLER,\n        BIND_GROUP_NONFILTERING_LAYOUT_MIN_SAMPLER,\n        BIND_GROUP_NONFILTERING_LAYOUT_MAG_SAMPLER,\n        BIND_GROUP_NONFILTERING_LAYOUT_MIPMAP_SAMPLER,\n        BIND_GROUP_WITH_MAX_BINDING_INDEX,\n    ]);\n}\n\n/// Create two bind groups against the same bind group layout, in the same\n/// compute pass, but against two different shaders that have different binding\n/// sizes. The first has binding size 8, the second has binding size 4.\n///\n/// Regression test for https://github.com/gfx-rs/wgpu/issues/7359.\nfn multiple_bindings_with_differing_sizes(ctx: TestingContext) {\n    const SHADER_SRC: &[&str] = &[\n        \"\n        @group(0) @binding(0)\n        var<uniform> buffer : vec2<f32>;\n\n        @compute @workgroup_size(1, 1, 1) fn main() {\n            // Just need a static use.\n            let _value = buffer.x;\n        }\n        \",\n        \"\n        @group(0) @binding(0)\n        var<uniform> buffer : f32;\n\n        @compute @workgroup_size(1, 1, 1) fn main() {\n            // Just need a static use.\n            let _value = buffer;\n        }\n        \",\n    ];\n\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"buffer\"),\n        size: 8,\n        usage: BufferUsages::UNIFORM | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bgl\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Uniform,\n                    has_dynamic_offset: true,\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n    let pipelines = SHADER_SRC\n        .iter()\n        .enumerate()\n        .map(|(i, &shader_src)| {\n            let module = ctx\n                .device\n                .create_shader_module(wgpu::ShaderModuleDescriptor {\n                    label: Some(&format!(\"shader{i}\")),\n                    source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n                });\n\n            ctx.device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: Some(&format!(\"pipeline{i}\")),\n                    layout: Some(&pipeline_layout),\n                    module: &module,\n                    entry_point: Some(\"main\"),\n                    compilation_options: Default::default(),\n                    cache: None,\n                })\n        })\n        .collect::<Vec<_>>();\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n\n    for (i, pipeline) in pipelines.iter().enumerate() {\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(&format!(\"bg{i}\")),\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &buffer,\n                    offset: 0,\n                    size: Some(NonZeroU64::new(u64::try_from(8 - 4 * i).unwrap()).unwrap()),\n                }),\n            }],\n        });\n\n        cpass.set_pipeline(pipeline);\n        cpass.set_bind_group(0, &bind_group, &[0]);\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n    drop(cpass);\n\n    let data = [0u8; 8];\n    ctx.queue.write_buffer(&buffer, 0, &data);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n}\n\n/// Test `descriptor` against a bind group layout that requires non-filtering sampler.\nfn try_sampler_nonfiltering_layout(\n    ctx: TestingContext,\n    descriptor: &wgpu::SamplerDescriptor,\n    good: bool,\n) {\n    let label = descriptor.label;\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::FRAGMENT,\n                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),\n                count: None,\n            }],\n        });\n\n    let sampler = ctx.device.create_sampler(descriptor);\n\n    let create_bind_group = || {\n        let _ = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label,\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Sampler(&sampler),\n            }],\n        });\n    };\n\n    if good {\n        wgpu_test::valid(&ctx.device, create_bind_group);\n    } else {\n        wgpu_test::fail(\n            &ctx.device,\n            create_bind_group,\n            Some(\"but given a sampler with filtering\"),\n        );\n    }\n}\n\n#[gpu_test]\nstatic MULTIPLE_BINDINGS_WITH_DIFFERENT_SIZES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits::downlevel_defaults())\n            .expect_fail(FailureCase::always())\n            .enable_noop(), // https://github.com/gfx-rs/wgpu/issues/7359\n    )\n    .run_sync(multiple_bindings_with_differing_sizes);\n\n#[gpu_test]\nstatic BIND_GROUP_NONFILTERING_LAYOUT_NONFILTERING_SAMPLER: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            try_sampler_nonfiltering_layout(\n                ctx,\n                &wgpu::SamplerDescriptor {\n                    label: Some(\"bind_group_non_filtering_layout_nonfiltering_sampler\"),\n                    min_filter: wgpu::FilterMode::Nearest,\n                    mag_filter: wgpu::FilterMode::Nearest,\n                    mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n                    ..wgpu::SamplerDescriptor::default()\n                },\n                true,\n            );\n        });\n\n#[gpu_test]\nstatic BIND_GROUP_NONFILTERING_LAYOUT_MIN_SAMPLER: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            try_sampler_nonfiltering_layout(\n                ctx,\n                &wgpu::SamplerDescriptor {\n                    label: Some(\"bind_group_non_filtering_layout_min_sampler\"),\n                    min_filter: wgpu::FilterMode::Linear,\n                    mag_filter: wgpu::FilterMode::Nearest,\n                    mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n                    ..wgpu::SamplerDescriptor::default()\n                },\n                false,\n            );\n        });\n\n#[gpu_test]\nstatic BIND_GROUP_NONFILTERING_LAYOUT_MAG_SAMPLER: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            try_sampler_nonfiltering_layout(\n                ctx,\n                &wgpu::SamplerDescriptor {\n                    label: Some(\"bind_group_non_filtering_layout_mag_sampler\"),\n                    min_filter: wgpu::FilterMode::Nearest,\n                    mag_filter: wgpu::FilterMode::Linear,\n                    mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n                    ..wgpu::SamplerDescriptor::default()\n                },\n                false,\n            );\n        });\n\n#[gpu_test]\nstatic BIND_GROUP_NONFILTERING_LAYOUT_MIPMAP_SAMPLER: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            try_sampler_nonfiltering_layout(\n                ctx,\n                &wgpu::SamplerDescriptor {\n                    label: Some(\"bind_group_non_filtering_layout_mipmap_sampler\"),\n                    min_filter: wgpu::FilterMode::Nearest,\n                    mag_filter: wgpu::FilterMode::Nearest,\n                    mipmap_filter: wgpu::MipmapFilterMode::Linear,\n                    ..wgpu::SamplerDescriptor::default()\n                },\n                false,\n            );\n        });\n\n#[gpu_test]\nstatic BIND_GROUP_WITH_MAX_BINDING_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits::downlevel_defaults())\n            .expect_fail(FailureCase::kosmic_krisp()), // https://github.com/gfx-rs/wgpu/issues/9187\n    )\n    .run_async(|ctx| async move {\n        let (device, queue) = ctx\n            .adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                required_limits: wgpu::Limits {\n                    max_bindings_per_bind_group: ctx.adapter.limits().max_bindings_per_bind_group,\n                    ..Default::default()\n                },\n                ..Default::default()\n            })\n            .await\n            .unwrap();\n\n        let max_binding_index = device.limits().max_bindings_per_bind_group - 1;\n        let src_binding_index = max_binding_index - 1;\n        let dst_binding_index = max_binding_index;\n\n        let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: src_binding_index,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: dst_binding_index,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n        let pl = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n        let shader = format!(\n            \"\n            @group(0) @binding({src_binding_index}) var<uniform> src: u32;\n            @group(0) @binding({dst_binding_index}) var<storage, read_write> dst: u32;\n            @compute @workgroup_size(1)\n            fn main() {{\n                dst = src;\n            }}\"\n        );\n\n        let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&pl),\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            module: &module,\n            cache: None,\n        });\n\n        let test_value = 123u32;\n\n        let src = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            usage: wgpu::BufferUsages::UNIFORM,\n            contents: &test_value.to_le_bytes(),\n        });\n        let dst = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 4,\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        let readback = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 4,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: src_binding_index,\n                    resource: wgpu::BindingResource::Buffer(src.as_entire_buffer_binding()),\n                },\n                wgpu::BindGroupEntry {\n                    binding: dst_binding_index,\n                    resource: wgpu::BindingResource::Buffer(dst.as_entire_buffer_binding()),\n                },\n            ],\n        });\n\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        {\n            let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n            pass.set_bind_group(0, &bg, &[]);\n            pass.set_pipeline(&pipeline);\n            pass.dispatch_workgroups(1, 1, 1);\n        }\n        encoder.copy_buffer_to_buffer(&dst, 0, &readback, 0, 4);\n        queue.submit(Some(encoder.finish()));\n\n        readback.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n        device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n        assert_eq!(\n            &*readback.slice(..).get_mapped_range(),\n            &test_value.to_le_bytes()\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/buffers.rs",
    "content": "use std::num::{NonZeroU32, NonZeroU64};\n\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        BINDING_ARRAY_UNIFORM_BUFFERS,\n        PARTIAL_BINDING_ARRAY_UNIFORM_BUFFERS,\n        BINDING_ARRAY_STORAGE_BUFFERS,\n        PARTIAL_BINDING_ARRAY_STORAGE_BUFFERS,\n    ]);\n}\n\n#[gpu_test]\nstatic BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(Features::BUFFER_BINDING_ARRAY | Features::UNIFORM_BUFFER_BINDING_ARRAYS)\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 16,\n                ..Limits::default()\n            })\n            // Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733\n            //\n            // Causes varying errors on different devices, so we don't match more closely.\n            .expect_fail(FailureCase::backend(Backends::VULKAN))\n            // These issues cause a segfault on lavapipe\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Uniform, false).await });\n\n#[gpu_test]\nstatic PARTIAL_BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::BUFFER_BINDING_ARRAY\n                    | Features::PARTIALLY_BOUND_BINDING_ARRAY\n                    | Features::UNIFORM_BUFFER_BINDING_ARRAYS,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 32,\n                ..Limits::default()\n            })\n            // Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733\n            //\n            // Causes varying errors on different devices, so we don't match more closely.\n            .expect_fail(FailureCase::backend(Backends::VULKAN))\n            // These issues cause a segfault on lavapipe\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Uniform, true).await });\n\n#[gpu_test]\nstatic BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters {\n        required_instance_flags: wgpu::InstanceFlags::GPU_BASED_VALIDATION,\n        required_features: Features::BUFFER_BINDING_ARRAY\n            | Features::STORAGE_RESOURCE_BINDING_ARRAY\n            | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n        required_limits: Limits {\n            max_binding_array_elements_per_shader_stage: 17,\n            ..Limits::default()\n        },\n        // See https://github.com/gfx-rs/wgpu/issues/6745.\n        failures: FailureCase::mac_vulkan(|case| case.panic(\"bad SPIR-V wrapper struct inference\")),\n        ..Default::default()\n    })\n    .run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Storage, false).await });\n\n#[gpu_test]\nstatic PARTIAL_BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters {\n        required_instance_flags: wgpu::InstanceFlags::GPU_BASED_VALIDATION,\n        required_features: Features::BUFFER_BINDING_ARRAY\n            | Features::PARTIALLY_BOUND_BINDING_ARRAY\n            | Features::STORAGE_RESOURCE_BINDING_ARRAY\n            | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n        required_limits: Limits {\n            max_binding_array_elements_per_shader_stage: 33,\n            ..Limits::default()\n        },\n        // See https://github.com/gfx-rs/wgpu/issues/6745.\n        failures: FailureCase::mac_vulkan(|case| case.panic(\"bad SPIR-V wrapper struct inference\")),\n        ..Default::default()\n    })\n    .run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Storage, true).await });\n\nenum BufferType {\n    Storage,\n    Uniform,\n}\n\nasync fn binding_array_buffers(\n    ctx: TestingContext,\n    buffer_type: BufferType,\n    partial_binding: bool,\n) {\n    let storage_mode = match buffer_type {\n        BufferType::Storage => \"storage\",\n        BufferType::Uniform => \"uniform\",\n    };\n\n    let shader = r#\"\n        struct ImAU32 {\n            value: u32,\n            _padding: u32,\n            _padding2: u32,\n            _padding3: u32,\n        };\n\n        @group(0) @binding(0)\n        var<{storage_mode}> buffers: binding_array<ImAU32>;\n\n        @group(0) @binding(1)\n        var<storage, read_write> output_buffer: array<u32>;\n\n        @compute\n        @workgroup_size(16, 1, 1)\n        fn compMain(@builtin(global_invocation_id) id: vec3u) {\n            output_buffer[id.x] = buffers[id.x].value;\n        }\n    \"#;\n    let shader = shader.replace(\"{storage_mode}\", storage_mode);\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"Binding Array Buffer\"),\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n    let image = image::load_from_memory(include_bytes!(\"../3x3_colors.png\")).unwrap();\n    // Resize image to 4x4\n    let image = image\n        .resize_exact(4, 4, image::imageops::FilterType::Gaussian)\n        .into_rgba8();\n\n    // Create one buffer for each pixel\n    let mut buffers = Vec::with_capacity(64);\n    for data in image.pixels() {\n        let buffer = ctx.device.create_buffer(&BufferDescriptor {\n            label: None,\n            usage: match buffer_type {\n                BufferType::Storage => BufferUsages::STORAGE | BufferUsages::COPY_DST,\n                BufferType::Uniform => BufferUsages::UNIFORM | BufferUsages::COPY_DST,\n            },\n            // 16 to allow padding for uniform buffers\n            size: 16,\n            mapped_at_creation: true,\n        });\n        buffer\n            .get_mapped_range_mut(..)\n            .slice(0..4)\n            .copy_from_slice(&data.0);\n        buffer.unmap();\n        buffers.push(buffer);\n    }\n\n    let output_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: 4 * 4 * 4,\n        usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let multiplier = if partial_binding { 2 } else { 1 };\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: Some(\"Bind Group Layout\"),\n            entries: &[\n                BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        ty: match buffer_type {\n                            BufferType::Storage => BufferBindingType::Storage { read_only: true },\n                            BufferType::Uniform => BufferBindingType::Uniform,\n                        },\n                        has_dynamic_offset: false,\n                        min_binding_size: Some(NonZeroU64::new(16).unwrap()),\n                    },\n                    count: Some(NonZeroU32::new(16 * multiplier).unwrap()),\n                },\n                BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        ty: BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n    let buffer_references: Vec<_> = buffers\n        .iter()\n        .map(|b| b.as_entire_buffer_binding())\n        .collect();\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::BufferArray(&buffer_references),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(\"Pipeline Layout\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: Some(\"Compute Pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"compMain\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor { label: None });\n    {\n        let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        render_pass.set_pipeline(&pipeline);\n        render_pass.set_bind_group(0, &bind_group, &[]);\n        render_pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    let readback_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: 4 * 4 * 4,\n        usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &readback_buffer, 0, 4 * 4 * 4);\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(MapMode::Read, |_| {});\n\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n\n    let data = slice.get_mapped_range();\n\n    assert_eq!(&data[..], &*image);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/mod.rs",
    "content": "mod buffers;\nmod sampled_textures;\nmod samplers;\nmod storage_textures;\nmod tlas;\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    buffers::all_tests(tests);\n    sampled_textures::all_tests(tests);\n    samplers::all_tests(tests);\n    storage_textures::all_tests(tests);\n    tlas::all_tests(tests);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/sampled_textures.rs",
    "content": "use std::num::NonZeroU32;\n\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        BINDING_ARRAY_SAMPLED_TEXTURES,\n        PARTIAL_BINDING_ARRAY_SAMPLED_TEXTURES,\n    ]);\n}\n\n#[gpu_test]\nstatic BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 16,\n                ..Limits::default()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n    )\n    .run_async(|ctx| async move { binding_array_sampled_textures(ctx, false).await });\n\n#[gpu_test]\nstatic PARTIAL_BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING\n                    | Features::PARTIALLY_BOUND_BINDING_ARRAY,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 32,\n                ..Limits::default()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n    )\n    .run_async(|ctx| async move { binding_array_sampled_textures(ctx, false).await });\n\n/// Test to see how texture bindings array work and additionally making sure\n/// that non-uniform indexing is working correctly.\n///\n/// If non-uniform indexing is not working correctly, AMD will produce the wrong\n/// output due to non-native support for non-uniform indexing within a WARP.\nasync fn binding_array_sampled_textures(ctx: TestingContext, partially_bound: bool) {\n    let shader = r#\"\n        @group(0) @binding(0)\n        var textures: binding_array<texture_2d<f32>>;\n\n        @vertex\n        fn vertMain(@builtin(vertex_index) id: u32) -> @builtin(position) vec4f {\n            var positions = array<vec2f, 3>(\n                vec2f(-1.0, -1.0),\n                vec2f(3.0, -1.0),\n                vec2f(-1.0, 3.0)\n            );\n\n            return vec4<f32>(positions[id], 0.0, 1.0);\n        }\n\n        @fragment\n        fn fragMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {\n            let pixel = vec2u(floor(pos.xy));\n            let index = pixel.y * 4 + pixel.x;\n\n            return textureLoad(textures[index], vec2u(0), 0);\n        }\n    \"#;\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"Binding Array Texture\"),\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n    let image = image::load_from_memory(include_bytes!(\"../3x3_colors.png\")).unwrap();\n    // Resize image to 4x4\n    let image = image\n        .resize_exact(4, 4, image::imageops::FilterType::Gaussian)\n        .into_rgba8();\n\n    // Create one texture for each pixel\n    let mut input_views = Vec::with_capacity(64);\n    for data in image.pixels() {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: TextureDimension::D2,\n            format: TextureFormat::Rgba8UnormSrgb,\n            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n\n        ctx.queue.write_texture(\n            TexelCopyTextureInfo {\n                texture: &texture,\n                mip_level: 0,\n                origin: Origin3d::ZERO,\n                aspect: TextureAspect::All,\n            },\n            &data.0,\n            TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: Some(1),\n            },\n            Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        input_views.push(texture.create_view(&TextureViewDescriptor::default()));\n    }\n\n    let output_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Output Texture\"),\n        size: Extent3d {\n            width: 4,\n            height: 4,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8UnormSrgb,\n        usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let output_view = output_texture.create_view(&TextureViewDescriptor::default());\n\n    let count = if partially_bound { 32 } else { 16 };\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: Some(\"Bind Group Layout\"),\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::FRAGMENT,\n                ty: BindingType::Texture {\n                    sample_type: TextureSampleType::Float { filterable: false },\n                    view_dimension: TextureViewDimension::D2,\n                    multisampled: false,\n                },\n                count: Some(NonZeroU32::new(count).unwrap()),\n            }],\n        });\n\n    let input_view_references: Vec<_> = input_views.iter().collect();\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::TextureViewArray(&input_view_references),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(\"Pipeline Layout\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: Some(\"Render Pipeline\"),\n            layout: Some(&pipeline_layout),\n            vertex: VertexState {\n                module: &module,\n                entry_point: Some(\"vertMain\"),\n                buffers: &[],\n                compilation_options: PipelineCompilationOptions::default(),\n            },\n            fragment: Some(FragmentState {\n                module: &module,\n                entry_point: Some(\"fragMain\"),\n                targets: &[Some(ColorTargetState {\n                    format: TextureFormat::Rgba8UnormSrgb,\n                    blend: None,\n                    write_mask: ColorWrites::ALL,\n                })],\n                compilation_options: PipelineCompilationOptions::default(),\n            }),\n            primitive: PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: MultisampleState::default(),\n            cache: None,\n            multiview_mask: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor { label: None });\n    {\n        let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {\n            label: Some(\"Render Pass\"),\n            color_attachments: &[Some(RenderPassColorAttachment {\n                view: &output_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: Operations {\n                    load: LoadOp::Clear(Color::BLACK),\n                    store: StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        render_pass.set_pipeline(&pipeline);\n        render_pass.set_bind_group(0, &bind_group, &[]);\n        render_pass.draw(0..3, 0..1);\n    }\n\n    let readback_buffers = ReadbackBuffers::new(&ctx.device, &output_texture);\n    readback_buffers.copy_from(&ctx.device, &mut encoder, &output_texture);\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    readback_buffers.assert_buffer_contents(&ctx, &image).await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/samplers.rs",
    "content": "use std::num::{NonZeroU32, NonZeroU64};\n\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([BINDING_ARRAY_SAMPLERS, PARTIAL_BINDING_ARRAY_SAMPLERS]);\n}\n\n#[gpu_test]\nstatic BINDING_ARRAY_SAMPLERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 2,\n                max_binding_array_sampler_elements_per_shader_stage: 2,\n                ..Limits::default()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n    )\n    .run_async(|ctx| async move { binding_array_samplers(ctx, false).await });\n\n#[gpu_test]\nstatic PARTIAL_BINDING_ARRAY_SAMPLERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING\n                    | Features::PARTIALLY_BOUND_BINDING_ARRAY,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 4,\n                max_binding_array_sampler_elements_per_shader_stage: 4,\n                ..Limits::default()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\"),\n            ),\n    )\n    .run_async(|ctx| async move { binding_array_samplers(ctx, true).await });\n\nasync fn binding_array_samplers(ctx: TestingContext, partially_bound: bool) {\n    let shader = r#\"\n        @group(0) @binding(0)\n        var samplers: binding_array<sampler>;\n        @group(0) @binding(1)\n        var texture: texture_2d<f32>;\n        @group(0) @binding(2)\n        var<storage, read_write> output_values: array<u32>;\n\n        @compute\n        @workgroup_size(2, 1, 1)\n        fn compMain(@builtin(global_invocation_id) id: vec3u) {\n            output_values[id.x] = pack4x8unorm(textureSampleLevel(texture, samplers[id.x], vec2f(0.25 + (0.5 * 0.25), 0.5), 0.0));\n        }\n    \"#;\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"Binding Array Texture\"),\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n    let input_image: [u8; 8] = [\n        255, 0, 0, 255, //\n        0, 255, 0, 255, //\n    ];\n\n    let expected_output: [u8; 8] = [\n        191, 64, 0, 255, //\n        255, 0, 0, 255, //\n    ];\n\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 2,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,\n        view_formats: &[],\n    });\n\n    ctx.queue.write_texture(\n        TexelCopyTextureInfo {\n            texture: &texture,\n            mip_level: 0,\n            origin: Origin3d::ZERO,\n            aspect: TextureAspect::All,\n        },\n        &input_image,\n        TexelCopyBufferLayout {\n            offset: 0,\n            bytes_per_row: Some(8),\n            rows_per_image: Some(1),\n        },\n        Extent3d {\n            width: 2,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n    );\n\n    let input_view = texture.create_view(&TextureViewDescriptor::default());\n\n    let samplers = [\n        ctx.device.create_sampler(&SamplerDescriptor {\n            label: None,\n            address_mode_u: AddressMode::ClampToEdge,\n            address_mode_v: AddressMode::ClampToEdge,\n            address_mode_w: AddressMode::ClampToEdge,\n            mag_filter: FilterMode::Linear,\n            min_filter: FilterMode::Linear,\n            mipmap_filter: MipmapFilterMode::Linear,\n            lod_min_clamp: 0.0,\n            lod_max_clamp: 1000.0,\n            compare: None,\n            anisotropy_clamp: 1,\n            border_color: None,\n        }),\n        ctx.device.create_sampler(&SamplerDescriptor {\n            label: None,\n            address_mode_u: AddressMode::ClampToEdge,\n            address_mode_v: AddressMode::ClampToEdge,\n            address_mode_w: AddressMode::ClampToEdge,\n            mag_filter: FilterMode::Nearest,\n            min_filter: FilterMode::Nearest,\n            mipmap_filter: MipmapFilterMode::Nearest,\n            lod_min_clamp: 0.0,\n            lod_max_clamp: 1000.0,\n            compare: None,\n            anisotropy_clamp: 1,\n            border_color: None,\n        }),\n    ];\n\n    let output_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: 4 * 2,\n        usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let multiplier = if partially_bound { 2 } else { 1 };\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: Some(\"Bind Group Layout\"),\n            entries: &[\n                BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Sampler(SamplerBindingType::Filtering),\n                    count: Some(NonZeroU32::new(2 * multiplier).unwrap()),\n                },\n                BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Texture {\n                        sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                        view_dimension: wgpu::TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: None,\n                },\n                BindGroupLayoutEntry {\n                    binding: 2,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        ty: BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: Some(NonZeroU64::new(4).unwrap()),\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n    let sampler_references: Vec<_> = samplers.iter().collect();\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::SamplerArray(&sampler_references),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: BindingResource::TextureView(&input_view),\n            },\n            BindGroupEntry {\n                binding: 2,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(\"Pipeline Layout\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: Some(\"Compute Pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"compMain\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor { label: None });\n    {\n        let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        render_pass.set_pipeline(&pipeline);\n        render_pass.set_bind_group(0, &bind_group, &[]);\n        render_pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    let readback_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: 4 * 2,\n        usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &readback_buffer, 0, 4 * 2);\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    readback_buffer.slice(..).map_async(MapMode::Read, |_| {});\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n\n    let readback_buffer_slice = readback_buffer.slice(..).get_mapped_range();\n\n    assert_eq!(&readback_buffer_slice[0..8], &expected_output[..]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/storage_textures.rs",
    "content": "use std::num::NonZeroU32;\n\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        BINDING_ARRAY_STORAGE_TEXTURES,\n        PARTIAL_BINDING_ARRAY_STORAGE_TEXTURES,\n    ]);\n}\n\n#[gpu_test]\nstatic BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::STORAGE_RESOURCE_BINDING_ARRAY\n                    | Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING\n                    | Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 17,\n                ..Limits::default()\n            }),\n    )\n    .run_async(|ctx| async move { binding_array_storage_textures(ctx, false).await });\n\n#[gpu_test]\nstatic PARTIAL_BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                Features::TEXTURE_BINDING_ARRAY\n                    | Features::PARTIALLY_BOUND_BINDING_ARRAY\n                    | Features::STORAGE_RESOURCE_BINDING_ARRAY\n                    | Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING\n                    | Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 33,\n                ..Limits::default()\n            }),\n    )\n    .run_async(|ctx| async move { binding_array_storage_textures(ctx, true).await });\n\nasync fn binding_array_storage_textures(ctx: TestingContext, partially_bound: bool) {\n    let shader = r#\"\n        @group(0) @binding(0)\n        var textures: binding_array<texture_storage_2d<rgba8unorm, read_write> >;\n\n        @compute\n        @workgroup_size(4, 4, 1)\n        fn compMain(@builtin(global_invocation_id) id: vec3u) {\n            // Read from the 4x4 textures in 0-15, then write to the 4x4 texture in 16\n\n            let pixel = vec2u(id.xy);\n            let index = pixel.y * 4 + pixel.x;\n\n            let color = textureLoad(textures[index], vec2u(0));\n            textureStore(textures[16], pixel, color);\n        }\n    \"#;\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"Binding Array Texture\"),\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n    let image = image::load_from_memory(include_bytes!(\"../3x3_colors.png\")).unwrap();\n    // Resize image to 4x4\n    let image = image\n        .resize_exact(4, 4, image::imageops::FilterType::Gaussian)\n        .into_rgba8();\n\n    // Create one texture for each pixel\n    let mut input_views = Vec::with_capacity(64);\n    for data in image.pixels() {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: TextureDimension::D2,\n            format: TextureFormat::Rgba8Unorm,\n            usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n\n        ctx.queue.write_texture(\n            TexelCopyTextureInfo {\n                texture: &texture,\n                mip_level: 0,\n                origin: Origin3d::ZERO,\n                aspect: TextureAspect::All,\n            },\n            &data.0,\n            TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(4),\n                rows_per_image: Some(1),\n            },\n            Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        input_views.push(texture.create_view(&TextureViewDescriptor::default()));\n    }\n\n    let output_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Output Texture\"),\n        size: Extent3d {\n            width: 4,\n            height: 4,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let output_view = output_texture.create_view(&TextureViewDescriptor::default());\n\n    let multiplier = if partially_bound { 2 } else { 1 };\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: Some(\"Bind Group Layout\"),\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::COMPUTE,\n                ty: BindingType::StorageTexture {\n                    access: StorageTextureAccess::ReadWrite,\n                    format: TextureFormat::Rgba8Unorm,\n                    view_dimension: TextureViewDimension::D2,\n                },\n                count: Some(NonZeroU32::new(4 * 4 * multiplier + 1).unwrap()),\n            }],\n        });\n\n    let mut input_view_references: Vec<_> = input_views.iter().collect();\n    input_view_references.push(&output_view);\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"Bind Group\"),\n        layout: &bind_group_layout,\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::TextureViewArray(&input_view_references),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(\"Pipeline Layout\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: Some(\"Compute Pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"compMain\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor { label: None });\n    {\n        let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        render_pass.set_pipeline(&pipeline);\n        render_pass.set_bind_group(0, &bind_group, &[]);\n        render_pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    let readback_buffers = ReadbackBuffers::new(&ctx.device, &output_texture);\n    readback_buffers.copy_from(&ctx.device, &mut encoder, &output_texture);\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    readback_buffers.assert_buffer_contents(&ctx, &image).await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/binding_array/tlas.rs",
    "content": "use std::{borrow::Cow, num::NonZeroU32};\n\nuse wgpu::util::DeviceExt;\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.push(BINDING_ARRAY_TLAS);\n}\n\n#[gpu_test]\nstatic BINDING_ARRAY_TLAS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            // Ray queries + acceleration structure bindings are gated behind this experimental feature.\n            .features(\n                Features::EXPERIMENTAL_RAY_QUERY | Features::ACCELERATION_STRUCTURE_BINDING_ARRAY,\n            )\n            .limits(Limits {\n                max_binding_array_elements_per_shader_stage: 8,\n                max_acceleration_structures_per_shader_stage: 8,\n                max_binding_array_acceleration_structure_elements_per_shader_stage: 8,\n                ..Limits::default().using_minimum_supported_acceleration_structure_values()\n            }),\n    )\n    .run_async(|ctx| async move { binding_array_tlas(ctx).await });\n\nasync fn binding_array_tlas(ctx: TestingContext) {\n    // Minimal shader that consumes a TLAS binding array.\n    //\n    // We don't need to actually \"trace\" anything for this test. We only need:\n    // - Pipeline compilation to accept `binding_array<acceleration_structure>`\n    // - Bind group creation to accept `BindingResource::AccelerationStructureArray`\n    // - Encoder to successfully set the bind group and submit.\n    //\n    // Creating a `ray_query` and initializing it against element 0 forces the binding to be used.\n    let shader = r#\"\n        enable wgpu_ray_query;\n\n        @group(0) @binding(0)\n        var tlas_array: binding_array<acceleration_structure>;\n\n        @compute\n        @workgroup_size(1, 1, 1)\n        fn main() {\n            var rq: ray_query;\n            rayQueryInitialize(\n                &rq,\n                tlas_array[0],\n                RayDesc(\n                    0u,\n                    0xffu,\n                    0.001,\n                    1000.0,\n                    vec3f(0.0, 0.0, 0.0),\n                    vec3f(0.0, 0.0, 1.0)\n                )\n            );\n        }\n    \"#;\n\n    let module = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"Binding Array TLAS\"),\n        source: ShaderSource::Wgsl(Cow::Borrowed(shader)),\n    });\n\n    // Build a minimal BLAS + two TLAS so we can bind an array of TLAS.\n    //\n    // This follows the shapes used in the ray tracing examples.\n    let vertex_data: [[f32; 3]; 3] = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];\n    let index_data: [u16; 3] = [0, 1, 2];\n\n    let vertex_buf = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"RT Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&vertex_data),\n            usage: BufferUsages::VERTEX | BufferUsages::BLAS_INPUT,\n        });\n\n    let index_buf = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"RT Index Buffer\"),\n            contents: bytemuck::cast_slice(&index_data),\n            usage: BufferUsages::INDEX | BufferUsages::BLAS_INPUT,\n        });\n\n    let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n        vertex_format: wgpu::VertexFormat::Float32x3,\n        vertex_count: vertex_data.len() as u32,\n        index_format: Some(wgpu::IndexFormat::Uint16),\n        index_count: Some(index_data.len() as u32),\n        flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n    };\n\n    let blas = ctx.device.create_blas(\n        &wgpu::CreateBlasDescriptor {\n            label: Some(\"BLAS\"),\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n        },\n        wgpu::BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_geo_size_desc.clone()],\n        },\n    );\n\n    let mut tlas_a = ctx.device.create_tlas(&wgpu::CreateTlasDescriptor {\n        label: Some(\"TLAS A\"),\n        flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n        update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n        max_instances: 1,\n    });\n\n    let mut tlas_b = ctx.device.create_tlas(&wgpu::CreateTlasDescriptor {\n        label: Some(\"TLAS B\"),\n        flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n        update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n        max_instances: 1,\n    });\n\n    // Put a single instance into each TLAS. Both reference the same BLAS.\n    //\n    // NOTE: This indexing API is how TLAS instances are populated in the examples.\n    tlas_a[0] = Some(wgpu::TlasInstance::new(\n        &blas,\n        [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],\n        0,\n        0xff,\n    ));\n    tlas_b[0] = Some(wgpu::TlasInstance::new(\n        &blas,\n        [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],\n        0,\n        0xff,\n    ));\n\n    // Build BLAS and TLASes.\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"RT Build\"),\n        });\n\n    encoder.build_acceleration_structures(\n        std::iter::once(&wgpu::BlasBuildEntry {\n            blas: &blas,\n            geometry: wgpu::BlasGeometries::TriangleGeometries(vec![wgpu::BlasTriangleGeometry {\n                size: &blas_geo_size_desc,\n                vertex_buffer: &vertex_buf,\n                first_vertex: 0,\n                vertex_stride: std::mem::size_of::<[f32; 3]>() as u64,\n                index_buffer: Some(&index_buf),\n                first_index: Some(0),\n                transform_buffer: None,\n                transform_buffer_offset: None,\n            }]),\n        }),\n        [&tlas_a, &tlas_b],\n    );\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    // Bind group layout with a TLAS array binding.\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: Some(\"TLAS array BGL\"),\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::COMPUTE,\n                ty: BindingType::AccelerationStructure {\n                    vertex_return: false,\n                },\n                count: Some(NonZeroU32::new(2).unwrap()),\n            }],\n        });\n\n    let tlas_refs: [&Tlas; 2] = [&tlas_a, &tlas_b];\n\n    let bg = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"TLAS array BG\"),\n        layout: &bgl,\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::AccelerationStructureArray(&tlas_refs),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(\"TLAS array pipeline layout\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: Some(\"TLAS array pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"Dispatch\"),\n        });\n\n    {\n        let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: Some(\"Compute pass\"),\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bg, &[]);\n        pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    ctx.queue.submit(Some(encoder.finish()));\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/buffer.rs",
    "content": "use wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        EMPTY_BUFFER,\n        MAP_OFFSET,\n        MINIMUM_BUFFER_BINDING_SIZE_LAYOUT,\n        MINIMUM_BUFFER_BINDING_SIZE_DISPATCH,\n        CLEAR_OFFSET_OUTSIDE_RESOURCE_BOUNDS,\n        CLEAR_OFFSET_PLUS_SIZE_OUTSIDE_U64_BOUNDS,\n    ]);\n}\n\nasync fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) {\n    let r = wgpu::BufferUsages::MAP_READ;\n    let rw = wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::MAP_WRITE;\n    for usage in [r, rw] {\n        let b0 = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(label),\n            size: buffer_size,\n            usage,\n            mapped_at_creation: false,\n        });\n\n        b0.slice(0..0)\n            .map_async(wgpu::MapMode::Read, Result::unwrap);\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        {\n            let view = b0.slice(0..0).get_mapped_range();\n            assert!(view.is_empty());\n        }\n\n        b0.unmap();\n\n        // Map and unmap right away.\n        b0.slice(0..0).map_async(wgpu::MapMode::Read, move |_| {});\n        b0.unmap();\n\n        // Map multiple times before unmapping.\n        b0.slice(0..0).map_async(wgpu::MapMode::Read, move |_| {});\n        b0.slice(0..0)\n            .map_async(wgpu::MapMode::Read, move |result| {\n                assert!(result.is_err());\n            });\n        b0.slice(0..0)\n            .map_async(wgpu::MapMode::Read, move |result| {\n                assert!(result.is_err());\n            });\n        b0.slice(0..0)\n            .map_async(wgpu::MapMode::Read, move |result| {\n                assert!(result.is_err());\n            });\n        b0.unmap();\n\n        // Write mode.\n        if usage == rw {\n            b0.slice(0..0)\n                .map_async(wgpu::MapMode::Write, Result::unwrap);\n\n            ctx.async_poll(wgpu::PollType::wait_indefinitely())\n                .await\n                .unwrap();\n\n            //{\n            //    let view = b0.slice(0..0).get_mapped_range_mut();\n            //    assert!(view.is_empty());\n            //}\n\n            b0.unmap();\n\n            // Map and unmap right away.\n            b0.slice(0..0).map_async(wgpu::MapMode::Write, move |_| {});\n            b0.unmap();\n        }\n    }\n\n    let b1 = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(label),\n        size: buffer_size,\n        usage: rw,\n        mapped_at_creation: true,\n    });\n\n    {\n        let view = b1.slice(0..0).get_mapped_range_mut();\n        assert_eq!(view.len(), 0);\n    }\n\n    b1.unmap();\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n}\n\n#[gpu_test]\nstatic EMPTY_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .expect_fail(FailureCase::always())\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        test_empty_buffer_range(&ctx, 2048, \"regular buffer\").await;\n        test_empty_buffer_range(&ctx, 0, \"zero-sized buffer\").await;\n    });\n\n#[gpu_test]\nstatic MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        // This test writes 16 bytes at the beginning of buffer mapped mapped with\n        // an offset of 32 bytes. Then the buffer is copied into another buffer that\n        // is read back and we check that the written bytes are correctly placed at\n        // offset 32..48.\n        // The goal is to check that get_mapped_range did not accidentally double-count\n        // the mapped offset.\n\n        let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        write_buf\n            .slice(32..)\n            .map_async(wgpu::MapMode::Write, move |result| {\n                result.unwrap();\n            });\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        {\n            let slice = write_buf.slice(32..48);\n            let mut view = slice.get_mapped_range_mut();\n            for byte in view.slice(..) {\n                byte.write(2);\n            }\n        }\n\n        write_buf.unmap();\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256);\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        read_buf\n            .slice(..)\n            .map_async(wgpu::MapMode::Read, Result::unwrap);\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        let slice = read_buf.slice(..);\n        let view = slice.get_mapped_range();\n        for byte in &view[0..32] {\n            assert_eq!(*byte, 0);\n        }\n        for byte in &view[32..48] {\n            assert_eq!(*byte, 2);\n        }\n        for byte in &view[48..] {\n            assert_eq!(*byte, 0);\n        }\n    });\n\n/// The WebGPU algorithm [validating shader binding][vsb] requires\n/// implementations to check that buffer bindings are large enough to\n/// hold the WGSL `storage` or `uniform` variables they're bound to.\n///\n/// This test tries to build a pipeline from a shader module with a\n/// 32-byte variable and a bindgroup layout with a min_binding_size of\n/// 16 for that variable's group/index. Pipeline creation should fail.\n#[gpu_test]\nstatic MINIMUM_BUFFER_BINDING_SIZE_LAYOUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().test_features_limits().enable_noop())\n    .run_sync(|ctx| {\n        // Create a shader module that statically uses a storage buffer.\n        let shader_module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                    r#\"\n                        @group(0) @binding(0)\n                        var<storage, read_write> a: array<u32, 8>;\n                        @compute @workgroup_size(1)\n                        fn main() {\n                            a[0] = a[1];\n                        }\n            \"#,\n                )),\n            });\n\n        let bind_group_layout =\n            ctx.device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: std::num::NonZeroU64::new(16),\n                        },\n                        count: None,\n                    }],\n                });\n\n        let pipeline_layout = ctx\n            .device\n            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        wgpu_test::fail(\n            &ctx.device,\n            || {\n                let _ = ctx.device\n                    .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                        label: None,\n                        layout: Some(&pipeline_layout),\n                        module: &shader_module,\n                        entry_point: Some(\"main\"),\n                        compilation_options: Default::default(),\n                        cache: None,\n                    });\n            },\n            Some(\"shader global resourcebinding { group: 0, binding: 0 } is not available in the pipeline layout\"),\n        );\n    });\n\n/// The WebGPU algorithm [validating shader binding][vsb] requires\n/// implementations to check that buffer bindings are large enough to\n/// hold the WGSL `storage` or `uniform` variables they're bound to.\n///\n/// This test tries to dispatch a compute shader that uses a 32-byte\n/// variable with a bindgroup layout with a min_binding_size of zero\n/// (meaning, \"validate at dispatch recording time\") and a 16-byte\n/// binding. Command recording should fail.\n#[gpu_test]\nstatic MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().test_features_limits().enable_noop())\n    .run_sync(|ctx| {\n        // This test tries to use a bindgroup layout with a\n        // min_binding_size of 16 to an index whose WGSL type requires 32\n        // bytes. Pipeline creation should fail.\n\n        // Create a shader module that statically uses a storage buffer.\n        let shader_module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                    r#\"\n                        @group(0) @binding(0)\n                        var<storage, read_write> a: array<u32, 8>;\n                        @compute @workgroup_size(1)\n                        fn main() {\n                            a[0] = a[1];\n                        }\n            \"#,\n                )),\n            });\n\n        let bind_group_layout =\n            ctx.device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: None,\n                        },\n                        count: None,\n                    }],\n                });\n\n        let pipeline_layout = ctx\n            .device\n            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        let pipeline = ctx\n            .device\n            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                label: None,\n                layout: Some(&pipeline_layout),\n                module: &shader_module,\n                entry_point: Some(\"main\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 16, // too small for 32-byte var `a` in shader module\n            usage: wgpu::BufferUsages::STORAGE,\n            mapped_at_creation: false,\n        });\n\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: buffer.as_entire_binding(),\n            }],\n        });\n\n        wgpu_test::fail(\n            &ctx.device,\n            || {\n                let mut encoder = ctx.device.create_command_encoder(&Default::default());\n\n                let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                    label: None,\n                    timestamp_writes: None,\n                });\n\n                pass.set_bind_group(0, &bind_group, &[]);\n                pass.set_pipeline(&pipeline);\n                pass.dispatch_workgroups(1, 1, 1);\n\n                drop(pass);\n                let _ = encoder.finish();\n            },\n            Some(\"In bind group index 0, the buffer bound at binding index 0 is bound with size 16 where the shader expects 32\"),\n        );\n    });\n\n#[gpu_test]\nstatic CLEAR_OFFSET_OUTSIDE_RESOURCE_BOUNDS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let size = 16;\n\n        let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size,\n            usage: wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let out_of_bounds = size.checked_add(wgpu::COPY_BUFFER_ALIGNMENT).unwrap();\n\n        let mut encoder = ctx.device.create_command_encoder(&Default::default());\n        encoder.clear_buffer(&buffer, out_of_bounds, None);\n\n        wgpu_test::fail(\n            &ctx.device,\n            || encoder.finish(),\n            Some(\"Clear of 20..20 would end up overrunning the bounds of the buffer of size 16\"),\n        );\n    });\n\n#[gpu_test]\nstatic CLEAR_OFFSET_PLUS_SIZE_OUTSIDE_U64_BOUNDS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size: 16, // unimportant for this test\n                usage: wgpu::BufferUsages::COPY_DST,\n                mapped_at_creation: false,\n            });\n\n            let max_valid_offset = u64::MAX - (u64::MAX % wgpu::COPY_BUFFER_ALIGNMENT);\n            let smallest_aligned_invalid_size = wgpu::COPY_BUFFER_ALIGNMENT;\n\n            let mut encoder = ctx.device.create_command_encoder(&Default::default());\n            encoder.clear_buffer(\n                &buffer,\n                max_valid_offset,\n                Some(smallest_aligned_invalid_size),\n            );\n\n            wgpu_test::fail(\n                &ctx.device,\n                || encoder.finish(),\n                Some(concat!(\n                    \"Clear starts at offset 18446744073709551612 with size of 4, \",\n                    \"but these added together exceed `u64::MAX`\"\n                )),\n            );\n        });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/buffer_copy.rs",
    "content": "//! Tests for buffer copy validation.\n\nuse wgpu::BufferAddress;\n\nuse wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(COPY_ALIGNMENT);\n}\n\nfn try_copy(\n    ctx: &wgpu_test::TestingContext,\n    offset: BufferAddress,\n    size: BufferAddress,\n    error_message: Option<&'static str>,\n) {\n    let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR);\n    let data = vec![255; size as usize];\n\n    fail_if(\n        &ctx.device,\n        error_message.is_some(),\n        || ctx.queue.write_buffer(&buffer, offset, &data),\n        error_message,\n    );\n}\n\n#[gpu_test]\nstatic COPY_ALIGNMENT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        try_copy(&ctx, 0, 0, None);\n        try_copy(\n            &ctx,\n            4,\n            16 + 1,\n            Some(\"copy size 17 does not respect `copy_buffer_alignment`\"),\n        );\n        try_copy(\n            &ctx,\n            64,\n            20 + 2,\n            Some(\"copy size 22 does not respect `copy_buffer_alignment`\"),\n        );\n        try_copy(\n            &ctx,\n            256,\n            44 + 3,\n            Some(\"copy size 47 does not respect `copy_buffer_alignment`\"),\n        );\n        try_copy(&ctx, 1024, 8 + 4, None);\n\n        try_copy(&ctx, 0, 4, None);\n        try_copy(\n            &ctx,\n            4 + 1,\n            8,\n            Some(\"buffer offset 5 is not aligned to block size or `copy_buffer_alignment`\"),\n        );\n        try_copy(\n            &ctx,\n            64 + 2,\n            12,\n            Some(\"buffer offset 66 is not aligned to block size or `copy_buffer_alignment`\"),\n        );\n        try_copy(\n            &ctx,\n            256 + 3,\n            16,\n            Some(\"buffer offset 259 is not aligned to block size or `copy_buffer_alignment`\"),\n        );\n        try_copy(&ctx, 1024 + 4, 4, None);\n    });\n\nconst BUFFER_SIZE: BufferAddress = 1234;\n\nconst BUFFER_DESCRIPTOR: wgpu::BufferDescriptor = wgpu::BufferDescriptor {\n    label: None,\n    size: BUFFER_SIZE,\n    usage: wgpu::BufferUsages::COPY_SRC.union(wgpu::BufferUsages::COPY_DST),\n    mapped_at_creation: false,\n};\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/buffer_usages.rs",
    "content": "//! Tests for buffer usages validation.\n\nuse wgpu::BufferAddress;\nuse wgpu::{BufferUsages as Bu, MapMode as Ma};\nuse wgpu_test::{\n    fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        BUFFER_USAGE,\n        BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS,\n        BUFFER_MAP_ASYNC_MAP_STATE,\n    ]);\n}\n\nconst BUFFER_SIZE: BufferAddress = 1234;\n\nconst ALWAYS_VALID: &[Bu; 4] = &[\n    Bu::MAP_READ,\n    Bu::MAP_WRITE,\n    Bu::MAP_READ.union(Bu::COPY_DST),\n    Bu::MAP_WRITE.union(Bu::COPY_SRC),\n];\n// MAP_READ can only be paired with COPY_DST and MAP_WRITE can only be paired with COPY_SRC\n// (unless Features::MAPPABlE_PRIMARY_BUFFERS is enabled).\nconst NEEDS_MAPPABLE_PRIMARY_BUFFERS: &[Bu; 7] = &[\n    Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::COPY_SRC)),\n    Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::COPY_DST)),\n    Bu::MAP_READ.union(Bu::MAP_WRITE),\n    Bu::MAP_WRITE.union(Bu::MAP_READ),\n    Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::STORAGE)),\n    Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::STORAGE)),\n    // these two require acceleration_structures feature\n    Bu::all().intersection(Bu::BLAS_INPUT.union(Bu::TLAS_INPUT).complement()),\n];\nconst INVALID_BITS: Bu = Bu::from_bits_retain(0b1111111111111);\nconst ALWAYS_FAIL: &[Bu; 2] = &[Bu::empty(), INVALID_BITS];\n\nfn try_create(ctx: TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) {\n    for (expect_validation_error, usage) in usages\n        .iter()\n        .flat_map(|&(expect_error, usages)| usages.iter().copied().map(move |u| (expect_error, u)))\n    {\n        fail_if(\n            &ctx.device,\n            expect_validation_error,\n            || {\n                let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                    label: None,\n                    size: BUFFER_SIZE,\n                    usage,\n                    mapped_at_creation: false,\n                });\n            },\n            None,\n        );\n    }\n}\n\n#[gpu_test]\nstatic BUFFER_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        try_create(\n            ctx,\n            &[\n                (false, ALWAYS_VALID),\n                (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS),\n                (true, ALWAYS_FAIL),\n            ],\n        );\n    });\n\n#[gpu_test]\nstatic BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        try_create(\n            ctx,\n            &[\n                (false, ALWAYS_VALID),\n                (false, NEEDS_MAPPABLE_PRIMARY_BUFFERS),\n                (true, ALWAYS_FAIL),\n            ],\n        );\n    });\n\nasync fn map_test(\n    ctx: &TestingContext,\n    usage_type: &str,\n    map_mode_type: Ma,\n    before_unmap: bool,\n    before_destroy: bool,\n    after_unmap: bool,\n    after_destroy: bool,\n) {\n    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}\");\n\n    let size = 8;\n    let usage = match usage_type {\n        \"read\" => Bu::COPY_DST | Bu::MAP_READ,\n        \"write\" => Bu::COPY_SRC | Bu::MAP_WRITE,\n        _ => Bu::from_bits(0).unwrap(),\n    };\n    let buffer_creation_validation_error = usage.is_empty();\n\n    let mut buffer = None;\n\n    fail_if(\n        &ctx.device,\n        buffer_creation_validation_error,\n        || {\n            buffer = Some(ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size,\n                usage,\n                mapped_at_creation: false,\n            }));\n        },\n        None,\n    );\n    if buffer_creation_validation_error {\n        return;\n    }\n\n    let buffer = buffer.unwrap();\n\n    let map_async_validation_error = buffer_creation_validation_error\n        || (map_mode_type == Ma::Read && !usage.contains(Bu::MAP_READ))\n        || (map_mode_type == Ma::Write && !usage.contains(Bu::MAP_WRITE));\n\n    fail_if(\n        &ctx.device,\n        map_async_validation_error,\n        || {\n            buffer.slice(0..size).map_async(map_mode_type, |_| {});\n        },\n        None,\n    );\n\n    if map_async_validation_error {\n        return;\n    }\n\n    if before_unmap {\n        buffer.unmap();\n    }\n\n    if before_destroy {\n        buffer.destroy();\n    }\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    if !before_unmap && !before_destroy {\n        {\n            let view = buffer.slice(0..size).get_mapped_range();\n            assert!(!view.is_empty());\n        }\n\n        if after_unmap {\n            buffer.unmap();\n        }\n\n        if after_destroy {\n            buffer.destroy();\n        }\n    }\n}\n\n#[gpu_test]\nstatic BUFFER_MAP_ASYNC_MAP_STATE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS)\n            .enable_noop(),\n    )\n    .run_async(move |ctx| async move {\n        for usage_type in [\"invalid\", \"read\", \"write\"] {\n            for map_mode_type in [Ma::Read, Ma::Write] {\n                for before_unmap in [false, true] {\n                    for before_destroy in [false, true] {\n                        for after_unmap in [false, true] {\n                            for after_destroy in [false, true] {\n                                map_test(\n                                    &ctx,\n                                    usage_type,\n                                    map_mode_type,\n                                    before_unmap,\n                                    before_destroy,\n                                    after_unmap,\n                                    after_destroy,\n                                )\n                                .await\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/clear_texture.rs",
    "content": "use wgpu_test::{\n    gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, GpuTestInitializer,\n    TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        CLEAR_TEXTURE_UNCOMPRESSED_GLES,\n        CLEAR_TEXTURE_UNCOMPRESSED,\n        CLEAR_TEXTURE_DEPTH,\n        CLEAR_TEXTURE_DEPTH32_STENCIL8,\n        CLEAR_TEXTURE_COMPRESSED_BCN,\n        CLEAR_TEXTURE_COMPRESSED_ASTC,\n        CLEAR_TEXTURE_COMPRESSED_ETC2,\n    ]);\n}\n\nstatic TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::R8Unorm,\n    wgpu::TextureFormat::R8Snorm,\n    wgpu::TextureFormat::R8Uint,\n    wgpu::TextureFormat::R8Sint,\n    wgpu::TextureFormat::R16Uint,\n    wgpu::TextureFormat::R16Sint,\n    wgpu::TextureFormat::R16Float,\n    wgpu::TextureFormat::R32Uint,\n    wgpu::TextureFormat::R32Sint,\n    wgpu::TextureFormat::R32Float,\n    wgpu::TextureFormat::Rg16Uint,\n    wgpu::TextureFormat::Rg16Sint,\n    wgpu::TextureFormat::Rg16Float,\n    wgpu::TextureFormat::Rgba8Unorm,\n    wgpu::TextureFormat::Rgba8UnormSrgb,\n    wgpu::TextureFormat::Rgba8Snorm,\n    wgpu::TextureFormat::Rgba8Uint,\n    wgpu::TextureFormat::Rgba8Sint,\n    wgpu::TextureFormat::Bgra8Unorm,\n    wgpu::TextureFormat::Bgra8UnormSrgb,\n    wgpu::TextureFormat::Rgb10a2Uint,\n    wgpu::TextureFormat::Rgb10a2Unorm,\n    wgpu::TextureFormat::Rg11b10Ufloat,\n    wgpu::TextureFormat::Rg32Uint,\n    wgpu::TextureFormat::Rg32Sint,\n    wgpu::TextureFormat::Rg32Float,\n    wgpu::TextureFormat::Rgba16Uint,\n    wgpu::TextureFormat::Rgba16Sint,\n    wgpu::TextureFormat::Rgba16Float,\n    wgpu::TextureFormat::Rgba32Uint,\n    wgpu::TextureFormat::Rgba32Sint,\n    wgpu::TextureFormat::Rgba32Float,\n];\n\nstatic TEXTURE_FORMATS_UNCOMPRESSED: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::Rg8Unorm,\n    wgpu::TextureFormat::Rg8Snorm,\n    wgpu::TextureFormat::Rg8Uint,\n    wgpu::TextureFormat::Rg8Sint,\n    wgpu::TextureFormat::Rgb9e5Ufloat,\n];\n\nstatic TEXTURE_FORMATS_DEPTH: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::Stencil8,\n    wgpu::TextureFormat::Depth16Unorm,\n    wgpu::TextureFormat::Depth24Plus,\n    wgpu::TextureFormat::Depth24PlusStencil8,\n    wgpu::TextureFormat::Depth32Float,\n];\n\n// needs TEXTURE_COMPRESSION_BC\nstatic TEXTURE_FORMATS_BC: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::Bc1RgbaUnorm,\n    wgpu::TextureFormat::Bc1RgbaUnormSrgb,\n    wgpu::TextureFormat::Bc2RgbaUnorm,\n    wgpu::TextureFormat::Bc2RgbaUnormSrgb,\n    wgpu::TextureFormat::Bc3RgbaUnorm,\n    wgpu::TextureFormat::Bc3RgbaUnormSrgb,\n    wgpu::TextureFormat::Bc4RUnorm,\n    wgpu::TextureFormat::Bc4RSnorm,\n    wgpu::TextureFormat::Bc5RgUnorm,\n    wgpu::TextureFormat::Bc5RgSnorm,\n    wgpu::TextureFormat::Bc6hRgbUfloat,\n    wgpu::TextureFormat::Bc6hRgbFloat,\n    wgpu::TextureFormat::Bc7RgbaUnorm,\n    wgpu::TextureFormat::Bc7RgbaUnormSrgb,\n];\n\n// needs TEXTURE_COMPRESSION_ETC2\nstatic TEXTURE_FORMATS_ETC2: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::Etc2Rgb8Unorm,\n    wgpu::TextureFormat::Etc2Rgb8UnormSrgb,\n    wgpu::TextureFormat::Etc2Rgb8A1Unorm,\n    wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb,\n    wgpu::TextureFormat::Etc2Rgba8Unorm,\n    wgpu::TextureFormat::Etc2Rgba8UnormSrgb,\n    wgpu::TextureFormat::EacR11Unorm,\n    wgpu::TextureFormat::EacR11Snorm,\n    wgpu::TextureFormat::EacRg11Unorm,\n    wgpu::TextureFormat::EacRg11Snorm,\n];\n\n// needs TEXTURE_COMPRESSION_ASTC\nuse wgpu::{AstcBlock, AstcChannel};\nstatic TEXTURE_FORMATS_ASTC: &[wgpu::TextureFormat] = &[\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B4x4,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B5x4,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B5x5,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B6x5,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B6x6,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x5,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x6,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x8,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x5,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x6,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x8,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x10,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B12x10,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B12x12,\n        channel: AstcChannel::Unorm,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B4x4,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B5x4,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B5x5,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B6x5,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B6x6,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x5,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x6,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B8x8,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x5,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x6,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x8,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B10x10,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B12x10,\n        channel: AstcChannel::UnormSrgb,\n    },\n    wgpu::TextureFormat::Astc {\n        block: AstcBlock::B12x12,\n        channel: AstcChannel::UnormSrgb,\n    },\n];\n\nasync fn single_texture_clear_test(\n    ctx: &TestingContext,\n    format: wgpu::TextureFormat,\n    size: wgpu::Extent3d,\n    dimension: wgpu::TextureDimension,\n) {\n    log::info!(\"clearing texture with {format:?}, dimension {dimension:?}, size {size:?}\");\n\n    let extra_usages = match format {\n        wgpu::TextureFormat::Depth24Plus | wgpu::TextureFormat::Depth24PlusStencil8 => {\n            wgpu::TextureUsages::TEXTURE_BINDING\n        }\n        _ => wgpu::TextureUsages::empty(),\n    };\n\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(&format!(\"texture {format:?}\")),\n        size,\n        mip_level_count: if dimension == wgpu::TextureDimension::D1 {\n            1\n        } else {\n            // arbitrary value between 2 and max\n            3\n        },\n        sample_count: 1, // multisampling is not supported for clear\n        dimension,\n        format,\n        usage: wgpu::TextureUsages::COPY_SRC | extra_usages,\n        view_formats: &[],\n    });\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    encoder.clear_texture(\n        &texture,\n        &wgpu::ImageSubresourceRange {\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: None,\n        },\n    );\n\n    let readback_buffers = ReadbackBuffers::new(&ctx.device, &texture);\n\n    readback_buffers.copy_from(&ctx.device, &mut encoder, &texture);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    assert!(\n        readback_buffers.are_zero(ctx).await,\n        \"texture with format {format:?} was not fully cleared\"\n    );\n}\n\nasync fn clear_texture_tests(ctx: TestingContext, formats: &'static [wgpu::TextureFormat]) {\n    for &format in formats {\n        let (block_width, block_height) = format.block_dimensions();\n        let rounded_width = block_width * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;\n        let rounded_height = block_height * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;\n\n        let is_compressed_or_depth_stencil_format =\n            format.is_compressed() || format.is_depth_stencil_format();\n        let supports_1d = !is_compressed_or_depth_stencil_format;\n        let supports_3d = format.is_bcn() || !is_compressed_or_depth_stencil_format;\n\n        // 1D texture\n        if supports_1d {\n            single_texture_clear_test(\n                &ctx,\n                format,\n                wgpu::Extent3d {\n                    width: rounded_width,\n                    height: 1,\n                    depth_or_array_layers: 1,\n                },\n                wgpu::TextureDimension::D1,\n            )\n            .await;\n        }\n        // 2D texture\n        single_texture_clear_test(\n            &ctx,\n            format,\n            wgpu::Extent3d {\n                width: rounded_width,\n                height: rounded_height,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureDimension::D2,\n        )\n        .await;\n        // 2D array texture\n        single_texture_clear_test(\n            &ctx,\n            format,\n            wgpu::Extent3d {\n                width: rounded_width,\n                height: rounded_height,\n                depth_or_array_layers: 4,\n            },\n            wgpu::TextureDimension::D2,\n        )\n        .await;\n        if supports_3d {\n            // volume texture\n            single_texture_clear_test(\n                &ctx,\n                format,\n                wgpu::Extent3d {\n                    width: rounded_width,\n                    height: rounded_height,\n                    depth_or_array_layers: 16,\n                },\n                wgpu::TextureDimension::D3,\n            )\n            .await;\n        }\n    }\n}\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLEAR_TEXTURE)\n            .skip(FailureCase::webgl2()),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .expect_fail(\n                FailureCase::backend(wgpu::Backends::GL)\n                    .panic(\"texture with format Rg8Snorm was not fully cleared\")\n                    .panic(\"texture with format Rgb9e5Ufloat was not fully cleared\")\n                    .validation_error(\"GL_INVALID_FRAMEBUFFER_OPERATION\")\n                    .validation_error(\"GL_INVALID_OPERATION\"),\n            )\n            .features(wgpu::Features::CLEAR_TEXTURE),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_UNCOMPRESSED));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(\n                wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES\n                    | wgpu::DownlevelFlags::COMPUTE_SHADERS,\n            )\n            // https://github.com/gfx-rs/wgpu/issues/5016\n            .skip(FailureCase::adapter(\"Apple Paravirtual device\"))\n            .skip(FailureCase::webgl2())\n            .limits(wgpu::Limits::downlevel_defaults())\n            .features(wgpu::Features::CLEAR_TEXTURE),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_DEPTH));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_DEPTH32_STENCIL8: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8)\n            .downlevel_flags(wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)\n            // https://github.com/gfx-rs/wgpu/issues/5016\n            .skip(FailureCase::adapter(\"Apple Paravirtual device\")),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_COMPRESSED_BCN: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(\n                wgpu::Features::CLEAR_TEXTURE\n                    | wgpu::Features::TEXTURE_COMPRESSION_BC\n                    | wgpu::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,\n            )\n            .limits(wgpu::Limits {\n                max_texture_dimension_3d: 1024,\n                ..wgpu::Limits::downlevel_defaults()\n            })\n            // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056\n            .expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, \"ANGLE\"))\n            // compressed texture copy to buffer not yet implemented\n            .expect_fail(FailureCase::backend(wgpu::Backends::GL)),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_BC));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_COMPRESSED_ASTC: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(\n                wgpu::Features::CLEAR_TEXTURE\n                    | wgpu::Features::TEXTURE_COMPRESSION_ASTC\n                    | wgpu::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,\n            )\n            .limits(wgpu::Limits {\n                max_texture_dimension_2d: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT * 12,\n                max_texture_dimension_3d: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT * 12,\n                ..wgpu::Limits::downlevel_defaults()\n            })\n            // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056\n            .expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, \"ANGLE\"))\n            // compressed texture copy to buffer not yet implemented\n            .expect_fail(FailureCase::backend(wgpu::Backends::GL)),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_ASTC));\n\n#[gpu_test]\nstatic CLEAR_TEXTURE_COMPRESSED_ETC2: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ETC2)\n            // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056\n            .expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, \"ANGLE\"))\n            // compressed texture copy to buffer not yet implemented\n            .expect_fail(FailureCase::backend(wgpu::Backends::GL)),\n    )\n    .run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_ETC2));\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/clip_distances.rs",
    "content": "use wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(CLIP_DISTANCES);\n}\n\n#[gpu_test]\nstatic CLIP_DISTANCES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLIP_DISTANCES)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(clip_distances);\n\nasync fn clip_distances(ctx: TestingContext) {\n    // Create pipeline\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                buffers: &[],\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::R8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    // Create render target\n    let render_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Render Texture\"),\n        size: wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    // Perform render\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color {\n                        r: 0.0,\n                        g: 0.0,\n                        b: 0.0,\n                        a: 0.0,\n                    }),\n                    store: wgpu::StoreOp::Store,\n                },\n                resolve_target: None,\n                view: &render_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        rpass.set_pipeline(&pipeline);\n        rpass.draw(0..3, 0..1);\n    }\n\n    // Read texture data\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 * 256,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &render_texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &readback_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(256),\n                rows_per_image: None,\n            },\n        },\n        wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n    );\n    ctx.queue.submit([encoder.finish()]);\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n    let data: &[u8] = &slice.get_mapped_range();\n\n    // We should have filled the upper sector of the texture. Verify that this is the case.\n    assert_eq!(data[128 + 64 * 256], 0xFF);\n    assert_eq!(data[64 + 128 * 256], 0x00);\n    assert_eq!(data[192 + 128 * 256], 0x00);\n    assert_eq!(data[128 + 192 * 256], 0x00);\n}\n\nconst SHADER_SRC: &str = \"\nenable clip_distances;\nstruct VertexOutput {\n    @builtin(position) pos: vec4f,\n    @builtin(clip_distances) clip_distances: array<f32, 2>,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var out: VertexOutput;\n    let x = f32(i32(vertex_index) / 2) * 4.0 - 1.0;\n    let y = f32(i32(vertex_index) & 1) * 4.0 - 1.0;\n    out.pos = vec4f(x, y, 0.5, 1.0);\n    out.clip_distances[0] = x + y;\n    out.clip_distances[1] = y - x;\n    return out;\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4f {\n    return vec4f(1.0);\n}\n\";\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/cloneable_types.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestInitializer, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(CLONEABLE_BUFFERS);\n}\n\n#[gpu_test]\nstatic CLONEABLE_BUFFERS: GpuTestConfiguration = wgpu_test::GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(cloneable_buffers);\n\n// Test a basic case of cloneable types where you clone the buffer to be able\n// to access the buffer inside the callback as well as outside.\nfn cloneable_buffers(ctx: TestingContext) {\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 32,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: true,\n    });\n\n    let buffer_contents: Vec<u8> = (0..32).collect();\n\n    buffer\n        .slice(..)\n        .get_mapped_range_mut()\n        .copy_from_slice(&buffer_contents);\n\n    buffer.unmap();\n\n    // This is actually a bug, we should not need to call submit to make the buffer contents visible.\n    ctx.queue.submit([]);\n\n    let cloned_buffer = buffer.clone();\n    let cloned_buffer_contents = buffer_contents.clone();\n\n    buffer.slice(..).map_async(wgpu::MapMode::Read, move |_| {\n        let data = cloned_buffer.slice(..).get_mapped_range();\n\n        assert_eq!(&*data, &cloned_buffer_contents);\n    });\n\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    let data = buffer.slice(..).get_mapped_range();\n\n    assert_eq!(&*data, &buffer_contents);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/compute_pass_ownership.rs",
    "content": "//! Tests that compute passes take ownership of resources that are associated with.\n//! I.e. once a resource is passed in to a compute pass, it can be dropped.\n\nuse std::num::NonZeroU64;\n\nuse wgpu::util::DeviceExt as _;\nuse wgpu_test::{\n    gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        COMPUTE_PASS_RESOURCE_OWNERSHIP,\n        COMPUTE_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS,\n        COMPUTE_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS,\n        COMPUTE_PASS_KEEP_ENCODER_ALIVE,\n    ]);\n}\n\nconst SHADER_SRC: &str = \"\n@group(0) @binding(0)\nvar<storage, read_write> buffer: array<vec4f>;\n\n@compute @workgroup_size(1, 1, 1) fn main() {\n    buffer[0] *= 2.0;\n}\n\";\n\n#[gpu_test]\nstatic COMPUTE_PASS_RESOURCE_OWNERSHIP: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            // https://github.com/gfx-rs/wgpu/issues/5800\n            .skip(wgpu_test::FailureCase::backend_adapter(\n                wgpu::Backends::GL,\n                \"AMD Radeon Pro WX 3200\",\n            )),\n    )\n    .run_async(compute_pass_resource_ownership);\n\nasync fn compute_pass_resource_ownership(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        indirect_buffer,\n        bind_group,\n        pipeline,\n    } = resource_setup(&ctx);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, &bind_group, &[]);\n        cpass.dispatch_workgroups_indirect(&indirect_buffer, 0);\n\n        // Now drop all resources we set. Then do a device poll to make sure the resources are really not dropped too early, no matter what.\n        drop(pipeline);\n        drop(bind_group);\n        drop(indirect_buffer);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic COMPUTE_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .features(wgpu::Features::PIPELINE_STATISTICS_QUERY),\n        )\n        .run_async(compute_pass_query_set_ownership_pipeline_statistics);\n\nasync fn compute_pass_query_set_ownership_pipeline_statistics(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        indirect_buffer: _,\n        bind_group,\n        pipeline,\n    } = resource_setup(&ctx);\n\n    let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set\"),\n        ty: wgpu::QueryType::PipelineStatistics(\n            wgpu::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS,\n        ),\n        count: 1,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, &bind_group, &[]);\n        cpass.begin_pipeline_statistics_query(&query_set, 0);\n        cpass.dispatch_workgroups(1, 1, 1);\n        cpass.end_pipeline_statistics_query();\n\n        // Drop the query set. Then do a device poll to make sure it's not dropped too early, no matter what.\n        drop(query_set);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic COMPUTE_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().test_features_limits().features(\n            wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,\n        ))\n        .run_async(compute_pass_query_set_ownership_timestamps);\n\nasync fn compute_pass_query_set_ownership_timestamps(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        indirect_buffer: _,\n        bind_group,\n        pipeline,\n    } = resource_setup(&ctx);\n\n    let query_set_timestamp_writes = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set_timestamp_writes\"),\n        ty: wgpu::QueryType::Timestamp,\n        count: 2,\n    });\n    let query_set_write_timestamp = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set_write_timestamp\"),\n        ty: wgpu::QueryType::Timestamp,\n        count: 1,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"compute_pass\"),\n            timestamp_writes: Some(wgpu::ComputePassTimestampWrites {\n                query_set: &query_set_timestamp_writes,\n                beginning_of_pass_write_index: Some(0),\n                end_of_pass_write_index: Some(1),\n            }),\n        });\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, &bind_group, &[]);\n        cpass.write_timestamp(&query_set_write_timestamp, 0);\n        cpass.dispatch_workgroups(1, 1, 1);\n\n        // Drop the query sets. Then do a device poll to make sure they're not dropped too early, no matter what.\n        drop(query_set_timestamp_writes);\n        drop(query_set_write_timestamp);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic COMPUTE_PASS_KEEP_ENCODER_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .enable_noop(),\n    )\n    .run_async(compute_pass_keep_encoder_alive);\n\nasync fn compute_pass_keep_encoder_alive(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer: _,\n        cpu_buffer: _,\n        buffer_size: _,\n        indirect_buffer,\n        bind_group,\n        pipeline,\n    } = resource_setup(&ctx);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    let cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: Some(\"compute_pass\"),\n        timestamp_writes: None,\n    });\n\n    // Now drop the encoder - it is kept alive by the compute pass.\n    // To do so, we have to make the compute pass forget the lifetime constraint first.\n    let mut cpass = cpass.forget_lifetime();\n    drop(encoder);\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    // Record some draw commands.\n    cpass.set_pipeline(&pipeline);\n    cpass.set_bind_group(0, &bind_group, &[]);\n    cpass.dispatch_workgroups_indirect(&indirect_buffer, 0);\n\n    // Dropping the pass will still execute the pass, even though there's no way to submit it.\n    // Ideally, this would log an error, but the encoder is not dropped until the compute pass is dropped,\n    // making this a valid operation.\n    // (If instead the encoder was explicitly destroyed or finished, this would be an error.)\n    valid(&ctx.device, || drop(cpass));\n}\n\nasync fn assert_compute_pass_executed_normally(\n    mut encoder: wgpu::CommandEncoder,\n    gpu_buffer: wgpu::Buffer,\n    cpu_buffer: wgpu::Buffer,\n    buffer_size: u64,\n    ctx: TestingContext,\n) {\n    encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size);\n    ctx.queue.submit([encoder.finish()]);\n    cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = cpu_buffer.slice(..).get_mapped_range();\n\n    let floats: &[f32] = bytemuck::cast_slice(&data);\n    assert_eq!(floats, [2.0, 4.0, 6.0, 8.0]);\n}\n\n// Setup ------------------------------------------------------------\n\nstruct ResourceSetup {\n    gpu_buffer: wgpu::Buffer,\n    cpu_buffer: wgpu::Buffer,\n    buffer_size: u64,\n\n    indirect_buffer: wgpu::Buffer,\n    bind_group: wgpu::BindGroup,\n    pipeline: wgpu::ComputePipeline,\n}\n\nfn resource_setup(ctx: &TestingContext) -> ResourceSetup {\n    let sm = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"shader\"),\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let buffer_size = 4 * size_of::<f32>() as u64;\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind_group_layout\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(buffer_size),\n                },\n                count: None,\n            }],\n        });\n\n    let gpu_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"gpu_buffer\"),\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n            contents: bytemuck::bytes_of(&[1.0_f32, 2.0, 3.0, 4.0]),\n        });\n\n    let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"cpu_buffer\"),\n        size: buffer_size,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let indirect_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"indirect_buffer\"),\n            usage: wgpu::BufferUsages::INDIRECT,\n            contents: wgpu::util::DispatchIndirectArgs { x: 1, y: 1, z: 1 }.as_bytes(),\n        });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"bind_group\"),\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: gpu_buffer.as_entire_binding(),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &sm,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        indirect_buffer,\n        bind_group,\n        pipeline,\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/create_surface_error.rs",
    "content": "//! Test that `create_surface_*()` accurately reports those errors we can provoke.\n\n/// This test applies to those cfgs that can create a surface from a canvas, which\n/// include WebGL and WebGPU, but *not* Emscripten GLES.\n#[cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))]\n#[wasm_bindgen_test::wasm_bindgen_test]\nfn canvas_get_context_returned_null() {\n    // Not using the normal testing infrastructure because that goes straight to creating the canvas for us.\n    let instance = wgpu_test::initialize_instance(\n        wgpu::Backends::all(),\n        &wgpu_test::TestParameters::default(),\n    );\n    // Create canvas\n    let canvas = wgpu_test::initialize_html_canvas();\n\n    // Using a context id that is not \"webgl2\" or \"webgpu\" will render the canvas unusable by wgpu.\n    canvas.get_context(\"2d\").unwrap();\n\n    #[allow(clippy::redundant_clone)] // false positive — can't and shouldn't move out.\n    let error = instance\n        .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))\n        .unwrap_err();\n\n    assert!(\n        error\n            .to_string()\n            .contains(\"canvas.getContext() returned null\"),\n        \"{error}\"\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/device.rs",
    "content": "use std::sync::atomic::AtomicBool;\n\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        CROSS_DEVICE_BIND_GROUP_USAGE,\n        DEVICE_DESTROY_THEN_MORE,\n        DEVICE_DESTROY_THEN_LOST,\n        DIFFERENT_BGL_ORDER_BW_SHADER_AND_API,\n        DEVICE_DESTROY_THEN_BUFFER_CLEANUP,\n        DEVICE_AND_QUEUE_HAVE_DIFFERENT_IDS,\n    ]);\n\n    #[cfg(not(all(target_arch = \"wasm32\", not(target_os = \"emscripten\"))))]\n    {\n        vec.extend([\n            DEVICE_LIFETIME_CHECK,\n            MULTIPLE_DEVICES,\n            REQUEST_DEVICE_ERROR_MESSAGE_NATIVE,\n        ]);\n    }\n}\n\n#[gpu_test]\nstatic CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .expect_fail(FailureCase::always())\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        // Create a bind group using a layout from another device. This should be a validation\n        // error but currently crashes.\n        let (device2, _) =\n            pollster::block_on(ctx.adapter.request_device(&Default::default())).unwrap();\n\n        {\n            let bind_group_layout =\n                device2.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[],\n                });\n\n            let _bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: None,\n                layout: &bind_group_layout,\n                entries: &[],\n            });\n        }\n\n        ctx.async_poll(wgpu::PollType::Poll).await.unwrap();\n    });\n\n#[cfg(not(all(target_arch = \"wasm32\", not(target_os = \"emscripten\"))))]\n#[gpu_test]\nstatic DEVICE_LIFETIME_CHECK: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        ctx.instance.poll_all(false);\n\n        let pre_report = ctx.instance.generate_report().unwrap();\n\n        let TestingContext {\n            instance,\n            device,\n            queue,\n            ..\n        } = ctx;\n\n        drop(queue);\n        drop(device);\n\n        let post_report = instance.generate_report().unwrap();\n\n        assert_ne!(\n            pre_report, post_report,\n            \"Queue and Device has not been dropped as expected\"\n        );\n    });\n\n#[cfg(not(all(target_arch = \"wasm32\", not(target_os = \"emscripten\"))))]\n#[gpu_test]\nstatic MULTIPLE_DEVICES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        use pollster::FutureExt as _;\n        ctx.adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                required_features: wgpu::Features::empty(),\n                required_limits: wgpu::Limits::downlevel_webgl2_defaults(),\n                ..Default::default()\n            })\n            .block_on()\n            .expect(\"failed to create device\");\n        ctx.adapter\n            .request_device(&wgpu::DeviceDescriptor {\n                required_features: wgpu::Features::empty(),\n                required_limits: wgpu::Limits::downlevel_webgl2_defaults(),\n                ..Default::default()\n            })\n            .block_on()\n            .expect(\"failed to create device\");\n    });\n\n#[cfg(not(all(target_arch = \"wasm32\", not(target_os = \"emscripten\"))))]\n#[gpu_test]\nstatic REQUEST_DEVICE_ERROR_MESSAGE_NATIVE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters({\n        let default = TestParameters::default();\n\n        // On CI, we have the vulkan SDK installed and this test initializes the Vulkan backend when all\n        // normal tests do not, so we get these weird error messages. This only actually gets hooked up\n        // on Windows, because that's the only platform where the actual runtime gets hooked up and there\n        // are no drivers.\n        if std::env::var(\"WGPU_CI\").is_ok() && cfg!(windows) {\n            default.expect_fail(\n                FailureCase::always()\n                    .validation_error(\"Registry lookup failed to get ICD manifest files.  Possibly missing Vulkan driver?\")\n                    .validation_error(\"vkCreateInstance: Found no drivers!\")\n            )\n        } else {\n            default\n        }\n    })\n    .run_async(|_ctx| request_device_error_message());\n\n/// Check that `RequestDeviceError`s produced have some diagnostic information.\n///\n/// Note: this is a wasm *and* native test. On wasm it is run directly; on native, indirectly\n#[cfg_attr(target_arch = \"wasm32\", wasm_bindgen_test::wasm_bindgen_test)]\nasync fn request_device_error_message() {\n    // Not using initialize_test() because that doesn't let us catch the error\n    // nor .await anything\n    let (_instance, adapter, _surface_guard) =\n        wgpu_test::initialize_adapter(None, &TestParameters::default()).await;\n\n    let device_error = adapter\n        .request_device(&wgpu::DeviceDescriptor {\n            // Force a failure by requesting absurd limits.\n            required_features: wgpu::Features::all(),\n            required_limits: wgpu::Limits {\n                max_texture_dimension_1d: u32::MAX,\n                max_texture_dimension_2d: u32::MAX,\n                max_texture_dimension_3d: u32::MAX,\n                max_bind_groups: u32::MAX,\n                max_immediate_size: u32::MAX,\n                ..Default::default()\n            },\n            ..Default::default()\n        })\n        .await\n        .unwrap_err();\n\n    let device_error = device_error.to_string();\n    cfg_if::cfg_if! {\n        if #[cfg(all(target_arch = \"wasm32\", not(feature = \"webgl\")))] {\n            // On WebGPU, so the error we get will be from the browser WebGPU API.\n            // Per the WebGPU specification this should be a `TypeError` when features are not\n            // available, <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestdevice>,\n            // and the stringification it goes through for Rust should put that in the message.\n            let expected = \"TypeError\";\n        } else {\n            // This message appears whenever wgpu-core is used as the implementation.\n            let expected = \"Unsupported features were requested:\";\n        }\n    }\n    assert!(device_error.contains(expected), \"{device_error}\");\n}\n\n// This is a test of device behavior after `device.destroy()`. Specifically, all operations\n// should turn into no-ops, per spec.\n#[gpu_test]\nstatic DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLEAR_TEXTURE)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        // Create some resources on the device that we will attempt to use *after* losing\n        // the device.\n\n        // Create some 512 x 512 2D textures.\n        let texture_extent = wgpu::Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        };\n        let texture_for_view = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: texture_extent,\n            mip_level_count: 2,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg8Uint,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n        let target_view = texture_for_view.create_view(&wgpu::TextureViewDescriptor::default());\n\n        let texture_for_read = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: texture_extent,\n            mip_level_count: 2,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg8Uint,\n            usage: wgpu::TextureUsages::COPY_SRC,\n            view_formats: &[],\n        });\n\n        let texture_for_write = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: texture_extent,\n            mip_level_count: 2,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg8Uint,\n            usage: wgpu::TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n\n        // Create some buffers.\n        let buffer_source = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        let buffer_dest = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n        let buffer_for_map = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        let buffer_for_unmap = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: true,\n        });\n\n        // Create a shader module.\n        let shader_module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\"\")),\n            });\n\n        // Create some command encoders.\n        let mut encoder_for_clear = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_compute_pass = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_render_pass = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_buffer_buffer_copy = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_buffer_texture_copy = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_texture_buffer_copy = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut encoder_for_texture_texture_copy = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        // Destroy the device. This will cause all further operations to become no-ops.\n        ctx.device.destroy();\n\n        // The following operations should fail if the device was not lost.\n        // Since the device is lost, we check that they succeed.\n\n        let _ = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: ctx.device.limits().max_buffer_size * 2,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n\n        let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: ctx.device.limits().max_texture_dimension_2d * 2,\n                height: ctx.device.limits().max_texture_dimension_2d * 2,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 2,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg8Uint,\n            usage: wgpu::TextureUsages::COPY_SRC,\n            view_formats: &[],\n        });\n\n        encoder_for_clear.clear_texture(\n            &texture_for_write,\n            &wgpu::ImageSubresourceRange {\n                aspect: wgpu::TextureAspect::StencilOnly, // texture has no stencil component\n                base_mip_level: 0,\n                mip_level_count: None,\n                base_array_layer: 0,\n                array_layer_count: None,\n            },\n        );\n        ctx.queue.submit([encoder_for_clear.finish()]);\n\n        let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n            label: None,\n            ty: wgpu::QueryType::Occlusion,\n            count: u32::MAX, // can be at most 4096\n        });\n\n        let pass = encoder_for_compute_pass.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: Some(wgpu::ComputePassTimestampWrites {\n                query_set: &query_set,\n                beginning_of_pass_write_index: None,\n                end_of_pass_write_index: None,\n            }),\n        });\n        drop(pass);\n        ctx.queue.submit([encoder_for_compute_pass.finish()]);\n\n        let pass = encoder_for_render_pass.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &target_view,\n                depth_slice: Some(u32::MAX), // should not be provided for 2D views\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        drop(pass);\n        ctx.queue.submit([encoder_for_render_pass.finish()]);\n\n        encoder_for_buffer_buffer_copy.copy_buffer_to_buffer(\n            &buffer_source,\n            0,\n            &buffer_dest,\n            0,\n            u64::MAX, // out of bounds for both buffers\n        );\n        ctx.queue.submit([encoder_for_buffer_buffer_copy.finish()]);\n\n        encoder_for_buffer_texture_copy.copy_buffer_to_texture(\n            wgpu::TexelCopyBufferInfo {\n                buffer: &buffer_source,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: u64::MAX, // out of bounds for buffer\n                    bytes_per_row: Some(4),\n                    rows_per_image: None,\n                },\n            },\n            texture_for_write.as_image_copy(),\n            texture_extent,\n        );\n        ctx.queue.submit([encoder_for_buffer_texture_copy.finish()]);\n\n        encoder_for_texture_buffer_copy.copy_texture_to_buffer(\n            texture_for_read.as_image_copy(),\n            wgpu::TexelCopyBufferInfo {\n                buffer: &buffer_source,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: u64::MAX, // out of bounds for buffer\n                    bytes_per_row: Some(4),\n                    rows_per_image: None,\n                },\n            },\n            texture_extent,\n        );\n        ctx.queue.submit([encoder_for_texture_buffer_copy.finish()]);\n\n        encoder_for_texture_texture_copy.copy_texture_to_texture(\n            texture_for_read.as_image_copy(),\n            texture_for_write.as_image_copy(),\n            wgpu::Extent3d {\n                width: 512,\n                height: 512,\n                depth_or_array_layers: u32::MAX, // out of bounds for both textures\n            },\n        );\n        ctx.queue\n            .submit([encoder_for_texture_texture_copy.finish()]);\n\n        let invalid_bind_group_layout =\n            ctx.device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::all(),\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: std::num::NonZeroU64::new(\n                                ctx.device.limits().max_uniform_buffer_binding_size * 2,\n                            ),\n                        },\n                        count: None,\n                    }],\n                });\n\n        let _ = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &invalid_bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Buffer(buffer_source.as_entire_buffer_binding()),\n            }],\n        });\n\n        let invalid_pipeline_layout =\n            ctx.device\n                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                    label: None,\n                    bind_group_layouts: &[Some(&invalid_bind_group_layout)],\n                    immediate_size: 0,\n                });\n\n        let _ = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                    \"not a valid shader source\",\n                )),\n            });\n\n        let _ = ctx\n            .device\n            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: None,\n                layout: Some(&invalid_pipeline_layout),\n                vertex: wgpu::VertexState {\n                    module: &shader_module,\n                    entry_point: Some(\"\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                primitive: wgpu::PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                fragment: None,\n                multiview_mask: None,\n                cache: None,\n            });\n\n        let _ = ctx\n            .device\n            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                label: None,\n                layout: Some(&invalid_pipeline_layout),\n                module: &shader_module,\n                entry_point: None,\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        buffer_for_map\n            .slice(..)\n            .map_async(wgpu::MapMode::Write, |_| ());\n\n        buffer_for_unmap.unmap();\n    });\n\n#[gpu_test]\nstatic DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        // This test checks that when device.destroy is called, the provided\n        // DeviceLostClosure is called with reason DeviceLostReason::Destroyed.\n        static WAS_CALLED: AtomicBool = AtomicBool::new(false);\n\n        // Set a LoseDeviceCallback on the device.\n        let callback = Box::new(|reason, _m| {\n            WAS_CALLED.store(true, std::sync::atomic::Ordering::SeqCst);\n            assert!(\n                matches!(reason, wgpu::DeviceLostReason::Destroyed),\n                \"Device lost info reason should match DeviceLostReason::Destroyed.\"\n            );\n        });\n        ctx.device.set_device_lost_callback(callback);\n\n        // Destroy the device.\n        ctx.device.destroy();\n\n        // Make sure the device queues are empty, which ensures that the closure\n        // has been called.\n        assert!(ctx\n            .async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap()\n            .is_queue_empty());\n\n        assert!(\n            WAS_CALLED.load(std::sync::atomic::Ordering::SeqCst),\n            \"Device lost callback should have been called.\"\n        );\n    });\n\n#[gpu_test]\nstatic DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        // This test addresses a bug found in multiple backends where `wgpu_core` and `wgpu_hal`\n        // backends made different assumptions about the element order of vectors of bind group\n        // layout entries and bind group resource bindings.\n        //\n        // Said bug was exposed originally by:\n        //\n        // 1. Shader-declared bindings having a different order than resource bindings provided to\n        //    `Device::create_bind_group`.\n        // 2. Having more of one type of resource in the bind group than another.\n        //\n        // …such that internals would accidentally attempt to use an out-of-bounds index (of one\n        // resource type) in the wrong list of a different resource type. Let's reproduce that\n        // here.\n\n        let trivial_shaders_with_some_reversed_bindings = concat!(\n            \"@group(0) @binding(3) var myTexture2: texture_2d<f32>;\\n\",\n            \"@group(0) @binding(2) var myTexture1: texture_2d<f32>;\\n\",\n            \"@group(0) @binding(1) var mySampler: sampler;\\n\",\n            \"\\n\",\n            \"@fragment\\n\",\n            \"fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4f {\\n\",\n            \"  return textureSample(myTexture1, mySampler, pos.xy) \\n\",\n            \"    + textureSample(myTexture2, mySampler, pos.xy);\\n\",\n            \"}\\n\",\n            \"\\n\",\n            \"@vertex\\n\",\n            \"fn vs_main() -> @builtin(position) vec4<f32> {\\n\",\n            \"  return vec4<f32>(0.0, 0.0, 0.0, 1.0);\\n\",\n            \"}\\n\",\n        );\n\n        let trivial_shaders_with_some_reversed_bindings =\n            ctx.device\n                .create_shader_module(wgpu::ShaderModuleDescriptor {\n                    label: None,\n                    source: wgpu::ShaderSource::Wgsl(\n                        trivial_shaders_with_some_reversed_bindings.into(),\n                    ),\n                });\n\n        let my_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 1024,\n                height: 512,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let my_texture_view = my_texture.create_view(&wgpu::TextureViewDescriptor {\n            label: None,\n            format: None,\n            dimension: None,\n            usage: None,\n            aspect: wgpu::TextureAspect::All,\n            base_mip_level: 0,\n            mip_level_count: None,\n            base_array_layer: 0,\n            array_layer_count: None,\n        });\n\n        let my_sampler = ctx\n            .device\n            .create_sampler(&wgpu::SamplerDescriptor::default());\n\n        let render_pipeline = ctx\n            .device\n            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                fragment: Some(wgpu::FragmentState {\n                    module: &trivial_shaders_with_some_reversed_bindings,\n                    entry_point: Some(\"fs_main\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(wgpu::ColorTargetState {\n                        format: wgpu::TextureFormat::Bgra8Unorm,\n                        blend: None,\n                        write_mask: wgpu::ColorWrites::ALL,\n                    })],\n                }),\n                layout: None,\n\n                // Other fields below aren't interesting for this text.\n                label: None,\n                vertex: wgpu::VertexState {\n                    module: &trivial_shaders_with_some_reversed_bindings,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                primitive: wgpu::PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        // fail(&ctx.device, || {\n        // }, \"\");\n        let _ = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &render_pipeline.get_bind_group_layout(0),\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&my_sampler),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 2,\n                    resource: wgpu::BindingResource::TextureView(&my_texture_view),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 3,\n                    resource: wgpu::BindingResource::TextureView(&my_texture_view),\n                },\n            ],\n        });\n    });\n\n#[gpu_test]\nstatic DEVICE_DESTROY_THEN_BUFFER_CLEANUP: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        // When a device is destroyed, its resources should be released,\n        // without causing a deadlock.\n\n        // Create a buffer to be left around until the device is destroyed.\n        let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        // Create a texture to be left around until the device is destroyed.\n        let texture_extent = wgpu::Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        };\n        let _texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: texture_extent,\n            mip_level_count: 2,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg8Uint,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n\n        // Destroy the device.\n        ctx.device.destroy();\n\n        // Poll the device, which should try to clean up its resources.\n        ctx.instance.poll_all(true);\n    });\n\n#[gpu_test]\nstatic DEVICE_AND_QUEUE_HAVE_DIFFERENT_IDS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let TestingContext {\n            adapter,\n            device_features,\n            device_limits,\n            device,\n            queue,\n            ..\n        } = ctx;\n\n        drop(device);\n\n        let (device2, queue2) =\n            wgpu_test::initialize_device(&adapter, device_features, device_limits).await;\n\n        drop(queue);\n        drop(device2);\n        drop(queue2); // this would previously panic since we would try to use the Device ID to drop the Queue\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/dispatch_workgroups_indirect.rs",
    "content": "use wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        NUM_WORKGROUPS_BUILTIN,\n        DISCARD_DISPATCH,\n        RESET_BIND_GROUPS,\n        ZERO_SIZED_BUFFER,\n    ]);\n}\n\n/// Make sure that the num_workgroups builtin works properly (it requires a workaround on D3D12).\n#[gpu_test]\nstatic NUM_WORKGROUPS_BUILTIN: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .downlevel_flags(\n                wgpu::DownlevelFlags::COMPUTE_SHADERS | wgpu::DownlevelFlags::INDIRECT_EXECUTION,\n            )\n            .limits(wgpu::Limits {\n                max_immediate_size: 4,\n                ..wgpu::Limits::downlevel_defaults()\n            }),\n    )\n    .run_async(|ctx| async move {\n        let num_workgroups = [1, 2, 3];\n        let res = run_test(&ctx, &num_workgroups).await;\n        assert_eq!(res, num_workgroups);\n    });\n\n/// Make sure that we discard (don't run) the dispatch if its size exceeds the device limit.\n#[gpu_test]\nstatic DISCARD_DISPATCH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .downlevel_flags(\n                wgpu::DownlevelFlags::COMPUTE_SHADERS | wgpu::DownlevelFlags::INDIRECT_EXECUTION,\n            )\n            .limits(wgpu::Limits {\n                max_compute_workgroups_per_dimension: 10,\n                max_immediate_size: 4,\n                ..wgpu::Limits::downlevel_defaults()\n            }),\n    )\n    .run_async(|ctx| async move {\n        let max = ctx.device.limits().max_compute_workgroups_per_dimension;\n\n        let res = run_test(&ctx, &[max, max, max]).await;\n        assert_eq!(res, [max; 3]);\n\n        let res = run_test(&ctx, &[max + 1, 1, 1]).await;\n        assert_eq!(res, [0; 3]);\n\n        let res = run_test(&ctx, &[1, max + 1, 1]).await;\n        assert_eq!(res, [0; 3]);\n\n        let res = run_test(&ctx, &[1, 1, max + 1]).await;\n        assert_eq!(res, [0; 3]);\n    });\n\n/// Make sure that resetting the bind groups set by the validation code works properly.\n#[gpu_test]\nstatic RESET_BIND_GROUPS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .downlevel_flags(\n                wgpu::DownlevelFlags::COMPUTE_SHADERS | wgpu::DownlevelFlags::INDIRECT_EXECUTION,\n            )\n            .limits(wgpu::Limits {\n                max_immediate_size: 4,\n                ..wgpu::Limits::downlevel_defaults()\n            }).enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        let scope = ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n        let test_resources = TestResources::new(&ctx);\n\n        let indirect_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 12,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDIRECT,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx.device.create_command_encoder(&Default::default());\n        {\n            let mut compute_pass = encoder.begin_compute_pass(&Default::default());\n            compute_pass.set_pipeline(&test_resources.pipeline);\n            compute_pass.set_immediates(0, &[0, 0, 0, 0]);\n            // compute_pass.set_bind_group(0, &test_resources.bind_group, &[]);\n            compute_pass.dispatch_workgroups_indirect(&indirect_buffer, 0);\n        }\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let error = pollster::block_on(scope.pop());\n        assert!(error.is_some_and(|error| {\n            format!(\"{error}\").contains(\"The current set ComputePipeline with '' label expects a BindGroup to be set at index 0\")\n        }));\n    });\n\n/// Make sure that zero sized buffer validation is raised.\n#[gpu_test]\nstatic ZERO_SIZED_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .downlevel_flags(\n                wgpu::DownlevelFlags::COMPUTE_SHADERS | wgpu::DownlevelFlags::INDIRECT_EXECUTION,\n            )\n            .limits(wgpu::Limits {\n                max_immediate_size: 4,\n                ..wgpu::Limits::downlevel_defaults()\n            })\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        let scope = ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n        let test_resources = TestResources::new(&ctx);\n\n        let indirect_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 0,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDIRECT,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx.device.create_command_encoder(&Default::default());\n        {\n            let mut compute_pass = encoder.begin_compute_pass(&Default::default());\n            compute_pass.set_pipeline(&test_resources.pipeline);\n            compute_pass.set_immediates(0, &[0, 0, 0, 0]);\n            compute_pass.set_bind_group(0, &test_resources.bind_group, &[]);\n            compute_pass.dispatch_workgroups_indirect(&indirect_buffer, 0);\n        }\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let error = pollster::block_on(scope.pop());\n        assert!(error.is_some_and(|error| {\n            format!(\"{error}\").contains(\n                \"Indirect buffer uses bytes 0..12 which overruns indirect buffer of size 0\",\n            )\n        }));\n    });\n\nstruct TestResources {\n    pipeline: wgpu::ComputePipeline,\n    out_buffer: wgpu::Buffer,\n    readback_buffer: wgpu::Buffer,\n    bind_group: wgpu::BindGroup,\n}\n\nimpl TestResources {\n    fn new(ctx: &TestingContext) -> Self {\n        const SHADER_SRC: &str = \"\n            struct TestOffsetPc {\n                inner: u32,\n            }\n\n            // `test_offset.inner` should always be 0; we test that resetting the immediate data set by the validation code works properly.\n            var<immediate> test_offset: TestOffsetPc;\n\n            @group(0) @binding(0)\n            var<storage, read_write> out: array<u32, 3>;\n\n            @compute @workgroup_size(1)\n            fn main(@builtin(num_workgroups) num_workgroups: vec3u, @builtin(workgroup_id) workgroup_id: vec3u) {\n                if (all(workgroup_id == vec3u())) {\n                    out[0] = num_workgroups.x + test_offset.inner;\n                    out[1] = num_workgroups.y + test_offset.inner;\n                    out[2] = num_workgroups.z + test_offset.inner;\n                }\n            }\n        \";\n\n        let module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n            });\n\n        let bgl = ctx\n            .device\n            .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: None,\n                entries: &[wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                }],\n            });\n\n        let layout = ctx\n            .device\n            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bgl)],\n                immediate_size: 4,\n            });\n\n        let pipeline = ctx\n            .device\n            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                label: None,\n                layout: Some(&layout),\n                module: &module,\n                entry_point: Some(\"main\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        let out_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 12,\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n\n        let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 12,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &pipeline.get_bind_group_layout(0),\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: out_buffer.as_entire_binding(),\n            }],\n        });\n\n        Self {\n            pipeline,\n            out_buffer,\n            readback_buffer,\n            bind_group,\n        }\n    }\n}\n\nasync fn run_test(ctx: &TestingContext, num_workgroups: &[u32; 3]) -> [u32; 3] {\n    let test_resources = TestResources::new(ctx);\n\n    let mut res = None;\n\n    for (indirect_offset, indirect_buffer_size) in [\n        // internal src buffer binding size will be buffer.size\n        (0, 12),\n        (4, 4 + 12),\n        (4, 8 + 12),\n        (256 * 2 - 4 - 12, 256 * 2 - 4),\n        // internal src buffer binding size will be 256 * 2 + x\n        (0, 256 * 2 * 2 + 4),\n        (256, 256 * 2 * 2 + 8),\n        (256 + 4, 256 * 2 * 2 + 12),\n        (256 * 2 + 16, 256 * 2 * 2 + 16),\n        (256 * 2 * 2, 256 * 2 * 2 + 32),\n        (256 + 12, 256 * 2 * 2 + 64),\n    ] {\n        let indirect_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: indirect_buffer_size,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDIRECT,\n            mapped_at_creation: false,\n        });\n\n        ctx.queue.write_buffer(\n            &indirect_buffer,\n            indirect_offset,\n            bytemuck::bytes_of(num_workgroups),\n        );\n\n        let mut encoder = ctx.device.create_command_encoder(&Default::default());\n        {\n            let mut compute_pass = encoder.begin_compute_pass(&Default::default());\n            compute_pass.set_pipeline(&test_resources.pipeline);\n            compute_pass.set_immediates(0, &[0, 0, 0, 0]);\n            compute_pass.set_bind_group(0, &test_resources.bind_group, &[]);\n            compute_pass.dispatch_workgroups_indirect(&indirect_buffer, indirect_offset);\n        }\n\n        encoder.copy_buffer_to_buffer(\n            &test_resources.out_buffer,\n            0,\n            &test_resources.readback_buffer,\n            0,\n            12,\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        test_resources\n            .readback_buffer\n            .slice(..)\n            .map_async(wgpu::MapMode::Read, |_| {});\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        let view = test_resources.readback_buffer.slice(..).get_mapped_range();\n\n        let current_res = *bytemuck::from_bytes(&view);\n        drop(view);\n        test_resources.readback_buffer.unmap();\n\n        if let Some(past_res) = res {\n            assert_eq!(past_res, current_res);\n        } else {\n            res = Some(current_res);\n        }\n    }\n\n    res.unwrap()\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/draw_index.rs",
    "content": "use wgpu::{\n    Backends, ColorTargetState, ColorWrites, Features, FragmentState, Limits,\n    MeshPipelineDescriptor, MeshState, RenderPipelineDescriptor, ShaderModuleDescriptor, TaskState,\n    TextureFormat, VertexState,\n};\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.push(DRAW_INDEX);\n    tests.push(DRAW_INDEX_MESH_NO_TASK);\n    tests.push(DRAW_INDEX_MESH_TASK);\n    tests.push(DRAW_INDEX_TASK_NO_MESH);\n}\n\nasync fn test(ctx: TestingContext) {\n    const CODE: &str = \"\\\nenable draw_index;\n\nstruct Input {\n    @builtin(draw_index) draw_index: u32,\n}\n\n@vertex\nfn vertex(input: Input) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(f32(input.draw_index), 1.0, 1.0, 1.0);\n}\n@fragment\nfn fragment() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n\";\n    let module = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(CODE)),\n    });\n    let _pipeline = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: VertexState {\n                module: &module,\n                entry_point: Some(\"vertex\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: Default::default(),\n            fragment: Some(FragmentState {\n                module: &module,\n                entry_point: Some(\"fragment\"),\n                compilation_options: Default::default(),\n                targets: &[Some(ColorTargetState {\n                    format: TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: ColorWrites::all(),\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n}\n\n#[gpu_test]\nstatic DRAW_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_DRAW_INDEX)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .validation_error(\"vkDestroyDevice\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(test);\n\nasync fn test_mesh(ctx: TestingContext, use_task: bool, mesh_uses_draw_id: bool) {\n    const CODE: &str = \"\\\nenable draw_index;\nenable wgpu_mesh_shader;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n}\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 3>,\n    @builtin(primitive_count) primitive_count: u32,\n}\nvar<workgroup> mesh_output: MeshOutput;\nstruct TaskPayload { value: u32 }\nvar<task_payload> payload: TaskPayload;\n\n@task\n@payload(payload)\n@workgroup_size(1)\nfn task(@builtin(draw_index) id: u32) -> @builtin(mesh_task_size) vec3<u32> {\n    return vec3<u32>(1, 1, 1);\n}\n\n@mesh(mesh_output)\n@payload(payload)\n@workgroup_size(1)\nfn mesh_ts(MESH_DRAW_INDEX1) {\n    mesh_output.vertex_count = 0;\n    mesh_output.primitive_count = 0;\n}\n\n@mesh(mesh_output)\n@workgroup_size(1)\nfn mesh_no_ts(MESH_DRAW_INDEX2) {\n    mesh_output.vertex_count = 0;\n    mesh_output.primitive_count = 0;\n}\n@fragment\nfn fragment() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n\";\n\n    let used_code = CODE\n        .replace(\n            \"MESH_DRAW_INDEX2\",\n            if mesh_uses_draw_id {\n                \"@builtin(draw_index) id: u32\"\n            } else {\n                \"\"\n            },\n        )\n        .replace(\n            \"MESH_DRAW_INDEX1\",\n            if mesh_uses_draw_id && use_task {\n                \"@builtin(draw_index) id: u32\"\n            } else {\n                \"\"\n            },\n        );\n    let module = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Owned(used_code)),\n    });\n    let _ = ctx.device.create_mesh_pipeline(&MeshPipelineDescriptor {\n        label: None,\n        layout: None,\n        task: use_task.then_some(TaskState {\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n        }),\n        mesh: MeshState {\n            module: &module,\n            entry_point: Some(if use_task { \"mesh_ts\" } else { \"mesh_no_ts\" }),\n            compilation_options: Default::default(),\n        },\n        primitive: Default::default(),\n        depth_stencil: None,\n        multisample: Default::default(),\n        fragment: Some(FragmentState {\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            targets: &[Some(ColorTargetState {\n                format: TextureFormat::Rgba8Unorm,\n                blend: None,\n                write_mask: ColorWrites::all(),\n            })],\n        }),\n        multiview: None,\n        cache: None,\n    });\n}\n\nfn mesh_params() -> TestParameters {\n    // TODO: when support for mesh shaders in naga on dx12/metal lands enable those backends\n    TestParameters::default()\n        .features(Features::SHADER_DRAW_INDEX | Features::EXPERIMENTAL_MESH_SHADER)\n        .skip(FailureCase::backend(Backends::DX12 | Backends::METAL))\n        .limits(Limits::defaults().using_recommended_minimum_mesh_shader_values())\n}\n\n#[gpu_test]\nstatic DRAW_INDEX_TASK_NO_MESH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params())\n    .run_async(async |ctx| test_mesh(ctx, true, false).await);\n\n#[gpu_test]\nstatic DRAW_INDEX_MESH_NO_TASK: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params())\n    .run_async(async |ctx| test_mesh(ctx, false, true).await);\n\n#[gpu_test]\nstatic DRAW_INDEX_MESH_TASK: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params().expect_fail(FailureCase::always()))\n    .run_async(async |ctx| test_mesh(ctx, true, true).await);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/draw_indirect.rs",
    "content": "use wgpu::{\n    util::{BufferInitDescriptor, DeviceExt},\n    vertex_attr_array,\n};\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend(&[\n        DRAW,\n        DRAW_OOB_START,\n        DRAW_OOB_COUNT,\n        INSTANCED_DRAW,\n        INSTANCED_DRAW_OOB_START,\n        INSTANCED_DRAW_OOB_COUNT,\n        INSTANCED_DRAW_OOB_INSTANCE_START,\n        INSTANCED_DRAW_OOB_INSTANCE_COUNT,\n        INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE,\n        INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE_MISSING_FEATURE,\n        INDEXED_DRAW,\n        INDEXED_DRAW_OOB_START,\n        INDEXED_DRAW_OOB_COUNT,\n        INSTANCED_INDEXED_DRAW,\n        INSTANCED_INDEXED_DRAW_OOB_START,\n        INSTANCED_INDEXED_DRAW_OOB_COUNT,\n        INSTANCED_INDEXED_DRAW_OOB_INSTANCE_START,\n        INSTANCED_INDEXED_DRAW_OOB_INSTANCE_COUNT,\n        INDIRECT_BUFFER_OFFSETS,\n    ]);\n}\n\nstruct TestData {\n    kind: Kind,\n    instanced: Option<Instanced>,\n}\n\nstruct Instanced {\n    instance_buffer_content: &'static [f32],\n\n    first_instance: u32,\n    instance_count: u32,\n}\n\nenum Kind {\n    NonIndexed {\n        vertex_buffer_content: &'static [f32],\n\n        first_vertex: u32,\n        vertex_count: u32,\n    },\n    Indexed {\n        vertex_buffer_content: &'static [f32],\n\n        index_buffer_content: &'static [u32],\n\n        first_index: u32,\n        index_count: u32,\n    },\n}\n\nimpl TestData {\n    fn vertex_buffer_content(&self) -> &'static [f32] {\n        match self.kind {\n            Kind::NonIndexed {\n                vertex_buffer_content,\n                ..\n            } => vertex_buffer_content,\n            Kind::Indexed {\n                vertex_buffer_content,\n                ..\n            } => vertex_buffer_content,\n        }\n    }\n\n    fn write_indirect_args(&self, buf: &mut Vec<u8>) {\n        let (first_instance, instance_count) = match self.instanced {\n            Some(ref instanced) => (instanced.first_instance, instanced.instance_count),\n            None => (0, 1),\n        };\n        match self.kind {\n            Kind::NonIndexed {\n                first_vertex,\n                vertex_count,\n                ..\n            } => {\n                buf.extend_from_slice(\n                    wgpu::util::DrawIndirectArgs {\n                        vertex_count,\n                        instance_count,\n                        first_vertex,\n                        first_instance,\n                    }\n                    .as_bytes(),\n                );\n            }\n            Kind::Indexed {\n                first_index,\n                index_count,\n                ..\n            } => {\n                buf.extend_from_slice(\n                    wgpu::util::DrawIndexedIndirectArgs {\n                        index_count,\n                        instance_count,\n                        first_index,\n                        base_vertex: 0,\n                        first_instance,\n                    }\n                    .as_bytes(),\n                );\n            }\n        }\n    }\n}\n\nasync fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) {\n    let mut vertex_buffer_layouts = Vec::new();\n    vertex_buffer_layouts.push(wgpu::VertexBufferLayout {\n        array_stride: 8,\n        step_mode: wgpu::VertexStepMode::Vertex,\n        attributes: &vertex_attr_array![0 => Float32x2],\n    });\n    let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(test_data.vertex_buffer_content()),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let index_buffer = match test_data.kind {\n        Kind::NonIndexed { .. } => None,\n        Kind::Indexed {\n            index_buffer_content,\n            ..\n        } => Some(ctx.device.create_buffer_init(&BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(index_buffer_content),\n            usage: wgpu::BufferUsages::INDEX,\n        })),\n    };\n\n    let instance_buffer = test_data.instanced.as_ref().map(|instanced| {\n        vertex_buffer_layouts.push(wgpu::VertexBufferLayout {\n            array_stride: 8,\n            step_mode: wgpu::VertexStepMode::Instance,\n            attributes: &vertex_attr_array![1 => Float32x2],\n        });\n        ctx.device.create_buffer_init(&BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(instanced.instance_buffer_content),\n            usage: wgpu::BufferUsages::VERTEX,\n        })\n    });\n\n    let shader_src = if instance_buffer.is_none() {\n        \"\n            @vertex\n            fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f {\n                return vec4f(position, 0.0, 1.0);\n            }\n\n            @fragment\n            fn fs_main() -> @location(0) vec4f {\n                return vec4f(1.0);\n            }\n        \"\n    } else {\n        \"\n            @vertex\n            fn vs_main(@location(0) position: vec2f, @location(1) position_offset: vec2f) -> @builtin(position) vec4f {\n                return vec4f(position + position_offset, 0.0, 1.0);\n            }\n\n            @fragment\n            fn fs_main() -> @location(0) vec4f {\n                return vec4f(1.0);\n            }\n        \"\n    };\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &vertex_buffer_layouts,\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 * 256,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    // Use 2 passes to trigger internal validation buffer reuse\n    let passes = 2;\n    // Issue 2 draws per indirect buffer to trigger internal validation batching\n    let draws = 2; // try 66000 to test multiple temporary validation buffers\n\n    let mut indirect_bytes = Vec::new();\n    for _ in 0..passes * draws {\n        test_data.write_indirect_args(&mut indirect_bytes);\n    }\n    let indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: &indirect_bytes,\n        usage: wgpu::BufferUsages::INDIRECT,\n    });\n    // Use a secondary indirect buffer to test multiple validation batches.\n    let indirect_buffer2 = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: &indirect_bytes,\n        usage: wgpu::BufferUsages::INDIRECT,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    for pass_index in 0..passes {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &out_texture_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        if let Some(ref instance_buffer) = instance_buffer {\n            rpass.set_vertex_buffer(1, instance_buffer.slice(..));\n        }\n        if let Some(ref index_buffer) = index_buffer {\n            rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        }\n        for draw_index in 0..draws {\n            if index_buffer.is_some() {\n                let offset = pass_index * draw_index * 20;\n                rpass.draw_indexed_indirect(&indirect_buffer, offset);\n                rpass.draw_indexed_indirect(&indirect_buffer2, offset);\n            } else {\n                let offset = pass_index * draw_index * 20;\n                rpass.draw_indirect(&indirect_buffer, offset);\n                rpass.draw_indirect(&indirect_buffer2, offset);\n            }\n        }\n    }\n\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &out_texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &readback_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(256),\n                rows_per_image: None,\n            },\n        },\n        wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n    );\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let succeeded = if expect_noop {\n        data.iter().all(|b| *b == 0)\n    } else {\n        data.iter().all(|b| *b == u8::MAX)\n    };\n    assert!(succeeded);\n}\n\nmacro_rules! make_test {\n    ($name:ident, $test_data:expr) => {\n        make_test!($name, $test_data, false, wgpu::Features::empty());\n    };\n    ($name:ident, $test_data:expr, $features:expr) => {\n        make_test!($name, $test_data, false, $features);\n    };\n    ($name:ident, $test_data:expr, $expect_noop:expr, $features:expr) => {\n        #[gpu_test]\n        static $name: GpuTestConfiguration = GpuTestConfiguration::new()\n            .parameters({\n                let params = TestParameters::default()\n                    .downlevel_flags(wgpu::DownlevelFlags::INDIRECT_EXECUTION)\n                    .features($features)\n                    .limits(wgpu::Limits::downlevel_defaults());\n\n                if $expect_noop {\n                    params.enable_noop()\n                } else {\n                    params\n                }\n            })\n            .run_async(|ctx| run_test(ctx, $test_data, $expect_noop));\n    };\n}\nmacro_rules! make_failing_test {\n    ($name:ident, $test_data:expr) => {\n        make_test!($name, $test_data, true, wgpu::Features::empty());\n    };\n    ($name:ident, $test_data:expr, $features:expr) => {\n        make_test!($name, $test_data, true, $features);\n    };\n}\n\nfn get_draw_test_data(first_vertex: u32, vertex_count: u32) -> TestData {\n    let vertex_buffer_content = &[\n        // Triangle 1\n        -1.0, -1.0, // Bottom left\n        1.0, 1.0, // Top right\n        -1.0, 1.0, // Top left\n        // Triangle 2\n        -1.0, -1.0, // Bottom left\n        1.0, -1.0, // Bottom right\n        1.0, 1.0, // Top right\n    ];\n    TestData {\n        kind: Kind::NonIndexed {\n            vertex_buffer_content,\n            first_vertex,\n            vertex_count,\n        },\n        instanced: None,\n    }\n}\n\nmake_test!(DRAW, get_draw_test_data(0, 6));\nmake_failing_test!(DRAW_OOB_START, get_draw_test_data(1, 6));\nmake_failing_test!(DRAW_OOB_COUNT, get_draw_test_data(0, 7));\n\nfn get_instanced_draw_test_data(\n    first_vertex: u32,\n    vertex_count: u32,\n    first_instance: u32,\n    instance_count: u32,\n) -> TestData {\n    let vertex_buffer_content = &[\n        // Triangle 1\n        -0.5, -0.5, // Bottom left\n        0.5, 0.5, // Top right\n        -0.5, 0.5, // Top left\n        // Triangle 2\n        -0.5, -0.5, // Bottom left\n        0.5, -0.5, // Bottom right\n        0.5, 0.5, // Top right\n    ];\n    let instance_buffer_content = &[\n        -0.5, -0.5, // Move quad to bottom left\n        0.5, 0.5, // Move quad to top right\n        -0.5, 0.5, // Move quad to top left\n        0.5, -0.5, // Move quad to bottom right\n    ];\n    TestData {\n        kind: Kind::NonIndexed {\n            vertex_buffer_content,\n            first_vertex,\n            vertex_count,\n        },\n        instanced: Some(Instanced {\n            instance_buffer_content,\n            first_instance,\n            instance_count,\n        }),\n    }\n}\n\nmake_test!(INSTANCED_DRAW, get_instanced_draw_test_data(0, 6, 0, 4));\nmake_failing_test!(\n    INSTANCED_DRAW_OOB_START,\n    get_instanced_draw_test_data(1, 6, 0, 4)\n);\nmake_failing_test!(\n    INSTANCED_DRAW_OOB_COUNT,\n    get_instanced_draw_test_data(0, 7, 0, 4)\n);\nmake_failing_test!(\n    INSTANCED_DRAW_OOB_INSTANCE_START,\n    get_instanced_draw_test_data(0, 6, 1, 4),\n    wgpu::Features::INDIRECT_FIRST_INSTANCE\n);\nmake_failing_test!(\n    INSTANCED_DRAW_OOB_INSTANCE_COUNT,\n    get_instanced_draw_test_data(0, 6, 0, 5)\n);\n\nfn get_instanced_draw_with_non_zero_first_instance_test_data() -> TestData {\n    let vertex_buffer_content = &[\n        // Triangle 1\n        -0.5, -0.5, // Bottom left\n        0.5, 0.5, // Top right\n        -0.5, 0.5, // Top left\n        // Triangle 2\n        -0.5, -0.5, // Bottom left\n        0.5, -0.5, // Bottom right\n        0.5, 0.5, // Top right\n    ];\n    let instance_buffer_content = &[\n        10.0, 10.0, // unused\n        -0.5, -0.5, // Move quad to bottom left\n        0.5, 0.5, // Move quad to top right\n        -0.5, 0.5, // Move quad to top left\n        0.5, -0.5, // Move quad to bottom right\n    ];\n    TestData {\n        kind: Kind::NonIndexed {\n            vertex_buffer_content,\n            first_vertex: 0,\n            vertex_count: 6,\n        },\n        instanced: Some(Instanced {\n            instance_buffer_content,\n            first_instance: 1,\n            instance_count: 4,\n        }),\n    }\n}\n\nmake_test!(\n    INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE,\n    get_instanced_draw_with_non_zero_first_instance_test_data(),\n    wgpu::Features::INDIRECT_FIRST_INSTANCE\n);\nmake_failing_test!(\n    INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE_MISSING_FEATURE,\n    get_instanced_draw_with_non_zero_first_instance_test_data()\n);\n\nfn get_indexed_draw_test_data(first_index: u32, index_count: u32) -> TestData {\n    let vertex_buffer_content = &[\n        -1.0, -1.0, // Bottom left\n        1.0, 1.0, // Top right\n        -1.0, 1.0, // Top left\n        1.0, -1.0, // Bottom right\n    ];\n    let index_buffer_content = &[\n        0, 1, 2, // Triangle 1\n        0, 3, 1, // Triangle 2\n    ];\n    TestData {\n        kind: Kind::Indexed {\n            vertex_buffer_content,\n            index_buffer_content,\n            first_index,\n            index_count,\n        },\n        instanced: None,\n    }\n}\n\nmake_test!(INDEXED_DRAW, get_indexed_draw_test_data(0, 6));\nmake_failing_test!(INDEXED_DRAW_OOB_START, get_indexed_draw_test_data(1, 6));\nmake_failing_test!(INDEXED_DRAW_OOB_COUNT, get_indexed_draw_test_data(0, 7));\n\nfn get_instanced_indexed_draw_test_data(\n    first_index: u32,\n    index_count: u32,\n    first_instance: u32,\n    instance_count: u32,\n) -> TestData {\n    let vertex_buffer_content = &[\n        -0.5, -0.5, // Bottom left\n        0.5, 0.5, // Top right\n        -0.5, 0.5, // Top left\n        0.5, -0.5, // Bottom right\n    ];\n    let index_buffer_content = &[\n        0, 1, 2, // Triangle 1\n        0, 3, 1, // Triangle 2\n    ];\n    let instance_buffer_content = &[\n        -0.5, -0.5, // Move quad to bottom left\n        0.5, 0.5, // Move quad to top right\n        -0.5, 0.5, // Move quad to top left\n        0.5, -0.5, // Move quad to bottom right\n    ];\n    TestData {\n        kind: Kind::Indexed {\n            vertex_buffer_content,\n            index_buffer_content,\n            first_index,\n            index_count,\n        },\n        instanced: Some(Instanced {\n            instance_buffer_content,\n            first_instance,\n            instance_count,\n        }),\n    }\n}\n\nmake_test!(\n    INSTANCED_INDEXED_DRAW,\n    get_instanced_indexed_draw_test_data(0, 6, 0, 4)\n);\nmake_failing_test!(\n    INSTANCED_INDEXED_DRAW_OOB_START,\n    get_instanced_indexed_draw_test_data(1, 6, 0, 4)\n);\nmake_failing_test!(\n    INSTANCED_INDEXED_DRAW_OOB_COUNT,\n    get_instanced_indexed_draw_test_data(0, 7, 0, 4)\n);\nmake_failing_test!(\n    INSTANCED_INDEXED_DRAW_OOB_INSTANCE_START,\n    get_instanced_indexed_draw_test_data(0, 6, 1, 4),\n    wgpu::Features::INDIRECT_FIRST_INSTANCE\n);\nmake_failing_test!(\n    INSTANCED_INDEXED_DRAW_OOB_INSTANCE_COUNT,\n    get_instanced_indexed_draw_test_data(0, 6, 0, 5)\n);\n\n#[gpu_test]\nstatic INDIRECT_BUFFER_OFFSETS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(wgpu::DownlevelFlags::INDIRECT_EXECUTION)\n            .features(wgpu::Features::INDIRECT_FIRST_INSTANCE)\n            .limits(wgpu::Limits::downlevel_defaults()),\n    )\n    .run_async(indirect_buffer_offsets);\n\n/// Tests that indirect draw calls work properly with offsets that straddle 16 byte boundaries (size of DrawIndirectArgs).\nasync fn indirect_buffer_offsets(ctx: TestingContext) {\n    // The first 2 draws are successful, the third one is not.\n    let indirect_args_offsets = [0, 4, 8];\n\n    let indirect_args = [\n        //     1st draw       | 2nd draw       | 3rd draw\n        9,  // vertex_count   |                |\n        9,  // instance_count | vertex_count   |\n        1,  // first_vertex   | instance_count | vertex_count\n        0,  // first_instance | first_vertex   | instance_count\n        9,  //                | first_instance | first_vertex\n        10, //                |                | first_instance\n    ];\n\n    // 1st draw (first_vertex: 1): ◤ ◢ ◢\n    // 2nd draw (first_vertex: 0): ◤ ◣ ◢\n    let vertex_buffer_content = [\n        -0.5, 0.5, // Top left\n        // Triangle 1\n        -0.5, -0.5, // Bottom left\n        0.5, 0.5, // Top right\n        -0.5, 0.5, // Top left\n        // Triangle 2\n        -0.5, -0.5, // Bottom left\n        0.5, -0.5, // Bottom right\n        0.5, 0.5, // Top right\n        // Triangle 3 (same as Triangle 2)\n        -0.5, -0.5, // Bottom left\n        0.5, -0.5, // Bottom right\n        0.5, 0.5, // Top right\n    ];\n    #[rustfmt::skip]\n    let instance_buffer_content = [\n        // Move quad to top left (for 1st draw):\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        -0.5, 0.5,\n        // Move quad to top right (for 2nd draw):\n        0.5, 0.5,\n    ];\n\n    let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice::<f32, u8>(&vertex_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n    let instance_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice::<f32, u8>(&instance_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let shader_src = \"\n        @vertex\n        fn vs_main(@location(0) position: vec2f, @location(1) position_offset: vec2f) -> @builtin(position) vec4f {\n            return vec4f(position + position_offset, 0.0, 1.0);\n        }\n\n        @fragment\n        fn fs_main() -> @location(0) vec4f {\n            return vec4f(1.0);\n        }\n    \";\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &[\n                wgpu::VertexBufferLayout {\n                    array_stride: 8,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &vertex_attr_array![0 => Float32x2],\n                },\n                wgpu::VertexBufferLayout {\n                    array_stride: 8,\n                    step_mode: wgpu::VertexStepMode::Instance,\n                    attributes: &vertex_attr_array![1 => Float32x2],\n                },\n            ],\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 * 256,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice::<u32, u8>(&indirect_args),\n        usage: wgpu::BufferUsages::INDIRECT,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &out_texture_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.set_vertex_buffer(1, instance_buffer.slice(..));\n        for offset in indirect_args_offsets {\n            rpass.draw_indirect(&indirect_buffer, offset);\n        }\n    }\n\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &out_texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &readback_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(256),\n                rows_per_image: None,\n            },\n        },\n        wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n    );\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let half = data.len() / 2;\n    let succeeded =\n        data[..half].iter().all(|b| *b == u8::MAX) && data[half..].iter().all(|b| *b == 0);\n    assert!(succeeded);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/dual_source_blending.rs",
    "content": "use wgpu::*;\nuse wgpu_test::{\n    fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        DUAL_SOURCE_BLENDING_FEATURE_DISABLED,\n        DUAL_SOURCE_BLENDING_FEATURE_ENABLED,\n    ]);\n}\n\nconst VERTEX_SHADER: &str = r#\"\n@vertex\nfn vs_main() -> @builtin(position) vec4f {\n    return vec4f(1.0);\n}\n\"#;\n\nconst FRAGMENT_SHADER_WITHOUT_DUAL_SOURCE_BLENDING: &str = r#\"\n@fragment\nfn fs_main() -> @location(0) vec4f {\n    return vec4f(1.0);\n}\n\"#;\n\nconst FRAGMENT_SHADER_WITH_DUAL_SOURCE_BLENDING: &str = r#\"\nenable dual_source_blending;\nstruct FragmentOutput {\n    @location(0) @blend_src(0) output0_: vec4<f32>,\n    @location(0) @blend_src(1) output1_: vec4<f32>,\n}\n\n@fragment\nfn fs_main() -> FragmentOutput {\n    return FragmentOutput(vec4<f32>(0.4f, 0.3f, 0.2f, 0.1f), vec4<f32>(0.9f, 0.8f, 0.7f, 0.6f));\n}\n\"#;\n\nfn blend_state_with_dual_source_blending() -> BlendState {\n    wgpu::BlendState {\n        // \"random\" blend factors using a second blend source.\n        color: wgpu::BlendComponent {\n            src_factor: wgpu::BlendFactor::Src1,\n            dst_factor: wgpu::BlendFactor::Src1Alpha,\n            operation: wgpu::BlendOperation::Add,\n        },\n        alpha: wgpu::BlendComponent {\n            src_factor: wgpu::BlendFactor::Src1Alpha,\n            dst_factor: wgpu::BlendFactor::OneMinusSrc1Alpha,\n            operation: wgpu::BlendOperation::Subtract,\n        },\n    }\n}\n\n#[gpu_test]\nstatic DUAL_SOURCE_BLENDING_FEATURE_DISABLED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(dual_source_blending_disabled);\n\nasync fn dual_source_blending_disabled(ctx: TestingContext) {\n    let vertex_shader = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"vertex_shader\"),\n        source: ShaderSource::Wgsl(VERTEX_SHADER.into()),\n    });\n    let fragment_shader = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"fragment_shader\"),\n        source: ShaderSource::Wgsl(FRAGMENT_SHADER_WITHOUT_DUAL_SOURCE_BLENDING.into()),\n    });\n\n    // Can't create a render pipeline using blend modes that require dual source blending.\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx\n                .device\n                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                    label: Some(\"render_pipeline\"),\n                    layout: None,\n                    fragment: Some(wgpu::FragmentState {\n                        module: &fragment_shader,\n                        entry_point: Some(\"fs_main\"),\n                        targets: &[Some(wgpu::ColorTargetState {\n                            format: wgpu::TextureFormat::Rgba8Unorm,\n                            blend: Some(blend_state_with_dual_source_blending()),\n                            write_mask: wgpu::ColorWrites::all(),\n                        })],\n                        compilation_options: Default::default(),\n                    }),\n                    vertex: wgpu::VertexState {\n                        module: &vertex_shader,\n                        entry_point: None,\n                        buffers: &[],\n                        compilation_options: Default::default(),\n                    },\n                    primitive: wgpu::PrimitiveState::default(),\n                    depth_stencil: None,\n                    multisample: wgpu::MultisampleState::default(),\n                    multiview_mask: None,\n                    cache: None,\n                });\n        },\n        Some(\"Features Features { features_wgpu: FeaturesWGPU(0x0), features_webgpu: FeaturesWebGPU(DUAL_SOURCE_BLENDING) } are required but not enabled on the device\"),\n    );\n\n    // Can't create a shader using dual source blending.\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_shader_module(ShaderModuleDescriptor {\n                label: Some(\"shader\"),\n                source: ShaderSource::Wgsl(FRAGMENT_SHADER_WITH_DUAL_SOURCE_BLENDING.into()),\n            });\n        },\n        Some(\"the `dual_source_blending` extension is not supported in the current environment\"),\n    );\n}\n\n#[gpu_test]\nstatic DUAL_SOURCE_BLENDING_FEATURE_ENABLED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::DUAL_SOURCE_BLENDING)\n            .enable_noop(),\n    )\n    .run_async(dual_source_blending_enabled);\n\nasync fn dual_source_blending_enabled(ctx: TestingContext) {\n    let vertex_shader = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"vertex_shader\"),\n        source: ShaderSource::Wgsl(VERTEX_SHADER.into()),\n    });\n    let fragment_shader_without_dual_source_blending =\n        ctx.device.create_shader_module(ShaderModuleDescriptor {\n            label: Some(\"fragment_shader\"),\n            source: ShaderSource::Wgsl(FRAGMENT_SHADER_WITHOUT_DUAL_SOURCE_BLENDING.into()),\n        });\n    let fragment_shader_with_dual_source_blending =\n        ctx.device.create_shader_module(ShaderModuleDescriptor {\n            label: Some(\"fragment_shader\"),\n            source: ShaderSource::Wgsl(FRAGMENT_SHADER_WITH_DUAL_SOURCE_BLENDING.into()),\n        });\n\n    let render_pipeline_descriptor_template = wgpu::RenderPipelineDescriptor {\n        label: Some(\"render_pipeline\"),\n        layout: None,\n        fragment: Some(wgpu::FragmentState {\n            module: &fragment_shader_without_dual_source_blending,\n            entry_point: Some(\"fs_main\"),\n            targets: &[],\n            compilation_options: Default::default(),\n        }),\n        vertex: wgpu::VertexState {\n            module: &vertex_shader,\n            entry_point: None,\n            buffers: &[],\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        multiview_mask: None,\n        cache: None,\n    };\n\n    // Happy path:\n    // blend operator dual source: yes\n    // shader handling dual source: yes\n    let _ = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            fragment: Some(wgpu::FragmentState {\n                module: &fragment_shader_with_dual_source_blending,\n                entry_point: None,\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: Some(blend_state_with_dual_source_blending()),\n                    write_mask: wgpu::ColorWrites::all(),\n                })],\n                compilation_options: Default::default(),\n            }),\n            ..render_pipeline_descriptor_template.clone()\n        });\n\n    // Happy path:\n    // blend operator dual source: no\n    // shader handling dual source: yes\n    // (It is okay for the shader to define dual-source I/O that the pipeline\n    // does not use.)\n    let _ = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            fragment: Some(wgpu::FragmentState {\n                module: &fragment_shader_with_dual_source_blending,\n                entry_point: None,\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::all(),\n                })],\n                compilation_options: Default::default(),\n            }),\n            ..render_pipeline_descriptor_template.clone()\n        });\n\n    // Failure mode:\n    // blend operator dual source: yes\n    // shader handling dual source: no\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx\n                .device\n                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                    fragment: Some(wgpu::FragmentState {\n                        module: &fragment_shader_without_dual_source_blending,\n                        entry_point: None,\n                        targets: &[Some(wgpu::ColorTargetState {\n                            format: wgpu::TextureFormat::Rgba8Unorm,\n                            blend: Some(blend_state_with_dual_source_blending()),\n                            write_mask: wgpu::ColorWrites::all(),\n                        })],\n                        compilation_options: Default::default(),\n                    }),\n                    ..render_pipeline_descriptor_template.clone()\n                });\n        },\n        Some(\"Pipeline uses dual-source blending, but the shader does not support it\"),\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/encoder.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu::CommandEncoder;\nuse wgpu_test::{\n    fail, gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        DROP_ENCODER,\n        DROP_QUEUE_BEFORE_CREATING_COMMAND_ENCODER,\n        DROP_ENCODER_AFTER_ERROR,\n        ENCODER_OPERATIONS_FAIL_WHILE_PASS_ALIVE,\n    ]);\n}\n\n#[gpu_test]\nstatic DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        drop(encoder);\n    });\n\n#[gpu_test]\nstatic DROP_QUEUE_BEFORE_CREATING_COMMAND_ENCODER: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .expect_fail(FailureCase::always())\n                .enable_noop(),\n        )\n        .run_sync(|ctx| {\n            // Use the device after the queue is dropped. Currently this panics\n            // but it probably shouldn't.\n            // TODO(https://github.com/gfx-rs/wgpu/issues/7781) revisit this\n            let TestingContext { device, queue, .. } = ctx;\n            drop(queue);\n            let _encoder =\n                device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        });\n\n#[gpu_test]\nstatic DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 100,\n                height: 100,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n        let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());\n\n        let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: Some(\"renderpass\"),\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &target_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        // This viewport is invalid because it has negative size.\n        renderpass.set_viewport(0.0, 0.0, -1.0, -1.0, 0.0, 1.0);\n        drop(renderpass);\n\n        fail(&ctx.device, || encoder.finish(), Some(\"less than zero\"));\n    });\n\n#[gpu_test]\nstatic ENCODER_OPERATIONS_FAIL_WHILE_PASS_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TIMESTAMP_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(encoder_operations_fail_while_pass_alive);\n\nfn encoder_operations_fail_while_pass_alive(ctx: TestingContext) {\n    let buffer_source = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: &[0u8; 4],\n            usage: wgpu::BufferUsages::COPY_SRC,\n        });\n    let buffer_dest = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: &[0u8; 4],\n            usage: wgpu::BufferUsages::COPY_DST,\n        });\n\n    let texture_desc = wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_DST,\n        view_formats: &[],\n    };\n    let texture_dst = ctx.device.create_texture(&texture_desc);\n    let texture_src = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        usage: wgpu::TextureUsages::COPY_SRC,\n        ..texture_desc\n    });\n    let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        count: 1,\n        ty: wgpu::QueryType::Timestamp,\n        label: None,\n    });\n\n    let target_desc = wgpu::TextureDescriptor {\n        label: Some(\"target_tex\"),\n        size: wgpu::Extent3d {\n            width: 4,\n            height: 4,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Bgra8UnormSrgb,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[wgpu::TextureFormat::Bgra8UnormSrgb],\n    };\n    let target_tex = ctx.device.create_texture(&target_desc);\n    let color_attachment_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());\n\n    #[allow(clippy::type_complexity)]\n    let recording_ops: Vec<(_, Box<dyn Fn(&mut CommandEncoder)>)> = vec![\n        (\n            \"begin_compute_pass\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n            }),\n        ),\n        (\n            \"begin_render_pass\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.begin_render_pass(&wgpu::RenderPassDescriptor::default());\n            }),\n        ),\n        (\n            \"copy_buffer_to_buffer\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.copy_buffer_to_buffer(&buffer_source, 0, &buffer_dest, 0, 4);\n            }),\n        ),\n        (\n            \"copy_buffer_to_texture\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.copy_buffer_to_texture(\n                    wgpu::TexelCopyBufferInfo {\n                        buffer: &buffer_source,\n                        layout: wgpu::TexelCopyBufferLayout {\n                            offset: 0,\n                            bytes_per_row: Some(4),\n                            rows_per_image: None,\n                        },\n                    },\n                    texture_dst.as_image_copy(),\n                    texture_dst.size(),\n                );\n            }),\n        ),\n        (\n            \"copy_texture_to_buffer\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.copy_texture_to_buffer(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &texture_src,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d::ZERO,\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::TexelCopyBufferInfo {\n                        buffer: &buffer_dest,\n                        layout: wgpu::TexelCopyBufferLayout {\n                            offset: 0,\n                            bytes_per_row: Some(4),\n                            rows_per_image: None,\n                        },\n                    },\n                    texture_dst.size(),\n                );\n            }),\n        ),\n        (\n            \"copy_texture_to_texture\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.copy_texture_to_texture(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &texture_src,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d::ZERO,\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &texture_dst,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d::ZERO,\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    texture_dst.size(),\n                );\n            }),\n        ),\n        (\n            \"clear_texture\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.clear_texture(&texture_dst, &wgpu::ImageSubresourceRange::default());\n            }),\n        ),\n        (\n            \"clear_buffer\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.clear_buffer(&buffer_dest, 0, None);\n            }),\n        ),\n        (\n            \"insert_debug_marker\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.insert_debug_marker(\"marker\");\n            }),\n        ),\n        (\n            \"push_debug_group\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.push_debug_group(\"marker\");\n            }),\n        ),\n        (\n            \"pop_debug_group\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.pop_debug_group();\n            }),\n        ),\n        (\n            \"resolve_query_set\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.resolve_query_set(&query_set, 0..1, &buffer_dest, 0);\n            }),\n        ),\n        (\n            \"write_timestamp\",\n            Box::new(|encoder: &mut wgpu::CommandEncoder| {\n                encoder.write_timestamp(&query_set, 0);\n            }),\n        ),\n    ];\n\n    #[derive(Clone, Copy, Debug)]\n    enum PassType {\n        Compute,\n        Render,\n    }\n\n    let create_pass = |encoder: &mut wgpu::CommandEncoder, pass_type| -> Box<dyn std::any::Any> {\n        match pass_type {\n            PassType::Compute => Box::new(\n                encoder\n                    .begin_compute_pass(&wgpu::ComputePassDescriptor::default())\n                    .forget_lifetime(),\n            ),\n            PassType::Render => Box::new(\n                encoder\n                    .begin_render_pass(&wgpu::RenderPassDescriptor {\n                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                            view: &color_attachment_view,\n                            depth_slice: None,\n                            resolve_target: None,\n                            ops: wgpu::Operations::default(),\n                        })],\n                        ..Default::default()\n                    })\n                    .forget_lifetime(),\n            ),\n        }\n    };\n\n    for &pass_type in [PassType::Compute, PassType::Render].iter() {\n        for (op_name, op) in recording_ops.iter() {\n            // Test the case where the pass is not ended before calling finish()\n            let mut encoder = ctx\n                .device\n                .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n            let pass = create_pass(&mut encoder, pass_type);\n\n            let _scope = ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n            log::info!(\"Testing operation {op_name:?} on a locked command encoder while a {pass_type:?} pass is active\");\n            op(&mut encoder);\n\n            fail(&ctx.device, || encoder.finish(), Some(\"encoder is locked\"));\n\n            drop(pass);\n\n            // ...and the case where the pass is ended before calling finish()\n            let mut encoder = ctx\n                .device\n                .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n            let pass = create_pass(&mut encoder, pass_type);\n\n            log::info!(\"Testing operation {op_name:?} on a locked command encoder while a {pass_type:?} pass is active\");\n            op(&mut encoder);\n\n            drop(pass);\n\n            fail(&ctx.device, || encoder.finish(), Some(\"encoder is locked\"));\n\n            // We don't care about any errors that happen outside of a `fail` call.\n            drop(_scope);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/external_image_copy.rs",
    "content": "#![cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))]\n\nuse wasm_bindgen::JsCast;\nuse wgpu::ExternalImageSource;\nuse wgpu_test::{fail_if, gpu_test, GpuTestConfiguration};\n\n#[gpu_test]\nstatic IMAGE_BITMAP_IMPORT: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        let image_encoded = include_bytes!(\"3x3_colors.png\");\n\n        // Create an array-of-arrays for Blob's constructor\n        let array = js_sys::Array::new();\n        array.push(&js_sys::Uint8Array::from(&image_encoded[..]));\n\n        // We're passing an array of Uint8Arrays\n        let blob = web_sys::Blob::new_with_u8_array_sequence(&array).unwrap();\n\n        // Parse the image from the blob\n\n        // Because we need to call the function in a way that isn't bound by\n        // web_sys, we need to manually construct the options struct and call\n        // the function.\n        let image_bitmap_function: js_sys::Function = web_sys::window()\n            .unwrap()\n            .get(\"createImageBitmap\")\n            .unwrap()\n            .dyn_into()\n            .unwrap();\n\n        let options_arg = js_sys::Object::new();\n        js_sys::Reflect::set(\n            &options_arg,\n            &wasm_bindgen::JsValue::from_str(\"premultiplyAlpha\"),\n            &wasm_bindgen::JsValue::from_str(\"none\"),\n        )\n        .unwrap();\n        let image_bitmap_promise: js_sys::Promise = image_bitmap_function\n            .call2(&wasm_bindgen::JsValue::UNDEFINED, &blob, &options_arg)\n            .unwrap()\n            .dyn_into()\n            .unwrap();\n\n        // Wait for the parsing to be done\n        let image_bitmap: web_sys::ImageBitmap =\n            wasm_bindgen_futures::JsFuture::from(image_bitmap_promise)\n                .await\n                .unwrap()\n                .dyn_into()\n                .unwrap();\n\n        // Sanity checks\n        assert_eq!(image_bitmap.width(), 3);\n        assert_eq!(image_bitmap.height(), 3);\n\n        // Due to restrictions with premultiplication with ImageBitmaps, we also create an HtmlCanvasElement\n        // by drawing the image bitmap onto the canvas.\n        let canvas: web_sys::HtmlCanvasElement = web_sys::window()\n            .unwrap()\n            .document()\n            .unwrap()\n            .create_element(\"canvas\")\n            .unwrap()\n            .dyn_into()\n            .unwrap();\n        canvas.set_width(3);\n        canvas.set_height(3);\n\n        let d2_context: web_sys::CanvasRenderingContext2d = canvas\n            .get_context(\"2d\")\n            .unwrap()\n            .unwrap()\n            .dyn_into()\n            .unwrap();\n        d2_context\n            .draw_image_with_image_bitmap(&image_bitmap, 0.0, 0.0)\n            .unwrap();\n\n        // Decode it cpu side\n        let raw_image = image::load_from_memory_with_format(image_encoded, image::ImageFormat::Png)\n            .unwrap()\n            .into_rgba8();\n\n        // Set of test cases to test with image import\n        #[derive(Debug, Copy, Clone)]\n        enum TestCase {\n            // Import the image as normal\n            Normal,\n            // Sets the FlipY flag. Deals with global state on GLES, so run before other tests to ensure it's reset.\n            //\n            // Only works on canvases.\n            FlipY,\n            // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.\n            //\n            // Only works on canvases.\n            Premultiplied,\n            // Sets the color space to P3.\n            //\n            // Only works on canvases.\n            ColorSpace,\n            // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.\n            // Set both the input offset and output offset to 1 in x, so the first column is omitted.\n            TrimLeft,\n            // Set the size to 2 in x, so the last column is omitted\n            TrimRight,\n            // Set only the output offset to 1, so the second column gets the first column's data.\n            SlideRight,\n            // Try to copy from out of bounds of the source image\n            SourceOutOfBounds,\n            // Try to copy from out of bounds of the destination image\n            DestOutOfBounds,\n            // Try to copy more than one slice from the source\n            MultiSliceCopy,\n            // Copy into the second slice of a 2D array texture,\n            SecondSliceCopy,\n        }\n        let sources = [\n            ExternalImageSource::ImageBitmap(image_bitmap),\n            ExternalImageSource::HTMLCanvasElement(canvas),\n        ];\n        let cases = [\n            TestCase::Normal,\n            TestCase::FlipY,\n            TestCase::Premultiplied,\n            TestCase::ColorSpace,\n            TestCase::TrimLeft,\n            TestCase::TrimRight,\n            TestCase::SlideRight,\n            TestCase::SourceOutOfBounds,\n            TestCase::DestOutOfBounds,\n            TestCase::MultiSliceCopy,\n            TestCase::SecondSliceCopy,\n        ];\n\n        for source in sources {\n            for case in cases {\n                // Copy the data, so we can modify it for tests\n                let mut raw_image = raw_image.clone();\n                // The origin used for the external copy on the source side.\n                let mut src_origin = wgpu::Origin2d::ZERO;\n                // If the source should be flipped in Y\n                let mut src_flip_y = false;\n                // The origin used for the external copy on the destination side.\n                let mut dest_origin = wgpu::Origin3d::ZERO;\n                // The layer the external image's data should end up in.\n                let mut dest_data_layer = 0;\n                // Color space the destination is in.\n                let mut dest_color_space = wgpu::PredefinedColorSpace::Srgb;\n                // If the destination image is premultiplied.\n                let mut dest_premultiplied = false;\n                // Size of the external copy\n                let mut copy_size = wgpu::Extent3d {\n                    width: 3,\n                    height: 3,\n                    depth_or_array_layers: 1,\n                };\n                // Width of the destination texture\n                let mut dest_width = 3;\n                // Layer count of the destination texture\n                let mut dest_layers = 1;\n\n                // If the test is supposed to be valid call to copyExternal.\n                let mut valid = true;\n                match case {\n                    TestCase::Normal => {}\n                    TestCase::FlipY => {\n                        valid = !matches!(source, wgpu::ExternalImageSource::ImageBitmap(_));\n                        src_flip_y = true;\n                        for x in 0..3 {\n                            let top = raw_image[(x, 0)];\n                            let bottom = raw_image[(x, 2)];\n                            raw_image[(x, 0)] = bottom;\n                            raw_image[(x, 2)] = top;\n                        }\n                    }\n                    TestCase::Premultiplied => {\n                        valid = !matches!(source, wgpu::ExternalImageSource::ImageBitmap(_));\n                        dest_premultiplied = true;\n                        for pixel in raw_image.pixels_mut() {\n                            let mut float_pix = pixel.0.map(|v| v as f32 / 255.0);\n                            float_pix[0] *= float_pix[3];\n                            float_pix[1] *= float_pix[3];\n                            float_pix[2] *= float_pix[3];\n                            pixel.0 = float_pix.map(|v| (v * 255.0).round() as u8);\n                        }\n                    }\n                    TestCase::ColorSpace => {\n                        valid = ctx\n                            .adapter_downlevel_capabilities\n                            .flags\n                            .contains(wgpu::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES);\n                        dest_color_space = wgpu::PredefinedColorSpace::DisplayP3;\n\n                        // As we don't test, we don't bother converting the color spaces\n                        // in the image as that's relatively annoying.\n                    }\n                    TestCase::TrimLeft => {\n                        valid = ctx\n                            .adapter_downlevel_capabilities\n                            .flags\n                            .contains(wgpu::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES);\n                        src_origin.x = 1;\n                        dest_origin.x = 1;\n                        copy_size.width = 2;\n                        for y in 0..3 {\n                            raw_image[(0, y)].0 = [0; 4];\n                        }\n                    }\n                    TestCase::TrimRight => {\n                        copy_size.width = 2;\n                        for y in 0..3 {\n                            raw_image[(2, y)].0 = [0; 4];\n                        }\n                    }\n                    TestCase::SlideRight => {\n                        dest_origin.x = 1;\n                        copy_size.width = 2;\n                        for x in (1..3).rev() {\n                            for y in 0..3 {\n                                raw_image[(x, y)].0 = raw_image[(x - 1, y)].0;\n                            }\n                        }\n                        for y in 0..3 {\n                            raw_image[(0, y)].0 = [0; 4];\n                        }\n                    }\n                    TestCase::SourceOutOfBounds => {\n                        valid = false;\n                        // It's now in bounds for the destination\n                        dest_width = 4;\n                        copy_size.width = 4;\n                    }\n                    TestCase::DestOutOfBounds => {\n                        valid = false;\n                        // It's now out bounds for the destination\n                        dest_width = 2;\n                    }\n                    TestCase::MultiSliceCopy => {\n                        valid = false;\n                        copy_size.depth_or_array_layers = 2;\n                        dest_layers = 2;\n                    }\n                    TestCase::SecondSliceCopy => {\n                        dest_origin.z = 1;\n                        dest_data_layer = 1;\n                        dest_layers = 2;\n                    }\n                }\n\n                let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                    label: Some(\"import dest\"),\n                    size: wgpu::Extent3d {\n                        width: dest_width,\n                        height: 3,\n                        depth_or_array_layers: dest_layers,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                    usage: wgpu::TextureUsages::RENDER_ATTACHMENT\n                        | wgpu::TextureUsages::COPY_DST\n                        | wgpu::TextureUsages::COPY_SRC,\n                    view_formats: &[],\n                });\n\n                fail_if(\n                    &ctx.device,\n                    !valid,\n                    || {\n                        ctx.queue.copy_external_image_to_texture(\n                            &wgpu::CopyExternalImageSourceInfo {\n                                source: source.clone(),\n                                origin: src_origin,\n                                flip_y: src_flip_y,\n                            },\n                            wgpu::CopyExternalImageDestInfo {\n                                texture: &texture,\n                                mip_level: 0,\n                                origin: dest_origin,\n                                aspect: wgpu::TextureAspect::All,\n                                color_space: dest_color_space,\n                                premultiplied_alpha: dest_premultiplied,\n                            },\n                            copy_size,\n                        );\n                    },\n                    None,\n                );\n\n                let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                    label: Some(\"readback buffer\"),\n                    size: 4 * 64 * 3,\n                    usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n                    mapped_at_creation: false,\n                });\n\n                let mut encoder = ctx\n                    .device\n                    .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n                encoder.copy_texture_to_buffer(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &texture,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d {\n                            x: 0,\n                            y: 0,\n                            z: dest_data_layer,\n                        },\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::TexelCopyBufferInfo {\n                        buffer: &readback_buffer,\n                        layout: wgpu::TexelCopyBufferLayout {\n                            offset: 0,\n                            bytes_per_row: Some(256),\n                            rows_per_image: None,\n                        },\n                    },\n                    wgpu::Extent3d {\n                        width: dest_width,\n                        height: 3,\n                        depth_or_array_layers: 1,\n                    },\n                );\n\n                ctx.queue.submit(Some(encoder.finish()));\n                readback_buffer\n                    .slice(..)\n                    .map_async(wgpu::MapMode::Read, |_| ());\n                ctx.async_poll(wgpu::PollType::wait_indefinitely())\n                    .await\n                    .unwrap();\n\n                let buffer = readback_buffer.slice(..).get_mapped_range();\n\n                // 64 because of 256 byte alignment / 4.\n                let gpu_image = image::RgbaImage::from_vec(64, 3, buffer.to_vec()).unwrap();\n                let gpu_image_cropped =\n                    image::imageops::crop_imm(&gpu_image, 0, 0, 3, 3).to_image();\n\n                if valid {\n                    assert_eq!(\n                        raw_image, gpu_image_cropped,\n                        \"Failed on test case {case:?} {source:?}\"\n                    );\n                } else {\n                    assert_ne!(\n                        raw_image, gpu_image_cropped,\n                        \"Failed on test case {case:?} {source:?}\"\n                    );\n                }\n            }\n        }\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/external_texture/dimensions.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_external;\n\n@group(0) @binding(1)\nvar<storage, read_write> output: vec2<u32>;\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) global_id: vec3<u32>) {\n    output = textureDimensions(tex);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/external_texture/load.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_external;\n\n@group(0) @binding(1)\nvar<storage, read> coords: array<vec2<u32>>;\n@group(0) @binding(2)\nvar<storage, read_write> output: array<vec4<f32>>;\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n    output[id.x] = textureLoad(tex, coords[id.x]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/external_texture/mod.rs",
    "content": "use approx::assert_abs_diff_eq;\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        EXTERNAL_TEXTURE_DIMENSIONS,\n        EXTERNAL_TEXTURE_LOAD,\n        EXTERNAL_TEXTURE_LOAD_YUV,\n        EXTERNAL_TEXTURE_LOAD_TRANSFORM,\n        EXTERNAL_TEXTURE_LOAD_INVALID_ADDRESS,\n        EXTERNAL_TEXTURE_SAMPLE,\n        EXTERNAL_TEXTURE_SAMPLE_YUV,\n        EXTERNAL_TEXTURE_SAMPLE_TRANSFORM,\n    ]);\n}\n\n// Input texture data\nconst RED_U8: [u8; 4] = [0xFF, 0x00, 0x00, 0xFF];\nconst GREEN_U8: [u8; 4] = [0x00, 0xFF, 0x00, 0xFF];\nconst BLUE_U8: [u8; 4] = [0x00, 0x00, 0xFF, 0xFF];\nconst YELLOW_U8: [u8; 4] = [0xFF, 0xFF, 0x00, 0xFF];\nconst BLACK_U8: [u8; 4] = [0x00, 0x00, 0x00, 0xFF];\n\n// Data for a 2x2 or 4x1 texture with red, green, blue, and yellow pixels\nconst RGBA_TEXTURE_DATA: [[u8; 4]; 4] = [RED_U8, GREEN_U8, BLUE_U8, YELLOW_U8];\n\n// Data for a 4x4 texture with red, green, blue, and yellow pixels in the\n// centre, surrounded by a black border.\n#[rustfmt::skip]\nconst RGBA_TEXTURE_DATA_WITH_BORDER: [[u8; 4]; 16] = [\n    BLACK_U8, BLACK_U8, BLACK_U8,  BLACK_U8,\n    BLACK_U8, RED_U8,   GREEN_U8,  BLACK_U8,\n    BLACK_U8, BLUE_U8,  YELLOW_U8, BLACK_U8,\n    BLACK_U8, BLACK_U8, BLACK_U8,  BLACK_U8,\n];\n\n// Red, green, blue, and yellow in BT601 limited range YUV. Values obtained by\n// extracting raw frames from the WebGPU CTS' \"four-colors\" video [1], and\n// inspecting the contents.\n// [1] https://github.com/gpuweb/cts/blob/44dac855ba07b23c49d2cbc2c9d87bf8e6f38c47/src/resources/four-colors-vp8-bt601.webm\nconst RED_Y_U8: u8 = 0x51;\nconst RED_U_U8: u8 = 0x5A;\nconst RED_V_U8: u8 = 0xF0;\nconst GREEN_Y_U8: u8 = 0x91;\nconst GREEN_U_U8: u8 = 0x35;\nconst GREEN_V_U8: u8 = 0x22;\nconst BLUE_Y_U8: u8 = 0x29;\nconst BLUE_U_U8: u8 = 0xF0;\nconst BLUE_V_U8: u8 = 0x6E;\nconst YELLOW_Y_U8: u8 = 0xD2;\nconst YELLOW_U_U8: u8 = 0x10;\nconst YELLOW_V_U8: u8 = 0x92;\n\n// Data for a 4x4 texture with 4:2:0 chroma subsampling. The top-left quadrant\n// is red, top-right green, bottom-left blue, and bottom-right yellow.\n#[rustfmt::skip]\nconst Y_TEXTURE_DATA: [u8; 16] = [\n    RED_Y_U8,  RED_Y_U8,  GREEN_Y_U8,  GREEN_Y_U8,\n    RED_Y_U8,  RED_Y_U8,  GREEN_Y_U8,  GREEN_Y_U8,\n    BLUE_Y_U8, BLUE_Y_U8, YELLOW_Y_U8, YELLOW_Y_U8,\n    BLUE_Y_U8, BLUE_Y_U8, YELLOW_Y_U8, YELLOW_Y_U8,\n];\nconst U_TEXTURE_DATA: [u8; 4] = [RED_U_U8, GREEN_U_U8, BLUE_U_U8, YELLOW_U_U8];\nconst V_TEXTURE_DATA: [u8; 4] = [RED_V_U8, GREEN_V_U8, BLUE_V_U8, YELLOW_V_U8];\n\n// Expected results after texture load/sample.\nconst RED_F32: [f32; 4] = [1.0, 0.0, 0.0, 1.0];\nconst GREEN_F32: [f32; 4] = [0.0, 1.0, 0.0, 1.0];\nconst BLUE_F32: [f32; 4] = [0.0, 0.0, 1.0, 1.0];\nconst YELLOW_F32: [f32; 4] = [1.0, 1.0, 0.0, 1.0];\nconst OPAQUE_BLACK_F32: [f32; 4] = [0.0, 0.0, 0.0, 1.0];\nconst TRANSPARENT_BLACK_F32: [f32; 4] = [0.0, 0.0, 0.0, 0.0];\n\n// Expected results after texture load/sample in sRGB color space. Values\n// taken from the WebGPU CTS:\n// https://github.com/gpuweb/cts/blob/44dac855ba07b23c49d2cbc2c9d87bf8e6f38c47/src/webgpu/web_platform/util.ts#L36-L43\nconst RED_SRGB_F32: [f32; 4] = [0.9729456, 0.14179438, -0.020958992, 1.0];\nconst GREEN_SRGB_F32: [f32; 4] = [0.24823427, 0.98481035, -0.056470133, 1.0];\nconst BLUE_SRGB_F32: [f32; 4] = [0.10159736, 0.13545112, 1.0026299, 1.0];\nconst YELLOW_SRGB_F32: [f32; 4] = [0.99547076, 0.9927421, -0.07742912, 1.0];\n\n#[rustfmt::skip]\nconst IDENTITY_YUV_CONVERSION_MATRIX: [f32; 16] = [\n    1.0, 0.0, 0.0, 0.0,\n    0.0, 1.0, 0.0, 0.0,\n    0.0, 0.0, 1.0, 0.0,\n    0.0, 0.0, 0.0, 1.0,\n];\n#[rustfmt::skip]\nconst BT601_YUV_CONVERSION_MATRIX: [f32; 16] = [\n    1.1643835,  1.1643834,   1.1643835,  0.0,\n    0.0,        -0.39176223, 2.017232,   0.0,\n    1.5960265,  -0.81296754, 0.0,        0.0,\n    -0.8742022, 0.5316678,   -1.0856307, 1.0\n];\n\nconst SRGB_TRANSFER_FUNCTION: wgpu::ExternalTextureTransferFunction =\n    wgpu::ExternalTextureTransferFunction {\n        a: 1.055,\n        b: 0.003130805,\n        g: 2.4,\n        k: 12.92,\n    };\n\nconst IDENTITY_GAMUT_CONVERSION_MATRIX: [f32; 9] = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];\n#[rustfmt::skip]\nconst BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX: [f32; 9] = [\n    0.93954253,  0.017772198, -0.0016215984,\n    0.050181333, 0.96579295,  -0.0043697506,\n    0.010276437, 0.016434949, 1.0059911,\n];\n\nconst IDENTITY_SAMPLE_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0];\nconst IDENTITY_LOAD_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0];\n// Flips a 2x2 texture horizontally\nconst HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 1.0, 1.0, 0.0];\nconst HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 1.0, 1.0, 0.0];\n// Flips a 2x2 texture vertically\nconst VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, -1.0, 0.0, 1.0];\nconst VERTICAL_FLIP_2X2_LOAD_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, -1.0, 0.0, 1.0];\n// Rotates a 4x1 texture 90 degrees\nconst ROTATE_90_4X1_SAMPLE_TRANSFORM: [f32; 6] = [0.0, -1.0, 1.0, 0.0, 0.0, 1.0];\nconst ROTATE_90_4X1_LOAD_TRANSFORM: [f32; 6] = [0.0, 1.0, 1.0, 0.0, 0.0, 0.0];\n// Rotates a 4x1 texture 180 degrees\nconst ROTATE_180_4X1_SAMPLE_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, -1.0, 1.0, 1.0];\nconst ROTATE_180_4X1_LOAD_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 0.0, 3.0, 0.0];\n// Rotates a 4xx1 texture 270 degrees\nconst ROTATE_270_4X1_SAMPLE_TRANSFORM: [f32; 6] = [0.0, 1.0, -1.0, 0.0, 1.0, 0.0];\nconst ROTATE_270_4X1_LOAD_TRANSFORM: [f32; 6] = [0.0, 0.0, -1.0, 0.0, 3.0, 0.0];\n// Crops the middle 2x2 pixels from a 4x4 texture\nconst CROP_4X4_SAMPLE_TRANSFORM: [f32; 6] = [0.5, 0.0, 0.0, 0.5, 0.25, 0.25];\nconst CROP_4X4_LOAD_TRANSFORM: [f32; 6] = [0.5, 0.0, 0.0, 0.5, 1.0, 1.0];\n\n/// Helper function to create a 2D texture and a view, optionally writing the\n/// provided data to the texture, and returning the view.\nfn create_texture_and_view(\n    ctx: &TestingContext,\n    size: wgpu::Extent3d,\n    format: wgpu::TextureFormat,\n    data: Option<&[u8]>,\n) -> wgpu::TextureView {\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format,\n        usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    });\n    if let Some(data) = data {\n        ctx.queue.write_texture(\n            texture.as_image_copy(),\n            data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size.width * format.components() as u32),\n                rows_per_image: None,\n            },\n            size,\n        );\n    }\n    texture.create_view(&wgpu::TextureViewDescriptor::default())\n}\n\n/// Helper function to perform textureDimensions() and return the result.\nfn get_dimensions(ctx: &TestingContext, texture_resource: wgpu::BindingResource) -> [u32; 2] {\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"dimensions.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: Some(\"main\"),\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: std::mem::size_of::<[u32; 2]>() as _,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n    let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: output_buffer.size(),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: texture_resource,\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bind_group, &[]);\n        pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size());\n    ctx.queue.submit(Some(encoder.finish()));\n    let buffer_slice = download_buffer.slice(..);\n    buffer_slice.map_async(wgpu::MapMode::Read, |_| {});\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    let data = buffer_slice.get_mapped_range();\n    let size: &[u32] = bytemuck::cast_slice(&data);\n    size.try_into().unwrap()\n}\n\n/// Helper function to perform `textureLoad()` for the specified coordinates and return\n/// the loaded values.\nfn get_loads(\n    ctx: &TestingContext,\n    coords: &[[u32; 2]],\n    texture_resource: wgpu::BindingResource,\n) -> Vec<[f32; 4]> {\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"load.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: Some(\"main\"),\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    let coords_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: std::mem::size_of_val(coords) as _,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    ctx.queue\n        .write_buffer(&coords_buffer, 0, bytemuck::cast_slice(coords));\n\n    let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: (coords.len() * std::mem::size_of::<[f32; 4]>()) as _,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n    let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: output_buffer.size(),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: texture_resource,\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: coords_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 2,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bind_group, &[]);\n        pass.dispatch_workgroups(coords.len() as _, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size());\n    ctx.queue.submit(Some(encoder.finish()));\n    let buffer_slice = download_buffer.slice(..);\n    buffer_slice.map_async(wgpu::MapMode::Read, |_| {});\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    let data = buffer_slice.get_mapped_range();\n    let values: &[[f32; 4]] = bytemuck::cast_slice(&data);\n    values.to_vec()\n}\n\n/// Helper function to perform `textureSampleBaseClampToEdge()` for the specified\n/// coordinates and return the sampled values.\nfn get_samples(\n    ctx: &TestingContext,\n    coords: &[[f32; 2]],\n    texture_resource: wgpu::BindingResource,\n) -> Vec<[f32; 4]> {\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"sample.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &module,\n            entry_point: Some(\"main\"),\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    let coords_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: std::mem::size_of_val(coords) as _,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    ctx.queue\n        .write_buffer(&coords_buffer, 0, bytemuck::cast_slice(coords));\n\n    let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: (coords.len() * std::mem::size_of::<[f32; 4]>()) as _,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n    let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: output_buffer.size(),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let sampler = ctx\n        .device\n        .create_sampler(&wgpu::SamplerDescriptor::default());\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: texture_resource,\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: wgpu::BindingResource::Sampler(&sampler),\n            },\n            wgpu::BindGroupEntry {\n                binding: 2,\n                resource: coords_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 3,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bind_group, &[]);\n        pass.dispatch_workgroups(coords.len() as _, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size());\n    ctx.queue.submit(Some(encoder.finish()));\n    let buffer_slice = download_buffer.slice(..);\n    buffer_slice.map_async(wgpu::MapMode::Read, |_| {});\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    let data = buffer_slice.get_mapped_range();\n    let values: &[[f32; 4]] = bytemuck::cast_slice(&data);\n    values.to_vec()\n}\n\n/// Tests that `textureDimensions()` returns the correct value for both external textures\n/// and texture views bound to an external texture binding.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_DIMENSIONS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        const TEXTURE_WIDTH: u32 = 128;\n        const TEXTURE_HEIGHT: u32 = 64;\n        let view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: TEXTURE_WIDTH,\n                height: TEXTURE_HEIGHT,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            None,\n        );\n        let dims = get_dimensions(&ctx, wgpu::BindingResource::TextureView(&view));\n        assert_eq!(dims, [TEXTURE_WIDTH, TEXTURE_HEIGHT]);\n\n        const EXTERNAL_TEXTURE_WIDTH: u32 = 32;\n        const EXTERNAL_TEXTURE_HEIGHT: u32 = 16;\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: EXTERNAL_TEXTURE_WIDTH,\n                height: EXTERNAL_TEXTURE_HEIGHT,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&view],\n        );\n        let dims = get_dimensions(\n            &ctx,\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n        // This should return the dimensions provided in the ExternalTextureDescriptor,\n        // rather than the dimensions of the underlying texture.\n        assert_eq!(dims, [EXTERNAL_TEXTURE_WIDTH, EXTERNAL_TEXTURE_HEIGHT])\n    });\n\n/// Tests that `textureLoad()` returns the correct values for both RGBA format external\n/// textures and texture views bound to an external texture binding.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_LOAD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::TextureView(&view),\n        );\n        assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&view],\n        );\n\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n        assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n    });\n\n/// Tests that `textureLoad()` returns the correct values for YUV format external\n/// textures.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_LOAD_YUV: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let y_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 4,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&Y_TEXTURE_DATA),\n        );\n        let u_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&U_TEXTURE_DATA),\n        );\n        let v_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&V_TEXTURE_DATA),\n        );\n\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 4,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Yu12,\n                yuv_conversion_matrix: BT601_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: SRGB_TRANSFER_FUNCTION,\n                dst_transfer_function: SRGB_TRANSFER_FUNCTION,\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&y_view, &u_view, &v_view],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[1, 1], [2, 1], [1, 2], [2, 2]],\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n\n        // `assert_abs_diff_eq!()` works for slices but not arrays, hence the\n        // following tomfoolery.\n        let loads = loads.iter().map(|arr| arr.as_slice()).collect::<Vec<_>>();\n        let expected = [RED_SRGB_F32, GREEN_SRGB_F32, BLUE_SRGB_F32, YELLOW_SRGB_F32];\n        let expected = expected.each_ref().map(|arr| arr.as_slice());\n        // We expect slight inaccuracies due to floating point maths.\n        assert_abs_diff_eq!(loads.as_slice(), expected.as_slice(), epsilon = 0.01);\n    });\n\n/// Tests that `textureLoad()` returns the correct values for external textures with\n/// various load transforms.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_LOAD_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let view_2x2 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n\n        let flip_h_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM,\n                load_transform: HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM,\n            },\n            &[&view_2x2],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::ExternalTexture(&flip_h_external_texture),\n        );\n        assert_eq!(&loads, &[GREEN_F32, RED_F32, YELLOW_F32, BLUE_F32]);\n\n        let flip_v_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM,\n                load_transform: VERTICAL_FLIP_2X2_LOAD_TRANSFORM,\n            },\n            &[&view_2x2],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::ExternalTexture(&flip_v_external_texture),\n        );\n        assert_eq!(&loads, &[BLUE_F32, YELLOW_F32, RED_F32, GREEN_F32]);\n\n        // Use a non-square texture for the rotation cases as it's more\n        // interesting\n        let view_4x1 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n\n        let rotate_90_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 1,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_90_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_90_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [0, 1], [0, 2], [0, 3]],\n            wgpu::BindingResource::ExternalTexture(&rotate_90_external_texture),\n        );\n        assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n\n        let rotate_180_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 4,\n                height: 1,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_180_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_180_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [2, 0], [3, 0]],\n            wgpu::BindingResource::ExternalTexture(&rotate_180_external_texture),\n        );\n        assert_eq!(&loads, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]);\n\n        let rotate_270_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 1,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_270_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_270_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [0, 1], [0, 2], [0, 3]],\n            wgpu::BindingResource::ExternalTexture(&rotate_270_external_texture),\n        );\n        assert_eq!(&loads, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]);\n\n        let view_4x4 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 4,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA_WITH_BORDER.as_flattened()),\n        );\n        let crop_tex = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: CROP_4X4_SAMPLE_TRANSFORM,\n                load_transform: CROP_4X4_LOAD_TRANSFORM,\n            },\n            &[&view_4x4],\n        );\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::ExternalTexture(&crop_tex),\n        );\n        assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n    });\n\n/// Tests that `textureLoad()` for an invalid address returns an allowed value.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_LOAD_INVALID_ADDRESS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        // Create a 2x2 texture, but specify the width and height of the\n        // external texture to be 1x1. (0, 1), (1, 0), and (1, 1) will\n        // therefore be invalid addresses, despite falling within the bounds of\n        // the underlying texture.\n        let view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 1,\n                height: 1,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&view],\n        );\n\n        let loads = get_loads(\n            &ctx,\n            &[[0, 0], [1, 0], [0, 1], [1, 1]],\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n        for load in &loads {\n            // From https://www.w3.org/TR/WGSL/#textureload:\n            // If the logical texel address is invalid, the built-in function returns one of:\n            //   * The data for some texel within bounds of the texture\n            //   * A vector (0,0,0,0) or (0,0,0,1) of the appropriate type for non-depth textures\n            //   * 0.0 for depth textures\n            // We therefore expect the loaded values to be red, opaque black,\n            // or transparent black. They must not be green, blue, or yellow.\n            assert!([RED_F32, OPAQUE_BLACK_F32, TRANSPARENT_BLACK_F32].contains(load));\n        }\n    });\n\n/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for both RGBA\n/// format external textures and texture views bound to an external texture binding.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_SAMPLE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]],\n            wgpu::BindingResource::TextureView(&view),\n        );\n        assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&view],\n        );\n\n        let samples = get_samples(\n            &ctx,\n            &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]],\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n        assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n    });\n\n/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for YUV\n/// format external textures.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_SAMPLE_YUV: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let y_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 4,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&Y_TEXTURE_DATA),\n        );\n        let u_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&U_TEXTURE_DATA),\n        );\n        let v_view = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::R8Unorm,\n            Some(&V_TEXTURE_DATA),\n        );\n\n        let external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 4,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Yu12,\n                yuv_conversion_matrix: BT601_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: SRGB_TRANSFER_FUNCTION,\n                dst_transfer_function: SRGB_TRANSFER_FUNCTION,\n                sample_transform: IDENTITY_SAMPLE_TRANSFORM,\n                load_transform: IDENTITY_LOAD_TRANSFORM,\n            },\n            &[&y_view, &u_view, &v_view],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[\n                [0.375, 0.375],\n                [0.625, 0.375],\n                [0.375, 0.625],\n                [0.625, 0.625],\n            ],\n            wgpu::BindingResource::ExternalTexture(&external_texture),\n        );\n\n        // `assert_abs_diff_eq!()` works for slices but not arrays, hence the\n        // following tomfoolery.\n        let samples = samples.iter().map(|arr| arr.as_slice()).collect::<Vec<_>>();\n        let expected = [RED_SRGB_F32, GREEN_SRGB_F32, BLUE_SRGB_F32, YELLOW_SRGB_F32];\n        let expected = expected.each_ref().map(|arr| arr.as_slice());\n        // We expect slight inaccuracies due to floating point maths.\n        assert_abs_diff_eq!(samples.as_slice(), expected.as_slice(), epsilon = 0.01);\n    });\n\n/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for external\n/// textures with various sample transforms.\n#[gpu_test]\nstatic EXTERNAL_TEXTURE_SAMPLE_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::EXTERNAL_TEXTURE),\n    )\n    .run_async(|ctx| async move {\n        let view_2x2 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 2,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n\n        let flip_h_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM,\n                load_transform: HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM,\n            },\n            &[&view_2x2],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]],\n            wgpu::BindingResource::ExternalTexture(&flip_h_external_texture),\n        );\n        assert_eq!(&samples, &[GREEN_F32, RED_F32, YELLOW_F32, BLUE_F32]);\n\n        let flip_v_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM,\n                load_transform: VERTICAL_FLIP_2X2_LOAD_TRANSFORM,\n            },\n            &[&view_2x2],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]],\n            wgpu::BindingResource::ExternalTexture(&flip_v_external_texture),\n        );\n        assert_eq!(&samples, &[BLUE_F32, YELLOW_F32, RED_F32, GREEN_F32]);\n\n        // Use a non-square texture for the rotation cases as it's more\n        // interesting\n        let view_4x1 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA.as_flattened()),\n        );\n\n        let rotate_90_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 1,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_90_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_90_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.5, 0.125], [0.5, 0.375], [0.5, 0.625], [0.5, 0.875]],\n            wgpu::BindingResource::ExternalTexture(&rotate_90_external_texture),\n        );\n        assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n\n        let rotate_180_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 4,\n                height: 1,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_180_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_180_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.125, 0.5], [0.375, 0.5], [0.625, 0.5], [0.875, 0.5]],\n            wgpu::BindingResource::ExternalTexture(&rotate_180_external_texture),\n        );\n        assert_eq!(&samples, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]);\n\n        let rotate_270_external_texture = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 1,\n                height: 4,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: ROTATE_270_4X1_SAMPLE_TRANSFORM,\n                load_transform: ROTATE_270_4X1_LOAD_TRANSFORM,\n            },\n            &[&view_4x1],\n        );\n        let samples = get_samples(\n            &ctx,\n            &[[0.5, 0.125], [0.5, 0.375], [0.5, 0.625], [0.5, 0.875]],\n            wgpu::BindingResource::ExternalTexture(&rotate_270_external_texture),\n        );\n        assert_eq!(&samples, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]);\n\n        let view_4x4 = create_texture_and_view(\n            &ctx,\n            wgpu::Extent3d {\n                width: 4,\n                height: 4,\n                depth_or_array_layers: 1,\n            },\n            wgpu::TextureFormat::Rgba8Unorm,\n            Some(RGBA_TEXTURE_DATA_WITH_BORDER.as_flattened()),\n        );\n        let crop_tex = ctx.device.create_external_texture(\n            &wgpu::ExternalTextureDescriptor {\n                label: None,\n                width: 2,\n                height: 2,\n                format: wgpu::ExternalTextureFormat::Rgba,\n                yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX,\n                gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX,\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: CROP_4X4_SAMPLE_TRANSFORM,\n                load_transform: CROP_4X4_LOAD_TRANSFORM,\n            },\n            &[&view_4x4],\n        );\n        // Deliberately sample from the edges of the external texture rather\n        // than the texel centres. This tests that clamping to a half-texel\n        // from the edge works as expected even when the external texture is\n        // cropped from a larger texture. If this weren't working, the black\n        // border would affect the sampled values.\n        let samples = get_samples(\n            &ctx,\n            &[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]],\n            wgpu::BindingResource::ExternalTexture(&crop_tex),\n        );\n        assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/external_texture/sample.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_external;\n@group(0) @binding(1)\nvar samp: sampler;\n\n@group(0) @binding(2)\nvar<storage, read> coords: array<vec2<f32>>;\n@group(0) @binding(3)\nvar<storage, read_write> output: array<vec4<f32>>;\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n    output[id.x] = textureSampleBaseClampToEdge(tex, samp, coords[id.x]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/float32_filterable.rs",
    "content": "//! Tests for FLOAT32_FILTERABLE feature.\n\nuse wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        FLOAT32_FILTERABLE_WITHOUT_FEATURE,\n        FLOAT32_FILTERABLE_WITH_FEATURE,\n    ]);\n}\n\nfn create_texture_binding(device: &wgpu::Device, format: wgpu::TextureFormat, filterable: bool) {\n    let texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format,\n        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n        view_formats: &[],\n    });\n\n    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[wgpu::BindGroupLayoutEntry {\n            binding: 0,\n            visibility: wgpu::ShaderStages::FRAGMENT,\n            ty: wgpu::BindingType::Texture {\n                sample_type: wgpu::TextureSampleType::Float { filterable },\n                multisampled: false,\n                view_dimension: wgpu::TextureViewDimension::D2,\n            },\n            count: None,\n        }],\n    });\n\n    let _bg = device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::TextureView(&view),\n        }],\n    });\n}\n\n#[gpu_test]\nstatic FLOAT32_FILTERABLE_WITHOUT_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let device = &ctx.device;\n        // Unorm textures are always filterable\n        create_texture_binding(device, wgpu::TextureFormat::R8Unorm, true);\n        create_texture_binding(device, wgpu::TextureFormat::R8Unorm, false);\n        // As are float16 textures\n        create_texture_binding(device, wgpu::TextureFormat::R16Float, true);\n        create_texture_binding(device, wgpu::TextureFormat::R16Float, false);\n        // Float 32 textures can be used as non-filterable only\n        create_texture_binding(device, wgpu::TextureFormat::R32Float, false);\n        // This is supposed to fail, since we have not activated the feature\n        fail(\n            &ctx.device,\n            || {\n                create_texture_binding(device, wgpu::TextureFormat::R32Float, true);\n            },\n            Some(concat!(\n                \"texture binding 0 expects sample type float { filterable: true }, \",\n                \"but was given a view with format r32float \",\n                \"(sample type float { filterable: false })\"\n            )),\n        );\n    });\n\n#[gpu_test]\nstatic FLOAT32_FILTERABLE_WITH_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::FLOAT32_FILTERABLE)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let device = &ctx.device;\n        // With the feature enabled, it does work!\n        create_texture_binding(device, wgpu::TextureFormat::R32Float, true);\n        create_texture_binding(device, wgpu::TextureFormat::Rg32Float, true);\n        create_texture_binding(device, wgpu::TextureFormat::Rgba32Float, true);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/image_atomics/image_32_atomics.wgsl",
    "content": "@group(0) @binding(0)\nvar image: texture_storage_2d<r32uint, atomic>;\n\n@compute\n@workgroup_size(4, 4, 1)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>, @builtin(workgroup_id) group_id: vec3<u32>) {\n    let pixel = id + group_id * 4;\n    textureAtomicMax(image, pixel.xy, u32(pixel.x));\n\n    storageBarrier();\n\n    textureAtomicMin(image, pixel.xy, u32(pixel.y));\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/image_atomics/image_64_atomics.wgsl",
    "content": "@group(0) @binding(0)\nvar image: texture_storage_2d<r64uint, atomic>;\n\n@compute\n@workgroup_size(4, 4, 1)\nfn cs_main(@builtin(local_invocation_id) id: vec3<u32>, @builtin(workgroup_id) group_id: vec3<u32>) {\n    let pixel = id + group_id * 4;\n    textureAtomicMax(image, pixel.xy, u64(pixel.x));\n\n    storageBarrier();\n\n    textureAtomicMin(image, pixel.xy, u64(pixel.y));\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/image_atomics/mod.rs",
    "content": "//! Tests for image atomics.\n\nuse wgpu::ShaderModuleDescriptor;\nuse wgpu_test::{\n    fail, gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer,\n    TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        IMAGE_64_ATOMICS,\n        IMAGE_32_ATOMICS,\n        IMAGE_ATOMICS_NOT_ENABLED,\n        IMAGE_ATOMICS_NOT_SUPPORTED,\n    ]);\n}\n\n#[gpu_test]\nstatic IMAGE_64_ATOMICS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits {\n                max_storage_textures_per_shader_stage: 1,\n                max_compute_invocations_per_workgroup: 64,\n                max_compute_workgroup_size_x: 4,\n                max_compute_workgroup_size_y: 4,\n                max_compute_workgroup_size_z: 4,\n                max_compute_workgroups_per_dimension: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT,\n                ..wgpu::Limits::downlevel_webgl2_defaults()\n            })\n            .features(\n                wgpu::Features::TEXTURE_ATOMIC\n                    | wgpu::Features::TEXTURE_INT64_ATOMIC\n                    | wgpu::Features::SHADER_INT64,\n            ),\n    )\n    .run_async(|ctx| async move {\n        test_format(\n            ctx,\n            wgpu::TextureFormat::R64Uint,\n            wgpu::include_wgsl!(\"image_64_atomics.wgsl\"),\n        )\n        .await;\n    });\n\n#[gpu_test]\nstatic IMAGE_32_ATOMICS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits {\n                max_storage_textures_per_shader_stage: 1,\n                max_compute_invocations_per_workgroup: 64,\n                max_compute_workgroup_size_x: 4,\n                max_compute_workgroup_size_y: 4,\n                max_compute_workgroup_size_z: 4,\n                max_compute_workgroups_per_dimension: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT,\n                ..wgpu::Limits::downlevel_webgl2_defaults()\n            })\n            .features(wgpu::Features::TEXTURE_ATOMIC),\n    )\n    .run_async(|ctx| async move {\n        test_format(\n            ctx,\n            wgpu::TextureFormat::R32Uint,\n            wgpu::include_wgsl!(\"image_32_atomics.wgsl\"),\n        )\n        .await;\n    });\n\nasync fn test_format(\n    ctx: TestingContext,\n    format: wgpu::TextureFormat,\n    desc: ShaderModuleDescriptor<'_>,\n) {\n    let pixel_bytes = format.target_pixel_byte_cost().unwrap();\n    let size = wgpu::Extent3d {\n        width: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT,\n        height: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT,\n        depth_or_array_layers: 1,\n    };\n    let bind_group_layout_entry = wgpu::BindGroupLayoutEntry {\n        binding: 0,\n        visibility: wgpu::ShaderStages::COMPUTE,\n        ty: wgpu::BindingType::StorageTexture {\n            access: wgpu::StorageTextureAccess::Atomic,\n            format,\n            view_dimension: wgpu::TextureViewDimension::D2,\n        },\n        count: None,\n    };\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[bind_group_layout_entry],\n        });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n    let shader = ctx.device.create_shader_module(desc);\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"image atomics pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &shader,\n            entry_point: Some(\"cs_main\"),\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        dimension: wgpu::TextureDimension::D2,\n        size,\n        format,\n        usage: wgpu::TextureUsages::STORAGE_BINDING\n            | wgpu::TextureUsages::STORAGE_ATOMIC\n            | wgpu::TextureUsages::COPY_SRC,\n        mip_level_count: 1,\n        sample_count: 1,\n        view_formats: &[],\n    });\n    let view = tex.create_view(&wgpu::TextureViewDescriptor {\n        format: Some(format),\n        aspect: wgpu::TextureAspect::All,\n        ..wgpu::TextureViewDescriptor::default()\n    });\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::TextureView(&view),\n        }],\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    let mut rpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: None,\n        timestamp_writes: None,\n    });\n    rpass.set_pipeline(&pipeline);\n    rpass.set_bind_group(0, Some(&bind_group), &[]);\n    rpass.dispatch_workgroups(size.width, size.height, 1);\n    drop(rpass);\n\n    let readback_buffers = ReadbackBuffers::new(&ctx.device, &tex);\n    readback_buffers.copy_from(&ctx.device, &mut encoder, &tex);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let padding = [0].repeat(pixel_bytes as usize - size_of::<u32>());\n    let data: Vec<u8> = (0..size.width as usize * size.height as usize)\n        .flat_map(|i| {\n            let x = i as u32 % size.width;\n            let y = i as u32 / size.width;\n            [bytemuck::bytes_of(&u32::min(x, y)), &padding].concat()\n        })\n        .collect();\n\n    readback_buffers.assert_buffer_contents(&ctx, &data).await;\n}\n\n#[gpu_test]\nstatic IMAGE_ATOMICS_NOT_ENABLED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n\n        fail(\n            &ctx.device,\n            || {\n                let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                    label: None,\n                    dimension: wgpu::TextureDimension::D2,\n                    size,\n                    format: wgpu::TextureFormat::R32Uint,\n                    usage: wgpu::TextureUsages::STORAGE_ATOMIC,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    view_formats: &[],\n                });\n            },\n            Some(\"Texture usages TextureUsages(STORAGE_ATOMIC) are not allowed on a texture of type R32Uint\"),\n        );\n    });\n\n#[gpu_test]\nstatic IMAGE_ATOMICS_NOT_SUPPORTED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_ATOMIC).enable_noop())\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n\n        fail(\n            &ctx.device,\n            || {\n                let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                    label: None,\n                    dimension: wgpu::TextureDimension::D2,\n                    size,\n                    format: wgpu::TextureFormat::R8Uint,\n                    usage: wgpu::TextureUsages::STORAGE_ATOMIC,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    view_formats: &[],\n                });\n            },\n            Some(\"Texture usages TextureUsages(STORAGE_ATOMIC) are not allowed on a texture of type R8Uint\"),\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/immediates.rs",
    "content": "use std::mem::size_of;\nuse std::num::NonZeroU64;\n\nuse wgpu::util::RenderEncoder;\nuse wgpu::*;\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([PARTIAL_UPDATE, RENDER_PASS_TEST]);\n}\n\n/// We want to test that partial updates to immediates work as expected.\n///\n/// As such, we dispatch two compute passes, one which writes the values\n/// before a partial update, and one which writes the values after the partial update.\n///\n/// If the update code is working correctly, the values not written to by the second update\n/// will remain unchanged.\n#[gpu_test]\nstatic PARTIAL_UPDATE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .limits(wgpu::Limits {\n                max_immediate_size: 32,\n                ..Default::default()\n            }),\n    )\n    .run_async(partial_update_test);\n\nconst SHADER: &str = r#\"\n    struct Pc {\n        offset: u32,\n        vector: vec4f,\n    }\n\n    var<immediate> pc: Pc;\n\n    @group(0) @binding(0)\n    var<storage, read_write> output: array<vec4f>;\n\n    @compute @workgroup_size(1)\n    fn main() {\n        output[pc.offset] = pc.vector;\n    }\n\"#;\n\nasync fn partial_update_test(ctx: TestingContext) {\n    let sm = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"shader\"),\n            source: wgpu::ShaderSource::Wgsl(SHADER.into()),\n        });\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind_group_layout\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(16),\n                },\n                count: None,\n            }],\n        });\n\n    let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"gpu_buffer\"),\n        size: 32,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"cpu_buffer\"),\n        size: 32,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"bind_group\"),\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: gpu_buffer.as_entire_binding(),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 32,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &sm,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"encoder\"),\n        });\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"compute_pass\"),\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, &bind_group, &[]);\n\n        // -- Dispatch 0 --\n\n        // Dispatch number\n        cpass.set_immediates(0, bytemuck::bytes_of(&[0_u32]));\n        // Update the whole vector.\n        cpass.set_immediates(16, bytemuck::bytes_of(&[1.0_f32, 2.0, 3.0, 4.0]));\n        cpass.dispatch_workgroups(1, 1, 1);\n\n        // -- Dispatch 1 --\n\n        // Dispatch number\n        cpass.set_immediates(0, bytemuck::bytes_of(&[1_u32]));\n        // Update just the y component of the vector.\n        cpass.set_immediates(20, bytemuck::bytes_of(&[5.0_f32]));\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, 32);\n    ctx.queue.submit([encoder.finish()]);\n    cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = cpu_buffer.slice(..).get_mapped_range();\n\n    let floats: &[f32] = bytemuck::cast_slice(&data);\n\n    // first 4 floats the initial value\n    // second 4 floats the first update\n    assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 1.0, 5.0, 3.0, 4.0]);\n}\n#[gpu_test]\nstatic RENDER_PASS_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::IMMEDIATES | Features::VERTEX_WRITABLE_STORAGE)\n            .limits(wgpu::Limits {\n                max_immediate_size: 64,\n                ..Default::default()\n            }),\n    )\n    .run_async(move |ctx| async move {\n        for use_render_bundle in [false, true] {\n            render_pass_test(&ctx, use_render_bundle).await;\n        }\n    });\n\n// This shader simply  moves the values from vector_constants and immediates into the\n// result buffer.  It expects to be called 4 times (with vector_index in 0..4) with its\n// topology being PointList, so that each vertex shader call leads to exactly one fragment\n// call.\nconst SHADER2: &str = \"\n    const POSITION: vec4f = vec4f(0, 0, 0, 1);\n\n    struct ImmediateData {\n        vertex_constants: vec4i,\n        fragment_constants: vec4i,\n    }\n\n    var<immediate> immediates: ImmediateData;\n\n    @group(0) @binding(0) var<storage, read_write> result: array<i32>;\n\n    struct VertexOutput {\n        @builtin(position) position: vec4f,\n        @location(0) index: u32,\n    }\n\n    @vertex fn vertex(\n        @builtin(vertex_index) ix: u32,\n    ) -> VertexOutput {\n        result[ix] = immediates.vertex_constants[ix];\n        return VertexOutput(POSITION, ix);\n    }\n\n    @fragment fn fragment(\n        @location(0) ix: u32,\n     ) -> @location(0) vec4f {\n        result[ix + 4u] = immediates.fragment_constants[ix];\n        return vec4f();\n    }\n\";\n\nasync fn render_pass_test(ctx: &TestingContext, use_render_bundle: bool) {\n    let output_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"output buffer\"),\n        size: 8 * size_of::<u32>() as BufferAddress,\n        usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let cpu_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"cpu buffer\"),\n        size: output_buffer.size(),\n        usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    // We need an output texture, even though we're not ever going to look at it.\n    let output_texture = ctx.device.create_texture(&TextureDescriptor {\n        size: Extent3d {\n            width: 2,\n            height: 2,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8UnormSrgb,\n        usage: TextureUsages::RENDER_ATTACHMENT,\n        label: Some(\"Output Texture\"),\n        view_formats: &[],\n    });\n    let output_texture_view = output_texture.create_view(&Default::default());\n\n    let shader = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: Some(\"Shader\"),\n        source: ShaderSource::Wgsl(SHADER2.into()),\n    });\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::VERTEX_FRAGMENT,\n                ty: BindingType::Buffer {\n                    ty: BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n\n    let render_pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 8 * size_of::<u32>() as u32,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: Some(\"Render Pipeline\"),\n            layout: Some(&render_pipeline_layout),\n            vertex: VertexState {\n                module: &shader,\n                entry_point: None,\n                buffers: &[],\n                compilation_options: Default::default(),\n            },\n            fragment: Some(FragmentState {\n                module: &shader,\n                entry_point: None,\n                targets: &[Some(output_texture.format().into())],\n                compilation_options: Default::default(),\n            }),\n            primitive: PrimitiveState {\n                topology: PrimitiveTopology::PointList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let render_pass_desc = RenderPassDescriptor {\n        label: Some(\"Render Pass\"),\n        color_attachments: &[Some(RenderPassColorAttachment {\n            view: &output_texture_view,\n            depth_slice: None,\n            resolve_target: None,\n            ops: Operations {\n                load: LoadOp::Clear(Color::default()),\n                store: StoreOp::Store,\n            },\n        })],\n        ..Default::default()\n    };\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: Some(\"bind group\"),\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: output_buffer.as_entire_binding(),\n        }],\n    });\n\n    let data: Vec<i32> = (0..8).map(|i| (i * i) - 1).collect();\n\n    fn do_encoding<'a>(\n        encoder: &mut dyn RenderEncoder<'a>,\n        pipeline: &'a RenderPipeline,\n        bind_group: &'a BindGroup,\n        data: &'a Vec<i32>,\n    ) {\n        let data_as_u8: &[u8] = bytemuck::cast_slice(data.as_slice());\n        encoder.set_pipeline(pipeline);\n        encoder.set_immediates(0, data_as_u8);\n        encoder.set_bind_group(0, Some(bind_group), &[]);\n        encoder.draw(0..4, 0..1);\n    }\n\n    let mut command_encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut render_pass = command_encoder.begin_render_pass(&render_pass_desc);\n        if use_render_bundle {\n            // Execute the commands in a render_bundle_encoder.\n            let mut render_bundle_encoder =\n                ctx.device\n                    .create_render_bundle_encoder(&RenderBundleEncoderDescriptor {\n                        color_formats: &[Some(output_texture.format())],\n                        sample_count: 1,\n                        ..RenderBundleEncoderDescriptor::default()\n                    });\n            do_encoding(&mut render_bundle_encoder, &pipeline, &bind_group, &data);\n            let render_bundle = render_bundle_encoder.finish(&RenderBundleDescriptor::default());\n            render_pass.execute_bundles([&render_bundle]);\n        } else {\n            // Execute the commands directly.\n            do_encoding(&mut render_pass, &pipeline, &bind_group, &data);\n        }\n    }\n    // Move the result to the cpu buffer, so that we can read them.\n    command_encoder.copy_buffer_to_buffer(&output_buffer, 0, &cpu_buffer, 0, output_buffer.size());\n    let command_buffer = command_encoder.finish();\n    ctx.queue.submit([command_buffer]);\n    cpu_buffer.slice(..).map_async(MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n    let mapped_data = cpu_buffer.slice(..).get_mapped_range();\n    let result = bytemuck::cast_slice::<u8, i32>(&mapped_data).to_vec();\n    drop(mapped_data);\n    cpu_buffer.unmap();\n    assert_eq!(&result, &data);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/instance.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(INITIALIZE);\n}\n\n#[gpu_test]\nstatic INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|_ctx| {});\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/life_cycle.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        BUFFER_DESTROY,\n        TEXTURE_DESTROY,\n        BUFFER_DESTROY_BEFORE_SUBMIT,\n        TEXTURE_DESTROY_BEFORE_SUBMIT,\n    ]);\n}\n\n#[gpu_test]\nstatic BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"buffer\"),\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n\n        buffer.destroy();\n\n        buffer.destroy();\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        fail(\n            &ctx.device,\n            || {\n                buffer\n                    .slice(..)\n                    .map_async(wgpu::MapMode::Write, move |_| {});\n            },\n            Some(\"buffer with 'buffer' label has been destroyed\"),\n        );\n\n        buffer.destroy();\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        buffer.destroy();\n\n        buffer.destroy();\n\n        let descriptor = wgpu::BufferDescriptor {\n            label: None,\n            size: 256,\n            usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        };\n\n        // Scopes to mix up the drop/poll ordering.\n        {\n            let buffer = ctx.device.create_buffer(&descriptor);\n            buffer.destroy();\n            let buffer = ctx.device.create_buffer(&descriptor);\n            buffer.destroy();\n        }\n        let buffer = ctx.device.create_buffer(&descriptor);\n        buffer.destroy();\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let buffer = ctx.device.create_buffer(&descriptor);\n        buffer.destroy();\n        {\n            let buffer = ctx.device.create_buffer(&descriptor);\n            buffer.destroy();\n            let buffer = ctx.device.create_buffer(&descriptor);\n            buffer.destroy();\n            let buffer = ctx.device.create_buffer(&descriptor);\n            ctx.async_poll(wgpu::PollType::wait_indefinitely())\n                .await\n                .unwrap();\n            buffer.destroy();\n        }\n        let buffer = ctx.device.create_buffer(&descriptor);\n        buffer.destroy();\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    });\n\n#[gpu_test]\nstatic TEXTURE_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 128,\n                height: 128,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1, // multisampling is not supported for clear\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Snorm,\n            usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        texture.destroy();\n\n        texture.destroy();\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        texture.destroy();\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        texture.destroy();\n\n        texture.destroy();\n    });\n\n// Test that destroying a buffer between command buffer recording and\n// submission fails gracefully.\n#[gpu_test]\nstatic BUFFER_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let buffer_source = ctx\n            .device\n            .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                label: None,\n                contents: &[0u8; 4],\n                usage: wgpu::BufferUsages::COPY_SRC,\n            });\n        let buffer_dest = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 4,\n            usage: wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        encoder.copy_buffer_to_buffer(&buffer_source, 0, &buffer_dest, 0, 4);\n\n        buffer_source.destroy();\n        buffer_dest.destroy();\n\n        let cmd_buffer = encoder.finish();\n\n        fail(\n            &ctx.device,\n            || ctx.queue.submit([cmd_buffer]),\n            Some(\"Buffer with '' label has been destroyed\"),\n        );\n    });\n\n// Test that destroying a texture between command buffer recording and\n// submission fails gracefully.\n#[gpu_test]\nstatic TEXTURE_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let descriptor = wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 128,\n                height: 128,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1, // multisampling is not supported for clear\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Snorm,\n            usage: wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::COPY_SRC\n                | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        };\n\n        let texture_1 = ctx.device.create_texture(&descriptor);\n        let texture_2 = ctx.device.create_texture(&descriptor);\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        encoder.copy_texture_to_texture(\n            wgpu::TexelCopyTextureInfo {\n                texture: &texture_1,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyTextureInfo {\n                texture: &texture_2,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::Extent3d {\n                width: 128,\n                height: 128,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        texture_1.destroy();\n        texture_2.destroy();\n\n        let cmd_buffer = encoder.finish();\n\n        fail(\n            &ctx.device,\n            || ctx.queue.submit([cmd_buffer]),\n            Some(\"Texture with '' label has been destroyed\"),\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/main.rs",
    "content": "mod regression {\n    pub mod issue_3349;\n    pub mod issue_3457;\n    pub mod issue_4024;\n    pub mod issue_4122;\n    pub mod issue_4485;\n    pub mod issue_4514;\n    pub mod issue_5553;\n    pub mod issue_6317;\n    pub mod issue_6467;\n    pub mod issue_6827;\n}\n\nmod bgra8unorm_storage;\nmod bind_group_layout_dedup;\nmod bind_groups;\nmod binding_array;\nmod buffer;\nmod buffer_copy;\nmod buffer_usages;\nmod clear_texture;\nmod clip_distances;\nmod cloneable_types;\nmod compute_pass_ownership;\nmod create_surface_error;\nmod device;\nmod dispatch_workgroups_indirect;\nmod draw_index;\nmod draw_indirect;\nmod dual_source_blending;\nmod encoder;\nmod external_image_copy;\nmod external_texture;\nmod float32_filterable;\nmod image_atomics;\nmod immediates;\nmod instance;\nmod life_cycle;\nmod mem_leaks;\nmod mesh_shader;\nmod multiview;\nmod naga_capabilities;\nmod occlusion_query;\nmod oob_indexing;\nmod oom;\nmod pass_ops;\nmod passthrough;\nmod per_vertex;\nmod pipeline;\nmod pipeline_cache;\nmod planar_texture;\nmod poll;\nmod primitive_index;\nmod query_set;\nmod queue_transfer;\nmod ray_tracing;\nmod render_pass_ownership;\nmod render_target;\nmod resource_descriptor_accessor;\nmod resource_error;\nmod samplers;\nmod scissor_tests;\nmod shader;\nmod shader_barycentric;\nmod shader_primitive_index;\nmod shader_view_format;\nmod subgroup_operations;\nmod texture_binding;\nmod texture_blit;\nmod texture_bounds;\nmod texture_view_creation;\nmod timestamp_normalization;\nmod timestamp_query;\nmod transfer;\nmod transient;\nmod transition_resources;\nmod vertex_formats;\nmod vertex_indices;\nmod vertex_state;\nmod write_texture;\nmod zero_init_texture_after_discard;\n\nfn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {\n    let mut tests = Vec::new();\n\n    bgra8unorm_storage::all_tests(&mut tests);\n    bind_group_layout_dedup::all_tests(&mut tests);\n    bind_groups::all_tests(&mut tests);\n    binding_array::all_tests(&mut tests);\n    buffer_copy::all_tests(&mut tests);\n    buffer_usages::all_tests(&mut tests);\n    buffer::all_tests(&mut tests);\n    clear_texture::all_tests(&mut tests);\n    clip_distances::all_tests(&mut tests);\n    cloneable_types::all_tests(&mut tests);\n    compute_pass_ownership::all_tests(&mut tests);\n    device::all_tests(&mut tests);\n    dispatch_workgroups_indirect::all_tests(&mut tests);\n    draw_index::all_tests(&mut tests);\n    draw_indirect::all_tests(&mut tests);\n    dual_source_blending::all_tests(&mut tests);\n    encoder::all_tests(&mut tests);\n    external_texture::all_tests(&mut tests);\n    float32_filterable::all_tests(&mut tests);\n    image_atomics::all_tests(&mut tests);\n    instance::all_tests(&mut tests);\n    life_cycle::all_tests(&mut tests);\n    mem_leaks::all_tests(&mut tests);\n    mesh_shader::all_tests(&mut tests);\n    multiview::all_tests(&mut tests);\n    occlusion_query::all_tests(&mut tests);\n    oob_indexing::all_tests(&mut tests);\n    oom::all_tests(&mut tests);\n    pass_ops::all_tests(&mut tests);\n    passthrough::all_tests(&mut tests);\n    per_vertex::all_tests(&mut tests);\n    pipeline_cache::all_tests(&mut tests);\n    pipeline::all_tests(&mut tests);\n    planar_texture::all_tests(&mut tests);\n    poll::all_tests(&mut tests);\n    primitive_index::all_tests(&mut tests);\n    immediates::all_tests(&mut tests);\n    query_set::all_tests(&mut tests);\n    queue_transfer::all_tests(&mut tests);\n    ray_tracing::all_tests(&mut tests);\n    regression::issue_3349::all_tests(&mut tests);\n    regression::issue_3457::all_tests(&mut tests);\n    regression::issue_4024::all_tests(&mut tests);\n    regression::issue_4122::all_tests(&mut tests);\n    regression::issue_4485::all_tests(&mut tests);\n    regression::issue_4514::all_tests(&mut tests);\n    regression::issue_5553::all_tests(&mut tests);\n    regression::issue_6317::all_tests(&mut tests);\n    regression::issue_6467::all_tests(&mut tests);\n    regression::issue_6827::all_tests(&mut tests);\n    render_pass_ownership::all_tests(&mut tests);\n    render_target::all_tests(&mut tests);\n    resource_descriptor_accessor::all_tests(&mut tests);\n    resource_error::all_tests(&mut tests);\n    samplers::all_tests(&mut tests);\n    scissor_tests::all_tests(&mut tests);\n    shader_primitive_index::all_tests(&mut tests);\n    shader_barycentric::all_tests(&mut tests);\n    shader_view_format::all_tests(&mut tests);\n    shader::all_tests(&mut tests);\n    subgroup_operations::all_tests(&mut tests);\n    texture_binding::all_tests(&mut tests);\n    texture_blit::all_tests(&mut tests);\n    texture_bounds::all_tests(&mut tests);\n    texture_view_creation::all_tests(&mut tests);\n    timestamp_normalization::all_tests(&mut tests);\n    timestamp_query::all_tests(&mut tests);\n    transfer::all_tests(&mut tests);\n    transient::all_tests(&mut tests);\n    transition_resources::all_tests(&mut tests);\n    vertex_formats::all_tests(&mut tests);\n    vertex_indices::all_tests(&mut tests);\n    vertex_state::all_tests(&mut tests);\n    write_texture::all_tests(&mut tests);\n    zero_init_texture_after_discard::all_tests(&mut tests);\n    naga_capabilities::all_tests(&mut tests);\n\n    tests\n}\n\nwgpu_test::gpu_test_main!(all_tests());\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/mem_leaks.rs",
    "content": "#[allow(\n    clippy::allow_attributes,\n    reason = \"Using expect is going to be much more verbose\"\n)]\n#[allow(clippy::ptr_arg)]\npub fn all_tests(_vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    #[cfg(any(\n        not(target_arch = \"wasm32\"),\n        target_os = \"emscripten\",\n        feature = \"webgl\"\n    ))]\n    _vec.push(SIMPLE_DRAW_CHECK_MEM_LEAKS);\n}\n\n#[cfg(any(\n    not(target_arch = \"wasm32\"),\n    target_os = \"emscripten\",\n    feature = \"webgl\"\n))]\nasync fn draw_test_with_reports(\n    ctx: wgpu_test::TestingContext,\n    expected: &[u32],\n    function: impl FnOnce(&mut wgpu::RenderPass<'_>),\n) {\n    use std::num::NonZeroU64;\n\n    use wgpu::util::DeviceExt;\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.devices.num_allocated, 1);\n    assert_eq!(report.queues.num_allocated, 1);\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"./vertex_indices/draw.vert.wgsl\"));\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.shader_modules.num_allocated, 1);\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(4),\n                },\n                visibility: wgpu::ShaderStages::VERTEX,\n                count: None,\n            }],\n        });\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 0);\n    assert_eq!(report.bind_groups.num_allocated, 0);\n    assert_eq!(report.bind_group_layouts.num_allocated, 1);\n\n    let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4 * expected.len() as u64,\n        usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::STORAGE,\n        mapped_at_creation: false,\n    });\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n\n    let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.bind_groups.num_allocated, 1);\n    assert_eq!(report.bind_group_layouts.num_allocated, 1);\n\n    let ppl = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.pipeline_layouts.num_allocated, 1);\n    assert_eq!(report.render_pipelines.num_allocated, 0);\n    assert_eq!(report.compute_pipelines.num_allocated, 0);\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&ppl),\n            vertex: wgpu::VertexState {\n                buffers: &[],\n                module: &shader,\n                entry_point: Some(\"vs_main_builtin\"),\n                compilation_options: Default::default(),\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.bind_groups.num_allocated, 1);\n    assert_eq!(report.bind_group_layouts.num_allocated, 1);\n    assert_eq!(report.shader_modules.num_allocated, 1);\n    assert_eq!(report.pipeline_layouts.num_allocated, 1);\n    assert_eq!(report.render_pipelines.num_allocated, 1);\n    assert_eq!(report.compute_pipelines.num_allocated, 0);\n\n    drop(shader);\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.shader_modules.num_allocated, 0);\n    assert_eq!(report.shader_modules.num_kept_from_user, 0);\n    assert_eq!(report.textures.num_allocated, 0);\n    assert_eq!(report.texture_views.num_allocated, 0);\n\n    let texture = ctx.device.create_texture_with_data(\n        &ctx.queue,\n        &wgpu::TextureDescriptor {\n            label: Some(\"dummy\"),\n            size: wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,\n            view_formats: &[],\n        },\n        wgpu::util::TextureDataOrder::LayerMajor,\n        &[0, 0, 0, 1],\n    );\n    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.texture_views.num_allocated, 1);\n    assert_eq!(report.textures.num_allocated, 1);\n\n    drop(texture);\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.texture_views.num_allocated, 1);\n    assert_eq!(report.texture_views.num_kept_from_user, 1);\n    // TextureViews in `wgpu` have a reference to the texture.\n    assert_eq!(report.textures.num_allocated, 1);\n    assert_eq!(report.textures.num_kept_from_user, 1);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.command_encoders.num_allocated, 1);\n    assert_eq!(report.buffers.num_allocated, 1);\n\n    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            ops: wgpu::Operations::default(),\n            resolve_target: None,\n            view: &texture_view,\n            depth_slice: None,\n        })],\n        depth_stencil_attachment: None,\n        timestamp_writes: None,\n        occlusion_query_set: None,\n        multiview_mask: None,\n    });\n\n    rpass.set_pipeline(&pipeline);\n    rpass.set_bind_group(0, &bg, &[]);\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.buffers.num_allocated, 1);\n    assert_eq!(report.bind_groups.num_allocated, 1);\n    assert_eq!(report.bind_group_layouts.num_allocated, 1);\n    assert_eq!(report.pipeline_layouts.num_allocated, 1);\n    assert_eq!(report.render_pipelines.num_allocated, 1);\n    assert_eq!(report.compute_pipelines.num_allocated, 0);\n    assert_eq!(report.command_encoders.num_allocated, 1);\n    assert_eq!(report.render_bundles.num_allocated, 0);\n    assert_eq!(report.texture_views.num_allocated, 1);\n    assert_eq!(report.textures.num_allocated, 1);\n\n    function(&mut rpass);\n\n    drop(rpass);\n    drop(pipeline);\n    drop(texture_view);\n    drop(ppl);\n    drop(bgl);\n    drop(bg);\n    drop(buffer);\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.command_encoders.num_kept_from_user, 1);\n    assert_eq!(report.render_pipelines.num_kept_from_user, 0);\n    assert_eq!(report.pipeline_layouts.num_kept_from_user, 0);\n    assert_eq!(report.bind_group_layouts.num_kept_from_user, 0);\n    assert_eq!(report.bind_groups.num_kept_from_user, 0);\n    assert_eq!(report.buffers.num_kept_from_user, 0);\n    assert_eq!(report.texture_views.num_kept_from_user, 0);\n    assert_eq!(report.textures.num_kept_from_user, 0);\n    assert_eq!(report.command_encoders.num_allocated, 1);\n    assert_eq!(report.render_pipelines.num_allocated, 0);\n    assert_eq!(report.pipeline_layouts.num_allocated, 0);\n    assert_eq!(report.bind_group_layouts.num_allocated, 0);\n    assert_eq!(report.bind_groups.num_allocated, 0);\n    assert_eq!(report.buffers.num_allocated, 0);\n    assert_eq!(report.texture_views.num_allocated, 0);\n    assert_eq!(report.textures.num_allocated, 0);\n\n    let command_buffer = encoder.finish();\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.command_encoders.num_allocated, 0);\n    assert_eq!(report.command_buffers.num_allocated, 1);\n\n    let submit_index = ctx.queue.submit(Some(command_buffer));\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n    assert_eq!(report.command_buffers.num_allocated, 0);\n\n    ctx.async_poll(wgpu::PollType::Wait {\n        submission_index: Some(submit_index),\n        timeout: None,\n    })\n    .await\n    .unwrap();\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n\n    assert_eq!(report.render_pipelines.num_allocated, 0);\n    assert_eq!(report.bind_groups.num_allocated, 0);\n    assert_eq!(report.bind_group_layouts.num_allocated, 0);\n    assert_eq!(report.pipeline_layouts.num_allocated, 0);\n    assert_eq!(report.texture_views.num_allocated, 0);\n    assert_eq!(report.textures.num_allocated, 0);\n    assert_eq!(report.buffers.num_allocated, 0);\n\n    drop(ctx.queue);\n    drop(ctx.device);\n    drop(ctx.adapter);\n\n    let global_report = ctx.instance.generate_report().unwrap();\n    let report = global_report.hub_report();\n\n    assert_eq!(report.queues.num_kept_from_user, 0);\n    assert_eq!(report.textures.num_kept_from_user, 0);\n    assert_eq!(report.devices.num_kept_from_user, 0);\n    assert_eq!(report.queues.num_allocated, 0);\n    assert_eq!(report.buffers.num_allocated, 0);\n    assert_eq!(report.textures.num_allocated, 0);\n    assert_eq!(report.texture_views.num_allocated, 0);\n    assert_eq!(report.devices.num_allocated, 0);\n}\n\n#[cfg(any(\n    not(target_arch = \"wasm32\"),\n    target_os = \"emscripten\",\n    feature = \"webgl\"\n))]\n#[wgpu_test::gpu_test]\nstatic SIMPLE_DRAW_CHECK_MEM_LEAKS: wgpu_test::GpuTestConfiguration =\n    wgpu_test::GpuTestConfiguration::new()\n        .parameters(\n            wgpu_test::TestParameters::default()\n                .test_features_limits()\n                .features(wgpu::Features::VERTEX_WRITABLE_STORAGE)\n                .enable_noop(),\n        )\n        .run_async(|ctx| {\n            draw_test_with_reports(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {\n                cmb.draw(0..6, 0..1);\n            })\n        });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/mesh_shader/basic.hlsl",
    "content": "struct OutVertex\n{\n    float4 Position : SV_POSITION;\n    float4 Color : COLOR;\n};\nstruct InVertex\n{\n    float4 Color : COLOR;\n};\n\nstatic 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) };\nstatic const float4 colors[3] = { float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.) };\n\nstruct EmptyPayload\n{\n    uint _nullField;\n};\ngroupshared EmptyPayload _emptyPayload;\n\n[numthreads(1, 1, 1)]\nvoid Task()\n{\n    DispatchMesh(1, 1, 1, _emptyPayload);\n}\n\n[outputtopology(\"triangle\")]\n[numthreads(1, 1, 1)]\nvoid Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], in payload EmptyPayload _emptyPayload)\n{\n    SetMeshOutputCounts(3, 1);\n\n    vertices[0].Position = positions[0];\n    vertices[1].Position = positions[1];\n    vertices[2].Position = positions[2];\n\n    vertices[0].Color = colors[0];\n    vertices[1].Color = colors[1];\n    vertices[2].Color = colors[2];\n\n    triangles[0] = uint3(0, 1, 2);\n}\n\n[outputtopology(\"triangle\")]\n[numthreads(1, 1, 1)]\nvoid MeshNoTask(out indices uint3 triangles[1], out vertices OutVertex vertices[3])\n{\n    SetMeshOutputCounts(3, 1);\n\n    vertices[0].Position = positions[0];\n    vertices[1].Position = positions[1];\n    vertices[2].Position = positions[2];\n\n    vertices[0].Color = colors[0];\n    vertices[1].Color = colors[1];\n    vertices[2].Color = colors[2];\n\n    triangles[0] = uint3(0, 1, 2);\n}\n\nfloat4 Frag(InVertex vertex) : SV_Target\n{\n    return vertex.Color;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/mesh_shader/mod.rs",
    "content": "use std::hash::{DefaultHasher, Hash, Hasher};\n\nuse wgpu::util::DeviceExt;\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        MESH_PIPELINE_BASIC_MESH,\n        MESH_PIPELINE_BASIC_TASK_MESH,\n        MESH_PIPELINE_BASIC_MESH_FRAG,\n        MESH_PIPELINE_BASIC_TASK_MESH_FRAG,\n        MESH_DRAW,\n        MESH_DRAW_NO_TASK,\n        MESH_DRAW_DIVERGENT,\n        MESH_DRAW_INDIRECT,\n        MESH_MULTI_DRAW_INDIRECT,\n        MESH_MULTI_DRAW_INDIRECT_COUNT,\n        MESH_PIPELINE_BASIC_MESH_NO_DRAW,\n        MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW,\n    ]);\n}\n\n// Same as in mesh shader example\nfn compile_wgsl(device: &wgpu::Device) -> wgpu::ShaderModule {\n    device.create_shader_module(wgpu::ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(include_str!(\"shader.wgsl\").into()),\n    })\n}\n\nfn compile_hlsl(\n    device: &wgpu::Device,\n    entry: &str,\n    stage_str: &str,\n    test_name: &str,\n) -> wgpu::ShaderModule {\n    // Each test needs its own files\n    let out_path = format!(\n        \"{}/tests/wgpu-gpu/mesh_shader/{test_name}.{stage_str}.cso\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n    let cmd = std::process::Command::new(\"dxc\")\n        .args([\n            \"-T\",\n            &format!(\"{stage_str}_6_5\"),\n            \"-E\",\n            entry,\n            &format!(\n                \"{}/tests/wgpu-gpu/mesh_shader/basic.hlsl\",\n                env!(\"CARGO_MANIFEST_DIR\")\n            ),\n            \"-Fo\",\n            &out_path,\n        ])\n        .output()\n        .unwrap();\n    if !cmd.status.success() {\n        panic!(\"DXC failed:\\n{}\", String::from_utf8(cmd.stderr).unwrap());\n    }\n    let file = std::fs::read(&out_path).unwrap();\n    std::fs::remove_file(out_path).unwrap();\n    unsafe {\n        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n            label: None,\n            num_workgroups: (1, 1, 1),\n            dxil: Some(std::borrow::Cow::Owned(file)),\n            ..Default::default()\n        })\n    }\n}\n\nfn compile_msl(device: &wgpu::Device) -> wgpu::ShaderModule {\n    unsafe {\n        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n            label: None,\n            msl: Some(std::borrow::Cow::Borrowed(include_str!(\"shader.metal\"))),\n            num_workgroups: (1, 1, 1),\n            ..Default::default()\n        })\n    }\n}\nstruct Shaders {\n    ts: Option<wgpu::ShaderModule>,\n    ms: wgpu::ShaderModule,\n    fs: Option<wgpu::ShaderModule>,\n    ts_name: &'static str,\n    ms_name: &'static str,\n    fs_name: &'static str,\n}\nfn get_shaders(\n    device: &wgpu::Device,\n    backend: wgpu::Backend,\n    test_name: &str,\n    info: &MeshPipelineTestInfo,\n) -> Shaders {\n    if info.divergent && info.use_task {\n        unreachable!();\n    }\n    // In the case that the platform does support mesh shaders, the dummy\n    // shader is used to avoid requiring PASSTHROUGH_SHADERS.\n    match backend {\n        wgpu::Backend::Vulkan => {\n            let compiled = compile_wgsl(device);\n            Shaders {\n                ts: info.use_task.then_some(compiled.clone()),\n                ms: compiled.clone(),\n                fs: info.use_frag.then_some(compiled),\n                ts_name: \"ts_main\",\n                ms_name: if info.divergent {\n                    \"ms_divergent\"\n                } else if info.use_task {\n                    \"ms_main\"\n                } else {\n                    \"ms_no_ts\"\n                },\n                fs_name: \"fs_main\",\n            }\n        }\n        wgpu::Backend::Dx12 => Shaders {\n            ts: info\n                .use_task\n                .then(|| compile_hlsl(device, \"Task\", \"as\", test_name)),\n            ms: compile_hlsl(\n                device,\n                if info.use_task { \"Mesh\" } else { \"MeshNoTask\" },\n                \"ms\",\n                test_name,\n            ),\n            fs: info\n                .use_frag\n                .then(|| compile_hlsl(device, \"Frag\", \"ps\", test_name)),\n            ts_name: \"main\",\n            ms_name: \"main\",\n            fs_name: \"main\",\n        },\n        wgpu::Backend::Metal => {\n            let compiled = compile_msl(device);\n            Shaders {\n                ts: info.use_task.then_some(compiled.clone()),\n                ms: compiled.clone(),\n                fs: info.use_frag.then_some(compiled),\n                ts_name: \"taskShader\",\n                ms_name: if info.use_task {\n                    \"meshShader\"\n                } else {\n                    \"meshNoTaskShader\"\n                },\n                fs_name: \"fragShader\",\n            }\n        }\n        _ => unreachable!(),\n    }\n}\n\nfn create_depth(\n    device: &wgpu::Device,\n) -> (wgpu::Texture, wgpu::TextureView, wgpu::DepthStencilState) {\n    let image_size = wgpu::Extent3d {\n        width: 64,\n        height: 64,\n        depth_or_array_layers: 1,\n    };\n    let depth_texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: image_size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Depth32Float,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    });\n    let depth_view = depth_texture.create_view(&Default::default());\n    let state = wgpu::DepthStencilState {\n        format: wgpu::TextureFormat::Depth32Float,\n        depth_write_enabled: Some(true),\n        depth_compare: Some(wgpu::CompareFunction::Less), // 1.\n        stencil: wgpu::StencilState::default(),           // 2.\n        bias: wgpu::DepthBiasState::default(),\n    };\n    (depth_texture, depth_view, state)\n}\n\nstruct MeshPipelineTestInfo {\n    use_task: bool,\n    use_frag: bool,\n    draw: bool,\n    divergent: bool,\n}\n\nfn hash_testing_context(ctx: &TestingContext) -> u64 {\n    let mut hasher = DefaultHasher::new();\n    ctx.hash(&mut hasher);\n    hasher.finish()\n}\n\nfn mesh_pipeline_build(ctx: &TestingContext, info: MeshPipelineTestInfo) {\n    let backend = ctx.adapter.get_info().backend;\n    let device = &ctx.device;\n    let (_depth_image, depth_view, depth_state) = create_depth(device);\n\n    let test_hash = hash_testing_context(ctx).to_string();\n    let Shaders {\n        ts,\n        ms,\n        fs,\n        ts_name,\n        ms_name,\n        fs_name,\n    } = get_shaders(device, backend, &test_hash, &info);\n    let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[],\n        immediate_size: 0,\n    });\n    let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {\n        label: None,\n        layout: Some(&layout),\n        task: ts.as_ref().map(|task| wgpu::TaskState {\n            module: task,\n            entry_point: Some(ts_name),\n            compilation_options: Default::default(),\n        }),\n        mesh: wgpu::MeshState {\n            module: &ms,\n            entry_point: Some(ms_name),\n            compilation_options: Default::default(),\n        },\n        fragment: fs.as_ref().map(|frag| wgpu::FragmentState {\n            module: frag,\n            entry_point: Some(fs_name),\n            targets: &[],\n            compilation_options: Default::default(),\n        }),\n        primitive: wgpu::PrimitiveState {\n            cull_mode: Some(wgpu::Face::Back),\n            ..Default::default()\n        },\n        depth_stencil: Some(depth_state),\n        multisample: Default::default(),\n        multiview: None,\n        cache: None,\n    });\n    if info.draw {\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &depth_view,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Store,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            pass.set_pipeline(&pipeline);\n            pass.draw_mesh_tasks(1, 1, 1);\n        }\n        ctx.queue.submit(Some(encoder.finish()));\n        ctx.device\n            .poll(wgpu::PollType::wait_indefinitely())\n            .unwrap();\n    }\n}\n\n#[derive(PartialEq, Eq, Clone, Copy)]\npub enum DrawType {\n    #[allow(dead_code)]\n    Standard,\n    Indirect,\n    MultiIndirect,\n    MultiIndirectCount,\n}\n\nfn mesh_draw(ctx: &TestingContext, draw_type: DrawType, info: MeshPipelineTestInfo) {\n    let backend = ctx.adapter.get_info().backend;\n    let device = &ctx.device;\n    let (_depth_image, depth_view, depth_state) = create_depth(device);\n    let test_hash = hash_testing_context(ctx).to_string();\n\n    let Shaders {\n        ts,\n        ms,\n        fs,\n        ts_name,\n        ms_name,\n        fs_name,\n    } = get_shaders(device, backend, &test_hash, &info);\n    let frag = fs.unwrap();\n    let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n        label: None,\n        bind_group_layouts: &[],\n        immediate_size: 0,\n    });\n    let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {\n        label: None,\n        layout: Some(&layout),\n        task: ts.as_ref().map(|task| wgpu::TaskState {\n            module: task,\n            entry_point: Some(ts_name),\n            compilation_options: Default::default(),\n        }),\n        mesh: wgpu::MeshState {\n            module: &ms,\n            entry_point: Some(ms_name),\n            compilation_options: Default::default(),\n        },\n        fragment: Some(wgpu::FragmentState {\n            module: &frag,\n            entry_point: Some(fs_name),\n            targets: &[],\n            compilation_options: Default::default(),\n        }),\n        primitive: wgpu::PrimitiveState {\n            cull_mode: Some(wgpu::Face::Back),\n            ..Default::default()\n        },\n        depth_stencil: Some(depth_state),\n        multisample: Default::default(),\n        multiview: None,\n        cache: None,\n    });\n    let buffer = match draw_type {\n        DrawType::Standard => None,\n        DrawType::Indirect | DrawType::MultiIndirect | DrawType::MultiIndirectCount => Some(\n            device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n                label: None,\n                usage: wgpu::BufferUsages::INDIRECT,\n                contents: bytemuck::bytes_of(&[1u32; 4]),\n            }),\n        ),\n    };\n    let count_buffer = match draw_type {\n        DrawType::MultiIndirectCount => Some(device.create_buffer_init(\n            &wgpu::util::BufferInitDescriptor {\n                label: None,\n                usage: wgpu::BufferUsages::INDIRECT,\n                contents: bytemuck::bytes_of(&[1u32; 1]),\n            },\n        )),\n        _ => None,\n    };\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[],\n            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                view: &depth_view,\n                depth_ops: Some(wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(1.0),\n                    store: wgpu::StoreOp::Store,\n                }),\n                stencil_ops: None,\n            }),\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        pass.set_pipeline(&pipeline);\n        match draw_type {\n            DrawType::Standard => pass.draw_mesh_tasks(1, 1, 1),\n            DrawType::Indirect => pass.draw_mesh_tasks_indirect(buffer.as_ref().unwrap(), 0),\n            DrawType::MultiIndirect => {\n                pass.multi_draw_mesh_tasks_indirect(buffer.as_ref().unwrap(), 0, 1)\n            }\n            DrawType::MultiIndirectCount => pass.multi_draw_mesh_tasks_indirect_count(\n                buffer.as_ref().unwrap(),\n                0,\n                count_buffer.as_ref().unwrap(),\n                0,\n                1,\n            ),\n        }\n    }\n    ctx.queue.submit(Some(encoder.finish()));\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n}\n\nfn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration {\n    GpuTestConfiguration::new().parameters(\n        TestParameters::default()\n            .instance_flags(wgpu::InstanceFlags::GPU_BASED_VALIDATION)\n            .features(\n                wgpu::Features::EXPERIMENTAL_MESH_SHADER\n                    | wgpu::Features::PASSTHROUGH_SHADERS\n                    | match draw_type {\n                        DrawType::Standard | DrawType::Indirect | DrawType::MultiIndirect => {\n                            wgpu::Features::empty()\n                        }\n                        DrawType::MultiIndirectCount => wgpu::Features::MULTI_DRAW_INDIRECT_COUNT,\n                    },\n            )\n            .limits(wgpu::Limits::default().using_recommended_minimum_mesh_shader_values())\n            .skip(wgpu_test::FailureCase {\n                backends: None,\n                // Skip Mesa because LLVMPIPE has what is believed to be a driver bug\n                vendor: Some(0x10005),\n                adapter: None,\n                driver: None,\n                reasons: vec![],\n                behavior: wgpu_test::FailureBehavior::Ignore,\n            }),\n    )\n}\n\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_MESH: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: false,\n                use_frag: false,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_TASK_MESH: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: false,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_MESH_FRAG: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: false,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_MESH_NO_DRAW: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: false,\n                use_frag: false,\n                draw: false,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_pipeline_build(\n            &ctx,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: false,\n                divergent: false,\n            },\n        );\n    });\n\n// Mesh draw\n#[gpu_test]\npub static MESH_DRAW: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::Standard,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_DRAW_NO_TASK: GpuTestConfiguration = default_gpu_test_config(DrawType::Standard)\n    .run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::Standard,\n            MeshPipelineTestInfo {\n                use_task: false,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_DRAW_DIVERGENT: GpuTestConfiguration = default_gpu_test_config(DrawType::Standard)\n    .run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::Standard,\n            MeshPipelineTestInfo {\n                use_task: false,\n                use_frag: true,\n                draw: true,\n                divergent: true,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_DRAW_INDIRECT: GpuTestConfiguration = default_gpu_test_config(DrawType::Indirect)\n    .run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::Indirect,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_MULTI_DRAW_INDIRECT: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::MultiIndirect).run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::MultiIndirect,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n#[gpu_test]\npub static MESH_MULTI_DRAW_INDIRECT_COUNT: GpuTestConfiguration =\n    default_gpu_test_config(DrawType::MultiIndirectCount).run_sync(|ctx| {\n        mesh_draw(\n            &ctx,\n            DrawType::MultiIndirectCount,\n            MeshPipelineTestInfo {\n                use_task: true,\n                use_frag: true,\n                draw: true,\n                divergent: false,\n            },\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/mesh_shader/shader.metal",
    "content": "using namespace metal;\n\nstruct OutVertex {\n    float4 Position [[position]];\n    float4 Color [[user(locn0)]];\n};\n\nstruct OutPrimitive {\n    float4 ColorMask [[flat]] [[user(locn1)]];\n    bool CullPrimitive [[primitive_culled]];\n};\n\nstruct InVertex {\n};\n\nstruct InPrimitive {\n    float4 ColorMask [[flat]] [[user(locn1)]];\n};\n\nstruct FragmentIn {\n    float4 Color [[user(locn0)]];\n    float4 ColorMask [[flat]] [[user(locn1)]];\n};\n\nstruct PayloadData {\n    float4 ColorMask;\n    bool Visible;\n};\n\nusing Meshlet = metal::mesh<OutVertex, OutPrimitive, 3, 1, topology::triangle>;\n\n\nconstant float4 positions[3] = {\n    float4(0.0, 1.0, 0.0, 1.0),\n    float4(-1.0, -1.0, 0.0, 1.0),\n    float4(1.0, -1.0, 0.0, 1.0)\n};\n\nconstant float4 colors[3] = {\n    float4(0.0, 1.0, 0.0, 1.0),\n    float4(0.0, 0.0, 1.0, 1.0),\n    float4(1.0, 0.0, 0.0, 1.0)\n};\n\n\n[[object]]\nvoid taskShader(uint3 tid [[thread_position_in_grid]], object_data PayloadData &outPayload [[payload]], mesh_grid_properties grid) {\n    outPayload.ColorMask = float4(1.0, 1.0, 0.0, 1.0);\n    outPayload.Visible = true;\n    grid.set_threadgroups_per_grid(uint3(3, 1, 1));\n}\n\n[[mesh]]\nvoid meshShader(\n    object_data PayloadData const& payload [[payload]],\n    Meshlet out\n)\n{\n    out.set_primitive_count(1);\n\n    for(int i = 0;i < 3;i++) {\n        OutVertex vert;\n        vert.Position = positions[i];\n        vert.Color = colors[i] * payload.ColorMask;\n        out.set_vertex(i, vert);\n        out.set_index(i, i);\n    }\n\n    OutPrimitive prim;\n    prim.ColorMask = float4(1.0, 0.0, 0.0, 1.0);\n    prim.CullPrimitive = !payload.Visible;\n    out.set_primitive(0, prim);\n}\n\n[[mesh]]\nvoid meshNoTaskShader(\n    Meshlet out\n)\n{\n    out.set_primitive_count(1);\n\n    for(int i = 0;i < 3;i++) {\n        OutVertex vert;\n        vert.Position = positions[i];\n        vert.Color = colors[i];\n        out.set_vertex(i, vert);\n        out.set_index(i, i);\n    }\n\n    OutPrimitive prim;\n    prim.ColorMask = float4(1.0, 0.0, 0.0, 1.0);\n    prim.CullPrimitive = false;\n    out.set_primitive(0, prim);\n}\n\nfragment float4 fragShader(FragmentIn data [[stage_in]]) {\n    return data.Color * data.ColorMask;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/mesh_shader/shader.wgsl",
    "content": "enable wgpu_mesh_shader;\n\nconst positions = array(\n    vec4(0., 1., 0., 1.),\n    vec4(-1., -1., 0., 1.),\n    vec4(1., -1., 0., 1.)\n);\nconst colors = array(\n    vec4(0., 1., 0., 1.),\n    vec4(0., 0., 1., 1.),\n    vec4(1., 0., 0., 1.)\n);\n\nstruct TaskPayload {\n    colorMask: vec4<f32>,\n    visible: bool,\n}\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) color: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,\n    @builtin(cull_primitive) cull: bool,\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\nstruct PrimitiveInput {\n    @per_primitive @location(1) colorMask: vec4<f32>,\n}\n\nvar<task_payload> taskPayload: TaskPayload;\nvar<workgroup> workgroupData: f32;\n\n@task\n@payload(taskPayload)\n@workgroup_size(1)\nfn ts_main() -> @builtin(mesh_task_size) vec3<u32> {\n    workgroupData = 1.0;\n    taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);\n    taskPayload.visible = true;\n    return vec3(1, 1, 1);\n}\n\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 1>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitive_count) primitive_count: u32,\n}\n\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@payload(taskPayload)\n@workgroup_size(1)\nfn ms_main() {\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    workgroupData = 2.0;\n\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0] * taskPayload.colorMask;\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1] * taskPayload.colorMask;\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2] * taskPayload.colorMask;\n\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    mesh_output.primitives[0].cull = !taskPayload.visible;\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n\n// Don't use task payload if no task shader is present\n@mesh(mesh_output)\n@workgroup_size(1)\nfn ms_no_ts() {\n    mesh_output.vertex_count = 3;\n    mesh_output.primitive_count = 1;\n    workgroupData = 2.0;\n\n    mesh_output.vertices[0].position = positions[0];\n    mesh_output.vertices[0].color = colors[0];\n\n    mesh_output.vertices[1].position = positions[1];\n    mesh_output.vertices[1].color = colors[1];\n\n    mesh_output.vertices[2].position = positions[2];\n    mesh_output.vertices[2].color = colors[2];\n\n    mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n    mesh_output.primitives[0].cull = false;\n    mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\n\n@mesh(mesh_output)\n@workgroup_size(2)\nfn ms_divergent(@builtin(local_invocation_id) thread_id: vec3<u32>) {\n    // Workgroup with 2 threads. They return at different points.\n    if thread_id.x == 0 {\n        mesh_output.vertex_count = 3;\n        mesh_output.primitive_count = 1;\n        workgroupData = 2.0;\n\n        mesh_output.vertices[0].position = positions[0];\n        mesh_output.vertices[0].color = colors[0];\n\n        mesh_output.vertices[1].position = positions[1];\n        mesh_output.vertices[1].color = colors[1];\n\n        mesh_output.vertices[2].position = positions[2];\n        mesh_output.vertices[2].color = colors[2];\n\n        mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);\n        mesh_output.primitives[0].cull = false;\n        mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);\n        return;\n    }\n}\n\n@fragment\nfn fs_main(vertex: VertexOutput, primitive: PrimitiveInput) -> @location(0) vec4<f32> {\n    return vertex.color * primitive.colorMask;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/multiview/mod.rs",
    "content": "use std::num::NonZero;\n\nuse wgpu::{Features, Limits};\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(DRAW_MULTIVIEW_SINGLE);\n    vec.push(DRAW_MULTIVIEW);\n    vec.push(DRAW_MULTIVIEW_NONCONTIGUOUS);\n    vec.push(DRAW_MULTIVIEW_MULTISAMPLE);\n}\n\n#[gpu_test]\nstatic DRAW_MULTIVIEW_SINGLE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::MULTIVIEW)\n            .limits(Limits {\n                max_multiview_view_count: 1,\n                ..Limits::defaults()\n            }),\n    )\n    .run_async(|ctx| run_test(ctx, 0b1, 1));\n\n#[gpu_test]\nstatic DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::MULTIVIEW)\n            .limits(Limits {\n                max_multiview_view_count: 2,\n                ..Limits::defaults()\n            }),\n    )\n    .run_async(|ctx| run_test(ctx, 0b11, 1));\n\n#[gpu_test]\nstatic DRAW_MULTIVIEW_NONCONTIGUOUS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters {\n        required_features: Features::MULTIVIEW | Features::SELECTIVE_MULTIVIEW,\n        required_limits: Limits {\n            max_multiview_view_count: 4,\n            ..Limits::defaults()\n        },\n        // https://github.com/gfx-rs/wgpu/issues/9184 and https://github.com/gfx-rs/wgpu/issues/9187\n        failures: wgpu_test::FailureCase::mac_vulkan(|case| {\n            case.panic(\n                \"assertion `left == right` failed: Expected 0\\n  left: Some(255)\\n right: None\",\n            )\n        }),\n        ..Default::default()\n    })\n    .run_async(|ctx| run_test(ctx, 0b1001, 1));\n\n#[gpu_test]\nstatic DRAW_MULTIVIEW_MULTISAMPLE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::MULTIVIEW | Features::MULTISAMPLE_ARRAY)\n            .limits(Limits {\n                max_multiview_view_count: 2,\n                ..Limits::defaults()\n            }),\n    )\n    .run_async(|ctx| run_test(ctx, 0b11, 4));\n\nasync fn run_test(ctx: TestingContext, layer_mask: u32, sample_count: u32) {\n    let num_layers = 32 - layer_mask.leading_zeros();\n\n    let shader_src = include_str!(\"shader.wgsl\");\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        vertex: wgpu::VertexState {\n            buffers: &[],\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: NonZero::new(layer_mask),\n        multisample: wgpu::MultisampleState {\n            count: sample_count,\n            ..Default::default()\n        },\n        layout: None,\n        depth_stencil: None,\n        cache: None,\n    };\n\n    const TEXTURE_SIZE: u32 = 256;\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    let texture_desc = wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: TEXTURE_SIZE,\n            height: TEXTURE_SIZE,\n            depth_or_array_layers: 32 - layer_mask.leading_zeros(),\n        },\n        mip_level_count: 1,\n        sample_count,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    };\n    let texture = ctx.device.create_texture(&texture_desc);\n    let texture_view_desc = wgpu::TextureViewDescriptor {\n        label: None,\n        format: Some(wgpu::TextureFormat::R8Unorm),\n        dimension: Some(wgpu::TextureViewDimension::D2Array),\n        usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),\n        aspect: wgpu::TextureAspect::All,\n        base_mip_level: 0,\n        mip_level_count: None,\n        base_array_layer: 0,\n        array_layer_count: Some(num_layers),\n    };\n    let entire_texture_view = texture.create_view(&texture_view_desc);\n\n    let (resolve_texture, resolve_texture_view) = if sample_count != 1 {\n        let mut texture_desc = texture_desc.clone();\n        texture_desc.sample_count = 1;\n\n        let resolve_texture = ctx.device.create_texture(&texture_desc);\n        let resolve_texture_view = resolve_texture.create_view(&texture_view_desc);\n\n        (Some(resolve_texture), Some(resolve_texture_view))\n    } else {\n        (None, None)\n    };\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64 * num_layers as u64,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let clear_color = 0.0;\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &entire_texture_view,\n                depth_slice: None,\n                resolve_target: resolve_texture_view.as_ref(),\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: NonZero::new(layer_mask),\n        });\n        rpass.set_pipeline(&pipeline);\n        rpass.draw(0..6, 0..1);\n    }\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: resolve_texture.as_ref().unwrap_or(&texture),\n            mip_level: 0,\n            origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &readback_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(TEXTURE_SIZE),\n                rows_per_image: Some(TEXTURE_SIZE),\n            },\n        },\n        wgpu::Extent3d {\n            width: TEXTURE_SIZE,\n            height: TEXTURE_SIZE,\n            depth_or_array_layers: num_layers,\n        },\n    );\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize;\n    assert_eq!(data.len(), each_texture_size * num_layers as usize);\n    for view_idx in 0..num_layers as usize {\n        let target_value = if (layer_mask & (1 << view_idx)) != 0 {\n            (32 + 64 * view_idx) as u8\n        } else {\n            (clear_color * 255.0) as u8\n        };\n        // Some metal devices automatically initialize stuff to 255, so I decided to use 128 instead of that\n        let failed_value = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)]\n            .iter()\n            .copied()\n            .find(|b| b.abs_diff(target_value) > 1);\n        assert_eq!(failed_value, None, \"Expected {target_value}\");\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/multiview/shader.wgsl",
    "content": "const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {\n    return vec4f(triangles[vertex_index], 0.0, 1.0);\n}\n\n@fragment\nfn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f {\n    return vec4f(f32(view_index) * 0.25 + 0.125);\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/naga_capabilities.rs",
    "content": "use wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(VALIDATE_CAPABILITIES);\n}\n\npub fn validate_capabilities(ctx: TestingContext) {\n    use naga::valid::Capabilities as Caps;\n    let device_caps = wgpu_core::device::features_to_naga_capabilities(\n        ctx.adapter.features(),\n        ctx.adapter.get_downlevel_capabilities().flags,\n    );\n    let max_caps = match ctx.adapter.get_info().backend {\n        wgpu::Backend::Vulkan => naga::back::spv::supported_capabilities(),\n        // TODO: when mesh shaders land, change this\n        wgpu::Backend::Dx12 => naga::back::hlsl::supported_capabilities() | Caps::MESH_SHADER,\n        wgpu::Backend::Metal => naga::back::msl::supported_capabilities() | Caps::MESH_SHADER,\n        wgpu::Backend::Gl => naga::back::glsl::supported_capabilities(),\n        wgpu::Backend::BrowserWebGpu => naga::back::wgsl::supported_capabilities(),\n        wgpu::Backend::Noop => Caps::all(),\n    };\n    let diff = device_caps - max_caps;\n    assert_eq!(diff, Caps::empty());\n}\n\n#[gpu_test]\nstatic VALIDATE_CAPABILITIES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(wgpu::DownlevelFlags::empty())\n            .limits(wgpu::Limits::downlevel_defaults()),\n    )\n    .run_sync(validate_capabilities);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/occlusion_query/mod.rs",
    "content": "use wgpu::InstanceFlags;\nuse wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(OCCLUSION_QUERY);\n}\n\n#[gpu_test]\nstatic OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .expect_fail(FailureCase::webgl2())\n            // Ensure timestamp normalization does not interfere with occlusion query results\n            .instance_flags(InstanceFlags::AUTOMATIC_TIMESTAMP_NORMALIZATION),\n    )\n    .run_async(|ctx| async move {\n        // Create depth texture\n        let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"Depth texture\"),\n            size: wgpu::Extent3d {\n                width: 64,\n                height: 64,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Depth32Float,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n        let depth_texture_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n        // Setup pipeline using a simple shader with hardcoded vertices\n        let shader = ctx\n            .device\n            .create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n        let pipeline = ctx\n            .device\n            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"Pipeline\"),\n                layout: None,\n                vertex: wgpu::VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                fragment: None,\n                primitive: wgpu::PrimitiveState::default(),\n                depth_stencil: Some(wgpu::DepthStencilState {\n                    format: wgpu::TextureFormat::Depth32Float,\n                    depth_write_enabled: Some(true),\n                    depth_compare: Some(wgpu::CompareFunction::Less),\n                    stencil: wgpu::StencilState::default(),\n                    bias: wgpu::DepthBiasState::default(),\n                }),\n                multisample: wgpu::MultisampleState::default(),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        // Create occlusion query set\n        let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n            label: Some(\"Query set\"),\n            ty: wgpu::QueryType::Occlusion,\n            count: 3,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        {\n            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"Render pass\"),\n                color_attachments: &[],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                    view: &depth_texture_view,\n                    depth_ops: Some(wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(1.0),\n                        store: wgpu::StoreOp::Store,\n                    }),\n                    stencil_ops: None,\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: Some(&query_set),\n                multiview_mask: None,\n            });\n            render_pass.set_pipeline(&pipeline);\n\n            // Not occluded (z = 1.0, nothing drawn yet)\n            render_pass.begin_occlusion_query(0);\n            render_pass.draw(4..7, 0..1);\n            render_pass.end_occlusion_query();\n\n            // Not occluded (z = 0.0)\n            render_pass.begin_occlusion_query(1);\n            render_pass.draw(0..3, 0..1);\n            render_pass.end_occlusion_query();\n\n            // Occluded (z = 1.0)\n            render_pass.begin_occlusion_query(2);\n            render_pass.draw(4..7, 0..1);\n            render_pass.end_occlusion_query();\n        }\n\n        // Resolve query set to buffer\n        let query_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"Query buffer\"),\n            size: size_of::<u64>() as u64 * 3,\n            usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n        encoder.resolve_query_set(&query_set, 0..3, &query_buffer, 0);\n\n        let mapping_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"Mapping buffer\"),\n            size: query_buffer.size(),\n            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n        encoder.copy_buffer_to_buffer(&query_buffer, 0, &mapping_buffer, 0, query_buffer.size());\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        mapping_buffer\n            .slice(..)\n            .map_async(wgpu::MapMode::Read, |_| ());\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let query_buffer_view = mapping_buffer.slice(..).get_mapped_range();\n        let query_data: &[u64; 3] = bytemuck::from_bytes(&query_buffer_view);\n\n        // WebGPU only defines query results as zero/non-zero\n        assert_ne!(query_data[0], 0);\n        assert_ne!(query_data[1], 0);\n        assert_eq!(query_data[2], 0);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/occlusion_query/shader.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {\n    let x = f32(i32(in_vertex_index & 3u) - 1);\n    let y = f32(i32(in_vertex_index & 1u) * 2 - 1);\n\n    return vec4<f32>(x, y, f32(in_vertex_index & 4u) / 8.0, 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/oob_indexing.rs",
    "content": "use wgpu::{Backend, Backends};\nuse wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.extend([\n        RESTRICT_WORKGROUP_PRIVATE_FUNCTION_LET,\n        D3D12_RESTRICT_DYNAMIC_BUFFERS,\n    ]);\n}\n\n/// Tests that writing and reading to the max length of a container (vec, mat, array)\n/// in the workgroup, private and function address spaces + let declarations\n/// will instead write to and read from the last element.\n#[gpu_test]\nstatic RESTRICT_WORKGROUP_PRIVATE_FUNCTION_LET: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults())\n            .skip(FailureCase::backend(Backends::GL))\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(|ctx| async move {\n        let test_resources = TestResources::new(&ctx);\n\n        ctx.queue\n            .write_buffer(&test_resources.in_buffer, 0, bytemuck::bytes_of(&4_u32));\n\n        let mut encoder = ctx.device.create_command_encoder(&Default::default());\n        {\n            let mut compute_pass = encoder.begin_compute_pass(&Default::default());\n            compute_pass.set_pipeline(&test_resources.pipeline);\n            compute_pass.set_bind_group(0, &test_resources.bind_group, &[]);\n            compute_pass.dispatch_workgroups(1, 1, 1);\n        }\n\n        encoder.copy_buffer_to_buffer(\n            &test_resources.out_buffer,\n            0,\n            &test_resources.readback_buffer,\n            0,\n            12 * 4,\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        test_resources\n            .readback_buffer\n            .slice(..)\n            .map_async(wgpu::MapMode::Read, |_| {});\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        let view = test_resources.readback_buffer.slice(..).get_mapped_range();\n\n        let current_res: [u32; 12] = *bytemuck::from_bytes(&view);\n        drop(view);\n        test_resources.readback_buffer.unmap();\n\n        if ctx.adapter_info.backend == Backend::Dx12 {\n            assert_eq!([1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0], current_res);\n        } else {\n            assert_eq!([1; 12], current_res);\n        }\n    });\n\nstruct TestResources {\n    pipeline: wgpu::ComputePipeline,\n    in_buffer: wgpu::Buffer,\n    out_buffer: wgpu::Buffer,\n    readback_buffer: wgpu::Buffer,\n    bind_group: wgpu::BindGroup,\n}\n\nimpl TestResources {\n    fn new(ctx: &TestingContext) -> Self {\n        // FXC doesn't support dynamically indexing and writing to vectors and matrices, it errors with:\n        // error X3500: array reference cannot be used as an l-value; not natively addressable\n        // see also: https://github.com/gfx-rs/wgpu/issues/4460\n        let opt = if ctx.adapter_info.backend == Backend::Dx12 {\n            \"//\"\n        } else {\n            \"\"\n        };\n        let shader_src = format!(\n            \"\n            @group(0) @binding(0)\n            var<storage, read_write> in: u32;\n            @group(0) @binding(1)\n            var<storage, read_write> out: array<u32>;\n\n            var<workgroup> wg_array: array<u32, 3>;\n            var<workgroup> wg_vec: vec3u;\n            var<workgroup> wg_mat: mat3x3f;\n\n            var<private> private_array: array<u32, 3>;\n            var<private> private_vec: vec3u;\n            var<private> private_mat: mat3x3f;\n\n            @compute @workgroup_size(1)\n            fn main() {{\n                let i = in;\n\n                var var_array = array<u32, 3>();\n                wg_array[i] = 1u;\n                private_array[i] = 1u;\n                var_array[i] = 1u;\n                let let_array = var_array;\n\n                out[0] = wg_array[i];\n                out[1] = private_array[i];\n                out[2] = var_array[i];\n                out[3] = let_array[i];\n\n                var var_vec = vec3u();\n                wg_vec[i] = 1u;\n                {opt} private_vec[i] = 1u;\n                {opt} var_vec[i] = 1u;\n                let let_vec = var_vec;\n\n                out[4] = wg_vec[i];\n                out[5] = private_vec[i];\n                out[6] = var_vec[i];\n                out[7] = let_vec[i];\n\n                var var_mat = mat3x3f();\n                wg_mat[i][0] = 1f;\n                {opt} private_mat[i][0] = 1f;\n                {opt} var_mat[i][0] = 1f;\n                let let_mat = var_mat;\n\n                out[8] = u32(wg_mat[i][0]);\n                out[9] = u32(private_mat[i][0]);\n                out[10] = u32(var_mat[i][0]);\n                out[11] = u32(let_mat[i][0]);\n            }}\n        \"\n        );\n\n        let module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n            });\n\n        let bgl = ctx\n            .device\n            .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: None,\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: None,\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Buffer {\n                            ty: wgpu::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: None,\n                        },\n                        count: None,\n                    },\n                ],\n            });\n\n        let layout = ctx\n            .device\n            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bgl)],\n                immediate_size: 0,\n            });\n\n        let pipeline = ctx\n            .device\n            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                label: None,\n                layout: Some(&layout),\n                module: &module,\n                entry_point: Some(\"main\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        let in_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 4,\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let out_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 12 * 4,\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n\n        let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 12 * 4,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &pipeline.get_bind_group_layout(0),\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: in_buffer.as_entire_binding(),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: out_buffer.as_entire_binding(),\n                },\n            ],\n        });\n\n        Self {\n            pipeline,\n            in_buffer,\n            out_buffer,\n            readback_buffer,\n            bind_group,\n        }\n    }\n}\n\n/// Tests behavior of OOB accesses for dynamic buffers.\n///\n/// This test is specific to D3D12 since Vulkan and Metal behave differently and\n/// the WGSL spec allows for multiple behaviors when it comes to OOB accesses.\n#[gpu_test]\nstatic D3D12_RESTRICT_DYNAMIC_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults())\n            .skip(FailureCase::backend(Backends::all() - Backends::DX12)),\n    )\n    .run_async(d3d12_restrict_dynamic_buffers);\n\nasync fn d3d12_restrict_dynamic_buffers(ctx: TestingContext) {\n    let shader_src = \"\n        @group(0) @binding(0)\n        var<storage, read_write> in: u32;\n        @group(0) @binding(1)\n        var<storage, read_write> out: array<u32>;\n\n        struct T {\n            @size(16)\n            t: u32\n        }\n\n        @group(0) @binding(2)\n        var<uniform> in_data_uniform: array<T, 1>;\n\n        @group(0) @binding(3)\n        var<storage, read_write> in_data_storage: array<T, 1>;\n\n        @compute @workgroup_size(1)\n        fn main() {\n            let i = in;\n            out[0] = in_data_uniform[i].t;   // should be 1 since we clamp the index\n\n            out[1] = in_data_storage[i].t;   // should be 3 since we rely on the D3D12 runtime to bound check and\n                                                // the index is still in the bounds of the buffer\n\n            out[2] = in_data_storage[i+1].t; // should be 0 since we rely on the D3D12 runtime to bound check\n        }\n    \";\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 2,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Uniform,\n                        has_dynamic_offset: true,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                wgpu::BindGroupLayoutEntry {\n                    binding: 3,\n                    visibility: wgpu::ShaderStages::COMPUTE,\n                    ty: wgpu::BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: true,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n    let layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&layout),\n            module: &module,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let in_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 4,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let out_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 3 * 4,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let in_data_uniform_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 + 8 * 4,\n        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    let in_data_storage_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 + 8 * 4,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 3 * 4,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: in_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: out_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 2,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &in_data_uniform_buffer,\n                    offset: 0,\n                    size: Some(std::num::NonZeroU64::new(4 * 4).unwrap()),\n                }),\n            },\n            wgpu::BindGroupEntry {\n                binding: 3,\n                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                    buffer: &in_data_storage_buffer,\n                    offset: 0,\n                    size: Some(std::num::NonZeroU64::new(4 * 4).unwrap()),\n                }),\n            },\n        ],\n    });\n\n    ctx.queue\n        .write_buffer(&in_buffer, 0, bytemuck::bytes_of(&1_u32));\n\n    #[rustfmt::skip]\n        let in_data = [\n            1_u32, 2_u32, 2_u32, 2_u32,\n            3_u32, 4_u32, 4_u32, 4_u32,\n        ];\n\n    ctx.queue\n        .write_buffer(&in_data_uniform_buffer, 256, bytemuck::bytes_of(&in_data));\n    ctx.queue\n        .write_buffer(&in_data_storage_buffer, 256, bytemuck::bytes_of(&in_data));\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n    {\n        let mut compute_pass = encoder.begin_compute_pass(&Default::default());\n        compute_pass.set_pipeline(&pipeline);\n        compute_pass.set_bind_group(0, &bind_group, &[256, 256]);\n        compute_pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&out_buffer, 0, &readback_buffer, 0, 3 * 4);\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    readback_buffer\n        .slice(..)\n        .map_async(wgpu::MapMode::Read, |_| {});\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let view = readback_buffer.slice(..).get_mapped_range();\n\n    let current_res: [u32; 3] = *bytemuck::from_bytes(&view);\n    drop(view);\n    readback_buffer.unmap();\n\n    assert_eq!([1, 3, 0], current_res);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/oom.rs",
    "content": "use wgpu::{\n    AccelerationStructureFlags, AccelerationStructureGeometryFlags,\n    AccelerationStructureUpdateMode, Backends, BlasGeometrySizeDescriptors,\n    BlasTriangleGeometrySizeDescriptor, BufferDescriptor, BufferUsages, CreateBlasDescriptor,\n    CreateTlasDescriptor, Error, ErrorFilter, Extent3d, Features, QuerySetDescriptor, QueryType,\n    TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, VertexFormat,\n};\nuse wgpu_test::GpuTestInitializer;\nuse wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        TEXTURE_OOM_TEST,\n        BUFFER_OOM_TEST,\n        MAPPING_BUFFER_OOM_TEST,\n        QUERY_SET_OOM_TEST,\n        BLAS_OOM_TEST,\n        TLAS_OOM_TEST,\n    ]);\n}\n\n// 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.\n// This is done so that other tests that create resources will not fail with OOM errors due to the OOM tests running in parallel.\n\n/// Backends for which OOM detection is implemented\nconst OOM_DETECTION_IMPL: Backends = Backends::DX12.union(Backends::VULKAN);\n\n/// Backends for which query set OOM detection is implemented\nconst QUERY_SET_OOM_DETECTION_IMPL: Backends = Backends::DX12;\n\n// All tests skip llvmpipe.\n// Even though llvmpipe supports VK_EXT_memory_budget it's happy to continue creating resources until\n// the process crashes with SIGABRT \"memory allocation of X bytes failed\" or the test times out.\n\n/// Nr of resources tests will try to create before failing.\nconst LOOP_BOUND: u32 = 1_000_000;\n\n#[gpu_test]\nstatic TEXTURE_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .skip(FailureCase::backend(!OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut textures = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let texture = ctx.device.create_texture(&TextureDescriptor {\n                label: None,\n                size: Extent3d {\n                    width: 2048,\n                    height: 2048,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format: TextureFormat::Rgba16Float,\n                usage: TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            });\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            textures.push(texture);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n\n#[gpu_test]\nstatic BUFFER_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .skip(FailureCase::backend(!OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut buffers = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let buffer = ctx.device.create_buffer(&BufferDescriptor {\n                label: None,\n                size: 256 * 1024 * 1024,\n                usage: BufferUsages::STORAGE,\n                mapped_at_creation: false,\n            });\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            buffers.push(buffer);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n\n#[gpu_test]\nstatic MAPPING_BUFFER_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .skip(FailureCase::backend(!OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut buffers = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let buffer = ctx.device.create_buffer(&BufferDescriptor {\n                label: None,\n                size: 256 * 1024 * 1024,\n                usage: BufferUsages::COPY_SRC | BufferUsages::MAP_WRITE,\n                mapped_at_creation: false,\n            });\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            buffers.push(buffer);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n\n#[gpu_test]\nstatic QUERY_SET_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            // Vulkan: https://github.com/gfx-rs/wgpu/issues/7817\n            .skip(FailureCase::backend(!QUERY_SET_OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut query_sets = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let query_set = ctx.device.create_query_set(&QuerySetDescriptor {\n                label: None,\n                ty: QueryType::Occlusion,\n                count: 4096,\n            });\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            query_sets.push(query_set);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n\n#[gpu_test]\nstatic BLAS_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::EXPERIMENTAL_RAY_QUERY)\n            .skip(FailureCase::backend(!OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut blases = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let blas = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: None,\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![BlasTriangleGeometrySizeDescriptor {\n                        vertex_format: VertexFormat::Float32x3,\n                        vertex_count: 1024 * 1024,\n                        index_format: None,\n                        index_count: None,\n                        flags: AccelerationStructureGeometryFlags::OPAQUE,\n                    }],\n                },\n            );\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            blases.push(blas);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n\n#[gpu_test]\nstatic TLAS_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::EXPERIMENTAL_RAY_QUERY)\n            .skip(FailureCase::backend(!OOM_DETECTION_IMPL))\n            // see comment at the top of the file\n            .skip(FailureCase::backend_adapter(Backends::VULKAN, \"llvmpipe\")),\n    )\n    .run_async(|ctx| async move {\n        let mut tlases = Vec::new();\n        for _ in 0..LOOP_BOUND {\n            let scope = ctx.device.push_error_scope(ErrorFilter::OutOfMemory);\n            let tlas = ctx.device.create_tlas(&CreateTlasDescriptor {\n                label: None,\n                max_instances: 1024 * 1024,\n                flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: AccelerationStructureUpdateMode::Build,\n            });\n            if let Some(err) = scope.pop().await {\n                match err {\n                    Error::OutOfMemory { .. } => {\n                        return;\n                    }\n                    _ => unreachable!(),\n                }\n            }\n            tlases.push(tlas);\n        }\n        panic!(\"Failed to OOM after {LOOP_BOUND} iterations.\");\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/pass_ops/mod.rs",
    "content": "use wgpu_test::{\n    gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(DONT_CARE);\n}\n\n#[gpu_test]\nstatic DONT_CARE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(run_test);\n\nasync fn run_test(ctx: TestingContext) {\n    let shader_src = \"\n        const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n\n        @vertex\n        fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {\n            return vec4f(triangles[vertex_index], 0.0, 1.0);\n        }\n\n        @fragment\n        fn fs_main() -> @location(0) vec4f {\n            return vec4f(127.0 / 255.0);\n        }\n    \";\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &[],\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::Rgba8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let readbacks = ReadbackBuffers::new(&ctx.device, &out_texture);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            view: &out_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n            depth_slice: None,\n            resolve_target: None,\n            ops: wgpu::Operations {\n                load: wgpu::LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() }),\n                store: wgpu::StoreOp::Store,\n            },\n        })],\n        ..Default::default()\n    });\n    rpass.set_pipeline(&pipeline);\n    rpass.draw(0..3, 0..1);\n\n    drop(rpass);\n\n    readbacks.copy_from(&ctx.device, &mut encoder, &out_texture);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    // Assert that DONT_CARE load op was fully overridden by the draw.\n    readbacks\n        .assert_buffer_contents(&ctx, &[127, 127, 127, 127])\n        .await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/mod.rs",
    "content": "use std::{\n    borrow::Cow,\n    hash::{DefaultHasher, Hash, Hasher},\n};\n\nuse wgpu::{\n    Backends, ColorTargetState, ColorWrites, Features, FragmentState, MultisampleState,\n    PipelineLayoutDescriptor, RenderPipelineDescriptor, VertexState,\n};\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\nuse wgpu_types::CreateShaderModuleDescriptorPassthrough;\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.push(METAL_PASSTHROUGH_SHADER);\n    tests.push(METALLIB_PASSTHROUGH_SHADER);\n    tests.push(HLSL_PASSTHROUGH_SHADER);\n    tests.push(DXIL_PASSTHROUGH_SHADER);\n    tests.push(SPIRV_PASSTHROUGH_SHADER);\n    tests.push(GLSL_PASSTHROUGH_SHADER);\n    tests.push(WGSL_PASSTHROUGH_SHADER);\n    tests.push(ALL_PASSTHROUGH_SHADERS_BINARY);\n    tests.push(ALL_PASSTHROUGH_SHADERS_SOURCE);\n    tests.push(PASSTHROUGH_SHADERS_EXPLICIT_LAYOUT_VALIDATION);\n}\n\nfn test_hash(ctx: &TestingContext, name: &str) -> u64 {\n    let mut hasher = DefaultHasher::new();\n    ctx.hash(&mut hasher);\n    name.hash(&mut hasher);\n    hasher.finish()\n}\n\nfn test_with_module(ctx: TestingContext, vertex: wgpu::ShaderModule, fragment: wgpu::ShaderModule) {\n    let layout = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[],\n            immediate_size: 0,\n        });\n    let _pipeline = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&layout),\n            vertex: VertexState {\n                module: &vertex,\n                entry_point: Some(\"vertex_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: MultisampleState::default(),\n            fragment: Some(FragmentState {\n                module: &fragment,\n                entry_point: Some(\"fragment_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: ColorWrites::all(),\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n}\n\nfn metal_source() -> Cow<'static, str> {\n    Cow::Borrowed(include_str!(\"shader.metal\"))\n}\n\nfn metal_test(ctx: TestingContext) {\n    let module = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                msl: Some(metal_source()),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, module.clone(), module);\n}\n\n#[gpu_test]\nstatic METAL_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::METAL)),\n    )\n    .run_sync(metal_test);\n\nfn metallib_source(test_hash: u64) -> Cow<'static, [u8]> {\n    struct FileDropGuard<'a> {\n        file_name: &'a str,\n    }\n    impl Drop for FileDropGuard<'_> {\n        fn drop(&mut self) {\n            let _ = std::fs::remove_file(self.file_name);\n        }\n    }\n    if cfg!(not(target_vendor = \"apple\")) {\n        return Cow::Borrowed(&[]);\n    }\n    let metal_compiler = std::process::Command::new(\"xcrun\")\n        .args([\"--find\", \"metal\"])\n        .status()\n        .is_ok_and(|a| a.success());\n    let metallib_linker = std::process::Command::new(\"xcrun\")\n        .args([\"--find\", \"metallib\"])\n        .status()\n        .is_ok_and(|a| a.success());\n    if !metal_compiler || !metallib_linker {\n        panic!(\"Metal compiler or metallib linker not present. Most users can safely ignore this.\");\n    }\n    let air_name = format!(\n        \"{}/tests/wgpu-gpu/passthrough/shader{test_hash}.air\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n    let output_name = format!(\n        \"{}/tests/wgpu-gpu/passthrough/shader{test_hash}.metallib\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n\n    let _air_drop_guard = FileDropGuard {\n        file_name: &air_name,\n    };\n\n    {\n        let output = std::process::Command::new(\"xcrun\")\n            .args([\n                \"metal\",\n                \"-c\",\n                &format!(\n                    \"{}/tests/wgpu-gpu/passthrough/shader.metal\",\n                    env!(\"CARGO_MANIFEST_DIR\")\n                ),\n                \"-o\",\n                &air_name,\n            ])\n            .output()\n            .unwrap();\n        if !output.status.success() {\n            panic!(\n                \"Failed to compile .metal into .air: {}\",\n                String::from_utf8(output.stderr).unwrap()\n            );\n        }\n    }\n\n    let _metallib_drop_guard = FileDropGuard {\n        file_name: &output_name,\n    };\n\n    {\n        let output = std::process::Command::new(\"xcrun\")\n            .args([\"metallib\", &air_name, \"-o\", &output_name])\n            .output()\n            .unwrap();\n        if !output.status.success() {\n            panic!(\n                \"Failed to compile .air into .metallib: {}\",\n                String::from_utf8(output.stderr).unwrap()\n            );\n        }\n    }\n    let source = std::fs::read(&output_name).unwrap();\n    Cow::Owned(source)\n}\n\nfn metallib_test(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"metallib_test\");\n    let source = metallib_source(test_hash);\n    let module = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                metallib: Some(std::borrow::Cow::Borrowed(&source)),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, module.clone(), module);\n}\n\n#[gpu_test]\nstatic METALLIB_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::METAL)),\n    )\n    .run_sync(metallib_test);\n\nfn hlsl_source() -> Cow<'static, str> {\n    std::borrow::Cow::Borrowed(include_str!(\"shader.hlsl\"))\n}\n\nfn hlsl_test(ctx: TestingContext) {\n    let module = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                hlsl: Some(hlsl_source()),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, module.clone(), module);\n}\n\n#[gpu_test]\nstatic HLSL_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::DX12)),\n    )\n    .run_sync(hlsl_test);\n\nfn compile_dxil(entry: &str, stage_str: &str, test_hash: u64) -> Cow<'static, [u8]> {\n    let out_path = format!(\n        \"{}/tests/wgpu-gpu/passthrough/shader{test_hash}.{stage_str}.cso\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n    let cmd = std::process::Command::new(\"dxc\")\n        .args([\n            \"-T\",\n            &format!(\"{stage_str}_6_3\"),\n            \"-E\",\n            entry,\n            &format!(\n                \"{}/tests/wgpu-gpu/passthrough/shader.hlsl\",\n                env!(\"CARGO_MANIFEST_DIR\")\n            ),\n            \"-Fo\",\n            &out_path,\n        ])\n        .output()\n        .unwrap();\n    let file = std::fs::read(&out_path);\n    let _ = std::fs::remove_file(out_path);\n    // Remove the file before checking for status\n    if !cmd.status.success() {\n        panic!(\"DXC failed:\\n{}\", String::from_utf8(cmd.stderr).unwrap());\n    }\n    let file = file.unwrap();\n    Cow::Owned(file)\n}\n\nfn dxil_vertex_source(test_hash: u64) -> Cow<'static, [u8]> {\n    if cfg!(target_os = \"windows\") {\n        compile_dxil(\"vertex_main\", \"vs\", test_hash)\n    } else {\n        Cow::Borrowed(&[])\n    }\n}\n\nfn dxil_fragment_source(test_hash: u64) -> Cow<'static, [u8]> {\n    if cfg!(target_os = \"windows\") {\n        compile_dxil(\"fragment_main\", \"ps\", test_hash)\n    } else {\n        Cow::Borrowed(&[])\n    }\n}\n\nfn dxil_test(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"dxil_test\");\n    let vertex_source = dxil_vertex_source(test_hash);\n    let vertex = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (1, 1, 1),\n                dxil: Some(vertex_source),\n                ..Default::default()\n            })\n    };\n    let fragment_source = dxil_fragment_source(test_hash);\n    let fragment = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (1, 1, 1),\n                dxil: Some(fragment_source),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, vertex, fragment);\n}\n\n#[gpu_test]\nstatic DXIL_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::DX12)),\n    )\n    .run_sync(dxil_test);\n\nfn spirv_source(test_hash: u64) -> Cow<'static, [u32]> {\n    let out_path = format!(\n        \"{}/tests/wgpu-gpu/passthrough/shade{test_hash}.spv\",\n        env!(\"CARGO_MANIFEST_DIR\")\n    );\n    let cmd = std::process::Command::new(\"dxc\")\n        .args([\n            \"-spirv\",\n            \"-T\",\n            \"lib_6_3\",\n            \"-fspv-target-env=vulkan1.0\",\n            // We need to tell it to compile for SPIRV which requires different info\n            \"-D\",\n            \"SPIRV\",\n            &format!(\n                \"{}/tests/wgpu-gpu/passthrough/shader.hlsl\",\n                env!(\"CARGO_MANIFEST_DIR\")\n            ),\n            \"-Fo\",\n            &out_path,\n        ])\n        .output()\n        .unwrap();\n    let file = std::fs::read(&out_path);\n    let _ = std::fs::remove_file(out_path);\n    // Remove the file before checking for status\n    if !cmd.status.success() {\n        panic!(\"DXC failed:\\n{}\", String::from_utf8(cmd.stderr).unwrap());\n    }\n    let file = file.unwrap();\n    let spirv = bytemuck::pod_collect_to_vec::<u8, u32>(&file);\n    Cow::Owned(spirv)\n}\n\nfn spirv_test(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"spirv_test\");\n    let module = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                spirv: Some(spirv_source(test_hash)),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, module.clone(), module);\n}\n\n#[gpu_test]\nstatic SPIRV_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::VULKAN)),\n    )\n    .run_sync(spirv_test);\n\nfn glsl_vertex_source() -> Cow<'static, str> {\n    std::borrow::Cow::Borrowed(include_str!(\"shader.vert\"))\n}\n\nfn glsl_fragment_source() -> Cow<'static, str> {\n    std::borrow::Cow::Borrowed(include_str!(\"shader.frag\"))\n}\n\nfn glsl_test(ctx: TestingContext) {\n    let vertex = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                glsl: Some(glsl_vertex_source()),\n                ..Default::default()\n            })\n    };\n    let fragment = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                glsl: Some(glsl_fragment_source()),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, vertex, fragment);\n}\n\n#[gpu_test]\nstatic GLSL_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::GL)),\n    )\n    .run_sync(glsl_test);\n\nfn wgsl_source() -> Cow<'static, str> {\n    std::borrow::Cow::Borrowed(include_str!(\"shader.wgsl\"))\n}\n\nfn wgsl_test(ctx: TestingContext) {\n    let module = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                wgsl: Some(wgsl_source()),\n                ..Default::default()\n            })\n    };\n    test_with_module(ctx, module.clone(), module);\n}\n\n#[gpu_test]\nstatic WGSL_PASSTHROUGH_SHADER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::PASSTHROUGH_SHADERS)\n            .skip(FailureCase::backend(!Backends::BROWSER_WEBGPU)),\n    )\n    .run_sync(wgsl_test);\n\nfn all_passthrough_shaders_binary(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"all_passthrough_binary\");\n    let vertex = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: Some(dxil_vertex_source(test_hash)),\n                hlsl: None,\n                metallib: Some(metallib_source(test_hash)),\n                msl: None,\n                glsl: Some(glsl_vertex_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n    let fragment = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: Some(dxil_fragment_source(test_hash)),\n                hlsl: None,\n                metallib: Some(metallib_source(test_hash)),\n                msl: None,\n                glsl: Some(glsl_fragment_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n    test_with_module(ctx, vertex, fragment);\n}\n\n#[gpu_test]\nstatic ALL_PASSTHROUGH_SHADERS_BINARY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().features(Features::PASSTHROUGH_SHADERS))\n    .run_sync(all_passthrough_shaders_binary);\n\nfn all_passthrough_shader_source(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"all_passthrough_source\");\n    let vertex = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: None,\n                hlsl: Some(hlsl_source()),\n                metallib: None,\n                msl: Some(metal_source()),\n                glsl: Some(glsl_vertex_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n    let fragment = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: None,\n                hlsl: Some(hlsl_source()),\n                metallib: None,\n                msl: Some(metal_source()),\n                glsl: Some(glsl_fragment_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n    test_with_module(ctx, vertex, fragment);\n}\n\n#[gpu_test]\nstatic ALL_PASSTHROUGH_SHADERS_SOURCE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().features(Features::PASSTHROUGH_SHADERS))\n    .run_sync(all_passthrough_shader_source);\n\nfn explicit_layout_validation(ctx: TestingContext) {\n    let test_hash = test_hash(&ctx, \"explicit_layout_validation\");\n    let vertex = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: None,\n                hlsl: Some(hlsl_source()),\n                metallib: None,\n                msl: Some(metal_source()),\n                glsl: Some(glsl_vertex_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n    let fragment = unsafe {\n        ctx.device\n            .create_shader_module_passthrough(CreateShaderModuleDescriptorPassthrough {\n                label: None,\n                num_workgroups: (0, 0, 0),\n                spirv: Some(spirv_source(test_hash)),\n                dxil: None,\n                hlsl: Some(hlsl_source()),\n                metallib: None,\n                msl: Some(metal_source()),\n                glsl: Some(glsl_fragment_source()),\n                wgsl: Some(wgsl_source()),\n            })\n    };\n\n    let _pipeline = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: VertexState {\n                module: &vertex,\n                entry_point: Some(\"vertex_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: MultisampleState::default(),\n            fragment: Some(FragmentState {\n                module: &fragment,\n                entry_point: Some(\"fragment_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: ColorWrites::all(),\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n}\n\n#[gpu_test]\nstatic PASSTHROUGH_SHADERS_EXPLICIT_LAYOUT_VALIDATION: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .features(Features::PASSTHROUGH_SHADERS)\n                .expect_fail(FailureCase::always()),\n        )\n        .run_sync(explicit_layout_validation);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/shader.frag",
    "content": "#version 300 es\nprecision mediump float;\n\nout vec4 fragColor;\n\nvoid main() {\n    fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/shader.hlsl",
    "content": "// Used for HLSL, DXIL, and SPIRV passthrough\n\nstruct VSOut\n{\n    float4 position : SV_POSITION;\n};\n\n#ifdef SPIRV\n[shader(\"vertex\")]\n#endif\nVSOut vertex_main(uint vid: SV_VertexID)\n{\n    VSOut output;\n\n    float2 positions[3] = {\n        float2(0.0, 0.5),\n        float2(-0.5, -0.5),\n        float2(0.5, -0.5),\n    };\n\n    output.position = float4(positions[vid], 0.0, 1.0);\n    return output;\n}\n\n#ifdef SPIRV\n[shader(\"pixel\")]\n#endif\nfloat4 fragment_main(VSOut input) : SV_TARGET\n{\n    return float4(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/shader.metal",
    "content": "// Used for metal and metallib passthrough\n\n#include <metal_stdlib>\nusing namespace metal;\n\nstruct VSOut {\n    float4 position [[position]];\n};\n\nvertex VSOut vertex_main(uint vid [[vertex_id]]) {\n    VSOut out;\n\n    float2 positions[3] = {\n        float2( 0.0,  0.5),\n        float2(-0.5, -0.5),\n        float2( 0.5, -0.5),\n    };\n\n    out.position = float4(positions[vid], 0.0, 1.0);\n    return out;\n}\n\nfragment float4 fragment_main(VSOut in [[stage_in]]) {\n    return float4(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/shader.vert",
    "content": "#version 300 es\n\nout vec4 v_position;\n\nvoid main() {\n    vec2 positions[3] = vec2[3](\n        vec2( 0.0,  0.5),\n        vec2(-0.5, -0.5),\n        vec2( 0.5, -0.5)\n    );\n    \n    gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/passthrough/shader.wgsl",
    "content": "struct VSOut {\n    @builtin(position) position: vec4<f32>,\n};\n\n@vertex\nfn vertex_main(@builtin(vertex_index) vid: u32) -> VSOut {\n    var out: VSOut;\n    \n    // Hardcoded triangle in clip space\n    var positions = array<vec2<f32>, 3>(\n        vec2<f32>( 0.0,  0.5),\n        vec2<f32>(-0.5, -0.5),\n        vec2<f32>( 0.5, -0.5),\n    );\n    \n    out.position = vec4<f32>(positions[vid], 0.0, 1.0);\n    return out;\n}\n\n@fragment\nfn fragment_main(in: VSOut) -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/per_vertex/mod.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(PER_VERTEX);\n}\n\n//\n// These tests render a triangle strip to a 2x2 render target. The first triangle\n// in the vertex buffer covers the top-left pixel, the second triangle\n// covers the bottom two pixels, and the last triangle covers the top-right pixel.\n// XY layout of the render target, with the three triangles, pixel centers marked with '\n//\n//      (-1,1)    (0,1)     (1,1)\n//        +---------+---------+\n//        | o-------o-------o |\n//        | |      /|\\      | |\n//        | |  '  / | \\  '  | |\n//        | |    /  |  \\    | |\n// (-1,0) +-|---/---+---\\---|-+ (1,0)\n//        | |  /    |    \\  | |\n//        | | /     |     \\ | |\n//        | |/ '    |    ' \\| |\n//        | o---------------o |\n//        +---------+---------+\n//     (-1,-1)    (0,-1)    (1,-1)\n//\n// The fragment shader outputs color based on per-vertex position:\n//\n//     return vec4(z[0], z[1], z[2], 1.0);\n//\n\n#[gpu_test]\nstatic PER_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::SHADER_PER_VERTEX)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(per_vertex);\n\nasync fn per_vertex(ctx: TestingContext) {\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"per_vertex.wgsl\"));\n\n    let trianglestrip_xyz: [f32; 15] = [\n        -0.9, 0.9, 0.0, // top left\n        -0.9, -0.9, 0.25, // bottom left\n        0.0, 0.9, 0.5, // top center\n        0.9, -0.9, 0.75, // bottom right\n        0.9, 0.9, 1.0, // top right\n    ];\n    let vertex_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&trianglestrip_xyz),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let indices = [0u32, 1, 2, 3, 4];\n    let index_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&indices),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: 12,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &[wgpu::VertexAttribute {\n                        format: wgpu::VertexFormat::Float32x3,\n                        offset: 0,\n                        shader_location: 0,\n                    }],\n                }],\n            },\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint32),\n                ..wgpu::PrimitiveState::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let width = 2;\n    let height = 2;\n    let texture_size = wgpu::Extent3d {\n        width,\n        height,\n        depth_or_array_layers: 1,\n    };\n    let color_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: texture_size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = wgpu_test::image::ReadbackBuffers::new(&ctx.device, &color_texture);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),\n                    store: wgpu::StoreOp::Store,\n                },\n                resolve_target: None,\n                view: &color_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.draw(0..5, 0..1);\n    }\n    readback_buffer.copy_from(&ctx.device, &mut encoder, &color_texture);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    //   0    127   255\n    //   o-----o-----o\n    //   |    /|\\    |\n    //   |  '/ | \\'  |\n    //   +--/--+--\\--+\n    //   | /   |   \\ |\n    //   |/ '  |  ' \\|\n    //   o-----+-----o\n    //  64          191\n    let expected = [\n        0, 64, 127, 255, // top left\n        127, 191, 255, 255, // top right\n        64, 191, 127, 255, // bottom left\n        64, 191, 127, 255, // bottom right\n    ];\n    readback_buffer\n        .assert_buffer_contents(&ctx, &expected)\n        .await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/per_vertex/per_vertex.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) clip: vec4<f32>,\n    @interpolate(flat) @location(0) z: f32,\n}\n\n@vertex\nfn vs_main(@location(0) xyz: vec3<f32>) -> VertexOutput {\n    return VertexOutput(vec4<f32>(xyz.xy, 0.0, 1.0), xyz.z);\n}\n\n@fragment\nfn fs_main(@interpolate(per_vertex) @location(0) z: array<f32, 3>) -> @location(0) vec4<f32> {\n    return vec4(z[0], z[1], z[2], 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/pipeline.rs",
    "content": "use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE,\n        COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX,\n        RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE,\n        RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX,\n        NO_TARGETLESS_RENDER,\n    ]);\n}\n\nconst INVALID_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {\n    label: Some(\"invalid shader\"),\n    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\"not valid wgsl\")),\n};\n\nconst TRIVIAL_COMPUTE_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {\n    label: Some(\"trivial compute shader\"),\n    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n        \"@compute @workgroup_size(1) fn main() {}\",\n    )),\n};\n\nconst TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {\n    label: Some(\"trivial vertex shader\"),\n    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n        \"@vertex fn main() -> @builtin(position) vec4<f32> { return vec4<f32>(0); }\",\n    )),\n};\n\nconst TRIVIAL_FRAGMENT_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {\n    label: Some(\"trivial fragment shader\"),\n    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n        \"@fragment fn main() -> @location(0) vec4<f32> { return vec4<f32>(0); }\",\n    )),\n};\n\n// Create an invalid shader and a compute pipeline that uses it\n// with a default bindgroup layout, and then ask for that layout.\n// Validation should fail, but wgpu should not panic.\n#[gpu_test]\nstatic COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            fail(\n                &ctx.device,\n                || {\n                    let module = ctx.device.create_shader_module(INVALID_SHADER_DESC);\n\n                    let pipeline =\n                        ctx.device\n                            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                                label: Some(\"compute pipeline\"),\n                                layout: None,\n                                module: &module,\n                                entry_point: Some(\"doesn't exist\"),\n                                compilation_options: Default::default(),\n                                cache: None,\n                            });\n\n                    // https://github.com/gfx-rs/wgpu/issues/4167 this used to panic\n                    pipeline.get_bind_group_layout(0);\n                },\n                Some(\"Shader 'invalid shader' parsing error\"),\n            );\n        });\n\n#[gpu_test]\nstatic COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .enable_noop(),\n        )\n        .run_sync(|ctx| {\n            fail(\n                &ctx.device,\n                || {\n                    let module = ctx.device.create_shader_module(TRIVIAL_COMPUTE_SHADER_DESC);\n\n                    let pipeline =\n                        ctx.device\n                            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                                label: Some(\"compute pipeline\"),\n                                layout: None,\n                                module: &module,\n                                entry_point: Some(\"main\"),\n                                compilation_options: Default::default(),\n                                cache: None,\n                            });\n\n                    pipeline.get_bind_group_layout(u32::MAX);\n                },\n                Some(\"Bind group layout index 4294967295 is greater than the device's configured `max_bind_groups` limit\"),\n            );\n        });\n\n#[gpu_test]\nstatic RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().enable_noop())\n        .run_sync(|ctx| {\n            fail(\n                &ctx.device,\n                || {\n                    let module = ctx.device.create_shader_module(INVALID_SHADER_DESC);\n\n                    let pipeline =\n                        ctx.device\n                            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                                label: Some(\"render pipeline\"),\n                                layout: None,\n                                vertex: wgpu::VertexState {\n                                    module: &module,\n                                    entry_point: Some(\"doesn't exist\"),\n                                    compilation_options: Default::default(),\n                                    buffers: &[],\n                                },\n                                primitive: Default::default(),\n                                depth_stencil: None,\n                                multisample: Default::default(),\n                                fragment: None,\n                                multiview_mask: None,\n                                cache: None,\n                            });\n\n                    pipeline.get_bind_group_layout(0);\n                },\n                Some(\"Shader 'invalid shader' parsing error\"),\n            );\n        });\n\n#[gpu_test]\nstatic RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .enable_noop(),\n        )\n        .run_sync(|ctx| {\n            fail(\n                &ctx.device,\n                || {\n                    let vs_module = ctx.device.create_shader_module(TRIVIAL_VERTEX_SHADER_DESC);\n                    let fs_module = ctx\n                        .device\n                        .create_shader_module(TRIVIAL_FRAGMENT_SHADER_DESC);\n\n                    let pipeline =\n                        ctx.device\n                            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                                label: Some(\"render pipeline\"),\n                                layout: None,\n                                vertex: wgpu::VertexState {\n                                    module: &vs_module,\n                                    entry_point: Some(\"main\"),\n                                    compilation_options: Default::default(),\n                                    buffers: &[],\n                                },\n                                primitive: Default::default(),\n                                depth_stencil: None,\n                                multisample: Default::default(),\n                                fragment: Some(wgpu::FragmentState {\n                                    module: &fs_module,\n                                    entry_point: Some(\"main\"),\n                                    compilation_options: Default::default(),\n                                    targets: &[Some(wgpu::ColorTargetState {\n                                        format: wgpu::TextureFormat::Rgba8Unorm,\n                                        blend: None,\n                                        write_mask: wgpu::ColorWrites::ALL,\n                                    })],\n                                }),\n                                multiview_mask: None,\n                                cache: None,\n                            });\n\n                    pipeline.get_bind_group_layout(u32::MAX);\n                },\n                Some(\"Bind group layout index 4294967295 is greater than the device's configured `max_bind_groups` limit\"),\n            );\n        });\n\n#[gpu_test]\nstatic NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        fail(\n            &ctx.device,\n            || {\n                // Testing multisampling is important, because some backends don't behave well if one\n                // tries to compile code in an unsupported multisample count. Failing to validate here\n                // has historically resulted in requesting the back end to compile code.\n                for power_of_two in [1, 2, 4, 8, 16, 32, 64] {\n                    let _ = ctx\n                        .device\n                        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                            label: None,\n                            layout: None,\n                            vertex: wgpu::VertexState {\n                                module: &ctx\n                                    .device\n                                    .create_shader_module(TRIVIAL_VERTEX_SHADER_DESC),\n                                entry_point: Some(\"main\"),\n                                compilation_options: Default::default(),\n                                buffers: &[],\n                            },\n                            primitive: Default::default(),\n                            depth_stencil: None,\n                            multisample: wgpu::MultisampleState {\n                                count: power_of_two,\n                                ..Default::default()\n                            },\n                            fragment: None,\n                            multiview_mask: None,\n                            cache: None,\n                        });\n                }\n            },\n            Some(concat!(\n                \"At least one color attachment or depth-stencil attachment was expected, \",\n                \"but no render target for the pipeline was specified.\"\n            )),\n        )\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/pipeline_cache.rs",
    "content": "use std::{fmt::Write, num::NonZeroU64};\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(PIPELINE_CACHE);\n}\n\n/// We want to test that using a pipeline cache doesn't cause failure\n///\n/// It would be nice if we could also assert that reusing a pipeline cache would make compilation\n/// be faster however, some drivers use a fallback pipeline cache, which makes this inconsistent\n/// (both intra- and inter-run).\n#[gpu_test]\nstatic PIPELINE_CACHE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::PIPELINE_CACHE),\n    )\n    .run_async(pipeline_cache_test);\n\n/// Set to a higher value if adding a timing based assertion. This is otherwise fast to compile\nconst ARRAY_SIZE: u64 = 256;\n\n/// Create a shader which should be slow-ish to compile\nfn shader() -> String {\n    let mut body = String::new();\n    for idx in 0..ARRAY_SIZE {\n        // \"Safety\": There will only be a single workgroup, and a single thread in that workgroup\n        writeln!(body, \"    output[{idx}] = {idx}u;\")\n            .expect(\"`u64::fmt` and `String::write_fmt` are infallible\");\n    }\n\n    format!(\n        r#\"\n        @group(0) @binding(0)\n        var<storage, read_write> output: array<u32>;\n\n        @compute @workgroup_size(1)\n        fn main() {{\n        {body}\n        }}\n        \"#,\n    )\n}\n\nasync fn pipeline_cache_test(ctx: TestingContext) {\n    let shader = shader();\n    let sm = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"shader\"),\n            source: wgpu::ShaderSource::Wgsl(shader.into()),\n        });\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind_group_layout\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(ARRAY_SIZE * 4),\n                },\n                count: None,\n            }],\n        });\n\n    let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"gpu_buffer\"),\n        size: ARRAY_SIZE * 4,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"cpu_buffer\"),\n        size: ARRAY_SIZE * 4,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"bind_group\"),\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: gpu_buffer.as_entire_binding(),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let first_cache_data;\n    {\n        let first_cache = unsafe {\n            ctx.device\n                .create_pipeline_cache(&wgpu::PipelineCacheDescriptor {\n                    label: Some(\"pipeline_cache\"),\n                    data: None,\n                    fallback: false,\n                })\n        };\n        let first_pipeline = ctx\n            .device\n            .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                label: Some(\"pipeline\"),\n                layout: Some(&pipeline_layout),\n                module: &sm,\n                entry_point: Some(\"main\"),\n                compilation_options: Default::default(),\n                cache: Some(&first_cache),\n            });\n        validate_pipeline(&ctx, first_pipeline, &bind_group, &gpu_buffer, &cpu_buffer).await;\n        first_cache_data = first_cache.get_data();\n    }\n    assert!(first_cache_data.is_some());\n\n    let second_cache = unsafe {\n        ctx.device\n            .create_pipeline_cache(&wgpu::PipelineCacheDescriptor {\n                label: Some(\"pipeline_cache\"),\n                data: first_cache_data.as_deref(),\n                fallback: false,\n            })\n    };\n    let first_pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &sm,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: Some(&second_cache),\n        });\n    validate_pipeline(&ctx, first_pipeline, &bind_group, &gpu_buffer, &cpu_buffer).await;\n\n    // Ideally, we could assert here that the second compilation was faster than the first\n    // However, that doesn't actually work, because drivers have their own internal caches.\n    // This does work on my machine if I set `MESA_DISABLE_PIPELINE_CACHE=1`\n    // before running the test; but of course that is not a realistic scenario\n}\n\nasync fn validate_pipeline(\n    ctx: &TestingContext,\n    pipeline: wgpu::ComputePipeline,\n    bind_group: &wgpu::BindGroup,\n    gpu_buffer: &wgpu::Buffer,\n    cpu_buffer: &wgpu::Buffer,\n) {\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"encoder\"),\n        });\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"compute_pass\"),\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, Some(bind_group), &[]);\n\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(gpu_buffer, 0, cpu_buffer, 0, ARRAY_SIZE * 4);\n    ctx.queue.submit([encoder.finish()]);\n    cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = cpu_buffer.slice(..).get_mapped_range();\n\n    let arrays: &[u32] = bytemuck::cast_slice(&data);\n\n    assert_eq!(arrays.len(), ARRAY_SIZE as usize);\n    for (idx, value) in arrays.iter().copied().enumerate() {\n        assert_eq!(value as usize, idx);\n    }\n    drop(data);\n    cpu_buffer.unmap();\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/planar_texture/mod.rs",
    "content": "//! Tests for nv12 texture creation and sampling.\n\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        NV12_TEXTURE_CREATION_SAMPLING,\n        P010_TEXTURE_CREATION_SAMPLING,\n        NV12_TEXTURE_RENDERING,\n        NV12_TEXTURE_COPYING,\n        P010_TEXTURE_COPYING,\n    ]);\n}\n\n// Helper function to test planar texture creation and sampling.\nfn test_planar_texture_creation_sampling(\n    ctx: &TestingContext,\n    y_view: &wgpu::TextureView,\n    uv_view: &wgpu::TextureView,\n) {\n    let target_format = wgpu::TextureFormat::Bgra8UnormSrgb;\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"planar_texture_sampling.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"planar texture pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(target_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint32),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor {\n        min_filter: wgpu::FilterMode::Linear,\n        mag_filter: wgpu::FilterMode::Linear,\n        ..Default::default()\n    });\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::Sampler(&sampler),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: wgpu::BindingResource::TextureView(y_view),\n            },\n            wgpu::BindGroupEntry {\n                binding: 2,\n                resource: wgpu::BindingResource::TextureView(uv_view),\n            },\n        ],\n    });\n\n    let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: y_view.texture().size(),\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: target_format,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n    let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            ops: wgpu::Operations::default(),\n            resolve_target: None,\n            view: &target_view,\n            depth_slice: None,\n        })],\n        depth_stencil_attachment: None,\n        timestamp_writes: None,\n        occlusion_query_set: None,\n        multiview_mask: None,\n    });\n    rpass.set_pipeline(&pipeline);\n    rpass.set_bind_group(0, &bind_group, &[]);\n    rpass.draw(0..4, 0..1);\n    drop(rpass);\n    ctx.queue.submit([encoder.finish()]);\n}\n\n// Helper function to test rendering onto planar texture.\nfn test_planar_texture_rendering(\n    ctx: &TestingContext,\n    (y_view, y_format): (&wgpu::TextureView, wgpu::TextureFormat),\n    (uv_view, uv_format): (&wgpu::TextureView, wgpu::TextureFormat),\n) {\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"planar_texture_rendering.wgsl\"));\n    let y_pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"y plane pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_y_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(y_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint32),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let uv_pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"uv plane pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_uv_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(uv_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint32),\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: y_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        rpass.set_pipeline(&y_pipeline);\n        rpass.draw(0..3, 0..1);\n    }\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: uv_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        rpass.set_pipeline(&uv_pipeline);\n        rpass.draw(0..3, 0..1);\n    }\n\n    ctx.queue.submit([encoder.finish()]);\n}\n\n/// Ensures that creation and sampling of an NV12 format texture works as\n/// expected.\n#[gpu_test]\nstatic NV12_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::TEXTURE_FORMAT_NV12)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::NV12,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let y_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::R8Unorm),\n            aspect: wgpu::TextureAspect::Plane0,\n            ..Default::default()\n        });\n        let uv_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::Rg8Unorm),\n            aspect: wgpu::TextureAspect::Plane1,\n            ..Default::default()\n        });\n\n        test_planar_texture_creation_sampling(&ctx, &y_view, &uv_view);\n    });\n\n/// Ensures that creation and sampling of a P010 format texture works as\n/// expected.\n#[gpu_test]\nstatic P010_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(\n                wgpu::Features::TEXTURE_FORMAT_P010 | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM,\n            )\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::P010,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let y_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::R16Unorm),\n            aspect: wgpu::TextureAspect::Plane0,\n            ..Default::default()\n        });\n        let uv_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::Rg16Unorm),\n            aspect: wgpu::TextureAspect::Plane1,\n            ..Default::default()\n        });\n\n        test_planar_texture_creation_sampling(&ctx, &y_view, &uv_view);\n    });\n\n/// Ensures that rendering on to NV12 format texture works as expected.\n#[gpu_test]\nstatic NV12_TEXTURE_RENDERING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::TEXTURE_FORMAT_NV12)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::NV12,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let y_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::R8Unorm),\n            aspect: wgpu::TextureAspect::Plane0,\n            ..Default::default()\n        });\n        let uv_view = tex.create_view(&wgpu::TextureViewDescriptor {\n            format: Some(wgpu::TextureFormat::Rg8Unorm),\n            aspect: wgpu::TextureAspect::Plane1,\n            ..Default::default()\n        });\n\n        test_planar_texture_rendering(\n            &ctx,\n            (&y_view, wgpu::TextureFormat::R8Unorm),\n            (&uv_view, wgpu::TextureFormat::Rg8Unorm),\n        );\n    });\n\n/// Ensures that copying NV12 texture to NV12 texture works as expected\n#[gpu_test]\nstatic NV12_TEXTURE_COPYING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::TEXTURE_FORMAT_NV12)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n        let input_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::NV12,\n            usage: wgpu::TextureUsages::COPY_SRC,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let output_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::NV12,\n            usage: wgpu::TextureUsages::COPY_DST,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n\n        let mut command_encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        command_encoder.copy_texture_to_texture(\n            input_texture.as_image_copy(),\n            output_texture.as_image_copy(),\n            size,\n        );\n        ctx.queue.submit([command_encoder.finish()]);\n    });\n\n/// Ensures that copying P010 texture to P010 texture works as expected\n#[gpu_test]\nstatic P010_TEXTURE_COPYING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(\n                wgpu::Features::TEXTURE_FORMAT_P010 | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM,\n            )\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let size = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n        let input_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::P010,\n            usage: wgpu::TextureUsages::COPY_SRC,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let output_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: wgpu::TextureFormat::P010,\n            usage: wgpu::TextureUsages::COPY_DST,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n\n        let mut command_encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n        command_encoder.copy_texture_to_texture(\n            input_texture.as_image_copy(),\n            output_texture.as_image_copy(),\n            size,\n        );\n        ctx.queue.submit([command_encoder.finish()]);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/planar_texture/planar_texture_rendering.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\n\nconst VERTICES: array<vec3<f32>, 3> = array<vec3<f32>, 3>(\n    vec3<f32>(-0.5, 0.0, 0.0),\n    vec3<f32>(0.5, 0.0, 0.0),\n    vec3<f32>(0.0, 1.0, 0.0),\n);\n\n@vertex\nfn vs_main(@builtin(vertex_index) idx: u32) -> VertexOutput {\n    var output: VertexOutput;\n    output.position = vec4(VERTICES[idx], 1.0);\n    return output;\n}\n\n@fragment\nfn fs_y_main(input: VertexOutput) -> @location(0) f32 {\n    let color = vec3<f32>(1.0);\n    let conversion_weights = vec3<f32>(0.2126, 0.7152, 0.0722);\n    return clamp(dot(color, conversion_weights), 0.0, 1.0);\n}\n\n@fragment\nfn fs_uv_main(input: VertexOutput) -> @location(0) vec2<f32> {\n    let color = vec3<f32>(1.0);\n    let conversion_weights = mat3x2<f32>(\n        -0.1146, 0.5,\n        -0.3854, -0.4542,\n        0.5, -0.0458,\n    );\n    let conversion_bias = vec2<f32>(0.5, 0.5);\n    return clamp(conversion_weights * color + conversion_bias, vec2(0.0, 0.0), vec2(1.0, 1.0));\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/planar_texture/planar_texture_sampling.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) pos: vec4<f32>,\n    @location(0) uv: vec2<f32>,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {\n    var output: VertexOutput;\n    // 0, 0\n    // 2, 0\n    // 0, 2\n    // 2, 2\n    let v_data = vec2<f32>(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u));\n    output.pos = vec4<f32>(v_data - 1.0, 0.0, 1.0);\n    output.uv = v_data / 2.0;\n    return output;\n}\n\n@group(0) @binding(0) var s: sampler;\n@group(0) @binding(1) var tex_y: texture_2d<f32>;\n@group(0) @binding(2) var tex_uv: texture_2d<f32>;\n\n@fragment\nfn fs_main(v_output: VertexOutput) -> @location(0) vec4<f32> {\n    let luminance = textureSample(tex_y, s, v_output.uv).r;\n    let chrominance = textureSample(tex_uv, s, v_output.uv).rg;\n    let rgb = mat3x3<f32>(\n        1.000000, 1.000000, 1.000000,\n        0.000000,-0.187324, 1.855600,\n        1.574800,-0.468124, 0.000000,\n    ) * vec3<f32>(luminance, chrominance.r - 0.5, chrominance.g - 0.5);\n    return vec4<f32>(rgb, 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/poll.rs",
    "content": "use std::{num::NonZeroU64, time::Duration};\n\nuse wgpu::{\n    BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,\n    BindingResource, BindingType, BufferBindingType, BufferDescriptor, BufferUsages, CommandBuffer,\n    CommandEncoderDescriptor, ComputePassDescriptor, PollType, ShaderStages,\n};\n\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        WAIT,\n        WAIT_WITH_TIMEOUT,\n        WAIT_WITH_TIMEOUT_MAX,\n        DOUBLE_WAIT,\n        WAIT_ON_SUBMISSION,\n        WAIT_ON_SUBMISSION_WITH_TIMEOUT,\n        WAIT_ON_SUBMISSION_WITH_TIMEOUT_MAX,\n        DOUBLE_WAIT_ON_SUBMISSION,\n        WAIT_OUT_OF_ORDER,\n        WAIT_AFTER_BAD_SUBMISSION,\n    ]);\n}\n\nfn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer {\n    let buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: 16,\n        usage: BufferUsages::UNIFORM,\n        mapped_at_creation: false,\n    });\n\n    let bind_group_layout = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::COMPUTE,\n                ty: BindingType::Buffer {\n                    ty: BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: Some(NonZeroU64::new(16).unwrap()),\n                },\n                count: None,\n            }],\n        });\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &bind_group_layout,\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::Buffer(buffer.as_entire_buffer_binding()),\n        }],\n    });\n\n    let mut cmd_buf = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default());\n    cpass.set_bind_group(0, &bind_group, &[]);\n    drop(cpass);\n\n    cmd_buf.finish()\n}\n\n#[gpu_test]\nstatic WAIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: None,\n            timeout: None,\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_WITH_TIMEOUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: None,\n            timeout: Some(Duration::from_secs(1)),\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_WITH_TIMEOUT_MAX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: None,\n            timeout: Some(Duration::MAX),\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic DOUBLE_WAIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: None,\n            timeout: None,\n        })\n        .await\n        .unwrap();\n        ctx.async_poll(PollType::Wait {\n            submission_index: None,\n            timeout: None,\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        let index = ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index),\n            timeout: None,\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_ON_SUBMISSION_WITH_TIMEOUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        let index = ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index),\n            timeout: Some(Duration::from_secs(1)),\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_ON_SUBMISSION_WITH_TIMEOUT_MAX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        let index = ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index),\n            timeout: Some(Duration::MAX),\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf = generate_dummy_work(&ctx);\n\n        let index = ctx.queue.submit(Some(cmd_buf));\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index.clone()),\n            timeout: None,\n        })\n        .await\n        .unwrap();\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index),\n            timeout: None,\n        })\n        .await\n        .unwrap();\n    });\n\n#[gpu_test]\nstatic WAIT_OUT_OF_ORDER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let cmd_buf1 = generate_dummy_work(&ctx);\n        let cmd_buf2 = generate_dummy_work(&ctx);\n\n        let index1 = ctx.queue.submit(Some(cmd_buf1));\n        let index2 = ctx.queue.submit(Some(cmd_buf2));\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index2),\n            timeout: None,\n        })\n        .await\n        .unwrap();\n        ctx.async_poll(PollType::Wait {\n            submission_index: Some(index1),\n            timeout: None,\n        })\n        .await\n        .unwrap();\n    });\n\n/// Submit a command buffer to the wrong device. A wait poll shouldn't hang.\n///\n/// We can't catch panics on Wasm, since they get reported directly to the\n/// console.\n#[gpu_test]\nstatic WAIT_AFTER_BAD_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        wgpu_test::TestParameters::default()\n            .skip(wgpu_test::FailureCase::webgl2())\n            .enable_noop(),\n    )\n    .run_async(wait_after_bad_submission);\n\nasync fn wait_after_bad_submission(ctx: TestingContext) {\n    let (device2, queue2) =\n        wgpu_test::initialize_device(&ctx.adapter, ctx.device_features, ctx.device_limits.clone())\n            .await;\n\n    let command_buffer1 = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default())\n        .finish();\n\n    // This should panic, since the command buffer belongs to the wrong\n    // device, and queue submission errors seem to be fatal errors?\n    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n        queue2.submit([command_buffer1]);\n    }));\n    assert!(result.is_err());\n\n    // This should not hang.\n    //\n    // Specifically, the failed submission should not cause a new fence value to\n    // be allocated that will not be signalled until further work is\n    // successfully submitted, causing a greater fence value to be signalled.\n    device2.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/primitive_index.rs",
    "content": "use wgpu::{\n    ColorTargetState, ColorWrites, Features, FragmentState, Limits, MeshPipelineDescriptor,\n    MeshState, RenderPipelineDescriptor, ShaderModuleDescriptor, TextureFormat, VertexState,\n};\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.push(PRIMITIVE_INDEX);\n    tests.push(PRIMITIVE_INDEX_MESH_FRAGMENT);\n    tests.push(PRIMITIVE_INDEX_MESH_NOT_FRAGMENT);\n    tests.push(PRIMITIVE_INDEX_FRAGMENT_NOT_MESH);\n}\n\nasync fn primitive_index(ctx: TestingContext) {\n    const CODE: &str = \"\\\nenable primitive_index;\n@vertex\nfn vertex() -> @builtin(position) vec4<f32> {\n    return vec4<f32>(1.0, 1.0, 1.0, 1.0);\n}\n@fragment\nfn fragment(@builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    return vec4<f32>(f32(index), 1.0, 1.0, 1.0);\n}\n\";\n    let module = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(CODE)),\n    });\n    let _ = ctx\n        .device\n        .create_render_pipeline(&RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: VertexState {\n                module: &module,\n                entry_point: None,\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: Default::default(),\n            depth_stencil: None,\n            multisample: Default::default(),\n            fragment: Some(FragmentState {\n                module: &module,\n                entry_point: None,\n                compilation_options: Default::default(),\n                targets: &[Some(ColorTargetState {\n                    format: TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: ColorWrites::all(),\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n}\n\n#[gpu_test]\nstatic PRIMITIVE_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().features(Features::PRIMITIVE_INDEX))\n    .run_async(primitive_index);\n\nasync fn mesh_primitive_index(\n    ctx: TestingContext,\n    mesh_primitive_index: bool,\n    fragment_primitive_index: bool,\n) {\n    const CODE: &str = \"\\\nenable primitive_index;\nenable wgpu_mesh_shader;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n}\nstruct PrimitiveOutput {\n    @builtin(triangle_indices) indices: vec3<u32>,MESH_PRIMITIVE_INDEX\n}\nstruct MeshOutput {\n    @builtin(vertices) vertices: array<VertexOutput, 3>,\n    @builtin(vertex_count) vertex_count: u32,\n    @builtin(primitives) primitives: array<PrimitiveOutput, 3>,\n    @builtin(primitive_count) primitive_count: u32,\n}\nvar<workgroup> mesh_output: MeshOutput;\n\n@mesh(mesh_output)\n@workgroup_size(1)\nfn mesh() {\n    mesh_output.vertex_count = 0;\n    mesh_output.primitive_count = 0;\n}\n@fragment\nfn fragment(FRAGMENT_PRIMITIVE_INDEX) -> @location(0) vec4<f32> {\n    return vec4<f32>(f32(index), 1.0, 1.0, 1.0);\n}\n\";\n    let used_code = CODE\n        .replace(\n            \"MESH_PRIMITIVE_INDEX\",\n            if mesh_primitive_index {\n                \"\\n@builtin(primitive_index) index: u32,\"\n            } else {\n                \"\"\n            },\n        )\n        .replace(\n            \"FRAGMENT_PRIMITIVE_INDEX\",\n            if fragment_primitive_index {\n                \"@builtin(primitive_index) index: u32\"\n            } else {\n                \"\"\n            },\n        );\n    let module = ctx.device.create_shader_module(ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&used_code)),\n    });\n    let _ = ctx.device.create_mesh_pipeline(&MeshPipelineDescriptor {\n        label: None,\n        layout: None,\n        task: None,\n        mesh: MeshState {\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n        },\n        primitive: Default::default(),\n        depth_stencil: None,\n        multisample: Default::default(),\n        fragment: Some(FragmentState {\n            module: &module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            targets: &[Some(ColorTargetState {\n                format: TextureFormat::Rgba8Unorm,\n                blend: None,\n                write_mask: ColorWrites::all(),\n            })],\n        }),\n        multiview: None,\n        cache: None,\n    });\n}\n\nfn mesh_params() -> TestParameters {\n    // TODO: when naga support for MSL/HLSL mesh shaders lands enable Metal and DX12\n    TestParameters::default()\n        .features(Features::PRIMITIVE_INDEX | Features::EXPERIMENTAL_MESH_SHADER)\n        .limits(Limits::defaults().using_recommended_minimum_mesh_shader_values())\n        .skip(FailureCase::backend(\n            wgpu::Backends::METAL | wgpu::Backends::DX12,\n        ))\n}\n\n#[gpu_test]\nstatic PRIMITIVE_INDEX_MESH_FRAGMENT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params())\n    .run_async(async |ctx| mesh_primitive_index(ctx, true, true).await);\n\n#[gpu_test]\nstatic PRIMITIVE_INDEX_MESH_NOT_FRAGMENT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params().expect_fail(FailureCase::always()))\n    .run_async(async |ctx| mesh_primitive_index(ctx, true, false).await);\n\n#[gpu_test]\nstatic PRIMITIVE_INDEX_FRAGMENT_NOT_MESH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(mesh_params().expect_fail(FailureCase::always()))\n    .run_async(async |ctx| mesh_primitive_index(ctx, false, true).await);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/query_set.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(DROP_FAILED_TIMESTAMP_QUERY_SET);\n}\n\n#[gpu_test]\nstatic DROP_FAILED_TIMESTAMP_QUERY_SET: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        // Enter an error scope, so the validation catch-all doesn't\n        // report the error too early.\n        let scope = ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n        // Creating this query set should fail, since we didn't include\n        // TIMESTAMP_QUERY in our required features.\n        let bad_query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n            label: Some(\"doomed query set\"),\n            ty: wgpu::QueryType::Timestamp,\n            count: 1,\n        });\n\n        // Dropping this should not panic.\n        drop(bad_query_set);\n\n        assert!(pollster::block_on(scope.pop()).is_some());\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/queue_transfer.rs",
    "content": "//! Tests for buffer copy validation.\n\nuse wgpu::PollType;\nuse wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        QUEUE_WRITE_TEXTURE_THEN_DESTROY,\n        QUEUE_WRITE_TEXTURE_OVERFLOW,\n        QUEUE_WRITE_TEXTURE_BUFFER_OOB,\n    ]);\n}\n\n#[gpu_test]\nstatic QUEUE_WRITE_TEXTURE_THEN_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 64,\n                height: 32,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba32Float,\n            usage: wgpu::TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n\n        let data = vec![255; 1024];\n\n        ctx.queue.write_texture(\n            wgpu::TexelCopyTextureInfo {\n                texture: &texture,\n                mip_level: 0,\n                origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },\n                aspect: wgpu::TextureAspect::All,\n            },\n            &data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(1024),\n                rows_per_image: Some(32),\n            },\n            wgpu::Extent3d {\n                width: 64,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        // Unlike textures used in a command buffer, which must not be destroyed prior to calling\n        // submit, it is permissible to destroy a texture used in an immediate queue operation\n        // before calling submit.\n        texture.destroy();\n\n        ctx.queue.submit([]);\n        ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n    });\n\n#[gpu_test]\nstatic QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 146,\n                height: 25,\n                depth_or_array_layers: 192,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba32Float,\n            usage: wgpu::TextureUsages::COPY_DST,\n            view_formats: &[],\n        });\n\n        let data = vec![255; 128];\n\n        fail(\n            &ctx.device,\n            || {\n                ctx.queue.write_texture(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &texture,\n                        mip_level: 0,\n                        origin: wgpu::Origin3d { x: 0, y: 0, z: 1 },\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    &data,\n                    wgpu::TexelCopyBufferLayout {\n                        offset: 0,\n                        bytes_per_row: Some(879161360),\n                        //bytes_per_image: 4294967295,\n                        rows_per_image: Some(4294967295 / 879161360),\n                    },\n                    wgpu::Extent3d {\n                        width: 3056263286,\n                        height: 64,\n                        depth_or_array_layers: 4294967295,\n                    },\n                );\n            },\n            Some(\"end up overrunning the bounds of the destination texture\"),\n        );\n    });\n\n#[gpu_test]\nstatic QUEUE_WRITE_TEXTURE_BUFFER_OOB: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_sync(|ctx| {\n        // Test that transfers overrunning the end of the source buffer, or\n        // where offset + size overflows a u64, are rejected.\n        for offset in [120, u64::MAX - 3] {\n            let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                label: None,\n                size: wgpu::Extent3d {\n                    width: 146,\n                    height: 25,\n                    depth_or_array_layers: 192,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba32Float,\n                usage: wgpu::TextureUsages::COPY_DST,\n                view_formats: &[],\n            });\n\n            let data = vec![255; 128];\n\n            fail(\n                &ctx.device,\n                || {\n                    ctx.queue.write_texture(\n                        wgpu::TexelCopyTextureInfo {\n                            texture: &texture,\n                            mip_level: 0,\n                            origin: wgpu::Origin3d { x: 0, y: 0, z: 1 },\n                            aspect: wgpu::TextureAspect::All,\n                        },\n                        &data,\n                        wgpu::TexelCopyBufferLayout {\n                            offset,\n                            bytes_per_row: Some(16),\n                            rows_per_image: Some(1),\n                        },\n                        wgpu::Extent3d {\n                            width: 1,\n                            height: 1,\n                            depth_or_array_layers: 1,\n                        },\n                    );\n                },\n                Some(\"would end up overrunning the bounds of the source buffer\"),\n            );\n        }\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/as_build.rs",
    "content": "use std::iter;\n\nuse crate::ray_tracing::{acceleration_structure_limits, AsBuildContext};\nuse wgpu::util::{BufferInitDescriptor, DeviceExt};\nuse wgpu::*;\nuse wgpu_test::{\n    fail, fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([\n        UNBUILT_BLAS,\n        UNBUILT_BLAS_COMPACTION,\n        BLAS_COMPACTION_WITHOUT_FLAGS,\n        UNPREPARED_BLAS_COMPACTION,\n        BLAS_COMPACTION,\n        OUT_OF_ORDER_AS_BUILD,\n        OUT_OF_ORDER_AS_BUILD_USE,\n        EMPTY_BUILD,\n        BUILD_WITH_TRANSFORM,\n        ONLY_BLAS_VERTEX_RETURN,\n        ONLY_TLAS_VERTEX_RETURN,\n        EXTRA_FORMAT_BUILD,\n        MISALIGNED_BUILD,\n        TOO_SMALL_STRIDE_BUILD,\n        BLAS_FIRST_VERTEX,\n    ]);\n}\n\n#[gpu_test]\nstatic UNBUILT_BLAS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(unbuilt_blas);\n\nfn unbuilt_blas(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    // Build the TLAS package with an unbuilt BLAS.\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    encoder.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    fail(\n        &ctx.device,\n        || {\n            ctx.queue.submit([encoder.finish()]);\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic UNBUILT_BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(unbuilt_blas_compaction);\n\nfn unbuilt_blas_compaction(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::ALLOW_COMPACTION,\n        AccelerationStructureFlags::empty(),\n    );\n\n    fail(\n        &ctx.device,\n        || {\n            // Prepare checks the BLAS has been built\n            as_ctx.blas.prepare_compaction_async(|_| {})\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic BLAS_COMPACTION_WITHOUT_FLAGS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(blas_compaction_without_flags);\n\nfn blas_compaction_without_flags(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    encoder.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    fail(\n        &ctx.device,\n        || {\n            // Prepare checks whether te BLAS is able to be compacted\n            as_ctx.blas.prepare_compaction_async(|_| {})\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic UNPREPARED_BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(unprepared_blas_compaction);\n\nfn unprepared_blas_compaction(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::ALLOW_COMPACTION,\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    encoder.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    fail(&ctx.device, || ctx.queue.compact_blas(&as_ctx.blas), None);\n}\n\n#[gpu_test]\nstatic BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(blas_compaction);\n\nfn blas_compaction(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::ALLOW_COMPACTION,\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    // Build the BLAS to be compacted (so compaction is valid).\n    encoder.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    ctx.queue.submit([encoder.finish()]);\n\n    // Prepare the BLAS to be compacted.\n    let (send, recv) = std::sync::mpsc::channel();\n    as_ctx.blas.prepare_compaction_async(move |res| {\n        res.unwrap();\n        send.send(()).unwrap();\n    });\n\n    // On native this will trigger the callback.\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n    // Check that the callback actually gets called (this test will timeout if it doesn't).\n    recv.recv().unwrap();\n    // This should return true because the callback has been called, and we haven't rebuilt the BLAS\n    assert!(as_ctx.blas.ready_for_compaction());\n\n    let compacted = ctx.queue.compact_blas(&as_ctx.blas);\n\n    // This actually executes the compact call.\n    ctx.queue.submit([]);\n\n    let mut fail_encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    // Try to build the compacted BLAS, this should fail.\n    let mut build_entry = as_ctx.blas_build_entry();\n    build_entry.blas = &compacted;\n\n    fail_encoder.build_acceleration_structures([&build_entry], []);\n    fail(&ctx.device, || fail_encoder.finish(), None);\n}\n\n#[gpu_test]\nstatic OUT_OF_ORDER_AS_BUILD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(out_of_order_as_build);\n\nfn out_of_order_as_build(ctx: TestingContext) {\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    //\n    // Encode the TLAS build before the BLAS build, but submit them in the right order.\n    //\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 1\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 1\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    ctx.queue\n        .submit([encoder_blas.finish(), encoder_tlas.finish()]);\n\n    drop(as_ctx);\n\n    //\n    // Create a clean `AsBuildContext`\n    //\n\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    //\n    // Encode the BLAS build before the TLAS build, but submit them in the wrong order.\n    //\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 2\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 2\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    fail(\n        &ctx.device,\n        || {\n            ctx.queue\n                .submit([encoder_tlas.finish(), encoder_blas.finish()]);\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic OUT_OF_ORDER_AS_BUILD_USE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(out_of_order_as_build_use);\n\nfn out_of_order_as_build_use(ctx: TestingContext) {\n    //\n    // Create a clean `AsBuildContext`\n    //\n\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    //\n    // Build in the right order, then rebuild the BLAS so the TLAS is invalid, then use the TLAS.\n    //\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 1\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 1\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    let mut encoder_blas2 = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 2\"),\n        });\n\n    encoder_blas2.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    ctx.queue.submit([\n        encoder_blas.finish(),\n        encoder_tlas.finish(),\n        encoder_blas2.finish(),\n    ]);\n\n    //\n    // Create shader to use tlas with\n    //\n\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n    let compute_pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"basic_usage\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &compute_pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n        }],\n    });\n\n    //\n    // Use TLAS\n    //\n\n    let mut encoder_compute = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut pass = encoder_compute.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&compute_pipeline);\n        pass.set_bind_group(0, Some(&bind_group), &[]);\n        pass.dispatch_workgroups(1, 1, 1)\n    }\n\n    fail(\n        &ctx.device,\n        || {\n            ctx.queue.submit(Some(encoder_compute.finish()));\n        },\n        None,\n    );\n\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    //\n    // Build in the right order, then rebuild the BLAS so the TLAS is invalid, then use the TLAS.\n    //\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 3\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_blas2 = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 4\"),\n        });\n\n    encoder_blas2.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 2\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    ctx.queue.submit([\n        encoder_blas.finish(),\n        encoder_tlas.finish(),\n        encoder_blas2.finish(),\n    ]);\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &compute_pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n        }],\n    });\n\n    //\n    // Use TLAS\n    //\n\n    let mut encoder_compute = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut pass = encoder_compute.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&compute_pipeline);\n        pass.set_bind_group(0, Some(&bind_group), &[]);\n        pass.dispatch_workgroups(1, 1, 1)\n    }\n\n    fail(\n        &ctx.device,\n        || {\n            ctx.queue.submit(Some(encoder_compute.finish()));\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic EMPTY_BUILD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(empty_build);\nfn empty_build(ctx: TestingContext) {\n    let mut encoder_safe = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 1\"),\n        });\n\n    encoder_safe.build_acceleration_structures(iter::empty(), iter::empty());\n\n    ctx.queue.submit([encoder_safe.finish()]);\n}\n\n#[gpu_test]\nstatic BUILD_WITH_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(build_with_transform);\n\nfn build_with_transform(ctx: TestingContext) {\n    let vertices = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: &[0; size_of::<[[f32; 3]; 3]>()],\n        usage: BufferUsages::BLAS_INPUT,\n    });\n\n    let transform = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Vertex Buffer\"),\n            contents: bytemuck::cast_slice(&[\n                1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,\n            ]),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n        });\n\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x3,\n        vertex_count: 3,\n        index_format: None,\n        index_count: None,\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    let blas = ctx.device.create_blas(\n        &CreateBlasDescriptor {\n            label: Some(\"BLAS\"),\n            flags: AccelerationStructureFlags::PREFER_FAST_TRACE\n                | AccelerationStructureFlags::USE_TRANSFORM,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        },\n        BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_size.clone()],\n        },\n    );\n\n    let mut tlas = ctx.device.create_tlas(&CreateTlasDescriptor {\n        label: Some(\"TLAS\"),\n        max_instances: 1,\n        flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n        update_mode: AccelerationStructureUpdateMode::Build,\n    });\n    tlas[0] = Some(TlasInstance::new(\n        &blas,\n        [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],\n        0,\n        0xFF,\n    ));\n\n    let mut encoder_build = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BUILD 1\"),\n        });\n\n    encoder_build.build_acceleration_structures(\n        [&BlasBuildEntry {\n            blas: &blas,\n            geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n                size: &blas_size,\n                vertex_buffer: &vertices,\n                first_vertex: 0,\n                vertex_stride: size_of::<[f32; 3]>() as BufferAddress,\n                index_buffer: None,\n                first_index: None,\n                transform_buffer: Some(&transform),\n                transform_buffer_offset: Some(0),\n            }]),\n        }],\n        [&tlas],\n    );\n    ctx.queue.submit([encoder_build.finish()]);\n}\n\n#[gpu_test]\nstatic ONLY_BLAS_VERTEX_RETURN: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(\n                wgpu::Features::EXPERIMENTAL_RAY_QUERY\n                    | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN,\n            ),\n    )\n    .run_sync(only_blas_vertex_return);\n\nfn only_blas_vertex_return(ctx: TestingContext) {\n    // Set up BLAS with TLAS\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 1\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 1\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n\n    ctx.queue\n        .submit([encoder_blas.finish(), encoder_tlas.finish()]);\n\n    // Create a bind-group containing a TLAS with a bind-group layout that requires vertex return,\n    // because only the BLAS and not the TLAS has `AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN`\n    // this is invalid.\n    {\n        let bind_group_layout = ctx\n            .device\n            .create_bind_group_layout(&BindGroupLayoutDescriptor {\n                label: None,\n                entries: &[BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::AccelerationStructure {\n                        vertex_return: true,\n                    },\n                    count: None,\n                }],\n            });\n        fail(\n            &ctx.device,\n            || {\n                let _ = ctx.device.create_bind_group(&BindGroupDescriptor {\n                    label: None,\n                    layout: &bind_group_layout,\n                    entries: &[BindGroupEntry {\n                        binding: 0,\n                        resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n                    }],\n                });\n            },\n            None,\n        );\n        // drop these\n    }\n\n    // We then use it with a shader that does not require vertex return which should succeed.\n    {\n        //\n        // Create shader to use tlas with\n        //\n\n        let shader = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n        let compute_pipeline = ctx\n            .device\n            .create_compute_pipeline(&ComputePipelineDescriptor {\n                label: None,\n                layout: None,\n                module: &shader,\n                entry_point: Some(\"basic_usage\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n            label: None,\n            layout: &compute_pipeline.get_bind_group_layout(0),\n            entries: &[BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n            }],\n        });\n\n        //\n        // Use TLAS\n        //\n\n        let mut encoder_compute = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor::default());\n        {\n            let mut pass = encoder_compute.begin_compute_pass(&ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            pass.set_pipeline(&compute_pipeline);\n            pass.set_bind_group(0, Some(&bind_group), &[]);\n            pass.dispatch_workgroups(1, 1, 1)\n        }\n\n        ctx.queue.submit(Some(encoder_compute.finish()));\n    }\n}\n\n#[gpu_test]\nstatic ONLY_TLAS_VERTEX_RETURN: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(\n                wgpu::Features::EXPERIMENTAL_RAY_QUERY\n                    | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN,\n            )\n            .enable_noop(),\n    )\n    .run_sync(only_tlas_vertex_return);\n\nfn only_tlas_vertex_return(ctx: TestingContext) {\n    // Set up BLAS with TLAS\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,\n    );\n\n    let mut encoder_blas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS 1\"),\n        });\n\n    encoder_blas.build_acceleration_structures([&as_ctx.blas_build_entry()], []);\n\n    let mut encoder_tlas = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"TLAS 1\"),\n        });\n\n    encoder_tlas.build_acceleration_structures([], [&as_ctx.tlas]);\n    fail(&ctx.device, || encoder_tlas.finish(), None);\n}\n\n#[gpu_test]\nstatic EXTRA_FORMAT_BUILD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(\n                wgpu::Features::EXPERIMENTAL_RAY_QUERY\n                    | wgpu::Features::EXTENDED_ACCELERATION_STRUCTURE_VERTEX_FORMATS,\n            )\n            .enable_noop(),\n    )\n    .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Snorm16x4, 6, false));\n\n#[gpu_test]\nstatic MISALIGNED_BUILD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    // Larger than the minimum size, but not aligned as required\n    .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Float32x3, 13, true));\n\n#[gpu_test]\nstatic TOO_SMALL_STRIDE_BUILD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    // Aligned as required, but smaller than minimum size\n    .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Float32x3, 8, true));\n\nfn test_as_build_format_stride(\n    ctx: TestingContext,\n    format: VertexFormat,\n    stride: BufferAddress,\n    invalid_combination: bool,\n) {\n    let vertices = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: &vec![0; (format.min_acceleration_structure_vertex_stride() * 3) as usize],\n        usage: BufferUsages::BLAS_INPUT,\n    });\n\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        // The fourth component is ignored, and it allows us to have a smaller stride.\n        vertex_format: format,\n        vertex_count: 3,\n        index_format: None,\n        index_count: None,\n        flags: wgpu::AccelerationStructureGeometryFlags::empty(),\n    };\n\n    let blas = ctx.device.create_blas(\n        &CreateBlasDescriptor {\n            label: Some(\"BLAS\"),\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        },\n        BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_size.clone()],\n        },\n    );\n\n    let mut command_encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"BLAS_1\"),\n        });\n    command_encoder.build_acceleration_structures(\n        &[BlasBuildEntry {\n            blas: &blas,\n            geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n                size: &blas_size,\n                vertex_buffer: &vertices,\n                first_vertex: 0,\n                vertex_stride: stride,\n                index_buffer: None,\n                first_index: None,\n                transform_buffer: None,\n                transform_buffer_offset: None,\n            }]),\n        }],\n        &[],\n    );\n    let command_buffer = fail_if(\n        &ctx.device,\n        invalid_combination,\n        || command_encoder.finish(),\n        None,\n    );\n    if !invalid_combination {\n        ctx.queue.submit([command_buffer]);\n    }\n}\n\n#[gpu_test]\nstatic BLAS_FIRST_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(blas_first_vertex);\n\nfn blas_first_vertex(ctx: TestingContext) {\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x3,\n        vertex_count: 3,\n        index_format: None,\n        index_count: None,\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    let blas = ctx.device.create_blas(\n        &CreateBlasDescriptor {\n            label: Some(\"BLAS\"),\n            flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        },\n        BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_size.clone()],\n        },\n    );\n\n    let large_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Large blas building buffer\"),\n        size: (size_of::<[f32; 3]>() * 12) as _,\n        usage: BufferUsages::BLAS_INPUT,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n\n    let entry = BlasBuildEntry {\n        blas: &blas,\n        geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n            size: &blas_size,\n            vertex_buffer: &large_buffer,\n            // Leaves 3 at the end to build with.\n            first_vertex: 9,\n            vertex_stride: size_of::<[f32; 3]>() as BufferAddress,\n            index_buffer: None,\n            first_index: None,\n            transform_buffer: None,\n            transform_buffer_offset: None,\n        }]),\n    };\n\n    encoder.build_acceleration_structures([&entry], []);\n\n    ctx.queue.submit([encoder.finish()]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/as_create.rs",
    "content": "use crate::ray_tracing::acceleration_structure_limits;\nuse wgpu::{\n    AccelerationStructureFlags, AccelerationStructureGeometryFlags,\n    AccelerationStructureUpdateMode, BlasGeometrySizeDescriptors,\n    BlasTriangleGeometrySizeDescriptor, CreateBlasDescriptor,\n};\nuse wgpu::{IndexFormat, VertexFormat};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{fail, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    tests.extend([\n        BLAS_INVALID_VERTEX_FORMAT,\n        BLAS_MISMATCHED_INDEX,\n        UNSUPPORTED_ACCELERATION_STRUCTURE_RESOURCES,\n    ]);\n}\n\n#[gpu_test]\nstatic BLAS_INVALID_VERTEX_FORMAT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(invalid_vertex_format_blas_create);\n\nfn invalid_vertex_format_blas_create(ctx: TestingContext) {\n    //\n    // Create a BLAS with a format that is not allowed\n    //\n\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x4,\n        vertex_count: 3,\n        index_format: None,\n        index_count: None,\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: Some(\"BLAS\"),\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![blas_size.clone()],\n                },\n            );\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic BLAS_MISMATCHED_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(mismatched_index_blas_create);\n\nfn mismatched_index_blas_create(ctx: TestingContext) {\n    //\n    // Create a BLAS with just an index format\n    //\n\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x3,\n        vertex_count: 3,\n        index_format: Some(IndexFormat::Uint32),\n        index_count: None,\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: Some(\"BLAS1\"),\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![blas_size.clone()],\n                },\n            );\n        },\n        None,\n    );\n\n    //\n    // Create a BLAS with just an index count\n    //\n\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x3,\n        vertex_count: 3,\n        index_format: None,\n        index_count: Some(3),\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: Some(\"BLAS2\"),\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![blas_size.clone()],\n                },\n            );\n        },\n        None,\n    );\n}\n\n#[gpu_test]\nstatic UNSUPPORTED_ACCELERATION_STRUCTURE_RESOURCES: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().test_features_limits())\n        .run_sync(unsupported_acceleration_structure_resources);\n\nfn unsupported_acceleration_structure_resources(ctx: TestingContext) {\n    fail(\n        &ctx.device,\n        || {\n            ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                label: None,\n                size: 4,\n                usage: wgpu::BufferUsages::BLAS_INPUT,\n                mapped_at_creation: false,\n            })\n        },\n        None,\n    );\n    fail(\n        &ctx.device,\n        || {\n            ctx.device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::AccelerationStructure {\n                            vertex_return: false,\n                        },\n                        count: None,\n                    }],\n                })\n        },\n        None,\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/as_use_after_free.rs",
    "content": "use crate::ray_tracing::acceleration_structure_limits;\nuse std::{iter, mem};\nuse wgpu::{\n    include_wgsl,\n    util::{BufferInitDescriptor, DeviceExt},\n    AccelerationStructureFlags, AccelerationStructureGeometryFlags,\n    AccelerationStructureUpdateMode, BindGroupDescriptor, BindGroupEntry, BindingResource,\n    BlasBuildEntry, BlasGeometries, BlasGeometrySizeDescriptors, BlasTriangleGeometry,\n    BlasTriangleGeometrySizeDescriptor, BufferAddress, BufferUsages, CommandEncoderDescriptor,\n    ComputePassDescriptor, ComputePipelineDescriptor, CreateBlasDescriptor, CreateTlasDescriptor,\n    PollType, TlasInstance, VertexFormat,\n};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    tests.push(ACCELERATION_STRUCTURE_USE_AFTER_FREE);\n}\n\nfn required_features() -> wgpu::Features {\n    wgpu::Features::EXPERIMENTAL_RAY_QUERY\n}\n\n/// This test creates a blas, puts a reference to it in a tlas instance inside a tlas package,\n/// drops the blas, and ensures it gets kept alive by the tlas instance. Then it uses the built\n/// package in a bindgroup, drops it, and checks that it is kept alive by the bindgroup by\n/// executing a shader using that bindgroup.\nfn acceleration_structure_use_after_free(ctx: TestingContext) {\n    // Dummy vertex buffer.\n    let vertices = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: &[0; mem::size_of::<[[f32; 3]; 3]>()],\n        usage: BufferUsages::BLAS_INPUT,\n    });\n\n    // Create a BLAS with a single triangle.\n    let blas_size = BlasTriangleGeometrySizeDescriptor {\n        vertex_format: VertexFormat::Float32x3,\n        vertex_count: 3,\n        index_format: None,\n        index_count: None,\n        flags: AccelerationStructureGeometryFlags::empty(),\n    };\n\n    let blas = ctx.device.create_blas(\n        &CreateBlasDescriptor {\n            label: Some(\"blas use after free\"),\n            flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        },\n        BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_size.clone()],\n        },\n    );\n    // Create the TLAS\n    let mut tlas = ctx.device.create_tlas(&CreateTlasDescriptor {\n        label: Some(\"tlas use after free\"),\n        max_instances: 1,\n        flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n        update_mode: AccelerationStructureUpdateMode::Build,\n    });\n\n    tlas[0] = Some(TlasInstance::new(\n        &blas,\n        [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],\n        0,\n        0xFF,\n    ));\n\n    // Actually build the BLAS.\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    encoder.build_acceleration_structures(\n        iter::once(&BlasBuildEntry {\n            blas: &blas,\n            geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n                size: &blas_size,\n                vertex_buffer: &vertices,\n                first_vertex: 0,\n                vertex_stride: mem::size_of::<[f32; 3]>() as BufferAddress,\n                index_buffer: None,\n                first_index: None,\n                transform_buffer: None,\n                transform_buffer_offset: None,\n            }]),\n        }),\n        iter::empty(),\n    );\n    ctx.queue.submit(Some(encoder.finish()));\n\n    // Drop the blas and ensure that if it was going to die, it is dead.\n    drop(blas);\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n\n    // build the tlas package to ensure the blas is dropped\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    encoder.build_acceleration_structures(iter::empty(), iter::once(&tlas));\n    ctx.queue.submit(Some(encoder.finish()));\n\n    // Create a compute shader that uses an AS.\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n    let compute_pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"basic_usage\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &compute_pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::AccelerationStructure(&tlas),\n        }],\n    });\n\n    // Drop the TLAS package and ensure that if it was going to die, it is dead.\n    drop(tlas);\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n\n    // Run the pass with the bind group that references the TLAS package.\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&compute_pipeline);\n        pass.set_bind_group(0, Some(&bind_group), &[]);\n        pass.dispatch_workgroups(1, 1, 1)\n    }\n    ctx.queue.submit(Some(encoder.finish()));\n}\n\n#[gpu_test]\nstatic ACCELERATION_STRUCTURE_USE_AFTER_FREE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(required_features()),\n    )\n    .run_sync(acceleration_structure_use_after_free);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/limits.rs",
    "content": "use wgpu::wgt::{\n    AccelerationStructureFlags, AccelerationStructureGeometryFlags,\n    AccelerationStructureUpdateMode, BlasGeometrySizeDescriptors,\n};\nuse wgpu::{\n    BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,\n    BlasTriangleGeometrySizeDescriptor, CreateBlasDescriptor, CreateTlasDescriptor, Limits,\n    ShaderStages, VertexFormat,\n};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{fail, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    tests.push(LIMITS_HIT);\n}\n\n#[gpu_test]\nstatic LIMITS_HIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(Limits {\n                max_blas_primitive_count: 3,\n                max_blas_geometry_count: 1,\n                max_tlas_instance_count: 1,\n                max_acceleration_structures_per_shader_stage: 1,\n                ..Limits::default()\n            })\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(hit_limits);\n\nfn hit_limits(ctx: TestingContext) {\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: None,\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![\n                        BlasTriangleGeometrySizeDescriptor {\n                            vertex_format: VertexFormat::Float32x3,\n                            vertex_count: 3,\n                            index_format: None,\n                            index_count: None,\n                            flags: AccelerationStructureGeometryFlags::empty(),\n                        };\n                        2\n                    ],\n                },\n            );\n        },\n        None,\n    );\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_blas(\n                &CreateBlasDescriptor {\n                    label: None,\n                    flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                    update_mode: AccelerationStructureUpdateMode::Build,\n                },\n                BlasGeometrySizeDescriptors::Triangles {\n                    descriptors: vec![BlasTriangleGeometrySizeDescriptor {\n                        vertex_format: VertexFormat::Float32x3,\n                        vertex_count: 6,\n                        index_format: None,\n                        index_count: None,\n                        flags: AccelerationStructureGeometryFlags::empty(),\n                    }],\n                },\n            );\n        },\n        None,\n    );\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx.device.create_tlas(&CreateTlasDescriptor {\n                label: None,\n                max_instances: 2,\n                flags: AccelerationStructureFlags::PREFER_FAST_TRACE,\n                update_mode: AccelerationStructureUpdateMode::Build,\n            });\n        },\n        None,\n    );\n    fail(\n        &ctx.device,\n        || {\n            let _ = ctx\n                .device\n                .create_bind_group_layout(&BindGroupLayoutDescriptor {\n                    label: None,\n                    entries: &[\n                        BindGroupLayoutEntry {\n                            binding: 0,\n                            visibility: ShaderStages::COMPUTE,\n                            ty: BindingType::AccelerationStructure {\n                                vertex_return: false,\n                            },\n                            count: None,\n                        },\n                        BindGroupLayoutEntry {\n                            binding: 1,\n                            visibility: ShaderStages::COMPUTE,\n                            ty: BindingType::AccelerationStructure {\n                                vertex_return: false,\n                            },\n                            count: None,\n                        },\n                    ],\n                });\n        },\n        None,\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/mod.rs",
    "content": "use std::mem;\nuse wgpu::util::BufferInitDescriptor;\nuse wgpu::{\n    util::DeviceExt, Blas, BlasBuildEntry, BlasGeometries, BlasGeometrySizeDescriptors,\n    BlasTriangleGeometry, BlasTriangleGeometrySizeDescriptor, Buffer, CreateBlasDescriptor,\n    CreateTlasDescriptor, Tlas, TlasInstance,\n};\nuse wgpu::{\n    AccelerationStructureFlags, AccelerationStructureGeometryFlags,\n    AccelerationStructureUpdateMode, BufferAddress, BufferUsages, VertexFormat,\n};\nuse wgpu_test::TestingContext;\n\nmod as_build;\nmod as_create;\nmod as_use_after_free;\nmod limits;\nmod scene;\nmod shader;\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    as_build::all_tests(tests);\n    as_create::all_tests(tests);\n    as_use_after_free::all_tests(tests);\n    limits::all_tests(tests);\n    scene::all_tests(tests);\n    shader::all_tests(tests);\n}\n\nfn acceleration_structure_limits() -> wgpu::Limits {\n    wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()\n}\n\npub struct AsBuildContext {\n    vertices: Buffer,\n    blas_size: BlasTriangleGeometrySizeDescriptor,\n    blas: Blas,\n    // Putting this last, forces the BLAS to die before the TLAS.\n    tlas: Tlas,\n}\n\nimpl AsBuildContext {\n    pub fn new(\n        ctx: &TestingContext,\n        additional_blas_flags: AccelerationStructureFlags,\n        additional_tlas_flags: AccelerationStructureFlags,\n    ) -> Self {\n        let vertices = ctx.device.create_buffer_init(&BufferInitDescriptor {\n            label: None,\n            contents: &[0; mem::size_of::<[[f32; 3]; 3]>()],\n            usage: BufferUsages::BLAS_INPUT,\n        });\n\n        let blas_size = BlasTriangleGeometrySizeDescriptor {\n            vertex_format: VertexFormat::Float32x3,\n            vertex_count: 3,\n            index_format: None,\n            index_count: None,\n            flags: AccelerationStructureGeometryFlags::empty(),\n        };\n\n        let blas = ctx.device.create_blas(\n            &CreateBlasDescriptor {\n                label: Some(\"BLAS\"),\n                flags: AccelerationStructureFlags::PREFER_FAST_TRACE | additional_blas_flags,\n                update_mode: AccelerationStructureUpdateMode::Build,\n            },\n            BlasGeometrySizeDescriptors::Triangles {\n                descriptors: vec![blas_size.clone()],\n            },\n        );\n\n        let mut tlas = ctx.device.create_tlas(&CreateTlasDescriptor {\n            label: Some(\"TLAS\"),\n            max_instances: 1,\n            flags: AccelerationStructureFlags::PREFER_FAST_TRACE | additional_tlas_flags,\n            update_mode: AccelerationStructureUpdateMode::Build,\n        });\n\n        tlas[0] = Some(TlasInstance::new(\n            &blas,\n            [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],\n            0,\n            0xFF,\n        ));\n\n        Self {\n            vertices,\n            blas_size,\n            blas,\n            tlas,\n        }\n    }\n\n    pub fn blas_build_entry(&self) -> BlasBuildEntry<'_> {\n        BlasBuildEntry {\n            blas: &self.blas,\n            geometry: BlasGeometries::TriangleGeometries(vec![BlasTriangleGeometry {\n                size: &self.blas_size,\n                vertex_buffer: &self.vertices,\n                first_vertex: 0,\n                vertex_stride: mem::size_of::<[f32; 3]>() as BufferAddress,\n                index_buffer: None,\n                first_index: None,\n                transform_buffer: None,\n                transform_buffer_offset: None,\n            }]),\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/scene/mesh_gen.rs",
    "content": "use bytemuck::{Pod, Zeroable};\nuse glam::Affine3A;\n\n#[repr(C)]\n#[derive(Clone, Copy, Pod, Zeroable)]\npub struct Vertex {\n    _pos: [f32; 4],\n    _tex_coord: [f32; 2],\n}\n\nfn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {\n    Vertex {\n        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],\n        _tex_coord: [tc[0] as f32, tc[1] as f32],\n    }\n}\n\npub fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {\n    let vertex_data = [\n        // top (0, 0, 1)\n        vertex([-1, -1, 1], [0, 0]),\n        vertex([1, -1, 1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([-1, 1, 1], [0, 1]),\n        // bottom (0, 0, -1)\n        vertex([-1, 1, -1], [1, 0]),\n        vertex([1, 1, -1], [0, 0]),\n        vertex([1, -1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // right (1, 0, 0)\n        vertex([1, -1, -1], [0, 0]),\n        vertex([1, 1, -1], [1, 0]),\n        vertex([1, 1, 1], [1, 1]),\n        vertex([1, -1, 1], [0, 1]),\n        // left (-1, 0, 0)\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, 1, 1], [0, 0]),\n        vertex([-1, 1, -1], [0, 1]),\n        vertex([-1, -1, -1], [1, 1]),\n        // front (0, 1, 0)\n        vertex([1, 1, -1], [1, 0]),\n        vertex([-1, 1, -1], [0, 0]),\n        vertex([-1, 1, 1], [0, 1]),\n        vertex([1, 1, 1], [1, 1]),\n        // back (0, -1, 0)\n        vertex([1, -1, 1], [0, 0]),\n        vertex([-1, -1, 1], [1, 0]),\n        vertex([-1, -1, -1], [1, 1]),\n        vertex([1, -1, -1], [0, 1]),\n    ];\n\n    let index_data: &[u16] = &[\n        0, 1, 2, 2, 3, 0, // top\n        4, 5, 6, 6, 7, 4, // bottom\n        8, 9, 10, 10, 11, 8, // right\n        12, 13, 14, 14, 15, 12, // left\n        16, 17, 18, 18, 19, 16, // front\n        20, 21, 22, 22, 23, 20, // back\n    ];\n\n    (vertex_data.to_vec(), index_data.to_vec())\n}\n\npub fn affine_to_rows(mat: &Affine3A) -> [f32; 12] {\n    let row_0 = mat.matrix3.row(0);\n    let row_1 = mat.matrix3.row(1);\n    let row_2 = mat.matrix3.row(2);\n    let translation = mat.translation;\n    [\n        row_0.x,\n        row_0.y,\n        row_0.z,\n        translation.x,\n        row_1.x,\n        row_1.y,\n        row_1.z,\n        translation.y,\n        row_2.x,\n        row_2.y,\n        row_2.z,\n        translation.z,\n    ]\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/scene/mod.rs",
    "content": "use std::{iter, mem};\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\nuse wgpu::util::DeviceExt;\n\nuse crate::ray_tracing::acceleration_structure_limits;\nuse glam::{Affine3A, Quat, Vec3};\n\nmod mesh_gen;\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    tests.extend([\n        ACCELERATION_STRUCTURE_BUILD_NO_INDEX,\n        ACCELERATION_STRUCTURE_BUILD_WITH_INDEX,\n    ]);\n}\n\nfn acceleration_structure_build(ctx: &TestingContext, use_index_buffer: bool) {\n    let max_instances = 1000;\n    let device = &ctx.device;\n\n    let (vertex_data, index_data) = mesh_gen::create_vertices();\n\n    let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Vertex Buffer\"),\n        contents: bytemuck::cast_slice(&vertex_data),\n        usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,\n    });\n\n    let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n        label: Some(\"Index Buffer\"),\n        contents: bytemuck::cast_slice(&index_data),\n        usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,\n    });\n\n    let index_format = wgpu::IndexFormat::Uint16;\n    let index_count = index_data.len() as u32;\n\n    let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {\n        vertex_format: wgpu::VertexFormat::Float32x3,\n        vertex_count: vertex_data.len() as u32,\n        index_format: use_index_buffer.then_some(index_format),\n        index_count: use_index_buffer.then_some(index_count),\n        flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,\n    };\n\n    let blas = device.create_blas(\n        &wgpu::CreateBlasDescriptor {\n            label: None,\n            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n            update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n        },\n        wgpu::BlasGeometrySizeDescriptors::Triangles {\n            descriptors: vec![blas_geo_size_desc.clone()],\n        },\n    );\n\n    let mut tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {\n        label: None,\n        flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,\n        update_mode: wgpu::AccelerationStructureUpdateMode::Build,\n        max_instances,\n    });\n\n    for j in 0..max_instances {\n        tlas[j as usize] = Some(wgpu::TlasInstance::new(\n            &blas,\n            mesh_gen::affine_to_rows(&Affine3A::from_rotation_translation(\n                Quat::from_rotation_y(45.9_f32.to_radians()),\n                Vec3 {\n                    x: j as f32,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            )),\n            0,\n            0xff,\n        ));\n    }\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    encoder.build_acceleration_structures(\n        iter::once(&wgpu::BlasBuildEntry {\n            blas: &blas,\n            geometry: wgpu::BlasGeometries::TriangleGeometries(vec![wgpu::BlasTriangleGeometry {\n                size: &blas_geo_size_desc,\n                vertex_buffer: &vertex_buf,\n                first_vertex: 0,\n                vertex_stride: mem::size_of::<mesh_gen::Vertex>() as u64,\n                index_buffer: use_index_buffer.then_some(&index_buffer),\n                first_index: use_index_buffer.then_some(0),\n                transform_buffer: None,\n                transform_buffer_offset: None,\n            }]),\n        }),\n        iter::once(&tlas),\n    );\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n}\n\n#[gpu_test]\nstatic ACCELERATION_STRUCTURE_BUILD_NO_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        acceleration_structure_build(&ctx, false);\n    });\n\n#[gpu_test]\nstatic ACCELERATION_STRUCTURE_BUILD_WITH_INDEX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        acceleration_structure_build(&ctx, true);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/shader.rs",
    "content": "use crate::ray_tracing::{acceleration_structure_limits, AsBuildContext};\nuse wgpu::util::{BufferInitDescriptor, DeviceExt};\nuse wgpu::{\n    include_wgsl, Backends, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferDescriptor,\n    CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, InstanceFlags,\n};\nuse wgpu::{AccelerationStructureFlags, BufferUsages};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{FailureCase, GpuTestInitializer};\nuse wgpu_test::{GpuTestConfiguration, TestParameters, TestingContext};\n\nconst STRUCT_SIZE: wgpu::BufferAddress = 176;\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.push(ACCESS_ALL_STRUCT_MEMBERS);\n    tests.push(PREVENT_INVALID_RAY_QUERY_CALLS);\n}\n\n#[gpu_test]\nstatic ACCESS_ALL_STRUCT_MEMBERS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY),\n    )\n    .run_sync(access_all_struct_members);\n\nfn access_all_struct_members(ctx: TestingContext) {\n    let buf = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: STRUCT_SIZE,\n        usage: BufferUsages::STORAGE,\n        mapped_at_creation: false,\n    });\n    //\n    // Create a clean `AsBuildContext`\n    //\n\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder_build = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"Build\"),\n        });\n\n    encoder_build.build_acceleration_structures([&as_ctx.blas_build_entry()], [&as_ctx.tlas]);\n\n    ctx.queue.submit([encoder_build.finish()]);\n\n    //\n    // Create shader to use tlas with\n    //\n\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n    let compute_pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"all_of_struct\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &compute_pipeline.get_bind_group_layout(0),\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: BindingResource::Buffer(buf.as_entire_buffer_binding()),\n            },\n        ],\n    });\n\n    //\n    // Submit once to check for no issues\n    //\n\n    let mut encoder_compute = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut pass = encoder_compute.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&compute_pipeline);\n        pass.set_bind_group(0, Some(&bind_group), &[]);\n        pass.dispatch_workgroups(1, 1, 1)\n    }\n\n    ctx.queue.submit([encoder_compute.finish()]);\n}\n\n#[gpu_test]\nstatic PREVENT_INVALID_RAY_QUERY_CALLS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(acceleration_structure_limits())\n            .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY)\n            // Otherwise, mistakes in the generated code won't be caught.\n            .instance_flags(InstanceFlags::GPU_BASED_VALIDATION)\n            // not yet implemented in directx12\n            .skip(FailureCase::backend(Backends::METAL)),\n    )\n    .run_sync(prevent_invalid_ray_query_calls);\n\nfn prevent_invalid_ray_query_calls(ctx: TestingContext) {\n    let invalid_values_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: Some(\"invalid values buffer\"),\n        contents: bytemuck::cast_slice(&[f32::NAN, f32::INFINITY]),\n        usage: BufferUsages::STORAGE,\n    });\n\n    //\n    // Create a clean `AsBuildContext`\n    //\n\n    let as_ctx = AsBuildContext::new(\n        &ctx,\n        AccelerationStructureFlags::empty(),\n        AccelerationStructureFlags::empty(),\n    );\n\n    let mut encoder_build = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor {\n            label: Some(\"Build\"),\n        });\n\n    encoder_build.build_acceleration_structures([&as_ctx.blas_build_entry()], [&as_ctx.tlas]);\n\n    ctx.queue.submit([encoder_build.finish()]);\n\n    //\n    // Create shader\n    //\n\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n    let compute_pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: Some(\"invalid_usages\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n    let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &compute_pipeline.get_bind_group_layout(0),\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::AccelerationStructure(&as_ctx.tlas),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: BindingResource::Buffer(invalid_values_buffer.as_entire_buffer_binding()),\n            },\n        ],\n    });\n\n    //\n    // Submit once to check for no issues\n    //\n\n    let mut encoder_compute = ctx\n        .device\n        .create_command_encoder(&CommandEncoderDescriptor::default());\n    {\n        let mut pass = encoder_compute.begin_compute_pass(&ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        pass.set_pipeline(&compute_pipeline);\n        pass.set_bind_group(0, Some(&bind_group), &[]);\n        pass.dispatch_workgroups(1, 1, 1)\n    }\n\n    ctx.queue.submit([encoder_compute.finish()]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/ray_tracing/shader.wgsl",
    "content": "enable wgpu_ray_query;\n\n@group(0) @binding(0)\nvar acc_struct: acceleration_structure;\n\nstruct Intersection {\n    kind: u32,\n    t: f32,\n    instance_custom_data: u32,\n    instance_index: u32,\n    sbt_record_offset: u32,\n    geometry_index: u32,\n    primitive_index: u32,\n    barycentrics: vec2<f32>,\n    front_face: u32,\n    object_to_world: mat4x3<f32>,\n    world_to_object: mat4x3<f32>,\n}\n\n@group(0) @binding(1)\nvar<storage, read_write> out: Intersection;\n\n@workgroup_size(1)\n@compute\nfn basic_usage() {\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n    rayQueryProceed(&rq);\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n}\n\n@workgroup_size(1)\n@compute\nfn all_of_struct() {\n    var rq: ray_query;\n    rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.0, 0.0, vec3f(0.0, 0.0, 1.0), vec3f(0.0, 0.0, 1.0)));\n    rayQueryProceed(&rq);\n    let intersection = rayQueryGetCommittedIntersection(&rq);\n    // this prevents optimisation as we use the fields\n    out = Intersection(\n        intersection.kind,\n        intersection.t,\n        intersection.instance_custom_data,\n        intersection.instance_index,\n        intersection.sbt_record_offset,\n        intersection.geometry_index,\n        intersection.primitive_index,\n        intersection.barycentrics,\n        u32(intersection.front_face),\n        intersection.world_to_object,\n        intersection.object_to_world,\n    );\n}\n\nstruct MaybeInvalidValues {\n    nan: f32,\n    inf: f32,\n}\n\n@group(0) @binding(1)\nvar<storage> invalid_values: MaybeInvalidValues;\n\n@workgroup_size(1)\n@compute\nfn invalid_usages() {\n    {\n        var rq: ray_query;\n        // no initialize\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n        // no proceed\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        // The acceleration structure has been set up to not generate an intersections, meaning it will be a committed intersection, not candidate.\n        let intersection = rayQueryGetCandidateIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // NaN in origin\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, invalid_values.nan, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // Inf in origin\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, invalid_values.inf, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // NaN in direction\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, invalid_values.nan, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // Inf in direction\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, invalid_values.inf, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // t_min greater than t_max\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 100000.0, 0.1, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        // t_min less than 0\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, -0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        let intersection = rayQueryGetCommittedIntersection(&rq);\n    }\n    {\n        var rq: ray_query;\n        rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.001, 100000.0, vec3f(0.0, 0.0, 0.0), vec3f(0.0, 0.0, 1.0)));\n        rayQueryProceed(&rq);\n        // The acceleration structure has been set up to not generate an intersections, meaning terminate is invalid here.\n        rayQueryTerminate(&rq);\n    }\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_3349.fs.wgsl",
    "content": "struct ShaderData {\n    a: f32,\n    b: f32,\n    c: f32,\n    d: f32,\n}\n\n@group(0) @binding(0)\nvar<uniform> data1: ShaderData;\n\nvar<immediate> data2: ShaderData;\n\nstruct FsIn {\n    @builtin(position) position: vec4f,\n    @location(0) data1: vec4f,\n    @location(1) data2: vec4f,\n}\n\n@fragment\nfn fs_main(fs_in: FsIn) -> @location(0) vec4f {\n    let floored = vec2u(floor(fs_in.position.xy));\n    // We're outputting a 2x2 image, each pixel coming from a different source\n    let serial = floored.x + floored.y * 2u;\n\n    switch serial {\n        // (0, 0) - uniform buffer from the vertex shader\n        case 0u: {\n            return fs_in.data1;\n        }\n        // (1, 0) - immediate data from the vertex shader\n        case 1u: {\n            return fs_in.data2;\n        }\n        // (0, 1) - uniform buffer from the fragment shader\n        case 2u: {\n            return vec4f(data1.a, data1.b, data1.c, data1.d);\n        }\n        // (1, 1) - immediate data from the fragment shader\n        case 3u: {\n            return vec4f(data2.a, data2.b, data2.c, data2.d);\n        }\n        default: {\n            return vec4f(0.0);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_3349.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{\n    gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(MULTI_STAGE_DATA_BINDING);\n}\n\n/// We thought we had an OpenGL bug that, when running without explicit in-shader locations,\n/// we will not properly bind uniform buffers to both the vertex and fragment\n/// shaders. This turned out to not reproduce at all with this test case.\n///\n/// However, it also caught issues with the immediate data implementation,\n/// making sure that it works correctly with different definitions for the immediate data\n/// block in vertex and fragment shaders.\n///\n/// This test needs to be able to run on GLES 3.0\n///\n/// What this test does is render a 2x2 texture. Each pixel corresponds to a different\n/// data source.\n///\n/// top left: Vertex Shader / Uniform Buffer\n/// top right: Vertex Shader / Immediate data\n/// bottom left: Fragment Shader / Uniform Buffer\n/// bottom right: Fragment Shader / Immediate data\n///\n/// We then validate the data is correct from every position.\n#[gpu_test]\nstatic MULTI_STAGE_DATA_BINDING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::IMMEDIATES)\n            .limits(wgpu::Limits {\n                max_immediate_size: 16,\n                ..Default::default()\n            }),\n    )\n    .run_async(multi_stage_data_binding_test);\n\nasync fn multi_stage_data_binding_test(ctx: TestingContext) {\n    // We use different shader modules to allow us to use different\n    // types for the uniform and immediate data blocks between stages.\n    let vs_sm = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"issue_3349.vs.wgsl\"));\n\n    let fs_sm = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"issue_3349.fs.wgsl\"));\n\n    // We start with u8s then convert to float, to make sure we don't have\n    // cross-vendor rounding issues unorm.\n    let input_as_unorm: [u8; 4] = [25_u8, 50, 75, 100];\n    let input = input_as_unorm.map(|v| v as f32 / 255.0);\n\n    let buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"buffer\"),\n            contents: bytemuck::cast_slice(&input),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bgl\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n\n    let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"bg\"),\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }],\n    });\n\n    let pll = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pll\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 16,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pll),\n            vertex: wgpu::VertexState {\n                module: &vs_sm,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &fs_sm,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"texture\"),\n        size: wgpu::Extent3d {\n            width: 2,\n            height: 2,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        // Important: NOT srgb.\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n\n    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"encoder\"),\n        });\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: Some(\"rpass\"),\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                    store: wgpu::StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_bind_group(0, &bg, &[]);\n        rpass.set_immediates(0, bytemuck::cast_slice(&input));\n        rpass.draw(0..3, 0..1);\n    }\n\n    let buffers = ReadbackBuffers::new(&ctx.device, &texture);\n    buffers.copy_from(&ctx.device, &mut encoder, &texture);\n    ctx.queue.submit([encoder.finish()]);\n\n    let result = input_as_unorm.repeat(4);\n    buffers.assert_buffer_contents(&ctx, &result).await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_3349.vs.wgsl",
    "content": "@group(0) @binding(0)\nvar<uniform> data1: vec4f;\n\n// D3DCompile requires this to be a struct\nstruct Pc {\n    inner: vec4f,\n}\n\nvar<immediate> data2: Pc;\n\nstruct VsOut {\n    @builtin(position) position: vec4f,\n    @location(0) data1: vec4f,\n    @location(1) data2: vec4f,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VsOut {\n    let uv = vec2f(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u));\n    let position = vec4f(uv * 2.0 - 1.0, 0.0, 1.0);\n    return VsOut(position, data1, data2.inner);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_3457.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\nuse wgpu::*;\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(PASS_RESET_VERTEX_BUFFER);\n}\n\n/// The core issue here was that we weren't properly disabling vertex attributes on GL\n/// when a renderpass ends. This ended up being rather tricky to test for as GL is remarkably\n/// tolerant of errors. This test, with the fix not-applied, only fails on WebGL.\n///\n/// We need to setup a situation where it's invalid to issue a draw call without the fix.\n/// To do this we first make a renderpass using two vertex buffers and draw on it. Then we\n/// submit, delete the second vertex buffer and `poll(Wait)`. Because we maintained the device,\n/// the actual underlying buffer for the second vertex buffer is deleted, causing a draw call\n/// that is invalid if the second attribute is still enabled.\n///\n/// We use non-consecutive vertex attribute locations (0 and 5) in order to also test\n/// that we unset the correct locations (see PR #3706).\n#[gpu_test]\nstatic PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let module = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"issue_3457.wgsl\"));\n\n        // We use two separate vertex buffers so we can delete one in between submissions\n        let vertex_buffer1 = ctx.device.create_buffer(&BufferDescriptor {\n            label: Some(\"vertex buffer 1\"),\n            size: 3 * 16,\n            usage: BufferUsages::VERTEX,\n            mapped_at_creation: false,\n        });\n\n        let vertex_buffer2 = ctx.device.create_buffer(&BufferDescriptor {\n            label: Some(\"vertex buffer 2\"),\n            size: 3 * 4,\n            usage: BufferUsages::VERTEX,\n            mapped_at_creation: false,\n        });\n\n        let pipeline_layout = ctx\n            .device\n            .create_pipeline_layout(&PipelineLayoutDescriptor {\n                label: Some(\"Pipeline Layout\"),\n                bind_group_layouts: &[],\n                immediate_size: 0,\n            });\n\n        let double_pipeline = ctx\n            .device\n            .create_render_pipeline(&RenderPipelineDescriptor {\n                label: Some(\"Double Pipeline\"),\n                layout: Some(&pipeline_layout),\n                vertex: VertexState {\n                    module: &module,\n                    entry_point: Some(\"double_buffer_vert\"),\n                    compilation_options: Default::default(),\n                    buffers: &[\n                        VertexBufferLayout {\n                            array_stride: 16,\n                            step_mode: VertexStepMode::Vertex,\n                            attributes: &vertex_attr_array![0 => Float32x4],\n                        },\n                        VertexBufferLayout {\n                            array_stride: 4,\n                            step_mode: VertexStepMode::Vertex,\n                            attributes: &vertex_attr_array![5 => Float32],\n                        },\n                    ],\n                },\n                primitive: PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: MultisampleState::default(),\n                fragment: Some(FragmentState {\n                    module: &module,\n                    entry_point: Some(\"double_buffer_frag\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(ColorTargetState {\n                        format: TextureFormat::Rgba8Unorm,\n                        blend: None,\n                        write_mask: ColorWrites::all(),\n                    })],\n                }),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        let single_pipeline = ctx\n            .device\n            .create_render_pipeline(&RenderPipelineDescriptor {\n                label: Some(\"Single Pipeline\"),\n                layout: Some(&pipeline_layout),\n                vertex: VertexState {\n                    module: &module,\n                    entry_point: Some(\"single_buffer_vert\"),\n                    compilation_options: Default::default(),\n                    buffers: &[VertexBufferLayout {\n                        array_stride: 16,\n                        step_mode: VertexStepMode::Vertex,\n                        attributes: &vertex_attr_array![0 => Float32x4],\n                    }],\n                },\n                primitive: PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: MultisampleState::default(),\n                fragment: Some(FragmentState {\n                    module: &module,\n                    entry_point: Some(\"single_buffer_frag\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(ColorTargetState {\n                        format: TextureFormat::Rgba8Unorm,\n                        blend: None,\n                        write_mask: ColorWrites::all(),\n                    })],\n                }),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        let view = ctx\n            .device\n            .create_texture(&TextureDescriptor {\n                label: Some(\"Render texture\"),\n                size: Extent3d {\n                    width: 4,\n                    height: 4,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format: TextureFormat::Rgba8Unorm,\n                usage: TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            })\n            .create_view(&TextureViewDescriptor::default());\n\n        let mut encoder1 = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor::default());\n\n        let mut double_rpass = encoder1.begin_render_pass(&RenderPassDescriptor {\n            label: Some(\"double renderpass\"),\n            color_attachments: &[Some(RenderPassColorAttachment {\n                view: &view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: Operations {\n                    load: LoadOp::Clear(Color::BLACK),\n                    store: StoreOp::Discard,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        double_rpass.set_pipeline(&double_pipeline);\n        double_rpass.set_vertex_buffer(0, vertex_buffer1.slice(..));\n        double_rpass.set_vertex_buffer(1, vertex_buffer2.slice(..));\n        double_rpass.draw(0..3, 0..1);\n\n        drop(double_rpass);\n\n        // Submit the first pass using both buffers\n        ctx.queue.submit(Some(encoder1.finish()));\n        // Drop the second buffer, meaning it's invalid to use draw\n        // unless it's unbound.\n        drop(vertex_buffer2);\n\n        // Make sure the buffers are actually deleted.\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n        let mut encoder2 = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor::default());\n\n        let mut single_rpass = encoder2.begin_render_pass(&RenderPassDescriptor {\n            label: Some(\"single renderpass\"),\n            color_attachments: &[Some(RenderPassColorAttachment {\n                view: &view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: Operations {\n                    load: LoadOp::Clear(Color::BLACK),\n                    store: StoreOp::Discard,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        single_rpass.set_pipeline(&single_pipeline);\n        single_rpass.set_vertex_buffer(0, vertex_buffer1.slice(..));\n        single_rpass.draw(0..3, 0..1);\n\n        drop(single_rpass);\n\n        ctx.queue.submit(Some(encoder2.finish()));\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_3457.wgsl",
    "content": "struct DoubleVertexIn {\n    @location(0) position: vec4<f32>,\n    @location(5) value: f32,\n}\n\nstruct DoubleVertexOut {\n    @builtin(position) position: vec4<f32>,\n    @location(0) value: f32,\n}\n\n@vertex\nfn double_buffer_vert(v_in: DoubleVertexIn) -> DoubleVertexOut {\n    return DoubleVertexOut(v_in.position, v_in.value);\n}\n\n@fragment\nfn double_buffer_frag(v_out: DoubleVertexOut) -> @location(0) vec4<f32> {\n    return vec4<f32>(v_out.value);\n}\n\nstruct SingleVertexIn {\n    @location(0) position: vec4<f32>,\n}\n\n@vertex\nfn single_buffer_vert(v_in: SingleVertexIn) -> @builtin(position) vec4<f32> {\n    return v_in.position;\n}\n\n@fragment\nfn single_buffer_frag() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4024.rs",
    "content": "use std::sync::Arc;\n\nuse parking_lot::Mutex;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\nuse wgpu::*;\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(QUEUE_SUBMITTED_CALLBACK_ORDERING);\n}\n\n/// The WebGPU specification has very specific requirements about the ordering of map_async\n/// and on_submitted_work_done callbacks. Specifically, all map_async callbacks that are initiated\n/// before a given on_submitted_work_done callback must be invoked before the on_submitted_work_done\n/// callback is invoked.\n///\n/// We previously immediately invoked on_submitted_work_done callbacks if there was no active submission\n/// to add them to. This is incorrect, as we do not immediately invoke map_async callbacks.\n#[gpu_test]\nstatic QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        // Create a mappable buffer\n        let buffer = ctx.device.create_buffer(&BufferDescriptor {\n            label: Some(\"mappable buffer\"),\n            size: 4,\n            usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        // Encode some work using it. The specifics of this work don't matter, just\n        // that the buffer is used.\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor {\n                label: Some(\"encoder\"),\n            });\n\n        encoder.clear_buffer(&buffer, 0, None);\n\n        // Submit the work.\n        ctx.queue.submit(Some(encoder.finish()));\n        // Ensure the work is finished.\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n        #[derive(Debug)]\n        struct OrderingContext {\n            /// Incremented every time a callback in invoked.\n            /// This allows the callbacks to know their ordering.\n            counter: u8,\n            /// The value of the counter when the map_async callback was invoked.\n            value_read_map_async: Option<u8>,\n            /// The value of the counter when the queue submitted work done callback was invoked.\n            value_read_queue_submitted: Option<u8>,\n        }\n\n        // Create shared ownership of the ordering context, and clone 2 copies.\n        let ordering = Arc::new(Mutex::new(OrderingContext {\n            counter: 0,\n            value_read_map_async: None,\n            value_read_queue_submitted: None,\n        }));\n        let ordering_clone_map_async = Arc::clone(&ordering);\n        let ordering_clone_queue_submitted = Arc::clone(&ordering);\n\n        // Register the callbacks.\n        buffer.slice(..).map_async(MapMode::Read, move |_| {\n            let mut guard = ordering_clone_map_async.lock();\n            guard.value_read_map_async = Some(guard.counter);\n            guard.counter += 1;\n        });\n\n        // If the bug is present, this callback will be invoked immediately inside this function,\n        // despite the fact there is an outstanding map_async callback.\n        ctx.queue.on_submitted_work_done(move || {\n            let mut guard = ordering_clone_queue_submitted.lock();\n            guard.value_read_queue_submitted = Some(guard.counter);\n            guard.counter += 1;\n        });\n\n        // No GPU work is happening at this point, but we want to process callbacks.\n        ctx.async_poll(PollType::Poll).await.unwrap();\n\n        // Extract the ordering out of the arc.\n        let ordering = Arc::into_inner(ordering).unwrap().into_inner();\n\n        // There were two callbacks invoked\n        assert_eq!(ordering.counter, 2);\n        // The map async callback was invoked fist\n        assert_eq!(ordering.value_read_map_async, Some(0));\n        // The queue submitted work done callback was invoked second.\n        assert_eq!(ordering.value_read_queue_submitted, Some(1));\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4122.rs",
    "content": "use std::ops::Range;\n\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(CLEAR_BUFFER_RANGE_RESPECTED);\n}\n\nasync fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {\n    let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"gpu_buffer\"),\n        size,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"cpu_buffer\"),\n        size,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    // Initialize the whole buffer with values.\n    let buffer_contents = vec![0xFF_u8; size as usize];\n    ctx.queue.write_buffer(&gpu_buffer, 0, &buffer_contents);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"encoder\"),\n        });\n\n    encoder.clear_buffer(&gpu_buffer, range.start, Some(range.end - range.start));\n    encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, size);\n\n    ctx.queue.submit(Some(encoder.finish()));\n    cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let buffer_slice = cpu_buffer.slice(..);\n    let buffer_data = buffer_slice.get_mapped_range();\n\n    let first_clear_byte = buffer_data\n        .iter()\n        .enumerate()\n        .find_map(|(index, byte)| (*byte == 0x00).then_some(index))\n        .expect(\"No clear happened at all\");\n\n    let first_dirty_byte = buffer_data\n        .iter()\n        .enumerate()\n        .skip(first_clear_byte)\n        .find_map(|(index, byte)| (*byte != 0x00).then_some(index))\n        .unwrap_or(size as usize);\n\n    let second_clear_byte = buffer_data\n        .iter()\n        .enumerate()\n        .skip(first_dirty_byte)\n        .find_map(|(index, byte)| (*byte == 0x00).then_some(index));\n\n    if second_clear_byte.is_some() {\n        eprintln!(\"Found multiple cleared ranges instead of a single clear range of {}..{} on a buffer of size {}.\", range.start, range.end, size);\n        return false;\n    }\n\n    let cleared_range = first_clear_byte as u64..first_dirty_byte as u64;\n\n    if cleared_range != range {\n        eprintln!(\n            \"Cleared range is {}..{}, but the clear range is {}..{} on a buffer of size {}.\",\n            cleared_range.start, cleared_range.end, range.start, range.end, size\n        );\n        return false;\n    }\n\n    eprintln!(\n        \"Cleared range is {}..{} on a buffer of size {}.\",\n        cleared_range.start, cleared_range.end, size\n    );\n\n    true\n}\n\n/// Nvidia has a bug in vkCmdFillBuffer where the clear range is not properly respected under\n/// certain conditions. See https://github.com/gfx-rs/wgpu/issues/4122 for more information.\n///\n/// This test will fail on nvidia if the bug is not properly worked around.\n#[gpu_test]\nstatic CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        // This hits most of the cases in nvidia's clear buffer bug\n        let mut succeeded = true;\n        for power in 4..14 {\n            let size = 1 << power;\n            for start_offset in (0..=36).step_by(4) {\n                for size_offset in (0..=36).step_by(4) {\n                    let range = start_offset..size + size_offset + start_offset;\n                    let result = fill_test(&ctx, range, 1 << 16).await;\n\n                    succeeded &= result;\n                }\n            }\n        }\n        assert!(succeeded);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4485.rs",
    "content": "use wgpu_test::{\n    gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(CONTINUE_SWITCH);\n}\n\n/// FXC doesn't accept `continue` inside a switch. Instead we store a flag for whether\n/// the loop should continue that is checked after the switch.\n///\n/// See <https://github.com/gfx-rs/wgpu/issues/4485>.\n///\n/// The shader will fail to compile on Dx12 with FXC without this fix.\n///\n/// This also tests that shaders generated with this fix execute correctly.\n#[gpu_test]\nstatic CONTINUE_SWITCH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().force_fxc(true).enable_noop())\n    .run_async(|ctx| async move { test_impl(&ctx).await });\n\nasync fn test_impl(ctx: &TestingContext) {\n    const TEXTURE_HEIGHT: u32 = 2;\n    const TEXTURE_WIDTH: u32 = 2;\n    const BUFFER_SIZE: usize = (TEXTURE_WIDTH * TEXTURE_HEIGHT * 4) as usize;\n\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Offscreen texture\"),\n        size: wgpu::Extent3d {\n            width: TEXTURE_WIDTH,\n            height: TEXTURE_HEIGHT,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"issue_4514.wgsl\"));\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"Pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let readback_buffer = image::ReadbackBuffers::new(&ctx.device, &texture);\n    {\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"Renderpass\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &texture_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        // Important: this isn't the color expected below\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.0,\n                            g: 0.0,\n                            b: 0.0,\n                            a: 0.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            render_pass.set_pipeline(&pipeline);\n            render_pass.draw(0..3, 0..1);\n        }\n        readback_buffer.copy_from(&ctx.device, &mut encoder, &texture);\n        ctx.queue.submit(Some(encoder.finish()));\n    }\n\n    let expected_data = [255; BUFFER_SIZE];\n    readback_buffer\n        .assert_buffer_contents(ctx, &expected_data)\n        .await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4485.wgsl",
    "content": "// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    .\n// |              * .\n// |***************\n// |            . 1,-1\n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) ->  @builtin(position) vec4<f32> {\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    return vec4<f32>(\n        f32(x) * 4.0 - 1.0,\n        1.0 - f32(y) * 4.0,\n        0.0, 1.0\n    );\n}\n\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    var x = 0.0;\n    loop {\n        if x != 0.0 { break; }\n        x = 0.5;\n        // Compiled to a do-while in hlsl and glsl,\n        // we want to confirm that continue applies to outer loop.\n        switch 0 {\n            default {\n                x = 1.0;\n                continue;\n            }\n        }\n        x = 0.0;\n    }\n    // expect X == 1.0\n\n    var y = 0.0;\n    loop {\n        if y != 0.0 { break; }\n        y = 0.5;\n        switch 1 {\n            case 0 {\n                continue;\n            }\n            case 1 {}\n        }\n        // test that loop doesn't continue after the switch when the continue case wasn't executed\n        y = 1.0;\n        break;\n    }\n    // expect y == 1.0\n\n    var z = 0.0;\n    loop {\n        if z != 0.0 { break; }\n        switch 0 {\n            case 0 {\n                z = 0.5;\n            }\n            case 1 {\n                z = 0.5;\n            }\n        }\n        // test that loop doesn't continue after the switch that contains no continue statements\n        z = 1.0\n    }\n    // expect z == 1.0\n\n    var w = 0.0;\n    loop {\n        if w != 0.0 { break; }\n        switch 0 {\n            case 0 {\n                loop {\n                    // continue in loop->switch->loop->switch->switch should affect inner loop\n                    switch 1 {\n                        case 0 {}\n                        case 1 {\n                            switch 0 {\n                                default { continue; }\n                            }\n                        }\n                    }\n                    w = 0.5\n                }\n            }\n            case 1 {\n                w = 0.5;\n            }\n        }\n        if w == 0.0 { w = 1.0; }\n    }\n    // expect w == 1.0\n\n    return vec4<f32>(x, y, z, w);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4514.rs",
    "content": "use wgpu_test::{\n    gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(DEGENERATE_SWITCH);\n}\n\n/// FXC and potentially some glsl consumers have a bug when handling switch statements on a constant\n/// with just a default case. (not sure if the constant part is relevant)\n/// See <https://github.com/gfx-rs/wgpu/issues/4514>.\n///\n/// This test will fail on Dx12 with FXC if this issue is not worked around.\n///\n/// So far no specific buggy glsl consumers have been identified and it isn't known whether the\n/// bug is avoided there.\n#[gpu_test]\nstatic DEGENERATE_SWITCH: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().force_fxc(true).enable_noop())\n    .run_async(|ctx| async move { test_impl(&ctx).await });\n\nasync fn test_impl(ctx: &TestingContext) {\n    const TEXTURE_HEIGHT: u32 = 2;\n    const TEXTURE_WIDTH: u32 = 2;\n    const BUFFER_SIZE: usize = (TEXTURE_WIDTH * TEXTURE_HEIGHT * 4) as usize;\n\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Offscreen texture\"),\n        size: wgpu::Extent3d {\n            width: TEXTURE_WIDTH,\n            height: TEXTURE_HEIGHT,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"issue_4514.wgsl\"));\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"Pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let readback_buffer = image::ReadbackBuffers::new(&ctx.device, &texture);\n    {\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"Renderpass\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &texture_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        // Important: this isn't the color expected below\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.0,\n                            g: 0.0,\n                            b: 0.0,\n                            a: 0.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            render_pass.set_pipeline(&pipeline);\n            render_pass.draw(0..3, 0..1);\n        }\n        readback_buffer.copy_from(&ctx.device, &mut encoder, &texture);\n        ctx.queue.submit(Some(encoder.finish()));\n    }\n\n    let expected_data = [255; BUFFER_SIZE];\n    readback_buffer\n        .assert_buffer_contents(ctx, &expected_data)\n        .await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_4514.wgsl",
    "content": "// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    .\n// |              * .\n// |***************\n// |            . 1,-1\n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) ->  @builtin(position) vec4<f32> {\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    return vec4<f32>(\n        f32(x) * 4.0 - 1.0,\n        1.0 - f32(y) * 4.0,\n        0.0, 1.0\n    );\n}\n\n\n@fragment\nfn fs_main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {\n    var x = 0.0;\n    // Succeeds on FXC without workaround.\n    switch i32(coord_in.x) {\n        default {\n            x = 1.0;\n        }\n    }\n    var y = 0.0;\n    // Fails on FXC without workaround.\n    // (even if we adjust switch above to give different x values based on the input coord)\n    switch i32(x * 30.0) {\n        default {\n            y = 1.0;\n        }\n    }\n    var z = 0.0;\n    // Multiple cases with a single body also fails on FXC without a workaround.\n    switch 0 {\n        case 0, 2, default {\n            z = 1.0;\n        }\n    }\n\n    var w = 0.0;\n    // Succeeds on FXC without workaround.\n    switch 0 {\n        case 0 {\n            w = 1.0;\n        }\n        default {\n            w = 1.0;\n        }\n    }\n\n    return vec4<f32>(x, y, z, w);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_5553.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\nuse wgpu::*;\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(ALLOW_INPUT_NOT_CONSUMED);\n}\n\n/// Previously, for every user-defined vertex output a fragment shader had to have a corresponding\n/// user-defined input. This would generate `StageError::InputNotConsumed`.\n///\n/// This requirement was removed from the WebGPU spec. Now, when generating hlsl, wgpu will\n/// automatically remove any user-defined outputs from the vertex shader that are not present in\n/// the fragment inputs. This is necessary for generating correct hlsl:\n/// https://github.com/gfx-rs/wgpu/issues/5553\n#[gpu_test]\nstatic ALLOW_INPUT_NOT_CONSUMED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let module = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"issue_5553.wgsl\"));\n\n        let pipeline_layout = ctx\n            .device\n            .create_pipeline_layout(&PipelineLayoutDescriptor {\n                label: Some(\"Pipeline Layout\"),\n                bind_group_layouts: &[],\n                immediate_size: 0,\n            });\n\n        let _ = ctx\n            .device\n            .create_render_pipeline(&RenderPipelineDescriptor {\n                label: Some(\"Pipeline\"),\n                layout: Some(&pipeline_layout),\n                vertex: VertexState {\n                    module: &module,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                primitive: PrimitiveState::default(),\n                depth_stencil: None,\n                multisample: MultisampleState::default(),\n                fragment: Some(FragmentState {\n                    module: &module,\n                    entry_point: Some(\"fs_main\"),\n                    compilation_options: Default::default(),\n                    targets: &[Some(ColorTargetState {\n                        format: TextureFormat::Rgba8Unorm,\n                        blend: None,\n                        write_mask: ColorWrites::all(),\n                    })],\n                }),\n                multiview_mask: None,\n                cache: None,\n            });\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_5553.wgsl",
    "content": "struct VertexOut {\n    @builtin(position) position: vec4<f32>,\n    @location(0) unused_value: f32,\n    @location(1) value: f32,\n}\n\nstruct FragmentIn {\n    @builtin(position) position: vec4<f32>,\n    // @location(0) unused_value: f32,\n    @location(1) value: f32,\n}\n\n@vertex\nfn vs_main() -> VertexOut {\n    return VertexOut(vec4(1.0), 1.0, 1.0);\n}\n\n@fragment\nfn fs_main(v_out: FragmentIn) -> @location(0) vec4<f32> {\n    return vec4<f32>(v_out.value);\n}\n\n\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_6317.rs",
    "content": "use wgpu::{DownlevelFlags, Limits};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{fail, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(NON_FATAL_ERRORS_IN_QUEUE_SUBMIT);\n}\n\n#[gpu_test]\nstatic NON_FATAL_ERRORS_IN_QUEUE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults())\n            .enable_noop(),\n    )\n    .run_sync(|ctx| {\n        let shader_with_trivial_bind_group = concat!(\n            \"@group(0) @binding(0) var<storage, read_write> stuff: u32;\\n\",\n            \"\\n\",\n            \"@compute @workgroup_size(1) fn main() { stuff = 2u; }\\n\"\n        );\n\n        let module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(shader_with_trivial_bind_group.into()),\n            });\n\n        let compute_pipeline =\n            ctx.device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: None,\n                    layout: None,\n                    module: &module,\n                    entry_point: None,\n                    compilation_options: Default::default(),\n                    cache: Default::default(),\n                });\n\n        fail(\n            &ctx.device,\n            || {\n                let mut command_encoder = ctx.device.create_command_encoder(&Default::default());\n                {\n                    let mut render_pass = command_encoder.begin_compute_pass(&Default::default());\n                    render_pass.set_pipeline(&compute_pipeline);\n\n                    // NOTE: We deliberately don't set a bind group here, to provoke a validation\n                    // error.\n\n                    render_pass.dispatch_workgroups(1, 1, 1);\n                }\n\n                let _idx = ctx.queue.submit([command_encoder.finish()]);\n            },\n            Some(concat!(\n                \"The current set ComputePipeline with '' label \",\n                \"expects a BindGroup to be set at index 0\"\n            )),\n        )\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_6467.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(ZERO_WORKGROUP_COUNT);\n}\n\n/// Running a compute shader with a total workgroup count of zero implies that no work\n/// should be done, and is a user error. Vulkan and DX12 accept this invalid input with grace, but\n/// Metal and some OpenGL drivers do not guard against this and eventually the machine will crash.\n/// Since this is a public  API that may be given untrusted values in a browser, this must be protected again.\n///\n/// The following test should successfully do nothing on all platforms.\n#[gpu_test]\nstatic ZERO_WORKGROUP_COUNT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits::default())\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        let module = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                    \"\n                @group(0)\n                @binding(0)\n                var<storage, read_write> vals: array<i32>;\n\n                @compute\n                @workgroup_size(1)\n                fn main(@builtin(global_invocation_id) id: vec3u) {\n                    vals[id.x] = vals[id.x] * i32(id.x);\n                }\n            \",\n                )),\n            });\n        let compute_pipeline =\n            ctx.device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: None,\n                    layout: None,\n                    module: &module,\n                    entry_point: Some(\"main\"),\n                    compilation_options: wgpu::PipelineCompilationOptions::default(),\n                    cache: None,\n                });\n        let buffer = DeviceExt::create_buffer_init(\n            &ctx.device,\n            &wgpu::util::BufferInitDescriptor {\n                label: None,\n                contents: &[1, 1, 1, 1, 1, 1, 1, 1],\n                usage: wgpu::BufferUsages::STORAGE\n                    | wgpu::BufferUsages::COPY_DST\n                    | wgpu::BufferUsages::COPY_SRC,\n            },\n        );\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&compute_pipeline);\n            let bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n            let bind_group_entries = [wgpu::BindGroupEntry {\n                binding: 0,\n                resource: buffer.as_entire_binding(),\n            }];\n            let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: None,\n                layout: &bind_group_layout,\n                entries: &bind_group_entries,\n            });\n            cpass.set_bind_group(0, &bind_group, &[]);\n            cpass.dispatch_workgroups(1, 0, 1);\n        }\n        ctx.queue.submit(Some(encoder.finish()));\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/regression/issue_6827.rs",
    "content": "use std::sync::Arc;\n\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    tests.extend([TEST_SINGLE_WRITE, TEST_SCATTER]);\n}\n\n#[gpu_test]\nstatic TEST_SINGLE_WRITE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| async move { run_test(ctx, false).await });\n\n#[gpu_test]\nstatic TEST_SCATTER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            // See https://github.com/gfx-rs/wgpu/issues/6827\n            .expect_fail(FailureCase::backend_adapter(\n                wgpu::Backends::METAL,\n                \"Apple M\", // M1,M2 etc\n            ))\n            .expect_fail(FailureCase::backend_adapter(\n                wgpu::Backends::METAL,\n                \"Apple Paravirtual device\", // CI on M1\n            )),\n    )\n    .run_async(|ctx| async move { run_test(ctx, true).await });\n\nasync fn run_test(ctx: TestingContext, use_many_writes: bool) {\n    let device = ctx.device;\n    let queue = ctx.queue;\n\n    let size = wgpu::Extent3d {\n        width: 4,\n        height: 4,\n        depth_or_array_layers: 4,\n    };\n    let texture = {\n        device.create_texture(&wgpu::TextureDescriptor {\n            size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D3,\n            format: wgpu::TextureFormat::Rgba8Uint,\n            view_formats: &[],\n            usage: wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::COPY_SRC,\n            label: None,\n        })\n    };\n\n    if use_many_writes {\n        many_writes(&texture, &device, &queue);\n    } else {\n        single_write(&texture, &queue);\n    }\n\n    let light_texels: Vec<[u8; 4]> = {\n        let tc = TextureCopyParameters::from_texture(&texture);\n        let temp_buffer = tc.copy_texture_to_new_buffer(&device, &queue, &texture);\n\n        let result_cell =\n            Arc::new(std::sync::OnceLock::<Result<(), wgpu::BufferAsyncError>>::new());\n        temp_buffer.slice(..).map_async(wgpu::MapMode::Read, {\n            let result_cell = result_cell.clone();\n            move |result| result_cell.set(result).unwrap()\n        });\n        device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n        result_cell\n            .get()\n            .as_ref()\n            .expect(\"cell not set\")\n            .as_ref()\n            .expect(\"mapping failed\");\n\n        tc.copy_mapped_to_vec(1, &temp_buffer)\n    };\n\n    let mut wrong_texels = Vec::new();\n    for (zyx_index, cube) in texel_iter(&texture).enumerate() {\n        #[allow(clippy::cast_possible_wrap)]\n        let expected = texel_for_cube(cube);\n        let actual = light_texels[zyx_index];\n        if expected != actual {\n            println!(\"{:?}\", (cube, expected, actual));\n            wrong_texels.push((cube, expected, actual));\n        }\n    }\n\n    let volume = size.width * size.height * size.depth_or_array_layers;\n    assert!(\n        wrong_texels.is_empty(),\n        \"out of {volume}, {len} were wrong\",\n        len = wrong_texels.len(),\n    );\n}\n\n// -------------------------------------------------------------------------------------------------\n\ntype Texel = [u8; COMPONENTS];\n\npub fn texel_for_cube(point: [u32; 3]) -> [u8; 4] {\n    [\n        10 + point[0] as u8,\n        10 + point[1] as u8,\n        10 + point[2] as u8,\n        10,\n    ]\n}\n\nconst COMPONENTS: usize = 4;\n\nfn texel_iter(texture: &wgpu::Texture) -> impl Iterator<Item = [u32; 3]> {\n    itertools::iproduct!(\n        0..texture.depth_or_array_layers(),\n        0..texture.height(),\n        0..texture.width()\n    )\n    .map(|(z, y, x)| [x, y, z])\n}\n\nfn compute_data(texture: &wgpu::Texture) -> Vec<Texel> {\n    let mut data = Vec::new();\n    for point in texel_iter(texture) {\n        data.push(texel_for_cube(point));\n    }\n    data\n}\n\npub fn single_write(texture: &wgpu::Texture, queue: &wgpu::Queue) {\n    let data = compute_data(texture);\n\n    queue.write_texture(\n        wgpu::TexelCopyTextureInfo {\n            texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        data.as_flattened(),\n        wgpu::TexelCopyBufferLayout {\n            offset: 0,\n            bytes_per_row: Some(texture.width() * COMPONENTS as u32),\n            rows_per_image: Some(texture.height()),\n        },\n        texture.size(),\n    )\n}\n\npub fn many_writes(texture: &wgpu::Texture, device: &wgpu::Device, queue: &wgpu::Queue) {\n    let data: Vec<Texel> = compute_data(texture);\n\n    let copy_buffer_2 = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: u64::try_from(data.len() * COMPONENTS).unwrap(),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    for (index, cube) in texel_iter(texture).enumerate() {\n        encoder.copy_buffer_to_texture(\n            wgpu::TexelCopyBufferInfo {\n                buffer: &copy_buffer_2,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: (index * COMPONENTS) as u64,\n                    bytes_per_row: None,\n                    rows_per_image: None,\n                },\n            },\n            wgpu::TexelCopyTextureInfo {\n                texture,\n                mip_level: 0,\n                origin: wgpu::Origin3d {\n                    x: cube[0],\n                    y: cube[1],\n                    z: cube[2],\n                },\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n        );\n    }\n\n    queue.write_buffer(&copy_buffer_2, 0, data.as_flattened());\n    queue.submit([encoder.finish()]);\n}\n\n// -------------------------------------------------------------------------------------------------\n\n/// Elements of GPU-to-CPU copying\n#[derive(Clone, Copy, Debug)]\nstruct TextureCopyParameters {\n    pub size: wgpu::Extent3d,\n    pub byte_size_of_texel: u32,\n}\nimpl TextureCopyParameters {\n    pub fn from_texture(texture: &wgpu::Texture) -> Self {\n        let format = texture.format();\n        assert_eq!(\n            format.block_dimensions(),\n            (1, 1),\n            \"compressed texture format {format:?} not supported\",\n        );\n\n        Self {\n            size: texture.size(),\n            byte_size_of_texel: format\n                .block_copy_size(None)\n                .expect(\"non-color texture format {format:} not supported\"),\n        }\n    }\n\n    pub fn dense_bytes_per_row(&self) -> u32 {\n        self.size.width * self.byte_size_of_texel\n    }\n\n    pub fn padded_bytes_per_row(&self) -> u32 {\n        self.dense_bytes_per_row()\n            .div_ceil(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)\n            * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT\n    }\n\n    #[track_caller]\n    pub fn copy_texture_to_new_buffer(\n        &self,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        texture: &wgpu::Texture,\n    ) -> wgpu::Buffer {\n        let padded_bytes_per_row = self.padded_bytes_per_row();\n\n        let temp_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"GPU-to-CPU image copy buffer\"),\n            size: u64::from(padded_bytes_per_row)\n                * u64::from(self.size.height)\n                * u64::from(self.size.depth_or_array_layers),\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        {\n            let mut encoder =\n                device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n            encoder.copy_texture_to_buffer(\n                texture.as_image_copy(),\n                wgpu::TexelCopyBufferInfo {\n                    buffer: &temp_buffer,\n                    layout: wgpu::TexelCopyBufferLayout {\n                        offset: 0,\n                        bytes_per_row: Some(padded_bytes_per_row),\n                        rows_per_image: Some(self.size.height),\n                    },\n                },\n                texture.size(),\n            );\n            queue.submit(Some(encoder.finish()));\n        }\n\n        temp_buffer\n    }\n\n    /// Given a mapped buffer, make a [`Vec<C>`] of it.\n    ///\n    /// `size_of::<C>() * components` must be equal to the byte size of a texel.\n    pub fn copy_mapped_to_vec<C>(&self, components: usize, buffer: &wgpu::Buffer) -> Vec<C>\n    where\n        C: bytemuck::AnyBitPattern,\n    {\n        assert_eq!(\n            u32::try_from(components * size_of::<C>()).ok(),\n            Some(self.byte_size_of_texel),\n            \"Texture format does not match requested format\",\n        );\n\n        // Copy the mapped buffer data into a Rust vector, removing row padding if present\n        // by copying it one row at a time.\n        let mut texel_vector: Vec<C> = Vec::new();\n        {\n            let mapped: &[u8] = &buffer.slice(..).get_mapped_range();\n            for row in 0..self.row_count() {\n                let byte_start_of_row = (self.padded_bytes_per_row()) as usize * row;\n                texel_vector.extend(bytemuck::cast_slice::<u8, C>(\n                    &mapped[byte_start_of_row..][..self.dense_bytes_per_row() as usize],\n                ));\n            }\n        }\n\n        texel_vector\n    }\n\n    fn row_count(&self) -> usize {\n        self.size.height as usize * self.size.depth_or_array_layers as usize\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/render_pass_ownership.rs",
    "content": "//! Tests that render passes take ownership of resources that are associated with.\n//! I.e. once a resource is passed in to a render pass, it can be dropped.\n//!\n//! TODO: Methods that take resources that weren't tested here:\n//! * rpass.draw_indexed_indirect(indirect_buffer, indirect_offset)\n//! * rpass.execute_bundles(render_bundles)\n//! * rpass.multi_draw_indirect(indirect_buffer, indirect_offset, count)\n//! * rpass.multi_draw_indexed_indirect(indirect_buffer, indirect_offset, count)\n//! * rpass.multi_draw_indirect_count\n//! * rpass.multi_draw_indexed_indirect_count\n//!\nuse std::num::NonZeroU64;\n\nuse wgpu::util::DeviceExt as _;\nuse wgpu_test::{\n    gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        RENDER_PASS_RESOURCE_OWNERSHIP,\n        RENDER_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS,\n        RENDER_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS,\n        RENDER_PASS_KEEP_ENCODER_ALIVE,\n    ]);\n}\n\n// Minimal shader with buffer based side effect - only needed to check whether the render pass has executed at all.\nconst SHADER_SRC: &str = \"\n@group(0) @binding(0)\nvar<storage, read_write> buffer: array<vec4f>;\n\nvar<private> positions: array<vec2f, 3> = array<vec2f, 3>(\n    vec2f(-1.0, -3.0),\n    vec2f(-1.0, 1.0),\n    vec2f(3.0, 1.0)\n);\n\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {\n    return vec4f(positions[vertex_index], 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    buffer[0] *= 2.0;\n    return vec4<f32>(1.0, 0.0, 1.0, 1.0);\n}\";\n\n#[gpu_test]\nstatic RENDER_PASS_RESOURCE_OWNERSHIP: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().test_features_limits())\n    .run_async(render_pass_resource_ownership);\n\nasync fn render_pass_resource_ownership(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        indirect_buffer,\n        vertex_buffer,\n        index_buffer,\n        bind_group,\n        pipeline,\n        color_attachment_view,\n        color_attachment_resolve_view,\n        depth_stencil_view,\n        occlusion_query_set,\n    } = resource_setup(&ctx);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: Some(\"render_pass\"),\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &color_attachment_view,\n                depth_slice: None,\n                resolve_target: Some(&color_attachment_resolve_view),\n                ops: wgpu::Operations::default(),\n            })],\n            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                view: &depth_stencil_view,\n                depth_ops: Some(wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(1.0),\n                    store: wgpu::StoreOp::Store,\n                }),\n                stencil_ops: None,\n            }),\n            timestamp_writes: None,\n            occlusion_query_set: Some(&occlusion_query_set),\n            multiview_mask: None,\n        });\n\n        // Drop render pass attachments right away.\n        drop(color_attachment_view);\n        drop(color_attachment_resolve_view);\n        drop(depth_stencil_view);\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_bind_group(0, &bind_group, &[]);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.begin_occlusion_query(0);\n        rpass.draw_indirect(&indirect_buffer, 0);\n        rpass.end_occlusion_query();\n\n        // Now drop all resources we set. Then do a device poll to make sure the resources are really not dropped too early, no matter what.\n        drop(pipeline);\n        drop(bind_group);\n        drop(indirect_buffer);\n        drop(vertex_buffer);\n        drop(index_buffer);\n        drop(occlusion_query_set);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic RENDER_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .test_features_limits()\n                .features(wgpu::Features::PIPELINE_STATISTICS_QUERY),\n        )\n        .run_async(render_pass_query_set_ownership_pipeline_statistics);\n\nasync fn render_pass_query_set_ownership_pipeline_statistics(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        vertex_buffer,\n        index_buffer,\n        bind_group,\n        pipeline,\n        color_attachment_view,\n        depth_stencil_view,\n        ..\n    } = resource_setup(&ctx);\n\n    let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set\"),\n        ty: wgpu::QueryType::PipelineStatistics(\n            wgpu::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS,\n        ),\n        count: 1,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &color_attachment_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations::default(),\n            })],\n            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                view: &depth_stencil_view,\n                depth_ops: Some(wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(1.0),\n                    store: wgpu::StoreOp::Store,\n                }),\n                stencil_ops: None,\n            }),\n            ..Default::default()\n        });\n        rpass.set_pipeline(&pipeline);\n        rpass.set_bind_group(0, &bind_group, &[]);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.begin_pipeline_statistics_query(&query_set, 0);\n        rpass.draw(0..3, 0..1);\n        rpass.end_pipeline_statistics_query();\n\n        // Drop the query set. Then do a device poll to make sure it's not dropped too early, no matter what.\n        drop(query_set);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic RENDER_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(TestParameters::default().test_features_limits().features(\n            wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,\n        ))\n        .run_async(render_pass_query_set_ownership_timestamps);\n\nasync fn render_pass_query_set_ownership_timestamps(ctx: TestingContext) {\n    let ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n        color_attachment_view,\n        depth_stencil_view,\n        pipeline,\n        bind_group,\n        vertex_buffer,\n        index_buffer,\n        ..\n    } = resource_setup(&ctx);\n\n    let query_set_timestamp_writes = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set_timestamp_writes\"),\n        ty: wgpu::QueryType::Timestamp,\n        count: 2,\n    });\n    let query_set_write_timestamp = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"query_set_write_timestamp\"),\n        ty: wgpu::QueryType::Timestamp,\n        count: 1,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &color_attachment_view,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgpu::Operations::default(),\n            })],\n            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n                view: &depth_stencil_view,\n                depth_ops: Some(wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(1.0),\n                    store: wgpu::StoreOp::Store,\n                }),\n                stencil_ops: None,\n            }),\n            timestamp_writes: Some(wgpu::RenderPassTimestampWrites {\n                query_set: &query_set_timestamp_writes,\n                beginning_of_pass_write_index: Some(0),\n                end_of_pass_write_index: Some(1),\n            }),\n            ..Default::default()\n        });\n        rpass.write_timestamp(&query_set_write_timestamp, 0);\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_bind_group(0, &bind_group, &[]);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.draw(0..3, 0..1);\n\n        // Drop the query sets. Then do a device poll to make sure they're not dropped too early, no matter what.\n        drop(query_set_timestamp_writes);\n        drop(query_set_write_timestamp);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n    }\n\n    assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;\n}\n\n#[gpu_test]\nstatic RENDER_PASS_KEEP_ENCODER_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .enable_noop(),\n    )\n    .run_async(render_pass_keep_encoder_alive);\n\nasync fn render_pass_keep_encoder_alive(ctx: TestingContext) {\n    let ResourceSetup {\n        bind_group,\n        vertex_buffer,\n        index_buffer,\n        pipeline,\n        color_attachment_view,\n        depth_stencil_view,\n        ..\n    } = resource_setup(&ctx);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            view: &color_attachment_view,\n            depth_slice: None,\n            resolve_target: None,\n            ops: wgpu::Operations::default(),\n        })],\n        depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {\n            view: &depth_stencil_view,\n            depth_ops: Some(wgpu::Operations {\n                load: wgpu::LoadOp::Clear(1.0),\n                store: wgpu::StoreOp::Store,\n            }),\n            stencil_ops: None,\n        }),\n        ..Default::default()\n    });\n\n    // Now drop the encoder - it is kept alive by the compute pass.\n    // To do so, we have to make the compute pass forget the lifetime constraint first.\n    let mut rpass = rpass.forget_lifetime();\n    drop(encoder);\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    // Record some a draw command.\n    rpass.set_pipeline(&pipeline);\n    rpass.set_bind_group(0, &bind_group, &[]);\n    rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n    rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n    rpass.draw(0..3, 0..1);\n\n    // Dropping the pass will still execute the pass, even though there's no way to submit it.\n    // Ideally, this would log an error, but the encoder is not dropped until the compute pass is dropped,\n    // making this a valid operation.\n    // (If instead the encoder was explicitly destroyed or finished, this would be an error.)\n    valid(&ctx.device, || drop(rpass));\n}\n\nasync fn assert_render_pass_executed_normally(\n    mut encoder: wgpu::CommandEncoder,\n    gpu_buffer: wgpu::Buffer,\n    cpu_buffer: wgpu::Buffer,\n    buffer_size: u64,\n    ctx: TestingContext,\n) {\n    encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size);\n    ctx.queue.submit([encoder.finish()]);\n    cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = cpu_buffer.slice(..).get_mapped_range();\n\n    let floats: &[f32] = bytemuck::cast_slice(&data);\n    assert!(floats[0] >= 2.0);\n    assert!(floats[1] >= 4.0);\n    assert!(floats[2] >= 6.0);\n    assert!(floats[3] >= 8.0);\n}\n\n// Setup ------------------------------------------------------------\n\nstruct ResourceSetup {\n    gpu_buffer: wgpu::Buffer,\n    cpu_buffer: wgpu::Buffer,\n    buffer_size: u64,\n\n    indirect_buffer: wgpu::Buffer,\n    vertex_buffer: wgpu::Buffer,\n    index_buffer: wgpu::Buffer,\n    bind_group: wgpu::BindGroup,\n    pipeline: wgpu::RenderPipeline,\n\n    color_attachment_view: wgpu::TextureView,\n    color_attachment_resolve_view: wgpu::TextureView,\n    depth_stencil_view: wgpu::TextureView,\n    occlusion_query_set: wgpu::QuerySet,\n}\n\nfn resource_setup(ctx: &TestingContext) -> ResourceSetup {\n    let sm = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"shader\"),\n            source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),\n        });\n\n    let buffer_size = 4 * size_of::<f32>() as u64;\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind_group_layout\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::FRAGMENT,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(buffer_size),\n                },\n                count: None,\n            }],\n        });\n\n    let gpu_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"gpu_buffer\"),\n            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n            contents: bytemuck::bytes_of(&[1.0_f32, 2.0, 3.0, 4.0]),\n        });\n\n    let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"cpu_buffer\"),\n        size: buffer_size,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let vertex_count = 3;\n    let indirect_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"gpu_buffer\"),\n            usage: wgpu::BufferUsages::INDIRECT,\n            contents: wgpu::util::DrawIndirectArgs {\n                vertex_count,\n                instance_count: 1,\n                first_vertex: 0,\n                first_instance: 0,\n            }\n            .as_bytes(),\n        });\n\n    let vertex_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"vertex_buffer\"),\n        usage: wgpu::BufferUsages::VERTEX,\n        size: size_of::<u32>() as u64 * vertex_count as u64,\n        mapped_at_creation: false,\n    });\n\n    let index_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"vertex_buffer\"),\n            usage: wgpu::BufferUsages::INDEX,\n            contents: bytemuck::cast_slice(&[0_u32, 1, 2]),\n        });\n\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"bind_group\"),\n        layout: &bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: gpu_buffer.as_entire_binding(),\n        }],\n    });\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let target_size = wgpu::Extent3d {\n        width: 4,\n        height: 4,\n        depth_or_array_layers: 1,\n    };\n    let target_msaa = 4;\n    let target_format = wgpu::TextureFormat::Bgra8UnormSrgb;\n\n    let target_desc = wgpu::TextureDescriptor {\n        label: Some(\"target_tex\"),\n        size: target_size,\n        mip_level_count: 1,\n        sample_count: target_msaa,\n        dimension: wgpu::TextureDimension::D2,\n        format: target_format,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[target_format],\n    };\n    let target_tex = ctx.device.create_texture(&target_desc);\n    let target_tex_resolve = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"target_resolve\"),\n        sample_count: 1,\n        ..target_desc\n    });\n\n    let color_attachment_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());\n    let color_attachment_resolve_view =\n        target_tex_resolve.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let depth_stencil_format = wgpu::TextureFormat::Depth32Float;\n    let depth_stencil = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"depth_stencil\"),\n        format: depth_stencil_format,\n        view_formats: &[depth_stencil_format],\n        ..target_desc\n    });\n    let depth_stencil_view = depth_stencil.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let occlusion_query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"occ_query_set\"),\n        ty: wgpu::QueryType::Occlusion,\n        count: 1,\n    });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &sm,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: 4,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &wgpu::vertex_attr_array![0 => Uint32],\n                }],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &sm,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(target_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleStrip,\n                strip_index_format: Some(wgpu::IndexFormat::Uint32),\n                ..Default::default()\n            },\n            depth_stencil: Some(wgpu::DepthStencilState {\n                format: depth_stencil_format,\n                depth_write_enabled: Some(true),\n                depth_compare: Some(wgpu::CompareFunction::LessEqual),\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            }),\n            multisample: wgpu::MultisampleState {\n                count: target_msaa,\n                mask: !0,\n                alpha_to_coverage_enabled: false,\n            },\n            multiview_mask: None,\n            cache: None,\n        });\n\n    ResourceSetup {\n        gpu_buffer,\n        cpu_buffer,\n        buffer_size,\n\n        indirect_buffer,\n        vertex_buffer,\n        index_buffer,\n        bind_group,\n        pipeline,\n\n        color_attachment_view,\n        color_attachment_resolve_view,\n        depth_stencil_view,\n        occlusion_query_set,\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/render_target.rs",
    "content": "use wgpu::{\n    util::{BufferInitDescriptor, DeviceExt},\n    vertex_attr_array,\n};\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        DRAW_TO_2D_VIEW,\n        DRAW_TO_2D_ARRAY_VIEW,\n        RESOLVE_TO_2D_VIEW,\n        RESOLVE_TO_2D_ARRAY_VIEW,\n        DRAW_TO_3D_VIEW,\n    ]);\n}\n\n#[gpu_test]\nstatic DRAW_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2, false));\n\n#[gpu_test]\nstatic DRAW_TO_2D_ARRAY_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2Array, false));\n\n#[gpu_test]\nstatic RESOLVE_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2, true));\n\n#[gpu_test]\nstatic RESOLVE_TO_2D_ARRAY_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| run_test(ctx, wgpu::TextureViewDimension::D2Array, true));\n\nasync fn run_test(\n    ctx: TestingContext,\n    view_dimension: wgpu::TextureViewDimension,\n    multisample: bool,\n) {\n    let vertex_buffer_content: &[f32; 12] = &[\n        // Triangle 1\n        -1.0, -1.0, // Bottom left\n        1.0, 1.0, // Top right\n        -1.0, 1.0, // Top left\n        // Triangle 2\n        -1.0, -1.0, // Bottom left\n        1.0, -1.0, // Bottom right\n        1.0, 1.0, // Top right\n    ];\n    let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(vertex_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let shader_src = \"\n            @vertex\n            fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f {\n                return vec4f(position, 0.0, 1.0);\n            }\n\n            @fragment\n            fn fs_main() -> @location(0) vec4f {\n                return vec4f(1.0);\n            }\n        \";\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &[wgpu::VertexBufferLayout {\n                array_stride: 8,\n                step_mode: wgpu::VertexStepMode::Vertex,\n                attributes: &vertex_attr_array![0 => Float32x2],\n            }],\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState {\n            count: if multisample { 4 } else { 1 },\n            mask: !0,\n            alpha_to_coverage_enabled: false,\n        },\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    const SIZE: u32 = 512;\n    const LAYERS: u32 = 2;\n    const MIPS: u32 = 2;\n    const fn size_for_mips(mips: u32) -> u64 {\n        let mut out: u64 = 0;\n        let mut mip = 0;\n        while mip < mips {\n            let size = SIZE as u64 >> mip;\n            out += size * size;\n\n            mip += 1;\n        }\n        out * LAYERS as u64\n    }\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: SIZE,\n            height: SIZE,\n            depth_or_array_layers: LAYERS,\n        },\n        mip_level_count: MIPS,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_for_mips(MIPS),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    for mip in 0..MIPS {\n        let ms_texture_view = if multisample {\n            let ms_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n                label: None,\n                size: wgpu::Extent3d {\n                    width: SIZE >> mip,\n                    height: SIZE >> mip,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 4,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::R8Unorm,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            });\n            let ms_texture_view = ms_texture.create_view(&wgpu::TextureViewDescriptor::default());\n            Some(ms_texture_view)\n        } else {\n            None\n        };\n        for layer in 0..LAYERS {\n            let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor {\n                label: None,\n                format: Some(wgpu::TextureFormat::R8Unorm),\n                dimension: Some(view_dimension),\n                usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),\n                aspect: wgpu::TextureAspect::All,\n                base_mip_level: mip,\n                mip_level_count: Some(1),\n                base_array_layer: layer,\n                array_layer_count: Some(1),\n            });\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: ms_texture_view.as_ref().unwrap_or(&out_texture_view),\n                    depth_slice: None,\n                    resolve_target: multisample.then_some(&out_texture_view),\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: if multisample {\n                            wgpu::StoreOp::Discard\n                        } else {\n                            wgpu::StoreOp::Store\n                        },\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.set_pipeline(&pipeline);\n            rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n            rpass.draw(0..6, 0..1);\n        }\n    }\n\n    for mip in 0..MIPS {\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &out_texture,\n                mip_level: mip,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &readback_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: size_for_mips(mip),\n                    bytes_per_row: Some(SIZE >> mip),\n                    rows_per_image: Some(SIZE >> mip),\n                },\n            },\n            wgpu::Extent3d {\n                width: SIZE >> mip,\n                height: SIZE >> mip,\n                depth_or_array_layers: LAYERS,\n            },\n        );\n    }\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let succeeded = data.iter().all(|b| *b == u8::MAX);\n    assert!(succeeded);\n}\n\n#[gpu_test]\nstatic DRAW_TO_3D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits {\n                max_texture_dimension_3d: 512,\n                ..wgpu::Limits::downlevel_webgl2_defaults()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT\"),\n            ),\n    )\n    .run_async(run_test_3d);\n\nasync fn run_test_3d(ctx: TestingContext) {\n    let vertex_buffer_content: &[f32; 12] = &[\n        // Triangle 1\n        -1.0, -1.0, // Bottom left\n        1.0, 1.0, // Top right\n        -1.0, 1.0, // Top left\n        // Triangle 2\n        -1.0, -1.0, // Bottom left\n        1.0, -1.0, // Bottom right\n        1.0, 1.0, // Top right\n    ];\n    let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice(vertex_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let shader_src = \"\n            @vertex\n            fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f {\n                return vec4f(position, 0.0, 1.0);\n            }\n\n            @fragment\n            fn fs_main() -> @location(0) vec4f {\n                return vec4f(1.0);\n            }\n        \";\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &[wgpu::VertexBufferLayout {\n                array_stride: 8,\n                step_mode: wgpu::VertexStepMode::Vertex,\n                attributes: &vertex_attr_array![0 => Float32x2],\n            }],\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    const SIZE: u32 = 512;\n    const DEPTH: u32 = 2;\n    const MIPS: u32 = 2;\n    const fn size_for_mips(mips: u32) -> u64 {\n        let mut out: u64 = 0;\n        let mut mip = 0;\n        while mip < mips {\n            let size = SIZE as u64 >> mip;\n            let z = DEPTH as u64 >> mip;\n            out += size * size * z;\n\n            mip += 1;\n        }\n        out\n    }\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: SIZE,\n            height: SIZE,\n            depth_or_array_layers: DEPTH,\n        },\n        mip_level_count: MIPS,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D3,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: size_for_mips(MIPS),\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    for mip in 0..MIPS {\n        let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor {\n            base_mip_level: mip,\n            mip_level_count: Some(1),\n            ..Default::default()\n        });\n        for layer in 0..DEPTH >> mip {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &out_texture_view,\n                    depth_slice: Some(layer),\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            rpass.set_pipeline(&pipeline);\n            rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n            rpass.draw(0..6, 0..1);\n        }\n    }\n\n    for mip in 0..MIPS {\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &out_texture,\n                mip_level: mip,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &readback_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: size_for_mips(mip),\n                    bytes_per_row: Some(SIZE >> mip),\n                    rows_per_image: Some(SIZE >> mip),\n                },\n            },\n            wgpu::Extent3d {\n                width: SIZE >> mip,\n                height: SIZE >> mip,\n                depth_or_array_layers: DEPTH >> mip,\n            },\n        );\n    }\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let succeeded = data.iter().all(|b| *b == u8::MAX);\n    assert!(succeeded);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/resource_descriptor_accessor.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(BUFFER_SIZE_AND_USAGE);\n}\n\n#[gpu_test]\nstatic BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 1234,\n            usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        assert_eq!(buffer.size(), 1234);\n        assert_eq!(\n            buffer.usage(),\n            wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/resource_error.rs",
    "content": "use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([BAD_BUFFER, BAD_TEXTURE]);\n}\n\n#[gpu_test]\nstatic BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        // Create a buffer with bad parameters and call a few methods.\n        // Validation should fail but there should be not panic.\n        let buffer = fail(\n            &ctx.device,\n            || {\n                ctx.device.create_buffer(&wgpu::BufferDescriptor {\n                    label: None,\n                    size: 99999999,\n                    usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE,\n                    mapped_at_creation: false,\n                })\n            },\n            Some(\"`map` usage can only be combined with the opposite `copy`\"),\n        );\n\n        fail(\n            &ctx.device,\n            || buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}),\n            Some(\"Buffer with '' label is invalid\"),\n        );\n        fail(\n            &ctx.device,\n            || buffer.unmap(),\n            Some(\"Buffer with '' label is invalid\"),\n        );\n        valid(&ctx.device, || buffer.destroy());\n        valid(&ctx.device, || buffer.destroy());\n    });\n\n#[gpu_test]\nstatic BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let texture = fail(\n            &ctx.device,\n            || {\n                ctx.device.create_texture(&wgpu::TextureDescriptor {\n                    label: None,\n                    size: wgpu::Extent3d {\n                        width: 0,\n                        height: 12345678,\n                        depth_or_array_layers: 9001,\n                    },\n                    mip_level_count: 2000,\n                    sample_count: 27,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                    usage: wgpu::TextureUsages::all(),\n                    view_formats: &[],\n                })\n            },\n            Some(\"dimension x is zero\"),\n        );\n\n        fail(\n            &ctx.device,\n            || {\n                let _ = texture.create_view(&wgpu::TextureViewDescriptor::default());\n            },\n            Some(\"Texture with '' label is invalid\"),\n        );\n        valid(&ctx.device, || texture.destroy());\n        valid(&ctx.device, || texture.destroy());\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/samplers.rs",
    "content": "//! D3D12 samplers are fun and we're doing a decent amount of polyfilling with them.\n//!\n//! Do some tests to ensure things are working correctly and nothing gets mad.\n\nuse wgpu_test::{\n    did_oom, gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters,\n    TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        SAMPLER_DEDUPLICATION,\n        SAMPLER_CREATION_FAILURE,\n        SAMPLER_SINGLE_BIND_GROUP,\n        SAMPLER_MULTI_BIND_GROUP,\n    ]);\n}\n\n// A number large enough to likely cause sampler caches to run out of space\n// on some devices.\nconst PROBABLY_PROBLEMATIC_SAMPLER_COUNT: u32 = 8 * 1024;\n\n#[gpu_test]\nstatic SAMPLER_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(sampler_deduplication);\n\n// Create a large number of samplers from the same two descriptors.\n//\n// Sampler deduplication in the backend should ensure this doesn't cause any issues.\nfn sampler_deduplication(ctx: TestingContext) {\n    // Create 2 different sampler descriptors\n    let desc1 = wgpu::SamplerDescriptor {\n        label: Some(\"sampler1\"),\n        address_mode_u: wgpu::AddressMode::ClampToEdge,\n        address_mode_v: wgpu::AddressMode::ClampToEdge,\n        address_mode_w: wgpu::AddressMode::ClampToEdge,\n        mag_filter: wgpu::FilterMode::Nearest,\n        min_filter: wgpu::FilterMode::Nearest,\n        mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n        lod_min_clamp: 0.0,\n        lod_max_clamp: 100.0,\n        compare: None,\n        anisotropy_clamp: 1,\n        border_color: None,\n    };\n\n    let desc2 = wgpu::SamplerDescriptor {\n        label: Some(\"sampler2\"),\n        address_mode_u: wgpu::AddressMode::ClampToEdge,\n        address_mode_v: wgpu::AddressMode::ClampToEdge,\n        address_mode_w: wgpu::AddressMode::ClampToEdge,\n        mag_filter: wgpu::FilterMode::Linear,\n        min_filter: wgpu::FilterMode::Linear,\n        mipmap_filter: wgpu::MipmapFilterMode::Linear,\n        lod_min_clamp: 0.0,\n        lod_max_clamp: 100.0,\n        compare: None,\n        anisotropy_clamp: 1,\n        border_color: None,\n    };\n\n    // Now create a bunch of samplers with these descriptors\n    let samplers = (0..PROBABLY_PROBLEMATIC_SAMPLER_COUNT)\n        .map(|i| {\n            let desc = if i % 2 == 0 { &desc1 } else { &desc2 };\n            valid(&ctx.device, || ctx.device.create_sampler(desc))\n        })\n        .collect::<Vec<_>>();\n\n    drop(samplers);\n}\n\n#[gpu_test]\nstatic SAMPLER_CREATION_FAILURE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(sampler_creation_failure);\n\n/// We want to test that sampler creation properly fails when we hit internal sampler\n/// cache limits. As we don't actually know what the limit is, we first create as many\n/// samplers as we can until we get the first failure.\n///\n/// This failure being caught ensures that the error catching machinery on samplers\n/// is working as expected.\n///\n/// We then clear all samplers and poll the device, which should leave the caches\n/// completely empty.\n///\n/// We then try to create the same number of samplers to ensure the cache was entirely\n/// cleared.\nfn sampler_creation_failure(ctx: TestingContext) {\n    let desc = wgpu::SamplerDescriptor {\n        label: Some(\"sampler1\"),\n        address_mode_u: wgpu::AddressMode::ClampToEdge,\n        address_mode_v: wgpu::AddressMode::ClampToEdge,\n        address_mode_w: wgpu::AddressMode::ClampToEdge,\n        mag_filter: wgpu::FilterMode::Nearest,\n        min_filter: wgpu::FilterMode::Nearest,\n        mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n        lod_min_clamp: 0.0,\n        lod_max_clamp: 100.0,\n        compare: None,\n        anisotropy_clamp: 1,\n        border_color: None,\n    };\n\n    let mut sampler_storage = Vec::with_capacity(PROBABLY_PROBLEMATIC_SAMPLER_COUNT as usize);\n\n    for i in 0..PROBABLY_PROBLEMATIC_SAMPLER_COUNT {\n        let (failed, sampler) = did_oom(&ctx.device, || {\n            ctx.device.create_sampler(&wgpu::SamplerDescriptor {\n                lod_min_clamp: i as f32 * 0.01,\n                ..desc\n            })\n        });\n\n        if failed {\n            break;\n        }\n\n        sampler_storage.push(sampler);\n    }\n\n    let failed_count = sampler_storage.len();\n\n    sampler_storage.clear();\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    for i in 0..failed_count {\n        valid(&ctx.device, || {\n            eprintln!(\"Trying to create sampler {i}\");\n            let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor {\n                lod_min_clamp: i as f32 * 0.01,\n                // Change the max clamp to ensure the sampler is using different cache slots from\n                // the previous run.\n                lod_max_clamp: 200.0,\n                ..desc\n            });\n            sampler_storage.push(sampler);\n        });\n    }\n}\n\nconst SINGLE_GROUP_BINDINGS: &str = r#\"\n@group(0) @binding(0) var texture: texture_2d<f32>;\n@group(0) @binding(1) var sampler0: sampler;\n@group(0) @binding(2) var sampler1: sampler;\n@group(0) @binding(3) var sampler2: sampler;\n\n@group(1) @binding(0) var<storage, read_write> results: array<vec4f, 3>;\n\"#;\n\nconst MULTI_GROUP_BINDINGS: &str = r#\"\n@group(0) @binding(0) var texture: texture_2d<f32>;\n@group(0) @binding(1) var sampler0: sampler;\n@group(1) @binding(0) var sampler1: sampler;\n@group(2) @binding(0) var sampler2: sampler;\n\n@group(3) @binding(0) var<storage, read_write> results: array<vec4f, 3>;\n\"#;\n\nconst SAMPLER_CODE: &str = r#\"\n@compute @workgroup_size(1, 1, 1)\nfn cs_main() {\n    // When sampling a 2x2 texture at the bottom left, we can change the address mode\n    // on S/T to get different values. This allows us to make sure the right sampler\n    // is being used.\n    results[0] = textureSampleLevel(texture, sampler0, vec2f(0.0, 1.0), 0.0);\n    results[1] = textureSampleLevel(texture, sampler1, vec2f(0.0, 1.0), 0.0);\n    results[2] = textureSampleLevel(texture, sampler2, vec2f(0.0, 1.0), 0.0);\n}\n\"#;\n\nenum GroupType {\n    Single,\n    Multi,\n}\n\n#[gpu_test]\nstatic SAMPLER_SINGLE_BIND_GROUP: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            // In OpenGL textures cannot be used with multiple samplers.\n            .skip(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),\n    )\n    .run_sync(|ctx| sampler_bind_group(ctx, GroupType::Single));\n\n#[gpu_test]\nstatic SAMPLER_MULTI_BIND_GROUP: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            // In OpenGL textures cannot be used with multiple samplers.\n            .skip(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),\n    )\n    .run_sync(|ctx| sampler_bind_group(ctx, GroupType::Multi));\n\nfn sampler_bind_group(ctx: TestingContext, group_type: GroupType) {\n    let bindings = match group_type {\n        GroupType::Single => SINGLE_GROUP_BINDINGS,\n        GroupType::Multi => MULTI_GROUP_BINDINGS,\n    };\n\n    let full_shader = format!(\"{bindings}\\n{SAMPLER_CODE}\");\n\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            source: wgpu::ShaderSource::Wgsl(full_shader.into()),\n            label: None,\n        });\n\n    let mut bind_group_layouts = Vec::new();\n\n    match group_type {\n        GroupType::Single => {\n            let bgl = ctx\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: Some(\"combination_bgl\"),\n                    entries: &[\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 0,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Texture {\n                                sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                                view_dimension: wgpu::TextureViewDimension::D2,\n                                multisampled: false,\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 1,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 2,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 3,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                            count: None,\n                        },\n                    ],\n                });\n\n            bind_group_layouts.push(bgl);\n        }\n        GroupType::Multi => {\n            let bgl0 = ctx\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: Some(\"multiple_bgl0\"),\n                    entries: &[\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 0,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Texture {\n                                sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                                view_dimension: wgpu::TextureViewDimension::D2,\n                                multisampled: false,\n                            },\n                            count: None,\n                        },\n                        wgpu::BindGroupLayoutEntry {\n                            binding: 1,\n                            visibility: wgpu::ShaderStages::COMPUTE,\n                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                            count: None,\n                        },\n                    ],\n                });\n\n            let bgl1 = ctx\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: Some(\"multiple_bgl1\"),\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    }],\n                });\n\n            let bgl2 = ctx\n                .device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    label: Some(\"multiple_bgl2\"),\n                    entries: &[wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::COMPUTE,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    }],\n                });\n\n            bind_group_layouts.push(bgl0);\n            bind_group_layouts.push(bgl1);\n            bind_group_layouts.push(bgl2);\n        }\n    }\n\n    let output_bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"output_bgl\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: None,\n                },\n                count: None,\n            }],\n        });\n\n    let mut bgl_references: Vec<_> = bind_group_layouts.iter().map(Some).collect();\n\n    bgl_references.push(Some(&output_bgl));\n\n    let pipeline_layout = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"pipeline_layout\"),\n            bind_group_layouts: &bgl_references,\n            immediate_size: 0,\n        });\n\n    let input_image = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"input_image\"),\n        size: wgpu::Extent3d {\n            width: 2,\n            height: 2,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n        view_formats: &[],\n    });\n\n    let input_image_view = input_image.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let image_data: [u8; 16] = [\n        255, 0, 0, 255, /* */ 0, 255, 0, 255, //\n        0, 0, 255, 255, /* */ 255, 255, 255, 255, //\n    ];\n\n    ctx.queue.write_texture(\n        wgpu::TexelCopyTextureInfo {\n            texture: &input_image,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        &image_data,\n        wgpu::TexelCopyBufferLayout {\n            offset: 0,\n            bytes_per_row: Some(8),\n            rows_per_image: None,\n        },\n        wgpu::Extent3d {\n            width: 2,\n            height: 2,\n            depth_or_array_layers: 1,\n        },\n    );\n\n    let address_modes = [\n        (\n            wgpu::AddressMode::ClampToEdge,\n            wgpu::AddressMode::ClampToEdge,\n        ),\n        (wgpu::AddressMode::Repeat, wgpu::AddressMode::ClampToEdge),\n        (wgpu::AddressMode::ClampToEdge, wgpu::AddressMode::Repeat),\n    ];\n\n    let samplers = address_modes.map(|(address_mode_u, address_mode_v)| {\n        ctx.device.create_sampler(&wgpu::SamplerDescriptor {\n            label: None,\n            address_mode_u,\n            address_mode_v,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::MipmapFilterMode::Nearest,\n            lod_min_clamp: 0.0,\n            lod_max_clamp: 100.0,\n            compare: None,\n            anisotropy_clamp: 1,\n            border_color: None,\n        })\n    });\n\n    let mut bind_groups = Vec::new();\n\n    match group_type {\n        GroupType::Single => {\n            let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: Some(\"combination_bg\"),\n                layout: &bind_group_layouts[0],\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureView(&input_image_view),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: wgpu::BindingResource::Sampler(&samplers[0]),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 2,\n                        resource: wgpu::BindingResource::Sampler(&samplers[1]),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 3,\n                        resource: wgpu::BindingResource::Sampler(&samplers[2]),\n                    },\n                ],\n            });\n\n            bind_groups.push(bg);\n        }\n        GroupType::Multi => {\n            let bg0 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: Some(\"multiple_bg0\"),\n                layout: &bind_group_layouts[0],\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureView(&input_image_view),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: wgpu::BindingResource::Sampler(&samplers[0]),\n                    },\n                ],\n            });\n\n            let bg1 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: Some(\"multiple_bg1\"),\n                layout: &bind_group_layouts[1],\n                entries: &[wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::Sampler(&samplers[1]),\n                }],\n            });\n\n            let bg2 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: Some(\"multiple_bg2\"),\n                layout: &bind_group_layouts[2],\n                entries: &[wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::Sampler(&samplers[2]),\n                }],\n            });\n\n            bind_groups.push(bg0);\n            bind_groups.push(bg1);\n            bind_groups.push(bg2);\n        }\n    }\n\n    let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"output_buffer\"),\n        size: 48,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let transfer_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"transfer_buffer\"),\n        size: 48,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let output_bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"output_bg\"),\n        layout: &output_bgl,\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                buffer: &output_buffer,\n                offset: 0,\n                size: None,\n            }),\n        }],\n    });\n\n    let mut bg_references = bind_groups.iter().collect::<Vec<_>>();\n\n    bg_references.push(&output_bg);\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"pipeline\"),\n            layout: Some(&pipeline_layout),\n            module: &module,\n            entry_point: Some(\"cs_main\"),\n            cache: None,\n            compilation_options: Default::default(),\n        });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"encoder\"),\n        });\n\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&pipeline);\n        for (i, &bg) in bg_references.iter().enumerate() {\n            cpass.set_bind_group(i as u32, bg, &[]);\n        }\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &transfer_buffer, 0, 48);\n\n    ctx.queue.submit([encoder.finish()]);\n    let buffer_slice = transfer_buffer.slice(..);\n    buffer_slice.map_async(wgpu::MapMode::Read, |_| {});\n\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    let buffer_data = buffer_slice.get_mapped_range();\n\n    let f32_buffer: &[f32] = bytemuck::cast_slice(&buffer_data);\n\n    let correct_values: [f32; 12] = [\n        0.0, 0.0, 1.0, 1.0, //\n        0.5, 0.5, 1.0, 1.0, //\n        0.5, 0.0, 0.5, 1.0, //\n    ];\n    let iter = f32_buffer.iter().zip(correct_values.iter());\n    for (&result, &value) in iter {\n        approx::assert_relative_eq!(result, value, max_relative = 0.02);\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/scissor_tests/mod.rs",
    "content": "use wgpu_test::{gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestingContext};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        SCISSOR_TEST_FULL_RECT,\n        SCISSOR_TEST_EMPTY_RECT,\n        SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET,\n        SCISSOR_TEST_CUSTOM_RECT,\n    ]);\n}\n\nstruct Rect {\n    x: u32,\n    y: u32,\n    width: u32,\n    height: u32,\n}\n\nconst TEXTURE_HEIGHT: u32 = 2;\nconst TEXTURE_WIDTH: u32 = 2;\nconst BUFFER_SIZE: usize = (TEXTURE_WIDTH * TEXTURE_HEIGHT * 4) as usize;\n\nasync fn scissor_test_impl(\n    ctx: &TestingContext,\n    scissor_rect: Rect,\n    expected_data: [u8; BUFFER_SIZE],\n) {\n    let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"Offscreen texture\"),\n        size: wgpu::Extent3d {\n            width: TEXTURE_WIDTH,\n            height: TEXTURE_HEIGHT,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"solid_white.wgsl\"));\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"Pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let readback_buffer = image::ReadbackBuffers::new(&ctx.device, &texture);\n    {\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"Renderpass\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &texture_view,\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: 0.0,\n                            g: 0.0,\n                            b: 0.0,\n                            a: 0.0,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            render_pass.set_pipeline(&pipeline);\n            render_pass.set_scissor_rect(\n                scissor_rect.x,\n                scissor_rect.y,\n                scissor_rect.width,\n                scissor_rect.height,\n            );\n            render_pass.draw(0..3, 0..1);\n        }\n        readback_buffer.copy_from(&ctx.device, &mut encoder, &texture);\n        ctx.queue.submit(Some(encoder.finish()));\n    }\n    readback_buffer\n        .assert_buffer_contents(ctx, &expected_data)\n        .await;\n}\n\n#[gpu_test]\nstatic SCISSOR_TEST_FULL_RECT: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        scissor_test_impl(\n            &ctx,\n            Rect {\n                x: 0,\n                y: 0,\n                width: TEXTURE_WIDTH,\n                height: TEXTURE_HEIGHT,\n            },\n            [255; BUFFER_SIZE],\n        )\n        .await\n    });\n\n#[gpu_test]\nstatic SCISSOR_TEST_EMPTY_RECT: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        scissor_test_impl(\n            &ctx,\n            Rect {\n                x: 0,\n                y: 0,\n                width: 0,\n                height: 0,\n            },\n            [0; BUFFER_SIZE],\n        )\n        .await;\n    });\n\n#[gpu_test]\nstatic SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new()\n    .run_async(|ctx| async move {\n        scissor_test_impl(\n            &ctx,\n            Rect {\n                x: TEXTURE_WIDTH / 2,\n                y: TEXTURE_HEIGHT / 2,\n                width: 0,\n                height: 0,\n            },\n            [0; BUFFER_SIZE],\n        )\n        .await\n    });\n\n#[gpu_test]\nstatic SCISSOR_TEST_CUSTOM_RECT: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        let mut expected_result = [0; BUFFER_SIZE];\n        expected_result[((3 * BUFFER_SIZE) / 4)..][..BUFFER_SIZE / 4]\n            .copy_from_slice(&[255; BUFFER_SIZE / 4]);\n\n        scissor_test_impl(\n            &ctx,\n            Rect {\n                x: TEXTURE_WIDTH / 2,\n                y: TEXTURE_HEIGHT / 2,\n                width: TEXTURE_WIDTH / 2,\n                height: TEXTURE_HEIGHT / 2,\n            },\n            expected_result,\n        )\n        .await;\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/scissor_tests/solid_white.wgsl",
    "content": "// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    .\n// |              * .\n// |***************\n// |            . 1,-1\n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) ->  @builtin(position) vec4<f32> {\n    let x = i32(vertex_index) / 2;\n    let y = i32(vertex_index) & 1;\n    return vec4<f32>(\n        f32(x) * 4.0 - 1.0,\n        1.0 - f32(y) * 4.0,\n        0.0, 1.0\n    );\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/array_size_overrides.rs",
    "content": "use std::mem::size_of_val;\nuse wgpu::util::DeviceExt;\nuse wgpu::{BufferDescriptor, BufferUsages, MapMode, PollType};\nuse wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(ARRAY_SIZE_OVERRIDES);\n}\n\nconst SHADER: &str = r#\"\n    const testing_shared: array<i32, 1> = array(8);\n    override n = testing_shared[0u];\n    override m = testing_shared[0u];\n\n    var<workgroup> arr: array<u32, n - 2>;\n\n    @group(0) @binding(0)\n    var<storage, read_write> output: array<u32>;\n\n    // When `n` is overridden to 14 above, it will generate the type `array<u32, 14 - 2>` which\n    // already exists in the program because of variable declaration. Ensures naga does not panic\n    // when this happens.\n    //\n    // See https://github.com/gfx-rs/wgpu/issues/6722 for more info.\n    var<workgroup> testing0: array<u32, 12>;\n\n    // Tests whether two overrides that are initialized by the same expression\n    // crashes the unique types arena\n    //\n    // See https://github.com/gfx-rs/wgpu/pull/6787#pullrequestreview-2576905294\n    var<workgroup> testing1: array<u32, n>;\n    var<workgroup> testing2: array<u32, m>;\n\n    @compute @workgroup_size(1) fn main() {\n        // 1d spiral\n        for (var i = 0; i < n - 2; i++) {\n            arr[i] = u32(n - 2 - i);\n            if (i + 1 < (n + (n % 2)) / 2) {\n                arr[i] -= 1u;\n            }\n        }\n        var i = 0u;\n        var j = 1u;\n        while (i != j) {\n            // non-commutative\n            output[0] = output[0] * arr[i] + arr[i];\n            j = i;\n            i = arr[i];\n        }\n    }\n\"#;\n\n#[gpu_test]\nstatic ARRAY_SIZE_OVERRIDES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits::default())\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(move |ctx| async move {\n        array_size_overrides(&ctx, None, &[534], false).await;\n        array_size_overrides(&ctx, Some(14), &[286480122], false).await;\n        // // TODO: Restore this with the resolution for\n        // // <https://github.com/gfx-rs/wgpu/issues/7806>.\n        // array_size_overrides(&ctx, Some(1), &[0], true).await;\n    });\n\nasync fn array_size_overrides(\n    ctx: &TestingContext,\n    n: Option<u32>,\n    out: &[u32],\n    should_fail: bool,\n) {\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(SHADER)),\n        });\n    let pipeline_options = wgpu::PipelineCompilationOptions {\n        constants: &[(\"n\", f64::from(n.unwrap_or(0)))],\n        ..Default::default()\n    };\n    let compute_pipeline = fail_if(\n        &ctx.device,\n        should_fail,\n        || {\n            ctx.device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: None,\n                    layout: None,\n                    module: &module,\n                    entry_point: Some(\"main\"),\n                    compilation_options: if n.is_some() {\n                        pipeline_options\n                    } else {\n                        wgpu::PipelineCompilationOptions::default()\n                    },\n                    cache: None,\n                })\n        },\n        None,\n    );\n    if should_fail {\n        return;\n    }\n    let init: &[u32] = &[0];\n    let init_size: u64 = size_of_val(init).try_into().unwrap();\n    let buffer = DeviceExt::create_buffer_init(\n        &ctx.device,\n        &wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(init),\n            usage: wgpu::BufferUsages::STORAGE\n                | wgpu::BufferUsages::COPY_DST\n                | wgpu::BufferUsages::COPY_SRC,\n        },\n    );\n    let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"mapping buffer\"),\n        size: init_size,\n        usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&compute_pipeline);\n        let bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n        let bind_group_entries = [wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }];\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &bind_group_entries,\n        });\n        cpass.set_bind_group(0, &bind_group, &[]);\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n    encoder.copy_buffer_to_buffer(&buffer, 0, &mapping_buffer, 0, init_size);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());\n    ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n    let mapped = mapping_buffer.slice(..).get_mapped_range();\n\n    let typed: &[u32] = bytemuck::cast_slice(&mapped);\n    assert_eq!(typed, out);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/compilation_messages/error_shader.wgsl",
    "content": "/*🐈🐈🐈🐈🐈🐈🐈*/?\n// Expected Error: invalid character found"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/compilation_messages/mod.rs",
    "content": "use wgpu::include_wgsl;\n\nuse wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        SHADER_COMPILE_SUCCESS,\n        SHADER_COMPILE_ERROR,\n        ENABLE_EXTENSION_AVAILABLE,\n        ENABLE_EXTENSION_UNAVAILABLE,\n    ]);\n}\n\n#[gpu_test]\nstatic SHADER_COMPILE_SUCCESS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let sm = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"successful_shader.wgsl\"));\n\n        let compilation_info = sm.get_compilation_info().await;\n        for message in compilation_info.messages.iter() {\n            assert!(message.message_type != wgpu::CompilationMessageType::Error);\n        }\n    });\n\n#[gpu_test]\nstatic SHADER_COMPILE_ERROR: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let scope = ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);\n        let sm = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"error_shader.wgsl\"));\n        assert!(pollster::block_on(scope.pop()).is_some());\n\n        let compilation_info = sm.get_compilation_info().await;\n        let error_message = compilation_info\n            .messages\n            .iter()\n            .find(|message| message.message_type == wgpu::CompilationMessageType::Error)\n            .expect(\"Expected error message not found\");\n        let span = error_message.location.expect(\"Expected span not found\");\n        assert_eq!(\n            span.offset, 32,\n            \"Expected the offset to be 32, because we're counting UTF-8 bytes\"\n        );\n        assert_eq!(span.length, 1, \"Expected length to roughly be 1\"); // Could be relaxed, depending on the parser requirements.\n        assert_eq!(\n            span.line_number, 1,\n            \"Expected the line number to be 1, because we're counting lines from 1\"\n        );\n        assert_eq!(\n            span.line_position, 33,\n            \"Expected the column number to be 33, because we're counting lines from 1\"\n        );\n    });\n\nconst ENABLE_EXTENSION_SHADER_SOURCE: &str = r#\"\n    enable f16;\n\n    @compute @workgroup_size(1)\n    fn main() {}\n\"#;\n\n#[gpu_test]\nstatic ENABLE_EXTENSION_AVAILABLE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::SHADER_F16)\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| async move {\n        valid(&ctx.device, || {\n            let _ = ctx\n                .device\n                .create_shader_module(wgpu::ShaderModuleDescriptor {\n                    label: Some(\"shader declaring enable extension\"),\n                    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                        ENABLE_EXTENSION_SHADER_SOURCE,\n                    )),\n                });\n        });\n    });\n\n#[gpu_test]\nstatic ENABLE_EXTENSION_UNAVAILABLE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            // SHADER_F16 feature not requested\n            .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)\n            .limits(wgpu::Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| async move {\n        fail(\n            &ctx.device,\n            || {\n                ctx.device\n                    .create_shader_module(wgpu::ShaderModuleDescriptor {\n                        label: Some(\"shader declaring enable extension\"),\n                        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n                            ENABLE_EXTENSION_SHADER_SOURCE,\n                        )),\n                    })\n            },\n            Some(\"the `f16` extension is not supported in the current environment\"),\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/compilation_messages/successful_shader.wgsl",
    "content": "const array_size = 512u;\n\nstruct WStruct {\n    arr: array<u32, array_size>,\n    atom: atomic<u32>\n}\n\nvar<workgroup> w_mem: WStruct;\n\n@group(0) @binding(0)\nvar<storage, read_write> output: array<u32>;\n\n@compute @workgroup_size(1)\nfn read(@builtin(workgroup_id) wgid: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {\n    var is_zero = true;\n    for(var i = 0u; i < array_size; i++) {\n        is_zero &= w_mem.arr[i] == 0u;\n    }\n    is_zero &= atomicLoad(&w_mem.atom) == 0u;\n\n    let idx = wgid.x + (wgid.y * num_workgroups.x) + (wgid.z * num_workgroups.x * num_workgroups.y);\n    output[idx] = u32(!is_zero);\n}\n\n@compute @workgroup_size(1)\nfn write() {\n    for(var i = 0u; i < array_size; i++) {\n        w_mem.arr[i] = i;\n    }\n    atomicStore(&w_mem.atom, 3u);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/data_builtins.rs",
    "content": "use wgpu::{DownlevelFlags, Limits};\n\nuse crate::shader::{shader_input_output_test, InputStorageType, ShaderTest};\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([UNPACK4xU8, UNPACK4xI8, PACK4xU8, PACK4xI8]);\n}\n\n#[allow(non_snake_case)]\nfn create_unpack4xU8_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let input: u32 = 0xAABBCCDD;\n    let output: [u32; 4] = [0xDD, 0xCC, 0xBB, 0xAA];\n    let unpack_u8 = ShaderTest::new(\n        format!(\"unpack4xU8({input:X}) == {output:X?}\"),\n        String::from(\"value: u32\"),\n        String::from(\n            \"\n                let a = unpack4xU8(input.value);\n                output[0] = a[0];\n                output[1] = a[1];\n                output[2] = a[2];\n                output[3] = a[3];\n            \",\n        ),\n        &[input],\n        &output,\n    );\n    tests.push(unpack_u8);\n\n    tests\n}\n\n#[gpu_test]\nstatic UNPACK4xU8: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_unpack4xU8_test())\n    });\n\n#[allow(non_snake_case)]\nfn create_unpack4xI8_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::with_capacity(2);\n\n    let values = [\n        // regular unpacking\n        (0x11223344, [0x44, 0x33, 0x22, 0x11]),\n        // sign extension\n        (0xFF, [-1, 0, 0, 0]),\n    ];\n\n    for (input, output) in values {\n        let unpack_i8 = ShaderTest::new(\n            format!(\"unpack4xI8({input:X}) == {output:X?}\"),\n            String::from(\"value: u32\"),\n            String::from(\n                \"\n                    let a = bitcast<vec4<u32>>(unpack4xI8(input.value));\n                    output[0] = a[0];\n                    output[1] = a[1];\n                    output[2] = a[2];\n                    output[3] = a[3];\n                \",\n            ),\n            &[input],\n            &output,\n        );\n        tests.push(unpack_i8);\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic UNPACK4xI8: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_unpack4xI8_test())\n    });\n\n#[allow(non_snake_case)]\nfn create_pack4xU8_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let input: [u32; 4] = [0xDD, 0xCC, 0xBB, 0xAA];\n    let output: u32 = 0xAABBCCDD;\n    let pack_u8 = ShaderTest::new(\n        format!(\"pack4xU8({input:X?}) == {output:X}\"),\n        String::from(\"value: vec4<u32>\"),\n        String::from(\"output[0] = pack4xU8(input.value);\"),\n        &input,\n        &[output],\n    );\n    tests.push(pack_u8);\n\n    tests\n}\n\n#[gpu_test]\nstatic PACK4xU8: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_pack4xU8_test())\n    });\n\n#[allow(non_snake_case)]\nfn create_pack4xI8_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::with_capacity(2);\n\n    let values: [([i32; 4], u32); 2] = [\n        ([0x44, 0x33, 0x22, 0x11], 0x11223344),\n        // Since the bit representation of the last 8 bits of each number in the input is the same\n        // as the previous test's input numbers, the output should be equal\n        ([-0xBB - 1, -0xCC - 1, -0xDD - 1, -0xEE - 1], 0x11223344),\n    ];\n    // Assure that test data of the first two cases end in equal bit values\n    for value in values.map(|value| value.0)[..2].chunks_exact(2) {\n        let [first, second] = value else {\n            panic!(\"Expected at least 2 test values\")\n        };\n        for (first, second) in first.iter().zip(second.iter()) {\n            assert_eq!(\n                first & 0xFF,\n                second & 0xFF,\n                \"Last 8 bits of test values must be equal\"\n            );\n        }\n    }\n    for (input, output) in values {\n        let pack_i8 = ShaderTest::new(\n            format!(\"pack4xI8({input:X?}) == {output:X}\"),\n            String::from(\"value: vec4<i32>\"),\n            String::from(\"output[0] = pack4xI8(input.value);\"),\n            &input,\n            &[output],\n        );\n        tests.push(pack_i8);\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic PACK4xI8: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_pack4xI8_test())\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/mod.rs",
    "content": "//! Infrastructure for testing particular behavior of shaders across platforms.\n//!\n//! The tests take the form of a input buffer filled with u32 data. A compute\n//! shader is run on the input buffer which generates an output buffer. This\n//! buffer is then read and compared to a given output.\n\nuse std::{borrow::Cow, fmt::Debug};\n\nuse wgpu::{\n    Backends, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,\n    BindingType, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor,\n    ComputePipelineDescriptor, MapMode, PipelineLayoutDescriptor, PollType, ShaderModuleDescriptor,\n    ShaderSource, ShaderStages,\n};\n\nuse wgpu_test::{GpuTestInitializer, TestingContext};\n\npub mod array_size_overrides;\npub mod compilation_messages;\npub mod data_builtins;\npub mod numeric_builtins;\npub mod struct_layout;\npub mod workgroup_size_overrides;\npub mod zero_init_workgroup_mem;\n\npub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {\n    array_size_overrides::all_tests(tests);\n    compilation_messages::all_tests(tests);\n    data_builtins::all_tests(tests);\n    numeric_builtins::all_tests(tests);\n    struct_layout::all_tests(tests);\n    workgroup_size_overrides::all_tests(tests);\n    zero_init_workgroup_mem::all_tests(tests);\n}\n\n#[derive(Clone, Copy, PartialEq)]\nenum InputStorageType {\n    Uniform,\n    Storage,\n    Immediate,\n}\n\nimpl InputStorageType {\n    fn as_str(&self) -> &'static str {\n        match self {\n            InputStorageType::Uniform => \"uniform\",\n            InputStorageType::Storage => \"storage\",\n            InputStorageType::Immediate => \"immediate\",\n        }\n    }\n}\n\n/// Describes a single test of a shader.\nstruct ShaderTest {\n    /// Human readable name\n    name: String,\n    /// Header text. This is arbitrary code injected at the top of the shader. Replaces {{header}}\n    header: String,\n    /// This text will be the body of the `Input` struct. Replaces \"{{input_members}}\"\n    /// in the shader_test shader.\n    custom_struct_members: String,\n    /// This text will be the body of the compute shader. Replaces \"{{body}}\"\n    /// in the shader_test shader.\n    body: String,\n    /// This text will be the input type of the compute shader. Replaces \"{{input_type}}\".\n    ///\n    /// Defaults to \"CustomStruct\"\n    input_type: String,\n    /// This text will be the output type of the compute shader. Replaces \"{{output_type}}\".\n    ///\n    /// Defaults to \"array<u32>\".\n    output_type: String,\n    /// List of values will be written to the input buffer.\n    input_values: Vec<u32>,\n    /// List of lists of valid expected outputs from the shader.\n    output_values: Vec<Vec<u32>>,\n    /// Function which compares the output values to the resulting values and\n    /// prints a message on failure.\n    ///\n    /// Defaults [`Self::default_comparison_function`].\n    output_comparison_fn: fn(&str, &[u32], &[Vec<u32>]) -> bool,\n    /// Value to pre-initialize the output buffer to. Often u32::MAX so\n    /// that writing a 0 looks different than not writing a value at all.\n    ///\n    /// Defaults to u32::MAX.\n    output_initialization: u32,\n    /// Which backends this test will fail on. If the test passes on this\n    /// backend when it shouldn't, an assert will be raised.\n    ///\n    /// Defaults to Backends::empty().\n    failures: Backends,\n}\nimpl ShaderTest {\n    fn default_comparison_function<O: bytemuck::Pod + Debug + PartialEq>(\n        test_name: &str,\n        actual_values: &[u32],\n        expected_values: &[Vec<u32>],\n    ) -> bool {\n        let cast_actual = bytemuck::cast_slice::<u32, O>(actual_values);\n\n        // When printing the error message, we want to trim `cast_actual` to the length\n        // of the longest set of expected values. This tracks that value.\n        let mut max_relevant_value_count = 0;\n\n        for expected in expected_values {\n            let cast_expected = bytemuck::cast_slice::<u32, O>(expected);\n\n            // We shorten the actual to the length of the expected.\n            if &cast_actual[0..cast_expected.len()] == cast_expected {\n                return true;\n            }\n\n            max_relevant_value_count = max_relevant_value_count.max(cast_expected.len());\n        }\n\n        // We haven't found a match, lets print an error.\n\n        eprint!(\n            \"Inner test failure. Actual {:?}. Expected\",\n            &cast_actual[0..max_relevant_value_count]\n        );\n\n        if expected_values.len() != 1 {\n            eprint!(\" one of: \");\n        } else {\n            eprint!(\": \");\n        }\n\n        for (idx, expected) in expected_values.iter().enumerate() {\n            let cast_expected = bytemuck::cast_slice::<u32, O>(expected);\n            eprint!(\"{cast_expected:?}\");\n            if idx + 1 != expected_values.len() {\n                eprint!(\" \");\n            }\n        }\n\n        eprintln!(\". Test {test_name}\");\n\n        false\n    }\n\n    fn new<I: bytemuck::Pod, O: bytemuck::Pod + Debug + PartialEq>(\n        name: String,\n        custom_struct_members: String,\n        body: String,\n        input_values: &[I],\n        output_values: &[O],\n    ) -> Self {\n        Self {\n            name,\n            header: String::new(),\n            custom_struct_members,\n            body,\n            input_type: String::from(\"CustomStruct\"),\n            output_type: String::from(\"array<u32>\"),\n            input_values: bytemuck::pod_collect_to_vec(input_values),\n            output_values: vec![bytemuck::pod_collect_to_vec(output_values)],\n            output_comparison_fn: Self::default_comparison_function::<O>,\n            output_initialization: u32::MAX,\n            failures: Backends::empty(),\n        }\n    }\n\n    fn header(mut self, header: String) -> Self {\n        self.header = header;\n\n        self\n    }\n\n    fn output_type(mut self, output_type: String) -> Self {\n        self.output_type = output_type;\n\n        self\n    }\n\n    /// Add another set of possible outputs. If any of the given\n    /// output values are seen it's considered a success (i.e. this is OR, not AND).\n    ///\n    /// Assumes that this type O is the same as the O provided to new.\n    fn extra_output_values<O: bytemuck::Pod + Debug + PartialEq>(\n        mut self,\n        output_values: &[O],\n    ) -> Self {\n        self.output_values\n            .push(bytemuck::cast_slice(output_values).to_vec());\n\n        self\n    }\n\n    fn failures(mut self, failures: Backends) -> Self {\n        self.failures = failures;\n\n        self\n    }\n}\n\nconst MAX_BUFFER_SIZE: u64 = 128;\n\n/// Runs the given shader tests with the given storage_type for the input_buffer.\nasync fn shader_input_output_test(\n    ctx: TestingContext,\n    storage_type: InputStorageType,\n    tests: Vec<ShaderTest>,\n) {\n    let source = String::from(include_str!(\"shader_test.wgsl\"));\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        // We don't use this buffer for immediates, but for simplicity\n                        // we just use the storage buffer binding.\n                        ty: match storage_type {\n                            InputStorageType::Uniform => wgpu::BufferBindingType::Uniform,\n                            InputStorageType::Storage | InputStorageType::Immediate => {\n                                wgpu::BufferBindingType::Storage { read_only: true }\n                            }\n                        },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n                BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        ty: wgpu::BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: false,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                },\n            ],\n        });\n\n    let input_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"input buffer\"),\n        size: MAX_BUFFER_SIZE,\n        usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM | BufferUsages::STORAGE,\n        mapped_at_creation: false,\n    });\n\n    let output_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"output buffer\"),\n        size: MAX_BUFFER_SIZE,\n        usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE,\n        mapped_at_creation: false,\n    });\n\n    let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"mapping buffer\"),\n        size: MAX_BUFFER_SIZE,\n        usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bg = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &bgl,\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: input_buffer.as_entire_binding(),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let pll = ctx\n        .device\n        .create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: match storage_type {\n                InputStorageType::Immediate => MAX_BUFFER_SIZE as u32,\n                _ => 0,\n            },\n        });\n\n    let mut fail = false;\n    for test in tests {\n        assert!(test.input_values.len() <= MAX_BUFFER_SIZE as usize / 4);\n        assert!(test.output_values.len() <= MAX_BUFFER_SIZE as usize / 4);\n\n        let test_name = test.name;\n\n        // -- Building shader + pipeline --\n\n        // This isn't terribly efficient but the string is short and it's a test.\n        // The body and input members are the longest part, so do them last.\n        let mut processed = source\n            .replace(\"{{header}}\", &test.header)\n            .replace(\"{{storage_type}}\", storage_type.as_str())\n            .replace(\"{{input_type}}\", &test.input_type)\n            .replace(\"{{output_type}}\", &test.output_type)\n            .replace(\"{{input_members}}\", &test.custom_struct_members)\n            .replace(\"{{body}}\", &test.body);\n\n        // Add the bindings for all inputs besides immediates.\n        processed = if matches!(storage_type, InputStorageType::Immediate) {\n            processed.replace(\"{{input_bindings}}\", \"\")\n        } else {\n            processed.replace(\"{{input_bindings}}\", \"@group(0) @binding(0)\")\n        };\n\n        let sm = ctx.device.create_shader_module(ShaderModuleDescriptor {\n            label: Some(&format!(\"shader {test_name}\")),\n            source: ShaderSource::Wgsl(Cow::Borrowed(&processed)),\n        });\n\n        let pipeline = ctx\n            .device\n            .create_compute_pipeline(&ComputePipelineDescriptor {\n                label: Some(&format!(\"pipeline {test_name}\")),\n                layout: Some(&pll),\n                module: &sm,\n                entry_point: Some(\"cs_main\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        // -- Initializing data --\n\n        let output_pre_init_data = vec![test.output_initialization; MAX_BUFFER_SIZE as usize / 4];\n        ctx.queue.write_buffer(\n            &output_buffer,\n            0,\n            bytemuck::cast_slice(&output_pre_init_data),\n        );\n\n        match storage_type {\n            InputStorageType::Uniform | InputStorageType::Storage => {\n                ctx.queue\n                    .write_buffer(&input_buffer, 0, bytemuck::cast_slice(&test.input_values));\n            }\n            _ => {\n                // Init happens in the compute pass\n            }\n        }\n\n        // -- Run test --\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor { label: None });\n\n        let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor {\n            label: Some(&format!(\"cpass {test_name}\")),\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&pipeline);\n        cpass.set_bind_group(0, &bg, &[]);\n\n        if let InputStorageType::Immediate = storage_type {\n            cpass.set_immediates(0, bytemuck::cast_slice(&test.input_values))\n        }\n\n        cpass.dispatch_workgroups(1, 1, 1);\n        drop(cpass);\n\n        // -- Pulldown data --\n\n        encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, MAX_BUFFER_SIZE);\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n        let mapped = mapping_buffer.slice(..).get_mapped_range();\n\n        let typed: &[u32] = bytemuck::cast_slice(&mapped);\n\n        // -- Check results --\n\n        let failure = !(test.output_comparison_fn)(&test_name, typed, &test.output_values);\n        // We don't immediately panic to let all tests execute\n        if failure\n            != test\n                .failures\n                .contains(ctx.adapter.get_info().backend.into())\n        {\n            fail |= true;\n            if !failure {\n                eprintln!(\"Unexpected test success. Test {test_name}\");\n            }\n        }\n\n        drop(mapped);\n        mapping_buffer.unmap();\n    }\n    assert!(!fail);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/numeric_builtins.rs",
    "content": "use wgpu::{DownlevelFlags, Limits};\n\nuse crate::shader::{shader_input_output_test, InputStorageType, ShaderTest};\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        NUMERIC_BUILTINS,\n        INT64_ATOMIC_MIN_MAX,\n        INT64_ATOMIC_ALL_OPS,\n        FLOAT32_ATOMIC,\n    ]);\n}\n\nfn create_numeric_builtin_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    #[rustfmt::skip]\n    let clamp_values: &[(f32, f32, f32, &[f32])] = &[\n        // value - low - high - valid outputs\n\n        // normal clamps\n        (   20.0,  0.0,  10.0,  &[10.0]),\n        (  -10.0,  0.0,  10.0,  &[0.0]),\n        (    5.0,  0.0,  10.0,  &[5.0]),\n\n        // med-of-three or min/max\n        (    3.0,  2.0,  1.0,   &[1.0, 2.0]),\n    ];\n\n    for &(input, low, high, output) in clamp_values {\n        let mut test = ShaderTest::new(\n            format!(\"clamp({input}, {low}, {high}) == {output:?}\"),\n            String::from(\"value: f32, low: f32, high: f32\"),\n            String::from(\"output[0] = bitcast<u32>(clamp(input.value, input.low, input.high));\"),\n            &[input, low, high],\n            &[output[0]],\n        );\n        for &extra in &output[1..] {\n            test = test.extra_output_values(&[extra]);\n        }\n\n        tests.push(test);\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic NUMERIC_BUILTINS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_numeric_builtin_test(),\n        )\n    });\n\nfn create_int64_atomic_min_max_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let test = ShaderTest::new(\n        \"atomicMax\".into(),\n        \"value: u64\".into(),\n        \"atomicMin(&output, 0lu); atomicMax(&output, 2lu);\".into(),\n        &[0],\n        &[2],\n    )\n    .output_type(\"atomic<u64>\".into());\n\n    tests.push(test);\n\n    let test = ShaderTest::new(\n        \"atomicMin\".into(),\n        \"value: u64\".into(),\n        \"atomicMax(&output, 100lu); atomicMin(&output, 4lu);\".into(),\n        &[0],\n        &[4],\n    )\n    .output_type(\"atomic<u64>\".into());\n\n    tests.push(test);\n\n    tests\n}\n\n#[gpu_test]\nstatic INT64_ATOMIC_MIN_MAX: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::SHADER_INT64 | wgpu::Features::SHADER_INT64_ATOMIC_MIN_MAX)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_int64_atomic_min_max_test(),\n        )\n    });\n\nfn create_int64_atomic_all_ops_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let test = ShaderTest::new(\n        \"atomicAdd\".into(),\n        \"value: u64\".into(),\n        \"atomicStore(&output, 0lu); atomicAdd(&output, 1lu); atomicAdd(&output, 1lu);\".into(),\n        &[0],\n        &[2],\n    )\n    .output_type(\"atomic<u64>\".into());\n\n    tests.push(test);\n\n    let test = ShaderTest::new(\n        \"atomicAnd\".into(),\n        \"value: u64\".into(),\n        \"atomicStore(&output, 31lu); atomicAnd(&output, 30lu); atomicAnd(&output, 3lu);\".into(),\n        &[0],\n        &[2],\n    )\n    .output_type(\"atomic<u64>\".into());\n\n    tests.push(test);\n\n    let test = ShaderTest::new(\n        \"atomicOr\".into(),\n        \"value: u64\".into(),\n        \"atomicStore(&output, 0lu); atomicOr(&output, 3lu); atomicOr(&output, 6lu);\".into(),\n        &[0],\n        &[7],\n    )\n    .output_type(\"atomic<u64>\".into());\n\n    tests.push(test);\n\n    tests\n}\n\n#[gpu_test]\nstatic INT64_ATOMIC_ALL_OPS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::SHADER_INT64 | wgpu::Features::SHADER_INT64_ATOMIC_ALL_OPS)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_int64_atomic_all_ops_test(),\n        )\n    });\n\nfn create_float32_atomic_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let test = ShaderTest::new(\n        \"atomicAdd\".into(),\n        \"value: f32\".into(),\n        \"atomicStore(&output, 0.0); atomicAdd(&output, -0.50); atomicAdd(&output, 1.75);\".into(),\n        &[0_f32],\n        &[1.25_f32],\n    )\n    .output_type(\"atomic<f32>\".into());\n\n    tests.push(test);\n\n    let test = ShaderTest::new(\n        \"atomicAdd\".into(),\n        \"value: f32\".into(),\n        \"atomicStore(&output, 0.0); atomicSub(&output, -2.5); atomicSub(&output, 3.0);\".into(),\n        &[0_f32],\n        &[-0.5_f32],\n    )\n    .output_type(\"atomic<f32>\".into());\n\n    tests.push(test);\n\n    tests\n}\n\n#[gpu_test]\nstatic FLOAT32_ATOMIC: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::SHADER_FLOAT32_ATOMIC)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_float32_atomic_test())\n    });\n\n// See https://github.com/gfx-rs/wgpu/issues/5276\n/*\nfn create_int64_polyfill_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    let u64_clz_values: &[(u64, u32)] = &[\n        (u64::MAX, 0),\n        (1, 63),\n        (2, 62),\n        (3, 62),\n        (1 << 63, 0),\n        (1 << 62, 1),\n        (0, 64),\n    ];\n\n    for &(input, output) in u64_clz_values {\n        let test = ShaderTest::new(\n            format!(\"countLeadingZeros({input}lu) == {output:?}\"),\n            String::from(\"value: u64\"),\n            String::from(\"output[0] = u32(countLeadingZeros(input.value));\"),\n            &[input],\n            &[output],\n        );\n\n        tests.push(test);\n    }\n\n    let i64_clz_values: &[(i64, u32)] = &[\n        (i64::MAX, 1),\n        (i64::MIN, 0),\n        (1, 63),\n        (1 << 62, 1),\n        (-1 << 62, 0),\n        (0, 64),\n        (-1, 0),\n    ];\n\n    for &(input, output) in i64_clz_values {\n        let test = ShaderTest::new(\n            format!(\"countLeadingZeros({input}li) == {output:?}\"),\n            String::from(\"value: i64\"),\n            String::from(\"output[0] = u32(countLeadingZeros(input.value));\"),\n            &[input],\n            &[output],\n        );\n\n        tests.push(test);\n    }\n\n    let u64_flb_values: &[(u64, u32)] = &[\n        (u64::MAX, 63),\n        (1, 0),\n        (2, 1),\n        (3, 1),\n        (1 << 63, 63),\n        (1 << 62, 62),\n        (0, u32::MAX),\n    ];\n\n    for &(input, output) in u64_flb_values {\n        let test = ShaderTest::new(\n            format!(\"firstLeadingBit({input}lu) == {output:?}\"),\n            String::from(\"value: u64\"),\n            String::from(\"output[0] = u32(firstLeadingBit(input.value));\"),\n            &[input],\n            &[output],\n        );\n\n        tests.push(test);\n    }\n\n    let i64_flb_values: &[(i64, u32)] = &[\n        (i64::MAX, 62),\n        (i64::MIN, 62),\n        (1, 0),\n        (1 << 62, 62),\n        (-1 << 62, 61),\n        (0, u32::MAX),\n        (-1, u32::MAX),\n    ];\n\n    for &(input, output) in i64_flb_values {\n        let test = ShaderTest::new(\n            format!(\"firstLeadingBit({input}li) == {output:?}\"),\n            String::from(\"value: i64\"),\n            String::from(\"output[0] = u32(firstLeadingBit(input.value));\"),\n            &[input],\n            &[output],\n        );\n\n        tests.push(test);\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic INT64_POLYFILL: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_INT64)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(ctx, InputStorageType::Storage, create_int64_polyfill_test())\n    });\n*/\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/shader_test.wgsl",
    "content": "{{header}}\n\nstruct CustomStruct {\n    {{input_members}}\n}\n\n{{input_bindings}}\nvar<{{storage_type}}> input: {{input_type}}; \n\n@group(0) @binding(1)\nvar<storage, read_write> output: {{output_type}};\n\n@compute @workgroup_size(1)\nfn cs_main() {\n    {{body}}\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/struct_layout.rs",
    "content": "use std::fmt::Write;\n\nuse wgpu::{Backends, DownlevelFlags, Features, Limits};\n\nuse crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE};\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        UNIFORM_INPUT,\n        STORAGE_INPUT,\n        IMMEDIATES_INPUT,\n        UNIFORM_INPUT_INT64,\n        STORAGE_INPUT_INT64,\n        IMMEDIATES_INPUT_INT64,\n        UNIFORM_INPUT_F16,\n        STORAGE_INPUT_F16,\n    ]);\n}\n\n// Note that some specific subtests are marked as failing on GL due to\n// https://github.com/gfx-rs/wgpu/issues/4371.\n#[gpu_test]\nstatic UNIFORM_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Uniform,\n            create_struct_layout_tests(InputStorageType::Uniform),\n        )\n    });\n\n#[gpu_test]\nstatic STORAGE_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_struct_layout_tests(InputStorageType::Storage),\n        )\n    });\n\n#[gpu_test]\nstatic IMMEDIATES_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::IMMEDIATES)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits {\n                max_immediate_size: MAX_BUFFER_SIZE as u32,\n                ..Limits::downlevel_defaults()\n            }),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Immediate,\n            create_struct_layout_tests(InputStorageType::Immediate),\n        )\n    });\n\nfn create_struct_layout_tests(storage_type: InputStorageType) -> Vec<ShaderTest> {\n    let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect();\n\n    let mut tests = Vec::new();\n\n    // Vector tests\n    for components in [2, 3, 4] {\n        for ty in [\"f32\", \"u32\", \"i32\"] {\n            let input_members = format!(\"member: vec{components}<{ty}>,\");\n            // There's 2 possible ways to load a component of a vector:\n            // - Do `input.member.x` (direct)\n            // - Store `input.member` in a variable; do `var.x` (loaded)\n            let mut direct = String::new();\n            let mut loaded = String::from(\"let loaded = input.member;\");\n            let component_accessors = [\"x\", \"y\", \"z\", \"w\"]\n                .into_iter()\n                .take(components)\n                .enumerate();\n            for (idx, component) in component_accessors {\n                writeln!(\n                    direct,\n                    \"output[{idx}] = bitcast<u32>(input.member.{component});\"\n                )\n                .unwrap();\n                writeln!(loaded, \"output[{idx}] = bitcast<u32>(loaded.{component});\").unwrap();\n            }\n\n            tests.push(ShaderTest::new(\n                format!(\"vec{components}<{ty}> - direct\"),\n                input_members.clone(),\n                direct,\n                &input_values,\n                &(0..components as u32).collect::<Vec<_>>(),\n            ));\n\n            tests.push(ShaderTest::new(\n                format!(\"vec{components}<{ty}> - loaded\"),\n                input_members.clone(),\n                loaded,\n                &input_values,\n                &(0..components as u32).collect::<Vec<_>>(),\n            ));\n        }\n    }\n\n    // Matrix tests\n    for columns in [2, 3, 4] {\n        for rows in [2, 3, 4] {\n            let ty = format!(\"mat{columns}x{rows}<f32>\");\n            let input_members = format!(\"member: {ty},\");\n            // There's 3 possible ways to load a component of a matrix:\n            // - Do `input.member[0].x` (direct)\n            // - Store `input.member[0]` in a variable; do `var.x` (vector_loaded)\n            // - Store `input.member` in a variable; do `var[0].x` (fully_loaded)\n            // For each of these, we can either use a static or dynamic index.\n            let mut direct_static = String::new();\n            let mut direct_dynamic = String::new();\n            let mut vector_loaded_static = String::new();\n            let mut vector_loaded_dynamic = String::new();\n            let mut fully_loaded_static = String::from(\"let loaded = input.member;\");\n            let mut fully_loaded_dynamic = String::from(\"let loaded = input.member;\");\n            let column_index_names = [\"zero\", \"one\", \"two\", \"three\"];\n            for (column, column_str) in column_index_names.iter().enumerate().take(columns) {\n                writeln!(direct_dynamic, \"var {column_str} = {column};\").unwrap();\n                writeln!(vector_loaded_dynamic, \"var {column_str} = {column};\").unwrap();\n                writeln!(fully_loaded_dynamic, \"var {column_str} = {column};\").unwrap();\n\n                writeln!(\n                    vector_loaded_static,\n                    \"let vec_{column} = input.member[{column}];\"\n                )\n                .unwrap();\n                writeln!(\n                    vector_loaded_dynamic,\n                    \"let vec_{column} = input.member[{column_str}];\",\n                )\n                .unwrap();\n            }\n\n            let mut output_values = Vec::new();\n\n            let mut current_output_idx = 0;\n            let mut current_input_idx = 0;\n            for (column, column_str) in column_index_names.iter().enumerate().take(columns) {\n                let component_accessors = [\"x\", \"y\", \"z\", \"w\"].into_iter().take(rows);\n                for component in component_accessors {\n                    writeln!(\n                        direct_static,\n                        \"output[{current_output_idx}] = bitcast<u32>(input.member[{column}].{component});\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        direct_dynamic,\n                        \"output[{current_output_idx}] = bitcast<u32>(input.member[{column_str}].{component});\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        vector_loaded_static,\n                        \"output[{current_output_idx}] = bitcast<u32>(vec_{column}.{component});\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        vector_loaded_dynamic,\n                        \"output[{current_output_idx}] = bitcast<u32>(vec_{column}.{component});\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        fully_loaded_static,\n                        \"output[{current_output_idx}] = bitcast<u32>(loaded[{column}].{component});\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        fully_loaded_dynamic,\n                        \"output[{current_output_idx}] = bitcast<u32>(loaded[{column_str}].{component});\"\n                    )\n                    .unwrap();\n\n                    output_values.push(current_input_idx);\n                    current_input_idx += 1;\n                    current_output_idx += 1;\n                }\n                // Round to next vec4 if we're matrices with vec3 columns\n                if rows == 3 {\n                    current_input_idx += 1;\n                }\n            }\n\n            // https://github.com/gfx-rs/wgpu/issues/4371\n            let failures = if storage_type == InputStorageType::Uniform && rows == 2 {\n                Backends::GL\n            } else {\n                Backends::empty()\n            };\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - direct, static index\"),\n                    input_members.clone(),\n                    direct_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - direct, dynamic index\"),\n                    input_members.clone(),\n                    direct_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - vector loaded, static index\"),\n                    input_members.clone(),\n                    vector_loaded_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - vector loaded, dynamic index\"),\n                    input_members.clone(),\n                    vector_loaded_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - fully loaded, static index\"),\n                    input_members.clone(),\n                    fully_loaded_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - fully loaded, dynamic index\"),\n                    input_members.clone(),\n                    fully_loaded_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n        }\n    }\n\n    // Array of matrix tests\n    for columns in [2, 4] {\n        for rows in [2, 3, 4] {\n            let array_size = 2;\n            let ty = format!(\"mat{columns}x{rows}<f32>\");\n            let input_members = format!(\"members: array<{ty}, {array_size}>\");\n            // There's 4 possible ways to load a component of a matrix in an array:\n            // - Do `input.members[0][0].x` (direct)\n            // - Store `input.members[0][0]` in a variable; do `var.x` (vector_loaded)\n            // - Store `input.members[0]` in a variable; do `var[0].x` (matrix_loaded)\n            // - Store `input.members` in a variable; do `var[0][0].x` (fully_loaded)\n            // For each of these, we can either use a static or dynamic index.\n            let mut direct_static = String::new();\n            let mut direct_dynamic = String::new();\n            let mut vector_loaded_static = String::new();\n            let mut vector_loaded_dynamic = String::new();\n            let mut matrix_loaded_static = String::new();\n            let mut matrix_loaded_dynamic = String::new();\n            let mut fully_loaded_static = String::from(\"let loaded = input.members;\");\n            let mut fully_loaded_dynamic = String::from(\"let loaded = input.members;\");\n            let column_index_names = [\"zero\", \"one\", \"two\", \"three\"];\n            for (column, column_str) in column_index_names.iter().enumerate().take(columns) {\n                writeln!(direct_dynamic, \"var {column_str} = {column};\").unwrap();\n                writeln!(vector_loaded_dynamic, \"var {column_str} = {column};\").unwrap();\n                writeln!(matrix_loaded_dynamic, \"var {column_str} = {column};\").unwrap();\n            }\n            for element in 0..array_size {\n                writeln!(\n                    matrix_loaded_static,\n                    \"let mat_{element} = input.members[{element}];\"\n                )\n                .unwrap();\n                writeln!(\n                    matrix_loaded_dynamic,\n                    \"let mat_{element} = input.members[{element}];\"\n                )\n                .unwrap();\n                for (column, column_str) in column_index_names.iter().enumerate().take(columns) {\n                    writeln!(\n                        vector_loaded_static,\n                        \"let mat_{element}_vec_{column} = input.members[{element}][{column}];\"\n                    )\n                    .unwrap();\n                    writeln!(\n                        vector_loaded_dynamic,\n                        \"let mat_{element}_vec_{column} = input.members[{element}][{column_str}];\",\n                    )\n                    .unwrap();\n                }\n            }\n\n            let mut output_values = Vec::new();\n\n            let mut current_output_idx = 0;\n            let mut current_input_idx = 0;\n            for element in 0..array_size {\n                for (column, column_str) in column_index_names.iter().enumerate().take(columns) {\n                    let component_accessors = [\"x\", \"y\", \"z\", \"w\"].into_iter().take(rows);\n                    for component in component_accessors {\n                        writeln!(\n                            direct_static,\n                            \"output[{current_output_idx}] = bitcast<u32>(input.members[{element}][{column}].{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            direct_dynamic,\n                            \"output[{current_output_idx}] = bitcast<u32>(input.members[{element}][{column_str}].{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            vector_loaded_static,\n                            \"output[{current_output_idx}] = bitcast<u32>(mat_{element}_vec_{column}.{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            vector_loaded_dynamic,\n                            \"output[{current_output_idx}] = bitcast<u32>(mat_{element}_vec_{column}.{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            matrix_loaded_static,\n                            \"output[{current_output_idx}] = bitcast<u32>(mat_{element}[{column}].{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            matrix_loaded_dynamic,\n                            \"output[{current_output_idx}] = bitcast<u32>(mat_{element}[{column_str}].{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            fully_loaded_static,\n                            \"output[{current_output_idx}] = bitcast<u32>(loaded[{column}].{component});\"\n                        )\n                        .unwrap();\n                        writeln!(\n                            fully_loaded_dynamic,\n                            \"output[{current_output_idx}] = bitcast<u32>(loaded[{column_str}].{component});\"\n                        )\n                        .unwrap();\n\n                        output_values.push(current_input_idx);\n                        current_input_idx += 1;\n                        current_output_idx += 1;\n                    }\n                    // Round to next vec4 if we're matrices with vec3 columns\n                    if rows == 3 {\n                        current_input_idx += 1;\n                    }\n                }\n            }\n\n            // https://github.com/gfx-rs/wgpu/issues/4371\n            let failures = if storage_type == InputStorageType::Uniform && rows == 2 {\n                Backends::GL\n            } else {\n                Backends::empty()\n            };\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - direct, static index\"),\n                    input_members.clone(),\n                    direct_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - direct, dynamic index\"),\n                    input_members.clone(),\n                    direct_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - vector loaded, static index\"),\n                    input_members.clone(),\n                    vector_loaded_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - vector loaded, dynamic index\"),\n                    input_members.clone(),\n                    vector_loaded_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - matrix loaded, static index\"),\n                    input_members.clone(),\n                    matrix_loaded_static,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n            tests.push(\n                ShaderTest::new(\n                    format!(\"{ty} - matrix loaded, dynamic index\"),\n                    input_members.clone(),\n                    matrix_loaded_dynamic,\n                    &input_values,\n                    &output_values,\n                )\n                .failures(failures),\n            );\n        }\n    }\n\n    // MatCx2 followed by other members in same struct. Since on some backends\n    // the matrix will be decomposed into separate column members in the struct,\n    // this tests that the other members can still be accessed correctly. This\n    // is especially important on SPIR-V where members are accessed by index\n    // rather than name.\n    {\n        let members = String::from(\"m: mat3x2<f32>,\\nf: f32,\");\n        let direct = String::from(\n            \"\\\n            output[0] = bitcast<u32>(input.m[0].x);\n            output[1] = bitcast<u32>(input.m[0].y);\n            output[2] = bitcast<u32>(input.m[1].x);\n            output[3] = bitcast<u32>(input.m[1].y);\n            output[4] = bitcast<u32>(input.m[2].x);\n            output[5] = bitcast<u32>(input.m[2].y);\n            output[6] = bitcast<u32>(input.f);\n        \",\n        );\n        tests.push(\n            ShaderTest::new(\n                String::from(\"MatCx2 followed by other members\"),\n                members,\n                direct,\n                &input_values,\n                &[0, 1, 2, 3, 4, 5, 6],\n            )\n            // https://github.com/gfx-rs/wgpu/issues/4371\n            .failures(if storage_type == InputStorageType::Uniform {\n                Backends::GL\n            } else {\n                Backends::empty()\n            }),\n        );\n    }\n\n    // Vec3 alignment tests\n    for ty in [\"f32\", \"u32\", \"i32\"] {\n        let members = format!(\"_vec: vec3<{ty}>,\\nscalar: {ty},\");\n        let direct = String::from(\"output[0] = bitcast<u32>(input.scalar);\");\n\n        tests.push(ShaderTest::new(\n            format!(\"vec3<{ty}>, {ty} alignment\"),\n            members,\n            direct,\n            &input_values,\n            &[3],\n        ));\n    }\n\n    // Test for https://github.com/gfx-rs/wgpu/issues/5262.\n    //\n    // The struct is supposed to have a size of 32 and alignment of 16.\n    for ty in [\"f32\", \"u32\", \"i32\"] {\n        let header = format!(\"struct Inner {{ vec: vec3<{ty}>, scalar1: u32, scalar2: u32 }}\");\n        let members = String::from(\"arr: array<Inner, 2>\");\n        let direct = String::from(\n            \"\\\n            output[0] = bitcast<u32>(input.arr[0].vec.x);\n            output[1] = bitcast<u32>(input.arr[0].vec.y);\n            output[2] = bitcast<u32>(input.arr[0].vec.z);\n            output[3] = bitcast<u32>(input.arr[0].scalar1);\n            output[4] = bitcast<u32>(input.arr[0].scalar2);\n            output[5] = bitcast<u32>(input.arr[1].vec.x);\n            output[6] = bitcast<u32>(input.arr[1].vec.y);\n            output[7] = bitcast<u32>(input.arr[1].vec.z);\n            output[8] = bitcast<u32>(input.arr[1].scalar1);\n            output[9] = bitcast<u32>(input.arr[1].scalar2);\n        \",\n        );\n\n        tests.push(\n            ShaderTest::new(\n                format!(\"Alignment of 24 byte struct with a vec3<{ty}>\"),\n                members,\n                direct,\n                &input_values,\n                &[0, 1, 2, 3, 4, 8, 9, 10, 11, 12],\n            )\n            .header(header),\n        );\n    }\n\n    // Mat3 alignment tests\n    for ty in [\"f32\", \"u32\", \"i32\"] {\n        for columns in [2, 3, 4] {\n            let members = format!(\"_mat: mat{columns}x3<f32>,\\nscalar: {ty},\");\n            let direct = String::from(\"output[0] = bitcast<u32>(input.scalar);\");\n\n            tests.push(ShaderTest::new(\n                format!(\"mat{columns}x3<f32>, {ty} alignment\"),\n                members,\n                direct,\n                &input_values,\n                &[columns * 4],\n            ));\n        }\n    }\n\n    // Nested struct and array test.\n    //\n    // This tries to exploit all the weird edge cases of the struct layout algorithm.\n    {\n        let header =\n            String::from(\"struct Inner { scalar: f32, member: array<vec3<f32>, 2>, scalar2: f32 }\");\n        let members = String::from(\"inner: Inner, scalar3: f32, vector: vec3<f32>, scalar4: f32\");\n        let direct = String::from(\n            \"\\\n            output[0] = bitcast<u32>(input.inner.scalar);\n            output[1] = bitcast<u32>(input.inner.member[0].x);\n            output[2] = bitcast<u32>(input.inner.member[0].y);\n            output[3] = bitcast<u32>(input.inner.member[0].z);\n            output[4] = bitcast<u32>(input.inner.member[1].x);\n            output[5] = bitcast<u32>(input.inner.member[1].y);\n            output[6] = bitcast<u32>(input.inner.member[1].z);\n            output[7] = bitcast<u32>(input.inner.scalar2);\n            output[8] = bitcast<u32>(input.scalar3);\n            output[9] = bitcast<u32>(input.vector.x);\n            output[10] = bitcast<u32>(input.vector.y);\n            output[11] = bitcast<u32>(input.vector.z);\n            output[12] = bitcast<u32>(input.scalar4);\n        \",\n        );\n\n        tests.push(\n            ShaderTest::new(\n                String::from(\"nested struct and array\"),\n                members,\n                direct,\n                &input_values,\n                &[\n                    0, // inner.scalar\n                    4, 5, 6, // inner.member[0]\n                    8, 9, 10, // inner.member[1]\n                    12, // scalar2\n                    16, // scalar3\n                    20, 21, 22, // vector\n                    23, // scalar4\n                ],\n            )\n            .header(header),\n        );\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic UNIFORM_INPUT_INT64: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_INT64)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_64bit_struct_layout_tests(),\n        )\n    });\n\n#[gpu_test]\nstatic STORAGE_INPUT_INT64: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_INT64)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_64bit_struct_layout_tests(),\n        )\n    });\n\n#[gpu_test]\nstatic IMMEDIATES_INPUT_INT64: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_INT64 | Features::IMMEDIATES)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits {\n                max_immediate_size: MAX_BUFFER_SIZE as u32,\n                ..Limits::downlevel_defaults()\n            }),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Immediate,\n            create_64bit_struct_layout_tests(),\n        )\n    });\n\nfn create_64bit_struct_layout_tests() -> Vec<ShaderTest> {\n    let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect();\n\n    let mut tests = Vec::new();\n\n    // 64 bit alignment tests\n    for ty in [\"u64\", \"i64\"] {\n        let members = format!(\"scalar: {ty},\");\n        let direct = String::from(\n            \"\\\n            output[0] = u32(bitcast<u64>(input.scalar) & 0xFFFFFFFF);\n            output[1] = u32((bitcast<u64>(input.scalar) >> 32) & 0xFFFFFFFF);\n        \",\n        );\n\n        tests.push(ShaderTest::new(\n            format!(\"{ty} alignment\"),\n            members,\n            direct,\n            &input_values,\n            &[0, 1],\n        ));\n    }\n\n    // Nested struct and array test.\n    //\n    // This tries to exploit all the weird edge cases of the struct layout algorithm.\n    // We dont go as all-out as the other nested struct test because\n    // all our primitives are twice as wide and we have only so much buffer to spare.\n    {\n        let header = String::from(\n            \"struct Inner { scalar: u64, scalar32: u32, member: array<vec3<u64>, 2> }\",\n        );\n        let members = String::from(\"inner: Inner\");\n        let direct = String::from(\n            \"\\\n            output[0] = u32(bitcast<u64>(input.inner.scalar) & 0xFFFFFFFF);\n            output[1] = u32((bitcast<u64>(input.inner.scalar) >> 32) & 0xFFFFFFFF);\n            output[2] = bitcast<u32>(input.inner.scalar32);\n            for (var index = 0u; index < 2u; index += 1u) {\n                for (var component = 0u; component < 3u; component += 1u) {\n                    output[3 + index * 6 + component * 2] = u32(bitcast<u64>(input.inner.member[index][component]) & 0xFFFFFFFF);\n                    output[4 + index * 6 + component * 2] = u32((bitcast<u64>(input.inner.member[index][component]) >> 32) & 0xFFFFFFFF);\n                }\n            }\n        \",\n        );\n\n        tests.push(\n            ShaderTest::new(\n                String::from(\"nested struct and array\"),\n                members,\n                direct,\n                &input_values,\n                &[\n                    0, 1, // inner.scalar\n                    2, // inner.scalar32\n                    8, 9, 10, 11, 12, 13, // inner.member[0]\n                    16, 17, 18, 19, 20, 21, // inner.member[1]\n                ],\n            )\n            .header(header),\n        );\n    }\n    {\n        let header = String::from(\"struct Inner { scalar32: u32, scalar: u64, scalar32_2: u32 }\");\n        let members = String::from(\"inner: Inner, vector: vec3<i64>\");\n        let direct = String::from(\n            \"\\\n            output[0] = bitcast<u32>(input.inner.scalar32);\n            output[1] = u32(bitcast<u64>(input.inner.scalar) & 0xFFFFFFFF);\n            output[2] = u32((bitcast<u64>(input.inner.scalar) >> 32) & 0xFFFFFFFF);\n            output[3] = bitcast<u32>(input.inner.scalar32_2);\n            output[4] = u32(bitcast<u64>(input.vector.x) & 0xFFFFFFFF);\n            output[5] = u32((bitcast<u64>(input.vector.x) >> 32) & 0xFFFFFFFF);\n            output[6] = u32(bitcast<u64>(input.vector.y) & 0xFFFFFFFF);\n            output[7] = u32((bitcast<u64>(input.vector.y) >> 32) & 0xFFFFFFFF);\n            output[8] = u32(bitcast<u64>(input.vector.z) & 0xFFFFFFFF);\n            output[9] = u32((bitcast<u64>(input.vector.z) >> 32) & 0xFFFFFFFF);\n        \",\n        );\n\n        tests.push(\n            ShaderTest::new(\n                String::from(\"nested struct and array\"),\n                members,\n                direct,\n                &input_values,\n                &[\n                    0, // inner.scalar32\n                    2, 3, // inner.scalar\n                    4, // inner.scalar32_2\n                    8, 9, 10, 11, 12, 13, // vector\n                ],\n            )\n            .header(header),\n        );\n    }\n\n    tests\n}\n\n#[gpu_test]\nstatic UNIFORM_INPUT_F16: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_F16)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_16bit_struct_layout_test(),\n        )\n    });\n\n#[gpu_test]\nstatic STORAGE_INPUT_F16: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(Features::SHADER_F16)\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| {\n        shader_input_output_test(\n            ctx,\n            InputStorageType::Storage,\n            create_16bit_struct_layout_test(),\n        )\n    });\n\nfn create_16bit_struct_layout_test() -> Vec<ShaderTest> {\n    let mut tests = Vec::new();\n\n    fn f16asu16(f32: f32) -> u16 {\n        half::f16::from_f32(f32).to_bits()\n    }\n\n    // 16 bit alignment tests\n    {\n        let members =\n            \"scalar1: f16, scalar2: f16, v3: vec3<f16>, tuck_in: f16, scalar4: f16, larger: u32\";\n        let direct = String::from(\n            \"\\\n            output[0] = u32(input.scalar1);\n            output[1] = u32(input.scalar2);\n            output[2] = u32(input.v3.x);\n            output[3] = u32(input.v3.y);\n            output[4] = u32(input.v3.z);\n            output[5] = u32(input.tuck_in);\n            output[6] = u32(input.scalar4);\n            output[7] = u32(extractBits(input.larger, 0u, 16u));\n            output[8] = u32(extractBits(input.larger, 16u, 16u));\n        \",\n        );\n\n        tests.push(ShaderTest::new(\n            \"f16 alignment\".into(),\n            members.into(),\n            direct,\n            &[\n                f16asu16(0.0),\n                f16asu16(1.0),\n                f16asu16(2.0),\n                f16asu16(3.0),\n                f16asu16(4.0),\n                f16asu16(5.0),\n                f16asu16(6.0),\n                f16asu16(7.0),\n                f16asu16(8.0),\n                f16asu16(9.0),\n                10_u16,\n                11_u16,\n                // Some extra values to help debug if the test fails.\n                12_u16,\n                13_u16,\n                14_u16,\n                15_u16,\n                16_u16,\n                17_u16,\n                18_u16,\n                19_u16,\n                20_u16,\n            ],\n            &[\n                0, // scalar1\n                1, // scalar2\n                4, 5, 6,  // v3\n                7,  // tuck_in\n                8,  // scalar4\n                10, // larger[0..16]\n                11, // larger[16..32]\n            ],\n        ));\n    }\n\n    // Matrix tests\n    {\n        let members = \"m2: mat2x2h, m3: mat3x3h, m4: mat4x4h\";\n        let direct = String::from(\n            \"\\\n            output[0] = u32(input.m2[0].x);\n            output[1] = u32(input.m2[0].y);\n            output[2] = u32(input.m2[1].x);\n            output[3] = u32(input.m2[1].y);\n\n            output[4] = u32(input.m3[0].x);\n            output[5] = u32(input.m3[0].y);\n            output[6] = u32(input.m3[0].z);\n            output[7] = u32(input.m3[1].x);\n            output[8] = u32(input.m3[1].y);\n            output[9] = u32(input.m3[1].z);\n            output[10] = u32(input.m3[2].x);\n            output[11] = u32(input.m3[2].y);\n            output[12] = u32(input.m3[2].z);\n\n            output[13] = u32(input.m4[0].x);\n            output[14] = u32(input.m4[0].y);\n            output[15] = u32(input.m4[0].z);\n            output[16] = u32(input.m4[0].w);\n            output[17] = u32(input.m4[1].x);\n            output[18] = u32(input.m4[1].y);\n            output[19] = u32(input.m4[1].z);\n            output[20] = u32(input.m4[1].w);\n            output[21] = u32(input.m4[2].x);\n            output[22] = u32(input.m4[2].y);\n            output[23] = u32(input.m4[2].z);\n            output[24] = u32(input.m4[2].w);\n            output[25] = u32(input.m4[3].x);\n            output[26] = u32(input.m4[3].y);\n            output[27] = u32(input.m4[3].z);\n            output[28] = u32(input.m4[3].w);\n        \",\n        );\n\n        tests.push(ShaderTest::new(\n            \"f16 matrix alignment\".into(),\n            members.into(),\n            direct,\n            &(0..32).map(|x| f16asu16(x as f32)).collect::<Vec<_>>(),\n            &[\n                0, 1, // m2[0]\n                2, 3, // m2[1]\n                //\n                4, 5, 6, // m3[0]\n                8, 9, 10, // m3[1]\n                12, 13, 14, // m3[2]\n                //\n                16, 17, 18, 19, // m4[0]\n                20, 21, 22, 23, // m4[1]\n                24, 25, 26, 27, // m4[2]\n                28, 29, 30, 31, // m4[3]\n            ],\n        ));\n    }\n\n    // Insert `enable f16;` header\n    tests\n        .into_iter()\n        .map(|test| test.header(\"enable f16;\".into()))\n        .collect()\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/workgroup_size_overrides.rs",
    "content": "use std::mem::size_of_val;\nuse wgpu::util::DeviceExt;\nuse wgpu::{BufferDescriptor, BufferUsages, MapMode, PollType};\nuse wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(WORKGROUP_SIZE_OVERRIDES);\n}\n\nconst SHADER: &str = r#\"\n    override n = 3;\n\n    @group(0) @binding(0)\n    var<storage, read_write> output: array<u32>;\n\n    @compute @workgroup_size(n - 2)\n    fn main(@builtin(local_invocation_index) lii: u32) {\n        output[lii] = lii + 2;\n    }\n\"#;\n\n#[gpu_test]\nstatic WORKGROUP_SIZE_OVERRIDES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().limits(wgpu::Limits::default()))\n    .run_async(move |ctx| async move {\n        workgroup_size_overrides(&ctx, None, &[2, 0, 0], false).await;\n        workgroup_size_overrides(&ctx, Some(4), &[2, 3, 0], false).await;\n        workgroup_size_overrides(&ctx, Some(1), &[0, 0, 0], true).await;\n    });\n\nasync fn workgroup_size_overrides(\n    ctx: &TestingContext,\n    n: Option<u32>,\n    out: &[u32],\n    should_fail: bool,\n) {\n    let module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(SHADER)),\n        });\n    let pipeline_options = wgpu::PipelineCompilationOptions {\n        constants: &[(\"n\", f64::from(n.unwrap_or(0)))],\n        ..Default::default()\n    };\n    let compute_pipeline = fail_if(\n        &ctx.device,\n        should_fail,\n        || {\n            ctx.device\n                .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n                    label: None,\n                    layout: None,\n                    module: &module,\n                    entry_point: Some(\"main\"),\n                    compilation_options: if n.is_some() {\n                        pipeline_options\n                    } else {\n                        wgpu::PipelineCompilationOptions::default()\n                    },\n                    cache: None,\n                })\n        },\n        None,\n    );\n    if should_fail {\n        return;\n    }\n    let init: &[u32] = &[0, 0, 0];\n    let init_size: u64 = size_of_val(init).try_into().unwrap();\n    let buffer = DeviceExt::create_buffer_init(\n        &ctx.device,\n        &wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(init),\n            usage: wgpu::BufferUsages::STORAGE\n                | wgpu::BufferUsages::COPY_DST\n                | wgpu::BufferUsages::COPY_SRC,\n        },\n    );\n    let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: Some(\"mapping buffer\"),\n        size: init_size,\n        usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    {\n        let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: None,\n            timestamp_writes: None,\n        });\n        cpass.set_pipeline(&compute_pipeline);\n        let bind_group_layout = compute_pipeline.get_bind_group_layout(0);\n        let bind_group_entries = [wgpu::BindGroupEntry {\n            binding: 0,\n            resource: buffer.as_entire_binding(),\n        }];\n        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &bind_group_entries,\n        });\n        cpass.set_bind_group(0, &bind_group, &[]);\n        cpass.dispatch_workgroups(1, 1, 1);\n    }\n    encoder.copy_buffer_to_buffer(&buffer, 0, &mapping_buffer, 0, init_size);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());\n    ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n    let mapped = mapping_buffer.slice(..).get_mapped_range();\n\n    let typed: &[u32] = bytemuck::cast_slice(&mapped);\n    assert_eq!(typed, out);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.rs",
    "content": "use std::num::NonZeroU64;\n\nuse wgpu::{\n    include_wgsl, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,\n    BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, BufferBindingType,\n    BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor,\n    ComputePipelineDescriptor, DownlevelFlags, Limits, MapMode, PipelineLayoutDescriptor, PollType,\n    ShaderStages,\n};\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(ZERO_INIT_WORKGROUP_MEMORY);\n}\n\n#[gpu_test]\nstatic ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)\n            .limits(Limits::downlevel_defaults())\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"Shader library compile failed\")\n                    .validation_error(\"could not be compiled into pipeline\")\n                    .panic(\"Unexpected Vulkan error: ERROR_INITIALIZATION_FAILED\"),\n            ),\n    )\n    .run_async(|ctx| async move {\n        let bgl = ctx\n            .device\n            .create_bind_group_layout(&BindGroupLayoutDescriptor {\n                label: None,\n                entries: &[BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::COMPUTE,\n                    ty: BindingType::Buffer {\n                        ty: BufferBindingType::Storage { read_only: false },\n                        has_dynamic_offset: true,\n                        min_binding_size: None,\n                    },\n                    count: None,\n                }],\n            });\n\n        let output_buffer = ctx.device.create_buffer(&BufferDescriptor {\n            label: Some(\"output buffer\"),\n            size: BUFFER_SIZE,\n            usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE,\n            mapped_at_creation: false,\n        });\n\n        let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {\n            label: Some(\"mapping buffer\"),\n            size: BUFFER_SIZE,\n            usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let bg = ctx.device.create_bind_group(&BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::Buffer(BufferBinding {\n                    buffer: &output_buffer,\n                    offset: 0,\n                    size: Some(NonZeroU64::new(BUFFER_BINDING_SIZE as u64).unwrap()),\n                }),\n            }],\n        });\n\n        let pll = ctx\n            .device\n            .create_pipeline_layout(&PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[Some(&bgl)],\n                immediate_size: 0,\n            });\n\n        let sm = ctx\n            .device\n            .create_shader_module(include_wgsl!(\"zero_init_workgroup_mem.wgsl\"));\n\n        let pipeline_read = ctx\n            .device\n            .create_compute_pipeline(&ComputePipelineDescriptor {\n                label: Some(\"pipeline read\"),\n                layout: Some(&pll),\n                module: &sm,\n                entry_point: Some(\"read\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        let pipeline_write = ctx\n            .device\n            .create_compute_pipeline(&ComputePipelineDescriptor {\n                label: Some(\"pipeline write\"),\n                layout: None,\n                module: &sm,\n                entry_point: Some(\"write\"),\n                compilation_options: Default::default(),\n                cache: None,\n            });\n\n        // -- Initializing data --\n\n        let output_pre_init_data = vec![1; OUTPUT_ARRAY_SIZE as usize];\n        ctx.queue.write_buffer(\n            &output_buffer,\n            0,\n            bytemuck::cast_slice(&output_pre_init_data),\n        );\n\n        // -- Run test --\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor::default());\n\n        let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor::default());\n\n        cpass.set_pipeline(&pipeline_write);\n        for _ in 0..NR_OF_DISPATCHES {\n            cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);\n        }\n\n        cpass.set_pipeline(&pipeline_read);\n        for i in 0..NR_OF_DISPATCHES {\n            cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]);\n            cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);\n        }\n        drop(cpass);\n\n        // -- Pulldown data --\n\n        encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, BUFFER_SIZE);\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n\n        let mapped = mapping_buffer.slice(..).get_mapped_range();\n\n        let typed: &[u32] = bytemuck::cast_slice(&mapped);\n\n        // -- Check results --\n\n        let num_disptaches_failed = typed.iter().filter(|&&res| res != 0).count();\n        let ratio = (num_disptaches_failed as f32 / OUTPUT_ARRAY_SIZE as f32) * 100.;\n\n        assert!(\n            num_disptaches_failed == 0,\n            \"Zero-initialization of workgroup memory failed ({ratio:.0}% of disptaches failed).\"\n        );\n\n        drop(mapped);\n        mapping_buffer.unmap();\n    });\n\nconst DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64);\nconst TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2;\n\n/// nr of bytes we use in the shader\nconst SHADER_WORKGROUP_MEMORY: u32 = 512 * 4 + 4;\n// assume we have this much workgroup memory (2GB)\nconst MAX_DEVICE_WORKGROUP_MEMORY: u32 = i32::MAX as u32;\nconst NR_OF_DISPATCHES: u32 =\n    MAX_DEVICE_WORKGROUP_MEMORY / (SHADER_WORKGROUP_MEMORY * TOTAL_WORK_GROUPS) + 1; // TODO: use div_ceil once stabilized\n\nconst OUTPUT_ARRAY_SIZE: u32 = TOTAL_WORK_GROUPS * NR_OF_DISPATCHES;\nconst BUFFER_SIZE: u64 = OUTPUT_ARRAY_SIZE as u64 * 4;\nconst BUFFER_BINDING_SIZE: u32 = TOTAL_WORK_GROUPS * 4;\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.wgsl",
    "content": "const array_size = 512u;\n\nstruct WStruct {\n    arr: array<u32, array_size>,\n    atom: atomic<u32>\n}\n\nvar<workgroup> w_mem: WStruct;\n\n@group(0) @binding(0)\nvar<storage, read_write> output: array<u32>;\n\n@compute @workgroup_size(1)\nfn read(@builtin(workgroup_id) wgid: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {\n    var is_zero = true;\n    for(var i = 0u; i < array_size; i++) {\n        is_zero &= w_mem.arr[i] == 0u;\n    }\n    is_zero &= atomicLoad(&w_mem.atom) == 0u;\n\n    let idx = wgid.x + (wgid.y * num_workgroups.x) + (wgid.z * num_workgroups.x * num_workgroups.y);\n    output[idx] = u32(!is_zero);\n}\n\n@compute @workgroup_size(1)\nfn write() {\n    for(var i = 0u; i < array_size; i++) {\n        w_mem.arr[i] = i;\n    }\n    atomicStore(&w_mem.atom, 3u);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_barycentric/barycentric.wgsl",
    "content": "@vertex\nfn vs_main(@location(0) xy: vec2<f32>) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(xy, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main(@builtin(barycentric) bary: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4<f32>(bary * 1.1 - 0.05, 1.0);\n}\n\n@fragment\nfn fs_main_no_perspective(@builtin(barycentric_no_perspective) bary: vec3<f32>) -> @location(0) vec4<f32> {\n    return vec4<f32>(bary * 1.1 - 0.05, 1.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_barycentric/mod.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(BARYCENTRIC);\n    vec.push(BARYCENTRIC_NO_PERSPECTIVE);\n}\n\n//\n// This test renders one triangle to a 2x2 render target. The triangle\n// covers the bottom-left, bottom-right, and the top-left pixel.\n// XY layout of the render target, with the triangle:\n//\n//     (-1,1)  (0,1)  (1,1)\n//        +------+------+\n//        |      |      |\n//        |   o  |      |\n//        |   |\\ |      |\n//        |   | \\|      |\n// (-1,0) +---|--\\------+ (1,0)\n//        |   |  |\\     |\n//        |   |  | \\    |\n//        |   o--+--o   |\n//        |      |      |\n//        +------+------+\n//     (-1,-1) (0,-1) (1,-1)\n//\n// The fragment shader outputs color based on builtin(barycentric):\n//\n//     return vec4<f32>(bary * 1.1 - 0.05, 1.0);\n//\n\n#[gpu_test]\nstatic BARYCENTRIC: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::SHADER_BARYCENTRICS),\n    )\n    .run_async(|ctx| barycentric(ctx, false));\n\n#[gpu_test]\nstatic BARYCENTRIC_NO_PERSPECTIVE: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::SHADER_BARYCENTRICS),\n    )\n    .run_async(|ctx| barycentric(ctx, true));\n\nasync fn barycentric(ctx: TestingContext, no_perspective: bool) {\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"barycentric.wgsl\"));\n\n    let n = -0.505;\n    let p = 0.51;\n    let triangle_xy: [f32; 6] = [n, n, p, n, n, p];\n    let vertex_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&triangle_xy),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let indices = [0u32, 1, 2];\n    let index_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&indices),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: 8,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &[wgpu::VertexAttribute {\n                        format: wgpu::VertexFormat::Float32x2,\n                        offset: 0,\n                        shader_location: 0,\n                    }],\n                }],\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: if no_perspective {\n                    Some(\"fs_main_no_perspective\")\n                } else {\n                    Some(\"fs_main\")\n                },\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let width = 2;\n    let height = 2;\n    let texture_size = wgpu::Extent3d {\n        width,\n        height,\n        depth_or_array_layers: 1,\n    };\n    let color_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: texture_size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = wgpu_test::image::ReadbackBuffers::new(&ctx.device, &color_texture);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),\n                    store: wgpu::StoreOp::Store,\n                },\n                resolve_target: None,\n                view: &color_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        rpass.draw(0..3, 0..1);\n    }\n    readback_buffer.copy_from(&ctx.device, &mut encoder, &color_texture);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    //\n    //   +-----+-----+\n    //   |blue |white|\n    //   +-----+-----+\n    //   | red |green|\n    //   +-----+-----+\n    //\n    let expected = [\n        0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0, 255, 0, 255,\n    ];\n    readback_buffer\n        .assert_buffer_contents(&ctx, &expected)\n        .await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_primitive_index/mod.rs",
    "content": "use wgpu::util::DeviceExt;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.extend([DRAW, DRAW_INDEXED]);\n}\n\n//\n// These tests render two triangles to a 2x2 render target. The first triangle\n// in the vertex buffer covers the bottom-left pixel, the second triangle\n// covers the top-right pixel.\n// XY layout of the render target, with two triangles:\n//\n//     (-1,1)   (0,1)   (1,1)\n//        +-------+-------+\n//        |       |   o   |\n//        |       |  / \\  |\n//        |       | /   \\ |\n//        |       |o-----o|\n// (-1,0) +-------+-------+ (1,0)\n//        |   o   |       |\n//        |  / \\  |       |\n//        | /   \\ |       |\n//        |o-----o|       |\n//        +-------+-------+\n//     (-1,-1)  (0,-1)  (1,-1)\n//\n//\n// The fragment shader outputs color based on builtin(primitive_index):\n//\n//         if ((index % 2u) == 0u) {\n//             return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n//         } else {\n//             return vec4<f32>(0.0, 0.0, 1.0, 1.0);\n//         }\n//\n// draw() renders directly from the vertex buffer: the first (bottom-left)\n// triangle is colored red, the other one (top-right) will be blue.\n// draw_indexed() draws the triangles in the opposite order, using index\n// buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors.\n//\n\n#[gpu_test]\nstatic DRAW: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::PRIMITIVE_INDEX),\n    )\n    .run_async(|ctx| async move {\n        //\n        //   +-----+-----+\n        //   |white|blue |\n        //   +-----+-----+\n        //   | red |white|\n        //   +-----+-----+\n        //\n        let expected = [\n            255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255,\n        ];\n        pulling_common(ctx, &expected, |rpass| {\n            rpass.draw(0..6, 0..1);\n        })\n        .await;\n    });\n\n#[gpu_test]\nstatic DRAW_INDEXED: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::PRIMITIVE_INDEX),\n    )\n    .run_async(|ctx| async move {\n        //\n        //   +-----+-----+\n        //   |white| red |\n        //   +-----+-----+\n        //   |blue |white|\n        //   +-----+-----+\n        //\n        let expected = [\n            255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255,\n        ];\n        pulling_common(ctx, &expected, |rpass| {\n            rpass.draw_indexed(0..6, 0, 0..1);\n        })\n        .await;\n    });\n\nasync fn pulling_common(\n    ctx: TestingContext,\n    expected: &[u8],\n    draw_command: impl FnOnce(&mut wgpu::RenderPass<'_>),\n) {\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"primitive_index.wgsl\"));\n\n    let two_triangles_xy: [f32; 12] = [\n        -1.0, -1.0, 0.0, -1.0, -0.5, 0.0, // left triangle, negative x, negative y\n        0.0, 0.0, 1.0, 0.0, 0.5, 1.0, // right triangle, positive x, positive y\n    ];\n    let vertex_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&two_triangles_xy),\n            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let indices = [3u32, 4, 5, 0, 1, 2]; // index buffer flips triangle order\n    let index_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(&indices),\n            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,\n        });\n\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: 8,\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: &[wgpu::VertexAttribute {\n                        format: wgpu::VertexFormat::Float32x2,\n                        offset: 0,\n                        shader_location: 0,\n                    }],\n                }],\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        });\n\n    let width = 2;\n    let height = 2;\n    let texture_size = wgpu::Extent3d {\n        width,\n        height,\n        depth_or_array_layers: 1,\n    };\n    let color_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: texture_size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = wgpu_test::image::ReadbackBuffers::new(&ctx.device, &color_texture);\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),\n                    store: wgpu::StoreOp::Store,\n                },\n                resolve_target: None,\n                view: &color_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_pipeline(&pipeline);\n        rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);\n        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));\n        draw_command(&mut rpass);\n    }\n    readback_buffer.copy_from(&ctx.device, &mut encoder, &color_texture);\n    ctx.queue.submit(Some(encoder.finish()));\n    readback_buffer.assert_buffer_contents(&ctx, expected).await;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_primitive_index/primitive_index.wgsl",
    "content": "enable primitive_index;\n\n@vertex\nfn vs_main(@location(0) xy: vec2<f32>) -> @builtin(position) vec4<f32> {\n    return vec4<f32>(xy, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main(@builtin(primitive_index) index: u32) -> @location(0) vec4<f32> {\n    if ((index % 2u) == 0u) {\n        return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n    } else {\n        return vec4<f32>(0.0, 0.0, 1.0, 1.0);\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_view_format/mod.rs",
    "content": "use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat};\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(REINTERPRET_SRGB);\n}\n\n#[gpu_test]\nstatic REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::VIEW_FORMATS)\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|ctx| async move {\n        let unorm_data: [[u8; 4]; 4] = [\n            [180, 0, 0, 255],\n            [0, 84, 0, 127],\n            [0, 0, 62, 100],\n            [62, 180, 84, 90],\n        ];\n        let srgb_data: [[u8; 4]; 4] = [\n            [116, 0, 0, 255],\n            [0, 23, 0, 127],\n            [0, 0, 12, 100],\n            [12, 116, 23, 90],\n        ];\n\n        let size = wgpu::Extent3d {\n            width: 2,\n            height: 2,\n            depth_or_array_layers: 1,\n        };\n\n        let shader = ctx\n            .device\n            .create_shader_module(wgpu::include_wgsl!(\"view_format.wgsl\"));\n\n        // Reinterpret Rgba8Unorm as Rgba8UnormSrgb\n        reinterpret(\n            &ctx,\n            &shader,\n            size,\n            TextureFormat::Rgba8Unorm,\n            TextureFormat::Rgba8UnormSrgb,\n            &unorm_data,\n            &srgb_data,\n        )\n        .await;\n\n        // Reinterpret Rgba8UnormSrgb back to Rgba8Unorm\n        reinterpret(\n            &ctx,\n            &shader,\n            size,\n            TextureFormat::Rgba8UnormSrgb,\n            TextureFormat::Rgba8Unorm,\n            &srgb_data,\n            &unorm_data,\n        )\n        .await;\n    });\n\nasync fn reinterpret(\n    ctx: &TestingContext,\n    shader: &wgpu::ShaderModule,\n    size: wgpu::Extent3d,\n    src_format: wgpu::TextureFormat,\n    reinterpret_to: wgpu::TextureFormat,\n    src_data: &[[u8; 4]],\n    expect_data: &[[u8; 4]],\n) {\n    let tex = ctx.device.create_texture_with_data(\n        &ctx.queue,\n        &wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: src_format,\n            usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[reinterpret_to],\n        },\n        wgpu::util::TextureDataOrder::LayerMajor,\n        bytemuck::cast_slice(src_data),\n    );\n    let tv = tex.create_view(&wgpu::TextureViewDescriptor {\n        format: Some(reinterpret_to),\n        ..Default::default()\n    });\n    let pipeline = ctx\n        .device\n        .create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"reinterpret pipeline\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: shader,\n                entry_point: Some(\"vs_main\"),\n\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(src_format.into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                front_face: wgpu::FrontFace::Cw,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n    let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[wgpu::BindGroupEntry {\n            binding: 0,\n            resource: wgpu::BindingResource::TextureView(&tv),\n        }],\n        label: None,\n    });\n\n    let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: src_format,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n            ops: wgpu::Operations::default(),\n            resolve_target: None,\n            view: &target_view,\n            depth_slice: None,\n        })],\n        depth_stencil_attachment: None,\n        timestamp_writes: None,\n        occlusion_query_set: None,\n        multiview_mask: None,\n    });\n    rpass.set_pipeline(&pipeline);\n    rpass.set_bind_group(0, &bind_group, &[]);\n    rpass.draw(0..3, 0..1);\n    drop(rpass);\n    ctx.queue.submit(Some(encoder.finish()));\n\n    let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as u64 * 2,\n        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &target_tex,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &read_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT),\n                rows_per_image: None,\n            },\n        },\n        size,\n    );\n    ctx.queue.submit(Some(encoder.finish()));\n\n    let slice = read_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data: Vec<u8> = slice.get_mapped_range().to_vec();\n    let tolerance_data: [[u8; 4]; 4] = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0]];\n\n    for h in 0..size.height {\n        let offset = h * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;\n        for w in 0..size.width {\n            let expect = expect_data[(h * size.width + w) as usize];\n            let tolerance = tolerance_data[(h * size.width + w) as usize];\n            let index = (w * 4 + offset) as usize;\n            if expect[0].abs_diff(data[index]) > tolerance[0]\n                || expect[1].abs_diff(data[index + 1]) > tolerance[1]\n                || expect[2].abs_diff(data[index + 2]) > tolerance[2]\n                || expect[3].abs_diff(data[index + 3]) > tolerance[3]\n            {\n                panic!(\n                    \"Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]\",\n                    src_format,\n                    reinterpret_to,\n                    expect,\n                    data[index],\n                    data[index + 1],\n                    data[index + 2],\n                    data[index + 3]\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/shader_view_format/view_format.wgsl",
    "content": "@vertex\nfn vs_main(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {\n    let uv: vec2<f32> = vec2<f32>(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u));\n    return vec4<f32>(uv * 2.0 - 1.0, 0.0, 1.0);\n}\n\n@group(0) @binding(0) var tex: texture_2d<f32>;\n\n@fragment\nfn fs_main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {\n    return textureLoad(tex, vec2<i32>(coord.xy), 0);\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/subgroup_operations/mod.rs",
    "content": "use std::num::NonZeroU64;\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(SUBGROUP_OPERATIONS);\n}\n\nconst THREAD_COUNT: u64 = 128;\nconst TEST_COUNT: u32 = 37;\n\n#[gpu_test]\nstatic SUBGROUP_OPERATIONS: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .features(wgpu::Features::SUBGROUP)\n            .limits(wgpu::Limits::downlevel_defaults())\n            // Expect metal to fail on tests involving operations in divergent control flow\n            //\n            // Newlines are included in the panic message to ensure that _additional_ failures\n            // are not matched against.\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    // 26.0 fails only test 28, and not on thread 0\n                    .panic(\"thread 1 failed tests: 28,\\n\")\n                    // 14.3 fails 27 and 28\n                    .panic(\"thread 0 failed tests: 27,\\nthread 1 failed tests: 27, 28,\\n\")\n                    // Prior versions fail 27, 28, and 29\n                    .panic(\"thread 0 failed tests: 27, 29,\\nthread 1 failed tests: 27, 28, 29,\\n\"),\n            )\n            .expect_fail(\n                wgpu_test::FailureCase::backend(wgpu::Backends::METAL)\n                    // 26.0 fails only test 28, and not on thread 0\n                    .panic(\"thread 1 failed tests: 28,\\n\")\n                    // 14.3 fails 27 and 28\n                    .panic(\"thread 0 failed tests: 27,\\nthread 1 failed tests: 27, 28,\\n\")\n                    // Prior versions fail 27, 28, and 29\n                    .panic(\"thread 0 failed tests: 27, 29,\\nthread 1 failed tests: 27, 28, 29,\\n\"),\n            ),\n    )\n    .run_sync(|ctx| {\n        let device = &ctx.device;\n\n        let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: THREAD_COUNT * size_of::<u64>() as u64,\n            usage: wgpu::BufferUsages::STORAGE\n                | wgpu::BufferUsages::COPY_DST\n                | wgpu::BufferUsages::COPY_SRC,\n            mapped_at_creation: false,\n        });\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"bind group layout\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::COMPUTE,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(THREAD_COUNT * size_of::<u64>() as u64),\n                },\n                count: None,\n            }],\n        });\n\n        let cs_module = device.create_shader_module(wgpu::include_wgsl!(\"shader.wgsl\"));\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"main\"),\n            bind_group_layouts: &[Some(&bind_group_layout)],\n            immediate_size: 0,\n        });\n\n        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: None,\n            layout: Some(&pipeline_layout),\n            module: &cs_module,\n            entry_point: Some(\"main\"),\n            compilation_options: Default::default(),\n            cache: None,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: storage_buffer.as_entire_binding(),\n            }],\n            layout: &bind_group_layout,\n            label: Some(\"bind group\"),\n        });\n\n        let mut encoder =\n            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n        {\n            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n                label: None,\n                timestamp_writes: None,\n            });\n            cpass.set_pipeline(&compute_pipeline);\n            cpass.set_bind_group(0, &bind_group, &[]);\n            cpass.dispatch_workgroups(1, 1, 1);\n        }\n        ctx.queue.submit(Some(encoder.finish()));\n\n        wgpu::util::DownloadBuffer::read_buffer(\n            device,\n            &ctx.queue,\n            &storage_buffer.slice(..),\n            |mapping_buffer_view| {\n                let mapping_buffer_view = mapping_buffer_view.unwrap();\n                let result: &[u64; THREAD_COUNT as usize] =\n                    bytemuck::from_bytes(&mapping_buffer_view);\n                let expected_mask = (1u64 << (TEST_COUNT)) - 1; // generate full mask\n                let expected_array = [expected_mask; THREAD_COUNT as usize];\n                if result != &expected_array {\n                    use std::fmt::Write;\n                    let mut msg = String::new();\n                    writeln!(\n                        &mut msg,\n                        \"Got from GPU:\\n{:x?}\\n  expected:\\n{:x?}\",\n                        result, &expected_array,\n                    )\n                    .unwrap();\n                    for (thread, (result, expected)) in result\n                        .iter()\n                        .zip(expected_array)\n                        .enumerate()\n                        .filter(|(_, (r, e))| *r != e)\n                    {\n                        write!(&mut msg, \"thread {thread} failed tests:\").unwrap();\n                        let difference = result ^ expected;\n                        for i in (0..u64::BITS).filter(|i| (difference & (1 << i)) != 0) {\n                            write!(&mut msg, \" {i},\").unwrap();\n                        }\n                        writeln!(&mut msg).unwrap();\n                    }\n                    panic!(\"{}\", msg);\n                }\n            },\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/subgroup_operations/shader.wgsl",
    "content": "@group(0)\n@binding(0)\nvar<storage, read_write> storage_buffer: array<vec2<u32>>;\n\nvar<workgroup> workgroup_buffer: u32;\n\nfn add_result_to_mask(mask: ptr<function, vec2<u32>>, index: u32, value: bool) {\n   (*mask)[index / 32u] |= u32(value) << (index % 32u);\n}\n\n@compute\n@workgroup_size(128)\nfn main(\n    @builtin(global_invocation_id) global_id: vec3<u32>,\n    @builtin(num_subgroups) num_subgroups: u32,\n    @builtin(subgroup_id) subgroup_id: u32,\n    @builtin(subgroup_size) subgroup_size: u32,\n    @builtin(subgroup_invocation_id) subgroup_invocation_id: u32,\n) {\n    var passed = vec2<u32>(0u);\n    var expected: u32;\n\n    add_result_to_mask(&passed, 0u, num_subgroups == 128u / subgroup_size);\n    add_result_to_mask(&passed, 1u, subgroup_id == global_id.x / subgroup_size);\n    add_result_to_mask(&passed, 2u, subgroup_invocation_id == global_id.x % subgroup_size);\n\n    var expected_ballot = vec4<u32>(0u);\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected_ballot[i / 32u] |= ((global_id.x - subgroup_invocation_id + i) & 1u) << (i % 32u);\n    }\n    add_result_to_mask(&passed, 3u, dot(vec4<u32>(1u), vec4<u32>(subgroupBallot((subgroup_invocation_id & 1u) == 1u) == expected_ballot)) == 4u);\n\n    add_result_to_mask(&passed, 4u, subgroupAll(true));\n    add_result_to_mask(&passed, 5u, !subgroupAll(subgroup_invocation_id != 0u));\n\n    add_result_to_mask(&passed, 6u, subgroupAny(subgroup_invocation_id == 0u));\n    add_result_to_mask(&passed, 7u, !subgroupAny(false));\n\n    expected = 0u;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected += global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 8u, subgroupAdd(global_id.x + 1u) == expected);\n\n    expected = 1u;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected *= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 9u, subgroupMul(global_id.x + 1u) == expected);\n\n    expected = 0u;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected = max(expected, global_id.x - subgroup_invocation_id + i + 1u);\n    }\n    add_result_to_mask(&passed, 10u, subgroupMax(global_id.x + 1u) == expected);\n\n    expected = 0xFFFFFFFFu;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected = min(expected, global_id.x - subgroup_invocation_id + i + 1u);\n    }\n    add_result_to_mask(&passed, 11u, subgroupMin(global_id.x + 1u) == expected);\n\n    expected = 0xFFFFFFFFu;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected &= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 12u, subgroupAnd(global_id.x + 1u) == expected);\n\n    expected = 0u;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected |= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 13u, subgroupOr(global_id.x + 1u) == expected);\n\n    expected = 0u;\n    for(var i = 0u; i < subgroup_size; i += 1u) {\n        expected ^= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 14u, subgroupXor(global_id.x + 1u) == expected);\n\n    expected = 0u;\n    for(var i = 0u; i < subgroup_invocation_id; i += 1u) {\n        expected += global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 15u, subgroupExclusiveAdd(global_id.x + 1u) == expected);\n\n    expected = 1u;\n    for(var i = 0u; i < subgroup_invocation_id; i += 1u) {\n        expected *= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 16u, subgroupExclusiveMul(global_id.x + 1u) == expected);\n\n    expected = 0u;\n    for(var i = 0u; i <= subgroup_invocation_id; i += 1u) {\n        expected += global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 17u, subgroupInclusiveAdd(global_id.x + 1u) == expected);\n\n    expected = 1u;\n    for(var i = 0u; i <= subgroup_invocation_id; i += 1u) {\n        expected *= global_id.x - subgroup_invocation_id + i + 1u;\n    }\n    add_result_to_mask(&passed, 18u, subgroupInclusiveMul(global_id.x + 1u) == expected);\n\n    add_result_to_mask(&passed, 19u, subgroupBroadcastFirst(u32(subgroup_invocation_id != 0u)) == 0u);\n    add_result_to_mask(&passed, 20u, subgroupBroadcastFirst(u32(subgroup_invocation_id == 0u)) == 1u);\n    add_result_to_mask(&passed, 21u, subgroupBroadcast(subgroup_invocation_id, 1u) == 1u);\n    add_result_to_mask(&passed, 22u, subgroupShuffle(subgroup_invocation_id, subgroup_invocation_id) == subgroup_invocation_id);\n    add_result_to_mask(&passed, 23u, subgroupShuffle(subgroup_invocation_id, subgroup_size - 1u - subgroup_invocation_id) == subgroup_size - 1u - subgroup_invocation_id);\n    add_result_to_mask(&passed, 24u, subgroupShuffleDown(subgroup_invocation_id, 1u) == subgroup_invocation_id + 1u || subgroup_invocation_id == subgroup_size - 1u);\n    add_result_to_mask(&passed, 25u, subgroupShuffleUp(subgroup_invocation_id, 1u) == subgroup_invocation_id - 1u || subgroup_invocation_id == 0u);\n    add_result_to_mask(&passed, 26u, subgroupShuffleXor(subgroup_invocation_id, subgroup_size - 1u) == (subgroup_invocation_id ^ (subgroup_size - 1u)));\n\n    // Mac/Apple will fail this test.\n    var passed_27 = false;\n    if subgroup_invocation_id % 2u == 0u {\n        passed_27 |= subgroupAdd(1u) == (subgroup_size / 2u);\n    } else {\n        passed_27 |= subgroupAdd(1u) == (subgroup_size / 2u);\n    }\n    add_result_to_mask(&passed, 27u, passed_27);\n\n    // Mac/Apple will fail this test.\n    var passed_28 = false;\n    switch subgroup_invocation_id % 3u {\n        case 0u: {\n            passed_28 = subgroupBroadcastFirst(subgroup_invocation_id) == 0u;\n        }\n        case 1u: {\n            passed_28 = subgroupBroadcastFirst(subgroup_invocation_id) == 1u;\n        }\n        case 2u: {\n            passed_28 = subgroupBroadcastFirst(subgroup_invocation_id) == 2u;\n        }\n        default {  }\n    }\n    add_result_to_mask(&passed, 28u, passed_28);\n\n    // Mac/Apple will sometimes fail this test. MacOS 14.3 passes it, so the bug in the metal compiler seems to be fixed.\n    expected = 0u;\n    for (var i = subgroup_size; i >= 0u; i -= 1u) {\n        expected = subgroupAdd(1u);\n        if i == subgroup_invocation_id {\n            break;\n        }\n    }\n    add_result_to_mask(&passed, 29u, expected == (subgroup_invocation_id + 1u));\n\n    if global_id.x == 0u {\n        workgroup_buffer = subgroup_size;\n    }\n    workgroupBarrier();\n    add_result_to_mask(&passed, 30u, workgroup_buffer == subgroup_size);\n\n    add_result_to_mask(&passed, 31u, quadBroadcast(quadBroadcast(subgroup_invocation_id, 0u) ^ subgroup_invocation_id, 0u) == 0u);\n    add_result_to_mask(&passed, 32u, quadBroadcast(quadBroadcast(subgroup_invocation_id, 1u) ^ quadSwapX(subgroup_invocation_id), 0u) == 0u);\n    add_result_to_mask(&passed, 33u, quadBroadcast(quadBroadcast(subgroup_invocation_id, 2u) ^ quadSwapY(subgroup_invocation_id), 0u) == 0u);\n    add_result_to_mask(&passed, 34u, quadBroadcast(quadBroadcast(subgroup_invocation_id, 3u) ^ quadSwapDiagonal(subgroup_invocation_id), 0u) == 0u);\n    add_result_to_mask(&passed, 35u, quadSwapX(quadSwapY(subgroup_invocation_id)) == quadSwapDiagonal(subgroup_invocation_id));\n\n    // Keep this test last, verify we are still convergent after running other tests\n    add_result_to_mask(&passed, 36u, subgroupAdd(1u) == subgroup_size);\n\n    // Increment TEST_COUNT in subgroup_operations/mod.rs if adding more tests\n\n    storage_buffer[global_id.x] = passed;\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_binding/mod.rs",
    "content": "use std::time::Duration;\nuse wgpu::wgt::BufferDescriptor;\nuse wgpu::{\n    include_wgsl, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferUsages,\n    ComputePassDescriptor, ComputePipelineDescriptor, DownlevelFlags, Extent3d, MapMode, Origin3d,\n    PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TexelCopyTextureInfo, TextureAspect,\n    TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,\n};\nuse wgpu_macros::gpu_test;\nuse wgpu_test::{GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([TEXTURE_BINDING, SINGLE_SCALAR_LOAD]);\n}\n\n#[gpu_test]\nstatic TEXTURE_BINDING: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .downlevel_flags(DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT)\n            .enable_noop(),\n    )\n    .run_sync(texture_binding);\n\nfn texture_binding(ctx: TestingContext) {\n    let texture = ctx.device.create_texture(&TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rg32Float,\n        usage: TextureUsages::STORAGE_BINDING,\n        view_formats: &[],\n    });\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"shader.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n    let bind = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::TextureView(&texture.create_view(&Default::default())),\n        }],\n    });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n    {\n        let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor::default());\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bind, &[]);\n        pass.dispatch_workgroups(1, 1, 1);\n    }\n    ctx.queue.submit([encoder.finish()]);\n}\n\n#[gpu_test]\nstatic SINGLE_SCALAR_LOAD: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .downlevel_flags(DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT),\n    )\n    .run_sync(single_scalar_load);\n\nfn single_scalar_load(ctx: TestingContext) {\n    let texture_read = ctx.device.create_texture(&TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::R32Float,\n        usage: TextureUsages::STORAGE_BINDING,\n        view_formats: &[],\n    });\n    let texture_write = ctx.device.create_texture(&TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba32Float,\n        usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let buffer = ctx.device.create_buffer(&BufferDescriptor {\n        label: None,\n        size: size_of::<[f32; 4]>() as wgpu::BufferAddress,\n        usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    let shader = ctx\n        .device\n        .create_shader_module(include_wgsl!(\"single_scalar.wgsl\"));\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&ComputePipelineDescriptor {\n            label: None,\n            layout: None,\n            module: &shader,\n            entry_point: None,\n            compilation_options: Default::default(),\n            cache: None,\n        });\n    let bind = ctx.device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[\n            BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::TextureView(\n                    &texture_write.create_view(&Default::default()),\n                ),\n            },\n            BindGroupEntry {\n                binding: 1,\n                resource: BindingResource::TextureView(\n                    &texture_read.create_view(&Default::default()),\n                ),\n            },\n        ],\n    });\n\n    let mut encoder = ctx.device.create_command_encoder(&Default::default());\n    {\n        let mut pass = encoder.begin_compute_pass(&ComputePassDescriptor::default());\n        pass.set_pipeline(&pipeline);\n        pass.set_bind_group(0, &bind, &[]);\n        pass.dispatch_workgroups(1, 1, 1);\n    }\n    encoder.copy_texture_to_buffer(\n        TexelCopyTextureInfo {\n            texture: &texture_write,\n            mip_level: 0,\n            origin: Origin3d::ZERO,\n            aspect: TextureAspect::All,\n        },\n        TexelCopyBufferInfo {\n            buffer: &buffer,\n            layout: TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: None,\n                rows_per_image: None,\n            },\n        },\n        Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 1,\n        },\n    );\n    ctx.queue.submit([encoder.finish()]);\n    let (send, recv) = std::sync::mpsc::channel();\n    buffer.slice(..).map_async(MapMode::Read, move |res| {\n        res.unwrap();\n        send.send(()).expect(\"Thread should wait for receive\");\n    });\n    // Poll to run map.\n    ctx.device.poll(PollType::wait_indefinitely()).unwrap();\n    recv.recv_timeout(Duration::from_secs(10))\n        .expect(\"mapping should not take this long\");\n    let val = *bytemuck::from_bytes::<[f32; 4]>(&buffer.slice(..).get_mapped_range());\n    assert_eq!(val, [0.0, 0.0, 0.0, 1.0]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_binding/shader.wgsl",
    "content": "@group(0) @binding(0)\nvar tex: texture_storage_2d<rg32float, read>;\n\n@compute @workgroup_size(1) fn csStore() {\n    _ = textureLoad(tex, vec2u(0));\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_binding/single_scalar.wgsl",
    "content": "@group(0) @binding(0)\nvar tex_w: texture_storage_2d<rgba32float, write>;\n@group(0) @binding(1)\nvar tex_r: texture_storage_2d<r32float, read>;\n\n@compute @workgroup_size(1) fn csStore() {\n    textureStore(tex_w, vec2u(0), textureLoad(tex_r, vec2u(0)));\n}"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_blit.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        TEXTURE_BLIT_WITH_LINEAR_FILTER_TEST,\n        TEXTURE_BLIT_WITH_NEAREST_FILTER_TEST,\n    ]);\n}\n\n#[gpu_test]\nstatic TEXTURE_BLIT_WITH_LINEAR_FILTER_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let source = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 100,\n                height: 100,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba16Float,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let target = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 100,\n                height: 100,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n\n        let blitter = wgpu::util::TextureBlitterBuilder::new(\n            &ctx.device,\n            wgpu::TextureFormat::Rgba8UnormSrgb,\n        )\n        .sample_type(wgpu::FilterMode::Linear)\n        .build();\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        blitter.copy(\n            &ctx.device,\n            &mut encoder,\n            &source.create_view(&wgpu::TextureViewDescriptor::default()),\n            &target.create_view(&wgpu::TextureViewDescriptor::default()),\n        );\n    });\n\n#[gpu_test]\nstatic TEXTURE_BLIT_WITH_NEAREST_FILTER_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let source = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 100,\n                height: 100,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba16Float,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let target = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 100,\n                height: 100,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            view_formats: &[],\n        });\n\n        let blitter = wgpu::util::TextureBlitterBuilder::new(\n            &ctx.device,\n            wgpu::TextureFormat::Rgba8UnormSrgb,\n        )\n        .sample_type(wgpu::FilterMode::Linear)\n        .build();\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        blitter.copy(\n            &ctx.device,\n            &mut encoder,\n            &source.create_view(&wgpu::TextureViewDescriptor::default()),\n            &target.create_view(&wgpu::TextureViewDescriptor::default()),\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_bounds.rs",
    "content": "//! Tests for texture copy bounds checks.\n\nuse wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(BAD_COPY_ORIGIN_TEST);\n}\n\n#[gpu_test]\nstatic BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let try_origin = |origin, size, should_panic| {\n            let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR);\n            let data = vec![255; BUFFER_SIZE as usize];\n\n            fail_if(\n                &ctx.device,\n                should_panic,\n                || {\n                    ctx.queue.write_texture(\n                        wgpu::TexelCopyTextureInfo {\n                            texture: &texture,\n                            mip_level: 0,\n                            origin,\n                            aspect: wgpu::TextureAspect::All,\n                        },\n                        &data,\n                        BUFFER_COPY_LAYOUT,\n                        size,\n                    )\n                },\n                None,\n            );\n        };\n\n        try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);\n        try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true);\n        try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true);\n        try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true);\n\n        try_origin(\n            wgpu::Origin3d {\n                x: TEXTURE_SIZE.width - 1,\n                y: TEXTURE_SIZE.height - 1,\n                z: TEXTURE_SIZE.depth_or_array_layers - 1,\n            },\n            wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            false,\n        );\n        try_origin(\n            wgpu::Origin3d {\n                x: u32::MAX,\n                y: 0,\n                z: 0,\n            },\n            wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            true,\n        );\n        try_origin(\n            wgpu::Origin3d {\n                x: u32::MAX,\n                y: 0,\n                z: 0,\n            },\n            wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            true,\n        );\n        try_origin(\n            wgpu::Origin3d {\n                x: u32::MAX,\n                y: 0,\n                z: 0,\n            },\n            wgpu::Extent3d {\n                width: 1,\n                height: 1,\n                depth_or_array_layers: 1,\n            },\n            true,\n        );\n    });\n\nconst TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d {\n    width: 64,\n    height: 64,\n    depth_or_array_layers: 1,\n};\n\nconst TEXTURE_DESCRIPTOR: wgpu::TextureDescriptor = wgpu::TextureDescriptor {\n    label: Some(\"CopyOrigin\"),\n    size: TEXTURE_SIZE,\n    mip_level_count: 1,\n    sample_count: 1,\n    dimension: wgpu::TextureDimension::D2,\n    format: wgpu::TextureFormat::Rgba8UnormSrgb,\n    usage: wgpu::TextureUsages::COPY_DST.union(wgpu::TextureUsages::COPY_SRC),\n    view_formats: &[],\n};\n\nconst BYTES_PER_PIXEL: u32 = 4;\n\nconst BUFFER_SIZE: u32 = TEXTURE_SIZE.width * TEXTURE_SIZE.height * BYTES_PER_PIXEL;\n\nconst BUFFER_COPY_LAYOUT: wgpu::TexelCopyBufferLayout = wgpu::TexelCopyBufferLayout {\n    offset: 0,\n    bytes_per_row: Some(TEXTURE_SIZE.width * BYTES_PER_PIXEL),\n    rows_per_image: None,\n};\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/texture_view_creation.rs",
    "content": "use wgpu::*;\nuse wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        STENCIL_ONLY_VIEW_CREATION,\n        DEPTH_ONLY_VIEW_CREATION,\n        SHARED_USAGE_VIEW_CREATION,\n    ]);\n}\n\n#[gpu_test]\nstatic STENCIL_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .skip(FailureCase::webgl2()) // WebGL doesn't have stencil only views\n            .limits(wgpu::Limits::downlevel_defaults())\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        for format in [TextureFormat::Stencil8, TextureFormat::Depth24PlusStencil8] {\n            let texture = ctx.device.create_texture(&TextureDescriptor {\n                label: None,\n                size: Extent3d {\n                    width: 256,\n                    height: 256,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format,\n                usage: TextureUsages::COPY_DST\n                    | TextureUsages::COPY_SRC\n                    | TextureUsages::TEXTURE_BINDING,\n                view_formats: &[],\n            });\n            let _view = texture.create_view(&TextureViewDescriptor {\n                aspect: TextureAspect::StencilOnly,\n                ..Default::default()\n            });\n        }\n    });\n\n#[gpu_test]\nstatic DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        for format in [\n            TextureFormat::Depth16Unorm,\n            TextureFormat::Depth24Plus,\n            TextureFormat::Depth24PlusStencil8,\n        ] {\n            let texture = ctx.device.create_texture(&TextureDescriptor {\n                label: None,\n                size: Extent3d {\n                    width: 256,\n                    height: 256,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format,\n                usage: TextureUsages::COPY_DST\n                    | TextureUsages::COPY_SRC\n                    | TextureUsages::TEXTURE_BINDING,\n                view_formats: &[],\n            });\n            let _view = texture.create_view(&TextureViewDescriptor {\n                aspect: TextureAspect::DepthOnly,\n                ..Default::default()\n            });\n        }\n    });\n\n#[gpu_test]\nstatic SHARED_USAGE_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(DownlevelFlags::VIEW_FORMATS)\n            .enable_noop(),\n    )\n    .run_async(|ctx| async move {\n        {\n            let (texture_format, view_format) =\n                (TextureFormat::Rgba8Unorm, TextureFormat::Rgba8UnormSrgb);\n            let texture = ctx.device.create_texture(&TextureDescriptor {\n                label: None,\n                size: Extent3d {\n                    width: 256,\n                    height: 256,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format: texture_format,\n                usage: TextureUsages::COPY_DST\n                    | TextureUsages::STORAGE_BINDING\n                    | TextureUsages::TEXTURE_BINDING\n                    | TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[TextureFormat::Rgba8UnormSrgb],\n            });\n            let _view = texture.create_view(&TextureViewDescriptor {\n                aspect: TextureAspect::All,\n                format: Some(view_format),\n                usage: Some(\n                    TextureUsages::COPY_DST\n                        | TextureUsages::TEXTURE_BINDING\n                        | TextureUsages::RENDER_ATTACHMENT,\n                ),\n                ..Default::default()\n            });\n        }\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/timestamp_normalization/mod.rs",
    "content": "mod utils;\n\npub fn all_tests(tests: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    utils::all_tests(tests);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/timestamp_normalization/shift_right_u96.wgsl",
    "content": "// Must have \"wgpu-core/src/timestamp_normalization/common.wgsl\"\n// preprocessed before this file's contents.\n\nstruct ShiftRight96 {\n    value: Uint96,\n    shift: u32,\n}\n\n@group(0) @binding(0)\nvar<storage> input: array<ShiftRight96>;\n\n@group(0) @binding(1)\nvar<storage, read_write> output: array<Uint96>;\n\n@compute @workgroup_size(256)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n    let index = id.x;\n\n    let input = input[index];\n\n    output[index] = shift_right_96(input.value, input.shift);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/timestamp_normalization/u64_mul_u32.wgsl",
    "content": "// Must have \"wgpu-core/src/timestamp_normalization/common.wgsl\"\n// preprocessed before this file's contents.\n\nstruct U64MulU32Input {\n    left: Uint64,\n    right: u32,\n    _padding: u32,\n}\n\n@group(0) @binding(0)\nvar<storage> input: array<U64MulU32Input>;\n\n@group(0) @binding(1)\nvar<storage, read_write> output: array<Uint96>;\n\n@compute @workgroup_size(256)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n    let index = id.x;\n\n    let input = input[index];\n\n    output[index] = u64_mul_u32(input.left, input.right);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/timestamp_normalization/utils.rs",
    "content": "//! Tests for the timestamp normalization algorithm's utility functions.\n//!\n//! Because they involve multiple kinds of hand-rolled math operations,\n//! we do testing to ensure the overall operation (which is very simple)\n//! works correctly.\n\nuse nanorand::Rng;\nuse wgpu::{util::DeviceExt, Limits};\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.extend([U64_MUL_U32, SHIFT_RIGHT_U96]);\n}\n\n#[repr(C)]\n#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]\nstruct Uint96(u32, u32, u32);\n\nimpl Uint96 {\n    fn from_u128(value: u128) -> Self {\n        let a = (value & 0xFFFF_FFFF) as u32;\n        let b = ((value >> 32) & 0xFFFF_FFFF) as u32;\n        let c = ((value >> 64) & 0xFFFF_FFFF) as u32;\n\n        Self(a, b, c)\n    }\n\n    fn as_u128(&self) -> u128 {\n        ((self.2 as u128) << 64) | ((self.1 as u128) << 32) | (self.0 as u128)\n    }\n}\n\n#[repr(C)]\n#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]\nstruct U64MulU32Input {\n    left: u64,\n    right: u32,\n    _pad: u32,\n}\n\nimpl U64MulU32Input {\n    fn new(left: u64, right: u32) -> Self {\n        Self {\n            left,\n            right,\n            _pad: 0,\n        }\n    }\n}\n\nfn assert_u64_mul_u32(left: u64, right: u32, computed: Uint96) {\n    let real = left as u128 * right as u128;\n\n    let computed = computed.as_u128();\n\n    assert_eq!(\n        computed, real,\n        \"{left} * {right} should be {real} but is {computed}\"\n    );\n}\n\n#[gpu_test]\nstatic U64_MUL_U32: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(Limits {\n                max_storage_buffer_binding_size: 256 * 1024 * 1024,\n                ..Limits::downlevel_defaults()\n            })\n            // https://github.com/gfx-rs/wgpu/issues/9187\n            .expect_fail(wgpu_test::FailureCase::kosmic_krisp().panic(\"18446744073709551615 * 4294967295 should be 79228162495817593515539431425 but is 39614081238685424718767456257\\n  left: 39614081238685424718767456257\\n right: 79228162495817593515539431425\")),\n    )\n    .run_sync(test_u64_mul_u32);\n\nfn test_u64_mul_u32(ctx: TestingContext) {\n    const TOTAL_RANDOM_INPUTS: usize = 1_000_000;\n    const MANUAL_INPUTS: usize = 2;\n\n    const TOTAL_INPUTS: usize = TOTAL_RANDOM_INPUTS + MANUAL_INPUTS;\n\n    let mut inputs = Vec::with_capacity(TOTAL_INPUTS);\n\n    inputs.push(U64MulU32Input::new(2, 2));\n    inputs.push(U64MulU32Input::new(u64::MAX, u32::MAX));\n\n    // Smoke test the algorithm by generating 1M random inputs, and checking the results.\n    let mut generator = nanorand::WyRand::new_seed(0xDEAD_BEEF);\n\n    for _ in 0..TOTAL_RANDOM_INPUTS {\n        let left = generator.generate::<u64>();\n        let right = generator.generate::<u32>();\n\n        inputs.push(U64MulU32Input::new(left, right));\n    }\n\n    assert_eq!(TOTAL_INPUTS, inputs.len());\n\n    let output_bytes = process_shader(\n        ctx,\n        bytemuck::cast_slice(&inputs),\n        include_str!(\"u64_mul_u32.wgsl\"),\n    );\n    let output_values = bytemuck::pod_collect_to_vec(&output_bytes);\n\n    for (&input, &output) in inputs.iter().zip(output_values.iter()) {\n        assert_u64_mul_u32(input.left, input.right, output);\n    }\n}\n\n#[repr(C)]\n#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]\nstruct ShiftRightU96Input {\n    value: Uint96,\n    shift: u32,\n}\n\nimpl ShiftRightU96Input {\n    fn new(value: u128, shift: u32) -> Self {\n        assert!(shift <= 32);\n        assert!(value >> 96 == 0);\n\n        Self {\n            value: Uint96::from_u128(value),\n            shift,\n        }\n    }\n}\n\nfn assert_shift_right_u96(value: Uint96, shift: u32, computed: Uint96) {\n    let value = value.as_u128();\n\n    let real = value >> shift;\n\n    let computed = computed.as_u128();\n\n    assert_eq!(\n        computed, real,\n        \"{value:X} >> {shift} should be {real:X} but is {computed:X}\",\n    );\n}\n\n#[gpu_test]\nstatic SHIFT_RIGHT_U96: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .limits(Limits {\n                max_storage_buffer_binding_size: 256 * 1024 * 1024,\n                ..Limits::downlevel_defaults()\n            }),\n    )\n    .run_sync(test_shift_right_u96);\n\nfn test_shift_right_u96(ctx: TestingContext) {\n    const TOTAL_RANDOM_INPUTS: usize = 1_000_000;\n    const TOTAL_SHIFT_INPUTS: usize = 33;\n    const MANUAL_INPUTS: usize = 1;\n\n    const TOTAL_INPUTS: usize = TOTAL_RANDOM_INPUTS + TOTAL_SHIFT_INPUTS + MANUAL_INPUTS;\n\n    let mut inputs = Vec::with_capacity(TOTAL_INPUTS);\n\n    inputs.push(ShiftRightU96Input::new(1, 1));\n\n    for shift in 0..TOTAL_SHIFT_INPUTS {\n        // 96 bit number with a visually recognizable pattern.\n        const INTERESTING_NUMBER: u128 = 0x1234_5678_9ABC_DEF0_1234_5678;\n\n        inputs.push(ShiftRightU96Input::new(INTERESTING_NUMBER, shift as u32));\n    }\n\n    // Smoke test the algorithm by generating 1M random inputs, and checking the results.\n    let mut generator = nanorand::WyRand::new_seed(0xDEAD_BEEF);\n\n    for _ in 0..TOTAL_RANDOM_INPUTS {\n        // nanorand doesn't have generate_range for u128, so just chop the top bits off.\n        let value = generator.generate::<u128>() >> 32;\n        let shift = generator.generate_range(0..=32);\n\n        inputs.push(ShiftRightU96Input::new(value, shift));\n    }\n\n    assert_eq!(TOTAL_INPUTS, inputs.len());\n\n    let output_bytes = process_shader(\n        ctx,\n        bytemuck::cast_slice(&inputs),\n        include_str!(\"shift_right_u96.wgsl\"),\n    );\n\n    let output_values = bytemuck::pod_collect_to_vec(&output_bytes);\n\n    for (&input, &output) in inputs.iter().zip(output_values.iter()) {\n        assert_shift_right_u96(input.value, input.shift, output);\n    }\n}\n\nfn process_shader(ctx: TestingContext, inputs: &[u8], entry_point_src: &str) -> Vec<u8> {\n    let common_src = include_str!(\"../../../../wgpu-core/src/timestamp_normalization/common.wgsl\");\n\n    let full_source = format!(\"{common_src}\\n{entry_point_src}\");\n\n    let shader_module = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"u64_mul_u32\"),\n            source: wgpu::ShaderSource::Wgsl(full_source.into()),\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"u64_mul_u32\"),\n            layout: None,\n            module: &shader_module,\n            entry_point: None,\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    let input_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Input Buffer\"),\n            contents: inputs,\n            usage: wgpu::BufferUsages::STORAGE,\n        });\n\n    let output_size = (size_of::<Uint96>() * inputs.len()) as u64;\n\n    let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Output Buffer\"),\n        size: output_size,\n        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let pulldown_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Pulldown Buffer\"),\n        size: output_size,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let bgl = pipeline.get_bind_group_layout(0);\n\n    let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n        label: Some(\"Bind Group\"),\n        layout: &bgl,\n        entries: &[\n            wgpu::BindGroupEntry {\n                binding: 0,\n                resource: input_buffer.as_entire_binding(),\n            },\n            wgpu::BindGroupEntry {\n                binding: 1,\n                resource: output_buffer.as_entire_binding(),\n            },\n        ],\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n            label: Some(\"Compute Encoder\"),\n        });\n\n    let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n        label: Some(\"Compute Pass\"),\n        timestamp_writes: None,\n    });\n\n    cpass.set_pipeline(&pipeline);\n    cpass.set_bind_group(0, &bg, &[]);\n    cpass.dispatch_workgroups(inputs.len().div_ceil(256) as u32, 1, 1);\n\n    drop(cpass);\n\n    encoder.copy_buffer_to_buffer(&output_buffer, 0, &pulldown_buffer, 0, output_size);\n\n    ctx.queue.submit([encoder.finish()]);\n    pulldown_buffer.map_async(wgpu::MapMode::Read, .., |_| {});\n\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n\n    pulldown_buffer.get_mapped_range(..).to_vec()\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/timestamp_query.rs",
    "content": "use wgpu::{\n    util::DeviceExt, ComputePassTimestampWrites, Features, InstanceFlags,\n    QUERY_RESOLVE_BUFFER_ALIGNMENT,\n};\nuse wgpu_test::{\n    gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(TIMESTAMP_QUERY);\n}\n\nconst SHADER: &str = r#\"\n@compute @workgroup_size(1)\nfn main() {\n    return;\n}\n\"#;\n\nconst ITERATIONS: u32 = 10;\n\nconst QUERIES_PER_ITERATION: u32 = 2;\nconst TOTAL_QUERIES: u32 = QUERIES_PER_ITERATION * ITERATIONS;\n\n#[gpu_test]\nstatic TIMESTAMP_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .expect_fail(FailureCase::webgl2())\n            .test_features_limits()\n            .features(Features::TIMESTAMP_QUERY)\n            // Ensure timestamp normalization functions correctly\n            .instance_flags(InstanceFlags::AUTOMATIC_TIMESTAMP_NORMALIZATION),\n    )\n    .run_sync(timestamp_query);\n\nfn timestamp_query(ctx: TestingContext) {\n    // Setup pipeline using a simple shader with hardcoded vertices\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"timestamp query shader\"),\n            source: wgpu::ShaderSource::Wgsl(SHADER.into()),\n        });\n\n    let pipeline = ctx\n        .device\n        .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {\n            label: Some(\"Pipeline\"),\n            layout: None,\n            module: &shader,\n            entry_point: None,\n            compilation_options: wgpu::PipelineCompilationOptions::default(),\n            cache: None,\n        });\n\n    // Create timestamp query set\n    let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {\n        label: Some(\"Query set\"),\n        ty: wgpu::QueryType::Timestamp,\n        count: TOTAL_QUERIES,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    for i in 0..ITERATIONS {\n        let base_index = i * QUERIES_PER_ITERATION;\n\n        let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {\n            label: Some(\"compute pass\"),\n            timestamp_writes: Some(ComputePassTimestampWrites {\n                query_set: &query_set,\n                beginning_of_pass_write_index: Some(base_index),\n                end_of_pass_write_index: Some(base_index + 1),\n            }),\n        });\n        compute_pass.set_pipeline(&pipeline);\n\n        compute_pass.dispatch_workgroups(1, 1, 1);\n    }\n\n    let buffer_size = QUERY_RESOLVE_BUFFER_ALIGNMENT * TOTAL_QUERIES as u64;\n    let init_constant = 0x0123_4567_89AB_CDEFu64;\n\n    let init_data = vec![init_constant; buffer_size as usize / 8];\n\n    // Resolve query set to buffer\n    let query_buffer = ctx\n        .device\n        .create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"Query buffer\"),\n            contents: bytemuck::cast_slice(&init_data),\n            usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,\n        });\n\n    for i in 0..ITERATIONS {\n        let start_query = i * QUERIES_PER_ITERATION;\n        let end_query = start_query + QUERIES_PER_ITERATION;\n        let buffer_offset = i as u64 * QUERY_RESOLVE_BUFFER_ALIGNMENT;\n\n        encoder.resolve_query_set(\n            &query_set,\n            start_query..end_query,\n            &query_buffer,\n            buffer_offset,\n        );\n    }\n\n    let mapping_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Mapping buffer\"),\n        size: query_buffer.size(),\n        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    encoder.copy_buffer_to_buffer(&query_buffer, 0, &mapping_buffer, 0, query_buffer.size());\n\n    ctx.queue.submit(Some(encoder.finish()));\n\n    mapping_buffer\n        .slice(..)\n        .map_async(wgpu::MapMode::Read, |_| ());\n    ctx.device\n        .poll(wgpu::PollType::wait_indefinitely())\n        .unwrap();\n    let query_buffer_view = mapping_buffer.slice(..).get_mapped_range();\n    let query_data: &[u64] = bytemuck::cast_slice(&query_buffer_view);\n\n    for i in 0..ITERATIONS {\n        // The byte and query offset for the current iteration\n        let byte_offset = i as u64 * QUERY_RESOLVE_BUFFER_ALIGNMENT;\n        let query_offset = byte_offset / 8;\n\n        // The byte and query offset for the next iteration\n        let next_byte_offset = (i + 1) as u64 * QUERY_RESOLVE_BUFFER_ALIGNMENT;\n        let next_query_offset = next_byte_offset / 8;\n\n        // The range of queries that should still be the value they were initialized to.\n        let untouched_query_start = query_offset + QUERIES_PER_ITERATION as u64;\n        let untouched_query_end = next_query_offset;\n\n        // WebGPU does not define the value of the timestamp queries. They unfortunately\n        // can be `0` in some situations. However, we should expect that some value\n        // has been written, and the odds of it being exactly `init_constant` are vanishingly low.\n        for query in 0..QUERIES_PER_ITERATION {\n            let query_index = query_offset + query as u64;\n            assert_ne!(query_data[query_index as usize], init_constant);\n        }\n\n        // Validate that the queries that were not written to are still the value they were initialized to.\n        for query in untouched_query_start..untouched_query_end {\n            assert_eq!(query_data[query as usize], init_constant);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/transfer.rs",
    "content": "use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(COPY_OVERFLOW_Z);\n}\n\n#[gpu_test]\nstatic COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size: wgpu::Extent3d {\n                width: 256,\n                height: 256,\n                depth_or_array_layers: 1,\n            },\n            format: wgpu::TextureFormat::Rgba8Uint,\n            usage: wgpu::TextureUsages::COPY_DST,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size: wgpu::Extent3d {\n                width: 256,\n                height: 256,\n                depth_or_array_layers: 1,\n            },\n            format: wgpu::TextureFormat::Rgba8Uint,\n            usage: wgpu::TextureUsages::COPY_DST,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n\n        fail(\n            &ctx.device,\n            || {\n                // Validation should catch the silly selected z layer range without panicking.\n                encoder.copy_texture_to_texture(\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &t1,\n                        mip_level: 1,\n                        origin: wgpu::Origin3d::ZERO,\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::TexelCopyTextureInfo {\n                        texture: &t2,\n                        mip_level: 1,\n                        origin: wgpu::Origin3d {\n                            x: 0,\n                            y: 0,\n                            z: 3824276442,\n                        },\n                        aspect: wgpu::TextureAspect::All,\n                    },\n                    wgpu::Extent3d {\n                        width: 100,\n                        height: 3,\n                        depth_or_array_layers: 613286111,\n                    },\n                );\n                ctx.queue.submit(Some(encoder.finish()));\n            },\n            Some(\"unable to select texture mip level\"),\n        );\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/transient.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(RESOLVE_WITH_TRANSIENT);\n}\n\n#[gpu_test]\nstatic RESOLVE_WITH_TRANSIENT: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default())\n    .run_async(|ctx| async move {\n        const SIZE: wgpu::Extent3d = wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        };\n\n        let shader_src = \"\n            @vertex\n            fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {\n                let positions: array<vec2f, 3> = array<vec2f, 3>(\n                    vec2f(-1.0, -1.0),\n                    vec2f(-1.0, 3.0),\n                    vec2f(3.0, -1.0)\n                );\n                return vec4f(positions[index], 0.0, 1.0);\n            }\n\n            @fragment\n            fn fs_main() -> @location(0) vec4f {\n                return vec4f(1.0);\n            }\n        \";\n\n        let shader = ctx\n            .device\n            .create_shader_module(wgpu::ShaderModuleDescriptor {\n                label: None,\n                source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n            });\n\n        let pipeline_desc = wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: None,\n            vertex: wgpu::VertexState {\n                buffers: &[],\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState {\n                count: 4,\n                mask: !0,\n                alpha_to_coverage_enabled: false,\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        };\n        let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n        let transient_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: SIZE,\n            mip_level_count: 1,\n            sample_count: 4,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,\n            view_formats: &[],\n        });\n\n        let target_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: SIZE,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n            view_formats: &[],\n        });\n\n        let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: 256 * 256 * 4,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        {\n            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &transient_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                    depth_slice: None,\n                    resolve_target: Some(\n                        &target_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                    ),\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),\n                        store: wgpu::StoreOp::Discard,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            rpass.set_pipeline(&pipeline);\n            rpass.draw(0..3, 0..1);\n        }\n\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &target_texture,\n                mip_level: 0,\n                origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &readback_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(256 * 4),\n                    rows_per_image: Some(256),\n                },\n            },\n            SIZE,\n        );\n\n        ctx.queue.submit([encoder.finish()]);\n\n        let slice = readback_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| ());\n\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n\n        let data = slice.get_mapped_range();\n        let succeeded = data.iter().all(|b| *b == u8::MAX);\n        assert!(succeeded);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/transition_resources.rs",
    "content": "use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(TRANSITION_RESOURCES);\n}\n\n#[gpu_test]\nstatic TRANSITION_RESOURCES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_sync(|ctx| {\n        let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            size: wgpu::Extent3d {\n                width: 32,\n                height: 32,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,\n            view_formats: &[],\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.transition_resources(\n            std::iter::empty(),\n            [wgpu::TextureTransition {\n                texture: &texture,\n                selector: None,\n                state: wgpu::TextureUses::COLOR_TARGET,\n            }]\n            .into_iter(),\n        );\n\n        ctx.queue.submit([encoder.finish()]);\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/vertex_formats/draw.vert.wgsl",
    "content": "@group(0) @binding(0)\nvar<storage, read_write> checksums: array<f32>;\n\nconst index_uint = 0u;\nconst index_sint = 1u;\nconst index_unorm = 2u;\nconst index_snorm = 3u;\nconst index_float16 = 4u;\nconst index_float32 = 5u;\n\nfn init_checksums() {\n  checksums[index_uint] = 0.0;\n  checksums[index_sint] = 0.0;\n  checksums[index_unorm] = 0.0;\n  checksums[index_snorm] = 0.0;\n  checksums[index_float16] = 0.0;\n  checksums[index_float32] = 0.0;\n}\n\n// Break down the 31 vertex formats specified at\n// https://gpuweb.github.io/gpuweb/#vertex-formats into blocks\n// of 8, to keep under the limits of max locations. Each\n// AttributeBlockX structure will get a corresponding\n// vertex_block_X function to process its attributes into\n// values written to the checksums buffer.\n\nstruct AttributeBlock0 {\n  // 4-byte-aligned unorm formats\n  @location(0) unorm8x4: vec4<f32>,\n  @location(1) unorm16x2: vec2<f32>,\n  @location(2) unorm16x4: vec4<f32>,\n\n  // 4-byte-aligned snorm formats\n  @location(3) snorm8x4: vec4<f32>,\n  @location(4) snorm16x2: vec2<f32>,\n  @location(5) snorm16x4: vec4<f32>,\n\n  // 2-byte-aligned formats\n  @location(6) unorm8x2: vec2<f32>,\n  @location(7) snorm8x2: vec2<f32>,\n}\n\n@vertex\nfn vertex_block_0(v_in: AttributeBlock0) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all unorm into one checksum value.\n  var all_unorm: f32 = 0.0;\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x2.x);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x2.y);\n\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4.x);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4.y);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4.z);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4.w);\n\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x2.x);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x2.y);\n\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x4.x);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x4.y);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x4.z);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16x4.w);\n\n  checksums[index_unorm] = f32(all_unorm);\n\n  // Accumulate all snorm into one checksum value.\n  var all_snorm: f32 = 0.0;\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x2.x);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x2.y);\n\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x4.x);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x4.y);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x4.z);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8x4.w);\n\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x2.x);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x2.y);\n\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x4.x);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x4.y);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x4.z);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16x4.w);\n\n  checksums[index_snorm] = f32(all_snorm);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock1 {\n  // 4-byte-aligned uint formats\n  @location(0) uint8x4: vec4<u32>,\n  @location(1) uint16x2: vec2<u32>,\n  @location(2) uint16x4: vec4<u32>,\n\n  // 4-byte-aligned sint formats\n  @location(3) sint8x4: vec4<i32>,\n  @location(4) sint16x2: vec2<i32>,\n  @location(5) sint16x4: vec4<i32>,\n\n  // 2-byte-aligned formats\n  @location(6) uint8x2: vec2<u32>,\n  @location(7) sint8x2: vec2<i32>,\n}\n\n@vertex\nfn vertex_block_1(v_in: AttributeBlock1) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all uint into one checksum value.\n  var all_uint: u32 = 0;\n  all_uint = accumulate_uint(all_uint, v_in.uint8x2.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint8x2.y);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint8x4.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint8x4.y);\n  all_uint = accumulate_uint(all_uint, v_in.uint8x4.z);\n  all_uint = accumulate_uint(all_uint, v_in.uint8x4.w);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint16x2.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint16x2.y);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint16x4.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint16x4.y);\n  all_uint = accumulate_uint(all_uint, v_in.uint16x4.z);\n  all_uint = accumulate_uint(all_uint, v_in.uint16x4.w);\n\n  checksums[index_uint] = f32(all_uint);\n\n  // Accumulate all sint into one checksum value.\n  var all_sint: i32 = 0;\n  all_sint = accumulate_sint(all_sint, v_in.sint8x2.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint8x2.y);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint8x4.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint8x4.y);\n  all_sint = accumulate_sint(all_sint, v_in.sint8x4.z);\n  all_sint = accumulate_sint(all_sint, v_in.sint8x4.w);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint16x2.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint16x2.y);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint16x4.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint16x4.y);\n  all_sint = accumulate_sint(all_sint, v_in.sint16x4.z);\n  all_sint = accumulate_sint(all_sint, v_in.sint16x4.w);\n\n  checksums[index_sint] = f32(all_sint);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock2 {\n  @location(0) uint32: u32,\n  @location(1) uint32x2: vec2<u32>,\n  @location(2) uint32x3: vec3<u32>,\n  @location(3) uint32x4: vec4<u32>,\n}\n\n@vertex\nfn vertex_block_2(v_in: AttributeBlock2) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all uint into one checksum value.\n  var all_uint: u32 = 0;\n  all_uint = accumulate_uint(all_uint, v_in.uint32);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint32x2.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x2.y);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint32x3.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x3.y);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x3.z);\n\n  all_uint = accumulate_uint(all_uint, v_in.uint32x4.x);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x4.y);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x4.z);\n  all_uint = accumulate_uint(all_uint, v_in.uint32x4.w);\n\n  checksums[index_uint] = f32(all_uint);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock3 {\n  @location(0) sint32: i32,\n  @location(1) sint32x2: vec2<i32>,\n  @location(2) sint32x3: vec3<i32>,\n  @location(3) sint32x4: vec4<i32>,\n}\n\n@vertex\nfn vertex_block_3(v_in: AttributeBlock3) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all sint into one checksum value.\n  var all_sint: i32 = 0;\n  all_sint = accumulate_sint(all_sint, v_in.sint32);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint32x2.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x2.y);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint32x3.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x3.y);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x3.z);\n\n  all_sint = accumulate_sint(all_sint, v_in.sint32x4.x);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x4.y);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x4.z);\n  all_sint = accumulate_sint(all_sint, v_in.sint32x4.w);\n\n  checksums[index_sint] = f32(all_sint);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock4{\n  @location(0) float32: f32,\n  @location(1) float32x2: vec2<f32>,\n  @location(2) float32x3: vec3<f32>,\n  @location(3) float32x4: vec4<f32>,\n  @location(4) float16x2: vec2<f32>,\n  @location(5) float16x4: vec4<f32>,\n  @location(6) float16: f32,\n}\n\n@vertex\nfn vertex_block_4(v_in: AttributeBlock4) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all float32 into one checksum value.\n  var all_float32: f32 = 0.0;\n  all_float32 = accumulate_float32(all_float32, v_in.float32);\n\n  all_float32 = accumulate_float32(all_float32, v_in.float32x2.x);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x2.y);\n\n  all_float32 = accumulate_float32(all_float32, v_in.float32x3.x);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x3.y);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x3.z);\n\n  all_float32 = accumulate_float32(all_float32, v_in.float32x4.x);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x4.y);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x4.z);\n  all_float32 = accumulate_float32(all_float32, v_in.float32x4.w);\n\n  checksums[index_float32] = f32(all_float32);\n\n  // Accumulate all float16 into one checksum value.\n  var all_float16: f32 = 0.0;\n  all_float16 = accumulate_float16(all_float16, v_in.float16x2.x);\n  all_float16 = accumulate_float16(all_float16, v_in.float16x2.y);\n\n  all_float16 = accumulate_float16(all_float16, v_in.float16x4.x);\n  all_float16 = accumulate_float16(all_float16, v_in.float16x4.y);\n  all_float16 = accumulate_float16(all_float16, v_in.float16x4.z);\n  all_float16 = accumulate_float16(all_float16, v_in.float16x4.w);\n\n  all_float16 = accumulate_float16(all_float16, v_in.float16);\n\n  checksums[index_float16] = f32(all_float16);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock5{\n  @location(0) unorm10_10_10_2: vec4<f32>,\n}\n\n@vertex\nfn vertex_block_5(v_in: AttributeBlock5) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all unorm into one checksum value.\n  var all_unorm: f32 = 0.0;\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm10_10_10_2.x);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm10_10_10_2.y);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm10_10_10_2.z);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm10_10_10_2.w);\n\n  checksums[index_unorm] = f32(all_unorm);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock6 {\n  @location(0) uint16: u32,\n  @location(1) sint16: i32,\n  @location(2) unorm16: f32,\n  @location(3) snorm16: f32,\n  @location(4) uint8: u32,\n  @location(5) sint8: i32,\n  @location(6) unorm8: f32,\n  @location(7) snorm8: f32,\n}\n\n@vertex\nfn vertex_block_6(v_in: AttributeBlock6) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all unorm into one checksum value.\n  var all_unorm: f32 = 0.0;\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm16);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8);\n  checksums[index_unorm] = f32(all_unorm);\n\n  // Accumulate all snorm into one checksum value.\n  var all_snorm: f32 = 0.0;\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm16);\n  all_snorm = accumulate_snorm(all_snorm, v_in.snorm8);\n  checksums[index_snorm] = f32(all_snorm);\n\n  // Accumulate all uint into one checksum value.\n  var all_uint: u32 = 0;\n  all_uint = accumulate_uint(all_uint, v_in.uint16);\n  all_uint = accumulate_uint(all_uint, v_in.uint8);\n  checksums[index_uint] = f32(all_uint);\n\n  // Accumulate all sint into one checksum value.\n  var all_sint: i32 = 0;\n  all_sint = accumulate_sint(all_sint, v_in.sint16);\n  all_sint = accumulate_sint(all_sint, v_in.sint8);\n  checksums[index_sint] = f32(all_sint);\n\n  return vec4(0.0);\n}\n\nstruct AttributeBlock7 {\n  @location(0) unorm8x4_bgra: vec4<f32>,\n}\n\n@vertex\nfn vertex_block_7(v_in: AttributeBlock7) -> @builtin(position) vec4<f32>\n{\n  init_checksums();\n\n  // Accumulate all unorm into one checksum value.\n  var all_unorm: f32 = 0.0;\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4_bgra.r);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4_bgra.g);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4_bgra.b);\n  all_unorm = accumulate_unorm(all_unorm, v_in.unorm8x4_bgra.a);\n\n  checksums[index_unorm] = f32(all_unorm);\n\n  return vec4(0.0);\n}\n\nfn accumulate_uint(accum: u32, val: u32) -> u32 {\n  return accum + val;\n}\n\nfn accumulate_sint(accum: i32, val: i32) -> i32 {\n  return accum + val;\n}\n\nfn accumulate_unorm(accum: f32, val: f32) -> f32 {\n  return accum + val;\n}\n\nfn accumulate_snorm(accum: f32, val: f32) -> f32 {\n  return accum + val;\n}\n\nfn accumulate_float16(accum: f32, val: f32) -> f32 {\n  return accum + val;\n}\n\nfn accumulate_float32(accum: f32, val: f32) -> f32 {\n  return accum + val;\n}\n\n@fragment\nfn fragment_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/vertex_formats/mod.rs",
    "content": "//! Tests that vertex formats pass through to vertex shaders accurately.\n\nuse std::num::NonZeroU64;\n\nuse wgpu::util::{BufferInitDescriptor, DeviceExt};\n\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.extend([VERTEX_FORMATS_ALL, VERTEX_FORMATS_10_10_10_2]);\n}\n\n#[derive(Debug, Copy, Clone)]\nenum TestCase {\n    UnormsAndSnorms,\n    UintsAndSintsSmall,\n    UintsBig,\n    SintsBig,\n    Floats,\n    Unorm1010102,\n    SingleSmallNormsAndInts,\n    Unorm8x4Bgra,\n}\n\nstruct Test<'a> {\n    case: TestCase,\n    entry_point: &'a str,\n    attributes: &'a [wgpu::VertexAttribute],\n    input: &'a [u8],\n    checksums: &'a [f32],\n}\n\nasync fn vertex_formats_all(ctx: TestingContext) {\n    let attributes_block_0 = &wgpu::vertex_attr_array![\n        0 => Unorm8x4,\n        1 => Unorm16x2,\n        2 => Unorm16x4,\n        3 => Snorm8x4,\n        4 => Snorm16x2,\n        5 => Snorm16x4,\n        6 => Unorm8x2,\n        7 => Snorm8x2,\n    ];\n\n    let attributes_block_1 = &wgpu::vertex_attr_array![\n        0 => Uint8x4,\n        1 => Uint16x2,\n        2 => Uint16x4,\n        3 => Sint8x4,\n        4 => Sint16x2,\n        5 => Sint16x4,\n        6 => Uint8x2,\n        7 => Sint8x2,\n    ];\n\n    let attributes_block_2 = &wgpu::vertex_attr_array![\n        0 => Uint32,\n        1 => Uint32x2,\n        2 => Uint32x3,\n        3 => Uint32x4,\n    ];\n\n    let attributes_block_3 = &wgpu::vertex_attr_array![\n        0 => Sint32,\n        1 => Sint32x2,\n        2 => Sint32x3,\n        3 => Sint32x4,\n    ];\n\n    let attributes_block_4 = &wgpu::vertex_attr_array![\n        0 => Float32,\n        1 => Float32x2,\n        2 => Float32x3,\n        3 => Float32x4,\n        4 => Float16x2,\n        5 => Float16x4,\n        6 => Float16,\n    ];\n\n    let attributes_block_6 = &wgpu::vertex_attr_array![\n        0 => Uint16,\n        1 => Sint16,\n        2 => Unorm16,\n        3 => Snorm16,\n        4 => Uint8,\n        5 => Sint8,\n        6 => Unorm8,\n        7 => Snorm8,\n    ];\n\n    let attributes_block_7 = &wgpu::vertex_attr_array![\n        0 => Unorm8x4Bgra,\n    ];\n\n    let tests = vec![\n        Test {\n            case: TestCase::UnormsAndSnorms,\n            entry_point: \"vertex_block_0\",\n            attributes: attributes_block_0,\n            input: &[\n                128u8, 128u8, 128u8, 128u8, // Unorm8x4 (0.5, 0.5, 0.5, 0.5)\n                0u8, 128u8, 0u8, 128u8, // Unorm16x2 (0.5, 0.5)\n                0u8, 64u8, 0u8, 64u8, 0u8, 64u8, 0u8,\n                64u8, // Unorm16x4 (0.25, 0.25, 0.25, 0.25)\n                127u8, 127u8, 127u8, 127u8, // Snorm8x4 (1, 1, 1, 1)\n                0u8, 128u8, 0u8, 128u8, // Snorm16x2 (-1, -1)\n                255u8, 127u8, 255u8, 127u8, 255u8, 127u8, 255u8,\n                127u8, // Snorm16x4 (1, 1, 1, 1)\n                255u8, 255u8, // Unorm8x2 (1, 1)\n                128u8, 128u8, // Snorm8x2 (-1, -1)\n            ],\n            checksums: &[0.0, 0.0, 6.0, 4.0, 0.0, 0.0],\n        },\n        Test {\n            case: TestCase::UintsAndSintsSmall,\n            entry_point: \"vertex_block_1\",\n            attributes: attributes_block_1,\n            input: &[\n                4u8, 8u8, 16u8, 32u8, // Uint8x4 (4, 8, 16, 32)\n                64u8, 0u8, 128u8, 0u8, // Uint16x2 (64, 128)\n                0u8, 1u8, 0u8, 2u8, 0u8, 4u8, 0u8, 8u8, // Uint16x4 (256, 512, 1024, 2048)\n                127u8, 127u8, 2u8, 0u8, // Sint8x4 (127, 127, 2, 0)\n                255u8, 255u8, 1u8, 0u8, // Sint16x2 (-1, 1)\n                128u8, 255u8, 128u8, 255u8, 0u8, 1u8, 240u8,\n                255u8, // Sint16x4 (-128, -128, 256, -16)\n                1u8, 2u8, // Uint8x2 (1, 2)\n                128u8, 128u8, // Sint8x2 (-128, -128)\n            ],\n            checksums: &[4095.0, -16.0, 0.0, 0.0, 0.0, 0.0],\n        },\n        Test {\n            case: TestCase::UintsBig,\n            entry_point: \"vertex_block_2\",\n            attributes: attributes_block_2,\n            input: &[\n                1u8, 0u8, 0u8, 0u8, // Uint32x2 (1)\n                2u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 0u8, // Uint32x2 (2, 4)\n                8u8, 0u8, 0u8, 0u8, 16u8, 0u8, 0u8, 0u8, 32u8, 0u8, 0u8,\n                0u8, // Uint32x3 (8, 16, 32)\n                64u8, 0u8, 0u8, 0u8, 128u8, 0u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 2u8, 0u8,\n                0u8, // Uint32x4 (64, 128, 256, 512)\n            ],\n            checksums: &[1023.0, 0.0, 0.0, 0.0, 0.0, 0.0],\n        },\n        Test {\n            case: TestCase::SintsBig,\n            entry_point: \"vertex_block_3\",\n            attributes: attributes_block_3,\n            input: &[\n                128u8, 255u8, 255u8, 255u8, // Sint32 (-128)\n                120u8, 0u8, 0u8, 0u8, 8u8, 0u8, 0u8, 0u8, // Sint32x2 (120, 8)\n                252u8, 255u8, 255u8, 255u8, 2u8, 0u8, 0u8, 0u8, 2u8, 0u8, 0u8,\n                0u8, // Sint32x3 (-4, 2, 2)\n                24u8, 252u8, 255u8, 255u8, 88u8, 2u8, 0u8, 0u8, 44u8, 1u8, 0u8, 0u8, 99u8, 0u8,\n                0u8, 0u8, // Sint32x4 (-1000, 600, 300, 99)\n            ],\n            checksums: &[0.0, -1.0, 0.0, 0.0, 0.0, 0.0],\n        },\n        Test {\n            case: TestCase::Floats,\n            entry_point: \"vertex_block_4\",\n            attributes: attributes_block_4,\n            input: &[\n                0u8, 0u8, 0u8, 63u8, // Float32 (0.5)\n                0u8, 0u8, 0u8, 191u8, 0u8, 0u8, 128u8, 64u8, // Float32x2 (-0.5, 4.0)\n                0u8, 0u8, 0u8, 192u8, 0u8, 0u8, 204u8, 194u8, 0u8, 0u8, 200u8,\n                66u8, // Float32x3 (-2.0, -102.0, 100.0)\n                0u8, 0u8, 92u8, 66u8, 0u8, 0u8, 72u8, 194u8, 0u8, 0u8, 32u8, 65u8, 0u8, 0u8, 128u8,\n                63u8, // Float32x4 (55.0, -50.0, 10.0, 1.0)\n                0u8, 68u8, // Float16 (4.0)\n                0u8, 60u8, 72u8, 53u8, // Float16x2 (1.0, 0.33)\n                72u8, 57u8, 0u8, 192u8, 0u8, 188u8, 0u8,\n                184u8, // Float16x4 (0.66, -2.0, -1.0, -0.5)\n            ],\n            checksums: &[0.0, 0.0, 0.0, 0.0, 2.5, 16.0],\n        },\n        Test {\n            case: TestCase::SingleSmallNormsAndInts,\n            entry_point: \"vertex_block_6\",\n            attributes: attributes_block_6,\n            input: &[\n                1u8, 2u8, // Uint16 (513)\n                1u8, 2u8, // Sint16 (513)\n                0u8, 64u8, // Unorm16 (0.25)\n                0u8, 64u8,  // Snorm16 (0.5)\n                32u8,  // Uint8 (32)\n                255u8, // Sint8 (-1)\n                128u8, // Unorm8 (0.5)\n                128u8, // Snorm8 (-1)\n            ],\n            checksums: &[513.0 + 32.0, 513.0 - 1.0, 0.25 + 0.5, 0.5 - 1.0, 0.0, 0.0],\n        },\n        Test {\n            case: TestCase::Unorm8x4Bgra,\n            entry_point: \"vertex_block_7\",\n            attributes: attributes_block_7,\n            input: &[\n                128u8, 85u8, 170u8, 64u8, // Unorm8x4Bgra (0.67, 0.33, 0.5, 0.25)\n            ],\n            checksums: &[0.0, 0.0, 1.75, 0.0, 0.0, 0.0],\n        },\n    ];\n\n    vertex_formats_common(ctx, &tests).await;\n}\n\nasync fn vertex_formats_10_10_10_2(ctx: TestingContext) {\n    let attributes_block_5 = &wgpu::vertex_attr_array![\n        0 => Unorm10_10_10_2,\n    ];\n\n    let tests = vec![Test {\n        case: TestCase::Unorm1010102,\n        entry_point: \"vertex_block_5\",\n        attributes: attributes_block_5,\n        input: &[\n            // We are aiming for rgba of (0.5, 0.5, 0.5, 0.66)\n            // Packing   AA BB BBBB BBBB GGGG GGGG GG RR RRRR RRRR\n            // Binary    10 10 0000 0000 1000 0000 00 10 0000 0000\n            // Hex               A0        08         02        00\n            // Decimal          160         8          2         0\n            // unorm   0.66          0.5          0.5          0.5 = 2.16\n            0u8, 2u8, 8u8, 160u8, // Unorm10_10_10_2\n        ],\n        checksums: &[0.0, 0.0, 2.16, 0.0, 0.0, 0.0],\n    }];\n\n    vertex_formats_common(ctx, &tests).await;\n}\n\nasync fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) {\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"draw.vert.wgsl\"));\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(4),\n                },\n                visibility: wgpu::ShaderStages::VERTEX,\n                count: None,\n            }],\n        });\n\n    let ppl = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let dummy = ctx\n        .device\n        .create_texture_with_data(\n            &ctx.queue,\n            &wgpu::TextureDescriptor {\n                label: Some(\"dummy\"),\n                size: wgpu::Extent3d {\n                    width: 1,\n                    height: 1,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8Unorm,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,\n                view_formats: &[],\n            },\n            wgpu::util::TextureDataOrder::LayerMajor,\n            &[0, 0, 0, 1],\n        )\n        .create_view(&wgpu::TextureViewDescriptor::default());\n\n    let mut failed = false;\n    for test in tests {\n        let buffer_input = ctx.device.create_buffer_init(&BufferInitDescriptor {\n            label: None,\n            contents: bytemuck::cast_slice(test.input),\n            usage: wgpu::BufferUsages::VERTEX,\n        });\n\n        let pipeline_desc = wgpu::RenderPipelineDescriptor {\n            label: None,\n            layout: Some(&ppl),\n            vertex: wgpu::VertexState {\n                buffers: &[wgpu::VertexBufferLayout {\n                    array_stride: 0, // Calculate, please!\n                    step_mode: wgpu::VertexStepMode::Vertex,\n                    attributes: test.attributes,\n                }],\n                module: &shader,\n                entry_point: Some(test.entry_point),\n                compilation_options: Default::default(),\n            },\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fragment_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(wgpu::ColorTargetState {\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    blend: None,\n                    write_mask: wgpu::ColorWrites::ALL,\n                })],\n            }),\n            multiview_mask: None,\n            cache: None,\n        };\n\n        let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n        let expected = test.checksums;\n        let buffer_size = (size_of_val(&expected[0]) * expected.len()) as u64;\n        let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size,\n            usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::STORAGE,\n            mapped_at_creation: false,\n        });\n\n        let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: gpu_buffer.as_entire_binding(),\n            }],\n        });\n\n        let mut encoder1 = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let mut rpass = encoder1.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &dummy,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        rpass.set_vertex_buffer(0, buffer_input.slice(..));\n        rpass.set_pipeline(&pipeline);\n        rpass.set_bind_group(0, &bg, &[]);\n\n        // Draw three vertices and no instance, which is enough to generate the\n        // checksums.\n        rpass.draw(0..3, 0..1);\n\n        drop(rpass);\n\n        let mut encoder2 = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        encoder2.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size);\n\n        // See https://github.com/gfx-rs/wgpu/issues/4732 for why this is split between two submissions\n        // with a hard wait in between.\n        ctx.queue.submit([encoder1.finish()]);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        ctx.queue.submit([encoder2.finish()]);\n        let slice = cpu_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| ());\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let data: Vec<f32> = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec();\n\n        let case_name = format!(\"Case {:?}\", test.case);\n\n        // Calculate the difference between data and expected. Since the data is\n        // a bunch of float checksums, we allow a fairly large epsilon, which helps\n        // with the accumulation of float rounding errors.\n        const EPSILON: f32 = 0.01;\n\n        let mut deltas = data.iter().zip(expected.iter()).map(|(d, e)| (d - e).abs());\n        if deltas.any(|x| x > EPSILON) {\n            eprintln!(\"Failed: Got: {data:?} Expected: {expected:?} - {case_name}\",);\n            failed = true;\n            continue;\n        }\n\n        eprintln!(\"Passed: {case_name}\");\n    }\n\n    assert!(!failed);\n}\n\n#[gpu_test]\nstatic VERTEX_FORMATS_ALL: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::VERTEX_WRITABLE_STORAGE)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"vertexAttributeAccessBeyondStride\"),\n            ),\n    )\n    .run_async(vertex_formats_all);\n\n// Some backends can handle Unorm-10-10-2, but GL backends seem to throw this error:\n// Validation Error: GL_INVALID_ENUM in glVertexAttribFormat(type = GL_UNSIGNED_INT_10_10_10_2)\n#[gpu_test]\nstatic VERTEX_FORMATS_10_10_10_2: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::VERTEX_WRITABLE_STORAGE)\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"vertexAttributeAccessBeyondStride\"),\n            ),\n    )\n    .run_async(vertex_formats_10_10_10_2);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/vertex_indices/draw.vert.wgsl",
    "content": "@group(0) @binding(0)\nvar<storage, read_write> indices: array<u32>; // this is used as both input and output for convenience\n\n@vertex\nfn vs_main_builtin(@builtin(instance_index) instance: u32, @builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> {\n    return vs_inner(instance, index);\n}\n\n@vertex\nfn vs_main_buffers(@location(0) instance: u32, @location(1) index: u32) -> @builtin(position) vec4<f32> {\n    return vs_inner(instance, index);\n}\n\nfn vs_inner(instance: u32, index: u32) -> vec4<f32> {\n    let idx = instance * 3u + index;\n    indices[idx] = idx;\n    return vec4<f32>(0.0, 0.0, 0.0, 1.0);\n}\n\n@fragment\nfn fs_main() -> @location(0) vec4<f32> {\n    return vec4<f32>(0.0);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/vertex_indices/mod.rs",
    "content": "//! Tests that vertex buffers, vertex indices, and instance indices are properly handled.\n//!\n//! We need tests for these as the backends use various schemes to work around the lack\n//! of support for things like `gl_BaseInstance` in shaders.\n\nuse std::{num::NonZeroU64, ops::Range};\n\nuse itertools::Itertools;\nuse strum::IntoEnumIterator;\nuse wgpu::util::{BufferInitDescriptor, DeviceExt, RenderEncoder};\nuse wgpu::RenderBundleDescriptor;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};\n\npub fn all_tests(vec: &mut Vec<wgpu_test::GpuTestInitializer>) {\n    vec.push(VERTEX_INDICES);\n}\n\n/// Generic struct representing a draw call\nstruct Draw {\n    vertex: Range<u32>,\n    instance: Range<u32>,\n    /// If present, is an indexed call\n    base_vertex: Option<i32>,\n}\n\nimpl Draw {\n    /// Directly execute the draw call\n    fn execute(&self, rpass: &mut dyn RenderEncoder<'_>) {\n        if let Some(base_vertex) = self.base_vertex {\n            rpass.draw_indexed(self.vertex.clone(), base_vertex, self.instance.clone());\n        } else {\n            rpass.draw(self.vertex.clone(), self.instance.clone());\n        }\n    }\n\n    /// Add the draw call to the given indirect buffer\n    fn add_to_buffer(&self, bytes: &mut Vec<u8>, features: wgpu::Features) {\n        // The behavior of non-zero first_instance in indirect draw calls in currently undefined if INDIRECT_FIRST_INSTANCE is not supported.\n        let supports_first_instance = features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE);\n        let first_instance = if supports_first_instance {\n            self.instance.start\n        } else {\n            0\n        };\n\n        if let Some(base_vertex) = self.base_vertex {\n            bytes.extend_from_slice(\n                wgpu::util::DrawIndexedIndirectArgs {\n                    index_count: self.vertex.end - self.vertex.start,\n                    instance_count: self.instance.end - self.instance.start,\n                    base_vertex,\n                    first_index: self.vertex.start,\n                    first_instance,\n                }\n                .as_bytes(),\n            )\n        } else {\n            bytes.extend_from_slice(\n                wgpu::util::DrawIndirectArgs {\n                    vertex_count: self.vertex.end - self.vertex.start,\n                    instance_count: self.instance.end - self.instance.start,\n                    first_vertex: self.vertex.start,\n                    first_instance,\n                }\n                .as_bytes(),\n            )\n        }\n    }\n\n    /// Execute the draw call from the given indirect buffer\n    fn execute_indirect<'rpass>(\n        &self,\n        rpass: &mut dyn RenderEncoder<'rpass>,\n        indirect: &'rpass wgpu::Buffer,\n        offset: &mut u64,\n    ) {\n        if self.base_vertex.is_some() {\n            rpass.draw_indexed_indirect(indirect, *offset);\n            *offset += 20;\n        } else {\n            rpass.draw_indirect(indirect, *offset);\n            *offset += 16;\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, strum::EnumIter)]\nenum TestCase {\n    /// A single draw call with 6 vertices\n    Draw,\n    /// Two draw calls of 0..3 and 3..6 verts\n    DrawNonZeroFirstVertex,\n    /// A single draw call with 6 vertices and a vertex offset of 3\n    DrawBaseVertex,\n    /// A single draw call with 3 vertices and 2 instances\n    DrawInstanced,\n    /// Two draw calls with 3 vertices and 0..1 and 1..2 instances.\n    DrawNonZeroFirstInstance,\n}\n\nimpl TestCase {\n    // Get the draw calls for this test case\n    fn draws(&self) -> &'static [Draw] {\n        match self {\n            TestCase::Draw => &[Draw {\n                vertex: 0..6,\n                instance: 0..1,\n                base_vertex: None,\n            }],\n            TestCase::DrawNonZeroFirstVertex => &[\n                Draw {\n                    vertex: 0..3,\n                    instance: 0..1,\n                    base_vertex: None,\n                },\n                Draw {\n                    vertex: 3..6,\n                    instance: 0..1,\n                    base_vertex: None,\n                },\n            ],\n            TestCase::DrawBaseVertex => &[Draw {\n                vertex: 0..6,\n                instance: 0..1,\n                base_vertex: Some(3),\n            }],\n            TestCase::DrawInstanced => &[Draw {\n                vertex: 0..3,\n                instance: 0..2,\n                base_vertex: None,\n            }],\n            TestCase::DrawNonZeroFirstInstance => &[\n                Draw {\n                    vertex: 0..3,\n                    instance: 0..1,\n                    base_vertex: None,\n                },\n                Draw {\n                    vertex: 0..3,\n                    instance: 1..2,\n                    base_vertex: None,\n                },\n            ],\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, strum::EnumIter)]\nenum IdSource {\n    /// Use buffers to load the vertex and instance index\n    Buffers,\n    /// Use builtins to load the vertex and instance index\n    Builtins,\n}\n\n#[derive(Debug, Copy, Clone, strum::EnumIter)]\nenum DrawCallKind {\n    Direct,\n    Indirect,\n}\n\n#[derive(Debug, Copy, Clone, strum::EnumIter)]\nenum EncoderKind {\n    RenderPass,\n    RenderBundle,\n}\n\nstruct Test {\n    case: TestCase,\n    id_source: IdSource,\n    draw_call_kind: DrawCallKind,\n    encoder_kind: EncoderKind,\n}\n\nimpl Test {\n    /// Get the expected result from this test, taking into account\n    /// the various features and capabilities that may be missing.\n    fn expectation(&self, ctx: &TestingContext) -> &'static [u32] {\n        let is_indirect = matches!(self.draw_call_kind, DrawCallKind::Indirect);\n\n        // Both of these failure modes require indirect rendering\n\n        // If this is false, the first instance will be ignored.\n        let non_zero_first_instance_supported = ctx\n            .adapter\n            .features()\n            .contains(wgpu::Features::INDIRECT_FIRST_INSTANCE)\n            || !is_indirect;\n\n        match self.case {\n            TestCase::DrawBaseVertex => &[0, 0, 0, 3, 4, 5, 6, 7, 8],\n            TestCase::Draw | TestCase::DrawInstanced => &[0, 1, 2, 3, 4, 5],\n            TestCase::DrawNonZeroFirstVertex => &[0, 1, 2, 3, 4, 5],\n            TestCase::DrawNonZeroFirstInstance => {\n                if !non_zero_first_instance_supported {\n                    return &[0, 1, 2, 0, 0, 0];\n                }\n\n                &[0, 1, 2, 3, 4, 5]\n            }\n        }\n    }\n}\n\nasync fn vertex_index_common(ctx: TestingContext) {\n    let identity_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: Some(\"identity buffer\"),\n        contents: bytemuck::cast_slice(&[0u32, 1, 2, 3, 4, 5, 6, 7, 8]),\n        usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::INDEX,\n    });\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::include_wgsl!(\"draw.vert.wgsl\"));\n\n    let bgl = ctx\n        .device\n        .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(4),\n                },\n                visibility: wgpu::ShaderStages::VERTEX,\n                count: None,\n            }],\n        });\n\n    let ppl = ctx\n        .device\n        .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: &[Some(&bgl)],\n            immediate_size: 0,\n        });\n\n    let mut pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: Some(&ppl),\n        vertex: wgpu::VertexState {\n            buffers: &[],\n            module: &shader,\n            entry_point: Some(\"vs_main_builtin\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::Rgba8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let builtin_pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    pipeline_desc.vertex.entry_point = Some(\"vs_main_buffers\");\n    pipeline_desc.vertex.buffers = &[\n        wgpu::VertexBufferLayout {\n            array_stride: 4,\n            step_mode: wgpu::VertexStepMode::Instance,\n            attributes: &wgpu::vertex_attr_array![0 => Uint32],\n        },\n        wgpu::VertexBufferLayout {\n            array_stride: 4,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &wgpu::vertex_attr_array![1 => Uint32],\n        },\n    ];\n    let buffer_pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n\n    let dummy = ctx\n        .device\n        .create_texture_with_data(\n            &ctx.queue,\n            &wgpu::TextureDescriptor {\n                label: Some(\"dummy\"),\n                size: wgpu::Extent3d {\n                    width: 1,\n                    height: 1,\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8Unorm,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,\n                view_formats: &[],\n            },\n            wgpu::util::TextureDataOrder::LayerMajor,\n            &[0, 0, 0, 1],\n        )\n        .create_view(&wgpu::TextureViewDescriptor::default());\n\n    let tests = TestCase::iter()\n        .cartesian_product(IdSource::iter())\n        .cartesian_product(DrawCallKind::iter())\n        .cartesian_product(EncoderKind::iter())\n        .map(|(((case, id_source), draw_call_kind), encoder_kind)| Test {\n            case,\n            id_source,\n            draw_call_kind,\n            encoder_kind,\n        })\n        .collect::<Vec<_>>();\n\n    let features = ctx.adapter.features();\n\n    let mut failed = false;\n    for test in tests {\n        let pipeline = match test.id_source {\n            IdSource::Buffers => &buffer_pipeline,\n            IdSource::Builtins => &builtin_pipeline,\n        };\n\n        let expected = test.expectation(&ctx);\n\n        let buffer_size = (size_of_val(&expected[0]) * expected.len()) as u64;\n        let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n\n        let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: buffer_size,\n            usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::STORAGE,\n            mapped_at_creation: false,\n        });\n\n        let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: gpu_buffer.as_entire_binding(),\n            }],\n        });\n\n        let mut encoder1 = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        let render_bundle;\n        let indirect_buffer;\n        let mut rpass = encoder1.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &dummy,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        {\n            // Need to scope render_bundle_encoder since it's not Send and would otherwise\n            // infect the function if not going out of scope before an await call.\n            // (it is dropped via `take` + `finish` earlier, but compiler does not take this into account)\n            let mut render_bundle_encoder = match test.encoder_kind {\n                EncoderKind::RenderPass => None,\n                EncoderKind::RenderBundle => Some(ctx.device.create_render_bundle_encoder(\n                    &wgpu::RenderBundleEncoderDescriptor {\n                        label: Some(\"test renderbundle encoder\"),\n                        color_formats: &[Some(wgpu::TextureFormat::Rgba8Unorm)],\n                        depth_stencil: None,\n                        sample_count: 1,\n                        multiview: None,\n                    },\n                )),\n            };\n\n            let render_encoder: &mut dyn RenderEncoder = render_bundle_encoder\n                .as_mut()\n                .map(|r| r as &mut dyn RenderEncoder)\n                .unwrap_or(&mut rpass);\n\n            render_encoder.set_vertex_buffer(0, identity_buffer.slice(..));\n            render_encoder.set_vertex_buffer(1, identity_buffer.slice(..));\n            render_encoder.set_index_buffer(identity_buffer.slice(..), wgpu::IndexFormat::Uint32);\n            render_encoder.set_pipeline(pipeline);\n            render_encoder.set_bind_group(0, Some(&bg), &[]);\n\n            let draws = test.case.draws();\n\n            match test.draw_call_kind {\n                DrawCallKind::Direct => {\n                    for draw in draws {\n                        draw.execute(render_encoder);\n                    }\n                }\n                DrawCallKind::Indirect => {\n                    let mut indirect_bytes = Vec::new();\n                    for draw in draws {\n                        draw.add_to_buffer(&mut indirect_bytes, features);\n                    }\n                    indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n                        label: Some(\"indirect\"),\n                        contents: &indirect_bytes,\n                        usage: wgpu::BufferUsages::INDIRECT,\n                    });\n                    let mut offset = 0;\n                    for draw in draws {\n                        draw.execute_indirect(render_encoder, &indirect_buffer, &mut offset);\n                    }\n                }\n            }\n\n            if let Some(render_bundle_encoder) = render_bundle_encoder.take() {\n                render_bundle = render_bundle_encoder.finish(&RenderBundleDescriptor {\n                    label: Some(\"test renderbundle\"),\n                });\n                rpass.execute_bundles([&render_bundle]);\n            }\n        }\n\n        drop(rpass);\n\n        let mut encoder2 = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        encoder2.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size);\n\n        // See https://github.com/gfx-rs/wgpu/issues/4732 for why this is split between two submissions\n        // with a hard wait in between.\n        ctx.queue.submit([encoder1.finish()]);\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        ctx.queue.submit([encoder2.finish()]);\n        let slice = cpu_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| ());\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let data: Vec<u32> = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec();\n\n        let case_name = format!(\n            \"Case {:?} getting indices from {:?} using {:?} draw calls, encoded with a {:?}\",\n            test.case, test.id_source, test.draw_call_kind, test.encoder_kind\n        );\n        if data != expected {\n            eprintln!(\"Failed: Got: {data:?} Expected: {expected:?} - {case_name}\",);\n            failed = true;\n        } else {\n            eprintln!(\"Passed: {case_name}\");\n        }\n    }\n\n    assert!(!failed);\n}\n\n#[gpu_test]\nstatic VERTEX_INDICES: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .test_features_limits()\n            .features(wgpu::Features::VERTEX_WRITABLE_STORAGE)\n            .features(wgpu::Features::INDIRECT_FIRST_INSTANCE),\n    )\n    .run_async(vertex_index_common);\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/vertex_state.rs",
    "content": "use wgpu::{\n    util::{BufferInitDescriptor, DeviceExt},\n    vertex_attr_array,\n};\nuse wgpu_test::{\n    gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.push(SET_ARRAY_STRIDE_TO_0);\n}\n\n#[gpu_test]\nstatic SET_ARRAY_STRIDE_TO_0: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .limits(wgpu::Limits::downlevel_defaults())\n            // https://github.com/gfx-rs/wgpu/issues/9184\n            .expect_fail(\n                wgpu_test::FailureCase::molten_vk()\n                    .validation_error(\"vertexAttributeAccessBeyondStride\"),\n            ),\n    )\n    .run_async(set_array_stride_to_0);\n\n/// Tests that draws using a vertex buffer with stride of 0 works correctly (especially on the\n/// D3D12 backend; see commentary within).\nasync fn set_array_stride_to_0(ctx: TestingContext) {\n    let position_buffer_content: &[f32; 12] = &[\n        // Triangle 1\n        -1.0, -1.0, // Bottom left\n        1.0, 1.0, // Top right\n        -1.0, 1.0, // Top left\n        // Triangle 2\n        -1.0, -1.0, // Bottom left\n        1.0, -1.0, // Bottom right\n        1.0, 1.0, // Top right\n    ];\n    let position_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice::<f32, u8>(position_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let color_buffer_content: &[f32; 4] = &[1.0, 1.0, 1.0, 1.0];\n    let color_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {\n        label: None,\n        contents: bytemuck::cast_slice::<f32, u8>(color_buffer_content),\n        usage: wgpu::BufferUsages::VERTEX,\n    });\n\n    let shader_src = \"\n        struct VertexOutput {\n            @builtin(position) position: vec4f,\n            @location(0) color: vec4f,\n        }\n\n        @vertex\n        fn vs_main(@location(0) position: vec2f, @location(1) color: vec4f) -> VertexOutput {\n            return VertexOutput(vec4f(position, 0.0, 1.0), color);\n        }\n\n        @fragment\n        fn fs_main(@location(0) color: vec4f) -> @location(0) vec4f {\n            return color;\n        }\n    \";\n\n    let shader = ctx\n        .device\n        .create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: None,\n            source: wgpu::ShaderSource::Wgsl(shader_src.into()),\n        });\n\n    let vbl = [\n        wgpu::VertexBufferLayout {\n            array_stride: 8,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &vertex_attr_array![0 => Float32x2],\n        },\n        wgpu::VertexBufferLayout {\n            array_stride: 0,\n            step_mode: wgpu::VertexStepMode::Vertex,\n            attributes: &vertex_attr_array![1 => Float32x4],\n        },\n    ];\n    let pipeline_desc = wgpu::RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: wgpu::VertexState {\n            buffers: &vbl,\n            module: &shader,\n            entry_point: Some(\"vs_main\"),\n            compilation_options: Default::default(),\n        },\n        primitive: wgpu::PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: wgpu::MultisampleState::default(),\n        fragment: Some(wgpu::FragmentState {\n            module: &shader,\n            entry_point: Some(\"fs_main\"),\n            compilation_options: Default::default(),\n            targets: &[Some(wgpu::ColorTargetState {\n                format: wgpu::TextureFormat::R8Unorm,\n                blend: None,\n                write_mask: wgpu::ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    };\n    let mut first_pipeline_desc = pipeline_desc.clone();\n    let mut first_vbl = vbl.clone();\n    first_vbl[1].array_stride = 16;\n    first_pipeline_desc.vertex.buffers = &first_vbl;\n    let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);\n    let first_pipeline = ctx.device.create_render_pipeline(&first_pipeline_desc);\n\n    let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n    let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256 * 256,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = ctx\n        .device\n        .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n    {\n        let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: None,\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                ops: wgpu::Operations::default(),\n                resolve_target: None,\n                view: &out_texture_view,\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n\n        // The D3D12 backend used to not set the stride of vertex buffers if it was 0.\n        rpass.set_pipeline(&first_pipeline); // This call caused the D3D12 backend to set the stride for the 2nd vertex buffer to 16.\n        rpass.set_pipeline(&pipeline); // This call doesn't set the stride for the 2nd vertex buffer to 0.\n        rpass.set_vertex_buffer(0, position_buffer.slice(..));\n        rpass.set_vertex_buffer(1, color_buffer.slice(..));\n        rpass.draw(0..6, 0..1); // Causing this draw to be skipped since it would read OOB of the 2nd vertex buffer.\n    }\n\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &out_texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &readback_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(256),\n                rows_per_image: None,\n            },\n        },\n        wgpu::Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n    );\n\n    ctx.queue.submit([encoder.finish()]);\n\n    let slice = readback_buffer.slice(..);\n    slice.map_async(wgpu::MapMode::Read, |_| ());\n\n    ctx.async_poll(wgpu::PollType::wait_indefinitely())\n        .await\n        .unwrap();\n\n    let data = slice.get_mapped_range();\n    let succeeded = data.iter().all(|b| *b == u8::MAX);\n    assert!(succeeded);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/write_texture.rs",
    "content": "//! Tests for texture copy\n\nuse wgpu::*;\nuse wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        WRITE_TEXTURE_SUBSET_2D,\n        WRITE_TEXTURE_SUBSET_3D,\n        WRITE_TEXTURE_NO_OOB,\n        WRITE_TEXTURE_VIA_STAGING_BUFFER,\n    ]);\n}\n\n#[gpu_test]\nstatic WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        let size = 256;\n\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size: wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: 1,\n            },\n            format: wgpu::TextureFormat::R8Uint,\n            usage: wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::COPY_SRC\n                | wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let data = vec![1u8; size as usize * 2];\n        // Write the first two rows\n        ctx.queue.write_texture(\n            wgpu::TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            &data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size),\n                rows_per_image: Some(size),\n            },\n            wgpu::Extent3d {\n                width: size,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        ctx.queue.submit(None);\n\n        let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: (size * size) as u64,\n            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &read_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(size),\n                    rows_per_image: Some(size),\n                },\n            },\n            wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let slice = read_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| ());\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let data: Vec<u8> = slice.get_mapped_range().to_vec();\n\n        for byte in &data[..(size as usize * 2)] {\n            assert_eq!(*byte, 1);\n        }\n        for byte in &data[(size as usize * 2)..] {\n            assert_eq!(*byte, 0);\n        }\n    });\n\n#[gpu_test]\nstatic WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration =\n    GpuTestConfiguration::new().run_async(|ctx| async move {\n        let size = 256;\n        let depth = 4;\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D3,\n            size: wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: depth,\n            },\n            format: wgpu::TextureFormat::R8Uint,\n            usage: wgpu::TextureUsages::COPY_DST\n                | wgpu::TextureUsages::COPY_SRC\n                | wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let data = vec![1u8; (size * size) as usize * 2];\n        // Write the first two slices\n        ctx.queue.write_texture(\n            wgpu::TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            &data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size),\n                rows_per_image: Some(size),\n            },\n            wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: 2,\n            },\n        );\n\n        ctx.queue.submit(None);\n\n        let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {\n            label: None,\n            size: (size * size * depth) as u64,\n            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n        encoder.copy_texture_to_buffer(\n            wgpu::TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            wgpu::TexelCopyBufferInfo {\n                buffer: &read_buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(size),\n                    rows_per_image: Some(size),\n                },\n            },\n            wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: depth,\n            },\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let slice = read_buffer.slice(..);\n        slice.map_async(wgpu::MapMode::Read, |_| ());\n        ctx.async_poll(wgpu::PollType::wait_indefinitely())\n            .await\n            .unwrap();\n        let data: Vec<u8> = slice.get_mapped_range().to_vec();\n\n        for byte in &data[..((size * size) as usize * 2)] {\n            assert_eq!(*byte, 1);\n        }\n        for byte in &data[((size * size) as usize * 2)..] {\n            assert_eq!(*byte, 0);\n        }\n    });\n\n#[gpu_test]\nstatic WRITE_TEXTURE_NO_OOB: GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().enable_noop())\n    .run_async(|ctx| async move {\n        let size = 256;\n\n        let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size: wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth_or_array_layers: 1,\n            },\n            format: wgpu::TextureFormat::R8Uint,\n            usage: wgpu::TextureUsages::COPY_DST,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        let data = vec![1u8; size as usize * 2 + 100]; // check that we don't attempt to copy OOB internally by adding 100 bytes here\n        ctx.queue.write_texture(\n            wgpu::TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: wgpu::Origin3d::ZERO,\n                aspect: wgpu::TextureAspect::All,\n            },\n            &data,\n            wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size),\n                rows_per_image: Some(size),\n            },\n            wgpu::Extent3d {\n                width: size,\n                height: 2,\n                depth_or_array_layers: 1,\n            },\n        );\n    });\n\n// Test a writeTexture operation that will use the staging buffer.\n// If run with the address sanitizer, this serves as a regression\n// test for https://github.com/gfx-rs/wgpu/pull/7893.\n#[gpu_test]\nstatic WRITE_TEXTURE_VIA_STAGING_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()\n    .run_async(|ctx| async move {\n        let width = 89;\n        let height = 17;\n\n        let tex = ctx.device.create_texture(&TextureDescriptor {\n            label: None,\n            dimension: TextureDimension::D2,\n            size: Extent3d {\n                width,\n                height,\n                depth_or_array_layers: 1,\n            },\n            format: TextureFormat::R8Uint,\n            usage: TextureUsages::COPY_DST\n                | TextureUsages::COPY_SRC\n                | TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n\n        let write_width: u32 = 31;\n        let write_height: u32 = 5;\n        let write_bytes_per_row: u32 = 113;\n        let write_data = (0..(write_height - 1) * write_bytes_per_row + write_width)\n            .map(|b| (b % 256) as u8)\n            .collect::<Vec<_>>();\n\n        ctx.queue.write_texture(\n            TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: Origin3d::ZERO,\n                aspect: TextureAspect::All,\n            },\n            &write_data,\n            TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(write_bytes_per_row),\n                rows_per_image: Some(19),\n            },\n            Extent3d {\n                width: write_width,\n                height: write_height,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        ctx.queue.submit(None);\n\n        let read_bytes_per_row = wgt::COPY_BYTES_PER_ROW_ALIGNMENT;\n        let read_buffer = ctx.device.create_buffer(&BufferDescriptor {\n            label: None,\n            size: (height * read_bytes_per_row) as u64,\n            usage: BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        });\n\n        let mut encoder = ctx\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor { label: None });\n\n        encoder.copy_texture_to_buffer(\n            TexelCopyTextureInfo {\n                texture: &tex,\n                mip_level: 0,\n                origin: Origin3d::ZERO,\n                aspect: TextureAspect::All,\n            },\n            TexelCopyBufferInfo {\n                buffer: &read_buffer,\n                layout: TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(read_bytes_per_row),\n                    rows_per_image: Some(height),\n                },\n            },\n            Extent3d {\n                width,\n                height,\n                depth_or_array_layers: 1,\n            },\n        );\n\n        ctx.queue.submit(Some(encoder.finish()));\n\n        let slice = read_buffer.slice(..);\n        slice.map_async(MapMode::Read, |_| ());\n        ctx.async_poll(PollType::wait_indefinitely()).await.unwrap();\n        let read_data: Vec<u8> = slice.get_mapped_range().to_vec();\n\n        for x in 0..write_width {\n            for y in 0..write_height {\n                assert_eq!(\n                    read_data[(y * read_bytes_per_row + x) as usize],\n                    write_data[(y * write_bytes_per_row + x) as usize]\n                );\n            }\n        }\n    });\n"
  },
  {
    "path": "tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs",
    "content": "use wgpu::*;\nuse wgpu_test::{\n    gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, GpuTestInitializer,\n    TestParameters, TestingContext,\n};\n\npub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {\n    vec.extend([\n        DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT,\n        DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER,\n        DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER,\n        DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST,\n    ]);\n}\n\n// 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.\n#[gpu_test]\nstatic DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT:\n    GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))\n    .run_async(|mut ctx| async move {\n        let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);\n        case.create_command_encoder();\n        case.discard();\n        case.submit_command_encoder();\n\n        case.create_command_encoder();\n        case.copy_texture_to_buffer();\n        case.submit_command_encoder();\n\n        case.assert_buffers_are_zero().await;\n    });\n\n#[gpu_test]\nstatic DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:\n    GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))\n    .run_async(|mut ctx| async move {\n        let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);\n        case.create_command_encoder();\n        case.discard();\n        case.copy_texture_to_buffer();\n        case.submit_command_encoder();\n\n        case.assert_buffers_are_zero().await;\n    });\n\n#[gpu_test]\nstatic DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:\n    GpuTestConfiguration = GpuTestConfiguration::new()\n    .parameters(\n        TestParameters::default()\n            .downlevel_flags(\n                DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS,\n            )\n            .limits(Limits::downlevel_defaults()),\n    )\n    .run_async(|mut ctx| async move {\n        for format in [\n            TextureFormat::Stencil8,\n            TextureFormat::Depth16Unorm,\n            TextureFormat::Depth24Plus,\n            TextureFormat::Depth24PlusStencil8,\n            TextureFormat::Depth32Float,\n        ] {\n            let mut case = TestCase::new(&mut ctx, format);\n            case.create_command_encoder();\n            case.discard();\n            case.copy_texture_to_buffer();\n            case.submit_command_encoder();\n\n            case.assert_buffers_are_zero().await;\n        }\n    });\n\n#[gpu_test]\nstatic DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration =\n    GpuTestConfiguration::new()\n        .parameters(\n            TestParameters::default()\n                .downlevel_flags(\n                    DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES\n                        | DownlevelFlags::COMPUTE_SHADERS,\n                )\n                .limits(Limits::downlevel_defaults()),\n        )\n        .run_async(|mut ctx| async move {\n            for format in [\n                TextureFormat::Stencil8,\n                TextureFormat::Depth16Unorm,\n                TextureFormat::Depth24Plus,\n                TextureFormat::Depth24PlusStencil8,\n                TextureFormat::Depth32Float,\n            ] {\n                let mut case = TestCase::new(&mut ctx, format);\n                case.create_command_encoder();\n                case.discard_depth();\n                case.submit_command_encoder();\n\n                case.create_command_encoder();\n                case.discard_stencil();\n                case.submit_command_encoder();\n\n                case.create_command_encoder();\n                case.copy_texture_to_buffer();\n                case.submit_command_encoder();\n\n                case.assert_buffers_are_zero().await;\n            }\n        });\n\nstruct TestCase<'ctx> {\n    ctx: &'ctx mut TestingContext,\n    format: TextureFormat,\n    texture: Texture,\n    readback_buffers: ReadbackBuffers,\n    encoder: Option<CommandEncoder>,\n}\n\nimpl<'ctx> TestCase<'ctx> {\n    pub fn new(ctx: &'ctx mut TestingContext, format: TextureFormat) -> Self {\n        let extra_usages = match format {\n            TextureFormat::Depth24Plus | TextureFormat::Depth24PlusStencil8 => {\n                TextureUsages::TEXTURE_BINDING\n            }\n            _ => TextureUsages::empty(),\n        };\n\n        let texture = ctx.device.create_texture(&TextureDescriptor {\n            label: Some(\"RenderTarget\"),\n            size: Extent3d {\n                width: COPY_BYTES_PER_ROW_ALIGNMENT,\n                height: COPY_BYTES_PER_ROW_ALIGNMENT,\n                depth_or_array_layers: 1,\n            },\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: TextureDimension::D2,\n            format,\n            usage: TextureUsages::COPY_DST\n                | TextureUsages::COPY_SRC\n                | TextureUsages::RENDER_ATTACHMENT\n                | extra_usages,\n            view_formats: &[],\n        });\n\n        // Clear using a write_texture operation. We could also clear using a render_pass clear.\n        // However, when making this test intentionally fail (by breaking wgpu impl), it shows that at least on the tested Vulkan driver,\n        // the later following discard pass in the test (i.e. internally vk::AttachmentStoreOp::DONT_CARE) will yield different depending on the operation we take here:\n        // * clearing white -> discard will cause it to become black!\n        // * clearing red -> discard will keep it red\n        // * write_texture -> discard will keep buffer\n        // This behavior is curious, but does not violate any spec - it is wgpu's job to pass this test no matter what a render target discard does.\n\n        // ... but that said, for depth/stencil textures we need to do a clear.\n        if format.is_depth_stencil_format() {\n            let mut encoder = ctx\n                .device\n                .create_command_encoder(&CommandEncoderDescriptor::default());\n            encoder.begin_render_pass(&RenderPassDescriptor {\n                label: Some(\"Depth/Stencil setup\"),\n                color_attachments: &[],\n                depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {\n                    view: &texture.create_view(&TextureViewDescriptor::default()),\n                    depth_ops: format.has_depth_aspect().then_some(Operations {\n                        load: LoadOp::Clear(1.0),\n                        store: StoreOp::Store,\n                    }),\n                    stencil_ops: format.has_stencil_aspect().then_some(Operations {\n                        load: LoadOp::Clear(0xFFFFFFFF),\n                        store: StoreOp::Store,\n                    }),\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n            ctx.queue.submit([encoder.finish()]);\n        } else {\n            let block_size = format.block_copy_size(None).unwrap();\n            let bytes_per_row = texture.width() * block_size;\n\n            // Size for tests is chosen so that we don't need to care about buffer alignments.\n            assert!(!format.is_compressed());\n            assert_eq!(bytes_per_row % COPY_BYTES_PER_ROW_ALIGNMENT, 0);\n\n            let buffer_size = texture.height() * bytes_per_row;\n            let data = vec![255; buffer_size as usize];\n            ctx.queue.write_texture(\n                TexelCopyTextureInfo {\n                    texture: &texture,\n                    mip_level: 0,\n                    origin: Origin3d { x: 0, y: 0, z: 0 },\n                    aspect: TextureAspect::All,\n                },\n                &data,\n                TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(bytes_per_row),\n                    rows_per_image: None,\n                },\n                texture.size(),\n            );\n        }\n\n        let readback_buffers = ReadbackBuffers::new(&ctx.device, &texture);\n\n        Self {\n            ctx,\n            format,\n            texture,\n            readback_buffers,\n            encoder: None,\n        }\n    }\n\n    pub fn create_command_encoder(&mut self) {\n        self.encoder = Some(\n            self.ctx\n                .device\n                .create_command_encoder(&CommandEncoderDescriptor::default()),\n        )\n    }\n\n    pub fn submit_command_encoder(&mut self) {\n        self.ctx\n            .queue\n            .submit([self.encoder.take().unwrap().finish()]);\n    }\n\n    pub fn discard(&mut self) {\n        self.encoder\n            .as_mut()\n            .unwrap()\n            .begin_render_pass(&RenderPassDescriptor {\n                label: Some(\"Discard\"),\n                color_attachments: &[self.format.has_color_aspect().then_some(\n                    RenderPassColorAttachment {\n                        view: &self.texture.create_view(&TextureViewDescriptor::default()),\n                        depth_slice: None,\n                        resolve_target: None,\n                        ops: Operations {\n                            load: LoadOp::Load,\n                            store: StoreOp::Discard,\n                        },\n                    },\n                )],\n                depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(\n                    RenderPassDepthStencilAttachment {\n                        view: &self.texture.create_view(&TextureViewDescriptor::default()),\n                        depth_ops: self.format.has_depth_aspect().then_some(Operations {\n                            load: LoadOp::Load,\n                            store: StoreOp::Discard,\n                        }),\n                        stencil_ops: self.format.has_stencil_aspect().then_some(Operations {\n                            load: LoadOp::Load,\n                            store: StoreOp::Discard,\n                        }),\n                    },\n                ),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n    }\n\n    pub fn discard_depth(&mut self) {\n        self.encoder\n            .as_mut()\n            .unwrap()\n            .begin_render_pass(&RenderPassDescriptor {\n                label: Some(\"Discard Depth\"),\n                color_attachments: &[],\n                depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(\n                    RenderPassDepthStencilAttachment {\n                        view: &self.texture.create_view(&TextureViewDescriptor::default()),\n                        depth_ops: Some(Operations {\n                            load: LoadOp::Load,\n                            store: StoreOp::Discard,\n                        }),\n                        stencil_ops: self.format.has_stencil_aspect().then_some(Operations {\n                            load: LoadOp::Clear(0),\n                            store: StoreOp::Store,\n                        }),\n                    },\n                ),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n    }\n\n    pub fn discard_stencil(&mut self) {\n        self.encoder\n            .as_mut()\n            .unwrap()\n            .begin_render_pass(&RenderPassDescriptor {\n                label: Some(\"Discard Stencil\"),\n                color_attachments: &[],\n                depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(\n                    RenderPassDepthStencilAttachment {\n                        view: &self.texture.create_view(&TextureViewDescriptor::default()),\n                        depth_ops: self.format.has_depth_aspect().then_some(Operations {\n                            load: LoadOp::Clear(0.0),\n                            store: StoreOp::Store,\n                        }),\n                        stencil_ops: Some(Operations {\n                            load: LoadOp::Load,\n                            store: StoreOp::Discard,\n                        }),\n                    },\n                ),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n    }\n\n    pub fn copy_texture_to_buffer(&mut self) {\n        self.readback_buffers.copy_from(\n            &self.ctx.device,\n            self.encoder.as_mut().unwrap(),\n            &self.texture,\n        );\n    }\n\n    pub async fn assert_buffers_are_zero(&mut self) {\n        assert!(\n            self.readback_buffers.are_zero(self.ctx).await,\n            \"texture was not fully cleared\"\n        );\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/binding_arrays.rs",
    "content": "use std::num::NonZeroU32;\n\nuse wgpu::*;\nuse wgpu_test::fail;\n\n#[test]\nfn dynamic_offset() {\n    let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::TEXTURE_BINDING_ARRAY,\n        required_limits: Limits {\n            max_binding_array_elements_per_shader_stage: 4,\n            ..Limits::default()\n        },\n        ..DeviceDescriptor::default()\n    });\n\n    // Check that you can't create a bind group with both dynamic offset and binding array\n    fail(\n        &device,\n        || {\n            device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n                label: Some(\"Test1\"),\n                entries: &[\n                    BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Texture {\n                            sample_type: TextureSampleType::Float { filterable: false },\n                            view_dimension: TextureViewDimension::D2,\n                            multisampled: false,\n                        },\n                        count: Some(NonZeroU32::new(4).unwrap()),\n                    },\n                    BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Buffer {\n                            ty: BufferBindingType::Storage { read_only: true },\n                            has_dynamic_offset: true,\n                            min_binding_size: None,\n                        },\n                        count: None,\n                    },\n                ],\n            })\n        },\n        Some(\"binding array and a dynamically offset buffer\"),\n    );\n}\n\n#[test]\nfn uniform_buffer() {\n    let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::TEXTURE_BINDING_ARRAY,\n        required_limits: Limits {\n            max_binding_array_elements_per_shader_stage: 4,\n            ..Limits::default()\n        },\n        ..DeviceDescriptor::default()\n    });\n\n    // Check that you can't create a bind group with both uniform buffer and binding array\n    fail(\n        &device,\n        || {\n            device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n                label: Some(\"Test2\"),\n                entries: &[\n                    BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Texture {\n                            sample_type: TextureSampleType::Float { filterable: false },\n                            view_dimension: TextureViewDimension::D2,\n                            multisampled: false,\n                        },\n                        count: Some(NonZeroU32::new(4).unwrap()),\n                    },\n                    BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Buffer {\n                            ty: BufferBindingType::Uniform,\n                            has_dynamic_offset: false,\n                            min_binding_size: None,\n                        },\n                        count: None,\n                    },\n                ],\n            })\n        },\n        Some(\"binding array and a uniform buffer\"),\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/buffer.rs",
    "content": "//! Tests of [`wgpu::Buffer`] and related.\n\n/// Ensures that submitting a command buffer referencing an already destroyed buffer\n/// results in an error.\n#[test]\n#[should_panic = \"Buffer with '' label has been destroyed\"]\nfn destroyed_buffer() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.clear_buffer(&buffer, 0, None);\n\n    buffer.destroy();\n\n    queue.submit([encoder.finish()]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/buffer_mapping.rs",
    "content": "// FIXME: Now that MAP_WRITE mappings are write-only,\n// the “mut” and “immutable” terminology is incorrect.\n\nfn read_mapping_is_zeroed(slice: &[u8]) {\n    for (i, &byte) in slice.iter().enumerate() {\n        assert_eq!(byte, 0, \"Byte at index {i} is not zero\");\n    }\n}\nfn write_mapping_is_zeroed(mut slice: wgpu::WriteOnly<'_, [u8]>) {\n    let ptr = slice.as_raw_ptr().cast::<u8>();\n    for i in 0..slice.len() {\n        assert_eq!(\n            // SAFETY: it is not, in general, safe to read from a write mapping, but our goal here\n            // is specifically to verify the internally provided zeroedness.\n            //\n            // FIXME: Is the goal of these tests to ensure that zeroes are what is exposed to Rust,\n            // and not to ensure that zeroes get into the GPU buffer? If so, then we can delete\n            // them, or perhaps replace them with tests of mapping without writing, then reading.\n            unsafe { ptr.add(i).read() },\n            0,\n            \"Byte at index {i} is not zero\"\n        );\n    }\n}\n\n// Ensure that a simple immutable mapping works and it is zeroed.\n#[test]\nfn full_immutable_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    buffer.map_async(wgpu::MapMode::Read, .., |_| {});\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n    let mapping = buffer.slice(..).get_mapped_range();\n\n    read_mapping_is_zeroed(&mapping);\n\n    drop(mapping);\n\n    buffer.unmap();\n}\n\n// Ensure that a simple mutable binding works and it is zeroed.\n#[test]\nfn full_mut_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: true,\n    });\n\n    let mut mapping = buffer.slice(..).get_mapped_range_mut();\n\n    write_mapping_is_zeroed(mapping.slice(..));\n\n    drop(mapping);\n\n    buffer.unmap();\n}\n\n// Ensure that you can make two non-overlapping immutable ranges, which are both zeroed\n#[test]\nfn split_immutable_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    buffer.map_async(wgpu::MapMode::Read, .., |_| {});\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n    let mapping0 = buffer.slice(0..512).get_mapped_range();\n    let mapping1 = buffer.slice(512..1024).get_mapped_range();\n\n    read_mapping_is_zeroed(&mapping0);\n    read_mapping_is_zeroed(&mapping1);\n\n    drop(mapping0);\n    drop(mapping1);\n\n    buffer.unmap();\n}\n\n/// Ensure that you can make two non-overlapping mapped ranges, which are both zeroed\n#[test]\nfn split_mut_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: true,\n    });\n\n    let mut mapping0 = buffer.slice(0..512).get_mapped_range_mut();\n    let mut mapping1 = buffer.slice(512..1024).get_mapped_range_mut();\n\n    write_mapping_is_zeroed(mapping0.slice(..));\n    write_mapping_is_zeroed(mapping1.slice(..));\n\n    drop(mapping0);\n    drop(mapping1);\n\n    buffer.unmap();\n}\n\n/// Ensure that you can make two overlapping immutablely mapped ranges.\n#[test]\nfn overlapping_ref_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: true,\n    });\n\n    let _mapping0 = buffer.slice(0..512).get_mapped_range();\n    let _mapping1 = buffer.slice(256..768).get_mapped_range();\n}\n\n/// Ensure that two overlapping mutably mapped ranges panics.\n#[test]\n#[should_panic(expected = \"break Rust memory aliasing rules\")]\nfn overlapping_mut_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: true,\n    });\n\n    let _mapping0 = buffer.slice(0..512).get_mapped_range_mut();\n    let _mapping1 = buffer.slice(256..768).get_mapped_range_mut();\n}\n\n/// Ensure that when you try to get a mapped range from an unmapped buffer, it panics with\n/// an error mentioning a completely unmapped buffer.\n#[test]\n#[should_panic(expected = \"an unmapped buffer\")]\nfn not_mapped() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let _mapping = buffer.slice(..).get_mapped_range_mut();\n}\n\n/// Ensure that when you partially map a buffer, then try to read outside of that range, it panics\n/// mentioning the mapped indices.\n#[test]\n#[should_panic(\n    expected = \"Attempted to get range 512..1024 (Mutable), but the mapped range is 0..512\"\n)]\nfn partially_mapped() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    buffer.map_async(wgpu::MapMode::Write, 0..512, |_| {});\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n\n    let _mapping0 = buffer.slice(0..512).get_mapped_range_mut();\n    let _mapping1 = buffer.slice(512..1024).get_mapped_range_mut();\n}\n\n/// Ensure that you cannot unmap a buffer while there are still accessible mapped views.\n#[test]\n#[should_panic(expected = \"You cannot unmap a buffer that still has accessible mapped views\")]\nfn unmap_while_visible() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: true,\n    });\n\n    let _mapping0 = buffer.slice(..).get_mapped_range_mut();\n    buffer.unmap();\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/buffer_slice.rs",
    "content": "use std::num::NonZero;\n\nconst ARBITRARY_DESC: &wgpu::BufferDescriptor = &wgpu::BufferDescriptor {\n    label: None,\n    size: 100,\n    usage: wgpu::BufferUsages::VERTEX,\n    mapped_at_creation: false,\n};\n\n#[test]\nfn reslice_success() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(ARBITRARY_DESC);\n\n    assert_eq!(buffer.slice(10..90).slice(10..70), buffer.slice(20..80));\n}\n\n#[test]\n#[should_panic = \"slice offset 10 size 80 is out of range for buffer of size 80\"]\nfn reslice_out_of_bounds() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(ARBITRARY_DESC);\n\n    buffer.slice(10..90).slice(10..90);\n}\n\n#[test]\nfn getters() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(ARBITRARY_DESC);\n\n    let slice_with_size = buffer.slice(10..90);\n    assert_eq!(\n        (\n            slice_with_size.buffer(),\n            slice_with_size.offset(),\n            slice_with_size.size()\n        ),\n        (&buffer, 10, NonZero::new(80).unwrap())\n    );\n\n    let slice_without_size = buffer.slice(10..);\n    assert_eq!(\n        (\n            slice_without_size.buffer(),\n            slice_without_size.offset(),\n            slice_without_size.size()\n        ),\n        (&buffer, 10, NonZero::new(90).unwrap())\n    );\n}\n\n#[test]\nfn into_buffer_binding() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(ARBITRARY_DESC);\n\n    // BindingResource doesn’t implement PartialEq, so use matching\n    let wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n        buffer: b,\n        offset: 50,\n        size: Some(size),\n    }) = wgpu::BindingResource::from(buffer.slice(50..80))\n    else {\n        panic!(\"didn't match\")\n    };\n    assert_eq!(b, &buffer);\n    assert_eq!(size, NonZero::new(30).unwrap());\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/command_buffer_actions.rs",
    "content": "use std::sync::atomic::{AtomicBool, AtomicU32, Ordering::SeqCst};\nuse std::sync::Arc;\n\n/// Helper to create a small mappable buffer for READ tests.\nfn make_read_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {\n    device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"read buffer\"),\n        size,\n        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    })\n}\n\n/// map_buffer_on_submit defers mapping until submit, then invokes the callback after polling.\n#[test]\nfn encoder_map_buffer_on_submit_defers_until_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    let fired = Arc::new(AtomicBool::new(false));\n    let fired_cl = Arc::clone(&fired);\n\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n        label: Some(\"encoder\"),\n    });\n\n    // Register deferred map.\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| {\n        fired_cl.store(true, SeqCst);\n    });\n    // Include a trivial command that uses the buffer.\n    encoder.clear_buffer(&buffer, 0, None);\n\n    // Polling before submit should not trigger the callback.\n    _ = device.poll(wgpu::PollType::Poll);\n    assert!(!fired.load(SeqCst));\n\n    // Submit and wait; callback should fire.\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n    assert!(fired.load(SeqCst));\n}\n\n/// Empty ranges panic immediately when registering the deferred map.\n#[test]\n#[should_panic = \"buffer slices can not be empty\"]\nfn encoder_map_buffer_on_submit_empty_range_panics_immediately() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    // This panics inside map_buffer_on_submit (range_to_offset_size).\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 8..8, |_| {});\n}\n\n/// Out-of-bounds ranges panic during submit (when the deferred map executes).\n#[test]\n#[should_panic = \"is out of range for buffer of size\"]\nfn encoder_map_buffer_on_submit_out_of_bounds_panics_on_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    // 12..24 overflows the 16-byte buffer (size=12, end=24).\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 12..24, |_| {});\n    encoder.clear_buffer(&buffer, 0, None);\n\n    // Panic happens inside submit when executing deferred actions.\n    queue.submit([encoder.finish()]);\n}\n\n/// If the buffer is already mapped when the deferred mapping executes, it panics during submit.\n#[test]\n#[should_panic = \"Buffer with 'read buffer' label is still mapped\"]\nfn encoder_map_buffer_on_submit_panics_if_already_mapped_on_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    // Start a mapping now so the buffer is considered mapped.\n    buffer.slice(0..4).map_async(wgpu::MapMode::Read, |_| {});\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    // Deferred mapping of an already-mapped buffer will panic when executed on submit or be rejected by submit.\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, |_| {});\n    // Include any trivial work; using the same buffer ensures core validation catches the mapped hazard.\n    encoder.clear_buffer(&buffer, 0, None);\n\n    queue.submit([encoder.finish()]);\n}\n\n/// on_submitted_work_done is deferred until submit.\n#[test]\nfn encoder_on_submitted_work_done_defers_until_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let fired = Arc::new(AtomicBool::new(false));\n    let fired_cl = Arc::clone(&fired);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    encoder.on_submitted_work_done(move || {\n        fired_cl.store(true, SeqCst);\n    });\n\n    // Include a trivial command so the command buffer isn't completely empty.\n    let dummy = make_read_buffer(&device, 4);\n    encoder.clear_buffer(&dummy, 0, None);\n\n    // Without submission, polling shouldn't invoke the callback.\n    _ = device.poll(wgpu::PollType::Poll);\n    assert!(!fired.load(SeqCst));\n\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n    assert!(fired.load(SeqCst));\n}\n\n/// Both kinds of deferred callbacks are enqueued and eventually invoked.\n#[test]\nfn encoder_both_callbacks_fire_after_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    let map_fired = Arc::new(AtomicBool::new(false));\n    let map_fired_cl = Arc::clone(&map_fired);\n    let queue_fired = Arc::new(AtomicBool::new(false));\n    let queue_fired_cl = Arc::clone(&queue_fired);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| {\n        map_fired_cl.store(true, SeqCst);\n    });\n    encoder.on_submitted_work_done(move || {\n        queue_fired_cl.store(true, SeqCst);\n    });\n    encoder.clear_buffer(&buffer, 0, None);\n\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n\n    assert!(map_fired.load(SeqCst));\n    assert!(queue_fired.load(SeqCst));\n}\n\n/// Registering multiple deferred mappings works; all callbacks fire after submit.\n#[test]\nfn encoder_multiple_map_buffer_on_submit_callbacks_fire() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer1 = make_read_buffer(&device, 32);\n    let buffer2 = make_read_buffer(&device, 32);\n\n    let counter = Arc::new(AtomicU32::new(0));\n    let c1 = Arc::clone(&counter);\n    let c2 = Arc::clone(&counter);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.map_buffer_on_submit(&buffer1, wgpu::MapMode::Read, 0..4, move |_| {\n        c1.fetch_add(1, SeqCst);\n    });\n    encoder.map_buffer_on_submit(&buffer2, wgpu::MapMode::Read, 8..12, move |_| {\n        c2.fetch_add(1, SeqCst);\n    });\n    encoder.clear_buffer(&buffer1, 0, None);\n\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n\n    assert_eq!(counter.load(SeqCst), 2);\n}\n\n/// Mapping with a buffer lacking MAP_* usage should panic when executed on submit.\n#[test]\n#[should_panic]\nfn encoder_map_buffer_on_submit_panics_if_usage_invalid_on_submit() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let unmappable = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"unmappable buffer\"),\n        size: 16,\n        usage: wgpu::BufferUsages::COPY_DST, // No MAP_READ or MAP_WRITE\n        mapped_at_creation: false,\n    });\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.map_buffer_on_submit(&unmappable, wgpu::MapMode::Read, 0..4, |_| {});\n\n    // Add unrelated work so the submission isn't empty.\n    let dummy = make_read_buffer(&device, 4);\n    encoder.clear_buffer(&dummy, 0, None);\n\n    // Panic expected when deferred mapping executes.\n    queue.submit([encoder.finish()]);\n}\n\n/// Deferred map callbacks run before on_submitted_work_done for the same submission.\n#[test]\nfn encoder_deferred_map_runs_before_on_submitted_work_done() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 16);\n\n    #[derive(Default)]\n    struct Order {\n        map_order: AtomicU32,\n        queue_order: AtomicU32,\n        counter: AtomicU32,\n    }\n    let order = Arc::new(Order::default());\n    let o_map = Arc::clone(&order);\n    let o_queue = Arc::clone(&order);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| {\n        let v = o_map.counter.fetch_add(1, SeqCst);\n        o_map.map_order.store(v, SeqCst);\n    });\n    encoder.on_submitted_work_done(move || {\n        let v = o_queue.counter.fetch_add(1, SeqCst);\n        o_queue.queue_order.store(v, SeqCst);\n    });\n    encoder.clear_buffer(&buffer, 0, None);\n\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n\n    assert_eq!(order.counter.load(SeqCst), 2);\n    assert_eq!(order.map_order.load(SeqCst), 0);\n    assert_eq!(order.queue_order.load(SeqCst), 1);\n}\n\n/// Multiple on_submitted_work_done callbacks registered on encoder all fire after submit.\n#[test]\nfn encoder_multiple_on_submitted_callbacks_fire() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = make_read_buffer(&device, 4);\n\n    let counter = Arc::new(AtomicU32::new(0));\n    let c1 = Arc::clone(&counter);\n    let c2 = Arc::clone(&counter);\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.on_submitted_work_done(move || {\n        c1.fetch_add(1, SeqCst);\n    });\n    encoder.on_submitted_work_done(move || {\n        c2.fetch_add(1, SeqCst);\n    });\n    encoder.clear_buffer(&buffer, 0, None);\n\n    queue.submit([encoder.finish()]);\n    _ = device.poll(wgpu::PollType::wait_indefinitely());\n\n    assert_eq!(counter.load(SeqCst), 2);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/device.rs",
    "content": "/// Test that if another error occurs reentrantly in the [`wgpu::Device::on_uncaptured_error`]\n/// handler, this does not result in a deadlock (as a previous implementation would have had).\n#[cfg(not(target_family = \"wasm\"))] // test needs wgpu::Device: Send + Sync to achieve reentrance\n#[test]\nfn recursive_uncaptured_error() {\n    use std::sync::atomic::{AtomicU32, Ordering::Relaxed};\n    use std::sync::Arc;\n\n    const ERRONEOUS_TEXTURE_DESC: wgpu::TextureDescriptor = wgpu::TextureDescriptor {\n        label: None,\n        size: wgpu::Extent3d {\n            width: 1,\n            height: 1,\n            depth_or_array_layers: 10,\n        },\n        mip_level_count: 0,\n        sample_count: 0,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8UnormSrgb,\n        usage: wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    };\n\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let errors_seen: Arc<AtomicU32> = Arc::default();\n    let handler = Arc::new({\n        let errors_seen = errors_seen.clone();\n        let device = device.clone();\n        move |_error| {\n            let previous_count = errors_seen.fetch_add(1, Relaxed);\n            if previous_count == 0 {\n                // Trigger another error recursively\n                _ = device.create_texture(&ERRONEOUS_TEXTURE_DESC);\n            }\n        }\n    });\n\n    // Trigger one error which will call the handler\n    device.on_uncaptured_error(handler);\n    _ = device.create_texture(&ERRONEOUS_TEXTURE_DESC);\n\n    assert_eq!(errors_seen.load(Relaxed), 2);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/encoding.rs",
    "content": "//! Tests of [`wgpu::CommandEncoder`] and related.\n\n#[test]\nfn as_hal() {\n    // Sanity-test that the raw encoding API isn't completely broken.\n\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    unsafe {\n        encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());\n    }\n    encoder.finish();\n}\n\n#[test]\n#[should_panic = \"Mixing the wgpu encoding API with the raw encoding API is not permitted\"]\nfn mix_apis_wgpu_then_hal() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256,\n        usage: wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    encoder.clear_buffer(&buffer, 0, None);\n    unsafe {\n        encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());\n    }\n}\n\n#[test]\n#[should_panic = \"Mixing the wgpu encoding API with the raw encoding API is not permitted\"]\nfn mix_apis_hal_then_wgpu() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 256,\n        usage: wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n    unsafe {\n        encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());\n    }\n    encoder.clear_buffer(&buffer, 0, None);\n}\n\n/// Test that the command encoder’s label is remembered and used in errors.\n#[test]\n#[should_panic = \"In a CommandEncoder, label = 'my encoder'\"]\nfn encoding_error_contains_label_of_encoder() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"my buffer\"),\n        size: 1024,\n        usage: wgpu::BufferUsages::MAP_READ,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n        label: Some(\"my encoder\"),\n    });\n    // This is erroneous because it is copying to the same buffer.\n    encoder.copy_buffer_to_buffer(&buffer, 0, &buffer, 0, 10);\n    queue.submit([encoder.finish()]);\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/error_scopes.rs",
    "content": "#![cfg(not(target_arch = \"wasm32\"))]\nuse std::{\n    panic::{resume_unwind, AssertUnwindSafe},\n    sync::Arc,\n};\n\nuse parking_lot::Mutex;\n\nconst ERR: &str = \"Buffer size 9223372036854775808 is greater than the maximum buffer size\";\nfn raise_validation_error(device: &wgpu::Device) {\n    let _buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: 1 << 63, // Too large!\n        usage: wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n}\n\nfn register_uncaptured_error_handler(device: &wgpu::Device) -> Arc<Mutex<Vec<wgpu::Error>>> {\n    let errors = Arc::new(Mutex::new(Vec::new()));\n    let errors_clone = errors.clone();\n\n    device.on_uncaptured_error(Arc::new(move |error| {\n        errors_clone.lock().push(error);\n    }));\n\n    errors\n}\n\nfn assert_matches_string(error: &wgpu::Error, substr: &str) {\n    let err_str = error.to_string();\n    assert!(\n        err_str.contains(substr),\n        \"Error string '{err_str}' does not contain expected substring '{substr}'\"\n    );\n}\n\n// Test that error scopes work correctly in the basic case.\n#[test]\nfn basic() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    raise_validation_error(&device);\n    let error = pollster::block_on(scope.pop());\n\n    assert!(error.is_some());\n}\n\n// Test that error scopes are thread-local: an error scope pushed on one thread\n// does not capture errors generated on another thread.\n#[test]\nfn multi_threaded_scopes() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    // Start an error scope on the main thread.\n    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    // Register an uncaptured error handler to catch errors from other threads.\n    let other_thread_error = register_uncaptured_error_handler(&device);\n\n    // Do something invalid on another thread.\n    std::thread::scope(|s| {\n        s.spawn(|| {\n            raise_validation_error(&device);\n        });\n    });\n\n    // Pop the error scope on the main thread.\n    let error = pollster::block_on(scope.pop());\n\n    // The main thread's error scope should not have captured the other thread's error.\n    assert!(error.is_none());\n    // The other thread's error should have been reported to the uncaptured error handler.\n    let uncaptured_errors = other_thread_error.lock();\n    assert_eq!(uncaptured_errors.len(), 1);\n    assert_matches_string(&uncaptured_errors[0], ERR);\n}\n\n// Test that error scopes error when popped in the wrong order.\n#[test]\n#[should_panic(expected = \"error scopes must be popped in reverse order\")]\nfn pop_out_of_order() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let scope1 = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    let _scope2 = device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n    let _ = pollster::block_on(scope1.pop());\n}\n\n// Test that error scopes are automatically popped when dropped.\n#[test]\nfn drop_automatically_pops() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let uncaptured_error = register_uncaptured_error_handler(&device);\n\n    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    raise_validation_error(&device);\n    drop(scope); // Automatically pops the error scope.\n\n    assert!(uncaptured_error.lock().is_empty());\n\n    // Raising another error will go to the uncaptured error handler, not the dropped scope.\n    raise_validation_error(&device);\n\n    assert_eq!(uncaptured_error.lock().len(), 1);\n    assert_matches_string(&uncaptured_error.lock()[0], ERR);\n}\n\n// Test that error scopes are automatically popped when dropped during unwinding,\n// even when they are dropped out of order.\n#[test]\nfn drop_during_unwind() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let scope1 = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    let scope2 = device.push_error_scope(wgpu::ErrorFilter::Validation);\n\n    let res = std::panic::catch_unwind(AssertUnwindSafe(|| {\n        raise_validation_error(&device);\n        // Move scope1 so that it is dropped before scope2.\n        let _scope2 = scope2;\n        let _scope1 = scope1;\n        resume_unwind(Box::new(\"unwind\"))\n    }));\n\n    assert_eq!(*res.unwrap_err().downcast_ref::<&str>().unwrap(), \"unwind\");\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/experimental.rs",
    "content": "fn noop_adapter() -> wgpu::Adapter {\n    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {\n        backends: wgpu::Backends::NOOP,\n        backend_options: wgpu::BackendOptions {\n            noop: wgpu::NoopBackendOptions { enable: true },\n            ..Default::default()\n        },\n        ..wgpu::InstanceDescriptor::new_without_display_handle()\n    });\n\n    pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))\n        .expect(\"noop backend adapter absent when it should be\")\n}\n\n#[test]\nfn request_no_experimental_features() {\n    let adapter = noop_adapter();\n\n    let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n        // Not experimental\n        required_features: wgpu::Features::FLOAT32_FILTERABLE,\n        experimental_features: wgpu::ExperimentalFeatures::disabled(),\n        ..Default::default()\n    }));\n\n    assert!(dq.is_ok());\n}\n\n#[test]\nfn request_experimental_features() {\n    let adapter = noop_adapter();\n\n    let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n        // Experimental\n        required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,\n        experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },\n        ..Default::default()\n    }));\n\n    assert!(dq.is_ok());\n}\n\n#[test]\nfn request_experimental_features_when_not_enabled() {\n    let adapter = noop_adapter();\n\n    let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n        // Experimental\n        required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,\n        experimental_features: wgpu::ExperimentalFeatures::disabled(),\n        ..Default::default()\n    }));\n\n    assert!(dq.is_err());\n}\n\n#[test]\nfn request_multiple_experimental_features_when_not_enabled() {\n    let adapter = noop_adapter();\n\n    let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {\n        // Experimental\n        required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER\n            | wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX,\n        experimental_features: wgpu::ExperimentalFeatures::disabled(),\n        ..Default::default()\n    }));\n\n    assert!(dq.is_err());\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/external_texture.rs",
    "content": "use wgpu::*;\nuse wgpu_test::{fail, valid};\n\n/// Ensures an [`ExternalTexture`] can be created from a valid descriptor and planes,\n/// but appropriate errors are returned for invalid descriptors and planes.\n#[test]\nfn create_external_texture() {\n    let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::EXTERNAL_TEXTURE,\n        ..Default::default()\n    });\n\n    let texture_descriptor = TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    };\n\n    let r_texture = device.create_texture(&TextureDescriptor {\n        format: TextureFormat::R8Unorm,\n        ..texture_descriptor\n    });\n    let r_view = r_texture.create_view(&TextureViewDescriptor::default());\n    let rg_texture = device.create_texture(&TextureDescriptor {\n        format: TextureFormat::Rg8Unorm,\n        ..texture_descriptor\n    });\n    let rg_view = rg_texture.create_view(&TextureViewDescriptor::default());\n    let rgba_texture = device.create_texture(&TextureDescriptor {\n        format: TextureFormat::Rgba8Unorm,\n        ..texture_descriptor\n    });\n    let rgba_view = rgba_texture.create_view(&TextureViewDescriptor::default());\n\n    let _ = valid(&device, || {\n        device.create_external_texture(\n            &ExternalTextureDescriptor {\n                format: ExternalTextureFormat::Rgba,\n                label: None,\n                width: r_texture.width(),\n                height: r_texture.height(),\n                yuv_conversion_matrix: [0.0; 16],\n                gamut_conversion_matrix: [0.0; 9],\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: [0.0; 6],\n                load_transform: [0.0; 6],\n            },\n            &[&rgba_view],\n        )\n    });\n    let _ = valid(&device, || {\n        device.create_external_texture(\n            &ExternalTextureDescriptor {\n                format: ExternalTextureFormat::Nv12,\n                label: None,\n                width: r_texture.width(),\n                height: r_texture.height(),\n                yuv_conversion_matrix: [0.0; 16],\n                gamut_conversion_matrix: [0.0; 9],\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: [0.0; 6],\n                load_transform: [0.0; 6],\n            },\n            &[&r_view, &rg_view],\n        )\n    });\n    let _ = valid(&device, || {\n        device.create_external_texture(\n            &ExternalTextureDescriptor {\n                format: ExternalTextureFormat::Yu12,\n                label: None,\n                width: r_texture.width(),\n                height: r_texture.height(),\n                yuv_conversion_matrix: [0.0; 16],\n                gamut_conversion_matrix: [0.0; 9],\n                src_transfer_function: Default::default(),\n                dst_transfer_function: Default::default(),\n                sample_transform: [0.0; 6],\n                load_transform: [0.0; 6],\n            },\n            &[&r_view, &r_view, &r_view],\n        )\n    });\n\n    // Wrong number of planes for format\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view, &r_view],\n            )\n        },\n        Some(\"External texture format Rgba expects 1 planes, but given 2\"),\n    );\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Nv12,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view],\n            )\n        },\n        Some(\"External texture format Nv12 expects 2 planes, but given 1\"),\n    );\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Yu12,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view, &r_view],\n            )\n        },\n        Some(\"External texture format Yu12 expects 3 planes, but given 2\"),\n    );\n\n    // Wrong plane formats\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view],\n            )\n        },\n        Some(\"External texture format Rgba plane 0 expects format with 4 components but given view with format R8Unorm (1 components)\"),\n    );\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Nv12,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view, &rgba_view],\n            )\n        },\n        Some(\"External texture format Nv12 plane 1 expects format with 2 components but given view with format Rgba8Unorm (4 components)\"),\n    );\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Yu12,\n                    label: None,\n                    width: r_texture.width(),\n                    height: r_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&r_view, &rg_view, &r_view],\n            )\n        },\n        Some(\"External texture format Yu12 plane 1 expects format with 1 components but given view with format Rg8Unorm (2 components)\"),\n    );\n\n    // Wrong sample type\n    let uint_texture = device.create_texture(&TextureDescriptor {\n        format: TextureFormat::Rgba8Uint,\n        ..texture_descriptor\n    });\n    let uint_view = uint_texture.create_view(&TextureViewDescriptor::default());\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: uint_texture.width(),\n                    height: uint_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&uint_view],\n            )\n        },\n        Some(\"External texture planes expect a filterable float sample type, but given view with format Rgba8Uint (sample type Uint)\"),\n    );\n\n    // Wrong texture dimension\n    let d3_texture = device.create_texture(&TextureDescriptor {\n        dimension: TextureDimension::D3,\n        ..texture_descriptor\n    });\n    let d3_view = d3_texture.create_view(&TextureViewDescriptor::default());\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: d3_texture.width(),\n                    height: d3_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&d3_view],\n            )\n        },\n        Some(\"External texture planes expect 2D dimension, but given view with dimension = D3\"),\n    );\n\n    // Multisampled\n    let multisampled_texture = device.create_texture(&TextureDescriptor {\n        sample_count: 4,\n        usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,\n        ..texture_descriptor\n    });\n    let multisampled_view = multisampled_texture.create_view(&TextureViewDescriptor::default());\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: multisampled_texture.width(),\n                    height: multisampled_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&multisampled_view],\n            )\n        },\n        Some(\"External texture planes cannot be multisampled, but given view with samples = 4\"),\n    );\n\n    // Missing TEXTURE_BINDING\n    let non_binding_texture = device.create_texture(&TextureDescriptor {\n        usage: TextureUsages::STORAGE_BINDING,\n        ..texture_descriptor\n    });\n    let non_binding_view = non_binding_texture.create_view(&TextureViewDescriptor::default());\n    let _ = fail(\n        &device,\n        || {\n            device.create_external_texture(\n                &ExternalTextureDescriptor {\n                    format: ExternalTextureFormat::Rgba,\n                    label: None,\n                    width: non_binding_texture.width(),\n                    height: non_binding_texture.height(),\n                    yuv_conversion_matrix: [0.0; 16],\n                    gamut_conversion_matrix: [0.0; 9],\n                    src_transfer_function: Default::default(),\n                    dst_transfer_function: Default::default(),\n                    sample_transform: [0.0; 6],\n                    load_transform: [0.0; 6],\n                },\n                &[&non_binding_view],\n            )\n        },\n        Some(\"Usage flags TextureUsages(STORAGE_BINDING) of TextureView with '' label do not contain required usage flags TextureUsages(TEXTURE_BINDING)\"),\n    );\n}\n\n/// Ensures an [`ExternalTexture`] can be bound to a [`BindingType::ExternalTexture`]\n/// resource binding.\n#[test]\nfn external_texture_binding() {\n    let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::EXTERNAL_TEXTURE,\n        ..Default::default()\n    });\n\n    let bgl = valid(&device, || {\n        device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[BindGroupLayoutEntry {\n                binding: 0,\n                visibility: ShaderStages::FRAGMENT,\n                ty: BindingType::ExternalTexture,\n                count: None,\n            }],\n        })\n    });\n\n    let texture_descriptor = TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    };\n    let external_texture_descriptor = ExternalTextureDescriptor {\n        label: None,\n        width: texture_descriptor.size.width,\n        height: texture_descriptor.size.height,\n        format: ExternalTextureFormat::Rgba,\n        yuv_conversion_matrix: [0.0; 16],\n        gamut_conversion_matrix: [0.0; 9],\n        src_transfer_function: Default::default(),\n        dst_transfer_function: Default::default(),\n        sample_transform: [0.0; 6],\n        load_transform: [0.0; 6],\n    };\n\n    valid(&device, || {\n        let texture = device.create_texture(&texture_descriptor);\n        let view = texture.create_view(&TextureViewDescriptor::default());\n        let external_texture =\n            device.create_external_texture(&external_texture_descriptor, &[&view]);\n\n        device.create_bind_group(&BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::ExternalTexture(&external_texture),\n            }],\n        })\n    });\n}\n\n/// Ensures a [`TextureView`] can be bound to a [`BindingType::ExternalTexture`]\n/// resource binding.\n#[test]\nfn external_texture_binding_texture_view() {\n    let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::EXTERNAL_TEXTURE,\n        ..Default::default()\n    });\n\n    let bgl = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n        label: None,\n        entries: &[BindGroupLayoutEntry {\n            binding: 0,\n            visibility: ShaderStages::FRAGMENT,\n            ty: BindingType::ExternalTexture,\n            count: None,\n        }],\n    });\n\n    let texture_descriptor = TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 256,\n            height: 256,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    };\n\n    let texture = device.create_texture(&texture_descriptor);\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    valid(&device, || {\n        device.create_bind_group(&BindGroupDescriptor {\n            label: None,\n            layout: &bgl,\n            entries: &[BindGroupEntry {\n                binding: 0,\n                resource: BindingResource::TextureView(&view),\n            }],\n        })\n    });\n\n    // Invalid usages (must include TEXTURE_BINDING)\n    let texture = device.create_texture(&TextureDescriptor {\n        usage: TextureUsages::STORAGE_BINDING,\n        ..texture_descriptor\n    });\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    fail(\n        &device,\n        || {\n            device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bgl,\n                entries: &[BindGroupEntry {\n                    binding: 0,\n                    resource: BindingResource::TextureView(&view),\n                }],\n            })\n        },\n        Some(\"Usage flags TextureUsages(STORAGE_BINDING) of TextureView with '' label do not contain required usage flags TextureUsages(TEXTURE_BINDING\"),\n    );\n\n    // Invalid dimension (must be D2)\n    let texture = device.create_texture(&TextureDescriptor {\n        dimension: TextureDimension::D3,\n        ..texture_descriptor\n    });\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    fail(\n        &device,\n        || {\n            device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bgl,\n                entries: &[BindGroupEntry {\n                    binding: 0,\n                    resource: BindingResource::TextureView(&view),\n                }],\n            })\n        },\n        Some(\"Texture binding 0 expects dimension = D2, but given a view with dimension = D3\"),\n    );\n\n    // Invalid mip_level_count (must be 1)\n    let texture = device.create_texture(&TextureDescriptor {\n        mip_level_count: 2,\n        ..texture_descriptor\n    });\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    fail(\n        &device,\n        || {\n\n            device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bgl,\n                entries: &[\n                    BindGroupEntry {\n                        binding: 0,\n                        resource: BindingResource::TextureView(&view),\n                    },\n                ],\n            })\n        },\n        Some(\"External texture bindings must have a single mip level, but given a view with mip_level_count = 2 at binding 0\")\n    );\n\n    // Invalid format (must be Rgba8Unorm, Bgra8Unorm, or Rgba16float)\n    let texture = device.create_texture(&TextureDescriptor {\n        format: TextureFormat::Rgba8Uint,\n        ..texture_descriptor\n    });\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    fail(\n        &device,\n        || {\n\n            device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bgl,\n                entries: &[\n                    BindGroupEntry {\n                        binding: 0,\n                        resource: BindingResource::TextureView(&view),\n                    },\n                ],\n            })\n        },\n        Some(\"External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = Rgba8Uint at binding 0\")\n    );\n\n    // Invalid sample count (must be 1)\n    let texture = device.create_texture(&TextureDescriptor {\n        sample_count: 4,\n        usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,\n        ..texture_descriptor\n    });\n    let view = texture.create_view(&TextureViewDescriptor::default());\n    fail(\n        &device,\n        || {\n            device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bgl,\n                entries: &[BindGroupEntry {\n                    binding: 0,\n                    resource: BindingResource::TextureView(&view),\n                }],\n            })\n        },\n        Some(\"Texture binding 0 expects multisampled = false, but given a view with samples = 4\"),\n    );\n}\n\n/// Ensures that submitting a command buffer referencing an external texture, any of\n/// whose plane textures have already been destroyed, results in an error.\n#[test]\nfn destroyed_external_texture_plane() {\n    let (device, queue) = wgpu::Device::noop(&DeviceDescriptor {\n        required_features: Features::EXTERNAL_TEXTURE,\n        ..Default::default()\n    });\n\n    let target_texture = device.create_texture(&TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::RENDER_ATTACHMENT,\n        view_formats: &[],\n    });\n    let target_view = target_texture.create_view(&TextureViewDescriptor::default());\n\n    let plane_texture = device.create_texture(&TextureDescriptor {\n        label: Some(\"External texture plane\"),\n        size: Extent3d {\n            width: 512,\n            height: 512,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format: TextureFormat::Rgba8Unorm,\n        usage: TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    });\n    let plane_view = plane_texture.create_view(&TextureViewDescriptor::default());\n\n    let external_texture = device.create_external_texture(\n        &ExternalTextureDescriptor {\n            format: ExternalTextureFormat::Rgba,\n            label: None,\n            width: plane_texture.width(),\n            height: plane_texture.height(),\n            yuv_conversion_matrix: [0.0; 16],\n            gamut_conversion_matrix: [0.0; 9],\n            src_transfer_function: Default::default(),\n            dst_transfer_function: Default::default(),\n            sample_transform: [0.0; 6],\n            load_transform: [0.0; 6],\n        },\n        &[&plane_view],\n    );\n\n    let module = device.create_shader_module(ShaderModuleDescriptor {\n        label: None,\n        source: ShaderSource::Wgsl(std::borrow::Cow::Borrowed(\n            \"\n@group(0) @binding(0)\nvar tex: texture_external;\n@vertex fn vert_main() -> @builtin(position) vec4<f32> { return vec4<f32>(0); }\n@fragment fn frag_main() -> @location(0) vec4<f32> { return textureLoad(tex, vec2(0)); }\",\n        )),\n    });\n\n    let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {\n        label: None,\n        layout: None,\n        vertex: VertexState {\n            module: &module,\n            entry_point: None,\n            compilation_options: PipelineCompilationOptions::default(),\n            buffers: &[],\n        },\n        primitive: PrimitiveState::default(),\n        depth_stencil: None,\n        multisample: MultisampleState::default(),\n        fragment: Some(FragmentState {\n            module: &module,\n            entry_point: None,\n            compilation_options: PipelineCompilationOptions::default(),\n            targets: &[Some(ColorTargetState {\n                format: target_texture.format(),\n                blend: None,\n                write_mask: ColorWrites::ALL,\n            })],\n        }),\n        multiview_mask: None,\n        cache: None,\n    });\n\n    let bind_group = device.create_bind_group(&BindGroupDescriptor {\n        label: None,\n        layout: &pipeline.get_bind_group_layout(0),\n        entries: &[BindGroupEntry {\n            binding: 0,\n            resource: BindingResource::ExternalTexture(&external_texture),\n        }],\n    });\n\n    let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None });\n    let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {\n        label: None,\n        color_attachments: &[Some(RenderPassColorAttachment {\n            view: &target_view,\n            depth_slice: None,\n            resolve_target: None,\n            ops: Operations {\n                load: LoadOp::Clear(Color {\n                    r: 0.0,\n                    g: 0.0,\n                    b: 0.0,\n                    a: 1.0,\n                }),\n                store: StoreOp::Store,\n            },\n        })],\n        depth_stencil_attachment: None,\n        timestamp_writes: None,\n        occlusion_query_set: None,\n        multiview_mask: None,\n    });\n\n    pass.set_pipeline(&pipeline);\n    pass.set_bind_group(0, &bind_group, &[]);\n    pass.draw(0..0, 0..0);\n    drop(pass);\n\n    plane_texture.destroy();\n\n    fail(\n        &device,\n        || queue.submit([encoder.finish()]),\n        Some(\"Texture with 'External texture plane' label has been destroyed\"),\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/instance.rs",
    "content": "mod multi_instance {\n    #![cfg(not(any(target_arch = \"wasm32\", miri)))]\n\n    async fn get() -> wgpu::Adapter {\n        let adapter = {\n            let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {\n                backends: wgpu::Backends::from_env().unwrap_or_default(),\n                ..wgpu::InstanceDescriptor::new_without_display_handle()\n            });\n            instance\n                .request_adapter(&wgpu::RequestAdapterOptions::default())\n                .await\n                .unwrap()\n        };\n\n        log::info!(\"Selected adapter: {:?}\", adapter.get_info());\n\n        adapter\n    }\n\n    #[test]\n    pub fn multi_instance() {\n        {\n            env_logger::init();\n\n            // Sequential instances.\n            for _ in 0..3 {\n                pollster::block_on(get());\n            }\n\n            // Concurrent instances\n            let _instances: Vec<_> = (0..3).map(|_| pollster::block_on(get())).collect();\n        }\n    }\n}\n\nmod request_adapter_error {\n    fn id(backends: wgpu::Backends) -> wgpu::InstanceDescriptor {\n        wgpu::InstanceDescriptor {\n            backends,\n            flags: wgpu::InstanceFlags::default(),\n            memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),\n            backend_options: wgpu::BackendOptions::default(),\n            display: None,\n        }\n    }\n\n    fn adapter_error(desc: wgpu::InstanceDescriptor) -> String {\n        let instance = wgpu::Instance::new(desc);\n        pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {\n            power_preference: wgpu::PowerPreference::default(),\n            force_fallback_adapter: false,\n            compatible_surface: None,\n        }))\n        .unwrap_err()\n        .to_string()\n    }\n\n    #[test]\n    fn no_backends_requested() {\n        assert_eq!(\n            adapter_error(id(wgpu::Backends::empty())),\n            \"No suitable graphics adapter found; \\\n            noop not requested, \\\n            vulkan not requested, \\\n            metal not requested, \\\n            dx12 not requested, \\\n            gl not requested, \\\n            webgpu not requested\"\n        );\n    }\n\n    /// This test nominally tests the noop backend, but it also exercises the logic for other backends\n    /// that fail to return any adapter.\n    #[test]\n    fn noop_not_enabled() {\n        assert_eq!(\n            adapter_error(id(wgpu::Backends::NOOP)),\n            \"No suitable graphics adapter found; \\\n            noop not explicitly enabled, \\\n            vulkan not requested, \\\n            metal not requested, \\\n            dx12 not requested, \\\n            gl not requested, \\\n            webgpu not requested\"\n        );\n    }\n\n    #[test]\n    fn no_compiled_support() {\n        // Whichever platform we are on, try asking for a backend that definitely will be\n        // cfged out regardless of feature flags. (Not that these tests run on wasm at all yet.)\n\n        #[cfg(target_family = \"wasm\")]\n        assert_eq!(\n            adapter_error(id(wgpu::Backends::METAL)),\n            \"No suitable graphics adapter found; \\\n            noop not requested, \\\n            vulkan not requested, \\\n            metal support not compiled in, \\\n            dx12 not requested, \\\n            gl not requested, \\\n            webgpu not requested\"\n        );\n\n        #[cfg(not(target_family = \"wasm\"))]\n        assert_eq!(\n            adapter_error(id(wgpu::Backends::BROWSER_WEBGPU)),\n            \"No suitable graphics adapter found; \\\n            noop not requested, \\\n            vulkan not requested, \\\n            metal not requested, \\\n            dx12 not requested, \\\n            gl not requested, \\\n            webgpu support not compiled in\"\n        );\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/mod.rs",
    "content": "mod binding_arrays;\nmod buffer;\nmod buffer_mapping;\nmod buffer_slice;\nmod command_buffer_actions;\nmod device;\nmod encoding;\nmod error_scopes;\nmod experimental;\nmod external_texture;\nmod instance;\nmod render_pipeline;\nmod texture;\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/render_pipeline.rs",
    "content": "//! Tests of [`wgpu::RenderPipeline`] and related.\n\nuse wgpu_test::fail;\n\n#[test]\nfn reject_fragment_shader_output_over_max_color_attachments() {\n    let (device, _queue) = wgpu::Device::noop(&Default::default());\n\n    // NOTE: Vertex shader is a boring quad. The fragment shader is the interesting part.\n    let source = format!(\n        \"\\\n@vertex\nfn vert(@builtin(vertex_index) vertex_index : u32) -> @builtin(position) vec4f {{\n    var pos = array<vec2f, 3>(\n        vec2(0.0, 0.5),\n        vec2(-0.5, -0.5),\n        vec2(0.5, -0.5)\n    );\n    return vec4f(pos[vertex_index], 0.0, 1.0);\n}}\n\n@fragment\nfn frag() -> @location({}) vec4f {{\n    return vec4(1.0, 0.0, 0.0, 1.0);\n}}\n\",\n        device.limits().max_color_attachments\n    );\n\n    let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n        label: None,\n        source: wgpu::ShaderSource::Wgsl(source.into()),\n    });\n    let module = &module;\n\n    fail(\n        &device,\n        || {\n            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                layout: None,\n                label: None,\n                vertex: wgpu::VertexState {\n                    module,\n                    entry_point: None,\n                    compilation_options: Default::default(),\n                    buffers: &[],\n                },\n                fragment: Some(wgpu::FragmentState {\n                    module,\n                    entry_point: None,\n                    compilation_options: Default::default(),\n                    targets: &[Some(wgpu::ColorTargetState {\n                        format: wgpu::TextureFormat::Rgba8Unorm,\n                        blend: None,\n                        write_mask: Default::default(),\n                    })],\n                }),\n                primitive: Default::default(),\n                depth_stencil: None,\n                multisample: Default::default(),\n                multiview_mask: None,\n                cache: None,\n            })\n        },\n        Some(concat!(\n            \"Location[8] Float32x4 interpolated as Some(Perspective) \",\n            \"with sampling Some(Center)'s index exceeds the `max_color_attachments` limit (8)\"\n        )),\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/api/texture.rs",
    "content": "//! Tests of [`wgpu::Texture`] and related.\n\nuse wgpu_test::{fail, valid};\n\n/// Ensures that submitting a command buffer referencing an already destroyed texture\n/// results in an error.\n#[test]\n#[should_panic = \"Texture with 'dst' label has been destroyed\"]\nfn destroyed_texture() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n    let texture_src = device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"src\"),\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n    let texture_dst = device.create_texture(&wgpu::TextureDescriptor {\n        label: Some(\"dst\"),\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::COPY_DST,\n        view_formats: &[],\n    });\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n    encoder.copy_texture_to_texture(\n        wgpu::TexelCopyTextureInfo {\n            texture: &texture_src,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        wgpu::TexelCopyTextureInfo {\n            texture: &texture_dst,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect: wgpu::TextureAspect::All,\n        },\n        size,\n    );\n\n    texture_dst.destroy();\n\n    queue.submit([encoder.finish()]);\n}\n\n/// Ensures that creating a texture view from a specific plane of a planar\n/// texture works as expected.\n#[test]\nfn planar_texture_view_plane() {\n    let required_features = wgpu::Features::TEXTURE_FORMAT_NV12\n        | wgpu::Features::TEXTURE_FORMAT_P010\n        | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    for (tex_format, view_format, view_aspect) in [\n        (\n            wgpu::TextureFormat::NV12,\n            wgpu::TextureFormat::R8Unorm,\n            wgpu::TextureAspect::Plane0,\n        ),\n        (\n            wgpu::TextureFormat::NV12,\n            wgpu::TextureFormat::Rg8Unorm,\n            wgpu::TextureAspect::Plane1,\n        ),\n        (\n            wgpu::TextureFormat::P010,\n            wgpu::TextureFormat::R16Unorm,\n            wgpu::TextureAspect::Plane0,\n        ),\n        (\n            wgpu::TextureFormat::P010,\n            wgpu::TextureFormat::Rg16Unorm,\n            wgpu::TextureAspect::Plane1,\n        ),\n    ] {\n        let tex = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: tex_format,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        valid(&device, || {\n            let _ = tex.create_view(&wgpu::TextureViewDescriptor {\n                format: Some(view_format),\n                aspect: view_aspect,\n                ..Default::default()\n            });\n        });\n    }\n}\n\n/// Ensures that attempting to create a texture view from a specific plane of a\n/// non-planar texture fails validation.\n#[test]\nfn non_planar_texture_view_plane() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n    let tex = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        dimension: wgpu::TextureDimension::D2,\n        size,\n        format: wgpu::TextureFormat::R8Unorm,\n        usage: wgpu::TextureUsages::TEXTURE_BINDING,\n        mip_level_count: 1,\n        sample_count: 1,\n        view_formats: &[],\n    });\n    fail(\n        &device,\n        || {\n            let _ = tex.create_view(&wgpu::TextureViewDescriptor {\n                aspect: wgpu::TextureAspect::Plane0,\n                ..Default::default()\n            });\n        },\n        Some(\"Aspect Plane0 is not a valid aspect of the source texture format R8Unorm\"),\n    );\n}\n\n/// Ensures that attempting to create a texture view from an invalid plane of a\n/// planar texture fails validation.\n#[test]\nfn planar_texture_view_plane_out_of_bounds() {\n    let required_features = wgpu::Features::TEXTURE_FORMAT_NV12\n        | wgpu::Features::TEXTURE_FORMAT_P010\n        | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    for (tex_format, view_format, view_aspect) in [\n        (\n            wgpu::TextureFormat::NV12,\n            wgpu::TextureFormat::R8Unorm,\n            wgpu::TextureAspect::Plane2,\n        ),\n        (\n            wgpu::TextureFormat::P010,\n            wgpu::TextureFormat::R16Unorm,\n            wgpu::TextureAspect::Plane2,\n        ),\n    ] {\n        let tex = device.create_texture(&wgpu::TextureDescriptor {\n            label: None,\n            dimension: wgpu::TextureDimension::D2,\n            size,\n            format: tex_format,\n            usage: wgpu::TextureUsages::TEXTURE_BINDING,\n            mip_level_count: 1,\n            sample_count: 1,\n            view_formats: &[],\n        });\n        fail(\n            &device,\n            || {\n                let _ = tex.create_view(&wgpu::TextureViewDescriptor {\n                    format: Some(view_format),\n                    aspect: view_aspect,\n                    ..Default::default()\n                });\n            },\n            Some(&format!(\n                \"Aspect {view_aspect:?} is not a valid aspect of the source texture format {tex_format:?}\"\n            )),\n        );\n    }\n}\n\n/// Ensures that attempting to create a texture view from a specific plane of a\n/// planar texture with an invalid format fails validation.\n#[test]\nfn planar_texture_bad_view_format() {\n    let required_features = wgpu::Features::TEXTURE_FORMAT_NV12\n        | wgpu::Features::TEXTURE_FORMAT_P010\n        | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n    for (tex_format, view_format) in [\n        (wgpu::TextureFormat::NV12, wgpu::TextureFormat::Rg8Unorm),\n        (wgpu::TextureFormat::P010, wgpu::TextureFormat::Rg16Unorm),\n    ] {\n        fail(\n            &device,\n            || {\n                let _ = device.create_texture(&wgpu::TextureDescriptor {\n                    label: None,\n                    dimension: wgpu::TextureDimension::D2,\n                    size,\n                    format: tex_format,\n                    usage: wgpu::TextureUsages::TEXTURE_BINDING,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    view_formats: &[view_format],\n                });\n            },\n            Some(&format!(\n                \"The view format {view_format:?} is not compatible with texture \\\n                 format {tex_format:?}, only changing srgb-ness is allowed.\"\n            )),\n        );\n    }\n}\n\n/// Ensures that attempting to create a planar texture with an invalid size\n/// fails validation.\n#[test]\nfn planar_texture_bad_size() {\n    let required_features =\n        wgpu::Features::TEXTURE_FORMAT_NV12 | wgpu::Features::TEXTURE_FORMAT_P010;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 255,\n        height: 255,\n        depth_or_array_layers: 1,\n    };\n    for format in [wgpu::TextureFormat::NV12, wgpu::TextureFormat::P010] {\n        fail(\n            &device,\n            || {\n                let _ = device.create_texture(&wgpu::TextureDescriptor {\n                    label: None,\n                    dimension: wgpu::TextureDimension::D2,\n                    size,\n                    format,\n                    usage: wgpu::TextureUsages::TEXTURE_BINDING,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    view_formats: &[],\n                });\n            },\n            Some(&format!(\n                \"width {} is not a multiple of {format:?}'s width multiple requirement\",\n                size.width\n            )),\n        );\n    }\n}\n\n/// Ensures that creating a planar textures that support `RENDER_ATTACHMENT` usage\n/// is possible.\n#[test]\nfn planar_texture_render_attachment() {\n    let required_features = wgpu::Features::TEXTURE_FORMAT_NV12;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    for (tex_format, view_format, view_aspect) in [\n        (\n            wgpu::TextureFormat::NV12,\n            wgpu::TextureFormat::R8Unorm,\n            wgpu::TextureAspect::Plane0,\n        ),\n        (\n            wgpu::TextureFormat::NV12,\n            wgpu::TextureFormat::Rg8Unorm,\n            wgpu::TextureAspect::Plane1,\n        ),\n    ] {\n        valid(&device, || {\n            let texture = device.create_texture(&wgpu::TextureDescriptor {\n                label: None,\n                dimension: wgpu::TextureDimension::D2,\n                size,\n                format: tex_format,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                mip_level_count: 1,\n                sample_count: 1,\n                view_formats: &[],\n            });\n\n            let _ = texture.create_view(&wgpu::TextureViewDescriptor {\n                format: Some(view_format),\n                aspect: view_aspect,\n                ..Default::default()\n            });\n        });\n    }\n}\n\n/// Ensures that creating a planar textures with `RENDER_ATTACHMENT`\n/// for non renderable planar formats fails validation.\n#[test]\nfn planar_texture_render_attachment_unsupported() {\n    let required_features =\n        wgpu::Features::TEXTURE_FORMAT_P010 | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    fail(\n        &device,\n        || {\n            let _ = device.create_texture(&wgpu::TextureDescriptor {\n                label: None,\n                dimension: wgpu::TextureDimension::D2,\n                size,\n                format: wgpu::TextureFormat::P010,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                mip_level_count: 1,\n                sample_count: 1,\n                view_formats: &[],\n            });\n        },\n        Some(\"Texture usages TextureUsages(RENDER_ATTACHMENT) are not allowed on a texture of type P010\"),\n    );\n}\n\n/// Creates a texture and a buffer, and encodes a copy from the texture to the\n/// buffer.\nfn encode_copy_texture_to_buffer(\n    device: &wgpu::Device,\n    format: wgpu::TextureFormat,\n    aspect: wgpu::TextureAspect,\n    bytes_per_texel: u32,\n) -> wgpu::CommandEncoder {\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    let texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format,\n        usage: wgpu::TextureUsages::COPY_SRC,\n        view_formats: &[],\n    });\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: (size.width * size.height * bytes_per_texel) as u64,\n        usage: wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    encoder.copy_texture_to_buffer(\n        wgpu::TexelCopyTextureInfo {\n            texture: &texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect,\n        },\n        wgpu::TexelCopyBufferInfo {\n            buffer: &buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size.width * bytes_per_texel),\n                rows_per_image: None,\n            },\n        },\n        size,\n    );\n\n    encoder\n}\n\n/// Ensures that attempting to copy a texture with a forbidden source format to\n/// a buffer fails validation.\n#[test]\nfn copy_texture_to_buffer_forbidden_format() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let format = wgpu::TextureFormat::Depth24Plus;\n\n    let encoder = encode_copy_texture_to_buffer(&device, format, wgpu::TextureAspect::All, 4);\n\n    fail(\n        &device,\n        || {\n            encoder.finish();\n        },\n        Some(&format!(\n            \"Copying from textures with format {format:?} is forbidden\"\n        )),\n    );\n}\n\n/// Ensures that attempting ta copy a texture with a forbidden source\n/// format/aspect combination to a buffer fails validation.\n#[test]\nfn copy_texture_to_buffer_forbidden_format_aspect() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let format = wgpu::TextureFormat::Depth24PlusStencil8;\n    let aspect = wgpu::TextureAspect::DepthOnly;\n\n    let encoder = encode_copy_texture_to_buffer(&device, format, aspect, 4);\n\n    fail(\n        &device,\n        || {\n            encoder.finish();\n        },\n        Some(&format!(\n            \"Copying from textures with format {format:?} and aspect {aspect:?} is forbidden\"\n        )),\n    );\n}\n\n/// Creates a texture and a buffer, and encodes a copy from the buffer to the\n/// texture.\nfn encode_copy_buffer_to_texture(\n    device: &wgpu::Device,\n    format: wgpu::TextureFormat,\n    aspect: wgpu::TextureAspect,\n    bytes_per_texel: u32,\n) -> wgpu::CommandEncoder {\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    let texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format,\n        usage: wgpu::TextureUsages::COPY_DST,\n        view_formats: &[],\n    });\n\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: (size.width * size.height * bytes_per_texel) as u64,\n        usage: wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder =\n        device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n    encoder.copy_buffer_to_texture(\n        wgpu::TexelCopyBufferInfo {\n            buffer: &buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(size.width * bytes_per_texel),\n                rows_per_image: None,\n            },\n        },\n        wgpu::TexelCopyTextureInfo {\n            texture: &texture,\n            mip_level: 0,\n            origin: wgpu::Origin3d::ZERO,\n            aspect,\n        },\n        size,\n    );\n\n    encoder\n}\n\n/// Ensures that attempting to copy a buffer to a texture with a forbidden\n/// destination format fails validation.\n#[test]\nfn copy_buffer_to_texture_forbidden_format() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    for format in [\n        wgpu::TextureFormat::Depth24Plus,\n        wgpu::TextureFormat::Depth32Float,\n    ] {\n        let encoder = encode_copy_buffer_to_texture(&device, format, wgpu::TextureAspect::All, 4);\n\n        fail(\n            &device,\n            || {\n                encoder.finish();\n            },\n            Some(&format!(\n                \"Copying to textures with format {format:?} is forbidden\"\n            )),\n        );\n    }\n}\n\n/// Ensures that attempting to copy a buffer to a texture with a forbidden\n/// destination format/aspect combination fails validation.\n#[test]\nfn copy_buffer_to_texture_forbidden_format_aspect() {\n    let required_features = wgpu::Features::DEPTH32FLOAT_STENCIL8;\n    let device_desc = wgpu::DeviceDescriptor {\n        required_features,\n        ..Default::default()\n    };\n    let (device, _queue) = wgpu::Device::noop(&device_desc);\n\n    let aspect = wgpu::TextureAspect::DepthOnly;\n\n    for (format, bytes_per_texel) in [\n        (wgpu::TextureFormat::Depth24PlusStencil8, 4),\n        (wgpu::TextureFormat::Depth32FloatStencil8, 8),\n    ] {\n        let encoder = encode_copy_buffer_to_texture(&device, format, aspect, bytes_per_texel);\n\n        fail(\n            &device,\n            || {\n                encoder.finish();\n            },\n            Some(&format!(\n                \"Copying to textures with format {format:?} and aspect {aspect:?} is forbidden\"\n            )),\n        );\n    }\n}\n\n/// Ensures that attempting to create a texture with [`wgpu::TextureUsages::TRANSIENT`]\n/// and its unsupported usages fails validation.\n#[test]\nfn transient_invalid_usage() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    let invalid_usages = wgpu::TextureUsages::all()\n        - wgpu::TextureUsages::RENDER_ATTACHMENT\n        - wgpu::TextureUsages::TRANSIENT;\n\n    for usage in invalid_usages {\n        let invalid_texture_descriptor = wgpu::TextureDescriptor {\n            label: None,\n            size,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8Unorm,\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT | usage,\n            view_formats: &[],\n        };\n        fail(\n            &device,\n            || device.create_texture(&invalid_texture_descriptor),\n            Some(&format!(\"Texture usage TextureUsages(TRANSIENT) is not compatible with texture usage {usage:?}\")),\n        );\n    }\n\n    let invalid_texture_descriptor = wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::TRANSIENT,\n        view_formats: &[],\n    };\n    fail(\n        &device,\n        || device.create_texture(&invalid_texture_descriptor),\n        Some(\"Invalid usage flags TextureUsages(TRANSIENT)\"),\n    );\n}\n\n/// Ensures that attempting to use a texture of [`wgpu::TextureUsages::TRANSIENT`]\n/// with [`wgpu::StoreOp::Store`] fails validation.\n#[test]\nfn transient_invalid_storeop() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    let size = wgpu::Extent3d {\n        width: 256,\n        height: 256,\n        depth_or_array_layers: 1,\n    };\n\n    let transient_texture = device.create_texture(&wgpu::TextureDescriptor {\n        label: None,\n        size,\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: wgpu::TextureDimension::D2,\n        format: wgpu::TextureFormat::Rgba8Unorm,\n        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TRANSIENT,\n        view_formats: &[],\n    });\n\n    fail(\n        &device,\n        || {\n            let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });\n\n            let invalid_render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view: &transient_texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),\n                        store: wgpu::StoreOp::Store,\n                    },\n                })],\n                depth_stencil_attachment: None,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            drop(invalid_render_pass);\n\n            encoder.finish()\n        },\n      Some(\"Color attachment's usage contains TextureUsages(TRANSIENT). This can only be used with StoreOp::Discard, but StoreOp::Store was provided\")\n    );\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/main.rs",
    "content": "//! Tests of the [`wgpu`] library API that are not run against a particular GPU.\n\nmod api;\nmod noop;\nmod util;\n"
  },
  {
    "path": "tests/tests/wgpu-validation/noop.rs",
    "content": "//! Tests of [`wgpu::Backend::Noop`].\n\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\nuse std::sync::Arc;\n\n#[test]\nfn device_is_not_available_by_default() {\n    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {\n        backends: wgpu::Backends::NOOP,\n        ..wgpu::InstanceDescriptor::new_without_display_handle()\n    });\n\n    pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))\n        .expect_err(\"noop backend adapter present when it should not be\");\n}\n\n#[test]\nfn device_is_available_when_requested() {\n    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {\n        backends: wgpu::Backends::NOOP,\n        backend_options: wgpu::BackendOptions {\n            noop: wgpu::NoopBackendOptions { enable: true },\n            ..Default::default()\n        },\n        ..wgpu::InstanceDescriptor::new_without_display_handle()\n    });\n\n    pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))\n        .expect(\"noop backend adapter absent when it should be\");\n}\n\n#[test]\nfn device_and_buffers() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n\n    // Demonstrate that creating and *writing* to a buffer succeeds.\n    // This also involves creation of a staging buffer.\n    let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"hello world\"),\n        size: 8,\n        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::COPY_SRC,\n        mapped_at_creation: false,\n    });\n    assert_eq!(buffer.size(), 8);\n    queue.write_buffer(&buffer, 0, &[1, 2, 3, 4]);\n    queue.write_buffer(&buffer, 4, &[5, 6, 7, 8]);\n\n    // Demonstrate that we can read back data from the buffer.\n    // This also involves copy_buffer_to_buffer().\n    let done: Arc<AtomicBool> = Arc::default();\n    let done2 = done.clone();\n    wgpu::util::DownloadBuffer::read_buffer(&device, &queue, &buffer.slice(..), move |result| {\n        assert_eq!(*result.unwrap(), [1, 2, 3, 4, 5, 6, 7, 8],);\n        done.store(true, Relaxed);\n    });\n    device.poll(wgpu::PollType::wait_indefinitely()).unwrap();\n    assert!(done2.load(Relaxed));\n}\n"
  },
  {
    "path": "tests/tests/wgpu-validation/util.rs",
    "content": "//! Tests of [`wgpu::util`].\n\nuse nanorand::Rng;\n\n/// Generate (deterministic) random staging belt operations to exercise its logic.\n#[test]\nfn staging_belt_random_test() {\n    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let mut rng = nanorand::WyRand::new_seed(0xDEAD_BEEF);\n    let buffer_size = 1024;\n    let align = wgpu::COPY_BUFFER_ALIGNMENT;\n    let mut belt = wgpu::util::StagingBelt::new(device.clone(), buffer_size / 2);\n    let target_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: None,\n        size: buffer_size,\n        usage: wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    for _batch in 0..100 {\n        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());\n\n        for _write in 0..5 {\n            let offset: u64 = rng.generate_range(0..=(buffer_size - align) / align) * align;\n            let size: u64 = rng.generate_range(1..=(buffer_size - offset) / align) * align;\n            println!(\"offset {offset} size {size}\");\n\n            let mut slice = belt.write_buffer(\n                &mut encoder,\n                &target_buffer,\n                offset,\n                wgpu::BufferSize::new(size).unwrap(),\n            );\n            // token amount of actual writing, just in case it makes a difference\n            slice.slice(..1).copy_from_slice(&[1]);\n        }\n\n        belt.finish();\n        queue.submit([encoder.finish()]);\n        belt.recall();\n    }\n}\n\n#[test]\nfn staging_belt_panics_with_invalid_buffer_usages() {\n    #[track_caller]\n    fn test_if_panics(usage: wgpu::BufferUsages) {\n        let result = std::panic::catch_unwind(|| {\n            let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n            let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(device.clone(), 512, usage);\n        });\n\n        if let Err(panic) = result {\n            // according to [1] the panic payload is either a `&str` or `String`\n            // [1]: https://doc.rust-lang.org/std/macro.panic.html\n\n            let message = if let Some(message) = panic.downcast_ref::<&str>() {\n                *message\n            } else if let Some(message) = panic.downcast_ref::<String>() {\n                message.as_str()\n            } else {\n                // don't know what this panic is, but it's not ours\n                std::panic::resume_unwind(panic);\n            };\n\n            let expected_message = format!(\"Only BufferUsages::COPY_SRC may be used when Features::MAPPABLE_PRIMARY_BUFFERS is not enabled. Specified buffer usages: {usage:?}\");\n            if expected_message == message {\n                // panicked with the correct message\n            } else {\n                // This is not our panic (or the panic message was changed)\n                std::panic::resume_unwind(panic);\n            }\n        } else {\n            panic!(\"StagingBelt::new_with_buffer_usages should panic without MAPPABLE_PRIMARY_BUFFERS with usage={usage:?}\");\n        }\n    }\n\n    // This tests that `StagingBelt::new_with_buffer_usages` panics for any buffer usages that contain anything else than `COPY_SRC | MAP_WRITE`.\n    //\n    // First we iterate over all possible buffer usages except `COPY_SRC | MAP_WRITE` (anything invalid).\n    for mut usage in wgpu::BufferUsages::all()\n        .difference(wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::MAP_WRITE)\n        .iter()\n    {\n        // check if the constructor panics with the selected buffer usage\n        test_if_panics(usage);\n\n        // add MAP_WRITE to the selected buffer usage and check that the constructor still panics\n        usage.insert(wgpu::BufferUsages::MAP_WRITE);\n        test_if_panics(usage);\n    }\n}\n\n#[test]\nfn staging_belt_works_with_non_exclusive_buffer_usages() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n    let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(\n        device.clone(),\n        512,\n        wgpu::BufferUsages::COPY_SRC,\n    );\n    let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(\n        device.clone(),\n        512,\n        wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::MAP_WRITE,\n    );\n    let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(\n        device.clone(),\n        512,\n        wgpu::BufferUsages::MAP_WRITE,\n    );\n}\n\n#[test]\nfn staging_belt_works_with_exclusive_buffer_usages_with_mappable_primary_buffers() {\n    let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor {\n        required_features: wgpu::Features::MAPPABLE_PRIMARY_BUFFERS,\n        ..Default::default()\n    });\n\n    // This tests that `StagingBelt::new_with_buffer_usages` works for any buffer usages that contain anything else than `COPY_SRC | MAP_WRITE`.\n    //\n    // First we iterate over all possible buffer usages except `COPY_SRC | MAP_WRITE` (anything that would be invalid without mappable primary buffers).\n    for usage in wgpu::BufferUsages::all()\n        .difference(wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::MAP_WRITE)\n        .iter()\n    {\n        // Check that the constructor doesn't panic without explicit `MAP_WRITE`\n        let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(device.clone(), 512, usage);\n\n        // Check that the constructor doesn't panic with explicitly `MAP_WRITE`\n        let _belt = wgpu::util::StagingBelt::new_with_buffer_usages(\n            device.clone(),\n            512,\n            wgpu::BufferUsages::MAP_WRITE,\n        );\n    }\n}\n"
  },
  {
    "path": "tests/tests/wgpu_trace.rs",
    "content": "//! Tests of [`wgpu::Buffer`] and related.\n\nuse std::any::Any;\n\nuse wgpu_core as wgc;\nuse wgpu_types as wgt;\n\nuse wgc::{command::Command, device::trace::Action};\n\n#[derive(Eq, PartialEq)]\nenum TestType {\n    Normal,\n    FailedCommands,\n    FailedSubmit,\n}\n\nfn trace_test(test_type: TestType) {\n    let global = wgc::global::Global::new(\n        \"test\",\n        wgt::instance::InstanceDescriptor {\n            backends: wgt::Backends::NOOP,\n            backend_options: wgt::BackendOptions {\n                noop: wgt::NoopBackendOptions { enable: true },\n                ..Default::default()\n            },\n            ..wgt::instance::InstanceDescriptor::new_without_display_handle()\n        },\n        None,\n    );\n    let adapter_id = global\n        .request_adapter(\n            &wgt::RequestAdapterOptions::default(),\n            wgt::Backends::NOOP,\n            None,\n        )\n        .unwrap();\n    let (device_id, queue_id) = global\n        .adapter_request_device(\n            adapter_id,\n            &wgt::DeviceDescriptor {\n                trace: wgt::Trace::Memory,\n                ..Default::default()\n            },\n            None,\n            None,\n        )\n        .unwrap();\n\n    let (buffer_id, error) = global.device_create_buffer(\n        device_id,\n        &wgt::BufferDescriptor {\n            label: None,\n            size: 1024,\n            usage: wgt::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        },\n        None,\n    );\n    assert!(error.is_none());\n\n    let (encoder_id, error) = global.device_create_command_encoder(\n        device_id,\n        &wgt::CommandEncoderDescriptor::default(),\n        None,\n    );\n    assert!(error.is_none());\n\n    match test_type {\n        TestType::Normal => {\n            global\n                .command_encoder_clear_buffer(encoder_id, buffer_id, 0, None)\n                .unwrap();\n            let (cmdbuf_id, error) = global.command_encoder_finish(\n                encoder_id,\n                &wgt::CommandBufferDescriptor::default(),\n                None,\n            );\n            assert!(error.is_none());\n            global.queue_submit(queue_id, &[cmdbuf_id]).unwrap();\n        }\n        TestType::FailedCommands => {\n            // Try to clear past the end of the buffer.\n            global\n                .command_encoder_clear_buffer(encoder_id, buffer_id, 0, Some(2048))\n                .unwrap();\n            let (_cmdbuf_id, error) = global.command_encoder_finish(\n                encoder_id,\n                &wgt::CommandBufferDescriptor::default(),\n                None,\n            );\n            assert!(error.is_some());\n        }\n        TestType::FailedSubmit => {\n            // Destroy the buffer after encoding the clear command, before submitting it.\n            global\n                .command_encoder_clear_buffer(encoder_id, buffer_id, 0, None)\n                .unwrap();\n            let (cmdbuf_id, error) = global.command_encoder_finish(\n                encoder_id,\n                &wgt::CommandBufferDescriptor::default(),\n                None,\n            );\n            assert!(error.is_none());\n            global.buffer_destroy(buffer_id);\n            global.queue_submit(queue_id, &[cmdbuf_id]).unwrap_err();\n        }\n    }\n\n    let trace = global.device_take_trace(device_id).unwrap();\n    let trace = (trace.as_ref() as &dyn Any)\n        .downcast_ref::<wgc::device::trace::MemoryTrace>()\n        .unwrap();\n    let actions = trace.actions();\n\n    match test_type {\n        TestType::Normal => {\n            let Some(Action::Submit(_, commands)) = actions.last() else {\n                panic!(\"expected last action to be Submit\");\n            };\n            assert_eq!(commands.len(), 1);\n            assert!(matches!(\n                commands[0],\n                Command::ClearBuffer {\n                    dst: _,\n                    offset: 0,\n                    size: None,\n                },\n            ));\n        }\n        TestType::FailedCommands => {\n            let Some(Action::FailedCommands {\n                commands: Some(commands),\n                failed_at_submit: None,\n                error,\n            }) = actions.last()\n            else {\n                panic!(\"expected last action to be FailedCommands\");\n            };\n            assert_eq!(\n                error,\n                \"Clear of 0..2048 would end up overrunning the bounds of the buffer of size 1024\"\n            );\n            assert_eq!(commands.len(), 1);\n            assert!(matches!(\n                commands[0],\n                Command::ClearBuffer {\n                    dst: _,\n                    offset: 0,\n                    size: Some(2048),\n                },\n            ));\n        }\n        TestType::FailedSubmit => {\n            let Some(Action::FailedCommands {\n                commands: Some(commands),\n                failed_at_submit: Some(_),\n                error,\n            }) = actions.last()\n            else {\n                panic!(\"expected last action to be FailedCommands\");\n            };\n            assert_eq!(error, \"Buffer with '' label has been destroyed\");\n            assert_eq!(commands.len(), 1);\n            assert!(matches!(\n                commands[0],\n                Command::ClearBuffer {\n                    dst: _,\n                    offset: 0,\n                    size: None,\n                },\n            ));\n        }\n    }\n}\n\n#[test]\nfn trace_clear_buffer() {\n    trace_test(TestType::Normal);\n}\n\n#[test]\nfn trace_failed_commands() {\n    trace_test(TestType::FailedCommands);\n}\n\n#[test]\nfn trace_failed_submit() {\n    trace_test(TestType::FailedSubmit);\n}\n"
  },
  {
    "path": "typos.toml",
    "content": "[files]\n# Include .github, .cargo, etc.\nignore-hidden = false\nextend-exclude = [\n    '/.git',\n    # spirv-asm isn't real source code\n    '*.spvasm',\n    'docs/big-picture.xml',\n    # This test has weird pattern-derived variable names.\n    'naga/tests/in/wgsl/abstract-types-builtins.wgsl',\n]\n\n# Corrections take the form of a key/value pair. The key is the incorrect word\n# and the value is the correct word. If the key and value are the same, the\n# word is treated as always correct. If the value is an empty string, the word\n# is treated as always incorrect.\n\n[default.extend-words]\n# Things that aren't typos\nconsts = \"consts\"\nlod = \"lod\"\nmetalness = \"metalness\"\n\n# A DXC command line argument\nFo = \"Fo\"\n\n# Usernames\nHealthire = \"Healthire\"\nREASY = \"REASY\"\n\n[type.rust.extend-identifiers]\nANDed = \"ANDed\"\nD3DCOLORtoUBYTE4 = \"D3DCOLORtoUBYTE4\"\nDerivate = \"Derivate\"\ninout = \"inout\"\nEct = \"Ect\"\n\n[type.wgsl]\nextend-glob = [\"*.wgsl\"]\n\n[type.wgsl.extend-identifiers]\npn = \"pn\"\n\n[type.yaml.extend-words]\ndota = \"dota\"\n"
  },
  {
    "path": "wgpu/Cargo.toml",
    "content": "[package]\nname = \"wgpu\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Cross-platform, safe, pure-rust graphics API\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nreadme = \"../README.md\"\nexclude = [\"Cargo.lock\"]\n\n# Override the workspace's `rust-version` key. `wgpu` and its dependencies\n# have a less strict MSRV, to allow users more leeway in updating their Rust toolchain.\n#\n# See the repo README for more information on MSRV policy.\nrust-version = \"1.87.0\"\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\ntargets = [\n    \"x86_64-unknown-linux-gnu\",\n    \"x86_64-apple-darwin\",\n    \"x86_64-pc-windows-msvc\",\n    \"wasm32-unknown-unknown\",\n]\n\n[package.metadata.cargo-machete]\n# Cargo machete can't check build.rs dependencies. See https://github.com/bnjbvr/cargo-machete/issues/100\nignored = [\"cfg_aliases\"]\n\n[lib]\n\n[features]\ndefault = [\n    \"std\",\n    \"parking_lot\",\n    \"dx12\",\n    \"metal\",\n    \"gles\",\n    \"vulkan\",\n    \"wgsl\",\n    \"webgpu\",\n]\n\n#! ### Backends\n# --------------------------------------------------------------------\n\n## Enables the DX12 backend on Windows.\ndx12 = [\"wgpu-core?/dx12\"]\n\n## Enables the Metal backend on macOS & iOS.\nmetal = [\"wgpu-core?/metal\"]\n\n## Enables the Vulkan backend on Windows, Linux, and Android.\nvulkan = [\"wgpu-core?/vulkan\"]\n\n## Enables the OpenGL/GLES backend on Windows, Linux, Android, and Emscripten.\ngles = [\"wgpu-core?/gles\"]\n\n## Enables the WebGPU backend on WebAssembly.\nwebgpu = [\n    \"web\",\n    \"naga?/wgsl-out\",\n    \"dep:wasm-bindgen-futures\",\n    \"web-sys/Document\",\n    \"web-sys/Event\",\n    \"web-sys/Navigator\",\n    \"web-sys/NodeList\",\n    \"web-sys/Window\",\n    \"web-sys/WorkerGlobalScope\",\n    \"web-sys/WorkerNavigator\",\n]\n\n#! ### Conditional Backends\n\n## Enables the GLES backend on macOS only for use with [ANGLE](https://github.com/google/angle).\nangle = [\"wgpu-core?/angle\"]\n\n## Enables the Vulkan backend on macOS & iOS only for use with [MoltenVK](https://github.com/KhronosGroup/MoltenVK).\nvulkan-portability = [\"wgpu-core?/vulkan-portability\"]\n\n## Enables the GLES backend on WebAssembly only.\nwebgl = [\"web\", \"wgpu-core/webgl\", \"dep:wgpu-hal\", \"dep:smallvec\"]\n\n## Enables the noop backend for testing.\n##\n## This backend allows creating resources such as buffers and textures,\n## but performs no computation.\n## Because it lacks basic functionality, it is only actually used if explicitly enabled\n## through `NoopBackendOptions`.\nnoop = [\"wgpu-core/noop\", \"dep:wgpu-hal\", \"dep:smallvec\"]\n\n#! **Note:** In the documentation, if you see that an item depends on a backend,\n#! it means that the item is only available when that backend is enabled _and_ the backend\n#! is supported on the current platform.\n\ncustom = []\n\n#! ### Shading language support\n# --------------------------------------------------------------------\n#! These features enable support for that input language on all platforms.\n#! We will translate the input language to whatever the backend requires.\n\n## Enable accepting SPIR-V shaders as input.\nspirv = [\"naga/spv-in\", \"wgpu-core?/spirv\"]\n\n## Enable accepting GLSL shaders as input.\nglsl = [\"naga/glsl-in\", \"wgpu-core?/glsl\"]\n\n## Enable accepting WGSL shaders as input.\nwgsl = [\"wgpu-core?/wgsl\"]\n\n## Enable accepting naga IR shaders as input.\nnaga-ir = [\"dep:naga\"]\n\n#! ### Assertions and Serialization\n# --------------------------------------------------------------------\n## Apply run-time checks, even in release builds. These are in addition\n## to the validation carried out at public APIs in all builds.\nstrict_asserts = [\"wgpu-core?/strict_asserts\", \"wgpu-types/strict_asserts\"]\n\n## Enables serialization via `serde` on common wgpu types.\nserde = [\"wgpu-core?/serde\", \"wgpu-types/serde\"]\n\n# ## Allow writing of trace capture files. See [`Adapter::request_device`].\ntrace = [\"serde\", \"wgpu-core?/trace\"]\n\n#! ### External libraries\n# --------------------------------------------------------------------\n#! The following features facilitate integration with third-party supporting libraries.\n\n## Enables statically linking DXC.\n##\n## Normally, to use the modern DXC shader compiler with wgpu, the final application\n## must be shipped alongside `dxcompiler.dll` (min v1.8.2502) (which can be downloaded from [Microsoft's GitHub][dxc]).\n## This feature statically links a version of DXC so that no external binaries are required\n## to compile DX12 shaders.\n##\n## [dxc]: https://github.com/Microsoft/DirectXShaderCompiler\nstatic-dxc = [\"wgpu-core?/static-dxc\"]\n\n#! ### Other\n# --------------------------------------------------------------------\n\n## Internally count resources and events for debugging purposes. If the counters\n## feature is disabled, the counting infrastructure is removed from the build and\n## the exposed counters always return 0.\ncounters = [\"wgpu-core?/counters\"]\n\n## Implement `Send` and `Sync` on Wasm, but only if atomics are not enabled.\n##\n## WebGL/WebGPU objects can not be shared between threads.\n## However, it can be useful to artificially mark them as `Send` and `Sync`\n## anyways to make it easier to write cross-platform code.\n## This is technically *very* unsafe in a multithreaded environment,\n## but on a wasm binary compiled without atomics is a definitionally single-threaded environment.\nfragile-send-sync-non-atomic-wasm = [\n    \"wgpu-core?/fragile-send-sync-non-atomic-wasm\",\n    \"wgpu-types/fragile-send-sync-non-atomic-wasm\",\n]\n\n## Use web-specific libraries on WASM\n##\n## Those libraries (wasm-bindgen, web-sys, js-sys) can only be used when there is a JavaScript\n## context around the WASM VM, e.g., when the WASM binary is used in a browser.\nweb = [\"dep:wasm-bindgen\", \"dep:js-sys\", \"dep:web-sys\", \"wgpu-types/web\"]\n\n## Enables use of the standard library within `wgpu` and its dependencies.\n##\n## This can allow for better error reporting and for improved multithreading\n## support.\nstd = [\n    \"raw-window-handle/std\",\n    \"wgpu-types/std\",\n    \"wgpu-core?/std\",\n    \"js-sys?/std\",\n    \"web-sys?/std\",\n    \"wasm-bindgen?/std\",\n    \"wasm-bindgen-futures?/std\",\n]\n\n## Uses `parking_lot` as the implementation for locking primitives.\n##\n## This is a recommended feature for most users and should only be disabled when\n## required, e.g., for `no_std` support.\n## If disabled, either `std::sync::Mutex` or `core::cell::RefCell` will be used,\n## based on whether `std` is enabled or not.\nparking_lot = [\"dep:parking_lot\"]\n\n#########################\n# Standard Dependencies #\n#########################\n\n[dependencies]\nnaga = { workspace = true, optional = true, features = [\"termcolor\"] }\nwgpu-core = { workspace = true, optional = true }\nwgpu-types.workspace = true\n\n# Needed for both wgpu-core and webgpu\narrayvec.workspace = true\nbitflags.workspace = true\nbytemuck.workspace = true\ncfg-if.workspace = true\ndocument-features.workspace = true\nlog.workspace = true\nhashbrown.workspace = true\nparking_lot = { workspace = true, optional = true }\nprofiling.workspace = true\nraw-window-handle = { workspace = true, features = [\"alloc\"] }\nstatic_assertions.workspace = true\n\n########################################\n# Target Specific Feature Dependencies #\n########################################\n\n###################\n# Not Webassembly #\n###################\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\n# Needed for only wgpu-core backend. Not optional as only wgpu-core backend exists on native platforms.\nwgpu-core = { workspace = true, features = [\n    \"renderdoc\",\n    \"wgsl\",            # needed for indirect draw/dispatch validation\n    \"portable-atomic\",\n] }\nwgpu-hal.workspace = true\n\nsmallvec.workspace = true\n\n###############\n# Webassembly #\n###############\n[target.'cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))'.dependencies]\n# Needed for all backends\njs-sys = { workspace = true, optional = true }\nwasm-bindgen = { workspace = true, optional = true }\nweb-sys = { workspace = true, optional = true, features = [\n    \"HtmlCanvasElement\",\n    \"OffscreenCanvas\",\n] }\n\n# Needed for only wgpu-core backend. Optional as webgl is optional on WebAssembly.\nwgpu-core = { workspace = true, optional = true }\nwgpu-hal = { workspace = true, optional = true }\n\nsmallvec = { workspace = true, optional = true }\n\n# Needed for the webgpu backend. Optional as webgpu is optional on WebAssembly.\nwasm-bindgen-futures = { workspace = true, optional = true }\n\n##############\n# Emscripten #\n##############\n[target.'cfg(target_os = \"emscripten\")'.dependencies]\nwgpu-core.workspace = true\nwgpu-hal.workspace = true\n\nsmallvec.workspace = true\n\n[target.'cfg(not(target_has_atomic = \"64\"))'.dependencies]\nportable-atomic.workspace = true\n\n[dev-dependencies]\n# Used in doc-tests\nbytemuck.workspace = true\n\n[build-dependencies]\ncfg_aliases.workspace = true\n"
  },
  {
    "path": "wgpu/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu/build.rs",
    "content": "fn main() {\n    cfg_aliases::cfg_aliases! {\n        native: { not(target_arch = \"wasm32\") },\n        Emscripten: { all(target_arch = \"wasm32\", target_os = \"emscripten\") },\n        web: { all(target_arch = \"wasm32\", not(Emscripten), feature = \"web\") },\n\n        send_sync: { any(\n            native,\n            all(feature = \"fragile-send-sync-non-atomic-wasm\", not(target_feature = \"atomics\"))\n        ) },\n\n        // Backends - keep this in sync with `wgpu-core/Cargo.toml` & docs in `wgpu/Cargo.toml`\n        webgpu: { all(not(native), not(Emscripten), feature = \"webgpu\") },\n        webgl: { all(not(native), not(Emscripten), feature = \"webgl\") },\n        dx12: { all(target_os = \"windows\", feature = \"dx12\") },\n        metal: { all(target_vendor = \"apple\", feature = \"metal\") },\n        vulkan: { any(\n            // The `vulkan` feature enables the Vulkan backend only on \"native Vulkan\" platforms, i.e. Windows/Linux/Android\n            all(any(windows, target_os = \"linux\", target_os = \"android\", target_os = \"freebsd\"), feature = \"vulkan\"),\n            // On Apple platforms, however, we require the `vulkan-portability` feature\n            // to explicitly opt-in to Vulkan since it's meant to be used with MoltenVK.\n            all(target_vendor = \"apple\", feature = \"vulkan-portability\")\n        ) },\n        gles: { any(\n            // The `gles` feature enables the OpenGL/GLES backend only on \"native OpenGL\" platforms, i.e. Windows, Linux, Android, and Emscripten.\n            // (Note that WebGL is also not included here!)\n            all(any(windows, target_os = \"linux\", target_os = \"android\", target_os = \"freebsd\", Emscripten), feature = \"gles\"),\n            // On Apple platforms, however, we require the `angle` feature to explicitly opt-in to OpenGL\n            // since its meant to be used with ANGLE.\n            all(target_vendor = \"apple\", feature = \"angle\")\n        ) },\n        noop: { feature = \"noop\" },\n\n        wgpu_core: {\n            any(\n                // On native, wgpu_core is currently always enabled, even if there's no backend enabled at all.\n                native,\n                // `wgpu_core` is implied if any backend other than WebGPU is enabled.\n                // (this is redundant except for `gles` and `noop`)\n                webgl, dx12, metal, vulkan, gles, noop\n            )\n        },\n\n        // This alias is _only_ if _we_ need naga in the wrapper. wgpu-core provides\n        // its own re-export of naga, which can be used in other situations\n        naga: { any(feature = \"naga-ir\", feature = \"spirv\", feature = \"glsl\") },\n        // ⚠️ Keep in sync with target.cfg() definition in wgpu-hal/Cargo.toml and cfg_alias in `wgpu-hal` crate ⚠️\n        static_dxc: { all(target_os = \"windows\", feature = \"static-dxc\", not(target_arch = \"aarch64\"), target_env = \"msvc\") },\n        supports_64bit_atomics: { target_has_atomic = \"64\" },\n        custom: {any(feature = \"custom\")},\n        std: { any(\n            feature = \"std\",\n            // TODO: Remove this when an alternative Mutex implementation is available for `no_std`.\n            // send_sync requires an appropriate Mutex implementation, which is only currently\n            // possible with `std` enabled.\n            send_sync,\n            // Unwinding panics necessitate access to `std` to determine if a thread is panicking\n            panic = \"unwind\"\n        ) },\n        no_std: { not(std) }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/adapter.rs",
    "content": "use alloc::vec::Vec;\nuse core::future::Future;\n#[cfg(wgpu_core)]\nuse core::ops::Deref;\n\nuse crate::*;\n\n/// Handle to a physical graphics and/or compute device.\n///\n/// Adapters can be created using [`Instance::request_adapter`]\n/// or other [`Instance`] methods.\n///\n/// Adapters can be used to open a connection to the corresponding [`Device`]\n/// on the host system by using [`Adapter::request_device`].\n///\n/// Does not have to be kept alive.\n///\n/// Corresponds to [WebGPU `GPUAdapter`](https://gpuweb.github.io/gpuweb/#gpu-adapter).\n#[derive(Debug, Clone)]\npub struct Adapter {\n    pub(crate) inner: dispatch::DispatchAdapter,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Adapter: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Adapter => .inner);\n\npub use wgt::RequestAdapterOptions as RequestAdapterOptionsBase;\n/// Additional information required when requesting an adapter.\n///\n/// For use with [`Instance::request_adapter`].\n///\n/// Corresponds to [WebGPU `GPURequestAdapterOptions`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions).\npub type RequestAdapterOptions<'a, 'b> = RequestAdapterOptionsBase<&'a Surface<'b>>;\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RequestAdapterOptions<'_, '_>: Send, Sync);\n\nimpl Adapter {\n    /// Requests a connection to a physical device, creating a logical device.\n    ///\n    /// Returns the [`Device`] together with a [`Queue`] that executes command buffers.\n    ///\n    /// [Per the WebGPU specification], an [`Adapter`] may only be used once to create a device.\n    /// If another device is wanted, call [`Instance::request_adapter()`] again to get a fresh\n    /// [`Adapter`].\n    /// However, `wgpu` does not currently enforce this restriction.\n    ///\n    /// # Panics\n    ///\n    /// - `request_device()` was already called on this `Adapter`.\n    /// - Features specified by `desc` are not supported by this adapter.\n    /// - Unsafe features were requested but not enabled when requesting the adapter.\n    /// - Limits requested exceed the values provided by the adapter.\n    /// - Adapter does not support all features wgpu requires to safely operate.\n    ///\n    /// [Per the WebGPU specification]: https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice\n    pub fn request_device(\n        &self,\n        desc: &DeviceDescriptor<'_>,\n    ) -> impl Future<Output = Result<(Device, Queue), RequestDeviceError>> + WasmNotSend {\n        let device = self.inner.request_device(desc);\n        async move {\n            device\n                .await\n                .map(|(device, queue)| (Device { inner: device }, Queue { inner: queue }))\n        }\n    }\n\n    /// Create a wgpu [`Device`] and [`Queue`] from a wgpu-hal [`hal::OpenDevice`].\n    ///\n    /// # Safety\n    ///\n    /// - `hal_device` must be created from this adapter internal handle.\n    /// - `desc.features` must be a subset of `hal_device`'s supported features.\n    #[cfg(wgpu_core)]\n    pub unsafe fn create_device_from_hal<A: hal::Api>(\n        &self,\n        hal_device: hal::OpenDevice<A>,\n        desc: &DeviceDescriptor<'_>,\n    ) -> Result<(Device, Queue), RequestDeviceError> {\n        let core_adapter = self.inner.as_core();\n        let (device, queue) = unsafe {\n            core_adapter\n                .context\n                .create_device_from_hal(core_adapter, hal_device, desc)\n        }?;\n\n        Ok((\n            Device {\n                inner: device.into(),\n            },\n            Queue {\n                inner: queue.into(),\n            },\n        ))\n    }\n\n    /// Get the [`wgpu_hal`] adapter from this `Adapter`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Adapter`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_metal!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_gles!(\"Adapter\")]\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The adapter is not from the backend specified by `A`.\n    /// - The adapter is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Adapter`]: hal::Api::Adapter\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &self,\n    ) -> Option<impl Deref<Target = A::Adapter> + WasmNotSendSync> {\n        let adapter = self.inner.as_core_opt()?;\n\n        unsafe { adapter.context.adapter_as_hal::<A>(adapter) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of adapter (if custom backend and is internally T)\n    pub fn as_custom<T: custom::AdapterInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n\n    #[cfg(custom)]\n    /// Creates Adapter from custom implementation\n    pub fn from_custom<T: custom::AdapterInterface>(adapter: T) -> Self {\n        Self {\n            inner: dispatch::DispatchAdapter::custom(adapter),\n        }\n    }\n\n    /// Returns whether this adapter may present to the passed surface.\n    pub fn is_surface_supported(&self, surface: &Surface<'_>) -> bool {\n        self.inner.is_surface_supported(&surface.inner)\n    }\n\n    /// The features which can be used to create devices on this adapter.\n    pub fn features(&self) -> Features {\n        self.inner.features()\n    }\n\n    /// The best limits which can be used to create devices on this adapter.\n    pub fn limits(&self) -> Limits {\n        self.inner.limits()\n    }\n\n    /// Get info about the adapter itself.\n    pub fn get_info(&self) -> AdapterInfo {\n        self.inner.get_info()\n    }\n\n    /// Get info about the adapter itself.\n    pub fn get_downlevel_capabilities(&self) -> DownlevelCapabilities {\n        self.inner.downlevel_capabilities()\n    }\n\n    /// Returns the features supported for a given texture format by this adapter.\n    ///\n    /// Note that the WebGPU spec further restricts the available usages/features.\n    /// To disable these restrictions on a device, request the [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] feature.\n    pub fn get_texture_format_features(&self, format: TextureFormat) -> TextureFormatFeatures {\n        self.inner.get_texture_format_features(format)\n    }\n\n    /// Generates a timestamp using the clock used by the presentation engine.\n    ///\n    /// When comparing completely opaque timestamp systems, we need a way of generating timestamps that signal\n    /// the exact same time. You can do this by calling your own timestamp function immediately after a call to\n    /// this function. This should result in timestamps that are 0.5 to 5 microseconds apart. There are locks\n    /// that must be taken during the call, so don't call your function before.\n    ///\n    /// ```no_run\n    /// # let adapter: wgpu::Adapter = panic!();\n    /// # let some_code = || wgpu::PresentationTimestamp::INVALID_TIMESTAMP;\n    /// use std::time::{Duration, Instant};\n    /// let presentation = adapter.get_presentation_timestamp();\n    /// let instant = Instant::now();\n    ///\n    /// // We can now turn a new presentation timestamp into an Instant.\n    /// let some_pres_timestamp = some_code();\n    /// let duration = Duration::from_nanos((some_pres_timestamp.0 - presentation.0) as u64);\n    /// let new_instant: Instant = instant + duration;\n    /// ```\n    //\n    /// [Instant]: std::time::Instant\n    pub fn get_presentation_timestamp(&self) -> PresentationTimestamp {\n        self.inner.get_presentation_timestamp()\n    }\n\n    /// Returns the supported cooperative matrix configurations for this adapter.\n    ///\n    /// Cooperative matrices enable hardware-accelerated matrix multiply-accumulate\n    /// operations where threads in a subgroup collectively process matrix tiles.\n    ///\n    /// Returns an empty vector if cooperative matrices are not supported.\n    ///\n    /// Requires [`Features::EXPERIMENTAL_COOPERATIVE_MATRIX`] to be meaningful.\n    pub fn cooperative_matrix_properties(&self) -> Vec<CooperativeMatrixProperties> {\n        self.inner.cooperative_matrix_properties()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/bind_group.rs",
    "content": "use crate::*;\n\n/// Handle to a binding group.\n///\n/// A `BindGroup` represents the set of resources bound to the bindings described by a\n/// [`BindGroupLayout`]. It can be created with [`Device::create_bind_group`]. A `BindGroup` can\n/// be bound to a particular [`RenderPass`] with [`RenderPass::set_bind_group`], or to a\n/// [`ComputePass`] with [`ComputePass::set_bind_group`].\n///\n/// Corresponds to [WebGPU `GPUBindGroup`](https://gpuweb.github.io/gpuweb/#gpubindgroup).\n#[derive(Debug, Clone)]\npub struct BindGroup {\n    pub(crate) inner: dispatch::DispatchBindGroup,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BindGroup: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(BindGroup => .inner);\n\nimpl BindGroup {\n    #[cfg(custom)]\n    /// Returns custom implementation of BindGroup (if custom backend and is internally T)\n    pub fn as_custom<T: custom::BindGroupInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Resource to be bound by a [`BindGroup`] for use with a pipeline.\n///\n/// The pipeline’s [`BindGroupLayout`] must contain a matching [`BindingType`].\n///\n/// Corresponds to [WebGPU `GPUBindingResource`](\n/// https://gpuweb.github.io/gpuweb/#typedefdef-gpubindingresource).\n#[non_exhaustive]\n#[derive(Clone, Debug)]\npub enum BindingResource<'a> {\n    /// Binding is backed by a buffer.\n    ///\n    /// Corresponds to [`wgt::BufferBindingType::Uniform`] and [`wgt::BufferBindingType::Storage`]\n    /// with [`BindGroupLayoutEntry::count`] set to None.\n    Buffer(BufferBinding<'a>),\n    /// Binding is backed by an array of buffers.\n    ///\n    /// [`Features::BUFFER_BINDING_ARRAY`] must be supported to use this feature.\n    ///\n    /// Corresponds to [`wgt::BufferBindingType::Uniform`] and [`wgt::BufferBindingType::Storage`]\n    /// with [`BindGroupLayoutEntry::count`] set to Some.\n    BufferArray(&'a [BufferBinding<'a>]),\n    /// Binding is a sampler.\n    ///\n    /// Corresponds to [`wgt::BindingType::Sampler`] with [`BindGroupLayoutEntry::count`] set to None.\n    Sampler(&'a Sampler),\n    /// Binding is backed by an array of samplers.\n    ///\n    /// [`Features::TEXTURE_BINDING_ARRAY`] must be supported to use this feature.\n    ///\n    /// Corresponds to [`wgt::BindingType::Sampler`] with [`BindGroupLayoutEntry::count`] set\n    /// to Some.\n    SamplerArray(&'a [&'a Sampler]),\n    /// Binding is backed by a texture.\n    ///\n    /// Corresponds to [`wgt::BindingType::Texture`] and [`wgt::BindingType::StorageTexture`] with\n    /// [`BindGroupLayoutEntry::count`] set to None.\n    TextureView(&'a TextureView),\n    /// Binding is backed by an array of textures.\n    ///\n    /// [`Features::TEXTURE_BINDING_ARRAY`] must be supported to use this feature.\n    ///\n    /// Corresponds to [`wgt::BindingType::Texture`] and [`wgt::BindingType::StorageTexture`] with\n    /// [`BindGroupLayoutEntry::count`] set to Some.\n    TextureViewArray(&'a [&'a TextureView]),\n    /// Binding is backed by a top level acceleration structure\n    ///\n    /// Corresponds to [`wgt::BindingType::AccelerationStructure`] with [`BindGroupLayoutEntry::count`] set to None.\n    ///\n    /// # Validation\n    /// When using (e.g. with `set_bind_group`) a bind group that has been created with one or more of this binding\n    /// resource certain checks take place.\n    /// - TLAS must have been built, if not a validation error is generated\n    /// - All BLASes that were built into the TLAS must be built before the TLAS, if this was not satisfied and TLAS was\n    ///   built using `build_acceleration_structures` a validation error is generated otherwise this is a part of the\n    ///   safety section of `build_acceleration_structures_unsafe_tlas` and so undefined behavior occurs.\n    AccelerationStructure(&'a Tlas),\n    /// Binding is backed by an array of top level acceleration structures.\n    ///\n    /// Corresponds to [`wgt::BindingType::AccelerationStructure`] with [`BindGroupLayoutEntry::count`] set to Some.\n    ///\n    /// # Validation\n    /// The same validation rules apply as for [`BindingResource::AccelerationStructure`], for each element.\n    ///\n    /// Note: backend support may vary; this is primarily intended for native backends.\n    AccelerationStructureArray(&'a [&'a Tlas]),\n    /// Binding is backed by an external texture.\n    ///\n    /// [`Features::EXTERNAL_TEXTURE`] must be supported to use this feature.\n    ///\n    /// Corresponds to [`wgt::BindingType::ExternalTexture`].\n    ExternalTexture(&'a ExternalTexture),\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BindingResource<'_>: Send, Sync);\n\n/// Describes the segment of a buffer to bind.\n///\n/// Corresponds to [WebGPU `GPUBufferBinding`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpubufferbinding).\n#[derive(Clone, Debug)]\npub struct BufferBinding<'a> {\n    /// The buffer to bind.\n    pub buffer: &'a Buffer,\n\n    /// Base offset of the buffer, in bytes.\n    ///\n    /// If the [`has_dynamic_offset`] field of this buffer's layout entry is\n    /// `true`, the offset here will be added to the dynamic offset passed to\n    /// [`RenderPass::set_bind_group`] or [`ComputePass::set_bind_group`].\n    ///\n    /// If the buffer was created with [`BufferUsages::UNIFORM`], then this\n    /// offset must be a multiple of\n    /// [`Limits::min_uniform_buffer_offset_alignment`].\n    ///\n    /// If the buffer was created with [`BufferUsages::STORAGE`], then this\n    /// offset must be a multiple of\n    /// [`Limits::min_storage_buffer_offset_alignment`].\n    ///\n    /// [`has_dynamic_offset`]: BindingType::Buffer::has_dynamic_offset\n    pub offset: BufferAddress,\n\n    /// Size of the binding in bytes, or `None` for using the rest of the buffer.\n    pub size: Option<BufferSize>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BufferBinding<'_>: Send, Sync);\n\n/// An element of a [`BindGroupDescriptor`], consisting of a bindable resource\n/// and the slot to bind it to.\n///\n/// Corresponds to [WebGPU `GPUBindGroupEntry`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpubindgroupentry).\n#[derive(Clone, Debug)]\npub struct BindGroupEntry<'a> {\n    /// Slot for which binding provides resource. Corresponds to an entry of the same\n    /// binding index in the [`BindGroupLayoutDescriptor`].\n    pub binding: u32,\n    /// Resource to attach to the binding\n    pub resource: BindingResource<'a>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BindGroupEntry<'_>: Send, Sync);\n\n/// Describes a group of bindings and the resources to be bound.\n///\n/// For use with [`Device::create_bind_group`].\n///\n/// Corresponds to [WebGPU `GPUBindGroupDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpubindgroupdescriptor).\n#[derive(Clone, Debug)]\npub struct BindGroupDescriptor<'a> {\n    /// Debug label of the bind group. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The [`BindGroupLayout`] that corresponds to this bind group.\n    pub layout: &'a BindGroupLayout,\n    /// The resources to bind to this bind group.\n    pub entries: &'a [BindGroupEntry<'a>],\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BindGroupDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/bind_group_layout.rs",
    "content": "use crate::*;\n\n/// Handle to a binding group layout.\n///\n/// A `BindGroupLayout` is a handle to the GPU-side layout of a binding group. It can be used to\n/// create a [`BindGroupDescriptor`] object, which in turn can be used to create a [`BindGroup`]\n/// object with [`Device::create_bind_group`]. A series of `BindGroupLayout`s can also be used to\n/// create a [`PipelineLayoutDescriptor`], which can be used to create a [`PipelineLayout`].\n///\n/// It can be created with [`Device::create_bind_group_layout`].\n///\n/// Corresponds to [WebGPU `GPUBindGroupLayout`](\n/// https://gpuweb.github.io/gpuweb/#gpubindgrouplayout).\n#[derive(Debug, Clone)]\npub struct BindGroupLayout {\n    pub(crate) inner: dispatch::DispatchBindGroupLayout,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BindGroupLayout: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(BindGroupLayout => .inner);\n\nimpl BindGroupLayout {\n    #[cfg(custom)]\n    /// Returns custom implementation of BindGroupLayout (if custom backend and is internally T)\n    pub fn as_custom<T: custom::BindGroupLayoutInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`BindGroupLayout`].\n///\n/// For use with [`Device::create_bind_group_layout`].\n///\n/// Corresponds to [WebGPU `GPUBindGroupLayoutDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpubindgrouplayoutdescriptor).\n#[derive(Clone, Debug)]\npub struct BindGroupLayoutDescriptor<'a> {\n    /// Debug label of the bind group layout. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n\n    /// Array of entries in this BindGroupLayout\n    pub entries: &'a [BindGroupLayoutEntry],\n}\nstatic_assertions::assert_impl_all!(BindGroupLayoutDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/blas.rs",
    "content": "#[cfg(wgpu_core)]\nuse core::ops::Deref;\n\nuse alloc::{boxed::Box, vec::Vec};\n\nuse wgt::{WasmNotSend, WasmNotSendSync};\n\nuse crate::dispatch;\nuse crate::{Buffer, Label};\n\n/// Descriptor for the size defining attributes of a triangle geometry, for a bottom level acceleration structure.\npub type BlasTriangleGeometrySizeDescriptor = wgt::BlasTriangleGeometrySizeDescriptor;\nstatic_assertions::assert_impl_all!(BlasTriangleGeometrySizeDescriptor: Send, Sync);\n\n/// Descriptor for the size defining attributes, for a bottom level acceleration structure.\npub type BlasGeometrySizeDescriptors = wgt::BlasGeometrySizeDescriptors;\nstatic_assertions::assert_impl_all!(BlasGeometrySizeDescriptors: Send, Sync);\n\n/// Flags for an acceleration structure.\npub type AccelerationStructureFlags = wgt::AccelerationStructureFlags;\nstatic_assertions::assert_impl_all!(AccelerationStructureFlags: Send, Sync);\n\n/// Flags for a geometry inside a bottom level acceleration structure.\npub type AccelerationStructureGeometryFlags = wgt::AccelerationStructureGeometryFlags;\nstatic_assertions::assert_impl_all!(AccelerationStructureGeometryFlags: Send, Sync);\n\n/// Update mode for acceleration structure builds.\npub type AccelerationStructureUpdateMode = wgt::AccelerationStructureUpdateMode;\nstatic_assertions::assert_impl_all!(AccelerationStructureUpdateMode: Send, Sync);\n\n/// Descriptor to create bottom level acceleration structures.\npub type CreateBlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(CreateBlasDescriptor<'_>: Send, Sync);\n\n/// Safe instance for a [Tlas].\n///\n/// A TlasInstance may be made invalid, if a TlasInstance is invalid, any attempt to build a [Tlas] containing an\n/// invalid TlasInstance will generate a validation error\n///\n/// Each one contains:\n/// - A reference to a BLAS, this ***must*** be interacted with using [TlasInstance::new] or [TlasInstance::set_blas], a\n///   TlasInstance that references a BLAS keeps that BLAS from being dropped\n/// - A user accessible transformation matrix\n/// - A user accessible mask\n/// - A user accessible custom index\n///\n/// [Tlas]: crate::Tlas\n#[derive(Debug, Clone)]\npub struct TlasInstance {\n    pub(crate) blas: dispatch::DispatchBlas,\n    /// Affine transform matrix 3x4 (rows x columns, row major order).\n    pub transform: [f32; 12],\n    /// Custom index for the instance used inside the shader.\n    ///\n    /// This must only use the lower 24 bits, if any bits are outside that range (byte 4 does not equal 0) the TlasInstance becomes\n    /// invalid and generates a validation error when built\n    pub custom_data: u32,\n    /// Mask for the instance used inside the shader to filter instances.\n    /// Reports hit only if `(shader_cull_mask & tlas_instance.mask) != 0u`.\n    pub mask: u8,\n}\n\nimpl TlasInstance {\n    /// Construct TlasInstance.\n    /// - blas: Reference to the bottom level acceleration structure\n    /// - transform: Transform buffer offset in bytes (optional, required if transform buffer is present)\n    /// - custom_data: Custom index for the instance used inside the shader (max 24 bits)\n    /// - mask: Mask for the instance used inside the shader to filter instances\n    ///\n    /// Note: while one of these contains a reference to a BLAS that BLAS will not be dropped,\n    /// but it can still be destroyed. Destroying a BLAS that is referenced by one or more\n    /// TlasInstance(s) will immediately make them invalid. If one or more of those invalid\n    /// TlasInstances is inside a TlasPackage that is attempted to be built, the build will\n    /// generate a validation error.\n    pub fn new(blas: &Blas, transform: [f32; 12], custom_data: u32, mask: u8) -> Self {\n        Self {\n            blas: blas.inner.clone(),\n            transform,\n            custom_data,\n            mask,\n        }\n    }\n\n    /// Set the bottom level acceleration structure.\n    ///\n    /// See the note on [TlasInstance] about the\n    /// guarantees of keeping a BLAS alive.\n    pub fn set_blas(&mut self, blas: &Blas) {\n        self.blas = blas.inner.clone();\n    }\n}\n\n#[derive(Debug)]\n/// Definition for a triangle geometry for a Bottom Level Acceleration Structure (BLAS).\n///\n/// The size must match the rest of the structures fields, otherwise the build will fail.\n/// (e.g. if index count is present in the size, the index buffer must be present as well.)\npub struct BlasTriangleGeometry<'a> {\n    /// Sub descriptor for the size defining attributes of a triangle geometry.\n    pub size: &'a BlasTriangleGeometrySizeDescriptor,\n    /// Vertex buffer.\n    pub vertex_buffer: &'a Buffer,\n    /// Offset into the vertex buffer as a factor of the vertex stride.\n    pub first_vertex: u32,\n    /// Vertex stride, must be greater than [`wgpu_types::VertexFormat::min_acceleration_structure_vertex_stride`]\n    /// of the format and must be a multiple of [`wgpu_types::VertexFormat::acceleration_structure_stride_alignment`].\n    pub vertex_stride: wgt::BufferAddress,\n    /// Index buffer (optional).\n    pub index_buffer: Option<&'a Buffer>,\n    /// Number of indexes to skip in the index buffer (optional, required if index buffer is present).\n    pub first_index: Option<u32>,\n    /// Transform buffer containing 3x4 (rows x columns, row major) affine transform matrices `[f32; 12]` (optional).\n    pub transform_buffer: Option<&'a Buffer>,\n    /// Transform buffer offset in bytes (optional, required if transform buffer is present).\n    pub transform_buffer_offset: Option<wgt::BufferAddress>,\n}\nstatic_assertions::assert_impl_all!(BlasTriangleGeometry<'_>: WasmNotSendSync);\n\n/// Contains the sets of geometry that go into a [Blas].\npub enum BlasGeometries<'a> {\n    /// Triangle geometry variant.\n    TriangleGeometries(Vec<BlasTriangleGeometry<'a>>),\n}\nstatic_assertions::assert_impl_all!(BlasGeometries<'_>: WasmNotSendSync);\n\n/// Builds the given sets of geometry into the given [Blas].\npub struct BlasBuildEntry<'a> {\n    /// Reference to the acceleration structure.\n    pub blas: &'a Blas,\n    /// Geometries.\n    pub geometry: BlasGeometries<'a>,\n}\nstatic_assertions::assert_impl_all!(BlasBuildEntry<'_>: WasmNotSendSync);\n\n#[derive(Debug, Clone)]\n/// Bottom Level Acceleration Structure (BLAS).\n///\n/// A BLAS is a device-specific raytracing acceleration structure that contains geometry data.\n///\n/// These BLASes are combined with transform in a [TlasInstance] to create a [Tlas].\n///\n/// [Tlas]: crate::Tlas\npub struct Blas {\n    pub(crate) handle: Option<u64>,\n    pub(crate) inner: dispatch::DispatchBlas,\n}\nstatic_assertions::assert_impl_all!(Blas: WasmNotSendSync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Blas => .inner);\n\nimpl Blas {\n    /// Raw handle to the acceleration structure, used inside raw instance buffers.\n    pub fn handle(&self) -> Option<u64> {\n        self.handle\n    }\n\n    /// Get the [`wgpu_hal`] acceleration structure from this `Blas`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::AccelerationStructure`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_metal!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_dx12!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_gles!(\"AccelerationStructure\")]\n    ///\n    /// # Deadlocks\n    ///\n    /// - The returned guard holds a read-lock on a device-local \"destruction\"\n    ///   lock, which will cause all calls to `destroy` to block until the\n    ///   guard is released.\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The acceleration structure is not from the backend specified by `A`.\n    /// - The acceleration structure is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::AccelerationStructure`]: hal::Api::AccelerationStructure\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &mut self,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure> + WasmNotSendSync> {\n        let blas = self.inner.as_core_opt()?;\n        unsafe { blas.context.blas_as_hal::<A>(blas) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Blas (if custom backend and is internally T)\n    pub fn as_custom<T: crate::custom::BlasInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Context version of [BlasTriangleGeometry].\npub struct ContextBlasTriangleGeometry<'a> {\n    #[expect(dead_code)]\n    pub(crate) size: &'a BlasTriangleGeometrySizeDescriptor,\n    #[expect(dead_code)]\n    pub(crate) vertex_buffer: &'a dispatch::DispatchBuffer,\n    #[expect(dead_code)]\n    pub(crate) index_buffer: Option<&'a dispatch::DispatchBuffer>,\n    #[expect(dead_code)]\n    pub(crate) transform_buffer: Option<&'a dispatch::DispatchBuffer>,\n    #[expect(dead_code)]\n    pub(crate) first_vertex: u32,\n    #[expect(dead_code)]\n    pub(crate) vertex_stride: wgt::BufferAddress,\n    #[expect(dead_code)]\n    pub(crate) index_buffer_offset: Option<wgt::BufferAddress>,\n    #[expect(dead_code)]\n    pub(crate) transform_buffer_offset: Option<wgt::BufferAddress>,\n}\n\n/// Context version of [BlasGeometries].\npub enum ContextBlasGeometries<'a> {\n    /// Triangle geometries.\n    TriangleGeometries(Box<dyn Iterator<Item = ContextBlasTriangleGeometry<'a>> + 'a>),\n}\n\n/// Context version see [BlasBuildEntry].\npub struct ContextBlasBuildEntry<'a> {\n    #[expect(dead_code)]\n    pub(crate) blas: &'a dispatch::DispatchBlas,\n    #[expect(dead_code)]\n    pub(crate) geometries: ContextBlasGeometries<'a>,\n}\n\n/// Error occurred when trying to asynchronously prepare a blas for compaction.\n#[derive(Clone, PartialEq, Eq, Debug)]\npub struct BlasAsyncError;\nstatic_assertions::assert_impl_all!(BlasAsyncError: Send, Sync);\n\nimpl core::fmt::Display for BlasAsyncError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(\n            f,\n            \"Error occurred when trying to asynchronously prepare a blas for compaction\"\n        )\n    }\n}\n\nimpl core::error::Error for BlasAsyncError {}\n\nimpl Blas {\n    /// Asynchronously prepares this BLAS for compaction. The callback is called once all builds\n    /// using this BLAS are finished and the BLAS is compactable. This can be checked using\n    /// [`Blas::ready_for_compaction`]. Rebuilding this BLAS will reset its compacted state, and it\n    /// will need to be prepared again.\n    ///\n    /// ### Interaction with other functions\n    /// On native, `queue.submit(..)` and polling devices (that is calling `instance.poll_all` or\n    /// `device.poll`) with [`PollType::Poll`] may call the callback. On native, polling devices with\n    /// [`PollType::Wait`] (optionally with a submission index greater\n    /// than the last submit the BLAS was used in) will guarantee callback is called.\n    ///\n    /// [`PollType::Poll`]: wgpu_types::PollType::Poll\n    /// [`PollType::Wait`]: wgpu_types::PollType::Wait\n    pub fn prepare_compaction_async(\n        &self,\n        callback: impl FnOnce(Result<(), BlasAsyncError>) + WasmNotSend + 'static,\n    ) {\n        self.inner.prepare_compact_async(Box::new(callback));\n    }\n\n    /// Checks whether this BLAS is ready for compaction. The returned value is `true` if\n    /// [`Blas::prepare_compaction_async`]'s callback was called with a non-error value, otherwise\n    /// this is `false`.\n    pub fn ready_for_compaction(&self) -> bool {\n        self.inner.ready_for_compaction()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/buffer.rs",
    "content": "use alloc::{boxed::Box, sync::Arc, vec::Vec};\nuse core::{\n    error, fmt,\n    ops::{Bound, Deref, Range, RangeBounds},\n};\n\nuse crate::util::Mutex;\nuse crate::*;\n\n/// Handle to a GPU-accessible buffer.\n///\n/// A `Buffer` is a memory allocation for use by the GPU, somewhat analogous to\n/// <code>[Box]&lt;[\\[u8\\]][primitive@slice]&gt;</code> in Rust.\n/// The contents of buffers are untyped bytes; it is up to the application to\n/// specify the interpretation of the bytes when the buffer is used, in ways\n/// such as [`VertexBufferLayout`].\n/// A single buffer can be used to hold multiple independent pieces of data at\n/// different offsets (e.g. both vertices and indices for one or more meshes).\n///\n/// A `Buffer`'s bytes have \"interior mutability\": functions like\n/// [`Queue::write_buffer`] or [mapping] a buffer for writing only require a\n/// `&Buffer`, not a `&mut Buffer`, even though they modify its contents. `wgpu`\n/// prevents simultaneous reads and writes of buffer contents using run-time\n/// checks.\n///\n/// Created with [`Device::create_buffer()`] or\n/// [`DeviceExt::create_buffer_init()`].\n///\n/// Corresponds to [WebGPU `GPUBuffer`](https://gpuweb.github.io/gpuweb/#buffer-interface).\n///\n/// [mapping]: Buffer#mapping-buffers\n///\n/// # How to get your data into a buffer\n///\n/// Every `Buffer` starts with all bytes zeroed.\n/// There are many ways to load data into a `Buffer`:\n///\n/// - When creating a buffer, you may set the [`mapped_at_creation`][mac] flag,\n///   then write to its [`get_mapped_range_mut()`][Buffer::get_mapped_range_mut].\n///   This only works when the buffer is created and has not yet been used by\n///   the GPU, but it is all you need for buffers whose contents do not change\n///   after creation.\n///   - You may use [`DeviceExt::create_buffer_init()`] as a convenient way to\n///     do that and copy data from a `&[u8]` you provide.\n/// - After creation, you may use [`Buffer::map_async()`] to map it again;\n///   however, you then need to wait until the GPU is no longer using the buffer\n///   before you begin writing.\n/// - You may use [`CommandEncoder::copy_buffer_to_buffer()`] to copy data into\n///   this buffer from another buffer.\n/// - You may use [`Queue::write_buffer()`] to copy data into the buffer from a\n///   `&[u8]`. This uses a temporary “staging” buffer managed by `wgpu` to hold\n///   the data.\n///   - [`Queue::write_buffer_with()`] allows you to write directly into temporary\n///     storage instead of providing a slice you already prepared, which may\n///     allow *your* code to save the allocation of a [`Vec`] or such.\n/// - You may use [`util::StagingBelt`] to manage a set of temporary buffers.\n///   This may be more efficient than [`Queue::write_buffer_with()`] when you\n///   have many small copies to perform, but requires more steps to use, and\n///   tuning of the belt buffer size.\n/// - You may write your own staging buffer management customized to your\n///   application, based on mapped buffers and\n///   [`CommandEncoder::copy_buffer_to_buffer()`].\n/// - A GPU computation’s results can be stored in a buffer:\n///   - A [compute shader][ComputePipeline] may write to a buffer bound as a\n///     [storage buffer][BufferBindingType::Storage].\n///   - A render pass may render to a texture which is then copied to a buffer\n///     using [`CommandEncoder::copy_texture_to_buffer()`].\n///\n/// # Mapping buffers\n///\n/// If a `Buffer` is created with the appropriate [`usage`], it can be *mapped*:\n/// you can make its contents accessible to the CPU as an ordinary `&[u8]` or\n/// `&mut [u8]` slice of bytes. Buffers created with the\n/// [`mapped_at_creation`][mac] flag set are also mapped initially.\n///\n/// Depending on the hardware, the buffer could be memory shared between CPU and\n/// GPU, so that the CPU has direct access to the same bytes the GPU will\n/// consult; or it may be ordinary CPU memory, whose contents the system must\n/// copy to/from the GPU as needed. This crate's API is designed to work the\n/// same way in either case: at any given time, a buffer is either mapped and\n/// available to the CPU, or unmapped and ready for use by the GPU, but never\n/// both. This makes it impossible for either side to observe changes by the\n/// other immediately, and any necessary transfers can be carried out when the\n/// buffer transitions from one state to the other.\n///\n/// There are two ways to map a buffer:\n///\n/// - If [`BufferDescriptor::mapped_at_creation`] is `true`, then the entire\n///   buffer is mapped when it is created. This is the easiest way to initialize\n///   a new buffer. You can set `mapped_at_creation` on any kind of buffer,\n///   regardless of its [`usage`] flags.\n///\n/// - If the buffer's [`usage`] includes the [`MAP_READ`] or [`MAP_WRITE`]\n///   flags, then you can call `buffer.slice(range).map_async(mode, callback)`\n///   to map the portion of `buffer` given by `range`. This waits for the GPU to\n///   finish using the buffer, and invokes `callback` as soon as the buffer is\n///   safe for the CPU to access.\n///\n/// Once a buffer is mapped:\n///\n/// - You can call `buffer.slice(range).get_mapped_range()` to obtain a\n///   [`BufferView`], which dereferences to a `&[u8]` that you can use to read\n///   the buffer's contents.\n///\n/// - Or, you can call `buffer.slice(range).get_mapped_range_mut()` to obtain a\n///   [`BufferViewMut`], which dereferences to a `&mut [u8]` that you can use to\n///   read and write the buffer's contents.\n///\n/// The given `range` must fall within the mapped portion of the buffer. If you\n/// attempt to access overlapping ranges, even for shared access only, these\n/// methods panic.\n///\n/// While a buffer is mapped, you may not submit any commands to the GPU that\n/// access it. You may record command buffers that use the buffer, but if you\n/// submit them while the buffer is mapped, submission will panic.\n///\n/// When you are done using the buffer on the CPU, you must call\n/// [`Buffer::unmap`] to make it available for use by the GPU again. All\n/// [`BufferView`] and [`BufferViewMut`] views referring to the buffer must be\n/// dropped before you unmap it; otherwise, [`Buffer::unmap`] will panic.\n///\n/// # Example\n///\n/// If `buffer` was created with [`BufferUsages::MAP_WRITE`], we could fill it\n/// with `f32` values like this:\n///\n/// ```\n/// # #[cfg(feature = \"noop\")]\n/// # let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());\n/// # #[cfg(not(feature = \"noop\"))]\n/// # let device: wgpu::Device = { return; };\n/// #\n/// # let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n/// #     label: None,\n/// #     size: 400,\n/// #     usage: wgpu::BufferUsages::MAP_WRITE,\n/// #     mapped_at_creation: false,\n/// # });\n/// let capturable = buffer.clone();\n/// buffer.map_async(wgpu::MapMode::Write, .., move |result| {\n///     if result.is_ok() {\n///         let mut view = capturable.get_mapped_range_mut(..);\n///         let mut floats: wgpu::WriteOnly<[[u8; 4]]> = view.slice(..).into_chunks::<4>().0;\n///         floats.fill(42.0f32.to_ne_bytes());\n///         drop(view);\n///         capturable.unmap();\n///     }\n/// });\n/// ```\n///\n/// This code takes the following steps:\n///\n/// - First, it makes a cloned handle to the buffer for capture by\n///   the callback passed to [`map_async`]. Since a [`map_async`] callback may be\n///   invoked from another thread, interaction between the callback and the\n///   thread calling [`map_async`] generally requires some sort of shared heap\n///   data like this. In real code, there might be an [`Arc`] to some larger\n///   structure that itself owns `buffer`.\n///\n/// - Then, it calls [`Buffer::slice`] to make a [`BufferSlice`] referring to\n///   the buffer's entire contents.\n///\n/// - Next, it calls [`BufferSlice::map_async`] to request that the bytes to\n///   which the slice refers be made accessible to the CPU (\"mapped\"). This may\n///   entail waiting for previously enqueued operations on `buffer` to finish.\n///   Although [`map_async`] itself always returns immediately, it saves the\n///   callback function to be invoked later.\n///\n/// - When some later call to [`Device::poll`] or [`Instance::poll_all`] (not\n///   shown in this example) determines that the buffer is mapped and ready for\n///   the CPU to use, it invokes the callback function.\n///\n/// - The callback function calls [`Buffer::slice`] and then\n///   [`BufferSlice::get_mapped_range_mut`] to obtain a [`BufferViewMut`], which\n///   dereferences to a `&mut [u8]` slice referring to the buffer's bytes.\n///\n/// - It then uses the [`bytemuck`] crate to turn the `&mut [u8]` into a `&mut\n///   [f32]`, and calls the slice [`fill`] method to fill the buffer with a\n///   useful value.\n///\n/// - Finally, the callback drops the view and calls [`Buffer::unmap`] to unmap\n///   the buffer. In real code, the callback would also need to do some sort of\n///   synchronization to let the rest of the program know that it has completed\n///   its work.\n///\n/// If using [`map_async`] directly is awkward, you may find it more convenient to\n/// use [`Queue::write_buffer`] and [`util::DownloadBuffer::read_buffer`].\n/// However, those each have their own tradeoffs; the asynchronous nature of GPU\n/// execution makes it hard to avoid friction altogether.\n///\n/// [`Arc`]: std::sync::Arc\n/// [`map_async`]: BufferSlice::map_async\n/// [`bytemuck`]: https://crates.io/crates/bytemuck\n/// [`fill`]: slice::fill\n///\n/// ## Mapping buffers on the web\n///\n/// When compiled to WebAssembly and running in a browser content process,\n/// `wgpu` implements its API in terms of the browser's WebGPU implementation.\n/// In this context, `wgpu` is further isolated from the GPU:\n///\n/// - Depending on the browser's WebGPU implementation, mapping and unmapping\n///   buffers probably entails copies between WebAssembly linear memory and the\n///   graphics driver's buffers.\n///\n/// - All modern web browsers isolate web content in its own sandboxed process,\n///   which can only interact with the GPU via interprocess communication (IPC).\n///   Although most browsers' IPC systems use shared memory for large data\n///   transfers, there will still probably need to be copies into and out of the\n///   shared memory buffers.\n///\n/// All of these copies contribute to the cost of buffer mapping in this\n/// configuration.\n///\n/// [`usage`]: BufferDescriptor::usage\n/// [mac]: BufferDescriptor::mapped_at_creation\n/// [`MAP_READ`]: BufferUsages::MAP_READ\n/// [`MAP_WRITE`]: BufferUsages::MAP_WRITE\n/// [`DeviceExt::create_buffer_init()`]: util::DeviceExt::create_buffer_init\n#[derive(Debug, Clone)]\npub struct Buffer {\n    pub(crate) inner: dispatch::DispatchBuffer,\n    pub(crate) map_context: Arc<Mutex<MapContext>>,\n    pub(crate) size: wgt::BufferAddress,\n    pub(crate) usage: BufferUsages,\n    // Todo: missing map_state https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapstate\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Buffer: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Buffer => .inner);\n\nimpl Buffer {\n    /// Return the binding view of the entire buffer.\n    pub fn as_entire_binding(&self) -> BindingResource<'_> {\n        BindingResource::Buffer(self.as_entire_buffer_binding())\n    }\n\n    /// Return the binding view of the entire buffer.\n    pub fn as_entire_buffer_binding(&self) -> BufferBinding<'_> {\n        BufferBinding {\n            buffer: self,\n            offset: 0,\n            size: None,\n        }\n    }\n\n    /// Get the [`wgpu_hal`] buffer from this `Buffer`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Buffer`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_metal!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_gles!(\"Buffer\")]\n    ///\n    /// # Deadlocks\n    ///\n    /// - The returned guard holds a read-lock on a device-local \"destruction\"\n    ///   lock, which will cause all calls to `destroy` to block until the\n    ///   guard is released.\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The buffer is not from the backend specified by `A`.\n    /// - The buffer is from the `webgpu` or `custom` backend.\n    /// - The buffer has had [`Self::destroy()`] called on it.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Buffer`]: hal::Api::Buffer\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &self,\n    ) -> Option<impl Deref<Target = A::Buffer> + WasmNotSendSync> {\n        let buffer = self.inner.as_core_opt()?;\n        unsafe { buffer.context.buffer_as_hal::<A>(buffer) }\n    }\n\n    /// Returns a [`BufferSlice`] referring to the portion of `self`'s contents\n    /// indicated by `bounds`. Regardless of what sort of data `self` stores,\n    /// `bounds` start and end are given in bytes.\n    ///\n    /// A [`BufferSlice`] can be used to supply vertex and index data, or to map\n    /// buffer contents for access from the CPU. See the [`BufferSlice`]\n    /// documentation for details.\n    ///\n    /// The `range` argument can be half or fully unbounded: for example,\n    /// `buffer.slice(..)` refers to the entire buffer, and `buffer.slice(n..)`\n    /// refers to the portion starting at the `n`th byte and extending to the\n    /// end of the buffer.\n    ///\n    /// # Panics\n    ///\n    /// - If `bounds` is outside of the bounds of `self`.\n    /// - If `bounds` has a length less than 1.\n    #[track_caller]\n    pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'_> {\n        let (offset, size) = range_to_offset_size(bounds, self.size);\n        check_buffer_bounds(self.size, offset, size);\n        BufferSlice {\n            buffer: self,\n            offset,\n            size,\n        }\n    }\n\n    /// Unmaps the buffer from host memory.\n    ///\n    /// This terminates the effect of all previous [`map_async()`](Self::map_async) operations and\n    /// makes the buffer available for use by the GPU again.\n    pub fn unmap(&self) {\n        self.map_context.lock().reset();\n        self.inner.unmap();\n    }\n\n    /// Destroy the associated native resources as soon as possible.\n    pub fn destroy(&self) {\n        self.inner.destroy();\n    }\n\n    /// Returns the length of the buffer allocation in bytes.\n    ///\n    /// This is always equal to the `size` that was specified when creating the buffer.\n    pub fn size(&self) -> BufferAddress {\n        self.size\n    }\n\n    /// Returns the allowed usages for this `Buffer`.\n    ///\n    /// This is always equal to the `usage` that was specified when creating the buffer.\n    pub fn usage(&self) -> BufferUsages {\n        self.usage\n    }\n\n    /// Map the buffer to host (CPU) memory, making it available for reading or writing via\n    /// [`get_mapped_range()`](Self::get_mapped_range). The buffer becomes accessible once the\n    /// `callback` is invoked with [`Ok`].\n    ///\n    /// Use this when you want to map the buffer immediately. If you need to submit GPU work that\n    /// uses the buffer before mapping it, use `map_buffer_on_submit` on\n    /// [`CommandEncoder`][CEmbos], [`CommandBuffer`][CBmbos], [`RenderPass`][RPmbos], or\n    /// [`ComputePass`][CPmbos] to schedule the mapping after submission. This avoids extra calls to\n    /// [`Buffer::map_async()`] or [`BufferSlice::map_async()`] and lets you initiate mapping from a\n    /// more convenient place.\n    ///\n    /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],\n    /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated into\n    /// an event loop or run on a separate thread.\n    ///\n    /// The callback runs on the thread that first calls one of the above functions after the GPU work\n    /// completes. There are no restrictions on the code you can run in the callback; however, on native\n    /// the polling call will not return until the callback finishes, so keep callbacks short (set flags,\n    /// send messages, etc.).\n    ///\n    /// While a buffer is mapped, it cannot be used by other commands; at any time, either the GPU or\n    /// the CPU has exclusive access to the buffer’s contents.\n    ///\n    /// This can also be performed using [`BufferSlice::map_async()`].\n    ///\n    /// # Panics\n    ///\n    /// - If the buffer is already mapped.\n    /// - If the buffer’s [`BufferUsages`] do not allow the requested [`MapMode`].\n    /// - If `bounds` is outside of the bounds of `self`.\n    /// - If `bounds` does not start at a multiple of [`MAP_ALIGNMENT`].\n    /// - If `bounds` has a length that is not a multiple of 4 greater than 0.\n    ///\n    /// [CEmbos]: CommandEncoder::map_buffer_on_submit\n    /// [CBmbos]: CommandBuffer::map_buffer_on_submit\n    /// [RPmbos]: RenderPass::map_buffer_on_submit\n    /// [CPmbos]: ComputePass::map_buffer_on_submit\n    /// [q::s]: Queue::submit\n    /// [i::p_a]: Instance::poll_all\n    /// [d::p]: Device::poll\n    pub fn map_async<S: RangeBounds<BufferAddress>>(\n        &self,\n        mode: MapMode,\n        bounds: S,\n        callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static,\n    ) {\n        self.slice(bounds).map_async(mode, callback)\n    }\n\n    /// Gain read-only access to the bytes of a [mapped] [`Buffer`].\n    ///\n    /// Returns a [`BufferView`] referring to the buffer range represented by\n    /// `self`. See the documentation for [`BufferView`] for details.\n    ///\n    /// `bounds` may be less than the bounds passed to [`Self::map_async()`],\n    /// and multiple views may be obtained and used simultaneously as long as they do not overlap.\n    ///\n    /// This can also be performed using [`BufferSlice::get_mapped_range()`].\n    ///\n    /// # Panics\n    ///\n    /// - If `bounds` is outside of the bounds of `self`.\n    /// - If `bounds` does not start at a multiple of [`MAP_ALIGNMENT`].\n    /// - If `bounds` has a length that is not a multiple of 4 greater than 0.\n    /// - If the buffer to which `self` refers is not currently [mapped].\n    /// - If you try to create a view which overlaps an existing [`BufferViewMut`].\n    ///\n    /// [mapped]: Buffer#mapping-buffers\n    #[track_caller]\n    pub fn get_mapped_range<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferView {\n        self.slice(bounds).get_mapped_range()\n    }\n\n    /// Gain write access to the bytes of a [mapped] [`Buffer`].\n    ///\n    /// Returns a [`BufferViewMut`] referring to the buffer range represented by\n    /// `self`. See the documentation for [`BufferViewMut`] for more details.\n    ///\n    /// `bounds` may be less than the bounds passed to [`Self::map_async()`],\n    /// and multiple views may be obtained and used simultaneously as long as they do not overlap.\n    ///\n    /// This can also be performed using [`BufferSlice::get_mapped_range_mut()`].\n    ///\n    /// # Panics\n    ///\n    /// - If `bounds` is outside of the bounds of `self`.\n    /// - If `bounds` does not start at a multiple of [`MAP_ALIGNMENT`].\n    /// - If `bounds` has a length that is not a multiple of 4 greater than 0.\n    /// - If the buffer to which `self` refers is not currently [mapped].\n    /// - If you try to create a view which overlaps an existing [`BufferView`] or [`BufferViewMut`].\n    ///\n    /// [mapped]: Buffer#mapping-buffers\n    #[track_caller]\n    pub fn get_mapped_range_mut<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferViewMut {\n        self.slice(bounds).get_mapped_range_mut()\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Buffer (if custom backend and is internally T)\n    pub fn as_custom<T: custom::BufferInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// A slice of a [`Buffer`], to be mapped, used for vertex or index data, or the like.\n///\n/// You can create a `BufferSlice` by calling [`Buffer::slice`]:\n///\n/// ```no_run\n/// # let buffer: wgpu::Buffer = todo!();\n/// let slice = buffer.slice(10..20);\n/// ```\n///\n/// This returns a slice referring to the second ten bytes of `buffer`. To get a\n/// slice of the entire `Buffer`:\n///\n/// ```no_run\n/// # let buffer: wgpu::Buffer = todo!();\n/// let whole_buffer_slice = buffer.slice(..);\n/// ```\n///\n/// You can pass buffer slices to methods like [`RenderPass::set_vertex_buffer`]\n/// and [`RenderPass::set_index_buffer`] to indicate which portion of the buffer\n/// a draw call should consult. You can also convert it to a [`BufferBinding`]\n/// with `.into()`.\n///\n/// To access the slice's contents on the CPU, you must first [map] the buffer,\n/// and then call [`BufferSlice::get_mapped_range`] or\n/// [`BufferSlice::get_mapped_range_mut`] to obtain a view of the slice's\n/// contents. See the documentation on [mapping][map] for more details,\n/// including example code.\n///\n/// Unlike a Rust shared slice `&[T]`, whose existence guarantees that\n/// nobody else is modifying the `T` values to which it refers, a\n/// [`BufferSlice`] doesn't guarantee that the buffer's contents aren't\n/// changing. You can still record and submit commands operating on the\n/// buffer while holding a [`BufferSlice`]. A [`BufferSlice`] simply\n/// represents a certain range of the buffer's bytes.\n///\n/// The `BufferSlice` type is unique to the Rust API of `wgpu`. In the WebGPU\n/// specification, an offset and size are specified as arguments to each call\n/// working with the [`Buffer`], instead.\n///\n/// [map]: Buffer#mapping-buffers\n#[derive(Copy, Clone, Debug, PartialEq)]\npub struct BufferSlice<'a> {\n    pub(crate) buffer: &'a Buffer,\n    pub(crate) offset: BufferAddress,\n    pub(crate) size: BufferSize,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(BufferSlice<'_>: Send, Sync);\n\nimpl<'a> BufferSlice<'a> {\n    /// Return another [`BufferSlice`] referring to the portion of `self`'s contents\n    /// indicated by `bounds`.\n    ///\n    /// The `range` argument can be half or fully unbounded: for example,\n    /// `buffer.slice(..)` refers to the entire buffer, and `buffer.slice(n..)`\n    /// refers to the portion starting at the `n`th byte and extending to the\n    /// end of the buffer.\n    ///\n    /// # Panics\n    ///\n    /// - If `bounds` is outside of the bounds of `self`.\n    /// - If `bounds` has a length less than 1.\n    #[track_caller]\n    pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'a> {\n        let (offset, size) = range_to_offset_size(bounds, self.size.get());\n        check_buffer_bounds(self.size.get(), offset, size);\n        BufferSlice {\n            buffer: self.buffer,\n            offset: self.offset + offset, // check_buffer_bounds ensures this does not overflow\n            size,                         // check_buffer_bounds ensures this is essentially min()\n        }\n    }\n\n    /// Map the buffer to host (CPU) memory, making it available for reading or writing via\n    /// [`get_mapped_range()`](Self::get_mapped_range). The buffer becomes accessible once the\n    /// `callback` is invoked with [`Ok`].\n    ///\n    /// Use this when you want to map the buffer immediately. If you need to submit GPU work that\n    /// uses the buffer before mapping it, use `map_buffer_on_submit` on\n    /// [`CommandEncoder`][CEmbos], [`CommandBuffer`][CBmbos], [`RenderPass`][RPmbos], or\n    /// [`ComputePass`][CPmbos] to schedule the mapping after submission. This avoids extra calls to\n    /// [`Buffer::map_async()`] or [`BufferSlice::map_async()`] and lets you initiate mapping from a\n    /// more convenient place.\n    ///\n    /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],\n    /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated into\n    /// an event loop or run on a separate thread.\n    ///\n    /// The callback runs on the thread that first calls one of the above functions after the GPU work\n    /// completes. There are no restrictions on the code you can run in the callback; however, on native\n    /// the polling call will not return until the callback finishes, so keep callbacks short (set flags,\n    /// send messages, etc.).\n    ///\n    /// While a buffer is mapped, it cannot be used by other commands; at any time, either the GPU or\n    /// the CPU has exclusive access to the buffer’s contents.\n    ///\n    /// This can also be performed using [`Buffer::map_async()`].\n    ///\n    /// # Panics\n    ///\n    /// - If the buffer is already mapped.\n    /// - If the buffer’s [`BufferUsages`] do not allow the requested [`MapMode`].\n    /// - If the beginning of this slice is not aligned to [`MAP_ALIGNMENT`] within the buffer.\n    /// - If the length of this slice is not a multiple of 4.\n    ///\n    /// [CEmbos]: CommandEncoder::map_buffer_on_submit\n    /// [CBmbos]: CommandBuffer::map_buffer_on_submit\n    /// [RPmbos]: RenderPass::map_buffer_on_submit\n    /// [CPmbos]: ComputePass::map_buffer_on_submit\n    /// [q::s]: Queue::submit\n    /// [i::p_a]: Instance::poll_all\n    /// [d::p]: Device::poll\n    pub fn map_async(\n        &self,\n        mode: MapMode,\n        callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static,\n    ) {\n        let mut mc = self.buffer.map_context.lock();\n        assert_eq!(mc.mapped_range, 0..0, \"Buffer is already mapped\");\n        let end = self.offset + self.size.get();\n        mc.mapped_range = self.offset..end;\n        drop(mc); // release the lock of map_context as callback can call lock it again\n\n        self.buffer\n            .inner\n            .map_async(mode, self.offset..end, Box::new(callback));\n    }\n\n    /// Gain read-only access to the bytes of a [mapped] [`Buffer`].\n    ///\n    /// Returns a [`BufferView`] referring to the buffer range represented by\n    /// `self`. See the documentation for [`BufferView`] for details.\n    ///\n    /// Multiple views may be obtained and used simultaneously as long as they are from\n    /// non-overlapping slices.\n    ///\n    /// This can also be performed using [`Buffer::get_mapped_range()`].\n    ///\n    /// # Panics\n    ///\n    /// - If the beginning of this slice is not aligned to [`MAP_ALIGNMENT`] within the buffer.\n    /// - If the length of this slice is not a multiple of 4.\n    /// - If the buffer to which `self` refers is not currently [mapped].\n    /// - If you try to create a view which overlaps an existing [`BufferViewMut`].\n    ///\n    /// [mapped]: Buffer#mapping-buffers\n    #[track_caller]\n    pub fn get_mapped_range(&self) -> BufferView {\n        let subrange = Subrange::new(self.offset, self.size, RangeMappingKind::Immutable);\n        self.buffer\n            .map_context\n            .lock()\n            .validate_and_add(subrange.clone());\n        let range = self.buffer.inner.get_mapped_range(subrange.index);\n        BufferView {\n            buffer: self.buffer.clone(),\n            size: self.size,\n            offset: self.offset,\n            inner: range,\n        }\n    }\n\n    /// Gain write-only access to the bytes of a [mapped] [`Buffer`].\n    ///\n    /// Returns a [`BufferViewMut`] referring to the buffer range represented by\n    /// `self`. See the documentation for [`BufferViewMut`] for more details.\n    ///\n    /// Multiple views may be obtained and used simultaneously as long as they are from\n    /// non-overlapping slices.\n    ///\n    /// This can also be performed using [`Buffer::get_mapped_range_mut()`].\n    ///\n    /// # Panics\n    ///\n    /// - If the beginning of this slice is not aligned to [`MAP_ALIGNMENT`] within the buffer.\n    /// - If the length of this slice is not a multiple of 4.\n    /// - If the buffer to which `self` refers is not currently [mapped].\n    /// - If you try to create a view which overlaps an existing [`BufferView`] or [`BufferViewMut`].\n    ///\n    /// [mapped]: Buffer#mapping-buffers\n    #[track_caller]\n    pub fn get_mapped_range_mut(&self) -> BufferViewMut {\n        let subrange = Subrange::new(self.offset, self.size, RangeMappingKind::Mutable);\n        self.buffer\n            .map_context\n            .lock()\n            .validate_and_add(subrange.clone());\n        let range = self.buffer.inner.get_mapped_range(subrange.index);\n        BufferViewMut {\n            buffer: self.buffer.clone(),\n            size: self.size,\n            offset: self.offset,\n            inner: range,\n        }\n    }\n\n    /// Returns the buffer this is a slice of.\n    ///\n    /// You should usually not need to call this, and if you received the buffer from code you\n    /// do not control, you should refrain from accessing the buffer outside the bounds of the\n    /// slice. Nevertheless, it’s possible to get this access, so this method makes it simple.\n    pub fn buffer(&self) -> &'a Buffer {\n        self.buffer\n    }\n\n    /// Returns the offset in [`Self::buffer()`] this slice starts at.\n    pub fn offset(&self) -> BufferAddress {\n        self.offset\n    }\n\n    /// Returns the size of this slice.\n    pub fn size(&self) -> BufferSize {\n        self.size\n    }\n}\n\nimpl<'a> From<BufferSlice<'a>> for crate::BufferBinding<'a> {\n    /// Convert a [`BufferSlice`] to an equivalent [`BufferBinding`],\n    /// provided that it will be used without a dynamic offset.\n    fn from(value: BufferSlice<'a>) -> Self {\n        BufferBinding {\n            buffer: value.buffer,\n            offset: value.offset,\n            size: Some(value.size),\n        }\n    }\n}\n\nimpl<'a> From<BufferSlice<'a>> for crate::BindingResource<'a> {\n    /// Convert a [`BufferSlice`] to an equivalent [`BindingResource::Buffer`],\n    /// provided that it will be used without a dynamic offset.\n    fn from(value: BufferSlice<'a>) -> Self {\n        crate::BindingResource::Buffer(crate::BufferBinding::from(value))\n    }\n}\n\nfn range_overlaps(a: &Range<BufferAddress>, b: &Range<BufferAddress>) -> bool {\n    a.start < b.end && b.start < a.end\n}\n\nfn range_contains(a: &Range<BufferAddress>, b: &Range<BufferAddress>) -> bool {\n    a.start <= b.start && a.end >= b.end\n}\n\n#[derive(Debug, Copy, Clone)]\nenum RangeMappingKind {\n    Mutable,\n    Immutable,\n}\n\nimpl RangeMappingKind {\n    /// Returns true if a range of this kind can touch the same bytes as a range of the other kind.\n    ///\n    /// This is Rust's Mutable XOR Shared rule.\n    fn allowed_concurrently_with(self, other: Self) -> bool {\n        matches!(\n            (self, other),\n            (RangeMappingKind::Immutable, RangeMappingKind::Immutable)\n        )\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct Subrange {\n    index: Range<BufferAddress>,\n    kind: RangeMappingKind,\n}\n\nimpl Subrange {\n    fn new(offset: BufferAddress, size: BufferSize, kind: RangeMappingKind) -> Self {\n        Self {\n            index: offset..(offset + size.get()),\n            kind,\n        }\n    }\n}\n\nimpl fmt::Display for Subrange {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(\n            f,\n            \"{}..{} ({:?})\",\n            self.index.start, self.index.end, self.kind\n        )\n    }\n}\n\n/// The mapped portion of a buffer, if any, and its outstanding views.\n///\n/// This ensures that views fall within the mapped range and don't overlap.\n#[derive(Debug)]\npub(crate) struct MapContext {\n    /// The range of the buffer that is mapped.\n    ///\n    /// This is `0..0` if the buffer is not mapped. This becomes non-empty when\n    /// the buffer is mapped at creation time, and when you call `map_async` on\n    /// some [`BufferSlice`] (so technically, it indicates the portion that is\n    /// *or has been requested to be* mapped.)\n    ///\n    /// All [`BufferView`]s and [`BufferViewMut`]s must fall within this range.\n    mapped_range: Range<BufferAddress>,\n\n    /// The ranges covered by all outstanding [`BufferView`]s and\n    /// [`BufferViewMut`]s. These are non-overlapping, and are all contained\n    /// within `mapped_range`.\n    sub_ranges: Vec<Subrange>,\n}\n\nimpl MapContext {\n    /// Creates a new `MapContext`.\n    ///\n    /// For [`mapped_at_creation`] buffers, pass the full buffer range in the\n    /// `mapped_range` argument. For other buffers, pass `None`.\n    ///\n    /// [`mapped_at_creation`]: BufferDescriptor::mapped_at_creation\n    pub(crate) fn new(mapped_range: Option<Range<BufferAddress>>) -> Self {\n        Self {\n            mapped_range: mapped_range.unwrap_or(0..0),\n            sub_ranges: Vec::new(),\n        }\n    }\n\n    /// Record that the buffer is no longer mapped.\n    fn reset(&mut self) {\n        self.mapped_range = 0..0;\n\n        assert!(\n            self.sub_ranges.is_empty(),\n            \"You cannot unmap a buffer that still has accessible mapped views\"\n        );\n    }\n\n    /// Record that the `size` bytes of the buffer at `offset` are now viewed.\n    ///\n    /// # Panics\n    ///\n    /// This panics if the given range is invalid.\n    #[track_caller]\n    fn validate_and_add(&mut self, new_sub: Subrange) {\n        if self.mapped_range.is_empty() {\n            panic!(\"tried to call get_mapped_range(_mut) on an unmapped buffer\");\n        }\n        if !range_contains(&self.mapped_range, &new_sub.index) {\n            panic!(\n                \"tried to call get_mapped_range(_mut) on a range that is not entirely mapped. \\\n                 Attempted to get range {}, but the mapped range is {}..{}\",\n                new_sub, self.mapped_range.start, self.mapped_range.end\n            );\n        }\n\n        // This check is essential for avoiding undefined behavior: it is the\n        // only thing that ensures that `&mut` references to the buffer's\n        // contents don't alias anything else.\n        for sub in self.sub_ranges.iter() {\n            if range_overlaps(&sub.index, &new_sub.index)\n                && !sub.kind.allowed_concurrently_with(new_sub.kind)\n            {\n                panic!(\n                    \"tried to call get_mapped_range(_mut) on a range that has already \\\n                     been mapped and would break Rust memory aliasing rules. Attempted \\\n                     to get range {}, and the conflicting range is {}\",\n                    new_sub, sub\n                );\n            }\n        }\n        self.sub_ranges.push(new_sub);\n    }\n\n    /// Record that the `size` bytes of the buffer at `offset` are no longer viewed.\n    ///\n    /// # Panics\n    ///\n    /// This panics if the given range does not exactly match one previously\n    /// passed to [`MapContext::validate_and_add`].\n    pub(crate) fn remove(&mut self, offset: BufferAddress, size: BufferSize) {\n        let end = offset + size.get();\n\n        let index = self\n            .sub_ranges\n            .iter()\n            .position(|r| r.index == (offset..end))\n            .expect(\"unable to remove range from map context\");\n        self.sub_ranges.swap_remove(index);\n    }\n}\n\n/// Describes a [`Buffer`].\n///\n/// For use with [`Device::create_buffer`].\n///\n/// Corresponds to [WebGPU `GPUBufferDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpubufferdescriptor).\npub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(BufferDescriptor<'_>: Send, Sync);\n\n/// Error occurred when trying to async map a buffer.\n#[derive(Clone, PartialEq, Eq, Debug)]\npub struct BufferAsyncError;\nstatic_assertions::assert_impl_all!(BufferAsyncError: Send, Sync);\n\nimpl fmt::Display for BufferAsyncError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"Error occurred when trying to async map a buffer\")\n    }\n}\n\nimpl error::Error for BufferAsyncError {}\n\n/// Type of buffer mapping.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum MapMode {\n    /// Map only for reading\n    Read,\n    /// Map only for writing\n    Write,\n}\nstatic_assertions::assert_impl_all!(MapMode: Send, Sync);\n\n/// A read-only view of a mapped buffer's bytes.\n///\n/// To get a `BufferView`, first [map] the buffer, and then\n/// call `buffer.slice(range).get_mapped_range()`.\n///\n/// `BufferView` dereferences to `&[u8]`, so you can use all the usual Rust\n/// slice methods to access the buffer's contents. It also implements\n/// `AsRef<[u8]>`, if that's more convenient.\n///\n/// Before the buffer can be unmapped, all `BufferView`s observing it\n/// must be dropped. Otherwise, the call to [`Buffer::unmap`] will panic.\n///\n/// For example code, see the documentation on [mapping buffers][map].\n///\n/// [map]: Buffer#mapping-buffers\n/// [`map_async`]: BufferSlice::map_async\n#[derive(Debug)]\npub struct BufferView {\n    // `buffer, offset, size` are similar to `BufferSlice`, except that they own the buffer.\n    buffer: Buffer,\n    offset: BufferAddress,\n    size: BufferSize,\n    inner: dispatch::DispatchBufferMappedRange,\n}\n\n/// A write-only view of a mapped buffer's bytes.\n///\n/// To get a `BufferViewMut`, first [map] the buffer, and then\n/// call `buffer.slice(range).get_mapped_range_mut()`.\n///\n/// Because Rust has no write-only reference type\n/// (`&[u8]` is read-only and `&mut [u8]` is read-write),\n/// this type does not dereference to a slice in the way that [`BufferView`] does.\n/// Instead, [`.slice()`][BufferViewMut::slice] returns a special [`WriteOnly`] pointer type,\n/// and there are also a few convenience methods such as [`BufferViewMut::copy_from_slice()`].\n///\n/// Before the buffer can be unmapped, all `BufferViewMut`s observing it\n/// must be dropped. Otherwise, the call to [`Buffer::unmap`] will panic.\n///\n/// For example code, see the documentation on [mapping buffers][map].\n///\n/// [map]: Buffer#mapping-buffers\n#[derive(Debug)]\npub struct BufferViewMut {\n    // `buffer, offset, size` are similar to `BufferSlice`, except that they own the buffer.\n    buffer: Buffer,\n    offset: BufferAddress,\n    size: BufferSize,\n    inner: dispatch::DispatchBufferMappedRange,\n}\n\n// `BufferView` simply dereferences. `BufferViewMut` cannot, because mapped memory may be\n// write-combining memory <https://en.wikipedia.org/wiki/Write_combining>,\n// and not support the expected behavior of atomic accesses.\n// Further context: <https://github.com/gfx-rs/wgpu/issues/8897>\n\nimpl core::ops::Deref for BufferView {\n    type Target = [u8];\n\n    #[inline]\n    fn deref(&self) -> &[u8] {\n        // SAFETY: this is a read mapping\n        unsafe { self.inner.read_slice() }\n    }\n}\n\nimpl AsRef<[u8]> for BufferView {\n    #[inline]\n    fn as_ref(&self) -> &[u8] {\n        self\n    }\n}\n\nimpl Drop for BufferView {\n    fn drop(&mut self) {\n        self.buffer\n            .map_context\n            .lock()\n            .remove(self.offset, self.size);\n    }\n}\n\nimpl Drop for BufferViewMut {\n    fn drop(&mut self) {\n        self.buffer\n            .map_context\n            .lock()\n            .remove(self.offset, self.size);\n    }\n}\n\n#[cfg(webgpu)]\nimpl BufferView {\n    /// Provides the same data as dereferencing the view, but as a `Uint8Array` in js.\n    /// This can be MUCH faster than dereferencing the view which copies the data into\n    /// the Rust / wasm heap.\n    pub fn as_uint8array(&self) -> &js_sys::Uint8Array {\n        self.inner.as_uint8array()\n    }\n}\n\n/// These methods are equivalent to the methods of the same names on [`WriteOnly`].\nimpl BufferViewMut {\n    /// Returns the length of this view; the number of bytes to be written.\n    pub fn len(&self) -> usize {\n        // cannot fail because we can't actually map more than isize::MAX bytes\n        usize::try_from(self.size.get()).unwrap()\n    }\n\n    /// Returns `true` if the view has a length of 0.\n    ///\n    /// Note that this is currently impossible.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns a [`WriteOnly`] reference to a portion of this.\n    ///\n    /// `.slice(..)` can be used to access the whole data.\n    pub fn slice<'a, S: RangeBounds<usize>>(&'a mut self, bounds: S) -> WriteOnly<'a, [u8]> {\n        // SAFETY: this is a write mapping\n        unsafe { self.inner.write_slice() }.into_slice(bounds)\n    }\n\n    /// Copies all elements from src into `self`.\n    ///\n    /// The length of `src` must be the same as `self`.\n    ///\n    /// This method is equivalent to\n    /// [`self.slice(..).copy_from_slice(src)`][WriteOnly::copy_from_slice].\n    pub fn copy_from_slice(&mut self, src: &[u8]) {\n        self.slice(..).copy_from_slice(src)\n    }\n}\n\n#[track_caller]\nfn check_buffer_bounds(\n    buffer_size: BufferAddress,\n    slice_offset: BufferAddress,\n    slice_size: BufferSize,\n) {\n    // A slice of length 0 is invalid, so the offset must not be equal to or greater than the buffer size.\n    if slice_offset >= buffer_size {\n        panic!(\n            \"slice offset {} is out of range for buffer of size {}\",\n            slice_offset, buffer_size\n        );\n    }\n\n    // Detect integer overflow.\n    let end = slice_offset.checked_add(slice_size.get());\n    if end.is_none_or(|end| end > buffer_size) {\n        panic!(\n            \"slice offset {} size {} is out of range for buffer of size {}\",\n            slice_offset, slice_size, buffer_size\n        );\n    }\n}\n\n#[track_caller]\npub(crate) fn range_to_offset_size<S: RangeBounds<BufferAddress>>(\n    bounds: S,\n    whole_size: BufferAddress,\n) -> (BufferAddress, BufferSize) {\n    let offset = match bounds.start_bound() {\n        Bound::Included(&bound) => bound,\n        Bound::Excluded(&bound) => bound + 1,\n        Bound::Unbounded => 0,\n    };\n    let size = BufferSize::new(match bounds.end_bound() {\n        Bound::Included(&bound) => bound + 1 - offset,\n        Bound::Excluded(&bound) => bound - offset,\n        Bound::Unbounded => whole_size - offset,\n    })\n    .expect(\"buffer slices can not be empty\");\n\n    (offset, size)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{\n        check_buffer_bounds, range_overlaps, range_to_offset_size, BufferAddress, BufferSize,\n    };\n\n    fn bs(value: BufferAddress) -> BufferSize {\n        BufferSize::new(value).unwrap()\n    }\n\n    #[test]\n    fn range_to_offset_size_works() {\n        let whole = 100;\n\n        assert_eq!(range_to_offset_size(0..2, whole), (0, bs(2)));\n        assert_eq!(range_to_offset_size(2..5, whole), (2, bs(3)));\n        assert_eq!(range_to_offset_size(.., whole), (0, bs(whole)));\n        assert_eq!(range_to_offset_size(21.., whole), (21, bs(whole - 21)));\n        assert_eq!(range_to_offset_size(0.., whole), (0, bs(whole)));\n        assert_eq!(range_to_offset_size(..21, whole), (0, bs(21)));\n    }\n\n    #[test]\n    #[should_panic = \"buffer slices can not be empty\"]\n    fn range_to_offset_size_panics_for_empty_range() {\n        range_to_offset_size(123..123, 200);\n    }\n\n    #[test]\n    #[should_panic = \"buffer slices can not be empty\"]\n    fn range_to_offset_size_panics_for_unbounded_empty_range() {\n        range_to_offset_size(..0, 100);\n    }\n\n    #[test]\n    fn check_buffer_bounds_works_for_end_in_range() {\n        check_buffer_bounds(200, 100, bs(50));\n        check_buffer_bounds(200, 100, bs(100));\n        check_buffer_bounds(u64::MAX, u64::MAX - 100, bs(100));\n        check_buffer_bounds(u64::MAX, 0, bs(u64::MAX));\n        check_buffer_bounds(u64::MAX, 1, bs(u64::MAX - 1));\n    }\n\n    #[test]\n    #[should_panic]\n    fn check_buffer_bounds_panics_for_end_over_size() {\n        check_buffer_bounds(200, 100, bs(101));\n    }\n\n    #[test]\n    #[should_panic]\n    fn check_buffer_bounds_panics_for_end_wraparound() {\n        check_buffer_bounds(u64::MAX, 1, bs(u64::MAX));\n    }\n\n    #[test]\n    fn range_overlapping() {\n        // First range to the left\n        assert_eq!(range_overlaps(&(0..1), &(1..3)), false);\n        // First range overlaps left edge\n        assert_eq!(range_overlaps(&(0..2), &(1..3)), true);\n        // First range completely inside second\n        assert_eq!(range_overlaps(&(1..2), &(0..3)), true);\n        // First range completely surrounds second\n        assert_eq!(range_overlaps(&(0..3), &(1..2)), true);\n        // First range overlaps right edge\n        assert_eq!(range_overlaps(&(1..3), &(0..2)), true);\n        // First range entirely to the right\n        assert_eq!(range_overlaps(&(2..3), &(0..2)), false);\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/command_buffer.rs",
    "content": "use crate::{\n    api::{impl_deferred_command_buffer_actions, SharedDeferredCommandBufferActions},\n    *,\n};\n\n/// Handle to a command buffer on the GPU.\n///\n/// A `CommandBuffer` represents a complete sequence of commands that may be submitted to a command\n/// queue with [`Queue::submit`]. A `CommandBuffer` is obtained by recording a series of commands to\n/// a [`CommandEncoder`] and then calling [`CommandEncoder::finish`].\n///\n/// Corresponds to [WebGPU `GPUCommandBuffer`](https://gpuweb.github.io/gpuweb/#command-buffer).\n#[derive(Debug)]\npub struct CommandBuffer {\n    pub(crate) buffer: dispatch::DispatchCommandBuffer,\n    /// Deferred actions recorded at encode time, to run at Queue::submit.\n    pub(crate) actions: SharedDeferredCommandBufferActions,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(CommandBuffer: Send, Sync);\n\nimpl CommandBuffer {\n    #[cfg(custom)]\n    /// Returns custom implementation of CommandBuffer (if custom backend and is internally T)\n    pub fn as_custom<T: custom::CommandBufferInterface>(&self) -> Option<&T> {\n        self.buffer.as_custom()\n    }\n\n    // Expose map_buffer_on_submit/on_submitted_work_done on CommandBuffer as well,\n    // so callers can schedule after finishing encoding.\n    impl_deferred_command_buffer_actions!();\n}\n"
  },
  {
    "path": "wgpu/src/api/command_buffer_actions.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\nuse core::num::NonZeroU64;\n\nuse crate::{util::Mutex, *};\n\n/// A deferred buffer mapping request captured during encoding (or a pass)\n/// and executed later when the command buffer is submitted.\npub(crate) struct DeferredBufferMapping {\n    pub buffer: api::Buffer,\n    pub mode: MapMode,\n    pub offset: u64,\n    pub size: NonZeroU64,\n    pub callback: dispatch::BufferMapCallback,\n}\n\npub(super) type SharedDeferredCommandBufferActions = Arc<Mutex<DeferredCommandBufferActions>>;\n\n/// Set of actions to take when the command buffer is submitted.\n#[derive(Default)]\npub(crate) struct DeferredCommandBufferActions {\n    pub buffer_mappings: Vec<DeferredBufferMapping>,\n    pub on_submitted_work_done_callbacks: Vec<dispatch::BoxSubmittedWorkDoneCallback>,\n}\n\nimpl DeferredCommandBufferActions {\n    pub fn append(&mut self, other: &mut Self) {\n        self.buffer_mappings.append(&mut other.buffer_mappings);\n        self.on_submitted_work_done_callbacks\n            .append(&mut other.on_submitted_work_done_callbacks);\n    }\n\n    pub fn execute(self, queue: &dispatch::DispatchQueue) {\n        for mapping in self.buffer_mappings {\n            mapping.buffer.map_async(\n                mapping.mode,\n                mapping.offset..mapping.offset + mapping.size.get(),\n                mapping.callback,\n            );\n        }\n        for callback in self.on_submitted_work_done_callbacks {\n            queue.on_submitted_work_done(callback);\n        }\n    }\n}\n\nimpl core::fmt::Debug for DeferredCommandBufferActions {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"DeferredCommandBufferActions\")\n            .field(\"buffer_mappings.len()\", &self.buffer_mappings.len())\n            .field(\n                \"on_submitted_work_done_callbacks.len()\",\n                &self.on_submitted_work_done_callbacks.len(),\n            )\n            .finish()\n    }\n}\n\n// We can't just implement this on CommandEncoders as by default passes make it so that\n// you can't call any commands on the encoder while this is happening. As such, we need\n// to implement these methods on the passes too. Use a macro to avoid massive code duplication\nmacro_rules! impl_deferred_command_buffer_actions {\n    () => {\n        /// On submission, maps the buffer to host (CPU) memory, making it available\n        /// for reading or writing via [`get_mapped_range()`](Buffer::get_mapped_range).\n        /// The buffer becomes accessible once the `callback` is invoked with [`Ok`].\n        ///\n        /// Use this when you need to submit work that uses the buffer before mapping it.\n        /// Because that submission must happen before calling `map_async`, this method\n        /// schedules the mapping for after submission, avoiding extra calls to\n        /// [`Buffer::map_async()`] or [`BufferSlice::map_async()`] and letting you start\n        /// the mapping from a more convenient place.\n        ///\n        /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],\n        /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated\n        /// into an event loop or run on a separate thread.\n        ///\n        /// The callback runs on the thread that first calls one of the above functions\n        /// after the GPU work completes. There are no restrictions on the code you can run\n        /// in the callback; however, on native the polling call will not return until the\n        /// callback finishes, so keep callbacks short (set flags, send messages, etc.).\n        ///\n        /// While a buffer is mapped, it cannot be used by other commands; at any time,\n        /// either the GPU or the CPU has exclusive access to the buffer’s contents.\n        ///\n        /// # Panics\n        ///\n        /// - If `bounds` is outside the bounds of `buffer`.\n        /// - If `bounds` has a length less than 1.\n        ///\n        /// # Panics During Submit\n        ///\n        /// - If the buffer is already mapped.\n        /// - If the buffer’s [`BufferUsages`] do not allow the requested [`MapMode`].\n        /// - If `bounds` is outside of the bounds of `buffer`.\n        /// - If `bounds` does not start at a multiple of [`MAP_ALIGNMENT`].\n        /// - If `bounds` has a length that is not a multiple of 4 greater than 0.\n        ///\n        /// [q::s]: Queue::submit\n        /// [i::p_a]: Instance::poll_all\n        /// [d::p]: Device::poll\n        /// [CEmbos]: CommandEncoder::map_buffer_on_submit\n        /// [CBmbos]: CommandBuffer::map_buffer_on_submit\n        /// [RPmbos]: RenderPass::map_buffer_on_submit\n        /// [CPmbos]: ComputePass::map_buffer_on_submit\n        pub fn map_buffer_on_submit<S: core::ops::RangeBounds<BufferAddress>>(\n            &self,\n            buffer: &api::Buffer,\n            mode: MapMode,\n            bounds: S,\n            callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static,\n        ) {\n            let (offset, size) = range_to_offset_size(bounds, buffer.size);\n            self.actions.lock().buffer_mappings.push(\n                crate::api::command_buffer_actions::DeferredBufferMapping {\n                    buffer: buffer.clone(),\n                    mode,\n                    offset,\n                    size,\n                    callback: alloc::boxed::Box::new(callback),\n                },\n            );\n        }\n\n        /// Registers a callback that is invoked when this command buffer’s work finishes\n        /// executing on the GPU. When this callback runs, all mapped-buffer callbacks\n        /// registered for the same submission are guaranteed to have been called.\n        ///\n        /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],\n        /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated\n        /// into an event loop or run on a separate thread.\n        ///\n        /// The callback runs on the thread that first calls one of the above functions\n        /// after the GPU work completes. There are no restrictions on the code you can run\n        /// in the callback; however, on native the polling call will not return until the\n        /// callback finishes, so keep callbacks short (set flags, send messages, etc.).\n        ///\n        /// [q::s]: Queue::submit\n        /// [i::p_a]: Instance::poll_all\n        /// [d::p]: Device::poll\n        pub fn on_submitted_work_done(&self, callback: impl FnOnce() + Send + 'static) {\n            self.actions\n                .lock()\n                .on_submitted_work_done_callbacks\n                .push(alloc::boxed::Box::new(callback));\n        }\n    };\n}\n\npub(crate) use impl_deferred_command_buffer_actions;\n"
  },
  {
    "path": "wgpu/src/api/command_encoder.rs",
    "content": "use alloc::sync::Arc;\nuse core::ops::Range;\n\nuse crate::{\n    api::{\n        blas::BlasBuildEntry, impl_deferred_command_buffer_actions, tlas::Tlas,\n        SharedDeferredCommandBufferActions,\n    },\n    *,\n};\n\n/// Encodes a series of GPU operations.\n///\n/// A command encoder can record [`RenderPass`]es, [`ComputePass`]es,\n/// and transfer operations between driver-managed resources like [`Buffer`]s and [`Texture`]s.\n///\n/// When finished recording, call [`CommandEncoder::finish`] to obtain a [`CommandBuffer`] which may\n/// be submitted for execution.\n///\n/// Corresponds to [WebGPU `GPUCommandEncoder`](https://gpuweb.github.io/gpuweb/#command-encoder).\n#[derive(Debug)]\npub struct CommandEncoder {\n    pub(crate) inner: dispatch::DispatchCommandEncoder,\n    pub(crate) actions: SharedDeferredCommandBufferActions,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(CommandEncoder: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(CommandEncoder => .inner);\n\n/// Describes a [`CommandEncoder`].\n///\n/// For use with [`Device::create_command_encoder`].\n///\n/// Corresponds to [WebGPU `GPUCommandEncoderDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpucommandencoderdescriptor).\npub type CommandEncoderDescriptor<'a> = wgt::CommandEncoderDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(CommandEncoderDescriptor<'_>: Send, Sync);\n\npub use wgt::TexelCopyBufferInfo as TexelCopyBufferInfoBase;\n/// View of a buffer which can be used to copy to/from a texture.\n///\n/// Corresponds to [WebGPU `GPUTexelCopyBufferInfo`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer).\npub type TexelCopyBufferInfo<'a> = TexelCopyBufferInfoBase<&'a Buffer>;\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(TexelCopyBufferInfo<'_>: Send, Sync);\n\npub use wgt::TexelCopyTextureInfo as TexelCopyTextureInfoBase;\n/// View of a texture which can be used to copy to/from a buffer/texture.\n///\n/// Corresponds to [WebGPU `GPUTexelCopyTextureInfo`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture).\npub type TexelCopyTextureInfo<'a> = TexelCopyTextureInfoBase<&'a Texture>;\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(TexelCopyTextureInfo<'_>: Send, Sync);\n\nimpl CommandEncoder {\n    /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution.\n    pub fn finish(self) -> CommandBuffer {\n        let Self { mut inner, actions } = self;\n        let buffer = inner.finish();\n        CommandBuffer { buffer, actions }\n    }\n\n    /// Begins recording of a render pass.\n    ///\n    /// This function returns a [`RenderPass`] object which records a single render pass.\n    ///\n    /// As long as the returned  [`RenderPass`] has not ended,\n    /// any mutating operation on this command encoder causes an error and invalidates it.\n    /// Note that the `'encoder` lifetime relationship protects against this,\n    /// but it is possible to opt out of it by calling [`RenderPass::forget_lifetime`].\n    /// This can be useful for runtime handling of the encoder->pass\n    /// dependency e.g. when pass and encoder are stored in the same data structure.\n    pub fn begin_render_pass<'encoder>(\n        &'encoder mut self,\n        desc: &RenderPassDescriptor<'_>,\n    ) -> RenderPass<'encoder> {\n        let rpass = self.inner.begin_render_pass(desc);\n        RenderPass {\n            inner: rpass,\n            actions: Arc::clone(&self.actions),\n            _encoder_guard: api::PhantomDrop::default(),\n        }\n    }\n\n    /// Begins recording of a compute pass.\n    ///\n    /// This function returns a [`ComputePass`] object which records a single compute pass.\n    ///\n    /// As long as the returned  [`ComputePass`] has not ended,\n    /// any mutating operation on this command encoder causes an error and invalidates it.\n    /// Note that the `'encoder` lifetime relationship protects against this,\n    /// but it is possible to opt out of it by calling [`ComputePass::forget_lifetime`].\n    /// This can be useful for runtime handling of the encoder->pass\n    /// dependency e.g. when pass and encoder are stored in the same data structure.\n    pub fn begin_compute_pass<'encoder>(\n        &'encoder mut self,\n        desc: &ComputePassDescriptor<'_>,\n    ) -> ComputePass<'encoder> {\n        let cpass = self.inner.begin_compute_pass(desc);\n        ComputePass {\n            inner: cpass,\n            actions: Arc::clone(&self.actions),\n            _encoder_guard: api::PhantomDrop::default(),\n        }\n    }\n\n    /// Copy data from one buffer to another.\n    ///\n    /// # Panics\n    ///\n    /// - Buffer offsets or copy size not a multiple of [`COPY_BUFFER_ALIGNMENT`].\n    /// - Copy would overrun buffer.\n    /// - Copy within the same buffer.\n    pub fn copy_buffer_to_buffer(\n        &mut self,\n        source: &Buffer,\n        source_offset: BufferAddress,\n        destination: &Buffer,\n        destination_offset: BufferAddress,\n        copy_size: impl Into<Option<BufferAddress>>,\n    ) {\n        self.inner.copy_buffer_to_buffer(\n            &source.inner,\n            source_offset,\n            &destination.inner,\n            destination_offset,\n            copy_size.into(),\n        );\n    }\n\n    /// Copy data from a buffer to a texture.\n    pub fn copy_buffer_to_texture(\n        &mut self,\n        source: TexelCopyBufferInfo<'_>,\n        destination: TexelCopyTextureInfo<'_>,\n        copy_size: Extent3d,\n    ) {\n        self.inner\n            .copy_buffer_to_texture(source, destination, copy_size);\n    }\n\n    /// Copy data from a texture to a buffer.\n    pub fn copy_texture_to_buffer(\n        &mut self,\n        source: TexelCopyTextureInfo<'_>,\n        destination: TexelCopyBufferInfo<'_>,\n        copy_size: Extent3d,\n    ) {\n        self.inner\n            .copy_texture_to_buffer(source, destination, copy_size);\n    }\n\n    /// Copy data from one texture to another.\n    ///\n    /// # Panics\n    ///\n    /// - Textures are not the same type\n    /// - If a depth texture, or a multisampled texture, the entire texture must be copied\n    /// - Copy would overrun either texture\n    pub fn copy_texture_to_texture(\n        &mut self,\n        source: TexelCopyTextureInfo<'_>,\n        destination: TexelCopyTextureInfo<'_>,\n        copy_size: Extent3d,\n    ) {\n        self.inner\n            .copy_texture_to_texture(source, destination, copy_size);\n    }\n\n    /// Clears texture to zero.\n    ///\n    /// Note that unlike with clear_buffer, `COPY_DST` usage is not required.\n    ///\n    /// # Implementation notes\n    ///\n    /// - implemented either via buffer copies and render/depth target clear, path depends on texture usages\n    /// - behaves like texture zero init, but is performed immediately (clearing is *not* delayed via marking it as uninitialized)\n    ///\n    /// # Panics\n    ///\n    /// - `CLEAR_TEXTURE` extension not enabled\n    /// - Range is out of bounds\n    pub fn clear_texture(&mut self, texture: &Texture, subresource_range: &ImageSubresourceRange) {\n        self.inner.clear_texture(&texture.inner, subresource_range);\n    }\n\n    /// Clears buffer to zero.\n    ///\n    /// # Panics\n    ///\n    /// - Buffer does not have `COPY_DST` usage.\n    /// - Range is out of bounds\n    pub fn clear_buffer(\n        &mut self,\n        buffer: &Buffer,\n        offset: BufferAddress,\n        size: Option<BufferAddress>,\n    ) {\n        self.inner.clear_buffer(&buffer.inner, offset, size);\n    }\n\n    /// Inserts debug marker.\n    pub fn insert_debug_marker(&mut self, label: &str) {\n        self.inner.insert_debug_marker(label);\n    }\n\n    /// Start record commands and group it into debug marker group.\n    pub fn push_debug_group(&mut self, label: &str) {\n        self.inner.push_debug_group(label);\n    }\n\n    /// Stops command recording and creates debug group.\n    pub fn pop_debug_group(&mut self) {\n        self.inner.pop_debug_group();\n    }\n\n    /// Copies query results stored in `query_set` into `destination` so that they can be read\n    /// by compute shaders or buffer operations.\n    ///\n    /// * `query_range` is the range of query result indices to copy from `query_set`.\n    ///   Occlusion and timestamp queries occupy 1 result index each;\n    ///   for pipeline statistics queries, see [`PipelineStatisticsTypes`].\n    /// * `destination_offset` is the offset within `destination` to start writing at.\n    ///   It must be a multiple of [`QUERY_RESOLVE_BUFFER_ALIGNMENT`].\n    ///\n    /// The length of the data written to `destination` will be 8 bytes ([`QUERY_SIZE`])\n    /// times the number of elements in `query_range`.\n    ///\n    /// For further information about using queries, see [`QuerySet`].\n    pub fn resolve_query_set(\n        &mut self,\n        query_set: &QuerySet,\n        query_range: Range<u32>,\n        destination: &Buffer,\n        destination_offset: BufferAddress,\n    ) {\n        self.inner.resolve_query_set(\n            &query_set.inner,\n            query_range.start,\n            query_range.end - query_range.start,\n            &destination.inner,\n            destination_offset,\n        );\n    }\n\n    impl_deferred_command_buffer_actions!();\n\n    /// Get the [`wgpu_hal`] command encoder from this `CommandEncoder`.\n    ///\n    /// The returned command encoder will be ready to record onto.\n    ///\n    /// # Errors\n    ///\n    /// This method will pass in [`None`] if:\n    /// - The encoder is not from the backend specified by `A`.\n    /// - The encoder is from the `webgpu` or `custom` backend.\n    ///\n    /// # Types\n    ///\n    /// The callback argument depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"CommandEncoder\")]\n    #[doc = crate::macros::hal_type_metal!(\"CommandEncoder\")]\n    #[doc = crate::macros::hal_type_dx12!(\"CommandEncoder\")]\n    #[doc = crate::macros::hal_type_gles!(\"CommandEncoder\")]\n    ///\n    /// # Safety\n    ///\n    /// - The raw handle obtained from the `A::CommandEncoder` must not be manually destroyed.\n    /// - You must not end the command buffer; wgpu will do it when you call finish.\n    /// - The wgpu command encoder must not be interacted with in any way while recording is\n    ///   happening to the wgpu_hal or backend command encoder.\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal_mut<A: hal::Api, F: FnOnce(Option<&mut A::CommandEncoder>) -> R, R>(\n        &mut self,\n        hal_command_encoder_callback: F,\n    ) -> R {\n        if let Some(encoder) = self.inner.as_core_mut_opt() {\n            unsafe {\n                encoder\n                    .context\n                    .command_encoder_as_hal_mut::<A, F, R>(encoder, hal_command_encoder_callback)\n            }\n        } else {\n            hal_command_encoder_callback(None)\n        }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of CommandEncoder (if custom backend and is internally T)\n    pub fn as_custom<T: custom::CommandEncoderInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] must be enabled on the device in order to call these functions.\nimpl CommandEncoder {\n    /// Issue a timestamp command at this point in the queue.\n    /// The timestamp will be written to the specified query set, at the specified index.\n    ///\n    /// Must be multiplied by [`Queue::get_timestamp_period`] to get\n    /// the value in nanoseconds. Absolute values have no meaning,\n    /// but timestamps can be subtracted to get the time it takes\n    /// for a string of operations to complete.\n    ///\n    /// Attention: Since commands within a command recorder may be reordered,\n    /// there is no strict guarantee that timestamps are taken after all commands\n    /// recorded so far and all before all commands recorded after.\n    /// This may depend both on the backend and the driver.\n    pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {\n        self.inner.write_timestamp(&query_set.inner, query_index);\n    }\n}\n\n/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.\nimpl CommandEncoder {\n    /// When encoding the acceleration structure build with the raw Hal encoder\n    /// (obtained from [`CommandEncoder::as_hal_mut`]), this function marks the\n    /// acceleration structures as having been built.\n    ///\n    /// This function must only be used with the raw encoder API. When using the\n    /// wgpu encoding API, acceleration structure build is tracked automatically.\n    ///\n    /// # Panics\n    ///\n    /// - If the encoder is being used with the wgpu encoding API.\n    ///\n    /// # Safety\n    ///\n    /// - All acceleration structures must have been build in this command encoder.\n    /// - All BLASes inputted must have been built before all TLASes that were inputted here and\n    ///   which use them.\n    pub unsafe fn mark_acceleration_structures_built<'a>(\n        &self,\n        blas: impl IntoIterator<Item = &'a Blas>,\n        tlas: impl IntoIterator<Item = &'a Tlas>,\n    ) {\n        self.inner\n            .mark_acceleration_structures_built(&mut blas.into_iter(), &mut tlas.into_iter())\n    }\n    /// Build bottom and top level acceleration structures.\n    ///\n    /// Builds the BLASes then the TLASes, but does ***not*** build the BLASes into the TLASes,\n    /// that must be done by setting a TLAS instance in the TLAS package to one that contains the BLAS (and with an appropriate transform)\n    ///\n    /// # Validation\n    ///\n    /// - blas: Iterator of bottom level acceleration structure entries to build.\n    ///   For each entry, the provided size descriptor must be strictly smaller or equal to the descriptor given at BLAS creation, this means:\n    ///   - Less or equal number of geometries\n    ///   - Same kind of geometry (with index buffer or without) (same vertex/index format)\n    ///   - Same flags\n    ///   - Less or equal number of vertices\n    ///   - Less or equal number of indices (if applicable)\n    /// - tlas: iterator of top level acceleration structure packages to build\n    ///   For each entry:\n    ///   - Each BLAS in each TLAS instance must have been being built in the current call or in a previous call to `build_acceleration_structures` or `build_acceleration_structures_unsafe_tlas`\n    ///   - The number of TLAS instances must be less than or equal to the max number of tlas instances when creating (if creating a package with `TlasPackage::new()` this is already satisfied)\n    ///\n    /// If the device the command encoder is created from does not have [Features::EXPERIMENTAL_RAY_QUERY] enabled then a validation error is generated\n    ///\n    /// A bottom level acceleration structure may be build and used as a reference in a top level acceleration structure in the same invocation of this function.\n    ///\n    /// # Bind group usage\n    ///\n    /// When a top level acceleration structure is used in a bind group, some validation takes place:\n    ///    - The top level acceleration structure is valid and has been built.\n    ///    - All the bottom level acceleration structures referenced by the top level acceleration structure are valid and have been built prior,\n    ///      or at same time as the containing top level acceleration structure.\n    ///\n    /// [Features::EXPERIMENTAL_RAY_QUERY]: wgt::Features::EXPERIMENTAL_RAY_QUERY\n    pub fn build_acceleration_structures<'a>(\n        &mut self,\n        blas: impl IntoIterator<Item = &'a BlasBuildEntry<'a>>,\n        tlas: impl IntoIterator<Item = &'a Tlas>,\n    ) {\n        self.inner\n            .build_acceleration_structures(&mut blas.into_iter(), &mut tlas.into_iter());\n    }\n\n    /// Transition resources to an underlying hal resource state.\n    ///\n    /// This is an advanced, native-only API (no-op on web) that has two main use cases:\n    ///\n    /// # Batching Barriers\n    ///\n    /// Wgpu does not have a global view of the frame when recording command buffers. When you submit multiple command buffers in a single queue submission, wgpu may need to record and\n    /// insert new command buffers (holding 1 or more barrier commands) in between the user-supplied command buffers in order to ensure that resources are transitioned to the correct state\n    /// for the start of the next user-supplied command buffer.\n    ///\n    /// Wgpu does not currently attempt to batch multiple of these generated command buffers/barriers together, which may lead to suboptimal barrier placement.\n    ///\n    /// Consider the following scenario, where the user does `queue.submit(&[a, b, c])`:\n    /// * CommandBuffer A: Use resource X as a render pass attachment\n    /// * CommandBuffer B: Use resource Y as a render pass attachment\n    /// * CommandBuffer C: Use resources X and Y in a bind group\n    ///\n    /// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, 1, b, 2, c])`:\n    /// * CommandBuffer 0: Barrier to transition resource X from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET\n    /// * CommandBuffer A: Use resource X as a render pass attachment\n    /// * CommandBuffer 1: Barrier to transition resource Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET\n    /// * CommandBuffer B: Use resource Y as a render pass attachment\n    /// * CommandBuffer 2: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE\n    /// * CommandBuffer C: Use resources X and Y in a bind group\n    ///\n    /// To prevent this, after profiling their app, an advanced user might choose to instead do `queue.submit(&[a, b, c])`:\n    /// * CommandBuffer A:\n    ///     * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET\n    ///     * Use resource X as a render pass attachment\n    /// * CommandBuffer B: Use resource Y as a render pass attachment\n    /// * CommandBuffer C:\n    ///     * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE\n    ///     * Use resources X and Y in a bind group\n    ///\n    /// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, b, 1, c])`:\n    /// * CommandBuffer 0: Barrier to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET\n    /// * CommandBuffer A: Use resource X as a render pass attachment\n    /// * CommandBuffer B: Use resource Y as a render pass attachment\n    /// * CommandBuffer 1: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE\n    /// * CommandBuffer C: Use resources X and Y in a bind group\n    ///\n    /// Which eliminates the extra command buffer and barrier between command buffers A and B.\n    ///\n    /// # Native Interoperability\n    ///\n    /// A user wanting to interoperate with the underlying native graphics APIs (Vulkan, DirectX12, Metal, etc) can use this API to generate barriers between wgpu commands and\n    /// the native API commands, for synchronization and resource state transition purposes.\n    pub fn transition_resources<'a>(\n        &mut self,\n        buffer_transitions: impl Iterator<Item = wgt::BufferTransition<&'a Buffer>>,\n        texture_transitions: impl Iterator<Item = wgt::TextureTransition<&'a Texture>>,\n    ) {\n        self.inner.transition_resources(\n            &mut buffer_transitions.map(|t| wgt::BufferTransition {\n                buffer: &t.buffer.inner,\n                state: t.state,\n            }),\n            &mut texture_transitions.map(|t| wgt::TextureTransition {\n                texture: &t.texture.inner,\n                selector: t.selector,\n                state: t.state,\n            }),\n        );\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/common_pipeline.rs",
    "content": "use crate::*;\n\n#[derive(Clone, Debug)]\n/// Advanced options for use when a pipeline is compiled\n///\n/// This implements `Default`, and for most users can be set to `Default::default()`\npub struct PipelineCompilationOptions<'a> {\n    /// Specifies the values of pipeline-overridable constants in the shader module.\n    ///\n    /// If an `@id` attribute was specified on the declaration,\n    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,\n    /// the key must be the constant's identifier name.\n    ///\n    /// If the given constant is specified more than once, the last value specified is used.\n    ///\n    /// The value may represent any of WGSL's concrete scalar types.\n    pub constants: &'a [(&'a str, f64)],\n    /// Whether workgroup scoped memory will be initialized with zero values for this stage.\n    ///\n    /// This is required by the WebGPU spec, but may have overhead which can be avoided\n    /// for cross-platform applications\n    pub zero_initialize_workgroup_memory: bool,\n}\n\nimpl Default for PipelineCompilationOptions<'_> {\n    fn default() -> Self {\n        Self {\n            constants: Default::default(),\n            zero_initialize_workgroup_memory: true,\n        }\n    }\n}\n\n/// Describes a pipeline cache, which allows reusing compilation work\n/// between program runs.\n///\n/// For use with [`Device::create_pipeline_cache`].\n///\n/// This type is unique to the Rust API of `wgpu`.\n#[derive(Clone, Debug)]\npub struct PipelineCacheDescriptor<'a> {\n    /// Debug label of the pipeline cache. This might show up in some logs from `wgpu`\n    pub label: Label<'a>,\n    /// The data used to initialise the cache initialise\n    ///\n    /// # Safety\n    ///\n    /// This data must have been provided from a previous call to\n    /// [`PipelineCache::get_data`], if not `None`\n    pub data: Option<&'a [u8]>,\n    /// Whether to create a cache without data when the provided data\n    /// is invalid.\n    ///\n    /// Recommended to set to true\n    pub fallback: bool,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(PipelineCacheDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/compute_pass.rs",
    "content": "use crate::{\n    api::{impl_deferred_command_buffer_actions, SharedDeferredCommandBufferActions},\n    *,\n};\n\n/// In-progress recording of a compute pass.\n///\n/// It can be created with [`CommandEncoder::begin_compute_pass`].\n///\n/// Corresponds to [WebGPU `GPUComputePassEncoder`](\n/// https://gpuweb.github.io/gpuweb/#compute-pass-encoder).\n#[derive(Debug)]\npub struct ComputePass<'encoder> {\n    pub(crate) inner: dispatch::DispatchComputePass,\n\n    /// Shared with CommandEncoder to enqueue deferred actions from within a pass.\n    pub(crate) actions: SharedDeferredCommandBufferActions,\n\n    /// This lifetime is used to protect the [`CommandEncoder`] from being used\n    /// while the pass is alive. This needs to be PhantomDrop to prevent the lifetime\n    /// from being shortened.\n    pub(crate) _encoder_guard: crate::api::PhantomDrop<&'encoder ()>,\n}\n\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ComputePass<'_>: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(ComputePass<'_> => .inner);\n\nimpl ComputePass<'_> {\n    /// Drops the lifetime relationship to the parent command encoder, making usage of\n    /// the encoder while this pass is recorded a run-time error instead.\n    ///\n    /// Attention: As long as the compute pass has not been ended, any mutating operation on the parent\n    /// command encoder will cause a run-time error and invalidate it!\n    /// By default, the lifetime constraint prevents this, but it can be useful\n    /// to handle this at run time, such as when storing the pass and encoder in the same\n    /// data structure.\n    ///\n    /// This operation has no effect on pass recording.\n    /// It's a safe operation, since [`CommandEncoder`] is in a locked state as long as the pass is active\n    /// regardless of the lifetime constraint or its absence.\n    pub fn forget_lifetime(self) -> ComputePass<'static> {\n        ComputePass {\n            inner: self.inner,\n            actions: self.actions,\n            _encoder_guard: crate::api::PhantomDrop::default(),\n        }\n    }\n\n    /// Sets the active bind group for a given bind group index. The bind group layout\n    /// in the active pipeline when the `dispatch()` function is called must match the layout of this bind group.\n    ///\n    /// If the bind group have dynamic offsets, provide them in the binding order.\n    /// These offsets have to be aligned to [`Limits::min_uniform_buffer_offset_alignment`]\n    /// or [`Limits::min_storage_buffer_offset_alignment`] appropriately.\n    pub fn set_bind_group<'a, BG>(&mut self, index: u32, bind_group: BG, offsets: &[DynamicOffset])\n    where\n        Option<&'a BindGroup>: From<BG>,\n    {\n        let bg: Option<&BindGroup> = bind_group.into();\n        let bg = bg.map(|bg| &bg.inner);\n        self.inner.set_bind_group(index, bg, offsets);\n    }\n\n    /// Sets the active compute pipeline.\n    pub fn set_pipeline(&mut self, pipeline: &ComputePipeline) {\n        self.inner.set_pipeline(&pipeline.inner);\n    }\n\n    /// Inserts debug marker.\n    pub fn insert_debug_marker(&mut self, label: &str) {\n        self.inner.insert_debug_marker(label);\n    }\n\n    /// Start record commands and group it into debug marker group.\n    pub fn push_debug_group(&mut self, label: &str) {\n        self.inner.push_debug_group(label);\n    }\n\n    /// Stops command recording and creates debug group.\n    pub fn pop_debug_group(&mut self) {\n        self.inner.pop_debug_group();\n    }\n\n    /// Dispatches compute work operations.\n    ///\n    /// `x`, `y` and `z` denote the number of work groups to dispatch in each dimension.\n    pub fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {\n        self.inner.dispatch_workgroups(x, y, z);\n    }\n\n    /// Dispatches compute work operations, based on the contents of the `indirect_buffer`.\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DispatchIndirectArgs`](crate::util::DispatchIndirectArgs).\n    pub fn dispatch_workgroups_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        self.inner\n            .dispatch_workgroups_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    impl_deferred_command_buffer_actions!();\n\n    #[cfg(custom)]\n    /// Returns custom implementation of ComputePass (if custom backend and is internally T)\n    pub fn as_custom<T: custom::ComputePassInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// [`Features::IMMEDIATES`] must be enabled on the device in order to call these functions.\nimpl ComputePass<'_> {\n    /// Set immediate data for subsequent dispatch calls.\n    ///\n    /// Write the bytes in `data` at offset `offset` within immediate data\n    /// storage.  Both `offset` and the length of `data` must be\n    /// multiples of [`crate::IMMEDIATE_DATA_ALIGNMENT`], which is always 4.\n    ///\n    /// For example, if `offset` is `4` and `data` is eight bytes long, this\n    /// call will write `data` to bytes `4..12` of immediate data storage.\n    pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        self.inner.set_immediates(offset, data);\n    }\n}\n\n/// [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`] must be enabled on the device in order to call these functions.\nimpl ComputePass<'_> {\n    /// Issue a timestamp command at this point in the queue. The timestamp will be written to the specified query set, at the specified index.\n    ///\n    /// Must be multiplied by [`Queue::get_timestamp_period`] to get\n    /// the value in nanoseconds. Absolute values have no meaning,\n    /// but timestamps can be subtracted to get the time it takes\n    /// for a string of operations to complete.\n    pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {\n        self.inner.write_timestamp(&query_set.inner, query_index);\n    }\n}\n\n/// [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.\nimpl ComputePass<'_> {\n    /// Start a pipeline statistics query on this compute pass. It can be ended with\n    /// `end_pipeline_statistics_query`. Pipeline statistics queries may not be nested.\n    ///\n    /// The amount of information collected by this query, and the space occupied in the query set,\n    /// is determined by the [`PipelineStatisticsTypes`] the query set was created with.\n    /// `query_index` is the index of the first query result slot that will be written to, and\n    /// `query_set` must have sufficient size to hold all results written starting at that slot.\n    pub fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, query_index: u32) {\n        self.inner\n            .begin_pipeline_statistics_query(&query_set.inner, query_index);\n    }\n\n    /// End the pipeline statistics query on this compute pass. It can be started with\n    /// `begin_pipeline_statistics_query`. Pipeline statistics queries may not be nested.\n    pub fn end_pipeline_statistics_query(&mut self) {\n        self.inner.end_pipeline_statistics_query();\n    }\n}\n\n/// Describes the timestamp writes of a compute pass.\n///\n/// For use with [`ComputePassDescriptor`].\n/// At least one of `beginning_of_pass_write_index` and `end_of_pass_write_index` must be `Some`.\n///\n/// Corresponds to [WebGPU `GPUComputePassTimestampWrites`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpucomputepasstimestampwrites).\n#[derive(Clone, Debug)]\npub struct ComputePassTimestampWrites<'a> {\n    /// The query set to write to.\n    pub query_set: &'a QuerySet,\n    /// The index of the query set at which a start timestamp of this pass is written, if any.\n    pub beginning_of_pass_write_index: Option<u32>,\n    /// The index of the query set at which an end timestamp of this pass is written, if any.\n    pub end_of_pass_write_index: Option<u32>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ComputePassTimestampWrites<'_>: Send, Sync);\n\n/// Describes the attachments of a compute pass.\n///\n/// For use with [`CommandEncoder::begin_compute_pass`].\n///\n/// Corresponds to [WebGPU `GPUComputePassDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpucomputepassdescriptor).\n#[derive(Clone, Default, Debug)]\npub struct ComputePassDescriptor<'a> {\n    /// Debug label of the compute pass. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Defines which timestamp values will be written for this pass, and where to write them to.\n    ///\n    /// Requires [`Features::TIMESTAMP_QUERY`] to be enabled.\n    pub timestamp_writes: Option<ComputePassTimestampWrites<'a>>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ComputePassDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/compute_pipeline.rs",
    "content": "use crate::*;\n\n/// Handle to a compute pipeline.\n///\n/// A `ComputePipeline` object represents a compute pipeline and its single shader stage.\n/// It can be created with [`Device::create_compute_pipeline`].\n///\n/// Corresponds to [WebGPU `GPUComputePipeline`](https://gpuweb.github.io/gpuweb/#compute-pipeline).\n#[derive(Debug, Clone)]\npub struct ComputePipeline {\n    pub(crate) inner: dispatch::DispatchComputePipeline,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ComputePipeline: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(ComputePipeline => .inner);\n\nimpl ComputePipeline {\n    /// Get an object representing the bind group layout at a given index.\n    ///\n    /// If this pipeline was created with a [default layout][ComputePipelineDescriptor::layout],\n    /// then bind groups created with the returned `BindGroupLayout` can only be used with this\n    /// pipeline.\n    ///\n    /// This method will raise a validation error if there is no bind group layout at `index`.\n    pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout {\n        let bind_group = self.inner.get_bind_group_layout(index);\n        BindGroupLayout { inner: bind_group }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of ComputePipeline (if custom backend and is internally T)\n    pub fn as_custom<T: custom::ComputePipelineInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a compute pipeline.\n///\n/// For use with [`Device::create_compute_pipeline`].\n///\n/// Corresponds to [WebGPU `GPUComputePipelineDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpucomputepipelinedescriptor).\n#[derive(Clone, Debug)]\npub struct ComputePipelineDescriptor<'a> {\n    /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    ///\n    /// If this is set, then [`Device::create_compute_pipeline`] will raise a validation error if\n    /// the layout doesn't match what the shader module(s) expect.\n    ///\n    /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`]\n    /// pipelines guarantees that you don't have to rebind any resources when switching between\n    /// those pipelines.\n    ///\n    /// ## Default pipeline layout\n    ///\n    /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead.\n    /// The default layout is deduced from the shader modules.\n    ///\n    /// You can use [`ComputePipeline::get_bind_group_layout`] to create bind groups for use with\n    /// the default layout. However, these bind groups cannot be used with any other pipelines. This\n    /// is convenient for simple pipelines, but using an explicit layout is recommended in most\n    /// cases.\n    ///\n    /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout\n    pub layout: Option<&'a PipelineLayout>,\n    /// The compiled shader module for this stage.\n    pub module: &'a ShaderModule,\n    /// The name of the entry point in the compiled shader to use.\n    ///\n    /// If [`Some`], there must be a compute shader entry point with this name in `module`.\n    /// Otherwise, expect exactly one compute shader entry point in `module`, which will be\n    /// selected.\n    // NOTE: keep phrasing in sync. with `FragmentState::entry_point`\n    // NOTE: keep phrasing in sync. with `VertexState::entry_point`\n    pub entry_point: Option<&'a str>,\n    /// Advanced options for when this pipeline is compiled\n    ///\n    /// This implements `Default`, and for most users can be set to `Default::default()`\n    pub compilation_options: PipelineCompilationOptions<'a>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<&'a PipelineCache>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ComputePipelineDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/device.rs",
    "content": "use alloc::{boxed::Box, string::String, sync::Arc, vec};\n#[cfg(wgpu_core)]\nuse core::ops::Deref;\nuse core::{error, fmt, future::Future, marker::PhantomData};\n\nuse crate::api::blas::{Blas, BlasGeometrySizeDescriptors, CreateBlasDescriptor};\nuse crate::api::tlas::{CreateTlasDescriptor, Tlas};\nuse crate::util::Mutex;\nuse crate::*;\n\n/// Open connection to a graphics and/or compute device.\n///\n/// Responsible for the creation of most rendering and compute resources.\n/// These are then used in commands, which are submitted to a [`Queue`].\n///\n/// A device may be requested from an adapter with [`Adapter::request_device`].\n///\n/// Corresponds to [WebGPU `GPUDevice`](https://gpuweb.github.io/gpuweb/#gpu-device).\n#[derive(Debug, Clone)]\npub struct Device {\n    pub(crate) inner: dispatch::DispatchDevice,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Device: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Device => .inner);\n\n/// Describes a [`Device`].\n///\n/// For use with [`Adapter::request_device`].\n///\n/// Corresponds to [WebGPU `GPUDeviceDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpudevicedescriptor).\npub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(DeviceDescriptor<'_>: Send, Sync);\n\nimpl Device {\n    #[cfg(custom)]\n    /// Returns custom implementation of Device (if custom backend and is internally T)\n    pub fn as_custom<T: custom::DeviceInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n\n    #[cfg(custom)]\n    /// Creates Device from custom implementation\n    pub fn from_custom<T: custom::DeviceInterface>(device: T) -> Self {\n        Self {\n            inner: dispatch::DispatchDevice::custom(device),\n        }\n    }\n\n    /// Constructs a stub device for testing using [`Backend::Noop`].\n    ///\n    /// This is a convenience function which avoids the configuration, `async`, and fallibility\n    /// aspects of constructing a device through `Instance`.\n    #[cfg(feature = \"noop\")]\n    pub fn noop(desc: &DeviceDescriptor<'_>) -> (Device, Queue) {\n        use core::future::Future as _;\n        use core::pin::pin;\n        use core::task;\n        let ctx = &mut task::Context::from_waker(task::Waker::noop());\n\n        let instance = Instance::new(InstanceDescriptor {\n            backends: Backends::NOOP,\n            backend_options: BackendOptions {\n                noop: NoopBackendOptions { enable: true },\n                ..Default::default()\n            },\n            ..InstanceDescriptor::new_without_display_handle()\n        });\n\n        // Both of these futures are trivial and should complete instantaneously,\n        // so we do not need an executor and can just poll them once.\n        let task::Poll::Ready(Ok(adapter)) =\n            pin!(instance.request_adapter(&RequestAdapterOptions::default())).poll(ctx)\n        else {\n            unreachable!()\n        };\n        let task::Poll::Ready(Ok(device_and_queue)) = pin!(adapter.request_device(desc)).poll(ctx)\n        else {\n            unreachable!()\n        };\n        device_and_queue\n    }\n\n    /// Check for resource cleanups and mapping callbacks. Will block if [`PollType::Wait`] is passed.\n    ///\n    /// Return `true` if the queue is empty, or `false` if there are more queue\n    /// submissions still in flight. (Note that, unless access to the [`Queue`] is\n    /// coordinated somehow, this information could be out of date by the time\n    /// the caller receives it. `Queue`s can be shared between threads, so\n    /// other threads could submit new work at any time.)\n    ///\n    /// When running on WebGPU, this is a no-op. `Device`s are automatically polled.\n    pub fn poll(&self, poll_type: PollType) -> Result<crate::PollStatus, crate::PollError> {\n        self.inner.poll(poll_type.map_index(|s| s.index))\n    }\n\n    /// The [features][Features] which can be used on this device.\n    ///\n    /// This will be equal to the [`required_features`][DeviceDescriptor::required_features]\n    /// specified when creating the device.\n    /// No additional features can be used, even if the underlying adapter can support them.\n    #[must_use]\n    pub fn features(&self) -> Features {\n        self.inner.features()\n    }\n\n    /// The limits which can be used on this device.\n    ///\n    /// This will be equal to the [`required_limits`][DeviceDescriptor::required_limits]\n    /// specified when creating the device.\n    /// No better limits can be used, even if the underlying adapter can support them.\n    #[must_use]\n    pub fn limits(&self) -> Limits {\n        self.inner.limits()\n    }\n\n    /// Get info about the adapter that this device was created from.\n    pub fn adapter_info(&self) -> AdapterInfo {\n        self.inner.adapter_info()\n    }\n\n    /// Creates a shader module.\n    ///\n    /// <div class=\"warning\">\n    // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!\n    // NOTE: Keep this in sync with `wgpu_core::Global::device_create_shader_module`!\n    ///\n    /// This function may consume a lot of stack space. Compiler-enforced limits for parsing\n    /// recursion exist; if shader compilation runs into them, it will return an error gracefully.\n    /// However, on some build profiles and platforms, the default stack size for a thread may be\n    /// exceeded before this limit is reached during parsing. Callers should ensure that there is\n    /// enough stack space for this, particularly if calls to this method are exposed to user\n    /// input.\n    ///\n    /// </div>\n    #[must_use]\n    pub fn create_shader_module(&self, desc: ShaderModuleDescriptor<'_>) -> ShaderModule {\n        let module = self\n            .inner\n            .create_shader_module(desc, wgt::ShaderRuntimeChecks::checked());\n        ShaderModule { inner: module }\n    }\n\n    /// Deprecated: Use [`create_shader_module_trusted`][csmt] instead.\n    ///\n    /// # Safety\n    ///\n    /// See [`create_shader_module_trusted`][csmt].\n    ///\n    /// [csmt]: Self::create_shader_module_trusted\n    #[deprecated(\n        since = \"24.0.0\",\n        note = \"Use `Device::create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())` instead.\"\n    )]\n    #[must_use]\n    pub unsafe fn create_shader_module_unchecked(\n        &self,\n        desc: ShaderModuleDescriptor<'_>,\n    ) -> ShaderModule {\n        unsafe { self.create_shader_module_trusted(desc, crate::ShaderRuntimeChecks::unchecked()) }\n    }\n\n    /// Creates a shader module with flags to dictate runtime checks.\n    ///\n    /// When running on WebGPU, this will merely call [`create_shader_module`][csm].\n    ///\n    /// # Safety\n    ///\n    /// In contrast with [`create_shader_module`][csm] this function\n    /// creates a shader module with user-customizable runtime checks which allows shaders to\n    /// perform operations which can lead to undefined behavior like indexing out of bounds,\n    /// thus it's the caller responsibility to pass a shader which doesn't perform any of this\n    /// operations.\n    ///\n    /// See the documentation for [`ShaderRuntimeChecks`] for more information about specific checks.\n    ///\n    /// [csm]: Self::create_shader_module\n    #[must_use]\n    pub unsafe fn create_shader_module_trusted(\n        &self,\n        desc: ShaderModuleDescriptor<'_>,\n        runtime_checks: crate::ShaderRuntimeChecks,\n    ) -> ShaderModule {\n        let module = self.inner.create_shader_module(desc, runtime_checks);\n        ShaderModule { inner: module }\n    }\n\n    /// Creates a shader module which will bypass wgpu's shader tooling and validation and be used directly by the backend.\n    ///\n    /// # Safety\n    ///\n    /// This function passes data to the backend as-is and can potentially result in a\n    /// driver crash or bogus behaviour. No attempt is made to ensure that data is valid.\n    #[must_use]\n    pub unsafe fn create_shader_module_passthrough(\n        &self,\n        desc: ShaderModuleDescriptorPassthrough<'_>,\n    ) -> ShaderModule {\n        let module = unsafe { self.inner.create_shader_module_passthrough(&desc) };\n        ShaderModule { inner: module }\n    }\n\n    /// Creates an empty [`CommandEncoder`].\n    #[must_use]\n    pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor<'_>) -> CommandEncoder {\n        let encoder = self.inner.create_command_encoder(desc);\n        // Each encoder starts with its own deferred-action store that travels\n        // with the CommandBuffer produced by finish().\n        CommandEncoder {\n            inner: encoder,\n            actions: Default::default(),\n        }\n    }\n\n    /// Creates an empty [`RenderBundleEncoder`].\n    #[must_use]\n    pub fn create_render_bundle_encoder<'a>(\n        &self,\n        desc: &RenderBundleEncoderDescriptor<'_>,\n    ) -> RenderBundleEncoder<'a> {\n        let encoder = self.inner.create_render_bundle_encoder(desc);\n        RenderBundleEncoder {\n            inner: encoder,\n            _p: PhantomData,\n        }\n    }\n\n    /// Creates a new [`BindGroup`].\n    #[must_use]\n    pub fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> BindGroup {\n        let group = self.inner.create_bind_group(desc);\n        BindGroup { inner: group }\n    }\n\n    /// Creates a [`BindGroupLayout`].\n    #[must_use]\n    pub fn create_bind_group_layout(\n        &self,\n        desc: &BindGroupLayoutDescriptor<'_>,\n    ) -> BindGroupLayout {\n        let layout = self.inner.create_bind_group_layout(desc);\n        BindGroupLayout { inner: layout }\n    }\n\n    /// Creates a [`PipelineLayout`].\n    #[must_use]\n    pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor<'_>) -> PipelineLayout {\n        let layout = self.inner.create_pipeline_layout(desc);\n        PipelineLayout { inner: layout }\n    }\n\n    /// Creates a [`RenderPipeline`].\n    #[must_use]\n    pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor<'_>) -> RenderPipeline {\n        let pipeline = self.inner.create_render_pipeline(desc);\n        RenderPipeline { inner: pipeline }\n    }\n\n    /// Creates a mesh shader based [`RenderPipeline`].\n    #[must_use]\n    pub fn create_mesh_pipeline(&self, desc: &MeshPipelineDescriptor<'_>) -> RenderPipeline {\n        let pipeline = self.inner.create_mesh_pipeline(desc);\n        RenderPipeline { inner: pipeline }\n    }\n\n    /// Creates a [`ComputePipeline`].\n    #[must_use]\n    pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor<'_>) -> ComputePipeline {\n        let pipeline = self.inner.create_compute_pipeline(desc);\n        ComputePipeline { inner: pipeline }\n    }\n\n    /// Creates a [`Buffer`].\n    #[must_use]\n    pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {\n        let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));\n\n        let buffer = self.inner.create_buffer(desc);\n\n        Buffer {\n            inner: buffer,\n            map_context: Arc::new(Mutex::new(map_context)),\n            size: desc.size,\n            usage: desc.usage,\n        }\n    }\n\n    /// Creates a new [`Texture`].\n    ///\n    /// `desc` specifies the general format of the texture.\n    #[must_use]\n    pub fn create_texture(&self, desc: &TextureDescriptor<'_>) -> Texture {\n        let texture = self.inner.create_texture(desc);\n\n        Texture {\n            inner: texture,\n            descriptor: TextureDescriptor {\n                label: None,\n                view_formats: &[],\n                ..desc.clone()\n            },\n        }\n    }\n\n    /// Creates a [`Texture`] from a wgpu-hal Texture.\n    ///\n    /// # Types\n    ///\n    /// The type of `A::Texture` depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Texture\")]\n    #[doc = crate::macros::hal_type_metal!(\"Texture\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Texture\")]\n    #[doc = crate::macros::hal_type_gles!(\"Texture\")]\n    ///\n    /// # Safety\n    ///\n    /// - `hal_texture` must be created from this device internal handle\n    /// - `hal_texture` must be created respecting `desc`\n    /// - `hal_texture` must be initialized\n    #[cfg(wgpu_core)]\n    #[must_use]\n    pub unsafe fn create_texture_from_hal<A: hal::Api>(\n        &self,\n        hal_texture: A::Texture,\n        desc: &TextureDescriptor<'_>,\n    ) -> Texture {\n        let texture = unsafe {\n            let core_device = self.inner.as_core();\n            core_device\n                .context\n                .create_texture_from_hal::<A>(hal_texture, core_device, desc)\n        };\n        Texture {\n            inner: texture.into(),\n            descriptor: TextureDescriptor {\n                label: None,\n                view_formats: &[],\n                ..desc.clone()\n            },\n        }\n    }\n\n    /// Creates a new [`ExternalTexture`].\n    #[must_use]\n    pub fn create_external_texture(\n        &self,\n        desc: &ExternalTextureDescriptor<'_>,\n        planes: &[&TextureView],\n    ) -> ExternalTexture {\n        let external_texture = self.inner.create_external_texture(desc, planes);\n\n        ExternalTexture {\n            inner: external_texture,\n        }\n    }\n\n    /// Creates a [`Buffer`] from a wgpu-hal Buffer.\n    ///\n    /// # Types\n    ///\n    /// The type of `A::Buffer` depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_metal!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Buffer\")]\n    #[doc = crate::macros::hal_type_gles!(\"Buffer\")]\n    ///\n    /// # Safety\n    ///\n    /// - `hal_buffer` must be created from this device internal handle\n    /// - `hal_buffer` must be created respecting `desc`\n    /// - `hal_buffer` must be initialized\n    /// - `hal_buffer` must not have zero size\n    #[cfg(wgpu_core)]\n    #[must_use]\n    pub unsafe fn create_buffer_from_hal<A: hal::Api>(\n        &self,\n        hal_buffer: A::Buffer,\n        desc: &BufferDescriptor<'_>,\n    ) -> Buffer {\n        let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));\n\n        let buffer = unsafe {\n            let core_device = self.inner.as_core();\n            core_device\n                .context\n                .create_buffer_from_hal::<A>(hal_buffer, core_device, desc)\n        };\n\n        Buffer {\n            inner: buffer.into(),\n            map_context: Arc::new(Mutex::new(map_context)),\n            size: desc.size,\n            usage: desc.usage,\n        }\n    }\n\n    /// Creates a new [`Sampler`].\n    ///\n    /// `desc` specifies the behavior of the sampler.\n    #[must_use]\n    pub fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> Sampler {\n        let sampler = self.inner.create_sampler(desc);\n        Sampler { inner: sampler }\n    }\n\n    /// Creates a new [`QuerySet`].\n    #[must_use]\n    pub fn create_query_set(&self, desc: &QuerySetDescriptor<'_>) -> QuerySet {\n        let query_set = self.inner.create_query_set(desc);\n        QuerySet { inner: query_set }\n    }\n\n    /// Set a callback which will be called for all errors that are not handled in error scopes.\n    pub fn on_uncaptured_error(&self, handler: Arc<dyn UncapturedErrorHandler>) {\n        self.inner.on_uncaptured_error(handler)\n    }\n\n    /// Push an error scope on this device's thread-local error scope\n    /// stack. All operations on this device, or on resources created\n    /// from this device, will have their errors captured by this scope\n    /// until the scope is popped.\n    ///\n    /// Scopes must be popped in reverse order to their creation. If\n    /// a guard is dropped without being `pop()`ped, the scope will be\n    /// popped, and the captured errors will be dropped.\n    ///\n    /// Multiple error scopes may be active at one time, forming a stack.\n    /// Each error will be reported to the inner-most scope that matches\n    /// its filter.\n    ///\n    /// With the `std` feature enabled, this stack is **thread-local**.\n    /// Without, this is **global** to all threads.\n    ///\n    /// ```rust\n    /// # async move {\n    /// # let device: wgpu::Device = unreachable!();\n    /// let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation);\n    ///\n    /// // ...\n    /// // do work that may produce validation errors\n    /// // ...\n    ///\n    /// // pop the error scope and get a future for the result\n    /// let error_future = error_scope.pop();\n    ///\n    /// // await the future to get the error, if any\n    /// let error = error_future.await;\n    /// # };\n    /// ```\n    pub fn push_error_scope(&self, filter: ErrorFilter) -> ErrorScopeGuard {\n        let index = self.inner.push_error_scope(filter);\n        ErrorScopeGuard {\n            device: self.inner.clone(),\n            index,\n            popped: false,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Starts a capture in the attached graphics debugger.\n    ///\n    /// This behaves differently depending on which graphics debugger is attached:\n    ///\n    /// - Renderdoc: Calls [`StartFrameCapture(device, NULL)`][rd].\n    /// - Xcode: Creates a capture with [`MTLCaptureManager`][xcode].\n    /// - None: No action is taken.\n    ///\n    /// # Safety\n    ///\n    /// - There should not be any other captures currently active.\n    /// - All other safety rules are defined by the graphics debugger, see the\n    ///   documentation for the specific debugger.\n    /// - In general, graphics debuggers can easily cause crashes, so this isn't\n    ///   ever guaranteed to be sound.\n    ///\n    /// # Tips\n    ///\n    /// - Debuggers need to capture both the recording of the commands and the\n    ///   submission of the commands to the GPU. Try to wrap all of your\n    ///   gpu work in a capture.\n    /// - If you encounter issues, try waiting for the GPU to finish all work\n    ///   before stopping the capture.\n    ///\n    /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv417StartFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle\n    /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager\n    #[doc(alias = \"start_renderdoc_capture\")]\n    #[doc(alias = \"start_xcode_capture\")]\n    pub unsafe fn start_graphics_debugger_capture(&self) {\n        unsafe { self.inner.start_graphics_debugger_capture() }\n    }\n\n    /// Stops the current capture in the attached graphics debugger.\n    ///\n    /// This behaves differently depending on which graphics debugger is attached:\n    ///\n    /// - Renderdoc: Calls [`EndFrameCapture(device, NULL)`][rd].\n    /// - Xcode: Stops the capture with [`MTLCaptureManager`][xcode].\n    /// - None: No action is taken.\n    ///\n    /// # Safety\n    ///\n    /// - There should be a capture currently active.\n    /// - All other safety rules are defined by the graphics debugger, see the\n    ///   documentation for the specific debugger.\n    /// - In general, graphics debuggers can easily cause crashes, so this isn't\n    ///   ever guaranteed to be sound.\n    ///\n    /// # Tips\n    ///\n    /// - If you encounter issues, try to submit all work to the GPU, and waiting\n    ///   for that work to finish before stopping the capture.\n    ///\n    /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv415EndFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle\n    /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager\n    #[doc(alias = \"stop_renderdoc_capture\")]\n    #[doc(alias = \"stop_xcode_capture\")]\n    pub unsafe fn stop_graphics_debugger_capture(&self) {\n        unsafe { self.inner.stop_graphics_debugger_capture() }\n    }\n\n    /// Query internal counters from the native backend for debugging purposes.\n    ///\n    /// Some backends may not set all counters, or may not set any counter at all.\n    /// The `counters` cargo feature must be enabled for any counter to be set.\n    ///\n    /// If a counter is not set, its contains its default value (zero).\n    #[must_use]\n    pub fn get_internal_counters(&self) -> wgt::InternalCounters {\n        self.inner.get_internal_counters()\n    }\n\n    /// Generate an GPU memory allocation report if the underlying backend supports it.\n    ///\n    /// Backends that do not support producing these reports return `None`. A backend may\n    /// Support it and still return `None` if it is not using performing sub-allocation,\n    /// for example as a workaround for driver issues.\n    #[must_use]\n    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {\n        self.inner.generate_allocator_report()\n    }\n\n    /// Get the [`wgpu_hal`] device from this `Device`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Device`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Device\")]\n    #[doc = crate::macros::hal_type_metal!(\"Device\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Device\")]\n    #[doc = crate::macros::hal_type_gles!(\"Device\")]\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The device is not from the backend specified by `A`.\n    /// - The device is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Device`]: hal::Api::Device\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &self,\n    ) -> Option<impl Deref<Target = A::Device> + WasmNotSendSync> {\n        let device = self.inner.as_core_opt()?;\n        unsafe { device.context.device_as_hal::<A>(device) }\n    }\n\n    /// Destroy this device.\n    pub fn destroy(&self) {\n        self.inner.destroy()\n    }\n\n    /// Set a DeviceLostCallback on this device.\n    pub fn set_device_lost_callback(\n        &self,\n        callback: impl Fn(DeviceLostReason, String) + Send + 'static,\n    ) {\n        self.inner.set_device_lost_callback(Box::new(callback))\n    }\n\n    /// Create a [`PipelineCache`] with initial data\n    ///\n    /// This can be passed to [`Device::create_compute_pipeline`]\n    /// and [`Device::create_render_pipeline`] to either accelerate these\n    /// or add the cache results from those.\n    ///\n    /// # Safety\n    ///\n    /// If the `data` field of `desc` is set, it must have previously been returned from a call\n    /// to [`PipelineCache::get_data`][^saving]. This `data` will only be used if it came\n    /// from an adapter with the same [`util::pipeline_cache_key`].\n    /// This *is* compatible across wgpu versions, as any data format change will\n    /// be accounted for.\n    ///\n    /// It is *not* supported to bring caches from previous direct uses of backend APIs\n    /// into this method.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error value if:\n    ///  * the [`PIPELINE_CACHE`](wgt::Features::PIPELINE_CACHE) feature is not enabled\n    ///  * this device is invalid; or\n    ///  * the device is out of memory\n    ///\n    /// This method also returns an error value if:\n    ///  * The `fallback` field on `desc` is false; and\n    ///  * the `data` provided would not be used[^data_not_used]\n    ///\n    /// If an error value is used in subsequent calls, default caching will be used.\n    ///\n    /// [^saving]: We do recognise that saving this data to disk means this condition\n    /// is impossible to fully prove. Consider the risks for your own application in this case.\n    ///\n    /// [^data_not_used]: This data may be not used if: the data was produced by a prior\n    /// version of wgpu; or was created for an incompatible adapter, or there was a GPU driver\n    /// update. In some cases, the data might not be used and a real value is returned,\n    /// this is left to the discretion of GPU drivers.\n    #[must_use]\n    pub unsafe fn create_pipeline_cache(\n        &self,\n        desc: &PipelineCacheDescriptor<'_>,\n    ) -> PipelineCache {\n        let cache = unsafe { self.inner.create_pipeline_cache(desc) };\n        PipelineCache { inner: cache }\n    }\n}\n\n/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.\nimpl Device {\n    /// Create a bottom level acceleration structure, used inside a top level acceleration structure for ray tracing.\n    /// - `desc`: The descriptor of the acceleration structure.\n    /// - `sizes`: Size descriptor limiting what can be built into the acceleration structure.\n    ///\n    /// # Validation\n    /// If any of the following is not satisfied a validation error is generated\n    ///\n    /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.\n    /// if `sizes` is [`BlasGeometrySizeDescriptors::Triangles`] then the following must be satisfied\n    /// - For every geometry descriptor (for the purposes this is called `geo_desc`) of `sizes.descriptors` the following must be satisfied:\n    ///     - `geo_desc.vertex_format` must be within allowed formats (allowed formats for a given feature set\n    ///       may be queried with [`Features::allowed_vertex_formats_for_blas`]).\n    ///     - Both or neither of `geo_desc.index_format` and `geo_desc.index_count` must be provided.\n    ///\n    /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY\n    /// [`Features::allowed_vertex_formats_for_blas`]: wgt::Features::allowed_vertex_formats_for_blas\n    #[must_use]\n    pub fn create_blas(\n        &self,\n        desc: &CreateBlasDescriptor<'_>,\n        sizes: BlasGeometrySizeDescriptors,\n    ) -> Blas {\n        let (handle, blas) = self.inner.create_blas(desc, sizes);\n\n        Blas {\n            inner: blas,\n            handle,\n        }\n    }\n\n    /// Create a top level acceleration structure, used for ray tracing.\n    /// - `desc`: The descriptor of the acceleration structure.\n    ///\n    /// # Validation\n    /// If any of the following is not satisfied a validation error is generated\n    ///\n    /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.\n    ///\n    /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY\n    #[must_use]\n    pub fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas {\n        let tlas = self.inner.create_tlas(desc);\n\n        Tlas {\n            inner: tlas,\n            instances: vec![None; desc.max_instances as usize],\n            lowest_unmodified: 0,\n        }\n    }\n}\n\n/// Requesting a device from an [`Adapter`] failed.\n#[derive(Clone, Debug)]\npub struct RequestDeviceError {\n    pub(crate) inner: RequestDeviceErrorKind,\n}\n#[derive(Clone, Debug)]\npub(crate) enum RequestDeviceErrorKind {\n    /// Error from [`wgpu_core`].\n    // must match dependency cfg\n    #[cfg(wgpu_core)]\n    Core(wgc::instance::RequestDeviceError),\n\n    /// Error from web API that was called by `wgpu` to request a device.\n    ///\n    /// (This is currently never used by the webgl backend, but it could be.)\n    #[cfg(webgpu)]\n    WebGpu(String),\n}\n\nstatic_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);\n\nimpl fmt::Display for RequestDeviceError {\n    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            #[cfg(wgpu_core)]\n            RequestDeviceErrorKind::Core(error) => error.fmt(_f),\n            #[cfg(webgpu)]\n            RequestDeviceErrorKind::WebGpu(error) => {\n                write!(_f, \"{error}\")\n            }\n            #[cfg(not(any(webgpu, wgpu_core)))]\n            _ => unimplemented!(\"unknown `RequestDeviceErrorKind`\"),\n        }\n    }\n}\n\nimpl error::Error for RequestDeviceError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match &self.inner {\n            #[cfg(wgpu_core)]\n            RequestDeviceErrorKind::Core(error) => error.source(),\n            #[cfg(webgpu)]\n            RequestDeviceErrorKind::WebGpu(_) => None,\n            #[cfg(not(any(webgpu, wgpu_core)))]\n            _ => unimplemented!(\"unknown `RequestDeviceErrorKind`\"),\n        }\n    }\n}\n\n#[cfg(wgpu_core)]\nimpl From<wgc::instance::RequestDeviceError> for RequestDeviceError {\n    fn from(error: wgc::instance::RequestDeviceError) -> Self {\n        Self {\n            inner: RequestDeviceErrorKind::Core(error),\n        }\n    }\n}\n\n/// The callback of [`Device::on_uncaptured_error()`].\n///\n/// It must be a function with this signature.\npub trait UncapturedErrorHandler: Fn(Error) + Send + Sync + 'static {}\nimpl<T> UncapturedErrorHandler for T where T: Fn(Error) + Send + Sync + 'static {}\n\n/// Kinds of [`Error`]s a [`Device::push_error_scope()`] may be configured to catch.\n#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]\npub enum ErrorFilter {\n    /// Catch only out-of-memory errors.\n    OutOfMemory,\n    /// Catch only validation errors.\n    Validation,\n    /// Catch only internal errors.\n    Internal,\n}\nstatic_assertions::assert_impl_all!(ErrorFilter: Send, Sync);\n\n/// Lower level source of the error.\n///\n/// `Send + Sync` varies depending on configuration.\n#[cfg(send_sync)]\n#[cfg_attr(docsrs, doc(cfg(all())))]\npub type ErrorSource = Box<dyn error::Error + Send + Sync + 'static>;\n/// Lower level source of the error.\n///\n/// `Send + Sync` varies depending on configuration.\n#[cfg(not(send_sync))]\n#[cfg_attr(docsrs, doc(cfg(all())))]\npub type ErrorSource = Box<dyn error::Error + 'static>;\n\n/// Errors resulting from usage of GPU APIs.\n///\n/// By default, errors translate into panics. Depending on the backend and circumstances,\n/// errors may occur synchronously or asynchronously. When errors need to be handled, use\n/// [`Device::push_error_scope()`] or [`Device::on_uncaptured_error()`].\n#[derive(Debug)]\npub enum Error {\n    /// Out of memory.\n    OutOfMemory {\n        /// Lower level source of the error.\n        source: ErrorSource,\n    },\n    /// Validation error, signifying a bug in code or data provided to `wgpu`.\n    Validation {\n        /// Lower level source of the error.\n        source: ErrorSource,\n        /// Description of the validation error.\n        description: String,\n    },\n    /// Internal error. Used for signalling any failures not explicitly expected by WebGPU.\n    ///\n    /// These could be due to internal implementation or system limits being reached.\n    Internal {\n        /// Lower level source of the error.\n        source: ErrorSource,\n        /// Description of the internal GPU error.\n        description: String,\n    },\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Error: Send, Sync);\n\nimpl error::Error for Error {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match self {\n            Error::OutOfMemory { source } => Some(source.as_ref()),\n            Error::Validation { source, .. } => Some(source.as_ref()),\n            Error::Internal { source, .. } => Some(source.as_ref()),\n        }\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Error::OutOfMemory { .. } => f.write_str(\"Out of Memory\"),\n            Error::Validation { description, .. } => f.write_str(description),\n            Error::Internal { description, .. } => f.write_str(description),\n        }\n    }\n}\n\n/// Guard for an error scope pushed with [`Device::push_error_scope()`].\n///\n/// Call [`pop()`] to pop the scope and get a future for the result. If\n/// the guard is dropped without being popped explicitly, the scope will still be popped,\n/// and the captured errors will be dropped.\n///\n/// This guard is neither `Send` nor `Sync`, as error scopes are handled\n/// on a per-thread basis when the `std` feature is enabled.\n///\n/// [`pop()`]: ErrorScopeGuard::pop\n#[must_use = \"Error scopes must be explicitly popped to retrieve errors they catch\"]\npub struct ErrorScopeGuard {\n    device: dispatch::DispatchDevice,\n    index: u32,\n    popped: bool,\n    // Ensure the guard is !Send and !Sync\n    _phantom: PhantomData<*mut ()>,\n}\n\nstatic_assertions::assert_not_impl_any!(ErrorScopeGuard: Send, Sync);\n\nimpl ErrorScopeGuard {\n    /// Pops the error scope.\n    ///\n    /// Returns a future which resolves to the error captured by this scope, if any.\n    /// The pop takes effect immediately; the future does not need to be awaited before doing work that is outside of this error scope.\n    pub fn pop(mut self) -> impl Future<Output = Option<Error>> + WasmNotSend {\n        self.popped = true;\n        self.device.pop_error_scope(self.index)\n    }\n}\n\nimpl Drop for ErrorScopeGuard {\n    fn drop(&mut self) {\n        if !self.popped {\n            drop(self.device.pop_error_scope(self.index));\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/external_texture.rs",
    "content": "use crate::*;\n\n/// Handle to an external texture on the GPU.\n///\n/// It can be created with [`Device::create_external_texture`].\n///\n/// Corresponds to [WebGPU `GPUExternalTexture`](https://gpuweb.github.io/gpuweb/#gpuexternaltexture).\n#[derive(Debug, Clone)]\npub struct ExternalTexture {\n    pub(crate) inner: dispatch::DispatchExternalTexture,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ExternalTexture: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(ExternalTexture => .inner);\n\nimpl ExternalTexture {\n    /// Destroy the associated native resources as soon as possible.\n    pub fn destroy(&self) {\n        self.inner.destroy();\n    }\n}\n\n/// Describes an [`ExternalTexture`].\n///\n/// For use with [`Device::create_external_texture`].\n///\n/// Corresponds to [WebGPU `GPUExternalTextureDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuexternaltexturedescriptor).\npub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(ExternalTextureDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/instance.rs",
    "content": "use alloc::vec::Vec;\nuse core::future::Future;\n\nuse crate::{dispatch::InstanceInterface, util::Mutex, *};\n\nbitflags::bitflags! {\n    /// WGSL language extensions.\n    ///\n    /// WGSL spec.: <https://www.w3.org/TR/WGSL/#language-extensions-sec>\n    #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]\n    pub struct WgslLanguageFeatures: u32 {\n        /// <https://www.w3.org/TR/WGSL/#language_extension-readonly_and_readwrite_storage_textures>\n        const ReadOnlyAndReadWriteStorageTextures = 1 << 0;\n        /// <https://www.w3.org/TR/WGSL/#language_extension-packed_4x8_integer_dot_product>\n        const Packed4x8IntegerDotProduct = 1 << 1;\n        /// <https://www.w3.org/TR/WGSL/#language_extension-unrestricted_pointer_parameters>\n        const UnrestrictedPointerParameters = 1 << 2;\n        /// <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>\n        const PointerCompositeAccess = 1 << 3;\n    }\n}\n\n/// Contains the various entry points to start interacting with the system's GPUs.\n///\n/// This is the first thing you create when using wgpu.\n/// Its primary use is to create [`Adapter`]s and [`Surface`]s.\n///\n/// Does not have to be kept alive.\n///\n/// Corresponds to [WebGPU `GPU`](https://gpuweb.github.io/gpuweb/#gpu-interface).\n#[derive(Debug, Clone)]\npub struct Instance {\n    inner: dispatch::DispatchInstance,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Instance: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Instance => .inner);\n\nimpl Default for Instance {\n    /// Creates a new instance of wgpu with default options.\n    ///\n    /// Backends are set to `Backends::all()`, and FXC is chosen as the `dx12_shader_compiler`.\n    ///\n    /// # Panics\n    ///\n    /// If no backend feature for the active target platform is enabled,\n    /// this method will panic, see [`Instance::enabled_backend_features()`].\n    fn default() -> Self {\n        // TODO: Differentiate constructors here too?\n        Self::new(InstanceDescriptor::new_without_display_handle())\n    }\n}\n\nimpl Instance {\n    /// Create an new instance of wgpu using the given options and enabled backends.\n    ///\n    /// # Panics\n    ///\n    /// - If no backend feature for the active target platform is enabled,\n    ///   this method will panic; see [`Instance::enabled_backend_features()`].\n    #[allow(clippy::allow_attributes, unreachable_code)]\n    pub fn new(desc: InstanceDescriptor) -> Self {\n        if Self::enabled_backend_features().is_empty() {\n            panic!(\n                \"No wgpu backend feature that is implemented for the target platform was enabled. \\\n                 See `wgpu::Instance::enabled_backend_features()` for more information.\"\n            );\n        }\n\n        #[cfg(webgpu)]\n        {\n            let is_only_available_backend = !cfg!(wgpu_core);\n            let requested_webgpu = desc.backends.contains(Backends::BROWSER_WEBGPU);\n            let support_webgpu = crate::backend::get_browser_gpu_property()\n                .map(|maybe_gpu| maybe_gpu.is_some())\n                .unwrap_or(false);\n\n            if is_only_available_backend || (requested_webgpu && support_webgpu) {\n                return Self {\n                    inner: crate::backend::ContextWebGpu::new(desc).into(),\n                };\n            }\n        }\n\n        #[cfg(wgpu_core)]\n        {\n            return Self {\n                inner: crate::backend::ContextWgpuCore::new(desc).into(),\n            };\n        }\n\n        // Silence unused variable warnings without adding _ to the parameter name (which shows up in docs).\n        let _ = desc;\n\n        unreachable!(\n            \"Earlier check of `enabled_backend_features` should have prevented getting here!\"\n        );\n    }\n\n    /// Returns which backends can be picked for the current build configuration.\n    ///\n    /// The returned set depends on a combination of target platform and enabled features.\n    /// This does *not* do any runtime checks and is exclusively based on compile time information.\n    ///\n    /// `InstanceDescriptor::backends` does not need to be a subset of this,\n    /// but any backend that is not in this set, will not be picked.\n    pub const fn enabled_backend_features() -> Backends {\n        let mut backends = Backends::empty();\n        // `.set` and `|=` don't work in a `const` context.\n        if cfg!(noop) {\n            backends = backends.union(Backends::NOOP);\n        }\n        if cfg!(vulkan) {\n            backends = backends.union(Backends::VULKAN);\n        }\n        if cfg!(any(gles, webgl)) {\n            backends = backends.union(Backends::GL);\n        }\n        if cfg!(metal) {\n            backends = backends.union(Backends::METAL);\n        }\n        if cfg!(dx12) {\n            backends = backends.union(Backends::DX12);\n        }\n        if cfg!(webgpu) {\n            backends = backends.union(Backends::BROWSER_WEBGPU);\n        }\n        backends\n    }\n\n    /// Returns the set of [WGSL language extensions] supported by this instance.\n    ///\n    /// [WGSL language extensions]: https://www.w3.org/TR/webgpu/#gpuwgsllanguagefeatures\n    #[cfg(feature = \"wgsl\")]\n    pub fn wgsl_language_features(&self) -> WgslLanguageFeatures {\n        self.inner.wgsl_language_features()\n    }\n\n    /// Retrieves all available [`Adapter`]s that match the given [`Backends`].\n    ///\n    /// # Arguments\n    ///\n    /// - `backends` - Backends from which to enumerate adapters.\n    pub fn enumerate_adapters(&self, backends: Backends) -> impl Future<Output = Vec<Adapter>> {\n        let future = self.inner.enumerate_adapters(backends);\n\n        async move {\n            future\n                .await\n                .iter()\n                .map(|adapter| Adapter {\n                    inner: adapter.clone(),\n                })\n                .collect()\n        }\n    }\n\n    /// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`].\n    ///\n    /// Some options are \"soft\", so treated as non-mandatory. Others are \"hard\".\n    ///\n    /// If no adapters are found that satisfy all the \"hard\" options, an error is returned.\n    ///\n    /// When targeting WebGL2, a [`compatible_surface`](RequestAdapterOptions::compatible_surface)\n    /// must be specified; using `RequestAdapterOptions::default()` will not succeed.\n    pub fn request_adapter(\n        &self,\n        options: &RequestAdapterOptions<'_, '_>,\n    ) -> impl Future<Output = Result<Adapter, RequestAdapterError>> + WasmNotSend {\n        let future = self.inner.request_adapter(options);\n        async move { future.await.map(|adapter| Adapter { inner: adapter }) }\n    }\n\n    /// Creates a new surface targeting a given window/canvas/surface/etc..\n    ///\n    /// Internally, this creates surfaces for all backends that are enabled for this instance.\n    ///\n    /// See [`SurfaceTarget`] for what targets are supported.\n    /// See [`Instance::create_surface_unsafe()`] for surface creation with unsafe target variants.\n    ///\n    /// Most commonly used are window handles (or provider of windows handles)\n    /// which can be passed directly as they're automatically converted to [`SurfaceTarget`].\n    pub fn create_surface<'window>(\n        &self,\n        target: impl Into<SurfaceTarget<'window>>,\n    ) -> Result<Surface<'window>, CreateSurfaceError> {\n        // Handle origin (i.e. window) to optionally take ownership of to make the surface outlast the window.\n        let handle_source;\n\n        let target = target.into();\n        let mut surface = match target {\n            SurfaceTarget::Window(window) => unsafe {\n                let surface = self.create_surface_unsafe(\n                    SurfaceTargetUnsafe::from_window(&window).map_err(|e| CreateSurfaceError {\n                        inner: CreateSurfaceErrorKind::RawHandle(e),\n                    })?,\n                );\n                handle_source = Some(window);\n\n                surface\n            }?,\n            SurfaceTarget::DisplayAndWindow(display_and_window_handle) => unsafe {\n                let surface = self.create_surface_unsafe(\n                    SurfaceTargetUnsafe::from_display_and_window(\n                        &display_and_window_handle,\n                        &display_and_window_handle,\n                    )\n                    .map_err(|e| CreateSurfaceError {\n                        inner: CreateSurfaceErrorKind::RawHandle(e),\n                    })?,\n                );\n                handle_source = Some(display_and_window_handle);\n\n                surface\n            }?,\n            #[cfg(web)]\n            SurfaceTarget::Canvas(canvas) => {\n                handle_source = None;\n\n                let value: &wasm_bindgen::JsValue = &canvas;\n                let obj = core::ptr::NonNull::from(value).cast();\n                let raw_window_handle = raw_window_handle::WebCanvasWindowHandle::new(obj).into();\n\n                // Note that we need to call this while we still have `value` around.\n                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.\n                unsafe {\n                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {\n                        raw_display_handle: None,\n                        raw_window_handle,\n                    })\n                }?\n            }\n            #[cfg(web)]\n            SurfaceTarget::OffscreenCanvas(canvas) => {\n                handle_source = None;\n\n                let value: &wasm_bindgen::JsValue = &canvas;\n                let obj = core::ptr::NonNull::from(value).cast();\n                let raw_window_handle =\n                    raw_window_handle::WebOffscreenCanvasWindowHandle::new(obj).into();\n\n                // Note that we need to call this while we still have `value` around.\n                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.\n                unsafe {\n                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {\n                        raw_display_handle: None,\n                        raw_window_handle,\n                    })\n                }?\n            }\n        };\n\n        surface._handle_source = handle_source;\n\n        Ok(surface)\n    }\n\n    /// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target.\n    ///\n    /// Internally, this creates surfaces for all backends that are enabled for this instance.\n    ///\n    /// See [`SurfaceTargetUnsafe`] for what targets are supported.\n    /// See [`Instance::create_surface`] for surface creation with safe target variants.\n    ///\n    /// # Safety\n    ///\n    /// - See respective [`SurfaceTargetUnsafe`] variants for safety requirements.\n    pub unsafe fn create_surface_unsafe<'window>(\n        &self,\n        target: SurfaceTargetUnsafe,\n    ) -> Result<Surface<'window>, CreateSurfaceError> {\n        let surface = unsafe { self.inner.create_surface(target)? };\n\n        Ok(Surface {\n            _handle_source: None,\n            inner: surface,\n            config: Mutex::new(None),\n        })\n    }\n\n    /// Polls all devices.\n    ///\n    /// If `force_wait` is true and this is not running on the web, then this\n    /// function will block until all in-flight buffers have been mapped and\n    /// all submitted commands have finished execution.\n    ///\n    /// Return `true` if all devices' queues are empty, or `false` if there are\n    /// queue submissions still in flight. (Note that, unless access to all\n    /// [`Queue`s] associated with this [`Instance`] is coordinated somehow,\n    /// this information could be out of date by the time the caller receives\n    /// it. `Queue`s can be shared between threads, and other threads could\n    /// submit new work at any time.)\n    ///\n    /// On the web, this is a no-op. `Device`s are automatically polled.\n    ///\n    /// [`Queue`s]: Queue\n    pub fn poll_all(&self, force_wait: bool) -> bool {\n        self.inner.poll_all_devices(force_wait)\n    }\n\n    /// Generates memory report.\n    ///\n    /// Returns `None` if the feature is not supported by the backend\n    /// which happens only when WebGPU is pre-selected by the instance creation.\n    #[cfg(wgpu_core)]\n    pub fn generate_report(&self) -> Option<wgc::global::GlobalReport> {\n        self.inner.as_core_opt().map(|ctx| ctx.generate_report())\n    }\n}\n\n/// Interop with wgpu-hal.\n#[cfg(wgpu_core)]\nimpl Instance {\n    /// Create an new instance of wgpu from a wgpu-hal instance. This is often useful\n    /// when you need to do backend specific logic, or interop with an existing backend\n    /// instance.\n    ///\n    /// # Types\n    ///\n    /// The type of `A::Instance` depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Instance\")]\n    #[doc = crate::macros::hal_type_metal!(\"Instance\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Instance\")]\n    #[doc = crate::macros::hal_type_gles!(\"Instance\")]\n    ///\n    /// # Safety\n    ///\n    /// - The `hal_instance` must be a valid and usable instance of the backend specified by `A`.\n    /// - wgpu will act like it has complete ownership of this instance, and will destroy it\n    ///   when the last reference to the instance, internal or external, is dropped.\n    pub unsafe fn from_hal<A: hal::Api>(hal_instance: A::Instance) -> Self {\n        Self {\n            inner: unsafe {\n                crate::backend::ContextWgpuCore::from_hal_instance::<A>(hal_instance).into()\n            },\n        }\n    }\n\n    /// Get the [`wgpu_hal`] instance from this `Instance`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Instance`].\n    ///\n    /// # Types\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Instance\")]\n    #[doc = crate::macros::hal_type_metal!(\"Instance\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Instance\")]\n    #[doc = crate::macros::hal_type_gles!(\"Instance\")]\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The instance is not from the backend specified by `A`.\n    /// - The instance is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Instance`]: hal::Api::Instance\n    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {\n        self.inner\n            .as_core_opt()\n            .and_then(|ctx| unsafe { ctx.instance_as_hal::<A>() })\n    }\n\n    /// Converts a wgpu-hal [`hal::ExposedAdapter`] to a wgpu [`Adapter`].\n    ///\n    /// # Types\n    ///\n    /// The type of `hal_adapter.adapter` depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_metal!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Adapter\")]\n    #[doc = crate::macros::hal_type_gles!(\"Adapter\")]\n    ///\n    /// # Safety\n    ///\n    /// `hal_adapter` must be created from this instance internal handle.\n    pub unsafe fn create_adapter_from_hal<A: hal::Api>(\n        &self,\n        hal_adapter: hal::ExposedAdapter<A>,\n    ) -> Adapter {\n        let core_instance = self.inner.as_core();\n        let adapter = unsafe { core_instance.create_adapter_from_hal(hal_adapter) };\n        let core = backend::wgpu_core::CoreAdapter {\n            context: core_instance.clone(),\n            id: adapter,\n        };\n\n        Adapter { inner: core.into() }\n    }\n}\n\n/// Interop with wgpu-core.\n#[cfg(wgpu_core)]\nimpl Instance {\n    /// Create an new instance of wgpu from a wgpu-core instance.\n    ///\n    /// # Arguments\n    ///\n    /// - `core_instance` - wgpu-core instance.\n    ///\n    /// # Safety\n    ///\n    /// Refer to the creation of wgpu-core Instance.\n    pub unsafe fn from_core(core_instance: wgc::instance::Instance) -> Self {\n        Self {\n            inner: unsafe {\n                crate::backend::ContextWgpuCore::from_core_instance(core_instance).into()\n            },\n        }\n    }\n}\n\n/// Interop with custom backends.\n#[cfg(custom)]\nimpl Instance {\n    /// Creates instance from custom context implementation\n    pub fn from_custom<T: InstanceInterface>(instance: T) -> Self {\n        Self {\n            inner: dispatch::DispatchInstance::Custom(backend::custom::DynContext::new(instance)),\n        }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Instance (if custom backend and is internally T)\n    pub fn as_custom<T: custom::InstanceInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/mod.rs",
    "content": "//! Types and functions which define our public api and their\n//! helper functionality.\n//!\n//! # Conventions\n//!\n//! Each major type gets its own module. The module is laid out as follows:\n//!\n//! - The type itself\n//! - `impl` block for the type\n//! - `Drop` implementation for the type (if needed)\n//! - Descriptor types and their subtypes.\n//! - Any non-public helper types or functions.\n//!\n//! # Imports\n//!\n//! Because our public api is \"flat\" (i.e. all types are directly under the `wgpu` module),\n//! we use a single `crate::*` import at the top of each module to bring in all the types in\n//! the public api. This is done to:\n//! - Avoid having to write out a long list of imports for each module.\n//! - Allow docs to be written naturally, without needing to worry about needing dedicated doc imports.\n//! - Treat wgpu-types types and wgpu-core types as a single set.\n\nmod adapter;\nmod bind_group;\nmod bind_group_layout;\nmod blas;\nmod buffer;\nmod command_buffer;\n/// Not a root type, but common types for command buffer deferral actions.\nmod command_buffer_actions;\nmod command_encoder;\n// Not a root type, but common descriptor types for pipelines.\nmod common_pipeline;\nmod compute_pass;\nmod compute_pipeline;\nmod device;\nmod external_texture;\nmod instance;\nmod pipeline_cache;\nmod pipeline_layout;\nmod query_set;\nmod queue;\nmod render_bundle;\nmod render_bundle_encoder;\nmod render_pass;\nmod render_pipeline;\nmod sampler;\nmod shader_module;\nmod surface;\nmod surface_texture;\nmod texture;\nmod texture_view;\nmod tlas;\n\npub use adapter::*;\npub use bind_group::*;\npub use bind_group_layout::*;\npub use blas::*;\npub use buffer::*;\npub use command_buffer::*;\nuse command_buffer_actions::*;\npub use command_encoder::*;\npub use common_pipeline::*;\npub use compute_pass::*;\npub use compute_pipeline::*;\npub use device::*;\npub use external_texture::*;\npub use instance::*;\npub use pipeline_cache::*;\npub use pipeline_layout::*;\npub use query_set::*;\npub use queue::*;\npub use render_bundle::*;\npub use render_bundle_encoder::*;\npub use render_pass::*;\npub use render_pipeline::*;\npub use sampler::*;\npub use shader_module::*;\npub use surface::*;\npub use surface_texture::*;\npub use texture::*;\npub use texture_view::*;\npub use tlas::*;\n\n/// Object debugging label.\npub type Label<'a> = Option<&'a str>;\n\n/// A cute utility type that works just like `PhantomData`, but also\n/// implements `Drop`. This forces any lifetimes that are associated\n/// with the type to be used until the `Drop` impl is ran. This prevents\n/// lifetimes from being shortened.\n#[derive(Debug)]\npub(crate) struct PhantomDrop<T>(core::marker::PhantomData<T>);\n\nimpl<T> Default for PhantomDrop<T> {\n    fn default() -> Self {\n        Self(core::marker::PhantomData)\n    }\n}\n\nimpl<T> Drop for PhantomDrop<T> {\n    fn drop(&mut self) {}\n}\n"
  },
  {
    "path": "wgpu/src/api/pipeline_cache.rs",
    "content": "use alloc::vec::Vec;\n\nuse crate::*;\n\n/// Handle to a pipeline cache, which is used to accelerate\n/// creating [`RenderPipeline`]s and [`ComputePipeline`]s\n/// in subsequent executions\n///\n/// This reuse is only applicable for the same or similar devices.\n/// See [`util::pipeline_cache_key`] for some details and a suggested workflow.\n///\n/// Created using [`Device::create_pipeline_cache`].\n///\n/// # Background\n///\n/// In most GPU drivers, shader code must be converted into a machine code\n/// which can be executed on the GPU.\n/// Generating this machine code can require a lot of computation.\n/// Pipeline caches allow this computation to be reused between executions\n/// of the program.\n/// This can be very useful for reducing program startup time.\n///\n/// Note that most desktop GPU drivers will manage their own caches,\n/// meaning that little advantage can be gained from this on those platforms.\n/// However, on some platforms, especially Android, drivers leave this to the\n/// application to implement.\n///\n/// Unfortunately, drivers do not expose whether they manage their own caches.\n/// Some reasonable policies for applications to use are:\n/// - Manage their own pipeline cache on all platforms\n/// - Only manage pipeline caches on Android\n///\n/// # Usage\n///\n/// This is used as [`RenderPipelineDescriptor::cache`] or [`ComputePipelineDescriptor::cache`].\n/// It is valid to use this resource when creating multiple pipelines, in\n/// which case it will likely cache each of those pipelines.\n/// It is also valid to create a new cache for each pipeline.\n///\n/// This resource is most useful when the data produced from it (using\n/// [`PipelineCache::get_data`]) is persisted.\n/// Care should be taken that pipeline caches are only used for the same device,\n/// as pipeline caches from compatible devices are unlikely to provide any advantage.\n/// `util::pipeline_cache_key` can be used as a file/directory name to help ensure that.\n///\n/// It is recommended to store pipeline caches atomically. If persisting to disk,\n/// this can usually be achieved by creating a temporary file, then moving/[renaming]\n/// the temporary file over the existing cache\n///\n/// # Storage Usage\n///\n/// There is not currently an API available to reduce the size of a cache.\n/// This is due to limitations in the underlying graphics APIs used.\n/// This is especially impactful if your application is being updated, so\n/// previous caches are no longer being used.\n///\n/// One option to work around this is to regenerate the cache.\n/// That is, creating the pipelines which your program runs using\n/// with the stored cached data, then recreating the *same* pipelines\n/// using a new cache, which your application then store.\n///\n/// # Implementations\n///\n/// This resource currently only works on the following backends:\n///  - Vulkan\n///\n/// This type is unique to the Rust API of `wgpu`.\n///\n/// [renaming]: std::fs::rename\n#[derive(Debug, Clone)]\npub struct PipelineCache {\n    pub(crate) inner: crate::dispatch::DispatchPipelineCache,\n}\n\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(PipelineCache: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(PipelineCache => .inner);\n\nimpl PipelineCache {\n    /// Get the data associated with this pipeline cache.\n    /// The data format is an implementation detail of `wgpu`.\n    /// The only defined operation on this data setting it as the `data` field\n    /// on [`PipelineCacheDescriptor`], then to [`Device::create_pipeline_cache`].\n    ///\n    /// This function is unique to the Rust API of `wgpu`.\n    pub fn get_data(&self) -> Option<Vec<u8>> {\n        self.inner.get_data()\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of PipelineCache (if custom backend and is internally T)\n    pub fn as_custom<T: custom::PipelineCacheInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/pipeline_layout.rs",
    "content": "use crate::*;\n\n/// Handle to a pipeline layout.\n///\n/// A `PipelineLayout` object describes the available binding groups of a pipeline.\n/// It can be created with [`Device::create_pipeline_layout`].\n///\n/// Corresponds to [WebGPU `GPUPipelineLayout`](https://gpuweb.github.io/gpuweb/#gpupipelinelayout).\n#[derive(Debug, Clone)]\npub struct PipelineLayout {\n    pub(crate) inner: dispatch::DispatchPipelineLayout,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(PipelineLayout: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(PipelineLayout => .inner);\n\nimpl PipelineLayout {\n    #[cfg(custom)]\n    /// Returns custom implementation of PipelineLayout (if custom backend and is internally T)\n    pub fn as_custom<T: custom::PipelineLayoutInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`PipelineLayout`].\n///\n/// For use with [`Device::create_pipeline_layout`].\n///\n/// Corresponds to [WebGPU `GPUPipelineLayoutDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpupipelinelayoutdescriptor).\n#[derive(Clone, Debug, Default)]\npub struct PipelineLayoutDescriptor<'a> {\n    /// Debug label of the pipeline layout. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Bind groups that this pipeline uses. The first entry will provide all the bindings for\n    /// \"set = 0\", second entry will provide all the bindings for \"set = 1\" etc.\n    pub bind_group_layouts: &'a [Option<&'a BindGroupLayout>],\n    /// The number of bytes of immediate data that are allocated for use\n    /// in the shader. The `var<immediate>`s in the shader attached to\n    /// this pipeline must be equal or smaller than this size.\n    ///\n    /// If this value is non-zero, [`Features::IMMEDIATES`] must be enabled.\n    pub immediate_size: u32,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(PipelineLayoutDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/query_set.rs",
    "content": "use crate::*;\n\n/// Handle to a query set.\n///\n/// A `QuerySet` is an opaque, mutable storage location for the results of queries:\n/// which are small pieces of information extracted from other operations such as render passes.\n/// See [`QueryType`] for what types of information can be collected.\n///\n/// Each query writes data into one or more result slots in the `QuerySet`, which must be created\n/// with a sufficient number of slots for that usage. Each result slot is a an unsigned 64-bit\n/// number.\n///\n/// Using queries consists of the following steps:\n///\n/// 1. Create a `QuerySet` of the appropriate type and number of query result slots\n///    using [`Device::create_query_set()`].\n/// 2. Pass the `QuerySet` to the commands which will write to it.\n///    See [`QueryType`] for the possible commands.\n/// 3. Execute the command [`CommandEncoder::resolve_query_set()`].\n///    This converts the opaque data stored in a `QuerySet` into [`u64`]s stored in a [`Buffer`].\n/// 4. Make use of that buffer, such as by copying its contents to the CPU\n///    or reading it from a compute shader.\n///\n/// Corresponds to [WebGPU `GPUQuerySet`](https://gpuweb.github.io/gpuweb/#queryset).\n#[derive(Debug, Clone)]\npub struct QuerySet {\n    pub(crate) inner: dispatch::DispatchQuerySet,\n}\n#[cfg(send_sync)]\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(QuerySet: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(QuerySet => .inner);\n\nimpl QuerySet {\n    #[cfg(custom)]\n    /// Returns custom implementation of QuerySet (if custom backend and is internally T)\n    pub fn as_custom<T: custom::QuerySetInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`QuerySet`].\n///\n/// For use with [`Device::create_query_set`].\n///\n/// Corresponds to [WebGPU `GPUQuerySetDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuquerysetdescriptor).\npub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(QuerySetDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/queue.rs",
    "content": "use alloc::boxed::Box;\nuse core::ops::{Deref, RangeBounds};\n\nuse crate::{api::DeferredCommandBufferActions, *};\n\n/// Handle to a command queue on a device.\n///\n/// A `Queue` executes recorded [`CommandBuffer`] objects and provides convenience methods\n/// for writing to [buffers](Queue::write_buffer) and [textures](Queue::write_texture).\n/// It can be created along with a [`Device`] by calling [`Adapter::request_device`].\n///\n/// Corresponds to [WebGPU `GPUQueue`](https://gpuweb.github.io/gpuweb/#gpu-queue).\n#[derive(Debug, Clone)]\npub struct Queue {\n    pub(crate) inner: dispatch::DispatchQueue,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Queue: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Queue => .inner);\n\nimpl Queue {\n    #[cfg(custom)]\n    /// Returns custom implementation of Queue (if custom backend and is internally T)\n    pub fn as_custom<T: custom::QueueInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n\n    #[cfg(custom)]\n    /// Creates Queue from custom implementation\n    pub fn from_custom<T: custom::QueueInterface>(queue: T) -> Self {\n        Self {\n            inner: dispatch::DispatchQueue::custom(queue),\n        }\n    }\n}\n\n/// Identifier for a particular call to [`Queue::submit`]. Can be used\n/// as part of an argument to [`Device::poll`] to block for a particular\n/// submission to finish.\n///\n/// This type is unique to the Rust API of `wgpu`.\n/// There is no analogue in the WebGPU specification.\n#[derive(Debug, Clone)]\npub struct SubmissionIndex {\n    pub(crate) index: u64,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(SubmissionIndex: Send, Sync);\n\n/// Passed to [`Device::poll`] to control how and if it should block.\npub type PollType = wgt::PollType<SubmissionIndex>;\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(PollType: Send, Sync);\n\n/// A write-only view into a staging buffer.\n///\n/// This type is what [`Queue::write_buffer_with()`] returns.\npub struct QueueWriteBufferView {\n    queue: Queue,\n    buffer: Buffer,\n    offset: BufferAddress,\n    inner: dispatch::DispatchQueueWriteBuffer,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(QueueWriteBufferView: Send, Sync);\n\nimpl QueueWriteBufferView {\n    #[cfg(custom)]\n    /// Returns custom implementation of QueueWriteBufferView (if custom backend and is internally T)\n    pub fn as_custom<T: custom::QueueWriteBufferInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\nimpl Drop for QueueWriteBufferView {\n    fn drop(&mut self) {\n        self.queue\n            .inner\n            .write_staging_buffer(&self.buffer.inner, self.offset, &self.inner);\n    }\n}\n\n/// These methods are equivalent to the methods of the same names on [`WriteOnly`].\nimpl QueueWriteBufferView {\n    /// Returns the length of this view; the number of bytes to be written.\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Returns `true` if the view has a length of 0.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns a [`WriteOnly`] reference to a portion of this.\n    ///\n    /// `.slice(..)` can be used to access the whole data.\n    pub fn slice<'a, S: RangeBounds<usize>>(&'a mut self, bounds: S) -> WriteOnly<'a, [u8]> {\n        // SAFETY:\n        // * this is a write mapping\n        // * function signature ensures no aliasing\n        unsafe { self.inner.write_slice() }.into_slice(bounds)\n    }\n\n    /// Copies all elements from src into `self`.\n    ///\n    /// The length of `src` must be the same as `self`.\n    ///\n    /// This method is equivalent to\n    /// [`self.slice(..).copy_from_slice(src)`][WriteOnly::copy_from_slice].\n    pub fn copy_from_slice(&mut self, src: &[u8]) {\n        self.slice(..).copy_from_slice(src)\n    }\n}\n\nimpl Queue {\n    /// Copies the bytes of `data` into `buffer` starting at `offset`.\n    ///\n    /// The data must be written fully in-bounds, that is, `offset + data.len() <= buffer.len()`.\n    ///\n    /// # Performance considerations\n    ///\n    /// * Calls to `write_buffer()` do *not* submit the transfer to the GPU\n    ///   immediately. They begin GPU execution only on the next call to\n    ///   [`Queue::submit()`], just before the explicitly submitted commands.\n    ///   To get a set of scheduled transfers started immediately,\n    ///   it's fine to call `submit` with no command buffers at all:\n    ///\n    ///   ```no_run\n    ///   # let queue: wgpu::Queue = todo!();\n    ///   # let buffer: wgpu::Buffer = todo!();\n    ///   # let data = [0u8];\n    ///   queue.write_buffer(&buffer, 0, &data);\n    ///   queue.submit([]);\n    ///   ```\n    ///\n    ///   However, `data` will be immediately copied into staging memory, so the\n    ///   caller may discard it any time after this call completes.\n    ///\n    /// * Consider using [`Queue::write_buffer_with()`] instead.\n    ///   That method allows you to prepare your data directly within the staging\n    ///   memory, rather than first placing it in a separate `[u8]` to be copied.\n    ///   That is, `queue.write_buffer(b, offset, data)` is approximately equivalent\n    ///   to `queue.write_buffer_with(b, offset, data.len()).copy_from_slice(data)`,\n    ///   so use `write_buffer_with()` if you can do something smarter than that\n    ///   [`copy_from_slice()`](slice::copy_from_slice). However, for small values\n    ///   (e.g. a typical uniform buffer whose contents come from a `struct`),\n    ///   there will likely be no difference, since the compiler will be able to\n    ///   optimize out unnecessary copies regardless.\n    ///\n    /// * Currently on native platforms, for both of these methods, the staging\n    ///   memory will be a new allocation. This will then be released after the\n    ///   next submission finishes. To entirely avoid short-lived allocations, you might\n    ///   be able to use [`StagingBelt`](crate::util::StagingBelt),\n    ///   or buffers you explicitly create, map, and unmap yourself.\n    pub fn write_buffer(&self, buffer: &Buffer, offset: BufferAddress, data: &[u8]) {\n        self.inner.write_buffer(&buffer.inner, offset, data);\n    }\n\n    /// Prepares to write data to a buffer via a mapped staging buffer.\n    ///\n    /// This operation allocates a temporary buffer and then returns a\n    /// [`QueueWriteBufferView`], which\n    ///\n    /// * dereferences to a `[u8]` of length `size`, and\n    /// * when dropped, schedules a copy of its contents into `buffer` at `offset`.\n    ///\n    /// Therefore, this obtains the same result as [`Queue::write_buffer()`], but may\n    /// allow you to skip one allocation and one copy of your data, if you are able to\n    /// assemble your data directly into the returned [`QueueWriteBufferView`] instead of\n    /// into a separate allocation like a [`Vec`](alloc::vec::Vec) first.\n    ///\n    /// The data must be written fully in-bounds, that is, `offset + size <= buffer.len()`.\n    ///\n    /// # Performance considerations\n    ///\n    /// * For small data not separately heap-allocated, there is no advantage of this\n    ///   over [`Queue::write_buffer()`].\n    ///\n    /// * Reading from the returned view may be slow, and will not yield the current\n    ///   contents of `buffer`. You should treat it as “write-only”.\n    ///\n    /// * Dropping the [`QueueWriteBufferView`] does *not* submit the\n    ///   transfer to the GPU immediately. The transfer begins only on the next\n    ///   call to [`Queue::submit()`] after the view is dropped, just before the\n    ///   explicitly submitted commands. To get a set of scheduled transfers started\n    ///   immediately, it's fine to call `queue.submit([])` with no command buffers at all.\n    ///\n    /// * Currently on native platforms, the staging memory will be a new allocation, which will\n    ///   then be released after the next submission finishes. To entirely avoid short-lived\n    ///   allocations, you might be able to use [`StagingBelt`](crate::util::StagingBelt),\n    ///   or buffers you explicitly create, map, and unmap yourself.\n    #[must_use]\n    pub fn write_buffer_with(\n        &self,\n        buffer: &Buffer,\n        offset: BufferAddress,\n        size: BufferSize,\n    ) -> Option<QueueWriteBufferView> {\n        profiling::scope!(\"Queue::write_buffer_with\");\n        self.inner\n            .validate_write_buffer(&buffer.inner, offset, size)?;\n        let staging_buffer = self.inner.create_staging_buffer(size)?;\n        Some(QueueWriteBufferView {\n            queue: self.clone(),\n            buffer: buffer.clone(),\n            offset,\n            inner: staging_buffer,\n        })\n    }\n\n    /// Copies the bytes of `data` into a texture.\n    ///\n    /// * `data` contains the texels to be written, which must be in\n    ///   [the same format as the texture](TextureFormat).\n    /// * `data_layout` describes the memory layout of `data`, which does not necessarily\n    ///   have to have tightly packed rows.\n    /// * `texture` specifies the texture to write into, and the location within the\n    ///   texture (coordinate offset, mip level) that will be overwritten.\n    /// * `size` is the size, in texels, of the region to be written.\n    ///\n    /// This method fails if `size` overruns the size of `texture`, or if `data` is too short.\n    ///\n    /// # Performance considerations\n    ///\n    /// This operation has the same performance considerations as [`Queue::write_buffer()`];\n    /// see its documentation for details.\n    ///\n    /// However, since there is no “mapped texture” like a mapped buffer,\n    /// alternate techniques for writing to textures will generally consist of first copying\n    /// the data to a buffer, then using [`CommandEncoder::copy_buffer_to_texture()`], or in\n    /// some cases a compute shader, to copy texels from that buffer to the texture.\n    pub fn write_texture(\n        &self,\n        texture: TexelCopyTextureInfo<'_>,\n        data: &[u8],\n        data_layout: TexelCopyBufferLayout,\n        size: Extent3d,\n    ) {\n        self.inner.write_texture(texture, data, data_layout, size);\n    }\n\n    /// Schedule a copy of data from `image` into `texture`.\n    #[cfg(web)]\n    pub fn copy_external_image_to_texture(\n        &self,\n        source: &wgt::CopyExternalImageSourceInfo,\n        dest: wgt::CopyExternalImageDestInfo<&api::Texture>,\n        size: Extent3d,\n    ) {\n        self.inner\n            .copy_external_image_to_texture(source, dest, size);\n    }\n\n    /// Submits a series of finished command buffers for execution.\n    pub fn submit<I: IntoIterator<Item = CommandBuffer>>(\n        &self,\n        command_buffers: I,\n    ) -> SubmissionIndex {\n        // As submit drains the iterator (even on error), collect deferred actions\n        // from each CommandBuffer along the way.\n        let mut actions = DeferredCommandBufferActions::default();\n\n        let mut command_buffers = command_buffers.into_iter().map(|comb| {\n            actions.append(&mut comb.actions.lock());\n            comb.buffer\n        });\n        let index = self.inner.submit(&mut command_buffers);\n\n        // Execute all deferred actions after submit.\n        actions.execute(&self.inner);\n\n        SubmissionIndex { index }\n    }\n\n    /// Gets the amount of nanoseconds each tick of a timestamp query represents.\n    ///\n    /// Returns zero if timestamp queries are unsupported.\n    ///\n    /// Timestamp values are represented in nanosecond values on WebGPU, see <https://gpuweb.github.io/gpuweb/#timestamp>\n    /// Therefore, this is always 1.0 on the web, but on wgpu-core a manual conversion is required.\n    pub fn get_timestamp_period(&self) -> f32 {\n        self.inner.get_timestamp_period()\n    }\n\n    /// Registers a callback that is invoked when the previous [`Queue::submit`] finishes executing\n    /// on the GPU. When this callback runs, all mapped-buffer callbacks registered for the same\n    /// submission are guaranteed to have been called.\n    ///\n    /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],\n    /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated into\n    /// an event loop or run on a separate thread.\n    ///\n    /// The callback runs on the thread that first calls one of the above functions after the GPU work\n    /// completes. There are no restrictions on the code you can run in the callback; however, on native\n    /// the polling call will not return until the callback finishes, so keep callbacks short (set flags,\n    /// send messages, etc.).\n    ///\n    /// [q::s]: Queue::submit\n    /// [i::p_a]: Instance::poll_all\n    /// [d::p]: Device::poll\n    pub fn on_submitted_work_done(&self, callback: impl FnOnce() + Send + 'static) {\n        self.inner.on_submitted_work_done(Box::new(callback));\n    }\n\n    /// Get the [`wgpu_hal`] device from this `Queue`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Queue`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Queue\")]\n    #[doc = crate::macros::hal_type_metal!(\"Queue\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Queue\")]\n    #[doc = crate::macros::hal_type_gles!(\"Queue\")]\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The queue is not from the backend specified by `A`.\n    /// - The queue is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Queue`]: hal::Api::Queue\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &self,\n    ) -> Option<impl Deref<Target = A::Queue> + WasmNotSendSync> {\n        let queue = self.inner.as_core_opt()?;\n        unsafe { queue.context.queue_as_hal::<A>(queue) }\n    }\n\n    /// Compact a BLAS, it must have had [`Blas::prepare_compaction_async`] called on it and had the\n    /// callback provided called.\n    ///\n    /// The returned BLAS is more restricted than a normal BLAS because it may not be rebuilt or\n    /// compacted.\n    pub fn compact_blas(&self, blas: &Blas) -> Blas {\n        let (handle, dispatch) = self.inner.compact_blas(&blas.inner);\n        Blas {\n            handle,\n            inner: dispatch,\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/render_bundle.rs",
    "content": "use crate::*;\n\n/// Pre-prepared reusable bundle of GPU operations.\n///\n/// It only supports a handful of render commands, but it makes them reusable. Executing a\n/// [`RenderBundle`] is often more efficient than issuing the underlying commands manually.\n///\n/// It can be created by use of a [`RenderBundleEncoder`], and executed onto a [`CommandEncoder`]\n/// using [`RenderPass::execute_bundles`].\n///\n/// Corresponds to [WebGPU `GPURenderBundle`](https://gpuweb.github.io/gpuweb/#render-bundle).\n#[derive(Debug, Clone)]\npub struct RenderBundle {\n    pub(crate) inner: dispatch::DispatchRenderBundle,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderBundle: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(RenderBundle => .inner);\n\nimpl RenderBundle {\n    #[cfg(custom)]\n    /// Returns custom implementation of RenderBundle (if custom backend and is internally T)\n    pub fn as_custom<T: custom::RenderBundleInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`RenderBundle`].\n///\n/// For use with [`RenderBundleEncoder::finish`].\n///\n/// Corresponds to [WebGPU `GPURenderBundleDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderbundledescriptor).\npub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(RenderBundleDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/render_bundle_encoder.rs",
    "content": "use core::{marker::PhantomData, num::NonZeroU32, ops::Range};\n\nuse crate::dispatch::RenderBundleEncoderInterface;\nuse crate::*;\n\n/// Encodes a series of GPU operations into a reusable \"render bundle\".\n///\n/// It only supports a handful of render commands, but it makes them reusable.\n/// It can be created with [`Device::create_render_bundle_encoder`].\n/// It can be executed onto a [`CommandEncoder`] using [`RenderPass::execute_bundles`].\n///\n/// Executing a [`RenderBundle`] is often more efficient than issuing the underlying commands\n/// manually.\n///\n/// Corresponds to [WebGPU `GPURenderBundleEncoder`](\n/// https://gpuweb.github.io/gpuweb/#gpurenderbundleencoder).\n#[derive(Debug)]\npub struct RenderBundleEncoder<'a> {\n    pub(crate) inner: dispatch::DispatchRenderBundleEncoder,\n    /// This type should be !Send !Sync, because it represents an allocation on this thread's\n    /// command buffer.\n    pub(crate) _p: PhantomData<(*const u8, &'a ())>,\n}\nstatic_assertions::assert_not_impl_any!(RenderBundleEncoder<'_>: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(RenderBundleEncoder<'_> => .inner);\n\n/// Describes a [`RenderBundleEncoder`].\n///\n/// For use with [`Device::create_render_bundle_encoder`].\n///\n/// Corresponds to [WebGPU `GPURenderBundleEncoderDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderbundleencoderdescriptor).\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct RenderBundleEncoderDescriptor<'a> {\n    /// Debug label of the render bundle encoder. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The formats of the color attachments that this render bundle is capable to rendering to. This\n    /// must match the formats of the color attachments in the render pass this render bundle is executed in.\n    pub color_formats: &'a [Option<TextureFormat>],\n    /// Information about the depth attachment that this render bundle is capable to rendering to. This\n    /// must match the format of the depth attachments in the render pass this render bundle is executed in.\n    pub depth_stencil: Option<RenderBundleDepthStencil>,\n    /// Sample count this render bundle is capable of rendering to. This must match the pipelines and\n    /// the render passes it is used in.\n    pub sample_count: u32,\n    /// If this render bundle will rendering to multiple array layers in the attachments at the same time.\n    pub multiview: Option<NonZeroU32>,\n}\nstatic_assertions::assert_impl_all!(RenderBundleEncoderDescriptor<'_>: Send, Sync);\n\nimpl<'a> RenderBundleEncoder<'a> {\n    /// Finishes recording and returns a [`RenderBundle`] that can be executed in other render passes.\n    pub fn finish(self, desc: &RenderBundleDescriptor<'_>) -> RenderBundle {\n        let bundle = match self.inner {\n            #[cfg(wgpu_core)]\n            dispatch::DispatchRenderBundleEncoder::Core(b) => b.finish(desc),\n            #[cfg(webgpu)]\n            dispatch::DispatchRenderBundleEncoder::WebGPU(b) => b.finish(desc),\n            #[cfg(custom)]\n            dispatch::DispatchRenderBundleEncoder::Custom(_) => unimplemented!(),\n        };\n\n        RenderBundle { inner: bundle }\n    }\n\n    /// Sets the active bind group for a given bind group index. The bind group layout\n    /// in the active pipeline when any `draw()` function is called must match the layout of this bind group.\n    ///\n    /// If the bind group have dynamic offsets, provide them in the binding order.\n    pub fn set_bind_group<'b, BG>(&mut self, index: u32, bind_group: BG, offsets: &[DynamicOffset])\n    where\n        Option<&'b BindGroup>: From<BG>,\n    {\n        let bg: Option<&'b BindGroup> = bind_group.into();\n        let bg = bg.map(|x| &x.inner);\n        self.inner.set_bind_group(index, bg, offsets);\n    }\n\n    /// Sets the active render pipeline.\n    ///\n    /// Subsequent draw calls will exhibit the behavior defined by `pipeline`.\n    pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) {\n        self.inner.set_pipeline(&pipeline.inner);\n    }\n\n    /// Sets the active index buffer.\n    ///\n    /// Subsequent calls to [`draw_indexed`](RenderBundleEncoder::draw_indexed) on this [`RenderBundleEncoder`] will\n    /// use `buffer` as the source index buffer.\n    pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat) {\n        self.inner.set_index_buffer(\n            &buffer_slice.buffer.inner,\n            index_format,\n            buffer_slice.offset,\n            Some(buffer_slice.size),\n        );\n    }\n\n    /// Assign a vertex buffer to a slot.\n    ///\n    /// Subsequent calls to [`draw`] and [`draw_indexed`] on this\n    /// [`RenderBundleEncoder`] will use `buffer` as one of the source vertex buffers.\n    ///\n    /// The `slot` refers to the index of the matching descriptor in\n    /// [`VertexState::buffers`].\n    ///\n    /// [`draw`]: RenderBundleEncoder::draw\n    /// [`draw_indexed`]: RenderBundleEncoder::draw_indexed\n    pub fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>) {\n        self.inner.set_vertex_buffer(\n            slot,\n            &buffer_slice.buffer.inner,\n            buffer_slice.offset,\n            Some(buffer_slice.size),\n        );\n    }\n\n    /// Draws primitives from the active vertex buffer(s).\n    ///\n    /// The active vertex buffers can be set with [`RenderBundleEncoder::set_vertex_buffer`].\n    /// Does not use an Index Buffer. If you need this see [`RenderBundleEncoder::draw_indexed`]\n    ///\n    /// Panics if vertices Range is outside of the range of the vertices range of any set vertex buffer.\n    ///\n    /// vertices: The range of vertices to draw.\n    /// instances: Range of Instances to draw. Use 0..1 if instance buffers are not used.\n    /// E.g.of how its used internally\n    /// ```rust ignore\n    /// for instance_id in instance_range {\n    ///     for vertex_id in vertex_range {\n    ///         let vertex = vertex[vertex_id];\n    ///         vertex_shader(vertex, vertex_id, instance_id);\n    ///     }\n    /// }\n    /// ```\n    pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        self.inner.draw(vertices, instances);\n    }\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffer(s).\n    ///\n    /// The active index buffer can be set with [`RenderBundleEncoder::set_index_buffer`].\n    /// The active vertex buffer(s) can be set with [`RenderBundleEncoder::set_vertex_buffer`].\n    ///\n    /// Panics if indices Range is outside of the range of the indices range of any set index buffer.\n    ///\n    /// indices: The range of indices to draw.\n    /// base_vertex: value added to each index value before indexing into the vertex buffers.\n    /// instances: Range of Instances to draw. Use 0..1 if instance buffers are not used.\n    /// E.g.of how its used internally\n    /// ```rust ignore\n    /// for instance_id in instance_range {\n    ///     for index_index in index_range {\n    ///         let vertex_id = index_buffer[index_index];\n    ///         let adjusted_vertex_id = vertex_id + base_vertex;\n    ///         let vertex = vertex[adjusted_vertex_id];\n    ///         vertex_shader(vertex, adjusted_vertex_id, instance_id);\n    ///     }\n    /// }\n    /// ```\n    pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        self.inner.draw_indexed(indices, base_vertex, instances);\n    }\n\n    /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`.\n    ///\n    /// The active vertex buffers can be set with [`RenderBundleEncoder::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).\n    pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) {\n        self.inner\n            .draw_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffers,\n    /// based on the contents of the `indirect_buffer`.\n    ///\n    /// The active index buffer can be set with [`RenderBundleEncoder::set_index_buffer`], while the active\n    /// vertex buffers can be set with [`RenderBundleEncoder::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).\n    pub fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &'a Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        self.inner\n            .draw_indexed_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of RenderBundleEncoder (if custom backend and is internally T)\n    pub fn as_custom<T: custom::RenderBundleEncoderInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// [`Features::IMMEDIATES`] must be enabled on the device in order to call these functions.\nimpl RenderBundleEncoder<'_> {\n    /// Set immediate data for subsequent draw calls within the render bundle.\n    ///\n    /// Write the bytes in `data` at offset `offset` within immediate data\n    /// storage. Both `offset` and the length of `data` must be\n    /// multiples of [`crate::IMMEDIATE_DATA_ALIGNMENT`], which is always 4.\n    ///\n    /// For example, if `offset` is `4` and `data` is eight bytes long, this\n    /// call will write `data` to bytes `4..12` of immediate data storage.\n    pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        self.inner.set_immediates(offset, data);\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/render_pass.rs",
    "content": "use core::{num::NonZeroU32, ops::Range};\n\nuse crate::{\n    api::{impl_deferred_command_buffer_actions, SharedDeferredCommandBufferActions},\n    *,\n};\npub use wgt::{LoadOp, Operations, StoreOp};\n\n/// In-progress recording of a render pass: a list of render commands in a [`CommandEncoder`].\n///\n/// It can be created with [`CommandEncoder::begin_render_pass()`], whose [`RenderPassDescriptor`]\n/// specifies the attachments (textures) that will be rendered to.\n///\n/// Most of the methods on `RenderPass` serve one of two purposes, identifiable by their names:\n///\n/// * `draw_*()`: Drawing (that is, encoding a render command, which, when executed by the GPU, will\n///   rasterize something and execute shaders).\n/// * `set_*()`: Setting part of the [render state](https://gpuweb.github.io/gpuweb/#renderstate)\n///   for future drawing commands.\n///\n/// A render pass may contain any number of drawing commands, and before/between each command the\n/// render state may be updated however you wish; each drawing command will be executed using the\n/// render state that has been set when the `draw_*()` function is called.\n///\n/// Corresponds to [WebGPU `GPURenderPassEncoder`](\n/// https://gpuweb.github.io/gpuweb/#render-pass-encoder).\n#[derive(Debug)]\npub struct RenderPass<'encoder> {\n    pub(crate) inner: dispatch::DispatchRenderPass,\n    pub(crate) actions: SharedDeferredCommandBufferActions,\n\n    /// This lifetime is used to protect the [`CommandEncoder`] from being used\n    /// while the pass is alive. This needs to be PhantomDrop to prevent the lifetime\n    /// from being shortened.\n    pub(crate) _encoder_guard: PhantomDrop<&'encoder ()>,\n}\n\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPass<'_>: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(RenderPass<'_> => .inner);\n\nimpl RenderPass<'_> {\n    /// Drops the lifetime relationship to the parent command encoder, making usage of\n    /// the encoder while this pass is recorded a run-time error instead.\n    ///\n    /// Attention: As long as the render pass has not been ended, any mutating operation on the parent\n    /// command encoder will cause a run-time error and invalidate it!\n    /// By default, the lifetime constraint prevents this, but it can be useful\n    /// to handle this at run time, such as when storing the pass and encoder in the same\n    /// data structure.\n    ///\n    /// This operation has no effect on pass recording.\n    /// It's a safe operation, since [`CommandEncoder`] is in a locked state as long as the pass is active\n    /// regardless of the lifetime constraint or its absence.\n    pub fn forget_lifetime(self) -> RenderPass<'static> {\n        RenderPass {\n            inner: self.inner,\n            actions: self.actions,\n            _encoder_guard: crate::api::PhantomDrop::default(),\n        }\n    }\n\n    /// Sets the active bind group for a given bind group index. The bind group layout\n    /// in the active pipeline when any `draw_*()` method is called must match the layout of\n    /// this bind group.\n    ///\n    /// If the bind group have dynamic offsets, provide them in binding order.\n    /// These offsets have to be aligned to [`Limits::min_uniform_buffer_offset_alignment`]\n    /// or [`Limits::min_storage_buffer_offset_alignment`] appropriately.\n    ///\n    /// Subsequent draw calls’ shader executions will be able to access data in these bind groups.\n    pub fn set_bind_group<'a, BG>(&mut self, index: u32, bind_group: BG, offsets: &[DynamicOffset])\n    where\n        Option<&'a BindGroup>: From<BG>,\n    {\n        let bg: Option<&'a BindGroup> = bind_group.into();\n        let bg = bg.map(|bg| &bg.inner);\n\n        self.inner.set_bind_group(index, bg, offsets);\n    }\n\n    /// Sets the active render pipeline.\n    ///\n    /// Subsequent draw calls will exhibit the behavior defined by `pipeline`.\n    pub fn set_pipeline(&mut self, pipeline: &RenderPipeline) {\n        self.inner.set_pipeline(&pipeline.inner);\n    }\n\n    /// Sets the blend color as used by some of the blending modes.\n    ///\n    /// Subsequent blending tests will test against this value.\n    /// If this method has not been called, the blend constant defaults to [`Color::TRANSPARENT`]\n    /// (all components zero).\n    pub fn set_blend_constant(&mut self, color: Color) {\n        self.inner.set_blend_constant(color);\n    }\n\n    /// Sets the active index buffer.\n    ///\n    /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will\n    /// use `buffer` as the source index buffer.\n    pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'_>, index_format: IndexFormat) {\n        self.inner.set_index_buffer(\n            &buffer_slice.buffer.inner,\n            index_format,\n            buffer_slice.offset,\n            Some(buffer_slice.size),\n        );\n    }\n\n    /// Assign a vertex buffer to a slot.\n    ///\n    /// Subsequent calls to [`draw`] and [`draw_indexed`] on this\n    /// [`RenderPass`] will use `buffer` as one of the source vertex buffers.\n    /// The format of the data in the buffer is specified by the [`VertexBufferLayout`] in the\n    /// pipeline's [`VertexState`].\n    ///\n    /// The `slot` refers to the index of the matching descriptor in\n    /// [`VertexState::buffers`].\n    ///\n    /// [`draw`]: RenderPass::draw\n    /// [`draw_indexed`]: RenderPass::draw_indexed\n    pub fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'_>) {\n        self.inner.set_vertex_buffer(\n            slot,\n            &buffer_slice.buffer.inner,\n            buffer_slice.offset,\n            Some(buffer_slice.size),\n        );\n    }\n\n    /// Sets the scissor rectangle used during the rasterization stage.\n    /// After transformation into [viewport coordinates](https://www.w3.org/TR/webgpu/#viewport-coordinates).\n    ///\n    /// Subsequent draw calls will discard any fragments which fall outside the scissor rectangle.\n    /// If this method has not been called, the scissor rectangle defaults to the entire bounds of\n    /// the render targets.\n    ///\n    /// The function of the scissor rectangle resembles [`set_viewport()`](Self::set_viewport),\n    /// but it does not affect the coordinate system, only which fragments are discarded.\n    pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {\n        self.inner.set_scissor_rect(x, y, width, height);\n    }\n\n    /// Sets the viewport used during the rasterization stage to linearly map\n    /// from [normalized device coordinates](https://www.w3.org/TR/webgpu/#ndc) to [viewport coordinates](https://www.w3.org/TR/webgpu/#viewport-coordinates).\n    ///\n    /// Subsequent draw calls will only draw within this region.\n    /// If this method has not been called, the viewport defaults to the entire bounds of the render\n    /// targets.\n    pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) {\n        self.inner.set_viewport(x, y, w, h, min_depth, max_depth);\n    }\n\n    /// Sets the stencil reference.\n    ///\n    /// Subsequent stencil tests will test against this value.\n    /// If this method has not been called, the stencil reference value defaults to `0`.\n    pub fn set_stencil_reference(&mut self, reference: u32) {\n        self.inner.set_stencil_reference(reference);\n    }\n\n    /// Inserts debug marker.\n    pub fn insert_debug_marker(&mut self, label: &str) {\n        self.inner.insert_debug_marker(label);\n    }\n\n    /// Start record commands and group it into debug marker group.\n    pub fn push_debug_group(&mut self, label: &str) {\n        self.inner.push_debug_group(label);\n    }\n\n    /// Stops command recording and creates debug group.\n    pub fn pop_debug_group(&mut self) {\n        self.inner.pop_debug_group();\n    }\n\n    /// Draws primitives from the active vertex buffer(s).\n    ///\n    /// The active vertex buffer(s) can be set with [`RenderPass::set_vertex_buffer`].\n    /// This does not use an index buffer. If you need indexed drawing, see [`RenderPass::draw_indexed`]\n    ///\n    /// Panics if `vertices` range is outside of the range of the vertices range of any set vertex buffer.\n    ///\n    /// - `vertices`: The range of vertices to draw.\n    /// - `instances`: Range of instances to draw. Use `0..1` if instance buffers are not used.\n    ///\n    /// E.g.of how its used internally\n    /// ```rust ignore\n    /// for instance_id in instance_range {\n    ///     for vertex_id in vertex_range {\n    ///         let vertex = vertex[vertex_id];\n    ///         vertex_shader(vertex, vertex_id, instance_id);\n    ///     }\n    /// }\n    /// ```\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        self.inner.draw(vertices, instances);\n    }\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffers.\n    ///\n    /// The active index buffer can be set with [`RenderPass::set_index_buffer`]\n    /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`].\n    ///\n    /// Panics if `indices` range is outside of the range of the indices range of the set index buffer.\n    ///\n    /// - `indices`: The range of indices to draw.\n    /// - `base_vertex`: value added to each index value before indexing into the vertex buffers.\n    /// - `instances`: Range of instances to draw. Use `0..1` if instance buffers are not used.\n    ///\n    /// E.g.of how its used internally\n    /// ```rust ignore\n    /// for instance_id in instance_range {\n    ///     for index_index in index_range {\n    ///         let vertex_id = index_buffer[index_index];\n    ///         let adjusted_vertex_id = vertex_id + base_vertex;\n    ///         let vertex = vertex[adjusted_vertex_id];\n    ///         vertex_shader(vertex, adjusted_vertex_id, instance_id);\n    ///     }\n    /// }\n    /// ```\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        self.inner.draw_indexed(indices, base_vertex, instances);\n    }\n\n    /// Draws using a mesh pipeline.\n    ///\n    /// The current pipeline must be a mesh pipeline.\n    ///\n    /// If the current pipeline has a task shader, run it with an workgroup for\n    /// every `vec3<u32>(i, j, k)` where `i`, `j`, and `k` are between `0` and\n    /// `group_count_x`, `group_count_y`, and `group_count_z`. The invocation with\n    /// index zero in each group is responsible for determining the mesh shader dispatch.\n    /// Its return value indicates the number of workgroups of mesh shaders to invoke. It also\n    /// passes a payload value for them to consume. Because each task workgroup is essentially\n    /// a mesh shader draw call, mesh workgroups dispatched by different task workgroups\n    /// cannot interact in any way, and `workgroup_id` corresponds to its location in the\n    /// calling specific task shader's dispatch group.\n    ///\n    /// If the current pipeline lacks a task shader, run its mesh shader with a\n    /// workgroup for every `vec3<u32>(i, j, k)` where `i`, `j`, and `k` are\n    /// between `0` and `group_count_x`, `group_count_y`, and `group_count_z`.\n    ///\n    /// Each mesh shader workgroup outputs a set of vertices and indices for primitives.\n    /// The indices outputted correspond to the vertices outputted by that same workgroup;\n    /// there is no global vertex buffer. These primitives are passed to the rasterizer and\n    /// essentially treated like a vertex shader output, except that the mesh shader may\n    /// choose to cull specific primitives or pass per-primitive non-interpolated values\n    /// to the fragment shader. As such, each primitive is then rendered with the current\n    /// pipeline's fragment shader, if present. Otherwise, [No Color Output mode] is used.\n    ///\n    /// [No Color Output mode]: https://www.w3.org/TR/webgpu/#no-color-output\n    pub fn draw_mesh_tasks(&mut self, group_count_x: u32, group_count_y: u32, group_count_z: u32) {\n        self.inner\n            .draw_mesh_tasks(group_count_x, group_count_y, group_count_z);\n    }\n\n    /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`.\n    ///\n    /// This is like calling [`RenderPass::draw`] but the contents of the call are specified in the `indirect_buffer`.\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).\n    ///\n    /// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].\n    pub fn draw_indirect(&mut self, indirect_buffer: &Buffer, indirect_offset: BufferAddress) {\n        self.inner\n            .draw_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffers,\n    /// based on the contents of the `indirect_buffer`.\n    ///\n    /// This is like calling [`RenderPass::draw_indexed`] but the contents of the call are specified in the `indirect_buffer`.\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).\n    ///\n    /// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].\n    pub fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        self.inner\n            .draw_indexed_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    /// Draws using a mesh pipeline,\n    /// based on the contents of the `indirect_buffer`\n    ///\n    /// This is like calling [`RenderPass::draw_mesh_tasks`] but the contents of the call are specified in the `indirect_buffer`.\n    /// The structure expected in the `indirect_buffer` must conform to [`DispatchIndirectArgs`](crate::util::DispatchIndirectArgs).\n    ///\n    /// Indirect drawing has some caveats depending on the features available. We are not currently able to validate\n    /// these and issue an error.\n    ///\n    /// See details on the individual flags for more information.\n    pub fn draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        self.inner\n            .draw_mesh_tasks_indirect(&indirect_buffer.inner, indirect_offset);\n    }\n\n    impl_deferred_command_buffer_actions!();\n\n    /// Execute a [render bundle][RenderBundle], which is a set of pre-recorded commands\n    /// that can be run together.\n    ///\n    /// Commands in the bundle do not inherit this render pass's current render state, and after the\n    /// bundle has executed, the state is **cleared** (reset to defaults, not the previous state).\n    pub fn execute_bundles<'a, I: IntoIterator<Item = &'a RenderBundle>>(\n        &mut self,\n        render_bundles: I,\n    ) {\n        let mut render_bundles = render_bundles.into_iter().map(|rb| &rb.inner);\n\n        self.inner.execute_bundles(&mut render_bundles);\n    }\n\n    /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`.\n    /// `count` draw calls are issued.\n    ///\n    /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).\n    /// These draw structures are expected to be tightly packed.\n    ///\n    /// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count: u32,\n    ) {\n        self.inner\n            .multi_draw_indirect(&indirect_buffer.inner, indirect_offset, count);\n    }\n\n    /// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,\n    /// based on the contents of the `indirect_buffer`. `count` draw calls are issued.\n    ///\n    /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active\n    /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).\n    /// These draw structures are expected to be tightly packed.\n    ///\n    /// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count: u32,\n    ) {\n        self.inner\n            .multi_draw_indexed_indirect(&indirect_buffer.inner, indirect_offset, count);\n    }\n\n    /// Dispatches multiple draw calls based on the contents of the `indirect_buffer`.\n    /// `count` draw calls are issued.\n    ///\n    /// The structure expected in the `indirect_buffer` must conform to [`DispatchIndirectArgs`](crate::util::DispatchIndirectArgs).\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count: u32,\n    ) {\n        self.inner\n            .multi_draw_mesh_tasks_indirect(&indirect_buffer.inner, indirect_offset, count);\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of RenderPass (if custom backend and is internally T)\n    pub fn as_custom<T: custom::RenderPassInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// [`Features::MULTI_DRAW_INDIRECT_COUNT`] must be enabled on the device in order to call these functions.\nimpl RenderPass<'_> {\n    /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`.\n    /// The count buffer is read to determine how many draws to issue.\n    ///\n    /// The indirect buffer must be long enough to account for `max_count` draws, however only `count`\n    /// draws will be read. If `count` is greater than `max_count`, `max_count` will be used.\n    ///\n    /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).\n    /// These draw structures are expected to be tightly packed.\n    ///\n    /// The structure expected in `count_buffer` is the following:\n    ///\n    /// ```rust\n    /// #[repr(C)]\n    /// struct DrawIndirectCount {\n    ///     count: u32, // Number of draw calls to issue.\n    /// }\n    /// ```\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_indirect_count(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count_buffer: &Buffer,\n        count_offset: BufferAddress,\n        max_count: u32,\n    ) {\n        self.inner.multi_draw_indirect_count(\n            &indirect_buffer.inner,\n            indirect_offset,\n            &count_buffer.inner,\n            count_offset,\n            max_count,\n        );\n    }\n\n    /// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,\n    /// based on the contents of the `indirect_buffer`. The count buffer is read to determine how many draws to issue.\n    ///\n    /// The indirect buffer must be long enough to account for `max_count` draws, however only `count`\n    /// draws will be read. If `count` is greater than `max_count`, `max_count` will be used.\n    ///\n    /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active\n    /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).\n    ///\n    /// These draw structures are expected to be tightly packed.\n    ///\n    /// The structure expected in `count_buffer` is the following:\n    ///\n    /// ```rust\n    /// #[repr(C)]\n    /// struct DrawIndexedIndirectCount {\n    ///     count: u32, // Number of draw calls to issue.\n    /// }\n    /// ```\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_indexed_indirect_count(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count_buffer: &Buffer,\n        count_offset: BufferAddress,\n        max_count: u32,\n    ) {\n        self.inner.multi_draw_indexed_indirect_count(\n            &indirect_buffer.inner,\n            indirect_offset,\n            &count_buffer.inner,\n            count_offset,\n            max_count,\n        );\n    }\n\n    /// Dispatches multiple draw calls based on the contents of the `indirect_buffer`. The count buffer is read to determine how many draws to issue.\n    ///\n    /// The indirect buffer must be long enough to account for `max_count` draws, however only `count`\n    /// draws will be read. If `count` is greater than `max_count`, `max_count` will be used.\n    ///\n    /// The structure expected in the `indirect_buffer` must conform to [`DispatchIndirectArgs`](crate::util::DispatchIndirectArgs).\n    ///\n    /// These draw structures are expected to be tightly packed.\n    ///\n    /// This drawing command uses the current render state, as set by preceding `set_*()` methods.\n    /// It is not affected by changes to the state that are performed after it is called.\n    pub fn multi_draw_mesh_tasks_indirect_count(\n        &mut self,\n        indirect_buffer: &Buffer,\n        indirect_offset: BufferAddress,\n        count_buffer: &Buffer,\n        count_offset: BufferAddress,\n        max_count: u32,\n    ) {\n        self.inner.multi_draw_mesh_tasks_indirect_count(\n            &indirect_buffer.inner,\n            indirect_offset,\n            &count_buffer.inner,\n            count_offset,\n            max_count,\n        );\n    }\n}\n\n/// [`Features::IMMEDIATES`] must be enabled on the device in order to call these functions.\nimpl RenderPass<'_> {\n    /// Set immediate data for subsequent draw calls.\n    ///\n    /// Write the bytes in `data` at offset `offset` within immediate data\n    /// storage. Both `offset` and the length of `data` must be\n    /// multiples of [`crate::IMMEDIATE_DATA_ALIGNMENT`], which is always 4.\n    ///\n    /// For example, if `offset` is `4` and `data` is eight bytes long, this\n    /// call will write `data` to bytes `4..12` of immediate data storage.\n    pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        self.inner.set_immediates(offset, data);\n    }\n}\n\n/// [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`] must be enabled on the device in order to call these functions.\nimpl RenderPass<'_> {\n    /// Issue a timestamp command at this point in the queue. The\n    /// timestamp will be written to the specified query set, at the specified index.\n    ///\n    /// Must be multiplied by [`Queue::get_timestamp_period`] to get\n    /// the value in nanoseconds. Absolute values have no meaning,\n    /// but timestamps can be subtracted to get the time it takes\n    /// for a string of operations to complete.\n    pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {\n        self.inner.write_timestamp(&query_set.inner, query_index);\n    }\n}\n\nimpl RenderPass<'_> {\n    /// Start a occlusion query on this render pass. It can be ended with\n    /// [`end_occlusion_query`](Self::end_occlusion_query).\n    /// Occlusion queries may not be nested.\n    pub fn begin_occlusion_query(&mut self, query_index: u32) {\n        self.inner.begin_occlusion_query(query_index);\n    }\n\n    /// End the occlusion query on this render pass. It can be started with\n    /// [`begin_occlusion_query`](Self::begin_occlusion_query).\n    /// Occlusion queries may not be nested.\n    pub fn end_occlusion_query(&mut self) {\n        self.inner.end_occlusion_query();\n    }\n}\n\n/// [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.\nimpl RenderPass<'_> {\n    /// Start a pipeline statistics query on this render pass. It can be ended with\n    /// [`end_pipeline_statistics_query`](Self::end_pipeline_statistics_query).\n    /// Pipeline statistics queries may not be nested.\n    ///\n    /// The amount of information collected by this query, and the space occupied in the query set,\n    /// is determined by the [`PipelineStatisticsTypes`] the query set was created with.\n    /// `query_index` is the index of the first query result slot that will be written to, and\n    /// `query_set` must have sufficient size to hold all results written starting at that slot.\n    pub fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, query_index: u32) {\n        self.inner\n            .begin_pipeline_statistics_query(&query_set.inner, query_index);\n    }\n\n    /// End the pipeline statistics query on this render pass. It can be started with\n    /// [`begin_pipeline_statistics_query`](Self::begin_pipeline_statistics_query).\n    /// Pipeline statistics queries may not be nested.\n    pub fn end_pipeline_statistics_query(&mut self) {\n        self.inner.end_pipeline_statistics_query();\n    }\n}\n\n/// Describes the timestamp writes of a render pass.\n///\n/// For use with [`RenderPassDescriptor`].\n/// At least one of [`Self::beginning_of_pass_write_index`] and [`Self::end_of_pass_write_index`]\n/// must be `Some`.\n///\n/// Corresponds to [WebGPU `GPURenderPassTimestampWrite`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderpasstimestampwrites).\n#[derive(Clone, Debug)]\npub struct RenderPassTimestampWrites<'a> {\n    /// The query set to write to.\n    pub query_set: &'a QuerySet,\n    /// The index of the query set at which a start timestamp of this pass is written, if any.\n    pub beginning_of_pass_write_index: Option<u32>,\n    /// The index of the query set at which an end timestamp of this pass is written, if any.\n    pub end_of_pass_write_index: Option<u32>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPassTimestampWrites<'_>: Send, Sync);\n\n/// Describes a color attachment to a [`RenderPass`].\n///\n/// For use with [`RenderPassDescriptor`].\n///\n/// Corresponds to [WebGPU `GPURenderPassColorAttachment`](\n/// https://gpuweb.github.io/gpuweb/#color-attachments).\n#[derive(Clone, Debug)]\npub struct RenderPassColorAttachment<'tex> {\n    /// The view to use as an attachment.\n    pub view: &'tex TextureView,\n    /// The depth slice index of a 3D view. It must not be provided if the view is not 3D.\n    pub depth_slice: Option<u32>,\n    /// The view that will receive the resolved output if multisampling is used.\n    ///\n    /// If set, it is always written to, regardless of how [`Self::ops`] is configured.\n    pub resolve_target: Option<&'tex TextureView>,\n    /// What operations will be performed on this color attachment.\n    pub ops: Operations<Color>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPassColorAttachment<'_>: Send, Sync);\n\n/// Describes a depth/stencil attachment to a [`RenderPass`].\n///\n/// For use with [`RenderPassDescriptor`].\n///\n/// Corresponds to [WebGPU `GPURenderPassDepthStencilAttachment`](\n/// https://gpuweb.github.io/gpuweb/#depth-stencil-attachments).\n#[derive(Clone, Debug)]\npub struct RenderPassDepthStencilAttachment<'tex> {\n    /// The view to use as an attachment.\n    pub view: &'tex TextureView,\n    /// What operations will be performed on the depth part of the attachment.\n    pub depth_ops: Option<Operations<f32>>,\n    /// What operations will be performed on the stencil part of the attachment.\n    pub stencil_ops: Option<Operations<u32>>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPassDepthStencilAttachment<'_>: Send, Sync);\n\n/// Describes the attachments of a render pass.\n///\n/// For use with [`CommandEncoder::begin_render_pass`].\n///\n/// Corresponds to [WebGPU `GPURenderPassDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderpassdescriptor).\n#[derive(Clone, Debug, Default)]\npub struct RenderPassDescriptor<'a> {\n    /// Debug label of the render pass. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The color attachments of the render pass.\n    pub color_attachments: &'a [Option<RenderPassColorAttachment<'a>>],\n    /// The depth and stencil attachment of the render pass, if any.\n    pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<'a>>,\n    /// Defines which timestamp values will be written for this pass, and where to write them to.\n    ///\n    /// Requires [`Features::TIMESTAMP_QUERY`] to be enabled.\n    pub timestamp_writes: Option<RenderPassTimestampWrites<'a>>,\n    /// Defines where the occlusion query results will be stored for this pass.\n    pub occlusion_query_set: Option<&'a QuerySet>,\n    /// The mask of multiview image layers to use for this render pass. For example, if you wish\n    /// to render to the first 2 layers, you would use 3=0b11. If you wanted ro render to only the\n    /// 2nd layer, you would use 2=0b10. If you aren't using multiview this should be `None`.\n    ///\n    /// Note that setting bits higher than the number of texture layers is a validation error.\n    ///\n    /// This doesn't influence load/store/clear/etc operations, as those are defined for attachments,\n    /// therefore affecting all attachments. Meaning, this affects only any shaders executed on the `RenderPass`.\n    pub multiview_mask: Option<NonZeroU32>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPassDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/render_pipeline.rs",
    "content": "use core::num::NonZeroU32;\n\nuse crate::*;\n\n/// Handle to a rendering (graphics) pipeline.\n///\n/// A `RenderPipeline` object represents a graphics pipeline and its stages, bindings, vertex\n/// buffers and targets. It can be created with [`Device::create_render_pipeline`].\n///\n/// Corresponds to [WebGPU `GPURenderPipeline`](https://gpuweb.github.io/gpuweb/#render-pipeline).\n#[derive(Debug, Clone)]\npub struct RenderPipeline {\n    pub(crate) inner: dispatch::DispatchRenderPipeline,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPipeline: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(RenderPipeline => .inner);\n\nimpl RenderPipeline {\n    /// Get an object representing the bind group layout at a given index.\n    ///\n    /// If this pipeline was created with a [default layout][RenderPipelineDescriptor::layout], then\n    /// bind groups created with the returned `BindGroupLayout` can only be used with this pipeline.\n    ///\n    /// This method will raise a validation error if there is no bind group layout at `index`.\n    pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout {\n        let layout = self.inner.get_bind_group_layout(index);\n        BindGroupLayout { inner: layout }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of RenderPipeline (if custom backend and is internally T)\n    pub fn as_custom<T: custom::RenderPipelineInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Specifies an interpretation of the bytes of a vertex buffer as vertex attributes.\n///\n/// Use this in a [`RenderPipelineDescriptor`] to describe the format of the vertex buffers that\n/// are passed to [`RenderPass::set_vertex_buffer()`].\n///\n/// Corresponds to [WebGPU `GPUVertexBufferLayout`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuvertexbufferlayout).\n///\n/// # Example\n///\n/// The following example defines a `struct` with three fields,\n/// and a [`VertexBufferLayout`] that contains [`VertexAttribute`]s for each field,\n/// using the [`vertex_attr_array!`] macro to compute attribute offsets:\n///\n/// ```\n/// #[repr(C, packed)]\n/// struct Vertex {\n///     foo: [f32; 2],\n///     bar: f32,\n///     baz: [u16; 4],\n/// }\n///\n/// impl Vertex {\n///     /// Layout to use with a buffer whose contents are a `[Vertex]`.\n///     pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {\n///         array_stride: size_of::<Self>() as wgpu::BufferAddress,\n///         step_mode: wgpu::VertexStepMode::Vertex,\n///         attributes: &wgpu::vertex_attr_array![\n///             0 => Float32x2,\n///             1 => Float32,\n///             2 => Uint16x4,\n///         ],\n///     };\n/// }\n///\n/// # assert_eq!(Vertex::LAYOUT.attributes[2].offset, Vertex::LAYOUT.array_stride - 2 * 4);\n#[derive(Clone, Debug, Hash, Eq, PartialEq)]\npub struct VertexBufferLayout<'a> {\n    /// The stride, in bytes, between elements of this buffer (between vertices).\n    ///\n    /// This must be a multiple of [`VERTEX_ALIGNMENT`].\n    pub array_stride: BufferAddress,\n    /// How often this vertex buffer is \"stepped\" forward.\n    pub step_mode: VertexStepMode,\n    /// The list of attributes which comprise a single vertex.\n    pub attributes: &'a [VertexAttribute],\n}\nstatic_assertions::assert_impl_all!(VertexBufferLayout<'_>: Send, Sync);\n\n/// Describes the vertex processing in a render pipeline.\n///\n/// For use in [`RenderPipelineDescriptor`].\n///\n/// Corresponds to [WebGPU `GPUVertexState`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpuvertexstate).\n#[derive(Clone, Debug)]\npub struct VertexState<'a> {\n    /// The compiled shader module for this stage.\n    pub module: &'a ShaderModule,\n    /// The name of the entry point in the compiled shader to use.\n    ///\n    /// If [`Some`], there must be a vertex-stage shader entry point with this name in `module`.\n    /// Otherwise, expect exactly one vertex-stage entry point in `module`, which will be\n    /// selected.\n    // NOTE: keep phrasing in sync. with `ComputePipelineDescriptor::entry_point`\n    // NOTE: keep phrasing in sync. with `FragmentState::entry_point`\n    pub entry_point: Option<&'a str>,\n    /// Advanced options for when this pipeline is compiled\n    ///\n    /// This implements `Default`, and for most users can be set to `Default::default()`\n    pub compilation_options: PipelineCompilationOptions<'a>,\n    /// The format of any vertex buffers used with this pipeline via\n    /// [`RenderPass::set_vertex_buffer()`].\n    ///\n    /// The attribute locations and types specified in this layout must match the\n    /// locations and types of the inputs to the `entry_point` function.\n    pub buffers: &'a [VertexBufferLayout<'a>],\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(VertexState<'_>: Send, Sync);\n\n/// Describes the fragment processing in a render pipeline.\n///\n/// For use in [`RenderPipelineDescriptor`].\n///\n/// Corresponds to [WebGPU `GPUFragmentState`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpufragmentstate).\n#[derive(Clone, Debug)]\npub struct FragmentState<'a> {\n    /// The compiled shader module for this stage.\n    pub module: &'a ShaderModule,\n    /// The name of the entry point in the compiled shader to use.\n    ///\n    /// If [`Some`], there must be a `@fragment` shader entry point with this name in `module`.\n    /// Otherwise, expect exactly one fragment-stage entry point in `module`, which will be\n    /// selected.\n    // NOTE: keep phrasing in sync. with `ComputePipelineDescriptor::entry_point`\n    // NOTE: keep phrasing in sync. with `VertexState::entry_point`\n    pub entry_point: Option<&'a str>,\n    /// Advanced options for when this pipeline is compiled\n    ///\n    /// This implements `Default`, and for most users can be set to `Default::default()`\n    pub compilation_options: PipelineCompilationOptions<'a>,\n    /// The color state of the render targets.\n    pub targets: &'a [Option<ColorTargetState>],\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(FragmentState<'_>: Send, Sync);\n\n/// Describes the task shader stage in a mesh shader pipeline.\n///\n/// For use in [`MeshPipelineDescriptor`]\n#[derive(Clone, Debug)]\npub struct TaskState<'a> {\n    /// The compiled shader module for this stage.\n    pub module: &'a ShaderModule,\n\n    /// The name of the task shader entry point in the shader module to use.\n    ///\n    /// If [`Some`], there must be a task shader entry point with the given name\n    /// in `module`. Otherwise, there must be exactly one task shader entry\n    /// point in `module`, which will be selected.\n    pub entry_point: Option<&'a str>,\n\n    /// Advanced options for when this pipeline is compiled.\n    ///\n    /// This implements `Default`, and for most users can be set to `Default::default()`\n    pub compilation_options: PipelineCompilationOptions<'a>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(TaskState<'_>: Send, Sync);\n\n/// Describes the mesh shader stage in a mesh shader pipeline.\n///\n/// For use in [`MeshPipelineDescriptor`]\n#[derive(Clone, Debug)]\npub struct MeshState<'a> {\n    /// The compiled shader module for this stage.\n    pub module: &'a ShaderModule,\n    /// The name of the entry point in the compiled shader to use.\n    ///\n    /// If [`Some`], there must be a vertex-stage shader entry point with this name in `module`.\n    /// Otherwise, expect exactly one vertex-stage entry point in `module`, which will be\n    /// selected.\n    pub entry_point: Option<&'a str>,\n    /// Advanced options for when this pipeline is compiled\n    ///\n    /// This implements `Default`, and for most users can be set to `Default::default()`\n    pub compilation_options: PipelineCompilationOptions<'a>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(MeshState<'_>: Send, Sync);\n\n/// Describes a render (graphics) pipeline.\n///\n/// For use with [`Device::create_render_pipeline`].\n///\n/// Corresponds to [WebGPU `GPURenderPipelineDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderpipelinedescriptor).\n#[derive(Clone, Debug)]\npub struct RenderPipelineDescriptor<'a> {\n    /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    ///\n    /// If this is set, then [`Device::create_render_pipeline`] will raise a validation error if\n    /// the layout doesn't match what the shader module(s) expect.\n    ///\n    /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`]\n    /// pipelines guarantees that you don't have to rebind any resources when switching between\n    /// those pipelines.\n    ///\n    /// ## Default pipeline layout\n    ///\n    /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead.\n    /// The default layout is deduced from the shader modules.\n    ///\n    /// You can use [`RenderPipeline::get_bind_group_layout`] to create bind groups for use with the\n    /// default layout. However, these bind groups cannot be used with any other pipelines. This is\n    /// convenient for simple pipelines, but using an explicit layout is recommended in most cases.\n    ///\n    /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout\n    pub layout: Option<&'a PipelineLayout>,\n    /// The compiled vertex stage, its entry point, and the input buffers layout.\n    pub vertex: VertexState<'a>,\n    /// The properties of the pipeline at the primitive assembly and rasterization level.\n    pub primitive: PrimitiveState,\n    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.\n    pub depth_stencil: Option<DepthStencilState>,\n    /// The multi-sampling properties of the pipeline.\n    pub multisample: MultisampleState,\n    /// The compiled fragment stage, its entry point, and the color targets.\n    pub fragment: Option<FragmentState<'a>>,\n    /// If the pipeline will be used with a multiview render pass, this indicates what multiview\n    /// mask the render pass will be used with. The masks must match exactly.\n    ///\n    /// For example, if you wish to render to the first 2 layers, you would use 3=0b11. If you\n    /// wanted to render to only the 2nd layer, you would use 2=0b10. If you aren't using\n    /// multiview this should be `None`.\n    pub multiview_mask: Option<NonZeroU32>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<&'a PipelineCache>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(RenderPipelineDescriptor<'_>: Send, Sync);\n\n/// Describes a mesh shader (graphics) pipeline.\n///\n/// For use with [`Device::create_mesh_pipeline`]. A mesh pipeline is very much\n/// like a render pipeline, except that instead of [`RenderPass::draw`] it is\n/// invoked with [`RenderPass::draw_mesh_tasks`], and instead of a vertex shader\n/// and a fragment shader:\n///\n/// - [`task`] specifies an optional task shader entry point, which determines how\n///   many groups of mesh shaders to dispatch.\n///\n/// - [`mesh`] specifies a mesh shader entry point, which generates groups of\n///   primitives to draw\n///\n/// - [`fragment`] specifies as fragment shader for drawing those primitives,\n///   just like in an ordinary render pipeline.\n///\n/// The key difference is that, whereas a vertex shader is invoked on the\n/// elements of vertex buffers, the task shader gets to decide how many mesh\n/// shader workgroups to make, and then each mesh shader workgroup gets to\n/// decide which primitives it wants to generate, and what their vertex\n/// attributes are. Task and mesh shaders can use whatever they please as\n/// inputs, like a compute shader. However, they cannot use specialized vertex\n/// or index buffers.\n///\n/// A mesh pipeline is invoked by [`RenderPass::draw_mesh_tasks`], which looks\n/// like a compute shader dispatch with [`ComputePass::dispatch_workgroups`]:\n/// you pass `x`, `y`, and `z` values indicating the number of task shaders to\n/// invoke in parallel. The output value of the first thread in a task shader\n/// workgroup determines how many mesh workgroups should be dispatched from there.\n/// Those mesh workgroups also get a special payload passed from the task shader.\n///\n/// If the task shader is omitted, then the (`x`, `y`, `z`) parameters to\n/// `draw_mesh_tasks` are used to decide how many invocations of the mesh shader\n/// to invoke directly, without a task payload.\n///\n/// [vertex formats]: wgpu_types::VertexFormat\n/// [`task`]: Self::task\n/// [`mesh`]: Self::mesh\n/// [`fragment`]: Self::fragment\n#[derive(Clone, Debug)]\npub struct MeshPipelineDescriptor<'a> {\n    /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    ///\n    /// If this is set, then [`Device::create_render_pipeline`] will raise a validation error if\n    /// the layout doesn't match what the shader module(s) expect.\n    ///\n    /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`]\n    /// pipelines guarantees that you don't have to rebind any resources when switching between\n    /// those pipelines.\n    ///\n    /// ## Default pipeline layout\n    ///\n    /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead.\n    /// The default layout is deduced from the shader modules.\n    ///\n    /// You can use [`RenderPipeline::get_bind_group_layout`] to create bind groups for use with the\n    /// default layout. However, these bind groups cannot be used with any other pipelines. This is\n    /// convenient for simple pipelines, but using an explicit layout is recommended in most cases.\n    ///\n    /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout\n    pub layout: Option<&'a PipelineLayout>,\n\n    /// The mesh pipeline's task shader.\n    ///\n    /// If this is `None`, the mesh pipeline has no task shader. Executing a\n    /// mesh drawing command simply dispatches a grid of mesh shaders directly.\n    ///\n    /// [`draw_mesh_tasks`]: RenderPass::draw_mesh_tasks\n    pub task: Option<TaskState<'a>>,\n\n    /// The compiled mesh stage and its entry point\n    pub mesh: MeshState<'a>,\n    /// The properties of the pipeline at the primitive assembly and rasterization level.\n    pub primitive: PrimitiveState,\n    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.\n    pub depth_stencil: Option<DepthStencilState>,\n    /// The multi-sampling properties of the pipeline.\n    pub multisample: MultisampleState,\n    /// The compiled fragment stage, its entry point, and the color targets.\n    pub fragment: Option<FragmentState<'a>>,\n    /// If the pipeline will be used with a multiview render pass, this indicates how many array\n    /// layers the attachments will have.\n    pub multiview: Option<NonZeroU32>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<&'a PipelineCache>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(MeshPipelineDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/sampler.rs",
    "content": "use crate::*;\n\n/// Handle to a sampler.\n///\n/// A `Sampler` object defines how a pipeline will sample from a [`TextureView`]. Samplers define\n/// image filters (including anisotropy) and address (wrapping) modes, among other things. See\n/// the documentation for [`SamplerDescriptor`] for more information.\n///\n/// It can be created with [`Device::create_sampler`].\n///\n/// Corresponds to [WebGPU `GPUSampler`](https://gpuweb.github.io/gpuweb/#sampler-interface).\n#[derive(Debug, Clone)]\npub struct Sampler {\n    pub(crate) inner: dispatch::DispatchSampler,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Sampler: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Sampler => .inner);\n\nimpl Sampler {\n    #[cfg(custom)]\n    /// Returns custom implementation of Sampler (if custom backend and is internally T)\n    pub fn as_custom<T: custom::SamplerInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`Sampler`].\n///\n/// For use with [`Device::create_sampler`].\n///\n/// Corresponds to [WebGPU `GPUSamplerDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpusamplerdescriptor).\npub type SamplerDescriptor<'a> = wgt::SamplerDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(SamplerDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/shader_module.rs",
    "content": "use alloc::{string::String, vec::Vec};\nuse core::{future::Future, marker::PhantomData};\n\nuse crate::*;\n\n/// Handle to a compiled shader module.\n///\n/// A `ShaderModule` represents a compiled shader module on the GPU. It can be created by passing\n/// source code to [`Device::create_shader_module`]. MSL shader or SPIR-V binary can also be passed\n/// directly using [`Device::create_shader_module_passthrough`]. Shader modules are used to define\n/// programmable stages of a pipeline.\n///\n/// Corresponds to [WebGPU `GPUShaderModule`](https://gpuweb.github.io/gpuweb/#shader-module).\n#[derive(Debug, Clone)]\npub struct ShaderModule {\n    pub(crate) inner: dispatch::DispatchShaderModule,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(ShaderModule: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(ShaderModule => .inner);\n\nimpl ShaderModule {\n    /// Get the compilation info for the shader module.\n    pub fn get_compilation_info(&self) -> impl Future<Output = CompilationInfo> + WasmNotSend {\n        self.inner.get_compilation_info()\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of ShaderModule (if custom backend and is internally T)\n    pub fn as_custom<T: custom::ShaderModuleInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Compilation information for a shader module.\n///\n/// Corresponds to [WebGPU `GPUCompilationInfo`](https://gpuweb.github.io/gpuweb/#gpucompilationinfo).\n/// The source locations use bytes, and index a UTF-8 encoded string.\n#[derive(Debug, Clone)]\npub struct CompilationInfo {\n    /// The messages from the shader compilation process.\n    pub messages: Vec<CompilationMessage>,\n}\n\n/// A single message from the shader compilation process.\n///\n/// Roughly corresponds to [`GPUCompilationMessage`](https://www.w3.org/TR/webgpu/#gpucompilationmessage),\n/// except that the location uses UTF-8 for all positions.\n#[derive(Debug, Clone)]\npub struct CompilationMessage {\n    /// The text of the message.\n    pub message: String,\n    /// The type of the message.\n    pub message_type: CompilationMessageType,\n    /// Where in the source code the message points at.\n    pub location: Option<SourceLocation>,\n}\n\n/// The type of a compilation message.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum CompilationMessageType {\n    /// An error message.\n    Error,\n    /// A warning message.\n    Warning,\n    /// An informational message.\n    Info,\n}\n\n/// A human-readable representation for a span, tailored for text source.\n///\n/// Roughly corresponds to the positional members of [`GPUCompilationMessage`][gcm] from\n/// the WebGPU specification, except\n/// - `offset` and `length` are in bytes (UTF-8 code units), instead of UTF-16 code units.\n/// - `line_position` is in bytes (UTF-8 code units), and is usually not directly intended for humans.\n///\n/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct SourceLocation {\n    /// 1-based line number.\n    pub line_number: u32,\n    /// 1-based column in code units (in bytes) of the start of the span.\n    /// Remember to convert accordingly when displaying to the user.\n    pub line_position: u32,\n    /// 0-based Offset in code units (in bytes) of the start of the span.\n    pub offset: u32,\n    /// Length in code units (in bytes) of the span.\n    pub length: u32,\n}\n\n#[cfg(all(feature = \"wgsl\", wgpu_core))]\nimpl From<crate::naga::error::ShaderError<crate::naga::front::wgsl::ParseError>>\n    for CompilationInfo\n{\n    fn from(value: crate::naga::error::ShaderError<crate::naga::front::wgsl::ParseError>) -> Self {\n        use alloc::{string::ToString, vec};\n        CompilationInfo {\n            messages: vec![CompilationMessage {\n                message: value.to_string(),\n                message_type: CompilationMessageType::Error,\n                location: value.inner.location(&value.source).map(Into::into),\n            }],\n        }\n    }\n}\n#[cfg(feature = \"glsl\")]\nimpl From<naga::error::ShaderError<naga::front::glsl::ParseErrors>> for CompilationInfo {\n    fn from(value: naga::error::ShaderError<naga::front::glsl::ParseErrors>) -> Self {\n        use alloc::string::ToString;\n        let messages = value\n            .inner\n            .errors\n            .into_iter()\n            .map(|err| CompilationMessage {\n                message: err.to_string(),\n                message_type: CompilationMessageType::Error,\n                location: err.location(&value.source).map(Into::into),\n            })\n            .collect();\n        CompilationInfo { messages }\n    }\n}\n\n#[cfg(feature = \"spirv\")]\nimpl From<naga::error::ShaderError<naga::front::spv::Error>> for CompilationInfo {\n    fn from(value: naga::error::ShaderError<naga::front::spv::Error>) -> Self {\n        use alloc::{string::ToString, vec};\n        CompilationInfo {\n            messages: vec![CompilationMessage {\n                message: value.to_string(),\n                message_type: CompilationMessageType::Error,\n                location: None,\n            }],\n        }\n    }\n}\n\n#[cfg(any(wgpu_core, naga))]\nimpl\n    From<\n        crate::naga::error::ShaderError<crate::naga::WithSpan<crate::naga::valid::ValidationError>>,\n    > for CompilationInfo\n{\n    fn from(\n        value: crate::naga::error::ShaderError<\n            crate::naga::WithSpan<crate::naga::valid::ValidationError>,\n        >,\n    ) -> Self {\n        use alloc::{string::ToString, vec};\n        CompilationInfo {\n            messages: vec![CompilationMessage {\n                message: value.to_string(),\n                message_type: CompilationMessageType::Error,\n                location: value.inner.location(&value.source).map(Into::into),\n            }],\n        }\n    }\n}\n\n#[cfg(any(wgpu_core, naga))]\nimpl From<crate::naga::SourceLocation> for SourceLocation {\n    fn from(value: crate::naga::SourceLocation) -> Self {\n        SourceLocation {\n            length: value.length,\n            offset: value.offset,\n            line_number: value.line_number,\n            line_position: value.line_position,\n        }\n    }\n}\n\n/// Source of a shader module.\n///\n/// The source will be parsed and validated.\n///\n/// Any necessary shader translation (e.g. from WGSL to SPIR-V or vice versa)\n/// will be done internally by wgpu.\n///\n/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification,\n/// only WGSL source code strings are accepted.\n#[cfg_attr(feature = \"naga-ir\", expect(clippy::large_enum_variant))]\n#[derive(Clone, Debug)]\n#[non_exhaustive]\npub enum ShaderSource<'a> {\n    /// SPIR-V module represented as a slice of words.\n    ///\n    /// See also: [`util::make_spirv`], [`include_spirv`]\n    #[cfg(feature = \"spirv\")]\n    SpirV(alloc::borrow::Cow<'a, [u32]>),\n    /// GLSL module as a string slice.\n    ///\n    /// Note: GLSL is not yet fully supported and must be a specific ShaderStage.\n    #[cfg(feature = \"glsl\")]\n    Glsl {\n        /// The source code of the shader.\n        shader: alloc::borrow::Cow<'a, str>,\n        /// The shader stage that the shader targets. For example, `naga::ShaderStage::Vertex`\n        stage: naga::ShaderStage,\n        /// Key-value pairs to represent defines sent to the glsl preprocessor.\n        ///\n        /// If the same name is defined multiple times, the last value is used.\n        defines: &'a [(&'a str, &'a str)],\n    },\n    /// WGSL module as a string slice.\n    #[cfg(feature = \"wgsl\")]\n    Wgsl(alloc::borrow::Cow<'a, str>),\n    /// Naga module.\n    #[cfg(feature = \"naga-ir\")]\n    Naga(alloc::borrow::Cow<'static, naga::Module>),\n    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it\n    /// could be the last one active.\n    #[doc(hidden)]\n    Dummy(PhantomData<&'a ()>),\n}\nstatic_assertions::assert_impl_all!(ShaderSource<'_>: Send, Sync);\n\n/// Descriptor for use with [`Device::create_shader_module`].\n///\n/// Corresponds to [WebGPU `GPUShaderModuleDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gpushadermoduledescriptor).\n#[derive(Clone, Debug)]\npub struct ShaderModuleDescriptor<'a> {\n    /// Debug label of the shader module. This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Source code for the shader.\n    pub source: ShaderSource<'a>,\n}\nstatic_assertions::assert_impl_all!(ShaderModuleDescriptor<'_>: Send, Sync);\n\n/// Descriptor for a shader module given by any of several sources.\n/// At least one of the shader types that may be used by the backend must be `Some`\n///\n/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification,\n/// only WGSL source code strings are accepted.\npub type ShaderModuleDescriptorPassthrough<'a> =\n    wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;\n"
  },
  {
    "path": "wgpu/src/api/surface.rs",
    "content": "use alloc::{boxed::Box, string::String, vec, vec::Vec};\n#[cfg(wgpu_core)]\nuse core::ops::Deref;\nuse core::{error, fmt};\n\nuse raw_window_handle::{HasDisplayHandle, HasWindowHandle};\n\nuse crate::util::Mutex;\nuse crate::*;\n\n/// Describes a [`Surface`].\n///\n/// For use with [`Surface::configure`].\n///\n/// Corresponds to [WebGPU `GPUCanvasConfiguration`](\n/// https://gpuweb.github.io/gpuweb/#canvas-configuration).\npub type SurfaceConfiguration = wgt::SurfaceConfiguration<Vec<TextureFormat>>;\nstatic_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);\n\n/// Handle to a presentable surface.\n///\n/// A `Surface` represents a platform-specific surface (e.g. a window) onto which rendered images may\n/// be presented. A `Surface` may be created with the function [`Instance::create_surface`].\n///\n/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification,\n/// [`GPUCanvasContext`](https://gpuweb.github.io/gpuweb/#canvas-context)\n/// serves a similar role.\npub struct Surface<'window> {\n    /// Additional surface data returned by [`InstanceInterface::create_surface`][cs].\n    ///\n    /// [cs]: crate::dispatch::InstanceInterface::create_surface\n    pub(crate) inner: dispatch::DispatchSurface,\n\n    // Stores the latest `SurfaceConfiguration` that was set using `Surface::configure`.\n    // It is required to set the attributes of the `SurfaceTexture` in the\n    // `Surface::get_current_texture` method.\n    // Because the `Surface::configure` method operates on an immutable reference this type has to\n    // be wrapped in a mutex and since the configuration is only supplied after the surface has\n    // been created is is additionally wrapped in an option.\n    pub(crate) config: Mutex<Option<SurfaceConfiguration>>,\n\n    /// Optionally, keep the source of the handle used for the surface alive.\n    ///\n    /// This is useful for platforms where the surface is created from a window and the surface\n    /// would become invalid when the window is dropped.\n    ///\n    /// SAFETY: This field must be dropped *after* all other fields to ensure proper cleanup.\n    pub(crate) _handle_source: Option<Box<dyn WindowHandle + 'window>>,\n}\n\nimpl Surface<'_> {\n    /// Returns the capabilities of the surface when used with the given adapter.\n    ///\n    /// Returns specified values (see [`SurfaceCapabilities`]) if surface is incompatible with the adapter.\n    pub fn get_capabilities(&self, adapter: &Adapter) -> SurfaceCapabilities {\n        self.inner.get_capabilities(&adapter.inner)\n    }\n\n    /// Return a default `SurfaceConfiguration` from width and height to use for the [`Surface`] with this adapter.\n    ///\n    /// Returns None if the surface isn't supported by this adapter\n    pub fn get_default_config(\n        &self,\n        adapter: &Adapter,\n        width: u32,\n        height: u32,\n    ) -> Option<SurfaceConfiguration> {\n        let caps = self.get_capabilities(adapter);\n        Some(SurfaceConfiguration {\n            usage: wgt::TextureUsages::RENDER_ATTACHMENT,\n            format: *caps.formats.first()?,\n            width,\n            height,\n            desired_maximum_frame_latency: 2,\n            present_mode: *caps.present_modes.first()?,\n            alpha_mode: wgt::CompositeAlphaMode::Auto,\n            view_formats: vec![],\n        })\n    }\n\n    /// Initializes [`Surface`] for presentation.\n    ///\n    /// If the surface is already configured, this will wait for the GPU to come idle\n    /// before recreating the swapchain to prevent race conditions.\n    ///\n    /// # Validation Errors\n    /// - Submissions that happen _during_ the configure may cause the\n    ///   internal wait-for-idle to fail, raising a validation error.\n    ///\n    /// # Panics\n    ///\n    /// - A old [`SurfaceTexture`] is still alive referencing an old surface.\n    /// - Texture format requested is unsupported on the surface.\n    /// - `config.width` or `config.height` is zero.\n    pub fn configure(&self, device: &Device, config: &SurfaceConfiguration) {\n        self.inner.configure(&device.inner, config);\n\n        let mut conf = self.config.lock();\n        *conf = Some(config.clone());\n    }\n\n    /// Returns the current configuration of [`Surface`], if configured.\n    ///\n    /// This is similar to [WebGPU `GPUcCanvasContext::getConfiguration`](https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getconfiguration).\n    pub fn get_configuration(&self) -> Option<SurfaceConfiguration> {\n        self.config.lock().clone()\n    }\n\n    /// Returns the next texture to be presented by the surface for drawing.\n    ///\n    /// In order to present the [`SurfaceTexture`] returned by this method,\n    /// first a [`Queue::submit`] needs to be done with some work rendering to this texture.\n    /// Then [`SurfaceTexture::present`] needs to be called.\n    ///\n    /// If a [`SurfaceTexture`] referencing this surface is alive when [`Surface::configure()`]\n    /// is called, the configure call will panic.\n    ///\n    /// See the documentation of [`CurrentSurfaceTexture`] for how each possible result\n    /// should be handled.\n    pub fn get_current_texture(&self) -> CurrentSurfaceTexture {\n        let (texture, status, detail) = self.inner.get_current_texture();\n\n        let suboptimal = match status {\n            SurfaceStatus::Good => false,\n            SurfaceStatus::Suboptimal => true,\n            SurfaceStatus::Timeout => return CurrentSurfaceTexture::Timeout,\n            SurfaceStatus::Occluded => return CurrentSurfaceTexture::Occluded,\n            SurfaceStatus::Outdated => return CurrentSurfaceTexture::Outdated,\n            SurfaceStatus::Lost => return CurrentSurfaceTexture::Lost,\n            SurfaceStatus::Validation => return CurrentSurfaceTexture::Validation,\n        };\n\n        let guard = self.config.lock();\n        let config = guard\n            .as_ref()\n            .expect(\"This surface has not been configured yet.\");\n\n        let descriptor = TextureDescriptor {\n            label: None,\n            size: Extent3d {\n                width: config.width,\n                height: config.height,\n                depth_or_array_layers: 1,\n            },\n            format: config.format,\n            usage: config.usage,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: TextureDimension::D2,\n            view_formats: &[],\n        };\n\n        match texture {\n            Some(texture) => {\n                let surface_texture = SurfaceTexture {\n                    texture: Texture {\n                        inner: texture,\n                        descriptor,\n                    },\n                    presented: false,\n                    detail,\n                };\n                if suboptimal {\n                    CurrentSurfaceTexture::Suboptimal(surface_texture)\n                } else {\n                    CurrentSurfaceTexture::Success(surface_texture)\n                }\n            }\n            None => CurrentSurfaceTexture::Lost,\n        }\n    }\n\n    /// Get the [`wgpu_hal`] surface from this `Surface`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Surface`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Surface\")]\n    #[doc = crate::macros::hal_type_metal!(\"Surface\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Surface\")]\n    #[doc = crate::macros::hal_type_gles!(\"Surface\")]\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The surface is not from the backend specified by `A`.\n    /// - The surface is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Surface`]: hal::Api::Surface\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &self,\n    ) -> Option<impl Deref<Target = A::Surface> + WasmNotSendSync> {\n        let core_surface = self.inner.as_core_opt()?;\n\n        unsafe { core_surface.context.surface_as_hal::<A>(core_surface) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Surface (if custom backend and is internally T)\n    pub fn as_custom<T: custom::SurfaceInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n// This custom implementation is required because [`Surface::_surface`] doesn't\n// require [`Debug`](fmt::Debug), which we should not require from the user.\nimpl fmt::Debug for Surface<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"Surface\")\n            .field(\n                \"_handle_source\",\n                &if self._handle_source.is_some() {\n                    \"Some\"\n                } else {\n                    \"None\"\n                },\n            )\n            .field(\"inner\", &self.inner)\n            .field(\"config\", &self.config)\n            .finish()\n    }\n}\n\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Surface<'_>: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Surface<'_> => .inner);\n\n/// [`Send`]/[`Sync`] blanket trait for [`HasWindowHandle`] used in [`SurfaceTarget`].\npub trait WindowHandle: HasWindowHandle + WasmNotSendSync {}\n\nimpl<T: HasWindowHandle + WasmNotSendSync> WindowHandle for T {}\n\n/// Super trait for a pair of display and window handles as used in [`SurfaceTarget`].\npub trait DisplayAndWindowHandle: WindowHandle + HasDisplayHandle {}\n\nimpl<T> DisplayAndWindowHandle for T where T: WindowHandle + HasDisplayHandle {}\n\n/// The window/canvas/surface/swap-chain/etc. a surface is attached to, for use with safe surface creation.\n///\n/// This is either a window or an actual web canvas depending on the platform and\n/// enabled features.\n/// Refer to the individual variants for more information.\n///\n/// See also [`SurfaceTargetUnsafe`] for unsafe variants.\n#[non_exhaustive]\npub enum SurfaceTarget<'window> {\n    /// Window and display handle producer.\n    ///\n    /// If the specified display and window handle are not supported by any of the backends, then the surface\n    /// will not be supported by any adapters.\n    ///\n    /// # Errors\n    ///\n    /// - On WebGL2: surface creation returns an error if the browser does not support WebGL2,\n    ///   or declines to provide GPU access (such as due to a resource shortage).\n    ///\n    /// # Panics\n    ///\n    /// - On macOS/Metal: will panic if not called on the main thread.\n    /// - On web: will panic if the [`HasWindowHandle`] does not properly refer to a\n    ///   canvas element.\n    /// - On all platforms: If [`crate::InstanceDescriptor::display`] was not [`None`]\n    ///   but its value is not identical to that returned by [`HasDisplayHandle::display_handle()`].\n    DisplayAndWindow(Box<dyn DisplayAndWindowHandle + 'window>),\n\n    /// Window handle producer.\n    ///\n    /// [`HasWindowHandle`]-only version of [`SurfaceTarget::DisplayAndWindow`].\n    ///\n    /// This requires that the display handle was already passed through\n    /// [`crate::InstanceDescriptor::display`].\n    Window(Box<dyn WindowHandle + 'window>),\n\n    /// Surface from a `web_sys::HtmlCanvasElement`.\n    ///\n    /// The `canvas` argument must be a valid `<canvas>` element to\n    /// create a surface upon.\n    ///\n    /// # Errors\n    ///\n    /// - On WebGL2: surface creation will return an error if the browser does not support WebGL2,\n    ///   or declines to provide GPU access (such as due to a resource shortage).\n    #[cfg(web)]\n    Canvas(web_sys::HtmlCanvasElement),\n\n    /// Surface from a `web_sys::OffscreenCanvas`.\n    ///\n    /// The `canvas` argument must be a valid `OffscreenCanvas` object\n    /// to create a surface upon.\n    ///\n    /// # Errors\n    ///\n    /// - On WebGL2: surface creation will return an error if the browser does not support WebGL2,\n    ///   or declines to provide GPU access (such as due to a resource shortage).\n    #[cfg(web)]\n    OffscreenCanvas(web_sys::OffscreenCanvas),\n}\n\nimpl<'a> SurfaceTarget<'a> {\n    /// Constructor for [`Self::Window`] without consuming a display handle\n    pub fn from_window_without_display(window: impl WindowHandle + 'a) -> Self {\n        Self::Window(Box::new(window))\n    }\n}\n\nimpl<'a, T> From<T> for SurfaceTarget<'a>\nwhere\n    T: DisplayAndWindowHandle + 'a,\n{\n    fn from(window: T) -> Self {\n        Self::DisplayAndWindow(Box::new(window))\n    }\n}\n\n/// The window/canvas/surface/swap-chain/etc. a surface is attached to, for use with unsafe surface creation.\n///\n/// This is either a window or an actual web canvas depending on the platform and\n/// enabled features.\n/// Refer to the individual variants for more information.\n///\n/// See also [`SurfaceTarget`] for safe variants.\n#[non_exhaustive]\npub enum SurfaceTargetUnsafe {\n    /// Raw window & display handle.\n    ///\n    /// If the specified display and window handle are not supported by any of the backends, then the surface\n    /// will not be supported by any adapters.\n    ///\n    /// If the `raw_display_handle` is not [`None`] here and was not [`None`] in\n    /// [`crate::InstanceDescriptor::display`], their values _must_ be identical.\n    ///\n    /// # Safety\n    ///\n    /// - `raw_window_handle` & `raw_display_handle` must be valid objects to create a surface upon.\n    /// - `raw_window_handle` & `raw_display_handle` must remain valid until after the returned\n    ///   [`Surface`] is  dropped.\n    RawHandle {\n        /// Raw display handle, underlying display must outlive the surface created from this.\n        raw_display_handle: Option<raw_window_handle::RawDisplayHandle>,\n\n        /// Raw window handle, underlying window must outlive the surface created from this.\n        raw_window_handle: raw_window_handle::RawWindowHandle,\n    },\n\n    /// Surface from a DRM device.\n    ///\n    /// If the specified DRM configuration is not supported by any of the backends, then the surface\n    /// will not be supported by any adapters.\n    ///\n    /// # Safety\n    ///\n    /// - All parameters must point to valid DRM values and remain valid for as long as the resulting [`Surface`] exists.\n    /// - The file descriptor (`fd`), plane, connector, and mode configuration must be valid and compatible.\n    #[cfg(all(unix, not(target_vendor = \"apple\"), not(target_family = \"wasm\")))]\n    Drm {\n        /// The file descriptor of the DRM device.\n        fd: i32,\n        /// The plane index on which to create the surface.\n        plane: u32,\n        /// The ID of the connector associated with the selected mode.\n        connector_id: u32,\n        /// The display width of the selected mode.\n        width: u32,\n        /// The display height of the selected mode.\n        height: u32,\n        /// The display refresh rate of the selected mode multiplied by 1000 (e.g., 60Hz → 60000).\n        refresh_rate: u32,\n    },\n\n    /// Surface from `CoreAnimationLayer`.\n    ///\n    /// # Safety\n    ///\n    /// - layer must be a valid object to create a surface upon.\n    #[cfg(metal)]\n    CoreAnimationLayer(*mut core::ffi::c_void),\n\n    /// Surface from `IDCompositionVisual`.\n    ///\n    /// # Safety\n    ///\n    /// - visual must be a valid `IDCompositionVisual` to create a surface upon.  Its refcount will be incremented internally and kept live as long as the resulting [`Surface`] is live.\n    #[cfg(dx12)]\n    CompositionVisual(*mut core::ffi::c_void),\n\n    /// Surface from DX12 `DirectComposition` handle.\n    ///\n    /// <https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_3/nf-dxgi1_3-idxgifactorymedia-createswapchainforcompositionsurfacehandle>\n    ///\n    /// # Safety\n    ///\n    /// - surface_handle must be a valid `DirectComposition` handle to create a surface upon.   Its lifetime **will not** be internally managed: this handle **should not** be freed before\n    ///   the resulting [`Surface`] is destroyed.\n    #[cfg(dx12)]\n    SurfaceHandle(*mut core::ffi::c_void),\n\n    /// Surface from DX12 `SwapChainPanel`.\n    ///\n    /// # Safety\n    ///\n    /// - visual must be a valid SwapChainPanel to create a surface upon.  Its refcount will be incremented internally and kept live as long as the resulting [`Surface`] is live.\n    #[cfg(dx12)]\n    SwapChainPanel(*mut core::ffi::c_void),\n}\n\nimpl SurfaceTargetUnsafe {\n    /// Creates a [`SurfaceTargetUnsafe::RawHandle`] from a display and window.\n    ///\n    /// The `display` is optional and may be omitted if it was also passed to\n    /// [`crate::InstanceDescriptor::display`].  If passed to both it must (currently) be identical.\n    ///\n    /// # Safety\n    ///\n    /// - `display` must outlive the resulting surface target\n    ///   (and subsequently the surface created for this target).\n    /// - `window` must outlive the resulting surface target\n    ///   (and subsequently the surface created for this target).\n    pub unsafe fn from_display_and_window(\n        display: &impl HasDisplayHandle,\n        window: &impl HasWindowHandle,\n    ) -> Result<Self, raw_window_handle::HandleError> {\n        Ok(Self::RawHandle {\n            raw_display_handle: Some(display.display_handle()?.as_raw()),\n            raw_window_handle: window.window_handle()?.as_raw(),\n        })\n    }\n\n    /// Creates a [`SurfaceTargetUnsafe::RawHandle`] from a window.\n    ///\n    /// # Safety\n    ///\n    /// - `window` must outlive the resulting surface target\n    ///   (and subsequently the surface created for this target).\n    pub unsafe fn from_window(\n        window: &impl HasWindowHandle,\n    ) -> Result<Self, raw_window_handle::HandleError> {\n        Ok(Self::RawHandle {\n            raw_display_handle: None,\n            raw_window_handle: window.window_handle()?.as_raw(),\n        })\n    }\n}\n\n/// [`Instance::create_surface()`] or a related function failed.\n#[derive(Clone, Debug)]\n#[non_exhaustive]\npub struct CreateSurfaceError {\n    pub(crate) inner: CreateSurfaceErrorKind,\n}\n#[derive(Clone, Debug)]\npub(crate) enum CreateSurfaceErrorKind {\n    /// Error from [`wgpu_hal`].\n    #[cfg(wgpu_core)]\n    Hal(wgc::instance::CreateSurfaceError),\n\n    /// Error from WebGPU surface creation.\n    #[cfg_attr(not(webgpu), expect(dead_code))]\n    Web(String),\n\n    /// Error when trying to get a [`RawDisplayHandle`][rdh] or a\n    /// [`RawWindowHandle`][rwh] from a [`SurfaceTarget`].\n    ///\n    /// [rdh]: raw_window_handle::RawDisplayHandle\n    /// [rwh]: raw_window_handle::RawWindowHandle\n    RawHandle(raw_window_handle::HandleError),\n}\nstatic_assertions::assert_impl_all!(CreateSurfaceError: Send, Sync);\n\nimpl fmt::Display for CreateSurfaceError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            #[cfg(wgpu_core)]\n            CreateSurfaceErrorKind::Hal(e) => e.fmt(f),\n            CreateSurfaceErrorKind::Web(e) => e.fmt(f),\n            CreateSurfaceErrorKind::RawHandle(e) => e.fmt(f),\n        }\n    }\n}\n\nimpl error::Error for CreateSurfaceError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match &self.inner {\n            #[cfg(wgpu_core)]\n            CreateSurfaceErrorKind::Hal(e) => e.source(),\n            CreateSurfaceErrorKind::Web(_) => None,\n            #[cfg(feature = \"std\")]\n            CreateSurfaceErrorKind::RawHandle(e) => e.source(),\n            #[cfg(not(feature = \"std\"))]\n            CreateSurfaceErrorKind::RawHandle(_) => None,\n        }\n    }\n}\n\n#[cfg(wgpu_core)]\nimpl From<wgc::instance::CreateSurfaceError> for CreateSurfaceError {\n    fn from(e: wgc::instance::CreateSurfaceError) -> Self {\n        Self {\n            inner: CreateSurfaceErrorKind::Hal(e),\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/surface_texture.rs",
    "content": "use crate::*;\n\n/// Surface texture that can be rendered to.\n/// Result of a successful call to [`Surface::get_current_texture`].\n///\n/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification,\n/// the [`GPUCanvasContext`](https://gpuweb.github.io/gpuweb/#canvas-context) provides\n/// a texture without any additional information.\n#[derive(Debug, Clone)]\npub struct SurfaceTexture {\n    /// Accessible view of the frame.\n    pub texture: Texture,\n    pub(crate) presented: bool,\n    pub(crate) detail: dispatch::DispatchSurfaceOutputDetail,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(SurfaceTexture: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(SurfaceTexture => .texture.inner);\n\nimpl SurfaceTexture {\n    /// Schedule this texture to be presented on the owning surface.\n    ///\n    /// Needs to be called after any work on the texture is scheduled via [`Queue::submit`].\n    ///\n    /// # Platform dependent behavior\n    ///\n    /// On Wayland, `present` will attach a `wl_buffer` to the underlying `wl_surface` and commit the new surface\n    /// state. If it is desired to do things such as request a frame callback, scale the surface using the viewporter\n    /// or synchronize other double buffered state, then these operations should be done before the call to `present`.\n    pub fn present(mut self) {\n        self.presented = true;\n        self.detail.present();\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of SurfaceTexture (if custom backend and is internally T)\n    pub fn as_custom<T: crate::custom::SurfaceOutputDetailInterface>(&self) -> Option<&T> {\n        self.detail.as_custom()\n    }\n}\n\nimpl Drop for SurfaceTexture {\n    fn drop(&mut self) {\n        if !self.presented && !thread_panicking() {\n            self.detail.texture_discard();\n        }\n    }\n}\n\n/// Result of a call to [`Surface::get_current_texture`].\n///\n/// See variant documentation for how to handle each case.\n#[derive(Debug)]\npub enum CurrentSurfaceTexture {\n    /// Successfully acquired a surface texture with no issues.\n    Success(SurfaceTexture),\n    /// Successfully acquired a surface texture, but texture no longer matches the properties of the underlying surface.\n    /// It's highly recommended to call [`Surface::configure`] again for optimal performance.\n    Suboptimal(SurfaceTexture),\n    /// A timeout was encountered while trying to acquire the next frame.\n    ///\n    /// Applications should skip the current frame and try again later.\n    Timeout,\n    /// The window is occluded (e.g. minimized or behind another window).\n    ///\n    /// Applications should skip the current frame and try again once the window\n    /// is no longer occluded.\n    Occluded,\n    /// The underlying surface has changed, and therefore the surface configuration is outdated.\n    ///\n    /// Call [`Surface::configure()`] and try again.\n    Outdated,\n    /// The surface has been lost and needs to be recreated.\n    ///\n    /// If the device as a whole is lost (see [`set_device_lost_callback()`][crate::Device::set_device_lost_callback]), then\n    /// you need to recreate the device and all resources.\n    /// Otherwise, call [`Instance::create_surface()`] to recreate the surface,\n    /// then [`Surface::configure()`], and try again.\n    Lost,\n    /// A validation error inside [`Surface::get_current_texture()`] was raised\n    /// and caught by an [error scope](crate::Device::push_error_scope) or\n    /// [`on_uncaptured_error()`][crate::Device::on_uncaptured_error].\n    ///\n    /// Applications should attend to the validation error and try again.\n    Validation,\n}\n\nfn thread_panicking() -> bool {\n    cfg_if::cfg_if! {\n        if #[cfg(std)] {\n            std::thread::panicking()\n        } else if #[cfg(panic = \"abort\")] {\n            // If `panic = \"abort\"` then a thread _cannot_ be observably panicking by definition.\n            false\n        } else {\n            // TODO: This is potentially overly pessimistic; it may be appropriate to instead allow a\n            // texture to not be discarded.\n            // Alternatively, this could _also_ be a `panic!`, since we only care if the thread is panicking\n            // when the surface has not been presented.\n            compile_error!(\n                \"cannot determine if a thread is panicking without either `panic = \\\"abort\\\"` or `std`\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/api/texture.rs",
    "content": "#[cfg(wgpu_core)]\nuse core::ops::Deref;\n\nuse crate::*;\n\n/// Handle to a texture on the GPU.\n///\n/// It can be created with [`Device::create_texture`].\n///\n/// Corresponds to [WebGPU `GPUTexture`](https://gpuweb.github.io/gpuweb/#texture-interface).\n#[derive(Debug, Clone)]\npub struct Texture {\n    pub(crate) inner: dispatch::DispatchTexture,\n    pub(crate) descriptor: TextureDescriptor<'static>,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(Texture: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Texture => .inner);\n\nimpl Texture {\n    /// Get the [`wgpu_hal`] texture from this `Texture`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::Texture`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"Texture\")]\n    #[doc = crate::macros::hal_type_metal!(\"Texture\")]\n    #[doc = crate::macros::hal_type_dx12!(\"Texture\")]\n    #[doc = crate::macros::hal_type_gles!(\"Texture\")]\n    ///\n    /// # Deadlocks\n    ///\n    /// - The returned guard holds a read-lock on a device-local \"destruction\"\n    ///   lock, which will cause all calls to `destroy` to block until the\n    ///   guard is released.\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The texture is not from the backend specified by `A`.\n    /// - The texture is from the `webgpu` or `custom` backend.\n    /// - The texture has had [`Self::destroy()`] called on it.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::Texture`]: hal::Api::Texture\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<impl Deref<Target = A::Texture>> {\n        let texture = self.inner.as_core_opt()?;\n        unsafe { texture.context.texture_as_hal::<A>(texture) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Texture (if custom backend and is internally T)\n    pub fn as_custom<T: custom::TextureInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n\n    #[cfg(custom)]\n    /// Creates a texture from already created custom implementation with the given description\n    pub fn from_custom<T: custom::TextureInterface>(\n        texture: T,\n        desc: &TextureDescriptor<'_>,\n    ) -> Self {\n        Self {\n            inner: dispatch::DispatchTexture::custom(texture),\n            descriptor: TextureDescriptor {\n                label: None,\n                view_formats: &[],\n                ..desc.clone()\n            },\n        }\n    }\n\n    /// Creates a view of this texture, specifying an interpretation of its texels and\n    /// possibly a subset of its layers and mip levels.\n    ///\n    /// Texture views are needed to use a texture as a binding in a [`BindGroup`]\n    /// or as an attachment in a [`RenderPass`].\n    pub fn create_view(&self, desc: &TextureViewDescriptor<'_>) -> TextureView {\n        let view = self.inner.create_view(desc);\n\n        TextureView {\n            inner: view,\n            texture: self.clone(),\n        }\n    }\n\n    /// Destroy the associated native resources as soon as possible.\n    pub fn destroy(&self) {\n        self.inner.destroy();\n    }\n\n    /// Make an `TexelCopyTextureInfo` representing the whole texture.\n    pub fn as_image_copy(&self) -> TexelCopyTextureInfo<'_> {\n        TexelCopyTextureInfo {\n            texture: self,\n            mip_level: 0,\n            origin: Origin3d::ZERO,\n            aspect: TextureAspect::All,\n        }\n    }\n\n    /// Returns the size of this `Texture`.\n    ///\n    /// This is always equal to the `size` that was specified when creating the texture.\n    pub fn size(&self) -> Extent3d {\n        self.descriptor.size\n    }\n\n    /// Returns the width of this `Texture`.\n    ///\n    /// This is always equal to the `size.width` that was specified when creating the texture.\n    pub fn width(&self) -> u32 {\n        self.descriptor.size.width\n    }\n\n    /// Returns the height of this `Texture`.\n    ///\n    /// This is always equal to the `size.height` that was specified when creating the texture.\n    pub fn height(&self) -> u32 {\n        self.descriptor.size.height\n    }\n\n    /// Returns the depth or layer count of this `Texture`.\n    ///\n    /// This is always equal to the `size.depth_or_array_layers` that was specified when creating the texture.\n    pub fn depth_or_array_layers(&self) -> u32 {\n        self.descriptor.size.depth_or_array_layers\n    }\n\n    /// Returns the mip_level_count of this `Texture`.\n    ///\n    /// This is always equal to the `mip_level_count` that was specified when creating the texture.\n    pub fn mip_level_count(&self) -> u32 {\n        self.descriptor.mip_level_count\n    }\n\n    /// Returns the sample_count of this `Texture`.\n    ///\n    /// This is always equal to the `sample_count` that was specified when creating the texture.\n    pub fn sample_count(&self) -> u32 {\n        self.descriptor.sample_count\n    }\n\n    /// Returns the dimension of this `Texture`.\n    ///\n    /// This is always equal to the `dimension` that was specified when creating the texture.\n    pub fn dimension(&self) -> TextureDimension {\n        self.descriptor.dimension\n    }\n\n    /// Returns the format of this `Texture`.\n    ///\n    /// This is always equal to the `format` that was specified when creating the texture.\n    pub fn format(&self) -> TextureFormat {\n        self.descriptor.format\n    }\n\n    /// Returns the allowed usages of this `Texture`.\n    ///\n    /// This is always equal to the `usage` that was specified when creating the texture.\n    pub fn usage(&self) -> TextureUsages {\n        self.descriptor.usage\n    }\n}\n\n/// Describes a [`Texture`].\n///\n/// For use with [`Device::create_texture`].\n///\n/// Corresponds to [WebGPU `GPUTextureDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gputexturedescriptor).\npub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, &'a [TextureFormat]>;\nstatic_assertions::assert_impl_all!(TextureDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/texture_view.rs",
    "content": "#[cfg(wgpu_core)]\nuse core::ops::Deref;\n\nuse crate::*;\n\n/// Handle to a texture view.\n///\n/// A `TextureView` object refers to a [`Texture`], or a subset of its layers and mip levels, and\n/// specifies an interpretation of the texture’s texels, which is needed to use a texture as a\n/// binding in a [`BindGroup`] or as an attachment in a [`RenderPass`].\n/// It can be created using [`Texture::create_view()`], which accepts a [`TextureViewDescriptor`]\n/// specifying the properties of the view.\n///\n/// Corresponds to [WebGPU `GPUTextureView`](https://gpuweb.github.io/gpuweb/#gputextureview).\n#[derive(Debug, Clone)]\npub struct TextureView {\n    pub(crate) inner: dispatch::DispatchTextureView,\n    pub(crate) texture: Texture,\n}\n#[cfg(send_sync)]\nstatic_assertions::assert_impl_all!(TextureView: Send, Sync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(TextureView => .inner);\n\nimpl TextureView {\n    /// Returns the [`Texture`] that this `TextureView` refers to.\n    ///\n    /// All wgpu resources are refcounted, so you can own the returned [`Texture`]\n    /// by cloning it.\n    pub fn texture(&self) -> &Texture {\n        &self.texture\n    }\n\n    /// Get the [`wgpu_hal`] texture view from this `TextureView`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::TextureView`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"TextureView\")]\n    #[doc = crate::macros::hal_type_metal!(\"TextureView\")]\n    #[doc = crate::macros::hal_type_dx12!(\"TextureView\")]\n    #[doc = crate::macros::hal_type_gles!(\"TextureView\")]\n    ///\n    /// # Deadlocks\n    ///\n    /// - The returned guard holds a read-lock on a device-local \"destruction\"\n    ///   lock, which will cause all calls to `destroy` to block until the\n    ///   guard is released.\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The texture view is not from the backend specified by `A`.\n    /// - The texture view is from the `webgpu` or `custom` backend.\n    /// - The texture this view points to has had [`Texture::destroy()`] called on it.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::TextureView`]: hal::Api::TextureView\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<impl Deref<Target = A::TextureView>> {\n        let view = self.inner.as_core_opt()?;\n        unsafe { view.context.texture_view_as_hal::<A>(view) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of TextureView (if custom backend and is internally T)\n    pub fn as_custom<T: custom::TextureViewInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n}\n\n/// Describes a [`TextureView`].\n///\n/// For use with [`Texture::create_view`].\n///\n/// Corresponds to [WebGPU `GPUTextureViewDescriptor`](\n/// https://gpuweb.github.io/gpuweb/#dictdef-gputextureviewdescriptor).\npub type TextureViewDescriptor<'a> = wgt::TextureViewDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(TextureViewDescriptor<'_>: Send, Sync);\n"
  },
  {
    "path": "wgpu/src/api/tlas.rs",
    "content": "use crate::{api::blas::TlasInstance, dispatch};\nuse crate::{BindingResource, Label};\nuse alloc::vec::Vec;\n#[cfg(wgpu_core)]\nuse core::ops::Deref;\nuse core::ops::{Index, IndexMut, Range};\nuse wgt::WasmNotSendSync;\n\n/// Descriptor to create top level acceleration structures.\npub type CreateTlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;\nstatic_assertions::assert_impl_all!(CreateTlasDescriptor<'_>: Send, Sync);\n\n#[derive(Debug, Clone)]\n/// Top Level Acceleration Structure (TLAS).\n///\n/// A TLAS contains a series of [TLAS instances], which are a reference to\n/// a BLAS and a transformation matrix placing the geometry in the world.\n///\n/// A TLAS also contains an extra set of TLAS instances in a device readable form, you cant interact\n/// directly with these, instead you have to build the TLAS with [TLAS instances].\n///\n/// [TLAS instances]: TlasInstance\npub struct Tlas {\n    pub(crate) inner: dispatch::DispatchTlas,\n    pub(crate) instances: Vec<Option<TlasInstance>>,\n    pub(crate) lowest_unmodified: u32,\n}\nstatic_assertions::assert_impl_all!(Tlas: WasmNotSendSync);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(Tlas => .inner);\n\nimpl Tlas {\n    /// Get the [`wgpu_hal`] acceleration structure from this `Tlas`.\n    ///\n    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],\n    /// and pass that struct to the to the `A` type parameter.\n    ///\n    /// Returns a guard that dereferences to the type of the hal backend\n    /// which implements [`A::AccelerationStructure`].\n    ///\n    /// # Types\n    ///\n    /// The returned type depends on the backend:\n    ///\n    #[doc = crate::macros::hal_type_vulkan!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_metal!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_dx12!(\"AccelerationStructure\")]\n    #[doc = crate::macros::hal_type_gles!(\"AccelerationStructure\")]\n    ///\n    /// # Deadlocks\n    ///\n    /// - The returned guard holds a read-lock on a device-local \"destruction\"\n    ///   lock, which will cause all calls to `destroy` to block until the\n    ///   guard is released.\n    ///\n    /// # Errors\n    ///\n    /// This method will return None if:\n    /// - The acceleration structure is not from the backend specified by `A`.\n    /// - The acceleration structure is from the `webgpu` or `custom` backend.\n    ///\n    /// # Safety\n    ///\n    /// - The returned resource must not be destroyed unless the guard\n    ///   is the last reference to it and it is not in use by the GPU.\n    ///   The guard and handle may be dropped at any time however.\n    /// - All the safety requirements of wgpu-hal must be upheld.\n    ///\n    /// [`A::AccelerationStructure`]: hal::Api::AccelerationStructure\n    #[cfg(wgpu_core)]\n    pub unsafe fn as_hal<A: hal::Api>(\n        &mut self,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {\n        let tlas = self.inner.as_core_opt()?;\n        unsafe { tlas.context.tlas_as_hal::<A>(tlas) }\n    }\n\n    #[cfg(custom)]\n    /// Returns custom implementation of Tlas (if custom backend and is internally T)\n    pub fn as_custom<T: crate::custom::TlasInterface>(&self) -> Option<&T> {\n        self.inner.as_custom()\n    }\n\n    /// Get a reference to all instances.\n    pub fn get(&self) -> &[Option<TlasInstance>] {\n        &self.instances\n    }\n\n    /// Get a mutable slice to a range of instances.\n    /// Returns None if the range is out of bounds.\n    /// All elements from the lowest accessed index up are marked as modified.\n    // this recommendation is not useful yet, but is likely to be when ability to update arrives or possible optimisations for building get implemented.\n    /// For best performance it is recommended to prefer access to low elements and modify higher elements as little as possible.\n    /// This can be done by ordering instances from the most to the least used. It is recommended\n    /// to use [`Self::index_mut`] unless the option if out of bounds is required\n    pub fn get_mut_slice(&mut self, range: Range<usize>) -> Option<&mut [Option<TlasInstance>]> {\n        if range.end > self.instances.len() {\n            return None;\n        }\n        if range.end as u32 > self.lowest_unmodified {\n            self.lowest_unmodified = range.end as u32;\n        }\n        Some(&mut self.instances[range])\n    }\n\n    /// Get a single mutable reference to an instance.\n    /// Returns None if the range is out of bounds.\n    /// All elements from the lowest accessed index up are marked as modified.\n    // this recommendation is not useful yet, but is likely to be when ability to update arrives or possible optimisations for building get implemented.\n    /// For best performance it is recommended to prefer access to low elements and modify higher elements as little as possible.\n    /// This can be done by ordering instances from the most to the least used. It is recommended\n    /// to use [`Self::index_mut`] unless the option if out of bounds is required\n    pub fn get_mut_single(&mut self, index: usize) -> Option<&mut Option<TlasInstance>> {\n        if index >= self.instances.len() {\n            return None;\n        }\n        if index as u32 + 1 > self.lowest_unmodified {\n            self.lowest_unmodified = index as u32 + 1;\n        }\n        Some(&mut self.instances[index])\n    }\n\n    /// Get the binding resource for the underling acceleration structure, to be used when creating a [`BindGroup`]\n    ///\n    /// [`BindGroup`]: super::BindGroup\n    pub fn as_binding(&self) -> BindingResource<'_> {\n        BindingResource::AccelerationStructure(self)\n    }\n}\n\nimpl Index<usize> for Tlas {\n    type Output = Option<TlasInstance>;\n\n    fn index(&self, index: usize) -> &Self::Output {\n        self.instances.index(index)\n    }\n}\n\nimpl Index<Range<usize>> for Tlas {\n    type Output = [Option<TlasInstance>];\n\n    fn index(&self, index: Range<usize>) -> &Self::Output {\n        self.instances.index(index)\n    }\n}\n\nimpl IndexMut<usize> for Tlas {\n    fn index_mut(&mut self, index: usize) -> &mut Self::Output {\n        let idx = self.instances.index_mut(index);\n        if index as u32 + 1 > self.lowest_unmodified {\n            self.lowest_unmodified = index as u32 + 1;\n        }\n        idx\n    }\n}\n\nimpl IndexMut<Range<usize>> for Tlas {\n    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {\n        let idx = self.instances.index_mut(index.clone());\n        if index.end > self.lowest_unmodified as usize {\n            self.lowest_unmodified = index.end as u32;\n        }\n        idx\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/custom.rs",
    "content": "//! Provides wrappers custom backend implementations\n\n#![allow(ambiguous_wide_pointer_comparisons)]\n\npub use crate::dispatch::*;\n\nuse alloc::sync::Arc;\n\nmacro_rules! dyn_type {\n    // cloning of arc forbidden\n    // but we still use it to provide Eq,Ord,Hash implementations\n    (pub mut struct $name:ident(dyn $interface:tt)) => {\n        #[derive(Debug)]\n        pub(crate) struct $name(Arc<dyn $interface>);\n        crate::cmp::impl_eq_ord_hash_arc_address!($name => .0);\n\n        impl $name {\n            pub(crate) fn new<T: $interface>(t: T) -> Self {\n                Self(Arc::new(t))\n            }\n\n            #[allow(clippy::allow_attributes, dead_code)]\n            pub(crate) fn downcast<T: $interface>(&self) -> Option<&T> {\n                self.0.as_ref().as_any().downcast_ref()\n            }\n        }\n\n        impl core::ops::Deref for $name {\n            type Target = dyn $interface;\n\n            #[inline]\n            fn deref(&self) -> &Self::Target {\n                self.0.as_ref()\n            }\n        }\n\n        impl core::ops::DerefMut for $name {\n            #[inline]\n            fn deref_mut(&mut self) -> &mut Self::Target {\n                Arc::get_mut(&mut self.0).expect(\"\")\n            }\n        }\n    };\n    // cloning of arc is allowed\n    (pub ref struct $name:ident(dyn $interface:tt)) => {\n        #[derive(Debug, Clone)]\n        pub(crate) struct $name(Arc<dyn $interface>);\n        crate::cmp::impl_eq_ord_hash_arc_address!($name => .0);\n\n        impl $name {\n            pub(crate) fn new<T: $interface>(t: T) -> Self {\n                Self(Arc::new(t))\n            }\n\n            pub(crate) fn downcast<T: $interface>(&self) -> Option<&T> {\n                self.0.as_ref().as_any().downcast_ref()\n            }\n        }\n\n        impl core::ops::Deref for $name {\n            type Target = dyn $interface;\n\n            #[inline]\n            fn deref(&self) -> &Self::Target {\n                self.0.as_ref()\n            }\n        }\n    };\n}\n\ndyn_type!(pub ref struct DynContext(dyn InstanceInterface));\ndyn_type!(pub ref struct DynAdapter(dyn AdapterInterface));\ndyn_type!(pub ref struct DynDevice(dyn DeviceInterface));\ndyn_type!(pub ref struct DynQueue(dyn QueueInterface));\ndyn_type!(pub ref struct DynShaderModule(dyn ShaderModuleInterface));\ndyn_type!(pub ref struct DynBindGroupLayout(dyn BindGroupLayoutInterface));\ndyn_type!(pub ref struct DynBindGroup(dyn BindGroupInterface));\ndyn_type!(pub ref struct DynTextureView(dyn TextureViewInterface));\ndyn_type!(pub ref struct DynSampler(dyn SamplerInterface));\ndyn_type!(pub ref struct DynBuffer(dyn BufferInterface));\ndyn_type!(pub ref struct DynTexture(dyn TextureInterface));\ndyn_type!(pub ref struct DynExternalTexture(dyn ExternalTextureInterface));\ndyn_type!(pub ref struct DynBlas(dyn BlasInterface));\ndyn_type!(pub ref struct DynTlas(dyn TlasInterface));\ndyn_type!(pub ref struct DynQuerySet(dyn QuerySetInterface));\ndyn_type!(pub ref struct DynPipelineLayout(dyn PipelineLayoutInterface));\ndyn_type!(pub ref struct DynRenderPipeline(dyn RenderPipelineInterface));\ndyn_type!(pub ref struct DynComputePipeline(dyn ComputePipelineInterface));\ndyn_type!(pub ref struct DynPipelineCache(dyn PipelineCacheInterface));\ndyn_type!(pub mut struct DynCommandEncoder(dyn CommandEncoderInterface));\ndyn_type!(pub mut struct DynComputePass(dyn ComputePassInterface));\ndyn_type!(pub mut struct DynRenderPass(dyn RenderPassInterface));\ndyn_type!(pub mut struct DynCommandBuffer(dyn CommandBufferInterface));\ndyn_type!(pub mut struct DynRenderBundleEncoder(dyn RenderBundleEncoderInterface));\ndyn_type!(pub ref struct DynRenderBundle(dyn RenderBundleInterface));\ndyn_type!(pub ref struct DynSurface(dyn SurfaceInterface));\ndyn_type!(pub ref struct DynSurfaceOutputDetail(dyn SurfaceOutputDetailInterface));\ndyn_type!(pub mut struct DynQueueWriteBuffer(dyn QueueWriteBufferInterface));\ndyn_type!(pub mut struct DynBufferMappedRange(dyn BufferMappedRangeInterface));\n"
  },
  {
    "path": "wgpu/src/backend/mod.rs",
    "content": "#[cfg(webgpu)]\npub mod webgpu;\n#[cfg(webgpu)]\npub(crate) use webgpu::{get_browser_gpu_property, ContextWebGpu};\n\n#[cfg(wgpu_core)]\npub mod wgpu_core;\n\n#[cfg(wgpu_core)]\npub(crate) use wgpu_core::ContextWgpuCore;\n\n#[cfg(custom)]\npub mod custom;\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/defined_non_null_js_value.rs",
    "content": "use core::ops::{Deref, DerefMut};\n\nuse wasm_bindgen::JsValue;\n\n/// Derefs to a [`JsValue`] that's known not to be `undefined` or `null`.\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct DefinedNonNullJsValue<T>(T);\n\nimpl<T> DefinedNonNullJsValue<T>\nwhere\n    T: AsRef<JsValue>,\n{\n    pub fn new(value: T) -> Option<Self> {\n        if value.as_ref().is_undefined() || value.as_ref().is_null() {\n            None\n        } else {\n            Some(Self(value))\n        }\n    }\n}\n\nimpl<T> Deref for DefinedNonNullJsValue<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<T> DerefMut for DefinedNonNullJsValue<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl<T> AsRef<T> for DefinedNonNullJsValue<T> {\n    fn as_ref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T> AsMut<T> for DefinedNonNullJsValue<T> {\n    fn as_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/ext_bindings.rs",
    "content": "//! Extension bindings for WebGPU.\n//!\n//! These contain ideomatic Rust extension traits for various parts of the WebGPU\n//! bindings that are missing, need to be improved, or otherwise need to be different\n//! from the generated web_sys bindings.\n\nuse crate::backend::webgpu::webgpu_sys;\nuse wasm_bindgen::prelude::*;\n\n/// Extension trait for [`web_sys::Navigator`] and [`web_sys::WorkerNavigator`] to\n/// access the `gpu` property.\npub trait NavigatorGpu {\n    /// Get the `gpu` property.\n    ///\n    /// This is intentionally a free function, to prevent overload conflicts with\n    /// the method if it is enabled in web-sys itself.\n    fn gpu(navigator: &Self) -> webgpu_sys::Gpu;\n}\n\n// --- Bindings for `Navigator` ---\n#[wasm_bindgen]\nextern \"C\" {\n    /// Create a fake class which we tell wasm-bindgen has access to the `gpu` property.\n    #[wasm_bindgen]\n    type NavigatorWithGpu;\n\n    #[wasm_bindgen(method, getter)]\n    fn gpu(ext: &NavigatorWithGpu) -> webgpu_sys::Gpu;\n}\n\nimpl NavigatorGpu for web_sys::Navigator {\n    fn gpu(navigator: &Self) -> webgpu_sys::Gpu {\n        // Must be an unchecked ref as this class does not exist at runtime.\n        let extension: &NavigatorWithGpu = navigator.unchecked_ref();\n        extension.gpu()\n    }\n}\n\nimpl NavigatorGpu for web_sys::WorkerNavigator {\n    fn gpu(navigator: &Self) -> webgpu_sys::Gpu {\n        // Must be an unchecked ref as this class does not exist at runtime.\n        let extension: &NavigatorWithGpu = navigator.unchecked_ref();\n        extension.gpu()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_Gpu.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPU , typescript_type = \"GPU\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `Gpu` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPU)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `Gpu`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type Gpu;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPU\" , js_name = wgslLanguageFeatures)]\n    #[doc = \"Getter for the `wgslLanguageFeatures` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPU/wgslLanguageFeatures)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `Gpu`, `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn wgsl_language_features(this: &Gpu) -> WgslLanguageFeatures;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPU\" , js_name = getPreferredCanvasFormat)]\n    #[doc = \"The `getPreferredCanvasFormat()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPU/getPreferredCanvasFormat)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `Gpu`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_preferred_canvas_format(this: &Gpu) -> GpuTextureFormat;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPU\" , js_name = requestAdapter)]\n    #[doc = \"The `requestAdapter()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPU/requestAdapter)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `Gpu`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn request_adapter(this: &Gpu) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPU\" , js_name = requestAdapter)]\n    #[doc = \"The `requestAdapter()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPU/requestAdapter)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `Gpu`, `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn request_adapter_with_options(\n        this: &Gpu,\n        options: &GpuRequestAdapterOptions,\n    ) -> ::js_sys::Promise;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuAdapter.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUAdapter , typescript_type = \"GPUAdapter\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuAdapter` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuAdapter;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapter\" , js_name = features)]\n    #[doc = \"Getter for the `features` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/features)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`, `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn features(this: &GpuAdapter) -> GpuSupportedFeatures;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapter\" , js_name = limits)]\n    #[doc = \"Getter for the `limits` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/limits)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`, `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn limits(this: &GpuAdapter) -> GpuSupportedLimits;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapter\" , js_name = info)]\n    #[doc = \"Getter for the `info` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/info)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`, `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn info(this: &GpuAdapter) -> GpuAdapterInfo;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapter\" , js_name = isFallbackAdapter)]\n    #[doc = \"Getter for the `isFallbackAdapter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/isFallbackAdapter)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn is_fallback_adapter(this: &GpuAdapter) -> bool;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUAdapter\" , js_name = requestDevice)]\n    #[doc = \"The `requestDevice()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/requestDevice)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn request_device(this: &GpuAdapter) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUAdapter\" , js_name = requestDevice)]\n    #[doc = \"The `requestDevice()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapter/requestDevice)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapter`, `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn request_device_with_descriptor(\n        this: &GpuAdapter,\n        descriptor: &GpuDeviceDescriptor,\n    ) -> ::js_sys::Promise;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuAdapterInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUAdapterInfo , typescript_type = \"GPUAdapterInfo\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuAdapterInfo` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapterInfo)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuAdapterInfo;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapterInfo\" , js_name = vendor)]\n    #[doc = \"Getter for the `vendor` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapterInfo/vendor)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn vendor(this: &GpuAdapterInfo) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapterInfo\" , js_name = architecture)]\n    #[doc = \"Getter for the `architecture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapterInfo/architecture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn architecture(this: &GpuAdapterInfo) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapterInfo\" , js_name = device)]\n    #[doc = \"Getter for the `device` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapterInfo/device)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn device(this: &GpuAdapterInfo) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUAdapterInfo\" , js_name = description)]\n    #[doc = \"Getter for the `description` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUAdapterInfo/description)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn description(this: &GpuAdapterInfo) -> ::alloc::string::String;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuAddressMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuAddressMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuAddressMode {\n    ClampToEdge = \"clamp-to-edge\",\n    Repeat = \"repeat\",\n    MirrorRepeat = \"mirror-repeat\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuAutoLayoutMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuAutoLayoutMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuAutoLayoutMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuAutoLayoutMode {\n    Auto = \"auto\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroup.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroup , typescript_type = \"GPUBindGroup\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroup` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroup;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBindGroup\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroup/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuBindGroup) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUBindGroup\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroup/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuBindGroup, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroupDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroupDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroupDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroupDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuBindGroupDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuBindGroupDescriptor, val: &str);\n\n    #[doc = \"Get the `entries` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"entries\")]\n    pub fn get_entries(this: &GpuBindGroupDescriptor) -> ::js_sys::Array;\n\n    #[doc = \"Change the `entries` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"entries\")]\n    pub fn set_entries(this: &GpuBindGroupDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`, `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"layout\")]\n    pub fn get_layout(this: &GpuBindGroupDescriptor) -> GpuBindGroupLayout;\n\n    #[doc = \"Change the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`, `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"layout\")]\n    pub fn set_layout(this: &GpuBindGroupDescriptor, val: &GpuBindGroupLayout);\n}\n\nimpl GpuBindGroupDescriptor {\n    #[doc = \"Construct a new `GpuBindGroupDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupDescriptor`, `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(entries: &::wasm_bindgen::JsValue, layout: &GpuBindGroupLayout) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_entries(entries);\n        ret.set_layout(layout);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_entries()` instead.\"]\n    pub fn entries(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_entries(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_layout()` instead.\"]\n    pub fn layout(&mut self, val: &GpuBindGroupLayout) -> &mut Self {\n        self.set_layout(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroupEntry.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroupEntry)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroupEntry` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroupEntry;\n\n    #[doc = \"Get the `binding` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"binding\")]\n    pub fn get_binding(this: &GpuBindGroupEntry) -> u32;\n\n    #[doc = \"Change the `binding` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"binding\")]\n    pub fn set_binding(this: &GpuBindGroupEntry, val: u32);\n\n    #[doc = \"Get the `resource` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"resource\")]\n    pub fn get_resource(this: &GpuBindGroupEntry) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `resource` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"resource\")]\n    pub fn set_resource(this: &GpuBindGroupEntry, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuBindGroupEntry {\n    #[doc = \"Construct a new `GpuBindGroupEntry`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(binding: u32, resource: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_binding(binding);\n        ret.set_resource(resource);\n        ret\n    }\n\n    #[deprecated = \"Use `set_binding()` instead.\"]\n    pub fn binding(&mut self, val: u32) -> &mut Self {\n        self.set_binding(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_resource()` instead.\"]\n    pub fn resource(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_resource(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroupLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroupLayout , typescript_type = \"GPUBindGroupLayout\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroupLayout` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroupLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroupLayout;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBindGroupLayout\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroupLayout/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuBindGroupLayout) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUBindGroupLayout\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBindGroupLayout/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuBindGroupLayout, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroupLayoutDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroupLayoutDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroupLayoutDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroupLayoutDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuBindGroupLayoutDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuBindGroupLayoutDescriptor, val: &str);\n\n    #[doc = \"Get the `entries` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"entries\")]\n    pub fn get_entries(this: &GpuBindGroupLayoutDescriptor) -> ::js_sys::Array;\n\n    #[doc = \"Change the `entries` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"entries\")]\n    pub fn set_entries(this: &GpuBindGroupLayoutDescriptor, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuBindGroupLayoutDescriptor {\n    #[doc = \"Construct a new `GpuBindGroupLayoutDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(entries: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_entries(entries);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_entries()` instead.\"]\n    pub fn entries(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_entries(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBindGroupLayoutEntry.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBindGroupLayoutEntry)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBindGroupLayoutEntry` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBindGroupLayoutEntry;\n\n    #[doc = \"Get the `binding` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"binding\")]\n    pub fn get_binding(this: &GpuBindGroupLayoutEntry) -> u32;\n\n    #[doc = \"Change the `binding` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"binding\")]\n    pub fn set_binding(this: &GpuBindGroupLayoutEntry, val: u32);\n\n    #[doc = \"Get the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"buffer\")]\n    pub fn get_buffer(this: &GpuBindGroupLayoutEntry) -> Option<GpuBufferBindingLayout>;\n\n    #[doc = \"Change the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"buffer\")]\n    pub fn set_buffer(this: &GpuBindGroupLayoutEntry, val: &GpuBufferBindingLayout);\n\n    #[doc = \"Get the `externalTexture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuExternalTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"externalTexture\")]\n    pub fn get_external_texture(\n        this: &GpuBindGroupLayoutEntry,\n    ) -> Option<GpuExternalTextureBindingLayout>;\n\n    #[doc = \"Change the `externalTexture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuExternalTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"externalTexture\")]\n    pub fn set_external_texture(\n        this: &GpuBindGroupLayoutEntry,\n        val: &GpuExternalTextureBindingLayout,\n    );\n\n    #[doc = \"Get the `sampler` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuSamplerBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"sampler\")]\n    pub fn get_sampler(this: &GpuBindGroupLayoutEntry) -> Option<GpuSamplerBindingLayout>;\n\n    #[doc = \"Change the `sampler` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuSamplerBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"sampler\")]\n    pub fn set_sampler(this: &GpuBindGroupLayoutEntry, val: &GpuSamplerBindingLayout);\n\n    #[doc = \"Get the `storageTexture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuStorageTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"storageTexture\")]\n    pub fn get_storage_texture(\n        this: &GpuBindGroupLayoutEntry,\n    ) -> Option<GpuStorageTextureBindingLayout>;\n\n    #[doc = \"Change the `storageTexture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuStorageTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"storageTexture\")]\n    pub fn set_storage_texture(\n        this: &GpuBindGroupLayoutEntry,\n        val: &GpuStorageTextureBindingLayout,\n    );\n\n    #[doc = \"Get the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"texture\")]\n    pub fn get_texture(this: &GpuBindGroupLayoutEntry) -> Option<GpuTextureBindingLayout>;\n\n    #[doc = \"Change the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`, `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"texture\")]\n    pub fn set_texture(this: &GpuBindGroupLayoutEntry, val: &GpuTextureBindingLayout);\n\n    #[doc = \"Get the `visibility` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"visibility\")]\n    pub fn get_visibility(this: &GpuBindGroupLayoutEntry) -> u32;\n\n    #[doc = \"Change the `visibility` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"visibility\")]\n    pub fn set_visibility(this: &GpuBindGroupLayoutEntry, val: u32);\n}\n\nimpl GpuBindGroupLayoutEntry {\n    #[doc = \"Construct a new `GpuBindGroupLayoutEntry`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayoutEntry`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(binding: u32, visibility: u32) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_binding(binding);\n        ret.set_visibility(visibility);\n        ret\n    }\n\n    #[deprecated = \"Use `set_binding()` instead.\"]\n    pub fn binding(&mut self, val: u32) -> &mut Self {\n        self.set_binding(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_buffer()` instead.\"]\n    pub fn buffer(&mut self, val: &GpuBufferBindingLayout) -> &mut Self {\n        self.set_buffer(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_external_texture()` instead.\"]\n    pub fn external_texture(&mut self, val: &GpuExternalTextureBindingLayout) -> &mut Self {\n        self.set_external_texture(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_sampler()` instead.\"]\n    pub fn sampler(&mut self, val: &GpuSamplerBindingLayout) -> &mut Self {\n        self.set_sampler(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_storage_texture()` instead.\"]\n    pub fn storage_texture(&mut self, val: &GpuStorageTextureBindingLayout) -> &mut Self {\n        self.set_storage_texture(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_texture()` instead.\"]\n    pub fn texture(&mut self, val: &GpuTextureBindingLayout) -> &mut Self {\n        self.set_texture(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_visibility()` instead.\"]\n    pub fn visibility(&mut self, val: u32) -> &mut Self {\n        self.set_visibility(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBlendComponent.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBlendComponent)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBlendComponent` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBlendComponent;\n\n    #[doc = \"Get the `dstFactor` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendFactor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"dstFactor\")]\n    pub fn get_dst_factor(this: &GpuBlendComponent) -> Option<GpuBlendFactor>;\n\n    #[doc = \"Change the `dstFactor` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendFactor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"dstFactor\")]\n    pub fn set_dst_factor(this: &GpuBlendComponent, val: GpuBlendFactor);\n\n    #[doc = \"Get the `operation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"operation\")]\n    pub fn get_operation(this: &GpuBlendComponent) -> Option<GpuBlendOperation>;\n\n    #[doc = \"Change the `operation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"operation\")]\n    pub fn set_operation(this: &GpuBlendComponent, val: GpuBlendOperation);\n\n    #[doc = \"Get the `srcFactor` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendFactor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"srcFactor\")]\n    pub fn get_src_factor(this: &GpuBlendComponent) -> Option<GpuBlendFactor>;\n\n    #[doc = \"Change the `srcFactor` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendFactor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"srcFactor\")]\n    pub fn set_src_factor(this: &GpuBlendComponent, val: GpuBlendFactor);\n}\n\nimpl GpuBlendComponent {\n    #[doc = \"Construct a new `GpuBlendComponent`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_dst_factor()` instead.\"]\n    pub fn dst_factor(&mut self, val: GpuBlendFactor) -> &mut Self {\n        self.set_dst_factor(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_operation()` instead.\"]\n    pub fn operation(&mut self, val: GpuBlendOperation) -> &mut Self {\n        self.set_operation(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_src_factor()` instead.\"]\n    pub fn src_factor(&mut self, val: GpuBlendFactor) -> &mut Self {\n        self.set_src_factor(val);\n        self\n    }\n}\n\nimpl Default for GpuBlendComponent {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBlendFactor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuBlendFactor` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuBlendFactor`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuBlendFactor {\n    Zero = \"zero\",\n    One = \"one\",\n    Src = \"src\",\n    OneMinusSrc = \"one-minus-src\",\n    SrcAlpha = \"src-alpha\",\n    OneMinusSrcAlpha = \"one-minus-src-alpha\",\n    Dst = \"dst\",\n    OneMinusDst = \"one-minus-dst\",\n    DstAlpha = \"dst-alpha\",\n    OneMinusDstAlpha = \"one-minus-dst-alpha\",\n    SrcAlphaSaturated = \"src-alpha-saturated\",\n    Constant = \"constant\",\n    OneMinusConstant = \"one-minus-constant\",\n    Src1 = \"src1\",\n    OneMinusSrc1 = \"one-minus-src1\",\n    Src1Alpha = \"src1-alpha\",\n    OneMinusSrc1Alpha = \"one-minus-src1-alpha\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBlendOperation.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuBlendOperation` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuBlendOperation`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuBlendOperation {\n    Add = \"add\",\n    Subtract = \"subtract\",\n    ReverseSubtract = \"reverse-subtract\",\n    Min = \"min\",\n    Max = \"max\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBlendState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBlendState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBlendState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBlendState;\n\n    #[doc = \"Get the `alpha` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"alpha\")]\n    pub fn get_alpha(this: &GpuBlendState) -> GpuBlendComponent;\n\n    #[doc = \"Change the `alpha` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"alpha\")]\n    pub fn set_alpha(this: &GpuBlendState, val: &GpuBlendComponent);\n\n    #[doc = \"Get the `color` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"color\")]\n    pub fn get_color(this: &GpuBlendState) -> GpuBlendComponent;\n\n    #[doc = \"Change the `color` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"color\")]\n    pub fn set_color(this: &GpuBlendState, val: &GpuBlendComponent);\n}\n\nimpl GpuBlendState {\n    #[doc = \"Construct a new `GpuBlendState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendComponent`, `GpuBlendState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(alpha: &GpuBlendComponent, color: &GpuBlendComponent) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_alpha(alpha);\n        ret.set_color(color);\n        ret\n    }\n\n    #[deprecated = \"Use `set_alpha()` instead.\"]\n    pub fn alpha(&mut self, val: &GpuBlendComponent) -> &mut Self {\n        self.set_alpha(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_color()` instead.\"]\n    pub fn color(&mut self, val: &GpuBlendComponent) -> &mut Self {\n        self.set_color(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBuffer.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBuffer , typescript_type = \"GPUBuffer\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBuffer` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBuffer;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBuffer\" , js_name = size)]\n    #[doc = \"Getter for the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/size)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn size(this: &GpuBuffer) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBuffer\" , js_name = usage)]\n    #[doc = \"Getter for the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/usage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn usage(this: &GpuBuffer) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBuffer\" , js_name = mapState)]\n    #[doc = \"Getter for the `mapState` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapState)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuBufferMapState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_state(this: &GpuBuffer) -> GpuBufferMapState;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUBuffer\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuBuffer) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUBuffer\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuBuffer, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = destroy)]\n    #[doc = \"The `destroy()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/destroy)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn destroy(this: &GpuBuffer);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range(this: &GpuBuffer) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_u32(\n        this: &GpuBuffer,\n        offset: u32,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_f64(\n        this: &GpuBuffer,\n        offset: f64,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_u32_and_u32(\n        this: &GpuBuffer,\n        offset: u32,\n        size: u32,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_f64_and_u32(\n        this: &GpuBuffer,\n        offset: f64,\n        size: u32,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_u32_and_f64(\n        this: &GpuBuffer,\n        offset: u32,\n        size: f64,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUBuffer\" , js_name = getMappedRange)]\n    #[doc = \"The `getMappedRange()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/getMappedRange)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_mapped_range_with_f64_and_f64(\n        this: &GpuBuffer,\n        offset: f64,\n        size: f64,\n    ) -> Result<::js_sys::ArrayBuffer, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async(this: &GpuBuffer, mode: u32) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_u32(this: &GpuBuffer, mode: u32, offset: u32) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_f64(this: &GpuBuffer, mode: u32, offset: f64) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_u32_and_u32(\n        this: &GpuBuffer,\n        mode: u32,\n        offset: u32,\n        size: u32,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_f64_and_u32(\n        this: &GpuBuffer,\n        mode: u32,\n        offset: f64,\n        size: u32,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_u32_and_f64(\n        this: &GpuBuffer,\n        mode: u32,\n        offset: u32,\n        size: f64,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = mapAsync)]\n    #[doc = \"The `mapAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn map_async_with_f64_and_f64(\n        this: &GpuBuffer,\n        mode: u32,\n        offset: f64,\n        size: f64,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUBuffer\" , js_name = unmap)]\n    #[doc = \"The `unmap()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/unmap)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn unmap(this: &GpuBuffer);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBufferBinding.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBufferBinding)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBufferBinding` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBufferBinding;\n\n    #[doc = \"Get the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"buffer\")]\n    pub fn get_buffer(this: &GpuBufferBinding) -> GpuBuffer;\n\n    #[doc = \"Change the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"buffer\")]\n    pub fn set_buffer(this: &GpuBufferBinding, val: &GpuBuffer);\n\n    #[doc = \"Get the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"offset\")]\n    pub fn get_offset(this: &GpuBufferBinding) -> Option<f64>;\n\n    #[doc = \"Change the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"offset\")]\n    pub fn set_offset(this: &GpuBufferBinding, val: f64);\n\n    #[doc = \"Get the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"size\")]\n    pub fn get_size(this: &GpuBufferBinding) -> Option<f64>;\n\n    #[doc = \"Change the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"size\")]\n    pub fn set_size(this: &GpuBufferBinding, val: f64);\n}\n\nimpl GpuBufferBinding {\n    #[doc = \"Construct a new `GpuBufferBinding`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuBufferBinding`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(buffer: &GpuBuffer) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_buffer(buffer);\n        ret\n    }\n\n    #[deprecated = \"Use `set_buffer()` instead.\"]\n    pub fn buffer(&mut self, val: &GpuBuffer) -> &mut Self {\n        self.set_buffer(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_offset()` instead.\"]\n    pub fn offset(&mut self, val: f64) -> &mut Self {\n        self.set_offset(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_size()` instead.\"]\n    pub fn size(&mut self, val: f64) -> &mut Self {\n        self.set_size(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBufferBindingLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBufferBindingLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBufferBindingLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBufferBindingLayout;\n\n    #[doc = \"Get the `hasDynamicOffset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"hasDynamicOffset\")]\n    pub fn get_has_dynamic_offset(this: &GpuBufferBindingLayout) -> Option<bool>;\n\n    #[doc = \"Change the `hasDynamicOffset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"hasDynamicOffset\")]\n    pub fn set_has_dynamic_offset(this: &GpuBufferBindingLayout, val: bool);\n\n    #[doc = \"Get the `minBindingSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"minBindingSize\")]\n    pub fn get_min_binding_size(this: &GpuBufferBindingLayout) -> Option<f64>;\n\n    #[doc = \"Change the `minBindingSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"minBindingSize\")]\n    pub fn set_min_binding_size(this: &GpuBufferBindingLayout, val: f64);\n\n    #[doc = \"Get the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`, `GpuBufferBindingType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"type\")]\n    pub fn get_type(this: &GpuBufferBindingLayout) -> Option<GpuBufferBindingType>;\n\n    #[doc = \"Change the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`, `GpuBufferBindingType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"type\")]\n    pub fn set_type(this: &GpuBufferBindingLayout, val: GpuBufferBindingType);\n}\n\nimpl GpuBufferBindingLayout {\n    #[doc = \"Construct a new `GpuBufferBindingLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_has_dynamic_offset()` instead.\"]\n    pub fn has_dynamic_offset(&mut self, val: bool) -> &mut Self {\n        self.set_has_dynamic_offset(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_min_binding_size()` instead.\"]\n    pub fn min_binding_size(&mut self, val: f64) -> &mut Self {\n        self.set_min_binding_size(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_type()` instead.\"]\n    pub fn type_(&mut self, val: GpuBufferBindingType) -> &mut Self {\n        self.set_type(val);\n        self\n    }\n}\n\nimpl Default for GpuBufferBindingLayout {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBufferBindingType.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuBufferBindingType` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuBufferBindingType`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuBufferBindingType {\n    Uniform = \"uniform\",\n    Storage = \"storage\",\n    ReadOnlyStorage = \"read-only-storage\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBufferDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUBufferDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuBufferDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuBufferDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuBufferDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuBufferDescriptor, val: &str);\n\n    #[doc = \"Get the `mappedAtCreation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mappedAtCreation\")]\n    pub fn get_mapped_at_creation(this: &GpuBufferDescriptor) -> Option<bool>;\n\n    #[doc = \"Change the `mappedAtCreation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mappedAtCreation\")]\n    pub fn set_mapped_at_creation(this: &GpuBufferDescriptor, val: bool);\n\n    #[doc = \"Get the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"size\")]\n    pub fn get_size(this: &GpuBufferDescriptor) -> f64;\n\n    #[doc = \"Change the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"size\")]\n    pub fn set_size(this: &GpuBufferDescriptor, val: f64);\n\n    #[doc = \"Get the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"usage\")]\n    pub fn get_usage(this: &GpuBufferDescriptor) -> u32;\n\n    #[doc = \"Change the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"usage\")]\n    pub fn set_usage(this: &GpuBufferDescriptor, val: u32);\n}\n\nimpl GpuBufferDescriptor {\n    #[doc = \"Construct a new `GpuBufferDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(size: f64, usage: u32) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_size(size);\n        ret.set_usage(usage);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mapped_at_creation()` instead.\"]\n    pub fn mapped_at_creation(&mut self, val: bool) -> &mut Self {\n        self.set_mapped_at_creation(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_size()` instead.\"]\n    pub fn size(&mut self, val: f64) -> &mut Self {\n        self.set_size(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_usage()` instead.\"]\n    pub fn usage(&mut self, val: u32) -> &mut Self {\n        self.set_usage(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuBufferMapState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuBufferMapState` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuBufferMapState`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuBufferMapState {\n    Unmapped = \"unmapped\",\n    Pending = \"pending\",\n    Mapped = \"mapped\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCanvasAlphaMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuCanvasAlphaMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuCanvasAlphaMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuCanvasAlphaMode {\n    Opaque = \"opaque\",\n    Premultiplied = \"premultiplied\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCanvasConfiguration.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCanvasConfiguration)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCanvasConfiguration` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCanvasConfiguration;\n\n    #[doc = \"Get the `alphaMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasAlphaMode`, `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"alphaMode\")]\n    pub fn get_alpha_mode(this: &GpuCanvasConfiguration) -> Option<GpuCanvasAlphaMode>;\n\n    #[doc = \"Change the `alphaMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasAlphaMode`, `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"alphaMode\")]\n    pub fn set_alpha_mode(this: &GpuCanvasConfiguration, val: GpuCanvasAlphaMode);\n\n    #[doc = \"Get the `device` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"device\")]\n    pub fn get_device(this: &GpuCanvasConfiguration) -> GpuDevice;\n\n    #[doc = \"Change the `device` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"device\")]\n    pub fn set_device(this: &GpuCanvasConfiguration, val: &GpuDevice);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuCanvasConfiguration) -> GpuTextureFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuCanvasConfiguration, val: GpuTextureFormat);\n\n    #[doc = \"Get the `toneMapping` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuCanvasToneMapping`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"toneMapping\")]\n    pub fn get_tone_mapping(this: &GpuCanvasConfiguration) -> Option<GpuCanvasToneMapping>;\n\n    #[doc = \"Change the `toneMapping` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuCanvasToneMapping`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"toneMapping\")]\n    pub fn set_tone_mapping(this: &GpuCanvasConfiguration, val: &GpuCanvasToneMapping);\n\n    #[doc = \"Get the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"usage\")]\n    pub fn get_usage(this: &GpuCanvasConfiguration) -> Option<u32>;\n\n    #[doc = \"Change the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"usage\")]\n    pub fn set_usage(this: &GpuCanvasConfiguration, val: u32);\n\n    #[doc = \"Get the `viewFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"viewFormats\")]\n    pub fn get_view_formats(this: &GpuCanvasConfiguration) -> Option<::js_sys::Array>;\n\n    #[doc = \"Change the `viewFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"viewFormats\")]\n    pub fn set_view_formats(this: &GpuCanvasConfiguration, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuCanvasConfiguration {\n    #[doc = \"Construct a new `GpuCanvasConfiguration`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuDevice`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(device: &GpuDevice, format: GpuTextureFormat) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_device(device);\n        ret.set_format(format);\n        ret\n    }\n\n    #[deprecated = \"Use `set_alpha_mode()` instead.\"]\n    pub fn alpha_mode(&mut self, val: GpuCanvasAlphaMode) -> &mut Self {\n        self.set_alpha_mode(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_device()` instead.\"]\n    pub fn device(&mut self, val: &GpuDevice) -> &mut Self {\n        self.set_device(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_tone_mapping()` instead.\"]\n    pub fn tone_mapping(&mut self, val: &GpuCanvasToneMapping) -> &mut Self {\n        self.set_tone_mapping(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_usage()` instead.\"]\n    pub fn usage(&mut self, val: u32) -> &mut Self {\n        self.set_usage(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view_formats()` instead.\"]\n    pub fn view_formats(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_view_formats(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCanvasContext.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCanvasContext , typescript_type = \"GPUCanvasContext\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCanvasContext` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasContext`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCanvasContext;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCanvasContext\" , js_name = canvas)]\n    #[doc = \"Getter for the `canvas` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/canvas)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasContext`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn canvas(this: &GpuCanvasContext) -> ::js_sys::Object;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCanvasContext\" , js_name = configure)]\n    #[doc = \"The `configure()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/configure)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuCanvasContext`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn configure(\n        this: &GpuCanvasContext,\n        configuration: &GpuCanvasConfiguration,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCanvasContext\" , js_name = getConfiguration)]\n    #[doc = \"The `getConfiguration()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/getConfiguration)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasConfiguration`, `GpuCanvasContext`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_configuration(this: &GpuCanvasContext) -> Option<GpuCanvasConfiguration>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCanvasContext\" , js_name = getCurrentTexture)]\n    #[doc = \"The `getCurrentTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/getCurrentTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasContext`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_current_texture(this: &GpuCanvasContext) -> Result<GpuTexture, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCanvasContext\" , js_name = unconfigure)]\n    #[doc = \"The `unconfigure()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/unconfigure)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasContext`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn unconfigure(this: &GpuCanvasContext);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCanvasToneMapping.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCanvasToneMapping)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCanvasToneMapping` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasToneMapping`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCanvasToneMapping;\n\n    #[doc = \"Get the `mode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasToneMapping`, `GpuCanvasToneMappingMode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mode\")]\n    pub fn get_mode(this: &GpuCanvasToneMapping) -> Option<GpuCanvasToneMappingMode>;\n\n    #[doc = \"Change the `mode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasToneMapping`, `GpuCanvasToneMappingMode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mode\")]\n    pub fn set_mode(this: &GpuCanvasToneMapping, val: GpuCanvasToneMappingMode);\n}\n\nimpl GpuCanvasToneMapping {\n    #[doc = \"Construct a new `GpuCanvasToneMapping`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCanvasToneMapping`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_mode()` instead.\"]\n    pub fn mode(&mut self, val: GpuCanvasToneMappingMode) -> &mut Self {\n        self.set_mode(val);\n        self\n    }\n}\n\nimpl Default for GpuCanvasToneMapping {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCanvasToneMappingMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuCanvasToneMappingMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuCanvasToneMappingMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuCanvasToneMappingMode {\n    Standard = \"standard\",\n    Extended = \"extended\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuColorDict.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUColorDict)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuColorDict` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuColorDict;\n\n    #[doc = \"Get the `a` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"a\")]\n    pub fn get_a(this: &GpuColorDict) -> f64;\n\n    #[doc = \"Change the `a` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"a\")]\n    pub fn set_a(this: &GpuColorDict, val: f64);\n\n    #[doc = \"Get the `b` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"b\")]\n    pub fn get_b(this: &GpuColorDict) -> f64;\n\n    #[doc = \"Change the `b` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"b\")]\n    pub fn set_b(this: &GpuColorDict, val: f64);\n\n    #[doc = \"Get the `g` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"g\")]\n    pub fn get_g(this: &GpuColorDict) -> f64;\n\n    #[doc = \"Change the `g` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"g\")]\n    pub fn set_g(this: &GpuColorDict, val: f64);\n\n    #[doc = \"Get the `r` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"r\")]\n    pub fn get_r(this: &GpuColorDict) -> f64;\n\n    #[doc = \"Change the `r` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"r\")]\n    pub fn set_r(this: &GpuColorDict, val: f64);\n}\n\nimpl GpuColorDict {\n    #[doc = \"Construct a new `GpuColorDict`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(a: f64, b: f64, g: f64, r: f64) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_a(a);\n        ret.set_b(b);\n        ret.set_g(g);\n        ret.set_r(r);\n        ret\n    }\n\n    #[deprecated = \"Use `set_a()` instead.\"]\n    pub fn a(&mut self, val: f64) -> &mut Self {\n        self.set_a(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_b()` instead.\"]\n    pub fn b(&mut self, val: f64) -> &mut Self {\n        self.set_b(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_g()` instead.\"]\n    pub fn g(&mut self, val: f64) -> &mut Self {\n        self.set_g(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_r()` instead.\"]\n    pub fn r(&mut self, val: f64) -> &mut Self {\n        self.set_r(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuColorTargetState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUColorTargetState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuColorTargetState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuColorTargetState;\n\n    #[doc = \"Get the `blend` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendState`, `GpuColorTargetState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"blend\")]\n    pub fn get_blend(this: &GpuColorTargetState) -> Option<GpuBlendState>;\n\n    #[doc = \"Change the `blend` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBlendState`, `GpuColorTargetState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"blend\")]\n    pub fn set_blend(this: &GpuColorTargetState, val: &GpuBlendState);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuColorTargetState) -> GpuTextureFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuColorTargetState, val: GpuTextureFormat);\n\n    #[doc = \"Get the `writeMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"writeMask\")]\n    pub fn get_write_mask(this: &GpuColorTargetState) -> Option<u32>;\n\n    #[doc = \"Change the `writeMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"writeMask\")]\n    pub fn set_write_mask(this: &GpuColorTargetState, val: u32);\n}\n\nimpl GpuColorTargetState {\n    #[doc = \"Construct a new `GpuColorTargetState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorTargetState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(format: GpuTextureFormat) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_format(format);\n        ret\n    }\n\n    #[deprecated = \"Use `set_blend()` instead.\"]\n    pub fn blend(&mut self, val: &GpuBlendState) -> &mut Self {\n        self.set_blend(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_write_mask()` instead.\"]\n    pub fn write_mask(&mut self, val: u32) -> &mut Self {\n        self.set_write_mask(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCommandBuffer.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCommandBuffer , typescript_type = \"GPUCommandBuffer\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCommandBuffer` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCommandBuffer;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCommandBuffer\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandBuffer/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuCommandBuffer) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUCommandBuffer\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandBuffer/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBuffer`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuCommandBuffer, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCommandBufferDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCommandBufferDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCommandBufferDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCommandBufferDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuCommandBufferDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuCommandBufferDescriptor, val: &str);\n}\n\nimpl GpuCommandBufferDescriptor {\n    #[doc = \"Construct a new `GpuCommandBufferDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBufferDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n}\n\nimpl Default for GpuCommandBufferDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCommandEncoder.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCommandEncoder , typescript_type = \"GPUCommandEncoder\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCommandEncoder` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCommandEncoder;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCommandEncoder\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuCommandEncoder) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUCommandEncoder\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuCommandEncoder, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = beginComputePass)]\n    #[doc = \"The `beginComputePass()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/beginComputePass)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn begin_compute_pass(this: &GpuCommandEncoder) -> GpuComputePassEncoder;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = beginComputePass)]\n    #[doc = \"The `beginComputePass()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/beginComputePass)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuComputePassDescriptor`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn begin_compute_pass_with_descriptor(\n        this: &GpuCommandEncoder,\n        descriptor: &GpuComputePassDescriptor,\n    ) -> GpuComputePassEncoder;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = beginRenderPass)]\n    #[doc = \"The `beginRenderPass()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/beginRenderPass)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuRenderPassDescriptor`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn begin_render_pass(\n        this: &GpuCommandEncoder,\n        descriptor: &GpuRenderPassDescriptor,\n    ) -> Result<GpuRenderPassEncoder, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer(this: &GpuCommandEncoder, buffer: &GpuBuffer);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_u32(this: &GpuCommandEncoder, buffer: &GpuBuffer, offset: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_f64(this: &GpuCommandEncoder, buffer: &GpuBuffer, offset: f64);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_u32_and_u32(\n        this: &GpuCommandEncoder,\n        buffer: &GpuBuffer,\n        offset: u32,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_f64_and_u32(\n        this: &GpuCommandEncoder,\n        buffer: &GpuBuffer,\n        offset: f64,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_u32_and_f64(\n        this: &GpuCommandEncoder,\n        buffer: &GpuBuffer,\n        offset: u32,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = clearBuffer)]\n    #[doc = \"The `clearBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/clearBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn clear_buffer_with_f64_and_f64(\n        this: &GpuCommandEncoder,\n        buffer: &GpuBuffer,\n        offset: f64,\n        size: f64,\n    );\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_u32_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_u32_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_f64_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_f64_and_u32(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_u32_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_u32_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_u32_and_f64_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: u32,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToBuffer)]\n    #[doc = \"The `copyBufferToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_buffer_with_f64_and_f64_and_f64(\n        this: &GpuCommandEncoder,\n        source: &GpuBuffer,\n        source_offset: f64,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToTexture)]\n    #[doc = \"The `copyBufferToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuTexelCopyBufferInfo`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_texture_with_u32_sequence(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyBufferInfo,\n        destination: &GpuTexelCopyTextureInfo,\n        copy_size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyBufferToTexture)]\n    #[doc = \"The `copyBufferToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuExtent3dDict`, `GpuTexelCopyBufferInfo`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_buffer_to_texture_with_gpu_extent_3d_dict(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyBufferInfo,\n        destination: &GpuTexelCopyTextureInfo,\n        copy_size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyTextureToBuffer)]\n    #[doc = \"The `copyTextureToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyTextureToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuTexelCopyBufferInfo`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_texture_to_buffer_with_u32_sequence(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyTextureInfo,\n        destination: &GpuTexelCopyBufferInfo,\n        copy_size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyTextureToBuffer)]\n    #[doc = \"The `copyTextureToBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyTextureToBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuExtent3dDict`, `GpuTexelCopyBufferInfo`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_texture_to_buffer_with_gpu_extent_3d_dict(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyTextureInfo,\n        destination: &GpuTexelCopyBufferInfo,\n        copy_size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyTextureToTexture)]\n    #[doc = \"The `copyTextureToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyTextureToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_texture_to_texture_with_u32_sequence(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyTextureInfo,\n        destination: &GpuTexelCopyTextureInfo,\n        copy_size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUCommandEncoder\" , js_name = copyTextureToTexture)]\n    #[doc = \"The `copyTextureToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyTextureToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuExtent3dDict`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_texture_to_texture_with_gpu_extent_3d_dict(\n        this: &GpuCommandEncoder,\n        source: &GpuTexelCopyTextureInfo,\n        destination: &GpuTexelCopyTextureInfo,\n        copy_size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = finish)]\n    #[doc = \"The `finish()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/finish)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBuffer`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn finish(this: &GpuCommandEncoder) -> GpuCommandBuffer;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = finish)]\n    #[doc = \"The `finish()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/finish)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandBuffer`, `GpuCommandBufferDescriptor`, `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn finish_with_descriptor(\n        this: &GpuCommandEncoder,\n        descriptor: &GpuCommandBufferDescriptor,\n    ) -> GpuCommandBuffer;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = resolveQuerySet)]\n    #[doc = \"The `resolveQuerySet()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/resolveQuerySet)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`, `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn resolve_query_set_with_u32(\n        this: &GpuCommandEncoder,\n        query_set: &GpuQuerySet,\n        first_query: u32,\n        query_count: u32,\n        destination: &GpuBuffer,\n        destination_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = resolveQuerySet)]\n    #[doc = \"The `resolveQuerySet()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/resolveQuerySet)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuCommandEncoder`, `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn resolve_query_set_with_f64(\n        this: &GpuCommandEncoder,\n        query_set: &GpuQuerySet,\n        first_query: u32,\n        query_count: u32,\n        destination: &GpuBuffer,\n        destination_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = insertDebugMarker)]\n    #[doc = \"The `insertDebugMarker()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/insertDebugMarker)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn insert_debug_marker(this: &GpuCommandEncoder, marker_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = popDebugGroup)]\n    #[doc = \"The `popDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/popDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn pop_debug_group(this: &GpuCommandEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUCommandEncoder\" , js_name = pushDebugGroup)]\n    #[doc = \"The `pushDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/pushDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn push_debug_group(this: &GpuCommandEncoder, group_label: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCommandEncoderDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCommandEncoderDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCommandEncoderDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCommandEncoderDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuCommandEncoderDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuCommandEncoderDescriptor, val: &str);\n}\n\nimpl GpuCommandEncoderDescriptor {\n    #[doc = \"Construct a new `GpuCommandEncoderDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n}\n\nimpl Default for GpuCommandEncoderDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCompareFunction.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuCompareFunction` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuCompareFunction {\n    Never = \"never\",\n    Less = \"less\",\n    Equal = \"equal\",\n    LessEqual = \"less-equal\",\n    Greater = \"greater\",\n    NotEqual = \"not-equal\",\n    GreaterEqual = \"greater-equal\",\n    Always = \"always\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCompilationInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCompilationInfo , typescript_type = \"GPUCompilationInfo\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCompilationInfo` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationInfo)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCompilationInfo;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationInfo\" , js_name = messages)]\n    #[doc = \"Getter for the `messages` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationInfo/messages)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn messages(this: &GpuCompilationInfo) -> ::js_sys::Array;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCompilationMessage.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCompilationMessage , typescript_type = \"GPUCompilationMessage\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCompilationMessage` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCompilationMessage;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = message)]\n    #[doc = \"Getter for the `message` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/message)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn message(this: &GpuCompilationMessage) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = type)]\n    #[doc = \"Getter for the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/type)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`, `GpuCompilationMessageType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn type_(this: &GpuCompilationMessage) -> GpuCompilationMessageType;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = lineNum)]\n    #[doc = \"Getter for the `lineNum` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/lineNum)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn line_num(this: &GpuCompilationMessage) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = linePos)]\n    #[doc = \"Getter for the `linePos` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/linePos)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn line_pos(this: &GpuCompilationMessage) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = offset)]\n    #[doc = \"Getter for the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/offset)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn offset(this: &GpuCompilationMessage) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUCompilationMessage\" , js_name = length)]\n    #[doc = \"Getter for the `length` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationMessage/length)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn length(this: &GpuCompilationMessage) -> f64;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCompilationMessageType.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuCompilationMessageType` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuCompilationMessageType`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuCompilationMessageType {\n    Error = \"error\",\n    Warning = \"warning\",\n    Info = \"info\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuComputePassDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUComputePassDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuComputePassDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuComputePassDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuComputePassDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuComputePassDescriptor, val: &str);\n\n    #[doc = \"Get the `timestampWrites` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`, `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"timestampWrites\")]\n    pub fn get_timestamp_writes(\n        this: &GpuComputePassDescriptor,\n    ) -> Option<GpuComputePassTimestampWrites>;\n\n    #[doc = \"Change the `timestampWrites` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`, `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"timestampWrites\")]\n    pub fn set_timestamp_writes(\n        this: &GpuComputePassDescriptor,\n        val: &GpuComputePassTimestampWrites,\n    );\n}\n\nimpl GpuComputePassDescriptor {\n    #[doc = \"Construct a new `GpuComputePassDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_timestamp_writes()` instead.\"]\n    pub fn timestamp_writes(&mut self, val: &GpuComputePassTimestampWrites) -> &mut Self {\n        self.set_timestamp_writes(val);\n        self\n    }\n}\n\nimpl Default for GpuComputePassDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuComputePassEncoder.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUComputePassEncoder , typescript_type = \"GPUComputePassEncoder\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuComputePassEncoder` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuComputePassEncoder;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUComputePassEncoder\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuComputePassEncoder) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUComputePassEncoder\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuComputePassEncoder, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = dispatchWorkgroups)]\n    #[doc = \"The `dispatchWorkgroups()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/dispatchWorkgroups)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dispatch_workgroups(this: &GpuComputePassEncoder, workgroup_count_x: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = dispatchWorkgroups)]\n    #[doc = \"The `dispatchWorkgroups()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/dispatchWorkgroups)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dispatch_workgroups_with_workgroup_count_y(\n        this: &GpuComputePassEncoder,\n        workgroup_count_x: u32,\n        workgroup_count_y: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = dispatchWorkgroups)]\n    #[doc = \"The `dispatchWorkgroups()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/dispatchWorkgroups)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dispatch_workgroups_with_workgroup_count_y_and_workgroup_count_z(\n        this: &GpuComputePassEncoder,\n        workgroup_count_x: u32,\n        workgroup_count_y: u32,\n        workgroup_count_z: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = dispatchWorkgroupsIndirect)]\n    #[doc = \"The `dispatchWorkgroupsIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/dispatchWorkgroupsIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dispatch_workgroups_indirect_with_u32(\n        this: &GpuComputePassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = dispatchWorkgroupsIndirect)]\n    #[doc = \"The `dispatchWorkgroupsIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/dispatchWorkgroupsIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dispatch_workgroups_indirect_with_f64(\n        this: &GpuComputePassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = end)]\n    #[doc = \"The `end()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/end)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn end(this: &GpuComputePassEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setPipeline)]\n    #[doc = \"The `setPipeline()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setPipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`, `GpuComputePipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_pipeline(this: &GpuComputePassEncoder, pipeline: &GpuComputePipeline);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_sequence(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets: &::wasm_bindgen::JsValue,\n    );\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUComputePassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuComputePassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = insertDebugMarker)]\n    #[doc = \"The `insertDebugMarker()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/insertDebugMarker)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn insert_debug_marker(this: &GpuComputePassEncoder, marker_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = popDebugGroup)]\n    #[doc = \"The `popDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/popDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn pop_debug_group(this: &GpuComputePassEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePassEncoder\" , js_name = pushDebugGroup)]\n    #[doc = \"The `pushDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePassEncoder/pushDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn push_debug_group(this: &GpuComputePassEncoder, group_label: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuComputePassTimestampWrites.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUComputePassTimestampWrites)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuComputePassTimestampWrites` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuComputePassTimestampWrites;\n\n    #[doc = \"Get the `beginningOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"beginningOfPassWriteIndex\")]\n    pub fn get_beginning_of_pass_write_index(this: &GpuComputePassTimestampWrites) -> Option<u32>;\n\n    #[doc = \"Change the `beginningOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"beginningOfPassWriteIndex\")]\n    pub fn set_beginning_of_pass_write_index(this: &GpuComputePassTimestampWrites, val: u32);\n\n    #[doc = \"Get the `endOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"endOfPassWriteIndex\")]\n    pub fn get_end_of_pass_write_index(this: &GpuComputePassTimestampWrites) -> Option<u32>;\n\n    #[doc = \"Change the `endOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"endOfPassWriteIndex\")]\n    pub fn set_end_of_pass_write_index(this: &GpuComputePassTimestampWrites, val: u32);\n\n    #[doc = \"Get the `querySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`, `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"querySet\")]\n    pub fn get_query_set(this: &GpuComputePassTimestampWrites) -> GpuQuerySet;\n\n    #[doc = \"Change the `querySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`, `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"querySet\")]\n    pub fn set_query_set(this: &GpuComputePassTimestampWrites, val: &GpuQuerySet);\n}\n\nimpl GpuComputePassTimestampWrites {\n    #[doc = \"Construct a new `GpuComputePassTimestampWrites`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePassTimestampWrites`, `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(query_set: &GpuQuerySet) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_query_set(query_set);\n        ret\n    }\n\n    #[deprecated = \"Use `set_beginning_of_pass_write_index()` instead.\"]\n    pub fn beginning_of_pass_write_index(&mut self, val: u32) -> &mut Self {\n        self.set_beginning_of_pass_write_index(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_end_of_pass_write_index()` instead.\"]\n    pub fn end_of_pass_write_index(&mut self, val: u32) -> &mut Self {\n        self.set_end_of_pass_write_index(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_query_set()` instead.\"]\n    pub fn query_set(&mut self, val: &GpuQuerySet) -> &mut Self {\n        self.set_query_set(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuComputePipeline.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUComputePipeline , typescript_type = \"GPUComputePipeline\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuComputePipeline` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuComputePipeline;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUComputePipeline\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePipeline/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuComputePipeline) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUComputePipeline\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePipeline/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuComputePipeline, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUComputePipeline\" , js_name = getBindGroupLayout)]\n    #[doc = \"The `getBindGroupLayout()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUComputePipeline/getBindGroupLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`, `GpuComputePipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_bind_group_layout(this: &GpuComputePipeline, index: u32) -> GpuBindGroupLayout;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuComputePipelineDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUComputePipelineDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuComputePipelineDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuComputePipelineDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuComputePipelineDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuComputePipelineDescriptor, val: &str);\n\n    #[doc = \"Get the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"layout\")]\n    pub fn get_layout(this: &GpuComputePipelineDescriptor) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"layout\")]\n    pub fn set_layout(this: &GpuComputePipelineDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `compute` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`, `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"compute\")]\n    pub fn get_compute(this: &GpuComputePipelineDescriptor) -> GpuProgrammableStage;\n\n    #[doc = \"Change the `compute` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`, `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"compute\")]\n    pub fn set_compute(this: &GpuComputePipelineDescriptor, val: &GpuProgrammableStage);\n}\n\nimpl GpuComputePipelineDescriptor {\n    #[doc = \"Construct a new `GpuComputePipelineDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`, `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(layout: &::wasm_bindgen::JsValue, compute: &GpuProgrammableStage) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_layout(layout);\n        ret.set_compute(compute);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_layout()` instead.\"]\n    pub fn layout(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_layout(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_compute()` instead.\"]\n    pub fn compute(&mut self, val: &GpuProgrammableStage) -> &mut Self {\n        self.set_compute(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCopyExternalImageDestInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCopyExternalImageDestInfo)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCopyExternalImageDestInfo` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCopyExternalImageDestInfo;\n\n    #[doc = \"Get the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuTextureAspect`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"aspect\")]\n    pub fn get_aspect(this: &GpuCopyExternalImageDestInfo) -> Option<GpuTextureAspect>;\n\n    #[doc = \"Change the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuTextureAspect`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"aspect\")]\n    pub fn set_aspect(this: &GpuCopyExternalImageDestInfo, val: GpuTextureAspect);\n\n    #[doc = \"Get the `mipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mipLevel\")]\n    pub fn get_mip_level(this: &GpuCopyExternalImageDestInfo) -> Option<u32>;\n\n    #[doc = \"Change the `mipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mipLevel\")]\n    pub fn set_mip_level(this: &GpuCopyExternalImageDestInfo, val: u32);\n\n    #[doc = \"Get the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"origin\")]\n    pub fn get_origin(this: &GpuCopyExternalImageDestInfo) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"origin\")]\n    pub fn set_origin(this: &GpuCopyExternalImageDestInfo, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"texture\")]\n    pub fn get_texture(this: &GpuCopyExternalImageDestInfo) -> GpuTexture;\n\n    #[doc = \"Change the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"texture\")]\n    pub fn set_texture(this: &GpuCopyExternalImageDestInfo, val: &GpuTexture);\n\n    #[doc = \"Get the `premultipliedAlpha` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"premultipliedAlpha\")]\n    pub fn get_premultiplied_alpha(this: &GpuCopyExternalImageDestInfo) -> Option<bool>;\n\n    #[doc = \"Change the `premultipliedAlpha` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"premultipliedAlpha\")]\n    pub fn set_premultiplied_alpha(this: &GpuCopyExternalImageDestInfo, val: bool);\n}\n\nimpl GpuCopyExternalImageDestInfo {\n    #[doc = \"Construct a new `GpuCopyExternalImageDestInfo`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(texture: &GpuTexture) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_texture(texture);\n        ret\n    }\n\n    #[deprecated = \"Use `set_aspect()` instead.\"]\n    pub fn aspect(&mut self, val: GpuTextureAspect) -> &mut Self {\n        self.set_aspect(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mip_level()` instead.\"]\n    pub fn mip_level(&mut self, val: u32) -> &mut Self {\n        self.set_mip_level(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_origin()` instead.\"]\n    pub fn origin(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_origin(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_texture()` instead.\"]\n    pub fn texture(&mut self, val: &GpuTexture) -> &mut Self {\n        self.set_texture(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_premultiplied_alpha()` instead.\"]\n    pub fn premultiplied_alpha(&mut self, val: bool) -> &mut Self {\n        self.set_premultiplied_alpha(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCopyExternalImageSourceInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUCopyExternalImageSourceInfo)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuCopyExternalImageSourceInfo` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuCopyExternalImageSourceInfo;\n\n    #[doc = \"Get the `flipY` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"flipY\")]\n    pub fn get_flip_y(this: &GpuCopyExternalImageSourceInfo) -> Option<bool>;\n\n    #[doc = \"Change the `flipY` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"flipY\")]\n    pub fn set_flip_y(this: &GpuCopyExternalImageSourceInfo, val: bool);\n\n    #[doc = \"Get the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"origin\")]\n    pub fn get_origin(this: &GpuCopyExternalImageSourceInfo) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"origin\")]\n    pub fn set_origin(this: &GpuCopyExternalImageSourceInfo, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `source` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"source\")]\n    pub fn get_source(this: &GpuCopyExternalImageSourceInfo) -> ::js_sys::Object;\n\n    #[doc = \"Change the `source` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"source\")]\n    pub fn set_source(this: &GpuCopyExternalImageSourceInfo, val: &::js_sys::Object);\n}\n\nimpl GpuCopyExternalImageSourceInfo {\n    #[doc = \"Construct a new `GpuCopyExternalImageSourceInfo`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageSourceInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(source: &::js_sys::Object) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_source(source);\n        ret\n    }\n\n    #[deprecated = \"Use `set_flip_y()` instead.\"]\n    pub fn flip_y(&mut self, val: bool) -> &mut Self {\n        self.set_flip_y(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_origin()` instead.\"]\n    pub fn origin(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_origin(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_source()` instead.\"]\n    pub fn source(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_source(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuCullMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuCullMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuCullMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuCullMode {\n    None = \"none\",\n    Front = \"front\",\n    Back = \"back\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuDepthStencilState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUDepthStencilState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuDepthStencilState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuDepthStencilState;\n\n    #[doc = \"Get the `depthBias` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthBias\")]\n    pub fn get_depth_bias(this: &GpuDepthStencilState) -> Option<i32>;\n\n    #[doc = \"Change the `depthBias` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthBias\")]\n    pub fn set_depth_bias(this: &GpuDepthStencilState, val: i32);\n\n    #[doc = \"Get the `depthBiasClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthBiasClamp\")]\n    pub fn get_depth_bias_clamp(this: &GpuDepthStencilState) -> Option<f32>;\n\n    #[doc = \"Change the `depthBiasClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthBiasClamp\")]\n    pub fn set_depth_bias_clamp(this: &GpuDepthStencilState, val: f32);\n\n    #[doc = \"Get the `depthBiasSlopeScale` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthBiasSlopeScale\")]\n    pub fn get_depth_bias_slope_scale(this: &GpuDepthStencilState) -> Option<f32>;\n\n    #[doc = \"Change the `depthBiasSlopeScale` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthBiasSlopeScale\")]\n    pub fn set_depth_bias_slope_scale(this: &GpuDepthStencilState, val: f32);\n\n    #[doc = \"Get the `depthCompare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthCompare\")]\n    pub fn get_depth_compare(this: &GpuDepthStencilState) -> Option<GpuCompareFunction>;\n\n    #[doc = \"Change the `depthCompare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthCompare\")]\n    pub fn set_depth_compare(this: &GpuDepthStencilState, val: GpuCompareFunction);\n\n    #[doc = \"Get the `depthWriteEnabled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthWriteEnabled\")]\n    pub fn get_depth_write_enabled(this: &GpuDepthStencilState) -> Option<bool>;\n\n    #[doc = \"Change the `depthWriteEnabled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthWriteEnabled\")]\n    pub fn set_depth_write_enabled(this: &GpuDepthStencilState, val: bool);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuDepthStencilState) -> GpuTextureFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuDepthStencilState, val: GpuTextureFormat);\n\n    #[doc = \"Get the `stencilBack` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilBack\")]\n    pub fn get_stencil_back(this: &GpuDepthStencilState) -> Option<GpuStencilFaceState>;\n\n    #[doc = \"Change the `stencilBack` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilBack\")]\n    pub fn set_stencil_back(this: &GpuDepthStencilState, val: &GpuStencilFaceState);\n\n    #[doc = \"Get the `stencilFront` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilFront\")]\n    pub fn get_stencil_front(this: &GpuDepthStencilState) -> Option<GpuStencilFaceState>;\n\n    #[doc = \"Change the `stencilFront` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilFront\")]\n    pub fn set_stencil_front(this: &GpuDepthStencilState, val: &GpuStencilFaceState);\n\n    #[doc = \"Get the `stencilReadMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilReadMask\")]\n    pub fn get_stencil_read_mask(this: &GpuDepthStencilState) -> Option<u32>;\n\n    #[doc = \"Change the `stencilReadMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilReadMask\")]\n    pub fn set_stencil_read_mask(this: &GpuDepthStencilState, val: u32);\n\n    #[doc = \"Get the `stencilWriteMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilWriteMask\")]\n    pub fn get_stencil_write_mask(this: &GpuDepthStencilState) -> Option<u32>;\n\n    #[doc = \"Change the `stencilWriteMask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilWriteMask\")]\n    pub fn set_stencil_write_mask(this: &GpuDepthStencilState, val: u32);\n}\n\nimpl GpuDepthStencilState {\n    #[doc = \"Construct a new `GpuDepthStencilState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(format: GpuTextureFormat) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_format(format);\n        ret\n    }\n\n    #[deprecated = \"Use `set_depth_bias()` instead.\"]\n    pub fn depth_bias(&mut self, val: i32) -> &mut Self {\n        self.set_depth_bias(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_bias_clamp()` instead.\"]\n    pub fn depth_bias_clamp(&mut self, val: f32) -> &mut Self {\n        self.set_depth_bias_clamp(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_bias_slope_scale()` instead.\"]\n    pub fn depth_bias_slope_scale(&mut self, val: f32) -> &mut Self {\n        self.set_depth_bias_slope_scale(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_compare()` instead.\"]\n    pub fn depth_compare(&mut self, val: GpuCompareFunction) -> &mut Self {\n        self.set_depth_compare(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_write_enabled()` instead.\"]\n    pub fn depth_write_enabled(&mut self, val: bool) -> &mut Self {\n        self.set_depth_write_enabled(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_back()` instead.\"]\n    pub fn stencil_back(&mut self, val: &GpuStencilFaceState) -> &mut Self {\n        self.set_stencil_back(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_front()` instead.\"]\n    pub fn stencil_front(&mut self, val: &GpuStencilFaceState) -> &mut Self {\n        self.set_stencil_front(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_read_mask()` instead.\"]\n    pub fn stencil_read_mask(&mut self, val: u32) -> &mut Self {\n        self.set_stencil_read_mask(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_write_mask()` instead.\"]\n    pub fn stencil_write_mask(&mut self, val: u32) -> &mut Self {\n        self.set_stencil_write_mask(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuDevice.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = EventTarget , extends = :: js_sys :: Object , js_name = GPUDevice , typescript_type = \"GPUDevice\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuDevice` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuDevice;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = features)]\n    #[doc = \"Getter for the `features` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/features)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn features(this: &GpuDevice) -> GpuSupportedFeatures;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = limits)]\n    #[doc = \"Getter for the `limits` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/limits)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn limits(this: &GpuDevice) -> GpuSupportedLimits;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = adapterInfo)]\n    #[doc = \"Getter for the `adapterInfo` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/adapterInfo)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAdapterInfo`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn adapter_info(this: &GpuDevice) -> GpuAdapterInfo;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = queue)]\n    #[doc = \"Getter for the `queue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/queue)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn queue(this: &GpuDevice) -> GpuQueue;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = lost)]\n    #[doc = \"Getter for the `lost` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/lost)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn lost(this: &GpuDevice) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = onuncapturederror)]\n    #[doc = \"Getter for the `onuncapturederror` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/onuncapturederror)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn onuncapturederror(this: &GpuDevice) -> Option<::js_sys::Function>;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUDevice\" , js_name = onuncapturederror)]\n    #[doc = \"Setter for the `onuncapturederror` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/onuncapturederror)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_onuncapturederror(this: &GpuDevice, value: Option<&::js_sys::Function>);\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDevice\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuDevice) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUDevice\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuDevice, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createBindGroup)]\n    #[doc = \"The `createBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuBindGroupDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_bind_group(this: &GpuDevice, descriptor: &GpuBindGroupDescriptor)\n        -> GpuBindGroup;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createBindGroupLayout)]\n    #[doc = \"The `createBindGroupLayout()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBindGroupLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`, `GpuBindGroupLayoutDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_bind_group_layout(\n        this: &GpuDevice,\n        descriptor: &GpuBindGroupLayoutDescriptor,\n    ) -> Result<GpuBindGroupLayout, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createBuffer)]\n    #[doc = \"The `createBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuBufferDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_buffer(\n        this: &GpuDevice,\n        descriptor: &GpuBufferDescriptor,\n    ) -> Result<GpuBuffer, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createCommandEncoder)]\n    #[doc = \"The `createCommandEncoder()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createCommandEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_command_encoder(this: &GpuDevice) -> GpuCommandEncoder;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createCommandEncoder)]\n    #[doc = \"The `createCommandEncoder()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createCommandEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCommandEncoder`, `GpuCommandEncoderDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_command_encoder_with_descriptor(\n        this: &GpuDevice,\n        descriptor: &GpuCommandEncoderDescriptor,\n    ) -> GpuCommandEncoder;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createComputePipeline)]\n    #[doc = \"The `createComputePipeline()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createComputePipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipeline`, `GpuComputePipelineDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_compute_pipeline(\n        this: &GpuDevice,\n        descriptor: &GpuComputePipelineDescriptor,\n    ) -> GpuComputePipeline;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createComputePipelineAsync)]\n    #[doc = \"The `createComputePipelineAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createComputePipelineAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuComputePipelineDescriptor`, `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_compute_pipeline_async(\n        this: &GpuDevice,\n        descriptor: &GpuComputePipelineDescriptor,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createPipelineLayout)]\n    #[doc = \"The `createPipelineLayout()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createPipelineLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuPipelineLayout`, `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_pipeline_layout(\n        this: &GpuDevice,\n        descriptor: &GpuPipelineLayoutDescriptor,\n    ) -> GpuPipelineLayout;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createQuerySet)]\n    #[doc = \"The `createQuerySet()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createQuerySet)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuQuerySet`, `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_query_set(\n        this: &GpuDevice,\n        descriptor: &GpuQuerySetDescriptor,\n    ) -> Result<GpuQuerySet, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createRenderBundleEncoder)]\n    #[doc = \"The `createRenderBundleEncoder()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createRenderBundleEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuRenderBundleEncoder`, `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_render_bundle_encoder(\n        this: &GpuDevice,\n        descriptor: &GpuRenderBundleEncoderDescriptor,\n    ) -> Result<GpuRenderBundleEncoder, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createRenderPipeline)]\n    #[doc = \"The `createRenderPipeline()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createRenderPipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuRenderPipeline`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_render_pipeline(\n        this: &GpuDevice,\n        descriptor: &GpuRenderPipelineDescriptor,\n    ) -> Result<GpuRenderPipeline, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createRenderPipelineAsync)]\n    #[doc = \"The `createRenderPipelineAsync()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createRenderPipelineAsync)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_render_pipeline_async(\n        this: &GpuDevice,\n        descriptor: &GpuRenderPipelineDescriptor,\n    ) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createSampler)]\n    #[doc = \"The `createSampler()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createSampler)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuSampler`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_sampler(this: &GpuDevice) -> GpuSampler;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createSampler)]\n    #[doc = \"The `createSampler()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createSampler)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuSampler`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_sampler_with_descriptor(\n        this: &GpuDevice,\n        descriptor: &GpuSamplerDescriptor,\n    ) -> GpuSampler;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = createShaderModule)]\n    #[doc = \"The `createShaderModule()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createShaderModule)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuShaderModule`, `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_shader_module(\n        this: &GpuDevice,\n        descriptor: &GpuShaderModuleDescriptor,\n    ) -> GpuShaderModule;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = createTexture)]\n    #[doc = \"The `createTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuTexture`, `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_texture(\n        this: &GpuDevice,\n        descriptor: &GpuTextureDescriptor,\n    ) -> Result<GpuTexture, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = destroy)]\n    #[doc = \"The `destroy()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/destroy)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn destroy(this: &GpuDevice);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUDevice\" , js_name = importExternalTexture)]\n    #[doc = \"The `importExternalTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/importExternalTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuExternalTexture`, `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn import_external_texture(\n        this: &GpuDevice,\n        descriptor: &GpuExternalTextureDescriptor,\n    ) -> Result<GpuExternalTexture, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = popErrorScope)]\n    #[doc = \"The `popErrorScope()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/popErrorScope)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn pop_error_scope(this: &GpuDevice) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUDevice\" , js_name = pushErrorScope)]\n    #[doc = \"The `pushErrorScope()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/pushErrorScope)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDevice`, `GpuErrorFilter`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn push_error_scope(this: &GpuDevice, filter: GpuErrorFilter);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuDeviceDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUDeviceDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuDeviceDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuDeviceDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuDeviceDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuDeviceDescriptor, val: &str);\n\n    #[doc = \"Get the `defaultQueue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`, `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"defaultQueue\")]\n    pub fn get_default_queue(this: &GpuDeviceDescriptor) -> Option<GpuQueueDescriptor>;\n\n    #[doc = \"Change the `defaultQueue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`, `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"defaultQueue\")]\n    pub fn set_default_queue(this: &GpuDeviceDescriptor, val: &GpuQueueDescriptor);\n\n    #[doc = \"Get the `requiredFeatures` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"requiredFeatures\")]\n    pub fn get_required_features(this: &GpuDeviceDescriptor) -> Option<::js_sys::Array>;\n\n    #[doc = \"Change the `requiredFeatures` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"requiredFeatures\")]\n    pub fn set_required_features(this: &GpuDeviceDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `requiredLimits` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"requiredLimits\")]\n    pub fn get_required_limits(this: &GpuDeviceDescriptor) -> Option<::js_sys::Object>;\n\n    #[doc = \"Change the `requiredLimits` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"requiredLimits\")]\n    pub fn set_required_limits(this: &GpuDeviceDescriptor, val: &::js_sys::Object);\n}\n\nimpl GpuDeviceDescriptor {\n    #[doc = \"Construct a new `GpuDeviceDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_default_queue()` instead.\"]\n    pub fn default_queue(&mut self, val: &GpuQueueDescriptor) -> &mut Self {\n        self.set_default_queue(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_required_features()` instead.\"]\n    pub fn required_features(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_required_features(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_required_limits()` instead.\"]\n    pub fn required_limits(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_required_limits(val);\n        self\n    }\n}\n\nimpl Default for GpuDeviceDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuDeviceLostInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUDeviceLostInfo , typescript_type = \"GPUDeviceLostInfo\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuDeviceLostInfo` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDeviceLostInfo)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceLostInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuDeviceLostInfo;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDeviceLostInfo\" , js_name = reason)]\n    #[doc = \"Getter for the `reason` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDeviceLostInfo/reason)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceLostInfo`, `GpuDeviceLostReason`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn reason(this: &GpuDeviceLostInfo) -> GpuDeviceLostReason;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUDeviceLostInfo\" , js_name = message)]\n    #[doc = \"Getter for the `message` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUDeviceLostInfo/message)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDeviceLostInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn message(this: &GpuDeviceLostInfo) -> ::alloc::string::String;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuDeviceLostReason.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuDeviceLostReason` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuDeviceLostReason`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuDeviceLostReason {\n    Unknown = \"unknown\",\n    Destroyed = \"destroyed\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuError.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUError , typescript_type = \"GPUError\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuError` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUError)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuError;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUError\" , js_name = message)]\n    #[doc = \"Getter for the `message` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUError/message)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn message(this: &GpuError) -> ::alloc::string::String;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuErrorFilter.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuErrorFilter` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuErrorFilter`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuErrorFilter {\n    Validation = \"validation\",\n    OutOfMemory = \"out-of-memory\",\n    Internal = \"internal\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuExtent3dDict.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUExtent3DDict)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuExtent3dDict` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuExtent3dDict;\n\n    #[doc = \"Get the `depthOrArrayLayers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthOrArrayLayers\")]\n    pub fn get_depth_or_array_layers(this: &GpuExtent3dDict) -> Option<u32>;\n\n    #[doc = \"Change the `depthOrArrayLayers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthOrArrayLayers\")]\n    pub fn set_depth_or_array_layers(this: &GpuExtent3dDict, val: u32);\n\n    #[doc = \"Get the `height` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"height\")]\n    pub fn get_height(this: &GpuExtent3dDict) -> Option<u32>;\n\n    #[doc = \"Change the `height` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"height\")]\n    pub fn set_height(this: &GpuExtent3dDict, val: u32);\n\n    #[doc = \"Get the `width` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"width\")]\n    pub fn get_width(this: &GpuExtent3dDict) -> u32;\n\n    #[doc = \"Change the `width` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"width\")]\n    pub fn set_width(this: &GpuExtent3dDict, val: u32);\n}\n\nimpl GpuExtent3dDict {\n    #[doc = \"Construct a new `GpuExtent3dDict`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(width: u32) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_width(width);\n        ret\n    }\n\n    #[deprecated = \"Use `set_depth_or_array_layers()` instead.\"]\n    pub fn depth_or_array_layers(&mut self, val: u32) -> &mut Self {\n        self.set_depth_or_array_layers(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_height()` instead.\"]\n    pub fn height(&mut self, val: u32) -> &mut Self {\n        self.set_height(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_width()` instead.\"]\n    pub fn width(&mut self, val: u32) -> &mut Self {\n        self.set_width(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuExternalTexture.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUExternalTexture , typescript_type = \"GPUExternalTexture\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuExternalTexture` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUExternalTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuExternalTexture;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUExternalTexture\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUExternalTexture/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuExternalTexture) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUExternalTexture\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUExternalTexture/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuExternalTexture, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuExternalTextureBindingLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUExternalTextureBindingLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuExternalTextureBindingLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuExternalTextureBindingLayout;\n}\n\nimpl GpuExternalTextureBindingLayout {\n    #[doc = \"Construct a new `GpuExternalTextureBindingLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n}\n\nimpl Default for GpuExternalTextureBindingLayout {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuExternalTextureDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUExternalTextureDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuExternalTextureDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuExternalTextureDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuExternalTextureDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuExternalTextureDescriptor, val: &str);\n\n    #[doc = \"Get the `source` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"source\")]\n    pub fn get_source(this: &GpuExternalTextureDescriptor) -> ::js_sys::Object;\n\n    #[doc = \"Change the `source` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"source\")]\n    pub fn set_source(this: &GpuExternalTextureDescriptor, val: &::js_sys::Object);\n}\n\nimpl GpuExternalTextureDescriptor {\n    #[doc = \"Construct a new `GpuExternalTextureDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExternalTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(source: &::js_sys::Object) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_source(source);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_source()` instead.\"]\n    pub fn source(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_source(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuFeatureName.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuFeatureName` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuFeatureName`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuFeatureName {\n    DepthClipControl = \"depth-clip-control\",\n    Depth32floatStencil8 = \"depth32float-stencil8\",\n    TextureCompressionBc = \"texture-compression-bc\",\n    TextureCompressionBcSliced3d = \"texture-compression-bc-sliced-3d\",\n    TextureCompressionEtc2 = \"texture-compression-etc2\",\n    TextureCompressionAstc = \"texture-compression-astc\",\n    TextureCompressionAstcSliced3d = \"texture-compression-astc-sliced-3d\",\n    TimestampQuery = \"timestamp-query\",\n    IndirectFirstInstance = \"indirect-first-instance\",\n    ShaderF16 = \"shader-f16\",\n    Rg11b10ufloatRenderable = \"rg11b10ufloat-renderable\",\n    Bgra8unormStorage = \"bgra8unorm-storage\",\n    Float32Filterable = \"float32-filterable\",\n    Float32Blendable = \"float32-blendable\",\n    ClipDistances = \"clip-distances\",\n    DualSourceBlending = \"dual-source-blending\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuFilterMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuFilterMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuFilterMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuFilterMode {\n    Nearest = \"nearest\",\n    Linear = \"linear\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuFragmentState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUFragmentState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuFragmentState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuFragmentState;\n\n    #[doc = \"Get the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"constants\")]\n    pub fn get_constants(this: &GpuFragmentState) -> Option<::js_sys::Object>;\n\n    #[doc = \"Change the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"constants\")]\n    pub fn set_constants(this: &GpuFragmentState, val: &::js_sys::Object);\n\n    #[doc = \"Get the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"entryPoint\")]\n    pub fn get_entry_point(this: &GpuFragmentState) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"entryPoint\")]\n    pub fn set_entry_point(this: &GpuFragmentState, val: &str);\n\n    #[doc = \"Get the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"module\")]\n    pub fn get_module(this: &GpuFragmentState) -> GpuShaderModule;\n\n    #[doc = \"Change the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"module\")]\n    pub fn set_module(this: &GpuFragmentState, val: &GpuShaderModule);\n\n    #[doc = \"Get the `targets` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"targets\")]\n    pub fn get_targets(this: &GpuFragmentState) -> ::js_sys::Array;\n\n    #[doc = \"Change the `targets` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"targets\")]\n    pub fn set_targets(this: &GpuFragmentState, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuFragmentState {\n    #[doc = \"Construct a new `GpuFragmentState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(module: &GpuShaderModule, targets: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_module(module);\n        ret.set_targets(targets);\n        ret\n    }\n\n    #[deprecated = \"Use `set_constants()` instead.\"]\n    pub fn constants(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_constants(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_entry_point()` instead.\"]\n    pub fn entry_point(&mut self, val: &str) -> &mut Self {\n        self.set_entry_point(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_module()` instead.\"]\n    pub fn module(&mut self, val: &GpuShaderModule) -> &mut Self {\n        self.set_module(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_targets()` instead.\"]\n    pub fn targets(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_targets(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuFrontFace.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuFrontFace` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuFrontFace`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuFrontFace {\n    Ccw = \"ccw\",\n    Cw = \"cw\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuIndexFormat.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuIndexFormat` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuIndexFormat`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuIndexFormat {\n    Uint16 = \"uint16\",\n    Uint32 = \"uint32\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuLoadOp.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuLoadOp` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuLoadOp {\n    Load = \"load\",\n    Clear = \"clear\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuMipmapFilterMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuMipmapFilterMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuMipmapFilterMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuMipmapFilterMode {\n    Nearest = \"nearest\",\n    Linear = \"linear\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuMultisampleState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUMultisampleState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuMultisampleState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuMultisampleState;\n\n    #[doc = \"Get the `alphaToCoverageEnabled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"alphaToCoverageEnabled\")]\n    pub fn get_alpha_to_coverage_enabled(this: &GpuMultisampleState) -> Option<bool>;\n\n    #[doc = \"Change the `alphaToCoverageEnabled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"alphaToCoverageEnabled\")]\n    pub fn set_alpha_to_coverage_enabled(this: &GpuMultisampleState, val: bool);\n\n    #[doc = \"Get the `count` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"count\")]\n    pub fn get_count(this: &GpuMultisampleState) -> Option<u32>;\n\n    #[doc = \"Change the `count` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"count\")]\n    pub fn set_count(this: &GpuMultisampleState, val: u32);\n\n    #[doc = \"Get the `mask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mask\")]\n    pub fn get_mask(this: &GpuMultisampleState) -> Option<u32>;\n\n    #[doc = \"Change the `mask` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mask\")]\n    pub fn set_mask(this: &GpuMultisampleState, val: u32);\n}\n\nimpl GpuMultisampleState {\n    #[doc = \"Construct a new `GpuMultisampleState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_alpha_to_coverage_enabled()` instead.\"]\n    pub fn alpha_to_coverage_enabled(&mut self, val: bool) -> &mut Self {\n        self.set_alpha_to_coverage_enabled(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_count()` instead.\"]\n    pub fn count(&mut self, val: u32) -> &mut Self {\n        self.set_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mask()` instead.\"]\n    pub fn mask(&mut self, val: u32) -> &mut Self {\n        self.set_mask(val);\n        self\n    }\n}\n\nimpl Default for GpuMultisampleState {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuObjectDescriptorBase.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUObjectDescriptorBase)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuObjectDescriptorBase` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuObjectDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuObjectDescriptorBase;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuObjectDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuObjectDescriptorBase) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuObjectDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuObjectDescriptorBase, val: &str);\n}\n\nimpl GpuObjectDescriptorBase {\n    #[doc = \"Construct a new `GpuObjectDescriptorBase`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuObjectDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n}\n\nimpl Default for GpuObjectDescriptorBase {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuOrigin2dDict.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUOrigin2DDict)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuOrigin2dDict` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuOrigin2dDict;\n\n    #[doc = \"Get the `x` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"x\")]\n    pub fn get_x(this: &GpuOrigin2dDict) -> Option<u32>;\n\n    #[doc = \"Change the `x` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"x\")]\n    pub fn set_x(this: &GpuOrigin2dDict, val: u32);\n\n    #[doc = \"Get the `y` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"y\")]\n    pub fn get_y(this: &GpuOrigin2dDict) -> Option<u32>;\n\n    #[doc = \"Change the `y` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"y\")]\n    pub fn set_y(this: &GpuOrigin2dDict, val: u32);\n}\n\nimpl GpuOrigin2dDict {\n    #[doc = \"Construct a new `GpuOrigin2dDict`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin2dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_x()` instead.\"]\n    pub fn x(&mut self, val: u32) -> &mut Self {\n        self.set_x(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_y()` instead.\"]\n    pub fn y(&mut self, val: u32) -> &mut Self {\n        self.set_y(val);\n        self\n    }\n}\n\nimpl Default for GpuOrigin2dDict {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuOrigin3dDict.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUOrigin3DDict)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuOrigin3dDict` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuOrigin3dDict;\n\n    #[doc = \"Get the `x` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"x\")]\n    pub fn get_x(this: &GpuOrigin3dDict) -> Option<u32>;\n\n    #[doc = \"Change the `x` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"x\")]\n    pub fn set_x(this: &GpuOrigin3dDict, val: u32);\n\n    #[doc = \"Get the `y` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"y\")]\n    pub fn get_y(this: &GpuOrigin3dDict) -> Option<u32>;\n\n    #[doc = \"Change the `y` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"y\")]\n    pub fn set_y(this: &GpuOrigin3dDict, val: u32);\n\n    #[doc = \"Get the `z` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"z\")]\n    pub fn get_z(this: &GpuOrigin3dDict) -> Option<u32>;\n\n    #[doc = \"Change the `z` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"z\")]\n    pub fn set_z(this: &GpuOrigin3dDict, val: u32);\n}\n\nimpl GpuOrigin3dDict {\n    #[doc = \"Construct a new `GpuOrigin3dDict`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOrigin3dDict`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_x()` instead.\"]\n    pub fn x(&mut self, val: u32) -> &mut Self {\n        self.set_x(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_y()` instead.\"]\n    pub fn y(&mut self, val: u32) -> &mut Self {\n        self.set_y(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_z()` instead.\"]\n    pub fn z(&mut self, val: u32) -> &mut Self {\n        self.set_z(val);\n        self\n    }\n}\n\nimpl Default for GpuOrigin3dDict {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuOutOfMemoryError.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = GpuError , extends = :: js_sys :: Object , js_name = GPUOutOfMemoryError , typescript_type = \"GPUOutOfMemoryError\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuOutOfMemoryError` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUOutOfMemoryError)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOutOfMemoryError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuOutOfMemoryError;\n\n    #[wasm_bindgen(catch, constructor, js_class = \"GPUOutOfMemoryError\")]\n    #[doc = \"The `new GpuOutOfMemoryError(..)` constructor, creating a new instance of `GpuOutOfMemoryError`.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUOutOfMemoryError/GPUOutOfMemoryError)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuOutOfMemoryError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(message: &str) -> Result<GpuOutOfMemoryError, JsValue>;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPipelineDescriptorBase.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUPipelineDescriptorBase)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuPipelineDescriptorBase` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuPipelineDescriptorBase;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuPipelineDescriptorBase) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuPipelineDescriptorBase, val: &str);\n\n    #[doc = \"Get the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"layout\")]\n    pub fn get_layout(this: &GpuPipelineDescriptorBase) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"layout\")]\n    pub fn set_layout(this: &GpuPipelineDescriptorBase, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuPipelineDescriptorBase {\n    #[doc = \"Construct a new `GpuPipelineDescriptorBase`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineDescriptorBase`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(layout: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_layout(layout);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_layout()` instead.\"]\n    pub fn layout(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_layout(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPipelineLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUPipelineLayout , typescript_type = \"GPUPipelineLayout\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuPipelineLayout` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUPipelineLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuPipelineLayout;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUPipelineLayout\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUPipelineLayout/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuPipelineLayout) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUPipelineLayout\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUPipelineLayout/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuPipelineLayout, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPipelineLayoutDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUPipelineLayoutDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuPipelineLayoutDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuPipelineLayoutDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuPipelineLayoutDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuPipelineLayoutDescriptor, val: &str);\n\n    #[doc = \"Get the `bindGroupLayouts` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"bindGroupLayouts\")]\n    pub fn get_bind_group_layouts(this: &GpuPipelineLayoutDescriptor) -> ::js_sys::Array;\n\n    #[doc = \"Change the `bindGroupLayouts` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"bindGroupLayouts\")]\n    pub fn set_bind_group_layouts(\n        this: &GpuPipelineLayoutDescriptor,\n        val: &::wasm_bindgen::JsValue,\n    );\n}\n\nimpl GpuPipelineLayoutDescriptor {\n    #[doc = \"Construct a new `GpuPipelineLayoutDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPipelineLayoutDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(bind_group_layouts: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_bind_group_layouts(bind_group_layouts);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_bind_group_layouts()` instead.\"]\n    pub fn bind_group_layouts(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_bind_group_layouts(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPowerPreference.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuPowerPreference` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuPowerPreference`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuPowerPreference {\n    LowPower = \"low-power\",\n    HighPerformance = \"high-performance\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPrimitiveState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUPrimitiveState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuPrimitiveState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuPrimitiveState;\n\n    #[doc = \"Get the `cullMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCullMode`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"cullMode\")]\n    pub fn get_cull_mode(this: &GpuPrimitiveState) -> Option<GpuCullMode>;\n\n    #[doc = \"Change the `cullMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCullMode`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"cullMode\")]\n    pub fn set_cull_mode(this: &GpuPrimitiveState, val: GpuCullMode);\n\n    #[doc = \"Get the `frontFace` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFrontFace`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"frontFace\")]\n    pub fn get_front_face(this: &GpuPrimitiveState) -> Option<GpuFrontFace>;\n\n    #[doc = \"Change the `frontFace` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFrontFace`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"frontFace\")]\n    pub fn set_front_face(this: &GpuPrimitiveState, val: GpuFrontFace);\n\n    #[doc = \"Get the `stripIndexFormat` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuIndexFormat`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stripIndexFormat\")]\n    pub fn get_strip_index_format(this: &GpuPrimitiveState) -> Option<GpuIndexFormat>;\n\n    #[doc = \"Change the `stripIndexFormat` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuIndexFormat`, `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stripIndexFormat\")]\n    pub fn set_strip_index_format(this: &GpuPrimitiveState, val: GpuIndexFormat);\n\n    #[doc = \"Get the `topology` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`, `GpuPrimitiveTopology`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"topology\")]\n    pub fn get_topology(this: &GpuPrimitiveState) -> Option<GpuPrimitiveTopology>;\n\n    #[doc = \"Change the `topology` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`, `GpuPrimitiveTopology`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"topology\")]\n    pub fn set_topology(this: &GpuPrimitiveState, val: GpuPrimitiveTopology);\n\n    #[doc = \"Get the `unclippedDepth` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"unclippedDepth\")]\n    pub fn get_unclipped_depth(this: &GpuPrimitiveState) -> Option<bool>;\n\n    #[doc = \"Change the `unclippedDepth` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"unclippedDepth\")]\n    pub fn set_unclipped_depth(this: &GpuPrimitiveState, val: bool);\n}\n\nimpl GpuPrimitiveState {\n    #[doc = \"Construct a new `GpuPrimitiveState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_cull_mode()` instead.\"]\n    pub fn cull_mode(&mut self, val: GpuCullMode) -> &mut Self {\n        self.set_cull_mode(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_front_face()` instead.\"]\n    pub fn front_face(&mut self, val: GpuFrontFace) -> &mut Self {\n        self.set_front_face(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_strip_index_format()` instead.\"]\n    pub fn strip_index_format(&mut self, val: GpuIndexFormat) -> &mut Self {\n        self.set_strip_index_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_topology()` instead.\"]\n    pub fn topology(&mut self, val: GpuPrimitiveTopology) -> &mut Self {\n        self.set_topology(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_unclipped_depth()` instead.\"]\n    pub fn unclipped_depth(&mut self, val: bool) -> &mut Self {\n        self.set_unclipped_depth(val);\n        self\n    }\n}\n\nimpl Default for GpuPrimitiveState {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuPrimitiveTopology.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuPrimitiveTopology` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveTopology`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuPrimitiveTopology {\n    PointList = \"point-list\",\n    LineList = \"line-list\",\n    LineStrip = \"line-strip\",\n    TriangleList = \"triangle-list\",\n    TriangleStrip = \"triangle-strip\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuProgrammableStage.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUProgrammableStage)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuProgrammableStage` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuProgrammableStage;\n\n    #[doc = \"Get the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"constants\")]\n    pub fn get_constants(this: &GpuProgrammableStage) -> Option<::js_sys::Object>;\n\n    #[doc = \"Change the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"constants\")]\n    pub fn set_constants(this: &GpuProgrammableStage, val: &::js_sys::Object);\n\n    #[doc = \"Get the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"entryPoint\")]\n    pub fn get_entry_point(this: &GpuProgrammableStage) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"entryPoint\")]\n    pub fn set_entry_point(this: &GpuProgrammableStage, val: &str);\n\n    #[doc = \"Get the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"module\")]\n    pub fn get_module(this: &GpuProgrammableStage) -> GpuShaderModule;\n\n    #[doc = \"Change the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"module\")]\n    pub fn set_module(this: &GpuProgrammableStage, val: &GpuShaderModule);\n}\n\nimpl GpuProgrammableStage {\n    #[doc = \"Construct a new `GpuProgrammableStage`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuProgrammableStage`, `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(module: &GpuShaderModule) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_module(module);\n        ret\n    }\n\n    #[deprecated = \"Use `set_constants()` instead.\"]\n    pub fn constants(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_constants(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_entry_point()` instead.\"]\n    pub fn entry_point(&mut self, val: &str) -> &mut Self {\n        self.set_entry_point(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_module()` instead.\"]\n    pub fn module(&mut self, val: &GpuShaderModule) -> &mut Self {\n        self.set_module(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuQuerySet.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUQuerySet , typescript_type = \"GPUQuerySet\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuQuerySet` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuQuerySet;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUQuerySet\" , js_name = type)]\n    #[doc = \"Getter for the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet/type)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuQueryType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn type_(this: &GpuQuerySet) -> GpuQueryType;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUQuerySet\" , js_name = count)]\n    #[doc = \"Getter for the `count` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet/count)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn count(this: &GpuQuerySet) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUQuerySet\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuQuerySet) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUQuerySet\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuQuerySet, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUQuerySet\" , js_name = destroy)]\n    #[doc = \"The `destroy()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet/destroy)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn destroy(this: &GpuQuerySet);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuQuerySetDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUQuerySetDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuQuerySetDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuQuerySetDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuQuerySetDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuQuerySetDescriptor, val: &str);\n\n    #[doc = \"Get the `count` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"count\")]\n    pub fn get_count(this: &GpuQuerySetDescriptor) -> u32;\n\n    #[doc = \"Change the `count` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"count\")]\n    pub fn set_count(this: &GpuQuerySetDescriptor, val: u32);\n\n    #[doc = \"Get the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`, `GpuQueryType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"type\")]\n    pub fn get_type(this: &GpuQuerySetDescriptor) -> GpuQueryType;\n\n    #[doc = \"Change the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`, `GpuQueryType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"type\")]\n    pub fn set_type(this: &GpuQuerySetDescriptor, val: GpuQueryType);\n}\n\nimpl GpuQuerySetDescriptor {\n    #[doc = \"Construct a new `GpuQuerySetDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySetDescriptor`, `GpuQueryType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(count: u32, type_: GpuQueryType) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_count(count);\n        ret.set_type(type_);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_count()` instead.\"]\n    pub fn count(&mut self, val: u32) -> &mut Self {\n        self.set_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_type()` instead.\"]\n    pub fn type_(&mut self, val: GpuQueryType) -> &mut Self {\n        self.set_type(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuQueryType.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuQueryType` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuQueryType`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuQueryType {\n    Occlusion = \"occlusion\",\n    Timestamp = \"timestamp\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuQueue.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUQueue , typescript_type = \"GPUQueue\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuQueue` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuQueue;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUQueue\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuQueue) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUQueue\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuQueue, value: &str);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = copyExternalImageToTexture)]\n    #[doc = \"The `copyExternalImageToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/copyExternalImageToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuCopyExternalImageSourceInfo`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_external_image_to_texture_with_u32_sequence(\n        this: &GpuQueue,\n        source: &GpuCopyExternalImageSourceInfo,\n        destination: &GpuCopyExternalImageDestInfo,\n        copy_size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = copyExternalImageToTexture)]\n    #[doc = \"The `copyExternalImageToTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/copyExternalImageToTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCopyExternalImageDestInfo`, `GpuCopyExternalImageSourceInfo`, `GpuExtent3dDict`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn copy_external_image_to_texture_with_gpu_extent_3d_dict(\n        this: &GpuQueue,\n        source: &GpuCopyExternalImageSourceInfo,\n        destination: &GpuCopyExternalImageDestInfo,\n        copy_size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUQueue\" , js_name = onSubmittedWorkDone)]\n    #[doc = \"The `onSubmittedWorkDone()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/onSubmittedWorkDone)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn on_submitted_work_done(this: &GpuQueue) -> ::js_sys::Promise;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUQueue\" , js_name = submit)]\n    #[doc = \"The `submit()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/submit)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn submit(this: &GpuQueue, command_buffers: &::wasm_bindgen::JsValue);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_u32_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_f64_and_u32(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n        size: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_u32_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: u32,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_buffer_source_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Object,\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_buffer_source_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Object,\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_slice_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &[u8],\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_slice_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &[u8],\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_u32_and_u8_array_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: u32,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeBuffer)]\n    #[doc = \"The `writeBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuQueue`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_buffer_with_f64_and_u8_array_and_f64_and_f64(\n        this: &GpuQueue,\n        buffer: &GpuBuffer,\n        buffer_offset: f64,\n        data: &::js_sys::Uint8Array,\n        data_offset: f64,\n        size: f64,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_buffer_source_and_u32_sequence(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &::js_sys::Object,\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_u8_slice_and_u32_sequence(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &[u8],\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_u8_array_and_u32_sequence(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &::js_sys::Uint8Array,\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`, `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_buffer_source_and_gpu_extent_3d_dict(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &::js_sys::Object,\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`, `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_u8_slice_and_gpu_extent_3d_dict(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &[u8],\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUQueue\" , js_name = writeTexture)]\n    #[doc = \"The `writeTexture()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuExtent3dDict`, `GpuQueue`, `GpuTexelCopyBufferLayout`, `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn write_texture_with_u8_array_and_gpu_extent_3d_dict(\n        this: &GpuQueue,\n        destination: &GpuTexelCopyTextureInfo,\n        data: &::js_sys::Uint8Array,\n        data_layout: &GpuTexelCopyBufferLayout,\n        size: &GpuExtent3dDict,\n    ) -> Result<(), JsValue>;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuQueueDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUQueueDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuQueueDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuQueueDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuQueueDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuQueueDescriptor, val: &str);\n}\n\nimpl GpuQueueDescriptor {\n    #[doc = \"Construct a new `GpuQueueDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQueueDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n}\n\nimpl Default for GpuQueueDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderBundle.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderBundle , typescript_type = \"GPURenderBundle\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderBundle` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundle)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundle`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderBundle;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPURenderBundle\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundle/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundle`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuRenderBundle) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPURenderBundle\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundle/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundle`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuRenderBundle, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderBundleDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderBundleDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderBundleDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderBundleDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuRenderBundleDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuRenderBundleDescriptor, val: &str);\n}\n\nimpl GpuRenderBundleDescriptor {\n    #[doc = \"Construct a new `GpuRenderBundleDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n}\n\nimpl Default for GpuRenderBundleDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderBundleEncoder.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderBundleEncoder , typescript_type = \"GPURenderBundleEncoder\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderBundleEncoder` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderBundleEncoder;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPURenderBundleEncoder\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuRenderBundleEncoder) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPURenderBundleEncoder\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuRenderBundleEncoder, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = finish)]\n    #[doc = \"The `finish()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/finish)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundle`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn finish(this: &GpuRenderBundleEncoder) -> GpuRenderBundle;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = finish)]\n    #[doc = \"The `finish()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/finish)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundle`, `GpuRenderBundleDescriptor`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn finish_with_descriptor(\n        this: &GpuRenderBundleEncoder,\n        descriptor: &GpuRenderBundleDescriptor,\n    ) -> GpuRenderBundle;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_sequence(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets: &::wasm_bindgen::JsValue,\n    );\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuRenderBundleEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = insertDebugMarker)]\n    #[doc = \"The `insertDebugMarker()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/insertDebugMarker)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn insert_debug_marker(this: &GpuRenderBundleEncoder, marker_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = popDebugGroup)]\n    #[doc = \"The `popDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/popDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn pop_debug_group(this: &GpuRenderBundleEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = pushDebugGroup)]\n    #[doc = \"The `pushDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/pushDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn push_debug_group(this: &GpuRenderBundleEncoder, group_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw(this: &GpuRenderBundleEncoder, vertex_count: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count(\n        this: &GpuRenderBundleEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count_and_first_vertex(\n        this: &GpuRenderBundleEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count_and_first_vertex_and_first_instance(\n        this: &GpuRenderBundleEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n        first_instance: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed(this: &GpuRenderBundleEncoder, index_count: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count(\n        this: &GpuRenderBundleEncoder,\n        index_count: u32,\n        instance_count: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index(\n        this: &GpuRenderBundleEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index_and_base_vertex(\n        this: &GpuRenderBundleEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(\n        this: &GpuRenderBundleEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n        first_instance: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexedIndirect)]\n    #[doc = \"The `drawIndexedIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexedIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_indirect_with_u32(\n        this: &GpuRenderBundleEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndexedIndirect)]\n    #[doc = \"The `drawIndexedIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndexedIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_indirect_with_f64(\n        this: &GpuRenderBundleEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndirect)]\n    #[doc = \"The `drawIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indirect_with_u32(\n        this: &GpuRenderBundleEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = drawIndirect)]\n    #[doc = \"The `drawIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/drawIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indirect_with_f64(\n        this: &GpuRenderBundleEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32_and_u32(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64_and_u32(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32_and_f64(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64_and_f64(\n        this: &GpuRenderBundleEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setPipeline)]\n    #[doc = \"The `setPipeline()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setPipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoder`, `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_pipeline(this: &GpuRenderBundleEncoder, pipeline: &GpuRenderPipeline);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer(this: &GpuRenderBundleEncoder, slot: u32, buffer: Option<&GpuBuffer>);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32_and_u32(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64_and_u32(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32_and_f64(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderBundleEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderBundleEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderBundleEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64_and_f64(\n        this: &GpuRenderBundleEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n        size: f64,\n    );\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderBundleEncoderDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderBundleEncoderDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderBundleEncoderDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderBundleEncoderDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuRenderBundleEncoderDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuRenderBundleEncoderDescriptor, val: &str);\n\n    #[doc = \"Get the `colorFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"colorFormats\")]\n    pub fn get_color_formats(this: &GpuRenderBundleEncoderDescriptor) -> ::js_sys::Array;\n\n    #[doc = \"Change the `colorFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"colorFormats\")]\n    pub fn set_color_formats(\n        this: &GpuRenderBundleEncoderDescriptor,\n        val: &::wasm_bindgen::JsValue,\n    );\n\n    #[doc = \"Get the `depthStencilFormat` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthStencilFormat\")]\n    pub fn get_depth_stencil_format(\n        this: &GpuRenderBundleEncoderDescriptor,\n    ) -> Option<GpuTextureFormat>;\n\n    #[doc = \"Change the `depthStencilFormat` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthStencilFormat\")]\n    pub fn set_depth_stencil_format(this: &GpuRenderBundleEncoderDescriptor, val: GpuTextureFormat);\n\n    #[doc = \"Get the `sampleCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"sampleCount\")]\n    pub fn get_sample_count(this: &GpuRenderBundleEncoderDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `sampleCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"sampleCount\")]\n    pub fn set_sample_count(this: &GpuRenderBundleEncoderDescriptor, val: u32);\n\n    #[doc = \"Get the `depthReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthReadOnly\")]\n    pub fn get_depth_read_only(this: &GpuRenderBundleEncoderDescriptor) -> Option<bool>;\n\n    #[doc = \"Change the `depthReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthReadOnly\")]\n    pub fn set_depth_read_only(this: &GpuRenderBundleEncoderDescriptor, val: bool);\n\n    #[doc = \"Get the `stencilReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilReadOnly\")]\n    pub fn get_stencil_read_only(this: &GpuRenderBundleEncoderDescriptor) -> Option<bool>;\n\n    #[doc = \"Change the `stencilReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilReadOnly\")]\n    pub fn set_stencil_read_only(this: &GpuRenderBundleEncoderDescriptor, val: bool);\n}\n\nimpl GpuRenderBundleEncoderDescriptor {\n    #[doc = \"Construct a new `GpuRenderBundleEncoderDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderBundleEncoderDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(color_formats: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_color_formats(color_formats);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_color_formats()` instead.\"]\n    pub fn color_formats(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_color_formats(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_stencil_format()` instead.\"]\n    pub fn depth_stencil_format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_depth_stencil_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_sample_count()` instead.\"]\n    pub fn sample_count(&mut self, val: u32) -> &mut Self {\n        self.set_sample_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_read_only()` instead.\"]\n    pub fn depth_read_only(&mut self, val: bool) -> &mut Self {\n        self.set_depth_read_only(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_read_only()` instead.\"]\n    pub fn stencil_read_only(&mut self, val: bool) -> &mut Self {\n        self.set_stencil_read_only(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPassColorAttachment.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPassColorAttachment)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPassColorAttachment` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPassColorAttachment;\n\n    #[doc = \"Get the `clearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"clearValue\")]\n    pub fn get_clear_value(this: &GpuRenderPassColorAttachment) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `clearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"clearValue\")]\n    pub fn set_clear_value(this: &GpuRenderPassColorAttachment, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `depthSlice` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthSlice\")]\n    pub fn get_depth_slice(this: &GpuRenderPassColorAttachment) -> Option<u32>;\n\n    #[doc = \"Change the `depthSlice` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthSlice\")]\n    pub fn set_depth_slice(this: &GpuRenderPassColorAttachment, val: u32);\n\n    #[doc = \"Get the `loadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"loadOp\")]\n    pub fn get_load_op(this: &GpuRenderPassColorAttachment) -> GpuLoadOp;\n\n    #[doc = \"Change the `loadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassColorAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"loadOp\")]\n    pub fn set_load_op(this: &GpuRenderPassColorAttachment, val: GpuLoadOp);\n\n    #[doc = \"Get the `resolveTarget` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"resolveTarget\")]\n    pub fn get_resolve_target(this: &GpuRenderPassColorAttachment) -> Option<GpuTextureView>;\n\n    #[doc = \"Change the `resolveTarget` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"resolveTarget\")]\n    pub fn set_resolve_target(this: &GpuRenderPassColorAttachment, val: &GpuTextureView);\n\n    #[doc = \"Get the `storeOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"storeOp\")]\n    pub fn get_store_op(this: &GpuRenderPassColorAttachment) -> GpuStoreOp;\n\n    #[doc = \"Change the `storeOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"storeOp\")]\n    pub fn set_store_op(this: &GpuRenderPassColorAttachment, val: GpuStoreOp);\n\n    #[doc = \"Get the `view` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"view\")]\n    pub fn get_view(this: &GpuRenderPassColorAttachment) -> GpuTextureView;\n\n    #[doc = \"Change the `view` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassColorAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"view\")]\n    pub fn set_view(this: &GpuRenderPassColorAttachment, val: &GpuTextureView);\n}\n\nimpl GpuRenderPassColorAttachment {\n    #[doc = \"Construct a new `GpuRenderPassColorAttachment`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassColorAttachment`, `GpuStoreOp`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(load_op: GpuLoadOp, store_op: GpuStoreOp, view: &GpuTextureView) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_load_op(load_op);\n        ret.set_store_op(store_op);\n        ret.set_view(view);\n        ret\n    }\n\n    #[deprecated = \"Use `set_clear_value()` instead.\"]\n    pub fn clear_value(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_clear_value(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_slice()` instead.\"]\n    pub fn depth_slice(&mut self, val: u32) -> &mut Self {\n        self.set_depth_slice(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_load_op()` instead.\"]\n    pub fn load_op(&mut self, val: GpuLoadOp) -> &mut Self {\n        self.set_load_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_resolve_target()` instead.\"]\n    pub fn resolve_target(&mut self, val: &GpuTextureView) -> &mut Self {\n        self.set_resolve_target(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_store_op()` instead.\"]\n    pub fn store_op(&mut self, val: GpuStoreOp) -> &mut Self {\n        self.set_store_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view()` instead.\"]\n    pub fn view(&mut self, val: &GpuTextureView) -> &mut Self {\n        self.set_view(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPassDepthStencilAttachment.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPassDepthStencilAttachment)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPassDepthStencilAttachment` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPassDepthStencilAttachment;\n\n    #[doc = \"Get the `depthClearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthClearValue\")]\n    pub fn get_depth_clear_value(this: &GpuRenderPassDepthStencilAttachment) -> Option<f32>;\n\n    #[doc = \"Change the `depthClearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthClearValue\")]\n    pub fn set_depth_clear_value(this: &GpuRenderPassDepthStencilAttachment, val: f32);\n\n    #[doc = \"Get the `depthLoadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthLoadOp\")]\n    pub fn get_depth_load_op(this: &GpuRenderPassDepthStencilAttachment) -> Option<GpuLoadOp>;\n\n    #[doc = \"Change the `depthLoadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthLoadOp\")]\n    pub fn set_depth_load_op(this: &GpuRenderPassDepthStencilAttachment, val: GpuLoadOp);\n\n    #[doc = \"Get the `depthReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthReadOnly\")]\n    pub fn get_depth_read_only(this: &GpuRenderPassDepthStencilAttachment) -> Option<bool>;\n\n    #[doc = \"Change the `depthReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthReadOnly\")]\n    pub fn set_depth_read_only(this: &GpuRenderPassDepthStencilAttachment, val: bool);\n\n    #[doc = \"Get the `depthStoreOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthStoreOp\")]\n    pub fn get_depth_store_op(this: &GpuRenderPassDepthStencilAttachment) -> Option<GpuStoreOp>;\n\n    #[doc = \"Change the `depthStoreOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthStoreOp\")]\n    pub fn set_depth_store_op(this: &GpuRenderPassDepthStencilAttachment, val: GpuStoreOp);\n\n    #[doc = \"Get the `stencilClearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilClearValue\")]\n    pub fn get_stencil_clear_value(this: &GpuRenderPassDepthStencilAttachment) -> Option<u32>;\n\n    #[doc = \"Change the `stencilClearValue` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilClearValue\")]\n    pub fn set_stencil_clear_value(this: &GpuRenderPassDepthStencilAttachment, val: u32);\n\n    #[doc = \"Get the `stencilLoadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilLoadOp\")]\n    pub fn get_stencil_load_op(this: &GpuRenderPassDepthStencilAttachment) -> Option<GpuLoadOp>;\n\n    #[doc = \"Change the `stencilLoadOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuLoadOp`, `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilLoadOp\")]\n    pub fn set_stencil_load_op(this: &GpuRenderPassDepthStencilAttachment, val: GpuLoadOp);\n\n    #[doc = \"Get the `stencilReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilReadOnly\")]\n    pub fn get_stencil_read_only(this: &GpuRenderPassDepthStencilAttachment) -> Option<bool>;\n\n    #[doc = \"Change the `stencilReadOnly` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilReadOnly\")]\n    pub fn set_stencil_read_only(this: &GpuRenderPassDepthStencilAttachment, val: bool);\n\n    #[doc = \"Get the `stencilStoreOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stencilStoreOp\")]\n    pub fn get_stencil_store_op(this: &GpuRenderPassDepthStencilAttachment) -> Option<GpuStoreOp>;\n\n    #[doc = \"Change the `stencilStoreOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuStoreOp`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stencilStoreOp\")]\n    pub fn set_stencil_store_op(this: &GpuRenderPassDepthStencilAttachment, val: GpuStoreOp);\n\n    #[doc = \"Get the `view` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"view\")]\n    pub fn get_view(this: &GpuRenderPassDepthStencilAttachment) -> GpuTextureView;\n\n    #[doc = \"Change the `view` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"view\")]\n    pub fn set_view(this: &GpuRenderPassDepthStencilAttachment, val: &GpuTextureView);\n}\n\nimpl GpuRenderPassDepthStencilAttachment {\n    #[doc = \"Construct a new `GpuRenderPassDepthStencilAttachment`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(view: &GpuTextureView) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_view(view);\n        ret\n    }\n\n    #[deprecated = \"Use `set_depth_clear_value()` instead.\"]\n    pub fn depth_clear_value(&mut self, val: f32) -> &mut Self {\n        self.set_depth_clear_value(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_load_op()` instead.\"]\n    pub fn depth_load_op(&mut self, val: GpuLoadOp) -> &mut Self {\n        self.set_depth_load_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_read_only()` instead.\"]\n    pub fn depth_read_only(&mut self, val: bool) -> &mut Self {\n        self.set_depth_read_only(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_store_op()` instead.\"]\n    pub fn depth_store_op(&mut self, val: GpuStoreOp) -> &mut Self {\n        self.set_depth_store_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_clear_value()` instead.\"]\n    pub fn stencil_clear_value(&mut self, val: u32) -> &mut Self {\n        self.set_stencil_clear_value(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_load_op()` instead.\"]\n    pub fn stencil_load_op(&mut self, val: GpuLoadOp) -> &mut Self {\n        self.set_stencil_load_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_read_only()` instead.\"]\n    pub fn stencil_read_only(&mut self, val: bool) -> &mut Self {\n        self.set_stencil_read_only(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_stencil_store_op()` instead.\"]\n    pub fn stencil_store_op(&mut self, val: GpuStoreOp) -> &mut Self {\n        self.set_stencil_store_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view()` instead.\"]\n    pub fn view(&mut self, val: &GpuTextureView) -> &mut Self {\n        self.set_view(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPassDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPassDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPassDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPassDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuRenderPassDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuRenderPassDescriptor, val: &str);\n\n    #[doc = \"Get the `colorAttachments` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"colorAttachments\")]\n    pub fn get_color_attachments(this: &GpuRenderPassDescriptor) -> ::js_sys::Array;\n\n    #[doc = \"Change the `colorAttachments` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"colorAttachments\")]\n    pub fn set_color_attachments(this: &GpuRenderPassDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `depthStencilAttachment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthStencilAttachment\")]\n    pub fn get_depth_stencil_attachment(\n        this: &GpuRenderPassDescriptor,\n    ) -> Option<GpuRenderPassDepthStencilAttachment>;\n\n    #[doc = \"Change the `depthStencilAttachment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDepthStencilAttachment`, `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthStencilAttachment\")]\n    pub fn set_depth_stencil_attachment(\n        this: &GpuRenderPassDescriptor,\n        val: &GpuRenderPassDepthStencilAttachment,\n    );\n\n    #[doc = \"Get the `maxDrawCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"maxDrawCount\")]\n    pub fn get_max_draw_count(this: &GpuRenderPassDescriptor) -> Option<f64>;\n\n    #[doc = \"Change the `maxDrawCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"maxDrawCount\")]\n    pub fn set_max_draw_count(this: &GpuRenderPassDescriptor, val: f64);\n\n    #[doc = \"Get the `occlusionQuerySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"occlusionQuerySet\")]\n    pub fn get_occlusion_query_set(this: &GpuRenderPassDescriptor) -> Option<GpuQuerySet>;\n\n    #[doc = \"Change the `occlusionQuerySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"occlusionQuerySet\")]\n    pub fn set_occlusion_query_set(this: &GpuRenderPassDescriptor, val: &GpuQuerySet);\n\n    #[doc = \"Get the `timestampWrites` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`, `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"timestampWrites\")]\n    pub fn get_timestamp_writes(\n        this: &GpuRenderPassDescriptor,\n    ) -> Option<GpuRenderPassTimestampWrites>;\n\n    #[doc = \"Change the `timestampWrites` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`, `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"timestampWrites\")]\n    pub fn set_timestamp_writes(this: &GpuRenderPassDescriptor, val: &GpuRenderPassTimestampWrites);\n}\n\nimpl GpuRenderPassDescriptor {\n    #[doc = \"Construct a new `GpuRenderPassDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(color_attachments: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_color_attachments(color_attachments);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_color_attachments()` instead.\"]\n    pub fn color_attachments(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_color_attachments(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_stencil_attachment()` instead.\"]\n    pub fn depth_stencil_attachment(\n        &mut self,\n        val: &GpuRenderPassDepthStencilAttachment,\n    ) -> &mut Self {\n        self.set_depth_stencil_attachment(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_max_draw_count()` instead.\"]\n    pub fn max_draw_count(&mut self, val: f64) -> &mut Self {\n        self.set_max_draw_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_occlusion_query_set()` instead.\"]\n    pub fn occlusion_query_set(&mut self, val: &GpuQuerySet) -> &mut Self {\n        self.set_occlusion_query_set(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_timestamp_writes()` instead.\"]\n    pub fn timestamp_writes(&mut self, val: &GpuRenderPassTimestampWrites) -> &mut Self {\n        self.set_timestamp_writes(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPassEncoder.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPassEncoder , typescript_type = \"GPURenderPassEncoder\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPassEncoder` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPassEncoder;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPURenderPassEncoder\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuRenderPassEncoder) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPURenderPassEncoder\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuRenderPassEncoder, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = beginOcclusionQuery)]\n    #[doc = \"The `beginOcclusionQuery()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/beginOcclusionQuery)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn begin_occlusion_query(this: &GpuRenderPassEncoder, query_index: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = end)]\n    #[doc = \"The `end()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/end)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn end(this: &GpuRenderPassEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = endOcclusionQuery)]\n    #[doc = \"The `endOcclusionQuery()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/endOcclusionQuery)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn end_occlusion_query(this: &GpuRenderPassEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = executeBundles)]\n    #[doc = \"The `executeBundles()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/executeBundles)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn execute_bundles(this: &GpuRenderPassEncoder, bundles: &::wasm_bindgen::JsValue);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBlendConstant)]\n    #[doc = \"The `setBlendConstant()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBlendConstant)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_blend_constant_with_f64_sequence(\n        this: &GpuRenderPassEncoder,\n        color: &::wasm_bindgen::JsValue,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBlendConstant)]\n    #[doc = \"The `setBlendConstant()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBlendConstant)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuColorDict`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_blend_constant_with_gpu_color_dict(\n        this: &GpuRenderPassEncoder,\n        color: &GpuColorDict,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setScissorRect)]\n    #[doc = \"The `setScissorRect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setScissorRect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_scissor_rect(this: &GpuRenderPassEncoder, x: u32, y: u32, width: u32, height: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setStencilReference)]\n    #[doc = \"The `setStencilReference()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setStencilReference)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_stencil_reference(this: &GpuRenderPassEncoder, reference: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setViewport)]\n    #[doc = \"The `setViewport()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setViewport)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_viewport(\n        this: &GpuRenderPassEncoder,\n        x: f32,\n        y: f32,\n        width: f32,\n        height: f32,\n        min_depth: f32,\n        max_depth: f32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_sequence(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets: &::wasm_bindgen::JsValue,\n    );\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_u32_and_dynamic_offsets_data_length(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: u32,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &[u32],\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setBindGroup)]\n    #[doc = \"The `setBindGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroup`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length(\n        this: &GpuRenderPassEncoder,\n        index: u32,\n        bind_group: Option<&GpuBindGroup>,\n        dynamic_offsets_data: &::js_sys::Uint32Array,\n        dynamic_offsets_data_start: f64,\n        dynamic_offsets_data_length: u32,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = insertDebugMarker)]\n    #[doc = \"The `insertDebugMarker()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/insertDebugMarker)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn insert_debug_marker(this: &GpuRenderPassEncoder, marker_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = popDebugGroup)]\n    #[doc = \"The `popDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/popDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn pop_debug_group(this: &GpuRenderPassEncoder);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = pushDebugGroup)]\n    #[doc = \"The `pushDebugGroup()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/pushDebugGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn push_debug_group(this: &GpuRenderPassEncoder, group_label: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw(this: &GpuRenderPassEncoder, vertex_count: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count(\n        this: &GpuRenderPassEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count_and_first_vertex(\n        this: &GpuRenderPassEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = draw)]\n    #[doc = \"The `draw()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/draw)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_with_instance_count_and_first_vertex_and_first_instance(\n        this: &GpuRenderPassEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n        first_instance: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed(this: &GpuRenderPassEncoder, index_count: u32);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count(\n        this: &GpuRenderPassEncoder,\n        index_count: u32,\n        instance_count: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index(\n        this: &GpuRenderPassEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index_and_base_vertex(\n        this: &GpuRenderPassEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexed)]\n    #[doc = \"The `drawIndexed()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexed)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(\n        this: &GpuRenderPassEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n        first_instance: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexedIndirect)]\n    #[doc = \"The `drawIndexedIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexedIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_indirect_with_u32(\n        this: &GpuRenderPassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndexedIndirect)]\n    #[doc = \"The `drawIndexedIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndexedIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indexed_indirect_with_f64(\n        this: &GpuRenderPassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndirect)]\n    #[doc = \"The `drawIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indirect_with_u32(\n        this: &GpuRenderPassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = drawIndirect)]\n    #[doc = \"The `drawIndirect()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/drawIndirect)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn draw_indirect_with_f64(\n        this: &GpuRenderPassEncoder,\n        indirect_buffer: &GpuBuffer,\n        indirect_offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32_and_u32(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64_and_u32(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_u32_and_f64(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: u32,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setIndexBuffer)]\n    #[doc = \"The `setIndexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setIndexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuIndexFormat`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_index_buffer_with_f64_and_f64(\n        this: &GpuRenderPassEncoder,\n        buffer: &GpuBuffer,\n        index_format: GpuIndexFormat,\n        offset: f64,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setPipeline)]\n    #[doc = \"The `setPipeline()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setPipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassEncoder`, `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_pipeline(this: &GpuRenderPassEncoder, pipeline: &GpuRenderPipeline);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer(this: &GpuRenderPassEncoder, slot: u32, buffer: Option<&GpuBuffer>);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32_and_u32(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64_and_u32(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n        size: u32,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_u32_and_f64(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: u32,\n        size: f64,\n    );\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPassEncoder\" , js_name = setVertexBuffer)]\n    #[doc = \"The `setVertexBuffer()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPassEncoder/setVertexBuffer)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuRenderPassEncoder`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_vertex_buffer_with_f64_and_f64(\n        this: &GpuRenderPassEncoder,\n        slot: u32,\n        buffer: Option<&GpuBuffer>,\n        offset: f64,\n        size: f64,\n    );\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPassTimestampWrites.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPassTimestampWrites)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPassTimestampWrites` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPassTimestampWrites;\n\n    #[doc = \"Get the `beginningOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"beginningOfPassWriteIndex\")]\n    pub fn get_beginning_of_pass_write_index(this: &GpuRenderPassTimestampWrites) -> Option<u32>;\n\n    #[doc = \"Change the `beginningOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"beginningOfPassWriteIndex\")]\n    pub fn set_beginning_of_pass_write_index(this: &GpuRenderPassTimestampWrites, val: u32);\n\n    #[doc = \"Get the `endOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"endOfPassWriteIndex\")]\n    pub fn get_end_of_pass_write_index(this: &GpuRenderPassTimestampWrites) -> Option<u32>;\n\n    #[doc = \"Change the `endOfPassWriteIndex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"endOfPassWriteIndex\")]\n    pub fn set_end_of_pass_write_index(this: &GpuRenderPassTimestampWrites, val: u32);\n\n    #[doc = \"Get the `querySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"querySet\")]\n    pub fn get_query_set(this: &GpuRenderPassTimestampWrites) -> GpuQuerySet;\n\n    #[doc = \"Change the `querySet` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"querySet\")]\n    pub fn set_query_set(this: &GpuRenderPassTimestampWrites, val: &GpuQuerySet);\n}\n\nimpl GpuRenderPassTimestampWrites {\n    #[doc = \"Construct a new `GpuRenderPassTimestampWrites`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuQuerySet`, `GpuRenderPassTimestampWrites`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(query_set: &GpuQuerySet) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_query_set(query_set);\n        ret\n    }\n\n    #[deprecated = \"Use `set_beginning_of_pass_write_index()` instead.\"]\n    pub fn beginning_of_pass_write_index(&mut self, val: u32) -> &mut Self {\n        self.set_beginning_of_pass_write_index(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_end_of_pass_write_index()` instead.\"]\n    pub fn end_of_pass_write_index(&mut self, val: u32) -> &mut Self {\n        self.set_end_of_pass_write_index(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_query_set()` instead.\"]\n    pub fn query_set(&mut self, val: &GpuQuerySet) -> &mut Self {\n        self.set_query_set(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPipeline.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPipeline , typescript_type = \"GPURenderPipeline\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPipeline` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPipeline)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPipeline;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPURenderPipeline\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPipeline/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuRenderPipeline) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPURenderPipeline\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPipeline/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuRenderPipeline, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPURenderPipeline\" , js_name = getBindGroupLayout)]\n    #[doc = \"The `getBindGroupLayout()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPURenderPipeline/getBindGroupLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBindGroupLayout`, `GpuRenderPipeline`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_bind_group_layout(this: &GpuRenderPipeline, index: u32) -> GpuBindGroupLayout;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRenderPipelineDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURenderPipelineDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRenderPipelineDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRenderPipelineDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuRenderPipelineDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuRenderPipelineDescriptor, val: &str);\n\n    #[doc = \"Get the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"layout\")]\n    pub fn get_layout(this: &GpuRenderPipelineDescriptor) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `layout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"layout\")]\n    pub fn set_layout(this: &GpuRenderPipelineDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `depthStencil` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthStencil\")]\n    pub fn get_depth_stencil(this: &GpuRenderPipelineDescriptor) -> Option<GpuDepthStencilState>;\n\n    #[doc = \"Change the `depthStencil` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuDepthStencilState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthStencil\")]\n    pub fn set_depth_stencil(this: &GpuRenderPipelineDescriptor, val: &GpuDepthStencilState);\n\n    #[doc = \"Get the `fragment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"fragment\")]\n    pub fn get_fragment(this: &GpuRenderPipelineDescriptor) -> Option<GpuFragmentState>;\n\n    #[doc = \"Change the `fragment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFragmentState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"fragment\")]\n    pub fn set_fragment(this: &GpuRenderPipelineDescriptor, val: &GpuFragmentState);\n\n    #[doc = \"Get the `multisample` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"multisample\")]\n    pub fn get_multisample(this: &GpuRenderPipelineDescriptor) -> Option<GpuMultisampleState>;\n\n    #[doc = \"Change the `multisample` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMultisampleState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"multisample\")]\n    pub fn set_multisample(this: &GpuRenderPipelineDescriptor, val: &GpuMultisampleState);\n\n    #[doc = \"Get the `primitive` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"primitive\")]\n    pub fn get_primitive(this: &GpuRenderPipelineDescriptor) -> Option<GpuPrimitiveState>;\n\n    #[doc = \"Change the `primitive` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPrimitiveState`, `GpuRenderPipelineDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"primitive\")]\n    pub fn set_primitive(this: &GpuRenderPipelineDescriptor, val: &GpuPrimitiveState);\n\n    #[doc = \"Get the `vertex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"vertex\")]\n    pub fn get_vertex(this: &GpuRenderPipelineDescriptor) -> GpuVertexState;\n\n    #[doc = \"Change the `vertex` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"vertex\")]\n    pub fn set_vertex(this: &GpuRenderPipelineDescriptor, val: &GpuVertexState);\n}\n\nimpl GpuRenderPipelineDescriptor {\n    #[doc = \"Construct a new `GpuRenderPipelineDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRenderPipelineDescriptor`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(layout: &::wasm_bindgen::JsValue, vertex: &GpuVertexState) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_layout(layout);\n        ret.set_vertex(vertex);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_layout()` instead.\"]\n    pub fn layout(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_layout(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_stencil()` instead.\"]\n    pub fn depth_stencil(&mut self, val: &GpuDepthStencilState) -> &mut Self {\n        self.set_depth_stencil(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_fragment()` instead.\"]\n    pub fn fragment(&mut self, val: &GpuFragmentState) -> &mut Self {\n        self.set_fragment(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_multisample()` instead.\"]\n    pub fn multisample(&mut self, val: &GpuMultisampleState) -> &mut Self {\n        self.set_multisample(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_primitive()` instead.\"]\n    pub fn primitive(&mut self, val: &GpuPrimitiveState) -> &mut Self {\n        self.set_primitive(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_vertex()` instead.\"]\n    pub fn vertex(&mut self, val: &GpuVertexState) -> &mut Self {\n        self.set_vertex(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuRequestAdapterOptions.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPURequestAdapterOptions)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuRequestAdapterOptions` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuRequestAdapterOptions;\n\n    #[doc = \"Get the `featureLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"featureLevel\")]\n    pub fn get_feature_level(this: &GpuRequestAdapterOptions) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `featureLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"featureLevel\")]\n    pub fn set_feature_level(this: &GpuRequestAdapterOptions, val: &str);\n\n    #[doc = \"Get the `forceFallbackAdapter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"forceFallbackAdapter\")]\n    pub fn get_force_fallback_adapter(this: &GpuRequestAdapterOptions) -> Option<bool>;\n\n    #[doc = \"Change the `forceFallbackAdapter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"forceFallbackAdapter\")]\n    pub fn set_force_fallback_adapter(this: &GpuRequestAdapterOptions, val: bool);\n\n    #[doc = \"Get the `powerPreference` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPowerPreference`, `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"powerPreference\")]\n    pub fn get_power_preference(this: &GpuRequestAdapterOptions) -> Option<GpuPowerPreference>;\n\n    #[doc = \"Change the `powerPreference` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuPowerPreference`, `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"powerPreference\")]\n    pub fn set_power_preference(this: &GpuRequestAdapterOptions, val: GpuPowerPreference);\n\n    #[doc = \"Get the `xrCompatible` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"xrCompatible\")]\n    pub fn get_xr_compatible(this: &GpuRequestAdapterOptions) -> Option<bool>;\n\n    #[doc = \"Change the `xrCompatible` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"xrCompatible\")]\n    pub fn set_xr_compatible(this: &GpuRequestAdapterOptions, val: bool);\n}\n\nimpl GpuRequestAdapterOptions {\n    #[doc = \"Construct a new `GpuRequestAdapterOptions`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuRequestAdapterOptions`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_feature_level()` instead.\"]\n    pub fn feature_level(&mut self, val: &str) -> &mut Self {\n        self.set_feature_level(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_force_fallback_adapter()` instead.\"]\n    pub fn force_fallback_adapter(&mut self, val: bool) -> &mut Self {\n        self.set_force_fallback_adapter(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_power_preference()` instead.\"]\n    pub fn power_preference(&mut self, val: GpuPowerPreference) -> &mut Self {\n        self.set_power_preference(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_xr_compatible()` instead.\"]\n    pub fn xr_compatible(&mut self, val: bool) -> &mut Self {\n        self.set_xr_compatible(val);\n        self\n    }\n}\n\nimpl Default for GpuRequestAdapterOptions {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSampler.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUSampler , typescript_type = \"GPUSampler\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuSampler` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSampler)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSampler`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuSampler;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSampler\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSampler/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSampler`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuSampler) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUSampler\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSampler/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSampler`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuSampler, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSamplerBindingLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUSamplerBindingLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuSamplerBindingLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuSamplerBindingLayout;\n\n    #[doc = \"Get the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerBindingLayout`, `GpuSamplerBindingType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"type\")]\n    pub fn get_type(this: &GpuSamplerBindingLayout) -> Option<GpuSamplerBindingType>;\n\n    #[doc = \"Change the `type` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerBindingLayout`, `GpuSamplerBindingType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"type\")]\n    pub fn set_type(this: &GpuSamplerBindingLayout, val: GpuSamplerBindingType);\n}\n\nimpl GpuSamplerBindingLayout {\n    #[doc = \"Construct a new `GpuSamplerBindingLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_type()` instead.\"]\n    pub fn type_(&mut self, val: GpuSamplerBindingType) -> &mut Self {\n        self.set_type(val);\n        self\n    }\n}\n\nimpl Default for GpuSamplerBindingLayout {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSamplerBindingType.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuSamplerBindingType` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuSamplerBindingType`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuSamplerBindingType {\n    Filtering = \"filtering\",\n    NonFiltering = \"non-filtering\",\n    Comparison = \"comparison\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSamplerDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUSamplerDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuSamplerDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuSamplerDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuSamplerDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuSamplerDescriptor, val: &str);\n\n    #[doc = \"Get the `addressModeU` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"addressModeU\")]\n    pub fn get_address_mode_u(this: &GpuSamplerDescriptor) -> Option<GpuAddressMode>;\n\n    #[doc = \"Change the `addressModeU` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"addressModeU\")]\n    pub fn set_address_mode_u(this: &GpuSamplerDescriptor, val: GpuAddressMode);\n\n    #[doc = \"Get the `addressModeV` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"addressModeV\")]\n    pub fn get_address_mode_v(this: &GpuSamplerDescriptor) -> Option<GpuAddressMode>;\n\n    #[doc = \"Change the `addressModeV` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"addressModeV\")]\n    pub fn set_address_mode_v(this: &GpuSamplerDescriptor, val: GpuAddressMode);\n\n    #[doc = \"Get the `addressModeW` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"addressModeW\")]\n    pub fn get_address_mode_w(this: &GpuSamplerDescriptor) -> Option<GpuAddressMode>;\n\n    #[doc = \"Change the `addressModeW` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuAddressMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"addressModeW\")]\n    pub fn set_address_mode_w(this: &GpuSamplerDescriptor, val: GpuAddressMode);\n\n    #[doc = \"Get the `compare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"compare\")]\n    pub fn get_compare(this: &GpuSamplerDescriptor) -> Option<GpuCompareFunction>;\n\n    #[doc = \"Change the `compare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"compare\")]\n    pub fn set_compare(this: &GpuSamplerDescriptor, val: GpuCompareFunction);\n\n    #[doc = \"Get the `lodMaxClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"lodMaxClamp\")]\n    pub fn get_lod_max_clamp(this: &GpuSamplerDescriptor) -> Option<f32>;\n\n    #[doc = \"Change the `lodMaxClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"lodMaxClamp\")]\n    pub fn set_lod_max_clamp(this: &GpuSamplerDescriptor, val: f32);\n\n    #[doc = \"Get the `lodMinClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"lodMinClamp\")]\n    pub fn get_lod_min_clamp(this: &GpuSamplerDescriptor) -> Option<f32>;\n\n    #[doc = \"Change the `lodMinClamp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"lodMinClamp\")]\n    pub fn set_lod_min_clamp(this: &GpuSamplerDescriptor, val: f32);\n\n    #[doc = \"Get the `magFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"magFilter\")]\n    pub fn get_mag_filter(this: &GpuSamplerDescriptor) -> Option<GpuFilterMode>;\n\n    #[doc = \"Change the `magFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"magFilter\")]\n    pub fn set_mag_filter(this: &GpuSamplerDescriptor, val: GpuFilterMode);\n\n    #[doc = \"Get the `maxAnisotropy` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"maxAnisotropy\")]\n    pub fn get_max_anisotropy(this: &GpuSamplerDescriptor) -> Option<u16>;\n\n    #[doc = \"Change the `maxAnisotropy` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"maxAnisotropy\")]\n    pub fn set_max_anisotropy(this: &GpuSamplerDescriptor, val: u16);\n\n    #[doc = \"Get the `minFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"minFilter\")]\n    pub fn get_min_filter(this: &GpuSamplerDescriptor) -> Option<GpuFilterMode>;\n\n    #[doc = \"Change the `minFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"minFilter\")]\n    pub fn set_min_filter(this: &GpuSamplerDescriptor, val: GpuFilterMode);\n\n    #[doc = \"Get the `mipmapFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMipmapFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mipmapFilter\")]\n    pub fn get_mipmap_filter(this: &GpuSamplerDescriptor) -> Option<GpuMipmapFilterMode>;\n\n    #[doc = \"Change the `mipmapFilter` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuMipmapFilterMode`, `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mipmapFilter\")]\n    pub fn set_mipmap_filter(this: &GpuSamplerDescriptor, val: GpuMipmapFilterMode);\n}\n\nimpl GpuSamplerDescriptor {\n    #[doc = \"Construct a new `GpuSamplerDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSamplerDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_address_mode_u()` instead.\"]\n    pub fn address_mode_u(&mut self, val: GpuAddressMode) -> &mut Self {\n        self.set_address_mode_u(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_address_mode_v()` instead.\"]\n    pub fn address_mode_v(&mut self, val: GpuAddressMode) -> &mut Self {\n        self.set_address_mode_v(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_address_mode_w()` instead.\"]\n    pub fn address_mode_w(&mut self, val: GpuAddressMode) -> &mut Self {\n        self.set_address_mode_w(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_compare()` instead.\"]\n    pub fn compare(&mut self, val: GpuCompareFunction) -> &mut Self {\n        self.set_compare(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_lod_max_clamp()` instead.\"]\n    pub fn lod_max_clamp(&mut self, val: f32) -> &mut Self {\n        self.set_lod_max_clamp(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_lod_min_clamp()` instead.\"]\n    pub fn lod_min_clamp(&mut self, val: f32) -> &mut Self {\n        self.set_lod_min_clamp(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mag_filter()` instead.\"]\n    pub fn mag_filter(&mut self, val: GpuFilterMode) -> &mut Self {\n        self.set_mag_filter(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_max_anisotropy()` instead.\"]\n    pub fn max_anisotropy(&mut self, val: u16) -> &mut Self {\n        self.set_max_anisotropy(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_min_filter()` instead.\"]\n    pub fn min_filter(&mut self, val: GpuFilterMode) -> &mut Self {\n        self.set_min_filter(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mipmap_filter()` instead.\"]\n    pub fn mipmap_filter(&mut self, val: GpuMipmapFilterMode) -> &mut Self {\n        self.set_mipmap_filter(val);\n        self\n    }\n}\n\nimpl Default for GpuSamplerDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuShaderModule.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUShaderModule , typescript_type = \"GPUShaderModule\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuShaderModule` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUShaderModule)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuShaderModule;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUShaderModule\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUShaderModule/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuShaderModule) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUShaderModule\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUShaderModule/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuShaderModule, value: &str);\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUShaderModule\" , js_name = getCompilationInfo)]\n    #[doc = \"The `getCompilationInfo()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUShaderModule/getCompilationInfo)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn get_compilation_info(this: &GpuShaderModule) -> ::js_sys::Promise;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuShaderModuleDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUShaderModuleDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuShaderModuleDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuShaderModuleDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuShaderModuleDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuShaderModuleDescriptor, val: &str);\n\n    #[doc = \"Get the `code` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"code\")]\n    pub fn get_code(this: &GpuShaderModuleDescriptor) -> ::alloc::string::String;\n\n    #[doc = \"Change the `code` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"code\")]\n    pub fn set_code(this: &GpuShaderModuleDescriptor, val: &str);\n\n    #[doc = \"Get the `compilationHints` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"compilationHints\")]\n    pub fn get_compilation_hints(this: &GpuShaderModuleDescriptor) -> Option<::js_sys::Array>;\n\n    #[doc = \"Change the `compilationHints` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"compilationHints\")]\n    pub fn set_compilation_hints(this: &GpuShaderModuleDescriptor, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuShaderModuleDescriptor {\n    #[doc = \"Construct a new `GpuShaderModuleDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModuleDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(code: &str) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_code(code);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_code()` instead.\"]\n    pub fn code(&mut self, val: &str) -> &mut Self {\n        self.set_code(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_compilation_hints()` instead.\"]\n    pub fn compilation_hints(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_compilation_hints(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuStencilFaceState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUStencilFaceState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuStencilFaceState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuStencilFaceState;\n\n    #[doc = \"Get the `compare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"compare\")]\n    pub fn get_compare(this: &GpuStencilFaceState) -> Option<GpuCompareFunction>;\n\n    #[doc = \"Change the `compare` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuCompareFunction`, `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"compare\")]\n    pub fn set_compare(this: &GpuStencilFaceState, val: GpuCompareFunction);\n\n    #[doc = \"Get the `depthFailOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"depthFailOp\")]\n    pub fn get_depth_fail_op(this: &GpuStencilFaceState) -> Option<GpuStencilOperation>;\n\n    #[doc = \"Change the `depthFailOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"depthFailOp\")]\n    pub fn set_depth_fail_op(this: &GpuStencilFaceState, val: GpuStencilOperation);\n\n    #[doc = \"Get the `failOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"failOp\")]\n    pub fn get_fail_op(this: &GpuStencilFaceState) -> Option<GpuStencilOperation>;\n\n    #[doc = \"Change the `failOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"failOp\")]\n    pub fn set_fail_op(this: &GpuStencilFaceState, val: GpuStencilOperation);\n\n    #[doc = \"Get the `passOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"passOp\")]\n    pub fn get_pass_op(this: &GpuStencilFaceState) -> Option<GpuStencilOperation>;\n\n    #[doc = \"Change the `passOp` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`, `GpuStencilOperation`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"passOp\")]\n    pub fn set_pass_op(this: &GpuStencilFaceState, val: GpuStencilOperation);\n}\n\nimpl GpuStencilFaceState {\n    #[doc = \"Construct a new `GpuStencilFaceState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStencilFaceState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_compare()` instead.\"]\n    pub fn compare(&mut self, val: GpuCompareFunction) -> &mut Self {\n        self.set_compare(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_depth_fail_op()` instead.\"]\n    pub fn depth_fail_op(&mut self, val: GpuStencilOperation) -> &mut Self {\n        self.set_depth_fail_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_fail_op()` instead.\"]\n    pub fn fail_op(&mut self, val: GpuStencilOperation) -> &mut Self {\n        self.set_fail_op(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_pass_op()` instead.\"]\n    pub fn pass_op(&mut self, val: GpuStencilOperation) -> &mut Self {\n        self.set_pass_op(val);\n        self\n    }\n}\n\nimpl Default for GpuStencilFaceState {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuStencilOperation.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuStencilOperation` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuStencilOperation`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuStencilOperation {\n    Keep = \"keep\",\n    Zero = \"zero\",\n    Replace = \"replace\",\n    Invert = \"invert\",\n    IncrementClamp = \"increment-clamp\",\n    DecrementClamp = \"decrement-clamp\",\n    IncrementWrap = \"increment-wrap\",\n    DecrementWrap = \"decrement-wrap\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuStorageTextureAccess.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuStorageTextureAccess` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureAccess`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuStorageTextureAccess {\n    WriteOnly = \"write-only\",\n    ReadOnly = \"read-only\",\n    ReadWrite = \"read-write\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuStorageTextureBindingLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUStorageTextureBindingLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuStorageTextureBindingLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuStorageTextureBindingLayout;\n\n    #[doc = \"Get the `access` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureAccess`, `GpuStorageTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"access\")]\n    pub fn get_access(this: &GpuStorageTextureBindingLayout) -> Option<GpuStorageTextureAccess>;\n\n    #[doc = \"Change the `access` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureAccess`, `GpuStorageTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"access\")]\n    pub fn set_access(this: &GpuStorageTextureBindingLayout, val: GpuStorageTextureAccess);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuStorageTextureBindingLayout) -> GpuTextureFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuStorageTextureBindingLayout, val: GpuTextureFormat);\n\n    #[doc = \"Get the `viewDimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"viewDimension\")]\n    pub fn get_view_dimension(\n        this: &GpuStorageTextureBindingLayout,\n    ) -> Option<GpuTextureViewDimension>;\n\n    #[doc = \"Change the `viewDimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"viewDimension\")]\n    pub fn set_view_dimension(this: &GpuStorageTextureBindingLayout, val: GpuTextureViewDimension);\n}\n\nimpl GpuStorageTextureBindingLayout {\n    #[doc = \"Construct a new `GpuStorageTextureBindingLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuStorageTextureBindingLayout`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(format: GpuTextureFormat) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_format(format);\n        ret\n    }\n\n    #[deprecated = \"Use `set_access()` instead.\"]\n    pub fn access(&mut self, val: GpuStorageTextureAccess) -> &mut Self {\n        self.set_access(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view_dimension()` instead.\"]\n    pub fn view_dimension(&mut self, val: GpuTextureViewDimension) -> &mut Self {\n        self.set_view_dimension(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuStoreOp.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuStoreOp` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuStoreOp`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuStoreOp {\n    Store = \"store\",\n    Discard = \"discard\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSupportedFeatures.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUSupportedFeatures , typescript_type = \"GPUSupportedFeatures\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuSupportedFeatures` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuSupportedFeatures;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedFeatures\" , js_name = size)]\n    #[doc = \"Getter for the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/size)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn size(this: &GpuSupportedFeatures) -> u32;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUSupportedFeatures\" , js_name = entries)]\n    #[doc = \"The `entries()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/entries)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn entries(this: &GpuSupportedFeatures) -> ::js_sys::Iterator;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUSupportedFeatures\" , js_name = forEach)]\n    #[doc = \"The `forEach()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/forEach)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn for_each(\n        this: &GpuSupportedFeatures,\n        callback: &::js_sys::Function,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUSupportedFeatures\" , js_name = has)]\n    #[doc = \"The `has()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/has)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn has(this: &GpuSupportedFeatures, value: &str) -> bool;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUSupportedFeatures\" , js_name = keys)]\n    #[doc = \"The `keys()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/keys)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn keys(this: &GpuSupportedFeatures) -> ::js_sys::Iterator;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUSupportedFeatures\" , js_name = values)]\n    #[doc = \"The `values()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedFeatures/values)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn values(this: &GpuSupportedFeatures) -> ::js_sys::Iterator;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuSupportedLimits.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUSupportedLimits , typescript_type = \"GPUSupportedLimits\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuSupportedLimits` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuSupportedLimits;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxTextureDimension1D)]\n    #[doc = \"Getter for the `maxTextureDimension1D` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxTextureDimension1D)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_texture_dimension_1d(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxTextureDimension2D)]\n    #[doc = \"Getter for the `maxTextureDimension2D` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxTextureDimension2D)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_texture_dimension_2d(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxTextureDimension3D)]\n    #[doc = \"Getter for the `maxTextureDimension3D` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxTextureDimension3D)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_texture_dimension_3d(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxTextureArrayLayers)]\n    #[doc = \"Getter for the `maxTextureArrayLayers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxTextureArrayLayers)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_texture_array_layers(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxBindGroups)]\n    #[doc = \"Getter for the `maxBindGroups` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxBindGroups)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_bind_groups(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxBindGroupsPlusVertexBuffers)]\n    #[doc = \"Getter for the `maxBindGroupsPlusVertexBuffers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxBindGroupsPlusVertexBuffers)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_bind_groups_plus_vertex_buffers(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxBindingsPerBindGroup)]\n    #[doc = \"Getter for the `maxBindingsPerBindGroup` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxBindingsPerBindGroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_bindings_per_bind_group(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxDynamicUniformBuffersPerPipelineLayout)]\n    #[doc = \"Getter for the `maxDynamicUniformBuffersPerPipelineLayout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxDynamicUniformBuffersPerPipelineLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_dynamic_uniform_buffers_per_pipeline_layout(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxDynamicStorageBuffersPerPipelineLayout)]\n    #[doc = \"Getter for the `maxDynamicStorageBuffersPerPipelineLayout` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxDynamicStorageBuffersPerPipelineLayout)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_dynamic_storage_buffers_per_pipeline_layout(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxSampledTexturesPerShaderStage)]\n    #[doc = \"Getter for the `maxSampledTexturesPerShaderStage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxSampledTexturesPerShaderStage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_sampled_textures_per_shader_stage(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxSamplersPerShaderStage)]\n    #[doc = \"Getter for the `maxSamplersPerShaderStage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxSamplersPerShaderStage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_samplers_per_shader_stage(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxStorageBuffersPerShaderStage)]\n    #[doc = \"Getter for the `maxStorageBuffersPerShaderStage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxStorageBuffersPerShaderStage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_storage_buffers_per_shader_stage(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxStorageTexturesPerShaderStage)]\n    #[doc = \"Getter for the `maxStorageTexturesPerShaderStage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxStorageTexturesPerShaderStage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_storage_textures_per_shader_stage(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxUniformBuffersPerShaderStage)]\n    #[doc = \"Getter for the `maxUniformBuffersPerShaderStage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxUniformBuffersPerShaderStage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_uniform_buffers_per_shader_stage(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxUniformBufferBindingSize)]\n    #[doc = \"Getter for the `maxUniformBufferBindingSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxUniformBufferBindingSize)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_uniform_buffer_binding_size(this: &GpuSupportedLimits) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxStorageBufferBindingSize)]\n    #[doc = \"Getter for the `maxStorageBufferBindingSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxStorageBufferBindingSize)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_storage_buffer_binding_size(this: &GpuSupportedLimits) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = minUniformBufferOffsetAlignment)]\n    #[doc = \"Getter for the `minUniformBufferOffsetAlignment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/minUniformBufferOffsetAlignment)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn min_uniform_buffer_offset_alignment(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = minStorageBufferOffsetAlignment)]\n    #[doc = \"Getter for the `minStorageBufferOffsetAlignment` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/minStorageBufferOffsetAlignment)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn min_storage_buffer_offset_alignment(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxVertexBuffers)]\n    #[doc = \"Getter for the `maxVertexBuffers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxVertexBuffers)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_vertex_buffers(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxBufferSize)]\n    #[doc = \"Getter for the `maxBufferSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxBufferSize)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_buffer_size(this: &GpuSupportedLimits) -> f64;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxVertexAttributes)]\n    #[doc = \"Getter for the `maxVertexAttributes` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxVertexAttributes)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_vertex_attributes(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxVertexBufferArrayStride)]\n    #[doc = \"Getter for the `maxVertexBufferArrayStride` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxVertexBufferArrayStride)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_vertex_buffer_array_stride(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxInterStageShaderVariables)]\n    #[doc = \"Getter for the `maxInterStageShaderVariables` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxInterStageShaderVariables)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_inter_stage_shader_variables(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxColorAttachments)]\n    #[doc = \"Getter for the `maxColorAttachments` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxColorAttachments)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_color_attachments(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxColorAttachmentBytesPerSample)]\n    #[doc = \"Getter for the `maxColorAttachmentBytesPerSample` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxColorAttachmentBytesPerSample)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_color_attachment_bytes_per_sample(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeWorkgroupStorageSize)]\n    #[doc = \"Getter for the `maxComputeWorkgroupStorageSize` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeWorkgroupStorageSize)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_workgroup_storage_size(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeInvocationsPerWorkgroup)]\n    #[doc = \"Getter for the `maxComputeInvocationsPerWorkgroup` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeInvocationsPerWorkgroup)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_invocations_per_workgroup(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeWorkgroupSizeX)]\n    #[doc = \"Getter for the `maxComputeWorkgroupSizeX` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeWorkgroupSizeX)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_workgroup_size_x(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeWorkgroupSizeY)]\n    #[doc = \"Getter for the `maxComputeWorkgroupSizeY` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeWorkgroupSizeY)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_workgroup_size_y(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeWorkgroupSizeZ)]\n    #[doc = \"Getter for the `maxComputeWorkgroupSizeZ` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeWorkgroupSizeZ)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_workgroup_size_z(this: &GpuSupportedLimits) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUSupportedLimits\" , js_name = maxComputeWorkgroupsPerDimension)]\n    #[doc = \"Getter for the `maxComputeWorkgroupsPerDimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits/maxComputeWorkgroupsPerDimension)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuSupportedLimits`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn max_compute_workgroups_per_dimension(this: &GpuSupportedLimits) -> u32;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTexelCopyBufferInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTexelCopyBufferInfo)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTexelCopyBufferInfo` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTexelCopyBufferInfo;\n\n    #[doc = \"Get the `bytesPerRow` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"bytesPerRow\")]\n    pub fn get_bytes_per_row(this: &GpuTexelCopyBufferInfo) -> Option<u32>;\n\n    #[doc = \"Change the `bytesPerRow` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"bytesPerRow\")]\n    pub fn set_bytes_per_row(this: &GpuTexelCopyBufferInfo, val: u32);\n\n    #[doc = \"Get the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"offset\")]\n    pub fn get_offset(this: &GpuTexelCopyBufferInfo) -> Option<f64>;\n\n    #[doc = \"Change the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"offset\")]\n    pub fn set_offset(this: &GpuTexelCopyBufferInfo, val: f64);\n\n    #[doc = \"Get the `rowsPerImage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"rowsPerImage\")]\n    pub fn get_rows_per_image(this: &GpuTexelCopyBufferInfo) -> Option<u32>;\n\n    #[doc = \"Change the `rowsPerImage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"rowsPerImage\")]\n    pub fn set_rows_per_image(this: &GpuTexelCopyBufferInfo, val: u32);\n\n    #[doc = \"Get the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"buffer\")]\n    pub fn get_buffer(this: &GpuTexelCopyBufferInfo) -> GpuBuffer;\n\n    #[doc = \"Change the `buffer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"buffer\")]\n    pub fn set_buffer(this: &GpuTexelCopyBufferInfo, val: &GpuBuffer);\n}\n\nimpl GpuTexelCopyBufferInfo {\n    #[doc = \"Construct a new `GpuTexelCopyBufferInfo`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuBuffer`, `GpuTexelCopyBufferInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(buffer: &GpuBuffer) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_buffer(buffer);\n        ret\n    }\n\n    #[deprecated = \"Use `set_bytes_per_row()` instead.\"]\n    pub fn bytes_per_row(&mut self, val: u32) -> &mut Self {\n        self.set_bytes_per_row(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_offset()` instead.\"]\n    pub fn offset(&mut self, val: f64) -> &mut Self {\n        self.set_offset(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_rows_per_image()` instead.\"]\n    pub fn rows_per_image(&mut self, val: u32) -> &mut Self {\n        self.set_rows_per_image(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_buffer()` instead.\"]\n    pub fn buffer(&mut self, val: &GpuBuffer) -> &mut Self {\n        self.set_buffer(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTexelCopyBufferLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTexelCopyBufferLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTexelCopyBufferLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTexelCopyBufferLayout;\n\n    #[doc = \"Get the `bytesPerRow` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"bytesPerRow\")]\n    pub fn get_bytes_per_row(this: &GpuTexelCopyBufferLayout) -> Option<u32>;\n\n    #[doc = \"Change the `bytesPerRow` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"bytesPerRow\")]\n    pub fn set_bytes_per_row(this: &GpuTexelCopyBufferLayout, val: u32);\n\n    #[doc = \"Get the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"offset\")]\n    pub fn get_offset(this: &GpuTexelCopyBufferLayout) -> Option<f64>;\n\n    #[doc = \"Change the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"offset\")]\n    pub fn set_offset(this: &GpuTexelCopyBufferLayout, val: f64);\n\n    #[doc = \"Get the `rowsPerImage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"rowsPerImage\")]\n    pub fn get_rows_per_image(this: &GpuTexelCopyBufferLayout) -> Option<u32>;\n\n    #[doc = \"Change the `rowsPerImage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"rowsPerImage\")]\n    pub fn set_rows_per_image(this: &GpuTexelCopyBufferLayout, val: u32);\n}\n\nimpl GpuTexelCopyBufferLayout {\n    #[doc = \"Construct a new `GpuTexelCopyBufferLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_bytes_per_row()` instead.\"]\n    pub fn bytes_per_row(&mut self, val: u32) -> &mut Self {\n        self.set_bytes_per_row(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_offset()` instead.\"]\n    pub fn offset(&mut self, val: f64) -> &mut Self {\n        self.set_offset(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_rows_per_image()` instead.\"]\n    pub fn rows_per_image(&mut self, val: u32) -> &mut Self {\n        self.set_rows_per_image(val);\n        self\n    }\n}\n\nimpl Default for GpuTexelCopyBufferLayout {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTexelCopyTextureInfo.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTexelCopyTextureInfo)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTexelCopyTextureInfo` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTexelCopyTextureInfo;\n\n    #[doc = \"Get the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`, `GpuTextureAspect`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"aspect\")]\n    pub fn get_aspect(this: &GpuTexelCopyTextureInfo) -> Option<GpuTextureAspect>;\n\n    #[doc = \"Change the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`, `GpuTextureAspect`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"aspect\")]\n    pub fn set_aspect(this: &GpuTexelCopyTextureInfo, val: GpuTextureAspect);\n\n    #[doc = \"Get the `mipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mipLevel\")]\n    pub fn get_mip_level(this: &GpuTexelCopyTextureInfo) -> Option<u32>;\n\n    #[doc = \"Change the `mipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mipLevel\")]\n    pub fn set_mip_level(this: &GpuTexelCopyTextureInfo, val: u32);\n\n    #[doc = \"Get the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"origin\")]\n    pub fn get_origin(this: &GpuTexelCopyTextureInfo) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `origin` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"origin\")]\n    pub fn set_origin(this: &GpuTexelCopyTextureInfo, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"texture\")]\n    pub fn get_texture(this: &GpuTexelCopyTextureInfo) -> GpuTexture;\n\n    #[doc = \"Change the `texture` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"texture\")]\n    pub fn set_texture(this: &GpuTexelCopyTextureInfo, val: &GpuTexture);\n}\n\nimpl GpuTexelCopyTextureInfo {\n    #[doc = \"Construct a new `GpuTexelCopyTextureInfo`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexelCopyTextureInfo`, `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(texture: &GpuTexture) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_texture(texture);\n        ret\n    }\n\n    #[deprecated = \"Use `set_aspect()` instead.\"]\n    pub fn aspect(&mut self, val: GpuTextureAspect) -> &mut Self {\n        self.set_aspect(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mip_level()` instead.\"]\n    pub fn mip_level(&mut self, val: u32) -> &mut Self {\n        self.set_mip_level(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_origin()` instead.\"]\n    pub fn origin(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_origin(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_texture()` instead.\"]\n    pub fn texture(&mut self, val: &GpuTexture) -> &mut Self {\n        self.set_texture(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTexture.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTexture , typescript_type = \"GPUTexture\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTexture` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTexture;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = width)]\n    #[doc = \"Getter for the `width` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/width)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn width(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = height)]\n    #[doc = \"Getter for the `height` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/height)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn height(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = depthOrArrayLayers)]\n    #[doc = \"Getter for the `depthOrArrayLayers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/depthOrArrayLayers)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn depth_or_array_layers(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = mipLevelCount)]\n    #[doc = \"Getter for the `mipLevelCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/mipLevelCount)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn mip_level_count(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = sampleCount)]\n    #[doc = \"Getter for the `sampleCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/sampleCount)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn sample_count(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = dimension)]\n    #[doc = \"Getter for the `dimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/dimension)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`, `GpuTextureDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn dimension(this: &GpuTexture) -> GpuTextureDimension;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = format)]\n    #[doc = \"Getter for the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/format)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn format(this: &GpuTexture) -> GpuTextureFormat;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = usage)]\n    #[doc = \"Getter for the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/usage)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn usage(this: &GpuTexture) -> u32;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTexture\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuTexture) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUTexture\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuTexture, value: &str);\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUTexture\" , js_name = createView)]\n    #[doc = \"The `createView()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/createView)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`, `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_view(this: &GpuTexture) -> Result<GpuTextureView, JsValue>;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"GPUTexture\" , js_name = createView)]\n    #[doc = \"The `createView()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/createView)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`, `GpuTextureView`, `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn create_view_with_descriptor(\n        this: &GpuTexture,\n        descriptor: &GpuTextureViewDescriptor,\n    ) -> Result<GpuTextureView, JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"GPUTexture\" , js_name = destroy)]\n    #[doc = \"The `destroy()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/destroy)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTexture`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn destroy(this: &GpuTexture);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureAspect.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuTextureAspect` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuTextureAspect`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuTextureAspect {\n    All = \"all\",\n    StencilOnly = \"stencil-only\",\n    DepthOnly = \"depth-only\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureBindingLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTextureBindingLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTextureBindingLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTextureBindingLayout;\n\n    #[doc = \"Get the `multisampled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"multisampled\")]\n    pub fn get_multisampled(this: &GpuTextureBindingLayout) -> Option<bool>;\n\n    #[doc = \"Change the `multisampled` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"multisampled\")]\n    pub fn set_multisampled(this: &GpuTextureBindingLayout, val: bool);\n\n    #[doc = \"Get the `sampleType` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`, `GpuTextureSampleType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"sampleType\")]\n    pub fn get_sample_type(this: &GpuTextureBindingLayout) -> Option<GpuTextureSampleType>;\n\n    #[doc = \"Change the `sampleType` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`, `GpuTextureSampleType`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"sampleType\")]\n    pub fn set_sample_type(this: &GpuTextureBindingLayout, val: GpuTextureSampleType);\n\n    #[doc = \"Get the `viewDimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"viewDimension\")]\n    pub fn get_view_dimension(this: &GpuTextureBindingLayout) -> Option<GpuTextureViewDimension>;\n\n    #[doc = \"Change the `viewDimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"viewDimension\")]\n    pub fn set_view_dimension(this: &GpuTextureBindingLayout, val: GpuTextureViewDimension);\n}\n\nimpl GpuTextureBindingLayout {\n    #[doc = \"Construct a new `GpuTextureBindingLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureBindingLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_multisampled()` instead.\"]\n    pub fn multisampled(&mut self, val: bool) -> &mut Self {\n        self.set_multisampled(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_sample_type()` instead.\"]\n    pub fn sample_type(&mut self, val: GpuTextureSampleType) -> &mut Self {\n        self.set_sample_type(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view_dimension()` instead.\"]\n    pub fn view_dimension(&mut self, val: GpuTextureViewDimension) -> &mut Self {\n        self.set_view_dimension(val);\n        self\n    }\n}\n\nimpl Default for GpuTextureBindingLayout {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTextureDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTextureDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTextureDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuTextureDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuTextureDescriptor, val: &str);\n\n    #[doc = \"Get the `dimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`, `GpuTextureDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"dimension\")]\n    pub fn get_dimension(this: &GpuTextureDescriptor) -> Option<GpuTextureDimension>;\n\n    #[doc = \"Change the `dimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`, `GpuTextureDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"dimension\")]\n    pub fn set_dimension(this: &GpuTextureDescriptor, val: GpuTextureDimension);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuTextureDescriptor) -> GpuTextureFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuTextureDescriptor, val: GpuTextureFormat);\n\n    #[doc = \"Get the `mipLevelCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mipLevelCount\")]\n    pub fn get_mip_level_count(this: &GpuTextureDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `mipLevelCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mipLevelCount\")]\n    pub fn set_mip_level_count(this: &GpuTextureDescriptor, val: u32);\n\n    #[doc = \"Get the `sampleCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"sampleCount\")]\n    pub fn get_sample_count(this: &GpuTextureDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `sampleCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"sampleCount\")]\n    pub fn set_sample_count(this: &GpuTextureDescriptor, val: u32);\n\n    #[doc = \"Get the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"size\")]\n    pub fn get_size(this: &GpuTextureDescriptor) -> ::wasm_bindgen::JsValue;\n\n    #[doc = \"Change the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"size\")]\n    pub fn set_size(this: &GpuTextureDescriptor, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"usage\")]\n    pub fn get_usage(this: &GpuTextureDescriptor) -> u32;\n\n    #[doc = \"Change the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"usage\")]\n    pub fn set_usage(this: &GpuTextureDescriptor, val: u32);\n\n    #[doc = \"Get the `viewFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"viewFormats\")]\n    pub fn get_view_formats(this: &GpuTextureDescriptor) -> Option<::js_sys::Array>;\n\n    #[doc = \"Change the `viewFormats` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"viewFormats\")]\n    pub fn set_view_formats(this: &GpuTextureDescriptor, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuTextureDescriptor {\n    #[doc = \"Construct a new `GpuTextureDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureDescriptor`, `GpuTextureFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(format: GpuTextureFormat, size: &::wasm_bindgen::JsValue, usage: u32) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_format(format);\n        ret.set_size(size);\n        ret.set_usage(usage);\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_dimension()` instead.\"]\n    pub fn dimension(&mut self, val: GpuTextureDimension) -> &mut Self {\n        self.set_dimension(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mip_level_count()` instead.\"]\n    pub fn mip_level_count(&mut self, val: u32) -> &mut Self {\n        self.set_mip_level_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_sample_count()` instead.\"]\n    pub fn sample_count(&mut self, val: u32) -> &mut Self {\n        self.set_sample_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_size()` instead.\"]\n    pub fn size(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_size(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_usage()` instead.\"]\n    pub fn usage(&mut self, val: u32) -> &mut Self {\n        self.set_usage(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_view_formats()` instead.\"]\n    pub fn view_formats(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_view_formats(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureDimension.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuTextureDimension` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuTextureDimension`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuTextureDimension {\n    N1d = \"1d\",\n    N2d = \"2d\",\n    N3d = \"3d\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureFormat.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuTextureFormat` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuTextureFormat`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuTextureFormat {\n    R8unorm = \"r8unorm\",\n    R8snorm = \"r8snorm\",\n    R8uint = \"r8uint\",\n    R8sint = \"r8sint\",\n    R16uint = \"r16uint\",\n    R16sint = \"r16sint\",\n    R16float = \"r16float\",\n    Rg8unorm = \"rg8unorm\",\n    Rg8snorm = \"rg8snorm\",\n    Rg8uint = \"rg8uint\",\n    Rg8sint = \"rg8sint\",\n    R32uint = \"r32uint\",\n    R32sint = \"r32sint\",\n    R32float = \"r32float\",\n    Rg16uint = \"rg16uint\",\n    Rg16sint = \"rg16sint\",\n    Rg16float = \"rg16float\",\n    Rgba8unorm = \"rgba8unorm\",\n    Rgba8unormSrgb = \"rgba8unorm-srgb\",\n    Rgba8snorm = \"rgba8snorm\",\n    Rgba8uint = \"rgba8uint\",\n    Rgba8sint = \"rgba8sint\",\n    Bgra8unorm = \"bgra8unorm\",\n    Bgra8unormSrgb = \"bgra8unorm-srgb\",\n    Rgb9e5ufloat = \"rgb9e5ufloat\",\n    Rgb10a2uint = \"rgb10a2uint\",\n    Rgb10a2unorm = \"rgb10a2unorm\",\n    Rg11b10ufloat = \"rg11b10ufloat\",\n    Rg32uint = \"rg32uint\",\n    Rg32sint = \"rg32sint\",\n    Rg32float = \"rg32float\",\n    Rgba16uint = \"rgba16uint\",\n    Rgba16sint = \"rgba16sint\",\n    Rgba16float = \"rgba16float\",\n    Rgba32uint = \"rgba32uint\",\n    Rgba32sint = \"rgba32sint\",\n    Rgba32float = \"rgba32float\",\n    Stencil8 = \"stencil8\",\n    Depth16unorm = \"depth16unorm\",\n    Depth24plus = \"depth24plus\",\n    Depth24plusStencil8 = \"depth24plus-stencil8\",\n    Depth32float = \"depth32float\",\n    Depth32floatStencil8 = \"depth32float-stencil8\",\n    Bc1RgbaUnorm = \"bc1-rgba-unorm\",\n    Bc1RgbaUnormSrgb = \"bc1-rgba-unorm-srgb\",\n    Bc2RgbaUnorm = \"bc2-rgba-unorm\",\n    Bc2RgbaUnormSrgb = \"bc2-rgba-unorm-srgb\",\n    Bc3RgbaUnorm = \"bc3-rgba-unorm\",\n    Bc3RgbaUnormSrgb = \"bc3-rgba-unorm-srgb\",\n    Bc4RUnorm = \"bc4-r-unorm\",\n    Bc4RSnorm = \"bc4-r-snorm\",\n    Bc5RgUnorm = \"bc5-rg-unorm\",\n    Bc5RgSnorm = \"bc5-rg-snorm\",\n    Bc6hRgbUfloat = \"bc6h-rgb-ufloat\",\n    Bc6hRgbFloat = \"bc6h-rgb-float\",\n    Bc7RgbaUnorm = \"bc7-rgba-unorm\",\n    Bc7RgbaUnormSrgb = \"bc7-rgba-unorm-srgb\",\n    Etc2Rgb8unorm = \"etc2-rgb8unorm\",\n    Etc2Rgb8unormSrgb = \"etc2-rgb8unorm-srgb\",\n    Etc2Rgb8a1unorm = \"etc2-rgb8a1unorm\",\n    Etc2Rgb8a1unormSrgb = \"etc2-rgb8a1unorm-srgb\",\n    Etc2Rgba8unorm = \"etc2-rgba8unorm\",\n    Etc2Rgba8unormSrgb = \"etc2-rgba8unorm-srgb\",\n    EacR11unorm = \"eac-r11unorm\",\n    EacR11snorm = \"eac-r11snorm\",\n    EacRg11unorm = \"eac-rg11unorm\",\n    EacRg11snorm = \"eac-rg11snorm\",\n    Astc4x4Unorm = \"astc-4x4-unorm\",\n    Astc4x4UnormSrgb = \"astc-4x4-unorm-srgb\",\n    Astc5x4Unorm = \"astc-5x4-unorm\",\n    Astc5x4UnormSrgb = \"astc-5x4-unorm-srgb\",\n    Astc5x5Unorm = \"astc-5x5-unorm\",\n    Astc5x5UnormSrgb = \"astc-5x5-unorm-srgb\",\n    Astc6x5Unorm = \"astc-6x5-unorm\",\n    Astc6x5UnormSrgb = \"astc-6x5-unorm-srgb\",\n    Astc6x6Unorm = \"astc-6x6-unorm\",\n    Astc6x6UnormSrgb = \"astc-6x6-unorm-srgb\",\n    Astc8x5Unorm = \"astc-8x5-unorm\",\n    Astc8x5UnormSrgb = \"astc-8x5-unorm-srgb\",\n    Astc8x6Unorm = \"astc-8x6-unorm\",\n    Astc8x6UnormSrgb = \"astc-8x6-unorm-srgb\",\n    Astc8x8Unorm = \"astc-8x8-unorm\",\n    Astc8x8UnormSrgb = \"astc-8x8-unorm-srgb\",\n    Astc10x5Unorm = \"astc-10x5-unorm\",\n    Astc10x5UnormSrgb = \"astc-10x5-unorm-srgb\",\n    Astc10x6Unorm = \"astc-10x6-unorm\",\n    Astc10x6UnormSrgb = \"astc-10x6-unorm-srgb\",\n    Astc10x8Unorm = \"astc-10x8-unorm\",\n    Astc10x8UnormSrgb = \"astc-10x8-unorm-srgb\",\n    Astc10x10Unorm = \"astc-10x10-unorm\",\n    Astc10x10UnormSrgb = \"astc-10x10-unorm-srgb\",\n    Astc12x10Unorm = \"astc-12x10-unorm\",\n    Astc12x10UnormSrgb = \"astc-12x10-unorm-srgb\",\n    Astc12x12Unorm = \"astc-12x12-unorm\",\n    Astc12x12UnormSrgb = \"astc-12x12-unorm-srgb\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureSampleType.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuTextureSampleType` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuTextureSampleType`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuTextureSampleType {\n    Float = \"float\",\n    UnfilterableFloat = \"unfilterable-float\",\n    Depth = \"depth\",\n    Sint = \"sint\",\n    Uint = \"uint\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureView.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTextureView , typescript_type = \"GPUTextureView\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTextureView` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTextureView)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTextureView;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUTextureView\" , js_name = label)]\n    #[doc = \"Getter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTextureView/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn label(this: &GpuTextureView) -> ::alloc::string::String;\n\n    # [wasm_bindgen (structural , method , setter , js_class = \"GPUTextureView\" , js_name = label)]\n    #[doc = \"Setter for the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUTextureView/label)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureView`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn set_label(this: &GpuTextureView, value: &str);\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureViewDescriptor.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUTextureViewDescriptor)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuTextureViewDescriptor` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuTextureViewDescriptor;\n\n    #[doc = \"Get the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"label\")]\n    pub fn get_label(this: &GpuTextureViewDescriptor) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `label` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"label\")]\n    pub fn set_label(this: &GpuTextureViewDescriptor, val: &str);\n\n    #[doc = \"Get the `arrayLayerCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"arrayLayerCount\")]\n    pub fn get_array_layer_count(this: &GpuTextureViewDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `arrayLayerCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"arrayLayerCount\")]\n    pub fn set_array_layer_count(this: &GpuTextureViewDescriptor, val: u32);\n\n    #[doc = \"Get the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureAspect`, `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"aspect\")]\n    pub fn get_aspect(this: &GpuTextureViewDescriptor) -> Option<GpuTextureAspect>;\n\n    #[doc = \"Change the `aspect` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureAspect`, `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"aspect\")]\n    pub fn set_aspect(this: &GpuTextureViewDescriptor, val: GpuTextureAspect);\n\n    #[doc = \"Get the `baseArrayLayer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"baseArrayLayer\")]\n    pub fn get_base_array_layer(this: &GpuTextureViewDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `baseArrayLayer` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"baseArrayLayer\")]\n    pub fn set_base_array_layer(this: &GpuTextureViewDescriptor, val: u32);\n\n    #[doc = \"Get the `baseMipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"baseMipLevel\")]\n    pub fn get_base_mip_level(this: &GpuTextureViewDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `baseMipLevel` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"baseMipLevel\")]\n    pub fn set_base_mip_level(this: &GpuTextureViewDescriptor, val: u32);\n\n    #[doc = \"Get the `dimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"dimension\")]\n    pub fn get_dimension(this: &GpuTextureViewDescriptor) -> Option<GpuTextureViewDimension>;\n\n    #[doc = \"Change the `dimension` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`, `GpuTextureViewDimension`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"dimension\")]\n    pub fn set_dimension(this: &GpuTextureViewDescriptor, val: GpuTextureViewDimension);\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureFormat`, `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuTextureViewDescriptor) -> Option<GpuTextureFormat>;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureFormat`, `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuTextureViewDescriptor, val: GpuTextureFormat);\n\n    #[doc = \"Get the `mipLevelCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"mipLevelCount\")]\n    pub fn get_mip_level_count(this: &GpuTextureViewDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `mipLevelCount` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"mipLevelCount\")]\n    pub fn set_mip_level_count(this: &GpuTextureViewDescriptor, val: u32);\n\n    #[doc = \"Get the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"usage\")]\n    pub fn get_usage(this: &GpuTextureViewDescriptor) -> Option<u32>;\n\n    #[doc = \"Change the `usage` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"usage\")]\n    pub fn set_usage(this: &GpuTextureViewDescriptor, val: u32);\n}\n\nimpl GpuTextureViewDescriptor {\n    #[doc = \"Construct a new `GpuTextureViewDescriptor`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDescriptor`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new() -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret\n    }\n\n    #[deprecated = \"Use `set_label()` instead.\"]\n    pub fn label(&mut self, val: &str) -> &mut Self {\n        self.set_label(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_array_layer_count()` instead.\"]\n    pub fn array_layer_count(&mut self, val: u32) -> &mut Self {\n        self.set_array_layer_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_aspect()` instead.\"]\n    pub fn aspect(&mut self, val: GpuTextureAspect) -> &mut Self {\n        self.set_aspect(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_base_array_layer()` instead.\"]\n    pub fn base_array_layer(&mut self, val: u32) -> &mut Self {\n        self.set_base_array_layer(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_base_mip_level()` instead.\"]\n    pub fn base_mip_level(&mut self, val: u32) -> &mut Self {\n        self.set_base_mip_level(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_dimension()` instead.\"]\n    pub fn dimension(&mut self, val: GpuTextureViewDimension) -> &mut Self {\n        self.set_dimension(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuTextureFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_mip_level_count()` instead.\"]\n    pub fn mip_level_count(&mut self, val: u32) -> &mut Self {\n        self.set_mip_level_count(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_usage()` instead.\"]\n    pub fn usage(&mut self, val: u32) -> &mut Self {\n        self.set_usage(val);\n        self\n    }\n}\n\nimpl Default for GpuTextureViewDescriptor {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuTextureViewDimension.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuTextureViewDimension` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuTextureViewDimension`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuTextureViewDimension {\n    N1d = \"1d\",\n    N2d = \"2d\",\n    N2dArray = \"2d-array\",\n    Cube = \"cube\",\n    CubeArray = \"cube-array\",\n    N3d = \"3d\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuUncapturedErrorEvent.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = Event , extends = :: js_sys :: Object , js_name = GPUUncapturedErrorEvent , typescript_type = \"GPUUncapturedErrorEvent\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuUncapturedErrorEvent` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUUncapturedErrorEvent)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEvent`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuUncapturedErrorEvent;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"GPUUncapturedErrorEvent\" , js_name = error)]\n    #[doc = \"Getter for the `error` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUUncapturedErrorEvent/error)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`, `GpuUncapturedErrorEvent`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn error(this: &GpuUncapturedErrorEvent) -> GpuError;\n\n    #[wasm_bindgen(catch, constructor, js_class = \"GPUUncapturedErrorEvent\")]\n    #[doc = \"The `new GpuUncapturedErrorEvent(..)` constructor, creating a new instance of `GpuUncapturedErrorEvent`.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUUncapturedErrorEvent/GPUUncapturedErrorEvent)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEvent`, `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(\n        type_: &str,\n        gpu_uncaptured_error_event_init_dict: &GpuUncapturedErrorEventInit,\n    ) -> Result<GpuUncapturedErrorEvent, JsValue>;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuUncapturedErrorEventInit.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUUncapturedErrorEventInit)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuUncapturedErrorEventInit` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuUncapturedErrorEventInit;\n\n    #[doc = \"Get the `bubbles` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"bubbles\")]\n    pub fn get_bubbles(this: &GpuUncapturedErrorEventInit) -> Option<bool>;\n\n    #[doc = \"Change the `bubbles` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"bubbles\")]\n    pub fn set_bubbles(this: &GpuUncapturedErrorEventInit, val: bool);\n\n    #[doc = \"Get the `cancelable` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"cancelable\")]\n    pub fn get_cancelable(this: &GpuUncapturedErrorEventInit) -> Option<bool>;\n\n    #[doc = \"Change the `cancelable` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"cancelable\")]\n    pub fn set_cancelable(this: &GpuUncapturedErrorEventInit, val: bool);\n\n    #[doc = \"Get the `composed` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"composed\")]\n    pub fn get_composed(this: &GpuUncapturedErrorEventInit) -> Option<bool>;\n\n    #[doc = \"Change the `composed` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"composed\")]\n    pub fn set_composed(this: &GpuUncapturedErrorEventInit, val: bool);\n\n    #[doc = \"Get the `error` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`, `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"error\")]\n    pub fn get_error(this: &GpuUncapturedErrorEventInit) -> GpuError;\n\n    #[doc = \"Change the `error` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`, `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"error\")]\n    pub fn set_error(this: &GpuUncapturedErrorEventInit, val: &GpuError);\n}\n\nimpl GpuUncapturedErrorEventInit {\n    #[doc = \"Construct a new `GpuUncapturedErrorEventInit`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuError`, `GpuUncapturedErrorEventInit`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(error: &GpuError) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_error(error);\n        ret\n    }\n\n    #[deprecated = \"Use `set_bubbles()` instead.\"]\n    pub fn bubbles(&mut self, val: bool) -> &mut Self {\n        self.set_bubbles(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_cancelable()` instead.\"]\n    pub fn cancelable(&mut self, val: bool) -> &mut Self {\n        self.set_cancelable(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_composed()` instead.\"]\n    pub fn composed(&mut self, val: bool) -> &mut Self {\n        self.set_composed(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_error()` instead.\"]\n    pub fn error(&mut self, val: &GpuError) -> &mut Self {\n        self.set_error(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuValidationError.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = GpuError , extends = :: js_sys :: Object , js_name = GPUValidationError , typescript_type = \"GPUValidationError\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuValidationError` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUValidationError)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuValidationError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuValidationError;\n\n    #[wasm_bindgen(catch, constructor, js_class = \"GPUValidationError\")]\n    #[doc = \"The `new GpuValidationError(..)` constructor, creating a new instance of `GpuValidationError`.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/GPUValidationError/GPUValidationError)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuValidationError`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(message: &str) -> Result<GpuValidationError, JsValue>;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuVertexAttribute.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUVertexAttribute)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuVertexAttribute` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuVertexAttribute;\n\n    #[doc = \"Get the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`, `GpuVertexFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"format\")]\n    pub fn get_format(this: &GpuVertexAttribute) -> GpuVertexFormat;\n\n    #[doc = \"Change the `format` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`, `GpuVertexFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"format\")]\n    pub fn set_format(this: &GpuVertexAttribute, val: GpuVertexFormat);\n\n    #[doc = \"Get the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"offset\")]\n    pub fn get_offset(this: &GpuVertexAttribute) -> f64;\n\n    #[doc = \"Change the `offset` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"offset\")]\n    pub fn set_offset(this: &GpuVertexAttribute, val: f64);\n\n    #[doc = \"Get the `shaderLocation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"shaderLocation\")]\n    pub fn get_shader_location(this: &GpuVertexAttribute) -> u32;\n\n    #[doc = \"Change the `shaderLocation` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"shaderLocation\")]\n    pub fn set_shader_location(this: &GpuVertexAttribute, val: u32);\n}\n\nimpl GpuVertexAttribute {\n    #[doc = \"Construct a new `GpuVertexAttribute`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexAttribute`, `GpuVertexFormat`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(format: GpuVertexFormat, offset: f64, shader_location: u32) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_format(format);\n        ret.set_offset(offset);\n        ret.set_shader_location(shader_location);\n        ret\n    }\n\n    #[deprecated = \"Use `set_format()` instead.\"]\n    pub fn format(&mut self, val: GpuVertexFormat) -> &mut Self {\n        self.set_format(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_offset()` instead.\"]\n    pub fn offset(&mut self, val: f64) -> &mut Self {\n        self.set_offset(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_shader_location()` instead.\"]\n    pub fn shader_location(&mut self, val: u32) -> &mut Self {\n        self.set_shader_location(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuVertexBufferLayout.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUVertexBufferLayout)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuVertexBufferLayout` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuVertexBufferLayout;\n\n    #[doc = \"Get the `arrayStride` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"arrayStride\")]\n    pub fn get_array_stride(this: &GpuVertexBufferLayout) -> f64;\n\n    #[doc = \"Change the `arrayStride` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"arrayStride\")]\n    pub fn set_array_stride(this: &GpuVertexBufferLayout, val: f64);\n\n    #[doc = \"Get the `attributes` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"attributes\")]\n    pub fn get_attributes(this: &GpuVertexBufferLayout) -> ::js_sys::Array;\n\n    #[doc = \"Change the `attributes` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"attributes\")]\n    pub fn set_attributes(this: &GpuVertexBufferLayout, val: &::wasm_bindgen::JsValue);\n\n    #[doc = \"Get the `stepMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`, `GpuVertexStepMode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"stepMode\")]\n    pub fn get_step_mode(this: &GpuVertexBufferLayout) -> Option<GpuVertexStepMode>;\n\n    #[doc = \"Change the `stepMode` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`, `GpuVertexStepMode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"stepMode\")]\n    pub fn set_step_mode(this: &GpuVertexBufferLayout, val: GpuVertexStepMode);\n}\n\nimpl GpuVertexBufferLayout {\n    #[doc = \"Construct a new `GpuVertexBufferLayout`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexBufferLayout`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(array_stride: f64, attributes: &::wasm_bindgen::JsValue) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_array_stride(array_stride);\n        ret.set_attributes(attributes);\n        ret\n    }\n\n    #[deprecated = \"Use `set_array_stride()` instead.\"]\n    pub fn array_stride(&mut self, val: f64) -> &mut Self {\n        self.set_array_stride(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_attributes()` instead.\"]\n    pub fn attributes(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_attributes(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_step_mode()` instead.\"]\n    pub fn step_mode(&mut self, val: GpuVertexStepMode) -> &mut Self {\n        self.set_step_mode(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuVertexFormat.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuVertexFormat` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuVertexFormat`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuVertexFormat {\n    Uint8 = \"uint8\",\n    Uint8x2 = \"uint8x2\",\n    Uint8x4 = \"uint8x4\",\n    Sint8 = \"sint8\",\n    Sint8x2 = \"sint8x2\",\n    Sint8x4 = \"sint8x4\",\n    Unorm8 = \"unorm8\",\n    Unorm8x2 = \"unorm8x2\",\n    Unorm8x4 = \"unorm8x4\",\n    Snorm8 = \"snorm8\",\n    Snorm8x2 = \"snorm8x2\",\n    Snorm8x4 = \"snorm8x4\",\n    Uint16 = \"uint16\",\n    Uint16x2 = \"uint16x2\",\n    Uint16x4 = \"uint16x4\",\n    Sint16 = \"sint16\",\n    Sint16x2 = \"sint16x2\",\n    Sint16x4 = \"sint16x4\",\n    Unorm16 = \"unorm16\",\n    Unorm16x2 = \"unorm16x2\",\n    Unorm16x4 = \"unorm16x4\",\n    Snorm16 = \"snorm16\",\n    Snorm16x2 = \"snorm16x2\",\n    Snorm16x4 = \"snorm16x4\",\n    Float16 = \"float16\",\n    Float16x2 = \"float16x2\",\n    Float16x4 = \"float16x4\",\n    Float32 = \"float32\",\n    Float32x2 = \"float32x2\",\n    Float32x3 = \"float32x3\",\n    Float32x4 = \"float32x4\",\n    Uint32 = \"uint32\",\n    Uint32x2 = \"uint32x2\",\n    Uint32x3 = \"uint32x3\",\n    Uint32x4 = \"uint32x4\",\n    Sint32 = \"sint32\",\n    Sint32x2 = \"sint32x2\",\n    Sint32x3 = \"sint32x3\",\n    Sint32x4 = \"sint32x4\",\n    Unorm1010102 = \"unorm10-10-10-2\",\n    Unorm8x4Bgra = \"unorm8x4-bgra\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuVertexState.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = GPUVertexState)]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `GpuVertexState` dictionary.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type GpuVertexState;\n\n    #[doc = \"Get the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"constants\")]\n    pub fn get_constants(this: &GpuVertexState) -> Option<::js_sys::Object>;\n\n    #[doc = \"Change the `constants` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"constants\")]\n    pub fn set_constants(this: &GpuVertexState, val: &::js_sys::Object);\n\n    #[doc = \"Get the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"entryPoint\")]\n    pub fn get_entry_point(this: &GpuVertexState) -> Option<::alloc::string::String>;\n\n    #[doc = \"Change the `entryPoint` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"entryPoint\")]\n    pub fn set_entry_point(this: &GpuVertexState, val: &str);\n\n    #[doc = \"Get the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"module\")]\n    pub fn get_module(this: &GpuVertexState) -> GpuShaderModule;\n\n    #[doc = \"Change the `module` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"module\")]\n    pub fn set_module(this: &GpuVertexState, val: &GpuShaderModule);\n\n    #[doc = \"Get the `buffers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, getter = \"buffers\")]\n    pub fn get_buffers(this: &GpuVertexState) -> Option<::js_sys::Array>;\n\n    #[doc = \"Change the `buffers` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    #[wasm_bindgen(method, setter = \"buffers\")]\n    pub fn set_buffers(this: &GpuVertexState, val: &::wasm_bindgen::JsValue);\n}\n\nimpl GpuVertexState {\n    #[doc = \"Construct a new `GpuVertexState`.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `GpuShaderModule`, `GpuVertexState`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn new(module: &GpuShaderModule) -> Self {\n        #[allow(unused_mut)]\n        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());\n        ret.set_module(module);\n        ret\n    }\n\n    #[deprecated = \"Use `set_constants()` instead.\"]\n    pub fn constants(&mut self, val: &::js_sys::Object) -> &mut Self {\n        self.set_constants(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_entry_point()` instead.\"]\n    pub fn entry_point(&mut self, val: &str) -> &mut Self {\n        self.set_entry_point(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_module()` instead.\"]\n    pub fn module(&mut self, val: &GpuShaderModule) -> &mut Self {\n        self.set_module(val);\n        self\n    }\n\n    #[deprecated = \"Use `set_buffers()` instead.\"]\n    pub fn buffers(&mut self, val: &::wasm_bindgen::JsValue) -> &mut Self {\n        self.set_buffers(val);\n        self\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_GpuVertexStepMode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\n#[doc = \"The `GpuVertexStepMode` enum.\"]\n#[doc = \"\"]\n#[doc = \"*This API requires the following crate features to be activated: `GpuVertexStepMode`*\"]\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum GpuVertexStepMode {\n    Vertex = \"vertex\",\n    Instance = \"instance\",\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_WgslLanguageFeatures.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n#![allow(unused_imports)]\n#![allow(clippy::all)]\nuse super::*;\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\nextern \"C\" {\n    # [wasm_bindgen (extends = :: js_sys :: Object , js_name = WGSLLanguageFeatures , typescript_type = \"WGSLLanguageFeatures\")]\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    #[doc = \"The `WgslLanguageFeatures` class.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub type WgslLanguageFeatures;\n\n    # [wasm_bindgen (structural , method , getter , js_class = \"WGSLLanguageFeatures\" , js_name = size)]\n    #[doc = \"Getter for the `size` field of this object.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/size)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn size(this: &WgslLanguageFeatures) -> u32;\n\n    # [wasm_bindgen (method , structural , js_class = \"WGSLLanguageFeatures\" , js_name = entries)]\n    #[doc = \"The `entries()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/entries)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn entries(this: &WgslLanguageFeatures) -> ::js_sys::Iterator;\n\n    # [wasm_bindgen (catch , method , structural , js_class = \"WGSLLanguageFeatures\" , js_name = forEach)]\n    #[doc = \"The `forEach()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/forEach)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn for_each(\n        this: &WgslLanguageFeatures,\n        callback: &::js_sys::Function,\n    ) -> Result<(), JsValue>;\n\n    # [wasm_bindgen (method , structural , js_class = \"WGSLLanguageFeatures\" , js_name = has)]\n    #[doc = \"The `has()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/has)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn has(this: &WgslLanguageFeatures, value: &str) -> bool;\n\n    # [wasm_bindgen (method , structural , js_class = \"WGSLLanguageFeatures\" , js_name = keys)]\n    #[doc = \"The `keys()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/keys)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn keys(this: &WgslLanguageFeatures) -> ::js_sys::Iterator;\n\n    # [wasm_bindgen (method , structural , js_class = \"WGSLLanguageFeatures\" , js_name = values)]\n    #[doc = \"The `values()` method.\"]\n    #[doc = \"\"]\n    #[doc = \"[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WGSLLanguageFeatures/values)\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `WgslLanguageFeatures`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub fn values(this: &WgslLanguageFeatures) -> ::js_sys::Iterator;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/gen_gpu_map_mode.rs",
    "content": "// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n\n#[doc = \"\"]\n#[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n#[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\npub mod gpu_map_mode {\n    #![allow(unused_imports)]\n    #![allow(clippy::all)]\n    use super::super::*;\n    use wasm_bindgen::prelude::*;\n\n    #[doc = \"The `GPUMapMode.READ` const.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `gpu_map_mode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub const READ: u32 = 1u64 as u32;\n\n    #[doc = \"The `GPUMapMode.WRITE` const.\"]\n    #[doc = \"\"]\n    #[doc = \"*This API requires the following crate features to be activated: `gpu_map_mode`*\"]\n    #[doc = \"\"]\n    #[doc = \"*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as\"]\n    #[doc = \"[described in the `wasm-bindgen` guide](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/unstable-apis.html)*\"]\n    pub const WRITE: u32 = 2u64 as u32;\n}\n"
  },
  {
    "path": "wgpu/src/backend/webgpu/webgpu_sys/mod.rs",
    "content": "//! Bindings to the WebGPU API.\n//!\n//! Internally vendored from the `web-sys` crate until the WebGPU binding are stabilized.\n// DO NOT EDIT THIS FILE!\n//\n// This module part of a subset of web-sys that is used by wgpu's webgpu backend.\n//\n// These bindings are vendored into wgpu for the sole purpose of letting\n// us pin the WebGPU backend to a specific version of the bindings, not\n// to enable local changes. There are no provisions to preserve changes\n// you make here the next time we re-vendor the bindings.\n//\n// The `web-sys` crate does not treat breaking changes to the WebGPU API\n// as semver breaking changes, as WebGPU is \"unstable\". This means Cargo\n// will not let us mix versions of `web-sys`, pinning WebGPU bindings to\n// a specific version, while letting other bindings like WebGL get\n// updated. Vendoring WebGPU was the workaround we chose.\n//\n// Vendoring also allows us to avoid building `web-sys` with\n// `--cfg=web_sys_unstable_apis`, needed to get the WebGPU bindings.\n//\n// If you want to improve the generated code, please submit a PR to the https://github.com/wasm-bindgen/wasm-bindgen repository.\n//\n// This file was generated by the `cargo xtask vendor-web-sys --version f7e0467c4b4d88303eb79fba485320cfb4dfdd58` command.\n\n#![allow(unused_imports, non_snake_case)]\nuse web_sys::{Event, EventTarget};\nmod gen_Gpu;\npub use gen_Gpu::*;\nmod gen_GpuAdapter;\npub use gen_GpuAdapter::*;\nmod gen_GpuAdapterInfo;\npub use gen_GpuAdapterInfo::*;\nmod gen_GpuAddressMode;\npub use gen_GpuAddressMode::*;\nmod gen_GpuAutoLayoutMode;\npub use gen_GpuAutoLayoutMode::*;\nmod gen_GpuBindGroup;\npub use gen_GpuBindGroup::*;\nmod gen_GpuBindGroupDescriptor;\npub use gen_GpuBindGroupDescriptor::*;\nmod gen_GpuBindGroupEntry;\npub use gen_GpuBindGroupEntry::*;\nmod gen_GpuBindGroupLayout;\npub use gen_GpuBindGroupLayout::*;\nmod gen_GpuBindGroupLayoutDescriptor;\npub use gen_GpuBindGroupLayoutDescriptor::*;\nmod gen_GpuBindGroupLayoutEntry;\npub use gen_GpuBindGroupLayoutEntry::*;\nmod gen_GpuBlendComponent;\npub use gen_GpuBlendComponent::*;\nmod gen_GpuBlendFactor;\npub use gen_GpuBlendFactor::*;\nmod gen_GpuBlendOperation;\npub use gen_GpuBlendOperation::*;\nmod gen_GpuBlendState;\npub use gen_GpuBlendState::*;\nmod gen_GpuBuffer;\npub use gen_GpuBuffer::*;\nmod gen_GpuBufferBinding;\npub use gen_GpuBufferBinding::*;\nmod gen_GpuBufferBindingLayout;\npub use gen_GpuBufferBindingLayout::*;\nmod gen_GpuBufferBindingType;\npub use gen_GpuBufferBindingType::*;\nmod gen_GpuBufferDescriptor;\npub use gen_GpuBufferDescriptor::*;\nmod gen_GpuBufferMapState;\npub use gen_GpuBufferMapState::*;\nmod gen_GpuCanvasAlphaMode;\npub use gen_GpuCanvasAlphaMode::*;\nmod gen_GpuCanvasContext;\npub use gen_GpuCanvasContext::*;\nmod gen_GpuCanvasConfiguration;\npub use gen_GpuCanvasConfiguration::*;\nmod gen_GpuCanvasToneMapping;\npub use gen_GpuCanvasToneMapping::*;\nmod gen_GpuCanvasToneMappingMode;\npub use gen_GpuCanvasToneMappingMode::*;\nmod gen_GpuColorDict;\npub use gen_GpuColorDict::*;\nmod gen_GpuColorTargetState;\npub use gen_GpuColorTargetState::*;\nmod gen_GpuCommandBuffer;\npub use gen_GpuCommandBuffer::*;\nmod gen_GpuCommandBufferDescriptor;\npub use gen_GpuCommandBufferDescriptor::*;\nmod gen_GpuCommandEncoder;\npub use gen_GpuCommandEncoder::*;\nmod gen_GpuCommandEncoderDescriptor;\npub use gen_GpuCommandEncoderDescriptor::*;\nmod gen_GpuCompareFunction;\npub use gen_GpuCompareFunction::*;\nmod gen_GpuCompilationInfo;\npub use gen_GpuCompilationInfo::*;\nmod gen_GpuCompilationMessage;\npub use gen_GpuCompilationMessage::*;\nmod gen_GpuCompilationMessageType;\npub use gen_GpuCompilationMessageType::*;\nmod gen_GpuComputePassDescriptor;\npub use gen_GpuComputePassDescriptor::*;\nmod gen_GpuComputePassEncoder;\npub use gen_GpuComputePassEncoder::*;\nmod gen_GpuComputePassTimestampWrites;\npub use gen_GpuComputePassTimestampWrites::*;\nmod gen_GpuComputePipeline;\npub use gen_GpuComputePipeline::*;\nmod gen_GpuComputePipelineDescriptor;\npub use gen_GpuComputePipelineDescriptor::*;\nmod gen_GpuCullMode;\npub use gen_GpuCullMode::*;\nmod gen_GpuDepthStencilState;\npub use gen_GpuDepthStencilState::*;\nmod gen_GpuDevice;\npub use gen_GpuDevice::*;\nmod gen_GpuDeviceDescriptor;\npub use gen_GpuDeviceDescriptor::*;\nmod gen_GpuDeviceLostInfo;\npub use gen_GpuDeviceLostInfo::*;\nmod gen_GpuDeviceLostReason;\npub use gen_GpuDeviceLostReason::*;\nmod gen_GpuError;\npub use gen_GpuError::*;\nmod gen_GpuErrorFilter;\npub use gen_GpuErrorFilter::*;\nmod gen_GpuExternalTexture;\npub use gen_GpuExternalTexture::*;\nmod gen_GpuExternalTextureBindingLayout;\npub use gen_GpuExternalTextureBindingLayout::*;\nmod gen_GpuExternalTextureDescriptor;\npub use gen_GpuExternalTextureDescriptor::*;\nmod gen_GpuExtent3dDict;\npub use gen_GpuExtent3dDict::*;\nmod gen_GpuFeatureName;\npub use gen_GpuFeatureName::*;\nmod gen_GpuFilterMode;\npub use gen_GpuFilterMode::*;\nmod gen_GpuFragmentState;\npub use gen_GpuFragmentState::*;\nmod gen_GpuFrontFace;\npub use gen_GpuFrontFace::*;\nmod gen_GpuTexelCopyBufferInfo;\npub use gen_GpuTexelCopyBufferInfo::*;\nmod gen_GpuCopyExternalImageSourceInfo;\npub use gen_GpuCopyExternalImageSourceInfo::*;\nmod gen_GpuTexelCopyTextureInfo;\npub use gen_GpuTexelCopyTextureInfo::*;\nmod gen_GpuCopyExternalImageDestInfo;\npub use gen_GpuCopyExternalImageDestInfo::*;\nmod gen_GpuTexelCopyBufferLayout;\npub use gen_GpuTexelCopyBufferLayout::*;\nmod gen_GpuIndexFormat;\npub use gen_GpuIndexFormat::*;\nmod gen_GpuLoadOp;\npub use gen_GpuLoadOp::*;\nmod gen_gpu_map_mode;\npub use gen_gpu_map_mode::*;\nmod gen_GpuMipmapFilterMode;\npub use gen_GpuMipmapFilterMode::*;\nmod gen_GpuMultisampleState;\npub use gen_GpuMultisampleState::*;\nmod gen_GpuObjectDescriptorBase;\npub use gen_GpuObjectDescriptorBase::*;\nmod gen_GpuOrigin2dDict;\npub use gen_GpuOrigin2dDict::*;\nmod gen_GpuOrigin3dDict;\npub use gen_GpuOrigin3dDict::*;\nmod gen_GpuOutOfMemoryError;\npub use gen_GpuOutOfMemoryError::*;\nmod gen_GpuPipelineDescriptorBase;\npub use gen_GpuPipelineDescriptorBase::*;\nmod gen_GpuPipelineLayout;\npub use gen_GpuPipelineLayout::*;\nmod gen_GpuPipelineLayoutDescriptor;\npub use gen_GpuPipelineLayoutDescriptor::*;\nmod gen_GpuPowerPreference;\npub use gen_GpuPowerPreference::*;\nmod gen_GpuPrimitiveState;\npub use gen_GpuPrimitiveState::*;\nmod gen_GpuPrimitiveTopology;\npub use gen_GpuPrimitiveTopology::*;\nmod gen_GpuProgrammableStage;\npub use gen_GpuProgrammableStage::*;\nmod gen_GpuQuerySet;\npub use gen_GpuQuerySet::*;\nmod gen_GpuQuerySetDescriptor;\npub use gen_GpuQuerySetDescriptor::*;\nmod gen_GpuQueryType;\npub use gen_GpuQueryType::*;\nmod gen_GpuQueue;\npub use gen_GpuQueue::*;\nmod gen_GpuQueueDescriptor;\npub use gen_GpuQueueDescriptor::*;\nmod gen_GpuRenderBundle;\npub use gen_GpuRenderBundle::*;\nmod gen_GpuRenderBundleDescriptor;\npub use gen_GpuRenderBundleDescriptor::*;\nmod gen_GpuRenderBundleEncoder;\npub use gen_GpuRenderBundleEncoder::*;\nmod gen_GpuRenderBundleEncoderDescriptor;\npub use gen_GpuRenderBundleEncoderDescriptor::*;\nmod gen_GpuRenderPassColorAttachment;\npub use gen_GpuRenderPassColorAttachment::*;\nmod gen_GpuRenderPassDepthStencilAttachment;\npub use gen_GpuRenderPassDepthStencilAttachment::*;\nmod gen_GpuRenderPassDescriptor;\npub use gen_GpuRenderPassDescriptor::*;\nmod gen_GpuRenderPassEncoder;\npub use gen_GpuRenderPassEncoder::*;\nmod gen_GpuRenderPassTimestampWrites;\npub use gen_GpuRenderPassTimestampWrites::*;\nmod gen_GpuRenderPipeline;\npub use gen_GpuRenderPipeline::*;\nmod gen_GpuRenderPipelineDescriptor;\npub use gen_GpuRenderPipelineDescriptor::*;\nmod gen_GpuRequestAdapterOptions;\npub use gen_GpuRequestAdapterOptions::*;\nmod gen_GpuSampler;\npub use gen_GpuSampler::*;\nmod gen_GpuSamplerBindingLayout;\npub use gen_GpuSamplerBindingLayout::*;\nmod gen_GpuSamplerBindingType;\npub use gen_GpuSamplerBindingType::*;\nmod gen_GpuSamplerDescriptor;\npub use gen_GpuSamplerDescriptor::*;\nmod gen_GpuShaderModule;\npub use gen_GpuShaderModule::*;\nmod gen_GpuShaderModuleDescriptor;\npub use gen_GpuShaderModuleDescriptor::*;\nmod gen_GpuStencilFaceState;\npub use gen_GpuStencilFaceState::*;\nmod gen_GpuStencilOperation;\npub use gen_GpuStencilOperation::*;\nmod gen_GpuStorageTextureAccess;\npub use gen_GpuStorageTextureAccess::*;\nmod gen_GpuStorageTextureBindingLayout;\npub use gen_GpuStorageTextureBindingLayout::*;\nmod gen_GpuStoreOp;\npub use gen_GpuStoreOp::*;\nmod gen_GpuSupportedFeatures;\npub use gen_GpuSupportedFeatures::*;\nmod gen_GpuSupportedLimits;\npub use gen_GpuSupportedLimits::*;\nmod gen_GpuTexture;\npub use gen_GpuTexture::*;\nmod gen_GpuTextureAspect;\npub use gen_GpuTextureAspect::*;\nmod gen_GpuTextureBindingLayout;\npub use gen_GpuTextureBindingLayout::*;\nmod gen_GpuTextureDescriptor;\npub use gen_GpuTextureDescriptor::*;\nmod gen_GpuTextureDimension;\npub use gen_GpuTextureDimension::*;\nmod gen_GpuTextureFormat;\npub use gen_GpuTextureFormat::*;\nmod gen_GpuTextureSampleType;\npub use gen_GpuTextureSampleType::*;\nmod gen_GpuTextureView;\npub use gen_GpuTextureView::*;\nmod gen_GpuTextureViewDescriptor;\npub use gen_GpuTextureViewDescriptor::*;\nmod gen_GpuTextureViewDimension;\npub use gen_GpuTextureViewDimension::*;\nmod gen_GpuUncapturedErrorEvent;\npub use gen_GpuUncapturedErrorEvent::*;\nmod gen_GpuUncapturedErrorEventInit;\npub use gen_GpuUncapturedErrorEventInit::*;\nmod gen_GpuValidationError;\npub use gen_GpuValidationError::*;\nmod gen_GpuVertexAttribute;\npub use gen_GpuVertexAttribute::*;\nmod gen_GpuVertexBufferLayout;\npub use gen_GpuVertexBufferLayout::*;\nmod gen_GpuVertexFormat;\npub use gen_GpuVertexFormat::*;\nmod gen_GpuVertexState;\npub use gen_GpuVertexState::*;\nmod gen_GpuVertexStepMode;\npub use gen_GpuVertexStepMode::*;\nmod gen_WgslLanguageFeatures;\npub use gen_WgslLanguageFeatures::*;\n"
  },
  {
    "path": "wgpu/src/backend/webgpu.rs",
    "content": "#![allow(clippy::type_complexity)]\n\nmod defined_non_null_js_value;\nmod ext_bindings;\n#[allow(clippy::allow_attributes)]\nmod webgpu_sys;\n\nuse alloc::{\n    boxed::Box,\n    format,\n    rc::Rc,\n    string::{String, ToString as _},\n    sync::Arc,\n    vec,\n    vec::Vec,\n};\nuse core::{\n    cell::{Cell, OnceCell, RefCell},\n    fmt,\n    future::Future,\n    ops::Range,\n    pin::Pin,\n    task::{self, Poll},\n};\nuse wgt::Backends;\n\nuse js_sys::Promise;\nuse wasm_bindgen::{prelude::*, JsCast};\n\nuse crate::{\n    dispatch::{self, BlasCompactCallback},\n    Blas, SurfaceTargetUnsafe, Tlas, WriteOnly,\n};\n\nuse defined_non_null_js_value::DefinedNonNullJsValue;\n\n// We need to mark various types as Send and Sync to satisfy the Rust type system.\n//\n// SAFETY: All webgpu handle types in wasm32 are internally a `JsValue`, and `JsValue` is neither\n// Send nor Sync.  Currently, wasm32 has no threading support by default, so implementing `Send` or\n// `Sync` for a type is harmless. However, nightly Rust supports compiling wasm with experimental\n// threading support via `--target-features`. If `wgpu` is being compiled with those features, we do\n// not implement `Send` and `Sync` on the webgpu handle types.\nmacro_rules! impl_send_sync {\n    ($name:ty) => {\n        #[cfg(send_sync)]\n        unsafe impl Send for $name {}\n        #[cfg(send_sync)]\n        unsafe impl Sync for $name {}\n    };\n}\n\n#[derive(Clone)]\npub struct ContextWebGpu {\n    /// `None` if browser does not advertise support for WebGPU.\n    gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,\n    /// Unique identifier for this context.\n    ident: crate::cmp::Identifier,\n    /// Backends requested in the [`crate::InstanceDescriptor`].\n    /// Remembered for error reporting even though this itself is strictly\n    /// [`Backends::BROWSER_WEBGPU`].\n    requested_backends: Backends,\n}\n\nimpl fmt::Debug for ContextWebGpu {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ContextWebGpu\")\n            .field(\"type\", &\"Web\")\n            .finish()\n    }\n}\n\nimpl crate::Error {\n    fn from_js(js_error: js_sys::Object) -> Self {\n        let source = Box::<dyn core::error::Error + Send + Sync>::from(\"<WebGPU Error>\");\n        if let Some(js_error) = js_error.dyn_ref::<webgpu_sys::GpuValidationError>() {\n            crate::Error::Validation {\n                source,\n                description: js_error.message(),\n            }\n        } else if js_error.has_type::<webgpu_sys::GpuOutOfMemoryError>() {\n            crate::Error::OutOfMemory { source }\n        } else {\n            panic!(\"Unexpected error\");\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct WebShaderModule {\n    module: webgpu_sys::GpuShaderModule,\n    compilation_info: WebShaderCompilationInfo,\n    /// Unique identifier for this shader module.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\nenum WebShaderCompilationInfo {\n    /// WGSL shaders get their compilation info from a native WebGPU function.\n    /// We need the source to be able to do UTF16 to UTF8 location remapping.\n    Wgsl { source: String },\n    /// Transformed shaders get their compilation info from the transformer.\n    /// Further compilation errors are reported without a span.\n    Transformed {\n        compilation_info: crate::CompilationInfo,\n    },\n}\n\nfn map_utf16_to_utf8_offset(utf16_offset: u32, text: &str) -> u32 {\n    let mut utf16_i = 0;\n    for (utf8_index, c) in text.char_indices() {\n        if utf16_i >= utf16_offset {\n            return utf8_index as u32;\n        }\n        utf16_i += c.len_utf16() as u32;\n    }\n    if utf16_i >= utf16_offset {\n        text.len() as u32\n    } else {\n        log::error!(\"UTF16 offset {utf16_offset} is out of bounds for string {text}\");\n        u32::MAX\n    }\n}\n\nimpl crate::CompilationMessage {\n    fn from_js(\n        js_message: webgpu_sys::GpuCompilationMessage,\n        compilation_info: &WebShaderCompilationInfo,\n    ) -> Self {\n        let message_type = match js_message.type_() {\n            webgpu_sys::GpuCompilationMessageType::Error => crate::CompilationMessageType::Error,\n            webgpu_sys::GpuCompilationMessageType::Warning => {\n                crate::CompilationMessageType::Warning\n            }\n            webgpu_sys::GpuCompilationMessageType::Info => crate::CompilationMessageType::Info,\n            _ => crate::CompilationMessageType::Error,\n        };\n        let utf16_offset = js_message.offset() as u32;\n        let utf16_length = js_message.length() as u32;\n        let span = match compilation_info {\n            WebShaderCompilationInfo::Wgsl { .. } if utf16_offset == 0 && utf16_length == 0 => None,\n            WebShaderCompilationInfo::Wgsl { source } => {\n                let offset = map_utf16_to_utf8_offset(utf16_offset, source);\n                let length = map_utf16_to_utf8_offset(utf16_length, &source[offset as usize..]);\n                let line_number = js_message.line_num() as u32; // That's legal, because we're counting lines the same way\n\n                let prefix = &source[..offset as usize];\n                let line_start = prefix.rfind('\\n').map(|pos| pos + 1).unwrap_or(0) as u32;\n                let line_position = offset - line_start + 1; // Counting UTF-8 byte indices\n\n                Some(crate::SourceLocation {\n                    offset,\n                    length,\n                    line_number,\n                    line_position,\n                })\n            }\n            WebShaderCompilationInfo::Transformed { .. } => None,\n        };\n\n        crate::CompilationMessage {\n            message: js_message.message(),\n            message_type,\n            location: span,\n        }\n    }\n}\n\n// We need to assert that any future we return is Send to match the native API.\n//\n// This is safe on wasm32 *for now*, but similarly to the unsafe Send impls for the handle type\n// wrappers, the full story for threading on wasm32 is still unfolding.\n\npub(crate) struct MakeSendFuture<F, M> {\n    future: F,\n    map: M,\n}\n\nimpl<F: Future, M: Fn(F::Output) -> T, T> Future for MakeSendFuture<F, M> {\n    type Output = T;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {\n        // This is safe because we have no Drop implementation to violate the Pin requirements and\n        // do not provide any means of moving the inner future.\n        unsafe {\n            let this = self.get_unchecked_mut();\n            match Pin::new_unchecked(&mut this.future).poll(cx) {\n                task::Poll::Ready(value) => task::Poll::Ready((this.map)(value)),\n                task::Poll::Pending => task::Poll::Pending,\n            }\n        }\n    }\n}\n\nimpl<F, M> MakeSendFuture<F, M> {\n    fn new(future: F, map: M) -> Self {\n        Self { future, map }\n    }\n}\n\n#[cfg(send_sync)]\nunsafe impl<F, M> Send for MakeSendFuture<F, M> {}\n\nfn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat {\n    use webgpu_sys::GpuTextureFormat as tf;\n    use wgt::TextureFormat;\n    match texture_format {\n        // 8-bit formats\n        TextureFormat::R8Unorm => tf::R8unorm,\n        TextureFormat::R8Snorm => tf::R8snorm,\n        TextureFormat::R8Uint => tf::R8uint,\n        TextureFormat::R8Sint => tf::R8sint,\n        // 16-bit formats\n        TextureFormat::R16Uint => tf::R16uint,\n        TextureFormat::R16Sint => tf::R16sint,\n        TextureFormat::R16Float => tf::R16float,\n        TextureFormat::Rg8Unorm => tf::Rg8unorm,\n        TextureFormat::Rg8Snorm => tf::Rg8snorm,\n        TextureFormat::Rg8Uint => tf::Rg8uint,\n        TextureFormat::Rg8Sint => tf::Rg8sint,\n        // 32-bit formats\n        TextureFormat::R32Uint => tf::R32uint,\n        TextureFormat::R32Sint => tf::R32sint,\n        TextureFormat::R32Float => tf::R32float,\n        TextureFormat::Rg16Uint => tf::Rg16uint,\n        TextureFormat::Rg16Sint => tf::Rg16sint,\n        TextureFormat::Rg16Float => tf::Rg16float,\n        TextureFormat::Rgba8Unorm => tf::Rgba8unorm,\n        TextureFormat::Rgba8UnormSrgb => tf::Rgba8unormSrgb,\n        TextureFormat::Rgba8Snorm => tf::Rgba8snorm,\n        TextureFormat::Rgba8Uint => tf::Rgba8uint,\n        TextureFormat::Rgba8Sint => tf::Rgba8sint,\n        TextureFormat::Bgra8Unorm => tf::Bgra8unorm,\n        TextureFormat::Bgra8UnormSrgb => tf::Bgra8unormSrgb,\n        // Packed 32-bit formats\n        TextureFormat::Rgb9e5Ufloat => tf::Rgb9e5ufloat,\n        TextureFormat::Rgb10a2Uint => tf::Rgb10a2uint,\n        TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm,\n        TextureFormat::Rg11b10Ufloat => tf::Rg11b10ufloat,\n        // 64-bit formats\n        TextureFormat::Rg32Uint => tf::Rg32uint,\n        TextureFormat::Rg32Sint => tf::Rg32sint,\n        TextureFormat::Rg32Float => tf::Rg32float,\n        TextureFormat::Rgba16Uint => tf::Rgba16uint,\n        TextureFormat::Rgba16Sint => tf::Rgba16sint,\n        TextureFormat::Rgba16Float => tf::Rgba16float,\n        // 128-bit formats\n        TextureFormat::Rgba32Uint => tf::Rgba32uint,\n        TextureFormat::Rgba32Sint => tf::Rgba32sint,\n        TextureFormat::Rgba32Float => tf::Rgba32float,\n        // Depth/stencil formats\n        TextureFormat::Stencil8 => tf::Stencil8,\n        TextureFormat::Depth16Unorm => tf::Depth16unorm,\n        TextureFormat::Depth24Plus => tf::Depth24plus,\n        TextureFormat::Depth24PlusStencil8 => tf::Depth24plusStencil8,\n        TextureFormat::Depth32Float => tf::Depth32float,\n        // \"depth32float-stencil8\" feature\n        TextureFormat::Depth32FloatStencil8 => tf::Depth32floatStencil8,\n\n        TextureFormat::Bc1RgbaUnorm => tf::Bc1RgbaUnorm,\n        TextureFormat::Bc1RgbaUnormSrgb => tf::Bc1RgbaUnormSrgb,\n        TextureFormat::Bc2RgbaUnorm => tf::Bc2RgbaUnorm,\n        TextureFormat::Bc2RgbaUnormSrgb => tf::Bc2RgbaUnormSrgb,\n        TextureFormat::Bc3RgbaUnorm => tf::Bc3RgbaUnorm,\n        TextureFormat::Bc3RgbaUnormSrgb => tf::Bc3RgbaUnormSrgb,\n        TextureFormat::Bc4RUnorm => tf::Bc4RUnorm,\n        TextureFormat::Bc4RSnorm => tf::Bc4RSnorm,\n        TextureFormat::Bc5RgUnorm => tf::Bc5RgUnorm,\n        TextureFormat::Bc5RgSnorm => tf::Bc5RgSnorm,\n        TextureFormat::Bc6hRgbUfloat => tf::Bc6hRgbUfloat,\n        TextureFormat::Bc6hRgbFloat => tf::Bc6hRgbFloat,\n        TextureFormat::Bc7RgbaUnorm => tf::Bc7RgbaUnorm,\n        TextureFormat::Bc7RgbaUnormSrgb => tf::Bc7RgbaUnormSrgb,\n        TextureFormat::Etc2Rgb8Unorm => tf::Etc2Rgb8unorm,\n        TextureFormat::Etc2Rgb8UnormSrgb => tf::Etc2Rgb8unormSrgb,\n        TextureFormat::Etc2Rgb8A1Unorm => tf::Etc2Rgb8a1unorm,\n        TextureFormat::Etc2Rgb8A1UnormSrgb => tf::Etc2Rgb8a1unormSrgb,\n        TextureFormat::Etc2Rgba8Unorm => tf::Etc2Rgba8unorm,\n        TextureFormat::Etc2Rgba8UnormSrgb => tf::Etc2Rgba8unormSrgb,\n        TextureFormat::EacR11Unorm => tf::EacR11unorm,\n        TextureFormat::EacR11Snorm => tf::EacR11snorm,\n        TextureFormat::EacRg11Unorm => tf::EacRg11unorm,\n        TextureFormat::EacRg11Snorm => tf::EacRg11snorm,\n        TextureFormat::Astc { block, channel } => match channel {\n            wgt::AstcChannel::Unorm => match block {\n                wgt::AstcBlock::B4x4 => tf::Astc4x4Unorm,\n                wgt::AstcBlock::B5x4 => tf::Astc5x4Unorm,\n                wgt::AstcBlock::B5x5 => tf::Astc5x5Unorm,\n                wgt::AstcBlock::B6x5 => tf::Astc6x5Unorm,\n                wgt::AstcBlock::B6x6 => tf::Astc6x6Unorm,\n                wgt::AstcBlock::B8x5 => tf::Astc8x5Unorm,\n                wgt::AstcBlock::B8x6 => tf::Astc8x6Unorm,\n                wgt::AstcBlock::B8x8 => tf::Astc8x8Unorm,\n                wgt::AstcBlock::B10x5 => tf::Astc10x5Unorm,\n                wgt::AstcBlock::B10x6 => tf::Astc10x6Unorm,\n                wgt::AstcBlock::B10x8 => tf::Astc10x8Unorm,\n                wgt::AstcBlock::B10x10 => tf::Astc10x10Unorm,\n                wgt::AstcBlock::B12x10 => tf::Astc12x10Unorm,\n                wgt::AstcBlock::B12x12 => tf::Astc12x12Unorm,\n            },\n            wgt::AstcChannel::UnormSrgb => match block {\n                wgt::AstcBlock::B4x4 => tf::Astc4x4UnormSrgb,\n                wgt::AstcBlock::B5x4 => tf::Astc5x4UnormSrgb,\n                wgt::AstcBlock::B5x5 => tf::Astc5x5UnormSrgb,\n                wgt::AstcBlock::B6x5 => tf::Astc6x5UnormSrgb,\n                wgt::AstcBlock::B6x6 => tf::Astc6x6UnormSrgb,\n                wgt::AstcBlock::B8x5 => tf::Astc8x5UnormSrgb,\n                wgt::AstcBlock::B8x6 => tf::Astc8x6UnormSrgb,\n                wgt::AstcBlock::B8x8 => tf::Astc8x8UnormSrgb,\n                wgt::AstcBlock::B10x5 => tf::Astc10x5UnormSrgb,\n                wgt::AstcBlock::B10x6 => tf::Astc10x6UnormSrgb,\n                wgt::AstcBlock::B10x8 => tf::Astc10x8UnormSrgb,\n                wgt::AstcBlock::B10x10 => tf::Astc10x10UnormSrgb,\n                wgt::AstcBlock::B12x10 => tf::Astc12x10UnormSrgb,\n                wgt::AstcBlock::B12x12 => tf::Astc12x12UnormSrgb,\n            },\n            wgt::AstcChannel::Hdr => {\n                unimplemented!(\"Format {texture_format:?} has no WebGPU equivalent\")\n            }\n        },\n        _ => unimplemented!(\"Format {texture_format:?} has no WebGPU equivalent\"),\n    }\n}\n\nfn map_texture_component_type(\n    sample_type: wgt::TextureSampleType,\n) -> webgpu_sys::GpuTextureSampleType {\n    use webgpu_sys::GpuTextureSampleType as ts;\n    use wgt::TextureSampleType;\n    match sample_type {\n        TextureSampleType::Float { filterable: true } => ts::Float,\n        TextureSampleType::Float { filterable: false } => ts::UnfilterableFloat,\n        TextureSampleType::Sint => ts::Sint,\n        TextureSampleType::Uint => ts::Uint,\n        TextureSampleType::Depth => ts::Depth,\n    }\n}\n\nfn map_cull_mode(cull_mode: Option<wgt::Face>) -> webgpu_sys::GpuCullMode {\n    use webgpu_sys::GpuCullMode as cm;\n    use wgt::Face;\n    match cull_mode {\n        None => cm::None,\n        Some(Face::Front) => cm::Front,\n        Some(Face::Back) => cm::Back,\n    }\n}\n\nfn map_front_face(front_face: wgt::FrontFace) -> webgpu_sys::GpuFrontFace {\n    use webgpu_sys::GpuFrontFace as ff;\n    use wgt::FrontFace;\n    match front_face {\n        FrontFace::Ccw => ff::Ccw,\n        FrontFace::Cw => ff::Cw,\n    }\n}\n\nfn map_primitive_state(primitive: &wgt::PrimitiveState) -> webgpu_sys::GpuPrimitiveState {\n    use webgpu_sys::GpuPrimitiveTopology as pt;\n    use wgt::PrimitiveTopology;\n\n    let mapped = webgpu_sys::GpuPrimitiveState::new();\n    mapped.set_cull_mode(map_cull_mode(primitive.cull_mode));\n    mapped.set_front_face(map_front_face(primitive.front_face));\n\n    if let Some(format) = primitive.strip_index_format {\n        mapped.set_strip_index_format(map_index_format(format));\n    }\n\n    mapped.set_topology(match primitive.topology {\n        PrimitiveTopology::PointList => pt::PointList,\n        PrimitiveTopology::LineList => pt::LineList,\n        PrimitiveTopology::LineStrip => pt::LineStrip,\n        PrimitiveTopology::TriangleList => pt::TriangleList,\n        PrimitiveTopology::TriangleStrip => pt::TriangleStrip,\n    });\n\n    mapped.set_unclipped_depth(primitive.unclipped_depth);\n\n    match primitive.polygon_mode {\n        wgt::PolygonMode::Fill => {}\n        wgt::PolygonMode::Line => panic!(\n            \"{:?} is not enabled for this backend\",\n            wgt::Features::POLYGON_MODE_LINE\n        ),\n        wgt::PolygonMode::Point => panic!(\n            \"{:?} is not enabled for this backend\",\n            wgt::Features::POLYGON_MODE_POINT\n        ),\n    }\n\n    mapped\n}\n\nfn map_compare_function(compare_fn: wgt::CompareFunction) -> webgpu_sys::GpuCompareFunction {\n    use webgpu_sys::GpuCompareFunction as cf;\n    use wgt::CompareFunction;\n    match compare_fn {\n        CompareFunction::Never => cf::Never,\n        CompareFunction::Less => cf::Less,\n        CompareFunction::Equal => cf::Equal,\n        CompareFunction::LessEqual => cf::LessEqual,\n        CompareFunction::Greater => cf::Greater,\n        CompareFunction::NotEqual => cf::NotEqual,\n        CompareFunction::GreaterEqual => cf::GreaterEqual,\n        CompareFunction::Always => cf::Always,\n    }\n}\n\nfn map_stencil_operation(op: wgt::StencilOperation) -> webgpu_sys::GpuStencilOperation {\n    use webgpu_sys::GpuStencilOperation as so;\n    use wgt::StencilOperation;\n    match op {\n        StencilOperation::Keep => so::Keep,\n        StencilOperation::Zero => so::Zero,\n        StencilOperation::Replace => so::Replace,\n        StencilOperation::Invert => so::Invert,\n        StencilOperation::IncrementClamp => so::IncrementClamp,\n        StencilOperation::DecrementClamp => so::DecrementClamp,\n        StencilOperation::IncrementWrap => so::IncrementWrap,\n        StencilOperation::DecrementWrap => so::DecrementWrap,\n    }\n}\n\nfn map_stencil_state_face(desc: &wgt::StencilFaceState) -> webgpu_sys::GpuStencilFaceState {\n    let mapped = webgpu_sys::GpuStencilFaceState::new();\n    mapped.set_compare(map_compare_function(desc.compare));\n    mapped.set_depth_fail_op(map_stencil_operation(desc.depth_fail_op));\n    mapped.set_fail_op(map_stencil_operation(desc.fail_op));\n    mapped.set_pass_op(map_stencil_operation(desc.pass_op));\n    mapped\n}\n\nfn map_depth_stencil_state(desc: &wgt::DepthStencilState) -> webgpu_sys::GpuDepthStencilState {\n    let mapped = webgpu_sys::GpuDepthStencilState::new(map_texture_format(desc.format));\n    if let Some(compare) = desc.depth_compare {\n        mapped.set_depth_compare(map_compare_function(compare));\n    }\n    if let Some(write_enabled) = desc.depth_write_enabled {\n        mapped.set_depth_write_enabled(write_enabled);\n    }\n    mapped.set_depth_bias(desc.bias.constant);\n    mapped.set_depth_bias_clamp(desc.bias.clamp);\n    mapped.set_depth_bias_slope_scale(desc.bias.slope_scale);\n    mapped.set_stencil_back(&map_stencil_state_face(&desc.stencil.back));\n    mapped.set_stencil_front(&map_stencil_state_face(&desc.stencil.front));\n    mapped.set_stencil_read_mask(desc.stencil.read_mask);\n    mapped.set_stencil_write_mask(desc.stencil.write_mask);\n    mapped\n}\n\nfn map_blend_component(desc: &wgt::BlendComponent) -> webgpu_sys::GpuBlendComponent {\n    let mapped = webgpu_sys::GpuBlendComponent::new();\n    mapped.set_dst_factor(map_blend_factor(desc.dst_factor));\n    mapped.set_operation(map_blend_operation(desc.operation));\n    mapped.set_src_factor(map_blend_factor(desc.src_factor));\n    mapped\n}\n\nfn map_blend_factor(factor: wgt::BlendFactor) -> webgpu_sys::GpuBlendFactor {\n    use webgpu_sys::GpuBlendFactor as bf;\n    use wgt::BlendFactor;\n    match factor {\n        BlendFactor::Zero => bf::Zero,\n        BlendFactor::One => bf::One,\n        BlendFactor::Src => bf::Src,\n        BlendFactor::OneMinusSrc => bf::OneMinusSrc,\n        BlendFactor::SrcAlpha => bf::SrcAlpha,\n        BlendFactor::OneMinusSrcAlpha => bf::OneMinusSrcAlpha,\n        BlendFactor::Dst => bf::Dst,\n        BlendFactor::OneMinusDst => bf::OneMinusDst,\n        BlendFactor::DstAlpha => bf::DstAlpha,\n        BlendFactor::OneMinusDstAlpha => bf::OneMinusDstAlpha,\n        BlendFactor::SrcAlphaSaturated => bf::SrcAlphaSaturated,\n        BlendFactor::Constant => bf::Constant,\n        BlendFactor::OneMinusConstant => bf::OneMinusConstant,\n        BlendFactor::Src1 => bf::Src1,\n        BlendFactor::OneMinusSrc1 => bf::OneMinusSrc1,\n        BlendFactor::Src1Alpha => bf::Src1Alpha,\n        BlendFactor::OneMinusSrc1Alpha => bf::OneMinusSrc1Alpha,\n    }\n}\n\nfn map_blend_operation(op: wgt::BlendOperation) -> webgpu_sys::GpuBlendOperation {\n    use webgpu_sys::GpuBlendOperation as bo;\n    use wgt::BlendOperation;\n    match op {\n        BlendOperation::Add => bo::Add,\n        BlendOperation::Subtract => bo::Subtract,\n        BlendOperation::ReverseSubtract => bo::ReverseSubtract,\n        BlendOperation::Min => bo::Min,\n        BlendOperation::Max => bo::Max,\n    }\n}\n\nfn map_index_format(format: wgt::IndexFormat) -> webgpu_sys::GpuIndexFormat {\n    use webgpu_sys::GpuIndexFormat as f;\n    use wgt::IndexFormat;\n    match format {\n        IndexFormat::Uint16 => f::Uint16,\n        IndexFormat::Uint32 => f::Uint32,\n    }\n}\n\nfn map_vertex_format(format: wgt::VertexFormat) -> webgpu_sys::GpuVertexFormat {\n    use webgpu_sys::GpuVertexFormat as vf;\n    use wgt::VertexFormat;\n    match format {\n        VertexFormat::Uint8 => vf::Uint8,\n        VertexFormat::Uint8x2 => vf::Uint8x2,\n        VertexFormat::Uint8x4 => vf::Uint8x4,\n        VertexFormat::Sint8 => vf::Sint8,\n        VertexFormat::Sint8x2 => vf::Sint8x2,\n        VertexFormat::Sint8x4 => vf::Sint8x4,\n        VertexFormat::Unorm8 => vf::Unorm8,\n        VertexFormat::Unorm8x2 => vf::Unorm8x2,\n        VertexFormat::Unorm8x4 => vf::Unorm8x4,\n        VertexFormat::Snorm8 => vf::Snorm8,\n        VertexFormat::Snorm8x2 => vf::Snorm8x2,\n        VertexFormat::Snorm8x4 => vf::Snorm8x4,\n        VertexFormat::Uint16 => vf::Uint16,\n        VertexFormat::Uint16x2 => vf::Uint16x2,\n        VertexFormat::Uint16x4 => vf::Uint16x4,\n        VertexFormat::Sint16 => vf::Sint16,\n        VertexFormat::Sint16x2 => vf::Sint16x2,\n        VertexFormat::Sint16x4 => vf::Sint16x4,\n        VertexFormat::Unorm16 => vf::Unorm16,\n        VertexFormat::Unorm16x2 => vf::Unorm16x2,\n        VertexFormat::Unorm16x4 => vf::Unorm16x4,\n        VertexFormat::Snorm16 => vf::Snorm16,\n        VertexFormat::Snorm16x2 => vf::Snorm16x2,\n        VertexFormat::Snorm16x4 => vf::Snorm16x4,\n        VertexFormat::Float16 => vf::Float16,\n        VertexFormat::Float16x2 => vf::Float16x2,\n        VertexFormat::Float16x4 => vf::Float16x4,\n        VertexFormat::Float32 => vf::Float32,\n        VertexFormat::Float32x2 => vf::Float32x2,\n        VertexFormat::Float32x3 => vf::Float32x3,\n        VertexFormat::Float32x4 => vf::Float32x4,\n        VertexFormat::Uint32 => vf::Uint32,\n        VertexFormat::Uint32x2 => vf::Uint32x2,\n        VertexFormat::Uint32x3 => vf::Uint32x3,\n        VertexFormat::Uint32x4 => vf::Uint32x4,\n        VertexFormat::Sint32 => vf::Sint32,\n        VertexFormat::Sint32x2 => vf::Sint32x2,\n        VertexFormat::Sint32x3 => vf::Sint32x3,\n        VertexFormat::Sint32x4 => vf::Sint32x4,\n        VertexFormat::Unorm10_10_10_2 => vf::Unorm1010102,\n        VertexFormat::Unorm8x4Bgra => vf::Unorm8x4Bgra,\n        VertexFormat::Float64\n        | VertexFormat::Float64x2\n        | VertexFormat::Float64x3\n        | VertexFormat::Float64x4 => {\n            panic!(\"VERTEX_ATTRIBUTE_64BIT feature must be enabled to use Double formats\")\n        }\n    }\n}\n\nfn map_vertex_step_mode(mode: wgt::VertexStepMode) -> webgpu_sys::GpuVertexStepMode {\n    use webgpu_sys::GpuVertexStepMode as sm;\n    use wgt::VertexStepMode;\n    match mode {\n        VertexStepMode::Vertex => sm::Vertex,\n        VertexStepMode::Instance => sm::Instance,\n    }\n}\n\nfn map_extent_3d(extent: wgt::Extent3d) -> webgpu_sys::GpuExtent3dDict {\n    let mapped = webgpu_sys::GpuExtent3dDict::new(extent.width);\n    mapped.set_height(extent.height);\n    mapped.set_depth_or_array_layers(extent.depth_or_array_layers);\n    mapped\n}\n\nfn map_origin_2d(extent: wgt::Origin2d) -> webgpu_sys::GpuOrigin2dDict {\n    let mapped = webgpu_sys::GpuOrigin2dDict::new();\n    mapped.set_x(extent.x);\n    mapped.set_y(extent.y);\n    mapped\n}\n\nfn map_origin_3d(origin: wgt::Origin3d) -> webgpu_sys::GpuOrigin3dDict {\n    let mapped = webgpu_sys::GpuOrigin3dDict::new();\n    mapped.set_x(origin.x);\n    mapped.set_y(origin.y);\n    mapped.set_z(origin.z);\n    mapped\n}\n\nfn map_texture_dimension(\n    texture_dimension: wgt::TextureDimension,\n) -> webgpu_sys::GpuTextureDimension {\n    match texture_dimension {\n        wgt::TextureDimension::D1 => webgpu_sys::GpuTextureDimension::N1d,\n        wgt::TextureDimension::D2 => webgpu_sys::GpuTextureDimension::N2d,\n        wgt::TextureDimension::D3 => webgpu_sys::GpuTextureDimension::N3d,\n    }\n}\n\nfn map_texture_view_dimension(\n    texture_view_dimension: wgt::TextureViewDimension,\n) -> webgpu_sys::GpuTextureViewDimension {\n    use webgpu_sys::GpuTextureViewDimension as tvd;\n    match texture_view_dimension {\n        wgt::TextureViewDimension::D1 => tvd::N1d,\n        wgt::TextureViewDimension::D2 => tvd::N2d,\n        wgt::TextureViewDimension::D2Array => tvd::N2dArray,\n        wgt::TextureViewDimension::Cube => tvd::Cube,\n        wgt::TextureViewDimension::CubeArray => tvd::CubeArray,\n        wgt::TextureViewDimension::D3 => tvd::N3d,\n    }\n}\n\nfn map_buffer_copy_view(\n    view: crate::TexelCopyBufferInfo<'_>,\n) -> webgpu_sys::GpuTexelCopyBufferInfo {\n    let buffer = view.buffer.inner.as_webgpu();\n    let mapped = webgpu_sys::GpuTexelCopyBufferInfo::new(&buffer.inner);\n    if let Some(bytes_per_row) = view.layout.bytes_per_row {\n        mapped.set_bytes_per_row(bytes_per_row);\n    }\n    if let Some(rows_per_image) = view.layout.rows_per_image {\n        mapped.set_rows_per_image(rows_per_image);\n    }\n    mapped.set_offset(view.layout.offset as f64);\n    mapped\n}\n\nfn map_texture_copy_view(\n    view: crate::TexelCopyTextureInfo<'_>,\n) -> webgpu_sys::GpuTexelCopyTextureInfo {\n    let texture = view.texture.inner.as_webgpu();\n    let mapped = webgpu_sys::GpuTexelCopyTextureInfo::new(&texture.inner);\n    mapped.set_mip_level(view.mip_level);\n    mapped.set_origin(&map_origin_3d(view.origin));\n    mapped.set_aspect(map_texture_aspect(view.aspect));\n    mapped\n}\n\nfn map_tagged_texture_copy_view(\n    view: crate::CopyExternalImageDestInfo<&crate::api::Texture>,\n) -> webgpu_sys::GpuCopyExternalImageDestInfo {\n    let texture = view.texture.inner.as_webgpu();\n    let mapped = webgpu_sys::GpuCopyExternalImageDestInfo::new(&texture.inner);\n    mapped.set_mip_level(view.mip_level);\n    mapped.set_origin(&map_origin_3d(view.origin));\n    mapped.set_aspect(map_texture_aspect(view.aspect));\n    // mapped.set_color_space(map_color_space(view.color_space));\n    mapped.set_premultiplied_alpha(view.premultiplied_alpha);\n    mapped\n}\n\nfn map_external_texture_copy_view(\n    view: &crate::CopyExternalImageSourceInfo,\n) -> webgpu_sys::GpuCopyExternalImageSourceInfo {\n    let mapped = webgpu_sys::GpuCopyExternalImageSourceInfo::new(&view.source);\n    mapped.set_origin(&map_origin_2d(view.origin));\n    mapped.set_flip_y(view.flip_y);\n    mapped\n}\n\nfn map_texture_aspect(aspect: wgt::TextureAspect) -> webgpu_sys::GpuTextureAspect {\n    match aspect {\n        wgt::TextureAspect::All => webgpu_sys::GpuTextureAspect::All,\n        wgt::TextureAspect::StencilOnly => webgpu_sys::GpuTextureAspect::StencilOnly,\n        wgt::TextureAspect::DepthOnly => webgpu_sys::GpuTextureAspect::DepthOnly,\n        wgt::TextureAspect::Plane0 | wgt::TextureAspect::Plane1 | wgt::TextureAspect::Plane2 => {\n            panic!(\"multi-plane textures are not supported\")\n        }\n    }\n}\n\nfn map_filter_mode(mode: wgt::FilterMode) -> webgpu_sys::GpuFilterMode {\n    match mode {\n        wgt::FilterMode::Nearest => webgpu_sys::GpuFilterMode::Nearest,\n        wgt::FilterMode::Linear => webgpu_sys::GpuFilterMode::Linear,\n    }\n}\n\nfn map_mipmap_filter_mode(mode: wgt::MipmapFilterMode) -> webgpu_sys::GpuMipmapFilterMode {\n    match mode {\n        wgt::MipmapFilterMode::Nearest => webgpu_sys::GpuMipmapFilterMode::Nearest,\n        wgt::MipmapFilterMode::Linear => webgpu_sys::GpuMipmapFilterMode::Linear,\n    }\n}\n\nfn map_address_mode(mode: wgt::AddressMode) -> webgpu_sys::GpuAddressMode {\n    match mode {\n        wgt::AddressMode::ClampToEdge => webgpu_sys::GpuAddressMode::ClampToEdge,\n        wgt::AddressMode::Repeat => webgpu_sys::GpuAddressMode::Repeat,\n        wgt::AddressMode::MirrorRepeat => webgpu_sys::GpuAddressMode::MirrorRepeat,\n        wgt::AddressMode::ClampToBorder => panic!(\"Clamp to border is not supported\"),\n    }\n}\n\nfn map_color(color: wgt::Color) -> webgpu_sys::GpuColorDict {\n    webgpu_sys::GpuColorDict::new(color.a, color.b, color.g, color.r)\n}\n\nfn map_store_op(store: crate::StoreOp) -> webgpu_sys::GpuStoreOp {\n    match store {\n        crate::StoreOp::Store => webgpu_sys::GpuStoreOp::Store,\n        crate::StoreOp::Discard => webgpu_sys::GpuStoreOp::Discard,\n    }\n}\n\nfn map_map_mode(mode: crate::MapMode) -> u32 {\n    match mode {\n        crate::MapMode::Read => webgpu_sys::gpu_map_mode::READ,\n        crate::MapMode::Write => webgpu_sys::gpu_map_mode::WRITE,\n    }\n}\n\nconst FEATURES_MAPPING: [(wgt::Features, webgpu_sys::GpuFeatureName); 16] = [\n    (\n        wgt::Features::DEPTH_CLIP_CONTROL,\n        webgpu_sys::GpuFeatureName::DepthClipControl,\n    ),\n    (\n        wgt::Features::DEPTH32FLOAT_STENCIL8,\n        webgpu_sys::GpuFeatureName::Depth32floatStencil8,\n    ),\n    (\n        wgt::Features::TEXTURE_COMPRESSION_BC,\n        webgpu_sys::GpuFeatureName::TextureCompressionBc,\n    ),\n    (\n        wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,\n        webgpu_sys::GpuFeatureName::TextureCompressionBcSliced3d,\n    ),\n    (\n        wgt::Features::TEXTURE_COMPRESSION_ETC2,\n        webgpu_sys::GpuFeatureName::TextureCompressionEtc2,\n    ),\n    (\n        wgt::Features::TEXTURE_COMPRESSION_ASTC,\n        webgpu_sys::GpuFeatureName::TextureCompressionAstc,\n    ),\n    (\n        wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,\n        webgpu_sys::GpuFeatureName::TextureCompressionAstcSliced3d,\n    ),\n    (\n        wgt::Features::TIMESTAMP_QUERY,\n        webgpu_sys::GpuFeatureName::TimestampQuery,\n    ),\n    (\n        wgt::Features::INDIRECT_FIRST_INSTANCE,\n        webgpu_sys::GpuFeatureName::IndirectFirstInstance,\n    ),\n    (\n        wgt::Features::SHADER_F16,\n        webgpu_sys::GpuFeatureName::ShaderF16,\n    ),\n    (\n        wgt::Features::RG11B10UFLOAT_RENDERABLE,\n        webgpu_sys::GpuFeatureName::Rg11b10ufloatRenderable,\n    ),\n    (\n        wgt::Features::BGRA8UNORM_STORAGE,\n        webgpu_sys::GpuFeatureName::Bgra8unormStorage,\n    ),\n    (\n        wgt::Features::FLOAT32_FILTERABLE,\n        webgpu_sys::GpuFeatureName::Float32Filterable,\n    ),\n    (\n        wgt::Features::FLOAT32_BLENDABLE,\n        webgpu_sys::GpuFeatureName::Float32Blendable,\n    ),\n    (\n        wgt::Features::DUAL_SOURCE_BLENDING,\n        webgpu_sys::GpuFeatureName::DualSourceBlending,\n    ),\n    (\n        wgt::Features::CLIP_DISTANCES,\n        webgpu_sys::GpuFeatureName::ClipDistances,\n    ),\n];\n\nfn map_wgt_features(supported_features: webgpu_sys::GpuSupportedFeatures) -> wgt::Features {\n    let mut features = wgt::Features::empty();\n    for (wgpu_feat, web_feat) in FEATURES_MAPPING {\n        match wasm_bindgen::JsValue::from(web_feat).as_string() {\n            Some(value) if supported_features.has(&value) => features |= wgpu_feat,\n            _ => {}\n        }\n    }\n    features\n}\n\nfn map_wgt_limits(limits: webgpu_sys::GpuSupportedLimits) -> wgt::Limits {\n    wgt::Limits {\n        max_texture_dimension_1d: limits.max_texture_dimension_1d(),\n        max_texture_dimension_2d: limits.max_texture_dimension_2d(),\n        max_texture_dimension_3d: limits.max_texture_dimension_3d(),\n        max_texture_array_layers: limits.max_texture_array_layers(),\n        max_bind_groups: limits.max_bind_groups(),\n        max_bindings_per_bind_group: limits.max_bindings_per_bind_group(),\n        max_dynamic_uniform_buffers_per_pipeline_layout: limits\n            .max_dynamic_uniform_buffers_per_pipeline_layout(),\n        max_dynamic_storage_buffers_per_pipeline_layout: limits\n            .max_dynamic_storage_buffers_per_pipeline_layout(),\n        max_sampled_textures_per_shader_stage: limits.max_sampled_textures_per_shader_stage(),\n        max_samplers_per_shader_stage: limits.max_samplers_per_shader_stage(),\n        max_storage_buffers_per_shader_stage: limits.max_storage_buffers_per_shader_stage(),\n        max_storage_textures_per_shader_stage: limits.max_storage_textures_per_shader_stage(),\n        max_uniform_buffers_per_shader_stage: limits.max_uniform_buffers_per_shader_stage(),\n        max_binding_array_elements_per_shader_stage: 0,\n        max_binding_array_sampler_elements_per_shader_stage: 0,\n        max_binding_array_acceleration_structure_elements_per_shader_stage: 0,\n        max_uniform_buffer_binding_size: limits.max_uniform_buffer_binding_size() as u64,\n        max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size() as u64,\n        max_vertex_buffers: limits.max_vertex_buffers(),\n        max_buffer_size: limits.max_buffer_size() as u64,\n        max_vertex_attributes: limits.max_vertex_attributes(),\n        max_vertex_buffer_array_stride: limits.max_vertex_buffer_array_stride(),\n        max_inter_stage_shader_variables: limits.max_inter_stage_shader_variables(),\n        min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment(),\n        min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment(),\n        max_color_attachments: limits.max_color_attachments(),\n        max_color_attachment_bytes_per_sample: limits.max_color_attachment_bytes_per_sample(),\n        max_compute_workgroup_storage_size: limits.max_compute_workgroup_storage_size(),\n        max_compute_invocations_per_workgroup: limits.max_compute_invocations_per_workgroup(),\n        max_compute_workgroup_size_x: limits.max_compute_workgroup_size_x(),\n        max_compute_workgroup_size_y: limits.max_compute_workgroup_size_y(),\n        max_compute_workgroup_size_z: limits.max_compute_workgroup_size_z(),\n        max_compute_workgroups_per_dimension: limits.max_compute_workgroups_per_dimension(),\n        max_immediate_size: wgt::Limits::default().max_immediate_size,\n        max_non_sampler_bindings: wgt::Limits::default().max_non_sampler_bindings,\n\n        max_task_mesh_workgroup_total_count: wgt::Limits::default()\n            .max_task_mesh_workgroup_total_count,\n        max_task_mesh_workgroups_per_dimension: wgt::Limits::default()\n            .max_task_mesh_workgroups_per_dimension,\n        max_task_invocations_per_workgroup: wgt::Limits::default()\n            .max_task_invocations_per_workgroup,\n        max_task_invocations_per_dimension: wgt::Limits::default()\n            .max_task_invocations_per_dimension,\n        max_mesh_invocations_per_workgroup: wgt::Limits::default()\n            .max_mesh_invocations_per_workgroup,\n        max_mesh_invocations_per_dimension: wgt::Limits::default()\n            .max_mesh_invocations_per_dimension,\n        max_task_payload_size: wgt::Limits::default().max_task_payload_size,\n        max_mesh_output_vertices: wgt::Limits::default().max_mesh_output_vertices,\n        max_mesh_output_primitives: wgt::Limits::default().max_mesh_output_primitives,\n        max_mesh_output_layers: wgt::Limits::default().max_mesh_output_layers,\n        max_mesh_multiview_view_count: wgt::Limits::default().max_mesh_multiview_view_count,\n\n        max_blas_primitive_count: wgt::Limits::default().max_blas_primitive_count,\n        max_blas_geometry_count: wgt::Limits::default().max_blas_geometry_count,\n        max_tlas_instance_count: wgt::Limits::default().max_tlas_instance_count,\n        max_acceleration_structures_per_shader_stage: wgt::Limits::default()\n            .max_acceleration_structures_per_shader_stage,\n\n        max_multiview_view_count: wgt::Limits::default().max_multiview_view_count,\n    }\n}\n\nfn map_adapter_info(adapter_info: &webgpu_sys::GpuAdapterInfo) -> wgt::AdapterInfo {\n    // TODO(https://github.com/gfx-rs/wgpu/issues/8819): populate more fields if/when possible\n    wgt::AdapterInfo {\n        name: adapter_info.description().to_string(),\n        vendor: 0,\n        device: 0,\n        device_type: wgt::DeviceType::Other,\n        device_pci_bus_id: String::new(),\n        driver: String::new(),\n        driver_info: String::new(),\n        backend: wgt::Backend::BrowserWebGpu,\n        subgroup_min_size: wgt::MINIMUM_SUBGROUP_MIN_SIZE,\n        subgroup_max_size: wgt::MAXIMUM_SUBGROUP_MAX_SIZE,\n        transient_saves_memory: false,\n    }\n}\n\nfn map_js_sys_limits(limits: &wgt::Limits) -> js_sys::Object {\n    let object = js_sys::Object::new();\n\n    macro_rules! set_properties {\n        (($from:expr) => ($on:expr) : $(($js_ident:ident, $rs_ident:ident)),* $(,)?) => {\n            $(\n                ::js_sys::Reflect::set(\n                    &$on,\n                    &::wasm_bindgen::JsValue::from(stringify!($js_ident)),\n                    // Numbers may be u64, however using `from` on a u64 yields\n                    // errors on the wasm side, since it uses an unsupported api.\n                    // Wasm sends us things that need to fit into u64s by sending\n                    // us f64s instead. So we just send them f64s back.\n                    &::wasm_bindgen::JsValue::from($from.$rs_ident as f64)\n                )\n                    .expect(\"Setting Object properties should never fail.\");\n            )*\n        }\n    }\n\n    // https://gpuweb.github.io/gpuweb/#gpusupportedlimits\n    set_properties![\n        (limits) => (object):\n        (maxTextureDimension1D, max_texture_dimension_1d),\n        (maxTextureDimension2D, max_texture_dimension_2d),\n        (maxTextureDimension3D, max_texture_dimension_3d),\n        (maxTextureArrayLayers, max_texture_array_layers),\n        (maxBindGroups, max_bind_groups),\n        // TODO: (maxBindGroupsPlusVertexBuffers, max_bind_groups_plus_vertex_buffers),\n        (maxBindingsPerBindGroup, max_bindings_per_bind_group),\n        (maxDynamicUniformBuffersPerPipelineLayout, max_dynamic_uniform_buffers_per_pipeline_layout),\n        (maxDynamicStorageBuffersPerPipelineLayout, max_dynamic_storage_buffers_per_pipeline_layout),\n        (maxSampledTexturesPerShaderStage, max_sampled_textures_per_shader_stage),\n        (maxSamplersPerShaderStage, max_samplers_per_shader_stage),\n        (maxStorageBuffersPerShaderStage, max_storage_buffers_per_shader_stage),\n        (maxStorageTexturesPerShaderStage, max_storage_textures_per_shader_stage),\n        (maxUniformBuffersPerShaderStage, max_uniform_buffers_per_shader_stage),\n        (maxUniformBufferBindingSize, max_uniform_buffer_binding_size),\n        (maxStorageBufferBindingSize, max_storage_buffer_binding_size),\n        (minUniformBufferOffsetAlignment, min_uniform_buffer_offset_alignment),\n        (minStorageBufferOffsetAlignment, min_storage_buffer_offset_alignment),\n        (maxVertexBuffers, max_vertex_buffers),\n        (maxBufferSize, max_buffer_size),\n        (maxVertexAttributes, max_vertex_attributes),\n        (maxVertexBufferArrayStride, max_vertex_buffer_array_stride),\n        (maxInterStageShaderVariables, max_inter_stage_shader_variables),\n        (maxColorAttachments, max_color_attachments),\n        (maxColorAttachmentBytesPerSample, max_color_attachment_bytes_per_sample),\n        (maxComputeWorkgroupStorageSize, max_compute_workgroup_storage_size),\n        (maxComputeInvocationsPerWorkgroup, max_compute_invocations_per_workgroup),\n        (maxComputeWorkgroupSizeX, max_compute_workgroup_size_x),\n        (maxComputeWorkgroupSizeY, max_compute_workgroup_size_y),\n        (maxComputeWorkgroupSizeZ, max_compute_workgroup_size_z),\n        (maxComputeWorkgroupsPerDimension, max_compute_workgroups_per_dimension),\n    ];\n\n    object\n}\n\ntype JsFutureResult = Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>;\n\nfn future_request_adapter(\n    result: JsFutureResult,\n    requested_backends: Backends,\n) -> Result<dispatch::DispatchAdapter, wgt::RequestAdapterError> {\n    let web_adapter: Option<webgpu_sys::GpuAdapter> =\n        result.and_then(wasm_bindgen::JsCast::dyn_into).ok();\n    web_adapter\n        .map(|adapter| {\n            WebAdapter {\n                inner: adapter,\n                ident: crate::cmp::Identifier::create(),\n            }\n            .into()\n        })\n        .ok_or_else(|| request_adapter_null_error(requested_backends))\n}\n\n// Translate WebGPU’s null return into our error.\nfn request_adapter_null_error(requested_backends: Backends) -> wgt::RequestAdapterError {\n    wgt::RequestAdapterError::NotFound {\n        active_backends: Backends::BROWSER_WEBGPU,\n        requested_backends,\n        // TODO: supported_backends should also include wgpu-core-based backends,\n        // if they were compiled in.\n        supported_backends: Backends::BROWSER_WEBGPU,\n        no_fallback_backends: Backends::empty(),\n        no_adapter_backends: Backends::BROWSER_WEBGPU,\n        incompatible_surface_backends: Backends::empty(),\n    }\n}\n\nfn future_request_device(\n    result: JsFutureResult,\n) -> Result<(dispatch::DispatchDevice, dispatch::DispatchQueue), crate::RequestDeviceError> {\n    result\n        .map(|js_value| {\n            let device = webgpu_sys::GpuDevice::from(js_value);\n            let queue = device.queue();\n\n            (\n                WebDevice {\n                    inner: device,\n                    ident: crate::cmp::Identifier::create(),\n                    error_scope_count: Rc::new(Cell::new(0)),\n                }\n                .into(),\n                WebQueue {\n                    inner: queue,\n                    ident: crate::cmp::Identifier::create(),\n                }\n                .into(),\n            )\n        })\n        .map_err(|error_value| crate::RequestDeviceError {\n            // wasm-bindgen provides a reasonable error stringification via `Debug` impl\n            inner: crate::RequestDeviceErrorKind::WebGpu(format!(\"{error_value:?}\")),\n        })\n}\n\nfn future_pop_error_scope(result: JsFutureResult) -> Option<crate::Error> {\n    match result {\n        Ok(js_value) if js_value.is_object() => {\n            let js_error = wasm_bindgen::JsCast::dyn_into(js_value).unwrap();\n            Some(crate::Error::from_js(js_error))\n        }\n        _ => None,\n    }\n}\n\nfn future_compilation_info(\n    result: JsFutureResult,\n    base_compilation_info: &WebShaderCompilationInfo,\n) -> crate::CompilationInfo {\n    let base_messages = match base_compilation_info {\n        WebShaderCompilationInfo::Transformed { compilation_info } => {\n            compilation_info.messages.iter().cloned()\n        }\n        _ => [].iter().cloned(),\n    };\n\n    let messages = match result {\n        Ok(js_value) => {\n            let info = webgpu_sys::GpuCompilationInfo::from(js_value);\n            base_messages\n                .chain(info.messages().into_iter().map(|message| {\n                    crate::CompilationMessage::from_js(\n                        webgpu_sys::GpuCompilationMessage::from(message),\n                        base_compilation_info,\n                    )\n                }))\n                .collect()\n        }\n        Err(_v) => base_messages\n            .chain(core::iter::once(crate::CompilationMessage {\n                message: \"Getting compilation info failed\".to_string(),\n                message_type: crate::CompilationMessageType::Error,\n                location: None,\n            }))\n            .collect(),\n    };\n\n    crate::CompilationInfo { messages }\n}\n\n/// Calls `callback(success_value)` when the promise completes successfully, calls `callback(failure_value)`\n/// when the promise completes unsuccessfully.\nfn register_then_closures<F, T>(promise: &Promise, callback: F, success_value: T, failure_value: T)\nwhere\n    F: FnOnce(T) + 'static,\n    T: 'static,\n{\n    // Both the 'success' and 'rejected' closures need access to callback, but only one\n    // of them will ever run. We have them both hold a reference to a `Rc<RefCell<Option<impl FnOnce...>>>`,\n    // and then take ownership of callback when invoked.\n    //\n    // We also only need Rc's because these will only ever be called on our thread.\n    //\n    // We also store the actual closure types inside this Rc, as the closures need to be kept alive\n    // until they are actually called by the callback. It is valid to drop a closure inside of a callback.\n    // This allows us to keep the closures alive without leaking them.\n    let rc_callback: Rc<RefCell<Option<(_, _, F)>>> = Rc::new(RefCell::new(None));\n\n    let rc_callback_clone1 = rc_callback.clone();\n    let rc_callback_clone2 = rc_callback.clone();\n    let closure_success = wasm_bindgen::closure::Closure::once(move |_| {\n        let (success_closure, rejection_closure, callback) =\n            rc_callback_clone1.borrow_mut().take().unwrap();\n        callback(success_value);\n        // drop the closures, including ourselves, which will free any captured memory.\n        drop((success_closure, rejection_closure));\n    });\n    let closure_rejected = wasm_bindgen::closure::Closure::once(move |_| {\n        let (success_closure, rejection_closure, callback) =\n            rc_callback_clone2.borrow_mut().take().unwrap();\n        callback(failure_value);\n        // drop the closures, including ourselves, which will free any captured memory.\n        drop((success_closure, rejection_closure));\n    });\n\n    // Calling then before setting the value in the Rc seems like a race, but it isn't\n    // because the promise callback will run on this thread, so there is no race.\n    let _ = promise.then2(&closure_success, &closure_rejected);\n\n    *rc_callback.borrow_mut() = Some((closure_success, closure_rejected, callback));\n}\n\nimpl ContextWebGpu {\n    /// Common portion of the internal branches of the public `instance_create_surface` function.\n    ///\n    /// Note: Analogous code also exists in the WebGL2 backend at\n    /// `wgpu_hal::gles::web::Instance`.\n    fn create_surface_from_context(\n        &self,\n        canvas: Canvas,\n        context_result: Result<Option<js_sys::Object>, wasm_bindgen::JsValue>,\n    ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {\n        let context: js_sys::Object = match context_result {\n            Ok(Some(context)) => context,\n            Ok(None) => {\n                // <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext-dev>\n                // A getContext() call “returns null if contextId is not supported, or if the\n                // canvas has already been initialized with another context type”. Additionally,\n                // “not supported” could include “insufficient GPU resources” or “the GPU process\n                // previously crashed”. So, we must return it as an `Err` since it could occur\n                // for circumstances outside the application author's control.\n                return Err(crate::CreateSurfaceError {\n                    inner: crate::CreateSurfaceErrorKind::Web(\n                        String::from(\n                            \"canvas.getContext() returned null; webgpu not available or canvas already in use\"\n                        )\n                    )\n                });\n            }\n            Err(js_error) => {\n                // <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext>\n                // A thrown exception indicates misuse of the canvas state.\n                return Err(crate::CreateSurfaceError {\n                    inner: crate::CreateSurfaceErrorKind::Web(format!(\n                        \"canvas.getContext() threw exception {js_error:?}\",\n                    )),\n                });\n            }\n        };\n\n        // Not returning this error because it is a type error that shouldn't happen unless\n        // the browser, JS builtin objects, or wasm bindings are misbehaving somehow.\n        let context: webgpu_sys::GpuCanvasContext = context\n            .dyn_into()\n            .expect(\"canvas context is not a GPUCanvasContext\");\n\n        Ok(WebSurface {\n            gpu: self.gpu.clone(),\n            context,\n            canvas,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into())\n    }\n}\n\n// Represents the global object in the JavaScript context.\n// It can be cast to from `webgpu_sys::global` and exposes two getters `window` and `worker` of which only one is defined depending on the caller's context.\n// When called from the UI thread only `window` is defined whereas `worker` is only defined within a web worker context.\n// See: https://github.com/rustwasm/gloo/blob/2c9e776701ecb90c53e62dec1abd19c2b70e47c7/crates/timers/src/callback.rs#L8-L40\n#[wasm_bindgen]\nextern \"C\" {\n    type Global;\n\n    #[wasm_bindgen(method, getter, js_name = Window)]\n    fn window(this: &Global) -> JsValue;\n\n    #[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)]\n    fn worker(this: &Global) -> JsValue;\n}\n\n#[derive(Debug, Clone)]\npub enum Canvas {\n    Canvas(web_sys::HtmlCanvasElement),\n    Offscreen(web_sys::OffscreenCanvas),\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct BrowserGpuPropertyInaccessible;\n\n/// Returns the browser's gpu object or `Err(BrowserGpuPropertyInaccessible)` if\n/// the current context is neither the main thread nor a dedicated worker.\n///\n/// If WebGPU is not supported, the Gpu property may (!) be `undefined`,\n/// and so this function will return `Ok(None)`.\n/// Note that this check is insufficient to determine whether WebGPU is\n/// supported, as the browser may define the Gpu property, but be unable to\n/// create any WebGPU adapters.\n/// To detect whether WebGPU is supported, use the [`crate::utils::is_browser_webgpu_supported`] function.\n///\n/// See:\n/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>\n/// * <https://developer.mozilla.org/en-US/docs/Web/API/WorkerNavigator/gpu>\npub fn get_browser_gpu_property(\n) -> Result<Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>, BrowserGpuPropertyInaccessible> {\n    let global: Global = js_sys::global().unchecked_into();\n\n    let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() {\n        let navigator = global.unchecked_into::<web_sys::Window>().navigator();\n        ext_bindings::NavigatorGpu::gpu(&navigator)\n    } else if !global.worker().is_undefined() {\n        let navigator = global\n            .unchecked_into::<web_sys::WorkerGlobalScope>()\n            .navigator();\n        ext_bindings::NavigatorGpu::gpu(&navigator)\n    } else {\n        return Err(BrowserGpuPropertyInaccessible);\n    };\n    Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu))\n}\n\n#[derive(Debug, Clone)]\npub struct WebAdapter {\n    pub(crate) inner: webgpu_sys::GpuAdapter,\n    /// Unique identifier for this Adapter.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebDevice {\n    pub(crate) inner: webgpu_sys::GpuDevice,\n    /// Unique identifier for this Device.\n    ident: crate::cmp::Identifier,\n    /// Current number of error scopes that have been pushed on the device.\n    error_scope_count: Rc<Cell<u32>>,\n}\n\n#[derive(Debug, Clone)]\npub struct WebQueue {\n    pub(crate) inner: webgpu_sys::GpuQueue,\n    /// Unique identifier for this Queue.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebBindGroupLayout {\n    pub(crate) inner: webgpu_sys::GpuBindGroupLayout,\n    /// Unique identifier for this BindGroupLayout.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebBindGroup {\n    pub(crate) inner: webgpu_sys::GpuBindGroup,\n    /// Unique identifier for this BindGroup.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebTextureView {\n    pub(crate) inner: webgpu_sys::GpuTextureView,\n    /// Unique identifier for this TextureView.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebSampler {\n    pub(crate) inner: webgpu_sys::GpuSampler,\n    /// Unique identifier for this Sampler.\n    ident: crate::cmp::Identifier,\n}\n\n/// Remembers which portion of a buffer has been mapped, along with a reference\n/// to the mapped portion.\n#[derive(Debug, Clone)]\nstruct WebBufferMapState {\n    /// The mapped memory of the buffer.\n    pub mapped_buffer: Option<js_sys::ArrayBuffer>,\n    /// The total range which has been mapped in the buffer overall.\n    pub range: Range<wgt::BufferAddress>,\n}\n\n/// Stores the state of a GPU buffer and a reference to its mapped `ArrayBuffer` (if any).\n/// The WebGPU specification forbids calling `getMappedRange` on a `webgpu_sys::GpuBuffer` more than\n/// once, so this struct stores the initial mapped range and re-uses it, allowing for multiple `get_mapped_range`\n/// calls on the Rust-side.\n#[derive(Debug, Clone)]\npub struct WebBuffer {\n    /// The associated GPU buffer.\n    inner: webgpu_sys::GpuBuffer,\n    /// The mapped array buffer and mapped range.\n    mapping: Rc<RefCell<WebBufferMapState>>,\n    /// Unique identifier for this Buffer.\n    ident: crate::cmp::Identifier,\n}\n\nimpl WebBuffer {\n    /// Creates a new web buffer for the given Javascript object and description.\n    fn new(inner: webgpu_sys::GpuBuffer, desc: &crate::BufferDescriptor<'_>) -> Self {\n        Self {\n            inner,\n            mapping: Rc::new(RefCell::new(WebBufferMapState {\n                mapped_buffer: None,\n                range: 0..desc.size,\n            })),\n            ident: crate::cmp::Identifier::create(),\n        }\n    }\n\n    /// Obtains a reference to the re-usable buffer mapping as a Javascript array view.\n    fn get_mapped_range(&self, sub_range: Range<wgt::BufferAddress>) -> js_sys::Uint8Array {\n        let mut mapping = self.mapping.borrow_mut();\n        let range = mapping.range.clone();\n        let array_buffer = mapping.mapped_buffer.get_or_insert_with(|| {\n            self.inner\n                .get_mapped_range_with_f64_and_f64(\n                    range.start as f64,\n                    (range.end - range.start) as f64,\n                )\n                .unwrap()\n        });\n        js_sys::Uint8Array::new_with_byte_offset_and_length(\n            array_buffer,\n            (sub_range.start - range.start) as u32,\n            (sub_range.end - sub_range.start) as u32,\n        )\n    }\n\n    /// Sets the range of the buffer which is presently mapped.\n    fn set_mapped_range(&self, range: Range<wgt::BufferAddress>) {\n        self.mapping.borrow_mut().range = range;\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct WebTexture {\n    pub(crate) inner: webgpu_sys::GpuTexture,\n    /// Unique identifier for this Texture.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebExternalTexture {\n    /// Unique identifier for this ExternalTexture.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebBlas {\n    /// Unique identifier for this Blas.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebTlas {\n    /// Unique identifier for this Blas.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebQuerySet {\n    pub(crate) inner: webgpu_sys::GpuQuerySet,\n    /// Unique identifier for this QuerySet.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebPipelineLayout {\n    pub(crate) inner: webgpu_sys::GpuPipelineLayout,\n    /// Unique identifier for this PipelineLayout.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebRenderPipeline {\n    pub(crate) inner: webgpu_sys::GpuRenderPipeline,\n    /// Unique identifier for this RenderPipeline.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebComputePipeline {\n    pub(crate) inner: webgpu_sys::GpuComputePipeline,\n    /// Unique identifier for this ComputePipeline.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebPipelineCache {\n    /// Unique identifier for this PipelineCache.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebCommandEncoder {\n    pub(crate) inner: webgpu_sys::GpuCommandEncoder,\n    /// Unique identifier for this CommandEncoder.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct WebComputePassEncoder {\n    pub(crate) inner: webgpu_sys::GpuComputePassEncoder,\n    /// Unique identifier for this ComputePassEncoder.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct WebRenderPassEncoder {\n    pub(crate) inner: webgpu_sys::GpuRenderPassEncoder,\n    /// Unique identifier for this RenderPassEncoder.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct WebCommandBuffer {\n    pub(crate) inner: webgpu_sys::GpuCommandBuffer,\n    /// Unique identifier for this CommandBuffer.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebRenderBundleEncoder {\n    pub(crate) inner: webgpu_sys::GpuRenderBundleEncoder,\n    /// Unique identifier for this RenderBundleEncoder.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebRenderBundle {\n    pub(crate) inner: webgpu_sys::GpuRenderBundle,\n    /// Unique identifier for this RenderBundle.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebSurface {\n    gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,\n    canvas: Canvas,\n    context: webgpu_sys::GpuCanvasContext,\n    /// Unique identifier for this Surface.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug, Clone)]\npub struct WebSurfaceOutputDetail {\n    /// Unique identifier for this SurfaceOutputDetail.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct WebQueueWriteBuffer {\n    inner: Box<[u8]>,\n    /// Unique identifier for this QueueWriteBuffer.\n    ident: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct WebBufferMappedRange {\n    actual_mapping: js_sys::Uint8Array,\n    /// Copy of actual_mapping that lives in the Rust/Wasm heap instead of JS. This\n    /// is done only when accessed for the first time to avoid unnecessary allocations.\n    temporary_mapping: OnceCell<Vec<u8>>,\n    /// Whether `temporary_mapping` has possibly been written to and needs to be written back to JS.\n    temporary_mapping_modified: bool,\n    /// Unique identifier for this BufferMappedRange.\n    ident: crate::cmp::Identifier,\n}\n\nimpl_send_sync!(ContextWebGpu);\nimpl_send_sync!(WebAdapter);\nimpl_send_sync!(WebDevice);\nimpl_send_sync!(WebQueue);\nimpl_send_sync!(WebShaderModule);\nimpl_send_sync!(WebBindGroupLayout);\nimpl_send_sync!(WebBindGroup);\nimpl_send_sync!(WebTextureView);\nimpl_send_sync!(WebSampler);\nimpl_send_sync!(WebBuffer);\nimpl_send_sync!(WebTexture);\nimpl_send_sync!(WebExternalTexture);\nimpl_send_sync!(WebBlas);\nimpl_send_sync!(WebTlas);\nimpl_send_sync!(WebQuerySet);\nimpl_send_sync!(WebPipelineLayout);\nimpl_send_sync!(WebRenderPipeline);\nimpl_send_sync!(WebComputePipeline);\nimpl_send_sync!(WebPipelineCache);\nimpl_send_sync!(WebCommandEncoder);\nimpl_send_sync!(WebComputePassEncoder);\nimpl_send_sync!(WebRenderPassEncoder);\nimpl_send_sync!(WebCommandBuffer);\nimpl_send_sync!(WebRenderBundleEncoder);\nimpl_send_sync!(WebRenderBundle);\nimpl_send_sync!(WebSurface);\nimpl_send_sync!(WebSurfaceOutputDetail);\nimpl_send_sync!(WebQueueWriteBuffer);\nimpl_send_sync!(WebBufferMappedRange);\n\ncrate::cmp::impl_eq_ord_hash_proxy!(ContextWebGpu => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebAdapter => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebDevice => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebQueue => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebShaderModule => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebBindGroupLayout => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebBindGroup => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebTextureView => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebSampler => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebBuffer => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebTexture => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebExternalTexture => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebBlas => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebTlas => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebQuerySet => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebPipelineLayout => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebRenderPipeline => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebComputePipeline => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebPipelineCache => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebCommandEncoder => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebComputePassEncoder => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebRenderPassEncoder => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebCommandBuffer => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundleEncoder => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundle => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebSurface => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebSurfaceOutputDetail => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebQueueWriteBuffer => .ident);\ncrate::cmp::impl_eq_ord_hash_proxy!(WebBufferMappedRange => .ident);\n\nimpl dispatch::InstanceInterface for ContextWebGpu {\n    fn new(desc: crate::InstanceDescriptor) -> Self\n    where\n        Self: Sized,\n    {\n        let Ok(gpu) = get_browser_gpu_property() else {\n            panic!(\n                \"Accessing the GPU is only supported on the main thread or from a dedicated worker\"\n            );\n        };\n\n        ContextWebGpu {\n            gpu,\n            requested_backends: desc.backends,\n            ident: crate::cmp::Identifier::create(),\n        }\n    }\n\n    unsafe fn create_surface(\n        &self,\n        target: crate::SurfaceTargetUnsafe,\n    ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {\n        match target {\n            SurfaceTargetUnsafe::RawHandle {\n                raw_display_handle: _,\n                raw_window_handle,\n            } => {\n                let canvas_element: web_sys::HtmlCanvasElement = match raw_window_handle {\n                    raw_window_handle::RawWindowHandle::Web(handle) => {\n                        let canvas_node: wasm_bindgen::JsValue = web_sys::window()\n                            .and_then(|win| win.document())\n                            .and_then(|doc| {\n                                doc.query_selector_all(&format!(\n                                    \"[data-raw-handle=\\\"{}\\\"]\",\n                                    handle.id\n                                ))\n                                .ok()\n                            })\n                            .and_then(|nodes| nodes.get(0))\n                            .expect(\"expected to find single canvas\")\n                            .into();\n                        canvas_node.into()\n                    }\n                    raw_window_handle::RawWindowHandle::WebCanvas(handle) => {\n                        let value: &JsValue = unsafe { handle.obj.cast().as_ref() };\n                        value.clone().unchecked_into()\n                    }\n                    raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {\n                        let value: &JsValue = unsafe { handle.obj.cast().as_ref() };\n                        let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();\n                        let context_result = canvas.get_context(\"webgpu\");\n\n                        return self.create_surface_from_context(\n                            Canvas::Offscreen(canvas),\n                            context_result,\n                        );\n                    }\n                    _ => panic!(\"expected valid handle for canvas\"),\n                };\n\n                let context_result = canvas_element.get_context(\"webgpu\");\n                self.create_surface_from_context(Canvas::Canvas(canvas_element), context_result)\n            }\n        }\n    }\n\n    fn request_adapter(\n        &self,\n        options: &crate::RequestAdapterOptions<'_, '_>,\n    ) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {\n        let requested_backends = self.requested_backends;\n\n        //TODO: support this check, return `None` if the flag is not set.\n        // It's not trivial, since we need the Future logic to have this check,\n        // and currently the Future here has no room for extra parameter `backends`.\n        if !(requested_backends.contains(wgt::Backends::BROWSER_WEBGPU)) {\n            return Box::pin(core::future::ready(Err(\n                wgt::RequestAdapterError::NotFound {\n                    active_backends: Backends::BROWSER_WEBGPU,\n                    requested_backends,\n                    // TODO: supported_backends should also include wgpu-core-based backends,\n                    // if they were compiled in.\n                    supported_backends: Backends::BROWSER_WEBGPU,\n                    no_fallback_backends: Backends::default(),\n                    no_adapter_backends: Backends::default(),\n                    incompatible_surface_backends: Backends::default(),\n                },\n            )));\n        }\n        let mapped_options = webgpu_sys::GpuRequestAdapterOptions::new();\n        let mapped_power_preference = match options.power_preference {\n            wgt::PowerPreference::None => None,\n            wgt::PowerPreference::LowPower => Some(webgpu_sys::GpuPowerPreference::LowPower),\n            wgt::PowerPreference::HighPerformance => {\n                Some(webgpu_sys::GpuPowerPreference::HighPerformance)\n            }\n        };\n        if let Some(mapped_pref) = mapped_power_preference {\n            mapped_options.set_power_preference(mapped_pref);\n        }\n\n        if let Some(gpu) = &self.gpu {\n            let adapter_promise = gpu.request_adapter_with_options(&mapped_options);\n            Box::pin(MakeSendFuture::new(\n                wasm_bindgen_futures::JsFuture::from(adapter_promise),\n                move |result| future_request_adapter(result, requested_backends),\n            ))\n        } else {\n            // Gpu is undefined; WebGPU is not supported in this browser.\n            // Treat this exactly like requestAdapter() returned null.\n            Box::pin(core::future::ready(Err(request_adapter_null_error(\n                requested_backends,\n            ))))\n        }\n    }\n    fn enumerate_adapters(\n        &self,\n        _backends: crate::Backends,\n    ) -> Pin<Box<dyn dispatch::EnumerateAdapterFuture>> {\n        let future = self.request_adapter(&crate::RequestAdapterOptions::default());\n        let enumerate_future = async move {\n            let adapter = future.await;\n            match adapter {\n                Ok(a) => vec![a],\n                Err(_) => vec![],\n            }\n        };\n        Box::pin(enumerate_future)\n    }\n\n    fn poll_all_devices(&self, _force_wait: bool) -> bool {\n        // Devices are automatically polled.\n        true\n    }\n\n    #[cfg(feature = \"wgsl\")]\n    fn wgsl_language_features(&self) -> crate::WgslLanguageFeatures {\n        let mut wgsl_language_features = crate::WgslLanguageFeatures::empty();\n        if let Some(gpu) = &self.gpu {\n            gpu.wgsl_language_features()\n                .keys()\n                .into_iter()\n                .map(|wlf| wlf.expect(\"`WgslLanguageFeatures` elements should be valid\"))\n                .map(|wlf| {\n                    wlf.as_string()\n                        .expect(\"`WgslLanguageFeatures` should be string set\")\n                })\n                .filter_map(|wlf| match wlf.as_str() {\n                    \"readonly_and_readwrite_storage_textures\" => {\n                        Some(crate::WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures)\n                    }\n                    \"packed_4x8_integer_dot_product\" => {\n                        Some(crate::WgslLanguageFeatures::Packed4x8IntegerDotProduct)\n                    }\n                    \"unrestricted_pointer_parameters\" => {\n                        Some(crate::WgslLanguageFeatures::UnrestrictedPointerParameters)\n                    }\n                    \"pointer_composite_access\" => {\n                        Some(crate::WgslLanguageFeatures::PointerCompositeAccess)\n                    }\n                    _ => None,\n                })\n                .for_each(|wlf| {\n                    wgsl_language_features |= wlf;\n                })\n        }\n        wgsl_language_features\n    }\n}\n\nimpl Drop for ContextWebGpu {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::AdapterInterface for WebAdapter {\n    fn request_device(\n        &self,\n        desc: &crate::DeviceDescriptor<'_>,\n    ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {\n        if !matches!(desc.trace, wgt::Trace::Off) {\n            log::warn!(\"The `trace` parameter is not supported on the WebGPU backend.\");\n        }\n\n        let mapped_desc = webgpu_sys::GpuDeviceDescriptor::new();\n\n        let required_limits = map_js_sys_limits(&desc.required_limits);\n        mapped_desc.set_required_limits(&required_limits);\n\n        let required_features = FEATURES_MAPPING\n            .iter()\n            .copied()\n            .flat_map(|(flag, value)| {\n                if desc.required_features.contains(flag) {\n                    Some(JsValue::from(value))\n                } else {\n                    None\n                }\n            })\n            .collect::<js_sys::Array>();\n        mapped_desc.set_required_features(&required_features);\n\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let device_promise = self.inner.request_device_with_descriptor(&mapped_desc);\n\n        Box::pin(MakeSendFuture::new(\n            wasm_bindgen_futures::JsFuture::from(device_promise),\n            future_request_device,\n        ))\n    }\n\n    fn is_surface_supported(&self, _surface: &dispatch::DispatchSurface) -> bool {\n        // All surfaces are inherently supported.\n        true\n    }\n\n    fn features(&self) -> crate::Features {\n        map_wgt_features(self.inner.features())\n    }\n\n    fn limits(&self) -> crate::Limits {\n        map_wgt_limits(self.inner.limits())\n    }\n\n    fn downlevel_capabilities(&self) -> crate::DownlevelCapabilities {\n        // WebGPU is assumed to be fully compliant\n        crate::DownlevelCapabilities::default()\n    }\n\n    fn get_info(&self) -> crate::AdapterInfo {\n        map_adapter_info(&self.inner.info())\n    }\n\n    fn get_texture_format_features(\n        &self,\n        format: crate::TextureFormat,\n    ) -> crate::TextureFormatFeatures {\n        format.guaranteed_format_features(dispatch::AdapterInterface::features(self))\n    }\n\n    fn get_presentation_timestamp(&self) -> crate::PresentationTimestamp {\n        crate::PresentationTimestamp::INVALID_TIMESTAMP\n    }\n\n    fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {\n        Vec::new()\n    }\n}\nimpl Drop for WebAdapter {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::DeviceInterface for WebDevice {\n    fn features(&self) -> crate::Features {\n        map_wgt_features(self.inner.features())\n    }\n\n    fn limits(&self) -> crate::Limits {\n        map_wgt_limits(self.inner.limits())\n    }\n\n    fn adapter_info(&self) -> crate::AdapterInfo {\n        map_adapter_info(&self.inner.adapter_info())\n    }\n\n    fn create_shader_module(\n        &self,\n        desc: crate::ShaderModuleDescriptor<'_>,\n        _shader_runtime_checks: crate::ShaderRuntimeChecks,\n    ) -> dispatch::DispatchShaderModule {\n        let shader_module_result = match desc.source {\n            #[cfg(feature = \"spirv\")]\n            crate::ShaderSource::SpirV(ref spv) => {\n                use naga::front;\n\n                let options = naga::front::spv::Options {\n                    adjust_coordinate_space: false,\n                    strict_capabilities: true,\n                    block_ctx_dump_prefix: None,\n                };\n                let spv_parser = front::spv::Frontend::new(spv.iter().cloned(), &options);\n                spv_parser\n                    .parse()\n                    .map_err(|inner| {\n                        crate::CompilationInfo::from(naga::error::ShaderError {\n                            source: String::new(),\n                            label: desc.label.map(|s| s.to_string()),\n                            inner: Box::new(inner),\n                        })\n                    })\n                    .and_then(|spv_module| {\n                        validate_transformed_shader_module(&spv_module, \"\", &desc).map(|v| {\n                            (\n                                v,\n                                WebShaderCompilationInfo::Transformed {\n                                    compilation_info: crate::CompilationInfo { messages: vec![] },\n                                },\n                            )\n                        })\n                    })\n            }\n            #[cfg(feature = \"glsl\")]\n            crate::ShaderSource::Glsl {\n                ref shader,\n                stage,\n                defines,\n            } => {\n                use naga::front;\n\n                // Parse the given shader code and store its representation.\n                let options = front::glsl::Options {\n                    stage,\n                    defines: defines\n                        .iter()\n                        .map(|&(key, value)| (String::from(key), String::from(value)))\n                        .collect(),\n                };\n                let mut parser = front::glsl::Frontend::default();\n                parser\n                    .parse(&options, shader)\n                    .map_err(|inner| {\n                        crate::CompilationInfo::from(naga::error::ShaderError {\n                            source: shader.to_string(),\n                            label: desc.label.map(|s| s.to_string()),\n                            inner: Box::new(inner),\n                        })\n                    })\n                    .and_then(|glsl_module| {\n                        validate_transformed_shader_module(&glsl_module, shader, &desc).map(|v| {\n                            (\n                                v,\n                                WebShaderCompilationInfo::Transformed {\n                                    compilation_info: crate::CompilationInfo { messages: vec![] },\n                                },\n                            )\n                        })\n                    })\n            }\n            #[cfg(feature = \"wgsl\")]\n            crate::ShaderSource::Wgsl(ref code) => {\n                let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);\n                Ok((\n                    shader_module,\n                    WebShaderCompilationInfo::Wgsl {\n                        source: code.to_string(),\n                    },\n                ))\n            }\n            #[cfg(feature = \"naga-ir\")]\n            crate::ShaderSource::Naga(ref module) => {\n                validate_transformed_shader_module(module, \"\", &desc).map(|v| {\n                    (\n                        v,\n                        WebShaderCompilationInfo::Transformed {\n                            compilation_info: crate::CompilationInfo { messages: vec![] },\n                        },\n                    )\n                })\n            }\n            crate::ShaderSource::Dummy(_) => {\n                panic!(\"found `ShaderSource::Dummy`\")\n            }\n        };\n\n        #[cfg(naga)]\n        fn validate_transformed_shader_module(\n            module: &naga::Module,\n            source: &str,\n            desc: &crate::ShaderModuleDescriptor<'_>,\n        ) -> Result<webgpu_sys::GpuShaderModuleDescriptor, crate::CompilationInfo> {\n            use naga::{back, valid};\n            let mut validator =\n                valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all());\n            let module_info = validator.validate(module).map_err(|err| {\n                crate::CompilationInfo::from(naga::error::ShaderError {\n                    source: source.to_string(),\n                    label: desc.label.map(|s| s.to_string()),\n                    inner: Box::new(err),\n                })\n            })?;\n\n            let writer_flags = naga::back::wgsl::WriterFlags::empty();\n            let wgsl_text = back::wgsl::write_string(module, &module_info, writer_flags).unwrap();\n            Ok(webgpu_sys::GpuShaderModuleDescriptor::new(\n                wgsl_text.as_str(),\n            ))\n        }\n        let (descriptor, compilation_info) = match shader_module_result {\n            Ok(v) => v,\n            Err(compilation_info) => (\n                webgpu_sys::GpuShaderModuleDescriptor::new(\"\"),\n                WebShaderCompilationInfo::Transformed { compilation_info },\n            ),\n        };\n        if let Some(label) = desc.label {\n            descriptor.set_label(label);\n        }\n        WebShaderModule {\n            module: self.inner.create_shader_module(&descriptor),\n            compilation_info,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    unsafe fn create_shader_module_passthrough(\n        &self,\n        desc: &crate::ShaderModuleDescriptorPassthrough<'_>,\n    ) -> dispatch::DispatchShaderModule {\n        let shader_module_result = if let Some(ref code) = desc.wgsl {\n            let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);\n            Ok((\n                shader_module,\n                WebShaderCompilationInfo::Wgsl {\n                    source: code.to_string(),\n                },\n            ))\n        } else {\n            Err(crate::CompilationInfo {\n                messages: vec![crate::CompilationMessage {\n                    message:\n                        \"Passthrough shader not compiled for WGSL on WebGPU backend (wgpu error)\"\n                            .to_string(),\n                    location: None,\n                    message_type: crate::CompilationMessageType::Error,\n                }],\n            })\n        };\n        let (descriptor, compilation_info) = match shader_module_result {\n            Ok(v) => v,\n            Err(compilation_info) => (\n                webgpu_sys::GpuShaderModuleDescriptor::new(\"\"),\n                WebShaderCompilationInfo::Transformed { compilation_info },\n            ),\n        };\n        if let Some(label) = desc.label {\n            descriptor.set_label(label);\n        }\n        WebShaderModule {\n            module: self.inner.create_shader_module(&descriptor),\n            compilation_info,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_bind_group_layout(\n        &self,\n        desc: &crate::BindGroupLayoutDescriptor<'_>,\n    ) -> dispatch::DispatchBindGroupLayout {\n        let mapped_bindings = desc\n            .entries\n            .iter()\n            .map(|bind| {\n                let mapped_entry =\n                    webgpu_sys::GpuBindGroupLayoutEntry::new(bind.binding, bind.visibility.bits());\n\n                match bind.ty {\n                    wgt::BindingType::Buffer {\n                        ty,\n                        has_dynamic_offset,\n                        min_binding_size,\n                    } => {\n                        let buffer = webgpu_sys::GpuBufferBindingLayout::new();\n                        buffer.set_has_dynamic_offset(has_dynamic_offset);\n                        if let Some(size) = min_binding_size {\n                            buffer.set_min_binding_size(size.get() as f64);\n                        }\n                        buffer.set_type(match ty {\n                            wgt::BufferBindingType::Uniform => {\n                                webgpu_sys::GpuBufferBindingType::Uniform\n                            }\n                            wgt::BufferBindingType::Storage { read_only: false } => {\n                                webgpu_sys::GpuBufferBindingType::Storage\n                            }\n                            wgt::BufferBindingType::Storage { read_only: true } => {\n                                webgpu_sys::GpuBufferBindingType::ReadOnlyStorage\n                            }\n                        });\n                        mapped_entry.set_buffer(&buffer);\n                    }\n                    wgt::BindingType::Sampler(ty) => {\n                        let sampler = webgpu_sys::GpuSamplerBindingLayout::new();\n                        sampler.set_type(match ty {\n                            wgt::SamplerBindingType::NonFiltering => {\n                                webgpu_sys::GpuSamplerBindingType::NonFiltering\n                            }\n                            wgt::SamplerBindingType::Filtering => {\n                                webgpu_sys::GpuSamplerBindingType::Filtering\n                            }\n                            wgt::SamplerBindingType::Comparison => {\n                                webgpu_sys::GpuSamplerBindingType::Comparison\n                            }\n                        });\n                        mapped_entry.set_sampler(&sampler);\n                    }\n                    wgt::BindingType::Texture {\n                        multisampled,\n                        sample_type,\n                        view_dimension,\n                    } => {\n                        let texture = webgpu_sys::GpuTextureBindingLayout::new();\n                        texture.set_multisampled(multisampled);\n                        texture.set_sample_type(map_texture_component_type(sample_type));\n                        texture.set_view_dimension(map_texture_view_dimension(view_dimension));\n                        mapped_entry.set_texture(&texture);\n                    }\n                    wgt::BindingType::StorageTexture {\n                        access,\n                        format,\n                        view_dimension,\n                    } => {\n                        let mapped_access = match access {\n                            wgt::StorageTextureAccess::WriteOnly => {\n                                webgpu_sys::GpuStorageTextureAccess::WriteOnly\n                            }\n                            wgt::StorageTextureAccess::ReadOnly => {\n                                webgpu_sys::GpuStorageTextureAccess::ReadOnly\n                            }\n                            wgt::StorageTextureAccess::ReadWrite => {\n                                webgpu_sys::GpuStorageTextureAccess::ReadWrite\n                            }\n                            wgt::StorageTextureAccess::Atomic => {\n                                // Validated out by `BindGroupLayoutEntryError::StorageTextureAtomic`\n                                unreachable!()\n                            }\n                        };\n                        let storage_texture = webgpu_sys::GpuStorageTextureBindingLayout::new(\n                            map_texture_format(format),\n                        );\n                        storage_texture.set_access(mapped_access);\n                        storage_texture\n                            .set_view_dimension(map_texture_view_dimension(view_dimension));\n                        mapped_entry.set_storage_texture(&storage_texture);\n                    }\n                    wgt::BindingType::AccelerationStructure { .. } => todo!(),\n                    wgt::BindingType::ExternalTexture => {\n                        mapped_entry.set_external_texture(\n                            &webgpu_sys::GpuExternalTextureBindingLayout::new(),\n                        );\n                    }\n                }\n\n                mapped_entry\n            })\n            .collect::<js_sys::Array>();\n\n        let mapped_desc = webgpu_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n        let bind_group_layout = self.inner.create_bind_group_layout(&mapped_desc).unwrap();\n\n        WebBindGroupLayout {\n            inner: bind_group_layout,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_bind_group(\n        &self,\n        desc: &crate::BindGroupDescriptor<'_>,\n    ) -> dispatch::DispatchBindGroup {\n        let mapped_entries = desc\n            .entries\n            .iter()\n            .map(|binding| {\n                let mapped_resource = match binding.resource {\n                    crate::BindingResource::Buffer(crate::BufferBinding {\n                        buffer,\n                        offset,\n                        size,\n                    }) => {\n                        let buffer = buffer.inner.as_webgpu();\n                        let mapped_buffer_binding =\n                            webgpu_sys::GpuBufferBinding::new(&buffer.inner);\n                        mapped_buffer_binding.set_offset(offset as f64);\n                        if let Some(s) = size {\n                            mapped_buffer_binding.set_size(s.get() as f64);\n                        }\n                        JsValue::from(mapped_buffer_binding)\n                    }\n                    crate::BindingResource::BufferArray(..) => {\n                        panic!(\"Web backend does not support arrays of buffers\")\n                    }\n                    crate::BindingResource::Sampler(sampler) => {\n                        let sampler = &sampler.inner.as_webgpu().inner;\n                        JsValue::from(sampler)\n                    }\n                    crate::BindingResource::SamplerArray(..) => {\n                        panic!(\"Web backend does not support arrays of samplers\")\n                    }\n                    crate::BindingResource::TextureView(texture_view) => {\n                        let texture_view = &texture_view.inner.as_webgpu().inner;\n                        JsValue::from(texture_view)\n                    }\n                    crate::BindingResource::TextureViewArray(..) => {\n                        panic!(\"Web backend does not support BINDING_INDEXING extension\")\n                    }\n                    crate::BindingResource::AccelerationStructure(_) => {\n                        unimplemented!(\"Raytracing not implemented for web\")\n                    }\n                    crate::BindingResource::AccelerationStructureArray(_) => {\n                        unimplemented!(\"Raytracing not implemented for web\")\n                    }\n                    crate::BindingResource::ExternalTexture(_) => {\n                        unimplemented!(\"ExternalTexture not implemented for web\")\n                    }\n                };\n\n                webgpu_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource)\n            })\n            .collect::<js_sys::Array>();\n\n        let bgl = &desc.layout.inner.as_webgpu().inner;\n        let mapped_desc = webgpu_sys::GpuBindGroupDescriptor::new(&mapped_entries, bgl);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n        let bind_group = self.inner.create_bind_group(&mapped_desc);\n\n        WebBindGroup {\n            inner: bind_group,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_pipeline_layout(\n        &self,\n        desc: &crate::PipelineLayoutDescriptor<'_>,\n    ) -> dispatch::DispatchPipelineLayout {\n        let null = wasm_bindgen::JsValue::NULL;\n        let temp_layouts = desc\n            .bind_group_layouts\n            .iter()\n            .map(|bgl| match bgl {\n                Some(bgl) => bgl.inner.as_webgpu().inner.as_ref(),\n                None => &null,\n            })\n            .collect::<js_sys::Array>();\n        let mapped_desc = webgpu_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let pipeline_layout = self.inner.create_pipeline_layout(&mapped_desc);\n\n        WebPipelineLayout {\n            inner: pipeline_layout,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_render_pipeline(\n        &self,\n        desc: &crate::RenderPipelineDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPipeline {\n        let module = desc.vertex.module.inner.as_webgpu();\n        let mapped_vertex_state = webgpu_sys::GpuVertexState::new(&module.module);\n        insert_constants_map(\n            &mapped_vertex_state,\n            desc.vertex.compilation_options.constants,\n        );\n        if let Some(ep) = desc.vertex.entry_point {\n            mapped_vertex_state.set_entry_point(ep);\n        }\n\n        let buffers = desc\n            .vertex\n            .buffers\n            .iter()\n            .map(|vbuf| {\n                let mapped_attributes = vbuf\n                    .attributes\n                    .iter()\n                    .map(|attr| {\n                        webgpu_sys::GpuVertexAttribute::new(\n                            map_vertex_format(attr.format),\n                            attr.offset as f64,\n                            attr.shader_location,\n                        )\n                    })\n                    .collect::<js_sys::Array>();\n\n                let mapped_vbuf = webgpu_sys::GpuVertexBufferLayout::new(\n                    vbuf.array_stride as f64,\n                    &mapped_attributes,\n                );\n                mapped_vbuf.set_step_mode(map_vertex_step_mode(vbuf.step_mode));\n                mapped_vbuf\n            })\n            .collect::<js_sys::Array>();\n\n        mapped_vertex_state.set_buffers(&buffers);\n\n        let auto_layout = wasm_bindgen::JsValue::from(webgpu_sys::GpuAutoLayoutMode::Auto);\n        let mapped_desc = webgpu_sys::GpuRenderPipelineDescriptor::new(\n            &match desc.layout {\n                Some(layout) => {\n                    let layout = &layout.inner.as_webgpu().inner;\n                    JsValue::from(layout)\n                }\n                None => auto_layout,\n            },\n            &mapped_vertex_state,\n        );\n\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        if let Some(ref depth_stencil) = desc.depth_stencil {\n            mapped_desc.set_depth_stencil(&map_depth_stencil_state(depth_stencil));\n        }\n\n        if let Some(ref frag) = desc.fragment {\n            let targets = frag\n                .targets\n                .iter()\n                .map(|target| match target {\n                    Some(target) => {\n                        let mapped_format = map_texture_format(target.format);\n                        let mapped_color_state =\n                            webgpu_sys::GpuColorTargetState::new(mapped_format);\n                        if let Some(ref bs) = target.blend {\n                            let alpha = map_blend_component(&bs.alpha);\n                            let color = map_blend_component(&bs.color);\n                            let mapped_blend_state = webgpu_sys::GpuBlendState::new(&alpha, &color);\n                            mapped_color_state.set_blend(&mapped_blend_state);\n                        }\n                        mapped_color_state.set_write_mask(target.write_mask.bits());\n                        wasm_bindgen::JsValue::from(mapped_color_state)\n                    }\n                    None => wasm_bindgen::JsValue::null(),\n                })\n                .collect::<js_sys::Array>();\n            let module = frag.module.inner.as_webgpu();\n            let mapped_fragment_desc = webgpu_sys::GpuFragmentState::new(&module.module, &targets);\n            insert_constants_map(&mapped_fragment_desc, frag.compilation_options.constants);\n            if let Some(ep) = frag.entry_point {\n                mapped_fragment_desc.set_entry_point(ep);\n            }\n            mapped_desc.set_fragment(&mapped_fragment_desc);\n        }\n\n        let mapped_multisample = webgpu_sys::GpuMultisampleState::new();\n        mapped_multisample.set_count(desc.multisample.count);\n        mapped_multisample.set_mask(desc.multisample.mask as u32);\n        mapped_multisample\n            .set_alpha_to_coverage_enabled(desc.multisample.alpha_to_coverage_enabled);\n        mapped_desc.set_multisample(&mapped_multisample);\n\n        let mapped_primitive = map_primitive_state(&desc.primitive);\n        mapped_desc.set_primitive(&mapped_primitive);\n\n        let render_pipeline = self.inner.create_render_pipeline(&mapped_desc).unwrap();\n\n        WebRenderPipeline {\n            inner: render_pipeline,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_mesh_pipeline(\n        &self,\n        _desc: &crate::MeshPipelineDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPipeline {\n        panic!(\"MESH_SHADER feature must be enabled to call create_mesh_pipeline\")\n    }\n\n    fn create_compute_pipeline(\n        &self,\n        desc: &crate::ComputePipelineDescriptor<'_>,\n    ) -> dispatch::DispatchComputePipeline {\n        let shader_module = desc.module.inner.as_webgpu();\n        let mapped_compute_stage = webgpu_sys::GpuProgrammableStage::new(&shader_module.module);\n        insert_constants_map(&mapped_compute_stage, desc.compilation_options.constants);\n        if let Some(ep) = desc.entry_point {\n            mapped_compute_stage.set_entry_point(ep);\n        }\n        let auto_layout = wasm_bindgen::JsValue::from(webgpu_sys::GpuAutoLayoutMode::Auto);\n        let mapped_desc = webgpu_sys::GpuComputePipelineDescriptor::new(\n            &match desc.layout {\n                Some(layout) => {\n                    let layout = &layout.inner.as_webgpu().inner;\n                    JsValue::from(layout)\n                }\n                None => auto_layout,\n            },\n            &mapped_compute_stage,\n        );\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let compute_pipeline = self.inner.create_compute_pipeline(&mapped_desc);\n\n        WebComputePipeline {\n            inner: compute_pipeline,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    unsafe fn create_pipeline_cache(\n        &self,\n        _desc: &crate::PipelineCacheDescriptor<'_>,\n    ) -> dispatch::DispatchPipelineCache {\n        WebPipelineCache {\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> dispatch::DispatchBuffer {\n        let mapped_desc = webgpu_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits());\n        mapped_desc.set_mapped_at_creation(desc.mapped_at_creation);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n        WebBuffer::new(self.inner.create_buffer(&mapped_desc).unwrap(), desc).into()\n    }\n\n    fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture {\n        let mapped_desc = webgpu_sys::GpuTextureDescriptor::new(\n            map_texture_format(desc.format),\n            &map_extent_3d(desc.size),\n            (desc.usage - crate::TextureUsages::TRANSIENT).bits(),\n        );\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n        mapped_desc.set_dimension(map_texture_dimension(desc.dimension));\n        mapped_desc.set_mip_level_count(desc.mip_level_count);\n        mapped_desc.set_sample_count(desc.sample_count);\n        let mapped_view_formats = desc\n            .view_formats\n            .iter()\n            .map(|format| JsValue::from(map_texture_format(*format)))\n            .collect::<js_sys::Array>();\n        mapped_desc.set_view_formats(&mapped_view_formats);\n\n        let texture = self.inner.create_texture(&mapped_desc).unwrap();\n        WebTexture {\n            inner: texture,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_external_texture(\n        &self,\n        _desc: &crate::ExternalTextureDescriptor<'_>,\n        _planes: &[&crate::TextureView],\n    ) -> dispatch::DispatchExternalTexture {\n        unimplemented!(\"ExternalTexture not implemented for web\");\n    }\n\n    fn create_blas(\n        &self,\n        _desc: &crate::CreateBlasDescriptor<'_>,\n        _sizes: crate::BlasGeometrySizeDescriptors,\n    ) -> (Option<u64>, dispatch::DispatchBlas) {\n        unimplemented!(\"Raytracing not implemented for web\");\n    }\n\n    fn create_tlas(&self, _desc: &crate::CreateTlasDescriptor<'_>) -> dispatch::DispatchTlas {\n        unimplemented!(\"Raytracing not implemented for web\");\n    }\n\n    fn create_sampler(&self, desc: &crate::SamplerDescriptor<'_>) -> dispatch::DispatchSampler {\n        let mapped_desc = webgpu_sys::GpuSamplerDescriptor::new();\n        mapped_desc.set_address_mode_u(map_address_mode(desc.address_mode_u));\n        mapped_desc.set_address_mode_v(map_address_mode(desc.address_mode_v));\n        mapped_desc.set_address_mode_w(map_address_mode(desc.address_mode_w));\n        if let Some(compare) = desc.compare {\n            mapped_desc.set_compare(map_compare_function(compare));\n        }\n        mapped_desc.set_lod_max_clamp(desc.lod_max_clamp);\n        mapped_desc.set_lod_min_clamp(desc.lod_min_clamp);\n        mapped_desc.set_mag_filter(map_filter_mode(desc.mag_filter));\n        mapped_desc.set_min_filter(map_filter_mode(desc.min_filter));\n        mapped_desc.set_mipmap_filter(map_mipmap_filter_mode(desc.mipmap_filter));\n        mapped_desc.set_max_anisotropy(desc.anisotropy_clamp);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let sampler = self.inner.create_sampler_with_descriptor(&mapped_desc);\n\n        WebSampler {\n            inner: sampler,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_query_set(&self, desc: &crate::QuerySetDescriptor<'_>) -> dispatch::DispatchQuerySet {\n        let ty = match desc.ty {\n            wgt::QueryType::Occlusion => webgpu_sys::GpuQueryType::Occlusion,\n            wgt::QueryType::Timestamp => webgpu_sys::GpuQueryType::Timestamp,\n            wgt::QueryType::PipelineStatistics(_) => unreachable!(),\n        };\n        let mapped_desc = webgpu_sys::GpuQuerySetDescriptor::new(desc.count, ty);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let query_set = self.inner.create_query_set(&mapped_desc).unwrap();\n\n        WebQuerySet {\n            inner: query_set,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_command_encoder(\n        &self,\n        desc: &crate::CommandEncoderDescriptor<'_>,\n    ) -> dispatch::DispatchCommandEncoder {\n        let mapped_desc = webgpu_sys::GpuCommandEncoderDescriptor::new();\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        let command_encoder = self\n            .inner\n            .create_command_encoder_with_descriptor(&mapped_desc);\n\n        WebCommandEncoder {\n            inner: command_encoder,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn create_render_bundle_encoder(\n        &self,\n        desc: &crate::RenderBundleEncoderDescriptor<'_>,\n    ) -> dispatch::DispatchRenderBundleEncoder {\n        let mapped_color_formats = desc\n            .color_formats\n            .iter()\n            .map(|cf| match cf {\n                Some(cf) => wasm_bindgen::JsValue::from(map_texture_format(*cf)),\n                None => wasm_bindgen::JsValue::null(),\n            })\n            .collect::<js_sys::Array>();\n        let mapped_desc = webgpu_sys::GpuRenderBundleEncoderDescriptor::new(&mapped_color_formats);\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n        if let Some(ds) = desc.depth_stencil {\n            mapped_desc.set_depth_stencil_format(map_texture_format(ds.format));\n            mapped_desc.set_depth_read_only(ds.depth_read_only);\n            mapped_desc.set_stencil_read_only(ds.stencil_read_only);\n        }\n        mapped_desc.set_sample_count(desc.sample_count);\n\n        let render_bundle_encoder = self\n            .inner\n            .create_render_bundle_encoder(&mapped_desc)\n            .unwrap();\n\n        WebRenderBundleEncoder {\n            inner: render_bundle_encoder,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn set_device_lost_callback(&self, device_lost_callback: dispatch::BoxDeviceLostCallback) {\n        let closure = Closure::once(move |info: JsValue| {\n            let info = info.dyn_into::<webgpu_sys::GpuDeviceLostInfo>().unwrap();\n            device_lost_callback(\n                match info.reason() {\n                    webgpu_sys::GpuDeviceLostReason::Destroyed => {\n                        crate::DeviceLostReason::Destroyed\n                    }\n                    webgpu_sys::GpuDeviceLostReason::Unknown => crate::DeviceLostReason::Unknown,\n                    _ => crate::DeviceLostReason::Unknown,\n                },\n                info.message(),\n            );\n        });\n        let _ = self.inner.lost().then(&closure);\n        // Release memory management of this closure from Rust to the JS GC.\n        // TODO: This will leak if weak references is not supported.\n        closure.forget();\n    }\n\n    fn on_uncaptured_error(&self, handler: Arc<dyn crate::UncapturedErrorHandler>) {\n        let f = Closure::wrap(Box::new(move |event: webgpu_sys::GpuUncapturedErrorEvent| {\n            let error = crate::Error::from_js(event.error().value_of());\n            handler(error);\n        }) as Box<dyn FnMut(_)>);\n        self.inner\n            .set_onuncapturederror(Some(f.as_ref().unchecked_ref()));\n        // Release memory management of this closure from Rust to the JS GC.\n        // TODO: This will leak if weak references is not supported.\n        f.forget();\n    }\n\n    fn push_error_scope(&self, filter: crate::ErrorFilter) -> u32 {\n        let index = self.error_scope_count.get();\n        self.error_scope_count.set(\n            index\n                .checked_add(1)\n                .expect(\"Greater than 2^32 nested error scopes\"),\n        );\n        self.inner.push_error_scope(match filter {\n            crate::ErrorFilter::OutOfMemory => webgpu_sys::GpuErrorFilter::OutOfMemory,\n            crate::ErrorFilter::Validation => webgpu_sys::GpuErrorFilter::Validation,\n            crate::ErrorFilter::Internal => webgpu_sys::GpuErrorFilter::Internal,\n        });\n        index\n    }\n\n    fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn dispatch::PopErrorScopeFuture>> {\n        let current_scope_count = self.error_scope_count.get();\n        let is_panicking = crate::util::is_panicking();\n        if current_scope_count == 0 && !is_panicking {\n            panic!(\"Mismatched pop_error_scope call: no error scope for this thread. Error scopes are thread-local.\");\n        }\n        if index + 1 != current_scope_count && !is_panicking {\n            panic!(\n                \"Mismatched pop_error_scope call: error scopes must be popped in reverse order.\"\n            );\n        }\n        // Decrement the error scope count. We've asserted that the current\n        // size is `index + 1` above.\n        self.error_scope_count.set(index);\n\n        let error_promise = self.inner.pop_error_scope();\n        Box::pin(MakeSendFuture::new(\n            wasm_bindgen_futures::JsFuture::from(error_promise),\n            future_pop_error_scope,\n        ))\n    }\n\n    unsafe fn start_graphics_debugger_capture(&self) {\n        // No capturing api in webgpu\n    }\n\n    unsafe fn stop_graphics_debugger_capture(&self) {\n        // No capturing api in webgpu\n    }\n\n    fn poll(&self, _poll_type: wgt::PollType<u64>) -> Result<crate::PollStatus, crate::PollError> {\n        // Device is polled automatically\n        Ok(crate::PollStatus::QueueEmpty)\n    }\n\n    fn get_internal_counters(&self) -> crate::InternalCounters {\n        crate::InternalCounters::default()\n    }\n\n    fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {\n        None\n    }\n\n    fn destroy(&self) {\n        self.inner.destroy();\n    }\n}\n\nimpl Drop for WebDevice {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::QueueInterface for WebQueue {\n    fn write_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        data: &[u8],\n    ) {\n        let buffer = buffer.as_webgpu();\n        self.inner\n            .write_buffer_with_f64_and_u8_slice_and_f64_and_f64(\n                &buffer.inner,\n                offset as f64,\n                data,\n                0f64,\n                data.len() as f64,\n            )\n            .unwrap();\n    }\n\n    fn create_staging_buffer(\n        &self,\n        size: crate::BufferSize,\n    ) -> Option<dispatch::DispatchQueueWriteBuffer> {\n        Some(\n            WebQueueWriteBuffer {\n                inner: vec![0; size.get() as usize].into_boxed_slice(),\n                ident: crate::cmp::Identifier::create(),\n            }\n            .into(),\n        )\n    }\n\n    fn validate_write_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: wgt::BufferAddress,\n        size: wgt::BufferSize,\n    ) -> Option<()> {\n        let buffer = buffer.as_webgpu();\n\n        let usage = wgt::BufferUsages::from_bits_truncate(buffer.inner.usage());\n        // TODO: actually send this down the error scope\n        if !usage.contains(wgt::BufferUsages::COPY_DST) {\n            log::error!(\"Destination buffer is missing the `COPY_DST` usage flag\");\n            return None;\n        }\n        let write_size = u64::from(size);\n        if !write_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            log::error!(\"Copy size {size} does not respect `COPY_BUFFER_ALIGNMENT`\");\n            return None;\n        }\n        if !offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            log::error!(\n                \"Buffer offset {offset} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`\"\n            );\n            return None;\n        }\n        if write_size + offset > buffer.inner.size() as u64 {\n            log::error!(\"copy of {}..{} would end up overrunning the bounds of the destination buffer of size {}\", offset, offset + write_size, buffer.inner.size());\n            return None;\n        }\n        Some(())\n    }\n\n    fn write_staging_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        staging_buffer: &dispatch::DispatchQueueWriteBuffer,\n    ) {\n        let staging_buffer = staging_buffer.as_webgpu();\n\n        dispatch::QueueInterface::write_buffer(self, buffer, offset, &staging_buffer.inner)\n    }\n\n    fn write_texture(\n        &self,\n        texture: crate::TexelCopyTextureInfo<'_>,\n        data: &[u8],\n        data_layout: crate::TexelCopyBufferLayout,\n        size: crate::Extent3d,\n    ) {\n        let mapped_data_layout = webgpu_sys::GpuTexelCopyBufferLayout::new();\n        if let Some(bytes_per_row) = data_layout.bytes_per_row {\n            mapped_data_layout.set_bytes_per_row(bytes_per_row);\n        }\n        if let Some(rows_per_image) = data_layout.rows_per_image {\n            mapped_data_layout.set_rows_per_image(rows_per_image);\n        }\n        mapped_data_layout.set_offset(data_layout.offset as f64);\n\n        self.inner\n            .write_texture_with_u8_slice_and_gpu_extent_3d_dict(\n                &map_texture_copy_view(texture),\n                data,\n                &mapped_data_layout,\n                &map_extent_3d(size),\n            )\n            .unwrap();\n    }\n\n    fn copy_external_image_to_texture(\n        &self,\n        source: &crate::CopyExternalImageSourceInfo,\n        dest: crate::CopyExternalImageDestInfo<&crate::api::Texture>,\n        size: crate::Extent3d,\n    ) {\n        self.inner\n            .copy_external_image_to_texture_with_gpu_extent_3d_dict(\n                &map_external_texture_copy_view(source),\n                &map_tagged_texture_copy_view(dest),\n                &map_extent_3d(size),\n            )\n            .unwrap();\n    }\n\n    fn submit(\n        &self,\n        command_buffers: &mut dyn Iterator<Item = dispatch::DispatchCommandBuffer>,\n    ) -> u64 {\n        let temp_command_buffers = command_buffers.collect::<Vec<_>>();\n\n        let array = temp_command_buffers\n            .iter()\n            .map(|buffer| &buffer.as_webgpu().inner)\n            .collect::<js_sys::Array>();\n\n        self.inner.submit(&array);\n\n        0\n    }\n\n    fn get_timestamp_period(&self) -> f32 {\n        // Timestamp values are always in nanoseconds, see https://gpuweb.github.io/gpuweb/#timestamp\n        1.0\n    }\n\n    fn on_submitted_work_done(&self, callback: dispatch::BoxSubmittedWorkDoneCallback) {\n        let promise = self.inner.on_submitted_work_done();\n        wasm_bindgen_futures::spawn_local(async move {\n            match wasm_bindgen_futures::JsFuture::from(promise).await {\n                Ok(_) => callback(),\n                Err(error) => {\n                    log::error!(\"on_submitted_work_done promise failed: {error:?}\");\n                    callback();\n                }\n            }\n        });\n    }\n\n    fn compact_blas(\n        &self,\n        _blas: &dispatch::DispatchBlas,\n    ) -> (Option<u64>, dispatch::DispatchBlas) {\n        unimplemented!(\"Raytracing not implemented for web\")\n    }\n}\nimpl Drop for WebQueue {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::ShaderModuleInterface for WebShaderModule {\n    fn get_compilation_info(&self) -> Pin<Box<dyn dispatch::ShaderCompilationInfoFuture>> {\n        let compilation_info_promise = self.module.get_compilation_info();\n        let map_future = Box::new({\n            let compilation_info = self.compilation_info.clone();\n            move |result| future_compilation_info(result, &compilation_info)\n        });\n        Box::pin(MakeSendFuture::new(\n            wasm_bindgen_futures::JsFuture::from(compilation_info_promise),\n            map_future,\n        ))\n    }\n}\nimpl Drop for WebShaderModule {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::BindGroupLayoutInterface for WebBindGroupLayout {}\nimpl Drop for WebBindGroupLayout {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::BindGroupInterface for WebBindGroup {}\nimpl Drop for WebBindGroup {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::TextureViewInterface for WebTextureView {}\nimpl Drop for WebTextureView {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::SamplerInterface for WebSampler {}\nimpl Drop for WebSampler {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::BufferInterface for WebBuffer {\n    fn map_async(\n        &self,\n        mode: crate::MapMode,\n        range: Range<crate::BufferAddress>,\n        callback: dispatch::BufferMapCallback,\n    ) {\n        let map_promise = self.inner.map_async_with_f64_and_f64(\n            map_map_mode(mode),\n            range.start as f64,\n            (range.end - range.start) as f64,\n        );\n\n        self.set_mapped_range(range);\n\n        register_then_closures(&map_promise, callback, Ok(()), Err(crate::BufferAsyncError));\n    }\n\n    fn get_mapped_range(\n        &self,\n        sub_range: Range<crate::BufferAddress>,\n    ) -> dispatch::DispatchBufferMappedRange {\n        let actual_mapping = self.get_mapped_range(sub_range);\n        WebBufferMappedRange {\n            actual_mapping,\n            temporary_mapping: OnceCell::new(),\n            temporary_mapping_modified: false,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn unmap(&self) {\n        self.inner.unmap();\n        self.mapping.borrow_mut().mapped_buffer = None;\n    }\n\n    fn destroy(&self) {\n        self.inner.destroy();\n    }\n}\nimpl Drop for WebBuffer {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::TextureInterface for WebTexture {\n    fn create_view(\n        &self,\n        desc: &crate::TextureViewDescriptor<'_>,\n    ) -> dispatch::DispatchTextureView {\n        let mapped = webgpu_sys::GpuTextureViewDescriptor::new();\n        if let Some(dim) = desc.dimension {\n            mapped.set_dimension(map_texture_view_dimension(dim));\n        }\n        if let Some(format) = desc.format {\n            mapped.set_format(map_texture_format(format));\n        }\n        mapped.set_aspect(map_texture_aspect(desc.aspect));\n        mapped.set_base_array_layer(desc.base_array_layer);\n        if let Some(count) = desc.array_layer_count {\n            mapped.set_array_layer_count(count);\n        }\n        mapped.set_base_mip_level(desc.base_mip_level);\n        if let Some(count) = desc.mip_level_count {\n            mapped.set_mip_level_count(count);\n        }\n        if let Some(label) = desc.label {\n            mapped.set_label(label);\n        }\n        mapped.set_usage(desc.usage.unwrap_or(wgt::TextureUsages::empty()).bits());\n\n        let view = self.inner.create_view_with_descriptor(&mapped).unwrap();\n\n        WebTextureView {\n            inner: view,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn destroy(&self) {\n        self.inner.destroy();\n    }\n}\nimpl Drop for WebTexture {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::ExternalTextureInterface for WebExternalTexture {\n    fn destroy(&self) {\n        unimplemented!(\"ExternalTexture not implemented for web\");\n    }\n}\nimpl Drop for WebExternalTexture {\n    fn drop(&mut self) {\n        unimplemented!(\"ExternalTexture not implemented for web\");\n    }\n}\n\nimpl dispatch::BlasInterface for WebBlas {\n    fn prepare_compact_async(&self, _callback: BlasCompactCallback) {\n        unimplemented!(\"Raytracing not implemented for web\")\n    }\n    fn ready_for_compaction(&self) -> bool {\n        unimplemented!(\"Raytracing not implemented for web\")\n    }\n}\nimpl Drop for WebBlas {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::TlasInterface for WebTlas {}\nimpl Drop for WebTlas {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::QuerySetInterface for WebQuerySet {}\nimpl Drop for WebQuerySet {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::PipelineLayoutInterface for WebPipelineLayout {}\nimpl Drop for WebPipelineLayout {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::RenderPipelineInterface for WebRenderPipeline {\n    fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {\n        let bind_group_layout = self.inner.get_bind_group_layout(index);\n\n        WebBindGroupLayout {\n            inner: bind_group_layout,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n}\nimpl Drop for WebRenderPipeline {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::ComputePipelineInterface for WebComputePipeline {\n    fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {\n        let bind_group_layout = self.inner.get_bind_group_layout(index);\n\n        WebBindGroupLayout {\n            inner: bind_group_layout,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n}\nimpl Drop for WebComputePipeline {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::CommandEncoderInterface for WebCommandEncoder {\n    fn copy_buffer_to_buffer(\n        &self,\n        source: &dispatch::DispatchBuffer,\n        source_offset: crate::BufferAddress,\n        destination: &dispatch::DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n        copy_size: Option<crate::BufferAddress>,\n    ) {\n        let source = source.as_webgpu();\n        let destination = destination.as_webgpu();\n\n        if let Some(size) = copy_size {\n            self.inner\n                .copy_buffer_to_buffer_with_f64_and_f64_and_f64(\n                    &source.inner,\n                    source_offset as f64,\n                    &destination.inner,\n                    destination_offset as f64,\n                    size as f64,\n                )\n                .unwrap();\n        } else {\n            self.inner\n                .copy_buffer_to_buffer_with_f64_and_f64(\n                    &source.inner,\n                    source_offset as f64,\n                    &destination.inner,\n                    destination_offset as f64,\n                )\n                .unwrap();\n        }\n    }\n\n    fn copy_buffer_to_texture(\n        &self,\n        source: crate::TexelCopyBufferInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        self.inner\n            .copy_buffer_to_texture_with_gpu_extent_3d_dict(\n                &map_buffer_copy_view(source),\n                &map_texture_copy_view(destination),\n                &map_extent_3d(copy_size),\n            )\n            .unwrap();\n    }\n\n    fn copy_texture_to_buffer(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyBufferInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        self.inner\n            .copy_texture_to_buffer_with_gpu_extent_3d_dict(\n                &map_texture_copy_view(source),\n                &map_buffer_copy_view(destination),\n                &map_extent_3d(copy_size),\n            )\n            .unwrap();\n    }\n\n    fn copy_texture_to_texture(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        self.inner\n            .copy_texture_to_texture_with_gpu_extent_3d_dict(\n                &map_texture_copy_view(source),\n                &map_texture_copy_view(destination),\n                &map_extent_3d(copy_size),\n            )\n            .unwrap();\n    }\n\n    fn begin_compute_pass(\n        &self,\n        desc: &crate::ComputePassDescriptor<'_>,\n    ) -> dispatch::DispatchComputePass {\n        let mapped_desc = webgpu_sys::GpuComputePassDescriptor::new();\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        if let Some(ref timestamp_writes) = desc.timestamp_writes {\n            let query_set = timestamp_writes.query_set.inner.as_webgpu();\n            let writes = webgpu_sys::GpuComputePassTimestampWrites::new(&query_set.inner);\n            if let Some(index) = timestamp_writes.beginning_of_pass_write_index {\n                writes.set_beginning_of_pass_write_index(index);\n            }\n            if let Some(index) = timestamp_writes.end_of_pass_write_index {\n                writes.set_end_of_pass_write_index(index);\n            }\n            mapped_desc.set_timestamp_writes(&writes);\n        }\n\n        let compute_pass = self.inner.begin_compute_pass_with_descriptor(&mapped_desc);\n\n        WebComputePassEncoder {\n            inner: compute_pass,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn begin_render_pass(\n        &self,\n        desc: &crate::RenderPassDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPass {\n        let mapped_color_attachments = desc\n            .color_attachments\n            .iter()\n            .map(|attachment| match attachment {\n                Some(ca) => {\n                    let mut clear_value: Option<wasm_bindgen::JsValue> = None;\n                    let load_value = match ca.ops.load {\n                        crate::LoadOp::Clear(color) => {\n                            clear_value = Some(wasm_bindgen::JsValue::from(map_color(color)));\n                            webgpu_sys::GpuLoadOp::Clear\n                        }\n                        crate::LoadOp::DontCare(_token) => {\n                            // WebGPU can't safely have a ClearOp::DontCare, so we clear to black\n                            // which is ideal for most GPUs.\n                            clear_value =\n                                Some(wasm_bindgen::JsValue::from(map_color(crate::Color::BLACK)));\n                            webgpu_sys::GpuLoadOp::Clear\n                        }\n                        crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,\n                    };\n\n                    let view = &ca.view.inner.as_webgpu().inner;\n\n                    let mapped_color_attachment = webgpu_sys::GpuRenderPassColorAttachment::new(\n                        load_value,\n                        map_store_op(ca.ops.store),\n                        view,\n                    );\n                    if let Some(cv) = clear_value {\n                        mapped_color_attachment.set_clear_value(&cv);\n                    }\n                    if let Some(rt) = ca.resolve_target {\n                        let resolve_target_view = &rt.inner.as_webgpu().inner;\n                        mapped_color_attachment.set_resolve_target(resolve_target_view);\n                    }\n                    mapped_color_attachment.set_store_op(map_store_op(ca.ops.store));\n\n                    wasm_bindgen::JsValue::from(mapped_color_attachment)\n                }\n                None => wasm_bindgen::JsValue::null(),\n            })\n            .collect::<js_sys::Array>();\n\n        let mapped_desc = webgpu_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments);\n\n        if let Some(label) = desc.label {\n            mapped_desc.set_label(label);\n        }\n\n        if let Some(dsa) = &desc.depth_stencil_attachment {\n            let depth_stencil_attachment = &dsa.view.inner.as_webgpu().inner;\n            let mapped_depth_stencil_attachment =\n                webgpu_sys::GpuRenderPassDepthStencilAttachment::new(depth_stencil_attachment);\n            if let Some(ref ops) = dsa.depth_ops {\n                let load_op = match ops.load {\n                    crate::LoadOp::Clear(v) => {\n                        mapped_depth_stencil_attachment.set_depth_clear_value(v);\n                        webgpu_sys::GpuLoadOp::Clear\n                    }\n                    crate::LoadOp::DontCare(_token) => {\n                        // WebGPU can't safely have a ClearOp::DontCare, so we clear to 1.0\n                        mapped_depth_stencil_attachment.set_depth_clear_value(1.0);\n                        webgpu_sys::GpuLoadOp::Clear\n                    }\n                    crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,\n                };\n                mapped_depth_stencil_attachment.set_depth_load_op(load_op);\n                mapped_depth_stencil_attachment.set_depth_store_op(map_store_op(ops.store));\n            }\n            mapped_depth_stencil_attachment.set_depth_read_only(dsa.depth_ops.is_none());\n            if let Some(ref ops) = dsa.stencil_ops {\n                let load_op = match ops.load {\n                    crate::LoadOp::Clear(v) => {\n                        mapped_depth_stencil_attachment.set_stencil_clear_value(v);\n                        webgpu_sys::GpuLoadOp::Clear\n                    }\n                    crate::LoadOp::DontCare(_token) => {\n                        // WebGPU can't safely have a ClearOp::DontCare, so we clear to 0\n                        mapped_depth_stencil_attachment.set_stencil_clear_value(0);\n                        webgpu_sys::GpuLoadOp::Clear\n                    }\n                    crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,\n                };\n                mapped_depth_stencil_attachment.set_stencil_load_op(load_op);\n                mapped_depth_stencil_attachment.set_stencil_store_op(map_store_op(ops.store));\n            }\n            mapped_depth_stencil_attachment.set_stencil_read_only(dsa.stencil_ops.is_none());\n            mapped_desc.set_depth_stencil_attachment(&mapped_depth_stencil_attachment);\n        }\n\n        if let Some(ref timestamp_writes) = desc.timestamp_writes {\n            let query_set = &timestamp_writes.query_set.inner.as_webgpu().inner;\n            let writes = webgpu_sys::GpuRenderPassTimestampWrites::new(query_set);\n            if let Some(index) = timestamp_writes.beginning_of_pass_write_index {\n                writes.set_beginning_of_pass_write_index(index);\n            }\n            if let Some(index) = timestamp_writes.end_of_pass_write_index {\n                writes.set_end_of_pass_write_index(index);\n            }\n            mapped_desc.set_timestamp_writes(&writes);\n        }\n\n        let render_pass = self.inner.begin_render_pass(&mapped_desc).unwrap();\n\n        WebRenderPassEncoder {\n            inner: render_pass,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn finish(&mut self) -> dispatch::DispatchCommandBuffer {\n        let label = self.inner.label();\n        let buffer = if label.is_empty() {\n            self.inner.finish()\n        } else {\n            let mapped_desc = webgpu_sys::GpuCommandBufferDescriptor::new();\n            mapped_desc.set_label(&label);\n\n            self.inner.finish_with_descriptor(&mapped_desc)\n        };\n\n        WebCommandBuffer {\n            inner: buffer,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn clear_texture(\n        &self,\n        _texture: &dispatch::DispatchTexture,\n        _subresource_range: &crate::ImageSubresourceRange,\n    ) {\n        unimplemented!(\"clear_texture is not yet implemented\");\n    }\n\n    fn clear_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferAddress>,\n    ) {\n        let buffer = buffer.as_webgpu();\n\n        match size {\n            Some(size) => {\n                self.inner\n                    .clear_buffer_with_f64_and_f64(&buffer.inner, offset as f64, size as f64)\n            }\n            None => self\n                .inner\n                .clear_buffer_with_f64(&buffer.inner, offset as f64),\n        }\n    }\n\n    fn insert_debug_marker(&self, label: &str) {\n        self.inner.insert_debug_marker(label)\n    }\n\n    fn push_debug_group(&self, group_label: &str) {\n        self.inner.push_debug_group(group_label)\n    }\n\n    fn pop_debug_group(&self) {\n        self.inner.pop_debug_group()\n    }\n\n    fn write_timestamp(&self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {\n        // Not available on WebGPU.\n        // This was part of the spec originally but got removed, see https://github.com/gpuweb/gpuweb/pull/4370\n        panic!(\"TIMESTAMP_QUERY_INSIDE_ENCODERS feature must be enabled to call write_timestamp on a command encoder.\")\n    }\n\n    fn resolve_query_set(\n        &self,\n        query_set: &dispatch::DispatchQuerySet,\n        first_query: u32,\n        query_count: u32,\n        destination: &dispatch::DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n    ) {\n        let query_set = &query_set.as_webgpu().inner;\n        let destination = &destination.as_webgpu().inner;\n\n        self.inner.resolve_query_set_with_u32(\n            query_set,\n            first_query,\n            query_count,\n            destination,\n            destination_offset as u32,\n        );\n    }\n\n    fn mark_acceleration_structures_built<'a>(\n        &self,\n        _blas: &mut dyn Iterator<Item = &'a Blas>,\n        _tlas: &mut dyn Iterator<Item = &'a Tlas>,\n    ) {\n        unimplemented!(\"Raytracing not implemented for web\");\n    }\n\n    fn build_acceleration_structures<'a>(\n        &self,\n        _blas: &mut dyn Iterator<Item = &'a crate::BlasBuildEntry<'a>>,\n        _tlas: &mut dyn Iterator<Item = &'a crate::Tlas>,\n    ) {\n        unimplemented!(\"Raytracing not implemented for web\");\n    }\n\n    fn transition_resources<'a>(\n        &mut self,\n        _buffer_transitions: &mut dyn Iterator<\n            Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,\n        >,\n        _texture_transitions: &mut dyn Iterator<\n            Item = wgt::TextureTransition<&'a dispatch::DispatchTexture>,\n        >,\n    ) {\n        // no-op\n    }\n}\nimpl Drop for WebCommandEncoder {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::PipelineCacheInterface for WebPipelineCache {\n    fn get_data(&self) -> Option<Vec<u8>> {\n        todo!()\n    }\n}\nimpl Drop for WebPipelineCache {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::ComputePassInterface for WebComputePassEncoder {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchComputePipeline) {\n        let pipeline = &pipeline.as_webgpu().inner;\n        self.inner.set_pipeline(pipeline);\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);\n\n        if offsets.is_empty() {\n            self.inner.set_bind_group(index, bind_group);\n        } else {\n            self.inner\n                .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n                    index,\n                    bind_group,\n                    offsets,\n                    0f64,\n                    offsets.len() as u32,\n                )\n                .unwrap();\n        }\n    }\n\n    fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {\n        panic!(\"IMMEDIATES feature must be enabled to call set_immediates\")\n    }\n\n    fn insert_debug_marker(&mut self, label: &str) {\n        self.inner.insert_debug_marker(label);\n    }\n\n    fn push_debug_group(&mut self, group_label: &str) {\n        self.inner.push_debug_group(group_label);\n    }\n\n    fn pop_debug_group(&mut self) {\n        self.inner.pop_debug_group();\n    }\n\n    fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {\n        panic!(\"TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a compute pass.\")\n    }\n\n    fn begin_pipeline_statistics_query(\n        &mut self,\n        _query_set: &dispatch::DispatchQuerySet,\n        _query_index: u32,\n    ) {\n        // Not available in gecko yet\n    }\n\n    fn end_pipeline_statistics_query(&mut self) {\n        // Not available in gecko yet\n    }\n\n    fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {\n        self.inner\n            .dispatch_workgroups_with_workgroup_count_y_and_workgroup_count_z(x, y, z);\n    }\n\n    fn dispatch_workgroups_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_webgpu();\n\n        self.inner\n            .dispatch_workgroups_indirect_with_f64(&indirect_buffer.inner, indirect_offset as f64);\n    }\n}\nimpl Drop for WebComputePassEncoder {\n    fn drop(&mut self) {\n        self.inner.end();\n    }\n}\n\nimpl dispatch::RenderPassInterface for WebRenderPassEncoder {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {\n        let pipeline = &pipeline.as_webgpu().inner;\n\n        self.inner.set_pipeline(pipeline);\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);\n\n        if offsets.is_empty() {\n            self.inner.set_bind_group(index, bind_group);\n        } else {\n            self.inner\n                .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n                    index,\n                    bind_group,\n                    offsets,\n                    0f64,\n                    offsets.len() as u32,\n                )\n                .unwrap();\n        }\n    }\n\n    fn set_index_buffer(\n        &mut self,\n        buffer: &dispatch::DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_webgpu();\n        let index_format = map_index_format(index_format);\n\n        if let Some(size) = size {\n            self.inner.set_index_buffer_with_f64_and_f64(\n                &buffer.inner,\n                index_format,\n                offset as f64,\n                size.get() as f64,\n            );\n        } else {\n            self.inner\n                .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);\n        }\n    }\n\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_webgpu();\n\n        if let Some(size) = size {\n            self.inner.set_vertex_buffer_with_f64_and_f64(\n                slot,\n                Some(&buffer.inner),\n                offset as f64,\n                size.get() as f64,\n            );\n        } else {\n            self.inner\n                .set_vertex_buffer_with_f64(slot, Some(&buffer.inner), offset as f64);\n        }\n    }\n\n    fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {\n        panic!(\"IMMEDIATES feature must be enabled to call set_immediates\")\n    }\n\n    fn set_blend_constant(&mut self, color: crate::Color) {\n        self.inner\n            .set_blend_constant_with_gpu_color_dict(&map_color(color))\n            .unwrap();\n    }\n\n    fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {\n        self.inner.set_scissor_rect(x, y, width, height);\n    }\n\n    fn set_viewport(\n        &mut self,\n        x: f32,\n        y: f32,\n        width: f32,\n        height: f32,\n        min_depth: f32,\n        max_depth: f32,\n    ) {\n        self.inner\n            .set_viewport(x, y, width, height, min_depth, max_depth);\n    }\n\n    fn set_stencil_reference(&mut self, reference: u32) {\n        self.inner.set_stencil_reference(reference);\n    }\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        self.inner\n            .draw_with_instance_count_and_first_vertex_and_first_instance(\n                vertices.end - vertices.start,\n                instances.end - instances.start,\n                vertices.start,\n                instances.start,\n            );\n    }\n\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        self.inner\n            .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(\n                indices.end - indices.start,\n                instances.end - instances.start,\n                indices.start,\n                base_vertex,\n                instances.start,\n            )\n    }\n\n    fn draw_mesh_tasks(&mut self, _group_count_x: u32, _group_count_y: u32, _group_count_z: u32) {\n        panic!(\"MESH_SHADER feature must be enabled to call draw_mesh_tasks\")\n    }\n\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n        self.inner\n            .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);\n    }\n\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n        self.inner\n            .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);\n    }\n\n    fn draw_mesh_tasks_indirect(\n        &mut self,\n        _indirect_buffer: &dispatch::DispatchBuffer,\n        _indirect_offset: crate::BufferAddress,\n    ) {\n        panic!(\"MESH_SHADER feature must be enabled to call draw_mesh_tasks_indirect\")\n    }\n\n    fn multi_draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n\n        for i in 0..count {\n            let offset = indirect_offset + i as crate::BufferAddress * 16;\n            self.inner\n                .draw_indirect_with_f64(&buffer.inner, offset as f64);\n        }\n    }\n\n    fn multi_draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n\n        for i in 0..count {\n            let offset = indirect_offset + i as crate::BufferAddress * 20;\n            self.inner\n                .draw_indexed_indirect_with_f64(&buffer.inner, offset as f64);\n        }\n    }\n\n    fn multi_draw_mesh_tasks_indirect(\n        &mut self,\n        _indirect_buffer: &dispatch::DispatchBuffer,\n        _indirect_offset: crate::BufferAddress,\n        _count: u32,\n    ) {\n        panic!(\"MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect\")\n    }\n\n    fn multi_draw_indirect_count(\n        &mut self,\n        _indirect_buffer: &dispatch::DispatchBuffer,\n        _indirect_offset: crate::BufferAddress,\n        _count_buffer: &dispatch::DispatchBuffer,\n        _count_buffer_offset: crate::BufferAddress,\n        _max_count: u32,\n    ) {\n        panic!(\n            \"MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indirect_count\"\n        )\n    }\n\n    fn multi_draw_indexed_indirect_count(\n        &mut self,\n        _indirect_buffer: &dispatch::DispatchBuffer,\n        _indirect_offset: crate::BufferAddress,\n        _count_buffer: &dispatch::DispatchBuffer,\n        _count_buffer_offset: crate::BufferAddress,\n        _max_count: u32,\n    ) {\n        panic!(\"MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indexed_indirect_count\")\n    }\n\n    fn multi_draw_mesh_tasks_indirect_count(\n        &mut self,\n        _indirect_buffer: &dispatch::DispatchBuffer,\n        _indirect_offset: crate::BufferAddress,\n        _count_buffer: &dispatch::DispatchBuffer,\n        _count_buffer_offset: crate::BufferAddress,\n        _max_count: u32,\n    ) {\n        panic!(\"MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect_count\")\n    }\n\n    fn insert_debug_marker(&mut self, label: &str) {\n        self.inner.insert_debug_marker(label);\n    }\n\n    fn push_debug_group(&mut self, group_label: &str) {\n        self.inner.push_debug_group(group_label);\n    }\n\n    fn pop_debug_group(&mut self) {\n        self.inner.pop_debug_group();\n    }\n\n    fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {\n        panic!(\"TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a render pass.\")\n    }\n\n    fn begin_occlusion_query(&mut self, query_index: u32) {\n        self.inner.begin_occlusion_query(query_index);\n    }\n\n    fn end_occlusion_query(&mut self) {\n        self.inner.end_occlusion_query();\n    }\n\n    fn begin_pipeline_statistics_query(\n        &mut self,\n        _query_set: &dispatch::DispatchQuerySet,\n        _query_index: u32,\n    ) {\n        // Removed from WebGPU in https://github.com/gpuweb/gpuweb/pull/2296\n    }\n\n    fn end_pipeline_statistics_query(&mut self) {\n        // Removed from WebGPU https://github.com/gpuweb/gpuweb/pull/2296\n    }\n\n    fn execute_bundles(\n        &mut self,\n        render_bundles: &mut dyn Iterator<Item = &dispatch::DispatchRenderBundle>,\n    ) {\n        let mapped = render_bundles\n            .map(|bundle| &bundle.as_webgpu().inner)\n            .collect::<js_sys::Array>();\n        self.inner.execute_bundles(&mapped);\n    }\n}\nimpl Drop for WebRenderPassEncoder {\n    fn drop(&mut self) {\n        self.inner.end();\n    }\n}\n\nimpl dispatch::CommandBufferInterface for WebCommandBuffer {}\nimpl Drop for WebCommandBuffer {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::RenderBundleEncoderInterface for WebRenderBundleEncoder {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {\n        let pipeline = &pipeline.as_webgpu().inner;\n        self.inner.set_pipeline(pipeline);\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);\n\n        if offsets.is_empty() {\n            self.inner.set_bind_group(index, bind_group);\n        } else {\n            self.inner\n                .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(\n                    index,\n                    bind_group,\n                    offsets,\n                    0f64,\n                    offsets.len() as u32,\n                )\n                .unwrap();\n        }\n    }\n\n    fn set_index_buffer(\n        &mut self,\n        buffer: &dispatch::DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_webgpu();\n        let index_format = map_index_format(index_format);\n\n        if let Some(size) = size {\n            self.inner.set_index_buffer_with_f64_and_f64(\n                &buffer.inner,\n                index_format,\n                offset as f64,\n                size.get() as f64,\n            );\n        } else {\n            self.inner\n                .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);\n        }\n    }\n\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_webgpu();\n\n        if let Some(size) = size {\n            self.inner.set_vertex_buffer_with_f64_and_f64(\n                slot,\n                Some(&buffer.inner),\n                offset as f64,\n                size.get() as f64,\n            );\n        } else {\n            self.inner\n                .set_vertex_buffer_with_f64(slot, Some(&buffer.inner), offset as f64);\n        }\n    }\n\n    fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {\n        panic!(\"IMMEDIATES feature must be enabled to call set_immediates\")\n    }\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        self.inner\n            .draw_with_instance_count_and_first_vertex_and_first_instance(\n                vertices.end - vertices.start,\n                instances.end - instances.start,\n                vertices.start,\n                instances.start,\n            );\n    }\n\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        self.inner\n            .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(\n                indices.end - indices.start,\n                instances.end - instances.start,\n                indices.start,\n                base_vertex,\n                instances.start,\n            )\n    }\n\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n        self.inner\n            .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);\n    }\n\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let buffer = indirect_buffer.as_webgpu();\n        self.inner\n            .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);\n    }\n\n    fn finish(self, desc: &crate::RenderBundleDescriptor<'_>) -> dispatch::DispatchRenderBundle\n    where\n        Self: Sized,\n    {\n        let bundle = match desc.label {\n            Some(label) => {\n                let mapped_desc = webgpu_sys::GpuRenderBundleDescriptor::new();\n                mapped_desc.set_label(label);\n                self.inner.finish_with_descriptor(&mapped_desc)\n            }\n            None => self.inner.finish(),\n        };\n\n        WebRenderBundle {\n            inner: bundle,\n            ident: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n}\nimpl Drop for WebRenderBundleEncoder {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::RenderBundleInterface for WebRenderBundle {}\nimpl Drop for WebRenderBundle {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::SurfaceInterface for WebSurface {\n    fn get_capabilities(&self, _adapter: &dispatch::DispatchAdapter) -> wgt::SurfaceCapabilities {\n        let mut formats = vec![\n            wgt::TextureFormat::Rgba8Unorm,\n            wgt::TextureFormat::Bgra8Unorm,\n            wgt::TextureFormat::Rgba16Float,\n        ];\n        let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format));\n        // Preferred canvas format will only be either \"rgba8unorm\" or \"bgra8unorm\".\n        // https://www.w3.org/TR/webgpu/#dom-gpu-getpreferredcanvasformat\n        let preferred_format = self\n            .gpu\n            .as_ref()\n            .expect(\"Caller could not have created an adapter if gpu is undefined.\")\n            .get_preferred_canvas_format();\n        if let Some(index) = mapped_formats.position(|format| format == preferred_format) {\n            formats.swap(0, index);\n        }\n\n        wgt::SurfaceCapabilities {\n            // https://gpuweb.github.io/gpuweb/#supported-context-formats\n            formats,\n            // Doesn't really have meaning on the web.\n            present_modes: vec![wgt::PresentMode::Fifo],\n            alpha_modes: vec![wgt::CompositeAlphaMode::Opaque],\n            // Statically set to RENDER_ATTACHMENT for now. See https://gpuweb.github.io/gpuweb/#dom-gpucanvasconfiguration-usage\n            usages: wgt::TextureUsages::RENDER_ATTACHMENT,\n        }\n    }\n\n    fn configure(&self, device: &dispatch::DispatchDevice, config: &crate::SurfaceConfiguration) {\n        let device = device.as_webgpu();\n\n        match self.canvas {\n            Canvas::Canvas(ref canvas) => {\n                canvas.set_width(config.width);\n                canvas.set_height(config.height);\n            }\n            Canvas::Offscreen(ref canvas) => {\n                canvas.set_width(config.width);\n                canvas.set_height(config.height);\n            }\n        }\n\n        if let wgt::PresentMode::Mailbox | wgt::PresentMode::Immediate = config.present_mode {\n            panic!(\"Only FIFO/Auto* is supported on web\");\n        }\n        if let wgt::CompositeAlphaMode::PostMultiplied | wgt::CompositeAlphaMode::Inherit =\n            config.alpha_mode\n        {\n            panic!(\"Only Opaque/Auto or PreMultiplied alpha mode are supported on web\");\n        }\n        let alpha_mode = match config.alpha_mode {\n            wgt::CompositeAlphaMode::PreMultiplied => webgpu_sys::GpuCanvasAlphaMode::Premultiplied,\n            _ => webgpu_sys::GpuCanvasAlphaMode::Opaque,\n        };\n        let mapped = webgpu_sys::GpuCanvasConfiguration::new(\n            &device.inner,\n            map_texture_format(config.format),\n        );\n        mapped.set_usage(config.usage.bits());\n        mapped.set_alpha_mode(alpha_mode);\n        let mapped_view_formats = config\n            .view_formats\n            .iter()\n            .map(|format| JsValue::from(map_texture_format(*format)))\n            .collect::<js_sys::Array>();\n        mapped.set_view_formats(&mapped_view_formats);\n        self.context.configure(&mapped).unwrap();\n    }\n\n    fn get_current_texture(\n        &self,\n    ) -> (\n        Option<dispatch::DispatchTexture>,\n        crate::SurfaceStatus,\n        dispatch::DispatchSurfaceOutputDetail,\n    ) {\n        let surface_texture = self.context.get_current_texture().unwrap();\n\n        let web_surface_texture = WebTexture {\n            inner: surface_texture,\n            ident: crate::cmp::Identifier::create(),\n        };\n\n        (\n            Some(web_surface_texture.into()),\n            crate::SurfaceStatus::Good,\n            WebSurfaceOutputDetail {\n                ident: crate::cmp::Identifier::create(),\n            }\n            .into(),\n        )\n    }\n}\nimpl Drop for WebSurface {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl dispatch::SurfaceOutputDetailInterface for WebSurfaceOutputDetail {\n    fn present(&self) {\n        // Swapchain is presented automatically on the web.\n    }\n\n    fn texture_discard(&self) {\n        // Can't really discard the texture on the web.\n    }\n}\nimpl Drop for WebSurfaceOutputDetail {\n    fn drop(&mut self) {\n        // no-op\n    }\n}\n\nimpl WebBufferMappedRange {\n    fn get_temporary_mapping(&self) -> &[u8] {\n        self.temporary_mapping\n            .get_or_init(|| self.actual_mapping.to_vec())\n    }\n}\nimpl dispatch::BufferMappedRangeInterface for WebBufferMappedRange {\n    fn len(&self) -> usize {\n        self.get_temporary_mapping().len()\n    }\n\n    #[inline]\n    unsafe fn read_slice(&self) -> &[u8] {\n        self.get_temporary_mapping()\n    }\n\n    #[inline]\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {\n        self.temporary_mapping_modified = true;\n        self.get_temporary_mapping();\n        let t: &mut Vec<u8> = self.temporary_mapping.get_mut().unwrap();\n        WriteOnly::from_mut(t)\n    }\n\n    #[inline]\n    fn as_uint8array(&self) -> &js_sys::Uint8Array {\n        &self.actual_mapping\n    }\n}\nimpl Drop for WebBufferMappedRange {\n    fn drop(&mut self) {\n        if !self.temporary_mapping_modified {\n            // For efficiency, skip the copy if it is not needed.\n            // This is also how we skip copying back on *read-only* mappings.\n            return;\n        }\n\n        // Copy from the temporary mapping back into the array buffer that was\n        // originally provided by the browser\n        let temporary_mapping_slice = self.temporary_mapping.get().unwrap().as_slice();\n        unsafe {\n            // Note: no allocations can happen between `view` and `set`, or this\n            // will break\n            self.actual_mapping\n                .set(&js_sys::Uint8Array::view(temporary_mapping_slice), 0);\n        }\n    }\n}\n\nimpl dispatch::QueueWriteBufferInterface for WebQueueWriteBuffer {\n    #[inline]\n    fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    #[inline]\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {\n        WriteOnly::from_mut(&mut *self.inner)\n    }\n}\nimpl Drop for WebQueueWriteBuffer {\n    fn drop(&mut self) {\n        // The api struct calls write_staging_buffer\n\n        // no-op\n    }\n}\n\n/// Adds the constants map to the given pipeline descriptor if the map is nonempty.\n/// Panics if the map cannot be set.\n///\n/// This function is necessary because the constants array is not currently\n/// exposed by `wasm-bindgen`. See the following issues for details:\n/// - [gfx-rs/wgpu#5688](https://github.com/gfx-rs/wgpu/pull/5688)\n/// - [rustwasm/wasm-bindgen#3587](https://github.com/rustwasm/wasm-bindgen/issues/3587)\nfn insert_constants_map(target: &JsValue, map: &[(&str, f64)]) {\n    if !map.is_empty() {\n        js_sys::Reflect::set(\n            target,\n            &JsValue::from_str(\"constants\"),\n            &hashmap_to_jsvalue(map),\n        )\n        .expect(\"Setting the values in a Javascript pipeline descriptor should never fail\");\n    }\n}\n\n/// Converts a hashmap to a Javascript object.\nfn hashmap_to_jsvalue(map: &[(&str, f64)]) -> JsValue {\n    let obj = js_sys::Object::new();\n\n    for &(key, v) in map.iter() {\n        js_sys::Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from_f64(v))\n            .expect(\"Setting the values in a Javascript map should never fail\");\n    }\n\n    JsValue::from(obj)\n}\n"
  },
  {
    "path": "wgpu/src/backend/wgpu_core/thread_id.rs",
    "content": "//! Implementation of thread IDs for error scope tracking.\n//!\n//! Supports both std and no_std environments, though\n//! the no_std implementation is a stub that does not\n//! actually distinguish between threads.\n\n#[cfg(feature = \"std\")]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct ThreadId(std::thread::ThreadId);\n\n#[cfg(feature = \"std\")]\nimpl ThreadId {\n    pub fn current() -> Self {\n        ThreadId(std::thread::current().id())\n    }\n}\n\n#[cfg(not(feature = \"std\"))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct ThreadId(());\n\n#[cfg(not(feature = \"std\"))]\nimpl ThreadId {\n    pub fn current() -> Self {\n        // A simple stub implementation for non-std environments. On\n        // no_std but multithreaded platforms, this will work, but\n        // make error scope global rather than thread-local.\n        ThreadId(())\n    }\n}\n"
  },
  {
    "path": "wgpu/src/backend/wgpu_core.rs",
    "content": "use alloc::{\n    borrow::Cow::{self, Borrowed},\n    boxed::Box,\n    format,\n    string::{String, ToString as _},\n    sync::Arc,\n    vec,\n    vec::Vec,\n};\nuse core::{\n    error::Error,\n    fmt,\n    future::ready,\n    ops::{Deref, Range},\n    pin::Pin,\n    ptr::NonNull,\n    slice,\n};\nuse hashbrown::HashMap;\n\nuse arrayvec::ArrayVec;\nuse smallvec::SmallVec;\nuse wgc::{\n    command::bundle_ffi::*, error::ContextErrorSource, pipeline::CreateShaderModuleError,\n    resource::BlasPrepareCompactResult,\n};\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    WasmNotSendSync,\n};\n\nuse crate::{\n    api,\n    dispatch::{self, BlasCompactCallback, BufferMappedRangeInterface},\n    BindingResource, Blas, BufferBinding, BufferDescriptor, CompilationInfo, CompilationMessage,\n    CompilationMessageType, ErrorSource, Features, Label, LoadOp, MapMode, Operations,\n    ShaderSource, SurfaceTargetUnsafe, TextureDescriptor, Tlas, WriteOnly,\n};\nuse crate::{dispatch::DispatchAdapter, util::Mutex};\n\nmod thread_id;\n\n#[derive(Clone)]\npub struct ContextWgpuCore(Arc<wgc::global::Global>);\n\nimpl Drop for ContextWgpuCore {\n    fn drop(&mut self) {\n        //nothing\n    }\n}\n\nimpl fmt::Debug for ContextWgpuCore {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ContextWgpuCore\")\n            .field(\"type\", &\"Native\")\n            .finish()\n    }\n}\n\nimpl ContextWgpuCore {\n    pub unsafe fn from_hal_instance<A: hal::Api>(hal_instance: A::Instance) -> Self {\n        Self(unsafe {\n            Arc::new(wgc::global::Global::from_hal_instance::<A>(\n                \"wgpu\",\n                hal_instance,\n            ))\n        })\n    }\n\n    /// # Safety\n    ///\n    /// - The raw instance handle returned must not be manually destroyed.\n    pub unsafe fn instance_as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {\n        unsafe { self.0.instance_as_hal::<A>() }\n    }\n\n    pub unsafe fn from_core_instance(core_instance: wgc::instance::Instance) -> Self {\n        Self(unsafe { Arc::new(wgc::global::Global::from_instance(core_instance)) })\n    }\n\n    #[cfg(wgpu_core)]\n    pub fn enumerate_adapters(&self, backends: wgt::Backends) -> Vec<wgc::id::AdapterId> {\n        self.0.enumerate_adapters(backends)\n    }\n\n    pub unsafe fn create_adapter_from_hal<A: hal::Api>(\n        &self,\n        hal_adapter: hal::ExposedAdapter<A>,\n    ) -> wgc::id::AdapterId {\n        unsafe { self.0.create_adapter_from_hal(hal_adapter.into(), None) }\n    }\n\n    pub unsafe fn adapter_as_hal<A: hal::Api>(\n        &self,\n        adapter: &CoreAdapter,\n    ) -> Option<impl Deref<Target = A::Adapter> + WasmNotSendSync> {\n        unsafe { self.0.adapter_as_hal::<A>(adapter.id) }\n    }\n\n    pub unsafe fn buffer_as_hal<A: hal::Api>(\n        &self,\n        buffer: &CoreBuffer,\n    ) -> Option<impl Deref<Target = A::Buffer>> {\n        unsafe { self.0.buffer_as_hal::<A>(buffer.id) }\n    }\n\n    pub unsafe fn create_device_from_hal<A: hal::Api>(\n        &self,\n        adapter: &CoreAdapter,\n        hal_device: hal::OpenDevice<A>,\n        desc: &crate::DeviceDescriptor<'_>,\n    ) -> Result<(CoreDevice, CoreQueue), crate::RequestDeviceError> {\n        let (device_id, queue_id) = unsafe {\n            self.0.create_device_from_hal(\n                adapter.id,\n                hal_device.into(),\n                &desc.map_label(|l| l.map(Borrowed)),\n                None,\n                None,\n            )\n        }?;\n        let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new()));\n        let device = CoreDevice {\n            context: self.clone(),\n            id: device_id,\n            error_sink: error_sink.clone(),\n            features: desc.required_features,\n        };\n        let queue = CoreQueue {\n            context: self.clone(),\n            id: queue_id,\n            error_sink,\n        };\n        Ok((device, queue))\n    }\n\n    pub unsafe fn create_texture_from_hal<A: hal::Api>(\n        &self,\n        hal_texture: A::Texture,\n        device: &CoreDevice,\n        desc: &TextureDescriptor<'_>,\n    ) -> CoreTexture {\n        let descriptor = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec());\n        let (id, error) = unsafe {\n            self.0\n                .create_texture_from_hal(Box::new(hal_texture), device.id, &descriptor, None)\n        };\n        if let Some(cause) = error {\n            self.handle_error(\n                &device.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_texture_from_hal\",\n            );\n        }\n        CoreTexture {\n            context: self.clone(),\n            id,\n            error_sink: Arc::clone(&device.error_sink),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// - `hal_buffer` must be created from `device`.\n    /// - `hal_buffer` must be created respecting `desc`\n    /// - `hal_buffer` must be initialized\n    /// - `hal_buffer` must not have zero size.\n    pub unsafe fn create_buffer_from_hal<A: hal::Api>(\n        &self,\n        hal_buffer: A::Buffer,\n        device: &CoreDevice,\n        desc: &BufferDescriptor<'_>,\n    ) -> CoreBuffer {\n        let (id, error) = unsafe {\n            self.0.create_buffer_from_hal::<A>(\n                hal_buffer,\n                device.id,\n                &desc.map_label(|l| l.map(Borrowed)),\n                None,\n            )\n        };\n        if let Some(cause) = error {\n            self.handle_error(\n                &device.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_buffer_from_hal\",\n            );\n        }\n        CoreBuffer {\n            context: self.clone(),\n            id,\n            error_sink: Arc::clone(&device.error_sink),\n        }\n    }\n\n    pub unsafe fn device_as_hal<A: hal::Api>(\n        &self,\n        device: &CoreDevice,\n    ) -> Option<impl Deref<Target = A::Device>> {\n        unsafe { self.0.device_as_hal::<A>(device.id) }\n    }\n\n    pub unsafe fn surface_as_hal<A: hal::Api>(\n        &self,\n        surface: &CoreSurface,\n    ) -> Option<impl Deref<Target = A::Surface>> {\n        unsafe { self.0.surface_as_hal::<A>(surface.id) }\n    }\n\n    pub unsafe fn texture_as_hal<A: hal::Api>(\n        &self,\n        texture: &CoreTexture,\n    ) -> Option<impl Deref<Target = A::Texture>> {\n        unsafe { self.0.texture_as_hal::<A>(texture.id) }\n    }\n\n    pub unsafe fn texture_view_as_hal<A: hal::Api>(\n        &self,\n        texture_view: &CoreTextureView,\n    ) -> Option<impl Deref<Target = A::TextureView>> {\n        unsafe { self.0.texture_view_as_hal::<A>(texture_view.id) }\n    }\n\n    /// This method will start the wgpu_core level command recording.\n    pub unsafe fn command_encoder_as_hal_mut<\n        A: hal::Api,\n        F: FnOnce(Option<&mut A::CommandEncoder>) -> R,\n        R,\n    >(\n        &self,\n        command_encoder: &CoreCommandEncoder,\n        hal_command_encoder_callback: F,\n    ) -> R {\n        unsafe {\n            self.0.command_encoder_as_hal_mut::<A, F, R>(\n                command_encoder.id,\n                hal_command_encoder_callback,\n            )\n        }\n    }\n\n    pub unsafe fn blas_as_hal<A: hal::Api>(\n        &self,\n        blas: &CoreBlas,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {\n        unsafe { self.0.blas_as_hal::<A>(blas.id) }\n    }\n\n    pub unsafe fn tlas_as_hal<A: hal::Api>(\n        &self,\n        tlas: &CoreTlas,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {\n        unsafe { self.0.tlas_as_hal::<A>(tlas.id) }\n    }\n\n    pub fn generate_report(&self) -> wgc::global::GlobalReport {\n        self.0.generate_report()\n    }\n\n    #[cold]\n    #[track_caller]\n    #[inline(never)]\n    fn handle_error_inner(\n        &self,\n        sink_mutex: &Mutex<ErrorSinkRaw>,\n        error_type: ErrorType,\n        source: ContextErrorSource,\n        label: Label<'_>,\n        fn_ident: &'static str,\n    ) {\n        let source: ErrorSource = Box::new(wgc::error::ContextError {\n            fn_ident,\n            source,\n            label: label.unwrap_or_default().to_string(),\n        });\n        let final_error_handling = {\n            let mut sink = sink_mutex.lock();\n            let description = || self.format_error(&*source);\n            let error = match error_type {\n                ErrorType::Internal => {\n                    let description = description();\n                    crate::Error::Internal {\n                        source,\n                        description,\n                    }\n                }\n                ErrorType::OutOfMemory => crate::Error::OutOfMemory { source },\n                ErrorType::Validation => {\n                    let description = description();\n                    crate::Error::Validation {\n                        source,\n                        description,\n                    }\n                }\n                ErrorType::DeviceLost => return, // will be surfaced via callback\n            };\n            sink.handle_error_or_return_handler(error)\n        };\n\n        if let Some(f) = final_error_handling {\n            // If the user has provided their own `uncaptured_handler` callback, invoke it now,\n            // having released our lock on `sink_mutex`. See the comments on\n            // `handle_error_or_return_handler` for details.\n            f();\n        }\n    }\n\n    #[inline]\n    #[track_caller]\n    fn handle_error(\n        &self,\n        sink_mutex: &Mutex<ErrorSinkRaw>,\n        source: impl WebGpuError + WasmNotSendSync + 'static,\n        label: Label<'_>,\n        fn_ident: &'static str,\n    ) {\n        let error_type = source.webgpu_error_type();\n        self.handle_error_inner(sink_mutex, error_type, Box::new(source), label, fn_ident)\n    }\n\n    #[inline]\n    #[track_caller]\n    fn handle_error_nolabel(\n        &self,\n        sink_mutex: &Mutex<ErrorSinkRaw>,\n        source: impl WebGpuError + WasmNotSendSync + 'static,\n        fn_ident: &'static str,\n    ) {\n        let error_type = source.webgpu_error_type();\n        self.handle_error_inner(sink_mutex, error_type, Box::new(source), None, fn_ident)\n    }\n\n    #[track_caller]\n    #[cold]\n    fn handle_error_fatal(\n        &self,\n        cause: impl Error + WasmNotSendSync + 'static,\n        operation: &'static str,\n    ) -> ! {\n        panic!(\"Error in {operation}: {f}\", f = self.format_error(&cause));\n    }\n\n    #[inline(never)]\n    fn format_error(&self, err: &(dyn Error + 'static)) -> String {\n        let mut output = String::new();\n        let mut level = 1;\n\n        fn print_tree(output: &mut String, level: &mut usize, e: &(dyn Error + 'static)) {\n            let mut print = |e: &(dyn Error + 'static)| {\n                use core::fmt::Write;\n                writeln!(output, \"{}{}\", \" \".repeat(*level * 2), e).unwrap();\n\n                if let Some(e) = e.source() {\n                    *level += 1;\n                    print_tree(output, level, e);\n                    *level -= 1;\n                }\n            };\n            if let Some(multi) = e.downcast_ref::<wgc::error::MultiError>() {\n                for e in multi.errors() {\n                    print(e);\n                }\n            } else {\n                print(e);\n            }\n        }\n\n        print_tree(&mut output, &mut level, err);\n\n        format!(\"Validation Error\\n\\nCaused by:\\n{output}\")\n    }\n\n    pub unsafe fn queue_as_hal<A: hal::Api>(\n        &self,\n        queue: &CoreQueue,\n    ) -> Option<impl Deref<Target = A::Queue> + WasmNotSendSync> {\n        unsafe { self.0.queue_as_hal::<A>(queue.id) }\n    }\n}\n\nfn map_buffer_copy_view(\n    view: crate::TexelCopyBufferInfo<'_>,\n) -> wgt::TexelCopyBufferInfo<wgc::id::BufferId> {\n    wgt::TexelCopyBufferInfo {\n        buffer: view.buffer.inner.as_core().id,\n        layout: view.layout,\n    }\n}\n\nfn map_texture_copy_view(\n    view: crate::TexelCopyTextureInfo<'_>,\n) -> wgt::TexelCopyTextureInfo<wgc::id::TextureId> {\n    wgt::TexelCopyTextureInfo {\n        texture: view.texture.inner.as_core().id,\n        mip_level: view.mip_level,\n        origin: view.origin,\n        aspect: view.aspect,\n    }\n}\n\n#[cfg_attr(not(webgl), expect(unused))]\nfn map_texture_tagged_copy_view(\n    view: crate::CopyExternalImageDestInfo<&api::Texture>,\n) -> wgt::CopyExternalImageDestInfo<wgc::id::TextureId> {\n    wgt::CopyExternalImageDestInfo {\n        texture: view.texture.inner.as_core().id,\n        mip_level: view.mip_level,\n        origin: view.origin,\n        aspect: view.aspect,\n        color_space: view.color_space,\n        premultiplied_alpha: view.premultiplied_alpha,\n    }\n}\n\nfn map_load_op<V: Copy>(load: &LoadOp<V>) -> LoadOp<Option<V>> {\n    match *load {\n        LoadOp::Clear(clear_value) => LoadOp::Clear(Some(clear_value)),\n        LoadOp::DontCare(token) => LoadOp::DontCare(token),\n        LoadOp::Load => LoadOp::Load,\n    }\n}\n\nfn map_pass_channel<V: Copy>(ops: Option<&Operations<V>>) -> wgc::command::PassChannel<Option<V>> {\n    match ops {\n        Some(&Operations { load, store }) => wgc::command::PassChannel {\n            load_op: Some(map_load_op(&load)),\n            store_op: Some(store),\n            read_only: false,\n        },\n        None => wgc::command::PassChannel {\n            load_op: None,\n            store_op: None,\n            read_only: true,\n        },\n    }\n}\n\n#[derive(Debug)]\npub struct CoreSurface {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::SurfaceId,\n    /// Configured device is needed to know which backend\n    /// code to execute when acquiring a new frame.\n    configured_device: Mutex<Option<wgc::id::DeviceId>>,\n    /// The error sink with which to report errors.\n    /// `None` if the surface has not been configured.\n    error_sink: Mutex<Option<ErrorSink>>,\n}\n\n#[derive(Debug)]\npub struct CoreAdapter {\n    pub(crate) context: ContextWgpuCore,\n    pub(crate) id: wgc::id::AdapterId,\n}\n\n#[derive(Debug)]\npub struct CoreDevice {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::DeviceId,\n    error_sink: ErrorSink,\n    features: Features,\n}\n\n#[derive(Debug)]\npub struct CoreBuffer {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::BufferId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreShaderModule {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::ShaderModuleId,\n    compilation_info: CompilationInfo,\n}\n\n#[derive(Debug)]\npub struct CoreBindGroupLayout {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::BindGroupLayoutId,\n}\n\n#[derive(Debug)]\npub struct CoreBindGroup {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::BindGroupId,\n}\n\n#[derive(Debug)]\npub struct CoreTexture {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::TextureId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreTextureView {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::TextureViewId,\n}\n\n#[derive(Debug)]\npub struct CoreExternalTexture {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::ExternalTextureId,\n}\n\n#[derive(Debug)]\npub struct CoreSampler {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::SamplerId,\n}\n\n#[derive(Debug)]\npub struct CoreQuerySet {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::QuerySetId,\n}\n\n#[derive(Debug)]\npub struct CorePipelineLayout {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::PipelineLayoutId,\n}\n\n#[derive(Debug)]\npub struct CorePipelineCache {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::PipelineCacheId,\n}\n\n#[derive(Debug)]\npub struct CoreCommandBuffer {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::CommandBufferId,\n}\n\n#[derive(Debug)]\npub struct CoreRenderBundleEncoder {\n    pub(crate) context: ContextWgpuCore,\n    encoder: wgc::command::RenderBundleEncoder,\n    id: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct CoreRenderBundle {\n    context: ContextWgpuCore,\n    id: wgc::id::RenderBundleId,\n}\n\n#[derive(Debug)]\npub struct CoreQueue {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::QueueId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreComputePipeline {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::ComputePipelineId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreRenderPipeline {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::RenderPipelineId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreComputePass {\n    pub(crate) context: ContextWgpuCore,\n    pass: wgc::command::ComputePass,\n    error_sink: ErrorSink,\n    id: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct CoreRenderPass {\n    pub(crate) context: ContextWgpuCore,\n    pass: wgc::command::RenderPass,\n    error_sink: ErrorSink,\n    id: crate::cmp::Identifier,\n}\n\n#[derive(Debug)]\npub struct CoreCommandEncoder {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::CommandEncoderId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreBlas {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::BlasId,\n    error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreTlas {\n    pub(crate) context: ContextWgpuCore,\n    id: wgc::id::TlasId,\n    // error_sink: ErrorSink,\n}\n\n#[derive(Debug)]\npub struct CoreSurfaceOutputDetail {\n    context: ContextWgpuCore,\n    surface_id: wgc::id::SurfaceId,\n    error_sink: ErrorSink,\n}\n\ntype ErrorSink = Arc<Mutex<ErrorSinkRaw>>;\n\nstruct ErrorScope {\n    error: Option<crate::Error>,\n    filter: crate::ErrorFilter,\n}\n\nstruct ErrorSinkRaw {\n    scopes: HashMap<thread_id::ThreadId, Vec<ErrorScope>>,\n    uncaptured_handler: Option<Arc<dyn crate::UncapturedErrorHandler>>,\n}\n\nimpl ErrorSinkRaw {\n    fn new() -> ErrorSinkRaw {\n        ErrorSinkRaw {\n            scopes: HashMap::new(),\n            uncaptured_handler: None,\n        }\n    }\n\n    /// Deliver the error to\n    ///\n    /// * the innermost error scope, if any, or\n    /// * the uncaptured error handler, if there is one, or\n    /// * [`default_error_handler()`].\n    ///\n    /// If a closure is returned, the caller should call it immediately after dropping the\n    /// [`ErrorSink`] mutex guard. This makes sure that the user callback is not called with\n    /// a wgpu mutex held.\n    #[track_caller]\n    #[must_use]\n    fn handle_error_or_return_handler(&mut self, err: crate::Error) -> Option<impl FnOnce()> {\n        let filter = match err {\n            crate::Error::OutOfMemory { .. } => crate::ErrorFilter::OutOfMemory,\n            crate::Error::Validation { .. } => crate::ErrorFilter::Validation,\n            crate::Error::Internal { .. } => crate::ErrorFilter::Internal,\n        };\n        let thread_id = thread_id::ThreadId::current();\n        let scopes = self.scopes.entry(thread_id).or_default();\n        match scopes.iter_mut().rev().find(|scope| scope.filter == filter) {\n            Some(scope) => {\n                if scope.error.is_none() {\n                    scope.error = Some(err);\n                }\n                None\n            }\n            None => {\n                if let Some(custom_handler) = &self.uncaptured_handler {\n                    let custom_handler = Arc::clone(custom_handler);\n                    Some(move || (custom_handler)(err))\n                } else {\n                    // direct call preserves #[track_caller] where dyn can't\n                    default_error_handler(err)\n                }\n            }\n        }\n    }\n}\n\nimpl fmt::Debug for ErrorSinkRaw {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"ErrorSink\")\n    }\n}\n\n#[track_caller]\nfn default_error_handler(err: crate::Error) -> ! {\n    log::error!(\"Handling wgpu errors as fatal by default\");\n    panic!(\"wgpu error: {err}\\n\");\n}\n\nimpl From<CreateShaderModuleError> for CompilationInfo {\n    fn from(value: CreateShaderModuleError) -> Self {\n        match value {\n            #[cfg(feature = \"wgsl\")]\n            CreateShaderModuleError::Parsing(v) => v.into(),\n            #[cfg(feature = \"glsl\")]\n            CreateShaderModuleError::ParsingGlsl(v) => v.into(),\n            #[cfg(feature = \"spirv\")]\n            CreateShaderModuleError::ParsingSpirV(v) => v.into(),\n            CreateShaderModuleError::Validation(v) => v.into(),\n            // Device errors are reported through the error sink, and are not compilation errors.\n            // Same goes for native shader module generation errors.\n            CreateShaderModuleError::Device(_) | CreateShaderModuleError::Generation => {\n                CompilationInfo {\n                    messages: Vec::new(),\n                }\n            }\n            // Everything else is an error message without location information.\n            _ => CompilationInfo {\n                messages: vec![CompilationMessage {\n                    message: value.to_string(),\n                    message_type: CompilationMessageType::Error,\n                    location: None,\n                }],\n            },\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct CoreQueueWriteBuffer {\n    buffer_id: wgc::id::StagingBufferId,\n    mapping: CoreBufferMappedRange,\n}\n\n#[derive(Debug)]\npub struct CoreBufferMappedRange {\n    ptr: NonNull<u8>,\n    size: usize,\n}\n\n#[cfg(send_sync)]\nunsafe impl Send for CoreBufferMappedRange {}\n#[cfg(send_sync)]\nunsafe impl Sync for CoreBufferMappedRange {}\n\nimpl Drop for CoreBufferMappedRange {\n    fn drop(&mut self) {\n        // Intentionally left blank so that `BufferMappedRange` still\n        // implements `Drop`, to match the web backend\n    }\n}\n\ncrate::cmp::impl_eq_ord_hash_arc_address!(ContextWgpuCore => .0);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreAdapter => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreDevice => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreQueue => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreShaderModule => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreBindGroupLayout => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreBindGroup => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreTextureView => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreSampler => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreBuffer => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreTexture => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreExternalTexture => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreBlas => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreTlas => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreQuerySet => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CorePipelineLayout => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreRenderPipeline => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreComputePipeline => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CorePipelineCache => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreCommandEncoder => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreComputePass => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreRenderPass => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreCommandBuffer => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreRenderBundleEncoder => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreRenderBundle => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreSurface => .id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreSurfaceOutputDetail => .surface_id);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreQueueWriteBuffer => .mapping.ptr);\ncrate::cmp::impl_eq_ord_hash_proxy!(CoreBufferMappedRange => .ptr);\n\nimpl dispatch::InstanceInterface for ContextWgpuCore {\n    fn new(desc: wgt::InstanceDescriptor) -> Self\n    where\n        Self: Sized,\n    {\n        Self(Arc::new(wgc::global::Global::new(\"wgpu\", desc, None)))\n    }\n\n    unsafe fn create_surface(\n        &self,\n        target: crate::api::SurfaceTargetUnsafe,\n    ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {\n        let id = match target {\n            SurfaceTargetUnsafe::RawHandle {\n                raw_display_handle,\n                raw_window_handle,\n            } => unsafe {\n                self.0\n                    .instance_create_surface(raw_display_handle, raw_window_handle, None)\n            },\n\n            #[cfg(all(\n                unix,\n                not(target_vendor = \"apple\"),\n                not(target_family = \"wasm\"),\n                not(target_os = \"netbsd\")\n            ))]\n            SurfaceTargetUnsafe::Drm {\n                fd,\n                plane,\n                connector_id,\n                width,\n                height,\n                refresh_rate,\n            } => unsafe {\n                self.0.instance_create_surface_from_drm(\n                    fd,\n                    plane,\n                    connector_id,\n                    width,\n                    height,\n                    refresh_rate,\n                    None,\n                )\n            },\n\n            #[cfg(metal)]\n            SurfaceTargetUnsafe::CoreAnimationLayer(layer) => unsafe {\n                self.0.instance_create_surface_metal(layer, None)\n            },\n\n            #[cfg(target_os = \"netbsd\")]\n            SurfaceTargetUnsafe::Drm { .. } => Err(\n                wgc::instance::CreateSurfaceError::BackendNotEnabled(wgt::Backend::Vulkan),\n            ),\n\n            #[cfg(dx12)]\n            SurfaceTargetUnsafe::CompositionVisual(visual) => unsafe {\n                self.0.instance_create_surface_from_visual(visual, None)\n            },\n\n            #[cfg(dx12)]\n            SurfaceTargetUnsafe::SurfaceHandle(surface_handle) => unsafe {\n                self.0\n                    .instance_create_surface_from_surface_handle(surface_handle, None)\n            },\n\n            #[cfg(dx12)]\n            SurfaceTargetUnsafe::SwapChainPanel(swap_chain_panel) => unsafe {\n                self.0\n                    .instance_create_surface_from_swap_chain_panel(swap_chain_panel, None)\n            },\n        }?;\n\n        Ok(CoreSurface {\n            context: self.clone(),\n            id,\n            configured_device: Mutex::default(),\n            error_sink: Mutex::default(),\n        }\n        .into())\n    }\n\n    fn request_adapter(\n        &self,\n        options: &crate::api::RequestAdapterOptions<'_, '_>,\n    ) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {\n        let id = self.0.request_adapter(\n            &wgc::instance::RequestAdapterOptions {\n                power_preference: options.power_preference,\n                force_fallback_adapter: options.force_fallback_adapter,\n                compatible_surface: options\n                    .compatible_surface\n                    .map(|surface| surface.inner.as_core().id),\n            },\n            wgt::Backends::all(),\n            None,\n        );\n        let adapter = id.map(|id| {\n            let core = CoreAdapter {\n                context: self.clone(),\n                id,\n            };\n            let generic: dispatch::DispatchAdapter = core.into();\n            generic\n        });\n        Box::pin(ready(adapter))\n    }\n\n    fn poll_all_devices(&self, force_wait: bool) -> bool {\n        match self.0.poll_all_devices(force_wait) {\n            Ok(all_queue_empty) => all_queue_empty,\n            Err(err) => self.handle_error_fatal(err, \"Instance::poll_all_devices\"),\n        }\n    }\n\n    #[cfg(feature = \"wgsl\")]\n    fn wgsl_language_features(&self) -> crate::WgslLanguageFeatures {\n        use wgc::naga::front::wgsl::ImplementedLanguageExtension;\n        ImplementedLanguageExtension::all().iter().copied().fold(\n            crate::WgslLanguageFeatures::empty(),\n            |acc, wle| {\n                acc | match wle {\n                    ImplementedLanguageExtension::ReadOnlyAndReadWriteStorageTextures => {\n                        crate::WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures\n                    }\n                    ImplementedLanguageExtension::Packed4x8IntegerDotProduct => {\n                        crate::WgslLanguageFeatures::Packed4x8IntegerDotProduct\n                    }\n                    ImplementedLanguageExtension::PointerCompositeAccess => {\n                        crate::WgslLanguageFeatures::PointerCompositeAccess\n                    }\n                }\n            },\n        )\n    }\n\n    fn enumerate_adapters(\n        &self,\n        backends: crate::Backends,\n    ) -> Pin<Box<dyn dispatch::EnumerateAdapterFuture>> {\n        let adapters: Vec<DispatchAdapter> = self\n            .enumerate_adapters(backends)\n            .into_iter()\n            .map(|adapter| {\n                let core = crate::backend::wgpu_core::CoreAdapter {\n                    context: self.clone(),\n                    id: adapter,\n                };\n                core.into()\n            })\n            .collect();\n        Box::pin(ready(adapters))\n    }\n}\n\nimpl dispatch::AdapterInterface for CoreAdapter {\n    fn request_device(\n        &self,\n        desc: &crate::DeviceDescriptor<'_>,\n    ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {\n        let res = self.context.0.adapter_request_device(\n            self.id,\n            &desc.map_label(|l| l.map(Borrowed)),\n            None,\n            None,\n        );\n        let (device_id, queue_id) = match res {\n            Ok(ids) => ids,\n            Err(err) => {\n                return Box::pin(ready(Err(err.into())));\n            }\n        };\n        let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new()));\n        let device = CoreDevice {\n            context: self.context.clone(),\n            id: device_id,\n            error_sink: error_sink.clone(),\n            features: desc.required_features,\n        };\n        let queue = CoreQueue {\n            context: self.context.clone(),\n            id: queue_id,\n            error_sink,\n        };\n        Box::pin(ready(Ok((device.into(), queue.into()))))\n    }\n\n    fn is_surface_supported(&self, surface: &dispatch::DispatchSurface) -> bool {\n        let surface = surface.as_core();\n\n        self.context\n            .0\n            .adapter_is_surface_supported(self.id, surface.id)\n    }\n\n    fn features(&self) -> crate::Features {\n        self.context.0.adapter_features(self.id)\n    }\n\n    fn limits(&self) -> crate::Limits {\n        self.context.0.adapter_limits(self.id)\n    }\n\n    fn downlevel_capabilities(&self) -> crate::DownlevelCapabilities {\n        self.context.0.adapter_downlevel_capabilities(self.id)\n    }\n\n    fn get_info(&self) -> crate::AdapterInfo {\n        self.context.0.adapter_get_info(self.id)\n    }\n\n    fn get_texture_format_features(\n        &self,\n        format: crate::TextureFormat,\n    ) -> crate::TextureFormatFeatures {\n        self.context\n            .0\n            .adapter_get_texture_format_features(self.id, format)\n    }\n\n    fn get_presentation_timestamp(&self) -> crate::PresentationTimestamp {\n        self.context.0.adapter_get_presentation_timestamp(self.id)\n    }\n\n    fn cooperative_matrix_properties(&self) -> Vec<crate::wgt::CooperativeMatrixProperties> {\n        self.context\n            .0\n            .adapter_cooperative_matrix_properties(self.id)\n    }\n}\n\nimpl Drop for CoreAdapter {\n    fn drop(&mut self) {\n        self.context.0.adapter_drop(self.id)\n    }\n}\n\nimpl dispatch::DeviceInterface for CoreDevice {\n    fn features(&self) -> crate::Features {\n        self.context.0.device_features(self.id)\n    }\n\n    fn limits(&self) -> crate::Limits {\n        self.context.0.device_limits(self.id)\n    }\n\n    fn adapter_info(&self) -> crate::AdapterInfo {\n        self.context.0.device_adapter_info(self.id)\n    }\n\n    // If we have no way to create a shader module, we can't return one, and so most of the function is unreachable.\n    #[cfg_attr(\n        not(any(\n            feature = \"spirv\",\n            feature = \"glsl\",\n            feature = \"wgsl\",\n            feature = \"naga-ir\"\n        )),\n        expect(unused)\n    )]\n    fn create_shader_module(\n        &self,\n        desc: crate::ShaderModuleDescriptor<'_>,\n        shader_bound_checks: wgt::ShaderRuntimeChecks,\n    ) -> dispatch::DispatchShaderModule {\n        let descriptor = wgc::pipeline::ShaderModuleDescriptor {\n            label: desc.label.map(Borrowed),\n            runtime_checks: shader_bound_checks,\n        };\n        let source = match desc.source {\n            #[cfg(feature = \"spirv\")]\n            ShaderSource::SpirV(ref spv) => {\n                // Parse the given shader code and store its representation.\n                let options = naga::front::spv::Options {\n                    adjust_coordinate_space: false, // we require NDC_Y_UP feature\n                    strict_capabilities: true,\n                    block_ctx_dump_prefix: None,\n                };\n                wgc::pipeline::ShaderModuleSource::SpirV(Borrowed(spv), options)\n            }\n            #[cfg(feature = \"glsl\")]\n            ShaderSource::Glsl {\n                ref shader,\n                stage,\n                defines,\n            } => {\n                let options = naga::front::glsl::Options {\n                    stage,\n                    defines: defines\n                        .iter()\n                        .map(|&(key, value)| (String::from(key), String::from(value)))\n                        .collect(),\n                };\n                wgc::pipeline::ShaderModuleSource::Glsl(Borrowed(shader), options)\n            }\n            #[cfg(feature = \"wgsl\")]\n            ShaderSource::Wgsl(ref code) => wgc::pipeline::ShaderModuleSource::Wgsl(Borrowed(code)),\n            #[cfg(feature = \"naga-ir\")]\n            ShaderSource::Naga(module) => wgc::pipeline::ShaderModuleSource::Naga(module),\n            ShaderSource::Dummy(_) => panic!(\"found `ShaderSource::Dummy`\"),\n        };\n        let (id, error) =\n            self.context\n                .0\n                .device_create_shader_module(self.id, &descriptor, source, None);\n        let compilation_info = match error {\n            Some(cause) => {\n                self.context.handle_error(\n                    &self.error_sink,\n                    cause.clone(),\n                    desc.label,\n                    \"Device::create_shader_module\",\n                );\n                CompilationInfo::from(cause)\n            }\n            None => CompilationInfo { messages: vec![] },\n        };\n\n        CoreShaderModule {\n            context: self.context.clone(),\n            id,\n            compilation_info,\n        }\n        .into()\n    }\n\n    unsafe fn create_shader_module_passthrough(\n        &self,\n        desc: &crate::ShaderModuleDescriptorPassthrough<'_>,\n    ) -> dispatch::DispatchShaderModule {\n        let desc = desc.map_label(|l| l.map(Cow::from));\n        let (id, error) = unsafe {\n            self.context\n                .0\n                .device_create_shader_module_passthrough(self.id, &desc, None)\n        };\n\n        let compilation_info = match error {\n            Some(cause) => {\n                self.context.handle_error(\n                    &self.error_sink,\n                    cause.clone(),\n                    desc.label.as_deref(),\n                    \"Device::create_shader_module_passthrough\",\n                );\n                CompilationInfo::from(cause)\n            }\n            None => CompilationInfo { messages: vec![] },\n        };\n\n        CoreShaderModule {\n            context: self.context.clone(),\n            id,\n            compilation_info,\n        }\n        .into()\n    }\n\n    fn create_bind_group_layout(\n        &self,\n        desc: &crate::BindGroupLayoutDescriptor<'_>,\n    ) -> dispatch::DispatchBindGroupLayout {\n        let descriptor = wgc::binding_model::BindGroupLayoutDescriptor {\n            label: desc.label.map(Borrowed),\n            entries: Borrowed(desc.entries),\n        };\n        let (id, error) =\n            self.context\n                .0\n                .device_create_bind_group_layout(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_bind_group_layout\",\n            );\n        }\n        CoreBindGroupLayout {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_bind_group(\n        &self,\n        desc: &crate::BindGroupDescriptor<'_>,\n    ) -> dispatch::DispatchBindGroup {\n        use wgc::binding_model as bm;\n\n        let mut arrayed_texture_views = Vec::new();\n        let mut arrayed_samplers = Vec::new();\n        if self.features.contains(Features::TEXTURE_BINDING_ARRAY) {\n            // gather all the array view IDs first\n            for entry in desc.entries.iter() {\n                if let BindingResource::TextureViewArray(array) = entry.resource {\n                    arrayed_texture_views.extend(array.iter().map(|view| view.inner.as_core().id));\n                }\n                if let BindingResource::SamplerArray(array) = entry.resource {\n                    arrayed_samplers.extend(array.iter().map(|sampler| sampler.inner.as_core().id));\n                }\n            }\n        }\n        let mut remaining_arrayed_texture_views = &arrayed_texture_views[..];\n        let mut remaining_arrayed_samplers = &arrayed_samplers[..];\n\n        let mut arrayed_buffer_bindings = Vec::new();\n        if self.features.contains(Features::BUFFER_BINDING_ARRAY) {\n            // gather all the buffers first\n            for entry in desc.entries.iter() {\n                if let BindingResource::BufferArray(array) = entry.resource {\n                    arrayed_buffer_bindings.extend(array.iter().map(|binding| bm::BufferBinding {\n                        buffer: binding.buffer.inner.as_core().id,\n                        offset: binding.offset,\n                        size: binding.size.map(wgt::BufferSize::get),\n                    }));\n                }\n            }\n        }\n        let mut remaining_arrayed_buffer_bindings = &arrayed_buffer_bindings[..];\n\n        let mut arrayed_acceleration_structures = Vec::new();\n        if self\n            .features\n            .contains(Features::ACCELERATION_STRUCTURE_BINDING_ARRAY)\n        {\n            // Gather all the TLAS IDs used by TLAS arrays first (same pattern as other arrayed resources).\n            for entry in desc.entries.iter() {\n                if let BindingResource::AccelerationStructureArray(array) = entry.resource {\n                    arrayed_acceleration_structures\n                        .extend(array.iter().map(|tlas| tlas.inner.as_core().id));\n                }\n            }\n        }\n        let mut remaining_arrayed_acceleration_structures = &arrayed_acceleration_structures[..];\n\n        let entries = desc\n            .entries\n            .iter()\n            .map(|entry| bm::BindGroupEntry {\n                binding: entry.binding,\n                resource: match entry.resource {\n                    BindingResource::Buffer(BufferBinding {\n                        buffer,\n                        offset,\n                        size,\n                    }) => bm::BindingResource::Buffer(bm::BufferBinding {\n                        buffer: buffer.inner.as_core().id,\n                        offset,\n                        size: size.map(wgt::BufferSize::get),\n                    }),\n                    BindingResource::BufferArray(array) => {\n                        let slice = &remaining_arrayed_buffer_bindings[..array.len()];\n                        remaining_arrayed_buffer_bindings =\n                            &remaining_arrayed_buffer_bindings[array.len()..];\n                        bm::BindingResource::BufferArray(Borrowed(slice))\n                    }\n                    BindingResource::Sampler(sampler) => {\n                        bm::BindingResource::Sampler(sampler.inner.as_core().id)\n                    }\n                    BindingResource::SamplerArray(array) => {\n                        let slice = &remaining_arrayed_samplers[..array.len()];\n                        remaining_arrayed_samplers = &remaining_arrayed_samplers[array.len()..];\n                        bm::BindingResource::SamplerArray(Borrowed(slice))\n                    }\n                    BindingResource::TextureView(texture_view) => {\n                        bm::BindingResource::TextureView(texture_view.inner.as_core().id)\n                    }\n                    BindingResource::TextureViewArray(array) => {\n                        let slice = &remaining_arrayed_texture_views[..array.len()];\n                        remaining_arrayed_texture_views =\n                            &remaining_arrayed_texture_views[array.len()..];\n                        bm::BindingResource::TextureViewArray(Borrowed(slice))\n                    }\n                    BindingResource::AccelerationStructure(acceleration_structure) => {\n                        bm::BindingResource::AccelerationStructure(\n                            acceleration_structure.inner.as_core().id,\n                        )\n                    }\n                    BindingResource::AccelerationStructureArray(array) => {\n                        let slice = &remaining_arrayed_acceleration_structures[..array.len()];\n                        remaining_arrayed_acceleration_structures =\n                            &remaining_arrayed_acceleration_structures[array.len()..];\n                        bm::BindingResource::AccelerationStructureArray(Borrowed(slice))\n                    }\n                    BindingResource::ExternalTexture(external_texture) => {\n                        bm::BindingResource::ExternalTexture(external_texture.inner.as_core().id)\n                    }\n                },\n            })\n            .collect::<Vec<_>>();\n        let descriptor = bm::BindGroupDescriptor {\n            label: desc.label.as_ref().map(|label| Borrowed(&label[..])),\n            layout: desc.layout.inner.as_core().id,\n            entries: Borrowed(&entries),\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_bind_group(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_bind_group\",\n            );\n        }\n        CoreBindGroup {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_pipeline_layout(\n        &self,\n        desc: &crate::PipelineLayoutDescriptor<'_>,\n    ) -> dispatch::DispatchPipelineLayout {\n        // Limit is always less or equal to hal::MAX_BIND_GROUPS, so this is always right\n        // Guards following ArrayVec\n        assert!(\n            desc.bind_group_layouts.len() <= wgc::MAX_BIND_GROUPS,\n            \"Bind group layout count {} exceeds device bind group limit {}\",\n            desc.bind_group_layouts.len(),\n            wgc::MAX_BIND_GROUPS\n        );\n\n        let temp_layouts = desc\n            .bind_group_layouts\n            .iter()\n            .map(|bgl| bgl.map(|bgl| bgl.inner.as_core().id))\n            .collect::<ArrayVec<_, { wgc::MAX_BIND_GROUPS }>>();\n        let descriptor = wgc::binding_model::PipelineLayoutDescriptor {\n            label: desc.label.map(Borrowed),\n            bind_group_layouts: Borrowed(&temp_layouts),\n            immediate_size: desc.immediate_size,\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_pipeline_layout(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_pipeline_layout\",\n            );\n        }\n        CorePipelineLayout {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_render_pipeline(\n        &self,\n        desc: &crate::RenderPipelineDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPipeline {\n        use wgc::pipeline as pipe;\n\n        let vertex_buffers: ArrayVec<_, { wgc::MAX_VERTEX_BUFFERS }> = desc\n            .vertex\n            .buffers\n            .iter()\n            .map(|vbuf| pipe::VertexBufferLayout {\n                array_stride: vbuf.array_stride,\n                step_mode: vbuf.step_mode,\n                attributes: Borrowed(vbuf.attributes),\n            })\n            .collect();\n\n        let vert_constants = desc\n            .vertex\n            .compilation_options\n            .constants\n            .iter()\n            .map(|&(key, value)| (String::from(key), value))\n            .collect();\n\n        let descriptor = pipe::RenderPipelineDescriptor {\n            label: desc.label.map(Borrowed),\n            layout: desc.layout.map(|layout| layout.inner.as_core().id),\n            vertex: pipe::VertexState {\n                stage: pipe::ProgrammableStageDescriptor {\n                    module: desc.vertex.module.inner.as_core().id,\n                    entry_point: desc.vertex.entry_point.map(Borrowed),\n                    constants: vert_constants,\n                    zero_initialize_workgroup_memory: desc\n                        .vertex\n                        .compilation_options\n                        .zero_initialize_workgroup_memory,\n                },\n                buffers: Borrowed(&vertex_buffers),\n            },\n            primitive: desc.primitive,\n            depth_stencil: desc.depth_stencil.clone(),\n            multisample: desc.multisample,\n            fragment: desc.fragment.as_ref().map(|frag| {\n                let frag_constants = frag\n                    .compilation_options\n                    .constants\n                    .iter()\n                    .map(|&(key, value)| (String::from(key), value))\n                    .collect();\n                pipe::FragmentState {\n                    stage: pipe::ProgrammableStageDescriptor {\n                        module: frag.module.inner.as_core().id,\n                        entry_point: frag.entry_point.map(Borrowed),\n                        constants: frag_constants,\n                        zero_initialize_workgroup_memory: frag\n                            .compilation_options\n                            .zero_initialize_workgroup_memory,\n                    },\n                    targets: Borrowed(frag.targets),\n                }\n            }),\n            multiview_mask: desc.multiview_mask,\n            cache: desc.cache.map(|cache| cache.inner.as_core().id),\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_render_pipeline(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            if let wgc::pipeline::CreateRenderPipelineError::Internal { stage, ref error } = cause {\n                log::error!(\"Shader translation error for stage {stage:?}: {error}\");\n                log::error!(\"Please report it to https://github.com/gfx-rs/wgpu\");\n            }\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_render_pipeline\",\n            );\n        }\n        CoreRenderPipeline {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_mesh_pipeline(\n        &self,\n        desc: &crate::MeshPipelineDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPipeline {\n        use wgc::pipeline as pipe;\n\n        let mesh_constants = desc\n            .mesh\n            .compilation_options\n            .constants\n            .iter()\n            .map(|&(key, value)| (String::from(key), value))\n            .collect();\n        let descriptor = pipe::MeshPipelineDescriptor {\n            label: desc.label.map(Borrowed),\n            task: desc.task.as_ref().map(|task| {\n                let task_constants = task\n                    .compilation_options\n                    .constants\n                    .iter()\n                    .map(|&(key, value)| (String::from(key), value))\n                    .collect();\n                pipe::TaskState {\n                    stage: pipe::ProgrammableStageDescriptor {\n                        module: task.module.inner.as_core().id,\n                        entry_point: task.entry_point.map(Borrowed),\n                        constants: task_constants,\n                        zero_initialize_workgroup_memory: desc\n                            .mesh\n                            .compilation_options\n                            .zero_initialize_workgroup_memory,\n                    },\n                }\n            }),\n            mesh: pipe::MeshState {\n                stage: pipe::ProgrammableStageDescriptor {\n                    module: desc.mesh.module.inner.as_core().id,\n                    entry_point: desc.mesh.entry_point.map(Borrowed),\n                    constants: mesh_constants,\n                    zero_initialize_workgroup_memory: desc\n                        .mesh\n                        .compilation_options\n                        .zero_initialize_workgroup_memory,\n                },\n            },\n            layout: desc.layout.map(|layout| layout.inner.as_core().id),\n            primitive: desc.primitive,\n            depth_stencil: desc.depth_stencil.clone(),\n            multisample: desc.multisample,\n            fragment: desc.fragment.as_ref().map(|frag| {\n                let frag_constants = frag\n                    .compilation_options\n                    .constants\n                    .iter()\n                    .map(|&(key, value)| (String::from(key), value))\n                    .collect();\n                pipe::FragmentState {\n                    stage: pipe::ProgrammableStageDescriptor {\n                        module: frag.module.inner.as_core().id,\n                        entry_point: frag.entry_point.map(Borrowed),\n                        constants: frag_constants,\n                        zero_initialize_workgroup_memory: frag\n                            .compilation_options\n                            .zero_initialize_workgroup_memory,\n                    },\n                    targets: Borrowed(frag.targets),\n                }\n            }),\n            multiview: desc.multiview,\n            cache: desc.cache.map(|cache| cache.inner.as_core().id),\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_mesh_pipeline(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            if let wgc::pipeline::CreateRenderPipelineError::Internal { stage, ref error } = cause {\n                log::error!(\"Shader translation error for stage {stage:?}: {error}\");\n                log::error!(\"Please report it to https://github.com/gfx-rs/wgpu\");\n            }\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_render_pipeline\",\n            );\n        }\n        CoreRenderPipeline {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_compute_pipeline(\n        &self,\n        desc: &crate::ComputePipelineDescriptor<'_>,\n    ) -> dispatch::DispatchComputePipeline {\n        use wgc::pipeline as pipe;\n\n        let constants = desc\n            .compilation_options\n            .constants\n            .iter()\n            .map(|&(key, value)| (String::from(key), value))\n            .collect();\n\n        let descriptor = pipe::ComputePipelineDescriptor {\n            label: desc.label.map(Borrowed),\n            layout: desc.layout.map(|pll| pll.inner.as_core().id),\n            stage: pipe::ProgrammableStageDescriptor {\n                module: desc.module.inner.as_core().id,\n                entry_point: desc.entry_point.map(Borrowed),\n                constants,\n                zero_initialize_workgroup_memory: desc\n                    .compilation_options\n                    .zero_initialize_workgroup_memory,\n            },\n            cache: desc.cache.map(|cache| cache.inner.as_core().id),\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_compute_pipeline(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            if let wgc::pipeline::CreateComputePipelineError::Internal(ref error) = cause {\n                log::error!(\n                    \"Shader translation error for stage {:?}: {}\",\n                    wgt::ShaderStages::COMPUTE,\n                    error\n                );\n                log::error!(\"Please report it to https://github.com/gfx-rs/wgpu\");\n            }\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_compute_pipeline\",\n            );\n        }\n        CoreComputePipeline {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    unsafe fn create_pipeline_cache(\n        &self,\n        desc: &crate::PipelineCacheDescriptor<'_>,\n    ) -> dispatch::DispatchPipelineCache {\n        use wgc::pipeline as pipe;\n\n        let descriptor = pipe::PipelineCacheDescriptor {\n            label: desc.label.map(Borrowed),\n            data: desc.data.map(Borrowed),\n            fallback: desc.fallback,\n        };\n        let (id, error) = unsafe {\n            self.context\n                .0\n                .device_create_pipeline_cache(self.id, &descriptor, None)\n        };\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::device_create_pipeline_cache_init\",\n            );\n        }\n        CorePipelineCache {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> dispatch::DispatchBuffer {\n        let (id, error) = self.context.0.device_create_buffer(\n            self.id,\n            &desc.map_label(|l| l.map(Borrowed)),\n            None,\n        );\n        if let Some(cause) = error {\n            self.context\n                .handle_error(&self.error_sink, cause, desc.label, \"Device::create_buffer\");\n        }\n\n        CoreBuffer {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture {\n        let wgt_desc = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec());\n        let (id, error) = self\n            .context\n            .0\n            .device_create_texture(self.id, &wgt_desc, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_texture\",\n            );\n        }\n\n        CoreTexture {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_external_texture(\n        &self,\n        desc: &crate::ExternalTextureDescriptor<'_>,\n        planes: &[&crate::TextureView],\n    ) -> dispatch::DispatchExternalTexture {\n        let wgt_desc = desc.map_label(|l| l.map(Borrowed));\n        let planes = planes\n            .iter()\n            .map(|plane| plane.inner.as_core().id)\n            .collect::<Vec<_>>();\n        let (id, error) = self\n            .context\n            .0\n            .device_create_external_texture(self.id, &wgt_desc, &planes, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_external_texture\",\n            );\n        }\n\n        CoreExternalTexture {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_blas(\n        &self,\n        desc: &crate::CreateBlasDescriptor<'_>,\n        sizes: crate::BlasGeometrySizeDescriptors,\n    ) -> (Option<u64>, dispatch::DispatchBlas) {\n        let global = &self.context.0;\n        let (id, handle, error) =\n            global.device_create_blas(self.id, &desc.map_label(|l| l.map(Borrowed)), sizes, None);\n        if let Some(cause) = error {\n            self.context\n                .handle_error(&self.error_sink, cause, desc.label, \"Device::create_blas\");\n        }\n        (\n            handle,\n            CoreBlas {\n                context: self.context.clone(),\n                id,\n                error_sink: Arc::clone(&self.error_sink),\n            }\n            .into(),\n        )\n    }\n\n    fn create_tlas(&self, desc: &crate::CreateTlasDescriptor<'_>) -> dispatch::DispatchTlas {\n        let global = &self.context.0;\n        let (id, error) =\n            global.device_create_tlas(self.id, &desc.map_label(|l| l.map(Borrowed)), None);\n        if let Some(cause) = error {\n            self.context\n                .handle_error(&self.error_sink, cause, desc.label, \"Device::create_tlas\");\n        }\n        CoreTlas {\n            context: self.context.clone(),\n            id,\n            // error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_sampler(&self, desc: &crate::SamplerDescriptor<'_>) -> dispatch::DispatchSampler {\n        let descriptor = wgc::resource::SamplerDescriptor {\n            label: desc.label.map(Borrowed),\n            address_modes: [\n                desc.address_mode_u,\n                desc.address_mode_v,\n                desc.address_mode_w,\n            ],\n            mag_filter: desc.mag_filter,\n            min_filter: desc.min_filter,\n            mipmap_filter: desc.mipmap_filter,\n            lod_min_clamp: desc.lod_min_clamp,\n            lod_max_clamp: desc.lod_max_clamp,\n            compare: desc.compare,\n            anisotropy_clamp: desc.anisotropy_clamp,\n            border_color: desc.border_color,\n        };\n\n        let (id, error) = self\n            .context\n            .0\n            .device_create_sampler(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_sampler\",\n            );\n        }\n        CoreSampler {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_query_set(&self, desc: &crate::QuerySetDescriptor<'_>) -> dispatch::DispatchQuerySet {\n        let (id, error) = self.context.0.device_create_query_set(\n            self.id,\n            &desc.map_label(|l| l.map(Borrowed)),\n            None,\n        );\n        if let Some(cause) = error {\n            self.context\n                .handle_error_nolabel(&self.error_sink, cause, \"Device::create_query_set\");\n        }\n        CoreQuerySet {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn create_command_encoder(\n        &self,\n        desc: &crate::CommandEncoderDescriptor<'_>,\n    ) -> dispatch::DispatchCommandEncoder {\n        let (id, error) = self.context.0.device_create_command_encoder(\n            self.id,\n            &desc.map_label(|l| l.map(Borrowed)),\n            None,\n        );\n        if let Some(cause) = error {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"Device::create_command_encoder\",\n            );\n        }\n\n        CoreCommandEncoder {\n            context: self.context.clone(),\n            id,\n            error_sink: Arc::clone(&self.error_sink),\n        }\n        .into()\n    }\n\n    fn create_render_bundle_encoder(\n        &self,\n        desc: &crate::RenderBundleEncoderDescriptor<'_>,\n    ) -> dispatch::DispatchRenderBundleEncoder {\n        let descriptor = wgc::command::RenderBundleEncoderDescriptor {\n            label: desc.label.map(Borrowed),\n            color_formats: Borrowed(desc.color_formats),\n            depth_stencil: desc.depth_stencil,\n            sample_count: desc.sample_count,\n            multiview: desc.multiview,\n        };\n        let encoder = match wgc::command::RenderBundleEncoder::new(&descriptor, self.id) {\n            Ok(encoder) => encoder,\n            Err(e) => panic!(\"Error in Device::create_render_bundle_encoder: {e}\"),\n        };\n\n        CoreRenderBundleEncoder {\n            context: self.context.clone(),\n            encoder,\n            id: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn set_device_lost_callback(&self, device_lost_callback: dispatch::BoxDeviceLostCallback) {\n        self.context\n            .0\n            .device_set_device_lost_closure(self.id, device_lost_callback);\n    }\n\n    fn on_uncaptured_error(&self, handler: Arc<dyn crate::UncapturedErrorHandler>) {\n        let mut error_sink = self.error_sink.lock();\n        error_sink.uncaptured_handler = Some(handler);\n    }\n\n    fn push_error_scope(&self, filter: crate::ErrorFilter) -> u32 {\n        let mut error_sink = self.error_sink.lock();\n        let thread_id = thread_id::ThreadId::current();\n        let scopes = error_sink.scopes.entry(thread_id).or_default();\n        let index = scopes\n            .len()\n            .try_into()\n            .expect(\"Greater than 2^32 nested error scopes\");\n        scopes.push(ErrorScope {\n            error: None,\n            filter,\n        });\n        index\n    }\n\n    fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn dispatch::PopErrorScopeFuture>> {\n        let mut error_sink = self.error_sink.lock();\n\n        // We go out of our way to avoid panicking while unwinding, because that would abort the process,\n        // and we are supposed to just drop the error scope on the floor.\n        let is_panicking = crate::util::is_panicking();\n        let thread_id = thread_id::ThreadId::current();\n        let err = \"Mismatched pop_error_scope call: no error scope for this thread. Error scopes are thread-local.\";\n        let scopes = match error_sink.scopes.get_mut(&thread_id) {\n            Some(s) => s,\n            None => {\n                if !is_panicking {\n                    panic!(\"{err}\");\n                } else {\n                    return Box::pin(ready(None));\n                }\n            }\n        };\n        if scopes.is_empty() && !is_panicking {\n            panic!(\"{err}\");\n        }\n        if index as usize != scopes.len() - 1 && !is_panicking {\n            panic!(\n                \"Mismatched pop_error_scope call: error scopes must be popped in reverse order.\"\n            );\n        }\n\n        // It would be more correct in this case to use `remove` here so that when unwinding is occurring\n        // we would remove the correct error scope, but we don't have such a primitive on the web\n        // and having consistent behavior here is more important. If you are unwinding and it unwinds\n        // the guards in the wrong order, it's totally reasonable to have incorrect behavior.\n        let scope = match scopes.pop() {\n            Some(s) => s,\n            None if !is_panicking => unreachable!(),\n            None => return Box::pin(ready(None)),\n        };\n\n        Box::pin(ready(scope.error))\n    }\n\n    unsafe fn start_graphics_debugger_capture(&self) {\n        unsafe {\n            self.context\n                .0\n                .device_start_graphics_debugger_capture(self.id)\n        };\n    }\n\n    unsafe fn stop_graphics_debugger_capture(&self) {\n        unsafe {\n            self.context\n                .0\n                .device_stop_graphics_debugger_capture(self.id)\n        };\n    }\n\n    fn poll(&self, poll_type: wgt::PollType<u64>) -> Result<crate::PollStatus, crate::PollError> {\n        match self.context.0.device_poll(self.id, poll_type) {\n            Ok(status) => Ok(status),\n            Err(err) => {\n                if let Some(poll_error) = err.to_poll_error() {\n                    return Err(poll_error);\n                }\n\n                self.context.handle_error_fatal(err, \"Device::poll\")\n            }\n        }\n    }\n\n    fn get_internal_counters(&self) -> crate::InternalCounters {\n        self.context.0.device_get_internal_counters(self.id)\n    }\n\n    fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {\n        self.context.0.device_generate_allocator_report(self.id)\n    }\n\n    fn destroy(&self) {\n        self.context.0.device_destroy(self.id);\n    }\n}\n\nimpl Drop for CoreDevice {\n    fn drop(&mut self) {\n        self.context.0.device_drop(self.id)\n    }\n}\n\nimpl dispatch::QueueInterface for CoreQueue {\n    fn write_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        data: &[u8],\n    ) {\n        let buffer = buffer.as_core();\n\n        match self\n            .context\n            .0\n            .queue_write_buffer(self.id, buffer.id, offset, data)\n        {\n            Ok(()) => (),\n            Err(err) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, err, \"Queue::write_buffer\")\n            }\n        }\n    }\n\n    fn create_staging_buffer(\n        &self,\n        size: crate::BufferSize,\n    ) -> Option<dispatch::DispatchQueueWriteBuffer> {\n        match self\n            .context\n            .0\n            .queue_create_staging_buffer(self.id, size, None)\n        {\n            Ok((buffer_id, ptr)) => Some(\n                CoreQueueWriteBuffer {\n                    buffer_id,\n                    mapping: CoreBufferMappedRange {\n                        ptr,\n                        size: size.get() as usize,\n                    },\n                }\n                .into(),\n            ),\n            Err(err) => {\n                self.context.handle_error_nolabel(\n                    &self.error_sink,\n                    err,\n                    \"Queue::write_buffer_with\",\n                );\n                None\n            }\n        }\n    }\n\n    fn validate_write_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: wgt::BufferAddress,\n        size: wgt::BufferSize,\n    ) -> Option<()> {\n        let buffer = buffer.as_core();\n\n        match self\n            .context\n            .0\n            .queue_validate_write_buffer(self.id, buffer.id, offset, size)\n        {\n            Ok(()) => Some(()),\n            Err(err) => {\n                self.context.handle_error_nolabel(\n                    &self.error_sink,\n                    err,\n                    \"Queue::write_buffer_with\",\n                );\n                None\n            }\n        }\n    }\n\n    fn write_staging_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        staging_buffer: &dispatch::DispatchQueueWriteBuffer,\n    ) {\n        let buffer = buffer.as_core();\n        let staging_buffer = staging_buffer.as_core();\n\n        match self.context.0.queue_write_staging_buffer(\n            self.id,\n            buffer.id,\n            offset,\n            staging_buffer.buffer_id,\n        ) {\n            Ok(()) => (),\n            Err(err) => {\n                self.context.handle_error_nolabel(\n                    &self.error_sink,\n                    err,\n                    \"Queue::write_buffer_with\",\n                );\n            }\n        }\n    }\n\n    fn write_texture(\n        &self,\n        texture: crate::TexelCopyTextureInfo<'_>,\n        data: &[u8],\n        data_layout: crate::TexelCopyBufferLayout,\n        size: crate::Extent3d,\n    ) {\n        match self.context.0.queue_write_texture(\n            self.id,\n            &map_texture_copy_view(texture),\n            data,\n            &data_layout,\n            &size,\n        ) {\n            Ok(()) => (),\n            Err(err) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, err, \"Queue::write_texture\")\n            }\n        }\n    }\n\n    // This method needs to exist if either webgpu or webgl is enabled,\n    // but we only actually have an implementation if webgl is enabled.\n    #[cfg(web)]\n    #[cfg_attr(not(webgl), expect(unused_variables))]\n    fn copy_external_image_to_texture(\n        &self,\n        source: &crate::CopyExternalImageSourceInfo,\n        dest: crate::CopyExternalImageDestInfo<&crate::api::Texture>,\n        size: crate::Extent3d,\n    ) {\n        #[cfg(webgl)]\n        match self.context.0.queue_copy_external_image_to_texture(\n            self.id,\n            source,\n            map_texture_tagged_copy_view(dest),\n            size,\n        ) {\n            Ok(()) => (),\n            Err(err) => self.context.handle_error_nolabel(\n                &self.error_sink,\n                err,\n                \"Queue::copy_external_image_to_texture\",\n            ),\n        }\n    }\n\n    fn submit(\n        &self,\n        command_buffers: &mut dyn Iterator<Item = dispatch::DispatchCommandBuffer>,\n    ) -> u64 {\n        let temp_command_buffers = command_buffers.collect::<SmallVec<[_; 4]>>();\n        let command_buffer_ids = temp_command_buffers\n            .iter()\n            .map(|cmdbuf| cmdbuf.as_core().id)\n            .collect::<SmallVec<[_; 4]>>();\n\n        let index = match self.context.0.queue_submit(self.id, &command_buffer_ids) {\n            Ok(index) => index,\n            Err((index, err)) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, err, \"Queue::submit\");\n                index\n            }\n        };\n\n        drop(temp_command_buffers);\n\n        index\n    }\n\n    fn get_timestamp_period(&self) -> f32 {\n        self.context.0.queue_get_timestamp_period(self.id)\n    }\n\n    fn on_submitted_work_done(&self, callback: dispatch::BoxSubmittedWorkDoneCallback) {\n        self.context\n            .0\n            .queue_on_submitted_work_done(self.id, callback);\n    }\n\n    fn compact_blas(&self, blas: &dispatch::DispatchBlas) -> (Option<u64>, dispatch::DispatchBlas) {\n        let (id, handle, error) =\n            self.context\n                .0\n                .queue_compact_blas(self.id, blas.as_core().id, None);\n\n        if let Some(cause) = error {\n            self.context\n                .handle_error_nolabel(&self.error_sink, cause, \"Queue::compact_blas\");\n        }\n        (\n            handle,\n            CoreBlas {\n                context: self.context.clone(),\n                id,\n                error_sink: Arc::clone(&self.error_sink),\n            }\n            .into(),\n        )\n    }\n}\n\nimpl Drop for CoreQueue {\n    fn drop(&mut self) {\n        self.context.0.queue_drop(self.id)\n    }\n}\n\nimpl dispatch::ShaderModuleInterface for CoreShaderModule {\n    fn get_compilation_info(&self) -> Pin<Box<dyn dispatch::ShaderCompilationInfoFuture>> {\n        Box::pin(ready(self.compilation_info.clone()))\n    }\n}\n\nimpl Drop for CoreShaderModule {\n    fn drop(&mut self) {\n        self.context.0.shader_module_drop(self.id)\n    }\n}\n\nimpl dispatch::BindGroupLayoutInterface for CoreBindGroupLayout {}\n\nimpl Drop for CoreBindGroupLayout {\n    fn drop(&mut self) {\n        self.context.0.bind_group_layout_drop(self.id)\n    }\n}\n\nimpl dispatch::BindGroupInterface for CoreBindGroup {}\n\nimpl Drop for CoreBindGroup {\n    fn drop(&mut self) {\n        self.context.0.bind_group_drop(self.id)\n    }\n}\n\nimpl dispatch::TextureViewInterface for CoreTextureView {}\n\nimpl Drop for CoreTextureView {\n    fn drop(&mut self) {\n        self.context.0.texture_view_drop(self.id);\n    }\n}\n\nimpl dispatch::ExternalTextureInterface for CoreExternalTexture {\n    fn destroy(&self) {\n        self.context.0.external_texture_destroy(self.id);\n    }\n}\n\nimpl Drop for CoreExternalTexture {\n    fn drop(&mut self) {\n        self.context.0.external_texture_drop(self.id);\n    }\n}\n\nimpl dispatch::SamplerInterface for CoreSampler {}\n\nimpl Drop for CoreSampler {\n    fn drop(&mut self) {\n        self.context.0.sampler_drop(self.id)\n    }\n}\n\nimpl dispatch::BufferInterface for CoreBuffer {\n    fn map_async(\n        &self,\n        mode: crate::MapMode,\n        range: Range<crate::BufferAddress>,\n        callback: dispatch::BufferMapCallback,\n    ) {\n        let operation = wgc::resource::BufferMapOperation {\n            host: match mode {\n                MapMode::Read => wgc::device::HostMap::Read,\n                MapMode::Write => wgc::device::HostMap::Write,\n            },\n            callback: Some(Box::new(|status| {\n                let res = status.map_err(|_| crate::BufferAsyncError);\n                callback(res);\n            })),\n        };\n\n        match self.context.0.buffer_map_async(\n            self.id,\n            range.start,\n            Some(range.end - range.start),\n            operation,\n        ) {\n            Ok(_) => (),\n            Err(cause) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, cause, \"Buffer::map_async\")\n            }\n        }\n    }\n\n    fn get_mapped_range(\n        &self,\n        sub_range: Range<crate::BufferAddress>,\n    ) -> dispatch::DispatchBufferMappedRange {\n        let size = sub_range.end - sub_range.start;\n        match self\n            .context\n            .0\n            .buffer_get_mapped_range(self.id, sub_range.start, Some(size))\n        {\n            Ok((ptr, size)) => CoreBufferMappedRange {\n                ptr,\n                size: size as usize,\n            }\n            .into(),\n            Err(err) => self\n                .context\n                .handle_error_fatal(err, \"Buffer::get_mapped_range\"),\n        }\n    }\n\n    fn unmap(&self) {\n        match self.context.0.buffer_unmap(self.id) {\n            Ok(()) => (),\n            Err(cause) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, cause, \"Buffer::buffer_unmap\")\n            }\n        }\n    }\n\n    fn destroy(&self) {\n        self.context.0.buffer_destroy(self.id);\n    }\n}\n\nimpl Drop for CoreBuffer {\n    fn drop(&mut self) {\n        self.context.0.buffer_drop(self.id)\n    }\n}\n\nimpl dispatch::TextureInterface for CoreTexture {\n    fn create_view(\n        &self,\n        desc: &crate::TextureViewDescriptor<'_>,\n    ) -> dispatch::DispatchTextureView {\n        let descriptor = wgc::resource::TextureViewDescriptor {\n            label: desc.label.map(Borrowed),\n            format: desc.format,\n            dimension: desc.dimension,\n            usage: desc.usage,\n            range: wgt::ImageSubresourceRange {\n                aspect: desc.aspect,\n                base_mip_level: desc.base_mip_level,\n                mip_level_count: desc.mip_level_count,\n                base_array_layer: desc.base_array_layer,\n                array_layer_count: desc.array_layer_count,\n            },\n        };\n        let (id, error) = self\n            .context\n            .0\n            .texture_create_view(self.id, &descriptor, None);\n        if let Some(cause) = error {\n            self.context\n                .handle_error(&self.error_sink, cause, desc.label, \"Texture::create_view\");\n        }\n        CoreTextureView {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn destroy(&self) {\n        self.context.0.texture_destroy(self.id);\n    }\n}\n\nimpl Drop for CoreTexture {\n    fn drop(&mut self) {\n        self.context.0.texture_drop(self.id)\n    }\n}\n\nimpl dispatch::BlasInterface for CoreBlas {\n    fn prepare_compact_async(&self, callback: BlasCompactCallback) {\n        let callback: Option<wgc::resource::BlasCompactCallback> =\n            Some(Box::new(|status: BlasPrepareCompactResult| {\n                let res = status.map_err(|_| crate::BlasAsyncError);\n                callback(res);\n            }));\n\n        match self.context.0.blas_prepare_compact_async(self.id, callback) {\n            Ok(_) => (),\n            Err(cause) => self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"Blas::prepare_compact_async\",\n            ),\n        }\n    }\n\n    fn ready_for_compaction(&self) -> bool {\n        match self.context.0.ready_for_compaction(self.id) {\n            Ok(ready) => ready,\n            Err(cause) => {\n                self.context.handle_error_nolabel(\n                    &self.error_sink,\n                    cause,\n                    \"Blas::ready_for_compaction\",\n                );\n                // A BLAS is definitely not ready for compaction if it's not valid\n                false\n            }\n        }\n    }\n}\n\nimpl Drop for CoreBlas {\n    fn drop(&mut self) {\n        self.context.0.blas_drop(self.id)\n    }\n}\n\nimpl dispatch::TlasInterface for CoreTlas {}\n\nimpl Drop for CoreTlas {\n    fn drop(&mut self) {\n        self.context.0.tlas_drop(self.id)\n    }\n}\n\nimpl dispatch::QuerySetInterface for CoreQuerySet {}\n\nimpl Drop for CoreQuerySet {\n    fn drop(&mut self) {\n        self.context.0.query_set_drop(self.id)\n    }\n}\n\nimpl dispatch::PipelineLayoutInterface for CorePipelineLayout {}\n\nimpl Drop for CorePipelineLayout {\n    fn drop(&mut self) {\n        self.context.0.pipeline_layout_drop(self.id)\n    }\n}\n\nimpl dispatch::RenderPipelineInterface for CoreRenderPipeline {\n    fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {\n        let (id, error) = self\n            .context\n            .0\n            .render_pipeline_get_bind_group_layout(self.id, index, None);\n        if let Some(err) = error {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                err,\n                \"RenderPipeline::get_bind_group_layout\",\n            )\n        }\n        CoreBindGroupLayout {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n}\n\nimpl Drop for CoreRenderPipeline {\n    fn drop(&mut self) {\n        self.context.0.render_pipeline_drop(self.id)\n    }\n}\n\nimpl dispatch::ComputePipelineInterface for CoreComputePipeline {\n    fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {\n        let (id, error) = self\n            .context\n            .0\n            .compute_pipeline_get_bind_group_layout(self.id, index, None);\n        if let Some(err) = error {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                err,\n                \"ComputePipeline::get_bind_group_layout\",\n            )\n        }\n        CoreBindGroupLayout {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n}\n\nimpl Drop for CoreComputePipeline {\n    fn drop(&mut self) {\n        self.context.0.compute_pipeline_drop(self.id)\n    }\n}\n\nimpl dispatch::PipelineCacheInterface for CorePipelineCache {\n    fn get_data(&self) -> Option<Vec<u8>> {\n        self.context.0.pipeline_cache_get_data(self.id)\n    }\n}\n\nimpl Drop for CorePipelineCache {\n    fn drop(&mut self) {\n        self.context.0.pipeline_cache_drop(self.id)\n    }\n}\n\nimpl dispatch::CommandEncoderInterface for CoreCommandEncoder {\n    fn copy_buffer_to_buffer(\n        &self,\n        source: &dispatch::DispatchBuffer,\n        source_offset: crate::BufferAddress,\n        destination: &dispatch::DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n        copy_size: Option<crate::BufferAddress>,\n    ) {\n        let source = source.as_core();\n        let destination = destination.as_core();\n\n        if let Err(cause) = self.context.0.command_encoder_copy_buffer_to_buffer(\n            self.id,\n            source.id,\n            source_offset,\n            destination.id,\n            destination_offset,\n            copy_size,\n        ) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::copy_buffer_to_buffer\",\n            );\n        }\n    }\n\n    fn copy_buffer_to_texture(\n        &self,\n        source: crate::TexelCopyBufferInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        if let Err(cause) = self.context.0.command_encoder_copy_buffer_to_texture(\n            self.id,\n            &map_buffer_copy_view(source),\n            &map_texture_copy_view(destination),\n            &copy_size,\n        ) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::copy_buffer_to_texture\",\n            );\n        }\n    }\n\n    fn copy_texture_to_buffer(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyBufferInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        if let Err(cause) = self.context.0.command_encoder_copy_texture_to_buffer(\n            self.id,\n            &map_texture_copy_view(source),\n            &map_buffer_copy_view(destination),\n            &copy_size,\n        ) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::copy_texture_to_buffer\",\n            );\n        }\n    }\n\n    fn copy_texture_to_texture(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    ) {\n        if let Err(cause) = self.context.0.command_encoder_copy_texture_to_texture(\n            self.id,\n            &map_texture_copy_view(source),\n            &map_texture_copy_view(destination),\n            &copy_size,\n        ) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::copy_texture_to_texture\",\n            );\n        }\n    }\n\n    fn begin_compute_pass(\n        &self,\n        desc: &crate::ComputePassDescriptor<'_>,\n    ) -> dispatch::DispatchComputePass {\n        let timestamp_writes =\n            desc.timestamp_writes\n                .as_ref()\n                .map(|tw| wgc::command::PassTimestampWrites {\n                    query_set: tw.query_set.inner.as_core().id,\n                    beginning_of_pass_write_index: tw.beginning_of_pass_write_index,\n                    end_of_pass_write_index: tw.end_of_pass_write_index,\n                });\n\n        let (pass, err) = self.context.0.command_encoder_begin_compute_pass(\n            self.id,\n            &wgc::command::ComputePassDescriptor {\n                label: desc.label.map(Borrowed),\n                timestamp_writes,\n            },\n        );\n\n        if let Some(cause) = err {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"CommandEncoder::begin_compute_pass\",\n            );\n        }\n\n        CoreComputePass {\n            context: self.context.clone(),\n            pass,\n            error_sink: self.error_sink.clone(),\n            id: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn begin_render_pass(\n        &self,\n        desc: &crate::RenderPassDescriptor<'_>,\n    ) -> dispatch::DispatchRenderPass {\n        let colors = desc\n            .color_attachments\n            .iter()\n            .map(|ca| {\n                ca.as_ref()\n                    .map(|at| wgc::command::RenderPassColorAttachment {\n                        view: at.view.inner.as_core().id,\n                        depth_slice: at.depth_slice,\n                        resolve_target: at.resolve_target.map(|view| view.inner.as_core().id),\n                        load_op: at.ops.load,\n                        store_op: at.ops.store,\n                    })\n            })\n            .collect::<Vec<_>>();\n\n        let depth_stencil = desc.depth_stencil_attachment.as_ref().map(|dsa| {\n            wgc::command::RenderPassDepthStencilAttachment {\n                view: dsa.view.inner.as_core().id,\n                depth: map_pass_channel(dsa.depth_ops.as_ref()),\n                stencil: map_pass_channel(dsa.stencil_ops.as_ref()),\n            }\n        });\n\n        let timestamp_writes =\n            desc.timestamp_writes\n                .as_ref()\n                .map(|tw| wgc::command::PassTimestampWrites {\n                    query_set: tw.query_set.inner.as_core().id,\n                    beginning_of_pass_write_index: tw.beginning_of_pass_write_index,\n                    end_of_pass_write_index: tw.end_of_pass_write_index,\n                });\n\n        let (pass, err) = self.context.0.command_encoder_begin_render_pass(\n            self.id,\n            &wgc::command::RenderPassDescriptor {\n                label: desc.label.map(Borrowed),\n                timestamp_writes: timestamp_writes.as_ref(),\n                color_attachments: Borrowed(&colors),\n                depth_stencil_attachment: depth_stencil.as_ref(),\n                occlusion_query_set: desc.occlusion_query_set.map(|qs| qs.inner.as_core().id),\n                multiview_mask: desc.multiview_mask,\n            },\n        );\n\n        if let Some(cause) = err {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                desc.label,\n                \"CommandEncoder::begin_render_pass\",\n            );\n        }\n\n        CoreRenderPass {\n            context: self.context.clone(),\n            pass,\n            error_sink: self.error_sink.clone(),\n            id: crate::cmp::Identifier::create(),\n        }\n        .into()\n    }\n\n    fn finish(&mut self) -> dispatch::DispatchCommandBuffer {\n        let descriptor = wgt::CommandBufferDescriptor::default();\n        let (id, opt_label_and_error) =\n            self.context\n                .0\n                .command_encoder_finish(self.id, &descriptor, None);\n        if let Some((label, cause)) = opt_label_and_error {\n            self.context\n                .handle_error(&self.error_sink, cause, Some(&label), \"a CommandEncoder\");\n        }\n        CoreCommandBuffer {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n\n    fn clear_texture(\n        &self,\n        texture: &dispatch::DispatchTexture,\n        subresource_range: &crate::ImageSubresourceRange,\n    ) {\n        let texture = texture.as_core();\n\n        if let Err(cause) =\n            self.context\n                .0\n                .command_encoder_clear_texture(self.id, texture.id, subresource_range)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::clear_texture\",\n            );\n        }\n    }\n\n    fn clear_buffer(\n        &self,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferAddress>,\n    ) {\n        let buffer = buffer.as_core();\n\n        if let Err(cause) = self\n            .context\n            .0\n            .command_encoder_clear_buffer(self.id, buffer.id, offset, size)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::fill_buffer\",\n            );\n        }\n    }\n\n    fn insert_debug_marker(&self, label: &str) {\n        if let Err(cause) = self\n            .context\n            .0\n            .command_encoder_insert_debug_marker(self.id, label)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::insert_debug_marker\",\n            );\n        }\n    }\n\n    fn push_debug_group(&self, label: &str) {\n        if let Err(cause) = self\n            .context\n            .0\n            .command_encoder_push_debug_group(self.id, label)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::push_debug_group\",\n            );\n        }\n    }\n\n    fn pop_debug_group(&self) {\n        if let Err(cause) = self.context.0.command_encoder_pop_debug_group(self.id) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::pop_debug_group\",\n            );\n        }\n    }\n\n    fn write_timestamp(&self, query_set: &dispatch::DispatchQuerySet, query_index: u32) {\n        let query_set = query_set.as_core();\n\n        if let Err(cause) =\n            self.context\n                .0\n                .command_encoder_write_timestamp(self.id, query_set.id, query_index)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::write_timestamp\",\n            );\n        }\n    }\n\n    fn resolve_query_set(\n        &self,\n        query_set: &dispatch::DispatchQuerySet,\n        first_query: u32,\n        query_count: u32,\n        destination: &dispatch::DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n    ) {\n        let query_set = query_set.as_core();\n        let destination = destination.as_core();\n\n        if let Err(cause) = self.context.0.command_encoder_resolve_query_set(\n            self.id,\n            query_set.id,\n            first_query,\n            query_count,\n            destination.id,\n            destination_offset,\n        ) {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::resolve_query_set\",\n            );\n        }\n    }\n\n    fn mark_acceleration_structures_built<'a>(\n        &self,\n        blas: &mut dyn Iterator<Item = &'a Blas>,\n        tlas: &mut dyn Iterator<Item = &'a Tlas>,\n    ) {\n        let blas = blas\n            .map(|b| b.inner.as_core().id)\n            .collect::<SmallVec<[_; 4]>>();\n        let tlas = tlas\n            .map(|t| t.inner.as_core().id)\n            .collect::<SmallVec<[_; 4]>>();\n        if let Err(cause) = self\n            .context\n            .0\n            .command_encoder_mark_acceleration_structures_built(self.id, &blas, &tlas)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::build_acceleration_structures_unsafe_tlas\",\n            );\n        }\n    }\n\n    fn build_acceleration_structures<'a>(\n        &self,\n        blas: &mut dyn Iterator<Item = &'a crate::BlasBuildEntry<'a>>,\n        tlas: &mut dyn Iterator<Item = &'a crate::Tlas>,\n    ) {\n        let blas = blas.map(|e: &crate::BlasBuildEntry<'_>| {\n            let geometries = match e.geometry {\n                crate::BlasGeometries::TriangleGeometries(ref triangle_geometries) => {\n                    let iter = triangle_geometries.iter().map(|tg| {\n                        wgc::ray_tracing::BlasTriangleGeometry {\n                            vertex_buffer: tg.vertex_buffer.inner.as_core().id,\n                            index_buffer: tg.index_buffer.map(|buf| buf.inner.as_core().id),\n                            transform_buffer: tg.transform_buffer.map(|buf| buf.inner.as_core().id),\n                            size: tg.size,\n                            transform_buffer_offset: tg.transform_buffer_offset,\n                            first_vertex: tg.first_vertex,\n                            vertex_stride: tg.vertex_stride,\n                            first_index: tg.first_index,\n                        }\n                    });\n                    wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter))\n                }\n            };\n            wgc::ray_tracing::BlasBuildEntry {\n                blas_id: e.blas.inner.as_core().id,\n                geometries,\n            }\n        });\n\n        let tlas = tlas.into_iter().map(|e| {\n            let instances = e\n                .instances\n                .iter()\n                .map(|instance: &Option<crate::TlasInstance>| {\n                    instance\n                        .as_ref()\n                        .map(|instance| wgc::ray_tracing::TlasInstance {\n                            blas_id: instance.blas.as_core().id,\n                            transform: &instance.transform,\n                            custom_data: instance.custom_data,\n                            mask: instance.mask,\n                        })\n                });\n            wgc::ray_tracing::TlasPackage {\n                tlas_id: e.inner.as_core().id,\n                instances: Box::new(instances),\n                lowest_unmodified: e.lowest_unmodified,\n            }\n        });\n\n        if let Err(cause) = self\n            .context\n            .0\n            .command_encoder_build_acceleration_structures(self.id, blas, tlas)\n        {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::build_acceleration_structures_unsafe_tlas\",\n            );\n        }\n    }\n\n    fn transition_resources<'a>(\n        &mut self,\n        buffer_transitions: &mut dyn Iterator<\n            Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,\n        >,\n        texture_transitions: &mut dyn Iterator<\n            Item = wgt::TextureTransition<&'a dispatch::DispatchTexture>,\n        >,\n    ) {\n        let result = self.context.0.command_encoder_transition_resources(\n            self.id,\n            buffer_transitions.map(|t| wgt::BufferTransition {\n                buffer: t.buffer.as_core().id,\n                state: t.state,\n            }),\n            texture_transitions.map(|t| wgt::TextureTransition {\n                texture: t.texture.as_core().id,\n                selector: t.selector.clone(),\n                state: t.state,\n            }),\n        );\n\n        if let Err(cause) = result {\n            self.context.handle_error_nolabel(\n                &self.error_sink,\n                cause,\n                \"CommandEncoder::transition_resources\",\n            );\n        }\n    }\n}\n\nimpl Drop for CoreCommandEncoder {\n    fn drop(&mut self) {\n        self.context.0.command_encoder_drop(self.id)\n    }\n}\n\nimpl dispatch::CommandBufferInterface for CoreCommandBuffer {}\n\nimpl Drop for CoreCommandBuffer {\n    fn drop(&mut self) {\n        self.context.0.command_buffer_drop(self.id)\n    }\n}\n\nimpl dispatch::ComputePassInterface for CoreComputePass {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchComputePipeline) {\n        let pipeline = pipeline.as_core();\n\n        if let Err(cause) = self\n            .context\n            .0\n            .compute_pass_set_pipeline(&mut self.pass, pipeline.id)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::set_pipeline\",\n            );\n        }\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bg = bind_group.map(|bg| bg.as_core().id);\n\n        if let Err(cause) =\n            self.context\n                .0\n                .compute_pass_set_bind_group(&mut self.pass, index, bg, offsets)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::set_bind_group\",\n            );\n        }\n    }\n\n    fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        if let Err(cause) = self\n            .context\n            .0\n            .compute_pass_set_immediates(&mut self.pass, offset, data)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::set_immediates\",\n            );\n        }\n    }\n\n    fn insert_debug_marker(&mut self, label: &str) {\n        if let Err(cause) =\n            self.context\n                .0\n                .compute_pass_insert_debug_marker(&mut self.pass, label, 0)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::insert_debug_marker\",\n            );\n        }\n    }\n\n    fn push_debug_group(&mut self, group_label: &str) {\n        if let Err(cause) =\n            self.context\n                .0\n                .compute_pass_push_debug_group(&mut self.pass, group_label, 0)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::push_debug_group\",\n            );\n        }\n    }\n\n    fn pop_debug_group(&mut self) {\n        if let Err(cause) = self.context.0.compute_pass_pop_debug_group(&mut self.pass) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::pop_debug_group\",\n            );\n        }\n    }\n\n    fn write_timestamp(&mut self, query_set: &dispatch::DispatchQuerySet, query_index: u32) {\n        let query_set = query_set.as_core();\n\n        if let Err(cause) =\n            self.context\n                .0\n                .compute_pass_write_timestamp(&mut self.pass, query_set.id, query_index)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::write_timestamp\",\n            );\n        }\n    }\n\n    fn begin_pipeline_statistics_query(\n        &mut self,\n        query_set: &dispatch::DispatchQuerySet,\n        query_index: u32,\n    ) {\n        let query_set = query_set.as_core();\n\n        if let Err(cause) = self.context.0.compute_pass_begin_pipeline_statistics_query(\n            &mut self.pass,\n            query_set.id,\n            query_index,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::begin_pipeline_statistics_query\",\n            );\n        }\n    }\n\n    fn end_pipeline_statistics_query(&mut self) {\n        if let Err(cause) = self\n            .context\n            .0\n            .compute_pass_end_pipeline_statistics_query(&mut self.pass)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::end_pipeline_statistics_query\",\n            );\n        }\n    }\n\n    fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {\n        if let Err(cause) = self\n            .context\n            .0\n            .compute_pass_dispatch_workgroups(&mut self.pass, x, y, z)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::dispatch_workgroups\",\n            );\n        }\n    }\n\n    fn dispatch_workgroups_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.compute_pass_dispatch_workgroups_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::dispatch_workgroups_indirect\",\n            );\n        }\n    }\n}\n\nimpl Drop for CoreComputePass {\n    fn drop(&mut self) {\n        if let Err(cause) = self.context.0.compute_pass_end(&mut self.pass) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"ComputePass::end\",\n            );\n        }\n    }\n}\n\nimpl dispatch::RenderPassInterface for CoreRenderPass {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {\n        let pipeline = pipeline.as_core();\n\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_set_pipeline(&mut self.pass, pipeline.id)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_pipeline\",\n            );\n        }\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bg = bind_group.map(|bg| bg.as_core().id);\n\n        if let Err(cause) =\n            self.context\n                .0\n                .render_pass_set_bind_group(&mut self.pass, index, bg, offsets)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_bind_group\",\n            );\n        }\n    }\n\n    fn set_index_buffer(\n        &mut self,\n        buffer: &dispatch::DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_set_index_buffer(\n            &mut self.pass,\n            buffer.id,\n            index_format,\n            offset,\n            size,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_index_buffer\",\n            );\n        }\n    }\n\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_set_vertex_buffer(\n            &mut self.pass,\n            slot,\n            buffer.id,\n            offset,\n            size,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_vertex_buffer\",\n            );\n        }\n    }\n\n    fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_set_immediates(&mut self.pass, offset, data)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_immediates\",\n            );\n        }\n    }\n\n    fn set_blend_constant(&mut self, color: crate::Color) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_set_blend_constant(&mut self.pass, color)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_blend_constant\",\n            );\n        }\n    }\n\n    fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {\n        if let Err(cause) =\n            self.context\n                .0\n                .render_pass_set_scissor_rect(&mut self.pass, x, y, width, height)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_scissor_rect\",\n            );\n        }\n    }\n\n    fn set_viewport(\n        &mut self,\n        x: f32,\n        y: f32,\n        width: f32,\n        height: f32,\n        min_depth: f32,\n        max_depth: f32,\n    ) {\n        if let Err(cause) = self.context.0.render_pass_set_viewport(\n            &mut self.pass,\n            x,\n            y,\n            width,\n            height,\n            min_depth,\n            max_depth,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_viewport\",\n            );\n        }\n    }\n\n    fn set_stencil_reference(&mut self, reference: u32) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_set_stencil_reference(&mut self.pass, reference)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::set_stencil_reference\",\n            );\n        }\n    }\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        if let Err(cause) = self.context.0.render_pass_draw(\n            &mut self.pass,\n            vertices.end - vertices.start,\n            instances.end - instances.start,\n            vertices.start,\n            instances.start,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw\",\n            );\n        }\n    }\n\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        if let Err(cause) = self.context.0.render_pass_draw_indexed(\n            &mut self.pass,\n            indices.end - indices.start,\n            instances.end - instances.start,\n            indices.start,\n            base_vertex,\n            instances.start,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw_indexed\",\n            );\n        }\n    }\n\n    fn draw_mesh_tasks(&mut self, group_count_x: u32, group_count_y: u32, group_count_z: u32) {\n        if let Err(cause) = self.context.0.render_pass_draw_mesh_tasks(\n            &mut self.pass,\n            group_count_x,\n            group_count_y,\n            group_count_z,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw_mesh_tasks\",\n            );\n        }\n    }\n\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_draw_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw_indirect\",\n            );\n        }\n    }\n\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_draw_indexed_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw_indexed_indirect\",\n            );\n        }\n    }\n\n    fn draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_draw_mesh_tasks_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::draw_mesh_tasks_indirect\",\n            );\n        }\n    }\n\n    fn multi_draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_multi_draw_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n            count,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_indirect\",\n            );\n        }\n    }\n\n    fn multi_draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_multi_draw_indexed_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n            count,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_indexed_indirect\",\n            );\n        }\n    }\n\n    fn multi_draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_multi_draw_mesh_tasks_indirect(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n            count,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_mesh_tasks_indirect\",\n            );\n        }\n    }\n\n    fn multi_draw_indirect_count(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &dispatch::DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n        let count_buffer = count_buffer.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_multi_draw_indirect_count(\n            &mut self.pass,\n            indirect_buffer.id,\n            indirect_offset,\n            count_buffer.id,\n            count_buffer_offset,\n            max_count,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_indirect_count\",\n            );\n        }\n    }\n\n    fn multi_draw_indexed_indirect_count(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &dispatch::DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n        let count_buffer = count_buffer.as_core();\n\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_multi_draw_indexed_indirect_count(\n                &mut self.pass,\n                indirect_buffer.id,\n                indirect_offset,\n                count_buffer.id,\n                count_buffer_offset,\n                max_count,\n            )\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_indexed_indirect_count\",\n            );\n        }\n    }\n\n    fn multi_draw_mesh_tasks_indirect_count(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &dispatch::DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n        let count_buffer = count_buffer.as_core();\n\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_multi_draw_mesh_tasks_indirect_count(\n                &mut self.pass,\n                indirect_buffer.id,\n                indirect_offset,\n                count_buffer.id,\n                count_buffer_offset,\n                max_count,\n            )\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::multi_draw_mesh_tasks_indirect_count\",\n            );\n        }\n    }\n\n    fn insert_debug_marker(&mut self, label: &str) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_insert_debug_marker(&mut self.pass, label, 0)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::insert_debug_marker\",\n            );\n        }\n    }\n\n    fn push_debug_group(&mut self, group_label: &str) {\n        if let Err(cause) =\n            self.context\n                .0\n                .render_pass_push_debug_group(&mut self.pass, group_label, 0)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::push_debug_group\",\n            );\n        }\n    }\n\n    fn pop_debug_group(&mut self) {\n        if let Err(cause) = self.context.0.render_pass_pop_debug_group(&mut self.pass) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::pop_debug_group\",\n            );\n        }\n    }\n\n    fn write_timestamp(&mut self, query_set: &dispatch::DispatchQuerySet, query_index: u32) {\n        let query_set = query_set.as_core();\n\n        if let Err(cause) =\n            self.context\n                .0\n                .render_pass_write_timestamp(&mut self.pass, query_set.id, query_index)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::write_timestamp\",\n            );\n        }\n    }\n\n    fn begin_occlusion_query(&mut self, query_index: u32) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_begin_occlusion_query(&mut self.pass, query_index)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::begin_occlusion_query\",\n            );\n        }\n    }\n\n    fn end_occlusion_query(&mut self) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_end_occlusion_query(&mut self.pass)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::end_occlusion_query\",\n            );\n        }\n    }\n\n    fn begin_pipeline_statistics_query(\n        &mut self,\n        query_set: &dispatch::DispatchQuerySet,\n        query_index: u32,\n    ) {\n        let query_set = query_set.as_core();\n\n        if let Err(cause) = self.context.0.render_pass_begin_pipeline_statistics_query(\n            &mut self.pass,\n            query_set.id,\n            query_index,\n        ) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::begin_pipeline_statistics_query\",\n            );\n        }\n    }\n\n    fn end_pipeline_statistics_query(&mut self) {\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_end_pipeline_statistics_query(&mut self.pass)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::end_pipeline_statistics_query\",\n            );\n        }\n    }\n\n    fn execute_bundles(\n        &mut self,\n        render_bundles: &mut dyn Iterator<Item = &dispatch::DispatchRenderBundle>,\n    ) {\n        let temp_render_bundles = render_bundles\n            .map(|rb| rb.as_core().id)\n            .collect::<SmallVec<[_; 4]>>();\n        if let Err(cause) = self\n            .context\n            .0\n            .render_pass_execute_bundles(&mut self.pass, &temp_render_bundles)\n        {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::execute_bundles\",\n            );\n        }\n    }\n}\n\nimpl Drop for CoreRenderPass {\n    fn drop(&mut self) {\n        if let Err(cause) = self.context.0.render_pass_end(&mut self.pass) {\n            self.context.handle_error(\n                &self.error_sink,\n                cause,\n                self.pass.label(),\n                \"RenderPass::end\",\n            );\n        }\n    }\n}\n\nimpl dispatch::RenderBundleEncoderInterface for CoreRenderBundleEncoder {\n    fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {\n        let pipeline = pipeline.as_core();\n\n        wgpu_render_bundle_set_pipeline(&mut self.encoder, pipeline.id)\n    }\n\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&dispatch::DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    ) {\n        let bg = bind_group.map(|bg| bg.as_core().id);\n\n        unsafe {\n            wgpu_render_bundle_set_bind_group(\n                &mut self.encoder,\n                index,\n                bg,\n                offsets.as_ptr(),\n                offsets.len(),\n            )\n        }\n    }\n\n    fn set_index_buffer(\n        &mut self,\n        buffer: &dispatch::DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_core();\n\n        self.encoder\n            .set_index_buffer(buffer.id, index_format, offset, size)\n    }\n\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &dispatch::DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    ) {\n        let buffer = buffer.as_core();\n\n        wgpu_render_bundle_set_vertex_buffer(&mut self.encoder, slot, buffer.id, offset, size)\n    }\n\n    fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        unsafe {\n            wgpu_render_bundle_set_immediates(\n                &mut self.encoder,\n                offset,\n                data.len().try_into().unwrap(),\n                data.as_ptr(),\n            )\n        }\n    }\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        wgpu_render_bundle_draw(\n            &mut self.encoder,\n            vertices.end - vertices.start,\n            instances.end - instances.start,\n            vertices.start,\n            instances.start,\n        )\n    }\n\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        wgpu_render_bundle_draw_indexed(\n            &mut self.encoder,\n            indices.end - indices.start,\n            instances.end - instances.start,\n            indices.start,\n            base_vertex,\n            instances.start,\n        )\n    }\n\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        wgpu_render_bundle_draw_indirect(&mut self.encoder, indirect_buffer.id, indirect_offset)\n    }\n\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &dispatch::DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    ) {\n        let indirect_buffer = indirect_buffer.as_core();\n\n        wgpu_render_bundle_draw_indexed_indirect(\n            &mut self.encoder,\n            indirect_buffer.id,\n            indirect_offset,\n        )\n    }\n\n    fn finish(self, desc: &crate::RenderBundleDescriptor<'_>) -> dispatch::DispatchRenderBundle\n    where\n        Self: Sized,\n    {\n        let (id, error) = self.context.0.render_bundle_encoder_finish(\n            self.encoder,\n            &desc.map_label(|l| l.map(Borrowed)),\n            None,\n        );\n        if let Some(err) = error {\n            self.context\n                .handle_error_fatal(err, \"RenderBundleEncoder::finish\");\n        }\n        CoreRenderBundle {\n            context: self.context.clone(),\n            id,\n        }\n        .into()\n    }\n}\n\nimpl dispatch::RenderBundleInterface for CoreRenderBundle {}\n\nimpl Drop for CoreRenderBundle {\n    fn drop(&mut self) {\n        self.context.0.render_bundle_drop(self.id)\n    }\n}\n\nimpl dispatch::SurfaceInterface for CoreSurface {\n    fn get_capabilities(&self, adapter: &dispatch::DispatchAdapter) -> wgt::SurfaceCapabilities {\n        let adapter = adapter.as_core();\n\n        self.context\n            .0\n            .surface_get_capabilities(self.id, adapter.id)\n            .unwrap_or_default()\n    }\n\n    fn configure(&self, device: &dispatch::DispatchDevice, config: &crate::SurfaceConfiguration) {\n        let device = device.as_core();\n\n        let error = self.context.0.surface_configure(self.id, device.id, config);\n        if let Some(e) = error {\n            self.context\n                .handle_error_nolabel(&device.error_sink, e, \"Surface::configure\");\n        } else {\n            *self.configured_device.lock() = Some(device.id);\n            *self.error_sink.lock() = Some(device.error_sink.clone());\n        }\n    }\n\n    fn get_current_texture(\n        &self,\n    ) -> (\n        Option<dispatch::DispatchTexture>,\n        crate::SurfaceStatus,\n        dispatch::DispatchSurfaceOutputDetail,\n    ) {\n        let error_sink = if let Some(error_sink) = self.error_sink.lock().as_ref() {\n            error_sink.clone()\n        } else {\n            Arc::new(Mutex::new(ErrorSinkRaw::new()))\n        };\n\n        let output_detail = CoreSurfaceOutputDetail {\n            context: self.context.clone(),\n            surface_id: self.id,\n            error_sink: error_sink.clone(),\n        }\n        .into();\n\n        match self.context.0.surface_get_current_texture(self.id, None) {\n            Ok(wgc::present::SurfaceOutput {\n                status,\n                texture: texture_id,\n            }) => {\n                let data = texture_id\n                    .map(|id| CoreTexture {\n                        context: self.context.clone(),\n                        id,\n                        error_sink,\n                    })\n                    .map(Into::into);\n\n                (data, status, output_detail)\n            }\n            Err(err) => {\n                let error_sink = self.error_sink.lock();\n                match error_sink.as_ref() {\n                    Some(error_sink) => {\n                        self.context.handle_error_nolabel(\n                            error_sink,\n                            err,\n                            \"Surface::get_current_texture_view\",\n                        );\n                        (None, crate::SurfaceStatus::Validation, output_detail)\n                    }\n                    None => self\n                        .context\n                        .handle_error_fatal(err, \"Surface::get_current_texture_view\"),\n                }\n            }\n        }\n    }\n}\n\nimpl Drop for CoreSurface {\n    fn drop(&mut self) {\n        self.context.0.surface_drop(self.id)\n    }\n}\n\nimpl dispatch::SurfaceOutputDetailInterface for CoreSurfaceOutputDetail {\n    fn present(&self) {\n        match self.context.0.surface_present(self.surface_id) {\n            Ok(_status) => (),\n            Err(err) => {\n                self.context\n                    .handle_error_nolabel(&self.error_sink, err, \"Surface::present\");\n            }\n        }\n    }\n\n    fn texture_discard(&self) {\n        match self.context.0.surface_texture_discard(self.surface_id) {\n            Ok(_status) => (),\n            Err(err) => self\n                .context\n                .handle_error_fatal(err, \"Surface::discard_texture\"),\n        }\n    }\n}\nimpl Drop for CoreSurfaceOutputDetail {\n    fn drop(&mut self) {\n        // Discard gets called by the api struct\n\n        // no-op\n    }\n}\n\nimpl dispatch::QueueWriteBufferInterface for CoreQueueWriteBuffer {\n    #[inline]\n    fn len(&self) -> usize {\n        self.mapping.len()\n    }\n\n    #[inline]\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {\n        unsafe { self.mapping.write_slice() }\n    }\n}\nimpl Drop for CoreQueueWriteBuffer {\n    fn drop(&mut self) {\n        // The api struct calls queue.write_staging_buffer\n\n        // no-op\n    }\n}\n\nimpl dispatch::BufferMappedRangeInterface for CoreBufferMappedRange {\n    #[inline]\n    fn len(&self) -> usize {\n        self.size\n    }\n\n    #[inline]\n    unsafe fn read_slice(&self) -> &[u8] {\n        unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.size) }\n    }\n\n    #[inline]\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {\n        unsafe { WriteOnly::new(NonNull::slice_from_raw_parts(self.ptr, self.size)) }\n    }\n\n    #[cfg(webgpu)]\n    fn as_uint8array(&self) -> &js_sys::Uint8Array {\n        panic!(\"Only available on WebGPU\")\n    }\n}\n"
  },
  {
    "path": "wgpu/src/cmp.rs",
    "content": "//! We need to impl `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for all handle types in wgpu.\n//!\n//! For types that have some already-unique property, we can use that property to implement these traits.\n//!\n//! For types (like WebGPU) that don't have such a property, we generate an identifier and use that.\n\n#[cfg(supports_64bit_atomics)]\npub use core::sync::atomic::AtomicU64;\n#[cfg(not(supports_64bit_atomics))]\npub use portable_atomic::AtomicU64;\n\nuse core::{num::NonZeroU64, sync::atomic::Ordering};\n\nstatic NEXT_ID: AtomicU64 = AtomicU64::new(1);\n\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct Identifier {\n    inner: NonZeroU64,\n}\n\nimpl Identifier {\n    pub fn create() -> Self {\n        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);\n        // Safety: Will take 7000+ years of constant incrementing to overflow. It's fine.\n        let inner = unsafe { NonZeroU64::new_unchecked(id) };\n        Self { inner }\n    }\n}\n\n/// Implements `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for a type by proxying the operations to a single field.\n///\n/// ```ignore\n/// impl_eq_ord_hash_proxy!(MyType => .field);\n/// ```\nmacro_rules! impl_eq_ord_hash_proxy {\n    ($type:ty => $($access:tt)*) => {\n        impl PartialEq for $type {\n            fn eq(&self, other: &Self) -> bool {\n                self $($access)* == other $($access)*\n            }\n        }\n\n        impl Eq for $type {}\n\n        impl PartialOrd for $type {\n            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {\n                Some(self.cmp(other))\n            }\n        }\n\n        impl Ord for $type {\n            fn cmp(&self, other: &Self) -> core::cmp::Ordering {\n                self $($access)*.cmp(&other $($access)*)\n            }\n        }\n\n        impl core::hash::Hash for $type {\n            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n                self $($access)*.hash(state)\n            }\n        }\n    };\n}\n\n/// Implements `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for a type by comparing the addresses of the `Arc`s.\n///\n/// ```ignore\n/// impl_eq_ord_hash_arc_address!(MyType => .field);\n/// ```\n#[cfg_attr(not(any(wgpu_core, custom)), expect(unused_macros))]\nmacro_rules! impl_eq_ord_hash_arc_address {\n    ($type:ty => $($access:tt)*) => {\n        impl PartialEq for $type {\n            fn eq(&self, other: &Self) -> bool {\n                let address_left = alloc::sync::Arc::as_ptr(&self $($access)*);\n                let address_right = alloc::sync::Arc::as_ptr(&other $($access)*);\n\n                address_left == address_right\n            }\n        }\n\n        impl Eq for $type {}\n\n        impl PartialOrd for $type {\n            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {\n                Some(self.cmp(other))\n            }\n        }\n\n        impl Ord for $type {\n            fn cmp(&self, other: &Self) -> core::cmp::Ordering {\n                let address_left = alloc::sync::Arc::as_ptr(&self $($access)*);\n                let address_right = alloc::sync::Arc::as_ptr(&other $($access)*);\n\n                address_left.cmp(&address_right)\n            }\n        }\n\n        impl core::hash::Hash for $type {\n            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n                let address = alloc::sync::Arc::as_ptr(&self $($access)*);\n                address.hash(state)\n            }\n        }\n    };\n}\n\n#[cfg_attr(not(any(wgpu_core, custom)), expect(unused_imports))]\npub(crate) use {impl_eq_ord_hash_arc_address, impl_eq_ord_hash_proxy};\n"
  },
  {
    "path": "wgpu/src/dispatch.rs",
    "content": "//! Infrastructure for dispatching calls to the appropriate \"backend\". The \"backends\" are:\n//!\n//! - `wgpu_core`: An implementation of the the wgpu api on top of various native graphics APIs.\n//! - `webgpu`: An implementation of the wgpu api which calls WebGPU directly.\n//!\n//! The interface traits are all object safe and listed in the `InterfaceTypes` trait.\n//!\n//! The method for dispatching should optimize well if only one backend is\n//! compiled in, as-if there was no dispatching at all. See the comments on\n//! [`dispatch_types`] for details.\n//!\n//! [`dispatch_types`]: macro.dispatch_types.html\n\n#![allow(\n    drop_bounds,\n    reason = \"This exists to remind implementors to impl drop.\"\n)]\n#![allow(clippy::too_many_arguments, reason = \"It's fine.\")]\n#![allow(\n    missing_docs,\n    clippy::missing_safety_doc,\n    reason = \"Interfaces are not documented\"\n)]\n#![allow(\n    clippy::len_without_is_empty,\n    reason = \"trait is minimal, not ergonomic\"\n)]\n\nuse crate::{Blas, Tlas, WasmNotSend, WasmNotSendSync, WriteOnly};\n\nuse alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};\nuse core::{any::Any, fmt::Debug, future::Future, hash::Hash, ops::Range, pin::Pin};\n\n#[cfg(custom)]\nuse crate::backend::custom::*;\n#[cfg(webgpu)]\nuse crate::backend::webgpu::*;\n#[cfg(wgpu_core)]\nuse crate::backend::wgpu_core::*;\n\n/// Create a single trait with the given supertraits and a blanket impl for all types that implement them.\n///\n/// This is useful for creating a trait alias as a shorthand.\nmacro_rules! trait_alias {\n    ($name:ident: $($bound:tt)+) => {\n        pub trait $name: $($bound)+ {}\n        impl<T: $($bound)+> $name for T {}\n    };\n}\n\n// Various return futures in the API.\ntrait_alias!(RequestAdapterFuture: Future<Output = Result<DispatchAdapter, wgt::RequestAdapterError>> + WasmNotSend + 'static);\ntrait_alias!(RequestDeviceFuture: Future<Output = Result<(DispatchDevice, DispatchQueue), crate::RequestDeviceError>> + WasmNotSend + 'static);\ntrait_alias!(PopErrorScopeFuture: Future<Output = Option<crate::Error>> + WasmNotSend + 'static);\ntrait_alias!(ShaderCompilationInfoFuture: Future<Output = crate::CompilationInfo> + WasmNotSend + 'static);\ntrait_alias!(EnumerateAdapterFuture: Future<Output = Vec<DispatchAdapter>> + WasmNotSend + 'static);\n\n// We can't use trait aliases here, as you can't convert from a dyn Trait to dyn Supertrait _yet_.\n#[cfg(send_sync)]\npub type BoxDeviceLostCallback = Box<dyn FnOnce(crate::DeviceLostReason, String) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BoxDeviceLostCallback = Box<dyn FnOnce(crate::DeviceLostReason, String) + 'static>;\n#[cfg(send_sync)]\npub type BoxSubmittedWorkDoneCallback = Box<dyn FnOnce() + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BoxSubmittedWorkDoneCallback = Box<dyn FnOnce() + 'static>;\n#[cfg(send_sync)]\npub type BufferMapCallback = Box<dyn FnOnce(Result<(), crate::BufferAsyncError>) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BufferMapCallback = Box<dyn FnOnce(Result<(), crate::BufferAsyncError>) + 'static>;\n\n#[cfg(send_sync)]\npub type BlasCompactCallback = Box<dyn FnOnce(Result<(), crate::BlasAsyncError>) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BlasCompactCallback = Box<dyn FnOnce(Result<(), crate::BlasAsyncError>) + 'static>;\n\n// remove when rust 1.86\n#[cfg_attr(not(custom), expect(dead_code))]\npub trait AsAny {\n    fn as_any(&self) -> &dyn Any;\n}\n\nimpl<T: 'static> AsAny for T {\n    fn as_any(&self) -> &dyn Any {\n        self\n    }\n}\n\n// Common traits on all the interface traits\ntrait_alias!(CommonTraits: AsAny + Any + Debug + WasmNotSendSync);\n\npub trait InstanceInterface: CommonTraits {\n    fn new(desc: crate::InstanceDescriptor) -> Self\n    where\n        Self: Sized;\n\n    unsafe fn create_surface(\n        &self,\n        target: crate::SurfaceTargetUnsafe,\n    ) -> Result<DispatchSurface, crate::CreateSurfaceError>;\n\n    fn request_adapter(\n        &self,\n        options: &crate::RequestAdapterOptions<'_, '_>,\n    ) -> Pin<Box<dyn RequestAdapterFuture>>;\n\n    fn poll_all_devices(&self, force_wait: bool) -> bool;\n\n    #[cfg(feature = \"wgsl\")]\n    fn wgsl_language_features(&self) -> crate::WgslLanguageFeatures;\n\n    fn enumerate_adapters(&self, backends: crate::Backends)\n        -> Pin<Box<dyn EnumerateAdapterFuture>>;\n}\n\npub trait AdapterInterface: CommonTraits {\n    fn request_device(\n        &self,\n        desc: &crate::DeviceDescriptor<'_>,\n    ) -> Pin<Box<dyn RequestDeviceFuture>>;\n\n    fn is_surface_supported(&self, surface: &DispatchSurface) -> bool;\n\n    fn features(&self) -> crate::Features;\n\n    fn limits(&self) -> crate::Limits;\n\n    fn downlevel_capabilities(&self) -> crate::DownlevelCapabilities;\n\n    fn get_info(&self) -> crate::AdapterInfo;\n\n    fn get_texture_format_features(\n        &self,\n        format: crate::TextureFormat,\n    ) -> crate::TextureFormatFeatures;\n\n    fn get_presentation_timestamp(&self) -> crate::PresentationTimestamp;\n\n    fn cooperative_matrix_properties(&self) -> Vec<crate::wgt::CooperativeMatrixProperties>;\n}\n\npub trait DeviceInterface: CommonTraits {\n    fn features(&self) -> crate::Features;\n    fn limits(&self) -> crate::Limits;\n    fn adapter_info(&self) -> crate::AdapterInfo;\n\n    fn create_shader_module(\n        &self,\n        desc: crate::ShaderModuleDescriptor<'_>,\n        shader_bound_checks: crate::ShaderRuntimeChecks,\n    ) -> DispatchShaderModule;\n\n    unsafe fn create_shader_module_passthrough(\n        &self,\n        desc: &crate::ShaderModuleDescriptorPassthrough<'_>,\n    ) -> DispatchShaderModule;\n\n    fn create_bind_group_layout(\n        &self,\n        desc: &crate::BindGroupLayoutDescriptor<'_>,\n    ) -> DispatchBindGroupLayout;\n    fn create_bind_group(&self, desc: &crate::BindGroupDescriptor<'_>) -> DispatchBindGroup;\n    fn create_pipeline_layout(\n        &self,\n        desc: &crate::PipelineLayoutDescriptor<'_>,\n    ) -> DispatchPipelineLayout;\n    fn create_render_pipeline(\n        &self,\n        desc: &crate::RenderPipelineDescriptor<'_>,\n    ) -> DispatchRenderPipeline;\n    fn create_mesh_pipeline(\n        &self,\n        desc: &crate::MeshPipelineDescriptor<'_>,\n    ) -> DispatchRenderPipeline;\n    fn create_compute_pipeline(\n        &self,\n        desc: &crate::ComputePipelineDescriptor<'_>,\n    ) -> DispatchComputePipeline;\n    unsafe fn create_pipeline_cache(\n        &self,\n        desc: &crate::PipelineCacheDescriptor<'_>,\n    ) -> DispatchPipelineCache;\n    fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> DispatchBuffer;\n    fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> DispatchTexture;\n    fn create_external_texture(\n        &self,\n        desc: &crate::ExternalTextureDescriptor<'_>,\n        planes: &[&crate::TextureView],\n    ) -> DispatchExternalTexture;\n    fn create_blas(\n        &self,\n        desc: &crate::CreateBlasDescriptor<'_>,\n        sizes: crate::BlasGeometrySizeDescriptors,\n    ) -> (Option<u64>, DispatchBlas);\n    fn create_tlas(&self, desc: &crate::CreateTlasDescriptor<'_>) -> DispatchTlas;\n    fn create_sampler(&self, desc: &crate::SamplerDescriptor<'_>) -> DispatchSampler;\n    fn create_query_set(&self, desc: &crate::QuerySetDescriptor<'_>) -> DispatchQuerySet;\n    fn create_command_encoder(\n        &self,\n        desc: &crate::CommandEncoderDescriptor<'_>,\n    ) -> DispatchCommandEncoder;\n    fn create_render_bundle_encoder(\n        &self,\n        desc: &crate::RenderBundleEncoderDescriptor<'_>,\n    ) -> DispatchRenderBundleEncoder;\n\n    fn set_device_lost_callback(&self, device_lost_callback: BoxDeviceLostCallback);\n\n    fn on_uncaptured_error(&self, handler: Arc<dyn crate::UncapturedErrorHandler>);\n    // Returns index on the stack of the pushed error scope.\n    fn push_error_scope(&self, filter: crate::ErrorFilter) -> u32;\n    fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn PopErrorScopeFuture>>;\n\n    unsafe fn start_graphics_debugger_capture(&self);\n    unsafe fn stop_graphics_debugger_capture(&self);\n\n    fn poll(&self, poll_type: wgt::PollType<u64>) -> Result<crate::PollStatus, crate::PollError>;\n\n    fn get_internal_counters(&self) -> crate::InternalCounters;\n    fn generate_allocator_report(&self) -> Option<crate::AllocatorReport>;\n\n    fn destroy(&self);\n}\n\npub trait QueueInterface: CommonTraits {\n    fn write_buffer(&self, buffer: &DispatchBuffer, offset: crate::BufferAddress, data: &[u8]);\n\n    fn create_staging_buffer(&self, size: crate::BufferSize) -> Option<DispatchQueueWriteBuffer>;\n    fn validate_write_buffer(\n        &self,\n        buffer: &DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: crate::BufferSize,\n    ) -> Option<()>;\n    fn write_staging_buffer(\n        &self,\n        buffer: &DispatchBuffer,\n        offset: crate::BufferAddress,\n        staging_buffer: &DispatchQueueWriteBuffer,\n    );\n\n    fn write_texture(\n        &self,\n        texture: crate::TexelCopyTextureInfo<'_>,\n        data: &[u8],\n        data_layout: crate::TexelCopyBufferLayout,\n        size: crate::Extent3d,\n    );\n    #[cfg(web)]\n    fn copy_external_image_to_texture(\n        &self,\n        source: &crate::CopyExternalImageSourceInfo,\n        dest: crate::CopyExternalImageDestInfo<&crate::api::Texture>,\n        size: crate::Extent3d,\n    );\n\n    /// Submit must always drain the iterator, even in the case of error.\n    fn submit(&self, command_buffers: &mut dyn Iterator<Item = DispatchCommandBuffer>) -> u64;\n\n    fn get_timestamp_period(&self) -> f32;\n    fn on_submitted_work_done(&self, callback: BoxSubmittedWorkDoneCallback);\n\n    fn compact_blas(&self, blas: &DispatchBlas) -> (Option<u64>, DispatchBlas);\n}\n\npub trait ShaderModuleInterface: CommonTraits {\n    fn get_compilation_info(&self) -> Pin<Box<dyn ShaderCompilationInfoFuture>>;\n}\npub trait BindGroupLayoutInterface: CommonTraits {}\npub trait BindGroupInterface: CommonTraits {}\npub trait TextureViewInterface: CommonTraits {}\npub trait SamplerInterface: CommonTraits {}\npub trait BufferInterface: CommonTraits {\n    fn map_async(\n        &self,\n        mode: crate::MapMode,\n        range: Range<crate::BufferAddress>,\n        callback: BufferMapCallback,\n    );\n    fn get_mapped_range(&self, sub_range: Range<crate::BufferAddress>)\n        -> DispatchBufferMappedRange;\n\n    fn unmap(&self);\n\n    fn destroy(&self);\n}\npub trait TextureInterface: CommonTraits {\n    fn create_view(&self, desc: &crate::TextureViewDescriptor<'_>) -> DispatchTextureView;\n\n    fn destroy(&self);\n}\npub trait ExternalTextureInterface: CommonTraits {\n    fn destroy(&self);\n}\npub trait BlasInterface: CommonTraits {\n    fn prepare_compact_async(&self, callback: BlasCompactCallback);\n    fn ready_for_compaction(&self) -> bool;\n}\npub trait TlasInterface: CommonTraits {}\npub trait QuerySetInterface: CommonTraits {}\npub trait PipelineLayoutInterface: CommonTraits {}\npub trait RenderPipelineInterface: CommonTraits {\n    fn get_bind_group_layout(&self, index: u32) -> DispatchBindGroupLayout;\n}\npub trait ComputePipelineInterface: CommonTraits {\n    fn get_bind_group_layout(&self, index: u32) -> DispatchBindGroupLayout;\n}\npub trait PipelineCacheInterface: CommonTraits {\n    fn get_data(&self) -> Option<Vec<u8>>;\n}\npub trait CommandEncoderInterface: CommonTraits {\n    fn copy_buffer_to_buffer(\n        &self,\n        source: &DispatchBuffer,\n        source_offset: crate::BufferAddress,\n        destination: &DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n        copy_size: Option<crate::BufferAddress>,\n    );\n    fn copy_buffer_to_texture(\n        &self,\n        source: crate::TexelCopyBufferInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    );\n    fn copy_texture_to_buffer(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyBufferInfo<'_>,\n        copy_size: crate::Extent3d,\n    );\n    fn copy_texture_to_texture(\n        &self,\n        source: crate::TexelCopyTextureInfo<'_>,\n        destination: crate::TexelCopyTextureInfo<'_>,\n        copy_size: crate::Extent3d,\n    );\n\n    fn begin_compute_pass(&self, desc: &crate::ComputePassDescriptor<'_>) -> DispatchComputePass;\n    fn begin_render_pass(&self, desc: &crate::RenderPassDescriptor<'_>) -> DispatchRenderPass;\n    fn finish(&mut self) -> DispatchCommandBuffer;\n\n    fn clear_texture(\n        &self,\n        texture: &DispatchTexture,\n        subresource_range: &crate::ImageSubresourceRange,\n    );\n    fn clear_buffer(\n        &self,\n        buffer: &DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferAddress>,\n    );\n\n    fn insert_debug_marker(&self, label: &str);\n    fn push_debug_group(&self, label: &str);\n    fn pop_debug_group(&self);\n\n    fn write_timestamp(&self, query_set: &DispatchQuerySet, query_index: u32);\n    fn resolve_query_set(\n        &self,\n        query_set: &DispatchQuerySet,\n        first_query: u32,\n        query_count: u32,\n        destination: &DispatchBuffer,\n        destination_offset: crate::BufferAddress,\n    );\n    fn mark_acceleration_structures_built<'a>(\n        &self,\n        blas: &mut dyn Iterator<Item = &'a Blas>,\n        tlas: &mut dyn Iterator<Item = &'a Tlas>,\n    );\n\n    fn build_acceleration_structures<'a>(\n        &self,\n        blas: &mut dyn Iterator<Item = &'a crate::BlasBuildEntry<'a>>,\n        tlas: &mut dyn Iterator<Item = &'a crate::Tlas>,\n    );\n\n    fn transition_resources<'a>(\n        &mut self,\n        buffer_transitions: &mut dyn Iterator<Item = wgt::BufferTransition<&'a DispatchBuffer>>,\n        texture_transitions: &mut dyn Iterator<Item = wgt::TextureTransition<&'a DispatchTexture>>,\n    );\n}\npub trait ComputePassInterface: CommonTraits + Drop {\n    fn set_pipeline(&mut self, pipeline: &DispatchComputePipeline);\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    );\n    fn set_immediates(&mut self, offset: u32, data: &[u8]);\n\n    fn insert_debug_marker(&mut self, label: &str);\n    fn push_debug_group(&mut self, group_label: &str);\n    fn pop_debug_group(&mut self);\n\n    fn write_timestamp(&mut self, query_set: &DispatchQuerySet, query_index: u32);\n    fn begin_pipeline_statistics_query(&mut self, query_set: &DispatchQuerySet, query_index: u32);\n    fn end_pipeline_statistics_query(&mut self);\n\n    fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32);\n    fn dispatch_workgroups_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n}\npub trait RenderPassInterface: CommonTraits + Drop {\n    fn set_pipeline(&mut self, pipeline: &DispatchRenderPipeline);\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    );\n    fn set_index_buffer(\n        &mut self,\n        buffer: &DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    );\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    );\n    fn set_immediates(&mut self, offset: u32, data: &[u8]);\n    fn set_blend_constant(&mut self, color: crate::Color);\n    fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32);\n    fn set_viewport(\n        &mut self,\n        x: f32,\n        y: f32,\n        width: f32,\n        height: f32,\n        min_depth: f32,\n        max_depth: f32,\n    );\n    fn set_stencil_reference(&mut self, reference: u32);\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>);\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);\n    fn draw_mesh_tasks(&mut self, group_count_x: u32, group_count_y: u32, group_count_z: u32);\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n    fn draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n\n    fn multi_draw_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    );\n    fn multi_draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    );\n    fn multi_draw_indirect_count(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    );\n    fn multi_draw_mesh_tasks_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count: u32,\n    );\n    fn multi_draw_indexed_indirect_count(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    );\n    fn multi_draw_mesh_tasks_indirect_count(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n        count_buffer: &DispatchBuffer,\n        count_buffer_offset: crate::BufferAddress,\n        max_count: u32,\n    );\n\n    fn insert_debug_marker(&mut self, label: &str);\n    fn push_debug_group(&mut self, group_label: &str);\n    fn pop_debug_group(&mut self);\n\n    fn write_timestamp(&mut self, query_set: &DispatchQuerySet, query_index: u32);\n    fn begin_occlusion_query(&mut self, query_index: u32);\n    fn end_occlusion_query(&mut self);\n    fn begin_pipeline_statistics_query(&mut self, query_set: &DispatchQuerySet, query_index: u32);\n    fn end_pipeline_statistics_query(&mut self);\n\n    fn execute_bundles(&mut self, render_bundles: &mut dyn Iterator<Item = &DispatchRenderBundle>);\n}\n\npub trait RenderBundleEncoderInterface: CommonTraits {\n    fn set_pipeline(&mut self, pipeline: &DispatchRenderPipeline);\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&DispatchBindGroup>,\n        offsets: &[crate::DynamicOffset],\n    );\n    fn set_index_buffer(\n        &mut self,\n        buffer: &DispatchBuffer,\n        index_format: crate::IndexFormat,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    );\n    fn set_vertex_buffer(\n        &mut self,\n        slot: u32,\n        buffer: &DispatchBuffer,\n        offset: crate::BufferAddress,\n        size: Option<crate::BufferSize>,\n    );\n    fn set_immediates(&mut self, offset: u32, data: &[u8]);\n\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>);\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);\n    fn draw_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &DispatchBuffer,\n        indirect_offset: crate::BufferAddress,\n    );\n\n    fn finish(self, desc: &crate::RenderBundleDescriptor<'_>) -> DispatchRenderBundle\n    where\n        Self: Sized;\n}\n\npub trait CommandBufferInterface: CommonTraits {}\npub trait RenderBundleInterface: CommonTraits {}\n\npub trait SurfaceInterface: CommonTraits {\n    fn get_capabilities(&self, adapter: &DispatchAdapter) -> crate::SurfaceCapabilities;\n\n    fn configure(&self, device: &DispatchDevice, config: &crate::SurfaceConfiguration);\n    fn get_current_texture(\n        &self,\n    ) -> (\n        Option<DispatchTexture>,\n        crate::SurfaceStatus,\n        DispatchSurfaceOutputDetail,\n    );\n}\n\npub trait SurfaceOutputDetailInterface: CommonTraits {\n    fn present(&self);\n    fn texture_discard(&self);\n}\n\npub trait QueueWriteBufferInterface: CommonTraits {\n    fn len(&self) -> usize;\n\n    /// # Safety\n    ///\n    /// Must only be used on write, not read, mappings.\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]>;\n}\n\npub trait BufferMappedRangeInterface: CommonTraits {\n    fn len(&self) -> usize;\n\n    /// # Safety\n    ///\n    /// Must only be used on read, not write, mappings.\n    unsafe fn read_slice(&self) -> &[u8];\n\n    /// # Safety\n    ///\n    /// Must only be used on write, not read, mappings.\n    unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]>;\n\n    #[cfg(webgpu)]\n    fn as_uint8array(&self) -> &js_sys::Uint8Array;\n}\n\n/// Generates a dispatch type for some `wgpu` API type.\n///\n/// Invocations of this macro take one of the following forms:\n///\n/// ```ignore\n/// dispatch_types! {mut type D: I = Core, Web, Dyn }\n/// dispatch_types! {ref type D: I = Core, Web, Dyn }\n/// ```\n///\n/// This defines `D` as a type that dereferences to a `dyn I` trait object. Most uses of\n/// `D` in the rest of this crate just call the methods from the `dyn I` object, not from\n/// `D` itself.\n///\n/// Internally, `D` is an enum with up to three variants holding values of type `Core`,\n/// `Web`, and `Dyn`, all of which must implement `I`. `Core`, `Web` and `Dyn` are the\n/// types from the `wgpu_core`, `webgpu`, and `custom` submodules of `wgpu::backend` that\n/// correspond to `D`. The macro generates `Deref` and `DerefMut` implementations that\n/// match on this enum and produce a `dyn I` reference for each variant.\n///\n/// The macro's `mut type` form defines `D` as the unique owner of the backend type, with\n/// a `DerefMut` implementation, and `as_*_mut` methods that return `&mut` references.\n/// This `D` does not implement `Clone`.\n///\n/// The macro's `ref type` form defines `D` to hold an `Arc` pointing to the backend type,\n/// permitting `Clone` and `Deref`, but losing exclusive, mutable access.\n///\n/// For example:\n///\n/// ```ignore\n/// dispatch_types! {ref type DispatchBuffer: BufferInterface =\n///                  CoreBuffer, WebBuffer, DynBuffer}\n/// ```\n///\n/// This defines `DispatchBuffer` as a type that dereferences to `&dyn BufferInterface`,\n/// which has methods like `map_async` and `destroy`. The enum would be:\n///\n/// ```ignore\n/// pub enum DispatchBuffer {\n///     #[cfg(wgpu_core)]\n///     Core(Arc<CoreBuffer>),\n///     #[cfg(webgpu)]\n///     WebGPU(WebBuffer),\n///     #[cfg(custom)]\n///     Custom(DynBuffer),\n/// }\n/// ```\n///\n/// This macro also defines `as_*` methods so that the backend implementations can\n/// dereference other arguments.\n///\n/// ## Devirtualization\n///\n/// The dispatch types generated by this macro are carefully designed to allow the\n/// compiler to completely devirtualize calls in most circumstances.\n///\n/// Note that every variant of the enum generated by this macro is under a `#[cfg]`.\n/// Naturally, the `match` expressions in the `Deref` and `DerefMut` implementations have\n/// matching `#[cfg]` attributes on each match arm.\n///\n/// In practice, when `wgpu`'s `\"custom\"` feature is not enabled, there is usually only\n/// one variant in the `enum`, making it effectively a newtype around the sole variant's\n/// data: it has no discriminant to branch on, and the `match` expressions are removed\n/// entirely by the compiler.\n///\n/// In this case, when we invoke a method from the interface trait `I` on a dispatch type,\n/// the `Deref` and `DerefMut` implementations' `match` statements build a `&dyn I` for\n/// the data, on which we immediately invoke a method. The vtable is a constant, allowing\n/// the Rust compiler to turn the `dyn` method call into an ordinary method call. This\n/// creates opportunities for inlining.\n///\n/// Similarly, the `as_*` methods are free when there is only one backend.\nmacro_rules! dispatch_types {\n    (\n        ref type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident\n    ) => {\n        #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]\n        pub enum $name {\n            #[cfg(wgpu_core)]\n            Core(Arc<$core_type>),\n            #[cfg(webgpu)]\n            WebGPU($webgpu_type),\n            #[allow(clippy::allow_attributes, private_interfaces)]\n            #[cfg(custom)]\n            Custom($custom_type),\n        }\n\n        impl $name {\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core(&self) -> &$core_type {\n                match self {\n                    Self::Core(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not core\")),\n                }\n            }\n\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core_opt(&self) -> Option<&$core_type> {\n                match self {\n                    Self::Core(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(custom)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_custom<T: $interface>(&self) -> Option<&T> {\n                match self {\n                    Self::Custom(value) => value.downcast(),\n                    _ => None,\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu(&self) -> &$webgpu_type {\n                match self {\n                    Self::WebGPU(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not webgpu\")),\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu_opt(&self) -> Option<&$webgpu_type> {\n                match self {\n                    Self::WebGPU(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(custom)]\n            #[inline]\n            pub fn custom<T: $interface>(t: T) -> Self {\n                Self::Custom($custom_type::new(t))\n            }\n        }\n\n        #[cfg(wgpu_core)]\n        impl From<$core_type> for $name {\n            #[inline]\n            fn from(value: $core_type) -> Self {\n                Self::Core(Arc::new(value))\n            }\n        }\n\n        #[cfg(webgpu)]\n        impl From<$webgpu_type> for $name {\n            #[inline]\n            fn from(value: $webgpu_type) -> Self {\n                Self::WebGPU(value)\n            }\n        }\n\n        impl core::ops::Deref for $name {\n            type Target = dyn $interface;\n\n            #[inline]\n            fn deref(&self) -> &Self::Target {\n                match self {\n                    #[cfg(wgpu_core)]\n                    Self::Core(value) => value.as_ref(),\n                    #[cfg(webgpu)]\n                    Self::WebGPU(value) => value,\n                    #[cfg(custom)]\n                    Self::Custom(value) => value.deref(),\n                    #[cfg(not(any(wgpu_core, webgpu)))]\n                    _ => panic!(\"No context available. You need to enable one of wgpu's backend feature build flags.\"),\n                }\n            }\n        }\n    };\n    (\n        mut type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident\n    ) => {\n        #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\n        pub enum $name {\n            #[cfg(wgpu_core)]\n            Core($core_type),\n            #[cfg(webgpu)]\n            WebGPU($webgpu_type),\n            #[allow(clippy::allow_attributes, private_interfaces)]\n            #[cfg(custom)]\n            Custom($custom_type),\n        }\n\n        impl $name {\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core(&self) -> &$core_type {\n                match self {\n                    Self::Core(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not core\")),\n                }\n            }\n\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core_mut(&mut self) -> &mut $core_type {\n                match self {\n                    Self::Core(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not core\")),\n                }\n            }\n\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core_opt(&self) -> Option<&$core_type> {\n                match self {\n                    Self::Core(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(wgpu_core)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_core_mut_opt(\n                &mut self,\n            ) -> Option<&mut $core_type> {\n                match self {\n                    Self::Core(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(custom)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_custom<T: $interface>(&self) -> Option<&T> {\n                match self {\n                    Self::Custom(value) => value.downcast(),\n                    _ => None,\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu(&self) -> &$webgpu_type {\n                match self {\n                    Self::WebGPU(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not webgpu\")),\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu_mut(&mut self) -> &mut $webgpu_type {\n                match self {\n                    Self::WebGPU(value) => value,\n                    _ => panic!(concat!(stringify!($name), \" is not webgpu\")),\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu_opt(&self) -> Option<&$webgpu_type> {\n                match self {\n                    Self::WebGPU(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(webgpu)]\n            #[inline]\n            #[allow(clippy::allow_attributes, unused)]\n            pub fn as_webgpu_mut_opt(\n                &mut self,\n            ) -> Option<&mut $webgpu_type> {\n                match self {\n                    Self::WebGPU(value) => Some(value),\n                    _ => None,\n                }\n            }\n\n            #[cfg(custom)]\n            #[inline]\n            pub fn custom<T: $interface>(t: T) -> Self {\n                Self::Custom($custom_type::new(t))\n            }\n        }\n\n        #[cfg(wgpu_core)]\n        impl From<$core_type> for $name {\n            #[inline]\n            fn from(value: $core_type) -> Self {\n                Self::Core(value)\n            }\n        }\n\n        #[cfg(webgpu)]\n        impl From<$webgpu_type> for $name {\n            #[inline]\n            fn from(value: $webgpu_type) -> Self {\n                Self::WebGPU(value)\n            }\n        }\n\n        impl core::ops::Deref for $name {\n            type Target = dyn $interface;\n\n            #[inline]\n            fn deref(&self) -> &Self::Target {\n                match self {\n                    #[cfg(wgpu_core)]\n                    Self::Core(value) => value,\n                    #[cfg(webgpu)]\n                    Self::WebGPU(value) => value,\n                    #[cfg(custom)]\n                    Self::Custom(value) => value.deref(),\n                    #[cfg(not(any(wgpu_core, webgpu)))]\n                    _ => panic!(\"No context available. You need to enable one of wgpu's backend feature build flags.\"),\n                }\n            }\n        }\n\n        impl core::ops::DerefMut for $name {\n            #[inline]\n            fn deref_mut(&mut self) -> &mut Self::Target {\n                match self {\n                    #[cfg(wgpu_core)]\n                    Self::Core(value) => value,\n                    #[cfg(webgpu)]\n                    Self::WebGPU(value) => value,\n                    #[cfg(custom)]\n                    Self::Custom(value) => value.deref_mut(),\n                    #[cfg(not(any(wgpu_core, webgpu)))]\n                    _ => panic!(\"No context available. You need to enable one of wgpu's backend feature build flags.\"),\n                }\n            }\n        }\n    };\n}\n\ndispatch_types! {ref type DispatchInstance: InstanceInterface = ContextWgpuCore, ContextWebGpu, DynContext}\ndispatch_types! {ref type DispatchAdapter: AdapterInterface = CoreAdapter, WebAdapter, DynAdapter}\ndispatch_types! {ref type DispatchDevice: DeviceInterface = CoreDevice, WebDevice, DynDevice}\ndispatch_types! {ref type DispatchQueue: QueueInterface = CoreQueue, WebQueue, DynQueue}\ndispatch_types! {ref type DispatchShaderModule: ShaderModuleInterface = CoreShaderModule, WebShaderModule, DynShaderModule}\ndispatch_types! {ref type DispatchBindGroupLayout: BindGroupLayoutInterface = CoreBindGroupLayout, WebBindGroupLayout, DynBindGroupLayout}\ndispatch_types! {ref type DispatchBindGroup: BindGroupInterface = CoreBindGroup, WebBindGroup, DynBindGroup}\ndispatch_types! {ref type DispatchTextureView: TextureViewInterface = CoreTextureView, WebTextureView, DynTextureView}\ndispatch_types! {ref type DispatchSampler: SamplerInterface = CoreSampler, WebSampler, DynSampler}\ndispatch_types! {ref type DispatchBuffer: BufferInterface = CoreBuffer, WebBuffer, DynBuffer}\ndispatch_types! {ref type DispatchTexture: TextureInterface = CoreTexture, WebTexture, DynTexture}\ndispatch_types! {ref type DispatchExternalTexture: ExternalTextureInterface = CoreExternalTexture, WebExternalTexture, DynExternalTexture}\ndispatch_types! {ref type DispatchBlas: BlasInterface = CoreBlas, WebBlas, DynBlas}\ndispatch_types! {ref type DispatchTlas: TlasInterface = CoreTlas, WebTlas, DynTlas}\ndispatch_types! {ref type DispatchQuerySet: QuerySetInterface = CoreQuerySet, WebQuerySet, DynQuerySet}\ndispatch_types! {ref type DispatchPipelineLayout: PipelineLayoutInterface = CorePipelineLayout, WebPipelineLayout, DynPipelineLayout}\ndispatch_types! {ref type DispatchRenderPipeline: RenderPipelineInterface = CoreRenderPipeline, WebRenderPipeline, DynRenderPipeline}\ndispatch_types! {ref type DispatchComputePipeline: ComputePipelineInterface = CoreComputePipeline, WebComputePipeline, DynComputePipeline}\ndispatch_types! {ref type DispatchPipelineCache: PipelineCacheInterface = CorePipelineCache, WebPipelineCache, DynPipelineCache}\ndispatch_types! {mut type DispatchCommandEncoder: CommandEncoderInterface = CoreCommandEncoder, WebCommandEncoder, DynCommandEncoder}\ndispatch_types! {mut type DispatchComputePass: ComputePassInterface = CoreComputePass, WebComputePassEncoder, DynComputePass}\ndispatch_types! {mut type DispatchRenderPass: RenderPassInterface = CoreRenderPass, WebRenderPassEncoder, DynRenderPass}\ndispatch_types! {mut type DispatchCommandBuffer: CommandBufferInterface = CoreCommandBuffer, WebCommandBuffer, DynCommandBuffer}\ndispatch_types! {mut type DispatchRenderBundleEncoder: RenderBundleEncoderInterface = CoreRenderBundleEncoder, WebRenderBundleEncoder, DynRenderBundleEncoder}\ndispatch_types! {ref type DispatchRenderBundle: RenderBundleInterface = CoreRenderBundle, WebRenderBundle, DynRenderBundle}\ndispatch_types! {ref type DispatchSurface: SurfaceInterface = CoreSurface, WebSurface, DynSurface}\ndispatch_types! {ref type DispatchSurfaceOutputDetail: SurfaceOutputDetailInterface = CoreSurfaceOutputDetail, WebSurfaceOutputDetail, DynSurfaceOutputDetail}\ndispatch_types! {mut type DispatchQueueWriteBuffer: QueueWriteBufferInterface = CoreQueueWriteBuffer, WebQueueWriteBuffer, DynQueueWriteBuffer}\ndispatch_types! {mut type DispatchBufferMappedRange: BufferMappedRangeInterface = CoreBufferMappedRange, WebBufferMappedRange, DynBufferMappedRange}\n"
  },
  {
    "path": "wgpu/src/lib.rs",
    "content": "//! `wgpu` is a cross-platform, safe, pure-Rust graphics API. It runs natively on\n//! Vulkan, Metal, D3D12, and OpenGL; and on top of WebGL2 and WebGPU on wasm.\n//!\n//! The API is based on the [WebGPU standard][webgpu], but is a fully native Rust library.\n//! It serves as the core of the WebGPU integration in Firefox, Servo, and Deno.\n//!\n//! [webgpu]: https://gpuweb.github.io/gpuweb/\n//!\n//! ## Getting Started\n//!\n//! The main entry point to the API is the [`Instance`] type, from which you can create [`Adapter`], [`Device`], and [`Surface`].\n//!\n//! If you are new to `wgpu` and graphics programming, we recommend starting with [Learn Wgpu].\n//! <!-- Note, \"Learn Wgpu\" is using the capitalization style in their header, NOT our styling -->\n//!\n//! Additionally, [WebGPU Fundamentals] is a tutorial for WebGPU which is very similar to our API, minus differences between Rust and Javascript.\n//!\n//! We have a [wiki](https://github.com/gfx-rs/wgpu/wiki) which has information on useful architecture patterns, debugging tips, and more getting started information.\n//!\n//! There are examples for this version [available on GitHub](https://github.com/gfx-rs/wgpu/tree/v29/examples#readme).\n//!\n//! The API is refcounted, so all handles are cloneable, and if you create a resource which references another,\n//! it will automatically keep dependent resources alive.\n//!\n//! `wgpu` uses the coordinate systems of D3D and Metal. Depth ranges from [0, 1].\n//!\n//! | Render                | Texture                |\n//! | --------------------- | ---------------------- |\n//! | ![render_coordinates] | ![texture_coordinates] |\n//!\n//! `wgpu`'s MSRV is **1.87**.\n//!\n//! [Learn Wgpu]: https://sotrh.github.io/learn-wgpu/\n//! [WebGPU Fundamentals]: https://webgpufundamentals.org/\n//! [render_coordinates]: https://raw.githubusercontent.com/gfx-rs/wgpu/refs/heads/v29/docs/render_coordinates.png\n//! [texture_coordinates]: https://raw.githubusercontent.com/gfx-rs/wgpu/refs/heads/v29/docs/texture_coordinates.png\n//!\n//! ## Extension Specifications\n//!\n//! While the core of `wgpu` is based on the WebGPU standard, we also support extensions that allow for features that the standard does not have yet.\n//! For high-level documentation on how to use these extensions, see documentation on [`Features`] or the relevant specification:\n//!\n//! 🧪EXPERIMENTAL🧪 APIs are subject to change and may allow undefined behavior if used incorrectly.\n//!\n//! - 🧪EXPERIMENTAL🧪 [Ray Tracing](https://github.com/gfx-rs/wgpu/blob/v29/docs/api-specs/ray_tracing.md).\n//! - 🧪EXPERIMENTAL🧪 [Mesh Shading](https://github.com/gfx-rs/wgpu/blob/v29/docs/api-specs/mesh_shading.md).\n//!\n//! ## Shader Support\n//!\n//! `wgpu` can consume shaders in [WGSL](https://gpuweb.github.io/gpuweb/wgsl/), SPIR-V, and GLSL.\n//! Both [HLSL](https://github.com/Microsoft/DirectXShaderCompiler) and [GLSL](https://github.com/KhronosGroup/glslang)\n//! have compilers to target SPIR-V. All of these shader languages can be used with any backend as we handle all of the conversions. Additionally, support for these shader inputs is not going away.\n//!\n//! While WebGPU does not support any shading language other than WGSL, we will automatically convert your\n//! non-WGSL shaders if you're running on WebGPU.\n//!\n//! WGSL is always supported by default, but GLSL and SPIR-V need features enabled to compile in support.\n//!\n//! To enable WGSL shaders, enable the `wgsl` feature of `wgpu` (enabled by default).\n//! To enable SPIR-V shaders, enable the `spirv` feature of `wgpu`.\n//! To enable GLSL shaders, enable the `glsl` feature of `wgpu`.\n//!\n//! ## Feature flags\n#![doc = document_features::document_features!()]\n//!\n//! ### Feature Aliases\n//!\n//! These features aren't actually features on the crate itself, but a convenient shorthand for\n//! complicated cases.\n//!\n//! - **`wgpu_core`** --- Enabled when there is any non-webgpu backend enabled on the platform.\n//! - **`naga`** --- Enabled when target `glsl` or `spirv` input is enabled, or when `wgpu_core` is enabled.\n//!\n\n#![no_std]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n#![doc(html_logo_url = \"https://raw.githubusercontent.com/gfx-rs/wgpu/trunk/logo.png\")]\n#![warn(\n    clippy::alloc_instead_of_core,\n    clippy::allow_attributes,\n    clippy::std_instead_of_alloc,\n    clippy::std_instead_of_core,\n    missing_docs,\n    rust_2018_idioms,\n    unsafe_op_in_unsafe_fn\n)]\n#![allow(\n    // We need to investiagate these.\n    clippy::large_enum_variant,\n    // These degrade readability significantly.\n    clippy::bool_assert_comparison,\n    clippy::bool_comparison,\n)]\n// NOTE: Keep this in sync with `wgpu-core`.\n#![cfg_attr(not(send_sync), allow(clippy::arc_with_non_send_sync))]\n#![cfg_attr(not(any(wgpu_core, webgpu)), allow(unused))]\n\nextern crate alloc;\n#[cfg(any(std, test))]\nextern crate std;\n#[cfg(wgpu_core)]\npub extern crate wgpu_core as wgc;\n#[cfg(wgpu_core)]\npub extern crate wgpu_hal as hal;\npub extern crate wgpu_types as wgt;\n\n//\n//\n// Modules\n//\n//\n\nmod api;\nmod backend;\nmod cmp;\nmod dispatch;\nmod macros;\npub mod util;\n\n//\n//\n// Public re-exports\n//\n//\n\n#[cfg(custom)]\npub use backend::custom;\n\npub use api::*;\npub use wgt::{\n    AdapterInfo, AddressMode, AllocatorReport, AstcBlock, AstcChannel, Backend, BackendOptions,\n    Backends, BindGroupLayoutEntry, BindingType, BlendComponent, BlendFactor, BlendOperation,\n    BlendState, BufferAddress, BufferBindingType, BufferSize, BufferTextureCopyInfo,\n    BufferTransition, BufferUsages, BufferUses, Color, ColorTargetState, ColorWrites,\n    CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CooperativeMatrixProperties,\n    CooperativeScalarType, CopyExternalImageDestInfo, CoreCounters, DepthBiasState,\n    DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, DownlevelFlags,\n    DownlevelLimits, Dx12BackendOptions, Dx12Compiler, Dx12SwapchainKind,\n    Dx12UseFrameLatencyWaitableObject, DxcShaderModel, DynamicOffset, ExperimentalFeatures,\n    Extent3d, ExternalTextureFormat, ExternalTextureTransferFunction, Face, Features, FeaturesWGPU,\n    FeaturesWebGPU, FilterMode, ForceShaderModelToken, FrontFace, GlBackendOptions, GlDebugFns,\n    GlFenceBehavior, Gles3MinorVersion, HalCounters, ImageSubresourceRange, IndexFormat,\n    InstanceDescriptor, InstanceFlags, InternalCounters, Limits, LoadOpDontCare,\n    MemoryBudgetThresholds, MemoryHints, MipmapFilterMode, MultisampleState, NoopBackendOptions,\n    Origin2d, Origin3d, PipelineStatisticsTypes, PollError, PollStatus, PolygonMode,\n    PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState,\n    PrimitiveTopology, QueryType, RenderBundleDepthStencil, RequestAdapterError,\n    SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks,\n    ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess,\n    SurfaceCapabilities, SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension,\n    TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType,\n    TextureTransition, TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute,\n    VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, WriteOnly,\n    WriteOnlyIter, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, IMMEDIATE_DATA_ALIGNMENT,\n    MAP_ALIGNMENT, MAXIMUM_SUBGROUP_MAX_SIZE, MINIMUM_SUBGROUP_MIN_SIZE,\n    QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_ALIGNMENT,\n};\n\n#[expect(deprecated)]\npub use wgt::VERTEX_STRIDE_ALIGNMENT;\n\n// wasm-only types, we try to keep as many types non-platform\n// specific, but these need to depend on web-sys.\n#[cfg(web)]\npub use wgt::{CopyExternalImageSourceInfo, ExternalImageSource};\n\n/// Re-export of our `naga` dependency.\n///\n#[cfg(wgpu_core)]\n#[cfg_attr(docsrs, doc(cfg(any(wgpu_core, naga))))]\n// We re-export wgpu-core's re-export of naga, as we may not have direct access to it.\npub use ::wgc::naga;\n/// Re-export of our `naga` dependency.\n///\n#[cfg(all(not(wgpu_core), naga))]\n#[cfg_attr(docsrs, doc(cfg(any(wgpu_core, naga))))]\n// If that's not available, we re-export our own.\npub use naga;\n\n/// Re-export of our `raw-window-handle` dependency.\n///\npub use raw_window_handle as rwh;\n\n/// Re-export of our `web-sys` dependency.\n///\n#[cfg(web)]\npub use web_sys;\n\n#[doc(hidden)]\npub use macros::helpers as __macro_helpers;\n"
  },
  {
    "path": "wgpu/src/macros/be-aligned.spv",
    "content": "\u0007#\u0002\u0003\u0011\"3D"
  },
  {
    "path": "wgpu/src/macros/le-aligned.spv",
    "content": "\u0003\u0002#\u0007D3\"\u0011"
  },
  {
    "path": "wgpu/src/macros/mod.rs",
    "content": "//! Convenience macros\n\n#[cfg(doc)]\nuse crate::{VertexAttribute, VertexBufferLayout, VertexFormat};\n\n/// Macro to produce an array of [`VertexAttribute`].\n///\n/// The input is a sequence of pairs of shader locations (expression of type [`u32`]) and\n/// variant names of [`VertexFormat`].\n///\n/// The return value has type `[VertexAttribute; N]`, where `N` is the number of inputs.\n///\n/// Offsets are calculated automatically,\n/// using the assumption that there is no padding or other data between attributes.\n///\n/// # Example\n///\n/// ```\n/// // Suppose that this is our vertex format:\n/// #[repr(C, packed)]\n/// struct Vertex {\n///     foo: [f32; 2],\n///     bar: f32,\n///     baz: [u16; 4],\n/// }\n///\n/// // Then these attributes match it:\n/// let attrs = wgpu::vertex_attr_array![\n///     0 => Float32x2,\n///     1 => Float32,\n///     2 => Uint16x4,\n/// ];\n///\n/// // Here's the full data structure the macro produced:\n/// use wgpu::{VertexAttribute as A, VertexFormat as F};\n/// assert_eq!(attrs, [\n///     A { format: F::Float32x2, offset:  0, shader_location: 0, },\n///     A { format: F::Float32,   offset:  8, shader_location: 1, },\n///     A { format: F::Uint16x4,  offset: 12, shader_location: 2, },\n/// ]);\n/// ```\n///\n/// See [`VertexBufferLayout`] for an example building on this one.\n#[macro_export]\nmacro_rules! vertex_attr_array {\n    ($($location:expr => $format:ident),* $(,)?) => {\n        $crate::_vertex_attr_array_helper!([] ; 0; $($location => $format ,)*)\n    };\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! _vertex_attr_array_helper {\n    ([$($t:expr,)*] ; $off:expr ;) => { [$($t,)*] };\n    ([$($t:expr,)*] ; $off:expr ; $location:expr => $format:ident, $($ll:expr => $ii:ident ,)*) => {\n        $crate::_vertex_attr_array_helper!(\n            [$($t,)*\n            $crate::VertexAttribute {\n                format: $crate::VertexFormat :: $format,\n                offset: $off,\n                shader_location: $location,\n            },];\n            $off + $crate::VertexFormat :: $format.size();\n            $($ll => $ii ,)*\n        )\n    };\n}\n\n#[test]\nfn test_vertex_attr_array() {\n    let attrs = vertex_attr_array![0 => Float32x2, 3 => Uint16x4];\n    // VertexAttribute does not support PartialEq, so we cannot test directly\n    assert_eq!(attrs.len(), 2);\n    assert_eq!(attrs[0].offset, 0);\n    assert_eq!(attrs[0].shader_location, 0);\n    assert_eq!(attrs[1].offset, size_of::<(f32, f32)>() as u64);\n    assert_eq!(attrs[1].shader_location, 3);\n}\n\n#[macro_export]\n#[doc(hidden)]\nmacro_rules! include_spirv_source {\n    ($($token:tt)*) => {\n        {\n            const SPIRV_SOURCE: [\n                u8;\n                $crate::__macro_helpers::include_bytes!($($token)*).len()\n            ] = *$crate::__macro_helpers::include_bytes!($($token)*);\n            const SPIRV_LEN: usize = SPIRV_SOURCE.len() / 4;\n            const SPIRV_WORDS: [u32; SPIRV_LEN] = $crate::util::make_spirv_const(SPIRV_SOURCE);\n            &SPIRV_WORDS\n        }\n    }\n}\n\n#[test]\nfn make_spirv_le_pass() {\n    static SPIRV: &[u32] = include_spirv_source!(\"le-aligned.spv\");\n    assert_eq!(SPIRV, &[0x07230203, 0x11223344]);\n}\n\n#[test]\nfn make_spirv_be_pass() {\n    static SPIRV: &[u32] = include_spirv_source!(\"be-aligned.spv\");\n    assert_eq!(SPIRV, &[0x07230203, 0x11223344]);\n}\n\n/// Macro to load a SPIR-V module statically.\n///\n/// It ensures the word alignment as well as the magic number.\n///\n/// Return type: [`crate::ShaderModuleDescriptor`]\n#[macro_export]\n#[cfg(feature = \"spirv\")]\nmacro_rules! include_spirv {\n    ($($token:tt)*) => {\n        {\n            $crate::ShaderModuleDescriptor {\n                label: Some($($token)*),\n                source: $crate::ShaderSource::SpirV(\n                    $crate::__macro_helpers::Cow::Borrowed($crate::include_spirv_source!($($token)*))\n                ),\n            }\n        }\n    };\n}\n\n#[cfg(all(feature = \"spirv\", test))]\n#[expect(dead_code)]\nstatic SPIRV: crate::ShaderModuleDescriptor<'_> = include_spirv!(\"le-aligned.spv\");\n\n/// Macro to load raw SPIR-V data statically, for use with [`Features::PASSTHROUGH_SHADERS`].\n///\n/// It ensures the word alignment as well as the magic number.\n///\n/// [`Features::PASSTHROUGH_SHADERS`]: crate::Features::PASSTHROUGH_SHADERS\n#[macro_export]\nmacro_rules! include_spirv_raw {\n    ($($token:tt)*) => {\n        {\n            $crate::ShaderModuleDescriptorPassthrough {\n                label: $crate::__macro_helpers::Some($($token)*),\n                spirv: Some($crate::__macro_helpers::Cow::Borrowed($crate::include_spirv_source!($($token)*))),\n                // This is unused for SPIR-V\n                num_workgroups: (0, 0, 0),\n                dxil: None,\n                metallib: None,\n                msl: None,\n                hlsl: None,\n                glsl: None,\n                wgsl: None,\n            }\n        }\n    };\n}\n\n#[cfg(test)]\n#[expect(dead_code)]\nstatic SPIRV_RAW: crate::ShaderModuleDescriptorPassthrough<'_> =\n    include_spirv_raw!(\"le-aligned.spv\");\n\n/// Load WGSL source code from a file at compile time.\n///\n/// The loaded path is relative to the path of the file containing the macro call, in the same way\n/// as [`include_str!`] operates.\n///\n/// ```ignore\n/// fn main() {\n///     let module: ShaderModuleDescriptor = include_wgsl!(\"shader.wgsl\");\n/// }\n/// ```\n#[macro_export]\nmacro_rules! include_wgsl {\n    ($($token:tt)*) => {\n        {\n            $crate::ShaderModuleDescriptor {\n                label: $crate::__macro_helpers::Some($($token)*),\n                source: $crate::ShaderSource::Wgsl($crate::__macro_helpers::Cow::Borrowed($crate::__macro_helpers::include_str!($($token)*))),\n            }\n        }\n    };\n}\n\n// Macros which help us generate the documentation of which hal types correspond to which backend.\n//\n// Because all backends are not compiled into the program, we cannot link to them in all situations,\n// we need to only link to the types if the backend is compiled in. These are used in #[doc] attributes\n// so cannot have more than one line, so cannot use internal cfgs.\n\n/// Helper macro to generate the documentation for dx12 hal methods, referencing the hal type.\n#[cfg(dx12)]\nmacro_rules! hal_type_dx12 {\n    ($ty: literal) => {\n        concat!(\"- [`hal::api::Dx12`] uses [`hal::dx12::\", $ty, \"`]\")\n    };\n}\n/// Helper macro to generate the documentation for dx12 hal methods, referencing the hal type.\n#[cfg(not(dx12))]\nmacro_rules! hal_type_dx12 {\n    ($ty: literal) => {\n        concat!(\"- `hal::api::Dx12` uses `hal::dx12::\", $ty, \"`\")\n    };\n}\npub(crate) use hal_type_dx12;\n\n/// Helper macro to generate the documentation for metal hal methods, referencing the hal type.\n#[cfg(metal)]\nmacro_rules! hal_type_metal {\n    ($ty: literal) => {\n        concat!(\"- [`hal::api::Metal`] uses [`hal::metal::\", $ty, \"`]\")\n    };\n}\n/// Helper macro to generate the documentation for metal hal methods, referencing the hal type.\n#[cfg(not(metal))]\nmacro_rules! hal_type_metal {\n    ($ty: literal) => {\n        concat!(\"- `hal::api::Metal` uses `hal::metal::\", $ty, \"`\")\n    };\n}\npub(crate) use hal_type_metal;\n\n/// Helper macro to generate the documentation for vulkan hal methods, referencing the hal type.\n#[cfg(vulkan)]\nmacro_rules! hal_type_vulkan {\n    ($ty: literal) => {\n        concat!(\"- [`hal::api::Vulkan`] uses [`hal::vulkan::\", $ty, \"`]\")\n    };\n}\n/// Helper macro to generate the documentation for vulkan hal methods, referencing the hal type.\n#[cfg(not(vulkan))]\nmacro_rules! hal_type_vulkan {\n    ($ty: literal) => {\n        concat!(\"- `hal::api::Vulkan` uses `hal::vulkan::\", $ty, \"`\")\n    };\n}\npub(crate) use hal_type_vulkan;\n\n/// Helper macro to generate the documentation for gles hal methods, referencing the hal type.\n#[cfg(gles)]\nmacro_rules! hal_type_gles {\n    ($ty: literal) => {\n        concat!(\"- [`hal::api::Gles`] uses [`hal::gles::\", $ty, \"`]\")\n    };\n}\n/// Helper macro to generate the documentation for gles hal methods, referencing the hal type.\n#[cfg(not(gles))]\nmacro_rules! hal_type_gles {\n    ($ty: literal) => {\n        concat!(\"- `hal::api::Gles` uses `hal::gles::\", $ty, \"`\")\n    };\n}\npub(crate) use hal_type_gles;\n\n#[doc(hidden)]\npub mod helpers {\n    pub use alloc::{borrow::Cow, string::String};\n    pub use core::{include_bytes, include_str};\n    pub use Some;\n}\n"
  },
  {
    "path": "wgpu/src/util/belt.rs",
    "content": "use crate::{\n    util::align_to, Buffer, BufferAddress, BufferDescriptor, BufferSize, BufferSlice, BufferUsages,\n    BufferViewMut, CommandEncoder, Device, MapMode,\n};\nuse alloc::vec::Vec;\nuse core::fmt;\nuse std::sync::mpsc;\nuse wgt::Features;\n\nuse crate::COPY_BUFFER_ALIGNMENT;\n\n/// Efficiently performs many buffer writes by sharing and reusing temporary buffers.\n///\n/// Internally it uses a ring-buffer of staging buffers that are sub-allocated.\n/// Its advantage over [`Queue::write_buffer_with()`] is that the individual allocations\n/// are cheaper; `StagingBelt` is most useful when you are writing very many small pieces\n/// of data. It can be understood as a sort of arena allocator.\n///\n/// Using a staging belt is slightly complicated, and generally goes as follows:\n/// 1. Use [`StagingBelt::write_buffer()`] or [`StagingBelt::allocate()`] to allocate\n///    buffer slices, then write your data to them.\n/// 2. Call [`StagingBelt::finish()`].\n/// 3. Submit all command encoders that were used in step 1.\n/// 4. Call [`StagingBelt::recall()`].\n///\n/// [`Queue::write_buffer_with()`]: crate::Queue::write_buffer_with\npub struct StagingBelt {\n    device: Device,\n    chunk_size: BufferAddress,\n    /// User-specified [`BufferUsages`] used to create the chunk buffers are created.\n    ///\n    /// [`new`](Self::new) guarantees that this always contains\n    /// [`MAP_WRITE`](BufferUsages::MAP_WRITE).\n    buffer_usages: BufferUsages,\n    /// Chunks into which we are accumulating data to be transferred.\n    active_chunks: Vec<Chunk>,\n    /// Chunks that have scheduled transfers already; they are unmapped and some\n    /// command encoder has one or more commands with them as source.\n    closed_chunks: Vec<Chunk>,\n    /// Chunks that are back from the GPU and ready to be mapped for write and put\n    /// into `active_chunks`.\n    free_chunks: Vec<Chunk>,\n    /// When closed chunks are mapped again, the map callback sends them here.\n    sender: Exclusive<mpsc::Sender<Chunk>>,\n    /// Free chunks are received here to be put on `self.free_chunks`.\n    receiver: Exclusive<mpsc::Receiver<Chunk>>,\n}\n\nimpl StagingBelt {\n    /// Create a new staging belt.\n    ///\n    /// The `chunk_size` is the unit of internal buffer allocation; writes will be\n    /// sub-allocated within each chunk. Therefore, for optimal use of memory, the\n    /// chunk size should be:\n    ///\n    /// * larger than the largest single [`StagingBelt::write_buffer()`] operation;\n    /// * 1-4 times less than the total amount of data uploaded per submission\n    ///   (per [`StagingBelt::finish()`]); and\n    /// * bigger is better, within these bounds.\n    ///\n    /// The buffers returned by this [`StagingBelt`] will be have the buffer usages\n    /// [`COPY_SRC | MAP_WRITE`](crate::BufferUsages)\n    pub fn new(device: Device, chunk_size: BufferAddress) -> Self {\n        Self::new_with_buffer_usages(device, chunk_size, BufferUsages::COPY_SRC)\n    }\n\n    /// Create a new staging belt.\n    ///\n    /// The `chunk_size` is the unit of internal buffer allocation; writes will be\n    /// sub-allocated within each chunk. Therefore, for optimal use of memory, the\n    /// chunk size should be:\n    ///\n    /// * larger than the largest single [`StagingBelt::write_buffer()`] operation;\n    /// * 1-4 times less than the total amount of data uploaded per submission\n    ///   (per [`StagingBelt::finish()`]); and\n    /// * bigger is better, within these bounds.\n    ///\n    /// `buffer_usages` specifies the [`BufferUsages`] the staging buffers\n    /// will be created with. [`MAP_WRITE`](BufferUsages::MAP_WRITE) will be added\n    /// automatically. The method will panic if the combination of usages is not\n    /// supported. Because [`MAP_WRITE`](BufferUsages::MAP_WRITE) is implied, the allowed usages\n    /// depends on if [`Features::MAPPABLE_PRIMARY_BUFFERS`] is enabled.\n    /// - If enabled: any usage is valid.\n    /// - If disabled: only [`COPY_SRC`](BufferUsages::COPY_SRC) can be used.\n    #[track_caller]\n    pub fn new_with_buffer_usages(\n        device: Device,\n        chunk_size: BufferAddress,\n        mut buffer_usages: BufferUsages,\n    ) -> Self {\n        let (sender, receiver) = mpsc::channel();\n\n        // make sure anything other than MAP_WRITE | COPY_SRC is only allowed with MAPPABLE_PRIMARY_BUFFERS.\n        let extra_usages =\n            buffer_usages.difference(BufferUsages::MAP_WRITE | BufferUsages::COPY_SRC);\n        if !extra_usages.is_empty()\n            && !device\n                .features()\n                .contains(Features::MAPPABLE_PRIMARY_BUFFERS)\n        {\n            panic!(\"Only BufferUsages::COPY_SRC may be used when Features::MAPPABLE_PRIMARY_BUFFERS is not enabled. Specified buffer usages: {buffer_usages:?}\");\n        }\n        // always set MAP_WRITE\n        buffer_usages.insert(BufferUsages::MAP_WRITE);\n\n        StagingBelt {\n            device,\n            chunk_size,\n            buffer_usages,\n            active_chunks: Vec::new(),\n            closed_chunks: Vec::new(),\n            free_chunks: Vec::new(),\n            sender: Exclusive::new(sender),\n            receiver: Exclusive::new(receiver),\n        }\n    }\n\n    /// Allocate a staging belt slice of `size` to be copied into the `target` buffer\n    /// at the specified offset.\n    ///\n    /// `offset` and `size` must be multiples of [`COPY_BUFFER_ALIGNMENT`]\n    /// (as is required by the underlying buffer operations).\n    ///\n    /// The upload will be placed into the provided command encoder. This encoder\n    /// must be submitted after [`StagingBelt::finish()`] is called and before\n    /// [`StagingBelt::recall()`] is called.\n    ///\n    /// If the `size` is greater than the size of any free internal buffer, a new buffer\n    /// will be allocated for it. Therefore, the `chunk_size` passed to [`StagingBelt::new()`]\n    /// should ideally be larger than every such size.\n    #[track_caller]\n    pub fn write_buffer(\n        &mut self,\n        encoder: &mut CommandEncoder,\n        target: &Buffer,\n        offset: BufferAddress,\n        size: BufferSize,\n    ) -> BufferViewMut {\n        // Asserting this explicitly gives a usefully more specific, and more prompt, error than\n        // leaving it to regular API validation.\n        // We check only `offset`, not `size`, because `self.allocate()` will check the size.\n        assert!(\n            offset.is_multiple_of(COPY_BUFFER_ALIGNMENT),\n            \"StagingBelt::write_buffer() offset {offset} must be a multiple of `COPY_BUFFER_ALIGNMENT`\"\n        );\n\n        let slice_of_belt = self.allocate(\n            size,\n            const { BufferSize::new(crate::COPY_BUFFER_ALIGNMENT).unwrap() },\n        );\n        encoder.copy_buffer_to_buffer(\n            slice_of_belt.buffer(),\n            slice_of_belt.offset(),\n            target,\n            offset,\n            size.get(),\n        );\n        slice_of_belt.get_mapped_range_mut()\n    }\n\n    /// Allocate a staging belt slice with the given `size` and `alignment` and return it.\n    ///\n    /// `size` must be a multiple of [`COPY_BUFFER_ALIGNMENT`]\n    /// (as is required by the underlying buffer operations).\n    ///\n    /// To use this slice, call [`BufferSlice::get_mapped_range_mut()`] and write your data into\n    /// that [`BufferViewMut`].\n    /// (The view must be dropped before [`StagingBelt::finish()`] is called.)\n    ///\n    /// You can then record your own GPU commands to perform with the slice,\n    /// such as copying it to a texture (whereas\n    /// [`StagingBelt::write_buffer()`] can only write to other buffers).\n    /// All commands involving this slice must be submitted after\n    /// [`StagingBelt::finish()`] is called and before [`StagingBelt::recall()`] is called.\n    ///\n    /// If the `size` is greater than the space available in any free internal buffer, a new buffer\n    /// will be allocated for it. Therefore, the `chunk_size` passed to [`StagingBelt::new()`]\n    /// should ideally be larger than every such size.\n    ///\n    /// The chosen slice will be positioned within the buffer at a multiple of `alignment`,\n    /// which may be used to meet alignment requirements for the operation you wish to perform\n    /// with the slice. This does not necessarily affect the alignment of the [`BufferViewMut`].\n    #[track_caller]\n    pub fn allocate(&mut self, size: BufferSize, alignment: BufferSize) -> BufferSlice<'_> {\n        assert!(\n            size.get().is_multiple_of(COPY_BUFFER_ALIGNMENT),\n            \"StagingBelt allocation size {size} must be a multiple of `COPY_BUFFER_ALIGNMENT`\"\n        );\n        assert!(\n            alignment.get().is_power_of_two(),\n            \"alignment must be a power of two, not {alignment}\"\n        );\n        // At minimum, we must have alignment sufficient to map the buffer.\n        let alignment = alignment.get().max(crate::MAP_ALIGNMENT);\n\n        let mut chunk = if let Some(index) = self\n            .active_chunks\n            .iter()\n            .position(|chunk| chunk.can_allocate(size, alignment))\n        {\n            self.active_chunks.swap_remove(index)\n        } else {\n            self.receive_chunks(); // ensure self.free_chunks is up to date\n\n            if let Some(index) = self\n                .free_chunks\n                .iter()\n                .position(|chunk| chunk.can_allocate(size, alignment))\n            {\n                self.free_chunks.swap_remove(index)\n            } else {\n                Chunk {\n                    buffer: self.device.create_buffer(&BufferDescriptor {\n                        label: Some(\"(wgpu internal) StagingBelt staging buffer\"),\n                        size: self.chunk_size.max(size.get()),\n                        usage: self.buffer_usages,\n                        mapped_at_creation: true,\n                    }),\n                    offset: 0,\n                }\n            }\n        };\n\n        let allocation_offset = chunk.allocate(size, alignment);\n\n        self.active_chunks.push(chunk);\n        let chunk = self.active_chunks.last().unwrap();\n\n        chunk\n            .buffer\n            .slice(allocation_offset..allocation_offset + size.get())\n    }\n\n    /// Prepare currently mapped buffers for use in a submission.\n    ///\n    /// This must be called before the command encoder(s) provided to\n    /// [`StagingBelt::write_buffer()`] are submitted.\n    ///\n    /// At this point, all the partially used staging buffers are closed (cannot be used for\n    /// further writes) until after [`StagingBelt::recall()`] is called *and* the GPU is done\n    /// copying the data from them.\n    pub fn finish(&mut self) {\n        for chunk in self.active_chunks.drain(..) {\n            chunk.buffer.unmap();\n            self.closed_chunks.push(chunk);\n        }\n    }\n\n    /// Recall all of the closed buffers back to be reused.\n    ///\n    /// This must only be called after the command encoder(s) provided to\n    /// [`StagingBelt::write_buffer()`] are submitted. Additional calls are harmless.\n    /// Not calling this as soon as possible may result in increased buffer memory usage.\n    pub fn recall(&mut self) {\n        self.receive_chunks();\n\n        for chunk in self.closed_chunks.drain(..) {\n            let sender = self.sender.get_mut().clone();\n            chunk\n                .buffer\n                .clone()\n                .slice(..)\n                .map_async(MapMode::Write, move |_| {\n                    let _ = sender.send(chunk);\n                });\n        }\n    }\n\n    /// Move all chunks that the GPU is done with (and are now mapped again)\n    /// from `self.receiver` to `self.free_chunks`.\n    fn receive_chunks(&mut self) {\n        while let Ok(mut chunk) = self.receiver.get_mut().try_recv() {\n            chunk.offset = 0;\n            self.free_chunks.push(chunk);\n        }\n    }\n}\n\nimpl fmt::Debug for StagingBelt {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let Self {\n            device,\n            chunk_size,\n            buffer_usages,\n            active_chunks,\n            closed_chunks,\n            free_chunks,\n            sender: _,\n            receiver: _,\n        } = self;\n        f.debug_struct(\"StagingBelt\")\n            .field(\"device\", device)\n            .field(\"chunk_size\", chunk_size)\n            .field(\"buffer_usages\", buffer_usages)\n            .field(\"active_chunks\", &active_chunks.len())\n            .field(\"closed_chunks\", &closed_chunks.len())\n            .field(\"free_chunks\", &free_chunks.len())\n            .finish_non_exhaustive()\n    }\n}\n\nstruct Chunk {\n    buffer: Buffer,\n    offset: BufferAddress,\n}\n\nimpl Chunk {\n    fn can_allocate(&self, size: BufferSize, alignment: BufferAddress) -> bool {\n        let alloc_start = align_to(self.offset, alignment);\n        let alloc_end = alloc_start + size.get();\n\n        alloc_end <= self.buffer.size()\n    }\n\n    fn allocate(&mut self, size: BufferSize, alignment: BufferAddress) -> BufferAddress {\n        let alloc_start = align_to(self.offset, alignment);\n        let alloc_end = alloc_start + size.get();\n\n        assert!(alloc_end <= self.buffer.size());\n        self.offset = alloc_end;\n        alloc_start\n    }\n}\n\nuse exclusive::Exclusive;\nmod exclusive {\n    /// `Sync` wrapper that works by providing only exclusive access.\n    ///\n    /// See <https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html>\n    pub(super) struct Exclusive<T>(T);\n\n    /// Safety: `&Exclusive` has no operations.\n    unsafe impl<T> Sync for Exclusive<T> {}\n\n    impl<T> Exclusive<T> {\n        pub fn new(value: T) -> Self {\n            Self(value)\n        }\n\n        pub fn get_mut(&mut self) -> &mut T {\n            &mut self.0\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/blit.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) tex_coords: vec2<f32>,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {\n    var out: VertexOutput;\n\n    out.tex_coords = vec2<f32>(\n        f32((vi << 1u) & 2u),\n        f32(vi & 2u),\n    );\n\n    out.position = vec4<f32>(out.tex_coords * 2.0 - 1.0, 0.0, 1.0);\n\n    // Invert y so the texture is not upside down\n    out.tex_coords.y = 1.0 - out.tex_coords.y;\n    return out;\n}\n\n@group(0) @binding(0)\nvar texture: texture_2d<f32>;\n@group(0) @binding(1)\nvar texture_sampler: sampler;\n\n@fragment\nfn fs_main(vs: VertexOutput) -> @location(0) vec4<f32> {\n    return textureSample(texture, texture_sampler, vs.tex_coords);\n}"
  },
  {
    "path": "wgpu/src/util/device.rs",
    "content": "use alloc::borrow::ToOwned as _;\n\nuse wgt::TextureDataOrder;\n\n/// Describes a [Buffer](crate::Buffer) when allocating.\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct BufferInitDescriptor<'a> {\n    /// Debug label of a buffer. This will show up in graphics debuggers for easy identification.\n    pub label: crate::Label<'a>,\n    /// Contents of a buffer on creation.\n    pub contents: &'a [u8],\n    /// Usages of a buffer. If the buffer is used in any way that isn't specified here, the operation\n    /// will panic.\n    pub usage: wgt::BufferUsages,\n}\n\n/// Utility methods not meant to be in the main API.\npub trait DeviceExt {\n    /// Creates a [Buffer](crate::Buffer) with data to initialize it.\n    fn create_buffer_init(&self, desc: &BufferInitDescriptor<'_>) -> crate::Buffer;\n\n    /// Upload an entire texture and its mipmaps from a source buffer.\n    ///\n    /// Expects all mipmaps to be tightly packed in the data buffer.\n    ///\n    /// See [`TextureDataOrder`] for the order in which the data is laid out in memory.\n    ///\n    /// Implicitly adds the `COPY_DST` usage if it is not present in the descriptor,\n    /// as it is required to be able to upload the data to the gpu.\n    fn create_texture_with_data(\n        &self,\n        queue: &crate::Queue,\n        desc: &crate::TextureDescriptor<'_>,\n        order: TextureDataOrder,\n        data: &[u8],\n    ) -> crate::Texture;\n}\n\nimpl DeviceExt for crate::Device {\n    fn create_buffer_init(&self, descriptor: &BufferInitDescriptor<'_>) -> crate::Buffer {\n        // Skip mapping if the buffer is zero sized\n        if descriptor.contents.is_empty() {\n            let wgt_descriptor = crate::BufferDescriptor {\n                label: descriptor.label,\n                size: 0,\n                usage: descriptor.usage,\n                mapped_at_creation: false,\n            };\n\n            self.create_buffer(&wgt_descriptor)\n        } else {\n            let unpadded_size = descriptor.contents.len() as crate::BufferAddress;\n            // Valid vulkan usage is\n            // 1. buffer size must be a multiple of COPY_BUFFER_ALIGNMENT.\n            // 2. buffer size must be greater than 0.\n            // Therefore we round the value up to the nearest multiple, and ensure it's at least COPY_BUFFER_ALIGNMENT.\n            let align_mask = crate::COPY_BUFFER_ALIGNMENT - 1;\n            let padded_size =\n                ((unpadded_size + align_mask) & !align_mask).max(crate::COPY_BUFFER_ALIGNMENT);\n\n            let wgt_descriptor = crate::BufferDescriptor {\n                label: descriptor.label,\n                size: padded_size,\n                usage: descriptor.usage,\n                mapped_at_creation: true,\n            };\n\n            let buffer = self.create_buffer(&wgt_descriptor);\n\n            buffer\n                .get_mapped_range_mut(..)\n                .slice(..unpadded_size as usize)\n                .copy_from_slice(descriptor.contents);\n            buffer.unmap();\n\n            buffer\n        }\n    }\n\n    fn create_texture_with_data(\n        &self,\n        queue: &crate::Queue,\n        desc: &crate::TextureDescriptor<'_>,\n        order: TextureDataOrder,\n        data: &[u8],\n    ) -> crate::Texture {\n        // Implicitly add the COPY_DST usage\n        let mut desc = desc.to_owned();\n        desc.usage |= crate::TextureUsages::COPY_DST;\n        let texture = self.create_texture(&desc);\n\n        // Will return None only if it's a combined depth-stencil format\n        // If so, default to 4, validation will fail later anyway since the depth or stencil\n        // aspect needs to be written to individually\n        let block_size = desc.format.block_copy_size(None).unwrap_or(4);\n        let (block_width, block_height) = desc.format.block_dimensions();\n        let layer_iterations = desc.array_layer_count();\n\n        let outer_iteration;\n        let inner_iteration;\n        match order {\n            TextureDataOrder::LayerMajor => {\n                outer_iteration = layer_iterations;\n                inner_iteration = desc.mip_level_count;\n            }\n            TextureDataOrder::MipMajor => {\n                outer_iteration = desc.mip_level_count;\n                inner_iteration = layer_iterations;\n            }\n        }\n\n        let mut binary_offset = 0;\n        for outer in 0..outer_iteration {\n            for inner in 0..inner_iteration {\n                let (layer, mip) = match order {\n                    TextureDataOrder::LayerMajor => (outer, inner),\n                    TextureDataOrder::MipMajor => (inner, outer),\n                };\n\n                let mut mip_size = desc.mip_level_size(mip).unwrap();\n                // copying layers separately\n                if desc.dimension != wgt::TextureDimension::D3 {\n                    mip_size.depth_or_array_layers = 1;\n                }\n\n                // When uploading mips of compressed textures and the mip is supposed to be\n                // a size that isn't a multiple of the block size, the mip needs to be uploaded\n                // as its \"physical size\" which is the size rounded up to the nearest block size.\n                let mip_physical = mip_size.physical_size(desc.format);\n\n                // All these calculations are performed on the physical size as that's the\n                // data that exists in the buffer.\n                let width_blocks = mip_physical.width / block_width;\n                let height_blocks = mip_physical.height / block_height;\n\n                let bytes_per_row = width_blocks * block_size;\n                let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;\n\n                let end_offset = binary_offset + data_size as usize;\n\n                queue.write_texture(\n                    crate::TexelCopyTextureInfo {\n                        texture: &texture,\n                        mip_level: mip,\n                        origin: crate::Origin3d {\n                            x: 0,\n                            y: 0,\n                            z: layer,\n                        },\n                        aspect: wgt::TextureAspect::All,\n                    },\n                    &data[binary_offset..end_offset],\n                    crate::TexelCopyBufferLayout {\n                        offset: 0,\n                        bytes_per_row: Some(bytes_per_row),\n                        rows_per_image: Some(height_blocks),\n                    },\n                    mip_physical,\n                );\n\n                binary_offset = end_offset;\n            }\n        }\n\n        texture\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/encoder.rs",
    "content": "use core::ops::Range;\n\nuse wgt::{BufferAddress, DynamicOffset, IndexFormat};\n\nuse crate::{BindGroup, Buffer, BufferSlice, RenderBundleEncoder, RenderPass, RenderPipeline};\n\n/// Methods shared by [`RenderPass`] and [`RenderBundleEncoder`].\npub trait RenderEncoder<'a> {\n    /// Sets the active bind group for a given bind group index. The bind group layout\n    /// in the active pipeline when any `draw()` function is called must match the layout of this bind group.\n    ///\n    /// If the bind group have dynamic offsets, provide them in order of their declaration.\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&'a BindGroup>,\n        offsets: &[DynamicOffset],\n    );\n\n    /// Sets the active render pipeline.\n    ///\n    /// Subsequent draw calls will exhibit the behavior defined by `pipeline`.\n    fn set_pipeline(&mut self, pipeline: &'a RenderPipeline);\n\n    /// Sets the active index buffer.\n    ///\n    /// Subsequent calls to [`draw_indexed`](RenderEncoder::draw_indexed) on this [`RenderEncoder`] will\n    /// use `buffer` as the source index buffer.\n    fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat);\n\n    /// Assign a vertex buffer to a slot.\n    ///\n    /// Subsequent calls to [`draw`] and [`draw_indexed`] on this\n    /// [`RenderEncoder`] will use `buffer` as one of the source vertex buffers.\n    ///\n    /// The `slot` refers to the index of the matching descriptor in\n    /// [`VertexState::buffers`](crate::VertexState::buffers).\n    ///\n    /// [`draw`]: RenderEncoder::draw\n    /// [`draw_indexed`]: RenderEncoder::draw_indexed\n    fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>);\n\n    /// Draws primitives from the active vertex buffer(s).\n    ///\n    /// The active vertex buffers can be set with [`RenderEncoder::set_vertex_buffer`].\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>);\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffers.\n    ///\n    /// The active index buffer can be set with [`RenderEncoder::set_index_buffer`], while the active\n    /// vertex buffers can be set with [`RenderEncoder::set_vertex_buffer`].\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);\n\n    /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`.\n    ///\n    /// The active vertex buffers can be set with [`RenderEncoder::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).\n    fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress);\n\n    /// Draws indexed primitives using the active index buffer and the active vertex buffers,\n    /// based on the contents of the `indirect_buffer`.\n    ///\n    /// The active index buffer can be set with [`RenderEncoder::set_index_buffer`], while the active\n    /// vertex buffers can be set with [`RenderEncoder::set_vertex_buffer`].\n    ///\n    /// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &'a Buffer,\n        indirect_offset: BufferAddress,\n    );\n\n    /// [`wgt::Features::IMMEDIATES`] must be enabled on the device in order to call this function.\n    ///\n    /// Set immediate data for subsequent draw calls.\n    ///\n    /// Write the bytes in `data` at offset `offset` within immediate data\n    /// storage. Both `offset` and the length of `data` must be\n    /// multiples of [`crate::IMMEDIATE_DATA_ALIGNMENT`], which is always 4.\n    ///\n    /// For example, if `offset` is `4` and `data` is eight bytes long, this\n    /// call will write `data` to bytes `4..12` of immediate data storage.\n    fn set_immediates(&mut self, offset: u32, data: &[u8]);\n}\n\nimpl<'a> RenderEncoder<'a> for RenderPass<'a> {\n    #[inline(always)]\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&'a BindGroup>,\n        offsets: &[DynamicOffset],\n    ) {\n        Self::set_bind_group(self, index, bind_group, offsets);\n    }\n\n    #[inline(always)]\n    fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) {\n        Self::set_pipeline(self, pipeline);\n    }\n\n    #[inline(always)]\n    fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat) {\n        Self::set_index_buffer(self, buffer_slice, index_format);\n    }\n\n    #[inline(always)]\n    fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>) {\n        Self::set_vertex_buffer(self, slot, buffer_slice);\n    }\n\n    #[inline(always)]\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        Self::draw(self, vertices, instances);\n    }\n\n    #[inline(always)]\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        Self::draw_indexed(self, indices, base_vertex, instances);\n    }\n\n    #[inline(always)]\n    fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) {\n        Self::draw_indirect(self, indirect_buffer, indirect_offset);\n    }\n\n    #[inline(always)]\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &'a Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        Self::draw_indexed_indirect(self, indirect_buffer, indirect_offset);\n    }\n\n    #[inline(always)]\n    fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        Self::set_immediates(self, offset, data);\n    }\n}\n\nimpl<'a> RenderEncoder<'a> for RenderBundleEncoder<'a> {\n    #[inline(always)]\n    fn set_bind_group(\n        &mut self,\n        index: u32,\n        bind_group: Option<&'a BindGroup>,\n        offsets: &[DynamicOffset],\n    ) {\n        Self::set_bind_group(self, index, bind_group, offsets);\n    }\n\n    #[inline(always)]\n    fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) {\n        Self::set_pipeline(self, pipeline);\n    }\n\n    #[inline(always)]\n    fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat) {\n        Self::set_index_buffer(self, buffer_slice, index_format);\n    }\n\n    #[inline(always)]\n    fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>) {\n        Self::set_vertex_buffer(self, slot, buffer_slice);\n    }\n\n    #[inline(always)]\n    fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {\n        Self::draw(self, vertices, instances);\n    }\n\n    #[inline(always)]\n    fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {\n        Self::draw_indexed(self, indices, base_vertex, instances);\n    }\n\n    #[inline(always)]\n    fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) {\n        Self::draw_indirect(self, indirect_buffer, indirect_offset);\n    }\n\n    #[inline(always)]\n    fn draw_indexed_indirect(\n        &mut self,\n        indirect_buffer: &'a Buffer,\n        indirect_offset: BufferAddress,\n    ) {\n        Self::draw_indexed_indirect(self, indirect_buffer, indirect_offset);\n    }\n\n    #[inline(always)]\n    fn set_immediates(&mut self, offset: u32, data: &[u8]) {\n        Self::set_immediates(self, offset, data);\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/init.rs",
    "content": "use crate::{Adapter, Instance, RequestAdapterOptions, Surface};\n\n#[cfg(doc)]\nuse crate::Backends;\n\n/// Initialize the adapter obeying the `WGPU_ADAPTER_NAME` environment variable.\n#[cfg(wgpu_core)]\n#[cfg_attr(not(std), expect(unused_variables, unreachable_code))]\npub async fn initialize_adapter_from_env(\n    instance: &Instance,\n    compatible_surface: Option<&Surface<'_>>,\n) -> Result<Adapter, wgt::RequestAdapterError> {\n    let desired_adapter_name: alloc::string::String = {\n        cfg_if::cfg_if! {\n            if #[cfg(std)] {\n                std::env::var(\"WGPU_ADAPTER_NAME\")\n                    .as_deref()\n                    .map(str::to_lowercase)\n                    .map_err(|_| wgt::RequestAdapterError::EnvNotSet)?\n            } else {\n                return Err(wgt::RequestAdapterError::EnvNotSet)\n            }\n        }\n    };\n\n    let adapters = instance.enumerate_adapters(crate::Backends::all()).await;\n\n    let mut chosen_adapter = None;\n    for adapter in adapters {\n        let info = adapter.get_info();\n\n        if let Some(surface) = compatible_surface {\n            if !adapter.is_surface_supported(surface) {\n                continue;\n            }\n        }\n\n        if info.name.to_lowercase().contains(&desired_adapter_name) {\n            chosen_adapter = Some(adapter);\n            break;\n        }\n    }\n\n    Ok(chosen_adapter.expect(\"WGPU_ADAPTER_NAME set but no matching adapter found!\"))\n}\n\n/// Initialize the adapter obeying the `WGPU_ADAPTER_NAME` environment variable.\n#[cfg(not(wgpu_core))]\npub async fn initialize_adapter_from_env(\n    _instance: &Instance,\n    _compatible_surface: Option<&Surface<'_>>,\n) -> Result<Adapter, wgt::RequestAdapterError> {\n    Err(wgt::RequestAdapterError::EnvNotSet)\n}\n\n/// Initialize the adapter obeying the `WGPU_ADAPTER_NAME` environment variable and if it doesn't exist fall back on a default adapter.\npub async fn initialize_adapter_from_env_or_default(\n    instance: &Instance,\n    compatible_surface: Option<&Surface<'_>>,\n) -> Result<Adapter, wgt::RequestAdapterError> {\n    match initialize_adapter_from_env(instance, compatible_surface).await {\n        Ok(a) => Ok(a),\n        Err(_) => {\n            instance\n                .request_adapter(&RequestAdapterOptions {\n                    power_preference: crate::PowerPreference::from_env().unwrap_or_default(),\n                    force_fallback_adapter: false,\n                    compatible_surface,\n                })\n                .await\n        }\n    }\n}\n\n/// Determines whether the [`Backends::BROWSER_WEBGPU`] backend is supported.\n///\n/// The result can only be true if this is called from the main thread or a dedicated worker.\n/// For convenience, this is also supported on non-wasm targets, always returning false there.\npub async fn is_browser_webgpu_supported() -> bool {\n    #[cfg(webgpu)]\n    {\n        // In theory it should be enough to check for the presence of the `gpu` property...\n        let gpu = crate::backend::get_browser_gpu_property();\n        let Ok(Some(gpu)) = gpu else {\n            return false;\n        };\n\n        // ...but in practice, we also have to try to create an adapter, since as of writing\n        // Chrome on Linux has the `gpu` property but doesn't support WebGPU.\n        let adapter_promise = gpu.request_adapter();\n        wasm_bindgen_futures::JsFuture::from(adapter_promise)\n            .await\n            .is_ok_and(|adapter| !adapter.is_undefined() && !adapter.is_null())\n    }\n    #[cfg(not(webgpu))]\n    {\n        false\n    }\n}\n\n/// Create an new instance of wgpu, but disabling [`Backends::BROWSER_WEBGPU`] if no WebGPU support was detected.\n///\n/// If the instance descriptor enables [`Backends::BROWSER_WEBGPU`],\n/// this checks via [`is_browser_webgpu_supported`] for WebGPU support before forwarding\n/// the descriptor with or without [`Backends::BROWSER_WEBGPU`] respectively to [`Instance::new`].\n///\n/// You should prefer this method over [`Instance::new`] if you want to target WebGPU and automatically\n/// fall back to WebGL if WebGPU is not available.\n/// This is because WebGPU support has to be decided upon instance creation and [`Instance::new`]\n/// (being a `sync` function) can't establish WebGPU support (details see [`is_browser_webgpu_supported`]).\n///\n/// # Panics\n///\n/// If no backend feature for the active target platform is enabled,\n/// this method will panic, see [`Instance::enabled_backend_features()`].\npub async fn new_instance_with_webgpu_detection(\n    mut instance_desc: wgt::InstanceDescriptor,\n) -> crate::Instance {\n    if instance_desc\n        .backends\n        .contains(wgt::Backends::BROWSER_WEBGPU)\n        && !is_browser_webgpu_supported().await\n    {\n        instance_desc.backends.remove(wgt::Backends::BROWSER_WEBGPU);\n    }\n\n    crate::Instance::new(instance_desc)\n}\n"
  },
  {
    "path": "wgpu/src/util/mod.rs",
    "content": "//! Utility structures and functions that are built on top of the main `wgpu` API.\n//!\n//! Nothing in this module is a part of the WebGPU API specification;\n//! they are unique to the `wgpu` library.\n\n// TODO: For [`belt::StagingBelt`] to be available in `no_std` its usage of [`std::sync::mpsc`]\n// must be replaced with an appropriate alternative.\n#[cfg(std)]\nmod belt;\nmod device;\nmod encoder;\nmod init;\nmod mutex;\nmod panicking;\nmod spirv;\nmod texture_blitter;\n\nuse alloc::{format, string::String};\n\n#[cfg(std)]\npub use belt::StagingBelt;\npub use device::{BufferInitDescriptor, DeviceExt};\npub use encoder::RenderEncoder;\npub use init::*;\npub use spirv::*;\n#[cfg(feature = \"wgsl\")]\npub use texture_blitter::{TextureBlitter, TextureBlitterBuilder};\npub use wgt::{\n    math::*, DispatchIndirectArgs, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder,\n};\n\npub(crate) use mutex::Mutex;\npub(crate) use panicking::is_panicking;\n\nuse crate::dispatch;\n\n/// CPU accessible buffer used to download data back from the GPU.\npub struct DownloadBuffer {\n    _gpu_buffer: super::Buffer,\n    mapped_range: dispatch::DispatchBufferMappedRange,\n}\n\nimpl DownloadBuffer {\n    /// Asynchronously read the contents of a buffer.\n    pub fn read_buffer(\n        device: &super::Device,\n        queue: &super::Queue,\n        buffer: &super::BufferSlice<'_>,\n        callback: impl FnOnce(Result<Self, super::BufferAsyncError>) + Send + 'static,\n    ) {\n        let size = buffer.size.into();\n\n        let download = device.create_buffer(&super::BufferDescriptor {\n            size,\n            usage: super::BufferUsages::COPY_DST | super::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n            label: None,\n        });\n\n        let mut encoder =\n            device.create_command_encoder(&super::CommandEncoderDescriptor { label: None });\n        encoder.copy_buffer_to_buffer(buffer.buffer, buffer.offset, &download, 0, size);\n        let command_buffer: super::CommandBuffer = encoder.finish();\n        queue.submit(Some(command_buffer));\n\n        download\n            .clone()\n            .slice(..)\n            .map_async(super::MapMode::Read, move |result| {\n                if let Err(e) = result {\n                    callback(Err(e));\n                    return;\n                }\n\n                let mapped_range = download.inner.get_mapped_range(0..size);\n                callback(Ok(Self {\n                    _gpu_buffer: download,\n                    mapped_range,\n                }));\n            });\n    }\n}\n\nimpl core::ops::Deref for DownloadBuffer {\n    type Target = [u8];\n    fn deref(&self) -> &[u8] {\n        // SAFETY: `self.mapped_range` is always a read mapping\n        unsafe { self.mapped_range.read_slice() }\n    }\n}\n\n/// A recommended key for storing [`PipelineCache`]s for the adapter\n/// associated with the given [`AdapterInfo`](wgt::AdapterInfo)\n/// This key will define a class of adapters for which the same cache\n/// might be valid.\n///\n/// If this returns `None`, the adapter doesn't support [`PipelineCache`].\n/// This may be because the API doesn't support application managed caches\n/// (such as browser WebGPU), or that `wgpu` hasn't implemented it for\n/// that API yet.\n///\n/// This key could be used as a filename, as seen in the example below.\n///\n/// # Examples\n///\n/// ```no_run\n/// # use std::path::PathBuf;\n/// use wgpu::PipelineCacheDescriptor;\n/// # let adapter_info = todo!();\n/// # let device: wgpu::Device = todo!();\n/// let cache_dir: PathBuf = unimplemented!(\"Some reasonable platform-specific cache directory for your app.\");\n/// let filename = wgpu::util::pipeline_cache_key(&adapter_info);\n/// let (pipeline_cache, cache_file) = if let Some(filename) = filename {\n///     let cache_path = cache_dir.join(&filename);\n///     // If we failed to read the cache, for whatever reason, treat the data as lost.\n///     // In a real app, we'd probably avoid caching entirely unless the error was \"file not found\".\n///     let cache_data = std::fs::read(&cache_path).ok();\n///     let pipeline_cache = unsafe {\n///         device.create_pipeline_cache(&PipelineCacheDescriptor {\n///             data: cache_data.as_deref(),\n///             label: None,\n///             fallback: true\n///         })\n///     };\n///     (Some(pipeline_cache), Some(cache_path))\n/// } else {\n///     (None, None)\n/// };\n///\n/// // Run pipeline initialisation, making sure to set the `cache`\n/// // fields of your `*PipelineDescriptor` to `pipeline_cache`\n///\n/// // And then save the resulting cache (probably off the main thread).\n/// if let (Some(pipeline_cache), Some(cache_file)) = (pipeline_cache, cache_file) {\n///     let data = pipeline_cache.get_data();\n///     if let Some(data) = data {\n///         let temp_file = cache_file.with_extension(\"temp\");\n///         std::fs::write(&temp_file, &data)?;\n///         std::fs::rename(&temp_file, &cache_file)?;\n///     }\n/// }\n/// # Ok::<_, std::io::Error>(())\n/// ```\n///\n/// [`PipelineCache`]: super::PipelineCache\npub fn pipeline_cache_key(adapter_info: &wgt::AdapterInfo) -> Option<String> {\n    match adapter_info.backend {\n        wgt::Backend::Vulkan => Some(format!(\n            // The vendor/device should uniquely define a driver\n            // We/the driver will also later validate that the vendor/device and driver\n            // version match, which may lead to clearing an outdated\n            // cache for the same device.\n            \"wgpu_pipeline_cache_vulkan_{}_{}\",\n            adapter_info.vendor, adapter_info.device\n        )),\n        _ => None,\n    }\n}\n\n/// Adds extra conversion functions to `TextureFormat`.\npub trait TextureFormatExt {\n    /// Finds the [`TextureFormat`](wgt::TextureFormat) corresponding to the given\n    /// [`StorageFormat`](wgc::naga::StorageFormat).\n    ///\n    /// # Examples\n    /// ```\n    /// use wgpu::util::TextureFormatExt;\n    /// assert_eq!(wgpu::TextureFormat::from_storage_format(wgpu::naga::StorageFormat::Bgra8Unorm), wgpu::TextureFormat::Bgra8Unorm);\n    /// ```\n    #[cfg(wgpu_core)]\n    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self;\n\n    /// Finds the [`StorageFormat`](wgc::naga::StorageFormat) corresponding to the given [`TextureFormat`](wgt::TextureFormat).\n    /// Returns `None` if there is no matching storage format,\n    /// which typically indicates this format is not supported\n    /// for storage textures.\n    ///\n    /// # Examples\n    /// ```\n    /// use wgpu::util::TextureFormatExt;\n    /// assert_eq!(wgpu::TextureFormat::Bgra8Unorm.to_storage_format(), Some(wgpu::naga::StorageFormat::Bgra8Unorm));\n    /// ```\n    #[cfg(wgpu_core)]\n    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat>;\n}\n\nimpl TextureFormatExt for wgt::TextureFormat {\n    #[cfg(wgpu_core)]\n    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self {\n        wgc::map_storage_format_from_naga(storage_format)\n    }\n\n    #[cfg(wgpu_core)]\n    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat> {\n        wgc::map_storage_format_to_naga(*self)\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/mutex.rs",
    "content": "//! Provides a [`Mutex`] for internal use based on what features are available.\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"parking_lot\")] {\n        use parking_lot::Mutex as MutexInner;\n    } else if #[cfg(std)] {\n        use std::sync::Mutex as MutexInner;\n    } else {\n        use core::cell::RefCell as MutexInner;\n    }\n}\n\npub(crate) struct Mutex<T: ?Sized> {\n    inner: MutexInner<T>,\n}\n\nimpl<T: ?Sized> core::fmt::Debug for Mutex<T>\nwhere\n    MutexInner<T>: core::fmt::Debug,\n{\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        <MutexInner<T> as core::fmt::Debug>::fmt(&self.inner, f)\n    }\n}\n\nimpl<T: Default> Default for Mutex<T> {\n    fn default() -> Self {\n        Self::new(<T as Default>::default())\n    }\n}\n\nimpl<T> Mutex<T> {\n    pub const fn new(value: T) -> Self {\n        Self {\n            inner: MutexInner::new(value),\n        }\n    }\n}\n\nimpl<T: ?Sized> Mutex<T> {\n    pub fn lock(&self) -> impl core::ops::DerefMut<Target = T> + '_ {\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"parking_lot\")] {\n                self.inner.lock()\n            } else if #[cfg(std)] {\n                self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner)\n            } else {\n                loop {\n                    let Ok(lock) = self.inner.try_borrow_mut() else {\n                        // Without `std` all we can do is spin until the current lock is released\n                        core::hint::spin_loop();\n                        continue;\n                    };\n\n                    break lock;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/panicking.rs",
    "content": "#[cfg(feature = \"std\")]\npub fn is_panicking() -> bool {\n    std::thread::panicking()\n}\n\n#[cfg(not(feature = \"std\"))]\npub fn is_panicking() -> bool {\n    false\n}\n"
  },
  {
    "path": "wgpu/src/util/spirv.rs",
    "content": "//! Utilities for loading SPIR-V module data.\n\nuse alloc::borrow::Cow;\nuse core::mem;\n\n#[cfg_attr(not(any(feature = \"spirv\", doc)), expect(unused_imports))]\nuse crate::ShaderSource;\n\n#[cfg(doc)]\nuse crate::Device;\n\nconst SPIRV_MAGIC_NUMBER: u32 = 0x0723_0203;\n\n/// Treat the given byte slice as a SPIR-V module.\n///\n/// # Panics\n///\n/// This function panics if:\n///\n/// - `data.len()` is not a multiple of 4\n/// - `data` does not begin with the SPIR-V magic number\n///\n/// It does not check that the data is a valid SPIR-V module in any other way.\n#[cfg(feature = \"spirv\")] // ShaderSource::SpirV only exists in this case\npub fn make_spirv(data: &[u8]) -> ShaderSource<'_> {\n    ShaderSource::SpirV(make_spirv_raw(data))\n}\n\n/// Check whether the byte slice has the SPIR-V magic number (in either byte order) and of an\n/// appropriate size, and panic with a suitable message when it is not.\n///\n/// Returns whether the endianness is opposite of native endianness (i.e. whether\n/// [`u32::swap_bytes()`] should be called.)\n///\n/// Note: this function’s checks are relied upon for the soundness of [`make_spirv_const()`].\n/// Undefined behavior will result if it does not panic when `bytes.len()` is not a multiple of 4.\n#[track_caller]\nconst fn assert_has_spirv_magic_number_and_length(bytes: &[u8]) -> bool {\n    // First, check the magic number.\n    // This way we give the best error for wrong formats.\n    // (Plus a special case for the empty slice.)\n    let found_magic_number: Option<bool> = match *bytes {\n        [] => panic!(\"byte slice is empty, not SPIR-V\"),\n        // This would be simpler as slice::starts_with(), but that isn't a const fn yet.\n        [b1, b2, b3, b4, ..] => {\n            let prefix = u32::from_ne_bytes([b1, b2, b3, b4]);\n            if prefix == SPIRV_MAGIC_NUMBER {\n                Some(false)\n            } else if prefix == const { SPIRV_MAGIC_NUMBER.swap_bytes() } {\n                // needs swapping\n                Some(true)\n            } else {\n                None\n            }\n        }\n        _ => None, // fallthrough case = between 1 and 3 bytes\n    };\n\n    match found_magic_number {\n        Some(needs_byte_swap) => {\n            // Note: this assertion is relied upon for the soundness of `make_spirv_const()`.\n            assert!(\n                bytes.len().is_multiple_of(mem::size_of::<u32>()),\n                \"SPIR-V data must be a multiple of 4 bytes long\"\n            );\n\n            needs_byte_swap\n        }\n        None => {\n            panic!(\n                \"byte slice does not start with SPIR-V magic number. \\\n            Make sure you are using a binary SPIR-V file.\"\n            );\n        }\n    }\n}\n\n#[cfg_attr(not(feature = \"spirv\"), expect(rustdoc::broken_intra_doc_links))]\n/// Version of [`make_spirv()`] intended for use with\n/// [`Device::create_shader_module_passthrough()`].\n///\n/// Returns a raw slice instead of [`ShaderSource`].\n///\n/// # Panics\n///\n/// This function panics if:\n///\n/// - `data.len()` is not a multiple of 4\n/// - `data` does not begin with the SPIR-V magic number\n///\n/// It does not check that the data is a valid SPIR-V module in any other way.\npub fn make_spirv_raw(bytes: &[u8]) -> Cow<'_, [u32]> {\n    let needs_byte_swap = assert_has_spirv_magic_number_and_length(bytes);\n\n    // If the data happens to be aligned, directly use the byte array,\n    // otherwise copy the byte array in an owned vector and use that instead.\n    let mut words: Cow<'_, [u32]> = match bytemuck::try_cast_slice(bytes) {\n        Ok(words) => Cow::Borrowed(words),\n        // We already checked the length, so if this fails, it fails due to lack of alignment only.\n        Err(_) => Cow::Owned(bytemuck::pod_collect_to_vec(bytes)),\n    };\n\n    // If necessary, swap bytes to native endianness.\n    if needs_byte_swap {\n        for word in Cow::to_mut(&mut words) {\n            *word = word.swap_bytes();\n        }\n    }\n\n    assert!(\n        words[0] == SPIRV_MAGIC_NUMBER,\n        \"can't happen: wrong magic number after swap_bytes\"\n    );\n    words\n}\n\n/// Version of `make_spirv_raw` used for implementing [`include_spirv!`] and [`include_spirv_raw!`] macros.\n///\n/// Not public API. Also, don't even try calling at runtime; you'll get a stack overflow.\n///\n/// [`include_spirv!`]: crate::include_spirv\n#[doc(hidden)]\npub const fn make_spirv_const<const IN: usize, const OUT: usize>(bytes: [u8; IN]) -> [u32; OUT] {\n    let needs_byte_swap = assert_has_spirv_magic_number_and_length(&bytes);\n\n    // NOTE: to get around lack of generic const expressions, the input and output lengths must\n    // be specified separately.\n    // Check that they are consistent with each other.\n    assert!(mem::size_of_val(&bytes) == mem::size_of::<[u32; OUT]>());\n\n    // Can't use `bytemuck` in `const fn` (yet), so do it unsafely.\n    // SAFETY:\n    // * The previous assertion checked that the byte sizes of `bytes` and `words` are equal.\n    // * `transmute_copy` doesn't care that the alignment might be wrong.\n    let mut words: [u32; OUT] = unsafe { mem::transmute_copy(&bytes) };\n\n    // If necessary, swap bytes to native endianness.\n    if needs_byte_swap {\n        let mut idx = 0;\n        while idx < words.len() {\n            words[idx] = words[idx].swap_bytes();\n            idx += 1;\n        }\n    }\n\n    assert!(\n        words[0] == SPIRV_MAGIC_NUMBER,\n        \"can't happen: wrong magic number after swap_bytes\"\n    );\n\n    words\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use alloc::vec;\n\n    fn test_success_with_misalignments<const IN: usize, const OUT: usize>(\n        input: &[u8; IN],\n        expected: [u32; OUT],\n    ) {\n        // We don't know which 3 out of 4 offsets will produce an actually misaligned slice,\n        // but they always will. (Note that it is necessary to reuse the same allocation for all 4\n        // tests, or we could (in theory) get unlucky and not test any misalignments.)\n        let mut buffer = vec![0; input.len() + 4];\n        for offset in 0..4 {\n            let misaligned_slice: &mut [u8; IN] =\n                (&mut buffer[offset..][..input.len()]).try_into().unwrap();\n\n            misaligned_slice.copy_from_slice(input);\n            assert_eq!(*make_spirv_raw(misaligned_slice), expected);\n            assert_eq!(make_spirv_const(*misaligned_slice), expected);\n        }\n    }\n\n    #[test]\n    fn success_be() {\n        // magic number followed by dummy data to see the endianness handling\n        let input = b\"\\x07\\x23\\x02\\x03\\xF1\\xF2\\xF3\\xF4\";\n        let expected: [u32; 2] = [SPIRV_MAGIC_NUMBER, 0xF1F2F3F4];\n        test_success_with_misalignments(input, expected);\n    }\n\n    #[test]\n    fn success_le() {\n        let input = b\"\\x03\\x02\\x23\\x07\\xF1\\xF2\\xF3\\xF4\";\n        let expected: [u32; 2] = [SPIRV_MAGIC_NUMBER, 0xF4F3F2F1];\n        test_success_with_misalignments(input, expected);\n    }\n\n    #[should_panic = \"multiple of 4\"]\n    #[test]\n    fn nonconst_le_fail() {\n        let _: Cow<'_, [u32]> = make_spirv_raw(&[0x03, 0x02, 0x23, 0x07, 0x44, 0x33]);\n    }\n\n    #[should_panic = \"multiple of 4\"]\n    #[test]\n    fn nonconst_be_fail() {\n        let _: Cow<'_, [u32]> = make_spirv_raw(&[0x07, 0x23, 0x02, 0x03, 0x11, 0x22]);\n    }\n\n    #[should_panic = \"multiple of 4\"]\n    #[test]\n    fn const_le_fail() {\n        let _: [u32; 1] = make_spirv_const([0x03, 0x02, 0x23, 0x07, 0x44, 0x33]);\n    }\n\n    #[should_panic = \"multiple of 4\"]\n    #[test]\n    fn const_be_fail() {\n        let _: [u32; 1] = make_spirv_const([0x07, 0x23, 0x02, 0x03, 0x11, 0x22]);\n    }\n\n    #[should_panic = \"byte slice is empty, not SPIR-V\"]\n    #[test]\n    fn make_spirv_empty() {\n        let _: [u32; 0] = make_spirv_const([]);\n    }\n}\n"
  },
  {
    "path": "wgpu/src/util/texture_blitter.rs",
    "content": "#![cfg(feature = \"wgsl\")]\n\nuse wgt::BlendState;\n\nuse crate::{\n    include_wgsl, AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,\n    BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, ColorTargetState, ColorWrites,\n    CommandEncoder, Device, FilterMode, FragmentState, FrontFace, LoadOp, MultisampleState,\n    PipelineCompilationOptions, PipelineLayoutDescriptor, PrimitiveState, PrimitiveTopology,\n    RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType,\n    SamplerDescriptor, ShaderStages, StoreOp, TextureFormat, TextureSampleType, TextureView,\n    TextureViewDimension, VertexState,\n};\n\n/// A builder for the [`TextureBlitter`] utility.\n/// If you want the default [`TextureBlitter`] use [`TextureBlitter::new`] instead.\npub struct TextureBlitterBuilder<'a> {\n    device: &'a Device,\n    format: TextureFormat,\n    sample_type: FilterMode,\n    blend_state: Option<BlendState>,\n}\n\nimpl<'a> TextureBlitterBuilder<'a> {\n    /// Returns a new [`TextureBlitterBuilder`]\n    ///\n    /// # Arguments\n    /// - `device` - A [`Device`]\n    /// - `format` - The [`TextureFormat`] of the texture that will be copied to. This has to have the `RENDER_TARGET` usage.\n    pub fn new(device: &'a Device, format: TextureFormat) -> Self {\n        Self {\n            device,\n            format,\n            sample_type: FilterMode::Nearest,\n            blend_state: None,\n        }\n    }\n\n    /// Sets the [`Sampler`] Filtering Mode\n    pub fn sample_type(mut self, sample_type: FilterMode) -> Self {\n        self.sample_type = sample_type;\n        self\n    }\n\n    /// Sets the [`BlendState`] that is used.\n    pub fn blend_state(mut self, blend_state: BlendState) -> Self {\n        self.blend_state = Some(blend_state);\n        self\n    }\n\n    /// Returns a new [`TextureBlitter`] with given settings.\n    pub fn build(self) -> TextureBlitter {\n        let sampler = self.device.create_sampler(&SamplerDescriptor {\n            label: Some(\"wgpu::util::TextureBlitter::sampler\"),\n            address_mode_u: AddressMode::ClampToEdge,\n            address_mode_v: AddressMode::ClampToEdge,\n            address_mode_w: AddressMode::ClampToEdge,\n            mag_filter: self.sample_type,\n            ..Default::default()\n        });\n\n        let bind_group_layout = self\n            .device\n            .create_bind_group_layout(&BindGroupLayoutDescriptor {\n                label: Some(\"wgpu::util::TextureBlitter::bind_group_layout\"),\n                entries: &[\n                    BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Texture {\n                            sample_type: TextureSampleType::Float {\n                                filterable: self.sample_type == FilterMode::Linear,\n                            },\n                            view_dimension: TextureViewDimension::D2,\n                            multisampled: false,\n                        },\n                        count: None,\n                    },\n                    BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: ShaderStages::FRAGMENT,\n                        ty: BindingType::Sampler(if self.sample_type == FilterMode::Linear {\n                            SamplerBindingType::Filtering\n                        } else {\n                            SamplerBindingType::NonFiltering\n                        }),\n                        count: None,\n                    },\n                ],\n            });\n\n        let pipeline_layout = self\n            .device\n            .create_pipeline_layout(&PipelineLayoutDescriptor {\n                label: Some(\"wgpu::util::TextureBlitter::pipeline_layout\"),\n                bind_group_layouts: &[Some(&bind_group_layout)],\n                immediate_size: 0,\n            });\n\n        let shader = self.device.create_shader_module(include_wgsl!(\"blit.wgsl\"));\n        let pipeline = self\n            .device\n            .create_render_pipeline(&RenderPipelineDescriptor {\n                label: Some(\"wgpu::util::TextureBlitter::pipeline\"),\n                layout: Some(&pipeline_layout),\n                vertex: VertexState {\n                    module: &shader,\n                    entry_point: Some(\"vs_main\"),\n                    compilation_options: PipelineCompilationOptions::default(),\n                    buffers: &[],\n                },\n                primitive: PrimitiveState {\n                    topology: PrimitiveTopology::TriangleList,\n                    strip_index_format: None,\n                    front_face: FrontFace::Ccw,\n                    cull_mode: None,\n                    unclipped_depth: false,\n                    polygon_mode: wgt::PolygonMode::Fill,\n                    conservative: false,\n                },\n                depth_stencil: None,\n                multisample: MultisampleState::default(),\n                fragment: Some(FragmentState {\n                    module: &shader,\n                    entry_point: Some(\"fs_main\"),\n                    compilation_options: PipelineCompilationOptions::default(),\n                    targets: &[Some(ColorTargetState {\n                        format: self.format,\n                        blend: self.blend_state,\n                        write_mask: ColorWrites::ALL,\n                    })],\n                }),\n                multiview_mask: None,\n                cache: None,\n            });\n\n        TextureBlitter {\n            pipeline,\n            bind_group_layout,\n            sampler,\n        }\n    }\n}\n\n/// Texture Blitting (Copying) Utility\n///\n/// Use this if you want to just render/copy texture A to texture B where [`CommandEncoder::copy_texture_to_texture`] would not work because:\n/// - Textures are in incompatible formats.\n/// - Textures are of different sizes.\n/// - Your copy destination is the surface texture and does not have the `COPY_DST` usage.\npub struct TextureBlitter {\n    pipeline: RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    sampler: Sampler,\n}\n\nimpl TextureBlitter {\n    /// Returns a [`TextureBlitter`] with default settings.\n    ///\n    /// # Arguments\n    /// - `device` - A [`Device`]\n    /// - `format` - The [`TextureFormat`] of the texture that will be copied to. This has to have the `RENDER_TARGET` usage.\n    ///\n    /// Properties of the blitting (such as the [`BlendState`]) can be customised by using [`TextureBlitterBuilder`] instead.\n    pub fn new(device: &Device, format: TextureFormat) -> Self {\n        TextureBlitterBuilder::new(device, format).build()\n    }\n\n    /// Copies the data from the source [`TextureView`] to the target [`TextureView`]\n    ///\n    /// # Arguments\n    /// - `device` - A [`Device`]\n    /// - `encoder` - A [`CommandEncoder`]\n    /// - `source` - A [`TextureView`] that gets copied. The format does not matter.\n    /// - `target` - A [`TextureView`] that gets the data copied from the `source`. It has to be the same format as the format specified in [`TextureBlitter::new`]\n    pub fn copy(\n        &self,\n        device: &Device,\n        encoder: &mut CommandEncoder,\n        source: &TextureView,\n        target: &TextureView,\n    ) {\n        let bind_group = device.create_bind_group(&BindGroupDescriptor {\n            label: Some(\"wgpu::util::TextureBlitter::bind_group\"),\n            layout: &self.bind_group_layout,\n            entries: &[\n                BindGroupEntry {\n                    binding: 0,\n                    resource: crate::BindingResource::TextureView(source),\n                },\n                BindGroupEntry {\n                    binding: 1,\n                    resource: crate::BindingResource::Sampler(&self.sampler),\n                },\n            ],\n        });\n\n        let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {\n            label: Some(\"wgpu::util::TextureBlitter::pass\"),\n            color_attachments: &[Some(crate::RenderPassColorAttachment {\n                view: target,\n                depth_slice: None,\n                resolve_target: None,\n                ops: wgt::Operations {\n                    load: LoadOp::Load,\n                    store: StoreOp::Store,\n                },\n            })],\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n        });\n        pass.set_pipeline(&self.pipeline);\n        pass.set_bind_group(0, &bind_group, &[]);\n        pass.draw(0..3, 0..1);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/Cargo.toml",
    "content": "[package]\nname = \"wgpu-core\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Core implementation logic of wgpu, the cross-platform, safe, pure-rust graphics API\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\n\n# Override the workspace's `rust-version` key. `wgpu-core` and its dependencies\n# have a less strict MSRV, to allow firefox more leeway in updating their Rust toolchain.\n#\n# See the repo README for more information on MSRV policy.\nrust-version = \"1.87\"\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\ntargets = [\n    \"x86_64-unknown-linux-gnu\",\n    \"x86_64-apple-darwin\",\n    \"x86_64-pc-windows-msvc\",\n    \"wasm32-unknown-unknown\",\n]\n\n[package.metadata.cargo-machete]\n# Cargo machete can't check build.rs dependencies. See https://github.com/bnjbvr/cargo-machete/issues/100\nignored = [\"cfg_aliases\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(wgpu_validate_locks)'] }\n\n[lib]\n\n[features]\n#! See documentation for the `wgpu` crate for more in-depth information on these features.\n\n# TODO(https://github.com/gfx-rs/wgpu/issues/6826): \"std\" is a default feature for\n# compatibility with prior behavior only, and should be removed once we know how\n# wgpu-core’s dependents want to handle no_std.\ndefault = [\"std\"]\n\n#! ### Logging Configuration\n# --------------------------------------------------------------------\n\n## Log all API entry points at info instead of trace level.\n## Also, promotes certain debug log calls to info.\napi_log_info = []\n\n## Log resource lifecycle management at info instead of trace level.\nresource_log_info = []\n\n#! ### Runtime Checks\n# --------------------------------------------------------------------\n\n## Apply run-time checks, even in release builds. These are in addition\n## to the validation carried out at public APIs in all builds.\nstrict_asserts = [\"wgpu-types/strict_asserts\"]\n\n#! ### Debugging\n# --------------------------------------------------------------------\n\n## Enable lock order observation.\nobserve_locks = [\"std\", \"dep:ron\", \"serde/serde_derive\"]\n\n#! ### Serialization\n# --------------------------------------------------------------------\n\n## Enables serialization via `serde` on common wgpu types.\nserde = [\n    \"dep:serde\",\n    \"wgpu-types/serde\",\n    \"arrayvec/serde\",\n    \"hashbrown/serde\",\n    \"smallvec/serde\",\n    \"macro_rules_attribute\",\n]\n\n## Enable API tracing.\ntrace = [\"serde\", \"std\", \"dep:ron\", \"naga/serialize\", \"wgpu-types/trace\"]\n\n## Enable API replaying\nreplay = [\"serde\", \"naga/deserialize\"]\n\n#! ### Shading Language Support\n# --------------------------------------------------------------------\n\n## Enable `ShaderModuleSource::Wgsl`\nwgsl = [\"naga/wgsl-in\"]\n\n## Enable `ShaderModuleSource::Glsl`\nglsl = [\"naga/glsl-in\"]\n\n## Enable `ShaderModuleSource::SpirV`\nspirv = [\"naga/spv-in\"]\n\n#! ### Other\n# --------------------------------------------------------------------\n\n## Internally count resources and events for debugging purposes. If the counters\n## feature is disabled, the counting infrastructure is removed from the build and\n## the exposed counters always return 0.\ncounters = [\"wgpu-types/counters\"]\n\n## Implement `Send` and `Sync` on Wasm, but only if atomics are not enabled.\nfragile-send-sync-non-atomic-wasm = [\n    \"wgpu-hal/fragile-send-sync-non-atomic-wasm\",\n]\n\n## Enable certain items to be `Send` and `Sync` when they would not otherwise be.\n## Also enables backtraces in some error cases when also under cfg(debug_assertions).\nstd = []\n\n#! ### External libraries\n# --------------------------------------------------------------------\n#! The following features facilitate integration with third-party supporting libraries.\n\n## Enable using the `mach-dxcompiler-rs` crate to compile DX12 shaders.\nstatic-dxc = [\"wgpu-hal/static-dxc\"]\n\n## Enable portable atomics on platforms that do not support 64bit atomics.\nportable-atomic = [\"dep:portable-atomic\", \"wgpu-hal/portable-atomic\"]\n\n#! ### Target Conditional Features\n# --------------------------------------------------------------------\n# Look to wgpu-hal's Cargo.toml for explaination how these features and the wgpu-core\n# platform crates collude to provide platform-specific behavior.\n\n## DX12 backend\ndx12 = [\"wgpu-core-deps-windows-linux-android/dx12\"]\n## Metal backend\nmetal = [\"wgpu-core-deps-apple/metal\"]\n## Vulkan backend, only available on Windows, Linux, Android\nvulkan = [\"wgpu-core-deps-windows-linux-android/vulkan\"]\n## OpenGL backend, only available on Windows, Linux, Android, and Emscripten\ngles = [\n    \"wgpu-core-deps-windows-linux-android/gles\",\n    \"wgpu-core-deps-emscripten/gles\",\n]\n\n## WebGL backend, only available on Emscripten\nwebgl = [\"wgpu-core-deps-wasm/webgl\", \"wgpu-types/web\"]\n## OpenGL backend, on macOS only\nangle = [\"wgpu-core-deps-apple/angle\"]\n## Vulkan portability backend, only available on macOS\nvulkan-portability = [\"wgpu-core-deps-apple/vulkan-portability\"]\n## Renderdoc integration, only available on Windows, Linux, and Android\nrenderdoc = [\"wgpu-core-deps-windows-linux-android/renderdoc\"]\n\n## Enable the `noop` backend.\n# TODO(https://github.com/gfx-rs/wgpu/issues/7120): there should be a hal feature\nnoop = []\n\n# The target limitation here isn't needed, but prevents more than one of these\n# platform crates from being included in the build at a time, preventing users\n# from getting confused by seeing them in the list of crates.\n[target.'cfg(target_vendor = \"apple\")'.dependencies]\nwgpu-core-deps-apple = { workspace = true, optional = true }\n[target.'cfg(target_os = \"emscripten\")'.dependencies]\nwgpu-core-deps-emscripten = { workspace = true, optional = true }\n[target.'cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))'.dependencies]\nwgpu-core-deps-wasm = { workspace = true, optional = true }\n[target.'cfg(any(windows, target_os = \"linux\", target_os = \"android\", target_os = \"freebsd\"))'.dependencies]\nwgpu-core-deps-windows-linux-android = { workspace = true, optional = true }\n\n[dependencies]\nnaga.workspace = true\nwgpu-naga-bridge.workspace = true\nwgpu-hal.workspace = true\nwgpu-types.workspace = true\n\narrayvec.workspace = true\nbit-vec.workspace = true\nbit-set.workspace = true\nbitflags.workspace = true\nbytemuck.workspace = true\ndocument-features.workspace = true\nhashbrown.workspace = true\nindexmap.workspace = true\nlog.workspace = true\nmacro_rules_attribute = { workspace = true, optional = true }\nonce_cell = { workspace = true, features = [\"std\"] }\nparking_lot.workspace = true\nprofiling = { workspace = true, default-features = false }\nraw-window-handle.workspace = true\nron = { workspace = true, optional = true }\nrustc-hash.workspace = true\nserde = { workspace = true, features = [\"default\", \"derive\"], optional = true }\nsmallvec.workspace = true\nthiserror.workspace = true\n\n[target.'cfg(not(target_has_atomic = \"64\"))'.dependencies]\nportable-atomic = { workspace = true, optional = true }\n\n[build-dependencies]\ncfg_aliases.workspace = true\n"
  },
  {
    "path": "wgpu-core/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-core/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-core/build.rs",
    "content": "fn main() {\n    cfg_aliases::cfg_aliases! {\n        windows_linux_android: { any(windows, target_os = \"linux\", target_os = \"android\", target_os = \"freebsd\") },\n        send_sync: { all(\n            feature = \"std\",\n            any(\n                not(target_arch = \"wasm32\"),\n                all(feature = \"fragile-send-sync-non-atomic-wasm\", not(target_feature = \"atomics\"))\n            )\n        ) },\n        dx12: { all(target_os = \"windows\", feature = \"dx12\") },\n        webgl: { all(target_arch = \"wasm32\", not(target_os = \"emscripten\"), feature = \"webgl\") },\n        gles: { any(\n            all(windows_linux_android, feature = \"gles\"), // Regular GLES\n            all(webgl), // WebGL\n            all(target_os = \"emscripten\", feature = \"gles\"), // Emscripten GLES\n            all(target_vendor = \"apple\", feature = \"angle\") // ANGLE on Apple\n        ) },\n        vulkan: { any(\n            all(windows_linux_android, feature = \"vulkan\"), // Regular Vulkan\n            all(target_vendor = \"apple\", feature = \"vulkan-portability\") // Vulkan Portability on Apple\n        ) },\n        metal: { all(target_vendor = \"apple\", feature = \"metal\") },\n\n        supports_64bit_atomics: { target_has_atomic = \"64\" }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/platform-deps/apple/Cargo.toml",
    "content": "[package]\nname = \"wgpu-core-deps-apple\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Feature unification helper crate for Apple platforms\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nreadme = \"README.md\"\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.76\"\n\n[features]\nmetal = [\"wgpu-hal/metal\"]\nangle = [\"wgpu-hal/gles\", \"wgpu-hal/renderdoc\"]\nvulkan-portability = [\"wgpu-hal/vulkan\", \"wgpu-hal/renderdoc\"]\n\n# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.\n[target.'cfg(target_vendor = \"apple\")'.dependencies]\nwgpu-hal.workspace = true\n"
  },
  {
    "path": "wgpu-core/platform-deps/apple/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-core/platform-deps/apple/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-core/platform-deps/apple/README.md",
    "content": "This crate exists to allow platform and feature specific features work correctly. The features\nenabled on this crate are only enabled on `target_vendor = \"apple\"` platforms. See wgpu-hal's `Cargo.toml`\nfor more information."
  },
  {
    "path": "wgpu-core/platform-deps/apple/src/lib.rs",
    "content": "//! This crate exists to allow platform and feature specific features work correctly. The features\n//! enabled on this crate are only enabled on `target_vendor = \"apple\"` platforms. See wgpu-hal's `Cargo.toml`\n//! for more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/emscripten/Cargo.toml",
    "content": "[package]\nname = \"wgpu-core-deps-emscripten\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Feature unification helper crate for the Emscripten platform\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nreadme = \"README.md\"\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.76\"\n\n[features]\ngles = [\"wgpu-hal/gles\"]\n\n# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.\n[target.'cfg(target_os = \"emscripten\")'.dependencies]\nwgpu-hal.workspace = true\n"
  },
  {
    "path": "wgpu-core/platform-deps/emscripten/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-core/platform-deps/emscripten/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-core/platform-deps/emscripten/README.md",
    "content": "This crate exists to allow platform and feature specific features work correctly. The features\nenabled on this crate are only enabled on `target_os = \"emscripten\"` platforms.\nSee wgpu-hal's `Cargo.toml` for more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/emscripten/src/lib.rs",
    "content": "//! This crate exists to allow platform and feature specific features work correctly. The features\n//! enabled on this crate are only enabled on `target_os = \"emscripten\"` platforms.\n//! See wgpu-hal's `Cargo.toml` for more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/wasm/Cargo.toml",
    "content": "[package]\nname = \"wgpu-core-deps-wasm\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Feature unification helper crate for the WebAssembly platform\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nreadme = \"README.md\"\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.76\"\n\n[features]\nwebgl = [\"wgpu-hal/gles\"]\n\n# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.\n[target.'cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))'.dependencies]\nwgpu-hal.workspace = true\n"
  },
  {
    "path": "wgpu-core/platform-deps/wasm/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-core/platform-deps/wasm/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-core/platform-deps/wasm/README.md",
    "content": "This crate exists to allow platform and feature specific features work correctly. The features\nenabled on this crate are only enabled on `target_arch = \"wasm32\"` platforms. See wgpu-hal's `Cargo.toml`\nfor more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/wasm/src/lib.rs",
    "content": "//! This crate exists to allow platform and feature specific features work correctly. The features\n//! enabled on this crate are only enabled on `target_arch = \"wasm32\"` platforms. See wgpu-hal's `Cargo.toml`\n//! for more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/windows-linux-android/Cargo.toml",
    "content": "[package]\nname = \"wgpu-core-deps-windows-linux-android\"\nversion.workspace = true\nauthors.workspace = true\nedition.workspace = true\ndescription = \"Feature unification helper crate for the Windows/Linux/Android platforms\"\nhomepage.workspace = true\nrepository.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nreadme = \"README.md\"\n\n# Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to\n# copy the crates it actually uses out of the workspace, so it's meaningful for\n# them to have less restrictive MSRVs individually than the workspace as a\n# whole, if their code permits. See `../README.md` for details.\nrust-version = \"1.76\"\n\n[features]\ngles = [\"wgpu-hal/gles\"]\nvulkan = [\"wgpu-hal/vulkan\"]\ndx12 = [\"wgpu-hal/dx12\"]\nrenderdoc = [\"wgpu-hal/renderdoc\"]\n\n# Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms.\n[target.'cfg(any(windows, target_os = \"linux\", target_os = \"android\", target_os = \"freebsd\", target_os = \"netbsd\"))'.dependencies]\nwgpu-hal.workspace = true\n"
  },
  {
    "path": "wgpu-core/platform-deps/windows-linux-android/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-core/platform-deps/windows-linux-android/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-core/platform-deps/windows-linux-android/README.md",
    "content": "This crate exists to allow platform and feature specific features work correctly. The features\nenabled on this crate are only enabled on `windows`, `target_os = \"linux\"`, and `target_os = \"android\"`\nplatforms. See wgpu-hal's `Cargo.toml` for more information.\n"
  },
  {
    "path": "wgpu-core/platform-deps/windows-linux-android/src/lib.rs",
    "content": "//! This crate exists to allow platform and feature specific features work correctly. The features\n//! enabled on this crate are only enabled on `windows`, `target_os = \"linux\"`, and `target_os = \"android\"`\n//! platforms. See wgpu-hal's `Cargo.toml` for more information.\n"
  },
  {
    "path": "wgpu-core/src/as_hal.rs",
    "content": "use core::{mem::ManuallyDrop, ops::Deref};\n\nuse alloc::sync::Arc;\nuse hal::DynResource;\n\nuse crate::{\n    device::Device,\n    global::Global,\n    id::{\n        AdapterId, BlasId, BufferId, CommandEncoderId, DeviceId, QueueId, SurfaceId, TextureId,\n        TextureViewId, TlasId,\n    },\n    lock::{RankData, RwLockReadGuard},\n    resource::RawResourceAccess,\n    snatch::SnatchGuard,\n};\n\n/// A guard which holds alive a wgpu-core resource and dereferences to the Hal type.\nstruct SimpleResourceGuard<Resource, HalType> {\n    _guard: Resource,\n    ptr: *const HalType,\n}\n\nimpl<Resource, HalType> SimpleResourceGuard<Resource, HalType> {\n    /// Creates a new guard from a resource, using a callback to derive the Hal type.\n    pub fn new<C>(guard: Resource, callback: C) -> Option<Self>\n    where\n        C: Fn(&Resource) -> Option<&HalType>,\n    {\n        // Derive the hal type from the resource and coerce it to a pointer.\n        let ptr: *const HalType = callback(&guard)?;\n\n        Some(Self { _guard: guard, ptr })\n    }\n}\n\nimpl<Resource, HalType> Deref for SimpleResourceGuard<Resource, HalType> {\n    type Target = HalType;\n\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: The pointer is guaranteed to be valid as the original resource is\n        // still alive and this guard cannot be used with snatchable resources.\n        unsafe { &*self.ptr }\n    }\n}\n\nunsafe impl<Resource, HalType> Send for SimpleResourceGuard<Resource, HalType>\nwhere\n    Resource: Send,\n    HalType: Send,\n{\n}\nunsafe impl<Resource, HalType> Sync for SimpleResourceGuard<Resource, HalType>\nwhere\n    Resource: Sync,\n    HalType: Sync,\n{\n}\n\n/// A guard which holds alive a snatchable wgpu-core resource and dereferences to the Hal type.\nstruct SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess,\n{\n    resource: Arc<Resource>,\n    snatch_lock_rank_data: ManuallyDrop<RankData>,\n    ptr: *const HalType,\n}\n\nimpl<Resource, HalType> SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess,\n    HalType: 'static,\n{\n    /// Creates a new guard from a snatchable resource.\n    ///\n    /// Returns `None` if:\n    /// - The resource is not of the expected Hal type.\n    /// - The resource has been destroyed.\n    pub fn new(resource: Arc<Resource>) -> Option<Self> {\n        // Grab the snatchable lock.\n        let snatch_guard = resource.device().snatchable_lock.read();\n\n        // Get the raw resource and downcast it to the expected Hal type.\n        let underlying = resource\n            .raw(&snatch_guard)?\n            .as_any()\n            .downcast_ref::<HalType>()?;\n\n        // Cast the raw resource to a pointer to get rid of the lifetime\n        // connecting us to the snatch guard.\n        let ptr: *const HalType = underlying;\n\n        // SAFETY: At this point all panicking or divergance has already happened,\n        // so we can safely forget the snatch guard without causing the lock to be left open.\n        let snatch_lock_rank_data = SnatchGuard::forget(snatch_guard);\n\n        // SAFETY: We only construct this guard while the snatchable lock is held,\n        // as the `drop` implementation of this guard will unsafely release the lock.\n        Some(Self {\n            resource,\n            snatch_lock_rank_data: ManuallyDrop::new(snatch_lock_rank_data),\n            ptr,\n        })\n    }\n}\n\nimpl<Resource, HalType> Deref for SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess,\n{\n    type Target = HalType;\n\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: The pointer is guaranteed to be valid as the original resource is\n        // still alive and the snatchable lock is still being held due to the forgotten\n        // snatch guard.\n        unsafe { &*self.ptr }\n    }\n}\n\nimpl<Resource, HalType> Drop for SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess,\n{\n    fn drop(&mut self) {\n        // SAFETY:\n        // - We are not going to access the rank data anymore.\n        let data = unsafe { ManuallyDrop::take(&mut self.snatch_lock_rank_data) };\n\n        // SAFETY:\n        // - The pointer is no longer going to be accessed.\n        // - The snatchable lock is being held because this type was not created\n        //   until after the snatchable lock was forgotten.\n        unsafe {\n            self.resource\n                .device()\n                .snatchable_lock\n                .force_unlock_read(data)\n        };\n    }\n}\n\nunsafe impl<Resource, HalType> Send for SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess + Send,\n    HalType: Send,\n{\n}\nunsafe impl<Resource, HalType> Sync for SnatchableResourceGuard<Resource, HalType>\nwhere\n    Resource: RawResourceAccess + Sync,\n    HalType: Sync,\n{\n}\n\n/// A guard which holds alive a device and the device's fence lock, dereferencing to the Hal type.\nstruct FenceGuard<Fence> {\n    device: Arc<Device>,\n    fence_lock_rank_data: ManuallyDrop<RankData>,\n    ptr: *const Fence,\n}\n\nimpl<Fence> FenceGuard<Fence>\nwhere\n    Fence: 'static,\n{\n    /// Creates a new guard over a device's fence.\n    ///\n    /// Returns `None` if:\n    /// - The device's fence is not of the expected Hal type.\n    pub fn new(device: Arc<Device>) -> Option<Self> {\n        // Grab the fence lock.\n        let fence_guard = device.fence.read();\n\n        // Get the raw fence and downcast it to the expected Hal type, coercing it to a pointer\n        // to get rid of the lifetime connecting us to the fence guard.\n        let ptr: *const Fence = fence_guard.as_any().downcast_ref::<Fence>()?;\n\n        // SAFETY: At this point all panicking or divergance has already happened,\n        // so we can safely forget the fence guard without causing the lock to be left open.\n        let fence_lock_rank_data = RwLockReadGuard::forget(fence_guard);\n\n        // SAFETY: We only construct this guard while the fence lock is held,\n        // as the `drop` implementation of this guard will unsafely release the lock.\n        Some(Self {\n            device,\n            fence_lock_rank_data: ManuallyDrop::new(fence_lock_rank_data),\n            ptr,\n        })\n    }\n}\n\nimpl<Fence> Deref for FenceGuard<Fence> {\n    type Target = Fence;\n\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: The pointer is guaranteed to be valid as the original device's fence\n        // is still alive and the fence lock is still being held due to the forgotten\n        // fence guard.\n        unsafe { &*self.ptr }\n    }\n}\n\nimpl<Fence> Drop for FenceGuard<Fence> {\n    fn drop(&mut self) {\n        // SAFETY:\n        // - We are not going to access the rank data anymore.\n        let data = unsafe { ManuallyDrop::take(&mut self.fence_lock_rank_data) };\n\n        // SAFETY:\n        // - The pointer is no longer going to be accessed.\n        // - The fence lock is being held because this type was not created\n        //   until after the fence lock was forgotten.\n        unsafe {\n            self.device.fence.force_unlock_read(data);\n        };\n    }\n}\n\nunsafe impl<Fence> Send for FenceGuard<Fence> where Fence: Send {}\nunsafe impl<Fence> Sync for FenceGuard<Fence> where Fence: Sync {}\n\nimpl Global {\n    /// # Safety\n    ///\n    /// - The raw buffer handle must not be manually destroyed\n    pub unsafe fn buffer_as_hal<A: hal::Api>(\n        &self,\n        id: BufferId,\n    ) -> Option<impl Deref<Target = A::Buffer>> {\n        profiling::scope!(\"Buffer::as_hal\");\n\n        let hub = &self.hub;\n\n        let buffer = hub.buffers.get(id).get().ok()?;\n\n        SnatchableResourceGuard::new(buffer)\n    }\n\n    /// # Safety\n    ///\n    /// - The raw texture handle must not be manually destroyed\n    pub unsafe fn texture_as_hal<A: hal::Api>(\n        &self,\n        id: TextureId,\n    ) -> Option<impl Deref<Target = A::Texture>> {\n        profiling::scope!(\"Texture::as_hal\");\n\n        let hub = &self.hub;\n\n        let texture = hub.textures.get(id).get().ok()?;\n\n        SnatchableResourceGuard::new(texture)\n    }\n\n    /// # Safety\n    ///\n    /// - The raw texture view handle must not be manually destroyed\n    pub unsafe fn texture_view_as_hal<A: hal::Api>(\n        &self,\n        id: TextureViewId,\n    ) -> Option<impl Deref<Target = A::TextureView>> {\n        profiling::scope!(\"TextureView::as_hal\");\n\n        let hub = &self.hub;\n\n        let view = hub.texture_views.get(id).get().ok()?;\n\n        SnatchableResourceGuard::new(view)\n    }\n\n    /// # Safety\n    ///\n    /// - The raw adapter handle must not be manually destroyed\n    pub unsafe fn adapter_as_hal<A: hal::Api>(\n        &self,\n        id: AdapterId,\n    ) -> Option<impl Deref<Target = A::Adapter>> {\n        profiling::scope!(\"Adapter::as_hal\");\n\n        let hub = &self.hub;\n        let adapter = hub.adapters.get(id);\n\n        SimpleResourceGuard::new(adapter, move |adapter| {\n            adapter.raw.adapter.as_any().downcast_ref()\n        })\n    }\n\n    /// # Safety\n    ///\n    /// - The raw device handle must not be manually destroyed\n    pub unsafe fn device_as_hal<A: hal::Api>(\n        &self,\n        id: DeviceId,\n    ) -> Option<impl Deref<Target = A::Device>> {\n        profiling::scope!(\"Device::as_hal\");\n\n        let device = self.hub.devices.get(id);\n\n        SimpleResourceGuard::new(device, move |device| device.raw().as_any().downcast_ref())\n    }\n\n    /// # Safety\n    ///\n    /// - The raw fence handle must not be manually destroyed\n    pub unsafe fn device_fence_as_hal<A: hal::Api>(\n        &self,\n        id: DeviceId,\n    ) -> Option<impl Deref<Target = A::Fence>> {\n        profiling::scope!(\"Device::fence_as_hal\");\n\n        let device = self.hub.devices.get(id);\n\n        FenceGuard::new(device)\n    }\n\n    /// # Safety\n    /// - The raw surface handle must not be manually destroyed\n    pub unsafe fn surface_as_hal<A: hal::Api>(\n        &self,\n        id: SurfaceId,\n    ) -> Option<impl Deref<Target = A::Surface>> {\n        profiling::scope!(\"Surface::as_hal\");\n\n        let surface = self.surfaces.get(id);\n\n        SimpleResourceGuard::new(surface, move |surface| {\n            surface.raw(A::VARIANT)?.as_any().downcast_ref()\n        })\n    }\n\n    /// Encode commands using the raw HAL command encoder.\n    ///\n    /// # Panics\n    ///\n    /// If the command encoder has already been used with the wgpu encoding API.\n    ///\n    /// # Safety\n    ///\n    /// - The raw command encoder handle must not be manually destroyed\n    pub unsafe fn command_encoder_as_hal_mut<\n        A: hal::Api,\n        F: FnOnce(Option<&mut A::CommandEncoder>) -> R,\n        R,\n    >(\n        &self,\n        id: CommandEncoderId,\n        hal_command_encoder_callback: F,\n    ) -> R {\n        profiling::scope!(\"CommandEncoder::as_hal\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n        cmd_buf_data.record_as_hal_mut(|opt_cmd_buf| -> R {\n            hal_command_encoder_callback(opt_cmd_buf.and_then(|cmd_buf| {\n                cmd_buf\n                    .encoder\n                    .open()\n                    .ok()\n                    .and_then(|encoder| encoder.as_any_mut().downcast_mut())\n            }))\n        })\n    }\n\n    /// # Safety\n    ///\n    /// - The raw queue handle must not be manually destroyed\n    pub unsafe fn queue_as_hal<A: hal::Api>(\n        &self,\n        id: QueueId,\n    ) -> Option<impl Deref<Target = A::Queue>> {\n        profiling::scope!(\"Queue::as_hal\");\n\n        let queue = self.hub.queues.get(id);\n\n        SimpleResourceGuard::new(queue, move |queue| queue.raw().as_any().downcast_ref())\n    }\n\n    /// # Safety\n    ///\n    /// - The raw blas handle must not be manually destroyed\n    pub unsafe fn blas_as_hal<A: hal::Api>(\n        &self,\n        id: BlasId,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {\n        profiling::scope!(\"Blas::as_hal\");\n\n        let hub = &self.hub;\n\n        let blas = hub.blas_s.get(id).get().ok()?;\n\n        SnatchableResourceGuard::new(blas)\n    }\n\n    /// # Safety\n    ///\n    /// - The raw tlas handle must not be manually destroyed\n    pub unsafe fn tlas_as_hal<A: hal::Api>(\n        &self,\n        id: TlasId,\n    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {\n        profiling::scope!(\"Tlas::as_hal\");\n\n        let hub = &self.hub;\n\n        let tlas = hub.tlas_s.get(id).get().ok()?;\n\n        SnatchableResourceGuard::new(tlas)\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/binding_model.rs",
    "content": "use alloc::{\n    borrow::{Cow, ToOwned},\n    boxed::Box,\n    string::String,\n    sync::{Arc, Weak},\n    vec::Vec,\n};\nuse core::{fmt, mem::ManuallyDrop, ops::Range};\n\nuse arrayvec::ArrayVec;\nuse thiserror::Error;\n\n#[cfg(feature = \"serde\")]\nuse serde::Deserialize;\n#[cfg(feature = \"serde\")]\nuse serde::Serialize;\n\nuse wgt::error::{ErrorType, WebGpuError};\n\nuse crate::{\n    device::{bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures},\n    id::{BindGroupLayoutId, BufferId, ExternalTextureId, SamplerId, TextureViewId, TlasId},\n    init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},\n    pipeline::{ComputePipeline, RenderPipeline},\n    resource::{\n        Buffer, DestroyedResourceError, ExternalTexture, InvalidResourceError, Labeled,\n        MissingBufferUsageError, MissingTextureUsageError, RawResourceAccess, ResourceErrorIdent,\n        Sampler, TextureView, Tlas, TrackingData,\n    },\n    resource_log,\n    snatch::{SnatchGuard, Snatchable},\n    track::{BindGroupStates, ResourceUsageCompatibilityError},\n    Label,\n};\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum BindGroupLayoutEntryError {\n    #[error(\"Cube dimension is not expected for texture storage\")]\n    StorageTextureCube,\n    #[error(\"Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES\")]\n    StorageTextureAtomic,\n    #[error(\"Arrays of bindings unsupported for this type of binding\")]\n    ArrayUnsupported,\n    #[error(\"Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.\")]\n    SampleTypeFloatFilterableBindingMultisampled,\n    #[error(\"Multisampled texture binding view dimension must be 2d, got {0:?}\")]\n    Non2DMultisampled(wgt::TextureViewDimension),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateBindGroupLayoutError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Conflicting binding at index {0}\")]\n    ConflictBinding(u32),\n    #[error(\"Binding {binding} entry is invalid\")]\n    Entry {\n        binding: u32,\n        #[source]\n        error: BindGroupLayoutEntryError,\n    },\n    #[error(transparent)]\n    TooManyBindings(BindingTypeMaxCountError),\n    #[error(\"Bind groups may not contain both a binding array and a dynamically offset buffer\")]\n    ContainsBothBindingArrayAndDynamicOffsetArray,\n    #[error(\"Bind groups may not contain both a binding array and a uniform buffer\")]\n    ContainsBothBindingArrayAndUniformBuffer,\n    #[error(\"Binding index {binding} is greater than the maximum number {maximum}\")]\n    InvalidBindingIndex { binding: u32, maximum: u32 },\n    #[error(\"Invalid visibility {0:?}\")]\n    InvalidVisibility(wgt::ShaderStages),\n    #[error(\"Binding index {binding}: {access:?} access to storage textures with format {format:?} is not supported\")]\n    UnsupportedStorageTextureAccess {\n        binding: u32,\n        access: wgt::StorageTextureAccess,\n        format: wgt::TextureFormat,\n    },\n}\n\nimpl WebGpuError for CreateBindGroupLayoutError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n\n            Self::ConflictBinding(_)\n            | Self::Entry { .. }\n            | Self::TooManyBindings(_)\n            | Self::InvalidBindingIndex { .. }\n            | Self::InvalidVisibility(_)\n            | Self::ContainsBothBindingArrayAndDynamicOffsetArray\n            | Self::ContainsBothBindingArrayAndUniformBuffer\n            | Self::UnsupportedStorageTextureAccess { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum BindingError {\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}\")]\n    BindingRangeTooLarge {\n        buffer: ResourceErrorIdent,\n        offset: wgt::BufferAddress,\n        binding_size: u64,\n        buffer_size: u64,\n    },\n    #[error(\"Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}\")]\n    BindingOffsetTooLarge {\n        buffer: ResourceErrorIdent,\n        offset: wgt::BufferAddress,\n        buffer_size: u64,\n    },\n}\n\nimpl WebGpuError for BindingError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::BindingRangeTooLarge { .. } | Self::BindingOffsetTooLarge { .. } => {\n                ErrorType::Validation\n            }\n        }\n    }\n}\n\n// TODO: there may be additional variants here that can be extracted into\n// `BindingError`.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateBindGroupError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    BindingError(#[from] BindingError),\n    #[error(\n        \"Binding count declared with at most {expected} items, but {actual} items were provided\"\n    )]\n    BindingArrayPartialLengthMismatch { actual: usize, expected: usize },\n    #[error(\n        \"Binding count declared with exactly {expected} items, but {actual} items were provided\"\n    )]\n    BindingArrayLengthMismatch { actual: usize, expected: usize },\n    #[error(\"Array binding provided zero elements\")]\n    BindingArrayZeroLength,\n    #[error(\"Binding size {actual} of {buffer} is less than minimum {min}\")]\n    BindingSizeTooSmall {\n        buffer: ResourceErrorIdent,\n        actual: u64,\n        min: u64,\n    },\n    #[error(\"{0} binding size is zero\")]\n    BindingZeroSize(ResourceErrorIdent),\n    #[error(\"Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})\")]\n    BindingsNumMismatch { actual: usize, expected: usize },\n    #[error(\"Binding {0} is used at least twice in the descriptor\")]\n    DuplicateBinding(u32),\n    #[error(\"Unable to find a corresponding declaration for the given binding {0}\")]\n    MissingBindingDeclaration(u32),\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(transparent)]\n    MissingTextureUsage(#[from] MissingTextureUsageError),\n    #[error(\"Binding declared as a single item, but bind group is using it as an array\")]\n    SingleBindingExpected,\n    #[error(\"Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}\")]\n    UnalignedEffectiveBufferBindingSizeForStorage { alignment: u32, size: u64 },\n    #[error(\"Buffer offset {0} does not respect device's requested `{1}` limit {2}\")]\n    UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),\n    #[error(\n        \"Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}\"\n    )]\n    BufferRangeTooLarge {\n        binding: u32,\n        given: u64,\n        limit: u64,\n    },\n    #[error(\"Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})\")]\n    WrongBindingType {\n        // Index of the binding\n        binding: u32,\n        // The type given to the function\n        actual: wgt::BindingType,\n        // Human-readable description of expected types\n        expected: &'static str,\n    },\n    #[error(\"Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}\")]\n    InvalidTextureMultisample {\n        binding: u32,\n        layout_multisampled: bool,\n        view_samples: u32,\n    },\n    #[error(\n        \"Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})\",\n        binding,\n        layout_sample_type,\n        view_format,\n        view_sample_type\n    )]\n    InvalidTextureSampleType {\n        binding: u32,\n        layout_sample_type: wgt::TextureSampleType,\n        view_format: wgt::TextureFormat,\n        view_sample_type: wgt::TextureSampleType,\n    },\n    #[error(\"Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}\")]\n    InvalidTextureDimension {\n        binding: u32,\n        layout_dimension: wgt::TextureViewDimension,\n        view_dimension: wgt::TextureViewDimension,\n    },\n    #[error(\"Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}\")]\n    InvalidStorageTextureFormat {\n        binding: u32,\n        layout_format: wgt::TextureFormat,\n        view_format: wgt::TextureFormat,\n    },\n    #[error(\"Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}\")]\n    InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },\n    #[error(\"External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}\")]\n    InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },\n    #[error(\"External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}\")]\n    InvalidExternalTextureFormat {\n        binding: u32,\n        format: wgt::TextureFormat,\n    },\n    #[error(\"Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}\")]\n    WrongSamplerComparison {\n        binding: u32,\n        layout_cmp: bool,\n        sampler_cmp: bool,\n    },\n    #[error(\"Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}\")]\n    WrongSamplerFiltering {\n        binding: u32,\n        layout_flt: bool,\n        sampler_flt: bool,\n    },\n    #[error(\"TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN\")]\n    MissingTLASVertexReturn { binding: u32 },\n    #[error(\"Bound texture views can not have both depth and stencil aspects enabled\")]\n    DepthStencilAspect,\n    #[error(transparent)]\n    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for CreateBindGroupError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::BindingError(e) => e.webgpu_error_type(),\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::MissingTextureUsage(e) => e.webgpu_error_type(),\n            Self::ResourceUsageCompatibility(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::BindingArrayPartialLengthMismatch { .. }\n            | Self::BindingArrayLengthMismatch { .. }\n            | Self::BindingArrayZeroLength\n            | Self::BindingSizeTooSmall { .. }\n            | Self::BindingsNumMismatch { .. }\n            | Self::BindingZeroSize(_)\n            | Self::DuplicateBinding(_)\n            | Self::MissingBindingDeclaration(_)\n            | Self::SingleBindingExpected\n            | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. }\n            | Self::UnalignedBufferOffset(_, _, _)\n            | Self::BufferRangeTooLarge { .. }\n            | Self::WrongBindingType { .. }\n            | Self::InvalidTextureMultisample { .. }\n            | Self::InvalidTextureSampleType { .. }\n            | Self::InvalidTextureDimension { .. }\n            | Self::InvalidStorageTextureFormat { .. }\n            | Self::InvalidStorageTextureMipLevelCount { .. }\n            | Self::WrongSamplerComparison { .. }\n            | Self::WrongSamplerFiltering { .. }\n            | Self::DepthStencilAspect\n            | Self::MissingTLASVertexReturn { .. }\n            | Self::InvalidExternalTextureMipLevelCount { .. }\n            | Self::InvalidExternalTextureFormat { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\npub enum BindingZone {\n    #[error(\"Stage {0:?}\")]\n    Stage(wgt::ShaderStages),\n    #[error(\"Whole pipeline\")]\n    Pipeline,\n}\n\n#[derive(Clone, Debug, Error)]\n#[error(\"Too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}. Check the limit `{}` passed to `Adapter::request_device`\", .kind.to_config_str())]\npub struct BindingTypeMaxCountError {\n    pub kind: BindingTypeMaxCountErrorKind,\n    pub zone: BindingZone,\n    pub limit: u32,\n    pub count: u32,\n}\n\nimpl WebGpuError for BindingTypeMaxCountError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum BindingTypeMaxCountErrorKind {\n    DynamicUniformBuffers,\n    DynamicStorageBuffers,\n    SampledTextures,\n    Samplers,\n    StorageBuffers,\n    StorageTextures,\n    UniformBuffers,\n    BindingArrayElements,\n    BindingArraySamplerElements,\n    BindingArrayAccelerationStructureElements,\n    AccelerationStructures,\n}\n\nimpl BindingTypeMaxCountErrorKind {\n    fn to_config_str(&self) -> &'static str {\n        match self {\n            BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {\n                \"max_dynamic_uniform_buffers_per_pipeline_layout\"\n            }\n            BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {\n                \"max_dynamic_storage_buffers_per_pipeline_layout\"\n            }\n            BindingTypeMaxCountErrorKind::SampledTextures => {\n                \"max_sampled_textures_per_shader_stage\"\n            }\n            BindingTypeMaxCountErrorKind::Samplers => \"max_samplers_per_shader_stage\",\n            BindingTypeMaxCountErrorKind::StorageBuffers => \"max_storage_buffers_per_shader_stage\",\n            BindingTypeMaxCountErrorKind::StorageTextures => {\n                \"max_storage_textures_per_shader_stage\"\n            }\n            BindingTypeMaxCountErrorKind::UniformBuffers => \"max_uniform_buffers_per_shader_stage\",\n            BindingTypeMaxCountErrorKind::BindingArrayElements => {\n                \"max_binding_array_elements_per_shader_stage\"\n            }\n            BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {\n                \"max_binding_array_sampler_elements_per_shader_stage\"\n            }\n            BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements => {\n                \"max_binding_array_acceleration_structure_elements_per_shader_stage\"\n            }\n            BindingTypeMaxCountErrorKind::AccelerationStructures => {\n                \"max_acceleration_structures_per_shader_stage\"\n            }\n        }\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct PerStageBindingTypeCounter {\n    vertex: u32,\n    fragment: u32,\n    compute: u32,\n}\n\nimpl PerStageBindingTypeCounter {\n    pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {\n        if stage.contains(wgt::ShaderStages::VERTEX) {\n            self.vertex += count;\n        }\n        if stage.contains(wgt::ShaderStages::FRAGMENT) {\n            self.fragment += count;\n        }\n        if stage.contains(wgt::ShaderStages::COMPUTE) {\n            self.compute += count;\n        }\n    }\n\n    pub(crate) fn max(&self) -> (BindingZone, u32) {\n        let max_value = self.vertex.max(self.fragment.max(self.compute));\n        let mut stage = wgt::ShaderStages::NONE;\n        if max_value == self.vertex {\n            stage |= wgt::ShaderStages::VERTEX\n        }\n        if max_value == self.fragment {\n            stage |= wgt::ShaderStages::FRAGMENT\n        }\n        if max_value == self.compute {\n            stage |= wgt::ShaderStages::COMPUTE\n        }\n        (BindingZone::Stage(stage), max_value)\n    }\n\n    pub(crate) fn merge(&mut self, other: &Self) {\n        self.vertex += other.vertex;\n        self.fragment += other.fragment;\n        self.compute += other.compute;\n    }\n\n    pub(crate) fn validate(\n        &self,\n        limit: u32,\n        kind: BindingTypeMaxCountErrorKind,\n    ) -> Result<(), BindingTypeMaxCountError> {\n        let (zone, count) = self.max();\n        if limit < count {\n            Err(BindingTypeMaxCountError {\n                kind,\n                zone,\n                limit,\n                count,\n            })\n        } else {\n            Ok(())\n        }\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct BindingTypeMaxCountValidator {\n    dynamic_uniform_buffers: u32,\n    dynamic_storage_buffers: u32,\n    sampled_textures: PerStageBindingTypeCounter,\n    samplers: PerStageBindingTypeCounter,\n    storage_buffers: PerStageBindingTypeCounter,\n    storage_textures: PerStageBindingTypeCounter,\n    uniform_buffers: PerStageBindingTypeCounter,\n    acceleration_structures: PerStageBindingTypeCounter,\n    binding_array_elements: PerStageBindingTypeCounter,\n    binding_array_sampler_elements: PerStageBindingTypeCounter,\n    binding_array_acceleration_structure_elements: PerStageBindingTypeCounter,\n    has_bindless_array: bool,\n}\n\nimpl BindingTypeMaxCountValidator {\n    pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {\n        let count = binding.count.map_or(1, |count| count.get());\n\n        if binding.count.is_some() {\n            self.binding_array_elements.add(binding.visibility, count);\n            self.has_bindless_array = true;\n\n            match binding.ty {\n                wgt::BindingType::Sampler(_) => {\n                    self.binding_array_sampler_elements\n                        .add(binding.visibility, count);\n                }\n                wgt::BindingType::AccelerationStructure { .. } => {\n                    self.binding_array_acceleration_structure_elements\n                        .add(binding.visibility, count);\n                }\n                _ => {}\n            }\n        } else {\n            match binding.ty {\n                wgt::BindingType::Buffer {\n                    ty: wgt::BufferBindingType::Uniform,\n                    has_dynamic_offset,\n                    ..\n                } => {\n                    self.uniform_buffers.add(binding.visibility, count);\n                    if has_dynamic_offset {\n                        self.dynamic_uniform_buffers += count;\n                    }\n                }\n                wgt::BindingType::Buffer {\n                    ty: wgt::BufferBindingType::Storage { .. },\n                    has_dynamic_offset,\n                    ..\n                } => {\n                    self.storage_buffers.add(binding.visibility, count);\n                    if has_dynamic_offset {\n                        self.dynamic_storage_buffers += count;\n                    }\n                }\n                wgt::BindingType::Sampler { .. } => {\n                    self.samplers.add(binding.visibility, count);\n                }\n                wgt::BindingType::Texture { .. } => {\n                    self.sampled_textures.add(binding.visibility, count);\n                }\n                wgt::BindingType::StorageTexture { .. } => {\n                    self.storage_textures.add(binding.visibility, count);\n                }\n                wgt::BindingType::AccelerationStructure { .. } => {\n                    self.acceleration_structures.add(binding.visibility, count);\n                }\n                wgt::BindingType::ExternalTexture => {\n                    // https://www.w3.org/TR/webgpu/#gpuexternaltexture\n                    // In order to account for many possible representations,\n                    // the binding conservatively uses the following, for each\n                    // external texture:\n                    // * Three sampled textures for up to 3 planes\n                    // * One additional sampled texture for a 3D LUT\n                    // * One sampler to sample the LUT\n                    // * One uniform buffer for metadata\n                    self.sampled_textures.add(binding.visibility, count * 4);\n                    self.samplers.add(binding.visibility, count);\n                    self.uniform_buffers.add(binding.visibility, count);\n                }\n            }\n        }\n    }\n\n    pub(crate) fn merge(&mut self, other: &Self) {\n        self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;\n        self.dynamic_storage_buffers += other.dynamic_storage_buffers;\n        self.sampled_textures.merge(&other.sampled_textures);\n        self.samplers.merge(&other.samplers);\n        self.storage_buffers.merge(&other.storage_buffers);\n        self.storage_textures.merge(&other.storage_textures);\n        self.uniform_buffers.merge(&other.uniform_buffers);\n        self.acceleration_structures\n            .merge(&other.acceleration_structures);\n        self.binding_array_elements\n            .merge(&other.binding_array_elements);\n        self.binding_array_sampler_elements\n            .merge(&other.binding_array_sampler_elements);\n        self.binding_array_acceleration_structure_elements\n            .merge(&other.binding_array_acceleration_structure_elements);\n    }\n\n    pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {\n        if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {\n            return Err(BindingTypeMaxCountError {\n                kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,\n                zone: BindingZone::Pipeline,\n                limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,\n                count: self.dynamic_uniform_buffers,\n            });\n        }\n        if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {\n            return Err(BindingTypeMaxCountError {\n                kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,\n                zone: BindingZone::Pipeline,\n                limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,\n                count: self.dynamic_storage_buffers,\n            });\n        }\n        self.sampled_textures.validate(\n            limits.max_sampled_textures_per_shader_stage,\n            BindingTypeMaxCountErrorKind::SampledTextures,\n        )?;\n        self.samplers.validate(\n            limits.max_samplers_per_shader_stage,\n            BindingTypeMaxCountErrorKind::Samplers,\n        )?;\n        self.storage_buffers.validate(\n            limits.max_storage_buffers_per_shader_stage,\n            BindingTypeMaxCountErrorKind::StorageBuffers,\n        )?;\n        self.storage_textures.validate(\n            limits.max_storage_textures_per_shader_stage,\n            BindingTypeMaxCountErrorKind::StorageTextures,\n        )?;\n        self.uniform_buffers.validate(\n            limits.max_uniform_buffers_per_shader_stage,\n            BindingTypeMaxCountErrorKind::UniformBuffers,\n        )?;\n        self.binding_array_elements.validate(\n            limits.max_binding_array_elements_per_shader_stage,\n            BindingTypeMaxCountErrorKind::BindingArrayElements,\n        )?;\n        self.binding_array_sampler_elements.validate(\n            limits.max_binding_array_sampler_elements_per_shader_stage,\n            BindingTypeMaxCountErrorKind::BindingArraySamplerElements,\n        )?;\n        self.binding_array_acceleration_structure_elements\n            .validate(\n                limits.max_binding_array_acceleration_structure_elements_per_shader_stage,\n                BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements,\n            )?;\n        self.acceleration_structures.validate(\n            limits.max_acceleration_structures_per_shader_stage,\n            BindingTypeMaxCountErrorKind::AccelerationStructures,\n        )?;\n        Ok(())\n    }\n\n    /// Validate that the bind group layout does not contain both a binding array and a dynamic offset array.\n    ///\n    /// This allows us to use `UPDATE_AFTER_BIND` on vulkan for bindless arrays. Vulkan does not allow\n    /// `UPDATE_AFTER_BIND` on dynamic offset arrays. See <https://github.com/gfx-rs/wgpu/issues/6737>\n    pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {\n        let has_dynamic_offset_array =\n            self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;\n        let has_uniform_buffer = self.uniform_buffers.max().1 > 0;\n        if self.has_bindless_array && has_dynamic_offset_array {\n            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);\n        }\n        if self.has_bindless_array && has_uniform_buffer {\n            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);\n        }\n        Ok(())\n    }\n}\n\n/// Bindable resource and the slot to bind it to.\n/// cbindgen:ignore\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct BindGroupEntry<\n    'a,\n    B = BufferId,\n    S = SamplerId,\n    TV = TextureViewId,\n    TLAS = TlasId,\n    ET = ExternalTextureId,\n> where\n    [BufferBinding<B>]: ToOwned,\n    [S]: ToOwned,\n    [TV]: ToOwned,\n    [TLAS]: ToOwned,\n    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,\n    <[S] as ToOwned>::Owned: fmt::Debug,\n    <[TV] as ToOwned>::Owned: fmt::Debug,\n    <[TLAS] as ToOwned>::Owned: fmt::Debug,\n{\n    /// Slot for which binding provides resource. Corresponds to an entry of the same\n    /// binding index in the [`BindGroupLayoutDescriptor`].\n    pub binding: u32,\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>\"))\n    )]\n    /// Resource to attach to the binding\n    pub resource: BindingResource<'a, B, S, TV, TLAS, ET>,\n}\n\n/// cbindgen:ignore\npub type ResolvedBindGroupEntry<'a> = BindGroupEntry<\n    'a,\n    Arc<Buffer>,\n    Arc<Sampler>,\n    Arc<TextureView>,\n    Arc<Tlas>,\n    Arc<ExternalTexture>,\n>;\n\n/// Describes a group of bindings and the resources to be bound.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct BindGroupDescriptor<\n    'a,\n    BGL = BindGroupLayoutId,\n    B = BufferId,\n    S = SamplerId,\n    TV = TextureViewId,\n    TLAS = TlasId,\n    ET = ExternalTextureId,\n> where\n    [BufferBinding<B>]: ToOwned,\n    [S]: ToOwned,\n    [TV]: ToOwned,\n    [TLAS]: ToOwned,\n    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,\n    <[S] as ToOwned>::Owned: fmt::Debug,\n    <[TV] as ToOwned>::Owned: fmt::Debug,\n    <[TLAS] as ToOwned>::Owned: fmt::Debug,\n    [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,\n    <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,\n{\n    /// Debug label of the bind group.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The [`BindGroupLayout`] that corresponds to this bind group.\n    pub layout: BGL,\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(\n            deserialize = \"<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>\"\n        ))\n    )]\n    /// The resources to bind to this bind group.\n    #[allow(clippy::type_complexity)]\n    pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>,\n}\n\n/// cbindgen:ignore\npub type ResolvedBindGroupDescriptor<'a> = BindGroupDescriptor<\n    'a,\n    Arc<BindGroupLayout>,\n    Arc<Buffer>,\n    Arc<Sampler>,\n    Arc<TextureView>,\n    Arc<Tlas>,\n    Arc<ExternalTexture>,\n>;\n\n/// Describes a [`BindGroupLayout`].\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct BindGroupLayoutDescriptor<'a> {\n    /// Debug label of the bind group layout.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Array of entries in this BindGroupLayout\n    pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,\n}\n\n/// Used by [`BindGroupLayout`]. It indicates whether the BGL must be\n/// used with a specific pipeline. This constraint only happens when\n/// the BGLs have been derived from a pipeline without a layout.\n#[derive(Clone, Debug)]\npub(crate) enum ExclusivePipeline {\n    None,\n    Render(Weak<RenderPipeline>),\n    Compute(Weak<ComputePipeline>),\n}\n\nimpl fmt::Display for ExclusivePipeline {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            ExclusivePipeline::None => f.write_str(\"None\"),\n            ExclusivePipeline::Render(p) => {\n                if let Some(p) = p.upgrade() {\n                    p.error_ident().fmt(f)\n                } else {\n                    f.write_str(\"RenderPipeline\")\n                }\n            }\n            ExclusivePipeline::Compute(p) => {\n                if let Some(p) = p.upgrade() {\n                    p.error_ident().fmt(f)\n                } else {\n                    f.write_str(\"ComputePipeline\")\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\npub enum RawBindGroupLayout {\n    Owning(ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>),\n    /// The empty BGL was created by the device and will be destroyed by the device.\n    RefDeviceEmptyBGL,\n}\n\n/// Bind group layout.\n#[derive(Debug)]\npub struct BindGroupLayout {\n    pub(crate) raw: RawBindGroupLayout,\n    pub(crate) device: Arc<Device>,\n    pub(crate) entries: bgl::EntryMap,\n    /// It is very important that we know if the bind group comes from the BGL pool.\n    ///\n    /// If it does, then we need to remove it from the pool when we drop it.\n    ///\n    /// We cannot unconditionally remove from the pool, as BGLs that don't come from the pool\n    /// (derived BGLs) must not be removed.\n    pub(crate) origin: bgl::Origin,\n    pub(crate) exclusive_pipeline: crate::OnceCellOrLock<ExclusivePipeline>,\n    pub(crate) binding_count_validator: BindingTypeMaxCountValidator,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n}\n\nimpl Drop for BindGroupLayout {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        if matches!(self.origin, bgl::Origin::Pool) {\n            self.device.bgl_pool.remove(&self.entries);\n        }\n        match self.raw {\n            RawBindGroupLayout::Owning(ref mut raw) => {\n                // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n                let raw = unsafe { ManuallyDrop::take(raw) };\n                unsafe {\n                    self.device.raw().destroy_bind_group_layout(raw);\n                }\n            }\n            RawBindGroupLayout::RefDeviceEmptyBGL => {}\n        }\n    }\n}\n\ncrate::impl_resource_type!(BindGroupLayout);\ncrate::impl_labeled!(BindGroupLayout);\ncrate::impl_parent_device!(BindGroupLayout);\ncrate::impl_storage_item!(BindGroupLayout);\n\nimpl BindGroupLayout {\n    pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {\n        match &self.raw {\n            RawBindGroupLayout::Owning(raw) => raw.as_ref(),\n            RawBindGroupLayout::RefDeviceEmptyBGL => self.device.empty_bgl.as_ref(),\n        }\n    }\n\n    fn empty(device: &Arc<Device>) -> Arc<Self> {\n        let exclusive_pipeline = crate::OnceCellOrLock::new();\n        exclusive_pipeline.set(ExclusivePipeline::None).unwrap();\n        Arc::new(Self {\n            raw: RawBindGroupLayout::RefDeviceEmptyBGL,\n            device: device.clone(),\n            entries: bgl::EntryMap::default(),\n            origin: bgl::Origin::Derived,\n            exclusive_pipeline,\n            binding_count_validator: BindingTypeMaxCountValidator::default(),\n            label: String::new(),\n        })\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreatePipelineLayoutError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\n        \"Immediate data has range bound {size} which is not aligned to IMMEDIATE_DATA_ALIGNMENT ({})\",\n        wgt::IMMEDIATE_DATA_ALIGNMENT\n    )]\n    MisalignedImmediateSize { size: u32 },\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\n        \"Immediate data has size {size} which exceeds device immediate data size limit 0..{max}\"\n    )]\n    ImmediateRangeTooLarge { size: u32, max: u32 },\n    #[error(transparent)]\n    TooManyBindings(BindingTypeMaxCountError),\n    #[error(\"Bind group layout count {actual} exceeds device bind group limit {max}\")]\n    TooManyGroups { actual: usize, max: usize },\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(\"Bind group layout at index {index} has an exclusive pipeline: {pipeline}\")]\n    BglHasExclusivePipeline { index: usize, pipeline: String },\n}\n\nimpl WebGpuError for CreatePipelineLayoutError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::TooManyBindings(e) => e.webgpu_error_type(),\n            Self::MisalignedImmediateSize { .. }\n            | Self::ImmediateRangeTooLarge { .. }\n            | Self::TooManyGroups { .. }\n            | Self::BglHasExclusivePipeline { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ImmediateUploadError {\n    #[error(\n        \"Start offset {start_offset} overruns the immediate data range with a size of {immediate_size}\"\n    )]\n    StartOffsetOverrun {\n        start_offset: u32,\n        immediate_size: u32,\n    },\n    #[error(\n        \"Provided immediate data start offset {0} does not respect \\\n        `IMMEDIATE_DATA_ALIGNMENT` ({ida})\",\n        ida = wgt::IMMEDIATE_DATA_ALIGNMENT\n    )]\n    StartOffsetUnaligned(u32),\n    #[error(\n        \"Provided immediate data byte size {0} does not respect \\\n        `IMMEDIATE_DATA_ALIGNMENT` ({ida})\",\n        ida = wgt::IMMEDIATE_DATA_ALIGNMENT\n    )]\n    SizeUnaligned(u32),\n    #[error(\n        \"Provided immediate data start offset {} + size {} overruns the immediate data range \\\n        with a size of {}\",\n        start_offset,\n        size,\n        immediate_size\n    )]\n    EndOffsetOverrun {\n        start_offset: u32,\n        size: u32,\n        immediate_size: u32,\n    },\n    #[error(\"Start index {start_index} overruns the value data range with {data_size} element(s)\")]\n    ValueStartIndexOverrun { start_index: u32, data_size: usize },\n    #[error(\n        \"Start index {} + count of {} overruns the value data range \\\n        with {} element(s)\",\n        start_index,\n        count,\n        data_size\n    )]\n    ValueEndIndexOverrun {\n        start_index: u32,\n        count: u32,\n        data_size: usize,\n    },\n}\n\nimpl WebGpuError for ImmediateUploadError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n/// Describes a pipeline layout.\n///\n/// A `PipelineLayoutDescriptor` can be used to create a pipeline layout.\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\n#[cfg_attr(feature = \"serde\", serde(bound = \"BGL: Serialize\"))]\npub struct PipelineLayoutDescriptor<'a, BGL = BindGroupLayoutId>\nwhere\n    [Option<BGL>]: ToOwned,\n    <[Option<BGL>] as ToOwned>::Owned: fmt::Debug,\n{\n    /// Debug label of the pipeline layout.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Bind groups that this pipeline uses. The first entry will provide all the bindings for\n    /// \"set = 0\", second entry will provide all the bindings for \"set = 1\" etc.\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"<[Option<BGL>] as ToOwned>::Owned: Deserialize<'de>\"))\n    )]\n    pub bind_group_layouts: Cow<'a, [Option<BGL>]>,\n    /// The number of bytes of immediate data that are allocated for use\n    /// in the shader. The `var<immediate>`s in the shader attached to\n    /// this pipeline must be equal or smaller than this size.\n    ///\n    /// If this value is non-zero, [`wgt::Features::IMMEDIATES`] must be enabled.\n    pub immediate_size: u32,\n}\n\n/// cbindgen:ignore\npub type ResolvedPipelineLayoutDescriptor<'a, BGL = Arc<BindGroupLayout>> =\n    PipelineLayoutDescriptor<'a, BGL>;\n\n#[derive(Debug)]\npub struct PipelineLayout {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,\n    pub(crate) device: Arc<Device>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) bind_group_layouts: ArrayVec<Option<Arc<BindGroupLayout>>, { hal::MAX_BIND_GROUPS }>,\n    pub(crate) immediate_size: u32,\n}\n\nimpl Drop for PipelineLayout {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_pipeline_layout(raw);\n        }\n    }\n}\n\nimpl PipelineLayout {\n    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {\n        self.raw.as_ref()\n    }\n\n    pub fn get_bind_group_layout(\n        self: &Arc<Self>,\n        index: u32,\n    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {\n        let max_bind_groups = self.device.limits.max_bind_groups;\n        if index >= max_bind_groups {\n            return Err(GetBindGroupLayoutError::IndexOutOfRange {\n                index,\n                max: max_bind_groups,\n            });\n        }\n        Ok(self\n            .bind_group_layouts\n            .get(index as usize)\n            .cloned()\n            .flatten()\n            .unwrap_or_else(|| BindGroupLayout::empty(&self.device)))\n    }\n\n    pub(crate) fn get_bgl_entry(\n        &self,\n        group: u32,\n        binding: u32,\n    ) -> Option<&wgt::BindGroupLayoutEntry> {\n        let bgl = self.bind_group_layouts.get(group as usize)?;\n        let bgl = bgl.as_ref()?;\n        bgl.entries.get(binding)\n    }\n\n    /// Validate immediates match up with expected ranges.\n    pub(crate) fn validate_immediates_ranges(\n        &self,\n        offset: u32,\n        size_bytes: u32,\n    ) -> Result<(), ImmediateUploadError> {\n        // Don't need to validate size against the immediate data size limit here,\n        // as immediate data ranges are already validated to be within bounds,\n        // and we validate that they are within the ranges.\n\n        if !offset.is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT) {\n            return Err(ImmediateUploadError::StartOffsetUnaligned(offset));\n        }\n\n        if !size_bytes.is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT) {\n            return Err(ImmediateUploadError::SizeUnaligned(offset));\n        }\n\n        if offset > self.immediate_size {\n            return Err(ImmediateUploadError::StartOffsetOverrun {\n                start_offset: offset,\n                immediate_size: self.immediate_size,\n            });\n        }\n\n        if size_bytes > self.immediate_size - offset {\n            return Err(ImmediateUploadError::EndOffsetOverrun {\n                start_offset: offset,\n                size: size_bytes,\n                immediate_size: self.immediate_size,\n            });\n        }\n\n        Ok(())\n    }\n}\n\ncrate::impl_resource_type!(PipelineLayout);\ncrate::impl_labeled!(PipelineLayout);\ncrate::impl_parent_device!(PipelineLayout);\ncrate::impl_storage_item!(PipelineLayout);\n\n#[repr(C)]\n#[derive(Clone, Debug, Hash, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct BufferBinding<B = BufferId> {\n    pub buffer: B,\n    pub offset: wgt::BufferAddress,\n\n    /// Size of the binding. If `None`, the binding spans from `offset` to the\n    /// end of the buffer.\n    ///\n    /// We use `BufferAddress` to allow a size of zero on this `wgpu_core` type,\n    /// because JavaScript bindings cannot readily express `Option<NonZeroU64>`.\n    /// The `wgpu` API uses `Option<BufferSize>` (i.e. `NonZeroU64`) for this\n    /// field.\n    pub size: Option<wgt::BufferAddress>,\n}\n\npub type ResolvedBufferBinding = BufferBinding<Arc<Buffer>>;\n\n// Note: Duplicated in `wgpu-rs` as `BindingResource`\n// They're different enough that it doesn't make sense to share a common type\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub enum BindingResource<\n    'a,\n    B = BufferId,\n    S = SamplerId,\n    TV = TextureViewId,\n    TLAS = TlasId,\n    ET = ExternalTextureId,\n> where\n    [BufferBinding<B>]: ToOwned,\n    [S]: ToOwned,\n    [TV]: ToOwned,\n    [TLAS]: ToOwned,\n    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,\n    <[S] as ToOwned>::Owned: fmt::Debug,\n    <[TV] as ToOwned>::Owned: fmt::Debug,\n    <[TLAS] as ToOwned>::Owned: fmt::Debug,\n{\n    Buffer(BufferBinding<B>),\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>\"))\n    )]\n    BufferArray(Cow<'a, [BufferBinding<B>]>),\n    Sampler(S),\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"<[S] as ToOwned>::Owned: Deserialize<'de>\"))\n    )]\n    SamplerArray(Cow<'a, [S]>),\n    TextureView(TV),\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"<[TV] as ToOwned>::Owned: Deserialize<'de>\"))\n    )]\n    TextureViewArray(Cow<'a, [TV]>),\n    AccelerationStructure(TLAS),\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(bound(deserialize = \"<[TLAS] as ToOwned>::Owned: Deserialize<'de>\"))\n    )]\n    AccelerationStructureArray(Cow<'a, [TLAS]>),\n    ExternalTexture(ET),\n}\n\npub type ResolvedBindingResource<'a> = BindingResource<\n    'a,\n    Arc<Buffer>,\n    Arc<Sampler>,\n    Arc<TextureView>,\n    Arc<Tlas>,\n    Arc<ExternalTexture>,\n>;\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum BindError {\n    #[error(\n        \"Dynamic offsets not expected with null bind group at index {group}. However {actual} dynamic offset{s1} were provided.\",\n        s1 = if *.actual >= 2 { \"s\" } else { \"\" },\n    )]\n    DynamicOffsetCountNotZero { group: u32, actual: usize },\n    #[error(\n        \"{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.\",\n        s0 = if *.expected >= 2 { \"s\" } else { \"\" },\n        s1 = if *.actual >= 2 { \"s\" } else { \"\" },\n    )]\n    MismatchedDynamicOffsetCount {\n        bind_group: ResourceErrorIdent,\n        group: u32,\n        actual: usize,\n        expected: usize,\n    },\n    #[error(\n        \"Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}\"\n    )]\n    UnalignedDynamicBinding {\n        bind_group: ResourceErrorIdent,\n        idx: usize,\n        group: u32,\n        binding: u32,\n        offset: u32,\n        alignment: u32,\n        limit_name: &'static str,\n    },\n    #[error(\n        \"Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \\\n         Buffer size is {buffer_size} bytes, the binding binds bytes {binding_range:?}, meaning the maximum the binding can be offset is {maximum_dynamic_offset} bytes\",\n    )]\n    DynamicBindingOutOfBounds {\n        bind_group: ResourceErrorIdent,\n        idx: usize,\n        group: u32,\n        binding: u32,\n        offset: u32,\n        buffer_size: wgt::BufferAddress,\n        binding_range: Range<wgt::BufferAddress>,\n        maximum_dynamic_offset: wgt::BufferAddress,\n    },\n}\n\nimpl WebGpuError for BindError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Debug)]\npub struct BindGroupDynamicBindingData {\n    /// The index of the binding.\n    ///\n    /// Used for more descriptive errors.\n    pub(crate) binding_idx: u32,\n    /// The size of the buffer.\n    ///\n    /// Used for more descriptive errors.\n    pub(crate) buffer_size: wgt::BufferAddress,\n    /// The range that the binding covers.\n    ///\n    /// Used for more descriptive errors.\n    pub(crate) binding_range: Range<wgt::BufferAddress>,\n    /// The maximum value the dynamic offset can have before running off the end of the buffer.\n    pub(crate) maximum_dynamic_offset: wgt::BufferAddress,\n    /// The binding type.\n    pub(crate) binding_type: wgt::BufferBindingType,\n}\n\npub(crate) fn buffer_binding_type_alignment(\n    limits: &wgt::Limits,\n    binding_type: wgt::BufferBindingType,\n) -> (u32, &'static str) {\n    match binding_type {\n        wgt::BufferBindingType::Uniform => (\n            limits.min_uniform_buffer_offset_alignment,\n            \"min_uniform_buffer_offset_alignment\",\n        ),\n        wgt::BufferBindingType::Storage { .. } => (\n            limits.min_storage_buffer_offset_alignment,\n            \"min_storage_buffer_offset_alignment\",\n        ),\n    }\n}\n\npub(crate) fn buffer_binding_type_bounds_check_alignment(\n    alignments: &hal::Alignments,\n    binding_type: wgt::BufferBindingType,\n) -> wgt::BufferAddress {\n    match binding_type {\n        wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),\n        wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct BindGroupLateBufferBindingInfo {\n    /// The normal binding index in the bind group.\n    pub binding_index: u32,\n    /// The size that exists at bind time.\n    pub size: wgt::BufferSize,\n}\n\n#[derive(Debug)]\npub struct BindGroup {\n    pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) layout: Arc<BindGroupLayout>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    pub(crate) used: BindGroupStates,\n    pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,\n    pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,\n    pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,\n    /// Actual binding sizes for buffers that don't have `min_binding_size`\n    /// specified in BGL. Listed in the order of iteration of `BGL.entries`.\n    pub(crate) late_buffer_binding_infos: Vec<BindGroupLateBufferBindingInfo>,\n}\n\nimpl Drop for BindGroup {\n    fn drop(&mut self) {\n        if let Some(raw) = self.raw.take() {\n            resource_log!(\"Destroy raw {}\", self.error_ident());\n            unsafe {\n                self.device.raw().destroy_bind_group(raw);\n            }\n        }\n    }\n}\n\nimpl BindGroup {\n    pub(crate) fn try_raw<'a>(\n        &'a self,\n        guard: &'a SnatchGuard,\n    ) -> Result<&'a dyn hal::DynBindGroup, DestroyedResourceError> {\n        // Clippy insist on writing it this way. The idea is to return None\n        // if any of the raw buffer is not valid anymore.\n        for buffer in &self.used_buffer_ranges {\n            buffer.buffer.try_raw(guard)?;\n        }\n        for texture in &self.used_texture_ranges {\n            texture.texture.try_raw(guard)?;\n        }\n\n        self.raw\n            .get(guard)\n            .map(|raw| raw.as_ref())\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n\n    pub(crate) fn validate_dynamic_bindings(\n        &self,\n        bind_group_index: u32,\n        offsets: &[wgt::DynamicOffset],\n    ) -> Result<(), BindError> {\n        if self.dynamic_binding_info.len() != offsets.len() {\n            return Err(BindError::MismatchedDynamicOffsetCount {\n                bind_group: self.error_ident(),\n                group: bind_group_index,\n                expected: self.dynamic_binding_info.len(),\n                actual: offsets.len(),\n            });\n        }\n\n        for (idx, (info, &offset)) in self\n            .dynamic_binding_info\n            .iter()\n            .zip(offsets.iter())\n            .enumerate()\n        {\n            let (alignment, limit_name) =\n                buffer_binding_type_alignment(&self.device.limits, info.binding_type);\n            if !(offset as wgt::BufferAddress).is_multiple_of(alignment as u64) {\n                return Err(BindError::UnalignedDynamicBinding {\n                    bind_group: self.error_ident(),\n                    group: bind_group_index,\n                    binding: info.binding_idx,\n                    idx,\n                    offset,\n                    alignment,\n                    limit_name,\n                });\n            }\n\n            if offset as wgt::BufferAddress > info.maximum_dynamic_offset {\n                return Err(BindError::DynamicBindingOutOfBounds {\n                    bind_group: self.error_ident(),\n                    group: bind_group_index,\n                    binding: info.binding_idx,\n                    idx,\n                    offset,\n                    buffer_size: info.buffer_size,\n                    binding_range: info.binding_range.clone(),\n                    maximum_dynamic_offset: info.maximum_dynamic_offset,\n                });\n            }\n        }\n\n        Ok(())\n    }\n}\n\ncrate::impl_resource_type!(BindGroup);\ncrate::impl_labeled!(BindGroup);\ncrate::impl_parent_device!(BindGroup);\ncrate::impl_storage_item!(BindGroup);\ncrate::impl_trackable!(BindGroup);\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum GetBindGroupLayoutError {\n    #[error(\"Bind group layout index {index} is greater than the device's configured `max_bind_groups` limit {max}\")]\n    IndexOutOfRange { index: u32, max: u32 },\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for GetBindGroupLayoutError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::IndexOutOfRange { .. } => ErrorType::Validation,\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error, Eq, PartialEq)]\n#[error(\n    \"In bind group index {group_index}, the buffer bound at binding index {binding_index} \\\n     is bound with size {bound_size} where the shader expects {shader_size}.\"\n)]\npub struct LateMinBufferBindingSizeMismatch {\n    pub group_index: u32,\n    pub binding_index: u32,\n    pub shader_size: wgt::BufferAddress,\n    pub bound_size: wgt::BufferAddress,\n}\n"
  },
  {
    "path": "wgpu-core/src/command/allocator.rs",
    "content": "use alloc::{boxed::Box, vec::Vec};\n\nuse crate::lock::{rank, Mutex};\n\n/// A pool of free [`wgpu_hal::CommandEncoder`]s, owned by a `Device`.\n///\n/// Each encoder in this list is in the \"closed\" state.\n///\n/// Since a raw [`CommandEncoder`][ce] is itself a pool for allocating\n/// raw [`CommandBuffer`][cb]s, this is a pool of pools.\n///\n/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\n/// [ce]: hal::CommandEncoder\n/// [cb]: hal::Api::CommandBuffer\npub(crate) struct CommandAllocator {\n    free_encoders: Mutex<Vec<Box<dyn hal::DynCommandEncoder>>>,\n}\n\nimpl CommandAllocator {\n    pub(crate) fn new() -> Self {\n        Self {\n            free_encoders: Mutex::new(rank::COMMAND_ALLOCATOR_FREE_ENCODERS, Vec::new()),\n        }\n    }\n\n    /// Return a fresh [`wgpu_hal::CommandEncoder`] in the \"closed\" state.\n    ///\n    /// If we have free encoders in the pool, take one of those. Otherwise,\n    /// create a new one on `device`.\n    ///\n    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\n    pub(crate) fn acquire_encoder(\n        &self,\n        device: &dyn hal::DynDevice,\n        queue: &dyn hal::DynQueue,\n    ) -> Result<Box<dyn hal::DynCommandEncoder>, hal::DeviceError> {\n        let mut free_encoders = self.free_encoders.lock();\n        match free_encoders.pop() {\n            Some(encoder) => Ok(encoder),\n            None => unsafe {\n                let hal_desc = hal::CommandEncoderDescriptor { label: None, queue };\n                device.create_command_encoder(&hal_desc)\n            },\n        }\n    }\n\n    /// Add `encoder` back to the free pool.\n    pub(crate) fn release_encoder(&self, encoder: Box<dyn hal::DynCommandEncoder>) {\n        let mut free_encoders = self.free_encoders.lock();\n        free_encoders.push(encoder);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/bind.rs",
    "content": "use alloc::{boxed::Box, sync::Arc, vec::Vec};\n\nuse thiserror::Error;\n\nuse crate::{\n    binding_model::{BindGroup, LateMinBufferBindingSizeMismatch, PipelineLayout},\n    pipeline::LateSizedBufferGroup,\n    resource::{Labeled, ParentDevice, ResourceErrorIdent},\n};\n\nmod compat {\n    use alloc::{\n        string::{String, ToString as _},\n        sync::{Arc, Weak},\n        vec::Vec,\n    };\n    use core::num::NonZeroU32;\n\n    use thiserror::Error;\n    use wgt::{BindingType, ShaderStages};\n\n    use crate::{\n        binding_model::BindGroupLayout,\n        error::MultiError,\n        resource::{Labeled, ParentDevice, ResourceErrorIdent},\n    };\n\n    pub(crate) enum Error {\n        Incompatible {\n            expected_bgl: ResourceErrorIdent,\n            assigned_bgl: ResourceErrorIdent,\n            inner: MultiError,\n        },\n        Missing,\n    }\n\n    #[derive(Debug, Clone)]\n    struct Entry {\n        assigned: Option<Arc<BindGroupLayout>>,\n        expected: Option<Arc<BindGroupLayout>>,\n    }\n\n    impl Entry {\n        const fn empty() -> Self {\n            Self {\n                assigned: None,\n                expected: None,\n            }\n        }\n\n        fn is_active(&self) -> bool {\n            self.assigned.is_some() && self.expected.is_some()\n        }\n\n        fn is_valid(&self) -> bool {\n            if let Some(expected_bgl) = self.expected.as_ref() {\n                if let Some(assigned_bgl) = self.assigned.as_ref() {\n                    expected_bgl.is_equal(assigned_bgl)\n                } else {\n                    false\n                }\n            } else {\n                false\n            }\n        }\n\n        fn check(&self) -> Result<(), Error> {\n            if let Some(expected_bgl) = self.expected.as_ref() {\n                if let Some(assigned_bgl) = self.assigned.as_ref() {\n                    if expected_bgl.is_equal(assigned_bgl) {\n                        Ok(())\n                    } else {\n                        #[derive(Clone, Debug, Error)]\n                        #[error(\n                            \"Exclusive pipelines don't match: expected {expected}, got {assigned}\"\n                        )]\n                        struct IncompatibleExclusivePipelines {\n                            expected: String,\n                            assigned: String,\n                        }\n\n                        use crate::binding_model::ExclusivePipeline;\n                        match (\n                            expected_bgl.exclusive_pipeline.get().unwrap(),\n                            assigned_bgl.exclusive_pipeline.get().unwrap(),\n                        ) {\n                            (ExclusivePipeline::None, ExclusivePipeline::None) => {}\n                            (\n                                ExclusivePipeline::Render(e_pipeline),\n                                ExclusivePipeline::Render(a_pipeline),\n                            ) if Weak::ptr_eq(e_pipeline, a_pipeline) => {}\n                            (\n                                ExclusivePipeline::Compute(e_pipeline),\n                                ExclusivePipeline::Compute(a_pipeline),\n                            ) if Weak::ptr_eq(e_pipeline, a_pipeline) => {}\n                            (expected, assigned) => {\n                                return Err(Error::Incompatible {\n                                    expected_bgl: expected_bgl.error_ident(),\n                                    assigned_bgl: assigned_bgl.error_ident(),\n                                    inner: MultiError::new(core::iter::once(\n                                        IncompatibleExclusivePipelines {\n                                            expected: expected.to_string(),\n                                            assigned: assigned.to_string(),\n                                        },\n                                    ))\n                                    .unwrap(),\n                                });\n                            }\n                        }\n\n                        #[derive(Clone, Debug, Error)]\n                        enum EntryError {\n                            #[error(\"Entries with binding {binding} differ in visibility: expected {expected:?}, got {assigned:?}\")]\n                            Visibility {\n                                binding: u32,\n                                expected: ShaderStages,\n                                assigned: ShaderStages,\n                            },\n                            #[error(\"Entries with binding {binding} differ in type: expected {expected:?}, got {assigned:?}\")]\n                            Type {\n                                binding: u32,\n                                expected: BindingType,\n                                assigned: BindingType,\n                            },\n                            #[error(\"Entries with binding {binding} differ in count: expected {expected:?}, got {assigned:?}\")]\n                            Count {\n                                binding: u32,\n                                expected: Option<NonZeroU32>,\n                                assigned: Option<NonZeroU32>,\n                            },\n                            #[error(\"Expected entry with binding {binding} not found in assigned bind group layout\")]\n                            ExtraExpected { binding: u32 },\n                            #[error(\"Assigned entry with binding {binding} not found in expected bind group layout\")]\n                            ExtraAssigned { binding: u32 },\n                        }\n\n                        let mut errors = Vec::new();\n\n                        for (&binding, expected_entry) in expected_bgl.entries.iter() {\n                            if let Some(assigned_entry) = assigned_bgl.entries.get(binding) {\n                                if assigned_entry.visibility != expected_entry.visibility {\n                                    errors.push(EntryError::Visibility {\n                                        binding,\n                                        expected: expected_entry.visibility,\n                                        assigned: assigned_entry.visibility,\n                                    });\n                                }\n                                if assigned_entry.ty != expected_entry.ty {\n                                    errors.push(EntryError::Type {\n                                        binding,\n                                        expected: expected_entry.ty,\n                                        assigned: assigned_entry.ty,\n                                    });\n                                }\n                                if assigned_entry.count != expected_entry.count {\n                                    errors.push(EntryError::Count {\n                                        binding,\n                                        expected: expected_entry.count,\n                                        assigned: assigned_entry.count,\n                                    });\n                                }\n                            } else {\n                                errors.push(EntryError::ExtraExpected { binding });\n                            }\n                        }\n\n                        for (&binding, _) in assigned_bgl.entries.iter() {\n                            if !expected_bgl.entries.contains_key(binding) {\n                                errors.push(EntryError::ExtraAssigned { binding });\n                            }\n                        }\n\n                        Err(Error::Incompatible {\n                            expected_bgl: expected_bgl.error_ident(),\n                            assigned_bgl: assigned_bgl.error_ident(),\n                            inner: MultiError::new(errors.drain(..)).unwrap(),\n                        })\n                    }\n                } else {\n                    Err(Error::Missing)\n                }\n            } else {\n                Ok(())\n            }\n        }\n    }\n\n    #[derive(Debug)]\n    pub(super) struct BoundBindGroupLayouts {\n        entries: [Entry; hal::MAX_BIND_GROUPS],\n        rebind_start: usize,\n    }\n\n    impl BoundBindGroupLayouts {\n        pub fn new() -> Self {\n            Self {\n                entries: [const { Entry::empty() }; hal::MAX_BIND_GROUPS],\n                rebind_start: 0,\n            }\n        }\n\n        /// Takes the start index of the bind group range to be rebound, and clears it.\n        pub fn take_rebind_start_index(&mut self) -> usize {\n            let start = self.rebind_start;\n            self.rebind_start = self.entries.len();\n            start\n        }\n\n        pub fn update_rebind_start_index(&mut self, start_index: usize) {\n            self.rebind_start = self.rebind_start.min(start_index);\n        }\n\n        pub fn update_expectations(&mut self, expectations: &[Option<Arc<BindGroupLayout>>]) {\n            let mut rebind_start_index = None;\n\n            for (i, (e, new_expected_bgl)) in self\n                .entries\n                .iter_mut()\n                .zip(expectations.iter().chain(core::iter::repeat(&None)))\n                .enumerate()\n            {\n                let (must_set, must_rebind) = match (&mut e.expected, new_expected_bgl) {\n                    (None, None) => (false, false),\n                    (None, Some(_)) => (true, true),\n                    (Some(_), None) => (true, false),\n                    (Some(old_expected_bgl), Some(new_expected_bgl)) => {\n                        let is_different = !old_expected_bgl.is_equal(new_expected_bgl);\n                        (is_different, is_different)\n                    }\n                };\n                if must_set {\n                    e.expected = new_expected_bgl.clone();\n                }\n                if must_rebind && rebind_start_index.is_none() {\n                    rebind_start_index = Some(i);\n                }\n            }\n\n            if let Some(rebind_start_index) = rebind_start_index {\n                self.update_rebind_start_index(rebind_start_index);\n            }\n        }\n\n        pub fn assign(&mut self, index: usize, value: Arc<BindGroupLayout>) {\n            self.entries[index].assigned = Some(value);\n            self.update_rebind_start_index(index);\n        }\n\n        pub fn clear(&mut self, index: usize) {\n            self.entries[index].assigned = None;\n        }\n\n        pub fn list_active(&self) -> impl Iterator<Item = usize> + '_ {\n            self.entries\n                .iter()\n                .enumerate()\n                .filter_map(|(i, e)| if e.is_active() { Some(i) } else { None })\n        }\n\n        pub fn list_valid(&self) -> impl Iterator<Item = usize> + '_ {\n            self.entries\n                .iter()\n                .enumerate()\n                .filter_map(|(i, e)| if e.is_valid() { Some(i) } else { None })\n        }\n\n        #[allow(clippy::result_large_err)]\n        pub fn get_invalid(&self) -> Result<(), (usize, Error)> {\n            for (index, entry) in self.entries.iter().enumerate() {\n                entry.check().map_err(|e| (index, e))?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\npub enum BinderError {\n    #[error(\"The current set {pipeline} expects a BindGroup to be set at index {index}\")]\n    MissingBindGroup {\n        index: usize,\n        pipeline: ResourceErrorIdent,\n    },\n    #[error(\"The {assigned_bgl} of current set {assigned_bg} at index {index} is not compatible with the corresponding {expected_bgl} of {pipeline}\")]\n    IncompatibleBindGroup {\n        expected_bgl: ResourceErrorIdent,\n        assigned_bgl: ResourceErrorIdent,\n        assigned_bg: ResourceErrorIdent,\n        index: usize,\n        pipeline: ResourceErrorIdent,\n        #[source]\n        inner: crate::error::MultiError,\n    },\n}\n\n#[derive(Debug)]\nstruct LateBufferBinding {\n    binding_index: u32,\n    shader_expect_size: wgt::BufferAddress,\n    bound_size: wgt::BufferAddress,\n}\n\n#[derive(Debug, Default)]\nstruct EntryPayload {\n    group: Option<Arc<BindGroup>>,\n    dynamic_offsets: Vec<wgt::DynamicOffset>,\n    late_buffer_bindings: Vec<LateBufferBinding>,\n    /// Since `LateBufferBinding` may contain information about the bindings\n    /// not used by the pipeline, we need to know when to stop validating.\n    late_bindings_effective_count: usize,\n}\n\nimpl EntryPayload {\n    fn reset(&mut self) {\n        self.group = None;\n        self.dynamic_offsets.clear();\n        self.late_buffer_bindings.clear();\n        self.late_bindings_effective_count = 0;\n    }\n}\n\n#[derive(Debug)]\npub(super) struct Binder {\n    pub(super) pipeline_layout: Option<Arc<PipelineLayout>>,\n    manager: compat::BoundBindGroupLayouts,\n    payloads: [EntryPayload; hal::MAX_BIND_GROUPS],\n}\n\nimpl Binder {\n    pub(super) fn new() -> Self {\n        Self {\n            pipeline_layout: None,\n            manager: compat::BoundBindGroupLayouts::new(),\n            payloads: Default::default(),\n        }\n    }\n    pub(super) fn reset(&mut self) {\n        self.pipeline_layout = None;\n        self.manager = compat::BoundBindGroupLayouts::new();\n        for payload in self.payloads.iter_mut() {\n            payload.reset();\n        }\n    }\n\n    /// Returns `true` if the pipeline layout has been changed, i.e. if the\n    /// new PL was not the same as the old PL.\n    pub(super) fn change_pipeline_layout<'a>(\n        &'a mut self,\n        new: &Arc<PipelineLayout>,\n        late_sized_buffer_groups: &[LateSizedBufferGroup],\n    ) -> bool {\n        if let Some(old) = self.pipeline_layout.as_ref() {\n            if old.is_equal(new) {\n                return false;\n            }\n        }\n\n        let old = self.pipeline_layout.replace(new.clone());\n\n        self.manager.update_expectations(&new.bind_group_layouts);\n\n        // Update the buffer binding sizes that are required by shaders.\n\n        for (payload, late_group) in self.payloads.iter_mut().zip(late_sized_buffer_groups) {\n            payload.late_bindings_effective_count = late_group.shader_sizes.len();\n            // Update entries that already exist as the bind group was bound before the pipeline\n            // was bound.\n            for (late_binding, &shader_expect_size) in payload\n                .late_buffer_bindings\n                .iter_mut()\n                .zip(late_group.shader_sizes.iter())\n            {\n                late_binding.shader_expect_size = shader_expect_size;\n            }\n            // Add new entries for the bindings that were not known when the bind group was bound.\n            if late_group.shader_sizes.len() > payload.late_buffer_bindings.len() {\n                for &shader_expect_size in\n                    late_group.shader_sizes[payload.late_buffer_bindings.len()..].iter()\n                {\n                    payload.late_buffer_bindings.push(LateBufferBinding {\n                        binding_index: 0,\n                        shader_expect_size,\n                        bound_size: 0,\n                    });\n                }\n            }\n        }\n\n        if let Some(old) = old {\n            // root constants are the base compatibility property\n            if old.immediate_size != new.immediate_size {\n                self.manager.update_rebind_start_index(0);\n            }\n        }\n\n        true\n    }\n\n    pub(super) fn assign_group<'a>(\n        &'a mut self,\n        index: usize,\n        bind_group: &Arc<BindGroup>,\n        offsets: &[wgt::DynamicOffset],\n    ) {\n        let payload = &mut self.payloads[index];\n        payload.group = Some(bind_group.clone());\n        payload.dynamic_offsets.clear();\n        payload.dynamic_offsets.extend_from_slice(offsets);\n\n        // Fill out the actual binding sizes for buffers,\n        // whose layout doesn't specify `min_binding_size`.\n\n        // Update entries that already exist as the pipeline was bound before the group\n        // was bound.\n        for (late_binding, late_info) in payload\n            .late_buffer_bindings\n            .iter_mut()\n            .zip(bind_group.late_buffer_binding_infos.iter())\n        {\n            late_binding.binding_index = late_info.binding_index;\n            late_binding.bound_size = late_info.size.get();\n        }\n\n        // Add new entries for the bindings that were not known when the pipeline was bound.\n        if bind_group.late_buffer_binding_infos.len() > payload.late_buffer_bindings.len() {\n            for late_info in\n                bind_group.late_buffer_binding_infos[payload.late_buffer_bindings.len()..].iter()\n            {\n                payload.late_buffer_bindings.push(LateBufferBinding {\n                    binding_index: late_info.binding_index,\n                    shader_expect_size: 0,\n                    bound_size: late_info.size.get(),\n                });\n            }\n        }\n\n        self.manager.assign(index, bind_group.layout.clone());\n    }\n\n    pub(super) fn clear_group(&mut self, index: usize) {\n        self.payloads[index].reset();\n        self.manager.clear(index);\n    }\n\n    /// Takes the start index of the bind group range to be rebound, and clears it.\n    pub(super) fn take_rebind_start_index(&mut self) -> usize {\n        self.manager.take_rebind_start_index()\n    }\n\n    pub(super) fn list_valid_with_start(\n        &self,\n        start: usize,\n    ) -> impl Iterator<Item = (usize, &Arc<BindGroup>, &[wgt::DynamicOffset])> + '_ {\n        let payloads = &self.payloads;\n        self.manager\n            .list_valid()\n            .filter(move |i| *i >= start)\n            .map(move |index| {\n                (\n                    index,\n                    payloads[index].group.as_ref().unwrap(),\n                    payloads[index].dynamic_offsets.as_slice(),\n                )\n            })\n    }\n\n    pub(super) fn list_active(&self) -> impl Iterator<Item = &Arc<BindGroup>> + '_ {\n        let payloads = &self.payloads;\n        self.manager\n            .list_active()\n            .map(move |index| payloads[index].group.as_ref().unwrap())\n    }\n\n    pub(super) fn list_valid(\n        &self,\n    ) -> impl Iterator<Item = (usize, &Arc<BindGroup>, &[wgt::DynamicOffset])> + '_ {\n        self.list_valid_with_start(0)\n    }\n\n    pub(super) fn check_compatibility<T: Labeled>(\n        &self,\n        pipeline: &T,\n    ) -> Result<(), Box<BinderError>> {\n        self.manager.get_invalid().map_err(|(index, error)| {\n            Box::new(match error {\n                compat::Error::Incompatible {\n                    expected_bgl,\n                    assigned_bgl,\n                    inner,\n                } => BinderError::IncompatibleBindGroup {\n                    expected_bgl,\n                    assigned_bgl,\n                    assigned_bg: self.payloads[index].group.as_ref().unwrap().error_ident(),\n                    index,\n                    pipeline: pipeline.error_ident(),\n                    inner,\n                },\n                compat::Error::Missing => BinderError::MissingBindGroup {\n                    index,\n                    pipeline: pipeline.error_ident(),\n                },\n            })\n        })\n    }\n\n    /// Scan active buffer bindings corresponding to layouts without `min_binding_size` specified.\n    pub(super) fn check_late_buffer_bindings(\n        &self,\n    ) -> Result<(), LateMinBufferBindingSizeMismatch> {\n        for group_index in self.manager.list_active() {\n            let payload = &self.payloads[group_index];\n            for late_binding in\n                &payload.late_buffer_bindings[..payload.late_bindings_effective_count]\n            {\n                if late_binding.bound_size < late_binding.shader_expect_size {\n                    return Err(LateMinBufferBindingSizeMismatch {\n                        group_index: group_index as u32,\n                        binding_index: late_binding.binding_index,\n                        shader_size: late_binding.shader_expect_size,\n                        bound_size: late_binding.bound_size,\n                    });\n                }\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/bundle.rs",
    "content": "/*! Render Bundles\n\nA render bundle is a prerecorded sequence of commands that can be replayed on a\ncommand encoder with a single call. A single bundle can replayed any number of\ntimes, on different encoders. Constructing a render bundle lets `wgpu` validate\nand analyze its commands up front, so that replaying a bundle can be more\nefficient than simply re-recording its commands each time.\n\nNot all commands are available in bundles; for example, a render bundle may not\ncontain a [`RenderCommand::SetViewport`] command.\n\nMost of `wgpu`'s backend graphics APIs have something like bundles. For example,\nVulkan calls them \"secondary command buffers\", and Metal calls them \"indirect\ncommand buffers\". Although we plan to take advantage of these platform features\nat some point in the future, for now `wgpu`'s implementation of render bundles\ndoes not use them: at the hal level, `wgpu` render bundles just replay the\ncommands.\n\n## Render Bundle Isolation\n\nOne important property of render bundles is that the draw calls in a render\nbundle depend solely on the pipeline and state established within the render\nbundle itself. A draw call in a bundle will never use a vertex buffer, say, that\nwas set in the `RenderPass` before executing the bundle. We call this property\n'isolation', in that a render bundle is somewhat isolated from the passes that\nuse it.\n\nRender passes are also isolated from the effects of bundles. After executing a\nrender bundle, a render pass's pipeline, bind groups, and vertex and index\nbuffers are are unset, so the bundle cannot affect later draw calls in the pass.\n\nA render pass is not fully isolated from a bundle's effects on immediate data\nvalues. Draw calls following a bundle's execution will see whatever values the\nbundle writes to immediate data storage. Setting a pipeline initializes any push\nconstant storage it could access to zero, and this initialization may also be\nvisible after bundle execution.\n\n## Render Bundle Lifecycle\n\nTo create a render bundle:\n\n1) Create a [`RenderBundleEncoder`] by calling\n   [`Global::device_create_render_bundle_encoder`][Gdcrbe].\n\n2) Record commands in the `RenderBundleEncoder` using functions from the\n   [`bundle_ffi`] module.\n\n3) Call [`Global::render_bundle_encoder_finish`][Grbef], which analyzes and cleans up\n   the command stream and returns a `RenderBundleId`.\n\n4) Then, any number of times, call [`render_pass_execute_bundles`][wrpeb] to\n   execute the bundle as part of some render pass.\n\n## Implementation\n\nThe most complex part of render bundles is the \"finish\" step, mostly implemented\nin [`RenderBundleEncoder::finish`]. This consumes the commands stored in the\nencoder's [`BasePass`], while validating everything, tracking the state,\ndropping redundant or unnecessary commands, and presenting the results as a new\n[`RenderBundle`]. It doesn't actually execute any commands.\n\nThis step also enforces the 'isolation' property mentioned above: every draw\ncall is checked to ensure that the resources it uses on were established since\nthe last time the pipeline was set. This means the bundle can be executed\nverbatim without any state tracking.\n\n### Execution\n\nWhen the bundle is used in an actual render pass, `RenderBundle::execute` is\ncalled. It goes through the commands and issues them into the native command\nbuffer. Thanks to isolation, it doesn't track any bind group invalidations or\nindex format changes.\n\n[Gdcrbe]: crate::global::Global::device_create_render_bundle_encoder\n[Grbef]: crate::global::Global::render_bundle_encoder_finish\n[wrpeb]: crate::global::Global::render_pass_execute_bundles\n!*/\n\n#![allow(clippy::reversed_empty_ranges)]\n\nuse alloc::{\n    borrow::{Cow, ToOwned as _},\n    string::String,\n    sync::Arc,\n    vec::Vec,\n};\nuse core::{\n    convert::Infallible,\n    num::{NonZeroU32, NonZeroU64},\n    ops::Range,\n};\n\nuse arrayvec::ArrayVec;\nuse thiserror::Error;\n\nuse wgpu_hal::ShouldBeNonZeroExt;\nuse wgt::error::{ErrorType, WebGpuError};\n\n#[cfg(feature = \"trace\")]\nuse crate::command::ArcReferences;\nuse crate::{\n    binding_model::{BindError, BindGroup, PipelineLayout},\n    command::{\n        bind::Binder, BasePass, BindGroupStateChange, ColorAttachmentError, DrawError,\n        IdReferences, MapPassErr, PassErrorScope, RenderCommand, RenderCommandError, StateChange,\n    },\n    device::{AttachmentData, Device, DeviceError, MissingDownlevelFlags, RenderPassContext},\n    hub::Hub,\n    id,\n    init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},\n    pipeline::{PipelineFlags, RenderPipeline, VertexStep},\n    resource::{\n        Buffer, DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice,\n        RawResourceAccess, TrackingData,\n    },\n    resource_log,\n    snatch::SnatchGuard,\n    track::RenderBundleScope,\n    Label, LabelHelpers,\n};\n\nuse super::{pass, render_command::ArcRenderCommand, DrawCommandFamily, DrawKind};\n\n/// Describes a [`RenderBundleEncoder`].\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct RenderBundleEncoderDescriptor<'a> {\n    /// Debug label of the render bundle encoder.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// The formats of the color attachments that this render bundle is capable\n    /// to rendering to.\n    ///\n    /// This must match the formats of the color attachments in the\n    /// renderpass this render bundle is executed in.\n    pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,\n    /// Information about the depth attachment that this render bundle is\n    /// capable to rendering to.\n    ///\n    /// The format must match the format of the depth attachments in the\n    /// renderpass this render bundle is executed in.\n    pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,\n    /// Sample count this render bundle is capable of rendering to.\n    ///\n    /// This must match the pipelines and the renderpasses it is used in.\n    pub sample_count: u32,\n    /// If this render bundle will rendering to multiple array layers in the\n    /// attachments at the same time.\n    pub multiview: Option<NonZeroU32>,\n}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct RenderBundleEncoder {\n    base: BasePass<RenderCommand<IdReferences>, Infallible>,\n    parent_id: id::DeviceId,\n    pub(crate) context: RenderPassContext,\n    pub(crate) is_depth_read_only: bool,\n    pub(crate) is_stencil_read_only: bool,\n\n    // Resource binding dedupe state.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    current_bind_groups: BindGroupStateChange,\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    current_pipeline: StateChange<id::RenderPipelineId>,\n}\n\nimpl RenderBundleEncoder {\n    pub fn new(\n        desc: &RenderBundleEncoderDescriptor,\n        parent_id: id::DeviceId,\n    ) -> Result<Self, CreateRenderBundleError> {\n        let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {\n            Some(ds) => {\n                let aspects = hal::FormatAspects::from(ds.format);\n                (\n                    !aspects.contains(hal::FormatAspects::DEPTH) || ds.depth_read_only,\n                    !aspects.contains(hal::FormatAspects::STENCIL) || ds.stencil_read_only,\n                )\n            }\n            // There's no depth/stencil attachment, so these values just don't\n            // matter.  Choose the most accommodating value, to simplify\n            // validation.\n            None => (true, true),\n        };\n\n        // TODO: should be device.limits.max_color_attachments\n        let max_color_attachments = hal::MAX_COLOR_ATTACHMENTS;\n\n        //TODO: validate that attachment formats are renderable,\n        // have expected aspects, support multisampling.\n        Ok(Self {\n            base: BasePass::new(&desc.label),\n            parent_id,\n            context: RenderPassContext {\n                attachments: AttachmentData {\n                    colors: if desc.color_formats.len() > max_color_attachments {\n                        return Err(CreateRenderBundleError::ColorAttachment(\n                            ColorAttachmentError::TooMany {\n                                given: desc.color_formats.len(),\n                                limit: max_color_attachments,\n                            },\n                        ));\n                    } else {\n                        desc.color_formats.iter().cloned().collect()\n                    },\n                    resolves: ArrayVec::new(),\n                    depth_stencil: desc.depth_stencil.map(|ds| ds.format),\n                },\n                sample_count: {\n                    let sc = desc.sample_count;\n                    if sc == 0 || sc > 32 || !sc.is_power_of_two() {\n                        return Err(CreateRenderBundleError::InvalidSampleCount(sc));\n                    }\n                    sc\n                },\n                multiview_mask: desc.multiview,\n            },\n\n            is_depth_read_only,\n            is_stencil_read_only,\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        })\n    }\n\n    pub fn dummy(parent_id: id::DeviceId) -> Self {\n        Self {\n            base: BasePass::new(&None),\n            parent_id,\n            context: RenderPassContext {\n                attachments: AttachmentData {\n                    colors: ArrayVec::new(),\n                    resolves: ArrayVec::new(),\n                    depth_stencil: None,\n                },\n                sample_count: 0,\n                multiview_mask: None,\n            },\n            is_depth_read_only: false,\n            is_stencil_read_only: false,\n\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        }\n    }\n\n    pub fn parent(&self) -> id::DeviceId {\n        self.parent_id\n    }\n\n    /// Convert this encoder's commands into a [`RenderBundle`].\n    ///\n    /// We want executing a [`RenderBundle`] to be quick, so we take\n    /// this opportunity to clean up the [`RenderBundleEncoder`]'s\n    /// command stream and gather metadata about it that will help\n    /// keep [`ExecuteBundle`] simple and fast. We remove redundant\n    /// commands (along with their side data), note resource usage,\n    /// and accumulate buffer and texture initialization actions.\n    ///\n    /// [`ExecuteBundle`]: RenderCommand::ExecuteBundle\n    pub(crate) fn finish(\n        self,\n        desc: &RenderBundleDescriptor,\n        device: &Arc<Device>,\n        hub: &Hub,\n    ) -> Result<Arc<RenderBundle>, RenderBundleError> {\n        let scope = PassErrorScope::Bundle;\n\n        device.check_is_valid().map_pass_err(scope)?;\n\n        let bind_group_guard = hub.bind_groups.read();\n        let pipeline_guard = hub.render_pipelines.read();\n        let buffer_guard = hub.buffers.read();\n\n        let mut state = State {\n            trackers: RenderBundleScope::new(),\n            pipeline: None,\n            vertex: Default::default(),\n            index: None,\n            flat_dynamic_offsets: Vec::new(),\n            device: device.clone(),\n            commands: Vec::new(),\n            buffer_memory_init_actions: Vec::new(),\n            texture_memory_init_actions: Vec::new(),\n            next_dynamic_offset: 0,\n            binder: Binder::new(),\n        };\n\n        let indices = &state.device.tracker_indices;\n        state.trackers.buffers.set_size(indices.buffers.size());\n        state.trackers.textures.set_size(indices.textures.size());\n\n        let base = &self.base;\n\n        for command in &base.commands {\n            match command {\n                &RenderCommand::SetBindGroup {\n                    index,\n                    num_dynamic_offsets,\n                    bind_group,\n                } => {\n                    let scope = PassErrorScope::SetBindGroup;\n                    set_bind_group(\n                        &mut state,\n                        &bind_group_guard,\n                        &base.dynamic_offsets,\n                        index,\n                        num_dynamic_offsets,\n                        bind_group,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                &RenderCommand::SetPipeline(pipeline) => {\n                    let scope = PassErrorScope::SetPipelineRender;\n                    set_pipeline(\n                        &mut state,\n                        &pipeline_guard,\n                        &self.context,\n                        self.is_depth_read_only,\n                        self.is_stencil_read_only,\n                        pipeline,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                &RenderCommand::SetIndexBuffer {\n                    buffer,\n                    index_format,\n                    offset,\n                    size,\n                } => {\n                    let scope = PassErrorScope::SetIndexBuffer;\n                    set_index_buffer(\n                        &mut state,\n                        &buffer_guard,\n                        buffer,\n                        index_format,\n                        offset,\n                        size,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                &RenderCommand::SetVertexBuffer {\n                    slot,\n                    buffer,\n                    offset,\n                    size,\n                } => {\n                    let scope = PassErrorScope::SetVertexBuffer;\n                    set_vertex_buffer(&mut state, &buffer_guard, slot, buffer, offset, size)\n                        .map_pass_err(scope)?;\n                }\n                &RenderCommand::SetImmediate {\n                    offset,\n                    size_bytes,\n                    values_offset,\n                } => {\n                    let scope = PassErrorScope::SetImmediate;\n                    set_immediates(&mut state, offset, size_bytes, values_offset)\n                        .map_pass_err(scope)?;\n                }\n                &RenderCommand::Draw {\n                    vertex_count,\n                    instance_count,\n                    first_vertex,\n                    first_instance,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::Draw,\n                    };\n                    draw(\n                        &mut state,\n                        vertex_count,\n                        instance_count,\n                        first_vertex,\n                        first_instance,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                &RenderCommand::DrawIndexed {\n                    index_count,\n                    instance_count,\n                    first_index,\n                    base_vertex,\n                    first_instance,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::DrawIndexed,\n                    };\n                    draw_indexed(\n                        &mut state,\n                        index_count,\n                        instance_count,\n                        first_index,\n                        base_vertex,\n                        first_instance,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                &RenderCommand::DrawMeshTasks {\n                    group_count_x,\n                    group_count_y,\n                    group_count_z,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::DrawMeshTasks,\n                    };\n                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)\n                        .map_pass_err(scope)?;\n                }\n                &RenderCommand::DrawIndirect {\n                    buffer,\n                    offset,\n                    count: 1,\n                    family,\n                    vertex_or_index_limit: None,\n                    instance_limit: None,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::DrawIndirect,\n                        family,\n                    };\n                    multi_draw_indirect(&mut state, &buffer_guard, buffer, offset, family)\n                        .map_pass_err(scope)?;\n                }\n                &RenderCommand::DrawIndirect {\n                    count,\n                    vertex_or_index_limit,\n                    instance_limit,\n                    ..\n                } => {\n                    unreachable!(\"unexpected (multi-)draw indirect with count {count}, vertex_or_index_limits {vertex_or_index_limit:?}, instance_limit {instance_limit:?} found in a render bundle\");\n                }\n                &RenderCommand::MultiDrawIndirectCount { .. }\n                | &RenderCommand::PushDebugGroup { color: _, len: _ }\n                | &RenderCommand::InsertDebugMarker { color: _, len: _ }\n                | &RenderCommand::PopDebugGroup => {\n                    unimplemented!(\"not supported by a render bundle\")\n                }\n                // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature\n                &RenderCommand::WriteTimestamp { .. }\n                | &RenderCommand::BeginOcclusionQuery { .. }\n                | &RenderCommand::EndOcclusionQuery\n                | &RenderCommand::BeginPipelineStatisticsQuery { .. }\n                | &RenderCommand::EndPipelineStatisticsQuery => {\n                    unimplemented!(\"not supported by a render bundle\")\n                }\n                &RenderCommand::ExecuteBundle(_)\n                | &RenderCommand::SetBlendConstant(_)\n                | &RenderCommand::SetStencilReference(_)\n                | &RenderCommand::SetViewport { .. }\n                | &RenderCommand::SetScissor(_) => unreachable!(\"not supported by a render bundle\"),\n            }\n        }\n\n        let State {\n            trackers,\n            flat_dynamic_offsets,\n            device,\n            commands,\n            buffer_memory_init_actions,\n            texture_memory_init_actions,\n            ..\n        } = state;\n\n        let tracker_indices = device.tracker_indices.bundles.clone();\n        let discard_hal_labels = device\n            .instance_flags\n            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);\n\n        let render_bundle = RenderBundle {\n            base: BasePass {\n                label: desc.label.as_deref().map(str::to_owned),\n                error: None,\n                commands,\n                dynamic_offsets: flat_dynamic_offsets,\n                string_data: self.base.string_data,\n                immediates_data: self.base.immediates_data,\n            },\n            is_depth_read_only: self.is_depth_read_only,\n            is_stencil_read_only: self.is_stencil_read_only,\n            device: device.clone(),\n            used: trackers,\n            buffer_memory_init_actions,\n            texture_memory_init_actions,\n            context: self.context,\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(tracker_indices),\n            discard_hal_labels,\n        };\n\n        let render_bundle = Arc::new(render_bundle);\n\n        Ok(render_bundle)\n    }\n\n    pub fn set_index_buffer(\n        &mut self,\n        buffer: id::BufferId,\n        index_format: wgt::IndexFormat,\n        offset: wgt::BufferAddress,\n        size: Option<wgt::BufferSize>,\n    ) {\n        self.base.commands.push(RenderCommand::SetIndexBuffer {\n            buffer,\n            index_format,\n            offset,\n            size,\n        });\n    }\n}\n\nfn set_bind_group(\n    state: &mut State,\n    bind_group_guard: &crate::storage::Storage<Fallible<BindGroup>>,\n    dynamic_offsets: &[u32],\n    index: u32,\n    num_dynamic_offsets: usize,\n    bind_group_id: Option<id::Id<id::markers::BindGroup>>,\n) -> Result<(), RenderBundleErrorInner> {\n    let max_bind_groups = state.device.limits.max_bind_groups;\n    if index >= max_bind_groups {\n        return Err(\n            RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {\n                index,\n                max: max_bind_groups,\n            })\n            .into(),\n        );\n    }\n\n    // Identify the next `num_dynamic_offsets` entries from `dynamic_offsets`.\n    let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;\n    state.next_dynamic_offset = offsets_range.end;\n    let offsets = &dynamic_offsets[offsets_range.clone()];\n\n    let bind_group = bind_group_id.map(|id| bind_group_guard.get(id));\n\n    if let Some(bind_group) = bind_group {\n        let bind_group = bind_group.get()?;\n        bind_group.same_device(&state.device)?;\n        bind_group.validate_dynamic_bindings(index, offsets)?;\n\n        unsafe { state.trackers.merge_bind_group(&bind_group.used)? };\n        let bind_group = state.trackers.bind_groups.insert_single(bind_group);\n\n        state\n            .binder\n            .assign_group(index as usize, bind_group, offsets);\n    } else {\n        if !offsets.is_empty() {\n            return Err(RenderBundleErrorInner::Bind(\n                BindError::DynamicOffsetCountNotZero {\n                    group: index,\n                    actual: offsets.len(),\n                },\n            ));\n        }\n\n        state.binder.clear_group(index as usize);\n    }\n\n    Ok(())\n}\n\nfn set_pipeline(\n    state: &mut State,\n    pipeline_guard: &crate::storage::Storage<Fallible<RenderPipeline>>,\n    context: &RenderPassContext,\n    is_depth_read_only: bool,\n    is_stencil_read_only: bool,\n    pipeline_id: id::Id<id::markers::RenderPipeline>,\n) -> Result<(), RenderBundleErrorInner> {\n    let pipeline = pipeline_guard.get(pipeline_id).get()?;\n\n    pipeline.same_device(&state.device)?;\n\n    context\n        .check_compatible(&pipeline.pass_context, pipeline.as_ref())\n        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;\n\n    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {\n        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());\n    }\n    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {\n        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());\n    }\n\n    let pipeline_state = PipelineState::new(&pipeline);\n\n    state\n        .commands\n        .push(ArcRenderCommand::SetPipeline(pipeline.clone()));\n\n    // If this pipeline uses immediates, zero out their values.\n    if let Some(cmd) = pipeline_state.zero_immediates() {\n        state.commands.push(cmd);\n    }\n\n    state.pipeline = Some(pipeline_state);\n\n    state\n        .binder\n        .change_pipeline_layout(&pipeline.layout, &pipeline.late_sized_buffer_groups);\n\n    state.trackers.render_pipelines.insert_single(pipeline);\n    Ok(())\n}\n\n// This function is duplicative of `render::set_index_buffer`.\nfn set_index_buffer(\n    state: &mut State,\n    buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,\n    buffer_id: id::Id<id::markers::Buffer>,\n    index_format: wgt::IndexFormat,\n    offset: u64,\n    size: Option<NonZeroU64>,\n) -> Result<(), RenderBundleErrorInner> {\n    let buffer = buffer_guard.get(buffer_id).get()?;\n\n    state\n        .trackers\n        .buffers\n        .merge_single(&buffer, wgt::BufferUses::INDEX)?;\n\n    buffer.same_device(&state.device)?;\n    buffer.check_usage(wgt::BufferUsages::INDEX)?;\n\n    if !offset.is_multiple_of(u64::try_from(index_format.byte_size()).unwrap()) {\n        return Err(RenderCommandError::UnalignedIndexBuffer {\n            offset,\n            alignment: index_format.byte_size(),\n        }\n        .into());\n    }\n    let end = offset + buffer.resolve_binding_size(offset, size)?;\n\n    state\n        .buffer_memory_init_actions\n        .extend(buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..end.get(),\n            MemoryInitKind::NeedsInitializedMemory,\n        ));\n    state.set_index_buffer(buffer, index_format, offset..end.get());\n    Ok(())\n}\n\n// This function is duplicative of `render::set_vertex_buffer`.\nfn set_vertex_buffer(\n    state: &mut State,\n    buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,\n    slot: u32,\n    buffer_id: id::Id<id::markers::Buffer>,\n    offset: u64,\n    size: Option<NonZeroU64>,\n) -> Result<(), RenderBundleErrorInner> {\n    let max_vertex_buffers = state.device.limits.max_vertex_buffers;\n    if slot >= max_vertex_buffers {\n        return Err(RenderCommandError::VertexBufferIndexOutOfRange {\n            index: slot,\n            max: max_vertex_buffers,\n        }\n        .into());\n    }\n\n    let buffer = buffer_guard.get(buffer_id).get()?;\n\n    state\n        .trackers\n        .buffers\n        .merge_single(&buffer, wgt::BufferUses::VERTEX)?;\n\n    buffer.same_device(&state.device)?;\n    buffer.check_usage(wgt::BufferUsages::VERTEX)?;\n\n    if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {\n        return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());\n    }\n    let end = offset + buffer.resolve_binding_size(offset, size)?;\n\n    state\n        .buffer_memory_init_actions\n        .extend(buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..end.get(),\n            MemoryInitKind::NeedsInitializedMemory,\n        ));\n    state.vertex[slot as usize] = Some(VertexState::new(buffer, offset..end.get()));\n    Ok(())\n}\n\nfn set_immediates(\n    state: &mut State,\n    offset: u32,\n    size_bytes: u32,\n    values_offset: Option<u32>,\n) -> Result<(), RenderBundleErrorInner> {\n    let pipeline_state = state.pipeline()?;\n\n    pipeline_state\n        .pipeline\n        .layout\n        .validate_immediates_ranges(offset, size_bytes)?;\n\n    state.commands.push(ArcRenderCommand::SetImmediate {\n        offset,\n        size_bytes,\n        values_offset,\n    });\n    Ok(())\n}\n\nfn draw(\n    state: &mut State,\n    vertex_count: u32,\n    instance_count: u32,\n    first_vertex: u32,\n    first_instance: u32,\n) -> Result<(), RenderBundleErrorInner> {\n    state.is_ready(DrawCommandFamily::Draw)?;\n    let pipeline = state.pipeline()?;\n\n    let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);\n    vertex_limits.validate_vertex_limit(first_vertex, vertex_count)?;\n    vertex_limits.validate_instance_limit(first_instance, instance_count)?;\n\n    if instance_count > 0 && vertex_count > 0 {\n        state.flush_vertices();\n        state.flush_bindings();\n        state.commands.push(ArcRenderCommand::Draw {\n            vertex_count,\n            instance_count,\n            first_vertex,\n            first_instance,\n        });\n    }\n    Ok(())\n}\n\nfn draw_indexed(\n    state: &mut State,\n    index_count: u32,\n    instance_count: u32,\n    first_index: u32,\n    base_vertex: i32,\n    first_instance: u32,\n) -> Result<(), RenderBundleErrorInner> {\n    state.is_ready(DrawCommandFamily::DrawIndexed)?;\n    let pipeline = state.pipeline()?;\n\n    let index = state.index.as_ref().unwrap();\n\n    let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);\n\n    let last_index = first_index as u64 + index_count as u64;\n    let index_limit = index.limit();\n    if last_index > index_limit {\n        return Err(DrawError::IndexBeyondLimit {\n            last_index,\n            index_limit,\n        }\n        .into());\n    }\n    vertex_limits.validate_instance_limit(first_instance, instance_count)?;\n\n    if instance_count > 0 && index_count > 0 {\n        state.flush_index();\n        state.flush_vertices();\n        state.flush_bindings();\n        state.commands.push(ArcRenderCommand::DrawIndexed {\n            index_count,\n            instance_count,\n            first_index,\n            base_vertex,\n            first_instance,\n        });\n    }\n    Ok(())\n}\n\nfn draw_mesh_tasks(\n    state: &mut State,\n    group_count_x: u32,\n    group_count_y: u32,\n    group_count_z: u32,\n) -> Result<(), RenderBundleErrorInner> {\n    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;\n\n    let groups_size_limit = state.device.limits.max_task_mesh_workgroups_per_dimension;\n    let max_groups = state.device.limits.max_task_mesh_workgroup_total_count;\n    if group_count_x > groups_size_limit\n        || group_count_y > groups_size_limit\n        || group_count_z > groups_size_limit\n        || group_count_x * group_count_y * group_count_z > max_groups\n    {\n        return Err(RenderBundleErrorInner::Draw(DrawError::InvalidGroupSize {\n            current: [group_count_x, group_count_y, group_count_z],\n            limit: groups_size_limit,\n            max_total: max_groups,\n        }));\n    }\n\n    if group_count_x > 0 && group_count_y > 0 && group_count_z > 0 {\n        state.flush_bindings();\n        state.commands.push(ArcRenderCommand::DrawMeshTasks {\n            group_count_x,\n            group_count_y,\n            group_count_z,\n        });\n    }\n    Ok(())\n}\n\nfn multi_draw_indirect(\n    state: &mut State,\n    buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,\n    buffer_id: id::Id<id::markers::Buffer>,\n    offset: u64,\n    family: DrawCommandFamily,\n) -> Result<(), RenderBundleErrorInner> {\n    state.is_ready(family)?;\n    state\n        .device\n        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;\n\n    let pipeline = state.pipeline()?;\n\n    let buffer = buffer_guard.get(buffer_id).get()?;\n\n    buffer.same_device(&state.device)?;\n    buffer.check_usage(wgt::BufferUsages::INDIRECT)?;\n\n    let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);\n\n    let stride = super::get_stride_of_indirect_args(family);\n    state\n        .buffer_memory_init_actions\n        .extend(buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..(offset + stride),\n            MemoryInitKind::NeedsInitializedMemory,\n        ));\n\n    let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {\n        let index = state.index.as_mut().unwrap();\n        state.commands.extend(index.flush());\n        index.limit()\n    } else {\n        vertex_limits.vertex_limit\n    };\n    let instance_limit = vertex_limits.instance_limit;\n\n    let buffer_uses = if state.device.indirect_validation.is_some() {\n        wgt::BufferUses::STORAGE_READ_ONLY\n    } else {\n        wgt::BufferUses::INDIRECT\n    };\n\n    state.trackers.buffers.merge_single(&buffer, buffer_uses)?;\n\n    state.flush_vertices();\n    state.flush_bindings();\n    state.commands.push(ArcRenderCommand::DrawIndirect {\n        buffer,\n        offset,\n        count: 1,\n        family,\n\n        vertex_or_index_limit: Some(vertex_or_index_limit),\n        instance_limit: Some(instance_limit),\n    });\n    Ok(())\n}\n\n/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateRenderBundleError {\n    #[error(transparent)]\n    ColorAttachment(#[from] ColorAttachmentError),\n    #[error(\"Invalid number of samples {0}\")]\n    InvalidSampleCount(u32),\n}\n\nimpl WebGpuError for CreateRenderBundleError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::ColorAttachment(e) => e.webgpu_error_type(),\n            Self::InvalidSampleCount(_) => ErrorType::Validation,\n        }\n    }\n}\n\n/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ExecutionError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"Using {0} in a render bundle is not implemented\")]\n    Unimplemented(&'static str),\n}\n\npub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;\n\n//Note: here, `RenderBundle` is just wrapping a raw stream of render commands.\n// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle,\n// or Metal indirect command buffer.\n/// cbindgen:ignore\n#[derive(Debug)]\npub struct RenderBundle {\n    // Normalized command stream. It can be executed verbatim,\n    // without re-binding anything on the pipeline change.\n    base: BasePass<ArcRenderCommand, Infallible>,\n    pub(super) is_depth_read_only: bool,\n    pub(super) is_stencil_read_only: bool,\n    pub(crate) device: Arc<Device>,\n    pub(crate) used: RenderBundleScope,\n    pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,\n    pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,\n    pub(super) context: RenderPassContext,\n    /// The `label` from the descriptor used to create the resource.\n    label: String,\n    pub(crate) tracking_data: TrackingData,\n    discard_hal_labels: bool,\n}\n\nimpl Drop for RenderBundle {\n    fn drop(&mut self) {\n        resource_log!(\"Drop {}\", self.error_ident());\n    }\n}\n\n#[cfg(send_sync)]\nunsafe impl Send for RenderBundle {}\n#[cfg(send_sync)]\nunsafe impl Sync for RenderBundle {}\n\nimpl RenderBundle {\n    #[cfg(feature = \"trace\")]\n    pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {\n        self.base.clone()\n    }\n\n    /// Actually encode the contents into a native command buffer.\n    ///\n    /// This is partially duplicating the logic of `render_pass_end`.\n    /// However the point of this function is to be lighter, since we already had\n    /// a chance to go through the commands in `render_bundle_encoder_finish`.\n    ///\n    /// Note that the function isn't expected to fail, generally.\n    /// All the validation has already been done by this point.\n    /// The only failure condition is if some of the used buffers are destroyed.\n    pub(super) unsafe fn execute(\n        &self,\n        raw: &mut dyn hal::DynCommandEncoder,\n        indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,\n        indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,\n        snatch_guard: &SnatchGuard,\n    ) -> Result<(), ExecutionError> {\n        let mut offsets = self.base.dynamic_offsets.as_slice();\n        let mut pipeline_layout = None::<Arc<PipelineLayout>>;\n        if !self.discard_hal_labels {\n            if let Some(ref label) = self.base.label {\n                unsafe { raw.begin_debug_marker(label) };\n            }\n        }\n\n        use ArcRenderCommand as Cmd;\n        for command in self.base.commands.iter() {\n            match command {\n                Cmd::SetBindGroup {\n                    index,\n                    num_dynamic_offsets,\n                    bind_group,\n                } => {\n                    let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;\n                    unsafe {\n                        raw.set_bind_group(\n                            pipeline_layout.as_ref().unwrap().raw(),\n                            *index,\n                            raw_bg,\n                            &offsets[..*num_dynamic_offsets],\n                        )\n                    };\n                    offsets = &offsets[*num_dynamic_offsets..];\n                }\n                Cmd::SetPipeline(pipeline) => {\n                    unsafe { raw.set_render_pipeline(pipeline.raw()) };\n\n                    pipeline_layout = Some(pipeline.layout.clone());\n                }\n                Cmd::SetIndexBuffer {\n                    buffer,\n                    index_format,\n                    offset,\n                    size,\n                } => {\n                    let buffer = buffer.try_raw(snatch_guard)?;\n                    // SAFETY: The binding size was checked against the buffer size\n                    // in `set_index_buffer` and again in `IndexState::flush`.\n                    let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);\n                    unsafe { raw.set_index_buffer(bb, *index_format) };\n                }\n                Cmd::SetVertexBuffer {\n                    slot,\n                    buffer,\n                    offset,\n                    size,\n                } => {\n                    let buffer = buffer.try_raw(snatch_guard)?;\n                    // SAFETY: The binding size was checked against the buffer size\n                    // in `set_vertex_buffer` and again in `VertexState::flush`.\n                    let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);\n                    unsafe { raw.set_vertex_buffer(*slot, bb) };\n                }\n                Cmd::SetImmediate {\n                    offset,\n                    size_bytes,\n                    values_offset,\n                } => {\n                    let pipeline_layout = pipeline_layout.as_ref().unwrap();\n\n                    if let Some(values_offset) = *values_offset {\n                        let values_end_offset =\n                            (values_offset + size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;\n                        let data_slice =\n                            &self.base.immediates_data[(values_offset as usize)..values_end_offset];\n\n                        unsafe { raw.set_immediates(pipeline_layout.raw(), *offset, data_slice) }\n                    } else {\n                        super::immediates_clear(\n                            *offset,\n                            *size_bytes,\n                            |clear_offset, clear_data| {\n                                unsafe {\n                                    raw.set_immediates(\n                                        pipeline_layout.raw(),\n                                        clear_offset,\n                                        clear_data,\n                                    )\n                                };\n                            },\n                        );\n                    }\n                }\n                Cmd::Draw {\n                    vertex_count,\n                    instance_count,\n                    first_vertex,\n                    first_instance,\n                } => {\n                    unsafe {\n                        raw.draw(\n                            *first_vertex,\n                            *vertex_count,\n                            *first_instance,\n                            *instance_count,\n                        )\n                    };\n                }\n                Cmd::DrawIndexed {\n                    index_count,\n                    instance_count,\n                    first_index,\n                    base_vertex,\n                    first_instance,\n                } => {\n                    unsafe {\n                        raw.draw_indexed(\n                            *first_index,\n                            *index_count,\n                            *base_vertex,\n                            *first_instance,\n                            *instance_count,\n                        )\n                    };\n                }\n                Cmd::DrawMeshTasks {\n                    group_count_x,\n                    group_count_y,\n                    group_count_z,\n                } => unsafe {\n                    raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);\n                },\n                Cmd::DrawIndirect {\n                    buffer,\n                    offset,\n                    count: 1,\n                    family,\n\n                    vertex_or_index_limit,\n                    instance_limit,\n                } => {\n                    let (buffer, offset) = if self.device.indirect_validation.is_some() {\n                        let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(\n                            indirect_draw_validation_resources,\n                            &self.device,\n                            buffer,\n                            *offset,\n                            *family,\n                            vertex_or_index_limit\n                                .expect(\"finalized render bundle missing vertex_or_index_limit\"),\n                            instance_limit.expect(\"finalized render bundle missing instance_limit\"),\n                        )?;\n\n                        let dst_buffer =\n                            indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);\n                        (dst_buffer, offset)\n                    } else {\n                        (buffer.try_raw(snatch_guard)?, *offset)\n                    };\n                    match family {\n                        DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },\n                        DrawCommandFamily::DrawIndexed => unsafe {\n                            raw.draw_indexed_indirect(buffer, offset, 1)\n                        },\n                        DrawCommandFamily::DrawMeshTasks => unsafe {\n                            raw.draw_mesh_tasks_indirect(buffer, offset, 1);\n                        },\n                    }\n                }\n                Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {\n                    return Err(ExecutionError::Unimplemented(\"multi-draw-indirect\"))\n                }\n                Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {\n                    return Err(ExecutionError::Unimplemented(\"debug-markers\"))\n                }\n                Cmd::WriteTimestamp { .. }\n                | Cmd::BeginOcclusionQuery { .. }\n                | Cmd::EndOcclusionQuery\n                | Cmd::BeginPipelineStatisticsQuery { .. }\n                | Cmd::EndPipelineStatisticsQuery => {\n                    return Err(ExecutionError::Unimplemented(\"queries\"))\n                }\n                Cmd::ExecuteBundle(_)\n                | Cmd::SetBlendConstant(_)\n                | Cmd::SetStencilReference(_)\n                | Cmd::SetViewport { .. }\n                | Cmd::SetScissor(_) => unreachable!(),\n            }\n        }\n\n        if !self.discard_hal_labels {\n            if let Some(_) = self.base.label {\n                unsafe { raw.end_debug_marker() };\n            }\n        }\n\n        Ok(())\n    }\n}\n\ncrate::impl_resource_type!(RenderBundle);\ncrate::impl_labeled!(RenderBundle);\ncrate::impl_parent_device!(RenderBundle);\ncrate::impl_storage_item!(RenderBundle);\ncrate::impl_trackable!(RenderBundle);\n\n/// A render bundle's current index buffer state.\n///\n/// [`RenderBundleEncoder::finish`] records the currently set index buffer here,\n/// and calls [`State::flush_index`] before any indexed draw command to produce\n/// a `SetIndexBuffer` command if one is necessary.\n///\n/// Binding ranges must be validated against the size of the buffer before\n/// being stored in `IndexState`.\n#[derive(Debug)]\nstruct IndexState {\n    buffer: Arc<Buffer>,\n    format: wgt::IndexFormat,\n    range: Range<wgt::BufferAddress>,\n    is_dirty: bool,\n}\n\nimpl IndexState {\n    /// Return the number of entries in the current index buffer.\n    ///\n    /// Panic if no index buffer has been set.\n    fn limit(&self) -> u64 {\n        let bytes_per_index = self.format.byte_size() as u64;\n\n        (self.range.end - self.range.start) / bytes_per_index\n    }\n\n    /// Generate a `SetIndexBuffer` command to prepare for an indexed draw\n    /// command, if needed.\n    fn flush(&mut self) -> Option<ArcRenderCommand> {\n        // This was all checked before, but let's check again just in case.\n        let binding_size = self\n            .range\n            .end\n            .checked_sub(self.range.start)\n            .filter(|_| self.range.end <= self.buffer.size)\n            .expect(\"index range must be contained in buffer\");\n\n        if self.is_dirty {\n            self.is_dirty = false;\n            Some(ArcRenderCommand::SetIndexBuffer {\n                buffer: self.buffer.clone(),\n                index_format: self.format,\n                offset: self.range.start,\n                size: NonZeroU64::new(binding_size),\n            })\n        } else {\n            None\n        }\n    }\n}\n\n/// The state of a single vertex buffer slot during render bundle encoding.\n///\n/// [`RenderBundleEncoder::finish`] uses this to drop redundant\n/// `SetVertexBuffer` commands from the final [`RenderBundle`]. It\n/// records one vertex buffer slot's state changes here, and then\n/// calls this type's [`flush`] method just before any draw command to\n/// produce a `SetVertexBuffer` commands if one is necessary.\n///\n/// Binding ranges must be validated against the size of the buffer before\n/// being stored in `VertexState`.\n///\n/// [`flush`]: IndexState::flush\n#[derive(Debug)]\nstruct VertexState {\n    buffer: Arc<Buffer>,\n    range: Range<wgt::BufferAddress>,\n    is_dirty: bool,\n}\n\nimpl VertexState {\n    /// Create a new `VertexState`.\n    ///\n    /// The `range` must be contained within `buffer`.\n    fn new(buffer: Arc<Buffer>, range: Range<wgt::BufferAddress>) -> Self {\n        Self {\n            buffer,\n            range,\n            is_dirty: true,\n        }\n    }\n\n    /// Generate a `SetVertexBuffer` command for this slot, if necessary.\n    ///\n    /// `slot` is the index of the vertex buffer slot that `self` tracks.\n    fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand> {\n        let binding_size = self\n            .range\n            .end\n            .checked_sub(self.range.start)\n            .filter(|_| self.range.end <= self.buffer.size)\n            .expect(\"vertex range must be contained in buffer\");\n\n        if self.is_dirty {\n            self.is_dirty = false;\n            Some(ArcRenderCommand::SetVertexBuffer {\n                slot,\n                buffer: self.buffer.clone(),\n                offset: self.range.start,\n                size: NonZeroU64::new(binding_size),\n            })\n        } else {\n            None\n        }\n    }\n}\n\n/// The bundle's current pipeline, and some cached information needed for validation.\nstruct PipelineState {\n    /// The pipeline\n    pipeline: Arc<RenderPipeline>,\n\n    /// How this pipeline's vertex shader traverses each vertex buffer, indexed\n    /// by vertex buffer slot number.\n    steps: Vec<VertexStep>,\n\n    /// Size of the immediate data ranges this pipeline uses. Copied from the pipeline layout.\n    immediate_size: u32,\n}\n\nimpl PipelineState {\n    fn new(pipeline: &Arc<RenderPipeline>) -> Self {\n        Self {\n            pipeline: pipeline.clone(),\n            steps: pipeline.vertex_steps.to_vec(),\n            immediate_size: pipeline.layout.immediate_size,\n        }\n    }\n\n    /// Return a sequence of commands to zero the immediate data ranges this\n    /// pipeline uses. If no initialization is necessary, return `None`.\n    fn zero_immediates(&self) -> Option<ArcRenderCommand> {\n        if self.immediate_size == 0 {\n            return None;\n        }\n\n        Some(ArcRenderCommand::SetImmediate {\n            offset: 0,\n            size_bytes: self.immediate_size,\n            values_offset: None,\n        })\n    }\n}\n\n/// State for analyzing and cleaning up bundle command streams.\n///\n/// To minimize state updates, [`RenderBundleEncoder::finish`]\n/// actually just applies commands like [`SetBindGroup`] and\n/// [`SetIndexBuffer`] to the simulated state stored here, and then\n/// calls the `flush_foo` methods before draw calls to produce the\n/// update commands we actually need.\n///\n/// [`SetBindGroup`]: RenderCommand::SetBindGroup\n/// [`SetIndexBuffer`]: RenderCommand::SetIndexBuffer\nstruct State {\n    /// Resources used by this bundle. This will become [`RenderBundle::used`].\n    trackers: RenderBundleScope,\n\n    /// The currently set pipeline, if any.\n    pipeline: Option<PipelineState>,\n\n    /// The state of each vertex buffer slot.\n    vertex: [Option<VertexState>; hal::MAX_VERTEX_BUFFERS],\n\n    /// The current index buffer, if one has been set. We flush this state\n    /// before indexed draw commands.\n    index: Option<IndexState>,\n\n    /// Dynamic offset values used by the cleaned-up command sequence.\n    ///\n    /// This becomes the final [`RenderBundle`]'s [`BasePass`]'s\n    /// [`dynamic_offsets`] list.\n    ///\n    /// [`dynamic_offsets`]: BasePass::dynamic_offsets\n    flat_dynamic_offsets: Vec<wgt::DynamicOffset>,\n\n    device: Arc<Device>,\n    commands: Vec<ArcRenderCommand>,\n    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,\n    texture_memory_init_actions: Vec<TextureInitTrackerAction>,\n    next_dynamic_offset: usize,\n    binder: Binder,\n}\n\nimpl State {\n    /// Return the current pipeline state. Return an error if none is set.\n    fn pipeline(&self) -> Result<&PipelineState, RenderBundleErrorInner> {\n        self.pipeline\n            .as_ref()\n            .ok_or(DrawError::MissingPipeline(pass::MissingPipeline).into())\n    }\n\n    /// Set the bundle's current index buffer and its associated parameters.\n    fn set_index_buffer(\n        &mut self,\n        buffer: Arc<Buffer>,\n        format: wgt::IndexFormat,\n        range: Range<wgt::BufferAddress>,\n    ) {\n        match self.index {\n            Some(ref current)\n                if current.buffer.is_equal(&buffer)\n                    && current.format == format\n                    && current.range == range =>\n            {\n                return\n            }\n            _ => (),\n        }\n\n        self.index = Some(IndexState {\n            buffer,\n            format,\n            range,\n            is_dirty: true,\n        });\n    }\n\n    /// Generate a `SetIndexBuffer` command to prepare for an indexed draw\n    /// command, if needed.\n    fn flush_index(&mut self) {\n        let commands = self.index.as_mut().and_then(|index| index.flush());\n        self.commands.extend(commands);\n    }\n\n    fn flush_vertices(&mut self) {\n        let commands = self\n            .vertex\n            .iter_mut()\n            .enumerate()\n            .flat_map(|(i, vs)| vs.as_mut().and_then(|vs| vs.flush(i as u32)));\n        self.commands.extend(commands);\n    }\n\n    /// Validation for a draw command.\n    ///\n    /// This should be further deduplicated with similar validation on render/compute passes.\n    fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {\n        if let Some(pipeline) = self.pipeline.as_ref() {\n            self.binder\n                .check_compatibility(pipeline.pipeline.as_ref())?;\n            self.binder.check_late_buffer_bindings()?;\n\n            if family == DrawCommandFamily::DrawIndexed {\n                let pipeline = &pipeline.pipeline;\n                let index_format = match &self.index {\n                    Some(index) => index.format,\n                    None => return Err(DrawError::MissingIndexBuffer),\n                };\n\n                if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)\n                {\n                    return Err(DrawError::UnmatchedStripIndexFormat {\n                        pipeline: pipeline.error_ident(),\n                        strip_index_format: pipeline.strip_index_format,\n                        buffer_format: index_format,\n                    });\n                }\n            }\n\n            Ok(())\n        } else {\n            Err(DrawError::MissingPipeline(pass::MissingPipeline))\n        }\n    }\n\n    /// Generate `SetBindGroup` commands for any bind groups that need to be updated.\n    ///\n    /// This should be further deduplicated with similar code on render/compute passes.\n    fn flush_bindings(&mut self) {\n        let start = self.binder.take_rebind_start_index();\n        let entries = self.binder.list_valid_with_start(start);\n\n        self.commands\n            .extend(entries.map(|(i, bind_group, dynamic_offsets)| {\n                self.buffer_memory_init_actions\n                    .extend_from_slice(&bind_group.used_buffer_ranges);\n                self.texture_memory_init_actions\n                    .extend_from_slice(&bind_group.used_texture_ranges);\n\n                self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);\n\n                ArcRenderCommand::SetBindGroup {\n                    index: i.try_into().unwrap(),\n                    bind_group: Some(bind_group.clone()),\n                    num_dynamic_offsets: dynamic_offsets.len(),\n                }\n            }));\n    }\n\n    fn vertex_buffer_sizes(&self) -> impl Iterator<Item = Option<wgt::BufferAddress>> + '_ {\n        self.vertex\n            .iter()\n            .map(|vbs| vbs.as_ref().map(|vbs| vbs.range.end - vbs.range.start))\n    }\n}\n\n/// Error encountered when finishing recording a render bundle.\n#[derive(Clone, Debug, Error)]\npub enum RenderBundleErrorInner {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    RenderCommand(RenderCommandError),\n    #[error(transparent)]\n    Draw(#[from] DrawError),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(transparent)]\n    Bind(#[from] BindError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl<T> From<T> for RenderBundleErrorInner\nwhere\n    T: Into<RenderCommandError>,\n{\n    fn from(t: T) -> Self {\n        Self::RenderCommand(t.into())\n    }\n}\n\n/// Error encountered when finishing recording a render bundle.\n#[derive(Clone, Debug, Error)]\n#[error(\"{scope}\")]\npub struct RenderBundleError {\n    pub scope: PassErrorScope,\n    #[source]\n    inner: RenderBundleErrorInner,\n}\n\nimpl WebGpuError for RenderBundleError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        let Self { scope: _, inner } = self;\n        match inner {\n            RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),\n            RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),\n            RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),\n            RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),\n            RenderBundleErrorInner::InvalidResource(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\nimpl RenderBundleError {\n    pub fn from_device_error(e: DeviceError) -> Self {\n        Self {\n            scope: PassErrorScope::Bundle,\n            inner: e.into(),\n        }\n    }\n}\n\nimpl<E> MapPassErr<RenderBundleError> for E\nwhere\n    E: Into<RenderBundleErrorInner>,\n{\n    fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {\n        RenderBundleError {\n            scope,\n            inner: self.into(),\n        }\n    }\n}\n\npub mod bundle_ffi {\n    use super::{RenderBundleEncoder, RenderCommand};\n    use crate::{command::DrawCommandFamily, id, RawString};\n    use core::{convert::TryInto, slice};\n    use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};\n\n    /// # Safety\n    ///\n    /// This function is unsafe as there is no guarantee that the given pointer is\n    /// valid for `offset_length` elements.\n    pub unsafe fn wgpu_render_bundle_set_bind_group(\n        bundle: &mut RenderBundleEncoder,\n        index: u32,\n        bind_group_id: Option<id::BindGroupId>,\n        offsets: *const DynamicOffset,\n        offset_length: usize,\n    ) {\n        let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) };\n\n        let redundant = bundle.current_bind_groups.set_and_check_redundant(\n            bind_group_id,\n            index,\n            &mut bundle.base.dynamic_offsets,\n            offsets,\n        );\n\n        if redundant {\n            return;\n        }\n\n        bundle.base.commands.push(RenderCommand::SetBindGroup {\n            index,\n            num_dynamic_offsets: offset_length,\n            bind_group: bind_group_id,\n        });\n    }\n\n    pub fn wgpu_render_bundle_set_pipeline(\n        bundle: &mut RenderBundleEncoder,\n        pipeline_id: id::RenderPipelineId,\n    ) {\n        if bundle.current_pipeline.set_and_check_redundant(pipeline_id) {\n            return;\n        }\n\n        bundle\n            .base\n            .commands\n            .push(RenderCommand::SetPipeline(pipeline_id));\n    }\n\n    pub fn wgpu_render_bundle_set_vertex_buffer(\n        bundle: &mut RenderBundleEncoder,\n        slot: u32,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    ) {\n        bundle.base.commands.push(RenderCommand::SetVertexBuffer {\n            slot,\n            buffer: buffer_id,\n            offset,\n            size,\n        });\n    }\n\n    pub fn wgpu_render_bundle_set_index_buffer(\n        encoder: &mut RenderBundleEncoder,\n        buffer: id::BufferId,\n        index_format: IndexFormat,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    ) {\n        encoder.set_index_buffer(buffer, index_format, offset, size);\n    }\n\n    /// # Safety\n    ///\n    /// This function is unsafe as there is no guarantee that the given pointer is\n    /// valid for `data` elements.\n    pub unsafe fn wgpu_render_bundle_set_immediates(\n        pass: &mut RenderBundleEncoder,\n        offset: u32,\n        size_bytes: u32,\n        data: *const u8,\n    ) {\n        assert_eq!(\n            offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),\n            0,\n            \"Immediate data offset must be aligned to 4 bytes.\"\n        );\n        assert_eq!(\n            size_bytes & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),\n            0,\n            \"Immediate data size must be aligned to 4 bytes.\"\n        );\n        let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) };\n        let value_offset = pass.base.immediates_data.len().try_into().expect(\n            \"Ran out of immediate data space. Don't set 4gb of immediates per RenderBundle.\",\n        );\n\n        pass.base.immediates_data.extend(\n            data_slice\n                .chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)\n                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),\n        );\n\n        pass.base.commands.push(RenderCommand::SetImmediate {\n            offset,\n            size_bytes,\n            values_offset: Some(value_offset),\n        });\n    }\n\n    pub fn wgpu_render_bundle_draw(\n        bundle: &mut RenderBundleEncoder,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n        first_instance: u32,\n    ) {\n        bundle.base.commands.push(RenderCommand::Draw {\n            vertex_count,\n            instance_count,\n            first_vertex,\n            first_instance,\n        });\n    }\n\n    pub fn wgpu_render_bundle_draw_indexed(\n        bundle: &mut RenderBundleEncoder,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n        first_instance: u32,\n    ) {\n        bundle.base.commands.push(RenderCommand::DrawIndexed {\n            index_count,\n            instance_count,\n            first_index,\n            base_vertex,\n            first_instance,\n        });\n    }\n\n    pub fn wgpu_render_bundle_draw_indirect(\n        bundle: &mut RenderBundleEncoder,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) {\n        bundle.base.commands.push(RenderCommand::DrawIndirect {\n            buffer: buffer_id,\n            offset,\n            count: 1,\n            family: DrawCommandFamily::Draw,\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n    }\n\n    pub fn wgpu_render_bundle_draw_indexed_indirect(\n        bundle: &mut RenderBundleEncoder,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) {\n        bundle.base.commands.push(RenderCommand::DrawIndirect {\n            buffer: buffer_id,\n            offset,\n            count: 1,\n            family: DrawCommandFamily::DrawIndexed,\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n    }\n\n    /// # Safety\n    ///\n    /// This function is unsafe as there is no guarantee that the given `label`\n    /// is a valid null-terminated string.\n    pub unsafe fn wgpu_render_bundle_push_debug_group(\n        _bundle: &mut RenderBundleEncoder,\n        _label: RawString,\n    ) {\n        //TODO\n    }\n\n    pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {\n        //TODO\n    }\n\n    /// # Safety\n    ///\n    /// This function is unsafe as there is no guarantee that the given `label`\n    /// is a valid null-terminated string.\n    pub unsafe fn wgpu_render_bundle_insert_debug_marker(\n        _bundle: &mut RenderBundleEncoder,\n        _label: RawString,\n    ) {\n        //TODO\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/clear.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\nuse core::ops::Range;\n\nuse crate::{\n    api_log,\n    command::{encoder::EncodingState, ArcCommand, EncoderStateError},\n    device::{DeviceError, MissingFeatures},\n    get_lowest_common_denom,\n    global::Global,\n    hal_label,\n    id::{BufferId, CommandEncoderId, TextureId},\n    init_tracker::{MemoryInitKind, TextureInitRange},\n    resource::{\n        Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,\n        ParentDevice, RawResourceAccess, ResourceErrorIdent, Texture, TextureClearMode,\n    },\n    snatch::SnatchGuard,\n    track::TextureTrackerSetSingle,\n};\n\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    math::align_to,\n    BufferAddress, BufferUsages, ImageSubresourceRange, TextureAspect, TextureSelector,\n};\n\n/// Error encountered while attempting a clear.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ClearError {\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\"{0} can not be cleared\")]\n    NoValidTextureClearMode(ResourceErrorIdent),\n    #[error(\"Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`\")]\n    UnalignedFillSize(BufferAddress),\n    #[error(\"Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`\")]\n    UnalignedBufferOffset(BufferAddress),\n    #[error(\"Clear starts at offset {start_offset} with size of {requested_size}, but these added together exceed `u64::MAX`\")]\n    OffsetPlusSizeExceeds64BitBounds {\n        start_offset: BufferAddress,\n        requested_size: BufferAddress,\n    },\n    #[error(\"Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}\")]\n    BufferOverrun {\n        start_offset: BufferAddress,\n        end_offset: BufferAddress,\n        buffer_size: BufferAddress,\n    },\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(\"Texture lacks the aspects that were specified in the image subresource range. Texture with format {texture_format:?}, specified was {subresource_range_aspects:?}\")]\n    MissingTextureAspect {\n        texture_format: wgt::TextureFormat,\n        subresource_range_aspects: TextureAspect,\n    },\n    #[error(\"Image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?},  \\\nwhereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}\")]\n    InvalidTextureLevelRange {\n        texture_level_range: Range<u32>,\n        subresource_base_mip_level: u32,\n        subresource_mip_level_count: Option<u32>,\n    },\n    #[error(\"Image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?},  \\\nwhereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}\")]\n    InvalidTextureLayerRange {\n        texture_layer_range: Range<u32>,\n        subresource_base_array_layer: u32,\n        subresource_array_layer_count: Option<u32>,\n    },\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for ClearError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::EncoderState(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::NoValidTextureClearMode(..)\n            | Self::UnalignedFillSize(..)\n            | Self::UnalignedBufferOffset(..)\n            | Self::OffsetPlusSizeExceeds64BitBounds { .. }\n            | Self::BufferOverrun { .. }\n            | Self::MissingTextureAspect { .. }\n            | Self::InvalidTextureLevelRange { .. }\n            | Self::InvalidTextureLayerRange { .. } => ErrorType::Validation,\n        }\n    }\n}\n\nimpl Global {\n    pub fn command_encoder_clear_buffer(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        dst: BufferId,\n        offset: BufferAddress,\n        size: Option<BufferAddress>,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::clear_buffer\");\n        api_log!(\"CommandEncoder::clear_buffer {dst:?}\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, ClearError> {\n            Ok(ArcCommand::ClearBuffer {\n                dst: self.resolve_buffer_id(dst)?,\n                offset,\n                size,\n            })\n        })\n    }\n\n    pub fn command_encoder_clear_texture(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        dst: TextureId,\n        subresource_range: &ImageSubresourceRange,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::clear_texture\");\n        api_log!(\"CommandEncoder::clear_texture {dst:?}\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, ClearError> {\n            Ok(ArcCommand::ClearTexture {\n                dst: self.resolve_texture_id(dst)?,\n                subresource_range: *subresource_range,\n            })\n        })\n    }\n}\n\npub(super) fn clear_buffer(\n    state: &mut EncodingState,\n    dst_buffer: Arc<Buffer>,\n    offset: BufferAddress,\n    size: Option<BufferAddress>,\n) -> Result<(), ClearError> {\n    dst_buffer.same_device(state.device)?;\n\n    let dst_pending = state\n        .tracker\n        .buffers\n        .set_single(&dst_buffer, wgt::BufferUses::COPY_DST);\n\n    let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;\n    dst_buffer.check_usage(BufferUsages::COPY_DST)?;\n\n    // Check if offset & size are valid.\n    if !offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n        return Err(ClearError::UnalignedBufferOffset(offset));\n    }\n\n    let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));\n    if !size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n        return Err(ClearError::UnalignedFillSize(size));\n    }\n    let end_offset =\n        offset\n            .checked_add(size)\n            .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {\n                start_offset: offset,\n                requested_size: size,\n            })?;\n    if end_offset > dst_buffer.size {\n        return Err(ClearError::BufferOverrun {\n            start_offset: offset,\n            end_offset,\n            buffer_size: dst_buffer.size,\n        });\n    }\n\n    // This must happen after parameter validation (so that errors are reported\n    // as required by the spec), but before any side effects.\n    if offset == end_offset {\n        log::trace!(\"Ignoring fill_buffer of size 0\");\n        return Ok(());\n    }\n\n    // Mark dest as initialized.\n    state\n        .buffer_memory_init_actions\n        .extend(dst_buffer.initialization_status.read().create_action(\n            &dst_buffer,\n            offset..end_offset,\n            MemoryInitKind::ImplicitlyInitialized,\n        ));\n\n    // actual hal barrier & operation\n    let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard));\n    unsafe {\n        state.raw_encoder.transition_buffers(dst_barrier.as_slice());\n        state.raw_encoder.clear_buffer(dst_raw, offset..end_offset);\n    }\n\n    Ok(())\n}\n\n/// Validate and encode a \"Clear Texture\" command.\n///\n/// This function implements `CommandEncoder::clear_texture` when invoked via\n/// the command encoder APIs or trace playback. It has the suffix `_cmd` to\n/// distinguish it from [`clear_texture`]. [`clear_texture`], used internally by\n/// this function, is a lower-level function that encodes a texture clear\n/// operation without validating it.\npub(super) fn clear_texture_cmd(\n    state: &mut EncodingState,\n    dst_texture: Arc<Texture>,\n    subresource_range: &ImageSubresourceRange,\n) -> Result<(), ClearError> {\n    dst_texture.same_device(state.device)?;\n    state\n        .device\n        .require_features(wgt::Features::CLEAR_TEXTURE)?;\n\n    // Check if subresource aspects are valid.\n    let clear_aspects = hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);\n    if clear_aspects.is_empty() {\n        return Err(ClearError::MissingTextureAspect {\n            texture_format: dst_texture.desc.format,\n            subresource_range_aspects: subresource_range.aspect,\n        });\n    };\n\n    // Check if subresource level range is valid\n    let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);\n    if dst_texture.full_range.mips.start > subresource_mip_range.start\n        || dst_texture.full_range.mips.end < subresource_mip_range.end\n    {\n        return Err(ClearError::InvalidTextureLevelRange {\n            texture_level_range: dst_texture.full_range.mips.clone(),\n            subresource_base_mip_level: subresource_range.base_mip_level,\n            subresource_mip_level_count: subresource_range.mip_level_count,\n        });\n    }\n    // Check if subresource layer range is valid\n    let subresource_layer_range = subresource_range.layer_range(dst_texture.full_range.layers.end);\n    if dst_texture.full_range.layers.start > subresource_layer_range.start\n        || dst_texture.full_range.layers.end < subresource_layer_range.end\n    {\n        return Err(ClearError::InvalidTextureLayerRange {\n            texture_layer_range: dst_texture.full_range.layers.clone(),\n            subresource_base_array_layer: subresource_range.base_array_layer,\n            subresource_array_layer_count: subresource_range.array_layer_count,\n        });\n    }\n\n    clear_texture(\n        &dst_texture,\n        TextureInitRange {\n            mip_range: subresource_mip_range,\n            layer_range: subresource_layer_range,\n        },\n        state.raw_encoder,\n        &mut state.tracker.textures,\n        &state.device.alignments,\n        state.device.zero_buffer.as_ref(),\n        state.snatch_guard,\n        state.device.instance_flags,\n    )?;\n\n    Ok(())\n}\n\n/// Encode a texture clear operation.\n///\n/// This function encodes a texture clear operation without validating it.\n/// Texture clears requested via the API call this function via\n/// [`clear_texture_cmd`], which does the validation. This function is also\n/// called directly from various places within wgpu that need to clear a\n/// texture.\npub(crate) fn clear_texture<T: TextureTrackerSetSingle>(\n    dst_texture: &Arc<Texture>,\n    range: TextureInitRange,\n    encoder: &mut dyn hal::DynCommandEncoder,\n    texture_tracker: &mut T,\n    alignments: &hal::Alignments,\n    zero_buffer: &dyn hal::DynBuffer,\n    snatch_guard: &SnatchGuard<'_>,\n    instance_flags: wgt::InstanceFlags,\n) -> Result<(), ClearError> {\n    let dst_raw = dst_texture.try_raw(snatch_guard)?;\n\n    // Issue the right barrier.\n    let clear_usage = match *dst_texture.clear_mode.read() {\n        TextureClearMode::BufferCopy => wgt::TextureUses::COPY_DST,\n        TextureClearMode::RenderPass {\n            is_color: false, ..\n        } => wgt::TextureUses::DEPTH_STENCIL_WRITE,\n        TextureClearMode::Surface { .. } | TextureClearMode::RenderPass { is_color: true, .. } => {\n            wgt::TextureUses::COLOR_TARGET\n        }\n        TextureClearMode::None => {\n            return Err(ClearError::NoValidTextureClearMode(\n                dst_texture.error_ident(),\n            ));\n        }\n    };\n\n    let selector = TextureSelector {\n        mips: range.mip_range.clone(),\n        layers: range.layer_range.clone(),\n    };\n\n    // If we're in a texture-init usecase, we know that the texture is already\n    // tracked since whatever caused the init requirement, will have caused the\n    // usage tracker to be aware of the texture. Meaning, that it is safe to\n    // call call change_replace_tracked if the life_guard is already gone (i.e.\n    // the user no longer holds on to this texture).\n    //\n    // On the other hand, when coming via command_encoder_clear_texture, the\n    // life_guard is still there since in order to call it a texture object is\n    // needed.\n    //\n    // We could in theory distinguish these two scenarios in the internal\n    // clear_texture api in order to remove this check and call the cheaper\n    // change_replace_tracked whenever possible.\n    let dst_barrier = texture_tracker\n        .set_single(dst_texture, selector, clear_usage)\n        .map(|pending| pending.into_hal(dst_raw))\n        .collect::<Vec<_>>();\n    unsafe {\n        encoder.transition_textures(&dst_barrier);\n    }\n\n    // Record actual clearing\n    let clear_mode = dst_texture.clear_mode.read();\n    match *clear_mode {\n        TextureClearMode::BufferCopy => clear_texture_via_buffer_copies(\n            &dst_texture.desc,\n            alignments,\n            zero_buffer,\n            range,\n            encoder,\n            dst_raw,\n        ),\n        TextureClearMode::Surface { .. } => {\n            drop(clear_mode);\n            clear_texture_via_render_passes(dst_texture, range, true, encoder, instance_flags)?\n        }\n        TextureClearMode::RenderPass { is_color, .. } => {\n            drop(clear_mode);\n            clear_texture_via_render_passes(dst_texture, range, is_color, encoder, instance_flags)?\n        }\n        TextureClearMode::None => {\n            return Err(ClearError::NoValidTextureClearMode(\n                dst_texture.error_ident(),\n            ));\n        }\n    }\n    Ok(())\n}\n\nfn clear_texture_via_buffer_copies(\n    texture_desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n    alignments: &hal::Alignments,\n    zero_buffer: &dyn hal::DynBuffer, // Buffer of size device::ZERO_BUFFER_SIZE\n    range: TextureInitRange,\n    encoder: &mut dyn hal::DynCommandEncoder,\n    dst_raw: &dyn hal::DynTexture,\n) {\n    assert!(!texture_desc.format.is_depth_stencil_format());\n\n    if texture_desc.format == wgt::TextureFormat::NV12\n        || texture_desc.format == wgt::TextureFormat::P010\n    {\n        // TODO: Currently COPY_DST for NV12 and P010 textures is unsupported.\n        return;\n    }\n\n    // Gather list of zero_buffer copies and issue a single command then to perform them\n    let mut zero_buffer_copy_regions = Vec::new();\n    let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;\n    let (block_width, block_height) = texture_desc.format.block_dimensions();\n    let block_size = texture_desc.format.block_copy_size(None).unwrap();\n\n    let bytes_per_row_alignment = get_lowest_common_denom(buffer_copy_pitch, block_size);\n\n    for mip_level in range.mip_range {\n        let mut mip_size = texture_desc.mip_level_size(mip_level).unwrap();\n        // Round to multiple of block size\n        mip_size.width = align_to(mip_size.width, block_width);\n        mip_size.height = align_to(mip_size.height, block_height);\n\n        let bytes_per_row = align_to(\n            mip_size.width / block_width * block_size,\n            bytes_per_row_alignment,\n        );\n\n        let max_rows_per_copy = crate::device::ZERO_BUFFER_SIZE as u32 / bytes_per_row;\n        // round down to a multiple of rows needed by the texture format\n        let max_rows_per_copy = max_rows_per_copy / block_height * block_height;\n        assert!(\n            max_rows_per_copy > 0,\n            \"Zero buffer size is too small to fill a single row \\\n            of a texture with format {:?} and desc {:?}\",\n            texture_desc.format,\n            texture_desc.size\n        );\n\n        let z_range = 0..(if texture_desc.dimension == wgt::TextureDimension::D3 {\n            mip_size.depth_or_array_layers\n        } else {\n            1\n        });\n\n        for array_layer in range.layer_range.clone() {\n            // TODO: Only doing one layer at a time for volume textures right now.\n            for z in z_range.clone() {\n                // May need multiple copies for each subresource! However, we\n                // assume that we never need to split a row.\n                let mut num_rows_left = mip_size.height;\n                while num_rows_left > 0 {\n                    let num_rows = num_rows_left.min(max_rows_per_copy);\n\n                    zero_buffer_copy_regions.push(hal::BufferTextureCopy {\n                        buffer_layout: wgt::TexelCopyBufferLayout {\n                            offset: 0,\n                            bytes_per_row: Some(bytes_per_row),\n                            rows_per_image: None,\n                        },\n                        texture_base: hal::TextureCopyBase {\n                            mip_level,\n                            array_layer,\n                            origin: wgt::Origin3d {\n                                x: 0, // Always full rows\n                                y: mip_size.height - num_rows_left,\n                                z,\n                            },\n                            aspect: hal::FormatAspects::COLOR,\n                        },\n                        size: hal::CopyExtent {\n                            width: mip_size.width, // full row\n                            height: num_rows,\n                            depth: 1, // Only single slice of volume texture at a time right now\n                        },\n                    });\n\n                    num_rows_left -= num_rows;\n                }\n            }\n        }\n    }\n\n    unsafe {\n        encoder.copy_buffer_to_texture(zero_buffer, dst_raw, &zero_buffer_copy_regions);\n    }\n}\n\nfn clear_texture_via_render_passes(\n    dst_texture: &Texture,\n    range: TextureInitRange,\n    is_color: bool,\n    encoder: &mut dyn hal::DynCommandEncoder,\n    instance_flags: wgt::InstanceFlags,\n) -> Result<(), ClearError> {\n    assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2);\n\n    let extent_base = wgt::Extent3d {\n        width: dst_texture.desc.size.width,\n        height: dst_texture.desc.size.height,\n        depth_or_array_layers: 1, // Only one layer is cleared at a time.\n    };\n\n    let clear_mode = dst_texture.clear_mode.read();\n\n    for mip_level in range.mip_range {\n        let extent = extent_base.mip_level_size(mip_level, dst_texture.desc.dimension);\n        for depth_or_layer in range.layer_range.clone() {\n            let color_attachments_tmp;\n            let (color_attachments, depth_stencil_attachment) = if is_color {\n                color_attachments_tmp = [Some(hal::ColorAttachment {\n                    target: hal::Attachment {\n                        view: Texture::get_clear_view(\n                            &clear_mode,\n                            &dst_texture.desc,\n                            mip_level,\n                            depth_or_layer,\n                        ),\n                        usage: wgt::TextureUses::COLOR_TARGET,\n                    },\n                    depth_slice: None,\n                    resolve_target: None,\n                    ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,\n                    clear_value: wgt::Color::TRANSPARENT,\n                })];\n                (&color_attachments_tmp[..], None)\n            } else {\n                (\n                    &[][..],\n                    Some(hal::DepthStencilAttachment {\n                        target: hal::Attachment {\n                            view: Texture::get_clear_view(\n                                &clear_mode,\n                                &dst_texture.desc,\n                                mip_level,\n                                depth_or_layer,\n                            ),\n                            usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,\n                        },\n                        depth_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,\n                        stencil_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,\n                        clear_value: (0.0, 0),\n                    }),\n                )\n            };\n            unsafe {\n                encoder\n                    .begin_render_pass(&hal::RenderPassDescriptor {\n                        label: hal_label(\n                            Some(\"(wgpu internal) clear_texture clear pass\"),\n                            instance_flags,\n                        ),\n                        extent,\n                        sample_count: dst_texture.desc.sample_count,\n                        color_attachments,\n                        depth_stencil_attachment,\n                        multiview_mask: None,\n                        timestamp_writes: None,\n                        occlusion_query_set: None,\n                    })\n                    .map_err(|e| dst_texture.device.handle_hal_error(e))?;\n                encoder.end_render_pass();\n            }\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "wgpu-core/src/command/compute.rs",
    "content": "use thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    BufferAddress, DynamicOffset,\n};\n\nuse alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};\nuse core::{convert::Infallible, fmt, str};\n\nuse crate::{\n    api_log,\n    binding_model::{BindError, ImmediateUploadError, LateMinBufferBindingSizeMismatch},\n    command::{\n        bind::{Binder, BinderError},\n        compute_command::ArcComputeCommand,\n        encoder::EncodingState,\n        memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},\n        pass::{self, flush_bindings_helper},\n        pass_base, pass_try,\n        query::{end_pipeline_statistics_query, validate_and_begin_pipeline_statistics_query},\n        ArcCommand, ArcPassTimestampWrites, BasePass, BindGroupStateChange, CommandEncoder,\n        CommandEncoderError, DebugGroupError, EncoderStateError, InnerCommandEncoder, MapPassErr,\n        PassErrorScope, PassStateError, PassTimestampWrites, QueryUseError, StateChange,\n        TimestampWritesError,\n    },\n    device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures},\n    global::Global,\n    hal_label, id,\n    init_tracker::MemoryInitKind,\n    pipeline::ComputePipeline,\n    resource::{\n        self, Buffer, DestroyedResourceError, InvalidResourceError, Labeled,\n        MissingBufferUsageError, ParentDevice, RawResourceAccess, Trackable,\n    },\n    track::{ResourceUsageCompatibilityError, Tracker},\n    Label,\n};\n\npub type ComputeBasePass = BasePass<ArcComputeCommand, ComputePassError>;\n\n/// A pass's [encoder state](https://www.w3.org/TR/webgpu/#encoder-state) and\n/// its validity are two distinct conditions, i.e., the full matrix of\n/// (open, ended) x (valid, invalid) is possible.\n///\n/// The presence or absence of the `parent` `Option` indicates the pass's state.\n/// The presence or absence of an error in `base.error` indicates the pass's\n/// validity.\npub struct ComputePass {\n    /// All pass data & records is stored here.\n    base: ComputeBasePass,\n\n    /// Parent command encoder that this pass records commands into.\n    ///\n    /// If this is `Some`, then the pass is in WebGPU's \"open\" state. If it is\n    /// `None`, then the pass is in the \"ended\" state.\n    /// See <https://www.w3.org/TR/webgpu/#encoder-state>\n    parent: Option<Arc<CommandEncoder>>,\n\n    timestamp_writes: Option<ArcPassTimestampWrites>,\n\n    // Resource binding dedupe state.\n    current_bind_groups: BindGroupStateChange,\n    current_pipeline: StateChange<id::ComputePipelineId>,\n}\n\nimpl ComputePass {\n    /// If the parent command encoder is invalid, the returned pass will be invalid.\n    fn new(parent: Arc<CommandEncoder>, desc: ArcComputePassDescriptor) -> Self {\n        let ArcComputePassDescriptor {\n            label,\n            timestamp_writes,\n        } = desc;\n\n        Self {\n            base: BasePass::new(&label),\n            parent: Some(parent),\n            timestamp_writes,\n\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        }\n    }\n\n    fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: ComputePassError) -> Self {\n        Self {\n            base: BasePass::new_invalid(label, err),\n            parent: Some(parent),\n            timestamp_writes: None,\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        }\n    }\n\n    #[inline]\n    pub fn label(&self) -> Option<&str> {\n        self.base.label.as_deref()\n    }\n}\n\nimpl fmt::Debug for ComputePass {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.parent {\n            Some(ref cmd_enc) => write!(f, \"ComputePass {{ parent: {} }}\", cmd_enc.error_ident()),\n            None => write!(f, \"ComputePass {{ parent: None }}\"),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Default)]\npub struct ComputePassDescriptor<'a, PTW = PassTimestampWrites> {\n    pub label: Label<'a>,\n    /// Defines where and when timestamp values will be written for this pass.\n    pub timestamp_writes: Option<PTW>,\n}\n\n/// cbindgen:ignore\ntype ArcComputePassDescriptor<'a> = ComputePassDescriptor<'a, ArcPassTimestampWrites>;\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum DispatchError {\n    #[error(\"Compute pipeline must be set\")]\n    MissingPipeline(pass::MissingPipeline),\n    #[error(transparent)]\n    IncompatibleBindGroup(#[from] Box<BinderError>),\n    #[error(\n        \"Each current dispatch group size dimension ({current:?}) must be less or equal to {limit}\"\n    )]\n    InvalidGroupSize { current: [u32; 3], limit: u32 },\n    #[error(transparent)]\n    BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),\n}\n\nimpl WebGpuError for DispatchError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n/// Error encountered when performing a compute pass.\n#[derive(Clone, Debug, Error)]\npub enum ComputePassErrorInner {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n    #[error(\"Parent encoder is invalid\")]\n    InvalidParentEncoder,\n    #[error(transparent)]\n    DebugGroupError(#[from] DebugGroupError),\n    #[error(transparent)]\n    BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"Indirect buffer offset {0:?} is not a multiple of 4\")]\n    UnalignedIndirectBufferOffset(BufferAddress),\n    #[error(\"Indirect buffer uses bytes {offset}..{end_offset} which overruns indirect buffer of size {buffer_size}\")]\n    IndirectBufferOverrun {\n        offset: u64,\n        end_offset: u64,\n        buffer_size: u64,\n    },\n    #[error(transparent)]\n    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(transparent)]\n    Dispatch(#[from] DispatchError),\n    #[error(transparent)]\n    Bind(#[from] BindError),\n    #[error(transparent)]\n    ImmediateData(#[from] ImmediateUploadError),\n    #[error(\"Immediate data offset must be aligned to 4 bytes\")]\n    ImmediateOffsetAlignment,\n    #[error(\"Immediate data size must be aligned to 4 bytes\")]\n    ImmediateDataizeAlignment,\n    #[error(\"Ran out of immediate data space. Don't set 4gb of immediates per ComputePass.\")]\n    ImmediateOutOfMemory,\n    #[error(transparent)]\n    QueryUse(#[from] QueryUseError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(\"The compute pass has already been ended and no further commands can be recorded\")]\n    PassEnded,\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    TimestampWrites(#[from] TimestampWritesError),\n    // This one is unreachable, but required for generic pass support\n    #[error(transparent)]\n    InvalidValuesOffset(#[from] pass::InvalidValuesOffset),\n}\n\n/// Error encountered when performing a compute pass, stored for later reporting\n/// when encoding ends.\n#[derive(Clone, Debug, Error)]\n#[error(\"{scope}\")]\npub struct ComputePassError {\n    pub scope: PassErrorScope,\n    #[source]\n    pub(super) inner: ComputePassErrorInner,\n}\n\nimpl From<pass::MissingPipeline> for ComputePassErrorInner {\n    fn from(value: pass::MissingPipeline) -> Self {\n        Self::Dispatch(DispatchError::MissingPipeline(value))\n    }\n}\n\nimpl<E> MapPassErr<ComputePassError> for E\nwhere\n    E: Into<ComputePassErrorInner>,\n{\n    fn map_pass_err(self, scope: PassErrorScope) -> ComputePassError {\n        ComputePassError {\n            scope,\n            inner: self.into(),\n        }\n    }\n}\n\nimpl WebGpuError for ComputePassError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        let Self { scope: _, inner } = self;\n        match inner {\n            ComputePassErrorInner::Device(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::EncoderState(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::ResourceUsageCompatibility(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::MissingBufferUsage(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::Dispatch(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::Bind(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::ImmediateData(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::QueryUse(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::InvalidResource(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),\n            ComputePassErrorInner::InvalidValuesOffset(e) => e.webgpu_error_type(),\n\n            ComputePassErrorInner::InvalidParentEncoder\n            | ComputePassErrorInner::BindGroupIndexOutOfRange { .. }\n            | ComputePassErrorInner::UnalignedIndirectBufferOffset(_)\n            | ComputePassErrorInner::IndirectBufferOverrun { .. }\n            | ComputePassErrorInner::ImmediateOffsetAlignment\n            | ComputePassErrorInner::ImmediateDataizeAlignment\n            | ComputePassErrorInner::ImmediateOutOfMemory\n            | ComputePassErrorInner::PassEnded => ErrorType::Validation,\n        }\n    }\n}\n\nstruct State<'scope, 'snatch_guard, 'cmd_enc> {\n    pipeline: Option<Arc<ComputePipeline>>,\n\n    pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,\n\n    active_query: Option<(Arc<resource::QuerySet>, u32)>,\n\n    immediates: Vec<u32>,\n\n    intermediate_trackers: Tracker,\n}\n\nimpl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {\n    fn is_ready(&self) -> Result<(), DispatchError> {\n        if let Some(pipeline) = self.pipeline.as_ref() {\n            self.pass.binder.check_compatibility(pipeline.as_ref())?;\n            self.pass.binder.check_late_buffer_bindings()?;\n            Ok(())\n        } else {\n            Err(DispatchError::MissingPipeline(pass::MissingPipeline))\n        }\n    }\n\n    /// Flush binding state in preparation for a dispatch.\n    ///\n    /// # Differences between render and compute passes\n    ///\n    /// There are differences between the `flush_bindings` implementations for\n    /// render and compute passes, because render passes have a single usage\n    /// scope for the entire pass, and compute passes have a separate usage\n    /// scope for each dispatch.\n    ///\n    /// For compute passes, bind groups are merged into a fresh usage scope\n    /// here, not into the pass usage scope within calls to `set_bind_group`. As\n    /// specified by WebGPU, for compute passes, we merge only the bind groups\n    /// that are actually used by the pipeline, unlike render passes, which\n    /// merge every bind group that is ever set, even if it is not ultimately\n    /// used by the pipeline.\n    ///\n    /// For compute passes, we call `drain_barriers` here, because barriers may\n    /// be needed before each dispatch if a previous dispatch had a conflicting\n    /// usage. For render passes, barriers are emitted once at the start of the\n    /// render pass.\n    ///\n    /// # Indirect buffer handling\n    ///\n    /// The `indirect_buffer` argument should be passed for any indirect\n    /// dispatch (with or without validation). It will be checked for\n    /// conflicting usages according to WebGPU rules. For the purpose of\n    /// these rules, the fact that we have actually processed the buffer in\n    /// the validation pass is an implementation detail.\n    ///\n    /// The `track_indirect_buffer` argument should be set when doing indirect\n    /// dispatch *without* validation. In this case, the indirect buffer will\n    /// be added to the tracker in order to generate any necessary transitions\n    /// for that usage.\n    ///\n    /// When doing indirect dispatch *with* validation, the indirect buffer is\n    /// processed by the validation pass and is not used by the actual dispatch.\n    /// The indirect validation code handles transitions for the validation\n    /// pass.\n    fn flush_bindings(\n        &mut self,\n        indirect_buffer: Option<&Arc<Buffer>>,\n        track_indirect_buffer: bool,\n    ) -> Result<(), ComputePassErrorInner> {\n        for bind_group in self.pass.binder.list_active() {\n            unsafe { self.pass.scope.merge_bind_group(&bind_group.used)? };\n        }\n\n        // Add the indirect buffer. Because usage scopes are per-dispatch, this\n        // is the only place where INDIRECT usage could be added, and it is safe\n        // for us to remove it below.\n        if let Some(buffer) = indirect_buffer {\n            self.pass\n                .scope\n                .buffers\n                .merge_single(buffer, wgt::BufferUses::INDIRECT)?;\n        }\n\n        // For compute, usage scopes are associated with each dispatch and not\n        // with the pass as a whole. However, because the cost of creating and\n        // dropping `UsageScope`s is significant (even with the pool), we\n        // add and then remove usage from a single usage scope.\n\n        for bind_group in self.pass.binder.list_active() {\n            self.intermediate_trackers\n                .set_and_remove_from_usage_scope_sparse(&mut self.pass.scope, &bind_group.used);\n        }\n\n        if track_indirect_buffer {\n            self.intermediate_trackers\n                .buffers\n                .set_and_remove_from_usage_scope_sparse(\n                    &mut self.pass.scope.buffers,\n                    indirect_buffer.map(|buf| buf.tracker_index()),\n                );\n        } else if let Some(buffer) = indirect_buffer {\n            self.pass\n                .scope\n                .buffers\n                .remove_usage(buffer, wgt::BufferUses::INDIRECT);\n        }\n\n        flush_bindings_helper(&mut self.pass)?;\n\n        CommandEncoder::drain_barriers(\n            self.pass.base.raw_encoder,\n            &mut self.intermediate_trackers,\n            self.pass.base.snatch_guard,\n        );\n        Ok(())\n    }\n}\n\n// Running the compute pass.\n\nimpl Global {\n    /// Creates a compute pass.\n    ///\n    /// If creation fails, an invalid pass is returned. Attempting to record\n    /// commands into an invalid pass is permitted, but a validation error will\n    /// ultimately be generated when the parent encoder is finished, and it is\n    /// not possible to run any commands from the invalid pass.\n    ///\n    /// If successful, puts the encoder into the [`Locked`] state.\n    ///\n    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked\n    pub fn command_encoder_begin_compute_pass(\n        &self,\n        encoder_id: id::CommandEncoderId,\n        desc: &ComputePassDescriptor<'_>,\n    ) -> (ComputePass, Option<CommandEncoderError>) {\n        use EncoderStateError as SErr;\n\n        let scope = PassErrorScope::Pass;\n        let hub = &self.hub;\n\n        let label = desc.label.as_deref().map(Cow::Borrowed);\n\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        match cmd_buf_data.lock_encoder() {\n            Ok(()) => {\n                drop(cmd_buf_data);\n                if let Err(err) = cmd_enc.device.check_is_valid() {\n                    return (\n                        ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)),\n                        None,\n                    );\n                }\n\n                match desc\n                    .timestamp_writes\n                    .as_ref()\n                    .map(|tw| {\n                        Self::validate_pass_timestamp_writes::<ComputePassErrorInner>(\n                            &cmd_enc.device,\n                            &hub.query_sets.read(),\n                            tw,\n                        )\n                    })\n                    .transpose()\n                {\n                    Ok(timestamp_writes) => {\n                        let arc_desc = ArcComputePassDescriptor {\n                            label,\n                            timestamp_writes,\n                        };\n                        (ComputePass::new(cmd_enc, arc_desc), None)\n                    }\n                    Err(err) => (\n                        ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)),\n                        None,\n                    ),\n                }\n            }\n            Err(err @ SErr::Locked) => {\n                // Attempting to open a new pass while the encoder is locked\n                // invalidates the encoder, but does not generate a validation\n                // error.\n                cmd_buf_data.invalidate(err.clone());\n                drop(cmd_buf_data);\n                (\n                    ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)),\n                    None,\n                )\n            }\n            Err(err @ (SErr::Ended | SErr::Submitted)) => {\n                // Attempting to open a new pass after the encode has ended\n                // generates an immediate validation error.\n                drop(cmd_buf_data);\n                (\n                    ComputePass::new_invalid(cmd_enc, &label, err.clone().map_pass_err(scope)),\n                    Some(err.into()),\n                )\n            }\n            Err(err @ SErr::Invalid) => {\n                // Passes can be opened even on an invalid encoder. Such passes\n                // are even valid, but since there's no visible side-effect of\n                // the pass being valid and there's no point in storing recorded\n                // commands that will ultimately be discarded, we open an\n                // invalid pass to save that work.\n                drop(cmd_buf_data);\n                (\n                    ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)),\n                    None,\n                )\n            }\n            Err(SErr::Unlocked) => {\n                unreachable!(\"lock_encoder cannot fail due to the encoder being unlocked\")\n            }\n        }\n    }\n\n    pub fn compute_pass_end(&self, pass: &mut ComputePass) -> Result<(), EncoderStateError> {\n        profiling::scope!(\n            \"CommandEncoder::run_compute_pass {}\",\n            pass.base.label.as_deref().unwrap_or(\"\")\n        );\n\n        let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.unlock_encoder()?;\n\n        let base = pass.base.take();\n\n        if let Err(ComputePassError {\n            inner:\n                ComputePassErrorInner::EncoderState(\n                    err @ (EncoderStateError::Locked | EncoderStateError::Ended),\n                ),\n            scope: _,\n        }) = base\n        {\n            // Most encoding errors are detected and raised within `finish()`.\n            //\n            // However, we raise a validation error here if the pass was opened\n            // within another pass, or on a finished encoder. The latter is\n            // particularly important, because in that case reporting errors via\n            // `CommandEncoder::finish` is not possible.\n            return Err(err.clone());\n        }\n\n        cmd_buf_data.push_with(|| -> Result<_, ComputePassError> {\n            Ok(ArcCommand::RunComputePass {\n                pass: base?,\n                timestamp_writes: pass.timestamp_writes.take(),\n            })\n        })\n    }\n}\n\npub(super) fn encode_compute_pass(\n    parent_state: &mut EncodingState<InnerCommandEncoder>,\n    mut base: BasePass<ArcComputeCommand, Infallible>,\n    mut timestamp_writes: Option<ArcPassTimestampWrites>,\n) -> Result<(), ComputePassError> {\n    let pass_scope = PassErrorScope::Pass;\n\n    let device = parent_state.device;\n\n    // We automatically keep extending command buffers over time, and because\n    // we want to insert a command buffer _before_ what we're about to record,\n    // we need to make sure to close the previous one.\n    parent_state\n        .raw_encoder\n        .close_if_open()\n        .map_pass_err(pass_scope)?;\n    let raw_encoder = parent_state\n        .raw_encoder\n        .open_pass(base.label.as_deref())\n        .map_pass_err(pass_scope)?;\n\n    let mut debug_scope_depth = 0;\n\n    let mut state = State {\n        pipeline: None,\n\n        pass: pass::PassState {\n            base: EncodingState {\n                device,\n                raw_encoder,\n                tracker: parent_state.tracker,\n                buffer_memory_init_actions: parent_state.buffer_memory_init_actions,\n                texture_memory_actions: parent_state.texture_memory_actions,\n                as_actions: parent_state.as_actions,\n                temp_resources: parent_state.temp_resources,\n                indirect_draw_validation_resources: parent_state.indirect_draw_validation_resources,\n                snatch_guard: parent_state.snatch_guard,\n                debug_scope_depth: &mut debug_scope_depth,\n            },\n            binder: Binder::new(),\n            temp_offsets: Vec::new(),\n            dynamic_offset_count: 0,\n            pending_discard_init_fixups: SurfacesInDiscardState::new(),\n            scope: device.new_usage_scope(),\n            string_offset: 0,\n        },\n        active_query: None,\n\n        immediates: Vec::new(),\n\n        intermediate_trackers: Tracker::new(\n            device.ordered_buffer_usages,\n            device.ordered_texture_usages,\n        ),\n    };\n\n    let indices = &device.tracker_indices;\n    state\n        .pass\n        .base\n        .tracker\n        .buffers\n        .set_size(indices.buffers.size());\n    state\n        .pass\n        .base\n        .tracker\n        .textures\n        .set_size(indices.textures.size());\n\n    let timestamp_writes: Option<hal::PassTimestampWrites<'_, dyn hal::DynQuerySet>> =\n        if let Some(tw) = timestamp_writes.take() {\n            tw.query_set.same_device(device).map_pass_err(pass_scope)?;\n\n            let query_set = state\n                .pass\n                .base\n                .tracker\n                .query_sets\n                .insert_single(tw.query_set);\n\n            // Unlike in render passes we can't delay resetting the query sets since\n            // there is no auxiliary pass.\n            let range = if let (Some(index_a), Some(index_b)) =\n                (tw.beginning_of_pass_write_index, tw.end_of_pass_write_index)\n            {\n                Some(index_a.min(index_b)..index_a.max(index_b) + 1)\n            } else {\n                tw.beginning_of_pass_write_index\n                    .or(tw.end_of_pass_write_index)\n                    .map(|i| i..i + 1)\n            };\n            // Range should always be Some, both values being None should lead to a validation error.\n            // But no point in erroring over that nuance here!\n            if let Some(range) = range {\n                unsafe {\n                    state\n                        .pass\n                        .base\n                        .raw_encoder\n                        .reset_queries(query_set.raw(), range);\n                }\n            }\n\n            Some(hal::PassTimestampWrites {\n                query_set: query_set.raw(),\n                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,\n                end_of_pass_write_index: tw.end_of_pass_write_index,\n            })\n        } else {\n            None\n        };\n\n    let hal_desc = hal::ComputePassDescriptor {\n        label: hal_label(base.label.as_deref(), device.instance_flags),\n        timestamp_writes,\n    };\n\n    unsafe {\n        state.pass.base.raw_encoder.begin_compute_pass(&hal_desc);\n    }\n\n    for command in base.commands.drain(..) {\n        match command {\n            ArcComputeCommand::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group,\n            } => {\n                let scope = PassErrorScope::SetBindGroup;\n                pass::set_bind_group::<ComputePassErrorInner>(\n                    &mut state.pass,\n                    device,\n                    &base.dynamic_offsets,\n                    index,\n                    num_dynamic_offsets,\n                    bind_group,\n                    false,\n                )\n                .map_pass_err(scope)?;\n            }\n            ArcComputeCommand::SetPipeline(pipeline) => {\n                let scope = PassErrorScope::SetPipelineCompute;\n                set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;\n            }\n            ArcComputeCommand::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            } => {\n                let scope = PassErrorScope::SetImmediate;\n                pass::set_immediates::<ComputePassErrorInner, _>(\n                    &mut state.pass,\n                    &base.immediates_data,\n                    offset,\n                    size_bytes,\n                    Some(values_offset),\n                    |data_slice| {\n                        let offset_in_elements = (offset / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;\n                        let size_in_elements =\n                            (size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;\n                        state.immediates[offset_in_elements..][..size_in_elements]\n                            .copy_from_slice(data_slice);\n                    },\n                )\n                .map_pass_err(scope)?;\n            }\n            ArcComputeCommand::Dispatch(groups) => {\n                let scope = PassErrorScope::Dispatch { indirect: false };\n                dispatch(&mut state, groups).map_pass_err(scope)?;\n            }\n            ArcComputeCommand::DispatchIndirect { buffer, offset } => {\n                let scope = PassErrorScope::Dispatch { indirect: true };\n                dispatch_indirect(&mut state, device, buffer, offset).map_pass_err(scope)?;\n            }\n            ArcComputeCommand::PushDebugGroup { color: _, len } => {\n                pass::push_debug_group(&mut state.pass, &base.string_data, len);\n            }\n            ArcComputeCommand::PopDebugGroup => {\n                let scope = PassErrorScope::PopDebugGroup;\n                pass::pop_debug_group::<ComputePassErrorInner>(&mut state.pass)\n                    .map_pass_err(scope)?;\n            }\n            ArcComputeCommand::InsertDebugMarker { color: _, len } => {\n                pass::insert_debug_marker(&mut state.pass, &base.string_data, len);\n            }\n            ArcComputeCommand::WriteTimestamp {\n                query_set,\n                query_index,\n            } => {\n                let scope = PassErrorScope::WriteTimestamp;\n                pass::write_timestamp::<ComputePassErrorInner>(\n                    &mut state.pass,\n                    device,\n                    None, // compute passes do not attempt to coalesce query resets\n                    query_set,\n                    query_index,\n                )\n                .map_pass_err(scope)?;\n            }\n            ArcComputeCommand::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            } => {\n                let scope = PassErrorScope::BeginPipelineStatisticsQuery;\n                validate_and_begin_pipeline_statistics_query(\n                    query_set,\n                    state.pass.base.raw_encoder,\n                    &mut state.pass.base.tracker.query_sets,\n                    device,\n                    query_index,\n                    None,\n                    &mut state.active_query,\n                )\n                .map_pass_err(scope)?;\n            }\n            ArcComputeCommand::EndPipelineStatisticsQuery => {\n                let scope = PassErrorScope::EndPipelineStatisticsQuery;\n                end_pipeline_statistics_query(state.pass.base.raw_encoder, &mut state.active_query)\n                    .map_pass_err(scope)?;\n            }\n        }\n    }\n\n    if *state.pass.base.debug_scope_depth > 0 {\n        Err(\n            ComputePassErrorInner::DebugGroupError(DebugGroupError::MissingPop)\n                .map_pass_err(pass_scope),\n        )?;\n    }\n\n    unsafe {\n        state.pass.base.raw_encoder.end_compute_pass();\n    }\n\n    let State {\n        pass: pass::PassState {\n            pending_discard_init_fixups,\n            ..\n        },\n        intermediate_trackers,\n        ..\n    } = state;\n\n    // Stop the current command encoder.\n    parent_state.raw_encoder.close().map_pass_err(pass_scope)?;\n\n    // Create a new command encoder, which we will insert _before_ the body of the compute pass.\n    //\n    // Use that buffer to insert barriers and clear discarded images.\n    let transit = parent_state\n        .raw_encoder\n        .open_pass(hal_label(\n            Some(\"(wgpu internal) Pre Pass\"),\n            device.instance_flags,\n        ))\n        .map_pass_err(pass_scope)?;\n    fixup_discarded_surfaces(\n        pending_discard_init_fixups.into_iter(),\n        transit,\n        &mut parent_state.tracker.textures,\n        device,\n        parent_state.snatch_guard,\n    );\n    CommandEncoder::insert_barriers_from_tracker(\n        transit,\n        parent_state.tracker,\n        &intermediate_trackers,\n        parent_state.snatch_guard,\n    );\n    // Close the command encoder, and swap it with the previous.\n    parent_state\n        .raw_encoder\n        .close_and_swap()\n        .map_pass_err(pass_scope)?;\n\n    Ok(())\n}\n\nfn set_pipeline(\n    state: &mut State,\n    device: &Arc<Device>,\n    pipeline: Arc<ComputePipeline>,\n) -> Result<(), ComputePassErrorInner> {\n    pipeline.same_device(device)?;\n\n    state.pipeline = Some(pipeline.clone());\n\n    let pipeline = state\n        .pass\n        .base\n        .tracker\n        .compute_pipelines\n        .insert_single(pipeline)\n        .clone();\n\n    unsafe {\n        state\n            .pass\n            .base\n            .raw_encoder\n            .set_compute_pipeline(pipeline.raw());\n    }\n\n    // Rebind resources\n    pass::change_pipeline_layout::<ComputePassErrorInner, _>(\n        &mut state.pass,\n        &pipeline.layout,\n        &pipeline.late_sized_buffer_groups,\n        || {\n            // This only needs to be here for compute pipelines because they use immediates for\n            // validating indirect draws.\n            state.immediates.clear();\n            // Note that can only be one range for each stage. See the `MoreThanOneImmediateRangePerStage` error.\n            if pipeline.layout.immediate_size != 0 {\n                // Note that non-0 range start doesn't work anyway https://github.com/gfx-rs/wgpu/issues/4502\n                let len = pipeline.layout.immediate_size as usize\n                    / wgt::IMMEDIATE_DATA_ALIGNMENT as usize;\n                state.immediates.extend(core::iter::repeat_n(0, len));\n            }\n        },\n    )\n}\n\nfn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorInner> {\n    api_log!(\"ComputePass::dispatch {groups:?}\");\n\n    state.is_ready()?;\n\n    state.flush_bindings(None, false)?;\n\n    let groups_size_limit = state\n        .pass\n        .base\n        .device\n        .limits\n        .max_compute_workgroups_per_dimension;\n\n    if groups.iter().copied().any(|g| g > groups_size_limit) {\n        return Err(ComputePassErrorInner::Dispatch(\n            DispatchError::InvalidGroupSize {\n                current: groups,\n                limit: groups_size_limit,\n            },\n        ));\n    }\n\n    unsafe {\n        state.pass.base.raw_encoder.dispatch(groups);\n    }\n    Ok(())\n}\n\nfn dispatch_indirect(\n    state: &mut State,\n    device: &Arc<Device>,\n    buffer: Arc<Buffer>,\n    offset: u64,\n) -> Result<(), ComputePassErrorInner> {\n    api_log!(\"ComputePass::dispatch_indirect\");\n\n    buffer.same_device(device)?;\n\n    state.is_ready()?;\n\n    state\n        .pass\n        .base\n        .device\n        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;\n\n    buffer.check_usage(wgt::BufferUsages::INDIRECT)?;\n\n    if !offset.is_multiple_of(4) {\n        return Err(ComputePassErrorInner::UnalignedIndirectBufferOffset(offset));\n    }\n\n    let end_offset = offset + size_of::<wgt::DispatchIndirectArgs>() as u64;\n    if end_offset > buffer.size {\n        return Err(ComputePassErrorInner::IndirectBufferOverrun {\n            offset,\n            end_offset,\n            buffer_size: buffer.size,\n        });\n    }\n\n    buffer.check_destroyed(state.pass.base.snatch_guard)?;\n\n    let stride = 3 * 4; // 3 integers, x/y/z group size\n    state.pass.base.buffer_memory_init_actions.extend(\n        buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..(offset + stride),\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    if let Some(ref indirect_validation) = state.pass.base.device.indirect_validation {\n        let params = indirect_validation.dispatch.params(\n            &state.pass.base.device.limits,\n            offset,\n            buffer.size,\n        );\n\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .set_compute_pipeline(params.pipeline);\n        }\n\n        unsafe {\n            state.pass.base.raw_encoder.set_immediates(\n                params.pipeline_layout,\n                0,\n                &[params.offset_remainder as u32 / 4],\n            );\n        }\n\n        unsafe {\n            state.pass.base.raw_encoder.set_bind_group(\n                params.pipeline_layout,\n                0,\n                params.dst_bind_group,\n                &[],\n            );\n        }\n        unsafe {\n            state.pass.base.raw_encoder.set_bind_group(\n                params.pipeline_layout,\n                1,\n                buffer\n                    .indirect_validation_bind_groups\n                    .get(state.pass.base.snatch_guard)\n                    .unwrap()\n                    .dispatch\n                    .as_ref(),\n                &[params.aligned_offset as u32],\n            );\n        }\n\n        let src_transition = state\n            .intermediate_trackers\n            .buffers\n            .set_single(&buffer, wgt::BufferUses::STORAGE_READ_ONLY);\n        let src_barrier = src_transition\n            .map(|transition| transition.into_hal(&buffer, state.pass.base.snatch_guard));\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .transition_buffers(src_barrier.as_slice());\n        }\n\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: params.dst_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::INDIRECT,\n                        to: wgt::BufferUses::STORAGE_READ_WRITE,\n                    },\n                }]);\n        }\n\n        unsafe {\n            state.pass.base.raw_encoder.dispatch([1, 1, 1]);\n        }\n\n        // reset state\n        {\n            let pipeline = state.pipeline.as_ref().unwrap();\n\n            unsafe {\n                state\n                    .pass\n                    .base\n                    .raw_encoder\n                    .set_compute_pipeline(pipeline.raw());\n            }\n\n            if !state.immediates.is_empty() {\n                unsafe {\n                    state.pass.base.raw_encoder.set_immediates(\n                        pipeline.layout.raw(),\n                        0,\n                        &state.immediates,\n                    );\n                }\n            }\n\n            for (i, group, dynamic_offsets) in state.pass.binder.list_valid() {\n                let raw_bg = group.try_raw(state.pass.base.snatch_guard)?;\n                unsafe {\n                    state.pass.base.raw_encoder.set_bind_group(\n                        pipeline.layout.raw(),\n                        i as u32,\n                        raw_bg,\n                        dynamic_offsets,\n                    );\n                }\n            }\n        }\n\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: params.dst_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::STORAGE_READ_WRITE,\n                        to: wgt::BufferUses::INDIRECT,\n                    },\n                }]);\n        }\n\n        state.flush_bindings(Some(&buffer), false)?;\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .dispatch_indirect(params.dst_buffer, 0);\n        }\n    } else {\n        state.flush_bindings(Some(&buffer), true)?;\n\n        let buf_raw = buffer.try_raw(state.pass.base.snatch_guard)?;\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .dispatch_indirect(buf_raw, offset);\n        }\n    }\n\n    Ok(())\n}\n\n// Recording a compute pass.\n//\n// The only error that should be returned from these methods is\n// `EncoderStateError::Ended`, when the pass has already ended and an immediate\n// validation error is raised.\n//\n// All other errors should be stored in the pass for later reporting when\n// `CommandEncoder.finish()` is called.\n//\n// The `pass_try!` macro should be used to handle errors appropriately. Note\n// that the `pass_try!` and `pass_base!` macros may return early from the\n// function that invokes them, like the `?` operator.\nimpl Global {\n    pub fn compute_pass_set_bind_group(\n        &self,\n        pass: &mut ComputePass,\n        index: u32,\n        bind_group_id: Option<id::BindGroupId>,\n        offsets: &[DynamicOffset],\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetBindGroup;\n\n        // This statement will return an error if the pass is ended. It's\n        // important the error check comes before the early-out for\n        // `set_and_check_redundant`.\n        let base = pass_base!(pass, scope);\n\n        if pass.current_bind_groups.set_and_check_redundant(\n            bind_group_id,\n            index,\n            &mut base.dynamic_offsets,\n            offsets,\n        ) {\n            return Ok(());\n        }\n\n        let mut bind_group = None;\n        if let Some(bind_group_id) = bind_group_id {\n            let hub = &self.hub;\n            bind_group = Some(pass_try!(\n                base,\n                scope,\n                hub.bind_groups.get(bind_group_id).get(),\n            ));\n        }\n\n        base.commands.push(ArcComputeCommand::SetBindGroup {\n            index,\n            num_dynamic_offsets: offsets.len(),\n            bind_group,\n        });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_set_pipeline(\n        &self,\n        pass: &mut ComputePass,\n        pipeline_id: id::ComputePipelineId,\n    ) -> Result<(), PassStateError> {\n        let redundant = pass.current_pipeline.set_and_check_redundant(pipeline_id);\n\n        let scope = PassErrorScope::SetPipelineCompute;\n\n        // This statement will return an error if the pass is ended.\n        // Its important the error check comes before the early-out for `redundant`.\n        let base = pass_base!(pass, scope);\n\n        if redundant {\n            return Ok(());\n        }\n\n        let hub = &self.hub;\n        let pipeline = pass_try!(base, scope, hub.compute_pipelines.get(pipeline_id).get());\n\n        base.commands.push(ArcComputeCommand::SetPipeline(pipeline));\n\n        Ok(())\n    }\n\n    pub fn compute_pass_set_immediates(\n        &self,\n        pass: &mut ComputePass,\n        offset: u32,\n        data: &[u8],\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetImmediate;\n        let base = pass_base!(pass, scope);\n\n        if offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {\n            pass_try!(\n                base,\n                scope,\n                Err(ComputePassErrorInner::ImmediateOffsetAlignment),\n            );\n        }\n\n        if data.len() as u32 & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {\n            pass_try!(\n                base,\n                scope,\n                Err(ComputePassErrorInner::ImmediateDataizeAlignment),\n            )\n        }\n        let value_offset = pass_try!(\n            base,\n            scope,\n            base.immediates_data\n                .len()\n                .try_into()\n                .map_err(|_| ComputePassErrorInner::ImmediateOutOfMemory)\n        );\n\n        base.immediates_data.extend(\n            data.chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)\n                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),\n        );\n\n        base.commands.push(ArcComputeCommand::SetImmediate {\n            offset,\n            size_bytes: data.len() as u32,\n            values_offset: value_offset,\n        });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_dispatch_workgroups(\n        &self,\n        pass: &mut ComputePass,\n        groups_x: u32,\n        groups_y: u32,\n        groups_z: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Dispatch { indirect: false };\n\n        pass_base!(pass, scope)\n            .commands\n            .push(ArcComputeCommand::Dispatch([groups_x, groups_y, groups_z]));\n\n        Ok(())\n    }\n\n    pub fn compute_pass_dispatch_workgroups_indirect(\n        &self,\n        pass: &mut ComputePass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) -> Result<(), PassStateError> {\n        let hub = &self.hub;\n        let scope = PassErrorScope::Dispatch { indirect: true };\n        let base = pass_base!(pass, scope);\n\n        let buffer = pass_try!(base, scope, hub.buffers.get(buffer_id).get());\n\n        base.commands\n            .push(ArcComputeCommand::DispatchIndirect { buffer, offset });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_push_debug_group(\n        &self,\n        pass: &mut ComputePass,\n        label: &str,\n        color: u32,\n    ) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::PushDebugGroup);\n\n        let bytes = label.as_bytes();\n        base.string_data.extend_from_slice(bytes);\n\n        base.commands.push(ArcComputeCommand::PushDebugGroup {\n            color,\n            len: bytes.len(),\n        });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_pop_debug_group(\n        &self,\n        pass: &mut ComputePass,\n    ) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::PopDebugGroup);\n\n        base.commands.push(ArcComputeCommand::PopDebugGroup);\n\n        Ok(())\n    }\n\n    pub fn compute_pass_insert_debug_marker(\n        &self,\n        pass: &mut ComputePass,\n        label: &str,\n        color: u32,\n    ) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::InsertDebugMarker);\n\n        let bytes = label.as_bytes();\n        base.string_data.extend_from_slice(bytes);\n\n        base.commands.push(ArcComputeCommand::InsertDebugMarker {\n            color,\n            len: bytes.len(),\n        });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_write_timestamp(\n        &self,\n        pass: &mut ComputePass,\n        query_set_id: id::QuerySetId,\n        query_index: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::WriteTimestamp;\n        let base = pass_base!(pass, scope);\n\n        let hub = &self.hub;\n        let query_set = pass_try!(base, scope, hub.query_sets.get(query_set_id).get());\n\n        base.commands.push(ArcComputeCommand::WriteTimestamp {\n            query_set,\n            query_index,\n        });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_begin_pipeline_statistics_query(\n        &self,\n        pass: &mut ComputePass,\n        query_set_id: id::QuerySetId,\n        query_index: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::BeginPipelineStatisticsQuery;\n        let base = pass_base!(pass, scope);\n\n        let hub = &self.hub;\n        let query_set = pass_try!(base, scope, hub.query_sets.get(query_set_id).get());\n\n        base.commands\n            .push(ArcComputeCommand::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            });\n\n        Ok(())\n    }\n\n    pub fn compute_pass_end_pipeline_statistics_query(\n        &self,\n        pass: &mut ComputePass,\n    ) -> Result<(), PassStateError> {\n        pass_base!(pass, PassErrorScope::EndPipelineStatisticsQuery)\n            .commands\n            .push(ArcComputeCommand::EndPipelineStatisticsQuery);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/compute_command.rs",
    "content": "#[cfg(feature = \"serde\")]\nuse crate::command::serde_object_reference_struct;\nuse crate::command::{ArcReferences, ReferenceType};\n\n#[cfg(feature = \"serde\")]\nuse macro_rules_attribute::apply;\n\n/// cbindgen:ignore\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub enum ComputeCommand<R: ReferenceType> {\n    SetBindGroup {\n        index: u32,\n        num_dynamic_offsets: usize,\n        bind_group: Option<R::BindGroup>,\n    },\n\n    SetPipeline(R::ComputePipeline),\n\n    /// Set a range of immediates to values stored in `immediates_data`.\n    SetImmediate {\n        /// The byte offset within the immediate data storage to write to. This\n        /// must be a multiple of four.\n        offset: u32,\n\n        /// The number of bytes to write. This must be a multiple of four.\n        size_bytes: u32,\n\n        /// Index in `immediates_data` of the start of the data\n        /// to be written.\n        ///\n        /// Note: this is not a byte offset like `offset`. Rather, it is the\n        /// index of the first `u32` element in `immediates_data` to read.\n        values_offset: u32,\n    },\n\n    Dispatch([u32; 3]),\n\n    DispatchIndirect {\n        buffer: R::Buffer,\n        offset: wgt::BufferAddress,\n    },\n\n    PushDebugGroup {\n        color: u32,\n        len: usize,\n    },\n\n    PopDebugGroup,\n\n    InsertDebugMarker {\n        color: u32,\n        len: usize,\n    },\n\n    WriteTimestamp {\n        query_set: R::QuerySet,\n        query_index: u32,\n    },\n\n    BeginPipelineStatisticsQuery {\n        query_set: R::QuerySet,\n        query_index: u32,\n    },\n\n    EndPipelineStatisticsQuery,\n}\n\n/// cbindgen:ignore\npub type ArcComputeCommand = ComputeCommand<ArcReferences>;\n"
  },
  {
    "path": "wgpu-core/src/command/draw.rs",
    "content": "use alloc::boxed::Box;\n\nuse thiserror::Error;\n\nuse wgt::error::{ErrorType, WebGpuError};\n\nuse super::bind::BinderError;\nuse crate::command::pass;\nuse crate::{\n    binding_model::{BindingError, ImmediateUploadError, LateMinBufferBindingSizeMismatch},\n    resource::{\n        DestroyedResourceError, MissingBufferUsageError, MissingTextureUsageError,\n        ResourceErrorIdent,\n    },\n    track::ResourceUsageCompatibilityError,\n};\n\n/// Error validating a draw call.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum DrawError {\n    #[error(\"Blend constant needs to be set\")]\n    MissingBlendConstant,\n    #[error(\"Render pipeline must be set\")]\n    MissingPipeline(#[from] pass::MissingPipeline),\n    #[error(\"Currently set {pipeline} requires vertex buffer {index} to be set\")]\n    MissingVertexBuffer {\n        pipeline: ResourceErrorIdent,\n        index: u32,\n    },\n    #[error(\"Index buffer must be set\")]\n    MissingIndexBuffer,\n    #[error(transparent)]\n    IncompatibleBindGroup(#[from] Box<BinderError>),\n    #[error(\"Vertex {last_vertex} extends beyond limit {vertex_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Vertex` step-rate vertex buffer?\")]\n    VertexBeyondLimit {\n        last_vertex: u64,\n        vertex_limit: u64,\n        slot: u32,\n    },\n    #[error(\"Instance {last_instance} extends beyond limit {instance_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Instance` step-rate vertex buffer?\")]\n    InstanceBeyondLimit {\n        last_instance: u64,\n        instance_limit: u64,\n        slot: u32,\n    },\n    #[error(\"Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?\")]\n    IndexBeyondLimit { last_index: u64, index_limit: u64 },\n    #[error(\"For indexed drawing with strip topology, {pipeline}'s strip index format {strip_index_format:?} must match index buffer format {buffer_format:?}\")]\n    UnmatchedStripIndexFormat {\n        pipeline: ResourceErrorIdent,\n        strip_index_format: Option<wgt::IndexFormat>,\n        buffer_format: wgt::IndexFormat,\n    },\n    #[error(transparent)]\n    BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),\n    #[error(\n        \"Wrong pipeline type for this draw command. Attempted to call {} draw command on {} pipeline\",\n        if *wanted_mesh_pipeline {\"mesh shader\"} else {\"standard\"},\n        if *wanted_mesh_pipeline {\"standard\"} else {\"mesh shader\"},\n    )]\n    WrongPipelineType { wanted_mesh_pipeline: bool },\n    #[error(\n        \"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}\"\n    )]\n    InvalidGroupSize {\n        current: [u32; 3],\n        limit: u32,\n        max_total: u32,\n    },\n    #[error(\n        \"Mesh shader calls in multiview render passes require enabling the `EXPERIMENTAL_MESH_SHADER_MULTIVIEW` feature, and the highest bit ({highest_view_index}) in the multiview mask must be <= `Limits::max_multiview_view_count` ({max_multiviews})\"\n    )]\n    MeshPipelineMultiviewLimitsViolated {\n        highest_view_index: u32,\n        max_multiviews: u32,\n    },\n}\n\nimpl WebGpuError for DrawError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n/// Error encountered when encoding a render command.\n/// This is the shared error set between render bundles and passes.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum RenderCommandError {\n    #[error(transparent)]\n    BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange),\n    #[error(\"Vertex buffer index {index} is greater than the device's requested `max_vertex_buffers` limit {max}\")]\n    VertexBufferIndexOutOfRange { index: u32, max: u32 },\n    #[error(\n        \"Offset {offset} for vertex buffer in slot {slot} is not a multiple of `VERTEX_ALIGNMENT`\"\n    )]\n    UnalignedVertexBuffer { slot: u32, offset: u64 },\n    #[error(\"Offset {offset} for index buffer is not a multiple of {alignment}\")]\n    UnalignedIndexBuffer { offset: u64, alignment: usize },\n    #[error(\"Render pipeline targets are incompatible with render pass\")]\n    IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),\n    #[error(\"{0} writes to depth, while the pass has read-only depth access\")]\n    IncompatibleDepthAccess(ResourceErrorIdent),\n    #[error(\"{0} writes to stencil, while the pass has read-only stencil access\")]\n    IncompatibleStencilAccess(ResourceErrorIdent),\n    #[error(transparent)]\n    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(transparent)]\n    MissingTextureUsage(#[from] MissingTextureUsageError),\n    #[error(transparent)]\n    ImmediateData(#[from] ImmediateUploadError),\n    #[error(transparent)]\n    BindingError(#[from] BindingError),\n    #[error(\"Viewport size {{ w: {w}, h: {h} }} greater than device's requested `max_texture_dimension_2d` limit {max}, or less than zero\")]\n    InvalidViewportRectSize { w: f32, h: f32, max: u32 },\n    #[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})\")]\n    InvalidViewportRectPosition { rect: Rect<f32>, min: f32, max: f32 },\n    #[error(\"Viewport minDepth {0} and/or maxDepth {1} are not in [0, 1]\")]\n    InvalidViewportDepth(f32, f32),\n    #[error(\"Scissor {0:?} is not contained in the render target {1:?}\")]\n    InvalidScissorRect(Rect<u32>, wgt::Extent3d),\n    #[error(\"Support for {0} is not implemented yet\")]\n    Unimplemented(&'static str),\n}\n\nimpl WebGpuError for RenderCommandError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::IncompatiblePipelineTargets(e) => e.webgpu_error_type(),\n            Self::ResourceUsageCompatibility(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::MissingTextureUsage(e) => e.webgpu_error_type(),\n            Self::ImmediateData(e) => e.webgpu_error_type(),\n            Self::BindingError(e) => e.webgpu_error_type(),\n\n            Self::BindGroupIndexOutOfRange { .. }\n            | Self::VertexBufferIndexOutOfRange { .. }\n            | Self::UnalignedIndexBuffer { .. }\n            | Self::UnalignedVertexBuffer { .. }\n            | Self::IncompatibleDepthAccess(..)\n            | Self::IncompatibleStencilAccess(..)\n            | Self::InvalidViewportRectSize { .. }\n            | Self::InvalidViewportRectPosition { .. }\n            | Self::InvalidViewportDepth(..)\n            | Self::InvalidScissorRect(..)\n            | Self::Unimplemented(..) => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct Rect<T> {\n    pub x: T,\n    pub y: T,\n    pub w: T,\n    pub h: T,\n}\n"
  },
  {
    "path": "wgpu-core/src/command/encoder.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\n\nuse crate::{\n    command::memory_init::CommandBufferTextureMemoryActions,\n    device::{queue::TempResource, Device},\n    init_tracker::BufferInitTrackerAction,\n    ray_tracing::AsAction,\n    snatch::SnatchGuard,\n    track::Tracker,\n};\n\n/// State applicable when encoding commands onto a compute pass, render pass, or\n/// directly to a command encoder.\n///\n/// Most encoding routines just want to receive an open encoder, write\n/// command(s) to it, and leave it open for whatever is next. In this case the\n/// `E` type parameter has the default value of `dyn hal::DynCommandEncoder`. To\n/// avoid confusion about encoder state, we set the convention that _the encoder\n/// in an `EncodingState` holding a bare HAL reference must always be open_.\n///\n/// Compute and render passes are more complicated. Because they record a\n/// command buffer for a housekeeping pre-pass which is inserted before the pass\n/// itself, the first thing they will do is close and reopen the encoder if it\n/// is already open. Unnecessary empty HAL passes can be avoided by passing them\n/// the encoder in whatever state it happens to be. In this case, `E` is\n/// `InnerCommandEncoder`, which tracks the state of the encoder. The callee\n/// (the render or compute pass) will open and close the encoder as necessary.\n///\n/// This structure is not supported by cbindgen because it contains a trait\n/// object reference.\n///\n/// cbindgen:ignore\npub(crate) struct EncodingState<'snatch_guard, 'cmd_enc, E: ?Sized = dyn hal::DynCommandEncoder> {\n    pub(crate) device: &'cmd_enc Arc<Device>,\n\n    pub(crate) raw_encoder: &'cmd_enc mut E,\n\n    pub(crate) tracker: &'cmd_enc mut Tracker,\n    pub(crate) buffer_memory_init_actions: &'cmd_enc mut Vec<BufferInitTrackerAction>,\n    pub(crate) texture_memory_actions: &'cmd_enc mut CommandBufferTextureMemoryActions,\n    pub(crate) as_actions: &'cmd_enc mut Vec<AsAction>,\n    pub(crate) temp_resources: &'cmd_enc mut Vec<TempResource>,\n    pub(crate) indirect_draw_validation_resources:\n        &'cmd_enc mut crate::indirect_validation::DrawResources,\n\n    pub(crate) snatch_guard: &'snatch_guard SnatchGuard<'snatch_guard>,\n\n    /// Current debug scope nesting depth.\n    ///\n    /// When encoding a compute or render pass, this is the depth of debug\n    /// scopes in the pass, not the depth of debug scopes in the parent encoder.\n    pub(crate) debug_scope_depth: &'cmd_enc mut u32,\n}\n"
  },
  {
    "path": "wgpu-core/src/command/encoder_command.rs",
    "content": "use core::{convert::Infallible, num::NonZero};\n\nuse alloc::{string::String, sync::Arc, vec::Vec};\n#[cfg(feature = \"serde\")]\nuse macro_rules_attribute::{apply, attribute_alias};\n\nuse crate::{\n    command::ColorAttachments,\n    id,\n    instance::Surface,\n    resource::{Buffer, QuerySet, Texture},\n};\n\npub trait ReferenceType {\n    type Buffer: Clone + core::fmt::Debug;\n    type Surface: Clone; // Surface does not implement Debug, although it probably could.\n    type Texture: Clone + core::fmt::Debug;\n    type TextureView: Clone + core::fmt::Debug;\n    type ExternalTexture: Clone + core::fmt::Debug;\n    type QuerySet: Clone + core::fmt::Debug;\n    type BindGroup: Clone + core::fmt::Debug;\n    type RenderPipeline: Clone + core::fmt::Debug;\n    type RenderBundle: Clone + core::fmt::Debug;\n    type ComputePipeline: Clone + core::fmt::Debug;\n    type Blas: Clone + core::fmt::Debug;\n    type Tlas: Clone + core::fmt::Debug;\n}\n\n/// Reference wgpu objects via numeric IDs assigned by [`crate::identity::IdentityManager`].\n#[derive(Clone, Debug)]\npub struct IdReferences;\n\n/// Reference wgpu objects via the integer value of pointers.\n///\n/// This is used for trace recording and playback. Recording stores the pointer\n/// value of `Arc` references in the trace. Playback uses the integer values\n/// as keys to a `HashMap`.\n#[cfg(any(feature = \"trace\", feature = \"replay\"))]\n#[doc(hidden)]\n#[derive(Clone, Debug)]\npub struct PointerReferences;\n\n/// Reference wgpu objects via `Arc`s.\n#[derive(Clone, Debug)]\npub struct ArcReferences;\n\nimpl ReferenceType for IdReferences {\n    type Buffer = id::BufferId;\n    type Surface = id::SurfaceId;\n    type Texture = id::TextureId;\n    type TextureView = id::TextureViewId;\n    type ExternalTexture = id::ExternalTextureId;\n    type QuerySet = id::QuerySetId;\n    type BindGroup = id::BindGroupId;\n    type RenderPipeline = id::RenderPipelineId;\n    type RenderBundle = id::RenderBundleId;\n    type ComputePipeline = id::ComputePipelineId;\n    type Blas = id::BlasId;\n    type Tlas = id::TlasId;\n}\n\n#[cfg(any(feature = \"trace\", feature = \"replay\"))]\nimpl ReferenceType for PointerReferences {\n    type Buffer = id::PointerId<id::markers::Buffer>;\n    type Surface = id::PointerId<id::markers::Surface>;\n    type Texture = id::PointerId<id::markers::Texture>;\n    type TextureView = id::PointerId<id::markers::TextureView>;\n    type ExternalTexture = id::PointerId<id::markers::ExternalTexture>;\n    type QuerySet = id::PointerId<id::markers::QuerySet>;\n    type BindGroup = id::PointerId<id::markers::BindGroup>;\n    type RenderPipeline = id::PointerId<id::markers::RenderPipeline>;\n    type RenderBundle = id::PointerId<id::markers::RenderBundle>;\n    type ComputePipeline = id::PointerId<id::markers::ComputePipeline>;\n    type Blas = id::PointerId<id::markers::Blas>;\n    type Tlas = id::PointerId<id::markers::Tlas>;\n}\n\nimpl ReferenceType for ArcReferences {\n    type Buffer = Arc<Buffer>;\n    type Surface = Arc<Surface>;\n    type Texture = Arc<Texture>;\n    type TextureView = Arc<crate::resource::TextureView>;\n    type ExternalTexture = Arc<crate::resource::ExternalTexture>;\n    type QuerySet = Arc<QuerySet>;\n    type BindGroup = Arc<crate::binding_model::BindGroup>;\n    type RenderPipeline = Arc<crate::pipeline::RenderPipeline>;\n    type RenderBundle = Arc<crate::command::RenderBundle>;\n    type ComputePipeline = Arc<crate::pipeline::ComputePipeline>;\n    type Blas = Arc<crate::resource::Blas>;\n    type Tlas = Arc<crate::resource::Tlas>;\n}\n\n#[cfg(feature = \"serde\")]\nattribute_alias! {\n    #[apply(serde_object_reference_struct)] =\n    #[derive(serde::Serialize, serde::Deserialize)]\n    #[serde(bound =\n         \"R::Buffer: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::Surface: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::Texture: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::TextureView: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::ExternalTexture: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::QuerySet: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::BindGroup: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::RenderPipeline: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::RenderBundle: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::ComputePipeline: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::Blas: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          R::Tlas: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          wgt::BufferTransition<R::Buffer>: serde::Serialize + for<'d> serde::Deserialize<'d>,\\\n          wgt::TextureTransition<R::Texture>: serde::Serialize + for<'d> serde::Deserialize<'d>\"\n    )];\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub enum Command<R: ReferenceType> {\n    CopyBufferToBuffer {\n        src: R::Buffer,\n        src_offset: wgt::BufferAddress,\n        dst: R::Buffer,\n        dst_offset: wgt::BufferAddress,\n        size: Option<wgt::BufferAddress>,\n    },\n    CopyBufferToTexture {\n        src: wgt::TexelCopyBufferInfo<R::Buffer>,\n        dst: wgt::TexelCopyTextureInfo<R::Texture>,\n        size: wgt::Extent3d,\n    },\n    CopyTextureToBuffer {\n        src: wgt::TexelCopyTextureInfo<R::Texture>,\n        dst: wgt::TexelCopyBufferInfo<R::Buffer>,\n        size: wgt::Extent3d,\n    },\n    CopyTextureToTexture {\n        src: wgt::TexelCopyTextureInfo<R::Texture>,\n        dst: wgt::TexelCopyTextureInfo<R::Texture>,\n        size: wgt::Extent3d,\n    },\n    ClearBuffer {\n        dst: R::Buffer,\n        offset: wgt::BufferAddress,\n        size: Option<wgt::BufferAddress>,\n    },\n    ClearTexture {\n        dst: R::Texture,\n        subresource_range: wgt::ImageSubresourceRange,\n    },\n    WriteTimestamp {\n        query_set: R::QuerySet,\n        query_index: u32,\n    },\n    ResolveQuerySet {\n        query_set: R::QuerySet,\n        start_query: u32,\n        query_count: u32,\n        destination: R::Buffer,\n        destination_offset: wgt::BufferAddress,\n    },\n    PushDebugGroup(String),\n    PopDebugGroup,\n    InsertDebugMarker(String),\n    RunComputePass {\n        pass: crate::command::BasePass<crate::command::ComputeCommand<R>, Infallible>,\n        timestamp_writes: Option<crate::command::PassTimestampWrites<R::QuerySet>>,\n    },\n    RunRenderPass {\n        pass: crate::command::BasePass<crate::command::RenderCommand<R>, Infallible>,\n        color_attachments: ColorAttachments<R::TextureView>,\n        depth_stencil_attachment:\n            Option<crate::command::ResolvedRenderPassDepthStencilAttachment<R::TextureView>>,\n        timestamp_writes: Option<crate::command::PassTimestampWrites<R::QuerySet>>,\n        occlusion_query_set: Option<R::QuerySet>,\n        multiview_mask: Option<NonZero<u32>>,\n    },\n    BuildAccelerationStructures {\n        blas: Vec<crate::ray_tracing::OwnedBlasBuildEntry<R>>,\n        tlas: Vec<crate::ray_tracing::OwnedTlasPackage<R>>,\n    },\n    TransitionResources {\n        buffer_transitions: Vec<wgt::BufferTransition<R::Buffer>>,\n        texture_transitions: Vec<wgt::TextureTransition<R::Texture>>,\n    },\n}\n\npub type ArcCommand = Command<ArcReferences>;\n"
  },
  {
    "path": "wgpu-core/src/command/ffi.rs",
    "content": "//! Types that are useful for FFI bindings to `wgpu`.\n\nuse crate::{command::IdReferences, id};\n\npub type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo<id::BufferId>;\npub type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<id::TextureId>;\npub type CopyExternalImageDestInfo = wgt::CopyExternalImageDestInfo<id::TextureId>;\n\npub type Command = super::Command<IdReferences>;\n"
  },
  {
    "path": "wgpu-core/src/command/memory_init.rs",
    "content": "use alloc::{\n    sync::Arc,\n    vec::{Drain, Vec},\n};\nuse core::ops::Range;\n\nuse hashbrown::hash_map::Entry;\n\nuse crate::{\n    device::Device,\n    init_tracker::*,\n    resource::{DestroyedResourceError, ParentDevice, RawResourceAccess, Texture, Trackable},\n    snatch::SnatchGuard,\n    track::{DeviceTracker, TextureTracker},\n    FastHashMap,\n};\n\nuse super::{clear_texture, BakedCommands, ClearError};\n\n/// Surface that was discarded by `StoreOp::Discard` of a preceding renderpass.\n/// Any read access to this surface needs to be preceded by a texture initialization.\n#[derive(Clone)]\npub(crate) struct TextureSurfaceDiscard {\n    pub texture: Arc<Texture>,\n    pub mip_level: u32,\n    pub layer: u32,\n}\n\npub(crate) type SurfacesInDiscardState = Vec<TextureSurfaceDiscard>;\n\n#[derive(Default)]\npub(crate) struct CommandBufferTextureMemoryActions {\n    /// The tracker actions that we need to be executed before the command\n    /// buffer is executed.\n    init_actions: Vec<TextureInitTrackerAction>,\n    /// All the discards that haven't been followed by init again within the\n    /// command buffer i.e. everything in this list resets the texture init\n    /// state *after* the command buffer execution\n    discards: Vec<TextureSurfaceDiscard>,\n}\n\nimpl CommandBufferTextureMemoryActions {\n    pub(crate) fn drain_init_actions(&mut self) -> Drain<'_, TextureInitTrackerAction> {\n        self.init_actions.drain(..)\n    }\n\n    pub(crate) fn discard(&mut self, discard: TextureSurfaceDiscard) {\n        self.discards.push(discard);\n    }\n\n    // Registers a TextureInitTrackerAction.\n    // Returns previously discarded surface that need to be initialized *immediately* now.\n    // Only returns a non-empty list if action is MemoryInitKind::NeedsInitializedMemory.\n    #[must_use]\n    pub(crate) fn register_init_action(\n        &mut self,\n        action: &TextureInitTrackerAction,\n    ) -> SurfacesInDiscardState {\n        let mut immediately_necessary_clears = SurfacesInDiscardState::new();\n\n        // Note that within a command buffer we may stack arbitrary memory init\n        // actions on the same texture Since we react to them in sequence, they\n        // are going to be dropped again at queue submit\n        //\n        // We don't need to add MemoryInitKind::NeedsInitializedMemory to\n        // init_actions if a surface is part of the discard list. But that would\n        // mean splitting up the action which is more than we'd win here.\n        self.init_actions.extend(\n            action\n                .texture\n                .initialization_status\n                .read()\n                .check_action(action),\n        );\n\n        // We expect very few discarded surfaces at any point in time which is\n        // why a simple linear search is likely best. (i.e. most of the time\n        // self.discards is empty!)\n        let init_actions = &mut self.init_actions;\n        self.discards.retain(|discarded_surface| {\n            if discarded_surface.texture.is_equal(&action.texture)\n                && action.range.layer_range.contains(&discarded_surface.layer)\n                && action\n                    .range\n                    .mip_range\n                    .contains(&discarded_surface.mip_level)\n            {\n                if let MemoryInitKind::NeedsInitializedMemory = action.kind {\n                    immediately_necessary_clears.push(discarded_surface.clone());\n\n                    // Mark surface as implicitly initialized (this is relevant\n                    // because it might have been uninitialized prior to\n                    // discarding\n                    init_actions.push(TextureInitTrackerAction {\n                        texture: discarded_surface.texture.clone(),\n                        range: TextureInitRange {\n                            mip_range: discarded_surface.mip_level\n                                ..(discarded_surface.mip_level + 1),\n                            layer_range: discarded_surface.layer..(discarded_surface.layer + 1),\n                        },\n                        kind: MemoryInitKind::ImplicitlyInitialized,\n                    });\n                }\n                false\n            } else {\n                true\n            }\n        });\n\n        immediately_necessary_clears\n    }\n\n    // Shortcut for register_init_action when it is known that the action is an\n    // implicit init, not requiring any immediate resource init.\n    pub(crate) fn register_implicit_init(\n        &mut self,\n        texture: &Arc<Texture>,\n        range: TextureInitRange,\n    ) {\n        let must_be_empty = self.register_init_action(&TextureInitTrackerAction {\n            texture: texture.clone(),\n            range,\n            kind: MemoryInitKind::ImplicitlyInitialized,\n        });\n        assert!(must_be_empty.is_empty());\n    }\n}\n\n// Utility function that takes discarded surfaces from (several calls to)\n// register_init_action and initializes them on the spot.\n//\n// Takes care of barriers as well!\npub(crate) fn fixup_discarded_surfaces<InitIter: Iterator<Item = TextureSurfaceDiscard>>(\n    inits: InitIter,\n    encoder: &mut dyn hal::DynCommandEncoder,\n    texture_tracker: &mut TextureTracker,\n    device: &Device,\n    snatch_guard: &SnatchGuard<'_>,\n) {\n    for init in inits {\n        clear_texture(\n            &init.texture,\n            TextureInitRange {\n                mip_range: init.mip_level..(init.mip_level + 1),\n                layer_range: init.layer..(init.layer + 1),\n            },\n            encoder,\n            texture_tracker,\n            &device.alignments,\n            device.zero_buffer.as_ref(),\n            snatch_guard,\n            device.instance_flags,\n        )\n        .unwrap();\n    }\n}\n\nimpl BakedCommands {\n    // inserts all buffer initializations that are going to be needed for\n    // executing the commands and updates resource init states accordingly\n    pub(crate) fn initialize_buffer_memory(\n        &mut self,\n        device_tracker: &mut DeviceTracker,\n        snatch_guard: &SnatchGuard<'_>,\n    ) -> Result<(), DestroyedResourceError> {\n        profiling::scope!(\"initialize_buffer_memory\");\n\n        // Gather init ranges for each buffer so we can collapse them.\n        // It is not possible to do this at an earlier point since previously\n        // executed command buffer change the resource init state.\n        let mut uninitialized_ranges_per_buffer = FastHashMap::default();\n        for buffer_use in self.buffer_memory_init_actions.drain(..) {\n            let mut initialization_status = buffer_use.buffer.initialization_status.write();\n\n            // align the end to 4\n            let end_remainder = buffer_use.range.end % wgt::COPY_BUFFER_ALIGNMENT;\n            let end = if end_remainder == 0 {\n                buffer_use.range.end\n            } else {\n                buffer_use.range.end + wgt::COPY_BUFFER_ALIGNMENT - end_remainder\n            };\n            let uninitialized_ranges = initialization_status.drain(buffer_use.range.start..end);\n\n            match buffer_use.kind {\n                MemoryInitKind::ImplicitlyInitialized => {}\n                MemoryInitKind::NeedsInitializedMemory => {\n                    match uninitialized_ranges_per_buffer.entry(buffer_use.buffer.tracker_index()) {\n                        Entry::Vacant(e) => {\n                            e.insert((\n                                buffer_use.buffer.clone(),\n                                uninitialized_ranges.collect::<Vec<Range<wgt::BufferAddress>>>(),\n                            ));\n                        }\n                        Entry::Occupied(mut e) => {\n                            e.get_mut().1.extend(uninitialized_ranges);\n                        }\n                    }\n                }\n            }\n        }\n\n        for (buffer, mut ranges) in uninitialized_ranges_per_buffer.into_values() {\n            // Collapse touching ranges.\n            ranges.sort_by_key(|r| r.start);\n            for i in (1..ranges.len()).rev() {\n                // The memory init tracker made sure of this!\n                assert!(ranges[i - 1].end <= ranges[i].start);\n                if ranges[i].start == ranges[i - 1].end {\n                    ranges[i - 1].end = ranges[i].end;\n                    ranges.swap_remove(i); // Ordering not important at this point\n                }\n            }\n\n            // Don't do use_replace since the buffer may already no longer have\n            // a ref_count.\n            //\n            // However, we *know* that it is currently in use, so the tracker\n            // must already know about it.\n            let transition = device_tracker\n                .buffers\n                .set_single(&buffer, wgt::BufferUses::COPY_DST);\n\n            let raw_buf = buffer.try_raw(snatch_guard)?;\n\n            unsafe {\n                self.encoder.raw.transition_buffers(\n                    transition\n                        .map(|pending| pending.into_hal(&buffer, snatch_guard))\n                        .as_slice(),\n                );\n            }\n\n            for range in ranges.iter() {\n                assert!(\n                    range.start % wgt::COPY_BUFFER_ALIGNMENT == 0,\n                    \"Buffer {:?} has an uninitialized range with a start \\\n                         not aligned to 4 (start was {})\",\n                    raw_buf,\n                    range.start\n                );\n                assert!(\n                    range.end % wgt::COPY_BUFFER_ALIGNMENT == 0,\n                    \"Buffer {:?} has an uninitialized range with an end \\\n                         not aligned to 4 (end was {})\",\n                    raw_buf,\n                    range.end\n                );\n\n                unsafe {\n                    self.encoder.raw.clear_buffer(raw_buf, range.clone());\n                }\n            }\n        }\n        Ok(())\n    }\n\n    // inserts all texture initializations that are going to be needed for\n    // executing the commands and updates resource init states accordingly any\n    // textures that are left discarded by this command buffer will be marked as\n    // uninitialized\n    pub(crate) fn initialize_texture_memory(\n        &mut self,\n        device_tracker: &mut DeviceTracker,\n        device: &Device,\n        snatch_guard: &SnatchGuard<'_>,\n    ) -> Result<(), DestroyedResourceError> {\n        profiling::scope!(\"initialize_texture_memory\");\n\n        let mut ranges: Vec<TextureInitRange> = Vec::new();\n        for texture_use in self.texture_memory_actions.drain_init_actions() {\n            let mut initialization_status = texture_use.texture.initialization_status.write();\n            let use_range = texture_use.range;\n            let affected_mip_trackers = initialization_status\n                .mips\n                .iter_mut()\n                .enumerate()\n                .skip(use_range.mip_range.start as usize)\n                .take((use_range.mip_range.end - use_range.mip_range.start) as usize);\n\n            match texture_use.kind {\n                MemoryInitKind::ImplicitlyInitialized => {\n                    for (_, mip_tracker) in affected_mip_trackers {\n                        mip_tracker.drain(use_range.layer_range.clone());\n                    }\n                }\n                MemoryInitKind::NeedsInitializedMemory => {\n                    for (mip_level, mip_tracker) in affected_mip_trackers {\n                        for layer_range in mip_tracker.drain(use_range.layer_range.clone()) {\n                            ranges.push(TextureInitRange {\n                                mip_range: (mip_level as u32)..(mip_level as u32 + 1),\n                                layer_range,\n                            });\n                        }\n                    }\n                }\n            }\n\n            // TODO: Could we attempt some range collapsing here?\n            for range in ranges.drain(..) {\n                let clear_result = clear_texture(\n                    &texture_use.texture,\n                    range,\n                    self.encoder.raw.as_mut(),\n                    &mut device_tracker.textures,\n                    &device.alignments,\n                    device.zero_buffer.as_ref(),\n                    snatch_guard,\n                    device.instance_flags,\n                );\n\n                // A Texture can be destroyed between the command recording\n                // and now, this is out of our control so we have to handle\n                // it gracefully.\n                if let Err(ClearError::DestroyedResource(e)) = clear_result {\n                    return Err(e);\n                }\n\n                // Other errors are unexpected.\n                if let Err(error) = clear_result {\n                    panic!(\"{error}\");\n                }\n            }\n        }\n\n        // Now that all buffers/textures have the proper init state for before\n        // cmdbuf start, we discard init states for textures it left discarded\n        // after its execution.\n        for surface_discard in self.texture_memory_actions.discards.iter() {\n            surface_discard\n                .texture\n                .initialization_status\n                .write()\n                .discard(surface_discard.mip_level, surface_discard.layer);\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/mod.rs",
    "content": "//! # Command Encoding\n//!\n//! TODO: High-level description of command encoding.\n//!\n//! The convention in this module is that functions accepting a [`&mut dyn\n//! hal::DynCommandEncoder`] are low-level helpers and may assume the encoder is\n//! in the open state, ready to encode commands. Encoders that are not open\n//! should be nested within some other container that provides additional\n//! state tracking, like [`InnerCommandEncoder`].\n\nmod allocator;\nmod bind;\nmod bundle;\nmod clear;\nmod compute;\nmod compute_command;\nmod draw;\nmod encoder;\nmod encoder_command;\npub mod ffi;\nmod memory_init;\nmod pass;\nmod query;\nmod ray_tracing;\nmod render;\nmod render_command;\nmod timestamp_writes;\nmod transfer;\nmod transition_resources;\n\nuse alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};\nuse core::convert::Infallible;\nuse core::mem::{self, ManuallyDrop};\nuse core::{ops, panic};\n\n#[cfg(feature = \"serde\")]\npub(crate) use self::encoder_command::serde_object_reference_struct;\n#[cfg(any(feature = \"trace\", feature = \"replay\"))]\n#[doc(hidden)]\npub use self::encoder_command::PointerReferences;\n// This module previously did `pub use *` for some of the submodules. When that\n// was removed, every type that was previously public via `use *` was listed\n// here. Some types (in particular `CopySide`) may be exported unnecessarily.\npub use self::{\n    bundle::{\n        bundle_ffi, CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor,\n        RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError,\n        RenderBundleErrorInner,\n    },\n    clear::ClearError,\n    compute::{\n        ComputeBasePass, ComputePass, ComputePassDescriptor, ComputePassError,\n        ComputePassErrorInner, DispatchError,\n    },\n    compute_command::ArcComputeCommand,\n    draw::{DrawError, Rect, RenderCommandError},\n    encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},\n    query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType},\n    render::{\n        ArcRenderPassColorAttachment, AttachmentError, AttachmentErrorLocation,\n        ColorAttachmentError, ColorAttachments, LoadOp, PassChannel, RenderBasePass, RenderPass,\n        RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,\n        RenderPassError, RenderPassErrorInner, ResolvedPassChannel,\n        ResolvedRenderPassDepthStencilAttachment, StoreOp,\n    },\n    render_command::ArcRenderCommand,\n    transfer::{CopySide, TransferError},\n    transition_resources::TransitionResourcesError,\n};\npub(crate) use self::{\n    clear::clear_texture,\n    encoder::EncodingState,\n    memory_init::CommandBufferTextureMemoryActions,\n    render::{get_stride_of_indirect_args, VertexLimits},\n    transfer::{\n        extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,\n        validate_texture_copy_dst_format, validate_texture_copy_range,\n    },\n};\n\npub(crate) use allocator::CommandAllocator;\n\n/// cbindgen:ignore\npub use self::{compute_command::ComputeCommand, render_command::RenderCommand};\n\npub(crate) use timestamp_writes::ArcPassTimestampWrites;\npub use timestamp_writes::PassTimestampWrites;\n\nuse crate::binding_model::BindingError;\nuse crate::device::queue::TempResource;\nuse crate::device::{Device, DeviceError, MissingFeatures};\nuse crate::id::Id;\nuse crate::lock::{rank, Mutex};\nuse crate::snatch::SnatchGuard;\n\nuse crate::init_tracker::BufferInitTrackerAction;\nuse crate::ray_tracing::{AsAction, BuildAccelerationStructureError};\nuse crate::resource::{\n    DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,\n};\nuse crate::storage::Storage;\nuse crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};\nuse crate::{api_log, global::Global, id, resource_log, Label};\nuse crate::{hal_label, LabelHelpers};\n\nuse wgt::error::{ErrorType, WebGpuError};\n\nuse thiserror::Error;\n\n/// cbindgen:ignore\npub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;\n/// cbindgen:ignore\npub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;\n/// cbindgen:ignore\npub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;\n\nconst IMMEDIATES_CLEAR_ARRAY: &[u32] = &[0_u32; 64];\n\npub(crate) struct EncoderErrorState {\n    error: CommandEncoderError,\n\n    #[cfg(feature = \"trace\")]\n    trace_commands: Option<Vec<Command<PointerReferences>>>,\n}\n\n/// Construct an `EncoderErrorState` with only a `CommandEncoderError` (without\n/// any traced commands).\n///\n/// This is used in cases where pass begin/end were mismatched, if the same\n/// encoder was finished multiple times, or in the status of a command buffer\n/// (in which case the commands were already saved to the trace). In some of\n/// these cases there may be commands that could be saved to the trace, but if\n/// the application is that confused about using encoders, it's not clear\n/// whether it's worth the effort to try and preserve the commands.\nfn make_error_state<E: Into<CommandEncoderError>>(error: E) -> CommandEncoderStatus {\n    CommandEncoderStatus::Error(EncoderErrorState {\n        error: error.into(),\n\n        #[cfg(feature = \"trace\")]\n        trace_commands: None,\n    })\n}\n\n/// The current state of a command or pass encoder.\n///\n/// In the WebGPU spec, the state of an encoder (open, locked, or ended) is\n/// orthogonal to the validity of the encoder. However, this enum does not\n/// represent the state of an invalid encoder.\npub(crate) enum CommandEncoderStatus {\n    /// Ready to record commands. An encoder's initial state.\n    ///\n    /// Command building methods like [`command_encoder_clear_buffer`] and\n    /// [`compute_pass_end`] require the encoder to be in this\n    /// state.\n    ///\n    /// This corresponds to WebGPU's \"open\" state.\n    /// See <https://www.w3.org/TR/webgpu/#encoder-state-open>\n    ///\n    /// [`command_encoder_clear_buffer`]: Global::command_encoder_clear_buffer\n    /// [`compute_pass_end`]: Global::compute_pass_end\n    Recording(CommandBufferMutable),\n\n    /// Locked by a render or compute pass.\n    ///\n    /// This state is entered when a render/compute pass is created,\n    /// and exited when the pass is ended.\n    ///\n    /// As long as the command encoder is locked, any command building operation\n    /// on it will fail and put the encoder into the [`Self::Error`] state. See\n    /// <https://www.w3.org/TR/webgpu/#encoder-state-locked>\n    Locked(CommandBufferMutable),\n\n    Consumed,\n\n    /// Command recording is complete, and the buffer is ready for submission.\n    ///\n    /// [`Global::command_encoder_finish`] transitions a\n    /// `CommandBuffer` from the `Recording` state into this state.\n    ///\n    /// [`Global::queue_submit`] requires that command buffers are\n    /// in this state.\n    ///\n    /// This corresponds to WebGPU's \"ended\" state.\n    /// See <https://www.w3.org/TR/webgpu/#encoder-state-ended>\n    Finished(CommandBufferMutable),\n\n    /// The command encoder is invalid.\n    ///\n    /// The error that caused the invalidation is stored here, and will\n    /// be raised by `CommandEncoder.finish()`.\n    Error(EncoderErrorState),\n\n    /// Temporary state used internally by methods on `CommandEncoderStatus`.\n    /// Encoder should never be left in this state.\n    Transitioning,\n}\n\nimpl CommandEncoderStatus {\n    #[doc(hidden)]\n    fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {\n        let Self::Recording(cmd_buf_data) = self else {\n            panic!(\"encoder should be in the recording state\");\n        };\n        cmd_buf_data.commands.extend(commands);\n    }\n\n    /// Push a command provided by a closure onto the encoder.\n    ///\n    /// If the encoder is in the [`Self::Recording`] state, calls the closure to\n    /// obtain a command, and pushes it onto the encoder. If the closure returns\n    /// an error, stores that error in the encoder for later reporting when\n    /// `finish()` is called. Returns `Ok(())` even if the closure returned an\n    /// error.\n    ///\n    /// If the encoder is not in the [`Self::Recording`] state, the closure will\n    /// not be called and nothing will be recorded. The encoder will be\n    /// invalidated (if it is not already). If the error is a [validation error\n    /// that should be raised immediately][ves], returns it in `Err`, otherwise,\n    /// returns `Ok(())`.\n    ///\n    /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state\n    fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(\n        &mut self,\n        f: F,\n    ) -> Result<(), EncoderStateError> {\n        match self {\n            Self::Recording(cmd_buf_data) => {\n                cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);\n                match f() {\n                    Ok(cmd) => cmd_buf_data.commands.push(cmd),\n                    Err(err) => {\n                        self.invalidate(err);\n                    }\n                }\n                Ok(())\n            }\n            Self::Locked(_) => {\n                // Invalidate the encoder and do not record anything, but do not\n                // return an immediate validation error.\n                self.invalidate(EncoderStateError::Locked);\n                Ok(())\n            }\n            // Encoder is ended. Invalidate the encoder, do not record anything,\n            // and return an immediate validation error.\n            Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),\n            Self::Consumed => Err(EncoderStateError::Ended),\n            // Encoder is already invalid. Do not record anything, but do not\n            // return an immediate validation error.\n            Self::Error(_) => Ok(()),\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    /// Call a closure with the inner command buffer structure.\n    ///\n    /// If the encoder is in the [`Self::Recording`] state, calls the provided\n    /// closure. If the closure returns an error, stores that error in the\n    /// encoder for later reporting when `finish()` is called. Returns `Ok(())`\n    /// even if the closure returned an error.\n    ///\n    /// If the encoder is not in the [`Self::Recording`] state, the closure will\n    /// not be called. The encoder will be invalidated (if it is not already).\n    /// If the error is a [validation error that should be raised\n    /// immediately][ves], returns it in `Err`, otherwise, returns `Ok(())`.\n    ///\n    /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state\n    fn with_buffer<\n        F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,\n        E: Clone + Into<CommandEncoderError>,\n    >(\n        &mut self,\n        api: EncodingApi,\n        f: F,\n    ) -> Result<(), EncoderStateError> {\n        match self {\n            Self::Recording(inner) => {\n                inner.encoder.api.set(api);\n                RecordingGuard { inner: self }.record(f);\n                Ok(())\n            }\n            Self::Locked(_) => {\n                // Invalidate the encoder and do not record anything, but do not\n                // return an immediate validation error.\n                self.invalidate(EncoderStateError::Locked);\n                Ok(())\n            }\n            // Encoder is ended. Invalidate the encoder, do not record anything,\n            // and return an immediate validation error.\n            Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),\n            Self::Consumed => Err(EncoderStateError::Ended),\n            // Encoder is already invalid. Do not record anything, but do not\n            // return an immediate validation error.\n            Self::Error(_) => Ok(()),\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    /// Special version of record used by `command_encoder_as_hal_mut`. This\n    /// differs from the regular version in two ways:\n    ///\n    /// 1. The recording closure is infallible.\n    /// 2. The recording closure takes `Option<&mut CommandBufferMutable>`, and\n    ///    in the case that the encoder is not in a valid state for recording, the\n    ///    closure is still called, with `None` as its argument.\n    pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(\n        &mut self,\n        f: F,\n    ) -> T {\n        match self {\n            Self::Recording(inner) => {\n                inner.encoder.api.set(EncodingApi::Raw);\n                RecordingGuard { inner: self }.record_as_hal_mut(f)\n            }\n            Self::Locked(_) => {\n                self.invalidate(EncoderStateError::Locked);\n                f(None)\n            }\n            Self::Finished(_) => {\n                self.invalidate(EncoderStateError::Ended);\n                f(None)\n            }\n            Self::Consumed => f(None),\n            Self::Error(_) => f(None),\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    /// Locks the encoder by putting it in the [`Self::Locked`] state.\n    ///\n    /// Render or compute passes call this on start. At the end of the pass,\n    /// they call [`Self::unlock_encoder`] to put the [`CommandBuffer`] back\n    /// into the [`Self::Recording`] state.\n    fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {\n        match mem::replace(self, Self::Transitioning) {\n            Self::Recording(inner) => {\n                *self = Self::Locked(inner);\n                Ok(())\n            }\n            st @ Self::Finished(_) => {\n                // Attempting to open a pass on a finished encoder raises a\n                // validation error but does not invalidate the encoder. This is\n                // related to https://github.com/gpuweb/gpuweb/issues/5207.\n                *self = st;\n                Err(EncoderStateError::Ended)\n            }\n            Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),\n            st @ Self::Consumed => {\n                *self = st;\n                Err(EncoderStateError::Ended)\n            }\n            st @ Self::Error(_) => {\n                *self = st;\n                Err(EncoderStateError::Invalid)\n            }\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    /// Unlocks the encoder and puts it back into the [`Self::Recording`] state.\n    ///\n    /// This function is the unlocking counterpart to [`Self::lock_encoder`]. It\n    /// is only valid to call this function if the encoder is in the\n    /// [`Self::Locked`] state.\n    ///\n    /// If the encoder is in a state other than [`Self::Locked`] and a\n    /// validation error should be raised immediately, returns it in `Err`,\n    /// otherwise, stores the error in the encoder and returns `Ok(())`.\n    fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {\n        match mem::replace(self, Self::Transitioning) {\n            Self::Locked(inner) => {\n                *self = Self::Recording(inner);\n                Ok(())\n            }\n            st @ Self::Finished(_) => {\n                *self = st;\n                Err(EncoderStateError::Ended)\n            }\n            Self::Recording(_) => {\n                *self = make_error_state(EncoderStateError::Unlocked);\n                Err(EncoderStateError::Unlocked)\n            }\n            st @ Self::Consumed => {\n                *self = st;\n                Err(EncoderStateError::Ended)\n            }\n            st @ Self::Error(_) => {\n                // Encoder is already invalid. The error will be reported by\n                // `CommandEncoder.finish`.\n                *self = st;\n                Ok(())\n            }\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    fn finish(&mut self) -> Self {\n        // Replace our state with `Consumed`, and return either the inner\n        // state or an error, to be transferred to the command buffer.\n        match mem::replace(self, Self::Consumed) {\n            Self::Recording(inner) => {\n                // Raw encoding leaves the encoder open in `command_encoder_as_hal_mut`.\n                // Otherwise, nothing should have opened it yet.\n                if inner.encoder.api != EncodingApi::Raw {\n                    assert!(!inner.encoder.is_open);\n                }\n                Self::Finished(inner)\n            }\n            Self::Consumed | Self::Finished(_) => make_error_state(EncoderStateError::Ended),\n            Self::Locked(_) => make_error_state(EncoderStateError::Locked),\n            st @ Self::Error(_) => st,\n            Self::Transitioning => unreachable!(),\n        }\n    }\n\n    /// Invalidate the command encoder due to an error.\n    ///\n    /// The error `err` is stored so that it can be reported when the encoder is\n    /// finished. If tracing is enabled, the traced commands are also stored.\n    ///\n    /// Since we do not track the state of an invalid encoder, it is not\n    /// necessary to unlock an encoder that has been invalidated.\n    fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {\n        #[cfg(feature = \"trace\")]\n        let trace_commands = match self {\n            Self::Recording(cmd_buf_data) => Some(\n                mem::take(&mut cmd_buf_data.commands)\n                    .into_iter()\n                    .map(crate::device::trace::IntoTrace::into_trace)\n                    .collect(),\n            ),\n            _ => None,\n        };\n\n        let enc_err = err.clone().into();\n        api_log!(\"Invalidating command encoder: {enc_err:?}\");\n        *self = Self::Error(EncoderErrorState {\n            error: enc_err,\n            #[cfg(feature = \"trace\")]\n            trace_commands,\n        });\n        err\n    }\n}\n\n/// A guard to enforce error reporting, for a [`CommandBuffer`] in the [`Recording`] state.\n///\n/// An [`RecordingGuard`] holds a mutable reference to a [`CommandEncoderStatus`] that\n/// has been verified to be in the [`Recording`] state. The [`RecordingGuard`] dereferences\n/// mutably to the [`CommandBufferMutable`] that the status holds.\n///\n/// Dropping an [`RecordingGuard`] sets the [`CommandBuffer`]'s state to\n/// [`CommandEncoderStatus::Error`]. If your use of the guard was\n/// successful, call its [`mark_successful`] method to dispose of it.\n///\n/// [`Recording`]: CommandEncoderStatus::Recording\n/// [`mark_successful`]: Self::mark_successful\npub(crate) struct RecordingGuard<'a> {\n    inner: &'a mut CommandEncoderStatus,\n}\n\nimpl<'a> RecordingGuard<'a> {\n    pub(crate) fn mark_successful(self) {\n        mem::forget(self)\n    }\n\n    fn record<\n        F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,\n        E: Clone + Into<CommandEncoderError>,\n    >(\n        mut self,\n        f: F,\n    ) {\n        match f(&mut self) {\n            Ok(()) => self.mark_successful(),\n            Err(err) => {\n                self.inner.invalidate(err);\n            }\n        }\n    }\n\n    /// Special version of record used by `command_encoder_as_hal_mut`. This\n    /// version takes an infallible recording closure.\n    pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(\n        mut self,\n        f: F,\n    ) -> T {\n        let res = f(Some(&mut self));\n        self.mark_successful();\n        res\n    }\n}\n\nimpl<'a> Drop for RecordingGuard<'a> {\n    fn drop(&mut self) {\n        if matches!(*self.inner, CommandEncoderStatus::Error(_)) {\n            // Don't overwrite an error that is already present.\n            return;\n        }\n        self.inner.invalidate(EncoderStateError::Invalid);\n    }\n}\n\nimpl<'a> ops::Deref for RecordingGuard<'a> {\n    type Target = CommandBufferMutable;\n\n    fn deref(&self) -> &Self::Target {\n        match &*self.inner {\n            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl<'a> ops::DerefMut for RecordingGuard<'a> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        match self.inner {\n            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,\n            _ => unreachable!(),\n        }\n    }\n}\n\npub(crate) struct CommandEncoder {\n    pub(crate) device: Arc<Device>,\n\n    pub(crate) label: String,\n\n    /// The mutable state of this command encoder.\n    pub(crate) data: Mutex<CommandEncoderStatus>,\n}\n\ncrate::impl_resource_type!(CommandEncoder);\ncrate::impl_labeled!(CommandEncoder);\ncrate::impl_parent_device!(CommandEncoder);\ncrate::impl_storage_item!(CommandEncoder);\n\nimpl Drop for CommandEncoder {\n    fn drop(&mut self) {\n        resource_log!(\"Drop {}\", self.error_ident());\n    }\n}\n\n/// The encoding API being used with a `CommandEncoder`.\n///\n/// Mixing APIs on the same encoder is not allowed.\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\npub enum EncodingApi {\n    // The regular wgpu encoding APIs are being used.\n    Wgpu,\n\n    // The raw hal encoding API is being used.\n    Raw,\n\n    // Neither encoding API has been called yet.\n    Undecided,\n\n    // The encoder is used internally by wgpu.\n    InternalUse,\n}\n\nimpl EncodingApi {\n    pub(crate) fn set(&mut self, api: EncodingApi) {\n        match *self {\n            EncodingApi::Undecided => {\n                *self = api;\n            }\n            self_api if self_api != api => {\n                panic!(\"Mixing the wgpu encoding API with the raw encoding API is not permitted\");\n            }\n            _ => {}\n        }\n    }\n}\n\n/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.\n///\n/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is\n/// where the commands are actually stored.\n///\n/// This holds a `Vec` of raw [`CommandBuffer`][rcb]s, not just one. We are not\n/// always able to record commands in the order in which they must ultimately be\n/// submitted to the queue, but raw command buffers don't permit inserting new\n/// commands into the middle of a recorded stream. However, hal queue submission\n/// accepts a series of command buffers at once, so we can simply break the\n/// stream up into multiple buffers, and then reorder the buffers. See\n/// [`InnerCommandEncoder::close_and_swap`] for a specific example of this.\n///\n/// [rce]: hal::Api::CommandEncoder\n/// [rcb]: hal::Api::CommandBuffer\npub(crate) struct InnerCommandEncoder {\n    /// The underlying `wgpu_hal` [`CommandEncoder`].\n    ///\n    /// Successfully executed command buffers' encoders are saved in a\n    /// [`CommandAllocator`] for recycling.\n    ///\n    /// [`CommandEncoder`]: hal::Api::CommandEncoder\n    /// [`CommandAllocator`]: crate::command::CommandAllocator\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,\n\n    /// All the raw command buffers for our owning [`CommandBuffer`], in\n    /// submission order.\n    ///\n    /// These command buffers were all constructed with `raw`. The\n    /// [`wgpu_hal::CommandEncoder`] trait forbids these from outliving `raw`,\n    /// and requires that we provide all of these when we call\n    /// [`raw.reset_all()`][CE::ra], so the encoder and its buffers travel\n    /// together.\n    ///\n    /// [CE::ra]: hal::CommandEncoder::reset_all\n    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\n    pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,\n\n    pub(crate) device: Arc<Device>,\n\n    /// True if `raw` is in the \"recording\" state.\n    ///\n    /// See the documentation for [`wgpu_hal::CommandEncoder`] for\n    /// details on the states `raw` can be in.\n    ///\n    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\n    pub(crate) is_open: bool,\n\n    /// Tracks which API is being used to encode commands.\n    ///\n    /// Mixing the wgpu encoding API with access to the raw hal encoder via\n    /// `as_hal_mut` is not supported. this field tracks which API is being used\n    /// in order to detect and reject invalid usage.\n    pub(crate) api: EncodingApi,\n\n    pub(crate) label: String,\n}\n\nimpl InnerCommandEncoder {\n    /// Finish the current command buffer and insert it just before\n    /// the last element in [`self.list`][l].\n    ///\n    /// On return, the underlying hal encoder is closed.\n    ///\n    /// What is this for?\n    ///\n    /// The `wgpu_hal` contract requires that each render or compute pass's\n    /// commands be preceded by calls to [`transition_buffers`] and\n    /// [`transition_textures`], to put the resources the pass operates on in\n    /// the appropriate state. Unfortunately, we don't know which transitions\n    /// are needed until we're done recording the pass itself. Rather than\n    /// iterating over the pass twice, we note the necessary transitions as we\n    /// record its commands, finish the raw command buffer for the actual pass,\n    /// record a new raw command buffer for the transitions, and jam that buffer\n    /// in just before the pass's. This is the function that jams in the\n    /// transitions' command buffer.\n    ///\n    /// # Panics\n    ///\n    /// - If the encoder is not open.\n    ///\n    /// [l]: InnerCommandEncoder::list\n    /// [`transition_buffers`]: hal::CommandEncoder::transition_buffers\n    /// [`transition_textures`]: hal::CommandEncoder::transition_textures\n    fn close_and_swap(&mut self) -> Result<(), DeviceError> {\n        assert!(self.is_open);\n        self.is_open = false;\n\n        let new =\n            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;\n        self.list.insert(self.list.len() - 1, new);\n\n        Ok(())\n    }\n\n    /// Finish the current command buffer and insert it at the beginning\n    /// of [`self.list`][l].\n    ///\n    /// On return, the underlying hal encoder is closed.\n    ///\n    /// # Panics\n    ///\n    /// - If the encoder is not open.\n    ///\n    /// [l]: InnerCommandEncoder::list\n    pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {\n        assert!(self.is_open);\n        self.is_open = false;\n\n        let new =\n            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;\n        self.list.insert(0, new);\n\n        Ok(())\n    }\n\n    /// Finish the current command buffer, and push it onto\n    /// the end of [`self.list`][l].\n    ///\n    /// On return, the underlying hal encoder is closed.\n    ///\n    /// # Panics\n    ///\n    /// - If the encoder is not open.\n    ///\n    /// [l]: InnerCommandEncoder::list\n    pub(crate) fn close(&mut self) -> Result<(), DeviceError> {\n        assert!(self.is_open);\n        self.is_open = false;\n\n        let cmd_buf =\n            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;\n        self.list.push(cmd_buf);\n\n        Ok(())\n    }\n\n    /// Finish the current command buffer, if any, and add it to the\n    /// end of [`self.list`][l].\n    ///\n    /// If we have opened this command encoder, finish its current\n    /// command buffer, and push it onto the end of [`self.list`][l].\n    /// If this command buffer is closed, do nothing.\n    ///\n    /// On return, the underlying hal encoder is closed.\n    ///\n    /// [l]: InnerCommandEncoder::list\n    fn close_if_open(&mut self) -> Result<(), DeviceError> {\n        if self.is_open {\n            self.is_open = false;\n            let cmd_buf =\n                unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;\n            self.list.push(cmd_buf);\n        }\n\n        Ok(())\n    }\n\n    /// If the command encoder is not open, begin recording a new command buffer.\n    ///\n    /// If the command encoder was already open, does nothing.\n    ///\n    /// In both cases, returns a reference to the raw encoder.\n    fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {\n        if !self.is_open {\n            let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);\n            unsafe { self.raw.begin_encoding(hal_label) }\n                .map_err(|e| self.device.handle_hal_error(e))?;\n            self.is_open = true;\n        }\n\n        Ok(self.raw.as_mut())\n    }\n\n    /// Begin recording a new command buffer, if we haven't already.\n    ///\n    /// The underlying hal encoder is put in the \"recording\" state.\n    pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {\n        if !self.is_open {\n            let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);\n            unsafe { self.raw.begin_encoding(hal_label) }\n                .map_err(|e| self.device.handle_hal_error(e))?;\n            self.is_open = true;\n        }\n\n        Ok(self.raw.as_mut())\n    }\n\n    /// Begin recording a new command buffer for a render or compute pass, with\n    /// its own label.\n    ///\n    /// The underlying hal encoder is put in the \"recording\" state.\n    ///\n    /// # Panics\n    ///\n    /// - If the encoder is already open.\n    pub(crate) fn open_pass(\n        &mut self,\n        label: Option<&str>,\n    ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {\n        assert!(!self.is_open);\n\n        let hal_label = hal_label(label, self.device.instance_flags);\n        unsafe { self.raw.begin_encoding(hal_label) }\n            .map_err(|e| self.device.handle_hal_error(e))?;\n        self.is_open = true;\n\n        Ok(self.raw.as_mut())\n    }\n}\n\nimpl Drop for InnerCommandEncoder {\n    fn drop(&mut self) {\n        if self.is_open {\n            unsafe { self.raw.discard_encoding() };\n        }\n        unsafe {\n            self.raw.reset_all(mem::take(&mut self.list));\n        }\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        self.device.command_allocator.release_encoder(raw);\n    }\n}\n\n/// Look at the documentation for [`CommandBufferMutable`] for an explanation of\n/// the fields in this struct. This is the \"built\" counterpart to that type.\npub(crate) struct BakedCommands {\n    pub(crate) encoder: InnerCommandEncoder,\n    pub(crate) trackers: Tracker,\n    pub(crate) temp_resources: Vec<TempResource>,\n    pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,\n    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,\n    texture_memory_actions: CommandBufferTextureMemoryActions,\n}\n\n/// The mutable state of a [`CommandBuffer`].\npub struct CommandBufferMutable {\n    /// The [`wgpu_hal::Api::CommandBuffer`]s we've built so far, and the encoder\n    /// they belong to.\n    ///\n    /// [`wgpu_hal::Api::CommandBuffer`]: hal::Api::CommandBuffer\n    pub(crate) encoder: InnerCommandEncoder,\n\n    /// All the resources that the commands recorded so far have referred to.\n    pub(crate) trackers: Tracker,\n\n    /// The regions of buffers and textures these commands will read and write.\n    ///\n    /// This is used to determine which portions of which\n    /// buffers/textures we actually need to initialize. If we're\n    /// definitely going to write to something before we read from it,\n    /// we don't need to clear its contents.\n    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,\n    texture_memory_actions: CommandBufferTextureMemoryActions,\n\n    as_actions: Vec<AsAction>,\n    temp_resources: Vec<TempResource>,\n\n    indirect_draw_validation_resources: crate::indirect_validation::DrawResources,\n\n    pub(crate) commands: Vec<Command<ArcReferences>>,\n\n    /// If tracing, `command_encoder_finish` replaces the `Arc`s in `commands`\n    /// with integer pointers, and moves them into `trace_commands`.\n    #[cfg(feature = \"trace\")]\n    pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,\n}\n\nimpl CommandBufferMutable {\n    pub(crate) fn into_baked_commands(self) -> BakedCommands {\n        BakedCommands {\n            encoder: self.encoder,\n            trackers: self.trackers,\n            temp_resources: self.temp_resources,\n            indirect_draw_validation_resources: self.indirect_draw_validation_resources,\n            buffer_memory_init_actions: self.buffer_memory_init_actions,\n            texture_memory_actions: self.texture_memory_actions,\n        }\n    }\n}\n\n/// A buffer of commands to be submitted to the GPU for execution.\n///\n/// Once a command buffer is submitted to the queue, its contents are taken\n/// to construct a [`BakedCommands`], whose contents eventually become the\n/// property of the submission queue.\npub struct CommandBuffer {\n    pub(crate) device: Arc<Device>,\n    /// The `label` from the descriptor used to create the resource.\n    label: String,\n\n    /// The mutable state of this command buffer.\n    pub(crate) data: Mutex<CommandEncoderStatus>,\n}\n\nimpl Drop for CommandBuffer {\n    fn drop(&mut self) {\n        resource_log!(\"Drop {}\", self.error_ident());\n    }\n}\n\nimpl CommandEncoder {\n    pub(crate) fn new(\n        encoder: Box<dyn hal::DynCommandEncoder>,\n        device: &Arc<Device>,\n        label: &Label,\n    ) -> Self {\n        CommandEncoder {\n            device: device.clone(),\n            label: label.to_string(),\n            data: Mutex::new(\n                rank::COMMAND_BUFFER_DATA,\n                CommandEncoderStatus::Recording(CommandBufferMutable {\n                    encoder: InnerCommandEncoder {\n                        raw: ManuallyDrop::new(encoder),\n                        list: Vec::new(),\n                        device: device.clone(),\n                        is_open: false,\n                        api: EncodingApi::Undecided,\n                        label: label.to_string(),\n                    },\n                    trackers: Tracker::new(\n                        device.ordered_buffer_usages,\n                        device.ordered_texture_usages,\n                    ),\n                    buffer_memory_init_actions: Default::default(),\n                    texture_memory_actions: Default::default(),\n                    as_actions: Default::default(),\n                    temp_resources: Default::default(),\n                    indirect_draw_validation_resources:\n                        crate::indirect_validation::DrawResources::new(device.clone()),\n                    commands: Vec::new(),\n                    #[cfg(feature = \"trace\")]\n                    trace_commands: if device.trace.lock().is_some() {\n                        Some(Vec::new())\n                    } else {\n                        None\n                    },\n                }),\n            ),\n        }\n    }\n\n    pub(crate) fn new_invalid(\n        device: &Arc<Device>,\n        label: &Label,\n        err: CommandEncoderError,\n    ) -> Self {\n        CommandEncoder {\n            device: device.clone(),\n            label: label.to_string(),\n            data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),\n        }\n    }\n\n    pub(crate) fn insert_barriers_from_tracker(\n        raw: &mut dyn hal::DynCommandEncoder,\n        base: &mut Tracker,\n        head: &Tracker,\n        snatch_guard: &SnatchGuard,\n    ) {\n        profiling::scope!(\"insert_barriers\");\n\n        base.buffers.set_from_tracker(&head.buffers);\n        base.textures.set_from_tracker(&head.textures);\n\n        Self::drain_barriers(raw, base, snatch_guard);\n    }\n\n    pub(crate) fn insert_barriers_from_scope(\n        raw: &mut dyn hal::DynCommandEncoder,\n        base: &mut Tracker,\n        head: &UsageScope,\n        snatch_guard: &SnatchGuard,\n    ) {\n        profiling::scope!(\"insert_barriers\");\n\n        base.buffers.set_from_usage_scope(&head.buffers);\n        base.textures.set_from_usage_scope(&head.textures);\n\n        Self::drain_barriers(raw, base, snatch_guard);\n    }\n\n    pub(crate) fn drain_barriers(\n        raw: &mut dyn hal::DynCommandEncoder,\n        base: &mut Tracker,\n        snatch_guard: &SnatchGuard,\n    ) {\n        profiling::scope!(\"drain_barriers\");\n\n        let buffer_barriers = base\n            .buffers\n            .drain_transitions(snatch_guard)\n            .collect::<Vec<_>>();\n        let (transitions, textures) = base.textures.drain_transitions(snatch_guard);\n        let texture_barriers = transitions\n            .into_iter()\n            .enumerate()\n            .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))\n            .collect::<Vec<_>>();\n\n        unsafe {\n            raw.transition_buffers(&buffer_barriers);\n            raw.transition_textures(&texture_barriers);\n        }\n    }\n\n    pub(crate) fn insert_barriers_from_device_tracker(\n        raw: &mut dyn hal::DynCommandEncoder,\n        base: &mut DeviceTracker,\n        head: &Tracker,\n        snatch_guard: &SnatchGuard,\n    ) {\n        profiling::scope!(\"insert_barriers_from_device_tracker\");\n\n        let buffer_barriers = base\n            .buffers\n            .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)\n            .collect::<Vec<_>>();\n\n        let texture_barriers = base\n            .textures\n            .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)\n            .collect::<Vec<_>>();\n\n        unsafe {\n            raw.transition_buffers(&buffer_barriers);\n            raw.transition_textures(&texture_barriers);\n        }\n    }\n\n    fn encode_commands(\n        device: &Arc<Device>,\n        cmd_buf_data: &mut CommandBufferMutable,\n    ) -> Result<(), CommandEncoderError> {\n        device.check_is_valid()?;\n        let snatch_guard = device.snatchable_lock.read();\n        let mut debug_scope_depth = 0;\n\n        if cmd_buf_data.encoder.api == EncodingApi::Raw {\n            // Should have panicked on the first call that switched APIs,\n            // but lets be sure.\n            assert!(cmd_buf_data.commands.is_empty());\n        }\n\n        let commands = mem::take(&mut cmd_buf_data.commands);\n\n        #[cfg(feature = \"trace\")]\n        if device.trace.lock().is_some() {\n            cmd_buf_data.trace_commands = Some(\n                commands\n                    .iter()\n                    .map(crate::device::trace::IntoTrace::to_trace)\n                    .collect(),\n            );\n        }\n\n        for command in commands {\n            if matches!(\n                command,\n                ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }\n            ) {\n                // Compute passes and render passes can accept either an\n                // open or closed encoder. This state object holds an\n                // `InnerCommandEncoder`. See the documentation of\n                // [`EncodingState`].\n                let mut state = EncodingState {\n                    device,\n                    raw_encoder: &mut cmd_buf_data.encoder,\n                    tracker: &mut cmd_buf_data.trackers,\n                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,\n                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,\n                    as_actions: &mut cmd_buf_data.as_actions,\n                    temp_resources: &mut cmd_buf_data.temp_resources,\n                    indirect_draw_validation_resources: &mut cmd_buf_data\n                        .indirect_draw_validation_resources,\n                    snatch_guard: &snatch_guard,\n                    debug_scope_depth: &mut debug_scope_depth,\n                };\n\n                match command {\n                    ArcCommand::RunRenderPass {\n                        pass,\n                        color_attachments,\n                        depth_stencil_attachment,\n                        timestamp_writes,\n                        occlusion_query_set,\n                        multiview_mask,\n                    } => {\n                        api_log!(\n                            \"Begin encoding render pass with '{}' label\",\n                            pass.label.as_deref().unwrap_or(\"\")\n                        );\n                        let res = render::encode_render_pass(\n                            &mut state,\n                            pass,\n                            color_attachments,\n                            depth_stencil_attachment,\n                            timestamp_writes,\n                            occlusion_query_set,\n                            multiview_mask,\n                        );\n                        match res.as_ref() {\n                            Err(err) => {\n                                api_log!(\"Finished encoding render pass ({err:?})\")\n                            }\n                            Ok(_) => {\n                                api_log!(\"Finished encoding render pass (success)\")\n                            }\n                        }\n                        res?;\n                    }\n                    ArcCommand::RunComputePass {\n                        pass,\n                        timestamp_writes,\n                    } => {\n                        api_log!(\n                            \"Begin encoding compute pass with '{}' label\",\n                            pass.label.as_deref().unwrap_or(\"\")\n                        );\n                        let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);\n                        match res.as_ref() {\n                            Err(err) => {\n                                api_log!(\"Finished encoding compute pass ({err:?})\")\n                            }\n                            Ok(_) => {\n                                api_log!(\"Finished encoding compute pass (success)\")\n                            }\n                        }\n                        res?;\n                    }\n                    _ => unreachable!(),\n                }\n            } else {\n                // All the other non-pass encoding routines assume the\n                // encoder is open, so open it if necessary. This state\n                // object holds an `&mut dyn hal::DynCommandEncoder`. By\n                // convention, a bare HAL encoder reference in\n                // [`EncodingState`] must always be an open encoder.\n                let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;\n                let mut state = EncodingState {\n                    device,\n                    raw_encoder,\n                    tracker: &mut cmd_buf_data.trackers,\n                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,\n                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,\n                    as_actions: &mut cmd_buf_data.as_actions,\n                    temp_resources: &mut cmd_buf_data.temp_resources,\n                    indirect_draw_validation_resources: &mut cmd_buf_data\n                        .indirect_draw_validation_resources,\n                    snatch_guard: &snatch_guard,\n                    debug_scope_depth: &mut debug_scope_depth,\n                };\n                match command {\n                    ArcCommand::CopyBufferToBuffer {\n                        src,\n                        src_offset,\n                        dst,\n                        dst_offset,\n                        size,\n                    } => {\n                        transfer::copy_buffer_to_buffer(\n                            &mut state, &src, src_offset, &dst, dst_offset, size,\n                        )?;\n                    }\n                    ArcCommand::CopyBufferToTexture { src, dst, size } => {\n                        transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;\n                    }\n                    ArcCommand::CopyTextureToBuffer { src, dst, size } => {\n                        transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;\n                    }\n                    ArcCommand::CopyTextureToTexture { src, dst, size } => {\n                        transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;\n                    }\n                    ArcCommand::ClearBuffer { dst, offset, size } => {\n                        clear::clear_buffer(&mut state, dst, offset, size)?;\n                    }\n                    ArcCommand::ClearTexture {\n                        dst,\n                        subresource_range,\n                    } => {\n                        clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;\n                    }\n                    ArcCommand::WriteTimestamp {\n                        query_set,\n                        query_index,\n                    } => {\n                        query::write_timestamp(&mut state, query_set, query_index)?;\n                    }\n                    ArcCommand::ResolveQuerySet {\n                        query_set,\n                        start_query,\n                        query_count,\n                        destination,\n                        destination_offset,\n                    } => {\n                        query::resolve_query_set(\n                            &mut state,\n                            query_set,\n                            start_query,\n                            query_count,\n                            destination,\n                            destination_offset,\n                        )?;\n                    }\n                    ArcCommand::PushDebugGroup(label) => {\n                        push_debug_group(&mut state, &label)?;\n                    }\n                    ArcCommand::PopDebugGroup => {\n                        pop_debug_group(&mut state)?;\n                    }\n                    ArcCommand::InsertDebugMarker(label) => {\n                        insert_debug_marker(&mut state, &label)?;\n                    }\n                    ArcCommand::BuildAccelerationStructures { blas, tlas } => {\n                        ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;\n                    }\n                    ArcCommand::TransitionResources {\n                        buffer_transitions,\n                        texture_transitions,\n                    } => {\n                        transition_resources::transition_resources(\n                            &mut state,\n                            buffer_transitions,\n                            texture_transitions,\n                        )?;\n                    }\n                    ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {\n                        unreachable!()\n                    }\n                }\n            }\n        }\n\n        if debug_scope_depth > 0 {\n            Err(CommandEncoderError::DebugGroupError(\n                DebugGroupError::MissingPop,\n            ))?;\n        }\n\n        // Close the encoder, unless it was closed already by a render or compute pass.\n        cmd_buf_data.encoder.close_if_open()?;\n\n        // Note: if we want to stop tracking the swapchain texture view,\n        // this is the place to do it.\n\n        Ok(())\n    }\n\n    fn finish(\n        self: &Arc<Self>,\n        desc: &wgt::CommandBufferDescriptor<Label>,\n    ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {\n        let mut cmd_enc_status = self.data.lock();\n\n        let res = match cmd_enc_status.finish() {\n            CommandEncoderStatus::Finished(mut cmd_buf_data) => {\n                match Self::encode_commands(&self.device, &mut cmd_buf_data) {\n                    Ok(()) => Ok(cmd_buf_data),\n                    Err(error) => Err(EncoderErrorState {\n                        error,\n                        #[cfg(feature = \"trace\")]\n                        trace_commands: mem::take(&mut cmd_buf_data.trace_commands),\n                    }),\n                }\n            }\n            CommandEncoderStatus::Error(error_state) => Err(error_state),\n            _ => unreachable!(),\n        };\n\n        let (data, error) = match res {\n            Err(EncoderErrorState {\n                error,\n                #[cfg(feature = \"trace\")]\n                trace_commands,\n            }) => {\n                // Normally, commands are added to the trace when submitted, but\n                // since this command buffer won't be submitted, add it to the\n                // trace now.\n                #[cfg(feature = \"trace\")]\n                if let Some(trace) = self.device.trace.lock().as_mut() {\n                    use alloc::string::ToString;\n\n                    trace.add(crate::device::trace::Action::FailedCommands {\n                        commands: trace_commands,\n                        failed_at_submit: None,\n                        error: error.to_string(),\n                    });\n                }\n\n                if error.is_destroyed_error() {\n                    // Errors related to destroyed resources are not reported until the\n                    // command buffer is submitted.\n                    (make_error_state(error), None)\n                } else {\n                    (make_error_state(error.clone()), Some(error))\n                }\n            }\n\n            Ok(data) => (CommandEncoderStatus::Finished(data), None),\n        };\n\n        let cmd_buf = Arc::new(CommandBuffer {\n            device: self.device.clone(),\n            label: desc.label.to_string(),\n            data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),\n        });\n\n        (cmd_buf, error)\n    }\n}\n\nimpl CommandBuffer {\n    /// Replay commands from a trace.\n    ///\n    /// This is exposed for the `player` crate only. It is not a public API.\n    /// It is not guaranteed to apply all of the validation that the original\n    /// entrypoints provide.\n    #[doc(hidden)]\n    pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {\n        let encoder = device.create_command_encoder(&None).unwrap();\n        let mut cmd_enc_status = encoder.data.lock();\n        cmd_enc_status.replay(commands);\n        drop(cmd_enc_status);\n\n        let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });\n        if let Some(err) = error {\n            panic!(\"CommandEncoder::finish failed: {err}\");\n        }\n\n        cmd_buf\n    }\n\n    pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {\n        use CommandEncoderStatus as St;\n        match mem::replace(\n            &mut *self.data.lock(),\n            make_error_state(EncoderStateError::Submitted),\n        ) {\n            St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),\n            St::Error(EncoderErrorState {\n                #[cfg(feature = \"trace\")]\n                    trace_commands: _,\n                error,\n            }) => Err(error),\n            St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),\n        }\n    }\n}\n\ncrate::impl_resource_type!(CommandBuffer);\ncrate::impl_labeled!(CommandBuffer);\ncrate::impl_parent_device!(CommandBuffer);\ncrate::impl_storage_item!(CommandBuffer);\n\n/// A stream of commands for a render pass or compute pass.\n///\n/// This also contains side tables referred to by certain commands,\n/// like dynamic offsets for [`SetBindGroup`] or string data for\n/// [`InsertDebugMarker`].\n///\n/// Render passes use `BasePass<RenderCommand>`, whereas compute\n/// passes use `BasePass<ComputeCommand>`.\n///\n/// [`SetBindGroup`]: RenderCommand::SetBindGroup\n/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker\n#[doc(hidden)]\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct BasePass<C, E> {\n    pub label: Option<String>,\n\n    /// If the pass is invalid, contains the error that caused the invalidation.\n    ///\n    /// If the pass is valid, this is `None`.\n    ///\n    /// Passes are serialized into traces. but we don't support doing so for\n    /// passes containing errors. These serde attributes allow `E` to be\n    /// `Infallible`.\n    #[cfg_attr(feature = \"serde\", serde(skip, default = \"Option::default\"))]\n    pub error: Option<E>,\n\n    /// The stream of commands.\n    ///\n    /// The commands are moved out of this vector when the pass is ended (i.e.\n    /// at the same time that `parent` is taken out of the\n    /// `ComputePass`/`RenderPass`).\n    pub commands: Vec<C>,\n\n    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.\n    ///\n    /// Each successive `SetBindGroup` consumes the next\n    /// [`num_dynamic_offsets`] values from this list.\n    pub dynamic_offsets: Vec<wgt::DynamicOffset>,\n\n    /// Strings used by debug instructions.\n    ///\n    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]\n    /// instruction consumes the next `len` bytes from this vector.\n    pub string_data: Vec<u8>,\n\n    /// Data used by `SetImmediate` instructions.\n    ///\n    /// See the documentation for [`RenderCommand::SetImmediate`]\n    /// and [`ComputeCommand::SetImmediate`] for details.\n    pub immediates_data: Vec<u32>,\n}\n\nimpl<C: Clone, E: Clone> BasePass<C, E> {\n    fn new(label: &Label) -> Self {\n        Self {\n            label: label.as_deref().map(str::to_owned),\n            error: None,\n            commands: Vec::new(),\n            dynamic_offsets: Vec::new(),\n            string_data: Vec::new(),\n            immediates_data: Vec::new(),\n        }\n    }\n\n    fn new_invalid(label: &Label, err: E) -> Self {\n        Self {\n            label: label.as_deref().map(str::to_owned),\n            error: Some(err),\n            commands: Vec::new(),\n            dynamic_offsets: Vec::new(),\n            string_data: Vec::new(),\n            immediates_data: Vec::new(),\n        }\n    }\n\n    /// Takes the commands from the pass, or returns an error if the pass is\n    /// invalid.\n    ///\n    /// This is called when the pass is ended, at the same time that the\n    /// `parent` member of the `ComputePass` or `RenderPass` containing the pass\n    /// is taken.\n    fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {\n        match self.error.as_ref() {\n            Some(err) => Err(err.clone()),\n            None => Ok(BasePass {\n                label: self.label.clone(),\n                error: None,\n                commands: mem::take(&mut self.commands),\n                dynamic_offsets: mem::take(&mut self.dynamic_offsets),\n                string_data: mem::take(&mut self.string_data),\n                immediates_data: mem::take(&mut self.immediates_data),\n            }),\n        }\n    }\n}\n\n/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and\n/// evaluates to a mutable reference to the [`BasePass`], if the pass is open and\n/// valid.\n///\n/// If the pass is ended or not valid, **returns from the invoking function**,\n/// like the `?` operator.\n///\n/// If the pass is ended (i.e. the application is attempting to record a command\n/// on a finished pass), returns `Err(EncoderStateError::Ended)` from the\n/// invoking function, for immediate propagation as a validation error.\n///\n/// If the pass is open but invalid (i.e. a previous command encountered an\n/// error), returns `Ok(())` from the invoking function. The pass should already\n/// have stored the previous error, which will be transferred to the parent\n/// encoder when the pass is ended, and then raised as a validation error when\n/// `finish()` is called for the parent).\n///\n/// Although in many cases the functionality of `pass_base!` could be achieved\n/// by combining a helper method on the passes with the `pass_try!` macro,\n/// taking the mutable reference to the base pass in a macro avoids borrowing\n/// conflicts when a reference to some other member of the pass struct is\n/// needed simultaneously with the base pass reference.\nmacro_rules! pass_base {\n    ($pass:expr, $scope:expr $(,)?) => {\n        match (&$pass.parent, &$pass.base.error) {\n            // Pass is ended\n            (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),\n            // Pass is invalid\n            (&Some(_), &Some(_)) => return Ok(()),\n            // Pass is open and valid\n            (&Some(_), &None) => &mut $pass.base,\n        }\n    };\n}\npub(crate) use pass_base;\n\n/// Handles the error case in an expression of type `Result<T, E>`.\n///\n/// This macro operates like the `?` operator (or, in early Rust versions, the\n/// `try!` macro, hence the name `pass_try`). **When there is an error, the\n/// macro returns from the invoking function.** However, `Ok(())`, and not the\n/// error itself, is returned. The error is stored in the pass and will later be\n/// transferred to the parent encoder when the pass ends, and then raised as a\n/// validation error when `finish()` is called for the parent.\n///\n/// `pass_try!` also calls [`MapPassErr::map_pass_err`] to annotate the error\n/// with the command being encoded at the time it occurred.\nmacro_rules! pass_try {\n    ($base:expr, $scope:expr, $res:expr $(,)?) => {\n        match $res.map_pass_err($scope) {\n            Ok(val) => val,\n            Err(err) => {\n                $base.error.get_or_insert(err);\n                return Ok(());\n            }\n        }\n    };\n}\npub(crate) use pass_try;\n\n/// Errors related to the state of a command or pass encoder.\n///\n/// The exact behavior of these errors may change based on the resolution of\n/// <https://github.com/gpuweb/gpuweb/issues/5207>.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum EncoderStateError {\n    /// Used internally by wgpu functions to indicate the encoder already\n    /// contained an error. This variant should usually not be seen by users of\n    /// the API, since an effort should be made to provide the caller with a\n    /// more specific reason for the encoder being invalid.\n    #[error(\"Encoder is invalid\")]\n    Invalid,\n\n    /// Returned immediately when an attempt is made to encode a command using\n    /// an encoder that has already finished.\n    #[error(\"Encoding must not have ended\")]\n    Ended,\n\n    /// Returned by a subsequent call to `encoder.finish()`, if there was an\n    /// attempt to open a second pass on the encoder while it was locked for\n    /// a first pass (i.e. the first pass was still open).\n    ///\n    /// Note: only command encoders can be locked (not pass encoders).\n    #[error(\"Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.\")]\n    Locked,\n\n    /// Returned when attempting to end a pass if the parent encoder is not\n    /// locked. This can only happen if pass begin/end calls are mismatched.\n    #[error(\n        \"Encoder is not currently locked. A pass can only be ended while the encoder is locked.\"\n    )]\n    Unlocked,\n\n    /// The command buffer has already been submitted.\n    ///\n    /// Although command encoders and command buffers are distinct WebGPU\n    /// objects, we use `CommandEncoderStatus` for both.\n    #[error(\"This command buffer has already been submitted.\")]\n    Submitted,\n}\n\nimpl WebGpuError for EncoderStateError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            EncoderStateError::Invalid\n            | EncoderStateError::Ended\n            | EncoderStateError::Locked\n            | EncoderStateError::Unlocked\n            | EncoderStateError::Submitted => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CommandEncoderError {\n    #[error(transparent)]\n    State(#[from] EncoderStateError),\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    ResourceUsage(#[from] ResourceUsageCompatibilityError),\n    #[error(transparent)]\n    DebugGroupError(#[from] DebugGroupError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    Transfer(#[from] TransferError),\n    #[error(transparent)]\n    Clear(#[from] ClearError),\n    #[error(transparent)]\n    Query(#[from] QueryError),\n    #[error(transparent)]\n    BuildAccelerationStructure(#[from] BuildAccelerationStructureError),\n    #[error(transparent)]\n    TransitionResources(#[from] TransitionResourcesError),\n    #[error(transparent)]\n    ComputePass(#[from] ComputePassError),\n    #[error(transparent)]\n    RenderPass(#[from] RenderPassError),\n}\n\nimpl CommandEncoderError {\n    fn is_destroyed_error(&self) -> bool {\n        matches!(\n            self,\n            Self::DestroyedResource(_)\n                | Self::Clear(ClearError::DestroyedResource(_))\n                | Self::Query(QueryError::DestroyedResource(_))\n                | Self::ComputePass(ComputePassError {\n                    inner: ComputePassErrorInner::DestroyedResource(_),\n                    ..\n                })\n                | Self::RenderPass(RenderPassError {\n                    inner: RenderPassErrorInner::DestroyedResource(_),\n                    ..\n                })\n                | Self::RenderPass(RenderPassError {\n                    inner: RenderPassErrorInner::RenderCommand(\n                        RenderCommandError::DestroyedResource(_)\n                    ),\n                    ..\n                })\n                | Self::RenderPass(RenderPassError {\n                    inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(\n                        BindingError::DestroyedResource(_)\n                    )),\n                    ..\n                })\n        )\n    }\n}\n\nimpl WebGpuError for CommandEncoderError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::DebugGroupError(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::State(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::Transfer(e) => e.webgpu_error_type(),\n            Self::Clear(e) => e.webgpu_error_type(),\n            Self::Query(e) => e.webgpu_error_type(),\n            Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),\n            Self::TransitionResources(e) => e.webgpu_error_type(),\n            Self::ResourceUsage(e) => e.webgpu_error_type(),\n            Self::ComputePass(e) => e.webgpu_error_type(),\n            Self::RenderPass(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum DebugGroupError {\n    #[error(\"Cannot pop debug group, because number of pushed debug groups is zero\")]\n    InvalidPop,\n    #[error(\"A debug group was not popped before the encoder was finished\")]\n    MissingPop,\n}\n\nimpl WebGpuError for DebugGroupError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::InvalidPop | Self::MissingPop => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum TimestampWritesError {\n    #[error(\n        \"begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed\"\n    )]\n    IndicesEqual { idx: u32 },\n    #[error(\"no begin or end indices were specified for pass timestamp writes, expected at least one to be set\")]\n    IndicesMissing,\n}\n\nimpl WebGpuError for TimestampWritesError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,\n        }\n    }\n}\n\nimpl Global {\n    fn resolve_buffer_id(\n        &self,\n        buffer_id: Id<id::markers::Buffer>,\n    ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {\n        self.hub.buffers.get(buffer_id).get()\n    }\n\n    fn resolve_texture_id(\n        &self,\n        texture_id: Id<id::markers::Texture>,\n    ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {\n        self.hub.textures.get(texture_id).get()\n    }\n\n    fn resolve_query_set(\n        &self,\n        query_set_id: Id<id::markers::QuerySet>,\n    ) -> Result<Arc<QuerySet>, InvalidResourceError> {\n        self.hub.query_sets.get(query_set_id).get()\n    }\n\n    /// Finishes a command encoder, creating a command buffer and returning errors that were\n    /// deferred until now.\n    ///\n    /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can\n    /// include the label when printing deferred errors without having its own copy of the label.\n    /// This is a kludge and should be replaced if we think of a better solution to propagating\n    /// labels.\n    pub fn command_encoder_finish(\n        &self,\n        encoder_id: id::CommandEncoderId,\n        desc: &wgt::CommandBufferDescriptor<Label>,\n        id_in: Option<id::CommandBufferId>,\n    ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {\n        profiling::scope!(\"CommandEncoder::finish\");\n\n        let hub = &self.hub;\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n\n        let (cmd_buf, opt_error) = cmd_enc.finish(desc);\n        let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);\n\n        (\n            cmd_buf_id,\n            opt_error.map(|error| (cmd_enc.label.clone(), error)),\n        )\n    }\n\n    pub fn command_encoder_push_debug_group(\n        &self,\n        encoder_id: id::CommandEncoderId,\n        label: &str,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::push_debug_group\");\n        api_log!(\"CommandEncoder::push_debug_group {label}\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::PushDebugGroup(label.to_owned()))\n        })\n    }\n\n    pub fn command_encoder_insert_debug_marker(\n        &self,\n        encoder_id: id::CommandEncoderId,\n        label: &str,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::insert_debug_marker\");\n        api_log!(\"CommandEncoder::insert_debug_marker {label}\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::InsertDebugMarker(label.to_owned()))\n        })\n    }\n\n    pub fn command_encoder_pop_debug_group(\n        &self,\n        encoder_id: id::CommandEncoderId,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::pop_debug_marker\");\n        api_log!(\"CommandEncoder::pop_debug_group\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data\n            .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })\n    }\n\n    fn validate_pass_timestamp_writes<E>(\n        device: &Device,\n        query_sets: &Storage<Fallible<QuerySet>>,\n        timestamp_writes: &PassTimestampWrites,\n    ) -> Result<ArcPassTimestampWrites, E>\n    where\n        E: From<TimestampWritesError>\n            + From<QueryUseError>\n            + From<DeviceError>\n            + From<MissingFeatures>\n            + From<InvalidResourceError>,\n    {\n        let &PassTimestampWrites {\n            query_set,\n            beginning_of_pass_write_index,\n            end_of_pass_write_index,\n        } = timestamp_writes;\n\n        device.require_features(wgt::Features::TIMESTAMP_QUERY)?;\n\n        let query_set = query_sets.get(query_set).get()?;\n\n        query_set.same_device(device)?;\n\n        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]\n            .into_iter()\n            .flatten()\n        {\n            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;\n        }\n\n        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {\n            if begin == end {\n                return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());\n            }\n        }\n\n        if beginning_of_pass_write_index\n            .or(end_of_pass_write_index)\n            .is_none()\n        {\n            return Err(TimestampWritesError::IndicesMissing.into());\n        }\n\n        Ok(ArcPassTimestampWrites {\n            query_set,\n            beginning_of_pass_write_index,\n            end_of_pass_write_index,\n        })\n    }\n}\n\npub(crate) fn push_debug_group(\n    state: &mut EncodingState,\n    label: &str,\n) -> Result<(), CommandEncoderError> {\n    *state.debug_scope_depth += 1;\n\n    if !state\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        unsafe { state.raw_encoder.begin_debug_marker(label) };\n    }\n\n    Ok(())\n}\n\npub(crate) fn insert_debug_marker(\n    state: &mut EncodingState,\n    label: &str,\n) -> Result<(), CommandEncoderError> {\n    if !state\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        unsafe { state.raw_encoder.insert_debug_marker(label) };\n    }\n\n    Ok(())\n}\n\npub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {\n    if *state.debug_scope_depth == 0 {\n        return Err(DebugGroupError::InvalidPop.into());\n    }\n    *state.debug_scope_depth -= 1;\n\n    if !state\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        unsafe { state.raw_encoder.end_debug_marker() };\n    }\n\n    Ok(())\n}\n\nfn immediates_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)\nwhere\n    PushFn: FnMut(u32, &[u32]),\n{\n    let mut count_words = 0_u32;\n    let size_words = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;\n    while count_words < size_words {\n        let count_bytes = count_words * wgt::IMMEDIATE_DATA_ALIGNMENT;\n        let size_to_write_words =\n            (size_words - count_words).min(IMMEDIATES_CLEAR_ARRAY.len() as u32);\n\n        push_fn(\n            offset + count_bytes,\n            &IMMEDIATES_CLEAR_ARRAY[0..size_to_write_words as usize],\n        );\n\n        count_words += size_to_write_words;\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\nstruct StateChange<T> {\n    last_state: Option<T>,\n}\n\nimpl<T: Copy + PartialEq> StateChange<T> {\n    fn new() -> Self {\n        Self { last_state: None }\n    }\n    fn set_and_check_redundant(&mut self, new_state: T) -> bool {\n        let already_set = self.last_state == Some(new_state);\n        self.last_state = Some(new_state);\n        already_set\n    }\n    fn reset(&mut self) {\n        self.last_state = None;\n    }\n}\n\nimpl<T: Copy + PartialEq> Default for StateChange<T> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[derive(Debug)]\nstruct BindGroupStateChange {\n    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],\n}\n\nimpl BindGroupStateChange {\n    fn new() -> Self {\n        Self {\n            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],\n        }\n    }\n\n    fn set_and_check_redundant(\n        &mut self,\n        bind_group_id: Option<id::BindGroupId>,\n        index: u32,\n        dynamic_offsets: &mut Vec<u32>,\n        offsets: &[wgt::DynamicOffset],\n    ) -> bool {\n        // For now never deduplicate bind groups with dynamic offsets.\n        if offsets.is_empty() {\n            // If this get returns None, that means we're well over the limit,\n            // so let the call through to get a proper error\n            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {\n                // Bail out if we're binding the same bind group.\n                if current_bind_group.set_and_check_redundant(bind_group_id) {\n                    return true;\n                }\n            }\n        } else {\n            // We intentionally remove the memory of this bind group if we have dynamic offsets,\n            // such that if you try to bind this bind group later with _no_ dynamic offsets it\n            // tries to bind it again and gives a proper validation error.\n            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {\n                current_bind_group.reset();\n            }\n            dynamic_offsets.extend_from_slice(offsets);\n        }\n        false\n    }\n    fn reset(&mut self) {\n        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];\n    }\n}\n\nimpl Default for BindGroupStateChange {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Helper to attach [`PassErrorScope`] to errors.\ntrait MapPassErr<T> {\n    fn map_pass_err(self, scope: PassErrorScope) -> T;\n}\n\nimpl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>\nwhere\n    E: MapPassErr<F>,\n{\n    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {\n        self.map_err(|err| err.map_pass_err(scope))\n    }\n}\n\nimpl MapPassErr<PassStateError> for EncoderStateError {\n    fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {\n        PassStateError { scope, inner: self }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub enum DrawKind {\n    Draw,\n    DrawIndirect,\n    MultiDrawIndirect,\n    MultiDrawIndirectCount,\n}\n\n/// The type of draw command(indexed or not, or mesh shader)\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum DrawCommandFamily {\n    Draw,\n    DrawIndexed,\n    DrawMeshTasks,\n}\n\n/// A command that can be recorded in a pass or bundle.\n///\n/// This is used to provide context for errors during command recording.\n/// [`MapPassErr`] is used as a helper to attach a `PassErrorScope` to\n/// an error.\n///\n/// The [`PassErrorScope::Bundle`] and [`PassErrorScope::Pass`] variants\n/// are used when the error occurs during the opening or closing of the\n/// pass or bundle.\n#[derive(Clone, Copy, Debug, Error)]\npub enum PassErrorScope {\n    // TODO: Extract out the 2 error variants below so that we can always\n    // include the ResourceErrorIdent of the pass around all inner errors\n    #[error(\"In a bundle parameter\")]\n    Bundle,\n    #[error(\"In a pass parameter\")]\n    Pass,\n    #[error(\"In a set_bind_group command\")]\n    SetBindGroup,\n    #[error(\"In a set_pipeline command\")]\n    SetPipelineRender,\n    #[error(\"In a set_pipeline command\")]\n    SetPipelineCompute,\n    #[error(\"In a set_immediates command\")]\n    SetImmediate,\n    #[error(\"In a set_vertex_buffer command\")]\n    SetVertexBuffer,\n    #[error(\"In a set_index_buffer command\")]\n    SetIndexBuffer,\n    #[error(\"In a set_blend_constant command\")]\n    SetBlendConstant,\n    #[error(\"In a set_stencil_reference command\")]\n    SetStencilReference,\n    #[error(\"In a set_viewport command\")]\n    SetViewport,\n    #[error(\"In a set_scissor_rect command\")]\n    SetScissorRect,\n    #[error(\"In a draw command, kind: {kind:?}\")]\n    Draw {\n        kind: DrawKind,\n        family: DrawCommandFamily,\n    },\n    #[error(\"In a write_timestamp command\")]\n    WriteTimestamp,\n    #[error(\"In a begin_occlusion_query command\")]\n    BeginOcclusionQuery,\n    #[error(\"In a end_occlusion_query command\")]\n    EndOcclusionQuery,\n    #[error(\"In a begin_pipeline_statistics_query command\")]\n    BeginPipelineStatisticsQuery,\n    #[error(\"In a end_pipeline_statistics_query command\")]\n    EndPipelineStatisticsQuery,\n    #[error(\"In a execute_bundle command\")]\n    ExecuteBundle,\n    #[error(\"In a dispatch command, indirect:{indirect}\")]\n    Dispatch { indirect: bool },\n    #[error(\"In a push_debug_group command\")]\n    PushDebugGroup,\n    #[error(\"In a pop_debug_group command\")]\n    PopDebugGroup,\n    #[error(\"In a insert_debug_marker command\")]\n    InsertDebugMarker,\n}\n\n/// Variant of `EncoderStateError` that includes the pass scope.\n#[derive(Clone, Debug, Error)]\n#[error(\"{scope}\")]\npub struct PassStateError {\n    pub scope: PassErrorScope,\n    #[source]\n    pub(super) inner: EncoderStateError,\n}\n\nimpl WebGpuError for PassStateError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        let Self { scope: _, inner } = self;\n        inner.webgpu_error_type()\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/pass.rs",
    "content": "//! Generic pass functions that both compute and render passes need.\n\nuse crate::binding_model::{BindError, BindGroup, ImmediateUploadError};\nuse crate::command::encoder::EncodingState;\nuse crate::command::{\n    bind::Binder, memory_init::SurfacesInDiscardState, query::QueryResetMap, DebugGroupError,\n    QueryUseError,\n};\nuse crate::device::{Device, DeviceError, MissingFeatures};\nuse crate::pipeline::LateSizedBufferGroup;\nuse crate::resource::{DestroyedResourceError, Labeled, ParentDevice, QuerySet};\nuse crate::track::{ResourceUsageCompatibilityError, UsageScope};\nuse crate::{api_log, binding_model};\nuse alloc::sync::Arc;\nuse alloc::vec::Vec;\nuse core::str;\nuse thiserror::Error;\nuse wgt::error::{ErrorType, WebGpuError};\nuse wgt::DynamicOffset;\n\n#[derive(Clone, Debug, Error)]\n#[error(\n    \"Bind group index {index} is greater than the device's configured `max_bind_groups` limit {max}\"\n)]\npub struct BindGroupIndexOutOfRange {\n    pub index: u32,\n    pub max: u32,\n}\n\n#[derive(Clone, Debug, Error)]\n#[error(\"Pipeline must be set\")]\npub struct MissingPipeline;\n\n#[derive(Clone, Debug, Error)]\n#[error(\"Setting `values_offset` to be `None` is only for internal use in render bundles\")]\npub struct InvalidValuesOffset;\n\nimpl WebGpuError for InvalidValuesOffset {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\npub(crate) struct PassState<'scope, 'snatch_guard, 'cmd_enc> {\n    pub(crate) base: EncodingState<'snatch_guard, 'cmd_enc>,\n\n    /// Immediate texture inits required because of prior discards. Need to\n    /// be inserted before texture reads.\n    pub(crate) pending_discard_init_fixups: SurfacesInDiscardState,\n\n    pub(crate) scope: UsageScope<'scope>,\n\n    pub(crate) binder: Binder,\n\n    pub(crate) temp_offsets: Vec<u32>,\n\n    pub(crate) dynamic_offset_count: usize,\n\n    pub(crate) string_offset: usize,\n}\n\npub(crate) fn set_bind_group<E>(\n    state: &mut PassState,\n    device: &Arc<Device>,\n    dynamic_offsets: &[DynamicOffset],\n    index: u32,\n    num_dynamic_offsets: usize,\n    bind_group: Option<Arc<BindGroup>>,\n    merge_bind_groups: bool,\n) -> Result<(), E>\nwhere\n    E: From<DeviceError>\n        + From<BindGroupIndexOutOfRange>\n        + From<ResourceUsageCompatibilityError>\n        + From<DestroyedResourceError>\n        + From<BindError>,\n{\n    if let Some(ref bind_group) = bind_group {\n        api_log!(\"Pass::set_bind_group {index} {}\", bind_group.error_ident());\n    } else {\n        api_log!(\"Pass::set_bind_group {index} None\");\n    }\n\n    let max_bind_groups = state.base.device.limits.max_bind_groups;\n    if index >= max_bind_groups {\n        return Err(BindGroupIndexOutOfRange {\n            index,\n            max: max_bind_groups,\n        }\n        .into());\n    }\n\n    state.temp_offsets.clear();\n    state.temp_offsets.extend_from_slice(\n        &dynamic_offsets\n            [state.dynamic_offset_count..state.dynamic_offset_count + num_dynamic_offsets],\n    );\n    state.dynamic_offset_count += num_dynamic_offsets;\n\n    if let Some(bind_group) = bind_group {\n        // Add the bind group to the tracker. This is done for both compute and\n        // render passes, and is used to fail submission of the command buffer if\n        // any resource in any of the bind groups has been destroyed, whether or\n        // not the bind group is actually used by the pipeline.\n        let bind_group = state.base.tracker.bind_groups.insert_single(bind_group);\n\n        bind_group.same_device(device)?;\n\n        bind_group.validate_dynamic_bindings(index, &state.temp_offsets)?;\n\n        if merge_bind_groups {\n            // Merge the bind group's resources into the tracker. We only do this\n            // for render passes. For compute passes it is done per dispatch in\n            // [`flush_bindings`].\n            unsafe {\n                state.scope.merge_bind_group(&bind_group.used)?;\n            }\n        }\n        //Note: stateless trackers are not merged: the lifetime reference\n        // is held to the bind group itself.\n\n        state\n            .binder\n            .assign_group(index as usize, bind_group, &state.temp_offsets);\n    } else {\n        if !state.temp_offsets.is_empty() {\n            return Err(BindError::DynamicOffsetCountNotZero {\n                group: index,\n                actual: state.temp_offsets.len(),\n            }\n            .into());\n        }\n\n        state.binder.clear_group(index as usize);\n    };\n\n    Ok(())\n}\n\n/// Implementation of `flush_bindings` for both compute and render passes.\n///\n/// See the compute pass version of `State::flush_bindings` for an explanation\n/// of some differences in handling the two types of passes.\npub(super) fn flush_bindings_helper(state: &mut PassState) -> Result<(), DestroyedResourceError> {\n    let start = state.binder.take_rebind_start_index();\n    let entries = state.binder.list_valid_with_start(start);\n    let pipeline_layout = state.binder.pipeline_layout.as_ref().unwrap();\n\n    for (i, bind_group, dynamic_offsets) in entries {\n        state.base.buffer_memory_init_actions.extend(\n            bind_group.used_buffer_ranges.iter().filter_map(|action| {\n                action\n                    .buffer\n                    .initialization_status\n                    .read()\n                    .check_action(action)\n            }),\n        );\n        for action in bind_group.used_texture_ranges.iter() {\n            state.pending_discard_init_fixups.extend(\n                state\n                    .base\n                    .texture_memory_actions\n                    .register_init_action(action),\n            );\n        }\n\n        let used_resource = bind_group\n            .used\n            .acceleration_structures\n            .into_iter()\n            .map(|tlas| crate::ray_tracing::AsAction::UseTlas(tlas.clone()));\n\n        state.base.as_actions.extend(used_resource);\n\n        let raw_bg = bind_group.try_raw(state.base.snatch_guard)?;\n        unsafe {\n            state.base.raw_encoder.set_bind_group(\n                pipeline_layout.raw(),\n                i as u32,\n                raw_bg,\n                dynamic_offsets,\n            );\n        }\n    }\n\n    Ok(())\n}\n\npub(super) fn change_pipeline_layout<E, F: FnOnce()>(\n    state: &mut PassState,\n    pipeline_layout: &Arc<binding_model::PipelineLayout>,\n    late_sized_buffer_groups: &[LateSizedBufferGroup],\n    f: F,\n) -> Result<(), E>\nwhere\n    E: From<DestroyedResourceError>,\n{\n    if state\n        .binder\n        .change_pipeline_layout(pipeline_layout, late_sized_buffer_groups)\n    {\n        f();\n\n        super::immediates_clear(\n            0,\n            pipeline_layout.immediate_size,\n            |clear_offset, clear_data| unsafe {\n                state.base.raw_encoder.set_immediates(\n                    pipeline_layout.raw(),\n                    clear_offset,\n                    clear_data,\n                );\n            },\n        );\n    }\n    Ok(())\n}\n\npub(crate) fn set_immediates<E, F: FnOnce(&[u32])>(\n    state: &mut PassState,\n    immediates_data: &[u32],\n    offset: u32,\n    size_bytes: u32,\n    values_offset: Option<u32>,\n    f: F,\n) -> Result<(), E>\nwhere\n    E: From<ImmediateUploadError> + From<InvalidValuesOffset> + From<MissingPipeline>,\n{\n    api_log!(\"Pass::set_immediates\");\n\n    let values_offset = values_offset.ok_or(InvalidValuesOffset)?;\n\n    let pipeline_layout = state\n        .binder\n        .pipeline_layout\n        .as_ref()\n        .ok_or(MissingPipeline)?;\n\n    pipeline_layout.validate_immediates_ranges(offset, size_bytes)?;\n\n    let values_offset_usize = usize::try_from(values_offset)\n        .expect(\"`values_offset` is outside the bounds of `usize` (!?)\");\n    if values_offset_usize > immediates_data.len() {\n        return Err(ImmediateUploadError::ValueStartIndexOverrun {\n            start_index: values_offset,\n            data_size: immediates_data.len(),\n        }\n        .into());\n    }\n\n    // NOTE: The `validate_immediates_ranges` call above validates `size_bytes` is aligned.\n    let size_immediate_elements = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;\n    let size_immediate_elements_usize = usize::try_from(size_immediate_elements)\n        .expect(\"`size_immediate_elements` is outside the bounds of `usize` (!?)\");\n    if size_immediate_elements_usize > immediates_data.len() - values_offset_usize {\n        return Err(ImmediateUploadError::ValueEndIndexOverrun {\n            start_index: values_offset,\n            count: size_immediate_elements,\n            data_size: immediates_data.len(),\n        }\n        .into());\n    }\n\n    // NOTE: These additions are will not overflow, because we've validated the range above.\n    let values_end_offset = values_offset_usize + size_immediate_elements_usize;\n    let data_slice = &immediates_data[(values_offset_usize)..values_end_offset];\n\n    f(data_slice);\n\n    unsafe {\n        state\n            .base\n            .raw_encoder\n            .set_immediates(pipeline_layout.raw(), offset, data_slice)\n    }\n    Ok(())\n}\n\npub(crate) fn write_timestamp<E>(\n    state: &mut PassState,\n    device: &Arc<Device>,\n    pending_query_resets: Option<&mut QueryResetMap>,\n    query_set: Arc<QuerySet>,\n    query_index: u32,\n) -> Result<(), E>\nwhere\n    E: From<MissingFeatures> + From<QueryUseError> + From<DeviceError>,\n{\n    api_log!(\n        \"Pass::write_timestamps {query_index} {}\",\n        query_set.error_ident()\n    );\n\n    query_set.same_device(device)?;\n\n    state\n        .base\n        .device\n        .require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES)?;\n\n    let query_set = state.base.tracker.query_sets.insert_single(query_set);\n\n    query_set.validate_and_write_timestamp(\n        state.base.raw_encoder,\n        query_index,\n        pending_query_resets,\n    )?;\n    Ok(())\n}\n\npub(crate) fn push_debug_group(state: &mut PassState, string_data: &[u8], len: usize) {\n    *state.base.debug_scope_depth += 1;\n    if !state\n        .base\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        let label =\n            str::from_utf8(&string_data[state.string_offset..state.string_offset + len]).unwrap();\n\n        api_log!(\"Pass::push_debug_group {label:?}\");\n        unsafe {\n            state.base.raw_encoder.begin_debug_marker(label);\n        }\n    }\n    state.string_offset += len;\n}\n\npub(crate) fn pop_debug_group<E>(state: &mut PassState) -> Result<(), E>\nwhere\n    E: From<DebugGroupError>,\n{\n    api_log!(\"Pass::pop_debug_group\");\n\n    if *state.base.debug_scope_depth == 0 {\n        return Err(DebugGroupError::InvalidPop.into());\n    }\n    *state.base.debug_scope_depth -= 1;\n    if !state\n        .base\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        unsafe {\n            state.base.raw_encoder.end_debug_marker();\n        }\n    }\n    Ok(())\n}\n\npub(crate) fn insert_debug_marker(state: &mut PassState, string_data: &[u8], len: usize) {\n    if !state\n        .base\n        .device\n        .instance_flags\n        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)\n    {\n        let label =\n            str::from_utf8(&string_data[state.string_offset..state.string_offset + len]).unwrap();\n        api_log!(\"Pass::insert_debug_marker {label:?}\");\n        unsafe {\n            state.base.raw_encoder.insert_debug_marker(label);\n        }\n    }\n    state.string_offset += len;\n}\n"
  },
  {
    "path": "wgpu-core/src/command/query.rs",
    "content": "use alloc::{sync::Arc, vec, vec::Vec};\nuse core::{iter, mem};\n\nuse crate::{\n    command::{encoder::EncodingState, ArcCommand, EncoderStateError},\n    device::{Device, DeviceError, MissingFeatures},\n    global::Global,\n    id,\n    init_tracker::MemoryInitKind,\n    resource::{\n        Buffer, DestroyedResourceError, InvalidResourceError, MissingBufferUsageError,\n        ParentDevice, QuerySet, RawResourceAccess, Trackable,\n    },\n    track::{StatelessTracker, TrackerIndex},\n    FastHashMap,\n};\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    BufferAddress,\n};\n\n#[derive(Debug)]\npub(crate) struct QueryResetMap {\n    map: FastHashMap<TrackerIndex, (Vec<bool>, Arc<QuerySet>)>,\n}\nimpl QueryResetMap {\n    pub fn new() -> Self {\n        Self {\n            map: FastHashMap::default(),\n        }\n    }\n\n    pub fn use_query_set(&mut self, query_set: &Arc<QuerySet>, query: u32) -> bool {\n        let vec_pair = self\n            .map\n            .entry(query_set.tracker_index())\n            .or_insert_with(|| {\n                (\n                    vec![false; query_set.desc.count as usize],\n                    query_set.clone(),\n                )\n            });\n\n        mem::replace(&mut vec_pair.0[query as usize], true)\n    }\n\n    pub fn reset_queries(&mut self, raw_encoder: &mut dyn hal::DynCommandEncoder) {\n        for (_, (state, query_set)) in self.map.drain() {\n            debug_assert_eq!(state.len(), query_set.desc.count as usize);\n\n            // Need to find all \"runs\" of values which need resets. If the state vector is:\n            // [false, true, true, false, true], we want to reset [1..3, 4..5]. This minimizes\n            // the amount of resets needed.\n            let mut run_start: Option<u32> = None;\n            for (idx, value) in state.into_iter().chain(iter::once(false)).enumerate() {\n                match (run_start, value) {\n                    // We're inside of a run, do nothing\n                    (Some(..), true) => {}\n                    // We've hit the end of a run, dispatch a reset\n                    (Some(start), false) => {\n                        run_start = None;\n                        unsafe { raw_encoder.reset_queries(query_set.raw(), start..idx as u32) };\n                    }\n                    // We're starting a run\n                    (None, true) => {\n                        run_start = Some(idx as u32);\n                    }\n                    // We're in a run of falses, do nothing.\n                    (None, false) => {}\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum SimplifiedQueryType {\n    Occlusion,\n    Timestamp,\n    PipelineStatistics,\n}\nimpl From<wgt::QueryType> for SimplifiedQueryType {\n    fn from(q: wgt::QueryType) -> Self {\n        match q {\n            wgt::QueryType::Occlusion => SimplifiedQueryType::Occlusion,\n            wgt::QueryType::Timestamp => SimplifiedQueryType::Timestamp,\n            wgt::QueryType::PipelineStatistics(..) => SimplifiedQueryType::PipelineStatistics,\n        }\n    }\n}\n\n/// Error encountered when dealing with queries\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum QueryError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n    #[error(transparent)]\n    MissingFeature(#[from] MissingFeatures),\n    #[error(\"Error encountered while trying to use queries\")]\n    Use(#[from] QueryUseError),\n    #[error(\"Error encountered while trying to resolve a query\")]\n    Resolve(#[from] ResolveError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for QueryError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::EncoderState(e) => e.webgpu_error_type(),\n            Self::Use(e) => e.webgpu_error_type(),\n            Self::Resolve(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeature(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\n/// Error encountered while trying to use queries\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum QueryUseError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Query {query_index} is out of bounds for a query set of size {query_set_size}\")]\n    OutOfBounds {\n        query_index: u32,\n        query_set_size: u32,\n    },\n    #[error(\"Query {query_index} has already been used within the same renderpass. Queries must only be used once per renderpass\")]\n    UsedTwiceInsideRenderpass { query_index: u32 },\n    #[error(\"Query {new_query_index} was started while query {active_query_index} was already active. No more than one statistic or occlusion query may be active at once\")]\n    AlreadyStarted {\n        active_query_index: u32,\n        new_query_index: u32,\n    },\n    #[error(\"Query was stopped while there was no active query\")]\n    AlreadyStopped,\n    #[error(\"A query of type {query_type:?} was started using a query set of type {set_type:?}\")]\n    IncompatibleType {\n        set_type: SimplifiedQueryType,\n        query_type: SimplifiedQueryType,\n    },\n    #[error(\"A query of type {query_type:?} was not ended before the encoder was finished\")]\n    MissingEnd { query_type: SimplifiedQueryType },\n}\n\nimpl WebGpuError for QueryUseError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::OutOfBounds { .. }\n            | Self::UsedTwiceInsideRenderpass { .. }\n            | Self::AlreadyStarted { .. }\n            | Self::AlreadyStopped\n            | Self::IncompatibleType { .. }\n            | Self::MissingEnd { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n/// Error encountered while trying to resolve a query.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ResolveError {\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(\"Resolve buffer offset has to be aligned to `QUERY_RESOLVE_BUFFER_ALIGNMENT\")]\n    BufferOffsetAlignment,\n    #[error(\"Resolving queries {start_query}..{end_query} would overrun the query set of size {query_set_size}\")]\n    QueryOverrun {\n        start_query: u32,\n        end_query: u64,\n        query_set_size: u32,\n    },\n    #[error(\"Resolving queries {start_query}..{end_query} ({stride} byte queries) will end up overrunning the bounds of the destination buffer of size {buffer_size} using offsets {buffer_start_offset}..(<start> + {bytes_used})\")]\n    BufferOverrun {\n        start_query: u32,\n        end_query: u32,\n        stride: u32,\n        buffer_size: BufferAddress,\n        buffer_start_offset: BufferAddress,\n        bytes_used: BufferAddress,\n    },\n}\n\nimpl WebGpuError for ResolveError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::BufferOffsetAlignment\n            | Self::QueryOverrun { .. }\n            | Self::BufferOverrun { .. } => ErrorType::Validation,\n        }\n    }\n}\n\nimpl QuerySet {\n    pub(crate) fn validate_query(\n        self: &Arc<Self>,\n        query_type: SimplifiedQueryType,\n        query_index: u32,\n        reset_state: Option<&mut QueryResetMap>,\n    ) -> Result<(), QueryUseError> {\n        // NOTE: Further code assumes the index is good, so do this first.\n        if query_index >= self.desc.count {\n            return Err(QueryUseError::OutOfBounds {\n                query_index,\n                query_set_size: self.desc.count,\n            });\n        }\n\n        // We need to defer our resets because we are in a renderpass,\n        // add the usage to the reset map.\n        if let Some(reset) = reset_state {\n            let used = reset.use_query_set(self, query_index);\n            if used {\n                return Err(QueryUseError::UsedTwiceInsideRenderpass { query_index });\n            }\n        }\n\n        let simple_set_type = SimplifiedQueryType::from(self.desc.ty);\n        if simple_set_type != query_type {\n            return Err(QueryUseError::IncompatibleType {\n                query_type,\n                set_type: simple_set_type,\n            });\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn validate_and_write_timestamp(\n        self: &Arc<Self>,\n        raw_encoder: &mut dyn hal::DynCommandEncoder,\n        query_index: u32,\n        reset_state: Option<&mut QueryResetMap>,\n    ) -> Result<(), QueryUseError> {\n        let needs_reset = reset_state.is_none();\n        self.validate_query(SimplifiedQueryType::Timestamp, query_index, reset_state)?;\n\n        unsafe {\n            // If we don't have a reset state tracker which can defer resets, we must reset now.\n            if needs_reset {\n                raw_encoder.reset_queries(self.raw(), query_index..(query_index + 1));\n            }\n            raw_encoder.write_timestamp(self.raw(), query_index);\n        }\n\n        Ok(())\n    }\n}\n\npub(super) fn validate_and_begin_occlusion_query(\n    query_set: Arc<QuerySet>,\n    raw_encoder: &mut dyn hal::DynCommandEncoder,\n    tracker: &mut StatelessTracker<QuerySet>,\n    query_index: u32,\n    reset_state: Option<&mut QueryResetMap>,\n    active_query: &mut Option<(Arc<QuerySet>, u32)>,\n) -> Result<(), QueryUseError> {\n    let needs_reset = reset_state.is_none();\n    query_set.validate_query(SimplifiedQueryType::Occlusion, query_index, reset_state)?;\n\n    tracker.insert_single(query_set.clone());\n\n    if let Some((_old, old_idx)) = active_query.take() {\n        return Err(QueryUseError::AlreadyStarted {\n            active_query_index: old_idx,\n            new_query_index: query_index,\n        });\n    }\n    let (query_set, _) = &active_query.insert((query_set, query_index));\n\n    unsafe {\n        // If we don't have a reset state tracker which can defer resets, we must reset now.\n        if needs_reset {\n            raw_encoder.reset_queries(query_set.raw(), query_index..(query_index + 1));\n        }\n        raw_encoder.begin_query(query_set.raw(), query_index);\n    }\n\n    Ok(())\n}\n\npub(super) fn end_occlusion_query(\n    raw_encoder: &mut dyn hal::DynCommandEncoder,\n    active_query: &mut Option<(Arc<QuerySet>, u32)>,\n) -> Result<(), QueryUseError> {\n    if let Some((query_set, query_index)) = active_query.take() {\n        unsafe { raw_encoder.end_query(query_set.raw(), query_index) };\n        Ok(())\n    } else {\n        Err(QueryUseError::AlreadyStopped)\n    }\n}\n\npub(super) fn validate_and_begin_pipeline_statistics_query(\n    query_set: Arc<QuerySet>,\n    raw_encoder: &mut dyn hal::DynCommandEncoder,\n    tracker: &mut StatelessTracker<QuerySet>,\n    device: &Arc<Device>,\n    query_index: u32,\n    reset_state: Option<&mut QueryResetMap>,\n    active_query: &mut Option<(Arc<QuerySet>, u32)>,\n) -> Result<(), QueryUseError> {\n    query_set.same_device(device)?;\n\n    let needs_reset = reset_state.is_none();\n    query_set.validate_query(\n        SimplifiedQueryType::PipelineStatistics,\n        query_index,\n        reset_state,\n    )?;\n\n    tracker.insert_single(query_set.clone());\n\n    if let Some((_old, old_idx)) = active_query.take() {\n        return Err(QueryUseError::AlreadyStarted {\n            active_query_index: old_idx,\n            new_query_index: query_index,\n        });\n    }\n    let (query_set, _) = &active_query.insert((query_set, query_index));\n\n    unsafe {\n        // If we don't have a reset state tracker which can defer resets, we must reset now.\n        if needs_reset {\n            raw_encoder.reset_queries(query_set.raw(), query_index..(query_index + 1));\n        }\n        raw_encoder.begin_query(query_set.raw(), query_index);\n    }\n\n    Ok(())\n}\n\npub(super) fn end_pipeline_statistics_query(\n    raw_encoder: &mut dyn hal::DynCommandEncoder,\n    active_query: &mut Option<(Arc<QuerySet>, u32)>,\n) -> Result<(), QueryUseError> {\n    if let Some((query_set, query_index)) = active_query.take() {\n        unsafe { raw_encoder.end_query(query_set.raw(), query_index) };\n        Ok(())\n    } else {\n        Err(QueryUseError::AlreadyStopped)\n    }\n}\n\nimpl Global {\n    pub fn command_encoder_write_timestamp(\n        &self,\n        command_encoder_id: id::CommandEncoderId,\n        query_set_id: id::QuerySetId,\n        query_index: u32,\n    ) -> Result<(), EncoderStateError> {\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, QueryError> {\n            Ok(ArcCommand::WriteTimestamp {\n                query_set: self.resolve_query_set(query_set_id)?,\n                query_index,\n            })\n        })\n    }\n\n    pub fn command_encoder_resolve_query_set(\n        &self,\n        command_encoder_id: id::CommandEncoderId,\n        query_set_id: id::QuerySetId,\n        start_query: u32,\n        query_count: u32,\n        destination: id::BufferId,\n        destination_offset: BufferAddress,\n    ) -> Result<(), EncoderStateError> {\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, QueryError> {\n            Ok(ArcCommand::ResolveQuerySet {\n                query_set: self.resolve_query_set(query_set_id)?,\n                start_query,\n                query_count,\n                destination: self.resolve_buffer_id(destination)?,\n                destination_offset,\n            })\n        })\n    }\n}\n\npub(super) fn write_timestamp(\n    state: &mut EncodingState,\n    query_set: Arc<QuerySet>,\n    query_index: u32,\n) -> Result<(), QueryError> {\n    state\n        .device\n        .require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)?;\n\n    query_set.same_device(state.device)?;\n\n    query_set.validate_and_write_timestamp(state.raw_encoder, query_index, None)?;\n\n    state.tracker.query_sets.insert_single(query_set);\n\n    Ok(())\n}\n\npub(super) fn resolve_query_set(\n    state: &mut EncodingState,\n    query_set: Arc<QuerySet>,\n    start_query: u32,\n    query_count: u32,\n    dst_buffer: Arc<Buffer>,\n    destination_offset: BufferAddress,\n) -> Result<(), QueryError> {\n    if !destination_offset.is_multiple_of(wgt::QUERY_RESOLVE_BUFFER_ALIGNMENT) {\n        return Err(QueryError::Resolve(ResolveError::BufferOffsetAlignment));\n    }\n\n    query_set.same_device(state.device)?;\n    dst_buffer.same_device(state.device)?;\n\n    dst_buffer.check_destroyed(state.snatch_guard)?;\n\n    let dst_pending = state\n        .tracker\n        .buffers\n        .set_single(&dst_buffer, wgt::BufferUses::COPY_DST);\n    let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard));\n\n    dst_buffer\n        .check_usage(wgt::BufferUsages::QUERY_RESOLVE)\n        .map_err(ResolveError::MissingBufferUsage)?;\n\n    let end_query = u64::from(start_query)\n        .checked_add(u64::from(query_count))\n        .expect(\"`u64` overflow from adding two `u32`s, should be unreachable\");\n    if end_query > u64::from(query_set.desc.count) {\n        return Err(ResolveError::QueryOverrun {\n            start_query,\n            end_query,\n            query_set_size: query_set.desc.count,\n        }\n        .into());\n    }\n    let end_query =\n        u32::try_from(end_query).expect(\"`u32` overflow for `end_query`, which should be `u32`\");\n\n    let elements_per_query = match query_set.desc.ty {\n        wgt::QueryType::Occlusion => 1,\n        wgt::QueryType::PipelineStatistics(ps) => ps.bits().count_ones(),\n        wgt::QueryType::Timestamp => 1,\n    };\n    let stride = elements_per_query * wgt::QUERY_SIZE;\n    let bytes_used: BufferAddress = u64::from(stride)\n        .checked_mul(u64::from(query_count))\n        .expect(\"`stride` * `query_count` overflowed `u32`, should be unreachable\");\n\n    let buffer_start_offset = destination_offset;\n    let buffer_end_offset = buffer_start_offset\n        .checked_add(bytes_used)\n        .filter(|buffer_end_offset| *buffer_end_offset <= dst_buffer.size)\n        .ok_or(ResolveError::BufferOverrun {\n            start_query,\n            end_query,\n            stride,\n            buffer_size: dst_buffer.size,\n            buffer_start_offset,\n            bytes_used,\n        })?;\n\n    // TODO(https://github.com/gfx-rs/wgpu/issues/3993): Need to track initialization state.\n    state\n        .buffer_memory_init_actions\n        .extend(dst_buffer.initialization_status.read().create_action(\n            &dst_buffer,\n            buffer_start_offset..buffer_end_offset,\n            MemoryInitKind::ImplicitlyInitialized,\n        ));\n\n    let raw_dst_buffer = dst_buffer.try_raw(state.snatch_guard)?;\n    unsafe {\n        state.raw_encoder.transition_buffers(dst_barrier.as_slice());\n        state.raw_encoder.copy_query_results(\n            query_set.raw(),\n            start_query..end_query,\n            raw_dst_buffer,\n            destination_offset,\n            wgt::BufferSize::new_unchecked(stride as u64),\n        );\n    }\n\n    if matches!(query_set.desc.ty, wgt::QueryType::Timestamp) {\n        // Timestamp normalization is only needed for timestamps.\n        state.device.timestamp_normalizer.get().unwrap().normalize(\n            state.snatch_guard,\n            state.raw_encoder,\n            &mut state.tracker.buffers,\n            dst_buffer\n                .timestamp_normalization_bind_group\n                .get(state.snatch_guard)\n                .unwrap(),\n            &dst_buffer,\n            destination_offset,\n            query_count,\n        );\n    }\n\n    state.tracker.query_sets.insert_single(query_set);\n\n    Ok(())\n}\n"
  },
  {
    "path": "wgpu-core/src/command/ray_tracing.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\nuse core::{\n    cmp::max,\n    num::NonZeroU64,\n    ops::{Deref, Range},\n};\n\nuse wgt::{math::align_to, BufferUsages, BufferUses, Features};\n\nuse crate::{\n    command::encoder::EncodingState,\n    ray_tracing::{AsAction, AsBuild, TlasBuild, ValidateAsActionsError},\n    resource::InvalidResourceError,\n};\nuse crate::{command::EncoderStateError, device::resource::CommandIndices};\nuse crate::{\n    command::{ArcCommand, ArcReferences, CommandBufferMutable},\n    device::queue::TempResource,\n    global::Global,\n    id::CommandEncoderId,\n    init_tracker::MemoryInitKind,\n    ray_tracing::{\n        ArcBlasBuildEntry, ArcBlasGeometries, ArcBlasTriangleGeometry, ArcTlasInstance,\n        ArcTlasPackage, BlasBuildEntry, BlasGeometries, BuildAccelerationStructureError,\n        OwnedBlasBuildEntry, OwnedTlasPackage, TlasPackage,\n    },\n    resource::{Blas, BlasCompactState, Labeled, StagingBuffer, Tlas},\n    scratch::ScratchBuffer,\n    snatch::SnatchGuard,\n};\nuse crate::{lock::RwLockWriteGuard, resource::RawResourceAccess};\n\nuse crate::id::{BlasId, TlasId};\n\nstruct BlasStore<'a> {\n    blas: Arc<Blas>,\n    entries: hal::AccelerationStructureEntries<'a, dyn hal::DynBuffer>,\n    scratch_buffer_offset: u64,\n}\n\nstruct UnsafeTlasStore<'a> {\n    tlas: Arc<Tlas>,\n    entries: hal::AccelerationStructureEntries<'a, dyn hal::DynBuffer>,\n    scratch_buffer_offset: u64,\n}\n\nstruct TlasStore<'a> {\n    internal: UnsafeTlasStore<'a>,\n    range: Range<usize>,\n}\n\nimpl Global {\n    fn resolve_blas_id(&self, blas_id: BlasId) -> Result<Arc<Blas>, InvalidResourceError> {\n        self.hub.blas_s.get(blas_id).get()\n    }\n\n    fn resolve_tlas_id(&self, tlas_id: TlasId) -> Result<Arc<Tlas>, InvalidResourceError> {\n        self.hub.tlas_s.get(tlas_id).get()\n    }\n\n    pub fn command_encoder_mark_acceleration_structures_built(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        blas_ids: &[BlasId],\n        tlas_ids: &[TlasId],\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::mark_acceleration_structures_built\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n\n        let mut cmd_buf_data = cmd_enc.data.lock();\n        cmd_buf_data.with_buffer(\n            crate::command::EncodingApi::Raw,\n            |cmd_buf_data| -> Result<(), BuildAccelerationStructureError> {\n                let device = &cmd_enc.device;\n                device.check_is_valid()?;\n                device.require_features(Features::EXPERIMENTAL_RAY_QUERY)?;\n\n                let mut build_command = AsBuild::with_capacity(blas_ids.len(), tlas_ids.len());\n\n                for blas in blas_ids {\n                    let blas = hub.blas_s.get(*blas).get()?;\n                    build_command.blas_s_built.push(blas);\n                }\n\n                for tlas in tlas_ids {\n                    let tlas = hub.tlas_s.get(*tlas).get()?;\n                    build_command.tlas_s_built.push(TlasBuild {\n                        tlas,\n                        dependencies: Vec::new(),\n                    });\n                }\n\n                cmd_buf_data.as_actions.push(AsAction::Build(build_command));\n                Ok(())\n            },\n        )\n    }\n\n    pub fn command_encoder_build_acceleration_structures<'a>(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        blas_iter: impl Iterator<Item = BlasBuildEntry<'a>>,\n        tlas_iter: impl Iterator<Item = TlasPackage<'a>>,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::build_acceleration_structures\");\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, BuildAccelerationStructureError> {\n            let blas = blas_iter\n                .map(|blas_entry| {\n                    let geometries = match blas_entry.geometries {\n                        BlasGeometries::TriangleGeometries(triangle_geometries) => {\n                            let tri_geo = triangle_geometries\n                                .map(|tg| {\n                                    Ok(ArcBlasTriangleGeometry {\n                                        size: tg.size.clone(),\n                                        vertex_buffer: self.resolve_buffer_id(tg.vertex_buffer)?,\n                                        index_buffer: tg\n                                            .index_buffer\n                                            .map(|id| self.resolve_buffer_id(id))\n                                            .transpose()?,\n                                        transform_buffer: tg\n                                            .transform_buffer\n                                            .map(|id| self.resolve_buffer_id(id))\n                                            .transpose()?,\n                                        first_vertex: tg.first_vertex,\n                                        vertex_stride: tg.vertex_stride,\n                                        first_index: tg.first_index,\n                                        transform_buffer_offset: tg.transform_buffer_offset,\n                                    })\n                                })\n                                .collect::<Result<_, BuildAccelerationStructureError>>()?;\n                            ArcBlasGeometries::TriangleGeometries(tri_geo)\n                        }\n                    };\n                    Ok(ArcBlasBuildEntry {\n                        blas: self.resolve_blas_id(blas_entry.blas_id)?,\n                        geometries,\n                    })\n                })\n                .collect::<Result<_, BuildAccelerationStructureError>>()?;\n\n            let tlas = tlas_iter\n                .map(|tlas_package| {\n                    let instances = tlas_package\n                        .instances\n                        .map(|instance| {\n                            instance\n                                .as_ref()\n                                .map(|instance| {\n                                    Ok(ArcTlasInstance {\n                                        blas: self.resolve_blas_id(instance.blas_id)?,\n                                        transform: *instance.transform,\n                                        custom_data: instance.custom_data,\n                                        mask: instance.mask,\n                                    })\n                                })\n                                .transpose()\n                        })\n                        .collect::<Result<_, BuildAccelerationStructureError>>()?;\n                    Ok(ArcTlasPackage {\n                        tlas: self.resolve_tlas_id(tlas_package.tlas_id)?,\n                        instances,\n                        lowest_unmodified: tlas_package.lowest_unmodified,\n                    })\n                })\n                .collect::<Result<_, BuildAccelerationStructureError>>()?;\n\n            Ok(ArcCommand::BuildAccelerationStructures { blas, tlas })\n        })\n    }\n}\n\npub(crate) fn build_acceleration_structures(\n    state: &mut EncodingState,\n    blas: Vec<OwnedBlasBuildEntry<ArcReferences>>,\n    tlas: Vec<OwnedTlasPackage<ArcReferences>>,\n) -> Result<(), BuildAccelerationStructureError> {\n    state\n        .device\n        .require_features(Features::EXPERIMENTAL_RAY_QUERY)?;\n\n    let mut build_command = AsBuild::with_capacity(blas.len(), tlas.len());\n    let mut input_barriers = Vec::<hal::BufferBarrier<dyn hal::DynBuffer>>::new();\n    let mut scratch_buffer_blas_size = 0;\n    let mut blas_storage = Vec::with_capacity(blas.len());\n    iter_blas(\n        blas.iter(),\n        &mut build_command,\n        &mut input_barriers,\n        &mut scratch_buffer_blas_size,\n        &mut blas_storage,\n        state,\n    )?;\n\n    let mut scratch_buffer_tlas_size = 0;\n    let mut tlas_storage = Vec::<TlasStore>::with_capacity(tlas.len());\n    let mut instance_buffer_staging_source = Vec::<u8>::new();\n\n    for package in tlas.iter() {\n        let tlas = &package.tlas;\n        state.tracker.tlas_s.insert_single(tlas.clone());\n\n        let scratch_buffer_offset = scratch_buffer_tlas_size;\n        scratch_buffer_tlas_size += align_to(\n            tlas.size_info.build_scratch_size as u32,\n            state.device.alignments.ray_tracing_scratch_buffer_alignment,\n        ) as u64;\n\n        let first_byte_index = instance_buffer_staging_source.len();\n\n        let mut dependencies = Vec::new();\n\n        let mut instance_count = 0;\n        for instance in package.instances.iter().flatten() {\n            if instance.custom_data >= (1u32 << 24u32) {\n                return Err(BuildAccelerationStructureError::TlasInvalidCustomIndex(\n                    tlas.error_ident(),\n                ));\n            }\n            let blas = &instance.blas;\n            state.tracker.blas_s.insert_single(blas.clone());\n\n            instance_buffer_staging_source.extend(state.device.raw().tlas_instance_to_bytes(\n                hal::TlasInstance {\n                    transform: instance.transform,\n                    custom_data: instance.custom_data,\n                    mask: instance.mask,\n                    blas_address: blas.handle,\n                },\n            ));\n\n            if tlas\n                .flags\n                .contains(wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN)\n                && !blas\n                    .flags\n                    .contains(wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN)\n            {\n                return Err(\n                    BuildAccelerationStructureError::TlasDependentMissingVertexReturn(\n                        tlas.error_ident(),\n                        blas.error_ident(),\n                    ),\n                );\n            }\n\n            instance_count += 1;\n\n            dependencies.push(blas.clone());\n        }\n\n        build_command.tlas_s_built.push(TlasBuild {\n            tlas: tlas.clone(),\n            dependencies,\n        });\n\n        if instance_count > tlas.max_instance_count {\n            return Err(BuildAccelerationStructureError::TlasInstanceCountExceeded(\n                tlas.error_ident(),\n                instance_count,\n                tlas.max_instance_count,\n            ));\n        }\n\n        tlas_storage.push(TlasStore {\n            internal: UnsafeTlasStore {\n                tlas: tlas.clone(),\n                entries: hal::AccelerationStructureEntries::Instances(\n                    hal::AccelerationStructureInstances {\n                        buffer: Some(tlas.instance_buffer.as_ref()),\n                        offset: 0,\n                        count: instance_count,\n                    },\n                ),\n                scratch_buffer_offset,\n            },\n            range: first_byte_index..instance_buffer_staging_source.len(),\n        });\n    }\n\n    let Some(scratch_size) =\n        wgt::BufferSize::new(max(scratch_buffer_blas_size, scratch_buffer_tlas_size))\n    else {\n        // if the size is zero there is nothing to build\n        return Ok(());\n    };\n\n    let scratch_buffer = ScratchBuffer::new(state.device, scratch_size)?;\n\n    let scratch_buffer_barrier = hal::BufferBarrier::<dyn hal::DynBuffer> {\n        buffer: scratch_buffer.raw(),\n        usage: hal::StateTransition {\n            from: BufferUses::ACCELERATION_STRUCTURE_SCRATCH,\n            to: BufferUses::ACCELERATION_STRUCTURE_SCRATCH,\n        },\n    };\n\n    let mut tlas_descriptors = Vec::with_capacity(tlas_storage.len());\n\n    for &TlasStore {\n        internal:\n            UnsafeTlasStore {\n                ref tlas,\n                ref entries,\n                ref scratch_buffer_offset,\n            },\n        ..\n    } in &tlas_storage\n    {\n        if tlas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate {\n            log::warn!(\"build_acceleration_structures called with PreferUpdate, but only rebuild is implemented\");\n        }\n        tlas_descriptors.push(hal::BuildAccelerationStructureDescriptor {\n            entries,\n            mode: hal::AccelerationStructureBuildMode::Build,\n            flags: tlas.flags,\n            source_acceleration_structure: None,\n            destination_acceleration_structure: tlas.try_raw(state.snatch_guard)?,\n            scratch_buffer: scratch_buffer.raw(),\n            scratch_buffer_offset: *scratch_buffer_offset,\n        })\n    }\n\n    let blas_present = !blas_storage.is_empty();\n    let tlas_present = !tlas_storage.is_empty();\n\n    let raw_encoder = &mut state.raw_encoder;\n\n    let mut blas_s_compactable = Vec::new();\n    let mut descriptors = Vec::with_capacity(blas.len());\n\n    for storage in &blas_storage {\n        descriptors.push(map_blas(\n            storage,\n            scratch_buffer.raw(),\n            state.snatch_guard,\n            &mut blas_s_compactable,\n        )?);\n    }\n\n    build_blas(\n        *raw_encoder,\n        blas_present,\n        tlas_present,\n        input_barriers,\n        &descriptors,\n        scratch_buffer_barrier,\n        blas_s_compactable,\n    );\n\n    if tlas_present {\n        let staging_buffer = if !instance_buffer_staging_source.is_empty() {\n            let mut staging_buffer = StagingBuffer::new(\n                state.device,\n                wgt::BufferSize::new(instance_buffer_staging_source.len() as u64).unwrap(),\n            )?;\n            staging_buffer.write(&instance_buffer_staging_source);\n            let flushed = staging_buffer.flush();\n            Some(flushed)\n        } else {\n            None\n        };\n\n        unsafe {\n            if let Some(ref staging_buffer) = staging_buffer {\n                raw_encoder.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {\n                    buffer: staging_buffer.raw(),\n                    usage: hal::StateTransition {\n                        from: BufferUses::MAP_WRITE,\n                        to: BufferUses::COPY_SRC,\n                    },\n                }]);\n            }\n        }\n\n        let mut instance_buffer_barriers = Vec::new();\n        for &TlasStore {\n            internal: UnsafeTlasStore { ref tlas, .. },\n            ref range,\n        } in &tlas_storage\n        {\n            let size = match wgt::BufferSize::new((range.end - range.start) as u64) {\n                None => continue,\n                Some(size) => size,\n            };\n            instance_buffer_barriers.push(hal::BufferBarrier::<dyn hal::DynBuffer> {\n                buffer: tlas.instance_buffer.as_ref(),\n                usage: hal::StateTransition {\n                    from: BufferUses::COPY_DST,\n                    to: BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                },\n            });\n            unsafe {\n                raw_encoder.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {\n                    buffer: tlas.instance_buffer.as_ref(),\n                    usage: hal::StateTransition {\n                        from: BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                        to: BufferUses::COPY_DST,\n                    },\n                }]);\n                let temp = hal::BufferCopy {\n                    src_offset: range.start as u64,\n                    dst_offset: 0,\n                    size,\n                };\n                raw_encoder.copy_buffer_to_buffer(\n                    staging_buffer.as_ref().unwrap().raw(),\n                    tlas.instance_buffer.as_ref(),\n                    &[temp],\n                );\n            }\n        }\n\n        unsafe {\n            raw_encoder.transition_buffers(&instance_buffer_barriers);\n\n            raw_encoder.build_acceleration_structures(&tlas_descriptors);\n\n            raw_encoder.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier {\n                usage: hal::StateTransition {\n                    from: hal::AccelerationStructureUses::BUILD_OUTPUT,\n                    to: hal::AccelerationStructureUses::SHADER_INPUT,\n                },\n            });\n        }\n\n        if let Some(staging_buffer) = staging_buffer {\n            state\n                .temp_resources\n                .push(TempResource::StagingBuffer(staging_buffer));\n        }\n    }\n\n    state\n        .temp_resources\n        .push(TempResource::ScratchBuffer(scratch_buffer));\n\n    state.as_actions.push(AsAction::Build(build_command));\n\n    Ok(())\n}\n\nimpl CommandBufferMutable {\n    pub(crate) fn validate_acceleration_structure_actions(\n        &self,\n        snatch_guard: &SnatchGuard,\n        command_index_guard: &mut RwLockWriteGuard<CommandIndices>,\n    ) -> Result<(), ValidateAsActionsError> {\n        profiling::scope!(\"CommandEncoder::[submission]::validate_as_actions\");\n        for action in &self.as_actions {\n            match action {\n                AsAction::Build(build) => {\n                    let build_command_index = NonZeroU64::new(\n                        command_index_guard.next_acceleration_structure_build_command_index,\n                    )\n                    .unwrap();\n\n                    command_index_guard.next_acceleration_structure_build_command_index += 1;\n                    for blas in build.blas_s_built.iter() {\n                        let mut state_lock = blas.compacted_state.lock();\n                        *state_lock = match *state_lock {\n                            BlasCompactState::Compacted => {\n                                unreachable!(\"Should be validated out in build.\")\n                            }\n                            // Reset the compacted state to idle. This means any prepares, before mapping their\n                            // internal buffer, will terminate.\n                            _ => BlasCompactState::Idle,\n                        };\n                        *blas.built_index.write() = Some(build_command_index);\n                    }\n\n                    for tlas_build in build.tlas_s_built.iter() {\n                        for blas in &tlas_build.dependencies {\n                            if blas.built_index.read().is_none() {\n                                return Err(ValidateAsActionsError::UsedUnbuiltBlas(\n                                    blas.error_ident(),\n                                    tlas_build.tlas.error_ident(),\n                                ));\n                            }\n                        }\n                        *tlas_build.tlas.built_index.write() = Some(build_command_index);\n                        tlas_build\n                            .tlas\n                            .dependencies\n                            .write()\n                            .clone_from(&tlas_build.dependencies)\n                    }\n                }\n                AsAction::UseTlas(tlas) => {\n                    let tlas_build_index = tlas.built_index.read();\n                    let dependencies = tlas.dependencies.read();\n\n                    if (*tlas_build_index).is_none() {\n                        return Err(ValidateAsActionsError::UsedUnbuiltTlas(tlas.error_ident()));\n                    }\n                    for blas in dependencies.deref() {\n                        let blas_build_index = *blas.built_index.read();\n                        if blas_build_index.is_none() {\n                            return Err(ValidateAsActionsError::UsedUnbuiltBlas(\n                                tlas.error_ident(),\n                                blas.error_ident(),\n                            ));\n                        }\n                        if blas_build_index.unwrap() > tlas_build_index.unwrap() {\n                            return Err(ValidateAsActionsError::BlasNewerThenTlas(\n                                blas.error_ident(),\n                                tlas.error_ident(),\n                            ));\n                        }\n                        blas.try_raw(snatch_guard)?;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub(crate) fn set_acceleration_structure_dependencies(&self, snatch_guard: &SnatchGuard) {\n        profiling::scope!(\"CommandEncoder::[submission]::set_acceleration_structure_dependencies\");\n        let tlas_dependencies_locks: Vec<_> = self\n            .as_actions\n            .iter()\n            .filter_map(|action| {\n                if let AsAction::UseTlas(tlas) = action {\n                    Some(tlas.dependencies.read())\n                } else {\n                    None\n                }\n            })\n            .collect();\n        let mut tlas_dependencies_lock_iter = tlas_dependencies_locks.iter();\n        let mut dependencies = Vec::new();\n        for action in &self.as_actions {\n            match action {\n                AsAction::Build(build) => {\n                    for tlas_build in build.tlas_s_built.iter() {\n                        for dependency in &tlas_build.dependencies {\n                            if let Some(dependency) = dependency.raw(snatch_guard) {\n                                dependencies.push(dependency);\n                            }\n                        }\n                    }\n                }\n                AsAction::UseTlas(_tlas) => {\n                    let tlas_dependencies = tlas_dependencies_lock_iter.next().unwrap(); // _tlas.dependencies.read();\n                    for dependency in tlas_dependencies.iter() {\n                        if let Some(dependency) = dependency.raw(snatch_guard) {\n                            dependencies.push(dependency);\n                        }\n                    }\n                }\n            }\n        }\n        if !dependencies.is_empty() {\n            unsafe {\n                self.encoder\n                    .raw\n                    .set_acceleration_structure_dependencies(&self.encoder.list, &dependencies);\n            }\n        }\n    }\n}\n\n///iterates over the blas iterator, and it's geometry, pushing the buffers into a storage vector (and also some validation).\nfn iter_blas<'snatch_guard: 'buffers, 'buffers>(\n    blas_iter: impl Iterator<Item = &'buffers OwnedBlasBuildEntry<ArcReferences>>,\n    build_command: &mut AsBuild,\n    input_barriers: &mut Vec<hal::BufferBarrier<'buffers, dyn hal::DynBuffer>>,\n    scratch_buffer_blas_size: &mut u64,\n    blas_storage: &mut Vec<BlasStore<'buffers>>,\n    state: &mut EncodingState<'snatch_guard, '_>,\n) -> Result<(), BuildAccelerationStructureError> {\n    for entry in blas_iter {\n        let blas = &entry.blas;\n        state.tracker.blas_s.insert_single(blas.clone());\n\n        build_command.blas_s_built.push(blas.clone());\n\n        match &entry.geometries {\n            ArcBlasGeometries::TriangleGeometries(triangle_geometries) => {\n                let mut triangle_entries =\n                    Vec::<hal::AccelerationStructureTriangles<dyn hal::DynBuffer>>::new();\n\n                for (i, mesh) in triangle_geometries.iter().enumerate() {\n                    let size_desc = match &blas.sizes {\n                        wgt::BlasGeometrySizeDescriptors::Triangles { descriptors } => descriptors,\n                    };\n                    if i >= size_desc.len() {\n                        return Err(BuildAccelerationStructureError::IncompatibleBlasBuildSizes(\n                            blas.error_ident(),\n                        ));\n                    }\n                    let size_desc = &size_desc[i];\n\n                    if size_desc.flags != mesh.size.flags {\n                        return Err(BuildAccelerationStructureError::IncompatibleBlasFlags(\n                            blas.error_ident(),\n                            size_desc.flags,\n                            mesh.size.flags,\n                        ));\n                    }\n\n                    if size_desc.vertex_count < mesh.size.vertex_count {\n                        return Err(\n                            BuildAccelerationStructureError::IncompatibleBlasVertexCount(\n                                blas.error_ident(),\n                                size_desc.vertex_count,\n                                mesh.size.vertex_count,\n                            ),\n                        );\n                    }\n\n                    if size_desc.vertex_format != mesh.size.vertex_format {\n                        return Err(BuildAccelerationStructureError::DifferentBlasVertexFormats(\n                            blas.error_ident(),\n                            size_desc.vertex_format,\n                            mesh.size.vertex_format,\n                        ));\n                    }\n\n                    if size_desc\n                        .vertex_format\n                        .min_acceleration_structure_vertex_stride()\n                        > mesh.vertex_stride\n                    {\n                        return Err(BuildAccelerationStructureError::VertexStrideTooSmall(\n                            blas.error_ident(),\n                            size_desc\n                                .vertex_format\n                                .min_acceleration_structure_vertex_stride(),\n                            mesh.vertex_stride,\n                        ));\n                    }\n\n                    if mesh.vertex_stride\n                        % size_desc\n                            .vertex_format\n                            .acceleration_structure_stride_alignment()\n                        != 0\n                    {\n                        return Err(BuildAccelerationStructureError::VertexStrideUnaligned(\n                            blas.error_ident(),\n                            size_desc\n                                .vertex_format\n                                .acceleration_structure_stride_alignment(),\n                            mesh.vertex_stride,\n                        ));\n                    }\n\n                    match (size_desc.index_count, mesh.size.index_count) {\n                        (Some(_), None) | (None, Some(_)) => {\n                            return Err(\n                                BuildAccelerationStructureError::BlasIndexCountProvidedMismatch(\n                                    blas.error_ident(),\n                                ),\n                            )\n                        }\n                        (Some(create), Some(build)) if create < build => {\n                            return Err(\n                                BuildAccelerationStructureError::IncompatibleBlasIndexCount(\n                                    blas.error_ident(),\n                                    create,\n                                    build,\n                                ),\n                            )\n                        }\n                        _ => {}\n                    }\n\n                    if size_desc.index_format != mesh.size.index_format {\n                        return Err(BuildAccelerationStructureError::DifferentBlasIndexFormats(\n                            blas.error_ident(),\n                            size_desc.index_format,\n                            mesh.size.index_format,\n                        ));\n                    }\n\n                    if size_desc.index_count.is_some() && mesh.index_buffer.is_none() {\n                        return Err(BuildAccelerationStructureError::MissingIndexBuffer(\n                            blas.error_ident(),\n                        ));\n                    }\n                    let vertex_buffer = mesh.vertex_buffer.clone();\n                    let vertex_pending = state.tracker.buffers.set_single(\n                        &vertex_buffer,\n                        BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                    );\n                    let vertex_buffer = {\n                        let vertex_raw = mesh.vertex_buffer.as_ref().try_raw(state.snatch_guard)?;\n                        let vertex_buffer = &mesh.vertex_buffer;\n                        vertex_buffer.check_usage(BufferUsages::BLAS_INPUT)?;\n\n                        if let Some(barrier) = vertex_pending.map(|pending| {\n                            pending.into_hal(vertex_buffer.as_ref(), state.snatch_guard)\n                        }) {\n                            input_barriers.push(barrier);\n                        }\n                        if vertex_buffer.size\n                            < (mesh.size.vertex_count + mesh.first_vertex) as u64\n                                * mesh.vertex_stride\n                        {\n                            return Err(BuildAccelerationStructureError::InsufficientBufferSize(\n                                vertex_buffer.error_ident(),\n                                vertex_buffer.size,\n                                (mesh.size.vertex_count + mesh.first_vertex) as u64\n                                    * mesh.vertex_stride,\n                            ));\n                        }\n                        let vertex_buffer_offset = mesh.first_vertex as u64 * mesh.vertex_stride;\n                        state.buffer_memory_init_actions.extend(\n                            vertex_buffer.initialization_status.read().create_action(\n                                vertex_buffer,\n                                vertex_buffer_offset\n                                    ..(vertex_buffer_offset\n                                        + mesh.size.vertex_count as u64 * mesh.vertex_stride),\n                                MemoryInitKind::NeedsInitializedMemory,\n                            ),\n                        );\n                        vertex_raw\n                    };\n                    let index_buffer = if let Some(ref index_buffer) = mesh.index_buffer {\n                        if mesh.first_index.is_none()\n                            || mesh.size.index_count.is_none()\n                            || mesh.size.index_count.is_none()\n                        {\n                            return Err(BuildAccelerationStructureError::MissingAssociatedData(\n                                index_buffer.error_ident(),\n                            ));\n                        }\n                        let index_pending = state.tracker.buffers.set_single(\n                            index_buffer,\n                            BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                        );\n                        let index_raw = index_buffer.try_raw(state.snatch_guard)?;\n                        index_buffer.check_usage(BufferUsages::BLAS_INPUT)?;\n\n                        if let Some(barrier) = index_pending.map(|pending| {\n                            pending.into_hal(index_buffer.as_ref(), state.snatch_guard)\n                        }) {\n                            input_barriers.push(barrier);\n                        }\n                        let index_stride = mesh.size.index_format.unwrap().byte_size() as u64;\n                        let offset = mesh.first_index.unwrap() as u64 * index_stride;\n                        let index_buffer_size =\n                            mesh.size.index_count.unwrap() as u64 * index_stride;\n\n                        if mesh.size.index_count.unwrap() % 3 != 0 {\n                            return Err(BuildAccelerationStructureError::InvalidIndexCount(\n                                index_buffer.error_ident(),\n                                mesh.size.index_count.unwrap(),\n                            ));\n                        }\n                        if index_buffer.size\n                            < mesh.size.index_count.unwrap() as u64 * index_stride + offset\n                        {\n                            return Err(BuildAccelerationStructureError::InsufficientBufferSize(\n                                index_buffer.error_ident(),\n                                index_buffer.size,\n                                mesh.size.index_count.unwrap() as u64 * index_stride + offset,\n                            ));\n                        }\n\n                        state.buffer_memory_init_actions.extend(\n                            index_buffer.initialization_status.read().create_action(\n                                index_buffer,\n                                offset..(offset + index_buffer_size),\n                                MemoryInitKind::NeedsInitializedMemory,\n                            ),\n                        );\n                        Some(index_raw)\n                    } else {\n                        None\n                    };\n                    let transform_buffer = if let Some(ref transform_buffer) = mesh.transform_buffer\n                    {\n                        if !blas\n                            .flags\n                            .contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)\n                        {\n                            return Err(BuildAccelerationStructureError::UseTransformMissing(\n                                blas.error_ident(),\n                            ));\n                        }\n                        if mesh.transform_buffer_offset.is_none() {\n                            return Err(BuildAccelerationStructureError::MissingAssociatedData(\n                                transform_buffer.error_ident(),\n                            ));\n                        }\n                        let transform_pending = state.tracker.buffers.set_single(\n                            transform_buffer,\n                            BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                        );\n                        if mesh.transform_buffer_offset.is_none() {\n                            return Err(BuildAccelerationStructureError::MissingAssociatedData(\n                                transform_buffer.error_ident(),\n                            ));\n                        }\n                        let transform_raw = transform_buffer.try_raw(state.snatch_guard)?;\n                        transform_buffer.check_usage(BufferUsages::BLAS_INPUT)?;\n\n                        if let Some(barrier) = transform_pending.map(|pending| {\n                            pending.into_hal(transform_buffer.as_ref(), state.snatch_guard)\n                        }) {\n                            input_barriers.push(barrier);\n                        }\n\n                        let offset = mesh.transform_buffer_offset.unwrap();\n\n                        if offset % wgt::TRANSFORM_BUFFER_ALIGNMENT != 0 {\n                            return Err(\n                                BuildAccelerationStructureError::UnalignedTransformBufferOffset(\n                                    transform_buffer.error_ident(),\n                                ),\n                            );\n                        }\n                        if transform_buffer.size < 48 + offset {\n                            return Err(BuildAccelerationStructureError::InsufficientBufferSize(\n                                transform_buffer.error_ident(),\n                                transform_buffer.size,\n                                48 + offset,\n                            ));\n                        }\n                        state.buffer_memory_init_actions.extend(\n                            transform_buffer.initialization_status.read().create_action(\n                                transform_buffer,\n                                offset..(offset + 48),\n                                MemoryInitKind::NeedsInitializedMemory,\n                            ),\n                        );\n                        Some(transform_raw)\n                    } else {\n                        if blas\n                            .flags\n                            .contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)\n                        {\n                            return Err(BuildAccelerationStructureError::TransformMissing(\n                                blas.error_ident(),\n                            ));\n                        }\n                        None\n                    };\n\n                    let triangles = hal::AccelerationStructureTriangles {\n                        vertex_buffer: Some(vertex_buffer),\n                        vertex_format: mesh.size.vertex_format,\n                        first_vertex: mesh.first_vertex,\n                        vertex_count: mesh.size.vertex_count,\n                        vertex_stride: mesh.vertex_stride,\n                        indices: index_buffer.map(|index_buffer| {\n                            let index_stride = mesh.size.index_format.unwrap().byte_size() as u32;\n                            hal::AccelerationStructureTriangleIndices::<dyn hal::DynBuffer> {\n                                format: mesh.size.index_format.unwrap(),\n                                buffer: Some(index_buffer),\n                                offset: mesh.first_index.unwrap() * index_stride,\n                                count: mesh.size.index_count.unwrap(),\n                            }\n                        }),\n                        transform: transform_buffer.map(|transform_buffer| {\n                            hal::AccelerationStructureTriangleTransform {\n                                buffer: transform_buffer,\n                                offset: mesh.transform_buffer_offset.unwrap() as u32,\n                            }\n                        }),\n                        flags: mesh.size.flags,\n                    };\n                    triangle_entries.push(triangles);\n                }\n\n                {\n                    let scratch_buffer_offset = *scratch_buffer_blas_size;\n                    *scratch_buffer_blas_size += align_to(\n                        blas.size_info.build_scratch_size as u32,\n                        state.device.alignments.ray_tracing_scratch_buffer_alignment,\n                    ) as u64;\n\n                    blas_storage.push(BlasStore {\n                        blas: blas.clone(),\n                        entries: hal::AccelerationStructureEntries::Triangles(triangle_entries),\n                        scratch_buffer_offset,\n                    });\n                }\n            }\n        }\n    }\n    Ok(())\n}\n\nfn map_blas<'a>(\n    storage: &'a BlasStore<'_>,\n    scratch_buffer: &'a dyn hal::DynBuffer,\n    snatch_guard: &'a SnatchGuard,\n    blases_compactable: &mut Vec<(\n        &'a dyn hal::DynBuffer,\n        &'a dyn hal::DynAccelerationStructure,\n    )>,\n) -> Result<\n    hal::BuildAccelerationStructureDescriptor<\n        'a,\n        dyn hal::DynBuffer,\n        dyn hal::DynAccelerationStructure,\n    >,\n    BuildAccelerationStructureError,\n> {\n    let BlasStore {\n        blas,\n        entries,\n        scratch_buffer_offset,\n    } = storage;\n    if blas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate {\n        log::debug!(\"only rebuild implemented\")\n    }\n    let raw = blas.try_raw(snatch_guard)?;\n\n    let state_lock = blas.compacted_state.lock();\n    if let BlasCompactState::Compacted = *state_lock {\n        return Err(BuildAccelerationStructureError::CompactedBlas(\n            blas.error_ident(),\n        ));\n    }\n\n    if blas\n        .flags\n        .contains(wgpu_types::AccelerationStructureFlags::ALLOW_COMPACTION)\n    {\n        blases_compactable.push((blas.compaction_buffer.as_ref().unwrap().as_ref(), raw));\n    }\n    Ok(hal::BuildAccelerationStructureDescriptor {\n        entries,\n        mode: hal::AccelerationStructureBuildMode::Build,\n        flags: blas.flags,\n        source_acceleration_structure: None,\n        destination_acceleration_structure: raw,\n        scratch_buffer,\n        scratch_buffer_offset: *scratch_buffer_offset,\n    })\n}\n\nfn build_blas<'a>(\n    cmd_buf_raw: &mut dyn hal::DynCommandEncoder,\n    blas_present: bool,\n    tlas_present: bool,\n    input_barriers: Vec<hal::BufferBarrier<dyn hal::DynBuffer>>,\n    blas_descriptors: &[hal::BuildAccelerationStructureDescriptor<\n        'a,\n        dyn hal::DynBuffer,\n        dyn hal::DynAccelerationStructure,\n    >],\n    scratch_buffer_barrier: hal::BufferBarrier<dyn hal::DynBuffer>,\n    blas_s_for_compaction: Vec<(\n        &'a dyn hal::DynBuffer,\n        &'a dyn hal::DynAccelerationStructure,\n    )>,\n) {\n    unsafe {\n        cmd_buf_raw.transition_buffers(&input_barriers);\n    }\n\n    if blas_present {\n        unsafe {\n            cmd_buf_raw.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier {\n                usage: hal::StateTransition {\n                    from: hal::AccelerationStructureUses::BUILD_INPUT,\n                    to: hal::AccelerationStructureUses::BUILD_OUTPUT,\n                },\n            });\n\n            cmd_buf_raw.build_acceleration_structures(blas_descriptors);\n        }\n    }\n\n    if blas_present && tlas_present {\n        unsafe {\n            cmd_buf_raw.transition_buffers(&[scratch_buffer_barrier]);\n        }\n    }\n\n    let mut source_usage = hal::AccelerationStructureUses::empty();\n    let mut destination_usage = hal::AccelerationStructureUses::empty();\n    for &(buf, blas) in blas_s_for_compaction.iter() {\n        unsafe {\n            cmd_buf_raw.transition_buffers(&[hal::BufferBarrier {\n                buffer: buf,\n                usage: hal::StateTransition {\n                    from: BufferUses::ACCELERATION_STRUCTURE_QUERY,\n                    to: BufferUses::ACCELERATION_STRUCTURE_QUERY,\n                },\n            }])\n        }\n        unsafe { cmd_buf_raw.read_acceleration_structure_compact_size(blas, buf) }\n        destination_usage |= hal::AccelerationStructureUses::COPY_SRC;\n    }\n\n    if blas_present {\n        source_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT;\n        destination_usage |= hal::AccelerationStructureUses::BUILD_INPUT\n    }\n    if tlas_present {\n        source_usage |= hal::AccelerationStructureUses::SHADER_INPUT;\n        destination_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT;\n    }\n    unsafe {\n        cmd_buf_raw.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier {\n            usage: hal::StateTransition {\n                from: source_usage,\n                to: destination_usage,\n            },\n        });\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/render.rs",
    "content": "use alloc::{borrow::Cow, sync::Arc, vec::Vec};\nuse core::{convert::Infallible, fmt, num::NonZeroU32, ops::Range, str};\nuse smallvec::SmallVec;\n\nuse arrayvec::ArrayVec;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, TextureSelector,\n    TextureUsages, TextureViewDimension, VertexStepMode,\n};\n\nuse crate::{\n    api_log,\n    binding_model::{BindError, ImmediateUploadError},\n    command::{\n        bind::Binder,\n        memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState, TextureSurfaceDiscard},\n        pass::{self, flush_bindings_helper},\n        pass_base, pass_try,\n        query::{\n            end_occlusion_query, end_pipeline_statistics_query, validate_and_begin_occlusion_query,\n            validate_and_begin_pipeline_statistics_query, QueryResetMap,\n        },\n        render_command::ArcRenderCommand,\n        ArcCommand, ArcPassTimestampWrites, BasePass, BindGroupStateChange,\n        CommandBufferTextureMemoryActions, CommandEncoder, CommandEncoderError, DebugGroupError,\n        DrawCommandFamily, DrawError, DrawKind, EncoderStateError, EncodingState, ExecutionError,\n        InnerCommandEncoder, MapPassErr, PassErrorScope, PassStateError, PassTimestampWrites,\n        QueryUseError, Rect, RenderCommandError, StateChange, TimestampWritesError,\n    },\n    device::{\n        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,\n        RenderPassCompatibilityError, RenderPassContext,\n    },\n    global::Global,\n    hal_label, id,\n    init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},\n    pipeline::{PipelineFlags, RenderPipeline, VertexStep},\n    resource::{\n        DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,\n        MissingTextureUsageError, ParentDevice, QuerySet, RawResourceAccess, ResourceErrorIdent,\n        Texture, TextureView, TextureViewNotRenderableReason,\n    },\n    snatch::SnatchGuard,\n    track::{ResourceUsageCompatibilityError, Tracker, UsageScope},\n    validation, Label,\n};\n\n#[cfg(feature = \"serde\")]\nuse serde::Deserialize;\n#[cfg(feature = \"serde\")]\nuse serde::Serialize;\n\npub use wgt::{LoadOp, StoreOp};\n\nfn load_hal_ops<V>(load: LoadOp<V>) -> hal::AttachmentOps {\n    match load {\n        LoadOp::Load => hal::AttachmentOps::LOAD,\n        LoadOp::Clear(_) => hal::AttachmentOps::LOAD_CLEAR,\n        LoadOp::DontCare(_) => hal::AttachmentOps::LOAD_DONT_CARE,\n    }\n}\n\nfn store_hal_ops(store: StoreOp) -> hal::AttachmentOps {\n    match store {\n        StoreOp::Store => hal::AttachmentOps::STORE,\n        StoreOp::Discard => hal::AttachmentOps::STORE_DISCARD,\n    }\n}\n\n/// Describes an individual channel within a render pass, such as color, depth, or stencil.\n///\n/// A channel must either be read-only, or it must specify both load and store\n/// operations. See [`ResolvedPassChannel`] for a validated version.\n#[repr(C)]\n#[derive(Clone, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct PassChannel<V> {\n    /// Operation to perform to the output attachment at the start of a\n    /// renderpass.\n    ///\n    /// This must be clear if it is the first renderpass rendering to a swap\n    /// chain image.\n    pub load_op: Option<LoadOp<V>>,\n    /// Operation to perform to the output attachment at the end of a renderpass.\n    pub store_op: Option<StoreOp>,\n    /// If true, the relevant channel is not changed by a renderpass, and the\n    /// corresponding attachment can be used inside the pass by other read-only\n    /// usages.\n    pub read_only: bool,\n}\n\nimpl<V: Copy + Default> PassChannel<Option<V>> {\n    fn resolve(\n        &self,\n        handle_clear: impl Fn(Option<V>) -> Result<V, AttachmentError>,\n    ) -> Result<ResolvedPassChannel<V>, AttachmentError> {\n        if self.read_only {\n            if self.load_op.is_some() {\n                return Err(AttachmentError::ReadOnlyWithLoad);\n            }\n            if self.store_op.is_some() {\n                return Err(AttachmentError::ReadOnlyWithStore);\n            }\n            Ok(ResolvedPassChannel::ReadOnly)\n        } else {\n            Ok(ResolvedPassChannel::Operational(wgt::Operations {\n                load: match self.load_op.ok_or(AttachmentError::NoLoad)? {\n                    LoadOp::Clear(clear_value) => LoadOp::Clear(handle_clear(clear_value)?),\n                    LoadOp::DontCare(token) => LoadOp::DontCare(token),\n                    LoadOp::Load => LoadOp::Load,\n                },\n                store: self.store_op.ok_or(AttachmentError::NoStore)?,\n            }))\n        }\n    }\n}\n\n/// Describes an individual channel within a render pass, such as color, depth, or stencil.\n///\n/// Unlike [`PassChannel`], this version uses the Rust type system to guarantee\n/// a valid specification.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub enum ResolvedPassChannel<V> {\n    ReadOnly,\n    Operational(wgt::Operations<V>),\n}\n\nimpl<V: Copy + Default> ResolvedPassChannel<V> {\n    fn load_op(&self) -> LoadOp<V> {\n        match self {\n            ResolvedPassChannel::ReadOnly => LoadOp::Load,\n            ResolvedPassChannel::Operational(wgt::Operations { load, .. }) => *load,\n        }\n    }\n\n    fn store_op(&self) -> StoreOp {\n        match self {\n            ResolvedPassChannel::ReadOnly => StoreOp::Store,\n            ResolvedPassChannel::Operational(wgt::Operations { store, .. }) => *store,\n        }\n    }\n\n    fn clear_value(&self) -> V {\n        match self {\n            Self::Operational(wgt::Operations {\n                load: LoadOp::Clear(clear_value),\n                ..\n            }) => *clear_value,\n            _ => Default::default(),\n        }\n    }\n\n    fn is_readonly(&self) -> bool {\n        matches!(self, Self::ReadOnly)\n    }\n\n    fn hal_ops(&self) -> hal::AttachmentOps {\n        load_hal_ops(self.load_op()) | store_hal_ops(self.store_op())\n    }\n}\n\n/// Describes a color attachment to a render pass.\n#[repr(C)]\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct RenderPassColorAttachment<TV = id::TextureViewId> {\n    /// The view to use as an attachment.\n    pub view: TV,\n    /// The depth slice index of a 3D view. It must not be provided if the view is not 3D.\n    pub depth_slice: Option<u32>,\n    /// The view that will receive the resolved output if multisampling is used.\n    pub resolve_target: Option<TV>,\n    /// Operation to perform to the output attachment at the start of a\n    /// renderpass.\n    ///\n    /// This must be clear if it is the first renderpass rendering to a swap\n    /// chain image.\n    pub load_op: LoadOp<Color>,\n    /// Operation to perform to the output attachment at the end of a renderpass.\n    pub store_op: StoreOp,\n}\n\npub type ArcRenderPassColorAttachment = RenderPassColorAttachment<Arc<TextureView>>;\n\n// Avoid allocation in the common case that there is only one color attachment,\n// but don't bloat `ArcCommand::RunRenderPass` excessively.\npub type ColorAttachments<TV = Arc<TextureView>> =\n    SmallVec<[Option<RenderPassColorAttachment<TV>>; 1]>;\n\nimpl ArcRenderPassColorAttachment {\n    fn hal_ops(&self) -> hal::AttachmentOps {\n        load_hal_ops(self.load_op) | store_hal_ops(self.store_op)\n    }\n\n    fn clear_value(&self) -> Color {\n        match self.load_op {\n            LoadOp::Clear(clear_value) => clear_value,\n            LoadOp::DontCare(_) | LoadOp::Load => Color::default(),\n        }\n    }\n}\n\n/// Describes a depth/stencil attachment to a render pass.\n///\n/// This version uses the unvalidated [`PassChannel`].\n#[repr(C)]\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct RenderPassDepthStencilAttachment<TV> {\n    /// The view to use as an attachment.\n    pub view: TV,\n    /// What operations will be performed on the depth part of the attachment.\n    pub depth: PassChannel<Option<f32>>,\n    /// What operations will be performed on the stencil part of the attachment.\n    pub stencil: PassChannel<Option<u32>>,\n}\n\n/// Describes a depth/stencil attachment to a render pass.\n///\n/// This version uses the validated [`ResolvedPassChannel`].\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]\npub struct ResolvedRenderPassDepthStencilAttachment<TV> {\n    /// The view to use as an attachment.\n    pub view: TV,\n    /// What operations will be performed on the depth part of the attachment.\n    pub depth: ResolvedPassChannel<f32>,\n    /// What operations will be performed on the stencil part of the attachment.\n    pub stencil: ResolvedPassChannel<u32>,\n}\n\n/// Describes the attachments of a render pass.\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct RenderPassDescriptor<'a> {\n    pub label: Label<'a>,\n    /// The color attachments of the render pass.\n    pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,\n    /// The depth and stencil attachment of the render pass, if any.\n    pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment<id::TextureViewId>>,\n    /// Defines where and when timestamp values will be written for this pass.\n    pub timestamp_writes: Option<&'a PassTimestampWrites>,\n    /// Defines where the occlusion query results will be stored for this pass.\n    pub occlusion_query_set: Option<id::QuerySetId>,\n    /// The multiview array layers that will be used\n    pub multiview_mask: Option<NonZeroU32>,\n}\n\n/// Describes the attachments of a render pass.\nstruct ArcRenderPassDescriptor<'a> {\n    pub label: &'a Label<'a>,\n    /// The color attachments of the render pass.\n    pub color_attachments:\n        ArrayVec<Option<ArcRenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,\n    /// The depth and stencil attachment of the render pass, if any.\n    pub depth_stencil_attachment:\n        Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,\n    /// Defines where and when timestamp values will be written for this pass.\n    pub timestamp_writes: Option<ArcPassTimestampWrites>,\n    /// Defines where the occlusion query results will be stored for this pass.\n    pub occlusion_query_set: Option<Arc<QuerySet>>,\n    /// The multiview array layers that will be used\n    pub multiview_mask: Option<NonZeroU32>,\n}\n\npub type RenderBasePass = BasePass<ArcRenderCommand, RenderPassError>;\n\n/// A pass's [encoder state](https://www.w3.org/TR/webgpu/#encoder-state) and\n/// its validity are two distinct conditions, i.e., the full matrix of\n/// (open, ended) x (valid, invalid) is possible.\n///\n/// The presence or absence of the `parent` `Option` indicates the pass's state.\n/// The presence or absence of an error in `base.error` indicates the pass's\n/// validity.\npub struct RenderPass {\n    /// All pass data & records is stored here.\n    base: BasePass<ArcRenderCommand, RenderPassError>,\n\n    /// Parent command encoder that this pass records commands into.\n    ///\n    /// If this is `Some`, then the pass is in WebGPU's \"open\" state. If it is\n    /// `None`, then the pass is in the \"ended\" state.\n    /// See <https://www.w3.org/TR/webgpu/#encoder-state>\n    parent: Option<Arc<CommandEncoder>>,\n\n    color_attachments:\n        ArrayVec<Option<ArcRenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,\n    depth_stencil_attachment: Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,\n    timestamp_writes: Option<ArcPassTimestampWrites>,\n    occlusion_query_set: Option<Arc<QuerySet>>,\n    multiview_mask: Option<NonZeroU32>,\n\n    // Resource binding dedupe state.\n    current_bind_groups: BindGroupStateChange,\n    current_pipeline: StateChange<id::RenderPipelineId>,\n}\n\nimpl RenderPass {\n    /// If the parent command encoder is invalid, the returned pass will be invalid.\n    fn new(parent: Arc<CommandEncoder>, desc: ArcRenderPassDescriptor) -> Self {\n        let ArcRenderPassDescriptor {\n            label,\n            timestamp_writes,\n            color_attachments,\n            depth_stencil_attachment,\n            occlusion_query_set,\n            multiview_mask,\n        } = desc;\n\n        Self {\n            base: BasePass::new(label),\n            parent: Some(parent),\n            color_attachments,\n            depth_stencil_attachment,\n            timestamp_writes,\n            occlusion_query_set,\n            multiview_mask,\n\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        }\n    }\n\n    fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: RenderPassError) -> Self {\n        Self {\n            base: BasePass::new_invalid(label, err),\n            parent: Some(parent),\n            color_attachments: ArrayVec::new(),\n            depth_stencil_attachment: None,\n            timestamp_writes: None,\n            occlusion_query_set: None,\n            multiview_mask: None,\n            current_bind_groups: BindGroupStateChange::new(),\n            current_pipeline: StateChange::new(),\n        }\n    }\n\n    #[inline]\n    pub fn label(&self) -> Option<&str> {\n        self.base.label.as_deref()\n    }\n}\n\nimpl fmt::Debug for RenderPass {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"RenderPass\")\n            .field(\"label\", &self.label())\n            .field(\"color_attachments\", &self.color_attachments)\n            .field(\"depth_stencil_target\", &self.depth_stencil_attachment)\n            .field(\"command count\", &self.base.commands.len())\n            .field(\"dynamic offset count\", &self.base.dynamic_offsets.len())\n            .field(\"immediate data u32 count\", &self.base.immediates_data.len())\n            .field(\"multiview mask\", &self.multiview_mask)\n            .finish()\n    }\n}\n\n#[derive(Debug, PartialEq)]\nenum OptionalState {\n    Unused,\n    Required,\n    Set,\n}\n\nimpl OptionalState {\n    fn require(&mut self, require: bool) {\n        if require && *self == Self::Unused {\n            *self = Self::Required;\n        }\n    }\n}\n\n#[derive(Debug, Default)]\nstruct IndexState {\n    buffer_format: Option<IndexFormat>,\n    limit: u64,\n}\n\nimpl IndexState {\n    fn update_buffer(&mut self, range: Range<BufferAddress>, format: IndexFormat) {\n        self.buffer_format = Some(format);\n        let shift = match format {\n            IndexFormat::Uint16 => 1,\n            IndexFormat::Uint32 => 2,\n        };\n        self.limit = (range.end - range.start) >> shift;\n    }\n\n    fn reset(&mut self) {\n        self.buffer_format = None;\n        self.limit = 0;\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct VertexLimits {\n    /// Length of the shortest vertex rate vertex buffer\n    pub(crate) vertex_limit: u64,\n    /// Buffer slot which the shortest vertex rate vertex buffer is bound to\n    vertex_limit_slot: u32,\n    /// Length of the shortest instance rate vertex buffer\n    pub(crate) instance_limit: u64,\n    /// Buffer slot which the shortest instance rate vertex buffer is bound to\n    instance_limit_slot: u32,\n}\n\nimpl VertexLimits {\n    pub(crate) fn new(\n        buffer_sizes: impl Iterator<Item = Option<BufferAddress>>,\n        pipeline_steps: &[VertexStep],\n    ) -> Self {\n        // Implements the validation from https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-draw\n        // Except that the formula is shuffled to extract the number of vertices in order\n        // to carry the bulk of the computation when changing states instead of when producing\n        // draws. Draw calls tend to happen at a higher frequency. Here we determine vertex\n        // limits that can be cheaply checked for each draw call.\n\n        let mut vertex_limit = u64::MAX;\n        let mut vertex_limit_slot = 0;\n        let mut instance_limit = u64::MAX;\n        let mut instance_limit_slot = 0;\n\n        for (idx, (buffer_size, step)) in buffer_sizes.zip(pipeline_steps).enumerate() {\n            let Some(buffer_size) = buffer_size else {\n                // Missing required vertex buffer\n                return Self::default();\n            };\n\n            let limit = if buffer_size < step.last_stride {\n                // The buffer cannot fit the last vertex.\n                0\n            } else {\n                if step.stride == 0 {\n                    // We already checked that the last stride fits, the same\n                    // vertex will be repeated so this slot can accommodate any number of\n                    // vertices.\n                    continue;\n                }\n\n                // The general case.\n                (buffer_size - step.last_stride) / step.stride + 1\n            };\n\n            match step.mode {\n                VertexStepMode::Vertex => {\n                    if limit < vertex_limit {\n                        vertex_limit = limit;\n                        vertex_limit_slot = idx as _;\n                    }\n                }\n                VertexStepMode::Instance => {\n                    if limit < instance_limit {\n                        instance_limit = limit;\n                        instance_limit_slot = idx as _;\n                    }\n                }\n            }\n        }\n\n        Self {\n            vertex_limit,\n            vertex_limit_slot,\n            instance_limit,\n            instance_limit_slot,\n        }\n    }\n\n    pub(crate) fn validate_vertex_limit(\n        &self,\n        first_vertex: u32,\n        vertex_count: u32,\n    ) -> Result<(), DrawError> {\n        let last_vertex = first_vertex as u64 + vertex_count as u64;\n        let vertex_limit = self.vertex_limit;\n        if last_vertex > vertex_limit {\n            return Err(DrawError::VertexBeyondLimit {\n                last_vertex,\n                vertex_limit,\n                slot: self.vertex_limit_slot,\n            });\n        }\n\n        Ok(())\n    }\n\n    pub(crate) fn validate_instance_limit(\n        &self,\n        first_instance: u32,\n        instance_count: u32,\n    ) -> Result<(), DrawError> {\n        let last_instance = first_instance as u64 + instance_count as u64;\n        let instance_limit = self.instance_limit;\n        if last_instance > instance_limit {\n            return Err(DrawError::InstanceBeyondLimit {\n                last_instance,\n                instance_limit,\n                slot: self.instance_limit_slot,\n            });\n        }\n\n        Ok(())\n    }\n}\n\n#[derive(Debug, Default)]\nstruct VertexState {\n    buffer_sizes: [Option<BufferAddress>; hal::MAX_VERTEX_BUFFERS],\n    limits: VertexLimits,\n}\n\nimpl VertexState {\n    fn update_limits(&mut self, pipeline_steps: &[VertexStep]) {\n        self.limits = VertexLimits::new(self.buffer_sizes.iter().copied(), pipeline_steps);\n    }\n}\n\nstruct State<'scope, 'snatch_guard, 'cmd_enc> {\n    pipeline_flags: PipelineFlags,\n    blend_constant: OptionalState,\n    stencil_reference: u32,\n    pipeline: Option<Arc<RenderPipeline>>,\n    index: IndexState,\n    vertex: VertexState,\n\n    info: RenderPassInfo,\n\n    pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,\n\n    active_occlusion_query: Option<(Arc<QuerySet>, u32)>,\n    active_pipeline_statistics_query: Option<(Arc<QuerySet>, u32)>,\n}\n\nimpl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {\n    fn is_ready(&self, family: DrawCommandFamily) -> Result<(), DrawError> {\n        if let Some(pipeline) = self.pipeline.as_ref() {\n            self.pass.binder.check_compatibility(pipeline.as_ref())?;\n            self.pass.binder.check_late_buffer_bindings()?;\n\n            if self.blend_constant == OptionalState::Required {\n                return Err(DrawError::MissingBlendConstant);\n            }\n\n            // Determine how many vertex buffers have already been bound\n            let vertex_buffer_count = self\n                .vertex\n                .buffer_sizes\n                .iter()\n                .take_while(|v| v.is_some())\n                .count() as u32;\n            // Compare with the needed quantity\n            if vertex_buffer_count < pipeline.vertex_steps.len() as u32 {\n                return Err(DrawError::MissingVertexBuffer {\n                    pipeline: pipeline.error_ident(),\n                    index: vertex_buffer_count,\n                });\n            }\n\n            if family == DrawCommandFamily::DrawIndexed {\n                // Pipeline expects an index buffer\n                // We have a buffer bound\n                let buffer_index_format = self\n                    .index\n                    .buffer_format\n                    .ok_or(DrawError::MissingIndexBuffer)?;\n\n                if pipeline.topology.is_strip()\n                    && pipeline.strip_index_format != Some(buffer_index_format)\n                {\n                    return Err(DrawError::UnmatchedStripIndexFormat {\n                        pipeline: pipeline.error_ident(),\n                        strip_index_format: pipeline.strip_index_format,\n                        buffer_format: buffer_index_format,\n                    });\n                }\n            }\n            if (family == DrawCommandFamily::DrawMeshTasks) != pipeline.is_mesh {\n                return Err(DrawError::WrongPipelineType {\n                    wanted_mesh_pipeline: !pipeline.is_mesh,\n                });\n            }\n            Ok(())\n        } else {\n            Err(DrawError::MissingPipeline(pass::MissingPipeline))\n        }\n    }\n\n    /// Flush binding state in preparation for a draw call.\n    ///\n    /// See the compute pass version for an explanation of some ways that\n    /// `flush_bindings` differs between the two types of passes.\n    fn flush_bindings(&mut self) -> Result<(), RenderPassErrorInner> {\n        flush_bindings_helper(&mut self.pass)?;\n        Ok(())\n    }\n\n    /// Reset the `RenderBundle`-related states.\n    fn reset_bundle(&mut self) {\n        self.pass.binder.reset();\n        self.pipeline = None;\n        self.index.reset();\n        self.vertex = Default::default();\n    }\n}\n\n/// Describes an attachment location in words.\n///\n/// Can be used as \"the {loc} has...\" or \"{loc} has...\"\n#[derive(Debug, Copy, Clone)]\npub enum AttachmentErrorLocation {\n    Color { index: usize, resolve: bool },\n    Depth,\n}\n\nimpl fmt::Display for AttachmentErrorLocation {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            AttachmentErrorLocation::Color {\n                index,\n                resolve: false,\n            } => write!(f, \"color attachment at index {index}'s texture view\"),\n            AttachmentErrorLocation::Color {\n                index,\n                resolve: true,\n            } => write!(\n                f,\n                \"color attachment at index {index}'s resolve texture view\"\n            ),\n            AttachmentErrorLocation::Depth => write!(f, \"depth attachment's texture view\"),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ColorAttachmentError {\n    #[error(\"Attachment format {0:?} is not a color format\")]\n    InvalidFormat(wgt::TextureFormat),\n    #[error(\"The number of color attachments {given} exceeds the limit {limit}\")]\n    TooMany { given: usize, limit: usize },\n    #[error(\"The total number of bytes per sample in color attachments {total} exceeds the limit {limit}\")]\n    TooManyBytesPerSample { total: u32, limit: u32 },\n    #[error(\"Depth slice must be less than {limit} but is {given}\")]\n    DepthSliceLimit { given: u32, limit: u32 },\n    #[error(\"Color attachment's view is 3D and requires depth slice to be provided\")]\n    MissingDepthSlice,\n    #[error(\"Depth slice was provided but the color attachment's view is not 3D\")]\n    UnneededDepthSlice,\n    #[error(\"{view}'s subresource at mip {mip_level} and depth/array layer {depth_or_array_layer} is already attached to this render pass\")]\n    SubresourceOverlap {\n        view: ResourceErrorIdent,\n        mip_level: u32,\n        depth_or_array_layer: u32,\n    },\n    #[error(\"Color attachment's usage contains {0:?}. This can only be used with StoreOp::{1:?}, but StoreOp::{2:?} was provided\")]\n    InvalidUsageForStoreOp(TextureUsages, StoreOp, StoreOp),\n}\n\nimpl WebGpuError for ColorAttachmentError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum AttachmentError {\n    #[error(\"The format of the depth-stencil attachment ({0:?}) is not a depth-or-stencil format\")]\n    InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),\n    #[error(\"LoadOp must be None for read-only attachments\")]\n    ReadOnlyWithLoad,\n    #[error(\"StoreOp must be None for read-only attachments\")]\n    ReadOnlyWithStore,\n    #[error(\"Attachment without load\")]\n    NoLoad,\n    #[error(\"Attachment without store\")]\n    NoStore,\n    #[error(\"LoadOp is `Clear` but no clear value was provided\")]\n    NoClearValue,\n    #[error(\"Clear value ({0}) must be between 0.0 and 1.0, inclusive\")]\n    ClearValueOutOfRange(f32),\n}\n\nimpl WebGpuError for AttachmentError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n/// Error encountered when performing a render pass.\n#[derive(Clone, Debug, Error)]\npub enum RenderPassErrorInner {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    ColorAttachment(#[from] ColorAttachmentError),\n    #[error(transparent)]\n    InvalidAttachment(#[from] AttachmentError),\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n    #[error(\"Parent encoder is invalid\")]\n    InvalidParentEncoder,\n    #[error(transparent)]\n    DebugGroupError(#[from] DebugGroupError),\n    #[error(\"The format of the {location} ({format:?}) is not resolvable\")]\n    UnsupportedResolveTargetFormat {\n        location: AttachmentErrorLocation,\n        format: wgt::TextureFormat,\n    },\n    #[error(\"No color attachments or depth attachments were provided, at least one attachment of any kind must be provided\")]\n    MissingAttachments,\n    #[error(\"The {location} is not renderable:\")]\n    TextureViewIsNotRenderable {\n        location: AttachmentErrorLocation,\n        #[source]\n        reason: TextureViewNotRenderableReason,\n    },\n    #[error(\"Attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}\")]\n    AttachmentsDimensionMismatch {\n        expected_location: AttachmentErrorLocation,\n        expected_extent: wgt::Extent3d,\n        actual_location: AttachmentErrorLocation,\n        actual_extent: wgt::Extent3d,\n    },\n    #[error(\"Attachments have differing sample counts: the {expected_location} has count {expected_samples:?} but is followed by the {actual_location} which has count {actual_samples:?}\")]\n    AttachmentSampleCountMismatch {\n        expected_location: AttachmentErrorLocation,\n        expected_samples: u32,\n        actual_location: AttachmentErrorLocation,\n        actual_samples: u32,\n    },\n    #[error(\"The resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)\")]\n    InvalidResolveSampleCounts {\n        location: AttachmentErrorLocation,\n        src: u32,\n        dst: u32,\n    },\n    #[error(\n        \"Resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})\"\n    )]\n    MismatchedResolveTextureFormat {\n        location: AttachmentErrorLocation,\n        src: wgt::TextureFormat,\n        dst: wgt::TextureFormat,\n    },\n    #[error(\"Unable to clear non-present/read-only depth\")]\n    InvalidDepthOps,\n    #[error(\"Unable to clear non-present/read-only stencil\")]\n    InvalidStencilOps,\n    #[error(transparent)]\n    InvalidValuesOffset(#[from] pass::InvalidValuesOffset),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(\"Indirect buffer offset {0:?} is not a multiple of 4\")]\n    UnalignedIndirectBufferOffset(BufferAddress),\n    #[error(\"Indirect draw uses bytes {offset}..{end_offset} using count {count} which overruns indirect buffer of size {buffer_size}\")]\n    IndirectBufferOverrun {\n        count: u32,\n        offset: u64,\n        end_offset: u64,\n        buffer_size: u64,\n    },\n    #[error(\"Indirect draw uses bytes {begin_count_offset}..{end_count_offset} which overruns indirect buffer of size {count_buffer_size}\")]\n    IndirectCountBufferOverrun {\n        begin_count_offset: u64,\n        end_count_offset: u64,\n        count_buffer_size: u64,\n    },\n    #[error(transparent)]\n    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),\n    #[error(\"Render bundle has incompatible targets, {0}\")]\n    IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),\n    #[error(\n        \"Render bundle has incompatible read-only flags: \\\n             bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \\\n             while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \\\n             Read-only renderpasses are only compatible with read-only bundles for that aspect.\"\n    )]\n    IncompatibleBundleReadOnlyDepthStencil {\n        pass_depth: bool,\n        pass_stencil: bool,\n        bundle_depth: bool,\n        bundle_stencil: bool,\n    },\n    #[error(transparent)]\n    RenderCommand(#[from] RenderCommandError),\n    #[error(transparent)]\n    Draw(#[from] DrawError),\n    #[error(transparent)]\n    Bind(#[from] BindError),\n    #[error(\"Immediate data offset must be aligned to 4 bytes\")]\n    ImmediateOffsetAlignment,\n    #[error(\"Immediate data size must be aligned to 4 bytes\")]\n    ImmediateDataizeAlignment,\n    #[error(\"Ran out of immediate data space. Don't set 4gb of immediates per ComputePass.\")]\n    ImmediateOutOfMemory,\n    #[error(transparent)]\n    QueryUse(#[from] QueryUseError),\n    #[error(\"Multiview layer count must match\")]\n    MultiViewMismatch,\n    #[error(\n        \"Multiview pass texture views with more than one array layer must have D2Array dimension\"\n    )]\n    MultiViewDimensionMismatch,\n    #[error(\"Multiview view count limit violated\")]\n    TooManyMultiviewViews,\n    #[error(\"missing occlusion query set\")]\n    MissingOcclusionQuerySet,\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"The compute pass has already been ended and no further commands can be recorded\")]\n    PassEnded,\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    TimestampWrites(#[from] TimestampWritesError),\n}\n\nimpl From<MissingBufferUsageError> for RenderPassErrorInner {\n    fn from(error: MissingBufferUsageError) -> Self {\n        Self::RenderCommand(error.into())\n    }\n}\n\nimpl From<MissingTextureUsageError> for RenderPassErrorInner {\n    fn from(error: MissingTextureUsageError) -> Self {\n        Self::RenderCommand(error.into())\n    }\n}\n\nimpl From<pass::BindGroupIndexOutOfRange> for RenderPassErrorInner {\n    fn from(error: pass::BindGroupIndexOutOfRange) -> Self {\n        Self::RenderCommand(RenderCommandError::BindGroupIndexOutOfRange(error))\n    }\n}\n\nimpl From<pass::MissingPipeline> for RenderPassErrorInner {\n    fn from(error: pass::MissingPipeline) -> Self {\n        Self::Draw(DrawError::MissingPipeline(error))\n    }\n}\n\nimpl From<ImmediateUploadError> for RenderPassErrorInner {\n    fn from(error: ImmediateUploadError) -> Self {\n        Self::RenderCommand(error.into())\n    }\n}\n\n/// Error encountered when performing a render pass.\n#[derive(Clone, Debug, Error)]\n#[error(\"{scope}\")]\npub struct RenderPassError {\n    pub scope: PassErrorScope,\n    #[source]\n    pub(super) inner: RenderPassErrorInner,\n}\n\nimpl<E: Into<RenderPassErrorInner>> MapPassErr<RenderPassError> for E {\n    fn map_pass_err(self, scope: PassErrorScope) -> RenderPassError {\n        RenderPassError {\n            scope,\n            inner: self.into(),\n        }\n    }\n}\n\nimpl WebGpuError for RenderPassError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        let Self { scope: _, inner } = self;\n        match inner {\n            RenderPassErrorInner::Device(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::ColorAttachment(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::EncoderState(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::RenderCommand(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::Draw(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::Bind(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::QueryUse(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::InvalidResource(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::IncompatibleBundleTargets(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::InvalidAttachment(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),\n            RenderPassErrorInner::InvalidValuesOffset(e) => e.webgpu_error_type(),\n\n            RenderPassErrorInner::InvalidParentEncoder\n            | RenderPassErrorInner::UnsupportedResolveTargetFormat { .. }\n            | RenderPassErrorInner::MissingAttachments\n            | RenderPassErrorInner::TextureViewIsNotRenderable { .. }\n            | RenderPassErrorInner::AttachmentsDimensionMismatch { .. }\n            | RenderPassErrorInner::AttachmentSampleCountMismatch { .. }\n            | RenderPassErrorInner::InvalidResolveSampleCounts { .. }\n            | RenderPassErrorInner::MismatchedResolveTextureFormat { .. }\n            | RenderPassErrorInner::InvalidDepthOps\n            | RenderPassErrorInner::InvalidStencilOps\n            | RenderPassErrorInner::UnalignedIndirectBufferOffset(..)\n            | RenderPassErrorInner::IndirectBufferOverrun { .. }\n            | RenderPassErrorInner::IndirectCountBufferOverrun { .. }\n            | RenderPassErrorInner::ResourceUsageCompatibility(..)\n            | RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil { .. }\n            | RenderPassErrorInner::ImmediateOffsetAlignment\n            | RenderPassErrorInner::ImmediateDataizeAlignment\n            | RenderPassErrorInner::ImmediateOutOfMemory\n            | RenderPassErrorInner::MultiViewMismatch\n            | RenderPassErrorInner::MultiViewDimensionMismatch\n            | RenderPassErrorInner::TooManyMultiviewViews\n            | RenderPassErrorInner::MissingOcclusionQuerySet\n            | RenderPassErrorInner::PassEnded => ErrorType::Validation,\n        }\n    }\n}\n\nstruct RenderAttachment {\n    texture: Arc<Texture>,\n    selector: TextureSelector,\n    usage: wgt::TextureUses,\n}\n\nimpl TextureView {\n    fn to_render_attachment(&self, usage: wgt::TextureUses) -> RenderAttachment {\n        RenderAttachment {\n            texture: self.parent.clone(),\n            selector: self.selector.clone(),\n            usage,\n        }\n    }\n}\n\nconst MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;\ntype AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;\n\nstruct RenderPassInfo {\n    context: RenderPassContext,\n    /// All render attachments, including depth/stencil\n    render_attachments: AttachmentDataVec<RenderAttachment>,\n    is_depth_read_only: bool,\n    is_stencil_read_only: bool,\n    extent: wgt::Extent3d,\n\n    divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc<TextureView>)>,\n    multiview_mask: Option<NonZeroU32>,\n}\n\nimpl RenderPassInfo {\n    fn add_pass_texture_init_actions<V>(\n        load_op: LoadOp<V>,\n        store_op: StoreOp,\n        texture_memory_actions: &mut CommandBufferTextureMemoryActions,\n        view: &TextureView,\n        pending_discard_init_fixups: &mut SurfacesInDiscardState,\n    ) {\n        if matches!(load_op, LoadOp::Load) {\n            pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(\n                &TextureInitTrackerAction {\n                    texture: view.parent.clone(),\n                    range: TextureInitRange::from(view.selector.clone()),\n                    // Note that this is needed even if the target is discarded,\n                    kind: MemoryInitKind::NeedsInitializedMemory,\n                },\n            ));\n        } else if store_op == StoreOp::Store {\n            // Clear + Store\n            texture_memory_actions.register_implicit_init(\n                &view.parent,\n                TextureInitRange::from(view.selector.clone()),\n            );\n        }\n        if store_op == StoreOp::Discard {\n            // the discard happens at the *end* of a pass, but recording the\n            // discard right away be alright since the texture can't be used\n            // during the pass anyways\n            texture_memory_actions.discard(TextureSurfaceDiscard {\n                texture: view.parent.clone(),\n                mip_level: view.selector.mips.start,\n                layer: view.selector.layers.start,\n            });\n        }\n    }\n\n    fn start(\n        device: &Arc<Device>,\n        hal_label: Option<&str>,\n        color_attachments: &[Option<ArcRenderPassColorAttachment>],\n        mut depth_stencil_attachment: Option<\n            ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,\n        >,\n        mut timestamp_writes: Option<ArcPassTimestampWrites>,\n        mut occlusion_query_set: Option<Arc<QuerySet>>,\n        encoder: &mut dyn hal::DynCommandEncoder,\n        trackers: &mut Tracker,\n        texture_memory_actions: &mut CommandBufferTextureMemoryActions,\n        pending_query_resets: &mut QueryResetMap,\n        pending_discard_init_fixups: &mut SurfacesInDiscardState,\n        snatch_guard: &SnatchGuard<'_>,\n        multiview_mask: Option<NonZeroU32>,\n    ) -> Result<Self, RenderPassErrorInner> {\n        profiling::scope!(\"RenderPassInfo::start\");\n\n        // We default to false intentionally, even if depth-stencil isn't used at all.\n        // This allows us to use the primary raw pipeline in `RenderPipeline`,\n        // instead of the special read-only one, which would be `None`.\n        let mut is_depth_read_only = false;\n        let mut is_stencil_read_only = false;\n\n        let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();\n        let mut discarded_surfaces = AttachmentDataVec::new();\n        let mut divergent_discarded_depth_stencil_aspect = None;\n\n        let mut attachment_location = AttachmentErrorLocation::Color {\n            index: usize::MAX,\n            resolve: false,\n        };\n        let mut extent = None;\n        let mut sample_count = 0;\n\n        let mut detected_multiview: Option<Option<NonZeroU32>> = None;\n\n        let mut check_multiview = |view: &TextureView| {\n            // Get the multiview configuration for this texture view\n            let layers = view.selector.layers.end - view.selector.layers.start;\n            let this_multiview = if layers >= 2 {\n                // Trivially proven by the if above\n                Some(unsafe { NonZeroU32::new_unchecked(layers) })\n            } else {\n                None\n            };\n\n            // Make sure that if this view is a multiview, it is set to be an array\n            if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {\n                return Err(RenderPassErrorInner::MultiViewDimensionMismatch);\n            }\n\n            // Validate matching first, or store the first one\n            if let Some(multiview) = detected_multiview {\n                if multiview != this_multiview {\n                    return Err(RenderPassErrorInner::MultiViewMismatch);\n                }\n            } else {\n                // Multiview is only supported if the feature is enabled\n                if let Some(this_multiview) = this_multiview {\n                    device.require_features(wgt::Features::MULTIVIEW)?;\n                    if this_multiview.get() > device.limits.max_multiview_view_count {\n                        return Err(RenderPassErrorInner::TooManyMultiviewViews);\n                    }\n                }\n\n                detected_multiview = Some(this_multiview);\n            }\n\n            Ok(())\n        };\n        let mut add_view = |view: &TextureView, location| {\n            let render_extent = view.render_extent.map_err(|reason| {\n                RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }\n            })?;\n            if let Some(ex) = extent {\n                if ex != render_extent {\n                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {\n                        expected_location: attachment_location,\n                        expected_extent: ex,\n                        actual_location: location,\n                        actual_extent: render_extent,\n                    });\n                }\n            } else {\n                extent = Some(render_extent);\n            }\n            if sample_count == 0 {\n                sample_count = view.samples;\n            } else if sample_count != view.samples {\n                return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {\n                    expected_location: attachment_location,\n                    expected_samples: sample_count,\n                    actual_location: location,\n                    actual_samples: view.samples,\n                });\n            }\n            attachment_location = location;\n            Ok(())\n        };\n\n        let mut depth_stencil = None;\n\n        if let Some(at) = depth_stencil_attachment.as_ref() {\n            let view = &at.view;\n            check_multiview(view)?;\n            add_view(view, AttachmentErrorLocation::Depth)?;\n\n            let ds_aspects = view.desc.aspects();\n\n            if !ds_aspects.contains(hal::FormatAspects::STENCIL)\n                || (at.stencil.load_op().eq_variant(at.depth.load_op())\n                    && at.stencil.store_op() == at.depth.store_op())\n            {\n                Self::add_pass_texture_init_actions(\n                    at.depth.load_op(),\n                    at.depth.store_op(),\n                    texture_memory_actions,\n                    view,\n                    pending_discard_init_fixups,\n                );\n            } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {\n                Self::add_pass_texture_init_actions(\n                    at.stencil.load_op(),\n                    at.stencil.store_op(),\n                    texture_memory_actions,\n                    view,\n                    pending_discard_init_fixups,\n                );\n            } else {\n                // This is the only place (anywhere in wgpu) where Stencil &\n                // Depth init state can diverge.\n                //\n                // To safe us the overhead of tracking init state of texture\n                // aspects everywhere, we're going to cheat a little bit in\n                // order to keep the init state of both Stencil and Depth\n                // aspects in sync. The expectation is that we hit this path\n                // extremely rarely!\n                //\n                // Diverging LoadOp, i.e. Load + Clear:\n                //\n                // Record MemoryInitKind::NeedsInitializedMemory for the entire\n                // surface, a bit wasteful on unit but no negative effect!\n                //\n                // Rationale: If the loaded channel is uninitialized it needs\n                // clearing, the cleared channel doesn't care. (If everything is\n                // already initialized nothing special happens)\n                //\n                // (possible minor optimization: Clear caused by\n                // NeedsInitializedMemory should know that it doesn't need to\n                // clear the aspect that was set to C)\n                let need_init_beforehand =\n                    at.depth.load_op() == LoadOp::Load || at.stencil.load_op() == LoadOp::Load;\n                if need_init_beforehand {\n                    pending_discard_init_fixups.extend(\n                        texture_memory_actions.register_init_action(&TextureInitTrackerAction {\n                            texture: view.parent.clone(),\n                            range: TextureInitRange::from(view.selector.clone()),\n                            kind: MemoryInitKind::NeedsInitializedMemory,\n                        }),\n                    );\n                }\n\n                // Diverging Store, i.e. Discard + Store:\n                //\n                // Immediately zero out channel that is set to discard after\n                // we're done with the render pass. This allows us to set the\n                // entire surface to MemoryInitKind::ImplicitlyInitialized (if\n                // it isn't already set to NeedsInitializedMemory).\n                //\n                // (possible optimization: Delay and potentially drop this zeroing)\n                if at.depth.store_op() != at.stencil.store_op() {\n                    if !need_init_beforehand {\n                        texture_memory_actions.register_implicit_init(\n                            &view.parent,\n                            TextureInitRange::from(view.selector.clone()),\n                        );\n                    }\n                    divergent_discarded_depth_stencil_aspect = Some((\n                        if at.depth.store_op() == StoreOp::Discard {\n                            wgt::TextureAspect::DepthOnly\n                        } else {\n                            wgt::TextureAspect::StencilOnly\n                        },\n                        view.clone(),\n                    ));\n                } else if at.depth.store_op() == StoreOp::Discard {\n                    // Both are discarded using the regular path.\n                    discarded_surfaces.push(TextureSurfaceDiscard {\n                        texture: view.parent.clone(),\n                        mip_level: view.selector.mips.start,\n                        layer: view.selector.layers.start,\n                    });\n                }\n            }\n\n            is_depth_read_only = at.depth.is_readonly();\n            is_stencil_read_only = at.stencil.is_readonly();\n\n            let usage = if is_depth_read_only\n                && is_stencil_read_only\n                && device\n                    .downlevel\n                    .flags\n                    .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)\n            {\n                wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::RESOURCE\n            } else {\n                wgt::TextureUses::DEPTH_STENCIL_WRITE\n            };\n            render_attachments.push(view.to_render_attachment(usage));\n\n            depth_stencil = Some(hal::DepthStencilAttachment {\n                target: hal::Attachment {\n                    view: view.try_raw(snatch_guard)?,\n                    usage,\n                },\n                depth_ops: at.depth.hal_ops(),\n                stencil_ops: at.stencil.hal_ops(),\n                clear_value: (at.depth.clear_value(), at.stencil.clear_value()),\n            });\n        }\n\n        let mut attachment_set = crate::FastHashSet::default();\n\n        let mut color_attachments_hal =\n            ArrayVec::<Option<hal::ColorAttachment<_>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();\n        for (index, attachment) in color_attachments.iter().enumerate() {\n            let at = if let Some(attachment) = attachment.as_ref() {\n                attachment\n            } else {\n                color_attachments_hal.push(None);\n                continue;\n            };\n            let color_view: &TextureView = &at.view;\n            color_view.same_device(device)?;\n            check_multiview(color_view)?;\n            add_view(\n                color_view,\n                AttachmentErrorLocation::Color {\n                    index,\n                    resolve: false,\n                },\n            )?;\n\n            if !color_view.desc.aspects().intersects(\n                hal::FormatAspects::COLOR\n                    | hal::FormatAspects::PLANE_0\n                    | hal::FormatAspects::PLANE_1\n                    | hal::FormatAspects::PLANE_2,\n            ) {\n                return Err(RenderPassErrorInner::ColorAttachment(\n                    ColorAttachmentError::InvalidFormat(color_view.desc.format),\n                ));\n            }\n\n            if color_view.desc.dimension == TextureViewDimension::D3 {\n                if let Some(depth_slice) = at.depth_slice {\n                    let mip = color_view.desc.range.base_mip_level;\n                    let mip_size = color_view\n                        .parent\n                        .desc\n                        .size\n                        .mip_level_size(mip, color_view.parent.desc.dimension);\n                    let limit = mip_size.depth_or_array_layers;\n                    if depth_slice >= limit {\n                        return Err(RenderPassErrorInner::ColorAttachment(\n                            ColorAttachmentError::DepthSliceLimit {\n                                given: depth_slice,\n                                limit,\n                            },\n                        ));\n                    }\n                } else {\n                    return Err(RenderPassErrorInner::ColorAttachment(\n                        ColorAttachmentError::MissingDepthSlice,\n                    ));\n                }\n            } else if at.depth_slice.is_some() {\n                return Err(RenderPassErrorInner::ColorAttachment(\n                    ColorAttachmentError::UnneededDepthSlice,\n                ));\n            }\n\n            validation::validate_color_attachment_bytes_per_sample(\n                color_attachments\n                    .iter()\n                    .flatten()\n                    .map(|at| at.view.desc.format),\n                device.limits.max_color_attachment_bytes_per_sample,\n            )\n            .map_err(RenderPassErrorInner::ColorAttachment)?;\n\n            fn check_attachment_overlap(\n                attachment_set: &mut crate::FastHashSet<(crate::track::TrackerIndex, u32, u32)>,\n                view: &TextureView,\n                depth_slice: Option<u32>,\n            ) -> Result<(), ColorAttachmentError> {\n                let mut insert = |slice| {\n                    let mip_level = view.desc.range.base_mip_level;\n                    if attachment_set.insert((\n                        view.parent.tracking_data.tracker_index(),\n                        mip_level,\n                        slice,\n                    )) {\n                        Ok(())\n                    } else {\n                        Err(ColorAttachmentError::SubresourceOverlap {\n                            view: view.error_ident(),\n                            mip_level,\n                            depth_or_array_layer: slice,\n                        })\n                    }\n                };\n                match view.desc.dimension {\n                    TextureViewDimension::D2 => {\n                        insert(view.desc.range.base_array_layer)?;\n                    }\n                    TextureViewDimension::D2Array => {\n                        for layer in view.selector.layers.clone() {\n                            insert(layer)?;\n                        }\n                    }\n                    TextureViewDimension::D3 => {\n                        insert(depth_slice.unwrap())?;\n                    }\n                    _ => unreachable!(),\n                };\n                Ok(())\n            }\n\n            check_attachment_overlap(&mut attachment_set, color_view, at.depth_slice)?;\n\n            Self::add_pass_texture_init_actions(\n                at.load_op,\n                at.store_op,\n                texture_memory_actions,\n                color_view,\n                pending_discard_init_fixups,\n            );\n            render_attachments\n                .push(color_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));\n\n            let mut hal_resolve_target = None;\n            if let Some(resolve_view) = &at.resolve_target {\n                resolve_view.same_device(device)?;\n                check_multiview(resolve_view)?;\n\n                check_attachment_overlap(&mut attachment_set, resolve_view, None)?;\n\n                let resolve_location = AttachmentErrorLocation::Color {\n                    index,\n                    resolve: true,\n                };\n\n                let render_extent = resolve_view.render_extent.map_err(|reason| {\n                    RenderPassErrorInner::TextureViewIsNotRenderable {\n                        location: resolve_location,\n                        reason,\n                    }\n                })?;\n                if color_view.render_extent.unwrap() != render_extent {\n                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {\n                        expected_location: attachment_location,\n                        expected_extent: extent.unwrap_or_default(),\n                        actual_location: resolve_location,\n                        actual_extent: render_extent,\n                    });\n                }\n                if color_view.samples == 1 || resolve_view.samples != 1 {\n                    return Err(RenderPassErrorInner::InvalidResolveSampleCounts {\n                        location: resolve_location,\n                        src: color_view.samples,\n                        dst: resolve_view.samples,\n                    });\n                }\n                if color_view.desc.format != resolve_view.desc.format {\n                    return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {\n                        location: resolve_location,\n                        src: color_view.desc.format,\n                        dst: resolve_view.desc.format,\n                    });\n                }\n                if !resolve_view\n                    .format_features\n                    .flags\n                    .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)\n                {\n                    return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {\n                        location: resolve_location,\n                        format: resolve_view.desc.format,\n                    });\n                }\n\n                texture_memory_actions.register_implicit_init(\n                    &resolve_view.parent,\n                    TextureInitRange::from(resolve_view.selector.clone()),\n                );\n                render_attachments\n                    .push(resolve_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));\n\n                hal_resolve_target = Some(hal::Attachment {\n                    view: resolve_view.try_raw(snatch_guard)?,\n                    usage: wgt::TextureUses::COLOR_TARGET,\n                });\n            }\n\n            color_attachments_hal.push(Some(hal::ColorAttachment {\n                target: hal::Attachment {\n                    view: color_view.try_raw(snatch_guard)?,\n                    usage: wgt::TextureUses::COLOR_TARGET,\n                },\n                depth_slice: at.depth_slice,\n                resolve_target: hal_resolve_target,\n                ops: at.hal_ops(),\n                clear_value: at.clear_value(),\n            }));\n        }\n\n        let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;\n\n        let detected_multiview =\n            detected_multiview.expect(\"Multiview was not detected, no attachments\");\n        if let Some(mask) = multiview_mask {\n            // 0x01 will have msb 0\n            let mask_msb = 31 - mask.leading_zeros();\n            let detected_mv = detected_multiview.map(NonZeroU32::get).unwrap_or(1);\n            if mask_msb >= detected_mv {\n                return Err(RenderPassErrorInner::MultiViewMismatch);\n            }\n            if mask.get() != (1 << detected_mv) - 1 {\n                device.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;\n            }\n        }\n\n        let attachment_formats = AttachmentData {\n            colors: color_attachments\n                .iter()\n                .map(|at| at.as_ref().map(|at| at.view.desc.format))\n                .collect(),\n            resolves: color_attachments\n                .iter()\n                .filter_map(|at| {\n                    at.as_ref().and_then(|at| {\n                        at.resolve_target\n                            .as_ref()\n                            .map(|resolve| resolve.desc.format)\n                    })\n                })\n                .collect(),\n            depth_stencil: depth_stencil_attachment\n                .as_ref()\n                .map(|at| at.view.desc.format),\n        };\n\n        let context = RenderPassContext {\n            attachments: attachment_formats,\n            sample_count,\n            multiview_mask,\n        };\n\n        let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {\n            let query_set = &tw.query_set;\n            query_set.same_device(device)?;\n\n            if let Some(index) = tw.beginning_of_pass_write_index {\n                pending_query_resets.use_query_set(query_set, index);\n            }\n            if let Some(index) = tw.end_of_pass_write_index {\n                pending_query_resets.use_query_set(query_set, index);\n            }\n\n            Some(hal::PassTimestampWrites {\n                query_set: query_set.raw(),\n                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,\n                end_of_pass_write_index: tw.end_of_pass_write_index,\n            })\n        } else {\n            None\n        };\n\n        let occlusion_query_set_hal = if let Some(query_set) = occlusion_query_set.as_ref() {\n            query_set.same_device(device)?;\n            Some(query_set.raw())\n        } else {\n            None\n        };\n\n        let hal_desc = hal::RenderPassDescriptor {\n            label: hal_label,\n            extent,\n            sample_count,\n            color_attachments: &color_attachments_hal,\n            depth_stencil_attachment: depth_stencil,\n            multiview_mask,\n            timestamp_writes: timestamp_writes_hal,\n            occlusion_query_set: occlusion_query_set_hal,\n        };\n        unsafe {\n            encoder\n                .begin_render_pass(&hal_desc)\n                .map_err(|e| device.handle_hal_error(e))?;\n        };\n        drop(color_attachments_hal); // Drop, so we can consume `color_attachments` for the tracker.\n\n        // Can't borrow the tracker more than once, so have to add to the tracker after the `begin_render_pass` hal call.\n        if let Some(tw) = timestamp_writes.take() {\n            trackers.query_sets.insert_single(tw.query_set);\n        };\n        if let Some(occlusion_query_set) = occlusion_query_set.take() {\n            trackers.query_sets.insert_single(occlusion_query_set);\n        };\n        if let Some(at) = depth_stencil_attachment.take() {\n            trackers.views.insert_single(at.view.clone());\n        }\n        for at in color_attachments.iter().flatten() {\n            trackers.views.insert_single(at.view.clone());\n            if let Some(resolve_target) = at.resolve_target.clone() {\n                trackers.views.insert_single(resolve_target);\n            }\n        }\n\n        Ok(Self {\n            context,\n            render_attachments,\n            is_depth_read_only,\n            is_stencil_read_only,\n            extent,\n            divergent_discarded_depth_stencil_aspect,\n            multiview_mask,\n        })\n    }\n\n    fn finish(\n        self,\n        device: &Device,\n        raw: &mut dyn hal::DynCommandEncoder,\n        snatch_guard: &SnatchGuard,\n        scope: &mut UsageScope<'_>,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<(), RenderPassErrorInner> {\n        profiling::scope!(\"RenderPassInfo::finish\");\n        unsafe {\n            raw.end_render_pass();\n        }\n\n        for ra in self.render_attachments {\n            let texture = &ra.texture;\n            texture.check_usage(TextureUsages::RENDER_ATTACHMENT)?;\n\n            // the tracker set of the pass is always in \"extend\" mode\n            unsafe {\n                scope\n                    .textures\n                    .merge_single(texture, Some(ra.selector.clone()), ra.usage)?\n            };\n        }\n\n        // If either only stencil or depth was discarded, we put in a special\n        // clear pass to keep the init status of the aspects in sync. We do this\n        // so we don't need to track init state for depth/stencil aspects\n        // individually.\n        //\n        // Note that we don't go the usual route of \"brute force\" initializing\n        // the texture when need arises here, since this path is actually\n        // something a user may genuinely want (where as the other cases are\n        // more seen along the lines as gracefully handling a user error).\n        if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {\n            let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {\n                (\n                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth\n                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE,       // unchanged stencil\n                )\n            } else {\n                (\n                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil\n                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth\n                )\n            };\n            let desc = hal::RenderPassDescriptor::<'_, _, dyn hal::DynTextureView> {\n                label: hal_label(\n                    Some(\"(wgpu internal) Zero init discarded depth/stencil aspect\"),\n                    instance_flags,\n                ),\n                extent: view.render_extent.unwrap(),\n                sample_count: view.samples,\n                color_attachments: &[],\n                depth_stencil_attachment: Some(hal::DepthStencilAttachment {\n                    target: hal::Attachment {\n                        view: view.try_raw(snatch_guard)?,\n                        usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,\n                    },\n                    depth_ops,\n                    stencil_ops,\n                    clear_value: (0.0, 0),\n                }),\n                multiview_mask: self.multiview_mask,\n                timestamp_writes: None,\n                occlusion_query_set: None,\n            };\n            unsafe {\n                raw.begin_render_pass(&desc)\n                    .map_err(|e| device.handle_hal_error(e))?;\n                raw.end_render_pass();\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl Global {\n    /// Creates a render pass.\n    ///\n    /// If creation fails, an invalid pass is returned. Attempting to record\n    /// commands into an invalid pass is permitted, but a validation error will\n    /// ultimately be generated when the parent encoder is finished, and it is\n    /// not possible to run any commands from the invalid pass.\n    ///\n    /// If successful, puts the encoder into the [`Locked`] state.\n    ///\n    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked\n    pub fn command_encoder_begin_render_pass(\n        &self,\n        encoder_id: id::CommandEncoderId,\n        desc: &RenderPassDescriptor<'_>,\n    ) -> (RenderPass, Option<CommandEncoderError>) {\n        use EncoderStateError as SErr;\n\n        fn fill_arc_desc(\n            hub: &crate::hub::Hub,\n            desc: &RenderPassDescriptor<'_>,\n            arc_desc: &mut ArcRenderPassDescriptor,\n            device: &Device,\n        ) -> Result<(), RenderPassErrorInner> {\n            device.check_is_valid()?;\n\n            let query_sets = hub.query_sets.read();\n            let texture_views = hub.texture_views.read();\n\n            let max_color_attachments = device.limits.max_color_attachments as usize;\n            if desc.color_attachments.len() > max_color_attachments {\n                return Err(RenderPassErrorInner::ColorAttachment(\n                    ColorAttachmentError::TooMany {\n                        given: desc.color_attachments.len(),\n                        limit: max_color_attachments,\n                    },\n                ));\n            }\n\n            for color_attachment in desc.color_attachments.iter() {\n                if let Some(RenderPassColorAttachment {\n                    view: view_id,\n                    depth_slice,\n                    resolve_target,\n                    load_op,\n                    store_op,\n                }) = color_attachment\n                {\n                    let view = texture_views.get(*view_id).get()?;\n                    view.same_device(device)?;\n\n                    if view.desc.usage.contains(TextureUsages::TRANSIENT)\n                        && *store_op != StoreOp::Discard\n                    {\n                        return Err(RenderPassErrorInner::ColorAttachment(\n                            ColorAttachmentError::InvalidUsageForStoreOp(\n                                TextureUsages::TRANSIENT,\n                                StoreOp::Discard,\n                                *store_op,\n                            ),\n                        ));\n                    }\n\n                    let resolve_target = if let Some(resolve_target_id) = resolve_target {\n                        let rt_arc = texture_views.get(*resolve_target_id).get()?;\n                        rt_arc.same_device(device)?;\n\n                        Some(rt_arc)\n                    } else {\n                        None\n                    };\n\n                    arc_desc\n                        .color_attachments\n                        .push(Some(ArcRenderPassColorAttachment {\n                            view,\n                            depth_slice: *depth_slice,\n                            resolve_target,\n                            load_op: *load_op,\n                            store_op: *store_op,\n                        }));\n                } else {\n                    arc_desc.color_attachments.push(None);\n                }\n            }\n\n            arc_desc.depth_stencil_attachment =\n            // https://gpuweb.github.io/gpuweb/#abstract-opdef-gpurenderpassdepthstencilattachment-gpurenderpassdepthstencilattachment-valid-usage\n                if let Some(depth_stencil_attachment) = desc.depth_stencil_attachment {\n                    let view = texture_views.get(depth_stencil_attachment.view).get()?;\n                    view.same_device(device)?;\n\n                    let format = view.desc.format;\n                    if !format.is_depth_stencil_format() {\n                        return Err(RenderPassErrorInner::InvalidAttachment(AttachmentError::InvalidDepthStencilAttachmentFormat(\n                            view.desc.format,\n                        )));\n                    }\n\n                    Some(ResolvedRenderPassDepthStencilAttachment {\n                        view,\n                        depth: if format.has_depth_aspect() {\n                            depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear {\n                                // If this.depthLoadOp is \"clear\", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive.\n                                if !(0.0..=1.0).contains(&clear) {\n                                    Err(AttachmentError::ClearValueOutOfRange(clear))\n                                } else {\n                                    Ok(clear)\n                                }\n                            } else {\n                                Err(AttachmentError::NoClearValue)\n                            })?\n                        } else {\n                            ResolvedPassChannel::ReadOnly\n                        },\n                        stencil: if format.has_stencil_aspect() {\n                            depth_stencil_attachment.stencil.resolve(|clear| Ok(clear.unwrap_or_default()))?\n                        } else {\n                            ResolvedPassChannel::ReadOnly\n                        },\n                    })\n                } else {\n                    None\n                };\n\n            arc_desc.timestamp_writes = desc\n                .timestamp_writes\n                .map(|tw| {\n                    Global::validate_pass_timestamp_writes::<RenderPassErrorInner>(\n                        device,\n                        &query_sets,\n                        tw,\n                    )\n                })\n                .transpose()?;\n\n            arc_desc.occlusion_query_set =\n                if let Some(occlusion_query_set) = desc.occlusion_query_set {\n                    let query_set = query_sets.get(occlusion_query_set).get()?;\n                    query_set.same_device(device)?;\n\n                    if !matches!(query_set.desc.ty, wgt::QueryType::Occlusion) {\n                        return Err(QueryUseError::IncompatibleType {\n                            set_type: query_set.desc.ty.into(),\n                            query_type: super::SimplifiedQueryType::Occlusion,\n                        }\n                        .into());\n                    }\n\n                    Some(query_set)\n                } else {\n                    None\n                };\n\n            arc_desc.multiview_mask = desc.multiview_mask;\n\n            Ok(())\n        }\n\n        let scope = PassErrorScope::Pass;\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        match cmd_buf_data.lock_encoder() {\n            Ok(()) => {\n                drop(cmd_buf_data);\n                let mut arc_desc = ArcRenderPassDescriptor {\n                    label: &desc.label,\n                    timestamp_writes: None,\n                    color_attachments: ArrayVec::new(),\n                    depth_stencil_attachment: None,\n                    occlusion_query_set: None,\n                    multiview_mask: None,\n                };\n                match fill_arc_desc(hub, desc, &mut arc_desc, &cmd_enc.device) {\n                    Ok(()) => (RenderPass::new(cmd_enc, arc_desc), None),\n                    Err(err) => (\n                        RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),\n                        None,\n                    ),\n                }\n            }\n            Err(err @ SErr::Locked) => {\n                // Attempting to open a new pass while the encoder is locked\n                // invalidates the encoder, but does not generate a validation\n                // error.\n                cmd_buf_data.invalidate(err.clone());\n                drop(cmd_buf_data);\n                (\n                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),\n                    None,\n                )\n            }\n            Err(err @ (SErr::Ended | SErr::Submitted)) => {\n                // Attempting to open a new pass after the encode has ended\n                // generates an immediate validation error.\n                drop(cmd_buf_data);\n                (\n                    RenderPass::new_invalid(cmd_enc, &desc.label, err.clone().map_pass_err(scope)),\n                    Some(err.into()),\n                )\n            }\n            Err(err @ SErr::Invalid) => {\n                // Passes can be opened even on an invalid encoder. Such passes\n                // are even valid, but since there's no visible side-effect of\n                // the pass being valid and there's no point in storing recorded\n                // commands that will ultimately be discarded, we open an\n                // invalid pass to save that work.\n                drop(cmd_buf_data);\n                (\n                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),\n                    None,\n                )\n            }\n            Err(SErr::Unlocked) => {\n                unreachable!(\"lock_encoder cannot fail due to the encoder being unlocked\")\n            }\n        }\n    }\n\n    pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> {\n        profiling::scope!(\n            \"CommandEncoder::run_render_pass {}\",\n            pass.base.label.as_deref().unwrap_or(\"\")\n        );\n\n        let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.unlock_encoder()?;\n\n        let base = pass.base.take();\n\n        if let Err(RenderPassError {\n            inner:\n                RenderPassErrorInner::EncoderState(\n                    err @ (EncoderStateError::Locked | EncoderStateError::Ended),\n                ),\n            scope: _,\n        }) = base\n        {\n            // Most encoding errors are detected and raised within `finish()`.\n            //\n            // However, we raise a validation error here if the pass was opened\n            // within another pass, or on a finished encoder. The latter is\n            // particularly important, because in that case reporting errors via\n            // `CommandEncoder::finish` is not possible.\n            return Err(err.clone());\n        }\n\n        cmd_buf_data.push_with(|| -> Result<_, RenderPassError> {\n            Ok(ArcCommand::RunRenderPass {\n                pass: base?,\n                color_attachments: SmallVec::from(pass.color_attachments.as_slice()),\n                depth_stencil_attachment: pass.depth_stencil_attachment.take(),\n                timestamp_writes: pass.timestamp_writes.take(),\n                occlusion_query_set: pass.occlusion_query_set.take(),\n                multiview_mask: pass.multiview_mask,\n            })\n        })\n    }\n}\n\npub(super) fn encode_render_pass(\n    parent_state: &mut EncodingState<InnerCommandEncoder>,\n    mut base: BasePass<ArcRenderCommand, Infallible>,\n    color_attachments: ColorAttachments<Arc<TextureView>>,\n    mut depth_stencil_attachment: Option<\n        ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,\n    >,\n    mut timestamp_writes: Option<ArcPassTimestampWrites>,\n    occlusion_query_set: Option<Arc<QuerySet>>,\n    multiview_mask: Option<NonZeroU32>,\n) -> Result<(), RenderPassError> {\n    let pass_scope = PassErrorScope::Pass;\n\n    let device = parent_state.device;\n\n    let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new();\n\n    // We automatically keep extending command buffers over time, and because\n    // we want to insert a command buffer _before_ what we're about to record,\n    // we need to make sure to close the previous one.\n    parent_state\n        .raw_encoder\n        .close_if_open()\n        .map_pass_err(pass_scope)?;\n    let raw_encoder = parent_state\n        .raw_encoder\n        .open_pass(base.label.as_deref())\n        .map_pass_err(pass_scope)?;\n\n    let (scope, pending_discard_init_fixups, mut pending_query_resets) = {\n        let mut pending_query_resets = QueryResetMap::new();\n        let mut pending_discard_init_fixups = SurfacesInDiscardState::new();\n\n        let info = RenderPassInfo::start(\n            device,\n            hal_label(base.label.as_deref(), device.instance_flags),\n            &color_attachments,\n            depth_stencil_attachment.take(),\n            timestamp_writes.take(),\n            // Still needed down the line.\n            // TODO(wumpf): by restructuring the code, we could get rid of some of this Arc clone.\n            occlusion_query_set.clone(),\n            raw_encoder,\n            parent_state.tracker,\n            parent_state.texture_memory_actions,\n            &mut pending_query_resets,\n            &mut pending_discard_init_fixups,\n            parent_state.snatch_guard,\n            multiview_mask,\n        )\n        .map_pass_err(pass_scope)?;\n\n        let indices = &device.tracker_indices;\n        parent_state\n            .tracker\n            .buffers\n            .set_size(indices.buffers.size());\n        parent_state\n            .tracker\n            .textures\n            .set_size(indices.textures.size());\n\n        let mut debug_scope_depth = 0;\n\n        let mut state = State {\n            pipeline_flags: PipelineFlags::empty(),\n            blend_constant: OptionalState::Unused,\n            stencil_reference: 0,\n            pipeline: None,\n            index: IndexState::default(),\n            vertex: VertexState::default(),\n\n            info,\n\n            pass: pass::PassState {\n                base: EncodingState {\n                    device,\n                    raw_encoder,\n                    tracker: parent_state.tracker,\n                    buffer_memory_init_actions: parent_state.buffer_memory_init_actions,\n                    texture_memory_actions: parent_state.texture_memory_actions,\n                    as_actions: parent_state.as_actions,\n                    temp_resources: parent_state.temp_resources,\n                    indirect_draw_validation_resources: parent_state\n                        .indirect_draw_validation_resources,\n                    snatch_guard: parent_state.snatch_guard,\n                    debug_scope_depth: &mut debug_scope_depth,\n                },\n                pending_discard_init_fixups,\n                scope: device.new_usage_scope(),\n                binder: Binder::new(),\n\n                temp_offsets: Vec::new(),\n                dynamic_offset_count: 0,\n\n                string_offset: 0,\n            },\n\n            active_occlusion_query: None,\n            active_pipeline_statistics_query: None,\n        };\n\n        for command in base.commands.drain(..) {\n            match command {\n                ArcRenderCommand::SetBindGroup {\n                    index,\n                    num_dynamic_offsets,\n                    bind_group,\n                } => {\n                    let scope = PassErrorScope::SetBindGroup;\n                    pass::set_bind_group::<RenderPassErrorInner>(\n                        &mut state.pass,\n                        device,\n                        &base.dynamic_offsets,\n                        index,\n                        num_dynamic_offsets,\n                        bind_group,\n                        true,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetPipeline(pipeline) => {\n                    let scope = PassErrorScope::SetPipelineRender;\n                    set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetIndexBuffer {\n                    buffer,\n                    index_format,\n                    offset,\n                    size,\n                } => {\n                    let scope = PassErrorScope::SetIndexBuffer;\n                    set_index_buffer(&mut state, device, buffer, index_format, offset, size)\n                        .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetVertexBuffer {\n                    slot,\n                    buffer,\n                    offset,\n                    size,\n                } => {\n                    let scope = PassErrorScope::SetVertexBuffer;\n                    set_vertex_buffer(&mut state, device, slot, buffer, offset, size)\n                        .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetBlendConstant(ref color) => {\n                    set_blend_constant(&mut state, color);\n                }\n                ArcRenderCommand::SetStencilReference(value) => {\n                    set_stencil_reference(&mut state, value);\n                }\n                ArcRenderCommand::SetViewport {\n                    rect,\n                    depth_min,\n                    depth_max,\n                } => {\n                    let scope = PassErrorScope::SetViewport;\n                    set_viewport(&mut state, rect, depth_min, depth_max).map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetImmediate {\n                    offset,\n                    size_bytes,\n                    values_offset,\n                } => {\n                    let scope = PassErrorScope::SetImmediate;\n                    pass::set_immediates::<RenderPassErrorInner, _>(\n                        &mut state.pass,\n                        &base.immediates_data,\n                        offset,\n                        size_bytes,\n                        values_offset,\n                        |_| {},\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::SetScissor(rect) => {\n                    let scope = PassErrorScope::SetScissorRect;\n                    set_scissor(&mut state, rect).map_pass_err(scope)?;\n                }\n                ArcRenderCommand::Draw {\n                    vertex_count,\n                    instance_count,\n                    first_vertex,\n                    first_instance,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::Draw,\n                    };\n                    draw(\n                        &mut state,\n                        vertex_count,\n                        instance_count,\n                        first_vertex,\n                        first_instance,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::DrawIndexed {\n                    index_count,\n                    instance_count,\n                    first_index,\n                    base_vertex,\n                    first_instance,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::DrawIndexed,\n                    };\n                    draw_indexed(\n                        &mut state,\n                        index_count,\n                        instance_count,\n                        first_index,\n                        base_vertex,\n                        first_instance,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::DrawMeshTasks {\n                    group_count_x,\n                    group_count_y,\n                    group_count_z,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::Draw,\n                        family: DrawCommandFamily::DrawMeshTasks,\n                    };\n                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)\n                        .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::DrawIndirect {\n                    buffer,\n                    offset,\n                    count,\n                    family,\n\n                    vertex_or_index_limit: _,\n                    instance_limit: _,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: if count != 1 {\n                            DrawKind::MultiDrawIndirect\n                        } else {\n                            DrawKind::DrawIndirect\n                        },\n                        family,\n                    };\n                    multi_draw_indirect(\n                        &mut state,\n                        &mut indirect_draw_validation_batcher,\n                        device,\n                        buffer,\n                        offset,\n                        count,\n                        family,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::MultiDrawIndirectCount {\n                    buffer,\n                    offset,\n                    count_buffer,\n                    count_buffer_offset,\n                    max_count,\n                    family,\n                } => {\n                    let scope = PassErrorScope::Draw {\n                        kind: DrawKind::MultiDrawIndirectCount,\n                        family,\n                    };\n                    multi_draw_indirect_count(\n                        &mut state,\n                        device,\n                        buffer,\n                        offset,\n                        count_buffer,\n                        count_buffer_offset,\n                        max_count,\n                        family,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::PushDebugGroup { color: _, len } => {\n                    pass::push_debug_group(&mut state.pass, &base.string_data, len);\n                }\n                ArcRenderCommand::PopDebugGroup => {\n                    let scope = PassErrorScope::PopDebugGroup;\n                    pass::pop_debug_group::<RenderPassErrorInner>(&mut state.pass)\n                        .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::InsertDebugMarker { color: _, len } => {\n                    pass::insert_debug_marker(&mut state.pass, &base.string_data, len);\n                }\n                ArcRenderCommand::WriteTimestamp {\n                    query_set,\n                    query_index,\n                } => {\n                    let scope = PassErrorScope::WriteTimestamp;\n                    pass::write_timestamp::<RenderPassErrorInner>(\n                        &mut state.pass,\n                        device,\n                        Some(&mut pending_query_resets),\n                        query_set,\n                        query_index,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::BeginOcclusionQuery { query_index } => {\n                    api_log!(\"RenderPass::begin_occlusion_query {query_index}\");\n                    let scope = PassErrorScope::BeginOcclusionQuery;\n\n                    let query_set = occlusion_query_set\n                        .clone()\n                        .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)\n                        .map_pass_err(scope)?;\n\n                    validate_and_begin_occlusion_query(\n                        query_set,\n                        state.pass.base.raw_encoder,\n                        &mut state.pass.base.tracker.query_sets,\n                        query_index,\n                        Some(&mut pending_query_resets),\n                        &mut state.active_occlusion_query,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::EndOcclusionQuery => {\n                    api_log!(\"RenderPass::end_occlusion_query\");\n                    let scope = PassErrorScope::EndOcclusionQuery;\n\n                    end_occlusion_query(\n                        state.pass.base.raw_encoder,\n                        &mut state.active_occlusion_query,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::BeginPipelineStatisticsQuery {\n                    query_set,\n                    query_index,\n                } => {\n                    api_log!(\n                        \"RenderPass::begin_pipeline_statistics_query {query_index} {}\",\n                        query_set.error_ident()\n                    );\n                    let scope = PassErrorScope::BeginPipelineStatisticsQuery;\n\n                    validate_and_begin_pipeline_statistics_query(\n                        query_set,\n                        state.pass.base.raw_encoder,\n                        &mut state.pass.base.tracker.query_sets,\n                        device,\n                        query_index,\n                        Some(&mut pending_query_resets),\n                        &mut state.active_pipeline_statistics_query,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::EndPipelineStatisticsQuery => {\n                    api_log!(\"RenderPass::end_pipeline_statistics_query\");\n                    let scope = PassErrorScope::EndPipelineStatisticsQuery;\n\n                    end_pipeline_statistics_query(\n                        state.pass.base.raw_encoder,\n                        &mut state.active_pipeline_statistics_query,\n                    )\n                    .map_pass_err(scope)?;\n                }\n                ArcRenderCommand::ExecuteBundle(bundle) => {\n                    let scope = PassErrorScope::ExecuteBundle;\n                    execute_bundle(\n                        &mut state,\n                        &mut indirect_draw_validation_batcher,\n                        device,\n                        bundle,\n                    )\n                    .map_pass_err(scope)?;\n                }\n            }\n        }\n\n        if *state.pass.base.debug_scope_depth > 0 {\n            Err(\n                RenderPassErrorInner::DebugGroupError(DebugGroupError::MissingPop)\n                    .map_pass_err(pass_scope),\n            )?;\n        }\n        if state.active_occlusion_query.is_some() {\n            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {\n                query_type: super::SimplifiedQueryType::Occlusion,\n            })\n            .map_pass_err(pass_scope))?;\n        }\n        if state.active_pipeline_statistics_query.is_some() {\n            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {\n                query_type: super::SimplifiedQueryType::PipelineStatistics,\n            })\n            .map_pass_err(pass_scope))?;\n        }\n\n        state\n            .info\n            .finish(\n                device,\n                state.pass.base.raw_encoder,\n                state.pass.base.snatch_guard,\n                &mut state.pass.scope,\n                device.instance_flags,\n            )\n            .map_pass_err(pass_scope)?;\n\n        let trackers = state.pass.scope;\n\n        let pending_discard_init_fixups = state.pass.pending_discard_init_fixups;\n\n        parent_state.raw_encoder.close().map_pass_err(pass_scope)?;\n        (trackers, pending_discard_init_fixups, pending_query_resets)\n    };\n\n    let encoder = &mut parent_state.raw_encoder;\n    let tracker = &mut parent_state.tracker;\n\n    {\n        let transit = encoder\n            .open_pass(hal_label(\n                Some(\"(wgpu internal) Pre Pass\"),\n                device.instance_flags,\n            ))\n            .map_pass_err(pass_scope)?;\n\n        fixup_discarded_surfaces(\n            pending_discard_init_fixups.into_iter(),\n            transit,\n            &mut tracker.textures,\n            device,\n            parent_state.snatch_guard,\n        );\n\n        pending_query_resets.reset_queries(transit);\n\n        CommandEncoder::insert_barriers_from_scope(\n            transit,\n            tracker,\n            &scope,\n            parent_state.snatch_guard,\n        );\n\n        if let Some(ref indirect_validation) = device.indirect_validation {\n            indirect_validation\n                .draw\n                .inject_validation_pass(\n                    device,\n                    parent_state.snatch_guard,\n                    parent_state.indirect_draw_validation_resources,\n                    parent_state.temp_resources,\n                    transit,\n                    indirect_draw_validation_batcher,\n                )\n                .map_pass_err(pass_scope)?;\n        }\n    }\n\n    encoder.close_and_swap().map_pass_err(pass_scope)?;\n\n    Ok(())\n}\n\nfn set_pipeline(\n    state: &mut State,\n    device: &Arc<Device>,\n    pipeline: Arc<RenderPipeline>,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::set_pipeline {}\", pipeline.error_ident());\n\n    state.pipeline = Some(pipeline.clone());\n\n    let pipeline = state\n        .pass\n        .base\n        .tracker\n        .render_pipelines\n        .insert_single(pipeline)\n        .clone();\n\n    pipeline.same_device(device)?;\n\n    state\n        .info\n        .context\n        .check_compatible(&pipeline.pass_context, pipeline.as_ref())\n        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;\n\n    state.pipeline_flags = pipeline.flags;\n\n    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && state.info.is_depth_read_only {\n        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());\n    }\n    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && state.info.is_stencil_read_only {\n        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());\n    }\n\n    state\n        .blend_constant\n        .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));\n\n    unsafe {\n        state\n            .pass\n            .base\n            .raw_encoder\n            .set_render_pipeline(pipeline.raw());\n    }\n\n    if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {\n        unsafe {\n            state\n                .pass\n                .base\n                .raw_encoder\n                .set_stencil_reference(state.stencil_reference);\n        }\n    }\n\n    // Rebind resource\n    pass::change_pipeline_layout::<RenderPassErrorInner, _>(\n        &mut state.pass,\n        &pipeline.layout,\n        &pipeline.late_sized_buffer_groups,\n        || {},\n    )?;\n\n    // Update vertex buffer limits.\n    state.vertex.update_limits(&pipeline.vertex_steps);\n    Ok(())\n}\n\n// This function is duplicative of `bundle::set_index_buffer`.\nfn set_index_buffer(\n    state: &mut State,\n    device: &Arc<Device>,\n    buffer: Arc<crate::resource::Buffer>,\n    index_format: IndexFormat,\n    offset: u64,\n    size: Option<BufferSize>,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::set_index_buffer {}\", buffer.error_ident());\n\n    state\n        .pass\n        .scope\n        .buffers\n        .merge_single(&buffer, wgt::BufferUses::INDEX)?;\n\n    buffer.same_device(device)?;\n\n    buffer.check_usage(BufferUsages::INDEX)?;\n\n    if !offset.is_multiple_of(u64::try_from(index_format.byte_size()).unwrap()) {\n        return Err(RenderCommandError::UnalignedIndexBuffer {\n            offset,\n            alignment: index_format.byte_size(),\n        }\n        .into());\n    }\n    let (binding, resolved_size) = buffer\n        .binding(offset, size, state.pass.base.snatch_guard)\n        .map_err(RenderCommandError::from)?;\n    let end = offset + resolved_size;\n    state.index.update_buffer(offset..end, index_format);\n\n    state.pass.base.buffer_memory_init_actions.extend(\n        buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..end,\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    unsafe {\n        hal::DynCommandEncoder::set_index_buffer(\n            state.pass.base.raw_encoder,\n            binding,\n            index_format,\n        );\n    }\n    Ok(())\n}\n\n// This function is duplicative of `render::set_vertex_buffer`.\nfn set_vertex_buffer(\n    state: &mut State,\n    device: &Arc<Device>,\n    slot: u32,\n    buffer: Arc<crate::resource::Buffer>,\n    offset: u64,\n    size: Option<BufferSize>,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\n        \"RenderPass::set_vertex_buffer {slot} {}\",\n        buffer.error_ident()\n    );\n\n    state\n        .pass\n        .scope\n        .buffers\n        .merge_single(&buffer, wgt::BufferUses::VERTEX)?;\n\n    buffer.same_device(device)?;\n\n    let max_vertex_buffers = state.pass.base.device.limits.max_vertex_buffers;\n    if slot >= max_vertex_buffers {\n        return Err(RenderCommandError::VertexBufferIndexOutOfRange {\n            index: slot,\n            max: max_vertex_buffers,\n        }\n        .into());\n    }\n\n    buffer.check_usage(BufferUsages::VERTEX)?;\n\n    if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {\n        return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());\n    }\n    let (binding, buffer_size) = buffer\n        .binding(offset, size, state.pass.base.snatch_guard)\n        .map_err(RenderCommandError::from)?;\n    state.vertex.buffer_sizes[slot as usize] = Some(buffer_size);\n\n    state.pass.base.buffer_memory_init_actions.extend(\n        buffer.initialization_status.read().create_action(\n            &buffer,\n            offset..(offset + buffer_size),\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    unsafe {\n        hal::DynCommandEncoder::set_vertex_buffer(state.pass.base.raw_encoder, slot, binding);\n    }\n    if let Some(pipeline) = state.pipeline.as_ref() {\n        state.vertex.update_limits(&pipeline.vertex_steps);\n    }\n    Ok(())\n}\n\nfn set_blend_constant(state: &mut State, color: &Color) {\n    api_log!(\"RenderPass::set_blend_constant\");\n\n    state.blend_constant = OptionalState::Set;\n    let array = [\n        color.r as f32,\n        color.g as f32,\n        color.b as f32,\n        color.a as f32,\n    ];\n    unsafe {\n        state.pass.base.raw_encoder.set_blend_constants(&array);\n    }\n}\n\nfn set_stencil_reference(state: &mut State, value: u32) {\n    api_log!(\"RenderPass::set_stencil_reference {value}\");\n\n    state.stencil_reference = value;\n    if state\n        .pipeline_flags\n        .contains(PipelineFlags::STENCIL_REFERENCE)\n    {\n        unsafe {\n            state.pass.base.raw_encoder.set_stencil_reference(value);\n        }\n    }\n}\n\nfn set_viewport(\n    state: &mut State,\n    rect: Rect<f32>,\n    depth_min: f32,\n    depth_max: f32,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::set_viewport {rect:?}\");\n\n    if rect.w < 0.0\n        || rect.h < 0.0\n        || rect.w > state.pass.base.device.limits.max_texture_dimension_2d as f32\n        || rect.h > state.pass.base.device.limits.max_texture_dimension_2d as f32\n    {\n        return Err(RenderCommandError::InvalidViewportRectSize {\n            w: rect.w,\n            h: rect.h,\n            max: state.pass.base.device.limits.max_texture_dimension_2d,\n        }\n        .into());\n    }\n\n    let max_viewport_range = state.pass.base.device.limits.max_texture_dimension_2d as f32 * 2.0;\n\n    if rect.x < -max_viewport_range\n        || rect.y < -max_viewport_range\n        || rect.x + rect.w > max_viewport_range - 1.0\n        || rect.y + rect.h > max_viewport_range - 1.0\n    {\n        return Err(RenderCommandError::InvalidViewportRectPosition {\n            rect,\n            min: -max_viewport_range,\n            max: max_viewport_range - 1.0,\n        }\n        .into());\n    }\n    if !(0.0..=1.0).contains(&depth_min)\n        || !(0.0..=1.0).contains(&depth_max)\n        || depth_min > depth_max\n    {\n        return Err(RenderCommandError::InvalidViewportDepth(depth_min, depth_max).into());\n    }\n    let r = hal::Rect {\n        x: rect.x,\n        y: rect.y,\n        w: rect.w,\n        h: rect.h,\n    };\n    unsafe {\n        state\n            .pass\n            .base\n            .raw_encoder\n            .set_viewport(&r, depth_min..depth_max);\n    }\n    Ok(())\n}\n\nfn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::set_scissor_rect {rect:?}\");\n\n    if rect.x.saturating_add(rect.w) > state.info.extent.width\n        || rect.y.saturating_add(rect.h) > state.info.extent.height\n    {\n        return Err(RenderCommandError::InvalidScissorRect(rect, state.info.extent).into());\n    }\n    let r = hal::Rect {\n        x: rect.x,\n        y: rect.y,\n        w: rect.w,\n        h: rect.h,\n    };\n    unsafe {\n        state.pass.base.raw_encoder.set_scissor_rect(&r);\n    }\n    Ok(())\n}\n\nfn validate_mesh_draw_multiview(state: &State) -> Result<(), RenderPassErrorInner> {\n    if let Some(mv) = state.info.multiview_mask {\n        let highest_bit = 31 - mv.leading_zeros();\n\n        let features = state.pass.base.device.features;\n\n        if !features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)\n            || highest_bit > state.pass.base.device.limits.max_mesh_multiview_view_count\n        {\n            return Err(RenderPassErrorInner::Draw(\n                DrawError::MeshPipelineMultiviewLimitsViolated {\n                    highest_view_index: highest_bit,\n                    max_multiviews: state.pass.base.device.limits.max_mesh_multiview_view_count,\n                },\n            ));\n        }\n    }\n    Ok(())\n}\n\nfn draw(\n    state: &mut State,\n    vertex_count: u32,\n    instance_count: u32,\n    first_vertex: u32,\n    first_instance: u32,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}\");\n\n    state.is_ready(DrawCommandFamily::Draw)?;\n    state.flush_bindings()?;\n\n    state\n        .vertex\n        .limits\n        .validate_vertex_limit(first_vertex, vertex_count)?;\n    state\n        .vertex\n        .limits\n        .validate_instance_limit(first_instance, instance_count)?;\n\n    unsafe {\n        if instance_count > 0 && vertex_count > 0 {\n            state.pass.base.raw_encoder.draw(\n                first_vertex,\n                vertex_count,\n                first_instance,\n                instance_count,\n            );\n        }\n    }\n    Ok(())\n}\n\nfn draw_indexed(\n    state: &mut State,\n    index_count: u32,\n    instance_count: u32,\n    first_index: u32,\n    base_vertex: i32,\n    first_instance: u32,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}\");\n\n    state.is_ready(DrawCommandFamily::DrawIndexed)?;\n    state.flush_bindings()?;\n\n    let last_index = first_index as u64 + index_count as u64;\n    let index_limit = state.index.limit;\n    if last_index > index_limit {\n        return Err(DrawError::IndexBeyondLimit {\n            last_index,\n            index_limit,\n        }\n        .into());\n    }\n    state\n        .vertex\n        .limits\n        .validate_instance_limit(first_instance, instance_count)?;\n\n    unsafe {\n        if instance_count > 0 && index_count > 0 {\n            state.pass.base.raw_encoder.draw_indexed(\n                first_index,\n                index_count,\n                base_vertex,\n                first_instance,\n                instance_count,\n            );\n        }\n    }\n    Ok(())\n}\n\nfn draw_mesh_tasks(\n    state: &mut State,\n    group_count_x: u32,\n    group_count_y: u32,\n    group_count_z: u32,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}\");\n\n    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;\n\n    state.flush_bindings()?;\n    validate_mesh_draw_multiview(state)?;\n\n    let groups_size_limit = state\n        .pass\n        .base\n        .device\n        .limits\n        .max_task_mesh_workgroups_per_dimension;\n    let max_groups = state\n        .pass\n        .base\n        .device\n        .limits\n        .max_task_mesh_workgroup_total_count;\n    if group_count_x > groups_size_limit\n        || group_count_y > groups_size_limit\n        || group_count_z > groups_size_limit\n        || group_count_x * group_count_y * group_count_z > max_groups\n    {\n        return Err(DrawError::InvalidGroupSize {\n            current: [group_count_x, group_count_y, group_count_z],\n            limit: groups_size_limit,\n            max_total: max_groups,\n        }\n        .into());\n    }\n\n    unsafe {\n        if group_count_x > 0 && group_count_y > 0 && group_count_z > 0 {\n            state.pass.base.raw_encoder.draw_mesh_tasks(\n                group_count_x,\n                group_count_y,\n                group_count_z,\n            );\n        }\n    }\n    Ok(())\n}\n\nfn multi_draw_indirect(\n    state: &mut State,\n    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,\n    device: &Arc<Device>,\n    indirect_buffer: Arc<crate::resource::Buffer>,\n    offset: u64,\n    count: u32,\n    family: DrawCommandFamily,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\n        \"RenderPass::draw_indirect (family:{family:?}) {} {offset} {count:?}\",\n        indirect_buffer.error_ident()\n    );\n\n    state.is_ready(family)?;\n    state.flush_bindings()?;\n\n    if family == DrawCommandFamily::DrawMeshTasks {\n        validate_mesh_draw_multiview(state)?;\n    }\n\n    state\n        .pass\n        .base\n        .device\n        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;\n\n    indirect_buffer.same_device(device)?;\n    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;\n    indirect_buffer.check_destroyed(state.pass.base.snatch_guard)?;\n\n    if !offset.is_multiple_of(4) {\n        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));\n    }\n\n    let stride = get_stride_of_indirect_args(family);\n\n    let end_offset = offset + stride * count as u64;\n    if end_offset > indirect_buffer.size {\n        return Err(RenderPassErrorInner::IndirectBufferOverrun {\n            count,\n            offset,\n            end_offset,\n            buffer_size: indirect_buffer.size,\n        });\n    }\n\n    state.pass.base.buffer_memory_init_actions.extend(\n        indirect_buffer.initialization_status.read().create_action(\n            &indirect_buffer,\n            offset..end_offset,\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    fn draw(\n        raw_encoder: &mut dyn hal::DynCommandEncoder,\n        family: DrawCommandFamily,\n        indirect_buffer: &dyn hal::DynBuffer,\n        offset: u64,\n        count: u32,\n    ) {\n        match family {\n            DrawCommandFamily::Draw => unsafe {\n                raw_encoder.draw_indirect(indirect_buffer, offset, count);\n            },\n            DrawCommandFamily::DrawIndexed => unsafe {\n                raw_encoder.draw_indexed_indirect(indirect_buffer, offset, count);\n            },\n            DrawCommandFamily::DrawMeshTasks => unsafe {\n                raw_encoder.draw_mesh_tasks_indirect(indirect_buffer, offset, count);\n            },\n        }\n    }\n\n    if state.pass.base.device.indirect_validation.is_some() {\n        state\n            .pass\n            .scope\n            .buffers\n            .merge_single(&indirect_buffer, wgt::BufferUses::STORAGE_READ_ONLY)?;\n\n        struct DrawData {\n            buffer_index: usize,\n            offset: u64,\n            count: u32,\n        }\n\n        struct DrawContext<'a> {\n            raw_encoder: &'a mut dyn hal::DynCommandEncoder,\n            device: &'a Device,\n\n            indirect_draw_validation_resources: &'a mut crate::indirect_validation::DrawResources,\n            indirect_draw_validation_batcher: &'a mut crate::indirect_validation::DrawBatcher,\n\n            indirect_buffer: Arc<crate::resource::Buffer>,\n            family: DrawCommandFamily,\n            vertex_or_index_limit: u64,\n            instance_limit: u64,\n        }\n\n        impl<'a> DrawContext<'a> {\n            fn add(&mut self, offset: u64) -> Result<DrawData, DeviceError> {\n                let (dst_resource_index, dst_offset) = self.indirect_draw_validation_batcher.add(\n                    self.indirect_draw_validation_resources,\n                    self.device,\n                    &self.indirect_buffer,\n                    offset,\n                    self.family,\n                    self.vertex_or_index_limit,\n                    self.instance_limit,\n                )?;\n                Ok(DrawData {\n                    buffer_index: dst_resource_index,\n                    offset: dst_offset,\n                    count: 1,\n                })\n            }\n            fn draw(&mut self, draw_data: DrawData) {\n                let dst_buffer = self\n                    .indirect_draw_validation_resources\n                    .get_dst_buffer(draw_data.buffer_index);\n                draw(\n                    self.raw_encoder,\n                    self.family,\n                    dst_buffer,\n                    draw_data.offset,\n                    draw_data.count,\n                );\n            }\n        }\n\n        let mut draw_ctx = DrawContext {\n            raw_encoder: state.pass.base.raw_encoder,\n            device: state.pass.base.device,\n            indirect_draw_validation_resources: state.pass.base.indirect_draw_validation_resources,\n            indirect_draw_validation_batcher,\n            indirect_buffer,\n            family,\n            vertex_or_index_limit: if family == DrawCommandFamily::DrawIndexed {\n                state.index.limit\n            } else {\n                state.vertex.limits.vertex_limit\n            },\n            instance_limit: state.vertex.limits.instance_limit,\n        };\n\n        let mut current_draw_data = draw_ctx.add(offset)?;\n\n        for i in 1..count {\n            let draw_data = draw_ctx.add(offset + stride * i as u64)?;\n\n            if draw_data.buffer_index == current_draw_data.buffer_index {\n                debug_assert_eq!(\n                    draw_data.offset,\n                    current_draw_data.offset + stride * current_draw_data.count as u64\n                );\n                current_draw_data.count += 1;\n            } else {\n                draw_ctx.draw(current_draw_data);\n                current_draw_data = draw_data;\n            }\n        }\n\n        draw_ctx.draw(current_draw_data);\n    } else {\n        state\n            .pass\n            .scope\n            .buffers\n            .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;\n\n        draw(\n            state.pass.base.raw_encoder,\n            family,\n            indirect_buffer.try_raw(state.pass.base.snatch_guard)?,\n            offset,\n            count,\n        );\n    };\n\n    Ok(())\n}\n\nfn multi_draw_indirect_count(\n    state: &mut State,\n    device: &Arc<Device>,\n    indirect_buffer: Arc<crate::resource::Buffer>,\n    offset: u64,\n    count_buffer: Arc<crate::resource::Buffer>,\n    count_buffer_offset: u64,\n    max_count: u32,\n    family: DrawCommandFamily,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\n        \"RenderPass::multi_draw_indirect_count (family:{family:?}) {} {offset} {} {count_buffer_offset:?} {max_count:?}\",\n        indirect_buffer.error_ident(),\n        count_buffer.error_ident()\n    );\n\n    state.is_ready(family)?;\n    state.flush_bindings()?;\n\n    if family == DrawCommandFamily::DrawMeshTasks {\n        validate_mesh_draw_multiview(state)?;\n    }\n\n    let stride = get_stride_of_indirect_args(family);\n\n    state\n        .pass\n        .base\n        .device\n        .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)?;\n    state\n        .pass\n        .base\n        .device\n        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;\n\n    indirect_buffer.same_device(device)?;\n    count_buffer.same_device(device)?;\n\n    state\n        .pass\n        .scope\n        .buffers\n        .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;\n\n    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;\n    let indirect_raw = indirect_buffer.try_raw(state.pass.base.snatch_guard)?;\n\n    state\n        .pass\n        .scope\n        .buffers\n        .merge_single(&count_buffer, wgt::BufferUses::INDIRECT)?;\n\n    count_buffer.check_usage(BufferUsages::INDIRECT)?;\n    let count_raw = count_buffer.try_raw(state.pass.base.snatch_guard)?;\n\n    if !offset.is_multiple_of(4) {\n        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));\n    }\n\n    let end_offset = offset + stride * max_count as u64;\n    if end_offset > indirect_buffer.size {\n        return Err(RenderPassErrorInner::IndirectBufferOverrun {\n            count: 1,\n            offset,\n            end_offset,\n            buffer_size: indirect_buffer.size,\n        });\n    }\n    state.pass.base.buffer_memory_init_actions.extend(\n        indirect_buffer.initialization_status.read().create_action(\n            &indirect_buffer,\n            offset..end_offset,\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    let begin_count_offset = count_buffer_offset;\n    let end_count_offset = count_buffer_offset + 4;\n    if end_count_offset > count_buffer.size {\n        return Err(RenderPassErrorInner::IndirectCountBufferOverrun {\n            begin_count_offset,\n            end_count_offset,\n            count_buffer_size: count_buffer.size,\n        });\n    }\n    state.pass.base.buffer_memory_init_actions.extend(\n        count_buffer.initialization_status.read().create_action(\n            &count_buffer,\n            count_buffer_offset..end_count_offset,\n            MemoryInitKind::NeedsInitializedMemory,\n        ),\n    );\n\n    match family {\n        DrawCommandFamily::Draw => unsafe {\n            state.pass.base.raw_encoder.draw_indirect_count(\n                indirect_raw,\n                offset,\n                count_raw,\n                count_buffer_offset,\n                max_count,\n            );\n        },\n        DrawCommandFamily::DrawIndexed => unsafe {\n            state.pass.base.raw_encoder.draw_indexed_indirect_count(\n                indirect_raw,\n                offset,\n                count_raw,\n                count_buffer_offset,\n                max_count,\n            );\n        },\n        DrawCommandFamily::DrawMeshTasks => unsafe {\n            state.pass.base.raw_encoder.draw_mesh_tasks_indirect_count(\n                indirect_raw,\n                offset,\n                count_raw,\n                count_buffer_offset,\n                max_count,\n            );\n        },\n    }\n    Ok(())\n}\n\nfn execute_bundle(\n    state: &mut State,\n    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,\n    device: &Arc<Device>,\n    bundle: Arc<super::RenderBundle>,\n) -> Result<(), RenderPassErrorInner> {\n    api_log!(\"RenderPass::execute_bundle {}\", bundle.error_ident());\n\n    let bundle = state.pass.base.tracker.bundles.insert_single(bundle);\n\n    bundle.same_device(device)?;\n\n    state\n        .info\n        .context\n        .check_compatible(&bundle.context, bundle.as_ref())\n        .map_err(RenderPassErrorInner::IncompatibleBundleTargets)?;\n\n    if (state.info.is_depth_read_only && !bundle.is_depth_read_only)\n        || (state.info.is_stencil_read_only && !bundle.is_stencil_read_only)\n    {\n        return Err(\n            RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {\n                pass_depth: state.info.is_depth_read_only,\n                pass_stencil: state.info.is_stencil_read_only,\n                bundle_depth: bundle.is_depth_read_only,\n                bundle_stencil: bundle.is_stencil_read_only,\n            },\n        );\n    }\n\n    state.pass.base.buffer_memory_init_actions.extend(\n        bundle\n            .buffer_memory_init_actions\n            .iter()\n            .filter_map(|action| {\n                action\n                    .buffer\n                    .initialization_status\n                    .read()\n                    .check_action(action)\n            }),\n    );\n    for action in bundle.texture_memory_init_actions.iter() {\n        state.pass.pending_discard_init_fixups.extend(\n            state\n                .pass\n                .base\n                .texture_memory_actions\n                .register_init_action(action),\n        );\n    }\n\n    unsafe {\n        bundle.execute(\n            state.pass.base.raw_encoder,\n            state.pass.base.indirect_draw_validation_resources,\n            indirect_draw_validation_batcher,\n            state.pass.base.snatch_guard,\n        )\n    }\n    .map_err(|e| match e {\n        ExecutionError::Device(e) => RenderPassErrorInner::Device(e),\n        ExecutionError::DestroyedResource(e) => {\n            RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(e))\n        }\n        ExecutionError::Unimplemented(what) => {\n            RenderPassErrorInner::RenderCommand(RenderCommandError::Unimplemented(what))\n        }\n    })?;\n\n    unsafe {\n        state.pass.scope.merge_render_bundle(&bundle.used)?;\n    };\n    state.reset_bundle();\n    Ok(())\n}\n\n// Recording a render pass.\n//\n// The only error that should be returned from these methods is\n// `EncoderStateError::Ended`, when the pass has already ended and an immediate\n// validation error is raised.\n//\n// All other errors should be stored in the pass for later reporting when\n// `CommandEncoder.finish()` is called.\n//\n// The `pass_try!` macro should be used to handle errors appropriately. Note\n// that the `pass_try!` and `pass_base!` macros may return early from the\n// function that invokes them, like the `?` operator.\nimpl Global {\n    pub fn render_pass_set_bind_group(\n        &self,\n        pass: &mut RenderPass,\n        index: u32,\n        bind_group_id: Option<id::BindGroupId>,\n        offsets: &[DynamicOffset],\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetBindGroup;\n\n        // This statement will return an error if the pass is ended. It's\n        // important the error check comes before the early-out for\n        // `set_and_check_redundant`.\n        let base = pass_base!(pass, scope);\n\n        if pass.current_bind_groups.set_and_check_redundant(\n            bind_group_id,\n            index,\n            &mut base.dynamic_offsets,\n            offsets,\n        ) {\n            return Ok(());\n        }\n\n        let mut bind_group = None;\n        if let Some(bind_group_id) = bind_group_id {\n            let hub = &self.hub;\n            bind_group = Some(pass_try!(\n                base,\n                scope,\n                hub.bind_groups.get(bind_group_id).get(),\n            ));\n        }\n\n        base.commands.push(ArcRenderCommand::SetBindGroup {\n            index,\n            num_dynamic_offsets: offsets.len(),\n            bind_group,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_pipeline(\n        &self,\n        pass: &mut RenderPass,\n        pipeline_id: id::RenderPipelineId,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetPipelineRender;\n\n        let redundant = pass.current_pipeline.set_and_check_redundant(pipeline_id);\n\n        // This statement will return an error if the pass is ended.\n        // Its important the error check comes before the early-out for `redundant`.\n        let base = pass_base!(pass, scope);\n\n        if redundant {\n            return Ok(());\n        }\n\n        let hub = &self.hub;\n        let pipeline = pass_try!(base, scope, hub.render_pipelines.get(pipeline_id).get());\n\n        base.commands.push(ArcRenderCommand::SetPipeline(pipeline));\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_index_buffer(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        index_format: IndexFormat,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetIndexBuffer;\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::SetIndexBuffer {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            index_format,\n            offset,\n            size,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_vertex_buffer(\n        &self,\n        pass: &mut RenderPass,\n        slot: u32,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetVertexBuffer;\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::SetVertexBuffer {\n            slot,\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            size,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_blend_constant(\n        &self,\n        pass: &mut RenderPass,\n        color: Color,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetBlendConstant;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::SetBlendConstant(color));\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_stencil_reference(\n        &self,\n        pass: &mut RenderPass,\n        value: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetStencilReference;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::SetStencilReference(value));\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_viewport(\n        &self,\n        pass: &mut RenderPass,\n        x: f32,\n        y: f32,\n        w: f32,\n        h: f32,\n        depth_min: f32,\n        depth_max: f32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetViewport;\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::SetViewport {\n            rect: Rect { x, y, w, h },\n            depth_min,\n            depth_max,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_scissor_rect(\n        &self,\n        pass: &mut RenderPass,\n        x: u32,\n        y: u32,\n        w: u32,\n        h: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetScissorRect;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::SetScissor(Rect { x, y, w, h }));\n\n        Ok(())\n    }\n\n    pub fn render_pass_set_immediates(\n        &self,\n        pass: &mut RenderPass,\n        offset: u32,\n        data: &[u8],\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::SetImmediate;\n        let base = pass_base!(pass, scope);\n\n        if offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {\n            pass_try!(\n                base,\n                scope,\n                Err(RenderPassErrorInner::ImmediateOffsetAlignment)\n            );\n        }\n        if data.len() as u32 & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {\n            pass_try!(\n                base,\n                scope,\n                Err(RenderPassErrorInner::ImmediateDataizeAlignment)\n            );\n        }\n\n        let value_offset = pass_try!(\n            base,\n            scope,\n            base.immediates_data\n                .len()\n                .try_into()\n                .map_err(|_| RenderPassErrorInner::ImmediateOutOfMemory),\n        );\n\n        base.immediates_data.extend(\n            data.chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)\n                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),\n        );\n\n        base.commands.push(ArcRenderCommand::SetImmediate {\n            offset,\n            size_bytes: data.len() as u32,\n            values_offset: Some(value_offset),\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_draw(\n        &self,\n        pass: &mut RenderPass,\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n        first_instance: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::Draw,\n            family: DrawCommandFamily::Draw,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::Draw {\n            vertex_count,\n            instance_count,\n            first_vertex,\n            first_instance,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_draw_indexed(\n        &self,\n        pass: &mut RenderPass,\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n        first_instance: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::Draw,\n            family: DrawCommandFamily::DrawIndexed,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndexed {\n            index_count,\n            instance_count,\n            first_index,\n            base_vertex,\n            first_instance,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_draw_mesh_tasks(\n        &self,\n        pass: &mut RenderPass,\n        group_count_x: u32,\n        group_count_y: u32,\n        group_count_z: u32,\n    ) -> Result<(), RenderPassError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::Draw,\n            family: DrawCommandFamily::DrawMeshTasks,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawMeshTasks {\n            group_count_x,\n            group_count_y,\n            group_count_z,\n        });\n        Ok(())\n    }\n\n    pub fn render_pass_draw_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::DrawIndirect,\n            family: DrawCommandFamily::Draw,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count: 1,\n            family: DrawCommandFamily::Draw,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_draw_indexed_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::DrawIndirect,\n            family: DrawCommandFamily::DrawIndexed,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count: 1,\n            family: DrawCommandFamily::DrawIndexed,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_draw_mesh_tasks_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n    ) -> Result<(), RenderPassError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::DrawIndirect,\n            family: DrawCommandFamily::DrawMeshTasks,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count: 1,\n            family: DrawCommandFamily::DrawMeshTasks,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirect,\n            family: DrawCommandFamily::Draw,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count,\n            family: DrawCommandFamily::Draw,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_indexed_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirect,\n            family: DrawCommandFamily::DrawIndexed,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count,\n            family: DrawCommandFamily::DrawIndexed,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_mesh_tasks_indirect(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count: u32,\n    ) -> Result<(), RenderPassError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirect,\n            family: DrawCommandFamily::DrawMeshTasks,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::DrawIndirect {\n            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n            offset,\n            count,\n            family: DrawCommandFamily::DrawMeshTasks,\n\n            vertex_or_index_limit: None,\n            instance_limit: None,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_indirect_count(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count_buffer_id: id::BufferId,\n        count_buffer_offset: BufferAddress,\n        max_count: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirectCount,\n            family: DrawCommandFamily::Draw,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::MultiDrawIndirectCount {\n                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n                offset,\n                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),\n                count_buffer_offset,\n                max_count,\n                family: DrawCommandFamily::Draw,\n            });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_indexed_indirect_count(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count_buffer_id: id::BufferId,\n        count_buffer_offset: BufferAddress,\n        max_count: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirectCount,\n            family: DrawCommandFamily::DrawIndexed,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::MultiDrawIndirectCount {\n                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n                offset,\n                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),\n                count_buffer_offset,\n                max_count,\n                family: DrawCommandFamily::DrawIndexed,\n            });\n\n        Ok(())\n    }\n\n    pub fn render_pass_multi_draw_mesh_tasks_indirect_count(\n        &self,\n        pass: &mut RenderPass,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        count_buffer_id: id::BufferId,\n        count_buffer_offset: BufferAddress,\n        max_count: u32,\n    ) -> Result<(), RenderPassError> {\n        let scope = PassErrorScope::Draw {\n            kind: DrawKind::MultiDrawIndirectCount,\n            family: DrawCommandFamily::DrawMeshTasks,\n        };\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::MultiDrawIndirectCount {\n                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),\n                offset,\n                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),\n                count_buffer_offset,\n                max_count,\n                family: DrawCommandFamily::DrawMeshTasks,\n            });\n\n        Ok(())\n    }\n\n    pub fn render_pass_push_debug_group(\n        &self,\n        pass: &mut RenderPass,\n        label: &str,\n        color: u32,\n    ) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::PushDebugGroup);\n\n        let bytes = label.as_bytes();\n        base.string_data.extend_from_slice(bytes);\n\n        base.commands.push(ArcRenderCommand::PushDebugGroup {\n            color,\n            len: bytes.len(),\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_pop_debug_group(&self, pass: &mut RenderPass) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::PopDebugGroup);\n\n        base.commands.push(ArcRenderCommand::PopDebugGroup);\n\n        Ok(())\n    }\n\n    pub fn render_pass_insert_debug_marker(\n        &self,\n        pass: &mut RenderPass,\n        label: &str,\n        color: u32,\n    ) -> Result<(), PassStateError> {\n        let base = pass_base!(pass, PassErrorScope::InsertDebugMarker);\n\n        let bytes = label.as_bytes();\n        base.string_data.extend_from_slice(bytes);\n\n        base.commands.push(ArcRenderCommand::InsertDebugMarker {\n            color,\n            len: bytes.len(),\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_write_timestamp(\n        &self,\n        pass: &mut RenderPass,\n        query_set_id: id::QuerySetId,\n        query_index: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::WriteTimestamp;\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::WriteTimestamp {\n            query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),\n            query_index,\n        });\n\n        Ok(())\n    }\n\n    pub fn render_pass_begin_occlusion_query(\n        &self,\n        pass: &mut RenderPass,\n        query_index: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::BeginOcclusionQuery;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::BeginOcclusionQuery { query_index });\n\n        Ok(())\n    }\n\n    pub fn render_pass_end_occlusion_query(\n        &self,\n        pass: &mut RenderPass,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::EndOcclusionQuery;\n        let base = pass_base!(pass, scope);\n\n        base.commands.push(ArcRenderCommand::EndOcclusionQuery);\n\n        Ok(())\n    }\n\n    pub fn render_pass_begin_pipeline_statistics_query(\n        &self,\n        pass: &mut RenderPass,\n        query_set_id: id::QuerySetId,\n        query_index: u32,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::BeginPipelineStatisticsQuery;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::BeginPipelineStatisticsQuery {\n                query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),\n                query_index,\n            });\n\n        Ok(())\n    }\n\n    pub fn render_pass_end_pipeline_statistics_query(\n        &self,\n        pass: &mut RenderPass,\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::EndPipelineStatisticsQuery;\n        let base = pass_base!(pass, scope);\n\n        base.commands\n            .push(ArcRenderCommand::EndPipelineStatisticsQuery);\n\n        Ok(())\n    }\n\n    pub fn render_pass_execute_bundles(\n        &self,\n        pass: &mut RenderPass,\n        render_bundle_ids: &[id::RenderBundleId],\n    ) -> Result<(), PassStateError> {\n        let scope = PassErrorScope::ExecuteBundle;\n        let base = pass_base!(pass, scope);\n\n        let hub = &self.hub;\n        let bundles = hub.render_bundles.read();\n\n        for &bundle_id in render_bundle_ids {\n            let bundle = pass_try!(base, scope, bundles.get(bundle_id).get());\n\n            base.commands.push(ArcRenderCommand::ExecuteBundle(bundle));\n        }\n        pass.current_pipeline.reset();\n        pass.current_bind_groups.reset();\n\n        Ok(())\n    }\n}\n\npub(crate) const fn get_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {\n    match family {\n        DrawCommandFamily::Draw => size_of::<wgt::DrawIndirectArgs>() as u64,\n        DrawCommandFamily::DrawIndexed => size_of::<wgt::DrawIndexedIndirectArgs>() as u64,\n        DrawCommandFamily::DrawMeshTasks => size_of::<wgt::DispatchIndirectArgs>() as u64,\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/command/render_command.rs",
    "content": "use wgt::{BufferAddress, BufferSize, Color};\n\nuse super::{DrawCommandFamily, Rect};\n#[cfg(feature = \"serde\")]\nuse crate::command::serde_object_reference_struct;\nuse crate::command::{ArcReferences, ReferenceType};\n\n#[cfg(feature = \"serde\")]\nuse macro_rules_attribute::apply;\n\n/// cbindgen:ignore\n#[doc(hidden)]\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub enum RenderCommand<R: ReferenceType> {\n    SetBindGroup {\n        index: u32,\n        num_dynamic_offsets: usize,\n        bind_group: Option<R::BindGroup>,\n    },\n    SetPipeline(R::RenderPipeline),\n    SetIndexBuffer {\n        buffer: R::Buffer,\n        index_format: wgt::IndexFormat,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    },\n    SetVertexBuffer {\n        slot: u32,\n        buffer: R::Buffer,\n        offset: BufferAddress,\n        size: Option<BufferSize>,\n    },\n    SetBlendConstant(Color),\n    SetStencilReference(u32),\n    SetViewport {\n        rect: Rect<f32>,\n        //TODO: use half-float to reduce the size?\n        depth_min: f32,\n        depth_max: f32,\n    },\n    SetScissor(Rect<u32>),\n\n    /// Set a range of immediates to values stored in [`BasePass::immediates_data`].\n    ///\n    /// See [`wgpu::RenderPass::set_immediates`] for a detailed explanation\n    /// of the restrictions these commands must satisfy.\n    SetImmediate {\n        /// The byte offset within the immediate data storage to write to.  This\n        /// must be a multiple of four.\n        offset: u32,\n\n        /// The number of bytes to write. This must be a multiple of four.\n        size_bytes: u32,\n\n        /// Index in [`BasePass::immediates_data`] of the start of the data\n        /// to be written.\n        ///\n        /// Note: this is not a byte offset like `offset`. Rather, it is the\n        /// index of the first `u32` element in `immediates_data` to read.\n        ///\n        /// `None` means zeros should be written to the destination range, and\n        /// there is no corresponding data in `immediates_data`. This is used\n        /// by render bundles, which explicitly clear out any state that\n        /// post-bundle code might see.\n        values_offset: Option<u32>,\n    },\n    Draw {\n        vertex_count: u32,\n        instance_count: u32,\n        first_vertex: u32,\n        first_instance: u32,\n    },\n    DrawIndexed {\n        index_count: u32,\n        instance_count: u32,\n        first_index: u32,\n        base_vertex: i32,\n        first_instance: u32,\n    },\n    DrawMeshTasks {\n        group_count_x: u32,\n        group_count_y: u32,\n        group_count_z: u32,\n    },\n    DrawIndirect {\n        buffer: R::Buffer,\n        offset: BufferAddress,\n        count: u32,\n        family: DrawCommandFamily,\n        /// This limit is only populated for commands in a finished [`RenderBundle`].\n        vertex_or_index_limit: Option<u64>,\n        /// This limit is only populated for commands in a finished [`RenderBundle`].\n        instance_limit: Option<u64>,\n    },\n    MultiDrawIndirectCount {\n        buffer: R::Buffer,\n        offset: BufferAddress,\n        count_buffer: R::Buffer,\n        count_buffer_offset: BufferAddress,\n        max_count: u32,\n        family: DrawCommandFamily,\n    },\n    PushDebugGroup {\n        color: u32,\n        len: usize,\n    },\n    PopDebugGroup,\n    InsertDebugMarker {\n        color: u32,\n        len: usize,\n    },\n    WriteTimestamp {\n        query_set: R::QuerySet,\n        query_index: u32,\n    },\n    BeginOcclusionQuery {\n        query_index: u32,\n    },\n    EndOcclusionQuery,\n    BeginPipelineStatisticsQuery {\n        query_set: R::QuerySet,\n        query_index: u32,\n    },\n    EndPipelineStatisticsQuery,\n    ExecuteBundle(R::RenderBundle),\n}\n\n/// Equivalent to `RenderCommand` with the Ids resolved into resource Arcs.\n///\n/// In a render pass, commands are stored in this format between when they are\n/// added to the pass, and when the pass is `end()`ed and the commands are\n/// replayed to the HAL encoder. Validation occurs when the pass is ended, which\n/// means that parameters stored in an `ArcRenderCommand` for a pass operation\n/// have generally not been validated.\n///\n/// In a render bundle, commands are stored in this format between when the bundle\n/// is `finish()`ed and when the bundle is executed. Validation occurs when the\n/// bundle is finished, which means that parameters stored in an `ArcRenderCommand`\n/// for a render bundle operation must have been validated.\n///\n/// cbindgen:ignore\npub type ArcRenderCommand = RenderCommand<ArcReferences>;\n"
  },
  {
    "path": "wgpu-core/src/command/timestamp_writes.rs",
    "content": "use alloc::sync::Arc;\n\nuse crate::id;\n\n/// Describes the writing of timestamp values in a render or compute pass.\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct PassTimestampWrites<QS = id::QuerySetId> {\n    /// The query set to write the timestamps to.\n    pub query_set: QS,\n    /// The index of the query set at which a start timestamp of this pass is written, if any.\n    pub beginning_of_pass_write_index: Option<u32>,\n    /// The index of the query set at which an end timestamp of this pass is written, if any.\n    pub end_of_pass_write_index: Option<u32>,\n}\n\n/// cbindgen:ignore\npub type ArcPassTimestampWrites = PassTimestampWrites<Arc<crate::resource::QuerySet>>;\n"
  },
  {
    "path": "wgpu-core/src/command/transfer.rs",
    "content": "use alloc::{format, string::String, sync::Arc, vec::Vec};\n\nuse arrayvec::ArrayVec;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    BufferAddress, BufferTextureCopyInfoError, BufferUsages, Extent3d, TextureSelector,\n    TextureUsages,\n};\n\nuse crate::{\n    api_log,\n    command::{\n        clear_texture, encoder::EncodingState, ArcCommand, CommandEncoderError, EncoderStateError,\n    },\n    device::MissingDownlevelFlags,\n    global::Global,\n    id::{BufferId, CommandEncoderId, TextureId},\n    init_tracker::{\n        has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,\n        TextureInitTrackerAction,\n    },\n    resource::{\n        Buffer, MissingBufferUsageError, MissingTextureUsageError, ParentDevice, RawResourceAccess,\n        Texture, TextureErrorDimension,\n    },\n};\n\nuse super::ClearError;\n\ntype TexelCopyBufferInfo = wgt::TexelCopyBufferInfo<BufferId>;\ntype TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<Arc<Texture>>;\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum CopySide {\n    Source,\n    Destination,\n}\n\n/// Error encountered while attempting a data transfer.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum TransferError {\n    #[error(\"Source and destination cannot be the same buffer\")]\n    SameSourceDestinationBuffer,\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(transparent)]\n    MissingTextureUsage(#[from] MissingTextureUsageError),\n    #[error(\n        \"Copy at offset {start_offset} bytes would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}\"\n    )]\n    BufferStartOffsetOverrun {\n        start_offset: BufferAddress,\n        buffer_size: BufferAddress,\n        side: CopySide,\n    },\n    #[error(\n        \"Copy at offset {start_offset} for {size} bytes would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}\"\n    )]\n    BufferEndOffsetOverrun {\n        start_offset: BufferAddress,\n        size: BufferAddress,\n        buffer_size: BufferAddress,\n        side: CopySide,\n    },\n    #[error(\"Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}\")]\n    TextureOverrun {\n        start_offset: u32,\n        end_offset: u32,\n        texture_size: u32,\n        dimension: TextureErrorDimension,\n        side: CopySide,\n    },\n    #[error(\"Partial copy of {start_offset}..{end_offset} on {dimension:?} dimension with size {texture_size} \\\n             is not supported for the {side:?} texture format {format:?} with {sample_count} samples\")]\n    UnsupportedPartialTransfer {\n        format: wgt::TextureFormat,\n        sample_count: u32,\n        start_offset: u32,\n        end_offset: u32,\n        texture_size: u32,\n        dimension: TextureErrorDimension,\n        side: CopySide,\n    },\n    #[error(\n        \"Copying{} layers {}..{} to{} layers {}..{} of the same texture is not allowed\",\n        if *src_aspects == wgt::TextureAspect::All { String::new() } else { format!(\" {src_aspects:?}\") },\n        src_origin_z,\n        src_origin_z + array_layer_count,\n        if *dst_aspects == wgt::TextureAspect::All { String::new() } else { format!(\" {dst_aspects:?}\") },\n        dst_origin_z,\n        dst_origin_z + array_layer_count,\n    )]\n    InvalidCopyWithinSameTexture {\n        src_aspects: wgt::TextureAspect,\n        dst_aspects: wgt::TextureAspect,\n        src_origin_z: u32,\n        dst_origin_z: u32,\n        array_layer_count: u32,\n    },\n    #[error(\"Unable to select texture aspect {aspect:?} from format {format:?}\")]\n    InvalidTextureAspect {\n        format: wgt::TextureFormat,\n        aspect: wgt::TextureAspect,\n    },\n    #[error(\"Unable to select texture mip level {level} out of {total}\")]\n    InvalidTextureMipLevel { level: u32, total: u32 },\n    #[error(\"Texture dimension must be 2D when copying from an external texture\")]\n    InvalidDimensionExternal,\n    #[error(\"Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`\")]\n    UnalignedBufferOffset(BufferAddress),\n    #[error(\"Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`\")]\n    UnalignedCopySize(BufferAddress),\n    #[error(\"Copy width is not a multiple of block width\")]\n    UnalignedCopyWidth,\n    #[error(\"Copy height is not a multiple of block height\")]\n    UnalignedCopyHeight,\n    #[error(\"Copy origin's x component is not a multiple of block width\")]\n    UnalignedCopyOriginX,\n    #[error(\"Copy origin's y component is not a multiple of block height\")]\n    UnalignedCopyOriginY,\n    #[error(\"Bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`\")]\n    UnalignedBytesPerRow,\n    #[error(\"Number of bytes per row needs to be specified since more than one row is copied\")]\n    UnspecifiedBytesPerRow,\n    #[error(\"Number of rows per image needs to be specified since more than one image is copied\")]\n    UnspecifiedRowsPerImage,\n    #[error(\"Number of bytes per row is less than the number of bytes in a complete row\")]\n    InvalidBytesPerRow,\n    #[error(\"Number of rows per image is invalid\")]\n    InvalidRowsPerImage,\n    #[error(\"Overflow while computing the size of the copy\")]\n    SizeOverflow,\n    #[error(\"Copy source aspects must refer to all aspects of the source texture format\")]\n    CopySrcMissingAspects,\n    #[error(\n        \"Copy destination aspects must refer to all aspects of the destination texture format\"\n    )]\n    CopyDstMissingAspects,\n    #[error(\"Copy aspect must refer to a single aspect of texture format\")]\n    CopyAspectNotOne,\n    #[error(\"Copying from textures with format {0:?} is forbidden\")]\n    CopyFromForbiddenTextureFormat(wgt::TextureFormat),\n    #[error(\"Copying from textures with format {format:?} and aspect {aspect:?} is forbidden\")]\n    CopyFromForbiddenTextureFormatAspect {\n        format: wgt::TextureFormat,\n        aspect: wgt::TextureAspect,\n    },\n    #[error(\"Copying to textures with format {0:?} is forbidden\")]\n    CopyToForbiddenTextureFormat(wgt::TextureFormat),\n    #[error(\"Copying to textures with format {format:?} and aspect {aspect:?} is forbidden\")]\n    CopyToForbiddenTextureFormatAspect {\n        format: wgt::TextureFormat,\n        aspect: wgt::TextureAspect,\n    },\n    #[error(\n        \"Copying to textures with format {0:?} is forbidden when copying from external texture\"\n    )]\n    ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),\n    #[error(\n        \"Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)\"\n    )]\n    TextureFormatsNotCopyCompatible {\n        src_format: wgt::TextureFormat,\n        dst_format: wgt::TextureFormat,\n    },\n    #[error(transparent)]\n    MemoryInitFailure(#[from] ClearError),\n    #[error(\"Cannot encode this copy because of a missing downelevel flag\")]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(\"Source texture sample count must be 1, got {sample_count}\")]\n    InvalidSampleCount { sample_count: u32 },\n    #[error(\n        \"Source sample count ({src_sample_count:?}) and destination sample count ({dst_sample_count:?}) are not equal\"\n    )]\n    SampleCountNotEqual {\n        src_sample_count: u32,\n        dst_sample_count: u32,\n    },\n    #[error(\"Requested mip level {requested} does not exist (count: {count})\")]\n    InvalidMipLevel { requested: u32, count: u32 },\n    #[error(\"Buffer is expected to be unmapped, but was not\")]\n    BufferNotAvailable,\n}\n\nimpl WebGpuError for TransferError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::MissingTextureUsage(e) => e.webgpu_error_type(),\n            Self::MemoryInitFailure(e) => e.webgpu_error_type(),\n\n            Self::BufferEndOffsetOverrun { .. }\n            | Self::TextureOverrun { .. }\n            | Self::BufferStartOffsetOverrun { .. }\n            | Self::UnsupportedPartialTransfer { .. }\n            | Self::InvalidCopyWithinSameTexture { .. }\n            | Self::InvalidTextureAspect { .. }\n            | Self::InvalidTextureMipLevel { .. }\n            | Self::InvalidDimensionExternal\n            | Self::UnalignedBufferOffset(..)\n            | Self::UnalignedCopySize(..)\n            | Self::UnalignedCopyWidth\n            | Self::UnalignedCopyHeight\n            | Self::UnalignedCopyOriginX\n            | Self::UnalignedCopyOriginY\n            | Self::UnalignedBytesPerRow\n            | Self::UnspecifiedBytesPerRow\n            | Self::UnspecifiedRowsPerImage\n            | Self::InvalidBytesPerRow\n            | Self::InvalidRowsPerImage\n            | Self::SizeOverflow\n            | Self::CopySrcMissingAspects\n            | Self::CopyDstMissingAspects\n            | Self::CopyAspectNotOne\n            | Self::CopyFromForbiddenTextureFormat(..)\n            | Self::CopyFromForbiddenTextureFormatAspect { .. }\n            | Self::CopyToForbiddenTextureFormat(..)\n            | Self::CopyToForbiddenTextureFormatAspect { .. }\n            | Self::ExternalCopyToForbiddenTextureFormat(..)\n            | Self::TextureFormatsNotCopyCompatible { .. }\n            | Self::MissingDownlevelFlags(..)\n            | Self::InvalidSampleCount { .. }\n            | Self::SampleCountNotEqual { .. }\n            | Self::InvalidMipLevel { .. }\n            | Self::SameSourceDestinationBuffer\n            | Self::BufferNotAvailable => ErrorType::Validation,\n        }\n    }\n}\n\nimpl From<BufferTextureCopyInfoError> for TransferError {\n    fn from(value: BufferTextureCopyInfoError) -> Self {\n        match value {\n            BufferTextureCopyInfoError::InvalidBytesPerRow => Self::InvalidBytesPerRow,\n            BufferTextureCopyInfoError::InvalidRowsPerImage => Self::InvalidRowsPerImage,\n            BufferTextureCopyInfoError::ImageStrideOverflow\n            | BufferTextureCopyInfoError::ImageBytesOverflow(_)\n            | BufferTextureCopyInfoError::ArraySizeOverflow(_) => Self::SizeOverflow,\n        }\n    }\n}\n\npub(crate) fn extract_texture_selector<T>(\n    copy_texture: &wgt::TexelCopyTextureInfo<T>,\n    copy_size: &Extent3d,\n    texture: &Texture,\n) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {\n    let format = texture.desc.format;\n    let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);\n    if copy_aspect.is_empty() {\n        return Err(TransferError::InvalidTextureAspect {\n            format,\n            aspect: copy_texture.aspect,\n        });\n    }\n\n    let (layers, origin_z) = match texture.desc.dimension {\n        wgt::TextureDimension::D1 => (0..1, 0),\n        wgt::TextureDimension::D2 => (\n            copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,\n            0,\n        ),\n        wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),\n    };\n    let base = hal::TextureCopyBase {\n        origin: wgt::Origin3d {\n            x: copy_texture.origin.x,\n            y: copy_texture.origin.y,\n            z: origin_z,\n        },\n        // this value will be incremented per copied layer\n        array_layer: layers.start,\n        mip_level: copy_texture.mip_level,\n        aspect: copy_aspect,\n    };\n    let selector = TextureSelector {\n        mips: copy_texture.mip_level..copy_texture.mip_level + 1,\n        layers,\n    };\n\n    Ok((selector, base))\n}\n\n/// WebGPU's [validating linear texture data][vltd] algorithm.\n///\n/// Copied with some modifications from WebGPU standard.\n///\n/// If successful, returns a tuple `(bytes, stride, is_contiguous)`, where:\n/// - `bytes` is the number of buffer bytes required for this copy, and\n/// - `stride` number of bytes between array layers.\n/// - `is_contiguous` is true if the linear texture data does not have padding\n///   between rows or between images.\n///\n/// [vltd]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-linear-texture-data\npub(crate) fn validate_linear_texture_data(\n    layout: &wgt::TexelCopyBufferLayout,\n    format: wgt::TextureFormat,\n    aspect: wgt::TextureAspect,\n    buffer_size: BufferAddress,\n    buffer_side: CopySide,\n    copy_size: &Extent3d,\n) -> Result<(BufferAddress, BufferAddress, bool), TransferError> {\n    let wgt::BufferTextureCopyInfo {\n        copy_width,\n        copy_height,\n        depth_or_array_layers,\n\n        offset,\n\n        block_size_bytes: _,\n        block_width_texels,\n        block_height_texels,\n\n        width_blocks: _,\n        height_blocks,\n\n        row_bytes_dense,\n        row_stride_bytes,\n\n        image_stride_rows: _,\n        image_stride_bytes,\n\n        image_rows_dense: _,\n        image_bytes_dense,\n\n        bytes_in_copy,\n    } = layout.get_buffer_texture_copy_info(format, aspect, copy_size)?;\n\n    if !copy_width.is_multiple_of(block_width_texels) {\n        return Err(TransferError::UnalignedCopyWidth);\n    }\n    if !copy_height.is_multiple_of(block_height_texels) {\n        return Err(TransferError::UnalignedCopyHeight);\n    }\n\n    let requires_multiple_rows = depth_or_array_layers > 1 || height_blocks > 1;\n    let requires_multiple_images = depth_or_array_layers > 1;\n\n    // `get_buffer_texture_copy_info()` already proceeded with defaults if these\n    // were not specified, and ensured that the values satisfy the minima if\n    // they were, but now we enforce the WebGPU requirement that they be\n    // specified any time they apply.\n    if layout.bytes_per_row.is_none() && requires_multiple_rows {\n        return Err(TransferError::UnspecifiedBytesPerRow);\n    }\n\n    if layout.rows_per_image.is_none() && requires_multiple_images {\n        return Err(TransferError::UnspecifiedRowsPerImage);\n    };\n\n    if offset > buffer_size {\n        return Err(TransferError::BufferStartOffsetOverrun {\n            start_offset: offset,\n            buffer_size,\n            side: buffer_side,\n        });\n    }\n    // NOTE: Should never underflow because of our earlier check.\n    if bytes_in_copy > buffer_size - offset {\n        return Err(TransferError::BufferEndOffsetOverrun {\n            start_offset: offset,\n            size: bytes_in_copy,\n            buffer_size,\n            side: buffer_side,\n        });\n    }\n\n    let is_contiguous = (row_stride_bytes == row_bytes_dense || !requires_multiple_rows)\n        && (image_stride_bytes == image_bytes_dense || !requires_multiple_images);\n\n    Ok((bytes_in_copy, image_stride_bytes, is_contiguous))\n}\n\n/// Validate the source format of a texture copy.\n///\n/// This performs the check from WebGPU's [validating texture buffer copy][vtbc]\n/// algorithm that ensures that the format and aspect form a valid texel copy source\n/// as defined in the [depth-stencil formats][dsf].\n///\n/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy\n/// [dsf]: https://gpuweb.github.io/gpuweb/#depth-formats\npub(crate) fn validate_texture_copy_src_format(\n    format: wgt::TextureFormat,\n    aspect: wgt::TextureAspect,\n) -> Result<(), TransferError> {\n    use wgt::TextureAspect as Ta;\n    use wgt::TextureFormat as Tf;\n    match (format, aspect) {\n        (Tf::Depth24Plus, _) => Err(TransferError::CopyFromForbiddenTextureFormat(format)),\n        (Tf::Depth24PlusStencil8, Ta::DepthOnly) => {\n            Err(TransferError::CopyFromForbiddenTextureFormatAspect { format, aspect })\n        }\n        _ => Ok(()),\n    }\n}\n\n/// Validate the destination format of a texture copy.\n///\n/// This performs the check from WebGPU's [validating texture buffer copy][vtbc]\n/// algorithm that ensures that the format and aspect form a valid texel copy destination\n/// as defined in the [depth-stencil formats][dsf].\n///\n/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy\n/// [dsf]: https://gpuweb.github.io/gpuweb/#depth-formats\npub(crate) fn validate_texture_copy_dst_format(\n    format: wgt::TextureFormat,\n    aspect: wgt::TextureAspect,\n) -> Result<(), TransferError> {\n    use wgt::TextureAspect as Ta;\n    use wgt::TextureFormat as Tf;\n    match (format, aspect) {\n        (Tf::Depth24Plus | Tf::Depth32Float, _) => {\n            Err(TransferError::CopyToForbiddenTextureFormat(format))\n        }\n        (Tf::Depth24PlusStencil8 | Tf::Depth32FloatStencil8, Ta::DepthOnly) => {\n            Err(TransferError::CopyToForbiddenTextureFormatAspect { format, aspect })\n        }\n        _ => Ok(()),\n    }\n}\n\n/// Validation for texture/buffer copies.\n///\n/// This implements the following checks from WebGPU's [validating texture buffer copy][vtbc]\n/// algorithm:\n///  * The texture must not be multisampled.\n///  * The copy must be from/to a single aspect of the texture.\n///  * If `aligned` is true, the buffer offset must be aligned appropriately.\n///\n/// And implements the following check from WebGPU's [validating GPUTexelCopyBufferInfo][vtcbi]\n/// algorithm:\n///  * If `aligned` is true, `bytesPerRow` must be a multiple of 256.\n///\n/// Note that the `bytesPerRow` alignment check is enforced whenever\n/// `bytesPerRow` is specified, even if the transfer is not multiple rows and\n/// `bytesPerRow` could have been omitted.\n///\n/// The following steps in [validating texture buffer copy][vtbc] are implemented elsewhere:\n///  * Invocation of other validation algorithms.\n///  * The texture usage (COPY_DST / COPY_SRC) check.\n///  * The check for non-copyable depth/stencil formats. The caller must perform\n///    this check using `validate_texture_copy_src_format` / `validate_texture_copy_dst_format`\n///    before calling this function. This function will panic if\n///    [`wgt::TextureFormat::block_copy_size`] returns `None` due to a\n///    non-copyable format.\n///\n/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy\n/// [vtcbi]: https://www.w3.org/TR/webgpu/#abstract-opdef-validating-gputexelcopybufferinfo\npub(crate) fn validate_texture_buffer_copy<T>(\n    texture_copy_view: &wgt::TexelCopyTextureInfo<T>,\n    aspect: hal::FormatAspects,\n    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n    layout: &wgt::TexelCopyBufferLayout,\n    aligned: bool,\n) -> Result<(), TransferError> {\n    if desc.sample_count != 1 {\n        return Err(TransferError::InvalidSampleCount {\n            sample_count: desc.sample_count,\n        });\n    }\n\n    if !aspect.is_one() {\n        return Err(TransferError::CopyAspectNotOne);\n    }\n\n    let offset_alignment = if desc.format.is_depth_stencil_format() {\n        4\n    } else {\n        // The case where `block_copy_size` returns `None` is currently\n        // unreachable both for the reason in the expect message, and also\n        // because the currently-defined non-copyable formats are depth/stencil\n        // formats so would take the `if` branch.\n        desc.format\n            .block_copy_size(Some(texture_copy_view.aspect))\n            .expect(\"non-copyable formats should have been rejected previously\")\n    };\n\n    if aligned && !layout.offset.is_multiple_of(u64::from(offset_alignment)) {\n        return Err(TransferError::UnalignedBufferOffset(layout.offset));\n    }\n\n    if let Some(bytes_per_row) = layout.bytes_per_row {\n        if aligned && bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT != 0 {\n            return Err(TransferError::UnalignedBytesPerRow);\n        }\n    }\n\n    Ok(())\n}\n\n/// Validate the extent and alignment of a texture copy.\n///\n/// Copied with minor modifications from WebGPU standard. This mostly follows\n/// the [validating GPUTexelCopyTextureInfo][vtcti] and [validating texture copy\n/// range][vtcr] algorithms.\n///\n/// Returns the HAL copy extent and the layer count.\n///\n/// [vtcti]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gputexelcopytextureinfo\n/// [vtcr]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-copy-range\npub(crate) fn validate_texture_copy_range<T>(\n    texture_copy_view: &wgt::TexelCopyTextureInfo<T>,\n    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n    texture_side: CopySide,\n    copy_size: &Extent3d,\n) -> Result<(hal::CopyExtent, u32), TransferError> {\n    let (block_width, block_height) = desc.format.block_dimensions();\n\n    let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(\n        TransferError::InvalidTextureMipLevel {\n            level: texture_copy_view.mip_level,\n            total: desc.mip_level_count,\n        },\n    )?;\n    // physical size can be larger than the virtual\n    let extent = extent_virtual.physical_size(desc.format);\n\n    // Multisampled and depth-stencil formats do not support partial copies\n    // on x and y dimensions, but do support copying a subset of layers.\n    let requires_exact_size = desc.format.is_depth_stencil_format() || desc.sample_count > 1;\n\n    // Return `Ok` if a run `size` texels long starting at `start_offset` is\n    // valid for `texture_size`. Otherwise, return an appropriate a`Err`.\n    let check_dimension = |dimension: TextureErrorDimension,\n                           start_offset: u32,\n                           size: u32,\n                           texture_size: u32,\n                           requires_exact_size: bool|\n     -> Result<(), TransferError> {\n        if requires_exact_size && (start_offset != 0 || size != texture_size) {\n            Err(TransferError::UnsupportedPartialTransfer {\n                format: desc.format,\n                sample_count: desc.sample_count,\n                start_offset,\n                end_offset: start_offset.wrapping_add(size),\n                texture_size,\n                dimension,\n                side: texture_side,\n            })\n        // Avoid underflow in the subtraction by checking start_offset against\n        // texture_size first.\n        } else if start_offset > texture_size || texture_size - start_offset < size {\n            Err(TransferError::TextureOverrun {\n                start_offset,\n                end_offset: start_offset.wrapping_add(size),\n                texture_size,\n                dimension,\n                side: texture_side,\n            })\n        } else {\n            Ok(())\n        }\n    };\n\n    check_dimension(\n        TextureErrorDimension::X,\n        texture_copy_view.origin.x,\n        copy_size.width,\n        extent.width,\n        requires_exact_size,\n    )?;\n    check_dimension(\n        TextureErrorDimension::Y,\n        texture_copy_view.origin.y,\n        copy_size.height,\n        extent.height,\n        requires_exact_size,\n    )?;\n    check_dimension(\n        TextureErrorDimension::Z,\n        texture_copy_view.origin.z,\n        copy_size.depth_or_array_layers,\n        extent.depth_or_array_layers,\n        false, // partial copy always allowed on Z/layer dimension\n    )?;\n\n    if !texture_copy_view.origin.x.is_multiple_of(block_width) {\n        return Err(TransferError::UnalignedCopyOriginX);\n    }\n    if !texture_copy_view.origin.y.is_multiple_of(block_height) {\n        return Err(TransferError::UnalignedCopyOriginY);\n    }\n    if !copy_size.width.is_multiple_of(block_width) {\n        return Err(TransferError::UnalignedCopyWidth);\n    }\n    if !copy_size.height.is_multiple_of(block_height) {\n        return Err(TransferError::UnalignedCopyHeight);\n    }\n\n    let (depth, array_layer_count) = match desc.dimension {\n        wgt::TextureDimension::D1 => (1, 1),\n        wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),\n        wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),\n    };\n\n    let copy_extent = hal::CopyExtent {\n        width: copy_size.width,\n        height: copy_size.height,\n        depth,\n    };\n    Ok((copy_extent, array_layer_count))\n}\n\n/// Validate a copy within the same texture.\n///\n/// This implements the WebGPU requirement that the [sets of subresources for\n/// texture copy][srtc] of the source and destination be disjoint, i.e. that the\n/// source and destination do not overlap.\n///\n/// This function assumes that the copy ranges have already been validated with\n/// `validate_texture_copy_range`.\n///\n/// [srtc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-set-of-subresources-for-texture-copy\npub(crate) fn validate_copy_within_same_texture<T>(\n    src: &wgt::TexelCopyTextureInfo<T>,\n    dst: &wgt::TexelCopyTextureInfo<T>,\n    format: wgt::TextureFormat,\n    array_layer_count: u32,\n) -> Result<(), TransferError> {\n    let src_aspects = hal::FormatAspects::new(format, src.aspect);\n    let dst_aspects = hal::FormatAspects::new(format, dst.aspect);\n    if (src_aspects & dst_aspects).is_empty() {\n        // Copying between different aspects (if it even makes sense), is okay.\n        return Ok(());\n    }\n\n    if src.origin.z >= dst.origin.z + array_layer_count\n        || dst.origin.z >= src.origin.z + array_layer_count\n    {\n        // Copying between non-overlapping layer ranges is okay.\n        return Ok(());\n    }\n\n    if src.mip_level != dst.mip_level {\n        // Copying between different mip levels is okay.\n        return Ok(());\n    }\n\n    Err(TransferError::InvalidCopyWithinSameTexture {\n        src_aspects: src.aspect,\n        dst_aspects: dst.aspect,\n        src_origin_z: src.origin.z,\n        dst_origin_z: dst.origin.z,\n        array_layer_count,\n    })\n}\n\nfn handle_texture_init(\n    state: &mut EncodingState,\n    init_kind: MemoryInitKind,\n    copy_texture: &TexelCopyTextureInfo,\n    copy_size: &Extent3d,\n    texture: &Arc<Texture>,\n) -> Result<(), ClearError> {\n    let init_action = TextureInitTrackerAction {\n        texture: texture.clone(),\n        range: TextureInitRange {\n            mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,\n            layer_range: copy_texture.origin.z\n                ..(copy_texture.origin.z + copy_size.depth_or_array_layers),\n        },\n        kind: init_kind,\n    };\n\n    // Register the init action.\n    let immediate_inits = state\n        .texture_memory_actions\n        .register_init_action(&{ init_action });\n\n    // In rare cases we may need to insert an init operation immediately onto the command buffer.\n    if !immediate_inits.is_empty() {\n        for init in immediate_inits {\n            clear_texture(\n                &init.texture,\n                TextureInitRange {\n                    mip_range: init.mip_level..(init.mip_level + 1),\n                    layer_range: init.layer..(init.layer + 1),\n                },\n                state.raw_encoder,\n                &mut state.tracker.textures,\n                &state.device.alignments,\n                state.device.zero_buffer.as_ref(),\n                state.snatch_guard,\n                state.device.instance_flags,\n            )?;\n        }\n    }\n\n    Ok(())\n}\n\n/// Prepare a transfer's source texture.\n///\n/// Ensure the source texture of a transfer is in the right initialization\n/// state, and record the state for after the transfer operation.\nfn handle_src_texture_init(\n    state: &mut EncodingState,\n    source: &TexelCopyTextureInfo,\n    copy_size: &Extent3d,\n    texture: &Arc<Texture>,\n) -> Result<(), TransferError> {\n    handle_texture_init(\n        state,\n        MemoryInitKind::NeedsInitializedMemory,\n        source,\n        copy_size,\n        texture,\n    )?;\n    Ok(())\n}\n\n/// Prepare a transfer's destination texture.\n///\n/// Ensure the destination texture of a transfer is in the right initialization\n/// state, and record the state for after the transfer operation.\nfn handle_dst_texture_init(\n    state: &mut EncodingState,\n    destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,\n    copy_size: &Extent3d,\n    texture: &Arc<Texture>,\n) -> Result<(), TransferError> {\n    // Attention: If we don't write full texture subresources, we need to a full\n    // clear first since we don't track subrects. This means that in rare cases\n    // even a *destination* texture of a transfer may need an immediate texture\n    // init.\n    let dst_init_kind = if has_copy_partial_init_tracker_coverage(\n        copy_size,\n        destination.mip_level,\n        &texture.desc,\n    ) {\n        MemoryInitKind::NeedsInitializedMemory\n    } else {\n        MemoryInitKind::ImplicitlyInitialized\n    };\n\n    handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;\n    Ok(())\n}\n\n/// Handle initialization tracking for a transfer's source or destination buffer.\n///\n/// Ensures that the transfer will not read from uninitialized memory, and updates\n/// the initialization state information to reflect the transfer.\nfn handle_buffer_init(\n    state: &mut EncodingState,\n    info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,\n    direction: CopySide,\n    required_buffer_bytes_in_copy: BufferAddress,\n    is_contiguous: bool,\n) {\n    const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;\n    const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;\n\n    let buffer = &info.buffer;\n    let start = info.layout.offset;\n    let end = info.layout.offset + required_buffer_bytes_in_copy;\n    if !is_contiguous || direction == CopySide::Source {\n        // If the transfer will read the buffer, then the whole region needs to\n        // be initialized.\n        //\n        // If the transfer will not write a contiguous region of the buffer,\n        // then we need to make sure the padding areas are initialized. For now,\n        // initialize the whole region, although this could be improved to\n        // initialize only the necessary parts if doing so is likely to be\n        // faster than initializing the whole thing.\n        //\n        // Adjust the start/end outwards to 4B alignment.\n        let aligned_start = start & !ALIGN_MASK;\n        let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;\n        state\n            .buffer_memory_init_actions\n            .extend(buffer.initialization_status.read().create_action(\n                buffer,\n                aligned_start..aligned_end,\n                MemoryInitKind::NeedsInitializedMemory,\n            ));\n    } else {\n        // If the transfer will write a contiguous region of the buffer, then we\n        // don't need to initialize that region.\n        //\n        // However, if the start and end are not 4B aligned, we need to make\n        // sure that we don't end up trying to initialize non-4B-aligned regions\n        // later.\n        //\n        // Adjust the start/end inwards to 4B alignment, we will handle the\n        // first/last pieces differently.\n        let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;\n        let aligned_end = end & !ALIGN_MASK;\n        if aligned_start != start {\n            state.buffer_memory_init_actions.extend(\n                buffer.initialization_status.read().create_action(\n                    buffer,\n                    aligned_start - ALIGN_SIZE..aligned_start,\n                    MemoryInitKind::NeedsInitializedMemory,\n                ),\n            );\n        }\n        if aligned_start != aligned_end {\n            state.buffer_memory_init_actions.extend(\n                buffer.initialization_status.read().create_action(\n                    buffer,\n                    aligned_start..aligned_end,\n                    MemoryInitKind::ImplicitlyInitialized,\n                ),\n            );\n        }\n        if aligned_end != end {\n            // It is possible that `aligned_end + ALIGN_SIZE > dst_buffer.size`,\n            // because `dst_buffer.size` is the user-requested size, not the\n            // final size of the buffer. The final size of the buffer is not\n            // readily available, but was rounded up to COPY_BUFFER_ALIGNMENT,\n            // so no overrun is possible.\n            state.buffer_memory_init_actions.extend(\n                buffer.initialization_status.read().create_action(\n                    buffer,\n                    aligned_end..aligned_end + ALIGN_SIZE,\n                    MemoryInitKind::NeedsInitializedMemory,\n                ),\n            );\n        }\n    }\n}\n\nimpl Global {\n    pub fn command_encoder_copy_buffer_to_buffer(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        source: BufferId,\n        source_offset: BufferAddress,\n        destination: BufferId,\n        destination_offset: BufferAddress,\n        size: Option<BufferAddress>,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::copy_buffer_to_buffer\");\n        api_log!(\n            \"CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes\"\n        );\n\n        let hub = &self.hub;\n\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::CopyBufferToBuffer {\n                src: self.resolve_buffer_id(source)?,\n                src_offset: source_offset,\n                dst: self.resolve_buffer_id(destination)?,\n                dst_offset: destination_offset,\n                size,\n            })\n        })\n    }\n\n    pub fn command_encoder_copy_buffer_to_texture(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        source: &TexelCopyBufferInfo,\n        destination: &wgt::TexelCopyTextureInfo<TextureId>,\n        copy_size: &Extent3d,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::copy_buffer_to_texture\");\n        api_log!(\n            \"CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}\",\n            source.buffer,\n            destination.texture\n        );\n\n        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::CopyBufferToTexture {\n                src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {\n                    buffer: self.resolve_buffer_id(source.buffer)?,\n                    layout: source.layout,\n                },\n                dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {\n                    texture: self.resolve_texture_id(destination.texture)?,\n                    mip_level: destination.mip_level,\n                    origin: destination.origin,\n                    aspect: destination.aspect,\n                },\n                size: *copy_size,\n            })\n        })\n    }\n\n    pub fn command_encoder_copy_texture_to_buffer(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        source: &wgt::TexelCopyTextureInfo<TextureId>,\n        destination: &TexelCopyBufferInfo,\n        copy_size: &Extent3d,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::copy_texture_to_buffer\");\n        api_log!(\n            \"CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}\",\n            source.texture,\n            destination.buffer\n        );\n\n        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::CopyTextureToBuffer {\n                src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {\n                    texture: self.resolve_texture_id(source.texture)?,\n                    mip_level: source.mip_level,\n                    origin: source.origin,\n                    aspect: source.aspect,\n                },\n                dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {\n                    buffer: self.resolve_buffer_id(destination.buffer)?,\n                    layout: destination.layout,\n                },\n                size: *copy_size,\n            })\n        })\n    }\n\n    pub fn command_encoder_copy_texture_to_texture(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        source: &wgt::TexelCopyTextureInfo<TextureId>,\n        destination: &wgt::TexelCopyTextureInfo<TextureId>,\n        copy_size: &Extent3d,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::copy_texture_to_texture\");\n        api_log!(\n            \"CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}\",\n            source.texture,\n            destination.texture\n        );\n\n        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n\n        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {\n            Ok(ArcCommand::CopyTextureToTexture {\n                src: wgt::TexelCopyTextureInfo {\n                    texture: self.resolve_texture_id(source.texture)?,\n                    mip_level: source.mip_level,\n                    origin: source.origin,\n                    aspect: source.aspect,\n                },\n                dst: wgt::TexelCopyTextureInfo {\n                    texture: self.resolve_texture_id(destination.texture)?,\n                    mip_level: destination.mip_level,\n                    origin: destination.origin,\n                    aspect: destination.aspect,\n                },\n                size: *copy_size,\n            })\n        })\n    }\n}\n\npub(super) fn copy_buffer_to_buffer(\n    state: &mut EncodingState,\n    src_buffer: &Arc<Buffer>,\n    source_offset: BufferAddress,\n    dst_buffer: &Arc<Buffer>,\n    destination_offset: BufferAddress,\n    size: Option<BufferAddress>,\n) -> Result<(), CommandEncoderError> {\n    if src_buffer.is_equal(dst_buffer) {\n        return Err(TransferError::SameSourceDestinationBuffer.into());\n    }\n\n    src_buffer.same_device(state.device)?;\n\n    let src_pending = state\n        .tracker\n        .buffers\n        .set_single(src_buffer, wgt::BufferUses::COPY_SRC);\n\n    let src_raw = src_buffer.try_raw(state.snatch_guard)?;\n    src_buffer\n        .check_usage(BufferUsages::COPY_SRC)\n        .map_err(TransferError::MissingBufferUsage)?;\n    // expecting only a single barrier\n    let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));\n\n    dst_buffer.same_device(state.device)?;\n\n    let dst_pending = state\n        .tracker\n        .buffers\n        .set_single(dst_buffer, wgt::BufferUses::COPY_DST);\n\n    let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;\n    dst_buffer\n        .check_usage(BufferUsages::COPY_DST)\n        .map_err(TransferError::MissingBufferUsage)?;\n    let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));\n\n    if source_offset > src_buffer.size {\n        return Err(TransferError::BufferStartOffsetOverrun {\n            start_offset: source_offset,\n            buffer_size: src_buffer.size,\n            side: CopySide::Source,\n        }\n        .into());\n    }\n    let size = size.unwrap_or_else(|| {\n        // NOTE: Should never underflow because of our earlier check.\n        src_buffer.size - source_offset\n    });\n\n    if !size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n        return Err(TransferError::UnalignedCopySize(size).into());\n    }\n    if !source_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n        return Err(TransferError::UnalignedBufferOffset(source_offset).into());\n    }\n    if !destination_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n        return Err(TransferError::UnalignedBufferOffset(destination_offset).into());\n    }\n    if !state\n        .device\n        .downlevel\n        .flags\n        .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)\n        && (src_buffer.usage.contains(BufferUsages::INDEX)\n            || dst_buffer.usage.contains(BufferUsages::INDEX))\n    {\n        let forbidden_usages = BufferUsages::VERTEX\n            | BufferUsages::UNIFORM\n            | BufferUsages::INDIRECT\n            | BufferUsages::STORAGE;\n        if src_buffer.usage.intersects(forbidden_usages)\n            || dst_buffer.usage.intersects(forbidden_usages)\n        {\n            return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(\n                wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,\n            ))\n            .into());\n        }\n    }\n\n    if size > src_buffer.size - source_offset {\n        return Err(TransferError::BufferEndOffsetOverrun {\n            start_offset: source_offset,\n            size,\n            buffer_size: src_buffer.size,\n            side: CopySide::Source,\n        }\n        .into());\n    }\n    // NOTE: Should never overflow because of our earlier check.\n    let source_end_offset = source_offset + size;\n\n    if destination_offset > dst_buffer.size {\n        return Err(TransferError::BufferStartOffsetOverrun {\n            start_offset: destination_offset,\n            buffer_size: dst_buffer.size,\n            side: CopySide::Destination,\n        }\n        .into());\n    }\n    // NOTE: Should never underflow because of our earlier check.\n    if size > dst_buffer.size - destination_offset {\n        return Err(TransferError::BufferEndOffsetOverrun {\n            start_offset: destination_offset,\n            size,\n            buffer_size: dst_buffer.size,\n            side: CopySide::Destination,\n        }\n        .into());\n    }\n    // NOTE: Should never overflow because of our earlier check.\n    let destination_end_offset = destination_offset + size;\n\n    // This must happen after parameter validation (so that errors are reported\n    // as required by the spec), but before any side effects.\n    if size == 0 {\n        log::trace!(\"Ignoring copy_buffer_to_buffer of size 0\");\n        return Ok(());\n    }\n\n    // Make sure source is initialized memory and mark dest as initialized.\n    state\n        .buffer_memory_init_actions\n        .extend(dst_buffer.initialization_status.read().create_action(\n            dst_buffer,\n            destination_offset..destination_end_offset,\n            MemoryInitKind::ImplicitlyInitialized,\n        ));\n    state\n        .buffer_memory_init_actions\n        .extend(src_buffer.initialization_status.read().create_action(\n            src_buffer,\n            source_offset..source_end_offset,\n            MemoryInitKind::NeedsInitializedMemory,\n        ));\n\n    let region = hal::BufferCopy {\n        src_offset: source_offset,\n        dst_offset: destination_offset,\n        size: wgt::BufferSize::new(size).unwrap(),\n    };\n    let barriers = src_barrier\n        .into_iter()\n        .chain(dst_barrier)\n        .collect::<Vec<_>>();\n    unsafe {\n        state.raw_encoder.transition_buffers(&barriers);\n        state\n            .raw_encoder\n            .copy_buffer_to_buffer(src_raw, dst_raw, &[region]);\n    }\n\n    Ok(())\n}\n\npub(super) fn copy_buffer_to_texture(\n    state: &mut EncodingState,\n    source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,\n    destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,\n    copy_size: &Extent3d,\n) -> Result<(), CommandEncoderError> {\n    let dst_texture = &destination.texture;\n    let src_buffer = &source.buffer;\n\n    dst_texture.same_device(state.device)?;\n    src_buffer.same_device(state.device)?;\n\n    let (hal_copy_size, array_layer_count) = validate_texture_copy_range(\n        destination,\n        &dst_texture.desc,\n        CopySide::Destination,\n        copy_size,\n    )?;\n\n    let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;\n\n    let src_raw = src_buffer.try_raw(state.snatch_guard)?;\n    src_buffer\n        .check_usage(BufferUsages::COPY_SRC)\n        .map_err(TransferError::MissingBufferUsage)?;\n\n    let dst_raw = dst_texture.try_raw(state.snatch_guard)?;\n    dst_texture\n        .check_usage(TextureUsages::COPY_DST)\n        .map_err(TransferError::MissingTextureUsage)?;\n\n    validate_texture_copy_dst_format(dst_texture.desc.format, destination.aspect)?;\n\n    validate_texture_buffer_copy(\n        destination,\n        dst_base.aspect,\n        &dst_texture.desc,\n        &source.layout,\n        true, // alignment required for buffer offset\n    )?;\n\n    let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =\n        validate_linear_texture_data(\n            &source.layout,\n            dst_texture.desc.format,\n            destination.aspect,\n            src_buffer.size,\n            CopySide::Source,\n            copy_size,\n        )?;\n\n    if dst_texture.desc.format.is_depth_stencil_format() {\n        state\n            .device\n            .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)\n            .map_err(TransferError::from)?;\n    }\n\n    // This must happen after parameter validation (so that errors are reported\n    // as required by the spec), but before any side effects.\n    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {\n        log::trace!(\"Ignoring copy_buffer_to_texture of size 0\");\n        return Ok(());\n    }\n\n    // Handle texture init *before* dealing with barrier transitions so we\n    // have an easier time inserting \"immediate-inits\" that may be required\n    // by prior discards in rare cases.\n    handle_dst_texture_init(state, destination, copy_size, dst_texture)?;\n\n    let src_pending = state\n        .tracker\n        .buffers\n        .set_single(src_buffer, wgt::BufferUses::COPY_SRC);\n    let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));\n\n    let dst_pending =\n        state\n            .tracker\n            .textures\n            .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);\n    let dst_barrier = dst_pending\n        .map(|pending| pending.into_hal(dst_raw))\n        .collect::<Vec<_>>();\n\n    handle_buffer_init(\n        state,\n        source,\n        CopySide::Source,\n        required_buffer_bytes_in_copy,\n        is_contiguous,\n    );\n\n    let regions = (0..array_layer_count)\n        .map(|rel_array_layer| {\n            let mut texture_base = dst_base.clone();\n            texture_base.array_layer += rel_array_layer;\n            let mut buffer_layout = source.layout;\n            buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;\n            hal::BufferTextureCopy {\n                buffer_layout,\n                texture_base,\n                size: hal_copy_size,\n            }\n        })\n        .collect::<Vec<_>>();\n\n    unsafe {\n        state.raw_encoder.transition_textures(&dst_barrier);\n        state.raw_encoder.transition_buffers(src_barrier.as_slice());\n        state\n            .raw_encoder\n            .copy_buffer_to_texture(src_raw, dst_raw, &regions);\n    }\n\n    Ok(())\n}\n\npub(super) fn copy_texture_to_buffer(\n    state: &mut EncodingState,\n    source: &TexelCopyTextureInfo,\n    destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,\n    copy_size: &Extent3d,\n) -> Result<(), CommandEncoderError> {\n    let src_texture = &source.texture;\n    let dst_buffer = &destination.buffer;\n\n    src_texture.same_device(state.device)?;\n    dst_buffer.same_device(state.device)?;\n\n    let (hal_copy_size, array_layer_count) =\n        validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;\n\n    let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;\n\n    let src_raw = src_texture.try_raw(state.snatch_guard)?;\n    src_texture\n        .check_usage(TextureUsages::COPY_SRC)\n        .map_err(TransferError::MissingTextureUsage)?;\n\n    if source.mip_level >= src_texture.desc.mip_level_count {\n        return Err(TransferError::InvalidMipLevel {\n            requested: source.mip_level,\n            count: src_texture.desc.mip_level_count,\n        }\n        .into());\n    }\n\n    validate_texture_copy_src_format(src_texture.desc.format, source.aspect)?;\n\n    validate_texture_buffer_copy(\n        source,\n        src_base.aspect,\n        &src_texture.desc,\n        &destination.layout,\n        true, // alignment required for buffer offset\n    )?;\n\n    let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =\n        validate_linear_texture_data(\n            &destination.layout,\n            src_texture.desc.format,\n            source.aspect,\n            dst_buffer.size,\n            CopySide::Destination,\n            copy_size,\n        )?;\n\n    if src_texture.desc.format.is_depth_stencil_format() {\n        state\n            .device\n            .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)\n            .map_err(TransferError::from)?;\n    }\n\n    let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;\n    dst_buffer\n        .check_usage(BufferUsages::COPY_DST)\n        .map_err(TransferError::MissingBufferUsage)?;\n\n    // This must happen after parameter validation (so that errors are reported\n    // as required by the spec), but before any side effects.\n    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {\n        log::trace!(\"Ignoring copy_texture_to_buffer of size 0\");\n        return Ok(());\n    }\n\n    // Handle texture init *before* dealing with barrier transitions so we\n    // have an easier time inserting \"immediate-inits\" that may be required\n    // by prior discards in rare cases.\n    handle_src_texture_init(state, source, copy_size, src_texture)?;\n\n    let src_pending =\n        state\n            .tracker\n            .textures\n            .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);\n    let src_barrier = src_pending\n        .map(|pending| pending.into_hal(src_raw))\n        .collect::<Vec<_>>();\n\n    let dst_pending = state\n        .tracker\n        .buffers\n        .set_single(dst_buffer, wgt::BufferUses::COPY_DST);\n\n    let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));\n\n    handle_buffer_init(\n        state,\n        destination,\n        CopySide::Destination,\n        required_buffer_bytes_in_copy,\n        is_contiguous,\n    );\n\n    let regions = (0..array_layer_count)\n        .map(|rel_array_layer| {\n            let mut texture_base = src_base.clone();\n            texture_base.array_layer += rel_array_layer;\n            let mut buffer_layout = destination.layout;\n            buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;\n            hal::BufferTextureCopy {\n                buffer_layout,\n                texture_base,\n                size: hal_copy_size,\n            }\n        })\n        .collect::<Vec<_>>();\n    unsafe {\n        state.raw_encoder.transition_buffers(dst_barrier.as_slice());\n        state.raw_encoder.transition_textures(&src_barrier);\n        state.raw_encoder.copy_texture_to_buffer(\n            src_raw,\n            wgt::TextureUses::COPY_SRC,\n            dst_raw,\n            &regions,\n        );\n    }\n\n    Ok(())\n}\n\npub(super) fn copy_texture_to_texture(\n    state: &mut EncodingState,\n    source: &TexelCopyTextureInfo,\n    destination: &TexelCopyTextureInfo,\n    copy_size: &Extent3d,\n) -> Result<(), CommandEncoderError> {\n    let src_texture = &source.texture;\n    let dst_texture = &destination.texture;\n\n    src_texture.same_device(state.device)?;\n    dst_texture.same_device(state.device)?;\n\n    // src and dst texture format must be copy-compatible\n    // https://gpuweb.github.io/gpuweb/#copy-compatible\n    if src_texture.desc.format.remove_srgb_suffix() != dst_texture.desc.format.remove_srgb_suffix()\n    {\n        return Err(TransferError::TextureFormatsNotCopyCompatible {\n            src_format: src_texture.desc.format,\n            dst_format: dst_texture.desc.format,\n        }\n        .into());\n    }\n\n    let (src_copy_size, array_layer_count) =\n        validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;\n    let (dst_copy_size, _) = validate_texture_copy_range(\n        destination,\n        &dst_texture.desc,\n        CopySide::Destination,\n        copy_size,\n    )?;\n\n    if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {\n        validate_copy_within_same_texture(\n            source,\n            destination,\n            src_texture.desc.format,\n            array_layer_count,\n        )?;\n    }\n\n    let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;\n    let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;\n    let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);\n    let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);\n    if src_tex_base.aspect != src_texture_aspects {\n        return Err(TransferError::CopySrcMissingAspects.into());\n    }\n    if dst_tex_base.aspect != dst_texture_aspects {\n        return Err(TransferError::CopyDstMissingAspects.into());\n    }\n\n    if src_texture.desc.sample_count != dst_texture.desc.sample_count {\n        return Err(TransferError::SampleCountNotEqual {\n            src_sample_count: src_texture.desc.sample_count,\n            dst_sample_count: dst_texture.desc.sample_count,\n        }\n        .into());\n    }\n\n    let src_raw = src_texture.try_raw(state.snatch_guard)?;\n    src_texture\n        .check_usage(TextureUsages::COPY_SRC)\n        .map_err(TransferError::MissingTextureUsage)?;\n    let dst_raw = dst_texture.try_raw(state.snatch_guard)?;\n    dst_texture\n        .check_usage(TextureUsages::COPY_DST)\n        .map_err(TransferError::MissingTextureUsage)?;\n\n    // This must happen after parameter validation (so that errors are reported\n    // as required by the spec), but before any side effects.\n    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {\n        log::trace!(\"Ignoring copy_texture_to_texture of size 0\");\n        return Ok(());\n    }\n\n    // Handle texture init *before* dealing with barrier transitions so we\n    // have an easier time inserting \"immediate-inits\" that may be required\n    // by prior discards in rare cases.\n    handle_src_texture_init(state, source, copy_size, src_texture)?;\n    handle_dst_texture_init(state, destination, copy_size, dst_texture)?;\n\n    let src_pending =\n        state\n            .tracker\n            .textures\n            .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);\n\n    //TODO: try to avoid this the collection. It's needed because both\n    // `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.\n    let mut barriers: ArrayVec<_, 2> = src_pending\n        .map(|pending| pending.into_hal(src_raw))\n        .collect();\n\n    let dst_pending =\n        state\n            .tracker\n            .textures\n            .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);\n    barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));\n\n    let hal_copy_size = hal::CopyExtent {\n        width: src_copy_size.width.min(dst_copy_size.width),\n        height: src_copy_size.height.min(dst_copy_size.height),\n        depth: src_copy_size.depth.min(dst_copy_size.depth),\n    };\n\n    let dst_format = dst_texture.desc.format;\n\n    let regions = (0..array_layer_count).map(|rel_array_layer| {\n        let mut src_base = src_tex_base.clone();\n        let mut dst_base = dst_tex_base.clone();\n        src_base.array_layer += rel_array_layer;\n        dst_base.array_layer += rel_array_layer;\n        hal::TextureCopy {\n            src_base,\n            dst_base,\n            size: hal_copy_size,\n        }\n    });\n\n    let regions = if dst_tex_base.aspect == hal::FormatAspects::DEPTH_STENCIL {\n        regions\n            .flat_map(|region| {\n                let (mut depth, mut stencil) = (region.clone(), region);\n                depth.src_base.aspect = hal::FormatAspects::DEPTH;\n                depth.dst_base.aspect = hal::FormatAspects::DEPTH;\n                stencil.src_base.aspect = hal::FormatAspects::STENCIL;\n                stencil.dst_base.aspect = hal::FormatAspects::STENCIL;\n                [depth, stencil]\n            })\n            .collect::<Vec<_>>()\n    } else if let Some(plane_count) = dst_format.planes() {\n        regions\n            .into_iter()\n            .flat_map(|region| {\n                (0..plane_count).map(move |plane| {\n                    let mut plane_region = region.clone();\n\n                    let plane_aspect = wgt::TextureAspect::from_plane(plane)\n                        .expect(\"expected texture aspect to exist for the plane\");\n                    let plane_aspect = hal::FormatAspects::new(dst_format, plane_aspect);\n                    plane_region.src_base.aspect = plane_aspect;\n                    plane_region.dst_base.aspect = plane_aspect;\n\n                    let (w_subsampling, h_subsampling) =\n                        dst_format.subsampling_factors(Some(plane));\n                    plane_region.src_base.origin.x /= w_subsampling;\n                    plane_region.src_base.origin.y /= h_subsampling;\n                    plane_region.dst_base.origin.x /= w_subsampling;\n                    plane_region.dst_base.origin.y /= h_subsampling;\n\n                    plane_region.size.width /= w_subsampling;\n                    plane_region.size.height /= h_subsampling;\n\n                    plane_region\n                })\n            })\n            .collect::<Vec<_>>()\n    } else {\n        regions.collect::<Vec<_>>()\n    };\n    unsafe {\n        state.raw_encoder.transition_textures(&barriers);\n        state.raw_encoder.copy_texture_to_texture(\n            src_raw,\n            wgt::TextureUses::COPY_SRC,\n            dst_raw,\n            &regions,\n        );\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "wgpu-core/src/command/transition_resources.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\n\nuse thiserror::Error;\nuse wgt::error::{ErrorType, WebGpuError};\n\nuse crate::{\n    command::{encoder::EncodingState, ArcCommand, CommandEncoder, EncoderStateError},\n    device::DeviceError,\n    global::Global,\n    id::{BufferId, CommandEncoderId, TextureId},\n    resource::{Buffer, InvalidResourceError, ParentDevice, Texture},\n    track::ResourceUsageCompatibilityError,\n};\n\nimpl Global {\n    pub fn command_encoder_transition_resources(\n        &self,\n        command_encoder_id: CommandEncoderId,\n        buffer_transitions: impl Iterator<Item = wgt::BufferTransition<BufferId>>,\n        texture_transitions: impl Iterator<Item = wgt::TextureTransition<TextureId>>,\n    ) -> Result<(), EncoderStateError> {\n        profiling::scope!(\"CommandEncoder::transition_resources\");\n\n        let hub = &self.hub;\n\n        // Lock command encoder for recording\n        let cmd_enc = hub.command_encoders.get(command_encoder_id);\n        let mut cmd_buf_data = cmd_enc.data.lock();\n        cmd_buf_data.push_with(|| -> Result<_, TransitionResourcesError> {\n            Ok(ArcCommand::TransitionResources {\n                buffer_transitions: buffer_transitions\n                    .map(|t| {\n                        Ok(wgt::BufferTransition {\n                            buffer: self.resolve_buffer_id(t.buffer)?,\n                            state: t.state,\n                        })\n                    })\n                    .collect::<Result<_, TransitionResourcesError>>()?,\n                texture_transitions: texture_transitions\n                    .map(|t| {\n                        Ok(wgt::TextureTransition {\n                            texture: self.resolve_texture_id(t.texture)?,\n                            selector: t.selector,\n                            state: t.state,\n                        })\n                    })\n                    .collect::<Result<_, TransitionResourcesError>>()?,\n            })\n        })\n    }\n}\n\npub(crate) fn transition_resources(\n    state: &mut EncodingState,\n    buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,\n    texture_transitions: Vec<wgt::TextureTransition<Arc<Texture>>>,\n) -> Result<(), TransitionResourcesError> {\n    let mut usage_scope = state.device.new_usage_scope();\n    let indices = &state.device.tracker_indices;\n    usage_scope.buffers.set_size(indices.buffers.size());\n    usage_scope.textures.set_size(indices.textures.size());\n\n    // Process buffer transitions\n    for buffer_transition in buffer_transitions {\n        buffer_transition.buffer.same_device(state.device)?;\n\n        usage_scope\n            .buffers\n            .merge_single(&buffer_transition.buffer, buffer_transition.state)?;\n    }\n\n    // Process texture transitions\n    for texture_transition in texture_transitions {\n        texture_transition.texture.same_device(state.device)?;\n\n        unsafe {\n            usage_scope.textures.merge_single(\n                &texture_transition.texture,\n                texture_transition.selector,\n                texture_transition.state,\n            )\n        }?;\n    }\n\n    // Record any needed barriers based on tracker data\n    CommandEncoder::insert_barriers_from_scope(\n        state.raw_encoder,\n        state.tracker,\n        &usage_scope,\n        state.snatch_guard,\n    );\n    Ok(())\n}\n\n/// Error encountered while attempting to perform [`Global::command_encoder_transition_resources`].\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum TransitionResourcesError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    ResourceUsage(#[from] ResourceUsageCompatibilityError),\n}\n\nimpl WebGpuError for TransitionResourcesError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::EncoderState(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::ResourceUsage(e) => e.webgpu_error_type(),\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/conv.rs",
    "content": "use wgt::TextureFormatFeatures;\n\nuse crate::resource::{self, TextureDescriptor};\n\n// Some core-only texture format helpers. The helper methods on `TextureFormat`\n// defined in wgpu-types may need to be modified along with the ones here.\n\n#[cfg_attr(any(not(webgl)), expect(unused))]\npub fn is_valid_external_image_copy_dst_texture_format(format: wgt::TextureFormat) -> bool {\n    use wgt::TextureFormat as Tf;\n    match format {\n        Tf::R8Unorm\n        | Tf::R16Float\n        | Tf::R32Float\n        | Tf::Rg8Unorm\n        | Tf::Rg16Float\n        | Tf::Rg32Float\n        | Tf::Rgba8Unorm\n        | Tf::Rgba8UnormSrgb\n        | Tf::Bgra8Unorm\n        | Tf::Bgra8UnormSrgb\n        | Tf::Rgb10a2Unorm\n        | Tf::Rgba16Float\n        | Tf::Rgba32Float => true,\n        _ => false,\n    }\n}\n\npub fn map_buffer_usage(usage: wgt::BufferUsages) -> wgt::BufferUses {\n    let mut u = wgt::BufferUses::empty();\n    u.set(\n        wgt::BufferUses::MAP_READ,\n        usage.contains(wgt::BufferUsages::MAP_READ),\n    );\n    u.set(\n        wgt::BufferUses::MAP_WRITE,\n        usage.contains(wgt::BufferUsages::MAP_WRITE),\n    );\n    u.set(\n        wgt::BufferUses::COPY_SRC,\n        usage.contains(wgt::BufferUsages::COPY_SRC),\n    );\n    u.set(\n        wgt::BufferUses::COPY_DST,\n        usage.contains(wgt::BufferUsages::COPY_DST),\n    );\n    u.set(\n        wgt::BufferUses::INDEX,\n        usage.contains(wgt::BufferUsages::INDEX),\n    );\n    u.set(\n        wgt::BufferUses::VERTEX,\n        usage.contains(wgt::BufferUsages::VERTEX),\n    );\n    u.set(\n        wgt::BufferUses::UNIFORM,\n        usage.contains(wgt::BufferUsages::UNIFORM),\n    );\n    u.set(\n        wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE,\n        usage.contains(wgt::BufferUsages::STORAGE),\n    );\n    u.set(\n        wgt::BufferUses::INDIRECT,\n        usage.contains(wgt::BufferUsages::INDIRECT),\n    );\n    u.set(\n        wgt::BufferUses::QUERY_RESOLVE,\n        usage.contains(wgt::BufferUsages::QUERY_RESOLVE),\n    );\n    u.set(\n        wgt::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n        usage.contains(wgt::BufferUsages::BLAS_INPUT),\n    );\n    u.set(\n        wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n        usage.contains(wgt::BufferUsages::TLAS_INPUT),\n    );\n    u\n}\n\npub fn map_texture_usage(\n    usage: wgt::TextureUsages,\n    aspect: hal::FormatAspects,\n    flags: wgt::TextureFormatFeatureFlags,\n) -> wgt::TextureUses {\n    let mut u = wgt::TextureUses::empty();\n    u.set(\n        wgt::TextureUses::COPY_SRC,\n        usage.contains(wgt::TextureUsages::COPY_SRC),\n    );\n    u.set(\n        wgt::TextureUses::COPY_DST,\n        usage.contains(wgt::TextureUsages::COPY_DST),\n    );\n    u.set(\n        wgt::TextureUses::RESOURCE,\n        usage.contains(wgt::TextureUsages::TEXTURE_BINDING),\n    );\n    if usage.contains(wgt::TextureUsages::STORAGE_BINDING) {\n        u.set(\n            wgt::TextureUses::STORAGE_READ_ONLY,\n            flags.contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY),\n        );\n        u.set(\n            wgt::TextureUses::STORAGE_WRITE_ONLY,\n            flags.contains(wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY),\n        );\n        u.set(\n            wgt::TextureUses::STORAGE_READ_WRITE,\n            flags.contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE),\n        );\n    }\n    let is_color = aspect.intersects(\n        hal::FormatAspects::COLOR\n            | hal::FormatAspects::PLANE_0\n            | hal::FormatAspects::PLANE_1\n            | hal::FormatAspects::PLANE_2,\n    );\n    u.set(\n        wgt::TextureUses::COLOR_TARGET,\n        usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) && is_color,\n    );\n    u.set(\n        wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::DEPTH_STENCIL_WRITE,\n        usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) && !is_color,\n    );\n    u.set(\n        wgt::TextureUses::STORAGE_ATOMIC,\n        usage.contains(wgt::TextureUsages::STORAGE_ATOMIC),\n    );\n    u.set(\n        wgt::TextureUses::TRANSIENT,\n        usage.contains(wgt::TextureUsages::TRANSIENT),\n    );\n    u\n}\n\npub fn map_texture_usage_for_texture(\n    desc: &TextureDescriptor,\n    format_features: &TextureFormatFeatures,\n) -> wgt::TextureUses {\n    // Enforce having COPY_DST/DEPTH_STENCIL_WRITE/COLOR_TARGET otherwise we\n    // wouldn't be able to initialize the texture.\n    map_texture_usage(desc.usage, desc.format.into(), format_features.flags)\n        | if desc.format.is_depth_stencil_format() {\n            wgt::TextureUses::DEPTH_STENCIL_WRITE\n        } else if desc.usage.contains(wgt::TextureUsages::COPY_DST) {\n            wgt::TextureUses::COPY_DST // (set already)\n        } else {\n            // Use COPY_DST only if we can't use COLOR_TARGET\n            if format_features\n                .allowed_usages\n                .contains(wgt::TextureUsages::RENDER_ATTACHMENT)\n                && desc.dimension == wgt::TextureDimension::D2\n            // Render targets dimension must be 2d\n            {\n                wgt::TextureUses::COLOR_TARGET\n            } else {\n                wgt::TextureUses::COPY_DST\n            }\n        }\n}\n\npub fn map_texture_usage_from_hal(uses: wgt::TextureUses) -> wgt::TextureUsages {\n    let mut u = wgt::TextureUsages::empty();\n    u.set(\n        wgt::TextureUsages::COPY_SRC,\n        uses.contains(wgt::TextureUses::COPY_SRC),\n    );\n    u.set(\n        wgt::TextureUsages::COPY_DST,\n        uses.contains(wgt::TextureUses::COPY_DST),\n    );\n    u.set(\n        wgt::TextureUsages::TEXTURE_BINDING,\n        uses.contains(wgt::TextureUses::RESOURCE),\n    );\n    u.set(\n        wgt::TextureUsages::STORAGE_BINDING,\n        uses.intersects(\n            wgt::TextureUses::STORAGE_READ_ONLY\n                | wgt::TextureUses::STORAGE_WRITE_ONLY\n                | wgt::TextureUses::STORAGE_READ_WRITE,\n        ),\n    );\n    u.set(\n        wgt::TextureUsages::RENDER_ATTACHMENT,\n        uses.contains(wgt::TextureUses::COLOR_TARGET),\n    );\n    u.set(\n        wgt::TextureUsages::STORAGE_ATOMIC,\n        uses.contains(wgt::TextureUses::STORAGE_ATOMIC),\n    );\n    u.set(\n        wgt::TextureUsages::TRANSIENT,\n        uses.contains(wgt::TextureUses::TRANSIENT),\n    );\n    u\n}\n\n/// Check the requested texture size against the supported limits.\n///\n/// This function implements the texture size and sample count checks in [vtd\n/// dimension step]. The format checks are elsewhere in [`create_texture`]`.\n///\n/// Note that while there is some basic checking of the sample count here, there\n/// is an additional set of checks when `sample_count > 1` elsewhere in\n/// [`create_texture`]`.\n///\n/// [vtd dimension step]: https://www.w3.org/TR/2025/CRD-webgpu-20251120/#:~:text=or%204.-,If%20descriptor.dimension%20is\n/// [`create_texture`]: crate::device::Device::create_texture\npub fn check_texture_dimension_size(\n    dimension: wgt::TextureDimension,\n    wgt::Extent3d {\n        width,\n        height,\n        depth_or_array_layers,\n    }: wgt::Extent3d,\n    sample_size: u32,\n    limits: &wgt::Limits,\n) -> Result<(), resource::TextureDimensionError> {\n    use resource::{TextureDimensionError as Tde, TextureErrorDimension as Ted};\n    use wgt::TextureDimension::*;\n\n    let (extent_limits, sample_limit) = match dimension {\n        D1 => ([limits.max_texture_dimension_1d, 1, 1], 1),\n        D2 => (\n            [\n                limits.max_texture_dimension_2d,\n                limits.max_texture_dimension_2d,\n                limits.max_texture_array_layers,\n            ],\n            32,\n        ),\n        D3 => (\n            [\n                limits.max_texture_dimension_3d,\n                limits.max_texture_dimension_3d,\n                limits.max_texture_dimension_3d,\n            ],\n            1,\n        ),\n    };\n\n    for (&dim, (&given, &limit)) in [Ted::X, Ted::Y, Ted::Z].iter().zip(\n        [width, height, depth_or_array_layers]\n            .iter()\n            .zip(extent_limits.iter()),\n    ) {\n        if given == 0 {\n            return Err(Tde::Zero(dim));\n        }\n        if given > limit {\n            return Err(Tde::LimitExceeded { dim, given, limit });\n        }\n    }\n    if sample_size == 0 || sample_size > sample_limit || !sample_size.is_power_of_two() {\n        return Err(Tde::InvalidSampleCount(sample_size));\n    }\n\n    Ok(())\n}\n\npub fn bind_group_layout_flags(features: wgt::Features) -> hal::BindGroupLayoutFlags {\n    let mut flags = hal::BindGroupLayoutFlags::empty();\n    flags.set(\n        hal::BindGroupLayoutFlags::PARTIALLY_BOUND,\n        features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY),\n    );\n    flags\n}\n"
  },
  {
    "path": "wgpu-core/src/device/bgl.rs",
    "content": "use core::hash::{Hash, Hasher};\n\nuse crate::{\n    binding_model::{self},\n    FastIndexMap,\n};\n\n/// Where a given BGL came from.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum Origin {\n    /// The bind group layout was created by the user and is present in the BGL resource pool.\n    Pool,\n    /// The bind group layout was derived and is not present in the BGL resource pool.\n    Derived,\n}\n\n/// A HashMap-like structure that stores a BindGroupLayouts [`wgt::BindGroupLayoutEntry`]s.\n///\n/// It is hashable, so bind group layouts can be deduplicated.\n#[derive(Debug, Default, Clone, Eq)]\npub struct EntryMap {\n    /// We use a IndexMap here so that we can sort the entries by their binding index,\n    /// guaranteeing that the hash of equivalent layouts will be the same.\n    inner: FastIndexMap<u32, wgt::BindGroupLayoutEntry>,\n    /// We keep track of whether the map is sorted or not, so that we can assert that\n    /// it is sorted, so that PartialEq and Hash will be stable.\n    ///\n    /// We only need sorted if it is used in a Hash or PartialEq, so we never need\n    /// to actively sort it.\n    sorted: bool,\n}\n\nimpl PartialEq for EntryMap {\n    fn eq(&self, other: &Self) -> bool {\n        self.assert_sorted();\n        other.assert_sorted();\n\n        self.inner == other.inner\n    }\n}\n\nimpl Hash for EntryMap {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.assert_sorted();\n\n        // We don't need to hash the keys, since they are just extracted from the values.\n        //\n        // We know this is stable and will match the behavior of PartialEq as we ensure\n        // that the array is sorted.\n        for entry in self.inner.values() {\n            entry.hash(state);\n        }\n    }\n}\n\nimpl EntryMap {\n    fn assert_sorted(&self) {\n        assert!(self.sorted);\n    }\n\n    /// Create a new [`EntryMap`] from a slice of [`wgt::BindGroupLayoutEntry`]s.\n    ///\n    /// Errors if there are duplicate bindings or if any binding index is greater than\n    /// the device's limits.\n    pub fn from_entries(\n        entries: &[wgt::BindGroupLayoutEntry],\n    ) -> Result<Self, binding_model::CreateBindGroupLayoutError> {\n        let mut inner = FastIndexMap::with_capacity_and_hasher(entries.len(), Default::default());\n        for entry in entries {\n            if inner.insert(entry.binding, *entry).is_some() {\n                return Err(binding_model::CreateBindGroupLayoutError::ConflictBinding(\n                    entry.binding,\n                ));\n            }\n        }\n        inner.sort_unstable_keys();\n\n        Ok(Self {\n            inner,\n            sorted: true,\n        })\n    }\n\n    /// Get the count of [`wgt::BindGroupLayoutEntry`]s in this map.\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Get the [`wgt::BindGroupLayoutEntry`] for the given binding index.\n    pub fn get(&self, binding: u32) -> Option<&wgt::BindGroupLayoutEntry> {\n        self.inner.get(&binding)\n    }\n\n    /// Iterator over all the binding indices in this map.\n    pub fn indices(&self) -> impl ExactSizeIterator<Item = u32> + '_ {\n        self.inner.keys().copied()\n    }\n\n    /// Iterator over all the [`wgt::BindGroupLayoutEntry`]s in this map.\n    pub fn values(&self) -> impl ExactSizeIterator<Item = &wgt::BindGroupLayoutEntry> + '_ {\n        self.inner.values()\n    }\n\n    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&u32, &wgt::BindGroupLayoutEntry)> + '_ {\n        self.inner.iter()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.inner.is_empty()\n    }\n\n    pub fn contains_key(&self, key: u32) -> bool {\n        self.inner.contains_key(&key)\n    }\n\n    pub fn entry(&mut self, key: u32) -> indexmap::map::Entry<'_, u32, wgt::BindGroupLayoutEntry> {\n        self.sorted = false;\n        self.inner.entry(key)\n    }\n\n    pub fn sort(&mut self) {\n        self.inner.sort_unstable_keys();\n        self.sorted = true;\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/global.rs",
    "content": "use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};\nuse core::{ptr::NonNull, sync::atomic::Ordering};\n\n#[cfg(feature = \"trace\")]\nuse crate::device::trace::{self, IntoTrace};\nuse crate::{\n    api_log,\n    binding_model::{\n        self, BindGroupEntry, BindingResource, BufferBinding, ResolvedBindGroupDescriptor,\n        ResolvedBindGroupEntry, ResolvedBindingResource, ResolvedBufferBinding,\n    },\n    command::{self, CommandEncoder},\n    conv,\n    device::{life::WaitIdleError, DeviceError, DeviceLostClosure},\n    global::Global,\n    id::{self, AdapterId, DeviceId, QueueId, SurfaceId},\n    instance::{self, Adapter, Surface},\n    pipeline::{\n        self, RenderPipelineVertexProcessor, ResolvedComputePipelineDescriptor,\n        ResolvedFragmentState, ResolvedGeneralRenderPipelineDescriptor, ResolvedMeshState,\n        ResolvedProgrammableStageDescriptor, ResolvedTaskState, ResolvedVertexState,\n    },\n    present,\n    resource::{\n        self, BufferAccessError, BufferAccessResult, BufferMapOperation, CreateBufferError,\n        Fallible,\n    },\n    storage::Storage,\n    Label, LabelHelpers,\n};\n\nuse wgt::{BufferAddress, TextureFormat};\n\nuse super::UserClosures;\n\nimpl Global {\n    pub fn adapter_is_surface_supported(\n        &self,\n        adapter_id: AdapterId,\n        surface_id: SurfaceId,\n    ) -> bool {\n        let surface = self.surfaces.get(surface_id);\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.is_surface_supported(&surface)\n    }\n\n    pub fn surface_get_capabilities(\n        &self,\n        surface_id: SurfaceId,\n        adapter_id: AdapterId,\n    ) -> Result<wgt::SurfaceCapabilities, instance::GetSurfaceSupportError> {\n        profiling::scope!(\"Surface::get_capabilities\");\n        self.fetch_adapter_and_surface::<_, _>(surface_id, adapter_id, |adapter, surface| {\n            let mut hal_caps = surface.get_capabilities(adapter)?;\n\n            hal_caps.formats.sort_by_key(|f| !f.is_srgb());\n\n            let usages = conv::map_texture_usage_from_hal(hal_caps.usage);\n\n            Ok(wgt::SurfaceCapabilities {\n                formats: hal_caps.formats,\n                present_modes: hal_caps.present_modes,\n                alpha_modes: hal_caps.composite_alpha_modes,\n                usages,\n            })\n        })\n    }\n\n    fn fetch_adapter_and_surface<F: FnOnce(&Adapter, &Surface) -> B, B>(\n        &self,\n        surface_id: SurfaceId,\n        adapter_id: AdapterId,\n        get_supported_callback: F,\n    ) -> B {\n        let surface = self.surfaces.get(surface_id);\n        let adapter = self.hub.adapters.get(adapter_id);\n        get_supported_callback(&adapter, &surface)\n    }\n\n    pub fn device_features(&self, device_id: DeviceId) -> wgt::Features {\n        let device = self.hub.devices.get(device_id);\n        device.features\n    }\n\n    pub fn device_limits(&self, device_id: DeviceId) -> wgt::Limits {\n        let device = self.hub.devices.get(device_id);\n        device.limits.clone()\n    }\n\n    pub fn device_adapter_info(&self, device_id: DeviceId) -> wgt::AdapterInfo {\n        let device = self.hub.devices.get(device_id);\n        device.adapter.get_info()\n    }\n\n    pub fn device_downlevel_properties(&self, device_id: DeviceId) -> wgt::DownlevelCapabilities {\n        let device = self.hub.devices.get(device_id);\n        device.downlevel.clone()\n    }\n\n    pub fn device_create_buffer(\n        &self,\n        device_id: DeviceId,\n        desc: &resource::BufferDescriptor,\n        id_in: Option<id::BufferId>,\n    ) -> (id::BufferId, Option<CreateBufferError>) {\n        profiling::scope!(\"Device::create_buffer\");\n\n        let hub = &self.hub;\n        let fid = hub.buffers.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let buffer = match device.create_buffer(desc) {\n                Ok(buffer) => buffer,\n                Err(e) => {\n                    break 'error e;\n                }\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                let mut desc = desc.clone();\n                let mapped_at_creation = core::mem::replace(&mut desc.mapped_at_creation, false);\n                if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {\n                    desc.usage |= wgt::BufferUsages::COPY_DST;\n                }\n                trace.add(trace::Action::CreateBuffer(buffer.to_trace(), desc));\n            }\n\n            let id = fid.assign(Fallible::Valid(buffer));\n\n            api_log!(\n                \"Device::create_buffer({:?}{}) -> {id:?}\",\n                desc.label.as_deref().unwrap_or(\"\"),\n                if desc.mapped_at_creation {\n                    \", mapped_at_creation\"\n                } else {\n                    \"\"\n                }\n            );\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    /// Assign `id_in` an error with the given `label`.\n    ///\n    /// Ensure that future attempts to use `id_in` as a buffer ID will propagate\n    /// the error, following the WebGPU [\"contagious invalidity\"] style.\n    ///\n    /// Firefox uses this function to comply strictly with the WebGPU spec,\n    /// which requires [`GPUBufferDescriptor`] validation to be generated on the\n    /// Device timeline and leave the newly created [`GPUBuffer`] invalid.\n    ///\n    /// Ideally, we would simply let [`device_create_buffer`] take care of all\n    /// of this, but some errors must be detected before we can even construct a\n    /// [`wgpu_types::BufferDescriptor`] to give it. For example, the WebGPU API\n    /// allows a `GPUBufferDescriptor`'s [`usage`] property to be any WebIDL\n    /// `unsigned long` value, but we can't construct a\n    /// [`wgpu_types::BufferUsages`] value from values with unassigned bits\n    /// set. This means we must validate `usage` before we can call\n    /// `device_create_buffer`.\n    ///\n    /// When that validation fails, we must arrange for the buffer id to be\n    /// considered invalid. This method provides the means to do so.\n    ///\n    /// [\"contagious invalidity\"]: https://www.w3.org/TR/webgpu/#invalidity\n    /// [`GPUBufferDescriptor`]: https://www.w3.org/TR/webgpu/#dictdef-gpubufferdescriptor\n    /// [`GPUBuffer`]: https://www.w3.org/TR/webgpu/#gpubuffer\n    /// [`wgpu_types::BufferDescriptor`]: wgt::BufferDescriptor\n    /// [`device_create_buffer`]: Global::device_create_buffer\n    /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage\n    /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages\n    pub fn create_buffer_error(\n        &self,\n        id_in: Option<id::BufferId>,\n        desc: &resource::BufferDescriptor,\n    ) {\n        let fid = self.hub.buffers.prepare(id_in);\n        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n    }\n\n    /// Assign `id_in` an error with the given `label`.\n    ///\n    /// See [`Self::create_buffer_error`] for more context and explanation.\n    pub fn create_render_bundle_error(\n        &self,\n        id_in: Option<id::RenderBundleId>,\n        desc: &command::RenderBundleDescriptor,\n    ) {\n        let fid = self.hub.render_bundles.prepare(id_in);\n        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n    }\n\n    /// Assign `id_in` an error with the given `label`.\n    ///\n    /// See [`Self::create_buffer_error`] for more context and explanation.\n    pub fn create_texture_error(\n        &self,\n        id_in: Option<id::TextureId>,\n        desc: &resource::TextureDescriptor,\n    ) {\n        let fid = self.hub.textures.prepare(id_in);\n        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n    }\n\n    /// Assign `id_in` an error with the given `label`.\n    ///\n    /// See [`Self::create_buffer_error`] for more context and explanation.\n    pub fn create_external_texture_error(\n        &self,\n        id_in: Option<id::ExternalTextureId>,\n        desc: &resource::ExternalTextureDescriptor,\n    ) {\n        let fid = self.hub.external_textures.prepare(id_in);\n        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n    }\n\n    /// Assign `id_in` an error with the given `label`.\n    ///\n    /// In JavaScript environments, it is possible to call `GPUDevice.createBindGroupLayout` with\n    /// entries that are invalid. Because our Rust's types for bind group layouts prevent even\n    /// calling [`Self::device_create_bind_group`], we let standards-compliant environments\n    /// register an invalid bind group layout so this crate's API can still be consistently used.\n    ///\n    /// See [`Self::create_buffer_error`] for additional context and explanation.\n    pub fn create_bind_group_layout_error(\n        &self,\n        id_in: Option<id::BindGroupLayoutId>,\n        label: Option<Cow<'_, str>>,\n    ) {\n        let fid = self.hub.bind_group_layouts.prepare(id_in);\n        fid.assign(Fallible::Invalid(Arc::new(label.to_string())));\n    }\n\n    pub fn buffer_destroy(&self, buffer_id: id::BufferId) {\n        profiling::scope!(\"Buffer::destroy\");\n        api_log!(\"Buffer::destroy {buffer_id:?}\");\n\n        let hub = &self.hub;\n\n        let Ok(buffer) = hub.buffers.get(buffer_id).get() else {\n            // If the buffer is already invalid, there's nothing to do.\n            return;\n        };\n\n        #[cfg(feature = \"trace\")]\n        if let Some(trace) = buffer.device.trace.lock().as_mut() {\n            trace.add(trace::Action::FreeBuffer(buffer.to_trace()));\n        }\n\n        let _ = buffer.unmap();\n\n        buffer.destroy();\n    }\n\n    pub fn buffer_drop(&self, buffer_id: id::BufferId) {\n        profiling::scope!(\"Buffer::drop\");\n        api_log!(\"Buffer::drop {buffer_id:?}\");\n\n        let hub = &self.hub;\n\n        let buffer = match hub.buffers.remove(buffer_id).get() {\n            Ok(buffer) => buffer,\n            Err(_) => {\n                return;\n            }\n        };\n\n        #[cfg(feature = \"trace\")]\n        if let Some(t) = buffer.device.trace.lock().as_mut() {\n            t.add(trace::Action::DestroyBuffer(buffer.to_trace()));\n        }\n\n        let _ = buffer.unmap();\n    }\n\n    pub fn device_create_texture(\n        &self,\n        device_id: DeviceId,\n        desc: &resource::TextureDescriptor,\n        id_in: Option<id::TextureId>,\n    ) -> (id::TextureId, Option<resource::CreateTextureError>) {\n        profiling::scope!(\"Device::create_texture\");\n\n        let hub = &self.hub;\n\n        let fid = hub.textures.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let texture = match device.create_texture(desc) {\n                Ok(texture) => texture,\n                Err(error) => break 'error error,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateTexture(\n                    texture.to_trace(),\n                    desc.clone(),\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(texture));\n            api_log!(\"Device::create_texture({desc:?}) -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    /// # Safety\n    ///\n    /// - `hal_texture` must be created from `device_id` corresponding raw handle.\n    /// - `hal_texture` must be created respecting `desc`\n    /// - `hal_texture` must be initialized\n    pub unsafe fn create_texture_from_hal(\n        &self,\n        hal_texture: Box<dyn hal::DynTexture>,\n        device_id: DeviceId,\n        desc: &resource::TextureDescriptor,\n        id_in: Option<id::TextureId>,\n    ) -> (id::TextureId, Option<resource::CreateTextureError>) {\n        profiling::scope!(\"Device::create_texture_from_hal\");\n\n        let hub = &self.hub;\n\n        let fid = hub.textures.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let texture = match device.create_texture_from_hal(hal_texture, desc) {\n                Ok(texture) => texture,\n                Err(error) => break 'error error,\n            };\n\n            // NB: Any change done through the raw texture handle will not be\n            // recorded in the replay\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateTexture(\n                    texture.to_trace(),\n                    desc.clone(),\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(texture));\n            api_log!(\"Device::create_texture({desc:?}) -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    /// # Safety\n    ///\n    /// - `hal_buffer` must be created from `device_id` corresponding raw handle.\n    /// - `hal_buffer` must be created respecting `desc`\n    /// - `hal_buffer` must be initialized\n    /// - `hal_buffer` must not have zero size.\n    pub unsafe fn create_buffer_from_hal<A: hal::Api>(\n        &self,\n        hal_buffer: A::Buffer,\n        device_id: DeviceId,\n        desc: &resource::BufferDescriptor,\n        id_in: Option<id::BufferId>,\n    ) -> (id::BufferId, Option<CreateBufferError>) {\n        profiling::scope!(\"Device::create_buffer\");\n\n        let hub = &self.hub;\n        let fid = hub.buffers.prepare(id_in);\n\n        let device = self.hub.devices.get(device_id);\n\n        let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) };\n\n        // NB: Any change done through the raw buffer handle will not be\n        // recorded in the replay\n        #[cfg(feature = \"trace\")]\n        if let Some(trace) = device.trace.lock().as_mut() {\n            match &buffer {\n                Fallible::Valid(arc) => {\n                    trace.add(trace::Action::CreateBuffer(arc.to_trace(), desc.clone()))\n                }\n                Fallible::Invalid(_) => {}\n            }\n        }\n\n        let id = fid.assign(buffer);\n        api_log!(\"Device::create_buffer -> {id:?}\");\n\n        (id, err)\n    }\n\n    pub fn texture_destroy(&self, texture_id: id::TextureId) {\n        profiling::scope!(\"Texture::destroy\");\n        api_log!(\"Texture::destroy {texture_id:?}\");\n\n        let hub = &self.hub;\n\n        let Ok(texture) = hub.textures.get(texture_id).get() else {\n            // If the texture is already invalid, there's nothing to do.\n            return;\n        };\n\n        #[cfg(feature = \"trace\")]\n        if let Some(trace) = texture.device.trace.lock().as_mut() {\n            trace.add(trace::Action::FreeTexture(texture.to_trace()));\n        }\n\n        texture.destroy();\n    }\n\n    pub fn texture_drop(&self, texture_id: id::TextureId) {\n        profiling::scope!(\"Texture::drop\");\n        api_log!(\"Texture::drop {texture_id:?}\");\n\n        let hub = &self.hub;\n\n        let _texture = hub.textures.remove(texture_id);\n        #[cfg(feature = \"trace\")]\n        if let Ok(texture) = _texture.get() {\n            if let Some(t) = texture.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyTexture(texture.to_trace()));\n            }\n        }\n    }\n\n    pub fn texture_create_view(\n        &self,\n        texture_id: id::TextureId,\n        desc: &resource::TextureViewDescriptor,\n        id_in: Option<id::TextureViewId>,\n    ) -> (id::TextureViewId, Option<resource::CreateTextureViewError>) {\n        profiling::scope!(\"Texture::create_view\");\n\n        let hub = &self.hub;\n\n        let fid = hub.texture_views.prepare(id_in);\n\n        let error = 'error: {\n            let texture = match hub.textures.get(texture_id).get() {\n                Ok(texture) => texture,\n                Err(e) => break 'error e.into(),\n            };\n            let device = &texture.device;\n\n            let view = match device.create_texture_view(&texture, desc) {\n                Ok(view) => view,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateTextureView {\n                    id: view.to_trace(),\n                    parent: texture.to_trace(),\n                    desc: desc.clone(),\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(view));\n\n            api_log!(\"Texture::create_view({texture_id:?}) -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn texture_view_drop(&self, texture_view_id: id::TextureViewId) {\n        profiling::scope!(\"TextureView::drop\");\n        api_log!(\"TextureView::drop {texture_view_id:?}\");\n\n        let hub = &self.hub;\n\n        let _view = hub.texture_views.remove(texture_view_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(view) = _view.get() {\n            if let Some(t) = view.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyTextureView(view.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_external_texture(\n        &self,\n        device_id: DeviceId,\n        desc: &resource::ExternalTextureDescriptor,\n        planes: &[id::TextureViewId],\n        id_in: Option<id::ExternalTextureId>,\n    ) -> (\n        id::ExternalTextureId,\n        Option<resource::CreateExternalTextureError>,\n    ) {\n        profiling::scope!(\"Device::create_external_texture\");\n\n        let hub = &self.hub;\n\n        let fid = hub.external_textures.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let planes = planes\n                .iter()\n                .map(|plane_id| self.hub.texture_views.get(*plane_id).get())\n                .collect::<Result<Vec<_>, _>>();\n            let planes = match planes {\n                Ok(planes) => planes,\n                Err(error) => break 'error error.into(),\n            };\n\n            let external_texture = match device.create_external_texture(desc, &planes) {\n                Ok(external_texture) => external_texture,\n                Err(error) => break 'error error,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                let planes = Box::from(\n                    planes\n                        .into_iter()\n                        .map(|plane| plane.to_trace())\n                        .collect::<Vec<_>>(),\n                );\n                trace.add(trace::Action::CreateExternalTexture {\n                    id: external_texture.to_trace(),\n                    desc: desc.clone(),\n                    planes,\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(external_texture));\n            api_log!(\"Device::create_external_texture({desc:?}) -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn external_texture_destroy(&self, external_texture_id: id::ExternalTextureId) {\n        profiling::scope!(\"ExternalTexture::destroy\");\n        api_log!(\"ExternalTexture::destroy {external_texture_id:?}\");\n\n        let hub = &self.hub;\n\n        let Ok(external_texture) = hub.external_textures.get(external_texture_id).get() else {\n            // If the external texture is already invalid, there's nothing to do.\n            return;\n        };\n\n        #[cfg(feature = \"trace\")]\n        if let Some(trace) = external_texture.device.trace.lock().as_mut() {\n            trace.add(trace::Action::FreeExternalTexture(\n                external_texture.to_trace(),\n            ));\n        }\n\n        external_texture.destroy();\n    }\n\n    pub fn external_texture_drop(&self, external_texture_id: id::ExternalTextureId) {\n        profiling::scope!(\"ExternalTexture::drop\");\n        api_log!(\"ExternalTexture::drop {external_texture_id:?}\");\n\n        let hub = &self.hub;\n\n        let _external_texture = hub.external_textures.remove(external_texture_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(external_texture) = _external_texture.get() {\n            if let Some(t) = external_texture.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyExternalTexture(\n                    external_texture.to_trace(),\n                ));\n            }\n        }\n    }\n\n    pub fn device_create_sampler(\n        &self,\n        device_id: DeviceId,\n        desc: &resource::SamplerDescriptor,\n        id_in: Option<id::SamplerId>,\n    ) -> (id::SamplerId, Option<resource::CreateSamplerError>) {\n        profiling::scope!(\"Device::create_sampler\");\n\n        let hub = &self.hub;\n        let fid = hub.samplers.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let sampler = match device.create_sampler(desc) {\n                Ok(sampler) => sampler,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateSampler(\n                    sampler.to_trace(),\n                    desc.clone(),\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(sampler));\n            api_log!(\"Device::create_sampler -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn sampler_drop(&self, sampler_id: id::SamplerId) {\n        profiling::scope!(\"Sampler::drop\");\n        api_log!(\"Sampler::drop {sampler_id:?}\");\n\n        let hub = &self.hub;\n\n        let _sampler = hub.samplers.remove(sampler_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(sampler) = _sampler.get() {\n            if let Some(t) = sampler.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroySampler(sampler.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_bind_group_layout(\n        &self,\n        device_id: DeviceId,\n        desc: &binding_model::BindGroupLayoutDescriptor,\n        id_in: Option<id::BindGroupLayoutId>,\n    ) -> (\n        id::BindGroupLayoutId,\n        Option<binding_model::CreateBindGroupLayoutError>,\n    ) {\n        profiling::scope!(\"Device::create_bind_group_layout\");\n\n        let hub = &self.hub;\n        let fid = hub.bind_group_layouts.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let layout = match device.create_bind_group_layout(desc) {\n                Ok(layout) => layout,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateBindGroupLayout(\n                    layout.to_trace(),\n                    desc.clone(),\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(layout.clone()));\n\n            api_log!(\"Device::create_bind_group_layout -> {id:?}\");\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn bind_group_layout_drop(&self, bind_group_layout_id: id::BindGroupLayoutId) {\n        profiling::scope!(\"BindGroupLayout::drop\");\n        api_log!(\"BindGroupLayout::drop {bind_group_layout_id:?}\");\n\n        let hub = &self.hub;\n\n        let _layout = hub.bind_group_layouts.remove(bind_group_layout_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(layout) = _layout.get() {\n            if let Some(t) = layout.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyBindGroupLayout(layout.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_pipeline_layout(\n        &self,\n        device_id: DeviceId,\n        desc: &binding_model::PipelineLayoutDescriptor,\n        id_in: Option<id::PipelineLayoutId>,\n    ) -> (\n        id::PipelineLayoutId,\n        Option<binding_model::CreatePipelineLayoutError>,\n    ) {\n        profiling::scope!(\"Device::create_pipeline_layout\");\n\n        let hub = &self.hub;\n        let fid = hub.pipeline_layouts.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            if let Err(e) = device.check_is_valid() {\n                break 'error e.into();\n            }\n\n            let bind_group_layouts = {\n                let bind_group_layouts_guard = hub.bind_group_layouts.read();\n                desc.bind_group_layouts\n                    .iter()\n                    .map(|bgl_id| match bgl_id {\n                        Some(bgl_id) => bind_group_layouts_guard.get(*bgl_id).get().map(Some),\n                        None => Ok(None),\n                    })\n                    .collect::<Result<Vec<_>, _>>()\n            };\n\n            let bind_group_layouts = match bind_group_layouts {\n                Ok(bind_group_layouts) => bind_group_layouts,\n                Err(e) => break 'error e.into(),\n            };\n\n            let desc = binding_model::ResolvedPipelineLayoutDescriptor {\n                label: desc.label.clone(),\n                bind_group_layouts: Cow::Owned(bind_group_layouts),\n                immediate_size: desc.immediate_size,\n            };\n\n            let layout = match device.create_pipeline_layout(&desc) {\n                Ok(layout) => layout,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreatePipelineLayout(\n                    layout.to_trace(),\n                    desc.to_trace(),\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(layout));\n            api_log!(\"Device::create_pipeline_layout -> {id:?}\");\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn pipeline_layout_drop(&self, pipeline_layout_id: id::PipelineLayoutId) {\n        profiling::scope!(\"PipelineLayout::drop\");\n        api_log!(\"PipelineLayout::drop {pipeline_layout_id:?}\");\n\n        let hub = &self.hub;\n\n        let _layout = hub.pipeline_layouts.remove(pipeline_layout_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(layout) = _layout.get() {\n            if let Some(t) = layout.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyPipelineLayout(layout.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_bind_group(\n        &self,\n        device_id: DeviceId,\n        desc: &binding_model::BindGroupDescriptor,\n        id_in: Option<id::BindGroupId>,\n    ) -> (id::BindGroupId, Option<binding_model::CreateBindGroupError>) {\n        profiling::scope!(\"Device::create_bind_group\");\n\n        let hub = &self.hub;\n        let fid = hub.bind_groups.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            if let Err(e) = device.check_is_valid() {\n                break 'error e.into();\n            }\n\n            let layout = match hub.bind_group_layouts.get(desc.layout).get() {\n                Ok(layout) => layout,\n                Err(e) => break 'error e.into(),\n            };\n\n            fn resolve_entry<'a>(\n                e: &BindGroupEntry<'a>,\n                buffer_storage: &Storage<Fallible<resource::Buffer>>,\n                sampler_storage: &Storage<Fallible<resource::Sampler>>,\n                texture_view_storage: &Storage<Fallible<resource::TextureView>>,\n                tlas_storage: &Storage<Fallible<resource::Tlas>>,\n                external_texture_storage: &Storage<Fallible<resource::ExternalTexture>>,\n            ) -> Result<ResolvedBindGroupEntry<'a>, binding_model::CreateBindGroupError>\n            {\n                let resolve_buffer = |bb: &BufferBinding| {\n                    buffer_storage\n                        .get(bb.buffer)\n                        .get()\n                        .map(|buffer| ResolvedBufferBinding {\n                            buffer,\n                            offset: bb.offset,\n                            size: bb.size,\n                        })\n                        .map_err(binding_model::CreateBindGroupError::from)\n                };\n                let resolve_sampler = |id: &id::SamplerId| {\n                    sampler_storage\n                        .get(*id)\n                        .get()\n                        .map_err(binding_model::CreateBindGroupError::from)\n                };\n                let resolve_view = |id: &id::TextureViewId| {\n                    texture_view_storage\n                        .get(*id)\n                        .get()\n                        .map_err(binding_model::CreateBindGroupError::from)\n                };\n                let resolve_tlas = |id: &id::TlasId| {\n                    tlas_storage\n                        .get(*id)\n                        .get()\n                        .map_err(binding_model::CreateBindGroupError::from)\n                };\n                let resolve_external_texture = |id: &id::ExternalTextureId| {\n                    external_texture_storage\n                        .get(*id)\n                        .get()\n                        .map_err(binding_model::CreateBindGroupError::from)\n                };\n                let resource = match e.resource {\n                    BindingResource::Buffer(ref buffer) => {\n                        ResolvedBindingResource::Buffer(resolve_buffer(buffer)?)\n                    }\n                    BindingResource::BufferArray(ref buffers) => {\n                        let buffers = buffers\n                            .iter()\n                            .map(resolve_buffer)\n                            .collect::<Result<Vec<_>, _>>()?;\n                        ResolvedBindingResource::BufferArray(Cow::Owned(buffers))\n                    }\n                    BindingResource::Sampler(ref sampler) => {\n                        ResolvedBindingResource::Sampler(resolve_sampler(sampler)?)\n                    }\n                    BindingResource::SamplerArray(ref samplers) => {\n                        let samplers = samplers\n                            .iter()\n                            .map(resolve_sampler)\n                            .collect::<Result<Vec<_>, _>>()?;\n                        ResolvedBindingResource::SamplerArray(Cow::Owned(samplers))\n                    }\n                    BindingResource::TextureView(ref view) => {\n                        ResolvedBindingResource::TextureView(resolve_view(view)?)\n                    }\n                    BindingResource::TextureViewArray(ref views) => {\n                        let views = views\n                            .iter()\n                            .map(resolve_view)\n                            .collect::<Result<Vec<_>, _>>()?;\n                        ResolvedBindingResource::TextureViewArray(Cow::Owned(views))\n                    }\n                    BindingResource::AccelerationStructure(ref tlas) => {\n                        ResolvedBindingResource::AccelerationStructure(resolve_tlas(tlas)?)\n                    }\n                    BindingResource::AccelerationStructureArray(ref tlas_array) => {\n                        let tlas_array = tlas_array\n                            .iter()\n                            .map(resolve_tlas)\n                            .collect::<Result<Vec<_>, _>>()?;\n                        ResolvedBindingResource::AccelerationStructureArray(Cow::Owned(tlas_array))\n                    }\n                    BindingResource::ExternalTexture(ref et) => {\n                        ResolvedBindingResource::ExternalTexture(resolve_external_texture(et)?)\n                    }\n                };\n                Ok(ResolvedBindGroupEntry {\n                    binding: e.binding,\n                    resource,\n                })\n            }\n\n            let entries = {\n                let buffer_guard = hub.buffers.read();\n                let texture_view_guard = hub.texture_views.read();\n                let sampler_guard = hub.samplers.read();\n                let tlas_guard = hub.tlas_s.read();\n                let external_texture_guard = hub.external_textures.read();\n                desc.entries\n                    .iter()\n                    .map(|e| {\n                        resolve_entry(\n                            e,\n                            &buffer_guard,\n                            &sampler_guard,\n                            &texture_view_guard,\n                            &tlas_guard,\n                            &external_texture_guard,\n                        )\n                    })\n                    .collect::<Result<Vec<_>, _>>()\n            };\n            let entries = match entries {\n                Ok(entries) => Cow::Owned(entries),\n                Err(e) => break 'error e,\n            };\n\n            let desc = ResolvedBindGroupDescriptor {\n                label: desc.label.clone(),\n                layout,\n                entries,\n            };\n            #[cfg(feature = \"trace\")]\n            let trace_desc = (&desc).to_trace();\n\n            let bind_group = match device.create_bind_group(desc) {\n                Ok(bind_group) => bind_group,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateBindGroup(\n                    bind_group.to_trace(),\n                    trace_desc,\n                ));\n            }\n\n            let id = fid.assign(Fallible::Valid(bind_group));\n\n            api_log!(\"Device::create_bind_group -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn bind_group_drop(&self, bind_group_id: id::BindGroupId) {\n        profiling::scope!(\"BindGroup::drop\");\n        api_log!(\"BindGroup::drop {bind_group_id:?}\");\n\n        let hub = &self.hub;\n\n        let _bind_group = hub.bind_groups.remove(bind_group_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(bind_group) = _bind_group.get() {\n            if let Some(t) = bind_group.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyBindGroup(bind_group.to_trace()));\n            }\n        }\n    }\n\n    /// Create a shader module with the given `source`.\n    ///\n    /// <div class=\"warning\">\n    // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!\n    // NOTE: Keep this in sync with `wgpu::Device::create_shader_module`!\n    ///\n    /// This function may consume a lot of stack space. Compiler-enforced limits for parsing\n    /// recursion exist; if shader compilation runs into them, it will return an error gracefully.\n    /// However, on some build profiles and platforms, the default stack size for a thread may be\n    /// exceeded before this limit is reached during parsing. Callers should ensure that there is\n    /// enough stack space for this, particularly if calls to this method are exposed to user\n    /// input.\n    ///\n    /// </div>\n    pub fn device_create_shader_module(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::ShaderModuleDescriptor,\n        source: pipeline::ShaderModuleSource,\n        id_in: Option<id::ShaderModuleId>,\n    ) -> (\n        id::ShaderModuleId,\n        Option<pipeline::CreateShaderModuleError>,\n    ) {\n        profiling::scope!(\"Device::create_shader_module\");\n\n        let hub = &self.hub;\n        let fid = hub.shader_modules.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            #[cfg(feature = \"trace\")]\n            let data = device.trace.lock().as_mut().map(|trace| {\n                use crate::device::trace::DataKind;\n\n                match source {\n                    #[cfg(feature = \"wgsl\")]\n                    pipeline::ShaderModuleSource::Wgsl(ref code) => {\n                        trace.make_binary(DataKind::Wgsl, code.as_bytes())\n                    }\n                    #[cfg(feature = \"glsl\")]\n                    pipeline::ShaderModuleSource::Glsl(ref code, _) => {\n                        trace.make_binary(DataKind::Glsl, code.as_bytes())\n                    }\n                    #[cfg(feature = \"spirv\")]\n                    pipeline::ShaderModuleSource::SpirV(ref code, _) => {\n                        trace.make_binary(DataKind::Spv, bytemuck::cast_slice::<u32, u8>(code))\n                    }\n                    pipeline::ShaderModuleSource::Naga(ref module) => {\n                        let string =\n                            ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default())\n                                .unwrap();\n                        trace.make_binary(DataKind::Ron, string.as_bytes())\n                    }\n                    pipeline::ShaderModuleSource::Dummy(_) => {\n                        panic!(\"found `ShaderModuleSource::Dummy`\")\n                    }\n                }\n            });\n\n            let shader = match device.create_shader_module(desc, source) {\n                Ok(shader) => shader,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(data) = data {\n                // We don't need these two operations with the trace to be atomic.\n                device\n                    .trace\n                    .lock()\n                    .as_mut()\n                    .expect(\"trace went away during create_shader_module?\")\n                    .add(trace::Action::CreateShaderModule {\n                        id: shader.to_trace(),\n                        desc: desc.clone(),\n                        data,\n                    });\n            };\n\n            let id = fid.assign(Fallible::Valid(shader));\n            api_log!(\"Device::create_shader_module -> {id:?}\");\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    /// # Safety\n    ///\n    /// This function passes source code or binary to the backend as-is and can potentially result in a\n    /// driver crash.\n    pub unsafe fn device_create_shader_module_passthrough(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::ShaderModuleDescriptorPassthrough<'_>,\n        id_in: Option<id::ShaderModuleId>,\n    ) -> (\n        id::ShaderModuleId,\n        Option<pipeline::CreateShaderModuleError>,\n    ) {\n        profiling::scope!(\"Device::create_shader_module_passthrough\");\n\n        let hub = &self.hub;\n        let fid = hub.shader_modules.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let result = unsafe { device.create_shader_module_passthrough(desc) };\n\n            let shader = match result {\n                Ok(shader) => shader,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                use crate::device::trace::DataKind;\n\n                let mut file_names = Vec::new();\n                for (data, kind) in [\n                    (\n                        desc.spirv.as_ref().map(|a| bytemuck::cast_slice(a)),\n                        DataKind::Spv,\n                    ),\n                    (desc.dxil.as_deref(), DataKind::Dxil),\n                    (desc.hlsl.as_ref().map(|a| a.as_bytes()), DataKind::Hlsl),\n                    (desc.metallib.as_deref(), DataKind::MetalLib),\n                    (desc.msl.as_ref().map(|a| a.as_bytes()), DataKind::Msl),\n                    (desc.glsl.as_ref().map(|a| a.as_bytes()), DataKind::Glsl),\n                    (desc.wgsl.as_ref().map(|a| a.as_bytes()), DataKind::Wgsl),\n                ] {\n                    if let Some(data) = data {\n                        file_names.push(trace.make_binary(kind, data));\n                    }\n                }\n                trace.add(trace::Action::CreateShaderModulePassthrough {\n                    id: shader.to_trace(),\n                    data: file_names,\n                    label: desc.label.clone(),\n                    num_workgroups: desc.num_workgroups,\n                });\n            };\n\n            let id = fid.assign(Fallible::Valid(shader));\n            api_log!(\"Device::create_shader_module_spirv -> {id:?}\");\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn shader_module_drop(&self, shader_module_id: id::ShaderModuleId) {\n        profiling::scope!(\"ShaderModule::drop\");\n        api_log!(\"ShaderModule::drop {shader_module_id:?}\");\n\n        let hub = &self.hub;\n\n        let _shader_module = hub.shader_modules.remove(shader_module_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(shader_module) = _shader_module.get() {\n            if let Some(t) = shader_module.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyShaderModule(shader_module.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_command_encoder(\n        &self,\n        device_id: DeviceId,\n        desc: &wgt::CommandEncoderDescriptor<Label>,\n        id_in: Option<id::CommandEncoderId>,\n    ) -> (id::CommandEncoderId, Option<DeviceError>) {\n        profiling::scope!(\"Device::create_command_encoder\");\n\n        let hub = &self.hub;\n        let fid = hub.command_encoders.prepare(id_in);\n\n        let device = self.hub.devices.get(device_id);\n\n        let error = 'error: {\n            let cmd_enc = match device.create_command_encoder(&desc.label) {\n                Ok(cmd_enc) => cmd_enc,\n                Err(e) => break 'error e,\n            };\n\n            let id = fid.assign(cmd_enc);\n            api_log!(\"Device::create_command_encoder -> {id:?}\");\n            return (id, None);\n        };\n\n        let id = fid.assign(Arc::new(CommandEncoder::new_invalid(\n            &device,\n            &desc.label,\n            error.clone().into(),\n        )));\n        (id, Some(error))\n    }\n\n    pub fn command_encoder_drop(&self, command_encoder_id: id::CommandEncoderId) {\n        profiling::scope!(\"CommandEncoder::drop\");\n        api_log!(\"CommandEncoder::drop {command_encoder_id:?}\");\n        let _cmd_enc = self.hub.command_encoders.remove(command_encoder_id);\n    }\n\n    pub fn command_buffer_drop(&self, command_buffer_id: id::CommandBufferId) {\n        profiling::scope!(\"CommandBuffer::drop\");\n        api_log!(\"CommandBuffer::drop {command_buffer_id:?}\");\n        let _cmd_buf = self.hub.command_buffers.remove(command_buffer_id);\n    }\n\n    pub fn device_create_render_bundle_encoder(\n        &self,\n        device_id: DeviceId,\n        desc: &command::RenderBundleEncoderDescriptor,\n    ) -> (\n        *mut command::RenderBundleEncoder,\n        Option<command::CreateRenderBundleError>,\n    ) {\n        profiling::scope!(\"Device::create_render_bundle_encoder\");\n        api_log!(\"Device::device_create_render_bundle_encoder\");\n        let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id) {\n            Ok(encoder) => (encoder, None),\n            Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)),\n        };\n        (Box::into_raw(Box::new(encoder)), error)\n    }\n\n    pub fn render_bundle_encoder_finish(\n        &self,\n        bundle_encoder: command::RenderBundleEncoder,\n        desc: &command::RenderBundleDescriptor,\n        id_in: Option<id::RenderBundleId>,\n    ) -> (id::RenderBundleId, Option<command::RenderBundleError>) {\n        profiling::scope!(\"RenderBundleEncoder::finish\");\n\n        let hub = &self.hub;\n\n        let fid = hub.render_bundles.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(bundle_encoder.parent());\n\n            #[cfg(feature = \"trace\")]\n            let trace_desc = trace::new_render_bundle_encoder_descriptor(\n                desc.label.clone(),\n                &bundle_encoder.context,\n                bundle_encoder.is_depth_read_only,\n                bundle_encoder.is_stencil_read_only,\n            );\n\n            let render_bundle = match bundle_encoder.finish(desc, &device, hub) {\n                Ok(bundle) => bundle,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateRenderBundle {\n                    id: render_bundle.to_trace(),\n                    desc: trace_desc,\n                    base: render_bundle.to_base_pass().to_trace(),\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(render_bundle));\n            api_log!(\"RenderBundleEncoder::finish -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn render_bundle_drop(&self, render_bundle_id: id::RenderBundleId) {\n        profiling::scope!(\"RenderBundle::drop\");\n        api_log!(\"RenderBundle::drop {render_bundle_id:?}\");\n\n        let hub = &self.hub;\n\n        let _bundle = hub.render_bundles.remove(render_bundle_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(bundle) = _bundle.get() {\n            if let Some(t) = bundle.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyRenderBundle(bundle.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_query_set(\n        &self,\n        device_id: DeviceId,\n        desc: &resource::QuerySetDescriptor,\n        id_in: Option<id::QuerySetId>,\n    ) -> (id::QuerySetId, Option<resource::CreateQuerySetError>) {\n        profiling::scope!(\"Device::create_query_set\");\n\n        let hub = &self.hub;\n        let fid = hub.query_sets.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let query_set = match device.create_query_set(desc) {\n                Ok(query_set) => query_set,\n                Err(err) => break 'error err,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateQuerySet {\n                    id: query_set.to_trace(),\n                    desc: desc.clone(),\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(query_set));\n            api_log!(\"Device::create_query_set -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn query_set_drop(&self, query_set_id: id::QuerySetId) {\n        profiling::scope!(\"QuerySet::drop\");\n        api_log!(\"QuerySet::drop {query_set_id:?}\");\n\n        let hub = &self.hub;\n\n        let _query_set = hub.query_sets.remove(query_set_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(query_set) = _query_set.get() {\n            if let Some(trace) = query_set.device.trace.lock().as_mut() {\n                trace.add(trace::Action::DestroyQuerySet(query_set.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_render_pipeline(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::RenderPipelineDescriptor,\n        id_in: Option<id::RenderPipelineId>,\n    ) -> (\n        id::RenderPipelineId,\n        Option<pipeline::CreateRenderPipelineError>,\n    ) {\n        profiling::scope!(\"Device::create_render_pipeline\");\n\n        let hub = &self.hub;\n\n        let fid = hub.render_pipelines.prepare(id_in);\n\n        let device = self.hub.devices.get(device_id);\n\n        self.device_create_general_render_pipeline(desc.clone().into(), device, fid)\n    }\n\n    pub fn device_create_mesh_pipeline(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::MeshPipelineDescriptor,\n        id_in: Option<id::RenderPipelineId>,\n    ) -> (\n        id::RenderPipelineId,\n        Option<pipeline::CreateRenderPipelineError>,\n    ) {\n        let hub = &self.hub;\n\n        let fid = hub.render_pipelines.prepare(id_in);\n\n        let device = self.hub.devices.get(device_id);\n        self.device_create_general_render_pipeline(desc.clone().into(), device, fid)\n    }\n\n    fn device_create_general_render_pipeline(\n        &self,\n        desc: pipeline::GeneralRenderPipelineDescriptor,\n        device: Arc<crate::device::resource::Device>,\n        fid: crate::registry::FutureId<Fallible<pipeline::RenderPipeline>>,\n    ) -> (\n        id::RenderPipelineId,\n        Option<pipeline::CreateRenderPipelineError>,\n    ) {\n        profiling::scope!(\"Device::create_general_render_pipeline\");\n\n        let hub = &self.hub;\n\n        let error = 'error: {\n            if let Err(e) = device.check_is_valid() {\n                break 'error e.into();\n            }\n\n            let layout = desc\n                .layout\n                .map(|layout| hub.pipeline_layouts.get(layout).get())\n                .transpose();\n            let layout = match layout {\n                Ok(layout) => layout,\n                Err(e) => break 'error e.into(),\n            };\n\n            let cache = desc\n                .cache\n                .map(|cache| hub.pipeline_caches.get(cache).get())\n                .transpose();\n            let cache = match cache {\n                Ok(cache) => cache,\n                Err(e) => break 'error e.into(),\n            };\n            let mut passthrough_stages = wgt::ShaderStages::empty();\n\n            let vertex = match desc.vertex {\n                RenderPipelineVertexProcessor::Vertex(ref vertex) => {\n                    let module = hub\n                        .shader_modules\n                        .get(vertex.stage.module)\n                        .get()\n                        .map_err(|e| pipeline::CreateRenderPipelineError::Stage {\n                            stage: wgt::ShaderStages::VERTEX,\n                            error: e.into(),\n                        });\n                    let module = match module {\n                        Ok(module) => module,\n                        Err(e) => break 'error e,\n                    };\n                    if module.interface.is_none() {\n                        passthrough_stages |= wgt::ShaderStages::VERTEX;\n                    }\n                    let stage = ResolvedProgrammableStageDescriptor {\n                        module,\n                        entry_point: vertex.stage.entry_point.clone(),\n                        constants: vertex.stage.constants.clone(),\n                        zero_initialize_workgroup_memory: vertex\n                            .stage\n                            .zero_initialize_workgroup_memory,\n                    };\n                    RenderPipelineVertexProcessor::Vertex(ResolvedVertexState {\n                        stage,\n                        buffers: vertex.buffers.clone(),\n                    })\n                }\n                RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {\n                    let task_module = if let Some(task) = task {\n                        let module = hub\n                            .shader_modules\n                            .get(task.stage.module)\n                            .get()\n                            .map_err(|e| pipeline::CreateRenderPipelineError::Stage {\n                                stage: wgt::ShaderStages::VERTEX,\n                                error: e.into(),\n                            });\n                        let module = match module {\n                            Ok(module) => module,\n                            Err(e) => break 'error e,\n                        };\n                        if module.interface.is_none() {\n                            passthrough_stages |= wgt::ShaderStages::TASK;\n                        }\n                        let state = ResolvedProgrammableStageDescriptor {\n                            module,\n                            entry_point: task.stage.entry_point.clone(),\n                            constants: task.stage.constants.clone(),\n                            zero_initialize_workgroup_memory: task\n                                .stage\n                                .zero_initialize_workgroup_memory,\n                        };\n                        Some(ResolvedTaskState { stage: state })\n                    } else {\n                        None\n                    };\n                    let mesh_module =\n                        hub.shader_modules\n                            .get(mesh.stage.module)\n                            .get()\n                            .map_err(|e| pipeline::CreateRenderPipelineError::Stage {\n                                stage: wgt::ShaderStages::MESH,\n                                error: e.into(),\n                            });\n                    let mesh_module = match mesh_module {\n                        Ok(module) => module,\n                        Err(e) => break 'error e,\n                    };\n                    if mesh_module.interface.is_none() {\n                        passthrough_stages |= wgt::ShaderStages::VERTEX;\n                    }\n                    let mesh_stage = ResolvedProgrammableStageDescriptor {\n                        module: mesh_module,\n                        entry_point: mesh.stage.entry_point.clone(),\n                        constants: mesh.stage.constants.clone(),\n                        zero_initialize_workgroup_memory: mesh\n                            .stage\n                            .zero_initialize_workgroup_memory,\n                    };\n                    RenderPipelineVertexProcessor::Mesh(\n                        task_module,\n                        ResolvedMeshState { stage: mesh_stage },\n                    )\n                }\n            };\n\n            let fragment = if let Some(ref state) = desc.fragment {\n                let module = hub\n                    .shader_modules\n                    .get(state.stage.module)\n                    .get()\n                    .map_err(|e| pipeline::CreateRenderPipelineError::Stage {\n                        stage: wgt::ShaderStages::FRAGMENT,\n                        error: e.into(),\n                    });\n                let module = match module {\n                    Ok(module) => module,\n                    Err(e) => break 'error e,\n                };\n                if module.interface.is_none() {\n                    passthrough_stages |= wgt::ShaderStages::FRAGMENT;\n                }\n                let stage = ResolvedProgrammableStageDescriptor {\n                    module,\n                    entry_point: state.stage.entry_point.clone(),\n                    constants: state.stage.constants.clone(),\n                    zero_initialize_workgroup_memory: state.stage.zero_initialize_workgroup_memory,\n                };\n                Some(ResolvedFragmentState {\n                    stage,\n                    targets: state.targets.clone(),\n                })\n            } else {\n                None\n            };\n\n            if !passthrough_stages.is_empty() && layout.is_none() {\n                break 'error pipeline::CreateRenderPipelineError::Implicit(\n                    pipeline::ImplicitLayoutError::Passthrough(passthrough_stages),\n                );\n            }\n\n            let desc = ResolvedGeneralRenderPipelineDescriptor {\n                label: desc.label.clone(),\n                layout,\n                vertex,\n                primitive: desc.primitive,\n                depth_stencil: desc.depth_stencil.clone(),\n                multisample: desc.multisample,\n                fragment,\n                multiview_mask: desc.multiview_mask,\n                cache,\n            };\n\n            #[cfg(feature = \"trace\")]\n            let trace_desc = desc.clone().into_trace();\n\n            let res = device.create_render_pipeline(desc);\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateGeneralRenderPipeline {\n                    id: res.as_ref().ok().map(IntoTrace::to_trace),\n                    desc: trace_desc,\n                });\n            }\n\n            let pipeline = match res {\n                Ok(pair) => pair,\n                Err(e) => break 'error e,\n            };\n\n            let id = fid.assign(Fallible::Valid(pipeline));\n            api_log!(\"Device::create_render_pipeline -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n\n        (id, Some(error))\n    }\n\n    /// Get an ID of one of the bind group layouts. The ID adds a refcount,\n    /// which needs to be released by calling `bind_group_layout_drop`.\n    pub fn render_pipeline_get_bind_group_layout(\n        &self,\n        pipeline_id: id::RenderPipelineId,\n        index: u32,\n        id_in: Option<id::BindGroupLayoutId>,\n    ) -> (\n        id::BindGroupLayoutId,\n        Option<binding_model::GetBindGroupLayoutError>,\n    ) {\n        let hub = &self.hub;\n\n        let fid = hub.bind_group_layouts.prepare(id_in);\n\n        let error = 'error: {\n            let pipeline = match hub.render_pipelines.get(pipeline_id).get() {\n                Ok(pipeline) => pipeline,\n                Err(e) => break 'error e.into(),\n            };\n            match pipeline.get_bind_group_layout(index) {\n                Ok(bgl) => {\n                    #[cfg(feature = \"trace\")]\n                    if let Some(ref mut trace) = *pipeline.device.trace.lock() {\n                        trace.add(trace::Action::GetRenderPipelineBindGroupLayout {\n                            id: bgl.to_trace(),\n                            pipeline: pipeline.to_trace(),\n                            index,\n                        });\n                    }\n\n                    let id = fid.assign(Fallible::Valid(bgl.clone()));\n                    return (id, None);\n                }\n                Err(err) => break 'error err,\n            };\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));\n        (id, Some(error))\n    }\n\n    pub fn render_pipeline_drop(&self, render_pipeline_id: id::RenderPipelineId) {\n        profiling::scope!(\"RenderPipeline::drop\");\n        api_log!(\"RenderPipeline::drop {render_pipeline_id:?}\");\n\n        let hub = &self.hub;\n\n        let _pipeline = hub.render_pipelines.remove(render_pipeline_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(pipeline) = _pipeline.get() {\n            if let Some(t) = pipeline.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyRenderPipeline(pipeline.to_trace()));\n            }\n        }\n    }\n\n    pub fn device_create_compute_pipeline(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::ComputePipelineDescriptor,\n        id_in: Option<id::ComputePipelineId>,\n    ) -> (\n        id::ComputePipelineId,\n        Option<pipeline::CreateComputePipelineError>,\n    ) {\n        profiling::scope!(\"Device::create_compute_pipeline\");\n\n        let hub = &self.hub;\n\n        let fid = hub.compute_pipelines.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            if let Err(e) = device.check_is_valid() {\n                break 'error e.into();\n            }\n\n            let layout = desc\n                .layout\n                .map(|layout| hub.pipeline_layouts.get(layout).get())\n                .transpose();\n            let layout = match layout {\n                Ok(layout) => layout,\n                Err(e) => break 'error e.into(),\n            };\n\n            let cache = desc\n                .cache\n                .map(|cache| hub.pipeline_caches.get(cache).get())\n                .transpose();\n            let cache = match cache {\n                Ok(cache) => cache,\n                Err(e) => break 'error e.into(),\n            };\n\n            let module = hub.shader_modules.get(desc.stage.module).get();\n            let module = match module {\n                Ok(module) => module,\n                Err(e) => break 'error e.into(),\n            };\n            if module.interface.is_none() && layout.is_none() {\n                break 'error pipeline::CreateComputePipelineError::Implicit(\n                    pipeline::ImplicitLayoutError::Passthrough(wgt::ShaderStages::COMPUTE),\n                );\n            }\n            let stage = ResolvedProgrammableStageDescriptor {\n                module,\n                entry_point: desc.stage.entry_point.clone(),\n                constants: desc.stage.constants.clone(),\n                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,\n            };\n\n            let desc = ResolvedComputePipelineDescriptor {\n                label: desc.label.clone(),\n                layout,\n                stage,\n                cache,\n            };\n\n            #[cfg(feature = \"trace\")]\n            let trace_desc = desc.clone().into_trace();\n\n            let res = device.create_compute_pipeline(desc);\n\n            #[cfg(feature = \"trace\")]\n            if let Some(ref mut trace) = *device.trace.lock() {\n                trace.add(trace::Action::CreateComputePipeline {\n                    id: res.as_ref().ok().map(IntoTrace::to_trace),\n                    desc: trace_desc,\n                });\n            }\n\n            let pipeline = match res {\n                Ok(pair) => pair,\n                Err(e) => break 'error e,\n            };\n\n            let id = fid.assign(Fallible::Valid(pipeline));\n            api_log!(\"Device::create_compute_pipeline -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n\n        (id, Some(error))\n    }\n\n    /// Get an ID of one of the bind group layouts. The ID adds a refcount,\n    /// which needs to be released by calling `bind_group_layout_drop`.\n    pub fn compute_pipeline_get_bind_group_layout(\n        &self,\n        pipeline_id: id::ComputePipelineId,\n        index: u32,\n        id_in: Option<id::BindGroupLayoutId>,\n    ) -> (\n        id::BindGroupLayoutId,\n        Option<binding_model::GetBindGroupLayoutError>,\n    ) {\n        let hub = &self.hub;\n\n        let fid = hub.bind_group_layouts.prepare(id_in);\n\n        let error = 'error: {\n            let pipeline = match hub.compute_pipelines.get(pipeline_id).get() {\n                Ok(pipeline) => pipeline,\n                Err(e) => break 'error e.into(),\n            };\n\n            match pipeline.get_bind_group_layout(index) {\n                Ok(bgl) => {\n                    #[cfg(feature = \"trace\")]\n                    if let Some(ref mut trace) = *pipeline.device.trace.lock() {\n                        trace.add(trace::Action::GetComputePipelineBindGroupLayout {\n                            id: bgl.to_trace(),\n                            pipeline: pipeline.to_trace(),\n                            index,\n                        });\n                    }\n\n                    let id = fid.assign(Fallible::Valid(bgl.clone()));\n                    return (id, None);\n                }\n                Err(err) => break 'error err,\n            };\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));\n        (id, Some(error))\n    }\n\n    pub fn compute_pipeline_drop(&self, compute_pipeline_id: id::ComputePipelineId) {\n        profiling::scope!(\"ComputePipeline::drop\");\n        api_log!(\"ComputePipeline::drop {compute_pipeline_id:?}\");\n\n        let hub = &self.hub;\n\n        let _pipeline = hub.compute_pipelines.remove(compute_pipeline_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(pipeline) = _pipeline.get() {\n            if let Some(t) = pipeline.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyComputePipeline(pipeline.to_trace()));\n            }\n        }\n    }\n\n    /// # Safety\n    /// The `data` argument of `desc` must have been returned by\n    /// [Self::pipeline_cache_get_data] for the same adapter\n    pub unsafe fn device_create_pipeline_cache(\n        &self,\n        device_id: DeviceId,\n        desc: &pipeline::PipelineCacheDescriptor<'_>,\n        id_in: Option<id::PipelineCacheId>,\n    ) -> (\n        id::PipelineCacheId,\n        Option<pipeline::CreatePipelineCacheError>,\n    ) {\n        profiling::scope!(\"Device::create_pipeline_cache\");\n\n        let hub = &self.hub;\n\n        let fid = hub.pipeline_caches.prepare(id_in);\n        let error: pipeline::CreatePipelineCacheError = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let cache = unsafe { device.create_pipeline_cache(desc) };\n            match cache {\n                Ok(cache) => {\n                    #[cfg(feature = \"trace\")]\n                    if let Some(ref mut trace) = *device.trace.lock() {\n                        trace.add(trace::Action::CreatePipelineCache {\n                            id: cache.to_trace(),\n                            desc: desc.clone(),\n                        });\n                    }\n\n                    let id = fid.assign(Fallible::Valid(cache));\n                    api_log!(\"Device::create_pipeline_cache -> {id:?}\");\n                    return (id, None);\n                }\n                Err(e) => break 'error e,\n            }\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));\n\n        (id, Some(error))\n    }\n\n    pub fn pipeline_cache_drop(&self, pipeline_cache_id: id::PipelineCacheId) {\n        profiling::scope!(\"PipelineCache::drop\");\n        api_log!(\"PipelineCache::drop {pipeline_cache_id:?}\");\n\n        let hub = &self.hub;\n\n        let _cache = hub.pipeline_caches.remove(pipeline_cache_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(cache) = _cache.get() {\n            if let Some(t) = cache.device.trace.lock().as_mut() {\n                t.add(trace::Action::DestroyPipelineCache(cache.to_trace()));\n            }\n        }\n    }\n\n    pub fn surface_configure(\n        &self,\n        surface_id: SurfaceId,\n        device_id: DeviceId,\n        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,\n    ) -> Option<present::ConfigureSurfaceError> {\n        let device = self.hub.devices.get(device_id);\n        let surface = self.surfaces.get(surface_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Some(ref mut trace) = *device.trace.lock() {\n            trace.add(trace::Action::ConfigureSurface(\n                surface.to_trace(),\n                config.clone(),\n            ));\n        }\n\n        device.configure_surface(&surface, config)\n    }\n\n    /// Check `device_id` for freeable resources and completed buffer mappings.\n    ///\n    /// Return `queue_empty` indicating whether there are more queue submissions still in flight.\n    pub fn device_poll(\n        &self,\n        device_id: DeviceId,\n        poll_type: wgt::PollType<crate::SubmissionIndex>,\n    ) -> Result<wgt::PollStatus, WaitIdleError> {\n        api_log!(\"Device::poll {poll_type:?}\");\n\n        let device = self.hub.devices.get(device_id);\n\n        let (closures, result) = device.poll_and_return_closures(poll_type);\n\n        closures.fire();\n\n        result\n    }\n\n    /// Poll all devices belonging to the specified backend.\n    ///\n    /// If `force_wait` is true, block until all buffer mappings are done.\n    ///\n    /// Return `all_queue_empty` indicating whether there are more queue\n    /// submissions still in flight.\n    fn poll_all_devices_of_api(\n        &self,\n        force_wait: bool,\n        closure_list: &mut UserClosures,\n    ) -> Result<bool, WaitIdleError> {\n        profiling::scope!(\"poll_device\");\n\n        let hub = &self.hub;\n        let mut all_queue_empty = true;\n        {\n            let device_guard = hub.devices.read();\n\n            for (_id, device) in device_guard.iter() {\n                let poll_type = if force_wait {\n                    // TODO(#8286): Should expose timeout to poll_all.\n                    wgt::PollType::wait_indefinitely()\n                } else {\n                    wgt::PollType::Poll\n                };\n\n                let (closures, result) = device.poll_and_return_closures(poll_type);\n\n                let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));\n\n                all_queue_empty &= is_queue_empty;\n\n                closure_list.extend(closures);\n            }\n        }\n\n        Ok(all_queue_empty)\n    }\n\n    /// Poll all devices on all backends.\n    ///\n    /// This is the implementation of `wgpu::Instance::poll_all`.\n    ///\n    /// Return `all_queue_empty` indicating whether there are more queue\n    /// submissions still in flight.\n    pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {\n        api_log!(\"poll_all_devices\");\n        let mut closures = UserClosures::default();\n        let all_queue_empty = self.poll_all_devices_of_api(force_wait, &mut closures)?;\n\n        closures.fire();\n\n        Ok(all_queue_empty)\n    }\n\n    /// # Safety\n    ///\n    /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety.\n    ///\n    /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture\n    pub unsafe fn device_start_graphics_debugger_capture(&self, device_id: DeviceId) {\n        unsafe {\n            self.hub\n                .devices\n                .get(device_id)\n                .start_graphics_debugger_capture();\n        }\n    }\n\n    /// # Safety\n    ///\n    /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety.\n    ///\n    /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture\n    pub unsafe fn device_stop_graphics_debugger_capture(&self, device_id: DeviceId) {\n        unsafe {\n            self.hub\n                .devices\n                .get(device_id)\n                .stop_graphics_debugger_capture();\n        }\n    }\n\n    pub fn pipeline_cache_get_data(&self, id: id::PipelineCacheId) -> Option<Vec<u8>> {\n        use crate::pipeline_cache;\n        api_log!(\"PipelineCache::get_data\");\n        let hub = &self.hub;\n\n        if let Ok(cache) = hub.pipeline_caches.get(id).get() {\n            // TODO: Is this check needed?\n            if !cache.device.is_valid() {\n                return None;\n            }\n            let mut vec = unsafe { cache.device.raw().pipeline_cache_get_data(cache.raw()) }?;\n            let validation_key = cache.device.raw().pipeline_cache_validation_key()?;\n\n            let mut header_contents = [0; pipeline_cache::HEADER_LENGTH];\n            pipeline_cache::add_cache_header(\n                &mut header_contents,\n                &vec,\n                &cache.device.adapter.raw.info,\n                validation_key,\n            );\n\n            let deleted = vec.splice(..0, header_contents).collect::<Vec<_>>();\n            debug_assert!(deleted.is_empty());\n\n            return Some(vec);\n        }\n        None\n    }\n\n    pub fn device_drop(&self, device_id: DeviceId) {\n        profiling::scope!(\"Device::drop\");\n        api_log!(\"Device::drop {device_id:?}\");\n\n        self.hub.devices.remove(device_id);\n    }\n\n    /// `device_lost_closure` might never be called.\n    pub fn device_set_device_lost_closure(\n        &self,\n        device_id: DeviceId,\n        device_lost_closure: DeviceLostClosure,\n    ) {\n        let device = self.hub.devices.get(device_id);\n\n        device\n            .device_lost_closure\n            .lock()\n            .replace(device_lost_closure);\n    }\n\n    pub fn device_destroy(&self, device_id: DeviceId) {\n        api_log!(\"Device::destroy {device_id:?}\");\n\n        let device = self.hub.devices.get(device_id);\n\n        // Follow the steps at\n        // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.\n        // It's legal to call destroy multiple times, but if the device\n        // is already invalid, there's nothing more to do. There's also\n        // no need to return an error.\n        if !device.is_valid() {\n            return;\n        }\n\n        // The last part of destroy is to lose the device. The spec says\n        // delay that until all \"currently-enqueued operations on any\n        // queue on this device are completed.\" This is accomplished by\n        // setting valid to false, and then relying upon maintain to\n        // check for empty queues and a DeviceLostClosure. At that time,\n        // the DeviceLostClosure will be called with \"destroyed\" as the\n        // reason.\n        device.valid.store(false, Ordering::Release);\n    }\n\n    pub fn device_get_internal_counters(&self, device_id: DeviceId) -> wgt::InternalCounters {\n        let device = self.hub.devices.get(device_id);\n        wgt::InternalCounters {\n            hal: device.get_hal_counters(),\n            core: wgt::CoreCounters {},\n        }\n    }\n\n    pub fn device_generate_allocator_report(\n        &self,\n        device_id: DeviceId,\n    ) -> Option<wgt::AllocatorReport> {\n        let device = self.hub.devices.get(device_id);\n        device.generate_allocator_report()\n    }\n\n    #[cfg(feature = \"trace\")]\n    pub fn device_take_trace(\n        &self,\n        device_id: DeviceId,\n    ) -> Option<Box<dyn trace::Trace + Send + Sync + 'static>> {\n        let device = self.hub.devices.get(device_id);\n        device.take_trace()\n    }\n\n    pub fn queue_drop(&self, queue_id: QueueId) {\n        profiling::scope!(\"Queue::drop\");\n        api_log!(\"Queue::drop {queue_id:?}\");\n\n        self.hub.queues.remove(queue_id);\n    }\n\n    /// `op.callback` is guaranteed to be called.\n    pub fn buffer_map_async(\n        &self,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        size: Option<BufferAddress>,\n        op: BufferMapOperation,\n    ) -> Result<crate::SubmissionIndex, BufferAccessError> {\n        profiling::scope!(\"Buffer::map_async\");\n        api_log!(\"Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}\");\n\n        let hub = &self.hub;\n\n        let map_result = match hub.buffers.get(buffer_id).get() {\n            Ok(buffer) => buffer.map_async(offset, size, op),\n            Err(e) => Err((op, e.into())),\n        };\n\n        match map_result {\n            Ok(submission_index) => Ok(submission_index),\n            Err((mut operation, err)) => {\n                if let Some(callback) = operation.callback.take() {\n                    callback(Err(err.clone()));\n                }\n                Err(err)\n            }\n        }\n    }\n\n    pub fn buffer_get_mapped_range(\n        &self,\n        buffer_id: id::BufferId,\n        offset: BufferAddress,\n        size: Option<BufferAddress>,\n    ) -> Result<(NonNull<u8>, u64), BufferAccessError> {\n        profiling::scope!(\"Buffer::get_mapped_range\");\n        api_log!(\"Buffer::get_mapped_range {buffer_id:?} offset {offset:?} size {size:?}\");\n\n        let hub = &self.hub;\n\n        let buffer = hub.buffers.get(buffer_id).get()?;\n\n        buffer.get_mapped_range(offset, size)\n    }\n\n    pub fn buffer_unmap(&self, buffer_id: id::BufferId) -> BufferAccessResult {\n        profiling::scope!(\"unmap\", \"Buffer\");\n        api_log!(\"Buffer::unmap {buffer_id:?}\");\n\n        let hub = &self.hub;\n\n        let buffer = hub.buffers.get(buffer_id).get()?;\n\n        let snatch_guard = buffer.device.snatchable_lock.read();\n        buffer.check_destroyed(&snatch_guard)?;\n        drop(snatch_guard);\n\n        buffer.device.check_is_valid()?;\n        buffer.unmap()\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/life.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\n\nuse smallvec::SmallVec;\nuse thiserror::Error;\n\nuse crate::{\n    device::{\n        queue::{EncoderInFlight, SubmittedWorkDoneClosure, TempResource},\n        DeviceError,\n    },\n    ray_tracing::BlasCompactReadyPendingClosure,\n    resource::{Blas, Buffer, Texture, Trackable},\n    snatch::SnatchGuard,\n    SubmissionIndex,\n};\n\n/// A command submitted to the GPU for execution.\n///\n/// ## Keeping resources alive while the GPU is using them\n///\n/// [`wgpu_hal`] requires that, when a command is submitted to a queue, all the\n/// resources it uses must remain alive until it has finished executing.\n///\n/// [`wgpu_hal`]: hal\nstruct ActiveSubmission {\n    /// The index of the submission we track.\n    ///\n    /// When `Device::fence`'s value is greater than or equal to this, our queue\n    /// submission has completed.\n    index: SubmissionIndex,\n\n    /// Buffers to be mapped once this submission has completed.\n    mapped: Vec<Arc<Buffer>>,\n\n    /// BLASes to have their compacted size read back once this submission has completed.\n    compact_read_back: Vec<Arc<Blas>>,\n\n    /// Command buffers used by this submission, and the encoder that owns them.\n    ///\n    /// [`wgpu_hal::Queue::submit`] requires the submitted command buffers to\n    /// remain alive until the submission has completed execution. Command\n    /// encoders double as allocation pools for command buffers, so holding them\n    /// here and cleaning them up in [`LifetimeTracker::triage_submissions`]\n    /// satisfies that requirement.\n    ///\n    /// Once this submission has completed, the command buffers are reset and\n    /// the command encoder is recycled.\n    ///\n    /// [`wgpu_hal::Queue::submit`]: hal::Queue::submit\n    encoders: Vec<EncoderInFlight>,\n\n    /// List of queue \"on_submitted_work_done\" closures to be called once this\n    /// submission has completed.\n    work_done_closures: SmallVec<[SubmittedWorkDoneClosure; 1]>,\n}\n\nimpl ActiveSubmission {\n    /// Returns true if this submission contains the given buffer.\n    ///\n    /// This only uses constant-time operations.\n    pub fn contains_buffer(&self, buffer: &Buffer) -> bool {\n        for encoder in &self.encoders {\n            // The ownership location of buffers depends on where the command encoder\n            // came from. If it is the staging command encoder on the queue, it is\n            // in the pending buffer list. If it came from a user command encoder,\n            // it is in the tracker.\n\n            if encoder.trackers.buffers.contains(buffer) {\n                return true;\n            }\n\n            if encoder\n                .pending_buffers\n                .contains_key(&buffer.tracker_index())\n            {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    /// Returns true if this submission contains the given texture.\n    ///\n    /// This only uses constant-time operations.\n    pub fn contains_texture(&self, texture: &Texture) -> bool {\n        for encoder in &self.encoders {\n            // The ownership location of textures depends on where the command encoder\n            // came from. If it is the staging command encoder on the queue, it is\n            // in the pending buffer list. If it came from a user command encoder,\n            // it is in the tracker.\n\n            if encoder.trackers.textures.contains(texture) {\n                return true;\n            }\n\n            if encoder\n                .pending_textures\n                .contains_key(&texture.tracker_index())\n            {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    /// Returns true if this submission contains the given blas.\n    ///\n    /// This only uses constant-time operations.\n    pub fn contains_blas(&self, blas: &Blas) -> bool {\n        for encoder in &self.encoders {\n            if encoder.trackers.blas_s.contains(blas) {\n                return true;\n            }\n\n            if encoder.pending_blas_s.contains_key(&blas.tracker_index()) {\n                return true;\n            }\n        }\n\n        false\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum WaitIdleError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Tried to wait using a submission index ({0}) that has not been returned by a successful submission (last successful submission: {1})\")]\n    WrongSubmissionIndex(SubmissionIndex, SubmissionIndex),\n    #[error(\"Timed out trying to wait for the given submission index.\")]\n    Timeout,\n}\n\nimpl WaitIdleError {\n    pub fn to_poll_error(&self) -> Option<wgt::PollError> {\n        match self {\n            WaitIdleError::Timeout => Some(wgt::PollError::Timeout),\n            &WaitIdleError::WrongSubmissionIndex(a, b) => {\n                Some(wgt::PollError::WrongSubmissionIndex(a, b))\n            }\n            _ => None,\n        }\n    }\n}\n\n/// Resource tracking for a device.\n///\n/// ## Host mapping buffers\n///\n/// A buffer cannot be mapped until all active queue submissions that use it\n/// have completed. To that end:\n///\n/// -   Each buffer's `ResourceInfo::submission_index` records the index of the\n///     most recent queue submission that uses that buffer.\n///\n/// -   When the device is polled, the following `LifetimeTracker` methods decide\n///     what should happen next:\n///\n///     1)  `triage_submissions` moves entries in `self.active[i]` for completed\n///         submissions to `self.ready_to_map`.  At this point, both\n///         `self.active` and `self.ready_to_map` are up to date with the given\n///         submission index.\n///\n///     2)  `handle_mapping` drains `self.ready_to_map` and actually maps the\n///         buffers, collecting a list of notification closures to call.\n///\n/// Only calling `Global::buffer_map_async` clones a new `Arc` for the\n/// buffer. This new `Arc` is only dropped by `handle_mapping`.\npub(crate) struct LifetimeTracker {\n    /// Resources used by queue submissions still in flight. One entry per\n    /// submission, with older submissions appearing before younger.\n    ///\n    /// Entries are added by `track_submission` and drained by\n    /// `LifetimeTracker::triage_submissions`. Lots of methods contribute data\n    /// to particular entries.\n    active: Vec<ActiveSubmission>,\n\n    /// Buffers the user has asked us to map, and which are not used by any\n    /// queue submission still in flight.\n    ready_to_map: Vec<Arc<Buffer>>,\n\n    /// BLASes the user has asked us to prepare to compact, and which are not used by any\n    /// queue submission still in flight.\n    ready_to_compact: Vec<Arc<Blas>>,\n\n    /// Queue \"on_submitted_work_done\" closures that were initiated for while there is no\n    /// currently pending submissions. These cannot be immediately invoked as they\n    /// must happen _after_ all mapped buffer callbacks are mapped, so we defer them\n    /// here until the next time the device is maintained.\n    work_done_closures: SmallVec<[SubmittedWorkDoneClosure; 1]>,\n}\n\nimpl LifetimeTracker {\n    pub fn new() -> Self {\n        Self {\n            active: Vec::new(),\n            ready_to_map: Vec::new(),\n            ready_to_compact: Vec::new(),\n            work_done_closures: SmallVec::new(),\n        }\n    }\n\n    /// Return true if there are no queue submissions still in flight.\n    pub fn queue_empty(&self) -> bool {\n        self.active.is_empty()\n    }\n\n    /// Start tracking resources associated with a new queue submission.\n    pub fn track_submission(&mut self, index: SubmissionIndex, encoders: Vec<EncoderInFlight>) {\n        self.active.push(ActiveSubmission {\n            index,\n            mapped: Vec::new(),\n            compact_read_back: Vec::new(),\n            encoders,\n            work_done_closures: SmallVec::new(),\n        });\n    }\n\n    pub(crate) fn map(&mut self, buffer: &Arc<Buffer>) -> Option<SubmissionIndex> {\n        // Determine which buffers are ready to map, and which must wait for the GPU.\n        let submission = self\n            .active\n            .iter_mut()\n            .rev()\n            .find(|a| a.contains_buffer(buffer));\n\n        let maybe_submission_index = submission.as_ref().map(|s| s.index);\n\n        submission\n            .map_or(&mut self.ready_to_map, |a| &mut a.mapped)\n            .push(buffer.clone());\n\n        maybe_submission_index\n    }\n\n    pub(crate) fn prepare_compact(&mut self, blas: &Arc<Blas>) -> Option<SubmissionIndex> {\n        // Determine which BLASes are ready to map, and which must wait for the GPU.\n        let submission = self.active.iter_mut().rev().find(|a| a.contains_blas(blas));\n\n        let maybe_submission_index = submission.as_ref().map(|s| s.index);\n\n        submission\n            .map_or(&mut self.ready_to_compact, |a| &mut a.compact_read_back)\n            .push(blas.clone());\n\n        maybe_submission_index\n    }\n\n    /// Returns the submission index of the most recent submission that uses the\n    /// given buffer.\n    pub fn get_buffer_latest_submission_index(&self, buffer: &Buffer) -> Option<SubmissionIndex> {\n        // We iterate in reverse order, so that we can bail out early as soon\n        // as we find a hit.\n        self.active.iter().rev().find_map(|submission| {\n            if submission.contains_buffer(buffer) {\n                Some(submission.index)\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Returns the submission index of the most recent submission that uses the\n    /// given texture.\n    pub fn get_texture_latest_submission_index(\n        &self,\n        texture: &Texture,\n    ) -> Option<SubmissionIndex> {\n        // We iterate in reverse order, so that we can bail out early as soon\n        // as we find a hit.\n        self.active.iter().rev().find_map(|submission| {\n            if submission.contains_texture(texture) {\n                Some(submission.index)\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Sort out the consequences of completed submissions.\n    ///\n    /// Assume that all submissions up through `last_done` have completed.\n    ///\n    /// -   Buffers used by those submissions are now ready to map, if requested.\n    ///     Add any buffers in the submission's [`mapped`] list to\n    ///     [`self.ready_to_map`], where [`LifetimeTracker::handle_mapping`]\n    ///     will find them.\n    ///\n    /// Return a list of [`SubmittedWorkDoneClosure`]s to run.\n    ///\n    /// [`mapped`]: ActiveSubmission::mapped\n    /// [`self.ready_to_map`]: LifetimeTracker::ready_to_map\n    /// [`SubmittedWorkDoneClosure`]: crate::device::queue::SubmittedWorkDoneClosure\n    #[must_use]\n    pub fn triage_submissions(\n        &mut self,\n        last_done: SubmissionIndex,\n    ) -> SmallVec<[SubmittedWorkDoneClosure; 1]> {\n        profiling::scope!(\"triage_submissions\");\n\n        //TODO: enable when `is_sorted_by_key` is stable\n        //debug_assert!(self.active.is_sorted_by_key(|a| a.index));\n        let done_count = self\n            .active\n            .iter()\n            .position(|a| a.index > last_done)\n            .unwrap_or(self.active.len());\n\n        let mut work_done_closures: SmallVec<_> = self.work_done_closures.drain(..).collect();\n        for a in self.active.drain(..done_count) {\n            self.ready_to_map.extend(a.mapped);\n            self.ready_to_compact.extend(a.compact_read_back);\n            for encoder in a.encoders {\n                // This involves actually decrementing the ref count of all command buffer\n                // resources, so can be _very_ expensive.\n                profiling::scope!(\"drop command buffer trackers\");\n                drop(encoder);\n            }\n            work_done_closures.extend(a.work_done_closures);\n        }\n        work_done_closures\n    }\n\n    pub fn schedule_resource_destruction(\n        &mut self,\n        temp_resource: TempResource,\n        last_submit_index: SubmissionIndex,\n    ) {\n        let resources = self\n            .active\n            .iter_mut()\n            .find(|a| a.index == last_submit_index)\n            .map(|a| {\n                // Because this resource's `last_submit_index` matches `a.index`,\n                // we know that we must have done something with the resource,\n                // so `a.encoders` should not be empty.\n                &mut a.encoders.last_mut().unwrap().temp_resources\n            });\n        if let Some(resources) = resources {\n            resources.push(temp_resource);\n        }\n    }\n\n    pub fn add_work_done_closure(\n        &mut self,\n        closure: SubmittedWorkDoneClosure,\n    ) -> Option<SubmissionIndex> {\n        match self.active.last_mut() {\n            Some(active) => {\n                active.work_done_closures.push(closure);\n                Some(active.index)\n            }\n            // We must defer the closure until all previously occurring map_async closures\n            // have fired. This is required by the spec.\n            None => {\n                self.work_done_closures.push(closure);\n                None\n            }\n        }\n    }\n\n    /// Map the buffers in `self.ready_to_map`.\n    ///\n    /// Return a list of mapping notifications to send.\n    ///\n    /// See the documentation for [`LifetimeTracker`] for details.\n    #[must_use]\n    pub(crate) fn handle_mapping(\n        &mut self,\n        snatch_guard: &SnatchGuard,\n    ) -> Vec<super::BufferMapPendingClosure> {\n        if self.ready_to_map.is_empty() {\n            return Vec::new();\n        }\n        let mut pending_callbacks: Vec<super::BufferMapPendingClosure> =\n            Vec::with_capacity(self.ready_to_map.len());\n\n        for buffer in self.ready_to_map.drain(..) {\n            match buffer.map(snatch_guard) {\n                Some(cb) => pending_callbacks.push(cb),\n                None => continue,\n            }\n        }\n        pending_callbacks\n    }\n    /// Read back compact sizes from the BLASes in `self.ready_to_compact`.\n    ///\n    /// Return a list of mapping notifications to send.\n    ///\n    /// See the documentation for [`LifetimeTracker`] for details.\n    #[must_use]\n    pub(crate) fn handle_compact_read_back(&mut self) -> Vec<BlasCompactReadyPendingClosure> {\n        if self.ready_to_compact.is_empty() {\n            return Vec::new();\n        }\n        let mut pending_callbacks: Vec<BlasCompactReadyPendingClosure> =\n            Vec::with_capacity(self.ready_to_compact.len());\n\n        for blas in self.ready_to_compact.drain(..) {\n            match blas.read_back_compact_size() {\n                Some(cb) => pending_callbacks.push(cb),\n                None => continue,\n            }\n        }\n        pending_callbacks\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/mod.rs",
    "content": "use alloc::{boxed::Box, string::String, vec::Vec};\nuse core::{fmt, num::NonZeroU32};\n\nuse crate::{\n    binding_model,\n    ray_tracing::BlasCompactReadyPendingClosure,\n    resource::{\n        Buffer, BufferAccessError, BufferAccessResult, BufferMapOperation, Labeled,\n        RawResourceAccess, ResourceErrorIdent,\n    },\n    snatch::SnatchGuard,\n    Label, DOWNLEVEL_ERROR_MESSAGE,\n};\n\nuse arrayvec::ArrayVec;\nuse smallvec::SmallVec;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    BufferAddress, DeviceLostReason, TextureFormat,\n};\n\npub(crate) mod bgl;\npub mod global;\nmod life;\npub mod queue;\npub mod ray_tracing;\npub mod resource;\n#[cfg(any(feature = \"trace\", feature = \"replay\"))]\npub mod trace;\npub use {life::WaitIdleError, resource::Device};\n\npub const SHADER_STAGE_COUNT: usize = hal::MAX_CONCURRENT_SHADER_STAGES;\n// Should be large enough for the largest possible texture row. This\n// value is enough for a 16k texture with float4 format.\npub(crate) const ZERO_BUFFER_SIZE: BufferAddress = 512 << 10;\n\npub(crate) const ENTRYPOINT_FAILURE_ERROR: &str = \"The given EntryPoint is Invalid\";\n\npub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;\n\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub enum HostMap {\n    Read,\n    Write,\n}\n\n#[derive(Clone, Debug, Hash, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct AttachmentData<T> {\n    pub colors: ArrayVec<Option<T>, { hal::MAX_COLOR_ATTACHMENTS }>,\n    pub resolves: ArrayVec<T, { hal::MAX_COLOR_ATTACHMENTS }>,\n    pub depth_stencil: Option<T>,\n}\nimpl<T: PartialEq> Eq for AttachmentData<T> {}\n\n#[derive(Clone, Debug, Hash, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct RenderPassContext {\n    pub attachments: AttachmentData<TextureFormat>,\n    pub sample_count: u32,\n    pub multiview_mask: Option<NonZeroU32>,\n}\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum RenderPassCompatibilityError {\n    #[error(\n        \"Incompatible color attachments at indices {indices:?}: the RenderPass uses textures with formats {expected:?} but the {res} uses attachments with formats {actual:?}\",\n    )]\n    IncompatibleColorAttachment {\n        indices: Vec<usize>,\n        expected: Vec<Option<TextureFormat>>,\n        actual: Vec<Option<TextureFormat>>,\n        res: ResourceErrorIdent,\n    },\n    #[error(\n        \"Incompatible depth-stencil attachment format: the RenderPass uses a texture with format {expected:?} but the {res} uses an attachment with format {actual:?}\",\n    )]\n    IncompatibleDepthStencilAttachment {\n        expected: Option<TextureFormat>,\n        actual: Option<TextureFormat>,\n        res: ResourceErrorIdent,\n    },\n    #[error(\n        \"Incompatible sample count: the RenderPass uses textures with sample count {expected:?} but the {res} uses attachments with format {actual:?}\",\n    )]\n    IncompatibleSampleCount {\n        expected: u32,\n        actual: u32,\n        res: ResourceErrorIdent,\n    },\n    #[error(\"Incompatible multiview setting: the RenderPass uses setting {expected:?} but the {res} uses setting {actual:?}\")]\n    IncompatibleMultiview {\n        expected: Option<NonZeroU32>,\n        actual: Option<NonZeroU32>,\n        res: ResourceErrorIdent,\n    },\n}\n\nimpl WebGpuError for RenderPassCompatibilityError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\nimpl RenderPassContext {\n    // Assumes the renderpass only contains one subpass\n    pub(crate) fn check_compatible<T: Labeled>(\n        &self,\n        other: &Self,\n        res: &T,\n    ) -> Result<(), RenderPassCompatibilityError> {\n        if self.attachments.colors != other.attachments.colors {\n            let indices = self\n                .attachments\n                .colors\n                .iter()\n                .zip(&other.attachments.colors)\n                .enumerate()\n                .filter_map(|(idx, (left, right))| (left != right).then_some(idx))\n                .collect();\n            return Err(RenderPassCompatibilityError::IncompatibleColorAttachment {\n                indices,\n                expected: self.attachments.colors.iter().cloned().collect(),\n                actual: other.attachments.colors.iter().cloned().collect(),\n                res: res.error_ident(),\n            });\n        }\n        if self.attachments.depth_stencil != other.attachments.depth_stencil {\n            return Err(\n                RenderPassCompatibilityError::IncompatibleDepthStencilAttachment {\n                    expected: self.attachments.depth_stencil,\n                    actual: other.attachments.depth_stencil,\n                    res: res.error_ident(),\n                },\n            );\n        }\n        if self.sample_count != other.sample_count {\n            return Err(RenderPassCompatibilityError::IncompatibleSampleCount {\n                expected: self.sample_count,\n                actual: other.sample_count,\n                res: res.error_ident(),\n            });\n        }\n        if self.multiview_mask != other.multiview_mask {\n            return Err(RenderPassCompatibilityError::IncompatibleMultiview {\n                expected: self.multiview_mask,\n                actual: other.multiview_mask,\n                res: res.error_ident(),\n            });\n        }\n        Ok(())\n    }\n}\n\npub type BufferMapPendingClosure = (BufferMapOperation, BufferAccessResult);\n\n#[derive(Default)]\npub struct UserClosures {\n    pub mappings: Vec<BufferMapPendingClosure>,\n    pub blas_compact_ready: Vec<BlasCompactReadyPendingClosure>,\n    pub submissions: SmallVec<[queue::SubmittedWorkDoneClosure; 1]>,\n    pub device_lost_invocations: SmallVec<[DeviceLostInvocation; 1]>,\n}\n\nimpl UserClosures {\n    fn extend(&mut self, other: Self) {\n        self.mappings.extend(other.mappings);\n        self.blas_compact_ready.extend(other.blas_compact_ready);\n        self.submissions.extend(other.submissions);\n        self.device_lost_invocations\n            .extend(other.device_lost_invocations);\n    }\n\n    fn fire(self) {\n        // Note: this logic is specifically moved out of `handle_mapping()` in order to\n        // have nothing locked by the time we execute users callback code.\n\n        // Mappings _must_ be fired before submissions, as the spec requires all mapping callbacks that are registered before\n        // a on_submitted_work_done callback to be fired before the on_submitted_work_done callback.\n        for (mut operation, status) in self.mappings {\n            if let Some(callback) = operation.callback.take() {\n                callback(status);\n            }\n        }\n        for (mut operation, status) in self.blas_compact_ready {\n            if let Some(callback) = operation.take() {\n                callback(status);\n            }\n        }\n        for closure in self.submissions {\n            closure();\n        }\n        for invocation in self.device_lost_invocations {\n            (invocation.closure)(invocation.reason, invocation.message);\n        }\n    }\n}\n\n#[cfg(send_sync)]\npub type DeviceLostClosure = Box<dyn FnOnce(DeviceLostReason, String) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type DeviceLostClosure = Box<dyn FnOnce(DeviceLostReason, String) + 'static>;\n\npub struct DeviceLostInvocation {\n    closure: DeviceLostClosure,\n    reason: DeviceLostReason,\n    message: String,\n}\n\npub(crate) fn map_buffer(\n    buffer: &Buffer,\n    offset: BufferAddress,\n    size: BufferAddress,\n    kind: HostMap,\n    snatch_guard: &SnatchGuard,\n) -> Result<hal::BufferMapping, BufferAccessError> {\n    let raw_device = buffer.device.raw();\n    let raw_buffer = buffer.try_raw(snatch_guard)?;\n    let mapping = unsafe {\n        raw_device\n            .map_buffer(raw_buffer, offset..offset + size)\n            .map_err(|e| buffer.device.handle_hal_error(e))?\n    };\n\n    if !mapping.is_coherent && kind == HostMap::Read {\n        #[allow(clippy::single_range_in_vec_init)]\n        unsafe {\n            raw_device.invalidate_mapped_ranges(raw_buffer, &[offset..offset + size]);\n        }\n    }\n\n    assert_eq!(offset % wgt::COPY_BUFFER_ALIGNMENT, 0);\n    assert_eq!(size % wgt::COPY_BUFFER_ALIGNMENT, 0);\n    // Zero out uninitialized parts of the mapping. (Spec dictates all resources\n    // behave as if they were initialized with zero)\n    //\n    // If this is a read mapping, ideally we would use a `clear_buffer` command\n    // before reading the data from GPU (i.e. `invalidate_range`). However, this\n    // would require us to kick off and wait for a command buffer or piggy back\n    // on an existing one (the later is likely the only worthwhile option). As\n    // reading uninitialized memory isn't a particular important path to\n    // support, we instead just initialize the memory here and make sure it is\n    // GPU visible, so this happens at max only once for every buffer region.\n    //\n    // If this is a write mapping zeroing out the memory here is the only\n    // reasonable way as all data is pushed to GPU anyways.\n\n    let mapped = unsafe { core::slice::from_raw_parts_mut(mapping.ptr.as_ptr(), size as usize) };\n\n    // We can't call flush_mapped_ranges in this case, so we can't drain the uninitialized ranges either\n    if !mapping.is_coherent\n        && kind == HostMap::Read\n        && !buffer.usage.contains(wgt::BufferUsages::MAP_WRITE)\n    {\n        for uninitialized in buffer\n            .initialization_status\n            .write()\n            .uninitialized(offset..(size + offset))\n        {\n            // The mapping's pointer is already offset, however we track the\n            // uninitialized range relative to the buffer's start.\n            let fill_range =\n                (uninitialized.start - offset) as usize..(uninitialized.end - offset) as usize;\n            mapped[fill_range].fill(0);\n        }\n    } else {\n        for uninitialized in buffer\n            .initialization_status\n            .write()\n            .drain(offset..(size + offset))\n        {\n            // The mapping's pointer is already offset, however we track the\n            // uninitialized range relative to the buffer's start.\n            let fill_range =\n                (uninitialized.start - offset) as usize..(uninitialized.end - offset) as usize;\n            mapped[fill_range].fill(0);\n\n            // NOTE: This is only possible when MAPPABLE_PRIMARY_BUFFERS is enabled.\n            if !mapping.is_coherent\n                && kind == HostMap::Read\n                && buffer.usage.contains(wgt::BufferUsages::MAP_WRITE)\n            {\n                unsafe { raw_device.flush_mapped_ranges(raw_buffer, &[uninitialized]) };\n            }\n        }\n    }\n\n    Ok(mapping)\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct DeviceMismatch {\n    pub(super) res: ResourceErrorIdent,\n    pub(super) res_device: ResourceErrorIdent,\n    pub(super) target: Option<ResourceErrorIdent>,\n    pub(super) target_device: ResourceErrorIdent,\n}\n\nimpl fmt::Display for DeviceMismatch {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(\n            f,\n            \"{} of {} doesn't match {}\",\n            self.res_device, self.res, self.target_device\n        )?;\n        if let Some(target) = self.target.as_ref() {\n            write!(f, \" of {target}\")?;\n        }\n        Ok(())\n    }\n}\n\nimpl core::error::Error for DeviceMismatch {}\n\nimpl WebGpuError for DeviceMismatch {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[non_exhaustive]\npub enum DeviceError {\n    #[error(\"Parent device is lost\")]\n    Lost,\n    #[error(\"Not enough memory left.\")]\n    OutOfMemory,\n    #[error(transparent)]\n    DeviceMismatch(#[from] Box<DeviceMismatch>),\n}\n\nimpl WebGpuError for DeviceError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::DeviceMismatch(e) => e.webgpu_error_type(),\n            Self::Lost => ErrorType::DeviceLost,\n            Self::OutOfMemory => ErrorType::OutOfMemory,\n        }\n    }\n}\n\nimpl DeviceError {\n    /// Only use this function in contexts where there is no `Device`.\n    ///\n    /// Use [`Device::handle_hal_error`] otherwise.\n    pub fn from_hal(error: hal::DeviceError) -> Self {\n        match error {\n            hal::DeviceError::Lost => Self::Lost,\n            hal::DeviceError::OutOfMemory => Self::OutOfMemory,\n            hal::DeviceError::Unexpected => Self::Lost,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[error(\"Features {0:?} are required but not enabled on the device\")]\npub struct MissingFeatures(pub wgt::Features);\n\nimpl WebGpuError for MissingFeatures {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[error(\n    \"Downlevel flags {0:?} are required but not supported on the device.\\n{DOWNLEVEL_ERROR_MESSAGE}\",\n)]\npub struct MissingDownlevelFlags(pub wgt::DownlevelFlags);\n\nimpl WebGpuError for MissingDownlevelFlags {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\npub use wgpu_naga_bridge::create_validator;\npub use wgpu_naga_bridge::features_to_naga_capabilities;\n"
  },
  {
    "path": "wgpu-core/src/device/queue.rs",
    "content": "use alloc::{boxed::Box, string::ToString, sync::Arc, vec, vec::Vec};\nuse core::{\n    iter,\n    mem::{self, ManuallyDrop},\n    num::NonZeroU64,\n    ptr::NonNull,\n    sync::atomic::Ordering,\n};\nuse smallvec::SmallVec;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    AccelerationStructureFlags,\n};\n\nuse super::{life::LifetimeTracker, Device};\n#[cfg(feature = \"trace\")]\nuse crate::device::trace::{Action, IntoTrace};\nuse crate::{\n    api_log,\n    command::{\n        extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,\n        validate_texture_copy_dst_format, validate_texture_copy_range, ClearError,\n        CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,\n        TransferError,\n    },\n    device::{DeviceError, WaitIdleError},\n    get_lowest_common_denom,\n    global::Global,\n    hal_label,\n    id::{self, BlasId, QueueId},\n    init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},\n    lock::{rank, Mutex, MutexGuard, RwLock, RwLockWriteGuard},\n    ray_tracing::{BlasCompactReadyPendingClosure, CompactBlasError},\n    resource::{\n        Blas, BlasCompactState, Buffer, BufferAccessError, BufferMapState, DestroyedBuffer,\n        DestroyedResourceError, DestroyedTexture, Fallible, FlushedStagingBuffer,\n        InvalidResourceError, Labeled, ParentDevice, ResourceErrorIdent, StagingBuffer, Texture,\n        TextureInner, Trackable, TrackingData,\n    },\n    resource_log,\n    scratch::ScratchBuffer,\n    snatch::{SnatchGuard, Snatchable},\n    track::{self, Tracker, TrackerIndex},\n    FastHashMap, SubmissionIndex,\n};\nuse crate::{device::resource::CommandIndices, resource::RawResourceAccess};\n\npub struct Queue {\n    raw: Box<dyn hal::DynQueue>,\n    pub(crate) pending_writes: Mutex<PendingWrites>,\n    life_tracker: Mutex<LifetimeTracker>,\n    // The device needs to be dropped last (`Device.zero_buffer` might be referenced by the encoder in pending writes).\n    pub(crate) device: Arc<Device>,\n}\n\nimpl Queue {\n    pub(crate) fn new(\n        device: Arc<Device>,\n        raw: Box<dyn hal::DynQueue>,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<Self, DeviceError> {\n        let pending_encoder = device\n            .command_allocator\n            .acquire_encoder(device.raw(), raw.as_ref())\n            .map_err(DeviceError::from_hal);\n\n        let pending_encoder = match pending_encoder {\n            Ok(pending_encoder) => pending_encoder,\n            Err(e) => {\n                return Err(e);\n            }\n        };\n\n        let mut pending_writes = PendingWrites::new(pending_encoder, instance_flags);\n\n        let zero_buffer = device.zero_buffer.as_ref();\n        pending_writes.activate();\n        unsafe {\n            pending_writes\n                .command_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: zero_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::empty(),\n                        to: wgt::BufferUses::COPY_DST,\n                    },\n                }]);\n            pending_writes\n                .command_encoder\n                .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);\n            pending_writes\n                .command_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: zero_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::COPY_DST,\n                        to: wgt::BufferUses::COPY_SRC,\n                    },\n                }]);\n        }\n\n        Ok(Queue {\n            raw,\n            device,\n            pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),\n            life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),\n        })\n    }\n\n    pub(crate) fn raw(&self) -> &dyn hal::DynQueue {\n        self.raw.as_ref()\n    }\n\n    #[track_caller]\n    pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {\n        self.life_tracker.lock()\n    }\n\n    pub(crate) fn maintain(\n        &self,\n        submission_index: u64,\n        snatch_guard: &SnatchGuard,\n    ) -> (\n        SmallVec<[SubmittedWorkDoneClosure; 1]>,\n        Vec<super::BufferMapPendingClosure>,\n        Vec<BlasCompactReadyPendingClosure>,\n        bool,\n    ) {\n        let mut life_tracker = self.lock_life();\n        let submission_closures = life_tracker.triage_submissions(submission_index);\n\n        let mapping_closures = life_tracker.handle_mapping(snatch_guard);\n        let blas_closures = life_tracker.handle_compact_read_back();\n\n        let queue_empty = life_tracker.queue_empty();\n\n        (\n            submission_closures,\n            mapping_closures,\n            blas_closures,\n            queue_empty,\n        )\n    }\n}\n\ncrate::impl_resource_type!(Queue);\n// TODO: https://github.com/gfx-rs/wgpu/issues/4014\nimpl Labeled for Queue {\n    fn label(&self) -> &str {\n        \"\"\n    }\n}\ncrate::impl_parent_device!(Queue);\ncrate::impl_storage_item!(Queue);\n\nimpl Drop for Queue {\n    fn drop(&mut self) {\n        resource_log!(\"Drop {}\", self.error_ident());\n\n        let last_successful_submission_index = self\n            .device\n            .last_successful_submission_index\n            .load(Ordering::Acquire);\n\n        let fence = self.device.fence.read();\n\n        // Try waiting on the last submission using the following sequence of timeouts\n        let timeouts_in_ms = [100, 200, 400, 800, 1600, 3200];\n\n        for (i, timeout_ms) in timeouts_in_ms.into_iter().enumerate() {\n            let is_last_iter = i == timeouts_in_ms.len() - 1;\n\n            api_log!(\n                \"Waiting on last submission. try: {}/{}. timeout: {}ms\",\n                i + 1,\n                timeouts_in_ms.len(),\n                timeout_ms\n            );\n\n            let wait_res = unsafe {\n                self.device.raw().wait(\n                    fence.as_ref(),\n                    last_successful_submission_index,\n                    #[cfg(not(target_arch = \"wasm32\"))]\n                    Some(core::time::Duration::from_millis(timeout_ms)),\n                    #[cfg(target_arch = \"wasm32\")]\n                    Some(core::time::Duration::ZERO), // WebKit and Chromium don't support a non-0 timeout\n                )\n            };\n            // Note: If we don't panic below we are in UB land (destroying resources while they are still in use by the GPU).\n            match wait_res {\n                Ok(true) => break,\n                Ok(false) => {\n                    // It's fine that we timed out on WebGL; GL objects can be deleted early as they\n                    // will be kept around by the driver if GPU work hasn't finished.\n                    // Moreover, the way we emulate read mappings on WebGL allows us to execute map_buffer earlier than on other\n                    // backends since getBufferSubData is synchronous with respect to the other previously enqueued GL commands.\n                    // Relying on this behavior breaks the clean abstraction wgpu-hal tries to maintain and\n                    // we should find ways to improve this. See https://github.com/gfx-rs/wgpu/issues/6538.\n                    #[cfg(target_arch = \"wasm32\")]\n                    {\n                        break;\n                    }\n                    #[cfg(not(target_arch = \"wasm32\"))]\n                    {\n                        if is_last_iter {\n                            panic!(\n                                \"We timed out while waiting on the last successful submission to complete!\"\n                            );\n                        }\n                    }\n                }\n                Err(e) => match e {\n                    hal::DeviceError::OutOfMemory => {\n                        if is_last_iter {\n                            panic!(\n                                \"We ran into an OOM error while waiting on the last successful submission to complete!\"\n                            );\n                        }\n                    }\n                    hal::DeviceError::Lost => {\n                        self.device.handle_hal_error(e); // will lose the device\n                        break;\n                    }\n                    hal::DeviceError::Unexpected => {\n                        panic!(\n                            \"We ran into an unexpected error while waiting on the last successful submission to complete!\"\n                        );\n                    }\n                },\n            }\n        }\n        drop(fence);\n\n        let snatch_guard = self.device.snatchable_lock.read();\n        let (submission_closures, mapping_closures, blas_compact_ready_closures, queue_empty) =\n            self.maintain(last_successful_submission_index, &snatch_guard);\n        drop(snatch_guard);\n\n        assert!(queue_empty);\n\n        let closures = crate::device::UserClosures {\n            mappings: mapping_closures,\n            blas_compact_ready: blas_compact_ready_closures,\n            submissions: submission_closures,\n            device_lost_invocations: SmallVec::new(),\n        };\n\n        closures.fire();\n    }\n}\n\n#[cfg(send_sync)]\npub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;\n#[cfg(not(send_sync))]\npub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;\n\n/// A texture or buffer to be freed soon.\n///\n/// This is just a tagged raw texture or buffer, generally about to be added to\n/// some other more specific container like:\n///\n/// - `PendingWrites::temp_resources`: resources used by queue writes and\n///   unmaps, waiting to be folded in with the next queue submission\n///\n/// - `ActiveSubmission::temp_resources`: temporary resources used by a queue\n///   submission, to be freed when it completes\n#[derive(Debug)]\npub enum TempResource {\n    StagingBuffer(FlushedStagingBuffer),\n    ScratchBuffer(ScratchBuffer),\n    DestroyedBuffer(DestroyedBuffer),\n    DestroyedTexture(DestroyedTexture),\n}\n\n/// A series of raw [`CommandBuffer`]s that have been submitted to a\n/// queue, and the [`wgpu_hal::CommandEncoder`] that built them.\n///\n/// [`CommandBuffer`]: hal::Api::CommandBuffer\n/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\npub(crate) struct EncoderInFlight {\n    inner: crate::command::InnerCommandEncoder,\n    pub(crate) trackers: Tracker,\n    pub(crate) temp_resources: Vec<TempResource>,\n    /// We only need to keep these resources alive.\n    _indirect_draw_validation_resources: crate::indirect_validation::DrawResources,\n\n    /// These are the buffers that have been tracked by `PendingWrites`.\n    pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,\n    /// These are the textures that have been tracked by `PendingWrites`.\n    pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,\n    /// These are the BLASes that have been tracked by `PendingWrites`.\n    pub(crate) pending_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,\n}\n\n/// A private command encoder for writes made directly on the device\n/// or queue.\n///\n/// Operations like `buffer_unmap`, `queue_write_buffer`, and\n/// `queue_write_texture` need to copy data to the GPU. At the hal\n/// level, this must be done by encoding and submitting commands, but\n/// these operations are not associated with any specific wgpu command\n/// buffer.\n///\n/// Instead, `Device::pending_writes` owns one of these values, which\n/// has its own hal command encoder and resource lists. The commands\n/// accumulated here are automatically submitted to the queue the next\n/// time the user submits a wgpu command buffer, ahead of the user's\n/// commands.\n///\n/// Important:\n/// When locking pending_writes be sure that tracker is not locked\n/// and try to lock trackers for the minimum timespan possible\n///\n/// All uses of [`StagingBuffer`]s end up here.\n#[derive(Debug)]\npub(crate) struct PendingWrites {\n    // The command encoder needs to be destroyed before any other resource in pending writes.\n    pub command_encoder: Box<dyn hal::DynCommandEncoder>,\n\n    /// True if `command_encoder` is in the \"recording\" state, as\n    /// described in the docs for the [`wgpu_hal::CommandEncoder`]\n    /// trait.\n    ///\n    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder\n    pub is_recording: bool,\n\n    temp_resources: Vec<TempResource>,\n    dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,\n    dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,\n    copied_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,\n    instance_flags: wgt::InstanceFlags,\n}\n\nimpl PendingWrites {\n    pub fn new(\n        command_encoder: Box<dyn hal::DynCommandEncoder>,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Self {\n        Self {\n            command_encoder,\n            is_recording: false,\n            temp_resources: Vec::new(),\n            dst_buffers: FastHashMap::default(),\n            dst_textures: FastHashMap::default(),\n            copied_blas_s: FastHashMap::default(),\n            instance_flags,\n        }\n    }\n\n    pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {\n        self.dst_buffers\n            .insert(buffer.tracker_index(), buffer.clone());\n    }\n\n    pub fn insert_texture(&mut self, texture: &Arc<Texture>) {\n        self.dst_textures\n            .insert(texture.tracker_index(), texture.clone());\n    }\n\n    pub fn insert_blas(&mut self, blas: &Arc<Blas>) {\n        self.copied_blas_s\n            .insert(blas.tracker_index(), blas.clone());\n    }\n\n    pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {\n        self.dst_buffers.contains_key(&buffer.tracker_index())\n    }\n\n    pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {\n        self.dst_textures.contains_key(&texture.tracker_index())\n    }\n\n    pub fn consume_temp(&mut self, resource: TempResource) {\n        self.temp_resources.push(resource);\n    }\n\n    pub fn consume(&mut self, buffer: FlushedStagingBuffer) {\n        self.temp_resources\n            .push(TempResource::StagingBuffer(buffer));\n    }\n\n    fn pre_submit(\n        &mut self,\n        command_allocator: &CommandAllocator,\n        device: &Arc<Device>,\n        queue: &Queue,\n    ) -> Result<Option<EncoderInFlight>, DeviceError> {\n        if self.is_recording {\n            let pending_buffers = mem::take(&mut self.dst_buffers);\n            let pending_textures = mem::take(&mut self.dst_textures);\n            let pending_blas_s = mem::take(&mut self.copied_blas_s);\n\n            let cmd_buf = unsafe { self.command_encoder.end_encoding() }\n                .map_err(|e| device.handle_hal_error(e))?;\n            self.is_recording = false;\n\n            let new_encoder = command_allocator\n                .acquire_encoder(device.raw(), queue.raw())\n                .map_err(|e| device.handle_hal_error(e))?;\n\n            let encoder = EncoderInFlight {\n                inner: crate::command::InnerCommandEncoder {\n                    raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),\n                    list: vec![cmd_buf],\n                    device: device.clone(),\n                    is_open: false,\n                    api: crate::command::EncodingApi::InternalUse,\n                    label: \"(wgpu internal) PendingWrites command encoder\".into(),\n                },\n                trackers: Tracker::new(device.ordered_buffer_usages, device.ordered_texture_usages),\n                temp_resources: mem::take(&mut self.temp_resources),\n                _indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new(\n                    device.clone(),\n                ),\n                pending_buffers,\n                pending_textures,\n                pending_blas_s,\n            };\n            Ok(Some(encoder))\n        } else {\n            self.dst_buffers.clear();\n            self.dst_textures.clear();\n            self.copied_blas_s.clear();\n            Ok(None)\n        }\n    }\n\n    pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {\n        if !self.is_recording {\n            unsafe {\n                self.command_encoder\n                    .begin_encoding(hal_label(\n                        Some(\"(wgpu internal) PendingWrites\"),\n                        self.instance_flags,\n                    ))\n                    .unwrap();\n            }\n            self.is_recording = true;\n        }\n        self.command_encoder.as_mut()\n    }\n}\n\nimpl Drop for PendingWrites {\n    fn drop(&mut self) {\n        unsafe {\n            if self.is_recording {\n                self.command_encoder.discard_encoding();\n            }\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum QueueWriteError {\n    #[error(transparent)]\n    Queue(#[from] DeviceError),\n    #[error(transparent)]\n    Transfer(#[from] TransferError),\n    #[error(transparent)]\n    MemoryInitFailure(#[from] ClearError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for QueueWriteError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Queue(e) => e.webgpu_error_type(),\n            Self::Transfer(e) => e.webgpu_error_type(),\n            Self::MemoryInitFailure(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum QueueSubmitError {\n    #[error(transparent)]\n    Queue(#[from] DeviceError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(transparent)]\n    Unmap(#[from] BufferAccessError),\n    #[error(\"{0} is still mapped\")]\n    BufferStillMapped(ResourceErrorIdent),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    CommandEncoder(#[from] CommandEncoderError),\n    #[error(transparent)]\n    ValidateAsActionsError(#[from] crate::ray_tracing::ValidateAsActionsError),\n}\n\nimpl WebGpuError for QueueSubmitError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Queue(e) => e.webgpu_error_type(),\n            Self::Unmap(e) => e.webgpu_error_type(),\n            Self::CommandEncoder(e) => e.webgpu_error_type(),\n            Self::ValidateAsActionsError(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(_) | Self::BufferStillMapped(_) => ErrorType::Validation,\n        }\n    }\n}\n\n//TODO: move out common parts of write_xxx.\n\nimpl Queue {\n    pub fn write_buffer(\n        &self,\n        buffer: Arc<Buffer>,\n        buffer_offset: wgt::BufferAddress,\n        data: &[u8],\n    ) -> Result<(), QueueWriteError> {\n        profiling::scope!(\"Queue::write_buffer\");\n        api_log!(\"Queue::write_buffer\");\n\n        self.device.check_is_valid()?;\n\n        let data_size = data.len() as wgt::BufferAddress;\n\n        self.same_device_as(buffer.as_ref())?;\n\n        let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {\n            data_size\n        } else {\n            // This must happen after parameter validation (so that errors are reported\n            // as required by the spec), but before any side effects.\n            log::trace!(\"Ignoring write_buffer of size 0\");\n            return Ok(());\n        };\n\n        // Platform validation requires that the staging buffer always be\n        // freed, even if an error occurs. All paths from here must call\n        // `device.pending_writes.consume`.\n        let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;\n\n        let staging_buffer = {\n            profiling::scope!(\"copy\");\n            staging_buffer.write(data);\n            staging_buffer.flush()\n        };\n\n        let snatch_guard = self.device.snatchable_lock.read();\n        let mut pending_writes = self.pending_writes.lock();\n\n        let result = self.write_staging_buffer_impl(\n            &snatch_guard,\n            &mut pending_writes,\n            &staging_buffer,\n            buffer,\n            buffer_offset,\n        );\n\n        drop(snatch_guard);\n\n        pending_writes.consume(staging_buffer);\n\n        drop(pending_writes);\n\n        result\n    }\n\n    pub fn create_staging_buffer(\n        &self,\n        buffer_size: wgt::BufferSize,\n    ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {\n        profiling::scope!(\"Queue::create_staging_buffer\");\n        resource_log!(\"Queue::create_staging_buffer\");\n\n        self.device.check_is_valid()?;\n\n        let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;\n        let ptr = unsafe { staging_buffer.ptr() };\n\n        Ok((staging_buffer, ptr))\n    }\n\n    pub fn write_staging_buffer(\n        &self,\n        buffer: Fallible<Buffer>,\n        buffer_offset: wgt::BufferAddress,\n        staging_buffer: StagingBuffer,\n    ) -> Result<(), QueueWriteError> {\n        profiling::scope!(\"Queue::write_staging_buffer\");\n\n        self.device.check_is_valid()?;\n\n        let buffer = buffer.get()?;\n\n        // At this point, we have taken ownership of the staging_buffer from the\n        // user. Platform validation requires that the staging buffer always\n        // be freed, even if an error occurs. All paths from here must call\n        // `device.pending_writes.consume`.\n        let staging_buffer = staging_buffer.flush();\n\n        let snatch_guard = self.device.snatchable_lock.read();\n        let mut pending_writes = self.pending_writes.lock();\n\n        let result = self.write_staging_buffer_impl(\n            &snatch_guard,\n            &mut pending_writes,\n            &staging_buffer,\n            buffer,\n            buffer_offset,\n        );\n\n        drop(snatch_guard);\n\n        pending_writes.consume(staging_buffer);\n\n        drop(pending_writes);\n\n        result\n    }\n\n    pub fn validate_write_buffer(\n        &self,\n        buffer: Fallible<Buffer>,\n        buffer_offset: u64,\n        buffer_size: wgt::BufferSize,\n    ) -> Result<(), QueueWriteError> {\n        profiling::scope!(\"Queue::validate_write_buffer\");\n\n        self.device.check_is_valid()?;\n\n        let buffer = buffer.get()?;\n\n        self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size)?;\n\n        Ok(())\n    }\n\n    fn validate_write_buffer_impl(\n        &self,\n        buffer: &Buffer,\n        buffer_offset: u64,\n        buffer_size: wgt::BufferSize,\n    ) -> Result<(), TransferError> {\n        if !matches!(&*buffer.map_state.lock(), BufferMapState::Idle) {\n            return Err(TransferError::BufferNotAvailable);\n        }\n        buffer.check_usage(wgt::BufferUsages::COPY_DST)?;\n        if !buffer_size.get().is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            return Err(TransferError::UnalignedCopySize(buffer_size.get()));\n        }\n        if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            return Err(TransferError::UnalignedBufferOffset(buffer_offset));\n        }\n\n        if buffer_offset > buffer.size {\n            return Err(TransferError::BufferStartOffsetOverrun {\n                start_offset: buffer_offset,\n                buffer_size: buffer.size,\n                side: CopySide::Destination,\n            });\n        }\n        if buffer_size.get() > buffer.size - buffer_offset {\n            return Err(TransferError::BufferEndOffsetOverrun {\n                start_offset: buffer_offset,\n                size: buffer_size.get(),\n                buffer_size: buffer.size,\n                side: CopySide::Destination,\n            });\n        }\n\n        Ok(())\n    }\n\n    fn write_staging_buffer_impl(\n        &self,\n        snatch_guard: &SnatchGuard,\n        pending_writes: &mut PendingWrites,\n        staging_buffer: &FlushedStagingBuffer,\n        buffer: Arc<Buffer>,\n        buffer_offset: u64,\n    ) -> Result<(), QueueWriteError> {\n        self.device.check_is_valid()?;\n\n        let transition = {\n            let mut trackers = self.device.trackers.lock();\n            trackers\n                .buffers\n                .set_single(&buffer, wgt::BufferUses::COPY_DST)\n        };\n\n        let dst_raw = buffer.try_raw(snatch_guard)?;\n\n        self.same_device_as(buffer.as_ref())?;\n\n        self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size)?;\n\n        let region = hal::BufferCopy {\n            src_offset: 0,\n            dst_offset: buffer_offset,\n            size: staging_buffer.size,\n        };\n        let barriers = iter::once(hal::BufferBarrier {\n            buffer: staging_buffer.raw(),\n            usage: hal::StateTransition {\n                from: wgt::BufferUses::MAP_WRITE,\n                to: wgt::BufferUses::COPY_SRC,\n            },\n        })\n        .chain(transition.map(|pending| pending.into_hal(&buffer, snatch_guard)))\n        .collect::<Vec<_>>();\n        let encoder = pending_writes.activate();\n        unsafe {\n            encoder.transition_buffers(&barriers);\n            encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);\n        }\n\n        pending_writes.insert_buffer(&buffer);\n\n        // Ensure the overwritten bytes are marked as initialized so\n        // they don't need to be nulled prior to mapping or binding.\n        {\n            buffer\n                .initialization_status\n                .write()\n                .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));\n        }\n\n        Ok(())\n    }\n\n    pub fn write_texture(\n        &self,\n        destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,\n        data: &[u8],\n        data_layout: &wgt::TexelCopyBufferLayout,\n        size: &wgt::Extent3d,\n    ) -> Result<(), QueueWriteError> {\n        profiling::scope!(\"Queue::write_texture\");\n        api_log!(\"Queue::write_texture\");\n\n        self.device.check_is_valid()?;\n\n        let dst = destination.texture;\n        let destination = wgt::TexelCopyTextureInfo {\n            texture: (),\n            mip_level: destination.mip_level,\n            origin: destination.origin,\n            aspect: destination.aspect,\n        };\n\n        self.same_device_as(dst.as_ref())?;\n\n        dst.check_usage(wgt::TextureUsages::COPY_DST)\n            .map_err(TransferError::MissingTextureUsage)?;\n\n        // Note: Doing the copy range validation early is important because ensures that the\n        // dimensions are not going to cause overflow in other parts of the validation.\n        let (hal_copy_size, array_layer_count) =\n            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;\n\n        let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;\n\n        validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;\n\n        validate_texture_buffer_copy(\n            &destination,\n            dst_base.aspect,\n            &dst.desc,\n            data_layout,\n            false, // alignment not required for buffer offset or bytes per row\n        )?;\n\n        // Note: `_source_bytes_per_array_layer` is ignored since we\n        // have a staging copy, and it can have a different value.\n        let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =\n            validate_linear_texture_data(\n                data_layout,\n                dst.desc.format,\n                destination.aspect,\n                data.len() as wgt::BufferAddress,\n                CopySide::Source,\n                size,\n            )?;\n\n        if dst.desc.format.is_depth_stencil_format() {\n            self.device\n                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)\n                .map_err(TransferError::from)?;\n        }\n\n        let snatch_guard = self.device.snatchable_lock.read();\n\n        let dst_raw = dst.try_raw(&snatch_guard)?;\n\n        // This must happen after parameter validation (so that errors are reported\n        // as required by the spec), but before any side effects.\n        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {\n            log::trace!(\"Ignoring write_texture of size 0\");\n            return Ok(());\n        }\n\n        let mut pending_writes = self.pending_writes.lock();\n        let encoder = pending_writes.activate();\n\n        // If the copy does not fully cover the layers, we need to initialize to\n        // zero *first* as we don't keep track of partial texture layer inits.\n        //\n        // Strictly speaking we only need to clear the areas of a layer\n        // untouched, but this would get increasingly messy.\n        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {\n            // volume textures don't have a layer range as array volumes aren't supported\n            0..1\n        } else {\n            destination.origin.z..destination.origin.z + size.depth_or_array_layers\n        };\n        let mut dst_initialization_status = dst.initialization_status.write();\n        if dst_initialization_status.mips[destination.mip_level as usize]\n            .check(init_layer_range.clone())\n            .is_some()\n        {\n            if has_copy_partial_init_tracker_coverage(size, destination.mip_level, &dst.desc) {\n                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]\n                    .drain(init_layer_range)\n                    .collect::<Vec<core::ops::Range<u32>>>()\n                {\n                    let mut trackers = self.device.trackers.lock();\n                    crate::command::clear_texture(\n                        &dst,\n                        TextureInitRange {\n                            mip_range: destination.mip_level..(destination.mip_level + 1),\n                            layer_range,\n                        },\n                        encoder,\n                        &mut trackers.textures,\n                        &self.device.alignments,\n                        self.device.zero_buffer.as_ref(),\n                        &snatch_guard,\n                        self.device.instance_flags,\n                    )\n                    .map_err(QueueWriteError::from)?;\n                }\n            } else {\n                dst_initialization_status.mips[destination.mip_level as usize]\n                    .drain(init_layer_range);\n            }\n        }\n\n        let (block_width, block_height) = dst.desc.format.block_dimensions();\n        let width_in_blocks = size.width / block_width;\n        let height_in_blocks = size.height / block_height;\n\n        let block_size = dst\n            .desc\n            .format\n            .block_copy_size(Some(destination.aspect))\n            .unwrap();\n        let bytes_in_last_row = width_in_blocks * block_size;\n\n        let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);\n        let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);\n\n        let bytes_per_row_alignment = get_lowest_common_denom(\n            self.device.alignments.buffer_copy_pitch.get() as u32,\n            block_size,\n        );\n        let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);\n\n        // Platform validation requires that the staging buffer always be\n        // freed, even if an error occurs. All paths from here must call\n        // `device.pending_writes.consume`.\n        let staging_buffer = if stage_bytes_per_row == bytes_per_row {\n            profiling::scope!(\"copy aligned\");\n            // Fast path if the data is already being aligned optimally.\n            let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();\n            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;\n            staging_buffer.write(&data[data_layout.offset as usize..]);\n            staging_buffer\n        } else {\n            profiling::scope!(\"copy chunked\");\n            // Copy row by row into the optimal alignment.\n            let block_rows_in_copy =\n                (size.depth_or_array_layers - 1) * rows_per_image + height_in_blocks;\n            let stage_size =\n                wgt::BufferSize::new(stage_bytes_per_row as u64 * block_rows_in_copy as u64)\n                    .unwrap();\n            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;\n            for layer in 0..size.depth_or_array_layers {\n                let rows_offset = layer * rows_per_image;\n                for row in rows_offset..rows_offset + height_in_blocks {\n                    let src_offset = data_layout.offset as u32 + row * bytes_per_row;\n                    let dst_offset = row * stage_bytes_per_row;\n                    unsafe {\n                        staging_buffer.write_with_offset(\n                            data,\n                            src_offset as isize,\n                            dst_offset as isize,\n                            bytes_in_last_row as usize,\n                        )\n                    }\n                }\n            }\n            staging_buffer\n        };\n\n        let staging_buffer = staging_buffer.flush();\n\n        let regions = (0..array_layer_count)\n            .map(|array_layer_offset| {\n                let mut texture_base = dst_base.clone();\n                texture_base.array_layer += array_layer_offset;\n                hal::BufferTextureCopy {\n                    buffer_layout: wgt::TexelCopyBufferLayout {\n                        offset: array_layer_offset as u64\n                            * rows_per_image as u64\n                            * stage_bytes_per_row as u64,\n                        bytes_per_row: Some(stage_bytes_per_row),\n                        rows_per_image: Some(rows_per_image),\n                    },\n                    texture_base,\n                    size: hal_copy_size,\n                }\n            })\n            .collect::<Vec<_>>();\n\n        {\n            let buffer_barrier = hal::BufferBarrier {\n                buffer: staging_buffer.raw(),\n                usage: hal::StateTransition {\n                    from: wgt::BufferUses::MAP_WRITE,\n                    to: wgt::BufferUses::COPY_SRC,\n                },\n            };\n\n            let mut trackers = self.device.trackers.lock();\n            let transition =\n                trackers\n                    .textures\n                    .set_single(&dst, selector, wgt::TextureUses::COPY_DST);\n            let texture_barriers = transition\n                .map(|pending| pending.into_hal(dst_raw))\n                .collect::<Vec<_>>();\n\n            unsafe {\n                encoder.transition_textures(&texture_barriers);\n                encoder.transition_buffers(&[buffer_barrier]);\n                encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, &regions);\n            }\n        }\n\n        pending_writes.consume(staging_buffer);\n        pending_writes.insert_texture(&dst);\n\n        Ok(())\n    }\n\n    #[cfg(webgl)]\n    pub fn copy_external_image_to_texture(\n        &self,\n        source: &wgt::CopyExternalImageSourceInfo,\n        destination: wgt::CopyExternalImageDestInfo<Fallible<Texture>>,\n        size: wgt::Extent3d,\n    ) -> Result<(), QueueWriteError> {\n        use crate::conv;\n\n        profiling::scope!(\"Queue::copy_external_image_to_texture\");\n\n        self.device.check_is_valid()?;\n\n        let mut needs_flag = false;\n        needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));\n        needs_flag |= source.origin != wgt::Origin2d::ZERO;\n        needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;\n        #[allow(clippy::bool_comparison)]\n        if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {\n            needs_flag |= source.flip_y != false;\n            needs_flag |= destination.premultiplied_alpha != false;\n        }\n\n        if needs_flag {\n            self.device\n                .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)\n                .map_err(TransferError::from)?;\n        }\n\n        let src_width = source.source.width();\n        let src_height = source.source.height();\n\n        let dst = destination.texture.get()?;\n        let premultiplied_alpha = destination.premultiplied_alpha;\n        let destination = wgt::TexelCopyTextureInfo {\n            texture: (),\n            mip_level: destination.mip_level,\n            origin: destination.origin,\n            aspect: destination.aspect,\n        };\n\n        if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {\n            return Err(\n                TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),\n            );\n        }\n        if dst.desc.dimension != wgt::TextureDimension::D2 {\n            return Err(TransferError::InvalidDimensionExternal.into());\n        }\n        dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)\n            .map_err(TransferError::MissingTextureUsage)?;\n        if dst.desc.sample_count != 1 {\n            return Err(TransferError::InvalidSampleCount {\n                sample_count: dst.desc.sample_count,\n            }\n            .into());\n        }\n\n        if source.origin.x + size.width > src_width {\n            return Err(TransferError::TextureOverrun {\n                start_offset: source.origin.x,\n                end_offset: source.origin.x + size.width,\n                texture_size: src_width,\n                dimension: crate::resource::TextureErrorDimension::X,\n                side: CopySide::Source,\n            }\n            .into());\n        }\n        if source.origin.y + size.height > src_height {\n            return Err(TransferError::TextureOverrun {\n                start_offset: source.origin.y,\n                end_offset: source.origin.y + size.height,\n                texture_size: src_height,\n                dimension: crate::resource::TextureErrorDimension::Y,\n                side: CopySide::Source,\n            }\n            .into());\n        }\n        if size.depth_or_array_layers != 1 {\n            return Err(TransferError::TextureOverrun {\n                start_offset: 0,\n                end_offset: size.depth_or_array_layers,\n                texture_size: 1,\n                dimension: crate::resource::TextureErrorDimension::Z,\n                side: CopySide::Source,\n            }\n            .into());\n        }\n\n        // Note: Doing the copy range validation early is important because ensures that the\n        // dimensions are not going to cause overflow in other parts of the validation.\n        let (hal_copy_size, _) =\n            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;\n\n        let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;\n\n        // This must happen after parameter validation (so that errors are reported\n        // as required by the spec), but before any side effects.\n        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {\n            log::trace!(\"Ignoring copy_external_image_to_texture of size 0\");\n            return Ok(());\n        }\n\n        let mut pending_writes = self.pending_writes.lock();\n        let encoder = pending_writes.activate();\n\n        // If the copy does not fully cover the layers, we need to initialize to\n        // zero *first* as we don't keep track of partial texture layer inits.\n        //\n        // Strictly speaking we only need to clear the areas of a layer\n        // untouched, but this would get increasingly messy.\n        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {\n            // volume textures don't have a layer range as array volumes aren't supported\n            0..1\n        } else {\n            destination.origin.z..destination.origin.z + size.depth_or_array_layers\n        };\n        let mut dst_initialization_status = dst.initialization_status.write();\n        if dst_initialization_status.mips[destination.mip_level as usize]\n            .check(init_layer_range.clone())\n            .is_some()\n        {\n            if has_copy_partial_init_tracker_coverage(&size, destination.mip_level, &dst.desc) {\n                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]\n                    .drain(init_layer_range)\n                    .collect::<Vec<core::ops::Range<u32>>>()\n                {\n                    let mut trackers = self.device.trackers.lock();\n                    crate::command::clear_texture(\n                        &dst,\n                        TextureInitRange {\n                            mip_range: destination.mip_level..(destination.mip_level + 1),\n                            layer_range,\n                        },\n                        encoder,\n                        &mut trackers.textures,\n                        &self.device.alignments,\n                        self.device.zero_buffer.as_ref(),\n                        &self.device.snatchable_lock.read(),\n                        self.device.instance_flags,\n                    )\n                    .map_err(QueueWriteError::from)?;\n                }\n            } else {\n                dst_initialization_status.mips[destination.mip_level as usize]\n                    .drain(init_layer_range);\n            }\n        }\n\n        let snatch_guard = self.device.snatchable_lock.read();\n        let dst_raw = dst.try_raw(&snatch_guard)?;\n\n        let regions = hal::TextureCopy {\n            src_base: hal::TextureCopyBase {\n                mip_level: 0,\n                array_layer: 0,\n                origin: source.origin.to_3d(0),\n                aspect: hal::FormatAspects::COLOR,\n            },\n            dst_base,\n            size: hal_copy_size,\n        };\n\n        let mut trackers = self.device.trackers.lock();\n        let transitions = trackers\n            .textures\n            .set_single(&dst, selector, wgt::TextureUses::COPY_DST);\n\n        // `copy_external_image_to_texture` is exclusive to the WebGL backend.\n        // Don't go through the `DynCommandEncoder` abstraction and directly to the WebGL backend.\n        let encoder_webgl = encoder\n            .as_any_mut()\n            .downcast_mut::<hal::gles::CommandEncoder>()\n            .unwrap();\n        let dst_raw_webgl = dst_raw\n            .as_any()\n            .downcast_ref::<hal::gles::Texture>()\n            .unwrap();\n        let transitions_webgl = transitions.map(|pending| {\n            let dyn_transition = pending.into_hal(dst_raw);\n            hal::TextureBarrier {\n                texture: dst_raw_webgl,\n                range: dyn_transition.range,\n                usage: dyn_transition.usage,\n            }\n        });\n\n        use hal::CommandEncoder as _;\n        unsafe {\n            encoder_webgl.transition_textures(transitions_webgl);\n            encoder_webgl.copy_external_image_to_texture(\n                source,\n                dst_raw_webgl,\n                premultiplied_alpha,\n                iter::once(regions),\n            );\n        }\n\n        Ok(())\n    }\n\n    #[cfg(feature = \"trace\")]\n    fn trace_submission(\n        &self,\n        submit_index: SubmissionIndex,\n        commands: Vec<crate::command::Command<crate::command::PointerReferences>>,\n    ) {\n        if let Some(ref mut trace) = *self.device.trace.lock() {\n            trace.add(Action::Submit(submit_index, commands));\n        }\n    }\n\n    #[cfg(feature = \"trace\")]\n    fn trace_failed_submission(\n        &self,\n        submit_index: SubmissionIndex,\n        commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,\n        error: alloc::string::String,\n    ) {\n        if let Some(ref mut trace) = *self.device.trace.lock() {\n            trace.add(Action::FailedCommands {\n                commands,\n                failed_at_submit: Some(submit_index),\n                error,\n            });\n        }\n    }\n\n    pub fn submit(\n        &self,\n        command_buffers: &[Arc<CommandBuffer>],\n    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {\n        profiling::scope!(\"Queue::submit\");\n        api_log!(\"Queue::submit\");\n\n        let submit_index;\n\n        let res = 'error: {\n            let snatch_guard = self.device.snatchable_lock.read();\n\n            // Fence lock must be acquired after the snatch lock everywhere to avoid deadlocks.\n            let mut fence = self.device.fence.write();\n\n            let mut command_index_guard = self.device.command_indices.write();\n            command_index_guard.active_submission_index += 1;\n            submit_index = command_index_guard.active_submission_index;\n\n            if let Err(e) = self.device.check_is_valid() {\n                break 'error Err(e.into());\n            }\n\n            let mut active_executions = Vec::new();\n\n            let mut used_surface_textures = track::TextureUsageScope::default();\n\n            // Use a hashmap here to deduplicate the surface textures that are used in the command buffers.\n            // This avoids vulkan deadlocking from the same surface texture being submitted multiple times.\n            let mut submit_surface_textures_owned = FastHashMap::default();\n\n            {\n                if !command_buffers.is_empty() {\n                    profiling::scope!(\"prepare\");\n\n                    let mut first_error = None;\n\n                    //TODO: if multiple command buffers are submitted, we can re-use the last\n                    // native command buffer of the previous chain instead of always creating\n                    // a temporary one, since the chains are not finished.\n\n                    // finish all the command buffers first\n                    for command_buffer in command_buffers {\n                        profiling::scope!(\"process command buffer\");\n\n                        // we reset the used surface textures every time we use\n                        // it, so make sure to set_size on it.\n                        used_surface_textures.set_size(self.device.tracker_indices.textures.size());\n\n                        // Note that we are required to invalidate all command buffers in both the success and failure paths.\n                        // This is why we `continue` and don't early return via `?`.\n                        #[allow(unused_mut)]\n                        let mut cmd_buf_data = command_buffer.take_finished();\n\n                        if first_error.is_some() {\n                            continue;\n                        }\n\n                        #[cfg(feature = \"trace\")]\n                        let trace_commands = cmd_buf_data\n                            .as_mut()\n                            .ok()\n                            .and_then(|data| mem::take(&mut data.trace_commands));\n\n                        let mut baked = match cmd_buf_data {\n                            Ok(cmd_buf_data) => {\n                                let res = validate_command_buffer(\n                                    command_buffer,\n                                    self,\n                                    &cmd_buf_data,\n                                    &snatch_guard,\n                                    &mut submit_surface_textures_owned,\n                                    &mut used_surface_textures,\n                                    &mut command_index_guard,\n                                );\n                                if let Err(err) = res {\n                                    #[cfg(feature = \"trace\")]\n                                    self.trace_failed_submission(\n                                        submit_index,\n                                        trace_commands,\n                                        err.to_string(),\n                                    );\n                                    first_error.get_or_insert(err);\n                                    continue;\n                                }\n\n                                #[cfg(feature = \"trace\")]\n                                if let Some(commands) = trace_commands {\n                                    self.trace_submission(submit_index, commands);\n                                }\n\n                                cmd_buf_data.set_acceleration_structure_dependencies(&snatch_guard);\n                                cmd_buf_data.into_baked_commands()\n                            }\n                            Err(err) => {\n                                #[cfg(feature = \"trace\")]\n                                self.trace_failed_submission(\n                                    submit_index,\n                                    trace_commands,\n                                    err.to_string(),\n                                );\n                                first_error.get_or_insert(err.into());\n                                continue;\n                            }\n                        };\n\n                        // execute resource transitions\n                        if let Err(e) = baked.encoder.open_pass(hal_label(\n                            Some(\"(wgpu internal) Transit\"),\n                            self.device.instance_flags,\n                        )) {\n                            break 'error Err(e.into());\n                        }\n\n                        //Note: locking the trackers has to be done after the storages\n                        let mut trackers = self.device.trackers.lock();\n                        if let Err(e) = baked.initialize_buffer_memory(&mut trackers, &snatch_guard)\n                        {\n                            break 'error Err(e.into());\n                        }\n                        if let Err(e) = baked.initialize_texture_memory(\n                            &mut trackers,\n                            &self.device,\n                            &snatch_guard,\n                        ) {\n                            break 'error Err(e.into());\n                        }\n\n                        //Note: stateless trackers are not merged:\n                        // device already knows these resources exist.\n                        CommandEncoder::insert_barriers_from_device_tracker(\n                            baked.encoder.raw.as_mut(),\n                            &mut trackers,\n                            &baked.trackers,\n                            &snatch_guard,\n                        );\n\n                        if let Err(e) = baked.encoder.close_and_push_front() {\n                            break 'error Err(e.into());\n                        }\n\n                        // Transition surface textures into `Present` state.\n                        // Note: we could technically do it after all of the command buffers,\n                        // but here we have a command encoder by hand, so it's easier to use it.\n                        if !used_surface_textures.is_empty() {\n                            if let Err(e) = baked.encoder.open_pass(hal_label(\n                                Some(\"(wgpu internal) Present\"),\n                                self.device.instance_flags,\n                            )) {\n                                break 'error Err(e.into());\n                            }\n                            let texture_barriers = trackers\n                                .textures\n                                .set_from_usage_scope_and_drain_transitions(\n                                    &used_surface_textures,\n                                    &snatch_guard,\n                                )\n                                .collect::<Vec<_>>();\n                            unsafe {\n                                baked.encoder.raw.transition_textures(&texture_barriers);\n                            };\n                            if let Err(e) = baked.encoder.close() {\n                                break 'error Err(e.into());\n                            }\n                            used_surface_textures = track::TextureUsageScope::default();\n                        }\n\n                        // done\n                        active_executions.push(EncoderInFlight {\n                            inner: baked.encoder,\n                            trackers: baked.trackers,\n                            temp_resources: baked.temp_resources,\n                            _indirect_draw_validation_resources: baked\n                                .indirect_draw_validation_resources,\n                            pending_buffers: FastHashMap::default(),\n                            pending_textures: FastHashMap::default(),\n                            pending_blas_s: FastHashMap::default(),\n                        });\n                    }\n\n                    if let Some(first_error) = first_error {\n                        break 'error Err(first_error);\n                    }\n                }\n            }\n\n            let mut pending_writes = self.pending_writes.lock();\n\n            {\n                used_surface_textures.set_size(self.device.tracker_indices.textures.size());\n                for texture in pending_writes.dst_textures.values() {\n                    match texture.try_inner(&snatch_guard) {\n                        Ok(TextureInner::Native { .. }) => {}\n                        Ok(TextureInner::Surface { .. }) => {\n                            // Compare the Arcs by pointer as Textures don't implement Eq\n                            submit_surface_textures_owned\n                                .insert(Arc::as_ptr(texture), texture.clone());\n\n                            unsafe {\n                                used_surface_textures\n                                    .merge_single(texture, None, wgt::TextureUses::PRESENT)\n                                    .unwrap()\n                            };\n                        }\n                        // The texture must not have been destroyed when its usage here was\n                        // encoded. If it was destroyed after that, then it was transferred\n                        // to `pending_writes.temp_resources` at the time of destruction, so\n                        // we are still okay to use it.\n                        Err(DestroyedResourceError(_)) => {}\n                    }\n                }\n\n                if !used_surface_textures.is_empty() {\n                    let mut trackers = self.device.trackers.lock();\n\n                    let texture_barriers = trackers\n                        .textures\n                        .set_from_usage_scope_and_drain_transitions(\n                            &used_surface_textures,\n                            &snatch_guard,\n                        )\n                        .collect::<Vec<_>>();\n                    unsafe {\n                        pending_writes\n                            .command_encoder\n                            .transition_textures(&texture_barriers);\n                    };\n                }\n            }\n\n            match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {\n                Ok(Some(pending_execution)) => {\n                    active_executions.insert(0, pending_execution);\n                }\n                Ok(None) => {}\n                Err(e) => break 'error Err(e.into()),\n            }\n            let hal_command_buffers = active_executions\n                .iter()\n                .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))\n                .collect::<Vec<_>>();\n\n            {\n                let mut submit_surface_textures =\n                    SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(\n                        submit_surface_textures_owned.len(),\n                    );\n\n                for texture in submit_surface_textures_owned.values() {\n                    let raw = match texture.inner.get(&snatch_guard) {\n                        Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),\n                        _ => unreachable!(),\n                    };\n                    submit_surface_textures.push(raw);\n                }\n\n                if let Err(e) = unsafe {\n                    self.raw().submit(\n                        &hal_command_buffers,\n                        &submit_surface_textures,\n                        (fence.as_mut(), submit_index),\n                    )\n                }\n                .map_err(|e| self.device.handle_hal_error(e))\n                {\n                    break 'error Err(e.into());\n                }\n\n                drop(command_index_guard);\n\n                // Advance the successful submission index.\n                self.device\n                    .last_successful_submission_index\n                    .fetch_max(submit_index, Ordering::SeqCst);\n            }\n\n            profiling::scope!(\"cleanup\");\n\n            // this will register the new submission to the life time tracker\n            self.lock_life()\n                .track_submission(submit_index, active_executions);\n            drop(pending_writes);\n\n            // This will schedule destruction of all resources that are no longer needed\n            // by the user but used in the command stream, among other things.\n            let fence_guard = RwLockWriteGuard::downgrade(fence);\n            let (closures, result) =\n                self.device\n                    .maintain(fence_guard, wgt::PollType::Poll, snatch_guard);\n            match result {\n                Ok(status) => {\n                    debug_assert!(matches!(\n                        status,\n                        wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll\n                    ));\n                }\n                Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),\n                Err(WaitIdleError::WrongSubmissionIndex(..)) => {\n                    unreachable!(\"Cannot get WrongSubmissionIndex from Poll\")\n                }\n                Err(WaitIdleError::Timeout) => unreachable!(\"Cannot get Timeout from Poll\"),\n            };\n\n            Ok(closures)\n        };\n\n        let callbacks = match res {\n            Ok(ok) => ok,\n            Err(e) => return Err((submit_index, e)),\n        };\n\n        // the closures should execute with nothing locked!\n        callbacks.fire();\n\n        self.device.lose_if_oom();\n\n        api_log!(\"Queue::submit returned submit index {submit_index}\");\n\n        Ok(submit_index)\n    }\n\n    pub fn get_timestamp_period(&self) -> f32 {\n        unsafe { self.raw().get_timestamp_period() }\n    }\n\n    /// `closure` is guaranteed to be called.\n    pub fn on_submitted_work_done(\n        &self,\n        closure: SubmittedWorkDoneClosure,\n    ) -> Option<SubmissionIndex> {\n        api_log!(\"Queue::on_submitted_work_done\");\n        //TODO: flush pending writes\n        self.lock_life().add_work_done_closure(closure)\n    }\n\n    pub fn compact_blas(&self, blas: &Arc<Blas>) -> Result<Arc<Blas>, CompactBlasError> {\n        profiling::scope!(\"Queue::compact_blas\");\n        api_log!(\"Queue::compact_blas\");\n\n        let new_label = blas.label.clone() + \" (compacted)\";\n\n        self.device.check_is_valid()?;\n        self.same_device_as(blas.as_ref())?;\n\n        let device = blas.device.clone();\n\n        let snatch_guard = device.snatchable_lock.read();\n\n        let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {\n            return Err(CompactBlasError::BlasNotReady);\n        };\n\n        let mut size_info = blas.size_info;\n        size_info.acceleration_structure_size = size;\n\n        let mut pending_writes = self.pending_writes.lock();\n        let cmd_buf_raw = pending_writes.activate();\n\n        let raw = unsafe {\n            device\n                .raw()\n                .create_acceleration_structure(&hal::AccelerationStructureDescriptor {\n                    label: hal_label(Some(&new_label), device.instance_flags),\n                    size: size_info.acceleration_structure_size,\n                    format: hal::AccelerationStructureFormat::BottomLevel,\n                    allow_compaction: false,\n                })\n        }\n        .map_err(DeviceError::from_hal)?;\n\n        let src_raw = blas.try_raw(&snatch_guard)?;\n\n        unsafe {\n            cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(\n                src_raw,\n                raw.as_ref(),\n                wgt::AccelerationStructureCopy::Compact,\n            )\n        };\n\n        let handle = unsafe {\n            device\n                .raw()\n                .get_acceleration_structure_device_address(raw.as_ref())\n        };\n\n        drop(snatch_guard);\n\n        let mut command_indices_lock = device.command_indices.write();\n        command_indices_lock.next_acceleration_structure_build_command_index += 1;\n        let built_index =\n            NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)\n                .unwrap();\n\n        let new_blas = Arc::new(Blas {\n            raw: Snatchable::new(raw),\n            device: device.clone(),\n            size_info,\n            sizes: blas.sizes.clone(),\n            flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,\n            update_mode: blas.update_mode,\n            // Bypass the submit checks which update this because we don't submit this normally.\n            built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),\n            handle,\n            label: new_label,\n            tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),\n            compaction_buffer: None,\n            compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),\n        });\n\n        pending_writes.insert_blas(blas);\n        pending_writes.insert_blas(&new_blas);\n\n        Ok(new_blas)\n    }\n}\n\nimpl Global {\n    pub fn queue_write_buffer(\n        &self,\n        queue_id: QueueId,\n        buffer_id: id::BufferId,\n        buffer_offset: wgt::BufferAddress,\n        data: &[u8],\n    ) -> Result<(), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let buffer = self.hub.buffers.get(buffer_id).get()?;\n\n        #[cfg(feature = \"trace\")]\n        if let Some(ref mut trace) = *queue.device.trace.lock() {\n            use crate::device::trace::DataKind;\n            let size = data.len() as u64;\n            let data = trace.make_binary(DataKind::Bin, data);\n            trace.add(Action::WriteBuffer {\n                id: buffer.to_trace(),\n                data,\n                offset: buffer_offset,\n                size,\n                queued: true,\n            });\n        }\n\n        queue.write_buffer(buffer, buffer_offset, data)\n    }\n\n    pub fn queue_create_staging_buffer(\n        &self,\n        queue_id: QueueId,\n        buffer_size: wgt::BufferSize,\n        id_in: Option<id::StagingBufferId>,\n    ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;\n\n        let fid = self.hub.staging_buffers.prepare(id_in);\n        let id = fid.assign(staging_buffer);\n\n        Ok((id, ptr))\n    }\n\n    pub fn queue_write_staging_buffer(\n        &self,\n        queue_id: QueueId,\n        buffer_id: id::BufferId,\n        buffer_offset: wgt::BufferAddress,\n        staging_buffer_id: id::StagingBufferId,\n    ) -> Result<(), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let buffer = self.hub.buffers.get(buffer_id);\n        let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);\n        queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)\n    }\n\n    pub fn queue_validate_write_buffer(\n        &self,\n        queue_id: QueueId,\n        buffer_id: id::BufferId,\n        buffer_offset: u64,\n        buffer_size: wgt::BufferSize,\n    ) -> Result<(), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let buffer = self.hub.buffers.get(buffer_id);\n        queue.validate_write_buffer(buffer, buffer_offset, buffer_size)\n    }\n\n    pub fn queue_write_texture(\n        &self,\n        queue_id: QueueId,\n        destination: &wgt::TexelCopyTextureInfo<id::TextureId>,\n        data: &[u8],\n        data_layout: &wgt::TexelCopyBufferLayout,\n        size: &wgt::Extent3d,\n    ) -> Result<(), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let texture = self.hub.textures.get(destination.texture).get()?;\n        let destination = wgt::TexelCopyTextureInfo {\n            texture,\n            mip_level: destination.mip_level,\n            origin: destination.origin,\n            aspect: destination.aspect,\n        };\n\n        #[cfg(feature = \"trace\")]\n        if let Some(ref mut trace) = *queue.device.trace.lock() {\n            use crate::device::trace::DataKind;\n            let data = trace.make_binary(DataKind::Bin, data);\n            trace.add(Action::WriteTexture {\n                to: destination.to_trace(),\n                data,\n                layout: *data_layout,\n                size: *size,\n            });\n        }\n\n        queue.write_texture(destination, data, data_layout, size)\n    }\n\n    #[cfg(webgl)]\n    pub fn queue_copy_external_image_to_texture(\n        &self,\n        queue_id: QueueId,\n        source: &wgt::CopyExternalImageSourceInfo,\n        destination: crate::command::CopyExternalImageDestInfo,\n        size: wgt::Extent3d,\n    ) -> Result<(), QueueWriteError> {\n        let queue = self.hub.queues.get(queue_id);\n        let destination = wgt::CopyExternalImageDestInfo {\n            texture: self.hub.textures.get(destination.texture),\n            mip_level: destination.mip_level,\n            origin: destination.origin,\n            aspect: destination.aspect,\n            color_space: destination.color_space,\n            premultiplied_alpha: destination.premultiplied_alpha,\n        };\n        queue.copy_external_image_to_texture(source, destination, size)\n    }\n\n    pub fn queue_submit(\n        &self,\n        queue_id: QueueId,\n        command_buffer_ids: &[id::CommandBufferId],\n    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {\n        let queue = self.hub.queues.get(queue_id);\n        let command_buffer_guard = self.hub.command_buffers.read();\n        let command_buffers = command_buffer_ids\n            .iter()\n            .map(|id| command_buffer_guard.get(*id))\n            .collect::<Vec<_>>();\n        drop(command_buffer_guard);\n        queue.submit(&command_buffers)\n    }\n\n    pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {\n        let queue = self.hub.queues.get(queue_id);\n\n        if queue.device.timestamp_normalizer.get().unwrap().enabled() {\n            return 1.0;\n        }\n\n        queue.get_timestamp_period()\n    }\n\n    pub fn queue_on_submitted_work_done(\n        &self,\n        queue_id: QueueId,\n        closure: SubmittedWorkDoneClosure,\n    ) -> SubmissionIndex {\n        api_log!(\"Queue::on_submitted_work_done {queue_id:?}\");\n\n        //TODO: flush pending writes\n        let queue = self.hub.queues.get(queue_id);\n        let result = queue.on_submitted_work_done(closure);\n        result.unwrap_or(0) // '0' means no wait is necessary\n    }\n\n    pub fn queue_compact_blas(\n        &self,\n        queue_id: QueueId,\n        blas_id: BlasId,\n        id_in: Option<BlasId>,\n    ) -> (BlasId, Option<u64>, Option<CompactBlasError>) {\n        api_log!(\"Queue::compact_blas {queue_id:?}, {blas_id:?}\");\n\n        let fid = self.hub.blas_s.prepare(id_in);\n\n        let queue = self.hub.queues.get(queue_id);\n        let blas = self.hub.blas_s.get(blas_id);\n        let device = &queue.device;\n\n        // TODO: Tracing\n\n        let error = 'error: {\n            match device.require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY) {\n                Ok(_) => {}\n                Err(err) => break 'error err.into(),\n            }\n\n            let blas = match blas.get() {\n                Ok(blas) => blas,\n                Err(err) => break 'error err.into(),\n            };\n\n            let new_blas = match queue.compact_blas(&blas) {\n                Ok(blas) => blas,\n                Err(err) => break 'error err,\n            };\n\n            // We should have no more errors after this because we have marked the command encoder as successful.\n            let old_blas_size = blas.size_info.acceleration_structure_size;\n            let new_blas_size = new_blas.size_info.acceleration_structure_size;\n            let handle = new_blas.handle;\n\n            let id = fid.assign(Fallible::Valid(new_blas));\n\n            api_log!(\"CommandEncoder::compact_blas {blas_id:?} (size: {old_blas_size}) -> {id:?} (size: {new_blas_size})\");\n\n            return (id, Some(handle), None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(error.to_string())));\n\n        (id, None, Some(error))\n    }\n}\n\nfn validate_command_buffer(\n    command_buffer: &CommandBuffer,\n    queue: &Queue,\n    cmd_buf_data: &crate::command::CommandBufferMutable,\n    snatch_guard: &SnatchGuard,\n    submit_surface_textures_owned: &mut FastHashMap<*const Texture, Arc<Texture>>,\n    used_surface_textures: &mut track::TextureUsageScope,\n    command_index_guard: &mut RwLockWriteGuard<CommandIndices>,\n) -> Result<(), QueueSubmitError> {\n    command_buffer.same_device_as(queue)?;\n\n    {\n        profiling::scope!(\"check resource state\");\n\n        {\n            profiling::scope!(\"buffers\");\n            for buffer in cmd_buf_data.trackers.buffers.used_resources() {\n                buffer.check_destroyed(snatch_guard)?;\n\n                match *buffer.map_state.lock() {\n                    BufferMapState::Idle => (),\n                    _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),\n                }\n            }\n        }\n        {\n            profiling::scope!(\"textures\");\n            for texture in cmd_buf_data.trackers.textures.used_resources() {\n                let should_extend = match texture.try_inner(snatch_guard)? {\n                    TextureInner::Native { .. } => false,\n                    TextureInner::Surface { .. } => {\n                        // Compare the Arcs by pointer as Textures don't implement Eq.\n                        submit_surface_textures_owned.insert(Arc::as_ptr(texture), texture.clone());\n\n                        true\n                    }\n                };\n                if should_extend {\n                    unsafe {\n                        used_surface_textures\n                            .merge_single(texture, None, wgt::TextureUses::PRESENT)\n                            .unwrap();\n                    };\n                }\n            }\n        }\n        // WebGPU requires that we check every bind group referenced during\n        // encoding, even ones that may have been replaced before being used.\n        // TODO(<https://github.com/gfx-rs/wgpu/issues/8510>): Optimize this.\n        {\n            profiling::scope!(\"bind groups\");\n            for bind_group in &cmd_buf_data.trackers.bind_groups {\n                // This checks the bind group and all resources it references.\n                bind_group.try_raw(snatch_guard)?;\n            }\n        }\n\n        if let Err(e) =\n            cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)\n        {\n            return Err(e.into());\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "wgpu-core/src/device/ray_tracing.rs",
    "content": "use alloc::{string::ToString as _, sync::Arc, vec::Vec};\nuse core::mem::{size_of, ManuallyDrop};\n\n#[cfg(feature = \"trace\")]\nuse crate::device::trace::{Action, IntoTrace};\nuse crate::device::DeviceError;\nuse crate::{\n    api_log,\n    device::Device,\n    global::Global,\n    hal_label,\n    id::{self, BlasId, TlasId},\n    lock::RwLock,\n    lock::{rank, Mutex},\n    ray_tracing::BlasPrepareCompactError,\n    ray_tracing::{CreateBlasError, CreateTlasError},\n    resource,\n    resource::{\n        BlasCompactCallback, BlasCompactState, Fallible, InvalidResourceError, TrackingData,\n    },\n    snatch::Snatchable,\n    LabelHelpers,\n};\nuse hal::AccelerationStructureTriangleIndices;\nuse wgt::Features;\n\nimpl Device {\n    pub fn create_blas(\n        self: &Arc<Self>,\n        blas_desc: &resource::BlasDescriptor,\n        sizes: wgt::BlasGeometrySizeDescriptors,\n    ) -> Result<Arc<resource::Blas>, CreateBlasError> {\n        self.check_is_valid()?;\n        self.require_features(Features::EXPERIMENTAL_RAY_QUERY)?;\n\n        if blas_desc\n            .flags\n            .contains(wgt::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN)\n        {\n            self.require_features(Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)?;\n        }\n\n        let size_info = match &sizes {\n            wgt::BlasGeometrySizeDescriptors::Triangles { descriptors } => {\n                if descriptors.len() as u32 > self.limits.max_blas_geometry_count {\n                    return Err(CreateBlasError::TooManyGeometries(\n                        self.limits.max_blas_geometry_count,\n                        descriptors.len() as u32,\n                    ));\n                }\n\n                let mut entries =\n                    Vec::<hal::AccelerationStructureTriangles<dyn hal::DynBuffer>>::with_capacity(\n                        descriptors.len(),\n                    );\n                for desc in descriptors {\n                    if desc.index_count.is_some() != desc.index_format.is_some() {\n                        return Err(CreateBlasError::MissingIndexData);\n                    }\n                    let indices =\n                        desc.index_count\n                            .map(|count| AccelerationStructureTriangleIndices::<\n                                dyn hal::DynBuffer,\n                            > {\n                                format: desc.index_format.unwrap(),\n                                buffer: Some(self.zero_buffer.as_ref()),\n                                offset: 0,\n                                count,\n                            });\n                    if !self\n                        .features\n                        .allowed_vertex_formats_for_blas()\n                        .contains(&desc.vertex_format)\n                    {\n                        return Err(CreateBlasError::InvalidVertexFormat(\n                            desc.vertex_format,\n                            self.features.allowed_vertex_formats_for_blas(),\n                        ));\n                    }\n\n                    let mut transform = None;\n\n                    if blas_desc\n                        .flags\n                        .contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)\n                    {\n                        transform = Some(wgpu_hal::AccelerationStructureTriangleTransform {\n                            buffer: self.zero_buffer.as_ref(),\n                            offset: 0,\n                        })\n                    }\n\n                    if desc.vertex_count > self.limits.max_blas_primitive_count {\n                        return Err(CreateBlasError::TooManyPrimitives(\n                            self.limits.max_blas_primitive_count,\n                            desc.vertex_count,\n                        ));\n                    }\n\n                    entries.push(hal::AccelerationStructureTriangles::<dyn hal::DynBuffer> {\n                        vertex_buffer: Some(self.zero_buffer.as_ref()),\n                        vertex_format: desc.vertex_format,\n                        first_vertex: 0,\n                        vertex_count: desc.vertex_count,\n                        vertex_stride: 0,\n                        indices,\n                        transform,\n                        flags: desc.flags,\n                    });\n                }\n                unsafe {\n                    self.raw().get_acceleration_structure_build_sizes(\n                        &hal::GetAccelerationStructureBuildSizesDescriptor {\n                            entries: &hal::AccelerationStructureEntries::Triangles(entries),\n                            flags: blas_desc.flags,\n                        },\n                    )\n                }\n            }\n        };\n\n        let raw = unsafe {\n            self.raw()\n                .create_acceleration_structure(&hal::AccelerationStructureDescriptor {\n                    label: blas_desc.label.as_deref(),\n                    size: size_info.acceleration_structure_size,\n                    format: hal::AccelerationStructureFormat::BottomLevel,\n                    allow_compaction: blas_desc\n                        .flags\n                        .contains(wgpu_types::AccelerationStructureFlags::ALLOW_COMPACTION),\n                })\n        }\n        .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let compaction_buffer = if blas_desc\n            .flags\n            .contains(wgpu_types::AccelerationStructureFlags::ALLOW_COMPACTION)\n        {\n            Some(ManuallyDrop::new(unsafe {\n                self.raw()\n                    .create_buffer(&hal::BufferDescriptor {\n                        label: Some(\"(wgpu internal) compaction read-back buffer\"),\n                        size: size_of::<wgpu_types::BufferAddress>() as wgpu_types::BufferAddress,\n                        usage: wgpu_types::BufferUses::ACCELERATION_STRUCTURE_QUERY\n                            | wgpu_types::BufferUses::MAP_READ,\n                        memory_flags: hal::MemoryFlags::PREFER_COHERENT,\n                    })\n                    .map_err(DeviceError::from_hal)?\n            }))\n        } else {\n            None\n        };\n\n        let handle = unsafe {\n            self.raw()\n                .get_acceleration_structure_device_address(raw.as_ref())\n        };\n\n        Ok(Arc::new(resource::Blas {\n            raw: Snatchable::new(raw),\n            device: self.clone(),\n            size_info,\n            sizes,\n            flags: blas_desc.flags,\n            update_mode: blas_desc.update_mode,\n            handle,\n            label: blas_desc.label.to_string(),\n            built_index: RwLock::new(rank::BLAS_BUILT_INDEX, None),\n            tracking_data: TrackingData::new(self.tracker_indices.blas_s.clone()),\n            compaction_buffer,\n            compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Idle),\n        }))\n    }\n\n    pub fn create_tlas(\n        self: &Arc<Self>,\n        desc: &resource::TlasDescriptor,\n    ) -> Result<Arc<resource::Tlas>, CreateTlasError> {\n        self.check_is_valid()?;\n        self.require_features(Features::EXPERIMENTAL_RAY_QUERY)?;\n\n        if desc.max_instances > self.limits.max_tlas_instance_count {\n            return Err(CreateTlasError::TooManyInstances(\n                self.limits.max_tlas_instance_count,\n                desc.max_instances,\n            ));\n        }\n\n        if desc\n            .flags\n            .contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)\n        {\n            return Err(CreateTlasError::DisallowedFlag(\n                wgt::AccelerationStructureFlags::USE_TRANSFORM,\n            ));\n        }\n\n        if desc\n            .flags\n            .contains(wgt::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN)\n        {\n            self.require_features(Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)?;\n        }\n\n        let size_info = unsafe {\n            self.raw().get_acceleration_structure_build_sizes(\n                &hal::GetAccelerationStructureBuildSizesDescriptor {\n                    entries: &hal::AccelerationStructureEntries::Instances(\n                        hal::AccelerationStructureInstances {\n                            buffer: Some(self.zero_buffer.as_ref()),\n                            offset: 0,\n                            count: desc.max_instances,\n                        },\n                    ),\n                    flags: desc.flags,\n                },\n            )\n        };\n\n        let raw = unsafe {\n            self.raw()\n                .create_acceleration_structure(&hal::AccelerationStructureDescriptor {\n                    label: desc.label.as_deref(),\n                    size: size_info.acceleration_structure_size,\n                    format: hal::AccelerationStructureFormat::TopLevel,\n                    allow_compaction: false,\n                })\n        }\n        .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let instance_buffer_size =\n            self.alignments.raw_tlas_instance_size * desc.max_instances.max(1) as usize;\n        let instance_buffer = unsafe {\n            self.raw().create_buffer(&hal::BufferDescriptor {\n                label: hal_label(Some(\"(wgpu-core) instances_buffer\"), self.instance_flags),\n                size: instance_buffer_size as u64,\n                usage: wgt::BufferUses::COPY_DST\n                    | wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,\n                memory_flags: hal::MemoryFlags::PREFER_COHERENT,\n            })\n        }\n        .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        Ok(Arc::new(resource::Tlas {\n            raw: Snatchable::new(raw),\n            device: self.clone(),\n            size_info,\n            flags: desc.flags,\n            update_mode: desc.update_mode,\n            built_index: RwLock::new(rank::TLAS_BUILT_INDEX, None),\n            dependencies: RwLock::new(rank::TLAS_DEPENDENCIES, Vec::new()),\n            instance_buffer: ManuallyDrop::new(instance_buffer),\n            label: desc.label.to_string(),\n            max_instance_count: desc.max_instances,\n            tracking_data: TrackingData::new(self.tracker_indices.tlas_s.clone()),\n        }))\n    }\n}\n\nimpl Global {\n    pub fn device_create_blas(\n        &self,\n        device_id: id::DeviceId,\n        desc: &resource::BlasDescriptor,\n        sizes: wgt::BlasGeometrySizeDescriptors,\n        id_in: Option<BlasId>,\n    ) -> (BlasId, Option<u64>, Option<CreateBlasError>) {\n        profiling::scope!(\"Device::create_blas\");\n\n        let fid = self.hub.blas_s.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            #[cfg(feature = \"trace\")]\n            let trace_sizes = sizes.clone();\n\n            let blas = match device.create_blas(desc, sizes) {\n                Ok(blas) => blas,\n                Err(e) => break 'error e,\n            };\n            let handle = blas.handle;\n\n            #[cfg(feature = \"trace\")]\n            if let Some(trace) = device.trace.lock().as_mut() {\n                trace.add(Action::CreateBlas {\n                    id: blas.to_trace(),\n                    desc: desc.clone(),\n                    sizes: trace_sizes,\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(blas));\n            api_log!(\"Device::create_blas -> {id:?}\");\n\n            return (id, Some(handle), None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(error.to_string())));\n        (id, None, Some(error))\n    }\n\n    pub fn device_create_tlas(\n        &self,\n        device_id: id::DeviceId,\n        desc: &resource::TlasDescriptor,\n        id_in: Option<TlasId>,\n    ) -> (TlasId, Option<CreateTlasError>) {\n        profiling::scope!(\"Device::create_tlas\");\n\n        let fid = self.hub.tlas_s.prepare(id_in);\n\n        let error = 'error: {\n            let device = self.hub.devices.get(device_id);\n\n            let tlas = match device.create_tlas(desc) {\n                Ok(tlas) => tlas,\n                Err(e) => break 'error e,\n            };\n\n            #[cfg(feature = \"trace\")]\n            if let Some(trace) = device.trace.lock().as_mut() {\n                trace.add(Action::CreateTlas {\n                    id: tlas.to_trace(),\n                    desc: desc.clone(),\n                });\n            }\n\n            let id = fid.assign(Fallible::Valid(tlas));\n            api_log!(\"Device::create_tlas -> {id:?}\");\n\n            return (id, None);\n        };\n\n        let id = fid.assign(Fallible::Invalid(Arc::new(error.to_string())));\n        (id, Some(error))\n    }\n\n    pub fn blas_drop(&self, blas_id: BlasId) {\n        profiling::scope!(\"Blas::drop\");\n        api_log!(\"Blas::drop {blas_id:?}\");\n\n        let _blas = self.hub.blas_s.remove(blas_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(blas) = _blas.get() {\n            if let Some(t) = blas.device.trace.lock().as_mut() {\n                t.add(Action::DestroyBlas(blas.to_trace()));\n            }\n        }\n    }\n\n    pub fn tlas_drop(&self, tlas_id: TlasId) {\n        profiling::scope!(\"Tlas::drop\");\n        api_log!(\"Tlas::drop {tlas_id:?}\");\n\n        let _tlas = self.hub.tlas_s.remove(tlas_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Ok(tlas) = _tlas.get() {\n            if let Some(t) = tlas.device.trace.lock().as_mut() {\n                t.add(Action::DestroyTlas(tlas.to_trace()));\n            }\n        }\n    }\n\n    pub fn blas_prepare_compact_async(\n        &self,\n        blas_id: BlasId,\n        callback: Option<BlasCompactCallback>,\n    ) -> Result<crate::SubmissionIndex, BlasPrepareCompactError> {\n        profiling::scope!(\"Blas::prepare_compact_async\");\n        api_log!(\"Blas::prepare_compact_async {blas_id:?}\");\n\n        let hub = &self.hub;\n\n        let compact_result = match hub.blas_s.get(blas_id).get() {\n            Ok(blas) => blas.prepare_compact_async(callback),\n            Err(e) => Err((callback, e.into())),\n        };\n\n        match compact_result {\n            Ok(submission_index) => Ok(submission_index),\n            Err((mut callback, err)) => {\n                if let Some(callback) = callback.take() {\n                    callback(Err(err.clone()));\n                }\n                Err(err)\n            }\n        }\n    }\n\n    pub fn ready_for_compaction(&self, blas_id: BlasId) -> Result<bool, InvalidResourceError> {\n        profiling::scope!(\"Blas::prepare_compact_async\");\n        api_log!(\"Blas::prepare_compact_async {blas_id:?}\");\n\n        let hub = &self.hub;\n\n        let blas = hub.blas_s.get(blas_id).get()?;\n\n        let lock = blas.compacted_state.lock();\n\n        Ok(matches!(*lock, BlasCompactState::Ready { .. }))\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/resource.rs",
    "content": "use alloc::{\n    borrow::Cow,\n    boxed::Box,\n    string::{String, ToString as _},\n    sync::{Arc, Weak},\n    vec::Vec,\n};\nuse core::{\n    fmt,\n    mem::{self, ManuallyDrop},\n    num::NonZeroU32,\n    sync::atomic::{AtomicBool, Ordering},\n};\nuse hal::ShouldBeNonZeroExt;\n\nuse arrayvec::ArrayVec;\nuse bitflags::Flags;\nuse smallvec::SmallVec;\nuse wgt::{\n    math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureSelector,\n    TextureViewDimension,\n};\n\n#[cfg(feature = \"trace\")]\nuse crate::device::trace;\nuse crate::{\n    api_log,\n    binding_model::{\n        self, BindGroup, BindGroupLateBufferBindingInfo, BindGroupLayout,\n        BindGroupLayoutEntryError, CreateBindGroupError, CreateBindGroupLayoutError,\n    },\n    command, conv,\n    device::{\n        bgl, create_validator, features_to_naga_capabilities, life::WaitIdleError, map_buffer,\n        AttachmentData, DeviceLostInvocation, HostMap, MissingDownlevelFlags, MissingFeatures,\n        RenderPassContext,\n    },\n    hal_label,\n    init_tracker::{\n        BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,\n        TextureInitTrackerAction,\n    },\n    instance::{Adapter, RequestDeviceError},\n    lock::{rank, Mutex, RwLock},\n    pipeline::{self, ColorStateError},\n    pool::ResourcePool,\n    present,\n    resource::{\n        self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet,\n        RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView,\n        TextureViewNotRenderableReason, Tlas, TrackingData,\n    },\n    resource_log,\n    snatch::{SnatchGuard, SnatchLock, Snatchable},\n    timestamp_normalization::TIMESTAMP_NORMALIZATION_BUFFER_USES,\n    track::{BindGroupStates, DeviceTracker, TrackerIndexAllocators, UsageScope, UsageScopePool},\n    validation,\n    weak_vec::WeakVec,\n    FastHashMap, LabelHelpers, OnceCellOrLock,\n};\n\nuse super::{\n    queue::Queue, DeviceDescriptor, DeviceError, DeviceLostClosure, UserClosures,\n    ENTRYPOINT_FAILURE_ERROR, ZERO_BUFFER_SIZE,\n};\n\n#[cfg(supports_64bit_atomics)]\nuse core::sync::atomic::AtomicU64;\n#[cfg(not(supports_64bit_atomics))]\nuse portable_atomic::AtomicU64;\n\npub(crate) struct CommandIndices {\n    /// The index of the last command submission that was attempted.\n    ///\n    /// Note that `fence` may never be signalled with this value, if the command\n    /// submission failed. If you need to wait for everything running on a\n    /// `Queue` to complete, wait for [`last_successful_submission_index`].\n    ///\n    /// [`last_successful_submission_index`]: Device::last_successful_submission_index\n    pub(crate) active_submission_index: hal::FenceValue,\n    pub(crate) next_acceleration_structure_build_command_index: u64,\n}\n\n/// Parameters provided to shaders via a uniform buffer of the type\n/// [`NagaExternalTextureParams`], describing an [`ExternalTexture`] resource\n/// binding.\n///\n/// [`NagaExternalTextureParams`]: naga::SpecialTypes::external_texture_params\n/// [`ExternalTexture`]: binding_model::BindingResource::ExternalTexture\n#[repr(C)]\n#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]\npub struct ExternalTextureParams {\n    /// 4x4 column-major matrix with which to convert sampled YCbCr values\n    /// to RGBA.\n    ///\n    /// This is ignored when `num_planes` is 1.\n    pub yuv_conversion_matrix: [f32; 16],\n\n    /// 3x3 column-major matrix to transform linear RGB values in the source\n    /// color space to linear RGB values in the destination color space. In\n    /// combination with [`Self::src_transfer_function`] and\n    /// [`Self::dst_transfer_function`] this can be used to ensure that\n    /// [`ImageSample`] and [`ImageLoad`] operations return values in the\n    /// desired destination color space rather than the source color space of\n    /// the underlying planes.\n    ///\n    /// Includes a padding element after each column.\n    ///\n    /// [`ImageSample`]: naga::ir::Expression::ImageSample\n    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad\n    pub gamut_conversion_matrix: [f32; 12],\n\n    /// Transfer function for the source color space. The *inverse* of this\n    /// will be applied to decode non-linear RGB to linear RGB in the source\n    /// color space.\n    pub src_transfer_function: wgt::ExternalTextureTransferFunction,\n\n    /// Transfer function for the destination color space. This will be applied\n    /// to encode linear RGB to non-linear RGB in the destination color space.\n    pub dst_transfer_function: wgt::ExternalTextureTransferFunction,\n\n    /// Transform to apply to [`ImageSample`] coordinates.\n    ///\n    /// This is a 3x2 column-major matrix representing an affine transform from\n    /// normalized texture coordinates to the normalized coordinates that should\n    /// be sampled from the external texture's underlying plane(s).\n    ///\n    /// This transform may scale, translate, flip, and rotate in 90-degree\n    /// increments, but the result of transforming the rectangle (0,0)..(1,1)\n    /// must be an axis-aligned rectangle that falls within the bounds of\n    /// (0,0)..(1,1).\n    ///\n    /// [`ImageSample`]: naga::ir::Expression::ImageSample\n    pub sample_transform: [f32; 6],\n\n    /// Transform to apply to [`ImageLoad`] coordinates.\n    ///\n    /// This is a 3x2 column-major matrix representing an affine transform from\n    /// non-normalized texel coordinates to the non-normalized coordinates of\n    /// the texel that should be loaded from the external texture's underlying\n    /// plane 0. For planes 1 and 2, if present, plane 0's coordinates are\n    /// scaled according to the textures' relative sizes.\n    ///\n    /// This transform may scale, translate, flip, and rotate in 90-degree\n    /// increments, but the result of transforming the rectangle (0,0)..[`size`]\n    /// must be an axis-aligned rectangle that falls within the bounds of\n    /// (0,0)..[`size`].\n    ///\n    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad\n    /// [`size`]: Self::size\n    pub load_transform: [f32; 6],\n\n    /// Size of the external texture.\n    ///\n    /// This is the value that should be returned by size queries in shader\n    /// code; it does not necessarily match the dimensions of the underlying\n    /// texture(s). As a special case, if this is `[0, 0]`, the actual size of\n    /// plane 0 should be used instead.\n    ///\n    /// This must be consistent with [`sample_transform`]: it should be the size\n    /// in texels of the rectangle covered by the square (0,0)..(1,1) after\n    /// [`sample_transform`] has been applied to it.\n    ///\n    /// [`sample_transform`]: Self::sample_transform\n    pub size: [u32; 2],\n\n    /// Number of planes. 1 indicates a single RGBA plane. 2 indicates a Y\n    /// plane and an interleaved CbCr plane. 3 indicates separate Y, Cb, and Cr\n    /// planes.\n    pub num_planes: u32,\n    // Ensure the size of this struct matches the type generated by Naga.\n    pub _padding: [u8; 4],\n}\n\nimpl ExternalTextureParams {\n    pub fn from_desc<L>(desc: &wgt::ExternalTextureDescriptor<L>) -> Self {\n        let gamut_conversion_matrix = [\n            desc.gamut_conversion_matrix[0],\n            desc.gamut_conversion_matrix[1],\n            desc.gamut_conversion_matrix[2],\n            0.0, // padding\n            desc.gamut_conversion_matrix[3],\n            desc.gamut_conversion_matrix[4],\n            desc.gamut_conversion_matrix[5],\n            0.0, // padding\n            desc.gamut_conversion_matrix[6],\n            desc.gamut_conversion_matrix[7],\n            desc.gamut_conversion_matrix[8],\n            0.0, // padding\n        ];\n\n        Self {\n            yuv_conversion_matrix: desc.yuv_conversion_matrix,\n            gamut_conversion_matrix,\n            src_transfer_function: desc.src_transfer_function,\n            dst_transfer_function: desc.dst_transfer_function,\n            size: [desc.width, desc.height],\n            sample_transform: desc.sample_transform,\n            load_transform: desc.load_transform,\n            num_planes: desc.num_planes() as u32,\n            _padding: Default::default(),\n        }\n    }\n}\n\n/// Structure describing a logical device. Some members are internally mutable,\n/// stored behind mutexes.\npub struct Device {\n    raw: Box<dyn hal::DynDevice>,\n    pub(crate) adapter: Arc<Adapter>,\n    pub(crate) queue: OnceCellOrLock<Weak<Queue>>,\n    pub(crate) zero_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    pub(crate) empty_bgl: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,\n    /// The `label` from the descriptor used to create the resource.\n    label: String,\n\n    pub(crate) command_allocator: command::CommandAllocator,\n\n    pub(crate) command_indices: RwLock<CommandIndices>,\n\n    /// The index of the last successful submission to this device's\n    /// [`hal::Queue`].\n    ///\n    /// Unlike [`active_submission_index`], which is incremented each time\n    /// submission is attempted, this is updated only when submission succeeds,\n    /// so waiting for this value won't hang waiting for work that was never\n    /// submitted.\n    ///\n    /// [`active_submission_index`]: CommandIndices::active_submission_index\n    pub(crate) last_successful_submission_index: hal::AtomicFenceValue,\n\n    // NOTE: if both are needed, the `snatchable_lock` must be consistently acquired before the\n    // `fence` lock to avoid deadlocks.\n    pub(crate) fence: RwLock<ManuallyDrop<Box<dyn hal::DynFence>>>,\n    pub(crate) snatchable_lock: SnatchLock,\n\n    /// Is this device valid? Valid is closely associated with \"lose the device\",\n    /// which can be triggered by various methods, including at the end of device\n    /// destroy, and by any GPU errors that cause us to no longer trust the state\n    /// of the device. Ideally we would like to fold valid into the storage of\n    /// the device itself (for example as an Error enum), but unfortunately we\n    /// need to continue to be able to retrieve the device in poll_devices to\n    /// determine if it can be dropped. If our internal accesses of devices were\n    /// done through ref-counted references and external accesses checked for\n    /// Error enums, we wouldn't need this. For now, we need it. All the call\n    /// sites where we check it are areas that should be revisited if we start\n    /// using ref-counted references for internal access.\n    pub(crate) valid: AtomicBool,\n\n    /// Closure to be called on \"lose the device\". This is invoked directly by\n    /// device.lose or by the UserCallbacks returned from maintain when the device\n    /// has been destroyed and its queues are empty.\n    pub(crate) device_lost_closure: Mutex<Option<DeviceLostClosure>>,\n\n    /// Stores the state of buffers and textures.\n    pub(crate) trackers: Mutex<DeviceTracker>,\n    pub(crate) tracker_indices: TrackerIndexAllocators,\n    /// Pool of bind group layouts, allowing deduplication.\n    pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout>,\n    pub(crate) alignments: hal::Alignments,\n    pub(crate) limits: wgt::Limits,\n    pub(crate) features: wgt::Features,\n    pub(crate) downlevel: wgt::DownlevelCapabilities,\n    /// Buffer uses listed here, are expected to be ordered by the underlying hardware.\n    /// If a usage is ordered, then if the buffer state doesn't change between draw calls,\n    /// there are no barriers needed for synchronization.\n    /// See the implementations of [`hal::Adapter::get_ordered_buffer_usages`] for hardware specific info\n    pub(crate) ordered_buffer_usages: wgt::BufferUses,\n    /// Texture uses listed here, are expected to be ordered by the underlying hardware.\n    /// If a usage is ordered, then if the buffer state doesn't change between draw calls,\n    /// there are no barriers needed for synchronization.\n    /// See the implementations of [`hal::Adapter::get_ordered_texture_usages`] for hardware specific info\n    pub(crate) ordered_texture_usages: wgt::TextureUses,\n    pub(crate) instance_flags: wgt::InstanceFlags,\n    pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy>>,\n    pub(crate) usage_scopes: UsageScopePool,\n    pub(crate) indirect_validation: Option<crate::indirect_validation::IndirectValidation>,\n    // Optional so that we can late-initialize this after the queue is created.\n    pub(crate) timestamp_normalizer:\n        OnceCellOrLock<crate::timestamp_normalization::TimestampNormalizer>,\n    /// Uniform buffer containing [`ExternalTextureParams`] with values such\n    /// that a [`TextureView`] bound to a [`wgt::BindingType::ExternalTexture`]\n    /// binding point will be rendered correctly. Intended to be used as the\n    /// [`hal::ExternalTextureBinding::params`] field.\n    pub(crate) default_external_texture_params_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    // needs to be dropped last\n    #[cfg(feature = \"trace\")]\n    pub(crate) trace: Mutex<Option<Box<dyn trace::Trace + Send + Sync + 'static>>>,\n}\n\npub(crate) enum DeferredDestroy {\n    TextureViews(WeakVec<TextureView>),\n    BindGroups(WeakVec<BindGroup>),\n}\n\nimpl fmt::Debug for Device {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"Device\")\n            .field(\"label\", &self.label())\n            .field(\"limits\", &self.limits)\n            .field(\"features\", &self.features)\n            .field(\"downlevel\", &self.downlevel)\n            .finish()\n    }\n}\n\nimpl Drop for Device {\n    fn drop(&mut self) {\n        resource_log!(\"Drop {}\", self.error_ident());\n\n        // SAFETY: We are in the Drop impl and we don't use self.zero_buffer anymore after this\n        // point.\n        let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) };\n        // SAFETY: We are in the Drop impl and we don't use self.empty_bgl anymore after this point.\n        let empty_bgl = unsafe { ManuallyDrop::take(&mut self.empty_bgl) };\n        // SAFETY: We are in the Drop impl and we don't use\n        // self.default_external_texture_params_buffer anymore after this point.\n        let default_external_texture_params_buffer =\n            unsafe { ManuallyDrop::take(&mut self.default_external_texture_params_buffer) };\n        // SAFETY: We are in the Drop impl and we don't use self.fence anymore after this point.\n        let fence = unsafe { ManuallyDrop::take(&mut self.fence.write()) };\n        if let Some(indirect_validation) = self.indirect_validation.take() {\n            indirect_validation.dispose(self.raw.as_ref());\n        }\n        if let Some(timestamp_normalizer) = self.timestamp_normalizer.take() {\n            timestamp_normalizer.dispose(self.raw.as_ref());\n        }\n        unsafe {\n            self.raw.destroy_buffer(zero_buffer);\n            self.raw.destroy_bind_group_layout(empty_bgl);\n            self.raw\n                .destroy_buffer(default_external_texture_params_buffer);\n            self.raw.destroy_fence(fence);\n        }\n    }\n}\n\nimpl Device {\n    pub(crate) fn raw(&self) -> &dyn hal::DynDevice {\n        self.raw.as_ref()\n    }\n    pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {\n        if self.features.contains(feature) {\n            Ok(())\n        } else {\n            Err(MissingFeatures(feature))\n        }\n    }\n\n    pub(crate) fn require_downlevel_flags(\n        &self,\n        flags: wgt::DownlevelFlags,\n    ) -> Result<(), MissingDownlevelFlags> {\n        if self.downlevel.flags.contains(flags) {\n            Ok(())\n        } else {\n            Err(MissingDownlevelFlags(flags))\n        }\n    }\n\n    /// # Safety\n    ///\n    /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety.\n    ///\n    /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture\n    pub unsafe fn start_graphics_debugger_capture(&self) {\n        api_log!(\"Device::start_graphics_debugger_capture\");\n\n        if !self.is_valid() {\n            return;\n        }\n        unsafe { self.raw().start_graphics_debugger_capture() };\n    }\n\n    /// # Safety\n    ///\n    /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety.\n    ///\n    /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture\n    pub unsafe fn stop_graphics_debugger_capture(&self) {\n        api_log!(\"Device::stop_graphics_debugger_capture\");\n\n        if !self.is_valid() {\n            return;\n        }\n        unsafe { self.raw().stop_graphics_debugger_capture() };\n    }\n}\n\nimpl Device {\n    pub(crate) fn new(\n        raw_device: Box<dyn hal::DynDevice>,\n        adapter: &Arc<Adapter>,\n        desc: &DeviceDescriptor,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<Self, DeviceError> {\n        #[cfg(not(feature = \"trace\"))]\n        match &desc.trace {\n            wgt::Trace::Off => {}\n            _ => {\n                log::error!(\"wgpu-core feature 'trace' is not enabled\");\n            }\n        };\n        #[cfg(feature = \"trace\")]\n        let trace: Option<Box<dyn trace::Trace + Send + Sync + 'static>> = match &desc.trace {\n            wgt::Trace::Off => None,\n            wgt::Trace::Directory(dir) => match trace::DiskTrace::new(dir.clone()) {\n                Ok(mut trace) => {\n                    trace::Trace::add(\n                        &mut trace,\n                        trace::Action::Init {\n                            desc: wgt::DeviceDescriptor {\n                                trace: wgt::Trace::Off,\n                                ..desc.clone()\n                            },\n                            backend: adapter.backend(),\n                        },\n                    );\n                    Some(Box::new(trace))\n                }\n                Err(e) => {\n                    log::error!(\"Unable to start a trace in '{dir:?}': {e}\");\n                    None\n                }\n            },\n            wgt::Trace::Memory => {\n                let mut trace = trace::MemoryTrace::new();\n                trace::Trace::add(\n                    &mut trace,\n                    trace::Action::Init {\n                        desc: wgt::DeviceDescriptor {\n                            trace: wgt::Trace::Off,\n                            ..desc.clone()\n                        },\n                        backend: adapter.backend(),\n                    },\n                );\n                Some(Box::new(trace))\n            }\n            // The enum is non_exhaustive, so we must have a fallback arm (that should be\n            // unreachable in practice).\n            t => {\n                log::error!(\"unimplemented wgpu_types::Trace variant {t:?}\");\n                None\n            }\n        };\n\n        let ordered_buffer_usages = adapter.raw.adapter.get_ordered_buffer_usages();\n        let ordered_texture_usages = adapter.raw.adapter.get_ordered_texture_usages();\n\n        let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;\n\n        let command_allocator = command::CommandAllocator::new();\n\n        let rt_uses = if desc\n            .required_features\n            .intersects(wgt::Features::EXPERIMENTAL_RAY_QUERY)\n        {\n            wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT\n        } else {\n            wgt::BufferUses::empty()\n        };\n\n        // Create zeroed buffer used for texture clears (and raytracing if required).\n        let zero_buffer = unsafe {\n            raw_device.create_buffer(&hal::BufferDescriptor {\n                label: hal_label(Some(\"(wgpu internal) zero init buffer\"), instance_flags),\n                size: ZERO_BUFFER_SIZE,\n                usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST | rt_uses,\n                memory_flags: hal::MemoryFlags::empty(),\n            })\n        }\n        .map_err(DeviceError::from_hal)?;\n\n        let empty_bgl = unsafe {\n            raw_device.create_bind_group_layout(&hal::BindGroupLayoutDescriptor {\n                label: None,\n                flags: hal::BindGroupLayoutFlags::empty(),\n                entries: &[],\n            })\n        }\n        .map_err(DeviceError::from_hal)?;\n\n        let default_external_texture_params_buffer = unsafe {\n            raw_device.create_buffer(&hal::BufferDescriptor {\n                label: hal_label(\n                    Some(\"(wgpu internal) default external texture params buffer\"),\n                    instance_flags,\n                ),\n                size: size_of::<ExternalTextureParams>() as _,\n                usage: wgt::BufferUses::COPY_DST | wgt::BufferUses::UNIFORM,\n                memory_flags: hal::MemoryFlags::empty(),\n            })\n        }\n        .map_err(DeviceError::from_hal)?;\n\n        // Cloned as we need them below anyway.\n        let alignments = adapter.raw.capabilities.alignments.clone();\n        let downlevel = adapter.raw.capabilities.downlevel.clone();\n        let limits = &adapter.raw.capabilities.limits;\n\n        let enable_indirect_validation = instance_flags\n            .contains(wgt::InstanceFlags::VALIDATION_INDIRECT_CALL)\n            && downlevel.flags.contains(\n                wgt::DownlevelFlags::INDIRECT_EXECUTION | wgt::DownlevelFlags::COMPUTE_SHADERS,\n            )\n            && limits.max_storage_buffers_per_shader_stage >= 2;\n\n        let indirect_validation = if enable_indirect_validation {\n            Some(crate::indirect_validation::IndirectValidation::new(\n                raw_device.as_ref(),\n                &desc.required_limits,\n                &desc.required_features,\n                instance_flags,\n                adapter.backend(),\n            )?)\n        } else {\n            None\n        };\n\n        Ok(Self {\n            raw: raw_device,\n            adapter: adapter.clone(),\n            queue: OnceCellOrLock::new(),\n            zero_buffer: ManuallyDrop::new(zero_buffer),\n            empty_bgl: ManuallyDrop::new(empty_bgl),\n            default_external_texture_params_buffer: ManuallyDrop::new(\n                default_external_texture_params_buffer,\n            ),\n            label: desc.label.to_string(),\n            command_allocator,\n            command_indices: RwLock::new(\n                rank::DEVICE_COMMAND_INDICES,\n                CommandIndices {\n                    active_submission_index: 0,\n                    // By starting at one, we can put the result in a NonZeroU64.\n                    next_acceleration_structure_build_command_index: 1,\n                },\n            ),\n            last_successful_submission_index: AtomicU64::new(0),\n            fence: RwLock::new(rank::DEVICE_FENCE, ManuallyDrop::new(fence)),\n            snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },\n            valid: AtomicBool::new(true),\n            device_lost_closure: Mutex::new(rank::DEVICE_LOST_CLOSURE, None),\n            trackers: Mutex::new(\n                rank::DEVICE_TRACKERS,\n                DeviceTracker::new(ordered_buffer_usages, ordered_texture_usages),\n            ),\n            tracker_indices: TrackerIndexAllocators::new(),\n            bgl_pool: ResourcePool::new(),\n            #[cfg(feature = \"trace\")]\n            trace: Mutex::new(rank::DEVICE_TRACE, trace),\n            alignments,\n            limits: desc.required_limits.clone(),\n            features: desc.required_features,\n            downlevel,\n            ordered_buffer_usages,\n            ordered_texture_usages,\n            instance_flags,\n            deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),\n            usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),\n            timestamp_normalizer: OnceCellOrLock::new(),\n            indirect_validation,\n        })\n    }\n\n    /// Initializes [`Device::default_external_texture_params_buffer`] with\n    /// required values such that a [`TextureView`] bound to a\n    /// [`wgt::BindingType::ExternalTexture`] binding point will be rendered\n    /// correctly.\n    fn init_default_external_texture_params_buffer(self: &Arc<Self>) -> Result<(), DeviceError> {\n        let data = ExternalTextureParams {\n            #[rustfmt::skip]\n            yuv_conversion_matrix: [\n                1.0, 0.0, 0.0, 0.0,\n                0.0, 1.0, 0.0, 0.0,\n                0.0, 0.0, 1.0, 0.0,\n                0.0, 0.0, 0.0, 1.0,\n            ],\n            #[rustfmt::skip]\n            gamut_conversion_matrix: [\n                1.0, 0.0, 0.0, /* padding */ 0.0,\n                0.0, 1.0, 0.0, /* padding */ 0.0,\n                0.0, 0.0, 1.0, /* padding */ 0.0,\n            ],\n            src_transfer_function: Default::default(),\n            dst_transfer_function: Default::default(),\n            size: [0, 0],\n            #[rustfmt::skip]\n            sample_transform: [\n                1.0, 0.0,\n                0.0, 1.0,\n                0.0, 0.0\n            ],\n            #[rustfmt::skip]\n            load_transform: [\n                1.0, 0.0,\n                0.0, 1.0,\n                0.0, 0.0\n            ],\n            num_planes: 1,\n            _padding: Default::default(),\n        };\n        let mut staging_buffer =\n            StagingBuffer::new(self, wgt::BufferSize::new(size_of_val(&data) as _).unwrap())?;\n        staging_buffer.write(bytemuck::bytes_of(&data));\n        let staging_buffer = staging_buffer.flush();\n\n        let params_buffer = self.default_external_texture_params_buffer.as_ref();\n        let queue = self.get_queue().unwrap();\n        let mut pending_writes = queue.pending_writes.lock();\n\n        unsafe {\n            pending_writes\n                .command_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: params_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::MAP_WRITE,\n                        to: wgt::BufferUses::COPY_DST,\n                    },\n                }]);\n            pending_writes.command_encoder.copy_buffer_to_buffer(\n                staging_buffer.raw(),\n                params_buffer,\n                &[hal::BufferCopy {\n                    src_offset: 0,\n                    dst_offset: 0,\n                    size: staging_buffer.size,\n                }],\n            );\n            pending_writes.consume(staging_buffer);\n            pending_writes\n                .command_encoder\n                .transition_buffers(&[hal::BufferBarrier {\n                    buffer: params_buffer,\n                    usage: hal::StateTransition {\n                        from: wgt::BufferUses::COPY_DST,\n                        to: wgt::BufferUses::UNIFORM,\n                    },\n                }]);\n        }\n\n        Ok(())\n    }\n\n    pub fn late_init_resources_with_queue(self: &Arc<Self>) -> Result<(), RequestDeviceError> {\n        let queue = self.get_queue().unwrap();\n\n        let timestamp_normalizer = crate::timestamp_normalization::TimestampNormalizer::new(\n            self,\n            queue.get_timestamp_period(),\n        )?;\n\n        self.timestamp_normalizer\n            .set(timestamp_normalizer)\n            .unwrap_or_else(|_| panic!(\"Called late_init_resources_with_queue twice\"));\n\n        self.init_default_external_texture_params_buffer()?;\n\n        Ok(())\n    }\n\n    /// Returns the backend this device is using.\n    pub fn backend(&self) -> wgt::Backend {\n        self.adapter.backend()\n    }\n\n    pub fn is_valid(&self) -> bool {\n        self.valid.load(Ordering::Acquire)\n    }\n\n    pub fn check_is_valid(&self) -> Result<(), DeviceError> {\n        if self.is_valid() {\n            Ok(())\n        } else {\n            Err(DeviceError::Lost)\n        }\n    }\n\n    /// Stop tracing and return the trace object.\n    ///\n    /// This is mostly useful for in-memory traces.\n    #[cfg(feature = \"trace\")]\n    pub fn take_trace(&self) -> Option<Box<dyn trace::Trace + Send + Sync + 'static>> {\n        self.trace.lock().take()\n    }\n\n    /// Checks that we are operating within the memory budget reported by the native APIs.\n    ///\n    /// If we are not, the device gets invalidated.\n    ///\n    /// The budget might fluctuate over the lifetime of the application, so it should be checked\n    /// somewhat frequently.\n    pub fn lose_if_oom(&self) {\n        let _ = self\n            .raw()\n            .check_if_oom()\n            .map_err(|e| self.handle_hal_error(e));\n    }\n\n    pub fn handle_hal_error(&self, error: hal::DeviceError) -> DeviceError {\n        match error {\n            hal::DeviceError::OutOfMemory\n            | hal::DeviceError::Lost\n            | hal::DeviceError::Unexpected => {\n                self.lose(&error.to_string());\n            }\n        }\n        DeviceError::from_hal(error)\n    }\n\n    pub fn handle_hal_error_with_nonfatal_oom(&self, error: hal::DeviceError) -> DeviceError {\n        match error {\n            hal::DeviceError::OutOfMemory => DeviceError::from_hal(error),\n            error => self.handle_hal_error(error),\n        }\n    }\n\n    /// Run some destroy operations that were deferred.\n    ///\n    /// Destroying the resources requires taking a write lock on the device's snatch lock,\n    /// so a good reason for deferring resource destruction is when we don't know for sure\n    /// how risky it is to take the lock (typically, it shouldn't be taken from the drop\n    /// implementation of a reference-counted structure).\n    /// The snatch lock must not be held while this function is called.\n    pub(crate) fn deferred_resource_destruction(&self) {\n        let deferred_destroy = mem::take(&mut *self.deferred_destroy.lock());\n        for item in deferred_destroy {\n            match item {\n                DeferredDestroy::TextureViews(views) => {\n                    for view in views {\n                        let Some(view) = view.upgrade() else {\n                            continue;\n                        };\n                        let Some(raw_view) = view.raw.snatch(&mut self.snatchable_lock.write())\n                        else {\n                            continue;\n                        };\n\n                        resource_log!(\"Destroy raw {}\", view.error_ident());\n\n                        unsafe {\n                            self.raw().destroy_texture_view(raw_view);\n                        }\n                    }\n                }\n                DeferredDestroy::BindGroups(bind_groups) => {\n                    for bind_group in bind_groups {\n                        let Some(bind_group) = bind_group.upgrade() else {\n                            continue;\n                        };\n                        let Some(raw_bind_group) =\n                            bind_group.raw.snatch(&mut self.snatchable_lock.write())\n                        else {\n                            continue;\n                        };\n\n                        resource_log!(\"Destroy raw {}\", bind_group.error_ident());\n\n                        unsafe {\n                            self.raw().destroy_bind_group(raw_bind_group);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn get_queue(&self) -> Option<Arc<Queue>> {\n        self.queue.get().as_ref()?.upgrade()\n    }\n\n    pub fn set_queue(&self, queue: &Arc<Queue>) {\n        assert!(self.queue.set(Arc::downgrade(queue)).is_ok());\n    }\n\n    pub fn poll(\n        &self,\n        poll_type: wgt::PollType<crate::SubmissionIndex>,\n    ) -> Result<wgt::PollStatus, WaitIdleError> {\n        let (user_closures, result) = self.poll_and_return_closures(poll_type);\n        user_closures.fire();\n        result\n    }\n\n    /// Poll the device, returning any `UserClosures` that need to be executed.\n    ///\n    /// The caller must invoke the `UserClosures` even if this function returns\n    /// an error. This is an internal helper, used by `Device::poll` and\n    /// `Global::poll_all_devices`, so that `poll_all_devices` can invoke\n    /// closures once after all devices have been polled.\n    pub(crate) fn poll_and_return_closures(\n        &self,\n        poll_type: wgt::PollType<crate::SubmissionIndex>,\n    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {\n        let snatch_guard = self.snatchable_lock.read();\n        let fence = self.fence.read();\n        let maintain_result = self.maintain(fence, poll_type, snatch_guard);\n\n        self.lose_if_oom();\n\n        // Some deferred destroys are scheduled in maintain so run this right after\n        // to avoid holding on to them until the next device poll.\n        self.deferred_resource_destruction();\n\n        maintain_result\n    }\n\n    /// Check the current status of the GPU and process any submissions that have\n    /// finished.\n    ///\n    /// The `poll_type` argument tells if this function should wait for a particular\n    /// submission index to complete, or if it should just poll the current status.\n    ///\n    /// This will process _all_ completed submissions, even if the caller only asked\n    /// us to poll to a given submission index.\n    ///\n    /// Return a pair `(closures, result)`, where:\n    ///\n    /// - `closures` is a list of callbacks that need to be invoked informing the user\n    ///   about various things occurring. These happen and should be handled even if\n    ///   this function returns an error, hence they are outside of the result.\n    ///\n    /// - `results` is a boolean indicating the result of the wait operation, including\n    ///   if there was a timeout or a validation error.\n    pub(crate) fn maintain<'this>(\n        &'this self,\n        fence: crate::lock::RwLockReadGuard<ManuallyDrop<Box<dyn hal::DynFence>>>,\n        poll_type: wgt::PollType<crate::SubmissionIndex>,\n        snatch_guard: SnatchGuard,\n    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {\n        profiling::scope!(\"Device::maintain\");\n\n        let mut user_closures = UserClosures::default();\n\n        // If a wait was requested, determine which submission index to wait for.\n        let wait_submission_index = match poll_type {\n            wgt::PollType::Wait {\n                submission_index: Some(submission_index),\n                ..\n            } => {\n                let last_successful_submission_index = self\n                    .last_successful_submission_index\n                    .load(Ordering::Acquire);\n\n                if submission_index > last_successful_submission_index {\n                    let result = Err(WaitIdleError::WrongSubmissionIndex(\n                        submission_index,\n                        last_successful_submission_index,\n                    ));\n\n                    return (user_closures, result);\n                }\n\n                Some(submission_index)\n            }\n            wgt::PollType::Wait {\n                submission_index: None,\n                ..\n            } => Some(\n                self.last_successful_submission_index\n                    .load(Ordering::Acquire),\n            ),\n            wgt::PollType::Poll => None,\n        };\n\n        // Wait for the submission index if requested.\n        if let Some(target_submission_index) = wait_submission_index {\n            log::trace!(\"Device::maintain: waiting for submission index {target_submission_index}\");\n\n            let wait_timeout = match poll_type {\n                wgt::PollType::Wait { timeout, .. } => timeout,\n                wgt::PollType::Poll => unreachable!(\n                    \"`wait_submission_index` index for poll type `Poll` should be None\"\n                ),\n            };\n\n            let wait_result = unsafe {\n                self.raw()\n                    .wait(fence.as_ref(), target_submission_index, wait_timeout)\n            };\n\n            // This error match is only about `DeviceErrors`. At this stage we do not care if\n            // the wait succeeded or not, and the `Ok(bool)`` variant is ignored.\n            if let Err(e) = wait_result {\n                let hal_error: WaitIdleError = self.handle_hal_error(e).into();\n                return (user_closures, Err(hal_error));\n            }\n        }\n\n        // Get the currently finished submission index. This may be higher than the requested\n        // wait, or it may be less than the requested wait if the wait failed.\n        let fence_value_result = unsafe { self.raw().get_fence_value(fence.as_ref()) };\n        let current_finished_submission = match fence_value_result {\n            Ok(fence_value) => fence_value,\n            Err(e) => {\n                let hal_error: WaitIdleError = self.handle_hal_error(e).into();\n                return (user_closures, Err(hal_error));\n            }\n        };\n\n        // Maintain all finished submissions on the queue, updating the relevant user closures and\n        // collecting if the queue is empty.\n        //\n        // We don't use the result of the wait here, as we want to progress forward as far as\n        // possible and the wait could have been for submissions that finished long ago.\n        let mut queue_empty = false;\n        if let Some(queue) = self.get_queue() {\n            let queue_result = queue.maintain(current_finished_submission, &snatch_guard);\n            (\n                user_closures.submissions,\n                user_closures.mappings,\n                user_closures.blas_compact_ready,\n                queue_empty,\n            ) = queue_result;\n            // DEADLOCK PREVENTION: We must drop `snatch_guard` before `queue` goes out of scope.\n            //\n            // `Queue::drop` acquires the snatch guard. If we still hold it when `queue` is dropped\n            // at the end of this block, we would deadlock. This can happen in the following\n            // scenario:\n            //\n            // - Thread A calls `Device::maintain` while Thread B holds the last strong ref to the\n            //   queue.\n            // - Thread A calls `self.get_queue()`, obtaining a new strong ref, and enters this\n            //   branch.\n            // - Thread B drops its strong ref, making Thread A's ref the last one.\n            // - When `queue` goes out of scope here, `Queue::drop` runs and tries to acquire the\n            //   snatch guard — but Thread A (this thread) still holds it, causing a deadlock.\n            drop(snatch_guard);\n        } else {\n            drop(snatch_guard);\n        };\n\n        // Based on the queue empty status, and the current finished submission index, determine\n        // the result of the poll.\n        let result = if queue_empty {\n            if let Some(wait_submission_index) = wait_submission_index {\n                // Assert to ensure that if we received a queue empty status, the fence shows the\n                // correct value. This is defensive, as this should never be hit.\n                assert!(\n                    current_finished_submission >= wait_submission_index,\n                    concat!(\n                        \"If the queue is empty, the current submission index \",\n                        \"({}) should be at least the wait submission index ({})\",\n                    ),\n                    current_finished_submission,\n                    wait_submission_index,\n                );\n            }\n\n            Ok(wgt::PollStatus::QueueEmpty)\n        } else if let Some(wait_submission_index) = wait_submission_index {\n            // This is theoretically possible to succeed more than checking on the poll result\n            // as submissions could have finished in the time between the timeout resolving,\n            // the thread getting scheduled again, and us checking the fence value.\n            if current_finished_submission >= wait_submission_index {\n                Ok(wgt::PollStatus::WaitSucceeded)\n            } else {\n                Err(WaitIdleError::Timeout)\n            }\n        } else {\n            Ok(wgt::PollStatus::Poll)\n        };\n\n        // Detect if we have been destroyed and now need to lose the device.\n        //\n        // If we are invalid (set at start of destroy) and our queue is empty,\n        // and we have a DeviceLostClosure, return the closure to be called by\n        // our caller. This will complete the steps for both destroy and for\n        // \"lose the device\".\n        let mut should_release_gpu_resource = false;\n        if !self.is_valid() && queue_empty {\n            // We can release gpu resources associated with this device (but not\n            // while holding the life_tracker lock).\n            should_release_gpu_resource = true;\n\n            // If we have a DeviceLostClosure, build an invocation with the\n            // reason DeviceLostReason::Destroyed and no message.\n            if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {\n                user_closures\n                    .device_lost_invocations\n                    .push(DeviceLostInvocation {\n                        closure: device_lost_closure,\n                        reason: DeviceLostReason::Destroyed,\n                        message: String::new(),\n                    });\n            }\n        }\n\n        // Don't hold the locks while calling release_gpu_resources.\n        drop(fence);\n\n        if should_release_gpu_resource {\n            self.release_gpu_resources();\n        }\n\n        (user_closures, result)\n    }\n\n    pub fn create_buffer(\n        self: &Arc<Self>,\n        desc: &resource::BufferDescriptor,\n    ) -> Result<Arc<Buffer>, resource::CreateBufferError> {\n        self.check_is_valid()?;\n\n        if desc.size > self.limits.max_buffer_size {\n            return Err(resource::CreateBufferError::MaxBufferSize {\n                requested: desc.size,\n                maximum: self.limits.max_buffer_size,\n            });\n        }\n\n        if desc\n            .usage\n            .intersects(wgt::BufferUsages::BLAS_INPUT | wgt::BufferUsages::TLAS_INPUT)\n        {\n            self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)?;\n        }\n\n        if desc.usage.contains(wgt::BufferUsages::INDEX)\n            && desc.usage.contains(\n                wgt::BufferUsages::VERTEX\n                    | wgt::BufferUsages::UNIFORM\n                    | wgt::BufferUsages::INDIRECT\n                    | wgt::BufferUsages::STORAGE,\n            )\n        {\n            self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;\n        }\n\n        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {\n            return Err(resource::CreateBufferError::InvalidUsage(desc.usage));\n        }\n\n        if !self\n            .features\n            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)\n        {\n            use wgt::BufferUsages as Bu;\n            let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)\n                && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);\n            let read_mismatch = desc.usage.contains(Bu::MAP_READ)\n                && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);\n            if write_mismatch || read_mismatch {\n                return Err(resource::CreateBufferError::UsageMismatch(desc.usage));\n            }\n        }\n\n        let mut usage = conv::map_buffer_usage(desc.usage);\n\n        if desc.usage.contains(wgt::BufferUsages::INDIRECT) {\n            self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;\n            // We are going to be reading from it, internally;\n            // when validating the content of the buffer\n            usage |= wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE;\n        }\n\n        if desc.usage.contains(wgt::BufferUsages::QUERY_RESOLVE) {\n            usage |= TIMESTAMP_NORMALIZATION_BUFFER_USES;\n        }\n\n        if desc.mapped_at_creation {\n            if !desc.size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n                return Err(resource::CreateBufferError::UnalignedSize);\n            }\n            if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {\n                // we are going to be copying into it, internally\n                usage |= wgt::BufferUses::COPY_DST;\n            }\n        } else {\n            // We are required to zero out (initialize) all memory. This is done\n            // on demand using clear_buffer which requires write transfer usage!\n            usage |= wgt::BufferUses::COPY_DST;\n        }\n\n        let actual_size = if desc.size == 0 {\n            wgt::COPY_BUFFER_ALIGNMENT\n        } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {\n            // Bumping the size by 1 so that we can bind an empty range at the\n            // end of the buffer.\n            desc.size + 1\n        } else {\n            desc.size\n        };\n        let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;\n        let aligned_size = if clear_remainder != 0 {\n            actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder\n        } else {\n            actual_size\n        };\n\n        let hal_desc = hal::BufferDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            size: aligned_size,\n            usage,\n            memory_flags: hal::MemoryFlags::empty(),\n        };\n        let buffer = unsafe { self.raw().create_buffer(&hal_desc) }\n            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let timestamp_normalization_bind_group = Snatchable::new(unsafe {\n            // SAFETY: The size passed here must not overflow the buffer.\n            self.timestamp_normalizer\n                .get()\n                .unwrap()\n                .create_normalization_bind_group(\n                    self,\n                    &*buffer,\n                    desc.label.as_deref(),\n                    wgt::BufferSize::new(hal_desc.size).unwrap(),\n                    desc.usage,\n                )\n        }?);\n\n        let indirect_validation_bind_groups =\n            self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?;\n\n        let buffer = Buffer {\n            raw: Snatchable::new(buffer),\n            device: self.clone(),\n            usage: desc.usage,\n            size: desc.size,\n            initialization_status: RwLock::new(\n                rank::BUFFER_INITIALIZATION_STATUS,\n                BufferInitTracker::new(aligned_size),\n            ),\n            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),\n            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),\n            timestamp_normalization_bind_group,\n            indirect_validation_bind_groups,\n        };\n\n        let buffer = Arc::new(buffer);\n\n        let buffer_use = if !desc.mapped_at_creation {\n            wgt::BufferUses::empty()\n        } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {\n            // buffer is mappable, so we are just doing that at start\n            let map_size = buffer.size;\n            let mapping = if map_size == 0 {\n                hal::BufferMapping {\n                    ptr: core::ptr::NonNull::dangling(),\n                    is_coherent: true,\n                }\n            } else {\n                let snatch_guard: SnatchGuard = self.snatchable_lock.read();\n                map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)?\n            };\n            *buffer.map_state.lock() = resource::BufferMapState::Active {\n                mapping,\n                range: 0..map_size,\n                host: HostMap::Write,\n            };\n            wgt::BufferUses::MAP_WRITE\n        } else {\n            let mut staging_buffer =\n                StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?;\n\n            // Zero initialize memory and then mark the buffer as initialized\n            // (it's guaranteed that this is the case by the time the buffer is usable)\n            staging_buffer.write_zeros();\n            buffer.initialization_status.write().drain(0..aligned_size);\n\n            *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer };\n            wgt::BufferUses::COPY_DST\n        };\n\n        self.trackers\n            .lock()\n            .buffers\n            .insert_single(&buffer, buffer_use);\n\n        Ok(buffer)\n    }\n\n    #[cfg(feature = \"replay\")]\n    pub fn set_buffer_data(\n        self: &Arc<Self>,\n        buffer: &Arc<Buffer>,\n        offset: wgt::BufferAddress,\n        data: &[u8],\n    ) -> resource::BufferAccessResult {\n        use crate::resource::RawResourceAccess;\n\n        let device = &buffer.device;\n\n        device.check_is_valid()?;\n        buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;\n\n        let last_submission = device\n            .get_queue()\n            .and_then(|queue| queue.lock_life().get_buffer_latest_submission_index(buffer));\n\n        if let Some(last_submission) = last_submission {\n            device.wait_for_submit(last_submission)?;\n        }\n\n        let snatch_guard = device.snatchable_lock.read();\n        let raw_buf = buffer.try_raw(&snatch_guard)?;\n\n        let mapping = unsafe {\n            device\n                .raw()\n                .map_buffer(raw_buf, offset..offset + data.len() as u64)\n        }\n        .map_err(|e| device.handle_hal_error(e))?;\n\n        unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) };\n\n        if !mapping.is_coherent {\n            #[allow(clippy::single_range_in_vec_init)]\n            unsafe {\n                device\n                    .raw()\n                    .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64])\n            };\n        }\n\n        unsafe { device.raw().unmap_buffer(raw_buf) };\n\n        Ok(())\n    }\n\n    pub(crate) fn create_texture_from_hal(\n        self: &Arc<Self>,\n        hal_texture: Box<dyn hal::DynTexture>,\n        desc: &resource::TextureDescriptor,\n    ) -> Result<Arc<Texture>, resource::CreateTextureError> {\n        let format_features = self\n            .describe_format_features(desc.format)\n            .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;\n\n        unsafe { self.raw().add_raw_texture(&*hal_texture) };\n\n        let texture = Texture::new(\n            self,\n            resource::TextureInner::Native { raw: hal_texture },\n            conv::map_texture_usage(desc.usage, desc.format.into(), format_features.flags),\n            desc,\n            format_features,\n            resource::TextureClearMode::None,\n            false,\n        );\n\n        let texture = Arc::new(texture);\n\n        self.trackers\n            .lock()\n            .textures\n            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);\n\n        Ok(texture)\n    }\n\n    /// # Safety\n    ///\n    /// - `hal_buffer` must have been created on this device.\n    /// - `hal_buffer` must have been created respecting `desc` (in particular, the size).\n    /// - `hal_buffer` must be initialized.\n    /// - `hal_buffer` must not have zero size.\n    pub(crate) unsafe fn create_buffer_from_hal(\n        self: &Arc<Self>,\n        hal_buffer: Box<dyn hal::DynBuffer>,\n        desc: &resource::BufferDescriptor,\n    ) -> (Fallible<Buffer>, Option<resource::CreateBufferError>) {\n        let timestamp_normalization_bind_group = unsafe {\n            match self\n                .timestamp_normalizer\n                .get()\n                .unwrap()\n                .create_normalization_bind_group(\n                    self,\n                    &*hal_buffer,\n                    desc.label.as_deref(),\n                    wgt::BufferSize::new(desc.size).unwrap(),\n                    desc.usage,\n                ) {\n                Ok(bg) => Snatchable::new(bg),\n                Err(e) => {\n                    return (\n                        Fallible::Invalid(Arc::new(desc.label.to_string())),\n                        Some(e.into()),\n                    )\n                }\n            }\n        };\n\n        let indirect_validation_bind_groups = match self.create_indirect_validation_bind_groups(\n            hal_buffer.as_ref(),\n            desc.size,\n            desc.usage,\n        ) {\n            Ok(ok) => ok,\n            Err(e) => return (Fallible::Invalid(Arc::new(desc.label.to_string())), Some(e)),\n        };\n\n        unsafe { self.raw().add_raw_buffer(&*hal_buffer) };\n\n        let buffer = Buffer {\n            raw: Snatchable::new(hal_buffer),\n            device: self.clone(),\n            usage: desc.usage,\n            size: desc.size,\n            initialization_status: RwLock::new(\n                rank::BUFFER_INITIALIZATION_STATUS,\n                BufferInitTracker::new(0),\n            ),\n            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),\n            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),\n            timestamp_normalization_bind_group,\n            indirect_validation_bind_groups,\n        };\n\n        let buffer = Arc::new(buffer);\n\n        self.trackers\n            .lock()\n            .buffers\n            .insert_single(&buffer, wgt::BufferUses::empty());\n\n        (Fallible::Valid(buffer), None)\n    }\n\n    fn create_indirect_validation_bind_groups(\n        &self,\n        raw_buffer: &dyn hal::DynBuffer,\n        buffer_size: u64,\n        usage: wgt::BufferUsages,\n    ) -> Result<Snatchable<crate::indirect_validation::BindGroups>, resource::CreateBufferError>\n    {\n        if !usage.contains(wgt::BufferUsages::INDIRECT) {\n            return Ok(Snatchable::empty());\n        }\n\n        let Some(ref indirect_validation) = self.indirect_validation else {\n            return Ok(Snatchable::empty());\n        };\n\n        let bind_groups = crate::indirect_validation::BindGroups::new(\n            indirect_validation,\n            self,\n            buffer_size,\n            raw_buffer,\n        )\n        .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?;\n\n        if let Some(bind_groups) = bind_groups {\n            Ok(Snatchable::new(bind_groups))\n        } else {\n            Ok(Snatchable::empty())\n        }\n    }\n\n    pub fn create_texture(\n        self: &Arc<Self>,\n        desc: &resource::TextureDescriptor,\n    ) -> Result<Arc<Texture>, resource::CreateTextureError> {\n        use resource::{CreateTextureError, TextureDimensionError};\n\n        self.check_is_valid()?;\n\n        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {\n            return Err(CreateTextureError::InvalidUsage(desc.usage));\n        }\n\n        conv::check_texture_dimension_size(\n            desc.dimension,\n            desc.size,\n            desc.sample_count,\n            &self.limits,\n        )?;\n\n        if desc.dimension != wgt::TextureDimension::D2 {\n            // Depth textures can only be 2D\n            if desc.format.is_depth_stencil_format() {\n                return Err(CreateTextureError::InvalidDepthDimension(\n                    desc.dimension,\n                    desc.format,\n                ));\n            }\n        }\n\n        if desc.dimension != wgt::TextureDimension::D2\n            && desc.dimension != wgt::TextureDimension::D3\n        {\n            // Compressed textures can only be 2D or 3D\n            if desc.format.is_compressed() {\n                return Err(CreateTextureError::InvalidCompressedDimension(\n                    desc.dimension,\n                    desc.format,\n                ));\n            }\n\n            // Renderable textures can only be 2D or 3D\n            if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {\n                return Err(CreateTextureError::InvalidDimensionUsages(\n                    wgt::TextureUsages::RENDER_ATTACHMENT,\n                    desc.dimension,\n                ));\n            }\n        }\n\n        if desc.format.is_compressed() {\n            let (block_width, block_height) = desc.format.block_dimensions();\n\n            if !desc.size.width.is_multiple_of(block_width) {\n                return Err(CreateTextureError::InvalidDimension(\n                    TextureDimensionError::NotMultipleOfBlockWidth {\n                        width: desc.size.width,\n                        block_width,\n                        format: desc.format,\n                    },\n                ));\n            }\n\n            if !desc.size.height.is_multiple_of(block_height) {\n                return Err(CreateTextureError::InvalidDimension(\n                    TextureDimensionError::NotMultipleOfBlockHeight {\n                        height: desc.size.height,\n                        block_height,\n                        format: desc.format,\n                    },\n                ));\n            }\n\n            if desc.dimension == wgt::TextureDimension::D3 {\n                // Only BCn formats with Sliced 3D feature can be used for 3D textures\n                if desc.format.is_bcn() {\n                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D)\n                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;\n                } else if desc.format.is_astc() {\n                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D)\n                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;\n                } else {\n                    return Err(CreateTextureError::InvalidCompressedDimension(\n                        desc.dimension,\n                        desc.format,\n                    ));\n                }\n            }\n        }\n\n        let mips = desc.mip_level_count;\n        let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);\n        if mips == 0 || mips > max_levels_allowed {\n            return Err(CreateTextureError::InvalidMipLevelCount {\n                requested: mips,\n                maximum: max_levels_allowed,\n            });\n        }\n\n        {\n            let (mut width_multiple, mut height_multiple) = desc.format.size_multiple_requirement();\n\n            if desc.format.is_multi_planar_format() {\n                // TODO(https://github.com/gfx-rs/wgpu/issues/8491): fix\n                // `mip_level_size` calculation for these formats and relax this\n                // restriction.\n                width_multiple <<= desc.mip_level_count.saturating_sub(1);\n                height_multiple <<= desc.mip_level_count.saturating_sub(1);\n            }\n\n            if !desc.size.width.is_multiple_of(width_multiple) {\n                return Err(CreateTextureError::InvalidDimension(\n                    TextureDimensionError::WidthNotMultipleOf {\n                        width: desc.size.width,\n                        multiple: width_multiple,\n                        format: desc.format,\n                    },\n                ));\n            }\n\n            if !desc.size.height.is_multiple_of(height_multiple) {\n                return Err(CreateTextureError::InvalidDimension(\n                    TextureDimensionError::HeightNotMultipleOf {\n                        height: desc.size.height,\n                        multiple: height_multiple,\n                        format: desc.format,\n                    },\n                ));\n            }\n        }\n\n        if desc.usage.contains(wgt::TextureUsages::TRANSIENT) {\n            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {\n                return Err(CreateTextureError::InvalidUsage(\n                    wgt::TextureUsages::TRANSIENT,\n                ));\n            }\n            let extra_usage =\n                desc.usage - wgt::TextureUsages::TRANSIENT - wgt::TextureUsages::RENDER_ATTACHMENT;\n            if !extra_usage.is_empty() {\n                return Err(CreateTextureError::IncompatibleUsage(\n                    wgt::TextureUsages::TRANSIENT,\n                    extra_usage,\n                ));\n            }\n        }\n\n        let format_features = self\n            .describe_format_features(desc.format)\n            .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;\n\n        if desc.sample_count > 1 {\n            // <https://www.w3.org/TR/2025/CRD-webgpu-20251120/#:~:text=If%20descriptor%2EsampleCount%20%3E%201>\n            //\n            // Note that there are also some checks related to the sample count\n            // in [`conv::check_texture_dimension_size`].\n\n            if desc.mip_level_count != 1 {\n                return Err(CreateTextureError::InvalidMipLevelCount {\n                    requested: desc.mip_level_count,\n                    maximum: 1,\n                });\n            }\n\n            if desc.size.depth_or_array_layers != 1\n                && !self.features.contains(wgt::Features::MULTISAMPLE_ARRAY)\n            {\n                return Err(CreateTextureError::InvalidDimension(\n                    TextureDimensionError::MultisampledDepthOrArrayLayer(\n                        desc.size.depth_or_array_layers,\n                    ),\n                ));\n            }\n\n            if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {\n                return Err(CreateTextureError::InvalidMultisampledStorageBinding);\n            }\n\n            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {\n                return Err(CreateTextureError::MultisampledNotRenderAttachment);\n            }\n\n            if !format_features.flags.intersects(\n                wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4\n                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2\n                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8\n                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,\n            ) {\n                return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));\n            }\n\n            if !format_features\n                .flags\n                .sample_count_supported(desc.sample_count)\n            {\n                return Err(CreateTextureError::InvalidSampleCount(\n                    desc.sample_count,\n                    desc.format,\n                    desc.format\n                        .guaranteed_format_features(self.features)\n                        .flags\n                        .supported_sample_counts(),\n                    self.adapter\n                        .get_texture_format_features(desc.format)\n                        .flags\n                        .supported_sample_counts(),\n                ));\n            };\n        }\n\n        let missing_allowed_usages = match desc.format.planes() {\n            Some(planes) => {\n                let mut planes_usages = wgt::TextureUsages::all();\n                for plane in 0..planes {\n                    let aspect = wgt::TextureAspect::from_plane(plane).unwrap();\n                    let format = desc.format.aspect_specific_format(aspect).unwrap();\n                    let format_features = self\n                        .describe_format_features(format)\n                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;\n\n                    planes_usages &= format_features.allowed_usages;\n                }\n\n                desc.usage - planes_usages\n            }\n            None => desc.usage - format_features.allowed_usages,\n        };\n\n        if !missing_allowed_usages.is_empty() {\n            // detect downlevel incompatibilities\n            let wgpu_allowed_usages = desc\n                .format\n                .guaranteed_format_features(self.features)\n                .allowed_usages;\n            let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;\n            return Err(CreateTextureError::InvalidFormatUsages(\n                missing_allowed_usages,\n                desc.format,\n                wgpu_missing_usages.is_empty(),\n            ));\n        }\n\n        let mut hal_view_formats = Vec::new();\n        for format in desc.view_formats.iter() {\n            if desc.format == *format {\n                continue;\n            }\n            if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {\n                return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));\n            }\n            hal_view_formats.push(*format);\n        }\n        if !hal_view_formats.is_empty() {\n            self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;\n        }\n\n        let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);\n\n        let hal_desc = hal::TextureDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            size: desc.size,\n            mip_level_count: desc.mip_level_count,\n            sample_count: desc.sample_count,\n            dimension: desc.dimension,\n            format: desc.format,\n            usage: hal_usage,\n            memory_flags: hal::MemoryFlags::empty(),\n            view_formats: hal_view_formats,\n        };\n\n        let raw_texture = unsafe { self.raw().create_texture(&hal_desc) }\n            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let clear_mode = if hal_usage\n            .intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE | wgt::TextureUses::COLOR_TARGET)\n            && desc.dimension == wgt::TextureDimension::D2\n        {\n            let (is_color, usage) = if desc.format.is_depth_stencil_format() {\n                (false, wgt::TextureUses::DEPTH_STENCIL_WRITE)\n            } else {\n                (true, wgt::TextureUses::COLOR_TARGET)\n            };\n\n            let clear_label = hal_label(\n                Some(\"(wgpu internal) clear texture view\"),\n                self.instance_flags,\n            );\n\n            let mut clear_views = SmallVec::new();\n            for mip_level in 0..desc.mip_level_count {\n                for array_layer in 0..desc.size.depth_or_array_layers {\n                    macro_rules! push_clear_view {\n                        ($format:expr, $aspect:expr) => {\n                            let desc = hal::TextureViewDescriptor {\n                                label: clear_label,\n                                format: $format,\n                                dimension: TextureViewDimension::D2,\n                                usage,\n                                range: wgt::ImageSubresourceRange {\n                                    aspect: $aspect,\n                                    base_mip_level: mip_level,\n                                    mip_level_count: Some(1),\n                                    base_array_layer: array_layer,\n                                    array_layer_count: Some(1),\n                                },\n                            };\n                            clear_views.push(ManuallyDrop::new(\n                                unsafe {\n                                    self.raw().create_texture_view(raw_texture.as_ref(), &desc)\n                                }\n                                .map_err(|e| self.handle_hal_error(e))?,\n                            ));\n                        };\n                    }\n\n                    if let Some(planes) = desc.format.planes() {\n                        for plane in 0..planes {\n                            let aspect = wgt::TextureAspect::from_plane(plane).unwrap();\n                            let format = desc.format.aspect_specific_format(aspect).unwrap();\n                            push_clear_view!(format, aspect);\n                        }\n                    } else {\n                        push_clear_view!(desc.format, wgt::TextureAspect::All);\n                    }\n                }\n            }\n            resource::TextureClearMode::RenderPass {\n                clear_views,\n                is_color,\n            }\n        } else {\n            resource::TextureClearMode::BufferCopy\n        };\n\n        let texture = Texture::new(\n            self,\n            resource::TextureInner::Native { raw: raw_texture },\n            hal_usage,\n            desc,\n            format_features,\n            clear_mode,\n            true,\n        );\n\n        let texture = Arc::new(texture);\n\n        self.trackers\n            .lock()\n            .textures\n            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);\n\n        Ok(texture)\n    }\n\n    pub fn create_texture_view(\n        self: &Arc<Self>,\n        texture: &Arc<Texture>,\n        desc: &resource::TextureViewDescriptor,\n    ) -> Result<Arc<TextureView>, resource::CreateTextureViewError> {\n        self.check_is_valid()?;\n\n        let snatch_guard = texture.device.snatchable_lock.read();\n\n        let texture_raw = texture.try_raw(&snatch_guard)?;\n\n        // resolve TextureViewDescriptor defaults\n        // https://gpuweb.github.io/gpuweb/#abstract-opdef-resolving-gputextureviewdescriptor-defaults\n        let resolved_format = desc.format.unwrap_or_else(|| {\n            texture\n                .desc\n                .format\n                .aspect_specific_format(desc.range.aspect)\n                .unwrap_or(texture.desc.format)\n        });\n\n        let resolved_dimension = desc\n            .dimension\n            .unwrap_or_else(|| match texture.desc.dimension {\n                wgt::TextureDimension::D1 => TextureViewDimension::D1,\n                wgt::TextureDimension::D2 => {\n                    if texture.desc.array_layer_count() == 1 {\n                        TextureViewDimension::D2\n                    } else {\n                        TextureViewDimension::D2Array\n                    }\n                }\n                wgt::TextureDimension::D3 => TextureViewDimension::D3,\n            });\n\n        let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {\n            texture\n                .desc\n                .mip_level_count\n                .saturating_sub(desc.range.base_mip_level)\n        });\n\n        let resolved_array_layer_count =\n            desc.range\n                .array_layer_count\n                .unwrap_or_else(|| match resolved_dimension {\n                    TextureViewDimension::D1\n                    | TextureViewDimension::D2\n                    | TextureViewDimension::D3 => 1,\n                    TextureViewDimension::Cube => 6,\n                    TextureViewDimension::D2Array | TextureViewDimension::CubeArray => texture\n                        .desc\n                        .array_layer_count()\n                        .saturating_sub(desc.range.base_array_layer),\n                });\n\n        let resolved_usage = {\n            let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());\n            if usage.is_empty() {\n                texture.desc.usage\n            } else if texture.desc.usage.contains(usage) {\n                usage\n            } else {\n                return Err(resource::CreateTextureViewError::InvalidTextureViewUsage {\n                    view: usage,\n                    texture: texture.desc.usage,\n                });\n            }\n        };\n\n        let format_features = self.describe_format_features(resolved_format)?;\n        let allowed_format_usages = format_features.allowed_usages;\n        if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)\n            && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)\n        {\n            return Err(\n                resource::CreateTextureViewError::TextureViewFormatNotRenderable(resolved_format),\n            );\n        }\n\n        if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)\n            && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)\n        {\n            return Err(\n                resource::CreateTextureViewError::TextureViewFormatNotStorage(resolved_format),\n            );\n        }\n\n        // validate TextureViewDescriptor\n\n        let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);\n        if aspects.is_empty() {\n            return Err(resource::CreateTextureViewError::InvalidAspect {\n                texture_format: texture.desc.format,\n                requested_aspect: desc.range.aspect,\n            });\n        }\n\n        let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {\n            resolved_format == texture.desc.format\n                || texture.desc.view_formats.contains(&resolved_format)\n        } else {\n            Some(resolved_format)\n                == texture\n                    .desc\n                    .format\n                    .aspect_specific_format(desc.range.aspect)\n        };\n        if !format_is_good {\n            return Err(resource::CreateTextureViewError::FormatReinterpretation {\n                texture: texture.desc.format,\n                view: resolved_format,\n            });\n        }\n\n        // check if multisampled texture is seen as anything but 2D\n        if texture.desc.sample_count > 1 && resolved_dimension != TextureViewDimension::D2 {\n            // Multisample is allowed on 2D arrays, only if explicitly supported\n            let multisample_array_exception = resolved_dimension == TextureViewDimension::D2Array\n                && self.features.contains(wgt::Features::MULTISAMPLE_ARRAY);\n\n            if !multisample_array_exception {\n                return Err(\n                    resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(\n                        resolved_dimension,\n                    ),\n                );\n            }\n        }\n\n        // check if the dimension is compatible with the texture\n        if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {\n            return Err(\n                resource::CreateTextureViewError::InvalidTextureViewDimension {\n                    view: resolved_dimension,\n                    texture: texture.desc.dimension,\n                },\n            );\n        }\n\n        match resolved_dimension {\n            TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {\n                if resolved_array_layer_count != 1 {\n                    return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {\n                        requested: resolved_array_layer_count,\n                        dim: resolved_dimension,\n                    });\n                }\n            }\n            TextureViewDimension::Cube => {\n                if resolved_array_layer_count != 6 {\n                    return Err(\n                        resource::CreateTextureViewError::InvalidCubemapTextureDepth {\n                            depth: resolved_array_layer_count,\n                        },\n                    );\n                }\n            }\n            TextureViewDimension::CubeArray => {\n                if !resolved_array_layer_count.is_multiple_of(6) {\n                    return Err(\n                        resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {\n                            depth: resolved_array_layer_count,\n                        },\n                    );\n                }\n            }\n            _ => {}\n        }\n\n        match resolved_dimension {\n            TextureViewDimension::Cube | TextureViewDimension::CubeArray => {\n                if texture.desc.size.width != texture.desc.size.height {\n                    return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);\n                }\n            }\n            _ => {}\n        }\n\n        if resolved_mip_level_count == 0 {\n            return Err(resource::CreateTextureViewError::ZeroMipLevelCount);\n        }\n\n        let mip_level_end = desc\n            .range\n            .base_mip_level\n            .saturating_add(resolved_mip_level_count);\n\n        let level_end = texture.desc.mip_level_count;\n        if mip_level_end > level_end {\n            return Err(resource::CreateTextureViewError::TooManyMipLevels {\n                base_mip_level: desc.range.base_mip_level,\n                mip_level_count: resolved_mip_level_count,\n                total: level_end,\n            });\n        }\n\n        if resolved_array_layer_count == 0 {\n            return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);\n        }\n\n        let array_layer_end = desc\n            .range\n            .base_array_layer\n            .saturating_add(resolved_array_layer_count);\n\n        let layer_end = texture.desc.array_layer_count();\n        if array_layer_end > layer_end {\n            return Err(resource::CreateTextureViewError::TooManyArrayLayers {\n                base_array_layer: desc.range.base_array_layer,\n                array_layer_count: resolved_array_layer_count,\n                total: layer_end,\n            });\n        };\n\n        // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view\n        let render_extent = 'error: {\n            if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {\n                break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));\n            }\n\n            let allowed_view_dimensions = [\n                TextureViewDimension::D2,\n                TextureViewDimension::D2Array,\n                TextureViewDimension::D3,\n            ];\n            if !allowed_view_dimensions.contains(&resolved_dimension) {\n                break 'error Err(TextureViewNotRenderableReason::Dimension(\n                    resolved_dimension,\n                ));\n            }\n\n            if resolved_mip_level_count != 1 {\n                break 'error Err(TextureViewNotRenderableReason::MipLevelCount(\n                    resolved_mip_level_count,\n                ));\n            }\n\n            if resolved_array_layer_count != 1\n                && !(self.features.contains(wgt::Features::MULTIVIEW))\n            {\n                break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(\n                    resolved_array_layer_count,\n                ));\n            }\n\n            if !texture.desc.format.is_multi_planar_format()\n                && aspects != hal::FormatAspects::from(texture.desc.format)\n            {\n                break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));\n            }\n\n            Ok(texture\n                .desc\n                .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))\n        };\n\n        // filter the usages based on the other criteria\n        let usage = {\n            let resolved_hal_usage = conv::map_texture_usage(\n                resolved_usage,\n                resolved_format.into(),\n                format_features.flags,\n            );\n            let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);\n            let mask_dimension = match resolved_dimension {\n                TextureViewDimension::Cube | TextureViewDimension::CubeArray => {\n                    wgt::TextureUses::RESOURCE\n                }\n                TextureViewDimension::D3 => {\n                    wgt::TextureUses::RESOURCE\n                        | wgt::TextureUses::STORAGE_READ_ONLY\n                        | wgt::TextureUses::STORAGE_WRITE_ONLY\n                        | wgt::TextureUses::STORAGE_READ_WRITE\n                }\n                _ => wgt::TextureUses::all(),\n            };\n            let mask_mip_level = if resolved_mip_level_count == 1 {\n                wgt::TextureUses::all()\n            } else {\n                wgt::TextureUses::RESOURCE\n            };\n            resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level\n        };\n\n        // use the combined depth-stencil format for the view\n        let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {\n            texture.desc.format\n        } else {\n            resolved_format\n        };\n\n        let resolved_range = wgt::ImageSubresourceRange {\n            aspect: desc.range.aspect,\n            base_mip_level: desc.range.base_mip_level,\n            mip_level_count: Some(resolved_mip_level_count),\n            base_array_layer: desc.range.base_array_layer,\n            array_layer_count: Some(resolved_array_layer_count),\n        };\n\n        let hal_desc = hal::TextureViewDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            format,\n            dimension: resolved_dimension,\n            usage,\n            range: resolved_range,\n        };\n\n        let raw = unsafe { self.raw().create_texture_view(texture_raw, &hal_desc) }\n            .map_err(|e| self.handle_hal_error(e))?;\n\n        let selector = TextureSelector {\n            mips: desc.range.base_mip_level..mip_level_end,\n            layers: desc.range.base_array_layer..array_layer_end,\n        };\n\n        let view = TextureView {\n            raw: Snatchable::new(raw),\n            parent: texture.clone(),\n            device: self.clone(),\n            desc: resource::HalTextureViewDescriptor {\n                texture_format: texture.desc.format,\n                format: resolved_format,\n                dimension: resolved_dimension,\n                usage: resolved_usage,\n                range: resolved_range,\n            },\n            format_features: texture.format_features,\n            render_extent,\n            samples: texture.desc.sample_count,\n            selector,\n            label: desc.label.to_string(),\n        };\n\n        let view = Arc::new(view);\n\n        {\n            let mut views = texture.views.lock();\n            views.push(Arc::downgrade(&view));\n        }\n\n        Ok(view)\n    }\n\n    pub fn create_external_texture(\n        self: &Arc<Self>,\n        desc: &resource::ExternalTextureDescriptor,\n        planes: &[Arc<TextureView>],\n    ) -> Result<Arc<ExternalTexture>, resource::CreateExternalTextureError> {\n        use resource::CreateExternalTextureError;\n        self.require_features(wgt::Features::EXTERNAL_TEXTURE)?;\n        self.check_is_valid()?;\n\n        if desc.num_planes() != planes.len() {\n            return Err(CreateExternalTextureError::IncorrectPlaneCount {\n                format: desc.format,\n                expected: desc.num_planes(),\n                provided: planes.len(),\n            });\n        }\n\n        let planes = planes\n            .iter()\n            .enumerate()\n            .map(|(i, plane)| {\n                if plane.samples != 1 {\n                    return Err(CreateExternalTextureError::InvalidPlaneMultisample(\n                        plane.samples,\n                    ));\n                }\n\n                let sample_type = plane\n                    .desc\n                    .format\n                    .sample_type(Some(plane.desc.range.aspect), Some(self.features))\n                    .unwrap();\n                if !matches!(sample_type, TextureSampleType::Float { filterable: true }) {\n                    return Err(CreateExternalTextureError::InvalidPlaneSampleType {\n                        format: plane.desc.format,\n                        sample_type,\n                    });\n                }\n\n                if plane.desc.dimension != TextureViewDimension::D2 {\n                    return Err(CreateExternalTextureError::InvalidPlaneDimension(\n                        plane.desc.dimension,\n                    ));\n                }\n\n                let expected_components = match desc.format {\n                    wgt::ExternalTextureFormat::Rgba => 4,\n                    wgt::ExternalTextureFormat::Nv12 => match i {\n                        0 => 1,\n                        1 => 2,\n                        _ => unreachable!(),\n                    },\n                    wgt::ExternalTextureFormat::Yu12 => 1,\n                };\n                if plane.desc.format.components() != expected_components {\n                    return Err(CreateExternalTextureError::InvalidPlaneFormat {\n                        format: desc.format,\n                        plane: i,\n                        expected: expected_components,\n                        provided: plane.desc.format,\n                    });\n                }\n\n                plane.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;\n                Ok(plane.clone())\n            })\n            .collect::<Result<_, _>>()?;\n\n        let params_data = ExternalTextureParams::from_desc(desc);\n        let label = desc.label.as_ref().map(|l| alloc::format!(\"{l} params\"));\n        let params_desc = resource::BufferDescriptor {\n            label: label.map(Cow::Owned),\n            size: size_of_val(&params_data) as wgt::BufferAddress,\n            usage: wgt::BufferUsages::UNIFORM | wgt::BufferUsages::COPY_DST,\n            mapped_at_creation: false,\n        };\n        let params = self.create_buffer(&params_desc)?;\n        self.get_queue().unwrap().write_buffer(\n            params.clone(),\n            0,\n            bytemuck::bytes_of(&params_data),\n        )?;\n\n        let external_texture = ExternalTexture {\n            device: self.clone(),\n            planes,\n            params,\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.external_textures.clone()),\n        };\n        let external_texture = Arc::new(external_texture);\n\n        Ok(external_texture)\n    }\n\n    pub fn create_sampler(\n        self: &Arc<Self>,\n        desc: &resource::SamplerDescriptor,\n    ) -> Result<Arc<Sampler>, resource::CreateSamplerError> {\n        self.check_is_valid()?;\n\n        if desc\n            .address_modes\n            .iter()\n            .any(|am| am == &wgt::AddressMode::ClampToBorder)\n        {\n            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;\n        }\n\n        if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {\n            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;\n        }\n\n        if desc.lod_min_clamp < 0.0 {\n            return Err(resource::CreateSamplerError::InvalidLodMinClamp(\n                desc.lod_min_clamp,\n            ));\n        }\n        if desc.lod_max_clamp < desc.lod_min_clamp {\n            return Err(resource::CreateSamplerError::InvalidLodMaxClamp {\n                lod_min_clamp: desc.lod_min_clamp,\n                lod_max_clamp: desc.lod_max_clamp,\n            });\n        }\n\n        if desc.anisotropy_clamp < 1 {\n            return Err(resource::CreateSamplerError::InvalidAnisotropy(\n                desc.anisotropy_clamp,\n            ));\n        }\n\n        if desc.anisotropy_clamp != 1 {\n            if !matches!(desc.min_filter, wgt::FilterMode::Linear) {\n                return Err(\n                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {\n                        filter_type: resource::SamplerFilterErrorType::MinFilter,\n                        filter_mode: desc.min_filter,\n                        anisotropic_clamp: desc.anisotropy_clamp,\n                    },\n                );\n            }\n            if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {\n                return Err(\n                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {\n                        filter_type: resource::SamplerFilterErrorType::MagFilter,\n                        filter_mode: desc.mag_filter,\n                        anisotropic_clamp: desc.anisotropy_clamp,\n                    },\n                );\n            }\n            if !matches!(desc.mipmap_filter, wgt::MipmapFilterMode::Linear) {\n                return Err(\n                    resource::CreateSamplerError::InvalidMipmapFilterModeWithAnisotropy {\n                        filter_type: resource::SamplerFilterErrorType::MipmapFilter,\n                        filter_mode: desc.mipmap_filter,\n                        anisotropic_clamp: desc.anisotropy_clamp,\n                    },\n                );\n            }\n        }\n\n        let anisotropy_clamp = if self\n            .downlevel\n            .flags\n            .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)\n        {\n            // Clamp anisotropy clamp to [1, 16] per the wgpu-hal interface\n            desc.anisotropy_clamp.min(16)\n        } else {\n            // If it isn't supported, set this unconditionally to 1\n            1\n        };\n\n        //TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS\n\n        let hal_desc = hal::SamplerDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            address_modes: desc.address_modes,\n            mag_filter: desc.mag_filter,\n            min_filter: desc.min_filter,\n            mipmap_filter: desc.mipmap_filter,\n            lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,\n            compare: desc.compare,\n            anisotropy_clamp,\n            border_color: desc.border_color,\n        };\n\n        let raw = unsafe { self.raw().create_sampler(&hal_desc) }\n            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let sampler = Sampler {\n            raw: ManuallyDrop::new(raw),\n            device: self.clone(),\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.samplers.clone()),\n            comparison: desc.compare.is_some(),\n            filtering: desc.min_filter == wgt::FilterMode::Linear\n                || desc.mag_filter == wgt::FilterMode::Linear\n                || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,\n        };\n\n        let sampler = Arc::new(sampler);\n\n        Ok(sampler)\n    }\n\n    pub fn create_shader_module<'a>(\n        self: &Arc<Self>,\n        desc: &pipeline::ShaderModuleDescriptor<'a>,\n        source: pipeline::ShaderModuleSource<'a>,\n    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {\n        self.check_is_valid()?;\n\n        let (module, source) = match source {\n            #[cfg(feature = \"wgsl\")]\n            pipeline::ShaderModuleSource::Wgsl(code) => {\n                profiling::scope!(\"naga::front::wgsl::parse\");\n                let capabilities =\n                    features_to_naga_capabilities(self.features, self.downlevel.flags);\n                let mut options = naga::front::wgsl::Options::new();\n                options.capabilities = capabilities;\n                let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);\n                let module = frontend.parse(&code).map_err(|inner| {\n                    pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {\n                        source: code.to_string(),\n                        label: desc.label.as_ref().map(|l| l.to_string()),\n                        inner: Box::new(inner),\n                    })\n                })?;\n                (Cow::Owned(module), code.into_owned())\n            }\n            #[cfg(feature = \"spirv\")]\n            pipeline::ShaderModuleSource::SpirV(spv, options) => {\n                let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);\n                profiling::scope!(\"naga::front::spv::Frontend\");\n                let module = parser.parse().map_err(|inner| {\n                    pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {\n                        source: String::new(),\n                        label: desc.label.as_ref().map(|l| l.to_string()),\n                        inner: Box::new(inner),\n                    })\n                })?;\n                (Cow::Owned(module), String::new())\n            }\n            #[cfg(feature = \"glsl\")]\n            pipeline::ShaderModuleSource::Glsl(code, options) => {\n                let mut parser = naga::front::glsl::Frontend::default();\n                profiling::scope!(\"naga::front::glsl::Frontend.parse\");\n                let module = parser.parse(&options, &code).map_err(|inner| {\n                    pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {\n                        source: code.to_string(),\n                        label: desc.label.as_ref().map(|l| l.to_string()),\n                        inner: Box::new(inner),\n                    })\n                })?;\n                (Cow::Owned(module), code.into_owned())\n            }\n            pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),\n            pipeline::ShaderModuleSource::Dummy(_) => panic!(\"found `ShaderModuleSource::Dummy`\"),\n        };\n        for (_, var) in module.global_variables.iter() {\n            match var.binding {\n                Some(br) if br.group >= self.limits.max_bind_groups => {\n                    return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {\n                        bind: br,\n                        group: br.group,\n                        limit: self.limits.max_bind_groups,\n                    });\n                }\n                _ => continue,\n            };\n        }\n\n        profiling::scope!(\"naga::validate\");\n        let debug_source =\n            if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {\n                Some(hal::DebugSource {\n                    file_name: Cow::Owned(\n                        desc.label\n                            .as_ref()\n                            .map_or(\"shader\".to_string(), |l| l.to_string()),\n                    ),\n                    source_code: Cow::Owned(source.clone()),\n                })\n            } else {\n                None\n            };\n\n        let info = create_validator(\n            self.features,\n            self.downlevel.flags,\n            naga::valid::ValidationFlags::all(),\n        )\n        .validate(&module)\n        .map_err(|inner| {\n            pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {\n                source,\n                label: desc.label.as_ref().map(|l| l.to_string()),\n                inner: Box::new(inner),\n            })\n        })?;\n\n        let interface = validation::Interface::new(&module, &info, self.limits.clone());\n        let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {\n            module,\n            info,\n            debug_source,\n        });\n        let hal_desc = hal::ShaderModuleDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            runtime_checks: desc.runtime_checks,\n        };\n        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {\n            Ok(raw) => raw,\n            Err(error) => {\n                return Err(match error {\n                    hal::ShaderError::Device(error) => {\n                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))\n                    }\n                    hal::ShaderError::Compilation(ref msg) => {\n                        log::error!(\"Shader error: {msg}\");\n                        pipeline::CreateShaderModuleError::Generation\n                    }\n                })\n            }\n        };\n\n        let module = pipeline::ShaderModule {\n            raw: ManuallyDrop::new(raw),\n            device: self.clone(),\n            interface: Some(interface),\n            label: desc.label.to_string(),\n        };\n\n        let module = Arc::new(module);\n\n        Ok(module)\n    }\n\n    /// Not a public API. For use by `player` only.\n    #[allow(unused_unsafe)]\n    #[doc(hidden)]\n    pub unsafe fn create_shader_module_passthrough<'a>(\n        self: &Arc<Self>,\n        descriptor: &pipeline::ShaderModuleDescriptorPassthrough<'a>,\n    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {\n        self.check_is_valid()?;\n        self.require_features(wgt::Features::PASSTHROUGH_SHADERS)?;\n\n        let hal_shader = match self.backend() {\n            wgt::Backend::Vulkan => hal::ShaderInput::SpirV(\n                descriptor\n                    .spirv\n                    .as_ref()\n                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,\n            ),\n            wgt::Backend::Dx12 => {\n                if let Some(dxil) = &descriptor.dxil {\n                    hal::ShaderInput::Dxil {\n                        shader: dxil,\n                        num_workgroups: descriptor.num_workgroups,\n                    }\n                } else if let Some(hlsl) = &descriptor.hlsl {\n                    hal::ShaderInput::Hlsl {\n                        shader: hlsl,\n                        num_workgroups: descriptor.num_workgroups,\n                    }\n                } else {\n                    return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);\n                }\n            }\n            wgt::Backend::Metal => {\n                if let Some(metallib) = &descriptor.metallib {\n                    hal::ShaderInput::MetalLib {\n                        file: metallib,\n                        num_workgroups: descriptor.num_workgroups,\n                    }\n                } else if let Some(msl) = &descriptor.msl {\n                    hal::ShaderInput::Msl {\n                        shader: msl,\n                        num_workgroups: descriptor.num_workgroups,\n                    }\n                } else {\n                    return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);\n                }\n            }\n            wgt::Backend::Gl => hal::ShaderInput::Glsl {\n                shader: descriptor\n                    .glsl\n                    .as_ref()\n                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,\n                num_workgroups: descriptor.num_workgroups,\n            },\n            wgt::Backend::Noop => {\n                return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend)\n            }\n            wgt::Backend::BrowserWebGpu => unreachable!(),\n        };\n\n        let hal_desc = hal::ShaderModuleDescriptor {\n            label: descriptor.label.to_hal(self.instance_flags),\n            runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),\n        };\n\n        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {\n            Ok(raw) => raw,\n            Err(error) => {\n                return Err(match error {\n                    hal::ShaderError::Device(error) => {\n                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))\n                    }\n                    hal::ShaderError::Compilation(ref msg) => {\n                        log::error!(\"Shader error: {msg}\");\n                        pipeline::CreateShaderModuleError::Generation\n                    }\n                })\n            }\n        };\n\n        let module = pipeline::ShaderModule {\n            raw: ManuallyDrop::new(raw),\n            device: self.clone(),\n            interface: None,\n            label: descriptor.label.to_string(),\n        };\n\n        Ok(Arc::new(module))\n    }\n\n    pub(crate) fn create_command_encoder(\n        self: &Arc<Self>,\n        label: &crate::Label,\n    ) -> Result<Arc<command::CommandEncoder>, DeviceError> {\n        self.check_is_valid()?;\n\n        let queue = self.get_queue().unwrap();\n\n        let encoder = self\n            .command_allocator\n            .acquire_encoder(self.raw(), queue.raw())\n            .map_err(|e| self.handle_hal_error(e))?;\n\n        let cmd_enc = command::CommandEncoder::new(encoder, self, label);\n\n        let cmd_enc = Arc::new(cmd_enc);\n\n        Ok(cmd_enc)\n    }\n\n    /// Generate information about late-validated buffer bindings for pipelines.\n    //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way?\n    fn make_late_sized_buffer_groups(\n        shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,\n        layout: &binding_model::PipelineLayout,\n    ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {\n        // Given the shader-required binding sizes and the pipeline layout,\n        // return the filtered list of them in the layout order,\n        // removing those with given `min_binding_size`.\n        layout\n            .bind_group_layouts\n            .iter()\n            .enumerate()\n            .map(|(group_index, bgl)| {\n                let Some(bgl) = bgl else {\n                    return pipeline::LateSizedBufferGroup::default();\n                };\n\n                let shader_sizes = bgl\n                    .entries\n                    .values()\n                    .filter_map(|entry| match entry.ty {\n                        wgt::BindingType::Buffer {\n                            min_binding_size: None,\n                            ..\n                        } => {\n                            let rb = naga::ResourceBinding {\n                                group: group_index as u32,\n                                binding: entry.binding,\n                            };\n                            let shader_size =\n                                shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());\n                            Some(shader_size)\n                        }\n                        _ => None,\n                    })\n                    .collect();\n                pipeline::LateSizedBufferGroup { shader_sizes }\n            })\n            .collect()\n    }\n\n    pub fn create_bind_group_layout(\n        self: &Arc<Self>,\n        desc: &binding_model::BindGroupLayoutDescriptor,\n    ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {\n        self.check_is_valid()?;\n\n        let entry_map = bgl::EntryMap::from_entries(&desc.entries)?;\n\n        let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| {\n            let bgl =\n                self.create_bind_group_layout_internal(&desc.label, entry_map, bgl::Origin::Pool)?;\n            bgl.exclusive_pipeline\n                .set(binding_model::ExclusivePipeline::None)\n                .unwrap();\n            Ok(bgl)\n        });\n\n        match bgl_result {\n            Ok(layout) => Ok(layout),\n            Err(e) => Err(e),\n        }\n    }\n\n    fn create_bind_group_layout_internal(\n        self: &Arc<Self>,\n        label: &crate::Label,\n        entry_map: bgl::EntryMap,\n        origin: bgl::Origin,\n    ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {\n        #[derive(PartialEq)]\n        enum WritableStorage {\n            Yes,\n            No,\n        }\n\n        for entry in entry_map.values() {\n            if entry.binding >= self.limits.max_bindings_per_bind_group {\n                return Err(CreateBindGroupLayoutError::InvalidBindingIndex {\n                    binding: entry.binding,\n                    maximum: self.limits.max_bindings_per_bind_group,\n                });\n            }\n\n            use wgt::BindingType as Bt;\n\n            let mut required_features = wgt::Features::empty();\n            let mut required_downlevel_flags = wgt::DownlevelFlags::empty();\n            let (array_feature, writable_storage) = match entry.ty {\n                Bt::Buffer {\n                    ty: wgt::BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: _,\n                } => (\n                    Some(wgt::Features::BUFFER_BINDING_ARRAY),\n                    WritableStorage::No,\n                ),\n                Bt::Buffer {\n                    ty: wgt::BufferBindingType::Uniform,\n                    has_dynamic_offset: true,\n                    min_binding_size: _,\n                } => (\n                    Some(wgt::Features::BUFFER_BINDING_ARRAY),\n                    WritableStorage::No,\n                ),\n                Bt::Buffer {\n                    ty: wgt::BufferBindingType::Storage { read_only },\n                    ..\n                } => (\n                    Some(\n                        wgt::Features::BUFFER_BINDING_ARRAY\n                            | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,\n                    ),\n                    match read_only {\n                        true => WritableStorage::No,\n                        false => WritableStorage::Yes,\n                    },\n                ),\n                Bt::Sampler { .. } => (\n                    Some(wgt::Features::TEXTURE_BINDING_ARRAY),\n                    WritableStorage::No,\n                ),\n                Bt::Texture {\n                    multisampled: true,\n                    sample_type: TextureSampleType::Float { filterable: true },\n                    ..\n                } => {\n                    return Err(CreateBindGroupLayoutError::Entry {\n                        binding: entry.binding,\n                        error:\n                            BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,\n                    });\n                }\n                Bt::Texture {\n                    multisampled,\n                    view_dimension,\n                    ..\n                } => {\n                    if multisampled && view_dimension != TextureViewDimension::D2 {\n                        return Err(CreateBindGroupLayoutError::Entry {\n                            binding: entry.binding,\n                            error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),\n                        });\n                    }\n\n                    (\n                        Some(wgt::Features::TEXTURE_BINDING_ARRAY),\n                        WritableStorage::No,\n                    )\n                }\n                Bt::StorageTexture {\n                    access,\n                    view_dimension,\n                    format,\n                } => {\n                    use wgt::{StorageTextureAccess as Access, TextureFormatFeatureFlags as Flags};\n\n                    match view_dimension {\n                        TextureViewDimension::Cube | TextureViewDimension::CubeArray => {\n                            return Err(CreateBindGroupLayoutError::Entry {\n                                binding: entry.binding,\n                                error: BindGroupLayoutEntryError::StorageTextureCube,\n                            })\n                        }\n                        _ => (),\n                    }\n                    match access {\n                        wgt::StorageTextureAccess::Atomic\n                            if !self.features.contains(wgt::Features::TEXTURE_ATOMIC) =>\n                        {\n                            return Err(CreateBindGroupLayoutError::Entry {\n                                binding: entry.binding,\n                                error: BindGroupLayoutEntryError::StorageTextureAtomic,\n                            });\n                        }\n                        _ => (),\n                    }\n\n                    let format_features =\n                        self.describe_format_features(format).map_err(|error| {\n                            CreateBindGroupLayoutError::Entry {\n                                binding: entry.binding,\n                                error: BindGroupLayoutEntryError::MissingFeatures(error),\n                            }\n                        })?;\n\n                    let required_feature_flag = match access {\n                        Access::WriteOnly => Flags::STORAGE_WRITE_ONLY,\n                        Access::ReadOnly => Flags::STORAGE_READ_ONLY,\n                        Access::ReadWrite => Flags::STORAGE_READ_WRITE,\n                        Access::Atomic => Flags::STORAGE_ATOMIC,\n                    };\n\n                    if !format_features.flags.contains(required_feature_flag) {\n                        return Err(\n                            CreateBindGroupLayoutError::UnsupportedStorageTextureAccess {\n                                binding: entry.binding,\n                                access,\n                                format,\n                            },\n                        );\n                    }\n\n                    (\n                        Some(\n                            wgt::Features::TEXTURE_BINDING_ARRAY\n                                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,\n                        ),\n                        match access {\n                            wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,\n                            wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,\n                            wgt::StorageTextureAccess::ReadWrite => WritableStorage::Yes,\n                            wgt::StorageTextureAccess::Atomic => {\n                                required_features |= wgt::Features::TEXTURE_ATOMIC;\n                                WritableStorage::Yes\n                            }\n                        },\n                    )\n                }\n                Bt::AccelerationStructure { vertex_return } => {\n                    self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)\n                        .map_err(|e| CreateBindGroupLayoutError::Entry {\n                            binding: entry.binding,\n                            error: e.into(),\n                        })?;\n                    if vertex_return {\n                        self.require_features(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)\n                            .map_err(|e| CreateBindGroupLayoutError::Entry {\n                                binding: entry.binding,\n                                error: e.into(),\n                            })?;\n                    }\n                    (\n                        Some(wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY),\n                        WritableStorage::No,\n                    )\n                }\n                Bt::ExternalTexture => {\n                    self.require_features(wgt::Features::EXTERNAL_TEXTURE)\n                        .map_err(|e| CreateBindGroupLayoutError::Entry {\n                            binding: entry.binding,\n                            error: e.into(),\n                        })?;\n                    (None, WritableStorage::No)\n                }\n            };\n\n            // Validate the count parameter\n            if entry.count.is_some() {\n                required_features |= array_feature\n                    .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)\n                    .map_err(|error| CreateBindGroupLayoutError::Entry {\n                        binding: entry.binding,\n                        error,\n                    })?;\n            }\n\n            if entry.visibility.contains_unknown_bits() {\n                return Err(CreateBindGroupLayoutError::InvalidVisibility(\n                    entry.visibility,\n                ));\n            }\n\n            if entry.visibility.contains(wgt::ShaderStages::VERTEX) {\n                if writable_storage == WritableStorage::Yes {\n                    required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;\n                }\n                if let Bt::Buffer {\n                    ty: wgt::BufferBindingType::Storage { .. },\n                    ..\n                } = entry.ty\n                {\n                    required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;\n                }\n            }\n            if writable_storage == WritableStorage::Yes\n                && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)\n            {\n                required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;\n            }\n\n            self.require_features(required_features)\n                .map_err(BindGroupLayoutEntryError::MissingFeatures)\n                .map_err(|error| CreateBindGroupLayoutError::Entry {\n                    binding: entry.binding,\n                    error,\n                })?;\n            self.require_downlevel_flags(required_downlevel_flags)\n                .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)\n                .map_err(|error| CreateBindGroupLayoutError::Entry {\n                    binding: entry.binding,\n                    error,\n                })?;\n        }\n\n        let bgl_flags = conv::bind_group_layout_flags(self.features);\n\n        let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();\n        let hal_desc = hal::BindGroupLayoutDescriptor {\n            label: label.to_hal(self.instance_flags),\n            flags: bgl_flags,\n            entries: &hal_bindings,\n        };\n\n        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();\n        for entry in entry_map.values() {\n            count_validator.add_binding(entry);\n        }\n        // If a single bind group layout violates limits, the pipeline layout is\n        // definitely going to violate limits too, lets catch it now.\n        count_validator\n            .validate(&self.limits)\n            .map_err(CreateBindGroupLayoutError::TooManyBindings)?;\n\n        // Validate that binding arrays don't conflict with dynamic offsets.\n        count_validator.validate_binding_arrays()?;\n\n        let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }\n            .map_err(|e| self.handle_hal_error(e))?;\n\n        let bgl = BindGroupLayout {\n            raw: binding_model::RawBindGroupLayout::Owning(ManuallyDrop::new(raw)),\n            device: self.clone(),\n            entries: entry_map,\n            origin,\n            exclusive_pipeline: OnceCellOrLock::new(),\n            binding_count_validator: count_validator,\n            label: label.to_string(),\n        };\n\n        let bgl = Arc::new(bgl);\n\n        Ok(bgl)\n    }\n\n    fn create_buffer_binding<'a>(\n        &self,\n        bb: &'a binding_model::ResolvedBufferBinding,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,\n        dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,\n        late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,\n        used: &mut BindGroupStates,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, CreateBindGroupError> {\n        use crate::binding_model::CreateBindGroupError as Error;\n\n        let (binding_ty, dynamic, min_size) = match decl.ty {\n            wgt::BindingType::Buffer {\n                ty,\n                has_dynamic_offset,\n                min_binding_size,\n            } => (ty, has_dynamic_offset, min_binding_size),\n            _ => {\n                return Err(Error::WrongBindingType {\n                    binding,\n                    actual: decl.ty,\n                    expected: \"UniformBuffer, StorageBuffer or ReadonlyStorageBuffer\",\n                })\n            }\n        };\n\n        let (pub_usage, internal_use, range_limit) = match binding_ty {\n            wgt::BufferBindingType::Uniform => (\n                wgt::BufferUsages::UNIFORM,\n                wgt::BufferUses::UNIFORM,\n                self.limits.max_uniform_buffer_binding_size,\n            ),\n            wgt::BufferBindingType::Storage { read_only } => (\n                wgt::BufferUsages::STORAGE,\n                if read_only {\n                    wgt::BufferUses::STORAGE_READ_ONLY\n                } else {\n                    wgt::BufferUses::STORAGE_READ_WRITE\n                },\n                self.limits.max_storage_buffer_binding_size,\n            ),\n        };\n\n        let (align, align_limit_name) =\n            binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);\n        if !bb.offset.is_multiple_of(align as u64) {\n            return Err(Error::UnalignedBufferOffset(\n                bb.offset,\n                align_limit_name,\n                align,\n            ));\n        }\n\n        let buffer = &bb.buffer;\n\n        used.buffers.insert_single(buffer.clone(), internal_use);\n\n        buffer.same_device(self)?;\n\n        buffer.check_usage(pub_usage)?;\n\n        let req_size = match bb.size.map(wgt::BufferSize::new) {\n            // Requested a non-zero size\n            Some(non_zero @ Some(_)) => non_zero,\n            // Requested size not specified\n            None => None,\n            // Requested zero size\n            Some(None) => return Err(CreateBindGroupError::BindingZeroSize(buffer.error_ident())),\n        };\n        let (bb, bind_size) = buffer.binding(bb.offset, req_size, snatch_guard)?;\n\n        if matches!(binding_ty, wgt::BufferBindingType::Storage { .. })\n            && bind_size % u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) != 0\n        {\n            return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {\n                alignment: wgt::STORAGE_BINDING_SIZE_ALIGNMENT,\n                size: bind_size,\n            });\n        }\n\n        let bind_end = bb.offset + bind_size;\n\n        if bind_size > range_limit {\n            return Err(Error::BufferRangeTooLarge {\n                binding,\n                given: bind_size,\n                limit: range_limit,\n            });\n        }\n\n        // Record binding info for validating dynamic offsets\n        if dynamic {\n            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {\n                binding_idx: binding,\n                buffer_size: buffer.size,\n                binding_range: bb.offset..bind_end,\n                maximum_dynamic_offset: buffer.size - bind_end,\n                binding_type: binding_ty,\n            });\n        }\n\n        if let Some(non_zero) = min_size {\n            let min_size = non_zero.get();\n            if min_size > bind_size {\n                return Err(Error::BindingSizeTooSmall {\n                    buffer: buffer.error_ident(),\n                    actual: bind_size,\n                    min: min_size,\n                });\n            }\n        } else {\n            let late_size = wgt::BufferSize::new(bind_size)\n                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;\n            late_buffer_binding_sizes.insert(binding, late_size);\n        }\n\n        // This was checked against the device's alignment requirements above,\n        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.\n        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);\n\n        // `wgpu_hal` only restricts shader access to bound buffer regions with\n        // a certain resolution. For the sake of lazy initialization, round up\n        // the size of the bound range to reflect how much of the buffer is\n        // actually going to be visible to the shader.\n        let bounds_check_alignment =\n            binding_model::buffer_binding_type_bounds_check_alignment(&self.alignments, binding_ty);\n        let visible_size = align_to(bind_size, bounds_check_alignment);\n\n        used_buffer_ranges.extend(buffer.initialization_status.read().create_action(\n            buffer,\n            bb.offset..bb.offset + visible_size,\n            MemoryInitKind::NeedsInitializedMemory,\n        ));\n\n        Ok(bb)\n    }\n\n    fn create_sampler_binding<'a>(\n        &self,\n        used: &mut BindGroupStates,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        sampler: &'a Arc<Sampler>,\n    ) -> Result<&'a dyn hal::DynSampler, CreateBindGroupError> {\n        use crate::binding_model::CreateBindGroupError as Error;\n\n        used.samplers.insert_single(sampler.clone());\n\n        sampler.same_device(self)?;\n\n        match decl.ty {\n            wgt::BindingType::Sampler(ty) => {\n                let (allowed_filtering, allowed_comparison) = match ty {\n                    wgt::SamplerBindingType::Filtering => (None, false),\n                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),\n                    wgt::SamplerBindingType::Comparison => (None, true),\n                };\n                if let Some(allowed_filtering) = allowed_filtering {\n                    if allowed_filtering != sampler.filtering {\n                        return Err(Error::WrongSamplerFiltering {\n                            binding,\n                            layout_flt: allowed_filtering,\n                            sampler_flt: sampler.filtering,\n                        });\n                    }\n                }\n                if allowed_comparison != sampler.comparison {\n                    return Err(Error::WrongSamplerComparison {\n                        binding,\n                        layout_cmp: allowed_comparison,\n                        sampler_cmp: sampler.comparison,\n                    });\n                }\n            }\n            _ => {\n                return Err(Error::WrongBindingType {\n                    binding,\n                    actual: decl.ty,\n                    expected: \"Sampler\",\n                })\n            }\n        }\n\n        Ok(sampler.raw())\n    }\n\n    fn create_texture_binding<'a>(\n        &self,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        view: &'a Arc<TextureView>,\n        used: &mut BindGroupStates,\n        used_texture_ranges: &mut Vec<TextureInitTrackerAction>,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, CreateBindGroupError> {\n        view.same_device(self)?;\n\n        let internal_use = self.texture_use_parameters(\n            binding,\n            decl,\n            view,\n            \"SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture\",\n        )?;\n\n        used.views.insert_single(view.clone(), internal_use);\n\n        let texture = &view.parent;\n\n        used_texture_ranges.push(TextureInitTrackerAction {\n            texture: texture.clone(),\n            range: TextureInitRange {\n                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),\n                layer_range: view\n                    .desc\n                    .range\n                    .layer_range(texture.desc.array_layer_count()),\n            },\n            kind: MemoryInitKind::NeedsInitializedMemory,\n        });\n\n        Ok(hal::TextureBinding {\n            view: view.try_raw(snatch_guard)?,\n            usage: internal_use,\n        })\n    }\n\n    fn create_tlas_binding<'a>(\n        self: &Arc<Self>,\n        used: &mut BindGroupStates,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        tlas: &'a Arc<Tlas>,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> Result<&'a dyn hal::DynAccelerationStructure, CreateBindGroupError> {\n        use crate::binding_model::CreateBindGroupError as Error;\n\n        used.acceleration_structures.insert_single(tlas.clone());\n\n        tlas.same_device(self)?;\n\n        match decl.ty {\n            wgt::BindingType::AccelerationStructure { vertex_return } => {\n                if vertex_return\n                    && !tlas.flags.contains(\n                        wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,\n                    )\n                {\n                    return Err(Error::MissingTLASVertexReturn { binding });\n                }\n            }\n            _ => {\n                return Err(Error::WrongBindingType {\n                    binding,\n                    actual: decl.ty,\n                    expected: \"Tlas\",\n                });\n            }\n        }\n\n        Ok(tlas.try_raw(snatch_guard)?)\n    }\n\n    fn create_external_texture_binding<'a>(\n        &'a self,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        external_texture: &'a Arc<ExternalTexture>,\n        used: &mut BindGroupStates,\n        snatch_guard: &'a SnatchGuard,\n    ) -> Result<\n        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,\n        CreateBindGroupError,\n    > {\n        use crate::binding_model::CreateBindGroupError as Error;\n\n        external_texture.same_device(self)?;\n\n        used.external_textures\n            .insert_single(external_texture.clone());\n\n        match decl.ty {\n            wgt::BindingType::ExternalTexture => {}\n            _ => {\n                return Err(Error::WrongBindingType {\n                    binding,\n                    actual: decl.ty,\n                    expected: \"ExternalTexture\",\n                });\n            }\n        }\n\n        let planes = (0..3)\n            .map(|i| {\n                // We always need 3 bindings. If we have fewer than 3 planes\n                // just bind plane 0 multiple times. The shader will only\n                // sample from valid planes anyway.\n                let plane = external_texture\n                    .planes\n                    .get(i)\n                    .unwrap_or(&external_texture.planes[0]);\n                let internal_use = wgt::TextureUses::RESOURCE;\n                used.views.insert_single(plane.clone(), internal_use);\n                let view = plane.try_raw(snatch_guard)?;\n                Ok(hal::TextureBinding {\n                    view,\n                    usage: internal_use,\n                })\n            })\n            // We can remove this intermediate Vec by using\n            // array::try_from_fn() above, once it stabilizes.\n            .collect::<Result<Vec<_>, Error>>()?;\n        let planes = planes.try_into().unwrap();\n\n        used.buffers\n            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);\n        let params = external_texture.params.binding(0, None, snatch_guard)?.0;\n\n        Ok(hal::ExternalTextureBinding { planes, params })\n    }\n\n    fn create_external_texture_binding_from_view<'a>(\n        &'a self,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        view: &'a Arc<TextureView>,\n        used: &mut BindGroupStates,\n        snatch_guard: &'a SnatchGuard,\n    ) -> Result<\n        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,\n        CreateBindGroupError,\n    > {\n        use crate::binding_model::CreateBindGroupError as Error;\n\n        view.same_device(self)?;\n\n        let internal_use = self.texture_use_parameters(binding, decl, view, \"SampledTexture\")?;\n        used.views.insert_single(view.clone(), internal_use);\n\n        match decl.ty {\n            wgt::BindingType::ExternalTexture => {}\n            _ => {\n                return Err(Error::WrongBindingType {\n                    binding,\n                    actual: decl.ty,\n                    expected: \"ExternalTexture\",\n                });\n            }\n        }\n\n        // We need 3 bindings, so just repeat the same texture view 3 times.\n        let planes = [\n            hal::TextureBinding {\n                view: view.try_raw(snatch_guard)?,\n                usage: internal_use,\n            },\n            hal::TextureBinding {\n                view: view.try_raw(snatch_guard)?,\n                usage: internal_use,\n            },\n            hal::TextureBinding {\n                view: view.try_raw(snatch_guard)?,\n                usage: internal_use,\n            },\n        ];\n        let params = hal::BufferBinding::new_unchecked(\n            self.default_external_texture_params_buffer.as_ref(),\n            0,\n            None,\n        );\n\n        Ok(hal::ExternalTextureBinding { planes, params })\n    }\n\n    // This function expects the provided bind group layout to be resolved\n    // (not passing a duplicate) beforehand.\n    pub fn create_bind_group(\n        self: &Arc<Self>,\n        desc: binding_model::ResolvedBindGroupDescriptor,\n    ) -> Result<Arc<BindGroup>, CreateBindGroupError> {\n        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};\n\n        let layout = desc.layout;\n\n        self.check_is_valid()?;\n        layout.same_device(self)?;\n\n        {\n            // Check that the number of entries in the descriptor matches\n            // the number of entries in the layout.\n            let actual = desc.entries.len();\n            let expected = layout.entries.len();\n            if actual != expected {\n                return Err(Error::BindingsNumMismatch { expected, actual });\n            }\n        }\n\n        // TODO: arrayvec/smallvec, or re-use allocations\n        // Record binding info for dynamic offset validation\n        let mut dynamic_binding_info = Vec::new();\n        // Map of binding -> shader reflected size\n        //Note: we can't collect into a vector right away because\n        // it needs to be in BGL iteration order, not BG entry order.\n        let mut late_buffer_binding_sizes = FastHashMap::default();\n        // fill out the descriptors\n        let mut used = BindGroupStates::new();\n\n        let mut used_buffer_ranges = Vec::new();\n        let mut used_texture_ranges = Vec::new();\n        let mut hal_entries = Vec::with_capacity(desc.entries.len());\n        let mut hal_buffers = Vec::new();\n        let mut hal_samplers = Vec::new();\n        let mut hal_textures = Vec::new();\n        let mut hal_tlas_s = Vec::new();\n        let mut hal_external_textures = Vec::new();\n        let snatch_guard = self.snatchable_lock.read();\n        for entry in desc.entries.iter() {\n            let binding = entry.binding;\n            // Find the corresponding declaration in the layout\n            let decl = layout\n                .entries\n                .get(binding)\n                .ok_or(Error::MissingBindingDeclaration(binding))?;\n            let (res_index, count) = match entry.resource {\n                Br::Buffer(ref bb) => {\n                    let bb = self.create_buffer_binding(\n                        bb,\n                        binding,\n                        decl,\n                        &mut used_buffer_ranges,\n                        &mut dynamic_binding_info,\n                        &mut late_buffer_binding_sizes,\n                        &mut used,\n                        &snatch_guard,\n                    )?;\n\n                    let res_index = hal_buffers.len();\n                    hal_buffers.push(bb);\n                    (res_index, 1)\n                }\n                Br::BufferArray(ref bindings_array) => {\n                    let num_bindings = bindings_array.len();\n                    Self::check_array_binding(self.features, decl.count, num_bindings)?;\n\n                    let res_index = hal_buffers.len();\n                    for bb in bindings_array.iter() {\n                        let bb = self.create_buffer_binding(\n                            bb,\n                            binding,\n                            decl,\n                            &mut used_buffer_ranges,\n                            &mut dynamic_binding_info,\n                            &mut late_buffer_binding_sizes,\n                            &mut used,\n                            &snatch_guard,\n                        )?;\n                        hal_buffers.push(bb);\n                    }\n                    (res_index, num_bindings)\n                }\n                Br::Sampler(ref sampler) => {\n                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;\n\n                    let res_index = hal_samplers.len();\n                    hal_samplers.push(sampler);\n                    (res_index, 1)\n                }\n                Br::SamplerArray(ref samplers) => {\n                    let num_bindings = samplers.len();\n                    Self::check_array_binding(self.features, decl.count, num_bindings)?;\n\n                    let res_index = hal_samplers.len();\n                    for sampler in samplers.iter() {\n                        let sampler =\n                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;\n\n                        hal_samplers.push(sampler);\n                    }\n\n                    (res_index, num_bindings)\n                }\n                Br::TextureView(ref view) => match decl.ty {\n                    wgt::BindingType::ExternalTexture => {\n                        let et = self.create_external_texture_binding_from_view(\n                            binding,\n                            decl,\n                            view,\n                            &mut used,\n                            &snatch_guard,\n                        )?;\n                        let res_index = hal_external_textures.len();\n                        hal_external_textures.push(et);\n                        (res_index, 1)\n                    }\n                    _ => {\n                        let tb = self.create_texture_binding(\n                            binding,\n                            decl,\n                            view,\n                            &mut used,\n                            &mut used_texture_ranges,\n                            &snatch_guard,\n                        )?;\n                        let res_index = hal_textures.len();\n                        hal_textures.push(tb);\n                        (res_index, 1)\n                    }\n                },\n                Br::TextureViewArray(ref views) => {\n                    let num_bindings = views.len();\n                    Self::check_array_binding(self.features, decl.count, num_bindings)?;\n\n                    let res_index = hal_textures.len();\n                    for view in views.iter() {\n                        let tb = self.create_texture_binding(\n                            binding,\n                            decl,\n                            view,\n                            &mut used,\n                            &mut used_texture_ranges,\n                            &snatch_guard,\n                        )?;\n\n                        hal_textures.push(tb);\n                    }\n\n                    (res_index, num_bindings)\n                }\n                Br::AccelerationStructure(ref tlas) => {\n                    let tlas =\n                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;\n                    let res_index = hal_tlas_s.len();\n                    hal_tlas_s.push(tlas);\n                    (res_index, 1)\n                }\n                Br::AccelerationStructureArray(ref tlas_array) => {\n                    // Feature validation for TLAS binding arrays happens at bind group layout\n                    // creation time (mirroring other binding-array resource types). By the time we\n                    // get here, `decl.count` has already been validated against device features.\n                    let num_bindings = tlas_array.len();\n                    Self::check_array_binding(self.features, decl.count, num_bindings)?;\n\n                    let res_index = hal_tlas_s.len();\n                    for tlas in tlas_array.iter() {\n                        let tlas = self.create_tlas_binding(\n                            &mut used,\n                            binding,\n                            decl,\n                            tlas,\n                            &snatch_guard,\n                        )?;\n                        hal_tlas_s.push(tlas);\n                    }\n                    (res_index, num_bindings)\n                }\n                Br::ExternalTexture(ref et) => {\n                    let et = self.create_external_texture_binding(\n                        binding,\n                        decl,\n                        et,\n                        &mut used,\n                        &snatch_guard,\n                    )?;\n                    let res_index = hal_external_textures.len();\n                    hal_external_textures.push(et);\n                    (res_index, 1)\n                }\n            };\n\n            hal_entries.push(hal::BindGroupEntry {\n                binding,\n                resource_index: res_index as u32,\n                count: count as u32,\n            });\n        }\n\n        used.optimize();\n\n        hal_entries.sort_by_key(|entry| entry.binding);\n        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {\n            if a.binding == b.binding {\n                return Err(Error::DuplicateBinding(a.binding));\n            }\n        }\n        let hal_desc = hal::BindGroupDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            layout: layout.raw(),\n            entries: &hal_entries,\n            buffers: &hal_buffers,\n            samplers: &hal_samplers,\n            textures: &hal_textures,\n            acceleration_structures: &hal_tlas_s,\n            external_textures: &hal_external_textures,\n        };\n        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }\n            .map_err(|e| self.handle_hal_error(e))?;\n\n        // collect in the order of BGL iteration\n        let late_buffer_binding_infos = layout\n            .entries\n            .indices()\n            .flat_map(|binding| {\n                let size = late_buffer_binding_sizes.get(&binding).cloned()?;\n                Some(BindGroupLateBufferBindingInfo {\n                    binding_index: binding,\n                    size,\n                })\n            })\n            .collect();\n\n        let bind_group = BindGroup {\n            raw: Snatchable::new(raw),\n            device: self.clone(),\n            layout,\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),\n            used,\n            used_buffer_ranges,\n            used_texture_ranges,\n            dynamic_binding_info,\n            late_buffer_binding_infos,\n        };\n\n        let bind_group = Arc::new(bind_group);\n\n        let weak_ref = Arc::downgrade(&bind_group);\n        for range in &bind_group.used_texture_ranges {\n            let mut bind_groups = range.texture.bind_groups.lock();\n            bind_groups.push(weak_ref.clone());\n        }\n        for range in &bind_group.used_buffer_ranges {\n            let mut bind_groups = range.buffer.bind_groups.lock();\n            bind_groups.push(weak_ref.clone());\n        }\n\n        Ok(bind_group)\n    }\n\n    fn check_array_binding(\n        features: wgt::Features,\n        count: Option<NonZeroU32>,\n        num_bindings: usize,\n    ) -> Result<(), CreateBindGroupError> {\n        use super::binding_model::CreateBindGroupError as Error;\n\n        if let Some(count) = count {\n            let count = count.get() as usize;\n            if count < num_bindings {\n                return Err(Error::BindingArrayPartialLengthMismatch {\n                    actual: num_bindings,\n                    expected: count,\n                });\n            }\n            if count != num_bindings\n                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)\n            {\n                return Err(Error::BindingArrayLengthMismatch {\n                    actual: num_bindings,\n                    expected: count,\n                });\n            }\n            if num_bindings == 0 {\n                return Err(Error::BindingArrayZeroLength);\n            }\n        } else {\n            return Err(Error::SingleBindingExpected);\n        };\n\n        Ok(())\n    }\n\n    fn texture_use_parameters(\n        &self,\n        binding: u32,\n        decl: &wgt::BindGroupLayoutEntry,\n        view: &TextureView,\n        expected: &'static str,\n    ) -> Result<wgt::TextureUses, CreateBindGroupError> {\n        use crate::binding_model::CreateBindGroupError as Error;\n        if view\n            .desc\n            .aspects()\n            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)\n        {\n            return Err(Error::DepthStencilAspect);\n        }\n        match decl.ty {\n            wgt::BindingType::Texture {\n                sample_type,\n                view_dimension,\n                multisampled,\n            } => {\n                use wgt::TextureSampleType as Tst;\n                if multisampled != (view.samples != 1) {\n                    return Err(Error::InvalidTextureMultisample {\n                        binding,\n                        layout_multisampled: multisampled,\n                        view_samples: view.samples,\n                    });\n                }\n                let compat_sample_type = view\n                    .desc\n                    .format\n                    .sample_type(Some(view.desc.range.aspect), Some(self.features))\n                    .unwrap();\n                match (sample_type, compat_sample_type) {\n                    (Tst::Uint, Tst::Uint) |\n                        (Tst::Sint, Tst::Sint) |\n                        (Tst::Depth, Tst::Depth) |\n                        // if we expect non-filterable, accept anything float\n                        (Tst::Float { filterable: false }, Tst::Float { .. }) |\n                        // if we expect filterable, require it\n                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |\n                        // if we expect non-filterable, also accept depth\n                        (Tst::Float { filterable: false }, Tst::Depth) => {}\n                    // if we expect filterable, also accept Float that is defined as\n                    // unfilterable if filterable feature is explicitly enabled (only hit\n                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is\n                    // enabled)\n                    (Tst::Float { filterable: true }, Tst::Float { .. })\n                        if view.format_features.flags\n                            .contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}\n                    _ => {\n                        return Err(Error::InvalidTextureSampleType {\n                            binding,\n                            layout_sample_type: sample_type,\n                            view_format: view.desc.format,\n                            view_sample_type: compat_sample_type,\n                        })\n                    }\n                }\n                if view_dimension != view.desc.dimension {\n                    return Err(Error::InvalidTextureDimension {\n                        binding,\n                        layout_dimension: view_dimension,\n                        view_dimension: view.desc.dimension,\n                    });\n                }\n                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;\n                Ok(wgt::TextureUses::RESOURCE)\n            }\n            wgt::BindingType::StorageTexture {\n                access,\n                format,\n                view_dimension,\n            } => {\n                if format != view.desc.format {\n                    return Err(Error::InvalidStorageTextureFormat {\n                        binding,\n                        layout_format: format,\n                        view_format: view.desc.format,\n                    });\n                }\n                if view_dimension != view.desc.dimension {\n                    return Err(Error::InvalidTextureDimension {\n                        binding,\n                        layout_dimension: view_dimension,\n                        view_dimension: view.desc.dimension,\n                    });\n                }\n\n                let mip_level_count = view.selector.mips.end - view.selector.mips.start;\n                if mip_level_count != 1 {\n                    return Err(Error::InvalidStorageTextureMipLevelCount {\n                        binding,\n                        mip_level_count,\n                    });\n                }\n\n                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;\n\n                Ok(match access {\n                    wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,\n                    wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,\n                    wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,\n                    wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,\n                })\n            }\n            wgt::BindingType::ExternalTexture => {\n                if view.desc.dimension != TextureViewDimension::D2 {\n                    return Err(Error::InvalidTextureDimension {\n                        binding,\n                        layout_dimension: TextureViewDimension::D2,\n                        view_dimension: view.desc.dimension,\n                    });\n                }\n                let mip_level_count = view.selector.mips.end - view.selector.mips.start;\n                if mip_level_count != 1 {\n                    return Err(Error::InvalidExternalTextureMipLevelCount {\n                        binding,\n                        mip_level_count,\n                    });\n                }\n                if view.desc.format != TextureFormat::Rgba8Unorm\n                    && view.desc.format != TextureFormat::Bgra8Unorm\n                    && view.desc.format != TextureFormat::Rgba16Float\n                {\n                    return Err(Error::InvalidExternalTextureFormat {\n                        binding,\n                        format: view.desc.format,\n                    });\n                }\n                if view.samples != 1 {\n                    return Err(Error::InvalidTextureMultisample {\n                        binding,\n                        layout_multisampled: false,\n                        view_samples: view.samples,\n                    });\n                }\n\n                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;\n                Ok(wgt::TextureUses::RESOURCE)\n            }\n            _ => Err(Error::WrongBindingType {\n                binding,\n                actual: decl.ty,\n                expected,\n            }),\n        }\n    }\n\n    pub fn create_pipeline_layout(\n        self: &Arc<Self>,\n        desc: &binding_model::ResolvedPipelineLayoutDescriptor,\n    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {\n        self.create_pipeline_layout_impl(desc, false)\n    }\n\n    fn create_pipeline_layout_impl(\n        self: &Arc<Self>,\n        desc: &binding_model::ResolvedPipelineLayoutDescriptor,\n        ignore_exclusive_pipeline_check: bool,\n    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {\n        use crate::binding_model::CreatePipelineLayoutError as Error;\n\n        self.check_is_valid()?;\n\n        let bind_group_layouts_count = desc.bind_group_layouts.len();\n        let device_max_bind_groups = self.limits.max_bind_groups as usize;\n        if bind_group_layouts_count > device_max_bind_groups {\n            return Err(Error::TooManyGroups {\n                actual: bind_group_layouts_count,\n                max: device_max_bind_groups,\n            });\n        }\n\n        if desc.immediate_size != 0 {\n            self.require_features(wgt::Features::IMMEDIATES)?;\n        }\n        if self.limits.max_immediate_size < desc.immediate_size {\n            return Err(Error::ImmediateRangeTooLarge {\n                size: desc.immediate_size,\n                max: self.limits.max_immediate_size,\n            });\n        }\n        if !desc\n            .immediate_size\n            .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)\n        {\n            return Err(Error::MisalignedImmediateSize {\n                size: desc.immediate_size,\n            });\n        }\n\n        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();\n\n        for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {\n            let Some(bgl) = bgl else {\n                continue;\n            };\n\n            bgl.same_device(self)?;\n\n            if !ignore_exclusive_pipeline_check {\n                let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();\n                if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {\n                    return Err(Error::BglHasExclusivePipeline {\n                        index,\n                        pipeline: alloc::format!(\"{exclusive_pipeline}\"),\n                    });\n                }\n            }\n\n            count_validator.merge(&bgl.binding_count_validator);\n        }\n\n        count_validator\n            .validate(&self.limits)\n            .map_err(Error::TooManyBindings)?;\n\n        let get_bgl_iter = || {\n            desc.bind_group_layouts\n                .iter()\n                .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))\n        };\n\n        let bind_group_layouts = get_bgl_iter()\n            .map(|bgl| bgl.cloned())\n            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();\n\n        let raw_bind_group_layouts = get_bgl_iter()\n            .map(|bgl| bgl.map(|bgl| bgl.raw()))\n            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();\n\n        let additional_flags = if self.indirect_validation.is_some() {\n            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE\n        } else {\n            hal::PipelineLayoutFlags::empty()\n        };\n\n        let hal_desc = hal::PipelineLayoutDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE\n                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS\n                | additional_flags,\n            bind_group_layouts: &raw_bind_group_layouts,\n            immediate_size: desc.immediate_size,\n        };\n\n        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }\n            .map_err(|e| self.handle_hal_error(e))?;\n\n        drop(raw_bind_group_layouts);\n\n        let layout = binding_model::PipelineLayout {\n            raw: ManuallyDrop::new(raw),\n            device: self.clone(),\n            label: desc.label.to_string(),\n            bind_group_layouts,\n            immediate_size: desc.immediate_size,\n        };\n\n        let layout = Arc::new(layout);\n\n        Ok(layout)\n    }\n\n    fn create_derived_pipeline_layout(\n        self: &Arc<Self>,\n        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,\n    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {\n        while derived_group_layouts\n            .last()\n            .is_some_and(|map| map.is_empty())\n        {\n            derived_group_layouts.pop();\n        }\n\n        let mut unique_bind_group_layouts = FastHashMap::default();\n\n        let bind_group_layouts = derived_group_layouts\n            .into_iter()\n            .map(|mut bgl_entry_map| {\n                if bgl_entry_map.is_empty() {\n                    return Ok(None);\n                }\n\n                bgl_entry_map.sort();\n                match unique_bind_group_layouts.entry(bgl_entry_map) {\n                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),\n                    hashbrown::hash_map::Entry::Vacant(e) => {\n                        match self.create_bind_group_layout_internal(\n                            &None,\n                            e.key().clone(),\n                            bgl::Origin::Derived,\n                        ) {\n                            Ok(bgl) => {\n                                e.insert(bgl.clone());\n                                Ok(Some(bgl))\n                            }\n                            Err(e) => Err(e),\n                        }\n                    }\n                }\n            })\n            .collect::<Result<Vec<_>, _>>()?;\n\n        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {\n            label: None,\n            bind_group_layouts: Cow::Owned(bind_group_layouts),\n            immediate_size: 0, //TODO?\n        };\n\n        let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;\n        Ok(layout)\n    }\n\n    pub fn create_compute_pipeline(\n        self: &Arc<Self>,\n        desc: pipeline::ResolvedComputePipelineDescriptor,\n    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {\n        self.check_is_valid()?;\n\n        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;\n\n        let shader_module = desc.stage.module;\n\n        shader_module.same_device(self)?;\n\n        let is_auto_layout = desc.layout.is_none();\n\n        // Get the pipeline layout from the desc if it is provided.\n        let pipeline_layout = match desc.layout {\n            Some(pipeline_layout) => {\n                pipeline_layout.same_device(self)?;\n                Some(pipeline_layout)\n            }\n            None => None,\n        };\n\n        let mut binding_layout_source = match pipeline_layout {\n            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),\n            None => validation::BindingLayoutSource::new_derived(&self.limits),\n        };\n        let mut shader_binding_sizes = FastHashMap::default();\n        let io = validation::StageIo::default();\n\n        let final_entry_point_name;\n\n        {\n            let stage = validation::ShaderStageForValidation::Compute;\n\n            final_entry_point_name = shader_module.finalize_entry_point_name(\n                stage.to_naga(),\n                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),\n            )?;\n\n            if let Some(ref interface) = shader_module.interface {\n                let _ = interface.check_stage(\n                    &mut binding_layout_source,\n                    &mut shader_binding_sizes,\n                    &final_entry_point_name,\n                    stage,\n                    io,\n                )?;\n            }\n        }\n\n        let pipeline_layout = match binding_layout_source {\n            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,\n            validation::BindingLayoutSource::Derived(entries) => {\n                self.create_derived_pipeline_layout(entries)?\n            }\n        };\n\n        let late_sized_buffer_groups =\n            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);\n\n        let cache = match desc.cache {\n            Some(cache) => {\n                cache.same_device(self)?;\n                Some(cache)\n            }\n            None => None,\n        };\n\n        let pipeline_desc = hal::ComputePipelineDescriptor {\n            label: desc.label.to_hal(self.instance_flags),\n            layout: pipeline_layout.raw(),\n            stage: hal::ProgrammableStage {\n                module: shader_module.raw(),\n                entry_point: final_entry_point_name.as_ref(),\n                constants: &desc.stage.constants,\n                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,\n            },\n            cache: cache.as_ref().map(|it| it.raw()),\n        };\n\n        let raw =\n            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(\n                |err| match err {\n                    hal::PipelineError::Device(error) => {\n                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))\n                    }\n                    hal::PipelineError::Linkage(_stages, msg) => {\n                        pipeline::CreateComputePipelineError::Internal(msg)\n                    }\n                    hal::PipelineError::EntryPoint(_stage) => {\n                        pipeline::CreateComputePipelineError::Internal(\n                            ENTRYPOINT_FAILURE_ERROR.to_string(),\n                        )\n                    }\n                    hal::PipelineError::PipelineConstants(_stages, msg) => {\n                        pipeline::CreateComputePipelineError::PipelineConstants(msg)\n                    }\n                },\n            )?;\n\n        let pipeline = pipeline::ComputePipeline {\n            raw: ManuallyDrop::new(raw),\n            layout: pipeline_layout,\n            device: self.clone(),\n            _shader_module: shader_module,\n            late_sized_buffer_groups,\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),\n        };\n\n        let pipeline = Arc::new(pipeline);\n\n        if is_auto_layout {\n            for bgl in pipeline.layout.bind_group_layouts.iter() {\n                let Some(bgl) = bgl else {\n                    continue;\n                };\n\n                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the\n                // result.\n                let _ = bgl\n                    .exclusive_pipeline\n                    .set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(\n                        &pipeline,\n                    )));\n            }\n        }\n\n        Ok(pipeline)\n    }\n\n    pub fn create_render_pipeline(\n        self: &Arc<Self>,\n        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,\n    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {\n        use wgt::TextureFormatFeatureFlags as Tfff;\n\n        self.check_is_valid()?;\n\n        let mut shader_binding_sizes = FastHashMap::default();\n\n        let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);\n        let max_attachments = self.limits.max_color_attachments as usize;\n        if num_attachments > max_attachments {\n            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(\n                command::ColorAttachmentError::TooMany {\n                    given: num_attachments,\n                    limit: max_attachments,\n                },\n            ));\n        }\n\n        let color_targets = desc\n            .fragment\n            .as_ref()\n            .map_or(&[][..], |fragment| &fragment.targets);\n        let depth_stencil_state = desc.depth_stencil.as_ref();\n\n        {\n            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =\n                color_targets.iter().filter_map(|x| x.as_ref()).collect();\n            if !cts.is_empty() && {\n                let first = &cts[0];\n                cts[1..]\n                    .iter()\n                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)\n            } {\n                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;\n            }\n        }\n\n        let mut io = validation::StageIo::default();\n        let mut validated_stages = wgt::ShaderStages::empty();\n\n        let mut vertex_steps;\n        let mut vertex_buffers;\n        let mut total_attributes;\n        let mut dual_source_blending = false;\n        let mut has_depth_attachment = false;\n        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {\n            vertex_steps = Vec::with_capacity(vertex.buffers.len());\n            vertex_buffers = Vec::with_capacity(vertex.buffers.len());\n            total_attributes = 0;\n            for (i, vb_state) in vertex.buffers.iter().enumerate() {\n                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout\n\n                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {\n                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {\n                        index: i as u32,\n                        given: vb_state.array_stride as u32,\n                        limit: self.limits.max_vertex_buffer_array_stride,\n                    });\n                }\n                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {\n                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {\n                        index: i as u32,\n                        stride: vb_state.array_stride,\n                    });\n                }\n\n                let max_stride = if vb_state.array_stride == 0 {\n                    self.limits.max_vertex_buffer_array_stride as u64\n                } else {\n                    vb_state.array_stride\n                };\n                let mut last_stride = 0;\n                for attribute in vb_state.attributes.iter() {\n                    let attribute_stride = attribute.offset + attribute.format.size();\n                    if attribute_stride > max_stride {\n                        return Err(\n                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {\n                                location: attribute.shader_location,\n                                given: attribute_stride as u32,\n                                limit: max_stride as u32,\n                            },\n                        );\n                    }\n\n                    let required_offset_alignment = attribute.format.size().min(4);\n                    if attribute.offset % required_offset_alignment != 0 {\n                        return Err(\n                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {\n                                location: attribute.shader_location,\n                                offset: attribute.offset,\n                            },\n                        );\n                    }\n\n                    if attribute.shader_location >= self.limits.max_vertex_attributes {\n                        return Err(\n                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {\n                                given: attribute.shader_location,\n                                limit: self.limits.max_vertex_attributes,\n                            },\n                        );\n                    }\n\n                    last_stride = last_stride.max(attribute_stride);\n                }\n                vertex_steps.push(pipeline::VertexStep {\n                    stride: vb_state.array_stride,\n                    last_stride,\n                    mode: vb_state.step_mode,\n                });\n                if vb_state.attributes.is_empty() {\n                    continue;\n                }\n                vertex_buffers.push(hal::VertexBufferLayout {\n                    array_stride: vb_state.array_stride,\n                    step_mode: vb_state.step_mode,\n                    attributes: vb_state.attributes.as_ref(),\n                });\n\n                for attribute in vb_state.attributes.iter() {\n                    if attribute.offset >= 0x10000000 {\n                        return Err(\n                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {\n                                location: attribute.shader_location,\n                                offset: attribute.offset,\n                            },\n                        );\n                    }\n\n                    if let wgt::VertexFormat::Float64\n                    | wgt::VertexFormat::Float64x2\n                    | wgt::VertexFormat::Float64x3\n                    | wgt::VertexFormat::Float64x4 = attribute.format\n                    {\n                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;\n                    }\n\n                    let previous = io.varyings.insert(\n                        attribute.shader_location,\n                        validation::InterfaceVar::vertex_attribute(attribute.format),\n                    );\n\n                    if previous.is_some() {\n                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(\n                            attribute.shader_location,\n                        ));\n                    }\n                }\n                total_attributes += vb_state.attributes.len();\n            }\n\n            if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {\n                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {\n                    given: vertex_buffers.len() as u32,\n                    limit: self.limits.max_vertex_buffers,\n                });\n            }\n            if total_attributes > self.limits.max_vertex_attributes as usize {\n                return Err(\n                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {\n                        given: total_attributes as u32,\n                        limit: self.limits.max_vertex_attributes,\n                    },\n                );\n            }\n        } else {\n            vertex_steps = Vec::new();\n            vertex_buffers = Vec::new();\n        };\n\n        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {\n            return Err(\n                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {\n                    strip_index_format: desc.primitive.strip_index_format,\n                    topology: desc.primitive.topology,\n                },\n            );\n        }\n\n        if desc.primitive.unclipped_depth {\n            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;\n        }\n\n        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {\n            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;\n        }\n        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {\n            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;\n        }\n\n        if desc.primitive.conservative {\n            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;\n        }\n\n        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {\n            return Err(\n                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,\n            );\n        }\n\n        let mut target_specified = false;\n\n        for (i, cs) in color_targets.iter().enumerate() {\n            if let Some(cs) = cs.as_ref() {\n                target_specified = true;\n                let error = 'error: {\n                    // This is expected to be the operative check for illegal write mask\n                    // values (larger than 15), because WebGPU requires that it be validated\n                    // on the device timeline.\n                    if cs.write_mask.contains_unknown_bits() {\n                        break 'error Some(ColorStateError::InvalidWriteMask(cs.write_mask));\n                    }\n\n                    let format_features = self.describe_format_features(cs.format)?;\n                    if !format_features\n                        .allowed_usages\n                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)\n                    {\n                        break 'error Some(ColorStateError::FormatNotRenderable(cs.format));\n                    }\n                    if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {\n                        break 'error Some(ColorStateError::FormatNotBlendable(cs.format));\n                    }\n                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {\n                        break 'error Some(ColorStateError::FormatNotColor(cs.format));\n                    }\n\n                    if desc.multisample.count > 1\n                        && !format_features\n                            .flags\n                            .sample_count_supported(desc.multisample.count)\n                    {\n                        break 'error Some(ColorStateError::InvalidSampleCount(\n                            desc.multisample.count,\n                            cs.format,\n                            cs.format\n                                .guaranteed_format_features(self.features)\n                                .flags\n                                .supported_sample_counts(),\n                            self.adapter\n                                .get_texture_format_features(cs.format)\n                                .flags\n                                .supported_sample_counts(),\n                        ));\n                    }\n\n                    if let Some(blend_mode) = cs.blend {\n                        for component in [&blend_mode.color, &blend_mode.alpha] {\n                            for factor in [component.src_factor, component.dst_factor] {\n                                if factor.ref_second_blend_source() {\n                                    self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;\n                                    if i == 0 {\n                                        dual_source_blending = true;\n                                    } else {\n                                        break 'error Some(\n                                            ColorStateError::BlendFactorOnUnsupportedTarget {\n                                                factor,\n                                                target: i as u32,\n                                            },\n                                        );\n                                    }\n                                }\n\n                                if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]\n                                    .contains(&component.operation)\n                                    && factor != wgt::BlendFactor::One\n                                {\n                                    break 'error Some(ColorStateError::InvalidMinMaxBlendFactor {\n                                        factor,\n                                        target: i as u32,\n                                    });\n                                }\n                            }\n                        }\n                    }\n\n                    break 'error None;\n                };\n                if let Some(e) = error {\n                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));\n                }\n            }\n        }\n\n        if dual_source_blending && color_targets.len() > 1 {\n            return Err(\n                pipeline::CreateRenderPipelineError::DualSourceBlendingWithMultipleColorTargets {\n                    count: color_targets.len(),\n                },\n            );\n        }\n\n        validation::validate_color_attachment_bytes_per_sample(\n            color_targets.iter().flatten().map(|cs| cs.format),\n            self.limits.max_color_attachment_bytes_per_sample,\n        )\n        .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;\n\n        if let Some(ds) = depth_stencil_state {\n            // See <https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpudepthstencilstate>.\n            target_specified = true;\n            let error = 'error: {\n                if !ds.format.is_depth_stencil_format() {\n                    // This error case is not redundant with the aspect check below when\n                    // neither depth nor stencil is enabled at all.\n                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(\n                        ds.format,\n                    ));\n                }\n\n                let format_features = self.describe_format_features(ds.format)?;\n                if !format_features\n                    .allowed_usages\n                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)\n                {\n                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(\n                        ds.format,\n                    ));\n                }\n\n                let aspect = hal::FormatAspects::from(ds.format);\n                if aspect.contains(hal::FormatAspects::DEPTH) {\n                    has_depth_attachment = true;\n                } else if ds.is_depth_enabled() {\n                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));\n                }\n                if has_depth_attachment {\n                    let Some(depth_write_enabled) = ds.depth_write_enabled else {\n                        break 'error Some(\n                            pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),\n                        );\n                    };\n\n                    let depth_compare_required = depth_write_enabled\n                        || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep\n                        || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;\n                    if depth_compare_required && ds.depth_compare.is_none() {\n                        break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(\n                            ds.format,\n                        ));\n                    }\n                }\n\n                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {\n                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(\n                        ds.format,\n                    ));\n                }\n                if desc.multisample.count > 1\n                    && !format_features\n                        .flags\n                        .sample_count_supported(desc.multisample.count)\n                {\n                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(\n                        desc.multisample.count,\n                        ds.format,\n                        ds.format\n                            .guaranteed_format_features(self.features)\n                            .flags\n                            .supported_sample_counts(),\n                        self.adapter\n                            .get_texture_format_features(ds.format)\n                            .flags\n                            .supported_sample_counts(),\n                    ));\n                }\n\n                break 'error None;\n            };\n            if let Some(e) = error {\n                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));\n            }\n\n            if ds.bias.clamp != 0.0 {\n                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;\n            }\n\n            if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)\n                && !desc.primitive.topology.is_triangles()\n            {\n                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(\n                    pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(\n                        desc.primitive.topology,\n                    ),\n                ));\n            }\n        }\n\n        if !target_specified {\n            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);\n        }\n\n        let is_auto_layout = desc.layout.is_none();\n\n        // Get the pipeline layout from the desc if it is provided.\n        let pipeline_layout = match desc.layout {\n            Some(pipeline_layout) => {\n                pipeline_layout.same_device(self)?;\n                Some(pipeline_layout)\n            }\n            None => None,\n        };\n\n        let mut binding_layout_source = match pipeline_layout {\n            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),\n            None => validation::BindingLayoutSource::new_derived(&self.limits),\n        };\n\n        let samples = {\n            let sc = desc.multisample.count;\n            if sc == 0 || sc > 32 || !sc.is_power_of_two() {\n                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));\n            }\n            sc\n        };\n\n        let mut vertex_stage = None;\n        let mut task_stage = None;\n        let mut mesh_stage = None;\n        let mut _vertex_entry_point_name = String::new();\n        let mut _task_entry_point_name = String::new();\n        let mut _mesh_entry_point_name = String::new();\n        match desc.vertex {\n            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {\n                vertex_stage = {\n                    let stage_desc = &vertex.stage;\n                    let stage = validation::ShaderStageForValidation::Vertex {\n                        topology: desc.primitive.topology,\n                        compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),\n                    };\n                    let stage_bit = stage.to_wgt_bit();\n\n                    let vertex_shader_module = &stage_desc.module;\n                    vertex_shader_module.same_device(self)?;\n\n                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {\n                        stage: stage_bit,\n                        error,\n                    };\n\n                    _vertex_entry_point_name = vertex_shader_module\n                        .finalize_entry_point_name(\n                            stage.to_naga(),\n                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),\n                        )\n                        .map_err(stage_err)?;\n\n                    if let Some(ref interface) = vertex_shader_module.interface {\n                        io = interface\n                            .check_stage(\n                                &mut binding_layout_source,\n                                &mut shader_binding_sizes,\n                                &_vertex_entry_point_name,\n                                stage,\n                                io,\n                            )\n                            .map_err(stage_err)?;\n                        validated_stages |= stage_bit;\n                    }\n                    Some(hal::ProgrammableStage {\n                        module: vertex_shader_module.raw(),\n                        entry_point: &_vertex_entry_point_name,\n                        constants: &stage_desc.constants,\n                        zero_initialize_workgroup_memory: stage_desc\n                            .zero_initialize_workgroup_memory,\n                    })\n                };\n            }\n            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {\n                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;\n\n                task_stage = if let Some(task) = task {\n                    let stage_desc = &task.stage;\n                    let stage = validation::ShaderStageForValidation::Task;\n                    let stage_bit = stage.to_wgt_bit();\n                    let task_shader_module = &stage_desc.module;\n                    task_shader_module.same_device(self)?;\n\n                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {\n                        stage: stage_bit,\n                        error,\n                    };\n\n                    _task_entry_point_name = task_shader_module\n                        .finalize_entry_point_name(\n                            stage.to_naga(),\n                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),\n                        )\n                        .map_err(stage_err)?;\n\n                    if let Some(ref interface) = task_shader_module.interface {\n                        io = interface\n                            .check_stage(\n                                &mut binding_layout_source,\n                                &mut shader_binding_sizes,\n                                &_task_entry_point_name,\n                                stage,\n                                io,\n                            )\n                            .map_err(stage_err)?;\n                        validated_stages |= stage_bit;\n                    }\n                    Some(hal::ProgrammableStage {\n                        module: task_shader_module.raw(),\n                        entry_point: &_task_entry_point_name,\n                        constants: &stage_desc.constants,\n                        zero_initialize_workgroup_memory: stage_desc\n                            .zero_initialize_workgroup_memory,\n                    })\n                } else {\n                    None\n                };\n                mesh_stage = {\n                    let stage_desc = &mesh.stage;\n                    let stage = validation::ShaderStageForValidation::Mesh;\n                    let stage_bit = stage.to_wgt_bit();\n                    let mesh_shader_module = &stage_desc.module;\n                    mesh_shader_module.same_device(self)?;\n\n                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {\n                        stage: stage_bit,\n                        error,\n                    };\n\n                    _mesh_entry_point_name = mesh_shader_module\n                        .finalize_entry_point_name(\n                            stage.to_naga(),\n                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),\n                        )\n                        .map_err(stage_err)?;\n\n                    if let Some(ref interface) = mesh_shader_module.interface {\n                        io = interface\n                            .check_stage(\n                                &mut binding_layout_source,\n                                &mut shader_binding_sizes,\n                                &_mesh_entry_point_name,\n                                stage,\n                                io,\n                            )\n                            .map_err(stage_err)?;\n                        validated_stages |= stage_bit;\n                    }\n                    Some(hal::ProgrammableStage {\n                        module: mesh_shader_module.raw(),\n                        entry_point: &_mesh_entry_point_name,\n                        constants: &stage_desc.constants,\n                        zero_initialize_workgroup_memory: stage_desc\n                            .zero_initialize_workgroup_memory,\n                    })\n                };\n            }\n        }\n\n        let fragment_entry_point_name;\n        let fragment_stage = match desc.fragment {\n            Some(ref fragment_state) => {\n                let stage = validation::ShaderStageForValidation::Fragment {\n                    dual_source_blending,\n                    has_depth_attachment,\n                };\n                let stage_bit = stage.to_wgt_bit();\n\n                let shader_module = &fragment_state.stage.module;\n                shader_module.same_device(self)?;\n\n                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {\n                    stage: stage_bit,\n                    error,\n                };\n\n                fragment_entry_point_name = shader_module\n                    .finalize_entry_point_name(\n                        stage.to_naga(),\n                        fragment_state\n                            .stage\n                            .entry_point\n                            .as_ref()\n                            .map(|ep| ep.as_ref()),\n                    )\n                    .map_err(stage_err)?;\n\n                if let Some(ref interface) = shader_module.interface {\n                    io = interface\n                        .check_stage(\n                            &mut binding_layout_source,\n                            &mut shader_binding_sizes,\n                            &fragment_entry_point_name,\n                            stage,\n                            io,\n                        )\n                        .map_err(stage_err)?;\n                    validated_stages |= stage_bit;\n                }\n\n                Some(hal::ProgrammableStage {\n                    module: shader_module.raw(),\n                    entry_point: &fragment_entry_point_name,\n                    constants: &fragment_state.stage.constants,\n                    zero_initialize_workgroup_memory: fragment_state\n                        .stage\n                        .zero_initialize_workgroup_memory,\n                })\n            }\n            None => None,\n        };\n\n        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {\n            for (i, output) in io.varyings.iter() {\n                match color_targets.get(*i as usize) {\n                    Some(Some(state)) => {\n                        validation::check_texture_format(state.format, &output.ty).map_err(\n                            |pipeline| {\n                                pipeline::CreateRenderPipelineError::ColorState(\n                                    *i as u8,\n                                    ColorStateError::IncompatibleFormat {\n                                        pipeline,\n                                        shader: output.ty,\n                                    },\n                                )\n                            },\n                        )?;\n                    }\n                    _ => {\n                        log::debug!(\n                            \"The fragment stage {:?} output @location({}) values are ignored\",\n                            fragment_stage\n                                .as_ref()\n                                .map_or(\"\", |stage| stage.entry_point),\n                            i\n                        );\n                    }\n                }\n            }\n        }\n        let last_stage = match desc.fragment {\n            Some(_) => wgt::ShaderStages::FRAGMENT,\n            None => wgt::ShaderStages::VERTEX,\n        };\n        if is_auto_layout && !validated_stages.contains(last_stage) {\n            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());\n        }\n\n        let pipeline_layout = match binding_layout_source {\n            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,\n            validation::BindingLayoutSource::Derived(entries) => {\n                self.create_derived_pipeline_layout(entries)?\n            }\n        };\n\n        // Multiview is only supported if the feature is enabled\n        if let Some(mv_mask) = desc.multiview_mask {\n            self.require_features(wgt::Features::MULTIVIEW)?;\n            if !(mv_mask.get() + 1).is_power_of_two() {\n                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;\n            }\n        }\n\n        if !self\n            .downlevel\n            .flags\n            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)\n        {\n            for (binding, size) in shader_binding_sizes.iter() {\n                if size.get() % 16 != 0 {\n                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {\n                        binding: binding.binding,\n                        group: binding.group,\n                        size: size.get(),\n                    });\n                }\n            }\n        }\n\n        let late_sized_buffer_groups =\n            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);\n\n        let cache = match desc.cache {\n            Some(cache) => {\n                cache.same_device(self)?;\n                Some(cache)\n            }\n            None => None,\n        };\n\n        let is_mesh = mesh_stage.is_some();\n        let raw = {\n            let pipeline_desc = hal::RenderPipelineDescriptor {\n                label: desc.label.to_hal(self.instance_flags),\n                layout: pipeline_layout.raw(),\n                vertex_processor: match vertex_stage {\n                    Some(vertex_stage) => hal::VertexProcessor::Standard {\n                        vertex_buffers: &vertex_buffers,\n                        vertex_stage,\n                    },\n                    None => hal::VertexProcessor::Mesh {\n                        task_stage,\n                        mesh_stage: mesh_stage.unwrap(),\n                    },\n                },\n                primitive: desc.primitive,\n                depth_stencil: desc.depth_stencil.clone(),\n                multisample: desc.multisample,\n                fragment_stage,\n                color_targets,\n                multiview_mask: desc.multiview_mask,\n                cache: cache.as_ref().map(|it| it.raw()),\n            };\n            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(\n                |err| match err {\n                    hal::PipelineError::Device(error) => {\n                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))\n                    }\n                    hal::PipelineError::Linkage(stage, msg) => {\n                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }\n                    }\n                    hal::PipelineError::EntryPoint(stage) => {\n                        pipeline::CreateRenderPipelineError::Internal {\n                            stage: hal::auxil::map_naga_stage(stage),\n                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),\n                        }\n                    }\n                    hal::PipelineError::PipelineConstants(stage, error) => {\n                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }\n                    }\n                },\n            )?\n        };\n\n        let pass_context = RenderPassContext {\n            attachments: AttachmentData {\n                colors: color_targets\n                    .iter()\n                    .map(|state| state.as_ref().map(|s| s.format))\n                    .collect(),\n                resolves: ArrayVec::new(),\n                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),\n            },\n            sample_count: samples,\n            multiview_mask: desc.multiview_mask,\n        };\n\n        let mut flags = pipeline::PipelineFlags::empty();\n        for state in color_targets.iter().filter_map(|s| s.as_ref()) {\n            if let Some(ref bs) = state.blend {\n                if bs.color.uses_constant() | bs.alpha.uses_constant() {\n                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;\n                }\n            }\n        }\n        if let Some(ds) = depth_stencil_state.as_ref() {\n            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {\n                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;\n            }\n            if !ds.is_depth_read_only() {\n                flags |= pipeline::PipelineFlags::WRITES_DEPTH;\n            }\n            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {\n                flags |= pipeline::PipelineFlags::WRITES_STENCIL;\n            }\n        }\n        let shader_modules = {\n            let mut shader_modules = ArrayVec::new();\n            match desc.vertex {\n                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {\n                    shader_modules.push(vertex.stage.module)\n                }\n                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {\n                    if let Some(task) = task {\n                        shader_modules.push(task.stage.module);\n                    }\n                    shader_modules.push(mesh.stage.module);\n                }\n            }\n            shader_modules.extend(desc.fragment.map(|f| f.stage.module));\n            shader_modules\n        };\n\n        let pipeline = pipeline::RenderPipeline {\n            raw: ManuallyDrop::new(raw),\n            layout: pipeline_layout,\n            device: self.clone(),\n            pass_context,\n            _shader_modules: shader_modules,\n            flags,\n            topology: desc.primitive.topology,\n            strip_index_format: desc.primitive.strip_index_format,\n            vertex_steps,\n            late_sized_buffer_groups,\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),\n            is_mesh,\n        };\n\n        let pipeline = Arc::new(pipeline);\n\n        if is_auto_layout {\n            for bgl in pipeline.layout.bind_group_layouts.iter() {\n                let Some(bgl) = bgl else {\n                    continue;\n                };\n\n                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the\n                // result.\n                let _ = bgl\n                    .exclusive_pipeline\n                    .set(binding_model::ExclusivePipeline::Render(Arc::downgrade(\n                        &pipeline,\n                    )));\n            }\n        }\n\n        Ok(pipeline)\n    }\n\n    /// # Safety\n    /// The `data` field on `desc` must have previously been returned from\n    /// [`crate::global::Global::pipeline_cache_get_data`]\n    pub unsafe fn create_pipeline_cache(\n        self: &Arc<Self>,\n        desc: &pipeline::PipelineCacheDescriptor,\n    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {\n        use crate::pipeline_cache;\n\n        self.check_is_valid()?;\n\n        self.require_features(wgt::Features::PIPELINE_CACHE)?;\n        let data = if let Some((data, validation_key)) = desc\n            .data\n            .as_ref()\n            .zip(self.raw().pipeline_cache_validation_key())\n        {\n            let data = pipeline_cache::validate_pipeline_cache(\n                data,\n                &self.adapter.raw.info,\n                validation_key,\n            );\n            match data {\n                Ok(data) => Some(data),\n                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),\n                // If the error was unavoidable and we are asked to fallback, do so\n                Err(_) => None,\n            }\n        } else {\n            None\n        };\n        let cache_desc = hal::PipelineCacheDescriptor {\n            data,\n            label: desc.label.to_hal(self.instance_flags),\n        };\n        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {\n            Ok(raw) => raw,\n            Err(e) => match e {\n                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),\n            },\n        };\n        let cache = pipeline::PipelineCache {\n            device: self.clone(),\n            label: desc.label.to_string(),\n            // This would be none in the error condition, which we don't implement yet\n            raw: ManuallyDrop::new(raw),\n        };\n\n        let cache = Arc::new(cache);\n\n        Ok(cache)\n    }\n\n    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {\n        // Variant of adapter.get_texture_format_features that takes device features into account\n        use wgt::TextureFormatFeatureFlags as tfsc;\n        let mut format_features = self.adapter.get_texture_format_features(format);\n        if (format == TextureFormat::R32Float\n            || format == TextureFormat::Rg32Float\n            || format == TextureFormat::Rgba32Float)\n            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)\n        {\n            format_features.flags.set(tfsc::FILTERABLE, false);\n        }\n        format_features\n    }\n\n    fn describe_format_features(\n        &self,\n        format: TextureFormat,\n    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {\n        self.require_features(format.required_features())?;\n\n        let using_device_features = self\n            .features\n            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);\n        // If we're running downlevel, we need to manually ask the backend what\n        // we can use as we can't trust WebGPU.\n        let downlevel = !self\n            .downlevel\n            .flags\n            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);\n\n        if using_device_features || downlevel {\n            Ok(self.get_texture_format_features(format))\n        } else {\n            Ok(format.guaranteed_format_features(self.features))\n        }\n    }\n\n    #[cfg(feature = \"replay\")]\n    pub(crate) fn wait_for_submit(\n        &self,\n        submission_index: crate::SubmissionIndex,\n    ) -> Result<(), DeviceError> {\n        let fence = self.fence.read();\n        let last_done_index = unsafe { self.raw().get_fence_value(fence.as_ref()) }\n            .map_err(|e| self.handle_hal_error(e))?;\n        if last_done_index < submission_index {\n            unsafe { self.raw().wait(fence.as_ref(), submission_index, None) }\n                .map_err(|e| self.handle_hal_error(e))?;\n            drop(fence);\n            if let Some(queue) = self.get_queue() {\n                let closures = queue.lock_life().triage_submissions(submission_index);\n                assert!(\n                    closures.is_empty(),\n                    \"wait_for_submit is not expected to work with closures\"\n                );\n            }\n        }\n        Ok(())\n    }\n\n    pub fn create_query_set(\n        self: &Arc<Self>,\n        desc: &resource::QuerySetDescriptor,\n    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {\n        use resource::CreateQuerySetError as Error;\n\n        self.check_is_valid()?;\n\n        match desc.ty {\n            wgt::QueryType::Occlusion => {}\n            wgt::QueryType::Timestamp => {\n                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;\n            }\n            wgt::QueryType::PipelineStatistics(..) => {\n                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;\n            }\n        }\n\n        if desc.count == 0 {\n            return Err(Error::ZeroCount);\n        }\n\n        if desc.count > wgt::QUERY_SET_MAX_QUERIES {\n            return Err(Error::TooManyQueries {\n                count: desc.count,\n                maximum: wgt::QUERY_SET_MAX_QUERIES,\n            });\n        }\n\n        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));\n\n        let raw = unsafe { self.raw().create_query_set(&hal_desc) }\n            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;\n\n        let query_set = QuerySet {\n            raw: ManuallyDrop::new(raw),\n            device: self.clone(),\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),\n            desc: desc.map_label(|_| ()),\n        };\n\n        let query_set = Arc::new(query_set);\n\n        Ok(query_set)\n    }\n\n    pub fn configure_surface(\n        self: &Arc<Self>,\n        surface: &crate::instance::Surface,\n        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,\n    ) -> Option<present::ConfigureSurfaceError> {\n        use present::ConfigureSurfaceError as E;\n        profiling::scope!(\"surface_configure\");\n\n        fn validate_surface_configuration(\n            config: &mut hal::SurfaceConfiguration,\n            caps: &hal::SurfaceCapabilities,\n            max_texture_dimension_2d: u32,\n        ) -> Result<(), E> {\n            let width = config.extent.width;\n            let height = config.extent.height;\n\n            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {\n                return Err(E::TooLarge {\n                    width,\n                    height,\n                    max_texture_dimension_2d,\n                });\n            }\n\n            if !caps.present_modes.contains(&config.present_mode) {\n                // Automatic present mode checks.\n                //\n                // The \"Automatic\" modes are never supported by the backends.\n                let fallbacks = match config.present_mode {\n                    wgt::PresentMode::AutoVsync => {\n                        &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]\n                    }\n                    // Always end in FIFO to make sure it's always supported\n                    wgt::PresentMode::AutoNoVsync => &[\n                        wgt::PresentMode::Immediate,\n                        wgt::PresentMode::Mailbox,\n                        wgt::PresentMode::Fifo,\n                    ][..],\n                    _ => {\n                        return Err(E::UnsupportedPresentMode {\n                            requested: config.present_mode,\n                            available: caps.present_modes.clone(),\n                        });\n                    }\n                };\n\n                let new_mode = fallbacks\n                    .iter()\n                    .copied()\n                    .find(|fallback| caps.present_modes.contains(fallback))\n                    .unwrap_or_else(|| {\n                        unreachable!(\n                            \"Fallback system failed to choose present mode. \\\n                            This is a bug. Mode: {:?}, Options: {:?}\",\n                            config.present_mode, &caps.present_modes\n                        );\n                    });\n\n                api_log!(\n                    \"Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}\",\n                    config.present_mode\n                );\n                config.present_mode = new_mode;\n            }\n            if !caps.formats.contains(&config.format) {\n                return Err(E::UnsupportedFormat {\n                    requested: config.format,\n                    available: caps.formats.clone(),\n                });\n            }\n            if !caps\n                .composite_alpha_modes\n                .contains(&config.composite_alpha_mode)\n            {\n                let new_alpha_mode = 'alpha: {\n                    // Automatic alpha mode checks.\n                    let fallbacks = match config.composite_alpha_mode {\n                        wgt::CompositeAlphaMode::Auto => &[\n                            wgt::CompositeAlphaMode::Opaque,\n                            wgt::CompositeAlphaMode::Inherit,\n                        ][..],\n                        _ => {\n                            return Err(E::UnsupportedAlphaMode {\n                                requested: config.composite_alpha_mode,\n                                available: caps.composite_alpha_modes.clone(),\n                            });\n                        }\n                    };\n\n                    for &fallback in fallbacks {\n                        if caps.composite_alpha_modes.contains(&fallback) {\n                            break 'alpha fallback;\n                        }\n                    }\n\n                    unreachable!(\n                        \"Fallback system failed to choose alpha mode. This is a bug. \\\n                                  AlphaMode: {:?}, Options: {:?}\",\n                        config.composite_alpha_mode, &caps.composite_alpha_modes\n                    );\n                };\n\n                api_log!(\n                    \"Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}\",\n                    config.composite_alpha_mode\n                );\n                config.composite_alpha_mode = new_alpha_mode;\n            }\n            if !caps.usage.contains(config.usage) {\n                return Err(E::UnsupportedUsage {\n                    requested: config.usage,\n                    available: caps.usage,\n                });\n            }\n            if width == 0 || height == 0 {\n                return Err(E::ZeroArea);\n            }\n            Ok(())\n        }\n\n        log::debug!(\"configuring surface with {config:?}\");\n\n        let error = 'error: {\n            // User callbacks must not be called while we are holding locks.\n            let user_callbacks;\n            {\n                if let Err(e) = self.check_is_valid() {\n                    break 'error e.into();\n                }\n\n                let caps = match surface.get_capabilities(&self.adapter) {\n                    Ok(caps) => caps,\n                    Err(_) => break 'error E::UnsupportedQueueFamily,\n                };\n\n                let mut hal_view_formats = Vec::new();\n                for format in config.view_formats.iter() {\n                    if *format == config.format {\n                        continue;\n                    }\n                    if !caps.formats.contains(&config.format) {\n                        break 'error E::UnsupportedFormat {\n                            requested: config.format,\n                            available: caps.formats,\n                        };\n                    }\n                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {\n                        break 'error E::InvalidViewFormat(*format, config.format);\n                    }\n                    hal_view_formats.push(*format);\n                }\n\n                if !hal_view_formats.is_empty() {\n                    if let Err(missing_flag) =\n                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)\n                    {\n                        break 'error E::MissingDownlevelFlags(missing_flag);\n                    }\n                }\n\n                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(\n                    *caps.maximum_frame_latency.start(),\n                    *caps.maximum_frame_latency.end(),\n                );\n                let mut hal_config = hal::SurfaceConfiguration {\n                    maximum_frame_latency,\n                    present_mode: config.present_mode,\n                    composite_alpha_mode: config.alpha_mode,\n                    format: config.format,\n                    extent: wgt::Extent3d {\n                        width: config.width,\n                        height: config.height,\n                        depth_or_array_layers: 1,\n                    },\n                    usage: conv::map_texture_usage(\n                        config.usage,\n                        hal::FormatAspects::COLOR,\n                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY\n                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY\n                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,\n                    ),\n                    view_formats: hal_view_formats,\n                };\n\n                if let Err(error) = validate_surface_configuration(\n                    &mut hal_config,\n                    &caps,\n                    self.limits.max_texture_dimension_2d,\n                ) {\n                    break 'error error;\n                }\n\n                // Wait for all work to finish before configuring the surface.\n                let snatch_guard = self.snatchable_lock.read();\n                let fence = self.fence.read();\n\n                let maintain_result;\n                (user_callbacks, maintain_result) =\n                    self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);\n\n                match maintain_result {\n                    // We're happy\n                    Ok(wgt::PollStatus::QueueEmpty) => {}\n                    Ok(wgt::PollStatus::WaitSucceeded) => {\n                        // After the wait, the queue should be empty. It can only be non-empty\n                        // if another thread is submitting at the same time.\n                        break 'error E::GpuWaitTimeout;\n                    }\n                    Ok(wgt::PollStatus::Poll) => {\n                        unreachable!(\"Cannot get a Poll result from a Wait action.\")\n                    }\n                    Err(WaitIdleError::Timeout) if cfg!(target_arch = \"wasm32\") => {\n                        // On wasm, you cannot actually successfully wait for the surface.\n                        // However WebGL does not actually require you do this, so ignoring\n                        // the failure is totally fine. See\n                        // https://github.com/gfx-rs/wgpu/issues/7363\n                    }\n                    Err(e) => {\n                        break 'error e.into();\n                    }\n                }\n\n                // All textures must be destroyed before the surface can be re-configured.\n                if let Some(present) = surface.presentation.lock().take() {\n                    if present.acquired_texture.is_some() {\n                        break 'error E::PreviousOutputExists;\n                    }\n                }\n\n                // TODO: Texture views may still be alive that point to the texture.\n                // this will allow the user to render to the surface texture, long after\n                // it has been removed.\n                //\n                // https://github.com/gfx-rs/wgpu/issues/4105\n\n                let surface_raw = surface.raw(self.backend()).unwrap();\n                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {\n                    Ok(()) => (),\n                    Err(error) => {\n                        break 'error match error {\n                            hal::SurfaceError::Outdated\n                            | hal::SurfaceError::Lost\n                            | hal::SurfaceError::Occluded\n                            | hal::SurfaceError::Timeout => E::InvalidSurface,\n                            hal::SurfaceError::Device(error) => {\n                                E::Device(self.handle_hal_error(error))\n                            }\n                            hal::SurfaceError::Other(message) => {\n                                log::error!(\"surface configuration failed: {message}\");\n                                E::InvalidSurface\n                            }\n                        }\n                    }\n                }\n\n                let mut presentation = surface.presentation.lock();\n                *presentation = Some(present::Presentation {\n                    device: Arc::clone(self),\n                    config: config.clone(),\n                    acquired_texture: None,\n                });\n            }\n\n            user_callbacks.fire();\n            return None;\n        };\n\n        Some(error)\n    }\n\n    fn lose(&self, message: &str) {\n        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.\n\n        // Mark the device explicitly as invalid. This is checked in various\n        // places to prevent new work from being submitted.\n        self.valid.store(false, Ordering::Release);\n\n        // 1) Resolve the GPUDevice device.lost promise.\n        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {\n            device_lost_closure(DeviceLostReason::Unknown, message.to_string());\n        }\n\n        // 2) Complete any outstanding mapAsync() steps.\n        // 3) Complete any outstanding onSubmittedWorkDone() steps.\n\n        // These parts are passively accomplished by setting valid to false,\n        // since that will prevent any new work from being added to the queues.\n        // Future calls to poll_devices will continue to check the work queues\n        // until they are cleared, and then drop the device.\n    }\n\n    fn release_gpu_resources(&self) {\n        // This is called when the device is lost, which makes every associated\n        // resource invalid and unusable. This is an opportunity to release all of\n        // the underlying gpu resources, even though the objects remain visible to\n        // the user agent. We purge this memory naturally when resources have been\n        // moved into the appropriate buckets, so this function just needs to\n        // initiate movement into those buckets, and it can do that by calling\n        // \"destroy\" on all the resources we know about.\n\n        // During these iterations, we discard all errors. We don't care!\n        let trackers = self.trackers.lock();\n        for buffer in trackers.buffers.used_resources() {\n            if let Some(buffer) = Weak::upgrade(buffer) {\n                buffer.destroy();\n            }\n        }\n        for texture in trackers.textures.used_resources() {\n            if let Some(texture) = Weak::upgrade(texture) {\n                texture.destroy();\n            }\n        }\n    }\n\n    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {\n        UsageScope::new_pooled(\n            &self.usage_scopes,\n            &self.tracker_indices,\n            self.ordered_buffer_usages,\n            self.ordered_texture_usages,\n        )\n    }\n\n    pub fn get_hal_counters(&self) -> wgt::HalCounters {\n        self.raw().get_internal_counters()\n    }\n\n    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {\n        self.raw().generate_allocator_report()\n    }\n}\n\ncrate::impl_resource_type!(Device);\ncrate::impl_labeled!(Device);\ncrate::impl_storage_item!(Device);\n"
  },
  {
    "path": "wgpu-core/src/device/trace/record.rs",
    "content": "use alloc::{borrow::Cow, string::ToString, sync::Arc, vec::Vec};\nuse core::{any::Any, convert::Infallible};\nuse std::io::Write as _;\n\nuse crate::{\n    command::{\n        ArcCommand, ArcComputeCommand, ArcPassTimestampWrites, ArcReferences, ArcRenderCommand,\n        BasePass, ColorAttachments, Command, ComputeCommand, PointerReferences, RenderCommand,\n        RenderPassColorAttachment, ResolvedRenderPassDepthStencilAttachment,\n    },\n    device::trace::{Data, DataKind},\n    id::{markers, PointerId},\n    storage::StorageItem,\n};\n\nuse super::{\n    Action, TraceBindGroupDescriptor, TraceComputePipelineDescriptor,\n    TraceGeneralRenderPipelineDescriptor, FILE_NAME,\n};\n\npub(crate) fn new_render_bundle_encoder_descriptor(\n    label: crate::Label<'_>,\n    context: &crate::device::RenderPassContext,\n    depth_read_only: bool,\n    stencil_read_only: bool,\n) -> crate::command::RenderBundleEncoderDescriptor<'static> {\n    crate::command::RenderBundleEncoderDescriptor {\n        label: label.map(|l| Cow::from(l.to_string())),\n        color_formats: Cow::from(context.attachments.colors.to_vec()),\n        depth_stencil: context.attachments.depth_stencil.map(|format| {\n            wgt::RenderBundleDepthStencil {\n                format,\n                depth_read_only,\n                stencil_read_only,\n            }\n        }),\n        sample_count: context.sample_count,\n        multiview: context.multiview_mask,\n    }\n}\n\npub trait Trace: Any + Send + Sync {\n    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data;\n\n    fn make_string(&mut self, kind: DataKind, data: &str) -> Data;\n\n    fn add(&mut self, action: Action<'_, PointerReferences>)\n    where\n        for<'a> Action<'a, PointerReferences>: serde::Serialize;\n}\n\n#[derive(Debug)]\npub struct DiskTrace {\n    path: std::path::PathBuf,\n    file: std::fs::File,\n    config: ron::ser::PrettyConfig,\n    data_id: usize,\n}\n\nimpl DiskTrace {\n    pub fn new(path: std::path::PathBuf) -> Result<Self, std::io::Error> {\n        log::debug!(\"Tracing into '{path:?}'\");\n        let mut file = std::fs::File::create(path.join(FILE_NAME))?;\n        file.write_all(b\"[\\n\")?;\n        Ok(Self {\n            path,\n            file,\n            config: ron::ser::PrettyConfig::default(),\n            data_id: 0,\n        })\n    }\n}\n\nimpl Trace for DiskTrace {\n    /// Store `[u8]` data in the trace.\n    ///\n    /// Using a string `kind` is probably a bug, but should work as long as the\n    /// data is UTF-8.\n    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data {\n        self.data_id += 1;\n        let name = std::format!(\"data{}.{}\", self.data_id, kind);\n        let _ = std::fs::write(self.path.join(&name), data);\n        Data::File(name)\n    }\n\n    /// Store `str` data in the trace.\n    ///\n    /// Using a binary `kind` is fine, but it's not clear why not use\n    /// `make_binary` instead.\n    fn make_string(&mut self, kind: DataKind, data: &str) -> Data {\n        self.make_binary(kind, data.as_bytes())\n    }\n\n    fn add(&mut self, action: Action<'_, PointerReferences>)\n    where\n        for<'a> Action<'a, PointerReferences>: serde::Serialize,\n    {\n        match ron::ser::to_string_pretty(&action, self.config.clone()) {\n            Ok(string) => {\n                let _ = writeln!(self.file, \"{string},\");\n            }\n            Err(e) => {\n                log::warn!(\"RON serialization failure: {e:?}\");\n            }\n        }\n    }\n}\n\nimpl Drop for DiskTrace {\n    fn drop(&mut self) {\n        let _ = self.file.write_all(b\"]\");\n    }\n}\n\n#[derive(Default)]\npub struct MemoryTrace {\n    actions: Vec<Action<'static, PointerReferences>>,\n}\n\nimpl MemoryTrace {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn actions(&self) -> &[Action<'static, PointerReferences>] {\n        &self.actions\n    }\n}\n\nimpl Trace for MemoryTrace {\n    /// Store `[u8]` data in the trace.\n    ///\n    /// Using a string `kind` is probably a bug, but should work as long as the\n    /// data is UTF-8.\n    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data {\n        Data::Binary(kind, data.to_vec())\n    }\n\n    /// Store `str` data in the trace.\n    ///\n    /// Using a binary `kind` is fine, but it's not clear why not use\n    /// `make_binary` instead.\n    fn make_string(&mut self, kind: DataKind, data: &str) -> Data {\n        Data::String(kind, data.to_string())\n    }\n\n    fn add(&mut self, action: Action<'_, PointerReferences>)\n    where\n        for<'a> Action<'a, PointerReferences>: serde::Serialize,\n    {\n        self.actions.push(action_to_owned(action))\n    }\n}\n\npub(crate) trait IntoTrace {\n    type Output;\n    fn into_trace(self) -> Self::Output;\n\n    fn to_trace(&self) -> Self::Output\n    where\n        Self: Sized + Clone,\n    {\n        self.clone().into_trace()\n    }\n}\n\nimpl<T: StorageItem> IntoTrace for Arc<T> {\n    type Output = PointerId<T::Marker>;\n    fn into_trace(self) -> Self::Output {\n        self.to_trace()\n    }\n\n    fn to_trace(&self) -> Self::Output {\n        PointerId::from(self)\n    }\n}\n\nimpl IntoTrace for ArcCommand {\n    type Output = Command<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        match self {\n            ArcCommand::CopyBufferToBuffer {\n                src,\n                src_offset,\n                dst,\n                dst_offset,\n                size,\n            } => Command::CopyBufferToBuffer {\n                src: src.to_trace(),\n                src_offset,\n                dst: dst.to_trace(),\n                dst_offset,\n                size,\n            },\n            ArcCommand::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture {\n                src: src.into_trace(),\n                dst: dst.into_trace(),\n                size,\n            },\n            ArcCommand::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer {\n                src: src.into_trace(),\n                dst: dst.into_trace(),\n                size,\n            },\n            ArcCommand::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture {\n                src: src.into_trace(),\n                dst: dst.into_trace(),\n                size,\n            },\n            ArcCommand::ClearBuffer { dst, offset, size } => Command::ClearBuffer {\n                dst: dst.to_trace(),\n                offset,\n                size,\n            },\n            ArcCommand::ClearTexture {\n                dst,\n                subresource_range,\n            } => Command::ClearTexture {\n                dst: dst.to_trace(),\n                subresource_range,\n            },\n            ArcCommand::WriteTimestamp {\n                query_set,\n                query_index,\n            } => Command::WriteTimestamp {\n                query_set: query_set.to_trace(),\n                query_index,\n            },\n            ArcCommand::ResolveQuerySet {\n                query_set,\n                start_query,\n                query_count,\n                destination,\n                destination_offset,\n            } => Command::ResolveQuerySet {\n                query_set: query_set.to_trace(),\n                start_query,\n                query_count,\n                destination: destination.to_trace(),\n                destination_offset,\n            },\n            ArcCommand::PushDebugGroup(label) => Command::PushDebugGroup(label),\n            ArcCommand::PopDebugGroup => Command::PopDebugGroup,\n            ArcCommand::InsertDebugMarker(label) => Command::InsertDebugMarker(label),\n            ArcCommand::RunComputePass {\n                pass,\n                timestamp_writes,\n            } => Command::RunComputePass {\n                pass: pass.into_trace(),\n                timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()),\n            },\n            ArcCommand::RunRenderPass {\n                pass,\n                color_attachments,\n                depth_stencil_attachment,\n                timestamp_writes,\n                occlusion_query_set,\n                multiview_mask,\n            } => Command::RunRenderPass {\n                pass: pass.into_trace(),\n                color_attachments: color_attachments.into_trace(),\n                depth_stencil_attachment: depth_stencil_attachment.map(|d| d.into_trace()),\n                timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()),\n                occlusion_query_set: occlusion_query_set.map(|q| q.to_trace()),\n                multiview_mask,\n            },\n            ArcCommand::BuildAccelerationStructures { blas, tlas } => {\n                Command::BuildAccelerationStructures {\n                    blas: blas.into_iter().map(|b| b.into_trace()).collect(),\n                    tlas: tlas.into_iter().map(|b| b.into_trace()).collect(),\n                }\n            }\n            ArcCommand::TransitionResources {\n                buffer_transitions: _,\n                texture_transitions: _,\n            } => {\n                // TransitionResources does not exist in Command, so skip or handle as needed.\n                // If you want to ignore, you could panic or return a default.\n                panic!(\"TransitionResources cannot be converted to Command\");\n            }\n        }\n    }\n}\n\nimpl<T: IntoTrace> IntoTrace for wgt::TexelCopyBufferInfo<T> {\n    type Output = wgt::TexelCopyBufferInfo<T::Output>;\n    fn into_trace(self) -> Self::Output {\n        wgt::TexelCopyBufferInfo {\n            buffer: self.buffer.into_trace(),\n            layout: self.layout,\n        }\n    }\n}\n\nimpl<T: IntoTrace> IntoTrace for wgt::TexelCopyTextureInfo<T> {\n    type Output = wgt::TexelCopyTextureInfo<T::Output>;\n    fn into_trace(self) -> Self::Output {\n        wgt::TexelCopyTextureInfo {\n            texture: self.texture.into_trace(),\n            mip_level: self.mip_level,\n            origin: self.origin,\n            aspect: self.aspect,\n        }\n    }\n}\n\nimpl IntoTrace for ArcPassTimestampWrites {\n    type Output = crate::command::PassTimestampWrites<PointerId<markers::QuerySet>>;\n    fn into_trace(self) -> Self::Output {\n        crate::command::PassTimestampWrites {\n            query_set: self.query_set.into_trace(),\n            beginning_of_pass_write_index: self.beginning_of_pass_write_index,\n            end_of_pass_write_index: self.end_of_pass_write_index,\n        }\n    }\n}\n\nimpl IntoTrace for ColorAttachments {\n    type Output = ColorAttachments<PointerId<markers::TextureView>>;\n    fn into_trace(self) -> Self::Output {\n        self.into_iter()\n            .map(|opt| {\n                opt.map(|att| RenderPassColorAttachment {\n                    view: att.view.into_trace(),\n                    depth_slice: att.depth_slice,\n                    resolve_target: att.resolve_target.map(|r| r.into_trace()),\n                    load_op: att.load_op,\n                    store_op: att.store_op,\n                })\n            })\n            .collect()\n    }\n}\n\nimpl<TV: IntoTrace> IntoTrace for ResolvedRenderPassDepthStencilAttachment<TV> {\n    type Output = ResolvedRenderPassDepthStencilAttachment<TV::Output>;\n    fn into_trace(self) -> Self::Output {\n        ResolvedRenderPassDepthStencilAttachment {\n            view: self.view.into_trace(),\n            depth: self.depth,\n            stencil: self.stencil,\n        }\n    }\n}\n\nimpl IntoTrace for crate::ray_tracing::OwnedBlasBuildEntry<ArcReferences> {\n    type Output = crate::ray_tracing::OwnedBlasBuildEntry<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        crate::ray_tracing::OwnedBlasBuildEntry {\n            blas: self.blas.into_trace(),\n            geometries: self.geometries.into_trace(),\n        }\n    }\n}\n\nimpl IntoTrace for crate::ray_tracing::OwnedBlasGeometries<ArcReferences> {\n    type Output = crate::ray_tracing::OwnedBlasGeometries<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        match self {\n            crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => {\n                crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries(\n                    geos.into_iter().map(|g| g.into_trace()).collect(),\n                )\n            }\n        }\n    }\n}\n\nimpl IntoTrace for crate::ray_tracing::OwnedBlasTriangleGeometry<ArcReferences> {\n    type Output = crate::ray_tracing::OwnedBlasTriangleGeometry<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        crate::ray_tracing::OwnedBlasTriangleGeometry {\n            size: self.size,\n            vertex_buffer: self.vertex_buffer.into_trace(),\n            index_buffer: self.index_buffer.map(|b| b.into_trace()),\n            transform_buffer: self.transform_buffer.map(|b| b.into_trace()),\n            first_vertex: self.first_vertex,\n            vertex_stride: self.vertex_stride,\n            first_index: self.first_index,\n            transform_buffer_offset: self.transform_buffer_offset,\n        }\n    }\n}\n\nimpl IntoTrace for crate::ray_tracing::OwnedTlasPackage<ArcReferences> {\n    type Output = crate::ray_tracing::OwnedTlasPackage<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        crate::ray_tracing::OwnedTlasPackage {\n            tlas: self.tlas.into_trace(),\n            instances: self\n                .instances\n                .into_iter()\n                .map(|opt| opt.map(|inst| inst.into_trace()))\n                .collect(),\n            lowest_unmodified: self.lowest_unmodified,\n        }\n    }\n}\n\nimpl IntoTrace for crate::ray_tracing::OwnedTlasInstance<ArcReferences> {\n    type Output = crate::ray_tracing::OwnedTlasInstance<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        crate::ray_tracing::OwnedTlasInstance {\n            blas: self.blas.into_trace(),\n            transform: self.transform,\n            custom_data: self.custom_data,\n            mask: self.mask,\n        }\n    }\n}\n\nimpl<C: IntoTrace> IntoTrace for BasePass<C, Infallible> {\n    type Output = BasePass<C::Output, Infallible>;\n\n    fn into_trace(self) -> Self::Output {\n        BasePass {\n            label: self.label,\n            error: self.error,\n            commands: self\n                .commands\n                .into_iter()\n                .map(|cmd| cmd.into_trace())\n                .collect(),\n            dynamic_offsets: self.dynamic_offsets,\n            string_data: self.string_data,\n            immediates_data: self.immediates_data,\n        }\n    }\n}\n\nimpl IntoTrace for ArcComputeCommand {\n    type Output = ComputeCommand<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        use ComputeCommand as C;\n        match self {\n            C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group,\n            } => C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group: bind_group.map(|bg| bg.into_trace()),\n            },\n            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),\n            C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            } => C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            },\n            C::Dispatch(groups) => C::Dispatch(groups),\n            C::DispatchIndirect { buffer, offset } => C::DispatchIndirect {\n                buffer: buffer.into_trace(),\n                offset,\n            },\n            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },\n            C::PopDebugGroup => C::PopDebugGroup,\n            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },\n            C::WriteTimestamp {\n                query_set,\n                query_index,\n            } => C::WriteTimestamp {\n                query_set: query_set.into_trace(),\n                query_index,\n            },\n            C::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            } => C::BeginPipelineStatisticsQuery {\n                query_set: query_set.into_trace(),\n                query_index,\n            },\n            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,\n        }\n    }\n}\n\nimpl IntoTrace for ArcRenderCommand {\n    type Output = RenderCommand<PointerReferences>;\n    fn into_trace(self) -> Self::Output {\n        use RenderCommand as C;\n        match self {\n            C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group,\n            } => C::SetBindGroup {\n                index,\n                num_dynamic_offsets,\n                bind_group: bind_group.map(|bg| bg.into_trace()),\n            },\n            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),\n            C::SetIndexBuffer {\n                buffer,\n                index_format,\n                offset,\n                size,\n            } => C::SetIndexBuffer {\n                buffer: buffer.into_trace(),\n                index_format,\n                offset,\n                size,\n            },\n            C::SetVertexBuffer {\n                slot,\n                buffer,\n                offset,\n                size,\n            } => C::SetVertexBuffer {\n                slot,\n                buffer: buffer.into_trace(),\n                offset,\n                size,\n            },\n            C::SetBlendConstant(color) => C::SetBlendConstant(color),\n            C::SetStencilReference(val) => C::SetStencilReference(val),\n            C::SetViewport {\n                rect,\n                depth_min,\n                depth_max,\n            } => C::SetViewport {\n                rect,\n                depth_min,\n                depth_max,\n            },\n            C::SetScissor(rect) => C::SetScissor(rect),\n            C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            } => C::SetImmediate {\n                offset,\n                size_bytes,\n                values_offset,\n            },\n            C::Draw {\n                vertex_count,\n                instance_count,\n                first_vertex,\n                first_instance,\n            } => C::Draw {\n                vertex_count,\n                instance_count,\n                first_vertex,\n                first_instance,\n            },\n            C::DrawIndexed {\n                index_count,\n                instance_count,\n                first_index,\n                base_vertex,\n                first_instance,\n            } => C::DrawIndexed {\n                index_count,\n                instance_count,\n                first_index,\n                base_vertex,\n                first_instance,\n            },\n            C::DrawMeshTasks {\n                group_count_x,\n                group_count_y,\n                group_count_z,\n            } => C::DrawMeshTasks {\n                group_count_x,\n                group_count_y,\n                group_count_z,\n            },\n            C::DrawIndirect {\n                buffer,\n                offset,\n                count,\n                family,\n                vertex_or_index_limit,\n                instance_limit,\n            } => C::DrawIndirect {\n                buffer: buffer.into_trace(),\n                offset,\n                count,\n                family,\n                vertex_or_index_limit,\n                instance_limit,\n            },\n            C::MultiDrawIndirectCount {\n                buffer,\n                offset,\n                count_buffer,\n                count_buffer_offset,\n                max_count,\n                family,\n            } => C::MultiDrawIndirectCount {\n                buffer: buffer.into_trace(),\n                offset,\n                count_buffer: count_buffer.into_trace(),\n                count_buffer_offset,\n                max_count,\n                family,\n            },\n            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },\n            C::PopDebugGroup => C::PopDebugGroup,\n            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },\n            C::WriteTimestamp {\n                query_set,\n                query_index,\n            } => C::WriteTimestamp {\n                query_set: query_set.into_trace(),\n                query_index,\n            },\n            C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index },\n            C::EndOcclusionQuery => C::EndOcclusionQuery,\n            C::BeginPipelineStatisticsQuery {\n                query_set,\n                query_index,\n            } => C::BeginPipelineStatisticsQuery {\n                query_set: query_set.into_trace(),\n                query_index,\n            },\n            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,\n            C::ExecuteBundle(bundle) => C::ExecuteBundle(bundle.into_trace()),\n        }\n    }\n}\n\nimpl IntoTrace for crate::binding_model::ResolvedPipelineLayoutDescriptor<'_> {\n    type Output = crate::binding_model::PipelineLayoutDescriptor<\n        'static,\n        PointerId<markers::BindGroupLayout>,\n    >;\n    fn into_trace(self) -> Self::Output {\n        crate::binding_model::PipelineLayoutDescriptor {\n            label: self.label.map(|l| Cow::Owned(l.into_owned())),\n            bind_group_layouts: self\n                .bind_group_layouts\n                .iter()\n                .map(|bgl| bgl.to_trace())\n                .collect(),\n            immediate_size: self.immediate_size,\n        }\n    }\n}\n\nimpl<'a> IntoTrace for &'_ crate::binding_model::ResolvedBindGroupDescriptor<'a> {\n    type Output = TraceBindGroupDescriptor<'a>;\n\n    fn into_trace(self) -> Self::Output {\n        use crate::binding_model::{\n            BindGroupEntry, BindingResource, BufferBinding, ResolvedBindingResource,\n        };\n        TraceBindGroupDescriptor {\n            label: self.label.clone(),\n            layout: self.layout.to_trace(),\n            entries: Cow::Owned(\n                self.entries\n                    .iter()\n                    .map(|entry| {\n                        let resource = match &entry.resource {\n                            ResolvedBindingResource::Buffer(buffer_binding) => {\n                                BindingResource::Buffer(BufferBinding {\n                                    buffer: buffer_binding.buffer.to_trace(),\n                                    offset: buffer_binding.offset,\n                                    size: buffer_binding.size,\n                                })\n                            }\n                            ResolvedBindingResource::BufferArray(buffer_bindings) => {\n                                let resolved_buffers: Vec<_> = buffer_bindings\n                                    .iter()\n                                    .map(|bb| BufferBinding {\n                                        buffer: bb.buffer.to_trace(),\n                                        offset: bb.offset,\n                                        size: bb.size,\n                                    })\n                                    .collect();\n                                BindingResource::BufferArray(Cow::Owned(resolved_buffers))\n                            }\n                            ResolvedBindingResource::Sampler(sampler_id) => {\n                                BindingResource::Sampler(sampler_id.to_trace())\n                            }\n                            ResolvedBindingResource::SamplerArray(sampler_ids) => {\n                                let resolved: Vec<_> =\n                                    sampler_ids.iter().map(|id| id.to_trace()).collect();\n                                BindingResource::SamplerArray(Cow::Owned(resolved))\n                            }\n                            ResolvedBindingResource::TextureView(texture_view_id) => {\n                                BindingResource::TextureView(texture_view_id.to_trace())\n                            }\n                            ResolvedBindingResource::TextureViewArray(texture_view_ids) => {\n                                let resolved: Vec<_> =\n                                    texture_view_ids.iter().map(|id| id.to_trace()).collect();\n                                BindingResource::TextureViewArray(Cow::Owned(resolved))\n                            }\n                            ResolvedBindingResource::AccelerationStructure(tlas_id) => {\n                                BindingResource::AccelerationStructure(tlas_id.to_trace())\n                            }\n                            ResolvedBindingResource::AccelerationStructureArray(tlas_ids) => {\n                                let resolved: Vec<_> =\n                                    tlas_ids.iter().map(|id| id.to_trace()).collect();\n                                BindingResource::AccelerationStructureArray(Cow::Owned(resolved))\n                            }\n                            ResolvedBindingResource::ExternalTexture(external_texture_id) => {\n                                BindingResource::ExternalTexture(external_texture_id.to_trace())\n                            }\n                        };\n                        BindGroupEntry {\n                            binding: entry.binding,\n                            resource,\n                        }\n                    })\n                    .collect(),\n            ),\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {\n    type Output = TraceGeneralRenderPipelineDescriptor<'a>;\n\n    fn into_trace(self) -> Self::Output {\n        TraceGeneralRenderPipelineDescriptor {\n            label: self.label,\n            layout: self.layout.into_trace(),\n            vertex: self.vertex.into_trace(),\n            primitive: self.primitive,\n            depth_stencil: self.depth_stencil,\n            multisample: self.multisample,\n            fragment: self.fragment.map(|f| f.into_trace()),\n            multiview_mask: self.multiview_mask,\n            cache: self.cache.map(|c| c.into_trace()),\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedComputePipelineDescriptor<'a> {\n    type Output = TraceComputePipelineDescriptor<'a>;\n\n    fn into_trace(self) -> Self::Output {\n        TraceComputePipelineDescriptor {\n            label: self.label,\n            layout: self.layout.into_trace(),\n            stage: self.stage.into_trace(),\n            cache: self.cache.map(|c| c.into_trace()),\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedProgrammableStageDescriptor<'a> {\n    type Output =\n        crate::pipeline::ProgrammableStageDescriptor<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        crate::pipeline::ProgrammableStageDescriptor {\n            module: self.module.into_trace(),\n            entry_point: self.entry_point,\n            constants: self.constants,\n            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,\n        }\n    }\n}\n\nimpl<'a> IntoTrace\n    for crate::pipeline::RenderPipelineVertexProcessor<'a, Arc<crate::pipeline::ShaderModule>>\n{\n    type Output =\n        crate::pipeline::RenderPipelineVertexProcessor<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        match self {\n            crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {\n                crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex.into_trace())\n            }\n            crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {\n                crate::pipeline::RenderPipelineVertexProcessor::Mesh(\n                    task.map(|t| t.into_trace()),\n                    mesh.into_trace(),\n                )\n            }\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedTaskState<'a> {\n    type Output = crate::pipeline::TaskState<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        crate::pipeline::TaskState {\n            stage: self.stage.into_trace(),\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedMeshState<'a> {\n    type Output = crate::pipeline::MeshState<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        crate::pipeline::MeshState {\n            stage: self.stage.into_trace(),\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedVertexState<'a> {\n    type Output = crate::pipeline::VertexState<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        crate::pipeline::VertexState {\n            stage: self.stage.into_trace(),\n            buffers: self.buffers,\n        }\n    }\n}\n\nimpl<'a> IntoTrace for crate::pipeline::ResolvedFragmentState<'a> {\n    type Output = crate::pipeline::FragmentState<'a, PointerId<markers::ShaderModule>>;\n    fn into_trace(self) -> Self::Output {\n        crate::pipeline::FragmentState {\n            stage: self.stage.into_trace(),\n            targets: self.targets,\n        }\n    }\n}\n\nimpl<T: IntoTrace> IntoTrace for Option<T> {\n    type Output = Option<T::Output>;\n    fn into_trace(self) -> Self::Output {\n        self.map(|v| v.into_trace())\n    }\n}\n\n/// For selected `Action`s (mostly actions containing only owned data), return a\n/// copy with `'static` lifetime.\n///\n/// This is used for in-memory tracing.\n///\n/// # Panics\n///\n/// If `action` is not supported for in-memory tracing (likely because it\n/// contains borrowed data).\nfn action_to_owned(action: Action<'_, PointerReferences>) -> Action<'static, PointerReferences> {\n    use Action as A;\n    match action {\n        A::Init { desc, backend } => A::Init {\n            desc: wgt::DeviceDescriptor {\n                label: desc.label.map(|l| Cow::Owned(l.into_owned())),\n                required_features: desc.required_features,\n                required_limits: desc.required_limits,\n                experimental_features: desc.experimental_features,\n                memory_hints: desc.memory_hints,\n                trace: desc.trace,\n            },\n            backend,\n        },\n        A::ConfigureSurface(surface, config) => A::ConfigureSurface(surface, config),\n        A::CreateBuffer(buffer, desc) => A::CreateBuffer(\n            buffer,\n            wgt::BufferDescriptor {\n                label: desc.label.map(|l| Cow::Owned(l.into_owned())),\n                size: desc.size,\n                usage: desc.usage,\n                mapped_at_creation: desc.mapped_at_creation,\n            },\n        ),\n        A::FreeBuffer(buffer) => A::FreeBuffer(buffer),\n        A::DestroyBuffer(buffer) => A::DestroyBuffer(buffer),\n        A::FreeTexture(texture) => A::FreeTexture(texture),\n        A::DestroyTexture(texture) => A::DestroyTexture(texture),\n        A::DestroyTextureView(texture_view) => A::DestroyTextureView(texture_view),\n        A::FreeExternalTexture(external_texture) => A::FreeExternalTexture(external_texture),\n        A::DestroyExternalTexture(external_texture) => A::DestroyExternalTexture(external_texture),\n        A::DestroySampler(sampler) => A::DestroySampler(sampler),\n        A::GetSurfaceTexture { id, parent } => A::GetSurfaceTexture { id, parent },\n        A::Present(surface) => A::Present(surface),\n        A::DiscardSurfaceTexture(surface) => A::DiscardSurfaceTexture(surface),\n        A::DestroyBindGroupLayout(layout) => A::DestroyBindGroupLayout(layout),\n        A::GetRenderPipelineBindGroupLayout {\n            id,\n            pipeline,\n            index,\n        } => A::GetRenderPipelineBindGroupLayout {\n            id,\n            pipeline,\n            index,\n        },\n        A::GetComputePipelineBindGroupLayout {\n            id,\n            pipeline,\n            index,\n        } => A::GetComputePipelineBindGroupLayout {\n            id,\n            pipeline,\n            index,\n        },\n        A::DestroyPipelineLayout(layout) => A::DestroyPipelineLayout(layout),\n        A::DestroyBindGroup(bind_group) => A::DestroyBindGroup(bind_group),\n        A::DestroyShaderModule(shader_module) => A::DestroyShaderModule(shader_module),\n        A::DestroyComputePipeline(pipeline) => A::DestroyComputePipeline(pipeline),\n        A::DestroyRenderPipeline(pipeline) => A::DestroyRenderPipeline(pipeline),\n        A::DestroyPipelineCache(cache) => A::DestroyPipelineCache(cache),\n        A::DestroyRenderBundle(render_bundle) => A::DestroyRenderBundle(render_bundle),\n        A::DestroyQuerySet(query_set) => A::DestroyQuerySet(query_set),\n        A::WriteBuffer {\n            id,\n            data,\n            offset,\n            size,\n            queued,\n        } => A::WriteBuffer {\n            id,\n            data,\n            offset,\n            size,\n            queued,\n        },\n        A::WriteTexture {\n            to,\n            data,\n            layout,\n            size,\n        } => A::WriteTexture {\n            to,\n            data,\n            layout,\n            size,\n        },\n        A::Submit(index, commands) => A::Submit(index, commands),\n        A::FailedCommands {\n            commands,\n            failed_at_submit,\n            error,\n        } => A::FailedCommands {\n            commands,\n            failed_at_submit,\n            error,\n        },\n        A::DestroyBlas(blas) => A::DestroyBlas(blas),\n        A::DestroyTlas(tlas) => A::DestroyTlas(tlas),\n\n        A::CreateTexture(..)\n        | A::CreateTextureView { .. }\n        | A::CreateExternalTexture { .. }\n        | A::CreateSampler(..)\n        | A::CreateBindGroupLayout(..)\n        | A::CreatePipelineLayout(..)\n        | A::CreateBindGroup(..)\n        | A::CreateShaderModule { .. }\n        | A::CreateShaderModulePassthrough { .. }\n        | A::CreateComputePipeline { .. }\n        | A::CreateGeneralRenderPipeline { .. }\n        | A::CreatePipelineCache { .. }\n        | A::CreateRenderBundle { .. }\n        | A::CreateQuerySet { .. }\n        | A::CreateBlas { .. }\n        | A::CreateTlas { .. } => panic!(\"Unsupported action for tracing: {action:?}\"),\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/trace/replay.rs",
    "content": "use alloc::borrow::Cow;\n\nuse super::Data;\n\npub trait DataLoader {\n    fn load<'loader, 'data>(&'loader self, data: &'data Data) -> Cow<'data, [u8]>;\n    fn load_utf8<'loader, 'data>(&'loader self, data: &'data Data) -> Cow<'data, str>;\n}\n\n#[cfg(feature = \"std\")]\npub struct DiskTraceLoader<'a>(&'a std::path::Path);\n\n#[cfg(feature = \"std\")]\nimpl<'a> DiskTraceLoader<'a> {\n    pub fn new(path: &'a std::path::Path) -> DiskTraceLoader<'a> {\n        DiskTraceLoader(path)\n    }\n}\n\nimpl DataLoader for DiskTraceLoader<'_> {\n    fn load<'loader, 'data>(&'loader self, data: &'data Data) -> Cow<'data, [u8]> {\n        match data {\n            Data::File(file) => {\n                Cow::from(std::fs::read(self.0.join(file)).expect(\"Failed to read data file\"))\n            }\n            Data::String(_, s) => Cow::from(s.as_bytes()),\n            Data::Binary(_, b) => Cow::from(b),\n        }\n    }\n\n    /// Load UTF-8 string data\n    ///\n    /// # Panics\n    ///\n    /// If the data kind is not a string format or the data is not valid UTF-8\n    fn load_utf8<'loader, 'data>(&'loader self, data: &'data Data) -> Cow<'data, str> {\n        match data {\n            Data::File(file) => Cow::from(\n                std::fs::read_to_string(self.0.join(file)).expect(\"Failed to read data file\"),\n            ),\n            Data::String(kind, s) => {\n                assert!(kind.is_string(), \"{kind:?} cannot be loaded as a string\");\n                Cow::from(s)\n            }\n            Data::Binary(kind, b) => {\n                assert!(kind.is_string(), \"{kind:?} cannot be loaded as a string\");\n                Cow::from(core::str::from_utf8(b).unwrap())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/device/trace.rs",
    "content": "#[cfg(feature = \"trace\")]\nmod record;\n#[cfg(feature = \"replay\")]\nmod replay;\n\nuse core::convert::Infallible;\n\nuse alloc::{string::String, vec::Vec};\nuse macro_rules_attribute::apply;\n\nuse crate::{\n    command::{serde_object_reference_struct, BasePass, Command, ReferenceType, RenderCommand},\n    id::{markers, PointerId},\n    pipeline::GeneralRenderPipelineDescriptor,\n};\n\n#[cfg(feature = \"trace\")]\npub use record::*;\n#[cfg(feature = \"replay\")]\npub use replay::*;\n\ntype FileName = String;\n\npub const FILE_NAME: &str = \"trace.ron\";\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub enum Data {\n    File(FileName),\n    String(DataKind, String),\n    Binary(DataKind, Vec<u8>),\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(\n    feature = \"serde\",\n    derive(serde::Serialize, serde::Deserialize),\n    serde(rename_all = \"lowercase\")\n)]\npub enum DataKind {\n    Bin,\n    Wgsl,\n\n    /// IR of Naga module, serialized in RON format\n    Ron,\n    Spv,\n    Dxil,\n    Hlsl,\n    MetalLib,\n    Msl,\n    Glsl,\n}\n\nimpl core::fmt::Display for DataKind {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let s = match self {\n            DataKind::Bin => \"bin\",\n            DataKind::Wgsl => \"wgsl\",\n            DataKind::Ron => \"ron\",\n            DataKind::Spv => \"spv\",\n            DataKind::Dxil => \"dxil\",\n            DataKind::Hlsl => \"hlsl\",\n            DataKind::MetalLib => \"metallib\",\n            DataKind::Msl => \"metal\",\n            DataKind::Glsl => \"glsl\",\n        };\n        write!(f, \"{s}\")\n    }\n}\n\nimpl DataKind {\n    #[cfg(feature = \"replay\")]\n    fn is_string(&self) -> bool {\n        match *self {\n            DataKind::Wgsl | DataKind::Ron | DataKind::Hlsl | DataKind::Msl | DataKind::Glsl => {\n                true\n            }\n            DataKind::Bin | DataKind::Spv | DataKind::Dxil | DataKind::MetalLib => false,\n        }\n    }\n}\n\nimpl Data {\n    pub fn kind(&self) -> DataKind {\n        match self {\n            Data::File(file) => {\n                if file.ends_with(\".bin\") {\n                    DataKind::Bin\n                } else if file.ends_with(\".wgsl\") {\n                    DataKind::Wgsl\n                } else if file.ends_with(\".ron\") {\n                    DataKind::Ron\n                } else if file.ends_with(\".spv\") {\n                    DataKind::Spv\n                } else if file.ends_with(\".dxil\") {\n                    DataKind::Dxil\n                } else if file.ends_with(\".hlsl\") {\n                    DataKind::Hlsl\n                } else if file.ends_with(\".metallib\") {\n                    DataKind::MetalLib\n                } else if file.ends_with(\".metal\") {\n                    DataKind::Msl\n                } else if file.ends_with(\".glsl\") {\n                    DataKind::Glsl\n                } else {\n                    panic!(\"unknown data file extension: {file}\");\n                }\n            }\n            Data::String(kind, _) => *kind,\n            Data::Binary(kind, _) => *kind,\n        }\n    }\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug)]\n#[apply(serde_object_reference_struct)]\npub enum Action<'a, R: ReferenceType> {\n    Init {\n        desc: crate::device::DeviceDescriptor<'a>,\n        backend: wgt::Backend,\n    },\n    ConfigureSurface(\n        R::Surface,\n        wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,\n    ),\n    CreateBuffer(R::Buffer, crate::resource::BufferDescriptor<'a>),\n    FreeBuffer(R::Buffer),\n    DestroyBuffer(R::Buffer),\n    CreateTexture(R::Texture, crate::resource::TextureDescriptor<'a>),\n    FreeTexture(R::Texture),\n    DestroyTexture(R::Texture),\n    CreateTextureView {\n        id: R::TextureView,\n        parent: R::Texture,\n        desc: crate::resource::TextureViewDescriptor<'a>,\n    },\n    DestroyTextureView(R::TextureView),\n    CreateExternalTexture {\n        id: R::ExternalTexture,\n        desc: crate::resource::ExternalTextureDescriptor<'a>,\n        planes: alloc::boxed::Box<[R::TextureView]>,\n    },\n    FreeExternalTexture(R::ExternalTexture),\n    DestroyExternalTexture(R::ExternalTexture),\n    CreateSampler(\n        PointerId<markers::Sampler>,\n        crate::resource::SamplerDescriptor<'a>,\n    ),\n    DestroySampler(PointerId<markers::Sampler>),\n    GetSurfaceTexture {\n        id: R::Texture,\n        parent: R::Surface,\n    },\n    Present(R::Surface),\n    DiscardSurfaceTexture(R::Surface),\n    CreateBindGroupLayout(\n        PointerId<markers::BindGroupLayout>,\n        crate::binding_model::BindGroupLayoutDescriptor<'a>,\n    ),\n    GetRenderPipelineBindGroupLayout {\n        id: PointerId<markers::BindGroupLayout>,\n        pipeline: PointerId<markers::RenderPipeline>,\n        index: u32,\n    },\n    GetComputePipelineBindGroupLayout {\n        id: PointerId<markers::BindGroupLayout>,\n        pipeline: PointerId<markers::ComputePipeline>,\n        index: u32,\n    },\n    DestroyBindGroupLayout(PointerId<markers::BindGroupLayout>),\n    CreatePipelineLayout(\n        PointerId<markers::PipelineLayout>,\n        crate::binding_model::ResolvedPipelineLayoutDescriptor<\n            'a,\n            PointerId<markers::BindGroupLayout>,\n        >,\n    ),\n    DestroyPipelineLayout(PointerId<markers::PipelineLayout>),\n    CreateBindGroup(PointerId<markers::BindGroup>, TraceBindGroupDescriptor<'a>),\n    DestroyBindGroup(PointerId<markers::BindGroup>),\n    CreateShaderModule {\n        id: PointerId<markers::ShaderModule>,\n        desc: crate::pipeline::ShaderModuleDescriptor<'a>,\n        data: Data,\n    },\n    CreateShaderModulePassthrough {\n        id: PointerId<markers::ShaderModule>,\n        data: Vec<Data>,\n\n        label: crate::Label<'a>,\n        num_workgroups: (u32, u32, u32),\n    },\n    DestroyShaderModule(PointerId<markers::ShaderModule>),\n    CreateComputePipeline {\n        id: Option<PointerId<markers::ComputePipeline>>,\n        desc: TraceComputePipelineDescriptor<'a>,\n    },\n    DestroyComputePipeline(PointerId<markers::ComputePipeline>),\n    CreateGeneralRenderPipeline {\n        id: Option<PointerId<markers::RenderPipeline>>,\n        desc: TraceGeneralRenderPipelineDescriptor<'a>,\n    },\n    DestroyRenderPipeline(PointerId<markers::RenderPipeline>),\n    CreatePipelineCache {\n        id: PointerId<markers::PipelineCache>,\n        desc: crate::pipeline::PipelineCacheDescriptor<'a>,\n    },\n    DestroyPipelineCache(PointerId<markers::PipelineCache>),\n    CreateRenderBundle {\n        id: R::RenderBundle,\n        desc: crate::command::RenderBundleEncoderDescriptor<'a>,\n        base: BasePass<RenderCommand<R>, Infallible>,\n    },\n    DestroyRenderBundle(PointerId<markers::RenderBundle>),\n    CreateQuerySet {\n        id: PointerId<markers::QuerySet>,\n        desc: crate::resource::QuerySetDescriptor<'a>,\n    },\n    DestroyQuerySet(PointerId<markers::QuerySet>),\n    WriteBuffer {\n        id: R::Buffer,\n        data: Data,\n        offset: wgt::BufferAddress,\n        size: wgt::BufferAddress,\n        queued: bool,\n    },\n    WriteTexture {\n        to: wgt::TexelCopyTextureInfo<R::Texture>,\n        data: Data,\n        layout: wgt::TexelCopyBufferLayout,\n        size: wgt::Extent3d,\n    },\n    Submit(crate::SubmissionIndex, Vec<Command<R>>),\n    FailedCommands {\n        commands: Option<Vec<Command<R>>>,\n        /// If `None`, then encoding failed due to a validation error (returned\n        /// from `CommandEncoder::finish`). If `Some`, submission failed due to\n        /// a resource having been destroyed.\n        failed_at_submit: Option<crate::SubmissionIndex>,\n        error: String,\n    },\n    CreateBlas {\n        id: R::Blas,\n        desc: crate::resource::BlasDescriptor<'a>,\n        sizes: wgt::BlasGeometrySizeDescriptors,\n    },\n    DestroyBlas(R::Blas),\n    CreateTlas {\n        id: R::Tlas,\n        desc: crate::resource::TlasDescriptor<'a>,\n    },\n    DestroyTlas(R::Tlas),\n}\n\n/// cbindgen:ignore\npub type TraceBindGroupDescriptor<'a> = crate::binding_model::BindGroupDescriptor<\n    'a,\n    PointerId<markers::BindGroupLayout>,\n    PointerId<markers::Buffer>,\n    PointerId<markers::Sampler>,\n    PointerId<markers::TextureView>,\n    PointerId<markers::Tlas>,\n    PointerId<markers::ExternalTexture>,\n>;\n\n/// Not a public API. For use by `player` only.\n///\n/// cbindgen:ignore\n#[doc(hidden)]\npub type TraceGeneralRenderPipelineDescriptor<'a> = GeneralRenderPipelineDescriptor<\n    'a,\n    PointerId<markers::PipelineLayout>,\n    PointerId<markers::ShaderModule>,\n    PointerId<markers::PipelineCache>,\n>;\n\n/// Not a public API. For use by `player` only.\n///\n/// cbindgen:ignore\n#[doc(hidden)]\npub type TraceComputePipelineDescriptor<'a> = crate::pipeline::ComputePipelineDescriptor<\n    'a,\n    PointerId<markers::PipelineLayout>,\n    PointerId<markers::ShaderModule>,\n    PointerId<markers::PipelineCache>,\n>;\n"
  },
  {
    "path": "wgpu-core/src/error.rs",
    "content": "use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};\nuse core::{error::Error, fmt};\n\nuse thiserror::Error;\n\n#[cfg(send_sync)]\npub type ContextErrorSource = Box<dyn Error + Send + Sync + 'static>;\n#[cfg(not(send_sync))]\npub type ContextErrorSource = Box<dyn Error + 'static>;\n\n#[derive(Debug, Error)]\n#[error(\n    \"In {fn_ident}{}{}{}\",\n    if self.label.is_empty() { \"\" } else { \", label = '\" },\n    self.label,\n    if self.label.is_empty() { \"\" } else { \"'\" }\n)]\npub struct ContextError {\n    pub fn_ident: &'static str,\n    #[source]\n    pub source: ContextErrorSource,\n    pub label: String,\n}\n\n/// Don't use this error type with thiserror's #[error(transparent)]\n#[derive(Clone)]\npub struct MultiError {\n    inner: Vec<Arc<dyn Error + Send + Sync + 'static>>,\n}\n\nimpl MultiError {\n    pub fn new<T: Error + Send + Sync + 'static>(\n        iter: impl ExactSizeIterator<Item = T>,\n    ) -> Option<Self> {\n        if iter.len() == 0 {\n            return None;\n        }\n        Some(Self {\n            inner: iter.map(Box::from).map(Arc::from).collect(),\n        })\n    }\n\n    pub fn errors(&self) -> Box<dyn Iterator<Item = &(dyn Error + Send + Sync + 'static)> + '_> {\n        Box::new(self.inner.iter().map(|e| e.as_ref()))\n    }\n}\n\nimpl fmt::Debug for MultiError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        fmt::Debug::fmt(&self.inner[0], f)\n    }\n}\n\nimpl fmt::Display for MultiError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        fmt::Display::fmt(&self.inner[0], f)\n    }\n}\n\nimpl Error for MultiError {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        self.inner[0].source()\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/global.rs",
    "content": "use alloc::{borrow::ToOwned as _, sync::Arc};\nuse core::fmt;\n\nuse crate::{\n    hub::{Hub, HubReport},\n    instance::{Instance, Surface},\n    registry::{Registry, RegistryReport},\n    resource_log,\n};\n\n#[derive(Debug, PartialEq, Eq)]\npub struct GlobalReport {\n    pub surfaces: RegistryReport,\n    pub hub: HubReport,\n}\n\nimpl GlobalReport {\n    pub fn surfaces(&self) -> &RegistryReport {\n        &self.surfaces\n    }\n    pub fn hub_report(&self) -> &HubReport {\n        &self.hub\n    }\n}\n\npub struct Global {\n    pub(crate) surfaces: Registry<Arc<Surface>>,\n    pub(crate) hub: Hub,\n    // the instance must be dropped last\n    pub instance: Instance,\n}\n\nimpl Global {\n    pub fn new(\n        name: &str,\n        instance_desc: wgt::InstanceDescriptor,\n        telemetry: Option<hal::Telemetry>,\n    ) -> Self {\n        profiling::scope!(\"Global::new\");\n        Self {\n            instance: Instance::new(name, instance_desc, telemetry),\n            surfaces: Registry::new(),\n            hub: Hub::new(),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Refer to the creation of wgpu-hal Instance for every backend.\n    pub unsafe fn from_hal_instance<A: hal::Api>(name: &str, hal_instance: A::Instance) -> Self {\n        profiling::scope!(\"Global::new\");\n\n        Self {\n            instance: Instance::from_hal_instance::<A>(name.to_owned(), hal_instance),\n            surfaces: Registry::new(),\n            hub: Hub::new(),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// - The raw instance handle returned must not be manually destroyed.\n    pub unsafe fn instance_as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {\n        unsafe { self.instance.as_hal::<A>() }\n    }\n\n    /// # Safety\n    ///\n    /// - The raw handles obtained from the Instance must not be manually destroyed\n    pub unsafe fn from_instance(instance: Instance) -> Self {\n        profiling::scope!(\"Global::new\");\n        Self {\n            instance,\n            surfaces: Registry::new(),\n            hub: Hub::new(),\n        }\n    }\n\n    pub fn generate_report(&self) -> GlobalReport {\n        GlobalReport {\n            surfaces: self.surfaces.generate_report(),\n            hub: self.hub.generate_report(),\n        }\n    }\n}\n\nimpl fmt::Debug for Global {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"Global\").finish()\n    }\n}\n\nimpl Drop for Global {\n    fn drop(&mut self) {\n        profiling::scope!(\"Global::drop\");\n        resource_log!(\"Global::drop\");\n    }\n}\n\n#[cfg(send_sync)]\nfn _test_send_sync(global: &Global) {\n    fn test_internal<T: Send + Sync>(_: T) {}\n    test_internal(global)\n}\n"
  },
  {
    "path": "wgpu-core/src/hash_utils.rs",
    "content": "//! Module for hashing utilities.\n//!\n//! Named hash_utils to prevent clashing with the core::hash module.\n\n/// HashMap using a fast, non-cryptographic hash algorithm.\npub type FastHashMap<K, V> =\n    hashbrown::HashMap<K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n/// HashSet using a fast, non-cryptographic hash algorithm.\npub type FastHashSet<K> =\n    hashbrown::HashSet<K, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n\n/// IndexMap using a fast, non-cryptographic hash algorithm.\npub type FastIndexMap<K, V> =\n    indexmap::IndexMap<K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;\n"
  },
  {
    "path": "wgpu-core/src/hub.rs",
    "content": "/*! Allocating resource ids, and tracking the resources they refer to.\n\nThe `wgpu_core` API uses identifiers of type [`Id<R>`] to refer to\nresources of type `R`. For example, [`id::DeviceId`] is an alias for\n`Id<markers::Device>`, and [`id::BufferId`] is an alias for\n`Id<markers::Buffer>`. `Id` implements `Copy`, `Hash`, `Eq`, `Ord`, and\nof course `Debug`.\n\n[`id::DeviceId`]: crate::id::DeviceId\n[`id::BufferId`]: crate::id::BufferId\n\nEach `Id` contains not only an index for the resource it denotes but\nalso a Backend indicating which `wgpu` backend it belongs to.\n\n`Id`s also incorporate a generation number, for additional validation.\n\nThe resources to which identifiers refer are freed explicitly.\nAttempting to use an identifier for a resource that has been freed\nelicits an error result.\n\nEventually, we would like to remove numeric IDs from wgpu-core.\nSee <https://github.com/gfx-rs/wgpu/issues/5121>.\n\n## Assigning ids to resources\n\nThe users of `wgpu_core` generally want resource ids to be assigned\nin one of two ways:\n\n- Users like `wgpu` want `wgpu_core` to assign ids to resources itself.\n  For example, `wgpu` expects to call `Global::device_create_buffer`\n  and have the return value indicate the newly created buffer's id.\n\n- Users like Firefox want to allocate ids themselves, and pass\n  `Global::device_create_buffer` and friends the id to assign the new\n  resource.\n\nTo accommodate either pattern, `wgpu_core` methods that create\nresources all expect an `id_in` argument that the caller can use to\nspecify the id, and they all return the id used. For example, the\ndeclaration of `Global::device_create_buffer` looks like this:\n\n```ignore\nimpl Global {\n    /* ... */\n    pub fn device_create_buffer<A: HalApi>(\n        &self,\n        device_id: id::DeviceId,\n        desc: &resource::BufferDescriptor,\n        id_in: Input<G>,\n    ) -> (id::BufferId, Option<resource::CreateBufferError>) {\n        /* ... */\n    }\n    /* ... */\n}\n```\n\nUsers that want to assign resource ids themselves pass in the id they\nwant as the `id_in` argument, whereas users that want `wgpu_core`\nitself to choose ids always pass `()`. In either case, the id\nultimately assigned is returned as the first element of the tuple.\n\nProducing true identifiers from `id_in` values is the job of an\n[`crate::identity::IdentityManager`] or ids will be received from outside through `Option<Id>` arguments.\n\n## Id allocation and streaming\n\nPerhaps surprisingly, allowing users to assign resource ids themselves\nenables major performance improvements in some applications.\n\nThe `wgpu_core` API is designed for use by Firefox's [WebGPU]\nimplementation. For security, web content and GPU use must be kept\nsegregated in separate processes, with all interaction between them\nmediated by an inter-process communication protocol. As web content uses\nthe WebGPU API, the content process sends messages to the GPU process,\nwhich interacts with the platform's GPU APIs on content's behalf,\noccasionally sending results back.\n\nIn a classic Rust API, a resource allocation function takes parameters\ndescribing the resource to create, and if creation succeeds, it returns\nthe resource id in a `Result::Ok` value. However, this design is a poor\nfit for the split-process design described above: content must wait for\nthe reply to its buffer-creation message (say) before it can know which\nid it can use in the next message that uses that buffer. On a common\nusage pattern, the classic Rust design imposes the latency of a full\ncross-process round trip.\n\nWe can avoid incurring these round-trip latencies simply by letting the\ncontent process assign resource ids itself. With this approach, content\ncan choose an id for the new buffer, send a message to create the\nbuffer, and then immediately send the next message operating on that\nbuffer, since it already knows its id. Allowing content and GPU process\nactivity to be pipelined greatly improves throughput.\n\nTo help propagate errors correctly in this style of usage, when resource\ncreation fails, the id supplied for that resource is marked to indicate\nas much, allowing subsequent operations using that id to be properly\nflagged as errors as well.\n\n[`process`]: crate::identity::IdentityManager::process\n[`Id<R>`]: crate::id::Id\n[wrapped in a mutex]: trait.IdentityHandler.html#impl-IdentityHandler%3CI%3E-for-Mutex%3CIdentityManager%3E\n[WebGPU]: https://www.w3.org/TR/webgpu/\n\n## IDs and tracing\n\nAs of `wgpu` v27, commands are encoded all at once when\n`CommandEncoder::finish` is called, not when the encoding methods are\ncalled for each command. This implies storing a representation of the\ncommands in memory until `finish` is called.  `Arc`s are more suitable\nfor this purpose than numeric ids. Rather than redundantly store both\n`Id`s and `Arc`s, tracing has been changed to work with `Arc`s. The\nserialized trace identifies resources by the integer value of\n`Arc::as_ptr`. These IDs have the type [`crate::id::PointerId`]. The\ntrace player uses hash maps to go from `PointerId`s to `Arc`s\nwhen replaying a trace.\n\n*/\n\nuse alloc::sync::Arc;\nuse core::fmt::Debug;\n\nuse crate::{\n    binding_model::{BindGroup, BindGroupLayout, PipelineLayout},\n    command::{CommandBuffer, CommandEncoder, RenderBundle},\n    device::{queue::Queue, Device},\n    instance::Adapter,\n    pipeline::{ComputePipeline, PipelineCache, RenderPipeline, ShaderModule},\n    registry::{Registry, RegistryReport},\n    resource::{\n        Blas, Buffer, ExternalTexture, Fallible, QuerySet, Sampler, StagingBuffer, Texture,\n        TextureView, Tlas,\n    },\n};\n\n#[derive(Debug, PartialEq, Eq)]\npub struct HubReport {\n    pub adapters: RegistryReport,\n    pub devices: RegistryReport,\n    pub queues: RegistryReport,\n    pub pipeline_layouts: RegistryReport,\n    pub shader_modules: RegistryReport,\n    pub bind_group_layouts: RegistryReport,\n    pub bind_groups: RegistryReport,\n    pub command_encoders: RegistryReport,\n    pub command_buffers: RegistryReport,\n    pub render_bundles: RegistryReport,\n    pub render_pipelines: RegistryReport,\n    pub compute_pipelines: RegistryReport,\n    pub pipeline_caches: RegistryReport,\n    pub query_sets: RegistryReport,\n    pub buffers: RegistryReport,\n    pub textures: RegistryReport,\n    pub texture_views: RegistryReport,\n    pub external_textures: RegistryReport,\n    pub samplers: RegistryReport,\n}\n\nimpl HubReport {\n    pub fn is_empty(&self) -> bool {\n        self.adapters.is_empty()\n    }\n}\n\n#[allow(rustdoc::private_intra_doc_links)]\n/// All the resources tracked by a [`crate::global::Global`].\n///\n/// ## Locking\n///\n/// Each field in `Hub` is a [`Registry`] holding all the values of a\n/// particular type of resource, all protected by a single RwLock.\n/// So for example, to access any [`Buffer`], you must acquire a read\n/// lock on the `Hub`s entire buffers registry. The lock guard\n/// gives you access to the `Registry`'s [`Storage`], which you can\n/// then index with the buffer's id. (Yes, this design causes\n/// contention; see [#2272].)\n///\n/// But most `wgpu` operations require access to several different\n/// kinds of resource, so you often need to hold locks on several\n/// different fields of your [`Hub`] simultaneously.\n///\n/// Inside the `Registry` there are `Arc<T>` where `T` is a Resource\n/// Lock of `Registry` happens only when accessing to get the specific resource\n///\n/// [`Storage`]: crate::storage::Storage\npub struct Hub {\n    pub(crate) adapters: Registry<Arc<Adapter>>,\n    pub(crate) devices: Registry<Arc<Device>>,\n    pub(crate) queues: Registry<Arc<Queue>>,\n    pub(crate) pipeline_layouts: Registry<Fallible<PipelineLayout>>,\n    pub(crate) shader_modules: Registry<Fallible<ShaderModule>>,\n    pub(crate) bind_group_layouts: Registry<Fallible<BindGroupLayout>>,\n    pub(crate) bind_groups: Registry<Fallible<BindGroup>>,\n    pub(crate) command_encoders: Registry<Arc<CommandEncoder>>,\n    pub(crate) command_buffers: Registry<Arc<CommandBuffer>>,\n    pub(crate) render_bundles: Registry<Fallible<RenderBundle>>,\n    pub(crate) render_pipelines: Registry<Fallible<RenderPipeline>>,\n    pub(crate) compute_pipelines: Registry<Fallible<ComputePipeline>>,\n    pub(crate) pipeline_caches: Registry<Fallible<PipelineCache>>,\n    pub(crate) query_sets: Registry<Fallible<QuerySet>>,\n    pub(crate) buffers: Registry<Fallible<Buffer>>,\n    pub(crate) staging_buffers: Registry<StagingBuffer>,\n    pub(crate) textures: Registry<Fallible<Texture>>,\n    pub(crate) texture_views: Registry<Fallible<TextureView>>,\n    pub(crate) external_textures: Registry<Fallible<ExternalTexture>>,\n    pub(crate) samplers: Registry<Fallible<Sampler>>,\n    pub(crate) blas_s: Registry<Fallible<Blas>>,\n    pub(crate) tlas_s: Registry<Fallible<Tlas>>,\n}\n\nimpl Hub {\n    pub(crate) fn new() -> Self {\n        Self {\n            adapters: Registry::new(),\n            devices: Registry::new(),\n            queues: Registry::new(),\n            pipeline_layouts: Registry::new(),\n            shader_modules: Registry::new(),\n            bind_group_layouts: Registry::new(),\n            bind_groups: Registry::new(),\n            command_encoders: Registry::new(),\n            command_buffers: Registry::new(),\n            render_bundles: Registry::new(),\n            render_pipelines: Registry::new(),\n            compute_pipelines: Registry::new(),\n            pipeline_caches: Registry::new(),\n            query_sets: Registry::new(),\n            buffers: Registry::new(),\n            staging_buffers: Registry::new(),\n            textures: Registry::new(),\n            texture_views: Registry::new(),\n            external_textures: Registry::new(),\n            samplers: Registry::new(),\n            blas_s: Registry::new(),\n            tlas_s: Registry::new(),\n        }\n    }\n\n    pub fn generate_report(&self) -> HubReport {\n        HubReport {\n            adapters: self.adapters.generate_report(),\n            devices: self.devices.generate_report(),\n            queues: self.queues.generate_report(),\n            pipeline_layouts: self.pipeline_layouts.generate_report(),\n            shader_modules: self.shader_modules.generate_report(),\n            bind_group_layouts: self.bind_group_layouts.generate_report(),\n            bind_groups: self.bind_groups.generate_report(),\n            command_encoders: self.command_encoders.generate_report(),\n            command_buffers: self.command_buffers.generate_report(),\n            render_bundles: self.render_bundles.generate_report(),\n            render_pipelines: self.render_pipelines.generate_report(),\n            compute_pipelines: self.compute_pipelines.generate_report(),\n            pipeline_caches: self.pipeline_caches.generate_report(),\n            query_sets: self.query_sets.generate_report(),\n            buffers: self.buffers.generate_report(),\n            textures: self.textures.generate_report(),\n            texture_views: self.texture_views.generate_report(),\n            external_textures: self.external_textures.generate_report(),\n            samplers: self.samplers.generate_report(),\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/id.rs",
    "content": "use crate::{Epoch, Index};\nuse core::{\n    cmp::Ordering,\n    fmt::{self, Debug},\n    hash::Hash,\n    marker::PhantomData,\n    num::NonZeroU64,\n};\nuse wgt::WasmNotSendSync;\n\nconst _: () = {\n    if size_of::<Index>() != 4 {\n        panic!()\n    }\n};\nconst _: () = {\n    if size_of::<Epoch>() != 4 {\n        panic!()\n    }\n};\nconst _: () = {\n    if size_of::<RawId>() != 8 {\n        panic!()\n    }\n};\n\n/// The raw underlying representation of an identifier.\n#[repr(transparent)]\n#[cfg_attr(\n    any(feature = \"serde\", feature = \"trace\"),\n    derive(serde::Serialize),\n    serde(into = \"SerialId\")\n)]\n#[cfg_attr(\n    any(feature = \"serde\", feature = \"replay\"),\n    derive(serde::Deserialize),\n    serde(try_from = \"SerialId\")\n)]\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct RawId(NonZeroU64);\n\nimpl RawId {\n    /// Zip together an identifier and return its raw underlying representation.\n    ///\n    /// # Panics\n    ///\n    /// If both ID components are zero.\n    pub fn zip(index: Index, epoch: Epoch) -> RawId {\n        let v = (index as u64) | ((epoch as u64) << 32);\n        Self(NonZeroU64::new(v).expect(\"IDs may not be zero\"))\n    }\n\n    /// Unzip a raw identifier into its components.\n    pub fn unzip(self) -> (Index, Epoch) {\n        (self.0.get() as Index, (self.0.get() >> 32) as Epoch)\n    }\n}\n\n/// An identifier for a wgpu object.\n///\n/// An `Id<T>` value identifies a value stored in a [`Global`]'s [`Hub`].\n///\n/// ## Note on `Id` typing\n///\n/// You might assume that an `Id<T>` can only be used to retrieve a resource of\n/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s\n/// public API ([`TextureId`], for example) can refer to resources belonging to\n/// any backend, but the corresponding resource types ([`Texture<A>`], for\n/// example) are always parameterized by a specific backend `A`.\n///\n/// So the `T` in `Id<T>` is usually a resource type like `Texture<Noop>`,\n/// where [`Noop`] is the `wgpu_hal` dummy back end. These empty types are\n/// never actually used, beyond just making sure you access each `Storage` with\n/// the right kind of identifier. The members of [`Hub<A>`] pair up each\n/// `X<Noop>` type with the resource type `X<A>`, for some specific backend\n/// `A`.\n///\n/// [`Global`]: crate::global::Global\n/// [`Hub`]: crate::hub::Hub\n/// [`Hub<A>`]: crate::hub::Hub\n/// [`Texture<A>`]: crate::resource::Texture\n/// [`Registry`]: crate::registry::Registry\n/// [`Noop`]: hal::api::Noop\n#[repr(transparent)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"serde\", serde(transparent))]\npub struct Id<T: Marker>(RawId, PhantomData<T>);\n\n// This type represents Id in a more readable (and editable) way.\n#[cfg(feature = \"serde\")]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug)]\npub enum SerialId {\n    // The only variant forces RON to not ignore \"Id\"\n    Id(Index, Epoch),\n}\n\n#[cfg(feature = \"serde\")]\nimpl From<RawId> for SerialId {\n    fn from(id: RawId) -> Self {\n        let (index, epoch) = id.unzip();\n        Self::Id(index, epoch)\n    }\n}\n\n#[cfg(feature = \"serde\")]\npub struct ZeroIdError;\n\n#[cfg(feature = \"serde\")]\nimpl fmt::Display for ZeroIdError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"IDs may not be zero\")\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl TryFrom<SerialId> for RawId {\n    type Error = ZeroIdError;\n    fn try_from(id: SerialId) -> Result<Self, ZeroIdError> {\n        let SerialId::Id(index, epoch) = id;\n        if index == 0 && epoch == 0 {\n            Err(ZeroIdError)\n        } else {\n            Ok(RawId::zip(index, epoch))\n        }\n    }\n}\n\n/// Identify an object by the pointer returned by `Arc::as_ptr`.\n///\n/// This is used for tracing. See [IDs and tracing](crate::hub#ids-and-tracing).\n#[allow(dead_code)]\n#[cfg(feature = \"serde\")]\n#[derive(Debug, serde::Serialize, serde::Deserialize)]\npub enum PointerId<T: Marker> {\n    // The only variant forces RON to not ignore \"Id\"\n    PointerId(core::num::NonZeroUsize, #[serde(skip)] PhantomData<T>),\n}\n\n#[cfg(feature = \"serde\")]\nimpl<T: Marker> Copy for PointerId<T> {}\n\n#[cfg(feature = \"serde\")]\nimpl<T: Marker> Clone for PointerId<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<T: Marker> PartialEq for PointerId<T> {\n    fn eq(&self, other: &Self) -> bool {\n        let PointerId::PointerId(this, _) = self;\n        let PointerId::PointerId(other, _) = other;\n        this == other\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<T: Marker> Eq for PointerId<T> {}\n\n#[cfg(feature = \"serde\")]\nimpl<T: Marker> Hash for PointerId<T> {\n    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n        let PointerId::PointerId(this, _) = self;\n        this.hash(state);\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<T: crate::storage::StorageItem> From<&alloc::sync::Arc<T>> for PointerId<T::Marker> {\n    fn from(arc: &alloc::sync::Arc<T>) -> Self {\n        // Since the memory representation of `Arc<T>` is just a pointer to\n        // `ArcInner<T>`, it would be nice to use that pointer as the trace ID,\n        // since many `into_trace` implementations would then be no-ops at\n        // runtime. However, `Arc::as_ptr` returns a pointer to the contained\n        // data, not to the `ArcInner`. The `ArcInner` stores the reference\n        // counts before the data, so the machine code for this conversion has\n        // to add an offset to the pointer.\n        PointerId::PointerId(\n            core::num::NonZeroUsize::new(alloc::sync::Arc::as_ptr(arc) as usize).unwrap(),\n            PhantomData,\n        )\n    }\n}\n\nimpl<T> Id<T>\nwhere\n    T: Marker,\n{\n    /// # Safety\n    ///\n    /// The raw id must be valid for the type.\n    pub unsafe fn from_raw(raw: RawId) -> Self {\n        Self(raw, PhantomData)\n    }\n\n    /// Coerce the identifiers into its raw underlying representation.\n    pub fn into_raw(self) -> RawId {\n        self.0\n    }\n\n    #[inline]\n    pub fn zip(index: Index, epoch: Epoch) -> Self {\n        Id(RawId::zip(index, epoch), PhantomData)\n    }\n\n    #[inline]\n    pub fn unzip(self) -> (Index, Epoch) {\n        self.0.unzip()\n    }\n}\n\nimpl<T> Copy for Id<T> where T: Marker {}\n\nimpl<T> Clone for Id<T>\nwhere\n    T: Marker,\n{\n    #[inline]\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> Debug for Id<T>\nwhere\n    T: Marker,\n{\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        let (index, epoch) = self.unzip();\n        write!(formatter, \"Id({index},{epoch})\")?;\n        Ok(())\n    }\n}\n\nimpl<T> Hash for Id<T>\nwhere\n    T: Marker,\n{\n    #[inline]\n    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n        self.0.hash(state);\n    }\n}\n\nimpl<T> PartialEq for Id<T>\nwhere\n    T: Marker,\n{\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.0 == other.0\n    }\n}\n\nimpl<T> Eq for Id<T> where T: Marker {}\n\nimpl<T> PartialOrd for Id<T>\nwhere\n    T: Marker,\n{\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl<T> Ord for Id<T>\nwhere\n    T: Marker,\n{\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.0.cmp(&other.0)\n    }\n}\n\n/// Marker trait used to determine which types uniquely identify a resource.\n///\n/// For example, `Device<A>` will have the same type of identifier as\n/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.\npub trait Marker: 'static + WasmNotSendSync {}\n\n// This allows `()` to be used as a marker type for tests.\n//\n// We don't want these in production code, since they essentially remove type\n// safety, like how identifiers across different types can be compared.\n#[cfg(test)]\nimpl Marker for () {}\n\n/// Define identifiers for each resource.\nmacro_rules! ids {\n    ($(\n        $(#[$($meta:meta)*])*\n        pub type $name:ident $marker:ident;\n    )*) => {\n        /// Marker types for each resource.\n        pub mod markers {\n            $(\n                #[derive(Debug)]\n                pub enum $marker {}\n                impl super::Marker for $marker {}\n            )*\n        }\n\n        $(\n            $(#[$($meta)*])*\n            pub type $name = Id<self::markers::$marker>;\n        )*\n    }\n}\n\nids! {\n    pub type AdapterId Adapter;\n    pub type SurfaceId Surface;\n    pub type DeviceId Device;\n    pub type QueueId Queue;\n    pub type BufferId Buffer;\n    pub type StagingBufferId StagingBuffer;\n    pub type TextureViewId TextureView;\n    pub type TextureId Texture;\n    pub type ExternalTextureId ExternalTexture;\n    pub type SamplerId Sampler;\n    pub type BindGroupLayoutId BindGroupLayout;\n    pub type PipelineLayoutId PipelineLayout;\n    pub type BindGroupId BindGroup;\n    pub type ShaderModuleId ShaderModule;\n    pub type RenderPipelineId RenderPipeline;\n    pub type ComputePipelineId ComputePipeline;\n    pub type PipelineCacheId PipelineCache;\n    pub type CommandEncoderId CommandEncoder;\n    pub type CommandBufferId CommandBuffer;\n    pub type RenderPassEncoderId RenderPassEncoder;\n    pub type ComputePassEncoderId ComputePassEncoder;\n    pub type RenderBundleEncoderId RenderBundleEncoder;\n    pub type RenderBundleId RenderBundle;\n    pub type QuerySetId QuerySet;\n    pub type BlasId Blas;\n    pub type TlasId Tlas;\n}\n\n#[test]\nfn test_id() {\n    let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];\n    let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];\n    for &i in &indexes {\n        for &e in &epochs {\n            let id = Id::<()>::zip(i, e);\n            let (index, epoch) = id.unzip();\n            assert_eq!(index, i);\n            assert_eq!(epoch, e);\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/identity.rs",
    "content": "use alloc::vec::Vec;\nuse core::{fmt::Debug, marker::PhantomData};\n\nuse crate::{\n    id::{Id, Marker},\n    lock::{rank, Mutex},\n    Epoch, Index,\n};\n\n#[derive(Copy, Clone, Debug, PartialEq)]\nenum IdSource {\n    External,\n    Allocated,\n    None,\n}\n\n/// A simple structure to allocate [`Id`] identifiers.\n///\n/// Calling [`alloc`] returns a fresh, never-before-seen id. Calling [`release`]\n/// marks an id as dead; it will never be returned again by `alloc`.\n///\n/// `IdentityValues` returns `Id`s whose index values are suitable for use as\n/// indices into a `Vec<T>` that holds those ids' referents:\n///\n/// - Every live id has a distinct index value. Every live id's index\n///   selects a distinct element in the vector.\n///\n/// - `IdentityValues` prefers low index numbers. If you size your vector to\n///   accommodate the indices produced here, the vector's length will reflect\n///   the highwater mark of actual occupancy.\n///\n/// - `IdentityValues` reuses the index values of freed ids before returning\n///   ids with new index values. Freed vector entries get reused.\n///\n/// - The non-reuse property is achieved by storing an `epoch` alongside the\n///   index in an `Id`. Index values are reused, but only with a different\n///   epoch.\n///\n/// `IdentityValues` can also be used to track the count of IDs allocated by\n/// some external allocator. Combining internal and external allocation is not\n/// allowed; calling both `alloc` and `mark_as_used` on the same\n/// `IdentityValues` will result in a panic. The external mode is used when\n/// [playing back a trace of wgpu operations][player].\n///\n/// [`Id`]: crate::id::Id\n/// [`alloc`]: IdentityValues::alloc\n/// [`release`]: IdentityValues::release\n/// [player]: https://github.com/gfx-rs/wgpu/tree/trunk/player/\n#[derive(Debug)]\npub(super) struct IdentityValues {\n    free: Vec<(Index, Epoch)>,\n    next_index: Index,\n    count: usize,\n    // Sanity check: The allocation logic works under the assumption that we don't\n    // do a mix of allocating ids from here and providing ids manually for the same\n    // storage container.\n    id_source: IdSource,\n}\n\nimpl IdentityValues {\n    /// Allocate a fresh, never-before-seen ID.\n    ///\n    /// # Panics\n    ///\n    /// If `mark_as_used` has previously been called on this `IdentityValues`.\n    pub fn alloc<T: Marker>(&mut self) -> Id<T> {\n        assert!(\n            self.id_source != IdSource::External,\n            \"Mix of internally allocated and externally provided IDs\"\n        );\n        self.id_source = IdSource::Allocated;\n\n        self.count += 1;\n        match self.free.pop() {\n            Some((index, epoch)) => Id::zip(index, epoch + 1),\n            None => {\n                let index = self.next_index;\n                self.next_index += 1;\n                let epoch = 1;\n                Id::zip(index, epoch)\n            }\n        }\n    }\n\n    /// Increment the count of used IDs.\n    ///\n    /// # Panics\n    ///\n    /// If `alloc` has previously been called on this `IdentityValues`.\n    pub fn mark_as_used<T: Marker>(&mut self, id: Id<T>) -> Id<T> {\n        assert!(\n            self.id_source != IdSource::Allocated,\n            \"Mix of internally allocated and externally provided IDs\"\n        );\n        self.id_source = IdSource::External;\n\n        self.count += 1;\n        id\n    }\n\n    /// Free `id` and/or decrement the count of used IDs.\n    ///\n    /// Freed IDs will never be returned from `alloc` again.\n    pub fn release<T: Marker>(&mut self, id: Id<T>) {\n        if let IdSource::Allocated = self.id_source {\n            let (index, epoch) = id.unzip();\n            self.free.push((index, epoch));\n        }\n        self.count -= 1;\n    }\n\n    pub fn count(&self) -> usize {\n        self.count\n    }\n}\n\n#[derive(Debug)]\npub struct IdentityManager<T: Marker> {\n    pub(super) values: Mutex<IdentityValues>,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Marker> IdentityManager<T> {\n    pub fn process(&self) -> Id<T> {\n        self.values.lock().alloc()\n    }\n    pub fn mark_as_used(&self, id: Id<T>) -> Id<T> {\n        self.values.lock().mark_as_used(id)\n    }\n    pub fn free(&self, id: Id<T>) {\n        self.values.lock().release(id)\n    }\n}\n\nimpl<T: Marker> IdentityManager<T> {\n    pub fn new() -> Self {\n        Self {\n            values: Mutex::new(\n                rank::IDENTITY_MANAGER_VALUES,\n                IdentityValues {\n                    free: Vec::new(),\n                    next_index: 0,\n                    count: 0,\n                    id_source: IdSource::None,\n                },\n            ),\n            _phantom: PhantomData,\n        }\n    }\n}\n\n#[test]\nfn test_epoch_end_of_life() {\n    use crate::id;\n    let man = IdentityManager::<id::markers::Buffer>::new();\n    let id1 = man.process();\n    assert_eq!(id1.unzip(), (0, 1));\n    man.free(id1);\n    let id2 = man.process();\n    // confirm that the epoch 1 is no longer re-used\n    assert_eq!(id2.unzip(), (0, 2));\n}\n"
  },
  {
    "path": "wgpu-core/src/indirect_validation/dispatch.rs",
    "content": "use super::CreateIndirectValidationPipelineError;\nuse crate::{\n    device::DeviceError,\n    hal_label,\n    pipeline::{CreateComputePipelineError, CreateShaderModuleError},\n};\nuse alloc::{boxed::Box, format, string::ToString as _};\nuse core::num::NonZeroU64;\n\n/// This machinery requires the following limits:\n///\n/// - max_bind_groups: 2,\n/// - max_dynamic_storage_buffers_per_pipeline_layout: 1,\n/// - max_storage_buffers_per_shader_stage: 2,\n/// - max_storage_buffer_binding_size: 3 * min_storage_buffer_offset_alignment,\n/// - max_immediate_size: 4,\n/// - max_compute_invocations_per_workgroup 1\n///\n/// These are all indirectly satisfied by `DownlevelFlags::INDIRECT_EXECUTION`, which is also\n/// required for this module's functionality to work.\n#[derive(Debug)]\npub(crate) struct Dispatch {\n    module: Box<dyn hal::DynShaderModule>,\n    dst_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    src_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    pipeline_layout: Box<dyn hal::DynPipelineLayout>,\n    pipeline: Box<dyn hal::DynComputePipeline>,\n    dst_buffer: Box<dyn hal::DynBuffer>,\n    dst_bind_group: Box<dyn hal::DynBindGroup>,\n}\n\npub struct Params<'a> {\n    pub pipeline_layout: &'a dyn hal::DynPipelineLayout,\n    pub pipeline: &'a dyn hal::DynComputePipeline,\n    pub dst_buffer: &'a dyn hal::DynBuffer,\n    pub dst_bind_group: &'a dyn hal::DynBindGroup,\n    pub aligned_offset: u64,\n    pub offset_remainder: u64,\n}\n\nimpl Dispatch {\n    pub(super) fn new(\n        device: &dyn hal::DynDevice,\n        instance_flags: wgt::InstanceFlags,\n        limits: &wgt::Limits,\n    ) -> Result<Self, CreateIndirectValidationPipelineError> {\n        let max_compute_workgroups_per_dimension = limits.max_compute_workgroups_per_dimension;\n\n        let src = format!(\n            \"\n            @group(0) @binding(0)\n            var<storage, read_write> dst: array<u32, 6>;\n            @group(1) @binding(0)\n            var<storage, read> src: array<u32>;\n            struct OffsetPc {{\n                inner: u32,\n            }}\n            var<immediate> offset: OffsetPc;\n\n            @compute @workgroup_size(1)\n            fn main() {{\n                let src = vec3(src[offset.inner], src[offset.inner + 1], src[offset.inner + 2]);\n                let max_compute_workgroups_per_dimension = {max_compute_workgroups_per_dimension}u;\n                if (\n                    src.x > max_compute_workgroups_per_dimension ||\n                    src.y > max_compute_workgroups_per_dimension ||\n                    src.z > max_compute_workgroups_per_dimension\n                ) {{\n                    dst = array(0u, 0u, 0u, 0u, 0u, 0u);\n                }} else {{\n                    dst = array(src.x, src.y, src.z, src.x, src.y, src.z);\n                }}\n            }}\n        \"\n        );\n\n        // SAFETY: The value we are passing to `new_unchecked` is not zero, so this is safe.\n        const SRC_BUFFER_SIZE: NonZeroU64 = NonZeroU64::new(size_of::<u32>() as u64 * 3).unwrap();\n\n        // SAFETY: The value we are passing to `new_unchecked` is not zero, so this is safe.\n        const DST_BUFFER_SIZE: NonZeroU64 = NonZeroU64::new(SRC_BUFFER_SIZE.get() * 2).unwrap();\n\n        #[cfg(feature = \"wgsl\")]\n        let module = naga::front::wgsl::parse_str(&src).map_err(|inner| {\n            CreateShaderModuleError::Parsing(naga::error::ShaderError {\n                source: src.clone(),\n                label: None,\n                inner: Box::new(inner),\n            })\n        })?;\n        #[cfg(not(feature = \"wgsl\"))]\n        #[allow(clippy::diverging_sub_expression)]\n        let module = panic!(\"Indirect validation requires the wgsl feature flag to be enabled!\");\n\n        let info = crate::device::create_validator(\n            wgt::Features::IMMEDIATES,\n            wgt::DownlevelFlags::empty(),\n            naga::valid::ValidationFlags::all(),\n        )\n        .validate(&module)\n        .map_err(|inner| {\n            CreateShaderModuleError::Validation(naga::error::ShaderError {\n                source: src,\n                label: None,\n                inner: Box::new(inner),\n            })\n        })?;\n        let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {\n            module: alloc::borrow::Cow::Owned(module),\n            info,\n            debug_source: None,\n        });\n        let hal_desc = hal::ShaderModuleDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation shader module\"),\n                instance_flags,\n            ),\n            runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),\n        };\n        let module =\n            unsafe { device.create_shader_module(&hal_desc, hal_shader) }.map_err(|error| {\n                match error {\n                    hal::ShaderError::Device(error) => {\n                        CreateShaderModuleError::Device(DeviceError::from_hal(error))\n                    }\n                    hal::ShaderError::Compilation(ref msg) => {\n                        log::error!(\"Shader error: {msg}\");\n                        CreateShaderModuleError::Generation\n                    }\n                }\n            })?;\n\n        let dst_bind_group_layout_desc = hal::BindGroupLayoutDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation destination bind group layout\"),\n                instance_flags,\n            ),\n            flags: hal::BindGroupLayoutFlags::empty(),\n            entries: &[wgt::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgt::ShaderStages::COMPUTE,\n                ty: wgt::BindingType::Buffer {\n                    ty: wgt::BufferBindingType::Storage { read_only: false },\n                    has_dynamic_offset: false,\n                    min_binding_size: Some(DST_BUFFER_SIZE),\n                },\n                count: None,\n            }],\n        };\n        let dst_bind_group_layout = unsafe {\n            device\n                .create_bind_group_layout(&dst_bind_group_layout_desc)\n                .map_err(DeviceError::from_hal)?\n        };\n\n        let src_bind_group_layout_desc = hal::BindGroupLayoutDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation source bind group layout\"),\n                instance_flags,\n            ),\n            flags: hal::BindGroupLayoutFlags::empty(),\n            entries: &[wgt::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgt::ShaderStages::COMPUTE,\n                ty: wgt::BindingType::Buffer {\n                    ty: wgt::BufferBindingType::Storage { read_only: true },\n                    has_dynamic_offset: true,\n                    min_binding_size: Some(SRC_BUFFER_SIZE),\n                },\n                count: None,\n            }],\n        };\n        let src_bind_group_layout = unsafe {\n            device\n                .create_bind_group_layout(&src_bind_group_layout_desc)\n                .map_err(DeviceError::from_hal)?\n        };\n\n        let pipeline_layout_desc = hal::PipelineLayoutDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation pipeline layout\"),\n                instance_flags,\n            ),\n            flags: hal::PipelineLayoutFlags::empty(),\n            bind_group_layouts: &[\n                Some(dst_bind_group_layout.as_ref()),\n                Some(src_bind_group_layout.as_ref()),\n            ],\n            immediate_size: 4,\n        };\n        let pipeline_layout = unsafe {\n            device\n                .create_pipeline_layout(&pipeline_layout_desc)\n                .map_err(DeviceError::from_hal)?\n        };\n\n        let pipeline_desc = hal::ComputePipelineDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation pipeline\"),\n                instance_flags,\n            ),\n            layout: pipeline_layout.as_ref(),\n            stage: hal::ProgrammableStage {\n                module: module.as_ref(),\n                entry_point: \"main\",\n                constants: &Default::default(),\n                zero_initialize_workgroup_memory: false,\n            },\n            cache: None,\n        };\n        let pipeline =\n            unsafe { device.create_compute_pipeline(&pipeline_desc) }.map_err(|err| match err {\n                hal::PipelineError::Device(error) => {\n                    CreateComputePipelineError::Device(DeviceError::from_hal(error))\n                }\n                hal::PipelineError::Linkage(_stages, msg) => {\n                    CreateComputePipelineError::Internal(msg)\n                }\n                hal::PipelineError::EntryPoint(_stage) => CreateComputePipelineError::Internal(\n                    crate::device::ENTRYPOINT_FAILURE_ERROR.to_string(),\n                ),\n                hal::PipelineError::PipelineConstants(_, error) => {\n                    CreateComputePipelineError::PipelineConstants(error)\n                }\n            })?;\n\n        let dst_buffer_desc = hal::BufferDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation destination buffer\"),\n                instance_flags,\n            ),\n            size: DST_BUFFER_SIZE.get(),\n            usage: wgt::BufferUses::INDIRECT | wgt::BufferUses::STORAGE_READ_WRITE,\n            memory_flags: hal::MemoryFlags::empty(),\n        };\n        let dst_buffer =\n            unsafe { device.create_buffer(&dst_buffer_desc) }.map_err(DeviceError::from_hal)?;\n\n        let dst_bind_group_desc = hal::BindGroupDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation destination bind group\"),\n                instance_flags,\n            ),\n            layout: dst_bind_group_layout.as_ref(),\n            entries: &[hal::BindGroupEntry {\n                binding: 0,\n                resource_index: 0,\n                count: 1,\n            }],\n            // SAFETY: We just created the buffer with this size.\n            buffers: &[hal::BufferBinding::new_unchecked(\n                dst_buffer.as_ref(),\n                0,\n                Some(DST_BUFFER_SIZE),\n            )],\n            samplers: &[],\n            textures: &[],\n            acceleration_structures: &[],\n            external_textures: &[],\n        };\n        let dst_bind_group = unsafe {\n            device\n                .create_bind_group(&dst_bind_group_desc)\n                .map_err(DeviceError::from_hal)\n        }?;\n\n        Ok(Self {\n            module,\n            dst_bind_group_layout,\n            src_bind_group_layout,\n            pipeline_layout,\n            pipeline,\n            dst_buffer,\n            dst_bind_group,\n        })\n    }\n\n    /// `Ok(None)` will only be returned if `buffer_size` is `0`.\n    pub(super) fn create_src_bind_group(\n        &self,\n        device: &dyn hal::DynDevice,\n        limits: &wgt::Limits,\n        buffer_size: u64,\n        buffer: &dyn hal::DynBuffer,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<Option<Box<dyn hal::DynBindGroup>>, DeviceError> {\n        let binding_size = calculate_src_buffer_binding_size(buffer_size, limits);\n        let Some(binding_size) = NonZeroU64::new(binding_size) else {\n            return Ok(None);\n        };\n        let hal_desc = hal::BindGroupDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect dispatch validation source bind group\"),\n                instance_flags,\n            ),\n            layout: self.src_bind_group_layout.as_ref(),\n            entries: &[hal::BindGroupEntry {\n                binding: 0,\n                resource_index: 0,\n                count: 1,\n            }],\n            // SAFETY: We calculated the binding size to fit within the buffer.\n            buffers: &[hal::BufferBinding::new_unchecked(buffer, 0, binding_size)],\n            samplers: &[],\n            textures: &[],\n            acceleration_structures: &[],\n            external_textures: &[],\n        };\n        unsafe {\n            device\n                .create_bind_group(&hal_desc)\n                .map(Some)\n                .map_err(DeviceError::from_hal)\n        }\n    }\n\n    pub fn params<'a>(&'a self, limits: &wgt::Limits, offset: u64, buffer_size: u64) -> Params<'a> {\n        // The offset we receive is only required to be aligned to 4 bytes.\n        //\n        // Binding offsets and dynamic offsets are required to be aligned to\n        // min_storage_buffer_offset_alignment (256 bytes by default).\n        //\n        // So, we work around this limitation by calculating an aligned offset\n        // and pass the remainder through a immediate data.\n        //\n        // We could bind the whole buffer and only have to pass the offset\n        // through a immediate data but we might run into the\n        // max_storage_buffer_binding_size limit.\n        //\n        // See the inner docs of `calculate_src_buffer_binding_size` to\n        // see how we get the appropriate `binding_size`.\n        let alignment = limits.min_storage_buffer_offset_alignment as u64;\n        let binding_size = calculate_src_buffer_binding_size(buffer_size, limits);\n        let aligned_offset = offset - offset % alignment;\n        // This works because `binding_size` is either `buffer_size` or `alignment * 2 + buffer_size % alignment`.\n        let max_aligned_offset = buffer_size - binding_size;\n        let aligned_offset = aligned_offset.min(max_aligned_offset);\n        let offset_remainder = offset - aligned_offset;\n\n        Params {\n            pipeline_layout: self.pipeline_layout.as_ref(),\n            pipeline: self.pipeline.as_ref(),\n            dst_buffer: self.dst_buffer.as_ref(),\n            dst_bind_group: self.dst_bind_group.as_ref(),\n            aligned_offset,\n            offset_remainder,\n        }\n    }\n\n    pub(super) fn dispose(self, device: &dyn hal::DynDevice) {\n        let Dispatch {\n            module,\n            dst_bind_group_layout,\n            src_bind_group_layout,\n            pipeline_layout,\n            pipeline,\n            dst_buffer,\n            dst_bind_group,\n        } = self;\n\n        unsafe {\n            device.destroy_bind_group(dst_bind_group);\n            device.destroy_buffer(dst_buffer);\n            device.destroy_compute_pipeline(pipeline);\n            device.destroy_pipeline_layout(pipeline_layout);\n            device.destroy_bind_group_layout(src_bind_group_layout);\n            device.destroy_bind_group_layout(dst_bind_group_layout);\n            device.destroy_shader_module(module);\n        }\n    }\n}\n\nfn calculate_src_buffer_binding_size(buffer_size: u64, limits: &wgt::Limits) -> u64 {\n    let alignment = limits.min_storage_buffer_offset_alignment as u64;\n\n    // We need to choose a binding size that can address all possible sets of 12 contiguous bytes in the buffer taking\n    // into account that the dynamic offset needs to be a multiple of `min_storage_buffer_offset_alignment`.\n\n    // Given the know variables: `offset`, `buffer_size`, `alignment` and the rule `offset + 12 <= buffer_size`.\n\n    // Let `chunks = floor(buffer_size / alignment)`.\n    // Let `chunk` be the interval `[0, chunks]`.\n    // Let `offset = alignment * chunk + r` where `r` is the interval [0, alignment - 4].\n    // Let `binding` be the interval `[offset, offset + 12]`.\n    // Let `aligned_offset = alignment * chunk`.\n    // Let `aligned_binding` be the interval `[aligned_offset, aligned_offset + r + 12]`.\n    // Let `aligned_binding_size = r + 12 = [12, alignment + 8]`.\n    // Let `min_aligned_binding_size = alignment + 8`.\n\n    // `min_aligned_binding_size` is the minimum binding size required to address all 12 contiguous bytes in the buffer\n    // but the last aligned_offset + min_aligned_binding_size might overflow the buffer. In order to avoid this we must\n    // pick a larger `binding_size` that satisfies: `last_aligned_offset + binding_size = buffer_size` and\n    // `binding_size >= min_aligned_binding_size`.\n\n    // Let `buffer_size = alignment * chunks + sr` where `sr` is the interval [0, alignment - 4].\n    // Let `last_aligned_offset = alignment * (chunks - u)` where `u` is the interval [0, chunks].\n    // => `binding_size = buffer_size - last_aligned_offset`\n    // => `binding_size = alignment * chunks + sr - alignment * (chunks - u)`\n    // => `binding_size = alignment * chunks + sr - alignment * chunks + alignment * u`\n    // => `binding_size = sr + alignment * u`\n    // => `min_aligned_binding_size <= sr + alignment * u`\n    // => `alignment + 8 <= sr + alignment * u`\n    // => `u` must be at least 2\n    // => `binding_size = sr + alignment * 2`\n\n    let binding_size = 2 * alignment + (buffer_size % alignment);\n    binding_size.min(buffer_size)\n}\n"
  },
  {
    "path": "wgpu-core/src/indirect_validation/draw.rs",
    "content": "use super::{\n    utils::{BufferBarrierScratch, BufferBarriers, UniqueIndexExt as _, UniqueIndexScratch},\n    CreateIndirectValidationPipelineError,\n};\nuse crate::{\n    command::RenderPassErrorInner,\n    device::{queue::TempResource, Device, DeviceError},\n    hal_label,\n    lock::{rank, Mutex},\n    pipeline::{CreateComputePipelineError, CreateShaderModuleError},\n    resource::{RawResourceAccess as _, StagingBuffer, Trackable},\n    snatch::SnatchGuard,\n    track::TrackerIndex,\n    FastHashMap,\n};\nuse alloc::{boxed::Box, string::ToString, sync::Arc, vec, vec::Vec};\nuse core::{mem::size_of, num::NonZeroU64};\nuse wgt::Limits;\n\n/// Note: This needs to be under:\n///\n/// default max_compute_workgroups_per_dimension * size_of::<wgt::DrawIndirectArgs>() * `workgroup_size` used by the shader\n///\n/// = (2^16 - 1) * 2^4 * 2^6\n///\n/// It is currently set to:\n///\n/// = (2^16 - 1) * 2^4\n///\n/// This is enough space for:\n///\n/// - 65535 [`wgt::DrawIndirectArgs`] / [`MetadataEntry`]\n/// - 52428 [`wgt::DrawIndexedIndirectArgs`]\nconst BUFFER_SIZE: wgt::BufferSize = wgt::BufferSize::new(1_048_560).unwrap();\n\n/// Holds all device-level resources that are needed to validate indirect draws.\n///\n/// This machinery requires the following limits:\n///\n/// - max_bind_groups: 3,\n/// - max_dynamic_storage_buffers_per_pipeline_layout: 1,\n/// - max_storage_buffers_per_shader_stage: 3,\n/// - max_immediate_size: 8,\n///\n/// These are all indirectly satisfied by `DownlevelFlags::INDIRECT_EXECUTION`, which is also\n/// required for this module's functionality to work.\n#[derive(Debug)]\npub(crate) struct Draw {\n    module: Box<dyn hal::DynShaderModule>,\n    metadata_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    src_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    dst_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    pipeline_layout: Box<dyn hal::DynPipelineLayout>,\n    pipeline: Box<dyn hal::DynComputePipeline>,\n\n    free_indirect_entries: Mutex<Vec<BufferPoolEntry>>,\n    free_metadata_entries: Mutex<Vec<BufferPoolEntry>>,\n}\n\nimpl Draw {\n    pub(super) fn new(\n        device: &dyn hal::DynDevice,\n        required_features: &wgt::Features,\n        instance_flags: wgt::InstanceFlags,\n        backend: wgt::Backend,\n    ) -> Result<Self, CreateIndirectValidationPipelineError> {\n        let module = create_validation_module(device, instance_flags)?;\n\n        let metadata_bind_group_layout = create_bind_group_layout(\n            device,\n            true,\n            false,\n            BUFFER_SIZE,\n            hal_label(\n                Some(\"(wgpu internal) Indirect draw validation metadata bind group layout\"),\n                instance_flags,\n            ),\n        )?;\n        let src_bind_group_layout = create_bind_group_layout(\n            device,\n            true,\n            true,\n            wgt::BufferSize::new(4 * 4).unwrap(),\n            hal_label(\n                Some(\"(wgpu internal) Indirect draw validation source bind group layout\"),\n                instance_flags,\n            ),\n        )?;\n        let dst_bind_group_layout = create_bind_group_layout(\n            device,\n            false,\n            false,\n            BUFFER_SIZE,\n            hal_label(\n                Some(\"(wgpu internal) Indirect draw validation destination bind group layout\"),\n                instance_flags,\n            ),\n        )?;\n\n        let pipeline_layout_desc = hal::PipelineLayoutDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect draw validation pipeline layout\"),\n                instance_flags,\n            ),\n            flags: hal::PipelineLayoutFlags::empty(),\n            bind_group_layouts: &[\n                Some(metadata_bind_group_layout.as_ref()),\n                Some(src_bind_group_layout.as_ref()),\n                Some(dst_bind_group_layout.as_ref()),\n            ],\n            immediate_size: 8,\n        };\n        let pipeline_layout = unsafe {\n            device\n                .create_pipeline_layout(&pipeline_layout_desc)\n                .map_err(DeviceError::from_hal)?\n        };\n\n        let supports_indirect_first_instance =\n            required_features.contains(wgt::Features::INDIRECT_FIRST_INSTANCE);\n        let write_d3d12_special_constants = backend == wgt::Backend::Dx12;\n        let pipeline = create_validation_pipeline(\n            device,\n            module.as_ref(),\n            pipeline_layout.as_ref(),\n            supports_indirect_first_instance,\n            write_d3d12_special_constants,\n            instance_flags,\n        )?;\n\n        Ok(Self {\n            module,\n            metadata_bind_group_layout,\n            src_bind_group_layout,\n            dst_bind_group_layout,\n            pipeline_layout,\n            pipeline,\n\n            free_indirect_entries: Mutex::new(rank::BUFFER_POOL, Vec::new()),\n            free_metadata_entries: Mutex::new(rank::BUFFER_POOL, Vec::new()),\n        })\n    }\n\n    /// `Ok(None)` will only be returned if `buffer_size` is `0`.\n    pub(super) fn create_src_bind_group(\n        &self,\n        device: &dyn hal::DynDevice,\n        limits: &Limits,\n        buffer_size: u64,\n        buffer: &dyn hal::DynBuffer,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<Option<Box<dyn hal::DynBindGroup>>, DeviceError> {\n        let binding_size = calculate_src_buffer_binding_size(buffer_size, limits);\n        let Some(binding_size) = NonZeroU64::new(binding_size) else {\n            return Ok(None);\n        };\n        let hal_desc = hal::BindGroupDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect draw validation source bind group\"),\n                instance_flags,\n            ),\n            layout: self.src_bind_group_layout.as_ref(),\n            entries: &[hal::BindGroupEntry {\n                binding: 0,\n                resource_index: 0,\n                count: 1,\n            }],\n            // SAFETY: We calculated the binding size to fit within the buffer.\n            buffers: &[hal::BufferBinding::new_unchecked(buffer, 0, binding_size)],\n            samplers: &[],\n            textures: &[],\n            acceleration_structures: &[],\n            external_textures: &[],\n        };\n        unsafe {\n            device\n                .create_bind_group(&hal_desc)\n                .map(Some)\n                .map_err(DeviceError::from_hal)\n        }\n    }\n\n    fn acquire_dst_entry(\n        &self,\n        device: &dyn hal::DynDevice,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<BufferPoolEntry, hal::DeviceError> {\n        let mut free_buffers = self.free_indirect_entries.lock();\n        match free_buffers.pop() {\n            Some(buffer) => Ok(buffer),\n            None => {\n                let usage = wgt::BufferUses::INDIRECT | wgt::BufferUses::STORAGE_READ_WRITE;\n                create_buffer_and_bind_group(\n                    device,\n                    usage,\n                    self.dst_bind_group_layout.as_ref(),\n                    hal_label(Some(\"(wgpu internal) Indirect draw validation destination buffer\"), instance_flags),\n                    hal_label(Some(\"(wgpu internal) Indirect draw validation destination bind group layout\"), instance_flags),\n                )\n            }\n        }\n    }\n\n    fn release_dst_entries(&self, entries: impl Iterator<Item = BufferPoolEntry>) {\n        self.free_indirect_entries.lock().extend(entries);\n    }\n\n    fn acquire_metadata_entry(\n        &self,\n        device: &dyn hal::DynDevice,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<BufferPoolEntry, hal::DeviceError> {\n        let mut free_buffers = self.free_metadata_entries.lock();\n        match free_buffers.pop() {\n            Some(buffer) => Ok(buffer),\n            None => {\n                let usage = wgt::BufferUses::COPY_DST | wgt::BufferUses::STORAGE_READ_ONLY;\n                create_buffer_and_bind_group(\n                    device,\n                    usage,\n                    self.metadata_bind_group_layout.as_ref(),\n                    hal_label(\n                        Some(\"(wgpu internal) Indirect draw validation metadata buffer\"),\n                        instance_flags,\n                    ),\n                    hal_label(\n                        Some(\"(wgpu internal) Indirect draw validation metadata bind group layout\"),\n                        instance_flags,\n                    ),\n                )\n            }\n        }\n    }\n\n    fn release_metadata_entries(&self, entries: impl Iterator<Item = BufferPoolEntry>) {\n        self.free_metadata_entries.lock().extend(entries);\n    }\n\n    /// Injects a compute pass that will validate all indirect draws in the current render pass.\n    pub(crate) fn inject_validation_pass(\n        &self,\n        device: &Arc<Device>,\n        snatch_guard: &SnatchGuard,\n        resources: &mut DrawResources,\n        temp_resources: &mut Vec<TempResource>,\n        encoder: &mut dyn hal::DynCommandEncoder,\n        batcher: DrawBatcher,\n    ) -> Result<(), RenderPassErrorInner> {\n        let mut batches = batcher.batches;\n\n        if batches.is_empty() {\n            return Ok(());\n        }\n\n        let max_staging_buffer_size = 1 << 26; // ~67MiB\n\n        let mut staging_buffers = Vec::new();\n\n        let mut current_size = 0;\n        for batch in batches.values_mut() {\n            let data = batch.metadata();\n            let offset = if current_size + data.len() > max_staging_buffer_size {\n                let staging_buffer =\n                    StagingBuffer::new(device, NonZeroU64::new(current_size as u64).unwrap())?;\n                staging_buffers.push(staging_buffer);\n                current_size = data.len();\n                0\n            } else {\n                let offset = current_size;\n                current_size += data.len();\n                offset as u64\n            };\n            batch.staging_buffer_index = staging_buffers.len();\n            batch.staging_buffer_offset = offset;\n        }\n        if current_size != 0 {\n            let staging_buffer =\n                StagingBuffer::new(device, NonZeroU64::new(current_size as u64).unwrap())?;\n            staging_buffers.push(staging_buffer);\n        }\n\n        for batch in batches.values() {\n            let data = batch.metadata();\n            let staging_buffer = &mut staging_buffers[batch.staging_buffer_index];\n            unsafe {\n                staging_buffer.write_with_offset(\n                    data,\n                    0,\n                    batch.staging_buffer_offset as isize,\n                    data.len(),\n                )\n            };\n        }\n\n        let staging_buffers: Vec<_> = staging_buffers\n            .into_iter()\n            .map(|buffer| buffer.flush())\n            .collect();\n\n        let mut current_metadata_entry = None;\n        for batch in batches.values_mut() {\n            let data = batch.metadata();\n            let (metadata_resource_index, metadata_buffer_offset) =\n                resources.get_metadata_subrange(data.len() as u64, &mut current_metadata_entry)?;\n            batch.metadata_resource_index = metadata_resource_index;\n            batch.metadata_buffer_offset = metadata_buffer_offset;\n        }\n\n        let buffer_barrier_scratch = &mut BufferBarrierScratch::new();\n        let unique_index_scratch = &mut UniqueIndexScratch::new();\n\n        BufferBarriers::new(buffer_barrier_scratch)\n            .extend(\n                batches\n                    .values()\n                    .map(|batch| batch.staging_buffer_index)\n                    .unique(unique_index_scratch)\n                    .map(|index| hal::BufferBarrier {\n                        buffer: staging_buffers[index].raw(),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::MAP_WRITE,\n                            to: wgt::BufferUses::COPY_SRC,\n                        },\n                    }),\n            )\n            .extend(\n                batches\n                    .values()\n                    .map(|batch| batch.metadata_resource_index)\n                    .unique(unique_index_scratch)\n                    .map(|index| hal::BufferBarrier {\n                        buffer: resources.get_metadata_buffer(index),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::STORAGE_READ_ONLY,\n                            to: wgt::BufferUses::COPY_DST,\n                        },\n                    }),\n            )\n            .encode(encoder);\n\n        for batch in batches.values() {\n            let data = batch.metadata();\n            let data_size = NonZeroU64::new(data.len() as u64).unwrap();\n\n            let staging_buffer = &staging_buffers[batch.staging_buffer_index];\n\n            let metadata_buffer = resources.get_metadata_buffer(batch.metadata_resource_index);\n\n            unsafe {\n                encoder.copy_buffer_to_buffer(\n                    staging_buffer.raw(),\n                    metadata_buffer,\n                    &[hal::BufferCopy {\n                        src_offset: batch.staging_buffer_offset,\n                        dst_offset: batch.metadata_buffer_offset,\n                        size: data_size,\n                    }],\n                );\n            }\n        }\n\n        for staging_buffer in staging_buffers {\n            temp_resources.push(TempResource::StagingBuffer(staging_buffer));\n        }\n\n        BufferBarriers::new(buffer_barrier_scratch)\n            .extend(\n                batches\n                    .values()\n                    .map(|batch| batch.metadata_resource_index)\n                    .unique(unique_index_scratch)\n                    .map(|index| hal::BufferBarrier {\n                        buffer: resources.get_metadata_buffer(index),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::COPY_DST,\n                            to: wgt::BufferUses::STORAGE_READ_ONLY,\n                        },\n                    }),\n            )\n            .extend(\n                batches\n                    .values()\n                    .map(|batch| batch.dst_resource_index)\n                    .unique(unique_index_scratch)\n                    .map(|index| hal::BufferBarrier {\n                        buffer: resources.get_dst_buffer(index),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::INDIRECT,\n                            to: wgt::BufferUses::STORAGE_READ_WRITE,\n                        },\n                    }),\n            )\n            .encode(encoder);\n\n        let desc = hal::ComputePassDescriptor {\n            label: hal_label(\n                Some(\"(wgpu internal) Indirect draw validation pass\"),\n                device.instance_flags,\n            ),\n            timestamp_writes: None,\n        };\n        unsafe {\n            encoder.begin_compute_pass(&desc);\n        }\n        unsafe {\n            encoder.set_compute_pipeline(self.pipeline.as_ref());\n        }\n\n        for batch in batches.values() {\n            let pipeline_layout = self.pipeline_layout.as_ref();\n\n            let metadata_start =\n                (batch.metadata_buffer_offset / size_of::<MetadataEntry>() as u64) as u32;\n            let metadata_count = batch.entries.len() as u32;\n            unsafe {\n                encoder.set_immediates(pipeline_layout, 0, &[metadata_start, metadata_count]);\n            }\n\n            let metadata_bind_group =\n                resources.get_metadata_bind_group(batch.metadata_resource_index);\n            unsafe {\n                encoder.set_bind_group(pipeline_layout, 0, metadata_bind_group, &[]);\n            }\n\n            // Make sure the indirect buffer is still valid.\n            batch.src_buffer.try_raw(snatch_guard)?;\n\n            let src_bind_group = batch\n                .src_buffer\n                .indirect_validation_bind_groups\n                .get(snatch_guard)\n                .unwrap()\n                .draw\n                .as_ref();\n            unsafe {\n                encoder.set_bind_group(\n                    pipeline_layout,\n                    1,\n                    src_bind_group,\n                    &[batch.src_dynamic_offset as u32],\n                );\n            }\n\n            let dst_bind_group = resources.get_dst_bind_group(batch.dst_resource_index);\n            unsafe {\n                encoder.set_bind_group(pipeline_layout, 2, dst_bind_group, &[]);\n            }\n\n            unsafe {\n                encoder.dispatch([(batch.entries.len() as u32).div_ceil(64), 1, 1]);\n            }\n        }\n\n        unsafe {\n            encoder.end_compute_pass();\n        }\n\n        BufferBarriers::new(buffer_barrier_scratch)\n            .extend(\n                batches\n                    .values()\n                    .map(|batch| batch.dst_resource_index)\n                    .unique(unique_index_scratch)\n                    .map(|index| hal::BufferBarrier {\n                        buffer: resources.get_dst_buffer(index),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::STORAGE_READ_WRITE,\n                            to: wgt::BufferUses::INDIRECT,\n                        },\n                    }),\n            )\n            .encode(encoder);\n\n        Ok(())\n    }\n\n    pub(super) fn dispose(self, device: &dyn hal::DynDevice) {\n        let Draw {\n            module,\n            metadata_bind_group_layout,\n            src_bind_group_layout,\n            dst_bind_group_layout,\n            pipeline_layout,\n            pipeline,\n\n            free_indirect_entries,\n            free_metadata_entries,\n        } = self;\n\n        for entry in free_indirect_entries.into_inner().drain(..) {\n            unsafe {\n                device.destroy_bind_group(entry.bind_group);\n                device.destroy_buffer(entry.buffer);\n            }\n        }\n\n        for entry in free_metadata_entries.into_inner().drain(..) {\n            unsafe {\n                device.destroy_bind_group(entry.bind_group);\n                device.destroy_buffer(entry.buffer);\n            }\n        }\n\n        unsafe {\n            device.destroy_compute_pipeline(pipeline);\n            device.destroy_pipeline_layout(pipeline_layout);\n            device.destroy_bind_group_layout(metadata_bind_group_layout);\n            device.destroy_bind_group_layout(src_bind_group_layout);\n            device.destroy_bind_group_layout(dst_bind_group_layout);\n            device.destroy_shader_module(module);\n        }\n    }\n}\n\nfn create_validation_module(\n    device: &dyn hal::DynDevice,\n    instance_flags: wgt::InstanceFlags,\n) -> Result<Box<dyn hal::DynShaderModule>, CreateIndirectValidationPipelineError> {\n    let src = include_str!(\"./validate_draw.wgsl\");\n\n    #[cfg(feature = \"wgsl\")]\n    let module = naga::front::wgsl::parse_str(src).map_err(|inner| {\n        CreateShaderModuleError::Parsing(naga::error::ShaderError {\n            source: src.to_string(),\n            label: None,\n            inner: Box::new(inner),\n        })\n    })?;\n    #[cfg(not(feature = \"wgsl\"))]\n    #[allow(clippy::diverging_sub_expression)]\n    let module = panic!(\"Indirect validation requires the wgsl feature flag to be enabled!\");\n\n    let info = crate::device::create_validator(\n        wgt::Features::IMMEDIATES,\n        wgt::DownlevelFlags::empty(),\n        naga::valid::ValidationFlags::all(),\n    )\n    .validate(&module)\n    .map_err(|inner| {\n        CreateShaderModuleError::Validation(naga::error::ShaderError {\n            source: src.to_string(),\n            label: None,\n            inner: Box::new(inner),\n        })\n    })?;\n    let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {\n        module: alloc::borrow::Cow::Owned(module),\n        info,\n        debug_source: None,\n    });\n    let hal_desc = hal::ShaderModuleDescriptor {\n        label: hal_label(\n            Some(\"(wgpu internal) Indirect draw validation shader module\"),\n            instance_flags,\n        ),\n        runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),\n    };\n    let module = unsafe { device.create_shader_module(&hal_desc, hal_shader) }.map_err(\n        |error| match error {\n            hal::ShaderError::Device(error) => {\n                CreateShaderModuleError::Device(DeviceError::from_hal(error))\n            }\n            hal::ShaderError::Compilation(ref msg) => {\n                log::error!(\"Shader error: {msg}\");\n                CreateShaderModuleError::Generation\n            }\n        },\n    )?;\n\n    Ok(module)\n}\n\nfn create_validation_pipeline(\n    device: &dyn hal::DynDevice,\n    module: &dyn hal::DynShaderModule,\n    pipeline_layout: &dyn hal::DynPipelineLayout,\n    supports_indirect_first_instance: bool,\n    write_d3d12_special_constants: bool,\n    instance_flags: wgt::InstanceFlags,\n) -> Result<Box<dyn hal::DynComputePipeline>, CreateIndirectValidationPipelineError> {\n    let pipeline_desc = hal::ComputePipelineDescriptor {\n        label: hal_label(\n            Some(\"(wgpu internal) Indirect draw validation pipeline\"),\n            instance_flags,\n        ),\n        layout: pipeline_layout,\n        stage: hal::ProgrammableStage {\n            module,\n            entry_point: \"main\",\n            constants: &hashbrown::HashMap::from([\n                (\n                    \"supports_indirect_first_instance\".to_string(),\n                    f64::from(supports_indirect_first_instance),\n                ),\n                (\n                    \"write_d3d12_special_constants\".to_string(),\n                    f64::from(write_d3d12_special_constants),\n                ),\n            ]),\n            zero_initialize_workgroup_memory: false,\n        },\n        cache: None,\n    };\n    let pipeline =\n        unsafe { device.create_compute_pipeline(&pipeline_desc) }.map_err(|err| match err {\n            hal::PipelineError::Device(error) => {\n                CreateComputePipelineError::Device(DeviceError::from_hal(error))\n            }\n            hal::PipelineError::Linkage(_stages, msg) => CreateComputePipelineError::Internal(msg),\n            hal::PipelineError::EntryPoint(_stage) => CreateComputePipelineError::Internal(\n                crate::device::ENTRYPOINT_FAILURE_ERROR.to_string(),\n            ),\n            hal::PipelineError::PipelineConstants(_, error) => {\n                CreateComputePipelineError::PipelineConstants(error)\n            }\n        })?;\n\n    Ok(pipeline)\n}\n\nfn create_bind_group_layout(\n    device: &dyn hal::DynDevice,\n    read_only: bool,\n    has_dynamic_offset: bool,\n    min_binding_size: wgt::BufferSize,\n    label: Option<&'static str>,\n) -> Result<Box<dyn hal::DynBindGroupLayout>, CreateIndirectValidationPipelineError> {\n    let bind_group_layout_desc = hal::BindGroupLayoutDescriptor {\n        label,\n        flags: hal::BindGroupLayoutFlags::empty(),\n        entries: &[wgt::BindGroupLayoutEntry {\n            binding: 0,\n            visibility: wgt::ShaderStages::COMPUTE,\n            ty: wgt::BindingType::Buffer {\n                ty: wgt::BufferBindingType::Storage { read_only },\n                has_dynamic_offset,\n                min_binding_size: Some(min_binding_size),\n            },\n            count: None,\n        }],\n    };\n    let bind_group_layout = unsafe {\n        device\n            .create_bind_group_layout(&bind_group_layout_desc)\n            .map_err(DeviceError::from_hal)?\n    };\n\n    Ok(bind_group_layout)\n}\n\n/// Returns the largest binding size that when combined with dynamic offsets can address the whole buffer.\nfn calculate_src_buffer_binding_size(buffer_size: u64, limits: &Limits) -> u64 {\n    let max_storage_buffer_binding_size = limits.max_storage_buffer_binding_size;\n    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment as u64;\n\n    if buffer_size <= max_storage_buffer_binding_size {\n        buffer_size\n    } else {\n        let buffer_rem = buffer_size % min_storage_buffer_offset_alignment;\n        let binding_rem = max_storage_buffer_binding_size % min_storage_buffer_offset_alignment;\n\n        // Can the buffer remainder fit in the binding remainder?\n        // If so, align max binding size and add buffer remainder\n        if buffer_rem <= binding_rem {\n            max_storage_buffer_binding_size - binding_rem + buffer_rem\n        }\n        // If not, align max binding size, shorten it by a chunk and add buffer remainder\n        else {\n            max_storage_buffer_binding_size - binding_rem - min_storage_buffer_offset_alignment\n                + buffer_rem\n        }\n    }\n}\n\n/// Splits the given `offset` into a dynamic offset & offset.\nfn calculate_src_offsets(buffer_size: u64, limits: &Limits, offset: u64) -> (u64, u64) {\n    let binding_size = calculate_src_buffer_binding_size(buffer_size, limits);\n\n    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment as u64;\n\n    let chunk_adjustment = match min_storage_buffer_offset_alignment {\n        // No need to adjust since the src_offset is 4 byte aligned.\n        4 => 0,\n        // With 16/20 bytes of data we can straddle up to 2 8 byte boundaries:\n        //  - 16 bytes of data: (4|8|4)\n        //  - 20 bytes of data: (4|8|8, 8|8|4)\n        8 => 2,\n        // With 16/20 bytes of data we can straddle up to 1 16+ byte boundary:\n        //  - 16 bytes of data: (4|12, 8|8, 12|4)\n        //  - 20 bytes of data: (4|16, 8|12, 12|8, 16|4)\n        16.. => 1,\n        _ => unreachable!(),\n    };\n\n    let chunks = binding_size / min_storage_buffer_offset_alignment;\n    let dynamic_offset_stride =\n        chunks.saturating_sub(chunk_adjustment) * min_storage_buffer_offset_alignment;\n\n    if dynamic_offset_stride == 0 {\n        return (0, offset);\n    }\n\n    let max_dynamic_offset = buffer_size - binding_size;\n    let max_dynamic_offset_index = max_dynamic_offset / dynamic_offset_stride;\n\n    let src_dynamic_offset_index = offset / dynamic_offset_stride;\n\n    let src_dynamic_offset =\n        src_dynamic_offset_index.min(max_dynamic_offset_index) * dynamic_offset_stride;\n    let src_offset = offset - src_dynamic_offset;\n\n    (src_dynamic_offset, src_offset)\n}\n\n#[derive(Debug)]\nstruct BufferPoolEntry {\n    buffer: Box<dyn hal::DynBuffer>,\n    bind_group: Box<dyn hal::DynBindGroup>,\n}\n\nfn create_buffer_and_bind_group(\n    device: &dyn hal::DynDevice,\n    usage: wgt::BufferUses,\n    bind_group_layout: &dyn hal::DynBindGroupLayout,\n    buffer_label: Option<&'static str>,\n    bind_group_label: Option<&'static str>,\n) -> Result<BufferPoolEntry, hal::DeviceError> {\n    let buffer_desc = hal::BufferDescriptor {\n        label: buffer_label,\n        size: BUFFER_SIZE.get(),\n        usage,\n        memory_flags: hal::MemoryFlags::empty(),\n    };\n    let buffer = unsafe { device.create_buffer(&buffer_desc) }?;\n    let bind_group_desc = hal::BindGroupDescriptor {\n        label: bind_group_label,\n        layout: bind_group_layout,\n        entries: &[hal::BindGroupEntry {\n            binding: 0,\n            resource_index: 0,\n            count: 1,\n        }],\n        // SAFETY: We just created the buffer with this size.\n        buffers: &[hal::BufferBinding::new_unchecked(\n            buffer.as_ref(),\n            0,\n            BUFFER_SIZE,\n        )],\n        samplers: &[],\n        textures: &[],\n        acceleration_structures: &[],\n        external_textures: &[],\n    };\n    let bind_group = unsafe { device.create_bind_group(&bind_group_desc) }?;\n    Ok(BufferPoolEntry { buffer, bind_group })\n}\n\n#[derive(Clone)]\nstruct CurrentEntry {\n    index: usize,\n    offset: u64,\n}\n\n/// Holds all command buffer-level resources that are needed to validate indirect draws.\npub(crate) struct DrawResources {\n    device: Arc<Device>,\n    dst_entries: Vec<BufferPoolEntry>,\n    metadata_entries: Vec<BufferPoolEntry>,\n}\n\nimpl Drop for DrawResources {\n    fn drop(&mut self) {\n        if let Some(ref indirect_validation) = self.device.indirect_validation {\n            let indirect_draw_validation = &indirect_validation.draw;\n            indirect_draw_validation.release_dst_entries(self.dst_entries.drain(..));\n            indirect_draw_validation.release_metadata_entries(self.metadata_entries.drain(..));\n        }\n    }\n}\n\nimpl DrawResources {\n    pub(crate) fn new(device: Arc<Device>) -> Self {\n        DrawResources {\n            device,\n            dst_entries: Vec::new(),\n            metadata_entries: Vec::new(),\n        }\n    }\n\n    pub(crate) fn get_dst_buffer(&self, index: usize) -> &dyn hal::DynBuffer {\n        self.dst_entries.get(index).unwrap().buffer.as_ref()\n    }\n\n    fn get_dst_bind_group(&self, index: usize) -> &dyn hal::DynBindGroup {\n        self.dst_entries.get(index).unwrap().bind_group.as_ref()\n    }\n\n    fn get_metadata_buffer(&self, index: usize) -> &dyn hal::DynBuffer {\n        self.metadata_entries.get(index).unwrap().buffer.as_ref()\n    }\n\n    fn get_metadata_bind_group(&self, index: usize) -> &dyn hal::DynBindGroup {\n        self.metadata_entries\n            .get(index)\n            .unwrap()\n            .bind_group\n            .as_ref()\n    }\n\n    fn get_dst_subrange(\n        &mut self,\n        size: u64,\n        current_entry: &mut Option<CurrentEntry>,\n    ) -> Result<(usize, u64), DeviceError> {\n        let indirect_draw_validation = &self.device.indirect_validation.as_ref().unwrap().draw;\n        let ensure_entry = |index: usize| {\n            if self.dst_entries.len() <= index {\n                let entry = indirect_draw_validation\n                    .acquire_dst_entry(self.device.raw(), self.device.instance_flags)?;\n                self.dst_entries.push(entry);\n            }\n            Ok(())\n        };\n        let entry_data = Self::get_subrange_impl(ensure_entry, current_entry, size)?;\n        Ok((entry_data.index, entry_data.offset))\n    }\n\n    fn get_metadata_subrange(\n        &mut self,\n        size: u64,\n        current_entry: &mut Option<CurrentEntry>,\n    ) -> Result<(usize, u64), DeviceError> {\n        let indirect_draw_validation = &self.device.indirect_validation.as_ref().unwrap().draw;\n        let ensure_entry = |index: usize| {\n            if self.metadata_entries.len() <= index {\n                let entry = indirect_draw_validation\n                    .acquire_metadata_entry(self.device.raw(), self.device.instance_flags)?;\n                self.metadata_entries.push(entry);\n            }\n            Ok(())\n        };\n        let entry_data = Self::get_subrange_impl(ensure_entry, current_entry, size)?;\n        Ok((entry_data.index, entry_data.offset))\n    }\n\n    fn get_subrange_impl(\n        ensure_entry: impl FnOnce(usize) -> Result<(), hal::DeviceError>,\n        current_entry: &mut Option<CurrentEntry>,\n        size: u64,\n    ) -> Result<CurrentEntry, DeviceError> {\n        let index = if let Some(current_entry) = current_entry.as_mut() {\n            if current_entry.offset + size <= BUFFER_SIZE.get() {\n                let entry_data = current_entry.clone();\n                current_entry.offset += size;\n                return Ok(entry_data);\n            } else {\n                current_entry.index + 1\n            }\n        } else {\n            0\n        };\n\n        ensure_entry(index).map_err(DeviceError::from_hal)?;\n\n        let entry_data = CurrentEntry { index, offset: 0 };\n\n        *current_entry = Some(CurrentEntry {\n            index,\n            offset: size,\n        });\n\n        Ok(entry_data)\n    }\n}\n\n/// This must match the `MetadataEntry` struct used by the shader.\n#[repr(C)]\nstruct MetadataEntry {\n    src_offset: u32,\n    dst_offset: u32,\n    vertex_or_index_limit: u32,\n    instance_limit: u32,\n}\n\nimpl MetadataEntry {\n    fn new(\n        indexed: bool,\n        src_offset: u64,\n        dst_offset: u64,\n        vertex_or_index_limit: u64,\n        instance_limit: u64,\n    ) -> Self {\n        const U32_MAX_AS_U64: u64 = u32::MAX as u64;\n\n        // NOTE: buffer sizes should never exceed `u32::MAX`.\n        assert!(src_offset <= U32_MAX_AS_U64);\n        assert!(dst_offset <= U32_MAX_AS_U64);\n\n        let src_offset = src_offset as u32;\n        let src_offset = src_offset / 4; // translate byte offset to offset in u32's\n\n        // `src_offset` needs at most 30 bits,\n        // pack `indexed` in bit 31 of `src_offset`\n        let src_offset = src_offset | ((indexed as u32) << 31);\n\n        // max value for limits since first_X and X_count indirect draw arguments are u32\n        let max_limit = U32_MAX_AS_U64 + U32_MAX_AS_U64; // 1 11111111 11111111 11111111 11111110\n\n        let vertex_or_index_limit = vertex_or_index_limit.min(max_limit);\n        let vertex_or_index_limit_bit_32 = (vertex_or_index_limit >> 32) as u32; // extract bit 32\n        let vertex_or_index_limit = vertex_or_index_limit as u32; // truncate the limit to a u32\n\n        let instance_limit = instance_limit.min(max_limit);\n        let instance_limit_bit_32 = (instance_limit >> 32) as u32; // extract bit 32\n        let instance_limit = instance_limit as u32; // truncate the limit to a u32\n\n        let dst_offset = dst_offset as u32;\n        let dst_offset = dst_offset / 4; // translate byte offset to offset in u32's\n\n        // `dst_offset` needs at most 30 bits,\n        // pack `vertex_or_index_limit_bit_32` in bit 30 of `dst_offset` and\n        // pack `instance_limit_bit_32` in bit 31 of `dst_offset`\n        let dst_offset =\n            dst_offset | (vertex_or_index_limit_bit_32 << 30) | (instance_limit_bit_32 << 31);\n\n        Self {\n            src_offset,\n            dst_offset,\n            vertex_or_index_limit,\n            instance_limit,\n        }\n    }\n}\n\nstruct DrawIndirectValidationBatch {\n    src_buffer: Arc<crate::resource::Buffer>,\n    src_dynamic_offset: u64,\n    dst_resource_index: usize,\n    entries: Vec<MetadataEntry>,\n\n    staging_buffer_index: usize,\n    staging_buffer_offset: u64,\n    metadata_resource_index: usize,\n    metadata_buffer_offset: u64,\n}\n\nimpl DrawIndirectValidationBatch {\n    /// Data to be written to the metadata buffer.\n    fn metadata(&self) -> &[u8] {\n        unsafe {\n            core::slice::from_raw_parts(\n                self.entries.as_ptr().cast::<u8>(),\n                self.entries.len() * size_of::<MetadataEntry>(),\n            )\n        }\n    }\n}\n\n/// Accumulates all needed data needed to validate indirect draws.\npub(crate) struct DrawBatcher {\n    batches: FastHashMap<(TrackerIndex, u64, usize), DrawIndirectValidationBatch>,\n    current_dst_entry: Option<CurrentEntry>,\n}\n\nimpl DrawBatcher {\n    pub(crate) fn new() -> Self {\n        Self {\n            batches: FastHashMap::default(),\n            current_dst_entry: None,\n        }\n    }\n\n    /// Add an indirect draw to be validated.\n    ///\n    /// Returns the index of the indirect buffer in `indirect_draw_validation_resources`\n    /// and the offset to be used for the draw.\n    pub(crate) fn add<'a>(\n        &mut self,\n        indirect_draw_validation_resources: &'a mut DrawResources,\n        device: &Device,\n        src_buffer: &Arc<crate::resource::Buffer>,\n        offset: u64,\n        family: crate::command::DrawCommandFamily,\n        vertex_or_index_limit: u64,\n        instance_limit: u64,\n    ) -> Result<(usize, u64), DeviceError> {\n        // space for D3D12 special constants\n        let extra = if device.backend() == wgt::Backend::Dx12 {\n            3 * size_of::<u32>() as u64\n        } else {\n            0\n        };\n        let stride = extra + crate::command::get_stride_of_indirect_args(family);\n\n        let (dst_resource_index, dst_offset) = indirect_draw_validation_resources\n            .get_dst_subrange(stride, &mut self.current_dst_entry)?;\n\n        let buffer_size = src_buffer.size;\n        let limits = device.adapter.limits();\n        let (src_dynamic_offset, src_offset) = calculate_src_offsets(buffer_size, &limits, offset);\n\n        let src_buffer_tracker_index = src_buffer.tracker_index();\n\n        let entry = MetadataEntry::new(\n            family == crate::command::DrawCommandFamily::DrawIndexed,\n            src_offset,\n            dst_offset,\n            vertex_or_index_limit,\n            instance_limit,\n        );\n\n        match self.batches.entry((\n            src_buffer_tracker_index,\n            src_dynamic_offset,\n            dst_resource_index,\n        )) {\n            hashbrown::hash_map::Entry::Occupied(mut occupied_entry) => {\n                occupied_entry.get_mut().entries.push(entry)\n            }\n            hashbrown::hash_map::Entry::Vacant(vacant_entry) => {\n                vacant_entry.insert(DrawIndirectValidationBatch {\n                    src_buffer: src_buffer.clone(),\n                    src_dynamic_offset,\n                    dst_resource_index,\n                    entries: vec![entry],\n\n                    // these will be initialized once we accumulated all entries for the batch\n                    staging_buffer_index: 0,\n                    staging_buffer_offset: 0,\n                    metadata_resource_index: 0,\n                    metadata_buffer_offset: 0,\n                });\n            }\n        }\n\n        Ok((dst_resource_index, dst_offset))\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/indirect_validation/mod.rs",
    "content": "use crate::{\n    device::DeviceError,\n    pipeline::{CreateComputePipelineError, CreateShaderModuleError},\n};\nuse alloc::boxed::Box;\nuse thiserror::Error;\n\nmod dispatch;\nmod draw;\nmod utils;\n\npub(crate) use dispatch::Dispatch;\npub(crate) use draw::{Draw, DrawBatcher, DrawResources};\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\nenum CreateIndirectValidationPipelineError {\n    #[error(transparent)]\n    DeviceError(#[from] DeviceError),\n    #[error(transparent)]\n    ShaderModule(#[from] CreateShaderModuleError),\n    #[error(transparent)]\n    ComputePipeline(#[from] CreateComputePipelineError),\n}\n\npub(crate) struct IndirectValidation {\n    pub(crate) dispatch: Dispatch,\n    pub(crate) draw: Draw,\n}\n\nimpl IndirectValidation {\n    pub(crate) fn new(\n        device: &dyn hal::DynDevice,\n        required_limits: &wgt::Limits,\n        required_features: &wgt::Features,\n        instance_flags: wgt::InstanceFlags,\n        backend: wgt::Backend,\n    ) -> Result<Self, DeviceError> {\n        let dispatch = match Dispatch::new(device, instance_flags, required_limits) {\n            Ok(dispatch) => dispatch,\n            Err(e) => {\n                log::error!(\"indirect-validation error: {e:?}\");\n                return Err(DeviceError::Lost);\n            }\n        };\n        let draw = match Draw::new(device, required_features, instance_flags, backend) {\n            Ok(draw) => draw,\n            Err(e) => {\n                log::error!(\"indirect-draw-validation error: {e:?}\");\n                return Err(DeviceError::Lost);\n            }\n        };\n        Ok(Self { dispatch, draw })\n    }\n\n    pub(crate) fn dispose(self, device: &dyn hal::DynDevice) {\n        let Self { dispatch, draw } = self;\n\n        dispatch.dispose(device);\n        draw.dispose(device);\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct BindGroups {\n    pub(crate) dispatch: Box<dyn hal::DynBindGroup>,\n    draw: Box<dyn hal::DynBindGroup>,\n}\n\nimpl BindGroups {\n    /// `Ok(None)` will only be returned if `buffer_size` is `0`.\n    pub(crate) fn new(\n        indirect_validation: &IndirectValidation,\n        device: &crate::device::Device,\n        buffer_size: u64,\n        buffer: &dyn hal::DynBuffer,\n    ) -> Result<Option<Self>, DeviceError> {\n        let dispatch = indirect_validation.dispatch.create_src_bind_group(\n            device.raw(),\n            &device.limits,\n            buffer_size,\n            buffer,\n            device.instance_flags,\n        )?;\n        let draw = indirect_validation.draw.create_src_bind_group(\n            device.raw(),\n            &device.adapter.limits(),\n            buffer_size,\n            buffer,\n            device.instance_flags,\n        )?;\n\n        match (dispatch, draw) {\n            (None, None) => Ok(None),\n            (None, Some(_)) => unreachable!(),\n            (Some(_), None) => unreachable!(),\n            (Some(dispatch), Some(draw)) => Ok(Some(Self { dispatch, draw })),\n        }\n    }\n\n    pub(crate) fn dispose(self, device: &dyn hal::DynDevice) {\n        let Self { dispatch, draw } = self;\n\n        unsafe {\n            device.destroy_bind_group(dispatch);\n            device.destroy_bind_group(draw);\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/indirect_validation/utils.rs",
    "content": "use alloc::vec::Vec;\n\npub(crate) struct UniqueIndexScratch(bit_set::BitSet);\n\nimpl UniqueIndexScratch {\n    pub(crate) fn new() -> Self {\n        Self(bit_set::BitSet::new())\n    }\n}\n\npub(crate) struct UniqueIndex<'a, I: Iterator<Item = usize>> {\n    inner: I,\n    scratch: &'a mut UniqueIndexScratch,\n}\n\nimpl<'a, I: Iterator<Item = usize>> UniqueIndex<'a, I> {\n    fn new(inner: I, scratch: &'a mut UniqueIndexScratch) -> Self {\n        scratch.0.make_empty();\n        Self { inner, scratch }\n    }\n}\n\nimpl<'a, I: Iterator<Item = usize>> Iterator for UniqueIndex<'a, I> {\n    type Item = usize;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.find(|&i| self.scratch.0.insert(i))\n    }\n}\n\npub(crate) trait UniqueIndexExt: Iterator<Item = usize> {\n    fn unique<'a>(self, scratch: &'a mut UniqueIndexScratch) -> UniqueIndex<'a, Self>\n    where\n        Self: Sized,\n    {\n        UniqueIndex::new(self, scratch)\n    }\n}\n\nimpl<T: Iterator<Item = usize>> UniqueIndexExt for T {}\n\ntype BufferBarrier<'b> = hal::BufferBarrier<'b, dyn hal::DynBuffer>;\n\npub(crate) struct BufferBarrierScratch<'b>(Vec<BufferBarrier<'b>>);\n\nimpl<'b> BufferBarrierScratch<'b> {\n    pub(crate) fn new() -> Self {\n        Self(Vec::new())\n    }\n}\n\npub(crate) struct BufferBarriers<'a, 'b> {\n    scratch: &'a mut BufferBarrierScratch<'b>,\n}\n\nimpl<'a, 'b> BufferBarriers<'a, 'b> {\n    pub(crate) fn new(scratch: &'a mut BufferBarrierScratch<'_>) -> Self {\n        // change lifetime of buffer reference, this is safe since `scratch` is empty,\n        // it was either just created or it has been cleared on `BufferBarriers::drop`\n        let scratch = unsafe {\n            core::mem::transmute::<&'a mut BufferBarrierScratch<'_>, &'a mut BufferBarrierScratch<'b>>(\n                scratch,\n            )\n        };\n        Self { scratch }\n    }\n\n    pub(crate) fn extend(self, iter: impl Iterator<Item = BufferBarrier<'b>>) -> Self {\n        self.scratch.0.extend(iter);\n        self\n    }\n\n    pub(crate) fn encode(self, encoder: &mut dyn hal::DynCommandEncoder) {\n        unsafe {\n            encoder.transition_buffers(&self.scratch.0);\n        }\n    }\n}\n\nimpl<'a, 'b> Drop for BufferBarriers<'a, 'b> {\n    fn drop(&mut self) {\n        self.scratch.0.clear();\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/indirect_validation/validate_draw.wgsl",
    "content": "override supports_indirect_first_instance: bool;\noverride write_d3d12_special_constants: bool;\n\nstruct MetadataEntry {\n    // bits 0..30 are an offset into `src`\n    // bit 31 signifies that we are validating an indexed draw\n    src_offset: u32,\n    // bits 0..30 are an offset into `dst`\n    // bit 30 is the most significant bit of `vertex_or_index_limit`\n    // bit 31 is the most significant bit of `instance_limit`\n    dst_offset: u32,\n    vertex_or_index_limit: u32,\n    instance_limit: u32,\n}\n\nstruct MetadataRange {\n    start: u32,\n    count: u32,\n}\nvar<immediate> metadata_range: MetadataRange;\n\n@group(0) @binding(0)\nvar<storage, read> metadata: array<MetadataEntry>;\n@group(1) @binding(0)\nvar<storage, read> src: array<u32>;\n@group(2) @binding(0)\nvar<storage, read_write> dst: array<u32>;\n\nfn is_bit_set(data: u32, index: u32) -> bool {\n    return ((data >> index) & 1u) == 1u;\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) global_invocation_id: vec3u) {\n    if global_invocation_id.x >= metadata_range.count { return; }\n\n    let metadata = metadata[metadata_range.start + global_invocation_id.x];\n    var failed = false;\n\n    let is_indexed = is_bit_set(metadata.src_offset, 31);\n    let src_base_offset = ((metadata.src_offset << 2) >> 2);\n    let dst_base_offset = ((metadata.dst_offset << 2) >> 2);\n\n    let first_vertex_or_index = src[src_base_offset + 2];\n    let vertex_or_index_count = src[src_base_offset + 0];\n\n    {\n        let can_overflow = is_bit_set(metadata.dst_offset, 30);\n        let sub_overflows = metadata.vertex_or_index_limit < first_vertex_or_index;\n        failed |= sub_overflows && !can_overflow;\n        let vertex_or_index_limit = metadata.vertex_or_index_limit - first_vertex_or_index;\n        failed |= vertex_or_index_limit < vertex_or_index_count;\n    }\n\n    let first_instance = src[src_base_offset + 3 + u32(is_indexed)];\n    let instance_count = src[src_base_offset + 1];\n\n    {\n        let can_overflow = is_bit_set(metadata.dst_offset, 31);\n        let sub_overflows = metadata.instance_limit < first_instance;\n        failed |= sub_overflows && !can_overflow;\n        let instance_limit = metadata.instance_limit - first_instance;\n        failed |= instance_limit < instance_count;\n    }\n\n    if !supports_indirect_first_instance {\n        failed |= first_instance != 0u;\n    }\n\n    let dst_offset = select(0u, 3u, write_d3d12_special_constants);\n    if failed {\n        if write_d3d12_special_constants {\n            dst[dst_base_offset + 0] = 0u;\n            dst[dst_base_offset + 1] = 0u;\n            dst[dst_base_offset + 2] = 0u;\n        }\n        dst[dst_base_offset + dst_offset + 0] = 0u;\n        dst[dst_base_offset + dst_offset + 1] = 0u;\n        dst[dst_base_offset + dst_offset + 2] = 0u;\n        dst[dst_base_offset + dst_offset + 3] = 0u;\n        if (is_indexed) {\n            dst[dst_base_offset + dst_offset + 4] = 0u;\n        }\n    } else {\n        if write_d3d12_special_constants {\n            dst[dst_base_offset + 0] = src[src_base_offset + 2 + u32(is_indexed)];\n            dst[dst_base_offset + 1] = src[src_base_offset + 3 + u32(is_indexed)];\n            dst[dst_base_offset + 2] = 0u;\n        }\n        dst[dst_base_offset + dst_offset + 0] = src[src_base_offset + 0];\n        dst[dst_base_offset + dst_offset + 1] = src[src_base_offset + 1];\n        dst[dst_base_offset + dst_offset + 2] = src[src_base_offset + 2];\n        dst[dst_base_offset + dst_offset + 3] = src[src_base_offset + 3];\n        if (is_indexed) {\n            dst[dst_base_offset + dst_offset + 4] = src[src_base_offset + 4];\n        }\n    }\n}"
  },
  {
    "path": "wgpu-core/src/init_tracker/buffer.rs",
    "content": "use super::{InitTracker, MemoryInitKind};\nuse crate::resource::Buffer;\nuse alloc::sync::Arc;\nuse core::ops::Range;\n\n#[derive(Debug, Clone)]\npub(crate) struct BufferInitTrackerAction {\n    pub buffer: Arc<Buffer>,\n    pub range: Range<wgt::BufferAddress>,\n    pub kind: MemoryInitKind,\n}\n\npub(crate) type BufferInitTracker = InitTracker<wgt::BufferAddress>;\n\nimpl BufferInitTracker {\n    /// Checks if an action has/requires any effect on the initialization status\n    /// and shrinks its range if possible.\n    pub(crate) fn check_action(\n        &self,\n        action: &BufferInitTrackerAction,\n    ) -> Option<BufferInitTrackerAction> {\n        self.create_action(&action.buffer, action.range.clone(), action.kind)\n    }\n\n    /// Creates an action if it would have any effect on the initialization\n    /// status and shrinks the range if possible.\n    pub(crate) fn create_action(\n        &self,\n        buffer: &Arc<Buffer>,\n        query_range: Range<wgt::BufferAddress>,\n        kind: MemoryInitKind,\n    ) -> Option<BufferInitTrackerAction> {\n        self.check(query_range)\n            .map(|range| BufferInitTrackerAction {\n                buffer: buffer.clone(),\n                range,\n                kind,\n            })\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/init_tracker/mod.rs",
    "content": "/*! Lazy initialization of texture and buffer memory.\n\nThe WebGPU specification requires all texture & buffer memory to be\nzero initialized on first read. To avoid unnecessary inits, we track\nthe initialization status of every resource and perform inits lazily.\n\nThe granularity is different for buffers and textures:\n\n- Buffer: Byte granularity to support usecases with large, partially\n  bound buffers well.\n\n- Texture: Mip-level per layer. That is, a 2D surface is either\n  completely initialized or not, subrects are not tracked.\n\nEvery use of a buffer/texture generates a InitTrackerAction which are\nrecorded and later resolved at queue submit by merging them with the\ncurrent state and each other in execution order.\n\nIt is important to note that from the point of view of the memory init\nsystem there are two kind of writes:\n\n- **Full writes**: Any kind of memcpy operation. These cause a\n  `MemoryInitKind.ImplicitlyInitialized` action.\n\n- **(Potentially) partial writes**: For example, write use in a\n  Shader. The system is not able to determine if a resource is fully\n  initialized afterwards but is no longer allowed to perform any\n  clears, therefore this leads to a\n  `MemoryInitKind.NeedsInitializedMemory` action, exactly like a read\n  would.\n\n */\n\nuse core::{fmt, iter, ops::Range};\n\nuse smallvec::SmallVec;\n\nmod buffer;\nmod texture;\n\npub(crate) use buffer::{BufferInitTracker, BufferInitTrackerAction};\npub(crate) use texture::{\n    has_copy_partial_init_tracker_coverage, TextureInitRange, TextureInitTracker,\n    TextureInitTrackerAction,\n};\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum MemoryInitKind {\n    // The memory range is going to be written by an already initialized source,\n    // thus doesn't need extra attention other than marking as initialized.\n    ImplicitlyInitialized,\n    // The memory range is going to be read, therefore needs to ensure prior\n    // initialization.\n    NeedsInitializedMemory,\n}\n\n// Most of the time a resource is either fully uninitialized (one element) or\n// initialized (zero elements).\ntype UninitializedRangeVec<Idx> = SmallVec<[Range<Idx>; 1]>;\n\n/// Tracks initialization status of a linear range from 0..size\n#[derive(Debug, Clone)]\npub(crate) struct InitTracker<Idx: Ord + Copy + Default> {\n    /// Non-overlapping list of all uninitialized ranges, sorted by\n    /// range end.\n    uninitialized_ranges: UninitializedRangeVec<Idx>,\n}\n\npub(crate) struct UninitializedIter<'a, Idx: fmt::Debug + Ord + Copy> {\n    uninitialized_ranges: &'a UninitializedRangeVec<Idx>,\n    drain_range: Range<Idx>,\n    next_index: usize,\n}\n\nimpl<'a, Idx> Iterator for UninitializedIter<'a, Idx>\nwhere\n    Idx: fmt::Debug + Ord + Copy,\n{\n    type Item = Range<Idx>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.uninitialized_ranges\n            .get(self.next_index)\n            .and_then(|range| {\n                if range.start < self.drain_range.end {\n                    self.next_index += 1;\n                    Some(\n                        range.start.max(self.drain_range.start)\n                            ..range.end.min(self.drain_range.end),\n                    )\n                } else {\n                    None\n                }\n            })\n    }\n}\n\npub(crate) struct InitTrackerDrain<'a, Idx: fmt::Debug + Ord + Copy> {\n    uninitialized_ranges: &'a mut UninitializedRangeVec<Idx>,\n    drain_range: Range<Idx>,\n    first_index: usize,\n    next_index: usize,\n}\n\nimpl<'a, Idx> Iterator for InitTrackerDrain<'a, Idx>\nwhere\n    Idx: fmt::Debug + Ord + Copy,\n{\n    type Item = Range<Idx>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if let Some(r) = self\n            .uninitialized_ranges\n            .get(self.next_index)\n            .and_then(|range| {\n                if range.start < self.drain_range.end {\n                    Some(range.clone())\n                } else {\n                    None\n                }\n            })\n        {\n            self.next_index += 1;\n            Some(r.start.max(self.drain_range.start)..r.end.min(self.drain_range.end))\n        } else {\n            let num_affected = self.next_index - self.first_index;\n            if num_affected == 0 {\n                return None;\n            }\n            let first_range = &mut self.uninitialized_ranges[self.first_index];\n\n            // Split one \"big\" uninitialized range?\n            if num_affected == 1\n                && first_range.start < self.drain_range.start\n                && first_range.end > self.drain_range.end\n            {\n                let old_start = first_range.start;\n                first_range.start = self.drain_range.end;\n                self.uninitialized_ranges\n                    .insert(self.first_index, old_start..self.drain_range.start);\n            }\n            // Adjust border ranges and delete everything in-between.\n            else {\n                let remove_start = if first_range.start >= self.drain_range.start {\n                    self.first_index\n                } else {\n                    first_range.end = self.drain_range.start;\n                    self.first_index + 1\n                };\n\n                let last_range = &mut self.uninitialized_ranges[self.next_index - 1];\n                let remove_end = if last_range.end <= self.drain_range.end {\n                    self.next_index\n                } else {\n                    last_range.start = self.drain_range.end;\n                    self.next_index - 1\n                };\n\n                self.uninitialized_ranges.drain(remove_start..remove_end);\n            }\n\n            None\n        }\n    }\n}\n\nimpl<'a, Idx> Drop for InitTrackerDrain<'a, Idx>\nwhere\n    Idx: fmt::Debug + Ord + Copy,\n{\n    fn drop(&mut self) {\n        if self.next_index <= self.first_index {\n            for _ in self {}\n        }\n    }\n}\n\nimpl<Idx> InitTracker<Idx>\nwhere\n    Idx: fmt::Debug + Ord + Copy + Default,\n{\n    pub(crate) fn new(size: Idx) -> Self {\n        Self {\n            uninitialized_ranges: iter::once(Idx::default()..size).collect(),\n        }\n    }\n\n    /// Checks for uninitialized ranges within a given query range.\n    ///\n    /// If `query_range` includes any uninitialized portions of this init\n    /// tracker's resource, return the smallest subrange of `query_range` that\n    /// covers all uninitialized regions.\n    ///\n    /// The returned range may be larger than necessary, to keep this function\n    /// O(log n).\n    pub(crate) fn check(&self, query_range: Range<Idx>) -> Option<Range<Idx>> {\n        let index = self\n            .uninitialized_ranges\n            .partition_point(|r| r.end <= query_range.start);\n        self.uninitialized_ranges\n            .get(index)\n            .and_then(|start_range| {\n                if start_range.start < query_range.end {\n                    let start = start_range.start.max(query_range.start);\n                    match self.uninitialized_ranges.get(index + 1) {\n                        Some(next_range) => {\n                            if next_range.start < query_range.end {\n                                // Would need to keep iterating for more\n                                // accurate upper bound. Don't do that here.\n                                Some(start..query_range.end)\n                            } else {\n                                Some(start..start_range.end.min(query_range.end))\n                            }\n                        }\n                        None => Some(start..start_range.end.min(query_range.end)),\n                    }\n                } else {\n                    None\n                }\n            })\n    }\n\n    // Returns an iterator over the uninitialized ranges in a query range.\n    pub(crate) fn uninitialized(&mut self, drain_range: Range<Idx>) -> UninitializedIter<'_, Idx> {\n        let index = self\n            .uninitialized_ranges\n            .partition_point(|r| r.end <= drain_range.start);\n        UninitializedIter {\n            drain_range,\n            uninitialized_ranges: &self.uninitialized_ranges,\n            next_index: index,\n        }\n    }\n\n    // Drains uninitialized ranges in a query range.\n    pub(crate) fn drain(&mut self, drain_range: Range<Idx>) -> InitTrackerDrain<'_, Idx> {\n        let index = self\n            .uninitialized_ranges\n            .partition_point(|r| r.end <= drain_range.start);\n        InitTrackerDrain {\n            drain_range,\n            uninitialized_ranges: &mut self.uninitialized_ranges,\n            first_index: index,\n            next_index: index,\n        }\n    }\n}\n\nimpl InitTracker<u32> {\n    // Makes a single entry uninitialized if not already uninitialized\n    pub(crate) fn discard(&mut self, pos: u32) {\n        // first range where end>=idx\n        let r_idx = self.uninitialized_ranges.partition_point(|r| r.end < pos);\n        if let Some(r) = self.uninitialized_ranges.get(r_idx) {\n            // Extend range at end\n            if r.end == pos {\n                // merge with next?\n                if let Some(right) = self.uninitialized_ranges.get(r_idx + 1) {\n                    if right.start == pos + 1 {\n                        self.uninitialized_ranges[r_idx] = r.start..right.end;\n                        self.uninitialized_ranges.remove(r_idx + 1);\n                        return;\n                    }\n                }\n                self.uninitialized_ranges[r_idx] = r.start..(pos + 1);\n            } else if r.start > pos {\n                // may still extend range at beginning\n                if r.start == pos + 1 {\n                    self.uninitialized_ranges[r_idx] = pos..r.end;\n                } else {\n                    // previous range end must be smaller than idx, therefore no merge possible\n                    self.uninitialized_ranges.push(pos..(pos + 1));\n                }\n            }\n        } else {\n            self.uninitialized_ranges.push(pos..(pos + 1));\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use alloc::{vec, vec::Vec};\n    use core::ops::Range;\n\n    type Tracker = super::InitTracker<u32>;\n\n    #[test]\n    fn check_for_newly_created_tracker() {\n        let tracker = Tracker::new(10);\n        assert_eq!(tracker.check(0..10), Some(0..10));\n        assert_eq!(tracker.check(0..3), Some(0..3));\n        assert_eq!(tracker.check(3..4), Some(3..4));\n        assert_eq!(tracker.check(4..10), Some(4..10));\n    }\n\n    #[test]\n    fn check_for_drained_tracker() {\n        let mut tracker = Tracker::new(10);\n        tracker.drain(0..10);\n        assert_eq!(tracker.check(0..10), None);\n        assert_eq!(tracker.check(0..3), None);\n        assert_eq!(tracker.check(3..4), None);\n        assert_eq!(tracker.check(4..10), None);\n    }\n\n    #[test]\n    fn check_for_partially_filled_tracker() {\n        let mut tracker = Tracker::new(25);\n        // Two regions of uninitialized memory\n        tracker.drain(0..5);\n        tracker.drain(10..15);\n        tracker.drain(20..25);\n\n        assert_eq!(tracker.check(0..25), Some(5..25)); // entire range\n\n        assert_eq!(tracker.check(0..5), None); // left non-overlapping\n        assert_eq!(tracker.check(3..8), Some(5..8)); // left overlapping region\n        assert_eq!(tracker.check(3..17), Some(5..17)); // left overlapping region + contained region\n\n        // right overlapping region + contained region (yes, doesn't fix range end!)\n        assert_eq!(tracker.check(8..22), Some(8..22));\n        // right overlapping region\n        assert_eq!(tracker.check(17..22), Some(17..20));\n        // right non-overlapping\n        assert_eq!(tracker.check(20..25), None);\n    }\n\n    #[test]\n    fn drain_already_drained() {\n        let mut tracker = Tracker::new(30);\n        tracker.drain(10..20);\n\n        // Overlapping with non-cleared\n        tracker.drain(5..15); // Left overlap\n        tracker.drain(15..25); // Right overlap\n        tracker.drain(0..30); // Inner overlap\n\n        // Clear fully cleared\n        tracker.drain(0..30);\n\n        assert_eq!(tracker.check(0..30), None);\n    }\n\n    #[test]\n    fn drain_never_returns_ranges_twice_for_same_range() {\n        let mut tracker = Tracker::new(19);\n        assert_eq!(tracker.drain(0..19).count(), 1);\n        assert_eq!(tracker.drain(0..19).count(), 0);\n\n        let mut tracker = Tracker::new(17);\n        assert_eq!(tracker.drain(5..8).count(), 1);\n        assert_eq!(tracker.drain(5..8).count(), 0);\n        assert_eq!(tracker.drain(1..3).count(), 1);\n        assert_eq!(tracker.drain(1..3).count(), 0);\n        assert_eq!(tracker.drain(7..13).count(), 1);\n        assert_eq!(tracker.drain(7..13).count(), 0);\n    }\n\n    #[test]\n    fn drain_splits_ranges_correctly() {\n        let mut tracker = Tracker::new(1337);\n        assert_eq!(\n            tracker.drain(21..42).collect::<Vec<Range<u32>>>(),\n            vec![21..42]\n        );\n        assert_eq!(\n            tracker.drain(900..1000).collect::<Vec<Range<u32>>>(),\n            vec![900..1000]\n        );\n\n        // Split ranges.\n        assert_eq!(\n            tracker.drain(5..1003).collect::<Vec<Range<u32>>>(),\n            vec![5..21, 42..900, 1000..1003]\n        );\n        assert_eq!(\n            tracker.drain(0..1337).collect::<Vec<Range<u32>>>(),\n            vec![0..5, 1003..1337]\n        );\n    }\n\n    #[test]\n    fn discard_adds_range_on_cleared() {\n        let mut tracker = Tracker::new(10);\n        tracker.drain(0..10);\n        tracker.discard(0);\n        tracker.discard(5);\n        tracker.discard(9);\n        assert_eq!(tracker.check(0..1), Some(0..1));\n        assert_eq!(tracker.check(1..5), None);\n        assert_eq!(tracker.check(5..6), Some(5..6));\n        assert_eq!(tracker.check(6..9), None);\n        assert_eq!(tracker.check(9..10), Some(9..10));\n    }\n\n    #[test]\n    fn discard_does_nothing_on_uncleared() {\n        let mut tracker = Tracker::new(10);\n        tracker.discard(0);\n        tracker.discard(5);\n        tracker.discard(9);\n        assert_eq!(tracker.uninitialized_ranges.len(), 1);\n        assert_eq!(tracker.uninitialized_ranges[0], 0..10);\n    }\n\n    #[test]\n    fn discard_extends_ranges() {\n        let mut tracker = Tracker::new(10);\n        tracker.drain(3..7);\n        tracker.discard(2);\n        tracker.discard(7);\n        assert_eq!(tracker.uninitialized_ranges.len(), 2);\n        assert_eq!(tracker.uninitialized_ranges[0], 0..3);\n        assert_eq!(tracker.uninitialized_ranges[1], 7..10);\n    }\n\n    #[test]\n    fn discard_merges_ranges() {\n        let mut tracker = Tracker::new(10);\n        tracker.drain(3..4);\n        tracker.discard(3);\n        assert_eq!(tracker.uninitialized_ranges.len(), 1);\n        assert_eq!(tracker.uninitialized_ranges[0], 0..10);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/init_tracker/texture.rs",
    "content": "use super::{InitTracker, MemoryInitKind};\nuse crate::resource::Texture;\nuse alloc::{sync::Arc, vec::Vec};\nuse arrayvec::ArrayVec;\nuse core::ops::Range;\nuse wgt::TextureSelector;\n\n#[derive(Debug, Clone)]\npub(crate) struct TextureInitRange {\n    pub(crate) mip_range: Range<u32>,\n    // Strictly array layers. We do *not* track volume slices separately.\n    pub(crate) layer_range: Range<u32>,\n}\n\n// Returns true if a copy operation doesn't fully cover the texture init\n// tracking granularity. I.e. if this function returns true for a pending copy\n// operation, the target texture needs to be ensured to be initialized first!\npub(crate) fn has_copy_partial_init_tracker_coverage(\n    copy_size: &wgt::Extent3d,\n    mip_level: u32,\n    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n) -> bool {\n    let target_size = desc.mip_level_size(mip_level).unwrap();\n    copy_size.width != target_size.width\n        || copy_size.height != target_size.height\n        || (desc.dimension == wgt::TextureDimension::D3\n            && copy_size.depth_or_array_layers != target_size.depth_or_array_layers)\n}\n\nimpl From<TextureSelector> for TextureInitRange {\n    fn from(selector: TextureSelector) -> Self {\n        TextureInitRange {\n            mip_range: selector.mips,\n            layer_range: selector.layers,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct TextureInitTrackerAction {\n    pub(crate) texture: Arc<Texture>,\n    pub(crate) range: TextureInitRange,\n    pub(crate) kind: MemoryInitKind,\n}\n\npub(crate) type TextureLayerInitTracker = InitTracker<u32>;\n\n#[derive(Debug)]\npub(crate) struct TextureInitTracker {\n    pub mips: ArrayVec<TextureLayerInitTracker, { hal::MAX_MIP_LEVELS as usize }>,\n}\n\nimpl TextureInitTracker {\n    pub(crate) fn new(mip_level_count: u32, depth_or_array_layers: u32) -> Self {\n        TextureInitTracker {\n            mips: core::iter::repeat_n(\n                TextureLayerInitTracker::new(depth_or_array_layers),\n                mip_level_count as usize,\n            )\n            .collect(),\n        }\n    }\n\n    pub(crate) fn check_action(\n        &self,\n        action: &TextureInitTrackerAction,\n    ) -> Option<TextureInitTrackerAction> {\n        let mut mip_range_start = usize::MAX;\n        let mut mip_range_end = usize::MIN;\n        let mut layer_range_start = u32::MAX;\n        let mut layer_range_end = u32::MIN;\n\n        for (i, mip_tracker) in self\n            .mips\n            .iter()\n            .enumerate()\n            .take(action.range.mip_range.end as usize)\n            .skip(action.range.mip_range.start as usize)\n        {\n            if let Some(uninitialized_layer_range) =\n                mip_tracker.check(action.range.layer_range.clone())\n            {\n                mip_range_start = mip_range_start.min(i);\n                mip_range_end = i + 1;\n                layer_range_start = layer_range_start.min(uninitialized_layer_range.start);\n                layer_range_end = layer_range_end.max(uninitialized_layer_range.end);\n            };\n        }\n\n        if mip_range_start < mip_range_end && layer_range_start < layer_range_end {\n            Some(TextureInitTrackerAction {\n                texture: action.texture.clone(),\n                range: TextureInitRange {\n                    mip_range: mip_range_start as u32..mip_range_end as u32,\n                    layer_range: layer_range_start..layer_range_end,\n                },\n                kind: action.kind,\n            })\n        } else {\n            None\n        }\n    }\n\n    pub(crate) fn discard(&mut self, mip_level: u32, layer: u32) {\n        self.mips[mip_level as usize].discard(layer);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/instance.rs",
    "content": "use alloc::{\n    borrow::{Cow, ToOwned as _},\n    boxed::Box,\n    string::String,\n    sync::Arc,\n    vec,\n    vec::Vec,\n};\n\nuse hashbrown::HashMap;\nuse thiserror::Error;\nuse wgt::error::{ErrorType, WebGpuError};\n\nuse crate::{\n    api_log, api_log_debug,\n    device::{queue::Queue, resource::Device, DeviceDescriptor, DeviceError},\n    global::Global,\n    id::{markers, AdapterId, DeviceId, QueueId, SurfaceId},\n    lock::{rank, Mutex},\n    present::Presentation,\n    resource::ResourceType,\n    resource_log,\n    timestamp_normalization::TimestampNormalizerInitError,\n    DOWNLEVEL_WARNING_MESSAGE,\n};\n\nuse wgt::{Backend, Backends, PowerPreference};\n\npub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[error(\"Limit '{name}' value {requested} is better than allowed {allowed}\")]\npub struct FailedLimit {\n    name: Cow<'static, str>,\n    requested: u64,\n    allowed: u64,\n}\n\nimpl WebGpuError for FailedLimit {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\nfn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {\n    let mut failed = Vec::new();\n\n    requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {\n        failed.push(FailedLimit {\n            name: Cow::Borrowed(name),\n            requested,\n            allowed,\n        })\n    });\n\n    failed\n}\n\n#[test]\nfn downlevel_default_limits_less_than_default_limits() {\n    let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());\n    assert!(\n        res.is_empty(),\n        \"Downlevel limits are greater than default limits\",\n    )\n}\n\n#[derive(Default)]\npub struct Instance {\n    _name: String,\n\n    /// List of instances per `wgpu-hal` backend.\n    ///\n    /// The ordering in this list implies prioritization and needs to be preserved.\n    instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,\n\n    /// The backends that were requested by the user.\n    requested_backends: Backends,\n\n    /// The backends that we could have attempted to obtain from `wgpu-hal` —\n    /// those for which support is compiled in, currently.\n    ///\n    /// The union of this and `requested_backends` is the set of backends that would be used,\n    /// independent of whether accessing the drivers/hardware for them succeeds.\n    /// To obtain the set of backends actually in use by this instance, check\n    /// `instance_per_backend` instead.\n    supported_backends: Backends,\n\n    pub flags: wgt::InstanceFlags,\n\n    /// Non-lifetimed [`raw_window_handle::DisplayHandle`], for keepalive and validation purposes in\n    /// [`Self::create_surface()`].\n    ///\n    /// When used with `winit`, callers are expected to pass its `OwnedDisplayHandle` (created from\n    /// the `EventLoop`) here.\n    display: Option<Box<dyn wgt::WgpuHasDisplayHandle>>,\n}\n\nimpl Instance {\n    pub fn new(\n        name: &str,\n        mut instance_desc: wgt::InstanceDescriptor,\n        telemetry: Option<hal::Telemetry>,\n    ) -> Self {\n        let mut this = Self {\n            _name: name.to_owned(),\n            instance_per_backend: Vec::new(),\n            requested_backends: instance_desc.backends,\n            supported_backends: Backends::empty(),\n            flags: instance_desc.flags,\n            // HACK: We must take ownership of the field here, without being able to pass it into\n            // try_add_hal(). Remove it from the mutable descriptor instead, while try_add_hal()\n            // borrows the handle from `this.display` instead.\n            display: instance_desc.display.take(),\n        };\n\n        #[cfg(all(vulkan, not(target_os = \"netbsd\")))]\n        this.try_add_hal(hal::api::Vulkan, &instance_desc, telemetry);\n        #[cfg(metal)]\n        this.try_add_hal(hal::api::Metal, &instance_desc, telemetry);\n        #[cfg(dx12)]\n        this.try_add_hal(hal::api::Dx12, &instance_desc, telemetry);\n        #[cfg(gles)]\n        this.try_add_hal(hal::api::Gles, &instance_desc, telemetry);\n        #[cfg(feature = \"noop\")]\n        this.try_add_hal(hal::api::Noop, &instance_desc, telemetry);\n\n        this\n    }\n\n    /// Helper for `Instance::new()`; attempts to add a single `wgpu-hal` backend to this instance.\n    fn try_add_hal<A: hal::Api>(\n        &mut self,\n        _: A,\n        instance_desc: &wgt::InstanceDescriptor,\n        telemetry: Option<hal::Telemetry>,\n    ) {\n        // Whether or not the backend was requested, and whether or not it succeeds,\n        // note that we *could* try it.\n        self.supported_backends |= A::VARIANT.into();\n\n        if !instance_desc.backends.contains(A::VARIANT.into()) {\n            log::trace!(\"Instance::new: backend {:?} not requested\", A::VARIANT);\n            return;\n        }\n\n        // If this was Some, it was moved into self\n        assert!(instance_desc.display.is_none());\n\n        let hal_desc = hal::InstanceDescriptor {\n            name: \"wgpu\",\n            flags: self.flags,\n            memory_budget_thresholds: instance_desc.memory_budget_thresholds,\n            backend_options: instance_desc.backend_options.clone(),\n            telemetry,\n            // Pass a borrow, the core instance here keeps the owned handle alive already\n            // WARNING: Using self here, not instance_desc!\n            display: self.display.as_ref().map(|hdh| {\n                hdh.display_handle()\n                    .expect(\"Implementation did not provide a DisplayHandle\")\n            }),\n        };\n\n        use hal::Instance as _;\n        // SAFETY: ???\n        match unsafe { A::Instance::init(&hal_desc) } {\n            Ok(instance) => {\n                log::debug!(\"Instance::new: created {:?} backend\", A::VARIANT);\n                self.instance_per_backend\n                    .push((A::VARIANT, Box::new(instance)));\n            }\n            Err(err) => {\n                log::debug!(\n                    \"Instance::new: failed to create {:?} backend: {:?}\",\n                    A::VARIANT,\n                    err\n                );\n            }\n        }\n    }\n\n    pub(crate) fn from_hal_instance<A: hal::Api>(\n        name: String,\n        hal_instance: <A as hal::Api>::Instance,\n    ) -> Self {\n        Self {\n            _name: name,\n            instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],\n            requested_backends: A::VARIANT.into(),\n            supported_backends: A::VARIANT.into(),\n            flags: wgt::InstanceFlags::default(),\n            display: None, // TODO: Extract display from HAL instance if available?\n        }\n    }\n\n    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynInstance> {\n        self.instance_per_backend\n            .iter()\n            .find_map(|(instance_backend, instance)| {\n                (*instance_backend == backend).then(|| instance.as_ref())\n            })\n    }\n\n    /// # Safety\n    ///\n    /// - The raw instance handle returned must not be manually destroyed.\n    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {\n        self.raw(A::VARIANT).map(|instance| {\n            instance\n                .as_any()\n                .downcast_ref()\n                // This should be impossible. It would mean that backend instance and enum type are mismatching.\n                .expect(\"Stored instance is not of the correct type\")\n        })\n    }\n\n    /// Creates a new surface targeting the given display/window handles.\n    ///\n    /// Internally attempts to create hal surfaces for all enabled backends.\n    ///\n    /// Fails only if creation for surfaces for all enabled backends fails in which case\n    /// the error for each enabled backend is listed.\n    /// Vice versa, if creation for any backend succeeds, success is returned.\n    /// Surface creation errors are logged to the debug log in any case.\n    ///\n    /// # Safety\n    ///\n    /// - `display_handle` must be a valid object to create a surface upon,\n    ///   falls back to the instance display handle otherwise.\n    /// - `window_handle` must remain valid as long as the returned\n    ///   [`SurfaceId`] is being used.\n    pub unsafe fn create_surface(\n        &self,\n        display_handle: Option<raw_window_handle::RawDisplayHandle>,\n        window_handle: raw_window_handle::RawWindowHandle,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::create_surface\");\n\n        let instance_display_handle = self.display.as_ref().map(|d| {\n            d.display_handle()\n                .expect(\"Implementation did not provide a DisplayHandle\")\n                .as_raw()\n        });\n        let display_handle = match (instance_display_handle, display_handle) {\n            (Some(a), Some(b)) => {\n                if a != b {\n                    return Err(CreateSurfaceError::MismatchingDisplayHandle);\n                }\n                a\n            }\n            (Some(hnd), None) => hnd,\n            (None, Some(hnd)) => hnd,\n            (None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),\n        };\n\n        let mut errors = HashMap::default();\n        let mut surface_per_backend = HashMap::default();\n\n        for (backend, instance) in &self.instance_per_backend {\n            match unsafe {\n                instance\n                    .as_ref()\n                    .create_surface(display_handle, window_handle)\n            } {\n                Ok(raw) => {\n                    surface_per_backend.insert(*backend, raw);\n                }\n                Err(err) => {\n                    log::debug!(\n                        \"Instance::create_surface: failed to create surface for {backend:?}: {err:?}\"\n                    );\n                    errors.insert(*backend, err);\n                }\n            }\n        }\n\n        if surface_per_backend.is_empty() {\n            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(\n                errors,\n            ))\n        } else {\n            let surface = Surface {\n                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),\n                surface_per_backend,\n            };\n\n            Ok(surface)\n        }\n    }\n\n    /// Creates a new surface from the given drm configuration.\n    ///\n    /// # Safety\n    ///\n    /// - All parameters must point to valid DRM values.\n    ///\n    /// # Platform Support\n    ///\n    /// This function is only available on non-apple Unix-like platforms (Linux, FreeBSD) and\n    /// currently only works with the Vulkan backend.\n    #[cfg(all(\n        unix,\n        not(target_vendor = \"apple\"),\n        not(target_family = \"wasm\"),\n        not(target_os = \"netbsd\")\n    ))]\n    #[cfg_attr(not(vulkan), expect(unused_variables))]\n    pub unsafe fn create_surface_from_drm(\n        &self,\n        fd: i32,\n        plane: u32,\n        connector_id: u32,\n        width: u32,\n        height: u32,\n        refresh_rate: u32,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::create_surface_from_drm\");\n\n        let mut errors = HashMap::default();\n        let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =\n            HashMap::default();\n\n        #[cfg(all(vulkan, not(target_os = \"netbsd\")))]\n        {\n            let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }\n                .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;\n\n            // Safety must be upheld by the caller\n            match unsafe {\n                instance.create_surface_from_drm(\n                    fd,\n                    plane,\n                    connector_id,\n                    width,\n                    height,\n                    refresh_rate,\n                )\n            } {\n                Ok(surface) => {\n                    surface_per_backend.insert(Backend::Vulkan, Box::new(surface));\n                }\n                Err(err) => {\n                    errors.insert(Backend::Vulkan, err);\n                }\n            }\n        }\n\n        if surface_per_backend.is_empty() {\n            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(\n                errors,\n            ))\n        } else {\n            let surface = Surface {\n                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),\n                surface_per_backend,\n            };\n\n            Ok(surface)\n        }\n    }\n\n    /// # Safety\n    ///\n    /// `layer` must be a valid pointer.\n    #[cfg(metal)]\n    pub unsafe fn create_surface_metal(\n        &self,\n        layer: *mut core::ffi::c_void,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::create_surface_metal\");\n\n        let instance = unsafe { self.as_hal::<hal::api::Metal>() }\n            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;\n\n        let layer = layer.cast();\n        // SAFETY: We do this cast and deref. (rather than using `metal` to get the\n        // object we want) to avoid direct coupling on the `metal` crate.\n        //\n        // To wit, this pointer…\n        //\n        // - …is properly aligned.\n        // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal`\n        //   field.\n        // - …points to an _initialized_ `MetalLayerRef`.\n        // - …is only ever aliased via an immutable reference that lives within this\n        //   lexical scope.\n        let layer = unsafe { &*layer };\n        let raw_surface: Box<dyn hal::DynSurface> =\n            Box::new(instance.create_surface_from_layer(layer));\n\n        let surface = Surface {\n            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),\n            surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(),\n        };\n\n        Ok(surface)\n    }\n\n    #[cfg(dx12)]\n    fn create_surface_dx12(\n        &self,\n        create_surface_func: impl FnOnce(&hal::dx12::Instance) -> hal::dx12::Surface,\n    ) -> Result<Surface, CreateSurfaceError> {\n        let instance = unsafe { self.as_hal::<hal::api::Dx12>() }\n            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?;\n        let surface: Box<dyn hal::DynSurface> = Box::new(create_surface_func(instance));\n\n        let surface = Surface {\n            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),\n            surface_per_backend: core::iter::once((Backend::Dx12, surface)).collect(),\n        };\n\n        Ok(surface)\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The visual must be valid and able to be used to make a swapchain with.\n    pub unsafe fn create_surface_from_visual(\n        &self,\n        visual: *mut core::ffi::c_void,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::instance_create_surface_from_visual\");\n        self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The surface_handle must be valid and able to be used to make a swapchain with.\n    pub unsafe fn create_surface_from_surface_handle(\n        &self,\n        surface_handle: *mut core::ffi::c_void,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::instance_create_surface_from_surface_handle\");\n        self.create_surface_dx12(|inst| unsafe {\n            inst.create_surface_from_surface_handle(surface_handle)\n        })\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.\n    pub unsafe fn create_surface_from_swap_chain_panel(\n        &self,\n        swap_chain_panel: *mut core::ffi::c_void,\n    ) -> Result<Surface, CreateSurfaceError> {\n        profiling::scope!(\"Instance::instance_create_surface_from_swap_chain_panel\");\n        self.create_surface_dx12(|inst| unsafe {\n            inst.create_surface_from_swap_chain_panel(swap_chain_panel)\n        })\n    }\n\n    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {\n        profiling::scope!(\"Instance::enumerate_adapters\");\n        api_log!(\"Instance::enumerate_adapters\");\n\n        let mut adapters = Vec::new();\n        for (_backend, instance) in self\n            .instance_per_backend\n            .iter()\n            .filter(|(backend, _)| backends.contains(Backends::from(*backend)))\n        {\n            // NOTE: We might be using `profiling` without any features. The empty backend of this\n            // macro emits no code, so unused code linting changes depending on the backend.\n            profiling::scope!(\"enumerating\", &*alloc::format!(\"{_backend:?}\"));\n\n            let hal_adapters = unsafe { instance.enumerate_adapters(None) };\n            for raw in hal_adapters {\n                let adapter = Adapter::new(raw);\n                api_log_debug!(\"Adapter {:?}\", adapter.raw.info);\n                adapters.push(adapter);\n            }\n        }\n        adapters\n    }\n\n    pub fn request_adapter(\n        &self,\n        desc: &wgt::RequestAdapterOptions<&Surface>,\n        backends: Backends,\n    ) -> Result<Adapter, wgt::RequestAdapterError> {\n        profiling::scope!(\"Instance::request_adapter\");\n        api_log!(\"Instance::request_adapter\");\n\n        let mut adapters = Vec::new();\n        let mut incompatible_surface_backends = Backends::empty();\n        let mut no_fallback_backends = Backends::empty();\n        let mut no_adapter_backends = Backends::empty();\n\n        for &(backend, ref instance) in self\n            .instance_per_backend\n            .iter()\n            .filter(|&&(backend, _)| backends.contains(Backends::from(backend)))\n        {\n            let compatible_hal_surface = desc\n                .compatible_surface\n                .and_then(|surface| surface.raw(backend));\n\n            let mut backend_adapters =\n                unsafe { instance.enumerate_adapters(compatible_hal_surface) };\n            if backend_adapters.is_empty() {\n                log::debug!(\"enabled backend `{backend:?}` has no adapters\");\n                no_adapter_backends |= Backends::from(backend);\n                // by continuing, we avoid setting the further error bits below\n                continue;\n            }\n\n            if desc.force_fallback_adapter {\n                log::debug!(\"Filtering `{backend:?}` for `force_fallback_adapter`\");\n                backend_adapters.retain(|exposed| {\n                    let keep = exposed.info.device_type == wgt::DeviceType::Cpu;\n                    if !keep {\n                        log::debug!(\"* Eliminating adapter `{}`\", exposed.info.name);\n                    }\n                    keep\n                });\n                if backend_adapters.is_empty() {\n                    log::debug!(\"* Backend `{backend:?}` has no fallback adapters\");\n                    no_fallback_backends |= Backends::from(backend);\n                    continue;\n                }\n            }\n\n            if let Some(surface) = desc.compatible_surface {\n                backend_adapters.retain(|exposed| {\n                    let capabilities = surface.get_capabilities_with_raw(exposed);\n                    if let Err(err) = capabilities {\n                        log::debug!(\n                            \"Adapter {:?} not compatible with surface: {}\",\n                            exposed.info,\n                            err\n                        );\n                        incompatible_surface_backends |= Backends::from(backend);\n                        false\n                    } else {\n                        true\n                    }\n                });\n                if backend_adapters.is_empty() {\n                    incompatible_surface_backends |= Backends::from(backend);\n                    continue;\n                }\n            }\n            adapters.extend(backend_adapters);\n        }\n\n        match desc.power_preference {\n            PowerPreference::LowPower => {\n                sort(&mut adapters, true);\n            }\n            PowerPreference::HighPerformance => {\n                sort(&mut adapters, false);\n            }\n            PowerPreference::None => {}\n        };\n\n        fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {\n            adapters\n                .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));\n        }\n\n        fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {\n            // Since devices of type \"Other\" might really be \"Unknown\" and come\n            // from APIs like OpenGL that don't specify device type, Prefer more\n            // Specific types over Other.\n            //\n            // This means that backends which do provide accurate device types\n            // will be preferred if their device type indicates an actual\n            // hardware GPU (integrated or discrete).\n            match device_type {\n                wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,\n                wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,\n                wgt::DeviceType::DiscreteGpu => 1,\n                wgt::DeviceType::IntegratedGpu => 2,\n                wgt::DeviceType::Other => 3,\n                wgt::DeviceType::VirtualGpu => 4,\n                wgt::DeviceType::Cpu => 5,\n            }\n        }\n\n        // `request_adapter` can be a bit of a black box.\n        // Shine some light on its decision in debug log.\n        if adapters.is_empty() {\n            log::debug!(\"Request adapter didn't find compatible adapters.\");\n        } else {\n            log::debug!(\n                \"Found {} compatible adapters. Sorted by preference:\",\n                adapters.len()\n            );\n            for adapter in &adapters {\n                log::debug!(\"* {:?}\", adapter.info);\n            }\n        }\n\n        if let Some(adapter) = adapters.into_iter().next() {\n            api_log_debug!(\"Request adapter result {:?}\", adapter.info);\n            let adapter = Adapter::new(adapter);\n            Ok(adapter)\n        } else {\n            Err(wgt::RequestAdapterError::NotFound {\n                supported_backends: self.supported_backends,\n                requested_backends: self.requested_backends,\n                active_backends: self.active_backends(),\n                no_fallback_backends,\n                no_adapter_backends,\n                incompatible_surface_backends,\n            })\n        }\n    }\n\n    fn active_backends(&self) -> Backends {\n        self.instance_per_backend\n            .iter()\n            .map(|&(backend, _)| Backends::from(backend))\n            .collect()\n    }\n}\n\npub struct Surface {\n    pub(crate) presentation: Mutex<Option<Presentation>>,\n    pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,\n}\n\nimpl ResourceType for Surface {\n    const TYPE: &'static str = \"Surface\";\n}\nimpl crate::storage::StorageItem for Surface {\n    type Marker = markers::Surface;\n}\n\nimpl Surface {\n    pub fn get_capabilities(\n        &self,\n        adapter: &Adapter,\n    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {\n        self.get_capabilities_with_raw(&adapter.raw)\n    }\n\n    pub fn get_capabilities_with_raw(\n        &self,\n        adapter: &hal::DynExposedAdapter,\n    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {\n        let backend = adapter.backend();\n        let suf = self\n            .raw(backend)\n            .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;\n        profiling::scope!(\"surface_capabilities\");\n        let caps = unsafe { adapter.adapter.surface_capabilities(suf) }\n            .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;\n        Ok(caps)\n    }\n\n    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {\n        self.surface_per_backend\n            .get(&backend)\n            .map(|surface| surface.as_ref())\n    }\n}\n\nimpl Drop for Surface {\n    fn drop(&mut self) {\n        if let Some(present) = self.presentation.lock().take() {\n            for (&backend, surface) in &self.surface_per_backend {\n                if backend == present.device.backend() {\n                    unsafe { surface.unconfigure(present.device.raw()) };\n                }\n            }\n        }\n    }\n}\n\npub struct Adapter {\n    pub(crate) raw: hal::DynExposedAdapter,\n}\n\nimpl Adapter {\n    pub fn new(mut raw: hal::DynExposedAdapter) -> Self {\n        // WebGPU requires this offset alignment as lower bound on all adapters.\n        const MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND: u32 = 32;\n\n        let limits = &mut raw.capabilities.limits;\n\n        limits.min_uniform_buffer_offset_alignment = limits\n            .min_uniform_buffer_offset_alignment\n            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);\n        limits.min_storage_buffer_offset_alignment = limits\n            .min_storage_buffer_offset_alignment\n            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);\n\n        Self { raw }\n    }\n\n    /// Returns the backend this adapter is using.\n    pub fn backend(&self) -> Backend {\n        self.raw.backend()\n    }\n\n    pub fn is_surface_supported(&self, surface: &Surface) -> bool {\n        // If get_capabilities returns Err, then the API does not advertise support for the surface.\n        //\n        // This could occur if the user is running their app on Wayland but Vulkan does not support\n        // VK_KHR_wayland_surface.\n        surface.get_capabilities(self).is_ok()\n    }\n\n    pub fn get_info(&self) -> wgt::AdapterInfo {\n        self.raw.info.clone()\n    }\n\n    pub fn features(&self) -> wgt::Features {\n        self.raw.features\n    }\n\n    pub fn limits(&self) -> wgt::Limits {\n        self.raw.capabilities.limits.clone()\n    }\n\n    pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {\n        self.raw.capabilities.downlevel.clone()\n    }\n\n    pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {\n        unsafe { self.raw.adapter.get_presentation_timestamp() }\n    }\n\n    pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {\n        self.raw.capabilities.cooperative_matrix_properties.clone()\n    }\n\n    pub fn get_texture_format_features(\n        &self,\n        format: wgt::TextureFormat,\n    ) -> wgt::TextureFormatFeatures {\n        use hal::TextureFormatCapabilities as Tfc;\n\n        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };\n        let mut allowed_usages = wgt::TextureUsages::empty();\n\n        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));\n        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));\n        allowed_usages.set(\n            wgt::TextureUsages::TEXTURE_BINDING,\n            caps.contains(Tfc::SAMPLED),\n        );\n        allowed_usages.set(\n            wgt::TextureUsages::STORAGE_BINDING,\n            caps.intersects(\n                Tfc::STORAGE_WRITE_ONLY\n                    | Tfc::STORAGE_READ_ONLY\n                    | Tfc::STORAGE_READ_WRITE\n                    | Tfc::STORAGE_ATOMIC,\n            ),\n        );\n        allowed_usages.set(\n            wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT,\n            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),\n        );\n        allowed_usages.set(\n            wgt::TextureUsages::STORAGE_ATOMIC,\n            caps.contains(Tfc::STORAGE_ATOMIC),\n        );\n\n        let mut flags = wgt::TextureFormatFeatureFlags::empty();\n        flags.set(\n            wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,\n            caps.contains(Tfc::STORAGE_READ_ONLY),\n        );\n        flags.set(\n            wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,\n            caps.contains(Tfc::STORAGE_WRITE_ONLY),\n        );\n        flags.set(\n            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,\n            caps.contains(Tfc::STORAGE_READ_WRITE),\n        );\n\n        flags.set(\n            wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,\n            caps.contains(Tfc::STORAGE_ATOMIC),\n        );\n\n        flags.set(\n            wgt::TextureFormatFeatureFlags::FILTERABLE,\n            caps.contains(Tfc::SAMPLED_LINEAR),\n        );\n\n        flags.set(\n            wgt::TextureFormatFeatureFlags::BLENDABLE,\n            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),\n        );\n\n        flags.set(\n            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,\n            caps.contains(Tfc::MULTISAMPLE_X2),\n        );\n        flags.set(\n            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,\n            caps.contains(Tfc::MULTISAMPLE_X4),\n        );\n        flags.set(\n            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,\n            caps.contains(Tfc::MULTISAMPLE_X8),\n        );\n        flags.set(\n            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,\n            caps.contains(Tfc::MULTISAMPLE_X16),\n        );\n\n        flags.set(\n            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,\n            caps.contains(Tfc::MULTISAMPLE_RESOLVE),\n        );\n\n        wgt::TextureFormatFeatures {\n            allowed_usages,\n            flags,\n        }\n    }\n\n    fn create_device_and_queue_from_hal(\n        self: &Arc<Self>,\n        hal_device: hal::DynOpenDevice,\n        desc: &DeviceDescriptor,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {\n        api_log!(\"Adapter::create_device\");\n\n        let device = Device::new(hal_device.device, self, desc, instance_flags)?;\n        let device = Arc::new(device);\n\n        let queue = Queue::new(device.clone(), hal_device.queue, instance_flags)?;\n        let queue = Arc::new(queue);\n\n        device.set_queue(&queue);\n        device.late_init_resources_with_queue()?;\n\n        Ok((device, queue))\n    }\n\n    pub fn create_device_and_queue(\n        self: &Arc<Self>,\n        desc: &DeviceDescriptor,\n        instance_flags: wgt::InstanceFlags,\n    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {\n        // Verify all features were exposed by the adapter\n        if !self.raw.features.contains(desc.required_features) {\n            return Err(RequestDeviceError::UnsupportedFeature(\n                desc.required_features - self.raw.features,\n            ));\n        }\n\n        // Check if experimental features are permitted to be enabled.\n        if desc\n            .required_features\n            .intersects(wgt::Features::all_experimental_mask())\n            && !desc.experimental_features.is_enabled()\n        {\n            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(\n                desc.required_features\n                    .intersection(wgt::Features::all_experimental_mask()),\n            ));\n        }\n\n        let caps = &self.raw.capabilities;\n        if Backends::PRIMARY.contains(Backends::from(self.backend()))\n            && !caps.downlevel.is_webgpu_compliant()\n        {\n            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;\n            log::warn!(\"Missing downlevel flags: {missing_flags:?}\\n{DOWNLEVEL_WARNING_MESSAGE}\");\n            log::warn!(\"{:#?}\", caps.downlevel);\n        }\n\n        // Verify feature preconditions\n        if desc\n            .required_features\n            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)\n            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu\n        {\n            log::warn!(\n                \"Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \\\n                        This is a massive performance footgun and likely not what you wanted\"\n            );\n        }\n\n        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {\n            return Err(RequestDeviceError::LimitsExceeded(failed));\n        }\n\n        let open = unsafe {\n            self.raw.adapter.open(\n                desc.required_features,\n                &desc.required_limits,\n                &desc.memory_hints,\n            )\n        }\n        .map_err(DeviceError::from_hal)?;\n\n        self.create_device_and_queue_from_hal(open, desc, instance_flags)\n    }\n}\n\ncrate::impl_resource_type!(Adapter);\ncrate::impl_storage_item!(Adapter);\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum GetSurfaceSupportError {\n    #[error(\"Surface is not supported for the specified backend {0}\")]\n    NotSupportedByBackend(Backend),\n    #[error(\"Failed to retrieve surface capabilities for the specified adapter.\")]\n    FailedToRetrieveSurfaceCapabilitiesForAdapter,\n}\n\n#[derive(Clone, Debug, Error)]\n/// Error when requesting a device from the adapter\n#[non_exhaustive]\npub enum RequestDeviceError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    LimitsExceeded(#[from] FailedLimit),\n    #[error(\"Failed to initialize Timestamp Normalizer\")]\n    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),\n    #[error(\"Unsupported features were requested: {0}\")]\n    UnsupportedFeature(wgt::Features),\n    #[error(\n        \"Some experimental features, {0}, were requested, but experimental features are not enabled\"\n    )]\n    ExperimentalFeaturesNotEnabled(wgt::Features),\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateSurfaceError {\n    #[error(\"The backend {0} was not enabled on the instance.\")]\n    BackendNotEnabled(Backend),\n    #[error(\"Failed to create surface for any enabled backend: {0:?}\")]\n    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),\n    #[error(\"The display handle used to create this Instance does not match the one used to create a surface on it\")]\n    MismatchingDisplayHandle,\n    #[error(\n        \"No `DisplayHandle` is available to create this surface with.  When creating a surface with `create_surface()` \\\n        you must specify a display handle in `InstanceDescriptor::display`.  \\\n        Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \\\n        you must use `create_surface_unsafe()`.\"\n    )]\n    MissingDisplayHandle,\n}\n\nimpl Global {\n    /// Creates a new surface targeting the given display/window handles.\n    ///\n    /// Internally attempts to create hal surfaces for all enabled backends.\n    ///\n    /// Fails only if creation for surfaces for all enabled backends fails in which case\n    /// the error for each enabled backend is listed.\n    /// Vice versa, if creation for any backend succeeds, success is returned.\n    /// Surface creation errors are logged to the debug log in any case.\n    ///\n    /// id_in:\n    /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.\n    ///\n    /// # Safety\n    ///\n    /// - `display_handle` must be a valid object to create a surface upon,\n    ///   falls back to the instance display handle otherwise.\n    /// - `window_handle` must remain valid as long as the returned\n    ///   [`SurfaceId`] is being used.\n    pub unsafe fn instance_create_surface(\n        &self,\n        display_handle: Option<raw_window_handle::RawDisplayHandle>,\n        window_handle: raw_window_handle::RawWindowHandle,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n        Ok(id)\n    }\n\n    /// Creates a new surface from the given drm configuration.\n    ///\n    /// # Safety\n    ///\n    /// - All parameters must point to valid DRM values.\n    ///\n    /// # Platform Support\n    ///\n    /// This function is only available on non-apple Unix-like platforms (Linux, FreeBSD) and\n    /// currently only works with the Vulkan backend.\n    #[cfg(all(\n        unix,\n        not(target_vendor = \"apple\"),\n        not(target_family = \"wasm\"),\n        not(target_os = \"netbsd\")\n    ))]\n    pub unsafe fn instance_create_surface_from_drm(\n        &self,\n        fd: i32,\n        plane: u32,\n        connector_id: u32,\n        width: u32,\n        height: u32,\n        refresh_rate: u32,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe {\n            self.instance.create_surface_from_drm(\n                fd,\n                plane,\n                connector_id,\n                width,\n                height,\n                refresh_rate,\n            )\n        }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n\n        Ok(id)\n    }\n\n    /// # Safety\n    ///\n    /// `layer` must be a valid pointer.\n    #[cfg(metal)]\n    pub unsafe fn instance_create_surface_metal(\n        &self,\n        layer: *mut core::ffi::c_void,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe { self.instance.create_surface_metal(layer) }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n        Ok(id)\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The visual must be valid and able to be used to make a swapchain with.\n    pub unsafe fn instance_create_surface_from_visual(\n        &self,\n        visual: *mut core::ffi::c_void,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe { self.instance.create_surface_from_visual(visual) }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n        Ok(id)\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The surface_handle must be valid and able to be used to make a swapchain with.\n    pub unsafe fn instance_create_surface_from_surface_handle(\n        &self,\n        surface_handle: *mut core::ffi::c_void,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe {\n            self.instance\n                .create_surface_from_surface_handle(surface_handle)\n        }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n        Ok(id)\n    }\n\n    #[cfg(dx12)]\n    /// # Safety\n    ///\n    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.\n    pub unsafe fn instance_create_surface_from_swap_chain_panel(\n        &self,\n        swap_chain_panel: *mut core::ffi::c_void,\n        id_in: Option<SurfaceId>,\n    ) -> Result<SurfaceId, CreateSurfaceError> {\n        let surface = unsafe {\n            self.instance\n                .create_surface_from_swap_chain_panel(swap_chain_panel)\n        }?;\n        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));\n        Ok(id)\n    }\n\n    pub fn surface_drop(&self, id: SurfaceId) {\n        profiling::scope!(\"Surface::drop\");\n\n        api_log!(\"Surface::drop {id:?}\");\n\n        self.surfaces.remove(id);\n    }\n\n    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<AdapterId> {\n        let adapters = self.instance.enumerate_adapters(backends);\n        adapters\n            .into_iter()\n            .map(|adapter| self.hub.adapters.prepare(None).assign(Arc::new(adapter)))\n            .collect()\n    }\n\n    pub fn request_adapter(\n        &self,\n        desc: &RequestAdapterOptions,\n        backends: Backends,\n        id_in: Option<AdapterId>,\n    ) -> Result<AdapterId, wgt::RequestAdapterError> {\n        let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));\n        let desc = wgt::RequestAdapterOptions {\n            power_preference: desc.power_preference,\n            force_fallback_adapter: desc.force_fallback_adapter,\n            compatible_surface: compatible_surface.as_deref(),\n        };\n        let adapter = self.instance.request_adapter(&desc, backends)?;\n        let id = self.hub.adapters.prepare(id_in).assign(Arc::new(adapter));\n        Ok(id)\n    }\n\n    /// # Safety\n    ///\n    /// `hal_adapter` must be created from this global internal instance handle.\n    pub unsafe fn create_adapter_from_hal(\n        &self,\n        hal_adapter: hal::DynExposedAdapter,\n        input: Option<AdapterId>,\n    ) -> AdapterId {\n        profiling::scope!(\"Instance::create_adapter_from_hal\");\n\n        let fid = self.hub.adapters.prepare(input);\n        let id = fid.assign(Arc::new(Adapter::new(hal_adapter)));\n\n        resource_log!(\"Created Adapter {:?}\", id);\n        id\n    }\n\n    pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.get_info()\n    }\n\n    pub fn adapter_get_texture_format_features(\n        &self,\n        adapter_id: AdapterId,\n        format: wgt::TextureFormat,\n    ) -> wgt::TextureFormatFeatures {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.get_texture_format_features(format)\n    }\n\n    pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.features()\n    }\n\n    pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.limits()\n    }\n\n    pub fn adapter_downlevel_capabilities(\n        &self,\n        adapter_id: AdapterId,\n    ) -> wgt::DownlevelCapabilities {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.downlevel_capabilities()\n    }\n\n    pub fn adapter_get_presentation_timestamp(\n        &self,\n        adapter_id: AdapterId,\n    ) -> wgt::PresentationTimestamp {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.get_presentation_timestamp()\n    }\n\n    pub fn adapter_cooperative_matrix_properties(\n        &self,\n        adapter_id: AdapterId,\n    ) -> Vec<wgt::CooperativeMatrixProperties> {\n        let adapter = self.hub.adapters.get(adapter_id);\n        adapter.cooperative_matrix_properties()\n    }\n\n    pub fn adapter_drop(&self, adapter_id: AdapterId) {\n        profiling::scope!(\"Adapter::drop\");\n        api_log!(\"Adapter::drop {adapter_id:?}\");\n\n        self.hub.adapters.remove(adapter_id);\n    }\n}\n\nimpl Global {\n    pub fn adapter_request_device(\n        &self,\n        adapter_id: AdapterId,\n        desc: &DeviceDescriptor,\n        device_id_in: Option<DeviceId>,\n        queue_id_in: Option<QueueId>,\n    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {\n        profiling::scope!(\"Adapter::request_device\");\n        api_log!(\"Adapter::request_device\");\n\n        let device_fid = self.hub.devices.prepare(device_id_in);\n        let queue_fid = self.hub.queues.prepare(queue_id_in);\n\n        let adapter = self.hub.adapters.get(adapter_id);\n        let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;\n\n        let device_id = device_fid.assign(device);\n        resource_log!(\"Created Device {:?}\", device_id);\n\n        let queue_id = queue_fid.assign(queue);\n        resource_log!(\"Created Queue {:?}\", queue_id);\n\n        Ok((device_id, queue_id))\n    }\n\n    /// # Safety\n    ///\n    /// - `hal_device` must be created from `adapter_id` or its internal handle.\n    /// - `desc` must be a subset of `hal_device` features and limits.\n    pub unsafe fn create_device_from_hal(\n        &self,\n        adapter_id: AdapterId,\n        hal_device: hal::DynOpenDevice,\n        desc: &DeviceDescriptor,\n        device_id_in: Option<DeviceId>,\n        queue_id_in: Option<QueueId>,\n    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {\n        profiling::scope!(\"Global::create_device_from_hal\");\n\n        let devices_fid = self.hub.devices.prepare(device_id_in);\n        let queues_fid = self.hub.queues.prepare(queue_id_in);\n\n        let adapter = self.hub.adapters.get(adapter_id);\n        let (device, queue) =\n            adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;\n\n        let device_id = devices_fid.assign(device);\n        resource_log!(\"Created Device {:?}\", device_id);\n\n        let queue_id = queues_fid.assign(queue);\n        resource_log!(\"Created Queue {:?}\", queue_id);\n\n        Ok((device_id, queue_id))\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/lib.rs",
    "content": "//! This library safely implements WebGPU on native platforms.\n//! It is designed for integration into browsers, as well as wrapping\n//! into other language-specific user-friendly libraries.\n//!\n//! ## Feature flags\n#![doc = document_features::document_features!()]\n//!\n\n#![no_std]\n// When we have no backends, we end up with a lot of dead or otherwise unreachable code.\n#![cfg_attr(\n    all(\n        not(all(feature = \"vulkan\", not(target_arch = \"wasm32\"))),\n        not(all(feature = \"metal\", any(target_vendor = \"apple\"))),\n        not(all(feature = \"dx12\", windows)),\n        not(feature = \"gles\"),\n    ),\n    allow(unused, clippy::let_and_return)\n)]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n#![allow(\n    // It is much clearer to assert negative conditions with eq! false\n    clippy::bool_assert_comparison,\n    // We don't use syntax sugar where it's not necessary.\n    clippy::match_like_matches_macro,\n    // Redundant matching is more explicit.\n    clippy::redundant_pattern_matching,\n    // Explicit lifetimes are often easier to reason about.\n    clippy::needless_lifetimes,\n    // No need for defaults in the internal types.\n    clippy::new_without_default,\n    // Needless updates are more scalable, easier to play with features.\n    clippy::needless_update,\n    // Need many arguments for some core functions to be able to re-use code in many situations.\n    clippy::too_many_arguments,\n    // It gets in the way a lot and does not prevent bugs in practice.\n    clippy::pattern_type_mismatch,\n    // `wgpu-core` isn't entirely user-facing, so it's useful to document internal items.\n    rustdoc::private_intra_doc_links,\n)]\n#![warn(\n    clippy::alloc_instead_of_core,\n    clippy::ptr_as_ptr,\n    clippy::std_instead_of_alloc,\n    clippy::std_instead_of_core,\n    trivial_casts,\n    trivial_numeric_casts,\n    unsafe_op_in_unsafe_fn,\n    unused_extern_crates,\n    unused_qualifications\n)]\n// We use `Arc` in wgpu-core, but on wasm (unless opted out via `fragile-send-sync-non-atomic-wasm`)\n// wgpu-hal resources are not Send/Sync, causing a clippy warning for unnecessary `Arc`s.\n// We could use `Rc`s in this case as recommended, but unless atomics are enabled\n// this doesn't make a difference.\n// Therefore, this is only really a concern for users targeting WebGL\n// (the only reason to use wgpu-core on the web in the first place) that have atomics enabled.\n//\n// NOTE: Keep this in sync with `wgpu`.\n#![cfg_attr(not(send_sync), allow(clippy::arc_with_non_send_sync))]\n\nextern crate alloc;\n#[cfg(feature = \"std\")]\nextern crate std;\nextern crate wgpu_hal as hal;\nextern crate wgpu_types as wgt;\n\nmod as_hal;\npub mod binding_model;\npub mod command;\nmod conv;\npub mod device;\npub mod error;\npub mod global;\nmod hash_utils;\npub mod hub;\npub mod id;\npub mod identity;\nmod indirect_validation;\nmod init_tracker;\npub mod instance;\nmod lock;\npub mod pipeline;\nmod pipeline_cache;\nmod pool;\npub mod present;\npub mod ray_tracing;\npub mod registry;\npub mod resource;\nmod snatch;\npub mod storage;\nmod timestamp_normalization;\nmod track;\nmod weak_vec;\n// This is public for users who pre-compile shaders while still wanting to\n// preserve all run-time checks that `wgpu-core` does.\n// See <https://github.com/gfx-rs/wgpu/issues/3103>, after which this can be\n// made private again.\nmod scratch;\npub mod validation;\n\npub use validation::{map_storage_format_from_naga, map_storage_format_to_naga};\n\npub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS};\npub use naga;\n\nuse alloc::{\n    borrow::{Cow, ToOwned as _},\n    string::String,\n};\n\npub(crate) use hash_utils::*;\n\n/// The index of a queue submission.\n///\n/// These are the values stored in `Device::fence`.\npub type SubmissionIndex = hal::FenceValue;\n\ntype Index = u32;\ntype Epoch = u32;\n\npub type RawString = *const core::ffi::c_char;\npub type Label<'a> = Option<Cow<'a, str>>;\n\ntrait LabelHelpers<'a> {\n    fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str>;\n    fn to_string(&self) -> String;\n}\nimpl<'a> LabelHelpers<'a> for Label<'a> {\n    fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str> {\n        if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) {\n            return None;\n        }\n\n        self.as_deref()\n    }\n    fn to_string(&self) -> String {\n        self.as_deref().map(str::to_owned).unwrap_or_default()\n    }\n}\n\npub fn hal_label<T: AsRef<str>>(opt: Option<T>, flags: wgt::InstanceFlags) -> Option<T> {\n    if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) {\n        return None;\n    }\n\n    opt\n}\n\nconst DOWNLEVEL_WARNING_MESSAGE: &str = concat!(\n    \"The underlying API or device in use does not \",\n    \"support enough features to be a fully compliant implementation of WebGPU. \",\n    \"A subset of the features can still be used. \",\n    \"If you are running this program on native and not in a browser and wish to limit \",\n    \"the features you use to the supported subset, \",\n    \"call Adapter::downlevel_properties or Device::downlevel_properties to get \",\n    \"a listing of the features the current \",\n    \"platform supports.\"\n);\n\nconst DOWNLEVEL_ERROR_MESSAGE: &str = concat!(\n    \"This is not an invalid use of WebGPU: the underlying API or device does not \",\n    \"support enough features to be a fully compliant implementation. \",\n    \"A subset of the features can still be used. \",\n    \"If you are running this program on native and not in a browser \",\n    \"and wish to work around this issue, call \",\n    \"Adapter::downlevel_properties or Device::downlevel_properties \",\n    \"to get a listing of the features the current platform supports.\"\n);\n\n#[cfg(feature = \"api_log_info\")]\nmacro_rules! api_log {\n    ($($arg:tt)+) => (log::info!($($arg)+))\n}\n#[cfg(not(feature = \"api_log_info\"))]\nmacro_rules! api_log {\n    ($($arg:tt)+) => (log::trace!($($arg)+))\n}\n\n#[cfg(feature = \"api_log_info\")]\nmacro_rules! api_log_debug {\n    ($($arg:tt)+) => (log::info!($($arg)+))\n}\n#[cfg(not(feature = \"api_log_info\"))]\nmacro_rules! api_log_debug {\n    ($($arg:tt)+) => (log::debug!($($arg)+))\n}\n\npub(crate) use api_log;\npub(crate) use api_log_debug;\n\n#[cfg(feature = \"resource_log_info\")]\nmacro_rules! resource_log {\n    ($($arg:tt)+) => (log::info!($($arg)+))\n}\n#[cfg(not(feature = \"resource_log_info\"))]\nmacro_rules! resource_log {\n    ($($arg:tt)+) => (log::trace!($($arg)+))\n}\npub(crate) use resource_log;\n\n#[inline]\npub(crate) fn get_lowest_common_denom(a: u32, b: u32) -> u32 {\n    let gcd = if a >= b {\n        get_greatest_common_divisor(a, b)\n    } else {\n        get_greatest_common_divisor(b, a)\n    };\n    a * b / gcd\n}\n\n#[inline]\npub(crate) fn get_greatest_common_divisor(mut a: u32, mut b: u32) -> u32 {\n    assert!(a >= b);\n    loop {\n        let c = a % b;\n        if c == 0 {\n            return b;\n        } else {\n            a = b;\n            b = c;\n        }\n    }\n}\n\n#[cfg(not(feature = \"std\"))]\nuse core::cell::OnceCell as OnceCellOrLock;\n#[cfg(feature = \"std\")]\nuse std::sync::OnceLock as OnceCellOrLock;\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_lcd() {\n        assert_eq!(get_lowest_common_denom(2, 2), 2);\n        assert_eq!(get_lowest_common_denom(2, 3), 6);\n        assert_eq!(get_lowest_common_denom(6, 4), 12);\n    }\n\n    #[test]\n    fn test_gcd() {\n        assert_eq!(get_greatest_common_divisor(5, 1), 1);\n        assert_eq!(get_greatest_common_divisor(4, 2), 2);\n        assert_eq!(get_greatest_common_divisor(6, 4), 2);\n        assert_eq!(get_greatest_common_divisor(7, 7), 7);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/lock/mod.rs",
    "content": "//! Instrumented lock types.\n//!\n//! This module defines a set of instrumented wrappers for the lock\n//! types used in `wgpu-core` ([`Mutex`], [`RwLock`], and\n//! [`SnatchLock`]) that help us understand and validate `wgpu-core`\n//! synchronization.\n//!\n//! - The [`ranked`] module defines lock types that perform run-time\n//!   checks to ensure that each thread acquires locks only in a\n//!   specific order, to prevent deadlocks.\n//!\n//! - The [`observing`] module defines lock types that record\n//!   `wgpu-core`'s lock acquisition activity to disk, for later\n//!   analysis by the `lock-analyzer` binary.\n//!\n//! - The [`vanilla`] module defines lock types that are\n//!   uninstrumented, no-overhead wrappers around the standard lock\n//!   types.\n//!\n//! If the `wgpu_validate_locks` config is set (for example, with\n//! `RUSTFLAGS='--cfg wgpu_validate_locks'`), `wgpu-core` uses the\n//! [`ranked`] module's locks. We hope to make this the default for\n//! debug builds soon.\n//!\n//! If the `observe_locks` feature is enabled, `wgpu-core` uses the\n//! [`observing`] module's locks.\n//!\n//! Otherwise, `wgpu-core` uses the [`vanilla`] module's locks.\n//!\n//! [`Mutex`]: parking_lot::Mutex\n//! [`RwLock`]: parking_lot::RwLock\n//! [`SnatchLock`]: crate::snatch::SnatchLock\n\npub mod rank;\n\n#[cfg(feature = \"std\")] // requires thread-locals to work\n#[cfg_attr(not(wgpu_validate_locks), allow(dead_code))]\nmod ranked;\n\n#[cfg(feature = \"observe_locks\")]\nmod observing;\n\n#[cfg_attr(any(wgpu_validate_locks, feature = \"observe_locks\"), allow(dead_code))]\nmod vanilla;\n\n#[cfg(wgpu_validate_locks)]\nuse ranked as chosen;\n\n#[cfg(feature = \"observe_locks\")]\nuse observing as chosen;\n\n#[cfg(not(any(wgpu_validate_locks, feature = \"observe_locks\")))]\nuse vanilla as chosen;\n\npub use chosen::{Mutex, MutexGuard, RankData, RwLock, RwLockReadGuard, RwLockWriteGuard};\n"
  },
  {
    "path": "wgpu-core/src/lock/observing.rs",
    "content": "//! Lock types that observe lock acquisition order.\n//!\n//! This module's [`Mutex`] type is instrumented to observe the\n//! nesting of `wgpu-core` lock acquisitions. Whenever `wgpu-core`\n//! acquires one lock while it is already holding another, we note\n//! that nesting pair. This tells us what the [`LockRank::followers`]\n//! set for each lock would need to include to accommodate\n//! `wgpu-core`'s observed behavior.\n//!\n//! When `wgpu-core`'s `observe_locks` feature is enabled, if the\n//! `WGPU_CORE_LOCK_OBSERVE_DIR` environment variable is set to the\n//! path of an existing directory, then every thread that acquires a\n//! lock in `wgpu-core` will write its own log file to that directory.\n//! You can then run the `wgpu` workspace's `lock-analyzer` binary to\n//! read those files and summarize the results. The output from\n//! `lock-analyzer` has the same form as the lock ranks given in\n//! [`lock/rank.rs`].\n//!\n//! If the `WGPU_CORE_LOCK_OBSERVE_DIR` environment variable is not\n//! set, then no instrumentation takes place, and the locks behave\n//! normally.\n//!\n//! To make sure we capture all acquisitions regardless of when the\n//! program exits, each thread writes events directly to its log file\n//! as they occur. A `write` system call is generally just a copy from\n//! userspace into the kernel's buffer, so hopefully this approach\n//! will still have tolerable performance.\n//!\n//! [`lock/rank.rs`]: ../../../src/wgpu_core/lock/rank.rs.html\n\nuse alloc::{format, string::String};\nuse core::{cell::RefCell, panic::Location};\nuse std::{\n    fs::File,\n    path::{Path, PathBuf},\n};\n\nuse super::rank::{LockRank, LockRankSet};\nuse crate::FastHashSet;\n\npub type RankData = Option<HeldLock>;\n\n/// A `Mutex` instrumented for lock acquisition order observation.\n///\n/// This is just a wrapper around a [`parking_lot::Mutex`], along with\n/// its rank in the `wgpu_core` lock ordering.\n///\n/// For details, see [the module documentation][self].\npub struct Mutex<T> {\n    inner: parking_lot::Mutex<T>,\n    rank: LockRank,\n}\n\n/// A guard produced by locking [`Mutex`].\n///\n/// This is just a wrapper around a [`parking_lot::MutexGuard`], along\n/// with the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct MutexGuard<'a, T> {\n    inner: parking_lot::MutexGuard<'a, T>,\n    _state: LockStateGuard,\n}\n\nimpl<T> Mutex<T> {\n    pub fn new(rank: LockRank, value: T) -> Mutex<T> {\n        Mutex {\n            inner: parking_lot::Mutex::new(value),\n            rank,\n        }\n    }\n\n    #[track_caller]\n    pub fn lock(&self) -> MutexGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        MutexGuard {\n            inner: self.inner.lock(),\n            _state: LockStateGuard { saved },\n        }\n    }\n\n    pub fn into_inner(self) -> T {\n        self.inner.into_inner()\n    }\n}\n\nimpl<'a, T> core::ops::Deref for MutexGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> core::ops::DerefMut for MutexGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.deref_mut()\n    }\n}\n\nimpl<T: core::fmt::Debug> core::fmt::Debug for Mutex<T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\n/// An `RwLock` instrumented for lock acquisition order observation.\n///\n/// This is just a wrapper around a [`parking_lot::RwLock`], along with\n/// its rank in the `wgpu_core` lock ordering.\n///\n/// For details, see [the module documentation][self].\npub struct RwLock<T> {\n    inner: parking_lot::RwLock<T>,\n    rank: LockRank,\n}\n\n/// A read guard produced by locking [`RwLock`] for reading.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`], along with\n/// the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct RwLockReadGuard<'a, T> {\n    inner: parking_lot::RwLockReadGuard<'a, T>,\n    _state: LockStateGuard,\n}\n\n/// A write guard produced by locking [`RwLock`] for writing.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`], along\n/// with the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct RwLockWriteGuard<'a, T> {\n    inner: parking_lot::RwLockWriteGuard<'a, T>,\n    _state: LockStateGuard,\n}\n\nimpl<T> RwLock<T> {\n    pub fn new(rank: LockRank, value: T) -> RwLock<T> {\n        RwLock {\n            inner: parking_lot::RwLock::new(value),\n            rank,\n        }\n    }\n\n    #[track_caller]\n    pub fn read(&self) -> RwLockReadGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        RwLockReadGuard {\n            inner: self.inner.read(),\n            _state: LockStateGuard { saved },\n        }\n    }\n\n    #[track_caller]\n    pub fn write(&self) -> RwLockWriteGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        RwLockWriteGuard {\n            inner: self.inner.write(),\n            _state: LockStateGuard { saved },\n        }\n    }\n\n    /// Force an read-unlock operation on this lock.\n    ///\n    /// Safety:\n    /// - A read lock must be held which is not held by a guard.\n    pub unsafe fn force_unlock_read(&self, data: RankData) {\n        release(data);\n        unsafe { self.inner.force_unlock_read() };\n    }\n}\n\nimpl<'a, T> RwLockReadGuard<'a, T> {\n    // Forget the read guard, leaving the lock in a locked state with no guard.\n    //\n    // Equivalent to std::mem::forget, but preserves the information about the lock\n    // rank.\n    pub fn forget(this: Self) -> RankData {\n        core::mem::forget(this.inner);\n\n        this._state.saved\n    }\n}\n\nimpl<'a, T> RwLockWriteGuard<'a, T> {\n    pub fn downgrade(this: Self) -> RwLockReadGuard<'a, T> {\n        RwLockReadGuard {\n            inner: parking_lot::RwLockWriteGuard::downgrade(this.inner),\n            _state: this._state,\n        }\n    }\n}\n\nimpl<T: core::fmt::Debug> core::fmt::Debug for RwLock<T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\nimpl<'a, T> core::ops::Deref for RwLockReadGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> core::ops::Deref for RwLockWriteGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> core::ops::DerefMut for RwLockWriteGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.deref_mut()\n    }\n}\n\n/// A container that restores a prior per-thread lock state when dropped.\n///\n/// This type serves two purposes:\n///\n/// - Operations like `RwLockWriteGuard::downgrade` would like to be able to\n///   destructure lock guards and reassemble their pieces into new guards, but\n///   if the guard type itself implements `Drop`, we can't destructure it\n///   without unsafe code or pointless `Option`s whose state is almost always\n///   statically known.\n///\n/// - We can just implement `Drop` for this type once, and then use it in lock\n///   guards, rather than implementing `Drop` separately for each guard type.\nstruct LockStateGuard {\n    /// The youngest lock that was already held when we acquired this\n    /// one, if any.\n    saved: Option<HeldLock>,\n}\n\nimpl Drop for LockStateGuard {\n    fn drop(&mut self) {\n        release(self.saved)\n    }\n}\n\n/// Check and record the acquisition of a lock with `new_rank`.\n///\n/// Log the acquisition of a lock with `new_rank`, and\n/// update the per-thread state accordingly.\n///\n/// Return the `Option<HeldLock>` state that must be restored when this lock is\n/// released.\nfn acquire(new_rank: LockRank, location: &'static Location<'static>) -> Option<HeldLock> {\n    LOCK_STATE.with_borrow_mut(|state| match *state {\n        ThreadState::Disabled => None,\n        ThreadState::Initial => {\n            let Ok(dir) = std::env::var(\"WGPU_CORE_LOCK_OBSERVE_DIR\") else {\n                *state = ThreadState::Disabled;\n                return None;\n            };\n\n            // Create the observation log file.\n            let mut log = ObservationLog::create(dir)\n                .expect(\"Failed to open lock observation file (does the dir exist?)\");\n\n            // Log the full set of lock ranks, so that the analysis can even see\n            // locks that are only acquired in isolation.\n            for rank in LockRankSet::all().iter() {\n                log.write_rank(rank);\n            }\n\n            // Update our state to reflect that we are logging acquisitions, and\n            // that we have acquired this lock.\n            *state = ThreadState::Enabled {\n                held_lock: Some(HeldLock {\n                    rank: new_rank,\n                    location,\n                }),\n                log,\n            };\n\n            // Since this is the first acquisition on this thread, we know that\n            // there is no prior lock held, and thus nothing to log yet.\n            None\n        }\n        ThreadState::Enabled {\n            ref mut held_lock,\n            ref mut log,\n        } => {\n            if let Some(ref held_lock) = held_lock {\n                log.write_acquisition(held_lock, new_rank, location);\n            }\n\n            held_lock.replace(HeldLock {\n                rank: new_rank,\n                location,\n            })\n        }\n    })\n}\n\n/// Record the release of a lock whose saved state was `saved`.\nfn release(saved: Option<HeldLock>) {\n    LOCK_STATE.with_borrow_mut(|state| {\n        if let ThreadState::Enabled {\n            ref mut held_lock, ..\n        } = *state\n        {\n            *held_lock = saved;\n        }\n    });\n}\n\nstd::thread_local! {\n    static LOCK_STATE: RefCell<ThreadState> = const { RefCell::new(ThreadState::Initial) };\n}\n\n/// Thread-local state for lock observation.\nenum ThreadState {\n    /// This thread hasn't yet checked the environment variable.\n    Initial,\n\n    /// This thread checked the environment variable, and it was\n    /// unset, so this thread is not observing lock acquisitions.\n    Disabled,\n\n    /// Lock observation is enabled for this thread.\n    Enabled {\n        held_lock: Option<HeldLock>,\n        log: ObservationLog,\n    },\n}\n\n/// Information about a currently held lock.\n#[derive(Debug, Copy, Clone)]\npub struct HeldLock {\n    /// The lock's rank.\n    rank: LockRank,\n\n    /// Where we acquired the lock.\n    location: &'static Location<'static>,\n}\n\n/// A log to which we can write observations of lock activity.\nstruct ObservationLog {\n    /// The file to which we are logging lock observations.\n    log_file: File,\n\n    /// [`Location`]s we've seen so far.\n    ///\n    /// This is a hashset of raw pointers because raw pointers have\n    /// the [`Eq`] and [`Hash`] relations we want: the pointer value, not\n    /// the contents. There's no unsafe code in this module.\n    locations_seen: FastHashSet<*const Location<'static>>,\n\n    /// Buffer for serializing events, retained for allocation reuse.\n    buffer: String,\n}\n\nimpl ObservationLog {\n    /// Create an observation log in `dir` for the current pid and thread.\n    fn create(dir: impl AsRef<Path>) -> Result<Self, std::io::Error> {\n        let mut path = PathBuf::from(dir.as_ref());\n        path.push(format!(\n            \"locks-{}.{:?}.ron\",\n            std::process::id(),\n            std::thread::current().id()\n        ));\n        let log_file = File::create(&path)?;\n        Ok(ObservationLog {\n            log_file,\n            locations_seen: FastHashSet::default(),\n            buffer: String::new(),\n        })\n    }\n\n    /// Record the acquisition of one lock while holding another.\n    ///\n    /// Log that we acquired a lock of `new_rank` at `new_location` while still\n    /// holding other locks, the most recently acquired of which has\n    /// `older_rank`.\n    fn write_acquisition(\n        &mut self,\n        older_lock: &HeldLock,\n        new_rank: LockRank,\n        new_location: &'static Location<'static>,\n    ) {\n        self.write_location(older_lock.location);\n        self.write_location(new_location);\n        self.write_action(&Action::Acquisition {\n            older_rank: older_lock.rank.bit.number(),\n            older_location: addr(older_lock.location),\n            newer_rank: new_rank.bit.number(),\n            newer_location: addr(new_location),\n        });\n    }\n\n    fn write_location(&mut self, location: &'static Location<'static>) {\n        if self.locations_seen.insert(location) {\n            self.write_action(&Action::Location {\n                address: addr(location),\n                file: location.file(),\n                line: location.line(),\n                column: location.column(),\n            });\n        }\n    }\n\n    fn write_rank(&mut self, rank: LockRankSet) {\n        self.write_action(&Action::Rank {\n            bit: rank.number(),\n            member_name: rank.member_name(),\n            const_name: rank.const_name(),\n        });\n    }\n\n    fn write_action(&mut self, action: &Action) {\n        use std::io::Write;\n\n        self.buffer.clear();\n        ron::ser::to_writer(&mut self.buffer, &action)\n            .expect(\"error serializing `lock::observing::Action`\");\n        self.buffer.push('\\n');\n        self.log_file\n            .write_all(self.buffer.as_bytes())\n            .expect(\"error writing `lock::observing::Action`\");\n    }\n}\n\n/// An action logged by a thread that is observing lock acquisition order.\n///\n/// Each thread's log file is a sequence of these enums, serialized\n/// using the [`ron`] crate, one action per line.\n///\n/// Lock observation cannot assume that there will be any convenient\n/// finalization point before the program exits, so in practice,\n/// actions must be written immediately when they occur. This means we\n/// can't, say, accumulate tables and write them out when they're\n/// complete. The `lock-analyzer` binary is then responsible for\n/// consolidating the data into a single table of observed transitions.\n#[derive(serde::Serialize)]\nenum Action {\n    /// A location that we will refer to in later actions.\n    ///\n    /// We write one of these events the first time we see a\n    /// particular `Location`. Treating this as a separate action\n    /// simply lets us avoid repeating the content over and over\n    /// again in every [`Acquisition`] action.\n    ///\n    /// [`Acquisition`]: Action::Acquisition\n    Location {\n        address: usize,\n        file: &'static str,\n        line: u32,\n        column: u32,\n    },\n\n    /// A lock rank that we will refer to in later actions.\n    ///\n    /// We write out one these events for every lock rank at the\n    /// beginning of each thread's log file. Treating this as a\n    /// separate action simply lets us avoid repeating the names over\n    /// and over again in every [`Acquisition`] action.\n    ///\n    /// [`Acquisition`]: Action::Acquisition\n    Rank {\n        bit: u32,\n        member_name: &'static str,\n        const_name: &'static str,\n    },\n\n    /// An attempt to acquire a lock while holding another lock.\n    Acquisition {\n        /// The number of the already acquired lock's rank.\n        older_rank: u32,\n\n        /// The source position at which we acquired it. Specifically,\n        /// its `Location`'s address, as an integer.\n        older_location: usize,\n\n        /// The number of the rank of the lock we are acquiring.\n        newer_rank: u32,\n\n        /// The source position at which we are acquiring it.\n        /// Specifically, its `Location`'s address, as an integer.\n        newer_location: usize,\n    },\n}\n\nimpl LockRankSet {\n    /// Return the number of this rank's first member.\n    fn number(self) -> u32 {\n        self.bits().trailing_zeros()\n    }\n}\n\n/// Convenience for `core::ptr::from_ref(t) as usize`.\nfn addr<T>(t: &T) -> usize {\n    core::ptr::from_ref(t) as usize\n}\n"
  },
  {
    "path": "wgpu-core/src/lock/rank.rs",
    "content": "//! Ranks for `wgpu-core` locks, restricting acquisition order.\n//!\n//! See [`LockRank`].\n\n/// The rank of a lock.\n///\n/// Each [`Mutex`], [`RwLock`], and [`SnatchLock`] in `wgpu-core` has been\n/// assigned a *rank*: a node in the DAG defined at the bottom of\n/// `wgpu-core/src/lock/rank.rs`. The rank of the most recently\n/// acquired lock you are still holding determines which locks you may\n/// attempt to acquire next.\n///\n/// When you create a lock in `wgpu-core`, you must specify its rank\n/// by passing in a [`LockRank`] value. This module declares a\n/// pre-defined set of ranks to cover everything in `wgpu-core`, named\n/// after the type in which they occur, and the name of the type's\n/// field that is a lock. For example, [`CommandBuffer::data`] is a\n/// `Mutex`, and its rank here is the constant\n/// [`COMMAND_BUFFER_DATA`].\n///\n/// [`Mutex`]: parking_lot::Mutex\n/// [`RwLock`]: parking_lot::RwLock\n/// [`SnatchLock`]: crate::snatch::SnatchLock\n/// [`CommandBuffer::data`]: crate::command::CommandBuffer::data\n#[derive(Debug, Copy, Clone)]\npub struct LockRank {\n    /// The bit representing this lock.\n    ///\n    /// There should only be a single bit set in this value.\n    pub(super) bit: LockRankSet,\n\n    /// A bitmask of permitted successor ranks.\n    ///\n    /// If `rank` is the rank of the most recently acquired lock we\n    /// are still holding, then `rank.followers` is the mask of\n    /// locks we are allowed to acquire next.\n    ///\n    /// The `define_lock_ranks!` macro ensures that there are no\n    /// cycles in the graph of lock ranks and their followers.\n    pub(super) followers: LockRankSet,\n}\n\n/// Define a set of lock ranks, and each rank's permitted successors.\nmacro_rules! define_lock_ranks {\n    {\n        $(\n            $( #[ $attr:meta ] )*\n            rank $name:ident $member:literal followed by { $( $follower:ident ),* $(,)? }\n        )*\n    } => {\n        // An enum that assigns a unique number to each rank.\n        #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n        enum LockRankNumber { $( $name, )* }\n\n        bitflags::bitflags! {\n            #[derive(Debug, Copy, Clone, Eq, PartialEq)]\n            /// A bitflags type representing a set of lock ranks.\n            pub struct LockRankSet: u64 {\n                $(\n                    const $name = 1 << (LockRankNumber:: $name as u64);\n                )*\n            }\n        }\n\n        impl LockRankSet {\n            pub fn member_name(self) -> &'static str {\n                match self {\n                    $(\n                        LockRankSet:: $name => $member,\n                    )*\n                    _ => \"<unrecognized LockRankSet bit>\",\n                }\n            }\n\n            #[cfg_attr(not(feature = \"observe_locks\"), allow(dead_code))]\n            pub fn const_name(self) -> &'static str {\n                match self {\n                    $(\n                        LockRankSet:: $name => stringify!($name),\n                    )*\n                    _ => \"<unrecognized LockRankSet bit>\",\n                }\n            }\n        }\n\n        $(\n            // If there is any cycle in the ranking, the initializers\n            // for `followers` will be cyclic, and rustc will give us\n            // an error message explaining the cycle.\n            $( #[ $attr ] )*\n            pub const $name: LockRank = LockRank {\n                bit: LockRankSet:: $name,\n                followers: LockRankSet::empty() $( .union($follower.bit) )*,\n            };\n        )*\n    }\n}\n\ndefine_lock_ranks! {\n    rank COMMAND_BUFFER_DATA \"CommandBuffer::data\" followed by {\n        DEVICE_SNATCHABLE_LOCK,\n        DEVICE_USAGE_SCOPES,\n        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n        BUFFER_MAP_STATE,\n    }\n    rank DEVICE_SNATCHABLE_LOCK \"Device::snatchable_lock\" followed by {\n        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n        DEVICE_TRACE,\n        BUFFER_MAP_STATE,\n        // Uncomment this to see an interesting cycle.\n        // COMMAND_BUFFER_DATA,\n    }\n    rank BUFFER_MAP_STATE \"Buffer::map_state\" followed by {\n        QUEUE_PENDING_WRITES,\n        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n        DEVICE_TRACE,\n    }\n    rank QUEUE_PENDING_WRITES \"Queue::pending_writes\" followed by {\n        COMMAND_ALLOCATOR_FREE_ENCODERS,\n        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n        QUEUE_LIFE_TRACKER,\n    }\n    rank QUEUE_LIFE_TRACKER \"Queue::life_tracker\" followed by {\n        COMMAND_ALLOCATOR_FREE_ENCODERS,\n        DEVICE_TRACE,\n    }\n    rank COMMAND_ALLOCATOR_FREE_ENCODERS \"CommandAllocator::free_encoders\" followed by {\n        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n    }\n\n    rank BUFFER_BIND_GROUPS \"Buffer::bind_groups\" followed by { }\n    rank BUFFER_INITIALIZATION_STATUS \"Buffer::initialization_status\" followed by { }\n    rank DEVICE_COMMAND_INDICES \"Device::command_indices\" followed by {}\n    rank DEVICE_DEFERRED_DESTROY \"Device::deferred_destroy\" followed by {}\n    rank DEVICE_FENCE \"Device::fence\" followed by { }\n    rank DEVICE_TRACE \"Device::trace\" followed by { }\n    rank DEVICE_TRACKERS \"Device::trackers\" followed by { }\n    rank DEVICE_LOST_CLOSURE \"Device::device_lost_closure\" followed by { }\n    rank DEVICE_USAGE_SCOPES \"Device::usage_scopes\" followed by { }\n    rank IDENTITY_MANAGER_VALUES \"IdentityManager::values\" followed by { }\n    rank REGISTRY_STORAGE \"Registry::storage\" followed by { }\n    rank RESOURCE_POOL_INNER \"ResourcePool::inner\" followed by { }\n    rank SHARED_TRACKER_INDEX_ALLOCATOR_INNER \"SharedTrackerIndexAllocator::inner\" followed by { }\n    rank SURFACE_PRESENTATION \"Surface::presentation\" followed by { }\n    rank TEXTURE_BIND_GROUPS \"Texture::bind_groups\" followed by { }\n    rank TEXTURE_INITIALIZATION_STATUS \"Texture::initialization_status\" followed by { }\n    rank TEXTURE_CLEAR_MODE \"Texture::clear_mode\" followed by { }\n    rank TEXTURE_VIEWS \"Texture::views\" followed by { }\n    rank BLAS_BUILT_INDEX \"Blas::built_index\" followed by { }\n    rank BLAS_COMPACTION_STATE \"Blas::compaction_size\" followed by { }\n    rank TLAS_BUILT_INDEX \"Tlas::built_index\" followed by { }\n    rank TLAS_DEPENDENCIES \"Tlas::dependencies\" followed by { }\n    rank BUFFER_POOL \"BufferPool::buffers\" followed by { }\n\n    #[cfg(test)]\n    rank PAWN \"pawn\" followed by { ROOK, BISHOP }\n    #[cfg(test)]\n    rank ROOK \"rook\" followed by { KNIGHT }\n    #[cfg(test)]\n    rank KNIGHT \"knight\" followed by { }\n    #[cfg(test)]\n    rank BISHOP \"bishop\" followed by { }\n}\n"
  },
  {
    "path": "wgpu-core/src/lock/ranked.rs",
    "content": "//! Lock types that enforce well-ranked lock acquisition order.\n//!\n//! This module's [`Mutex`] and [`RwLock` types are instrumented to check that\n//! `wgpu-core` acquires locks according to their rank, to prevent deadlocks. To\n//! use it, put `--cfg wgpu_validate_locks` in `RUSTFLAGS`.\n//!\n//! The [`LockRank`] constants in the [`lock::rank`] module describe edges in a\n//! directed graph of lock acquisitions: each lock's rank says, if this is the most\n//! recently acquired lock that you are still holding, then these are the locks you\n//! are allowed to acquire next.\n//!\n//! As long as this graph doesn't have cycles, any number of threads can acquire\n//! locks along paths through the graph without deadlock:\n//!\n//! - Assume that if a thread is holding a lock, then it will either release it,\n//!   or block trying to acquire another one. No thread just sits on its locks\n//!   forever for unrelated reasons. If it did, then that would be a source of\n//!   deadlock \"outside the system\" that we can't do anything about.\n//!\n//! - This module asserts that threads acquire and release locks in a stack-like\n//!   order: a lock is dropped only when it is the *most recently acquired* lock\n//!   *still held* - call this the \"youngest\" lock. This stack-like ordering\n//!   isn't a Rust requirement; Rust lets you drop guards in any order you like.\n//!   This is a restriction we impose.\n//!\n//! - Consider the directed graph whose nodes are locks, and whose edges go from\n//!   each lock to its permitted followers, the locks in its [`LockRank::followers`]\n//!   set. The definition of the [`lock::rank`] module's [`LockRank`] constants\n//!   ensures that this graph has no cycles, including trivial cycles from a node to\n//!   itself.\n//!\n//! - This module then asserts that each thread attempts to acquire a lock only if\n//!   it is among its youngest lock's permitted followers. Thus, as a thread\n//!   acquires locks, it must be traversing a path through the graph along its\n//!   edges.\n//!\n//! - Because there are no cycles in the graph, whenever one thread is blocked\n//!   waiting to acquire a lock, that lock must be held by a different thread: if\n//!   you were allowed to acquire a lock you already hold, that would be a cycle in\n//!   the graph.\n//!\n//! - Furthermore, because the graph has no cycles, as we work our way from each\n//!   thread to the thread it is blocked waiting for, we must eventually reach an\n//!   end point: there must be some thread that is able to acquire its next lock, or\n//!   that is about to release a lock.\n//!\n//! Thus, the system as a whole is always able to make progress: it is free of\n//! deadlocks.\n//!\n//! Note that this validation only monitors each thread's behavior in isolation:\n//! there's only thread-local state, nothing communicated between threads. So we\n//! don't detect deadlocks, per se, only the potential to cause deadlocks. This\n//! means that the validation is conservative, but more reproducible, since it's not\n//! dependent on any particular interleaving of execution.\n//!\n//! [`lock::rank`]: crate::lock::rank\n\nuse core::{cell::Cell, fmt, ops, panic::Location};\n\nuse super::rank::LockRank;\n\npub use LockState as RankData;\n\n/// A `Mutex` instrumented for deadlock prevention.\n///\n/// This is just a wrapper around a [`parking_lot::Mutex`], along with\n/// its rank in the `wgpu_core` lock ordering.\n///\n/// For details, see [the module documentation][self].\npub struct Mutex<T> {\n    inner: parking_lot::Mutex<T>,\n    rank: LockRank,\n}\n\n/// A guard produced by locking [`Mutex`].\n///\n/// This is just a wrapper around a [`parking_lot::MutexGuard`], along\n/// with the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct MutexGuard<'a, T> {\n    inner: parking_lot::MutexGuard<'a, T>,\n    saved: LockStateGuard,\n}\n\nstd::thread_local! {\n    static LOCK_STATE: Cell<LockState> = const { Cell::new(LockState::INITIAL) };\n}\n\n/// Per-thread state for the deadlock checker.\n#[derive(Debug, Copy, Clone)]\npub struct LockState {\n    /// The last lock we acquired, and where.\n    last_acquired: Option<(LockRank, &'static Location<'static>)>,\n\n    /// The number of locks currently held.\n    ///\n    /// This is used to enforce stack-like lock acquisition and release.\n    depth: u32,\n}\n\nimpl LockState {\n    const INITIAL: LockState = LockState {\n        last_acquired: None,\n        depth: 0,\n    };\n}\n\n/// A container that restores a [`LockState`] when dropped.\n///\n/// This type serves two purposes:\n///\n/// - Operations like `RwLockWriteGuard::downgrade` would like to be able to\n///   destructure lock guards and reassemble their pieces into new guards, but\n///   if the guard type itself implements `Drop`, we can't destructure it\n///   without unsafe code or pointless `Option`s whose state is almost always\n///   statically known.\n///\n/// - We can just implement `Drop` for this type once, and then use it in lock\n///   guards, rather than implementing `Drop` separately for each guard type.\nstruct LockStateGuard(LockState);\n\nimpl Drop for LockStateGuard {\n    fn drop(&mut self) {\n        release(self.0)\n    }\n}\n\n/// Check and record the acquisition of a lock with `new_rank`.\n///\n/// Check that acquiring a lock with `new_rank` is permitted at this point, and\n/// update the per-thread state accordingly.\n///\n/// Return the `LockState` that must be restored when this thread is released.\nfn acquire(new_rank: LockRank, location: &'static Location<'static>) -> LockState {\n    let state = LOCK_STATE.get();\n    // Initially, it's fine to acquire any lock. So we only\n    // need to check when `last_acquired` is `Some`.\n    if let Some((ref last_rank, ref last_location)) = state.last_acquired {\n        assert!(\n            last_rank.followers.contains(new_rank.bit),\n            \"Attempt to acquire nested mutexes in wrong order:\\n\\\n             last locked {:<35} at {}\\n\\\n             now locking {:<35} at {}\\n\\\n             Locking {} after locking {} is not permitted.\",\n            last_rank.bit.member_name(),\n            last_location,\n            new_rank.bit.member_name(),\n            location,\n            new_rank.bit.member_name(),\n            last_rank.bit.member_name(),\n        );\n    }\n    LOCK_STATE.set(LockState {\n        last_acquired: Some((new_rank, location)),\n        depth: state.depth + 1,\n    });\n    state\n}\n\n/// Record the release of a lock whose saved state was `saved`.\n///\n/// Check that locks are being acquired in stacking order, and update the\n/// per-thread state accordingly.\nfn release(saved: LockState) {\n    let prior = LOCK_STATE.replace(saved);\n\n    // Although Rust allows mutex guards to be dropped in any\n    // order, this analysis requires that locks be acquired and\n    // released in stack order: the next lock to be released must be\n    // the most recently acquired lock still held.\n    assert_eq!(\n        prior.depth,\n        saved.depth + 1,\n        \"Lock not released in stacking order\"\n    );\n}\n\nimpl<T> Mutex<T> {\n    pub fn new(rank: LockRank, value: T) -> Mutex<T> {\n        Mutex {\n            inner: parking_lot::Mutex::new(value),\n            rank,\n        }\n    }\n\n    #[track_caller]\n    pub fn lock(&self) -> MutexGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        MutexGuard {\n            inner: self.inner.lock(),\n            saved: LockStateGuard(saved),\n        }\n    }\n}\n\nimpl<'a, T> ops::Deref for MutexGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> ops::DerefMut for MutexGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.deref_mut()\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for Mutex<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\n/// An `RwLock` instrumented for deadlock prevention.\n///\n/// This is just a wrapper around a [`parking_lot::RwLock`], along with\n/// its rank in the `wgpu_core` lock ordering.\n///\n/// For details, see [the module documentation][self].\npub struct RwLock<T> {\n    inner: parking_lot::RwLock<T>,\n    rank: LockRank,\n}\n\n/// A read guard produced by locking [`RwLock`] for reading.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`], along with\n/// the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct RwLockReadGuard<'a, T> {\n    inner: parking_lot::RwLockReadGuard<'a, T>,\n    saved: LockStateGuard,\n}\n\n/// A write guard produced by locking [`RwLock`] for writing.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`], along\n/// with the state needed to track lock acquisition.\n///\n/// For details, see [the module documentation][self].\npub struct RwLockWriteGuard<'a, T> {\n    inner: parking_lot::RwLockWriteGuard<'a, T>,\n    saved: LockStateGuard,\n}\n\nimpl<T> RwLock<T> {\n    pub fn new(rank: LockRank, value: T) -> RwLock<T> {\n        RwLock {\n            inner: parking_lot::RwLock::new(value),\n            rank,\n        }\n    }\n\n    #[track_caller]\n    pub fn read(&self) -> RwLockReadGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        RwLockReadGuard {\n            inner: self.inner.read(),\n            saved: LockStateGuard(saved),\n        }\n    }\n\n    #[track_caller]\n    pub fn write(&self) -> RwLockWriteGuard<'_, T> {\n        let saved = acquire(self.rank, Location::caller());\n        RwLockWriteGuard {\n            inner: self.inner.write(),\n            saved: LockStateGuard(saved),\n        }\n    }\n\n    /// Force an read-unlock operation on this lock.\n    ///\n    /// Safety:\n    /// - A read lock must be held which is not held by a guard.\n    pub unsafe fn force_unlock_read(&self, data: RankData) {\n        release(data);\n        unsafe { self.inner.force_unlock_read() };\n    }\n}\n\nimpl<'a, T> RwLockReadGuard<'a, T> {\n    // Forget the read guard, leaving the lock in a locked state with no guard.\n    //\n    // Equivalent to std::mem::forget, but preserves the information about the lock\n    // rank.\n    pub fn forget(this: Self) -> RankData {\n        core::mem::forget(this.inner);\n\n        this.saved.0\n    }\n}\n\nimpl<'a, T> RwLockWriteGuard<'a, T> {\n    pub fn downgrade(this: Self) -> RwLockReadGuard<'a, T> {\n        RwLockReadGuard {\n            inner: parking_lot::RwLockWriteGuard::downgrade(this.inner),\n            saved: this.saved,\n        }\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for RwLock<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\nimpl<'a, T> ops::Deref for RwLockReadGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> ops::Deref for RwLockWriteGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<'a, T> ops::DerefMut for RwLockWriteGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.deref_mut()\n    }\n}\n\n/// Locks can be acquired in the order indicated by their ranks.\n#[test]\nfn permitted() {\n    use super::rank;\n\n    let lock1 = Mutex::new(rank::PAWN, ());\n    let lock2 = Mutex::new(rank::ROOK, ());\n\n    let _guard1 = lock1.lock();\n    let _guard2 = lock2.lock();\n}\n\n/// Locks can only be acquired in the order indicated by their ranks.\n#[test]\n#[should_panic(expected = \"Locking pawn after locking rook\")]\nfn forbidden_unrelated() {\n    use super::rank;\n\n    let lock1 = Mutex::new(rank::ROOK, ());\n    let lock2 = Mutex::new(rank::PAWN, ());\n\n    let _guard1 = lock1.lock();\n    let _guard2 = lock2.lock();\n}\n\n/// Lock acquisitions can't skip ranks.\n///\n/// These two locks *could* be acquired in this order, but only if other locks\n/// are acquired in between them. Skipping ranks isn't allowed.\n#[test]\n#[should_panic(expected = \"Locking knight after locking pawn\")]\nfn forbidden_skip() {\n    use super::rank;\n\n    let lock1 = Mutex::new(rank::PAWN, ());\n    let lock2 = Mutex::new(rank::KNIGHT, ());\n\n    let _guard1 = lock1.lock();\n    let _guard2 = lock2.lock();\n}\n\n/// Locks can be acquired and released in a stack-like order.\n#[test]\nfn stack_like() {\n    use super::rank;\n\n    let lock1 = Mutex::new(rank::PAWN, ());\n    let lock2 = Mutex::new(rank::ROOK, ());\n    let lock3 = Mutex::new(rank::BISHOP, ());\n\n    let guard1 = lock1.lock();\n    let guard2 = lock2.lock();\n    drop(guard2);\n\n    let guard3 = lock3.lock();\n    drop(guard3);\n    drop(guard1);\n}\n\n/// Locks can only be acquired and released in a stack-like order.\n#[test]\n#[should_panic(expected = \"Lock not released in stacking order\")]\nfn non_stack_like() {\n    use super::rank;\n\n    let lock1 = Mutex::new(rank::PAWN, ());\n    let lock2 = Mutex::new(rank::ROOK, ());\n\n    let guard1 = lock1.lock();\n    let guard2 = lock2.lock();\n\n    // Avoid a double panic from dropping this while unwinding due to the panic\n    // we're testing for.\n    core::mem::forget(guard2);\n\n    drop(guard1);\n}\n"
  },
  {
    "path": "wgpu-core/src/lock/vanilla.rs",
    "content": "//! Plain, uninstrumented wrappers around [`parking_lot`] lock types.\n//!\n//! These definitions are used when no particular lock instrumentation\n//! Cargo feature is selected.\n\nuse core::{fmt, ops};\n\nuse crate::lock::rank::LockRank;\n\npub struct RankData;\n\n/// A plain wrapper around [`parking_lot::Mutex`].\n///\n/// This is just like [`parking_lot::Mutex`], except that our [`new`]\n/// method takes a rank, indicating where the new mutex should sit in\n/// `wgpu-core`'s lock ordering. The rank is ignored.\n///\n/// See the [`lock`] module documentation for other wrappers.\n///\n/// [`new`]: Mutex::new\n/// [`lock`]: crate::lock\npub struct Mutex<T>(parking_lot::Mutex<T>);\n\n/// A guard produced by locking [`Mutex`].\n///\n/// This is just a wrapper around a [`parking_lot::MutexGuard`].\npub struct MutexGuard<'a, T>(parking_lot::MutexGuard<'a, T>);\n\nimpl<T> Mutex<T> {\n    pub fn new(_rank: LockRank, value: T) -> Mutex<T> {\n        Mutex(parking_lot::Mutex::new(value))\n    }\n\n    pub fn lock(&self) -> MutexGuard<'_, T> {\n        MutexGuard(self.0.lock())\n    }\n\n    pub fn into_inner(self) -> T {\n        self.0.into_inner()\n    }\n}\n\nimpl<'a, T> ops::Deref for MutexGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\nimpl<'a, T> ops::DerefMut for MutexGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.0.deref_mut()\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for Mutex<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n/// A plain wrapper around [`parking_lot::RwLock`].\n///\n/// This is just like [`parking_lot::RwLock`], except that our [`new`]\n/// method takes a rank, indicating where the new mutex should sit in\n/// `wgpu-core`'s lock ordering. The rank is ignored.\n///\n/// See the [`lock`] module documentation for other wrappers.\n///\n/// [`new`]: RwLock::new\n/// [`lock`]: crate::lock\npub struct RwLock<T>(parking_lot::RwLock<T>);\n\n/// A read guard produced by locking [`RwLock`] as a reader.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`].\npub struct RwLockReadGuard<'a, T>(parking_lot::RwLockReadGuard<'a, T>);\n\n/// A write guard produced by locking [`RwLock`] as a writer.\n///\n/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`].\npub struct RwLockWriteGuard<'a, T>(parking_lot::RwLockWriteGuard<'a, T>);\n\nimpl<T> RwLock<T> {\n    pub fn new(_rank: LockRank, value: T) -> RwLock<T> {\n        RwLock(parking_lot::RwLock::new(value))\n    }\n\n    pub fn read(&self) -> RwLockReadGuard<'_, T> {\n        RwLockReadGuard(self.0.read())\n    }\n\n    pub fn write(&self) -> RwLockWriteGuard<'_, T> {\n        RwLockWriteGuard(self.0.write())\n    }\n\n    /// Force an read-unlock operation on this lock.\n    ///\n    /// Safety:\n    /// - A read lock must be held which is not held by a guard.\n    pub unsafe fn force_unlock_read(&self, _data: RankData) {\n        unsafe { self.0.force_unlock_read() };\n    }\n}\n\nimpl<'a, T> RwLockReadGuard<'a, T> {\n    // Forget the read guard, leaving the lock in a locked state with no guard.\n    //\n    // Equivalent to std::mem::forget, but preserves the information about the lock\n    // rank.\n    pub fn forget(this: Self) -> RankData {\n        core::mem::forget(this.0);\n\n        RankData\n    }\n}\n\nimpl<'a, T> RwLockWriteGuard<'a, T> {\n    pub fn downgrade(this: Self) -> RwLockReadGuard<'a, T> {\n        RwLockReadGuard(parking_lot::RwLockWriteGuard::downgrade(this.0))\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for RwLock<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nimpl<'a, T> ops::Deref for RwLockReadGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\nimpl<'a, T> ops::Deref for RwLockWriteGuard<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\nimpl<'a, T> ops::DerefMut for RwLockWriteGuard<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.0.deref_mut()\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/pipeline.rs",
    "content": "use alloc::{\n    borrow::Cow,\n    boxed::Box,\n    string::{String, ToString as _},\n    sync::Arc,\n    vec::Vec,\n};\nuse core::{marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32};\n\nuse arrayvec::ArrayVec;\nuse naga::error::ShaderError;\nuse thiserror::Error;\nuse wgt::error::{ErrorType, WebGpuError};\n\npub use crate::pipeline_cache::PipelineCacheValidationError;\nuse crate::{\n    binding_model::{\n        BindGroupLayout, CreateBindGroupLayoutError, CreatePipelineLayoutError,\n        GetBindGroupLayoutError, PipelineLayout,\n    },\n    command::ColorAttachmentError,\n    device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},\n    id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},\n    resource::{InvalidResourceError, Labeled, TrackingData},\n    resource_log, validation, Label,\n};\n\n/// Information about buffer bindings, which\n/// is validated against the shader (and pipeline)\n/// at draw time as opposed to initialization time.\n#[derive(Debug, Default)]\npub(crate) struct LateSizedBufferGroup {\n    // The order has to match `BindGroup::late_buffer_binding_sizes`.\n    pub(crate) shader_sizes: Vec<wgt::BufferAddress>,\n}\n\n#[allow(clippy::large_enum_variant)]\npub enum ShaderModuleSource<'a> {\n    #[cfg(feature = \"wgsl\")]\n    Wgsl(Cow<'a, str>),\n    #[cfg(feature = \"glsl\")]\n    Glsl(Cow<'a, str>, naga::front::glsl::Options),\n    #[cfg(feature = \"spirv\")]\n    SpirV(Cow<'a, [u32]>, naga::front::spv::Options),\n    Naga(Cow<'static, naga::Module>),\n    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it\n    /// could be the last one active.\n    #[doc(hidden)]\n    Dummy(PhantomData<&'a ()>),\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct ShaderModuleDescriptor<'a> {\n    pub label: Label<'a>,\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub runtime_checks: wgt::ShaderRuntimeChecks,\n}\n\npub type ShaderModuleDescriptorPassthrough<'a> =\n    wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;\n\n#[derive(Debug)]\npub struct ShaderModule {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) interface: Option<validation::Interface>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n}\n\nimpl Drop for ShaderModule {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_shader_module(raw);\n        }\n    }\n}\n\ncrate::impl_resource_type!(ShaderModule);\ncrate::impl_labeled!(ShaderModule);\ncrate::impl_parent_device!(ShaderModule);\ncrate::impl_storage_item!(ShaderModule);\n\nimpl ShaderModule {\n    pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {\n        self.raw.as_ref()\n    }\n\n    pub(crate) fn finalize_entry_point_name(\n        &self,\n        stage: naga::ShaderStage,\n        entry_point: Option<&str>,\n    ) -> Result<String, validation::StageError> {\n        match &self.interface {\n            Some(interface) => interface.finalize_entry_point_name(stage, entry_point),\n            None => entry_point\n                .map(|ep| ep.to_string())\n                .ok_or(validation::StageError::NoEntryPointFound),\n        }\n    }\n}\n\n//Note: `Clone` would require `WithSpan: Clone`.\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateShaderModuleError {\n    #[cfg(feature = \"wgsl\")]\n    #[error(transparent)]\n    Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),\n    #[cfg(feature = \"glsl\")]\n    #[error(transparent)]\n    ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),\n    #[cfg(feature = \"spirv\")]\n    #[error(transparent)]\n    ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),\n    #[error(\"Failed to generate the backend-specific code\")]\n    Generation,\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\n        \"Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}.\"\n    )]\n    InvalidGroupIndex {\n        bind: naga::ResourceBinding,\n        group: u32,\n        limit: u32,\n    },\n    #[error(\"Generic shader passthrough does not contain any code compatible with this backend.\")]\n    NotCompiledForBackend,\n}\n\nimpl WebGpuError for CreateShaderModuleError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n\n            Self::Generation => ErrorType::Internal,\n\n            Self::Validation(..) | Self::InvalidGroupIndex { .. } => ErrorType::Validation,\n            #[cfg(feature = \"wgsl\")]\n            Self::Parsing(..) => ErrorType::Validation,\n            #[cfg(feature = \"glsl\")]\n            Self::ParsingGlsl(..) => ErrorType::Validation,\n            #[cfg(feature = \"spirv\")]\n            Self::ParsingSpirV(..) => ErrorType::Validation,\n            Self::NotCompiledForBackend => ErrorType::Validation,\n        }\n    }\n}\n\n/// Describes a programmable pipeline stage.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct ProgrammableStageDescriptor<'a, SM = ShaderModuleId> {\n    /// The compiled shader module for this stage.\n    pub module: SM,\n    /// The name of the entry point in the compiled shader. The name is selected using the\n    /// following logic:\n    ///\n    /// * If `Some(name)` is specified, there must be a function with this name in the shader.\n    /// * If a single entry point associated with this stage must be in the shader, then proceed as\n    ///   if `Some(…)` was specified with that entry point's name.\n    pub entry_point: Option<Cow<'a, str>>,\n    /// Specifies the values of pipeline-overridable constants in the shader module.\n    ///\n    /// If an `@id` attribute was specified on the declaration,\n    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,\n    /// the key must be the constant's identifier name.\n    ///\n    /// The value may represent any of WGSL's concrete scalar types.\n    pub constants: naga::back::PipelineConstants,\n    /// Whether workgroup scoped memory will be initialized with zero values for this stage.\n    ///\n    /// This is required by the WebGPU spec, but may have overhead which can be avoided\n    /// for cross-platform applications\n    pub zero_initialize_workgroup_memory: bool,\n}\n\n/// cbindgen:ignore\npub type ResolvedProgrammableStageDescriptor<'a> =\n    ProgrammableStageDescriptor<'a, Arc<ShaderModule>>;\n\n/// Number of implicit bind groups derived at pipeline creation.\npub type ImplicitBindGroupCount = u8;\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ImplicitLayoutError {\n    #[error(\"Unable to reflect the shader {0:?} interface\")]\n    ReflectionError(wgt::ShaderStages),\n    #[error(transparent)]\n    BindGroup(#[from] CreateBindGroupLayoutError),\n    #[error(transparent)]\n    Pipeline(#[from] CreatePipelineLayoutError),\n    #[error(\"Unable to create implicit pipeline layout from passthrough shader stage: {0:?}\")]\n    Passthrough(wgt::ShaderStages),\n}\n\nimpl WebGpuError for ImplicitLayoutError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::ReflectionError(_) => ErrorType::Validation,\n            Self::BindGroup(e) => e.webgpu_error_type(),\n            Self::Pipeline(e) => e.webgpu_error_type(),\n            Self::Passthrough(_) => ErrorType::Validation,\n        }\n    }\n}\n\n/// Describes a compute pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct ComputePipelineDescriptor<\n    'a,\n    PLL = PipelineLayoutId,\n    SM = ShaderModuleId,\n    PLC = PipelineCacheId,\n> {\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    pub layout: Option<PLL>,\n    /// The compiled compute stage and its entry point.\n    pub stage: ProgrammableStageDescriptor<'a, SM>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<PLC>,\n}\n\n/// cbindgen:ignore\npub type ResolvedComputePipelineDescriptor<'a> =\n    ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateComputePipelineError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Unable to derive an implicit layout\")]\n    Implicit(#[from] ImplicitLayoutError),\n    #[error(\"Error matching shader requirements against the pipeline\")]\n    Stage(#[from] validation::StageError),\n    #[error(\"Internal error: {0}\")]\n    Internal(String),\n    #[error(\"Pipeline constant error: {0}\")]\n    PipelineConstants(String),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for CreateComputePipelineError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            Self::Implicit(e) => e.webgpu_error_type(),\n            Self::Stage(e) => e.webgpu_error_type(),\n            Self::Internal(_) => ErrorType::Internal,\n            Self::PipelineConstants(_) => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct ComputePipeline {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,\n    pub(crate) layout: Arc<PipelineLayout>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) _shader_module: Arc<ShaderModule>,\n    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n}\n\nimpl Drop for ComputePipeline {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_compute_pipeline(raw);\n        }\n    }\n}\n\ncrate::impl_resource_type!(ComputePipeline);\ncrate::impl_labeled!(ComputePipeline);\ncrate::impl_parent_device!(ComputePipeline);\ncrate::impl_storage_item!(ComputePipeline);\ncrate::impl_trackable!(ComputePipeline);\n\nimpl ComputePipeline {\n    pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {\n        self.raw.as_ref()\n    }\n\n    pub fn get_bind_group_layout(\n        self: &Arc<Self>,\n        index: u32,\n    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {\n        self.layout.get_bind_group_layout(index)\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreatePipelineCacheError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Pipeline cache validation failed\")]\n    Validation(#[from] PipelineCacheValidationError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n}\n\nimpl WebGpuError for CreatePipelineCacheError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::Validation(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct PipelineCache {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,\n    pub(crate) device: Arc<Device>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n}\n\nimpl Drop for PipelineCache {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_pipeline_cache(raw);\n        }\n    }\n}\n\ncrate::impl_resource_type!(PipelineCache);\ncrate::impl_labeled!(PipelineCache);\ncrate::impl_parent_device!(PipelineCache);\ncrate::impl_storage_item!(PipelineCache);\n\nimpl PipelineCache {\n    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {\n        self.raw.as_ref()\n    }\n}\n\n/// Describes how the vertex buffer is interpreted.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"serde\", serde(rename_all = \"camelCase\"))]\npub struct VertexBufferLayout<'a> {\n    /// The stride, in bytes, between elements of this buffer.\n    pub array_stride: wgt::BufferAddress,\n    /// How often this vertex buffer is \"stepped\" forward.\n    pub step_mode: wgt::VertexStepMode,\n    /// The list of attributes which comprise a single vertex.\n    pub attributes: Cow<'a, [wgt::VertexAttribute]>,\n}\n\n/// A null vertex buffer layout that may be placed in unused slots.\nimpl Default for VertexBufferLayout<'_> {\n    fn default() -> Self {\n        Self {\n            array_stride: Default::default(),\n            step_mode: Default::default(),\n            attributes: Cow::Borrowed(&[]),\n        }\n    }\n}\n\n/// Describes the vertex process in a render pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct VertexState<'a, SM = ShaderModuleId> {\n    /// The compiled vertex stage and its entry point.\n    pub stage: ProgrammableStageDescriptor<'a, SM>,\n    /// The format of any vertex buffers used with this pipeline.\n    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,\n}\n\n/// cbindgen:ignore\npub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;\n\n/// Describes fragment processing in a render pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct FragmentState<'a, SM = ShaderModuleId> {\n    /// The compiled fragment stage and its entry point.\n    pub stage: ProgrammableStageDescriptor<'a, SM>,\n    /// The effect of draw calls on the color aspect of the output target.\n    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,\n}\n\n/// cbindgen:ignore\npub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;\n\n/// Describes the task shader in a mesh shader pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct TaskState<'a, SM = ShaderModuleId> {\n    /// The compiled task stage and its entry point.\n    pub stage: ProgrammableStageDescriptor<'a, SM>,\n}\n\npub type ResolvedTaskState<'a> = TaskState<'a, Arc<ShaderModule>>;\n\n/// Describes the mesh shader in a mesh shader pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct MeshState<'a, SM = ShaderModuleId> {\n    /// The compiled mesh stage and its entry point.\n    pub stage: ProgrammableStageDescriptor<'a, SM>,\n}\n\npub type ResolvedMeshState<'a> = MeshState<'a, Arc<ShaderModule>>;\n\n/// Describes a vertex processor for either a conventional or mesh shading\n/// pipeline architecture.\n///\n/// This is not a public API. It is for use by `player` only. The public APIs\n/// are [`VertexState`], [`TaskState`], and [`MeshState`].\n///\n/// cbindgen:ignore\n#[doc(hidden)]\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> {\n    Vertex(VertexState<'a, SM>),\n    Mesh(Option<TaskState<'a, SM>>, MeshState<'a, SM>),\n}\n\n/// Describes a render (graphics) pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct RenderPipelineDescriptor<\n    'a,\n    PLL = PipelineLayoutId,\n    SM = ShaderModuleId,\n    PLC = PipelineCacheId,\n> {\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    pub layout: Option<PLL>,\n    /// The vertex processing state for this pipeline.\n    pub vertex: VertexState<'a, SM>,\n    /// The properties of the pipeline at the primitive assembly and rasterization level.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub primitive: wgt::PrimitiveState,\n    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub depth_stencil: Option<wgt::DepthStencilState>,\n    /// The multi-sampling properties of the pipeline.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub multisample: wgt::MultisampleState,\n    /// The fragment processing state for this pipeline.\n    pub fragment: Option<FragmentState<'a, SM>>,\n    /// If the pipeline will be used with a multiview render pass, this indicates how many array\n    /// layers the attachments will have.\n    pub multiview_mask: Option<NonZeroU32>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<PLC>,\n}\n/// Describes a mesh shader pipeline.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct MeshPipelineDescriptor<\n    'a,\n    PLL = PipelineLayoutId,\n    SM = ShaderModuleId,\n    PLC = PipelineCacheId,\n> {\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    pub layout: Option<PLL>,\n    /// The task processing state for this pipeline.\n    pub task: Option<TaskState<'a, SM>>,\n    /// The mesh processing state for this pipeline\n    pub mesh: MeshState<'a, SM>,\n    /// The properties of the pipeline at the primitive assembly and rasterization level.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub primitive: wgt::PrimitiveState,\n    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub depth_stencil: Option<wgt::DepthStencilState>,\n    /// The multi-sampling properties of the pipeline.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub multisample: wgt::MultisampleState,\n    /// The fragment processing state for this pipeline.\n    pub fragment: Option<FragmentState<'a, SM>>,\n    /// If the pipeline will be used with a multiview render pass, this indicates how many array\n    /// layers the attachments will have.\n    pub multiview: Option<NonZeroU32>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<PLC>,\n}\n\n/// Describes a render (graphics) pipeline, with either conventional or mesh\n/// shading architecture.\n///\n/// This is not a public API. It is for use by `player` only. The public APIs\n/// are [`RenderPipelineDescriptor`] and [`MeshPipelineDescriptor`].\n///\n/// cbindgen:ignore\n#[doc(hidden)]\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct GeneralRenderPipelineDescriptor<\n    'a,\n    PLL = PipelineLayoutId,\n    SM = ShaderModuleId,\n    PLC = PipelineCacheId,\n> {\n    pub label: Label<'a>,\n    /// The layout of bind groups for this pipeline.\n    pub layout: Option<PLL>,\n    /// The vertex processing state for this pipeline.\n    pub vertex: RenderPipelineVertexProcessor<'a, SM>,\n    /// The properties of the pipeline at the primitive assembly and rasterization level.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub primitive: wgt::PrimitiveState,\n    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub depth_stencil: Option<wgt::DepthStencilState>,\n    /// The multi-sampling properties of the pipeline.\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    pub multisample: wgt::MultisampleState,\n    /// The fragment processing state for this pipeline.\n    pub fragment: Option<FragmentState<'a, SM>>,\n    /// If the pipeline will be used with a multiview render pass, this indicates how many array\n    /// layers the attachments will have.\n    pub multiview_mask: Option<NonZeroU32>,\n    /// The pipeline cache to use when creating this pipeline.\n    pub cache: Option<PLC>,\n}\nimpl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>\n    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>\n{\n    fn from(value: RenderPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {\n        Self {\n            label: value.label,\n            layout: value.layout,\n            vertex: RenderPipelineVertexProcessor::Vertex(value.vertex),\n            primitive: value.primitive,\n            depth_stencil: value.depth_stencil,\n            multisample: value.multisample,\n            fragment: value.fragment,\n            multiview_mask: value.multiview_mask,\n            cache: value.cache,\n        }\n    }\n}\nimpl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>\n    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>\n{\n    fn from(value: MeshPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {\n        Self {\n            label: value.label,\n            layout: value.layout,\n            vertex: RenderPipelineVertexProcessor::Mesh(value.task, value.mesh),\n            primitive: value.primitive,\n            depth_stencil: value.depth_stencil,\n            multisample: value.multisample,\n            fragment: value.fragment,\n            multiview_mask: value.multiview,\n            cache: value.cache,\n        }\n    }\n}\n\n/// Not a public API. For use by `player` only.\n///\n/// cbindgen:ignore\npub type ResolvedGeneralRenderPipelineDescriptor<'a> =\n    GeneralRenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct PipelineCacheDescriptor<'a> {\n    pub label: Label<'a>,\n    pub data: Option<Cow<'a, [u8]>>,\n    pub fallback: bool,\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ColorStateError {\n    #[error(\"Format {0:?} is not renderable\")]\n    FormatNotRenderable(wgt::TextureFormat),\n    #[error(\"Format {0:?} is not blendable\")]\n    FormatNotBlendable(wgt::TextureFormat),\n    #[error(\"Format {0:?} does not have a color aspect\")]\n    FormatNotColor(wgt::TextureFormat),\n    #[error(\"Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.\")]\n    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),\n    #[error(\"Output format {pipeline} is incompatible with the shader {shader}\")]\n    IncompatibleFormat {\n        pipeline: validation::NumericType,\n        shader: validation::NumericType,\n    },\n    #[error(\"Invalid write mask {0:?}\")]\n    InvalidWriteMask(wgt::ColorWrites),\n    #[error(\"Using the blend factor {factor:?} for render target {target} is not possible. Only the first render target may be used when dual-source blending.\")]\n    BlendFactorOnUnsupportedTarget {\n        factor: wgt::BlendFactor,\n        target: u32,\n    },\n    #[error(\n        \"Blend factor {factor:?} for render target {target} is not valid. Blend factor must be `one` when using min/max blend operations.\"\n    )]\n    InvalidMinMaxBlendFactor {\n        factor: wgt::BlendFactor,\n        target: u32,\n    },\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum DepthStencilStateError {\n    #[error(\"Format {0:?} is not renderable\")]\n    FormatNotRenderable(wgt::TextureFormat),\n    #[error(\"Format {0:?} is not a depth/stencil format\")]\n    FormatNotDepthOrStencil(wgt::TextureFormat),\n    #[error(\"Format {0:?} does not have a depth aspect, but depth test/write is enabled\")]\n    FormatNotDepth(wgt::TextureFormat),\n    #[error(\"Format {0:?} does not have a stencil aspect, but stencil test/write is enabled\")]\n    FormatNotStencil(wgt::TextureFormat),\n    #[error(\"Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.\")]\n    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),\n    #[error(\"Depth bias is not compatible with non-triangle topology {0:?}\")]\n    DepthBiasWithIncompatibleTopology(wgt::PrimitiveTopology),\n    #[error(\"Depth compare function must be specified for depth format {0:?}\")]\n    MissingDepthCompare(wgt::TextureFormat),\n    #[error(\"Depth write enabled must be specified for depth format {0:?}\")]\n    MissingDepthWriteEnabled(wgt::TextureFormat),\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateRenderPipelineError {\n    #[error(transparent)]\n    ColorAttachment(#[from] ColorAttachmentError),\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Unable to derive an implicit layout\")]\n    Implicit(#[from] ImplicitLayoutError),\n    #[error(\"Color state [{0}] is invalid\")]\n    ColorState(u8, #[source] ColorStateError),\n    #[error(\"Depth/stencil state is invalid\")]\n    DepthStencilState(#[from] DepthStencilStateError),\n    #[error(\"Invalid sample count {0}\")]\n    InvalidSampleCount(u32),\n    #[error(\"The number of vertex buffers {given} exceeds the limit {limit}\")]\n    TooManyVertexBuffers { given: u32, limit: u32 },\n    #[error(\"The total number of vertex attributes {given} exceeds the limit {limit}\")]\n    TooManyVertexAttributes { given: u32, limit: u32 },\n    #[error(\"Vertex attribute location {given} must be less than limit {limit}\")]\n    VertexAttributeLocationTooLarge { given: u32, limit: u32 },\n    #[error(\"Vertex buffer {index} stride {given} exceeds the limit {limit}\")]\n    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },\n    #[error(\"Vertex attribute at location {location} stride {given} exceeds the limit {limit}\")]\n    VertexAttributeStrideTooLarge {\n        location: wgt::ShaderLocation,\n        given: u32,\n        limit: u32,\n    },\n    #[error(\"Vertex buffer {index} stride {stride} does not respect `VERTEX_ALIGNMENT`\")]\n    UnalignedVertexStride {\n        index: u32,\n        stride: wgt::BufferAddress,\n    },\n    #[error(\"Vertex attribute at location {location} has invalid offset {offset}\")]\n    InvalidVertexAttributeOffset {\n        location: wgt::ShaderLocation,\n        offset: wgt::BufferAddress,\n    },\n    #[error(\"Two or more vertex attributes were assigned to the same location in the shader: {0}\")]\n    ShaderLocationClash(u32),\n    #[error(\"Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}\")]\n    StripIndexFormatForNonStripTopology {\n        strip_index_format: Option<wgt::IndexFormat>,\n        topology: wgt::PrimitiveTopology,\n    },\n    #[error(\"Conservative Rasterization is only supported for wgt::PolygonMode::Fill\")]\n    ConservativeRasterizationNonFillPolygonMode,\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(\"Error matching {stage:?} shader requirements against the pipeline\")]\n    Stage {\n        stage: wgt::ShaderStages,\n        #[source]\n        error: validation::StageError,\n    },\n    #[error(\"Internal error in {stage:?} shader: {error}\")]\n    Internal {\n        stage: wgt::ShaderStages,\n        error: String,\n    },\n    #[error(\"Pipeline constant error in {stage:?} shader: {error}\")]\n    PipelineConstants {\n        stage: wgt::ShaderStages,\n        error: String,\n    },\n    #[error(\"In the provided shader, the type given for group {group} binding {binding} has a size of {size}. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes.\")]\n    UnalignedShader { group: u32, binding: u32, size: u64 },\n    #[error(\"Dual-source blending requires exactly one color target, but {count} color targets are present\")]\n    DualSourceBlendingWithMultipleColorTargets { count: usize },\n    #[error(\"{}\", concat!(\n        \"At least one color attachment or depth-stencil attachment was expected, \",\n        \"but no render target for the pipeline was specified.\"\n    ))]\n    NoTargetSpecified,\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n}\n\nimpl WebGpuError for CreateRenderPipelineError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n\n            Self::Internal { .. } => ErrorType::Internal,\n\n            Self::ColorAttachment(_)\n            | Self::Implicit(_)\n            | Self::ColorState(_, _)\n            | Self::DepthStencilState(_)\n            | Self::InvalidSampleCount(_)\n            | Self::TooManyVertexBuffers { .. }\n            | Self::TooManyVertexAttributes { .. }\n            | Self::VertexAttributeLocationTooLarge { .. }\n            | Self::VertexStrideTooLarge { .. }\n            | Self::UnalignedVertexStride { .. }\n            | Self::InvalidVertexAttributeOffset { .. }\n            | Self::ShaderLocationClash(_)\n            | Self::StripIndexFormatForNonStripTopology { .. }\n            | Self::ConservativeRasterizationNonFillPolygonMode\n            | Self::Stage { .. }\n            | Self::UnalignedShader { .. }\n            | Self::DualSourceBlendingWithMultipleColorTargets { .. }\n            | Self::NoTargetSpecified\n            | Self::PipelineConstants { .. }\n            | Self::VertexAttributeStrideTooLarge { .. } => ErrorType::Validation,\n        }\n    }\n}\n\nbitflags::bitflags! {\n    #[repr(transparent)]\n    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n    pub struct PipelineFlags: u32 {\n        const BLEND_CONSTANT = 1 << 0;\n        const STENCIL_REFERENCE = 1 << 1;\n        const WRITES_DEPTH = 1 << 2;\n        const WRITES_STENCIL = 1 << 3;\n    }\n}\n\n/// How a render pipeline will retrieve attributes from a particular vertex buffer.\n#[derive(Clone, Copy, Debug)]\npub struct VertexStep {\n    /// The byte stride in the buffer between one attribute value and the next.\n    pub stride: wgt::BufferAddress,\n\n    /// The byte size required to fit the last vertex in the stream.\n    pub last_stride: wgt::BufferAddress,\n\n    /// Whether the buffer is indexed by vertex number or instance number.\n    pub mode: wgt::VertexStepMode,\n}\n\nimpl Default for VertexStep {\n    fn default() -> Self {\n        Self {\n            stride: 0,\n            last_stride: 0,\n            mode: wgt::VertexStepMode::Vertex,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct RenderPipeline {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) layout: Arc<PipelineLayout>,\n    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,\n    pub(crate) pass_context: RenderPassContext,\n    pub(crate) flags: PipelineFlags,\n    pub(crate) topology: wgt::PrimitiveTopology,\n    pub(crate) strip_index_format: Option<wgt::IndexFormat>,\n    pub(crate) vertex_steps: Vec<VertexStep>,\n    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    /// Whether this is a mesh shader pipeline\n    pub(crate) is_mesh: bool,\n}\n\nimpl Drop for RenderPipeline {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_render_pipeline(raw);\n        }\n    }\n}\n\ncrate::impl_resource_type!(RenderPipeline);\ncrate::impl_labeled!(RenderPipeline);\ncrate::impl_parent_device!(RenderPipeline);\ncrate::impl_storage_item!(RenderPipeline);\ncrate::impl_trackable!(RenderPipeline);\n\nimpl RenderPipeline {\n    pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {\n        self.raw.as_ref()\n    }\n\n    pub fn get_bind_group_layout(\n        self: &Arc<Self>,\n        index: u32,\n    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {\n        self.layout.get_bind_group_layout(index)\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/pipeline_cache.rs",
    "content": "use thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    AdapterInfo,\n};\n\npub const HEADER_LENGTH: usize = size_of::<PipelineCacheHeader>();\n\n#[derive(Debug, PartialEq, Eq, Clone, Error)]\n#[non_exhaustive]\npub enum PipelineCacheValidationError {\n    #[error(\"The pipeline cache data was truncated\")]\n    Truncated,\n    #[error(\"The pipeline cache data was longer than recorded\")]\n    // TODO: Is it plausible that this would happen\n    Extended,\n    #[error(\"The pipeline cache data was corrupted (e.g. the hash didn't match)\")]\n    Corrupted,\n    #[error(\"The pipeline cacha data was out of date and so cannot be safely used\")]\n    Outdated,\n    #[error(\"The cache data was created for a different device\")]\n    DeviceMismatch,\n    #[error(\"Pipeline cacha data was created for a future version of wgpu\")]\n    Unsupported,\n}\n\nimpl PipelineCacheValidationError {\n    /// Could the error have been avoided?\n    /// That is, is there a mistake in user code interacting with the cache\n    pub fn was_avoidable(&self) -> bool {\n        match self {\n            PipelineCacheValidationError::DeviceMismatch => true,\n            PipelineCacheValidationError::Truncated\n            | PipelineCacheValidationError::Unsupported\n            | PipelineCacheValidationError::Extended\n            // It's unusual, but not implausible, to be downgrading wgpu\n            | PipelineCacheValidationError::Outdated\n            | PipelineCacheValidationError::Corrupted => false,\n        }\n    }\n}\n\nimpl WebGpuError for PipelineCacheValidationError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n/// Validate the data in a pipeline cache\npub fn validate_pipeline_cache<'d>(\n    cache_data: &'d [u8],\n    adapter: &AdapterInfo,\n    validation_key: [u8; 16],\n) -> Result<&'d [u8], PipelineCacheValidationError> {\n    let adapter_key = adapter_key(adapter)?;\n    let Some((header, remaining_data)) = PipelineCacheHeader::read(cache_data) else {\n        return Err(PipelineCacheValidationError::Truncated);\n    };\n    if header.magic != MAGIC {\n        return Err(PipelineCacheValidationError::Corrupted);\n    }\n    if header.header_version != HEADER_VERSION {\n        return Err(PipelineCacheValidationError::Outdated);\n    }\n    if header.cache_abi != ABI {\n        return Err(PipelineCacheValidationError::Outdated);\n    }\n    if header.backend != adapter.backend as u8 {\n        return Err(PipelineCacheValidationError::DeviceMismatch);\n    }\n    if header.adapter_key != adapter_key {\n        return Err(PipelineCacheValidationError::DeviceMismatch);\n    }\n    if header.validation_key != validation_key {\n        // If the validation key is wrong, that means that this device has changed\n        // in a way where the cache won't be compatible since the cache was made,\n        // so it is outdated\n        return Err(PipelineCacheValidationError::Outdated);\n    }\n    let data_size: usize = header\n        .data_size\n        .try_into()\n        // If the data was previously more than 4GiB, and we're still on a 32 bit system (ABI check, above)\n        // Then the data must be corrupted\n        .map_err(|_| PipelineCacheValidationError::Corrupted)?;\n    if remaining_data.len() < data_size {\n        return Err(PipelineCacheValidationError::Truncated);\n    }\n    if remaining_data.len() > data_size {\n        return Err(PipelineCacheValidationError::Extended);\n    }\n    if header.hash_space != HASH_SPACE_VALUE {\n        return Err(PipelineCacheValidationError::Corrupted);\n    }\n    Ok(remaining_data)\n}\n\npub fn add_cache_header(\n    in_region: &mut [u8],\n    data: &[u8],\n    adapter: &AdapterInfo,\n    validation_key: [u8; 16],\n) {\n    assert_eq!(in_region.len(), HEADER_LENGTH);\n    let header = PipelineCacheHeader {\n        adapter_key: adapter_key(adapter)\n            .expect(\"Called add_cache_header for an adapter which doesn't support cache data. This is a wgpu internal bug\"),\n        backend: adapter.backend as u8,\n        cache_abi: ABI,\n        magic: MAGIC,\n        header_version: HEADER_VERSION,\n        validation_key,\n        hash_space: HASH_SPACE_VALUE,\n        data_size: data\n            .len()\n            .try_into()\n            .expect(\"Cache larger than u64::MAX bytes\"),\n    };\n    header.write(in_region);\n}\n\nconst MAGIC: [u8; 8] = *b\"WGPUPLCH\";\nconst HEADER_VERSION: u32 = 1;\nconst ABI: u32 = size_of::<*const ()>() as u32;\n\n/// The value used to fill [`PipelineCacheHeader::hash_space`]\n///\n/// If we receive reports of pipeline cache data corruption which is not otherwise caught\n/// on a real device, it would be worth modifying this\n///\n/// Note that wgpu does not protect against malicious writes to e.g. a file used\n/// to store a pipeline cache.\n/// That is the responsibility of the end application, such as by using a\n/// private space.\nconst HASH_SPACE_VALUE: u64 = 0xFEDCBA9_876543210;\n\n#[repr(C)]\n#[derive(PartialEq, Eq)]\nstruct PipelineCacheHeader {\n    /// The magic header to ensure that we have the right file format\n    /// Has a value of MAGIC, as above\n    magic: [u8; 8],\n    // /// The total size of this header, in bytes\n    // header_size: u32,\n    /// The version of this wgpu header\n    /// Should be equal to HEADER_VERSION above\n    ///\n    /// This must always be the second item, after the value above\n    header_version: u32,\n    /// The number of bytes in the pointers of this ABI, because some drivers\n    /// have previously not distinguished between their 32 bit and 64 bit drivers\n    /// leading to Vulkan data corruption\n    cache_abi: u32,\n    /// The id for the backend in use, from [wgt::Backend]\n    backend: u8,\n    /// The key which identifiers the device/adapter.\n    /// This is used to validate that this pipeline cache (probably) was produced for\n    /// the expected device.\n    /// On Vulkan: it is a combination of vendor ID and device ID\n    adapter_key: [u8; 15],\n    /// A key used to validate that this device is still compatible with the cache\n    ///\n    /// This should e.g. contain driver version and/or intermediate compiler versions\n    validation_key: [u8; 16],\n    /// The length of the data which is sent to/recieved from the backend\n    data_size: u64,\n    /// Space reserved for a hash of the data in future\n    ///\n    /// We assume that your cache storage system will be relatively robust, and so\n    /// do not validate this hash\n    ///\n    /// Therefore, this will always have a value of [`HASH_SPACE_VALUE`]\n    hash_space: u64,\n}\n\nimpl PipelineCacheHeader {\n    fn read(data: &[u8]) -> Option<(PipelineCacheHeader, &[u8])> {\n        let mut reader = Reader {\n            data,\n            total_read: 0,\n        };\n        let magic = reader.read_array()?;\n        let header_version = reader.read_u32()?;\n        let cache_abi = reader.read_u32()?;\n        let backend = reader.read_byte()?;\n        let adapter_key = reader.read_array()?;\n        let validation_key = reader.read_array()?;\n        let data_size = reader.read_u64()?;\n        let data_hash = reader.read_u64()?;\n\n        assert_eq!(reader.total_read, size_of::<PipelineCacheHeader>());\n\n        Some((\n            PipelineCacheHeader {\n                magic,\n                header_version,\n                cache_abi,\n                backend,\n                adapter_key,\n                validation_key,\n                data_size,\n                hash_space: data_hash,\n            },\n            reader.data,\n        ))\n    }\n\n    fn write(&self, into: &mut [u8]) -> Option<()> {\n        let mut writer = Writer { data: into };\n        writer.write_array(&self.magic)?;\n        writer.write_u32(self.header_version)?;\n        writer.write_u32(self.cache_abi)?;\n        writer.write_byte(self.backend)?;\n        writer.write_array(&self.adapter_key)?;\n        writer.write_array(&self.validation_key)?;\n        writer.write_u64(self.data_size)?;\n        writer.write_u64(self.hash_space)?;\n\n        assert_eq!(writer.data.len(), 0);\n        Some(())\n    }\n}\n\nfn adapter_key(adapter: &AdapterInfo) -> Result<[u8; 15], PipelineCacheValidationError> {\n    match adapter.backend {\n        wgt::Backend::Vulkan => {\n            // If these change size, the header format needs to change\n            // We set the type explicitly so this won't compile in that case\n            let v: [u8; 4] = adapter.vendor.to_be_bytes();\n            let d: [u8; 4] = adapter.device.to_be_bytes();\n            let adapter = [\n                255, 255, 255, v[0], v[1], v[2], v[3], d[0], d[1], d[2], d[3], 255, 255, 255, 255,\n            ];\n            Ok(adapter)\n        }\n        _ => Err(PipelineCacheValidationError::Unsupported),\n    }\n}\n\nstruct Reader<'a> {\n    data: &'a [u8],\n    total_read: usize,\n}\n\nimpl<'a> Reader<'a> {\n    fn read_byte(&mut self) -> Option<u8> {\n        let res = *self.data.first()?;\n        self.total_read += 1;\n        self.data = &self.data[1..];\n        Some(res)\n    }\n    fn read_array<const N: usize>(&mut self) -> Option<[u8; N]> {\n        // Only greater than because we're indexing fenceposts, not items\n        if N > self.data.len() {\n            return None;\n        }\n        let (start, data) = self.data.split_at(N);\n        self.total_read += N;\n        self.data = data;\n        Some(start.try_into().expect(\"off-by-one-error in array size\"))\n    }\n\n    // fn read_u16(&mut self) -> Option<u16> {\n    //     self.read_array().map(u16::from_be_bytes)\n    // }\n    fn read_u32(&mut self) -> Option<u32> {\n        self.read_array().map(u32::from_be_bytes)\n    }\n    fn read_u64(&mut self) -> Option<u64> {\n        self.read_array().map(u64::from_be_bytes)\n    }\n}\n\nstruct Writer<'a> {\n    data: &'a mut [u8],\n}\n\nimpl<'a> Writer<'a> {\n    fn write_byte(&mut self, byte: u8) -> Option<()> {\n        self.write_array(&[byte])\n    }\n    fn write_array<const N: usize>(&mut self, array: &[u8; N]) -> Option<()> {\n        // Only greater than because we're indexing fenceposts, not items\n        if N > self.data.len() {\n            return None;\n        }\n        let data = core::mem::take(&mut self.data);\n        let (start, data) = data.split_at_mut(N);\n        self.data = data;\n        start.copy_from_slice(array);\n        Some(())\n    }\n\n    // fn write_u16(&mut self, value: u16) -> Option<()> {\n    //     self.write_array(&value.to_be_bytes())\n    // }\n    fn write_u32(&mut self, value: u32) -> Option<()> {\n        self.write_array(&value.to_be_bytes())\n    }\n    fn write_u64(&mut self, value: u64) -> Option<()> {\n        self.write_array(&value.to_be_bytes())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use alloc::{string::String, vec::Vec};\n    use wgt::AdapterInfo;\n\n    use crate::pipeline_cache::{PipelineCacheValidationError as E, HEADER_LENGTH};\n\n    use super::ABI;\n\n    // Assert the correct size\n    const _: [(); HEADER_LENGTH] = [(); 64];\n\n    const ADAPTER: AdapterInfo = AdapterInfo {\n        name: String::new(),\n        vendor: 0x0002_FEED,\n        device: 0xFEFE_FEFE,\n        device_type: wgt::DeviceType::Other,\n        device_pci_bus_id: String::new(),\n        driver: String::new(),\n        driver_info: String::new(),\n        backend: wgt::Backend::Vulkan,\n        subgroup_min_size: 32,\n        subgroup_max_size: 32,\n        transient_saves_memory: true,\n    };\n\n    // IMPORTANT: If these tests fail, then you MUST increment HEADER_VERSION\n    const VALIDATION_KEY: [u8; 16] = u128::to_be_bytes(0xFFFFFFFF_FFFFFFFF_88888888_88888888);\n    #[test]\n    fn written_header() {\n        let mut result = [0; HEADER_LENGTH];\n        super::add_cache_header(&mut result, &[], &ADAPTER, VALIDATION_KEY);\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let expected = cache.into_iter().flatten().collect::<Vec<u8>>();\n\n        assert_eq!(result.as_slice(), expected.as_slice());\n    }\n\n    #[test]\n    fn valid_data() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let expected: &[u8] = &[];\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Ok(expected));\n    }\n    #[test]\n    fn invalid_magic() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"NOT_WGPU\",                                 // (Wrong) MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Corrupted));\n    }\n\n    #[test]\n    fn wrong_version() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 2, 0, 0, 0, ABI as u8],             // (wrong) Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Outdated));\n    }\n    #[test]\n    fn wrong_abi() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\", // MAGIC\n            // a 14 bit ABI is improbable\n            [0, 0, 0, 1, 0, 0, 0, 14],            // Version and (wrong) ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED], // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(), // Validation key\n            0x88888888_88888888u64.to_be_bytes(), // Validation key\n            0x0u64.to_be_bytes(),                 // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(), // Header\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Outdated));\n    }\n\n    #[test]\n    fn wrong_backend() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [2, 255, 255, 255, 0, 2, 0xFE, 0xED],         // (wrong) Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::DeviceMismatch));\n    }\n    #[test]\n    fn wrong_adapter() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0x00],         // Backend and (wrong) Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::DeviceMismatch));\n    }\n    #[test]\n    fn wrong_validation() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_00000000u64.to_be_bytes(),         // (wrong) Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Outdated));\n    }\n    #[test]\n    fn too_little_data() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x064u64.to_be_bytes(),                       // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Truncated));\n    }\n    #[test]\n    fn not_no_data() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            100u64.to_be_bytes(),                         // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache\n            .into_iter()\n            .flatten()\n            .chain(core::iter::repeat_n(0u8, 100))\n            .collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        let expected: &[u8] = &[0; 100];\n        assert_eq!(validation_result, Ok(expected));\n    }\n    #[test]\n    fn too_much_data() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x064u64.to_be_bytes(),                       // Data size\n            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache\n            .into_iter()\n            .flatten()\n            .chain(core::iter::repeat_n(0u8, 200))\n            .collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Extended));\n    }\n    #[test]\n    fn wrong_hash() {\n        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [\n            *b\"WGPUPLCH\",                                 // MAGIC\n            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI\n            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key\n            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key\n            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key\n            0x88888888_88888888u64.to_be_bytes(),         // Validation key\n            0x0u64.to_be_bytes(),                         // Data size\n            0x00000000_00000000u64.to_be_bytes(),         // Hash\n        ];\n        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();\n        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);\n        assert_eq!(validation_result, Err(E::Corrupted));\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/pool.rs",
    "content": "use alloc::sync::{Arc, Weak};\nuse core::hash::Hash;\n\nuse hashbrown::{hash_map::Entry, HashMap};\nuse once_cell::sync::OnceCell;\n\nuse crate::lock::{rank, Mutex};\nuse crate::FastHashMap;\n\ntype SlotInner<V> = Weak<V>;\ntype ResourcePoolSlot<V> = Arc<OnceCell<SlotInner<V>>>;\n\npub struct ResourcePool<K, V> {\n    inner: Mutex<FastHashMap<K, ResourcePoolSlot<V>>>,\n}\n\nimpl<K: Clone + Eq + Hash, V> ResourcePool<K, V> {\n    pub fn new() -> Self {\n        Self {\n            inner: Mutex::new(rank::RESOURCE_POOL_INNER, HashMap::default()),\n        }\n    }\n\n    /// Get a resource from the pool with the given entry map, or create a new\n    /// one if it doesn't exist using the given constructor.\n    ///\n    /// Behaves such that only one resource will be created for each unique\n    /// entry map at any one time.\n    pub fn get_or_init<F, E>(&self, key: K, constructor: F) -> Result<Arc<V>, E>\n    where\n        F: FnOnce(K) -> Result<Arc<V>, E>,\n    {\n        // We can't prove at compile time that these will only ever be consumed once,\n        // so we need to do the check at runtime.\n        let mut key = Some(key);\n        let mut constructor = Some(constructor);\n\n        'race: loop {\n            let mut map_guard = self.inner.lock();\n\n            let entry = match map_guard.entry(key.clone().unwrap()) {\n                // An entry exists for this resource.\n                //\n                // We know that either:\n                // - The resource is still alive, and Weak::upgrade will succeed.\n                // - The resource is in the process of being dropped, and Weak::upgrade will fail.\n                //\n                // The entry will never be empty while the BGL is still alive.\n                Entry::Occupied(entry) => Arc::clone(entry.get()),\n                // No entry exists for this resource.\n                //\n                // We know that the resource is not alive, so we can create a new entry.\n                Entry::Vacant(entry) => Arc::clone(entry.insert(Arc::new(OnceCell::new()))),\n            };\n\n            drop(map_guard);\n\n            // Some other thread may beat us to initializing the entry, but OnceCell guarantees that only one thread\n            // will actually initialize the entry.\n            //\n            // We pass the strong reference outside of the closure to keep it alive while we're the only one keeping a reference to it.\n            let mut strong = None;\n            let weak = entry.get_or_try_init(|| {\n                let strong_inner = constructor.take().unwrap()(key.take().unwrap())?;\n                let weak = Arc::downgrade(&strong_inner);\n                strong = Some(strong_inner);\n                Ok(weak)\n            })?;\n\n            // If strong is Some, that means we just initialized the entry, so we can just return it.\n            if let Some(strong) = strong {\n                return Ok(strong);\n            }\n\n            // The entry was already initialized by someone else, so we need to try to upgrade it.\n            if let Some(strong) = weak.upgrade() {\n                // We succeed, the resource is still alive, just return that.\n                return Ok(strong);\n            }\n\n            // The resource is in the process of being dropped, because upgrade failed.\n            // The entry still exists in the map, but it points to nothing.\n            //\n            // We're in a race with the drop implementation of the resource,\n            //  so lets just go around again. When we go around again:\n            // - If the entry exists, we might need to go around a few more times.\n            // - If the entry doesn't exist, we'll create a new one.\n            continue 'race;\n        }\n    }\n\n    /// Remove the given entry map from the pool.\n    ///\n    /// Must *only* be called in the Drop impl of [`BindGroupLayout`].\n    ///\n    /// [`BindGroupLayout`]: crate::binding_model::BindGroupLayout\n    pub fn remove(&self, key: &K) {\n        let mut map_guard = self.inner.lock();\n\n        // Weak::upgrade will be failing long before this code is called. All threads trying to access the resource will be spinning,\n        // waiting for the entry to be removed. It is safe to remove the entry from the map.\n        map_guard.remove(key);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::{\n        sync::atomic::{AtomicU32, Ordering},\n        time::Duration,\n    };\n    use std::{eprintln, sync::Barrier, thread};\n\n    use super::*;\n\n    #[test]\n    fn deduplication() {\n        let pool = ResourcePool::<u32, u32>::new();\n\n        let mut counter = 0_u32;\n\n        let arc1 = pool\n            .get_or_init::<_, ()>(0, |key| {\n                counter += 1;\n                Ok(Arc::new(key))\n            })\n            .unwrap();\n\n        assert_eq!(*arc1, 0);\n        assert_eq!(counter, 1);\n\n        let arc2 = pool\n            .get_or_init::<_, ()>(0, |key| {\n                counter += 1;\n                Ok(Arc::new(key))\n            })\n            .unwrap();\n\n        assert!(Arc::ptr_eq(&arc1, &arc2));\n        assert_eq!(*arc2, 0);\n        assert_eq!(counter, 1);\n\n        drop(arc1);\n        drop(arc2);\n        pool.remove(&0);\n\n        let arc3 = pool\n            .get_or_init::<_, ()>(0, |key| {\n                counter += 1;\n                Ok(Arc::new(key))\n            })\n            .unwrap();\n\n        assert_eq!(*arc3, 0);\n        assert_eq!(counter, 2);\n    }\n\n    // Test name has \"2_threads\" in the name so nextest reserves two threads for it.\n    #[test]\n    fn concurrent_creation_2_threads() {\n        struct Resources {\n            pool: ResourcePool<u32, u32>,\n            counter: AtomicU32,\n            barrier: Barrier,\n        }\n\n        let resources = Arc::new(Resources {\n            pool: ResourcePool::<u32, u32>::new(),\n            counter: AtomicU32::new(0),\n            barrier: Barrier::new(2),\n        });\n\n        // Like all races, this is not inherently guaranteed to work, but in practice it should work fine.\n        //\n        // To validate the expected order of events, we've put print statements in the code, indicating when each thread is at a certain point.\n        // The output will look something like this if the test is working as expected:\n        //\n        // ```\n        // 0: prewait\n        // 1: prewait\n        // 1: postwait\n        // 0: postwait\n        // 1: init\n        // 1: postget\n        // 0: postget\n        // ```\n        fn thread_inner(idx: u8, resources: &Resources) -> Arc<u32> {\n            eprintln!(\"{idx}: prewait\");\n\n            // Once this returns, both threads should hit get_or_init at about the same time,\n            // allowing us to actually test concurrent creation.\n            //\n            // Like all races, this is not inherently guaranteed to work, but in practice it should work fine.\n            resources.barrier.wait();\n\n            eprintln!(\"{idx}: postwait\");\n\n            let ret = resources\n                .pool\n                .get_or_init::<_, ()>(0, |key| {\n                    eprintln!(\"{idx}: init\");\n\n                    // Simulate long running constructor, ensuring that both threads will be in get_or_init.\n                    thread::sleep(Duration::from_millis(250));\n\n                    resources.counter.fetch_add(1, Ordering::SeqCst);\n\n                    Ok(Arc::new(key))\n                })\n                .unwrap();\n\n            eprintln!(\"{idx}: postget\");\n\n            ret\n        }\n\n        let thread1 = thread::spawn({\n            let resource_clone = Arc::clone(&resources);\n            move || thread_inner(1, &resource_clone)\n        });\n\n        let arc0 = thread_inner(0, &resources);\n\n        assert_eq!(resources.counter.load(Ordering::Acquire), 1);\n\n        let arc1 = thread1.join().unwrap();\n\n        assert!(Arc::ptr_eq(&arc0, &arc1));\n    }\n\n    // Test name has \"2_threads\" in the name so nextest reserves two threads for it.\n    #[test]\n    fn create_while_drop_2_threads() {\n        struct Resources {\n            pool: ResourcePool<u32, u32>,\n            barrier: Barrier,\n        }\n\n        let resources = Arc::new(Resources {\n            pool: ResourcePool::<u32, u32>::new(),\n            barrier: Barrier::new(2),\n        });\n\n        // Like all races, this is not inherently guaranteed to work, but in practice it should work fine.\n        //\n        // To validate the expected order of events, we've put print statements in the code, indicating when each thread is at a certain point.\n        // The output will look something like this if the test is working as expected:\n        //\n        // ```\n        // 0: prewait\n        // 1: prewait\n        // 1: postwait\n        // 0: postwait\n        // 1: postsleep\n        // 1: removal\n        // 0: postget\n        // ```\n        //\n        // The last two _may_ be flipped.\n\n        let existing_entry = resources\n            .pool\n            .get_or_init::<_, ()>(0, |key| Ok(Arc::new(key)))\n            .unwrap();\n\n        // Drop the entry, but do _not_ remove it from the pool.\n        // This simulates the situation where the resource arc has been dropped, but the Drop implementation\n        // has not yet run, which calls remove.\n        drop(existing_entry);\n\n        fn thread0_inner(resources: &Resources) {\n            eprintln!(\"0: prewait\");\n            resources.barrier.wait();\n\n            eprintln!(\"0: postwait\");\n            // We try to create a new entry, but the entry already exists.\n            //\n            // As Arc::upgrade is failing, we will just keep spinning until remove is called.\n            resources\n                .pool\n                .get_or_init::<_, ()>(0, |key| Ok(Arc::new(key)))\n                .unwrap();\n            eprintln!(\"0: postget\");\n        }\n\n        fn thread1_inner(resources: &Resources) {\n            eprintln!(\"1: prewait\");\n            resources.barrier.wait();\n\n            eprintln!(\"1: postwait\");\n            // We wait a little bit, making sure that thread0_inner has started spinning.\n            thread::sleep(Duration::from_millis(250));\n            eprintln!(\"1: postsleep\");\n\n            // We remove the entry from the pool, allowing thread0_inner to re-create.\n            resources.pool.remove(&0);\n            eprintln!(\"1: removal\");\n        }\n\n        let thread1 = thread::spawn({\n            let resource_clone = Arc::clone(&resources);\n            move || thread1_inner(&resource_clone)\n        });\n\n        thread0_inner(&resources);\n\n        thread1.join().unwrap();\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/present.rs",
    "content": "/*! Presentation.\n\n## Lifecycle\n\nWhenever a submission detects the use of any surface texture, it adds it to the device\ntracker for the duration of the submission (temporarily, while recording).\nIt's added with `UNINITIALIZED` state and transitioned into `empty()` state.\nWhen this texture is presented, we remove it from the device tracker as well as\nextract it from the hub.\n!*/\n\nuse alloc::{sync::Arc, vec::Vec};\nuse core::mem::ManuallyDrop;\n\n#[cfg(feature = \"trace\")]\nuse crate::device::trace::{Action, IntoTrace};\nuse crate::{\n    conv,\n    device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError},\n    global::Global,\n    hal_label, id,\n    instance::Surface,\n    resource,\n};\n\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    SurfaceStatus as Status,\n};\n\nconst FRAME_TIMEOUT_MS: u32 = 1000;\n\n#[derive(Debug)]\npub(crate) struct Presentation {\n    pub(crate) device: Arc<Device>,\n    pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,\n    pub(crate) acquired_texture: Option<Arc<resource::Texture>>,\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum SurfaceError {\n    #[error(\"Surface is invalid\")]\n    Invalid,\n    #[error(\"Surface is not configured for presentation\")]\n    NotConfigured,\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Surface image is already acquired\")]\n    AlreadyAcquired,\n    #[error(\"Texture has been destroyed\")]\n    TextureDestroyed,\n}\n\nimpl WebGpuError for SurfaceError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::Invalid\n            | Self::NotConfigured\n            | Self::AlreadyAcquired\n            | Self::TextureDestroyed => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum ConfigureSurfaceError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Invalid surface\")]\n    InvalidSurface,\n    #[error(\"The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.\")]\n    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(\"`SurfaceOutput` must be dropped before a new `Surface` is made\")]\n    PreviousOutputExists,\n    #[error(\"Failed to wait for GPU to come idle before reconfiguring the Surface\")]\n    GpuWaitTimeout,\n    #[error(\"Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.\")]\n    ZeroArea,\n    #[error(\"`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, {height}), maximum extent for either dimension is {max_texture_dimension_2d}.\")]\n    TooLarge {\n        width: u32,\n        height: u32,\n        max_texture_dimension_2d: u32,\n    },\n    #[error(\"Surface does not support the adapter's queue family\")]\n    UnsupportedQueueFamily,\n    #[error(\"Requested format {requested:?} is not in list of supported formats: {available:?}\")]\n    UnsupportedFormat {\n        requested: wgt::TextureFormat,\n        available: Vec<wgt::TextureFormat>,\n    },\n    #[error(\"Requested present mode {requested:?} is not in the list of supported present modes: {available:?}\")]\n    UnsupportedPresentMode {\n        requested: wgt::PresentMode,\n        available: Vec<wgt::PresentMode>,\n    },\n    #[error(\"Requested alpha mode {requested:?} is not in the list of supported alpha modes: {available:?}\")]\n    UnsupportedAlphaMode {\n        requested: wgt::CompositeAlphaMode,\n        available: Vec<wgt::CompositeAlphaMode>,\n    },\n    #[error(\"Requested usage {requested:?} is not in the list of supported usages: {available:?}\")]\n    UnsupportedUsage {\n        requested: wgt::TextureUses,\n        available: wgt::TextureUses,\n    },\n}\n\nimpl From<WaitIdleError> for ConfigureSurfaceError {\n    fn from(e: WaitIdleError) -> Self {\n        match e {\n            WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),\n            WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),\n            WaitIdleError::Timeout => ConfigureSurfaceError::GpuWaitTimeout,\n        }\n    }\n}\n\nimpl WebGpuError for ConfigureSurfaceError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            Self::InvalidSurface\n            | Self::InvalidViewFormat(..)\n            | Self::PreviousOutputExists\n            | Self::GpuWaitTimeout\n            | Self::ZeroArea\n            | Self::TooLarge { .. }\n            | Self::UnsupportedQueueFamily\n            | Self::UnsupportedFormat { .. }\n            | Self::UnsupportedPresentMode { .. }\n            | Self::UnsupportedAlphaMode { .. }\n            | Self::UnsupportedUsage { .. } => ErrorType::Validation,\n        }\n    }\n}\n\npub type ResolvedSurfaceOutput = SurfaceOutput<Arc<resource::Texture>>;\n\n#[repr(C)]\n#[derive(Debug)]\npub struct SurfaceOutput<T = id::TextureId> {\n    pub status: Status,\n    pub texture: Option<T>,\n}\n\nimpl Surface {\n    pub fn get_current_texture(&self) -> Result<ResolvedSurfaceOutput, SurfaceError> {\n        profiling::scope!(\"Surface::get_current_texture\");\n\n        let (device, config) = if let Some(ref present) = *self.presentation.lock() {\n            present.device.check_is_valid()?;\n            (present.device.clone(), present.config.clone())\n        } else {\n            return Err(SurfaceError::NotConfigured);\n        };\n\n        let fence = device.fence.read();\n\n        let suf = self.raw(device.backend()).unwrap();\n        let (texture, status) = match unsafe {\n            suf.acquire_texture(\n                Some(core::time::Duration::from_millis(FRAME_TIMEOUT_MS as u64)),\n                fence.as_ref(),\n            )\n        } {\n            Ok(ast) => {\n                drop(fence);\n\n                let texture_desc = wgt::TextureDescriptor {\n                    label: hal_label(\n                        Some(alloc::borrow::Cow::Borrowed(\"<Surface Texture>\")),\n                        device.instance_flags,\n                    ),\n                    size: wgt::Extent3d {\n                        width: config.width,\n                        height: config.height,\n                        depth_or_array_layers: 1,\n                    },\n                    sample_count: 1,\n                    mip_level_count: 1,\n                    format: config.format,\n                    dimension: wgt::TextureDimension::D2,\n                    usage: config.usage,\n                    view_formats: config.view_formats,\n                };\n                let format_features = wgt::TextureFormatFeatures {\n                    allowed_usages: wgt::TextureUsages::RENDER_ATTACHMENT,\n                    flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4\n                        | wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,\n                };\n                let hal_usage = conv::map_texture_usage(\n                    config.usage,\n                    config.format.into(),\n                    format_features.flags,\n                );\n                let clear_view_desc = hal::TextureViewDescriptor {\n                    label: hal_label(\n                        Some(\"(wgpu internal) clear surface texture view\"),\n                        device.instance_flags,\n                    ),\n                    format: config.format,\n                    dimension: wgt::TextureViewDimension::D2,\n                    usage: wgt::TextureUses::COLOR_TARGET,\n                    range: wgt::ImageSubresourceRange::default(),\n                };\n                let clear_view = unsafe {\n                    device\n                        .raw()\n                        .create_texture_view(ast.texture.as_ref().borrow(), &clear_view_desc)\n                }\n                .map_err(|e| device.handle_hal_error(e))?;\n\n                let mut presentation = self.presentation.lock();\n                let present = presentation.as_mut().unwrap();\n                let texture = resource::Texture::new(\n                    &device,\n                    resource::TextureInner::Surface { raw: ast.texture },\n                    hal_usage,\n                    &texture_desc,\n                    format_features,\n                    resource::TextureClearMode::Surface {\n                        clear_view: ManuallyDrop::new(clear_view),\n                    },\n                    true,\n                );\n\n                let texture = Arc::new(texture);\n\n                device\n                    .trackers\n                    .lock()\n                    .textures\n                    .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);\n\n                if present.acquired_texture.is_some() {\n                    return Err(SurfaceError::AlreadyAcquired);\n                }\n                present.acquired_texture = Some(texture.clone());\n\n                let status = if ast.suboptimal {\n                    Status::Suboptimal\n                } else {\n                    Status::Good\n                };\n                (Some(texture), status)\n            }\n            Err(err) => (\n                None,\n                match err {\n                    hal::SurfaceError::Timeout => Status::Timeout,\n                    hal::SurfaceError::Occluded => Status::Occluded,\n                    hal::SurfaceError::Lost => Status::Lost,\n                    hal::SurfaceError::Device(err) => {\n                        return Err(device.handle_hal_error(err).into());\n                    }\n                    hal::SurfaceError::Outdated => Status::Outdated,\n                    hal::SurfaceError::Other(msg) => {\n                        log::error!(\"acquire error: {msg}\");\n                        Status::Lost\n                    }\n                },\n            ),\n        };\n\n        Ok(ResolvedSurfaceOutput { status, texture })\n    }\n\n    pub fn present(&self) -> Result<Status, SurfaceError> {\n        profiling::scope!(\"Surface::present\");\n\n        let mut presentation = self.presentation.lock();\n        let present = match presentation.as_mut() {\n            Some(present) => present,\n            None => return Err(SurfaceError::NotConfigured),\n        };\n\n        let device = &present.device;\n\n        device.check_is_valid()?;\n        let queue = device.get_queue().unwrap();\n\n        let texture = present\n            .acquired_texture\n            .take()\n            .ok_or(SurfaceError::AlreadyAcquired)?;\n\n        let mut exclusive_snatch_guard = device.snatchable_lock.write();\n        let inner = texture.inner.snatch(&mut exclusive_snatch_guard);\n        drop(exclusive_snatch_guard);\n\n        let result = match inner {\n            None => return Err(SurfaceError::TextureDestroyed),\n            Some(resource::TextureInner::Surface { raw }) => {\n                let raw_surface = self.raw(device.backend()).unwrap();\n                let raw_queue = queue.raw();\n                let _fence_lock = device.fence.write();\n                unsafe { raw_queue.present(raw_surface, raw) }\n            }\n            _ => unreachable!(),\n        };\n\n        match result {\n            Ok(()) => Ok(Status::Good),\n            Err(err) => match err {\n                hal::SurfaceError::Timeout => Ok(Status::Timeout),\n                hal::SurfaceError::Occluded => Ok(Status::Occluded),\n                hal::SurfaceError::Lost => Ok(Status::Lost),\n                hal::SurfaceError::Device(err) => {\n                    Err(SurfaceError::from(device.handle_hal_error(err)))\n                }\n                hal::SurfaceError::Outdated => Ok(Status::Outdated),\n                hal::SurfaceError::Other(msg) => {\n                    log::error!(\"present error: {msg}\");\n                    Err(SurfaceError::Invalid)\n                }\n            },\n        }\n    }\n\n    pub fn discard(&self) -> Result<(), SurfaceError> {\n        profiling::scope!(\"Surface::discard\");\n\n        let mut presentation = self.presentation.lock();\n        let present = match presentation.as_mut() {\n            Some(present) => present,\n            None => return Err(SurfaceError::NotConfigured),\n        };\n\n        let device = &present.device;\n\n        device.check_is_valid()?;\n\n        let texture = present\n            .acquired_texture\n            .take()\n            .ok_or(SurfaceError::AlreadyAcquired)?;\n\n        let mut exclusive_snatch_guard = device.snatchable_lock.write();\n        let inner = texture.inner.snatch(&mut exclusive_snatch_guard);\n        drop(exclusive_snatch_guard);\n\n        match inner {\n            None => return Err(SurfaceError::TextureDestroyed),\n            Some(resource::TextureInner::Surface { raw }) => {\n                let raw_surface = self.raw(device.backend()).unwrap();\n                unsafe { raw_surface.discard_texture(raw) };\n            }\n            _ => unreachable!(),\n        }\n\n        Ok(())\n    }\n}\n\nimpl Global {\n    pub fn surface_get_current_texture(\n        &self,\n        surface_id: id::SurfaceId,\n        texture_id_in: Option<id::TextureId>,\n    ) -> Result<SurfaceOutput, SurfaceError> {\n        let surface = self.surfaces.get(surface_id);\n\n        let fid = self.hub.textures.prepare(texture_id_in);\n\n        let output = surface.get_current_texture()?;\n\n        #[cfg(feature = \"trace\")]\n        if let Some(present) = surface.presentation.lock().as_ref() {\n            if let Some(ref mut trace) = *present.device.trace.lock() {\n                if let Some(texture) = present.acquired_texture.as_ref() {\n                    trace.add(Action::GetSurfaceTexture {\n                        id: texture.to_trace(),\n                        parent: surface.to_trace(),\n                    });\n                }\n            }\n        }\n\n        let status = output.status;\n        let texture_id = output\n            .texture\n            .map(|texture| fid.assign(resource::Fallible::Valid(texture)));\n\n        Ok(SurfaceOutput {\n            status,\n            texture: texture_id,\n        })\n    }\n\n    pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result<Status, SurfaceError> {\n        let surface = self.surfaces.get(surface_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Some(present) = surface.presentation.lock().as_ref() {\n            if let Some(ref mut trace) = *present.device.trace.lock() {\n                trace.add(Action::Present(surface.to_trace()));\n            }\n        }\n\n        surface.present()\n    }\n\n    pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> {\n        let surface = self.surfaces.get(surface_id);\n\n        #[cfg(feature = \"trace\")]\n        if let Some(present) = surface.presentation.lock().as_ref() {\n            if let Some(ref mut trace) = *present.device.trace.lock() {\n                trace.add(Action::DiscardSurfaceTexture(surface.to_trace()));\n            }\n        }\n\n        surface.discard()\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/ray_tracing.rs",
    "content": "// Ray tracing\n// Major missing optimizations (no api surface changes needed):\n// - use custom tracker to track build state\n// - no forced rebuilt (build mode deduction)\n// - lazy instance buffer allocation\n// - maybe share scratch and instance staging buffer allocation\n// - partial instance buffer uploads (api surface already designed with this in mind)\n// - Batch BLAS read-backs (if it shows up in performance).\n// - ([non performance] extract function in build (rust function extraction with guards is a pain))\n\nuse alloc::{boxed::Box, sync::Arc, vec::Vec};\n\n#[cfg(feature = \"serde\")]\nuse macro_rules_attribute::apply;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    AccelerationStructureGeometryFlags, BufferAddress, IndexFormat, VertexFormat,\n};\n\n#[cfg(feature = \"serde\")]\nuse crate::command::serde_object_reference_struct;\nuse crate::{\n    command::{ArcReferences, EncoderStateError, IdReferences, ReferenceType},\n    device::{DeviceError, MissingFeatures},\n    id::{BlasId, BufferId, TlasId},\n    resource::{\n        Blas, BlasCompactCallback, BlasPrepareCompactResult, DestroyedResourceError,\n        InvalidResourceError, MissingBufferUsageError, ResourceErrorIdent, Tlas,\n    },\n};\n\n#[derive(Clone, Debug, Error)]\npub enum CreateBlasError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\n        \"Only one of 'index_count' and 'index_format' was provided (either provide both or none)\"\n    )]\n    MissingIndexData,\n    #[error(\"Provided format was not within allowed formats. Provided format: {0:?}. Allowed formats: {1:?}\")]\n    InvalidVertexFormat(VertexFormat, Vec<VertexFormat>),\n    #[error(\"Limit `max_blas_geometry_count` is {0}, but the BLAS had {1} geometries\")]\n    TooManyGeometries(u32, u32),\n    #[error(\n        \"Limit `max_blas_primitive_count` is {0}, but the BLAS had a maximum of {1} primitives\"\n    )]\n    TooManyPrimitives(u32, u32),\n}\n\nimpl WebGpuError for CreateBlasError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::MissingIndexData\n            | Self::InvalidVertexFormat(..)\n            | Self::TooManyGeometries(..)\n            | Self::TooManyPrimitives(..) => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\npub enum CreateTlasError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\"Flag {0:?} is not allowed on a TLAS\")]\n    DisallowedFlag(wgt::AccelerationStructureFlags),\n    #[error(\"Limit `max_tlas_instance_count` is {0}, but the TLAS had a maximum of {1} instances\")]\n    TooManyInstances(u32, u32),\n}\n\nimpl WebGpuError for CreateTlasError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::DisallowedFlag(..) | Self::TooManyInstances(..) => ErrorType::Validation,\n        }\n    }\n}\n\n/// Error encountered while attempting to do a copy on a command encoder.\n#[derive(Clone, Debug, Error)]\npub enum BuildAccelerationStructureError {\n    #[error(transparent)]\n    EncoderState(#[from] EncoderStateError),\n\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n\n    #[error(\n        \"Buffer {0:?} size is insufficient for provided size information (size: {1}, required: {2}\"\n    )]\n    InsufficientBufferSize(ResourceErrorIdent, u64, u64),\n\n    #[error(\"Buffer {0:?} associated offset doesn't align with the index type\")]\n    UnalignedIndexBufferOffset(ResourceErrorIdent),\n\n    #[error(\"Buffer {0:?} associated offset is unaligned\")]\n    UnalignedTransformBufferOffset(ResourceErrorIdent),\n\n    #[error(\"Buffer {0:?} associated index count not divisible by 3 (count: {1}\")]\n    InvalidIndexCount(ResourceErrorIdent, u32),\n\n    #[error(\"Buffer {0:?} associated data contains None\")]\n    MissingAssociatedData(ResourceErrorIdent),\n\n    #[error(\n        \"Blas {0:?} build sizes to may be greater than the descriptor at build time specified\"\n    )]\n    IncompatibleBlasBuildSizes(ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} flags are different, creation flags: {1:?}, provided: {2:?}\")]\n    IncompatibleBlasFlags(\n        ResourceErrorIdent,\n        AccelerationStructureGeometryFlags,\n        AccelerationStructureGeometryFlags,\n    ),\n\n    #[error(\"Blas {0:?} build vertex count is greater than creation count (needs to be less than or equal to), creation: {1:?}, build: {2:?}\")]\n    IncompatibleBlasVertexCount(ResourceErrorIdent, u32, u32),\n\n    #[error(\"Blas {0:?} vertex formats are different, creation format: {1:?}, provided: {2:?}\")]\n    DifferentBlasVertexFormats(ResourceErrorIdent, VertexFormat, VertexFormat),\n\n    #[error(\"Blas {0:?} stride was required to be at least {1} but stride given was {2}\")]\n    VertexStrideTooSmall(ResourceErrorIdent, u64, u64),\n\n    #[error(\"Blas {0:?} stride was required to be a multiple of {1} but stride given was {2}\")]\n    VertexStrideUnaligned(ResourceErrorIdent, u64, u64),\n\n    #[error(\"Blas {0:?} index count was provided at creation or building, but not the other\")]\n    BlasIndexCountProvidedMismatch(ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} build index count is greater than creation count (needs to be less than or equal to), creation: {1:?}, build: {2:?}\")]\n    IncompatibleBlasIndexCount(ResourceErrorIdent, u32, u32),\n\n    #[error(\"Blas {0:?} index formats are different, creation format: {1:?}, provided: {2:?}\")]\n    DifferentBlasIndexFormats(ResourceErrorIdent, Option<IndexFormat>, Option<IndexFormat>),\n\n    #[error(\"Blas {0:?} is compacted and so cannot be built\")]\n    CompactedBlas(ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} build sizes require index buffer but none was provided\")]\n    MissingIndexBuffer(ResourceErrorIdent),\n\n    #[error(\n        \"Tlas {0:?} an associated instances contains an invalid custom index (more than 24bits)\"\n    )]\n    TlasInvalidCustomIndex(ResourceErrorIdent),\n\n    #[error(\n        \"Tlas {0:?} has {1} active instances but only {2} are allowed as specified by the descriptor at creation\"\n    )]\n    TlasInstanceCountExceeded(ResourceErrorIdent, u32, u32),\n\n    #[error(\"Blas {0:?} has flag USE_TRANSFORM but the transform buffer is missing\")]\n    TransformMissing(ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} is missing the flag USE_TRANSFORM but the transform buffer is set\")]\n    UseTransformMissing(ResourceErrorIdent),\n    #[error(\n        \"Tlas {0:?} dependent {1:?} is missing AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN\"\n    )]\n    TlasDependentMissingVertexReturn(ResourceErrorIdent, ResourceErrorIdent),\n}\n\nimpl WebGpuError for BuildAccelerationStructureError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::EncoderState(e) => e.webgpu_error_type(),\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::MissingBufferUsage(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::InsufficientBufferSize(..)\n            | Self::UnalignedIndexBufferOffset(..)\n            | Self::UnalignedTransformBufferOffset(..)\n            | Self::InvalidIndexCount(..)\n            | Self::MissingAssociatedData(..)\n            | Self::IncompatibleBlasBuildSizes(..)\n            | Self::IncompatibleBlasFlags(..)\n            | Self::IncompatibleBlasVertexCount(..)\n            | Self::DifferentBlasVertexFormats(..)\n            | Self::VertexStrideTooSmall(..)\n            | Self::VertexStrideUnaligned(..)\n            | Self::BlasIndexCountProvidedMismatch(..)\n            | Self::IncompatibleBlasIndexCount(..)\n            | Self::DifferentBlasIndexFormats(..)\n            | Self::CompactedBlas(..)\n            | Self::MissingIndexBuffer(..)\n            | Self::TlasInvalidCustomIndex(..)\n            | Self::TlasInstanceCountExceeded(..)\n            | Self::TransformMissing(..)\n            | Self::UseTransformMissing(..)\n            | Self::TlasDependentMissingVertexReturn(..) => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\npub enum ValidateAsActionsError {\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n\n    #[error(\"Tlas {0:?} is used before it is built\")]\n    UsedUnbuiltTlas(ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} is used before it is built (in Tlas {1:?})\")]\n    UsedUnbuiltBlas(ResourceErrorIdent, ResourceErrorIdent),\n\n    #[error(\"Blas {0:?} is newer than the containing Tlas {1:?}\")]\n    BlasNewerThenTlas(ResourceErrorIdent, ResourceErrorIdent),\n}\n\nimpl WebGpuError for ValidateAsActionsError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::UsedUnbuiltTlas(..) | Self::UsedUnbuiltBlas(..) | Self::BlasNewerThenTlas(..) => {\n                ErrorType::Validation\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct BlasTriangleGeometry<'a> {\n    pub size: &'a wgt::BlasTriangleGeometrySizeDescriptor,\n    pub vertex_buffer: BufferId,\n    pub index_buffer: Option<BufferId>,\n    pub transform_buffer: Option<BufferId>,\n    pub first_vertex: u32,\n    pub vertex_stride: BufferAddress,\n    pub first_index: Option<u32>,\n    pub transform_buffer_offset: Option<BufferAddress>,\n}\n\npub enum BlasGeometries<'a> {\n    TriangleGeometries(Box<dyn Iterator<Item = BlasTriangleGeometry<'a>> + 'a>),\n}\n\npub struct BlasBuildEntry<'a> {\n    pub blas_id: BlasId,\n    pub geometries: BlasGeometries<'a>,\n}\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct TlasBuildEntry {\n    pub tlas_id: TlasId,\n    pub instance_buffer_id: BufferId,\n    pub instance_count: u32,\n}\n\n#[derive(Debug)]\npub struct TlasInstance<'a> {\n    pub blas_id: BlasId,\n    pub transform: &'a [f32; 12],\n    pub custom_data: u32,\n    pub mask: u8,\n}\n\npub struct TlasPackage<'a> {\n    pub tlas_id: TlasId,\n    pub instances: Box<dyn Iterator<Item = Option<TlasInstance<'a>>> + 'a>,\n    pub lowest_unmodified: u32,\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct TlasBuild {\n    pub tlas: Arc<Tlas>,\n    pub dependencies: Vec<Arc<Blas>>,\n}\n\n#[derive(Debug, Clone, Default)]\npub(crate) struct AsBuild {\n    pub blas_s_built: Vec<Arc<Blas>>,\n    pub tlas_s_built: Vec<TlasBuild>,\n}\n\nimpl AsBuild {\n    pub(crate) fn with_capacity(blas: usize, tlas: usize) -> Self {\n        Self {\n            blas_s_built: Vec::with_capacity(blas),\n            tlas_s_built: Vec::with_capacity(tlas),\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub(crate) enum AsAction {\n    Build(AsBuild),\n    UseTlas(Arc<Tlas>),\n}\n\n/// Like [`BlasTriangleGeometry`], but with owned data.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub struct OwnedBlasTriangleGeometry<R: ReferenceType> {\n    pub size: wgt::BlasTriangleGeometrySizeDescriptor,\n    pub vertex_buffer: R::Buffer,\n    pub index_buffer: Option<R::Buffer>,\n    pub transform_buffer: Option<R::Buffer>,\n    pub first_vertex: u32,\n    pub vertex_stride: BufferAddress,\n    pub first_index: Option<u32>,\n    pub transform_buffer_offset: Option<BufferAddress>,\n}\n\npub type ArcBlasTriangleGeometry = OwnedBlasTriangleGeometry<ArcReferences>;\npub type TraceBlasTriangleGeometry = OwnedBlasTriangleGeometry<IdReferences>;\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub enum OwnedBlasGeometries<R: ReferenceType> {\n    TriangleGeometries(Vec<OwnedBlasTriangleGeometry<R>>),\n}\n\npub type ArcBlasGeometries = OwnedBlasGeometries<ArcReferences>;\npub type TraceBlasGeometries = OwnedBlasGeometries<IdReferences>;\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub struct OwnedBlasBuildEntry<R: ReferenceType> {\n    pub blas: R::Blas,\n    pub geometries: OwnedBlasGeometries<R>,\n}\n\npub type ArcBlasBuildEntry = OwnedBlasBuildEntry<ArcReferences>;\npub type TraceBlasBuildEntry = OwnedBlasBuildEntry<IdReferences>;\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub struct OwnedTlasInstance<R: ReferenceType> {\n    pub blas: R::Blas,\n    pub transform: [f32; 12],\n    pub custom_data: u32,\n    pub mask: u8,\n}\n\npub type ArcTlasInstance = OwnedTlasInstance<ArcReferences>;\npub type TraceTlasInstance = OwnedTlasInstance<IdReferences>;\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", apply(serde_object_reference_struct))]\npub struct OwnedTlasPackage<R: ReferenceType> {\n    pub tlas: R::Tlas,\n    pub instances: Vec<Option<OwnedTlasInstance<R>>>,\n    pub lowest_unmodified: u32,\n}\n\npub type TraceTlasPackage = OwnedTlasPackage<IdReferences>;\npub type ArcTlasPackage = OwnedTlasPackage<ArcReferences>;\n\n/// [`BlasTriangleGeometry`], without the resources.\n#[derive(Debug, Clone)]\npub struct BlasTriangleGeometryInfo {\n    pub size: wgt::BlasTriangleGeometrySizeDescriptor,\n    pub first_vertex: u32,\n    pub vertex_stride: BufferAddress,\n    pub first_index: Option<u32>,\n    pub transform_buffer_offset: Option<BufferAddress>,\n}\n\n#[derive(Clone, Debug, Error)]\npub enum BlasPrepareCompactError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(\"Compaction is already being prepared\")]\n    CompactionPreparingAlready,\n    #[error(\"Cannot compact an already compacted BLAS\")]\n    DoubleCompaction,\n    #[error(\"BLAS is not yet built\")]\n    NotBuilt,\n    #[error(\"BLAS does not support compaction (is AccelerationStructureFlags::ALLOW_COMPACTION missing?)\")]\n    CompactionUnsupported,\n}\n\nimpl WebGpuError for BlasPrepareCompactError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::CompactionPreparingAlready\n            | Self::DoubleCompaction\n            | Self::NotBuilt\n            | Self::CompactionUnsupported => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\npub enum CompactBlasError {\n    #[error(transparent)]\n    Encoder(#[from] EncoderStateError),\n\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n\n    #[error(\"BLAS was not prepared for compaction\")]\n    BlasNotReady,\n}\n\nimpl WebGpuError for CompactBlasError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Encoder(e) => e.webgpu_error_type(),\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n            Self::BlasNotReady => ErrorType::Validation,\n        }\n    }\n}\n\npub type BlasCompactReadyPendingClosure = (Option<BlasCompactCallback>, BlasPrepareCompactResult);\n"
  },
  {
    "path": "wgpu-core/src/registry.rs",
    "content": "use alloc::sync::Arc;\n\nuse crate::{\n    id::Id,\n    identity::IdentityManager,\n    lock::{rank, RwLock, RwLockReadGuard},\n    storage::{Element, Storage, StorageItem},\n};\n\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]\npub struct RegistryReport {\n    pub num_allocated: usize,\n    pub num_kept_from_user: usize,\n    pub num_released_from_user: usize,\n    pub element_size: usize,\n}\n\nimpl RegistryReport {\n    pub fn is_empty(&self) -> bool {\n        self.num_allocated + self.num_kept_from_user == 0\n    }\n}\n\n/// Registry is the primary holder of each resource type\n/// Every resource is now arcanized so the last arc released\n/// will in the end free the memory and release the inner raw resource\n///\n/// Registry act as the main entry point to keep resource alive\n/// when created and released from user land code\n///\n/// A resource may still be alive when released from user land code\n/// if it's used in active submission or anyway kept alive from\n/// any other dependent resource\n///\n#[derive(Debug)]\npub(crate) struct Registry<T: StorageItem> {\n    // Must only contain an id which has either never been used or has been released from `storage`\n    identity: Arc<IdentityManager<T::Marker>>,\n    storage: RwLock<Storage<T>>,\n}\n\nimpl<T: StorageItem> Registry<T> {\n    pub(crate) fn new() -> Self {\n        Self {\n            identity: Arc::new(IdentityManager::new()),\n            storage: RwLock::new(rank::REGISTRY_STORAGE, Storage::new()),\n        }\n    }\n}\n\n#[must_use]\npub(crate) struct FutureId<'a, T: StorageItem> {\n    id: Id<T::Marker>,\n    data: &'a RwLock<Storage<T>>,\n}\n\nimpl<T: StorageItem> FutureId<'_, T> {\n    /// Assign a new resource to this ID.\n    ///\n    /// Registers it with the registry.\n    pub fn assign(self, value: T) -> Id<T::Marker> {\n        let mut data = self.data.write();\n        data.insert(self.id, value);\n        self.id\n    }\n}\n\nimpl<T: StorageItem> Registry<T> {\n    pub(crate) fn prepare(&self, id_in: Option<Id<T::Marker>>) -> FutureId<'_, T> {\n        FutureId {\n            id: match id_in {\n                Some(id_in) => {\n                    self.identity.mark_as_used(id_in);\n                    id_in\n                }\n                None => self.identity.process(),\n            },\n            data: &self.storage,\n        }\n    }\n\n    #[track_caller]\n    pub(crate) fn read<'a>(&'a self) -> RwLockReadGuard<'a, Storage<T>> {\n        self.storage.read()\n    }\n    pub(crate) fn remove(&self, id: Id<T::Marker>) -> T {\n        let value = self.storage.write().remove(id);\n        // This needs to happen *after* removing it from the storage, to maintain the\n        // invariant that `self.identity` only contains ids which are actually available\n        // See https://github.com/gfx-rs/wgpu/issues/5372\n        self.identity.free(id);\n        //Returning None is legal if it's an error ID\n        value\n    }\n\n    pub(crate) fn generate_report(&self) -> RegistryReport {\n        let storage = self.storage.read();\n        let mut report = RegistryReport {\n            element_size: size_of::<T>(),\n            ..Default::default()\n        };\n        report.num_allocated = self.identity.values.lock().count();\n        for element in storage.map.iter() {\n            match *element {\n                Element::Occupied(..) => report.num_kept_from_user += 1,\n                Element::Vacant => report.num_released_from_user += 1,\n            }\n        }\n        report\n    }\n}\n\nimpl<T: StorageItem + Clone> Registry<T> {\n    pub(crate) fn get(&self, id: Id<T::Marker>) -> T {\n        self.read().get(id)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::Registry;\n    use crate::{id::Marker, resource::ResourceType, storage::StorageItem};\n    use alloc::sync::Arc;\n\n    struct TestData;\n    struct TestDataId;\n    impl Marker for TestDataId {}\n\n    impl ResourceType for TestData {\n        const TYPE: &'static str = \"TestData\";\n    }\n    impl StorageItem for TestData {\n        type Marker = TestDataId;\n    }\n\n    #[test]\n    fn simultaneous_registration() {\n        let registry = Registry::new();\n        std::thread::scope(|s| {\n            for _ in 0..5 {\n                s.spawn(|| {\n                    for _ in 0..1000 {\n                        let value = Arc::new(TestData);\n                        let new_id = registry.prepare(None);\n                        let id = new_id.assign(value);\n                        registry.remove(id);\n                    }\n                });\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/resource.rs",
    "content": "use alloc::{borrow::Cow, borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};\nuse core::{\n    borrow::Borrow,\n    fmt,\n    mem::{self, size_of, ManuallyDrop},\n    num::NonZeroU64,\n    ops::Range,\n    ptr::NonNull,\n};\nuse smallvec::SmallVec;\nuse thiserror::Error;\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    TextureSelector,\n};\n\n#[cfg(feature = \"trace\")]\nuse crate::device::trace;\nuse crate::{\n    binding_model::{BindGroup, BindingError},\n    device::{\n        queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,\n        DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,\n    },\n    hal_label,\n    init_tracker::{BufferInitTracker, TextureInitTracker},\n    lock::{rank, Mutex, RwLock},\n    ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},\n    resource_log,\n    snatch::{SnatchGuard, Snatchable},\n    timestamp_normalization::TimestampNormalizationBindGroup,\n    track::{SharedTrackerIndexAllocator, TrackerIndex},\n    weak_vec::WeakVec,\n    Label, LabelHelpers, SubmissionIndex,\n};\n\n/// Information about the wgpu-core resource.\n///\n/// Each type representing a `wgpu-core` resource, like [`Device`],\n/// [`Buffer`], etc., contains a `ResourceInfo` which contains\n/// its latest submission index and label.\n///\n/// A resource may need to be retained for any of several reasons:\n/// and any lifetime logic will be handled by `Arc<Resource>` refcount\n///\n/// - The user may hold a reference to it (via a `wgpu::Buffer`, say).\n///\n/// - Other resources may depend on it (a texture view's backing\n///   texture, for example).\n///\n/// - It may be used by commands sent to the GPU that have not yet\n///   finished execution.\n///\n/// [`Device`]: crate::device::resource::Device\n/// [`Buffer`]: crate::resource::Buffer\n#[derive(Debug)]\npub(crate) struct TrackingData {\n    tracker_index: TrackerIndex,\n    tracker_indices: Arc<SharedTrackerIndexAllocator>,\n}\n\nimpl Drop for TrackingData {\n    fn drop(&mut self) {\n        self.tracker_indices.free(self.tracker_index);\n    }\n}\n\nimpl TrackingData {\n    pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {\n        Self {\n            tracker_index: tracker_indices.alloc(),\n            tracker_indices,\n        }\n    }\n\n    pub(crate) fn tracker_index(&self) -> TrackerIndex {\n        self.tracker_index\n    }\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct ResourceErrorIdent {\n    r#type: Cow<'static, str>,\n    label: String,\n}\n\nimpl fmt::Display for ResourceErrorIdent {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(f, \"{} with '{}' label\", self.r#type, self.label)\n    }\n}\n\npub trait ParentDevice: Labeled {\n    fn device(&self) -> &Arc<Device>;\n\n    fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {\n        Arc::ptr_eq(self, other)\n    }\n\n    fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {\n        if Arc::ptr_eq(self.device(), other.device()) {\n            Ok(())\n        } else {\n            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {\n                res: self.error_ident(),\n                res_device: self.device().error_ident(),\n                target: Some(other.error_ident()),\n                target_device: other.device().error_ident(),\n            })))\n        }\n    }\n\n    fn same_device(&self, device: &Device) -> Result<(), DeviceError> {\n        if core::ptr::eq(&**self.device(), device) {\n            Ok(())\n        } else {\n            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {\n                res: self.error_ident(),\n                res_device: self.device().error_ident(),\n                target: None,\n                target_device: device.error_ident(),\n            })))\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! impl_parent_device {\n    ($ty:ident) => {\n        impl $crate::resource::ParentDevice for $ty {\n            fn device(&self) -> &Arc<Device> {\n                &self.device\n            }\n        }\n    };\n}\n\n/// Allow access to the hal resource as guarded by the `SnatchGuard`.\npub trait RawResourceAccess: ParentDevice {\n    type DynResource: hal::DynResource + ?Sized;\n\n    /// Get access to the raw resource if it is not destroyed.\n    ///\n    /// Returns `None` if the resource has been destroyed. This method\n    /// does not allocate in either case.\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;\n\n    /// Get access to the raw resource if it is not destroyed.\n    ///\n    /// Returns a full error if the resource has been destroyed. This\n    /// method allocates a label in the error case.\n    fn try_raw<'a>(\n        &'a self,\n        guard: &'a SnatchGuard,\n    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {\n        self.raw(guard)\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n}\n\npub trait ResourceType {\n    const TYPE: &'static str;\n}\n\n#[macro_export]\nmacro_rules! impl_resource_type {\n    ($ty:ident) => {\n        impl $crate::resource::ResourceType for $ty {\n            const TYPE: &'static str = stringify!($ty);\n        }\n    };\n}\n\npub trait Labeled: ResourceType {\n    /// Returns a string identifying this resource for logging and errors.\n    ///\n    /// It may be a user-provided string or it may be a placeholder from wgpu.\n    ///\n    /// It is non-empty unless the user-provided string was empty.\n    fn label(&self) -> &str;\n\n    fn error_ident(&self) -> ResourceErrorIdent {\n        ResourceErrorIdent {\n            r#type: Cow::Borrowed(Self::TYPE),\n            label: self.label().to_owned(),\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! impl_labeled {\n    ($ty:ident) => {\n        impl $crate::resource::Labeled for $ty {\n            fn label(&self) -> &str {\n                &self.label\n            }\n        }\n    };\n}\n\npub(crate) trait Trackable {\n    fn tracker_index(&self) -> TrackerIndex;\n}\n\n#[macro_export]\nmacro_rules! impl_trackable {\n    ($ty:ident) => {\n        impl $crate::resource::Trackable for $ty {\n            fn tracker_index(&self) -> $crate::track::TrackerIndex {\n                self.tracking_data.tracker_index()\n            }\n        }\n    };\n}\n\n#[derive(Debug)]\npub(crate) enum BufferMapState {\n    /// Mapped at creation.\n    Init { staging_buffer: StagingBuffer },\n    /// Waiting for GPU to be done before mapping\n    Waiting(BufferPendingMapping),\n    /// Mapped\n    Active {\n        mapping: hal::BufferMapping,\n        range: hal::MemoryRange,\n        host: HostMap,\n    },\n    /// Not mapped\n    Idle,\n}\n\n#[cfg(send_sync)]\nunsafe impl Send for BufferMapState {}\n#[cfg(send_sync)]\nunsafe impl Sync for BufferMapState {}\n\n#[cfg(send_sync)]\npub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;\n\npub struct BufferMapOperation {\n    pub host: HostMap,\n    pub callback: Option<BufferMapCallback>,\n}\n\nimpl fmt::Debug for BufferMapOperation {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"BufferMapOperation\")\n            .field(\"host\", &self.host)\n            .field(\"callback\", &self.callback.as_ref().map(|_| \"?\"))\n            .finish()\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[non_exhaustive]\npub enum BufferAccessError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Buffer map failed\")]\n    Failed,\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"Buffer is already mapped\")]\n    AlreadyMapped,\n    #[error(\"Buffer map is pending\")]\n    MapAlreadyPending,\n    #[error(transparent)]\n    MissingBufferUsage(#[from] MissingBufferUsageError),\n    #[error(\"Buffer is not mapped\")]\n    NotMapped,\n    #[error(\n        \"Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`\"\n    )]\n    UnalignedRange,\n    #[error(\"Buffer offset invalid: offset {offset} must be multiple of 8\")]\n    UnalignedOffset { offset: wgt::BufferAddress },\n    #[error(\"Buffer range size invalid: range_size {range_size} must be multiple of 4\")]\n    UnalignedRangeSize { range_size: wgt::BufferAddress },\n    #[error(\"Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})\")]\n    OutOfBoundsStartOffsetUnderrun {\n        index: wgt::BufferAddress,\n        min: wgt::BufferAddress,\n    },\n    #[error(\n        \"Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})\"\n    )]\n    OutOfBoundsStartOffsetOverrun {\n        index: wgt::BufferAddress,\n        max: wgt::BufferAddress,\n    },\n    #[error(\n        \"Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})\"\n    )]\n    OutOfBoundsEndOffsetOverrun {\n        index: wgt::BufferAddress,\n        size: wgt::BufferAddress,\n        max: wgt::BufferAddress,\n    },\n    #[error(\"Buffer map aborted\")]\n    MapAborted,\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(\"Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}\")]\n    MapStartOffsetOverrun {\n        offset: wgt::BufferAddress,\n        buffer_size: wgt::BufferAddress,\n    },\n    #[error(\n        \"Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}\",\n        offset,\n        size,\n        buffer_size\n    )]\n    MapEndOffsetOverrun {\n        offset: wgt::BufferAddress,\n        size: wgt::BufferAddress,\n        buffer_size: wgt::BufferAddress,\n    },\n}\n\nimpl WebGpuError for BufferAccessError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::InvalidResource(e) => e.webgpu_error_type(),\n            Self::DestroyedResource(e) => e.webgpu_error_type(),\n\n            Self::Failed\n            | Self::AlreadyMapped\n            | Self::MapAlreadyPending\n            | Self::MissingBufferUsage(_)\n            | Self::NotMapped\n            | Self::UnalignedRange\n            | Self::UnalignedOffset { .. }\n            | Self::UnalignedRangeSize { .. }\n            | Self::OutOfBoundsStartOffsetUnderrun { .. }\n            | Self::OutOfBoundsStartOffsetOverrun { .. }\n            | Self::OutOfBoundsEndOffsetOverrun { .. }\n            | Self::MapAborted\n            | Self::MapStartOffsetOverrun { .. }\n            | Self::MapEndOffsetOverrun { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[error(\"Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}\")]\npub struct MissingBufferUsageError {\n    pub(crate) res: ResourceErrorIdent,\n    pub(crate) actual: wgt::BufferUsages,\n    pub(crate) expected: wgt::BufferUsages,\n}\n\nimpl WebGpuError for MissingBufferUsageError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[error(\"Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}\")]\npub struct MissingTextureUsageError {\n    pub(crate) res: ResourceErrorIdent,\n    pub(crate) actual: wgt::TextureUsages,\n    pub(crate) expected: wgt::TextureUsages,\n}\n\nimpl WebGpuError for MissingTextureUsageError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[error(\"{0} has been destroyed\")]\npub struct DestroyedResourceError(pub ResourceErrorIdent);\n\nimpl WebGpuError for DestroyedResourceError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[error(\"{0} is invalid\")]\npub struct InvalidResourceError(pub ResourceErrorIdent);\n\nimpl WebGpuError for InvalidResourceError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\npub enum Fallible<T: ParentDevice> {\n    Valid(Arc<T>),\n    Invalid(Arc<String>),\n}\n\nimpl<T: ParentDevice> Fallible<T> {\n    pub fn get(self) -> Result<Arc<T>, InvalidResourceError> {\n        match self {\n            Fallible::Valid(v) => Ok(v),\n            Fallible::Invalid(label) => Err(InvalidResourceError(ResourceErrorIdent {\n                r#type: Cow::Borrowed(T::TYPE),\n                label: (*label).clone(),\n            })),\n        }\n    }\n}\n\nimpl<T: ParentDevice> Clone for Fallible<T> {\n    fn clone(&self) -> Self {\n        match self {\n            Self::Valid(v) => Self::Valid(v.clone()),\n            Self::Invalid(l) => Self::Invalid(l.clone()),\n        }\n    }\n}\n\nimpl<T: ParentDevice> ResourceType for Fallible<T> {\n    const TYPE: &'static str = T::TYPE;\n}\n\nimpl<T: ParentDevice + crate::storage::StorageItem> crate::storage::StorageItem for Fallible<T> {\n    type Marker = T::Marker;\n}\n\npub type BufferAccessResult = Result<(), BufferAccessError>;\n\n#[derive(Debug)]\npub(crate) struct BufferPendingMapping {\n    pub(crate) range: Range<wgt::BufferAddress>,\n    pub(crate) op: BufferMapOperation,\n    // hold the parent alive while the mapping is active\n    pub(crate) _parent_buffer: Arc<Buffer>,\n}\n\npub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;\n\n#[derive(Debug)]\npub struct Buffer {\n    pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) usage: wgt::BufferUsages,\n    pub(crate) size: wgt::BufferAddress,\n    pub(crate) initialization_status: RwLock<BufferInitTracker>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    pub(crate) map_state: Mutex<BufferMapState>,\n    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,\n    pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,\n    pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,\n}\n\nimpl Drop for Buffer {\n    fn drop(&mut self) {\n        if let Some(raw) = self.timestamp_normalization_bind_group.take() {\n            raw.dispose(self.device.raw());\n        }\n\n        if let Some(raw) = self.indirect_validation_bind_groups.take() {\n            raw.dispose(self.device.raw());\n        }\n\n        if let Some(raw) = self.raw.take() {\n            resource_log!(\"Destroy raw {}\", self.error_ident());\n            unsafe {\n                self.device.raw().destroy_buffer(raw);\n            }\n        }\n    }\n}\n\nimpl RawResourceAccess for Buffer {\n    type DynResource = dyn hal::DynBuffer;\n\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {\n        self.raw.get(guard).map(|b| b.as_ref())\n    }\n}\n\nimpl Buffer {\n    pub(crate) fn check_destroyed(\n        &self,\n        guard: &SnatchGuard,\n    ) -> Result<(), DestroyedResourceError> {\n        self.raw\n            .get(guard)\n            .map(|_| ())\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n\n    /// Checks that the given buffer usage contains the required buffer usage,\n    /// returns an error otherwise.\n    pub(crate) fn check_usage(\n        &self,\n        expected: wgt::BufferUsages,\n    ) -> Result<(), MissingBufferUsageError> {\n        if self.usage.contains(expected) {\n            Ok(())\n        } else {\n            Err(MissingBufferUsageError {\n                res: self.error_ident(),\n                actual: self.usage,\n                expected,\n            })\n        }\n    }\n\n    /// Resolve the size of a binding for buffer with `offset` and `size`.\n    ///\n    /// If `size` is `None`, then the remainder of the buffer starting from\n    /// `offset` is used.\n    ///\n    /// If the binding would overflow the buffer, then an error is returned.\n    ///\n    /// Zero-size bindings are permitted here for historical reasons. Although\n    /// zero-size bindings are permitted by WebGPU, they are not permitted by\n    /// some backends. See [`Buffer::binding`] and\n    /// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).\n    pub fn resolve_binding_size(\n        &self,\n        offset: wgt::BufferAddress,\n        binding_size: Option<wgt::BufferSize>,\n    ) -> Result<u64, BindingError> {\n        let buffer_size = self.size;\n\n        match binding_size {\n            Some(binding_size) => match offset.checked_add(binding_size.get()) {\n                Some(end) if end <= buffer_size => Ok(binding_size.get()),\n                _ => Err(BindingError::BindingRangeTooLarge {\n                    buffer: self.error_ident(),\n                    offset,\n                    binding_size: binding_size.get(),\n                    buffer_size,\n                }),\n            },\n            None => {\n                buffer_size\n                    .checked_sub(offset)\n                    .ok_or_else(|| BindingError::BindingOffsetTooLarge {\n                        buffer: self.error_ident(),\n                        offset,\n                        buffer_size,\n                    })\n            }\n        }\n    }\n\n    /// Create a new [`hal::BufferBinding`] for the buffer with `offset` and\n    /// `binding_size`.\n    ///\n    /// If `binding_size` is `None`, then the remainder of the buffer starting\n    /// from `offset` is used.\n    ///\n    /// If the binding would overflow the buffer, then an error is returned.\n    ///\n    /// A zero-size binding at the end of the buffer is permitted here for historical reasons. Although\n    /// zero-size bindings are permitted by WebGPU, they are not permitted by\n    /// some backends. The zero-size binding need to be quashed or remapped to a\n    /// non-zero size, either universally in wgpu-core, or in specific backends\n    /// that do not support them. See\n    /// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).\n    ///\n    /// Although it seems like it would be simpler and safer to use the resolved\n    /// size in the returned [`hal::BufferBinding`], doing this (and removing\n    /// redundant logic in backends to resolve the implicit size) was observed\n    /// to cause problems in certain CTS tests, so an implicit size\n    /// specification is preserved in the output.\n    pub fn binding<'a>(\n        &'a self,\n        offset: wgt::BufferAddress,\n        binding_size: Option<wgt::BufferSize>,\n        snatch_guard: &'a SnatchGuard,\n    ) -> Result<(hal::BufferBinding<'a, dyn hal::DynBuffer>, u64), BindingError> {\n        let buf_raw = self.try_raw(snatch_guard)?;\n        let resolved_size = self.resolve_binding_size(offset, binding_size)?;\n        // SAFETY: The offset and size passed to hal::BufferBinding::new_unchecked must\n        // define a binding contained within the buffer.\n        Ok((\n            hal::BufferBinding::new_unchecked(buf_raw, offset, binding_size),\n            resolved_size,\n        ))\n    }\n\n    /// Returns the mapping callback in case of error so that the callback can be fired outside\n    /// of the locks that are held in this function.\n    pub fn map_async(\n        self: &Arc<Self>,\n        offset: wgt::BufferAddress,\n        size: Option<wgt::BufferAddress>,\n        op: BufferMapOperation,\n    ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {\n        let range_size = if let Some(size) = size {\n            size\n        } else {\n            self.size.saturating_sub(offset)\n        };\n\n        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {\n            return Err((op, BufferAccessError::UnalignedOffset { offset }));\n        }\n        if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));\n        }\n\n        if offset > self.size {\n            return Err((\n                op,\n                BufferAccessError::MapStartOffsetOverrun {\n                    offset,\n                    buffer_size: self.size,\n                },\n            ));\n        }\n        // NOTE: Should never underflow because of our earlier check.\n        if range_size > self.size - offset {\n            return Err((\n                op,\n                BufferAccessError::MapEndOffsetOverrun {\n                    offset,\n                    size: range_size,\n                    buffer_size: self.size,\n                },\n            ));\n        }\n        let end_offset = offset + range_size;\n\n        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)\n            || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)\n        {\n            return Err((op, BufferAccessError::UnalignedRange));\n        }\n\n        let (pub_usage, internal_use) = match op.host {\n            HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),\n            HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),\n        };\n\n        if let Err(e) = self.check_usage(pub_usage) {\n            return Err((op, e.into()));\n        }\n\n        let device = &self.device;\n        if let Err(e) = device.check_is_valid() {\n            return Err((op, e.into()));\n        }\n\n        {\n            let snatch_guard = device.snatchable_lock.read();\n            if let Err(e) = self.check_destroyed(&snatch_guard) {\n                return Err((op, e.into()));\n            }\n        }\n\n        {\n            let map_state = &mut *self.map_state.lock();\n            *map_state = match *map_state {\n                BufferMapState::Init { .. } | BufferMapState::Active { .. } => {\n                    return Err((op, BufferAccessError::AlreadyMapped));\n                }\n                BufferMapState::Waiting(_) => {\n                    return Err((op, BufferAccessError::MapAlreadyPending));\n                }\n                BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {\n                    range: offset..end_offset,\n                    op,\n                    _parent_buffer: self.clone(),\n                }),\n            };\n        }\n\n        // TODO: we are ignoring the transition here, I think we need to add a barrier\n        // at the end of the submission\n        device\n            .trackers\n            .lock()\n            .buffers\n            .set_single(self, internal_use);\n\n        let submit_index = if let Some(queue) = device.get_queue() {\n            queue.lock_life().map(self).unwrap_or(0) // '0' means no wait is necessary\n        } else {\n            // We can safely unwrap below since we just set the `map_state` to `BufferMapState::Waiting`.\n            let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();\n            if let Some(callback) = operation.callback.take() {\n                callback(status);\n            }\n            0\n        };\n\n        Ok(submit_index)\n    }\n\n    pub fn get_mapped_range(\n        self: &Arc<Self>,\n        offset: wgt::BufferAddress,\n        size: Option<wgt::BufferAddress>,\n    ) -> Result<(NonNull<u8>, u64), BufferAccessError> {\n        {\n            let snatch_guard = self.device.snatchable_lock.read();\n            self.check_destroyed(&snatch_guard)?;\n        }\n\n        let range_size = if let Some(size) = size {\n            size\n        } else {\n            self.size.saturating_sub(offset)\n        };\n\n        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {\n            return Err(BufferAccessError::UnalignedOffset { offset });\n        }\n        if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {\n            return Err(BufferAccessError::UnalignedRangeSize { range_size });\n        }\n        let map_state = &*self.map_state.lock();\n        match *map_state {\n            BufferMapState::Init { ref staging_buffer } => {\n                if offset > self.size {\n                    return Err(BufferAccessError::MapStartOffsetOverrun {\n                        offset,\n                        buffer_size: self.size,\n                    });\n                }\n                // NOTE: Should never underflow because of our earlier check.\n                if range_size > self.size - offset {\n                    return Err(BufferAccessError::MapEndOffsetOverrun {\n                        offset,\n                        size: range_size,\n                        buffer_size: self.size,\n                    });\n                }\n                let ptr = unsafe { staging_buffer.ptr() };\n                let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };\n                Ok((ptr, range_size))\n            }\n            BufferMapState::Active {\n                ref mapping,\n                ref range,\n                ..\n            } => {\n                if offset > range.end {\n                    return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {\n                        index: offset,\n                        max: range.end,\n                    });\n                }\n                if offset < range.start {\n                    return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {\n                        index: offset,\n                        min: range.start,\n                    });\n                }\n                if range_size > range.end - offset {\n                    return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {\n                        index: offset,\n                        size: range_size,\n                        max: range.end,\n                    });\n                }\n                // ptr points to the beginning of the range we mapped in map_async\n                // rather than the beginning of the buffer.\n                let relative_offset = (offset - range.start) as isize;\n                unsafe {\n                    Ok((\n                        NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),\n                        range_size,\n                    ))\n                }\n            }\n            BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),\n        }\n    }\n    /// This function returns [`None`] only if [`Self::map_state`] is not [`BufferMapState::Waiting`].\n    #[must_use]\n    pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {\n        // This _cannot_ be inlined into the match. If it is, the lock will be held\n        // open through the whole match, resulting in a deadlock when we try to re-lock\n        // the buffer back to active.\n        let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);\n        let pending_mapping = match mapping {\n            BufferMapState::Waiting(pending_mapping) => pending_mapping,\n            // Mapping cancelled\n            BufferMapState::Idle => return None,\n            // Mapping queued at least twice by map -> unmap -> map\n            // and was already successfully mapped below\n            BufferMapState::Active { .. } => {\n                *self.map_state.lock() = mapping;\n                return None;\n            }\n            _ => panic!(\"No pending mapping.\"),\n        };\n        let status = if pending_mapping.range.start != pending_mapping.range.end {\n            let host = pending_mapping.op.host;\n            let size = pending_mapping.range.end - pending_mapping.range.start;\n            match crate::device::map_buffer(\n                self,\n                pending_mapping.range.start,\n                size,\n                host,\n                snatch_guard,\n            ) {\n                Ok(mapping) => {\n                    *self.map_state.lock() = BufferMapState::Active {\n                        mapping,\n                        range: pending_mapping.range.clone(),\n                        host,\n                    };\n                    Ok(())\n                }\n                Err(e) => Err(e),\n            }\n        } else {\n            *self.map_state.lock() = BufferMapState::Active {\n                mapping: hal::BufferMapping {\n                    ptr: NonNull::dangling(),\n                    is_coherent: true,\n                },\n                range: pending_mapping.range,\n                host: pending_mapping.op.host,\n            };\n            Ok(())\n        };\n        Some((pending_mapping.op, status))\n    }\n\n    // Note: This must not be called while holding a lock.\n    pub fn unmap(self: &Arc<Self>) -> Result<(), BufferAccessError> {\n        if let Some((mut operation, status)) = self.unmap_inner()? {\n            if let Some(callback) = operation.callback.take() {\n                callback(status);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn unmap_inner(self: &Arc<Self>) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {\n        let device = &self.device;\n        let snatch_guard = device.snatchable_lock.read();\n        let raw_buf = self.try_raw(&snatch_guard)?;\n        match mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle) {\n            BufferMapState::Init { staging_buffer } => {\n                #[cfg(feature = \"trace\")]\n                if let Some(ref mut trace) = *device.trace.lock() {\n                    use crate::device::trace::{DataKind, IntoTrace};\n\n                    let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());\n                    trace.add(trace::Action::WriteBuffer {\n                        id: self.to_trace(),\n                        data,\n                        // NOTE: `self.size` here corresponds to `data`'s actual length.\n                        offset: 0,\n                        size: self.size,\n                        queued: true,\n                    });\n                }\n\n                let staging_buffer = staging_buffer.flush();\n\n                if let Some(queue) = device.get_queue() {\n                    let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {\n                        src_offset: 0,\n                        dst_offset: 0,\n                        size,\n                    });\n                    let transition_src = hal::BufferBarrier {\n                        buffer: staging_buffer.raw(),\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::MAP_WRITE,\n                            to: wgt::BufferUses::COPY_SRC,\n                        },\n                    };\n                    let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {\n                        buffer: raw_buf,\n                        usage: hal::StateTransition {\n                            from: wgt::BufferUses::empty(),\n                            to: wgt::BufferUses::COPY_DST,\n                        },\n                    };\n                    let mut pending_writes = queue.pending_writes.lock();\n                    let encoder = pending_writes.activate();\n                    unsafe {\n                        encoder.transition_buffers(&[transition_src, transition_dst]);\n                        if self.size > 0 {\n                            encoder.copy_buffer_to_buffer(\n                                staging_buffer.raw(),\n                                raw_buf,\n                                region.as_slice(),\n                            );\n                        }\n                    }\n                    pending_writes.consume(staging_buffer);\n                    pending_writes.insert_buffer(self);\n                }\n            }\n            BufferMapState::Idle => {\n                return Err(BufferAccessError::NotMapped);\n            }\n            BufferMapState::Waiting(pending) => {\n                return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));\n            }\n            BufferMapState::Active {\n                mapping,\n                range,\n                host,\n            } => {\n                if host == HostMap::Write {\n                    #[cfg(feature = \"trace\")]\n                    if let Some(ref mut trace) = *device.trace.lock() {\n                        use crate::device::trace::{DataKind, IntoTrace};\n\n                        let size = range.end - range.start;\n                        let data = trace.make_binary(DataKind::Bin, unsafe {\n                            core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)\n                        });\n                        trace.add(trace::Action::WriteBuffer {\n                            id: self.to_trace(),\n                            data,\n                            offset: range.start,\n                            size,\n                            queued: false,\n                        });\n                    }\n                    if !mapping.is_coherent {\n                        unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };\n                    }\n                }\n                unsafe { device.raw().unmap_buffer(raw_buf) };\n            }\n        }\n        Ok(None)\n    }\n\n    pub fn destroy(self: &Arc<Self>) {\n        let device = &self.device;\n\n        let temp = {\n            let mut snatch_guard = device.snatchable_lock.write();\n\n            let raw = match self.raw.snatch(&mut snatch_guard) {\n                Some(raw) => raw,\n                None => {\n                    // Per spec, it is valid to call `destroy` multiple times.\n                    return;\n                }\n            };\n\n            let timestamp_normalization_bind_group = self\n                .timestamp_normalization_bind_group\n                .snatch(&mut snatch_guard);\n\n            let indirect_validation_bind_groups = self\n                .indirect_validation_bind_groups\n                .snatch(&mut snatch_guard);\n\n            drop(snatch_guard);\n\n            let bind_groups = {\n                let mut guard = self.bind_groups.lock();\n                mem::take(&mut *guard)\n            };\n\n            queue::TempResource::DestroyedBuffer(DestroyedBuffer {\n                raw: ManuallyDrop::new(raw),\n                device: Arc::clone(&self.device),\n                label: self.label().to_owned(),\n                bind_groups,\n                timestamp_normalization_bind_group,\n                indirect_validation_bind_groups,\n            })\n        };\n\n        if let Some(queue) = device.get_queue() {\n            let mut pending_writes = queue.pending_writes.lock();\n            if pending_writes.contains_buffer(self) {\n                pending_writes.consume_temp(temp);\n            } else {\n                let mut life_lock = queue.lock_life();\n                let last_submit_index = life_lock.get_buffer_latest_submission_index(self);\n                if let Some(last_submit_index) = last_submit_index {\n                    life_lock.schedule_resource_destruction(temp, last_submit_index);\n                }\n            }\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateBufferError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Failed to map buffer while creating: {0}\")]\n    AccessError(#[from] BufferAccessError),\n    #[error(\"Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`\")]\n    UnalignedSize,\n    #[error(\"Invalid usage flags {0:?}\")]\n    InvalidUsage(wgt::BufferUsages),\n    #[error(\"`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}\")]\n    UsageMismatch(wgt::BufferUsages),\n    #[error(\"Buffer size {requested} is greater than the maximum buffer size ({maximum})\")]\n    MaxBufferSize { requested: u64, maximum: u64 },\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(\"Failed to create bind group for indirect buffer validation: {0}\")]\n    IndirectValidationBindGroup(DeviceError),\n}\n\ncrate::impl_resource_type!(Buffer);\ncrate::impl_labeled!(Buffer);\ncrate::impl_parent_device!(Buffer);\ncrate::impl_storage_item!(Buffer);\ncrate::impl_trackable!(Buffer);\n\nimpl WebGpuError for CreateBufferError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::AccessError(e) => e.webgpu_error_type(),\n            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n            Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n\n            Self::UnalignedSize\n            | Self::InvalidUsage(_)\n            | Self::UsageMismatch(_)\n            | Self::MaxBufferSize { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n/// A buffer that has been marked as destroyed and is staged for actual deletion soon.\n#[derive(Debug)]\npub struct DestroyedBuffer {\n    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    device: Arc<Device>,\n    label: String,\n    bind_groups: WeakVec<BindGroup>,\n    timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,\n    indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,\n}\n\nimpl DestroyedBuffer {\n    pub fn label(&self) -> &dyn fmt::Debug {\n        &self.label\n    }\n}\n\nimpl Drop for DestroyedBuffer {\n    fn drop(&mut self) {\n        let mut deferred = self.device.deferred_destroy.lock();\n        deferred.push(DeferredDestroy::BindGroups(mem::take(\n            &mut self.bind_groups,\n        )));\n        drop(deferred);\n\n        if let Some(raw) = self.timestamp_normalization_bind_group.take() {\n            raw.dispose(self.device.raw());\n        }\n\n        if let Some(raw) = self.indirect_validation_bind_groups.take() {\n            raw.dispose(self.device.raw());\n        }\n\n        resource_log!(\"Destroy raw Buffer (destroyed) {:?}\", self.label());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            hal::DynDevice::destroy_buffer(self.device.raw(), raw);\n        }\n    }\n}\n\n#[cfg(send_sync)]\nunsafe impl Send for StagingBuffer {}\n#[cfg(send_sync)]\nunsafe impl Sync for StagingBuffer {}\n\n/// A temporary buffer, consumed by the command that uses it.\n///\n/// A [`StagingBuffer`] is designed for one-shot uploads of data to the GPU. It\n/// is always created mapped, and the command that uses it destroys the buffer\n/// when it is done.\n///\n/// [`StagingBuffer`]s can be created with [`queue_create_staging_buffer`] and\n/// used with [`queue_write_staging_buffer`]. They are also used internally by\n/// operations like [`queue_write_texture`] that need to upload data to the GPU,\n/// but that don't belong to any particular wgpu command buffer.\n///\n/// Used `StagingBuffer`s are accumulated in [`Device::pending_writes`], to be\n/// freed once their associated operation's queue submission has finished\n/// execution.\n///\n/// [`queue_create_staging_buffer`]: crate::global::Global::queue_create_staging_buffer\n/// [`queue_write_staging_buffer`]: crate::global::Global::queue_write_staging_buffer\n/// [`queue_write_texture`]: crate::global::Global::queue_write_texture\n/// [`Device::pending_writes`]: crate::device::Device\n#[derive(Debug)]\npub struct StagingBuffer {\n    raw: Box<dyn hal::DynBuffer>,\n    device: Arc<Device>,\n    pub(crate) size: wgt::BufferSize,\n    is_coherent: bool,\n    ptr: NonNull<u8>,\n}\n\nimpl StagingBuffer {\n    pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {\n        profiling::scope!(\"StagingBuffer::new\");\n        let stage_desc = hal::BufferDescriptor {\n            label: hal_label(Some(\"(wgpu internal) Staging\"), device.instance_flags),\n            size: size.get(),\n            usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,\n            memory_flags: hal::MemoryFlags::TRANSIENT,\n        };\n\n        let raw = unsafe { device.raw().create_buffer(&stage_desc) }\n            .map_err(|e| device.handle_hal_error(e))?;\n        let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }\n            .map_err(|e| device.handle_hal_error(e))?;\n\n        let staging_buffer = StagingBuffer {\n            raw,\n            device: device.clone(),\n            size,\n            is_coherent: mapping.is_coherent,\n            ptr: mapping.ptr,\n        };\n\n        Ok(staging_buffer)\n    }\n\n    /// SAFETY: You must not call any functions of `self`\n    /// until you stopped using the returned pointer.\n    pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {\n        self.ptr\n    }\n\n    #[cfg(feature = \"trace\")]\n    pub(crate) fn get_data(&self) -> &[u8] {\n        unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }\n    }\n\n    pub(crate) fn write_zeros(&mut self) {\n        unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };\n    }\n\n    pub(crate) fn write(&mut self, data: &[u8]) {\n        assert!(data.len() >= self.size.get() as usize);\n        // SAFETY: With the assert above, all of `copy_nonoverlapping`'s\n        // requirements are satisfied.\n        unsafe {\n            core::ptr::copy_nonoverlapping(\n                data.as_ptr(),\n                self.ptr.as_ptr(),\n                self.size.get() as usize,\n            );\n        }\n    }\n\n    /// SAFETY: The offsets and size must be in-bounds.\n    pub(crate) unsafe fn write_with_offset(\n        &mut self,\n        data: &[u8],\n        src_offset: isize,\n        dst_offset: isize,\n        size: usize,\n    ) {\n        unsafe {\n            debug_assert!(\n                (src_offset + size as isize) as usize <= data.len(),\n                \"src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}\",\n                src_offset,\n                size,\n                data.len()\n            );\n            core::ptr::copy_nonoverlapping(\n                data.as_ptr().offset(src_offset),\n                self.ptr.as_ptr().offset(dst_offset),\n                size,\n            );\n        }\n    }\n\n    pub(crate) fn flush(self) -> FlushedStagingBuffer {\n        let device = self.device.raw();\n        if !self.is_coherent {\n            #[allow(clippy::single_range_in_vec_init)]\n            unsafe {\n                device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])\n            };\n        }\n        unsafe { device.unmap_buffer(self.raw.as_ref()) };\n\n        let StagingBuffer {\n            raw, device, size, ..\n        } = self;\n\n        FlushedStagingBuffer {\n            raw: ManuallyDrop::new(raw),\n            device,\n            size,\n        }\n    }\n}\n\ncrate::impl_resource_type!(StagingBuffer);\ncrate::impl_storage_item!(StagingBuffer);\n\n#[derive(Debug)]\npub struct FlushedStagingBuffer {\n    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    device: Arc<Device>,\n    pub(crate) size: wgt::BufferSize,\n}\n\nimpl FlushedStagingBuffer {\n    pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {\n        self.raw.as_ref()\n    }\n}\n\nimpl Drop for FlushedStagingBuffer {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw StagingBuffer\");\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe { self.device.raw().destroy_buffer(raw) };\n    }\n}\n\npub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;\n\n#[derive(Debug)]\npub(crate) enum TextureInner {\n    Native {\n        raw: Box<dyn hal::DynTexture>,\n    },\n    Surface {\n        raw: Box<dyn hal::DynSurfaceTexture>,\n    },\n}\n\nimpl TextureInner {\n    pub(crate) fn raw(&self) -> &dyn hal::DynTexture {\n        match self {\n            Self::Native { raw } => raw.as_ref(),\n            Self::Surface { raw, .. } => raw.as_ref().borrow(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub enum TextureClearMode {\n    BufferCopy,\n    // View for clear via RenderPass for every subsurface (mip/layer/slice)\n    RenderPass {\n        clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,\n        is_color: bool,\n    },\n    Surface {\n        clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,\n    },\n    // Texture can't be cleared, attempting to do so will cause panic.\n    // (either because it is impossible for the type of texture or it is being destroyed)\n    None,\n}\n\n#[derive(Debug)]\npub struct Texture {\n    pub(crate) inner: Snatchable<TextureInner>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n    pub(crate) _hal_usage: wgt::TextureUses,\n    pub(crate) format_features: wgt::TextureFormatFeatures,\n    pub(crate) initialization_status: RwLock<TextureInitTracker>,\n    pub(crate) full_range: TextureSelector,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    pub(crate) clear_mode: RwLock<TextureClearMode>,\n    pub(crate) views: Mutex<WeakVec<TextureView>>,\n    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,\n}\n\nimpl Texture {\n    pub(crate) fn new(\n        device: &Arc<Device>,\n        inner: TextureInner,\n        hal_usage: wgt::TextureUses,\n        desc: &TextureDescriptor,\n        format_features: wgt::TextureFormatFeatures,\n        clear_mode: TextureClearMode,\n        init: bool,\n    ) -> Self {\n        Texture {\n            inner: Snatchable::new(inner),\n            device: device.clone(),\n            desc: desc.map_label(|_| ()),\n            _hal_usage: hal_usage,\n            format_features,\n            initialization_status: RwLock::new(\n                rank::TEXTURE_INITIALIZATION_STATUS,\n                if init {\n                    TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())\n                } else {\n                    TextureInitTracker::new(desc.mip_level_count, 0)\n                },\n            ),\n            full_range: TextureSelector {\n                mips: 0..desc.mip_level_count,\n                layers: 0..desc.array_layer_count(),\n            },\n            label: desc.label.to_string(),\n            tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),\n            clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),\n            views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),\n            bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),\n        }\n    }\n\n    /// Checks that the given texture usage contains the required texture usage,\n    /// returns an error otherwise.\n    pub(crate) fn check_usage(\n        &self,\n        expected: wgt::TextureUsages,\n    ) -> Result<(), MissingTextureUsageError> {\n        if self.desc.usage.contains(expected) {\n            Ok(())\n        } else {\n            Err(MissingTextureUsageError {\n                res: self.error_ident(),\n                actual: self.desc.usage,\n                expected,\n            })\n        }\n    }\n}\n\nimpl Drop for Texture {\n    fn drop(&mut self) {\n        match *self.clear_mode.write() {\n            TextureClearMode::Surface {\n                ref mut clear_view, ..\n            } => {\n                // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.\n                let raw = unsafe { ManuallyDrop::take(clear_view) };\n                unsafe {\n                    self.device.raw().destroy_texture_view(raw);\n                }\n            }\n            TextureClearMode::RenderPass {\n                ref mut clear_views,\n                ..\n            } => {\n                clear_views.iter_mut().for_each(|clear_view| {\n                    // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.\n                    let raw = unsafe { ManuallyDrop::take(clear_view) };\n                    unsafe {\n                        self.device.raw().destroy_texture_view(raw);\n                    }\n                });\n            }\n            _ => {}\n        };\n\n        if let Some(TextureInner::Native { raw }) = self.inner.take() {\n            resource_log!(\"Destroy raw {}\", self.error_ident());\n            unsafe {\n                self.device.raw().destroy_texture(raw);\n            }\n        }\n    }\n}\n\nimpl RawResourceAccess for Texture {\n    type DynResource = dyn hal::DynTexture;\n\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {\n        self.inner.get(guard).map(|t| t.raw())\n    }\n}\n\nimpl Texture {\n    pub(crate) fn try_inner<'a>(\n        &'a self,\n        guard: &'a SnatchGuard,\n    ) -> Result<&'a TextureInner, DestroyedResourceError> {\n        self.inner\n            .get(guard)\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n\n    pub(crate) fn check_destroyed(\n        &self,\n        guard: &SnatchGuard,\n    ) -> Result<(), DestroyedResourceError> {\n        self.inner\n            .get(guard)\n            .map(|_| ())\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n\n    pub(crate) fn get_clear_view<'a>(\n        clear_mode: &'a TextureClearMode,\n        desc: &'a wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,\n        mip_level: u32,\n        depth_or_layer: u32,\n    ) -> &'a dyn hal::DynTextureView {\n        match *clear_mode {\n            TextureClearMode::BufferCopy => {\n                panic!(\"Given texture is cleared with buffer copies, not render passes\")\n            }\n            TextureClearMode::None => {\n                panic!(\"Given texture can't be cleared\")\n            }\n            TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),\n            TextureClearMode::RenderPass {\n                ref clear_views, ..\n            } => {\n                let index = if desc.dimension == wgt::TextureDimension::D3 {\n                    (0..mip_level).fold(0, |acc, mip| {\n                        acc + (desc.size.depth_or_array_layers >> mip).max(1)\n                    })\n                } else {\n                    mip_level * desc.size.depth_or_array_layers\n                } + depth_or_layer;\n                clear_views[index as usize].as_ref()\n            }\n        }\n    }\n\n    pub fn destroy(self: &Arc<Self>) {\n        let device = &self.device;\n\n        let temp = {\n            let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) {\n                Some(TextureInner::Native { raw }) => raw,\n                Some(TextureInner::Surface { .. }) => {\n                    return;\n                }\n                None => {\n                    // Per spec, it is valid to call `destroy` multiple times.\n                    return;\n                }\n            };\n\n            let views = {\n                let mut guard = self.views.lock();\n                mem::take(&mut *guard)\n            };\n\n            let bind_groups = {\n                let mut guard = self.bind_groups.lock();\n                mem::take(&mut *guard)\n            };\n\n            queue::TempResource::DestroyedTexture(DestroyedTexture {\n                raw: ManuallyDrop::new(raw),\n                views,\n                clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),\n                bind_groups,\n                device: Arc::clone(&self.device),\n                label: self.label().to_owned(),\n            })\n        };\n\n        if let Some(queue) = device.get_queue() {\n            let mut pending_writes = queue.pending_writes.lock();\n            if pending_writes.contains_texture(self) {\n                pending_writes.consume_temp(temp);\n            } else {\n                let mut life_lock = queue.lock_life();\n                let last_submit_index = life_lock.get_texture_latest_submission_index(self);\n                if let Some(last_submit_index) = last_submit_index {\n                    life_lock.schedule_resource_destruction(temp, last_submit_index);\n                }\n            }\n        }\n    }\n}\n\n/// A texture that has been marked as destroyed and is staged for actual deletion soon.\n#[derive(Debug)]\npub struct DestroyedTexture {\n    raw: ManuallyDrop<Box<dyn hal::DynTexture>>,\n    views: WeakVec<TextureView>,\n    clear_mode: TextureClearMode,\n    bind_groups: WeakVec<BindGroup>,\n    device: Arc<Device>,\n    label: String,\n}\n\nimpl DestroyedTexture {\n    pub fn label(&self) -> &dyn fmt::Debug {\n        &self.label\n    }\n}\n\nimpl Drop for DestroyedTexture {\n    fn drop(&mut self) {\n        let device = &self.device;\n\n        let mut deferred = device.deferred_destroy.lock();\n        deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));\n        deferred.push(DeferredDestroy::BindGroups(mem::take(\n            &mut self.bind_groups,\n        )));\n        drop(deferred);\n\n        match mem::replace(&mut self.clear_mode, TextureClearMode::None) {\n            TextureClearMode::RenderPass { clear_views, .. } => {\n                for clear_view in clear_views {\n                    let raw = ManuallyDrop::into_inner(clear_view);\n                    unsafe { self.device.raw().destroy_texture_view(raw) };\n                }\n            }\n            TextureClearMode::Surface { clear_view } => {\n                let raw = ManuallyDrop::into_inner(clear_view);\n                unsafe { self.device.raw().destroy_texture_view(raw) };\n            }\n            _ => (),\n        }\n\n        resource_log!(\"Destroy raw Texture (destroyed) {:?}\", self.label());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_texture(raw);\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub enum TextureErrorDimension {\n    X,\n    Y,\n    Z,\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum TextureDimensionError {\n    #[error(\"Dimension {0:?} is zero\")]\n    Zero(TextureErrorDimension),\n    #[error(\"Dimension {dim:?} value {given} exceeds the limit of {limit}\")]\n    LimitExceeded {\n        dim: TextureErrorDimension,\n        given: u32,\n        limit: u32,\n    },\n    #[error(\"Sample count {0} is invalid\")]\n    InvalidSampleCount(u32),\n    #[error(\"Width {width} is not a multiple of {format:?}'s block width ({block_width})\")]\n    NotMultipleOfBlockWidth {\n        width: u32,\n        block_width: u32,\n        format: wgt::TextureFormat,\n    },\n    #[error(\"Height {height} is not a multiple of {format:?}'s block height ({block_height})\")]\n    NotMultipleOfBlockHeight {\n        height: u32,\n        block_height: u32,\n        format: wgt::TextureFormat,\n    },\n    #[error(\n        \"Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})\"\n    )]\n    WidthNotMultipleOf {\n        width: u32,\n        multiple: u32,\n        format: wgt::TextureFormat,\n    },\n    #[error(\"Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})\")]\n    HeightNotMultipleOf {\n        height: u32,\n        multiple: u32,\n        format: wgt::TextureFormat,\n    },\n    #[error(\"Multisampled texture depth or array layers must be 1, got {0}\")]\n    MultisampledDepthOrArrayLayer(u32),\n}\n\nimpl WebGpuError for TextureDimensionError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateTextureError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    CreateTextureView(#[from] CreateTextureViewError),\n    #[error(\"Invalid usage flags {0:?}\")]\n    InvalidUsage(wgt::TextureUsages),\n    #[error(\"Texture usage {0:?} is not compatible with texture usage {1:?}\")]\n    IncompatibleUsage(wgt::TextureUsages, wgt::TextureUsages),\n    #[error(transparent)]\n    InvalidDimension(#[from] TextureDimensionError),\n    #[error(\"Depth texture ({1:?}) can't be created as {0:?}\")]\n    InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),\n    #[error(\"Compressed texture ({1:?}) can't be created as {0:?}\")]\n    InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),\n    #[error(\n        \"Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}\"\n    )]\n    InvalidMipLevelCount { requested: u32, maximum: u32 },\n    #[error(\n        \"Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}\",\n        downlevel_suffix = if *.2 { \" due to downlevel restrictions\" } else { \"\" }\n    )]\n    InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),\n    #[error(\"The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.\")]\n    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),\n    #[error(\"Texture usages {0:?} are not allowed on a texture of dimensions {1:?}\")]\n    InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),\n    #[error(\"Texture usage STORAGE_BINDING is not allowed for multisampled textures\")]\n    InvalidMultisampledStorageBinding,\n    #[error(\"Format {0:?} does not support multisampling\")]\n    InvalidMultisampledFormat(wgt::TextureFormat),\n    #[error(\"Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.\")]\n    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),\n    #[error(\"Multisampled textures must have RENDER_ATTACHMENT usage\")]\n    MultisampledNotRenderAttachment,\n    #[error(\"Texture format {0:?} can't be used due to missing features\")]\n    MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),\n    #[error(transparent)]\n    MissingDownlevelFlags(#[from] MissingDownlevelFlags),\n}\n\ncrate::impl_resource_type!(Texture);\ncrate::impl_labeled!(Texture);\ncrate::impl_parent_device!(Texture);\ncrate::impl_storage_item!(Texture);\ncrate::impl_trackable!(Texture);\n\nimpl Borrow<TextureSelector> for Texture {\n    fn borrow(&self) -> &TextureSelector {\n        &self.full_range\n    }\n}\n\nimpl WebGpuError for CreateTextureError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::CreateTextureView(e) => e.webgpu_error_type(),\n            Self::InvalidDimension(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(_, e) => e.webgpu_error_type(),\n            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),\n\n            Self::InvalidUsage(_)\n            | Self::IncompatibleUsage(_, _)\n            | Self::InvalidDepthDimension(_, _)\n            | Self::InvalidCompressedDimension(_, _)\n            | Self::InvalidMipLevelCount { .. }\n            | Self::InvalidFormatUsages(_, _, _)\n            | Self::InvalidViewFormat(_, _)\n            | Self::InvalidDimensionUsages(_, _)\n            | Self::InvalidMultisampledStorageBinding\n            | Self::InvalidMultisampledFormat(_)\n            | Self::InvalidSampleCount(..)\n            | Self::MultisampledNotRenderAttachment => ErrorType::Validation,\n        }\n    }\n}\n\n/// Describes a [`TextureView`].\n#[derive(Clone, Debug, Default, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextureViewDescriptor<'a> {\n    /// Debug label of the texture view.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// Format of the texture view, or `None` for the same format as the texture\n    /// itself.\n    ///\n    /// At this time, it must be the same the underlying format of the texture.\n    pub format: Option<wgt::TextureFormat>,\n    /// The dimension of the texture view.\n    ///\n    /// - For 1D textures, this must be `D1`.\n    /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`.\n    /// - For 3D textures it must be `D3`.\n    pub dimension: Option<wgt::TextureViewDimension>,\n    /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture.\n    /// If not provided, defaults to the full set of usage flags of the texture.\n    pub usage: Option<wgt::TextureUsages>,\n    /// Range within the texture that is accessible via this view.\n    pub range: wgt::ImageSubresourceRange,\n}\n\n#[derive(Debug)]\npub(crate) struct HalTextureViewDescriptor {\n    pub texture_format: wgt::TextureFormat,\n    pub format: wgt::TextureFormat,\n    pub usage: wgt::TextureUsages,\n    pub dimension: wgt::TextureViewDimension,\n    pub range: wgt::ImageSubresourceRange,\n}\n\nimpl HalTextureViewDescriptor {\n    pub fn aspects(&self) -> hal::FormatAspects {\n        hal::FormatAspects::new(self.texture_format, self.range.aspect)\n    }\n}\n\n#[derive(Debug, Copy, Clone, Error)]\npub enum TextureViewNotRenderableReason {\n    #[error(\"The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}\")]\n    Usage(wgt::TextureUsages),\n    #[error(\"The dimension of this texture view is not 2D. View dimension: {0:?}\")]\n    Dimension(wgt::TextureViewDimension),\n    #[error(\"This texture view has more than one mipmap level. View mipmap levels: {0:?}\")]\n    MipLevelCount(u32),\n    #[error(\"This texture view has more than one array layer. View array layers: {0:?}\")]\n    ArrayLayerCount(u32),\n    #[error(\n        \"The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}\"\n    )]\n    Aspects(hal::FormatAspects),\n}\n\n#[derive(Debug)]\npub struct TextureView {\n    pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,\n    // if it's a surface texture - it's none\n    pub(crate) parent: Arc<Texture>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) desc: HalTextureViewDescriptor,\n    pub(crate) format_features: wgt::TextureFormatFeatures,\n    /// This is `Err` only if the texture view is not renderable\n    pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,\n    pub(crate) samples: u32,\n    pub(crate) selector: TextureSelector,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n}\n\nimpl Drop for TextureView {\n    fn drop(&mut self) {\n        if let Some(raw) = self.raw.take() {\n            resource_log!(\"Destroy raw {}\", self.error_ident());\n            unsafe {\n                self.device.raw().destroy_texture_view(raw);\n            }\n        }\n    }\n}\n\nimpl RawResourceAccess for TextureView {\n    type DynResource = dyn hal::DynTextureView;\n\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {\n        self.raw.get(guard).map(|it| it.as_ref())\n    }\n\n    fn try_raw<'a>(\n        &'a self,\n        guard: &'a SnatchGuard,\n    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {\n        self.parent.check_destroyed(guard)?;\n\n        self.raw(guard)\n            .ok_or_else(|| DestroyedResourceError(self.error_ident()))\n    }\n}\n\nimpl TextureView {\n    /// Checks that the given texture usage contains the required texture usage,\n    /// returns an error otherwise.\n    pub(crate) fn check_usage(\n        &self,\n        expected: wgt::TextureUsages,\n    ) -> Result<(), MissingTextureUsageError> {\n        if self.desc.usage.contains(expected) {\n            Ok(())\n        } else {\n            Err(MissingTextureUsageError {\n                res: self.error_ident(),\n                actual: self.desc.usage,\n                expected,\n            })\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateTextureViewError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    DestroyedResource(#[from] DestroyedResourceError),\n    #[error(\"Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`\")]\n    InvalidTextureViewDimension {\n        view: wgt::TextureViewDimension,\n        texture: wgt::TextureDimension,\n    },\n    #[error(\"Texture view format `{0:?}` cannot be used as a render attachment. Make sure the format supports RENDER_ATTACHMENT usage and required device features are enabled.\")]\n    TextureViewFormatNotRenderable(wgt::TextureFormat),\n    #[error(\"Texture view format `{0:?}` cannot be used as a storage binding. Make sure the format supports STORAGE usage and required device features are enabled.\")]\n    TextureViewFormatNotStorage(wgt::TextureFormat),\n    #[error(\"Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)\")]\n    InvalidTextureViewUsage {\n        view: wgt::TextureUsages,\n        texture: wgt::TextureUsages,\n    },\n    #[error(\"Texture view dimension `{0:?}` cannot be used with a multisampled texture\")]\n    InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),\n    #[error(\n        \"TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6.\"\n    )]\n    InvalidCubemapTextureDepth { depth: u32 },\n    #[error(\"TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.\")]\n    InvalidCubemapArrayTextureDepth { depth: u32 },\n    #[error(\"Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`\")]\n    InvalidCubeTextureViewSize,\n    #[error(\"Mip level count is 0\")]\n    ZeroMipLevelCount,\n    #[error(\"Array layer count is 0\")]\n    ZeroArrayLayerCount,\n    #[error(\n        \"TextureView spans mip levels [{base_mip_level}, {end_mip_level}) \\\n        (mipLevelCount {mip_level_count}) but the texture view only has {total} total mip levels\",\n        end_mip_level = base_mip_level + mip_level_count\n    )]\n    TooManyMipLevels {\n        base_mip_level: u32,\n        mip_level_count: u32,\n        total: u32,\n    },\n    #[error(\n        \"TextureView spans array layers [{base_array_layer}, {end_array_layer}) \\\n         (arrayLayerCount {array_layer_count}) but the texture view only has {total} total layers\",\n        end_array_layer = base_array_layer + array_layer_count\n    )]\n    TooManyArrayLayers {\n        base_array_layer: u32,\n        array_layer_count: u32,\n        total: u32,\n    },\n    #[error(\"Requested array layer count {requested} is not valid for the target view dimension {dim:?}\")]\n    InvalidArrayLayerCount {\n        requested: u32,\n        dim: wgt::TextureViewDimension,\n    },\n    #[error(\n        \"Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}\"\n    )]\n    InvalidAspect {\n        texture_format: wgt::TextureFormat,\n        requested_aspect: wgt::TextureAspect,\n    },\n    #[error(\n        \"Trying to create a view of format {view:?} of a texture with format {texture:?}, \\\n         but this view format is not present in the texture's viewFormat array\"\n    )]\n    FormatReinterpretation {\n        texture: wgt::TextureFormat,\n        view: wgt::TextureFormat,\n    },\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n}\n\nimpl WebGpuError for CreateTextureViewError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n\n            Self::InvalidTextureViewDimension { .. }\n            | Self::InvalidResource(_)\n            | Self::InvalidMultisampledTextureViewDimension(_)\n            | Self::InvalidCubemapTextureDepth { .. }\n            | Self::InvalidCubemapArrayTextureDepth { .. }\n            | Self::InvalidCubeTextureViewSize\n            | Self::ZeroMipLevelCount\n            | Self::ZeroArrayLayerCount\n            | Self::TooManyMipLevels { .. }\n            | Self::TooManyArrayLayers { .. }\n            | Self::InvalidArrayLayerCount { .. }\n            | Self::InvalidAspect { .. }\n            | Self::FormatReinterpretation { .. }\n            | Self::DestroyedResource(_)\n            | Self::TextureViewFormatNotRenderable(_)\n            | Self::TextureViewFormatNotStorage(_)\n            | Self::InvalidTextureViewUsage { .. }\n            | Self::MissingFeatures(_) => ErrorType::Validation,\n        }\n    }\n}\n\ncrate::impl_resource_type!(TextureView);\ncrate::impl_labeled!(TextureView);\ncrate::impl_parent_device!(TextureView);\ncrate::impl_storage_item!(TextureView);\n\npub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;\n\n#[derive(Debug)]\npub struct ExternalTexture {\n    pub(crate) device: Arc<Device>,\n    /// Between 1 and 3 (inclusive) planes of texture data.\n    pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,\n    /// Buffer containing a [`crate::device::resource::ExternalTextureParams`]\n    /// describing the external texture.\n    pub(crate) params: Arc<Buffer>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n}\n\nimpl Drop for ExternalTexture {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n    }\n}\n\nimpl ExternalTexture {\n    pub fn destroy(self: &Arc<Self>) {\n        self.params.destroy();\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateExternalTextureError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n    #[error(transparent)]\n    InvalidResource(#[from] InvalidResourceError),\n    #[error(transparent)]\n    CreateBuffer(#[from] CreateBufferError),\n    #[error(transparent)]\n    QueueWrite(#[from] queue::QueueWriteError),\n    #[error(\"External texture format {format:?} expects {expected} planes, but given {provided}\")]\n    IncorrectPlaneCount {\n        format: wgt::ExternalTextureFormat,\n        expected: usize,\n        provided: usize,\n    },\n    #[error(\"External texture planes cannot be multisampled, but given view with samples = {0}\")]\n    InvalidPlaneMultisample(u32),\n    #[error(\"External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})\")]\n    InvalidPlaneSampleType {\n        format: wgt::TextureFormat,\n        sample_type: wgt::TextureSampleType,\n    },\n    #[error(\"External texture planes expect 2D dimension, but given view with dimension = {0:?}\")]\n    InvalidPlaneDimension(wgt::TextureViewDimension),\n    #[error(transparent)]\n    MissingTextureUsage(#[from] MissingTextureUsageError),\n    #[error(\"External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)\",\n        provided.components())]\n    InvalidPlaneFormat {\n        format: wgt::ExternalTextureFormat,\n        plane: usize,\n        expected: u8,\n        provided: wgt::TextureFormat,\n    },\n}\n\nimpl WebGpuError for CreateExternalTextureError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            CreateExternalTextureError::Device(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),\n            CreateExternalTextureError::IncorrectPlaneCount { .. }\n            | CreateExternalTextureError::InvalidPlaneMultisample(_)\n            | CreateExternalTextureError::InvalidPlaneSampleType { .. }\n            | CreateExternalTextureError::InvalidPlaneDimension(_)\n            | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,\n        }\n    }\n}\n\ncrate::impl_resource_type!(ExternalTexture);\ncrate::impl_labeled!(ExternalTexture);\ncrate::impl_parent_device!(ExternalTexture);\ncrate::impl_storage_item!(ExternalTexture);\ncrate::impl_trackable!(ExternalTexture);\n\n/// Describes a [`Sampler`]\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct SamplerDescriptor<'a> {\n    /// Debug label of the sampler.\n    ///\n    /// This will show up in graphics debuggers for easy identification.\n    pub label: Label<'a>,\n    /// How to deal with out of bounds accesses in the u (i.e. x) direction\n    pub address_modes: [wgt::AddressMode; 3],\n    /// How to filter the texture when it needs to be magnified (made larger)\n    pub mag_filter: wgt::FilterMode,\n    /// How to filter the texture when it needs to be minified (made smaller)\n    pub min_filter: wgt::FilterMode,\n    /// How to filter between mip map levels\n    pub mipmap_filter: wgt::MipmapFilterMode,\n    /// Minimum level of detail (i.e. mip level) to use\n    pub lod_min_clamp: f32,\n    /// Maximum level of detail (i.e. mip level) to use\n    pub lod_max_clamp: f32,\n    /// If this is enabled, this is a comparison sampler using the given comparison function.\n    pub compare: Option<wgt::CompareFunction>,\n    /// Must be at least 1. If this is not 1, all filter modes must be linear.\n    pub anisotropy_clamp: u16,\n    /// Border color to use when address_mode is\n    /// [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)\n    pub border_color: Option<wgt::SamplerBorderColor>,\n}\n\n#[derive(Debug)]\npub struct Sampler {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynSampler>>,\n    pub(crate) device: Arc<Device>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    /// `true` if this is a comparison sampler\n    pub(crate) comparison: bool,\n    /// `true` if this is a filtering sampler\n    pub(crate) filtering: bool,\n}\n\nimpl Drop for Sampler {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_sampler(raw);\n        }\n    }\n}\n\nimpl Sampler {\n    pub(crate) fn raw(&self) -> &dyn hal::DynSampler {\n        self.raw.as_ref()\n    }\n}\n\n#[derive(Copy, Clone)]\npub enum SamplerFilterErrorType {\n    MagFilter,\n    MinFilter,\n    MipmapFilter,\n}\n\nimpl fmt::Debug for SamplerFilterErrorType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            SamplerFilterErrorType::MagFilter => write!(f, \"magFilter\"),\n            SamplerFilterErrorType::MinFilter => write!(f, \"minFilter\"),\n            SamplerFilterErrorType::MipmapFilter => write!(f, \"mipmapFilter\"),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateSamplerError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"Invalid lodMinClamp: {0}. Must be greater or equal to 0.0\")]\n    InvalidLodMinClamp(f32),\n    #[error(\"Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).\")]\n    InvalidLodMaxClamp {\n        lod_min_clamp: f32,\n        lod_max_clamp: f32,\n    },\n    #[error(\"Invalid anisotropic clamp: {0}. Must be at least 1.\")]\n    InvalidAnisotropy(u16),\n    #[error(\"Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.\")]\n    InvalidFilterModeWithAnisotropy {\n        filter_type: SamplerFilterErrorType,\n        filter_mode: wgt::FilterMode,\n        anisotropic_clamp: u16,\n    },\n    #[error(\"Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.\")]\n    InvalidMipmapFilterModeWithAnisotropy {\n        filter_type: SamplerFilterErrorType,\n        filter_mode: wgt::MipmapFilterMode,\n        anisotropic_clamp: u16,\n    },\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n}\n\ncrate::impl_resource_type!(Sampler);\ncrate::impl_labeled!(Sampler);\ncrate::impl_parent_device!(Sampler);\ncrate::impl_storage_item!(Sampler);\ncrate::impl_trackable!(Sampler);\n\nimpl WebGpuError for CreateSamplerError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n\n            Self::InvalidLodMinClamp(_)\n            | Self::InvalidLodMaxClamp { .. }\n            | Self::InvalidAnisotropy(_)\n            | Self::InvalidFilterModeWithAnisotropy { .. }\n            | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Error)]\n#[non_exhaustive]\npub enum CreateQuerySetError {\n    #[error(transparent)]\n    Device(#[from] DeviceError),\n    #[error(\"QuerySets cannot be made with zero queries\")]\n    ZeroCount,\n    #[error(\"{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.\")]\n    TooManyQueries { count: u32, maximum: u32 },\n    #[error(transparent)]\n    MissingFeatures(#[from] MissingFeatures),\n}\n\nimpl WebGpuError for CreateQuerySetError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        match self {\n            Self::Device(e) => e.webgpu_error_type(),\n            Self::MissingFeatures(e) => e.webgpu_error_type(),\n\n            Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,\n        }\n    }\n}\n\npub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;\n\n#[derive(Debug)]\npub struct QuerySet {\n    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,\n    pub(crate) device: Arc<Device>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    pub(crate) desc: wgt::QuerySetDescriptor<()>,\n}\n\nimpl Drop for QuerySet {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe {\n            self.device.raw().destroy_query_set(raw);\n        }\n    }\n}\n\ncrate::impl_resource_type!(QuerySet);\ncrate::impl_labeled!(QuerySet);\ncrate::impl_parent_device!(QuerySet);\ncrate::impl_storage_item!(QuerySet);\ncrate::impl_trackable!(QuerySet);\n\nimpl QuerySet {\n    pub(crate) fn raw(&self) -> &dyn hal::DynQuerySet {\n        self.raw.as_ref()\n    }\n}\n\npub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;\npub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;\n\npub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;\n\n#[cfg(send_sync)]\npub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;\n#[cfg(not(send_sync))]\npub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;\n\npub(crate) struct BlasPendingCompact {\n    pub(crate) op: Option<BlasCompactCallback>,\n    // hold the parent alive while the mapping is active\n    pub(crate) _parent_blas: Arc<Blas>,\n}\n\nimpl fmt::Debug for BlasPendingCompact {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"BlasPendingCompact\")\n            .field(\"op\", &())\n            .field(\"_parent_blas\", &self._parent_blas)\n            .finish()\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum BlasCompactState {\n    /// Created from a compact operation.\n    Compacted,\n    /// Waiting for GPU to be done before mapping to get compacted size\n    Waiting(BlasPendingCompact),\n    /// Ready to be compacted\n    Ready { size: wgt::BufferAddress },\n    /// Ready to prepare to compact.\n    Idle,\n}\n\n#[cfg(send_sync)]\nunsafe impl Send for BlasCompactState {}\n#[cfg(send_sync)]\nunsafe impl Sync for BlasCompactState {}\n\n#[derive(Debug)]\npub struct Blas {\n    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) size_info: hal::AccelerationStructureBuildSizes,\n    pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,\n    pub(crate) flags: wgt::AccelerationStructureFlags,\n    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,\n    pub(crate) built_index: RwLock<Option<NonZeroU64>>,\n    pub(crate) handle: u64,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n    pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,\n    pub(crate) compacted_state: Mutex<BlasCompactState>,\n}\n\nimpl Drop for Blas {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw {}\", self.error_ident());\n        // SAFETY: We are in the Drop impl, and we don't use self.raw or self.compaction_buffer anymore after this point.\n        if let Some(raw) = self.raw.take() {\n            unsafe {\n                self.device.raw().destroy_acceleration_structure(raw);\n            }\n        }\n        if let Some(mut raw) = self.compaction_buffer.take() {\n            unsafe {\n                self.device\n                    .raw()\n                    .destroy_buffer(ManuallyDrop::take(&mut raw))\n            }\n        }\n    }\n}\n\nimpl RawResourceAccess for Blas {\n    type DynResource = dyn hal::DynAccelerationStructure;\n\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {\n        self.raw.get(guard).map(|it| it.as_ref())\n    }\n}\n\nimpl Blas {\n    pub(crate) fn prepare_compact_async(\n        self: &Arc<Self>,\n        op: Option<BlasCompactCallback>,\n    ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {\n        let device = &self.device;\n        if let Err(e) = device.check_is_valid() {\n            return Err((op, e.into()));\n        }\n\n        if self.built_index.read().is_none() {\n            return Err((op, BlasPrepareCompactError::NotBuilt));\n        }\n\n        if !self\n            .flags\n            .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)\n        {\n            return Err((op, BlasPrepareCompactError::CompactionUnsupported));\n        }\n\n        let mut state = self.compacted_state.lock();\n        *state = match *state {\n            BlasCompactState::Compacted => {\n                return Err((op, BlasPrepareCompactError::DoubleCompaction))\n            }\n            BlasCompactState::Waiting(_) => {\n                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))\n            }\n            BlasCompactState::Ready { .. } => {\n                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))\n            }\n            BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {\n                op,\n                _parent_blas: self.clone(),\n            }),\n        };\n\n        let submit_index = if let Some(queue) = device.get_queue() {\n            queue.lock_life().prepare_compact(self).unwrap_or(0) // '0' means no wait is necessary\n        } else {\n            // We can safely unwrap below since we just set the `compacted_state` to `BlasCompactState::Waiting`.\n            let (mut callback, status) = self.read_back_compact_size().unwrap();\n            if let Some(callback) = callback.take() {\n                callback(status);\n            }\n            0\n        };\n\n        Ok(submit_index)\n    }\n\n    /// This function returns [`None`] only if [`Self::compacted_state`] is not [`BlasCompactState::Waiting`].\n    #[must_use]\n    pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {\n        let mut state = self.compacted_state.lock();\n        let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {\n            BlasCompactState::Waiting(pending_mapping) => pending_mapping,\n            // Compaction cancelled e.g. by rebuild\n            BlasCompactState::Idle => return None,\n            BlasCompactState::Ready { .. } => {\n                unreachable!(\"This should be validated out by `prepare_for_compaction`\")\n            }\n            _ => panic!(\"No pending mapping.\"),\n        };\n        let status = {\n            let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();\n            unsafe {\n                let map_res = self.device.raw().map_buffer(\n                    compaction_buffer,\n                    0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,\n                );\n                match map_res {\n                    Ok(mapping) => {\n                        if !mapping.is_coherent {\n                            // Clippy complains about this because it might not be intended, but\n                            // this is intentional.\n                            #[expect(clippy::single_range_in_vec_init)]\n                            self.device.raw().invalidate_mapped_ranges(\n                                compaction_buffer,\n                                &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],\n                            );\n                        }\n                        let size = core::ptr::read_unaligned(\n                            mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),\n                        );\n                        self.device.raw().unmap_buffer(compaction_buffer);\n                        if self.size_info.acceleration_structure_size != 0 {\n                            debug_assert_ne!(size, 0);\n                        }\n                        *state = BlasCompactState::Ready { size };\n                        Ok(())\n                    }\n                    Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),\n                }\n            }\n        };\n        Some((pending_compact.op, status))\n    }\n}\n\ncrate::impl_resource_type!(Blas);\ncrate::impl_labeled!(Blas);\ncrate::impl_parent_device!(Blas);\ncrate::impl_storage_item!(Blas);\ncrate::impl_trackable!(Blas);\n\n#[derive(Debug)]\npub struct Tlas {\n    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,\n    pub(crate) device: Arc<Device>,\n    pub(crate) size_info: hal::AccelerationStructureBuildSizes,\n    pub(crate) max_instance_count: u32,\n    pub(crate) flags: wgt::AccelerationStructureFlags,\n    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,\n    pub(crate) built_index: RwLock<Option<NonZeroU64>>,\n    pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,\n    pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    /// The `label` from the descriptor used to create the resource.\n    pub(crate) label: String,\n    pub(crate) tracking_data: TrackingData,\n}\n\nimpl Drop for Tlas {\n    fn drop(&mut self) {\n        unsafe {\n            resource_log!(\"Destroy raw {}\", self.error_ident());\n            if let Some(structure) = self.raw.take() {\n                self.device.raw().destroy_acceleration_structure(structure);\n            }\n            let buffer = ManuallyDrop::take(&mut self.instance_buffer);\n            self.device.raw().destroy_buffer(buffer);\n        }\n    }\n}\n\nimpl RawResourceAccess for Tlas {\n    type DynResource = dyn hal::DynAccelerationStructure;\n\n    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {\n        self.raw.get(guard).map(|raw| raw.as_ref())\n    }\n}\n\ncrate::impl_resource_type!(Tlas);\ncrate::impl_labeled!(Tlas);\ncrate::impl_parent_device!(Tlas);\ncrate::impl_storage_item!(Tlas);\ncrate::impl_trackable!(Tlas);\n"
  },
  {
    "path": "wgpu-core/src/scratch.rs",
    "content": "use alloc::{boxed::Box, sync::Arc};\nuse core::mem::ManuallyDrop;\n\nuse wgt::BufferUses;\n\nuse crate::device::{Device, DeviceError};\nuse crate::{hal_label, resource_log};\n\n#[derive(Debug)]\npub struct ScratchBuffer {\n    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,\n    device: Arc<Device>,\n}\n\nimpl ScratchBuffer {\n    pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {\n        let raw = unsafe {\n            device\n                .raw()\n                .create_buffer(&hal::BufferDescriptor {\n                    label: hal_label(Some(\"(wgpu) scratch buffer\"), device.instance_flags),\n                    size: size.get(),\n                    usage: BufferUses::ACCELERATION_STRUCTURE_SCRATCH,\n                    memory_flags: hal::MemoryFlags::empty(),\n                })\n                .map_err(DeviceError::from_hal)?\n        };\n        Ok(Self {\n            raw: ManuallyDrop::new(raw),\n            device: device.clone(),\n        })\n    }\n    pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {\n        self.raw.as_ref()\n    }\n}\n\nimpl Drop for ScratchBuffer {\n    fn drop(&mut self) {\n        resource_log!(\"Destroy raw ScratchBuffer\");\n        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.\n        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };\n        unsafe { self.device.raw().destroy_buffer(raw) };\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/snatch.rs",
    "content": "use core::{cell::UnsafeCell, fmt, mem::ManuallyDrop};\n\nuse crate::lock::{rank, RankData, RwLock, RwLockReadGuard, RwLockWriteGuard};\n\n/// A guard that provides read access to snatchable data.\npub struct SnatchGuard<'a>(RwLockReadGuard<'a, ()>);\n/// A guard that allows snatching the snatchable data.\npub struct ExclusiveSnatchGuard<'a>(#[expect(dead_code)] RwLockWriteGuard<'a, ()>);\n\n/// A value that is mostly immutable but can be \"snatched\" if we need to destroy\n/// it early.\n///\n/// In order to safely access the underlying data, the device's global snatchable\n/// lock must be taken. To guarantee it, methods take a read or write guard of that\n/// special lock.\npub struct Snatchable<T> {\n    value: UnsafeCell<Option<T>>,\n}\n\nimpl<T> Snatchable<T> {\n    pub fn new(val: T) -> Self {\n        Snatchable {\n            value: UnsafeCell::new(Some(val)),\n        }\n    }\n\n    #[allow(dead_code)]\n    pub fn empty() -> Self {\n        Snatchable {\n            value: UnsafeCell::new(None),\n        }\n    }\n\n    /// Get read access to the value. Requires a the snatchable lock's read guard.\n    pub fn get<'a>(&'a self, _guard: &'a SnatchGuard) -> Option<&'a T> {\n        unsafe { (*self.value.get()).as_ref() }\n    }\n\n    /// Take the value. Requires a the snatchable lock's write guard.\n    pub fn snatch(&self, _guard: &mut ExclusiveSnatchGuard) -> Option<T> {\n        unsafe { (*self.value.get()).take() }\n    }\n\n    /// Take the value without a guard. This can only be used with exclusive access\n    /// to self, so it does not require locking.\n    ///\n    /// Typically useful in a drop implementation.\n    pub fn take(&mut self) -> Option<T> {\n        self.value.get_mut().take()\n    }\n}\n\n// Can't safely print the contents of a snatchable object without holding\n// the lock.\nimpl<T> fmt::Debug for Snatchable<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"<snatchable>\")\n    }\n}\n\nunsafe impl<T> Sync for Snatchable<T> {}\n\nuse trace::LockTrace;\n#[cfg(all(debug_assertions, feature = \"std\"))]\nmod trace {\n    use core::{cell::Cell, fmt, panic::Location};\n    use std::{backtrace::Backtrace, thread};\n\n    pub(super) struct LockTrace {\n        purpose: &'static str,\n        caller: &'static Location<'static>,\n        backtrace: Backtrace,\n    }\n\n    impl fmt::Display for LockTrace {\n        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n            write!(\n                f,\n                \"a {} lock at {}\\n{}\",\n                self.purpose, self.caller, self.backtrace\n            )\n        }\n    }\n\n    impl LockTrace {\n        #[track_caller]\n        pub(super) fn enter(purpose: &'static str) {\n            let new = LockTrace {\n                purpose,\n                caller: Location::caller(),\n                backtrace: Backtrace::capture(),\n            };\n\n            if let Some(prev) = SNATCH_LOCK_TRACE.take() {\n                let current = thread::current();\n                let name = current.name().unwrap_or(\"<unnamed>\");\n                panic!(\n                    \"thread '{name}' attempted to acquire a snatch lock recursively.\\n\\\n                 - Currently trying to acquire {new}\\n\\\n                 - Previously acquired {prev}\",\n                );\n            } else {\n                SNATCH_LOCK_TRACE.set(Some(new));\n            }\n        }\n\n        pub(super) fn exit() {\n            SNATCH_LOCK_TRACE.take();\n        }\n    }\n\n    std::thread_local! {\n        static SNATCH_LOCK_TRACE: Cell<Option<LockTrace>> = const { Cell::new(None) };\n    }\n}\n#[cfg(not(all(debug_assertions, feature = \"std\")))]\nmod trace {\n    pub(super) struct LockTrace {\n        _private: (),\n    }\n\n    impl LockTrace {\n        pub(super) fn enter(_purpose: &'static str) {}\n        pub(super) fn exit() {}\n    }\n}\n\n/// A Device-global lock for all snatchable data.\npub struct SnatchLock {\n    lock: RwLock<()>,\n}\n\nimpl SnatchLock {\n    /// The safety of `Snatchable::get` and `Snatchable::snatch` rely on their using of the\n    /// right SnatchLock (the one associated to the same device). This method is unsafe\n    /// to force force sers to think twice about creating a SnatchLock. The only place this\n    /// method should be called is when creating the device.\n    pub unsafe fn new(rank: rank::LockRank) -> Self {\n        SnatchLock {\n            lock: RwLock::new(rank, ()),\n        }\n    }\n\n    /// Request read access to snatchable resources.\n    #[track_caller]\n    pub fn read(&self) -> SnatchGuard<'_> {\n        LockTrace::enter(\"read\");\n        SnatchGuard(self.lock.read())\n    }\n\n    /// Request write access to snatchable resources.\n    ///\n    /// This should only be called when a resource needs to be snatched. This has\n    /// a high risk of causing lock contention if called concurrently with other\n    /// wgpu work.\n    #[track_caller]\n    pub fn write(&self) -> ExclusiveSnatchGuard<'_> {\n        LockTrace::enter(\"write\");\n        ExclusiveSnatchGuard(self.lock.write())\n    }\n\n    #[track_caller]\n    pub unsafe fn force_unlock_read(&self, data: RankData) {\n        // This is unsafe because it can cause deadlocks if the lock is held.\n        // It should only be used in very specific cases, like when a resource\n        // needs to be snatched in a panic handler.\n        LockTrace::exit();\n        unsafe { self.lock.force_unlock_read(data) };\n    }\n}\n\nimpl SnatchGuard<'_> {\n    /// Forget the guard, leaving the lock in a locked state with no guard.\n    ///\n    /// This is equivalent to `std::mem::forget`, but preserves the information about the lock\n    /// rank.\n    pub fn forget(this: Self) -> RankData {\n        // Cancel the drop implementation of the current guard.\n        let manually_drop = ManuallyDrop::new(this);\n\n        // As we are unable to destructure out of this guard due to the drop implementation,\n        // so we manually read the inner value.\n        // SAFETY: This is safe because we never access the original guard again.\n        let inner_guard = unsafe { core::ptr::read(&manually_drop.0) };\n\n        RwLockReadGuard::forget(inner_guard)\n    }\n}\n\nimpl Drop for SnatchGuard<'_> {\n    fn drop(&mut self) {\n        LockTrace::exit();\n    }\n}\n\nimpl Drop for ExclusiveSnatchGuard<'_> {\n    fn drop(&mut self) {\n        LockTrace::exit();\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/storage.rs",
    "content": "use alloc::{sync::Arc, vec::Vec};\nuse core::mem;\n\nuse crate::id::{Id, Marker};\nuse crate::resource::ResourceType;\nuse crate::{Epoch, Index};\n\n/// An entry in a `Storage::map` table.\n#[derive(Debug)]\npub(crate) enum Element<T>\nwhere\n    T: StorageItem,\n{\n    /// There are no live ids with this index.\n    Vacant,\n\n    /// There is one live id with this index, allocated at the given\n    /// epoch.\n    Occupied(T, Epoch),\n}\n\n/// Not a public API. For use only by `player`.\n#[doc(hidden)]\npub trait StorageItem: ResourceType {\n    type Marker: Marker;\n}\n\nimpl<T: ResourceType> ResourceType for Arc<T> {\n    const TYPE: &'static str = T::TYPE;\n}\n\nimpl<T: StorageItem> StorageItem for Arc<T> {\n    type Marker = T::Marker;\n}\n\n#[macro_export]\nmacro_rules! impl_storage_item {\n    ($ty:ident) => {\n        impl $crate::storage::StorageItem for $ty {\n            type Marker = $crate::id::markers::$ty;\n        }\n    };\n}\n\n/// A table of `T` values indexed by the id type `I`.\n///\n/// `Storage` implements [`core::ops::Index`], accepting `Id` values as\n/// indices.\n///\n/// The table is represented as a vector indexed by the ids' index\n/// values, so you should use an id allocator like `IdentityManager`\n/// that keeps the index values dense and close to zero.\n#[derive(Debug)]\npub(crate) struct Storage<T>\nwhere\n    T: StorageItem,\n{\n    pub(crate) map: Vec<Element<T>>,\n    kind: &'static str,\n}\n\nimpl<T> Storage<T>\nwhere\n    T: StorageItem,\n{\n    pub(crate) fn new() -> Self {\n        Self {\n            map: Vec::new(),\n            kind: T::TYPE,\n        }\n    }\n}\n\nimpl<T> Storage<T>\nwhere\n    T: StorageItem,\n{\n    pub(crate) fn insert(&mut self, id: Id<T::Marker>, value: T) {\n        let (index, epoch) = id.unzip();\n        let index = index as usize;\n        if index >= self.map.len() {\n            self.map.resize_with(index + 1, || Element::Vacant);\n        }\n        match mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) {\n            Element::Vacant => {}\n            Element::Occupied(_, storage_epoch) => {\n                assert_ne!(\n                    epoch,\n                    storage_epoch,\n                    \"Index {index:?} of {} is already occupied\",\n                    T::TYPE\n                );\n            }\n        }\n    }\n\n    pub(crate) fn remove(&mut self, id: Id<T::Marker>) -> T {\n        let (index, epoch) = id.unzip();\n        let stored = self\n            .map\n            .get_mut(index as usize)\n            .unwrap_or_else(|| panic!(\"{}[{:?}] does not exist\", self.kind, id));\n        match mem::replace(stored, Element::Vacant) {\n            Element::Occupied(value, storage_epoch) => {\n                assert_eq!(epoch, storage_epoch, \"id epoch mismatch\");\n                value\n            }\n            Element::Vacant => panic!(\"Cannot remove a vacant resource\"),\n        }\n    }\n\n    pub(crate) fn iter(&self) -> impl Iterator<Item = (Id<T::Marker>, &T)> {\n        self.map\n            .iter()\n            .enumerate()\n            .filter_map(move |(index, x)| match *x {\n                Element::Occupied(ref value, storage_epoch) => {\n                    Some((Id::zip(index as Index, storage_epoch), value))\n                }\n                _ => None,\n            })\n    }\n}\n\nimpl<T> Storage<T>\nwhere\n    T: StorageItem + Clone,\n{\n    /// Get an owned reference to an item.\n    /// Panics if there is an epoch mismatch, the entry is empty or in error.\n    pub(crate) fn get(&self, id: Id<T::Marker>) -> T {\n        let (index, epoch) = id.unzip();\n        let (result, storage_epoch) = match self.map.get(index as usize) {\n            Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch),\n            None | Some(&Element::Vacant) => panic!(\"{}[{:?}] does not exist\", self.kind, id),\n        };\n        assert_eq!(\n            epoch, storage_epoch,\n            \"{}[{:?}] is no longer alive\",\n            self.kind, id\n        );\n        result\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/timestamp_normalization/common.wgsl",
    "content": "// Common routines for timestamp normalization.\n//\n// This is split out into its own file so that the tests in `tests` can include\n// it without including the normal endpoints and interface definitions.\n\n/// 64-bit unsigned integer type.\n///\n/// We cannot rely on native 64-bit integers, so we define our own 64-bit\n/// integer type as two 32-bit integers.\nstruct Uint64 {\n    /// Least significant word.\n    low: u32,\n    /// Most significant word.\n    high: u32,\n}\n\n/// 96-bit unsigned integer type.\nstruct Uint96 {\n    /// Least significant word.\n    low: u32,\n    /// Middle word.\n    mid: u32,\n    /// Most significant word.\n    high: u32,\n}\n\n/// Truncates a 96-bit number to a 64-bit number by discarding the upper 32 bits.\nfn truncate_u96_to_u64(x: Uint96) -> Uint64 {\n    return Uint64(\n        x.low,\n        x.mid,\n    );\n}\n\n/// Returns the lower 16 bits of a 32-bit integer.\nfn low(a: u32) -> u32 {\n    return a & 0xFFFF;\n}\n\n/// Returns the upper 16 bits of a 32-bit integer.\nfn high(a: u32) -> u32 {\n    return a >> 16;\n}\n\n/// Combines two 16bit words into a single 32bit word. \n/// `w1` is the upper 16 bits and `w0` is the lower 16 bits.\n///\n/// The high 16 bits of each argument are discarded.\nfn u32_from_u16s(w1: u32, w0: u32) -> u32 {\n    return low(w1) << 16 | low(w0);\n}\n\n// Multiplies a 64-bit number by a 32-bit number and outputs a 96-bit result.\n//\n// The number of digits (bits) needed to represent the result of a multiplication\n// is the sum of the number of input digits (bits). Since we are multiplying a\n// 64-bit number by a 32-bit number, we need 96 bits to represent the result.\nfn u64_mul_u32(a: Uint64, b: u32) -> Uint96 {\n    // Does not use any 64-bit operations and we don't have access to `mul(u32, u32) -> u64`\n    // operations, so we operate entirely on `mul(u16, u16) -> u32`.\n\n    // This implements standard \"long multiplication\" algorithm using 16-bit words.\n    // Each element in this diagram is a 16-bit word.\n    //\n    //                  a3  a2  a1  a0\n    //               *          b1  b0\n    //     ----------------------------\n    // i0 =                    p00 p00\n    // i1 =                p10 p10\n    // i2 =            p20 p20\n    // i3 =        p30 p30\n    // i4 =                p01 p01\n    // i5 =            p11 p11\n    // i6 =        p21 p21\n    // i7 =    p31 p31\n    //     ----------------------------\n    //      r6  r5  r4  r3  r2  r1  r0\n\n    // Decompose the 64-bit number into four 16-bit words.\n    let a0 = low(a.low);\n    let a1 = high(a.low);\n    let a2 = low(a.high);\n    let a3 = high(a.high);\n\n    // Decompose the 32-bit number into two 16-bit words.\n    let b0 = low(b);\n    let b1 = high(b);\n\n    // Each line represents one row in the diagram above.\n    let i0 = a0 * b0;\n    let i1 = a1 * b0;\n    let i2 = a2 * b0;\n    let i3 = a3 * b0;\n    let i4 = a0 * b1;\n    let i5 = a1 * b1;\n    let i6 = a2 * b1;\n    let i7 = a3 * b1;\n\n    // Each line represents one column in the diagram above.\n    //\n    // The high 16 bits of each column are the carry to the next column.\n    let r0 = low(i0);\n    let r1 = high(i0) + low(i1) + low(i4) + high(r0);\n    let r2 = high(i1) + low(i2) + high(i4) + low(i5) + high(r1);\n    let r3 = high(i2) + low(i3) + high(i5) + low(i6) + high(r2);\n    let r4 = high(i3) + high(i6) + low(i7) + high(r3);\n    let r5 = high(i7) + high(r4);\n    // The r5 carry will always be zero.\n\n    let out0 = u32_from_u16s(r1, r0);\n    let out1 = u32_from_u16s(r3, r2);\n    let out2 = u32_from_u16s(r5, r4);\n\n    return Uint96(out0, out1, out2);\n}\n\n// Shifts a 96-bit number right by a given number of bits.\n//\n// The shift is in the range [0, 32].\nfn shift_right_96(x: Uint96, shift: u32) -> Uint96 {\n    // Shift wraps around at 32, which breaks the algorithm when\n    // either shift is 32 or inv_shift is 32.\n    if (shift == 0) {\n        return x;\n    }\n    if (shift == 32) {\n        return Uint96(x.mid, x.high, 0);\n    }\n\n    let inv_shift = 32 - shift;\n\n    let carry2 = x.high << inv_shift;\n    let carry1 = x.mid << inv_shift;\n\n    var out: Uint96;\n\n    out.high = x.high >> shift;\n    out.mid = (x.mid >> shift) | carry2;\n    out.low = (x.low >> shift) | carry1;\n\n    return out;\n}\n"
  },
  {
    "path": "wgpu-core/src/timestamp_normalization/mod.rs",
    "content": "//! Utility for normalizing GPU timestamp queries to have a consistent\n//! 1GHz period. This uses a compute shader to do the normalization,\n//! so the timestamps exist in their correct format on the GPU, as\n//! is required by the WebGPU specification.\n//!\n//! ## Algorithm\n//!\n//! The fundamental operation is multiplying a u64 timestamp by an f32\n//! value. We have neither f64s nor u64s in shaders, so we need to do\n//! something more complicated.\n//!\n//! We first decompose the f32 into a u32 fraction where the denominator\n//! is a power of two. We do the computation with f64 for ease of computation,\n//! as those can store u32s losslessly.\n//!\n//! Because the denominator is a power of two, this means the shader can evaluate\n//! this divide by using a shift. Additionally, we always choose the largest denominator\n//! we can, so that the fraction is as precise as possible.\n//!\n//! To evaluate this function, we have two helper operations (both in common.wgsl).\n//!\n//! 1. `u64_mul_u32` multiplies a u64 by a u32 and returns a u96.\n//! 2. `shift_right_u96` shifts a u96 right by a given amount, returning a u96.\n//!\n//! See their implementations for more details.\n//!\n//! We then multiply the timestamp by the numerator, and shift it right by the\n//! denominator. This gives us the normalized timestamp.\n\nuse core::num::NonZeroU64;\n\nuse alloc::{boxed::Box, string::String, string::ToString, sync::Arc};\n\nuse hashbrown::HashMap;\n\nuse crate::{\n    device::{Device, DeviceError},\n    hal_label,\n    pipeline::{CreateComputePipelineError, CreateShaderModuleError},\n    resource::Buffer,\n    snatch::SnatchGuard,\n    track::BufferTracker,\n};\n\npub const TIMESTAMP_NORMALIZATION_BUFFER_USES: wgt::BufferUses =\n    wgt::BufferUses::STORAGE_READ_WRITE;\n\nstruct InternalState {\n    temporary_bind_group_layout: Box<dyn hal::DynBindGroupLayout>,\n    pipeline_layout: Box<dyn hal::DynPipelineLayout>,\n    pipeline: Box<dyn hal::DynComputePipeline>,\n}\n\n#[derive(Debug, Clone, thiserror::Error)]\npub enum TimestampNormalizerInitError {\n    #[error(\"Failed to initialize bind group layout\")]\n    BindGroupLayout(#[source] DeviceError),\n    #[cfg(feature = \"wgsl\")]\n    #[error(\"Failed to parse shader\")]\n    ParseWgsl(#[source] naga::error::ShaderError<naga::front::wgsl::ParseError>),\n    #[error(\"Failed to validate shader module\")]\n    ValidateWgsl(#[source] naga::error::ShaderError<naga::WithSpan<naga::valid::ValidationError>>),\n    #[error(\"Failed to create shader module\")]\n    CreateShaderModule(#[from] CreateShaderModuleError),\n    #[error(\"Failed to create pipeline layout\")]\n    PipelineLayout(#[source] DeviceError),\n    #[error(\"Failed to create compute pipeline\")]\n    ComputePipeline(#[from] CreateComputePipelineError),\n}\n\n/// Normalizes GPU timestamps to have a consistent 1GHz period.\n/// See module documentation for more information.\npub struct TimestampNormalizer {\n    state: Option<InternalState>,\n}\n\nimpl TimestampNormalizer {\n    /// Creates a new timestamp normalizer.\n    ///\n    /// If the device cannot support automatic timestamp normalization,\n    /// this will return a normalizer that does nothing.\n    ///\n    /// # Errors\n    ///\n    /// If any resources are invalid, this will return an error.\n    pub fn new(\n        device: &Device,\n        timestamp_period: f32,\n    ) -> Result<Self, TimestampNormalizerInitError> {\n        unsafe {\n            if !device\n                .instance_flags\n                .contains(wgt::InstanceFlags::AUTOMATIC_TIMESTAMP_NORMALIZATION)\n            {\n                return Ok(Self { state: None });\n            }\n\n            if !device\n                .downlevel\n                .flags\n                .contains(wgt::DownlevelFlags::COMPUTE_SHADERS)\n            {\n                log::error!(\"Automatic timestamp normalization was requested, but compute shaders are not supported.\");\n                return Ok(Self { state: None });\n            }\n\n            if timestamp_period == 1.0 {\n                // If the period is 1, we don't need to do anything to them.\n                return Ok(Self { state: None });\n            }\n\n            let temporary_bind_group_layout = device\n                .raw()\n                .create_bind_group_layout(&hal::BindGroupLayoutDescriptor {\n                    label: hal_label(\n                        Some(\"(wgpu internal) Timestamp Normalization Bind Group Layout\"),\n                        device.instance_flags,\n                    ),\n                    flags: hal::BindGroupLayoutFlags::empty(),\n                    entries: &[wgt::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgt::ShaderStages::COMPUTE,\n                        ty: wgt::BindingType::Buffer {\n                            ty: wgt::BufferBindingType::Storage { read_only: false },\n                            has_dynamic_offset: false,\n                            min_binding_size: Some(NonZeroU64::new(8).unwrap()),\n                        },\n                        count: None,\n                    }],\n                })\n                .map_err(|e| {\n                    TimestampNormalizerInitError::BindGroupLayout(device.handle_hal_error(e))\n                })?;\n\n            let common_src = include_str!(\"common.wgsl\");\n            let src = include_str!(\"timestamp_normalization.wgsl\");\n\n            let preprocessed_src = alloc::format!(\"{common_src}\\n{src}\");\n\n            #[cfg(feature = \"wgsl\")]\n            let module = naga::front::wgsl::parse_str(&preprocessed_src).map_err(|inner| {\n                TimestampNormalizerInitError::ParseWgsl(naga::error::ShaderError {\n                    source: preprocessed_src.clone(),\n                    label: None,\n                    inner: Box::new(inner),\n                })\n            })?;\n            #[cfg(not(feature = \"wgsl\"))]\n            #[allow(clippy::diverging_sub_expression)]\n            let module =\n                panic!(\"Timestamp normalization requires the wgsl feature flag to be enabled!\");\n\n            let info = crate::device::create_validator(\n                wgt::Features::IMMEDIATES,\n                wgt::DownlevelFlags::empty(),\n                naga::valid::ValidationFlags::all(),\n            )\n            .validate(&module)\n            .map_err(|inner| {\n                TimestampNormalizerInitError::ValidateWgsl(naga::error::ShaderError {\n                    source: preprocessed_src.clone(),\n                    label: None,\n                    inner: Box::new(inner),\n                })\n            })?;\n            let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {\n                module: alloc::borrow::Cow::Owned(module),\n                info,\n                debug_source: None,\n            });\n            let hal_desc = hal::ShaderModuleDescriptor {\n                label: hal_label(\n                    Some(\"(wgpu internal) Timestamp normalizer shader module\"),\n                    device.instance_flags,\n                ),\n                runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),\n            };\n            let module = device\n                .raw()\n                .create_shader_module(&hal_desc, hal_shader)\n                .map_err(|error| match error {\n                    hal::ShaderError::Device(error) => {\n                        CreateShaderModuleError::Device(device.handle_hal_error(error))\n                    }\n                    hal::ShaderError::Compilation(ref msg) => {\n                        log::error!(\"Shader error: {msg}\");\n                        CreateShaderModuleError::Generation\n                    }\n                })?;\n\n            let pipeline_layout = device\n                .raw()\n                .create_pipeline_layout(&hal::PipelineLayoutDescriptor {\n                    label: hal_label(\n                        Some(\"(wgpu internal) Timestamp normalizer pipeline layout\"),\n                        device.instance_flags,\n                    ),\n                    bind_group_layouts: &[Some(temporary_bind_group_layout.as_ref())],\n                    immediate_size: 8,\n                    flags: hal::PipelineLayoutFlags::empty(),\n                })\n                .map_err(|e| {\n                    TimestampNormalizerInitError::PipelineLayout(device.handle_hal_error(e))\n                })?;\n\n            let (multiplier, shift) = compute_timestamp_period(timestamp_period);\n\n            let mut constants = HashMap::with_capacity(2);\n            constants.insert(String::from(\"TIMESTAMP_PERIOD_MULTIPLY\"), multiplier as f64);\n            constants.insert(String::from(\"TIMESTAMP_PERIOD_SHIFT\"), shift as f64);\n\n            let pipeline_desc = hal::ComputePipelineDescriptor {\n                label: hal_label(\n                    Some(\"(wgpu internal) Timestamp normalizer pipeline\"),\n                    device.instance_flags,\n                ),\n                layout: pipeline_layout.as_ref(),\n                stage: hal::ProgrammableStage {\n                    module: module.as_ref(),\n                    entry_point: \"main\",\n                    constants: &constants,\n                    zero_initialize_workgroup_memory: false,\n                },\n                cache: None,\n            };\n            let pipeline = device\n                .raw()\n                .create_compute_pipeline(&pipeline_desc)\n                .map_err(|err| match err {\n                    hal::PipelineError::Device(error) => {\n                        CreateComputePipelineError::Device(device.handle_hal_error(error))\n                    }\n                    hal::PipelineError::Linkage(_stages, msg) => {\n                        CreateComputePipelineError::Internal(msg)\n                    }\n                    hal::PipelineError::EntryPoint(_stage) => CreateComputePipelineError::Internal(\n                        crate::device::ENTRYPOINT_FAILURE_ERROR.to_string(),\n                    ),\n                    hal::PipelineError::PipelineConstants(_, error) => {\n                        CreateComputePipelineError::PipelineConstants(error)\n                    }\n                })?;\n\n            Ok(Self {\n                state: Some(InternalState {\n                    temporary_bind_group_layout,\n                    pipeline_layout,\n                    pipeline,\n                }),\n            })\n        }\n    }\n\n    /// Create a bind group for normalizing timestamps in `buffer`.\n    ///\n    /// This function is unsafe because it does not know that `buffer_size` is\n    /// the true size of the buffer.\n    pub unsafe fn create_normalization_bind_group(\n        &self,\n        device: &Device,\n        buffer: &dyn hal::DynBuffer,\n        buffer_label: Option<&str>,\n        buffer_size: wgt::BufferSize,\n        buffer_usages: wgt::BufferUsages,\n    ) -> Result<TimestampNormalizationBindGroup, DeviceError> {\n        unsafe {\n            let Some(ref state) = &self.state else {\n                return Ok(TimestampNormalizationBindGroup { raw: None });\n            };\n\n            if !buffer_usages.contains(wgt::BufferUsages::QUERY_RESOLVE) {\n                return Ok(TimestampNormalizationBindGroup { raw: None });\n            }\n\n            // If this buffer is large enough that we wouldn't be able to bind the entire thing\n            // at once to normalize the timestamps, we can't use it. We force the buffer to fail\n            // to allocate. The lowest max binding size is 128MB, and query sets must be small\n            // (no more than 4096), so this should never be hit in practice by sane programs.\n            if buffer_size.get() > device.adapter.limits().max_storage_buffer_binding_size {\n                return Err(DeviceError::OutOfMemory);\n            }\n\n            let bg_label_alloc;\n            let label = match buffer_label {\n                Some(label) => {\n                    bg_label_alloc = alloc::format!(\"Timestamp normalization bind group ({label})\");\n                    &*bg_label_alloc\n                }\n                None => \"Timestamp normalization bind group\",\n            };\n\n            let bg = device\n                .raw()\n                .create_bind_group(&hal::BindGroupDescriptor {\n                    label: hal_label(Some(label), device.instance_flags),\n                    layout: &*state.temporary_bind_group_layout,\n                    buffers: &[hal::BufferBinding::new_unchecked(buffer, 0, buffer_size)],\n                    samplers: &[],\n                    textures: &[],\n                    acceleration_structures: &[],\n                    external_textures: &[],\n                    entries: &[hal::BindGroupEntry {\n                        binding: 0,\n                        resource_index: 0,\n                        count: 1,\n                    }],\n                })\n                .map_err(|e| device.handle_hal_error(e))?;\n\n            Ok(TimestampNormalizationBindGroup { raw: Some(bg) })\n        }\n    }\n\n    pub fn normalize(\n        &self,\n        snatch_guard: &SnatchGuard<'_>,\n        encoder: &mut dyn hal::DynCommandEncoder,\n        tracker: &mut BufferTracker,\n        bind_group: &TimestampNormalizationBindGroup,\n        buffer: &Arc<Buffer>,\n        buffer_offset_bytes: u64,\n        total_timestamps: u32,\n    ) {\n        let Some(ref state) = &self.state else {\n            return;\n        };\n\n        let Some(bind_group) = bind_group.raw.as_deref() else {\n            return;\n        };\n\n        let buffer_offset_timestamps: u32 = (buffer_offset_bytes / 8).try_into().unwrap(); // Unreachable as MAX_QUERIES is way less than u32::MAX\n\n        let pending_barrier = tracker.set_single(buffer, wgt::BufferUses::STORAGE_READ_WRITE);\n\n        let barrier = pending_barrier.map(|pending| pending.into_hal(buffer, snatch_guard));\n\n        let needed_workgroups = total_timestamps.div_ceil(64);\n\n        unsafe {\n            encoder.transition_buffers(barrier.as_slice());\n            encoder.begin_compute_pass(&hal::ComputePassDescriptor {\n                label: hal_label(\n                    Some(\"(wgpu internal) Timestamp normalization pass\"),\n                    buffer.device.instance_flags,\n                ),\n                timestamp_writes: None,\n            });\n            encoder.set_compute_pipeline(&*state.pipeline);\n            encoder.set_bind_group(&*state.pipeline_layout, 0, bind_group, &[]);\n            encoder.set_immediates(\n                &*state.pipeline_layout,\n                0,\n                &[buffer_offset_timestamps, total_timestamps],\n            );\n            encoder.dispatch([needed_workgroups, 1, 1]);\n            encoder.end_compute_pass();\n        }\n    }\n\n    pub fn dispose(self, device: &dyn hal::DynDevice) {\n        unsafe {\n            let Some(state) = self.state else {\n                return;\n            };\n\n            device.destroy_compute_pipeline(state.pipeline);\n            device.destroy_pipeline_layout(state.pipeline_layout);\n            device.destroy_bind_group_layout(state.temporary_bind_group_layout);\n        }\n    }\n\n    pub fn enabled(&self) -> bool {\n        self.state.is_some()\n    }\n}\n\n#[derive(Debug)]\npub struct TimestampNormalizationBindGroup {\n    raw: Option<Box<dyn hal::DynBindGroup>>,\n}\n\nimpl TimestampNormalizationBindGroup {\n    pub fn dispose(self, device: &dyn hal::DynDevice) {\n        unsafe {\n            if let Some(raw) = self.raw {\n                device.destroy_bind_group(raw);\n            }\n        }\n    }\n}\n\nfn compute_timestamp_period(input: f32) -> (u32, u32) {\n    let pow2 = input.log2().ceil() as i32;\n    let clamped_pow2 = pow2.clamp(-32, 32).unsigned_abs();\n    let shift = 32 - clamped_pow2;\n\n    let denominator = (1u64 << shift) as f64;\n\n    // float -> int conversions are defined to saturate.\n    let multiplier = (input as f64 * denominator).round() as u32;\n\n    (multiplier, shift)\n}\n\n#[cfg(test)]\nmod tests {\n    use core::f64;\n\n    fn assert_timestamp_case(input: f32) {\n        let (multiplier, shift) = super::compute_timestamp_period(input);\n\n        let output = multiplier as f64 / (1u64 << shift) as f64;\n\n        assert!((input as f64 - output).abs() < 0.0000001);\n    }\n\n    #[test]\n    fn compute_timestamp_period() {\n        assert_timestamp_case(0.01);\n        assert_timestamp_case(0.5);\n        assert_timestamp_case(1.0);\n        assert_timestamp_case(2.0);\n        assert_timestamp_case(2.7);\n        assert_timestamp_case(1000.7);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/timestamp_normalization/timestamp_normalization.wgsl",
    "content": "// Must have \"common.wgsl\" preprocessed before this file's contents.\n//\n// To compile this locally, you can run:\n// ```\n// cat common.wgsl timestamp_normalization.wgsl | cargo run -p naga-cli -- --stdin-file-path timestamp_normalization.wgsl\n// ```\n\n// For an explanation of the timestamp normalization process, see\n// the `mod.rs` file in this folder.\n\n// These is the timestamp period turned into a fraction\n// with an integer numerator and denominator. The denominator\n// is a power of two, so the division can be done with a shift.\noverride TIMESTAMP_PERIOD_MULTIPLY: u32 = 1;\noverride TIMESTAMP_PERIOD_SHIFT: u32 = 0;\n\n@group(0) @binding(0)\nvar<storage, read_write> timestamps: array<Uint64>;\n\nstruct ImmediateData {\n    timestamp_offset: u32,\n    timestamp_count: u32,\n}\n\nvar<immediate> im: ImmediateData;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n    if id.x >= im.timestamp_count {\n        return;\n    }\n\n    let index = id.x + im.timestamp_offset;\n\n    let input_value = timestamps[index];\n\n    let tmp1 = u64_mul_u32(input_value, TIMESTAMP_PERIOD_MULTIPLY);\n    let tmp2 = shift_right_96(tmp1, TIMESTAMP_PERIOD_SHIFT);\n\n    timestamps[index] = truncate_u96_to_u64(tmp2);\n}\n"
  },
  {
    "path": "wgpu-core/src/track/blas.rs",
    "content": "use crate::{\n    resource::{Blas, Trackable},\n    track::metadata::ResourceMetadata,\n};\nuse alloc::sync::Arc;\n\n/// A tracker that holds tracks BLASes.\n///\n/// This is mostly a safe shell around [`ResourceMetadata`]\n#[derive(Debug)]\npub(crate) struct BlasTracker {\n    metadata: ResourceMetadata<Arc<Blas>>,\n    size: usize,\n}\n\nimpl BlasTracker {\n    pub fn new() -> Self {\n        Self {\n            metadata: ResourceMetadata::new(),\n            size: 0,\n        }\n    }\n\n    /// Inserts a single resource into the resource tracker.\n    ///\n    /// Returns a reference to the newly inserted resource.\n    /// (This allows avoiding a clone/reference count increase in many cases.)\n    pub fn insert_single(&mut self, resource: Arc<Blas>) -> &Arc<Blas> {\n        let index = resource.tracker_index().as_usize();\n        self.allow_index(index);\n\n        unsafe {\n            // # SAFETY: we just allowed this resource, which makes the metadata object larger if\n            // it's not in bounds\n            self.metadata.insert(index, resource)\n        }\n    }\n\n    /// Sets the size of all the vectors inside the tracker.\n    ///\n    /// Must be called with the highest possible Texture ID before\n    /// all unsafe functions are called.\n    pub fn set_size(&mut self, size: usize) {\n        self.size = size;\n        self.metadata.set_size(size)\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.size {\n            self.set_size(index + 1);\n        }\n    }\n\n    /// Returns true if the tracker owns the given texture.\n    pub fn contains(&self, blas: &Blas) -> bool {\n        self.metadata.contains(blas.tracker_index().as_usize())\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/track/buffer.rs",
    "content": "//! Buffer Trackers\n//!\n//! Buffers are represented by a single state for the whole resource,\n//! a 16 bit bitflag of buffer usages. Because there is only ever\n//! one subresource, they have no selector.\n\nuse alloc::{\n    sync::{Arc, Weak},\n    vec::Vec,\n};\n\nuse hal::BufferBarrier;\nuse wgt::{strict_assert, strict_assert_eq, BufferUses};\n\nuse super::{PendingTransition, TrackerIndex};\nuse crate::{\n    resource::{Buffer, Trackable},\n    snatch::SnatchGuard,\n    track::{\n        invalid_resource_state, skip_barrier, ResourceMetadata, ResourceMetadataProvider,\n        ResourceUsageCompatibilityError, ResourceUses,\n    },\n};\n\nimpl ResourceUses for BufferUses {\n    const EXCLUSIVE: Self = Self::EXCLUSIVE;\n\n    type Selector = ();\n\n    fn bits(self) -> u16 {\n        Self::bits(&self)\n    }\n\n    fn any_exclusive(self) -> bool {\n        self.intersects(Self::EXCLUSIVE)\n    }\n}\n\n/// Stores a bind group's buffers + their usages (within the bind group).\n#[derive(Debug)]\npub(crate) struct BufferBindGroupState {\n    buffers: Vec<(Arc<Buffer>, BufferUses)>,\n}\nimpl BufferBindGroupState {\n    pub fn new() -> Self {\n        Self {\n            buffers: Vec::new(),\n        }\n    }\n\n    /// Optimize the buffer bind group state by sorting it by ID.\n    ///\n    /// When this list of states is merged into a tracker, the memory\n    /// accesses will be in a constant ascending order.\n    pub(crate) fn optimize(&mut self) {\n        self.buffers\n            .sort_unstable_by_key(|(b, _)| b.tracker_index());\n    }\n\n    /// Returns a list of all buffers tracked. May contain duplicates.\n    pub fn used_tracker_indices(&self) -> impl Iterator<Item = TrackerIndex> + '_ {\n        self.buffers\n            .iter()\n            .map(|(b, _)| b.tracker_index())\n            .collect::<Vec<_>>()\n            .into_iter()\n    }\n\n    /// Adds the given resource with the given state.\n    pub fn insert_single(&mut self, buffer: Arc<Buffer>, state: BufferUses) {\n        self.buffers.push((buffer, state));\n    }\n}\n\n/// Stores all buffer state within a single usage scope.\n#[derive(Debug)]\npub(crate) struct BufferUsageScope {\n    state: Vec<BufferUses>,\n    metadata: ResourceMetadata<Arc<Buffer>>,\n    ordered_uses_mask: BufferUses,\n}\n\nimpl Default for BufferUsageScope {\n    fn default() -> Self {\n        Self {\n            state: Vec::new(),\n            metadata: ResourceMetadata::new(),\n            ordered_uses_mask: BufferUses::empty(),\n        }\n    }\n}\n\nimpl BufferUsageScope {\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        strict_assert!(index < self.state.len());\n        self.metadata.tracker_assert_in_bounds(index);\n    }\n    pub fn clear(&mut self) {\n        self.state.clear();\n        self.metadata.clear();\n    }\n\n    /// Sets the size of all the vectors inside the tracker.\n    ///\n    /// Must be called with the highest possible Buffer ID before\n    /// all unsafe functions are called.\n    pub fn set_size(&mut self, size: usize) {\n        self.state.resize(size, BufferUses::empty());\n        self.metadata.set_size(size);\n    }\n\n    pub fn set_ordered_uses_mask(&mut self, ordered_uses_mask: BufferUses) {\n        self.ordered_uses_mask = ordered_uses_mask;\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.state.len() {\n            self.set_size(index + 1);\n        }\n    }\n\n    /// Merge the list of buffer states in the given bind group into this usage scope.\n    ///\n    /// If any of the resulting states is invalid, stops the merge and returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// Because bind groups do not check if the union of all their states is valid,\n    /// this method is allowed to return Err on the first bind group bound.\n    ///\n    /// # Safety\n    ///\n    /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this\n    /// method is called.\n    pub unsafe fn merge_bind_group(\n        &mut self,\n        bind_group: &BufferBindGroupState,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        for &(ref resource, state) in bind_group.buffers.iter() {\n            let index = resource.tracker_index().as_usize();\n\n            unsafe {\n                self.insert_or_merge(\n                    index as _,\n                    index,\n                    BufferStateProvider::Direct { state },\n                    ResourceMetadataProvider::Direct { resource },\n                )?\n            };\n        }\n\n        Ok(())\n    }\n\n    /// Merge the list of buffer states in the given usage scope into this UsageScope.\n    ///\n    /// If any of the resulting states is invalid, stops the merge and returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// If the given tracker uses IDs higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn merge_usage_scope(\n        &mut self,\n        scope: &Self,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        let incoming_size = scope.state.len();\n        if incoming_size > self.state.len() {\n            self.set_size(incoming_size);\n        }\n\n        for index in scope.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            scope.tracker_assert_in_bounds(index);\n\n            unsafe {\n                self.insert_or_merge(\n                    index as u32,\n                    index,\n                    BufferStateProvider::Indirect {\n                        state: &scope.state,\n                    },\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                )?;\n            };\n        }\n\n        Ok(())\n    }\n\n    /// Merge a single state into the UsageScope.\n    ///\n    /// If the resulting state is invalid, returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn merge_single(\n        &mut self,\n        buffer: &Arc<Buffer>,\n        new_state: BufferUses,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        let index = buffer.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        unsafe {\n            self.insert_or_merge(\n                index as _,\n                index,\n                BufferStateProvider::Direct { state: new_state },\n                ResourceMetadataProvider::Direct { resource: buffer },\n            )?;\n        }\n\n        Ok(())\n    }\n\n    /// Does an insertion operation if the index isn't tracked\n    /// in the current metadata, otherwise merges the given state\n    /// with the current state. If the merging would cause\n    /// a conflict, returns that usage conflict.\n    ///\n    /// # Safety\n    ///\n    /// Indexes must be valid indexes into all arrays passed in\n    /// to this function, either directly or via metadata or provider structs.\n    #[inline(always)]\n    unsafe fn insert_or_merge(\n        &mut self,\n        index32: u32,\n        index: usize,\n        state_provider: BufferStateProvider<'_>,\n        metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        let currently_owned = unsafe { self.metadata.contains_unchecked(index) };\n\n        if !currently_owned {\n            unsafe {\n                insert(\n                    None,\n                    &mut self.state,\n                    &mut self.metadata,\n                    index,\n                    state_provider,\n                    None,\n                    metadata_provider,\n                )\n            };\n            return Ok(());\n        }\n\n        unsafe {\n            merge(\n                &mut self.state,\n                index32,\n                index,\n                state_provider,\n                metadata_provider,\n            )\n        }\n    }\n\n    /// Removes the indicated usage from the scope.\n    ///\n    /// Note that multiple uses of the same type get merged. It is only\n    /// safe to remove a usage if you are certain you aren't going to\n    /// erase another usage you don't know about.\n    pub fn remove_usage(&mut self, buffer: &Buffer, usage: BufferUses) {\n        let index = buffer.tracker_index().as_usize();\n        if self.metadata.contains(index) {\n            // SAFETY: If the buffer is part of this usage scope, then the index\n            // is in range.\n            unsafe {\n                *self.state.get_unchecked_mut(index) &= !usage;\n            }\n        }\n    }\n}\n\n/// Stores all buffer state within a command buffer.\npub(crate) struct BufferTracker {\n    start: Vec<BufferUses>,\n    end: Vec<BufferUses>,\n\n    metadata: ResourceMetadata<Arc<Buffer>>,\n\n    temp: Vec<PendingTransition<BufferUses>>,\n\n    ordered_uses_mask: BufferUses,\n}\n\nimpl BufferTracker {\n    pub fn new(ordered_uses_mask: BufferUses) -> Self {\n        Self {\n            start: Vec::new(),\n            end: Vec::new(),\n\n            metadata: ResourceMetadata::new(),\n\n            temp: Vec::new(),\n\n            ordered_uses_mask,\n        }\n    }\n\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        strict_assert!(index < self.start.len());\n        strict_assert!(index < self.end.len());\n        self.metadata.tracker_assert_in_bounds(index);\n    }\n\n    /// Sets the size of all the vectors inside the tracker.\n    ///\n    /// Must be called with the highest possible Buffer ID before\n    /// all unsafe functions are called.\n    pub fn set_size(&mut self, size: usize) {\n        self.start.resize(size, BufferUses::empty());\n        self.end.resize(size, BufferUses::empty());\n\n        self.metadata.set_size(size);\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.start.len() {\n            self.set_size(index + 1);\n        }\n    }\n\n    /// Returns true if the given buffer is tracked.\n    pub fn contains(&self, buffer: &Buffer) -> bool {\n        self.metadata.contains(buffer.tracker_index().as_usize())\n    }\n\n    /// Returns a list of all buffers tracked.\n    pub fn used_resources(&self) -> impl Iterator<Item = &Arc<Buffer>> + '_ {\n        self.metadata.owned_resources()\n    }\n\n    /// Drains all currently pending transitions.\n    pub fn drain_transitions<'a, 'b: 'a>(\n        &'b mut self,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> {\n        let buffer_barriers = self.temp.drain(..).map(|pending| {\n            let buf = unsafe { self.metadata.get_resource_unchecked(pending.id as _) };\n            pending.into_hal(buf, snatch_guard)\n        });\n        buffer_barriers\n    }\n\n    /// Sets the state of a single buffer.\n    ///\n    /// If a transition is needed to get the buffer into the given state, that transition\n    /// is returned. No more than one transition is needed.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_single(\n        &mut self,\n        buffer: &Arc<Buffer>,\n        state: BufferUses,\n    ) -> Option<PendingTransition<BufferUses>> {\n        let index: usize = buffer.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        unsafe {\n            self.insert_or_barrier_update(\n                index,\n                BufferStateProvider::Direct { state },\n                None,\n                ResourceMetadataProvider::Direct { resource: buffer },\n            )\n        };\n\n        strict_assert!(self.temp.len() <= 1);\n\n        self.temp.pop()\n    }\n\n    /// Sets the given state for all buffers in the given tracker.\n    ///\n    /// If a transition is needed to get the buffers into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_from_tracker(&mut self, tracker: &Self) {\n        let incoming_size = tracker.start.len();\n        if incoming_size > self.start.len() {\n            self.set_size(incoming_size);\n        }\n\n        for index in tracker.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            tracker.tracker_assert_in_bounds(index);\n            unsafe {\n                self.insert_or_barrier_update(\n                    index,\n                    BufferStateProvider::Indirect {\n                        state: &tracker.start,\n                    },\n                    Some(BufferStateProvider::Indirect {\n                        state: &tracker.end,\n                    }),\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &tracker.metadata,\n                    },\n                )\n            }\n        }\n    }\n\n    /// Sets the given state for all buffers in the given UsageScope.\n    ///\n    /// If a transition is needed to get the buffers into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_from_usage_scope(&mut self, scope: &BufferUsageScope) {\n        let incoming_size = scope.state.len();\n        if incoming_size > self.start.len() {\n            self.set_size(incoming_size);\n        }\n\n        for index in scope.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            scope.tracker_assert_in_bounds(index);\n            unsafe {\n                self.insert_or_barrier_update(\n                    index,\n                    BufferStateProvider::Indirect {\n                        state: &scope.state,\n                    },\n                    None,\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                )\n            }\n        }\n    }\n\n    /// Iterates through all buffers in the given bind group and adopts\n    /// the state given for those buffers in the UsageScope. It also\n    /// removes all touched buffers from the usage scope.\n    ///\n    /// If a transition is needed to get the buffers into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// This is a really funky method used by Compute Passes to generate\n    /// barriers after a call to dispatch without needing to iterate\n    /// over all elements in the usage scope. We use each the\n    /// a given iterator of ids as a source of which IDs to look at.\n    /// All the IDs must have first been added to the usage scope.\n    ///\n    /// # Panics\n    ///\n    /// If a resource identified by `index_source` is not found in the usage\n    /// scope.\n    pub fn set_and_remove_from_usage_scope_sparse(\n        &mut self,\n        scope: &mut BufferUsageScope,\n        index_source: impl IntoIterator<Item = TrackerIndex>,\n    ) {\n        let incoming_size = scope.state.len();\n        if incoming_size > self.start.len() {\n            self.set_size(incoming_size);\n        }\n\n        for index in index_source {\n            let index = index.as_usize();\n\n            scope.tracker_assert_in_bounds(index);\n\n            if unsafe { !scope.metadata.contains_unchecked(index) } {\n                continue;\n            }\n\n            // SAFETY: we checked that the index is in bounds for the scope, and\n            // called `set_size` to ensure it is valid for `self`.\n            unsafe {\n                self.insert_or_barrier_update(\n                    index,\n                    BufferStateProvider::Indirect {\n                        state: &scope.state,\n                    },\n                    None,\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                )\n            };\n\n            unsafe { scope.metadata.remove(index) };\n        }\n    }\n\n    /// If the resource isn't tracked\n    /// - Inserts the given resource.\n    /// - Uses the `start_state_provider` to populate `start_states`\n    /// - Uses either `end_state_provider` or `start_state_provider`\n    ///   to populate `current_states`.\n    ///\n    /// If the resource is tracked\n    /// - Inserts barriers from the state in `current_states`\n    ///   to the state provided by `start_state_provider`.\n    /// - Updates the `current_states` with either the state from\n    ///   `end_state_provider` or `start_state_provider`.\n    ///\n    /// Any barriers are added to the barrier vector.\n    ///\n    /// # Safety\n    ///\n    /// Indexes must be valid indexes into all arrays passed in\n    /// to this function, either directly or via metadata or provider structs.\n    #[inline(always)]\n    unsafe fn insert_or_barrier_update(\n        &mut self,\n        index: usize,\n        start_state_provider: BufferStateProvider<'_>,\n        end_state_provider: Option<BufferStateProvider<'_>>,\n        metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,\n    ) {\n        let currently_owned = unsafe { self.metadata.contains_unchecked(index) };\n\n        if !currently_owned {\n            unsafe {\n                insert(\n                    Some(&mut self.start),\n                    &mut self.end,\n                    &mut self.metadata,\n                    index,\n                    start_state_provider,\n                    end_state_provider,\n                    metadata_provider,\n                )\n            };\n            return;\n        }\n\n        let update_state_provider =\n            end_state_provider.unwrap_or_else(|| start_state_provider.clone());\n        unsafe {\n            barrier(\n                &mut self.end,\n                index,\n                start_state_provider,\n                &mut self.temp,\n                self.ordered_uses_mask,\n            )\n        };\n\n        unsafe { update(&mut self.end, index, update_state_provider) };\n    }\n}\n\n/// Stores all buffer state within a device.\npub(crate) struct DeviceBufferTracker {\n    current_states: Vec<BufferUses>,\n    metadata: ResourceMetadata<Weak<Buffer>>,\n    temp: Vec<PendingTransition<BufferUses>>,\n    ordered_uses_mask: BufferUses,\n}\n\nimpl DeviceBufferTracker {\n    pub fn new(ordered_uses_mask: BufferUses) -> Self {\n        Self {\n            current_states: Vec::new(),\n            metadata: ResourceMetadata::new(),\n            temp: Vec::new(),\n            ordered_uses_mask,\n        }\n    }\n\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        strict_assert!(index < self.current_states.len());\n        self.metadata.tracker_assert_in_bounds(index);\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.current_states.len() {\n            self.current_states.resize(index + 1, BufferUses::empty());\n            self.metadata.set_size(index + 1);\n        }\n    }\n\n    /// Returns a list of all buffers tracked.\n    pub fn used_resources(&self) -> impl Iterator<Item = &Weak<Buffer>> + '_ {\n        self.metadata.owned_resources()\n    }\n\n    /// Inserts a single buffer and its state into the resource tracker.\n    ///\n    /// If the resource already exists in the tracker, it will be overwritten.\n    pub fn insert_single(&mut self, buffer: &Arc<Buffer>, state: BufferUses) {\n        let index = buffer.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        unsafe {\n            insert(\n                None,\n                &mut self.current_states,\n                &mut self.metadata,\n                index,\n                BufferStateProvider::Direct { state },\n                None,\n                ResourceMetadataProvider::Direct {\n                    resource: &Arc::downgrade(buffer),\n                },\n            )\n        }\n    }\n\n    /// Sets the state of a single buffer.\n    ///\n    /// If a transition is needed to get the buffer into the given state, that transition\n    /// is returned. No more than one transition is needed.\n    pub fn set_single(\n        &mut self,\n        buffer: &Arc<Buffer>,\n        state: BufferUses,\n    ) -> Option<PendingTransition<BufferUses>> {\n        let index: usize = buffer.tracker_index().as_usize();\n\n        self.tracker_assert_in_bounds(index);\n\n        let start_state_provider = BufferStateProvider::Direct { state };\n\n        unsafe {\n            barrier(\n                &mut self.current_states,\n                index,\n                start_state_provider.clone(),\n                &mut self.temp,\n                self.ordered_uses_mask,\n            )\n        };\n        unsafe { update(&mut self.current_states, index, start_state_provider) };\n\n        strict_assert!(self.temp.len() <= 1);\n\n        self.temp.pop()\n    }\n\n    /// Sets the given state for all buffers in the given tracker.\n    ///\n    /// If a transition is needed to get the buffers into the needed state,\n    /// those transitions are returned.\n    pub fn set_from_tracker_and_drain_transitions<'a, 'b: 'a>(\n        &'a mut self,\n        tracker: &'a BufferTracker,\n        snatch_guard: &'b SnatchGuard<'b>,\n    ) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> {\n        for index in tracker.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n\n            let start_state_provider = BufferStateProvider::Indirect {\n                state: &tracker.start,\n            };\n            let end_state_provider = BufferStateProvider::Indirect {\n                state: &tracker.end,\n            };\n            unsafe {\n                barrier(\n                    &mut self.current_states,\n                    index,\n                    start_state_provider,\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                )\n            };\n            unsafe { update(&mut self.current_states, index, end_state_provider) };\n        }\n\n        self.temp.drain(..).map(|pending| {\n            let buf = unsafe { tracker.metadata.get_resource_unchecked(pending.id as _) };\n            pending.into_hal(buf, snatch_guard)\n        })\n    }\n}\n\n/// Source of Buffer State.\n#[derive(Debug, Clone)]\nenum BufferStateProvider<'a> {\n    /// Get a state that was provided directly.\n    Direct { state: BufferUses },\n    /// Get a state from an an array of states.\n    Indirect { state: &'a [BufferUses] },\n}\nimpl BufferStateProvider<'_> {\n    /// Gets the state from the provider, given a resource ID index.\n    ///\n    /// # Safety\n    ///\n    /// Index must be in bounds for the indirect source iff this is in the indirect state.\n    #[inline(always)]\n    unsafe fn get_state(&self, index: usize) -> BufferUses {\n        match *self {\n            BufferStateProvider::Direct { state } => state,\n            BufferStateProvider::Indirect { state } => {\n                strict_assert!(index < state.len());\n                *unsafe { state.get_unchecked(index) }\n            }\n        }\n    }\n}\n\n#[inline(always)]\nunsafe fn insert<T: Clone>(\n    start_states: Option<&mut [BufferUses]>,\n    current_states: &mut [BufferUses],\n    resource_metadata: &mut ResourceMetadata<T>,\n    index: usize,\n    start_state_provider: BufferStateProvider<'_>,\n    end_state_provider: Option<BufferStateProvider<'_>>,\n    metadata_provider: ResourceMetadataProvider<'_, T>,\n) {\n    let new_start_state = unsafe { start_state_provider.get_state(index) };\n    let new_end_state =\n        end_state_provider.map_or(new_start_state, |p| unsafe { p.get_state(index) });\n\n    // This should only ever happen with a wgpu bug, but let's just double\n    // check that resource states don't have any conflicts.\n    strict_assert_eq!(invalid_resource_state(new_start_state), false);\n    strict_assert_eq!(invalid_resource_state(new_end_state), false);\n\n    unsafe {\n        if let Some(&mut ref mut start_state) = start_states {\n            *start_state.get_unchecked_mut(index) = new_start_state;\n        }\n        *current_states.get_unchecked_mut(index) = new_end_state;\n\n        let resource = metadata_provider.get(index);\n        resource_metadata.insert(index, resource.clone());\n    }\n}\n\n#[inline(always)]\nunsafe fn merge(\n    current_states: &mut [BufferUses],\n    _index32: u32,\n    index: usize,\n    state_provider: BufferStateProvider<'_>,\n    metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,\n) -> Result<(), ResourceUsageCompatibilityError> {\n    let current_state = unsafe { current_states.get_unchecked_mut(index) };\n    let new_state = unsafe { state_provider.get_state(index) };\n\n    let merged_state = *current_state | new_state;\n\n    if invalid_resource_state(merged_state) {\n        return Err(ResourceUsageCompatibilityError::from_buffer(\n            unsafe { metadata_provider.get(index) },\n            *current_state,\n            new_state,\n        ));\n    }\n\n    *current_state = merged_state;\n\n    Ok(())\n}\n\n#[inline(always)]\nunsafe fn barrier(\n    current_states: &mut [BufferUses],\n    index: usize,\n    state_provider: BufferStateProvider<'_>,\n    barriers: &mut Vec<PendingTransition<BufferUses>>,\n    ordered_uses_mask: BufferUses,\n) {\n    let current_state = unsafe { *current_states.get_unchecked(index) };\n    let new_state = unsafe { state_provider.get_state(index) };\n\n    if skip_barrier(current_state, ordered_uses_mask, new_state) {\n        return;\n    }\n\n    barriers.push(PendingTransition {\n        id: index as _,\n        selector: (),\n        usage: hal::StateTransition {\n            from: current_state,\n            to: new_state,\n        },\n    });\n}\n\n#[inline(always)]\nunsafe fn update(\n    current_states: &mut [BufferUses],\n    index: usize,\n    state_provider: BufferStateProvider<'_>,\n) {\n    let current_state = unsafe { current_states.get_unchecked_mut(index) };\n    let new_state = unsafe { state_provider.get_state(index) };\n\n    *current_state = new_state;\n}\n"
  },
  {
    "path": "wgpu-core/src/track/metadata.rs",
    "content": "//! The `ResourceMetadata` type.\n\nuse alloc::vec::Vec;\n\nuse bit_vec::BitVec;\nuse wgt::strict_assert;\n\n/// A set of resources, holding a `Arc<T>` and epoch for each member.\n///\n/// Testing for membership is fast, and iterating over members is\n/// reasonably fast in practice. Storage consumption is proportional\n/// to the largest id index of any member, not to the number of\n/// members, but a bit vector tracks occupancy, so iteration touches\n/// only occupied elements.\n#[derive(Debug)]\npub(super) struct ResourceMetadata<T: Clone> {\n    /// If the resource with index `i` is a member, `owned[i]` is `true`.\n    owned: BitVec<usize>,\n\n    /// A vector holding clones of members' `T`s.\n    resources: Vec<Option<T>>,\n}\n\nimpl<T: Clone> ResourceMetadata<T> {\n    pub(super) fn new() -> Self {\n        Self {\n            owned: BitVec::default(),\n            resources: Vec::new(),\n        }\n    }\n\n    pub(super) fn set_size(&mut self, size: usize) {\n        self.resources.resize(size, None);\n        resize_bitvec(&mut self.owned, size);\n    }\n\n    pub(super) fn clear(&mut self) {\n        self.resources.clear();\n        self.owned.fill(false);\n    }\n\n    /// Ensures a given index is in bounds for all arrays and does\n    /// sanity checks of the presence of a refcount.\n    ///\n    /// In release mode this function is completely empty and is removed.\n    pub(super) fn tracker_assert_in_bounds(&self, index: usize) {\n        strict_assert!(index < self.owned.len());\n        strict_assert!(index < self.resources.len());\n        strict_assert!(if self.contains(index) {\n            self.resources[index].is_some()\n        } else {\n            true\n        });\n    }\n\n    /// Returns true if the tracker owns no resources.\n    ///\n    /// This is a O(n) operation.\n    pub(super) fn is_empty(&self) -> bool {\n        !self.owned.any()\n    }\n\n    /// Returns true if the set contains the resource with the given index.\n    pub(super) fn contains(&self, index: usize) -> bool {\n        self.owned.get(index).unwrap_or(false)\n    }\n\n    /// Returns true if the set contains the resource with the given index.\n    ///\n    /// # Safety\n    ///\n    /// The given `index` must be in bounds for this `ResourceMetadata`'s\n    /// existing tables. See `tracker_assert_in_bounds`.\n    #[inline(always)]\n    pub(super) unsafe fn contains_unchecked(&self, index: usize) -> bool {\n        unsafe { self.owned.get(index).unwrap_unchecked() }\n    }\n\n    /// Insert a resource into the set.\n    ///\n    /// Add the resource with the given index, epoch, and reference count to the\n    /// set.\n    ///\n    /// Returns a reference to the newly inserted resource.\n    /// (This allows avoiding a clone/reference count increase in many cases.)\n    ///\n    /// # Safety\n    ///\n    /// The given `index` must be in bounds for this `ResourceMetadata`'s\n    /// existing tables. See `tracker_assert_in_bounds`.\n    #[inline(always)]\n    pub(super) unsafe fn insert(&mut self, index: usize, resource: T) -> &T {\n        self.owned.set(index, true);\n        let resource_dst = unsafe { self.resources.get_unchecked_mut(index) };\n        resource_dst.insert(resource)\n    }\n\n    /// Get the resource with the given index.\n    ///\n    /// # Safety\n    ///\n    /// The given `index` must be in bounds for this `ResourceMetadata`'s\n    /// existing tables. See `tracker_assert_in_bounds`.\n    #[inline(always)]\n    pub(super) unsafe fn get_resource_unchecked(&self, index: usize) -> &T {\n        unsafe {\n            self.resources\n                .get_unchecked(index)\n                .as_ref()\n                .unwrap_unchecked()\n        }\n    }\n\n    /// Returns an iterator over the resources owned by `self`.\n    pub(super) fn owned_resources(&self) -> impl Iterator<Item = &T> + '_ {\n        if !self.owned.is_empty() {\n            self.tracker_assert_in_bounds(self.owned.len() - 1)\n        };\n        iterate_bitvec_indices(&self.owned).map(move |index| {\n            let resource = unsafe { self.resources.get_unchecked(index) };\n            resource.as_ref().unwrap()\n        })\n    }\n\n    /// Returns an iterator over the indices of all resources owned by `self`.\n    pub(super) fn owned_indices(&self) -> impl Iterator<Item = usize> + '_ {\n        if !self.owned.is_empty() {\n            self.tracker_assert_in_bounds(self.owned.len() - 1)\n        };\n        iterate_bitvec_indices(&self.owned)\n    }\n\n    /// Remove the resource with the given index from the set.\n    pub(super) unsafe fn remove(&mut self, index: usize) {\n        unsafe {\n            *self.resources.get_unchecked_mut(index) = None;\n        }\n        self.owned.set(index, false);\n    }\n}\n\n/// A source of resource metadata.\n///\n/// This is used to abstract over the various places\n/// trackers can get new resource metadata from.\npub(super) enum ResourceMetadataProvider<'a, T: Clone> {\n    /// Comes directly from explicit values.\n    Direct { resource: &'a T },\n    /// Comes from another metadata tracker.\n    Indirect { metadata: &'a ResourceMetadata<T> },\n}\nimpl<T: Clone> ResourceMetadataProvider<'_, T> {\n    /// Get a reference to the resource from this.\n    ///\n    /// # Safety\n    ///\n    /// - The index must be in bounds of the metadata tracker if this uses an indirect source.\n    #[inline(always)]\n    pub(super) unsafe fn get(&self, index: usize) -> &T {\n        match self {\n            ResourceMetadataProvider::Direct { resource } => resource,\n            ResourceMetadataProvider::Indirect { metadata } => {\n                metadata.tracker_assert_in_bounds(index);\n                {\n                    let resource = unsafe { metadata.resources.get_unchecked(index) }.as_ref();\n                    unsafe { resource.unwrap_unchecked() }\n                }\n            }\n        }\n    }\n}\n\n/// Resizes the given bitvec to the given size. I'm not sure why this is hard to do but it is.\nfn resize_bitvec<B: bit_vec::BitBlock>(vec: &mut BitVec<B>, size: usize) {\n    let owned_size_to_grow = size.checked_sub(vec.len());\n    if let Some(delta) = owned_size_to_grow {\n        if delta != 0 {\n            vec.grow(delta, false);\n        }\n    } else {\n        vec.truncate(size);\n    }\n}\n\n/// Produces an iterator that yields the indexes of all bits that are set in the bitvec.\n///\n/// Will skip entire usize's worth of bits if they are all false.\nfn iterate_bitvec_indices(ownership: &BitVec<usize>) -> impl Iterator<Item = usize> + '_ {\n    const BITS_PER_BLOCK: usize = usize::BITS as usize;\n\n    let size = ownership.len();\n\n    ownership\n        .blocks()\n        .enumerate()\n        .filter(|&(_, word)| word != 0)\n        .flat_map(move |(word_index, mut word)| {\n            let bit_start = word_index * BITS_PER_BLOCK;\n            let bit_end = (bit_start + BITS_PER_BLOCK).min(size);\n\n            (bit_start..bit_end).filter(move |_| {\n                let active = word & 0b1 != 0;\n                word >>= 1;\n\n                active\n            })\n        })\n}\n"
  },
  {
    "path": "wgpu-core/src/track/mod.rs",
    "content": "/*! Resource State and Lifetime Trackers\n\nThese structures are responsible for keeping track of resource state,\ngenerating barriers where needednd making sure resources are kept\nalive until the trackers die.\n\n## General Architecture\n\nTracking is some of the hottest code in the entire codebase, so the trackers\nare designed to be as cache efficient as possible. They store resource state\nin flat vectors, storing metadata SOA style, one vector per type of metadata.\n\nA lot of the tracker code is deeply unsafe, using unchecked accesses all over\nto make performance as good as possible. However, for all unsafe accesses, there\nis a corresponding debug assert the checks if that access is valid. This helps\nget bugs caught fast, while still letting users not need to pay for the bounds\nchecks.\n\nIn wgpu, each resource ID includes a bitfield holding an index.\nIndices are allocated and re-used, so they will always be as low as\nreasonably possible. This allows us to use IDs to index into an array\nof tracking information.\n\n## Statefulness\n\nThere are two main types of trackers, stateful and stateless.\n\nStateful trackers are for buffers and textures. They both have\nresource state attached to them which needs to be used to generate\nautomatic synchronization. Because of the different requirements of\nbuffers and textures, they have two separate tracking structures.\n\nStateless trackers only store metadata and own the given resource.\n\n## Use Case\n\nWithin each type of tracker, the trackers are further split into 3 different\nuse cases, Bind Group, Usage Scopend a full Tracker.\n\nBind Group trackers are just a list of different resources, their refcount,\nand how they are used. Textures are used via a selector and a usage type.\nBuffers by just a usage type. Stateless resources don't have a usage type.\n\nUsage Scope trackers are only for stateful resources. These trackers represent\na single [`UsageScope`] in the spec. When a use is added to a usage scope,\nit is merged with all other uses of that resource in that scope. If there\nis a usage conflict, merging will fail and an error will be reported.\n\nFull trackers represent a before and after state of a resource. These\nare used for tracking on the device and on command buffers. The before\nstate represents the state the resource is first used as in the command buffer,\nthe after state is the state the command buffer leaves the resource in.\nThese double ended buffers can then be used to generate the needed transitions\nbetween command buffers.\n\n## Dense Datastructure with Sparse Data\n\nThis tracking system is based on having completely dense data, but trackers do\nnot always contain every resource. Some resources (or even most resources) go\nunused in any given command buffer. So to help speed up the process of iterating\nthrough possibly thousands of resources, we use a bit vector to represent if\na resource is in the buffer or not. This allows us extremely efficient memory\nutilizations well as being able to bail out of whole blocks of 32-64 resources\nwith a single usize comparison with zero. In practice this means that merging\npartially resident buffers is extremely quick.\n\nThe main advantage of this dense datastructure is that we can do merging\nof trackers in an extremely efficient fashion that results in us doing linear\nscans down a couple of buffers. CPUs and their caches absolutely eat this up.\n\n## Stateful Resource Operations\n\nAll operations on stateful trackers boil down to one of four operations:\n- `insert(tracker, new_state)` adds a resource with a given state to the tracker\n  for the first time.\n- `merge(tracker, new_state)` merges this new state with the previous state, checking\n  for usage conflicts.\n- `barrier(tracker, new_state)` compares the given state to the existing state and\n  generates the needed barriers.\n- `update(tracker, new_state)` takes the given new state and overrides the old state.\n\nThis allows us to compose the operations to form the various kinds of tracker merges\nthat need to happen in the codebase. For each resource in the given merger, the following\noperation applies:\n\n```text\nUsageScope <- Resource = insert(scope, usage) OR merge(scope, usage)\nUsageScope <- UsageScope = insert(scope, scope) OR merge(scope, scope)\nCommandBuffer <- UsageScope = insert(buffer.start, buffer.end, scope)\n                              OR barrier(buffer.end, scope) + update(buffer.end, scope)\nDevice <- CommandBuffer = insert(device.start, device.end, buffer.start, buffer.end)\n                          OR barrier(device.end, buffer.start) + update(device.end, buffer.end)\n```\n\n[`UsageScope`]: https://gpuweb.github.io/gpuweb/#programming-model-synchronization\n*/\n\nmod blas;\nmod buffer;\nmod metadata;\nmod range;\nmod stateless;\nmod texture;\n\nuse crate::{\n    binding_model, command,\n    lock::{rank, Mutex},\n    pipeline,\n    resource::{self, Labeled, RawResourceAccess, ResourceErrorIdent},\n    snatch::SnatchGuard,\n    track::blas::BlasTracker,\n};\n\nuse alloc::{sync::Arc, vec::Vec};\nuse bitflags::Flags;\nuse core::{fmt, mem, ops};\n\nuse thiserror::Error;\n\npub(crate) use buffer::{\n    BufferBindGroupState, BufferTracker, BufferUsageScope, DeviceBufferTracker,\n};\nuse metadata::{ResourceMetadata, ResourceMetadataProvider};\npub(crate) use stateless::StatelessTracker;\npub(crate) use texture::{\n    DeviceTextureTracker, TextureTracker, TextureTrackerSetSingle, TextureUsageScope,\n    TextureViewBindGroupState,\n};\nuse wgt::{\n    error::{ErrorType, WebGpuError},\n    strict_assert_ne,\n};\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub(crate) struct TrackerIndex(u32);\n\nimpl TrackerIndex {\n    pub fn as_usize(self) -> usize {\n        self.0 as usize\n    }\n}\n\n/// wgpu-core internally use some array-like storage for tracking resources.\n/// To that end, there needs to be a uniquely assigned index for each live resource\n/// of a certain type. This index is separate from the resource ID for various reasons:\n/// - There can be multiple resource IDs pointing the the same resource.\n/// - IDs of dead handles can be recycled while resources are internally held alive (and tracked).\n/// - The plan is to remove IDs in the long run\n///   ([#5121](https://github.com/gfx-rs/wgpu/issues/5121)).\n///\n/// In order to produce these tracker indices, there is a shared TrackerIndexAllocator\n/// per resource type. Indices have the same lifetime as the internal resource they\n/// are associated to (alloc happens when creating the resource and free is called when\n/// the resource is dropped).\nstruct TrackerIndexAllocator {\n    unused: Vec<TrackerIndex>,\n    next_index: TrackerIndex,\n}\n\nimpl TrackerIndexAllocator {\n    pub fn new() -> Self {\n        TrackerIndexAllocator {\n            unused: Vec::new(),\n            next_index: TrackerIndex(0),\n        }\n    }\n\n    pub fn alloc(&mut self) -> TrackerIndex {\n        if let Some(index) = self.unused.pop() {\n            return index;\n        }\n\n        let index = self.next_index;\n        self.next_index.0 += 1;\n\n        index\n    }\n\n    pub fn free(&mut self, index: TrackerIndex) {\n        self.unused.push(index);\n    }\n\n    // This is used to pre-allocate the tracker storage.\n    pub fn size(&self) -> usize {\n        self.next_index.0 as usize\n    }\n}\n\nimpl fmt::Debug for TrackerIndexAllocator {\n    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        Ok(())\n    }\n}\n\n/// See TrackerIndexAllocator.\n#[derive(Debug)]\npub(crate) struct SharedTrackerIndexAllocator {\n    inner: Mutex<TrackerIndexAllocator>,\n}\n\nimpl SharedTrackerIndexAllocator {\n    pub fn new() -> Self {\n        SharedTrackerIndexAllocator {\n            inner: Mutex::new(\n                rank::SHARED_TRACKER_INDEX_ALLOCATOR_INNER,\n                TrackerIndexAllocator::new(),\n            ),\n        }\n    }\n\n    pub fn alloc(&self) -> TrackerIndex {\n        self.inner.lock().alloc()\n    }\n\n    pub fn free(&self, index: TrackerIndex) {\n        self.inner.lock().free(index);\n    }\n\n    pub fn size(&self) -> usize {\n        self.inner.lock().size()\n    }\n}\n\npub(crate) struct TrackerIndexAllocators {\n    pub buffers: Arc<SharedTrackerIndexAllocator>,\n    pub textures: Arc<SharedTrackerIndexAllocator>,\n    pub external_textures: Arc<SharedTrackerIndexAllocator>,\n    pub samplers: Arc<SharedTrackerIndexAllocator>,\n    pub bind_groups: Arc<SharedTrackerIndexAllocator>,\n    pub compute_pipelines: Arc<SharedTrackerIndexAllocator>,\n    pub render_pipelines: Arc<SharedTrackerIndexAllocator>,\n    pub bundles: Arc<SharedTrackerIndexAllocator>,\n    pub query_sets: Arc<SharedTrackerIndexAllocator>,\n    pub blas_s: Arc<SharedTrackerIndexAllocator>,\n    pub tlas_s: Arc<SharedTrackerIndexAllocator>,\n}\n\nimpl TrackerIndexAllocators {\n    pub fn new() -> Self {\n        TrackerIndexAllocators {\n            buffers: Arc::new(SharedTrackerIndexAllocator::new()),\n            textures: Arc::new(SharedTrackerIndexAllocator::new()),\n            external_textures: Arc::new(SharedTrackerIndexAllocator::new()),\n            samplers: Arc::new(SharedTrackerIndexAllocator::new()),\n            bind_groups: Arc::new(SharedTrackerIndexAllocator::new()),\n            compute_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),\n            render_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),\n            bundles: Arc::new(SharedTrackerIndexAllocator::new()),\n            query_sets: Arc::new(SharedTrackerIndexAllocator::new()),\n            blas_s: Arc::new(SharedTrackerIndexAllocator::new()),\n            tlas_s: Arc::new(SharedTrackerIndexAllocator::new()),\n        }\n    }\n}\n\n/// A structure containing all the information about a particular resource\n/// transition. User code should be able to generate a pipeline barrier\n/// based on the contents.\n#[derive(Debug, PartialEq)]\npub(crate) struct PendingTransition<S: ResourceUses> {\n    pub id: u32,\n    pub selector: S::Selector,\n    pub usage: hal::StateTransition<S>,\n}\n\npub(crate) type PendingTransitionList = Vec<PendingTransition<wgt::TextureUses>>;\n\nimpl PendingTransition<wgt::BufferUses> {\n    /// Produce the hal barrier corresponding to the transition.\n    pub fn into_hal<'a>(\n        self,\n        buf: &'a resource::Buffer,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> hal::BufferBarrier<'a, dyn hal::DynBuffer> {\n        let buffer = buf.raw(snatch_guard).expect(\"Buffer is destroyed\");\n        hal::BufferBarrier {\n            buffer,\n            usage: self.usage,\n        }\n    }\n}\n\nimpl PendingTransition<wgt::TextureUses> {\n    /// Produce the hal barrier corresponding to the transition.\n    pub fn into_hal(\n        self,\n        texture: &dyn hal::DynTexture,\n    ) -> hal::TextureBarrier<'_, dyn hal::DynTexture> {\n        // These showing up in a barrier is always a bug\n        strict_assert_ne!(self.usage.from, wgt::TextureUses::UNKNOWN);\n        strict_assert_ne!(self.usage.to, wgt::TextureUses::UNKNOWN);\n\n        let mip_count = self.selector.mips.end - self.selector.mips.start;\n        strict_assert_ne!(mip_count, 0);\n        let layer_count = self.selector.layers.end - self.selector.layers.start;\n        strict_assert_ne!(layer_count, 0);\n\n        hal::TextureBarrier {\n            texture,\n            range: wgt::ImageSubresourceRange {\n                aspect: wgt::TextureAspect::All,\n                base_mip_level: self.selector.mips.start,\n                mip_level_count: Some(mip_count),\n                base_array_layer: self.selector.layers.start,\n                array_layer_count: Some(layer_count),\n            },\n            usage: self.usage,\n        }\n    }\n}\n\n/// The uses that a resource or subresource can be in.\npub(crate) trait ResourceUses:\n    fmt::Debug + ops::BitAnd<Output = Self> + ops::BitOr<Output = Self> + PartialEq + Sized + Copy\n{\n    /// All flags that are exclusive.\n    const EXCLUSIVE: Self;\n\n    /// The selector used by this resource.\n    type Selector: fmt::Debug;\n\n    /// Turn the resource into a pile of bits.\n    fn bits(self) -> u16;\n    /// Returns true if any of the uses are exclusive.\n    fn any_exclusive(self) -> bool;\n}\n\n/// Returns true if the given states violates the usage scope rule\n/// of any(inclusive) XOR one(exclusive)\nfn invalid_resource_state<T: ResourceUses>(state: T) -> bool {\n    // Is power of two also means \"is one bit set\". We check for this as if\n    // we're in any exclusive state, we must only be in a single state.\n    state.any_exclusive() && !state.bits().is_power_of_two()\n}\n\n/// Returns true if the transition from one state to another does not require\n/// a barrier.\nfn skip_barrier<F: Flags>(old_state: F, ordered_uses_mask: F, new_state: F) -> bool {\n    // If the state didn't change and all the usages are ordered, the hardware\n    // will guarantee the order of accesses, so we do not need to issue a barrier at all\n    old_state.bits() == new_state.bits() && ordered_uses_mask.contains(old_state)\n}\n\n#[derive(Clone, Debug, Error)]\npub enum ResourceUsageCompatibilityError {\n    #[error(\"Attempted to use {res} with {invalid_use}.\")]\n    Buffer {\n        res: ResourceErrorIdent,\n        invalid_use: InvalidUse<wgt::BufferUses>,\n    },\n    #[error(\n        \"Attempted to use {res} (mips {mip_levels:?} layers {array_layers:?}) with {invalid_use}.\"\n    )]\n    Texture {\n        res: ResourceErrorIdent,\n        mip_levels: ops::Range<u32>,\n        array_layers: ops::Range<u32>,\n        invalid_use: InvalidUse<wgt::TextureUses>,\n    },\n}\n\nimpl WebGpuError for ResourceUsageCompatibilityError {\n    fn webgpu_error_type(&self) -> ErrorType {\n        ErrorType::Validation\n    }\n}\n\nimpl ResourceUsageCompatibilityError {\n    fn from_buffer(\n        buffer: &resource::Buffer,\n        current_state: wgt::BufferUses,\n        new_state: wgt::BufferUses,\n    ) -> Self {\n        Self::Buffer {\n            res: buffer.error_ident(),\n            invalid_use: InvalidUse {\n                current_state,\n                new_state,\n            },\n        }\n    }\n\n    fn from_texture(\n        texture: &resource::Texture,\n        selector: wgt::TextureSelector,\n        current_state: wgt::TextureUses,\n        new_state: wgt::TextureUses,\n    ) -> Self {\n        Self::Texture {\n            res: texture.error_ident(),\n            mip_levels: selector.mips,\n            array_layers: selector.layers,\n            invalid_use: InvalidUse {\n                current_state,\n                new_state,\n            },\n        }\n    }\n}\n\n/// Pretty print helper that shows helpful descriptions of a conflicting usage.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct InvalidUse<T> {\n    current_state: T,\n    new_state: T,\n}\n\nimpl<T: ResourceUses> fmt::Display for InvalidUse<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let current = self.current_state;\n        let new = self.new_state;\n\n        let current_exclusive = current & T::EXCLUSIVE;\n        let new_exclusive = new & T::EXCLUSIVE;\n\n        let exclusive = current_exclusive | new_exclusive;\n\n        // The text starts with \"tried to use X resource with {self}\"\n        write!(\n            f,\n            \"conflicting usages. Current usage {current:?} and new usage {new:?}. \\\n            {exclusive:?} is an exclusive usage and cannot be used with any other \\\n            usages within the usage scope (renderpass or compute dispatch)\"\n        )\n    }\n}\n\n/// All the usages that a bind group contains. The uses are not deduplicated in any way\n/// and may include conflicting uses. This is fully compliant by the WebGPU spec.\n///\n/// All bind group states are sorted by their ID so that when adding to a tracker,\n/// they are added in the most efficient order possible (ascending order).\n#[derive(Debug)]\npub(crate) struct BindGroupStates {\n    pub buffers: BufferBindGroupState,\n    pub views: TextureViewBindGroupState,\n    pub external_textures: StatelessTracker<resource::ExternalTexture>,\n    pub samplers: StatelessTracker<resource::Sampler>,\n    pub acceleration_structures: StatelessTracker<resource::Tlas>,\n}\n\nimpl BindGroupStates {\n    pub fn new() -> Self {\n        Self {\n            buffers: BufferBindGroupState::new(),\n            views: TextureViewBindGroupState::new(),\n            external_textures: StatelessTracker::new(),\n            samplers: StatelessTracker::new(),\n            acceleration_structures: StatelessTracker::new(),\n        }\n    }\n\n    /// Optimize the bind group states by sorting them by ID.\n    ///\n    /// When this list of states is merged into a tracker, the memory\n    /// accesses will be in a constant ascending order.\n    pub fn optimize(&mut self) {\n        self.buffers.optimize();\n        // Views are stateless, however, `TextureViewBindGroupState`\n        // is special as it will be merged with other texture trackers.\n        self.views.optimize();\n        // Samplers and Tlas's are stateless and don't need to be optimized\n        // since the tracker is never merged with any other tracker.\n    }\n}\n\n/// This is a render bundle specific usage scope. It includes stateless resources\n/// that are not normally included in a usage scope, but are used by render bundles\n/// and need to be owned by the render bundles.\n#[derive(Debug)]\npub(crate) struct RenderBundleScope {\n    pub buffers: BufferUsageScope,\n    pub textures: TextureUsageScope,\n    // Don't need to track views and samplers, they are never used directly, only by bind groups.\n    pub bind_groups: StatelessTracker<binding_model::BindGroup>,\n    pub render_pipelines: StatelessTracker<pipeline::RenderPipeline>,\n}\n\nimpl RenderBundleScope {\n    /// Create the render bundle scope and pull the maximum IDs from the hubs.\n    pub fn new() -> Self {\n        Self {\n            buffers: BufferUsageScope::default(),\n            textures: TextureUsageScope::default(),\n            bind_groups: StatelessTracker::new(),\n            render_pipelines: StatelessTracker::new(),\n        }\n    }\n\n    /// Merge the inner contents of a bind group into the render bundle tracker.\n    ///\n    /// Only stateful things are merged in herell other resources are owned\n    /// indirectly by the bind group.\n    ///\n    /// # Safety\n    ///\n    /// The maximum ID given by each bind group resource must be less than the\n    /// length of the storage given at the call to `new`.\n    pub unsafe fn merge_bind_group(\n        &mut self,\n        bind_group: &BindGroupStates,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        unsafe { self.buffers.merge_bind_group(&bind_group.buffers)? };\n        unsafe { self.textures.merge_bind_group(&bind_group.views)? };\n\n        Ok(())\n    }\n}\n\n/// A pool for storing the memory used by [`UsageScope`]s. We take and store this memory when the\n/// scope is dropped to avoid reallocating. The memory required only grows and allocation cost is\n/// significant when a large number of resources have been used.\npub(crate) type UsageScopePool = Mutex<Vec<(BufferUsageScope, TextureUsageScope)>>;\n\n/// A usage scope tracker. Only needs to store stateful resources as stateless\n/// resources cannot possibly have a usage conflict.\n#[derive(Debug)]\npub(crate) struct UsageScope<'a> {\n    pub pool: &'a UsageScopePool,\n    pub buffers: BufferUsageScope,\n    pub textures: TextureUsageScope,\n}\n\nimpl<'a> Drop for UsageScope<'a> {\n    fn drop(&mut self) {\n        // clear vecs and push into pool\n        self.buffers.clear();\n        self.textures.clear();\n        self.pool\n            .lock()\n            .push((mem::take(&mut self.buffers), mem::take(&mut self.textures)));\n    }\n}\n\nimpl UsageScope<'static> {\n    pub fn new_pooled<'d>(\n        pool: &'d UsageScopePool,\n        tracker_indices: &TrackerIndexAllocators,\n        ordered_buffer_usages: wgt::BufferUses,\n        ordered_texture_usages: wgt::TextureUses,\n    ) -> UsageScope<'d> {\n        let pooled = pool.lock().pop().unwrap_or_default();\n\n        let mut scope = UsageScope::<'d> {\n            pool,\n            buffers: pooled.0,\n            textures: pooled.1,\n        };\n\n        scope.buffers.set_size(tracker_indices.buffers.size());\n        scope.buffers.set_ordered_uses_mask(ordered_buffer_usages);\n        scope.textures.set_size(tracker_indices.textures.size());\n        scope.textures.set_ordered_uses_mask(ordered_texture_usages);\n        scope\n    }\n}\n\nimpl<'a> UsageScope<'a> {\n    /// Merge the inner contents of a bind group into the usage scope.\n    ///\n    /// Only stateful things are merged in herell other resources are owned\n    /// indirectly by the bind group.\n    ///\n    /// # Safety\n    ///\n    /// The maximum ID given by each bind group resource must be less than the\n    /// length of the storage given at the call to `new`.\n    pub unsafe fn merge_bind_group(\n        &mut self,\n        bind_group: &BindGroupStates,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        unsafe {\n            self.buffers.merge_bind_group(&bind_group.buffers)?;\n            self.textures.merge_bind_group(&bind_group.views)?;\n        }\n\n        Ok(())\n    }\n\n    /// Merge the inner contents of a bind group into the usage scope.\n    ///\n    /// Only stateful things are merged in herell other resources are owned\n    /// indirectly by a bind group or are merged directly into the command buffer tracker.\n    ///\n    /// # Safety\n    ///\n    /// The maximum ID given by each bind group resource must be less than the\n    /// length of the storage given at the call to `new`.\n    pub unsafe fn merge_render_bundle(\n        &mut self,\n        render_bundle: &RenderBundleScope,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        self.buffers.merge_usage_scope(&render_bundle.buffers)?;\n        self.textures.merge_usage_scope(&render_bundle.textures)?;\n\n        Ok(())\n    }\n}\n\n/// A tracker used by Device.\npub(crate) struct DeviceTracker {\n    pub buffers: DeviceBufferTracker,\n    pub textures: DeviceTextureTracker,\n}\n\nimpl DeviceTracker {\n    pub fn new(\n        ordered_buffer_usages: wgt::BufferUses,\n        ordered_texture_usages: wgt::TextureUses,\n    ) -> Self {\n        Self {\n            buffers: DeviceBufferTracker::new(ordered_buffer_usages),\n            textures: DeviceTextureTracker::new(ordered_texture_usages),\n        }\n    }\n}\n\n/// A full double sided tracker used by CommandBuffers.\npub(crate) struct Tracker {\n    /// Buffers used within this command buffer.\n    ///\n    /// For compute passes, this only includes buffers actually used by the\n    /// pipeline (contrast with the `bind_groups` member).\n    pub buffers: BufferTracker,\n\n    /// Textures used within this command buffer.\n    ///\n    /// For compute passes, this only includes textures actually used by the\n    /// pipeline (contrast with the `bind_groups` member).\n    pub textures: TextureTracker,\n\n    pub blas_s: BlasTracker,\n    pub tlas_s: StatelessTracker<resource::Tlas>,\n    pub views: StatelessTracker<resource::TextureView>,\n\n    /// Contains all bind groups that were passed in any call to\n    /// `set_bind_group` on the encoder.\n    ///\n    /// WebGPU requires that submission fails if any resource in any of these\n    /// bind groups is destroyed, even if the resource is not actually used by\n    /// the pipeline (e.g. because the pipeline does not use the bound slot, or\n    /// because the bind group was replaced by a subsequent call to\n    /// `set_bind_group`).\n    pub bind_groups: StatelessTracker<binding_model::BindGroup>,\n\n    pub compute_pipelines: StatelessTracker<pipeline::ComputePipeline>,\n    pub render_pipelines: StatelessTracker<pipeline::RenderPipeline>,\n    pub bundles: StatelessTracker<command::RenderBundle>,\n    pub query_sets: StatelessTracker<resource::QuerySet>,\n}\n\nimpl Tracker {\n    pub fn new(\n        ordered_buffer_usages: wgt::BufferUses,\n        ordered_texture_usages: wgt::TextureUses,\n    ) -> Self {\n        Self {\n            buffers: BufferTracker::new(ordered_buffer_usages),\n            textures: TextureTracker::new(ordered_texture_usages),\n            blas_s: BlasTracker::new(),\n            tlas_s: StatelessTracker::new(),\n            views: StatelessTracker::new(),\n            bind_groups: StatelessTracker::new(),\n            compute_pipelines: StatelessTracker::new(),\n            render_pipelines: StatelessTracker::new(),\n            bundles: StatelessTracker::new(),\n            query_sets: StatelessTracker::new(),\n        }\n    }\n\n    /// Iterates through all resources in the given bind group and adopts\n    /// the state given for those resources in the UsageScope. It also\n    /// removes all touched resources from the usage scope.\n    ///\n    /// If a transition is needed to get the resources into the needed\n    /// state, those transitions are stored within the tracker. A\n    /// subsequent call to [`BufferTracker::drain_transitions`] or\n    /// [`TextureTracker::drain_transitions`] is needed to get those transitions.\n    ///\n    /// This is a really funky method used by Compute Passes to generate\n    /// barriers after a call to dispatch without needing to iterate\n    /// over all elements in the usage scope. We use each the\n    /// bind group as a source of which IDs to look at. The bind groups\n    /// must have first been added to the usage scope.\n    ///\n    /// Only stateful things are merged in here, all other resources are owned\n    /// indirectly by the bind group.\n    ///\n    /// # Panics\n    ///\n    /// If a resource in the `bind_group` is not found in the usage scope.\n    pub fn set_and_remove_from_usage_scope_sparse(\n        &mut self,\n        scope: &mut UsageScope,\n        bind_group: &BindGroupStates,\n    ) {\n        self.buffers.set_and_remove_from_usage_scope_sparse(\n            &mut scope.buffers,\n            bind_group.buffers.used_tracker_indices(),\n        );\n        self.textures\n            .set_and_remove_from_usage_scope_sparse(&mut scope.textures, &bind_group.views);\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/track/texture.rs",
    "content": "//! Texture Trackers\n//!\n//! Texture trackers are significantly more complicated than\n//! the buffer trackers because textures can be in a \"complex\"\n//! state where each individual subresource can potentially be\n//! in a different state from every other subtresource. These\n//! complex states are stored separately from the simple states\n//! because they are signifignatly more difficult to track and\n//! most resources spend the vast majority of their lives in\n//! simple states.\n//!\n//! There are two special texture usages: `UNKNOWN` and `UNINITIALIZED`.\n//! - `UNKNOWN` is only used in complex states and is used to signify\n//!   that the complex state does not know anything about those subresources.\n//!   It cannot leak into transitions, it is invalid to transition into UNKNOWN\n//!   state.\n//! - `UNINITIALIZED` is used in both simple and complex states to mean the texture\n//!   is known to be in some undefined state. Any transition away from UNINITIALIZED\n//!   will treat the contents as junk.\n\nuse super::{range::RangedStates, PendingTransition, PendingTransitionList};\nuse crate::{\n    resource::{RawResourceAccess, Texture, TextureInner, TextureView, Trackable},\n    snatch::SnatchGuard,\n    track::{\n        invalid_resource_state, skip_barrier, ResourceMetadata, ResourceMetadataProvider,\n        ResourceUsageCompatibilityError, ResourceUses,\n    },\n};\nuse hal::TextureBarrier;\n\nuse arrayvec::ArrayVec;\nuse naga::FastHashMap;\n\nuse wgt::{strict_assert, strict_assert_eq, TextureSelector, TextureUses};\n\nuse alloc::{\n    sync::{Arc, Weak},\n    vec::{Drain, Vec},\n};\nuse core::iter;\n\nimpl ResourceUses for TextureUses {\n    const EXCLUSIVE: Self = Self::EXCLUSIVE;\n\n    type Selector = TextureSelector;\n\n    fn bits(self) -> u16 {\n        Self::bits(&self)\n    }\n\n    fn any_exclusive(self) -> bool {\n        self.intersects(Self::EXCLUSIVE)\n    }\n}\n\n/// Represents the complex state of textures where every subresource is potentially\n/// in a different state.\n#[derive(Clone, Debug, Default, PartialEq)]\nstruct ComplexTextureState {\n    mips: ArrayVec<RangedStates<u32, TextureUses>, { hal::MAX_MIP_LEVELS as usize }>,\n}\n\nimpl ComplexTextureState {\n    /// Creates complex texture state for the given sizes.\n    ///\n    /// This state will be initialized with the UNKNOWN state, a special state\n    /// which means the trakcer knows nothing about the state.\n    fn new(mip_level_count: u32, array_layer_count: u32) -> Self {\n        Self {\n            mips: iter::repeat_with(|| {\n                RangedStates::from_range(0..array_layer_count, TextureUses::UNKNOWN)\n            })\n            .take(mip_level_count as usize)\n            .collect(),\n        }\n    }\n\n    /// Initialize a complex state from a selector representing the full size of the texture\n    /// and an iterator of a selector and a texture use, specifying a usage for a specific\n    /// set of subresources.\n    ///\n    /// [`Self::to_selector_state_iter`] can be used to create such an iterator.\n    ///\n    /// # Safety\n    ///\n    /// All selectors in the iterator must be inside of the full_range selector.\n    ///\n    /// The full range selector must have mips and layers start at 0.\n    unsafe fn from_selector_state_iter(\n        full_range: TextureSelector,\n        state_iter: impl Iterator<Item = (TextureSelector, TextureUses)>,\n    ) -> Self {\n        strict_assert_eq!(full_range.layers.start, 0);\n        strict_assert_eq!(full_range.mips.start, 0);\n\n        let mut complex =\n            ComplexTextureState::new(full_range.mips.len() as u32, full_range.layers.len() as u32);\n        for (selector, desired_state) in state_iter {\n            strict_assert!(selector.layers.end <= full_range.layers.end);\n            strict_assert!(selector.mips.end <= full_range.mips.end);\n\n            // This should only ever happen with a wgpu bug, but let's just double\n            // check that resource states don't have any conflicts.\n            strict_assert_eq!(invalid_resource_state(desired_state), false);\n\n            let mips = selector.mips.start as usize..selector.mips.end as usize;\n            for mip in unsafe { complex.mips.get_unchecked_mut(mips) } {\n                for &mut (_, ref mut state) in mip.isolate(&selector.layers, TextureUses::UNKNOWN) {\n                    *state = desired_state;\n                }\n            }\n        }\n        complex\n    }\n\n    /// Convert a complex state into an iterator over all states stored.\n    ///\n    /// [`Self::from_selector_state_iter`] can be used to consume such an iterator.\n    fn to_selector_state_iter(\n        &self,\n    ) -> impl Iterator<Item = (TextureSelector, TextureUses)> + Clone + '_ {\n        self.mips.iter().enumerate().flat_map(|(mip, inner)| {\n            let mip = mip as u32;\n            {\n                inner.iter().map(move |&(ref layers, inner)| {\n                    (\n                        TextureSelector {\n                            mips: mip..mip + 1,\n                            layers: layers.clone(),\n                        },\n                        inner,\n                    )\n                })\n            }\n        })\n    }\n}\n\n/// Stores a bind group's texture views + their usages (within the bind group).\n#[derive(Debug)]\npub(crate) struct TextureViewBindGroupState {\n    views: Vec<(Arc<TextureView>, TextureUses)>,\n}\nimpl TextureViewBindGroupState {\n    pub fn new() -> Self {\n        Self { views: Vec::new() }\n    }\n\n    /// Optimize the texture bind group state by sorting it by ID.\n    ///\n    /// When this list of states is merged into a tracker, the memory\n    /// accesses will be in a constant ascending order.\n    pub(crate) fn optimize(&mut self) {\n        self.views\n            .sort_unstable_by_key(|(view, _)| view.parent.tracker_index());\n    }\n\n    /// Adds the given resource with the given state.\n    pub fn insert_single(&mut self, view: Arc<TextureView>, usage: TextureUses) {\n        self.views.push((view, usage));\n    }\n}\n\n/// Container for corresponding simple and complex texture states.\n#[derive(Debug)]\npub(crate) struct TextureStateSet {\n    simple: Vec<TextureUses>,\n    complex: FastHashMap<usize, ComplexTextureState>,\n}\n\nimpl TextureStateSet {\n    fn new() -> Self {\n        Self {\n            simple: Vec::new(),\n            complex: FastHashMap::default(),\n        }\n    }\n\n    fn clear(&mut self) {\n        self.simple.clear();\n        self.complex.clear();\n    }\n\n    fn set_size(&mut self, size: usize) {\n        self.simple.resize(size, TextureUses::UNINITIALIZED);\n    }\n\n    fn size(&self) -> usize {\n        self.simple.len()\n    }\n\n    /// SAFETY: `index` must be in bounds.\n    unsafe fn get_unchecked(\n        &self,\n        index: usize,\n    ) -> SingleOrManyStates<TextureUses, &ComplexTextureState> {\n        let simple = unsafe { *self.simple.get_unchecked(index) };\n        if simple == TextureUses::COMPLEX {\n            SingleOrManyStates::Many(unsafe { self.complex.get(&index).unwrap_unchecked() })\n        } else {\n            SingleOrManyStates::Single(simple)\n        }\n    }\n\n    /// # Safety\n    ///\n    /// The `index` must be in bounds.\n    unsafe fn get_mut_unchecked(\n        &mut self,\n        index: usize,\n    ) -> SingleOrManyStates<&mut TextureUses, &mut ComplexTextureState> {\n        let simple = unsafe { self.simple.get_unchecked_mut(index) };\n        if *simple == TextureUses::COMPLEX {\n            SingleOrManyStates::Many(unsafe { self.complex.get_mut(&index).unwrap_unchecked() })\n        } else {\n            SingleOrManyStates::Single(simple)\n        }\n    }\n\n    /// # Safety\n    ///\n    /// The `index` must be in bounds.\n    unsafe fn insert_simple_unchecked(&mut self, index: usize, simple: TextureUses) {\n        unsafe { *self.simple.get_unchecked_mut(index) = simple };\n    }\n\n    /// # Safety\n    ///\n    /// The `index` must be in bounds.\n    unsafe fn insert_complex_unchecked(&mut self, index: usize, complex: ComplexTextureState) {\n        unsafe { *self.simple.get_unchecked_mut(index) = TextureUses::COMPLEX };\n        self.complex.insert(index, complex);\n    }\n\n    /// # Safety\n    ///\n    /// The `index` must be in bounds.\n    unsafe fn make_simple_unchecked(&mut self, index: usize, simple: TextureUses) {\n        unsafe { *self.simple.get_unchecked_mut(index) = simple };\n        unsafe { self.complex.remove(&index).unwrap_unchecked() };\n    }\n\n    /// # Safety\n    ///\n    /// The `index` must be in bounds.\n    unsafe fn make_complex_unchecked(&mut self, index: usize, complex: ComplexTextureState) {\n        unsafe { *self.simple.get_unchecked_mut(index) = TextureUses::COMPLEX };\n        self.complex.insert(index, complex);\n    }\n\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        strict_assert!(index < self.size());\n    }\n}\n\n/// Stores all texture state within a single usage scope.\n#[derive(Debug)]\npub(crate) struct TextureUsageScope {\n    set: TextureStateSet,\n    metadata: ResourceMetadata<Arc<Texture>>,\n    ordered_uses_mask: TextureUses,\n}\n\nimpl Default for TextureUsageScope {\n    fn default() -> Self {\n        Self {\n            set: TextureStateSet::new(),\n            metadata: ResourceMetadata::new(),\n            ordered_uses_mask: TextureUses::empty(),\n        }\n    }\n}\n\nimpl TextureUsageScope {\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        self.metadata.tracker_assert_in_bounds(index);\n        self.set.tracker_assert_in_bounds(index);\n    }\n\n    pub fn clear(&mut self) {\n        self.set.clear();\n        self.metadata.clear();\n    }\n\n    /// Sets the size of all the vectors inside the tracker.\n    ///\n    /// Must be called with the highest possible Texture ID before\n    /// all unsafe functions are called.\n    pub fn set_size(&mut self, size: usize) {\n        self.set.set_size(size);\n        self.metadata.set_size(size);\n    }\n\n    pub fn set_ordered_uses_mask(&mut self, ordered_uses_mask: TextureUses) {\n        self.ordered_uses_mask = ordered_uses_mask;\n    }\n\n    /// Returns true if the tracker owns no resources.\n    ///\n    /// This is a O(n) operation.\n    pub(crate) fn is_empty(&self) -> bool {\n        self.metadata.is_empty()\n    }\n\n    /// Merge the list of texture states in the given usage scope into this UsageScope.\n    ///\n    /// If any of the resulting states is invalid, stops the merge and returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// If the given tracker uses IDs higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn merge_usage_scope(\n        &mut self,\n        scope: &Self,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        let incoming_size = scope.set.size();\n        if incoming_size > self.set.size() {\n            self.set_size(incoming_size);\n        }\n\n        for index in scope.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            scope.tracker_assert_in_bounds(index);\n\n            let texture_selector =\n                unsafe { &scope.metadata.get_resource_unchecked(index).full_range };\n            unsafe {\n                insert_or_merge(\n                    texture_selector,\n                    &mut self.set,\n                    &mut self.metadata,\n                    index,\n                    TextureStateProvider::TextureSet { set: &scope.set },\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                )?\n            };\n        }\n\n        Ok(())\n    }\n\n    /// Merge the list of texture states in the given bind group into this usage scope.\n    ///\n    /// If any of the resulting states is invalid, stops the merge and returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// Because bind groups do not check if the union of all their states is valid,\n    /// this method is allowed to return Err on the first bind group bound.\n    ///\n    /// # Safety\n    ///\n    /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this\n    /// method is called.\n    pub unsafe fn merge_bind_group(\n        &mut self,\n        bind_group: &TextureViewBindGroupState,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        for (view, usage) in bind_group.views.iter() {\n            unsafe { self.merge_single(&view.parent, Some(view.selector.clone()), *usage)? };\n        }\n\n        Ok(())\n    }\n\n    /// Merge a single state into the UsageScope.\n    ///\n    /// If the resulting state is invalid, returns a usage\n    /// conflict with the details of the invalid state.\n    ///\n    /// # Safety\n    ///\n    /// Unlike other trackers whose merge_single is safe, this method is only\n    /// called where there is already other unsafe tracking functions active,\n    /// so we can prove this unsafe \"for free\".\n    ///\n    /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this\n    /// method is called.\n    pub unsafe fn merge_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: Option<TextureSelector>,\n        new_state: TextureUses,\n    ) -> Result<(), ResourceUsageCompatibilityError> {\n        let index = texture.tracker_index().as_usize();\n\n        self.tracker_assert_in_bounds(index);\n\n        let texture_selector = &texture.full_range;\n        unsafe {\n            insert_or_merge(\n                texture_selector,\n                &mut self.set,\n                &mut self.metadata,\n                index,\n                TextureStateProvider::from_option(selector, new_state),\n                ResourceMetadataProvider::Direct { resource: texture },\n            )?\n        };\n\n        Ok(())\n    }\n}\n\npub(crate) trait TextureTrackerSetSingle {\n    fn set_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: TextureSelector,\n        new_state: TextureUses,\n    ) -> Drain<'_, PendingTransition<TextureUses>>;\n}\n\n/// Stores all texture state within a command buffer.\npub(crate) struct TextureTracker {\n    start_set: TextureStateSet,\n    end_set: TextureStateSet,\n\n    metadata: ResourceMetadata<Arc<Texture>>,\n\n    temp: Vec<PendingTransition<TextureUses>>,\n\n    ordered_uses_mask: TextureUses,\n}\n\nimpl TextureTracker {\n    pub fn new(ordered_uses_mask: TextureUses) -> Self {\n        Self {\n            start_set: TextureStateSet::new(),\n            end_set: TextureStateSet::new(),\n\n            metadata: ResourceMetadata::new(),\n\n            temp: Vec::new(),\n\n            ordered_uses_mask,\n        }\n    }\n\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        self.metadata.tracker_assert_in_bounds(index);\n        self.start_set.tracker_assert_in_bounds(index);\n        self.end_set.tracker_assert_in_bounds(index);\n    }\n\n    /// Sets the size of all the vectors inside the tracker.\n    ///\n    /// Must be called with the highest possible Texture ID before\n    /// all unsafe functions are called.\n    pub fn set_size(&mut self, size: usize) {\n        self.start_set.set_size(size);\n        self.end_set.set_size(size);\n\n        self.metadata.set_size(size);\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.start_set.size() {\n            self.set_size(index + 1);\n        }\n    }\n\n    /// Returns true if the tracker owns the given texture.\n    pub fn contains(&self, texture: &Texture) -> bool {\n        self.metadata.contains(texture.tracker_index().as_usize())\n    }\n\n    /// Returns a list of all textures tracked.\n    pub fn used_resources(&self) -> impl Iterator<Item = &Arc<Texture>> + '_ {\n        self.metadata.owned_resources()\n    }\n    /// Drain all currently pending transitions.\n    pub fn drain_transitions<'a>(\n        &'a mut self,\n        snatch_guard: &'a SnatchGuard<'a>,\n    ) -> (PendingTransitionList, Vec<Option<&'a TextureInner>>) {\n        let mut textures = Vec::new();\n        let transitions = self\n            .temp\n            .drain(..)\n            .inspect(|pending| {\n                let tex = unsafe { self.metadata.get_resource_unchecked(pending.id as _) };\n                textures.push(tex.inner.get(snatch_guard));\n            })\n            .collect();\n        (transitions, textures)\n    }\n\n    /// Sets the state of a single texture.\n    ///\n    /// If a transition is needed to get the texture into the given state, that transition\n    /// is returned.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: TextureSelector,\n        new_state: TextureUses,\n    ) -> Drain<'_, PendingTransition<TextureUses>> {\n        let index = texture.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        unsafe {\n            insert_or_barrier_update(\n                &texture.full_range,\n                Some(&mut self.start_set),\n                &mut self.end_set,\n                &mut self.metadata,\n                index,\n                TextureStateProvider::Selector {\n                    selector,\n                    state: new_state,\n                },\n                None,\n                ResourceMetadataProvider::Direct { resource: texture },\n                &mut self.temp,\n                self.ordered_uses_mask,\n            )\n        }\n\n        self.temp.drain(..)\n    }\n\n    /// Sets the given state for all texture in the given tracker.\n    ///\n    /// If a transition is needed to get the texture into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_from_tracker(&mut self, tracker: &Self) {\n        let incoming_size = tracker.start_set.size();\n        if incoming_size > self.start_set.size() {\n            self.set_size(incoming_size);\n        }\n\n        for index in tracker.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            tracker.tracker_assert_in_bounds(index);\n            unsafe {\n                let texture_selector = &tracker.metadata.get_resource_unchecked(index).full_range;\n                insert_or_barrier_update(\n                    texture_selector,\n                    Some(&mut self.start_set),\n                    &mut self.end_set,\n                    &mut self.metadata,\n                    index,\n                    TextureStateProvider::TextureSet {\n                        set: &tracker.start_set,\n                    },\n                    Some(TextureStateProvider::TextureSet {\n                        set: &tracker.end_set,\n                    }),\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &tracker.metadata,\n                    },\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                );\n            }\n        }\n    }\n\n    /// Sets the given state for all textures in the given UsageScope.\n    ///\n    /// If a transition is needed to get the textures into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// If the ID is higher than the length of internal vectors,\n    /// the vectors will be extended. A call to set_size is not needed.\n    pub fn set_from_usage_scope(&mut self, scope: &TextureUsageScope) {\n        let incoming_size = scope.set.size();\n        if incoming_size > self.start_set.size() {\n            self.set_size(incoming_size);\n        }\n\n        for index in scope.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n            scope.tracker_assert_in_bounds(index);\n            unsafe {\n                let texture_selector = &scope.metadata.get_resource_unchecked(index).full_range;\n                insert_or_barrier_update(\n                    texture_selector,\n                    Some(&mut self.start_set),\n                    &mut self.end_set,\n                    &mut self.metadata,\n                    index,\n                    TextureStateProvider::TextureSet { set: &scope.set },\n                    None,\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                );\n            }\n        }\n    }\n\n    /// Iterates through all textures in the given bind group and adopts\n    /// the state given for those textures in the UsageScope. It also\n    /// removes all touched textures from the usage scope.\n    ///\n    /// If a transition is needed to get the textures into the needed state,\n    /// those transitions are stored within the tracker. A subsequent\n    /// call to [`Self::drain_transitions`] is needed to get those transitions.\n    ///\n    /// This is a really funky method used by Compute Passes to generate\n    /// barriers after a call to dispatch without needing to iterate\n    /// over all elements in the usage scope. We use each the\n    /// bind group as a source of which IDs to look at. The bind groups\n    /// must have first been added to the usage scope.\n    ///\n    /// # Panics\n    ///\n    /// If a resource in `bind_group_state` is not found in the usage scope.\n    pub fn set_and_remove_from_usage_scope_sparse(\n        &mut self,\n        scope: &mut TextureUsageScope,\n        bind_group_state: &TextureViewBindGroupState,\n    ) {\n        let incoming_size = scope.set.size();\n        if incoming_size > self.start_set.size() {\n            self.set_size(incoming_size);\n        }\n\n        for (view, _) in bind_group_state.views.iter() {\n            let index = view.parent.tracker_index().as_usize();\n            scope.tracker_assert_in_bounds(index);\n\n            if unsafe { !scope.metadata.contains_unchecked(index) } {\n                continue;\n            }\n            let texture_selector = &view.parent.full_range;\n            // SAFETY: we checked that the index is in bounds for the scope, and\n            // called `set_size` to ensure it is valid for `self`.\n            unsafe {\n                insert_or_barrier_update(\n                    texture_selector,\n                    Some(&mut self.start_set),\n                    &mut self.end_set,\n                    &mut self.metadata,\n                    index,\n                    TextureStateProvider::TextureSet { set: &scope.set },\n                    None,\n                    ResourceMetadataProvider::Indirect {\n                        metadata: &scope.metadata,\n                    },\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                )\n            };\n\n            unsafe { scope.metadata.remove(index) };\n        }\n    }\n}\n\nimpl TextureTrackerSetSingle for TextureTracker {\n    fn set_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: TextureSelector,\n        new_state: TextureUses,\n    ) -> Drain<'_, PendingTransition<TextureUses>> {\n        self.set_single(texture, selector, new_state)\n    }\n}\n\n/// Stores all texture state within a device.\npub(crate) struct DeviceTextureTracker {\n    current_state_set: TextureStateSet,\n    metadata: ResourceMetadata<Weak<Texture>>,\n    temp: Vec<PendingTransition<TextureUses>>,\n    ordered_uses_mask: TextureUses,\n}\n\nimpl DeviceTextureTracker {\n    pub fn new(ordered_uses_mask: TextureUses) -> Self {\n        Self {\n            current_state_set: TextureStateSet::new(),\n            metadata: ResourceMetadata::new(),\n            temp: Vec::new(),\n            ordered_uses_mask,\n        }\n    }\n\n    fn tracker_assert_in_bounds(&self, index: usize) {\n        self.metadata.tracker_assert_in_bounds(index);\n        self.current_state_set.tracker_assert_in_bounds(index);\n    }\n\n    /// Extend the vectors to let the given index be valid.\n    fn allow_index(&mut self, index: usize) {\n        if index >= self.current_state_set.size() {\n            self.current_state_set.set_size(index + 1);\n            self.metadata.set_size(index + 1);\n        }\n    }\n\n    /// Returns a list of all textures tracked.\n    pub fn used_resources(&self) -> impl Iterator<Item = &Weak<Texture>> + '_ {\n        self.metadata.owned_resources()\n    }\n\n    /// Inserts a single texture and a state into the resource tracker.\n    ///\n    /// If the resource already exists in the tracker, it will be overwritten.\n    pub fn insert_single(&mut self, texture: &Arc<Texture>, state: TextureUses) {\n        let index = texture.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        unsafe {\n            insert(\n                None,\n                None,\n                &mut self.current_state_set,\n                &mut self.metadata,\n                index,\n                TextureStateProvider::KnownSingle { state },\n                None,\n                ResourceMetadataProvider::Direct {\n                    resource: &Arc::downgrade(texture),\n                },\n            )\n        };\n    }\n\n    /// Sets the state of a single texture.\n    ///\n    /// If a transition is needed to get the texture into the given state, that transition\n    /// is returned.\n    pub fn set_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: TextureSelector,\n        new_state: TextureUses,\n    ) -> Drain<'_, PendingTransition<TextureUses>> {\n        let index = texture.tracker_index().as_usize();\n\n        self.allow_index(index);\n\n        self.tracker_assert_in_bounds(index);\n\n        let start_state_provider = TextureStateProvider::Selector {\n            selector,\n            state: new_state,\n        };\n        unsafe {\n            barrier(\n                &texture.full_range,\n                &self.current_state_set,\n                index,\n                start_state_provider.clone(),\n                &mut self.temp,\n                self.ordered_uses_mask,\n            )\n        };\n        unsafe {\n            update(\n                &texture.full_range,\n                None,\n                &mut self.current_state_set,\n                index,\n                start_state_provider,\n            )\n        };\n\n        self.temp.drain(..)\n    }\n\n    /// Sets the given state for all texture in the given tracker.\n    ///\n    /// If a transition is needed to get the texture into the needed state,\n    /// those transitions are returned.\n    pub fn set_from_tracker_and_drain_transitions<'a, 'b: 'a>(\n        &'a mut self,\n        tracker: &'a TextureTracker,\n        snatch_guard: &'b SnatchGuard<'b>,\n    ) -> impl Iterator<Item = TextureBarrier<'a, dyn hal::DynTexture>> {\n        for index in tracker.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n\n            let start_state_provider = TextureStateProvider::TextureSet {\n                set: &tracker.start_set,\n            };\n            let end_state_provider = TextureStateProvider::TextureSet {\n                set: &tracker.end_set,\n            };\n            unsafe {\n                let texture_selector = &tracker.metadata.get_resource_unchecked(index).full_range;\n                barrier(\n                    texture_selector,\n                    &self.current_state_set,\n                    index,\n                    start_state_provider,\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                );\n                update(\n                    texture_selector,\n                    None,\n                    &mut self.current_state_set,\n                    index,\n                    end_state_provider,\n                );\n            }\n        }\n\n        self.temp.drain(..).map(|pending| {\n            let tex = unsafe { tracker.metadata.get_resource_unchecked(pending.id as _) };\n            let tex = tex.try_raw(snatch_guard).unwrap();\n            pending.into_hal(tex)\n        })\n    }\n\n    /// Sets the given state for all textures in the given UsageScope.\n    ///\n    /// If a transition is needed to get the textures into the needed state,\n    /// those transitions are returned.\n    pub fn set_from_usage_scope_and_drain_transitions<'a, 'b: 'a>(\n        &'a mut self,\n        scope: &'a TextureUsageScope,\n        snatch_guard: &'b SnatchGuard<'b>,\n    ) -> impl Iterator<Item = TextureBarrier<'a, dyn hal::DynTexture>> {\n        for index in scope.metadata.owned_indices() {\n            self.tracker_assert_in_bounds(index);\n\n            let start_state_provider = TextureStateProvider::TextureSet { set: &scope.set };\n            unsafe {\n                let texture_selector = &scope.metadata.get_resource_unchecked(index).full_range;\n                barrier(\n                    texture_selector,\n                    &self.current_state_set,\n                    index,\n                    start_state_provider.clone(),\n                    &mut self.temp,\n                    self.ordered_uses_mask,\n                );\n                update(\n                    texture_selector,\n                    None,\n                    &mut self.current_state_set,\n                    index,\n                    start_state_provider,\n                );\n            }\n        }\n\n        self.temp.drain(..).map(|pending| {\n            let tex = unsafe { scope.metadata.get_resource_unchecked(pending.id as _) };\n            let tex = tex.try_raw(snatch_guard).unwrap();\n            pending.into_hal(tex)\n        })\n    }\n}\n\nimpl TextureTrackerSetSingle for DeviceTextureTracker {\n    fn set_single(\n        &mut self,\n        texture: &Arc<Texture>,\n        selector: TextureSelector,\n        new_state: TextureUses,\n    ) -> Drain<'_, PendingTransition<TextureUses>> {\n        self.set_single(texture, selector, new_state)\n    }\n}\n\n/// An iterator adapter that can store two different iterator types.\n#[derive(Clone)]\nenum EitherIter<L, R> {\n    Left(L),\n    Right(R),\n}\n\nimpl<L, R, D> Iterator for EitherIter<L, R>\nwhere\n    L: Iterator<Item = D>,\n    R: Iterator<Item = D>,\n{\n    type Item = D;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match *self {\n            EitherIter::Left(ref mut inner) => inner.next(),\n            EitherIter::Right(ref mut inner) => inner.next(),\n        }\n    }\n}\n\n/// Container that signifies storing both different things\n/// if there is a single state or many different states\n/// involved in the operation.\n#[derive(Debug, Clone)]\nenum SingleOrManyStates<S, M> {\n    Single(S),\n    Many(M),\n}\n\n/// A source of texture state.\n#[derive(Clone)]\nenum TextureStateProvider<'a> {\n    /// Comes directly from a single state.\n    KnownSingle { state: TextureUses },\n    /// Comes from a selector and a single state.\n    Selector {\n        selector: TextureSelector,\n        state: TextureUses,\n    },\n    /// Comes from another texture set.\n    TextureSet { set: &'a TextureStateSet },\n}\nimpl<'a> TextureStateProvider<'a> {\n    /// Convenience function turning `Option<Selector>` into this enum.\n    fn from_option(selector: Option<TextureSelector>, state: TextureUses) -> Self {\n        match selector {\n            Some(selector) => Self::Selector { selector, state },\n            None => Self::KnownSingle { state },\n        }\n    }\n\n    /// Get the state provided by this.\n    ///\n    /// # Panics\n    ///\n    /// Panics if texture_selector is None and this uses a Selector source.\n    ///\n    /// # Safety\n    ///\n    /// - The index must be in bounds of the state set if this uses an TextureSet source.\n    #[inline(always)]\n    unsafe fn get_state(\n        self,\n        texture_selector: Option<&TextureSelector>,\n        index: usize,\n    ) -> SingleOrManyStates<\n        TextureUses,\n        impl Iterator<Item = (TextureSelector, TextureUses)> + Clone + 'a,\n    > {\n        match self {\n            TextureStateProvider::KnownSingle { state } => SingleOrManyStates::Single(state),\n            TextureStateProvider::Selector { selector, state } => {\n                // We check if the selector given is actually for the full resource,\n                // and if it is we promote to a simple state. This allows upstream\n                // code to specify selectors willy nilly, and all that are really\n                // single states are promoted here.\n                if *texture_selector.unwrap() == selector {\n                    SingleOrManyStates::Single(state)\n                } else {\n                    SingleOrManyStates::Many(EitherIter::Left(iter::once((selector, state))))\n                }\n            }\n            TextureStateProvider::TextureSet { set } => match unsafe { set.get_unchecked(index) } {\n                SingleOrManyStates::Single(single) => SingleOrManyStates::Single(single),\n                SingleOrManyStates::Many(complex) => {\n                    SingleOrManyStates::Many(EitherIter::Right(complex.to_selector_state_iter()))\n                }\n            },\n        }\n    }\n}\n\n/// Does an insertion operation if the index isn't tracked\n/// in the current metadata, otherwise merges the given state\n/// with the current state. If the merging would cause\n/// a conflict, returns that usage conflict.\n///\n/// # Safety\n///\n/// Indexes must be valid indexes into all arrays passed in\n/// to this function, either directly or via metadata or provider structs.\n#[inline(always)]\nunsafe fn insert_or_merge(\n    texture_selector: &TextureSelector,\n    current_state_set: &mut TextureStateSet,\n    resource_metadata: &mut ResourceMetadata<Arc<Texture>>,\n    index: usize,\n    state_provider: TextureStateProvider<'_>,\n    metadata_provider: ResourceMetadataProvider<'_, Arc<Texture>>,\n) -> Result<(), ResourceUsageCompatibilityError> {\n    let currently_owned = unsafe { resource_metadata.contains_unchecked(index) };\n\n    if !currently_owned {\n        unsafe {\n            insert(\n                Some(texture_selector),\n                None,\n                current_state_set,\n                resource_metadata,\n                index,\n                state_provider,\n                None,\n                metadata_provider,\n            )\n        };\n        return Ok(());\n    }\n\n    unsafe {\n        merge(\n            texture_selector,\n            current_state_set,\n            index,\n            state_provider,\n            metadata_provider,\n        )\n    }\n}\n\n/// If the resource isn't tracked\n/// - Inserts the given resource.\n/// - Uses the `start_state_provider` to populate `start_states`\n/// - Uses either `end_state_provider` or `start_state_provider`\n///   to populate `current_states`.\n///\n/// If the resource is tracked\n/// - Inserts barriers from the state in `current_states`\n///   to the state provided by `start_state_provider`.\n/// - Updates the `current_states` with either the state from\n///   `end_state_provider` or `start_state_provider`.\n///\n/// Any barriers are added to the barrier vector.\n///\n/// # Safety\n///\n/// Indexes must be valid indexes into all arrays passed in\n/// to this function, either directly or via metadata or provider structs.\n#[inline(always)]\nunsafe fn insert_or_barrier_update(\n    texture_selector: &TextureSelector,\n    start_state: Option<&mut TextureStateSet>,\n    current_state_set: &mut TextureStateSet,\n    resource_metadata: &mut ResourceMetadata<Arc<Texture>>,\n    index: usize,\n    start_state_provider: TextureStateProvider<'_>,\n    end_state_provider: Option<TextureStateProvider<'_>>,\n    metadata_provider: ResourceMetadataProvider<'_, Arc<Texture>>,\n    barriers: &mut Vec<PendingTransition<TextureUses>>,\n    ordered_uses_mask: TextureUses,\n) {\n    let currently_owned = unsafe { resource_metadata.contains_unchecked(index) };\n\n    if !currently_owned {\n        unsafe {\n            insert(\n                Some(texture_selector),\n                start_state,\n                current_state_set,\n                resource_metadata,\n                index,\n                start_state_provider,\n                end_state_provider,\n                metadata_provider,\n            )\n        };\n        return;\n    }\n\n    let update_state_provider = end_state_provider.unwrap_or_else(|| start_state_provider.clone());\n    unsafe {\n        barrier(\n            texture_selector,\n            current_state_set,\n            index,\n            start_state_provider,\n            barriers,\n            ordered_uses_mask,\n        )\n    };\n    unsafe {\n        update(\n            texture_selector,\n            start_state,\n            current_state_set,\n            index,\n            update_state_provider,\n        )\n    };\n}\n\n#[inline(always)]\nunsafe fn insert<T: Clone>(\n    texture_selector: Option<&TextureSelector>,\n    start_state: Option<&mut TextureStateSet>,\n    end_state: &mut TextureStateSet,\n    resource_metadata: &mut ResourceMetadata<T>,\n    index: usize,\n    start_state_provider: TextureStateProvider<'_>,\n    end_state_provider: Option<TextureStateProvider<'_>>,\n    metadata_provider: ResourceMetadataProvider<'_, T>,\n) {\n    let start_layers = unsafe { start_state_provider.get_state(texture_selector, index) };\n    match start_layers {\n        SingleOrManyStates::Single(state) => {\n            // This should only ever happen with a wgpu bug, but let's just double\n            // check that resource states don't have any conflicts.\n            strict_assert_eq!(invalid_resource_state(state), false);\n\n            if let Some(start_state) = start_state {\n                unsafe { start_state.insert_simple_unchecked(index, state) };\n            }\n\n            // We only need to insert ourselves the end state if there is no end state provider.\n            if end_state_provider.is_none() {\n                unsafe { end_state.insert_simple_unchecked(index, state) };\n            }\n        }\n        SingleOrManyStates::Many(state_iter) => {\n            let full_range = texture_selector.unwrap().clone();\n\n            let complex =\n                unsafe { ComplexTextureState::from_selector_state_iter(full_range, state_iter) };\n\n            if let Some(start_state) = start_state {\n                unsafe { start_state.insert_complex_unchecked(index, complex.clone()) };\n            }\n\n            // We only need to insert ourselves the end state if there is no end state provider.\n            if end_state_provider.is_none() {\n                unsafe { end_state.insert_complex_unchecked(index, complex) };\n            }\n        }\n    }\n\n    if let Some(end_state_provider) = end_state_provider {\n        match unsafe { end_state_provider.get_state(texture_selector, index) } {\n            SingleOrManyStates::Single(state) => {\n                // This should only ever happen with a wgpu bug, but let's just double\n                // check that resource states don't have any conflicts.\n                strict_assert_eq!(invalid_resource_state(state), false);\n\n                // We only need to insert into the end, as there is guaranteed to be\n                // a start state provider.\n                unsafe { end_state.insert_simple_unchecked(index, state) };\n            }\n            SingleOrManyStates::Many(state_iter) => {\n                let full_range = texture_selector.unwrap().clone();\n\n                let complex = unsafe {\n                    ComplexTextureState::from_selector_state_iter(full_range, state_iter)\n                };\n\n                // We only need to insert into the end, as there is guaranteed to be\n                // a start state provider.\n                unsafe { end_state.insert_complex_unchecked(index, complex) };\n            }\n        }\n    }\n\n    unsafe {\n        let resource = metadata_provider.get(index);\n        resource_metadata.insert(index, resource.clone());\n    }\n}\n\n#[inline(always)]\nunsafe fn merge(\n    texture_selector: &TextureSelector,\n    current_state_set: &mut TextureStateSet,\n    index: usize,\n    state_provider: TextureStateProvider<'_>,\n    metadata_provider: ResourceMetadataProvider<'_, Arc<Texture>>,\n) -> Result<(), ResourceUsageCompatibilityError> {\n    let current_state = unsafe { current_state_set.get_mut_unchecked(index) };\n\n    let new_state = unsafe { state_provider.get_state(Some(texture_selector), index) };\n\n    match (current_state, new_state) {\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => {\n            let merged_state = *current_simple | new_simple;\n\n            if invalid_resource_state(merged_state) {\n                return Err(ResourceUsageCompatibilityError::from_texture(\n                    unsafe { metadata_provider.get(index) },\n                    texture_selector.clone(),\n                    *current_simple,\n                    new_simple,\n                ));\n            }\n\n            *current_simple = merged_state;\n        }\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => {\n            // Because we are now demoting this simple state to a complex state,\n            // we actually need to make a whole new complex state for us to use\n            // as there wasn't one before.\n            let mut new_complex = unsafe {\n                ComplexTextureState::from_selector_state_iter(\n                    texture_selector.clone(),\n                    iter::once((texture_selector.clone(), *current_simple)),\n                )\n            };\n\n            for (selector, new_state) in new_many {\n                let merged_state = *current_simple | new_state;\n\n                if invalid_resource_state(merged_state) {\n                    return Err(ResourceUsageCompatibilityError::from_texture(\n                        unsafe { metadata_provider.get(index) },\n                        selector,\n                        *current_simple,\n                        new_state,\n                    ));\n                }\n\n                for mip in\n                    &mut new_complex.mips[selector.mips.start as usize..selector.mips.end as usize]\n                {\n                    for &mut (_, ref mut current_layer_state) in\n                        mip.isolate(&selector.layers, TextureUses::UNKNOWN)\n                    {\n                        *current_layer_state = merged_state;\n                    }\n\n                    mip.coalesce();\n                }\n            }\n\n            unsafe { current_state_set.make_complex_unchecked(index, new_complex) };\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_simple)) => {\n            for (mip_id, mip) in current_complex.mips.iter_mut().enumerate() {\n                let mip_id = mip_id as u32;\n\n                for &mut (ref layers, ref mut current_layer_state) in mip.iter_mut() {\n                    let merged_state = *current_layer_state | new_simple;\n\n                    // Once we remove unknown, this will never be empty, as\n                    // simple states are never unknown.\n                    let merged_state = merged_state - TextureUses::UNKNOWN;\n\n                    if invalid_resource_state(merged_state) {\n                        return Err(ResourceUsageCompatibilityError::from_texture(\n                            unsafe { metadata_provider.get(index) },\n                            TextureSelector {\n                                mips: mip_id..mip_id + 1,\n                                layers: layers.clone(),\n                            },\n                            *current_layer_state,\n                            new_simple,\n                        ));\n                    }\n\n                    *current_layer_state = merged_state;\n                }\n\n                mip.coalesce();\n            }\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => {\n            for (selector, new_state) in new_many {\n                for mip_id in selector.mips {\n                    strict_assert!((mip_id as usize) < current_complex.mips.len());\n\n                    let mip = unsafe { current_complex.mips.get_unchecked_mut(mip_id as usize) };\n\n                    for &mut (ref layers, ref mut current_layer_state) in\n                        mip.isolate(&selector.layers, TextureUses::UNKNOWN)\n                    {\n                        let merged_state = *current_layer_state | new_state;\n                        let merged_state = merged_state - TextureUses::UNKNOWN;\n\n                        if merged_state.is_empty() {\n                            // We know nothing about this state, lets just move on.\n                            continue;\n                        }\n\n                        if invalid_resource_state(merged_state) {\n                            return Err(ResourceUsageCompatibilityError::from_texture(\n                                unsafe { metadata_provider.get(index) },\n                                TextureSelector {\n                                    mips: mip_id..mip_id + 1,\n                                    layers: layers.clone(),\n                                },\n                                *current_layer_state,\n                                new_state,\n                            ));\n                        }\n                        *current_layer_state = merged_state;\n                    }\n\n                    mip.coalesce();\n                }\n            }\n        }\n    }\n    Ok(())\n}\n\n#[inline(always)]\nunsafe fn barrier(\n    texture_selector: &TextureSelector,\n    current_state_set: &TextureStateSet,\n    index: usize,\n    state_provider: TextureStateProvider<'_>,\n    barriers: &mut Vec<PendingTransition<TextureUses>>,\n    ordered_uses_mask: TextureUses,\n) {\n    let current_state = unsafe { current_state_set.get_unchecked(index) };\n\n    let new_state = unsafe { state_provider.get_state(Some(texture_selector), index) };\n\n    match (current_state, new_state) {\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => {\n            if skip_barrier(current_simple, ordered_uses_mask, new_simple) {\n                return;\n            }\n\n            barriers.push(PendingTransition {\n                id: index as _,\n                selector: texture_selector.clone(),\n                usage: hal::StateTransition {\n                    from: current_simple,\n                    to: new_simple,\n                },\n            });\n        }\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => {\n            for (selector, new_state) in new_many {\n                if new_state == TextureUses::UNKNOWN {\n                    continue;\n                }\n\n                if skip_barrier(current_simple, ordered_uses_mask, new_state) {\n                    continue;\n                }\n\n                barriers.push(PendingTransition {\n                    id: index as _,\n                    selector,\n                    usage: hal::StateTransition {\n                        from: current_simple,\n                        to: new_state,\n                    },\n                });\n            }\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_simple)) => {\n            for (mip_id, mip) in current_complex.mips.iter().enumerate() {\n                let mip_id = mip_id as u32;\n\n                for &(ref layers, current_layer_state) in mip.iter() {\n                    if current_layer_state == TextureUses::UNKNOWN {\n                        continue;\n                    }\n\n                    if skip_barrier(current_layer_state, ordered_uses_mask, new_simple) {\n                        continue;\n                    }\n\n                    barriers.push(PendingTransition {\n                        id: index as _,\n                        selector: TextureSelector {\n                            mips: mip_id..mip_id + 1,\n                            layers: layers.clone(),\n                        },\n                        usage: hal::StateTransition {\n                            from: current_layer_state,\n                            to: new_simple,\n                        },\n                    });\n                }\n            }\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => {\n            for (selector, new_state) in new_many {\n                for mip_id in selector.mips {\n                    strict_assert!((mip_id as usize) < current_complex.mips.len());\n\n                    let mip = unsafe { current_complex.mips.get_unchecked(mip_id as usize) };\n\n                    for (layers, current_layer_state) in mip.iter_filter(&selector.layers) {\n                        if *current_layer_state == TextureUses::UNKNOWN\n                            || new_state == TextureUses::UNKNOWN\n                        {\n                            continue;\n                        }\n\n                        if skip_barrier(*current_layer_state, ordered_uses_mask, new_state) {\n                            continue;\n                        }\n\n                        barriers.push(PendingTransition {\n                            id: index as _,\n                            selector: TextureSelector {\n                                mips: mip_id..mip_id + 1,\n                                layers,\n                            },\n                            usage: hal::StateTransition {\n                                from: *current_layer_state,\n                                to: new_state,\n                            },\n                        });\n                    }\n                }\n            }\n        }\n    }\n}\n\n#[inline(always)]\nunsafe fn update(\n    texture_selector: &TextureSelector,\n    start_state_set: Option<&mut TextureStateSet>,\n    current_state_set: &mut TextureStateSet,\n    index: usize,\n    state_provider: TextureStateProvider<'_>,\n) {\n    // We only ever need to update the start state here if the state is complex.\n    //\n    // If the state is simple, the first insert to the tracker would cover it.\n    let mut start_complex = start_state_set.and_then(|start_state_set| {\n        match unsafe { start_state_set.get_mut_unchecked(index) } {\n            SingleOrManyStates::Single(_) => None,\n            SingleOrManyStates::Many(complex) => Some(complex),\n        }\n    });\n\n    let current_state = unsafe { current_state_set.get_mut_unchecked(index) };\n\n    let new_state = unsafe { state_provider.get_state(Some(texture_selector), index) };\n\n    match (current_state, new_state) {\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => {\n            *current_simple = new_simple;\n        }\n        (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => {\n            // Because we are now demoting this simple state to a complex state,\n            // we actually need to make a whole new complex state for us to use\n            // as there wasn't one before.\n            let mut new_complex = unsafe {\n                ComplexTextureState::from_selector_state_iter(\n                    texture_selector.clone(),\n                    iter::once((texture_selector.clone(), *current_simple)),\n                )\n            };\n\n            for (selector, mut new_state) in new_many {\n                if new_state == TextureUses::UNKNOWN {\n                    new_state = *current_simple;\n                }\n                for mip in\n                    &mut new_complex.mips[selector.mips.start as usize..selector.mips.end as usize]\n                {\n                    for &mut (_, ref mut current_layer_state) in\n                        mip.isolate(&selector.layers, TextureUses::UNKNOWN)\n                    {\n                        *current_layer_state = new_state;\n                    }\n\n                    mip.coalesce();\n                }\n            }\n\n            unsafe { current_state_set.make_complex_unchecked(index, new_complex) };\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_single)) => {\n            for (mip_id, mip) in current_complex.mips.iter().enumerate() {\n                for &(ref layers, current_layer_state) in mip.iter() {\n                    // If this state is unknown, that means that the start is _also_ unknown.\n                    if current_layer_state == TextureUses::UNKNOWN {\n                        if let Some(&mut ref mut start_complex) = start_complex {\n                            strict_assert!(mip_id < start_complex.mips.len());\n\n                            let start_mip = unsafe { start_complex.mips.get_unchecked_mut(mip_id) };\n\n                            for &mut (_, ref mut current_start_state) in\n                                start_mip.isolate(layers, TextureUses::UNKNOWN)\n                            {\n                                strict_assert_eq!(*current_start_state, TextureUses::UNKNOWN);\n                                *current_start_state = new_single;\n                            }\n\n                            start_mip.coalesce();\n                        }\n                    }\n                }\n            }\n\n            unsafe { current_state_set.make_simple_unchecked(index, new_single) };\n        }\n        (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => {\n            for (selector, new_state) in new_many {\n                if new_state == TextureUses::UNKNOWN {\n                    // We know nothing new\n                    continue;\n                }\n\n                for mip_id in selector.mips {\n                    let mip_id = mip_id as usize;\n                    strict_assert!(mip_id < current_complex.mips.len());\n\n                    let mip = unsafe { current_complex.mips.get_unchecked_mut(mip_id) };\n\n                    for &mut (ref layers, ref mut current_layer_state) in\n                        mip.isolate(&selector.layers, TextureUses::UNKNOWN)\n                    {\n                        if *current_layer_state == TextureUses::UNKNOWN\n                            && new_state != TextureUses::UNKNOWN\n                        {\n                            // We now know something about this subresource that\n                            // we didn't before so we should go back and update\n                            // the start state.\n                            //\n                            // We know we must have starter state be complex,\n                            // otherwise we would know about this state.\n                            strict_assert!(start_complex.is_some());\n\n                            let start_complex =\n                                unsafe { start_complex.as_deref_mut().unwrap_unchecked() };\n\n                            strict_assert!(mip_id < start_complex.mips.len());\n\n                            let start_mip = unsafe { start_complex.mips.get_unchecked_mut(mip_id) };\n\n                            for &mut (_, ref mut current_start_state) in\n                                start_mip.isolate(layers, TextureUses::UNKNOWN)\n                            {\n                                strict_assert_eq!(*current_start_state, TextureUses::UNKNOWN);\n                                *current_start_state = new_state;\n                            }\n\n                            start_mip.coalesce();\n                        }\n\n                        *current_layer_state = new_state;\n                    }\n\n                    mip.coalesce();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "wgpu-core/src/validation/shader_io_deductions.rs",
    "content": "use core::fmt::{self, Debug, Display, Formatter};\n\n#[cfg(doc)]\n#[expect(unused_imports)]\nuse crate::validation::StageError;\n\n/// Max shader I/O variable deductions for vertex shader output. Used by\n/// [`StageError::TooManyUserDefinedVertexOutputs`] and\n/// [`StageError::VertexOutputLocationTooLarge`].\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum MaxVertexShaderOutputDeduction {\n    /// When a pipeline's [`crate::pipeline::RenderPipelineDescriptor::primitive`] is set to\n    /// [`wgt::PrimitiveTopology::PointList`].\n    PointListPrimitiveTopology,\n    /// When a clip distances array primitive is used in an output.\n    ClipDistances { array_size: u32 },\n}\n\nimpl MaxVertexShaderOutputDeduction {\n    fn variables_from_clip_distance_slot(num_slots: u32) -> u32 {\n        num_slots.div_ceil(4)\n    }\n}\n\nimpl MaxVertexShaderOutputDeduction {\n    pub fn for_variables(self) -> u32 {\n        match self {\n            Self::PointListPrimitiveTopology => 1,\n            Self::ClipDistances { array_size } => {\n                Self::variables_from_clip_distance_slot(array_size)\n            }\n        }\n    }\n\n    pub fn for_location(self) -> u32 {\n        match self {\n            Self::PointListPrimitiveTopology => 0,\n            Self::ClipDistances { array_size } => {\n                Self::variables_from_clip_distance_slot(array_size)\n            }\n        }\n    }\n}\n\n/// Max shader I/O variable deductions for vertex shader output. Used by\n/// [`StageError::TooManyUserDefinedFragmentInputs`] and\n/// [`StageError::FragmentInputLocationTooLarge`].\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum MaxFragmentShaderInputDeduction {\n    InterStageBuiltIn(InterStageBuiltIn),\n}\n\nimpl MaxFragmentShaderInputDeduction {\n    pub fn for_variables(self) -> u32 {\n        match self {\n            Self::InterStageBuiltIn(builtin) => match builtin {\n                InterStageBuiltIn::FrontFacing\n                | InterStageBuiltIn::SampleIndex\n                | InterStageBuiltIn::SampleMask\n                | InterStageBuiltIn::PrimitiveIndex\n                | InterStageBuiltIn::SubgroupInvocationId\n                | InterStageBuiltIn::SubgroupSize\n                | InterStageBuiltIn::ViewIndex\n                | InterStageBuiltIn::PointCoord => 1,\n                InterStageBuiltIn::Barycentric => 3,\n                InterStageBuiltIn::Position => 0,\n            },\n        }\n    }\n\n    pub fn from_inter_stage_builtin(builtin: naga::BuiltIn) -> Option<Self> {\n        use naga::BuiltIn;\n\n        Some(Self::InterStageBuiltIn(match builtin {\n            BuiltIn::Position { .. } => InterStageBuiltIn::Position,\n            BuiltIn::FrontFacing => InterStageBuiltIn::FrontFacing,\n            BuiltIn::SampleIndex => InterStageBuiltIn::SampleIndex,\n            BuiltIn::SampleMask => InterStageBuiltIn::SampleMask,\n            BuiltIn::PrimitiveIndex => InterStageBuiltIn::PrimitiveIndex,\n            BuiltIn::SubgroupSize => InterStageBuiltIn::SubgroupSize,\n            BuiltIn::SubgroupInvocationId => InterStageBuiltIn::SubgroupInvocationId,\n            BuiltIn::PointCoord => InterStageBuiltIn::PointCoord,\n            BuiltIn::Barycentric { .. } => InterStageBuiltIn::Barycentric,\n            BuiltIn::ViewIndex => InterStageBuiltIn::ViewIndex,\n            BuiltIn::BaseInstance\n            | BuiltIn::BaseVertex\n            | BuiltIn::ClipDistances\n            | BuiltIn::CullDistance\n            | BuiltIn::InstanceIndex\n            | BuiltIn::PointSize\n            | BuiltIn::VertexIndex\n            | BuiltIn::DrawIndex\n            | BuiltIn::FragDepth\n            | BuiltIn::GlobalInvocationId\n            | BuiltIn::LocalInvocationId\n            | BuiltIn::LocalInvocationIndex\n            | BuiltIn::WorkGroupId\n            | BuiltIn::WorkGroupSize\n            | BuiltIn::NumWorkGroups\n            | BuiltIn::NumSubgroups\n            | BuiltIn::SubgroupId\n            | BuiltIn::MeshTaskSize\n            | BuiltIn::CullPrimitive\n            | BuiltIn::PointIndex\n            | BuiltIn::LineIndices\n            | BuiltIn::TriangleIndices\n            | BuiltIn::VertexCount\n            | BuiltIn::Vertices\n            | BuiltIn::PrimitiveCount\n            | BuiltIn::Primitives\n            | BuiltIn::RayInvocationId\n            | BuiltIn::NumRayInvocations\n            | BuiltIn::InstanceCustomData\n            | BuiltIn::GeometryIndex\n            | BuiltIn::WorldRayOrigin\n            | BuiltIn::WorldRayDirection\n            | BuiltIn::ObjectRayOrigin\n            | BuiltIn::ObjectRayDirection\n            | BuiltIn::RayTmin\n            | BuiltIn::RayTCurrentMax\n            | BuiltIn::ObjectToWorld\n            | BuiltIn::WorldToObject\n            | BuiltIn::HitKind => return None,\n        }))\n    }\n}\n\n/// A [`naga::BuiltIn`] that counts towards\n/// a [`MaxFragmentShaderInputDeduction::InterStageBuiltIn`].\n///\n/// See also <https://www.w3.org/TR/webgpu/#inter-stage-builtins>.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum InterStageBuiltIn {\n    // Standard for WebGPU\n    Position,\n    FrontFacing,\n    SampleIndex,\n    SampleMask,\n    PrimitiveIndex,\n    SubgroupInvocationId,\n    SubgroupSize,\n\n    // Non-standard\n    PointCoord,\n    Barycentric,\n    ViewIndex,\n}\n\npub(in crate::validation) fn display_deductions_as_optional_list<T>(\n    deductions: &[T],\n    accessor: fn(&T) -> u32,\n) -> impl Display + '_\nwhere\n    T: Debug,\n{\n    struct DisplayFromFn<F>(F);\n\n    impl<F> Display for DisplayFromFn<F>\n    where\n        F: Fn(&mut Formatter<'_>) -> fmt::Result,\n    {\n        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n            let Self(inner) = self;\n            inner(f)\n        }\n    }\n\n    DisplayFromFn(move |f: &mut Formatter<'_>| {\n        let relevant_deductions = deductions\n            .iter()\n            .map(|deduction| (deduction, accessor(deduction)))\n            .filter(|(_, effective_deduction)| *effective_deduction > 0);\n        if relevant_deductions.clone().next().is_some() {\n            writeln!(f, \"; note that some deductions apply during validation:\")?;\n            let mut wrote_something = false;\n            for deduction in deductions {\n                let deducted_amount = accessor(deduction);\n                if deducted_amount > 0 {\n                    writeln!(f, \"\\n- {deduction:?}: {}\", accessor(deduction))?;\n                    wrote_something = true;\n                }\n            }\n            debug_assert!(\n                wrote_something,\n                \"no substantial deductions found in error display\"\n            );\n        }\n        Ok(())\n    })\n}\n"
  },
  {
    "path": "wgpu-hal/LICENSE.APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "wgpu-hal/LICENSE.MIT",
    "content": "MIT License\n\nCopyright (c) 2025 The gfx-rs developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "wgpu-hal/README.md",
    "content": "# `wgpu_hal`: a cross-platform unsafe graphics abstraction\n\nThis crate defines a set of traits abstracting over modern graphics APIs,\nwith implementations (\"backends\") for Vulkan, Metal, Direct3D, and GL.\n\n`wgpu_hal` is a spiritual successor to\n[gfx-hal](https://github.com/gfx-rs/gfx), but with reduced scope, and\noriented towards WebGPU implementation goals. It has no overhead for\nvalidation or tracking, and the API translation overhead is kept to the bare\nminimum by the design of WebGPU. This API can be used for resource-demanding\napplications and engines.\n\nThe `wgpu_hal` crate's main design choices:\n\n- Our traits are meant to be *portable*: proper use\n  should get equivalent results regardless of the backend.\n\n- Our traits' contracts are *unsafe*: implementations perform minimal\n  validation, if any, and incorrect use will often cause undefined behavior.\n  This allows us to minimize the overhead we impose over the underlying\n  graphics system. If you need safety, the [`wgpu-core`] crate provides a\n  safe API for driving `wgpu_hal`, implementing all necessary validation,\n  resource state tracking, and so on. (Note that `wgpu-core` is designed for\n  use via FFI; the [`wgpu`] crate provides more idiomatic Rust bindings for\n  `wgpu-core`.) Or, you can do your own validation.\n\n- In the same vein, returned errors *only cover cases the user can't\n  anticipate*, like running out of memory or losing the device. Any errors\n  that the user could reasonably anticipate are their responsibility to\n  avoid. For example, `wgpu_hal` returns no error for mapping a buffer that's\n  not mappable: as the buffer creator, the user should already know if they\n  can map it.\n\n- We use *static dispatch*. The traits are not\n  generally object-safe. You must select a specific backend type\n  like [`vulkan::Api`] or [`metal::Api`], and then use that\n  according to the main traits, or call backend-specific methods.\n\n- We use *idiomatic Rust parameter passing*,\n  taking objects by reference, returning them by value, and so on,\n  unlike `wgpu-core`, which refers to objects by ID.\n\n- We map buffer contents *persistently*. This means that the buffer\n  can remain mapped on the CPU while the GPU reads or writes to it.\n  You must explicitly indicate when data might need to be\n  transferred between CPU and GPU, if `wgpu_hal` indicates that the\n  mapping is not coherent (that is, automatically synchronized\n  between the two devices).\n\n- You must record *explicit barriers* between different usages of a\n  resource. For example, if a buffer is written to by a compute\n  shader, and then used as and index buffer to a draw call, you\n  must use [`CommandEncoder::transition_buffers`] between those two\n  operations.\n\n- Pipeline layouts are *explicitly specified* when setting bind\n  group. Incompatible layouts disturb groups bound at higher indices.\n\n- The API *accepts collections as iterators*, to avoid forcing the user to\n  store data in particular containers. The implementation doesn't guarantee\n  that any of the iterators are drained, unless stated otherwise by the\n  function documentation. For this reason, we recommend that iterators don't\n  do any mutating work.\n\nUnfortunately, `wgpu_hal`'s safety requirements are not fully documented.\nIdeally, all trait methods would have doc comments setting out the\nrequirements users must meet to ensure correct and portable behavior. If you\nare aware of a specific requirement that a backend imposes that is not\nensured by the traits' documented rules, please file an issue. Or, if you are\na capable technical writer, please file a pull request!\n\n[`wgpu-core`]: https://crates.io/crates/wgpu-core\n[`wgpu`]: https://crates.io/crates/wgpu\n[`vulkan::Api`]: vulkan/struct.Api.html\n[`metal::Api`]: metal/struct.Api.html\n\n## Primary backends\n\nThe `wgpu_hal` crate has full-featured backends implemented on the following\nplatform graphics APIs:\n\n- Vulkan, available on Linux, Android, and Windows, using the [`ash`] crate's\n  Vulkan bindings. It's also available on macOS, if you install [MoltenVK].\n\n- Metal on macOS, using the [`metal`] crate's bindings.\n\n- Direct3D 12 on Windows, using the [`windows`] crate's bindings.\n\n[`ash`]: https://crates.io/crates/ash\n[MoltenVK]: https://github.com/KhronosGroup/MoltenVK\n[`metal`]: https://crates.io/crates/metal\n[`windows`]: https://crates.io/crates/windows\n\n## Secondary backends\n\nThe `wgpu_hal` crate has a partial implementation based on the following\nplatform graphics API:\n\n- The GL backend is available anywhere OpenGL, OpenGL ES, or WebGL are\n  available. See the [`gles`] module documentation for details.\n\n[`gles`]: gles/index.html\n\nYou can see what capabilities an adapter is missing by checking the\n[`DownlevelCapabilities`][tdc] in [`ExposedAdapter::capabilities`], available\nfrom [`Instance::enumerate_adapters`].\n\nThe API is generally designed to fit the primary backends better than the\nsecondary backends, so the latter may impose more overhead.\n\n[tdc]: wgt::DownlevelCapabilities\n\n## Debugging\n\nMost of the information on the wiki [Debugging wgpu Applications][wiki-debug]\npage still applies to this API, with the exception of API tracing/replay\nfunctionality, which is only available in `wgpu-core`.\n\n[wiki-debug]: https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications\n\n"
  },
  {
    "path": "wgpu-hal/examples/raw-gles.em.html",
    "content": "<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <canvas id=\"canvas\" width=\"640\" height=\"400\"></canvas>\n    <script>\n        var Module = {\n            canvas: document.getElementById(\"canvas\"),\n            preRun: [function() {ENV.RUST_LOG = \"debug\"}]\n        };\n    </script>\n    <script src=\"raw-gles.js\"></script>\n  </body>\n</html>"
  },
  {
    "path": "wgpu-hal/src/auxil/dxgi/conv.rs",
    "content": "use alloc::string::String;\nuse std::{ffi::OsString, os::windows::ffi::OsStringExt};\n\nuse windows::Win32::Graphics::Dxgi;\n\n// Helper to convert DXGI adapter name to a normal string\npub fn map_adapter_name(name: [u16; 128]) -> String {\n    let len = name.iter().take_while(|&&c| c != 0).count();\n    let name = OsString::from_wide(&name[..len]);\n    name.to_string_lossy().into_owned()\n}\n\npub fn map_texture_format_failable(\n    format: wgt::TextureFormat,\n) -> Option<Dxgi::Common::DXGI_FORMAT> {\n    use wgt::TextureFormat as Tf;\n    use Dxgi::Common::*;\n\n    Some(match format {\n        Tf::R8Unorm => DXGI_FORMAT_R8_UNORM,\n        Tf::R8Snorm => DXGI_FORMAT_R8_SNORM,\n        Tf::R8Uint => DXGI_FORMAT_R8_UINT,\n        Tf::R8Sint => DXGI_FORMAT_R8_SINT,\n        Tf::R16Uint => DXGI_FORMAT_R16_UINT,\n        Tf::R16Sint => DXGI_FORMAT_R16_SINT,\n        Tf::R16Unorm => DXGI_FORMAT_R16_UNORM,\n        Tf::R16Snorm => DXGI_FORMAT_R16_SNORM,\n        Tf::R16Float => DXGI_FORMAT_R16_FLOAT,\n        Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM,\n        Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM,\n        Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT,\n        Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT,\n        Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM,\n        Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM,\n        Tf::R32Uint => DXGI_FORMAT_R32_UINT,\n        Tf::R32Sint => DXGI_FORMAT_R32_SINT,\n        Tf::R32Float => DXGI_FORMAT_R32_FLOAT,\n        Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT,\n        Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT,\n        Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT,\n        Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM,\n        Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,\n        Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,\n        Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM,\n        Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM,\n        Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT,\n        Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT,\n        Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP,\n        Tf::Rgb10a2Uint => DXGI_FORMAT_R10G10B10A2_UINT,\n        Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM,\n        Tf::Rg11b10Ufloat => DXGI_FORMAT_R11G11B10_FLOAT,\n        Tf::R64Uint => DXGI_FORMAT_R32G32_UINT, // R64 emulated by R32G32\n        Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT,\n        Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT,\n        Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT,\n        Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT,\n        Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT,\n        Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM,\n        Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM,\n        Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT,\n        Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT,\n        Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT,\n        Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT,\n        Tf::Stencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,\n        Tf::Depth16Unorm => DXGI_FORMAT_D16_UNORM,\n        Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT,\n        Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,\n        Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT,\n        Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT,\n        Tf::NV12 => DXGI_FORMAT_NV12,\n        Tf::P010 => DXGI_FORMAT_P010,\n        Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM,\n        Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB,\n        Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM,\n        Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB,\n        Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM,\n        Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB,\n        Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM,\n        Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM,\n        Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM,\n        Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM,\n        Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16,\n        Tf::Bc6hRgbFloat => DXGI_FORMAT_BC6H_SF16,\n        Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM,\n        Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB,\n        Tf::Etc2Rgb8Unorm\n        | Tf::Etc2Rgb8UnormSrgb\n        | Tf::Etc2Rgb8A1Unorm\n        | Tf::Etc2Rgb8A1UnormSrgb\n        | Tf::Etc2Rgba8Unorm\n        | Tf::Etc2Rgba8UnormSrgb\n        | Tf::EacR11Unorm\n        | Tf::EacR11Snorm\n        | Tf::EacRg11Unorm\n        | Tf::EacRg11Snorm\n        | Tf::Astc {\n            block: _,\n            channel: _,\n        } => return None,\n    })\n}\n\npub fn map_texture_format(format: wgt::TextureFormat) -> Dxgi::Common::DXGI_FORMAT {\n    match map_texture_format_failable(format) {\n        Some(f) => f,\n        None => unreachable!(),\n    }\n}\n\n// Note: DXGI doesn't allow sRGB format on the swapchain,\n// but creating RTV of swapchain buffers with sRGB works.\npub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> Dxgi::Common::DXGI_FORMAT {\n    match format {\n        wgt::TextureFormat::Bgra8UnormSrgb => Dxgi::Common::DXGI_FORMAT_B8G8R8A8_UNORM,\n        wgt::TextureFormat::Rgba8UnormSrgb => Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM,\n        _ => map_texture_format(format),\n    }\n}\n\n// SRV and UAV can't use the depth or typeless formats\n// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#view-creation\npub fn map_texture_format_for_srv_uav(\n    format: wgt::TextureFormat,\n    aspect: crate::FormatAspects,\n) -> Option<Dxgi::Common::DXGI_FORMAT> {\n    Some(match (format, aspect) {\n        (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => {\n            Dxgi::Common::DXGI_FORMAT_R16_UNORM\n        }\n        (wgt::TextureFormat::Depth32Float, crate::FormatAspects::DEPTH) => {\n            Dxgi::Common::DXGI_FORMAT_R32_FLOAT\n        }\n        (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::DEPTH) => {\n            Dxgi::Common::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS\n        }\n        (\n            wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8,\n            crate::FormatAspects::DEPTH,\n        ) => Dxgi::Common::DXGI_FORMAT_R24_UNORM_X8_TYPELESS,\n\n        (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::STENCIL) => {\n            Dxgi::Common::DXGI_FORMAT_X32_TYPELESS_G8X24_UINT\n        }\n        (\n            wgt::TextureFormat::Stencil8 | wgt::TextureFormat::Depth24PlusStencil8,\n            crate::FormatAspects::STENCIL,\n        ) => Dxgi::Common::DXGI_FORMAT_X24_TYPELESS_G8_UINT,\n\n        (_, crate::FormatAspects::DEPTH)\n        | (_, crate::FormatAspects::STENCIL)\n        | (_, crate::FormatAspects::DEPTH_STENCIL) => return None,\n\n        _ => map_texture_format(format),\n    })\n}\n\n// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#planar-layout-for-staging-from-buffer\npub fn map_texture_format_for_copy(\n    format: wgt::TextureFormat,\n    aspect: crate::FormatAspects,\n) -> Option<Dxgi::Common::DXGI_FORMAT> {\n    Some(match (format, aspect) {\n        (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => {\n            Dxgi::Common::DXGI_FORMAT_R16_UNORM\n        }\n        (\n            wgt::TextureFormat::Depth32Float | wgt::TextureFormat::Depth32FloatStencil8,\n            crate::FormatAspects::DEPTH,\n        ) => Dxgi::Common::DXGI_FORMAT_R32_FLOAT,\n\n        (\n            wgt::TextureFormat::Stencil8\n            | wgt::TextureFormat::Depth24PlusStencil8\n            | wgt::TextureFormat::Depth32FloatStencil8,\n            crate::FormatAspects::STENCIL,\n        ) => Dxgi::Common::DXGI_FORMAT_R8_UINT,\n\n        (format, crate::FormatAspects::COLOR) => map_texture_format(format),\n\n        _ => return None,\n    })\n}\n\npub fn map_texture_format_for_resource(\n    format: wgt::TextureFormat,\n    usage: wgt::TextureUses,\n    has_view_formats: bool,\n    casting_fully_typed_format_supported: bool,\n) -> Dxgi::Common::DXGI_FORMAT {\n    use wgt::TextureFormat as Tf;\n    use Dxgi::Common::*;\n\n    if casting_fully_typed_format_supported {\n        map_texture_format(format)\n\n    // We might view this resource as srgb or non-srgb\n    } else if has_view_formats {\n        match format {\n            Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_TYPELESS,\n            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_TYPELESS,\n            Tf::Bc1RgbaUnorm | Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_TYPELESS,\n            Tf::Bc2RgbaUnorm | Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_TYPELESS,\n            Tf::Bc3RgbaUnorm | Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_TYPELESS,\n            Tf::Bc7RgbaUnorm | Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_TYPELESS,\n            format => map_texture_format(format),\n        }\n\n    // We might view this resource as SRV/UAV but also as DSV\n    } else if format.is_depth_stencil_format()\n        && usage.intersects(\n            wgt::TextureUses::RESOURCE\n                | wgt::TextureUses::STORAGE_READ_ONLY\n                | wgt::TextureUses::STORAGE_WRITE_ONLY\n                | wgt::TextureUses::STORAGE_READ_WRITE,\n        )\n    {\n        match format {\n            Tf::Depth16Unorm => DXGI_FORMAT_R16_TYPELESS,\n            Tf::Depth32Float => DXGI_FORMAT_R32_TYPELESS,\n            Tf::Depth32FloatStencil8 => DXGI_FORMAT_R32G8X24_TYPELESS,\n            Tf::Stencil8 | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => DXGI_FORMAT_R24G8_TYPELESS,\n            _ => unreachable!(),\n        }\n    } else {\n        map_texture_format(format)\n    }\n}\n\npub fn map_index_format(format: wgt::IndexFormat) -> Dxgi::Common::DXGI_FORMAT {\n    match format {\n        wgt::IndexFormat::Uint16 => Dxgi::Common::DXGI_FORMAT_R16_UINT,\n        wgt::IndexFormat::Uint32 => Dxgi::Common::DXGI_FORMAT_R32_UINT,\n    }\n}\n\npub fn map_vertex_format(format: wgt::VertexFormat) -> Dxgi::Common::DXGI_FORMAT {\n    use wgt::VertexFormat as Vf;\n    use Dxgi::Common::*;\n\n    match format {\n        Vf::Unorm8 => DXGI_FORMAT_R8_UNORM,\n        Vf::Snorm8 => DXGI_FORMAT_R8_SNORM,\n        Vf::Uint8 => DXGI_FORMAT_R8_UINT,\n        Vf::Sint8 => DXGI_FORMAT_R8_SINT,\n        Vf::Unorm8x2 => DXGI_FORMAT_R8G8_UNORM,\n        Vf::Snorm8x2 => DXGI_FORMAT_R8G8_SNORM,\n        Vf::Uint8x2 => DXGI_FORMAT_R8G8_UINT,\n        Vf::Sint8x2 => DXGI_FORMAT_R8G8_SINT,\n        Vf::Unorm8x4 => DXGI_FORMAT_R8G8B8A8_UNORM,\n        Vf::Snorm8x4 => DXGI_FORMAT_R8G8B8A8_SNORM,\n        Vf::Uint8x4 => DXGI_FORMAT_R8G8B8A8_UINT,\n        Vf::Sint8x4 => DXGI_FORMAT_R8G8B8A8_SINT,\n        Vf::Unorm16 => DXGI_FORMAT_R16_UNORM,\n        Vf::Snorm16 => DXGI_FORMAT_R16_SNORM,\n        Vf::Uint16 => DXGI_FORMAT_R16_UINT,\n        Vf::Sint16 => DXGI_FORMAT_R16_SINT,\n        Vf::Float16 => DXGI_FORMAT_R16_FLOAT,\n        Vf::Unorm16x2 => DXGI_FORMAT_R16G16_UNORM,\n        Vf::Snorm16x2 => DXGI_FORMAT_R16G16_SNORM,\n        Vf::Uint16x2 => DXGI_FORMAT_R16G16_UINT,\n        Vf::Sint16x2 => DXGI_FORMAT_R16G16_SINT,\n        Vf::Float16x2 => DXGI_FORMAT_R16G16_FLOAT,\n        Vf::Unorm16x4 => DXGI_FORMAT_R16G16B16A16_UNORM,\n        Vf::Snorm16x4 => DXGI_FORMAT_R16G16B16A16_SNORM,\n        Vf::Uint16x4 => DXGI_FORMAT_R16G16B16A16_UINT,\n        Vf::Sint16x4 => DXGI_FORMAT_R16G16B16A16_SINT,\n        Vf::Float16x4 => DXGI_FORMAT_R16G16B16A16_FLOAT,\n        Vf::Uint32 => DXGI_FORMAT_R32_UINT,\n        Vf::Sint32 => DXGI_FORMAT_R32_SINT,\n        Vf::Float32 => DXGI_FORMAT_R32_FLOAT,\n        Vf::Uint32x2 => DXGI_FORMAT_R32G32_UINT,\n        Vf::Sint32x2 => DXGI_FORMAT_R32G32_SINT,\n        Vf::Float32x2 => DXGI_FORMAT_R32G32_FLOAT,\n        Vf::Uint32x3 => DXGI_FORMAT_R32G32B32_UINT,\n        Vf::Sint32x3 => DXGI_FORMAT_R32G32B32_SINT,\n        Vf::Float32x3 => DXGI_FORMAT_R32G32B32_FLOAT,\n        Vf::Uint32x4 => DXGI_FORMAT_R32G32B32A32_UINT,\n        Vf::Sint32x4 => DXGI_FORMAT_R32G32B32A32_SINT,\n        Vf::Float32x4 => DXGI_FORMAT_R32G32B32A32_FLOAT,\n        Vf::Unorm10_10_10_2 => DXGI_FORMAT_R10G10B10A2_UNORM,\n        Vf::Unorm8x4Bgra => DXGI_FORMAT_B8G8R8A8_UNORM,\n        Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(),\n    }\n}\n\npub fn map_acomposite_alpha_mode(mode: wgt::CompositeAlphaMode) -> Dxgi::Common::DXGI_ALPHA_MODE {\n    match mode {\n        wgt::CompositeAlphaMode::PreMultiplied => Dxgi::Common::DXGI_ALPHA_MODE_PREMULTIPLIED,\n        wgt::CompositeAlphaMode::PostMultiplied => Dxgi::Common::DXGI_ALPHA_MODE_STRAIGHT,\n        wgt::CompositeAlphaMode::Opaque => Dxgi::Common::DXGI_ALPHA_MODE_IGNORE,\n        wgt::CompositeAlphaMode::Auto | wgt::CompositeAlphaMode::Inherit => {\n            Dxgi::Common::DXGI_ALPHA_MODE_UNSPECIFIED\n        }\n    }\n}\n"
  }
]